Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
8ebe219
feat(medicine): implement view medicine detail:
LetMeSoloSir Feb 26, 2026
c36baa3
refactor(medicine): move code from list view to detail view
LetMeSoloSir Feb 27, 2026
e9ac492
feat(medicine): add new medicine functionality
LetMeSoloSir Feb 27, 2026
5988edf
feat(medicine): implement deactivate medicine feature
LetMeSoloSir Feb 27, 2026
e282806
feat(medicine): implement update medicine feature
LetMeSoloSir Feb 27, 2026
93499b0
feat(patient): add new patient functionality
LetMeSoloSir Feb 27, 2026
ed81776
fix: gender field in Account Entity has change and add more descripti…
LetMeSoloSir Feb 26, 2026
fd251e1
feat(medicine): implement view medicine detail:
LetMeSoloSir Feb 26, 2026
e020e71
refactor(medicine): move code from list view to detail view
LetMeSoloSir Feb 27, 2026
435bc74
feat(medicine): add new medicine functionality
LetMeSoloSir Feb 27, 2026
cd4f20d
feat(medicine): implement deactivate medicine feature
LetMeSoloSir Feb 27, 2026
ba42a07
feat(medicine): implement update medicine feature
LetMeSoloSir Feb 27, 2026
7285eee
feat(patient): add new patient functionality
LetMeSoloSir Feb 27, 2026
250bc99
fix: MedicineMapper and fix all Medicine Service to conveter with bra…
LetMeSoloSir Mar 3, 2026
b7f92c1
fix: MedicineMapper and fix all Medicine Service to conveter with bra…
LetMeSoloSir Mar 3, 2026
cef6505
fix: rename medicine services and requests
LetMeSoloSir Mar 4, 2026
d63e127
Refactor MedicineService and update MedicineController logic
LetMeSoloSir Mar 5, 2026
727df93
Merge branch 'main' of https://github.com/RedAvocado22/Hospital-Manag…
LetMeSoloSir Mar 5, 2026
2e1c433
Refactor MedicineService and update MedicineController logic
LetMeSoloSir Mar 5, 2026
9a82bbe
fix: update MedicineController logic
LetMeSoloSir Mar 5, 2026
57c26b6
fix: medicine controller
LetMeSoloSir Mar 5, 2026
de85d97
fix: Refactor Medicine Controller and Create, Update, De-active Medic…
LetMeSoloSir Mar 6, 2026
bb260f4
fix: Refactor and Validated All Medicine Service
LetMeSoloSir Mar 6, 2026
e69c5a1
fix: Refactor logic Get Detail Medicine API
LetMeSoloSir Mar 6, 2026
b4709d2
fix: logic De-Active Medicine API
LetMeSoloSir Mar 6, 2026
872501e
Merge branch 'main' of https://github.com/RedAvocado22/Hospital-Manag…
LetMeSoloSir Mar 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public class SecurityConfig {
"/swagger-ui/**",
"/swagger-ui.html",
"/v3/api-docs/**",
"/v1/medicines/**",
// Actuator health check
"/actuator/health",
"/actuator/info",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
package com.hospital.hms.pharmacy.controller;

import com.hospital.hms.base.api.ApiResponse;
import com.hospital.hms.base.api.ResponseMetadata;
import com.hospital.hms.base.response.PaginatedResponse;
import com.hospital.hms.pharmacy.dto.request.*;
import com.hospital.hms.pharmacy.dto.response.MedicineResponse;
import com.hospital.hms.pharmacy.service.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.UUID;

@Slf4j
@RestController
@RequestMapping("/v1/medicines")
@RequiredArgsConstructor
public class MedicineController {
private final GetMedicineDetailService getMedicineDetailService;
private final GetAllMedicineService getAllMedicineService;
private final CreateMedicineService createMedicineService;
private final DeActiveMedicineService deActiveMedicineService;
private final UpdateMedicineService updateMedicineService;

@PatchMapping("/deactivate")
public ResponseEntity<ApiResponse<Void>> deActiveMedicine(@Valid @RequestBody DeActiveMedicineRequest request, HttpServletRequest httpRequest) {
long startTime = System.currentTimeMillis();
String traceId = java.util.UUID.randomUUID().toString();

log.info("[TraceID: {}] De-active medicine with medicine id : {}", traceId, request.getId());

deActiveMedicineService.execute(request);

long duration = System.currentTimeMillis() - startTime;

ResponseMetadata metadata = ResponseMetadata.builder()
.traceId(traceId)
.path(httpRequest.getRequestURI())
.method(httpRequest.getMethod())
.duration(duration)
.apiVersion("v1")
.build();

ApiResponse<Void> response = ApiResponse.success(
null,
"De-active medicine successfully",
HttpStatus.OK.value(),
metadata
);

return ResponseEntity
.status(HttpStatus.OK)
.body(response);
}

@PostMapping("/create")
public ResponseEntity<ApiResponse<MedicineResponse>> createMedicine(@Valid @RequestBody CreateMedicineRequest request, HttpServletRequest httpRequest) {

long startTime = System.currentTimeMillis();
String traceId = java.util.UUID.randomUUID().toString();

log.info("[TraceID: {}] Creating new medicine with name: {}", traceId, request.getName());

MedicineResponse response = createMedicineService.execute(request);

long duration = System.currentTimeMillis() - startTime;

ResponseMetadata metadata = ResponseMetadata.builder()
.traceId(traceId)
.path(httpRequest.getRequestURI())
.method(httpRequest.getMethod())
.duration(duration)
.apiVersion("v1")
.build();

ApiResponse<MedicineResponse> apiResponse = ApiResponse.success(
response,
"Medicine created successfully",
HttpStatus.CREATED.value(),
metadata
);

log.info("[TraceID: {}] Medicine created successfully with ID: {} (took {}ms)",
traceId, response.id(), duration);

return ResponseEntity
.status(HttpStatus.CREATED)
.body(apiResponse);
}

@GetMapping
public ResponseEntity<ApiResponse<PaginatedResponse<MedicineResponse>>> getAllMedicines(@Valid @ModelAttribute GetAllMedicineRequest request, HttpServletRequest httpRequest) {

long startTime = System.currentTimeMillis();
String traceId = java.util.UUID.randomUUID().toString();

log.info("[TraceID: {}] Fetching all medicines{}", traceId, request.getId() != null);

PaginatedResponse<MedicineResponse> medicines = getAllMedicineService.execute(request);

long duration = System.currentTimeMillis() - startTime;

ResponseMetadata metadata = ResponseMetadata.builder()
.traceId(traceId)
.path(httpRequest.getRequestURI())
.method(httpRequest.getMethod())
.duration(duration)
.apiVersion("v1")
.build();

ApiResponse<PaginatedResponse<MedicineResponse>> apiResponse = ApiResponse.success(
medicines,
"Medicine retrieved successfully",
HttpStatus.OK.value(),
metadata
);

log.info("[TraceID: {}] Retrieved {} medicine successfully (took {}ms)",
traceId, medicines.size(), duration);

return ResponseEntity
.status(HttpStatus.OK)
.body(apiResponse);
}

@GetMapping("/detail/{id}")
public ResponseEntity<ApiResponse<MedicineResponse>> getMedicine(
@PathVariable UUID id,
HttpServletRequest httpRequest) {

long startTime = System.currentTimeMillis();
String traceId = java.util.UUID.randomUUID().toString();

log.info("[TraceID: {}] Fetching medicine with Medicine Id: {}", traceId, id);

// Tạo request object từ id
GetMedicineDetailRequest request = new GetMedicineDetailRequest(id);

MedicineResponse response = getMedicineDetailService.execute(request);
long duration = System.currentTimeMillis() - startTime;

ResponseMetadata metadata = ResponseMetadata.builder()
.traceId(traceId)
.path(httpRequest.getRequestURI())
.method(httpRequest.getMethod())
.duration(duration)
.apiVersion("v1")
.build();

ApiResponse<MedicineResponse> apiResponse = ApiResponse.success(
response,
"medicine retrieved successfully",
HttpStatus.OK.value(),
metadata
);

log.info("[TraceID: {}] Retrieved medicine: {} successfully (took {}ms)",
traceId, response.name(), duration);

return ResponseEntity
.status(HttpStatus.OK)
.body(apiResponse);
}

@PatchMapping("/update")
public ResponseEntity<ApiResponse<MedicineResponse>> updateMedicine(@Valid @RequestBody UpdateMedicineRequest request, HttpServletRequest httpRequest) {

long startTime = System.currentTimeMillis();
String traceId = java.util.UUID.randomUUID().toString();

log.info("[TraceID: {}] Updating medicine with id: {}", traceId, request.getId());

MedicineResponse response = updateMedicineService.execute(request);

long duration = System.currentTimeMillis() - startTime;

ResponseMetadata metadata = ResponseMetadata.builder()
.traceId(traceId)
.path(httpRequest.getRequestURI())
.method(httpRequest.getMethod())
.duration(duration)
.apiVersion("v1")
.build();

ApiResponse<MedicineResponse> apiResponse = ApiResponse.success(
response,
"Medicine updated successfully",
HttpStatus.OK.value(),
metadata
);

log.info("[TraceID: {}] Updated medicine: {} successfully (took {}ms)",
traceId, response.name(), duration);

return ResponseEntity
.status(HttpStatus.OK)
.body(apiResponse);

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package com.hospital.hms.pharmacy.dto.request;

import com.hospital.hms.base.request.BaseRequest;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;

import java.math.BigDecimal;

@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class CreateMedicineRequest extends BaseRequest {

@NotBlank(message = "Name is required")
@Size(min = 3, max = 200, message = "Name must be between 3 and 200 characters")
private String name;

@NotNull(message = "Quantity is required")
private Integer quantity;

@NotNull(message = "Price is required")
@DecimalMin(value = "0.01", message = "Price must be greater than 0")
private BigDecimal price;

@NotBlank(message = "Description is required")
@Size(min = 20, max = 5000, message = "Description must be between 20 and 5000 characters")
private String description;

@NotNull(message = "Active status is required")
private Boolean isActive;

@Override
public void validate() {
super.validate();

if (name != null && !name.matches(".*[a-zA-Z]{2,}.*")) {
throw new IllegalArgumentException("Name must contain at least 2 alphabetic characters");
}
if (quantity != null && quantity <= 0) {
throw new IllegalArgumentException("Quantity must be greater than 0");
}
if (price != null && price.compareTo(BigDecimal.ZERO) <= 0) {
throw new IllegalArgumentException("Price must be greater than 0");
}
if (description != null && description.trim().length() < 20) {
throw new IllegalArgumentException("Description must have at least 20 non-whitespace characters");
}
if (isActive == null) {
throw new IllegalArgumentException("Active status is required");
}
if (name != null && name.equals(description)) {
throw new IllegalArgumentException("Name and description cannot be identical");
}
if (description != null && description.length() > 5000) {
throw new IllegalArgumentException("Description is too long");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.hospital.hms.pharmacy.dto.request;

import com.hospital.hms.base.request.BaseRequest;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;

import java.util.UUID;

@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class DeActiveMedicineRequest extends BaseRequest {

@NotNull(message = "Medicine id are required ")
private UUID id;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.hospital.hms.pharmacy.dto.request;

import com.hospital.hms.base.request.PaginatedRequest;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;

import java.math.BigDecimal;
import java.util.UUID;

@Data
@SuperBuilder
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class GetAllMedicineRequest extends PaginatedRequest {

private UUID id;

private String name;

private BigDecimal price;

private BigDecimal minPrice;

private BigDecimal maxPrice;

private Integer quantity;

private String description;

public void validate() {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.hospital.hms.pharmacy.dto.request;

import com.hospital.hms.base.request.BaseRequest;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;

import java.util.UUID;

@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class GetMedicineDetailRequest extends BaseRequest {

@NotNull(message = "Medicine id are required ")
private UUID id;
}
Loading