From d613a418542dd21586df94e96c64f5690d1e9bd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=8C=E5=85=88=E8=88=9C?= <2499071264@qq.com> Date: Wed, 29 Jul 2026 15:51:26 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=A7=AF=E5=88=86=E5=95=86=E5=9F=8E-?= =?UTF-8?q?=E5=9F=BA=E6=9C=AC=E5=AE=9E=E7=8E=B0=E6=8B=86=E5=8D=95=E3=80=81?= =?UTF-8?q?=E7=A1=AE=E8=AE=A4=E6=94=B6=E8=B4=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../h5/order/PointsMallH5OrderController.java | 7 + .../PointsMallBridgeController.java | 16 + .../event/MallOrderEventHandler.java | 20 + .../MallAbolishSubOrderEventHandler.java | 29 + .../MallCancelPayedMainOrderEventHandler.java | 29 + .../MallCancelPendingOrderEventHandler.java | 29 + .../MallCancelSubOrderEventHandler.java | 29 + .../MallConfirmReceiptEventHandler.java | 29 + .../handlers/MallExchangeSubEventHandler.java | 29 + .../handlers/MallInvoiceInfoEventHandler.java | 29 + .../MallSplitSubOrderEventHandler.java | 29 + .../MallSubOrderReturnEventHandler.java | 29 + .../MallInboundConfirmPurchaseService.java | 85 +++ .../service/MallInboundEventService.java | 313 +++++++++ .../MallInboundPendingOrderService.java | 42 ++ .../MallInboundPointsQueryService.java | 27 + .../MallInboundPurchaseResultService.java | 69 ++ .../MallInvoiceApplyOutboundService.java | 99 +++ .../service/MallInvoiceInfoQueryService.java | 53 ++ .../MallOrderQueryOutboundService.java | 90 +++ .../service/MallPayFinishNoticeService.java | 59 ++ .../MallPictureApplyOutboundService.java | 85 +++ .../mallbridge/service/MallTokenService.java | 96 +++ .../MallUnReconciledMsgNoticeService.java | 53 ++ .../impl/PointsMallBridgeServiceImpl.java | 663 ++---------------- .../support/AbstractMallBridgeSupport.java | 241 +++++++ .../mallbridge/util/MallBridgeCryptoUtil.java | 31 + .../message/models/PointsMallMessage.java | 2 +- .../service/PointsMallMessageService.java | 1 - .../impl/PointsMallMessageServiceImpl.java | 117 +++- .../PointsMallMobileOrderReceivedParam.java | 30 + .../order/service/PointsMallOrderService.java | 3 + .../impl/PointsMallOrderServiceImpl.java | 41 +- .../enums/PointsMallPointLockStatusEnum.java | 20 + .../models/PointsMallMainPointsLock.java | 62 ++ .../models/PointsMallSubPointsLock.java | 66 ++ .../service/PointsMallPointsLockService.java | 22 + .../impl/PointsMallPointsLockServiceImpl.java | 213 ++++++ .../impl/PointsMallUserPointsServiceImpl.java | 20 +- src/main/resources/application-dev.yaml | 8 + .../zhgh/points-mall/order/index.html | 6 +- 41 files changed, 2262 insertions(+), 659 deletions(-) create mode 100644 src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/MallOrderEventHandler.java create mode 100644 src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallAbolishSubOrderEventHandler.java create mode 100644 src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallCancelPayedMainOrderEventHandler.java create mode 100644 src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallCancelPendingOrderEventHandler.java create mode 100644 src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallCancelSubOrderEventHandler.java create mode 100644 src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallConfirmReceiptEventHandler.java create mode 100644 src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallExchangeSubEventHandler.java create mode 100644 src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallInvoiceInfoEventHandler.java create mode 100644 src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallSplitSubOrderEventHandler.java create mode 100644 src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallSubOrderReturnEventHandler.java create mode 100644 src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInboundConfirmPurchaseService.java create mode 100644 src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInboundEventService.java create mode 100644 src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInboundPendingOrderService.java create mode 100644 src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInboundPointsQueryService.java create mode 100644 src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInboundPurchaseResultService.java create mode 100644 src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInvoiceApplyOutboundService.java create mode 100644 src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInvoiceInfoQueryService.java create mode 100644 src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallOrderQueryOutboundService.java create mode 100644 src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallPayFinishNoticeService.java create mode 100644 src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallPictureApplyOutboundService.java create mode 100644 src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallTokenService.java create mode 100644 src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallUnReconciledMsgNoticeService.java create mode 100644 src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/support/AbstractMallBridgeSupport.java create mode 100644 src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/util/MallBridgeCryptoUtil.java create mode 100644 src/main/java/com/budwk/app/zhgh/pointsmall/order/param/PointsMallMobileOrderReceivedParam.java create mode 100644 src/main/java/com/budwk/app/zhgh/pointsmall/points/enums/PointsMallPointLockStatusEnum.java create mode 100644 src/main/java/com/budwk/app/zhgh/pointsmall/points/models/PointsMallMainPointsLock.java create mode 100644 src/main/java/com/budwk/app/zhgh/pointsmall/points/models/PointsMallSubPointsLock.java create mode 100644 src/main/java/com/budwk/app/zhgh/pointsmall/points/service/PointsMallPointsLockService.java create mode 100644 src/main/java/com/budwk/app/zhgh/pointsmall/points/service/impl/PointsMallPointsLockServiceImpl.java diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/h5/order/PointsMallH5OrderController.java b/src/main/java/com/budwk/app/zhgh/pointsmall/h5/order/PointsMallH5OrderController.java index b73e3676..cd260f7c 100644 --- a/src/main/java/com/budwk/app/zhgh/pointsmall/h5/order/PointsMallH5OrderController.java +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/h5/order/PointsMallH5OrderController.java @@ -4,6 +4,7 @@ import cn.dev33.satoken.annotation.SaCheckPermission; import cn.dev33.satoken.stp.StpUtil; import com.budwk.app.base.result.Result; import com.budwk.app.zhgh.pointsmall.order.param.PointsMallMobileOrderPageParam; +import com.budwk.app.zhgh.pointsmall.order.param.PointsMallMobileOrderReceivedParam; import com.budwk.app.zhgh.pointsmall.order.service.PointsMallOrderService; import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; @@ -43,6 +44,12 @@ public class PointsMallH5OrderController { return Result.success(pointsMallOrderService.supplierOrderUrl(supplierId, orderId, orderType)); } + @At("/userOrderReceive") + @SaCheckPermission("h5.points.mall") + public Result userOrderReceive(PointsMallMobileOrderReceivedParam param) { + return Result.success(pointsMallOrderService.userOrderReceive(param, currentUserId())); + } + private String currentUserId() { try { return StpUtil.getLoginIdAsString(); diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/controller/PointsMallBridgeController.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/controller/PointsMallBridgeController.java index 76e1c289..c669eaf9 100644 --- a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/controller/PointsMallBridgeController.java +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/controller/PointsMallBridgeController.java @@ -9,6 +9,8 @@ import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundResult; import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallOutboundResult; import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallPictureOutboundResult; import com.budwk.app.zhgh.pointsmall.mallbridge.service.PointsMallBridgeService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; import org.nutz.mvc.adaptor.JsonAdaptor; @@ -24,6 +26,7 @@ import org.nutz.mvc.annotation.POST; @Ok("json:full") @At("/open") @AdaptBy(type = JsonAdaptor.class) +@Api(tags = "积分商城桥接接口", description = "供应商 token、订单、积分、发票、对账等桥接接口") public class PointsMallBridgeController { @Inject @@ -31,6 +34,7 @@ public class PointsMallBridgeController { @POST @At("/connect/token") + @ApiOperation(value = "获取 token", notes = "供应商获取访问令牌") public MallInboundResult getToken(@Param("..") MallTokenDTO dto) { try { return pointsMallBridgeService.getToken(dto); @@ -41,6 +45,7 @@ public class PointsMallBridgeController { @POST @At("/connect/refreshToken") + @ApiOperation(value = "刷新 token", notes = "供应商刷新访问令牌") public MallInboundResult refreshToken(@Param("..") MallTokenRefreshDTO dto) { try { return pointsMallBridgeService.refreshToken(dto); @@ -53,6 +58,7 @@ public class PointsMallBridgeController { @At("/pointOrder/queryPoints") @MallTokenRequired @Filters(@By(type = MallTokenInterceptor.class)) + @ApiOperation(value = "积分查询", notes = "查询用户可用积分") public MallInboundPointsResult queryPoints(@Param("..") MallPointQueryDTO dto) { try { return pointsMallBridgeService.queryPoints(dto); @@ -65,6 +71,7 @@ public class PointsMallBridgeController { @At("/pointOrder/pendingOrderInfo") @MallTokenRequired @Filters(@By(type = MallTokenInterceptor.class)) + @ApiOperation(value = "接收待支付订单", notes = "供应商推送待支付主单") public MallInboundResult receivePending(@Param("..") MallPendingOrderDTO dto) { return inbound(() -> pointsMallBridgeService.receivePending(dto)); } @@ -73,6 +80,7 @@ public class PointsMallBridgeController { @At("/pointOrder/confirmPurchase") @MallTokenRequired @Filters(@By(type = MallTokenInterceptor.class)) + @ApiOperation(value = "确认购买", notes = "支付完成后确认主单已购买") public MallInboundResult confirmPurchase(@Param("..") MallPurchaseConfirmDTO dto) { return inbound(() -> pointsMallBridgeService.confirmPurchase(dto)); } @@ -81,6 +89,7 @@ public class PointsMallBridgeController { @At("/pointOrder/purchaseResult") @MallTokenRequired @Filters(@By(type = MallTokenInterceptor.class)) + @ApiOperation(value = "支付结果通知", notes = "接收供应商支付结果") public MallInboundResult purchaseResult(@Param("..") MallPurchaseResultDTO dto) { return inbound(() -> pointsMallBridgeService.purchaseResult(dto)); } @@ -89,12 +98,14 @@ public class PointsMallBridgeController { @At("/pointOrder/pushOrderInfoMsg") @MallTokenRequired @Filters(@By(type = MallTokenInterceptor.class)) + @ApiOperation(value = "订单事件推送", notes = "接收取消、拆单、退换货、完成、发票等订单事件") public MallInboundResult receiveEvent(@Param("..") MallOrderEventDTO dto) { return inbound(() -> pointsMallBridgeService.receiveEvent(dto)); } @POST @At("/pointOrder/queryOrderInfo") + @ApiOperation(value = "查询订单详情", notes = "按主单或子单查询订单信息") public MallOutboundResult queryOrderInfo(@Param("..") MallOrderQueryDTO dto) { try { return pointsMallBridgeService.queryOrderInfo(dto); @@ -105,6 +116,7 @@ public class PointsMallBridgeController { @POST @At("/pointOrder/applyForInvoices") + @ApiOperation(value = "发票开具申请", notes = "接收供应商开票申请") public MallOutboundResult applyInvoice(@Param("..") MallInvoiceApplyDTO dto) { try { return pointsMallBridgeService.applyInvoice(dto); @@ -115,6 +127,7 @@ public class PointsMallBridgeController { @POST @At("/pointOrder/queryInvoicesInfo") + @ApiOperation(value = "查询发票信息", notes = "查询开票结果和发票明细") public Result queryInvoicesInfo(@Param("..") QueryInvoiceInfoDTO dto) { try { return Result.success(pointsMallBridgeService.queryInvoicesInfo(dto)); @@ -125,6 +138,7 @@ public class PointsMallBridgeController { @POST @At("/pointOrder/queryFileBase64Info") + @ApiOperation(value = "查询图片 Base64", notes = "查询订单商品图片或发票图片") public MallPictureOutboundResult queryFileBase64Info(@Param("..") PictureQueryDTO dto) { try { return pointsMallBridgeService.queryFileBase64Info(dto); @@ -135,12 +149,14 @@ public class PointsMallBridgeController { @POST @At("/pointOrder/unReconciledMsg") + @ApiOperation(value = "对账异常通知", notes = "接收未对平订单通知") public MallInboundResult unReconciledMsg(@Param("..") UnReconciledMsgNoticeDTO dto) { return inbound(() -> pointsMallBridgeService.unReconciledMsg(dto)); } @POST @At("/pointOrder/payCompletion") + @ApiOperation(value = "订单支付完成通知", notes = "接收结算支付完成通知") public MallInboundResult payCompletion(@Param("..") OrderPayFinishNoticeDTO dto) { return inbound(() -> pointsMallBridgeService.payCompletion(dto)); } diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/MallOrderEventHandler.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/MallOrderEventHandler.java new file mode 100644 index 00000000..5014a5ae --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/MallOrderEventHandler.java @@ -0,0 +1,20 @@ +package com.budwk.app.zhgh.pointsmall.mallbridge.event; + +import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallOrderEventDTO; +import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundResult; + +/** + * 订单事件处理器策略接口。 + * 不同 orderType 的事件由不同实现处理,便于维护扩展。 + */ +public interface MallOrderEventHandler { + /** + * 返回所支持的事件类型(orderType)。 + */ + int getType(); + + /** + * 处理事件,返回统一结果。 + */ + MallInboundResult handle(MallOrderEventDTO dto); +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallAbolishSubOrderEventHandler.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallAbolishSubOrderEventHandler.java new file mode 100644 index 00000000..10a88205 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallAbolishSubOrderEventHandler.java @@ -0,0 +1,29 @@ +package com.budwk.app.zhgh.pointsmall.mallbridge.event.handlers; + +import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallOrderEventDTO; +import com.budwk.app.zhgh.pointsmall.mallbridge.event.MallOrderEventHandler; +import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundResult; +import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallInboundEventService; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; + +/** + * MallAbolishSubOrderEventHandler + * orderType=8: 废除子订单事件处理器。 + */ +@IocBean +public class MallAbolishSubOrderEventHandler implements MallOrderEventHandler { + + @Inject + private MallInboundEventService inboundEventService; + + @Override + public int getType() { + return 8; + } + + @Override + public MallInboundResult handle(MallOrderEventDTO dto) { + return inboundEventService.updateSubOrderStatus(dto, MallInboundEventService.ORDER_ABOLISH); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallCancelPayedMainOrderEventHandler.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallCancelPayedMainOrderEventHandler.java new file mode 100644 index 00000000..3bd3653a --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallCancelPayedMainOrderEventHandler.java @@ -0,0 +1,29 @@ +package com.budwk.app.zhgh.pointsmall.mallbridge.event.handlers; + +import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallOrderEventDTO; +import com.budwk.app.zhgh.pointsmall.mallbridge.event.MallOrderEventHandler; +import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundResult; +import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallInboundEventService; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; + +/** + * MallCancelPayedMainOrderEventHandler + * orderType=7: 已支付未拆单的整个父单取消。 + */ +@IocBean +public class MallCancelPayedMainOrderEventHandler implements MallOrderEventHandler { + + @Inject + private MallInboundEventService inboundEventService; + + @Override + public int getType() { + return 7; + } + + @Override + public MallInboundResult handle(MallOrderEventDTO dto) { + return inboundEventService.cancelMainOrder(dto, true); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallCancelPendingOrderEventHandler.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallCancelPendingOrderEventHandler.java new file mode 100644 index 00000000..8d38ae87 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallCancelPendingOrderEventHandler.java @@ -0,0 +1,29 @@ +package com.budwk.app.zhgh.pointsmall.mallbridge.event.handlers; + +import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallOrderEventDTO; +import com.budwk.app.zhgh.pointsmall.mallbridge.event.MallOrderEventHandler; +import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundResult; +import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallInboundEventService; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; + +/** + * MallCancelPendingOrderEventHandler + * orderType=1: 取消待支付主单事件处理器。 + */ +@IocBean +public class MallCancelPendingOrderEventHandler implements MallOrderEventHandler { + + @Inject + private MallInboundEventService inboundEventService; + + @Override + public int getType() { + return 1; + } + + @Override + public MallInboundResult handle(MallOrderEventDTO dto) { + return inboundEventService.cancelMainOrder(dto, false); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallCancelSubOrderEventHandler.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallCancelSubOrderEventHandler.java new file mode 100644 index 00000000..29d5e13a --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallCancelSubOrderEventHandler.java @@ -0,0 +1,29 @@ +package com.budwk.app.zhgh.pointsmall.mallbridge.event.handlers; + +import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallOrderEventDTO; +import com.budwk.app.zhgh.pointsmall.mallbridge.event.MallOrderEventHandler; +import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundResult; +import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallInboundEventService; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; + +/** + * MallCancelSubOrderEventHandler + * orderType=3: 取消子订单事件处理器。 + */ +@IocBean +public class MallCancelSubOrderEventHandler implements MallOrderEventHandler { + + @Inject + private MallInboundEventService inboundEventService; + + @Override + public int getType() { + return 3; + } + + @Override + public MallInboundResult handle(MallOrderEventDTO dto) { + return inboundEventService.updateSubOrderStatus(dto, MallInboundEventService.ORDER_CANCELLED); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallConfirmReceiptEventHandler.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallConfirmReceiptEventHandler.java new file mode 100644 index 00000000..35d54853 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallConfirmReceiptEventHandler.java @@ -0,0 +1,29 @@ +package com.budwk.app.zhgh.pointsmall.mallbridge.event.handlers; + +import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallOrderEventDTO; +import com.budwk.app.zhgh.pointsmall.mallbridge.event.MallOrderEventHandler; +import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundResult; +import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallInboundEventService; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; + +/** + * MallConfirmReceiptEventHandler + * orderType=6: 确认收货事件处理器。 + */ +@IocBean +public class MallConfirmReceiptEventHandler implements MallOrderEventHandler { + + @Inject + private MallInboundEventService inboundEventService; + + @Override + public int getType() { + return 6; + } + + @Override + public MallInboundResult handle(MallOrderEventDTO dto) { + return inboundEventService.updateSubOrderStatus(dto, MallInboundEventService.ORDER_COMPLETED); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallExchangeSubEventHandler.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallExchangeSubEventHandler.java new file mode 100644 index 00000000..f70ac17d --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallExchangeSubEventHandler.java @@ -0,0 +1,29 @@ +package com.budwk.app.zhgh.pointsmall.mallbridge.event.handlers; + +import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallOrderEventDTO; +import com.budwk.app.zhgh.pointsmall.mallbridge.event.MallOrderEventHandler; +import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundResult; +import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallInboundEventService; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; + +/** + * MallExchangeSubEventHandler + * orderType=5: 换货单事件处理器。 + */ +@IocBean +public class MallExchangeSubEventHandler implements MallOrderEventHandler { + + @Inject + private MallInboundEventService inboundEventService; + + @Override + public int getType() { + return 5; + } + + @Override + public MallInboundResult handle(MallOrderEventDTO dto) { + return inboundEventService.updateSubOrderStatus(dto, MallInboundEventService.ORDER_EXCHANGE); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallInvoiceInfoEventHandler.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallInvoiceInfoEventHandler.java new file mode 100644 index 00000000..4a51cf59 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallInvoiceInfoEventHandler.java @@ -0,0 +1,29 @@ +package com.budwk.app.zhgh.pointsmall.mallbridge.event.handlers; + +import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallOrderEventDTO; +import com.budwk.app.zhgh.pointsmall.mallbridge.event.MallOrderEventHandler; +import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundResult; +import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallInboundEventService; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; + +/** + * MallInvoiceInfoEventHandler + * orderType=11: 发票信息事件处理器。 + */ +@IocBean +public class MallInvoiceInfoEventHandler implements MallOrderEventHandler { + + @Inject + private MallInboundEventService inboundEventService; + + @Override + public int getType() { + return 11; + } + + @Override + public MallInboundResult handle(MallOrderEventDTO dto) { + return inboundEventService.invoiceInfo(dto); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallSplitSubOrderEventHandler.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallSplitSubOrderEventHandler.java new file mode 100644 index 00000000..25e6456b --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallSplitSubOrderEventHandler.java @@ -0,0 +1,29 @@ +package com.budwk.app.zhgh.pointsmall.mallbridge.event.handlers; + +import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallOrderEventDTO; +import com.budwk.app.zhgh.pointsmall.mallbridge.event.MallOrderEventHandler; +import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundResult; +import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallInboundEventService; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; + +/** + * MallSplitSubOrderEventHandler + * orderType=2: 拆分子订单事件处理器。 + */ +@IocBean +public class MallSplitSubOrderEventHandler implements MallOrderEventHandler { + + @Inject + private MallInboundEventService inboundEventService; + + @Override + public int getType() { + return 2; + } + + @Override + public MallInboundResult handle(MallOrderEventDTO dto) { + return inboundEventService.saveOrderSub(dto); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallSubOrderReturnEventHandler.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallSubOrderReturnEventHandler.java new file mode 100644 index 00000000..40abcee8 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/event/handlers/MallSubOrderReturnEventHandler.java @@ -0,0 +1,29 @@ +package com.budwk.app.zhgh.pointsmall.mallbridge.event.handlers; + +import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallOrderEventDTO; +import com.budwk.app.zhgh.pointsmall.mallbridge.event.MallOrderEventHandler; +import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundResult; +import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallInboundEventService; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; + +/** + * MallSubOrderReturnEventHandler + * orderType=4: 退货单事件处理器。 + */ +@IocBean +public class MallSubOrderReturnEventHandler implements MallOrderEventHandler { + + @Inject + private MallInboundEventService inboundEventService; + + @Override + public int getType() { + return 4; + } + + @Override + public MallInboundResult handle(MallOrderEventDTO dto) { + return inboundEventService.updateSubOrderStatus(dto, MallInboundEventService.ORDER_REFUNDED); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInboundConfirmPurchaseService.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInboundConfirmPurchaseService.java new file mode 100644 index 00000000..30a80486 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInboundConfirmPurchaseService.java @@ -0,0 +1,85 @@ +package com.budwk.app.zhgh.pointsmall.mallbridge.service; + +import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallPurchaseConfirmDTO; +import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundResult; +import com.budwk.app.zhgh.pointsmall.mallbridge.service.support.AbstractMallBridgeSupport; +import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderMain; +import com.budwk.app.zhgh.pointsmall.points.service.PointsMallPointsLockService; +import com.budwk.app.zhgh.pointsmall.points.service.PointsMallUserPointsService; +import lombok.extern.slf4j.Slf4j; +import org.nutz.dao.Cnd; +import org.nutz.dao.Dao; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.trans.Atom; +import org.nutz.trans.Trans; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Date; + +/** + * 商城桥接确认购买服务。 + */ +@Slf4j +@IocBean(args = {"refer:dao"}) +public class MallInboundConfirmPurchaseService extends AbstractMallBridgeSupport { + + @Inject + private PointsMallUserPointsService pointsMallUserPointsService; + @Inject + private PointsMallPointsLockService pointsMallPointsLockService; + + public MallInboundConfirmPurchaseService(Dao dao) { + super(dao); + } + + /** + * 确认购买:校验积分是否充足,更新主单支付状态,并锁定主单积分。 + */ + public MallInboundResult confirmPurchase(MallPurchaseConfirmDTO dto) { + log.info("积分商城确认购买开始,supplierId={},orderId={}", dto.getSupplierId(), dto.getOrderId()); + PointsMallOrderMain main = dao.fetch(PointsMallOrderMain.class, Cnd.where("supplierId", "=", dto.getSupplierId()) + .and("orderId", "=", dto.getOrderId()).and("delFlag", "=", false)); + if (main == null) { + return MallInboundResult.fail(404, "订单不存在"); + } + BigDecimal totalPrice = money(nvl(dto.getTotalPrice(), main.getTotalPrice())); + BigDecimal cash = money(nvl(dto.getWPayOrAPay())); + BigDecimal points = money(nvl(dto.getPointsPrice())); + if (totalPrice.compareTo(cash.add(points)) != 0 || money(nvl(main.getTotalPrice())).compareTo(totalPrice) != 0) { + return MallInboundResult.fail(400, "订单总金额不一致"); + } + BigDecimal available = pointsMallUserPointsService.availablePoints(main.getUserId()); + if (available.compareTo(points) < 0) { + return MallInboundResult.fail(400, "积分不足,无法确认购买"); + } + Trans.exec((Atom) () -> { + updateMainOrderPay(dto.getSupplierId(), dto.getOrderId(), cash.compareTo(BigDecimal.ZERO) == 0 ? 0 : 1, totalPrice, cash, points); + pointsMallPointsLockService.lockMain(main, points); + }); + log.info("积分商城确认购买完成,supplierId={},orderId={},points={}", dto.getSupplierId(), dto.getOrderId(), points); + return MallInboundResult.success(); + } + + protected MallInboundResult updateMainOrderPay(String supplierId, String orderId, Integer cashPaid, BigDecimal totalPrice, BigDecimal cash, BigDecimal points) { + PointsMallOrderMain main = dao.fetch(PointsMallOrderMain.class, Cnd.where("supplierId", "=", supplierId) + .and("orderId", "=", orderId).and("delFlag", "=", false)); + if (main == null) { + return MallInboundResult.fail(404, "订单不存在"); + } + main.setCashPaid(cashPaid); + main.setTotalPrice(money(nvl(totalPrice, main.getTotalPrice()))); + main.setWPayOrAPay(money(nvl(cash, main.getWPayOrAPay()))); + main.setPointsPrice(money(nvl(points, main.getPointsPrice()))); + main.setOrderState(cashPaid == 1 || cashPaid == 0 ? ORDER_FINISH_PAY : main.getOrderState()); + main.setOrderUpdateTime(new Date()); + dao.updateIgnoreNull(main); + log.info("积分商城主单支付状态更新完成,supplierId={},orderId={},cashPaid={}", supplierId, orderId, cashPaid); + return MallInboundResult.success(); + } + + private BigDecimal money(BigDecimal value) { + return nvl(value).setScale(2, RoundingMode.HALF_UP); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInboundEventService.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInboundEventService.java new file mode 100644 index 00000000..0396b89e --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInboundEventService.java @@ -0,0 +1,313 @@ +package com.budwk.app.zhgh.pointsmall.mallbridge.service; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONUtil; +import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallOrderQueryDTO; +import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallOrderEventDTO; +import com.budwk.app.zhgh.pointsmall.mallbridge.dto.OrderInfoDTO; +import com.budwk.app.zhgh.pointsmall.mallbridge.dto.OrderProInfoDTO; +import com.budwk.app.zhgh.pointsmall.mallbridge.event.MallOrderEventHandler; +import com.budwk.app.zhgh.pointsmall.mallbridge.event.handlers.MallAbolishSubOrderEventHandler; +import com.budwk.app.zhgh.pointsmall.mallbridge.event.handlers.MallCancelPayedMainOrderEventHandler; +import com.budwk.app.zhgh.pointsmall.mallbridge.event.handlers.MallCancelPendingOrderEventHandler; +import com.budwk.app.zhgh.pointsmall.mallbridge.event.handlers.MallCancelSubOrderEventHandler; +import com.budwk.app.zhgh.pointsmall.mallbridge.event.handlers.MallConfirmReceiptEventHandler; +import com.budwk.app.zhgh.pointsmall.mallbridge.event.handlers.MallExchangeSubEventHandler; +import com.budwk.app.zhgh.pointsmall.mallbridge.event.handlers.MallInvoiceInfoEventHandler; +import com.budwk.app.zhgh.pointsmall.mallbridge.event.handlers.MallSplitSubOrderEventHandler; +import com.budwk.app.zhgh.pointsmall.mallbridge.event.handlers.MallSubOrderReturnEventHandler; +import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundResult; +import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallOutboundResult; +import com.budwk.app.zhgh.pointsmall.mallbridge.service.support.AbstractMallBridgeSupport; +import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderMain; +import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderSub; +import com.budwk.app.zhgh.pointsmall.points.service.PointsMallPointsLockService; +import lombok.extern.slf4j.Slf4j; +import org.nutz.dao.Chain; +import org.nutz.dao.Cnd; +import org.nutz.dao.Dao; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.trans.Atom; +import org.nutz.trans.Trans; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 商城桥接订单事件入站服务。 + */ +@Slf4j +@IocBean(args = {"refer:dao"}) +public class MallInboundEventService extends AbstractMallBridgeSupport { + + public static final int ORDER_CANCELLED = 3; + public static final int ORDER_COMPLETED = 5; + public static final int ORDER_EXCHANGE = 6; + public static final int ORDER_REFUNDED = 7; + public static final int ORDER_ABOLISH = 8; + + @Inject + private PointsMallPointsLockService pointsMallPointsLockService; + @Inject + private MallOrderQueryOutboundService mallOrderQueryOutboundService; + @Inject + private MallCancelPendingOrderEventHandler mallCancelPendingOrderEventHandler; + @Inject + private MallSplitSubOrderEventHandler mallSplitSubOrderEventHandler; + @Inject + private MallCancelSubOrderEventHandler mallCancelSubOrderEventHandler; + @Inject + private MallSubOrderReturnEventHandler mallSubOrderReturnEventHandler; + @Inject + private MallExchangeSubEventHandler mallExchangeSubEventHandler; + @Inject + private MallConfirmReceiptEventHandler mallConfirmReceiptEventHandler; + @Inject + private MallCancelPayedMainOrderEventHandler mallCancelPayedMainOrderEventHandler; + @Inject + private MallAbolishSubOrderEventHandler mallAbolishSubOrderEventHandler; + @Inject + private MallInvoiceInfoEventHandler mallInvoiceInfoEventHandler; + + public MallInboundEventService(Dao dao) { + super(dao); + } + + /** + * 处理通用事件: 分发到具体处理器。 + */ + public MallInboundResult processEvent(MallOrderEventDTO dto) { + Integer type = dto.getOrderType(); + if (type == null) { + return MallInboundResult.fail(400, "缺少事件类型"); + } + MallOrderEventHandler handler = handlerRegistry().get(type); + if (handler == null) { + return MallInboundResult.fail(400, "未知事件类型"); + } + try { + return handler.handle(dto); + } catch (Exception e) { + log.error("事件处理失败:{}", e.getMessage(), e); + return MallInboundResult.fail(500, "服务器内部错误"); + } + } + + private Map handlerRegistry() { + Map handlerRegistry = new HashMap<>(); + register(handlerRegistry, mallCancelPendingOrderEventHandler); + register(handlerRegistry, mallSplitSubOrderEventHandler); + register(handlerRegistry, mallCancelSubOrderEventHandler); + register(handlerRegistry, mallSubOrderReturnEventHandler); + register(handlerRegistry, mallExchangeSubEventHandler); + register(handlerRegistry, mallConfirmReceiptEventHandler); + register(handlerRegistry, mallCancelPayedMainOrderEventHandler); + register(handlerRegistry, mallAbolishSubOrderEventHandler); + register(handlerRegistry, mallInvoiceInfoEventHandler); + return handlerRegistry; + } + + private void register(Map handlerRegistry, MallOrderEventHandler handler) { + if (handler != null) { + handlerRegistry.put(handler.getType(), handler); + } + } + + public MallInboundResult cancelMainOrder(MallOrderEventDTO dto, boolean paidNotSplit) { + String mainOrderId = StrUtil.blankToDefault(dto.getMainOrderId(), dto.getOrderId()); + if (StrUtil.isBlank(mainOrderId)) { + return MallInboundResult.fail(400, "主订单信息不能为空"); + } + PointsMallOrderMain main = dao.fetch(PointsMallOrderMain.class, Cnd.where("supplierId", "=", dto.getSupplierId()) + .and("orderId", "=", mainOrderId).and("delFlag", "=", false)); + if (main == null) { + return MallInboundResult.fail(404, "主订单不存在"); + } + if (!paidNotSplit && !Integer.valueOf(ORDER_PENDING_PAY).equals(main.getOrderState())) { + return MallInboundResult.fail(400, "只能取消未支付主单"); + } + Date now = new Date(); + Trans.exec((Atom) () -> { + main.setOrderState(ORDER_CANCELLED); + main.setOrderUpdateTime(now); + dao.updateIgnoreNull(main); + dao.update(PointsMallOrderSub.class, Chain.make("orderState", ORDER_CANCELLED).add("orderUpdateTime", now), + Cnd.where("supplierId", "=", dto.getSupplierId()).and("mainOrderId", "=", mainOrderId).and("delFlag", "=", false)); + pointsMallPointsLockService.releaseMain(main.getUserId(), main.getSupplierId(), main.getOrderId()); + }); + log.info("积分商城主单取消完成,supplierId={},mainOrderId={},paidNotSplit={}", dto.getSupplierId(), mainOrderId, paidNotSplit); + return MallInboundResult.success(); + } + + public MallInboundResult saveOrderSub(MallOrderEventDTO dto) { + PointsMallOrderMain main = dao.fetch(PointsMallOrderMain.class, Cnd.where("supplierId", "=", dto.getSupplierId()) + .and("orderId", "=", dto.getMainOrderId()).and("delFlag", "=", false)); + if (main == null) { + return MallInboundResult.fail(404, "主订单不存在"); + } + List subOrderIds = splitIds(dto.getOrderId()); + if (mallOrderQueryOutboundService != null) { + try { + return splitSubOrderByQuery(dto, main, subOrderIds); + } catch (Exception e) { + log.error("split sub order by query failed, fallback to local split logic, supplierId={}, mainOrderId={}, orderId={}", + dto.getSupplierId(), dto.getMainOrderId(), dto.getOrderId(), e); + } + } + if (subOrderIds.isEmpty()) { + return MallInboundResult.fail(400, "子订单信息不能为空"); + } + List products = StrUtil.isBlank(main.getExtJson()) || "null".equalsIgnoreCase(main.getExtJson()) + ? Collections.emptyList() + : JSONUtil.toList(main.getExtJson(), OrderProInfoDTO.class); + List subLocks = new ArrayList<>(); + Trans.exec((Atom) () -> { + for (String subOrderId : subOrderIds) { + PointsMallOrderSub sub = dao.fetch(PointsMallOrderSub.class, Cnd.where("orderId", "=", subOrderId) + .and("supplierId", "=", dto.getSupplierId()).and("delFlag", "=", false)); + if (sub == null) { + sub = toSub(main, subOrderId); + sub.setOrderState(ORDER_IN_PROGRESS); + dao.insert(sub); + insertProducts(subOrderId, products); + } else { + sub.setOrderState(ORDER_IN_PROGRESS); + sub.setOrderUpdateTime(new Date()); + dao.updateIgnoreNull(sub); + } + subLocks.add(sub); + } + pointsMallPointsLockService.createSubLocks(main, subLocks); + }); + log.info("积分商城拆单完成,supplierId={},mainOrderId={},subOrderCount={}", dto.getSupplierId(), dto.getMainOrderId(), subOrderIds.size()); + return MallInboundResult.success(); + } + + private MallInboundResult splitSubOrderByQuery(MallOrderEventDTO dto, PointsMallOrderMain main, List subOrderIds) { + log.info("9.2.1 生成订单信息推送-订单状态为:2(拆单子单)开始执行:{}", dto); + if (subOrderIds.isEmpty()) { + return MallInboundResult.fail(400, "缺少拆单信息"); + } + log.info("9.2.1 生成订单信息推送-订单状态为:2(拆单子单)调用外部服务获取子单信息:{}", dto); + List orderInfoList; + try { + orderInfoList = getOrderInfoList(dto); + } catch (Exception e) { + log.error("9.2.1 生成订单信息推送-订单状态为:2(拆单子单),调用9.2.2获取订单详情接口异常:{}", e.getMessage(), e); + return MallInboundResult.fail(-1, "调用9.2.2获取订单详情接口异常,异常订单:" + subOrderIds); + } + if (orderInfoList == null || orderInfoList.isEmpty()) { + return MallInboundResult.fail(404, "子单不存在"); + } + + List subLocks = new ArrayList<>(); + Trans.exec((Atom) () -> { + for (OrderInfoDTO info : orderInfoList) { + PointsMallOrderSub sub = dao.fetch(PointsMallOrderSub.class, Cnd.where("orderId", "=", info.getOrderId()) + .and("supplierId", "=", dto.getSupplierId()).and("delFlag", "=", false)); + if (sub == null) { + sub = toSub(main, info); + sub.setOrderState(ORDER_IN_PROGRESS); + dao.insert(sub); + insertProducts(info.getOrderId(), info.getOrderProInfos() == null ? Collections.emptyList() : info.getOrderProInfos()); + } else { + log.info("9.2.1 生成订单信息推送-订单状态为:2(拆单子单)子单已存在:{}", info); + sub.setOrderState(ORDER_IN_PROGRESS); + sub.setOrderUpdateTime(new Date()); + dao.updateIgnoreNull(sub); + } + subLocks.add(sub); + } + pointsMallPointsLockService.createSubLocks(main, subLocks); + }); + log.info("9.2.1 生成订单信息推送-订单状态为:2(拆单子单)结束,supplierId={},mainOrderId={},subOrderCount={}", + dto.getSupplierId(), dto.getMainOrderId(), orderInfoList.size()); + return MallInboundResult.success(); + } + + private List getOrderInfoList(MallOrderEventDTO dto) { + MallOrderQueryDTO queryDTO = new MallOrderQueryDTO(); + queryDTO.setSupplierId(dto.getSupplierId()); + queryDTO.setOrderId(dto.getOrderId()); + queryDTO.setOrderType(dto.getOrderType()); + MallOutboundResult result = mallOrderQueryOutboundService.orderQuery(queryDTO); + if (result == null || result.getResultCode() != 0) { + throw new RuntimeException(result == null ? "获取订单详情返回为空" : result.getMessage()); + } + return result.getOrderInfo(); + } + + private PointsMallOrderSub toSub(PointsMallOrderMain main, OrderInfoDTO info) { + PointsMallOrderSub sub = toSub(main, info.getOrderId()); + sub.setOrderNumberId(info.getOrderNumberId()); + sub.setAplName(StrUtil.blankToDefault(info.getAplName(), main.getAplName())); + sub.setOrderCreateTime(parseDate(info.getCreateTime())); + sub.setOrderUpdateTime(parseDate(info.getUpdateTime())); + sub.setDeliveryTime(parseDate(info.getDeliveryTime())); + sub.setOrderFinishTime(parseDate(info.getOrderFinishTime())); + sub.setOrderCompleteTime(parseDate(info.getCompleteTime())); + sub.setDeliveryStatus(parseInteger(info.getDeliveryStatus())); + sub.setFreightCost(nvl(info.getFreightCost())); + sub.setWPayOrAPayFreightCost(nvl(info.getWPayOrAPayFreightCost())); + sub.setRefund(nvl(info.getRefund())); + sub.setWPayOrAPayRefund(nvl(info.getWPayOrAPayRefund())); + sub.setLogisticsOrderId(info.getLogisticsOrderId()); + sub.setCrrgBsnNm(info.getCrrgBsnNm()); + sub.setTotalPrice(nvl(info.getTotalPrice())); + sub.setWPayOrAPay(nvl(info.getWPayOrAPay())); + sub.setPointsPrice(nvl(info.getPointsPrice())); + sub.setInvoiceCode(info.getInvoiceCode()); + sub.setExtJson(JSONUtil.toJsonStr(info.getOrderProInfos() == null ? Collections.emptyList() : info.getOrderProInfos())); + sub.setReconciliationStatus(0); + sub.setInvoiceStatus(0); + return sub; + } + + public MallInboundResult updateSubOrderStatus(MallOrderEventDTO dto, int state) { + PointsMallOrderSub sub = dao.fetch(PointsMallOrderSub.class, Cnd.where("supplierId", "=", dto.getSupplierId()) + .and("orderId", "=", dto.getOrderId()).and("delFlag", "=", false)); + if (sub == null) { + return MallInboundResult.fail(404, "订单不存在"); + } + if (Integer.valueOf(state).equals(sub.getOrderState())) { + log.info("积分商城子单状态已是目标状态,跳过重复处理,supplierId={},orderId={},state={}", dto.getSupplierId(), dto.getOrderId(), state); + return MallInboundResult.success(); + } + Trans.exec((Atom) () -> { + sub.setOrderState(state); + sub.setOrderUpdateTime(new Date()); + if (state == ORDER_RECEIVED) { + sub.setOrderFinishTime(new Date()); + } + if (state == ORDER_COMPLETED) { + sub.setOrderCompleteTime(new Date()); + } + dao.updateIgnoreNull(sub); + if (state == ORDER_CANCELLED || state == ORDER_REFUNDED || state == ORDER_ABOLISH) { + pointsMallPointsLockService.releaseOrReturnSub(sub, state, nvl(sub.getRefund(), BigDecimal.ZERO)); + } + if (state == ORDER_COMPLETED) { + pointsMallPointsLockService.deductCompletedSub(sub); + } + }); + log.info("积分商城子单状态更新完成,supplierId={},orderId={},state={}", dto.getSupplierId(), dto.getOrderId(), state); + return MallInboundResult.success(); + } + + public MallInboundResult invoiceInfo(MallOrderEventDTO dto) { + String mainOrderId = StrUtil.blankToDefault(dto.getMainOrderId(), dto.getOrderId()); + if (StrUtil.isBlank(mainOrderId)) { + return MallInboundResult.fail(400, "主订单信息不能为空"); + } + dao.update(PointsMallOrderSub.class, Chain.make("invoiceStatus", 1), Cnd.where("supplierId", "=", dto.getSupplierId()) + .and("mainOrderId", "=", mainOrderId).and("delFlag", "=", false)); + log.info("积分商城发票通知状态更新完成,supplierId={},mainOrderId={}", dto.getSupplierId(), mainOrderId); + return MallInboundResult.success(); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInboundPendingOrderService.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInboundPendingOrderService.java new file mode 100644 index 00000000..1bc5bad5 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInboundPendingOrderService.java @@ -0,0 +1,42 @@ +package com.budwk.app.zhgh.pointsmall.mallbridge.service; + +import cn.hutool.core.util.StrUtil; +import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallPendingOrderDTO; +import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundResult; +import com.budwk.app.zhgh.pointsmall.mallbridge.service.support.AbstractMallBridgeSupport; +import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderMain; +import lombok.extern.slf4j.Slf4j; +import org.nutz.dao.Cnd; +import org.nutz.dao.Dao; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.trans.Atom; +import org.nutz.trans.Trans; + +/** + * 商城桥接待支付主单入站服务。 + */ +@Slf4j +@IocBean(args = {"refer:dao"}) +public class MallInboundPendingOrderService extends AbstractMallBridgeSupport { + + public MallInboundPendingOrderService(Dao dao) { + super(dao); + } + + /** + * 接收供应商推送的待支付主单。 + */ + public MallInboundResult receivePending(MallPendingOrderDTO dto) { + if (dto.getOrderInfo() == null || StrUtil.isBlank(dto.getOrderInfo().getOrderId())) { + return MallInboundResult.fail(400, "订单信息不能为空"); + } + log.info("积分商城接收待支付订单开始,supplierId={},orderId={}", dto.getSupplierId(), dto.getOrderInfo().getOrderId()); + if (dao.count(PointsMallOrderMain.class, Cnd.where("orderId", "=", dto.getOrderInfo().getOrderId()) + .and("supplierId", "=", dto.getSupplierId()).and("delFlag", "=", false)) > 0) { + return MallInboundResult.fail(-1, "订单已存在"); + } + Trans.exec((Atom) () -> dao.insert(toMain(dto.getSupplierId(), dto.getUserId(), dto.getOrderInfo(), ORDER_PENDING_PAY))); + log.info("积分商城接收待支付订单完成,supplierId={},orderId={}", dto.getSupplierId(), dto.getOrderInfo().getOrderId()); + return MallInboundResult.success(); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInboundPointsQueryService.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInboundPointsQueryService.java new file mode 100644 index 00000000..822f1eef --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInboundPointsQueryService.java @@ -0,0 +1,27 @@ +package com.budwk.app.zhgh.pointsmall.mallbridge.service; + +import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallPointQueryDTO; +import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundPointsResult; +import com.budwk.app.zhgh.pointsmall.points.service.PointsMallUserPointsService; +import lombok.extern.slf4j.Slf4j; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; + +/** + * 商城桥接积分查询服务。 + */ +@Slf4j +@IocBean +public class MallInboundPointsQueryService { + + @Inject + private PointsMallUserPointsService pointsMallUserPointsService; + + /** + * 查询用户可用积分。 + */ + public MallInboundPointsResult queryPoints(MallPointQueryDTO dto) { + log.info("积分商城查询用户积分,supplierId={},userId={}", dto == null ? null : dto.getSupplierId(), dto == null ? null : dto.getUserId()); + return MallInboundPointsResult.success(pointsMallUserPointsService.availablePoints(dto.getUserId())); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInboundPurchaseResultService.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInboundPurchaseResultService.java new file mode 100644 index 00000000..5d1ac433 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInboundPurchaseResultService.java @@ -0,0 +1,69 @@ +package com.budwk.app.zhgh.pointsmall.mallbridge.service; + +import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallPurchaseResultDTO; +import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundResult; +import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderMain; +import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderSub; +import com.budwk.app.zhgh.pointsmall.points.service.PointsMallPointsLockService; +import lombok.extern.slf4j.Slf4j; +import org.nutz.dao.Chain; +import org.nutz.dao.Cnd; +import org.nutz.dao.Dao; +import org.nutz.ioc.loader.annotation.Inject; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.trans.Atom; +import org.nutz.trans.Trans; + +import java.util.Date; + +/** + * 商城桥接购买结果通知服务。 + */ +@Slf4j +@IocBean(args = {"refer:dao"}) +public class MallInboundPurchaseResultService { + + private final Dao dao; + + @Inject + private MallInboundConfirmPurchaseService mallInboundConfirmPurchaseService; + @Inject + private PointsMallPointsLockService pointsMallPointsLockService; + + public MallInboundPurchaseResultService(Dao dao) { + this.dao = dao; + } + + /** + * 接收购买结果。支付成功只同步现金支付状态;支付失败取消主单并释放锁定积分。 + */ + public MallInboundResult purchaseResult(MallPurchaseResultDTO dto) { + log.info("积分商城购买结果通知,supplierId={},orderId={},purchaseResult={}", dto.getSupplierId(), dto.getOrderId(), dto.getPurchaseResult()); + PointsMallOrderMain main = dao.fetch(PointsMallOrderMain.class, Cnd.where("supplierId", "=", dto.getSupplierId()) + .and("orderId", "=", dto.getOrderId()).and("delFlag", "=", false)); + if (main == null) { + return MallInboundResult.fail(404, "订单不存在"); + } + if (Boolean.TRUE.equals(dto.getPurchaseResult())) { + Integer cashPaid = main.getWPayOrAPay() != null && main.getWPayOrAPay().signum() > 0 ? 2 : 0; + return mallInboundConfirmPurchaseService.updateMainOrderPay(dto.getSupplierId(), dto.getOrderId(), cashPaid, + dto.getTotalPrice(), dto.getWPayOrAPay(), dto.getPointsPrice()); + } + Trans.exec((Atom) () -> { + dao.update(PointsMallOrderMain.class, + Chain.make("orderState", 3) + .add("cashPaid", main.getWPayOrAPay() != null && main.getWPayOrAPay().signum() > 0 ? 3 : main.getCashPaid()) + .add("totalPrice", dto.getTotalPrice()) + .add("pointsPrice", dto.getPointsPrice()) + .add("wPayOrAPay", dto.getWPayOrAPay()) + .add("orderUpdateTime", new Date()), + Cnd.where("id", "=", main.getId())); + dao.update(PointsMallOrderSub.class, + Chain.make("orderState", 3).add("orderUpdateTime", new Date()), + Cnd.where("supplierId", "=", dto.getSupplierId()).and("mainOrderId", "=", dto.getOrderId()).and("delFlag", "=", false)); + pointsMallPointsLockService.releaseMain(main.getUserId(), main.getSupplierId(), main.getOrderId()); + }); + log.info("积分商城购买失败处理完成,supplierId={},orderId={}", dto.getSupplierId(), dto.getOrderId()); + return MallInboundResult.success(); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInvoiceApplyOutboundService.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInvoiceApplyOutboundService.java new file mode 100644 index 00000000..84b0c0e9 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInvoiceApplyOutboundService.java @@ -0,0 +1,99 @@ +package com.budwk.app.zhgh.pointsmall.mallbridge.service; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.StrUtil; +import com.budwk.app.zhgh.pointsmall.invoice.models.PointsMallInvoiceInfo; +import com.budwk.app.zhgh.pointsmall.invoice.models.PointsMallInvoiceMain; +import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallInvoiceApplyDTO; +import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallOutboundResult; +import com.budwk.app.zhgh.pointsmall.mallbridge.service.support.AbstractMallBridgeSupport; +import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderSub; +import lombok.extern.slf4j.Slf4j; +import org.nutz.dao.Chain; +import org.nutz.dao.Cnd; +import org.nutz.dao.Dao; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.trans.Atom; +import org.nutz.trans.Trans; + +import java.math.BigDecimal; +import java.util.List; +import java.util.Objects; + +/** + * 商城桥接开票申请出站服务。 + */ +@Slf4j +@IocBean(args = {"refer:dao"}) +public class MallInvoiceApplyOutboundService extends AbstractMallBridgeSupport { + + public MallInvoiceApplyOutboundService(Dao dao) { + super(dao); + } + + /** + * 接收开票申请并保存开票记录。 + */ + public MallOutboundResult applyInvoice(MallInvoiceApplyDTO dto) { + if (dto == null || StrUtil.hasBlank(dto.getSupplierId(), dto.getSettlementId(), dto.getOrderIds())) { + return MallOutboundResult.fail(400, "开票申请参数不能为空"); + } + List orderIds = splitIds(dto.getOrderIds()); + log.info("积分商城开票申请开始,supplierId={},settlementId={},orderCount={}", dto.getSupplierId(), dto.getSettlementId(), orderIds.size()); + List orders = dao.query(PointsMallOrderSub.class, Cnd.where("supplierId", "=", dto.getSupplierId()) + .and("orderId", "in", orderIds).and("delFlag", "=", false)); + if (orders.isEmpty()) { + return MallOutboundResult.fail(404, "未找到可开票订单"); + } + if (dto.getOrderNum() != null && dto.getOrderNum() != orders.size()) { + return MallOutboundResult.fail(400, "订单数量与开票申请不一致"); + } + BigDecimal total = orders.stream().map(PointsMallOrderSub::getTotalPrice).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add); + if (dto.getOrderTotalPrice() != null && total.compareTo(dto.getOrderTotalPrice()) != 0) { + return MallOutboundResult.fail(400, "订单金额与开票申请不一致"); + } + Trans.exec((Atom) () -> saveInvoiceApply(dto, orderIds, total)); + log.info("积分商城开票申请完成,supplierId={},settlementId={}", dto.getSupplierId(), dto.getSettlementId()); + return MallOutboundResult.success(); + } + + private void saveInvoiceApply(MallInvoiceApplyDTO dto, List orderIds, BigDecimal total) { + PointsMallInvoiceMain main = dao.fetch(PointsMallInvoiceMain.class, Cnd.where("settlementId", "=", dto.getSettlementId()).and("delFlag", "=", false)); + if (main == null) { + main = new PointsMallInvoiceMain(); + main.setSettlementId(dto.getSettlementId()); + main.setBSuccess(0); + main.setSucOrderIds(String.join(",", orderIds)); + main.setFailOrderIds(""); + main.setFailMsg(""); + dao.insert(main); + } else { + main.setBSuccess(0); + main.setSucOrderIds(String.join(",", orderIds)); + main.setFailOrderIds(""); + main.setFailMsg(""); + dao.updateIgnoreNull(main); + } + + PointsMallInvoiceInfo info = new PointsMallInvoiceInfo(); + info.setMainId(main.getId()); + info.setInvoiceId(dto.getSettlementId()); + info.setInvoiceCode(dto.getInvoiceCode()); + info.setInvoiceDate(parseDate(dto.getInvoiceDate())); + info.setInvoiceNakeAmount(nvl(dto.getInvoiceNakedPrice(), total)); + info.setInvoiceTaxAmount(nvl(dto.getInvoiceTaxPrice(), BigDecimal.ZERO)); + info.setInvoiceAmount(total); + info.setInvoiceType(Convert.toInt(dto.getInvoiceType(), null)); + info.setInvoiceAddress(dto.getInvoiceAddress()); + info.setInvoiceContact(dto.getInvoiceContact()); + info.setInvoiceContent(dto.getInvoiceContent()); + info.setInvoiceBank(dto.getBankName()); + info.setInvoiceAccount(dto.getBankAccount()); + info.setTaxIdNumber(dto.getInvoiceCode()); + info.setRemark(dto.getRemark()); + dao.insert(info); + + dao.update(PointsMallOrderSub.class, Chain.make("invoiceStatus", 2).add("invoiceCode", dto.getSettlementId()), + Cnd.where("supplierId", "=", dto.getSupplierId()).and("orderId", "in", orderIds)); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInvoiceInfoQueryService.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInvoiceInfoQueryService.java new file mode 100644 index 00000000..656571f0 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallInvoiceInfoQueryService.java @@ -0,0 +1,53 @@ +package com.budwk.app.zhgh.pointsmall.mallbridge.service; + +import cn.hutool.core.util.StrUtil; +import com.budwk.app.zhgh.pointsmall.invoice.models.PointsMallInvoiceInfo; +import com.budwk.app.zhgh.pointsmall.invoice.models.PointsMallInvoiceMain; +import com.budwk.app.zhgh.pointsmall.mallbridge.dto.GetInvoiceInfoDTO; +import com.budwk.app.zhgh.pointsmall.mallbridge.dto.InvoiceInfoDTO; +import com.budwk.app.zhgh.pointsmall.mallbridge.dto.QueryInvoiceInfoDTO; +import com.budwk.app.zhgh.pointsmall.mallbridge.service.support.AbstractMallBridgeSupport; +import lombok.extern.slf4j.Slf4j; +import org.nutz.dao.Cnd; +import org.nutz.dao.Dao; +import org.nutz.ioc.loader.annotation.IocBean; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * 商城桥接发票信息查询服务。 + */ +@Slf4j +@IocBean(args = {"refer:dao"}) +public class MallInvoiceInfoQueryService extends AbstractMallBridgeSupport { + + public MallInvoiceInfoQueryService(Dao dao) { + super(dao); + } + + /** + * 查询指定结算单的发票信息。 + */ + public GetInvoiceInfoDTO queryInvoicesInfo(QueryInvoiceInfoDTO dto) { + if (dto == null || StrUtil.hasBlank(dto.getSupplierId(), dto.getSettlementId())) { + throw new IllegalArgumentException("发票查询参数不能为空"); + } + log.info("积分商城查询发票信息,supplierId={},settlementId={}", dto.getSupplierId(), dto.getSettlementId()); + PointsMallInvoiceMain main = dao.fetch(PointsMallInvoiceMain.class, Cnd.where("settlementId", "=", dto.getSettlementId()) + .and("delFlag", "=", false)); + if (main == null) { + throw new IllegalArgumentException("发票信息不存在"); + } + List invoiceInfos = dao.query(PointsMallInvoiceInfo.class, Cnd.where("mainId", "=", main.getId()).and("delFlag", "=", false)) + .stream().map(this::toInvoiceInfoDTO).collect(Collectors.toList()); + GetInvoiceInfoDTO result = new GetInvoiceInfoDTO(); + result.setBSuccess(main.getBSuccess()); + result.setSettlementId(main.getSettlementId()); + result.setSucOrderIds(main.getSucOrderIds()); + result.setFailOrderIds(main.getFailOrderIds()); + result.setFailMsg(main.getFailMsg()); + result.setInvoiceInfos(invoiceInfos); + return result; + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallOrderQueryOutboundService.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallOrderQueryOutboundService.java new file mode 100644 index 00000000..d6a079a2 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallOrderQueryOutboundService.java @@ -0,0 +1,90 @@ +package com.budwk.app.zhgh.pointsmall.mallbridge.service; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.http.HttpUtil; +import cn.hutool.json.JSONUtil; +import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallOrderQueryDTO; +import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallOutboundResult; +import com.budwk.app.zhgh.pointsmall.mallbridge.util.MallBridgeCryptoUtil; +import com.budwk.app.zhgh.pointsmall.supplier.models.PointsMallSupplier; +import lombok.extern.slf4j.Slf4j; +import org.nutz.dao.Cnd; +import org.nutz.dao.Dao; +import org.nutz.ioc.impl.PropertiesProxy; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.ioc.loader.annotation.Inject; + +import java.util.HashMap; +import java.util.Map; + +/** + * 9.2.2 获取订单详情外呼服务。 + */ +@Slf4j +@IocBean(args = {"refer:dao"}) +public class MallOrderQueryOutboundService { + + private final Dao dao; + + @Inject + private PropertiesProxy conf; + + public MallOrderQueryOutboundService(Dao dao) { + this.dao = dao; + } + + public MallOutboundResult queryOrderInfo(MallOrderQueryDTO dto) { + return orderQuery(dto); + } + + /** + * 调用 BAB7/供应商开放接口获取订单详情。 + */ + public MallOutboundResult orderQuery(MallOrderQueryDTO dto) { + log.info("9.2.2 获取订单详情调用开始:{}", dto); + String clientBAB7ServerUrl = conf.get("spdb.bab7.url", ""); + String keyHex = conf.get("sm4cbc.keyHex", ""); + String ivHex = conf.get("sm4cbc.ivHex", ""); + if (StrUtil.isBlank(clientBAB7ServerUrl)) { + throw new RuntimeException("未配置 BAB7 服务地址"); + } + + PointsMallSupplier supplier = dao.fetch(PointsMallSupplier.class, Cnd.where("supplierId", "=", dto.getSupplierId()).and("delFlag", "=", false)); + if (supplier == null) { + log.error("9.2.2 获取订单详情接口 未找到对应供应商信息,supplierId={}", dto.getSupplierId()); + throw new RuntimeException("未找到对应供应商信息"); + } + + long timestamp = System.currentTimeMillis(); + dto.setToken(MallBridgeCryptoUtil.generateSign(supplier.getSupplierId(), supplier.getClientId(), timestamp)); + dto.setTimestamp(timestamp); + + String address4 = StrUtil.blankToDefault(supplier.getSupplierApiUrl(), "") + "/supplierOpenApi/pointOrder/queryOrderInfo"; + String params = JSONUtil.toJsonStr(dto); + log.info("9.2.2 获取订单详情接口-请求参数:{}", params); + + String encrypt; + try { + encrypt = MallBridgeCryptoUtil.sm4CbcEncrypt(params, keyHex, ivHex); + } catch (Exception e) { + log.error("9.2.2 获取订单详情接口请求参数 SM4CBC 加密出错:{}", params, e); + throw new RuntimeException("9.2.2 获取订单详情接口请求参数 SM4CBC 加密出错"); + } + + Map bodyMap = new HashMap<>(); + bodyMap.put("Address4", address4); + bodyMap.put("BussDealMd", "P"); + bodyMap.put("SroNo", supplier.getSroNo()); + bodyMap.put("RsrvFld1", supplier.getClientId()); + bodyMap.put("AplParmObjct", encrypt); + + log.info("9.2.2 获取订单详情调用 BAB7 入参:{}", JSONUtil.toJsonStr(bodyMap)); + String response = HttpUtil.createPost(clientBAB7ServerUrl) + .contentType("application/json;charset=UTF-8") + .body(JSONUtil.toJsonStr(bodyMap)) + .execute() + .body(); + log.info("9.2.2 获取订单详情调用结束,返回报文:{}", response); + return JSONUtil.toBean(response, MallOutboundResult.class); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallPayFinishNoticeService.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallPayFinishNoticeService.java new file mode 100644 index 00000000..66ea6666 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallPayFinishNoticeService.java @@ -0,0 +1,59 @@ +package com.budwk.app.zhgh.pointsmall.mallbridge.service; + +import cn.hutool.core.util.StrUtil; +import com.budwk.app.zhgh.pointsmall.invoice.models.PointsMallInvoiceMain; +import com.budwk.app.zhgh.pointsmall.mallbridge.dto.FinalStatementDTO; +import com.budwk.app.zhgh.pointsmall.mallbridge.dto.OrderPayFinishNoticeDTO; +import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundResult; +import com.budwk.app.zhgh.pointsmall.mallbridge.service.support.AbstractMallBridgeSupport; +import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderSub; +import lombok.extern.slf4j.Slf4j; +import org.nutz.dao.Chain; +import org.nutz.dao.Cnd; +import org.nutz.dao.Dao; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.trans.Atom; +import org.nutz.trans.Trans; + +import java.util.List; + +/** + * 商城桥接支付完成通知服务。 + */ +@Slf4j +@IocBean(args = {"refer:dao"}) +public class MallPayFinishNoticeService extends AbstractMallBridgeSupport { + + public MallPayFinishNoticeService(Dao dao) { + super(dao); + } + + /** + * 接收结算支付完成通知并更新发票/订单开票状态。 + */ + public MallInboundResult payCompletion(OrderPayFinishNoticeDTO dto) { + if (dto == null || StrUtil.isBlank(dto.getSupplierId()) || dto.getExpFinalInfos() == null) { + return MallInboundResult.fail(400, "支付完成通知参数不能为空"); + } + log.info("积分商城支付完成通知开始,supplierId={},statementCount={}", dto.getSupplierId(), dto.getExpFinalInfos().size()); + Trans.exec((Atom) () -> { + for (FinalStatementDTO item : dto.getExpFinalInfos()) { + if (item == null || StrUtil.isBlank(item.getSettlementId())) { + continue; + } + PointsMallInvoiceMain main = dao.fetch(PointsMallInvoiceMain.class, Cnd.where("settlementId", "=", item.getSettlementId()) + .and("delFlag", "=", false)); + if (main != null) { + main.setBSuccess(1); + dao.updateIgnoreNull(main); + } + List orderIds = splitIds(item.getOrderIds()); + if (!orderIds.isEmpty()) { + dao.update(PointsMallOrderSub.class, Chain.make("invoiceStatus", 1), Cnd.where("supplierId", "=", dto.getSupplierId()).and("orderId", "in", orderIds)); + } + } + }); + log.info("积分商城支付完成通知完成,supplierId={}", dto.getSupplierId()); + return MallInboundResult.success(); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallPictureApplyOutboundService.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallPictureApplyOutboundService.java new file mode 100644 index 00000000..98cf33b7 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallPictureApplyOutboundService.java @@ -0,0 +1,85 @@ +package com.budwk.app.zhgh.pointsmall.mallbridge.service; + +import cn.hutool.core.util.StrUtil; +import com.budwk.app.zhgh.pointsmall.invoice.models.PointsMallInvoiceInfo; +import com.budwk.app.zhgh.pointsmall.invoice.models.PointsMallInvoiceMain; +import com.budwk.app.zhgh.pointsmall.mallbridge.dto.PictureQueryDTO; +import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallPictureOutboundResult; +import com.budwk.app.zhgh.pointsmall.mallbridge.service.support.AbstractMallBridgeSupport; +import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderSubProduct; +import lombok.extern.slf4j.Slf4j; +import org.nutz.dao.Cnd; +import org.nutz.dao.Dao; +import org.nutz.ioc.loader.annotation.IocBean; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * 商城桥接图片文件查询服务。 + */ +@Slf4j +@IocBean(args = {"refer:dao"}) +public class MallPictureApplyOutboundService extends AbstractMallBridgeSupport { + + public MallPictureApplyOutboundService(Dao dao) { + super(dao); + } + + /** + * 查询商品或发票图片 Base64 信息。 + */ + public MallPictureOutboundResult queryFileBase64Info(PictureQueryDTO dto) { + if (dto == null || StrUtil.hasBlank(dto.getSupplierId(), dto.getQueryType(), dto.getIdNumber())) { + return MallPictureOutboundResult.fail(400, "图片查询参数不能为空"); + } + log.info("积分商城查询图片信息,supplierId={},queryType={},idNumber={}", dto.getSupplierId(), dto.getQueryType(), dto.getIdNumber()); + Map map = "2".equals(dto.getQueryType()) ? queryInvoiceFiles(dto) : queryProductPictures(dto); + MallPictureOutboundResult result = MallPictureOutboundResult.success(map); + if ("2".equals(dto.getQueryType())) { + result.setFileType(firstInvoiceFileType(dto)); + } + return result; + } + + private Map queryProductPictures(PictureQueryDTO dto) { + Cnd cnd = Cnd.where("orderSubId", "=", dto.getIdNumber()).and("delFlag", "=", false); + if (dto.getFileIdList() != null && !dto.getFileIdList().isEmpty()) { + cnd.and("sku", "in", dto.getFileIdList()); + } + List products = dao.query(PointsMallOrderSubProduct.class, cnd); + Map map = new LinkedHashMap<>(); + for (PointsMallOrderSubProduct product : products) { + map.put(product.getSku(), product.getPicInfo()); + } + return map; + } + + private Map queryInvoiceFiles(PictureQueryDTO dto) { + PointsMallInvoiceMain main = dao.fetch(PointsMallInvoiceMain.class, Cnd.where("settlementId", "=", dto.getIdNumber()).and("delFlag", "=", false)); + if (main == null) { + return Collections.emptyMap(); + } + Cnd cnd = Cnd.where("mainId", "=", main.getId()).and("delFlag", "=", false); + if (dto.getFileIdList() != null && !dto.getFileIdList().isEmpty()) { + cnd.and("invoiceId", "in", dto.getFileIdList()); + } + List invoices = dao.query(PointsMallInvoiceInfo.class, cnd); + Map map = new LinkedHashMap<>(); + for (PointsMallInvoiceInfo invoice : invoices) { + map.put(invoice.getInvoiceId(), invoice.getImageEncode()); + } + return map; + } + + private String firstInvoiceFileType(PictureQueryDTO dto) { + PointsMallInvoiceMain main = dao.fetch(PointsMallInvoiceMain.class, Cnd.where("settlementId", "=", dto.getIdNumber()).and("delFlag", "=", false)); + if (main == null) { + return null; + } + PointsMallInvoiceInfo info = dao.fetch(PointsMallInvoiceInfo.class, Cnd.where("mainId", "=", main.getId()).and("delFlag", "=", false)); + return info == null ? null : info.getFileType(); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallTokenService.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallTokenService.java new file mode 100644 index 00000000..52588599 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallTokenService.java @@ -0,0 +1,96 @@ +package com.budwk.app.zhgh.pointsmall.mallbridge.service; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.crypto.SecureUtil; +import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallTokenDTO; +import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallTokenRefreshDTO; +import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundTokenResult; +import com.budwk.app.zhgh.pointsmall.mallbridge.models.PointsMallSupplierTokenStore; +import com.budwk.app.zhgh.pointsmall.mallbridge.service.support.AbstractMallBridgeSupport; +import com.budwk.app.zhgh.pointsmall.supplier.models.PointsMallSupplier; +import lombok.extern.slf4j.Slf4j; +import org.nutz.dao.Cnd; +import org.nutz.dao.Dao; +import org.nutz.ioc.loader.annotation.IocBean; + +import java.util.Date; +import java.util.Objects; +import java.util.UUID; + +/** + * 商城桥接授权服务。 + */ +@Slf4j +@IocBean(args = {"refer:dao"}) +public class MallTokenService extends AbstractMallBridgeSupport { + + public MallTokenService(Dao dao) { + super(dao); + } + + /** + * 获取访问令牌。 + */ + public MallInboundTokenResult getToken(MallTokenDTO dto) { + log.info("积分商城供应商获取Token开始,supplierId={}", dto == null ? null : dto.getSupplierId()); + PointsMallSupplier supplier = checkedSupplier(dto.getSupplierId(), dto.getClientId(), dto.getClientSecret(), dto.getSendSha256(), dto.getTimestamp()); + invalidateOldToken(supplier.getSupplierId(), supplier.getClientId()); + MallInboundTokenResult result = createToken(supplier); + log.info("积分商城供应商获取Token完成,supplierId={}", supplier.getSupplierId()); + return result; + } + + /** + * 刷新访问令牌。 + */ + public MallInboundTokenResult refreshToken(MallTokenRefreshDTO dto) { + log.info("积分商城供应商刷新Token开始,supplierId={}", dto == null ? null : dto.getSupplierId()); + PointsMallSupplier supplier = checkedSupplier(dto.getSupplierId(), dto.getClientId(), dto.getClientSecret(), dto.getSendSha256(), dto.getTimestamp()); + PointsMallSupplierTokenStore store = dao.fetch(PointsMallSupplierTokenStore.class, Cnd.where("supplierId", "=", supplier.getSupplierId()) + .and("refreshToken", "=", dto.getRefreshToken()).and("isValid", "=", true)); + if (store == null || store.getRefreshExpireTime() == null || new Date().after(store.getRefreshExpireTime())) { + throw new IllegalArgumentException("刷新令牌已失效,请重新获取授权"); + } + invalidateOldToken(supplier.getSupplierId(), supplier.getClientId()); + MallInboundTokenResult result = createToken(supplier); + log.info("积分商城供应商刷新Token完成,supplierId={}", supplier.getSupplierId()); + return result; + } + + private PointsMallSupplier checkedSupplier(String supplierId, String clientId, String clientSecret, String sendSha256, String timestamp) { + if (StrUtil.hasBlank(supplierId, clientId, clientSecret, sendSha256, timestamp)) { + throw new IllegalArgumentException("供应商授权信息不能为空"); + } + PointsMallSupplier supplier = dao.fetch(PointsMallSupplier.class, Cnd.where("supplierId", "=", supplierId) + .and("clientId", "=", clientId).and("delFlag", "=", false)); + if (supplier == null) { + throw new IllegalArgumentException("供应商不存在或已停用"); + } + String serverSha256 = SecureUtil.sha256(supplier.getClientId() + supplier.getClientSecret() + timestamp); + if (!Objects.equals(sendSha256, serverSha256)) { + throw new IllegalArgumentException("签名验证信息错误"); + } + return supplier; + } + + private MallInboundTokenResult createToken(PointsMallSupplier supplier) { + String accessToken = UUID.randomUUID().toString().replace("-", ""); + String refreshToken = UUID.randomUUID().toString().replace("-", ""); + Date now = new Date(); + PointsMallSupplierTokenStore store = new PointsMallSupplierTokenStore(); + store.setSupplierId(supplier.getSupplierId()); + store.setClientId(supplier.getClientId()); + store.setAccessToken(accessToken); + store.setRefreshToken(refreshToken); + store.setAccessExpireTime(DateUtil.offsetSecond(now, (int) ACCESS_EXPIRE_SECONDS)); + store.setRefreshExpireTime(DateUtil.offsetSecond(now, (int) REFRESH_EXPIRE_SECONDS)); + store.setIsValid(true); + dao.insert(store); + return MallInboundTokenResult.success(DateUtil.format(now, "yyyyMMddHHmmss"), accessToken, refreshToken, ACCESS_EXPIRE_SECONDS, REFRESH_EXPIRE_SECONDS); + } + + private void invalidateOldToken(String supplierId, String clientId) { + dao.clear(PointsMallSupplierTokenStore.class, Cnd.where("supplierId", "=", supplierId).and("clientId", "=", clientId)); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallUnReconciledMsgNoticeService.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallUnReconciledMsgNoticeService.java new file mode 100644 index 00000000..7bee8b24 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/MallUnReconciledMsgNoticeService.java @@ -0,0 +1,53 @@ +package com.budwk.app.zhgh.pointsmall.mallbridge.service; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.StrUtil; +import com.budwk.app.zhgh.pointsmall.mallbridge.dto.UnReconciledMsgNoticeDTO; +import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundResult; +import com.budwk.app.zhgh.pointsmall.mallbridge.service.support.AbstractMallBridgeSupport; +import com.budwk.app.zhgh.pointsmall.reconciliation.models.PointsMallReconciliationRecord; +import lombok.extern.slf4j.Slf4j; +import org.nutz.dao.Dao; +import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.trans.Atom; +import org.nutz.trans.Trans; + +import java.util.List; + +/** + * 商城桥接对账异常通知服务。 + */ +@Slf4j +@IocBean(args = {"refer:dao"}) +public class MallUnReconciledMsgNoticeService extends AbstractMallBridgeSupport { + + public MallUnReconciledMsgNoticeService(Dao dao) { + super(dao); + } + + /** + * 接收未对平订单通知并保存异常记录。 + */ + public MallInboundResult unReconciledMsg(UnReconciledMsgNoticeDTO dto) { + if (dto == null || StrUtil.hasBlank(dto.getSupplierId(), dto.getOrderIds())) { + return MallInboundResult.fail(400, "对账异常通知参数不能为空"); + } + List orderIds = splitIds(dto.getOrderIds()); + log.info("积分商城对账异常通知开始,supplierId={},orderCount={}", dto.getSupplierId(), orderIds.size()); + Trans.exec((Atom) () -> { + for (String orderId : orderIds) { + PointsMallReconciliationRecord record = new PointsMallReconciliationRecord(); + record.setSupplierId(dto.getSupplierId()); + record.setOrderId(orderId); + record.setDiffReason(dto.getUnReconciledMsg()); + record.setDiffType(Convert.toInt(dto.getUnReconciledType(), null)); + record.setBatchNo(StrUtil.blankToDefault(dto.getUniqueSeqNo(), dto.getStartDate() + "_" + dto.getEndDate())); + record.setNoticeStatus(1); + record.setPushTime(1); + dao.insert(record); + } + }); + log.info("积分商城对账异常通知完成,supplierId={},orderCount={}", dto.getSupplierId(), orderIds.size()); + return MallInboundResult.success(); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/impl/PointsMallBridgeServiceImpl.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/impl/PointsMallBridgeServiceImpl.java index e689328c..deb5eb11 100644 --- a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/impl/PointsMallBridgeServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/impl/PointsMallBridgeServiceImpl.java @@ -1,15 +1,6 @@ package com.budwk.app.zhgh.pointsmall.mallbridge.service.impl; -import cn.hutool.core.convert.Convert; -import cn.hutool.core.date.DateUtil; -import cn.hutool.core.util.StrUtil; -import cn.hutool.crypto.SecureUtil; -import cn.hutool.json.JSONUtil; -import com.budwk.app.zhgh.pointsmall.invoice.models.PointsMallInvoiceInfo; -import com.budwk.app.zhgh.pointsmall.invoice.models.PointsMallInvoiceMain; -import com.budwk.app.zhgh.pointsmall.mallbridge.dto.FinalStatementDTO; import com.budwk.app.zhgh.pointsmall.mallbridge.dto.GetInvoiceInfoDTO; -import com.budwk.app.zhgh.pointsmall.mallbridge.dto.InvoiceInfoDTO; import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallInvoiceApplyDTO; import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallOrderEventDTO; import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallOrderQueryDTO; @@ -19,9 +10,7 @@ import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallPurchaseConfirmDTO; import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallPurchaseResultDTO; import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallTokenDTO; import com.budwk.app.zhgh.pointsmall.mallbridge.dto.MallTokenRefreshDTO; -import com.budwk.app.zhgh.pointsmall.mallbridge.dto.OrderInfoDTO; import com.budwk.app.zhgh.pointsmall.mallbridge.dto.OrderPayFinishNoticeDTO; -import com.budwk.app.zhgh.pointsmall.mallbridge.dto.OrderProInfoDTO; import com.budwk.app.zhgh.pointsmall.mallbridge.dto.PictureQueryDTO; import com.budwk.app.zhgh.pointsmall.mallbridge.dto.QueryInvoiceInfoDTO; import com.budwk.app.zhgh.pointsmall.mallbridge.dto.UnReconciledMsgNoticeDTO; @@ -30,661 +19,117 @@ import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundResult; import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallInboundTokenResult; import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallOutboundResult; import com.budwk.app.zhgh.pointsmall.mallbridge.model.MallPictureOutboundResult; -import com.budwk.app.zhgh.pointsmall.mallbridge.models.PointsMallSupplierTokenStore; +import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallInboundConfirmPurchaseService; +import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallInboundEventService; +import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallInboundPendingOrderService; +import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallInboundPointsQueryService; +import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallInboundPurchaseResultService; +import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallInvoiceApplyOutboundService; +import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallInvoiceInfoQueryService; +import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallOrderQueryOutboundService; +import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallPayFinishNoticeService; +import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallPictureApplyOutboundService; +import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallTokenService; +import com.budwk.app.zhgh.pointsmall.mallbridge.service.MallUnReconciledMsgNoticeService; import com.budwk.app.zhgh.pointsmall.mallbridge.service.PointsMallBridgeService; -import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderMain; -import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderSub; -import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderSubProduct; -import com.budwk.app.zhgh.pointsmall.points.service.PointsMallUserPointsService; -import com.budwk.app.zhgh.pointsmall.reconciliation.models.PointsMallReconciliationRecord; -import com.budwk.app.zhgh.pointsmall.supplier.models.PointsMallSupplier; -import org.nutz.dao.Chain; -import org.nutz.dao.Cnd; -import org.nutz.dao.Dao; -import org.nutz.dao.Sqls; -import org.nutz.dao.entity.Record; -import org.nutz.dao.sql.Sql; import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; -import org.nutz.trans.Atom; -import org.nutz.trans.Trans; -import java.math.BigDecimal; -import java.time.LocalDate; -import java.time.ZoneId; -import java.util.Arrays; -import java.util.Collections; -import java.util.Date; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.UUID; -import java.util.stream.Collectors; - -@IocBean(args = {"refer:dao"}) +/** + * 积分商城桥接门面服务。 + * + *

Controller 只依赖该门面,具体业务拆分到各个专职服务,避免桥接接口逻辑堆在一个实现类里。

+ */ +@IocBean public class PointsMallBridgeServiceImpl implements PointsMallBridgeService { - private static final long ACCESS_EXPIRE_SECONDS = 3600; - private static final long REFRESH_EXPIRE_SECONDS = 86400L * 7; - - private static final int ORDER_PENDING_PAY = 0; - private static final int ORDER_FINISH_PAY = 1; - private static final int ORDER_IN_PROGRESS = 2; - private static final int ORDER_CANCELLED = 3; - private static final int ORDER_RECEIVED = 4; - private static final int ORDER_COMPLETED = 5; - private static final int ORDER_EXCHANGE = 6; - private static final int ORDER_REFUNDED = 7; - private static final int ORDER_ABOLISH = 8; - - private final Dao dao; @Inject - private PointsMallUserPointsService pointsMallUserPointsService; - - public PointsMallBridgeServiceImpl(Dao dao) { - this.dao = dao; - } + private MallTokenService mallTokenService; + @Inject + private MallInboundPointsQueryService mallInboundPointsQueryService; + @Inject + private MallInboundPendingOrderService mallInboundPendingOrderService; + @Inject + private MallInboundConfirmPurchaseService mallInboundConfirmPurchaseService; + @Inject + private MallInboundPurchaseResultService mallInboundPurchaseResultService; + @Inject + private MallInboundEventService mallInboundEventService; + @Inject + private MallOrderQueryOutboundService mallOrderQueryOutboundService; + @Inject + private MallInvoiceApplyOutboundService mallInvoiceApplyOutboundService; + @Inject + private MallInvoiceInfoQueryService mallInvoiceInfoQueryService; + @Inject + private MallPictureApplyOutboundService mallPictureApplyOutboundService; + @Inject + private MallUnReconciledMsgNoticeService mallUnReconciledMsgNoticeService; + @Inject + private MallPayFinishNoticeService mallPayFinishNoticeService; @Override public MallInboundTokenResult getToken(MallTokenDTO dto) { - PointsMallSupplier supplier = checkedSupplier(dto.getSupplierId(), dto.getClientId(), dto.getClientSecret(), dto.getSendSha256(), dto.getTimestamp()); - invalidateOldToken(supplier.getSupplierId(), supplier.getClientId()); - return createToken(supplier); + return mallTokenService.getToken(dto); } @Override public MallInboundTokenResult refreshToken(MallTokenRefreshDTO dto) { - PointsMallSupplier supplier = checkedSupplier(dto.getSupplierId(), dto.getClientId(), dto.getClientSecret(), dto.getSendSha256(), dto.getTimestamp()); - PointsMallSupplierTokenStore store = dao.fetch(PointsMallSupplierTokenStore.class, Cnd.where("supplierId", "=", supplier.getSupplierId()) - .and("refreshToken", "=", dto.getRefreshToken()).and("isValid", "=", true)); - if (store == null || store.getRefreshExpireTime() == null || new Date().after(store.getRefreshExpireTime())) { - throw new IllegalArgumentException("刷新令牌已失效,请重新获取授权"); - } - invalidateOldToken(supplier.getSupplierId(), supplier.getClientId()); - return createToken(supplier); + return mallTokenService.refreshToken(dto); } @Override public MallInboundPointsResult queryPoints(MallPointQueryDTO dto) { - return MallInboundPointsResult.success(pointsMallUserPointsService.availablePoints(dto.getUserId())); + return mallInboundPointsQueryService.queryPoints(dto); } @Override public MallInboundResult receivePending(MallPendingOrderDTO dto) { - if (dto.getOrderInfo() == null || StrUtil.isBlank(dto.getOrderInfo().getOrderId())) { - return MallInboundResult.fail(400, "订单信息不能为空"); - } - if (dao.count(PointsMallOrderMain.class, Cnd.where("orderId", "=", dto.getOrderInfo().getOrderId()) - .and("supplierId", "=", dto.getSupplierId()).and("delFlag", "=", false)) > 0) { - return MallInboundResult.fail(-1, "订单已存在"); - } - Trans.exec((Atom) () -> dao.insert(toMain(dto.getSupplierId(), dto.getUserId(), dto.getOrderInfo(), ORDER_PENDING_PAY))); - return MallInboundResult.success(); + return mallInboundPendingOrderService.receivePending(dto); } @Override public MallInboundResult confirmPurchase(MallPurchaseConfirmDTO dto) { - return updateMainOrderPay(dto.getSupplierId(), dto.getOrderId(), 1, dto.getTotalPrice(), dto.getWPayOrAPay(), dto.getPointsPrice()); + return mallInboundConfirmPurchaseService.confirmPurchase(dto); } @Override public MallInboundResult purchaseResult(MallPurchaseResultDTO dto) { - int cashPaid = Boolean.TRUE.equals(dto.getPurchaseResult()) ? 1 : 0; - return updateMainOrderPay(dto.getSupplierId(), dto.getOrderId(), cashPaid, dto.getTotalPrice(), dto.getWPayOrAPay(), dto.getPointsPrice()); + return mallInboundPurchaseResultService.purchaseResult(dto); } @Override public MallInboundResult receiveEvent(MallOrderEventDTO dto) { - Integer type = dto.getOrderType(); - if (type == null) { - return MallInboundResult.fail(400, "订单事件类型不能为空"); - } - if (type == 1) { - return cancelMainOrder(dto, false); - } - if (type == 2) { - return splitSubOrder(dto); - } - if (type == 3) { - return updateSubState(dto, ORDER_CANCELLED); - } - if (type == 4) { - return updateSubState(dto, ORDER_REFUNDED); - } - if (type == 5) { - return updateSubState(dto, ORDER_EXCHANGE); - } - if (type == 6) { - return updateSubState(dto, ORDER_COMPLETED); - } - if (type == 7) { - return cancelMainOrder(dto, true); - } - if (type == 8) { - return updateSubState(dto, ORDER_ABOLISH); - } - if (type == 11) { - return markInvoiceNotice(dto); - } - return MallInboundResult.success(); + return mallInboundEventService.processEvent(dto); } @Override public MallOutboundResult queryOrderInfo(MallOrderQueryDTO dto) { - String table = Integer.valueOf(2).equals(dto.getOrderType()) ? "points_mall_order_sub" : "points_mall_order_main"; - Sql sql = Sqls.create("select * from " + table + " where supplierId=@supplierId and orderId=@orderId and delFlag=0"); - sql.params().set("supplierId", dto.getSupplierId()); - sql.params().set("orderId", dto.getOrderId()); - sql.setCallback(Sqls.callback.records()); - dao.execute(sql); - List list = sql.getList(Record.class).stream().map(this::toOrderInfo).collect(Collectors.toList()); - return MallOutboundResult.success(list); + return mallOrderQueryOutboundService.queryOrderInfo(dto); } @Override public MallOutboundResult applyInvoice(MallInvoiceApplyDTO dto) { - if (dto == null || StrUtil.hasBlank(dto.getSupplierId(), dto.getSettlementId(), dto.getOrderIds())) { - return MallOutboundResult.fail(400, "开票申请参数不能为空"); - } - List orderIds = splitIds(dto.getOrderIds()); - List orders = dao.query(PointsMallOrderSub.class, Cnd.where("supplierId", "=", dto.getSupplierId()) - .and("orderId", "in", orderIds).and("delFlag", "=", false)); - if (orders.isEmpty()) { - return MallOutboundResult.fail(404, "未找到可开票订单"); - } - if (dto.getOrderNum() != null && dto.getOrderNum() != orders.size()) { - return MallOutboundResult.fail(400, "订单数量与开票申请不一致"); - } - BigDecimal total = orders.stream().map(PointsMallOrderSub::getTotalPrice).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add); - if (dto.getOrderTotalPrice() != null && total.compareTo(dto.getOrderTotalPrice()) != 0) { - return MallOutboundResult.fail(400, "订单金额与开票申请不一致"); - } - Trans.exec((Atom) () -> saveInvoiceApply(dto, orderIds, total)); - return MallOutboundResult.success(); + return mallInvoiceApplyOutboundService.applyInvoice(dto); } @Override public GetInvoiceInfoDTO queryInvoicesInfo(QueryInvoiceInfoDTO dto) { - if (dto == null || StrUtil.hasBlank(dto.getSupplierId(), dto.getSettlementId())) { - throw new IllegalArgumentException("发票查询参数不能为空"); - } - PointsMallInvoiceMain main = dao.fetch(PointsMallInvoiceMain.class, Cnd.where("settlementId", "=", dto.getSettlementId()) - .and("delFlag", "=", false)); - if (main == null) { - throw new IllegalArgumentException("发票信息不存在"); - } - List invoiceInfos = dao.query(PointsMallInvoiceInfo.class, Cnd.where("mainId", "=", main.getId()).and("delFlag", "=", false)) - .stream().map(this::toInvoiceInfoDTO).collect(Collectors.toList()); - GetInvoiceInfoDTO result = new GetInvoiceInfoDTO(); - result.setBSuccess(main.getBSuccess()); - result.setSettlementId(main.getSettlementId()); - result.setSucOrderIds(main.getSucOrderIds()); - result.setFailOrderIds(main.getFailOrderIds()); - result.setFailMsg(main.getFailMsg()); - result.setInvoiceInfos(invoiceInfos); - return result; + return mallInvoiceInfoQueryService.queryInvoicesInfo(dto); } @Override public MallPictureOutboundResult queryFileBase64Info(PictureQueryDTO dto) { - if (dto == null || StrUtil.hasBlank(dto.getSupplierId(), dto.getQueryType(), dto.getIdNumber())) { - return MallPictureOutboundResult.fail(400, "图片查询参数不能为空"); - } - Map map = "2".equals(dto.getQueryType()) ? queryInvoiceFiles(dto) : queryProductPictures(dto); - MallPictureOutboundResult result = MallPictureOutboundResult.success(map); - if ("2".equals(dto.getQueryType())) { - result.setFileType(firstInvoiceFileType(dto)); - } - return result; + return mallPictureApplyOutboundService.queryFileBase64Info(dto); } @Override public MallInboundResult unReconciledMsg(UnReconciledMsgNoticeDTO dto) { - if (dto == null || StrUtil.hasBlank(dto.getSupplierId(), dto.getOrderIds())) { - return MallInboundResult.fail(400, "对账异常通知参数不能为空"); - } - List orderIds = splitIds(dto.getOrderIds()); - Trans.exec((Atom) () -> { - for (String orderId : orderIds) { - PointsMallReconciliationRecord record = new PointsMallReconciliationRecord(); - record.setSupplierId(dto.getSupplierId()); - record.setOrderId(orderId); - record.setDiffReason(dto.getUnReconciledMsg()); - record.setDiffType(Convert.toInt(dto.getUnReconciledType(), null)); - record.setBatchNo(StrUtil.blankToDefault(dto.getUniqueSeqNo(), dto.getStartDate() + "_" + dto.getEndDate())); - record.setNoticeStatus(1); - record.setPushTime(1); - dao.insert(record); - } - }); - return MallInboundResult.success(); + return mallUnReconciledMsgNoticeService.unReconciledMsg(dto); } @Override public MallInboundResult payCompletion(OrderPayFinishNoticeDTO dto) { - if (dto == null || StrUtil.isBlank(dto.getSupplierId()) || dto.getExpFinalInfos() == null) { - return MallInboundResult.fail(400, "支付完成通知参数不能为空"); - } - Trans.exec((Atom) () -> { - for (FinalStatementDTO item : dto.getExpFinalInfos()) { - if (item == null || StrUtil.isBlank(item.getSettlementId())) { - continue; - } - PointsMallInvoiceMain main = dao.fetch(PointsMallInvoiceMain.class, Cnd.where("settlementId", "=", item.getSettlementId()) - .and("delFlag", "=", false)); - if (main != null) { - main.setBSuccess(1); - dao.updateIgnoreNull(main); - } - List orderIds = splitIds(item.getOrderIds()); - if (!orderIds.isEmpty()) { - dao.update(PointsMallOrderSub.class, Chain.make("invoiceStatus", 1), Cnd.where("supplierId", "=", dto.getSupplierId()).and("orderId", "in", orderIds)); - } - } - }); - return MallInboundResult.success(); - } - - private PointsMallSupplier checkedSupplier(String supplierId, String clientId, String clientSecret, String sendSha256, String timestamp) { - if (StrUtil.hasBlank(supplierId, clientId, clientSecret, sendSha256, timestamp)) { - throw new IllegalArgumentException("供应商授权信息不能为空"); - } - PointsMallSupplier supplier = dao.fetch(PointsMallSupplier.class, Cnd.where("supplierId", "=", supplierId) - .and("clientId", "=", clientId).and("delFlag", "=", false)); - if (supplier == null) { - throw new IllegalArgumentException("供应商不存在或已停用"); - } - String serverSha256 = SecureUtil.sha256(supplier.getClientId() + supplier.getClientSecret() + timestamp); - if (!Objects.equals(sendSha256, serverSha256)) { - throw new IllegalArgumentException("签名验证信息错误"); - } - return supplier; - } - - private MallInboundTokenResult createToken(PointsMallSupplier supplier) { - String accessToken = UUID.randomUUID().toString().replace("-", ""); - String refreshToken = UUID.randomUUID().toString().replace("-", ""); - Date now = new Date(); - PointsMallSupplierTokenStore store = new PointsMallSupplierTokenStore(); - store.setSupplierId(supplier.getSupplierId()); - store.setClientId(supplier.getClientId()); - store.setAccessToken(accessToken); - store.setRefreshToken(refreshToken); - store.setAccessExpireTime(DateUtil.offsetSecond(now, (int) ACCESS_EXPIRE_SECONDS)); - store.setRefreshExpireTime(DateUtil.offsetSecond(now, (int) REFRESH_EXPIRE_SECONDS)); - store.setIsValid(true); - dao.insert(store); - return MallInboundTokenResult.success(DateUtil.format(now, "yyyyMMddHHmmss"), accessToken, refreshToken, ACCESS_EXPIRE_SECONDS, REFRESH_EXPIRE_SECONDS); - } - - private void invalidateOldToken(String supplierId, String clientId) { - dao.clear(PointsMallSupplierTokenStore.class, Cnd.where("supplierId", "=", supplierId).and("clientId", "=", clientId)); - } - - private MallInboundResult updateMainOrderPay(String supplierId, String orderId, Integer cashPaid, BigDecimal totalPrice, BigDecimal cash, BigDecimal points) { - PointsMallOrderMain main = dao.fetch(PointsMallOrderMain.class, Cnd.where("supplierId", "=", supplierId) - .and("orderId", "=", orderId).and("delFlag", "=", false)); - if (main == null) { - return MallInboundResult.fail(404, "订单不存在"); - } - main.setCashPaid(cashPaid); - main.setTotalPrice(nvl(totalPrice, main.getTotalPrice())); - main.setWPayOrAPay(nvl(cash, main.getWPayOrAPay())); - main.setPointsPrice(nvl(points, main.getPointsPrice())); - main.setOrderState(cashPaid == 1 ? ORDER_FINISH_PAY : main.getOrderState()); - main.setOrderUpdateTime(new Date()); - dao.updateIgnoreNull(main); - return MallInboundResult.success(); - } - - private MallInboundResult cancelMainOrder(MallOrderEventDTO dto, boolean paidNotSplit) { - String mainOrderId = StrUtil.blankToDefault(dto.getMainOrderId(), dto.getOrderId()); - if (StrUtil.isBlank(mainOrderId)) { - return MallInboundResult.fail(400, "主订单信息不能为空"); - } - PointsMallOrderMain main = dao.fetch(PointsMallOrderMain.class, Cnd.where("supplierId", "=", dto.getSupplierId()) - .and("orderId", "=", mainOrderId).and("delFlag", "=", false)); - if (main == null) { - return MallInboundResult.fail(404, "主订单不存在"); - } - if (!paidNotSplit && !Integer.valueOf(ORDER_PENDING_PAY).equals(main.getOrderState())) { - return MallInboundResult.fail(400, "只能取消未支付主单"); - } - main.setOrderState(ORDER_CANCELLED); - main.setOrderUpdateTime(new Date()); - dao.updateIgnoreNull(main); - dao.update(PointsMallOrderSub.class, Chain.make("orderState", ORDER_CANCELLED).add("orderUpdateTime", new Date()), - Cnd.where("supplierId", "=", dto.getSupplierId()).and("mainOrderId", "=", mainOrderId).and("delFlag", "=", false)); - return MallInboundResult.success(); - } - - private MallInboundResult splitSubOrder(MallOrderEventDTO dto) { - PointsMallOrderMain main = dao.fetch(PointsMallOrderMain.class, Cnd.where("supplierId", "=", dto.getSupplierId()) - .and("orderId", "=", dto.getMainOrderId()).and("delFlag", "=", false)); - if (main == null) { - return MallInboundResult.fail(404, "主订单不存在"); - } - List subOrderIds = splitIds(dto.getOrderId()); - if (subOrderIds.isEmpty()) { - return MallInboundResult.fail(400, "子订单信息不能为空"); - } - List products = StrUtil.isBlank(main.getExtJson()) || "null".equalsIgnoreCase(main.getExtJson()) - ? Collections.emptyList() - : JSONUtil.toList(main.getExtJson(), OrderProInfoDTO.class); - Trans.exec((Atom) () -> { - for (String subOrderId : subOrderIds) { - PointsMallOrderSub sub = dao.fetch(PointsMallOrderSub.class, Cnd.where("orderId", "=", subOrderId) - .and("supplierId", "=", dto.getSupplierId()).and("delFlag", "=", false)); - if (sub == null) { - sub = toSub(main, subOrderId); - sub.setOrderState(ORDER_IN_PROGRESS); - dao.insert(sub); - insertProducts(subOrderId, products); - } else { - sub.setOrderState(ORDER_IN_PROGRESS); - sub.setOrderUpdateTime(new Date()); - dao.updateIgnoreNull(sub); - } - } - }); - return MallInboundResult.success(); - } - - private MallInboundResult updateSubState(MallOrderEventDTO dto, int state) { - PointsMallOrderSub sub = dao.fetch(PointsMallOrderSub.class, Cnd.where("supplierId", "=", dto.getSupplierId()) - .and("orderId", "=", dto.getOrderId()).and("delFlag", "=", false)); - if (sub == null) { - return MallInboundResult.fail(404, "订单不存在"); - } - sub.setOrderState(state); - sub.setOrderUpdateTime(new Date()); - if (state == ORDER_RECEIVED) { - sub.setOrderFinishTime(new Date()); - } - if (state == ORDER_COMPLETED) { - sub.setOrderCompleteTime(new Date()); - } - dao.updateIgnoreNull(sub); - return MallInboundResult.success(); - } - - private MallInboundResult markInvoiceNotice(MallOrderEventDTO dto) { - String mainOrderId = StrUtil.blankToDefault(dto.getMainOrderId(), dto.getOrderId()); - if (StrUtil.isBlank(mainOrderId)) { - return MallInboundResult.fail(400, "主订单信息不能为空"); - } - dao.update(PointsMallOrderSub.class, Chain.make("invoiceStatus", 1), Cnd.where("supplierId", "=", dto.getSupplierId()) - .and("mainOrderId", "=", mainOrderId).and("delFlag", "=", false)); - return MallInboundResult.success(); - } - - private void saveInvoiceApply(MallInvoiceApplyDTO dto, List orderIds, BigDecimal total) { - PointsMallInvoiceMain main = dao.fetch(PointsMallInvoiceMain.class, Cnd.where("settlementId", "=", dto.getSettlementId()).and("delFlag", "=", false)); - if (main == null) { - main = new PointsMallInvoiceMain(); - main.setSettlementId(dto.getSettlementId()); - main.setBSuccess(0); - main.setSucOrderIds(String.join(",", orderIds)); - main.setFailOrderIds(""); - main.setFailMsg(""); - dao.insert(main); - } else { - main.setBSuccess(0); - main.setSucOrderIds(String.join(",", orderIds)); - main.setFailOrderIds(""); - main.setFailMsg(""); - dao.updateIgnoreNull(main); - } - - PointsMallInvoiceInfo info = new PointsMallInvoiceInfo(); - info.setMainId(main.getId()); - info.setInvoiceId(dto.getSettlementId()); - info.setInvoiceCode(dto.getInvoiceCode()); - info.setInvoiceDate(parseDate(dto.getInvoiceDate())); - info.setInvoiceNakeAmount(nvl(dto.getInvoiceNakedPrice(), total)); - info.setInvoiceTaxAmount(nvl(dto.getInvoiceTaxPrice(), BigDecimal.ZERO)); - info.setInvoiceAmount(total); - info.setInvoiceType(Convert.toInt(dto.getInvoiceType(), null)); - info.setInvoiceAddress(dto.getInvoiceAddress()); - info.setInvoiceContact(dto.getInvoiceContact()); - info.setInvoiceContent(dto.getInvoiceContent()); - info.setInvoiceBank(dto.getBankName()); - info.setInvoiceAccount(dto.getBankAccount()); - info.setTaxIdNumber(dto.getInvoiceCode()); - info.setRemark(dto.getRemark()); - dao.insert(info); - - dao.update(PointsMallOrderSub.class, Chain.make("invoiceStatus", 2).add("invoiceCode", dto.getSettlementId()), - Cnd.where("supplierId", "=", dto.getSupplierId()).and("orderId", "in", orderIds)); - } - - private Map queryProductPictures(PictureQueryDTO dto) { - Cnd cnd = Cnd.where("orderSubId", "=", dto.getIdNumber()).and("delFlag", "=", false); - if (dto.getFileIdList() != null && !dto.getFileIdList().isEmpty()) { - cnd.and("sku", "in", dto.getFileIdList()); - } - List products = dao.query(PointsMallOrderSubProduct.class, cnd); - Map map = new LinkedHashMap<>(); - for (PointsMallOrderSubProduct product : products) { - map.put(product.getSku(), product.getPicInfo()); - } - return map; - } - - private Map queryInvoiceFiles(PictureQueryDTO dto) { - PointsMallInvoiceMain main = dao.fetch(PointsMallInvoiceMain.class, Cnd.where("settlementId", "=", dto.getIdNumber()).and("delFlag", "=", false)); - if (main == null) { - return Collections.emptyMap(); - } - Cnd cnd = Cnd.where("mainId", "=", main.getId()).and("delFlag", "=", false); - if (dto.getFileIdList() != null && !dto.getFileIdList().isEmpty()) { - cnd.and("invoiceId", "in", dto.getFileIdList()); - } - List invoices = dao.query(PointsMallInvoiceInfo.class, cnd); - Map map = new LinkedHashMap<>(); - for (PointsMallInvoiceInfo invoice : invoices) { - map.put(invoice.getInvoiceId(), invoice.getImageEncode()); - } - return map; - } - - private String firstInvoiceFileType(PictureQueryDTO dto) { - PointsMallInvoiceMain main = dao.fetch(PointsMallInvoiceMain.class, Cnd.where("settlementId", "=", dto.getIdNumber()).and("delFlag", "=", false)); - if (main == null) { - return null; - } - PointsMallInvoiceInfo info = dao.fetch(PointsMallInvoiceInfo.class, Cnd.where("mainId", "=", main.getId()).and("delFlag", "=", false)); - return info == null ? null : info.getFileType(); - } - - private PointsMallOrderMain toMain(String supplierId, String userId, OrderInfoDTO orderInfo, Integer state) { - PointsMallOrderMain main = new PointsMallOrderMain(); - main.setOrderId(orderInfo.getOrderId()); - main.setSupplierId(supplierId); - main.setOrderNumberId(orderInfo.getOrderNumberId()); - main.setUserId(userId); - main.setAplName(orderInfo.getAplName()); - main.setOrderCreateTime(parseDate(orderInfo.getCreateTime())); - main.setOrderUpdateTime(parseDate(orderInfo.getUpdateTime())); - main.setDeliveryTime(parseDate(orderInfo.getDeliveryTime())); - main.setOrderFinishTime(parseDate(orderInfo.getOrderFinishTime())); - main.setOrderCompleteTime(parseDate(orderInfo.getCompleteTime())); - main.setDeliveryStatus(orderInfo.getDeliveryStatus()); - main.setFreightCost(nvl(orderInfo.getFreightCost())); - main.setRefund(nvl(orderInfo.getRefund())); - main.setLogisticsOrderId(orderInfo.getLogisticsOrderId()); - main.setCrrgBsnNm(orderInfo.getCrrgBsnNm()); - main.setOrderState(state); - main.setTotalPrice(nvl(orderInfo.getTotalPrice())); - main.setWPayOrAPay(nvl(orderInfo.getWPayOrAPay())); - main.setPointsPrice(nvl(orderInfo.getPointsPrice())); - main.setInvoiceCode(orderInfo.getInvoiceCode()); - main.setExtJson(JSONUtil.toJsonStr(orderInfo.getOrderProInfos() == null ? Collections.emptyList() : orderInfo.getOrderProInfos())); - main.setCashPaid(0); - return main; - } - - private PointsMallOrderSub toSub(PointsMallOrderMain main, String subOrderId) { - PointsMallOrderSub sub = new PointsMallOrderSub(); - sub.setOrderId(StrUtil.blankToDefault(subOrderId, main.getOrderId())); - sub.setOrderNumberId(main.getOrderNumberId()); - sub.setMainOrderId(main.getOrderId()); - sub.setSupplierId(main.getSupplierId()); - sub.setUserId(main.getUserId()); - sub.setAplName(main.getAplName()); - sub.setOrderCreateTime(main.getOrderCreateTime()); - sub.setOrderUpdateTime(new Date()); - sub.setDeliveryTime(main.getDeliveryTime()); - sub.setOrderFinishTime(main.getOrderFinishTime()); - sub.setOrderCompleteTime(main.getOrderCompleteTime()); - sub.setDeliveryStatus(parseInteger(main.getDeliveryStatus())); - sub.setFreightCost(main.getFreightCost()); - sub.setRefund(main.getRefund()); - sub.setLogisticsOrderId(main.getLogisticsOrderId()); - sub.setCrrgBsnNm(main.getCrrgBsnNm()); - sub.setOrderState(main.getOrderState()); - sub.setTotalPrice(main.getTotalPrice()); - sub.setWPayOrAPay(main.getWPayOrAPay()); - sub.setPointsPrice(main.getPointsPrice()); - sub.setInvoiceCode(main.getInvoiceCode()); - sub.setExtJson(main.getExtJson()); - sub.setReconciliationStatus(0); - sub.setInvoiceStatus(0); - return sub; - } - - private void insertProducts(String orderSubId, List products) { - if (products == null) { - return; - } - for (OrderProInfoDTO item : products) { - PointsMallOrderSubProduct product = new PointsMallOrderSubProduct(); - product.setOrderSubId(orderSubId); - product.setSku(item.getSku()); - product.setNumber(item.getNumber()); - product.setPrice(item.getPrice()); - product.setName(item.getName()); - product.setTaxRate(item.getTaxRate()); - product.setPicInfo(item.getPicInfo() == null || item.getPicInfo().isEmpty() ? null : item.getPicInfo().get(0)); - dao.insert(product); - } - } - - private OrderInfoDTO toOrderInfo(Record record) { - OrderInfoDTO dto = new OrderInfoDTO(); - dto.setOrderId(record.getString("orderId")); - dto.setOrderNumberId(record.getString("orderNumberId")); - dto.setSupplierId(record.getString("supplierId")); - dto.setAplName(record.getString("aplName")); - dto.setCreateTime(formatDate(record.get("orderCreateTime"))); - dto.setUpdateTime(formatDate(record.get("orderUpdateTime"))); - dto.setDeliveryTime(formatDate(record.get("deliveryTime"))); - dto.setOrderFinishTime(formatDate(record.get("orderFinishTime"))); - dto.setCompleteTime(formatDate(record.get("orderCompleteTime"))); - dto.setDeliveryStatus(record.getString("deliveryStatus")); - dto.setFreightCost(decimal(record.get("freightCost"))); - dto.setWPayOrAPayFreightCost(decimal(record.get("wPayOrAPayFreightCost"))); - dto.setRefund(decimal(record.get("refund"))); - dto.setWPayOrAPayRefund(decimal(record.get("wPayOrAPayRefund"))); - dto.setLogisticsOrderId(record.getString("logisticsOrderId")); - dto.setCrrgBsnNm(record.getString("crrgBsnNm")); - dto.setOrderState(record.getInt("orderState")); - dto.setTotalPrice(decimal(record.get("totalPrice"))); - dto.setWPayOrAPay(decimal(record.get("wPayOrAPay"))); - dto.setPointsPrice(decimal(record.get("pointsPrice"))); - dto.setInvoiceCode(record.getString("invoiceCode")); - dto.setOrderProInfos(readProducts(dto.getOrderId(), record.getString("extJson"))); - return dto; - } - - private List readProducts(String orderId, String extJson) { - List products = dao.query(PointsMallOrderSubProduct.class, Cnd.where("orderSubId", "=", orderId).and("delFlag", "=", false)); - if (!products.isEmpty()) { - return products.stream().map(this::toProductDTO).collect(Collectors.toList()); - } - if (StrUtil.isBlank(extJson) || "null".equalsIgnoreCase(extJson)) { - return Collections.emptyList(); - } - return JSONUtil.toList(extJson, OrderProInfoDTO.class); - } - - private OrderProInfoDTO toProductDTO(PointsMallOrderSubProduct product) { - OrderProInfoDTO dto = new OrderProInfoDTO(); - dto.setSku(product.getSku()); - dto.setNumber(product.getNumber()); - dto.setPrice(product.getPrice()); - dto.setName(product.getName()); - dto.setTaxRate(product.getTaxRate()); - dto.setPicInfo(StrUtil.isBlank(product.getPicInfo()) ? Collections.emptyList() : Collections.singletonList(product.getPicInfo())); - dto.setFileId(product.getSku()); - dto.setFilePath(product.getPicInfo()); - return dto; - } - - private InvoiceInfoDTO toInvoiceInfoDTO(PointsMallInvoiceInfo info) { - InvoiceInfoDTO dto = new InvoiceInfoDTO(); - dto.setInvoiceId(info.getInvoiceId()); - dto.setInvoiceCode(info.getInvoiceCode()); - dto.setInvoiceDate(toLocalDate(info.getInvoiceDate())); - dto.setInvoiceNakeAmount(info.getInvoiceNakeAmount()); - dto.setInvoiceTaxRate(info.getInvoiceTaxRate()); - dto.setInvoiceTaxAmount(info.getInvoiceTaxAmount()); - dto.setInvoiceAmount(info.getInvoiceAmount()); - dto.setInvoiceType(info.getInvoiceType()); - dto.setUrl(info.getUrl()); - dto.setImageEncode(info.getImageEncode()); - dto.setFileType(info.getFileType()); - dto.setCheckCode(info.getCheckCode()); - dto.setInvoiceAddress(info.getInvoiceAddress()); - return dto; - } - - private Date parseDate(String value) { - if (StrUtil.isBlank(value)) { - return null; - } - return DateUtil.parse(value.replace("T", " ")); - } - - private String formatDate(Object value) { - if (value == null) { - return null; - } - return DateUtil.format(Convert.toDate(value), "yyyy-MM-dd HH:mm:ss"); - } - - private LocalDate toLocalDate(Date date) { - return date == null ? null : date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); - } - - private List splitIds(String value) { - if (StrUtil.isBlank(value)) { - return Collections.emptyList(); - } - return Arrays.stream(value.split(",")) - .map(String::trim) - .filter(StrUtil::isNotBlank) - .distinct() - .collect(Collectors.toList()); - } - - private BigDecimal nvl(BigDecimal value) { - return value == null ? BigDecimal.ZERO : value; - } - - private BigDecimal nvl(BigDecimal value, BigDecimal defaultValue) { - return value == null ? defaultValue : value; - } - - private BigDecimal decimal(Object value) { - return Convert.toBigDecimal(value, BigDecimal.ZERO); - } - - private Integer parseInteger(String value) { - if (StrUtil.isBlank(value)) { - return null; - } - return Convert.toInt(value, null); + return mallPayFinishNoticeService.payCompletion(dto); } } diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/support/AbstractMallBridgeSupport.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/support/AbstractMallBridgeSupport.java new file mode 100644 index 00000000..2bb9de2e --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/service/support/AbstractMallBridgeSupport.java @@ -0,0 +1,241 @@ +package com.budwk.app.zhgh.pointsmall.mallbridge.service.support; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONUtil; +import com.budwk.app.zhgh.pointsmall.invoice.models.PointsMallInvoiceInfo; +import com.budwk.app.zhgh.pointsmall.mallbridge.dto.InvoiceInfoDTO; +import com.budwk.app.zhgh.pointsmall.mallbridge.dto.OrderInfoDTO; +import com.budwk.app.zhgh.pointsmall.mallbridge.dto.OrderProInfoDTO; +import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderMain; +import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderSub; +import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderSubProduct; +import org.nutz.dao.Cnd; +import org.nutz.dao.Dao; +import org.nutz.dao.entity.Record; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.ZoneId; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 商城桥接公共支撑能力。 + * + *

只放通用转换和常量,具体接口业务放在各自服务中。

+ */ +public abstract class AbstractMallBridgeSupport { + protected static final long ACCESS_EXPIRE_SECONDS = 3600; + protected static final long REFRESH_EXPIRE_SECONDS = 86400L * 7; + + protected static final int ORDER_PENDING_PAY = 0; + protected static final int ORDER_FINISH_PAY = 1; + protected static final int ORDER_IN_PROGRESS = 2; + protected static final int ORDER_CANCELLED = 3; + protected static final int ORDER_RECEIVED = 4; + protected static final int ORDER_COMPLETED = 5; + protected static final int ORDER_EXCHANGE = 6; + protected static final int ORDER_REFUNDED = 7; + protected static final int ORDER_ABOLISH = 8; + + protected final Dao dao; + + protected AbstractMallBridgeSupport(Dao dao) { + this.dao = dao; + } + + protected PointsMallOrderMain toMain(String supplierId, String userId, OrderInfoDTO orderInfo, Integer state) { + PointsMallOrderMain main = new PointsMallOrderMain(); + main.setOrderId(orderInfo.getOrderId()); + main.setSupplierId(supplierId); + main.setOrderNumberId(orderInfo.getOrderNumberId()); + main.setUserId(userId); + main.setAplName(orderInfo.getAplName()); + main.setOrderCreateTime(parseDate(orderInfo.getCreateTime())); + main.setOrderUpdateTime(parseDate(orderInfo.getUpdateTime())); + main.setDeliveryTime(parseDate(orderInfo.getDeliveryTime())); + main.setOrderFinishTime(parseDate(orderInfo.getOrderFinishTime())); + main.setOrderCompleteTime(parseDate(orderInfo.getCompleteTime())); + main.setDeliveryStatus(orderInfo.getDeliveryStatus()); + main.setFreightCost(nvl(orderInfo.getFreightCost())); + main.setRefund(nvl(orderInfo.getRefund())); + main.setLogisticsOrderId(orderInfo.getLogisticsOrderId()); + main.setCrrgBsnNm(orderInfo.getCrrgBsnNm()); + main.setOrderState(state); + main.setTotalPrice(nvl(orderInfo.getTotalPrice())); + main.setWPayOrAPay(nvl(orderInfo.getWPayOrAPay())); + main.setPointsPrice(nvl(orderInfo.getPointsPrice())); + main.setInvoiceCode(orderInfo.getInvoiceCode()); + main.setExtJson(JSONUtil.toJsonStr(orderInfo.getOrderProInfos() == null ? Collections.emptyList() : orderInfo.getOrderProInfos())); + main.setCashPaid(0); + return main; + } + + protected PointsMallOrderSub toSub(PointsMallOrderMain main, String subOrderId) { + PointsMallOrderSub sub = new PointsMallOrderSub(); + sub.setOrderId(StrUtil.blankToDefault(subOrderId, main.getOrderId())); + sub.setOrderNumberId(main.getOrderNumberId()); + sub.setMainOrderId(main.getOrderId()); + sub.setSupplierId(main.getSupplierId()); + sub.setUserId(main.getUserId()); + sub.setAplName(main.getAplName()); + sub.setOrderCreateTime(main.getOrderCreateTime()); + sub.setOrderUpdateTime(new Date()); + sub.setDeliveryTime(main.getDeliveryTime()); + sub.setOrderFinishTime(main.getOrderFinishTime()); + sub.setOrderCompleteTime(main.getOrderCompleteTime()); + sub.setDeliveryStatus(parseInteger(main.getDeliveryStatus())); + sub.setFreightCost(main.getFreightCost()); + sub.setRefund(main.getRefund()); + sub.setLogisticsOrderId(main.getLogisticsOrderId()); + sub.setCrrgBsnNm(main.getCrrgBsnNm()); + sub.setOrderState(main.getOrderState()); + sub.setTotalPrice(main.getTotalPrice()); + sub.setWPayOrAPay(main.getWPayOrAPay()); + sub.setPointsPrice(main.getPointsPrice()); + sub.setInvoiceCode(main.getInvoiceCode()); + sub.setExtJson(main.getExtJson()); + sub.setReconciliationStatus(0); + sub.setInvoiceStatus(0); + return sub; + } + + protected void insertProducts(String orderSubId, List products) { + if (products == null) { + return; + } + for (OrderProInfoDTO item : products) { + PointsMallOrderSubProduct product = new PointsMallOrderSubProduct(); + product.setOrderSubId(orderSubId); + product.setSku(item.getSku()); + product.setNumber(item.getNumber()); + product.setPrice(item.getPrice()); + product.setName(item.getName()); + product.setTaxRate(item.getTaxRate()); + product.setPicInfo(item.getPicInfo() == null || item.getPicInfo().isEmpty() ? null : item.getPicInfo().get(0)); + dao.insert(product); + } + } + + protected OrderInfoDTO toOrderInfo(Record record) { + OrderInfoDTO dto = new OrderInfoDTO(); + dto.setOrderId(record.getString("orderId")); + dto.setOrderNumberId(record.getString("orderNumberId")); + dto.setSupplierId(record.getString("supplierId")); + dto.setAplName(record.getString("aplName")); + dto.setCreateTime(formatDate(record.get("orderCreateTime"))); + dto.setUpdateTime(formatDate(record.get("orderUpdateTime"))); + dto.setDeliveryTime(formatDate(record.get("deliveryTime"))); + dto.setOrderFinishTime(formatDate(record.get("orderFinishTime"))); + dto.setCompleteTime(formatDate(record.get("orderCompleteTime"))); + dto.setDeliveryStatus(record.getString("deliveryStatus")); + dto.setFreightCost(decimal(record.get("freightCost"))); + dto.setWPayOrAPayFreightCost(decimal(record.get("wPayOrAPayFreightCost"))); + dto.setRefund(decimal(record.get("refund"))); + dto.setWPayOrAPayRefund(decimal(record.get("wPayOrAPayRefund"))); + dto.setLogisticsOrderId(record.getString("logisticsOrderId")); + dto.setCrrgBsnNm(record.getString("crrgBsnNm")); + dto.setOrderState(record.getInt("orderState")); + dto.setTotalPrice(decimal(record.get("totalPrice"))); + dto.setWPayOrAPay(decimal(record.get("wPayOrAPay"))); + dto.setPointsPrice(decimal(record.get("pointsPrice"))); + dto.setInvoiceCode(record.getString("invoiceCode")); + dto.setOrderProInfos(readProducts(dto.getOrderId(), record.getString("extJson"))); + return dto; + } + + protected List readProducts(String orderId, String extJson) { + List products = dao.query(PointsMallOrderSubProduct.class, Cnd.where("orderSubId", "=", orderId).and("delFlag", "=", false)); + if (!products.isEmpty()) { + return products.stream().map(this::toProductDTO).collect(Collectors.toList()); + } + if (StrUtil.isBlank(extJson) || "null".equalsIgnoreCase(extJson)) { + return Collections.emptyList(); + } + return JSONUtil.toList(extJson, OrderProInfoDTO.class); + } + + protected OrderProInfoDTO toProductDTO(PointsMallOrderSubProduct product) { + OrderProInfoDTO dto = new OrderProInfoDTO(); + dto.setSku(product.getSku()); + dto.setNumber(product.getNumber()); + dto.setPrice(product.getPrice()); + dto.setName(product.getName()); + dto.setTaxRate(product.getTaxRate()); + dto.setPicInfo(StrUtil.isBlank(product.getPicInfo()) ? Collections.emptyList() : Collections.singletonList(product.getPicInfo())); + dto.setFileId(product.getSku()); + dto.setFilePath(product.getPicInfo()); + return dto; + } + + protected InvoiceInfoDTO toInvoiceInfoDTO(PointsMallInvoiceInfo info) { + InvoiceInfoDTO dto = new InvoiceInfoDTO(); + dto.setInvoiceId(info.getInvoiceId()); + dto.setInvoiceCode(info.getInvoiceCode()); + dto.setInvoiceDate(toLocalDate(info.getInvoiceDate())); + dto.setInvoiceNakeAmount(info.getInvoiceNakeAmount()); + dto.setInvoiceTaxRate(info.getInvoiceTaxRate()); + dto.setInvoiceTaxAmount(info.getInvoiceTaxAmount()); + dto.setInvoiceAmount(info.getInvoiceAmount()); + dto.setInvoiceType(info.getInvoiceType()); + dto.setUrl(info.getUrl()); + dto.setImageEncode(info.getImageEncode()); + dto.setFileType(info.getFileType()); + dto.setCheckCode(info.getCheckCode()); + dto.setInvoiceAddress(info.getInvoiceAddress()); + return dto; + } + + protected Date parseDate(String value) { + if (StrUtil.isBlank(value)) { + return null; + } + return DateUtil.parse(value.replace("T", " ")); + } + + protected String formatDate(Object value) { + if (value == null) { + return null; + } + return DateUtil.format(Convert.toDate(value), "yyyy-MM-dd HH:mm:ss"); + } + + protected LocalDate toLocalDate(Date date) { + return date == null ? null : date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); + } + + protected List splitIds(String value) { + if (StrUtil.isBlank(value)) { + return Collections.emptyList(); + } + return Arrays.stream(value.split(",")) + .map(String::trim) + .filter(StrUtil::isNotBlank) + .distinct() + .collect(Collectors.toList()); + } + + protected BigDecimal nvl(BigDecimal value) { + return value == null ? BigDecimal.ZERO : value; + } + + protected BigDecimal nvl(BigDecimal value, BigDecimal defaultValue) { + return value == null ? defaultValue : value; + } + + protected BigDecimal decimal(Object value) { + return Convert.toBigDecimal(value, BigDecimal.ZERO); + } + + protected Integer parseInteger(String value) { + if (StrUtil.isBlank(value)) { + return null; + } + return Convert.toInt(value, null); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/util/MallBridgeCryptoUtil.java b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/util/MallBridgeCryptoUtil.java new file mode 100644 index 00000000..26016b03 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/mallbridge/util/MallBridgeCryptoUtil.java @@ -0,0 +1,31 @@ +package com.budwk.app.zhgh.pointsmall.mallbridge.util; + +import cn.hutool.core.util.HexUtil; +import cn.hutool.crypto.Mode; +import cn.hutool.crypto.Padding; +import cn.hutool.crypto.SmUtil; +import cn.hutool.crypto.symmetric.SM4; + +/** + * 积分商城桥接国密工具。 + */ +public class MallBridgeCryptoUtil { + + private MallBridgeCryptoUtil() { + } + + /** + * 生成供应商开放接口签名。 + */ + public static String generateSign(String supplierId, String clientId, long timestamp) { + return SmUtil.sm3(supplierId + clientId + timestamp); + } + + /** + * SM4-CBC 加密,返回 Base64 密文。 + */ + public static String sm4CbcEncrypt(String data, String keyHex, String ivHex) { + SM4 sm4 = new SM4(Mode.CBC, Padding.PKCS5Padding, HexUtil.decodeHex(keyHex), HexUtil.decodeHex(ivHex)); + return sm4.encryptBase64(data); + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/message/models/PointsMallMessage.java b/src/main/java/com/budwk/app/zhgh/pointsmall/message/models/PointsMallMessage.java index 71509792..f5cdc940 100644 --- a/src/main/java/com/budwk/app/zhgh/pointsmall/message/models/PointsMallMessage.java +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/message/models/PointsMallMessage.java @@ -72,6 +72,6 @@ public class PointsMallMessage extends BaseModel implements Serializable { private Date planPushTime; @Column - @Comment("状态") + @Comment("状态:0待推送,1已推送,2待重推,3推送失败") private Integer status; } diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/message/service/PointsMallMessageService.java b/src/main/java/com/budwk/app/zhgh/pointsmall/message/service/PointsMallMessageService.java index ce9a8ecc..798765ec 100644 --- a/src/main/java/com/budwk/app/zhgh/pointsmall/message/service/PointsMallMessageService.java +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/message/service/PointsMallMessageService.java @@ -6,7 +6,6 @@ import com.budwk.app.zhgh.pointsmall.message.models.PointsMallMessage; import com.budwk.app.zhgh.pointsmall.message.models.PointsMallMessageTemplate; import com.budwk.app.zhgh.pointsmall.message.param.PointsMallMessagePageParam; import com.budwk.app.zhgh.pointsmall.message.param.PointsMallMessageTemplatePageParam; - import java.util.List; public interface PointsMallMessageService extends BaseService { diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/message/service/impl/PointsMallMessageServiceImpl.java b/src/main/java/com/budwk/app/zhgh/pointsmall/message/service/impl/PointsMallMessageServiceImpl.java index f882112e..0b379f37 100644 --- a/src/main/java/com/budwk/app/zhgh/pointsmall/message/service/impl/PointsMallMessageServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/message/service/impl/PointsMallMessageServiceImpl.java @@ -5,6 +5,8 @@ import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.StrUtil; import com.budwk.app.base.page.Pagination; import com.budwk.app.base.service.impl.BaseServiceImpl; +import com.budwk.app.sys.models.Sys_user; +import com.budwk.app.zhgh.dayofficework.message.service.GlobalMessageSendService; import com.budwk.app.zhgh.pointsmall.message.models.PointsMallMessage; import com.budwk.app.zhgh.pointsmall.message.models.PointsMallMessageTemplate; import com.budwk.app.zhgh.pointsmall.message.param.PointsMallMessagePageParam; @@ -13,14 +15,19 @@ import com.budwk.app.zhgh.pointsmall.message.service.PointsMallMessageService; import org.nutz.dao.Chain; import org.nutz.dao.Cnd; import org.nutz.dao.Dao; +import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; import java.util.Arrays; import java.util.List; +import java.util.stream.Collectors; @IocBean(args = {"refer:dao"}) public class PointsMallMessageServiceImpl extends BaseServiceImpl implements PointsMallMessageService { + @Inject + private GlobalMessageSendService globalMessageSendService; + public PointsMallMessageServiceImpl(Dao dao) { super(dao); } @@ -64,7 +71,7 @@ public class PointsMallMessageServiceImpl extends BaseServiceImpl messagePage(PointsMallMessagePageParam param) { + Cnd cnd = messageCnd(param); + cnd.desc("createdAt"); + return listPage(param.getPageNumber(), param.getPageSize(), PointsMallMessage.class, cnd); + } + + @Override + public void resend(List ids) { + if (CollUtil.isEmpty(ids)) { + throw new IllegalArgumentException("请选择消息记录"); + } + List messages = dao().query(PointsMallMessage.class, Cnd.where("id", "in", ids) + .and("status", "in", Arrays.asList(0, 2, 3)).and("delFlag", "=", false)); + if (CollUtil.isEmpty(messages)) { + throw new IllegalArgumentException("没有可推送的消息记录"); + } + for (PointsMallMessage message : messages) { + pushToGlobalMessage(message); + } + } + + @Override + public void resendAll(PointsMallMessagePageParam param) { + Cnd cnd = messageCnd(param); + cnd.and("status", "in", Arrays.asList(0, 2, 3)); + List messages = dao().query(PointsMallMessage.class, cnd); + for (PointsMallMessage message : messages) { + pushToGlobalMessage(message); + } + } + + private void pushToGlobalMessage(PointsMallMessage message) { + try { + if (StrUtil.isBlank(message.getTouser())) { + throw new IllegalArgumentException("目标用户不能为空"); + } + List loginNames = StrUtil.splitTrim(message.getTouser(), ","); + List users = dao().query(Sys_user.class, Cnd.where("loginname", "in", loginNames) + .and("disabled", "=", false).and("delFlag", "=", false)); + if (CollUtil.isEmpty(users)) { + throw new IllegalArgumentException("未找到目标用户"); + } + List receiverIds = users.stream().map(Sys_user::getId).collect(Collectors.toList()); + PointsMallMessageTemplate template = StrUtil.isBlank(message.getTemplateId()) ? null : fetch(message.getTemplateId()); + String title = template == null || StrUtil.isBlank(template.getName()) ? "积分商城消息" : template.getName(); + String content = StrUtil.blankToDefault(message.getContent(), template == null ? "" : template.getContent()); + if (StrUtil.isBlank(content)) { + throw new IllegalArgumentException("消息内容不能为空"); + } + globalMessageSendService.sendMessage(title, content, 2, receiverIds, null); + dao().update(PointsMallMessage.class, Chain.make("status", 1), Cnd.where("id", "=", message.getId())); + } catch (Exception e) { + dao().update(PointsMallMessage.class, Chain.make("status", 3), Cnd.where("id", "=", message.getId())); + throw new IllegalArgumentException("消息推送失败:" + e.getMessage(), e); + } + } + + private Cnd templateCnd(PointsMallMessageTemplatePageParam param) { + Cnd cnd = Cnd.where("delFlag", "=", false); + if (StrUtil.isNotBlank(param.getName())) { + cnd.and("name", "like", "%" + param.getName() + "%"); + } + if (param.getType() != null) { + cnd.and("type", "=", param.getType()); + } + if (param.getStatus() != null) { + cnd.and("status", "=", param.getStatus()); + } + return cnd; + } + + private Cnd messageCnd(PointsMallMessagePageParam param) { Cnd cnd = Cnd.where("delFlag", "=", false); if (StrUtil.isNotBlank(param.getTemplateId())) { cnd.and("templateId", "=", param.getTemplateId()); @@ -91,41 +169,6 @@ public class PointsMallMessageServiceImpl extends BaseServiceImpl ids) { - if (CollUtil.isEmpty(ids)) { - throw new IllegalArgumentException("请选择消息记录"); - } - dao().update(PointsMallMessage.class, Chain.make("status", 2), Cnd.where("id", "in", ids).and("status", "in", Arrays.asList(0, 3))); - } - - @Override - public void resendAll(PointsMallMessagePageParam param) { - Cnd cnd = Cnd.where("delFlag", "=", false).and("status", "in", Arrays.asList(0, 3)); - if (StrUtil.isNotBlank(param.getTemplateId())) { - cnd.and("templateId", "=", param.getTemplateId()); - } - if (StrUtil.isNotBlank(param.getTouser())) { - cnd.and("touser", "like", "%" + param.getTouser() + "%"); - } - dao().update(PointsMallMessage.class, Chain.make("status", 2), cnd); - } - - private Cnd templateCnd(PointsMallMessageTemplatePageParam param) { - Cnd cnd = Cnd.where("delFlag", "=", false); - if (StrUtil.isNotBlank(param.getName())) { - cnd.and("name", "like", "%" + param.getName() + "%"); - } - if (param.getType() != null) { - cnd.and("type", "=", param.getType()); - } - if (param.getStatus() != null) { - cnd.and("status", "=", param.getStatus()); - } return cnd; } @@ -152,6 +195,6 @@ public class PointsMallMessageServiceImpl extends BaseServiceImpl Record supplierOrderUrl(String supplierId, String orderId, Integer orderType); + Boolean userOrderReceive(PointsMallMobileOrderReceivedParam param, String defaultUserId); + List listExchange(String mainOrderId); List listSubProducts(String orderId); diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/order/service/impl/PointsMallOrderServiceImpl.java b/src/main/java/com/budwk/app/zhgh/pointsmall/order/service/impl/PointsMallOrderServiceImpl.java index 9bafd7ac..ea0f63cc 100644 --- a/src/main/java/com/budwk/app/zhgh/pointsmall/order/service/impl/PointsMallOrderServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/order/service/impl/PointsMallOrderServiceImpl.java @@ -7,8 +7,10 @@ import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderExchange; import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderMain; import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderSub; import com.budwk.app.zhgh.pointsmall.order.param.PointsMallMobileOrderPageParam; +import com.budwk.app.zhgh.pointsmall.order.param.PointsMallMobileOrderReceivedParam; import com.budwk.app.zhgh.pointsmall.order.param.PointsMallOrderPageParam; import com.budwk.app.zhgh.pointsmall.order.service.PointsMallOrderService; +import com.budwk.app.zhgh.pointsmall.points.service.PointsMallPointsLockService; import com.budwk.app.zhgh.pointsmall.supplier.models.PointsMallSupplier; import org.nutz.dao.Chain; import org.nutz.dao.Cnd; @@ -16,7 +18,10 @@ import org.nutz.dao.Dao; import org.nutz.dao.Sqls; import org.nutz.dao.entity.Record; import org.nutz.dao.sql.Sql; +import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; +import org.nutz.trans.Atom; +import org.nutz.trans.Trans; import java.util.Arrays; import java.util.Date; @@ -25,6 +30,9 @@ import java.util.List; @IocBean(args = {"refer:dao"}) public class PointsMallOrderServiceImpl extends BaseServiceImpl implements PointsMallOrderService { + @Inject + private PointsMallPointsLockService pointsMallPointsLockService; + public PointsMallOrderServiceImpl(Dao dao) { super(dao); } @@ -39,7 +47,7 @@ public class PointsMallOrderServiceImpl extends BaseServiceImpl page = listPageMap(param.getPageNumber(), param.getPageSize(), sql); + Pagination page = listPage(param.getPageNumber(), param.getPageSize(), sql); for (Record item : page.getList()) { item.put("productImgList", productImages(item.getString("orderId"))); } @@ -122,6 +130,29 @@ public class PointsMallOrderServiceImpl extends BaseServiceImpl { + sub.setOrderState(5); + sub.setOrderCompleteTime(new Date()); + sub.setOrderUpdateTime(new Date()); + dao().updateIgnoreNull(sub); + pointsMallPointsLockService.deductCompletedSub(sub); + }); + return true; + } + @Override public List listExchange(String mainOrderId) { Cnd cnd = Cnd.where("e.delFlag", "=", false); @@ -129,7 +160,7 @@ public class PointsMallOrderServiceImpl extends BaseServiceImpl subOrders); + + void releaseOrReturnSub(PointsMallOrderSub subOrder, Integer orderState, BigDecimal refund); + + void deductCompletedSub(PointsMallOrderSub subOrder); +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/points/service/impl/PointsMallPointsLockServiceImpl.java b/src/main/java/com/budwk/app/zhgh/pointsmall/points/service/impl/PointsMallPointsLockServiceImpl.java new file mode 100644 index 00000000..503243d1 --- /dev/null +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/points/service/impl/PointsMallPointsLockServiceImpl.java @@ -0,0 +1,213 @@ +package com.budwk.app.zhgh.pointsmall.points.service.impl; + +import cn.hutool.core.util.StrUtil; +import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderMain; +import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderSub; +import com.budwk.app.zhgh.pointsmall.points.enums.PointsMallPointLockStatusEnum; +import com.budwk.app.zhgh.pointsmall.points.models.PointsMallMainPointsLock; +import com.budwk.app.zhgh.pointsmall.points.models.PointsMallSubPointsLock; +import com.budwk.app.zhgh.pointsmall.points.models.PointsMallUserPoints; +import com.budwk.app.zhgh.pointsmall.points.models.PointsMallUserPointsLog; +import com.budwk.app.zhgh.pointsmall.points.service.PointsMallPointsLockService; +import lombok.extern.slf4j.Slf4j; +import org.nutz.dao.Chain; +import org.nutz.dao.Cnd; +import org.nutz.dao.Dao; +import org.nutz.ioc.loader.annotation.IocBean; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; +import java.util.Objects; + +/** + * 积分锁定、释放和实际扣减服务。 + */ +@Slf4j +@IocBean(args = {"refer:dao"}) +public class PointsMallPointsLockServiceImpl implements PointsMallPointsLockService { + + private final Dao dao; + + public PointsMallPointsLockServiceImpl(Dao dao) { + this.dao = dao; + } + + @Override + public BigDecimal sumLocked(String userId) { + if (StrUtil.isBlank(userId)) { + return BigDecimal.ZERO; + } + List list = dao.query(PointsMallMainPointsLock.class, Cnd.where("userId", "=", userId) + .and("status", "=", PointsMallPointLockStatusEnum.LOCK.getCode()).and("delFlag", "=", false)); + return list.stream().map(PointsMallMainPointsLock::getLockAmount).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add); + } + + @Override + public void lockMain(PointsMallOrderMain mainOrder, BigDecimal lockAmount) { + if (mainOrder == null || StrUtil.hasBlank(mainOrder.getUserId(), mainOrder.getOrderId(), mainOrder.getSupplierId())) { + throw new IllegalArgumentException("主单积分锁定参数不能为空"); + } + BigDecimal amount = nvl(lockAmount); + if (amount.compareTo(BigDecimal.ZERO) <= 0) { + return; + } + PointsMallMainPointsLock exists = dao.fetch(PointsMallMainPointsLock.class, Cnd.where("userId", "=", mainOrder.getUserId()) + .and("supplierId", "=", mainOrder.getSupplierId()) + .and("orderId", "=", mainOrder.getOrderId()) + .and("status", "=", PointsMallPointLockStatusEnum.LOCK.getCode()) + .and("delFlag", "=", false)); + if (exists != null) { + throw new IllegalArgumentException("该订单已经确认购买,请不要重复确认购买"); + } + PointsMallMainPointsLock lock = new PointsMallMainPointsLock(); + lock.setUserId(mainOrder.getUserId()); + lock.setSupplierId(mainOrder.getSupplierId()); + lock.setOrderId(mainOrder.getOrderId()); + lock.setLockAmount(amount); + lock.setStatus(PointsMallPointLockStatusEnum.LOCK.getCode()); + lock.setLockTime(new Date()); + dao.insert(lock); + log.info("积分商城主单积分锁定完成,userId={},supplierId={},orderId={},lockAmount={}", + mainOrder.getUserId(), mainOrder.getSupplierId(), mainOrder.getOrderId(), amount); + } + + @Override + public void releaseMain(String userId, String supplierId, String orderId) { + if (StrUtil.hasBlank(userId, supplierId, orderId)) { + return; + } + dao.update(PointsMallMainPointsLock.class, + Chain.make("status", PointsMallPointLockStatusEnum.UNLOCK.getCode()).add("delFlag", true), + Cnd.where("userId", "=", userId) + .and("supplierId", "=", supplierId) + .and("orderId", "=", orderId) + .and("status", "=", PointsMallPointLockStatusEnum.LOCK.getCode()) + .and("delFlag", "=", false)); + log.info("积分商城主单积分释放完成,userId={},supplierId={},orderId={}", userId, supplierId, orderId); + } + + @Override + public void createSubLocks(PointsMallOrderMain mainOrder, List subOrders) { + if (mainOrder == null || subOrders == null || subOrders.isEmpty()) { + return; + } + for (PointsMallOrderSub sub : subOrders) { + if (sub == null || StrUtil.isBlank(sub.getOrderId())) { + continue; + } + PointsMallSubPointsLock exists = dao.fetch(PointsMallSubPointsLock.class, Cnd.where("supplierId", "=", sub.getSupplierId()) + .and("subOrderId", "=", sub.getOrderId()).and("delFlag", "=", false)); + if (exists != null) { + continue; + } + PointsMallSubPointsLock lock = new PointsMallSubPointsLock(); + lock.setUserId(mainOrder.getUserId()); + lock.setMainOrderId(sub.getMainOrderId()); + lock.setSubOrderId(sub.getOrderId()); + lock.setSupplierId(sub.getSupplierId()); + lock.setChangePoints(nvl(sub.getPointsPrice()).negate()); + lock.setOrderSubStatus(sub.getOrderState()); + lock.setLockTime(new Date()); + dao.insert(lock); + } + log.info("积分商城子单积分锁定记录生成完成,supplierId={},mainOrderId={},subOrderCount={}", + mainOrder.getSupplierId(), mainOrder.getOrderId(), subOrders.size()); + } + + @Override + public void releaseOrReturnSub(PointsMallOrderSub subOrder, Integer orderState, BigDecimal refund) { + if (subOrder == null || StrUtil.hasBlank(subOrder.getSupplierId(), subOrder.getOrderId(), subOrder.getMainOrderId())) { + return; + } + PointsMallMainPointsLock mainLock = activeMainLock(subOrder.getUserId(), subOrder.getSupplierId(), subOrder.getMainOrderId()); + if (mainLock == null) { + return; + } + PointsMallSubPointsLock lastSubLock = latestSubLock(subOrder.getSupplierId(), subOrder.getOrderId()); + if (lastSubLock == null) { + return; + } + BigDecimal returnPoint = Integer.valueOf(7).equals(orderState) ? nvl(refund) : nvl(lastSubLock.getChangePoints()).negate(); + if (returnPoint.compareTo(BigDecimal.ZERO) <= 0) { + return; + } + PointsMallSubPointsLock insert = new PointsMallSubPointsLock(); + insert.setUserId(subOrder.getUserId()); + insert.setMainOrderId(subOrder.getMainOrderId()); + insert.setSubOrderId(subOrder.getOrderId()); + insert.setSupplierId(subOrder.getSupplierId()); + insert.setChangePoints(returnPoint); + insert.setOrderSubStatus(orderState); + insert.setLockTime(new Date()); + dao.insert(insert); + + BigDecimal after = nvl(mainLock.getLockAmount()).subtract(returnPoint); + if (after.compareTo(BigDecimal.ZERO) < 0) { + after = BigDecimal.ZERO; + } + dao.update(PointsMallMainPointsLock.class, Chain.make("lockAmount", after), Cnd.where("id", "=", mainLock.getId())); + log.info("积分商城子单积分释放完成,supplierId={},mainOrderId={},subOrderId={},returnPoint={}", + subOrder.getSupplierId(), subOrder.getMainOrderId(), subOrder.getOrderId(), returnPoint); + } + + @Override + public void deductCompletedSub(PointsMallOrderSub subOrder) { + if (subOrder == null || StrUtil.hasBlank(subOrder.getUserId(), subOrder.getSupplierId(), subOrder.getMainOrderId(), subOrder.getOrderId())) { + return; + } + PointsMallMainPointsLock mainLock = activeMainLock(subOrder.getUserId(), subOrder.getSupplierId(), subOrder.getMainOrderId()); + if (mainLock == null) { + return; + } + BigDecimal subtract = nvl(subOrder.getPointsPrice()).subtract(nvl(subOrder.getRefund())); + if (subtract.compareTo(BigDecimal.ZERO) <= 0) { + return; + } + PointsMallUserPoints userPoints = dao.fetch(PointsMallUserPoints.class, Cnd.where("userId", "=", subOrder.getUserId()).and("delFlag", "=", false)); + if (userPoints == null) { + throw new IllegalArgumentException("用户积分不存在"); + } + BigDecimal availableAfter = nvl(userPoints.getAvailablePoints()).subtract(subtract); + if (availableAfter.compareTo(BigDecimal.ZERO) < 0) { + throw new IllegalArgumentException("积分不足,无法扣减"); + } + dao.update(PointsMallUserPoints.class, + Chain.make("availablePoints", availableAfter).add("usedPoints", nvl(userPoints.getUsedPoints()).add(subtract)), + Cnd.where("id", "=", userPoints.getId())); + + PointsMallUserPointsLog logRecord = new PointsMallUserPointsLog(); + logRecord.setUserId(subOrder.getUserId()); + logRecord.setChangeAmount(subtract); + logRecord.setChangeType("DEDUCTION"); + logRecord.setSubOrderId(subOrder.getOrderId()); + logRecord.setRemark("积分商城订单确认完成扣减"); + dao.insert(logRecord); + + BigDecimal lockAfter = nvl(mainLock.getLockAmount()).subtract(subtract); + if (lockAfter.compareTo(BigDecimal.ZERO) < 0) { + lockAfter = BigDecimal.ZERO; + } + dao.update(PointsMallMainPointsLock.class, Chain.make("lockAmount", lockAfter), Cnd.where("id", "=", mainLock.getId())); + log.info("积分商城子单确认完成扣减积分,userId={},supplierId={},subOrderId={},points={}", + subOrder.getUserId(), subOrder.getSupplierId(), subOrder.getOrderId(), subtract); + } + + private PointsMallMainPointsLock activeMainLock(String userId, String supplierId, String orderId) { + return dao.fetch(PointsMallMainPointsLock.class, Cnd.where("userId", "=", userId) + .and("supplierId", "=", supplierId) + .and("orderId", "=", orderId) + .and("status", "=", PointsMallPointLockStatusEnum.LOCK.getCode()) + .and("delFlag", "=", false)); + } + + private PointsMallSubPointsLock latestSubLock(String supplierId, String subOrderId) { + List list = dao.query(PointsMallSubPointsLock.class, Cnd.where("supplierId", "=", supplierId) + .and("subOrderId", "=", subOrderId).and("delFlag", "=", false).desc("createdAt")); + return list.isEmpty() ? null : list.get(0); + } + + private BigDecimal nvl(BigDecimal value) { + return value == null ? BigDecimal.ZERO : value; + } +} diff --git a/src/main/java/com/budwk/app/zhgh/pointsmall/points/service/impl/PointsMallUserPointsServiceImpl.java b/src/main/java/com/budwk/app/zhgh/pointsmall/points/service/impl/PointsMallUserPointsServiceImpl.java index 974989b8..79017ca9 100644 --- a/src/main/java/com/budwk/app/zhgh/pointsmall/points/service/impl/PointsMallUserPointsServiceImpl.java +++ b/src/main/java/com/budwk/app/zhgh/pointsmall/points/service/impl/PointsMallUserPointsServiceImpl.java @@ -10,6 +10,7 @@ import com.budwk.app.zhgh.pointsmall.points.param.PointsMallUserPointsBatchParam import com.budwk.app.zhgh.pointsmall.points.param.PointsMallUserPointsChangeParam; import com.budwk.app.zhgh.pointsmall.points.param.PointsMallUserPointsLogPageParam; import com.budwk.app.zhgh.pointsmall.points.param.PointsMallUserPointsPageParam; +import com.budwk.app.zhgh.pointsmall.points.service.PointsMallPointsLockService; import com.budwk.app.zhgh.pointsmall.points.service.PointsMallUserPointsService; import org.nutz.dao.Chain; import org.nutz.dao.Cnd; @@ -17,6 +18,7 @@ import org.nutz.dao.Dao; import org.nutz.dao.Sqls; import org.nutz.dao.entity.Record; import org.nutz.dao.sql.Sql; +import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.IocBean; import org.nutz.trans.Atom; import org.nutz.trans.Trans; @@ -28,6 +30,9 @@ import java.util.List; @IocBean(args = {"refer:dao"}) public class PointsMallUserPointsServiceImpl extends BaseServiceImpl implements PointsMallUserPointsService { + @Inject + private PointsMallPointsLockService pointsMallPointsLockService; + public PointsMallUserPointsServiceImpl(Dao dao) { super(dao); } @@ -47,10 +52,16 @@ public class PointsMallUserPointsServiceImpl extends BaseServiceImpl - + @@ -73,7 +73,7 @@ layout("/layouts/platform.html"){ - +