Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3501447cbe | |||
| d4ee9ad8d0 | |||
| 71575ec2ad | |||
| bd548278b4 | |||
| 26d7bb940b | |||
| 2000ecc7eb | |||
| 42609e4547 |
@@ -3,9 +3,9 @@
|
||||
{
|
||||
"build": {
|
||||
"dockerfile": "Dockerfile",
|
||||
"args": {
|
||||
"http_proxy": "http://host.docker.internal:3142"
|
||||
},
|
||||
// "args": {
|
||||
// "http_proxy": "http://host.docker.internal:3142"
|
||||
// },
|
||||
"options": ["--add-host=host.docker.internal:host-gateway"]
|
||||
},
|
||||
"runArgs": ["--add-host=host.docker.internal:host-gateway"],
|
||||
@@ -30,7 +30,7 @@
|
||||
"containerEnv": {
|
||||
"TESTCONTAINERS_RYUK_DISABLED": "true",
|
||||
"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",
|
||||
"postCreateCommand": "git config --global --add safe.directory '*'",
|
||||
|
||||
Vendored
+2
-1
@@ -7,7 +7,8 @@
|
||||
"**/Thumbs.db": true,
|
||||
"**/target": true,
|
||||
"**/docker": true,
|
||||
"node_modules": true
|
||||
"node_modules": true,
|
||||
"**/.mvn": true
|
||||
},
|
||||
"explorerExclude.backup": {},
|
||||
"java.compile.nullAnalysis.mode": "automatic",
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
## [1.5.23](https://git.demonkernel.io.vn/FoodSurf/backend/compare/v1.5.22...v1.5.23) (2026-05-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* resolved image issue ([#47](https://git.demonkernel.io.vn/FoodSurf/backend/issues/47)) ([da6f329](https://git.demonkernel.io.vn/FoodSurf/backend/commit/da6f3295ddc8f34836ae7009c2c76e18e414b4df))
|
||||
|
||||
## [1.5.22](https://git.demonkernel.io.vn/FoodSurf/backend/compare/v1.5.21...v1.5.22) (2026-05-07)
|
||||
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>com.drinkool</groupId>
|
||||
<artifactId>drinkool</artifactId>
|
||||
<version>1.5.22</version>
|
||||
<version>1.5.23</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -54,6 +54,12 @@
|
||||
<artifactId>jose4j</artifactId>
|
||||
<version>0.9.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.datafaker</groupId>
|
||||
<artifactId>datafaker</artifactId>
|
||||
<version>2.0.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-messaging-kafka</artifactId>
|
||||
|
||||
@@ -3,11 +3,20 @@ package com.drinkool.entities;
|
||||
import com.drinkool.Role;
|
||||
import com.drinkool.models.UserModel;
|
||||
import jakarta.persistence.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Entity
|
||||
@Table(name = Role.Manager)
|
||||
public class ManagerEntity extends UserModel {
|
||||
|
||||
@OneToMany(
|
||||
mappedBy = "serve",
|
||||
cascade = CascadeType.ALL,
|
||||
orphanRemoval = true
|
||||
)
|
||||
public List<StaffEntity> staffs = new ArrayList<>();
|
||||
|
||||
public ManagerEntity() {
|
||||
super(Role.Manager);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.drinkool.entities;
|
||||
|
||||
import com.drinkool.Role;
|
||||
import com.drinkool.models.UserModel;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
import jakarta.transaction.Transactional;
|
||||
|
||||
@Entity
|
||||
@Table(name = Role.Staff)
|
||||
public class StaffEntity extends UserModel {
|
||||
|
||||
@Transactional
|
||||
public void signup(
|
||||
String name,
|
||||
String phone,
|
||||
String rawPassword,
|
||||
ManagerEntity serve
|
||||
) {
|
||||
this.serve = serve;
|
||||
super.signup(name, phone, rawPassword);
|
||||
}
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "managerId")
|
||||
public ManagerEntity serve;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.drinkool.filters;
|
||||
|
||||
import com.drinkool.lib.utils.BaseHeaderAuthentication;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
|
||||
@ApplicationScoped
|
||||
public class HeaderRolesFilter extends BaseHeaderAuthentication {}
|
||||
@@ -3,6 +3,7 @@ package com.drinkool.services;
|
||||
import com.drinkool.Role;
|
||||
import com.drinkool.Url;
|
||||
import com.drinkool.entities.ManagerEntity;
|
||||
import com.drinkool.entities.StaffEntity;
|
||||
import com.drinkool.lib.dtos.SmsOtp;
|
||||
import com.drinkool.models.UserModel;
|
||||
import jakarta.inject.Inject;
|
||||
@@ -28,6 +29,10 @@ public class AccountService {
|
||||
ManagerEntity.find("phone", input.phone).firstResult() != null
|
||||
) return Response.accepted().entity(Role.Manager).build();
|
||||
|
||||
if (
|
||||
StaffEntity.find("phone", input.phone).firstResult() != null
|
||||
) return Response.accepted().entity(Role.Staff).build();
|
||||
|
||||
String otp = otpService.generateAndSaveOtp(input.phone);
|
||||
|
||||
System.out.println("OTP cho " + input.phone + " là: " + otp);
|
||||
|
||||
@@ -44,25 +44,25 @@ public class ManagerService extends BaseService<ManagerEntity> {
|
||||
@POST
|
||||
@Path(Url.Login)
|
||||
public Response login(Login input) {
|
||||
ManagerEntity customer = ManagerEntity.find(
|
||||
ManagerEntity manager = ManagerEntity.find(
|
||||
"phone",
|
||||
input.phone
|
||||
).firstResult();
|
||||
|
||||
if (customer == null) return Response.status(Response.Status.BAD_REQUEST)
|
||||
if (manager == null) return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity("InvalidPhoneNumber")
|
||||
.build();
|
||||
|
||||
if (!customer.login(input.password)) return Response.status(
|
||||
if (!manager.login(input.password)) return Response.status(
|
||||
Response.Status.BAD_REQUEST
|
||||
)
|
||||
.entity("InvalidPassword")
|
||||
.build();
|
||||
|
||||
return Response.accepted()
|
||||
.header(InternalValue.userId, customer.id.toString())
|
||||
.header(InternalValue.userId, manager.id.toString())
|
||||
.header(InternalValue.role, Role.Manager)
|
||||
.entity(customer)
|
||||
.entity(manager)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
package com.drinkool.services;
|
||||
|
||||
import com.drinkool.InternalValue;
|
||||
import com.drinkool.Role;
|
||||
import com.drinkool.Url;
|
||||
import com.drinkool.entities.ManagerEntity;
|
||||
import com.drinkool.entities.StaffEntity;
|
||||
import com.drinkool.lib.dtos.Login;
|
||||
import com.drinkool.lib.dtos.Signup;
|
||||
import io.vertx.core.http.HttpServerRequest;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.transaction.Transactional;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import java.util.UUID;
|
||||
|
||||
@Path(Role.Staff)
|
||||
public class StaffService extends BaseService<StaffEntity> {
|
||||
|
||||
@Inject
|
||||
HttpServerRequest request;
|
||||
|
||||
public StaffService() {
|
||||
super(StaffEntity.class);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path(Url.Signup)
|
||||
@RolesAllowed(Role.Manager)
|
||||
@Transactional
|
||||
public Response signup(Signup input) {
|
||||
UUID managerId = UUID.fromString(request.getHeader(InternalValue.userId));
|
||||
ManagerEntity manager = ManagerEntity.findById(managerId);
|
||||
|
||||
if (manager == null) return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity("InvalidManager")
|
||||
.build();
|
||||
|
||||
StaffEntity staff = new StaffEntity();
|
||||
staff.signup(input.name, input.phone, input.password, manager);
|
||||
|
||||
return Response.status(Response.Status.CREATED)
|
||||
.header(InternalValue.userId, staff.id.toString())
|
||||
.header(InternalValue.role, Role.Staff)
|
||||
.build();
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path(Url.Login)
|
||||
public Response login(Login input) {
|
||||
StaffEntity staff = StaffEntity.find("phone", input.phone).firstResult();
|
||||
|
||||
if (staff == null) return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity("InvalidPhoneNumber")
|
||||
.build();
|
||||
|
||||
if (!staff.login(input.password)) return Response.status(
|
||||
Response.Status.BAD_REQUEST
|
||||
)
|
||||
.entity("InvalidPassword")
|
||||
.build();
|
||||
|
||||
return Response.accepted()
|
||||
.header(InternalValue.userId, staff.id.toString())
|
||||
.header(InternalValue.role, Role.Staff)
|
||||
.entity(staff)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
package com.drinkool.services;
|
||||
|
||||
import static io.restassured.RestAssured.*;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import com.drinkool.*;
|
||||
import com.drinkool.entities.StaffEntity;
|
||||
import com.drinkool.lib.dtos.*;
|
||||
import io.quarkus.test.common.QuarkusTestResource;
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import io.quarkus.test.kafka.*;
|
||||
import io.restassured.http.ContentType;
|
||||
import io.smallrye.reactive.messaging.kafka.companion.ConsumerTask;
|
||||
import io.smallrye.reactive.messaging.kafka.companion.KafkaCompanion;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import java.time.Duration;
|
||||
import net.datafaker.Faker;
|
||||
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@QuarkusTest
|
||||
@QuarkusTestResource(KafkaCompanionResource.class)
|
||||
public class StaffServiceTest {
|
||||
|
||||
private final Faker faker = new Faker();
|
||||
|
||||
private String managerId;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
ManagerSignup input = new ManagerSignup();
|
||||
input.name = "Ho Van Quan";
|
||||
input.phone = Utils.generateRandomPhone();
|
||||
input.password = "strong_password";
|
||||
input.eateryName = "Quan Com Ngon";
|
||||
|
||||
var response = given()
|
||||
.contentType("application/json")
|
||||
.body(input)
|
||||
.when()
|
||||
.post(Role.Manager + Url.Signup)
|
||||
.then()
|
||||
.statusCode(201)
|
||||
.header(InternalValue.userId, org.hamcrest.Matchers.notNullValue())
|
||||
.header(InternalValue.role, Role.Manager)
|
||||
.extract();
|
||||
|
||||
managerId = response.header(InternalValue.userId);
|
||||
}
|
||||
|
||||
@Test
|
||||
void signup_Success() {
|
||||
String staffName = faker.name().fullName();
|
||||
String staffPhone = Utils.generateRandomPhone();
|
||||
|
||||
Signup signup = new Signup(
|
||||
staffName,
|
||||
staffPhone,
|
||||
faker.famousLastWords().lastWords()
|
||||
);
|
||||
|
||||
given()
|
||||
.contentType(ContentType.JSON)
|
||||
.header(InternalValue.role, Role.Manager)
|
||||
.header(InternalValue.userId, managerId)
|
||||
.body(signup)
|
||||
.when()
|
||||
.post(Role.Staff + Url.Signup)
|
||||
.then()
|
||||
.statusCode(Response.Status.CREATED.getStatusCode())
|
||||
.header(InternalValue.userId, org.hamcrest.Matchers.notNullValue())
|
||||
.header(InternalValue.role, Role.Staff);
|
||||
|
||||
StaffEntity staff = StaffEntity.find("phone", staffPhone).firstResult();
|
||||
|
||||
assertEquals(managerId, staff.serve.id.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void signup_FailedDueToInvalidRole() {
|
||||
given()
|
||||
.contentType(ContentType.JSON)
|
||||
.header(InternalValue.role, Role.Customer)
|
||||
.header(InternalValue.userId, managerId)
|
||||
.body("")
|
||||
.when()
|
||||
.post(Role.Staff + Url.Signup)
|
||||
.then()
|
||||
.statusCode(Response.Status.FORBIDDEN.getStatusCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLoginSuccess() {
|
||||
String staffName = faker.name().fullName();
|
||||
String staffPhone = Utils.generateRandomPhone();
|
||||
String password = faker.famousLastWords().lastWords();
|
||||
|
||||
Signup signup = new Signup(staffName, staffPhone, password);
|
||||
|
||||
given()
|
||||
.contentType(ContentType.JSON)
|
||||
.header(InternalValue.role, Role.Manager)
|
||||
.header(InternalValue.userId, managerId)
|
||||
.body(signup)
|
||||
.when()
|
||||
.post(Role.Staff + Url.Signup)
|
||||
.then();
|
||||
|
||||
Login loginInput = new Login(staffPhone, password);
|
||||
|
||||
given()
|
||||
.contentType(ContentType.JSON)
|
||||
.body(loginInput)
|
||||
.when()
|
||||
.post(Role.Staff + Url.Login)
|
||||
.then()
|
||||
.statusCode(Response.Status.ACCEPTED.getStatusCode());
|
||||
}
|
||||
}
|
||||
+2
-12
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>com.drinkool</groupId>
|
||||
<artifactId>drinkool</artifactId>
|
||||
<version>1.5.22</version>
|
||||
<version>1.5.23</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -111,16 +111,6 @@
|
||||
<artifactId>quarkus-maven-plugin</artifactId>
|
||||
<version>${quarkus.platform.version}</version>
|
||||
<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>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
@@ -128,7 +118,6 @@
|
||||
<configuration>
|
||||
<parameters>true</parameters>
|
||||
<annotationProcessorPaths>
|
||||
|
||||
<path>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
@@ -181,6 +170,7 @@
|
||||
</property>
|
||||
</activation>
|
||||
<properties>
|
||||
<quarkus.package.jar.enabled>false</quarkus.package.jar.enabled>
|
||||
<skipITs>false</skipITs>
|
||||
<quarkus.native.enabled>true</quarkus.native.enabled>
|
||||
</properties>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>com.drinkool</groupId>
|
||||
<artifactId>drinkool</artifactId>
|
||||
<version>1.5.22</version>
|
||||
<version>1.5.23</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>com.drinkool</groupId>
|
||||
<artifactId>drinkool</artifactId>
|
||||
<version>1.5.22</version>
|
||||
<version>1.5.23</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.drinkool.controller;
|
||||
|
||||
import com.drinkool.InternalValue;
|
||||
import com.drinkool.Url;
|
||||
import com.drinkool.enums.SortBy;
|
||||
import com.drinkool.lib.dtos.GraphqlDto;
|
||||
@@ -9,6 +10,7 @@ import io.smallrye.mutiny.Uni;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.ws.rs.DefaultValue;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.HeaderParam;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
@@ -43,8 +45,12 @@ public class EateryController extends GraphqlBase {
|
||||
|
||||
@POST
|
||||
@Path(Url.Review)
|
||||
public Uni<Response> receiveReview(SendReview input) {
|
||||
return reviewService.receiveReview(input);
|
||||
public Uni<Response> receiveReview(
|
||||
SendReview input,
|
||||
@HeaderParam(InternalValue.userId) UUID userId,
|
||||
@HeaderParam(InternalValue.role) String role
|
||||
) {
|
||||
return reviewService.receiveReview(input, userId, role);
|
||||
}
|
||||
|
||||
@POST
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
package com.drinkool.services;
|
||||
|
||||
import com.drinkool.InternalValue;
|
||||
import com.drinkool.Url;
|
||||
import com.drinkool.enums.SortBy;
|
||||
import com.drinkool.lib.dtos.SendReview;
|
||||
import io.smallrye.mutiny.Uni;
|
||||
import jakarta.ws.rs.DefaultValue;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.HeaderParam;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
@@ -25,5 +27,9 @@ public interface ReviewService {
|
||||
);
|
||||
|
||||
@POST
|
||||
public Uni<Response> receiveReview(SendReview input);
|
||||
public Uni<Response> receiveReview(
|
||||
SendReview input,
|
||||
@HeaderParam(InternalValue.userId) UUID userId,
|
||||
@HeaderParam(InternalValue.role) String role
|
||||
);
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: account-pod
|
||||
image: git.demonkernel.io.vn/foodsurf/account-service:1.5.22
|
||||
image: git.demonkernel.io.vn/foodsurf/account-service:1.5.23
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: cart-pod
|
||||
image: git.demonkernel.io.vn/foodsurf/cart-service:1.5.22
|
||||
image: git.demonkernel.io.vn/foodsurf/cart-service:1.5.23
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
|
||||
@@ -14,7 +14,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: eatery-pod
|
||||
image: git.demonkernel.io.vn/foodsurf/eatery-service:1.5.22
|
||||
image: git.demonkernel.io.vn/foodsurf/eatery-service:1.5.23
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
|
||||
@@ -14,7 +14,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: gateway-pod
|
||||
image: git.demonkernel.io.vn/foodsurf/gateway-service:1.5.22
|
||||
image: git.demonkernel.io.vn/foodsurf/gateway-service:1.5.23
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
|
||||
@@ -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
|
||||
+1
-1
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>com.drinkool</groupId>
|
||||
<artifactId>drinkool</artifactId>
|
||||
<version>1.5.22</version>
|
||||
<version>1.5.23</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -4,4 +4,5 @@ public class Role {
|
||||
|
||||
public static final String Customer = "customer";
|
||||
public static final String Manager = "manager";
|
||||
public static final String Staff = "Staff";
|
||||
}
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
package com.drinkool.lib.dtos;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class Login {
|
||||
|
||||
public String phone;
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
package com.drinkool.lib.dtos;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class Signup {
|
||||
|
||||
public String name;
|
||||
|
||||
Reference in New Issue
Block a user