Compare commits

...

2 Commits

Author SHA1 Message Date
TakahashiNguyen 88244cb56f fix: add payment features (#52)
Release package / release (push) Successful in 30m1s
Co-authored-by: TakahashiNg <83152264+TakahashiNguyen@users.noreply.github.com>
Reviewed-on: #52
2026-05-12 05:22:08 +00:00
gitea-actions 6032a7c31b chore: release [ci skip] 2026-05-12 02:40:32 +00:00
21 changed files with 213 additions and 53 deletions
+8 -2
View File
@@ -20,7 +20,10 @@ jobs:
steps:
- uses: actions/checkout@v6
- name: Install JDK and Maven
- name: Install dependencies with Proxy
env:
http_proxy: "http://host.docker.internal:3142"
https_proxy: "http://host.docker.internal:3142"
run: |
# Kiểm tra proxy có hoạt động không
echo "Acquire::http::Proxy \"$http_proxy\";" > /etc/apt/apt.conf.d/80proxy
@@ -48,7 +51,10 @@ jobs:
steps:
- uses: actions/checkout@v6
- name: Install JDK and Maven
- name: Install dependencies with Proxy
env:
http_proxy: "http://host.docker.internal:3142"
https_proxy: "http://host.docker.internal:3142"
run: |
# Kiểm tra proxy có hoạt động không
echo "Acquire::http::Proxy \"$http_proxy\";" > /etc/apt/apt.conf.d/80proxy
+1 -1
View File
@@ -13,7 +13,7 @@ plugins:
mvn versions:set -DnewVersion=${nextRelease.version} &&
sed -i "/image:.*-service:/ s|:[^:]*$|:${nextRelease.version}|" k8s/base/*.yaml &&
if [ "${branch.name}" = "main" ]; then
mvn package -B -Pnative -Dquarkus.container-image.tag=${nextRelease.version} -DskipTests
mvn package -B -Pnative -Dquarkus.container-image.tag=${nextRelease.version} -DskipTests -Ppush
fi
- - "@saithodev/semantic-release-gitea"
- giteaUrl: "https://git.demonkernel.io.vn/"
+8
View File
@@ -1,3 +1,11 @@
## [1.5.26](https://git.demonkernel.io.vn/FoodSurf/backend/compare/v1.5.25...v1.5.26) (2026-05-12)
### Bug Fixes
* add shift features ([#51](https://git.demonkernel.io.vn/FoodSurf/backend/issues/51)) ([345d4c0](https://git.demonkernel.io.vn/FoodSurf/backend/commit/345d4c056ccf6ab5f7c455a17e8a3eedfde3ac62))
* better getCart ([#49](https://git.demonkernel.io.vn/FoodSurf/backend/issues/49)) ([4290f48](https://git.demonkernel.io.vn/FoodSurf/backend/commit/4290f482f4aa74467982cc5aa6293d9d308538d3))
## [1.5.26](https://git.demonkernel.io.vn/FoodSurf/backend/compare/v1.5.25...v1.5.26) (2026-05-11)
@@ -3,6 +3,7 @@ package com.drinkool.entities;
import com.drinkool.Role;
import com.drinkool.models.UserModel;
import jakarta.persistence.*;
import jakarta.transaction.Transactional;
import java.util.ArrayList;
import java.util.List;
@@ -17,7 +18,22 @@ public class ManagerEntity extends UserModel {
)
public List<StaffEntity> staffs = new ArrayList<>();
@Column(nullable = true)
public String bankAccount;
public ManagerEntity() {
super(Role.Manager);
}
@Transactional
public void signup(
String name,
String phone,
String bankAccount,
String rawPassword
) {
this.bankAccount = bankAccount;
super.signup(name, phone, rawPassword);
}
}
@@ -65,7 +65,6 @@ public class CustomerService extends BaseService<CustomerEntity> {
return Response.accepted()
.header(InternalValue.userId, customer.id.toString())
.header(InternalValue.role, Role.Customer)
.entity(customer)
.build();
}
@@ -8,6 +8,7 @@ import jakarta.enterprise.context.ApplicationScoped;
import jakarta.transaction.Transactional;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.*;
import java.util.UUID;
import org.eclipse.microprofile.reactive.messaging.*;
@Path(Role.Manager)
@@ -27,7 +28,12 @@ public class ManagerService extends BaseService<ManagerEntity> {
public Response signup(ManagerSignup input) {
ManagerEntity newManager = new ManagerEntity();
newManager.signup(input.name, input.phone, input.password);
newManager.signup(
input.name,
input.phone,
input.bankAccount,
input.password
);
ManagerCreate message = new ManagerCreate();
message.id = newManager.id;
@@ -62,7 +68,38 @@ public class ManagerService extends BaseService<ManagerEntity> {
return Response.accepted()
.header(InternalValue.userId, manager.id.toString())
.header(InternalValue.role, Role.Manager)
.entity(manager)
.build();
}
@GET
@Path("/{id}/payment-qr")
public Response getManagerPaymentQr(
@PathParam("id") UUID id,
@QueryParam("amount") Long amount,
@QueryParam("cartId") String cartId
) {
ManagerEntity manager = ManagerEntity.findById(id);
if (manager == null) {
return Response.status(Response.Status.BAD_REQUEST)
.entity("ManagerNotFound")
.build();
}
if (manager.bankAccount == null || manager.bankAccount.isEmpty()) {
return Response.status(Response.Status.BAD_REQUEST)
.entity("ManagerHasNotConfiguredBankInfo")
.build();
}
String description = cartId;
return Response.ok(
String.format(
"https://img.vietqr.io/image/%s-compact.png?amount=%d&addInfo=%s",
manager.bankAccount,
amount,
description
)
).build();
}
}
@@ -67,7 +67,6 @@ public class StaffService extends BaseService<StaffEntity> {
.header(InternalValue.userId, staff.id.toString())
.header(InternalValue.managerId, staff.serve.id.toString())
.header(InternalValue.role, Role.Staff)
.entity(staff)
.build();
}
}
@@ -0,0 +1,22 @@
package com.drinkool.controllers;
import com.drinkool.Role;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.Response;
import java.util.UUID;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
@RegisterRestClient(configKey = "eatery")
@Path(Role.Manager)
public interface ManagerController {
@GET
@Path(Role.Manager + "/{id}/payment-qr")
public Response getManagerPaymentQr(
@PathParam("id") UUID id,
@QueryParam("amount") Long amount,
@QueryParam("cartId") String cartId
);
}
@@ -15,4 +15,6 @@ public class Cart {
public UUID userId;
public UUID eateryId;
public List<CartItem> items;
public long totalAmount;
public String paymentQrUrl;
}
@@ -11,4 +11,7 @@ public class CartItem {
public String productId;
public Integer quantity;
public Double priceAtTimeOfAdding;
public Double subTotal;
}
@@ -2,19 +2,20 @@ package com.drinkool.services;
import com.drinkool.InternalValue;
import com.drinkool.controllers.EateryController;
import com.drinkool.controllers.ManagerController;
import com.drinkool.dtos.Cart;
import com.drinkool.dtos.CartItem;
import io.quarkus.redis.datasource.RedisDataSource;
import io.quarkus.redis.datasource.hash.HashCommands;
import io.quarkus.redis.datasource.value.ValueCommands;
import io.smallrye.mutiny.Uni;
import io.smallrye.mutiny.infrastructure.Infrastructure;
import io.vertx.core.http.HttpServerRequest;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.core.Response;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.eclipse.microprofile.graphql.GraphQLApi;
@@ -35,6 +36,10 @@ public class CartService {
@RestClient
EateryController eateryController;
@Inject
@RestClient
ManagerController managerController;
private final HashCommands<String, String, String> hashCommands;
private final ValueCommands<String, String> valueCommands;
@@ -50,6 +55,7 @@ public class CartService {
return eateryController
.getEateryById(eateryId)
.emitOn(Infrastructure.getDefaultWorkerPool())
.onItem()
.transform(response -> {
if (response.getStatus() != Response.Status.OK.getStatusCode()) {
@@ -58,6 +64,7 @@ public class CartService {
String cartId = UUID.randomUUID().toString();
String redisKey = "cart:" + cartId;
String ownerId = response.getEntity().toString();
hashCommands.hset(
redisKey,
@@ -65,7 +72,9 @@ public class CartService {
"_metadata_owner",
userId != null ? userId : "GUEST",
"_metadata_eatery",
eateryId.toString()
eateryId.toString(),
"_metadata_eateryManagerId",
ownerId
)
);
@@ -87,27 +96,56 @@ public class CartService {
@Query("getCart")
public Cart getCart(@Name("cartId") UUID cartId) throws GraphQLException {
String key = "cart:" + cartId;
String priceKey = "cart_prices:" + cartId;
Map<String, String> data = hashCommands.hgetall(key);
Map<String, String> itemPrices = hashCommands.hgetall(priceKey);
String managerId = hashCommands.hget(key, "_metadata_eateryManagerId");
if (data.isEmpty()) {
throw new GraphQLException("NotFoundCart");
}
List<CartItem> items = new ArrayList<>();
Cart cart = new Cart();
cart.Id = cartId;
cart.eateryId = UUID.fromString(data.get("_metadata_eatery"));
cart.userId = UUID.fromString(data.get("_metadata_owner"));
cart.items = new ArrayList<>();
data.forEach((k, v) -> {
if (!k.startsWith("_")) {
items.add(new CartItem(k, Integer.parseInt(v)));
}
});
long total = 0;
return new Cart(
cartId,
UUID.fromString(data.get("_metadata_owner")),
UUID.fromString(data.get("_metadata_eatery")),
items
);
for (Map.Entry<String, String> entry : data.entrySet()) {
String field = entry.getKey();
if (field.startsWith("_metadata_")) continue;
Integer qty = Integer.parseInt(entry.getValue());
Double price = Double.parseDouble(itemPrices.getOrDefault(field, "0"));
Double subTotal = qty * price;
CartItem item = new CartItem();
item.productId = field;
item.quantity = qty;
item.priceAtTimeOfAdding = price;
item.subTotal = subTotal;
cart.items.add(item);
total += subTotal;
}
cart.totalAmount = total;
try {
Response response = managerController.getManagerPaymentQr(
UUID.fromString(managerId),
total,
cartId.toString()
);
cart.paymentQrUrl = response.readEntity(String.class);
} catch (Exception e) {
cart.paymentQrUrl = "";
}
return cart;
}
@Mutation("addItem")
@@ -117,19 +155,36 @@ public class CartService {
@Name("quantity") long quantity
) {
String key = "cart:" + cartId;
String priceKey = "cart_prices:" + cartId;
UUID eateryId = UUID.fromString(hashCommands.hget(key, "_metadata_eatery"));
String eateryIdStr = hashCommands.hget(key, "_metadata_eatery");
if (eateryIdStr == null) throw new RuntimeException("CartNotFound");
UUID eateryId = UUID.fromString(eateryIdStr);
return eateryController
.checkMenuItemExists(eateryId, menuItemId)
.onItem()
.transform(response -> {
if (response.getStatus() != Response.Status.OK.getStatusCode()) {
throw new RuntimeException("NotFoundEatery");
throw new RuntimeException("NotFoundMenuItemInEatery");
}
Double menuItemPrice = response.readEntity(Double.class);
hashCommands.hincrby(key, menuItemId.toString(), quantity);
hashCommands.hset(
priceKey,
menuItemId.toString(),
String.valueOf(menuItemPrice)
);
hashCommands.getDataSource().key().expire(key, Duration.ofDays(30));
hashCommands
.getDataSource()
.key()
.expire(priceKey, Duration.ofDays(30));
try {
return getCart(cartId);
@@ -21,3 +21,4 @@ quarkus.redis.hosts=redis://${REDIS_SERVICE_SERVICE_HOST:localhost}:${REDIS_SERV
# Rest Client
quarkus.rest-client.eatery.url=http://eatery-service
quarkus.rest-client.account.url=http://account-service
@@ -7,6 +7,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.when;
import com.drinkool.controllers.EateryController;
import com.drinkool.controllers.ManagerController;
import com.drinkool.dtos.Cart;
import io.quarkus.redis.datasource.RedisDataSource;
import io.quarkus.redis.datasource.hash.HashCommands;
@@ -40,6 +41,11 @@ public class CartServiceTest {
@RestClient
EateryController eateryController;
@InjectMock
@RestClient
ManagerController managerController;
@Inject
RedisDataSource redisDataSource;
@@ -59,7 +65,7 @@ public class CartServiceTest {
UUID eateryId = UUID.randomUUID();
when(eateryController.getEateryById(eateryId)).thenReturn(
Uni.createFrom().item(Response.ok().build())
Uni.createFrom().item(Response.ok(UUID.randomUUID()).build())
);
String cartId = cartService
@@ -129,7 +135,7 @@ public class CartServiceTest {
hashCommands.hset(key, "_metadata_owner", UUID.randomUUID().toString());
when(eateryController.checkMenuItemExists(eateryId, menuItemId)).thenReturn(
Uni.createFrom().item(Response.ok().build())
Uni.createFrom().item(Response.ok(17.0).build())
);
Cart result = cartService
@@ -1,9 +1,8 @@
package com.drinkool.dtos;
import java.time.LocalTime;
import org.eclipse.microprofile.graphql.NonNull;
import lombok.NoArgsConstructor;
import org.eclipse.microprofile.graphql.NonNull;
@NoArgsConstructor
public class CreateShift {
@@ -5,7 +5,6 @@ import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import java.util.List;
import java.util.UUID;
@@ -42,33 +42,33 @@ public class EateryService {
EateryEntity eatery = EateryEntity.findById(id);
if (eatery == null) {
return Response.status(Response.Status.NOT_FOUND)
return Response.status(Response.Status.BAD_REQUEST)
.entity("NotFoundEatery")
.build();
}
return Response.ok().build();
return Response.ok(eatery.ownerId.toString()).build();
}
@GET
@Path("/{eateryId}/{menuItemId}")
public Response checkMenuItemExists(
public Response getMenuItemPrice(
@PathParam("eateryId") UUID eateryId,
@PathParam("menuItemId") UUID menuItemId
) {
long count = MenuItemEntity.find(
"id = ?2 and eatery.id = ?1",
eateryId,
menuItemId
).count();
MenuItemEntity menuItem = MenuItemEntity.find(
"id = ?1 and eatery.id = ?2",
menuItemId,
eateryId
).firstResult();
if (count == 0) {
return Response.status(Response.Status.NOT_FOUND)
if (menuItem == null) {
return Response.status(Response.Status.BAD_REQUEST)
.entity("NotFoundMenuItem")
.build();
}
return Response.ok().build();
return Response.ok(menuItem.price).build();
}
@Incoming("manager-in")
@@ -28,10 +28,12 @@ quarkus.native.additional-build-args=--future-defaults=all
# Kafka
%prod.mp.messaging.connector.smallrye-kafka.bootstrap.servers=${KAFKA_SERVICE_SERVICE_HOST:host.docker.internal}:9092
mp.messaging.incoming.manager-in.connector=smallrye-in-memory
## Create restaurant topic
mp.messaging.incoming.manager-in.connector=smallrye-kafka
mp.messaging.incoming.manager-in.topic=create-restaurant
mp.messaging.incoming.manager-in.auto.offset.reset=earliest
quarkus.messaging.kafka.serializer-generation.enabled=true
quarkus.messaging.kafka.serializer-generation.enabled=true
# Rest Client
quarkus.rest-client.account.url=http://account-service
@@ -103,7 +103,7 @@ public class ShiftServiceTest {
given()
.contentType("application/json")
.header(InternalValue.role, Role.Staff)
.header(InternalValue.userId, staffId.toString())
.header(InternalValue.userId, staffId.toString())
.header(InternalValue.managerId, ownerId.toString())
.body("{ \"query\": \"" + mutation + "\" }")
.when()
+2 -2
View File
@@ -71,7 +71,7 @@
<profiles>
<profile>
<id>push-python-image</id>
<id>push</id>
<activation>
<property>
<name>quarkus.container-image.push</name>
@@ -86,7 +86,7 @@
<executions>
<execution>
<id>docker-push-python</id>
<phase>install</phase>
<phase>package</phase>
<goals>
<goal>exec</goal>
</goals>
+13 -9
View File
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<?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"
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>
@@ -20,9 +20,11 @@
<compiler-plugin.version>3.15.0</compiler-plugin.version>
<maven.compiler.release>25</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>
@@ -130,7 +132,8 @@
<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>
<forkCount>1</forkCount>
@@ -154,7 +157,8 @@
<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>
@@ -176,4 +180,4 @@
</properties>
</profile>
</profiles>
</project>
</project>
@@ -3,4 +3,6 @@ package com.drinkool.lib.dtos;
public class ManagerSignup extends Signup {
public String eateryName;
public String bankAccount;
}