From a34de9f2239cedfcba3ff189c76df36bb6c7b615 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Tue, 6 May 2025 20:49:13 +0800 Subject: [PATCH] =?UTF-8?q?feat=EF=BC=9A=E3=80=90MALL=20=E5=95=86=E5=9F=8E?= =?UTF-8?q?=E3=80=91=E5=A2=9E=E5=8A=A0=E5=BE=AE=E4=BF=A1=E7=89=A9=E6=B5=81?= =?UTF-8?q?=E7=9A=84=E5=AF=B9=E6=8E=A5=EF=BC=88=E5=92=8C=E7=A4=BE=E5=8C=BA?= =?UTF-8?q?=E5=90=8C=E5=AD=A6=EF=BC=8C=E4=B8=80=E8=B5=B7=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E4=B8=AD=E3=80=82=E3=80=82=E3=80=82=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../order/config/TradeOrderProperties.java | 6 ++ .../order/TradeOrderUpdateServiceImpl.java | 16 +++- .../order/handler/TradeOrderHandler.java | 21 +++++ .../TradeStatusSyncToWxaOrderHandler.java | 86 +++++++++++++++++++ .../pay/api/order/dto/PayOrderRespDTO.java | 15 ++++ .../module/pay/enums/PayChannelEnum.java | 42 +++++++++ .../impl/weixin/AbstractWxPayClient.java | 3 +- .../system/api/social/SocialClientApi.java | 16 ++++ ...ialWxaOrderNotifyConfirmReceiveReqDTO.java | 30 +++++++ ...ocialWxaOrderUploadShippingInfoReqDTO.java | 67 +++++++++++++++ .../system/enums/ErrorCodeConstants.java | 3 +- .../api/social/SocialClientApiImpl.java | 10 +++ .../service/social/SocialClientService.java | 18 ++++ .../social/SocialClientServiceImpl.java | 67 +++++++++++++++ .../src/main/resources/application.yaml | 1 + 15 files changed, 396 insertions(+), 5 deletions(-) create mode 100644 yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeStatusSyncToWxaOrderHandler.java create mode 100644 yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/PayChannelEnum.java create mode 100644 yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxaOrderNotifyConfirmReceiveReqDTO.java create mode 100644 yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxaOrderUploadShippingInfoReqDTO.java diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/order/config/TradeOrderProperties.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/order/config/TradeOrderProperties.java index 0d7b271d91..5a64f944cf 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/order/config/TradeOrderProperties.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/order/config/TradeOrderProperties.java @@ -48,4 +48,10 @@ public class TradeOrderProperties { @NotNull(message = "评论超时时间不能为空") private Duration commentExpireTime; + /** + * 是否同步订单状态到微信小程序 + */ + @NotNull(message = "是否同步订单状态到微信小程序不能为空") + private Boolean statusSyncToWxaEnable; + } diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateServiceImpl.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateServiceImpl.java index d47da484e2..6db33b6302 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateServiceImpl.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateServiceImpl.java @@ -401,6 +401,11 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { .setOrderId(order.getId()).setUserId(order.getUserId()).setMessage(null)); // 4.2 发送订阅消息 getSelf().sendDeliveryOrderMessage(order, deliveryReqVO); + + // 5. 处理订单发货后逻辑 + order.setLogisticsId(updateOrderObj.getLogisticsId()).setLogisticsNo(updateOrderObj.getLogisticsNo()) + .setStatus(updateOrderObj.getStatus()).setDeliveryTime(updateOrderObj.getDeliveryTime()); + tradeOrderHandlers.forEach(handler -> handler.afterDeliveryOrder(order)); } @Async @@ -499,15 +504,20 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { * @param order 订单 */ private void receiveOrder0(TradeOrderDO order) { - // 更新 TradeOrderDO 状态为已完成 + // 1. 更新 TradeOrderDO 状态为已完成 + LocalDateTime receiveTime = LocalDateTime.now(); int updateCount = tradeOrderMapper.updateByIdAndStatus(order.getId(), order.getStatus(), - new TradeOrderDO().setStatus(TradeOrderStatusEnum.COMPLETED.getStatus()).setReceiveTime(LocalDateTime.now())); + new TradeOrderDO().setStatus(TradeOrderStatusEnum.COMPLETED.getStatus()).setReceiveTime(receiveTime)); if (updateCount == 0) { throw exception(ORDER_RECEIVE_FAIL_STATUS_NOT_DELIVERED); } - // 插入订单日志 + // 2. 插入订单日志 TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), TradeOrderStatusEnum.COMPLETED.getStatus()); + + // 3. 执行 TradeOrderHandler 后置处理 + order.setStatus(TradeOrderStatusEnum.COMPLETED.getStatus()).setReceiveTime(receiveTime); + tradeOrderHandlers.forEach(handler -> handler.afterReceiveOrder(order)); } /** diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeOrderHandler.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeOrderHandler.java index 4cc5c69a37..1b3d2f7fde 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeOrderHandler.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeOrderHandler.java @@ -62,6 +62,27 @@ public interface TradeOrderHandler { */ default void beforeDeliveryOrder(TradeOrderDO order) {} + /** + * 订单发货后 + * + * @param order 订单 + */ + default void afterDeliveryOrder(TradeOrderDO order) {} + + /** + * 订单收货前 + * + * @param order 订单 + */ + default void beforeReceiveOrder(TradeOrderDO order) {} + + /** + * 订单收货后 + * + * @param order 订单 + */ + default void afterReceiveOrder(TradeOrderDO order) {} + // ========== 公用方法 ========== /** diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeStatusSyncToWxaOrderHandler.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeStatusSyncToWxaOrderHandler.java new file mode 100644 index 0000000000..8bd1a802ea --- /dev/null +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeStatusSyncToWxaOrderHandler.java @@ -0,0 +1,86 @@ +package cn.iocoder.yudao.module.trade.service.order.handler; + +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.module.pay.api.order.PayOrderApi; +import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO; +import cn.iocoder.yudao.module.pay.enums.PayChannelEnum; +import cn.iocoder.yudao.module.system.api.social.SocialClientApi; +import cn.iocoder.yudao.module.system.api.social.dto.SocialWxaOrderNotifyConfirmReceiveReqDTO; +import cn.iocoder.yudao.module.system.api.social.dto.SocialWxaOrderUploadShippingInfoReqDTO; +import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; +import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum; +import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Component; + +/** + * 同步订单状态到微信小程序的 {@link TradeOrderHandler} 实现类 + * + * 背景:电商类目的微信小程序需要上传发货信息,不然微信支付会被封 = =! + * 注意:微信小程序开发环境下的订单不能用来发货。只有小程序正式版才会有发货,所以体验版无法调通,提示订单不存在。注意别踩坑。 + */ +@Slf4j +@Component +@ConditionalOnProperty(prefix = "yudao.trade.order", value = "status-sync-to-wxa-enable") +public class TradeStatusSyncToWxaOrderHandler implements TradeOrderHandler { + + @Resource + private PayOrderApi payOrderApi; + @Resource + private SocialClientApi socialClientApi; + + @Resource + private DeliveryExpressService expressService; + + @Override + public void afterDeliveryOrder(TradeOrderDO order) { + // 注意:只有微信小程序支付的订单,才需要同步 + if (ObjUtil.notEqual(order.getPayChannelCode(), PayChannelEnum.WX_LITE.getCode())) { + return; + } + PayOrderRespDTO payOrder = payOrderApi.getOrder(order.getPayOrderId()); + SocialWxaOrderUploadShippingInfoReqDTO reqDTO = new SocialWxaOrderUploadShippingInfoReqDTO() + .setTransactionId(payOrder.getChannelOrderNo()) + .setOpenid(payOrder.getChannelUserId()) + .setItemDesc(payOrder.getSubject()) + .setReceiverContact(order.getReceiverMobile()); + if (DeliveryTypeEnum.EXPRESS.getType().equals(order.getDeliveryType()) && StrUtil.isNotEmpty(order.getLogisticsNo())) { + reqDTO.setLogisticsType(SocialWxaOrderUploadShippingInfoReqDTO.LOGISTICS_TYPE_EXPRESS) + .setExpressCompany(expressService.getDeliveryExpress(order.getLogisticsId()).getCode()) + .setLogisticsNo(order.getLogisticsNo()); + } else if (DeliveryTypeEnum.PICK_UP.getType().equals(order.getDeliveryType())) { + reqDTO.setLogisticsType(SocialWxaOrderUploadShippingInfoReqDTO.LOGISTICS_TYPE_PICK_UP); + } else { + reqDTO.setLogisticsType(SocialWxaOrderUploadShippingInfoReqDTO.LOGISTICS_TYPE_VIRTUAL); + } + try { + socialClientApi.uploadWxaOrderShippingInfo(UserTypeEnum.MEMBER.getValue(), reqDTO); + } catch (Exception ex) { + log.error("[afterDeliveryOrder][订单({}) 上传订单物流信息到微信小程序失败]", order, ex); + } + } + + @Override + public void afterReceiveOrder(TradeOrderDO order) { + // 注意:只有微信小程序支付的订单,才需要同步 + if (ObjUtil.notEqual(order.getPayChannelCode(), PayChannelEnum.WX_LITE.getCode())) { + return; + } + PayOrderRespDTO payOrder = payOrderApi.getOrder(order.getPayOrderId()); + SocialWxaOrderNotifyConfirmReceiveReqDTO reqDTO = new SocialWxaOrderNotifyConfirmReceiveReqDTO() + .setTransactionId(payOrder.getChannelOrderNo()) + .setReceivedTime(order.getReceiveTime()); + try { + socialClientApi.notifyWxaOrderConfirmReceive(UserTypeEnum.MEMBER.getValue(), reqDTO); + } catch (Exception ex) { + log.error("[afterReceiveOrder][订单({}) 通知订单收货到微信小程序失败]", order, ex); + } + } + + // TODO @芋艿:【设置路径】 https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/business-capabilities/order-shipping/order-shipping.html#%E5%85%AD%E3%80%81%E6%B6%88%E6%81%AF%E8%B7%B3%E8%BD%AC%E8%B7%AF%E5%BE%84%E8%AE%BE%E7%BD%AE%E6%8E%A5%E5%8F%A3 + +} diff --git a/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/order/dto/PayOrderRespDTO.java b/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/order/dto/PayOrderRespDTO.java index a1c9c32b3b..1287ce7a39 100644 --- a/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/order/dto/PayOrderRespDTO.java +++ b/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/order/dto/PayOrderRespDTO.java @@ -32,6 +32,10 @@ public class PayOrderRespDTO { private String merchantOrderId; // ========== 订单相关字段 ========== + /** + * 商品标题 + */ + private String subject; /** * 支付金额,单位:分 */ @@ -50,4 +54,15 @@ public class PayOrderRespDTO { // ========== 渠道相关字段 ========== + /** + * 渠道用户编号 + * + * 例如说,微信 openid、支付宝账号 + */ + private String channelUserId; + /** + * 渠道订单号 + */ + private String channelOrderNo; + } diff --git a/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/PayChannelEnum.java b/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/PayChannelEnum.java new file mode 100644 index 0000000000..52d35d2aa9 --- /dev/null +++ b/yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/PayChannelEnum.java @@ -0,0 +1,42 @@ +package cn.iocoder.yudao.module.pay.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 支付渠道的编码的枚举 + * + * @author 芋道源码 + */ +@Getter +@AllArgsConstructor +public enum PayChannelEnum { + + WX_PUB("wx_pub", "微信 JSAPI 支付"), // 公众号网页 + WX_LITE("wx_lite", "微信小程序支付"), + WX_APP("wx_app", "微信 App 支付"), + WX_NATIVE("wx_native", "微信 Native 支付"), + WX_WAP("wx_wap", "微信 Wap 网站支付"), // H5 网页 + WX_BAR("wx_bar", "微信付款码支付"), + + ALIPAY_PC("alipay_pc", "支付宝 PC 网站支付"), + ALIPAY_WAP("alipay_wap", "支付宝 Wap 网站支付"), + ALIPAY_APP("alipay_app", "支付宝App 支付"), + ALIPAY_QR("alipay_qr", "支付宝扫码支付"), + ALIPAY_BAR("alipay_bar", "支付宝条码支付"), + MOCK("mock", "模拟支付"), + + WALLET("wallet", "钱包支付"); + + /** + * 编码 + * + * 参考 支付渠道属性值 + */ + private final String code; + /** + * 名字 + */ + private final String name; + +} diff --git a/yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java b/yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java index ce01ddb794..e681cfbea4 100644 --- a/yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java +++ b/yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java @@ -179,7 +179,8 @@ public abstract class AbstractWxPayClient extends AbstractPayClient headers) throws WxPayException { // 1. 解析回调 - SignatureHeader signatureHeader = getRequestHeader(headers); +// SignatureHeader signatureHeader = getRequestHeader(headers); + SignatureHeader signatureHeader = null; WxPayNotifyV3Result response = client.parseOrderNotifyV3Result(body, signatureHeader); WxPayNotifyV3Result.DecryptNotifyResult result = response.getResult(); // 2. 构建结果 diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialClientApi.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialClientApi.java index 3324eea09d..7306310d91 100644 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialClientApi.java +++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialClientApi.java @@ -65,4 +65,20 @@ public interface SocialClientApi { */ void sendWxaSubscribeMessage(SocialWxaSubscribeMessageSendReqDTO reqDTO); + /** + * 上传订单发货到微信小程序 + * + * @param userType 用户类型 + * @param reqDTO 请求 + */ + void uploadWxaOrderShippingInfo(Integer userType, SocialWxaOrderUploadShippingInfoReqDTO reqDTO); + + /** + * 通知订单收货到微信小程序 + * + * @param userType 用户类型 + * @param reqDTO 请求 + */ + void notifyWxaOrderConfirmReceive(Integer userType, SocialWxaOrderNotifyConfirmReceiveReqDTO reqDTO); + } diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxaOrderNotifyConfirmReceiveReqDTO.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxaOrderNotifyConfirmReceiveReqDTO.java new file mode 100644 index 0000000000..d2bf62c0d6 --- /dev/null +++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxaOrderNotifyConfirmReceiveReqDTO.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.system.api.social.dto; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * 小程序订单上传购物详情 + * + * @see 上传购物详情 + * @author 芋道源码 + */ +@Data +public class SocialWxaOrderNotifyConfirmReceiveReqDTO { + + /** + * 原支付交易对应的微信订单号 + */ + @NotEmpty(message = "原支付交易对应的微信订单号不能为空") + private String transactionId; + + /** + * 快递签收时间 + */ + @NotNull(message = "快递签收时间不能为空") + private LocalDateTime receivedTime; + +} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxaOrderUploadShippingInfoReqDTO.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxaOrderUploadShippingInfoReqDTO.java new file mode 100644 index 0000000000..555da62a4d --- /dev/null +++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxaOrderUploadShippingInfoReqDTO.java @@ -0,0 +1,67 @@ +package cn.iocoder.yudao.module.system.api.social.dto; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +/** + * 小程序订单上传购物详情 + * + * @see 上传购物详情 + * @author 芋道源码 + */ +@Data +public class SocialWxaOrderUploadShippingInfoReqDTO { + + /** + * 物流模式 - 实体物流配送采用快递公司进行实体物流配送形式 + */ + public static final Integer LOGISTICS_TYPE_EXPRESS = 1; + /** + * 物流模式 - 虚拟商品,虚拟商品,例如话费充值,点卡等,无实体配送形式 + */ + public static final Integer LOGISTICS_TYPE_VIRTUAL = 3; + /** + * 物流模式 - 用户自提 + */ + public static final Integer LOGISTICS_TYPE_PICK_UP = 4; + + /** + * 支付者,支付者信息(openid) + */ + @NotEmpty(message = "支付者,支付者信息(openid)不能为空") + private String openid; + + /** + * 原支付交易对应的微信订单号 + */ + @NotEmpty(message = "原支付交易对应的微信订单号不能为空") + private String transactionId; + + /** + * 物流模式 + */ + @NotNull(message = "物流模式不能为空") + private Integer logisticsType; + /** + * 物流发货单号 + */ + private String logisticsNo; + /** + * 物流公司编号 + * + * @see 物流查询插件简介 + */ + private String expressCompany; + /** + * 商品信息 + */ + @NotEmpty(message = "商品信息不能为空") + private String itemDesc; + /** + * 收件人手机号 + */ + @NotEmpty(message = "收件人手机号") + private String receiverContact; + +} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java index 6ed77aeef7..c89a5dcdb2 100644 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java +++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java @@ -124,10 +124,11 @@ public interface ErrorCodeConstants { ErrorCode SOCIAL_CLIENT_WEIXIN_MINI_APP_QRCODE_ERROR = new ErrorCode(1_002_018_201, "获得小程序码失败"); ErrorCode SOCIAL_CLIENT_WEIXIN_MINI_APP_SUBSCRIBE_TEMPLATE_ERROR = new ErrorCode(1_002_018_202, "获得小程序订阅消息模版失败"); ErrorCode SOCIAL_CLIENT_WEIXIN_MINI_APP_SUBSCRIBE_MESSAGE_ERROR = new ErrorCode(1_002_018_203, "发送小程序订阅消息失败"); + ErrorCode SOCIAL_CLIENT_WEIXIN_MINI_APP_ORDER_UPLOAD_SHIPPING_INFO_ERROR = new ErrorCode(1_002_018_204, "上传微信小程序发货信息失败"); + ErrorCode SOCIAL_CLIENT_WEIXIN_MINI_APP_ORDER_NOTIFY_CONFIRM_RECEIVE_ERROR = new ErrorCode(1_002_018_205, "上传微信小程序订单收货信息失败"); ErrorCode SOCIAL_CLIENT_NOT_EXISTS = new ErrorCode(1_002_018_210, "社交客户端不存在"); ErrorCode SOCIAL_CLIENT_UNIQUE = new ErrorCode(1_002_018_211, "社交客户端已存在配置"); - // ========== OAuth2 客户端 1-002-020-000 ========= ErrorCode OAUTH2_CLIENT_NOT_EXISTS = new ErrorCode(1_002_020_000, "OAuth2 客户端不存在"); ErrorCode OAUTH2_CLIENT_EXISTS = new ErrorCode(1_002_020_001, "OAuth2 客户端编号已存在"); diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialClientApiImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialClientApiImpl.java index 046fc3a0d6..d2eb00de8e 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialClientApiImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialClientApiImpl.java @@ -94,4 +94,14 @@ public class SocialClientApiImpl implements SocialClientApi { socialClientService.sendSubscribeMessage(reqDTO, template.getPriTmplId(), socialUser.getOpenid()); } + @Override + public void uploadWxaOrderShippingInfo(Integer userType, SocialWxaOrderUploadShippingInfoReqDTO reqDTO) { + socialClientService.uploadWxaOrderShippingInfo(userType, reqDTO); + } + + @Override + public void notifyWxaOrderConfirmReceive(Integer userType, SocialWxaOrderNotifyConfirmReceiveReqDTO reqDTO) { + socialClientService.notifyWxaOrderConfirmReceive(userType, reqDTO); + } + } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialClientService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialClientService.java index 40578a6cd0..91bb9c1336 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialClientService.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialClientService.java @@ -3,6 +3,8 @@ package cn.iocoder.yudao.module.system.service.social; import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.system.api.social.dto.SocialWxQrcodeReqDTO; +import cn.iocoder.yudao.module.system.api.social.dto.SocialWxaOrderNotifyConfirmReceiveReqDTO; +import cn.iocoder.yudao.module.system.api.social.dto.SocialWxaOrderUploadShippingInfoReqDTO; import cn.iocoder.yudao.module.system.api.social.dto.SocialWxaSubscribeMessageSendReqDTO; import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientPageReqVO; import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientSaveReqVO; @@ -92,6 +94,22 @@ public interface SocialClientService { */ void sendSubscribeMessage(SocialWxaSubscribeMessageSendReqDTO reqDTO, String templateId, String openId); + /** + * 上传订单发货到微信小程序 + * + * @param userType 用户类型 + * @param reqDTO 请求 + */ + void uploadWxaOrderShippingInfo(Integer userType, SocialWxaOrderUploadShippingInfoReqDTO reqDTO); + + /** + * 通知订单收货到微信小程序 + * + * @param userType 用户类型 + * @param reqDTO 请求 + */ + void notifyWxaOrderConfirmReceive(Integer userType, SocialWxaOrderNotifyConfirmReceiveReqDTO reqDTO); + // =================== 客户端管理 =================== /** diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialClientServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialClientServiceImpl.java index 4d5d13ca27..c25a28d676 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialClientServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialClientServiceImpl.java @@ -5,11 +5,15 @@ import cn.binarywang.wx.miniapp.api.WxMaSubscribeService; import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl; import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo; import cn.binarywang.wx.miniapp.bean.WxMaSubscribeMessage; +import cn.binarywang.wx.miniapp.bean.shop.request.shipping.*; +import cn.binarywang.wx.miniapp.bean.shop.response.WxMaOrderShippingInfoBaseResponse; import cn.binarywang.wx.miniapp.config.impl.WxMaRedisBetterConfigImpl; import cn.binarywang.wx.miniapp.constant.WxMaConstants; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.LocalDateTimeUtil; import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.DesensitizedUtil; import cn.hutool.core.util.ObjUtil; import cn.hutool.core.util.ReflectUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; @@ -19,6 +23,8 @@ import cn.iocoder.yudao.framework.common.util.cache.CacheUtils; import cn.iocoder.yudao.framework.common.util.http.HttpUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.system.api.social.dto.SocialWxQrcodeReqDTO; +import cn.iocoder.yudao.module.system.api.social.dto.SocialWxaOrderNotifyConfirmReceiveReqDTO; +import cn.iocoder.yudao.module.system.api.social.dto.SocialWxaOrderUploadShippingInfoReqDTO; import cn.iocoder.yudao.module.system.api.social.dto.SocialWxaSubscribeMessageSendReqDTO; import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientPageReqVO; import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientSaveReqVO; @@ -54,14 +60,17 @@ import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Service; import java.time.Duration; +import java.time.LocalDateTime; import java.util.List; import java.util.Map; import java.util.Objects; +import static cn.hutool.core.date.DatePattern.UTC_MS_WITH_XXX_OFFSET_PATTERN; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen; import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; +import static java.util.Collections.singletonList; /** * 社交应用 Service 实现类 @@ -327,6 +336,64 @@ public class SocialClientServiceImpl implements SocialClientService { return subscribeMessage; } + @Override + public void uploadWxaOrderShippingInfo(Integer userType, SocialWxaOrderUploadShippingInfoReqDTO reqDTO) { + WxMaService service = getWxMaService(userType); + List shippingList; + if (Objects.equals(reqDTO.getLogisticsType(), SocialWxaOrderUploadShippingInfoReqDTO.LOGISTICS_TYPE_EXPRESS)) { + shippingList = singletonList(ShippingListBean.builder() + .trackingNo(reqDTO.getLogisticsNo()) + .expressCompany(reqDTO.getExpressCompany()) + .itemDesc(reqDTO.getItemDesc()) + .contact(ContactBean.builder().receiverContact(DesensitizedUtil.mobilePhone(reqDTO.getReceiverContact())).build()) + .build()); + } else { + shippingList = singletonList(ShippingListBean.builder().itemDesc(reqDTO.getItemDesc()).build()); + } + WxMaOrderShippingInfoUploadRequest request = WxMaOrderShippingInfoUploadRequest.builder() + .orderKey(OrderKeyBean.builder() + .orderNumberType(2) // 使用原支付交易对应的微信订单号,即渠道单号 + .transactionId(reqDTO.getTransactionId()) + .build()) + .logisticsType(reqDTO.getLogisticsType()) // 配送方式 + .deliveryMode(1) // 统一发货 + .shippingList(shippingList) + .payer(PayerBean.builder().openid(reqDTO.getOpenid()).build()) + .uploadTime(LocalDateTimeUtil.format(LocalDateTime.now(), UTC_MS_WITH_XXX_OFFSET_PATTERN)) + .build(); + try { + WxMaOrderShippingInfoBaseResponse response = service.getWxMaOrderShippingService().upload(request); + if (response.getErrCode() != 0) { + log.error("[uploadWxaOrderShippingInfo][上传微信小程序发货信息失败:request({}) response({})]", request, response); + throw exception(SOCIAL_CLIENT_WEIXIN_MINI_APP_ORDER_UPLOAD_SHIPPING_INFO_ERROR, response.getErrMsg()); + } + log.info("[uploadWxaOrderShippingInfo][上传微信小程序发货信息成功:request({}) response({})]", request, response); + } catch (WxErrorException ex) { + log.error("[uploadWxaOrderShippingInfo][上传微信小程序发货信息失败:request({})]", request, ex); + throw exception(SOCIAL_CLIENT_WEIXIN_MINI_APP_ORDER_UPLOAD_SHIPPING_INFO_ERROR, ex.getError().getErrorMsg()); + } + } + + @Override + public void notifyWxaOrderConfirmReceive(Integer userType, SocialWxaOrderNotifyConfirmReceiveReqDTO reqDTO) { + WxMaService service = getWxMaService(userType); + WxMaOrderShippingInfoNotifyConfirmRequest request = WxMaOrderShippingInfoNotifyConfirmRequest.builder() + .transactionId(reqDTO.getTransactionId()) + .receivedTime(LocalDateTimeUtil.toEpochMilli(reqDTO.getReceivedTime())) + .build(); + try { + WxMaOrderShippingInfoBaseResponse response = service.getWxMaOrderShippingService().notifyConfirmReceive(request); + if (response.getErrCode() != 0) { + log.error("[notifyWxaOrderConfirmReceive][确认收货提醒到微信小程序失败:request({}) response({})]", request, response); + throw exception(SOCIAL_CLIENT_WEIXIN_MINI_APP_ORDER_NOTIFY_CONFIRM_RECEIVE_ERROR, response.getErrMsg()); + } + log.info("[notifyWxaOrderConfirmReceive][确认收货提醒到微信小程序成功:request({}) response({})]", request, response); + } catch (WxErrorException ex) { + log.error("[notifyWxaOrderConfirmReceive][确认收货提醒到微信小程序失败:request({})]", request, ex); + throw exception(SOCIAL_CLIENT_WEIXIN_MINI_APP_ORDER_NOTIFY_CONFIRM_RECEIVE_ERROR, ex.getError().getErrorMsg()); + } + } + /** * 获得 clientId + clientSecret 对应的 WxMpService 对象 * diff --git a/yudao-server/src/main/resources/application.yaml b/yudao-server/src/main/resources/application.yaml index 14a6f9faa5..bdb30b49f0 100644 --- a/yudao-server/src/main/resources/application.yaml +++ b/yudao-server/src/main/resources/application.yaml @@ -299,6 +299,7 @@ yudao: pay-expire-time: 2h # 支付的过期时间 receive-expire-time: 14d # 收货的过期时间 comment-expire-time: 7d # 评论的过期时间 + status-sync-to-wxa-enable: true # 是否同步订单状态到微信小程序 express: client: kd_100 kd-niao: