Compare commits

...

10 Commits

Author SHA1 Message Date
5db80d5299 Initial commit - Extended Hospital Management API v2.0 2025-09-18 10:45:49 +07:00
Mirna Gama
c372590e28 [develop] Add version tag 2024-01-31 14:52:17 -03:00
Mirna Gama
52969bde9a [develop] Add instructions for running the applications and tests 2024-01-31 14:52:17 -03:00
Mirna Gama
d5a78e5375 [R10] Feature completed 2024-01-31 14:52:17 -03:00
Mirna Gama
48a026fa7e [R10] Avoid DataIntegrityViolation 2024-01-31 14:52:17 -03:00
Mirna Gama
0b053a99c6 [R10] Add docker workflow 2024-01-31 14:52:17 -03:00
Mirna Gama
b9706a4a64 [R10] Add spring datasource credentials variables 2024-01-31 14:52:17 -03:00
Mirna Gama
ece9d2037b [R10] Test properties are not fully recognized 2024-01-31 14:52:17 -03:00
Mirna Gama
5d1c28ad1e [R10] Update README with /GET and /DELETE consultations endpoints 2024-01-31 14:52:17 -03:00
Mirna Gama
e322ebf199 [R10] Change response code in POST consultations 2024-01-31 14:52:17 -03:00
232 changed files with 9749 additions and 2697 deletions

View File

@ -28,4 +28,15 @@ jobs:
distribution: 'temurin' distribution: 'temurin'
cache: maven cache: maven
- name: Build with Maven - name: Build with Maven
run: mvn -B package -DJWT_SECRET=${{ secrets.JWT_SECRET }} --file pom.xml run: mvn -B package -Dspring.profiles.active=test -DJWT_SECRET=${{ secrets.JWT_SECRET }} --file pom.xml
- name: Build & push Docker image
uses: mr-smithers-excellent/docker-build-push@v5
with:
image: mbgama/hospital-management-api
tags: latest
registry: docker.io
dockerfile: Dockerfile
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}

2
.gitignore vendored
View File

@ -31,3 +31,5 @@ build/
### VS Code ### ### VS Code ###
.vscode/ .vscode/
.env

14
Dockerfile Normal file
View File

@ -0,0 +1,14 @@
# ใช้ 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"]

787
README.md
View File

@ -1,557 +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 ![API Version](https://img.shields.io/badge/API%20Version-v2.0-blue.svg)
Hospital Management API built in Spring Boot ![Developed by](https://img.shields.io/badge/Developed%20by-FlookSP-green.svg)
### Prerequisites: 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.
- Spring Boot 3.2.1
- JDK 17
- Maven 4.0.0
### Features ---
- [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
- [ ] R10 - Consultation Cancellation
## API Documentation - /swagger-ui/index.html ## ✨ Improvements and New Features
### authentication ### 1. Code Structure and Architecture
#### POST - [**/api/auth/register**] - Register a new user * **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.
- **Body:** ### 2. Endpoint and Data Handling Changes
```
{ * **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.
"login" (string, required), * **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.
"password" (string, required),
} ### 3. Major Test Suite Refinement
* **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.
### 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 <this repository>
cd hospital-management-api
``` ```
- **Responses:** ### 2⃣ Set Environment Variables for IntelliJ
| Code | Description | เปิด IntelliJ → ไปที่ Run → Edit Configurations
| ------------- | ------------- |
| `200` | _Successful operation_ |
| `400` | _Validation Error_ |
#### POST - [**/api/auth/login**] - Perform the login เลือก Configuration ของ Spring Boot ของโปรเจค
- **Body:** ในส่วน Environment Variables กำหนดค่าตามนี้:
``` ```
{ POSTGRES_HOST=localhost
"login" (string, required), POSTGRES_PORT=5432
"password" (string, required), POSTGRES_DB=<your_database_name>
} POSTGRES_USER=<your_database_user>
POSTGRES_PASSWORD=<your_database_password>
JWT_SECRET=<your_jwt_secret>
MINIO_URL=<your_minio_url>
MINIO_ACCESS=<your_minio_access_key>
MINIO_SECRET=<your_minio_secret_key>
MINIO_BUCKET=<your_minio_bucket>
PGADMIN_DEFAULT_EMAIL=<your_pgadmin_email>
PGADMIN_DEFAULT_PASSWORD=<your_pgadmin_password>
``` ```
- **Responses:** ### 3⃣ Run PostgreSQL and pgAdmin
| Code | Description |
| ------------- | ------------- |
| `200` | _Successful operation_ |
| `400` | _Validation Error_ |
| `403` | _Incorrect credentials_ |
### doctors
#### POST - [**/api/v1.0/doctors**] - Adds a new doctor
- **Body:**
``` ```
{ # รันเฉพาะ PostgreSQL และ pgAdmin
"name" (string, required), docker-compose up -d postgresdb pgadmin
"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)
}
}
``` ```
- **Request Headers:** ### 4⃣ Run Spring Boot Application
| Key | Description | เปิด IntelliJ
| ------------- | ------------- |
| `Authorization` | _Authorization token_ |
- **Responses:** Run Spring Boot จาก Configuration ที่ตั้งค่า Environment Variables แล้ว
| Code | Description | ✅ แอปจะเชื่อมต่อ PostgreSQL และ MinIO ตามค่าที่กำหนด
| ------------- | ------------- |
| `201` | _Successfully created_ |
| `400` | _Validation Error_ |
| `403` | _Unauthorized / Invalid token_ |
#### GET - [**/api/v1.0/doctors/{id}**] - Get an existing doctor ---
- **Response Body Example:** ### Future Development Plan (v2.1+)
```
{
"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
}
}
```
- **Request Headers:** We will continue to develop on Spring Boot following standard best practices to add the following capabilities:
| Key | Description | **AI-Driven Enhancements**
| ------------- | ------------- | - Implement predictive analytics for patient admissions and resource allocation.
| `Authorization` | _Authorization token_ | - 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:** ## 🙏 Credits
Extended version of MirnaGama/hospital-management-api.
| Code | Description | This continuation improves code quality, adds features, and addresses real-world issues.
| ------------- | ------------- |
| `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 |
| ------------- | ------------- |
| `200` | _Successful operation |
| `400` | _Validation Error_ |
| `403` | _Unauthorized / Invalid token_ |
| `404` | _Entity not found_ |

51
docker-compose.yml Normal file
View File

@ -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:

39
pom.xml
View File

@ -10,7 +10,7 @@
</parent> </parent>
<groupId>com.mirna</groupId> <groupId>com.mirna</groupId>
<artifactId>hospital-management-api</artifactId> <artifactId>hospital-management-api</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>1.0</version>
<name>hospital-management-api</name> <name>hospital-management-api</name>
<description>Hospital Management API project for Spring Boot</description> <description>Hospital Management API project for Spring Boot</description>
<properties> <properties>
@ -40,24 +40,37 @@
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId> <artifactId>spring-boot-starter-validation</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>
<!-- PostgreSQL JDBC Driver -->
<dependency> <dependency>
<groupId>org.flywaydb</groupId> <groupId>org.postgresql</groupId>
<artifactId>flyway-core</artifactId> <artifactId>postgresql</artifactId>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-mysql</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope> <scope>runtime</scope>
</dependency> </dependency>
<!-- Flyway PostgreSQL (optional ตั้งแต่ Flyway 7+ ไม่จำเป็นถ้าใช้ core) -->
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
<version>9.22.3</version>
</dependency>
<!-- MinIO Java Client -->
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.5.2</version>
</dependency>
<dependency> <dependency>
<groupId>com.h2database</groupId> <groupId>com.h2database</groupId>
<artifactId>h2</artifactId> <artifactId>h2</artifactId>
<version>2.1.214</version> <version>2.2.220</version>
<scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>

View File

@ -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<Billing> 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<Billing> getBillingById(@PathVariable Long id) {
Billing billing = billingService.getBillingById(id);
return ResponseEntity.ok(billing);
}
@PutMapping("/{id}")
public ResponseEntity<Billing> updateBilling(@PathVariable Long id, @RequestBody @Valid BillingDTO billingDTO) {
Billing updatedBilling = billingService.updateBilling(id, billingDTO);
return ResponseEntity.ok(updatedBilling);
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteBilling(@PathVariable Long id) {
billingService.deleteBilling(id);
return ResponseEntity.noContent().build();
}
}

View File

@ -21,6 +21,10 @@ import com.mirna.hospitalmanagementapi.domain.entities.Consultation;
import com.mirna.hospitalmanagementapi.domain.exceptions.ConsultationValidationException; import com.mirna.hospitalmanagementapi.domain.exceptions.ConsultationValidationException;
import com.mirna.hospitalmanagementapi.domain.services.ConsultationService; 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 io.swagger.v3.oas.annotations.security.SecurityRequirement;
import jakarta.validation.Valid; import jakarta.validation.Valid;
@ -35,59 +39,84 @@ import jakarta.validation.Valid;
@SecurityRequirement(name = "bearer-key") @SecurityRequirement(name = "bearer-key")
public class ConsultationController { public class ConsultationController {
@Autowired @Autowired
private ConsultationService consultationService; 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<Object> postConsultation(@RequestBody @Valid ConsultationDTO consultationDTO) throws ConsultationValidationException {
Consultation consultation = consultationService.addConsultation(consultationDTO);
UriComponents uriComponents = UriComponentsBuilder
.fromUriString("/api/v1.0/consultations/{id}")
.encode()
.build();
URI uri = uriComponents.expand(consultation.getId()).toUri(); /**
* Post method to create a new Consultation object based on the provided DTO.
return ResponseEntity.created(uri).body(consultation); *
} * @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
* Get method to receive a Consultation record by the provided ID * a 400-level error if there is a validation error
* * @throws ConsultationValidationException if there is a validation error
* @param id A long representing the consultation's unique identifier */
* @PostMapping
* @return A response entity containing the corresponding consultation if successful, or @Operation(
* a 400-level error if it is non-existent summary = "สร้างการนัดหมายใหม่",
*/ description = "อนุญาตให้ผู้ป่วยจองการนัดหมายกับแพทย์ที่ระบุหรือตามแผนก",
@GetMapping("/{id}") requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(
public ResponseEntity<Object> getConsultation(@PathVariable Long id) { content = @Content(
Consultation consultation = consultationService.findConsultationById(id); mediaType = "application/json",
examples = {
return ResponseEntity.ok(consultation); @ExampleObject(
} name = "ตัวอย่าง: จองด้วยรหัสแพทย์",
summary = "จองนัดหมายกับแพทย์ที่ต้องการโดยตรง",
/** value = "{\"doctorId\": 1, \"patientId\": 1, \"consultationDate\": \"2025-09-15T10:00:00+07:00\", \"specialty\": null}"
* Delete method to update a new Consultation object based on the provided DTO. ),
* @ExampleObject(
* @param consultationCanceledDTO The data transfer object containing data to update Consultation name = "ตัวอย่าง: จองด้วยแผนก",
* entity. summary = "จองนัดหมายโดยให้ระบบเลือกแพทย์ที่ว่างในแผนกที่ระบุ",
* value = "{\"doctorId\": null, \"patientId\": 1, \"consultationDate\": \"2025-09-15T11:00:00+07:00\", \"specialty\": \"ORTHOPEDICS\"}"
* @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<Object> deleteConsultation(@RequestBody @Valid ConsultationCanceledDTO consultationCanceledDTO) { responses = {
return ResponseEntity.ok(consultationService.cancelConsultation(consultationCanceledDTO)); @ApiResponse(responseCode = "201", description = "สร้างการนัดหมายสำเร็จ"),
} @ApiResponse(responseCode = "400", description = "ข้อมูลใน Request Body ไม่ถูกต้อง หรือการตรวจสอบล้มเหลว")
} }
)
public ResponseEntity<Object> 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<Object> 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<Object> deleteConsultation(@RequestBody @Valid ConsultationCanceledDTO consultationCanceledDTO) {
return ResponseEntity.ok(consultationService.cancelConsultation(consultationCanceledDTO));
}
}

View File

@ -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<DoctorSchedule> 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);
}
}

View File

@ -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<InsuranceClaim> 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<InsuranceClaim> getInsuranceClaimById(@PathVariable Long id) {
InsuranceClaim claim = insuranceClaimService.getInsuranceClaimById(id);
return ResponseEntity.ok(claim);
}
@GetMapping
public ResponseEntity<Page<InsuranceClaim>> getAllInsuranceClaims(@PageableDefault(size = 10, page = 0, sort = {"id"}) Pageable pageable) {
Page<InsuranceClaim> claims = insuranceClaimService.getAllInsuranceClaims(pageable);
return ResponseEntity.ok(claims);
}
@PutMapping("/{id}")
public ResponseEntity<InsuranceClaim> updateInsuranceClaim(@PathVariable Long id, @RequestBody @Valid InsuranceClaimDTO insuranceClaimDTO) {
InsuranceClaim updatedClaim = insuranceClaimService.updateInsuranceClaim(id, insuranceClaimDTO);
return ResponseEntity.ok(updatedClaim);
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteInsuranceClaim(@PathVariable Long id) {
insuranceClaimService.deleteInsuranceClaim(id);
return ResponseEntity.noContent().build();
}
}

View File

@ -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<InsuranceProvider> 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<List<InsuranceProvider>> getAllInsuranceProviders() {
List<InsuranceProvider> providers = insuranceProviderService.getAllInsuranceProviders();
return ResponseEntity.ok(providers);
}
@GetMapping("/{id}")
@Operation(summary = "ดึงข้อมูลผู้ให้บริการประกันตามรหัส (ID)", security = @SecurityRequirement(name = "bearer-key"))
public ResponseEntity<InsuranceProvider> 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<InsuranceProvider> 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<Void> deleteInsuranceProvider(@PathVariable Long id) throws EntityNotFoundException {
insuranceProviderService.deleteInsuranceProvider(id);
return ResponseEntity.noContent().build();
}
}

View File

@ -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<InventoryItemResponseDTO> 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<InventoryItemResponseDTO> getInventoryItem(@PathVariable UUID id) {
InventoryItemResponseDTO item = inventoryItemService.getInventoryItemById(id);
return ResponseEntity.ok(item);
}
@PutMapping("/{id}")
@Operation(summary = "อัปเดตข้อมูลรายการสินค้าคงคลัง", security = @SecurityRequirement(name = "bearer-key"))
public ResponseEntity<InventoryItemResponseDTO> 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<Void> deleteInventoryItem(@PathVariable UUID id) {
inventoryItemService.deleteInventoryItem(id);
return ResponseEntity.noContent().build();
}
}

View File

@ -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<InventoryItemType> createItemType(@RequestBody @Valid ItemTypeDTO itemTypeDTO) {
InventoryItemType newItemType = itemTypeService.addItemType(itemTypeDTO);
return ResponseEntity.status(HttpStatus.CREATED).body(newItemType);
}
}

View File

@ -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<InventorySupplier> createSupplier(@RequestBody @Valid SupplierDTO supplierDTO) {
InventorySupplier newSupplier = supplierService.addSupplier(supplierDTO);
return ResponseEntity.status(HttpStatus.CREATED).body(newSupplier);
}
}

View File

@ -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<InventoryTransactionResponseDTO> createTransaction(@RequestBody @Valid InventoryTransactionDTO transactionDTO) {
InventoryTransactionResponseDTO newTransaction = transactionService.addTransaction(transactionDTO);
return ResponseEntity.status(HttpStatus.CREATED).body(newTransaction);
}
}

View File

@ -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<LabResult> 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<LabResult> getLabResultById(@PathVariable UUID id) {
LabResult labResult = labResultService.getLabResultById(id);
return ResponseEntity.ok(labResult);
}
}

View File

@ -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<MedicalEquipmentScheduleResponseDTO> 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<MedicalEquipmentScheduleResponseDTO> 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<Page<MedicalEquipmentScheduleResponseDTO>> getAllMedicalEquipmentSchedules(@PageableDefault(size = 10, page = 0, sort = {"startTime"}) Pageable pageable) {
Page<MedicalEquipmentScheduleResponseDTO> schedules = medicalEquipmentScheduleService.getAllMedicalEquipmentSchedules(pageable);
return ResponseEntity.ok(schedules);
}
}

View File

@ -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<MedicalImage> 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<Resource> 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);
}
}

View File

@ -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<MedicalRecord> 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<MedicalRecord> 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<MedicalRecord> getMedicalRecordDetails(@PathVariable Long id) {
MedicalRecord medicalRecord = medicalRecordService.getMedicalRecordDetails(id);
return ResponseEntity.ok(medicalRecord);
}*/
@PutMapping("/{id}")
@Operation(summary = "อัปเดตบันทึกทางการแพทย์", security = @SecurityRequirement(name = "bearer-key"))
public ResponseEntity<MedicalRecord> 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<Void> deleteMedicalRecord(@PathVariable Long id) {
medicalRecordService.deleteMedicalRecord(id);
return ResponseEntity.noContent().build();
}
}

View File

@ -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<Nurse> 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<Page<Nurse>> getAllNurses(@PageableDefault(size = 10, page = 0, sort = {"name"}) Pageable pageable) {
Page<Nurse> nurses = nurseService.getAllNurses(pageable);
return ResponseEntity.ok(nurses);
}
@GetMapping("/{id}")
@Operation(summary = "ดึงข้อมูลพยาบาลตามรหัส (ID)", security = @SecurityRequirement(name = "bearer-key"))
public ResponseEntity<Nurse> 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<Nurse> 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<Void> deleteNurse(@PathVariable Long id) throws EntityNotFoundException {
nurseService.deleteNurse(id);
return ResponseEntity.noContent().build();
}
}

View File

@ -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<Object> 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<NurseSchedule> 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<Page<NurseSchedule>> getAllNurseSchedules(@PageableDefault(size = 10, page = 0, sort = {"startTime"}) Pageable pageable) {
Page<NurseSchedule> schedules = nurseScheduleService.getAllNurseSchedules(pageable);
return ResponseEntity.ok(schedules);
}
}

View File

@ -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<OperatingRoom> 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<Page<OperatingRoom>> getAllOperatingRooms(@PageableDefault(size = 10, page = 0, sort = {"id"}) Pageable pageable) {
Page<OperatingRoom> rooms = operatingRoomService.getAllOperatingRooms(pageable);
return ResponseEntity.ok(rooms);
}
@GetMapping("/{id}")
@Operation(summary = "ดึงข้อมูลห้องผ่าตัดตามรหัส (ID)", security = @SecurityRequirement(name = "bearer-key"))
public ResponseEntity<OperatingRoom> 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<OperatingRoom> 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<Void> deleteOperatingRoom(@PathVariable Long id) throws EntityNotFoundException {
operatingRoomService.deleteOperatingRoom(id);
return ResponseEntity.noContent().build();
}
}

View File

@ -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<OperatingRoomSchedule> 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<OperatingRoomSchedule> 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<Page<OperatingRoomSchedule>> getAllOperatingRoomSchedules(@PageableDefault(size = 10, page = 0, sort = {"startTime"}) Pageable pageable) {
Page<OperatingRoomSchedule> schedules = operatingRoomScheduleService.getAllOperatingRoomSchedules(pageable);
return ResponseEntity.ok(schedules);
}
}

View File

@ -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<Payment> createPayment(@RequestBody @Valid PaymentDTO paymentDTO) {
Payment newPayment = paymentService.createPayment(paymentDTO);
return ResponseEntity.status(HttpStatus.CREATED).body(newPayment);
}
@GetMapping("/{id}")
public ResponseEntity<Payment> getPaymentById(@PathVariable Long id) {
Payment payment = paymentService.getPaymentById(id);
return ResponseEntity.ok(payment);
}
@GetMapping
public ResponseEntity<Page<Payment>> getAllPayments(@PageableDefault(size = 10, sort = {"paymentDate"}) Pageable pageable) {
Page<Payment> payments = paymentService.getAllPayments(pageable);
return ResponseEntity.ok(payments);
}
}

View File

@ -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<PrescriptionResponseDTO> 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<PrescriptionResponseDTO> 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<PrescriptionResponseDTO> 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);
}
}

View File

@ -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<List<FinancialReportDTO>> getFinancialOverview(
@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate startDate,
@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate endDate) {
List<FinancialReportDTO> financialData = reportService.getFinancialReport(startDate, endDate);
if (financialData.isEmpty()) {
return ResponseEntity.noContent().build();
}
return ResponseEntity.ok(financialData);
}
@GetMapping("/inventory/low-stock")
public ResponseEntity<Page<LowStockItemDTO>> getLowStockReport(@PageableDefault(size = 10, sort = "itemName") Pageable pageable) {
Page<LowStockItemDTO> 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<AppointmentReportDTO> 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);
}
}

View File

@ -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<Staff> 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);
}
}

View File

@ -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<User> 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<User> 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<User> 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<Void> 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<Void> 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<String> handleAccessDeniedException(AccessDeniedException ex) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(ex.getMessage());
}
@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<String> handleIllegalArgumentException(IllegalArgumentException ex) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ex.getMessage());
}
}

View File

@ -1,10 +1,8 @@
package com.mirna.hospitalmanagementapi.application.controllers.auth; package com.mirna.hospitalmanagementapi.application.controllers.auth;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; 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.PostMapping;
import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping; 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.entities.auth.User;
import com.mirna.hospitalmanagementapi.domain.services.auth.AuthService; import com.mirna.hospitalmanagementapi.domain.services.auth.AuthService;
import com.mirna.hospitalmanagementapi.domain.services.auth.jwt.TokenService; import com.mirna.hospitalmanagementapi.domain.services.auth.jwt.TokenService;
import com.mirna.hospitalmanagementapi.domain.services.PatientService;
import jakarta.validation.Valid; 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. * A Spring REST controller for managing authentication and user information.
@ -26,46 +44,127 @@ import jakarta.validation.Valid;
@RestController @RestController
@RequestMapping("/api/auth") @RequestMapping("/api/auth")
public class AuthenticationController { 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<Object> 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") @Autowired
public ResponseEntity<Object> register(@RequestBody @Valid UserDTO userDTO) { private AuthService authService;
User user = authService.register(userDTO);
return ResponseEntity.ok(user);
}
} @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<Object> 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<Object> 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<Object> 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<Object> 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<String> 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<Object> 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<Staff> 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);
}*/
}

View File

@ -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<BillableItem> 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);
}
}

View File

@ -1,12 +1,8 @@
package com.mirna.hospitalmanagementapi.application.services; package com.mirna.hospitalmanagementapi.application.services;
import org.apache.coyote.BadRequestException;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; 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.FindConsultationByIdUseCase;
import com.mirna.hospitalmanagementapi.application.usecase.consultation.FindConsultationByPatientAndDateUseCase;
import com.mirna.hospitalmanagementapi.application.usecase.consultation.SaveConsultationUseCase; import com.mirna.hospitalmanagementapi.application.usecase.consultation.SaveConsultationUseCase;
import com.mirna.hospitalmanagementapi.application.usecase.doctor.FindDoctorByIdUseCase; import com.mirna.hospitalmanagementapi.application.usecase.doctor.FindDoctorByIdUseCase;
import com.mirna.hospitalmanagementapi.application.usecase.doctor.FindOneFreeDoctorBySpecialtyUseCase; 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.entities.Patient;
import com.mirna.hospitalmanagementapi.domain.exceptions.ConsultationValidationException; import com.mirna.hospitalmanagementapi.domain.exceptions.ConsultationValidationException;
import com.mirna.hospitalmanagementapi.domain.services.ConsultationService; 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 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 @Service
public class ConsultationServiceImpl implements ConsultationService { public class ConsultationServiceImpl implements ConsultationService {
@Autowired @Autowired
private SaveConsultationUseCase saveConsultation; private SaveConsultationUseCase saveConsultation;
@Autowired @Autowired
private FindConsultationByIdUseCase findConsultationById; private FindConsultationByIdUseCase findConsultationById;
@Autowired
private FindConsultationByDoctorAndDateUseCase findConsultationByDoctorAndDate;
@Autowired @Autowired
private FindConsultationByPatientAndDateUseCase findConsultationByPatientAndDate; private ConsultationRepository consultationRepository;
@Autowired @Autowired
private FindPatientByIdUseCase findPatientById; private FindPatientByIdUseCase findPatientById;
@Autowired @Autowired
private FindDoctorByIdUseCase findDoctorById; private FindDoctorByIdUseCase findDoctorById;
@Autowired @Autowired
private FindOneFreeDoctorBySpecialtyUseCase findOneFreeDoctorBySpecialty; private FindOneFreeDoctorBySpecialtyUseCase findOneFreeDoctorBySpecialty;
/** @Autowired
* Adds a new consultation to the repository. private DoctorScheduleService doctorScheduleService;
*
* @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 {
Patient patient = findPatientById.execute(consultationDTO.patientId()); private static final ZoneOffset BANGKOK_ZONE_OFFSET = ZoneOffset.ofHours(7);
if (!patient.getActive()) @Override
throw new ConsultationValidationException("This patient is not active"); @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) Consultation consultation = new Consultation(patient, doctor, consultationDTO.consultationDate());
throw new ConsultationValidationException("This patient is not free on this date");
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()) if (patient == null) {
throw new ConsultationValidationException("This doctor is not active"); 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) OffsetDateTime consultationDateInBkkZone = consultationDate.withOffsetSameInstant(BANGKOK_ZONE_OFFSET);
throw new ConsultationValidationException("This doctor is not free on this date"); 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(), private Doctor validateAndFindDoctor(ConsultationDTO consultationDTO) throws ConsultationValidationException {
consultationDTO.consultationDate()); 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"); if (consultationDTO.doctorId() != null) {
doctor = findDoctorById.execute(consultationDTO.doctorId());
} else { } else if (consultationDTO.specialty() != null) {
throw new ConsultationValidationException("At least the specialty or doctor ID must be filled in"); 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);
/** OffsetDateTime startOfDay = consultationDateInBkkZone.with(LocalTime.MIN);
* Finds a stored consultation by id. OffsetDateTime endOfDay = consultationDateInBkkZone.with(LocalTime.MAX);
*
* @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);
if (consultation == null) // แก้ไขบรรทัดนี้: ส่งพารามิเตอร์ที่ถูกต้องไปยัง Repository
throw new EntityNotFoundException("No existing consultation with this id"); if (consultationRepository.findConsultationByDoctorAndDate(doctor.getId(), startOfDay, endOfDay) != null) {
throw new ConsultationValidationException("This doctor is not free on this date");
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());
consultation.setCanceled(true); boolean isDoctorAvailable = doctorScheduleService.isDoctorAvailable(
consultation.setReasonCancellation(consultationCanceledDTO.reasonCancellation()); 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);
}
}

View File

@ -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);
}
}

View File

@ -99,14 +99,14 @@ public class DoctorServiceImpl implements DoctorService {
if (doctor == null) { if (doctor == null) {
throw new EntityNotFoundException("No existing doctor with this id"); throw new EntityNotFoundException("No existing doctor with this id");
} }
if (doctorUpdatedDataDTO.name() != null) { if (doctorUpdatedDataDTO.name() != null) {
doctor.setName(doctorUpdatedDataDTO.name()); doctor.setName(doctorUpdatedDataDTO.name());
} }
if (doctorUpdatedDataDTO.telephone() != null) { if (doctorUpdatedDataDTO.telephone() != null) {
doctor.setName(doctorUpdatedDataDTO.telephone()); doctor.setTelephone(doctorUpdatedDataDTO.telephone());
} }
if (doctorUpdatedDataDTO.address() != null) { if (doctorUpdatedDataDTO.address() != null) {

View File

@ -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<InsuranceClaim> 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);
}
}

View File

@ -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<InsuranceProvider> 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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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<MedicalEquipmentSchedule> 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<MedicalEquipmentScheduleResponseDTO> getAllMedicalEquipmentSchedules(Pageable pageable) {
Page<MedicalEquipmentSchedule> schedulesPage = medicalEquipmentScheduleRepository.findAll(pageable);
return schedulesPage.map(MedicalEquipmentScheduleResponseDTO::new);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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<NurseSchedule> 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<NurseSchedule> getAllNurseSchedules(Pageable pageable) {
return nurseScheduleRepository.findAll(pageable);
}
}

View File

@ -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<Nurse> 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);
}
}

View File

@ -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<OperatingRoomSchedule> 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<OperatingRoomSchedule> getAllOperatingRoomSchedules(Pageable pageable) {
return operatingRoomScheduleRepository.findAll(pageable);
}
}

View File

@ -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<OperatingRoom> 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);
}
}

View File

@ -4,6 +4,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service; 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.FindPatientByIdUseCase;
import com.mirna.hospitalmanagementapi.application.usecase.patient.FindPatientsUseCase; 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.Address;
import com.mirna.hospitalmanagementapi.domain.entities.Patient; import com.mirna.hospitalmanagementapi.domain.entities.Patient;
import com.mirna.hospitalmanagementapi.domain.services.PatientService; 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; import jakarta.persistence.EntityNotFoundException;
@ -29,136 +35,177 @@ import jakarta.persistence.EntityNotFoundException;
@Service @Service
public class PatientServiceImpl implements PatientService { public class PatientServiceImpl implements PatientService {
@Autowired @Autowired
private SavePatientUseCase savePatient; 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
* Finds a stored patient by id. private FindPatientByIdUseCase findPatientById;
*
* @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
* Finds patients from the database. private FindPatientsUseCase findPatients;
*
* @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<PatientPublicDataDTO> findPatients(Pageable pageable) {
return findPatients.execute(pageable).map(PatientPublicDataDTO::new);
}
/** @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<PatientPublicDataDTO> findPatients(Pageable pageable) {
return findPatients.execute(pageable).map(PatientPublicDataDTO::new);
}
/**
* Updates an existing patient record * Updates an existing patient record
* @param patientUpdatedDataDTO Data transfer object containing the patient updated data along with their corresponding id * @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.
* @return The updated patient if successful, or null if there is an error. */
*/ @Override
@Override public Patient updatePatient(PatientUpdatedDataDTO patientUpdatedDataDTO) {
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) {
AddressDTO addressUpdatedDataDTO = patientUpdatedDataDTO.address(); Patient patient = findPatientById.execute(patientUpdatedDataDTO.id());
Address address = patient.getAddress();
if (addressUpdatedDataDTO.street() != null) { if (patient == null) throw new EntityNotFoundException("No existing patient with this id");
address.setStreet(addressUpdatedDataDTO.street());
}
if (addressUpdatedDataDTO.neighborhood() != null) { if (patientUpdatedDataDTO.name() != null) patient.setName(patientUpdatedDataDTO.name());
address.setNeighborhood(addressUpdatedDataDTO.neighborhood());
}
if (addressUpdatedDataDTO.city() != null) { if (patientUpdatedDataDTO.telephone() != null) patient.setTelephone(patientUpdatedDataDTO.telephone());
address.setCity(addressUpdatedDataDTO.city());
}
if (addressUpdatedDataDTO.zipCode() != null) { if (patientUpdatedDataDTO.address() != null) {
address.setZipCode(addressUpdatedDataDTO.zipCode());
}
if (addressUpdatedDataDTO.state() != null) { AddressDTO addressUpdatedDataDTO = patientUpdatedDataDTO.address();
address.setState(addressUpdatedDataDTO.state()); Address address = patient.getAddress();
}
if (addressUpdatedDataDTO.additionalDetails() != null) { if (addressUpdatedDataDTO.street() != null) {
address.setAdditionalDetails(addressUpdatedDataDTO.additionalDetails()); address.setStreet(addressUpdatedDataDTO.street());
} }
if (addressUpdatedDataDTO.houseNumber() != null) { if (addressUpdatedDataDTO.neighborhood() != null) {
address.setHouseNumber(addressUpdatedDataDTO.houseNumber()); address.setNeighborhood(addressUpdatedDataDTO.neighborhood());
} }
patient.setAddress(address); if (addressUpdatedDataDTO.city() != null) {
} address.setCity(addressUpdatedDataDTO.city());
}
patient = savePatient.execute(patient); if (addressUpdatedDataDTO.zipCode() != null) {
address.setZipCode(addressUpdatedDataDTO.zipCode());
return patient; }
}
/** if (addressUpdatedDataDTO.state() != null) {
* Deactivates an existing patient record by provided id address.setState(addressUpdatedDataDTO.state());
* }
* @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) { if (addressUpdatedDataDTO.additionalDetails() != null) {
throw new EntityNotFoundException("No existing patient with this id"); address.setAdditionalDetails(addressUpdatedDataDTO.additionalDetails());
} }
patient.setActive(false);
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);
}
}

View File

@ -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<Payment> getAllPayments(Pageable pageable) {
return paymentRepository.findAll(pageable);
}
}

View File

@ -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);
}
}

View File

@ -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<FinancialReportDTO> getFinancialReport(LocalDate startDate, LocalDate endDate) {
List<FinancialReportDTO> 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<LowStockItemDTO> 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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -2,55 +2,103 @@ package com.mirna.hospitalmanagementapi.application.services;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails; 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.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.mirna.hospitalmanagementapi.application.usecase.user.FindUserByLoginUseCase; import com.mirna.hospitalmanagementapi.application.usecase.user.FindUserByLoginUseCase;
import com.mirna.hospitalmanagementapi.application.usecase.user.SaveUserUseCase; import com.mirna.hospitalmanagementapi.application.usecase.user.SaveUserUseCase;
import com.mirna.hospitalmanagementapi.domain.dtos.auth.UserDTO; import com.mirna.hospitalmanagementapi.domain.dtos.auth.UserDTO;
import com.mirna.hospitalmanagementapi.domain.entities.auth.User; 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.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 is an implementation of the UserService interface.
* *
* This class provides methods to perform operations on User entity * This class provides methods to perform operations on User entity
* *
* @author Mirna Gama * @author Mirna Gama (Extended by FlookSP)
* @version 1.0 * @version 1.1
*/ */
@Service @Service
public class UserServiceImpl implements UserService { public class UserServiceImpl implements UserService {
@Autowired @Autowired
private SaveUserUseCase saveUser; 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
* Adds a new user to the repository. private FindUserByLoginUseCase findUserByLogin;
*
* @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 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);
}
}

View File

@ -6,11 +6,29 @@ import org.springframework.security.authentication.UsernamePasswordAuthenticatio
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.mirna.hospitalmanagementapi.domain.dtos.auth.UserDTO; import com.mirna.hospitalmanagementapi.domain.dtos.auth.UserDTO;
import com.mirna.hospitalmanagementapi.domain.entities.auth.User; import com.mirna.hospitalmanagementapi.domain.entities.auth.User;
import com.mirna.hospitalmanagementapi.domain.services.UserService; import com.mirna.hospitalmanagementapi.domain.services.UserService;
import com.mirna.hospitalmanagementapi.domain.services.auth.AuthService; 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. * This class is an implementation of the AuthService interface.
@ -23,42 +41,142 @@ import com.mirna.hospitalmanagementapi.domain.services.auth.AuthService;
@Service @Service
public class AuthServiceImpl implements AuthService { public class AuthServiceImpl implements AuthService {
@Autowired @Autowired
private AuthenticationManager manager; private AuthenticationManager manager;
@Autowired @Autowired
private UserService userService; 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
* Performs the user registration private PasswordEncoder passwordEncoder;
*
* @param userDTO Data transfer object containing user credentials for authentication operations @Autowired
* @return A user object including the credentials private PatientRepository patientRepository;
*/
@Override @Autowired
public User register(UserDTO userDTO) { private DoctorRepository doctorRepository;
String encodedPassword = passwordEncoder.encode(userDTO.password()); @Autowired
userDTO = new UserDTO(userDTO.login(), encodedPassword); private StaffService staffService;
return this.userService.addUser(userDTO); @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;
}
}

View File

@ -11,38 +11,24 @@ import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.algorithms.Algorithm;
import com.mirna.hospitalmanagementapi.domain.entities.auth.User; 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 @Component
public class CreateJWTUseCase { public class CreateJWTUseCase {
@Value("${api.security.token.secret}") @Value("${api.security.token.secret}")
private String secret; private String secret;
/** Creates a json web token public String execute(User user) {
* Algorithm algorithm = Algorithm.HMAC256(secret);
* @param user The authenticated user
* @return A string containing the json web token signed with Algorithm HMAC256 return JWT.create().withIssuer("Hospital-Management-API (Extended Version)")
*/ .withSubject(user.getLogin())
public String execute(User user) { .withClaim("id", user.getId())
Algorithm algorithm = Algorithm.HMAC256(secret); .withClaim("roles", user.getRole().name()) // <-- เพิ่มบรรทัดนี้
.withExpiresAt(_getExpirationDate())
return JWT.create().withIssuer("Hospital-Management-API") .sign(algorithm);
.withSubject(user.getLogin()) }
.withClaim("id", user.getId())
.withExpiresAt(_getExpirationDate()) private Instant _getExpirationDate() {
.sign(algorithm); return LocalDateTime.now().plusHours(2).toInstant(ZoneOffset.of("-03:00"));
} }
}
/**
* 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"));
}
}

View File

@ -29,7 +29,7 @@ public class GetJWTSubjectUseCase {
public String execute(String token) { public String execute(String token) {
Algorithm algorithm = Algorithm.HMAC256(secret); Algorithm algorithm = Algorithm.HMAC256(secret);
DecodedJWT decodedJWT = JWT.require(algorithm) DecodedJWT decodedJWT = JWT.require(algorithm)
.withIssuer("Hospital-Management-API") .withIssuer("Hospital-Management-API (Extended Version)")
.build() .build()
.verify(token); .verify(token);

View File

@ -1,34 +1,22 @@
package com.mirna.hospitalmanagementapi.application.usecase.consultation; 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.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.mirna.hospitalmanagementapi.domain.entities.Consultation; import com.mirna.hospitalmanagementapi.domain.entities.Consultation;
import com.mirna.hospitalmanagementapi.domain.repositories.ConsultationRepository; 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 @Component
public class FindConsultationByDoctorAndDateUseCase { public class FindConsultationByDoctorAndDateUseCase {
@Autowired @Autowired
private ConsultationRepository consultationRepository; private ConsultationRepository consultationRepository;
/** public Consultation execute(Long doctorId, OffsetDateTime consultationDate) {
* Executes the findConsultationByDoctorAndDate method from Consultation repository // เพิ่มบรรทัดนี้เพื่อกำหนดจุดสิ้นสุดของช่วงเวลา
* OffsetDateTime consultationDatePlusOneMinute = consultationDate.plusMinutes(1);
* @param doctorId The doctor's id from the consultation
* @param consultationDate The date of the consultation return this.consultationRepository.findConsultationByDoctorAndDate(doctorId, consultationDate, consultationDatePlusOneMinute);
* @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);
}
}

View File

@ -1,34 +1,22 @@
package com.mirna.hospitalmanagementapi.application.usecase.consultation; 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.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.mirna.hospitalmanagementapi.domain.entities.Consultation; import com.mirna.hospitalmanagementapi.domain.entities.Consultation;
import com.mirna.hospitalmanagementapi.domain.repositories.ConsultationRepository; 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 @Component
public class FindConsultationByPatientAndDateUseCase { public class FindConsultationByPatientAndDateUseCase {
@Autowired @Autowired
private ConsultationRepository consultationRepository; private ConsultationRepository consultationRepository;
/** public Consultation execute(Long patientId, OffsetDateTime consultationDate) {
* Executes the findConsultationByPatientAndDate method from Consultation repository // เพิ่มบรรทัดนี้เพื่อกำหนดจุดสิ้นสุดของช่วงเวลา
* OffsetDateTime consultationDatePlusOneMinute = consultationDate.plusMinutes(1);
* @param patientId The patient's id from the consultation
* @param consultationDate The date of the consultation return this.consultationRepository.findConsultationByPatientAndDate(patientId, consultationDate, consultationDatePlusOneMinute);
* @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);
}
}

View File

@ -1,6 +1,9 @@
// ไฟล์: FindOneFreeDoctorBySpecialtyUseCase.java
package com.mirna.hospitalmanagementapi.application.usecase.doctor; 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.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; 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.enums.Specialty;
import com.mirna.hospitalmanagementapi.domain.repositories.DoctorRepository; import com.mirna.hospitalmanagementapi.domain.repositories.DoctorRepository;
/**
* This class is used to execute the findOneFreeDoctorBySpecialty method
*
* @author Mirna Gama
* @version 1.0
*/
@Component @Component
public class FindOneFreeDoctorBySpecialtyUseCase { public class FindOneFreeDoctorBySpecialtyUseCase {
@Autowired @Autowired
private DoctorRepository doctorRepository; private DoctorRepository doctorRepository;
/** public Doctor execute(Specialty specialty, OffsetDateTime consultationDate) {
* Executes the findOneFreeDoctorBySpecialty method from Doctor repository // ใช้ Query ใหม่ที่แม่นยำกว่าเดิม
* List<Doctor> freeDoctors = doctorRepository.findFreeDoctorsBySpecialty(specialty, consultationDate);
* @param specialty Desired specialty for doctor search
* @param consultationDate Date to check if the doctor is free if (freeDoctors.isEmpty()) {
* @return A random free doctor with the defined specialty if successful, or null if it is non-existent return null;
* }
*/
public Doctor execute(Specialty specialty, LocalDateTime consultationDate) { // เลือกแพทย์แบบสุ่มจากรายการที่ว่าง
return doctorRepository.findOneFreeDoctorBySpecialty(specialty, consultationDate); return freeDoctors.get(new Random().nextInt(freeDoctors.size()));
} }
} }

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -18,7 +18,7 @@ public record AddressDTO(
String neighborhood, String neighborhood,
@NotBlank(message="zipCode cannot be blank") @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, String zipCode,
@NotBlank(message="city cannot be blank") @NotBlank(message="city cannot be blank")

View File

@ -0,0 +1,7 @@
package com.mirna.hospitalmanagementapi.domain.dtos;
public record AppointmentReportDTO(
Long totalAppointments,
Long canceledAppointments,
Long totalPatients
) {}

View File

@ -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;
}
}

View File

@ -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<BillableItemDTO> billableItems;
// Getters and Setters
public Long getMedicalRecordId() {
return medicalRecordId;
}
public void setMedicalRecordId(Long medicalRecordId) {
this.medicalRecordId = medicalRecordId;
}
public List<BillableItemDTO> getBillableItems() {
return billableItems;
}
public void setBillableItems(List<BillableItemDTO> billableItems) {
this.billableItems = billableItems;
}
}

View File

@ -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
) {}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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
) {}

View File

@ -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;
}
}

View File

@ -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
) {}

View File

@ -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
) {}

View File

@ -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; }
}

View File

@ -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;
}
}

View File

@ -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
) {}

View File

@ -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
) {}

View File

@ -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
) {}

View File

@ -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
) {}

View File

@ -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
) {}

View File

@ -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
) {}

View File

@ -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()
);
}
}

View File

@ -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;
}
}

View File

@ -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
) {}

View File

@ -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) {
}

View File

@ -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
) {}

View File

@ -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
) {}

View File

@ -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
) {}

View File

@ -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
) {}

View File

@ -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
) {}

View File

@ -1,36 +1,49 @@
package com.mirna.hospitalmanagementapi.domain.dtos.consultation; package com.mirna.hospitalmanagementapi.domain.dtos.consultation;
import java.time.LocalDateTime; import java.time.OffsetDateTime;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.mirna.hospitalmanagementapi.domain.enums.Specialty; import com.mirna.hospitalmanagementapi.domain.enums.Specialty;
import com.mirna.hospitalmanagementapi.domain.validators.consultation.constraints.ConsultationDateBusinessHours; import com.mirna.hospitalmanagementapi.domain.validators.consultation.constraints.ConsultationDateBusinessHours;
import com.mirna.hospitalmanagementapi.domain.validators.consultation.constraints.ConsultationDateScheduledInAdvance; 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.Future;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
/** /**
* Data transfer object used to transfer data that will be saved in a Consultation entity * Data transfer object used to transfer data that will be saved in a Consultation entity
* * * @author Mirna Gama (Extended by FlookSP)
* @author Mirna Gama * @version 1.0
* @version 1.0 */
*/ public record ConsultationDTO(
public record ConsultationDTO (
Long doctorId, @Schema(
description = "รหัสแพทย์สำหรับจองโดยตรง (ไม่บังคับ) ต้องเป็น null หากระบุแผนก",
@NotNull(message="patient id cannot be null") example = "1"
Long patientId, )
Long doctorId,
@NotNull(message="consultation date cannot be null")
@Future @Schema(
@JsonFormat(pattern = "dd/MM/yyyy HH:mm") description = "รหัสผู้ป่วยที่ต้องการนัดหมาย",
@ConsultationDateScheduledInAdvance example = "1"
@ConsultationDateBusinessHours )
LocalDateTime consultationDate, @NotNull(message="patient id cannot be null")
Long patientId,
Specialty specialty
@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
) { ) {
} }

View File

@ -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;
}

View File

@ -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; }
}

View File

@ -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; }
}

View File

@ -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 นี้เพื่อความยืดหยุ่นในการทดสอบ
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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;
}

Some files were not shown because too many files have changed in this diff Show More