From 91072d6d503f943b858ffebf2fb55d52bcba24a5 Mon Sep 17 00:00:00 2001 From: Timi Date: Sun, 13 Sep 2026 01:25:48 +0800 Subject: [PATCH 1/3] auto complete order --- .../GaoOrderStraightThroughService.java | 17 +++ .../gao/service/GaoProductStockService.java | 3 + .../GaoCustomerPackageServiceImplement.java | 5 + .../GaoOrderFulfillmentServiceImplement.java | 3 +- ...oOrderStraightThroughServiceImplement.java | 129 ++++++++++++++++++ .../GaoProductStockServiceImplement.java | 19 ++- .../GaoSalesOrderServiceImplement.java | 6 +- .../vo/GaoCustomerPackagePurchaseRequest.java | 3 + .../GaoDirectProductOrderCreateRequest.java | 3 + .../vo/GaoOrderStraightThroughRequest.java | 38 ++++++ 10 files changed, 220 insertions(+), 6 deletions(-) create mode 100644 src/main/java/com/imyeyu/api/modules/gao/service/GaoOrderStraightThroughService.java create mode 100644 src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoOrderStraightThroughServiceImplement.java create mode 100644 src/main/java/com/imyeyu/api/modules/gao/vo/GaoOrderStraightThroughRequest.java diff --git a/src/main/java/com/imyeyu/api/modules/gao/service/GaoOrderStraightThroughService.java b/src/main/java/com/imyeyu/api/modules/gao/service/GaoOrderStraightThroughService.java new file mode 100644 index 0000000..b624eb7 --- /dev/null +++ b/src/main/java/com/imyeyu/api/modules/gao/service/GaoOrderStraightThroughService.java @@ -0,0 +1,17 @@ +package com.imyeyu.api.modules.gao.service; + +import com.imyeyu.api.modules.gao.entity.GaoSalesOrder; +import com.imyeyu.api.modules.gao.vo.GaoOrderStraightThroughRequest; + +/// GAO 订单创建直通服务 +/// +/// @author Codex +/// @since 2026-09-13 +public interface GaoOrderStraightThroughService { + + /// 在订单创建事务内完成可选的全额收款和全部履约 + /// + /// @param order 销售订单 + /// @param straightThrough 直通请求,空为不直通 + void complete(GaoSalesOrder order, GaoOrderStraightThroughRequest straightThrough); +} diff --git a/src/main/java/com/imyeyu/api/modules/gao/service/GaoProductStockService.java b/src/main/java/com/imyeyu/api/modules/gao/service/GaoProductStockService.java index 43a9999..f85156c 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/service/GaoProductStockService.java +++ b/src/main/java/com/imyeyu/api/modules/gao/service/GaoProductStockService.java @@ -46,6 +46,9 @@ public interface GaoProductStockService { /// 为履约预占库存 boolean hasSufficientForFulfillment(String storeId, List items); + /// 获取履约库存缺口提示,无缺口时返回 null + String getFulfillmentStockShortageMessage(String storeId, List items); + /// 为履约预占库存 List reserveForFulfillment(String storeId, String bizId, List items, String requestNo, String operatorUserId); diff --git a/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoCustomerPackageServiceImplement.java b/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoCustomerPackageServiceImplement.java index c6faeb0..580e226 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoCustomerPackageServiceImplement.java +++ b/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoCustomerPackageServiceImplement.java @@ -13,6 +13,7 @@ import com.imyeyu.api.modules.gao.entity.GaoSalesOrder; import com.imyeyu.api.modules.gao.entity.GaoSalesOrderItem; import com.imyeyu.api.modules.gao.mapper.GaoCustomerPackageMapper; import com.imyeyu.api.modules.gao.service.GaoSalesOrderService; +import com.imyeyu.api.modules.gao.service.GaoOrderStraightThroughService; import com.imyeyu.api.modules.gao.service.GaoCustomerService; import com.imyeyu.api.modules.gao.service.GaoCustomerPackageService; import com.imyeyu.api.modules.gao.service.GaoPackageConfigService; @@ -67,6 +68,7 @@ public class GaoCustomerPackageServiceImplement extends AbstractEntityService stockItems = buildStockItems(order.getStoreId(), sourceList); applySkuSpecSnapshots(stockItems, fulfillmentItemList); TimiException.requiredTrue(!stockItems.isEmpty(), "履约单没有需要占用的实物库存"); - TimiException.requiredTrue(productStockService.hasSufficientForFulfillment(order.getStoreId(), stockItems), "库存仍然不足,不能准备履约"); + String stockShortageMessage = productStockService.getFulfillmentStockShortageMessage(order.getStoreId(), stockItems); + TimiException.requiredTrue(stockShortageMessage == null, stockShortageMessage); String operatorUserId = userLoginService.getRequireLoginUserId(); productStockService.reserveForFulfillment(order.getStoreId(), fulfillment.getId(), stockItems, stockRequestNo(fulfillment, "RESERVE"), operatorUserId); diff --git a/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoOrderStraightThroughServiceImplement.java b/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoOrderStraightThroughServiceImplement.java new file mode 100644 index 0000000..2e53cc5 --- /dev/null +++ b/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoOrderStraightThroughServiceImplement.java @@ -0,0 +1,129 @@ +package com.imyeyu.api.modules.gao.service.implement; + +import com.imyeyu.api.bean.ModuleCode; +import com.imyeyu.api.config.dbsource.TimiServerDBConfig; +import com.imyeyu.api.modules.gao.bean.GaoPermissionCode; +import com.imyeyu.api.modules.gao.entity.GaoFundRecord; +import com.imyeyu.api.modules.gao.entity.GaoOrderFulfillment; +import com.imyeyu.api.modules.gao.entity.GaoSalesOrder; +import com.imyeyu.api.modules.gao.entity.GaoSalesOrderItem; +import com.imyeyu.api.modules.gao.service.GaoOrderFulfillmentService; +import com.imyeyu.api.modules.gao.service.GaoOrderPaymentService; +import com.imyeyu.api.modules.gao.service.GaoOrderStraightThroughService; +import com.imyeyu.api.modules.gao.vo.GaoOrderFulfillmentCreateRequest; +import com.imyeyu.api.modules.gao.vo.GaoOrderFulfillmentActionRequest; +import com.imyeyu.api.modules.gao.vo.GaoOrderFulfillmentItemRequest; +import com.imyeyu.api.modules.gao.vo.GaoOrderPaymentReceiveRequest; +import com.imyeyu.api.modules.gao.vo.GaoOrderStraightThroughRequest; +import com.imyeyu.api.modules.user.service.PermissionChecker; +import com.imyeyu.java.TimiJava; +import com.imyeyu.java.bean.timi.TimiException; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; + +/// GAO 订单创建直通服务实现 +/// +/// @author Codex +/// @since 2026-09-13 +@Service +@RequiredArgsConstructor +public class GaoOrderStraightThroughServiceImplement implements GaoOrderStraightThroughService { + + private final GaoOrderPaymentService paymentService; + private final GaoOrderFulfillmentService fulfillmentService; + private final PermissionChecker permissionChecker; + + @Override + @Transactional(TimiServerDBConfig.ROLLBACKER) + public void complete(GaoSalesOrder order, GaoOrderStraightThroughRequest straightThrough) { + if (straightThrough == null) { + return; + } + boolean receiveFullPayment = Boolean.TRUE.equals(straightThrough.getReceiveFullPayment()); + boolean fulfillAll = Boolean.TRUE.equals(straightThrough.getFulfillAll()); + if (!receiveFullPayment && !fulfillAll) { + return; + } + TimiException.required(order, "not found sales order"); + TimiException.required(order.getId(), "not found salesOrderId"); + TimiException.requiredTrue(!fulfillAll || receiveFullPayment || order.getSaleAmount() == 0, + "全部履约直通必须同时直接全额收款"); + checkPermissions(order.getOrderType(), receiveFullPayment, fulfillAll); + if (receiveFullPayment) { + settle(order, straightThrough); + } + if (fulfillAll) { + fulfill(order, straightThrough); + } + } + + private void checkPermissions(GaoSalesOrder.OrderType orderType, boolean receiveFullPayment, boolean fulfillAll) { + List permissionCodeList = new ArrayList<>(); + permissionCodeList.add(orderType == GaoSalesOrder.OrderType.PACKAGE + ? GaoPermissionCode.CUSTOMER_PACKAGE_CREATE.getValue() : GaoPermissionCode.SALES_ORDER_CREATE.getValue()); + if (receiveFullPayment) { + permissionCodeList.add(GaoPermissionCode.FUND_RECEIVE_CREATE.getValue()); + permissionCodeList.add(GaoPermissionCode.FUND_RECEIVE_VERIFY.getValue()); + } + if (fulfillAll) { + permissionCodeList.add(GaoPermissionCode.ORDER_FULFILLMENT_CREATE.getValue()); + permissionCodeList.add(GaoPermissionCode.ORDER_FULFILLMENT_VERIFY.getValue()); + } + permissionChecker.checkAll(ModuleCode.GAO, permissionCodeList.toArray(String[]::new)); + } + + private void settle(GaoSalesOrder order, GaoOrderStraightThroughRequest straightThrough) { + if (order.getSaleAmount() == null || order.getSaleAmount() == 0) { + return; + } + GaoOrderPaymentReceiveRequest paymentRequest = new GaoOrderPaymentReceiveRequest(); + paymentRequest.setSalesOrderId(order.getId()); + paymentRequest.setRequestNo("ORDER-%s-FULL-PAY".formatted(order.getId())); + paymentRequest.setAmount(order.getSaleAmount()); + paymentRequest.setOccurredAt(TimiJava.defaultIfNull(straightThrough.getPaymentOccurredAt(), order.getPurchasedAt())); + paymentRequest.setAttachmentList(straightThrough.getPaymentAttachmentList()); + paymentRequest.setRemark(TimiJava.defaultIfEmpty(straightThrough.getPaymentRemark(), "随订单创建直接全额收款")); + GaoFundRecord record = paymentService.receive(paymentRequest); + if (record.getStatus() == GaoFundRecord.Status.PENDING) { + paymentService.verify(record.getId()); + return; + } + TimiException.requiredTrue(record.getStatus() == GaoFundRecord.Status.VERIFIED, "直通收款记录不是已核销状态"); + } + + private void fulfill(GaoSalesOrder order, GaoOrderStraightThroughRequest straightThrough) { + List orderItemList = order.getItemList(); + TimiException.requiredTrue(orderItemList != null && !orderItemList.isEmpty(), "销售订单没有可履约明细"); + List itemList = orderItemList.stream().map(item -> { + GaoOrderFulfillmentItemRequest result = new GaoOrderFulfillmentItemRequest(); + result.setSourceOrderItemId(item.getId()); + result.setSkuUnitId(item.getSkuUnitId()); + result.setQuantity(item.getQuantity()); + return result; + }).toList(); + GaoOrderFulfillmentCreateRequest fulfillmentRequest = new GaoOrderFulfillmentCreateRequest(); + fulfillmentRequest.setSalesOrderId(order.getId()); + fulfillmentRequest.setRequestNo("ORDER-%s-FULL-FULFILL".formatted(order.getId())); + fulfillmentRequest.setDeliveredAt(TimiJava.defaultIfNull(straightThrough.getDeliveredAt(), order.getPurchasedAt())); + fulfillmentRequest.setItemList(itemList); + fulfillmentRequest.setAttachmentList(straightThrough.getFulfillmentAttachmentList()); + fulfillmentRequest.setRemark(TimiJava.defaultIfEmpty(straightThrough.getFulfillmentRemark(), "随订单创建直接全部履约")); + GaoOrderFulfillment fulfillment = fulfillmentService.create(fulfillmentRequest); + if (fulfillment.getStatus() == GaoOrderFulfillment.Status.PENDING) { + fulfillmentService.verify(actionRequest(fulfillment.getId())); + return; + } + TimiException.requiredTrue(fulfillment.getStatus() == GaoOrderFulfillment.Status.VERIFIED, + fulfillment.getStatus() == GaoOrderFulfillment.Status.WAITING_STOCK ? "库存不足,不能全部履约直通" : "直通履约单不是已核销状态"); + } + + private GaoOrderFulfillmentActionRequest actionRequest(String fulfillmentId) { + GaoOrderFulfillmentActionRequest request = new GaoOrderFulfillmentActionRequest(); + request.setFulfillmentId(fulfillmentId); + return request; + } +} diff --git a/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoProductStockServiceImplement.java b/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoProductStockServiceImplement.java index f2d0dae..bc7ef7c 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoProductStockServiceImplement.java +++ b/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoProductStockServiceImplement.java @@ -156,19 +156,30 @@ public class GaoProductStockServiceImplement implements GaoProductStockService { @Override @Transactional(TimiServerDBConfig.ROLLBACKER) public boolean hasSufficientForFulfillment(String storeId, List items) { + return getFulfillmentStockShortageMessage(storeId, items) == null; + } + + @Override + @Transactional(TimiServerDBConfig.ROLLBACKER) + public String getFulfillmentStockShortageMessage(String storeId, List items) { TimiException.required(storeId, "not found storeId"); if (items == null || items.isEmpty()) { - return true; + return null; } List sortedItems = new ArrayList<>(items); sortedItems.sort(Comparator.comparing(GaoStockChangeItem::getSkuId)); + List shortageList = new ArrayList<>(); for (GaoStockChangeItem item : sortedItems) { GaoProductStock stock = stockMapper.selectForUpdate(storeId, item.getSkuId()); - if (stock == null || decimal(stock.getOnHandQuantity()).subtract(decimal(stock.getReservedQuantity())).compareTo(item.getBaseQuantity()) < 0) { - return false; + BigDecimal availableQuantity = stock == null ? ZERO : decimal(stock.getOnHandQuantity()).subtract(decimal(stock.getReservedQuantity())); + BigDecimal shortageQuantity = item.getBaseQuantity().subtract(availableQuantity); + if (0 < shortageQuantity.signum()) { + shortageList.add("%s(%s)可用 %s %s,缺少 %s %s".formatted( + item.getProductNameSnapshot(), item.getSkuNameSnapshot(), availableQuantity.toPlainString(), item.getUnitSnapshot(), + shortageQuantity.toPlainString(), item.getUnitSnapshot())); } } - return true; + return shortageList.isEmpty() ? null : "库存不足:" + String.join(";", shortageList); } @Override diff --git a/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoSalesOrderServiceImplement.java b/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoSalesOrderServiceImplement.java index ed77d79..1c1eba9 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoSalesOrderServiceImplement.java +++ b/src/main/java/com/imyeyu/api/modules/gao/service/implement/GaoSalesOrderServiceImplement.java @@ -21,6 +21,7 @@ import com.imyeyu.api.modules.gao.service.GaoCustomerService; import com.imyeyu.api.modules.gao.service.GaoEventRecordService; import com.imyeyu.api.modules.gao.service.GaoFundRecordService; import com.imyeyu.api.modules.gao.service.GaoOrderFulfillmentService; +import com.imyeyu.api.modules.gao.service.GaoOrderStraightThroughService; import com.imyeyu.api.modules.gao.service.GaoPointTriggerService; import com.imyeyu.api.modules.gao.service.GaoProductService; import com.imyeyu.api.modules.gao.service.GaoProductSkuService; @@ -80,6 +81,7 @@ public class GaoSalesOrderServiceImplement extends AbstractEntityService fulfillmentServiceProvider; + private final ObjectProvider straightThroughServiceProvider; private final GaoCustomerService customerService; private final GaoProductService productService; private final GaoProductSkuService skuService; @@ -143,7 +145,9 @@ public class GaoSalesOrderServiceImplement extends AbstractEntityService attachmentList; + /// 创建订单后的直通处理 + private GaoOrderStraightThroughRequest straightThrough; + /// 备注 private String remark; } diff --git a/src/main/java/com/imyeyu/api/modules/gao/vo/GaoDirectProductOrderCreateRequest.java b/src/main/java/com/imyeyu/api/modules/gao/vo/GaoDirectProductOrderCreateRequest.java index 2fd8703..73017f5 100644 --- a/src/main/java/com/imyeyu/api/modules/gao/vo/GaoDirectProductOrderCreateRequest.java +++ b/src/main/java/com/imyeyu/api/modules/gao/vo/GaoDirectProductOrderCreateRequest.java @@ -30,6 +30,9 @@ public class GaoDirectProductOrderCreateRequest { /// 购买照片 private List attachmentList; + /// 创建订单后的直通处理 + private GaoOrderStraightThroughRequest straightThrough; + /// 备注 private String remark; } diff --git a/src/main/java/com/imyeyu/api/modules/gao/vo/GaoOrderStraightThroughRequest.java b/src/main/java/com/imyeyu/api/modules/gao/vo/GaoOrderStraightThroughRequest.java new file mode 100644 index 0000000..6632167 --- /dev/null +++ b/src/main/java/com/imyeyu/api/modules/gao/vo/GaoOrderStraightThroughRequest.java @@ -0,0 +1,38 @@ +package com.imyeyu.api.modules.gao.vo; + +import com.imyeyu.api.modules.common.entity.Attachment; +import lombok.Data; + +import java.util.List; + +/// GAO 订单创建直通请求 +/// +/// @author Codex +/// @since 2026-09-13 +@Data +public class GaoOrderStraightThroughRequest { + + /// true 为直接全额收款并核销 + private Boolean receiveFullPayment; + + /// 实际收款时间,空为订单下单时间 + private Long paymentOccurredAt; + + /// 收款凭证照片 + private List paymentAttachmentList; + + /// 收款备注 + private String paymentRemark; + + /// true 为直接全部履约并核销 + private Boolean fulfillAll; + + /// 实际履约时间,空为订单下单时间 + private Long deliveredAt; + + /// 履约凭证照片 + private List fulfillmentAttachmentList; + + /// 履约备注 + private String fulfillmentRemark; +} From c35132d06b6e098e38015f0e2832381f956e0695 Mon Sep 17 00:00:00 2001 From: Timi Date: Sun, 13 Sep 2026 01:37:41 +0800 Subject: [PATCH 2/3] total_earned DESC order for point account --- .../resources/mapper/timi-server/gao/GaoPointAccountMapper.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/mapper/timi-server/gao/GaoPointAccountMapper.xml b/src/main/resources/mapper/timi-server/gao/GaoPointAccountMapper.xml index 5451847..4918411 100644 --- a/src/main/resources/mapper/timi-server/gao/GaoPointAccountMapper.xml +++ b/src/main/resources/mapper/timi-server/gao/GaoPointAccountMapper.xml @@ -53,7 +53,7 @@ INNER JOIN `gao_customer` `c` ON `c`.`id` = `a`.`customer_id` AND `c`.`store_id` = `a`.`store_id` WHERE - ORDER BY `a`.`created_at` DESC, `a`.`id` DESC + ORDER BY `a`.`total_earned` DESC, `a`.`created_at` DESC, `a`.`id` DESC LIMIT #{offset}, #{limit} From b24d6b3a0198cb0ef4e4106b5d8566a93f7f889a Mon Sep 17 00:00:00 2001 From: Timi Date: Sun, 13 Sep 2026 01:37:51 +0800 Subject: [PATCH 3/3] v1.0.44 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ee44e77..6d6d03c 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ com.imyeyu.timiserverapi TimiServerAPI - 1.0.43 + 1.0.44 jar TimiServerAPI imyeyu.com API