Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4bdb24e9ac | ||
|
|
398e1b8095 | ||
|
|
086d2b42e2 | ||
|
|
425922fd04 | ||
|
|
b24d6b3a01 | ||
|
|
c35132d06b | ||
|
|
91072d6d50 |
@@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
<groupId>com.imyeyu.timiserverapi</groupId>
|
<groupId>com.imyeyu.timiserverapi</groupId>
|
||||||
<artifactId>TimiServerAPI</artifactId>
|
<artifactId>TimiServerAPI</artifactId>
|
||||||
<version>1.0.43</version>
|
<version>1.0.45</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
<name>TimiServerAPI</name>
|
<name>TimiServerAPI</name>
|
||||||
<description>imyeyu.com API</description>
|
<description>imyeyu.com API</description>
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ public interface GaoOrderPaymentService {
|
|||||||
/// 登记收款
|
/// 登记收款
|
||||||
GaoFundRecord receive(GaoOrderPaymentReceiveRequest request);
|
GaoFundRecord receive(GaoOrderPaymentReceiveRequest request);
|
||||||
|
|
||||||
|
/// 登记已收款并直接核销
|
||||||
|
GaoFundRecord receiveAndVerify(GaoOrderPaymentReceiveRequest request);
|
||||||
|
|
||||||
/// 核销收款
|
/// 核销收款
|
||||||
GaoFundRecord verify(String fundRecordId);
|
GaoFundRecord verify(String fundRecordId);
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -46,6 +46,9 @@ public interface GaoProductStockService {
|
|||||||
/// 为履约预占库存
|
/// 为履约预占库存
|
||||||
boolean hasSufficientForFulfillment(String storeId, List<GaoStockChangeItem> items);
|
boolean hasSufficientForFulfillment(String storeId, List<GaoStockChangeItem> items);
|
||||||
|
|
||||||
|
/// 获取履约库存缺口提示,无缺口时返回 null
|
||||||
|
String getFulfillmentStockShortageMessage(String storeId, List<GaoStockChangeItem> items);
|
||||||
|
|
||||||
/// 为履约预占库存
|
/// 为履约预占库存
|
||||||
List<GaoProductStock> reserveForFulfillment(String storeId, String bizId, List<GaoStockChangeItem> items, String requestNo, String operatorUserId);
|
List<GaoProductStock> reserveForFulfillment(String storeId, String bizId, List<GaoStockChangeItem> items, String requestNo, String operatorUserId);
|
||||||
|
|
||||||
|
|||||||
+5
@@ -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.entity.GaoSalesOrderItem;
|
||||||
import com.imyeyu.api.modules.gao.mapper.GaoCustomerPackageMapper;
|
import com.imyeyu.api.modules.gao.mapper.GaoCustomerPackageMapper;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoSalesOrderService;
|
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.GaoCustomerService;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoCustomerPackageService;
|
import com.imyeyu.api.modules.gao.service.GaoCustomerPackageService;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoPackageConfigService;
|
import com.imyeyu.api.modules.gao.service.GaoPackageConfigService;
|
||||||
@@ -67,6 +68,7 @@ public class GaoCustomerPackageServiceImplement extends AbstractEntityService<Ga
|
|||||||
private final GaoProductSkuService skuService;
|
private final GaoProductSkuService skuService;
|
||||||
private final GaoCustomerService customerService;
|
private final GaoCustomerService customerService;
|
||||||
private final GaoSalesOrderService salesOrderService;
|
private final GaoSalesOrderService salesOrderService;
|
||||||
|
private final GaoOrderStraightThroughService straightThroughService;
|
||||||
private final GaoStoreScopeService storeScopeService;
|
private final GaoStoreScopeService storeScopeService;
|
||||||
private final AttachmentService attachmentService;
|
private final AttachmentService attachmentService;
|
||||||
private final UserLoginService userLoginService;
|
private final UserLoginService userLoginService;
|
||||||
@@ -168,6 +170,7 @@ public class GaoCustomerPackageServiceImplement extends AbstractEntityService<Ga
|
|||||||
if (existingOrder != null) {
|
if (existingOrder != null) {
|
||||||
GaoCustomerPackage existingPackage = getBySalesOrderId(existingOrder.getId());
|
GaoCustomerPackage existingPackage = getBySalesOrderId(existingOrder.getId());
|
||||||
verifyPurchaseIdempotency(existingOrder, existingPackage, request);
|
verifyPurchaseIdempotency(existingOrder, existingPackage, request);
|
||||||
|
straightThroughService.complete(salesOrderService.detail(existingOrder.getId()), request.getStraightThrough());
|
||||||
return existingPackage;
|
return existingPackage;
|
||||||
}
|
}
|
||||||
Long purchasedAt = TimiJava.defaultIfNull(request.getPurchasedAt(), Time.now());
|
Long purchasedAt = TimiJava.defaultIfNull(request.getPurchasedAt(), Time.now());
|
||||||
@@ -205,6 +208,8 @@ public class GaoCustomerPackageServiceImplement extends AbstractEntityService<Ga
|
|||||||
result.setStandardAmountSnapshot(config.getStandardAmount());
|
result.setStandardAmountSnapshot(config.getStandardAmount());
|
||||||
result.setRemark(request.getRemark());
|
result.setRemark(request.getRemark());
|
||||||
super.create(result);
|
super.create(result);
|
||||||
|
straightThroughService.complete(salesOrder, request.getStraightThrough());
|
||||||
|
salesOrder = salesOrderService.detail(salesOrder.getId());
|
||||||
result.setSalesOrder(salesOrder);
|
result.setSalesOrder(salesOrder);
|
||||||
result.setItemList(salesOrder.getItemList());
|
result.setItemList(salesOrder.getItemList());
|
||||||
result.setStatus(salesOrder.getStatus());
|
result.setStatus(salesOrder.getStatus());
|
||||||
|
|||||||
+2
-1
@@ -207,7 +207,8 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
|
|||||||
List<GaoStockChangeItem> stockItems = buildStockItems(order.getStoreId(), sourceList);
|
List<GaoStockChangeItem> stockItems = buildStockItems(order.getStoreId(), sourceList);
|
||||||
applySkuSpecSnapshots(stockItems, fulfillmentItemList);
|
applySkuSpecSnapshots(stockItems, fulfillmentItemList);
|
||||||
TimiException.requiredTrue(!stockItems.isEmpty(), "履约单没有需要占用的实物库存");
|
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();
|
String operatorUserId = userLoginService.getRequireLoginUserId();
|
||||||
productStockService.reserveForFulfillment(order.getStoreId(), fulfillment.getId(), stockItems,
|
productStockService.reserveForFulfillment(order.getStoreId(), fulfillment.getId(), stockItems,
|
||||||
stockRequestNo(fulfillment, "RESERVE"), operatorUserId);
|
stockRequestNo(fulfillment, "RESERVE"), operatorUserId);
|
||||||
|
|||||||
+29
-4
@@ -49,10 +49,17 @@ public class GaoOrderPaymentServiceImplement implements GaoOrderPaymentService {
|
|||||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public GaoFundRecord receive(GaoOrderPaymentReceiveRequest request) {
|
public GaoFundRecord receive(GaoOrderPaymentReceiveRequest request) {
|
||||||
return receiveInternal(request);
|
return receiveInternal(request, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private GaoFundRecord receiveInternal(GaoOrderPaymentReceiveRequest request) {
|
@OperationAudit(AuditOperation.GAO_SALES_ORDER_PAYMENT_RECEIVE)
|
||||||
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
|
@Override
|
||||||
|
public GaoFundRecord receiveAndVerify(GaoOrderPaymentReceiveRequest request) {
|
||||||
|
return receiveInternal(request, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private GaoFundRecord receiveInternal(GaoOrderPaymentReceiveRequest request, boolean verified) {
|
||||||
TimiException.required(request, "not found order payment request");
|
TimiException.required(request, "not found order payment request");
|
||||||
TimiException.required(request.getSalesOrderId(), "not found salesOrderId");
|
TimiException.required(request.getSalesOrderId(), "not found salesOrderId");
|
||||||
TimiException.required(request.getAmount(), "收款金额不能为空");
|
TimiException.required(request.getAmount(), "收款金额不能为空");
|
||||||
@@ -62,7 +69,7 @@ public class GaoOrderPaymentServiceImplement implements GaoOrderPaymentService {
|
|||||||
GaoFundRecord existing = fundRecordService.getByRequestNo(order.getStoreId(), requestNo);
|
GaoFundRecord existing = fundRecordService.getByRequestNo(order.getStoreId(), requestNo);
|
||||||
if (existing != null) {
|
if (existing != null) {
|
||||||
verifySameReceive(existing, order, request);
|
verifySameReceive(existing, order, request);
|
||||||
return existing;
|
return verified && existing.getStatus() == GaoFundRecord.Status.PENDING ? verify(existing.getId()) : existing;
|
||||||
}
|
}
|
||||||
TimiException.requiredTrue(order.getStatus() != GaoSalesOrder.Status.CANCELLED, "已取消订单不能收款");
|
TimiException.requiredTrue(order.getStatus() != GaoSalesOrder.Status.CANCELLED, "已取消订单不能收款");
|
||||||
TimiException.requiredTrue(order.getRefundStatus() != GaoSalesOrder.RefundStatus.REFUNDED, "已完成退款订单不能收款");
|
TimiException.requiredTrue(order.getRefundStatus() != GaoSalesOrder.RefundStatus.REFUNDED, "已完成退款订单不能收款");
|
||||||
@@ -71,9 +78,27 @@ public class GaoOrderPaymentServiceImplement implements GaoOrderPaymentService {
|
|||||||
TimiException.requiredTrue(pending + paid + request.getAmount() <= safe(order.getSaleAmount()), "累计收款不能超过成交价");
|
TimiException.requiredTrue(pending + paid + request.getAmount() <= safe(order.getSaleAmount()), "累计收款不能超过成交价");
|
||||||
TimiException.requiredTrue(hasPhoto(request.getAttachmentList()), "收款必须上传照片");
|
TimiException.requiredTrue(hasPhoto(request.getAttachmentList()), "收款必须上传照片");
|
||||||
GaoFundRecord record = newRecord(order, GaoFundRecord.OperationType.RECEIVE, requestNo, request.getAmount(), request.getOccurredAt(), request.getRemark());
|
GaoFundRecord record = newRecord(order, GaoFundRecord.OperationType.RECEIVE, requestNo, request.getAmount(), request.getOccurredAt(), request.getRemark());
|
||||||
record.setStatus(GaoFundRecord.Status.PENDING);
|
record.setStatus(verified ? GaoFundRecord.Status.VERIFIED : GaoFundRecord.Status.PENDING);
|
||||||
|
String verifierUserId = verified ? userLoginService.getRequireLoginUserId() : null;
|
||||||
|
Long verifiedAt = verified ? Time.now() : null;
|
||||||
|
if (verified) {
|
||||||
|
record.setVerifierUserId(verifierUserId);
|
||||||
|
record.setVerifiedAt(verifiedAt);
|
||||||
|
}
|
||||||
fundRecordService.createByOrderPayment(record);
|
fundRecordService.createByOrderPayment(record);
|
||||||
|
if (!verified) {
|
||||||
order.setPendingPaidAmount(pending + request.getAmount());
|
order.setPendingPaidAmount(pending + request.getAmount());
|
||||||
|
} else {
|
||||||
|
Long paidAtBefore = order.getPaidAt();
|
||||||
|
order.setPaidAmount(paid + request.getAmount());
|
||||||
|
orderService.recomputePaymentStatus(order);
|
||||||
|
orderService.updatePayment(order);
|
||||||
|
if (paidAtBefore == null && order.getPaidAt() != null) {
|
||||||
|
pointTriggerService.onSalesOrderPaid(order);
|
||||||
|
}
|
||||||
|
attachmentService.updateByBizId(Attachment.BizType.GAO_FUND_RECORD, record.getId(), request.getAttachmentList());
|
||||||
|
return record;
|
||||||
|
}
|
||||||
orderService.recomputePaymentStatus(order);
|
orderService.recomputePaymentStatus(order);
|
||||||
orderService.updatePayment(order);
|
orderService.updatePayment(order);
|
||||||
attachmentService.updateByBizId(Attachment.BizType.GAO_FUND_RECORD, record.getId(), request.getAttachmentList());
|
attachmentService.updateByBizId(Attachment.BizType.GAO_FUND_RECORD, record.getId(), request.getAttachmentList());
|
||||||
|
|||||||
+155
@@ -0,0 +1,155 @@
|
|||||||
|
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 paymentVerified = Boolean.TRUE.equals(straightThrough.getPaymentVerified());
|
||||||
|
boolean receiveFullPayment = Boolean.TRUE.equals(straightThrough.getReceiveFullPayment());
|
||||||
|
boolean fulfillAll = Boolean.TRUE.equals(straightThrough.getFulfillAll());
|
||||||
|
Long paymentAmount = paymentVerified ? straightThrough.getPaymentAmount() : order.getSaleAmount();
|
||||||
|
if (!paymentVerified && !receiveFullPayment && !fulfillAll) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
TimiException.required(order, "not found sales order");
|
||||||
|
TimiException.required(order.getId(), "not found salesOrderId");
|
||||||
|
TimiException.requiredTrue(!(paymentVerified && receiveFullPayment), "已核销收款和已全额收款和核销不能同时启用");
|
||||||
|
if (paymentVerified) {
|
||||||
|
TimiException.required(paymentAmount, "已核销收款金额不能为空");
|
||||||
|
TimiException.requiredTrue(0 < paymentAmount && paymentAmount <= safe(order.getSaleAmount()), "已核销收款金额不能超过订单金额");
|
||||||
|
}
|
||||||
|
TimiException.requiredTrue(!fulfillAll || receiveFullPayment || order.getSaleAmount() == 0
|
||||||
|
|| (paymentVerified && paymentAmount.equals(order.getSaleAmount())),
|
||||||
|
"全部履约直通必须同时完成收款核销");
|
||||||
|
checkPermissions(order.getOrderType(), paymentVerified || receiveFullPayment, fulfillAll);
|
||||||
|
if (receiveFullPayment) {
|
||||||
|
settle(order, straightThrough);
|
||||||
|
} else if (paymentVerified) {
|
||||||
|
verifyReceivedPayment(order, straightThrough, paymentAmount);
|
||||||
|
}
|
||||||
|
if (fulfillAll) {
|
||||||
|
fulfill(order, straightThrough);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkPermissions(GaoSalesOrder.OrderType orderType, boolean paymentCompleted, boolean fulfillAll) {
|
||||||
|
List<String> permissionCodeList = new ArrayList<>();
|
||||||
|
permissionCodeList.add(orderType == GaoSalesOrder.OrderType.PACKAGE
|
||||||
|
? GaoPermissionCode.CUSTOMER_PACKAGE_CREATE.getValue() : GaoPermissionCode.SALES_ORDER_CREATE.getValue());
|
||||||
|
if (paymentCompleted) {
|
||||||
|
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 verifyReceivedPayment(GaoSalesOrder order, GaoOrderStraightThroughRequest straightThrough, Long paymentAmount) {
|
||||||
|
GaoOrderPaymentReceiveRequest paymentRequest = new GaoOrderPaymentReceiveRequest();
|
||||||
|
paymentRequest.setSalesOrderId(order.getId());
|
||||||
|
paymentRequest.setRequestNo("ORDER-%s-VERIFIED-PAY".formatted(order.getId()));
|
||||||
|
paymentRequest.setAmount(paymentAmount);
|
||||||
|
paymentRequest.setOccurredAt(TimiJava.defaultIfNull(straightThrough.getPaymentOccurredAt(), order.getPurchasedAt()));
|
||||||
|
paymentRequest.setAttachmentList(straightThrough.getPaymentAttachmentList());
|
||||||
|
paymentRequest.setRemark(TimiJava.defaultIfEmpty(straightThrough.getPaymentRemark(), "随订单创建直接核销已有收款"));
|
||||||
|
GaoFundRecord record = paymentService.receiveAndVerify(paymentRequest);
|
||||||
|
TimiException.requiredTrue(record.getStatus() == GaoFundRecord.Status.VERIFIED, "直通收款记录不是已核销状态");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fulfill(GaoSalesOrder order, GaoOrderStraightThroughRequest straightThrough) {
|
||||||
|
List<GaoSalesOrderItem> orderItemList = order.getItemList();
|
||||||
|
TimiException.requiredTrue(orderItemList != null && !orderItemList.isEmpty(), "销售订单没有可履约明细");
|
||||||
|
List<GaoOrderFulfillmentItemRequest> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
private long safe(Long value) {
|
||||||
|
return value == null ? 0L : value;
|
||||||
|
}
|
||||||
|
}
|
||||||
+15
-4
@@ -156,19 +156,30 @@ public class GaoProductStockServiceImplement implements GaoProductStockService {
|
|||||||
@Override
|
@Override
|
||||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
public boolean hasSufficientForFulfillment(String storeId, List<GaoStockChangeItem> items) {
|
public boolean hasSufficientForFulfillment(String storeId, List<GaoStockChangeItem> items) {
|
||||||
|
return getFulfillmentStockShortageMessage(storeId, items) == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
|
public String getFulfillmentStockShortageMessage(String storeId, List<GaoStockChangeItem> items) {
|
||||||
TimiException.required(storeId, "not found storeId");
|
TimiException.required(storeId, "not found storeId");
|
||||||
if (items == null || items.isEmpty()) {
|
if (items == null || items.isEmpty()) {
|
||||||
return true;
|
return null;
|
||||||
}
|
}
|
||||||
List<GaoStockChangeItem> sortedItems = new ArrayList<>(items);
|
List<GaoStockChangeItem> sortedItems = new ArrayList<>(items);
|
||||||
sortedItems.sort(Comparator.comparing(GaoStockChangeItem::getSkuId));
|
sortedItems.sort(Comparator.comparing(GaoStockChangeItem::getSkuId));
|
||||||
|
List<String> shortageList = new ArrayList<>();
|
||||||
for (GaoStockChangeItem item : sortedItems) {
|
for (GaoStockChangeItem item : sortedItems) {
|
||||||
GaoProductStock stock = stockMapper.selectForUpdate(storeId, item.getSkuId());
|
GaoProductStock stock = stockMapper.selectForUpdate(storeId, item.getSkuId());
|
||||||
if (stock == null || decimal(stock.getOnHandQuantity()).subtract(decimal(stock.getReservedQuantity())).compareTo(item.getBaseQuantity()) < 0) {
|
BigDecimal availableQuantity = stock == null ? ZERO : decimal(stock.getOnHandQuantity()).subtract(decimal(stock.getReservedQuantity()));
|
||||||
return false;
|
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
|
@Override
|
||||||
|
|||||||
+5
-1
@@ -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.GaoEventRecordService;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoFundRecordService;
|
import com.imyeyu.api.modules.gao.service.GaoFundRecordService;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoOrderFulfillmentService;
|
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.GaoPointTriggerService;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoProductService;
|
import com.imyeyu.api.modules.gao.service.GaoProductService;
|
||||||
import com.imyeyu.api.modules.gao.service.GaoProductSkuService;
|
import com.imyeyu.api.modules.gao.service.GaoProductSkuService;
|
||||||
@@ -80,6 +81,7 @@ public class GaoSalesOrderServiceImplement extends AbstractEntityService<GaoSale
|
|||||||
private final GaoSalesOrderItemMapper itemMapper;
|
private final GaoSalesOrderItemMapper itemMapper;
|
||||||
private final GaoFundRecordService fundRecordService;
|
private final GaoFundRecordService fundRecordService;
|
||||||
private final ObjectProvider<GaoOrderFulfillmentService> fulfillmentServiceProvider;
|
private final ObjectProvider<GaoOrderFulfillmentService> fulfillmentServiceProvider;
|
||||||
|
private final ObjectProvider<GaoOrderStraightThroughService> straightThroughServiceProvider;
|
||||||
private final GaoCustomerService customerService;
|
private final GaoCustomerService customerService;
|
||||||
private final GaoProductService productService;
|
private final GaoProductService productService;
|
||||||
private final GaoProductSkuService skuService;
|
private final GaoProductSkuService skuService;
|
||||||
@@ -143,7 +145,9 @@ public class GaoSalesOrderServiceImplement extends AbstractEntityService<GaoSale
|
|||||||
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
@Transactional(TimiServerDBConfig.ROLLBACKER)
|
||||||
@Override
|
@Override
|
||||||
public GaoSalesOrder createDirectProductOrder(GaoDirectProductOrderCreateRequest request) {
|
public GaoSalesOrder createDirectProductOrder(GaoDirectProductOrderCreateRequest request) {
|
||||||
return createDirectInternal(request);
|
GaoSalesOrder order = createDirectInternal(request);
|
||||||
|
straightThroughServiceProvider.getObject().complete(order, request.getStraightThrough());
|
||||||
|
return detail(order.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@OperationAudit(AuditOperation.GAO_SALES_ORDER_UPDATE)
|
@OperationAudit(AuditOperation.GAO_SALES_ORDER_UPDATE)
|
||||||
|
|||||||
@@ -33,6 +33,9 @@ public class GaoCustomerPackagePurchaseRequest {
|
|||||||
/// 购买照片
|
/// 购买照片
|
||||||
private List<Attachment> attachmentList;
|
private List<Attachment> attachmentList;
|
||||||
|
|
||||||
|
/// 创建订单后的直通处理
|
||||||
|
private GaoOrderStraightThroughRequest straightThrough;
|
||||||
|
|
||||||
/// 备注
|
/// 备注
|
||||||
private String remark;
|
private String remark;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,9 @@ public class GaoDirectProductOrderCreateRequest {
|
|||||||
/// 购买照片
|
/// 购买照片
|
||||||
private List<Attachment> attachmentList;
|
private List<Attachment> attachmentList;
|
||||||
|
|
||||||
|
/// 创建订单后的直通处理
|
||||||
|
private GaoOrderStraightThroughRequest straightThrough;
|
||||||
|
|
||||||
/// 备注
|
/// 备注
|
||||||
private String remark;
|
private String remark;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
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 paymentVerified;
|
||||||
|
|
||||||
|
/// 已核销收款金额,单位分
|
||||||
|
private Long paymentAmount;
|
||||||
|
|
||||||
|
/// true 为直接全额收款并核销
|
||||||
|
private Boolean receiveFullPayment;
|
||||||
|
|
||||||
|
/// 实际收款时间,空为订单下单时间
|
||||||
|
private Long paymentOccurredAt;
|
||||||
|
|
||||||
|
/// 收款凭证照片
|
||||||
|
private List<Attachment> paymentAttachmentList;
|
||||||
|
|
||||||
|
/// 收款备注
|
||||||
|
private String paymentRemark;
|
||||||
|
|
||||||
|
/// true 为直接全部履约并核销
|
||||||
|
private Boolean fulfillAll;
|
||||||
|
|
||||||
|
/// 实际履约时间,空为订单下单时间
|
||||||
|
private Long deliveredAt;
|
||||||
|
|
||||||
|
/// 履约凭证照片
|
||||||
|
private List<Attachment> fulfillmentAttachmentList;
|
||||||
|
|
||||||
|
/// 履约备注
|
||||||
|
private String fulfillmentRemark;
|
||||||
|
}
|
||||||
@@ -53,7 +53,7 @@
|
|||||||
INNER JOIN `gao_customer` `c` ON `c`.`id` = `a`.`customer_id` AND `c`.`store_id` = `a`.`store_id`
|
INNER JOIN `gao_customer` `c` ON `c`.`id` = `a`.`customer_id` AND `c`.`store_id` = `a`.`store_id`
|
||||||
WHERE
|
WHERE
|
||||||
<include refid="queryWhere"/>
|
<include refid="queryWhere"/>
|
||||||
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}
|
LIMIT #{offset}, #{limit}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user