Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3501447cbe | |||
| d4ee9ad8d0 | |||
| 26d7bb940b | |||
| 2000ecc7eb | |||
| 42609e4547 |
@@ -3,9 +3,9 @@
|
|||||||
{
|
{
|
||||||
"build": {
|
"build": {
|
||||||
"dockerfile": "Dockerfile",
|
"dockerfile": "Dockerfile",
|
||||||
"args": {
|
// "args": {
|
||||||
"http_proxy": "http://host.docker.internal:3142"
|
// "http_proxy": "http://host.docker.internal:3142"
|
||||||
},
|
// },
|
||||||
"options": ["--add-host=host.docker.internal:host-gateway"]
|
"options": ["--add-host=host.docker.internal:host-gateway"]
|
||||||
},
|
},
|
||||||
"runArgs": ["--add-host=host.docker.internal:host-gateway"],
|
"runArgs": ["--add-host=host.docker.internal:host-gateway"],
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
"containerEnv": {
|
"containerEnv": {
|
||||||
"TESTCONTAINERS_RYUK_DISABLED": "true",
|
"TESTCONTAINERS_RYUK_DISABLED": "true",
|
||||||
"QUARKUS_DEBUG_HOST": "0.0.0.0",
|
"QUARKUS_DEBUG_HOST": "0.0.0.0",
|
||||||
"http_proxy": "http://host.docker.internal:3142"
|
// "http_proxy": "http://host.docker.internal:3142"
|
||||||
},
|
},
|
||||||
"postStartCommand": "redis-server --daemonize yes",
|
"postStartCommand": "redis-server --daemonize yes",
|
||||||
"postCreateCommand": "git config --global --add safe.directory '*'",
|
"postCreateCommand": "git config --global --add safe.directory '*'",
|
||||||
|
|||||||
@@ -111,16 +111,6 @@
|
|||||||
<artifactId>quarkus-maven-plugin</artifactId>
|
<artifactId>quarkus-maven-plugin</artifactId>
|
||||||
<version>${quarkus.platform.version}</version>
|
<version>${quarkus.platform.version}</version>
|
||||||
<extensions>true</extensions>
|
<extensions>true</extensions>
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<goals>
|
|
||||||
<goal>build</goal>
|
|
||||||
<goal>generate-code</goal>
|
|
||||||
<goal>generate-code-tests</goal>
|
|
||||||
<goal>native-image-agent</goal>
|
|
||||||
</goals>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
@@ -128,7 +118,6 @@
|
|||||||
<configuration>
|
<configuration>
|
||||||
<parameters>true</parameters>
|
<parameters>true</parameters>
|
||||||
<annotationProcessorPaths>
|
<annotationProcessorPaths>
|
||||||
|
|
||||||
<path>
|
<path>
|
||||||
<groupId>org.projectlombok</groupId>
|
<groupId>org.projectlombok</groupId>
|
||||||
<artifactId>lombok</artifactId>
|
<artifactId>lombok</artifactId>
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package com.drinkool.dtos;
|
||||||
|
|
||||||
|
public class AttendanceRequest {
|
||||||
|
public Long shiftId;
|
||||||
|
public String note;
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package com.drinkool.dtos;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
public class ShiftRequest {
|
||||||
|
public Long staffId;
|
||||||
|
public Long templateId; // Gửi lên nếu dùng mẫu có sẵn
|
||||||
|
public LocalDateTime startTime; // Tùy chỉnh tự do
|
||||||
|
public LocalDateTime endTime; // Tùy chỉnh tự do
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package com.drinkool.enums;
|
||||||
|
|
||||||
|
public enum Role {
|
||||||
|
MANAGER, STAFF
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package com.drinkool.enums;
|
||||||
|
|
||||||
|
public enum ShiftStatus {
|
||||||
|
SCHEDULED, // Đã xếp lịch
|
||||||
|
IN_PROGRESS, // Đang diễn ra
|
||||||
|
COMPLETED, // Đã hoàn thành
|
||||||
|
CANCELLED // Đã hủy
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package com.drinkool.models;
|
||||||
|
|
||||||
|
import io.quarkus.hibernate.orm.panache.PanacheEntity;
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "attendance")
|
||||||
|
public class Attendance extends PanacheEntity {
|
||||||
|
|
||||||
|
@OneToOne(fetch = FetchType.EAGER)
|
||||||
|
@JoinColumn(name = "shift_id", nullable = false, unique = true)
|
||||||
|
public Shift shift;
|
||||||
|
|
||||||
|
public LocalDateTime checkInTime;
|
||||||
|
public LocalDateTime checkOutTime;
|
||||||
|
|
||||||
|
@Column(length = 500)
|
||||||
|
public String note; // Ghi chú đi trễ, về sớm...
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package com.drinkool.models;
|
||||||
|
|
||||||
|
import com.drinkool.enums.ShiftStatus;
|
||||||
|
import io.quarkus.hibernate.orm.panache.PanacheEntity;
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "shift")
|
||||||
|
public class Shift extends PanacheEntity {
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.EAGER)
|
||||||
|
@JoinColumn(name = "staff_id", nullable = false)
|
||||||
|
public Staff staff;
|
||||||
|
|
||||||
|
// Giờ linh hoạt, không bắt buộc phải khớp 100% với template
|
||||||
|
@Column(nullable = false)
|
||||||
|
public LocalDateTime startTime;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
public LocalDateTime endTime;
|
||||||
|
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
public ShiftStatus status = ShiftStatus.SCHEDULED;
|
||||||
|
|
||||||
|
// Lấy danh sách ca làm của 1 nhân viên trong khoảng thời gian
|
||||||
|
public static java.util.List<Shift> findByStaffAndDateRange(Long staffId, LocalDateTime start, LocalDateTime end) {
|
||||||
|
return find("staff.id = ?1 and startTime >= ?2 and endTime <= ?3", staffId, start, end).list();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package com.drinkool.models;
|
||||||
|
|
||||||
|
import io.quarkus.hibernate.orm.panache.PanacheEntity;
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import java.time.LocalTime;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "shift_template")
|
||||||
|
public class ShiftTemplate extends PanacheEntity {
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
public String templateName; // VD: Ca Sáng, Ca Chiều
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
public LocalTime defaultStartTime; // Giờ bắt đầu mặc định
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
public LocalTime defaultEndTime; // Giờ kết thúc mặc định
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package com.drinkool.models;
|
||||||
|
|
||||||
|
import com.drinkool.enums.Role;
|
||||||
|
import io.quarkus.hibernate.orm.panache.PanacheEntity;
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "staff")
|
||||||
|
public class Staff extends PanacheEntity {
|
||||||
|
|
||||||
|
@Column(nullable = false, unique = true)
|
||||||
|
public String staffCode; // Mã NV (vd: NV001)
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
public String name;
|
||||||
|
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
public Role role;
|
||||||
|
|
||||||
|
// Thuộc tính để link sang account-service nếu cần
|
||||||
|
// public Long accountUserId;
|
||||||
|
}
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
package com.drinkool.resources;
|
||||||
|
|
||||||
|
import com.drinkool.dtos.AttendanceRequest;
|
||||||
|
import com.drinkool.dtos.ShiftRequest;
|
||||||
|
import com.drinkool.enums.ShiftStatus;
|
||||||
|
import com.drinkool.models.Attendance;
|
||||||
|
import com.drinkool.models.Shift;
|
||||||
|
import com.drinkool.models.ShiftTemplate;
|
||||||
|
import com.drinkool.models.Staff;
|
||||||
|
import jakarta.transaction.Transactional;
|
||||||
|
import jakarta.ws.rs.*;
|
||||||
|
import jakarta.ws.rs.core.MediaType;
|
||||||
|
import jakarta.ws.rs.core.Response;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Path("/api/shifts")
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
|
public class ShiftResource {
|
||||||
|
|
||||||
|
// --- QUẢN LÝ CA LÀM (XẾP LỊCH) ---
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("/schedule")
|
||||||
|
@Transactional
|
||||||
|
public Response scheduleShift(ShiftRequest request) {
|
||||||
|
Staff staff = Staff.findById(request.staffId);
|
||||||
|
if (staff == null) {
|
||||||
|
throw new BadRequestException("Staff không tồn tại");
|
||||||
|
}
|
||||||
|
|
||||||
|
Shift shift = new Shift();
|
||||||
|
shift.staff = staff;
|
||||||
|
shift.status = ShiftStatus.SCHEDULED;
|
||||||
|
|
||||||
|
// Nếu có truyền templateId, lấy giờ từ template (có thể kết hợp với ngày để tạo LocalDateTime)
|
||||||
|
if (request.templateId != null) {
|
||||||
|
ShiftTemplate template = ShiftTemplate.findById(request.templateId);
|
||||||
|
if (template != null) {
|
||||||
|
// Giả sử xếp cho ngày hiện tại dựa trên template
|
||||||
|
shift.startTime = LocalDateTime.now().with(template.defaultStartTime);
|
||||||
|
shift.endTime = LocalDateTime.now().with(template.defaultEndTime);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Sử dụng giờ tùy chỉnh hoàn toàn
|
||||||
|
shift.startTime = request.startTime;
|
||||||
|
shift.endTime = request.endTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
shift.persist();
|
||||||
|
return Response.status(Response.Status.CREATED).entity(shift).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/staff/{staffId}")
|
||||||
|
public Response getShiftsByStaff(@PathParam("staffId") Long staffId,
|
||||||
|
@QueryParam("start") String startStr,
|
||||||
|
@QueryParam("end") String endStr) {
|
||||||
|
// Cần parse chuỗi ISO thành LocalDateTime, ở đây làm mẫu đơn giản
|
||||||
|
LocalDateTime start = startStr != null ? LocalDateTime.parse(startStr) : LocalDateTime.now().minusDays(7);
|
||||||
|
LocalDateTime end = endStr != null ? LocalDateTime.parse(endStr) : LocalDateTime.now().plusDays(7);
|
||||||
|
|
||||||
|
List<Shift> shifts = Shift.findByStaffAndDateRange(staffId, start, end);
|
||||||
|
return Response.ok(shifts).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- ĐIỂM DANH (CHECK-IN / CHECK-OUT) ---
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("/check-in")
|
||||||
|
@Transactional
|
||||||
|
public Response checkIn(AttendanceRequest request) {
|
||||||
|
Shift shift = Shift.findById(request.shiftId);
|
||||||
|
if (shift == null) throw new BadRequestException("Ca làm không tồn tại");
|
||||||
|
|
||||||
|
Attendance attendance = Attendance.find("shift", shift).firstResult();
|
||||||
|
if (attendance != null) throw new BadRequestException("Đã check-in cho ca này rồi");
|
||||||
|
|
||||||
|
attendance = new Attendance();
|
||||||
|
attendance.shift = shift;
|
||||||
|
attendance.checkInTime = LocalDateTime.now();
|
||||||
|
attendance.note = request.note;
|
||||||
|
attendance.persist();
|
||||||
|
|
||||||
|
shift.status = ShiftStatus.IN_PROGRESS;
|
||||||
|
shift.persist();
|
||||||
|
|
||||||
|
return Response.accepted(attendance).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("/check-out")
|
||||||
|
@Transactional
|
||||||
|
public Response checkOut(AttendanceRequest request) {
|
||||||
|
Shift shift = Shift.findById(request.shiftId);
|
||||||
|
if (shift == null) throw new BadRequestException("Ca làm không tồn tại");
|
||||||
|
|
||||||
|
Attendance attendance = Attendance.find("shift", shift).firstResult();
|
||||||
|
if (attendance == null) throw new BadRequestException("Chưa check-in nên không thể check-out");
|
||||||
|
|
||||||
|
attendance.checkOutTime = LocalDateTime.now();
|
||||||
|
if (request.note != null) {
|
||||||
|
attendance.note += " | " + request.note;
|
||||||
|
}
|
||||||
|
attendance.persist();
|
||||||
|
|
||||||
|
shift.status = ShiftStatus.COMPLETED;
|
||||||
|
shift.persist();
|
||||||
|
|
||||||
|
return Response.accepted(attendance).build();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: redis
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: redis
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: redis
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: redis
|
||||||
|
image: redis:alpine
|
||||||
|
ports:
|
||||||
|
- containerPort: 6379
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: redis-service
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: redis
|
||||||
|
ports:
|
||||||
|
- port: 6379
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: account-service
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: account-service
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: account-service
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: account-service
|
||||||
|
image: drinkool/account-service:1.0.0-SNAPSHOT
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
env:
|
||||||
|
- name: QUARKUS_REDIS_HOST
|
||||||
|
value: "redis-service"
|
||||||
|
ports:
|
||||||
|
- containerPort: 8081
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: account-service
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: account-service
|
||||||
|
ports:
|
||||||
|
- port: 8081
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: gateway-service
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: gateway-service
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: gateway-service
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: gateway-service
|
||||||
|
image: drinkool/gateway-service:1.0.0-SNAPSHOT
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
ports:
|
||||||
|
- containerPort: 8080
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: gateway-service
|
||||||
|
spec:
|
||||||
|
type: NodePort
|
||||||
|
selector:
|
||||||
|
app: gateway-service
|
||||||
|
ports:
|
||||||
|
- port: 8080
|
||||||
|
targetPort: 8080
|
||||||
|
nodePort: 30001
|
||||||
Reference in New Issue
Block a user