@@ -1,7 +1,8 @@
|
||||
package com.drinkool;
|
||||
|
||||
import com.drinkool.dtos.*;
|
||||
import com.drinkool.models.Customer;
|
||||
import com.drinkool.entities.CustomerEntity;
|
||||
import com.drinkool.entities.CustomerEntity;
|
||||
import com.drinkool.services.*;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.transaction.Transactional;
|
||||
@@ -18,7 +19,7 @@ public class RestAPI {
|
||||
@Path("/signup")
|
||||
@Transactional
|
||||
public Response signup(Signup input) {
|
||||
Customer newCustomer = new Customer();
|
||||
CustomerEntity newCustomer = new CustomerEntity();
|
||||
|
||||
newCustomer.signup(input.name, input.phone, input.password);
|
||||
|
||||
@@ -29,7 +30,7 @@ public class RestAPI {
|
||||
@Path("/quickSignup")
|
||||
@Transactional
|
||||
public Response quickSignup(QuickSignup input) {
|
||||
Customer newCustomer = new Customer();
|
||||
CustomerEntity newCustomer = new CustomerEntity();
|
||||
|
||||
newCustomer.signup(input.phone);
|
||||
|
||||
@@ -39,7 +40,7 @@ public class RestAPI {
|
||||
@POST
|
||||
@Path("/login")
|
||||
public Response login(Login input) {
|
||||
Customer customer = Customer.find("phone", input.phone).firstResult();
|
||||
CustomerEntity customer = CustomerEntity.find("phone", input.phone).firstResult();
|
||||
|
||||
if (customer == null) throw new BadRequestException("InvalidPhoneNumber");
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.drinkool.entities;
|
||||
|
||||
import com.drinkool.models.UserModel;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
|
||||
@Entity
|
||||
@Table(name = "customer")
|
||||
public class CustomerEntity extends UserModel {}
|
||||
@@ -1,7 +0,0 @@
|
||||
package com.drinkool.models;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
|
||||
@Entity
|
||||
@Table(name = "customer")
|
||||
public class Customer extends User {}
|
||||
+2
-6
@@ -11,7 +11,7 @@ import java.util.UUID;
|
||||
@Entity
|
||||
@Inheritance(strategy = InheritanceType.JOINED)
|
||||
@Table(name = "users")
|
||||
public abstract class User extends PanacheEntity {
|
||||
public abstract class UserModel extends PanacheEntity {
|
||||
|
||||
@Column(nullable = false)
|
||||
public String name;
|
||||
@@ -22,10 +22,6 @@ public abstract class User extends PanacheEntity {
|
||||
@Column(nullable = false, length = 1024)
|
||||
public String hashedPassword;
|
||||
|
||||
public User() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void signup(String phone) {
|
||||
this.signup(phone, phone, UUID.randomUUID().toString().substring(0, 32));
|
||||
@@ -34,7 +30,7 @@ public abstract class User extends PanacheEntity {
|
||||
@Transactional
|
||||
public void signup(String name, String phone, String rawPassword) {
|
||||
// Check existed user
|
||||
Customer existedUser = User.find("phone", phone).firstResult();
|
||||
UserModel existedUser = UserModel.find("phone", phone).firstResult();
|
||||
|
||||
if (existedUser != null) throw new BadRequestException("ExistedUser");
|
||||
|
||||
+18
-15
@@ -1,9 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<project
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"
|
||||
>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
@@ -20,11 +18,9 @@
|
||||
<compiler-plugin.version>3.15.0</compiler-plugin.version>
|
||||
<maven.compiler.release>21</maven.compiler.release>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding
|
||||
>UTF-8</project.reporting.outputEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
|
||||
<quarkus.platform.group-id
|
||||
>io.quarkus.platform</quarkus.platform.group-id>
|
||||
<quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
|
||||
<quarkus.platform.version>3.32.4</quarkus.platform.version>
|
||||
<skipITs>true</skipITs>
|
||||
<surefire-plugin.version>3.5.4</surefire-plugin.version>
|
||||
@@ -48,6 +44,11 @@
|
||||
<artifactId>lib</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.awaitility</groupId>
|
||||
<artifactId>awaitility</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-hibernate-orm-panache</artifactId>
|
||||
@@ -72,6 +73,10 @@
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-hibernate-orm</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-rest-jackson</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-junit</artifactId>
|
||||
@@ -100,8 +105,7 @@
|
||||
<configuration>
|
||||
<argLine>@{argLine}</argLine>
|
||||
<systemPropertyVariables>
|
||||
<java.util.logging.manager
|
||||
>org.jboss.logmanager.LogManager</java.util.logging.manager>
|
||||
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
|
||||
<maven.home>${maven.home}</maven.home>
|
||||
</systemPropertyVariables>
|
||||
</configuration>
|
||||
@@ -122,8 +126,7 @@
|
||||
<systemPropertyVariables>
|
||||
<native.image.path>
|
||||
${project.build.directory}/${project.build.finalName}-runner</native.image.path>
|
||||
<java.util.logging.manager
|
||||
>org.jboss.logmanager.LogManager</java.util.logging.manager>
|
||||
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
|
||||
<maven.home>${maven.home}</maven.home>
|
||||
</systemPropertyVariables>
|
||||
</configuration>
|
||||
@@ -146,4 +149,4 @@
|
||||
</properties>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
||||
</project>
|
||||
@@ -1,29 +0,0 @@
|
||||
package com.drinkool;
|
||||
|
||||
import io.quarkus.hibernate.orm.panache.PanacheEntity;
|
||||
import jakarta.persistence.Entity;
|
||||
|
||||
/**
|
||||
* Example JPA entity defined as a Panache Entity.
|
||||
* An ID field of Long type is provided, if you want to define your own ID field extends <code>PanacheEntityBase</code> instead.
|
||||
*
|
||||
* This uses the active record pattern, you can also use the repository pattern instead:
|
||||
* {@see https://quarkus.io/guides/hibernate-orm-panache#solution-2-using-the-repository-pattern}.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* {@code
|
||||
* public void doSomething() {
|
||||
* MyEntity entity1 = new MyEntity();
|
||||
* entity1.field = "field-1";
|
||||
* entity1.persist();
|
||||
*
|
||||
* List<MyEntity> entities = MyEntity.listAll();
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
@Entity
|
||||
public class MyEntity extends PanacheEntity {
|
||||
|
||||
public String field;
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package com.drinkool;
|
||||
|
||||
import io.quarkus.runtime.StartupEvent;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.enterprise.event.Observes;
|
||||
import java.util.stream.Stream;
|
||||
import org.eclipse.microprofile.reactive.messaging.Channel;
|
||||
import org.eclipse.microprofile.reactive.messaging.Emitter;
|
||||
import org.eclipse.microprofile.reactive.messaging.Incoming;
|
||||
import org.eclipse.microprofile.reactive.messaging.Outgoing;
|
||||
|
||||
@ApplicationScoped
|
||||
public class MyMessagingApplication {
|
||||
|
||||
/**
|
||||
* Injects an emitter to send messages to the "words-out" channel.
|
||||
*/
|
||||
@Channel("words-out")
|
||||
Emitter<String> emitter;
|
||||
|
||||
/**
|
||||
* Sends message to the "words-out" channel, can be used from a JAX-RS resource or any bean of your application.
|
||||
* Messages are sent to the broker.
|
||||
**/
|
||||
void onStart(@Observes StartupEvent ev) {
|
||||
Stream.of("Hello", "with", "Quarkus", "Messaging", "message").forEach(
|
||||
string -> emitter.send(string)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Consume the message from the "words-in" channel, uppercase it and send it to the uppercase channel.
|
||||
* This method is called by the framework when a message is received on the "words-in" channel (from the broker).
|
||||
**/
|
||||
@Incoming("words-in")
|
||||
@Outgoing("uppercase")
|
||||
public String toUpperCase(String message) {
|
||||
return message.toUpperCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Consume the uppercase channel (coming from within the application) and print the messages to the console.
|
||||
**/
|
||||
@Incoming("uppercase")
|
||||
public void sink(String word) {
|
||||
System.out.println(">> " + word);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.drinkool;
|
||||
|
||||
import com.drinkool.dtos.SendReview;
|
||||
import com.drinkool.entities.ReviewEntity;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.transaction.Transactional;
|
||||
import org.eclipse.microprofile.reactive.messaging.Incoming;
|
||||
|
||||
@ApplicationScoped
|
||||
public class ReviewService {
|
||||
|
||||
/**
|
||||
* Injects an emitter to send messages to the "words-out" channel.
|
||||
*/
|
||||
// @Channel("words-out")
|
||||
// Emitter<String> emitter;
|
||||
|
||||
// /**
|
||||
// * Sends message to the "words-out" channel, can be used from a JAX-RS resource or any bean of your application.
|
||||
// * Messages are sent to the broker.
|
||||
// **/
|
||||
// void onStart(@Observes StartupEvent ev) {
|
||||
// Stream.of("Hello", "with", "Quarkus", "Messaging", "message").forEach(
|
||||
// string -> emitter.send(string)
|
||||
// );
|
||||
// }
|
||||
|
||||
/**
|
||||
* Consume the message from the "words-in" channel, uppercase it and send it to the uppercase channel.
|
||||
* This method is called by the framework when a message is received on the "words-in" channel (from the broker).
|
||||
**/
|
||||
// @Incoming("words-in")
|
||||
// @Outgoing("uppercase")
|
||||
// public String toUpperCase(String message) {
|
||||
// return message.toUpperCase();
|
||||
// }
|
||||
|
||||
/**
|
||||
* Consume the uppercase channel (coming from within the application) and print the messages to the console.
|
||||
**/
|
||||
// @Incoming("uppercase")
|
||||
// public void sink(String word) {
|
||||
// System.out.println(">> " + word);
|
||||
// }
|
||||
|
||||
@Incoming("review")
|
||||
@Transactional
|
||||
public void receiveReview(SendReview input) {
|
||||
ReviewEntity newReview = new ReviewEntity();
|
||||
|
||||
newReview.receive(input.reviewerId, input.orderId, input.review);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.drinkool.entities;
|
||||
|
||||
import com.drinkool.models.ReviewModel;
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.transaction.Transactional;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
@Table(name = "reviews")
|
||||
public class ReviewEntity extends ReviewModel {
|
||||
|
||||
public UUID reviewerId;
|
||||
|
||||
public UUID eateryId;
|
||||
|
||||
@Transactional
|
||||
public void receive(UUID reviewerId, UUID eateryId, ReviewModel review) {
|
||||
this.reviewerId = reviewerId;
|
||||
|
||||
this.eateryId = eateryId;
|
||||
|
||||
this.rating = review.rating;
|
||||
|
||||
this.comment = review.comment;
|
||||
|
||||
this.persist();
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package com.drinkool.models;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
|
||||
@Entity
|
||||
@Table(name = "reviews")
|
||||
public class Review extends BaseReview {}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.drinkool.utils;
|
||||
|
||||
import com.drinkool.dtos.SendReview;
|
||||
|
||||
import io.quarkus.kafka.client.serialization.ObjectMapperDeserializer;
|
||||
|
||||
public class SendReviewDeserializer extends ObjectMapperDeserializer<SendReview> {
|
||||
public SendReviewDeserializer() {
|
||||
super(SendReview.class);
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,33 @@
|
||||
mp.messaging.incoming.words-in.auto.offset.reset=earliest
|
||||
mp.messaging.incoming.words-in.topic=words
|
||||
mp.messaging.outgoing.words-out.topic=words
|
||||
# Cấu hình Database
|
||||
|
||||
# Kafka
|
||||
# Địa chỉ server Kafka
|
||||
kafka.bootstrap.servers=localhost:9092
|
||||
|
||||
# --- CẤU HÌNH GỬI TIN (OUTGOING) ---
|
||||
# Kênh "eatery-out" sẽ gửi vào topic "eatery-events"
|
||||
# mp.messaging.outgoing.eatery-out.connector=smallrye-kafka
|
||||
# mp.messaging.outgoing.eatery-out.topic=eatery-events
|
||||
# # Tự động biến Object thành JSON
|
||||
# mp.messaging.outgoing.eatery-out.value.serializer=io.quarkus.kafka.client.serialization.ObjectMapperSerializer
|
||||
|
||||
# --- CẤU HÌNH NHẬN TIN (INCOMING) ---
|
||||
# Kênh "review-in" sẽ nhận từ topic "review-topic"
|
||||
mp.messaging.incoming.review.connector=smallrye-kafka
|
||||
mp.messaging.incoming.review.topic=review-topic
|
||||
# Tự động biến JSON thành Object (giả sử là class ReviewModel)
|
||||
mp.messaging.incoming.review.value.deserializer=com.drinkool.utils.SendReviewDeserializer
|
||||
|
||||
# Postgress
|
||||
# Cấu hình Database
|
||||
quarkus.datasource.db-kind=postgresql
|
||||
quarkus.datasource.username=postgres
|
||||
quarkus.datasource.password=password
|
||||
quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/eatery
|
||||
|
||||
# Tự động tạo bảng từ Entity (chỉ dùng cho môi trường dev)
|
||||
quarkus.hibernate-orm.database.generation=update
|
||||
|
||||
# Dependency
|
||||
quarkus.index-dependency.lib.group-id=com.drinkool
|
||||
quarkus.index-dependency.lib.artifact-id=lib
|
||||
@@ -9,12 +9,12 @@ import org.junit.jupiter.api.Test;
|
||||
@QuarkusTest
|
||||
class MyMessagingApplicationTest {
|
||||
|
||||
@Inject
|
||||
MyMessagingApplication application;
|
||||
// @Inject
|
||||
// MyMessagingApplication application;
|
||||
|
||||
@Test
|
||||
void test() {
|
||||
assertEquals("HELLO", application.toUpperCase("Hello"));
|
||||
assertEquals("BONJOUR", application.toUpperCase("bonjour"));
|
||||
}
|
||||
// @Test
|
||||
// void test() {
|
||||
// assertEquals("HELLO", application.toUpperCase("Hello"));
|
||||
// assertEquals("BONJOUR", application.toUpperCase("bonjour"));
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.drinkool;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import com.drinkool.dtos.SendReview;
|
||||
import com.drinkool.entities.ReviewEntity;
|
||||
import com.drinkool.models.ReviewModel;
|
||||
import io.quarkus.narayana.jta.QuarkusTransaction;
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import jakarta.inject.Inject;
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.awaitility.Awaitility;
|
||||
import org.eclipse.microprofile.reactive.messaging.*;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@QuarkusTest
|
||||
public class ReviewServiceTest {
|
||||
|
||||
@Inject
|
||||
ReviewService service;
|
||||
|
||||
@Inject
|
||||
@Channel("review-test")
|
||||
Emitter<SendReview> producer;
|
||||
|
||||
@Test
|
||||
void receiveReview() {
|
||||
Random random = new Random();
|
||||
|
||||
UUID reviewerId = UUID.randomUUID();
|
||||
UUID orderId = UUID.randomUUID();
|
||||
String comment = UUID.randomUUID().toString();
|
||||
int rating = random.nextInt();
|
||||
|
||||
SendReview input = new SendReview();
|
||||
input.reviewerId = reviewerId;
|
||||
input.orderId = orderId;
|
||||
ReviewModel reviewModel = new ReviewModel();
|
||||
reviewModel.comment = comment;
|
||||
reviewModel.rating = rating;
|
||||
input.review = reviewModel;
|
||||
|
||||
producer.send(input);
|
||||
|
||||
Awaitility.await()
|
||||
.atMost(5, TimeUnit.SECONDS)
|
||||
.untilAsserted(() -> {
|
||||
ReviewEntity saved = QuarkusTransaction.requiringNew().call(() ->
|
||||
ReviewEntity.find("reviewerId", reviewerId).firstResult()
|
||||
);
|
||||
assertNotNull(saved);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
# Kafka
|
||||
kafka.bootstrap.servers=localhost:9092
|
||||
|
||||
# --- CẤU HÌNH GỬI TIN (OUTGOING) ---
|
||||
# Kênh "eatery-out" sẽ gửi vào topic "eatery-events"
|
||||
# mp.messaging.outgoing.eatery-out.connector=smallrye-kafka
|
||||
# mp.messaging.outgoing.eatery-out.topic=eatery-events
|
||||
# # Tự động biến Object thành JSON
|
||||
# mp.messaging.outgoing.eatery-out.value.serializer=io.quarkus.kafka.client.serialization.ObjectMapperSerializer
|
||||
|
||||
# --- CẤU HÌNH NHẬN TIN (INCOMING) ---
|
||||
mp.messaging.outgoing.review-test.connector=smallrye-kafka
|
||||
mp.messaging.outgoing.review-test.topic=review-topic
|
||||
# Sửa serializer cho đúng loại dành cho việc GỬI
|
||||
mp.messaging.outgoing.review-test.value.serializer=io.quarkus.kafka.client.serialization.ObjectMapperSerializer
|
||||
@@ -1,5 +1 @@
|
||||
mp.messaging.incoming.words-in.auto.offset.reset=earliest
|
||||
mp.messaging.incoming.words-in.topic=words
|
||||
mp.messaging.outgoing.words-out.topic=words
|
||||
|
||||
quarkus.rest-client.account.url=http://localhost:502
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.drinkool.dtos;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import com.drinkool.models.ReviewModel;
|
||||
|
||||
public class SendReview {
|
||||
public UUID reviewerId;
|
||||
public UUID orderId;
|
||||
public ReviewModel review;
|
||||
}
|
||||
+1
-1
@@ -5,7 +5,7 @@ import jakarta.persistence.*;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@MappedSuperclass
|
||||
public class BaseReview extends PanacheEntity {
|
||||
public class ReviewModel extends PanacheEntity {
|
||||
|
||||
@Column
|
||||
public int rating;
|
||||
Reference in New Issue
Block a user