fix: add staff entity (#48)
Release package / release (push) Successful in 30m26s

Co-authored-by: TakahashiNg <83152264+TakahashiNguyen@users.noreply.github.com>
Reviewed-on: #48
This commit was merged in pull request #48.
This commit is contained in:
2026-05-07 14:45:44 +00:00
parent bd548278b4
commit 71575ec2ad
15 changed files with 282 additions and 9 deletions
+2 -1
View File
@@ -7,7 +7,8 @@
"**/Thumbs.db": true, "**/Thumbs.db": true,
"**/target": true, "**/target": true,
"**/docker": true, "**/docker": true,
"node_modules": true "node_modules": true,
"**/.mvn": true
}, },
"explorerExclude.backup": {}, "explorerExclude.backup": {},
"java.compile.nullAnalysis.mode": "automatic", "java.compile.nullAnalysis.mode": "automatic",
+6
View File
@@ -54,6 +54,12 @@
<artifactId>jose4j</artifactId> <artifactId>jose4j</artifactId>
<version>0.9.3</version> <version>0.9.3</version>
</dependency> </dependency>
<dependency>
<groupId>net.datafaker</groupId>
<artifactId>datafaker</artifactId>
<version>2.0.2</version>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>io.quarkus</groupId> <groupId>io.quarkus</groupId>
<artifactId>quarkus-messaging-kafka</artifactId> <artifactId>quarkus-messaging-kafka</artifactId>
@@ -3,11 +3,20 @@ package com.drinkool.entities;
import com.drinkool.Role; import com.drinkool.Role;
import com.drinkool.models.UserModel; import com.drinkool.models.UserModel;
import jakarta.persistence.*; import jakarta.persistence.*;
import java.util.ArrayList;
import java.util.List;
@Entity @Entity
@Table(name = Role.Manager) @Table(name = Role.Manager)
public class ManagerEntity extends UserModel { public class ManagerEntity extends UserModel {
@OneToMany(
mappedBy = "serve",
cascade = CascadeType.ALL,
orphanRemoval = true
)
public List<StaffEntity> staffs = new ArrayList<>();
public ManagerEntity() { public ManagerEntity() {
super(Role.Manager); 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.Role;
import com.drinkool.Url; import com.drinkool.Url;
import com.drinkool.entities.ManagerEntity; import com.drinkool.entities.ManagerEntity;
import com.drinkool.entities.StaffEntity;
import com.drinkool.lib.dtos.SmsOtp; import com.drinkool.lib.dtos.SmsOtp;
import com.drinkool.models.UserModel; import com.drinkool.models.UserModel;
import jakarta.inject.Inject; import jakarta.inject.Inject;
@@ -28,6 +29,10 @@ public class AccountService {
ManagerEntity.find("phone", input.phone).firstResult() != null ManagerEntity.find("phone", input.phone).firstResult() != null
) return Response.accepted().entity(Role.Manager).build(); ) 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); String otp = otpService.generateAndSaveOtp(input.phone);
System.out.println("OTP cho " + input.phone + " là: " + otp); System.out.println("OTP cho " + input.phone + " là: " + otp);
@@ -44,25 +44,25 @@ public class ManagerService extends BaseService<ManagerEntity> {
@POST @POST
@Path(Url.Login) @Path(Url.Login)
public Response login(Login input) { public Response login(Login input) {
ManagerEntity customer = ManagerEntity.find( ManagerEntity manager = ManagerEntity.find(
"phone", "phone",
input.phone input.phone
).firstResult(); ).firstResult();
if (customer == null) return Response.status(Response.Status.BAD_REQUEST) if (manager == null) return Response.status(Response.Status.BAD_REQUEST)
.entity("InvalidPhoneNumber") .entity("InvalidPhoneNumber")
.build(); .build();
if (!customer.login(input.password)) return Response.status( if (!manager.login(input.password)) return Response.status(
Response.Status.BAD_REQUEST Response.Status.BAD_REQUEST
) )
.entity("InvalidPassword") .entity("InvalidPassword")
.build(); .build();
return Response.accepted() return Response.accepted()
.header(InternalValue.userId, customer.id.toString()) .header(InternalValue.userId, manager.id.toString())
.header(InternalValue.role, Role.Manager) .header(InternalValue.role, Role.Manager)
.entity(customer) .entity(manager)
.build(); .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());
}
}
+1
View File
@@ -181,6 +181,7 @@
</property> </property>
</activation> </activation>
<properties> <properties>
<quarkus.package.jar.enabled>false</quarkus.package.jar.enabled>
<skipITs>false</skipITs> <skipITs>false</skipITs>
<quarkus.native.enabled>true</quarkus.native.enabled> <quarkus.native.enabled>true</quarkus.native.enabled>
</properties> </properties>
@@ -1,5 +1,6 @@
package com.drinkool.controller; package com.drinkool.controller;
import com.drinkool.InternalValue;
import com.drinkool.Url; import com.drinkool.Url;
import com.drinkool.enums.SortBy; import com.drinkool.enums.SortBy;
import com.drinkool.lib.dtos.GraphqlDto; import com.drinkool.lib.dtos.GraphqlDto;
@@ -9,6 +10,7 @@ import io.smallrye.mutiny.Uni;
import jakarta.inject.Inject; import jakarta.inject.Inject;
import jakarta.ws.rs.DefaultValue; import jakarta.ws.rs.DefaultValue;
import jakarta.ws.rs.GET; import jakarta.ws.rs.GET;
import jakarta.ws.rs.HeaderParam;
import jakarta.ws.rs.POST; import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path; import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam; import jakarta.ws.rs.PathParam;
@@ -43,8 +45,12 @@ public class EateryController extends GraphqlBase {
@POST @POST
@Path(Url.Review) @Path(Url.Review)
public Uni<Response> receiveReview(SendReview input) { public Uni<Response> receiveReview(
return reviewService.receiveReview(input); SendReview input,
@HeaderParam(InternalValue.userId) UUID userId,
@HeaderParam(InternalValue.role) String role
) {
return reviewService.receiveReview(input, userId, role);
} }
@POST @POST
@@ -1,11 +1,13 @@
package com.drinkool.services; package com.drinkool.services;
import com.drinkool.InternalValue;
import com.drinkool.Url; import com.drinkool.Url;
import com.drinkool.enums.SortBy; import com.drinkool.enums.SortBy;
import com.drinkool.lib.dtos.SendReview; import com.drinkool.lib.dtos.SendReview;
import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.Uni;
import jakarta.ws.rs.DefaultValue; import jakarta.ws.rs.DefaultValue;
import jakarta.ws.rs.GET; import jakarta.ws.rs.GET;
import jakarta.ws.rs.HeaderParam;
import jakarta.ws.rs.POST; import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path; import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam; import jakarta.ws.rs.PathParam;
@@ -25,5 +27,9 @@ public interface ReviewService {
); );
@POST @POST
public Uni<Response> receiveReview(SendReview input); public Uni<Response> receiveReview(
SendReview input,
@HeaderParam(InternalValue.userId) UUID userId,
@HeaderParam(InternalValue.role) String role
);
} }
+1
View File
@@ -4,4 +4,5 @@ public class Role {
public static final String Customer = "customer"; public static final String Customer = "customer";
public static final String Manager = "manager"; public static final String Manager = "manager";
public static final String Staff = "Staff";
} }
@@ -1,5 +1,10 @@
package com.drinkool.lib.dtos; package com.drinkool.lib.dtos;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
@AllArgsConstructor
@NoArgsConstructor
public class Login { public class Login {
public String phone; public String phone;
@@ -1,5 +1,10 @@
package com.drinkool.lib.dtos; package com.drinkool.lib.dtos;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@AllArgsConstructor
public class Signup { public class Signup {
public String name; public String name;