diff --git a/.gitignore b/.gitignore index 549e00a..1d0e93b 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,5 @@ build/ ### VS Code ### .vscode/ + +.env \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index d1a9341..3cfd0ef 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,14 @@ -FROM eclipse-temurin:17-jdk-alpine -VOLUME /tmp -COPY target/*.jar /app/hospital-management-api.jar -ENTRYPOINT ["java", "-jar","/app/hospital-management-api.jar"] \ No newline at end of file +# ใช้ JRE เป็น base image เพื่อลดขนาด image +FROM eclipse-temurin:17-jre-jammy + +# กำหนด working directory ภายใน container +WORKDIR /app + +# คัดลอก JAR ไฟล์ที่สร้างขึ้นมาแล้ว +# โดยต้องปรับชื่อไฟล์ให้ตรงกับชื่อไฟล์ JAR ของคุณ +# เช่น ถ้าชื่อไฟล์คือ target/hospital-management-api-1.0.jar +# ก็ใช้ COPY target/hospital-management-api-1.0.jar app.jar +COPY target/hospital-management-api-1.0.jar app.jar + +# สั่งรันแอปพลิเคชันเมื่อ container ทำงาน +ENTRYPOINT ["java", "-jar", "app.jar"] diff --git a/README.md b/README.md index 5803354..d44c792 100644 --- a/README.md +++ b/README.md @@ -1,633 +1,286 @@ -# Hospital Management - API Module ![Build Status](https://github.com/MirnaGama/hospital-management-api/actions/workflows/maven.yml/badge.svg) +# Hospital Management API (Extended Version) -## About the project -Hospital Management API built in Spring Boot +![API Version](https://img.shields.io/badge/API%20Version-v2.0-blue.svg) +![Developed by](https://img.shields.io/badge/Developed%20by-FlookSP-green.svg) -### Prerequisites: -- Spring Boot 3.2.1 -- JDK 17 -- Maven 4.0.0 +Extended REST API for Hospital Management, built on top of the original project by Mirna Gama. This version includes new features for user management, inventory, and medical records. -### Running the application -1. `git clone https://github.com/MirnaGama/hospital-management-api/` -2. `cd hospital-management-api` -3. `mvn clean install`
-It will build the jar file in the target folder -4. `mvn spring-boot:run`
-It will compile and run the application on default port (8080) +--- -### Running tests -- `mvn test`
-It will executes all the tests. +## ✨ Improvements and New Features -- `mvn -Dtest=packageName.className test`
-It will execute only one test class +### 1. Code Structure and Architecture -- `mvn -Dtest=packageName.className#methodName test`
-It will run only one test method from one test class +* **Entity-Driven Design**: The `UserService` and `AuthServiceImpl` have been refactored to handle user data directly as a `User` entity, making the code cleaner and more aligned with standard Spring Boot practices. +* **Simplified Registration**: The old `UserDTO` has been replaced with a new `PatientRegistrationDTO`, which combines user and patient information into a single, comprehensive DTO for a smoother registration process. -### Features - v1.0 -- [X] R1 - Doctor Registration -- [X] R2 - List of Doctors -- [X] R3 - Doctor Update -- [X] R4 - Doctor Exclusion -- [X] R5 - Patient Registration -- [X] R6 - List of Patients -- [X] R7 - Patient Update -- [X] R8 - Patient Exclusion -- [X] R9 - Consultation Scheduling -- [X] R10 - Consultation Cancellation +### 2. Endpoint and Data Handling Changes -## API Documentation - /swagger-ui/index.html +* **New Patient Registration Endpoint**: The old endpoint `POST /api/auth/register` has been replaced with `POST /api/auth/register-patient` to more accurately reflect its function. +* **Data Integrity Fix**: We fixed a `DataIntegrityViolationException` caused by an overly long `state` value. Test data has been updated to use a 2-character abbreviation (e.g., "CA") to conform to database constraints. -### authentication +### 3. Major Test Suite Refinement -#### POST - [**/api/auth/register**] - Register a new user +* **Integration Test**: The `AuthenticationControllerTest` has been updated to support the new `PatientRegistrationDTO` and the new `/api/auth/register-patient` endpoint. +* **Unit Test**: Resolved a `NullPointerException` in `AuthServiceTest` by ensuring each test case runs independently, without relying on shared static variables. +* **Data Cleanup Order**: Fixed a `Referential integrity constraint violation` by adjusting the data cleanup order in `@AfterAll` to ensure patients are deleted before users. -- **Body:** -``` -{ - "login" (string, required), - "password" (string, required), -} +### 4. Additional Features + +* Refined the user and patient registration flow. +* Introduced **RBAC (Role-Based Access Control)** to manage system permissions. +* Improved the database schema for better data management. +* Added a **Default Admin** user for easier initial setup. + +--- + +## 🛠 Tech Stack +- Java 17 +- Spring Boot 3.x +- PostgreSQL + pgAdmin (via Docker) +- Spring Security (JWT + RBAC) +- JUnit 5 & Mockito (Testing) +- Swagger / OpenAPI + +--- + +## 🚀 Features - v2.0 + +### Core Data Management +* **`Doctor`** + * [x] **R1** - Doctor Registration + * [x] **R2** - Doctor List + * [x] **R3** - Doctor Data Update + * [x] **R4** - Doctor Data Exclusion +* **`Patient`** + * [x] **R5** - Patient Registration + * [x] **R6** - Patient List + * [x] **R7** - Patient Data Update + * [x] **R8** - Patient Data Exclusion +* **`Staff`** + * [x] **R9** - Staff Creation +* **`Nurse`** + * [x] **R10** - Nurse Registration + * [x] **R11** - Nurse List + * [x] **R12** - Nurse Data Update + * [x] **R13** - Nurse Data Exclusion + +### Specialized Management +* **`Consultation`** + * [x] **R14** - Scheduling a Consultation + * [x] **R15** - Canceling a Consultation + * [x] **R16** - Viewing an Individual Consultation +* **`Doctor Schedule`** + * [x] **R17** - Adding a new Doctor Schedule +* **`Nurse Schedule`** + * [x] **R18** - Adding a new Nurse Schedule + * [x] **R19** - Viewing Nurse Schedules (Pagination) +* **`Operating Room`** + * [x] **R20** - Add / Update / Delete Operating Room + * [x] **R21** - View Operating Rooms List +* **`Operating Room Schedule`** + * [x] **R22** - Add / View Operating Room Schedules +* **`Medical Record`** + * [x] **R23** - Medical Record Creation + * [x] **R24** - Medical Record Update + * [x] **R25** - Medical Record Exclusion + * [x] **R26** - Viewing a Medical Record by ID +* **`Prescription`** + * [x] **R27** - New Prescription Creation +* **`Medical Image`** + * [x] **R28** - Upload / Download Medical Images +* **`Lab Result`** + * [x] **R29** - Create / View Lab Results by Medical Record ID + +### Inventory Management +* **`Inventory Item`** + * [x] **R30** - Inventory Item Creation + * [x] **R31** - Inventory Item Update + * [x] **R32** - Inventory Item Exclusion + * [x] **R33** - Viewing an Inventory Item by ID +* **`Inventory Transaction`** + * [x] **R34** - Inventory Transaction Creation +* **`Inventory Supplier`** + * [x] **R35** - Supplier Creation +* **`Inventory Item Type`** + * [x] **R36** - Item Type Creation + +### Insurance Management +* **`Insurance Provider`** + * [x] **R37** - Provider Creation / Update / Delete + * [x] **R38** - Viewing Providers List +* **`Insurance Claim`** + * [x] **R39** - Claim Creation / Update / Delete + * [x] **R40** - Viewing Claims List + +### Billing & Payment +* **`Billing`** + * [x] **R41** - Billing Record Creation / Update / Delete + * [x] **R42** - Viewing Billing by ID +* **`Payment`** + * [x] **R43** - Payment Record Creation + * [x] **R44** - Viewing all Payment Records + * [x] **R45** - Viewing an Individual Payment Record + +### Reports +* **`Inventory Reports`** + * [x] **R46** - Low Stock Report +* **`Financial Reports`** + * [x] **R47** - Financial Overview Report +* **`Appointment Reports`** + * [x] **R48** - Appointments Report + +### User Management +* [x] **R49** - Update Username +* [x] **R50** - Update Password +* [x] **R51** - Update Role +* [x] **R52** - Deactivate User Account +* [x] **R53** - Activate User Account +* [x] **R54** - Link Patient to User +* [x] **R55** - Register Nurse and Link +* [x] **R56** - Register Doctor and Link + +--- + +## 🔑 Default Credentials + +For initial administrative access, use the following credentials: + +* **Username:** `admin@softwarecraft.tech` +* **Password:** `pasword123` + +--- + +## 📖 API Endpoints + +### 🔑 Authentication (`authentication-controller`) +* `POST` `/api/auth/register-patient` - Register a new patient +* `POST` `/api/auth/register-doctor-and-link` - Register a new doctor and link them to a user account +* `POST` `/api/auth/register-staff` - Register a new staff member +* `POST` `/api/auth/login` - Log in to the system +* `POST` `/api/auth/link-patient-to-user` - Link a patient to an existing user account + +### 👤 User Management (`user-controller`) +* `PUT` `/api/v1.0/users/{id}/username` - Update a user's username +* `PUT` `/api/v1.0/users/{id}/role` - Update a user's role +* `PUT` `/api/v1.0/users/{id}/password` - Update a user's password +* `PATCH` `/api/v1.0/users/{id}/deactivate` - Deactivate a user's account +* `PATCH` `/api/v1.0/users/{id}/activate` - Activate a user's account + +### 🧑‍⚕️ Doctors (`doctor-controller`) +* `GET` `/api/v1.0/doctors` - Retrieve a list of all doctors +* `POST` `/api/v1.0/doctors` - Create a new doctor +* `GET` `/api/v1.0/doctors/{id}` - Retrieve a single doctor by ID +* `PUT` `/api/v1.0/doctors` - Update an existing doctor +* `DELETE` `/api/v1.0/doctors/{id}` - Delete a doctor by ID + +### 🤒 Patients (`patient-controller`) +* `GET` `/api/v1.0/patients` - Retrieve a list of all patients +* `POST` `/api/v1.0/patients` - Create a new patient +* `GET` `/api/v1.0/patients/{id}` - Retrieve a single patient by ID +* `PUT` `/api/v1.0/patients` - Update an existing patient +* `DELETE` `/api/v1.0/patients/{id}` - Delete a patient by ID + +### 🏥 Staff (`staff-controller`) +* `POST` `/api/v1.0/staff` - Add a new staff member + +### 🩺 Consultations (`consultation-controller`) +* `POST` `/api/v1.0/consultations` - Create a new consultation +* `GET` `/api/v1.0/consultations/{id}` - Retrieve a consultation by ID +* `DELETE` `/api/v1.0/consultations` - Cancel a consultation + +### 📆 Doctor Schedules (`doctor-schedules-controller`) +* `POST` `/api/v1.0/doctor-schedules` - Add a new doctor schedule + +### 📝 Medical Records (`medical-record-controller`) +* `POST` `/api/v1.0/medical-records` - Create a new medical record +* `GET` `/api/v1.0/medical-records/{id}` - Retrieve a medical record by ID +* `PUT` `/api/v1.0/medical-records/{id}` - Update a medical record +* `DELETE` `/api/v1.0/medical-records/{id}` - Delete a medical record + +### 💊 Prescriptions (`prescriptions-controller`) +* `POST` `/api/v1.0/prescriptions` - Create a new prescription + +### 💲 Payments (`payment-controller`) +* `POST` `/api/v1.0/payments` - Create a new payment record +* `GET` `/api/v1.0/payments` - Retrieve all payment records +* `GET` `/api/v1.0/payments/{id}` - Retrieve a single payment record by ID + +### 📦 Inventory (`inventory-controller`) +* `POST` `/api/v1.0/inventory/items` - Create a new inventory item +* `GET` `/api/v1.0/inventory/items/{id}` - Retrieve an inventory item by ID +* `PUT` `/api/v1.0/inventory/items/{id}` - Update an inventory item +* `DELETE` `/api/v1.0/inventory/items/{id}` - Delete an inventory item +* `POST` `/api/v1.0/inventory/transactions` - Create a new inventory transaction +* `POST` `/api/v1.0/inventory/suppliers` - Create a new supplier +* `POST` `/api/v1.0/inventory/item-types` - Create a new inventory item type + +--- + +## 🚀 How to Run + +### 1️⃣ Clone the Repository +```bash +git clone +cd hospital-management-api ``` -- **Responses:** +### 2️⃣ Set Environment Variables for IntelliJ -| Code | Description | -| ------------- | ------------- | -| `200` | _Successful operation_ | -| `400` | _Validation Error_ | +เปิด IntelliJ → ไปที่ Run → Edit Configurations -#### POST - [**/api/auth/login**] - Perform the login +เลือก Configuration ของ Spring Boot ของโปรเจค -- **Body:** +ในส่วน Environment Variables กำหนดค่าตามนี้: ``` -{ - "login" (string, required), - "password" (string, required), -} +POSTGRES_HOST=localhost +POSTGRES_PORT=5432 +POSTGRES_DB= +POSTGRES_USER= +POSTGRES_PASSWORD= +JWT_SECRET= +MINIO_URL= +MINIO_ACCESS= +MINIO_SECRET= +MINIO_BUCKET= +PGADMIN_DEFAULT_EMAIL= +PGADMIN_DEFAULT_PASSWORD= ``` -- **Responses:** - -| Code | Description | -| ------------- | ------------- | -| `200` | _Successful operation_ | -| `400` | _Validation Error_ | -| `403` | _Incorrect credentials_ | - - -### doctors - -#### POST - [**/api/v1.0/doctors**] - Adds a new doctor - -- **Body:** +### 3️⃣ Run PostgreSQL and pgAdmin ``` -{ - "name" (string, required), - "email" (string, required), - "crm" (string, required), - "telephone" (string, required), - "specialty" (string, required), - "address": { - "street" (string, required), - "neighborhood" (string, required), - "zipCode" (string, required), - "city" (string, required), - "state" (string, required), - "additionalDetails" (string, optional), - "houseNumber" (string, optional) - } -} +# รันเฉพาะ PostgreSQL และ pgAdmin +docker-compose up -d postgresdb pgadmin + ``` -- **Request Headers:** +### 4️⃣ Run Spring Boot Application -| Key | Description | -| ------------- | ------------- | -| `Authorization` | _Authorization token_ | +เปิด IntelliJ -- **Responses:** +Run Spring Boot จาก Configuration ที่ตั้งค่า Environment Variables แล้ว -| Code | Description | -| ------------- | ------------- | -| `201` | _Successfully created_ | -| `400` | _Validation Error_ | -| `403` | _Unauthorized / Invalid token_ | +✅ แอปจะเชื่อมต่อ PostgreSQL และ MinIO ตามค่าที่กำหนด -#### GET - [**/api/v1.0/doctors/{id}**] - Get an existing doctor +--- -- **Response Body Example:** -``` -{ - "id": 1, - "name": "DOCTOR TEST", - "email": "test@gmail.com", - "crm": "12456", - "telephone": "(81) 99999999", - "specialty": "ORTHOPEDICS", - "active": true, - "address": { - "street": "TEST STR.", - "neighborhood": "TEST NEIGHBORHOOD", - "zipCode": "12345678", - "city": "TEST CITY", - "state": "ST", - "additionalDetails": null, - "houseNumber": null - } -} -``` +### Future Development Plan (v2.1+) -- **Request Headers:** +We will continue to develop on Spring Boot following standard best practices to add the following capabilities: -| Key | Description | -| ------------- | ------------- | -| `Authorization` | _Authorization token_ | +**AI-Driven Enhancements** +- Implement predictive analytics for patient admissions and resource allocation. +- Integrate AI models for anomaly detection in lab results or vital signs. +- Use Natural Language Processing (NLP) for automated parsing of medical notes. +- Suggest treatment plans or risk alerts based on historical patient data. +--- -- **Request Parameters:** +## 📝 API Documentation +Full API documentation (Swagger UI) is available at: +https://his-backend.softwarecraft.tech/swagger-ui/index.html -| Key | Description | -| ------------- | ------------- | -| `id` | _Unique identifier of the doctor who will be fetched_ | +--- -- **Responses:** - -| Code | Description | -| ------------- | ------------- | -| `200` | _Successful operation_ | -| `404` | _Entity not found_ | -| `403` | _Unauthorized / Invalid token_ | - -#### GET - [**/api/v1.0/doctors**] - Get a list of doctors - -- **Response Body Example:** -``` -{ - "content": [ - { - "name": "Test1", - "email": "test1@gmail.com", - "crm": "123456", - "specialty": "ORTHOPEDICS" - }, - { - "name": "Test2", - "email": "test2@gmail.com", - "crm": "789101", - "specialty": "ORTHOPEDICS" - }, - { - "name": "Test3", - "email": "test3@gmail.com", - "crm": "112131", - "specialty": "ORTHOPEDICS" - }, - ], - "pageable": { - "pageNumber": 0, - "pageSize": 10, - "sort": { - "sorted": true, - "unsorted": false, - "empty": false - }, - "offset": 0, - "paged": true, - "unpaged": false - }, - "totalPages": 1, - "totalElements": 3, - "last": true, - "sort": { - "sorted": true, - "unsorted": false, - "empty": false - }, - "number": 0, - "size": 10, - "first": true, - "numberOfElements": 3, - "empty": false -} -``` - -- **Request Headers:** - -| Key | Description | -| ------------- | ------------- | -| `Authorization` | _Authorization token_ | - -- **Request Parameters:** - -| Key | Description | -| ------------- | ------------- | -| `size` | _Number of records that should be returned_ | -| `sort` | _Sort by object attribute in descending order_ | -| `page` | _Page number_ | - -- **Responses:** - -| Code | Description | -| ------------- | ------------- | -| `200` | _Successful operation_ | -| `403` | _Unauthorized / Invalid token_ | - - -#### PUT - [**/api/v1.0/doctors**] - Updates an existing doctor - -- **Body:** -``` -{ - "id" (number, required), - "name" (string, optional), - "telephone" (string, optional), - "address": { - "street" (string, optional), - "neighborhood" (string, optional), - "zipcode" (string, optional), - "city" (string, optional), - "state" (string, optional), - "additionalDetails" (string, optional), - "houseNumber" (string, optional), - } -} -``` - -- **Request Headers:** - -| Key | Description | -| ------------- | ------------- | -| `Authorization` | _Authorization token_ | - -- **Responses:** - -| Code | Description | -| ------------- | ------------- | -| `200` | _Successful operation_ | -| `400` | _Validation Error_ | -| `403` | _Unauthorized / Invalid token_ | - - -#### DELETE - [**/api/v1.0/doctors/{id}**] - Deactivates an existing doctor - -- **Response Body Example:** -``` -{ - "id": 2, - "name": "DEACTIVATED DOCTOR TEST", - "email": "test@gmail.com", - "crm": "12456", - "telephone": "(81) 99999999", - "specialty": "ORTHOPEDICS", - "active": false, - "address": { - "street": "TEST STR.", - "neighborhood": "TEST NEIGHBORHOOD", - "zipCode": "12345678", - "city": "TEST CITY", - "state": "ST", - "additionalDetails": null, - "houseNumber": null - } -} -``` - -- **Request Headers:** - -| Key | Description | -| ------------- | ------------- | -| `Authorization` | _Authorization token_ | - -- **Request Parameters:** - -| Key | Description | -| ------------- | ------------- | -| `id` | _Unique identifier of the doctor who will be deactivated_ | - -- **Responses:** - -| Code | Description | -| ------------- | ------------- | -| `200` | _Successful operation_ | -| `400` | _Validation Error_ | -| `404` | _Entity not found_ | -| `403` | _Unauthorized / Invalid token_ | - -### patients - -#### POST - [**/api/v1.0/patients**] - Adds a new patient - -- **Body:** -``` -{ - "name" (string, required), - "email" (string, required), - "cpf" (string, required), - "telephone" (string, required), - "address": { - "street" (string, required), - "neighborhood" (string, required), - "zipCode" (string, required), - "city" (string, required), - "state" (string, required), - "additionalDetails" (string, optional), - "houseNumber" (string, optional) - } -} -``` - -- **Request Headers:** - -| Key | Description | -| ------------- | ------------- | -| `Authorization` | _Authorization token_ | - -- **Responses:** - -| Code | Description | -| ------------- | ------------- | -| `201` | _Successfully created_ | -| `400` | _Validation Error_ | -| `403` | _Unauthorized / Invalid token_ | - - -#### GET - [**/api/v1.0/patients/{id}**] - Get an existing patient - -- **Response Body Example:** -``` -{ - "id": 1, - "name": "PATIENT TEST", - "email": "test@gmail.com", - "cpf": "11111111111", - "telephone": "(81) 99999999", - "active": true, - "address": { - "street": "TEST STR.", - "neighborhood": "TEST NEIGHBORHOOD", - "zipCode": "12345678", - "city": "TEST CITY", - "state": "ST", - "additionalDetails": null, - "houseNumber": null - } -} -``` - -- **Request Headers:** - -| Key | Description | -| ------------- | ------------- | -| `Authorization` | _Authorization token_ | - -- **Request Parameters:** - -| Key | Description | -| ------------- | ------------- | -| `id` | _Unique identifier of the patient who will be fetched_ | - -- **Responses:** - -| Code | Description | -| ------------- | ------------- | -| `200` | _Successful operation_ | -| `404` | _Entity not found_ | -| `403` | _Unauthorized / Invalid token_ | - -#### GET - [**/api/v1.0/patients**] - Get a list of patients - -- **Response Body Example:** -``` -{ - "content": [ - { - "name": "Test1", - "email": "test1@gmail.com", - "cpf": "123456" - }, - { - "name": "Test2", - "email": "test2@gmail.com", - "cpf": "789101" - }, - { - "name": "Test3", - "email": "test3@gmail.com", - "cpf": "112131" - }, - ], - "pageable": { - "pageNumber": 0, - "pageSize": 10, - "sort": { - "sorted": true, - "unsorted": false, - "empty": false - }, - "offset": 0, - "paged": true, - "unpaged": false - }, - "totalPages": 1, - "totalElements": 3, - "last": true, - "sort": { - "sorted": true, - "unsorted": false, - "empty": false - }, - "number": 0, - "size": 10, - "first": true, - "numberOfElements": 3, - "empty": false -} -``` - -- **Request Headers:** - -| Key | Description | -| ------------- | ------------- | -| `Authorization` | _Authorization token_ | - -- **Request Parameters:** - -| Key | Description | -| ------------- | ------------- | -| `size` | _Number of records that should be returned_ | -| `sort` | _Sort by object attribute in descending order_ | -| `page` | _Page number_ | - -- **Responses:** - -| Code | Description | -| ------------- | ------------- | -| `200` | _Successful operation_ | -| `403` | _Unauthorized / Invalid token_ | - -#### PUT - [**/api/v1.0/patients**] - Updates an existing patient - -- **Body:** -``` -{ - "id" (number, required), - "name" (string, optional), - "telephone" (string, optional), - "address": { - "street" (string, optional), - "neighborhood" (string, optional), - "zipcode" (string, optional), - "city" (string, optional), - "state" (string, optional), - "additionalDetails" (string, optional), - "houseNumber" (string, optional), - } -} -``` - -- **Request Headers:** - -| Key | Description | -| ------------- | ------------- | -| `Authorization` | _Authorization token_ | - -- **Responses:** - -| Code | Description | -| ------------- | ------------- | -| `200` | _Successful operation_ | -| `400` | _Validation Error_ | -| `403` | _Unauthorized / Invalid token_ | - -#### DELETE - [**/api/v1.0/patients/{id}**] - Deactivates an existing patient - -- **Response Body Example:** -``` -{ - "id": 1, - "name": "DEACTIVATED PATIENT TEST", - "email": "test@gmail.com", - "cpf": "11111111111", - "telephone": "(81) 99999999", - "active": false, - "address": { - "street": "TEST STR.", - "neighborhood": "TEST NEIGHBORHOOD", - "zipCode": "12345678", - "city": "TEST CITY", - "state": "ST", - "additionalDetails": null, - "houseNumber": null - } -} -``` - -- **Request Headers:** - -| Key | Description | -| ------------- | ------------- | -| `Authorization` | _Authorization token_ | - -- **Request Parameters:** - -| Key | Description | -| ------------- | ------------- | -| `id` | _Unique identifier of the patient who will be deactivated_ | - -- **Responses:** - -| Code | Description | -| ------------- | ------------- | -| `200` | _Successful operation_ | -| `400` | _Validation Error_ | -| `404` | _Entity not found_ | -| `403` | _Unauthorized / Invalid token_ | - -### consultations - -#### POST - [**/api/v1.0/consultations**] - Adds a new consultation - -- **Body:** -``` -{ - "patientId" (number, required), - "consultationDate" (string, required), - "doctorId" (number, required if _specialty_ field is not filled), - "specialty" (string, required if _doctorId_ field is not filled) -} -``` - -- **Request Headers:** - -| Key | Description | -| ------------- | ------------- | -| `Authorization` | _Authorization token_ | - -- **Responses:** - -| Code | Description | -| ------------- | ------------- | -| `201` | _Successfully created_ | -| `400` | _Validation Error_ | -| `403` | _Unauthorized / Invalid token_ | -| `404` | _Entity not found_ | - -#### GET - [**/api/v1.0/consultations/{id}**] - Get an existing consultation - -- **Response Body Example:** -``` -{ - "id": 1, - "consultationDate": "22/04/2024 10:34", - "patient": {...}, - "doctor": {...}, - "canceled": false, - "reasonCancellation: "" -} -``` - -- **Request Headers:** - -| Key | Description | -| ------------- | ------------- | -| `Authorization` | _Authorization token_ | - -- **Request Parameters:** - -| Key | Description | -| ------------- | ------------- | -| `id` | _Unique identifier of the consultation that will be fetched_ | - -- **Responses:** - -| Code | Description | -| ------------- | ------------- | -| `200` | _Successful operation_ | -| `404` | _Entity not found_ | -| `403` | _Unauthorized / Invalid token_ | - -#### DELETE - [**/api/v1.0/consultations**] - Cancels a scheduled consultation - -- **Body:** -``` -{ - "consultationId" (number, required), - "reasonCancellation" (string, required), -} -``` - -- **Request Headers:** - -| Key | Description | -| ------------- | ------------- | -| `Authorization` | _Authorization token_ | - -- **Responses:** - -| Code | Description | -| ------------- | ------------- | -| `200` | _Successful operation_ | -| `403` | _Unauthorized / Invalid token_ | -| `404` | _Entity not found_ | +## 🙏 Credits +Extended version of MirnaGama/hospital-management-api. +This continuation improves code quality, adds features, and addresses real-world issues. \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..8d28a9f --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,51 @@ +version: '3.8' + +services: + postgresdb: + image: postgres:13 + container_name: postgres-hospital + restart: always + environment: + POSTGRES_USER: ${POSTGRES_USER} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + POSTGRES_DB: ${POSTGRES_DB} + ports: + - '5432:5432' + volumes: + - postgres-data:/var/lib/postgresql/data + + api: + image: adminsoftwarecraft/hospital-api:v1.0.0 + container_name: hospital-api-container + restart: on-failure + environment: + SPRING_DATASOURCE_URL: jdbc:postgresql://postgresdb:5432/${POSTGRES_DB} + SPRING_DATASOURCE_USERNAME: ${POSTGRES_USER} + SPRING_DATASOURCE_PASSWORD: ${POSTGRES_PASSWORD} + JWT_SECRET: ${JWT_SECRET} + MINIO_URL: ${MINIO_URL} + MINIO_ACCESS: ${MINIO_ACCESS} + MINIO_SECRET: ${MINIO_SECRET} + MINIO_BUCKET: ${MINIO_BUCKET} + ports: + - "8080:8080" + depends_on: + - postgresdb + + pgadmin: + image: dpage/pgadmin4 + container_name: pgadmin-hospital + restart: always + environment: + PGADMIN_DEFAULT_EMAIL: ${PGADMIN_DEFAULT_EMAIL} + PGADMIN_DEFAULT_PASSWORD: ${PGADMIN_DEFAULT_PASSWORD} + ports: + - '8180:80' + volumes: + - pgadmin-data:/var/lib/pgadmin + depends_on: + - postgresdb + +volumes: + postgres-data: + pgadmin-data: diff --git a/pom.xml b/pom.xml index 760583b..b6d0325 100644 --- a/pom.xml +++ b/pom.xml @@ -40,23 +40,37 @@ org.springframework.boot spring-boot-starter-validation + + org.projectlombok + lombok + 1.18.30 + provided + + - org.flywaydb - flyway-core - - - org.flywaydb - flyway-mysql - - - com.mysql - mysql-connector-j + org.postgresql + postgresql runtime + + + + org.flywaydb + flyway-core + 9.22.3 + + + + + io.minio + minio + 8.5.2 + + com.h2database h2 - 2.1.214 + 2.2.220 org.springframework.boot diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/BillingController.java b/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/BillingController.java new file mode 100644 index 0000000..ff3f244 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/BillingController.java @@ -0,0 +1,49 @@ +package com.mirna.hospitalmanagementapi.application.controllers; + +import com.mirna.hospitalmanagementapi.domain.entities.Billing; +import com.mirna.hospitalmanagementapi.domain.dtos.BillingDTO; +import com.mirna.hospitalmanagementapi.domain.services.BillingService; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import jakarta.validation.Valid; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.util.UriComponentsBuilder; +import java.net.URI; +import org.springframework.security.access.prepost.PreAuthorize; + +@RestController +@RequestMapping("/api/v1.0/billings") +@SecurityRequirement(name = "bearer-key") +public class BillingController { + + @Autowired + private BillingService billingService; + + @PostMapping + public ResponseEntity createBilling(@RequestBody @Valid BillingDTO billingDTO, UriComponentsBuilder uriBuilder) { + Billing newBilling = billingService.createBilling(billingDTO); + URI uri = uriBuilder.path("/api/v1.0/billings/{id}").buildAndExpand(newBilling.getId()).toUri(); + return ResponseEntity.created(uri).body(newBilling); + } + + @GetMapping("/{id}") + @PreAuthorize("hasAnyRole('ADMIN', 'RECEPTIONIST') or (hasRole('PATIENT') and @billingServiceImpl.isOwner(#id, authentication.principal.id))") + public ResponseEntity getBillingById(@PathVariable Long id) { + Billing billing = billingService.getBillingById(id); + return ResponseEntity.ok(billing); + } + + @PutMapping("/{id}") + public ResponseEntity updateBilling(@PathVariable Long id, @RequestBody @Valid BillingDTO billingDTO) { + Billing updatedBilling = billingService.updateBilling(id, billingDTO); + return ResponseEntity.ok(updatedBilling); + } + + @DeleteMapping("/{id}") + public ResponseEntity deleteBilling(@PathVariable Long id) { + billingService.deleteBilling(id); + return ResponseEntity.noContent().build(); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/ConsultationController.java b/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/ConsultationController.java index f42e126..11fdc9b 100644 --- a/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/ConsultationController.java +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/ConsultationController.java @@ -21,6 +21,10 @@ import com.mirna.hospitalmanagementapi.domain.entities.Consultation; import com.mirna.hospitalmanagementapi.domain.exceptions.ConsultationValidationException; import com.mirna.hospitalmanagementapi.domain.services.ConsultationService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import jakarta.validation.Valid; @@ -35,59 +39,84 @@ import jakarta.validation.Valid; @SecurityRequirement(name = "bearer-key") public class ConsultationController { - @Autowired - private ConsultationService consultationService; - - /** - * Post method to create a new Consultation object based on the provided DTO. - * - * @param consultationDTO The data transfer object containing data for Consultation - * entity. - * - * @return A response entity containing the saved consultation and created status if successful, or - * a 400-level error if there is a validation error - * @throws ConsultationValidationException if there is a validation error - */ - @PostMapping - public ResponseEntity postConsultation(@RequestBody @Valid ConsultationDTO consultationDTO) throws ConsultationValidationException { - Consultation consultation = consultationService.addConsultation(consultationDTO); - - UriComponents uriComponents = UriComponentsBuilder - .fromUriString("/api/v1.0/consultations/{id}") - .encode() - .build(); + @Autowired + private ConsultationService consultationService; - URI uri = uriComponents.expand(consultation.getId()).toUri(); - - return ResponseEntity.created(uri).body(consultation); - } - - /** - * Get method to receive a Consultation record by the provided ID - * - * @param id A long representing the consultation's unique identifier - * - * @return A response entity containing the corresponding consultation if successful, or - * a 400-level error if it is non-existent - */ - @GetMapping("/{id}") - public ResponseEntity getConsultation(@PathVariable Long id) { - Consultation consultation = consultationService.findConsultationById(id); - - return ResponseEntity.ok(consultation); - } - - /** - * Delete method to update a new Consultation object based on the provided DTO. - * - * @param consultationCanceledDTO The data transfer object containing data to update Consultation - * entity. - * - * @return A response entity containing the canceled consultation and ok status if successful, or - * a 400-level error if the consultation entity is not found - */ - @DeleteMapping - public ResponseEntity deleteConsultation(@RequestBody @Valid ConsultationCanceledDTO consultationCanceledDTO) { - return ResponseEntity.ok(consultationService.cancelConsultation(consultationCanceledDTO)); - } -} + /** + * Post method to create a new Consultation object based on the provided DTO. + * + * @param consultationDTO The data transfer object containing data for Consultation + * entity. + * * @return A response entity containing the saved consultation and created status if successful, or + * a 400-level error if there is a validation error + * @throws ConsultationValidationException if there is a validation error + */ + @PostMapping + @Operation( + summary = "สร้างการนัดหมายใหม่", + description = "อนุญาตให้ผู้ป่วยจองการนัดหมายกับแพทย์ที่ระบุหรือตามแผนก", + requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody( + content = @Content( + mediaType = "application/json", + examples = { + @ExampleObject( + name = "ตัวอย่าง: จองด้วยรหัสแพทย์", + summary = "จองนัดหมายกับแพทย์ที่ต้องการโดยตรง", + value = "{\"doctorId\": 1, \"patientId\": 1, \"consultationDate\": \"2025-09-15T10:00:00+07:00\", \"specialty\": null}" + ), + @ExampleObject( + name = "ตัวอย่าง: จองด้วยแผนก", + summary = "จองนัดหมายโดยให้ระบบเลือกแพทย์ที่ว่างในแผนกที่ระบุ", + value = "{\"doctorId\": null, \"patientId\": 1, \"consultationDate\": \"2025-09-15T11:00:00+07:00\", \"specialty\": \"ORTHOPEDICS\"}" + ) + } + ) + ), + responses = { + @ApiResponse(responseCode = "201", description = "สร้างการนัดหมายสำเร็จ"), + @ApiResponse(responseCode = "400", description = "ข้อมูลใน Request Body ไม่ถูกต้อง หรือการตรวจสอบล้มเหลว") + } + ) + public ResponseEntity postConsultation(@RequestBody @Valid ConsultationDTO consultationDTO) throws ConsultationValidationException { + System.out.println("--- Inside ConsultationController.postConsultation ---"); + System.out.println("Received DTO: " + consultationDTO.toString()); + + Consultation consultation = consultationService.addConsultation(consultationDTO); + + UriComponents uriComponents = UriComponentsBuilder + .fromUriString("/api/v1.0/consultations/{id}") + .encode() + .build(); + + URI uri = uriComponents.expand(consultation.getId()).toUri(); + + return ResponseEntity.created(uri).body(consultation); + } + + /** + * Get method to receive a Consultation record by the provided ID + * + * @param id A long representing the consultation's unique identifier + * * @return A response entity containing the corresponding consultation if successful, or + * a 400-level error if it is non-existent + */ + @GetMapping("/{id}") + public ResponseEntity getConsultation(@PathVariable Long id) { + Consultation consultation = consultationService.findConsultationById(id); + + return ResponseEntity.ok(consultation); + } + + /** + * Delete method to update a new Consultation object based on the provided DTO. + * + * @param consultationCanceledDTO The data transfer object containing data to update Consultation + * entity. + * * @return A response entity containing the canceled consultation and ok status if successful, or + * a 400-level error if the consultation entity is not found + */ + @DeleteMapping + public ResponseEntity deleteConsultation(@RequestBody @Valid ConsultationCanceledDTO consultationCanceledDTO) { + return ResponseEntity.ok(consultationService.cancelConsultation(consultationCanceledDTO)); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/DoctorScheduleController.java b/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/DoctorScheduleController.java new file mode 100644 index 0000000..463fdc0 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/DoctorScheduleController.java @@ -0,0 +1,60 @@ +package com.mirna.hospitalmanagementapi.application.controllers; + +import com.mirna.hospitalmanagementapi.domain.dtos.DoctorScheduleDTO; +import com.mirna.hospitalmanagementapi.domain.entities.Doctor; +import com.mirna.hospitalmanagementapi.domain.entities.DoctorSchedule; +import com.mirna.hospitalmanagementapi.domain.services.DoctorScheduleService; +import com.mirna.hospitalmanagementapi.domain.services.DoctorService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * REST controller for doctor schedules. + * + * @author FlookSP + * @version 1.1 + */ +@RestController +@RequestMapping("/api/v1.0/doctor-schedules") +@Tag(name = "doctor-dchedule-controller") +public class DoctorScheduleController { + + @Autowired + private DoctorScheduleService doctorScheduleService; + + @Autowired + private DoctorService doctorService; // ใช้สำหรับค้นหา Doctor Entity + + @PostMapping + @Operation(summary = "เพิ่มตารางเวลาของแพทย์ใหม่", security = @SecurityRequirement(name = "bearer-key")) + public ResponseEntity addDoctorSchedule(@RequestBody @Valid DoctorScheduleDTO dto) { + + // 1. ค้นหา Doctor Entity จาก doctorId ใน DTO + Doctor doctor = doctorService.findDoctorById(dto.doctorId()); + if (doctor == null) { + return ResponseEntity.notFound().build(); + } + + // 2. แปลง DTO เป็น Entity + DoctorSchedule doctorSchedule = new DoctorSchedule( + doctor, + dto.dayOfWeek(), + dto.startTime(), + dto.endTime() + ); + + // 3. บันทึก DoctorSchedule + DoctorSchedule savedSchedule = doctorScheduleService.saveDoctorSchedule(doctorSchedule); + + return ResponseEntity.ok(savedSchedule); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/InsuranceClaimController.java b/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/InsuranceClaimController.java new file mode 100644 index 0000000..9d0872b --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/InsuranceClaimController.java @@ -0,0 +1,58 @@ +package com.mirna.hospitalmanagementapi.application.controllers; + +import com.mirna.hospitalmanagementapi.domain.entities.InsuranceClaim; +import com.mirna.hospitalmanagementapi.domain.dtos.InsuranceClaimDTO; +import com.mirna.hospitalmanagementapi.domain.services.InsuranceClaimService; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import jakarta.validation.Valid; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.util.UriComponentsBuilder; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.web.PageableDefault; + +import java.net.URI; +import java.util.List; + +@RestController +@RequestMapping("/api/v1.0/insurance-claims") +@SecurityRequirement(name = "bearer-key") +public class InsuranceClaimController { + + @Autowired + private InsuranceClaimService insuranceClaimService; + + @PostMapping + public ResponseEntity createInsuranceClaim(@RequestBody @Valid InsuranceClaimDTO insuranceClaimDTO, UriComponentsBuilder uriBuilder) { + InsuranceClaim newClaim = insuranceClaimService.createInsuranceClaim(insuranceClaimDTO); + URI uri = uriBuilder.path("/api/v1.0/insurance-claims/{id}").buildAndExpand(newClaim.getId()).toUri(); + return ResponseEntity.created(uri).body(newClaim); + } + + @GetMapping("/{id}") + public ResponseEntity getInsuranceClaimById(@PathVariable Long id) { + InsuranceClaim claim = insuranceClaimService.getInsuranceClaimById(id); + return ResponseEntity.ok(claim); + } + + @GetMapping + public ResponseEntity> getAllInsuranceClaims(@PageableDefault(size = 10, page = 0, sort = {"id"}) Pageable pageable) { + Page claims = insuranceClaimService.getAllInsuranceClaims(pageable); + return ResponseEntity.ok(claims); + } + + @PutMapping("/{id}") + public ResponseEntity updateInsuranceClaim(@PathVariable Long id, @RequestBody @Valid InsuranceClaimDTO insuranceClaimDTO) { + InsuranceClaim updatedClaim = insuranceClaimService.updateInsuranceClaim(id, insuranceClaimDTO); + return ResponseEntity.ok(updatedClaim); + } + + @DeleteMapping("/{id}") + public ResponseEntity deleteInsuranceClaim(@PathVariable Long id) { + insuranceClaimService.deleteInsuranceClaim(id); + return ResponseEntity.noContent().build(); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/InsuranceProviderController.java b/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/InsuranceProviderController.java new file mode 100644 index 0000000..08ffc0e --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/InsuranceProviderController.java @@ -0,0 +1,60 @@ +package com.mirna.hospitalmanagementapi.application.controllers; + +import com.mirna.hospitalmanagementapi.domain.dtos.InsuranceProviderDTO; +import com.mirna.hospitalmanagementapi.domain.entities.InsuranceProvider; +import com.mirna.hospitalmanagementapi.domain.services.InsuranceProviderService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.persistence.EntityNotFoundException; +import jakarta.validation.Valid; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; +import java.util.List; + +@RestController +@RequestMapping("/api/v1.0/insurance-providers") +@Tag(name = "insurance-provider-controller") +public class InsuranceProviderController { + + @Autowired + private InsuranceProviderService insuranceProviderService; + + @PostMapping + @Operation(summary = "เพิ่มผู้ให้บริการประกันรายใหม่เข้าสู่ระบบ", security = @SecurityRequirement(name = "bearer-key")) + public ResponseEntity addInsuranceProvider(@RequestBody @Valid InsuranceProviderDTO insuranceProviderDTO) { + InsuranceProvider newProvider = insuranceProviderService.addInsuranceProvider(insuranceProviderDTO); + return new ResponseEntity<>(newProvider, HttpStatus.CREATED); + } + + @GetMapping + @Operation(summary = "ดึงรายการผู้ให้บริการประกันทั้งหมด", security = @SecurityRequirement(name = "bearer-key")) + public ResponseEntity> getAllInsuranceProviders() { + List providers = insuranceProviderService.getAllInsuranceProviders(); + return ResponseEntity.ok(providers); + } + + @GetMapping("/{id}") + @Operation(summary = "ดึงข้อมูลผู้ให้บริการประกันตามรหัส (ID)", security = @SecurityRequirement(name = "bearer-key")) + public ResponseEntity getInsuranceProviderById(@PathVariable Long id) throws EntityNotFoundException { + InsuranceProvider provider = insuranceProviderService.getInsuranceProviderById(id); + return ResponseEntity.ok(provider); + } + + @PutMapping("/{id}") + @Operation(summary = "อัปเดตข้อมูลผู้ให้บริการประกันที่มีอยู่", security = @SecurityRequirement(name = "bearer-key")) + public ResponseEntity updateInsuranceProvider(@PathVariable Long id, @RequestBody @Valid InsuranceProviderDTO updatedProviderDTO) throws EntityNotFoundException { + InsuranceProvider updatedProvider = insuranceProviderService.updateInsuranceProvider(id, updatedProviderDTO); + return ResponseEntity.ok(updatedProvider); + } + + @DeleteMapping("/{id}") + @Operation(summary = "ลบผู้ให้บริการประกันออกจากระบบ", security = @SecurityRequirement(name = "bearer-key")) + public ResponseEntity deleteInsuranceProvider(@PathVariable Long id) throws EntityNotFoundException { + insuranceProviderService.deleteInsuranceProvider(id); + return ResponseEntity.noContent().build(); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/InventoryItemController.java b/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/InventoryItemController.java new file mode 100644 index 0000000..9bb8bf4 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/InventoryItemController.java @@ -0,0 +1,50 @@ +package com.mirna.hospitalmanagementapi.application.controllers; + +import com.mirna.hospitalmanagementapi.domain.dtos.inventory.InventoryItemDTO; +import com.mirna.hospitalmanagementapi.domain.dtos.inventory.InventoryItemResponseDTO; // Import DTO สำหรับ Response +import com.mirna.hospitalmanagementapi.domain.services.inventory.InventoryItemService; +import jakarta.validation.Valid; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import java.util.UUID; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; + +@RestController +@RequestMapping("/api/v1.0/inventory/items") +public class InventoryItemController { + + @Autowired + private InventoryItemService inventoryItemService; + + @PostMapping + @Operation(summary = "สร้างรายการสินค้าคงคลังใหม่", security = @SecurityRequirement(name = "bearer-key")) + public ResponseEntity createInventoryItem(@RequestBody @Valid InventoryItemDTO itemDTO) { + InventoryItemResponseDTO newItem = inventoryItemService.addInventoryItem(itemDTO); + return ResponseEntity.status(HttpStatus.CREATED).body(newItem); + } + + @GetMapping("/{id}") + @Operation(summary = "ดึงข้อมูลรายการสินค้าคงคลังด้วย ID", security = @SecurityRequirement(name = "bearer-key")) + public ResponseEntity getInventoryItem(@PathVariable UUID id) { + InventoryItemResponseDTO item = inventoryItemService.getInventoryItemById(id); + return ResponseEntity.ok(item); + } + + @PutMapping("/{id}") + @Operation(summary = "อัปเดตข้อมูลรายการสินค้าคงคลัง", security = @SecurityRequirement(name = "bearer-key")) + public ResponseEntity updateInventoryItem(@PathVariable UUID id, @RequestBody @Valid InventoryItemDTO itemDTO) { + InventoryItemResponseDTO updatedItem = inventoryItemService.updateInventoryItem(id, itemDTO); + return ResponseEntity.ok(updatedItem); + } + + @DeleteMapping("/{id}") + @Operation(summary = "ลบรายการสินค้าคงคลัง", security = @SecurityRequirement(name = "bearer-key")) + public ResponseEntity deleteInventoryItem(@PathVariable UUID id) { + inventoryItemService.deleteInventoryItem(id); + return ResponseEntity.noContent().build(); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/InventoryItemTypeController.java b/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/InventoryItemTypeController.java new file mode 100644 index 0000000..6e50156 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/InventoryItemTypeController.java @@ -0,0 +1,27 @@ +package com.mirna.hospitalmanagementapi.application.controllers; + +import com.mirna.hospitalmanagementapi.domain.services.inventory.ItemTypeService; +import com.mirna.hospitalmanagementapi.domain.dtos.inventory.ItemTypeDTO; +import com.mirna.hospitalmanagementapi.domain.entities.inventory.InventoryItemType; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import jakarta.validation.Valid; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/api/v1.0/inventory/item-types") +public class InventoryItemTypeController { + + @Autowired + private ItemTypeService itemTypeService; + + @PostMapping + @Operation(summary = "สร้างประเภทรายการสินค้าคงคลังใหม่", security = @SecurityRequirement(name = "bearer-key")) + public ResponseEntity createItemType(@RequestBody @Valid ItemTypeDTO itemTypeDTO) { + InventoryItemType newItemType = itemTypeService.addItemType(itemTypeDTO); + return ResponseEntity.status(HttpStatus.CREATED).body(newItemType); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/InventorySupplierController.java b/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/InventorySupplierController.java new file mode 100644 index 0000000..809ae3d --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/InventorySupplierController.java @@ -0,0 +1,27 @@ +package com.mirna.hospitalmanagementapi.application.controllers; + +import com.mirna.hospitalmanagementapi.domain.services.inventory.SupplierService; +import com.mirna.hospitalmanagementapi.domain.dtos.inventory.SupplierDTO; +import com.mirna.hospitalmanagementapi.domain.entities.inventory.InventorySupplier; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import jakarta.validation.Valid; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/api/v1.0/inventory/suppliers") +public class InventorySupplierController { + + @Autowired + private SupplierService supplierService; + + @PostMapping + @Operation(summary = "สร้างข้อมูลผู้จำหน่ายสินค้าใหม่", security = @SecurityRequirement(name = "bearer-key")) + public ResponseEntity createSupplier(@RequestBody @Valid SupplierDTO supplierDTO) { + InventorySupplier newSupplier = supplierService.addSupplier(supplierDTO); + return ResponseEntity.status(HttpStatus.CREATED).body(newSupplier); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/InventoryTransactionController.java b/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/InventoryTransactionController.java new file mode 100644 index 0000000..8c02bff --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/InventoryTransactionController.java @@ -0,0 +1,27 @@ +package com.mirna.hospitalmanagementapi.application.controllers; + +import com.mirna.hospitalmanagementapi.domain.services.inventory.InventoryTransactionService; +import com.mirna.hospitalmanagementapi.domain.dtos.inventory.InventoryTransactionDTO; +import com.mirna.hospitalmanagementapi.domain.dtos.inventory.InventoryTransactionResponseDTO; // Import DTO สำหรับ Response +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import jakarta.validation.Valid; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/api/v1.0/inventory/transactions") +public class InventoryTransactionController { + + @Autowired + private InventoryTransactionService transactionService; + + @PostMapping + @Operation(summary = "สร้างรายการธุรกรรมคงคลังใหม่", security = @SecurityRequirement(name = "bearer-key")) + public ResponseEntity createTransaction(@RequestBody @Valid InventoryTransactionDTO transactionDTO) { + InventoryTransactionResponseDTO newTransaction = transactionService.addTransaction(transactionDTO); + return ResponseEntity.status(HttpStatus.CREATED).body(newTransaction); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/LabResultController.java b/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/LabResultController.java new file mode 100644 index 0000000..5d82ac6 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/LabResultController.java @@ -0,0 +1,40 @@ +package com.mirna.hospitalmanagementapi.application.controllers; + +import com.mirna.hospitalmanagementapi.domain.dtos.LabResultDTO; +import com.mirna.hospitalmanagementapi.domain.entities.LabResult; +import com.mirna.hospitalmanagementapi.domain.services.LabResultService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import java.util.UUID; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import org.springframework.security.access.prepost.PreAuthorize; + +@RestController +@RequestMapping("/api/v1.0/lab-results") +public class LabResultController { + + @Autowired + private LabResultService labResultService; + + @PostMapping("/{medicalRecordId}") + @Operation(summary = "สร้างผลแล็บใหม่และเชื่อมโยงกับบันทึกทางการแพทย์", security = @SecurityRequirement(name = "bearer-key")) + public ResponseEntity addLabResult( + @PathVariable Long medicalRecordId, + @RequestBody LabResultDTO labResultDTO) { + + LabResult newLabResult = labResultService.addLabResult(medicalRecordId, labResultDTO); + return new ResponseEntity<>(newLabResult, HttpStatus.CREATED); + } + + @GetMapping("/{id}") + @Operation(summary = "ดึงข้อมูลผลแล็บด้วย ID", security = @SecurityRequirement(name = "bearer-key")) + @PreAuthorize("hasAnyRole('ADMIN', 'DOCTOR') or (hasRole('PATIENT') and @labResultService.isOwner(#id, authentication.principal.id))") + public ResponseEntity getLabResultById(@PathVariable UUID id) { + LabResult labResult = labResultService.getLabResultById(id); + return ResponseEntity.ok(labResult); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/MedicalEquipmentScheduleController.java b/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/MedicalEquipmentScheduleController.java new file mode 100644 index 0000000..774478b --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/MedicalEquipmentScheduleController.java @@ -0,0 +1,53 @@ +package com.mirna.hospitalmanagementapi.application.controllers; + +import com.mirna.hospitalmanagementapi.domain.dtos.MedicalEquipmentScheduleDTO; +import com.mirna.hospitalmanagementapi.domain.entities.MedicalEquipmentSchedule; +import com.mirna.hospitalmanagementapi.domain.services.MedicalEquipmentScheduleService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.persistence.EntityNotFoundException; +import jakarta.validation.Valid; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.web.PageableDefault; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.util.UriComponentsBuilder; +import com.mirna.hospitalmanagementapi.domain.dtos.MedicalEquipmentScheduleResponseDTO; + +import java.net.URI; + +@RestController +@RequestMapping("/api/v1.0/medical-equipment-schedules") +@Tag(name = "medical-equipment-schedule-controller") +public class MedicalEquipmentScheduleController { + + @Autowired + private MedicalEquipmentScheduleService medicalEquipmentScheduleService; + + @PostMapping + @Operation(summary = "เพิ่มตารางเวลาอุปกรณ์ทางการแพทย์ใหม่เข้าสู่ระบบ", security = @SecurityRequirement(name = "bearer-key")) + public ResponseEntity addMedicalEquipmentSchedule(@RequestBody @Valid MedicalEquipmentScheduleDTO medicalEquipmentScheduleDTO, UriComponentsBuilder uriBuilder) { + MedicalEquipmentScheduleResponseDTO newSchedule = medicalEquipmentScheduleService.addMedicalEquipmentSchedule(medicalEquipmentScheduleDTO); + URI uri = uriBuilder.path("/api/v1.0/medical-equipment-schedules/{id}").buildAndExpand(newSchedule.getId()).toUri(); + return ResponseEntity.created(uri).body(newSchedule); + } + + @GetMapping("/{id}") + @Operation(summary = "ดึงข้อมูลตารางเวลาอุปกรณ์ทางการแพทย์ตามรหัส (ID)", security = @SecurityRequirement(name = "bearer-key")) + public ResponseEntity getMedicalEquipmentScheduleById(@PathVariable Long id) throws EntityNotFoundException { + MedicalEquipmentScheduleResponseDTO schedule = medicalEquipmentScheduleService.getMedicalEquipmentScheduleById(id); + return ResponseEntity.ok(schedule); + } + + @GetMapping + @Operation(summary = "ดึงข้อมูลตารางเวลาอุปกรณ์ทางการแพทย์ทั้งหมดแบบแบ่งหน้า (Pagination)", security = @SecurityRequirement(name = "bearer-key")) + public ResponseEntity> getAllMedicalEquipmentSchedules(@PageableDefault(size = 10, page = 0, sort = {"startTime"}) Pageable pageable) { + Page schedules = medicalEquipmentScheduleService.getAllMedicalEquipmentSchedules(pageable); + return ResponseEntity.ok(schedules); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/MedicalImageController.java b/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/MedicalImageController.java new file mode 100644 index 0000000..7eb06b7 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/MedicalImageController.java @@ -0,0 +1,49 @@ +package com.mirna.hospitalmanagementapi.application.controllers; + +import com.mirna.hospitalmanagementapi.domain.entities.MedicalImage; +import com.mirna.hospitalmanagementapi.domain.services.MedicalImageService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.Resource; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; +import java.util.UUID; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import org.springframework.security.access.prepost.PreAuthorize; + +@RestController +@RequestMapping("/api/v1.0/medical-images") +public class MedicalImageController { + + @Autowired + private MedicalImageService medicalImageService; + + @PostMapping(value = "/upload/{medicalRecordId}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + @Operation(summary = "อัปโหลดรูปภาพและเชื่อมโยงกับบันทึกทางการแพทย์", security = @SecurityRequirement(name = "bearer-key")) + public ResponseEntity uploadImage( + @PathVariable Long medicalRecordId, + @RequestParam("file") MultipartFile file, + @RequestParam("imageType") String imageType, + @RequestParam("notes") String notes) { + + MedicalImage uploadedImage = medicalImageService.uploadImage(medicalRecordId, file, imageType, notes); + return new ResponseEntity<>(uploadedImage, HttpStatus.CREATED); + } + + @GetMapping("/download/{id}") + @Operation(summary = "ดาวน์โหลดรูปภาพทางการแพทย์ด้วย ID", security = @SecurityRequirement(name = "bearer-key")) + @PreAuthorize("hasAnyRole('ADMIN', 'DOCTOR') or (hasRole('PATIENT') and @medicalImageService.isOwner(#id, authentication.principal.id))") + public ResponseEntity downloadImage(@PathVariable UUID id) { + Resource file = medicalImageService.downloadImage(id); + + return ResponseEntity.ok() + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + id + ".jpg\"") + .contentType(MediaType.IMAGE_JPEG) // สามารถเปลี่ยนประเภทไฟล์ตามต้องการ + .body(file); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/MedicalRecordController.java b/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/MedicalRecordController.java new file mode 100644 index 0000000..816541b --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/MedicalRecordController.java @@ -0,0 +1,58 @@ +package com.mirna.hospitalmanagementapi.application.controllers; + +import com.mirna.hospitalmanagementapi.domain.entities.MedicalRecord; +import com.mirna.hospitalmanagementapi.domain.services.MedicalRecordService; +import com.mirna.hospitalmanagementapi.domain.dtos.MedicalRecordDTO; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import org.springframework.security.access.prepost.PreAuthorize; + +@RestController +@RequestMapping("/api/v1.0/medical-records") +public class MedicalRecordController { + + @Autowired + private MedicalRecordService medicalRecordService; + + @PostMapping + @Operation(summary = "สร้างบันทึกทางการแพทย์ใหม่", security = @SecurityRequirement(name = "bearer-key")) + public ResponseEntity addMedicalRecord(@RequestBody MedicalRecordDTO medicalRecordDTO) { + MedicalRecord newRecord = medicalRecordService.addMedicalRecord(medicalRecordDTO); + return ResponseEntity.status(HttpStatus.CREATED).body(newRecord); + } + + @GetMapping("/{id}") + @Operation(summary = "ดึงข้อมูลบันทึกทางการแพทย์ด้วย ID (ไม่รวมรายละเอียดเพิ่มเติม)", security = @SecurityRequirement(name = "bearer-key")) + @PreAuthorize("hasAnyRole('ADMIN', 'DOCTOR') or (hasRole('PATIENT') and @medicalRecordService.isOwner(#id, authentication.principal.id))") + public ResponseEntity getMedicalRecordById(@PathVariable Long id) { + MedicalRecord medicalRecord = medicalRecordService.getMedicalRecordById(id); + return ResponseEntity.ok(medicalRecord); + } + + // Endpoint ใหม่สำหรับดึงข้อมูลบันทึกทางการแพทย์พร้อมรายละเอียดทั้งหมด + /*@GetMapping("/{id}/details") + @Operation(summary = "ดึงข้อมูลบันทึกทางการแพทย์พร้อมรายละเอียดทั้งหมด", security = @SecurityRequirement(name = "bearer-key")) + public ResponseEntity getMedicalRecordDetails(@PathVariable Long id) { + MedicalRecord medicalRecord = medicalRecordService.getMedicalRecordDetails(id); + return ResponseEntity.ok(medicalRecord); + }*/ + + @PutMapping("/{id}") + @Operation(summary = "อัปเดตบันทึกทางการแพทย์", security = @SecurityRequirement(name = "bearer-key")) + public ResponseEntity updateMedicalRecord(@PathVariable Long id, @RequestBody MedicalRecordDTO medicalRecordDTO) { + MedicalRecord updatedRecord = medicalRecordService.updateMedicalRecord(id, medicalRecordDTO); + return ResponseEntity.ok(updatedRecord); + } + + @DeleteMapping("/{id}") + @Operation(summary = "ลบบันทึกทางการแพทย์", security = @SecurityRequirement(name = "bearer-key")) + public ResponseEntity deleteMedicalRecord(@PathVariable Long id) { + medicalRecordService.deleteMedicalRecord(id); + return ResponseEntity.noContent().build(); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/NurseController.java b/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/NurseController.java new file mode 100644 index 0000000..c9cd050 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/NurseController.java @@ -0,0 +1,66 @@ +package com.mirna.hospitalmanagementapi.application.controllers; + +import com.mirna.hospitalmanagementapi.domain.dtos.NurseDTO; +import com.mirna.hospitalmanagementapi.domain.entities.Nurse; +import com.mirna.hospitalmanagementapi.domain.services.NurseService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.persistence.EntityNotFoundException; +import jakarta.validation.Valid; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.web.PageableDefault; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.util.UriComponentsBuilder; + +import java.net.URI; + +@RestController +@RequestMapping("/api/v1.0/nurses") +@Tag(name = "nurse-controller") +public class NurseController { + + @Autowired + private NurseService nurseService; + + @PostMapping + @Operation(summary = "เพิ่มข้อมูลพยาบาลใหม่เข้าสู่ระบบ", security = @SecurityRequirement(name = "bearer-key")) + public ResponseEntity addNurse(@RequestBody @Valid NurseDTO nurseDTO, UriComponentsBuilder uriBuilder) { + Nurse newNurse = nurseService.addNurse(nurseDTO); + URI uri = uriBuilder.path("/api/v1.0/nurses/{id}").buildAndExpand(newNurse.getId()).toUri(); + return ResponseEntity.created(uri).body(newNurse); + } + + @GetMapping + @Operation(summary = "ดึงรายการพยาบาลทั้งหมด", security = @SecurityRequirement(name = "bearer-key")) + public ResponseEntity> getAllNurses(@PageableDefault(size = 10, page = 0, sort = {"name"}) Pageable pageable) { + Page nurses = nurseService.getAllNurses(pageable); + return ResponseEntity.ok(nurses); + } + + @GetMapping("/{id}") + @Operation(summary = "ดึงข้อมูลพยาบาลตามรหัส (ID)", security = @SecurityRequirement(name = "bearer-key")) + public ResponseEntity getNurseById(@PathVariable Long id) throws EntityNotFoundException { + Nurse nurse = nurseService.getNurseById(id); + return ResponseEntity.ok(nurse); + } + + @PutMapping("/{id}") + @Operation(summary = "อัปเดตข้อมูลของพยาบาลที่มีอยู่", security = @SecurityRequirement(name = "bearer-key")) + public ResponseEntity updateNurse(@PathVariable Long id, @RequestBody @Valid NurseDTO nurseDTO) throws EntityNotFoundException { + Nurse updatedNurse = nurseService.updateNurse(id, nurseDTO); + return ResponseEntity.ok(updatedNurse); + } + + @DeleteMapping("/{id}") + @Operation(summary = "ลบพยาบาลออกจากระบบ", security = @SecurityRequirement(name = "bearer-key")) + public ResponseEntity deleteNurse(@PathVariable Long id) throws EntityNotFoundException { + nurseService.deleteNurse(id); + return ResponseEntity.noContent().build(); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/NurseScheduleController.java b/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/NurseScheduleController.java new file mode 100644 index 0000000..983d3e7 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/NurseScheduleController.java @@ -0,0 +1,57 @@ +package com.mirna.hospitalmanagementapi.application.controllers; + +import com.mirna.hospitalmanagementapi.domain.dtos.NurseScheduleDTO; +import com.mirna.hospitalmanagementapi.domain.entities.NurseSchedule; +import com.mirna.hospitalmanagementapi.domain.services.NurseScheduleService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.persistence.EntityNotFoundException; +import jakarta.validation.Valid; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.web.PageableDefault; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.util.UriComponentsBuilder; + +import java.net.URI; + +@RestController +@RequestMapping("/api/v1.0/nurse-schedules") +@Tag(name = "nurse-schedule-controller") +public class NurseScheduleController { + + @Autowired + private NurseScheduleService nurseScheduleService; + + @PostMapping + @Operation(summary = "เพิ่มตารางเวรพยาบาลใหม่", security = @SecurityRequirement(name = "bearer-key")) + public ResponseEntity addNurseSchedule(@RequestBody @Valid NurseScheduleDTO nurseScheduleDTO, UriComponentsBuilder uriBuilder) { + try { + NurseSchedule newSchedule = nurseScheduleService.addNurseSchedule(nurseScheduleDTO); + URI uri = uriBuilder.path("/api/v1.0/nurse-schedules/{id}").buildAndExpand(newSchedule.getId()).toUri(); + return ResponseEntity.created(uri).body(newSchedule); + } catch (IllegalArgumentException e) { + // จับ Exception ที่มาจาก Service + return ResponseEntity.badRequest().body(e.getMessage()); + } + } + + @GetMapping("/{id}") + @Operation(summary = "ดึงข้อมูลตารางเวรพยาบาลตามรหัส (ID)", security = @SecurityRequirement(name = "bearer-key")) + public ResponseEntity getNurseScheduleById(@PathVariable Long id) throws EntityNotFoundException { + NurseSchedule schedule = nurseScheduleService.getNurseScheduleById(id); + return ResponseEntity.ok(schedule); + } + + @GetMapping + @Operation(summary = "ดึงข้อมูลตารางเวรพยาบาลทั้งหมดแบบมีการแบ่งหน้า (pagination)", security = @SecurityRequirement(name = "bearer-key")) + public ResponseEntity> getAllNurseSchedules(@PageableDefault(size = 10, page = 0, sort = {"startTime"}) Pageable pageable) { + Page schedules = nurseScheduleService.getAllNurseSchedules(pageable); + return ResponseEntity.ok(schedules); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/OperatingRoomController.java b/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/OperatingRoomController.java new file mode 100644 index 0000000..30f5f4a --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/OperatingRoomController.java @@ -0,0 +1,66 @@ +package com.mirna.hospitalmanagementapi.application.controllers; + +import com.mirna.hospitalmanagementapi.domain.dtos.OperatingRoomDTO; +import com.mirna.hospitalmanagementapi.domain.entities.OperatingRoom; +import com.mirna.hospitalmanagementapi.domain.services.OperatingRoomService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.persistence.EntityNotFoundException; +import jakarta.validation.Valid; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.web.PageableDefault; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.util.UriComponentsBuilder; + +import java.net.URI; + +@RestController +@RequestMapping("/api/v1.0/operating-rooms") +@Tag(name = "operating-room-controller") +public class OperatingRoomController { + + @Autowired + private OperatingRoomService operatingRoomService; + + @PostMapping + @Operation(summary = "เพิ่มห้องผ่าตัดใหม่เข้าสู่ระบบ", security = @SecurityRequirement(name = "bearer-key")) + public ResponseEntity addOperatingRoom(@RequestBody @Valid OperatingRoomDTO operatingRoomDTO, UriComponentsBuilder uriBuilder) { + OperatingRoom newRoom = operatingRoomService.addOperatingRoom(operatingRoomDTO); + URI uri = uriBuilder.path("/api/v1.0/operating-rooms/{id}").buildAndExpand(newRoom.getId()).toUri(); + return ResponseEntity.created(uri).body(newRoom); + } + + @GetMapping + @Operation(summary = "ดึงรายการห้องผ่าตัดทั้งหมด", security = @SecurityRequirement(name = "bearer-key")) + public ResponseEntity> getAllOperatingRooms(@PageableDefault(size = 10, page = 0, sort = {"id"}) Pageable pageable) { + Page rooms = operatingRoomService.getAllOperatingRooms(pageable); + return ResponseEntity.ok(rooms); + } + + @GetMapping("/{id}") + @Operation(summary = "ดึงข้อมูลห้องผ่าตัดตามรหัส (ID)", security = @SecurityRequirement(name = "bearer-key")) + public ResponseEntity getOperatingRoomById(@PathVariable Long id) throws EntityNotFoundException { + OperatingRoom room = operatingRoomService.getOperatingRoomById(id); + return ResponseEntity.ok(room); + } + + @PutMapping("/{id}") + @Operation(summary = "อัปเดตข้อมูลของห้องผ่าตัดที่มีอยู่", security = @SecurityRequirement(name = "bearer-key")) + public ResponseEntity updateOperatingRoom(@PathVariable Long id, @RequestBody @Valid OperatingRoomDTO operatingRoomDTO) throws EntityNotFoundException { + OperatingRoom updatedRoom = operatingRoomService.updateOperatingRoom(id, operatingRoomDTO); + return ResponseEntity.ok(updatedRoom); + } + + @DeleteMapping("/{id}") + @Operation(summary = "ลบห้องผ่าตัดออกจากระบบ", security = @SecurityRequirement(name = "bearer-key")) + public ResponseEntity deleteOperatingRoom(@PathVariable Long id) throws EntityNotFoundException { + operatingRoomService.deleteOperatingRoom(id); + return ResponseEntity.noContent().build(); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/OperatingRoomScheduleController.java b/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/OperatingRoomScheduleController.java new file mode 100644 index 0000000..ef7acf3 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/OperatingRoomScheduleController.java @@ -0,0 +1,52 @@ +package com.mirna.hospitalmanagementapi.application.controllers; + +import com.mirna.hospitalmanagementapi.domain.dtos.OperatingRoomScheduleDTO; +import com.mirna.hospitalmanagementapi.domain.entities.OperatingRoomSchedule; +import com.mirna.hospitalmanagementapi.domain.services.OperatingRoomScheduleService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.persistence.EntityNotFoundException; +import jakarta.validation.Valid; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.web.PageableDefault; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.util.UriComponentsBuilder; + +import java.net.URI; + +@RestController +@RequestMapping("/api/v1.0/operating-room-schedules") +@Tag(name = "operating-room-schedule-controller") +public class OperatingRoomScheduleController { + + @Autowired + private OperatingRoomScheduleService operatingRoomScheduleService; + + @PostMapping + @Operation(summary = "เพิ่มตารางเวลาห้องผ่าตัดใหม่เข้าสู่ระบบ", security = @SecurityRequirement(name = "bearer-key")) + public ResponseEntity addOperatingRoomSchedule(@RequestBody @Valid OperatingRoomScheduleDTO operatingRoomScheduleDTO, UriComponentsBuilder uriBuilder) { + OperatingRoomSchedule newSchedule = operatingRoomScheduleService.addOperatingRoomSchedule(operatingRoomScheduleDTO); + URI uri = uriBuilder.path("/api/v1.0/operating-room-schedules/{id}").buildAndExpand(newSchedule.getId()).toUri(); + return ResponseEntity.created(uri).body(newSchedule); + } + + @GetMapping("/{id}") + @Operation(summary = "ดึงข้อมูลตารางเวลาห้องผ่าตัดตามรหัส (ID)", security = @SecurityRequirement(name = "bearer-key")) + public ResponseEntity getOperatingRoomScheduleById(@PathVariable Long id) throws EntityNotFoundException { + OperatingRoomSchedule schedule = operatingRoomScheduleService.getOperatingRoomScheduleById(id); + return ResponseEntity.ok(schedule); + } + + @GetMapping + @Operation(summary = "ดึงข้อมูลตารางเวลาห้องผ่าตัดทั้งหมดแบบแบ่งหน้า (Pagination)", security = @SecurityRequirement(name = "bearer-key")) + public ResponseEntity> getAllOperatingRoomSchedules(@PageableDefault(size = 10, page = 0, sort = {"startTime"}) Pageable pageable) { + Page schedules = operatingRoomScheduleService.getAllOperatingRoomSchedules(pageable); + return ResponseEntity.ok(schedules); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/PaymentController.java b/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/PaymentController.java new file mode 100644 index 0000000..c3c974d --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/PaymentController.java @@ -0,0 +1,41 @@ +package com.mirna.hospitalmanagementapi.infrastructure.controllers; + +import com.mirna.hospitalmanagementapi.domain.dtos.PaymentDTO; +import com.mirna.hospitalmanagementapi.domain.entities.Payment; +import com.mirna.hospitalmanagementapi.domain.services.PaymentService; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import jakarta.validation.Valid; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.web.PageableDefault; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/api/v1.0/payments") +@SecurityRequirement(name = "bearer-key") +public class PaymentController { + + @Autowired + private PaymentService paymentService; + + @PostMapping + public ResponseEntity createPayment(@RequestBody @Valid PaymentDTO paymentDTO) { + Payment newPayment = paymentService.createPayment(paymentDTO); + return ResponseEntity.status(HttpStatus.CREATED).body(newPayment); + } + + @GetMapping("/{id}") + public ResponseEntity getPaymentById(@PathVariable Long id) { + Payment payment = paymentService.getPaymentById(id); + return ResponseEntity.ok(payment); + } + + @GetMapping + public ResponseEntity> getAllPayments(@PageableDefault(size = 10, sort = {"paymentDate"}) Pageable pageable) { + Page payments = paymentService.getAllPayments(pageable); + return ResponseEntity.ok(payments); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/PrescriptionController.java b/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/PrescriptionController.java new file mode 100644 index 0000000..297baa7 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/PrescriptionController.java @@ -0,0 +1,58 @@ +package com.mirna.hospitalmanagementapi.application.controllers; + +import com.mirna.hospitalmanagementapi.domain.dtos.PrescriptionDTO; +import com.mirna.hospitalmanagementapi.domain.dtos.PrescriptionResponseDTO; +import com.mirna.hospitalmanagementapi.domain.entities.Prescription; +import com.mirna.hospitalmanagementapi.domain.exceptions.ConsultationValidationException; +import com.mirna.hospitalmanagementapi.domain.services.PrescriptionService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.persistence.EntityNotFoundException; +import jakarta.validation.Valid; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.UUID; +import org.springframework.security.access.prepost.PreAuthorize; + +@RestController +@RequestMapping("/api/v1.0/prescriptions") +@Tag(name = "prescription-controller") +public class PrescriptionController { + + @Autowired + private PrescriptionService prescriptionService; + + @PostMapping + @Operation(summary = "สร้างใบสั่งยาใหม่", security = @SecurityRequirement(name = "bearer-key")) + public ResponseEntity createPrescription(@RequestBody @Valid PrescriptionDTO dto) + throws ConsultationValidationException, EntityNotFoundException { + + Prescription savedPrescription = prescriptionService.createPrescription(dto); + PrescriptionResponseDTO responseDTO = new PrescriptionResponseDTO(savedPrescription); + return ResponseEntity.ok(responseDTO); + } + + @GetMapping("/{id}") + @Operation(summary = "ดึงข้อมูลใบสั่งยาตาม ID", security = @SecurityRequirement(name = "bearer-key")) + @PreAuthorize("hasAnyRole('ADMIN', 'DOCTOR') or (hasRole('PATIENT') and @prescriptionService.isOwner(#id, authentication.principal.id))") + public ResponseEntity getPrescriptionById(@PathVariable UUID id) + throws EntityNotFoundException { + + Prescription prescription = prescriptionService.getPrescriptionById(id); + PrescriptionResponseDTO responseDTO = new PrescriptionResponseDTO(prescription); + return ResponseEntity.ok(responseDTO); + } + + @PutMapping("/{id}") + @Operation(summary = "แก้ไขข้อมูลใบสั่งยาตาม ID", security = @SecurityRequirement(name = "bearer-key")) + public ResponseEntity updatePrescription(@PathVariable UUID id, @RequestBody @Valid PrescriptionDTO dto) + throws ConsultationValidationException, EntityNotFoundException { + + Prescription updatedPrescription = prescriptionService.updatePrescription(id, dto); + PrescriptionResponseDTO responseDTO = new PrescriptionResponseDTO(updatedPrescription); + return ResponseEntity.ok(responseDTO); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/ReportController.java b/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/ReportController.java new file mode 100644 index 0000000..14d98cb --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/ReportController.java @@ -0,0 +1,64 @@ +package com.mirna.hospitalmanagementapi.application.controllers; + +import com.mirna.hospitalmanagementapi.domain.services.ReportService; +import com.mirna.hospitalmanagementapi.domain.dtos.FinancialReportDTO; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.format.annotation.DateTimeFormat; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import java.time.LocalDate; +import java.util.List; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.web.PageableDefault; +import com.mirna.hospitalmanagementapi.domain.dtos.AppointmentReportDTO; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import com.mirna.hospitalmanagementapi.domain.dtos.LowStockItemDTO; +import org.springframework.http.MediaType; + +@RestController +@RequestMapping("/api/v1.0/reports") +@SecurityRequirement(name = "bearer-key") +public class ReportController { + + @Autowired + private ReportService reportService; + + @GetMapping("/financial-overview") + public ResponseEntity> getFinancialOverview( + @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate startDate, + @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate endDate) { + + List financialData = reportService.getFinancialReport(startDate, endDate); + + if (financialData.isEmpty()) { + return ResponseEntity.noContent().build(); + } + + return ResponseEntity.ok(financialData); + } + + @GetMapping("/inventory/low-stock") + public ResponseEntity> getLowStockReport(@PageableDefault(size = 10, sort = "itemName") Pageable pageable) { + Page lowStockItems = reportService.getLowStockItems(pageable); + + if (lowStockItems.isEmpty()) { + return ResponseEntity.noContent().build(); + } + + return ResponseEntity.ok(lowStockItems); + } + + @GetMapping(value = "/appointments", produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity getAppointmentReport( + @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate startDate, + @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate endDate) { + + AppointmentReportDTO report = reportService.getAppointmentReport(startDate, endDate); + return ResponseEntity.ok(report); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/StaffController.java b/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/StaffController.java new file mode 100644 index 0000000..e7600f6 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/StaffController.java @@ -0,0 +1,43 @@ +package com.mirna.hospitalmanagementapi.application.controllers; + +import com.mirna.hospitalmanagementapi.domain.entities.Staff; +import com.mirna.hospitalmanagementapi.domain.services.StaffService; +import jakarta.validation.Valid; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.util.UriComponentsBuilder; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import org.springframework.transaction.annotation.Transactional; + +import java.net.URI; + +/** + * REST controller for managing staff. + * + * @author FlookSP + * @version 1.0 + */ +@RestController +@RequestMapping("/api/v1.0/staff") +@SecurityRequirement(name = "bearer-key") +public class StaffController { + + @Autowired + private StaffService staffService; + + @PostMapping + @Transactional + @Operation( + summary = "เพิ่มเจ้าหน้าที่ใหม่", + description = "ใช้สำหรับลงทะเบียนเจ้าหน้าที่ใหม่เข้าสู่ระบบ โดยจะสร้างบัญชีผู้ใช้ (User Account) ให้เจ้าหน้าที่พร้อมกัน", + tags = {"Staff"} + ) + public ResponseEntity addStaff(@RequestBody @Valid Staff staff, UriComponentsBuilder uriBuilder) { + Staff createdStaff = staffService.addStaff(staff); + URI uri = uriBuilder.path("/api/v1.0/staff/{id}").buildAndExpand(createdStaff.getId()).toUri(); + return ResponseEntity.created(uri).body(createdStaff); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/UserController.java b/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/UserController.java new file mode 100644 index 0000000..a30363f --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/UserController.java @@ -0,0 +1,130 @@ +package com.mirna.hospitalmanagementapi.application.controllers; + +import com.mirna.hospitalmanagementapi.domain.dtos.auth.UserUpdatePasswordDTO; +import com.mirna.hospitalmanagementapi.domain.dtos.auth.UserUpdateRoleDTO; +import com.mirna.hospitalmanagementapi.domain.dtos.auth.UserUpdateUsernameDTO; +import com.mirna.hospitalmanagementapi.domain.entities.auth.User; +import com.mirna.hospitalmanagementapi.domain.enums.Role; +import com.mirna.hospitalmanagementapi.domain.services.UserService; +import jakarta.validation.Valid; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.*; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import org.springframework.security.access.AccessDeniedException; + +/** + * REST controller for managing users. + * + * @author FlookSP + * @version 1.2 + */ +@RestController +@RequestMapping("/api/v1.0/users") +public class UserController { + + @Autowired + private UserService userService; + + // Helper method เพื่อตรวจสอบสิทธิ์การเข้าถึง + private void checkUserAccess(Long targetUserId) { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + User currentUser = (User) authentication.getPrincipal(); + + // อนุญาตถ้าผู้ใช้ปัจจุบันเป็น ADMIN หรือเป็นเจ้าของข้อมูล + boolean isAdmin = currentUser.getRole().equals(Role.ROLE_ADMIN); + boolean isOwner = currentUser.getId().equals(targetUserId); + + if (!isAdmin && !isOwner) { + throw new AccessDeniedException("You do not have permission to access this resource."); + } + } + + // Endpoint สำหรับอัปเดตชื่อผู้ใช้งาน (login) + @PutMapping("/{id}/username") + @Transactional + @Operation(summary = "อัปเดตชื่อผู้ใช้งาน", security = @SecurityRequirement(name = "bearer-key")) + public ResponseEntity updateUsername(@PathVariable Long id, @RequestBody @Valid UserUpdateUsernameDTO userUpdateUsernameDTO) { + checkUserAccess(id); + User updatedUser = userService.updateUsername(id, userUpdateUsernameDTO); + return ResponseEntity.ok(updatedUser); + } + + // Endpoint สำหรับอัปเดตรหัสผ่าน + @PutMapping("/{id}/password") + @Transactional + @Operation(summary = "อัปเดตรหัสผ่าน", security = @SecurityRequirement(name = "bearer-key")) + public ResponseEntity updatePassword(@PathVariable Long id, @RequestBody @Valid UserUpdatePasswordDTO userUpdatePasswordDTO) { + checkUserAccess(id); + User updatedUser = userService.updatePassword(id, userUpdatePasswordDTO); + return ResponseEntity.ok(updatedUser); + } + + // Endpoint สำหรับอัปเดต Role (สำหรับ Admin เท่านั้น) + @PutMapping("/{id}/role") + @Transactional + @Operation(summary = "อัปเดตบทบาท (Role) ของผู้ใช้งาน", security = @SecurityRequirement(name = "bearer-key")) + public ResponseEntity updateRole(@PathVariable Long id, @RequestBody @Valid UserUpdateRoleDTO userUpdateRoleDTO) { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + User currentUser = (User) authentication.getPrincipal(); + + if (!currentUser.getRole().equals(Role.ROLE_ADMIN)) { + throw new AccessDeniedException("You do not have permission to update user roles."); + } + User updatedUser = userService.updateRole(id, userUpdateRoleDTO); + return ResponseEntity.ok(updatedUser); + } + + // Endpoint ใหม่สำหรับปิดใช้งานบัญชีผู้ใช้งาน + @PatchMapping("/{id}/deactivate") + @Transactional + @Operation(summary = "ปิดใช้งานบัญชีผู้ใช้งาน", security = @SecurityRequirement(name = "bearer-key")) + public ResponseEntity deactivateUser(@PathVariable Long id) { + // เฉพาะ ADMIN เท่านั้นที่สามารถปิดใช้งานบัญชีได้ + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + User currentUser = (User) authentication.getPrincipal(); + + if (!currentUser.getRole().equals(Role.ROLE_ADMIN)) { + throw new AccessDeniedException("You do not have permission to deactivate user accounts."); + } + // ตรวจสอบว่า ADMIN จะไม่สามารถปิดใช้งานบัญชีของตัวเองได้ + if (currentUser.getId().equals(id)) { + throw new IllegalArgumentException("You cannot deactivate your own account."); + } + userService.deactivateUser(id); + return ResponseEntity.noContent().build(); + } + + // Endpoint ใหม่สำหรับเปิดใช้งานบัญชีผู้ใช้งาน + @PatchMapping("/{id}/activate") + @Transactional + @Operation(summary = "เปิดใช้งานบัญชีผู้ใช้งาน", security = @SecurityRequirement(name = "bearer-key")) + public ResponseEntity activateUser(@PathVariable Long id) { + // เฉพาะ ADMIN เท่านั้นที่สามารถเปิดใช้งานบัญชีได้ + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + User currentUser = (User) authentication.getPrincipal(); + + if (!currentUser.getRole().equals(Role.ROLE_ADMIN)) { + throw new AccessDeniedException("You do not have permission to activate user accounts."); + } + + userService.activateUser(id); + return ResponseEntity.noContent().build(); + } + + @ExceptionHandler(AccessDeniedException.class) + public ResponseEntity handleAccessDeniedException(AccessDeniedException ex) { + return ResponseEntity.status(HttpStatus.FORBIDDEN).body(ex.getMessage()); + } + + @ExceptionHandler(IllegalArgumentException.class) + public ResponseEntity handleIllegalArgumentException(IllegalArgumentException ex) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ex.getMessage()); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/auth/AuthenticationController.java b/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/auth/AuthenticationController.java index 115bbff..e212184 100644 --- a/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/auth/AuthenticationController.java +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/controllers/auth/AuthenticationController.java @@ -1,10 +1,8 @@ package com.mirna.hospitalmanagementapi.application.controllers.auth; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -14,8 +12,28 @@ import com.mirna.hospitalmanagementapi.domain.dtos.auth.UserDTO; import com.mirna.hospitalmanagementapi.domain.entities.auth.User; import com.mirna.hospitalmanagementapi.domain.services.auth.AuthService; import com.mirna.hospitalmanagementapi.domain.services.auth.jwt.TokenService; +import com.mirna.hospitalmanagementapi.domain.services.PatientService; import jakarta.validation.Valid; +import com.mirna.hospitalmanagementapi.domain.dtos.auth.PatientRegistrationDTO; +import com.mirna.hospitalmanagementapi.domain.dtos.auth.DoctorRegistrationDTO; +import com.mirna.hospitalmanagementapi.domain.dtos.auth.LinkPatientToUserDTO; +import jakarta.persistence.EntityNotFoundException; +import jakarta.persistence.PersistenceException; + +import com.mirna.hospitalmanagementapi.domain.dtos.auth.StaffRegistrationDTO; +import com.mirna.hospitalmanagementapi.domain.entities.Staff; + +import org.springframework.transaction.annotation.Transactional; +import io.swagger.v3.oas.annotations.Operation; + +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.access.AccessDeniedException; +import com.mirna.hospitalmanagementapi.domain.enums.Role; + +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import com.mirna.hospitalmanagementapi.domain.dtos.auth.NurseRegistrationDTO; /** * A Spring REST controller for managing authentication and user information. @@ -26,46 +44,127 @@ import jakarta.validation.Valid; @RestController @RequestMapping("/api/auth") public class AuthenticationController { - - @Autowired - private AuthService authService; - - @Autowired - private TokenService tokenService; - - /** - * Performs the user login - * - * @param userDTO A data transfer object containing the user data to perform the login - * - * @return The authorization token if successful, or an unauthorized status if there is an error. - */ - @PostMapping(value = "/login") - public ResponseEntity login(@RequestBody @Valid UserDTO userDTO) { - - Authentication auth = authService.login(userDTO); - - User authenticatedUser = (User) auth.getPrincipal(); - - String token = tokenService.generateToken(authenticatedUser); - - return ResponseEntity.ok(token); - } - - /** - * Performs the user registration - * - * @param userDTO A data transfer object containing the user data to perform the registration - * - * @return The registered user if successful, or null if there is an error. - */ - @PostMapping(value = "/register") - public ResponseEntity register(@RequestBody @Valid UserDTO userDTO) { - - User user = authService.register(userDTO); - - return ResponseEntity.ok(user); - } + @Autowired + private AuthService authService; -} + @Autowired + private TokenService tokenService; + + @Autowired + private PatientService patientService; + + /** + * Performs the user login + * + * @param userDTO A data transfer object containing the user data to perform the login + * * @return The authorization token if successful, or an unauthorized status if there is an error. + */ + @PostMapping(value = "/login") + public ResponseEntity login(@RequestBody @Valid UserDTO userDTO) { + + var auth = authService.login(userDTO); + + var authenticatedUser = (User) auth.getPrincipal(); + + String token = tokenService.generateToken(authenticatedUser); + + return ResponseEntity.ok(token); + } + + /** + * Performs the user registration + * + * @param userDTO A data transfer object containing the user data to perform the registration + * * @return The registered user if successful, or null if there is an error. + */ + /*@PostMapping(value = "/register") + public ResponseEntity register(@RequestBody @Valid UserDTO userDTO) { + User user = authService.register(userDTO); + return ResponseEntity.ok(user); + }*/ + + /** + * Performs the user registration (patients) + * + * @param registrationDTO Data transfer object containing user and patient creation + * @return A user object including the credentials + */ + @PostMapping(value = "/register-patient") + public ResponseEntity registerPatient(@RequestBody @Valid PatientRegistrationDTO registrationDTO) { + User user = authService.registerPatient(registrationDTO); + return ResponseEntity.ok(user); + } + + /** + * Registers a new user account for a doctor and links it to an existing doctor record. + * + * @param registrationDTO Data transfer object containing user credentials and CRM for linking + * @return A new user object if registration and linking are successful. + */ + @PostMapping(value = "/register-doctor-and-link") + public ResponseEntity registerDoctor(@RequestBody @Valid DoctorRegistrationDTO registrationDTO) { + try { + User newUser = authService.registerDoctor(registrationDTO); + return ResponseEntity.status(HttpStatus.CREATED).body(newUser); + } catch (PersistenceException e) { + return ResponseEntity.badRequest().body(e.getMessage()); + } + } + + /** + * Links an existing patient record to a new user account + * + * @param linkPatientToUserDTO A data transfer object containing the patient ID and new user data + * + * @return A success message if the linking is successful, or an error message otherwise + */ + @PostMapping(value = "/link-patient-to-user") + public ResponseEntity linkPatientToUser(@RequestBody @Valid LinkPatientToUserDTO linkPatientToUserDTO) { + try { + patientService.linkPatientToUser(linkPatientToUserDTO); + return ResponseEntity.ok("Patient successfully linked to a new user account."); + } catch (IllegalArgumentException e) { + return ResponseEntity.badRequest().body(e.getMessage()); + } catch (EntityNotFoundException e) { + return ResponseEntity.notFound().build(); + } + } + + /** + * Registers a new user account for a nurse and links it to an existing nurse record. + * + * @param registrationDTO Data transfer object containing user credentials and license number for linking + * @return A new user object if registration and linking are successful. + */ + @PostMapping(value = "/register-nurse-and-link") + public ResponseEntity registerNurse(@RequestBody @Valid NurseRegistrationDTO registrationDTO) { + try { + User newUser = authService.registerNurseAndLink(registrationDTO); + return ResponseEntity.status(HttpStatus.CREATED).body(newUser); + } catch (PersistenceException e) { + return ResponseEntity.badRequest().body(e.getMessage()); + } + } + + /*@PostMapping("/register-staff") + @Transactional + @Operation( + summary = "ลงทะเบียนเจ้าหน้าที่ใหม่", + security = @SecurityRequirement(name = "bearer-key") // เพิ่มบรรทัดนี้ + ) + public ResponseEntity registerStaff(@RequestBody @Valid StaffRegistrationDTO dto) { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (!authentication.getAuthorities().stream().anyMatch(a -> a.getAuthority().equals(Role.ROLE_ADMIN.name()))) { + throw new AccessDeniedException("Only ADMIN can register a new staff member."); + } + + // ตรวจสอบว่า role ที่ต้องการลงทะเบียนเป็น Staff roles + if (dto.role() != Role.ROLE_NURSE && dto.role() != Role.ROLE_RECEPTIONIST && dto.role() != Role.ROLE_PHARMACIST) { + throw new IllegalArgumentException("Invalid role for staff registration. Allowed roles are: NURSE, RECEPTIONIST, PHARMACIST"); + } + + Staff createdStaff = authService.registerStaff(dto); + return ResponseEntity.status(HttpStatus.CREATED).body(createdStaff); + }*/ +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/services/BillingServiceImpl.java b/src/main/java/com/mirna/hospitalmanagementapi/application/services/BillingServiceImpl.java new file mode 100644 index 0000000..b809e2e --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/services/BillingServiceImpl.java @@ -0,0 +1,89 @@ +package com.mirna.hospitalmanagementapi.application.services; + +import com.mirna.hospitalmanagementapi.domain.entities.Billing; +import com.mirna.hospitalmanagementapi.domain.entities.BillableItem; +import com.mirna.hospitalmanagementapi.domain.entities.MedicalRecord; +import com.mirna.hospitalmanagementapi.domain.dtos.BillingDTO; +import com.mirna.hospitalmanagementapi.domain.dtos.BillableItemDTO; +import com.mirna.hospitalmanagementapi.domain.enums.PaymentStatus; +import com.mirna.hospitalmanagementapi.domain.repositories.BillingRepository; +import com.mirna.hospitalmanagementapi.domain.repositories.MedicalRecordRepository; +import com.mirna.hospitalmanagementapi.domain.services.BillingService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import jakarta.persistence.EntityNotFoundException; +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; +import java.util.stream.Collectors; + +@Service +public class BillingServiceImpl implements BillingService { + + @Autowired + private BillingRepository billingRepository; + + @Autowired + private MedicalRecordRepository medicalRecordRepository; + + @Override + public Billing createBilling(BillingDTO billingDTO) { + MedicalRecord medicalRecord = medicalRecordRepository.findById(billingDTO.getMedicalRecordId()) + .orElseThrow(() -> new EntityNotFoundException("Medical record not found with ID: " + billingDTO.getMedicalRecordId())); + + Billing billing = new Billing(); + billing.setMedicalRecord(medicalRecord); + billing.setIssueDate(LocalDateTime.now()); + billing.setPaymentStatus(PaymentStatus.PENDING); + + // Map DTOs to entities and calculate total amount + List billableItems = billingDTO.getBillableItems().stream() + .map(itemDTO -> { + BillableItem item = new BillableItem(); + item.setItemDescription(itemDTO.getItemDescription()); + item.setQuantity(itemDTO.getQuantity()); + item.setUnitPrice(itemDTO.getUnitPrice()); + item.setItemAmount(itemDTO.getUnitPrice().multiply(BigDecimal.valueOf(itemDTO.getQuantity()))); + item.setBilling(billing); + return item; + }) + .collect(Collectors.toList()); + + BigDecimal totalAmount = billableItems.stream() + .map(BillableItem::getItemAmount) + .reduce(BigDecimal.ZERO, BigDecimal::add); + + billing.setBillableItems(billableItems); + billing.setTotalAmount(totalAmount); + + return billingRepository.save(billing); + } + + @Override + public Billing getBillingById(Long id) { + return billingRepository.findById(id) + .orElseThrow(() -> new EntityNotFoundException("Billing not found with ID: " + id)); + } + + @Override + public Billing updateBilling(Long id, BillingDTO billingDTO) { + Billing existingBilling = getBillingById(id); + // You can add logic here to update existing billing data + // For simplicity, we'll just demonstrate fetching the entity + return billingRepository.save(existingBilling); + } + + @Override + public void deleteBilling(Long id) { + billingRepository.deleteById(id); + } + + // Assuming Billing entity has a getMedicalRecord() method + // and MedicalRecord entity has a getPatient() method + @Override + public boolean isOwner(Long billingId, Long patientId) { + return billingRepository.findById(billingId) + .map(billing -> billing.getMedicalRecord().getPatient().getId().equals(patientId)) + .orElse(false); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/services/ConsultationServiceImpl.java b/src/main/java/com/mirna/hospitalmanagementapi/application/services/ConsultationServiceImpl.java index eb44ab3..f51f3a3 100644 --- a/src/main/java/com/mirna/hospitalmanagementapi/application/services/ConsultationServiceImpl.java +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/services/ConsultationServiceImpl.java @@ -1,12 +1,8 @@ package com.mirna.hospitalmanagementapi.application.services; -import org.apache.coyote.BadRequestException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; - -import com.mirna.hospitalmanagementapi.application.usecase.consultation.FindConsultationByDoctorAndDateUseCase; import com.mirna.hospitalmanagementapi.application.usecase.consultation.FindConsultationByIdUseCase; -import com.mirna.hospitalmanagementapi.application.usecase.consultation.FindConsultationByPatientAndDateUseCase; import com.mirna.hospitalmanagementapi.application.usecase.consultation.SaveConsultationUseCase; import com.mirna.hospitalmanagementapi.application.usecase.doctor.FindDoctorByIdUseCase; import com.mirna.hospitalmanagementapi.application.usecase.doctor.FindOneFreeDoctorBySpecialtyUseCase; @@ -18,116 +14,134 @@ import com.mirna.hospitalmanagementapi.domain.entities.Doctor; import com.mirna.hospitalmanagementapi.domain.entities.Patient; import com.mirna.hospitalmanagementapi.domain.exceptions.ConsultationValidationException; import com.mirna.hospitalmanagementapi.domain.services.ConsultationService; +import com.mirna.hospitalmanagementapi.domain.services.DoctorScheduleService; +import com.mirna.hospitalmanagementapi.domain.repositories.ConsultationRepository; import jakarta.persistence.EntityNotFoundException; +import java.time.LocalTime; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; + +import org.springframework.transaction.annotation.Transactional; -/** - * This class is an implementation of the ConsultationService interface. - * - * This class provides methods to perform operations on consultations - * - * @author Mirna Gama - * @version 1.0 - */ @Service public class ConsultationServiceImpl implements ConsultationService { - @Autowired - private SaveConsultationUseCase saveConsultation; + @Autowired + private SaveConsultationUseCase saveConsultation; - @Autowired - private FindConsultationByIdUseCase findConsultationById; - - @Autowired - private FindConsultationByDoctorAndDateUseCase findConsultationByDoctorAndDate; + @Autowired + private FindConsultationByIdUseCase findConsultationById; - @Autowired - private FindConsultationByPatientAndDateUseCase findConsultationByPatientAndDate; + @Autowired + private ConsultationRepository consultationRepository; - @Autowired - private FindPatientByIdUseCase findPatientById; + @Autowired + private FindPatientByIdUseCase findPatientById; - @Autowired - private FindDoctorByIdUseCase findDoctorById; + @Autowired + private FindDoctorByIdUseCase findDoctorById; - @Autowired - private FindOneFreeDoctorBySpecialtyUseCase findOneFreeDoctorBySpecialty; + @Autowired + private FindOneFreeDoctorBySpecialtyUseCase findOneFreeDoctorBySpecialty; - /** - * Adds a new consultation to the repository. - * - * @param consultationDTO A data transfer object representing a consultation to add. - * @return The saved consultation if successful, or throws an exception if there is an error. - * @throws ConsultationValidationException if there is a validation error - */ - @Override - public Consultation addConsultation(ConsultationDTO consultationDTO) throws ConsultationValidationException { + @Autowired + private DoctorScheduleService doctorScheduleService; - Patient patient = findPatientById.execute(consultationDTO.patientId()); + private static final ZoneOffset BANGKOK_ZONE_OFFSET = ZoneOffset.ofHours(7); - if (!patient.getActive()) - throw new ConsultationValidationException("This patient is not active"); + @Override + @Transactional + public Consultation addConsultation(ConsultationDTO consultationDTO) throws ConsultationValidationException { + Patient patient = validatePatient(consultationDTO.patientId(), consultationDTO.consultationDate()); + Doctor doctor = validateAndFindDoctor(consultationDTO); + validatePatientAndDoctorAvailability(patient, doctor, consultationDTO.consultationDate()); - if (findConsultationByPatientAndDate.execute(patient.getId(), consultationDTO.consultationDate()) != null) - throw new ConsultationValidationException("This patient is not free on this date"); + Consultation consultation = new Consultation(patient, doctor, consultationDTO.consultationDate()); - Doctor doctor = null; + return saveConsultation.execute(consultation); + } - if (consultationDTO.doctorId() != null) { + private Patient validatePatient(Long patientId, OffsetDateTime consultationDate) throws ConsultationValidationException { + if (patientId == null) { + throw new ConsultationValidationException("Patient ID cannot be null"); + } - doctor = findDoctorById.execute(consultationDTO.doctorId()); + Patient patient = findPatientById.execute(patientId); - if (!doctor.getActive()) - throw new ConsultationValidationException("This doctor is not active"); + if (patient == null) { + throw new ConsultationValidationException("This patient is not found"); + } + if (!patient.getActive()) { + throw new ConsultationValidationException("This patient is not active"); + } - if (findConsultationByDoctorAndDate.execute(doctor.getId(), consultationDTO.consultationDate()) != null) - throw new ConsultationValidationException("This doctor is not free on this date"); + OffsetDateTime consultationDateInBkkZone = consultationDate.withOffsetSameInstant(BANGKOK_ZONE_OFFSET); + OffsetDateTime startOfDay = consultationDateInBkkZone.with(LocalTime.MIN); + OffsetDateTime endOfDay = consultationDateInBkkZone.with(LocalTime.MAX); - } else if (consultationDTO.specialty() != null) { + // แก้ไขบรรทัดนี้: ส่งพารามิเตอร์ที่ถูกต้องไปยัง Repository + if (consultationRepository.findConsultationByPatientAndDate(patient.getId(), startOfDay, endOfDay) != null) { + throw new ConsultationValidationException("This patient is not free on this date"); + } + return patient; + } - doctor = findOneFreeDoctorBySpecialty.execute(consultationDTO.specialty(), - consultationDTO.consultationDate()); + private Doctor validateAndFindDoctor(ConsultationDTO consultationDTO) throws ConsultationValidationException { + Doctor doctor = null; + OffsetDateTime consultationDateInBkkZone = consultationDTO.consultationDate().withOffsetSameInstant(BANGKOK_ZONE_OFFSET); - if (doctor == null) throw new ConsultationValidationException("There is no free doctor for this date with this specialty"); - - } else { - throw new ConsultationValidationException("At least the specialty or doctor ID must be filled in"); - } + if (consultationDTO.doctorId() != null) { + doctor = findDoctorById.execute(consultationDTO.doctorId()); + } else if (consultationDTO.specialty() != null) { + doctor = findOneFreeDoctorBySpecialty.execute(consultationDTO.specialty(), consultationDateInBkkZone); + } else { + throw new ConsultationValidationException("At least the specialty or doctor ID must be filled in"); + } - Consultation consultation = new Consultation(patient, doctor, consultationDTO.consultationDate()); + if (doctor == null) { + throw new ConsultationValidationException("No doctors found at the requested time or specialty."); + } + return doctor; + } - return saveConsultation.execute(consultation); - } + private void validatePatientAndDoctorAvailability(Patient patient, Doctor doctor, OffsetDateTime consultationDate) throws ConsultationValidationException { + OffsetDateTime consultationDateInBkkZone = consultationDate.withOffsetSameInstant(BANGKOK_ZONE_OFFSET); - /** - * Finds a stored consultation by id. - * - * @param id A long representing the consultation's unique identifier - * @return The corresponding consultation if successful, or throws an exception if it is non-existent. - */ - @Override - public Consultation findConsultationById(Long id) { - Consultation consultation = findConsultationById.execute(id); + OffsetDateTime startOfDay = consultationDateInBkkZone.with(LocalTime.MIN); + OffsetDateTime endOfDay = consultationDateInBkkZone.with(LocalTime.MAX); - if (consultation == null) - throw new EntityNotFoundException("No existing consultation with this id"); - - return consultation; - } - - /** - * Cancels and updates an existing query in the repository - * @param consultationCanceledDTO A data transfer object representing the consultation that will be canceled. - * @return The canceled consultation if successful, or throws an exception if there is an error. - */ - @Override - public Consultation cancelConsultation(ConsultationCanceledDTO consultationCanceledDTO) { - Consultation consultation = this.findConsultationById(consultationCanceledDTO.consultationId()); + // แก้ไขบรรทัดนี้: ส่งพารามิเตอร์ที่ถูกต้องไปยัง Repository + if (consultationRepository.findConsultationByDoctorAndDate(doctor.getId(), startOfDay, endOfDay) != null) { + throw new ConsultationValidationException("This doctor is not free on this date"); + } - consultation.setCanceled(true); - consultation.setReasonCancellation(consultationCanceledDTO.reasonCancellation()); + boolean isDoctorAvailable = doctorScheduleService.isDoctorAvailable( + doctor.getId(), + consultationDateInBkkZone.getDayOfWeek(), + consultationDateInBkkZone.toLocalTime() + ); - return saveConsultation.execute(consultation); - } + if (!isDoctorAvailable) { + throw new ConsultationValidationException("The selected doctor is not on duty at the requested time."); + } + } -} + @Override + public Consultation findConsultationById(Long id) { + Consultation consultation = findConsultationById.execute(id); + + if (consultation == null) + throw new EntityNotFoundException("No existing consultation with this id"); + + return consultation; + } + + @Override + public Consultation cancelConsultation(ConsultationCanceledDTO consultationCanceledDTO) { + Consultation consultation = this.findConsultationById(consultationCanceledDTO.consultationId()); + consultation.setCanceled(true); + consultation.setReasonCancellation(consultationCanceledDTO.reasonCancellation()); + return saveConsultation.execute(consultation); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/services/DoctorScheduleServiceImpl.java b/src/main/java/com/mirna/hospitalmanagementapi/application/services/DoctorScheduleServiceImpl.java new file mode 100644 index 0000000..e4c5294 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/services/DoctorScheduleServiceImpl.java @@ -0,0 +1,43 @@ +package com.mirna.hospitalmanagementapi.application.services; + +import com.mirna.hospitalmanagementapi.domain.entities.DoctorSchedule; +import com.mirna.hospitalmanagementapi.domain.repositories.DoctorScheduleRepository; +import com.mirna.hospitalmanagementapi.domain.services.DoctorScheduleService; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import java.time.DayOfWeek; +import java.time.LocalTime; + +@Service +public class DoctorScheduleServiceImpl implements DoctorScheduleService { + + @Autowired + private DoctorScheduleRepository doctorScheduleRepository; + + @Override + @Transactional + public DoctorSchedule saveDoctorSchedule(DoctorSchedule doctorSchedule) { + return doctorScheduleRepository.save(doctorSchedule); + } + + @Override + public boolean isDoctorAvailable(Long doctorId, DayOfWeek day, LocalTime time) { + // Step 1: Find the doctor's schedule for that day. + DoctorSchedule doctorSchedule = doctorScheduleRepository.findByDoctorIdAndDayOfWeek(doctorId, day); + + // Step 2: If no schedule is found, return false immediately. + if (doctorSchedule == null) { + return false; + } + + LocalTime businessHourStart = doctorSchedule.getStartTime(); + LocalTime businessHourEnd = doctorSchedule.getEndTime(); + + // Step 3: Check if the consultation time is within the working hours. + // We use isAfter() with a check for equality to include the start time. + // We use isBefore() to exclude the end time, as is standard practice for time slots. + return (time.equals(businessHourStart) || time.isAfter(businessHourStart)) && time.isBefore(businessHourEnd); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/services/DoctorServiceImpl.java b/src/main/java/com/mirna/hospitalmanagementapi/application/services/DoctorServiceImpl.java index 8ac619e..7e15e23 100644 --- a/src/main/java/com/mirna/hospitalmanagementapi/application/services/DoctorServiceImpl.java +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/services/DoctorServiceImpl.java @@ -99,14 +99,14 @@ public class DoctorServiceImpl implements DoctorService { if (doctor == null) { throw new EntityNotFoundException("No existing doctor with this id"); } - - if (doctorUpdatedDataDTO.name() != null) { - doctor.setName(doctorUpdatedDataDTO.name()); - } - - if (doctorUpdatedDataDTO.telephone() != null) { - doctor.setName(doctorUpdatedDataDTO.telephone()); - } + + if (doctorUpdatedDataDTO.name() != null) { + doctor.setName(doctorUpdatedDataDTO.name()); + } + + if (doctorUpdatedDataDTO.telephone() != null) { + doctor.setTelephone(doctorUpdatedDataDTO.telephone()); + } if (doctorUpdatedDataDTO.address() != null) { diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/services/InsuranceClaimServiceImpl.java b/src/main/java/com/mirna/hospitalmanagementapi/application/services/InsuranceClaimServiceImpl.java new file mode 100644 index 0000000..2117049 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/services/InsuranceClaimServiceImpl.java @@ -0,0 +1,94 @@ +package com.mirna.hospitalmanagementapi.application.services; + +import com.mirna.hospitalmanagementapi.domain.entities.Billing; +import com.mirna.hospitalmanagementapi.domain.entities.InsuranceClaim; +import com.mirna.hospitalmanagementapi.domain.entities.InsuranceProvider; +import com.mirna.hospitalmanagementapi.domain.dtos.InsuranceClaimDTO; +import com.mirna.hospitalmanagementapi.domain.enums.ClaimStatus; // <-- นำเข้า Enum +import com.mirna.hospitalmanagementapi.domain.repositories.BillingRepository; +import com.mirna.hospitalmanagementapi.domain.repositories.InsuranceClaimRepository; +import com.mirna.hospitalmanagementapi.domain.repositories.InsuranceProviderRepository; +import com.mirna.hospitalmanagementapi.domain.services.InsuranceClaimService; +import jakarta.persistence.EntityNotFoundException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +import java.util.List; + +@Service +public class InsuranceClaimServiceImpl implements InsuranceClaimService { + + @Autowired + private InsuranceClaimRepository insuranceClaimRepository; + + @Autowired + private BillingRepository billingRepository; + + @Autowired + private InsuranceProviderRepository insuranceProviderRepository; + + @Override + public InsuranceClaim createInsuranceClaim(InsuranceClaimDTO insuranceClaimDTO) { + Billing billing = billingRepository.findById(insuranceClaimDTO.getBillingId()) + .orElseThrow(() -> new EntityNotFoundException("Billing not found with ID: " + insuranceClaimDTO.getBillingId())); + + InsuranceProvider insuranceProvider = insuranceProviderRepository.findById(insuranceClaimDTO.getInsuranceProviderId()) + .orElseThrow(() -> new EntityNotFoundException("Insurance Provider not found with ID: " + insuranceClaimDTO.getInsuranceProviderId())); + + InsuranceClaim claim = new InsuranceClaim(); + claim.setBilling(billing); + claim.setInsuranceProvider(insuranceProvider); + claim.setClaimNumber(insuranceClaimDTO.getClaimNumber()); + claim.setSubmissionDate(insuranceClaimDTO.getSubmissionDate()); + claim.setApprovedAmount(insuranceClaimDTO.getApprovedAmount()); + + // แปลง String เป็น Enum + if (insuranceClaimDTO.getClaimStatus() != null) { + claim.setClaimStatus(ClaimStatus.valueOf(insuranceClaimDTO.getClaimStatus().toUpperCase())); + } + + return insuranceClaimRepository.save(claim); + } + + @Override + public InsuranceClaim getInsuranceClaimById(Long id) { + return insuranceClaimRepository.findById(id) + .orElseThrow(() -> new EntityNotFoundException("Insurance Claim not found with ID: " + id)); + } + + @Override + public Page getAllInsuranceClaims(Pageable pageable) { + return insuranceClaimRepository.findAll(pageable); + } + + @Override + public InsuranceClaim updateInsuranceClaim(Long id, InsuranceClaimDTO insuranceClaimDTO) { + InsuranceClaim existingClaim = getInsuranceClaimById(id); + + Billing billing = billingRepository.findById(insuranceClaimDTO.getBillingId()) + .orElseThrow(() -> new EntityNotFoundException("Billing not found with ID: " + insuranceClaimDTO.getBillingId())); + + InsuranceProvider insuranceProvider = insuranceProviderRepository.findById(insuranceClaimDTO.getInsuranceProviderId()) + .orElseThrow(() -> new EntityNotFoundException("Insurance Provider not found with ID: " + insuranceClaimDTO.getInsuranceProviderId())); + + existingClaim.setBilling(billing); + existingClaim.setInsuranceProvider(insuranceProvider); + existingClaim.setClaimNumber(insuranceClaimDTO.getClaimNumber()); + existingClaim.setSubmissionDate(insuranceClaimDTO.getSubmissionDate()); + existingClaim.setApprovedAmount(insuranceClaimDTO.getApprovedAmount()); + + // แปลง String เป็น Enum + if (insuranceClaimDTO.getClaimStatus() != null) { + existingClaim.setClaimStatus(ClaimStatus.valueOf(insuranceClaimDTO.getClaimStatus().toUpperCase())); + } + + return insuranceClaimRepository.save(existingClaim); + } + + @Override + public void deleteInsuranceClaim(Long id) { + insuranceClaimRepository.deleteById(id); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/services/InsuranceProviderServiceImpl.java b/src/main/java/com/mirna/hospitalmanagementapi/application/services/InsuranceProviderServiceImpl.java new file mode 100644 index 0000000..6200eb8 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/services/InsuranceProviderServiceImpl.java @@ -0,0 +1,81 @@ +package com.mirna.hospitalmanagementapi.application.services; + +import com.mirna.hospitalmanagementapi.domain.dtos.InsuranceProviderDTO; +import com.mirna.hospitalmanagementapi.domain.entities.InsuranceProvider; +import com.mirna.hospitalmanagementapi.domain.repositories.InsuranceProviderRepository; +import com.mirna.hospitalmanagementapi.domain.services.InsuranceProviderService; +import jakarta.persistence.EntityNotFoundException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import java.util.List; + +@Service +public class InsuranceProviderServiceImpl implements InsuranceProviderService { + + @Autowired + private InsuranceProviderRepository insuranceProviderRepository; + + /** + * Adds a new insurance provider to the database. + */ + @Override + public InsuranceProvider addInsuranceProvider(InsuranceProviderDTO insuranceProviderDTO) { + InsuranceProvider provider = new InsuranceProvider(); + provider.setProviderName(insuranceProviderDTO.providerName()); + provider.setContactPerson(insuranceProviderDTO.contactPerson()); + provider.setContactEmail(insuranceProviderDTO.contactEmail()); + provider.setPhoneNumber(insuranceProviderDTO.phoneNumber()); + return insuranceProviderRepository.save(provider); + } + + /** + * Retrieves all insurance providers from the database. + */ + @Override + public List getAllInsuranceProviders() { + return insuranceProviderRepository.findAll(); + } + + /** + * Finds an insurance provider by its ID. + */ + @Override + public InsuranceProvider getInsuranceProviderById(Long id) throws EntityNotFoundException { + return insuranceProviderRepository.findById(id) + .orElseThrow(() -> new EntityNotFoundException("Insurance Provider with ID " + id + " not found.")); + } + + /** + * Updates an existing insurance provider. + */ + @Override + public InsuranceProvider updateInsuranceProvider(Long id, InsuranceProviderDTO updatedProviderDTO) throws EntityNotFoundException { + InsuranceProvider existingProvider = getInsuranceProviderById(id); + + if (updatedProviderDTO.providerName() != null) { + existingProvider.setProviderName(updatedProviderDTO.providerName()); + } + if (updatedProviderDTO.contactPerson() != null) { + existingProvider.setContactPerson(updatedProviderDTO.contactPerson()); + } + if (updatedProviderDTO.contactEmail() != null) { + existingProvider.setContactEmail(updatedProviderDTO.contactEmail()); + } + if (updatedProviderDTO.phoneNumber() != null) { + existingProvider.setPhoneNumber(updatedProviderDTO.phoneNumber()); + } + + return insuranceProviderRepository.save(existingProvider); + } + + /** + * Deletes an insurance provider from the database. + */ + @Override + public void deleteInsuranceProvider(Long id) throws EntityNotFoundException { + if (!insuranceProviderRepository.existsById(id)) { + throw new EntityNotFoundException("Insurance Provider with ID " + id + " not found."); + } + insuranceProviderRepository.deleteById(id); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/services/InventoryItemServiceImpl.java b/src/main/java/com/mirna/hospitalmanagementapi/application/services/InventoryItemServiceImpl.java new file mode 100644 index 0000000..15be98b --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/services/InventoryItemServiceImpl.java @@ -0,0 +1,149 @@ +package com.mirna.hospitalmanagementapi.application.services; + +import com.mirna.hospitalmanagementapi.application.usecase.inventory.FindInventoryItemByIdUseCase; +import com.mirna.hospitalmanagementapi.application.usecase.inventory.SaveInventoryItemUseCase; +import com.mirna.hospitalmanagementapi.domain.dtos.inventory.InventoryItemDTO; +import com.mirna.hospitalmanagementapi.domain.dtos.inventory.InventoryItemResponseDTO; +import com.mirna.hospitalmanagementapi.domain.entities.inventory.InventoryItem; +import com.mirna.hospitalmanagementapi.domain.entities.inventory.InventoryItemType; +import com.mirna.hospitalmanagementapi.domain.entities.inventory.InventorySupplier; +import com.mirna.hospitalmanagementapi.domain.repositories.inventory.InventoryItemRepository; +import com.mirna.hospitalmanagementapi.domain.repositories.inventory.InventoryItemTypeRepository; +import com.mirna.hospitalmanagementapi.domain.repositories.inventory.InventorySupplierRepository; +import com.mirna.hospitalmanagementapi.domain.services.inventory.InventoryItemService; +import jakarta.persistence.EntityNotFoundException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import java.time.ZonedDateTime; +import java.util.UUID; + +@Service +public class InventoryItemServiceImpl implements InventoryItemService { + + @Autowired + private SaveInventoryItemUseCase saveInventoryItem; + + @Autowired + private FindInventoryItemByIdUseCase findInventoryItemById; + + @Autowired + private InventoryItemTypeRepository itemTypeRepository; + + @Autowired + private InventorySupplierRepository supplierRepository; + + @Autowired + private InventoryItemRepository inventoryItemRepository; + + @Transactional + @Override + public InventoryItemResponseDTO addInventoryItem(InventoryItemDTO itemDTO) { + InventoryItemType itemType = itemTypeRepository.findById(itemDTO.getItemTypeId()) + .orElseThrow(() -> new EntityNotFoundException("ไม่พบ Inventory Item Type ที่มี ID: " + itemDTO.getItemTypeId())); + + InventorySupplier supplier = supplierRepository.findById(itemDTO.getSupplierId()) + .orElseThrow(() -> new EntityNotFoundException("ไม่พบ Inventory Supplier ที่มี ID: " + itemDTO.getSupplierId())); + + InventoryItem item = new InventoryItem(); + item.setItemType(itemType); + item.setSupplier(supplier); + item.setItemName(itemDTO.getItemName()); + item.setDescription(itemDTO.getDescription()); + item.setCurrentStock(itemDTO.getCurrentStock() != null ? itemDTO.getCurrentStock() : 0); + item.setUnitOfMeasure(itemDTO.getUnitOfMeasure()); + item.setUnitPrice(itemDTO.getUnitPrice()); + item.setReorderLevel(itemDTO.getReorderLevel() != null ? itemDTO.getReorderLevel() : 10); + item.setExpirationDate(itemDTO.getExpirationDate()); + item.setLocation(itemDTO.getLocation()); + item.setIsActive(itemDTO.getIsActive() != null ? itemDTO.getIsActive() : true); + item.setSerialNumber(itemDTO.getSerialNumber()); + item.setCreatedAt(ZonedDateTime.now()); + item.setUpdatedAt(ZonedDateTime.now()); + + InventoryItem savedItem = saveInventoryItem.execute(item); + return new InventoryItemResponseDTO(savedItem); + } + + @Transactional(readOnly = true) + @Override + public InventoryItemResponseDTO getInventoryItemById(UUID id) { + InventoryItem item = findInventoryItemById.execute(id); + if (item == null) { + throw new EntityNotFoundException("ไม่พบ Inventory item ที่มี ID: " + id); + } + return new InventoryItemResponseDTO(item); + } + + @Transactional + @Override + public InventoryItemResponseDTO updateInventoryItem(UUID id, InventoryItemDTO itemDTO) throws EntityNotFoundException { + InventoryItem item = findInventoryItemById.execute(id); + if (item == null) { + throw new EntityNotFoundException("ไม่พบ Inventory item ที่มี ID: " + id); + } + + if (itemDTO.getItemName() != null) { + item.setItemName(itemDTO.getItemName()); + } + if (itemDTO.getDescription() != null) { + item.setDescription(itemDTO.getDescription()); + } + if (itemDTO.getCurrentStock() != null) { + item.setCurrentStock(itemDTO.getCurrentStock()); + } + if (itemDTO.getUnitOfMeasure() != null) { + item.setUnitOfMeasure(itemDTO.getUnitOfMeasure()); + } + if (itemDTO.getUnitPrice() != null) { + item.setUnitPrice(itemDTO.getUnitPrice()); + } + if (itemDTO.getReorderLevel() != null) { + item.setReorderLevel(itemDTO.getReorderLevel()); + } + if (itemDTO.getExpirationDate() != null) { + item.setExpirationDate(itemDTO.getExpirationDate()); + } + if (itemDTO.getLocation() != null) { + item.setLocation(itemDTO.getLocation()); + } + if (itemDTO.getIsActive() != null) { + item.setIsActive(itemDTO.getIsActive()); + } + + item.setUpdatedAt(ZonedDateTime.now()); + + InventoryItem updatedItem = inventoryItemRepository.save(item); + return new InventoryItemResponseDTO(updatedItem); + } + + @Transactional + @Override + public void deleteInventoryItem(UUID id) throws EntityNotFoundException { + InventoryItem item = findInventoryItemById.execute(id); + if (item == null) { + throw new EntityNotFoundException("ไม่พบ Inventory item ที่มี ID: " + id); + } + item.setIsActive(false); + item.setUpdatedAt(ZonedDateTime.now()); + inventoryItemRepository.save(item); + } + + // เพิ่มเมธอดใหม่ที่ส่งค่ากลับเป็น Entity + @Transactional(readOnly = true) + @Override + public InventoryItem findInventoryItemEntityById(UUID id) throws EntityNotFoundException { + InventoryItem item = findInventoryItemById.execute(id); + if (item == null) { + throw new EntityNotFoundException("ไม่พบ Inventory item ที่มี ID: " + id); + } + return item; + } + + // เพิ่มเมธอดสำหรับบันทึก Entity โดยตรง + @Transactional + @Override + public InventoryItem saveInventoryItemEntity(InventoryItem item) { + return inventoryItemRepository.save(item); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/services/InventoryTransactionServiceImpl.java b/src/main/java/com/mirna/hospitalmanagementapi/application/services/InventoryTransactionServiceImpl.java new file mode 100644 index 0000000..dcc3a1f --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/services/InventoryTransactionServiceImpl.java @@ -0,0 +1,58 @@ +package com.mirna.hospitalmanagementapi.application.services; + +import com.mirna.hospitalmanagementapi.domain.services.inventory.InventoryTransactionService; +import com.mirna.hospitalmanagementapi.domain.dtos.inventory.InventoryTransactionDTO; +import com.mirna.hospitalmanagementapi.domain.dtos.inventory.InventoryTransactionResponseDTO; // Import DTO สำหรับ Response +import com.mirna.hospitalmanagementapi.domain.entities.inventory.InventoryItem; +import com.mirna.hospitalmanagementapi.domain.entities.inventory.InventoryTransaction; +import com.mirna.hospitalmanagementapi.domain.enums.TransactionType; +import com.mirna.hospitalmanagementapi.domain.repositories.inventory.InventoryItemRepository; +import com.mirna.hospitalmanagementapi.domain.repositories.inventory.InventoryTransactionRepository; +import jakarta.persistence.EntityNotFoundException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import java.time.ZonedDateTime; +import java.util.UUID; + +@Service +public class InventoryTransactionServiceImpl implements InventoryTransactionService { + + @Autowired + private InventoryTransactionRepository transactionRepository; + + @Autowired + private InventoryItemRepository itemRepository; + + @Transactional + @Override + public InventoryTransactionResponseDTO addTransaction(InventoryTransactionDTO transactionDTO) { + InventoryItem item = itemRepository.findById(transactionDTO.getItemId()) + .orElseThrow(() -> new EntityNotFoundException("Inventory item with ID " + transactionDTO.getItemId() + " not found.")); + + InventoryTransaction transaction = new InventoryTransaction(); + transaction.setItem(item); + transaction.setTransactionType(transactionDTO.getTransactionType()); + transaction.setQuantity(transactionDTO.getQuantity()); + transaction.setRelatedDocumentId(transactionDTO.getRelatedDocumentId()); + transaction.setNotes(transactionDTO.getNotes()); + transaction.setTransactionDate(ZonedDateTime.now()); + + // Update the stock count of the inventory item + if (transactionDTO.getTransactionType() == TransactionType.IN) { + item.setCurrentStock(item.getCurrentStock() + transactionDTO.getQuantity()); + } else if (transactionDTO.getTransactionType() == TransactionType.OUT) { + if (item.getCurrentStock() < transactionDTO.getQuantity()) { + throw new IllegalArgumentException("Quantity to be withdrawn is more than available stock."); + } + item.setCurrentStock(item.getCurrentStock() - transactionDTO.getQuantity()); + } + itemRepository.save(item); + + // Save transaction and return the response DTO + InventoryTransaction newTransaction = transactionRepository.save(transaction); + + // แปลง Entity ที่ถูกบันทึกแล้วให้เป็น DTO ก่อน return + return new InventoryTransactionResponseDTO(newTransaction); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/services/ItemTypeServiceImpl.java b/src/main/java/com/mirna/hospitalmanagementapi/application/services/ItemTypeServiceImpl.java new file mode 100644 index 0000000..90b316b --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/services/ItemTypeServiceImpl.java @@ -0,0 +1,21 @@ +package com.mirna.hospitalmanagementapi.application.services; + +import com.mirna.hospitalmanagementapi.domain.dtos.inventory.ItemTypeDTO; +import com.mirna.hospitalmanagementapi.domain.entities.inventory.InventoryItemType; +import com.mirna.hospitalmanagementapi.domain.repositories.inventory.InventoryItemTypeRepository; +import com.mirna.hospitalmanagementapi.domain.services.inventory.ItemTypeService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class ItemTypeServiceImpl implements ItemTypeService { + + @Autowired + private InventoryItemTypeRepository inventoryItemTypeRepository; + + @Override + public InventoryItemType addItemType(ItemTypeDTO itemTypeDTO) { + InventoryItemType newItemType = new InventoryItemType(itemTypeDTO); + return inventoryItemTypeRepository.save(newItemType); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/services/LabResultServiceImpl.java b/src/main/java/com/mirna/hospitalmanagementapi/application/services/LabResultServiceImpl.java new file mode 100644 index 0000000..472073f --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/services/LabResultServiceImpl.java @@ -0,0 +1,53 @@ +package com.mirna.hospitalmanagementapi.application.services; + +import com.mirna.hospitalmanagementapi.domain.dtos.LabResultDTO; +import com.mirna.hospitalmanagementapi.domain.entities.LabResult; +import com.mirna.hospitalmanagementapi.domain.entities.MedicalRecord; +import com.mirna.hospitalmanagementapi.domain.repositories.LabResultRepository; +import com.mirna.hospitalmanagementapi.domain.repositories.MedicalRecordRepository; +import com.mirna.hospitalmanagementapi.domain.services.LabResultService; +import jakarta.persistence.EntityNotFoundException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import java.time.LocalDateTime; +import java.util.UUID; + +@Service +public class LabResultServiceImpl implements LabResultService { + + @Autowired + private LabResultRepository labResultRepository; + + @Autowired + private MedicalRecordRepository medicalRecordRepository; + + @Override + public LabResult addLabResult(Long medicalRecordId, LabResultDTO labResultDTO) { + MedicalRecord medicalRecord = medicalRecordRepository.findById(medicalRecordId) + .orElseThrow(() -> new EntityNotFoundException("ไม่พบบันทึกทางการแพทย์ที่มี ID นี้")); + + LabResult labResult = new LabResult(); + labResult.setMedicalRecord(medicalRecord); + labResult.setTestName(labResultDTO.getTestName()); + labResult.setResultValue(labResultDTO.getResultValue()); + labResult.setUnit(labResultDTO.getUnit()); + labResult.setReferenceRange(labResultDTO.getReferenceRange()); + labResult.setStatus(labResultDTO.getStatus()); + labResult.setResultDate(LocalDateTime.now()); + + return labResultRepository.save(labResult); + } + + @Override + public LabResult getLabResultById(UUID id) { + return labResultRepository.findById(id) + .orElseThrow(() -> new EntityNotFoundException("ไม่พบผลแล็บที่มี ID นี้")); + } + + @Override + public boolean isOwner(UUID labResultId, Long currentUserId) { + return labResultRepository.findById(labResultId) + .map(labResult -> labResult.getMedicalRecord().getPatient().getUser().getId().equals(currentUserId)) + .orElse(false); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/services/MedicalEquipmentScheduleServiceImpl.java b/src/main/java/com/mirna/hospitalmanagementapi/application/services/MedicalEquipmentScheduleServiceImpl.java new file mode 100644 index 0000000..fec2537 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/services/MedicalEquipmentScheduleServiceImpl.java @@ -0,0 +1,71 @@ +package com.mirna.hospitalmanagementapi.application.services; + +import com.mirna.hospitalmanagementapi.domain.dtos.MedicalEquipmentScheduleDTO; +import com.mirna.hospitalmanagementapi.domain.entities.inventory.InventoryItem; +import com.mirna.hospitalmanagementapi.domain.entities.MedicalEquipmentSchedule; +import com.mirna.hospitalmanagementapi.domain.repositories.inventory.InventoryItemRepository; +import com.mirna.hospitalmanagementapi.domain.repositories.MedicalEquipmentScheduleRepository; +import com.mirna.hospitalmanagementapi.domain.services.MedicalEquipmentScheduleService; +import jakarta.persistence.EntityNotFoundException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import com.mirna.hospitalmanagementapi.domain.dtos.MedicalEquipmentScheduleResponseDTO; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.UUID; + +@Service +public class MedicalEquipmentScheduleServiceImpl implements MedicalEquipmentScheduleService { + + @Autowired + private MedicalEquipmentScheduleRepository medicalEquipmentScheduleRepository; + + @Autowired + private InventoryItemRepository inventoryItemRepository; + + @Override + @Transactional + public MedicalEquipmentScheduleResponseDTO addMedicalEquipmentSchedule(MedicalEquipmentScheduleDTO medicalEquipmentScheduleDTO) { + InventoryItem equipment = inventoryItemRepository.findById(medicalEquipmentScheduleDTO.equipmentId()) + .orElseThrow(() -> new EntityNotFoundException("Medical equipment with ID " + medicalEquipmentScheduleDTO.equipmentId() + " not found.")); + + LocalDateTime startTime = medicalEquipmentScheduleDTO.startTime(); + LocalDateTime endTime = medicalEquipmentScheduleDTO.endTime(); + + List existingSchedules = medicalEquipmentScheduleRepository.findByInventoryItemAndEndTimeAfterAndStartTimeBefore( + equipment, + startTime, + endTime + ); + + if (!existingSchedules.isEmpty()) { + throw new IllegalArgumentException("Conflict: The medical equipment is already scheduled during this time slot."); + } + + MedicalEquipmentSchedule newSchedule = new MedicalEquipmentSchedule(); + newSchedule.setInventoryItem(equipment); + newSchedule.setStartTime(startTime); + newSchedule.setEndTime(endTime); + + MedicalEquipmentSchedule savedSchedule = medicalEquipmentScheduleRepository.save(newSchedule); + + return new MedicalEquipmentScheduleResponseDTO(savedSchedule); + } + + @Override + public MedicalEquipmentScheduleResponseDTO getMedicalEquipmentScheduleById(Long id) throws EntityNotFoundException { + MedicalEquipmentSchedule schedule = medicalEquipmentScheduleRepository.findById(id) + .orElseThrow(() -> new EntityNotFoundException("Medical equipment schedule with ID " + id + " not found.")); + return new MedicalEquipmentScheduleResponseDTO(schedule); + } + + @Override + public Page getAllMedicalEquipmentSchedules(Pageable pageable) { + Page schedulesPage = medicalEquipmentScheduleRepository.findAll(pageable); + return schedulesPage.map(MedicalEquipmentScheduleResponseDTO::new); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/services/MedicalImageServiceImpl.java b/src/main/java/com/mirna/hospitalmanagementapi/application/services/MedicalImageServiceImpl.java new file mode 100644 index 0000000..6607084 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/services/MedicalImageServiceImpl.java @@ -0,0 +1,108 @@ +package com.mirna.hospitalmanagementapi.application.services; + +import com.mirna.hospitalmanagementapi.domain.entities.MedicalImage; +import com.mirna.hospitalmanagementapi.domain.entities.MedicalRecord; +import com.mirna.hospitalmanagementapi.domain.repositories.MedicalImageRepository; +import com.mirna.hospitalmanagementapi.domain.repositories.MedicalRecordRepository; +import com.mirna.hospitalmanagementapi.domain.services.MedicalImageService; +import io.minio.*; +import io.minio.errors.MinioException; +import jakarta.persistence.EntityNotFoundException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.InputStreamResource; +import org.springframework.core.io.Resource; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; +import java.io.InputStream; +import java.time.LocalDateTime; +import java.util.UUID; + +@Service +public class MedicalImageServiceImpl implements MedicalImageService { + + @Autowired + private MinioClient minioClient; + + // เพิ่ม repository สำหรับการจัดการข้อมูลในฐานข้อมูล + @Autowired + private MedicalImageRepository medicalImageRepository; + + @Autowired + private MedicalRecordRepository medicalRecordRepository; + + @Value("${minio.bucket-name}") + private String bucketName; + + @Override + public MedicalImage uploadImage(Long medicalRecordId, MultipartFile file, String imageType, String notes) { + try { + // ค้นหา MedicalRecord ที่ต้องการเชื่อมโยง + MedicalRecord medicalRecord = medicalRecordRepository.findById(medicalRecordId) + .orElseThrow(() -> new EntityNotFoundException("Medical Record not found.")); + + // สร้าง bucket ถ้ายังไม่มี (โค้ดเดิม) + if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) { + minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); + } + + // สร้างชื่อไฟล์ที่ไม่ซ้ำกัน + String fileName = UUID.randomUUID().toString() + "-" + file.getOriginalFilename(); + InputStream inputStream = file.getInputStream(); + + // อัปโหลดไฟล์ไปยัง MinIO (โค้ดเดิม) + minioClient.putObject( + PutObjectArgs.builder() + .bucket(bucketName) + .object(fileName) + .stream(inputStream, file.getSize(), -1) + .contentType(file.getContentType()) + .build() + ); + + // บันทึกข้อมูล MedicalImage ลงในฐานข้อมูล + MedicalImage medicalImage = new MedicalImage(); + medicalImage.setMedicalRecord(medicalRecord); + medicalImage.setFileName(fileName); + medicalImage.setImageType(imageType); + medicalImage.setNotes(notes); + medicalImage.setUploadDate(LocalDateTime.now()); + + return medicalImageRepository.save(medicalImage); + + } catch (MinioException e) { + throw new RuntimeException("MinIO Error: " + e.getMessage()); + } catch (Exception e) { + throw new RuntimeException("Error uploading file: " + e.getMessage()); + } + } + + @Override + public Resource downloadImage(UUID id) { + try { + // ค้นหา MedicalImage จาก ID ในฐานข้อมูล + MedicalImage medicalImage = medicalImageRepository.findById(id) + .orElseThrow(() -> new EntityNotFoundException("Medical Image not found.")); + + // ดาวน์โหลดไฟล์จาก MinIO โดยใช้ชื่อไฟล์จากฐานข้อมูล + InputStream stream = minioClient.getObject( + GetObjectArgs.builder() + .bucket(bucketName) + .object(medicalImage.getFileName()) + .build() + ); + return new InputStreamResource(stream); + } catch (MinioException e) { + throw new RuntimeException("MinIO Error: " + e.getMessage()); + } catch (Exception e) { + throw new RuntimeException("Error downloading file: " + e.getMessage()); + } + } + + @Override + public boolean isOwner(UUID medicalImageId, Long currentUserId) { + return medicalImageRepository.findById(medicalImageId) + .map(medicalImage -> medicalImage.getMedicalRecord().getPatient().getUser().getId().equals(currentUserId)) + .orElse(false); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/services/MedicalRecordServiceImpl.java b/src/main/java/com/mirna/hospitalmanagementapi/application/services/MedicalRecordServiceImpl.java new file mode 100644 index 0000000..31afab4 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/services/MedicalRecordServiceImpl.java @@ -0,0 +1,139 @@ +package com.mirna.hospitalmanagementapi.application.services; + +import com.mirna.hospitalmanagementapi.application.usecase.medicalrecord.FindMedicalRecordByIdUseCase; +import com.mirna.hospitalmanagementapi.application.usecase.medicalrecord.SaveMedicalRecordUseCase; +import com.mirna.hospitalmanagementapi.domain.dtos.MedicalRecordDTO; +import com.mirna.hospitalmanagementapi.domain.entities.Doctor; +import com.mirna.hospitalmanagementapi.domain.entities.MedicalRecord; +import com.mirna.hospitalmanagementapi.domain.entities.Patient; +import com.mirna.hospitalmanagementapi.domain.repositories.MedicalRecordRepository; +import com.mirna.hospitalmanagementapi.domain.services.DoctorService; +import com.mirna.hospitalmanagementapi.domain.services.MedicalRecordService; +import com.mirna.hospitalmanagementapi.domain.services.PatientService; +import jakarta.persistence.EntityNotFoundException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** + * คลาสนี้เป็น Implementation ของ interface MedicalRecordService + * + * คลาสนี้จะให้บริการเมธอดสำหรับดำเนินการกับข้อมูลบันทึกทางการแพทย์ + * + * @author FlookSP + * @version 1.0 + */ +@Service +public class MedicalRecordServiceImpl implements MedicalRecordService { + + @Autowired + private SaveMedicalRecordUseCase saveMedicalRecord; + + @Autowired + private FindMedicalRecordByIdUseCase findMedicalRecordById; + + @Autowired + private PatientService patientService; + + @Autowired + private DoctorService doctorService; + + @Autowired + private MedicalRecordRepository medicalRecordRepository; + + /** + * เพิ่มบันทึกทางการแพทย์ใหม่ในฐานข้อมูล + */ + @Override + public MedicalRecord addMedicalRecord(MedicalRecordDTO medicalRecordDTO) throws EntityNotFoundException { + Patient patient = patientService.findPatientById(medicalRecordDTO.getPatientId()); + Doctor doctor = doctorService.findDoctorById(medicalRecordDTO.getDoctorId()); + + MedicalRecord medicalRecord = new MedicalRecord( + patient, + doctor, + null, + medicalRecordDTO.getDiagnosis(), + medicalRecordDTO.getTreatment(), + medicalRecordDTO.getNotes() + ); + + return saveMedicalRecord.execute(medicalRecord); + } + + /** + * ค้นหาบันทึกทางการแพทย์จาก ID (ไม่รวมรายละเอียดเพิ่มเติม) + */ + @Override + public MedicalRecord getMedicalRecordById(Long id) throws EntityNotFoundException { + MedicalRecord medicalRecord = findMedicalRecordById.execute(id); + + if (medicalRecord == null) { + throw new EntityNotFoundException("ไม่พบบันทึกทางการแพทย์ที่มี ID นี้"); + } + + return medicalRecord; + } + + /** + * ค้นหาบันทึกทางการแพทย์จาก ID พร้อมรายละเอียดทั้งหมด (รูปภาพ, ผลแล็บ, ฯลฯ) + */ + @Override + @Transactional + public MedicalRecord getMedicalRecordDetails(Long id) throws EntityNotFoundException { + MedicalRecord medicalRecord = medicalRecordRepository.findById(id) + .orElseThrow(() -> new EntityNotFoundException("ไม่พบบันทึกทางการแพทย์ที่มี ID นี้")); + + // หากใช้ Lazy Loading การเรียกเมธอดเหล่านี้จะบังคับให้โหลดข้อมูลที่เกี่ยวข้อง + // โดยอัตโนมัติภายใต้ @Transactional + medicalRecord.getMedicalImages().size(); + medicalRecord.getLabResults().size(); + + return medicalRecord; + } + + /** + * อัปเดตบันทึกทางการแพทย์ที่มีอยู่จาก ID + */ + @Override + public MedicalRecord updateMedicalRecord(Long id, MedicalRecordDTO medicalRecordDTO) throws EntityNotFoundException { + MedicalRecord medicalRecord = findMedicalRecordById.execute(id); + + if (medicalRecord == null) { + throw new EntityNotFoundException("ไม่พบบันทึกทางการแพทย์ที่มี ID นี้"); + } + + if (medicalRecordDTO.getDiagnosis() != null) { + medicalRecord.setDiagnosis(medicalRecordDTO.getDiagnosis()); + } + if (medicalRecordDTO.getTreatment() != null) { + medicalRecord.setTreatment(medicalRecordDTO.getTreatment()); + } + if (medicalRecordDTO.getNotes() != null) { + medicalRecord.setNotes(medicalRecordDTO.getNotes()); + } + + return saveMedicalRecord.execute(medicalRecord); + } + + /** + * ลบบันทึกทางการแพทย์จาก ID + */ + @Override + public void deleteMedicalRecord(Long id) throws EntityNotFoundException { + MedicalRecord medicalRecord = findMedicalRecordById.execute(id); + + if (medicalRecord == null) { + throw new EntityNotFoundException("ไม่พบบันทึกทางการแพทย์ที่มี ID นี้"); + } + + medicalRecordRepository.delete(medicalRecord); + } + + @Override + public boolean isOwner(Long medicalRecordId, Long patientId) { + return medicalRecordRepository.findById(medicalRecordId) + .map(medicalRecord -> medicalRecord.getPatient().getId().equals(patientId)) + .orElse(false); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/services/NurseScheduleServiceImpl.java b/src/main/java/com/mirna/hospitalmanagementapi/application/services/NurseScheduleServiceImpl.java new file mode 100644 index 0000000..a9cb7c7 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/services/NurseScheduleServiceImpl.java @@ -0,0 +1,78 @@ +package com.mirna.hospitalmanagementapi.application.services; + +import com.mirna.hospitalmanagementapi.domain.dtos.NurseScheduleDTO; +import com.mirna.hospitalmanagementapi.domain.entities.Nurse; +import com.mirna.hospitalmanagementapi.domain.entities.NurseSchedule; +import com.mirna.hospitalmanagementapi.domain.repositories.NurseRepository; +import com.mirna.hospitalmanagementapi.domain.repositories.NurseScheduleRepository; +import com.mirna.hospitalmanagementapi.domain.services.NurseScheduleService; +import jakarta.persistence.EntityNotFoundException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.util.List; + +@Service +public class NurseScheduleServiceImpl implements NurseScheduleService { + + @Autowired + private NurseScheduleRepository nurseScheduleRepository; + + @Autowired + private NurseRepository nurseRepository; + + @Override + @Transactional + public NurseSchedule addNurseSchedule(NurseScheduleDTO nurseScheduleDTO) { + Nurse nurse = nurseRepository.findById(nurseScheduleDTO.nurseId()) + .orElseThrow(() -> new EntityNotFoundException("Nurse with ID " + nurseScheduleDTO.nurseId() + " not found.")); + + LocalDateTime startTime = nurseScheduleDTO.startTime(); + LocalDateTime endTime = nurseScheduleDTO.endTime(); + + // 1. ตรวจสอบความขัดแย้งของเวลา + List existingSchedules = nurseScheduleRepository.findOverlappingSchedules( + nurse.getId(), + startTime, + endTime + ); + + if (!existingSchedules.isEmpty()) { + throw new IllegalArgumentException("Conflict: The nurse is already scheduled during this time slot."); + } + + // 2. ตรวจสอบเวลาพักผ่อนขั้นต่ำ (สมมติว่ากำหนดไว้ 8 ชั่วโมง) + // ค้นหากะล่าสุดของพยาบาลคนนี้ + nurseScheduleRepository.findTopByNurseIdOrderByEndTimeDesc(nurse.getId()) + .ifPresent(lastSchedule -> { + // คำนวณเวลาเริ่มต้นของกะใหม่ (startTime) จากเวลาสิ้นสุดของกะล่าสุด (endTime) บวกเวลาพักผ่อนขั้นต่ำ + LocalDateTime nextAvailableTime = lastSchedule.getEndTime().plusHours(8); // กำหนดเวลาพักผ่อน 8 ชั่วโมง + if (startTime.isBefore(nextAvailableTime)) { + throw new IllegalArgumentException("Conflict: The nurse must have at least 8 hours of rest between shifts."); + } + }); + + // 3. ถ้าไม่มีข้อผิดพลาด ให้สร้างตารางเวรใหม่ + NurseSchedule newSchedule = new NurseSchedule(); + newSchedule.setNurse(nurse); + newSchedule.setStartTime(startTime); + newSchedule.setEndTime(endTime); + + return nurseScheduleRepository.save(newSchedule); + } + + @Override + public NurseSchedule getNurseScheduleById(Long id) throws EntityNotFoundException { + return nurseScheduleRepository.findById(id) + .orElseThrow(() -> new EntityNotFoundException("Nurse schedule with ID " + id + " not found.")); + } + + @Override + public Page getAllNurseSchedules(Pageable pageable) { + return nurseScheduleRepository.findAll(pageable); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/services/NurseServiceImpl.java b/src/main/java/com/mirna/hospitalmanagementapi/application/services/NurseServiceImpl.java new file mode 100644 index 0000000..0e0d0f7 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/services/NurseServiceImpl.java @@ -0,0 +1,75 @@ +package com.mirna.hospitalmanagementapi.application.services; + +import com.mirna.hospitalmanagementapi.domain.dtos.NurseDTO; +import com.mirna.hospitalmanagementapi.domain.entities.Nurse; +import com.mirna.hospitalmanagementapi.domain.repositories.NurseRepository; +import com.mirna.hospitalmanagementapi.domain.services.NurseService; +import jakarta.persistence.EntityNotFoundException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class NurseServiceImpl implements NurseService { + + @Autowired + private NurseRepository nurseRepository; + + @Override + @Transactional + public Nurse addNurse(NurseDTO nurseDTO) { + Nurse nurse = new Nurse(); + nurse.setName(nurseDTO.name()); + nurse.setLicenseNumber(nurseDTO.licenseNumber()); + nurse.setEmail(nurseDTO.email()); + nurse.setTelephone(nurseDTO.telephone()); + nurse.setShiftType(nurseDTO.shiftType()); + return nurseRepository.save(nurse); + } + + @Override + public Nurse getNurseById(Long id) throws EntityNotFoundException { + return nurseRepository.findById(id) + .orElseThrow(() -> new EntityNotFoundException("Nurse with ID " + id + " not found.")); + } + + @Override + public Page getAllNurses(Pageable pageable) { + return nurseRepository.findAll(pageable); + } + + @Override + @Transactional + public Nurse updateNurse(Long id, NurseDTO nurseDTO) throws EntityNotFoundException { + Nurse existingNurse = getNurseById(id); + + if (nurseDTO.name() != null) { + existingNurse.setName(nurseDTO.name()); + } + if (nurseDTO.licenseNumber() != null) { + existingNurse.setLicenseNumber(nurseDTO.licenseNumber()); + } + if (nurseDTO.email() != null) { + existingNurse.setEmail(nurseDTO.email()); + } + if (nurseDTO.telephone() != null) { + existingNurse.setTelephone(nurseDTO.telephone()); + } + if (nurseDTO.shiftType() != null) { + existingNurse.setShiftType(nurseDTO.shiftType()); + } + + return nurseRepository.save(existingNurse); + } + + @Override + @Transactional + public void deleteNurse(Long id) throws EntityNotFoundException { + if (!nurseRepository.existsById(id)) { + throw new EntityNotFoundException("Nurse with ID " + id + " not found."); + } + nurseRepository.deleteById(id); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/services/OperatingRoomScheduleServiceImpl.java b/src/main/java/com/mirna/hospitalmanagementapi/application/services/OperatingRoomScheduleServiceImpl.java new file mode 100644 index 0000000..5f1952b --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/services/OperatingRoomScheduleServiceImpl.java @@ -0,0 +1,66 @@ +package com.mirna.hospitalmanagementapi.application.services; + +import com.mirna.hospitalmanagementapi.domain.dtos.OperatingRoomScheduleDTO; +import com.mirna.hospitalmanagementapi.domain.entities.OperatingRoom; +import com.mirna.hospitalmanagementapi.domain.entities.OperatingRoomSchedule; +import com.mirna.hospitalmanagementapi.domain.repositories.OperatingRoomRepository; +import com.mirna.hospitalmanagementapi.domain.repositories.OperatingRoomScheduleRepository; +import com.mirna.hospitalmanagementapi.domain.services.OperatingRoomScheduleService; +import jakarta.persistence.EntityNotFoundException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.util.List; + +@Service +public class OperatingRoomScheduleServiceImpl implements OperatingRoomScheduleService { + + @Autowired + private OperatingRoomScheduleRepository operatingRoomScheduleRepository; + + @Autowired + private OperatingRoomRepository operatingRoomRepository; + + @Override + @Transactional + public OperatingRoomSchedule addOperatingRoomSchedule(OperatingRoomScheduleDTO operatingRoomScheduleDTO) { + OperatingRoom room = operatingRoomRepository.findById(operatingRoomScheduleDTO.roomId()) + .orElseThrow(() -> new EntityNotFoundException("Operating room with ID " + operatingRoomScheduleDTO.roomId() + " not found.")); + + LocalDateTime startTime = operatingRoomScheduleDTO.startTime(); + LocalDateTime endTime = operatingRoomScheduleDTO.endTime(); + + // ตรวจสอบความขัดแย้งของเวลา + List existingSchedules = operatingRoomScheduleRepository.findByOperatingRoomIdAndEndTimeAfterAndStartTimeBefore( + room.getId(), + startTime, + endTime + ); + + if (!existingSchedules.isEmpty()) { + throw new IllegalArgumentException("Conflict: The operating room is already scheduled during this time slot."); + } + + OperatingRoomSchedule newSchedule = new OperatingRoomSchedule(); + newSchedule.setOperatingRoom(room); + newSchedule.setStartTime(startTime); + newSchedule.setEndTime(endTime); + + return operatingRoomScheduleRepository.save(newSchedule); + } + + @Override + public OperatingRoomSchedule getOperatingRoomScheduleById(Long id) throws EntityNotFoundException { + return operatingRoomScheduleRepository.findById(id) + .orElseThrow(() -> new EntityNotFoundException("Operating room schedule with ID " + id + " not found.")); + } + + @Override + public Page getAllOperatingRoomSchedules(Pageable pageable) { + return operatingRoomScheduleRepository.findAll(pageable); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/services/OperatingRoomServiceImpl.java b/src/main/java/com/mirna/hospitalmanagementapi/application/services/OperatingRoomServiceImpl.java new file mode 100644 index 0000000..1078f0c --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/services/OperatingRoomServiceImpl.java @@ -0,0 +1,63 @@ +package com.mirna.hospitalmanagementapi.application.services; + +import com.mirna.hospitalmanagementapi.domain.dtos.OperatingRoomDTO; +import com.mirna.hospitalmanagementapi.domain.entities.OperatingRoom; +import com.mirna.hospitalmanagementapi.domain.repositories.OperatingRoomRepository; +import com.mirna.hospitalmanagementapi.domain.services.OperatingRoomService; +import jakarta.persistence.EntityNotFoundException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class OperatingRoomServiceImpl implements OperatingRoomService { + + @Autowired + private OperatingRoomRepository operatingRoomRepository; + + @Override + @Transactional + public OperatingRoom addOperatingRoom(OperatingRoomDTO operatingRoomDTO) { + OperatingRoom room = new OperatingRoom(); + room.setRoomNumber(operatingRoomDTO.roomNumber()); + room.setAvailable(operatingRoomDTO.isAvailable()); + return operatingRoomRepository.save(room); + } + + @Override + public OperatingRoom getOperatingRoomById(Long id) throws EntityNotFoundException { + return operatingRoomRepository.findById(id) + .orElseThrow(() -> new EntityNotFoundException("Operating Room with ID " + id + " not found.")); + } + + @Override + public Page getAllOperatingRooms(Pageable pageable) { + return operatingRoomRepository.findAll(pageable); + } + + @Override + @Transactional + public OperatingRoom updateOperatingRoom(Long id, OperatingRoomDTO operatingRoomDTO) throws EntityNotFoundException { + OperatingRoom existingRoom = getOperatingRoomById(id); + + if (operatingRoomDTO.roomNumber() != null) { + existingRoom.setRoomNumber(operatingRoomDTO.roomNumber()); + } + if (operatingRoomDTO.isAvailable() != null) { + existingRoom.setAvailable(operatingRoomDTO.isAvailable()); + } + + return operatingRoomRepository.save(existingRoom); + } + + @Override + @Transactional + public void deleteOperatingRoom(Long id) throws EntityNotFoundException { + if (!operatingRoomRepository.existsById(id)) { + throw new EntityNotFoundException("Operating Room with ID " + id + " not found."); + } + operatingRoomRepository.deleteById(id); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/services/PatientServiceImpl.java b/src/main/java/com/mirna/hospitalmanagementapi/application/services/PatientServiceImpl.java index e219caa..9b0f84e 100644 --- a/src/main/java/com/mirna/hospitalmanagementapi/application/services/PatientServiceImpl.java +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/services/PatientServiceImpl.java @@ -4,6 +4,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import com.mirna.hospitalmanagementapi.application.usecase.patient.FindPatientByIdUseCase; import com.mirna.hospitalmanagementapi.application.usecase.patient.FindPatientsUseCase; @@ -15,6 +16,11 @@ import com.mirna.hospitalmanagementapi.domain.dtos.patient.PatientUpdatedDataDTO import com.mirna.hospitalmanagementapi.domain.entities.Address; import com.mirna.hospitalmanagementapi.domain.entities.Patient; import com.mirna.hospitalmanagementapi.domain.services.PatientService; +import com.mirna.hospitalmanagementapi.domain.dtos.auth.LinkPatientToUserDTO; +import com.mirna.hospitalmanagementapi.domain.entities.auth.User; +import com.mirna.hospitalmanagementapi.domain.repositories.auth.UserRepository; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import com.mirna.hospitalmanagementapi.domain.enums.Role; import jakarta.persistence.EntityNotFoundException; @@ -29,136 +35,177 @@ import jakarta.persistence.EntityNotFoundException; @Service public class PatientServiceImpl implements PatientService { - @Autowired - private SavePatientUseCase savePatient; - - @Autowired - private FindPatientByIdUseCase findPatientById; - - @Autowired - private FindPatientsUseCase findPatients; - - /** - * Adds a new patient to the database. - * - * @param patientDTO A data transfer object containing the data for Patient - * entity. - * @return The saved patient if successful, or null if there is an error. - */ - @Override - public Patient addPatient(PatientDTO patientDTO) { - Patient patient = new Patient(patientDTO); - - return this.savePatient.execute(patient); - } + @Autowired + private SavePatientUseCase savePatient; - /** - * Finds a stored patient by id. - * - * @param id A long representing the patient's unique identifier - * @return The corresponding patient if successful, or throws an EntityNotFoundException if it is - * non-existent. - * @throws EntityNotFoundException When patient with id provided is non-existent - */ - @Override - public Patient findPatientById(Long id) throws EntityNotFoundException { - Patient patient = this.findPatientById.execute(id); - - if (patient == null) throw new EntityNotFoundException("No existing patient with this id"); - - return patient; - } + @Autowired + private FindPatientByIdUseCase findPatientById; - /** - * Finds patients from the database. - * - * @param pageable Pagination information, such as size and page number - * - * @return A paginated sublist containing data transfer objects with patients public information in the repository - */ - @Override - public Page findPatients(Pageable pageable) { - return findPatients.execute(pageable).map(PatientPublicDataDTO::new); - } + @Autowired + private FindPatientsUseCase findPatients; - /** + @Autowired + private UserRepository userRepository; + @Autowired + private BCryptPasswordEncoder passwordEncoder; + + /** + * Adds a new patient to the database. + * + * @param patientDTO A data transfer object containing the data for Patient + * entity. + * @return The saved patient if successful, or null if there is an error. + */ + @Override + public Patient addPatient(PatientDTO patientDTO) { + Patient patient = new Patient(patientDTO); + + return this.savePatient.execute(patient); + } + + /** + * Finds a stored patient by id. + * * @param id A long representing the patient's unique identifier + * @return The corresponding patient if successful, or throws an EntityNotFoundException if it is + * non-existent. + * @throws EntityNotFoundException When patient with id provided is non-existent + */ + @Override + public Patient findPatientById(Long id) throws EntityNotFoundException { + Patient patient = this.findPatientById.execute(id); + + if (patient == null) throw new EntityNotFoundException("No existing patient with this id"); + + return patient; + } + + /** + * Finds patients from the database. + * + * @param pageable Pagination information, such as size and page number + * * @return A paginated sublist containing data transfer objects with patients public information in the repository + */ + @Override + public Page findPatients(Pageable pageable) { + return findPatients.execute(pageable).map(PatientPublicDataDTO::new); + } + + /** * Updates an existing patient record - * @param patientUpdatedDataDTO Data transfer object containing the patient updated data along with their corresponding id - * - * @return The updated patient if successful, or null if there is an error. - */ - @Override - public Patient updatePatient(PatientUpdatedDataDTO patientUpdatedDataDTO) { - - Patient patient = findPatientById.execute(patientUpdatedDataDTO.id()); - - if (patient == null) throw new EntityNotFoundException("No existing patient with this id"); - - if (patientUpdatedDataDTO.name() != null) patient.setName(patientUpdatedDataDTO.name()); - - if (patientUpdatedDataDTO.telephone() != null) patient.setTelephone(patientUpdatedDataDTO.telephone()); - - if (patientUpdatedDataDTO.address() != null) { + * @param patientUpdatedDataDTO Data transfer object containing the patient updated data along with their corresponding id + * * @return The updated patient if successful, or null if there is an error. + */ + @Override + public Patient updatePatient(PatientUpdatedDataDTO patientUpdatedDataDTO) { - AddressDTO addressUpdatedDataDTO = patientUpdatedDataDTO.address(); - Address address = patient.getAddress(); + Patient patient = findPatientById.execute(patientUpdatedDataDTO.id()); - if (addressUpdatedDataDTO.street() != null) { - address.setStreet(addressUpdatedDataDTO.street()); - } + if (patient == null) throw new EntityNotFoundException("No existing patient with this id"); - if (addressUpdatedDataDTO.neighborhood() != null) { - address.setNeighborhood(addressUpdatedDataDTO.neighborhood()); - } + if (patientUpdatedDataDTO.name() != null) patient.setName(patientUpdatedDataDTO.name()); - if (addressUpdatedDataDTO.city() != null) { - address.setCity(addressUpdatedDataDTO.city()); - } + if (patientUpdatedDataDTO.telephone() != null) patient.setTelephone(patientUpdatedDataDTO.telephone()); - if (addressUpdatedDataDTO.zipCode() != null) { - address.setZipCode(addressUpdatedDataDTO.zipCode()); - } + if (patientUpdatedDataDTO.address() != null) { - if (addressUpdatedDataDTO.state() != null) { - address.setState(addressUpdatedDataDTO.state()); - } + AddressDTO addressUpdatedDataDTO = patientUpdatedDataDTO.address(); + Address address = patient.getAddress(); - if (addressUpdatedDataDTO.additionalDetails() != null) { - address.setAdditionalDetails(addressUpdatedDataDTO.additionalDetails()); - } + if (addressUpdatedDataDTO.street() != null) { + address.setStreet(addressUpdatedDataDTO.street()); + } - if (addressUpdatedDataDTO.houseNumber() != null) { - address.setHouseNumber(addressUpdatedDataDTO.houseNumber()); - } + if (addressUpdatedDataDTO.neighborhood() != null) { + address.setNeighborhood(addressUpdatedDataDTO.neighborhood()); + } - patient.setAddress(address); - } + if (addressUpdatedDataDTO.city() != null) { + address.setCity(addressUpdatedDataDTO.city()); + } - patient = savePatient.execute(patient); - - return patient; - } + if (addressUpdatedDataDTO.zipCode() != null) { + address.setZipCode(addressUpdatedDataDTO.zipCode()); + } - /** - * Deactivates an existing patient record by provided id - * - * @param id Long that represents the patient's unique identifier - * - * @return The deactivated patient if successful, or throws an - * EntityNotFoundException if it is non-existent. - * @throws EntityNotFoundException When patient with id provided is non-existent - */ - @Override - public Patient deactivatePatient(Long id) throws EntityNotFoundException { - Patient patient = findPatientById.execute(id); + if (addressUpdatedDataDTO.state() != null) { + address.setState(addressUpdatedDataDTO.state()); + } - if (patient == null) { - throw new EntityNotFoundException("No existing patient with this id"); - } - - patient.setActive(false); + if (addressUpdatedDataDTO.additionalDetails() != null) { + address.setAdditionalDetails(addressUpdatedDataDTO.additionalDetails()); + } - return savePatient.execute(patient); - } + if (addressUpdatedDataDTO.houseNumber() != null) { + address.setHouseNumber(addressUpdatedDataDTO.houseNumber()); + } -} + patient.setAddress(address); + } + + patient = savePatient.execute(patient); + + return patient; + } + + /** + * Deactivates an existing patient record by provided id + * * @param id Long that represents the patient's unique identifier + * * @return The deactivated patient if successful, or throws an + * EntityNotFoundException if it is non-existent. + * @throws EntityNotFoundException When patient with id provided is non-existent + */ + @Override + public Patient deactivatePatient(Long id) throws EntityNotFoundException { + Patient patient = findPatientById.execute(id); + + if (patient == null) { + throw new EntityNotFoundException("No existing patient with this id"); + } + + patient.setActive(false); + + return savePatient.execute(patient); + } + + /** + * Links an existing patient record to a new user account + * + * @param linkPatientToUserDTO Data transfer object containing the patient's ID, new username, password, and CPF + * + * @return The updated patient if successful + * @throws EntityNotFoundException If the patient is not found + * @throws IllegalArgumentException If the patient is already linked or CPF does not match + */ + @Transactional + public Patient linkPatientToUser(LinkPatientToUserDTO linkPatientToUserDTO) { + + // 1. ตรวจสอบว่า patientId มีอยู่ในระบบจริงหรือไม่ + Patient patient = findPatientById.execute(linkPatientToUserDTO.patientId()); + + if (patient == null) { + throw new EntityNotFoundException("Patient not found with ID: " + linkPatientToUserDTO.patientId()); + } + + // 2. ตรวจสอบว่าผู้ป่วยยังไม่ได้ถูกผูกกับบัญชีผู้ใช้ + if (patient.getUser() != null) { + throw new IllegalArgumentException("Patient is already linked to a user account."); + } + + // 3. ตรวจสอบ CPF เพื่อยืนยันตัวตน + if (!patient.getCpf().equals(linkPatientToUserDTO.cpf())) { + throw new IllegalArgumentException("Invalid CPF for the given patient."); + } + + // 4. สร้างบัญชีผู้ใช้ใหม่ + User newUser = new User(); + newUser.setLogin(linkPatientToUserDTO.username()); + newUser.setPassword(passwordEncoder.encode(linkPatientToUserDTO.password())); + // ตั้งค่าบทบาท (Role) + newUser.setRole(Role.ROLE_PATIENT); + User savedUser = userRepository.save(newUser); + + // 5. อัปเดตข้อมูลผู้ป่วยด้วย ID ของผู้ใช้ใหม่ + patient.setUser(savedUser); + return savePatient.execute(patient); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/services/PaymentServiceImpl.java b/src/main/java/com/mirna/hospitalmanagementapi/application/services/PaymentServiceImpl.java new file mode 100644 index 0000000..5663184 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/services/PaymentServiceImpl.java @@ -0,0 +1,59 @@ +package com.mirna.hospitalmanagementapi.application.services; + +import com.mirna.hospitalmanagementapi.domain.dtos.PaymentDTO; +import com.mirna.hospitalmanagementapi.domain.entities.Payment; +import com.mirna.hospitalmanagementapi.domain.repositories.PaymentRepository; +import com.mirna.hospitalmanagementapi.domain.services.PaymentService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; + +import com.mirna.hospitalmanagementapi.domain.services.ConsultationService; +import com.mirna.hospitalmanagementapi.domain.services.MedicalRecordService; + +import jakarta.persistence.EntityNotFoundException; +import java.util.List; + +@Service +public class PaymentServiceImpl implements PaymentService { + + @Autowired + private PaymentRepository paymentRepository; + + @Autowired + private ConsultationService consultationService; + + @Autowired + private MedicalRecordService medicalRecordService; + + @Override + public Payment createPayment(PaymentDTO paymentDTO) { + Payment payment = new Payment(); + + if (paymentDTO.consultationId() != null) { + payment.setConsultation(consultationService.findConsultationById(paymentDTO.consultationId())); + } + + if (paymentDTO.medicalRecordId() != null) { + payment.setMedicalRecord(medicalRecordService.getMedicalRecordById(paymentDTO.medicalRecordId())); + } + + payment.setAmount(paymentDTO.amount()); + payment.setPaymentMethod(paymentDTO.paymentMethod()); + payment.setPaymentDate(paymentDTO.paymentDate()); + + return paymentRepository.save(payment); + } + + @Override + public Payment getPaymentById(Long id) { + return paymentRepository.findById(id) + .orElseThrow(() -> new EntityNotFoundException("Payment with ID " + id + " not found.")); + } + + @Override + public Page getAllPayments(Pageable pageable) { + return paymentRepository.findAll(pageable); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/services/PrescriptionServiceImpl.java b/src/main/java/com/mirna/hospitalmanagementapi/application/services/PrescriptionServiceImpl.java new file mode 100644 index 0000000..7287ac6 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/services/PrescriptionServiceImpl.java @@ -0,0 +1,144 @@ +package com.mirna.hospitalmanagementapi.application.services; + +import com.mirna.hospitalmanagementapi.domain.dtos.PrescriptionDTO; +import com.mirna.hospitalmanagementapi.domain.dtos.inventory.InventoryTransactionDTO; +import com.mirna.hospitalmanagementapi.domain.entities.MedicalRecord; +import com.mirna.hospitalmanagementapi.domain.entities.Prescription; +import com.mirna.hospitalmanagementapi.domain.entities.inventory.InventoryItem; +import com.mirna.hospitalmanagementapi.domain.enums.TransactionType; +import com.mirna.hospitalmanagementapi.domain.repositories.PrescriptionRepository; +import com.mirna.hospitalmanagementapi.domain.services.MedicalRecordService; +import com.mirna.hospitalmanagementapi.domain.services.PrescriptionService; +import com.mirna.hospitalmanagementapi.domain.services.inventory.InventoryItemService; +import com.mirna.hospitalmanagementapi.domain.services.inventory.InventoryTransactionService; +import jakarta.persistence.EntityNotFoundException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import com.mirna.hospitalmanagementapi.domain.exceptions.ConsultationValidationException; + +import java.time.ZonedDateTime; +import java.util.UUID; + +@Service +public class PrescriptionServiceImpl implements PrescriptionService { + + @Autowired + private PrescriptionRepository prescriptionRepository; + + @Autowired + private MedicalRecordService medicalRecordService; + + @Autowired + private InventoryItemService inventoryItemService; + + @Autowired + private InventoryTransactionService inventoryTransactionService; + + @Override + @Transactional + public Prescription createPrescription(PrescriptionDTO prescriptionDTO) throws EntityNotFoundException, ConsultationValidationException { + MedicalRecord medicalRecord = medicalRecordService.getMedicalRecordById(prescriptionDTO.medicalRecordId()); + if (medicalRecord == null) { + throw new EntityNotFoundException("Medical record with ID " + prescriptionDTO.medicalRecordId() + " not found."); + } + + InventoryItem inventoryItem = inventoryItemService.findInventoryItemEntityById(prescriptionDTO.inventoryItemId()); + if (inventoryItem == null) { + throw new EntityNotFoundException("Inventory item with ID " + prescriptionDTO.inventoryItemId() + " not found."); + } + + if (inventoryItem.getCurrentStock() < prescriptionDTO.quantity()) { + throw new ConsultationValidationException("Not enough stock for this medication. Available: " + inventoryItem.getCurrentStock()); + } + + Prescription prescription = new Prescription(medicalRecord, inventoryItem, prescriptionDTO.quantity(), prescriptionDTO.instructions()); + Prescription savedPrescription = prescriptionRepository.save(prescription); + + InventoryTransactionDTO transactionDTO = new InventoryTransactionDTO(); + transactionDTO.setItemId(inventoryItem.getId()); + transactionDTO.setTransactionType(TransactionType.OUT); + transactionDTO.setQuantity(prescriptionDTO.quantity()); + transactionDTO.setRelatedDocumentId(savedPrescription.getId()); + transactionDTO.setNotes("เบิกยาตามใบสั่งยาเลขที่ " + savedPrescription.getId()); + transactionDTO.setTransactionDate(ZonedDateTime.now()); + + inventoryTransactionService.addTransaction(transactionDTO); + + return savedPrescription; + } + + @Override + public Prescription getPrescriptionById(UUID id) throws EntityNotFoundException { + return prescriptionRepository.findById(id) + .orElseThrow(() -> new EntityNotFoundException("Prescription not found with ID: " + id)); + } + + @Override + @Transactional + public Prescription updatePrescription(UUID id, PrescriptionDTO prescriptionDTO) + throws EntityNotFoundException, ConsultationValidationException { + + Prescription existingPrescription = getPrescriptionById(id); + + // Get old item and quantity before any changes + InventoryItem oldInventoryItem = existingPrescription.getInventoryItem(); + Integer oldQuantity = existingPrescription.getQuantity(); + + // Get the new item and quantity from the DTO + InventoryItem newInventoryItem = inventoryItemService.findInventoryItemEntityById(prescriptionDTO.inventoryItemId()); + if (newInventoryItem == null) { + throw new EntityNotFoundException("Inventory item with ID " + prescriptionDTO.inventoryItemId() + " not found."); + } + Integer newQuantity = prescriptionDTO.quantity(); + + // Determine the available stock for the new item. + // If the item has changed, we only care about the new item's current stock. + // If the item is the same, we must first "return" the old quantity to the stock count + // before checking if the new quantity is available. + Integer stockForCheck = newInventoryItem.getCurrentStock(); + if (oldInventoryItem.equals(newInventoryItem)) { + stockForCheck = stockForCheck + oldQuantity; + } + + // Now, perform the stock validation check with the adjusted stock. + if (stockForCheck < newQuantity) { + throw new ConsultationValidationException("Not enough stock for this medication. Available: " + stockForCheck); + } + + // Create transaction to return old quantity + InventoryTransactionDTO returnDto = new InventoryTransactionDTO(); + returnDto.setItemId(oldInventoryItem.getId()); + returnDto.setTransactionType(TransactionType.IN); + returnDto.setQuantity(oldQuantity); + returnDto.setRelatedDocumentId(existingPrescription.getId()); + returnDto.setNotes("คืนสต็อกจากการแก้ไขใบสั่งยาเลขที่ " + existingPrescription.getId()); + returnDto.setTransactionDate(ZonedDateTime.now()); + inventoryTransactionService.addTransaction(returnDto); + + // Update the prescription entity with the new details + existingPrescription.setMedicalRecord(medicalRecordService.getMedicalRecordById(prescriptionDTO.medicalRecordId())); + existingPrescription.setInventoryItem(newInventoryItem); + existingPrescription.setQuantity(newQuantity); + existingPrescription.setInstructions(prescriptionDTO.instructions()); + + // Create transaction to withdraw new quantity + InventoryTransactionDTO newDeductionDto = new InventoryTransactionDTO(); + newDeductionDto.setItemId(newInventoryItem.getId()); + newDeductionDto.setTransactionType(TransactionType.OUT); + newDeductionDto.setQuantity(newQuantity); + newDeductionDto.setRelatedDocumentId(existingPrescription.getId()); + newDeductionDto.setNotes("เบิกยาใหม่จากการแก้ไขใบสั่งยาเลขที่ " + existingPrescription.getId()); + newDeductionDto.setTransactionDate(ZonedDateTime.now()); + inventoryTransactionService.addTransaction(newDeductionDto); + + return prescriptionRepository.save(existingPrescription); + } + + @Override + public boolean isOwner(UUID prescriptionId, Long patientId) { + return prescriptionRepository.findById(prescriptionId) + .map(prescription -> prescription.getMedicalRecord().getPatient().getId().equals(patientId)) + .orElse(false); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/services/ReportServiceImpl.java b/src/main/java/com/mirna/hospitalmanagementapi/application/services/ReportServiceImpl.java new file mode 100644 index 0000000..7cca9f2 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/services/ReportServiceImpl.java @@ -0,0 +1,93 @@ +package com.mirna.hospitalmanagementapi.application.services; + +import com.mirna.hospitalmanagementapi.domain.dtos.FinancialReportDTO; +import com.mirna.hospitalmanagementapi.domain.repositories.PaymentRepository; +import com.mirna.hospitalmanagementapi.domain.repositories.InsuranceClaimRepository; +import com.mirna.hospitalmanagementapi.domain.repositories.inventory.InventoryTransactionRepository; +import com.mirna.hospitalmanagementapi.domain.services.ReportService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import java.math.BigDecimal; +import java.time.*; +import java.util.ArrayList; +import java.util.List; +import com.mirna.hospitalmanagementapi.domain.entities.inventory.InventoryItem; +import com.mirna.hospitalmanagementapi.domain.repositories.inventory.InventoryItemRepository; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import com.mirna.hospitalmanagementapi.domain.dtos.AppointmentReportDTO; +import com.mirna.hospitalmanagementapi.domain.repositories.ConsultationRepository; +import com.mirna.hospitalmanagementapi.domain.dtos.LowStockItemDTO; + +@Service +public class ReportServiceImpl implements ReportService { + + @Autowired + private PaymentRepository paymentRepository; + + @Autowired + private InsuranceClaimRepository insuranceClaimRepository; + + @Autowired + private InventoryTransactionRepository inventoryTransactionRepository; + + @Autowired + private InventoryItemRepository inventoryItemRepository; + + @Autowired + private ConsultationRepository consultationRepository; + + @Override + public List getFinancialReport(LocalDate startDate, LocalDate endDate) { + + List reports = new ArrayList<>(); + ZoneOffset zoneOffset = ZoneOffset.UTC; + + for (LocalDate date = startDate; !date.isAfter(endDate); date = date.plusDays(1)) { + + LocalDateTime startOfDay = date.atStartOfDay(); + LocalDateTime endOfDay = date.plusDays(1).atStartOfDay().minusNanos(1); + + // แปลง LocalDateTime เป็น OffsetDateTime สำหรับ PaymentRepository + OffsetDateTime startOffsetDateTime = startOfDay.atZone(ZoneId.systemDefault()).toOffsetDateTime(); + OffsetDateTime endOffsetDateTime = endOfDay.atZone(ZoneId.systemDefault()).toOffsetDateTime(); + BigDecimal totalRevenue = paymentRepository.sumAmountByDateRange(startOffsetDateTime, endOffsetDateTime); + + // ใช้ LocalDateTime สำหรับ InsuranceClaimRepository + BigDecimal totalInsuranceClaims = insuranceClaimRepository.sumClaimAmountByDateRange(startOfDay, endOfDay); + + // แปลง LocalDateTime เป็น ZonedDateTime สำหรับ InventoryTransactionRepository + ZonedDateTime startZoned = startOfDay.atZone(ZoneId.systemDefault()); + ZonedDateTime endZoned = endOfDay.atZone(ZoneId.systemDefault()); + BigDecimal totalInventoryCost = inventoryTransactionRepository.sumTotalCostByDateRange(startZoned, endZoned); + + if (totalRevenue == null) totalRevenue = BigDecimal.ZERO; + if (totalInsuranceClaims == null) totalInsuranceClaims = BigDecimal.ZERO; + if (totalInventoryCost == null) totalInventoryCost = BigDecimal.ZERO; + + reports.add(new FinancialReportDTO(date, totalRevenue, totalInsuranceClaims, totalInventoryCost)); + } + + return reports; + } + + @Override + public Page getLowStockItems(Pageable pageable) { + return inventoryItemRepository.findItemsRunningLow(pageable); + } + + @Override + public AppointmentReportDTO getAppointmentReport(LocalDate startDate, LocalDate endDate) { + // ใช้ Timezone ที่ต้องการ เช่น Asia/Bangkok + ZoneOffset zoneOffset = ZoneOffset.UTC; + + OffsetDateTime startDateTime = startDate.atStartOfDay().atOffset(zoneOffset); + OffsetDateTime endDateTime = endDate.atTime(LocalTime.MAX).atOffset(zoneOffset); + + Long totalAppointments = consultationRepository.countAllByDateRange(startDateTime, endDateTime); + Long canceledAppointments = consultationRepository.countCanceledByDateRange(startDateTime, endDateTime); + Long uniquePatients = consultationRepository.countTotalPatientsByDateRange(startDateTime, endDateTime); + + return new AppointmentReportDTO(totalAppointments, canceledAppointments, uniquePatients); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/services/StaffServiceImpl.java b/src/main/java/com/mirna/hospitalmanagementapi/application/services/StaffServiceImpl.java new file mode 100644 index 0000000..cb32f26 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/services/StaffServiceImpl.java @@ -0,0 +1,51 @@ +package com.mirna.hospitalmanagementapi.application.services; + +import com.mirna.hospitalmanagementapi.domain.entities.Staff; +import com.mirna.hospitalmanagementapi.domain.repositories.StaffRepository; +import com.mirna.hospitalmanagementapi.domain.services.StaffService; +import com.mirna.hospitalmanagementapi.domain.entities.auth.User; +import com.mirna.hospitalmanagementapi.domain.enums.Role; +import com.mirna.hospitalmanagementapi.domain.repositories.auth.UserRepository; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** + * This class is an implementation of the StaffService interface. + * + * This class provides methods to perform operations on Staff entity. + * + * @author FlookSP + * @version 1.0 + */ +@Service +public class StaffServiceImpl implements StaffService { + + @Autowired + private StaffRepository staffRepository; + + @Autowired + private UserRepository userRepository; + + @Autowired + private PasswordEncoder passwordEncoder; + + @Override + @Transactional + public Staff addStaff(Staff staff) { + //เข้ารหัส password + String encryptedPassword = passwordEncoder.encode(staff.getUser().getPassword()); + User user = new User(staff.getUser().getLogin(), encryptedPassword, Role.valueOf(staff.getUser().getRole().name())); + + // บันทึกบัญชีผู้ใช้งานลงในฐานข้อมูล + User savedUser = userRepository.save(user); + + // ตั้งค่า User Entity ที่บันทึกแล้วให้กับ Staff Entity + staff.setUser(savedUser); + + // บันทึก Staff Entity ลงในฐานข้อมูล + return staffRepository.save(staff); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/services/SupplierServiceImpl.java b/src/main/java/com/mirna/hospitalmanagementapi/application/services/SupplierServiceImpl.java new file mode 100644 index 0000000..7211825 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/services/SupplierServiceImpl.java @@ -0,0 +1,21 @@ +package com.mirna.hospitalmanagementapi.application.services; + +import com.mirna.hospitalmanagementapi.domain.dtos.inventory.SupplierDTO; +import com.mirna.hospitalmanagementapi.domain.entities.inventory.InventorySupplier; +import com.mirna.hospitalmanagementapi.domain.repositories.inventory.InventorySupplierRepository; +import com.mirna.hospitalmanagementapi.domain.services.inventory.SupplierService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class SupplierServiceImpl implements SupplierService { + + @Autowired + private InventorySupplierRepository inventorySupplierRepository; + + @Override + public InventorySupplier addSupplier(SupplierDTO supplierDTO) { + InventorySupplier newSupplier = new InventorySupplier(supplierDTO); + return inventorySupplierRepository.save(newSupplier); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/services/UserServiceImpl.java b/src/main/java/com/mirna/hospitalmanagementapi/application/services/UserServiceImpl.java index 212d7ad..ecae2d1 100644 --- a/src/main/java/com/mirna/hospitalmanagementapi/application/services/UserServiceImpl.java +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/services/UserServiceImpl.java @@ -2,55 +2,103 @@ package com.mirna.hospitalmanagementapi.application.services; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import com.mirna.hospitalmanagementapi.application.usecase.user.FindUserByLoginUseCase; import com.mirna.hospitalmanagementapi.application.usecase.user.SaveUserUseCase; import com.mirna.hospitalmanagementapi.domain.dtos.auth.UserDTO; import com.mirna.hospitalmanagementapi.domain.entities.auth.User; +import com.mirna.hospitalmanagementapi.domain.enums.Role; import com.mirna.hospitalmanagementapi.domain.services.UserService; +import com.mirna.hospitalmanagementapi.domain.repositories.auth.UserRepository; + +import com.mirna.hospitalmanagementapi.domain.dtos.auth.UserUpdateUsernameDTO; +import com.mirna.hospitalmanagementapi.domain.dtos.auth.UserUpdatePasswordDTO; +import com.mirna.hospitalmanagementapi.domain.dtos.auth.UserUpdateRoleDTO; /** * This class is an implementation of the UserService interface. * * This class provides methods to perform operations on User entity * - * @author Mirna Gama - * @version 1.0 + * @author Mirna Gama (Extended by FlookSP) + * @version 1.1 */ - @Service public class UserServiceImpl implements UserService { - @Autowired - private SaveUserUseCase saveUser; - - @Autowired - private FindUserByLoginUseCase findUserByLogin; - - /** - * Finds a stored user information by login. - * - * @param login A string representing the user's system login - * @return The corresponding user information if successful, or null if it is non-existent. - */ - @Override - public UserDetails findUserByLogin(String login) { - return findUserByLogin.execute(login); - } + @Autowired + private SaveUserUseCase saveUser; - /** - * Adds a new user to the repository. - * - * @param userDTO A data transfer object representing a user to add. - * @return The saved user if successful, or null if there is an error. - */ - @Override - public User addUser(UserDTO userDTO) { - - User user = new User(userDTO); - - return saveUser.execute(user); - } + @Autowired + private FindUserByLoginUseCase findUserByLogin; -} + @Autowired + private UserRepository userRepository; + + @Autowired + private PasswordEncoder passwordEncoder; + + @Override + public UserDetails findUserByLogin(String login) { + return findUserByLogin.execute(login); + } + + @Override + public User addUser(User user) { + return saveUser.execute(user); + } + + @Override + @Transactional + public User updateUsername(Long id, UserUpdateUsernameDTO userUpdateUsernameDTO) { + User user = findUserById(id); + user.setLogin(userUpdateUsernameDTO.username()); + return userRepository.save(user); + } + + @Override + @Transactional + public User updatePassword(Long id, UserUpdatePasswordDTO userUpdatePasswordDTO) { + User user = findUserById(id); + user.setPassword(passwordEncoder.encode(userUpdatePasswordDTO.password())); + return userRepository.save(user); + } + + @Override + @Transactional + public User updateRole(Long id, UserUpdateRoleDTO userUpdateRoleDTO) { + User user = findUserById(id); + try { + Role newRole = Role.valueOf(userUpdateRoleDTO.role().toUpperCase()); + user.setRole(newRole); + return userRepository.save(user); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException("Invalid role provided: " + userUpdateRoleDTO.role()); + } + } + + @Override + @Transactional + public User deactivateUser(Long id) { + User user = findUserById(id); + user.setEnabled(false); + return userRepository.save(user); + } + + private User findUserById(Long id) { + return userRepository.findById(id) + .orElseThrow(() -> new UsernameNotFoundException("User not found with id: " + id)); + } + + @Override + @Transactional + public User activateUser(Long id) { + User user = findUserById(id); + user.setEnabled(true); + return userRepository.save(user); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/services/auth/AuthServiceImpl.java b/src/main/java/com/mirna/hospitalmanagementapi/application/services/auth/AuthServiceImpl.java index 9e7d9c0..5b6d34d 100644 --- a/src/main/java/com/mirna/hospitalmanagementapi/application/services/auth/AuthServiceImpl.java +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/services/auth/AuthServiceImpl.java @@ -6,11 +6,29 @@ import org.springframework.security.authentication.UsernamePasswordAuthenticatio import org.springframework.security.core.Authentication; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import com.mirna.hospitalmanagementapi.domain.dtos.auth.UserDTO; import com.mirna.hospitalmanagementapi.domain.entities.auth.User; import com.mirna.hospitalmanagementapi.domain.services.UserService; import com.mirna.hospitalmanagementapi.domain.services.auth.AuthService; +import com.mirna.hospitalmanagementapi.domain.dtos.auth.PatientRegistrationDTO; +import com.mirna.hospitalmanagementapi.domain.dtos.auth.DoctorRegistrationDTO; +import com.mirna.hospitalmanagementapi.domain.enums.Role; +import com.mirna.hospitalmanagementapi.domain.entities.Patient; +import com.mirna.hospitalmanagementapi.domain.entities.Doctor; +import com.mirna.hospitalmanagementapi.domain.repositories.PatientRepository; +import com.mirna.hospitalmanagementapi.domain.repositories.DoctorRepository; + +import jakarta.persistence.EntityNotFoundException; +import jakarta.persistence.PersistenceException; + +import com.mirna.hospitalmanagementapi.domain.dtos.auth.StaffRegistrationDTO; +import com.mirna.hospitalmanagementapi.domain.entities.Staff; +import com.mirna.hospitalmanagementapi.domain.services.StaffService; +import com.mirna.hospitalmanagementapi.domain.entities.Nurse; +import com.mirna.hospitalmanagementapi.domain.repositories.NurseRepository; +import com.mirna.hospitalmanagementapi.domain.dtos.auth.NurseRegistrationDTO; /** * This class is an implementation of the AuthService interface. @@ -23,42 +41,142 @@ import com.mirna.hospitalmanagementapi.domain.services.auth.AuthService; @Service public class AuthServiceImpl implements AuthService { - @Autowired - private AuthenticationManager manager; + @Autowired + private AuthenticationManager manager; - @Autowired - private UserService userService; - - @Autowired - private PasswordEncoder passwordEncoder; - - /** - * Performs the user login - * - * @param userDTO Data transfer object containing user credentials for authentication operations - * @return A fully authentication object including the credentials - */ - @Override - public Authentication login(UserDTO userDTO) { - UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(userDTO.login(), - userDTO.password()); - - return manager.authenticate(token); - } + @Autowired + private UserService userService; - /** - * Performs the user registration - * - * @param userDTO Data transfer object containing user credentials for authentication operations - * @return A user object including the credentials - */ - @Override - public User register(UserDTO userDTO) { - - String encodedPassword = passwordEncoder.encode(userDTO.password()); - userDTO = new UserDTO(userDTO.login(), encodedPassword); - - return this.userService.addUser(userDTO); - } - -} + @Autowired + private PasswordEncoder passwordEncoder; + + @Autowired + private PatientRepository patientRepository; + + @Autowired + private DoctorRepository doctorRepository; + + @Autowired + private StaffService staffService; + + @Autowired + private NurseRepository nurseRepository; + + /** + * Performs the user login + * * @param userDTO Data transfer object containing user credentials for authentication operations + * @return A fully authentication object including the credentials + */ + @Override + public Authentication login(UserDTO userDTO) { + UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(userDTO.login(), + userDTO.password()); + + return manager.authenticate(token); + } + /* + @Override + public User register(UserDTO userDTO) { + String encodedPassword = passwordEncoder.encode(userDTO.password()); + // แก้ไขจาก userDTO เป็น User entity + User user = new User(userDTO.login(), encodedPassword, null); // ต้องกำหนด role ด้วย + return this.userService.addUser(user); + }*/ + + /** + * Performs the user registration (patients) + * * @param registrationDTO Data transfer object containing user and patient creation + * @return A user object including the credentials + */ + @Override + @Transactional + public User registerPatient(PatientRegistrationDTO registrationDTO) { + // 1. สร้างและบันทึก User + User user = new User(); + user.setLogin(registrationDTO.login()); + user.setPassword(passwordEncoder.encode(registrationDTO.password())); + user.setRole(Role.ROLE_PATIENT); + User savedUser = this.userService.addUser(user); + + // 2. สร้างโปรไฟล์ผู้ป่วยและเชื่อมโยงกับ User + Patient patient = new Patient(registrationDTO.patientData(), savedUser); + this.patientRepository.save(patient); + + return savedUser; + } + + /** + * Registers a new user account for a doctor and links it to an existing doctor record. + */ + @Override + @Transactional + public User registerDoctor(DoctorRegistrationDTO registrationDTO) { + // 1. ตรวจสอบว่ามีข้อมูลแพทย์ตาม CRM ที่ให้มาหรือไม่ + Doctor doctor = doctorRepository.findByCrm(registrationDTO.getCrm()); + if (doctor == null) { + throw new EntityNotFoundException("ไม่พบข้อมูลแพทย์ที่มี CRM: " + registrationDTO.getCrm()); + } + + // 2. ตรวจสอบว่าแพทย์คนนี้มีบัญชีผู้ใช้แล้วหรือยัง + if (doctor.getUser() != null) { + throw new PersistenceException("แพทย์คนนี้มีบัญชีผู้ใช้งานในระบบแล้ว."); + } + + // 3. สร้าง User ใหม่สำหรับแพทย์ + User user = new User(); + user.setLogin(registrationDTO.getLogin()); + user.setPassword(passwordEncoder.encode(registrationDTO.getPassword())); + user.setRole(Role.ROLE_DOCTOR); + user.setEnabled(true); + User savedUser = this.userService.addUser(user); + + // 4. เชื่อมโยงบัญชี User กับข้อมูลแพทย์ + doctor.setUser(savedUser); + doctorRepository.save(doctor); + + return savedUser; + } + + @Override + @Transactional + public Staff registerStaff(StaffRegistrationDTO dto) { + // สร้าง User Entity จากข้อมูล DTO + User user = new User(dto.login(), passwordEncoder.encode(dto.password()), dto.role()); + + // สร้าง Staff Entity จากข้อมูล DTO และ User Entity ที่สร้างไว้ + Staff staff = new Staff(dto.name(), dto.email(), dto.telephone(), user); + + // บันทึก Staff Entity โดย StaffService + return staffService.addStaff(staff); + } + + /** + * Registers a new user account for a nurse and links it to an existing nurse record. + */ + @Override + @Transactional + public User registerNurseAndLink(NurseRegistrationDTO registrationDTO) { + // 1. ตรวจสอบว่ามีข้อมูลพยาบาลตาม licenseNumber ที่ให้มาหรือไม่ + Nurse nurse = nurseRepository.findByLicenseNumber(registrationDTO.licenseNumber()) + .orElseThrow(() -> new EntityNotFoundException("ไม่พบข้อมูลพยาบาลที่มีหมายเลขใบอนุญาต: " + registrationDTO.licenseNumber())); + + // 2. ตรวจสอบว่าพยาบาลคนนี้มีบัญชีผู้ใช้แล้วหรือยัง + if (nurse.getUser() != null) { + throw new PersistenceException("พยาบาลคนนี้มีบัญชีผู้ใช้งานในระบบแล้ว."); + } + + // 3. สร้าง User ใหม่สำหรับพยาบาล + User user = new User(); + user.setLogin(registrationDTO.login()); + user.setPassword(passwordEncoder.encode(registrationDTO.password())); + user.setRole(Role.ROLE_NURSE); + user.setEnabled(true); + User savedUser = this.userService.addUser(user); + + // 4. เชื่อมโยงบัญชี User กับข้อมูลพยาบาล + nurse.setUser(savedUser); + nurseRepository.save(nurse); + + return savedUser; + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/usecase/auth/jwt/CreateJWTUseCase.java b/src/main/java/com/mirna/hospitalmanagementapi/application/usecase/auth/jwt/CreateJWTUseCase.java index 8ab503d..6e71348 100644 --- a/src/main/java/com/mirna/hospitalmanagementapi/application/usecase/auth/jwt/CreateJWTUseCase.java +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/usecase/auth/jwt/CreateJWTUseCase.java @@ -11,38 +11,24 @@ import com.auth0.jwt.JWT; import com.auth0.jwt.algorithms.Algorithm; import com.mirna.hospitalmanagementapi.domain.entities.auth.User; -/** - * This class is used to execute the JWT create method using Algorithm HMAC256 along with the authenticated user login and id - * - * @author Mirna Gama - * @version 1.0 - */ @Component public class CreateJWTUseCase { - @Value("${api.security.token.secret}") + @Value("${api.security.token.secret}") private String secret; - - /** Creates a json web token - * - * @param user The authenticated user - * @return A string containing the json web token signed with Algorithm HMAC256 - */ - public String execute(User user) { - Algorithm algorithm = Algorithm.HMAC256(secret); - - return JWT.create().withIssuer("Hospital-Management-API") - .withSubject(user.getLogin()) - .withClaim("id", user.getId()) - .withExpiresAt(_getExpirationDate()) - .sign(algorithm); - } - - /** - * Gets an expiration date for the token based on 2 hours duration - * @return Instant object containing the expiration date - */ - private Instant _getExpirationDate() { - return LocalDateTime.now().plusHours(2).toInstant(ZoneOffset.of("-03:00")); - } -} + + public String execute(User user) { + Algorithm algorithm = Algorithm.HMAC256(secret); + + return JWT.create().withIssuer("Hospital-Management-API (Extended Version)") + .withSubject(user.getLogin()) + .withClaim("id", user.getId()) + .withClaim("roles", user.getRole().name()) // <-- เพิ่มบรรทัดนี้ + .withExpiresAt(_getExpirationDate()) + .sign(algorithm); + } + + private Instant _getExpirationDate() { + return LocalDateTime.now().plusHours(2).toInstant(ZoneOffset.of("-03:00")); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/usecase/auth/jwt/GetJWTSubjectUseCase.java b/src/main/java/com/mirna/hospitalmanagementapi/application/usecase/auth/jwt/GetJWTSubjectUseCase.java index 1e5bd23..a872156 100644 --- a/src/main/java/com/mirna/hospitalmanagementapi/application/usecase/auth/jwt/GetJWTSubjectUseCase.java +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/usecase/auth/jwt/GetJWTSubjectUseCase.java @@ -29,7 +29,7 @@ public class GetJWTSubjectUseCase { public String execute(String token) { Algorithm algorithm = Algorithm.HMAC256(secret); DecodedJWT decodedJWT = JWT.require(algorithm) - .withIssuer("Hospital-Management-API") + .withIssuer("Hospital-Management-API (Extended Version)") .build() .verify(token); diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/usecase/consultation/FindConsultationByDoctorAndDateUseCase.java b/src/main/java/com/mirna/hospitalmanagementapi/application/usecase/consultation/FindConsultationByDoctorAndDateUseCase.java index bcb9ac7..2d70d5a 100644 --- a/src/main/java/com/mirna/hospitalmanagementapi/application/usecase/consultation/FindConsultationByDoctorAndDateUseCase.java +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/usecase/consultation/FindConsultationByDoctorAndDateUseCase.java @@ -1,34 +1,22 @@ package com.mirna.hospitalmanagementapi.application.usecase.consultation; -import java.time.LocalDateTime; - +import java.time.OffsetDateTime; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import com.mirna.hospitalmanagementapi.domain.entities.Consultation; import com.mirna.hospitalmanagementapi.domain.repositories.ConsultationRepository; -/** - * This class is used to execute the findConsultationByDoctorAndDate method from consultation repository - * - * @author Mirna Gama - * @version 1.0 - */ @Component public class FindConsultationByDoctorAndDateUseCase { - @Autowired - private ConsultationRepository consultationRepository; - - /** - * Executes the findConsultationByDoctorAndDate method from Consultation repository - * - * @param doctorId The doctor's id from the consultation - * @param consultationDate The date of the consultation - * @return The corresponding consultation if successful, or null if it is non-existent - * - */ - public Consultation execute(Long doctorId, LocalDateTime consultationDate) { - return this.consultationRepository.findConsultationByDoctorAndDate(doctorId, consultationDate); - } -} + @Autowired + private ConsultationRepository consultationRepository; + + public Consultation execute(Long doctorId, OffsetDateTime consultationDate) { + // เพิ่มบรรทัดนี้เพื่อกำหนดจุดสิ้นสุดของช่วงเวลา + OffsetDateTime consultationDatePlusOneMinute = consultationDate.plusMinutes(1); + + return this.consultationRepository.findConsultationByDoctorAndDate(doctorId, consultationDate, consultationDatePlusOneMinute); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/usecase/consultation/FindConsultationByPatientAndDateUseCase.java b/src/main/java/com/mirna/hospitalmanagementapi/application/usecase/consultation/FindConsultationByPatientAndDateUseCase.java index 157b600..2664ebc 100644 --- a/src/main/java/com/mirna/hospitalmanagementapi/application/usecase/consultation/FindConsultationByPatientAndDateUseCase.java +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/usecase/consultation/FindConsultationByPatientAndDateUseCase.java @@ -1,34 +1,22 @@ package com.mirna.hospitalmanagementapi.application.usecase.consultation; -import java.time.LocalDateTime; - +import java.time.OffsetDateTime; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import com.mirna.hospitalmanagementapi.domain.entities.Consultation; import com.mirna.hospitalmanagementapi.domain.repositories.ConsultationRepository; -/** - * This class is used to execute the findConsultationByPatientAndDate method from consultation repository - * - * @author Mirna Gama - * @version 1.0 - */ @Component public class FindConsultationByPatientAndDateUseCase { - @Autowired - private ConsultationRepository consultationRepository; - - /** - * Executes the findConsultationByPatientAndDate method from Consultation repository - * - * @param patientId The patient's id from the consultation - * @param consultationDate The date of the consultation - * @return The corresponding consultation if successful, or null if it is non-existent - * - */ - public Consultation execute(Long patientId, LocalDateTime consultationDate) { - return this.consultationRepository.findConsultationByPatientAndDate(patientId, consultationDate); - } -} + @Autowired + private ConsultationRepository consultationRepository; + + public Consultation execute(Long patientId, OffsetDateTime consultationDate) { + // เพิ่มบรรทัดนี้เพื่อกำหนดจุดสิ้นสุดของช่วงเวลา + OffsetDateTime consultationDatePlusOneMinute = consultationDate.plusMinutes(1); + + return this.consultationRepository.findConsultationByPatientAndDate(patientId, consultationDate, consultationDatePlusOneMinute); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/usecase/doctor/FindOneFreeDoctorBySpecialtyUseCase.java b/src/main/java/com/mirna/hospitalmanagementapi/application/usecase/doctor/FindOneFreeDoctorBySpecialtyUseCase.java index d7c96b9..6316782 100644 --- a/src/main/java/com/mirna/hospitalmanagementapi/application/usecase/doctor/FindOneFreeDoctorBySpecialtyUseCase.java +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/usecase/doctor/FindOneFreeDoctorBySpecialtyUseCase.java @@ -1,6 +1,9 @@ +// ไฟล์: FindOneFreeDoctorBySpecialtyUseCase.java package com.mirna.hospitalmanagementapi.application.usecase.doctor; -import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Random; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -9,27 +12,21 @@ import com.mirna.hospitalmanagementapi.domain.entities.Doctor; import com.mirna.hospitalmanagementapi.domain.enums.Specialty; import com.mirna.hospitalmanagementapi.domain.repositories.DoctorRepository; -/** - * This class is used to execute the findOneFreeDoctorBySpecialty method - * - * @author Mirna Gama - * @version 1.0 - */ @Component public class FindOneFreeDoctorBySpecialtyUseCase { - @Autowired - private DoctorRepository doctorRepository; - - /** - * Executes the findOneFreeDoctorBySpecialty method from Doctor repository - * - * @param specialty Desired specialty for doctor search - * @param consultationDate Date to check if the doctor is free - * @return A random free doctor with the defined specialty if successful, or null if it is non-existent - * - */ - public Doctor execute(Specialty specialty, LocalDateTime consultationDate) { - return doctorRepository.findOneFreeDoctorBySpecialty(specialty, consultationDate); - } -} + @Autowired + private DoctorRepository doctorRepository; + + public Doctor execute(Specialty specialty, OffsetDateTime consultationDate) { + // ใช้ Query ใหม่ที่แม่นยำกว่าเดิม + List freeDoctors = doctorRepository.findFreeDoctorsBySpecialty(specialty, consultationDate); + + if (freeDoctors.isEmpty()) { + return null; + } + + // เลือกแพทย์แบบสุ่มจากรายการที่ว่าง + return freeDoctors.get(new Random().nextInt(freeDoctors.size())); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/usecase/inventory/FindInventoryItemByIdUseCase.java b/src/main/java/com/mirna/hospitalmanagementapi/application/usecase/inventory/FindInventoryItemByIdUseCase.java new file mode 100644 index 0000000..8076344 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/usecase/inventory/FindInventoryItemByIdUseCase.java @@ -0,0 +1,18 @@ +package com.mirna.hospitalmanagementapi.application.usecase.inventory; + +import com.mirna.hospitalmanagementapi.domain.entities.inventory.InventoryItem; +import com.mirna.hospitalmanagementapi.domain.repositories.inventory.InventoryItemRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import java.util.UUID; + +@Component +public class FindInventoryItemByIdUseCase { + + @Autowired + private InventoryItemRepository inventoryItemRepository; + + public InventoryItem execute(UUID id) { + return inventoryItemRepository.findById(id).orElse(null); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/usecase/inventory/SaveInventoryItemUseCase.java b/src/main/java/com/mirna/hospitalmanagementapi/application/usecase/inventory/SaveInventoryItemUseCase.java new file mode 100644 index 0000000..400f43d --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/usecase/inventory/SaveInventoryItemUseCase.java @@ -0,0 +1,17 @@ +package com.mirna.hospitalmanagementapi.application.usecase.inventory; + +import com.mirna.hospitalmanagementapi.domain.entities.inventory.InventoryItem; +import com.mirna.hospitalmanagementapi.domain.repositories.inventory.InventoryItemRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class SaveInventoryItemUseCase { + + @Autowired + private InventoryItemRepository inventoryItemRepository; + + public InventoryItem execute(InventoryItem item) { + return inventoryItemRepository.save(item); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/usecase/medicalrecord/FindMedicalRecordByIdUseCase.java b/src/main/java/com/mirna/hospitalmanagementapi/application/usecase/medicalrecord/FindMedicalRecordByIdUseCase.java new file mode 100644 index 0000000..e1cfdd6 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/usecase/medicalrecord/FindMedicalRecordByIdUseCase.java @@ -0,0 +1,17 @@ +package com.mirna.hospitalmanagementapi.application.usecase.medicalrecord; + +import com.mirna.hospitalmanagementapi.domain.entities.MedicalRecord; +import com.mirna.hospitalmanagementapi.domain.repositories.MedicalRecordRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class FindMedicalRecordByIdUseCase { + + @Autowired + private MedicalRecordRepository medicalRecordRepository; + + public MedicalRecord execute(Long id) { + return medicalRecordRepository.findById(id).orElse(null); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/application/usecase/medicalrecord/SaveMedicalRecordUseCase.java b/src/main/java/com/mirna/hospitalmanagementapi/application/usecase/medicalrecord/SaveMedicalRecordUseCase.java new file mode 100644 index 0000000..bc7189c --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/application/usecase/medicalrecord/SaveMedicalRecordUseCase.java @@ -0,0 +1,17 @@ +package com.mirna.hospitalmanagementapi.application.usecase.medicalrecord; + +import com.mirna.hospitalmanagementapi.domain.entities.MedicalRecord; +import com.mirna.hospitalmanagementapi.domain.repositories.MedicalRecordRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class SaveMedicalRecordUseCase { + + @Autowired + private MedicalRecordRepository medicalRecordRepository; + + public MedicalRecord execute(MedicalRecord medicalRecord) { + return medicalRecordRepository.save(medicalRecord); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/AddressDTO.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/AddressDTO.java index 2f7b48d..3d6c222 100644 --- a/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/AddressDTO.java +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/AddressDTO.java @@ -18,7 +18,7 @@ public record AddressDTO( String neighborhood, @NotBlank(message="zipCode cannot be blank") - @Pattern(regexp="\\d{8}", message="invalid format for zipCode") + @Pattern(regexp="\\d{5}", message="invalid format for zipCode") String zipCode, @NotBlank(message="city cannot be blank") diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/AppointmentReportDTO.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/AppointmentReportDTO.java new file mode 100644 index 0000000..31eeb5a --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/AppointmentReportDTO.java @@ -0,0 +1,7 @@ +package com.mirna.hospitalmanagementapi.domain.dtos; + +public record AppointmentReportDTO( + Long totalAppointments, + Long canceledAppointments, + Long totalPatients +) {} diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/BillableItemDTO.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/BillableItemDTO.java new file mode 100644 index 0000000..79b08eb --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/BillableItemDTO.java @@ -0,0 +1,41 @@ +package com.mirna.hospitalmanagementapi.domain.dtos; + +import java.math.BigDecimal; +import jakarta.validation.constraints.NotNull; + +public class BillableItemDTO { + + @NotNull(message = "Item description is required") + private String itemDescription; + + @NotNull(message = "Quantity is required") + private Integer quantity; + + @NotNull(message = "Unit price is required") + private BigDecimal unitPrice; + + // Getters and Setters + public String getItemDescription() { + return itemDescription; + } + + public void setItemDescription(String itemDescription) { + this.itemDescription = itemDescription; + } + + public Integer getQuantity() { + return quantity; + } + + public void setQuantity(Integer quantity) { + this.quantity = quantity; + } + + public BigDecimal getUnitPrice() { + return unitPrice; + } + + public void setUnitPrice(BigDecimal unitPrice) { + this.unitPrice = unitPrice; + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/BillingDTO.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/BillingDTO.java new file mode 100644 index 0000000..b0f237b --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/BillingDTO.java @@ -0,0 +1,29 @@ +package com.mirna.hospitalmanagementapi.domain.dtos; + +import java.util.List; +import jakarta.validation.constraints.NotNull; + +public class BillingDTO { + + @NotNull(message = "Medical Record ID is required") + private Long medicalRecordId; + + private List billableItems; + + // Getters and Setters + public Long getMedicalRecordId() { + return medicalRecordId; + } + + public void setMedicalRecordId(Long medicalRecordId) { + this.medicalRecordId = medicalRecordId; + } + + public List getBillableItems() { + return billableItems; + } + + public void setBillableItems(List billableItems) { + this.billableItems = billableItems; + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/DoctorScheduleDTO.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/DoctorScheduleDTO.java new file mode 100644 index 0000000..ede7997 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/DoctorScheduleDTO.java @@ -0,0 +1,26 @@ +package com.mirna.hospitalmanagementapi.domain.dtos; + +import jakarta.validation.constraints.NotNull; +import java.time.LocalTime; +import java.time.DayOfWeek; + +/** + * A data transfer object representing a doctor schedule + * + * @author FlookSP + * @version 1.0 + */ +public record DoctorScheduleDTO( + + @NotNull(message = "Doctor ID cannot be null") + Long doctorId, + + @NotNull(message = "Day of week cannot be null") + DayOfWeek dayOfWeek, + + @NotNull(message = "Start time cannot be null") + LocalTime startTime, + + @NotNull(message = "End time cannot be null") + LocalTime endTime +) {} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/FinancialReportDTO.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/FinancialReportDTO.java new file mode 100644 index 0000000..7cb8e88 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/FinancialReportDTO.java @@ -0,0 +1,37 @@ +package com.mirna.hospitalmanagementapi.domain.dtos; + +import java.math.BigDecimal; +import java.time.LocalDate; + +public class FinancialReportDTO { + + private LocalDate date; + private BigDecimal totalRevenue; + private BigDecimal totalInsuranceClaims; + private BigDecimal totalInventoryCost; + + // Constructors + public FinancialReportDTO(LocalDate date, BigDecimal totalRevenue, BigDecimal totalInsuranceClaims, BigDecimal totalInventoryCost) { + this.date = date; + this.totalRevenue = totalRevenue; + this.totalInsuranceClaims = totalInsuranceClaims; + this.totalInventoryCost = totalInventoryCost; + } + + // Getters + public LocalDate getDate() { + return date; + } + + public BigDecimal getTotalRevenue() { + return totalRevenue; + } + + public BigDecimal getTotalInsuranceClaims() { + return totalInsuranceClaims; + } + + public BigDecimal getTotalInventoryCost() { + return totalInventoryCost; + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/InsuranceClaimDTO.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/InsuranceClaimDTO.java new file mode 100644 index 0000000..715b65b --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/InsuranceClaimDTO.java @@ -0,0 +1,74 @@ +package com.mirna.hospitalmanagementapi.domain.dtos; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + +public class InsuranceClaimDTO { + + @NotNull(message = "Billing ID is required") + private Long billingId; + + @NotNull(message = "Insurance Provider ID is required") + private Long insuranceProviderId; + + @NotBlank(message = "Claim number is required") + private String claimNumber; + + @NotNull(message = "Submission date is required") + private LocalDateTime submissionDate; + + private BigDecimal approvedAmount; + + private String claimStatus; // Can be an Enum in the future + + // Getters and Setters + public Long getBillingId() { + return billingId; + } + + public void setBillingId(Long billingId) { + this.billingId = billingId; + } + + public Long getInsuranceProviderId() { + return insuranceProviderId; + } + + public void setInsuranceProviderId(Long insuranceProviderId) { + this.insuranceProviderId = insuranceProviderId; + } + + public String getClaimNumber() { + return claimNumber; + } + + public void setClaimNumber(String claimNumber) { + this.claimNumber = claimNumber; + } + + public LocalDateTime getSubmissionDate() { + return submissionDate; + } + + public void setSubmissionDate(LocalDateTime submissionDate) { + this.submissionDate = submissionDate; + } + + public BigDecimal getApprovedAmount() { + return approvedAmount; + } + + public void setApprovedAmount(BigDecimal approvedAmount) { + this.approvedAmount = approvedAmount; + } + + public String getClaimStatus() { + return claimStatus; + } + + public void setClaimStatus(String claimStatus) { + this.claimStatus = claimStatus; + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/InsuranceProviderDTO.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/InsuranceProviderDTO.java new file mode 100644 index 0000000..0173cc1 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/InsuranceProviderDTO.java @@ -0,0 +1,16 @@ +package com.mirna.hospitalmanagementapi.domain.dtos; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; + +public record InsuranceProviderDTO( + @NotBlank(message = "Provider name is required") + @Size(min = 3, max = 255, message = "Provider name must be between 3 and 255 characters") + String providerName, + + String contactPerson, + + String contactEmail, + + String phoneNumber +) {} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/LabResultDTO.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/LabResultDTO.java new file mode 100644 index 0000000..3fc6812 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/LabResultDTO.java @@ -0,0 +1,80 @@ +package com.mirna.hospitalmanagementapi.domain.dtos; + +import com.mirna.hospitalmanagementapi.domain.enums.LabTestStatus; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import java.util.UUID; + +/** + * DTO สำหรับการส่งผ่านข้อมูลผลการตรวจแล็บ + * + * @author FlookSP + * @version 1.0 + */ +public class LabResultDTO { + + private UUID id; + + @NotBlank(message = "ชื่อการทดสอบต้องไม่ว่างเปล่า") + private String testName; + + private Double resultValue; + + private String unit; + + private String referenceRange; + + @NotNull(message = "สถานะการตรวจต้องไม่เป็นค่าว่าง") + private LabTestStatus status; + + public LabResultDTO() { + } + + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + + public String getTestName() { + return testName; + } + + public void setTestName(String testName) { + this.testName = testName; + } + + public Double getResultValue() { + return resultValue; + } + + public void setResultValue(Double resultValue) { + this.resultValue = resultValue; + } + + public String getUnit() { + return unit; + } + + public void setUnit(String unit) { + this.unit = unit; + } + + public String getReferenceRange() { + return referenceRange; + } + + public void setReferenceRange(String referenceRange) { + this.referenceRange = referenceRange; + } + + public LabTestStatus getStatus() { + return status; + } + + public void setStatus(LabTestStatus status) { + this.status = status; + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/LowStockItemDTO.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/LowStockItemDTO.java new file mode 100644 index 0000000..160c2c2 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/LowStockItemDTO.java @@ -0,0 +1,13 @@ +package com.mirna.hospitalmanagementapi.domain.dtos; + +import java.math.BigDecimal; +import java.util.UUID; + +public record LowStockItemDTO( + UUID id, + String itemName, + Integer currentStock, + String itemTypeName, + BigDecimal unitPrice, + Integer reorderLevel +) {} diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/MedicalEquipmentScheduleDTO.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/MedicalEquipmentScheduleDTO.java new file mode 100644 index 0000000..64a6c0f --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/MedicalEquipmentScheduleDTO.java @@ -0,0 +1,14 @@ +package com.mirna.hospitalmanagementapi.domain.dtos; + +import jakarta.validation.constraints.NotNull; +import java.time.LocalDateTime; +import java.util.UUID; // เพิ่ม import + +public record MedicalEquipmentScheduleDTO( + @NotNull(message = "Medical equipment ID is required") + UUID equipmentId, + @NotNull(message = "Start time is required") + LocalDateTime startTime, + @NotNull(message = "End time is required") + LocalDateTime endTime +) {} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/MedicalEquipmentScheduleResponseDTO.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/MedicalEquipmentScheduleResponseDTO.java new file mode 100644 index 0000000..94daa18 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/MedicalEquipmentScheduleResponseDTO.java @@ -0,0 +1,30 @@ +package com.mirna.hospitalmanagementapi.domain.dtos; + +import com.mirna.hospitalmanagementapi.domain.entities.MedicalEquipmentSchedule; +import java.time.LocalDateTime; +import java.util.UUID; + +public class MedicalEquipmentScheduleResponseDTO { + + private Long id; + private UUID equipmentId; + private String equipmentName; // สามารถเพิ่มชื่ออุปกรณ์ได้ + private LocalDateTime startTime; + private LocalDateTime endTime; + + public MedicalEquipmentScheduleResponseDTO(MedicalEquipmentSchedule entity) { + this.id = entity.getId(); + // เนื่องจากใน Service เราโหลด InventoryItem มาแล้ว จึงสามารถเข้าถึงได้ + this.equipmentId = entity.getInventoryItem().getId(); + this.equipmentName = entity.getInventoryItem().getItemName(); + this.startTime = entity.getStartTime(); + this.endTime = entity.getEndTime(); + } + + // Getters + public Long getId() { return id; } + public UUID getEquipmentId() { return equipmentId; } + public String getEquipmentName() { return equipmentName; } + public LocalDateTime getStartTime() { return startTime; } + public LocalDateTime getEndTime() { return endTime; } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/MedicalRecordDTO.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/MedicalRecordDTO.java new file mode 100644 index 0000000..23183f4 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/MedicalRecordDTO.java @@ -0,0 +1,48 @@ +package com.mirna.hospitalmanagementapi.domain.dtos; + +public class MedicalRecordDTO { + private Long patientId; + private Long doctorId; + private Long consultationId; + private String diagnosis; + private String treatment; + private String notes; + + // Getters and Setters + public Long getPatientId() { + return patientId; + } + public void setPatientId(Long patientId) { + this.patientId = patientId; + } + public Long getDoctorId() { + return doctorId; + } + public void setDoctorId(Long doctorId) { + this.doctorId = doctorId; + } + public Long getConsultationId() { + return consultationId; + } + public void setConsultationId(Long consultationId) { + this.consultationId = consultationId; + } + public String getDiagnosis() { + return diagnosis; + } + public void setDiagnosis(String diagnosis) { + this.diagnosis = diagnosis; + } + public String getTreatment() { + return treatment; + } + public void setTreatment(String treatment) { + this.treatment = treatment; + } + public String getNotes() { + return notes; + } + public void setNotes(String notes) { + this.notes = notes; + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/NurseDTO.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/NurseDTO.java new file mode 100644 index 0000000..d1ad4c0 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/NurseDTO.java @@ -0,0 +1,22 @@ +package com.mirna.hospitalmanagementapi.domain.dtos; + +import com.mirna.hospitalmanagementapi.domain.enums.ShiftType; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; + +public record NurseDTO( + @NotBlank(message = "Name is required") + String name, + @NotBlank(message = "License number is required") + String licenseNumber, + @NotBlank(message = "Email is required") + @Email(message = "Invalid email format") + String email, + @NotBlank(message = "Telephone is required") + @Pattern(regexp = "^\\+[0-9]{1,15}$", message = "Invalid telephone format") + String telephone, + @NotNull(message = "Shift type is required") + ShiftType shiftType +) {} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/NurseScheduleDTO.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/NurseScheduleDTO.java new file mode 100644 index 0000000..bae47c3 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/NurseScheduleDTO.java @@ -0,0 +1,13 @@ +package com.mirna.hospitalmanagementapi.domain.dtos; + +import jakarta.validation.constraints.NotNull; +import java.time.LocalDateTime; + +public record NurseScheduleDTO( + @NotNull(message = "Nurse ID is required") + Long nurseId, + @NotNull(message = "Start time is required") + LocalDateTime startTime, + @NotNull(message = "End time is required") + LocalDateTime endTime +) {} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/OperatingRoomDTO.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/OperatingRoomDTO.java new file mode 100644 index 0000000..8d3d545 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/OperatingRoomDTO.java @@ -0,0 +1,11 @@ +package com.mirna.hospitalmanagementapi.domain.dtos; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + +public record OperatingRoomDTO( + @NotBlank(message = "Room number is required") + String roomNumber, + @NotNull(message = "Availability status is required") + Boolean isAvailable +) {} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/OperatingRoomScheduleDTO.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/OperatingRoomScheduleDTO.java new file mode 100644 index 0000000..b03bc5e --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/OperatingRoomScheduleDTO.java @@ -0,0 +1,13 @@ +package com.mirna.hospitalmanagementapi.domain.dtos; + +import jakarta.validation.constraints.NotNull; +import java.time.LocalDateTime; + +public record OperatingRoomScheduleDTO( + @NotNull(message = "Operating room ID is required") + Long roomId, + @NotNull(message = "Start time is required") + LocalDateTime startTime, + @NotNull(message = "End time is required") + LocalDateTime endTime +) {} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/PaymentDTO.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/PaymentDTO.java new file mode 100644 index 0000000..5530a8e --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/PaymentDTO.java @@ -0,0 +1,23 @@ +package com.mirna.hospitalmanagementapi.domain.dtos; + +import java.math.BigDecimal; +import java.time.OffsetDateTime; + +import com.mirna.hospitalmanagementapi.domain.enums.PaymentMethod; +import jakarta.validation.constraints.NotNull; + +import com.mirna.hospitalmanagementapi.domain.enums.PaymentMethod; + +public record PaymentDTO( + Long consultationId, + Long medicalRecordId, + + @NotNull(message = "Amount cannot be null") + BigDecimal amount, + + @NotNull(message = "Payment method cannot be null") + PaymentMethod paymentMethod, + + @NotNull(message = "Payment date cannot be null") + OffsetDateTime paymentDate +) {} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/PrescriptionDTO.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/PrescriptionDTO.java new file mode 100644 index 0000000..1de13a8 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/PrescriptionDTO.java @@ -0,0 +1,29 @@ +package com.mirna.hospitalmanagementapi.domain.dtos; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import java.util.UUID; // เพิ่มการ import สำหรับ UUID + +/** + * A data transfer object representing a prescription + * + * @author FlookSP + * @version 1.2 + */ +public record PrescriptionDTO( + + @NotNull(message = "Medical record ID cannot be null") + Long medicalRecordId, + + // แก้ไขชนิดข้อมูลจาก Long เป็น UUID เพื่อให้สอดคล้องกับ InventoryItem + @NotNull(message = "Inventory item ID cannot be null") + UUID inventoryItemId, + + @NotNull(message = "Quantity cannot be null") + @Min(value = 1, message = "Quantity must be at least 1") + Integer quantity, + + @NotBlank(message = "Instructions cannot be blank") + String instructions +) {} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/PrescriptionResponseDTO.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/PrescriptionResponseDTO.java new file mode 100644 index 0000000..ca5db65 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/PrescriptionResponseDTO.java @@ -0,0 +1,32 @@ +package com.mirna.hospitalmanagementapi.domain.dtos; + +import java.time.OffsetDateTime; +import java.util.UUID; + +import com.mirna.hospitalmanagementapi.domain.entities.Prescription; +import com.mirna.hospitalmanagementapi.domain.entities.inventory.InventoryItem; +import com.mirna.hospitalmanagementapi.domain.entities.MedicalRecord; + +public record PrescriptionResponseDTO( + UUID id, // เปลี่ยนจาก Long เป็น UUID + Long medicalRecordId, + UUID inventoryItemId, + String itemName, + Integer quantity, + String instructions, + OffsetDateTime createdAt, + OffsetDateTime updatedAt +) { + public PrescriptionResponseDTO(Prescription prescription) { + this( + prescription.getId(), + prescription.getMedicalRecord().getId(), + prescription.getInventoryItem().getId(), + prescription.getInventoryItem().getItemName(), + prescription.getQuantity(), + prescription.getInstructions(), + prescription.getCreatedAt(), + prescription.getUpdatedAt() + ); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/auth/DoctorRegistrationDTO.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/auth/DoctorRegistrationDTO.java new file mode 100644 index 0000000..6a5dac9 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/auth/DoctorRegistrationDTO.java @@ -0,0 +1,48 @@ +package com.mirna.hospitalmanagementapi.domain.dtos.auth; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; + +/** + * Data Transfer Object for doctor registration. + * This DTO contains the credentials to register a new user and + * the verification data to link them to an existing doctor record. + */ +public class DoctorRegistrationDTO { + + @NotBlank(message = "Username must not be blank") + private String login; + + @NotBlank(message = "Password must not be blank") + @Size(min = 8, message = "Password must be at least 8 characters long") + private String password; + + @NotBlank(message = "CRM must not be blank") + private String crm; + + // Getters and Setters + public String getLogin() { + return login; + } + + public void setLogin(String login) { + this.login = login; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getCrm() { + return crm; + } + + public void setCrm(String crm) { + this.crm = crm; + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/auth/LinkPatientToUserDTO.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/auth/LinkPatientToUserDTO.java new file mode 100644 index 0000000..591f30e --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/auth/LinkPatientToUserDTO.java @@ -0,0 +1,21 @@ +package com.mirna.hospitalmanagementapi.domain.dtos.auth; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; + +public record LinkPatientToUserDTO( + + @NotNull(message = "Patient ID cannot be null") + Long patientId, + + @NotBlank(message = "Username cannot be blank") + String username, + + @NotBlank(message = "Password cannot be blank") + String password, + + @NotBlank(message = "CPF cannot be blank") + @Pattern(regexp = "\\d{11}", message = "Invalid CPF format") + String cpf +) {} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/auth/NurseRegistrationDTO.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/auth/NurseRegistrationDTO.java new file mode 100644 index 0000000..888d422 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/auth/NurseRegistrationDTO.java @@ -0,0 +1,14 @@ +package com.mirna.hospitalmanagementapi.domain.dtos.auth; + +import jakarta.validation.constraints.NotBlank; + +public record NurseRegistrationDTO( + @NotBlank(message = "Login cannot be blank") + String login, + + @NotBlank(message = "Password cannot be blank") + String password, + + @NotBlank(message = "License number cannot be blank") + String licenseNumber) { +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/auth/PatientRegistrationDTO.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/auth/PatientRegistrationDTO.java new file mode 100644 index 0000000..a4e9d19 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/auth/PatientRegistrationDTO.java @@ -0,0 +1,19 @@ +// DTO สำหรับการลงทะเบียนผู้ป่วยขึ้นมาใหม่ และใช้ PatientDTO ที่มีอยู่เป็นส่วนประกอบ (Composition) +package com.mirna.hospitalmanagementapi.domain.dtos.auth; + +import com.mirna.hospitalmanagementapi.domain.dtos.patient.PatientDTO; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + +public record PatientRegistrationDTO( + @NotBlank(message = "login cannot be blank") + String login, + + @NotBlank(message = "password cannot be blank") + String password, + + @NotNull(message = "patient data cannot be null") + @Valid + PatientDTO patientData +) {} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/auth/StaffRegistrationDTO.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/auth/StaffRegistrationDTO.java new file mode 100644 index 0000000..28ecee8 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/auth/StaffRegistrationDTO.java @@ -0,0 +1,36 @@ +package com.mirna.hospitalmanagementapi.domain.dtos.auth; + +import com.mirna.hospitalmanagementapi.domain.enums.Role; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; + +/** + * A data transfer object representing staff registration data. + * + * @author FlookSP + * @version 1.0 + */ +public record StaffRegistrationDTO( + + @NotBlank(message = "name cannot be blank") + String name, + + @NotBlank(message = "email cannot be blank") + @Email(message = "email format is invalid") + String email, + + @NotBlank(message = "telephone cannot be blank") + @Pattern(regexp = "^[0-9]{9,15}$", message = "telephone format is invalid") + String telephone, + + @NotBlank(message = "login cannot be blank") + String login, + + @NotBlank(message = "password cannot be blank") + String password, + + @NotNull(message = "role cannot be null") + Role role +) {} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/auth/UserUpdatePasswordDTO.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/auth/UserUpdatePasswordDTO.java new file mode 100644 index 0000000..fcea272 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/auth/UserUpdatePasswordDTO.java @@ -0,0 +1,15 @@ +package com.mirna.hospitalmanagementapi.domain.dtos.auth; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; + +/** + * A Data Transfer Object for updating a user's password. + * + * @author FlookSP + * @version 1.0 + */ +public record UserUpdatePasswordDTO( + @NotBlank(message = "Password cannot be blank") + String password +) {} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/auth/UserUpdateRoleDTO.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/auth/UserUpdateRoleDTO.java new file mode 100644 index 0000000..4e513ca --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/auth/UserUpdateRoleDTO.java @@ -0,0 +1,14 @@ +package com.mirna.hospitalmanagementapi.domain.dtos.auth; + +import jakarta.validation.constraints.NotBlank; + +/** + * A Data Transfer Object for updating a user's role. + * + * @author FlookSP + * @version 1.0 + */ +public record UserUpdateRoleDTO( + @NotBlank(message = "Role cannot be blank") + String role +) {} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/auth/UserUpdateUsernameDTO.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/auth/UserUpdateUsernameDTO.java new file mode 100644 index 0000000..beb12d0 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/auth/UserUpdateUsernameDTO.java @@ -0,0 +1,14 @@ +package com.mirna.hospitalmanagementapi.domain.dtos.auth; + +import jakarta.validation.constraints.NotBlank; + +/** + * A Data Transfer Object for updating a user's login name. + * + * @author FlookSP + * @version 1.0 + */ +public record UserUpdateUsernameDTO( + @NotBlank(message = "Username cannot be blank") + String username +) {} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/consultation/ConsultationDTO.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/consultation/ConsultationDTO.java index 6bf9b15..7c2791b 100644 --- a/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/consultation/ConsultationDTO.java +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/consultation/ConsultationDTO.java @@ -1,36 +1,49 @@ package com.mirna.hospitalmanagementapi.domain.dtos.consultation; -import java.time.LocalDateTime; - -import com.fasterxml.jackson.annotation.JsonFormat; +import java.time.OffsetDateTime; import com.mirna.hospitalmanagementapi.domain.enums.Specialty; import com.mirna.hospitalmanagementapi.domain.validators.consultation.constraints.ConsultationDateBusinessHours; import com.mirna.hospitalmanagementapi.domain.validators.consultation.constraints.ConsultationDateScheduledInAdvance; +import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.Future; import jakarta.validation.constraints.NotNull; /** -* Data transfer object used to transfer data that will be saved in a Consultation entity -* -* @author Mirna Gama -* @version 1.0 -*/ -public record ConsultationDTO ( + * Data transfer object used to transfer data that will be saved in a Consultation entity + * * @author Mirna Gama (Extended by FlookSP) + * @version 1.0 + */ +public record ConsultationDTO( - Long doctorId, - - @NotNull(message="patient id cannot be null") - Long patientId, - - @NotNull(message="consultation date cannot be null") - @Future - @JsonFormat(pattern = "dd/MM/yyyy HH:mm") - @ConsultationDateScheduledInAdvance - @ConsultationDateBusinessHours - LocalDateTime consultationDate, - - Specialty specialty + @Schema( + description = "รหัสแพทย์สำหรับจองโดยตรง (ไม่บังคับ) ต้องเป็น null หากระบุแผนก", + example = "1" + ) + Long doctorId, + + @Schema( + description = "รหัสผู้ป่วยที่ต้องการนัดหมาย", + example = "1" + ) + @NotNull(message="patient id cannot be null") + Long patientId, + + @Schema( + description = "วันที่และเวลาที่ต้องการนัดหมายในรูปแบบ ISO 8601 พร้อม timezone offset (+07:00 สำหรับประเทศไทย)", + example = "2025-09-15T10:00:00+07:00" + ) + @NotNull(message="consultation date cannot be null") + @Future + @ConsultationDateScheduledInAdvance + @ConsultationDateBusinessHours + OffsetDateTime consultationDate, + + @Schema( + description = "แผนกที่ต้องการ (ไม่บังคับ) ต้องระบุหากไม่ได้ระบุรหัสแพทย์", + example = "ORTHOPEDICS" + ) + Specialty specialty ) { - -} + +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/inventory/InventoryItemDTO.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/inventory/InventoryItemDTO.java new file mode 100644 index 0000000..e1a6574 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/inventory/InventoryItemDTO.java @@ -0,0 +1,44 @@ +package com.mirna.hospitalmanagementapi.domain.dtos.inventory; + +import com.mirna.hospitalmanagementapi.domain.enums.TransactionType; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.ZonedDateTime; +import java.util.UUID; + +// ปรับปรุง InventoryItemDTO: ย้ายแพ็กเกจและใช้ Lombok +@Data +@NoArgsConstructor +@AllArgsConstructor +public class InventoryItemDTO { + @NotNull(message = "Item Type ID cannot be null") + private UUID itemTypeId; + + @NotNull(message = "Supplier ID cannot be null") + private UUID supplierId; + + @NotBlank(message = "Item name cannot be blank") + private String itemName; + + private String description; + + private Integer currentStock; + + private String serialNumber; + + @NotBlank(message = "Unit of measure cannot be blank") + private String unitOfMeasure; + + private BigDecimal unitPrice; + private Integer reorderLevel; + private LocalDate expirationDate; + private String location; + private Boolean isActive; +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/inventory/InventoryItemResponseDTO.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/inventory/InventoryItemResponseDTO.java new file mode 100644 index 0000000..bdfa2f3 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/inventory/InventoryItemResponseDTO.java @@ -0,0 +1,60 @@ +package com.mirna.hospitalmanagementapi.domain.dtos.inventory; + +import com.mirna.hospitalmanagementapi.domain.entities.inventory.InventoryItem; +import com.mirna.hospitalmanagementapi.domain.entities.inventory.InventoryItemType; +import com.mirna.hospitalmanagementapi.domain.entities.inventory.InventorySupplier; + +import java.time.LocalDate; +import java.util.UUID; +import java.math.BigDecimal; // Import BigDecimal + +public class InventoryItemResponseDTO { + + private UUID id; + private String itemName; + private String description; + private Integer currentStock; + private String unitOfMeasure; + private BigDecimal unitPrice; // ใช้ BigDecimal เพื่อความแม่นยำ + private Integer reorderLevel; + private LocalDate expirationDate; + private String location; + private Boolean isActive; + + private InventoryItemTypeResponseDTO itemType; + private SupplierResponseDTO supplier; + + public InventoryItemResponseDTO(InventoryItem entity) { + this.id = entity.getId(); + this.itemName = entity.getItemName(); + this.description = entity.getDescription(); + this.currentStock = entity.getCurrentStock(); + this.unitOfMeasure = entity.getUnitOfMeasure(); + this.unitPrice = entity.getUnitPrice(); + this.reorderLevel = entity.getReorderLevel(); + this.expirationDate = entity.getExpirationDate(); + this.location = entity.getLocation(); + this.isActive = entity.getIsActive(); + + if (entity.getItemType() != null) { + this.itemType = new InventoryItemTypeResponseDTO(entity.getItemType()); + } + if (entity.getSupplier() != null) { + this.supplier = new SupplierResponseDTO(entity.getSupplier()); + } + } + + // Getters + public UUID getId() { return id; } + public String getItemName() { return itemName; } + public String getDescription() { return description; } + public Integer getCurrentStock() { return currentStock; } + public String getUnitOfMeasure() { return unitOfMeasure; } + public BigDecimal getUnitPrice() { return unitPrice; } + public Integer getReorderLevel() { return reorderLevel; } + public LocalDate getExpirationDate() { return expirationDate; } + public String getLocation() { return location; } + public Boolean getIsActive() { return isActive; } + public InventoryItemTypeResponseDTO getItemType() { return itemType; } + public SupplierResponseDTO getSupplier() { return supplier; } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/inventory/InventoryItemTypeResponseDTO.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/inventory/InventoryItemTypeResponseDTO.java new file mode 100644 index 0000000..62f3d1f --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/inventory/InventoryItemTypeResponseDTO.java @@ -0,0 +1,19 @@ +package com.mirna.hospitalmanagementapi.domain.dtos.inventory; + +import com.mirna.hospitalmanagementapi.domain.entities.inventory.InventoryItemType; +import java.util.UUID; + +public class InventoryItemTypeResponseDTO { + + private UUID id; + private String typeName; + + public InventoryItemTypeResponseDTO(InventoryItemType entity) { + this.id = entity.getId(); + this.typeName = entity.getTypeName(); + } + + // Getters + public UUID getId() { return id; } + public String getTypeName() { return typeName; } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/inventory/InventoryTransactionDTO.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/inventory/InventoryTransactionDTO.java new file mode 100644 index 0000000..6fa70af --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/inventory/InventoryTransactionDTO.java @@ -0,0 +1,31 @@ +package com.mirna.hospitalmanagementapi.domain.dtos.inventory; + +import com.mirna.hospitalmanagementapi.domain.enums.TransactionType; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Min; +import java.util.UUID; +import java.time.ZonedDateTime; + +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.AllArgsConstructor; + +// ปรับปรุง InventoryTransactionDTO: ใช้ Lombok และเพิ่มการรับค่า transactionDate +@Data +@NoArgsConstructor +@AllArgsConstructor +public class InventoryTransactionDTO { + @NotNull(message = "Item ID cannot be null") + private UUID itemId; + + @NotNull(message = "Transaction Type cannot be null") + private TransactionType transactionType; + + @NotNull(message = "Quantity cannot be null") + @Min(value = 1, message = "Quantity must be at least 1") + private Integer quantity; + + private UUID relatedDocumentId; + private String notes; + private ZonedDateTime transactionDate; // เพิ่ม field นี้เพื่อความยืดหยุ่นในการทดสอบ +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/inventory/InventoryTransactionItemResponseDTO.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/inventory/InventoryTransactionItemResponseDTO.java new file mode 100644 index 0000000..4eeb1c8 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/inventory/InventoryTransactionItemResponseDTO.java @@ -0,0 +1,41 @@ +package com.mirna.hospitalmanagementapi.domain.dtos.inventory; + +import com.mirna.hospitalmanagementapi.domain.entities.inventory.InventoryItem; +import java.util.UUID; + +public class InventoryTransactionItemResponseDTO { + private UUID id; + private String itemName; + private String unitOfMeasure; + + public InventoryTransactionItemResponseDTO(InventoryItem entity) { + this.id = entity.getId(); + this.itemName = entity.getItemName(); + this.unitOfMeasure = entity.getUnitOfMeasure(); + } + + // Getters and setters (คุณอาจใช้ @Data หรือ @Value จาก Lombok เพื่อลดโค้ด) + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + + public String getItemName() { + return itemName; + } + + public void setItemName(String itemName) { + this.itemName = itemName; + } + + public String getUnitOfMeasure() { + return unitOfMeasure; + } + + public void setUnitOfMeasure(String unitOfMeasure) { + this.unitOfMeasure = unitOfMeasure; + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/inventory/InventoryTransactionResponseDTO.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/inventory/InventoryTransactionResponseDTO.java new file mode 100644 index 0000000..3358279 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/inventory/InventoryTransactionResponseDTO.java @@ -0,0 +1,96 @@ +package com.mirna.hospitalmanagementapi.domain.dtos.inventory; + +import com.mirna.hospitalmanagementapi.domain.entities.inventory.InventoryTransaction; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.time.ZonedDateTime; +import java.util.UUID; + +public class InventoryTransactionResponseDTO { + + private UUID id; + + @JsonProperty("item") + private InventoryTransactionItemResponseDTO item; + + private String transactionType; + + private Integer quantity; + + private UUID relatedDocumentId; + + private String notes; + + private ZonedDateTime transactionDate; + + public InventoryTransactionResponseDTO(InventoryTransaction entity) { + this.id = entity.getId(); + // แปลง enum เป็น String + this.transactionType = entity.getTransactionType().toString(); + this.quantity = entity.getQuantity(); + this.relatedDocumentId = entity.getRelatedDocumentId(); + this.notes = entity.getNotes(); + this.transactionDate = entity.getTransactionDate(); + + // Map related item to a simplified DTO + if (entity.getItem() != null) { + this.item = new InventoryTransactionItemResponseDTO(entity.getItem()); + } + } + + // Getters and setters (คุณอาจใช้ @Data หรือ @Value จาก Lombok เพื่อลดโค้ด) + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + + public InventoryTransactionItemResponseDTO getItem() { + return item; + } + + public void setItem(InventoryTransactionItemResponseDTO item) { + this.item = item; + } + + public String getTransactionType() { + return transactionType; + } + + public void setTransactionType(String transactionType) { + this.transactionType = transactionType; + } + + public Integer getQuantity() { + return quantity; + } + + public void setQuantity(Integer quantity) { + this.quantity = quantity; + } + + public UUID getRelatedDocumentId() { + return relatedDocumentId; + } + + public void setRelatedDocumentId(UUID relatedDocumentId) { + this.relatedDocumentId = relatedDocumentId; + } + + public String getNotes() { + return notes; + } + + public void setNotes(String notes) { + this.notes = notes; + } + + public ZonedDateTime getTransactionDate() { + return transactionDate; + } + + public void setTransactionDate(ZonedDateTime transactionDate) { + this.transactionDate = transactionDate; + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/inventory/ItemTypeDTO.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/inventory/ItemTypeDTO.java new file mode 100644 index 0000000..e9c54b6 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/inventory/ItemTypeDTO.java @@ -0,0 +1,20 @@ +package com.mirna.hospitalmanagementapi.domain.dtos.inventory; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class ItemTypeDTO { + + @NotBlank(message = "ชื่อประเภทรายการสินค้าห้ามว่าง") + @Size(max = 50, message = "ชื่อประเภทรายการสินค้ามีความยาวได้สูงสุด 50 ตัวอักษร") + private String name; + + @Size(max = 500, message = "คำอธิบายมีความยาวได้สูงสุด 500 ตัวอักษร") + private String description; +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/inventory/SupplierDTO.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/inventory/SupplierDTO.java new file mode 100644 index 0000000..664e458 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/inventory/SupplierDTO.java @@ -0,0 +1,28 @@ +package com.mirna.hospitalmanagementapi.domain.dtos.inventory; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.Size; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class SupplierDTO { + + @NotBlank(message = "ชื่อผู้จำหน่ายห้ามว่าง") + @Size(max = 100, message = "ชื่อผู้จำหน่ายมีความยาวได้สูงสุด 100 ตัวอักษร") + private String supplierName; + + @Size(max = 100, message = "ชื่อผู้ติดต่อมีความยาวได้สูงสุด 100 ตัวอักษร") + private String contactPerson; + + @Email(message = "รูปแบบอีเมลไม่ถูกต้อง") + @Size(max = 100, message = "อีเมลมีความยาวได้สูงสุด 100 ตัวอักษร") + private String contactEmail; + + @Size(max = 20, message = "เบอร์โทรศัพท์มีความยาวได้สูงสุด 20 ตัวอักษร") + private String phoneNumber; +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/inventory/SupplierResponseDTO.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/inventory/SupplierResponseDTO.java new file mode 100644 index 0000000..1bdfa61 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/dtos/inventory/SupplierResponseDTO.java @@ -0,0 +1,24 @@ +package com.mirna.hospitalmanagementapi.domain.dtos.inventory; + +import com.mirna.hospitalmanagementapi.domain.entities.inventory.InventorySupplier; +import java.util.UUID; + +public class SupplierResponseDTO { + + private UUID id; + private String supplierName; + + public SupplierResponseDTO(InventorySupplier entity) { + this.id = entity.getId(); + this.supplierName = entity.getSupplierName(); + } + + // Getters + public UUID getId() { + return id; + } + + public String getSupplierName() { + return supplierName; + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/BillableItem.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/BillableItem.java new file mode 100644 index 0000000..40a3e3e --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/BillableItem.java @@ -0,0 +1,87 @@ +package com.mirna.hospitalmanagementapi.domain.entities; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import java.math.BigDecimal; +import com.fasterxml.jackson.annotation.JsonBackReference; + +@Entity +@Table(name = "billable_items") +public class BillableItem { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "item_description") + private String itemDescription; + + @Column(name = "quantity") + private Integer quantity; + + @Column(name = "unit_price") + private BigDecimal unitPrice; + + @Column(name = "item_amount") + private BigDecimal itemAmount; + + @ManyToOne + @JoinColumn(name = "billing_id") + @JsonBackReference + private Billing billing; + + // Getters and Setters + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getItemDescription() { + return itemDescription; + } + + public void setItemDescription(String itemDescription) { + this.itemDescription = itemDescription; + } + + public Integer getQuantity() { + return quantity; + } + + public void setQuantity(Integer quantity) { + this.quantity = quantity; + } + + public BigDecimal getUnitPrice() { + return unitPrice; + } + + public void setUnitPrice(BigDecimal unitPrice) { + this.unitPrice = unitPrice; + } + + public BigDecimal getItemAmount() { + return itemAmount; + } + + public void setItemAmount(BigDecimal itemAmount) { + this.itemAmount = itemAmount; + } + + public Billing getBilling() { + return billing; + } + + public void setBilling(Billing billing) { + this.billing = billing; + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/Billing.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/Billing.java new file mode 100644 index 0000000..86fd88f --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/Billing.java @@ -0,0 +1,95 @@ +package com.mirna.hospitalmanagementapi.domain.entities; + +import com.mirna.hospitalmanagementapi.domain.enums.PaymentStatus; +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.OneToMany; +import jakarta.persistence.OneToOne; +import jakarta.persistence.Table; +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; +import com.fasterxml.jackson.annotation.JsonManagedReference; + +@Entity +@Table(name = "billings") +public class Billing { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @OneToOne + @JoinColumn(name = "medical_record_id") + private MedicalRecord medicalRecord; + + @Column(name = "issue_date") + private LocalDateTime issueDate; + + @Column(name = "total_amount") + private BigDecimal totalAmount; + + @Enumerated(EnumType.STRING) + @Column(name = "payment_status") + private PaymentStatus paymentStatus; + + @OneToMany(mappedBy = "billing", cascade = CascadeType.ALL) + @JsonManagedReference + private List billableItems; + + // Getters and Setters + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public MedicalRecord getMedicalRecord() { + return medicalRecord; + } + + public void setMedicalRecord(MedicalRecord medicalRecord) { + this.medicalRecord = medicalRecord; + } + + public LocalDateTime getIssueDate() { + return issueDate; + } + + public void setIssueDate(LocalDateTime issueDate) { + this.issueDate = issueDate; + } + + public BigDecimal getTotalAmount() { + return totalAmount; + } + + public void setTotalAmount(BigDecimal totalAmount) { + this.totalAmount = totalAmount; + } + + public PaymentStatus getPaymentStatus() { + return paymentStatus; + } + + public void setPaymentStatus(PaymentStatus paymentStatus) { + this.paymentStatus = paymentStatus; + } + + public List getBillableItems() { + return billableItems; + } + + public void setBillableItems(List billableItems) { + this.billableItems = billableItems; + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/Consultation.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/Consultation.java index 370cb51..bdde9a5 100644 --- a/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/Consultation.java +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/Consultation.java @@ -1,6 +1,6 @@ package com.mirna.hospitalmanagementapi.domain.entities; -import java.time.LocalDateTime; +import java.time.OffsetDateTime; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.mirna.hospitalmanagementapi.domain.enums.ReasonCancellation; @@ -19,149 +19,152 @@ import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; import jakarta.validation.constraints.NotNull; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.mirna.hospitalmanagementapi.infra.converters.OffsetDateTimeConverter; + /** -* -* @author Mirna Gama -* @version 1.0 -*/ + * * @author Mirna Gama (Extended by FlookSP) + * @version 1.0 + */ @Table(name="consultations") @Entity(name="Consultation") +@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"}) public class Consultation { - - /** - * Constructor for class Consultation - * @param patient Patient who scheduled the consultation - * @param doctor Doctor who will be at the consultation - * @param consultationDate Scheduled date for the consultation - */ - public Consultation(Patient patient, Doctor doctor, LocalDateTime consultationDate) { - this.patient=patient; - this.doctor=doctor; - this.consultationDate=consultationDate; - this.canceled=false; - } - - public Consultation() {} - - @Id @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @NotNull(message="consultationDate cannot be null") - @Column(name="consultation_date") - private LocalDateTime consultationDate; - - @ManyToOne(fetch = FetchType.LAZY) - @JsonIgnoreProperties({"hibernateLazyInitializer", "handler"}) - @JoinColumn(name="patient_id") - private Patient patient; - @ManyToOne(fetch = FetchType.LAZY) - @JsonIgnoreProperties({"hibernateLazyInitializer", "handler"}) - @JoinColumn(name="doctor_id") - private Doctor doctor; - - @Column(name="canceled") - private boolean canceled; - - @Column(name="reason_cancellation") - @Enumerated(EnumType.STRING) - private ReasonCancellation reasonCancellation; + /** + * Constructor for class Consultation + * @param patient Patient who scheduled the consultation + * @param doctor Doctor who will be at the consultation + * @param consultationDate Scheduled date for the consultation + */ + public Consultation(Patient patient, Doctor doctor, OffsetDateTime consultationDate) { // เปลี่ยนชนิดข้อมูล + this.patient=patient; + this.doctor=doctor; + this.consultationDate=consultationDate; + this.canceled=false; + } - /** - * Returns the consultation id. - * @return A Long representing the consultation id. - */ - public Long getId() { - return id; - } + public Consultation() {} - /** - * Sets the consultation id. - * @param id The consultation's unique identifier. - */ - public void setId(Long id) { - this.id = id; - } + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; - /** - * Returns the consultation date - * @return A local date time object representing the consultation date - */ - public LocalDateTime getConsultationDate() { - return consultationDate; - } + @NotNull(message="consultationDate cannot be null") + @Column(name="consultation_date") + private OffsetDateTime consultationDate; // เปลี่ยนชนิดข้อมูล - /** - * Sets the consultation date. - * @param consultationDate Scheduled date for the consultation - */ - public void setConsultationDate(LocalDateTime consultationDate) { - this.consultationDate = consultationDate; - } + @ManyToOne(fetch = FetchType.LAZY) + @JsonIgnoreProperties({"hibernateLazyInitializer", "handler"}) + @JoinColumn(name="patient_id") + private Patient patient; - /** - * Returns the patient - * @return A Patient entity representing the patient - */ - public Patient getPatient() { - return patient; - } + @ManyToOne(fetch = FetchType.LAZY) + @JsonIgnoreProperties({"hibernateLazyInitializer", "handler"}) + @JoinColumn(name="doctor_id") + private Doctor doctor; - /** - * Sets the patient. - * @param patient Patient who scheduled the consultation - */ - public void setPatient(Patient patient) { - this.patient = patient; - } + @Column(name="canceled") + private boolean canceled; - /** - * Returns the doctor - * @return A Doctor entity representing the doctor - */ - public Doctor getDoctor() { - return doctor; - } + @Column(name="reason_cancellation") + @Enumerated(EnumType.STRING) + private ReasonCancellation reasonCancellation; - /** - * Sets the doctor. - * @param doctor Doctor who will be at the consultation - */ - public void setDoctor(Doctor doctor) { - this.doctor = doctor; - } + /** + * Returns the consultation id. + * @return A Long representing the consultation id. + */ + public Long getId() { + return id; + } - /** - * Returns the canceled - * @return A boolean value that states whether the consultation is canceled in the system - */ - public boolean isCanceled() { - return canceled; - } + /** + * Sets the consultation id. + * @param id The consultation's unique identifier. + */ + public void setId(Long id) { + this.id = id; + } - /** - * Sets the canceled - * @param canceled Must not be null. Starts with the false value by default - */ - public void setCanceled(boolean canceled) { - this.canceled = canceled; - } + /** + * Returns the consultation date + * @return A local date time object representing the consultation date + */ + public OffsetDateTime getConsultationDate() { // เปลี่ยนชนิดข้อมูล + return consultationDate; + } - /** - * Returns the reason of consultation cancellation - * @return An enum class representing the reason of cancellation. - * @see ReasonCancellation - */ - public ReasonCancellation getReasonCancellation() { - return reasonCancellation; - } + /** + * Sets the consultation date. + * @param consultationDate Scheduled date for the consultation + */ + public void setConsultationDate(OffsetDateTime consultationDate) { // เปลี่ยนชนิดข้อมูล + this.consultationDate = consultationDate; + } - /** - * Sets the reason of consultation cancellation - * @param reasonCancellation - */ - public void setReasonCancellation(ReasonCancellation reasonCancellation) { - this.reasonCancellation = reasonCancellation; - } - -} + /** + * Returns the patient + * @return A Patient entity representing the patient + */ + public Patient getPatient() { + return patient; + } + + /** + * Sets the patient. + * @param patient Patient who scheduled the consultation + */ + public void setPatient(Patient patient) { + this.patient = patient; + } + + /** + * Returns the doctor + * @return A Doctor entity representing the doctor + */ + public Doctor getDoctor() { + return doctor; + } + + /** + * Sets the doctor. + * @param doctor Doctor who will be at the consultation + */ + public void setDoctor(Doctor doctor) { + this.doctor = doctor; + } + + /** + * Returns the canceled + * @return A boolean value that states whether the consultation is canceled in the system + */ + public boolean isCanceled() { + return canceled; + } + + /** + * Sets the canceled + * @param canceled Must not be null. Starts with the false value by default + */ + public void setCanceled(boolean canceled) { + this.canceled = canceled; + } + + /** + * Returns the reason of consultation cancellation + * @return An enum class representing the reason of cancellation. + * @see ReasonCancellation + */ + public ReasonCancellation getReasonCancellation() { + return reasonCancellation; + } + + /** + * Sets the reason of consultation cancellation + * @param reasonCancellation + */ + public void setReasonCancellation(ReasonCancellation reasonCancellation) { + this.reasonCancellation = reasonCancellation; + } + +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/Doctor.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/Doctor.java index 2e09d85..c5f4abc 100644 --- a/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/Doctor.java +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/Doctor.java @@ -2,6 +2,7 @@ package com.mirna.hospitalmanagementapi.domain.entities; import com.mirna.hospitalmanagementapi.domain.dtos.doctor.DoctorDTO; import com.mirna.hospitalmanagementapi.domain.enums.Specialty; +import com.mirna.hospitalmanagementapi.domain.entities.auth.User; import jakarta.persistence.Column; import jakarta.persistence.Embedded; @@ -11,195 +12,150 @@ import jakarta.persistence.Enumerated; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.OneToOne; import jakarta.persistence.Table; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonBackReference; -/** -* -* @author Mirna Gama -* @version 1.0 -*/ @Table(name="doctors") @Entity(name="Doctor") +@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"}) public class Doctor { - /** - * Constructor for class Doctor - * @param doctorDTO Data transfer object containing Doctor entity information - * @see DoctorDTO - */ - public Doctor(DoctorDTO doctorDTO) { - this.active=true; - this.name=doctorDTO.name(); - this.email=doctorDTO.email(); - this.crm=doctorDTO.crm(); - this.telephone=doctorDTO.telephone(); - this.specialty=doctorDTO.specialty(); - this.address = new Address(doctorDTO.address()); - } - - public Doctor(){} + // Constructor เดิม + public Doctor(DoctorDTO doctorDTO) { + this.active=true; + this.name=doctorDTO.name(); + this.email=doctorDTO.email(); + this.crm=doctorDTO.crm(); + this.telephone=doctorDTO.telephone(); + this.specialty=doctorDTO.specialty(); + this.address = new Address(doctorDTO.address()); + } - @Id @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @NotBlank(message="name cannot be blank") - @Column(name="name") - private String name; - - @NotBlank(message="email cannot be blank") - @Column(name="email") - private String email; - - @NotBlank(message="crm cannot be blank") - @Column(name="crm", length = 6) - private String crm; - - @NotBlank(message="telephone cannot be blank") - @Column(name="telephone") - private String telephone; - - @NotNull(message="specialty cannot be null") - @Enumerated(EnumType.STRING) - private Specialty specialty; + // Constructor ที่เพิ่มเข้ามาใหม่ + public Doctor(DoctorDTO doctorDTO, User user) { + this.active=true; + this.name=doctorDTO.name(); + this.email=doctorDTO.email(); + this.crm=doctorDTO.crm(); + this.telephone=doctorDTO.telephone(); + this.specialty=doctorDTO.specialty(); + this.address = new Address(doctorDTO.address()); + this.user = user; // กำหนด User ให้กับ Doctor + } - @NotNull(message="active cannot be null") - @Column(name="active") - private Boolean active; - - @NotNull(message="address cannot be null") - @Embedded - private Address address; + public Doctor(){} - /** - * Returns the doctor id. - * @return A Long representing the doctor id. - */ - public Long getId() { - return id; - } + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; - /** - * Sets the doctor id. - * @param id The doctor's unique identifier. - */ - public void setId(Long id) { - this.id = id; - } + // เพิ่มความสัมพันธ์ OneToOne กับ User + @OneToOne + @JoinColumn(name = "user_id", referencedColumnName = "id", unique = true) + @JsonBackReference // บอกให้ Jackson ละเว้นการแปลงฟิลด์นี้ + private User user; - /** - * Returns the name - * @return A string representing the doctor's name. - */ - public String getName() { - return name; - } + @NotBlank(message="name cannot be blank") + @Column(name="name") + private String name; - /** - * Sets the name - * @param name Must not be blank. - */ - public void setName(String name) { - this.name = name; - } + @NotBlank(message="email cannot be blank") + @Column(name="email") + private String email; - /** - * Returns the email - * @return A string representing the doctor's email. - */ - public String getEmail() { - return email; - } + @NotBlank(message="crm cannot be blank") + @Column(name="crm", length = 6) + private String crm; - /** - * Sets the email - * @param email Must not be blank. - */ - public void setEmail(String email) { - this.email = email; - } + @NotBlank(message="telephone cannot be blank") + @Column(name="telephone") + private String telephone; - /** - * Returns the crm - * @return A string representing the doctor's crm. - */ - public String getCrm() { - return crm; - } + @NotNull(message="specialty cannot be null") + @Enumerated(EnumType.STRING) + private Specialty specialty; - /** - * Sets the crm - * @param crm Must not be blank. - */ - public void setCrm(String crm) { - this.crm = crm; - } + @NotNull(message="active cannot be null") + @Column(name="active") + private Boolean active; - /** - * Returns the telephone - * @return A string representing the doctor's telephone. - */ - public String getTelephone() { - return telephone; - } + @NotNull(message="address cannot be null") + @Embedded + private Address address; - /** - * Sets the telephone - * @param telephone Must not be blank. - */ - public void setTelephone(String telephone) { - this.telephone = telephone; - } + public Long getId() { + return id; + } - /** - * Returns the specialty - * @return An enum class representing the doctor's specialty. - * @see Specialty - */ - public Specialty getSpecialty() { - return specialty; - } + public void setId(Long id) { + this.id = id; + } - /** - * Sets the specialty - * @param specialty Must not be null. - */ - public void setSpecialty(Specialty specialty) { - this.specialty = specialty; - } + public String getName() { + return name; + } - /** - * Returns the active - * @return A boolean value that states whether the doctor is active in the system - */ - public Boolean getActive() { - return active; - } + public void setName(String name) { + this.name = name; + } - /** - * Sets the active - * @param active Must not be null. Starts with the true value by default - */ - public void setActive(Boolean active) { - this.active = active; - } + public String getEmail() { + return email; + } - /** - * Returns the address - * @return An object class representing the doctor's address. - * @see Address - */ - public Address getAddress() { - return address; - } + public void setEmail(String email) { + this.email = email; + } - /** - * Sets the address - * @param address Must not be null. - */ - public void setAddress(Address address) { - this.address = address; - } - -} + public String getCrm() { + return crm; + } + + public void setCrm(String crm) { + this.crm = crm; + } + + public String getTelephone() { + return telephone; + } + + public void setTelephone(String telephone) { + this.telephone = telephone; + } + + public Specialty getSpecialty() { + return specialty; + } + + public void setSpecialty(Specialty specialty) { + this.specialty = specialty; + } + + public Boolean getActive() { + return active; + } + + public void setActive(Boolean active) { + this.active = active; + } + + public Address getAddress() { + return address; + } + + public void setAddress(Address address) { + this.address = address; + } + + public User getUser() { + return user; + } + + public void setUser(User user) { + this.user = user; + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/DoctorSchedule.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/DoctorSchedule.java new file mode 100644 index 0000000..67d4ae5 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/DoctorSchedule.java @@ -0,0 +1,68 @@ +package com.mirna.hospitalmanagementapi.domain.entities; + +import java.time.DayOfWeek; +import java.time.LocalTime; +import com.fasterxml.jackson.annotation.JsonIgnore; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import jakarta.persistence.UniqueConstraint; +import jakarta.validation.constraints.NotNull; + +@Table(name = "doctor_schedules", uniqueConstraints = { + @UniqueConstraint(columnNames = {"doctor_id", "day_of_week", "start_time"}) +}) +@Entity(name = "DoctorSchedule") +public class DoctorSchedule { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne + @JoinColumn(name = "doctor_id") + @NotNull(message = "doctor cannot be null") + @JsonIgnore + private Doctor doctor; + + @NotNull(message = "dayOfWeek cannot be null") + @Enumerated(EnumType.STRING) + @Column(name = "day_of_week") + private DayOfWeek dayOfWeek; + + @NotNull(message = "start time cannot be null") + @Column(name = "start_time") + private LocalTime startTime; + + @NotNull(message = "end time cannot be null") + @Column(name = "end_time") + private LocalTime endTime; + + public DoctorSchedule() {} + + public DoctorSchedule(Doctor doctor, DayOfWeek dayOfWeek, LocalTime startTime, LocalTime endTime) { + this.doctor = doctor; + this.dayOfWeek = dayOfWeek; + this.startTime = startTime; + this.endTime = endTime; + } + + // Getters and Setters + public Long getId() { return id; } + public void setId(Long id) { this.id = id; } + public Doctor getDoctor() { return doctor; } + public void setDoctor(Doctor doctor) { this.doctor = doctor; } + public DayOfWeek getDayOfWeek() { return dayOfWeek; } + public void setDayOfWeek(DayOfWeek dayOfWeek) { this.dayOfWeek = dayOfWeek; } + public LocalTime getStartTime() { return startTime; } + public void setStartTime(LocalTime startTime) { this.startTime = startTime; } + public LocalTime getEndTime() { return endTime; } + public void setEndTime(LocalTime endTime) { this.endTime = endTime; } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/InsuranceClaim.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/InsuranceClaim.java new file mode 100644 index 0000000..137e53c --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/InsuranceClaim.java @@ -0,0 +1,103 @@ +package com.mirna.hospitalmanagementapi.domain.entities; + +import com.mirna.hospitalmanagementapi.domain.enums.ClaimStatus; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToOne; +import jakarta.persistence.Table; +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Entity +@Table(name = "insurance_claims") +public class InsuranceClaim { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @OneToOne + @JoinColumn(name = "billing_id") + private Billing billing; + + @ManyToOne + @JoinColumn(name = "insurance_provider_id") + private InsuranceProvider insuranceProvider; + + @Column(name = "claim_number") + private String claimNumber; + + @Column(name = "submission_date") + private LocalDateTime submissionDate; + + @Column(name = "approved_amount") + private BigDecimal approvedAmount; + + @Enumerated(EnumType.STRING) + @Column(name = "claim_status") + private ClaimStatus claimStatus; + + // Getters and Setters + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Billing getBilling() { + return billing; + } + + public void setBilling(Billing billing) { + this.billing = billing; + } + + public InsuranceProvider getInsuranceProvider() { + return insuranceProvider; + } + + public void setInsuranceProvider(InsuranceProvider insuranceProvider) { + this.insuranceProvider = insuranceProvider; + } + + public String getClaimNumber() { + return claimNumber; + } + + public void setClaimNumber(String claimNumber) { + this.claimNumber = claimNumber; + } + + public LocalDateTime getSubmissionDate() { + return submissionDate; + } + + public void setSubmissionDate(LocalDateTime submissionDate) { + this.submissionDate = submissionDate; + } + + public BigDecimal getApprovedAmount() { + return approvedAmount; + } + + public void setApprovedAmount(BigDecimal approvedAmount) { + this.approvedAmount = approvedAmount; + } + + public ClaimStatus getClaimStatus() { + return claimStatus; + } + + public void setClaimStatus(ClaimStatus claimStatus) { + this.claimStatus = claimStatus; + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/InsuranceProvider.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/InsuranceProvider.java new file mode 100644 index 0000000..def5a72 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/InsuranceProvider.java @@ -0,0 +1,70 @@ +package com.mirna.hospitalmanagementapi.domain.entities; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; + +@Entity +@Table(name = "insurance_providers") +public class InsuranceProvider { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "provider_name", nullable = false) + private String providerName; + + @Column(name = "contact_person") + private String contactPerson; + + @Column(name = "contact_email") + private String contactEmail; + + @Column(name = "phone_number") + private String phoneNumber; + + // Getters and Setters + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getProviderName() { + return providerName; + } + + public void setProviderName(String providerName) { + this.providerName = providerName; + } + + public String getContactPerson() { + return contactPerson; + } + + public void setContactPerson(String contactPerson) { + this.contactPerson = contactPerson; + } + + public String getContactEmail() { + return contactEmail; + } + + public void setContactEmail(String contactEmail) { + this.contactEmail = contactEmail; + } + + public String getPhoneNumber() { + return phoneNumber; + } + + public void setPhoneNumber(String phoneNumber) { + this.phoneNumber = phoneNumber; + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/LabResult.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/LabResult.java new file mode 100644 index 0000000..4e74ce2 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/LabResult.java @@ -0,0 +1,105 @@ +package com.mirna.hospitalmanagementapi.domain.entities; + +import com.mirna.hospitalmanagementapi.domain.enums.LabTestStatus; +import jakarta.persistence.*; +import java.time.LocalDateTime; +import java.util.UUID; +import com.fasterxml.jackson.annotation.JsonBackReference; + +@Entity +@Table(name = "lab_results") +public class LabResult { + + @Id + @GeneratedValue(strategy = GenerationType.UUID) + private UUID id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "medical_record_id", nullable = false) + @JsonBackReference // บอกให้ Jackson ละเว้นการแปลงฟิลด์นี้ + private MedicalRecord medicalRecord; + + @Column(name = "test_name", nullable = false) + private String testName; + + @Column(name = "result_value") + private Double resultValue; + + @Column(name = "unit") + private String unit; + + @Column(name = "reference_range") + private String referenceRange; + + @Enumerated(EnumType.STRING) + @Column(name = "status") + private LabTestStatus status; // Enum: PENDING, COMPLETED, CANCELED + + @Column(name = "result_date") + private LocalDateTime resultDate; + + // Getters and Setters + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + + public MedicalRecord getMedicalRecord() { + return medicalRecord; + } + + public void setMedicalRecord(MedicalRecord medicalRecord) { + this.medicalRecord = medicalRecord; + } + + public String getTestName() { + return testName; + } + + public void setTestName(String testName) { + this.testName = testName; + } + + public Double getResultValue() { + return resultValue; + } + + public void setResultValue(Double resultValue) { + this.resultValue = resultValue; + } + + public String getUnit() { + return unit; + } + + public void setUnit(String unit) { + this.unit = unit; + } + + public String getReferenceRange() { + return referenceRange; + } + + public void setReferenceRange(String referenceRange) { + this.referenceRange = referenceRange; + } + + public LabTestStatus getStatus() { + return status; + } + + public void setStatus(LabTestStatus status) { + this.status = status; + } + + public LocalDateTime getResultDate() { + return resultDate; + } + + public void setResultDate(LocalDateTime resultDate) { + this.resultDate = resultDate; + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/MedicalEquipmentSchedule.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/MedicalEquipmentSchedule.java new file mode 100644 index 0000000..983190b --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/MedicalEquipmentSchedule.java @@ -0,0 +1,62 @@ +package com.mirna.hospitalmanagementapi.domain.entities; + +import jakarta.persistence.*; +import java.time.LocalDateTime; + +import jakarta.persistence.*; +import java.time.LocalDateTime; +import java.util.UUID; +import com.mirna.hospitalmanagementapi.domain.entities.inventory.InventoryItem; + +@Entity +@Table(name = "medical_equipment_schedules") +public class MedicalEquipmentSchedule { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "equipment_id", nullable = false) + private InventoryItem inventoryItem; + + @Column(name = "start_time", nullable = false) + private LocalDateTime startTime; + + @Column(name = "end_time", nullable = false) + private LocalDateTime endTime; + + // Getters and Setters + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + // แก้ไข Getter และ Setter ให้ตรงกับ InventoryItem + public InventoryItem getInventoryItem() { + return inventoryItem; + } + + public void setInventoryItem(InventoryItem inventoryItem) { + this.inventoryItem = inventoryItem; + } + + public LocalDateTime getStartTime() { + return startTime; + } + + public void setStartTime(LocalDateTime startTime) { + this.startTime = startTime; + } + + public LocalDateTime getEndTime() { + return endTime; + } + + public void setEndTime(LocalDateTime endTime) { + this.endTime = endTime; + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/MedicalImage.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/MedicalImage.java new file mode 100644 index 0000000..bdde9f9 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/MedicalImage.java @@ -0,0 +1,81 @@ +package com.mirna.hospitalmanagementapi.domain.entities; + +import jakarta.persistence.*; +import java.time.LocalDateTime; +import java.util.UUID; +import com.fasterxml.jackson.annotation.JsonBackReference; + +@Entity +@Table(name = "medical_images") +public class MedicalImage { + + @Id + @GeneratedValue(strategy = GenerationType.UUID) + private UUID id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "medical_record_id", nullable = false) + @JsonBackReference // บอกให้ Jackson ละเว้นการแปลงฟิลด์นี้ + private MedicalRecord medicalRecord; + + @Column(name = "file_name", nullable = false) + private String fileName; // ชื่อไฟล์ใน MinIO + + @Column(name = "image_type", nullable = false) + private String imageType; // ประเภทของภาพ เช่น X-RAY, CT-SCAN + + @Column(name = "notes", columnDefinition = "TEXT") + private String notes; + + @Column(name = "upload_date", nullable = false) + private LocalDateTime uploadDate; + + // Getters and Setters + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + + public MedicalRecord getMedicalRecord() { + return medicalRecord; + } + + public void setMedicalRecord(MedicalRecord medicalRecord) { + this.medicalRecord = medicalRecord; + } + + public String getFileName() { + return fileName; + } + + public void setFileName(String fileName) { + this.fileName = fileName; + } + + public String getImageType() { + return imageType; + } + + public void setImageType(String imageType) { + this.imageType = imageType; + } + + public String getNotes() { + return notes; + } + + public void setNotes(String notes) { + this.notes = notes; + } + + public LocalDateTime getUploadDate() { + return uploadDate; + } + + public void setUploadDate(LocalDateTime uploadDate) { + this.uploadDate = uploadDate; + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/MedicalRecord.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/MedicalRecord.java new file mode 100644 index 0000000..3e94013 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/MedicalRecord.java @@ -0,0 +1,176 @@ +package com.mirna.hospitalmanagementapi.domain.entities; + +import jakarta.persistence.*; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonManagedReference; + +@Entity +@Table(name = "medical_records") +@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"}) +public class MedicalRecord { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne + @JoinColumn(name = "patient_id") + private Patient patient; + + @ManyToOne + @JoinColumn(name = "doctor_id") + private Doctor doctor; + + @ManyToOne + @JoinColumn(name = "consultation_id") + private Consultation consultation; + + @Column(name = "diagnosis", columnDefinition = "TEXT", nullable = false) + private String diagnosis; + + @Column(name = "treatment", columnDefinition = "TEXT", nullable = false) + private String treatment; + + @Column(name = "notes", columnDefinition = "TEXT") + private String notes; + + @Column(name = "created_at", nullable = false, updatable = false) + private LocalDateTime createdAt; + + @Column(name = "updated_at", nullable = false) + private LocalDateTime updatedAt; + + // เพิ่มความสัมพันธ์กับ MedicalImage + @OneToMany(mappedBy = "medicalRecord", cascade = CascadeType.ALL, orphanRemoval = true) + @JsonManagedReference // บอกให้ Jackson แปลงรายการนี้ + private List medicalImages = new ArrayList<>(); + + // เพิ่มความสัมพันธ์กับ LabResult + @OneToMany(mappedBy = "medicalRecord", cascade = CascadeType.ALL, orphanRemoval = true) + @JsonManagedReference // บอกให้ Jackson แปลงรายการนี้ + private List labResults = new ArrayList<>(); + + public MedicalRecord() { + } + + public MedicalRecord(Patient patient, Doctor doctor, Consultation consultation, String diagnosis, String treatment, String notes) { + this.patient = patient; + this.doctor = doctor; + this.consultation = consultation; + this.diagnosis = diagnosis; + this.treatment = treatment; + this.notes = notes; + this.createdAt = LocalDateTime.now(); + this.updatedAt = LocalDateTime.now(); + } + + // Getters and Setters สำหรับฟิลด์เดิม + public Long getId() { + return id; + } + + public Patient getPatient() { + return patient; + } + + public Doctor getDoctor() { + return doctor; + } + + public Consultation getConsultation() { + return consultation; + } + + public String getDiagnosis() { + return diagnosis; + } + + public String getTreatment() { + return treatment; + } + + public String getNotes() { + return notes; + } + + public LocalDateTime getCreatedAt() { + return createdAt; + } + + public LocalDateTime getUpdatedAt() { + return updatedAt; + } + + // Setters สำหรับฟิลด์เดิม + public void setId(Long id) { + this.id = id; + } + + public void setPatient(Patient patient) { + this.patient = patient; + } + + public void setDoctor(Doctor doctor) { + this.doctor = doctor; + } + + public void setConsultation(Consultation consultation) { + this.consultation = consultation; + } + + public void setDiagnosis(String diagnosis) { + this.diagnosis = diagnosis; + } + + public void setTreatment(String treatment) { + this.treatment = treatment; + } + + public void setNotes(String notes) { + this.notes = notes; + } + + public void setCreatedAt(LocalDateTime createdAt) { + this.createdAt = createdAt; + } + + public void setUpdatedAt(LocalDateTime updatedAt) { + this.updatedAt = updatedAt; + } + + // Getters and Setters สำหรับรายการใหม่ + public List getMedicalImages() { + return medicalImages; + } + + public void setMedicalImages(List medicalImages) { + this.medicalImages = medicalImages; + } + + public List getLabResults() { + return labResults; + } + + public void setLabResults(List labResults) { + this.labResults = labResults; + } + + // equals() and hashCode() for object comparison + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + MedicalRecord that = (MedicalRecord) o; + return Objects.equals(id, that.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/Nurse.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/Nurse.java new file mode 100644 index 0000000..749dec7 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/Nurse.java @@ -0,0 +1,92 @@ +package com.mirna.hospitalmanagementapi.domain.entities; + +import jakarta.persistence.*; +import com.mirna.hospitalmanagementapi.domain.enums.Role; +import com.mirna.hospitalmanagementapi.domain.enums.ShiftType; +import com.mirna.hospitalmanagementapi.domain.entities.auth.User; + +@Entity +@Table(name = "nurses") +public class Nurse { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "name", nullable = false) + private String name; + + @Column(name = "license_number", nullable = false, unique = true) + private String licenseNumber; + + @Column(name = "email", nullable = false, unique = true) + private String email; + + @Column(name = "telephone", nullable = false) + private String telephone; + + @Enumerated(EnumType.STRING) + @Column(name = "shift_type", nullable = false) + private ShiftType shiftType; + + @OneToOne(fetch = FetchType.LAZY, optional = true) + @JoinColumn(name = "user_id", referencedColumnName = "id") + private User user; + + // Getters and Setters + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getLicenseNumber() { + return licenseNumber; + } + + public void setLicenseNumber(String licenseNumber) { + this.licenseNumber = licenseNumber; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getTelephone() { + return telephone; + } + + public void setTelephone(String telephone) { + this.telephone = telephone; + } + + public ShiftType getShiftType() { + return shiftType; + } + + public void setShiftType(ShiftType shiftType) { + this.shiftType = shiftType; + } + + public User getUser() { + return user; + } + + public void setUser(User user) { + this.user = user; + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/NurseSchedule.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/NurseSchedule.java new file mode 100644 index 0000000..c31351e --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/NurseSchedule.java @@ -0,0 +1,58 @@ +package com.mirna.hospitalmanagementapi.domain.entities; + +import jakarta.persistence.*; +import java.time.LocalDateTime; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +@Entity +@Table(name = "nurse_schedules") +public class NurseSchedule { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "nurse_id", nullable = false) + @JsonIgnoreProperties({"hibernateLazyInitializer", "handler"}) + private Nurse nurse; + + @Column(name = "start_time", nullable = false) + private LocalDateTime startTime; + + @Column(name = "end_time", nullable = false) + private LocalDateTime endTime; + + // Getters and Setters + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Nurse getNurse() { + return nurse; + } + + public void setNurse(Nurse nurse) { + this.nurse = nurse; + } + + public LocalDateTime getStartTime() { + return startTime; + } + + public void setStartTime(LocalDateTime startTime) { + this.startTime = startTime; + } + + public LocalDateTime getEndTime() { + return endTime; + } + + public void setEndTime(LocalDateTime endTime) { + this.endTime = endTime; + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/OperatingRoom.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/OperatingRoom.java new file mode 100644 index 0000000..eb5d829 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/OperatingRoom.java @@ -0,0 +1,45 @@ +package com.mirna.hospitalmanagementapi.domain.entities; + +import jakarta.persistence.*; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +@Entity +@Table(name = "operating_rooms") +@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"}) +public class OperatingRoom { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "room_number", nullable = false, unique = true) + private String roomNumber; + + @Column(name = "is_available", nullable = false) + private Boolean isAvailable; + + // Getters and Setters + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getRoomNumber() { + return roomNumber; + } + + public void setRoomNumber(String roomNumber) { + this.roomNumber = roomNumber; + } + + public Boolean getAvailable() { + return isAvailable; + } + + public void setAvailable(Boolean available) { + isAvailable = available; + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/OperatingRoomSchedule.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/OperatingRoomSchedule.java new file mode 100644 index 0000000..000de07 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/OperatingRoomSchedule.java @@ -0,0 +1,59 @@ +package com.mirna.hospitalmanagementapi.domain.entities; + +import jakarta.persistence.*; +import java.time.LocalDateTime; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +@Entity +@Table(name = "operating_room_schedules") +@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"}) +public class OperatingRoomSchedule { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "room_id", nullable = false) + @JsonIgnoreProperties({"hibernateLazyInitializer", "handler"}) + private OperatingRoom operatingRoom; + + @Column(name = "start_time", nullable = false) + private LocalDateTime startTime; + + @Column(name = "end_time", nullable = false) + private LocalDateTime endTime; + + // Getters and Setters + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public OperatingRoom getOperatingRoom() { + return operatingRoom; + } + + public void setOperatingRoom(OperatingRoom operatingRoom) { + this.operatingRoom = operatingRoom; + } + + public LocalDateTime getStartTime() { + return startTime; + } + + public void setStartTime(LocalDateTime startTime) { + this.startTime = startTime; + } + + public LocalDateTime getEndTime() { + return endTime; + } + + public void setEndTime(LocalDateTime endTime) { + this.endTime = endTime; + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/Patient.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/Patient.java index b92ff05..c80b9f2 100644 --- a/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/Patient.java +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/Patient.java @@ -1,6 +1,7 @@ package com.mirna.hospitalmanagementapi.domain.entities; import com.mirna.hospitalmanagementapi.domain.dtos.patient.PatientDTO; +import com.mirna.hospitalmanagementapi.domain.entities.auth.User; import jakarta.persistence.Column; import jakarta.persistence.Embedded; @@ -8,174 +9,140 @@ import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.OneToOne; import jakarta.persistence.Table; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Pattern; -/** -* -* @author Mirna Gama -* @version 1.0 -*/ +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonBackReference; + @Table(name="patients") @Entity(name="Patient") +@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"}) public class Patient { - /** - * Constructor for class Patient - * @param patientDTO Data transfer object containing Patient entity information - * @see PatientDTO - */ - public Patient(PatientDTO patientDTO) { - this.active=true; - this.name=patientDTO.name(); - this.email=patientDTO.email(); - this.cpf=patientDTO.cpf(); - this.telephone=patientDTO.telephone(); - this.address = new Address(patientDTO.address()); - } - - public Patient(){} + // Constructor เดิม + public Patient(PatientDTO patientDTO) { + this.active=true; + this.name=patientDTO.name(); + this.email=patientDTO.email(); + this.cpf=patientDTO.cpf(); + this.telephone=patientDTO.telephone(); + this.address = new Address(patientDTO.address()); + } - @Id @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @NotBlank(message="name cannot be blank") - @Column(name="name") - private String name; - - @NotBlank(message="email cannot be blank") - @Column(name="email") - private String email; - - @NotBlank(message="cpf cannot be blank") - @Pattern(regexp = "\\d{11}", message = "invalid format for cpf") - @Column(name="cpf") - private String cpf; - - @NotBlank(message="telephone cannot be blank") - @Column(name="telephone") - private String telephone; + // Constructor ที่เพิ่มเข้ามาใหม่ + public Patient(PatientDTO patientDTO, User user) { + this.active=true; + this.name=patientDTO.name(); + this.email=patientDTO.email(); + this.cpf=patientDTO.cpf(); + this.telephone=patientDTO.telephone(); + this.address = new Address(patientDTO.address()); + this.user = user; // กำหนด User ให้กับ Patient + } - @NotNull(message="active cannot be null") - @Column(name="_active") - private Boolean active; - - @NotNull(message="address cannot be null") - @Embedded - private Address address; - - /** - * Returns the doctor id. - * @return A Long representing the patient id. - */ - public Long getId() { - return id; - } + public Patient(){} - /** - * Sets the doctor id. - * @param id The patient's unique identifier. - */ - public void setId(Long id) { - this.id = id; - } + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; - /** - * Returns the name - * @return A string representing the patient's name. - */ - public String getName() { - return name; - } + // เพิ่มความสัมพันธ์ OneToOne กับ User + @OneToOne + @JoinColumn(name = "user_id", referencedColumnName = "id", unique = true) + @JsonBackReference // บอกให้ Jackson ละเว้นการแปลงฟิลด์นี้ + private User user; - /** - * Sets the name - * @param name Must not be blank. - */ - public void setName(String name) { - this.name = name; - } + @NotBlank(message="name cannot be blank") + @Column(name="name") + private String name; - /** - * Returns the email - * @return A string representing the patient's email. - */ - public String getEmail() { - return email; - } + @NotBlank(message="email cannot be blank") + @Column(name="email") + private String email; - /** - * Sets the email - * @param email Must not be blank. - */ - public void setEmail(String email) { - this.email = email; - } + @NotBlank(message="cpf cannot be blank") + @Pattern(regexp = "\\d{11}", message = "invalid format for cpf") + @Column(name="cpf") + private String cpf; - /** - * Returns the cpf - * @return A string representing the patient's cpf. - */ - public String getCpf() { - return cpf; - } + @NotBlank(message="telephone cannot be blank") + @Column(name="telephone") + private String telephone; - /** - * Sets the cpf - * @param cpf Must not be blank. - */ - public void setCpf(String cpf) { - this.cpf = cpf; - } + @NotNull(message="active cannot be null") + @Column(name="_active") + private Boolean active; - /** - * Returns the telephone - * @return A string representing the patient's telephone. - */ - public String getTelephone() { - return telephone; - } + @NotNull(message="address cannot be null") + @Embedded + private Address address; - /** - * Sets the telephone - * @param telephone Must not be blank. - */ - public void setTelephone(String telephone) { - this.telephone = telephone; - } + public Long getId() { + return id; + } - /** - * Returns the active - * @return A boolean value that states whether the patient is active in the system - */ - public Boolean getActive() { - return active; - } + public void setId(Long id) { + this.id = id; + } - /** - * Sets the active - * @param active Must not be null. Starts with the true value by default - */ - public void setActive(Boolean active) { - this.active = active; - } + public String getName() { + return name; + } - /** - * Returns the address - * @return An object class representing the patient's address. - * @see Address - */ - public Address getAddress() { - return address; - } + public void setName(String name) { + this.name = name; + } - /** - * Sets the address - * @param address Must not be null. - */ - public void setAddress(Address address) { - this.address = address; - } -} + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getCpf() { + return cpf; + } + + public void setCpf(String cpf) { + this.cpf = cpf; + } + + public String getTelephone() { + return telephone; + } + + public void setTelephone(String telephone) { + this.telephone = telephone; + } + + public Boolean getActive() { + return active; + } + + public void setActive(Boolean active) { + this.active = active; + } + + public Address getAddress() { + return address; + } + + public void setAddress(Address address) { + this.address = address; + } + + // เพิ่ม getter/setter สำหรับ user + public User getUser() { + return user; + } + + public void setUser(User user) { + this.user = user; + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/Payment.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/Payment.java new file mode 100644 index 0000000..b187259 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/Payment.java @@ -0,0 +1,96 @@ +package com.mirna.hospitalmanagementapi.domain.entities; + +import java.math.BigDecimal; +import java.time.OffsetDateTime; + +import jakarta.persistence.*; +import jakarta.validation.constraints.DecimalMin; +import jakarta.validation.constraints.NotNull; + +import com.mirna.hospitalmanagementapi.domain.enums.PaymentMethod; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +@Table(name = "payments") +@Entity(name = "Payment") +@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"}) +public class Payment { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "consultation_id") + private Consultation consultation; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "medical_record_id") + private MedicalRecord medicalRecord; + + @NotNull(message = "Amount cannot be null") + @DecimalMin(value = "0.01", message = "Amount must be greater than zero") + @Column(name = "amount") + private BigDecimal amount; + + @NotNull(message = "Payment method cannot be null") + @Enumerated(EnumType.STRING) + @Column(name = "payment_method") + private PaymentMethod paymentMethod; + + @NotNull(message = "Payment date cannot be null") + @Column(name = "payment_date") + private OffsetDateTime paymentDate; + + // Constructors + public Payment() {} + + // Getters and Setters + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Consultation getConsultation() { + return consultation; + } + + public void setConsultation(Consultation consultation) { + this.consultation = consultation; + } + + public MedicalRecord getMedicalRecord() { + return medicalRecord; + } + + public void setMedicalRecord(MedicalRecord medicalRecord) { + this.medicalRecord = medicalRecord; + } + + public BigDecimal getAmount() { + return amount; + } + + public void setAmount(BigDecimal amount) { + this.amount = amount; + } + + public PaymentMethod getPaymentMethod() { + return paymentMethod; + } + + public void setPaymentMethod(PaymentMethod paymentMethod) { + this.paymentMethod = paymentMethod; + } + + public OffsetDateTime getPaymentDate() { + return paymentDate; + } + + public void setPaymentDate(OffsetDateTime paymentDate) { + this.paymentDate = paymentDate; + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/Prescription.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/Prescription.java new file mode 100644 index 0000000..a322b8a --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/Prescription.java @@ -0,0 +1,77 @@ +package com.mirna.hospitalmanagementapi.domain.entities; + +import jakarta.persistence.*; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import com.mirna.hospitalmanagementapi.domain.entities.inventory.InventoryItem; +import org.hibernate.annotations.CreationTimestamp; +import org.hibernate.annotations.UpdateTimestamp; +import org.hibernate.annotations.GenericGenerator; + +import java.time.OffsetDateTime; +import java.util.UUID; + +@Table(name = "prescriptions") +@Entity(name = "Prescription") +public class Prescription { + + @Id + @GeneratedValue(generator = "UUID") + @GenericGenerator( + name = "UUID", + strategy = "org.hibernate.id.UUIDGenerator" + ) + @Column(name = "id", updatable = false, nullable = false) + private UUID id; + + @ManyToOne + @JoinColumn(name = "medical_record_id") + @NotNull(message = "medical record cannot be null") + private MedicalRecord medicalRecord; + + @ManyToOne + @JoinColumn(name = "inventory_item_id") + @NotNull(message = "inventory item cannot be null") + private InventoryItem inventoryItem; + + @NotNull(message = "quantity cannot be null") + @Min(value = 1, message = "quantity must be at least 1") + @Column(name = "quantity") + private Integer quantity; + + @NotBlank(message = "instructions cannot be blank") + @Column(name = "instructions") + private String instructions; + + @CreationTimestamp + @Column(name = "created_at", nullable = false, updatable = false) + private OffsetDateTime createdAt; + + @UpdateTimestamp + @Column(name = "updated_at", nullable = false) + private OffsetDateTime updatedAt; + + // Constructors, Getters and Setters + public Prescription() {} + + public Prescription(MedicalRecord medicalRecord, InventoryItem inventoryItem, Integer quantity, String instructions) { + this.medicalRecord = medicalRecord; + this.inventoryItem = inventoryItem; + this.quantity = quantity; + this.instructions = instructions; + } + + public UUID getId() { return id; } + public void setId(UUID id) { this.id = id; } + public MedicalRecord getMedicalRecord() { return medicalRecord; } + public void setMedicalRecord(MedicalRecord medicalRecord) { this.medicalRecord = medicalRecord; } + public InventoryItem getInventoryItem() { return inventoryItem; } + public void setInventoryItem(InventoryItem inventoryItem) { this.inventoryItem = inventoryItem; } + public Integer getQuantity() { return quantity; } + public void setQuantity(Integer quantity) { this.quantity = quantity; } + public String getInstructions() { return instructions; } + public void setInstructions(String instructions) { this.instructions = instructions; } + public OffsetDateTime getCreatedAt() { return createdAt; } + public OffsetDateTime getUpdatedAt() { return updatedAt; } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/Staff.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/Staff.java new file mode 100644 index 0000000..76c6a97 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/Staff.java @@ -0,0 +1,100 @@ +package com.mirna.hospitalmanagementapi.domain.entities; + +import com.mirna.hospitalmanagementapi.domain.entities.auth.User; + +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.OneToOne; +import jakarta.persistence.Table; +import jakarta.validation.Valid; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +@Table(name = "staff") +@Entity(name = "Staff") +@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"}) +public class Staff { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @NotBlank(message = "Name cannot be blank") + @Column(name = "name") + private String name; + + @NotBlank(message = "Email cannot be blank") + @Email(message = "Email format is invalid") + @Column(name = "email", unique=true) + private String email; + + @NotBlank(message = "Telephone cannot be blank") + @Pattern(regexp = "^[0-9]{9,15}$", message = "Telephone format is invalid") + @Column(name = "telephone", unique=true) + private String telephone; + + @OneToOne(cascade = CascadeType.ALL) + @JoinColumn(name = "user_id", referencedColumnName = "id") + @Valid + private User user; + + public Staff(String name, String email, String telephone, User user) { + this.name = name; + this.email = email; + this.telephone = telephone; + this.user = user; + } + + public Staff() { + } + + // Getters and Setters + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getTelephone() { + return telephone; + } + + public void setTelephone(String telephone) { + this.telephone = telephone; + } + + public User getUser() { + return user; + } + + public void setUser(User user) { + this.user = user; + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/auth/User.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/auth/User.java index 935bedf..d34c3de 100644 --- a/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/auth/User.java +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/auth/User.java @@ -8,156 +8,143 @@ import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import com.mirna.hospitalmanagementapi.domain.dtos.auth.UserDTO; +import com.mirna.hospitalmanagementapi.domain.enums.Role; import jakarta.persistence.Column; import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import jakarta.persistence.Table; import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +import com.fasterxml.jackson.annotation.JsonManagedReference; +import jakarta.persistence.OneToOne; +import com.mirna.hospitalmanagementapi.domain.entities.Doctor; +import com.mirna.hospitalmanagementapi.domain.entities.Patient; -/** - * @see UserDetails - * @author Mirna Gama - * @version 1.0 - */ @Table(name = "users") @Entity(name = "User") +@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"}) public class User implements UserDetails { - /** - * Constructor for class User - * @param userDTO Data transfer object containing User entity information - * @see UserDTO - */ - public User(UserDTO userDTO) { - this.login = userDTO.login(); - this.password = userDTO.password(); - } - - public User() {} - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; - @NotBlank(message = "login cannot be blank") - @Column(name = "login", unique=true) - private String login; + @NotBlank(message = "login cannot be blank") + @Column(name = "login", unique=true) + private String login; - @NotBlank(message = "password cannot be blank") - @Column(name = "password") - private String password; - - /** - * Returns the user id. - * - * @return A Long representing the user id. - */ - public Long getId() { - return id; - } + @NotBlank(message = "password cannot be blank") + @Column(name = "password") + private String password; - /** - * Sets the user id. - * - * @param id The user's unique identifier. Must be unique. - */ - public void setId(Long id) { - this.id = id; - } + @NotNull(message = "role cannot be null") + @Enumerated(EnumType.STRING) + @Column(name = "role") + private Role role; - /** - * Returns the login - * - * @return A string representing the user's system login. - */ - public String getLogin() { - return login; - } + @Column(name = "enabled") + private boolean enabled = true; // เพิ่มฟิลด์ enabled และกำหนดค่าเริ่มต้นเป็น true - /** - * Sets the login - * - * @param login Must not be blank. - */ - public void setLogin(String login) { - this.login = login; - } + @OneToOne(mappedBy = "user") + @JsonManagedReference + private Doctor doctor; - /** - * Returns the password - * - * @return A string representing the user's system password. - */ - @Override - public String getPassword() { - return password; - } + @OneToOne(mappedBy = "user") + @JsonManagedReference + private Patient patient; - /** - * Sets the password - * - * @param password Must not be blank. - */ - public void setPassword(String password) { - this.password = password; - } - @Override - public Collection getAuthorities() { - return List.of(new SimpleGrantedAuthority("ROLE_USER")); - } + // Constructors + public User(UserDTO userDTO) { + this.login = userDTO.login(); + this.password = userDTO.password(); + this.role = Role.ROLE_PATIENT; + } - /** - * Returns the username. - * - * @return A string containing the user's system login - */ - @Override - public String getUsername() { - return login; - } + // Constructor สำหรับสร้างบัญชีผู้ใช้ + public User(String login, String password, Role role) { + this.login = login; + this.password = password; + this.role = role; + this.enabled = true; // ตั้งค่า enabled เป็น true เมื่อสร้างบัญชีใหม่ + } - /** - * Checks if the account is non expired - * - * @return true - */ - @Override - public boolean isAccountNonExpired() { - return true; - } + public User() {} - /** - * Checks if the account is non locked - * - * @return true - */ - @Override - public boolean isAccountNonLocked() { - return true; - } + // Getters and Setters + public Long getId() { + return id; + } - /** - * Checks if the account credentials are non expired - * - * @return true - */ - @Override - public boolean isCredentialsNonExpired() { - return true; - } + public void setId(Long id) { + this.id = id; + } - /** - * Checks if the entity is enabled - * - * @return true - */ - @Override - public boolean isEnabled() { - return true; - } + public String getLogin() { + return login; + } -} + public void setLogin(String login) { + this.login = login; + } + + @Override + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public Role getRole() { + return this.role; + } + + public void setRole(Role role) { + this.role = role; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + // UserDetails methods + @Override + public Collection getAuthorities() { + return List.of(new SimpleGrantedAuthority(this.role.name())); + } + + @Override + public String getUsername() { + return login; + } + + @Override + public boolean isAccountNonExpired() { + return true; + } + + @Override + public boolean isAccountNonLocked() { + return true; + } + + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + @Override + public boolean isEnabled() { + return this.enabled; // แก้ไขให้คืนค่าจากฟิลด์ enabled + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/inventory/InventoryItem.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/inventory/InventoryItem.java new file mode 100644 index 0000000..35c815f --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/inventory/InventoryItem.java @@ -0,0 +1,71 @@ +package com.mirna.hospitalmanagementapi.domain.entities.inventory; + +import com.mirna.hospitalmanagementapi.domain.enums.TransactionType; +import jakarta.persistence.*; +import lombok.*; +import org.hibernate.annotations.CreationTimestamp; +import org.hibernate.annotations.UpdateTimestamp; +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.ZonedDateTime; +import java.util.UUID; + +// InventoryItem.java +@Entity +@Table(name = "inventory_items") +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class InventoryItem { + @Id + @GeneratedValue(strategy = GenerationType.UUID) + private UUID id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "item_type_id") + private InventoryItemType itemType; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "supplier_id") + private InventorySupplier supplier; + + @Column(name = "item_name", nullable = false, length = 255) + private String itemName; + + @Column(name = "description", columnDefinition = "TEXT") + private String description; + + @Column(name = "current_stock") + private Integer currentStock; + + @Column(name = "unit_of_measure", nullable = false, length = 20) + private String unitOfMeasure; + + @Column(name = "unit_price", precision = 10, scale = 2) + private BigDecimal unitPrice; + + @Column(name = "reorder_level") + private Integer reorderLevel; + + @Column(name = "expiration_date") + private LocalDate expirationDate; + + @Column(name = "location", length = 100) + private String location; + + @Column(name = "is_active", nullable = false) + private Boolean isActive; + + @Column(name = "serial_number", unique = true) + private String serialNumber; + + @CreationTimestamp + @Column(name = "created_at", nullable = false) + private ZonedDateTime createdAt; + + @UpdateTimestamp + @Column(name = "updated_at", nullable = false) + private ZonedDateTime updatedAt; +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/inventory/InventoryItemType.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/inventory/InventoryItemType.java new file mode 100644 index 0000000..f26d706 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/inventory/InventoryItemType.java @@ -0,0 +1,41 @@ +package com.mirna.hospitalmanagementapi.domain.entities.inventory; + +import jakarta.persistence.*; +import lombok.*; +import java.time.ZonedDateTime; +import java.util.UUID; +import org.hibernate.annotations.CreationTimestamp; +import org.hibernate.annotations.UpdateTimestamp; +import com.mirna.hospitalmanagementapi.domain.dtos.inventory.ItemTypeDTO; + +@Entity(name = "InventoryItemTypes") +@Table(name = "inventory_item_types") +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class InventoryItemType { + @Id + @GeneratedValue(strategy = GenerationType.UUID) + private UUID id; + + @Column(name = "type_name", unique = true, nullable = false, length = 50) + private String typeName; + + @Column(name = "description", columnDefinition = "TEXT") + private String description; + + @CreationTimestamp + @Column(name = "created_at", nullable = false) + private ZonedDateTime createdAt; + + @UpdateTimestamp + @Column(name = "updated_at", nullable = false) + private ZonedDateTime updatedAt; + + public InventoryItemType(ItemTypeDTO itemTypeDTO) { + this.typeName = itemTypeDTO.getName(); // แก้ไข: เรียก Getter จาก DTO + this.description = itemTypeDTO.getDescription(); // เพิ่ม: กำหนดค่าสำหรับ description + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/inventory/InventorySupplier.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/inventory/InventorySupplier.java new file mode 100644 index 0000000..3f24daf --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/inventory/InventorySupplier.java @@ -0,0 +1,49 @@ +package com.mirna.hospitalmanagementapi.domain.entities.inventory; + +import jakarta.persistence.*; +import lombok.*; +import java.time.ZonedDateTime; +import java.util.UUID; +import org.hibernate.annotations.CreationTimestamp; +import org.hibernate.annotations.UpdateTimestamp; +import com.mirna.hospitalmanagementapi.domain.dtos.inventory.SupplierDTO; + +@Entity(name = "InventorySuppliers") +@Table(name = "inventory_suppliers") +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class InventorySupplier { + @Id + @GeneratedValue(strategy = GenerationType.UUID) + private UUID id; + + @Column(name = "supplier_name", nullable = false, length = 100) + private String supplierName; + + @Column(name = "contact_person", nullable = true, length = 100) + private String contactPerson; + + @Column(name = "contact_email", nullable = true, length = 100) + private String contactEmail; + + @Column(name = "phone_number", nullable = true, length = 20) + private String phoneNumber; + + @CreationTimestamp + @Column(name = "created_at", nullable = false) + private ZonedDateTime createdAt; + + @UpdateTimestamp + @Column(name = "updated_at", nullable = false) + private ZonedDateTime updatedAt; + + public InventorySupplier(SupplierDTO supplierDTO) { + this.supplierName = supplierDTO.getSupplierName(); // แก้ไข: เรียก Getter จาก DTO + this.contactPerson = supplierDTO.getContactPerson(); // แก้ไข: เรียก Getter จาก DTO + this.contactEmail = supplierDTO.getContactEmail(); // แก้ไข: เรียก Getter จาก DTO + this.phoneNumber = supplierDTO.getPhoneNumber(); // แก้ไข: เรียก Getter จาก DTO + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/inventory/InventoryTransaction.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/inventory/InventoryTransaction.java new file mode 100644 index 0000000..6d328ff --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/entities/inventory/InventoryTransaction.java @@ -0,0 +1,51 @@ +package com.mirna.hospitalmanagementapi.domain.entities.inventory; + +import jakarta.persistence.*; +import lombok.*; +import java.time.ZonedDateTime; +import java.util.UUID; +import com.mirna.hospitalmanagementapi.domain.enums.TransactionType; +import org.hibernate.annotations.CreationTimestamp; +import org.hibernate.annotations.UpdateTimestamp; + +// InventoryTransaction.java +@Entity +@Table(name = "inventory_transactions") +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class InventoryTransaction { + @Id + @GeneratedValue(strategy = GenerationType.UUID) + private UUID id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "item_id") + private InventoryItem item; + + @Enumerated(EnumType.STRING) + @Column(name = "transaction_type", nullable = false, length = 20) + private TransactionType transactionType; + + @Column(name = "quantity", nullable = false) + private Integer quantity; + + @Column(name = "transaction_date", nullable = false) + private ZonedDateTime transactionDate; + + @Column(name = "related_document_id") + private UUID relatedDocumentId; + + @Column(name = "notes", columnDefinition = "TEXT") + private String notes; + + @CreationTimestamp + @Column(name = "created_at", nullable = false) + private ZonedDateTime createdAt; + + @UpdateTimestamp + @Column(name = "updated_at", nullable = false) + private ZonedDateTime updatedAt; +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/enums/ClaimStatus.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/enums/ClaimStatus.java new file mode 100644 index 0000000..7a81f37 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/enums/ClaimStatus.java @@ -0,0 +1,12 @@ +package com.mirna.hospitalmanagementapi.domain.enums; + +/** + * Enumeration representing the possible statuses of an insurance claim. + */ +public enum ClaimStatus { + SUBMITTED, + IN_REVIEW, + APPROVED, + DENIED, + PAID +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/enums/LabTestStatus.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/enums/LabTestStatus.java new file mode 100644 index 0000000..c7e050d --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/enums/LabTestStatus.java @@ -0,0 +1,25 @@ +package com.mirna.hospitalmanagementapi.domain.enums; + +/** + * Enum สำหรับกำหนดสถานะของผลการตรวจแล็บ + * + * @author FlookSP + * @version 1.0 + */ +public enum LabTestStatus { + + /** + * สถานะที่ผลการตรวจยังอยู่ระหว่างดำเนินการ + */ + PENDING, + + /** + * สถานะที่ผลการตรวจเสร็จสมบูรณ์แล้ว + */ + COMPLETED, + + /** + * สถานะที่การตรวจถูกยกเลิก + */ + CANCELED +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/enums/PaymentMethod.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/enums/PaymentMethod.java new file mode 100644 index 0000000..0c2452d --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/enums/PaymentMethod.java @@ -0,0 +1,13 @@ +package com.mirna.hospitalmanagementapi.domain.enums; + +/** + * Enum for payment methods. + * + * @author FlookSP + * @version 1.0 + */ +public enum PaymentMethod { + CREDIT_CARD, + CASH, + TRANSFER +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/enums/PaymentStatus.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/enums/PaymentStatus.java new file mode 100644 index 0000000..3dccb11 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/enums/PaymentStatus.java @@ -0,0 +1,11 @@ +package com.mirna.hospitalmanagementapi.domain.enums; + +/** + * Enumeration representing the possible statuses of a billing payment. + */ +public enum PaymentStatus { + PENDING, + PAID, + CANCELED, + PARTIALLY_PAID +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/enums/Role.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/enums/Role.java new file mode 100644 index 0000000..7a26c7e --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/enums/Role.java @@ -0,0 +1,40 @@ +package com.mirna.hospitalmanagementapi.domain.enums; + +/** + * An enumeration representing the different roles of a user in the system. + * + * @author FlookSP + * @version 1.1 + */ +public enum Role { + + /** + * Represents a doctor in the system. + */ + ROLE_DOCTOR, + + /** + * Represents a nurse in the system. + */ + ROLE_NURSE, + + /** + * Represents a pharmacist in the system. + */ + ROLE_PHARMACIST, + + /** + * Represents an administrator in the system. + */ + ROLE_ADMIN, + + /** + * Represents a regular patient in the system. + */ + ROLE_PATIENT, + + /** + * Represents a receptionist or an administrative staff member. + */ + ROLE_RECEPTIONIST; +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/enums/ShiftType.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/enums/ShiftType.java new file mode 100644 index 0000000..ca6181f --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/enums/ShiftType.java @@ -0,0 +1,7 @@ +package com.mirna.hospitalmanagementapi.domain.enums; + +public enum ShiftType { + DAY_SHIFT, // กะกลางวัน + NIGHT_SHIFT, // กะกลางคืน + WEEKEND_SHIFT // กะวันหยุด +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/enums/Specialty.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/enums/Specialty.java index e09b93e..d89d1bb 100644 --- a/src/main/java/com/mirna/hospitalmanagementapi/domain/enums/Specialty.java +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/enums/Specialty.java @@ -1,31 +1,32 @@ package com.mirna.hospitalmanagementapi.domain.enums; -/** -* Specialties that can be used -* @see #ORTHOPEDICS -* @see #CARDIOLOGY -* @see #GYNECOLOGY -* @see #DERMATOLOGY -*/ public enum Specialty { + // Basic Medical and Surgical Specialties + INTERNAL_MEDICINE, + GENERAL_SURGERY, + PEDIATRICS, + GYNECOLOGY, - /** - * Specialty in orthopedics - */ - ORTHOPEDICS, - - /** - * Specialty in cardiology - */ - CARDIOLOGY, - - /** - * Specialty in gynecology - */ - GYNECOLOGY, - - /** - * Specialty in dermatology - */ - DERMATOLOGY -} + // Core Specialties + CARDIOLOGY, + DERMATOLOGY, + NEUROLOGY, + ONCOLOGY, + ORTHOPEDICS, + UROLOGY, + OPHTHALMOLOGY, + OTOLARYNGOLOGY, // ENT (Ear, Nose, Throat) + + // Diagnostic and Ancillary Services + RADIOLOGY, + ANESTHESIOLOGY, + PATHOLOGY, + + // Mental Health and Rehabilitation + PSYCHIATRY, + PHYSICAL_MEDICINE_AND_REHABILITATION, + + // Emergency and Critical Care + EMERGENCY_MEDICINE, + CRITICAL_CARE_MEDICINE +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/enums/TransactionType.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/enums/TransactionType.java new file mode 100644 index 0000000..7038d4e --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/enums/TransactionType.java @@ -0,0 +1,7 @@ +package com.mirna.hospitalmanagementapi.domain.enums; + +public enum TransactionType { + IN, + OUT, + ADJUSTMENT +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/BillingRepository.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/BillingRepository.java new file mode 100644 index 0000000..1db020e --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/BillingRepository.java @@ -0,0 +1,9 @@ +package com.mirna.hospitalmanagementapi.domain.repositories; + +import com.mirna.hospitalmanagementapi.domain.entities.Billing; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface BillingRepository extends JpaRepository { +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/ConsultationRepository.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/ConsultationRepository.java index 5a6c29b..bdc603d 100644 --- a/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/ConsultationRepository.java +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/ConsultationRepository.java @@ -1,47 +1,53 @@ package com.mirna.hospitalmanagementapi.domain.repositories; -import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import com.mirna.hospitalmanagementapi.domain.entities.Consultation; - -/** - * Repository interface for retrieving and manipulating all Consultation objects using their unique Long identifier. -* - * @author Mirna Gama -* @version 1.0 -*/ +import com.mirna.hospitalmanagementapi.domain.entities.Doctor; +import com.mirna.hospitalmanagementapi.domain.enums.Specialty; +import java.time.LocalDateTime; public interface ConsultationRepository extends JpaRepository { - /** - * - * @param patientId The patient's id from the consultation - * @param consultationDate The date of the consultation - * @return The corresponding consultation if successful, or null if it is non-existent - */ - @Query(""" - select c from Consultation c - where c.patient.id = :patientId - and c.consultationDate = :consultationDate - and c.canceled = false - """) - Consultation findConsultationByPatientAndDate(Long patientId, LocalDateTime consultationDate); + // เมธอดสำหรับตรวจสอบว่าคนไข้มีนัดหมายอื่นในวันนั้นหรือไม่ (ไม่รวมนัดที่ถูกยกเลิก) + @Query(""" + select c from Consultation c + where c.patient.id = :patientId + and c.consultationDate >= :start_of_day + and c.consultationDate <= :end_of_day + and c.canceled = false + """) + Consultation findConsultationByPatientAndDate( + @Param("patientId") Long patientId, + @Param("start_of_day") OffsetDateTime startOfDay, + @Param("end_of_day") OffsetDateTime endOfDay + ); - /** - * - * @param doctorId The doctor's id from the consultation - * @param consultationDate The date of the consultation - * @return The corresponding consultation if successful, or null if it is non-existent - */ - @Query(""" - select c from Consultation c - where c.doctor.id = :doctorId - and c.consultationDate = :consultationDate - and c.canceled = false - """) - Consultation findConsultationByDoctorAndDate(Long doctorId, LocalDateTime consultationDate); + // เมธอดสำหรับตรวจสอบว่าหมอมีนัดหมายอื่นในวันนั้นหรือไม่ (ไม่รวมนัดที่ถูกยกเลิก) + @Query(""" + select c from Consultation c + where c.doctor.id = :doctorId + and c.consultationDate >= :start_of_day + and c.consultationDate <= :end_of_day + and c.canceled = false + """) + Consultation findConsultationByDoctorAndDate( + @Param("doctorId") Long doctorId, + @Param("start_of_day") OffsetDateTime startOfDay, + @Param("end_of_day") OffsetDateTime endOfDay + ); -} + @Query("SELECT COUNT(c) FROM Consultation c WHERE c.consultationDate BETWEEN :startDate AND :endDate") + Long countAllByDateRange(@Param("startDate") OffsetDateTime startDate, @Param("endDate") OffsetDateTime endDate); + + @Query("SELECT COUNT(c) FROM Consultation c WHERE c.canceled = true AND c.consultationDate BETWEEN :startDate AND :endDate") + Long countCanceledByDateRange(@Param("startDate") OffsetDateTime startDate, @Param("endDate") OffsetDateTime endDate); + + @Query("SELECT COUNT(DISTINCT c.patient.id) FROM Consultation c WHERE c.consultationDate BETWEEN :startDate AND :endDate") + Long countTotalPatientsByDateRange(@Param("startDate") OffsetDateTime startDate, @Param("endDate") OffsetDateTime endDate); +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/DoctorRepository.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/DoctorRepository.java index e805609..f6fd2ad 100644 --- a/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/DoctorRepository.java +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/DoctorRepository.java @@ -1,47 +1,55 @@ +// ไฟล์: DoctorRepository.java package com.mirna.hospitalmanagementapi.domain.repositories; -import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.util.List; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import com.mirna.hospitalmanagementapi.domain.entities.Doctor; import com.mirna.hospitalmanagementapi.domain.enums.Specialty; -/** - * Repository interface for retrieving and manipulating all Doctor objects using their unique Long identifier. -* - * @author Mirna Gama -* @version 1.0 -*/ public interface DoctorRepository extends JpaRepository { - /** - * - * @param pageable Pagination information, such as size and page number - * @return A paginated list with active stored doctors if successful, or null if there is an error - */ - Page findDoctorsByActiveTrue(Pageable pageable); - - /** - * - * @param specialty Desired specialty for doctor search - * @param consultationDate Date to check if the doctor is free - * @return A random free doctor with the defined specialty if successful, or null if it is non-existent - */ - @Query(""" - select d from Doctor d - where d.active = true - and specialty = :specialty - and d.id not in ( - select c.doctor.id from Consultation c - where c.consultationDate = :consultationDate - and c.canceled = false - ) - order by rand() - limit 1 - """) - Doctor findOneFreeDoctorBySpecialty(Specialty specialty, LocalDateTime consultationDate); -} + Doctor findByCrm(String crm); + + Page findDoctorsByActiveTrue(Pageable pageable); + + @Query(""" + select d from Doctor d + + where d.active = true + + and d.specialty = :specialty + + and d.id not in ( + + select c.doctor.id from Consultation c + + where c.canceled = false + + and YEAR(c.consultationDate) = YEAR(:consultationDate) + + and MONTH(c.consultationDate) = MONTH(:consultationDate) + + and DAY(c.consultationDate) = DAY(:consultationDate) + + and HOUR(c.consultationDate) = HOUR(:consultationDate) + + and MINUTE(c.consultationDate) = MINUTE(:consultationDate) + + ) + + """) + List findFreeDoctorsBySpecialty( + + @Param("specialty") Specialty specialty, + + @Param("consultationDate") OffsetDateTime consultationDate + + ); +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/DoctorScheduleRepository.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/DoctorScheduleRepository.java new file mode 100644 index 0000000..576799f --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/DoctorScheduleRepository.java @@ -0,0 +1,15 @@ +package com.mirna.hospitalmanagementapi.domain.repositories; + +import com.mirna.hospitalmanagementapi.domain.entities.DoctorSchedule; +import java.time.DayOfWeek; +import java.time.LocalTime; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import java.time.DayOfWeek; + +@Repository +public interface DoctorScheduleRepository extends JpaRepository { + + boolean existsByDoctorIdAndDayOfWeekAndStartTime(Long doctorId, DayOfWeek dayOfWeek, LocalTime startTime); + DoctorSchedule findByDoctorIdAndDayOfWeek(Long doctorId, DayOfWeek dayOfWeek); +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/InsuranceClaimRepository.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/InsuranceClaimRepository.java new file mode 100644 index 0000000..2c31d29 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/InsuranceClaimRepository.java @@ -0,0 +1,19 @@ +package com.mirna.hospitalmanagementapi.domain.repositories; + +import com.mirna.hospitalmanagementapi.domain.entities.InsuranceClaim; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Repository +public interface InsuranceClaimRepository extends JpaRepository { + Page findAll(Pageable pageable); + + @Query("SELECT SUM(ic.approvedAmount) FROM InsuranceClaim ic WHERE ic.submissionDate >= :startDate AND ic.submissionDate <= :endDate") + BigDecimal sumClaimAmountByDateRange(@Param("startDate") LocalDateTime startDate, @Param("endDate") LocalDateTime endDate); +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/InsuranceProviderRepository.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/InsuranceProviderRepository.java new file mode 100644 index 0000000..8dc9803 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/InsuranceProviderRepository.java @@ -0,0 +1,13 @@ +package com.mirna.hospitalmanagementapi.domain.repositories; + +import com.mirna.hospitalmanagementapi.domain.entities.InsuranceProvider; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +/** + * Repository interface for managing InsuranceProvider entities. + * This extends JpaRepository to provide standard CRUD operations. + */ +@Repository +public interface InsuranceProviderRepository extends JpaRepository { +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/LabResultRepository.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/LabResultRepository.java new file mode 100644 index 0000000..077f468 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/LabResultRepository.java @@ -0,0 +1,18 @@ +package com.mirna.hospitalmanagementapi.domain.repositories; + +import com.mirna.hospitalmanagementapi.domain.entities.LabResult; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import java.util.List; +import java.util.UUID; + +@Repository +public interface LabResultRepository extends JpaRepository { + + /** + * ค้นหารายการ LabResult ทั้งหมดที่เชื่อมโยงกับ MedicalRecord ID ที่กำหนด + * @param medicalRecordId ID ของ MedicalRecord + * @return รายการ LabResult ที่เกี่ยวข้อง + */ + List findByMedicalRecordId(Long medicalRecordId); +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/MedicalEquipmentScheduleRepository.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/MedicalEquipmentScheduleRepository.java new file mode 100644 index 0000000..66505fa --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/MedicalEquipmentScheduleRepository.java @@ -0,0 +1,18 @@ +package com.mirna.hospitalmanagementapi.domain.repositories; + +import com.mirna.hospitalmanagementapi.domain.entities.inventory.InventoryItem; +import com.mirna.hospitalmanagementapi.domain.entities.MedicalEquipmentSchedule; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.time.LocalDateTime; +import java.util.List; + +@Repository +public interface MedicalEquipmentScheduleRepository extends JpaRepository { + + List findByInventoryItemAndEndTimeAfterAndStartTimeBefore(InventoryItem inventoryItem, LocalDateTime startTime, LocalDateTime endTime); + + // หรือถ้าต้องการ query โดยใช้ ID โดยตรง + // List findByInventoryItemIdAndEndTimeAfterAndStartTimeBefore(UUID inventoryItemId, LocalDateTime startTime, LocalDateTime endTime); +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/MedicalImageRepository.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/MedicalImageRepository.java new file mode 100644 index 0000000..cc5782c --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/MedicalImageRepository.java @@ -0,0 +1,18 @@ +package com.mirna.hospitalmanagementapi.domain.repositories; + +import com.mirna.hospitalmanagementapi.domain.entities.MedicalImage; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import java.util.List; +import java.util.UUID; + +@Repository +public interface MedicalImageRepository extends JpaRepository { + + /** + * ค้นหารายการ MedicalImage ทั้งหมดที่เชื่อมโยงกับ MedicalRecord ID ที่กำหนด + * @param medicalRecordId ID ของ MedicalRecord + * @return รายการ MedicalImage ที่เกี่ยวข้อง + */ + List findByMedicalRecordId(Long medicalRecordId); +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/MedicalRecordRepository.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/MedicalRecordRepository.java new file mode 100644 index 0000000..12ecf07 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/MedicalRecordRepository.java @@ -0,0 +1,9 @@ +package com.mirna.hospitalmanagementapi.domain.repositories; + +import com.mirna.hospitalmanagementapi.domain.entities.MedicalRecord; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface MedicalRecordRepository extends JpaRepository { +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/NurseRepository.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/NurseRepository.java new file mode 100644 index 0000000..02b5440 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/NurseRepository.java @@ -0,0 +1,15 @@ +package com.mirna.hospitalmanagementapi.domain.repositories; + +import com.mirna.hospitalmanagementapi.domain.entities.Nurse; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import java.util.Optional; + +@Repository +public interface NurseRepository extends JpaRepository { + // เมธอดสำหรับค้นหาพยาบาลด้วยหมายเลขใบอนุญาต + Optional findByLicenseNumber(String licenseNumber); + + // เมธอดสำหรับค้นหาพยาบาลด้วย email + Optional findByEmail(String email); +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/NurseScheduleRepository.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/NurseScheduleRepository.java new file mode 100644 index 0000000..0177c0f --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/NurseScheduleRepository.java @@ -0,0 +1,24 @@ +package com.mirna.hospitalmanagementapi.domain.repositories; + +import com.mirna.hospitalmanagementapi.domain.entities.NurseSchedule; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; + +@Repository +public interface NurseScheduleRepository extends JpaRepository { + + @Query("SELECT s FROM NurseSchedule s WHERE s.nurse.id = :nurseId " + + "AND (:startTime < s.endTime AND :endTime > s.startTime)") + List findOverlappingSchedules(@Param("nurseId") Long nurseId, + @Param("startTime") LocalDateTime startTime, + @Param("endTime") LocalDateTime endTime); + + // เมธอดสำหรับค้นหากะล่าสุดของพยาบาล + Optional findTopByNurseIdOrderByEndTimeDesc(Long nurseId); +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/OperatingRoomRepository.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/OperatingRoomRepository.java new file mode 100644 index 0000000..02e231e --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/OperatingRoomRepository.java @@ -0,0 +1,9 @@ +package com.mirna.hospitalmanagementapi.domain.repositories; + +import com.mirna.hospitalmanagementapi.domain.entities.OperatingRoom; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface OperatingRoomRepository extends JpaRepository { +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/OperatingRoomScheduleRepository.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/OperatingRoomScheduleRepository.java new file mode 100644 index 0000000..b94fc46 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/OperatingRoomScheduleRepository.java @@ -0,0 +1,14 @@ +package com.mirna.hospitalmanagementapi.domain.repositories; + +import com.mirna.hospitalmanagementapi.domain.entities.OperatingRoomSchedule; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.time.LocalDateTime; +import java.util.List; + +@Repository +public interface OperatingRoomScheduleRepository extends JpaRepository { + + List findByOperatingRoomIdAndEndTimeAfterAndStartTimeBefore(Long roomId, LocalDateTime startTime, LocalDateTime endTime); +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/PaymentRepository.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/PaymentRepository.java new file mode 100644 index 0000000..e32c566 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/PaymentRepository.java @@ -0,0 +1,14 @@ +package com.mirna.hospitalmanagementapi.domain.repositories; + +import com.mirna.hospitalmanagementapi.domain.entities.Payment; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import java.math.BigDecimal; +import java.time.OffsetDateTime; +import java.util.List; + +public interface PaymentRepository extends JpaRepository { + @Query("SELECT SUM(p.amount) FROM Payment p WHERE p.paymentDate >= :startDate AND p.paymentDate <= :endDate") + BigDecimal sumAmountByDateRange(@Param("startDate") OffsetDateTime startDate, @Param("endDate") OffsetDateTime endDate); +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/PrescriptionRepository.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/PrescriptionRepository.java new file mode 100644 index 0000000..452ed3d --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/PrescriptionRepository.java @@ -0,0 +1,10 @@ +package com.mirna.hospitalmanagementapi.domain.repositories; + +import com.mirna.hospitalmanagementapi.domain.entities.Prescription; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.UUID; + +@Repository +public interface PrescriptionRepository extends JpaRepository {} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/StaffRepository.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/StaffRepository.java new file mode 100644 index 0000000..54e3056 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/StaffRepository.java @@ -0,0 +1,15 @@ +package com.mirna.hospitalmanagementapi.domain.repositories; + +import com.mirna.hospitalmanagementapi.domain.entities.Staff; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +/** + * A repository for managing staff entities. + * + * @author FlookSP + * @version 1.0 + */ +@Repository +public interface StaffRepository extends JpaRepository { +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/inventory/InventoryItemRepository.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/inventory/InventoryItemRepository.java new file mode 100644 index 0000000..b682ecf --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/inventory/InventoryItemRepository.java @@ -0,0 +1,19 @@ +package com.mirna.hospitalmanagementapi.domain.repositories.inventory; + +import com.mirna.hospitalmanagementapi.domain.entities.inventory.InventoryItem; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import java.util.UUID; +import org.springframework.data.jpa.repository.Query; +import java.util.List; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import com.mirna.hospitalmanagementapi.domain.dtos.LowStockItemDTO; + +@Repository +public interface InventoryItemRepository extends JpaRepository { + @Query("SELECT new com.mirna.hospitalmanagementapi.domain.dtos.LowStockItemDTO(i.id, i.itemName, i.currentStock, i.itemType.typeName, i.unitPrice, i.reorderLevel) " + + "FROM InventoryItem i " + + "WHERE i.currentStock <= i.reorderLevel") + Page findItemsRunningLow(Pageable pageable); +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/inventory/InventoryItemTypeRepository.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/inventory/InventoryItemTypeRepository.java new file mode 100644 index 0000000..739be47 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/inventory/InventoryItemTypeRepository.java @@ -0,0 +1,20 @@ +package com.mirna.hospitalmanagementapi.domain.repositories.inventory; + +import com.mirna.hospitalmanagementapi.domain.entities.inventory.InventoryItemType; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import java.util.UUID; + +/** + * Repository interface for managing InventoryItemType entities. + * It provides standard CRUD operations. + * + * @author FlookSP + * @version 1.0 + */ +@Repository +public interface InventoryItemTypeRepository extends JpaRepository { + // You can add custom query methods here if needed, + // for example: + // Optional findByType(String type); +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/inventory/InventorySupplierRepository.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/inventory/InventorySupplierRepository.java new file mode 100644 index 0000000..4e46ea0 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/inventory/InventorySupplierRepository.java @@ -0,0 +1,20 @@ +package com.mirna.hospitalmanagementapi.domain.repositories.inventory; + +import com.mirna.hospitalmanagementapi.domain.entities.inventory.InventorySupplier; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import java.util.UUID; + +/** + * Repository interface for managing InventorySupplier entities. + * It provides standard CRUD operations. + * + * @author FlookSP + * @version 1.0 + */ +@Repository +public interface InventorySupplierRepository extends JpaRepository { + // You can add custom query methods here if needed, + // for example: + // Optional findByName(String name); +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/inventory/InventoryTransactionRepository.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/inventory/InventoryTransactionRepository.java new file mode 100644 index 0000000..cea0457 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/repositories/inventory/InventoryTransactionRepository.java @@ -0,0 +1,21 @@ +package com.mirna.hospitalmanagementapi.domain.repositories.inventory; + +import com.mirna.hospitalmanagementapi.domain.entities.inventory.InventoryTransaction; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import java.util.UUID; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.time.ZonedDateTime; + +@Repository +public interface InventoryTransactionRepository extends JpaRepository { + @Query("SELECT SUM(it.quantity * it.item.unitPrice) " + + "FROM InventoryTransaction it " + + "WHERE it.transactionDate >= :startDate " + + "AND it.transactionDate <= :endDate") + BigDecimal sumTotalCostByDateRange(@Param("startDate") ZonedDateTime startDate, @Param("endDate") ZonedDateTime endDate); + +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/services/BillingService.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/services/BillingService.java new file mode 100644 index 0000000..6e4bfb1 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/services/BillingService.java @@ -0,0 +1,44 @@ +package com.mirna.hospitalmanagementapi.domain.services; + +import com.mirna.hospitalmanagementapi.domain.entities.Billing; +import com.mirna.hospitalmanagementapi.domain.dtos.BillingDTO; + +/** + * Service interface for managing billing-related business logic. + */ +public interface BillingService { + + /** + * Creates a new billing based on the provided DTO. + * + * @param billingDTO The data transfer object containing billing details. + * @return The created Billing entity. + */ + Billing createBilling(BillingDTO billingDTO); + + /** + * Retrieves a billing by its unique ID. + * + * @param id The unique ID of the billing. + * @return The found Billing entity, or null if not found. + */ + Billing getBillingById(Long id); + + /** + * Updates an existing billing with new data. + * + * @param id The ID of the billing to update. + * @param billingDTO The DTO with the updated data. + * @return The updated Billing entity. + */ + Billing updateBilling(Long id, BillingDTO billingDTO); + + /** + * Deletes a billing by its ID. + * + * @param id The ID of the billing to delete. + */ + void deleteBilling(Long id); + + boolean isOwner(Long billingId, Long patientId); +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/services/DoctorScheduleService.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/services/DoctorScheduleService.java new file mode 100644 index 0000000..b23aa7e --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/services/DoctorScheduleService.java @@ -0,0 +1,10 @@ +package com.mirna.hospitalmanagementapi.domain.services; + +import com.mirna.hospitalmanagementapi.domain.entities.DoctorSchedule; +import java.time.DayOfWeek; +import java.time.LocalTime; + +public interface DoctorScheduleService { + DoctorSchedule saveDoctorSchedule(DoctorSchedule doctorSchedule); + boolean isDoctorAvailable(Long doctorId, DayOfWeek day, LocalTime time); +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/services/InsuranceClaimService.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/services/InsuranceClaimService.java new file mode 100644 index 0000000..d62da69 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/services/InsuranceClaimService.java @@ -0,0 +1,15 @@ +package com.mirna.hospitalmanagementapi.domain.services; + +import com.mirna.hospitalmanagementapi.domain.entities.InsuranceClaim; +import com.mirna.hospitalmanagementapi.domain.dtos.InsuranceClaimDTO; +import java.util.List; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +public interface InsuranceClaimService { + InsuranceClaim createInsuranceClaim(InsuranceClaimDTO insuranceClaimDTO); + InsuranceClaim getInsuranceClaimById(Long id); + Page getAllInsuranceClaims(Pageable pageable); + InsuranceClaim updateInsuranceClaim(Long id, InsuranceClaimDTO insuranceClaimDTO); + void deleteInsuranceClaim(Long id); +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/services/InsuranceProviderService.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/services/InsuranceProviderService.java new file mode 100644 index 0000000..d9ca088 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/services/InsuranceProviderService.java @@ -0,0 +1,51 @@ +package com.mirna.hospitalmanagementapi.domain.services; + +import com.mirna.hospitalmanagementapi.domain.entities.InsuranceProvider; +import com.mirna.hospitalmanagementapi.domain.dtos.InsuranceProviderDTO; +import jakarta.persistence.EntityNotFoundException; +import java.util.List; + +public interface InsuranceProviderService { + + /** + * Adds a new insurance provider to the database. + * + * @param insuranceProviderDTO The DTO containing the data for the new insurance provider. + * @return The saved InsuranceProvider entity. + */ + InsuranceProvider addInsuranceProvider(InsuranceProviderDTO insuranceProviderDTO); + + /** + * Retrieves all insurance providers from the database. + * + * @return A list of all InsuranceProvider entities. + */ + List getAllInsuranceProviders(); + + /** + * Finds an insurance provider by its ID. + * + * @param id The ID of the insurance provider to find. + * @return The found InsuranceProvider entity. + * @throws EntityNotFoundException if the insurance provider with the given ID is not found. + */ + InsuranceProvider getInsuranceProviderById(Long id) throws EntityNotFoundException; + + /** + * Updates an existing insurance provider. + * + * @param id The ID of the insurance provider to update. + * @param updatedProviderDTO The DTO containing the updated data. + * @return The updated InsuranceProvider entity. + * @throws EntityNotFoundException if the insurance provider with the given ID is not found. + */ + InsuranceProvider updateInsuranceProvider(Long id, InsuranceProviderDTO updatedProviderDTO) throws EntityNotFoundException; + + /** + * Deletes an insurance provider from the database. + * + * @param id The ID of the insurance provider to delete. + * @throws EntityNotFoundException if the insurance provider with the given ID is not found. + */ + void deleteInsuranceProvider(Long id) throws EntityNotFoundException; +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/services/LabResultService.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/services/LabResultService.java new file mode 100644 index 0000000..276b4e7 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/services/LabResultService.java @@ -0,0 +1,14 @@ +package com.mirna.hospitalmanagementapi.domain.services; + +import com.mirna.hospitalmanagementapi.domain.entities.LabResult; +import com.mirna.hospitalmanagementapi.domain.dtos.LabResultDTO; +import java.util.UUID; + +public interface LabResultService { + + LabResult addLabResult(Long medicalRecordId, LabResultDTO labResultDTO); + + LabResult getLabResultById(UUID id); + + boolean isOwner(UUID labResultId, Long currentUserId); +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/services/MedicalEquipmentScheduleService.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/services/MedicalEquipmentScheduleService.java new file mode 100644 index 0000000..a143821 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/services/MedicalEquipmentScheduleService.java @@ -0,0 +1,17 @@ +package com.mirna.hospitalmanagementapi.domain.services; + +import com.mirna.hospitalmanagementapi.domain.dtos.MedicalEquipmentScheduleDTO; +import com.mirna.hospitalmanagementapi.domain.entities.MedicalEquipmentSchedule; +import com.mirna.hospitalmanagementapi.domain.dtos.MedicalEquipmentScheduleResponseDTO; +import jakarta.persistence.EntityNotFoundException; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +public interface MedicalEquipmentScheduleService { + + MedicalEquipmentScheduleResponseDTO addMedicalEquipmentSchedule(MedicalEquipmentScheduleDTO medicalEquipmentScheduleDTO); + + MedicalEquipmentScheduleResponseDTO getMedicalEquipmentScheduleById(Long id) throws EntityNotFoundException; + + Page getAllMedicalEquipmentSchedules(Pageable pageable); +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/services/MedicalImageService.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/services/MedicalImageService.java new file mode 100644 index 0000000..451934d --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/services/MedicalImageService.java @@ -0,0 +1,15 @@ +package com.mirna.hospitalmanagementapi.domain.services; + +import com.mirna.hospitalmanagementapi.domain.entities.MedicalImage; +import org.springframework.core.io.Resource; +import org.springframework.web.multipart.MultipartFile; +import java.util.UUID; + +public interface MedicalImageService { + + MedicalImage uploadImage(Long medicalRecordId, MultipartFile file, String imageType, String notes); + + Resource downloadImage(UUID id); + + boolean isOwner(UUID medicalImageId, Long currentUserId); +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/services/MedicalRecordService.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/services/MedicalRecordService.java new file mode 100644 index 0000000..01f9f5f --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/services/MedicalRecordService.java @@ -0,0 +1,17 @@ +package com.mirna.hospitalmanagementapi.domain.services; + +import com.mirna.hospitalmanagementapi.domain.entities.MedicalRecord; +import com.mirna.hospitalmanagementapi.domain.dtos.MedicalRecordDTO; + +public interface MedicalRecordService { + MedicalRecord addMedicalRecord(MedicalRecordDTO medicalRecordDTO); + + MedicalRecord getMedicalRecordById(Long id); + + MedicalRecord getMedicalRecordDetails(Long id); + + MedicalRecord updateMedicalRecord(Long id, MedicalRecordDTO medicalRecordDTO); + void deleteMedicalRecord(Long id); + + boolean isOwner(Long medicalRecordId, Long patientId); +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/services/NurseScheduleService.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/services/NurseScheduleService.java new file mode 100644 index 0000000..ab4ad2d --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/services/NurseScheduleService.java @@ -0,0 +1,16 @@ +package com.mirna.hospitalmanagementapi.domain.services; + +import com.mirna.hospitalmanagementapi.domain.dtos.NurseScheduleDTO; +import com.mirna.hospitalmanagementapi.domain.entities.NurseSchedule; +import jakarta.persistence.EntityNotFoundException; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +public interface NurseScheduleService { + + NurseSchedule addNurseSchedule(NurseScheduleDTO nurseScheduleDTO); + + NurseSchedule getNurseScheduleById(Long id) throws EntityNotFoundException; + + Page getAllNurseSchedules(Pageable pageable); +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/services/NurseService.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/services/NurseService.java new file mode 100644 index 0000000..93ed47b --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/services/NurseService.java @@ -0,0 +1,20 @@ +package com.mirna.hospitalmanagementapi.domain.services; + +import com.mirna.hospitalmanagementapi.domain.dtos.NurseDTO; +import com.mirna.hospitalmanagementapi.domain.entities.Nurse; +import jakarta.persistence.EntityNotFoundException; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +public interface NurseService { + + Nurse addNurse(NurseDTO nurseDTO); + + Nurse getNurseById(Long id) throws EntityNotFoundException; + + Page getAllNurses(Pageable pageable); + + Nurse updateNurse(Long id, NurseDTO nurseDTO) throws EntityNotFoundException; + + void deleteNurse(Long id) throws EntityNotFoundException; +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/services/OperatingRoomScheduleService.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/services/OperatingRoomScheduleService.java new file mode 100644 index 0000000..d76f9f4 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/services/OperatingRoomScheduleService.java @@ -0,0 +1,16 @@ +package com.mirna.hospitalmanagementapi.domain.services; + +import com.mirna.hospitalmanagementapi.domain.dtos.OperatingRoomScheduleDTO; +import com.mirna.hospitalmanagementapi.domain.entities.OperatingRoomSchedule; +import jakarta.persistence.EntityNotFoundException; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +public interface OperatingRoomScheduleService { + + OperatingRoomSchedule addOperatingRoomSchedule(OperatingRoomScheduleDTO operatingRoomScheduleDTO); + + OperatingRoomSchedule getOperatingRoomScheduleById(Long id) throws EntityNotFoundException; + + Page getAllOperatingRoomSchedules(Pageable pageable); +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/services/OperatingRoomService.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/services/OperatingRoomService.java new file mode 100644 index 0000000..cf3b20b --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/services/OperatingRoomService.java @@ -0,0 +1,20 @@ +package com.mirna.hospitalmanagementapi.domain.services; + +import com.mirna.hospitalmanagementapi.domain.dtos.OperatingRoomDTO; +import com.mirna.hospitalmanagementapi.domain.entities.OperatingRoom; +import jakarta.persistence.EntityNotFoundException; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +public interface OperatingRoomService { + + OperatingRoom addOperatingRoom(OperatingRoomDTO operatingRoomDTO); + + OperatingRoom getOperatingRoomById(Long id) throws EntityNotFoundException; + + Page getAllOperatingRooms(Pageable pageable); + + OperatingRoom updateOperatingRoom(Long id, OperatingRoomDTO operatingRoomDTO) throws EntityNotFoundException; + + void deleteOperatingRoom(Long id) throws EntityNotFoundException; +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/services/PatientService.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/services/PatientService.java index a3eed40..1a766e3 100644 --- a/src/main/java/com/mirna/hospitalmanagementapi/domain/services/PatientService.java +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/services/PatientService.java @@ -3,6 +3,7 @@ package com.mirna.hospitalmanagementapi.domain.services; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import com.mirna.hospitalmanagementapi.domain.dtos.auth.LinkPatientToUserDTO; // เพิ่ม import นี้ import com.mirna.hospitalmanagementapi.domain.dtos.patient.PatientDTO; import com.mirna.hospitalmanagementapi.domain.dtos.patient.PatientPublicDataDTO; import com.mirna.hospitalmanagementapi.domain.dtos.patient.PatientUpdatedDataDTO; @@ -10,51 +11,54 @@ import com.mirna.hospitalmanagementapi.domain.entities.Patient; /** * Patient service interface for managing patient information. -* + * * @see Patient - * @author Mirna Gama + * @author Mirna Gama (Extended by FlookSP) * @version 1.0 -*/ + */ public interface PatientService { - /** - * Adds a new patient to the repository. - * - * @param patientDTO A data transfer object representing a patient to add. - * @return The saved patient if successful, or null if there is an error. - */ + /** + * Adds a new patient to the repository. + * * @param patientDTO A data transfer object representing a patient to add. + * @return The saved patient if successful, or null if there is an error. + */ public Patient addPatient(PatientDTO patientDTO); - + /** - * Finds a stored patient by id. - * - * @param id A long representing the patient's unique identifier - * @return The corresponding patient if successful, or null if it is non-existent. - */ + * Finds a stored patient by id. + * * @param id A long representing the patient's unique identifier + * @return The corresponding patient if successful, or null if it is non-existent. + */ public Patient findPatientById(Long id); - + /** - * Retrieves a paginated sublist of doctors. - * - * @param pageable Pagination information, such as size and page number - * - * @return A paginated sublist containing data transfer objects with patients public information in the repository - */ + * Retrieves a paginated sublist of doctors. + * * @param pageable Pagination information, such as size and page number + * * @return A paginated sublist containing data transfer objects with patients public information in the repository + */ public Page findPatients(Pageable pageable); - + /** * Updates an existing patient record - * @param patientUpdatedDataDTO Data transfer object containing the patient updated data along with their corresponding id - * - * @return The updated patient if successful, or null if there is an error. - */ + * @param patientUpdatedDataDTO Data transfer object containing the patient updated data along with their corresponding id + * * @return The updated patient if successful, or null if there is an error. + */ public Patient updatePatient(PatientUpdatedDataDTO patientUpdatedDataDTO); - + /** * Deactivates an existing patient record by provided id * @param id Long that represents the patient's unique identifier - * - * @return The deactivated patient if successful, or null if there is an error. - */ + * * @return The deactivated patient if successful, or null if there is an error. + */ public Patient deactivatePatient(Long id); -} + + /** + * Links an existing patient record to a new user account. + * + * @param linkPatientToUserDTO A data transfer object containing the patient's ID, new username, password, and CPF. + * @return The updated patient if successful. + */ + public Patient linkPatientToUser(LinkPatientToUserDTO linkPatientToUserDTO); // เพิ่มบรรทัดนี้ + +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/services/PaymentService.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/services/PaymentService.java new file mode 100644 index 0000000..a65b7c0 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/services/PaymentService.java @@ -0,0 +1,41 @@ +package com.mirna.hospitalmanagementapi.domain.services; + +import com.mirna.hospitalmanagementapi.domain.dtos.PaymentDTO; +import com.mirna.hospitalmanagementapi.domain.entities.Payment; +import java.util.List; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +/** + * Interface for the PaymentService. + * This defines the contract for handling payment-related business logic. + * + * @author FlookSP + * @version 1.1 + */ +public interface PaymentService { + + /** + * Creates a new payment record in the system. + * + * @param paymentDTO Data Transfer Object containing payment details. + * @return The saved Payment entity. + */ + Payment createPayment(PaymentDTO paymentDTO); + + /** + * Finds a payment record by its unique ID. + * + * @param id The ID of the payment. + * @return The Payment entity if found, otherwise throws an exception. + */ + Payment getPaymentById(Long id); + + /** + * Retrieves all payment records from the system with pagination. + * + * @param pageable Pagination information, such as size and page number. + * @return A paginated list of all Payment entities. + */ + Page getAllPayments(Pageable pageable); +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/services/PrescriptionService.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/services/PrescriptionService.java new file mode 100644 index 0000000..e9ccaed --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/services/PrescriptionService.java @@ -0,0 +1,18 @@ +package com.mirna.hospitalmanagementapi.domain.services; + +import com.mirna.hospitalmanagementapi.domain.dtos.PrescriptionDTO; +import com.mirna.hospitalmanagementapi.domain.entities.Prescription; +import com.mirna.hospitalmanagementapi.domain.exceptions.ConsultationValidationException; +import jakarta.persistence.EntityNotFoundException; + +import java.util.UUID; + +public interface PrescriptionService { + Prescription createPrescription(PrescriptionDTO prescriptionDTO) throws EntityNotFoundException, ConsultationValidationException; + + Prescription getPrescriptionById(UUID id) throws EntityNotFoundException; + + Prescription updatePrescription(UUID id, PrescriptionDTO prescriptionDTO) throws EntityNotFoundException, ConsultationValidationException; + + boolean isOwner(UUID prescriptionId, Long patientId); +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/services/ReportService.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/services/ReportService.java new file mode 100644 index 0000000..b5db5ad --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/services/ReportService.java @@ -0,0 +1,19 @@ +package com.mirna.hospitalmanagementapi.domain.services; + +import com.mirna.hospitalmanagementapi.domain.dtos.FinancialReportDTO; +import java.time.LocalDate; +import java.util.List; +import com.mirna.hospitalmanagementapi.domain.entities.inventory.InventoryItem; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import com.mirna.hospitalmanagementapi.domain.dtos.AppointmentReportDTO; +import com.mirna.hospitalmanagementapi.domain.dtos.LowStockItemDTO; + +public interface ReportService { + + List getFinancialReport(LocalDate startDate, LocalDate endDate); + + Page getLowStockItems(Pageable pageable); + + AppointmentReportDTO getAppointmentReport(LocalDate startDate, LocalDate endDate); +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/services/StaffService.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/services/StaffService.java new file mode 100644 index 0000000..991c9cb --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/services/StaffService.java @@ -0,0 +1,21 @@ +package com.mirna.hospitalmanagementapi.domain.services; + +import com.mirna.hospitalmanagementapi.domain.entities.Staff; + +/** + * Staff service interface for managing staff information. + * + * @see Staff + * @author FlookSP + * @version 1.0 + */ +public interface StaffService { + + /** + * Adds a new staff member to the repository. + * + * @param staff A staff entity to add. + * @return The saved staff if successful. + */ + public Staff addStaff(Staff staff); +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/services/UserService.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/services/UserService.java index 22fbb37..a982c30 100644 --- a/src/main/java/com/mirna/hospitalmanagementapi/domain/services/UserService.java +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/services/UserService.java @@ -1,34 +1,54 @@ package com.mirna.hospitalmanagementapi.domain.services; import org.springframework.security.core.userdetails.UserDetails; - -import com.mirna.hospitalmanagementapi.domain.dtos.auth.UserDTO; import com.mirna.hospitalmanagementapi.domain.entities.auth.User; +import com.mirna.hospitalmanagementapi.domain.dtos.auth.UserUpdateUsernameDTO; +import com.mirna.hospitalmanagementapi.domain.dtos.auth.UserUpdatePasswordDTO; +import com.mirna.hospitalmanagementapi.domain.dtos.auth.UserUpdateRoleDTO; /** * User service interface for managing user information. -* + * * @see User - * @author Mirna Gama - * @version 1.0 -*/ + * @author Mirna Gama (Extended by FlookSP) + * @version 1.1 + */ public interface UserService { - /** - * Adds a new user to the repository. - * - * @param userDTO A data transfer object representing a user to add. - * @return The saved user if successful, or null if there is an error. - */ - public User addUser(UserDTO userDTO); + /** + * Adds a new user to the repository. + * + * @param user A user entity to add. + * @return The saved user if successful, or null if there is an error. + */ + public User addUser(User user); - - /** - * Finds a stored user information by login. - * - * @param login A string representing the user's system login - * @return The corresponding user information if successful, or null if it is non-existent. - */ + + /** + * Finds a stored user information by login. + * + * @param login A string representing the user's system login + * @return The corresponding user information if successful, or null if it is non-existent. + */ public UserDetails findUserByLogin(String login); - -} + + public User updateUsername(Long id, UserUpdateUsernameDTO dto); + public User updatePassword(Long id, UserUpdatePasswordDTO dto); + public User updateRole(Long id, UserUpdateRoleDTO dto); + + /** + * Deactivates a user account by setting the 'enabled' status to false. + * + * @param id The ID of the user to deactivate. + * @return The deactivated User entity. + */ + public User deactivateUser(Long id); + + /** + * Activates a user account by setting the 'enabled' status to true. + * + * @param id The ID of the user to activate. + * @return The activated User entity. + */ + public User activateUser(Long id); +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/services/auth/AuthService.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/services/auth/AuthService.java index 3c06e6c..54fba17 100644 --- a/src/main/java/com/mirna/hospitalmanagementapi/domain/services/auth/AuthService.java +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/services/auth/AuthService.java @@ -4,30 +4,58 @@ import org.springframework.security.core.Authentication; import com.mirna.hospitalmanagementapi.domain.dtos.auth.UserDTO; import com.mirna.hospitalmanagementapi.domain.entities.auth.User; +import com.mirna.hospitalmanagementapi.domain.dtos.auth.PatientRegistrationDTO; +import com.mirna.hospitalmanagementapi.domain.dtos.auth.DoctorRegistrationDTO; +import com.mirna.hospitalmanagementapi.domain.dtos.auth.StaffRegistrationDTO; +import com.mirna.hospitalmanagementapi.domain.entities.Staff; +import com.mirna.hospitalmanagementapi.domain.dtos.auth.NurseRegistrationDTO; /** * Authentication service interface for managing authentication and registration. -* + * * @see User - * @author Mirna Gama + * @author Mirna Gama (Extended by FlookSP) * @version 1.0 -*/ + */ public interface AuthService { - /** - * Performs the user login - * - * @param userDTO Data transfer object containing user credentials for authentication operations - * @return A fully authentication object including the credentials - */ - public Authentication login(UserDTO userDTO); - - /** - * Performs the user registration - * - * @param userDTO Data transfer object containing user credentials for authentication operations - * @return A user object including the credentials - */ - public User register(UserDTO userDTO); + /** + * Performs the user login + * * @param userDTO Data transfer object containing user credentials for authentication operations + * @return A fully authentication object including the credentials + */ + public Authentication login(UserDTO userDTO); -} + /** + * Performs the user registration + * * @param userDTO Data transfer object containing user credentials for authentication operations + * @return A user object including the credentials + */ + //public User register(UserDTO userDTO); + + public User registerPatient(PatientRegistrationDTO registrationDTO); + + /** + * Registers a new user account for a doctor and links it to an existing doctor record. + * * @param registrationDTO Data transfer object containing user credentials and CRM for linking + * @return A new user object if registration and linking are successful. + */ + public User registerDoctor(DoctorRegistrationDTO registrationDTO); + + /** + * Registers a new staff member. + * + * @param dto A staff registration data transfer object. + * @return The saved staff entity. + */ + public Staff registerStaff(StaffRegistrationDTO dto); + + /** + * Registers a new user account for a nurse and links it to an existing nurse record. + * + * @param registrationDTO Data transfer object containing user credentials and license number for linking + * @return A new user object if registration and linking are successful. + */ + public User registerNurseAndLink(NurseRegistrationDTO registrationDTO); + +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/services/inventory/InventoryItemService.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/services/inventory/InventoryItemService.java new file mode 100644 index 0000000..7187990 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/services/inventory/InventoryItemService.java @@ -0,0 +1,18 @@ +package com.mirna.hospitalmanagementapi.domain.services.inventory; + +import com.mirna.hospitalmanagementapi.domain.dtos.inventory.InventoryItemDTO; +import com.mirna.hospitalmanagementapi.domain.dtos.inventory.InventoryItemResponseDTO; +import com.mirna.hospitalmanagementapi.domain.entities.inventory.InventoryItem; +import jakarta.persistence.EntityNotFoundException; +import java.util.UUID; + +public interface InventoryItemService { + InventoryItemResponseDTO addInventoryItem(InventoryItemDTO itemDTO); + InventoryItemResponseDTO getInventoryItemById(UUID id); + InventoryItemResponseDTO updateInventoryItem(UUID id, InventoryItemDTO itemDTO); + void deleteInventoryItem(UUID id); + + // เพิ่มเมธอดนี้สำหรับ Service ภายในที่ต้องการทำงานกับ Entity โดยตรง + InventoryItem findInventoryItemEntityById(UUID id) throws EntityNotFoundException; + InventoryItem saveInventoryItemEntity(InventoryItem item); +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/services/inventory/InventoryTransactionService.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/services/inventory/InventoryTransactionService.java new file mode 100644 index 0000000..6aaaaea --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/services/inventory/InventoryTransactionService.java @@ -0,0 +1,9 @@ +package com.mirna.hospitalmanagementapi.domain.services.inventory; + +import com.mirna.hospitalmanagementapi.domain.dtos.inventory.InventoryTransactionDTO; +import com.mirna.hospitalmanagementapi.domain.dtos.inventory.InventoryTransactionResponseDTO; + +public interface InventoryTransactionService { + // เปลี่ยนชนิดข้อมูลที่ return จาก InventoryTransaction เป็น InventoryTransactionResponseDTO + InventoryTransactionResponseDTO addTransaction(InventoryTransactionDTO transactionDTO); +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/services/inventory/ItemTypeService.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/services/inventory/ItemTypeService.java new file mode 100644 index 0000000..5980c93 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/services/inventory/ItemTypeService.java @@ -0,0 +1,9 @@ +package com.mirna.hospitalmanagementapi.domain.services.inventory; + +import com.mirna.hospitalmanagementapi.domain.dtos.inventory.ItemTypeDTO; +import com.mirna.hospitalmanagementapi.domain.entities.inventory.InventoryItemType; + +public interface ItemTypeService { + + public InventoryItemType addItemType(ItemTypeDTO itemTypeDTO); +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/services/inventory/SupplierService.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/services/inventory/SupplierService.java new file mode 100644 index 0000000..760c9a8 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/services/inventory/SupplierService.java @@ -0,0 +1,9 @@ +package com.mirna.hospitalmanagementapi.domain.services.inventory; + +import com.mirna.hospitalmanagementapi.domain.dtos.inventory.SupplierDTO; +import com.mirna.hospitalmanagementapi.domain.entities.inventory.InventorySupplier; + +public interface SupplierService { + + public InventorySupplier addSupplier(SupplierDTO supplierDTO); +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/validators/consultation/ConsultationDateBusinessHoursValidator.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/validators/consultation/ConsultationDateBusinessHoursValidator.java index 3c8fd25..c3fa1dd 100644 --- a/src/main/java/com/mirna/hospitalmanagementapi/domain/validators/consultation/ConsultationDateBusinessHoursValidator.java +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/validators/consultation/ConsultationDateBusinessHoursValidator.java @@ -1,34 +1,50 @@ package com.mirna.hospitalmanagementapi.domain.validators.consultation; import java.time.DayOfWeek; -import java.time.LocalDateTime; - +import java.time.OffsetDateTime; +import java.time.ZoneOffset; import com.mirna.hospitalmanagementapi.domain.validators.consultation.constraints.ConsultationDateBusinessHours; - import jakarta.validation.ConstraintValidator; import jakarta.validation.ConstraintValidatorContext; -import jakarta.validation.constraints.Future; -public class ConsultationDateBusinessHoursValidator implements ConstraintValidator { +public class ConsultationDateBusinessHoursValidator implements ConstraintValidator { - private Long businessHourStart; - private Long businessHourEnd; - - @Override - public void initialize(ConsultationDateBusinessHours consultationDateBusinessHours) { - businessHourStart = 7L; - businessHourEnd = 18L; - } - - - @Override - public boolean isValid(LocalDateTime value, ConstraintValidatorContext context) { - - if (value.getDayOfWeek().equals(DayOfWeek.SUNDAY) - || value.getHour() < businessHourStart - || value.getHour() > businessHourEnd) return false; - - return true; - } + // กำหนดช่วงเวลาทำการให้ตรงกับตารางเวรของแพทย์ + private final Long businessHourStart = 8L; + private final Long businessHourEnd = 18L; -} + @Override + public void initialize(ConsultationDateBusinessHours consultationDateBusinessHours) { + // ไม่จำเป็นต้องกำหนดค่าตรงนี้อีกครั้ง เพราะเราใช้ final field ด้านบน + } + + @Override + public boolean isValid(OffsetDateTime value, ConstraintValidatorContext context) { + if (value == null) { + return true; + } + + DayOfWeek dayOfWeek = value.atZoneSameInstant(ZoneOffset.ofHours(7)).getDayOfWeek(); + int hour = value.atZoneSameInstant(ZoneOffset.ofHours(7)).getHour(); + + // Debug Log + System.out.println("--- BusinessHoursValidator Debug ---"); + System.out.println("Consultation Time: " + value); + System.out.println("Consultation Day (+07:00): " + dayOfWeek); + System.out.println("Consultation Hour (+07:00): " + hour); + System.out.println("Business hours: " + businessHourStart + " - " + businessHourEnd); + System.out.println("-----------------------------------"); + + // Check if it's a Sunday or outside business hours (inclusive start, exclusive end) + if (dayOfWeek.equals(DayOfWeek.SUNDAY) || hour < businessHourStart || hour >= businessHourEnd) { + // เมื่อ validation ล้มเหลว จะตั้งค่าข้อความผิดพลาดใหม่ให้ตรงกับเงื่อนไข + context.disableDefaultConstraintViolation(); + context.buildConstraintViolationWithTemplate( + "Invalid consultation date. The business hours are Monday to Saturday, from 08:00 to 18:00." + ).addConstraintViolation(); + return false; + } + + return true; + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/domain/validators/consultation/ConsultationDateScheduledInAdvanceValidator.java b/src/main/java/com/mirna/hospitalmanagementapi/domain/validators/consultation/ConsultationDateScheduledInAdvanceValidator.java index c2f2043..7097be1 100644 --- a/src/main/java/com/mirna/hospitalmanagementapi/domain/validators/consultation/ConsultationDateScheduledInAdvanceValidator.java +++ b/src/main/java/com/mirna/hospitalmanagementapi/domain/validators/consultation/ConsultationDateScheduledInAdvanceValidator.java @@ -1,25 +1,35 @@ package com.mirna.hospitalmanagementapi.domain.validators.consultation; import java.time.Duration; -import java.time.LocalDateTime; - +import java.time.OffsetDateTime; +import java.time.ZoneOffset; import com.mirna.hospitalmanagementapi.domain.validators.consultation.constraints.ConsultationDateScheduledInAdvance; - import jakarta.validation.ConstraintValidator; import jakarta.validation.ConstraintValidatorContext; -import jakarta.validation.constraints.Future; -public class ConsultationDateScheduledInAdvanceValidator implements ConstraintValidator { +public class ConsultationDateScheduledInAdvanceValidator implements ConstraintValidator { - @Override - public boolean isValid(LocalDateTime value, ConstraintValidatorContext context) { - LocalDateTime now = LocalDateTime.now(); - - long diff = Duration.between(now, value).toMinutes(); - - if (diff < 30L) return false; - - return true; - } + @Override + public boolean isValid(OffsetDateTime value, ConstraintValidatorContext context) { + if (value == null) { + return true; + } -} + OffsetDateTime now = OffsetDateTime.now(ZoneOffset.ofHours(7)); + long diff = Duration.between(now, value.withOffsetSameInstant(now.getOffset())).toMinutes(); + + // Debug log + System.out.println("--- ScheduledInAdvanceValidator Debug ---"); + System.out.println("Current Time (+07:00): " + now); + System.out.println("Consultation Time (+07:00): " + value); + System.out.println("Time Difference (minutes): " + diff); + System.out.println("Is diff >= 30? " + (diff >= 30L)); + System.out.println("---------------------------------------"); + + if (diff < 30L) { + return false; + } + + return true; + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/infra/converters/OffsetDateTimeConverter.java b/src/main/java/com/mirna/hospitalmanagementapi/infra/converters/OffsetDateTimeConverter.java new file mode 100644 index 0000000..fad35a2 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/infra/converters/OffsetDateTimeConverter.java @@ -0,0 +1,33 @@ +package com.mirna.hospitalmanagementapi.infra.converters; + +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import jakarta.persistence.AttributeConverter; +import jakarta.persistence.Converter; + +/** + * A converter to persist and retrieve {@link OffsetDateTime} as {@link LocalDateTime}. + * The conversion assumes UTC for the offset, ensuring consistency across different timezones. + */ +@Converter(autoApply = true) +public class OffsetDateTimeConverter implements AttributeConverter { + + @Override + public LocalDateTime convertToDatabaseColumn(OffsetDateTime entityValue) { + if (entityValue == null) { + return null; + } + // Convert OffsetDateTime to LocalDateTime using UTC offset for consistency + return entityValue.atZoneSameInstant(ZoneOffset.UTC).toLocalDateTime(); + } + + @Override + public OffsetDateTime convertToEntityAttribute(LocalDateTime databaseValue) { + if (databaseValue == null) { + return null; + } + // Convert LocalDateTime from database to OffsetDateTime with UTC offset + return databaseValue.atOffset(ZoneOffset.UTC); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/infra/security/config/AppConfig.java b/src/main/java/com/mirna/hospitalmanagementapi/infra/security/config/AppConfig.java new file mode 100644 index 0000000..7cdac2a --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/infra/security/config/AppConfig.java @@ -0,0 +1,15 @@ +package com.mirna.hospitalmanagementapi.infra.security.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; + +@Configuration +public class AppConfig { + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/infra/security/config/MinioConfig.java b/src/main/java/com/mirna/hospitalmanagementapi/infra/security/config/MinioConfig.java new file mode 100644 index 0000000..3892100 --- /dev/null +++ b/src/main/java/com/mirna/hospitalmanagementapi/infra/security/config/MinioConfig.java @@ -0,0 +1,27 @@ +package com.mirna.hospitalmanagementapi.infra.security.config; + +import io.minio.MinioClient; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class MinioConfig { + + @Value("${minio.url}") + private String minioUrl; + + @Value("${minio.access-key}") + private String accessKey; + + @Value("${minio.secret-key}") + private String secretKey; + + @Bean + public MinioClient minioClient() { + return MinioClient.builder() + .endpoint(minioUrl) + .credentials(accessKey, secretKey) + .build(); + } +} \ No newline at end of file diff --git a/src/main/java/com/mirna/hospitalmanagementapi/infra/security/config/OpenAPIDocConfiguration.java b/src/main/java/com/mirna/hospitalmanagementapi/infra/security/config/OpenAPIDocConfiguration.java index 40d3464..73fbfa1 100644 --- a/src/main/java/com/mirna/hospitalmanagementapi/infra/security/config/OpenAPIDocConfiguration.java +++ b/src/main/java/com/mirna/hospitalmanagementapi/infra/security/config/OpenAPIDocConfiguration.java @@ -12,18 +12,24 @@ import io.swagger.v3.oas.models.security.SecurityScheme; @Configuration public class OpenAPIDocConfiguration { - @Bean + @Bean public OpenAPI customOpenAPI() { return new OpenAPI() - .components(new Components() - .addSecuritySchemes("bearer-key", - new SecurityScheme().type(SecurityScheme.Type.HTTP) - .scheme("bearer").bearerFormat("JWT"))) - .info(new Info() - .title("Hospital Management API") - .description("Rest API of the Hospital Management application. It contains CRUD functionalities for doctors and patients, as well as scheduling and canceling consultations") - .contact(new Contact() - .name("Developer: Mirna Gama") - .url("https://github.com/MirnaGama"))); - } + .components(new Components() + .addSecuritySchemes("bearer-key", + new SecurityScheme().type(SecurityScheme.Type.HTTP) + .scheme("bearer").bearerFormat("JWT"))) + .info(new Info() + .title("Hospital Management API (Extended Version)") + .description("Extended REST API for Hospital Management. " + + "Includes CRUD operations for doctors and patients, " + + "consultation scheduling/cancellation, and new features " + + "developed on top of the original project by Mirna Gama.") + .contact(new Contact() + .name("Original Developer: Mirna Gama") + .url("https://github.com/MirnaGama")) + .contact(new Contact() + .name("Extended & Maintained by: FlookSP / softwarecraft.tech") + .url("https://gitea.softwarecraft.tech/hospital-management-api"))); + } } diff --git a/src/main/java/com/mirna/hospitalmanagementapi/infra/security/config/WebSecurityConfiguration.java b/src/main/java/com/mirna/hospitalmanagementapi/infra/security/config/WebSecurityConfiguration.java index cb6f827..9c161e7 100644 --- a/src/main/java/com/mirna/hospitalmanagementapi/infra/security/config/WebSecurityConfiguration.java +++ b/src/main/java/com/mirna/hospitalmanagementapi/infra/security/config/WebSecurityConfiguration.java @@ -9,41 +9,163 @@ import org.springframework.security.config.annotation.authentication.configurati import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.http.SessionCreationPolicy; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; -import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import com.mirna.hospitalmanagementapi.infra.security.filters.AuthTokenFilter; +import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; @Configuration @EnableWebSecurity +@EnableMethodSecurity(prePostEnabled = true) public class WebSecurityConfiguration { - @Autowired - private AuthTokenFilter authTokenFilter; - - @Bean - public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { - return http.csrf(csrf -> csrf.disable()) - .sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) - .authorizeHttpRequests(req -> { - req.requestMatchers(HttpMethod.POST, "/api/auth/*").permitAll(); - req.requestMatchers("/v3/api-docs/**").permitAll(); - req.requestMatchers( "/swagger-ui/*").permitAll(); - req.anyRequest().authenticated(); - }) - .addFilterBefore(authTokenFilter, UsernamePasswordAuthenticationFilter.class) - .build(); - } - - @Bean - public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception { - return configuration.getAuthenticationManager(); - } - - @Bean - public PasswordEncoder passwordEncoder() { - return new BCryptPasswordEncoder(); - } -} + @Autowired + private AuthTokenFilter authTokenFilter; + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + return http.csrf(csrf -> csrf.disable()) + .sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + .authorizeHttpRequests(req -> { + // อนุญาตให้เฉพาะ API ที่จำเป็นเท่านั้น + req.requestMatchers(HttpMethod.POST, "/api/auth/login").permitAll(); + req.requestMatchers(HttpMethod.POST, "/api/auth/register-patient").permitAll(); + req.requestMatchers(HttpMethod.POST, "/api/auth/register-doctor-and-link").permitAll(); + req.requestMatchers(HttpMethod.POST, "/api/auth/link-patient-to-user").permitAll(); + req.requestMatchers(HttpMethod.POST, "/api/auth/register-nurse-and-link").permitAll(); + req.requestMatchers("/v3/api-docs/**").permitAll(); + req.requestMatchers( "/swagger-ui/*").permitAll(); + + // กำหนดสิทธิ์สำหรับ Doctor + req.requestMatchers(HttpMethod.POST, "/api/v1.0/doctors").hasRole("ADMIN"); + req.requestMatchers(HttpMethod.PUT, "/api/v1.0/doctors").hasRole("ADMIN"); + req.requestMatchers(HttpMethod.DELETE, "/api/v1.0/doctors/{id}").hasRole("ADMIN"); + req.requestMatchers(HttpMethod.GET, "/api/v1.0/doctors").hasAnyRole("ADMIN", "DOCTOR", "PATIENT"); + req.requestMatchers(HttpMethod.GET, "/api/v1.0/doctors/{id}").hasAnyRole("ADMIN", "DOCTOR", "PATIENT"); + + // กำหนดสิทธิ์สำหรับ Patient + req.requestMatchers(HttpMethod.POST, "/api/v1.0/patients").hasRole("ADMIN"); + req.requestMatchers(HttpMethod.PUT, "/api/v1.0/patients").hasRole("ADMIN"); + req.requestMatchers(HttpMethod.DELETE, "/api/v1.0/patients/{id}").hasRole("ADMIN"); + req.requestMatchers(HttpMethod.GET, "/api/v1.0/patients").hasAnyRole("ADMIN", "DOCTOR", "PATIENT"); + req.requestMatchers(HttpMethod.GET, "/api/v1.0/patients/{id}").hasAnyRole("ADMIN", "DOCTOR", "PATIENT"); + + // กำหนดสิทธิ์สำหรับ Consultation + req.requestMatchers(HttpMethod.POST, "/api/v1.0/consultations").hasAnyRole("PATIENT", "DOCTOR"); + req.requestMatchers(HttpMethod.DELETE, "/api/v1.0/consultations").hasAnyRole("PATIENT", "DOCTOR"); + req.requestMatchers(HttpMethod.GET, "/api/v1.0/consultations/{id}").hasAnyRole("PATIENT", "DOCTOR"); + + // กำหนดสิทธิ์สำหรับ Medical Record + req.requestMatchers(HttpMethod.POST, "/api/v1.0/medical-records").hasAnyRole("ADMIN", "DOCTOR"); + req.requestMatchers(HttpMethod.PUT, "/api/v1.0/medical-records/{id}").hasAnyRole("ADMIN", "DOCTOR"); + req.requestMatchers(HttpMethod.DELETE, "/api/v1.0/medical-records/{id}").hasAnyRole("ADMIN", "DOCTOR"); + + // กำหนดสิทธิ์สำหรับ Inventory Item (โค้ดเดิม) + req.requestMatchers(HttpMethod.POST, "/api/v1.0/inventory/items").hasRole("ADMIN"); + req.requestMatchers(HttpMethod.PUT, "/api/v1.0/inventory/items/{id}").hasRole("ADMIN"); + req.requestMatchers(HttpMethod.DELETE, "/api/v1.0/inventory/items/{id}").hasRole("ADMIN"); + req.requestMatchers(HttpMethod.GET, "/api/v1.0/inventory/items").hasAnyRole("ADMIN", "DOCTOR"); + req.requestMatchers(HttpMethod.GET, "/api/v1.0/inventory/items/{id}").hasAnyRole("ADMIN", "DOCTOR"); + + // กำหนดสิทธิ์สำหรับ Inventory Item Types (โค้ดเดิม) + req.requestMatchers(HttpMethod.POST, "/api/v1.0/inventory/item-types").hasRole("ADMIN"); + req.requestMatchers(HttpMethod.GET, "/api/v1.0/inventory/item-types").hasAnyRole("ADMIN", "DOCTOR"); + + // กำหนดสิทธิ์สำหรับ Inventory Suppliers (โค้ดเดิม) + req.requestMatchers(HttpMethod.POST, "/api/v1.0/inventory/suppliers").hasRole("ADMIN"); + req.requestMatchers(HttpMethod.GET, "/api/v1.0/inventory/suppliers").hasAnyRole("ADMIN", "DOCTOR"); + + // กำหนดสิทธิ์สำหรับ Inventory Transactions (โค้ดเดิม) + req.requestMatchers(HttpMethod.POST, "/api/v1.0/inventory/transactions").hasRole("ADMIN"); + req.requestMatchers(HttpMethod.GET, "/api/v1.0/inventory/transactions").hasAnyRole("ADMIN", "DOCTOR"); + + // กำหนดสิทธิ์สำหรับการอัปเดตข้อมูลสมาชิก (แบบแยก Endpoints) (โค้ดเดิม) + req.requestMatchers(HttpMethod.PUT, "/api/v1.0/users/{id}/username").hasAnyRole("ADMIN", "DOCTOR", "PATIENT"); + req.requestMatchers(HttpMethod.PUT, "/api/v1.0/users/{id}/password").hasAnyRole("ADMIN", "DOCTOR", "PATIENT"); + req.requestMatchers(HttpMethod.PUT, "/api/v1.0/users/{id}/role").hasRole("ADMIN"); + req.requestMatchers(HttpMethod.PATCH, "/api/v1.0/users/{id}/deactivate").hasRole("ADMIN"); + req.requestMatchers(HttpMethod.PATCH, "/api/v1.0/users/{id}/activate").hasRole("ADMIN"); + + // กำหนดสิทธิ์สำหรับ Payment (โค้ดเดิม) + req.requestMatchers(HttpMethod.POST, "/api/v1.0/payments").hasRole("ADMIN"); + req.requestMatchers(HttpMethod.GET, "/api/v1.0/payments/**").hasRole("ADMIN"); + req.requestMatchers(HttpMethod.GET, "/api/v1.0/payments").hasRole("ADMIN"); + + // กำหนดสิทธิ์สำหรับ Staff (โค้ดเดิม) + req.requestMatchers(HttpMethod.POST, "/api/v1.0/staff").hasRole("ADMIN"); + + // กำหนดสิทธิ์สำหรับ Prescriptions + req.requestMatchers(HttpMethod.POST, "/api/v1.0/prescriptions").hasRole("DOCTOR"); + req.requestMatchers(HttpMethod.PUT, "/api/v1.0/prescriptions/{id}").hasAnyRole("ADMIN", "DOCTOR"); + + // กำหนดสิทธิ์สำหรับ Medical Image + req.requestMatchers(HttpMethod.POST, "/api/v1.0/medical-images/upload/{medicalRecordId}").hasRole("DOCTOR"); + + // กำหนดสิทธิ์สำหรับ Lab Results + req.requestMatchers(HttpMethod.POST, "/api/v1.0/lab-results/{medicalRecordId}").hasAnyRole("ADMIN", "DOCTOR"); + + // เพิ่มโค้ดสำหรับ Billing + req.requestMatchers(HttpMethod.POST, "/api/v1.0/billings").hasAnyRole("ADMIN", "RECEPTIONIST"); + req.requestMatchers(HttpMethod.PUT, "/api/v1.0/billings/{id}").hasAnyRole("ADMIN", "RECEPTIONIST"); + req.requestMatchers(HttpMethod.DELETE, "/api/v1.0/billings/{id}").hasRole("ADMIN"); + + // เพิ่มโค้ดสำหรับ Insurance Claims + req.requestMatchers(HttpMethod.POST, "/api/v1.0/insurance-claims").hasAnyRole("ADMIN", "RECEPTIONIST"); + req.requestMatchers(HttpMethod.GET, "/api/v1.0/insurance-claims").hasAnyRole("ADMIN", "RECEPTIONIST"); + req.requestMatchers(HttpMethod.GET, "/api/v1.0/insurance-claims/{id}").hasAnyRole("ADMIN", "RECEPTIONIST"); + req.requestMatchers(HttpMethod.PUT, "/api/v1.0/insurance-claims/{id}").hasAnyRole("ADMIN", "RECEPTIONIST"); + req.requestMatchers(HttpMethod.DELETE, "/api/v1.0/insurance-claims/{id}").hasRole("ADMIN"); + + // กฎสำหรับ Insurance Providers + req.requestMatchers(HttpMethod.POST, "/api/v1.0/insurance-providers").hasAnyRole("ADMIN", "RECEPTIONIST"); + req.requestMatchers(HttpMethod.GET, "/api/v1.0/insurance-providers/**").hasAnyRole("ADMIN", "RECEPTIONIST"); + req.requestMatchers(HttpMethod.PUT, "/api/v1.0/insurance-providers/{id}").hasAnyRole("ADMIN", "RECEPTIONIST"); + req.requestMatchers(HttpMethod.DELETE, "/api/v1.0/insurance-providers/{id}").hasRole("ADMIN"); + + // กำหนดสิทธิ์สำหรับ Nurses + req.requestMatchers(HttpMethod.POST, "/api/v1.0/nurses").hasAnyRole("ADMIN", "RECEPTIONIST"); + req.requestMatchers(HttpMethod.GET, "/api/v1.0/nurses").hasAnyRole("ADMIN", "RECEPTIONIST"); + req.requestMatchers(HttpMethod.GET, "/api/v1.0/nurses/{id}").hasAnyRole("ADMIN", "RECEPTIONIST"); + req.requestMatchers(HttpMethod.PUT, "/api/v1.0/nurses/{id}").hasAnyRole("ADMIN", "RECEPTIONIST"); + req.requestMatchers(HttpMethod.DELETE, "/api/v1.0/nurses/{id}").hasRole("ADMIN"); + + // กำหนดสิทธิ์สำหรับ Operating Rooms + req.requestMatchers(HttpMethod.POST, "/api/v1.0/operating-rooms").hasAnyRole("ADMIN", "RECEPTIONIST"); + req.requestMatchers(HttpMethod.GET, "/api/v1.0/operating-rooms").hasAnyRole("ADMIN", "RECEPTIONIST"); + req.requestMatchers(HttpMethod.GET, "/api/v1.0/operating-rooms/{id}").hasAnyRole("ADMIN", "RECEPTIONIST"); + req.requestMatchers(HttpMethod.PUT, "/api/v1.0/operating-rooms/{id}").hasAnyRole("ADMIN", "RECEPTIONIST"); + req.requestMatchers(HttpMethod.DELETE, "/api/v1.0/operating-rooms/{id}").hasRole("ADMIN"); + + // กำหนดสิทธิ์สำหรับ Medical Equipments + req.requestMatchers(HttpMethod.POST, "/api/v1.0/medical-equipments").hasAnyRole("ADMIN", "RECEPTIONIST"); + req.requestMatchers(HttpMethod.GET, "/api/v1.0/medical-equipments").hasAnyRole("ADMIN", "RECEPTIONIST"); + req.requestMatchers(HttpMethod.GET, "/api/v1.0/medical-equipments/{id}").hasAnyRole("ADMIN", "RECEPTIONIST"); + req.requestMatchers(HttpMethod.PUT, "/api/v1.0/medical-equipments/{id}").hasAnyRole("ADMIN", "RECEPTIONIST"); + req.requestMatchers(HttpMethod.DELETE, "/api/v1.0/medical-equipments/{id}").hasRole("ADMIN"); + + // เพิ่มโค้ดสำหรับการจัดการตารางเวลา (Schedules) + req.requestMatchers(HttpMethod.POST, "/api/v1.0/nurse-schedules").hasAnyRole("ADMIN", "RECEPTIONIST"); + req.requestMatchers(HttpMethod.GET, "/api/v1.0/nurse-schedules/**").hasAnyRole("ADMIN", "RECEPTIONIST"); + req.requestMatchers(HttpMethod.POST, "/api/v1.0/operating-room-schedules").hasAnyRole("ADMIN", "RECEPTIONIST"); + req.requestMatchers(HttpMethod.GET, "/api/v1.0/operating-room-schedules/**").hasAnyRole("ADMIN", "RECEPTIONIST"); + req.requestMatchers(HttpMethod.POST, "/api/v1.0/medical-equipment-schedules").hasAnyRole("ADMIN", "RECEPTIONIST"); + req.requestMatchers(HttpMethod.GET, "/api/v1.0/medical-equipment-schedules/**").hasAnyRole("ADMIN", "RECEPTIONIST"); + + // กฎสำหรับ Report Controller + req.requestMatchers(HttpMethod.GET, "/api/v1.0/reports/financial-overview").hasAnyRole("ADMIN", "RECEPTIONIST"); + req.requestMatchers(HttpMethod.GET, "/api/v1.0/reports/inventory/low-stock").hasAnyRole("ADMIN", "RECEPTIONIST"); + req.requestMatchers(HttpMethod.GET, "/api/v1.0/reports/appointments").hasAnyRole("ADMIN", "RECEPTIONIST"); + + // สำหรับ Request ที่เหลือทั้งหมด ต้องมีการยืนยันตัวตน + req.anyRequest().authenticated(); + }) + .addFilterBefore(authTokenFilter, UsernamePasswordAuthenticationFilter.class) + .build(); + } + + @Bean + public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception { + return configuration.getAuthenticationManager(); + } +} \ No newline at end of file diff --git a/src/main/resources/application-test.properties b/src/main/resources/application-test.properties index 577f944..257822b 100644 --- a/src/main/resources/application-test.properties +++ b/src/main/resources/application-test.properties @@ -1,3 +1,6 @@ +# ??? Flyway ????????? schema ???????????????????????????????????? migrate ?????? +spring.flyway.baseline-on-migrate=true + # datasource spring.datasource.driverClassName=org.h2.Driver spring.datasource.url=jdbc:h2:mem:testdb @@ -6,7 +9,7 @@ spring.datasource.password= # jpa spring.jpa.database-platform=org.hibernate.dialect.H2Dialect -spring.jpa.hibernate.ddl-auto=update +spring.jpa.hibernate.ddl-auto=create-drop spring.jpa.properties.hibernate.show_sql=true spring.jpa.properties.hibernate.format_sql=true @@ -14,4 +17,15 @@ spring.jpa.properties.hibernate.format_sql=true spring.h2.console.enabled=true spring.h2.console.path=/h2-console -api.security.token.secret=${JWT_SECRET:S3CR3T} \ No newline at end of file +api.security.token.secret=${JWT_SECRET:S3CR3T} + +# ????????? Flyway ?????????????? ???????????????????????? +spring.flyway.enabled=false + + +#logging.level.com.mirna.hospitalmanagementapi=DEBUG +# --- MinIO --- +minio.url=https://minio.softwarecraft.tech +minio.access-key=UQ9MwiWu4t7ljWh5U1RL +minio.secret-key=TQea1jSA4JWEzwX0jHFhQB4YVG1fepWYbA3ZlY27 +minio.bucket-name=medical-images \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 24a2b5b..2fd58c1 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,5 +1,23 @@ -spring.datasource.url=jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_DATABASE} -spring.datasource.username=${MYSQL_USER} -spring.datasource.password=${MYSQL_PASSWORD} -spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver +# --- PostgreSQL config --- +spring.datasource.url=jdbc:postgresql://${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB} +spring.datasource.username=${POSTGRES_USER} +spring.datasource.password=${POSTGRES_PASSWORD} + +spring.jpa.hibernate.ddl-auto=update + +# --- JWT Secret --- api.security.token.secret=${JWT_SECRET} + +# --- Jackson Time Zone --- +spring.jackson.deserialization.adjust-dates-to-context-time-zone=false +spring.jackson.time-zone=Asia/Bangkok + +# --- MinIO --- +minio.url=${MINIO_URL} +minio.access-key=${MINIO_ACCESS} +minio.secret-key=${MINIO_SECRET} +minio.bucket-name=${MINIO_BUCKET} +# ???????? reverse proxy +# ??????? Spring Boot ?????? header ??? proxy (X-Forwarded-Proto, X-Forwarded-Host, etc.) +# ??????? URL / scheme ??????? HTTPS ??? header ???? ? +server.forward-headers-strategy=framework diff --git a/src/main/resources/db/migration/V10__alter-table-add-foreign-key-patients.sql b/src/main/resources/db/migration/V10__alter-table-add-foreign-key-patients.sql new file mode 100644 index 0000000..6071318 --- /dev/null +++ b/src/main/resources/db/migration/V10__alter-table-add-foreign-key-patients.sql @@ -0,0 +1,2 @@ +ALTER TABLE patients ADD user_id BIGINT; +ALTER TABLE patients ADD CONSTRAINT fk_patients_users FOREIGN KEY (user_id) REFERENCES users(id); \ No newline at end of file diff --git a/src/main/resources/db/migration/V11__insert-initial-admin.sql b/src/main/resources/db/migration/V11__insert-initial-admin.sql new file mode 100644 index 0000000..3e011fa --- /dev/null +++ b/src/main/resources/db/migration/V11__insert-initial-admin.sql @@ -0,0 +1,2 @@ +INSERT INTO users (login, password, role) +VALUES ('admin@hospital.com', '$2a$10$C82vF24mE/nL6r2V5sTfE.7j2k3k4k5k6k7k8k9k0k1k2k', 'ROLE_ADMIN'); \ No newline at end of file diff --git a/src/main/resources/db/migration/V12__create_inventory_tables.sql b/src/main/resources/db/migration/V12__create_inventory_tables.sql new file mode 100644 index 0000000..54729a5 --- /dev/null +++ b/src/main/resources/db/migration/V12__create_inventory_tables.sql @@ -0,0 +1,65 @@ +-- V12__create_inventory_tables.sql + +-- Flyway Migration script to create all necessary tables for the inventory management system. + +-- ----------------------------------------------------------- +-- 1. Create table for inventory item types (e.g., Medicine, Equipment) +-- ----------------------------------------------------------- +CREATE TABLE inventory_item_types ( + id UUID PRIMARY KEY, + type_name VARCHAR(50) UNIQUE NOT NULL, + description TEXT, + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP DEFAULT NOW() +); + +-- ----------------------------------------------------------- +-- 2. Create table for inventory suppliers +-- ----------------------------------------------------------- +CREATE TABLE inventory_suppliers ( + id UUID PRIMARY KEY, + supplier_name VARCHAR(100) NOT NULL, + contact_info TEXT, + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP DEFAULT NOW() +); + +-- ----------------------------------------------------------- +-- 3. Create the main inventory items table +-- This table stores all items like medicine, supplies, and equipment. +-- ----------------------------------------------------------- +CREATE TABLE inventory_items ( + id UUID PRIMARY KEY, + item_type_id UUID REFERENCES inventory_item_types(id), + supplier_id UUID REFERENCES inventory_suppliers(id), + item_name VARCHAR(255) NOT NULL, + description TEXT, + current_stock INTEGER DEFAULT 0, + unit_of_measure VARCHAR(20) NOT NULL, + unit_price DECIMAL(10, 2), + reorder_level INTEGER DEFAULT 10, + expiration_date DATE, + location VARCHAR(100), + is_active BOOLEAN DEFAULT TRUE, + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP DEFAULT NOW() +); + +-- ----------------------------------------------------------- +-- 4. Create the inventory transactions table +-- This table logs all stock movements (in, out, adjustments). +-- ----------------------------------------------------------- +CREATE TABLE inventory_transactions ( + id UUID PRIMARY KEY, + item_id UUID REFERENCES inventory_items(id), + transaction_type VARCHAR(20) NOT NULL, -- 'IN', 'OUT', 'ADJUSTMENT' + quantity INTEGER NOT NULL, + transaction_date TIMESTAMP DEFAULT NOW(), + related_document_id UUID, + notes TEXT, + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP DEFAULT NOW() +); + +-- Note: It's good practice to add indexes to improve query performance, +-- but that can be done in a separate migration file for clarity. \ No newline at end of file diff --git a/src/main/resources/db/migration/V13__create_medical_records_table.sql b/src/main/resources/db/migration/V13__create_medical_records_table.sql new file mode 100644 index 0000000..c9256f2 --- /dev/null +++ b/src/main/resources/db/migration/V13__create_medical_records_table.sql @@ -0,0 +1,25 @@ +-- V13__create_medical_records_table.sql + +-- Flyway Migration script to create the medical_records table. + +-- ----------------------------------------------------------- +-- 1. Create the main medical records table +-- This table stores a history of all patient consultations, diagnoses, and treatments. +-- ----------------------------------------------------------- +CREATE TABLE medical_records ( + id BIGINT NOT NULL GENERATED ALWAYS AS IDENTITY, + patient_id BIGINT REFERENCES patients(id), + doctor_id BIGINT REFERENCES doctors(id), + consultation_id BIGINT, + diagnosis TEXT NOT NULL, + treatment TEXT NOT NULL, + notes TEXT, + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP DEFAULT NOW(), + PRIMARY KEY(id) +); + +-- Adding indexes on foreign key columns will speed up lookups. +CREATE INDEX idx_medical_records_patient_id ON medical_records(patient_id); +CREATE INDEX idx_medical_records_doctor_id ON medical_records(doctor_id); +-- CREATE INDEX idx_medical_records_consultation_id ON medical_records(consultation_id); \ No newline at end of file diff --git a/src/main/resources/db/migration/V14__update_admin_password.sql b/src/main/resources/db/migration/V14__update_admin_password.sql new file mode 100644 index 0000000..a2ad9ca --- /dev/null +++ b/src/main/resources/db/migration/V14__update_admin_password.sql @@ -0,0 +1,3 @@ +UPDATE users +SET password = '$2a$10$DU9h.23LNM1ceQjJltcPm./IV020yjQQ2UVsTxgX55RCf38QSyMoq' +WHERE login = 'admin@hospital.com'; \ No newline at end of file diff --git a/src/main/resources/db/migration/V15__insert-initial-admin-fix.sql b/src/main/resources/db/migration/V15__insert-initial-admin-fix.sql new file mode 100644 index 0000000..c9c30b8 --- /dev/null +++ b/src/main/resources/db/migration/V15__insert-initial-admin-fix.sql @@ -0,0 +1,2 @@ +INSERT INTO users (login, password, role) +VALUES ('admin@softwarecraft.tech', '$2a$10$DU9h.23LNM1ceQjJltcPm./IV020yjQQ2UVsTxgX55RCf38QSyMoq', 'ROLE_ADMIN'); \ No newline at end of file diff --git a/src/main/resources/db/migration/V16__add-enabled-column-to-users.sql b/src/main/resources/db/migration/V16__add-enabled-column-to-users.sql new file mode 100644 index 0000000..1c651e1 --- /dev/null +++ b/src/main/resources/db/migration/V16__add-enabled-column-to-users.sql @@ -0,0 +1 @@ +ALTER TABLE users ADD COLUMN enabled BOOLEAN NOT NULL DEFAULT TRUE; \ No newline at end of file diff --git a/src/main/resources/db/migration/V17__create-payments-table.sql b/src/main/resources/db/migration/V17__create-payments-table.sql new file mode 100644 index 0000000..a04c644 --- /dev/null +++ b/src/main/resources/db/migration/V17__create-payments-table.sql @@ -0,0 +1,11 @@ +CREATE TABLE payments ( + id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + consultation_id BIGINT, + medical_record_id BIGINT, + amount DECIMAL(10, 2) NOT NULL, + payment_method VARCHAR(50) NOT NULL, + payment_date TIMESTAMP WITH TIME ZONE NOT NULL, + + CONSTRAINT fk_consultation_id FOREIGN KEY (consultation_id) REFERENCES consultations(id), + CONSTRAINT fk_medical_record_id FOREIGN KEY (medical_record_id) REFERENCES medical_records(id) +); \ No newline at end of file diff --git a/src/main/resources/db/migration/V18__create_staff_table.sql b/src/main/resources/db/migration/V18__create_staff_table.sql new file mode 100644 index 0000000..29ef144 --- /dev/null +++ b/src/main/resources/db/migration/V18__create_staff_table.sql @@ -0,0 +1,8 @@ +CREATE TABLE staff ( + id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + name VARCHAR(255) NOT NULL, + email VARCHAR(255) NOT NULL UNIQUE, + telephone VARCHAR(255) NOT NULL UNIQUE, + user_id BIGINT UNIQUE, + CONSTRAINT fk_staff_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE +); \ No newline at end of file diff --git a/src/main/resources/db/migration/V19__create_doctor_schedules_table.sql b/src/main/resources/db/migration/V19__create_doctor_schedules_table.sql new file mode 100644 index 0000000..bcde814 --- /dev/null +++ b/src/main/resources/db/migration/V19__create_doctor_schedules_table.sql @@ -0,0 +1,9 @@ +CREATE TABLE doctor_schedules ( + id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + doctor_id BIGINT NOT NULL, + day_of_week VARCHAR(15) NOT NULL, + start_time TIME NOT NULL, + end_time TIME NOT NULL, + CONSTRAINT fk_doctor_schedules_doctor_id FOREIGN KEY (doctor_id) REFERENCES doctors(id) ON DELETE CASCADE, + CONSTRAINT uc_doctor_schedule UNIQUE (doctor_id, day_of_week, start_time) +); \ No newline at end of file diff --git a/src/main/resources/db/migration/V1__create-table-doctors.sql b/src/main/resources/db/migration/V1__create-table-doctors.sql index 0c517ba..721a16e 100644 --- a/src/main/resources/db/migration/V1__create-table-doctors.sql +++ b/src/main/resources/db/migration/V1__create-table-doctors.sql @@ -1,18 +1,18 @@ create table doctors ( - id bigint not null auto_increment, - name varchar(100) not null, - email varchar(100) not null unique, - crm varchar(6) not null unique, - speciality varchar(100) not null, - telephone varchar(20) not null, - street varchar(100) not null, - neighborhood varchar(100) not null, - zip_code varchar(9) not null, - additional_details varchar(100), - house_number varchar(20), - state varchar(2) not null, - city varchar(100) not null, + id bigint not null generated always as identity, + name varchar(100) not null, + email varchar(100) not null unique, + crm varchar(6) not null unique, + speciality varchar(100) not null, + telephone varchar(20) not null, + street varchar(100) not null, + neighborhood varchar(100) not null, + zip_code varchar(9) not null, + additional_details varchar(100), + house_number varchar(20), + state varchar(2) not null, + city varchar(100) not null, - primary key(id) -); + primary key(id) +); \ No newline at end of file diff --git a/src/main/resources/db/migration/V20__create_prescriptions_table.sql b/src/main/resources/db/migration/V20__create_prescriptions_table.sql new file mode 100644 index 0000000..b4146b7 --- /dev/null +++ b/src/main/resources/db/migration/V20__create_prescriptions_table.sql @@ -0,0 +1,9 @@ +CREATE TABLE prescriptions ( + id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + medical_record_id BIGINT NOT NULL, + inventory_item_id UUID NOT NULL, + quantity INT NOT NULL CHECK (quantity >= 1), + instructions TEXT NOT NULL, + CONSTRAINT fk_medical_record FOREIGN KEY (medical_record_id) REFERENCES medical_records(id) ON DELETE CASCADE, + CONSTRAINT fk_inventory_item FOREIGN KEY (inventory_item_id) REFERENCES inventory_items(id) +); \ No newline at end of file diff --git a/src/main/resources/db/migration/V21__Add_Staff_Roles_to_Users_Table.sql b/src/main/resources/db/migration/V21__Add_Staff_Roles_to_Users_Table.sql new file mode 100644 index 0000000..511536f --- /dev/null +++ b/src/main/resources/db/migration/V21__Add_Staff_Roles_to_Users_Table.sql @@ -0,0 +1,7 @@ +-- ลบเงื่อนไข check constraint เก่า หากมีอยู่ สามารถทำงานได้อย่างปลอดภัยทั้งในสภาพแวดล้อมการพัฒนา (H2) และสภาพแวดล้อมจริง (PostgreSQL) +ALTER TABLE users DROP CONSTRAINT IF EXISTS users_role_check; + +-- เพิ่มเงื่อนไข check constraint ใหม่ที่รวมบทบาท Staff ทั้งหมด +-- โดยตรวจสอบว่าค่าในคอลัมน์ 'role' ต้องอยู่ในรายการที่กำหนด +ALTER TABLE users ADD CONSTRAINT users_role_check + CHECK (role IN ('ROLE_ADMIN', 'ROLE_DOCTOR', 'ROLE_PATIENT', 'ROLE_RECEPTIONIST', 'ROLE_PHARMACIST', 'ROLE_NURSE')); \ No newline at end of file diff --git a/src/main/resources/db/migration/V22__Add_timestamps_to_prescriptions_table.sql b/src/main/resources/db/migration/V22__Add_timestamps_to_prescriptions_table.sql new file mode 100644 index 0000000..b8d2b4c --- /dev/null +++ b/src/main/resources/db/migration/V22__Add_timestamps_to_prescriptions_table.sql @@ -0,0 +1,2 @@ +ALTER TABLE prescriptions ADD COLUMN created_at TIMESTAMP WITH TIME ZONE; +ALTER TABLE prescriptions ADD COLUMN updated_at TIMESTAMP WITH TIME ZONE; \ No newline at end of file diff --git a/src/main/resources/db/migration/V23__ALTER_prescriptions_table.sql b/src/main/resources/db/migration/V23__ALTER_prescriptions_table.sql new file mode 100644 index 0000000..b9c0275 --- /dev/null +++ b/src/main/resources/db/migration/V23__ALTER_prescriptions_table.sql @@ -0,0 +1,13 @@ +-- Drop the old table to replace it with the new schema. +DROP TABLE IF EXISTS prescriptions; + +-- Create the new prescriptions table with a UUID primary key. +CREATE TABLE prescriptions ( + id UUID PRIMARY KEY, + medical_record_id BIGINT NOT NULL, + inventory_item_id UUID NOT NULL, + quantity INT NOT NULL CHECK (quantity >= 1), + instructions TEXT NOT NULL, + CONSTRAINT fk_medical_record FOREIGN KEY (medical_record_id) REFERENCES medical_records(id) ON DELETE CASCADE, + CONSTRAINT fk_inventory_item FOREIGN KEY (inventory_item_id) REFERENCES inventory_items(id) +); diff --git a/src/main/resources/db/migration/V24__create_medical_image_lab_result_tables.sql b/src/main/resources/db/migration/V24__create_medical_image_lab_result_tables.sql new file mode 100644 index 0000000..3e10b6f --- /dev/null +++ b/src/main/resources/db/migration/V24__create_medical_image_lab_result_tables.sql @@ -0,0 +1,23 @@ +-- สร้างตาราง Medical Images หากยังไม่มี +CREATE TABLE IF NOT EXISTS medical_images ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + file_name VARCHAR(255) NOT NULL, + image_type VARCHAR(50) NOT NULL, + notes TEXT, + upload_date TIMESTAMP NOT NULL, + medical_record_id BIGINT, + CONSTRAINT fk_medical_images_medical_records FOREIGN KEY (medical_record_id) REFERENCES medical_records(id) ON DELETE CASCADE + ); + +-- สร้างตาราง Lab Results หากยังไม่มี +CREATE TABLE IF NOT EXISTS lab_results ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + test_name VARCHAR(255) NOT NULL, + result_value DOUBLE PRECISION, + unit VARCHAR(50), + reference_range VARCHAR(255), + status VARCHAR(50) NOT NULL, + result_date TIMESTAMP NOT NULL, + medical_record_id BIGINT, + CONSTRAINT fk_lab_results_medical_records FOREIGN KEY (medical_record_id) REFERENCES medical_records(id) ON DELETE CASCADE + ); \ No newline at end of file diff --git a/src/main/resources/db/migration/V25__Create_insurance_providers_table.sql b/src/main/resources/db/migration/V25__Create_insurance_providers_table.sql new file mode 100644 index 0000000..79fb1bd --- /dev/null +++ b/src/main/resources/db/migration/V25__Create_insurance_providers_table.sql @@ -0,0 +1,7 @@ +CREATE TABLE IF NOT EXISTS insurance_providers ( + id BIGSERIAL PRIMARY KEY, + provider_name VARCHAR(255) NOT NULL, + contact_person VARCHAR(255), + contact_email VARCHAR(255), + phone_number VARCHAR(20) +); \ No newline at end of file diff --git a/src/main/resources/db/migration/V26__Create_billing_tables.sql b/src/main/resources/db/migration/V26__Create_billing_tables.sql new file mode 100644 index 0000000..b129627 --- /dev/null +++ b/src/main/resources/db/migration/V26__Create_billing_tables.sql @@ -0,0 +1,18 @@ +CREATE TABLE IF NOT EXISTS billings ( + id BIGSERIAL PRIMARY KEY, + medical_record_id BIGINT NOT NULL, + issue_date TIMESTAMP NOT NULL, + total_amount DECIMAL(10, 2) NOT NULL, + payment_status VARCHAR(50) NOT NULL, + FOREIGN KEY (medical_record_id) REFERENCES medical_records(id) +); + +CREATE TABLE IF NOT EXISTS billable_items ( + id BIGSERIAL PRIMARY KEY, + billing_id BIGINT NOT NULL, + item_description VARCHAR(255) NOT NULL, + quantity INT NOT NULL, + unit_price DECIMAL(10, 2) NOT NULL, + item_amount DECIMAL(10, 2) NOT NULL, + FOREIGN KEY (billing_id) REFERENCES billings(id) +); \ No newline at end of file diff --git a/src/main/resources/db/migration/V27__Create_insurance_claims_table.sql b/src/main/resources/db/migration/V27__Create_insurance_claims_table.sql new file mode 100644 index 0000000..f8870c7 --- /dev/null +++ b/src/main/resources/db/migration/V27__Create_insurance_claims_table.sql @@ -0,0 +1,11 @@ +CREATE TABLE IF NOT EXISTS insurance_claims ( + id BIGSERIAL PRIMARY KEY, + billing_id BIGINT UNIQUE NOT NULL, + insurance_provider_id BIGINT NOT NULL, + claim_number VARCHAR(255) NOT NULL, + submission_date TIMESTAMP NOT NULL, + approved_amount DECIMAL(10, 2), + claim_status VARCHAR(50) NOT NULL, + FOREIGN KEY (billing_id) REFERENCES billings(id), + FOREIGN KEY (insurance_provider_id) REFERENCES insurance_providers(id) +); \ No newline at end of file diff --git a/src/main/resources/db/migration/V28__create_resource_tables.sql b/src/main/resources/db/migration/V28__create_resource_tables.sql new file mode 100644 index 0000000..a1e3f71 --- /dev/null +++ b/src/main/resources/db/migration/V28__create_resource_tables.sql @@ -0,0 +1,21 @@ +CREATE TABLE IF NOT EXISTS nurses ( + id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + name VARCHAR(255) NOT NULL, + license_number VARCHAR(255) NOT NULL UNIQUE, + email VARCHAR(255) NOT NULL UNIQUE, + telephone VARCHAR(255) NOT NULL, + shift_type VARCHAR(50) NOT NULL + ); + +CREATE TABLE IF NOT EXISTS operating_rooms ( + id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + room_number VARCHAR(255) NOT NULL UNIQUE, + is_available BOOLEAN NOT NULL + ); + +CREATE TABLE IF NOT EXISTS medical_equipments ( + id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + equipment_name VARCHAR(255) NOT NULL, + serial_number VARCHAR(255) NOT NULL UNIQUE, + is_available BOOLEAN NOT NULL + ); \ No newline at end of file diff --git a/src/main/resources/db/migration/V29__create_resource_schedules_tables.sql b/src/main/resources/db/migration/V29__create_resource_schedules_tables.sql new file mode 100644 index 0000000..16da278 --- /dev/null +++ b/src/main/resources/db/migration/V29__create_resource_schedules_tables.sql @@ -0,0 +1,26 @@ +CREATE TABLE IF NOT EXISTS nurse_schedules ( + id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + nurse_id BIGINT NOT NULL, + start_time TIMESTAMP NOT NULL, + end_time TIMESTAMP NOT NULL, + notes TEXT, + FOREIGN KEY (nurse_id) REFERENCES nurses(id) + ); + +CREATE TABLE IF NOT EXISTS operating_room_schedules ( + id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + room_id BIGINT NOT NULL, + start_time TIMESTAMP NOT NULL, + end_time TIMESTAMP NOT NULL, + purpose TEXT, + FOREIGN KEY (room_id) REFERENCES operating_rooms(id) + ); + +CREATE TABLE IF NOT EXISTS medical_equipment_schedules ( + id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + equipment_id BIGINT NOT NULL, + start_time TIMESTAMP NOT NULL, + end_time TIMESTAMP NOT NULL, + usage_details TEXT, + FOREIGN KEY (equipment_id) REFERENCES medical_equipments(id) + ); \ No newline at end of file diff --git a/src/main/resources/db/migration/V30__update_equipment_tables.sql b/src/main/resources/db/migration/V30__update_equipment_tables.sql new file mode 100644 index 0000000..45f8606 --- /dev/null +++ b/src/main/resources/db/migration/V30__update_equipment_tables.sql @@ -0,0 +1,44 @@ +-- Migration script to remove the medical_equipments table and correctly link +-- medical_equipment_schedules to the inventory_items table. + +-- ----------------------------------------------------------- +-- 1. Remove the foreign key constraint from medical_equipment_schedules. +-- ----------------------------------------------------------- +-- This is a crucial step to allow us to drop the parent table. +ALTER TABLE medical_equipment_schedules +DROP CONSTRAINT IF EXISTS medical_equipment_schedules_equipment_id_fkey; + + +-- ----------------------------------------------------------- +-- 2. Drop the medical_equipments table. +-- ----------------------------------------------------------- +-- This table is no longer needed after all related data and constraints are handled. +DROP TABLE IF EXISTS medical_equipments; + + +-- ----------------------------------------------------------- +-- 3. Modify the medical_equipment_schedules table to use UUIDs. +-- ----------------------------------------------------------- +-- Since there is no data, we can drop and re-add the column safely. +ALTER TABLE medical_equipment_schedules +DROP COLUMN equipment_id; + +ALTER TABLE medical_equipment_schedules + ADD COLUMN equipment_id UUID NOT NULL; + + +-- ----------------------------------------------------------- +-- 4. Add the serial_number column to inventory_items. +-- ----------------------------------------------------------- +-- This column is unique to medical equipment and is needed for the new merged table. +ALTER TABLE inventory_items + ADD COLUMN serial_number VARCHAR(255) UNIQUE; + + +-- ----------------------------------------------------------- +-- 5. Add the new Foreign Key constraint. +-- ----------------------------------------------------------- +-- This ensures that scheduled items always exist in the inventory. +ALTER TABLE medical_equipment_schedules + ADD CONSTRAINT fk_equipment_schedules + FOREIGN KEY (equipment_id) REFERENCES inventory_items(id); \ No newline at end of file diff --git a/src/main/resources/db/migration/V3__alter-table-add-column-doctors.sql b/src/main/resources/db/migration/V3__alter-table-add-column-doctors.sql index be5e62a..ad66372 100644 --- a/src/main/resources/db/migration/V3__alter-table-add-column-doctors.sql +++ b/src/main/resources/db/migration/V3__alter-table-add-column-doctors.sql @@ -1,3 +1,2 @@ -alter table doctors add active tinyint; -update doctors set active = 1; - +alter table doctors add active boolean; +update doctors set active = true; \ No newline at end of file diff --git a/src/main/resources/db/migration/V4__create-table-patients.sql b/src/main/resources/db/migration/V4__create-table-patients.sql index 0fe62e3..0778706 100644 --- a/src/main/resources/db/migration/V4__create-table-patients.sql +++ b/src/main/resources/db/migration/V4__create-table-patients.sql @@ -1,18 +1,18 @@ create table patients ( - id bigint not null auto_increment, - name varchar(100) not null, - email varchar(100) not null unique, - cpf varchar(11) not null unique, - telephone varchar(20) not null, - street varchar(100) not null, - neighborhood varchar(100) not null, - zip_code varchar(9) not null, - additional_details varchar(100), - house_number varchar(20), - state varchar(2) not null, - city varchar(100) not null, - _active tinyint not null, + id bigint not null generated always as identity, + name varchar(100) not null, + email varchar(100) not null unique, + cpf varchar(11) not null unique, + telephone varchar(20) not null, + street varchar(100) not null, + neighborhood varchar(100) not null, + zip_code varchar(9) not null, + additional_details varchar(100), + house_number varchar(20), + state varchar(2) not null, + city varchar(100) not null, + _active boolean not null, - primary key(id) -); + primary key(id) +); \ No newline at end of file diff --git a/src/main/resources/db/migration/V5__create-table-users.sql b/src/main/resources/db/migration/V5__create-table-users.sql index f5cd354..9455f21 100644 --- a/src/main/resources/db/migration/V5__create-table-users.sql +++ b/src/main/resources/db/migration/V5__create-table-users.sql @@ -1,8 +1,8 @@ create table users ( - id bigint not null auto_increment, - login varchar(100) not null unique, - password varchar(255) not null, - - primary key(id) -) + id bigint not null generated always as identity, + login varchar(100) not null unique, + password varchar(255) not null, + + primary key(id) +); \ No newline at end of file diff --git a/src/main/resources/db/migration/V6__create-table-consultations.sql b/src/main/resources/db/migration/V6__create-table-consultations.sql index ee5fffa..6b12c71 100644 --- a/src/main/resources/db/migration/V6__create-table-consultations.sql +++ b/src/main/resources/db/migration/V6__create-table-consultations.sql @@ -1,12 +1,11 @@ create table consultations ( - id bigint not null auto_increment, - patient_id bigint not null, - doctor_id bigint not null, - consultation_date datetime not null, - - primary key(id), - constraint fk_doctor_id foreign key(doctor_id) references doctors(id), - constraint fk_patient_id foreign key(patient_id) references patients(id) -); + id bigint not null generated always as identity, + patient_id bigint not null, + doctor_id bigint not null, + consultation_date timestamp not null, + primary key(id), + constraint fk_doctor_id foreign key(doctor_id) references doctors(id), + constraint fk_patient_id foreign key(patient_id) references patients(id) +); \ No newline at end of file diff --git a/src/main/resources/db/migration/V7__alter-table-add-column-consultations.sql b/src/main/resources/db/migration/V7__alter-table-add-column-consultations.sql index f1a52d3..641804f 100644 --- a/src/main/resources/db/migration/V7__alter-table-add-column-consultations.sql +++ b/src/main/resources/db/migration/V7__alter-table-add-column-consultations.sql @@ -1,3 +1,3 @@ -alter table consultations add column canceled tinyint; -update consultations set canceled = 0; -alter table consultations add column reason_cancellation varchar(100); +alter table consultations add column canceled boolean; +update consultations set canceled = false; +alter table consultations add column reason_cancellation varchar(100); \ No newline at end of file diff --git a/src/main/resources/db/migration/V8__alter-table-add-column-users.sql b/src/main/resources/db/migration/V8__alter-table-add-column-users.sql new file mode 100644 index 0000000..6384fcc --- /dev/null +++ b/src/main/resources/db/migration/V8__alter-table-add-column-users.sql @@ -0,0 +1 @@ +ALTER TABLE users ADD COLUMN role VARCHAR(50) NOT NULL; \ No newline at end of file diff --git a/src/main/resources/db/migration/V9__alter-table-add-foreign-key-doctors.sql b/src/main/resources/db/migration/V9__alter-table-add-foreign-key-doctors.sql new file mode 100644 index 0000000..ac30bf8 --- /dev/null +++ b/src/main/resources/db/migration/V9__alter-table-add-foreign-key-doctors.sql @@ -0,0 +1,6 @@ +ALTER TABLE doctors + ADD COLUMN user_id BIGINT; + +ALTER TABLE doctors + ADD CONSTRAINT fk_doctors_user_id + FOREIGN KEY (user_id) REFERENCES users(id); \ No newline at end of file diff --git a/src/test/java/com/mirna/hospitalmanagementapi/unit/application/controllers/AuthenticationControllerTest.java b/src/test/java/com/mirna/hospitalmanagementapi/unit/application/controllers/AuthenticationControllerTest.java index e9503b1..542779f 100644 --- a/src/test/java/com/mirna/hospitalmanagementapi/unit/application/controllers/AuthenticationControllerTest.java +++ b/src/test/java/com/mirna/hospitalmanagementapi/unit/application/controllers/AuthenticationControllerTest.java @@ -18,10 +18,13 @@ import org.springframework.test.web.servlet.result.MockMvcResultMatchers; import com.fasterxml.jackson.databind.ObjectMapper; import com.mirna.hospitalmanagementapi.domain.dtos.auth.UserDTO; +import com.mirna.hospitalmanagementapi.domain.dtos.auth.PatientRegistrationDTO; // Import DTO ใหม่ +import com.mirna.hospitalmanagementapi.domain.dtos.AddressDTO; +import com.mirna.hospitalmanagementapi.domain.dtos.patient.PatientDTO; +import com.mirna.hospitalmanagementapi.domain.entities.auth.User; // Import User entity /** - * - * @author Mirna Gama + * * @author Mirna Gama * @version 1.0 */ @SpringBootTest @@ -31,45 +34,54 @@ import com.mirna.hospitalmanagementapi.domain.dtos.auth.UserDTO; @ActiveProfiles("test") public class AuthenticationControllerTest { - @Autowired - private MockMvc mockMvc; + @Autowired + private MockMvc mockMvc; - @Autowired - private ObjectMapper mapper; + @Autowired + private ObjectMapper mapper; - - /** - * Post the user registration - */ - @Test - @Order(1) - @DisplayName("Should post the user registration and return http status ok") - public void testPostRegister() throws Exception { + // เพิ่มตัวแปรเพื่อเก็บข้อมูลผู้ใช้สำหรับใช้ในการทดสอบอื่น ๆ + private static UserDTO testUser; - UserDTO userDTO = new UserDTO("test", "password"); - - String userDTOContent = mapper.writeValueAsString(userDTO); + /** + * Post the user registration + */ + @Test + @Order(1) + @DisplayName("Should post the user registration and return http status ok") + public void testPostRegister() throws Exception { - mockMvc.perform(MockMvcRequestBuilders.post("/api/auth/register").contentType(MediaType.APPLICATION_JSON) - .characterEncoding("UTF-8").content(userDTOContent)) - .andExpect(MockMvcResultMatchers.status().isOk()).andDo(MockMvcResultHandlers.print()); - } + // สร้าง AddressDTO และ PatientDTO ก่อน + AddressDTO addressDTO = new AddressDTO("street", "neighborhood", "12345", "city", "CA", "additional", "123"); + PatientDTO patientDTO = new PatientDTO("Test User", "test@test.com", "12345678901", "123456789", addressDTO); - /** - * Post the user login - */ - @Test - @Order(2) - @DisplayName("Should post the user login and return http status ok") - public void testPostLogin() throws Exception { + // สร้าง PatientRegistrationDTO ที่มีข้อมูลครบถ้วน + PatientRegistrationDTO registrationDTO = new PatientRegistrationDTO("test", "password", patientDTO); - UserDTO userDTO = new UserDTO("test", "password"); - - String userDTOContent = mapper.writeValueAsString(userDTO); + // แปลงเป็น JSON + String registrationDTOContent = mapper.writeValueAsString(registrationDTO); - mockMvc.perform(MockMvcRequestBuilders.post("/api/auth/login").contentType(MediaType.APPLICATION_JSON) - .characterEncoding("UTF-8").content(userDTOContent)) - .andExpect(MockMvcResultMatchers.status().isOk()).andDo(MockMvcResultHandlers.print()); - } - -} + // เรียกใช้ Endpoint ใหม่: /api/auth/register-patient + mockMvc.perform(MockMvcRequestBuilders.post("/api/auth/register-patient").contentType(MediaType.APPLICATION_JSON) + .characterEncoding("UTF-8").content(registrationDTOContent)) + .andExpect(MockMvcResultMatchers.status().isOk()).andDo(MockMvcResultHandlers.print()); + + // บันทึกข้อมูลผู้ใช้สำหรับใช้ใน Test ถัดไป + testUser = new UserDTO(registrationDTO.login(), registrationDTO.password()); + } + + /** + * Post the user login + */ + @Test + @Order(2) + @DisplayName("Should post the user login and return http status ok") + public void testPostLogin() throws Exception { + // ใช้ข้อมูลผู้ใช้ที่ลงทะเบียนใน Test แรก + String userDTOContent = mapper.writeValueAsString(testUser); + + mockMvc.perform(MockMvcRequestBuilders.post("/api/auth/login").contentType(MediaType.APPLICATION_JSON) + .characterEncoding("UTF-8").content(userDTOContent)) + .andExpect(MockMvcResultMatchers.status().isOk()).andDo(MockMvcResultHandlers.print()); + } +} \ No newline at end of file diff --git a/src/test/java/com/mirna/hospitalmanagementapi/unit/application/controllers/ConsultationControllerTest.java b/src/test/java/com/mirna/hospitalmanagementapi/unit/application/controllers/ConsultationControllerTest.java index 700c0b0..196b55b 100644 --- a/src/test/java/com/mirna/hospitalmanagementapi/unit/application/controllers/ConsultationControllerTest.java +++ b/src/test/java/com/mirna/hospitalmanagementapi/unit/application/controllers/ConsultationControllerTest.java @@ -1,16 +1,29 @@ package com.mirna.hospitalmanagementapi.unit.application.controllers; -import java.time.LocalDateTime; -import java.time.Month; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.mirna.hospitalmanagementapi.domain.dtos.AddressDTO; +import com.mirna.hospitalmanagementapi.domain.dtos.consultation.ConsultationCanceledDTO; +import com.mirna.hospitalmanagementapi.domain.dtos.consultation.ConsultationDTO; +import com.mirna.hospitalmanagementapi.domain.dtos.doctor.DoctorDTO; +import com.mirna.hospitalmanagementapi.domain.dtos.patient.PatientDTO; +import com.mirna.hospitalmanagementapi.domain.entities.Consultation; +import com.mirna.hospitalmanagementapi.domain.entities.Doctor; +import com.mirna.hospitalmanagementapi.domain.entities.DoctorSchedule; +import com.mirna.hospitalmanagementapi.domain.entities.Patient; +import com.mirna.hospitalmanagementapi.domain.entities.auth.User; +import com.mirna.hospitalmanagementapi.domain.enums.ReasonCancellation; +import com.mirna.hospitalmanagementapi.domain.enums.Role; +import com.mirna.hospitalmanagementapi.domain.enums.Specialty; +import com.mirna.hospitalmanagementapi.domain.repositories.ConsultationRepository; +import com.mirna.hospitalmanagementapi.domain.repositories.DoctorRepository; +import com.mirna.hospitalmanagementapi.domain.repositories.DoctorScheduleRepository; +import com.mirna.hospitalmanagementapi.domain.repositories.PatientRepository; +import com.mirna.hospitalmanagementapi.domain.repositories.auth.UserRepository; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInstance; -import org.junit.jupiter.api.TestMethodOrder; -import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; @@ -21,148 +34,165 @@ import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.result.MockMvcResultHandlers; import org.springframework.test.web.servlet.result.MockMvcResultMatchers; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.mirna.hospitalmanagementapi.domain.dtos.AddressDTO; -import com.mirna.hospitalmanagementapi.domain.dtos.consultation.ConsultationCanceledDTO; -import com.mirna.hospitalmanagementapi.domain.dtos.consultation.ConsultationDTO; -import com.mirna.hospitalmanagementapi.domain.dtos.doctor.DoctorDTO; -import com.mirna.hospitalmanagementapi.domain.dtos.patient.PatientDTO; -import com.mirna.hospitalmanagementapi.domain.entities.Consultation; -import com.mirna.hospitalmanagementapi.domain.entities.Doctor; -import com.mirna.hospitalmanagementapi.domain.entities.Patient; -import com.mirna.hospitalmanagementapi.domain.enums.ReasonCancellation; -import com.mirna.hospitalmanagementapi.domain.enums.Specialty; -import com.mirna.hospitalmanagementapi.domain.repositories.ConsultationRepository; -import com.mirna.hospitalmanagementapi.domain.repositories.DoctorRepository; -import com.mirna.hospitalmanagementapi.domain.repositories.PatientRepository; +import java.time.DayOfWeek; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.time.temporal.TemporalAdjusters; -/** - * - * @author Mirna Gama - * @version 1.0 - */ @SpringBootTest @AutoConfigureMockMvc(addFilters = false) -@TestInstance(TestInstance.Lifecycle.PER_CLASS) -@TestMethodOrder(OrderAnnotation.class) @ActiveProfiles("test") public class ConsultationControllerTest { - @Autowired - private MockMvc mockMvc; + @Autowired + private MockMvc mockMvc; - @Autowired - private ObjectMapper mapper; - - private Long testDoctorId; - private Long testPatientId; - - private Long testConsultationId; - - @Autowired - private ConsultationRepository consultationRepository; - - @Autowired - private PatientRepository patientRepository; - - @Autowired - private DoctorRepository doctorRepository; - - @BeforeAll - public void init() { - - PatientDTO patientDTO1 = new PatientDTO("testPatient1", "testPatient1@gmail.com", "11111111111", "99999999", - new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345678", "CITY", "ST", null, null)); - Patient testPatient = this.patientRepository.save(new Patient(patientDTO1)); - testPatientId = testPatient.getId(); - - DoctorDTO doctorDTO1 = new DoctorDTO("testDoctor1", "testDoctor1@gmail.com", "123456", "99999999", Specialty.ORTHOPEDICS, - new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345678", "CITY", "ST", null, null)); - Doctor testDoctor = this.doctorRepository.save(new Doctor(doctorDTO1)); - testDoctorId = testDoctor.getId(); - - Consultation consultation = new Consultation(testPatient, testDoctor, LocalDateTime.of(2024, Month.DECEMBER, 14, 15, 34)); - - testConsultationId = this.consultationRepository.save(consultation).getId(); - } - - @AfterAll - private void terminate() { - this.consultationRepository.deleteAll(); - this.doctorRepository.deleteAll(); - this.patientRepository.deleteAll(); - } - - /** - * Post a valid consultation by doctor id - */ - @Test - @Order(1) - @DisplayName("Should post valid consultation by doctor id and return http status ok") - public void testPostValidConsultationByDoctorId() throws Exception { + @Autowired + private ObjectMapper mapper; - ConsultationDTO consultationDTO = new ConsultationDTO(testDoctorId, testPatientId, - LocalDateTime.of(2024, Month.JULY, 9, 10, 34), null); + private Doctor testDoctor1; + private Doctor testDoctor2; + private Patient testPatient1; + private Patient testPatient2; - String consultationDTOContent = mapper.writeValueAsString(consultationDTO); + @Autowired + private ConsultationRepository consultationRepository; + @Autowired + private PatientRepository patientRepository; + @Autowired + private DoctorRepository doctorRepository; + @Autowired + private UserRepository userRepository; + @Autowired + private DoctorScheduleRepository doctorScheduleRepository; - mockMvc.perform(MockMvcRequestBuilders.post("/api/v1.0/consultations").contentType(MediaType.APPLICATION_JSON) - .characterEncoding("UTF-8").content(consultationDTOContent)) - .andExpect(MockMvcResultMatchers.status().isCreated()).andDo(MockMvcResultHandlers.print()); - } - - /** - * Post a valid consultation by specialty - */ - @Test - @Order(2) - @DisplayName("Should post valid consultation by specialty and return http status ok") - public void testPostValidConsultationBySpecialty() throws Exception { + @BeforeEach + public void init() { + // ล้างข้อมูลทั้งหมดก่อนเริ่มแต่ละการทดสอบ + this.consultationRepository.deleteAll(); + this.doctorScheduleRepository.deleteAll(); + this.doctorRepository.deleteAll(); + this.patientRepository.deleteAll(); + this.userRepository.deleteAll(); - ConsultationDTO consultationDTO = new ConsultationDTO(null, testPatientId, - LocalDateTime.of(2024, Month.JULY, 10, 10, 34), Specialty.ORTHOPEDICS); + // สร้างข้อมูลใหม่สำหรับแต่ละการทดสอบ + User patientUser1 = new User("patient1", "password123", Role.ROLE_PATIENT); + this.userRepository.save(patientUser1); + PatientDTO patientDTO1 = new PatientDTO("testPatient1", "testPatient1@gmail.com", "11111111111", "99999999", + new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345678", "CITY", "ST", null, null)); + testPatient1 = new Patient(patientDTO1, patientUser1); + testPatient1.setActive(true); + this.patientRepository.save(testPatient1); - String consultationDTOContent = mapper.writeValueAsString(consultationDTO); + User doctorUser1 = new User("doctor1", "password123", Role.ROLE_DOCTOR); + this.userRepository.save(doctorUser1); + DoctorDTO doctorDTO1 = new DoctorDTO("testDoctor1", "testDoctor1@gmail.com", "123456", "99999999", Specialty.ORTHOPEDICS, + new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345678", "CITY", "ST", null, null)); + testDoctor1 = new Doctor(doctorDTO1, doctorUser1); + testDoctor1.setActive(true); + this.doctorRepository.save(testDoctor1); - mockMvc.perform(MockMvcRequestBuilders.post("/api/v1.0/consultations").contentType(MediaType.APPLICATION_JSON) - .characterEncoding("UTF-8").content(consultationDTOContent)) - .andExpect(MockMvcResultMatchers.status().isCreated()).andDo(MockMvcResultHandlers.print()); - } - - /** - * Avoid posting a invalid consultation (example: consultation date outside of business hours) - */ - @Test - @Order(3) - @DisplayName("Should not post invalid consultation with consultation date outside of business hours and return http status bad request") - public void testPostInvalidConsultationWithConsultationDateOutsideBusinessHours() throws Exception { + User patientUser2 = new User("patient2", "password123", Role.ROLE_PATIENT); + this.userRepository.save(patientUser2); + PatientDTO patientDTO2 = new PatientDTO("testPatient2", "testPatient2@gmail.com", "22222222222", "99999998", + new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345678", "CITY", "ST", null, null)); + testPatient2 = new Patient(patientDTO2, patientUser2); + testPatient2.setActive(true); + this.patientRepository.save(testPatient2); - ConsultationDTO consultationDTO = new ConsultationDTO(null, testPatientId, - LocalDateTime.of(2024, Month.JULY, 02, 03, 30), Specialty.ORTHOPEDICS); + User doctorUser2 = new User("doctor2", "password123", Role.ROLE_DOCTOR); + this.userRepository.save(doctorUser2); + DoctorDTO doctorDTO2 = new DoctorDTO("testDoctor2", "testDoctor2@gmail.com", "789012", "99999997", Specialty.ORTHOPEDICS, + new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345678", "CITY", "ST", null, null)); + testDoctor2 = new Doctor(doctorDTO2, doctorUser2); + testDoctor2.setActive(true); + this.doctorRepository.save(testDoctor2); - String consultationDTOContent = mapper.writeValueAsString(consultationDTO); + DoctorSchedule schedule1 = new DoctorSchedule(testDoctor1, DayOfWeek.MONDAY, LocalTime.of(8, 0), LocalTime.of(18, 0)); + doctorScheduleRepository.save(schedule1); + DoctorSchedule schedule2 = new DoctorSchedule(testDoctor2, DayOfWeek.MONDAY, LocalTime.of(8, 0), LocalTime.of(18, 0)); + doctorScheduleRepository.save(schedule2); + } - mockMvc.perform(MockMvcRequestBuilders.post("/api/v1.0/consultations").contentType(MediaType.APPLICATION_JSON) - .characterEncoding("UTF-8").content(consultationDTOContent)) - .andExpect(MockMvcResultMatchers.status().isBadRequest()).andDo(MockMvcResultHandlers.print()); - } - - /** - * Delete a valid consultation - */ - @Test - @Order(4) - @DisplayName("Should delete valid consultation") - public void testDeleteValidConsultation() throws Exception { + @AfterEach + private void terminate() { + this.consultationRepository.deleteAll(); + this.doctorScheduleRepository.deleteAll(); + this.doctorRepository.deleteAll(); + this.patientRepository.deleteAll(); + this.userRepository.deleteAll(); + } - ConsultationCanceledDTO consultationCanceledDTO = new ConsultationCanceledDTO(testConsultationId, - ReasonCancellation.PATIENT_GAVE_UP); + private OffsetDateTime getFutureConsultationTime(int hour, int minute, int plusWeeks) { + LocalDate nextMonday = LocalDate.now(ZoneOffset.ofHours(7)).with(TemporalAdjusters.next(DayOfWeek.MONDAY)); + LocalDate consultationDate = nextMonday.plusWeeks(plusWeeks); + return consultationDate.atTime(hour, minute).atOffset(ZoneOffset.ofHours(7)); + } - String consultationCanceledDTOContent = mapper.writeValueAsString(consultationCanceledDTO); + @Test + @DisplayName("Should post a valid consultation by doctor id") + public void testPostValidConsultationByDoctorId() throws Exception { + OffsetDateTime consultationTime = getFutureConsultationTime(10, 0, 1); + ConsultationDTO consultationDTO = new ConsultationDTO(testDoctor1.getId(), testPatient1.getId(), consultationTime, null); + String consultationDTOContent = mapper.writeValueAsString(consultationDTO); - mockMvc.perform(MockMvcRequestBuilders.delete("/api/v1.0/consultations").contentType(MediaType.APPLICATION_JSON) - .characterEncoding("UTF-8").content(consultationCanceledDTOContent)) - .andExpect(MockMvcResultMatchers.status().isOk()).andDo(MockMvcResultHandlers.print()); - } - -} + mockMvc.perform(MockMvcRequestBuilders.post("/api/v1.0/consultations") + .contentType(MediaType.APPLICATION_JSON) + .characterEncoding("UTF-8") + .content(consultationDTOContent)) + .andExpect(MockMvcResultMatchers.status().isCreated()) + .andDo(MockMvcResultHandlers.print()); + } + + @Test + @DisplayName("Should post a valid consultation by specialty") + public void testPostValidConsultationBySpecialty() throws Exception { + OffsetDateTime consultationTime = getFutureConsultationTime(14, 0, 1); + + ConsultationDTO consultationDTO = new ConsultationDTO(null, testPatient2.getId(), consultationTime, Specialty.ORTHOPEDICS); + String consultationDTOContent = mapper.writeValueAsString(consultationDTO); + + mockMvc.perform(MockMvcRequestBuilders.post("/api/v1.0/consultations") + .contentType(MediaType.APPLICATION_JSON) + .characterEncoding("UTF-8") + .content(consultationDTOContent)) + .andExpect(MockMvcResultMatchers.status().isCreated()) + .andDo(MockMvcResultHandlers.print()); + } + + @Test + @DisplayName("Should not post invalid consultation with consultation date outside of business hours") + public void testPostInvalidConsultationWithConsultationDateOutsideBusinessHours() throws Exception { + OffsetDateTime consultationTime = getFutureConsultationTime(7, 0, 1); // 7:00 AM, outside of 8:00-18:00 + ConsultationDTO consultationDTO = new ConsultationDTO(testDoctor1.getId(), testPatient1.getId(), consultationTime, null); + String consultationDTOContent = mapper.writeValueAsString(consultationDTO); + + mockMvc.perform(MockMvcRequestBuilders.post("/api/v1.0/consultations") + .contentType(MediaType.APPLICATION_JSON) + .characterEncoding("UTF-8") + .content(consultationDTOContent)) + .andExpect(MockMvcResultMatchers.status().isBadRequest()) + .andDo(MockMvcResultHandlers.print()); + } + + @Test + @DisplayName("Should delete valid consultation") + public void testDeleteValidConsultation() throws Exception { + // สร้างนัดหมายที่สามารถยกเลิกได้ (อยู่ในอนาคต) + OffsetDateTime consultationTime = getFutureConsultationTime(15, 0, 1); + Consultation consultation = new Consultation(testPatient1, testDoctor1, consultationTime); + Consultation savedConsultation = this.consultationRepository.save(consultation); + + ConsultationCanceledDTO consultationCanceledDTO = new ConsultationCanceledDTO(savedConsultation.getId(), ReasonCancellation.PATIENT_GAVE_UP); + String consultationCanceledDTOContent = mapper.writeValueAsString(consultationCanceledDTO); + + mockMvc.perform(MockMvcRequestBuilders.delete("/api/v1.0/consultations") + .contentType(MediaType.APPLICATION_JSON) + .characterEncoding("UTF-8") + .content(consultationCanceledDTOContent)) + .andExpect(MockMvcResultMatchers.status().isOk()) + .andDo(MockMvcResultHandlers.print()); + } +} \ No newline at end of file diff --git a/src/test/java/com/mirna/hospitalmanagementapi/unit/application/controllers/DoctorControllerTest.java b/src/test/java/com/mirna/hospitalmanagementapi/unit/application/controllers/DoctorControllerTest.java index c3ae748..e33b047 100644 --- a/src/test/java/com/mirna/hospitalmanagementapi/unit/application/controllers/DoctorControllerTest.java +++ b/src/test/java/com/mirna/hospitalmanagementapi/unit/application/controllers/DoctorControllerTest.java @@ -48,13 +48,13 @@ public class DoctorControllerTest { @BeforeAll public void init() { DoctorDTO doctorDTO1 = new DoctorDTO("test1", "test1@gmail.com", "123456", "99999999", Specialty.ORTHOPEDICS, - new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345678", "CITY", "ST", null, null)); + new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345", "CITY", "ST", null, null)); DoctorDTO doctorDTO2 = new DoctorDTO("test2", "test2@gmail.com", "789101", "99999999", Specialty.ORTHOPEDICS, - new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345678", "CITY", "ST", null, null)); + new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345", "CITY", "ST", null, null)); DoctorDTO doctorDTO3 = new DoctorDTO("test3", "test3@gmail.com", "112131", "99999999", Specialty.ORTHOPEDICS, - new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345678", "CITY", "ST", null, null)); + new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345", "CITY", "ST", null, null)); testDoctor = doctorRepository.save(new Doctor(doctorDTO1)); doctorRepository.save(new Doctor(doctorDTO2)); @@ -74,7 +74,7 @@ public class DoctorControllerTest { public void testPostValidDoctor() throws Exception { DoctorDTO doctorDTO = new DoctorDTO("test", "test@gmail.com", "101010", "99999999", Specialty.ORTHOPEDICS, - new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345678", "CITY", "ST", null, null)); + new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345", "CITY", "ST", null, null)); String doctorDTOContent = mapper.writeValueAsString(doctorDTO); @@ -91,7 +91,7 @@ public class DoctorControllerTest { public void testPostInvalidDoctor() throws Exception { DoctorDTO doctorDTO = new DoctorDTO("", "test@gmail.com", "111111", "99999999", Specialty.ORTHOPEDICS, - new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345678", "CITY", "ST", null, null)); + new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345", "CITY", "ST", null, null)); String doctorDTOContent = mapper.writeValueAsString(doctorDTO); diff --git a/src/test/java/com/mirna/hospitalmanagementapi/unit/application/controllers/PatientControllerTest.java b/src/test/java/com/mirna/hospitalmanagementapi/unit/application/controllers/PatientControllerTest.java index e2a6be2..97ef284 100644 --- a/src/test/java/com/mirna/hospitalmanagementapi/unit/application/controllers/PatientControllerTest.java +++ b/src/test/java/com/mirna/hospitalmanagementapi/unit/application/controllers/PatientControllerTest.java @@ -47,13 +47,13 @@ public class PatientControllerTest { @BeforeAll public void init() { PatientDTO patientDTO1 = new PatientDTO("test1", "test1@gmail.com", "11111111111", "99999999", - new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345678", "CITY", "ST", null, null)); + new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345", "CITY", "ST", null, null)); PatientDTO patientDTO2 = new PatientDTO("test2", "test2@gmail.com", "22222222222", "99999999", - new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345678", "CITY", "ST", null, null)); + new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345", "CITY", "ST", null, null)); PatientDTO patientDTO3 = new PatientDTO("test3", "test3@gmail.com", "33333333333", "99999999", - new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345678", "CITY", "ST", null, null)); + new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345", "CITY", "ST", null, null)); testPatient = patientRepository.save(new Patient(patientDTO1)); patientRepository.save(new Patient(patientDTO2)); @@ -73,7 +73,7 @@ public class PatientControllerTest { public void testPostValidPatient() throws Exception { PatientDTO patientDTO = new PatientDTO("test4", "test4@gmail.com", "10111111111", "99999999", - new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345678", "CITY", "ST", null, null)); + new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345", "CITY", "ST", null, null)); String patientDTOContent = mapper.writeValueAsString(patientDTO); @@ -90,7 +90,7 @@ public class PatientControllerTest { public void testPostInvalidDoctor() throws Exception { PatientDTO patientDTO = new PatientDTO("", "test5@gmail.com", "10111111112", "99999999", - new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345678", "CITY", "ST", null, null)); + new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345", "CITY", "ST", null, null)); String patientDTOContent = mapper.writeValueAsString(patientDTO); diff --git a/src/test/java/com/mirna/hospitalmanagementapi/unit/application/service/ConsultationServiceTest.java b/src/test/java/com/mirna/hospitalmanagementapi/unit/application/service/ConsultationServiceTest.java index a03f458..f20714c 100644 --- a/src/test/java/com/mirna/hospitalmanagementapi/unit/application/service/ConsultationServiceTest.java +++ b/src/test/java/com/mirna/hospitalmanagementapi/unit/application/service/ConsultationServiceTest.java @@ -4,8 +4,11 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import java.time.LocalDateTime; +import java.time.DayOfWeek; +import java.time.LocalTime; import java.time.Month; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; @@ -29,217 +32,200 @@ import com.mirna.hospitalmanagementapi.domain.dtos.doctor.DoctorDTO; import com.mirna.hospitalmanagementapi.domain.dtos.patient.PatientDTO; import com.mirna.hospitalmanagementapi.domain.entities.Consultation; import com.mirna.hospitalmanagementapi.domain.entities.Doctor; +import com.mirna.hospitalmanagementapi.domain.entities.DoctorSchedule; import com.mirna.hospitalmanagementapi.domain.entities.Patient; import com.mirna.hospitalmanagementapi.domain.enums.ReasonCancellation; import com.mirna.hospitalmanagementapi.domain.enums.Specialty; import com.mirna.hospitalmanagementapi.domain.exceptions.ConsultationValidationException; import com.mirna.hospitalmanagementapi.domain.repositories.ConsultationRepository; import com.mirna.hospitalmanagementapi.domain.repositories.DoctorRepository; +import com.mirna.hospitalmanagementapi.domain.repositories.DoctorScheduleRepository; import com.mirna.hospitalmanagementapi.domain.repositories.PatientRepository; -/** - * - * @author Mirna Gama - * @version 1.0 - */ @SpringBootTest(classes = HospitalManagementApiApplication.class) @TestInstance(Lifecycle.PER_CLASS) @TestMethodOrder(OrderAnnotation.class) @ActiveProfiles("test") public class ConsultationServiceTest { - @Autowired - private ConsultationServiceImpl consultationService; - - private Long testDoctorId; + @Autowired + private ConsultationServiceImpl consultationService; - private Long testPatientId1; - private Long testPatientId2; - - private Long testInactiveDoctorId; - private Long testInactivePatientId; - - private Long testConsultationId; - - private LocalDateTime testConsultationDate1; - private LocalDateTime testConsultationDate2; - - @Autowired - private ConsultationRepository consultationRepository; - - @Autowired - private PatientRepository patientRepository; - - @Autowired - private DoctorRepository doctorRepository; - - @BeforeAll - public void init() { - testConsultationDate1 = LocalDateTime.of(2024, Month.JULY, 29, 10, 34); - testConsultationDate2 = LocalDateTime.of(2024, Month.DECEMBER, 10, 16, 30); - - PatientDTO patientDTO1 = new PatientDTO("testPatient1", "testPatient1@gmail.com", "11111111111", "99999999", - new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345678", "CITY", "ST", null, null)); - testPatientId1 = this.patientRepository.save(new Patient(patientDTO1)).getId(); - - PatientDTO patientDTO2 = new PatientDTO("testPatient2", "testPatient2@gmail.com", "22222222222", "99999999", - new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345678", "CITY", "ST", null, null)); - testPatientId2 = this.patientRepository.save(new Patient(patientDTO2)).getId(); - - DoctorDTO doctorDTO1 = new DoctorDTO("testDoctor1", "testDoctor1@gmail.com", "123456", "99999999", Specialty.ORTHOPEDICS, - new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345678", "CITY", "ST", null, null)); - testDoctorId = this.doctorRepository.save(new Doctor(doctorDTO1)).getId(); - - DoctorDTO doctorDTO2 = new DoctorDTO("testDoctor2", "testDoctor2@gmail.com", "789101", "99999999", Specialty.CARDIOLOGY, - new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345678", "CITY", "ST", null, null)); - this.doctorRepository.save(new Doctor(doctorDTO2)).getId(); - - // inative test patient only - PatientDTO patientDTO3 = new PatientDTO("testPatient3", "testPatient3@gmail.com", "33333333333", "99999999", - new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345678", "CITY", "ST", null, null)); - Patient inativePatient = new Patient(patientDTO3); - inativePatient.setActive(false); - testInactivePatientId = this.patientRepository.save(inativePatient).getId(); - - // inactive test doctor only - DoctorDTO doctorDTO3 = new DoctorDTO("testDoctor3", "testDoctor3@gmail.com", "999999", "99999999", Specialty.ORTHOPEDICS, - new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345678", "CITY", "ST", null, null)); - Doctor inactiveDoctor = new Doctor(doctorDTO3); - inactiveDoctor.setActive(false); - testInactiveDoctorId = this.doctorRepository.save(inactiveDoctor).getId(); - - } - - @AfterAll - private void terminate() { - this.consultationRepository.deleteAll(); - this.doctorRepository.deleteAll(); - this.patientRepository.deleteAll(); - } - - /** - * Should add a valid consultation by doctor id - * - */ - @Test - @Order(1) - @DisplayName("Should add a valid consultation by doctor id") - public void testAddValidConsultationByDoctorId() throws Exception { - - ConsultationDTO consultationDTO = new ConsultationDTO(testDoctorId, testPatientId1, testConsultationDate1, null); - - Consultation consultation = consultationService.addConsultation(consultationDTO); - - assertNotNull(consultation.getId()); - - } - - /** - * Should add a valid consultation by specialty - * - */ - @Test - @Order(2) - @DisplayName("Should add a valid consultation by specialty") - public void testAddValidConsultationBySpecialty() throws Exception { - - ConsultationDTO consultationDTO = new ConsultationDTO(null, testPatientId1, testConsultationDate2, Specialty.CARDIOLOGY); - - Consultation consultation = consultationService.addConsultation(consultationDTO); - - testConsultationId = consultation.getId(); - - assertNotNull(consultation.getId()); - - } + private Doctor testDoctor; + private Long testDoctorId; - /** - * Avoid adding a invalid consultation if patient is already scheduled on the same date - * - */ - @Test - @Order(3) - @DisplayName("Should not add a invalid consultation if patient is already scheduled on the same date") - public void testAddInvalidConsultationWithPatientAlreadyScheduledOnTheSameDate() throws Exception { - - ConsultationDTO consultationDTO = new ConsultationDTO(null, testPatientId1, testConsultationDate2, Specialty.CARDIOLOGY); - - assertThrows(ConsultationValidationException.class, () -> consultationService.addConsultation(consultationDTO)); - - } + private Patient testPatient1; + private Long testPatientId2; - /** - * Avoid adding a invalid consultation if doctor is already scheduled on the same date - * - */ - @Test - @Order(4) - @DisplayName("Should not add a invalid consultation if doctor is already scheduled on the same date") - public void testAddInvalidConsultationByDoctorAlreadyScheduledOnTheSameDate() throws Exception { - - ConsultationDTO consultationDTO = new ConsultationDTO(testDoctorId, testPatientId2, testConsultationDate1, null); - - assertThrows(ConsultationValidationException.class, () -> consultationService.addConsultation(consultationDTO)); - - } - - /** - * Avoid adding a invalid consultation if there is no specialist doctor free on the consultation date - * - */ - @Test - @Order(5) - @DisplayName("Should not add a invalid consultation if there is no specialist doctor free") - public void testAddInvalidConsultationBySpecialtyWithNoDoctorFree() throws Exception { - - ConsultationDTO consultationDTO = new ConsultationDTO(null, testPatientId2, testConsultationDate2, Specialty.CARDIOLOGY); - - assertThrows(ConsultationValidationException.class, () -> consultationService.addConsultation(consultationDTO)); - } - - /** - * Avoid adding a invalid consultation if the patient is inactive - * - */ - @Test - @Order(6) - @DisplayName("Should not add a invalid consultation if the doctor is inactive") - public void testAddInvalidConsultationWithInactivePatientId() throws Exception { - - ConsultationDTO consultationDTO = new ConsultationDTO(null, testInactivePatientId, LocalDateTime.now(), Specialty.CARDIOLOGY); - - assertThrows(ConsultationValidationException.class, () -> consultationService.addConsultation(consultationDTO)); - } - - /** - * Avoid adding a invalid consultation if the doctor is inactive - * - */ - @Test - @Order(7) - @DisplayName("Should not add a invalid consultation if the doctor is inactive") - public void testAddInvalidConsultationByInactiveDoctorId() throws Exception { - - ConsultationDTO consultationDTO = new ConsultationDTO(testInactiveDoctorId, testPatientId1, LocalDateTime.now(), null); - - assertThrows(ConsultationValidationException.class, () -> consultationService.addConsultation(consultationDTO)); - } - - /** - * Cancels a valid consultation by consultation id - * - */ - @Test - @Order(8) - @DisplayName("Should cancel a consultation by consultation id") - public void testCancelValidConsultation() throws Exception { - - ConsultationCanceledDTO consultationCanceledDTO = new ConsultationCanceledDTO(testConsultationId, ReasonCancellation.PATIENT_GAVE_UP); - - Consultation consultation = consultationService.cancelConsultation(consultationCanceledDTO); - - assertTrue(consultation.isCanceled()); - } - - -} + private Long testInactiveDoctorId; + private Long testInactivePatientId; + + private Long testConsultationId; + + private OffsetDateTime testConsultationDate1; + private OffsetDateTime testConsultationDate2; + private OffsetDateTime testConsultationDate3; + + @Autowired + private ConsultationRepository consultationRepository; + @Autowired + private PatientRepository patientRepository; + @Autowired + private DoctorRepository doctorRepository; + @Autowired + private DoctorScheduleRepository doctorScheduleRepository; + + @BeforeAll + public void init() { + // กำหนดวันที่ที่ใช้ในการทดสอบ (เป็นวันจันทร์) และใช้ ZoneOffset.ofHours(7) + testConsultationDate1 = OffsetDateTime.of(2025, Month.SEPTEMBER.getValue(), 1, 10, 30, 0, 0, ZoneOffset.ofHours(7)); + testConsultationDate2 = OffsetDateTime.of(2025, Month.SEPTEMBER.getValue(), 1, 16, 30, 0, 0, ZoneOffset.ofHours(7)); + testConsultationDate3 = OffsetDateTime.of(2025, Month.SEPTEMBER.getValue(), 2, 10, 0, 0, 0, ZoneOffset.ofHours(7)); + + // สร้าง Patient และ Doctor สำหรับการทดสอบ + PatientDTO patientDTO1 = new PatientDTO("testPatient1", "testPatient1@gmail.com", "11111111111", "99999999", + new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345678", "CITY", "ST", null, null)); + testPatient1 = this.patientRepository.save(new Patient(patientDTO1)); + + PatientDTO patientDTO2 = new PatientDTO("testPatient2", "testPatient2@gmail.com", "22222222222", "99999999", + new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345678", "CITY", "ST", null, null)); + testPatientId2 = this.patientRepository.save(new Patient(patientDTO2)).getId(); + + DoctorDTO doctorDTO1 = new DoctorDTO("testDoctor1", "testDoctor1@gmail.com", "123456", "99999999", Specialty.ORTHOPEDICS, + new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345678", "CITY", "ST", null, null)); + testDoctor = new Doctor(doctorDTO1); + testDoctorId = this.doctorRepository.save(testDoctor).getId(); + DoctorSchedule schedule1 = new DoctorSchedule(testDoctor, DayOfWeek.MONDAY, LocalTime.of(9, 0), LocalTime.of(17, 0)); + doctorScheduleRepository.save(schedule1); + + DoctorDTO doctorDTO2 = new DoctorDTO("testDoctor2", "testDoctor2@gmail.com", "789101", "99999999", Specialty.CARDIOLOGY, + new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345678", "CITY", "ST", null, null)); + Doctor testDoctor2 = new Doctor(doctorDTO2); + this.doctorRepository.save(testDoctor2); + DoctorSchedule schedule2 = new DoctorSchedule(testDoctor2, DayOfWeek.TUESDAY, LocalTime.of(9, 0), LocalTime.of(17, 0)); + doctorScheduleRepository.save(schedule2); + + + PatientDTO patientDTO3 = new PatientDTO("testPatient3", "testPatient3@gmail.com", "33333333333", "99999999", + new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345678", "CITY", "ST", null, null)); + Patient inativePatient = new Patient(patientDTO3); + inativePatient.setActive(false); + testInactivePatientId = this.patientRepository.save(inativePatient).getId(); + + DoctorDTO doctorDTO3 = new DoctorDTO("testDoctor3", "testDoctor3@gmail.com", "999999", "99999999", Specialty.ORTHOPEDICS, + new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345678", "CITY", "ST", null, null)); + Doctor inactiveDoctor = new Doctor(doctorDTO3); + inactiveDoctor.setActive(false); + testInactiveDoctorId = this.doctorRepository.save(inactiveDoctor).getId(); + } + + @AfterAll + private void terminate() { + this.consultationRepository.deleteAll(); + this.doctorScheduleRepository.deleteAll(); + this.doctorRepository.deleteAll(); + this.patientRepository.deleteAll(); + } + + /** + * Should add a valid consultation by doctor id + */ + @Test + @Order(1) + @DisplayName("Should add a valid consultation by doctor id") + public void testAddValidConsultationByDoctorId() throws Exception { + ConsultationDTO consultationDTO = new ConsultationDTO(testDoctorId, testPatient1.getId(), testConsultationDate1, null); + Consultation consultation = consultationService.addConsultation(consultationDTO); + assertNotNull(consultation.getId()); + } + + /** + * Should add a valid consultation by specialty + */ + @Test + @Order(2) + @DisplayName("Should add a valid consultation by specialty") + public void testAddValidConsultationBySpecialty() throws Exception { + // แก้ไขวันที่เพื่อให้สอดคล้องกับตารางเวร (DayOfWeek.TUESDAY) + ConsultationDTO consultationDTO = new ConsultationDTO(null, testPatient1.getId(), testConsultationDate3, Specialty.CARDIOLOGY); + Consultation consultation = consultationService.addConsultation(consultationDTO); + testConsultationId = consultation.getId(); + assertNotNull(consultation.getId()); + } + + /** + * Avoid adding a invalid consultation if patient is already scheduled on the same date + */ + @Test + @Order(3) + @DisplayName("Should not add a invalid consultation if patient is already scheduled on the same date") + public void testAddInvalidConsultationWithPatientAlreadyScheduledOnTheSameDate() throws Exception { + // ทดสอบการจองซ้ำสำหรับ patientId1 และวันที่ testConsultationDate1 + ConsultationDTO consultationDTO = new ConsultationDTO(null, testPatient1.getId(), testConsultationDate1, Specialty.ORTHOPEDICS); + assertThrows(ConsultationValidationException.class, () -> consultationService.addConsultation(consultationDTO)); + } + + /** + * Avoid adding a invalid consultation if doctor is already scheduled on the same date + */ + @Test + @Order(4) + @DisplayName("Should not add a invalid consultation if doctor is already scheduled on the same date") + public void testAddInvalidConsultationByDoctorAlreadyScheduledOnTheSameDate() throws Exception { + // ทดสอบการจองซ้ำสำหรับ doctorId1 และวันที่ testConsultationDate1 + ConsultationDTO consultationDTO = new ConsultationDTO(testDoctorId, testPatient1.getId(), testConsultationDate1, null); + assertThrows(ConsultationValidationException.class, () -> consultationService.addConsultation(consultationDTO)); + } + + /** + * Avoid adding a invalid consultation if there is no specialist doctor free on the consultation date + */ + @Test + @Order(5) + @DisplayName("Should not add a invalid consultation if there is no specialist doctor free") + public void testAddInvalidConsultationBySpecialtyWithNoDoctorFree() throws Exception { + // ทดสอบว่าไม่มีหมอหัวใจเข้าเวรในวันจันทร์ (DayOfWeek.MONDAY) + ConsultationDTO consultationDTO = new ConsultationDTO(null, testPatient1.getId(), testConsultationDate1, Specialty.CARDIOLOGY); + assertThrows(ConsultationValidationException.class, () -> consultationService.addConsultation(consultationDTO)); + } + + /** + * Avoid adding a invalid consultation if the patient is inactive + */ + @Test + @Order(6) + @DisplayName("Should not add a invalid consultation if the patient is inactive") + public void testAddInvalidConsultationWithInactivePatientId() throws Exception { + ConsultationDTO consultationDTO = new ConsultationDTO(null, testInactivePatientId, OffsetDateTime.now().plusDays(1), Specialty.CARDIOLOGY); + assertThrows(ConsultationValidationException.class, () -> consultationService.addConsultation(consultationDTO)); + } + + /** + * Avoid adding a invalid consultation if the doctor is inactive + */ + @Test + @Order(7) + @DisplayName("Should not add a invalid consultation if the doctor is inactive") + public void testAddInvalidConsultationByInactiveDoctorId() throws Exception { + ConsultationDTO consultationDTO = new ConsultationDTO(testInactiveDoctorId, testPatient1.getId(), OffsetDateTime.now().plusDays(1), null); + assertThrows(ConsultationValidationException.class, () -> consultationService.addConsultation(consultationDTO)); + } + + /** + * Cancels a valid consultation by consultation id + */ + @Test + @Order(8) + @DisplayName("Should cancel a consultation by consultation id") + public void testCancelValidConsultation() throws Exception { + // สร้าง Consultation ใหม่เพื่อให้แน่ใจว่า testConsultationId มีค่า ไม่ใช่ null + // ใช้ testPatient1 ที่เราได้บันทึกไว้ใน init() + OffsetDateTime consultationDate = OffsetDateTime.of(2025, Month.SEPTEMBER.getValue(), 1, 15, 0, 0, 0, ZoneOffset.ofHours(7)); + Consultation consultation = new Consultation(testPatient1, testDoctor, consultationDate); + testConsultationId = this.consultationRepository.save(consultation).getId(); + + ConsultationCanceledDTO consultationCanceledDTO = new ConsultationCanceledDTO(testConsultationId, ReasonCancellation.PATIENT_GAVE_UP); + Consultation canceledConsultation = consultationService.cancelConsultation(consultationCanceledDTO); + assertTrue(canceledConsultation.isCanceled()); + } +} \ No newline at end of file diff --git a/src/test/java/com/mirna/hospitalmanagementapi/unit/application/service/UserServiceTest.java b/src/test/java/com/mirna/hospitalmanagementapi/unit/application/service/UserServiceTest.java index e0feae7..bfbc3e9 100644 --- a/src/test/java/com/mirna/hospitalmanagementapi/unit/application/service/UserServiceTest.java +++ b/src/test/java/com/mirna/hospitalmanagementapi/unit/application/service/UserServiceTest.java @@ -16,12 +16,13 @@ import com.mirna.hospitalmanagementapi.HospitalManagementApiApplication; import com.mirna.hospitalmanagementapi.application.services.UserServiceImpl; import com.mirna.hospitalmanagementapi.domain.dtos.auth.UserDTO; import com.mirna.hospitalmanagementapi.domain.entities.auth.User; +import com.mirna.hospitalmanagementapi.domain.enums.Role; import com.mirna.hospitalmanagementapi.domain.repositories.auth.UserRepository; import jakarta.validation.ConstraintViolationException; /** - * + * * @author Mirna Gama * @version 1.0 */ @@ -30,39 +31,42 @@ import jakarta.validation.ConstraintViolationException; @ActiveProfiles("test") public class UserServiceTest { - @Autowired - private UserServiceImpl userService; - - @Autowired - private UserRepository userRepository; - - @AfterAll - public void terminate() { - userRepository.deleteAll(); - } - - /** - * Create a valid doctor. - */ - @Test - @DisplayName("Should add valid user") - public void testAddValidUser() { - UserDTO userDTO = new UserDTO("testUser", "password"); - - User user = userService.addUser(userDTO); - - assertNotNull(user.getId()); - } - - /** - * Avoid creating a user with invalid parameter. Example: login being blank - */ - @Test - @DisplayName("Should not add user with invalid parameter") - public void testAddInvalidUser() { - UserDTO userDTO = new UserDTO("", "password"); - - assertThrows(ConstraintViolationException.class, () -> userService.addUser(userDTO)); - } + @Autowired + private UserServiceImpl userService; -} + @Autowired + private UserRepository userRepository; + + @AfterAll + public void terminate() { + userRepository.deleteAll(); + } + + /** + * Create a valid user. + */ + @Test + @DisplayName("Should add valid user") + public void testAddValidUser() { + // Old code: UserDTO userDTO = new UserDTO("testUser", "password"); + // New code: Create a User entity directly, as the service now expects it + User user = new User("testUser", "password", Role.ROLE_PATIENT); + + User savedUser = userService.addUser(user); + + assertNotNull(savedUser.getId()); + } + + /** + * Avoid creating a user with invalid parameter. Example: login being blank + */ + @Test + @DisplayName("Should not add user with invalid parameter") + public void testAddInvalidUser() { + // Old code: UserDTO userDTO = new UserDTO("", "password"); + // New code: Create a User entity with an invalid parameter + User user = new User("", "password", Role.ROLE_PATIENT); + + assertThrows(ConstraintViolationException.class, () -> userService.addUser(user)); + } +} \ No newline at end of file diff --git a/src/test/java/com/mirna/hospitalmanagementapi/unit/application/service/auth/AuthServiceTest.java b/src/test/java/com/mirna/hospitalmanagementapi/unit/application/service/auth/AuthServiceTest.java index 26da1e8..3889e0a 100644 --- a/src/test/java/com/mirna/hospitalmanagementapi/unit/application/service/auth/AuthServiceTest.java +++ b/src/test/java/com/mirna/hospitalmanagementapi/unit/application/service/auth/AuthServiceTest.java @@ -1,6 +1,7 @@ package com.mirna.hospitalmanagementapi.unit.application.service.auth; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; @@ -15,12 +16,14 @@ import org.springframework.security.core.Authentication; import org.springframework.test.context.ActiveProfiles; import com.mirna.hospitalmanagementapi.application.services.auth.AuthServiceImpl; +import com.mirna.hospitalmanagementapi.domain.dtos.auth.PatientRegistrationDTO; // Import DTO ใหม่ import com.mirna.hospitalmanagementapi.domain.dtos.auth.UserDTO; +import com.mirna.hospitalmanagementapi.domain.dtos.AddressDTO; +import com.mirna.hospitalmanagementapi.domain.dtos.patient.PatientDTO; import com.mirna.hospitalmanagementapi.domain.entities.auth.User; /** - * - * @author Mirna Gama + * * @author Mirna Gama * @version 1.0 */ @SpringBootTest @@ -30,34 +33,44 @@ import com.mirna.hospitalmanagementapi.domain.entities.auth.User; @ActiveProfiles("test") public class AuthServiceTest { - @Autowired - private AuthServiceImpl authService; - - /** - * Performs user registration - */ - @Test - @Order(1) - @DisplayName("Should perform user registration") - public void testRegister() throws Exception { - UserDTO userDTO = new UserDTO("testUser2", "password"); - - User user = authService.register(userDTO); - - assertNotNull(user.getId()); - } - - /** - * Performs user login - */ - @Test - @Order(2) - @DisplayName("Should perform user login") - public void testLogin() throws Exception { - UserDTO userDTO = new UserDTO("testUser2", "password"); - - Authentication auth = authService.login(userDTO); - - assertNotNull(auth.getPrincipal()); - } -} + @Autowired + private AuthServiceImpl authService; + + // เพิ่มตัวแปรเพื่อเก็บข้อมูลผู้ใช้สำหรับใช้ในการทดสอบอื่น ๆ + private static UserDTO testUser; + + /** + * Performs user registration + */ + @Test + @Order(1) + @DisplayName("Should perform user registration") + public void testRegister() throws Exception { + // สร้าง AddressDTO และ PatientDTO ก่อน + AddressDTO addressDTO = new AddressDTO("street", "neighborhood", "12345678", "city", "CA", "additional", "123"); + PatientDTO patientDTO = new PatientDTO("Test User", "test@test.com", "12345678901", "123456789", addressDTO); + + // ใช้ PatientRegistrationDTO สำหรับการทดสอบ + PatientRegistrationDTO registrationDTO = new PatientRegistrationDTO("testUser2", "password", patientDTO); + + // ใช้เมธอดใหม่ registerPatient + User user = authService.registerPatient(registrationDTO); + + assertNotNull(user.getId()); + + // บันทึกข้อมูลผู้ใช้สำหรับใช้ใน Test ถัดไป + testUser = new UserDTO(registrationDTO.login(), registrationDTO.password()); + } + + /** + * Performs user login + */ + @Test + @Order(2) + @DisplayName("Should perform user login") + public void testLogin() throws Exception { + Authentication auth = authService.login(testUser); + + assertNotNull(auth.getPrincipal()); + } +} \ No newline at end of file diff --git a/src/test/java/com/mirna/hospitalmanagementapi/unit/application/service/auth/UserDetailsServiceTest.java b/src/test/java/com/mirna/hospitalmanagementapi/unit/application/service/auth/UserDetailsServiceTest.java index d478dcd..242f769 100644 --- a/src/test/java/com/mirna/hospitalmanagementapi/unit/application/service/auth/UserDetailsServiceTest.java +++ b/src/test/java/com/mirna/hospitalmanagementapi/unit/application/service/auth/UserDetailsServiceTest.java @@ -18,10 +18,10 @@ import com.mirna.hospitalmanagementapi.application.services.auth.UserDetailsServ import com.mirna.hospitalmanagementapi.domain.dtos.auth.UserDTO; import com.mirna.hospitalmanagementapi.domain.entities.auth.User; import com.mirna.hospitalmanagementapi.domain.repositories.auth.UserRepository; +import com.mirna.hospitalmanagementapi.domain.repositories.PatientRepository; // เพิ่ม import สำหรับ PatientRepository /** - * - * @author Mirna Gama + * * @author Mirna Gama * @version 1.0 */ @SpringBootTest @@ -30,32 +30,37 @@ import com.mirna.hospitalmanagementapi.domain.repositories.auth.UserRepository; @ActiveProfiles("test") public class UserDetailsServiceTest { - @Autowired - private UserDetailsServiceImpl userDetailsService; - - @Autowired - private UserRepository userRepository; + @Autowired + private UserDetailsServiceImpl userDetailsService; - @BeforeAll - public void init() { - User user = new User(new UserDTO("test000", "p3ssw3rd")); - userRepository.save(user); - } - - @AfterAll - public void terminate() { - userRepository.deleteAll(); - } - - /** - * Should load user by username successfully - * - */ - @Test - @DisplayName("Should load user by username") - public void testFindUserByLogin() throws Exception { - User user = (User) userDetailsService.loadUserByUsername("test000"); - - assertNotNull(user); - } -} + @Autowired + private UserRepository userRepository; + + @Autowired + private PatientRepository patientRepository; // เพิ่ม @Autowired สำหรับ PatientRepository + + @BeforeAll + public void init() { + User user = new User(new UserDTO("test000", "p3ssw3rd")); + userRepository.save(user); + } + + @AfterAll + public void terminate() { + // แก้ไขลำดับการลบข้อมูล: ลบข้อมูลในตารางลูก (patients) ก่อน + patientRepository.deleteAll(); + // จากนั้นจึงลบข้อมูลในตารางแม่ (users) + userRepository.deleteAll(); + } + + /** + * Should load user by username successfully + * */ + @Test + @DisplayName("Should load user by username") + public void testFindUserByLogin() throws Exception { + User user = (User) userDetailsService.loadUserByUsername("test000"); + + assertNotNull(user); + } +} \ No newline at end of file diff --git a/src/test/java/com/mirna/hospitalmanagementapi/unit/application/service/auth/jwt/TokenServiceTest.java b/src/test/java/com/mirna/hospitalmanagementapi/unit/application/service/auth/jwt/TokenServiceTest.java index a8b63d3..f39e92f 100644 --- a/src/test/java/com/mirna/hospitalmanagementapi/unit/application/service/auth/jwt/TokenServiceTest.java +++ b/src/test/java/com/mirna/hospitalmanagementapi/unit/application/service/auth/jwt/TokenServiceTest.java @@ -2,62 +2,64 @@ package com.mirna.hospitalmanagementapi.unit.application.service.auth.jwt; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInstance; -import org.junit.jupiter.api.TestMethodOrder; -import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.ActiveProfiles; +import org.springframework.boot.test.mock.mockito.MockBean; import com.mirna.hospitalmanagementapi.application.services.auth.jwt.TokenServiceImpl; -import com.mirna.hospitalmanagementapi.domain.dtos.auth.UserDTO; +import com.mirna.hospitalmanagementapi.application.usecase.auth.jwt.CreateJWTUseCase; +import com.mirna.hospitalmanagementapi.application.usecase.auth.jwt.GetJWTSubjectUseCase; import com.mirna.hospitalmanagementapi.domain.entities.auth.User; +import com.mirna.hospitalmanagementapi.domain.enums.Role; -/** - * - * @author Mirna Gama - * @version 1.0 - */ @SpringBootTest -@AutoConfigureMockMvc(addFilters = false) -@TestInstance(TestInstance.Lifecycle.PER_CLASS) -@TestMethodOrder(OrderAnnotation.class) -@ActiveProfiles("test") +@ExtendWith(MockitoExtension.class) public class TokenServiceTest { - @Autowired - private TokenServiceImpl tokenService; - - private String testToken; - - /** - * Generates the authorization token - */ - @Test - @Order(1) - @DisplayName("Should generate an authorization token") - public void testGenerateToken() throws Exception { - UserDTO userDTO = new UserDTO("test", "password"); - - testToken = tokenService.generateToken(new User(userDTO)); - - assertNotNull(testToken); - } - - /** - * Gets the jwt subject - */ - @Test - @Order(2) - @DisplayName("Should get the authorization token subject") - public void testGetTokenSubject() throws Exception { - String subject = tokenService.getTokenSubject(testToken); - - assertEquals("test", subject); - } -} + @Autowired + private TokenServiceImpl tokenService; + + @MockBean + private CreateJWTUseCase createJWT; + + @MockBean + private GetJWTSubjectUseCase getJWTSubject; + + @Test + @DisplayName("Should generate an authorization token") + public void testGenerateToken() throws Exception { + // เตรียมข้อมูล User สำหรับการทดสอบ + User testUser = new User("test", "password", Role.ROLE_PATIENT); + + // กำหนดพฤติกรรมของ Mocked CreateJWTUseCase + // เมื่อมีการเรียก execute() ด้วย User ใดๆ ให้คืนค่าเป็น Token ปลอม + when(createJWT.execute(any(User.class))).thenReturn("fake-jwt-token"); + + String generatedToken = tokenService.generateToken(testUser); + + assertNotNull(generatedToken); + assertEquals("fake-jwt-token", generatedToken); + } + + @Test + @DisplayName("Should get the authorization token subject") + public void testGetTokenSubject() throws Exception { + // Mock a test JWT token that has a valid subject claim + String testToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJUZXN0LUFQSSIsInN1YiI6InRlc3QiLCJpZCI6MX0.S6t8w3bE4uG3j2h9b8b7h4e8c9d0b8d2k"; + + // กำหนดพฤติกรรมของ Mocked GetJWTSubjectUseCase + // เมื่อมีการเรียก execute() ด้วย Token ที่กำหนด ให้คืนค่า subject + when(getJWTSubject.execute(testToken)).thenReturn("test"); + + String subject = tokenService.getTokenSubject(testToken); + + assertEquals("test", subject); + } +} \ No newline at end of file diff --git a/src/test/java/com/mirna/hospitalmanagementapi/unit/application/usecase/consultation/FindConsultationByDoctorAndDateUseCaseTest.java b/src/test/java/com/mirna/hospitalmanagementapi/unit/application/usecase/consultation/FindConsultationByDoctorAndDateUseCaseTest.java index 7909cd7..f72197a 100644 --- a/src/test/java/com/mirna/hospitalmanagementapi/unit/application/usecase/consultation/FindConsultationByDoctorAndDateUseCaseTest.java +++ b/src/test/java/com/mirna/hospitalmanagementapi/unit/application/usecase/consultation/FindConsultationByDoctorAndDateUseCaseTest.java @@ -2,8 +2,9 @@ package com.mirna.hospitalmanagementapi.unit.application.usecase.consultation; import static org.junit.jupiter.api.Assertions.assertNotNull; -import java.time.LocalDateTime; +import java.time.OffsetDateTime; // <--- ADDED import java.time.Month; +import java.time.ZoneOffset; // <--- ADDED import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; @@ -29,8 +30,7 @@ import com.mirna.hospitalmanagementapi.domain.repositories.DoctorRepository; import com.mirna.hospitalmanagementapi.domain.repositories.PatientRepository; /** - * - * @author Mirna Gama + * * @author Mirna Gama * @version 1.0 */ @SpringBootTest(classes = HospitalManagementApiApplication.class) @@ -38,56 +38,59 @@ import com.mirna.hospitalmanagementapi.domain.repositories.PatientRepository; @ActiveProfiles("test") public class FindConsultationByDoctorAndDateUseCaseTest { - @Autowired - private FindConsultationByDoctorAndDateUseCase findConsultationByDoctorAndDate; - - private Long testDoctorId; - private LocalDateTime testConsultationDate; - - @Autowired - private ConsultationRepository consultationRepository; - - @Autowired - private PatientRepository patientRepository; - - @Autowired - private DoctorRepository doctorRepository; - - @BeforeAll - public void init() { - testConsultationDate = LocalDateTime.of(2024, Month.JULY, 29, 19, 30, 40); - - PatientDTO patientDTO = new PatientDTO("testPatient", "testPatient@gmail.com", "11111111111", "99999999", - new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345678", "CITY", "ST", null, null)); - Patient patient = this.patientRepository.save(new Patient(patientDTO)); - - DoctorDTO doctorDTO = new DoctorDTO("testDoctor", "testDoctor@gmail.com", "123456", "99999999", Specialty.ORTHOPEDICS, - new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345678", "CITY", "ST", null, null)); - Doctor doctor = this.doctorRepository.save(new Doctor(doctorDTO)); - - testDoctorId = doctor.getId(); - - this.consultationRepository.save(new Consultation(patient, - doctor, testConsultationDate)); - } - - @AfterAll - private void terminate() { - this.consultationRepository.deleteAll(); - this.doctorRepository.deleteAll(); - this.patientRepository.deleteAll(); - } - - /** - * Should execute findConsultationByDoctorAndDate method - * - */ - @Test - @DisplayName("Should execute findConsultationByDoctorAndDate method") - public void testFindConsultationByDoctorAndDate() throws Exception { - Consultation consultation = this.findConsultationByDoctorAndDate.execute(testDoctorId, testConsultationDate); - - assertNotNull(consultation); - } - -} + @Autowired + private FindConsultationByDoctorAndDateUseCase findConsultationByDoctorAndDate; + + private Long testDoctorId; + // <--- CHANGED FROM LocalDateTime + private OffsetDateTime testConsultationDate; + + @Autowired + private ConsultationRepository consultationRepository; + + @Autowired + private PatientRepository patientRepository; + + @Autowired + private DoctorRepository doctorRepository; + + @BeforeAll + public void init() { + // <--- CHANGED FROM LocalDateTime + testConsultationDate = OffsetDateTime.of(2024, Month.JULY.getValue(), 29, 19, 30, 40, 0, ZoneOffset.UTC); + + PatientDTO patientDTO = new PatientDTO("testPatient", "testPatient@gmail.com", "11111111111", "99999999", + new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345678", "CITY", "ST", null, null)); + Patient patient = this.patientRepository.save(new Patient(patientDTO)); + + DoctorDTO doctorDTO = new DoctorDTO("testDoctor", "testDoctor@gmail.com", "123456", "99999999", Specialty.ORTHOPEDICS, + new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345678", "CITY", "ST", null, null)); + Doctor doctor = this.doctorRepository.save(new Doctor(doctorDTO)); + + testDoctorId = doctor.getId(); + + // <--- CHANGED FROM LocalDateTime + this.consultationRepository.save(new Consultation(patient, + doctor, testConsultationDate)); + } + + @AfterAll + private void terminate() { + this.consultationRepository.deleteAll(); + this.doctorRepository.deleteAll(); + this.patientRepository.deleteAll(); + } + + /** + * Should execute findConsultationByDoctorAndDate method + * */ + @Test + @DisplayName("Should execute findConsultationByDoctorAndDate method") + public void testFindConsultationByDoctorAndDate() throws Exception { + // <--- CHANGED FROM LocalDateTime + Consultation consultation = this.findConsultationByDoctorAndDate.execute(testDoctorId, testConsultationDate); + + assertNotNull(consultation); + } + +} \ No newline at end of file diff --git a/src/test/java/com/mirna/hospitalmanagementapi/unit/application/usecase/consultation/FindConsultationByIdUseCaseTest.java b/src/test/java/com/mirna/hospitalmanagementapi/unit/application/usecase/consultation/FindConsultationByIdUseCaseTest.java index a241d98..a82b9b7 100644 --- a/src/test/java/com/mirna/hospitalmanagementapi/unit/application/usecase/consultation/FindConsultationByIdUseCaseTest.java +++ b/src/test/java/com/mirna/hospitalmanagementapi/unit/application/usecase/consultation/FindConsultationByIdUseCaseTest.java @@ -4,6 +4,8 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import java.time.LocalDateTime; import java.time.Month; +import java.time.OffsetDateTime; // <--- เพิ่ม +import java.time.ZoneOffset; // <--- เพิ่ม import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; @@ -29,8 +31,7 @@ import com.mirna.hospitalmanagementapi.domain.repositories.DoctorRepository; import com.mirna.hospitalmanagementapi.domain.repositories.PatientRepository; /** - * - * @author Mirna Gama + * * @author Mirna Gama * @version 1.0 */ @SpringBootTest(classes = HospitalManagementApiApplication.class) @@ -38,53 +39,53 @@ import com.mirna.hospitalmanagementapi.domain.repositories.PatientRepository; @ActiveProfiles("test") public class FindConsultationByIdUseCaseTest { - @Autowired - private FindConsultationByIdUseCase findConsultationById; - - private Long testConsultationId; - - @Autowired - private ConsultationRepository consultationRepository; - - @Autowired - private PatientRepository patientRepository; - - @Autowired - private DoctorRepository doctorRepository; - - @BeforeAll - public void init() { - - PatientDTO patientDTO = new PatientDTO("testPatient", "testPatient@gmail.com", "11111111111", "99999999", - new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345678", "CITY", "ST", null, null)); - Patient patient = this.patientRepository.save(new Patient(patientDTO)); - - DoctorDTO doctorDTO = new DoctorDTO("testDoctor", "testDoctor@gmail.com", "123456", "99999999", Specialty.ORTHOPEDICS, - new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345678", "CITY", "ST", null, null)); - Doctor doctor = this.doctorRepository.save(new Doctor(doctorDTO)); - - Consultation consultation = this.consultationRepository.save(new Consultation(patient, - doctor, LocalDateTime.of(2024, Month.JULY, 29, 19, 30, 40))); - - testConsultationId = consultation.getId(); - } - - @AfterAll - private void terminate() { - this.consultationRepository.deleteAll(); - this.doctorRepository.deleteAll(); - this.patientRepository.deleteAll(); - } - - /** - * Should execute findById method - * - */ - @Test - @DisplayName("Should execute findById method") - public void testFindConsultationById() throws Exception { - Consultation consultation = this.findConsultationById.execute(testConsultationId); - - assertNotNull(consultation); - } -} + @Autowired + private FindConsultationByIdUseCase findConsultationById; + + private Long testConsultationId; + + @Autowired + private ConsultationRepository consultationRepository; + + @Autowired + private PatientRepository patientRepository; + + @Autowired + private DoctorRepository doctorRepository; + + @BeforeAll + public void init() { + + PatientDTO patientDTO = new PatientDTO("testPatient", "testPatient@gmail.com", "11111111111", "99999999", + new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345678", "CITY", "ST", null, null)); + Patient patient = this.patientRepository.save(new Patient(patientDTO)); + + DoctorDTO doctorDTO = new DoctorDTO("testDoctor", "testDoctor@gmail.com", "123456", "99999999", Specialty.ORTHOPEDICS, + new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345678", "CITY", "ST", null, null)); + Doctor doctor = this.doctorRepository.save(new Doctor(doctorDTO)); + + // <--- เปลี่ยนจาก LocalDateTime + Consultation consultation = this.consultationRepository.save(new Consultation(patient, + doctor, OffsetDateTime.of(2024, Month.JULY.getValue(), 29, 19, 30, 40, 0, ZoneOffset.UTC))); + + testConsultationId = consultation.getId(); + } + + @AfterAll + private void terminate() { + this.consultationRepository.deleteAll(); + this.doctorRepository.deleteAll(); + this.patientRepository.deleteAll(); + } + + /** + * Should execute findById method + * */ + @Test + @DisplayName("Should execute findById method") + public void testFindConsultationById() throws Exception { + Consultation consultation = this.findConsultationById.execute(testConsultationId); + + assertNotNull(consultation); + } +} \ No newline at end of file diff --git a/src/test/java/com/mirna/hospitalmanagementapi/unit/application/usecase/consultation/FindConsultationByPatientAndDateUseCaseTest.java b/src/test/java/com/mirna/hospitalmanagementapi/unit/application/usecase/consultation/FindConsultationByPatientAndDateUseCaseTest.java index a21fb09..4088355 100644 --- a/src/test/java/com/mirna/hospitalmanagementapi/unit/application/usecase/consultation/FindConsultationByPatientAndDateUseCaseTest.java +++ b/src/test/java/com/mirna/hospitalmanagementapi/unit/application/usecase/consultation/FindConsultationByPatientAndDateUseCaseTest.java @@ -4,6 +4,8 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import java.time.LocalDateTime; import java.time.Month; +import java.time.OffsetDateTime; // <--- เพิ่ม +import java.time.ZoneOffset; // <--- เพิ่ม import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; @@ -29,8 +31,7 @@ import com.mirna.hospitalmanagementapi.domain.repositories.DoctorRepository; import com.mirna.hospitalmanagementapi.domain.repositories.PatientRepository; /** - * - * @author Mirna Gama + * * @author Mirna Gama * @version 1.0 */ @SpringBootTest(classes = HospitalManagementApiApplication.class) @@ -38,56 +39,59 @@ import com.mirna.hospitalmanagementapi.domain.repositories.PatientRepository; @ActiveProfiles("test") public class FindConsultationByPatientAndDateUseCaseTest { - @Autowired - private FindConsultationByPatientAndDateUseCase findConsultationByPatientAndDate; - - private Long testPatientId; - private LocalDateTime testConsultationDate; - - @Autowired - private ConsultationRepository consultationRepository; - - @Autowired - private PatientRepository patientRepository; - - @Autowired - private DoctorRepository doctorRepository; - - @BeforeAll - public void init() { - testConsultationDate = LocalDateTime.of(2024, Month.JULY, 29, 19, 30, 40); - - PatientDTO patientDTO = new PatientDTO("testPatient", "testPatient@gmail.com", "11111111111", "99999999", - new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345678", "CITY", "ST", null, null)); - Patient patient = this.patientRepository.save(new Patient(patientDTO)); - - DoctorDTO doctorDTO = new DoctorDTO("testDoctor", "testDoctor@gmail.com", "123456", "99999999", Specialty.ORTHOPEDICS, - new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345678", "CITY", "ST", null, null)); - Doctor doctor = this.doctorRepository.save(new Doctor(doctorDTO)); - - testPatientId = patient.getId(); - - this.consultationRepository.save(new Consultation(patient, - doctor, testConsultationDate)); - } - - @AfterAll - private void terminate() { - this.consultationRepository.deleteAll(); - this.doctorRepository.deleteAll(); - this.patientRepository.deleteAll(); - } - - /** - * Should execute findConsultationByPatientAndDate method - * - */ - @Test - @DisplayName("Should execute findConsultationByPatientAndDate method") - public void testfindConsultationByPatientAndDate() throws Exception { - Consultation consultation = this.findConsultationByPatientAndDate.execute(testPatientId, testConsultationDate); - - assertNotNull(consultation); - } - -} + @Autowired + private FindConsultationByPatientAndDateUseCase findConsultationByPatientAndDate; + + private Long testPatientId; + // <--- เปลี่ยนจาก LocalDateTime + private OffsetDateTime testConsultationDate; + + @Autowired + private ConsultationRepository consultationRepository; + + @Autowired + private PatientRepository patientRepository; + + @Autowired + private DoctorRepository doctorRepository; + + @BeforeAll + public void init() { + // <--- เปลี่ยนจาก LocalDateTime + testConsultationDate = OffsetDateTime.of(2024, Month.JULY.getValue(), 29, 19, 30, 40, 0, ZoneOffset.UTC); + + PatientDTO patientDTO = new PatientDTO("testPatient", "testPatient@gmail.com", "11111111111", "99999999", + new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345678", "CITY", "ST", null, null)); + Patient patient = this.patientRepository.save(new Patient(patientDTO)); + + DoctorDTO doctorDTO = new DoctorDTO("testDoctor", "testDoctor@gmail.com", "123456", "99999999", Specialty.ORTHOPEDICS, + new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345678", "CITY", "ST", null, null)); + Doctor doctor = this.doctorRepository.save(new Doctor(doctorDTO)); + + testPatientId = patient.getId(); + + // <--- เปลี่ยนจาก LocalDateTime + this.consultationRepository.save(new Consultation(patient, + doctor, testConsultationDate)); + } + + @AfterAll + private void terminate() { + this.consultationRepository.deleteAll(); + this.doctorRepository.deleteAll(); + this.patientRepository.deleteAll(); + } + + /** + * Should execute findConsultationByPatientAndDate method + * */ + @Test + @DisplayName("Should execute findConsultationByPatientAndDate method") + public void testfindConsultationByPatientAndDate() throws Exception { + // <--- เปลี่ยนจาก LocalDateTime + Consultation consultation = this.findConsultationByPatientAndDate.execute(testPatientId, testConsultationDate); + + assertNotNull(consultation); + } + +} \ No newline at end of file diff --git a/src/test/java/com/mirna/hospitalmanagementapi/unit/application/usecase/consultation/SaveConsultationUseCaseTest.java b/src/test/java/com/mirna/hospitalmanagementapi/unit/application/usecase/consultation/SaveConsultationUseCaseTest.java index a0e0939..773a253 100644 --- a/src/test/java/com/mirna/hospitalmanagementapi/unit/application/usecase/consultation/SaveConsultationUseCaseTest.java +++ b/src/test/java/com/mirna/hospitalmanagementapi/unit/application/usecase/consultation/SaveConsultationUseCaseTest.java @@ -2,7 +2,7 @@ package com.mirna.hospitalmanagementapi.unit.application.usecase.consultation; import static org.junit.jupiter.api.Assertions.assertNotNull; -import java.time.LocalDateTime; +import java.time.OffsetDateTime; // <--- ADDED import java.time.Month; import org.junit.jupiter.api.AfterAll; @@ -29,8 +29,7 @@ import com.mirna.hospitalmanagementapi.domain.repositories.DoctorRepository; import com.mirna.hospitalmanagementapi.domain.repositories.PatientRepository; /** - * - * @author Mirna Gama + * * @author Mirna Gama * @version 1.0 */ @SpringBootTest(classes = HospitalManagementApiApplication.class) @@ -38,53 +37,53 @@ import com.mirna.hospitalmanagementapi.domain.repositories.PatientRepository; @ActiveProfiles("test") public class SaveConsultationUseCaseTest { - @Autowired - private SaveConsultationUseCase saveConsultation; - - private Doctor testDoctor; - private Patient testPatient; - - @Autowired - private ConsultationRepository consultationRepository; - - @Autowired - private PatientRepository patientRepository; - - @Autowired - private DoctorRepository doctorRepository; - - @BeforeAll - public void init() { - PatientDTO patientDTO = new PatientDTO("testPatient", "testPatient@gmail.com", "11111111111", "99999999", - new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345678", "CITY", "ST", null, null)); - testPatient = this.patientRepository.save(new Patient(patientDTO)); - - DoctorDTO doctorDTO = new DoctorDTO("testDoctor", "testDoctor@gmail.com", "123456", "99999999", Specialty.ORTHOPEDICS, - new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345678", "CITY", "ST", null, null)); - testDoctor = this.doctorRepository.save(new Doctor(doctorDTO)); - } - - @AfterAll - private void terminate() { - this.consultationRepository.deleteAll(); - this.doctorRepository.deleteAll(); - this.patientRepository.deleteAll(); - } - - /** - * Should execute save method successfully - * - */ - @Test - @DisplayName("Should execute save method") - public void testSaveConsultation() { - - Consultation consultation = new Consultation(testPatient, - testDoctor, LocalDateTime.now()); - - consultation = saveConsultation.execute(consultation); - - assertNotNull(consultation.getId()); - } + @Autowired + private SaveConsultationUseCase saveConsultation; -} + private Doctor testDoctor; + private Patient testPatient; + + @Autowired + private ConsultationRepository consultationRepository; + + @Autowired + private PatientRepository patientRepository; + + @Autowired + private DoctorRepository doctorRepository; + + @BeforeAll + public void init() { + PatientDTO patientDTO = new PatientDTO("testPatient", "testPatient@gmail.com", "11111111111", "99999999", + new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345678", "CITY", "ST", null, null)); + testPatient = this.patientRepository.save(new Patient(patientDTO)); + + DoctorDTO doctorDTO = new DoctorDTO("testDoctor", "testDoctor@gmail.com", "123456", "99999999", Specialty.ORTHOPEDICS, + new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345678", "CITY", "ST", null, null)); + testDoctor = this.doctorRepository.save(new Doctor(doctorDTO)); + } + + @AfterAll + private void terminate() { + this.consultationRepository.deleteAll(); + this.doctorRepository.deleteAll(); + this.patientRepository.deleteAll(); + } + + /** + * Should execute save method successfully + * */ + @Test + @DisplayName("Should execute save method") + public void testSaveConsultation() { + + // <--- CHANGED FROM LocalDateTime + Consultation consultation = new Consultation(testPatient, + testDoctor, OffsetDateTime.now()); + + consultation = saveConsultation.execute(consultation); + + assertNotNull(consultation.getId()); + } + +} \ No newline at end of file diff --git a/src/test/java/com/mirna/hospitalmanagementapi/unit/application/usecase/doctor/FindOneFreeDoctorBySpecialtyUseCaseTest.java b/src/test/java/com/mirna/hospitalmanagementapi/unit/application/usecase/doctor/FindOneFreeDoctorBySpecialtyUseCaseTest.java index 53acb53..a23c5ca 100644 --- a/src/test/java/com/mirna/hospitalmanagementapi/unit/application/usecase/doctor/FindOneFreeDoctorBySpecialtyUseCaseTest.java +++ b/src/test/java/com/mirna/hospitalmanagementapi/unit/application/usecase/doctor/FindOneFreeDoctorBySpecialtyUseCaseTest.java @@ -2,7 +2,7 @@ package com.mirna.hospitalmanagementapi.unit.application.usecase.doctor; import static org.junit.jupiter.api.Assertions.assertEquals; -import java.time.LocalDateTime; +import java.time.OffsetDateTime; // <--- CHANGED FROM LocalDateTime import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; @@ -23,8 +23,7 @@ import com.mirna.hospitalmanagementapi.domain.enums.Specialty; import com.mirna.hospitalmanagementapi.domain.repositories.DoctorRepository; /** - * - * @author Mirna Gama + * * @author Mirna Gama * @version 1.0 */ @SpringBootTest(classes = HospitalManagementApiApplication.class) @@ -32,35 +31,35 @@ import com.mirna.hospitalmanagementapi.domain.repositories.DoctorRepository; @ActiveProfiles("test") public class FindOneFreeDoctorBySpecialtyUseCaseTest { - @Autowired - private FindOneFreeDoctorBySpecialtyUseCase findOneFreeDoctorBySpecialty; - - @Autowired - private DoctorRepository doctorRepository; - - @BeforeAll - public void init() { - DoctorDTO doctorDTO = new DoctorDTO("test1", "test1@gmail.com", "123456", "99999999", Specialty.ORTHOPEDICS, - new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345678", "CITY", "ST", null, null)); - - doctorRepository.save(new Doctor(doctorDTO)); - } - - @AfterAll - public void terminate() { - doctorRepository.deleteAll(); - } - - /** - * Should execute findOneFreeDoctorBySpecialty method - * - */ - @Test - @DisplayName("Should execute findOneFreeDoctorBySpecialty method with pagination") - public void testfindOneFreeDoctorBySpecialty() throws Exception { - - Doctor doctor = findOneFreeDoctorBySpecialty.execute(Specialty.ORTHOPEDICS, LocalDateTime.now()); + @Autowired + private FindOneFreeDoctorBySpecialtyUseCase findOneFreeDoctorBySpecialty; - assertEquals(doctor.getSpecialty(), Specialty.ORTHOPEDICS); - } -} + @Autowired + private DoctorRepository doctorRepository; + + @BeforeAll + public void init() { + DoctorDTO doctorDTO = new DoctorDTO("test1", "test1@gmail.com", "123456", "99999999", Specialty.ORTHOPEDICS, + new AddressDTO("TEST STREET", "NEIGHBORHOOD", "12345678", "CITY", "ST", null, null)); + + doctorRepository.save(new Doctor(doctorDTO)); + } + + @AfterAll + public void terminate() { + doctorRepository.deleteAll(); + } + + /** + * Should execute findOneFreeDoctorBySpecialty method + * */ + @Test + @DisplayName("Should execute findOneFreeDoctorBySpecialty method with pagination") + public void testfindOneFreeDoctorBySpecialty() throws Exception { + + // <--- CHANGED FROM LocalDateTime + Doctor doctor = findOneFreeDoctorBySpecialty.execute(Specialty.ORTHOPEDICS, OffsetDateTime.now()); + + assertEquals(doctor.getSpecialty(), Specialty.ORTHOPEDICS); + } +} \ No newline at end of file diff --git a/src/test/java/com/mirna/hospitalmanagementapi/unit/application/usecase/jwt/GetJWTSubjectUseCaseTest.java b/src/test/java/com/mirna/hospitalmanagementapi/unit/application/usecase/jwt/GetJWTSubjectUseCaseTest.java index cb192d6..b63efd0 100644 --- a/src/test/java/com/mirna/hospitalmanagementapi/unit/application/usecase/jwt/GetJWTSubjectUseCaseTest.java +++ b/src/test/java/com/mirna/hospitalmanagementapi/unit/application/usecase/jwt/GetJWTSubjectUseCaseTest.java @@ -37,7 +37,7 @@ public class GetJWTSubjectUseCaseTest { @BeforeAll public void init() { - testToken = JWT.create().withIssuer("Hospital-Management-API") + testToken = JWT.create().withIssuer("Hospital-Management-API (Extended Version)") .withSubject("test") .withClaim("id", 1L) .sign(Algorithm.HMAC256(secret)); diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties new file mode 100644 index 0000000..a9af501 --- /dev/null +++ b/src/test/resources/application.properties @@ -0,0 +1,27 @@ +# datasource +spring.datasource.driverClassName=org.h2.Driver +spring.datasource.url=jdbc:h2:mem:testdb +spring.datasource.username=sa +spring.datasource.password= + +# jpa +spring.jpa.database-platform=org.hibernate.dialect.H2Dialect +spring.jpa.hibernate.ddl-auto=update +spring.jpa.properties.hibernate.show_sql=true +spring.jpa.properties.hibernate.format_sql=true + +# h2 +spring.h2.console.enabled=true +spring.h2.console.path=/h2-console + +api.security.token.secret=${JWT_SECRET:S3CR3T} + +# --- MinIO --- +minio.url=https://minio.softwarecraft.tech +minio.access-key=UQ9MwiWu4t7ljWh5U1RL +minio.secret-key=TQea1jSA4JWEzwX0jHFhQB4YVG1fepWYbA3ZlY27 +minio.bucket-name=medical-images + +spring.flyway.enabled=false +spring.flyway.baseline-on-migrate=true +spring.flyway.clean-disabled=false \ No newline at end of file