Compare commits

..

No commits in common. "5db80d5299563bd2c90cab2dc26dd5068aff4bcc" and "78149d338bb1b66853a529c6d14bbf798dde37f7" have entirely different histories.

232 changed files with 2697 additions and 9749 deletions

View File

@ -28,15 +28,4 @@ jobs:
distribution: 'temurin' distribution: 'temurin'
cache: maven cache: maven
- name: Build with Maven - name: Build with Maven
run: mvn -B package -Dspring.profiles.active=test -DJWT_SECRET=${{ secrets.JWT_SECRET }} --file pom.xml run: mvn -B package -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,5 +31,3 @@ build/
### VS Code ### ### VS Code ###
.vscode/ .vscode/
.env

View File

@ -1,14 +0,0 @@
# ใช้ 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,286 +1,557 @@
# Hospital Management API (Extended Version) # Hospital Management - API Module ![Build Status](https://github.com/MirnaGama/hospital-management-api/actions/workflows/maven.yml/badge.svg)
![API Version](https://img.shields.io/badge/API%20Version-v2.0-blue.svg) ## About the project
![Developed by](https://img.shields.io/badge/Developed%20by-FlookSP-green.svg) Hospital Management API built in Spring Boot
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. ### Prerequisites:
- 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
## ✨ Improvements and New Features ## API Documentation - /swagger-ui/index.html
### 1. Code Structure and Architecture ### authentication
* **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. #### POST - [**/api/auth/register**] - Register a new user
* **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.
### 2. Endpoint and Data Handling Changes - **Body:**
```
* **New Patient Registration Endpoint**: The old endpoint `POST /api/auth/register` has been replaced with `POST /api/auth/register-patient` to more accurately reflect its function. {
* **Data Integrity Fix**: We fixed a `DataIntegrityViolationException` caused by an overly long `state` value. Test data has been updated to use a 2-character abbreviation (e.g., "CA") to conform to database constraints. "login" (string, required),
"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
``` ```
### 2⃣ Set Environment Variables for IntelliJ - **Responses:**
เปิด IntelliJ → ไปที่ Run → Edit Configurations | Code | Description |
| ------------- | ------------- |
| `200` | _Successful operation_ |
| `400` | _Validation Error_ |
เลือก Configuration ของ Spring Boot ของโปรเจค #### POST - [**/api/auth/login**] - Perform the login
ในส่วน Environment Variables กำหนดค่าตามนี้: - **Body:**
``` ```
POSTGRES_HOST=localhost {
POSTGRES_PORT=5432 "login" (string, required),
POSTGRES_DB=<your_database_name> "password" (string, required),
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>
``` ```
### 3⃣ Run PostgreSQL and pgAdmin - **Responses:**
```
# รันเฉพาะ PostgreSQL และ pgAdmin
docker-compose up -d postgresdb pgadmin
| Code | Description |
| ------------- | ------------- |
| `200` | _Successful operation_ |
| `400` | _Validation Error_ |
| `403` | _Incorrect credentials_ |
### doctors
#### POST - [**/api/v1.0/doctors**] - Adds a new doctor
- **Body:**
```
{
"name" (string, required),
"email" (string, required),
"crm" (string, required),
"telephone" (string, required),
"specialty" (string, required),
"address": {
"street" (string, required),
"neighborhood" (string, required),
"zipCode" (string, required),
"city" (string, required),
"state" (string, required),
"additionalDetails" (string, optional),
"houseNumber" (string, optional)
}
}
``` ```
### 4⃣ Run Spring Boot Application - **Request Headers:**
เปิด IntelliJ | Key | Description |
| ------------- | ------------- |
| `Authorization` | _Authorization token_ |
Run Spring Boot จาก Configuration ที่ตั้งค่า Environment Variables แล้ว - **Responses:**
✅ แอปจะเชื่อมต่อ PostgreSQL และ MinIO ตามค่าที่กำหนด | Code | Description |
| ------------- | ------------- |
| `201` | _Successfully created_ |
| `400` | _Validation Error_ |
| `403` | _Unauthorized / Invalid token_ |
--- #### GET - [**/api/v1.0/doctors/{id}**] - Get an existing doctor
### Future Development Plan (v2.1+) - **Response Body Example:**
```
{
"id": 1,
"name": "DOCTOR TEST",
"email": "test@gmail.com",
"crm": "12456",
"telephone": "(81) 99999999",
"specialty": "ORTHOPEDICS",
"active": true,
"address": {
"street": "TEST STR.",
"neighborhood": "TEST NEIGHBORHOOD",
"zipCode": "12345678",
"city": "TEST CITY",
"state": "ST",
"additionalDetails": null,
"houseNumber": null
}
}
```
We will continue to develop on Spring Boot following standard best practices to add the following capabilities: - **Request Headers:**
**AI-Driven Enhancements** | Key | Description |
- Implement predictive analytics for patient admissions and resource allocation. | ------------- | ------------- |
- Integrate AI models for anomaly detection in lab results or vital signs. | `Authorization` | _Authorization token_ |
- Use Natural Language Processing (NLP) for automated parsing of medical notes.
- Suggest treatment plans or risk alerts based on historical patient data.
---
## 📝 API Documentation - **Request Parameters:**
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_ |
## 🙏 Credits - **Responses:**
Extended version of MirnaGama/hospital-management-api.
This continuation improves code quality, adds features, and addresses real-world issues. | Code | Description |
| ------------- | ------------- |
| `200` | _Successful operation_ |
| `404` | _Entity not found_ |
| `403` | _Unauthorized / Invalid token_ |
#### GET - [**/api/v1.0/doctors**] - Get a list of doctors
- **Response Body Example:**
```
{
"content": [
{
"name": "Test1",
"email": "test1@gmail.com",
"crm": "123456",
"specialty": "ORTHOPEDICS"
},
{
"name": "Test2",
"email": "test2@gmail.com",
"crm": "789101",
"specialty": "ORTHOPEDICS"
},
{
"name": "Test3",
"email": "test3@gmail.com",
"crm": "112131",
"specialty": "ORTHOPEDICS"
},
],
"pageable": {
"pageNumber": 0,
"pageSize": 10,
"sort": {
"sorted": true,
"unsorted": false,
"empty": false
},
"offset": 0,
"paged": true,
"unpaged": false
},
"totalPages": 1,
"totalElements": 3,
"last": true,
"sort": {
"sorted": true,
"unsorted": false,
"empty": false
},
"number": 0,
"size": 10,
"first": true,
"numberOfElements": 3,
"empty": false
}
```
- **Request Headers:**
| Key | Description |
| ------------- | ------------- |
| `Authorization` | _Authorization token_ |
- **Request Parameters:**
| Key | Description |
| ------------- | ------------- |
| `size` | _Number of records that should be returned_ |
| `sort` | _Sort by object attribute in descending order_ |
| `page` | _Page number_ |
- **Responses:**
| Code | Description |
| ------------- | ------------- |
| `200` | _Successful operation_ |
| `403` | _Unauthorized / Invalid token_ |
#### PUT - [**/api/v1.0/doctors**] - Updates an existing doctor
- **Body:**
```
{
"id" (number, required),
"name" (string, optional),
"telephone" (string, optional),
"address": {
"street" (string, optional),
"neighborhood" (string, optional),
"zipcode" (string, optional),
"city" (string, optional),
"state" (string, optional),
"additionalDetails" (string, optional),
"houseNumber" (string, optional),
}
}
```
- **Request Headers:**
| Key | Description |
| ------------- | ------------- |
| `Authorization` | _Authorization token_ |
- **Responses:**
| Code | Description |
| ------------- | ------------- |
| `200` | _Successful operation_ |
| `400` | _Validation Error_ |
| `403` | _Unauthorized / Invalid token_ |
#### DELETE - [**/api/v1.0/doctors/{id}**] - Deactivates an existing doctor
- **Response Body Example:**
```
{
"id": 2,
"name": "DEACTIVATED DOCTOR TEST",
"email": "test@gmail.com",
"crm": "12456",
"telephone": "(81) 99999999",
"specialty": "ORTHOPEDICS",
"active": false,
"address": {
"street": "TEST STR.",
"neighborhood": "TEST NEIGHBORHOOD",
"zipCode": "12345678",
"city": "TEST CITY",
"state": "ST",
"additionalDetails": null,
"houseNumber": null
}
}
```
- **Request Headers:**
| Key | Description |
| ------------- | ------------- |
| `Authorization` | _Authorization token_ |
- **Request Parameters:**
| Key | Description |
| ------------- | ------------- |
| `id` | _Unique identifier of the doctor who will be deactivated_ |
- **Responses:**
| Code | Description |
| ------------- | ------------- |
| `200` | _Successful operation_ |
| `400` | _Validation Error_ |
| `404` | _Entity not found_ |
| `403` | _Unauthorized / Invalid token_ |
### patients
#### POST - [**/api/v1.0/patients**] - Adds a new patient
- **Body:**
```
{
"name" (string, required),
"email" (string, required),
"cpf" (string, required),
"telephone" (string, required),
"address": {
"street" (string, required),
"neighborhood" (string, required),
"zipCode" (string, required),
"city" (string, required),
"state" (string, required),
"additionalDetails" (string, optional),
"houseNumber" (string, optional)
}
}
```
- **Request Headers:**
| Key | Description |
| ------------- | ------------- |
| `Authorization` | _Authorization token_ |
- **Responses:**
| Code | Description |
| ------------- | ------------- |
| `201` | _Successfully created_ |
| `400` | _Validation Error_ |
| `403` | _Unauthorized / Invalid token_ |
#### GET - [**/api/v1.0/patients/{id}**] - Get an existing patient
- **Response Body Example:**
```
{
"id": 1,
"name": "PATIENT TEST",
"email": "test@gmail.com",
"cpf": "11111111111",
"telephone": "(81) 99999999",
"active": true,
"address": {
"street": "TEST STR.",
"neighborhood": "TEST NEIGHBORHOOD",
"zipCode": "12345678",
"city": "TEST CITY",
"state": "ST",
"additionalDetails": null,
"houseNumber": null
}
}
```
- **Request Headers:**
| Key | Description |
| ------------- | ------------- |
| `Authorization` | _Authorization token_ |
- **Request Parameters:**
| Key | Description |
| ------------- | ------------- |
| `id` | _Unique identifier of the patient who will be fetched_ |
- **Responses:**
| Code | Description |
| ------------- | ------------- |
| `200` | _Successful operation_ |
| `404` | _Entity not found_ |
| `403` | _Unauthorized / Invalid token_ |
#### GET - [**/api/v1.0/patients**] - Get a list of patients
- **Response Body Example:**
```
{
"content": [
{
"name": "Test1",
"email": "test1@gmail.com",
"cpf": "123456"
},
{
"name": "Test2",
"email": "test2@gmail.com",
"cpf": "789101"
},
{
"name": "Test3",
"email": "test3@gmail.com",
"cpf": "112131"
},
],
"pageable": {
"pageNumber": 0,
"pageSize": 10,
"sort": {
"sorted": true,
"unsorted": false,
"empty": false
},
"offset": 0,
"paged": true,
"unpaged": false
},
"totalPages": 1,
"totalElements": 3,
"last": true,
"sort": {
"sorted": true,
"unsorted": false,
"empty": false
},
"number": 0,
"size": 10,
"first": true,
"numberOfElements": 3,
"empty": false
}
```
- **Request Headers:**
| Key | Description |
| ------------- | ------------- |
| `Authorization` | _Authorization token_ |
- **Request Parameters:**
| Key | Description |
| ------------- | ------------- |
| `size` | _Number of records that should be returned_ |
| `sort` | _Sort by object attribute in descending order_ |
| `page` | _Page number_ |
- **Responses:**
| Code | Description |
| ------------- | ------------- |
| `200` | _Successful operation_ |
| `403` | _Unauthorized / Invalid token_ |
#### PUT - [**/api/v1.0/patients**] - Updates an existing patient
- **Body:**
```
{
"id" (number, required),
"name" (string, optional),
"telephone" (string, optional),
"address": {
"street" (string, optional),
"neighborhood" (string, optional),
"zipcode" (string, optional),
"city" (string, optional),
"state" (string, optional),
"additionalDetails" (string, optional),
"houseNumber" (string, optional),
}
}
```
- **Request Headers:**
| Key | Description |
| ------------- | ------------- |
| `Authorization` | _Authorization token_ |
- **Responses:**
| Code | Description |
| ------------- | ------------- |
| `200` | _Successful operation_ |
| `400` | _Validation Error_ |
| `403` | _Unauthorized / Invalid token_ |
#### DELETE - [**/api/v1.0/patients/{id}**] - Deactivates an existing patient
- **Response Body Example:**
```
{
"id": 1,
"name": "DEACTIVATED PATIENT TEST",
"email": "test@gmail.com",
"cpf": "11111111111",
"telephone": "(81) 99999999",
"active": false,
"address": {
"street": "TEST STR.",
"neighborhood": "TEST NEIGHBORHOOD",
"zipCode": "12345678",
"city": "TEST CITY",
"state": "ST",
"additionalDetails": null,
"houseNumber": null
}
}
```
- **Request Headers:**
| Key | Description |
| ------------- | ------------- |
| `Authorization` | _Authorization token_ |
- **Request Parameters:**
| Key | Description |
| ------------- | ------------- |
| `id` | _Unique identifier of the patient who will be deactivated_ |
- **Responses:**
| Code | Description |
| ------------- | ------------- |
| `200` | _Successful operation_ |
| `400` | _Validation Error_ |
| `404` | _Entity not found_ |
| `403` | _Unauthorized / Invalid token_ |
### consultations
#### POST - [**/api/v1.0/consultations**] - Adds a new consultation
- **Body:**
```
{
"patientId" (number, required),
"consultationDate" (string, required),
"doctorId" (number, required if _specialty_ field is not filled),
"specialty" (string, required if _doctorId_ field is not filled)
}
```
- **Request Headers:**
| Key | Description |
| ------------- | ------------- |
| `Authorization` | _Authorization token_ |
- **Responses:**
| Code | Description |
| ------------- | ------------- |
| `200` | _Successful operation |
| `400` | _Validation Error_ |
| `403` | _Unauthorized / Invalid token_ |
| `404` | _Entity not found_ |

View File

@ -1,51 +0,0 @@
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>1.0</version> <version>0.0.1-SNAPSHOT</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,37 +40,24 @@
<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.postgresql</groupId> <groupId>org.flywaydb</groupId>
<artifactId>postgresql</artifactId> <artifactId>flyway-core</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.2.220</version> <version>2.1.214</version>
<scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>

View File

@ -1,49 +0,0 @@
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,10 +21,6 @@ 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;
@ -39,84 +35,59 @@ 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. * Post method to create a new Consultation object based on the provided DTO.
* *
* @param consultationDTO The data transfer object containing data for Consultation * @param consultationDTO The data transfer object containing data for Consultation
* entity. * 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 * @return A response entity containing the saved consultation and created status if successful, or
* @throws ConsultationValidationException if there is a validation error * a 400-level error if there is a validation error
*/ * @throws ConsultationValidationException if there is a validation error
@PostMapping */
@Operation( @PostMapping
summary = "สร้างการนัดหมายใหม่", public ResponseEntity<Object> postConsultation(@RequestBody @Valid ConsultationDTO consultationDTO) throws ConsultationValidationException {
description = "อนุญาตให้ผู้ป่วยจองการนัดหมายกับแพทย์ที่ระบุหรือตามแผนก", Consultation consultation = consultationService.addConsultation(consultationDTO);
requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(
content = @Content(
mediaType = "application/json",
examples = {
@ExampleObject(
name = "ตัวอย่าง: จองด้วยรหัสแพทย์",
summary = "จองนัดหมายกับแพทย์ที่ต้องการโดยตรง",
value = "{\"doctorId\": 1, \"patientId\": 1, \"consultationDate\": \"2025-09-15T10:00:00+07:00\", \"specialty\": null}"
),
@ExampleObject(
name = "ตัวอย่าง: จองด้วยแผนก",
summary = "จองนัดหมายโดยให้ระบบเลือกแพทย์ที่ว่างในแผนกที่ระบุ",
value = "{\"doctorId\": null, \"patientId\": 1, \"consultationDate\": \"2025-09-15T11:00:00+07:00\", \"specialty\": \"ORTHOPEDICS\"}"
)
}
)
),
responses = {
@ApiResponse(responseCode = "201", description = "สร้างการนัดหมายสำเร็จ"),
@ApiResponse(responseCode = "400", description = "ข้อมูลใน Request Body ไม่ถูกต้อง หรือการตรวจสอบล้มเหลว")
}
)
public ResponseEntity<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();
UriComponents uriComponents = UriComponentsBuilder URI uri = uriComponents.expand(consultation.getId()).toUri();
.fromUriString("/api/v1.0/consultations/{id}")
.encode()
.build();
URI uri = uriComponents.expand(consultation.getId()).toUri(); return ResponseEntity.created(uri).body(consultation);
}
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);
* 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
* Delete method to update a new Consultation object based on the provided DTO. * entity.
* *
* @param consultationCanceledDTO The data transfer object containing data to update Consultation * @return A response entity containing the canceled consultation and ok status if successful, or
* entity. * a 400-level error if the consultation entity is not found
* * @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) {
@DeleteMapping return ResponseEntity.ok(consultationService.cancelConsultation(consultationCanceledDTO));
public ResponseEntity<Object> deleteConsultation(@RequestBody @Valid ConsultationCanceledDTO consultationCanceledDTO) { }
return ResponseEntity.ok(consultationService.cancelConsultation(consultationCanceledDTO));
}
} }

View File

@ -1,60 +0,0 @@
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

@ -1,58 +0,0 @@
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

@ -1,60 +0,0 @@
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

@ -1,50 +0,0 @@
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

@ -1,27 +0,0 @@
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

@ -1,27 +0,0 @@
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

@ -1,27 +0,0 @@
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

@ -1,40 +0,0 @@
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

@ -1,53 +0,0 @@
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

@ -1,49 +0,0 @@
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

@ -1,58 +0,0 @@
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

@ -1,66 +0,0 @@
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

@ -1,57 +0,0 @@
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

@ -1,66 +0,0 @@
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

@ -1,52 +0,0 @@
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

@ -1,41 +0,0 @@
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

@ -1,58 +0,0 @@
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

@ -1,64 +0,0 @@
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

@ -1,43 +0,0 @@
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

@ -1,130 +0,0 @@
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,8 +1,10 @@
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;
@ -12,28 +14,8 @@ 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.
@ -45,126 +27,45 @@ import com.mirna.hospitalmanagementapi.domain.dtos.auth.NurseRegistrationDTO;
@RequestMapping("/api/auth") @RequestMapping("/api/auth")
public class AuthenticationController { public class AuthenticationController {
@Autowired @Autowired
private AuthService authService; private AuthService authService;
@Autowired @Autowired
private TokenService tokenService; 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) {
/** Authentication auth = authService.login(userDTO);
* 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); User authenticatedUser = (User) auth.getPrincipal();
var authenticatedUser = (User) auth.getPrincipal(); String token = tokenService.generateToken(authenticatedUser);
String token = tokenService.generateToken(authenticatedUser); return ResponseEntity.ok(token);
}
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")
* Performs the user registration public ResponseEntity<Object> register(@RequestBody @Valid UserDTO userDTO) {
*
* @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);
}*/
/** User user = authService.register(userDTO);
* 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);
}
/** 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

@ -1,89 +0,0 @@
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,8 +1,12 @@
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;
@ -14,134 +18,116 @@ 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 @Autowired
private ConsultationRepository consultationRepository; private FindConsultationByDoctorAndDateUseCase findConsultationByDoctorAndDate;
@Autowired @Autowired
private FindPatientByIdUseCase findPatientById; private FindConsultationByPatientAndDateUseCase findConsultationByPatientAndDate;
@Autowired @Autowired
private FindDoctorByIdUseCase findDoctorById; private FindPatientByIdUseCase findPatientById;
@Autowired @Autowired
private FindOneFreeDoctorBySpecialtyUseCase findOneFreeDoctorBySpecialty; private FindDoctorByIdUseCase findDoctorById;
@Autowired @Autowired
private DoctorScheduleService doctorScheduleService; private FindOneFreeDoctorBySpecialtyUseCase findOneFreeDoctorBySpecialty;
private static final ZoneOffset BANGKOK_ZONE_OFFSET = ZoneOffset.ofHours(7); /**
* Adds a new consultation to the repository.
*
* @param consultationDTO A data transfer object representing a consultation to add.
* @return The saved consultation if successful, or throws an exception if there is an error.
* @throws ConsultationValidationException if there is a validation error
*/
@Override
public Consultation addConsultation(ConsultationDTO consultationDTO) throws ConsultationValidationException {
@Override Patient patient = findPatientById.execute(consultationDTO.patientId());
@Transactional
public Consultation addConsultation(ConsultationDTO consultationDTO) throws ConsultationValidationException {
Patient patient = validatePatient(consultationDTO.patientId(), consultationDTO.consultationDate());
Doctor doctor = validateAndFindDoctor(consultationDTO);
validatePatientAndDoctorAvailability(patient, doctor, consultationDTO.consultationDate());
Consultation consultation = new Consultation(patient, doctor, consultationDTO.consultationDate()); if (!patient.getActive())
throw new ConsultationValidationException("This patient is not active");
return saveConsultation.execute(consultation); if (findConsultationByPatientAndDate.execute(patient.getId(), consultationDTO.consultationDate()) != null)
} throw new ConsultationValidationException("This patient is not free on this date");
private Patient validatePatient(Long patientId, OffsetDateTime consultationDate) throws ConsultationValidationException { Doctor doctor = null;
if (patientId == null) {
throw new ConsultationValidationException("Patient ID cannot be null");
}
Patient patient = findPatientById.execute(patientId); if (consultationDTO.doctorId() != null) {
if (patient == null) { doctor = findDoctorById.execute(consultationDTO.doctorId());
throw new ConsultationValidationException("This patient is not found");
}
if (!patient.getActive()) {
throw new ConsultationValidationException("This patient is not active");
}
OffsetDateTime consultationDateInBkkZone = consultationDate.withOffsetSameInstant(BANGKOK_ZONE_OFFSET); if (!doctor.getActive())
OffsetDateTime startOfDay = consultationDateInBkkZone.with(LocalTime.MIN); throw new ConsultationValidationException("This doctor is not active");
OffsetDateTime endOfDay = consultationDateInBkkZone.with(LocalTime.MAX);
// แก้ไขบรรทัดนี้: ส่งพารามิเตอร์ที่ถูกต้องไปยัง Repository if (findConsultationByDoctorAndDate.execute(doctor.getId(), consultationDTO.consultationDate()) != null)
if (consultationRepository.findConsultationByPatientAndDate(patient.getId(), startOfDay, endOfDay) != null) { throw new ConsultationValidationException("This doctor is not free on this date");
throw new ConsultationValidationException("This patient is not free on this date");
}
return patient;
}
private Doctor validateAndFindDoctor(ConsultationDTO consultationDTO) throws ConsultationValidationException { } else if (consultationDTO.specialty() != null) {
Doctor doctor = null;
OffsetDateTime consultationDateInBkkZone = consultationDTO.consultationDate().withOffsetSameInstant(BANGKOK_ZONE_OFFSET);
if (consultationDTO.doctorId() != null) { doctor = findOneFreeDoctorBySpecialty.execute(consultationDTO.specialty(),
doctor = findDoctorById.execute(consultationDTO.doctorId()); consultationDTO.consultationDate());
} else if (consultationDTO.specialty() != null) {
doctor = findOneFreeDoctorBySpecialty.execute(consultationDTO.specialty(), consultationDateInBkkZone);
} else {
throw new ConsultationValidationException("At least the specialty or doctor ID must be filled in");
}
if (doctor == null) { if (doctor == null) throw new ConsultationValidationException("There is no free doctor for this date with this specialty");
throw new ConsultationValidationException("No doctors found at the requested time or specialty.");
}
return doctor;
}
private void validatePatientAndDoctorAvailability(Patient patient, Doctor doctor, OffsetDateTime consultationDate) throws ConsultationValidationException { } else {
OffsetDateTime consultationDateInBkkZone = consultationDate.withOffsetSameInstant(BANGKOK_ZONE_OFFSET); throw new ConsultationValidationException("At least the specialty or doctor ID must be filled in");
}
OffsetDateTime startOfDay = consultationDateInBkkZone.with(LocalTime.MIN); Consultation consultation = new Consultation(patient, doctor, consultationDTO.consultationDate());
OffsetDateTime endOfDay = consultationDateInBkkZone.with(LocalTime.MAX);
// แก้ไขบรรทัดนี้: ส่งพารามิเตอร์ที่ถูกต้องไปยัง Repository return saveConsultation.execute(consultation);
if (consultationRepository.findConsultationByDoctorAndDate(doctor.getId(), startOfDay, endOfDay) != null) { }
throw new ConsultationValidationException("This doctor is not free on this date");
}
boolean isDoctorAvailable = doctorScheduleService.isDoctorAvailable( /**
doctor.getId(), * Finds a stored consultation by id.
consultationDateInBkkZone.getDayOfWeek(), *
consultationDateInBkkZone.toLocalTime() * @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 (!isDoctorAvailable) { if (consultation == null)
throw new ConsultationValidationException("The selected doctor is not on duty at the requested time."); throw new EntityNotFoundException("No existing consultation with this id");
}
}
@Override return consultation;
public Consultation findConsultationById(Long id) { }
Consultation consultation = findConsultationById.execute(id);
if (consultation == null) /**
throw new EntityNotFoundException("No existing consultation with this id"); * 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());
return consultation; consultation.setCanceled(true);
} consultation.setReasonCancellation(consultationCanceledDTO.reasonCancellation());
return saveConsultation.execute(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

@ -1,43 +0,0 @@
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

@ -100,13 +100,13 @@ public class DoctorServiceImpl implements DoctorService {
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.setTelephone(doctorUpdatedDataDTO.telephone()); doctor.setName(doctorUpdatedDataDTO.telephone());
} }
if (doctorUpdatedDataDTO.address() != null) { if (doctorUpdatedDataDTO.address() != null) {

View File

@ -1,94 +0,0 @@
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

@ -1,81 +0,0 @@
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

@ -1,149 +0,0 @@
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

@ -1,58 +0,0 @@
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

@ -1,21 +0,0 @@
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

@ -1,53 +0,0 @@
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

@ -1,71 +0,0 @@
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

@ -1,108 +0,0 @@
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

@ -1,139 +0,0 @@
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

@ -1,78 +0,0 @@
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

@ -1,75 +0,0 @@
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

@ -1,66 +0,0 @@
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

@ -1,63 +0,0 @@
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,7 +4,6 @@ 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;
@ -16,11 +15,6 @@ 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;
@ -35,177 +29,136 @@ 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 @Autowired
private FindPatientByIdUseCase findPatientById; private FindPatientByIdUseCase findPatientById;
@Autowired @Autowired
private FindPatientsUseCase findPatients; private FindPatientsUseCase findPatients;
@Autowired /**
private UserRepository userRepository; * Adds a new patient to the database.
@Autowired *
private BCryptPasswordEncoder passwordEncoder; * @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);
* 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");
* 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;
}
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);
}
/** /**
* 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 */
public Patient updatePatient(PatientUpdatedDataDTO patientUpdatedDataDTO) { @Override
public Patient updatePatient(PatientUpdatedDataDTO patientUpdatedDataDTO) {
Patient patient = findPatientById.execute(patientUpdatedDataDTO.id()); Patient patient = findPatientById.execute(patientUpdatedDataDTO.id());
if (patient == null) throw new EntityNotFoundException("No existing patient with this id"); if (patient == null) throw new EntityNotFoundException("No existing patient with this id");
if (patientUpdatedDataDTO.name() != null) patient.setName(patientUpdatedDataDTO.name()); if (patientUpdatedDataDTO.name() != null) patient.setName(patientUpdatedDataDTO.name());
if (patientUpdatedDataDTO.telephone() != null) patient.setTelephone(patientUpdatedDataDTO.telephone()); if (patientUpdatedDataDTO.telephone() != null) patient.setTelephone(patientUpdatedDataDTO.telephone());
if (patientUpdatedDataDTO.address() != null) { if (patientUpdatedDataDTO.address() != null) {
AddressDTO addressUpdatedDataDTO = patientUpdatedDataDTO.address(); AddressDTO addressUpdatedDataDTO = patientUpdatedDataDTO.address();
Address address = patient.getAddress(); Address address = patient.getAddress();
if (addressUpdatedDataDTO.street() != null) { if (addressUpdatedDataDTO.street() != null) {
address.setStreet(addressUpdatedDataDTO.street()); address.setStreet(addressUpdatedDataDTO.street());
} }
if (addressUpdatedDataDTO.neighborhood() != null) { if (addressUpdatedDataDTO.neighborhood() != null) {
address.setNeighborhood(addressUpdatedDataDTO.neighborhood()); address.setNeighborhood(addressUpdatedDataDTO.neighborhood());
} }
if (addressUpdatedDataDTO.city() != null) { if (addressUpdatedDataDTO.city() != null) {
address.setCity(addressUpdatedDataDTO.city()); address.setCity(addressUpdatedDataDTO.city());
} }
if (addressUpdatedDataDTO.zipCode() != null) { if (addressUpdatedDataDTO.zipCode() != null) {
address.setZipCode(addressUpdatedDataDTO.zipCode()); address.setZipCode(addressUpdatedDataDTO.zipCode());
} }
if (addressUpdatedDataDTO.state() != null) { if (addressUpdatedDataDTO.state() != null) {
address.setState(addressUpdatedDataDTO.state()); address.setState(addressUpdatedDataDTO.state());
} }
if (addressUpdatedDataDTO.additionalDetails() != null) { if (addressUpdatedDataDTO.additionalDetails() != null) {
address.setAdditionalDetails(addressUpdatedDataDTO.additionalDetails()); address.setAdditionalDetails(addressUpdatedDataDTO.additionalDetails());
} }
if (addressUpdatedDataDTO.houseNumber() != null) { if (addressUpdatedDataDTO.houseNumber() != null) {
address.setHouseNumber(addressUpdatedDataDTO.houseNumber()); address.setHouseNumber(addressUpdatedDataDTO.houseNumber());
} }
patient.setAddress(address); patient.setAddress(address);
} }
patient = savePatient.execute(patient); patient = savePatient.execute(patient);
return patient; return patient;
} }
/** /**
* Deactivates an existing patient record by provided id * 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 * @param id Long that represents the patient's unique identifier
* EntityNotFoundException if it is non-existent. *
* @throws EntityNotFoundException When patient with id provided is non-existent * @return The deactivated patient if successful, or throws an
*/ * EntityNotFoundException if it is non-existent.
@Override * @throws EntityNotFoundException When patient with id provided is non-existent
public Patient deactivatePatient(Long id) throws EntityNotFoundException { */
Patient patient = findPatientById.execute(id); @Override
public Patient deactivatePatient(Long id) throws EntityNotFoundException {
Patient patient = findPatientById.execute(id);
if (patient == null) { if (patient == null) {
throw new EntityNotFoundException("No existing patient with this id"); throw new EntityNotFoundException("No existing patient with this id");
} }
patient.setActive(false); patient.setActive(false);
return savePatient.execute(patient); 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

@ -1,59 +0,0 @@
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

@ -1,144 +0,0 @@
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

@ -1,93 +0,0 @@
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

@ -1,51 +0,0 @@
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

@ -1,21 +0,0 @@
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,103 +2,55 @@ 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 (Extended by FlookSP) * @author Mirna Gama
* @version 1.1 * @version 1.0
*/ */
@Service @Service
public class UserServiceImpl implements UserService { public class UserServiceImpl implements UserService {
@Autowired @Autowired
private SaveUserUseCase saveUser; private SaveUserUseCase saveUser;
@Autowired @Autowired
private FindUserByLoginUseCase findUserByLogin; private FindUserByLoginUseCase findUserByLogin;
@Autowired /**
private UserRepository userRepository; * Finds a stored user information by login.
*
* @param login A string representing the user's system login
* @return The corresponding user information if successful, or null if it is non-existent.
*/
@Override
public UserDetails findUserByLogin(String login) {
return findUserByLogin.execute(login);
}
@Autowired /**
private PasswordEncoder passwordEncoder; * Adds a new user to the repository.
*
* @param userDTO A data transfer object representing a user to add.
* @return The saved user if successful, or null if there is an error.
*/
@Override
public User addUser(UserDTO userDTO) {
@Override User user = new User(userDTO);
public UserDetails findUserByLogin(String login) {
return findUserByLogin.execute(login);
}
@Override return saveUser.execute(user);
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,29 +6,11 @@ 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.
@ -41,142 +23,42 @@ import com.mirna.hospitalmanagementapi.domain.dtos.auth.NurseRegistrationDTO;
@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 @Autowired
private PasswordEncoder passwordEncoder; private PasswordEncoder passwordEncoder;
@Autowired /**
private PatientRepository patientRepository; * 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());
@Autowired return manager.authenticate(token);
private DoctorRepository doctorRepository; }
@Autowired /**
private StaffService staffService; * Performs the user registration
*
* @param userDTO Data transfer object containing user credentials for authentication operations
* @return A user object including the credentials
*/
@Override
public User register(UserDTO userDTO) {
@Autowired String encodedPassword = passwordEncoder.encode(userDTO.password());
private NurseRepository nurseRepository; userDTO = new UserDTO(userDTO.login(), encodedPassword);
/** return this.userService.addUser(userDTO);
* 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,24 +11,38 @@ 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;
public String execute(User user) { /** Creates a json web token
Algorithm algorithm = Algorithm.HMAC256(secret); *
* @param user The authenticated user
* @return A string containing the json web token signed with Algorithm HMAC256
*/
public String execute(User user) {
Algorithm algorithm = Algorithm.HMAC256(secret);
return JWT.create().withIssuer("Hospital-Management-API (Extended Version)") return JWT.create().withIssuer("Hospital-Management-API")
.withSubject(user.getLogin()) .withSubject(user.getLogin())
.withClaim("id", user.getId()) .withClaim("id", user.getId())
.withClaim("roles", user.getRole().name()) // <-- เพิ่มบรรทัดนี้ .withExpiresAt(_getExpirationDate())
.withExpiresAt(_getExpirationDate()) .sign(algorithm);
.sign(algorithm); }
}
private Instant _getExpirationDate() { /**
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 (Extended Version)") .withIssuer("Hospital-Management-API")
.build() .build()
.verify(token); .verify(token);

View File

@ -1,22 +1,34 @@
package com.mirna.hospitalmanagementapi.application.usecase.consultation; package com.mirna.hospitalmanagementapi.application.usecase.consultation;
import java.time.OffsetDateTime; import java.time.LocalDateTime;
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
return this.consultationRepository.findConsultationByDoctorAndDate(doctorId, consultationDate, consultationDatePlusOneMinute); * @param consultationDate The date of the consultation
} * @return The corresponding consultation if successful, or null if it is non-existent
*
*/
public Consultation execute(Long doctorId, LocalDateTime consultationDate) {
return this.consultationRepository.findConsultationByDoctorAndDate(doctorId, consultationDate);
}
} }

View File

@ -1,22 +1,34 @@
package com.mirna.hospitalmanagementapi.application.usecase.consultation; package com.mirna.hospitalmanagementapi.application.usecase.consultation;
import java.time.OffsetDateTime; import java.time.LocalDateTime;
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
return this.consultationRepository.findConsultationByPatientAndDate(patientId, consultationDate, consultationDatePlusOneMinute); * @param consultationDate The date of the consultation
} * @return The corresponding consultation if successful, or null if it is non-existent
*
*/
public Consultation execute(Long patientId, LocalDateTime consultationDate) {
return this.consultationRepository.findConsultationByPatientAndDate(patientId, consultationDate);
}
} }

View File

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

View File

@ -1,18 +0,0 @@
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

@ -1,17 +0,0 @@
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

@ -1,17 +0,0 @@
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

@ -1,17 +0,0 @@
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{5}", message="invalid format for zipCode") @Pattern(regexp="\\d{8}", message="invalid format for zipCode")
String zipCode, String zipCode,
@NotBlank(message="city cannot be blank") @NotBlank(message="city cannot be blank")

View File

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

View File

@ -1,41 +0,0 @@
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

@ -1,29 +0,0 @@
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

@ -1,26 +0,0 @@
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

@ -1,37 +0,0 @@
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

@ -1,74 +0,0 @@
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

@ -1,16 +0,0 @@
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

@ -1,80 +0,0 @@
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

@ -1,13 +0,0 @@
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

@ -1,14 +0,0 @@
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

@ -1,30 +0,0 @@
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

@ -1,48 +0,0 @@
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

@ -1,22 +0,0 @@
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

@ -1,13 +0,0 @@
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

@ -1,11 +0,0 @@
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

@ -1,13 +0,0 @@
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

@ -1,23 +0,0 @@
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

@ -1,29 +0,0 @@
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

@ -1,32 +0,0 @@
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

@ -1,48 +0,0 @@
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

@ -1,21 +0,0 @@
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

@ -1,14 +0,0 @@
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

@ -1,19 +0,0 @@
// 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

@ -1,36 +0,0 @@
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

@ -1,15 +0,0 @@
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

@ -1,14 +0,0 @@
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

@ -1,14 +0,0 @@
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,49 +1,36 @@
package com.mirna.hospitalmanagementapi.domain.dtos.consultation; package com.mirna.hospitalmanagementapi.domain.dtos.consultation;
import java.time.OffsetDateTime; import java.time.LocalDateTime;
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) *
* @version 1.0 * @author Mirna Gama
*/ * @version 1.0
public record ConsultationDTO( */
public record ConsultationDTO (
@Schema( Long doctorId,
description = "รหัสแพทย์สำหรับจองโดยตรง (ไม่บังคับ) ต้องเป็น null หากระบุแผนก",
example = "1"
)
Long doctorId,
@Schema( @NotNull(message="patient id cannot be null")
description = "รหัสผู้ป่วยที่ต้องการนัดหมาย", Long patientId,
example = "1"
)
@NotNull(message="patient id cannot be null")
Long patientId,
@Schema( @NotNull(message="consultation date cannot be null")
description = "วันที่และเวลาที่ต้องการนัดหมายในรูปแบบ ISO 8601 พร้อม timezone offset (+07:00 สำหรับประเทศไทย)", @Future
example = "2025-09-15T10:00:00+07:00" @JsonFormat(pattern = "dd/MM/yyyy HH:mm")
) @ConsultationDateScheduledInAdvance
@NotNull(message="consultation date cannot be null") @ConsultationDateBusinessHours
@Future LocalDateTime consultationDate,
@ConsultationDateScheduledInAdvance
@ConsultationDateBusinessHours
OffsetDateTime consultationDate,
@Schema( Specialty specialty
description = "แผนกที่ต้องการ (ไม่บังคับ) ต้องระบุหากไม่ได้ระบุรหัสแพทย์",
example = "ORTHOPEDICS"
)
Specialty specialty
) { ) {
} }

View File

@ -1,44 +0,0 @@
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

@ -1,60 +0,0 @@
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

@ -1,19 +0,0 @@
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

@ -1,31 +0,0 @@
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

@ -1,41 +0,0 @@
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

@ -1,96 +0,0 @@
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

@ -1,20 +0,0 @@
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

@ -1,28 +0,0 @@
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