Commit a34de9f2239cedfcba3ff189c76df36bb6c7b615
1 parent
9847d4cd
feat:【MALL 商城】增加微信物流的对接(和社区同学,一起测试中。。。)
Showing
15 changed files
with
396 additions
and
5 deletions
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/order/config/TradeOrderProperties.java
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 { |
| 401 | 401 | .setOrderId(order.getId()).setUserId(order.getUserId()).setMessage(null)); |
| 402 | 402 | // 4.2 发送订阅消息 |
| 403 | 403 | getSelf().sendDeliveryOrderMessage(order, deliveryReqVO); |
| 404 | + | |
| 405 | + // 5. 处理订单发货后逻辑 | |
| 406 | + order.setLogisticsId(updateOrderObj.getLogisticsId()).setLogisticsNo(updateOrderObj.getLogisticsNo()) | |
| 407 | + .setStatus(updateOrderObj.getStatus()).setDeliveryTime(updateOrderObj.getDeliveryTime()); | |
| 408 | + tradeOrderHandlers.forEach(handler -> handler.afterDeliveryOrder(order)); | |
| 404 | 409 | } |
| 405 | 410 | |
| 406 | 411 | @Async |
| ... | ... | @@ -499,15 +504,20 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { |
| 499 | 504 | * @param order 订单 |
| 500 | 505 | */ |
| 501 | 506 | private void receiveOrder0(TradeOrderDO order) { |
| 502 | - // 更新 TradeOrderDO 状态为已完成 | |
| 507 | + // 1. 更新 TradeOrderDO 状态为已完成 | |
| 508 | + LocalDateTime receiveTime = LocalDateTime.now(); | |
| 503 | 509 | int updateCount = tradeOrderMapper.updateByIdAndStatus(order.getId(), order.getStatus(), |
| 504 | - new TradeOrderDO().setStatus(TradeOrderStatusEnum.COMPLETED.getStatus()).setReceiveTime(LocalDateTime.now())); | |
| 510 | + new TradeOrderDO().setStatus(TradeOrderStatusEnum.COMPLETED.getStatus()).setReceiveTime(receiveTime)); | |
| 505 | 511 | if (updateCount == 0) { |
| 506 | 512 | throw exception(ORDER_RECEIVE_FAIL_STATUS_NOT_DELIVERED); |
| 507 | 513 | } |
| 508 | 514 | |
| 509 | - // 插入订单日志 | |
| 515 | + // 2. 插入订单日志 | |
| 510 | 516 | TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), TradeOrderStatusEnum.COMPLETED.getStatus()); |
| 517 | + | |
| 518 | + // 3. 执行 TradeOrderHandler 后置处理 | |
| 519 | + order.setStatus(TradeOrderStatusEnum.COMPLETED.getStatus()).setReceiveTime(receiveTime); | |
| 520 | + tradeOrderHandlers.forEach(handler -> handler.afterReceiveOrder(order)); | |
| 511 | 521 | } |
| 512 | 522 | |
| 513 | 523 | /** | ... | ... |
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 { |
| 62 | 62 | */ |
| 63 | 63 | default void beforeDeliveryOrder(TradeOrderDO order) {} |
| 64 | 64 | |
| 65 | + /** | |
| 66 | + * 订单发货后 | |
| 67 | + * | |
| 68 | + * @param order 订单 | |
| 69 | + */ | |
| 70 | + default void afterDeliveryOrder(TradeOrderDO order) {} | |
| 71 | + | |
| 72 | + /** | |
| 73 | + * 订单收货前 | |
| 74 | + * | |
| 75 | + * @param order 订单 | |
| 76 | + */ | |
| 77 | + default void beforeReceiveOrder(TradeOrderDO order) {} | |
| 78 | + | |
| 79 | + /** | |
| 80 | + * 订单收货后 | |
| 81 | + * | |
| 82 | + * @param order 订单 | |
| 83 | + */ | |
| 84 | + default void afterReceiveOrder(TradeOrderDO order) {} | |
| 85 | + | |
| 65 | 86 | // ========== 公用方法 ========== |
| 66 | 87 | |
| 67 | 88 | /** | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeStatusSyncToWxaOrderHandler.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.trade.service.order.handler; | |
| 2 | + | |
| 3 | +import cn.hutool.core.util.ObjUtil; | |
| 4 | +import cn.hutool.core.util.StrUtil; | |
| 5 | +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; | |
| 6 | +import cn.iocoder.yudao.module.pay.api.order.PayOrderApi; | |
| 7 | +import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO; | |
| 8 | +import cn.iocoder.yudao.module.pay.enums.PayChannelEnum; | |
| 9 | +import cn.iocoder.yudao.module.system.api.social.SocialClientApi; | |
| 10 | +import cn.iocoder.yudao.module.system.api.social.dto.SocialWxaOrderNotifyConfirmReceiveReqDTO; | |
| 11 | +import cn.iocoder.yudao.module.system.api.social.dto.SocialWxaOrderUploadShippingInfoReqDTO; | |
| 12 | +import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; | |
| 13 | +import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum; | |
| 14 | +import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService; | |
| 15 | +import jakarta.annotation.Resource; | |
| 16 | +import lombok.extern.slf4j.Slf4j; | |
| 17 | +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | |
| 18 | +import org.springframework.stereotype.Component; | |
| 19 | + | |
| 20 | +/** | |
| 21 | + * 同步订单状态到微信小程序的 {@link TradeOrderHandler} 实现类 | |
| 22 | + * | |
| 23 | + * 背景:电商类目的微信小程序需要上传发货信息,不然微信支付会被封 = =! | |
| 24 | + * 注意:微信小程序开发环境下的订单不能用来发货。只有小程序正式版才会有发货,所以体验版无法调通,提示订单不存在。注意别踩坑。 | |
| 25 | + */ | |
| 26 | +@Slf4j | |
| 27 | +@Component | |
| 28 | +@ConditionalOnProperty(prefix = "yudao.trade.order", value = "status-sync-to-wxa-enable") | |
| 29 | +public class TradeStatusSyncToWxaOrderHandler implements TradeOrderHandler { | |
| 30 | + | |
| 31 | + @Resource | |
| 32 | + private PayOrderApi payOrderApi; | |
| 33 | + @Resource | |
| 34 | + private SocialClientApi socialClientApi; | |
| 35 | + | |
| 36 | + @Resource | |
| 37 | + private DeliveryExpressService expressService; | |
| 38 | + | |
| 39 | + @Override | |
| 40 | + public void afterDeliveryOrder(TradeOrderDO order) { | |
| 41 | + // 注意:只有微信小程序支付的订单,才需要同步 | |
| 42 | + if (ObjUtil.notEqual(order.getPayChannelCode(), PayChannelEnum.WX_LITE.getCode())) { | |
| 43 | + return; | |
| 44 | + } | |
| 45 | + PayOrderRespDTO payOrder = payOrderApi.getOrder(order.getPayOrderId()); | |
| 46 | + SocialWxaOrderUploadShippingInfoReqDTO reqDTO = new SocialWxaOrderUploadShippingInfoReqDTO() | |
| 47 | + .setTransactionId(payOrder.getChannelOrderNo()) | |
| 48 | + .setOpenid(payOrder.getChannelUserId()) | |
| 49 | + .setItemDesc(payOrder.getSubject()) | |
| 50 | + .setReceiverContact(order.getReceiverMobile()); | |
| 51 | + if (DeliveryTypeEnum.EXPRESS.getType().equals(order.getDeliveryType()) && StrUtil.isNotEmpty(order.getLogisticsNo())) { | |
| 52 | + reqDTO.setLogisticsType(SocialWxaOrderUploadShippingInfoReqDTO.LOGISTICS_TYPE_EXPRESS) | |
| 53 | + .setExpressCompany(expressService.getDeliveryExpress(order.getLogisticsId()).getCode()) | |
| 54 | + .setLogisticsNo(order.getLogisticsNo()); | |
| 55 | + } else if (DeliveryTypeEnum.PICK_UP.getType().equals(order.getDeliveryType())) { | |
| 56 | + reqDTO.setLogisticsType(SocialWxaOrderUploadShippingInfoReqDTO.LOGISTICS_TYPE_PICK_UP); | |
| 57 | + } else { | |
| 58 | + reqDTO.setLogisticsType(SocialWxaOrderUploadShippingInfoReqDTO.LOGISTICS_TYPE_VIRTUAL); | |
| 59 | + } | |
| 60 | + try { | |
| 61 | + socialClientApi.uploadWxaOrderShippingInfo(UserTypeEnum.MEMBER.getValue(), reqDTO); | |
| 62 | + } catch (Exception ex) { | |
| 63 | + log.error("[afterDeliveryOrder][订单({}) 上传订单物流信息到微信小程序失败]", order, ex); | |
| 64 | + } | |
| 65 | + } | |
| 66 | + | |
| 67 | + @Override | |
| 68 | + public void afterReceiveOrder(TradeOrderDO order) { | |
| 69 | + // 注意:只有微信小程序支付的订单,才需要同步 | |
| 70 | + if (ObjUtil.notEqual(order.getPayChannelCode(), PayChannelEnum.WX_LITE.getCode())) { | |
| 71 | + return; | |
| 72 | + } | |
| 73 | + PayOrderRespDTO payOrder = payOrderApi.getOrder(order.getPayOrderId()); | |
| 74 | + SocialWxaOrderNotifyConfirmReceiveReqDTO reqDTO = new SocialWxaOrderNotifyConfirmReceiveReqDTO() | |
| 75 | + .setTransactionId(payOrder.getChannelOrderNo()) | |
| 76 | + .setReceivedTime(order.getReceiveTime()); | |
| 77 | + try { | |
| 78 | + socialClientApi.notifyWxaOrderConfirmReceive(UserTypeEnum.MEMBER.getValue(), reqDTO); | |
| 79 | + } catch (Exception ex) { | |
| 80 | + log.error("[afterReceiveOrder][订单({}) 通知订单收货到微信小程序失败]", order, ex); | |
| 81 | + } | |
| 82 | + } | |
| 83 | + | |
| 84 | + // 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 | |
| 85 | + | |
| 86 | +} | ... | ... |
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/order/dto/PayOrderRespDTO.java
| ... | ... | @@ -33,6 +33,10 @@ public class PayOrderRespDTO { |
| 33 | 33 | |
| 34 | 34 | // ========== 订单相关字段 ========== |
| 35 | 35 | /** |
| 36 | + * 商品标题 | |
| 37 | + */ | |
| 38 | + private String subject; | |
| 39 | + /** | |
| 36 | 40 | * 支付金额,单位:分 |
| 37 | 41 | */ |
| 38 | 42 | private Integer price; |
| ... | ... | @@ -50,4 +54,15 @@ public class PayOrderRespDTO { |
| 50 | 54 | |
| 51 | 55 | // ========== 渠道相关字段 ========== |
| 52 | 56 | |
| 57 | + /** | |
| 58 | + * 渠道用户编号 | |
| 59 | + * | |
| 60 | + * 例如说,微信 openid、支付宝账号 | |
| 61 | + */ | |
| 62 | + private String channelUserId; | |
| 63 | + /** | |
| 64 | + * 渠道订单号 | |
| 65 | + */ | |
| 66 | + private String channelOrderNo; | |
| 67 | + | |
| 53 | 68 | } | ... | ... |
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/PayChannelEnum.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.pay.enums; | |
| 2 | + | |
| 3 | +import lombok.AllArgsConstructor; | |
| 4 | +import lombok.Getter; | |
| 5 | + | |
| 6 | +/** | |
| 7 | + * 支付渠道的编码的枚举 | |
| 8 | + * | |
| 9 | + * @author 芋道源码 | |
| 10 | + */ | |
| 11 | +@Getter | |
| 12 | +@AllArgsConstructor | |
| 13 | +public enum PayChannelEnum { | |
| 14 | + | |
| 15 | + WX_PUB("wx_pub", "微信 JSAPI 支付"), // 公众号网页 | |
| 16 | + WX_LITE("wx_lite", "微信小程序支付"), | |
| 17 | + WX_APP("wx_app", "微信 App 支付"), | |
| 18 | + WX_NATIVE("wx_native", "微信 Native 支付"), | |
| 19 | + WX_WAP("wx_wap", "微信 Wap 网站支付"), // H5 网页 | |
| 20 | + WX_BAR("wx_bar", "微信付款码支付"), | |
| 21 | + | |
| 22 | + ALIPAY_PC("alipay_pc", "支付宝 PC 网站支付"), | |
| 23 | + ALIPAY_WAP("alipay_wap", "支付宝 Wap 网站支付"), | |
| 24 | + ALIPAY_APP("alipay_app", "支付宝App 支付"), | |
| 25 | + ALIPAY_QR("alipay_qr", "支付宝扫码支付"), | |
| 26 | + ALIPAY_BAR("alipay_bar", "支付宝条码支付"), | |
| 27 | + MOCK("mock", "模拟支付"), | |
| 28 | + | |
| 29 | + WALLET("wallet", "钱包支付"); | |
| 30 | + | |
| 31 | + /** | |
| 32 | + * 编码 | |
| 33 | + * | |
| 34 | + * 参考 <a href="https://www.pingxx.com/api/支付渠道属性值.html">支付渠道属性值</a> | |
| 35 | + */ | |
| 36 | + private final String code; | |
| 37 | + /** | |
| 38 | + * 名字 | |
| 39 | + */ | |
| 40 | + private final String name; | |
| 41 | + | |
| 42 | +} | ... | ... |
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<WxPayClientC |
| 179 | 179 | |
| 180 | 180 | private PayOrderRespDTO doParseOrderNotifyV3(String body, Map<String, String> headers) throws WxPayException { |
| 181 | 181 | // 1. 解析回调 |
| 182 | - SignatureHeader signatureHeader = getRequestHeader(headers); | |
| 182 | +// SignatureHeader signatureHeader = getRequestHeader(headers); | |
| 183 | + SignatureHeader signatureHeader = null; | |
| 183 | 184 | WxPayNotifyV3Result response = client.parseOrderNotifyV3Result(body, signatureHeader); |
| 184 | 185 | WxPayNotifyV3Result.DecryptNotifyResult result = response.getResult(); |
| 185 | 186 | // 2. 构建结果 | ... | ... |
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 { |
| 65 | 65 | */ |
| 66 | 66 | void sendWxaSubscribeMessage(SocialWxaSubscribeMessageSendReqDTO reqDTO); |
| 67 | 67 | |
| 68 | + /** | |
| 69 | + * 上传订单发货到微信小程序 | |
| 70 | + * | |
| 71 | + * @param userType 用户类型 | |
| 72 | + * @param reqDTO 请求 | |
| 73 | + */ | |
| 74 | + void uploadWxaOrderShippingInfo(Integer userType, SocialWxaOrderUploadShippingInfoReqDTO reqDTO); | |
| 75 | + | |
| 76 | + /** | |
| 77 | + * 通知订单收货到微信小程序 | |
| 78 | + * | |
| 79 | + * @param userType 用户类型 | |
| 80 | + * @param reqDTO 请求 | |
| 81 | + */ | |
| 82 | + void notifyWxaOrderConfirmReceive(Integer userType, SocialWxaOrderNotifyConfirmReceiveReqDTO reqDTO); | |
| 83 | + | |
| 68 | 84 | } | ... | ... |
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxaOrderNotifyConfirmReceiveReqDTO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.system.api.social.dto; | |
| 2 | + | |
| 3 | +import jakarta.validation.constraints.NotEmpty; | |
| 4 | +import jakarta.validation.constraints.NotNull; | |
| 5 | +import lombok.Data; | |
| 6 | + | |
| 7 | +import java.time.LocalDateTime; | |
| 8 | + | |
| 9 | +/** | |
| 10 | + * 小程序订单上传购物详情 | |
| 11 | + * | |
| 12 | + * @see <a href="https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/shopping-order/normal-shopping-detail/uploadShoppingInfo.html">上传购物详情</a> | |
| 13 | + * @author 芋道源码 | |
| 14 | + */ | |
| 15 | +@Data | |
| 16 | +public class SocialWxaOrderNotifyConfirmReceiveReqDTO { | |
| 17 | + | |
| 18 | + /** | |
| 19 | + * 原支付交易对应的微信订单号 | |
| 20 | + */ | |
| 21 | + @NotEmpty(message = "原支付交易对应的微信订单号不能为空") | |
| 22 | + private String transactionId; | |
| 23 | + | |
| 24 | + /** | |
| 25 | + * 快递签收时间 | |
| 26 | + */ | |
| 27 | + @NotNull(message = "快递签收时间不能为空") | |
| 28 | + private LocalDateTime receivedTime; | |
| 29 | + | |
| 30 | +} | ... | ... |
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxaOrderUploadShippingInfoReqDTO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.system.api.social.dto; | |
| 2 | + | |
| 3 | +import jakarta.validation.constraints.NotEmpty; | |
| 4 | +import jakarta.validation.constraints.NotNull; | |
| 5 | +import lombok.Data; | |
| 6 | + | |
| 7 | +/** | |
| 8 | + * 小程序订单上传购物详情 | |
| 9 | + * | |
| 10 | + * @see <a href="https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/shopping-order/normal-shopping-detail/uploadShoppingInfo.html">上传购物详情</a> | |
| 11 | + * @author 芋道源码 | |
| 12 | + */ | |
| 13 | +@Data | |
| 14 | +public class SocialWxaOrderUploadShippingInfoReqDTO { | |
| 15 | + | |
| 16 | + /** | |
| 17 | + * 物流模式 - 实体物流配送采用快递公司进行实体物流配送形式 | |
| 18 | + */ | |
| 19 | + public static final Integer LOGISTICS_TYPE_EXPRESS = 1; | |
| 20 | + /** | |
| 21 | + * 物流模式 - 虚拟商品,虚拟商品,例如话费充值,点卡等,无实体配送形式 | |
| 22 | + */ | |
| 23 | + public static final Integer LOGISTICS_TYPE_VIRTUAL = 3; | |
| 24 | + /** | |
| 25 | + * 物流模式 - 用户自提 | |
| 26 | + */ | |
| 27 | + public static final Integer LOGISTICS_TYPE_PICK_UP = 4; | |
| 28 | + | |
| 29 | + /** | |
| 30 | + * 支付者,支付者信息(openid) | |
| 31 | + */ | |
| 32 | + @NotEmpty(message = "支付者,支付者信息(openid)不能为空") | |
| 33 | + private String openid; | |
| 34 | + | |
| 35 | + /** | |
| 36 | + * 原支付交易对应的微信订单号 | |
| 37 | + */ | |
| 38 | + @NotEmpty(message = "原支付交易对应的微信订单号不能为空") | |
| 39 | + private String transactionId; | |
| 40 | + | |
| 41 | + /** | |
| 42 | + * 物流模式 | |
| 43 | + */ | |
| 44 | + @NotNull(message = "物流模式不能为空") | |
| 45 | + private Integer logisticsType; | |
| 46 | + /** | |
| 47 | + * 物流发货单号 | |
| 48 | + */ | |
| 49 | + private String logisticsNo; | |
| 50 | + /** | |
| 51 | + * 物流公司编号 | |
| 52 | + * | |
| 53 | + * @see <a href="https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/industry/express/business/express_search.html#%E8%8E%B7%E5%8F%96%E8%BF%90%E5%8A%9Bid%E5%88%97%E8%A1%A8get-delivery-list">物流查询插件简介</a> | |
| 54 | + */ | |
| 55 | + private String expressCompany; | |
| 56 | + /** | |
| 57 | + * 商品信息 | |
| 58 | + */ | |
| 59 | + @NotEmpty(message = "商品信息不能为空") | |
| 60 | + private String itemDesc; | |
| 61 | + /** | |
| 62 | + * 收件人手机号 | |
| 63 | + */ | |
| 64 | + @NotEmpty(message = "收件人手机号") | |
| 65 | + private String receiverContact; | |
| 66 | + | |
| 67 | +} | ... | ... |
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 { |
| 124 | 124 | ErrorCode SOCIAL_CLIENT_WEIXIN_MINI_APP_QRCODE_ERROR = new ErrorCode(1_002_018_201, "获得小程序码失败"); |
| 125 | 125 | ErrorCode SOCIAL_CLIENT_WEIXIN_MINI_APP_SUBSCRIBE_TEMPLATE_ERROR = new ErrorCode(1_002_018_202, "获得小程序订阅消息模版失败"); |
| 126 | 126 | ErrorCode SOCIAL_CLIENT_WEIXIN_MINI_APP_SUBSCRIBE_MESSAGE_ERROR = new ErrorCode(1_002_018_203, "发送小程序订阅消息失败"); |
| 127 | + ErrorCode SOCIAL_CLIENT_WEIXIN_MINI_APP_ORDER_UPLOAD_SHIPPING_INFO_ERROR = new ErrorCode(1_002_018_204, "上传微信小程序发货信息失败"); | |
| 128 | + ErrorCode SOCIAL_CLIENT_WEIXIN_MINI_APP_ORDER_NOTIFY_CONFIRM_RECEIVE_ERROR = new ErrorCode(1_002_018_205, "上传微信小程序订单收货信息失败"); | |
| 127 | 129 | ErrorCode SOCIAL_CLIENT_NOT_EXISTS = new ErrorCode(1_002_018_210, "社交客户端不存在"); |
| 128 | 130 | ErrorCode SOCIAL_CLIENT_UNIQUE = new ErrorCode(1_002_018_211, "社交客户端已存在配置"); |
| 129 | 131 | |
| 130 | - | |
| 131 | 132 | // ========== OAuth2 客户端 1-002-020-000 ========= |
| 132 | 133 | ErrorCode OAUTH2_CLIENT_NOT_EXISTS = new ErrorCode(1_002_020_000, "OAuth2 客户端不存在"); |
| 133 | 134 | ErrorCode OAUTH2_CLIENT_EXISTS = new ErrorCode(1_002_020_001, "OAuth2 客户端编号已存在"); | ... | ... |
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 { |
| 94 | 94 | socialClientService.sendSubscribeMessage(reqDTO, template.getPriTmplId(), socialUser.getOpenid()); |
| 95 | 95 | } |
| 96 | 96 | |
| 97 | + @Override | |
| 98 | + public void uploadWxaOrderShippingInfo(Integer userType, SocialWxaOrderUploadShippingInfoReqDTO reqDTO) { | |
| 99 | + socialClientService.uploadWxaOrderShippingInfo(userType, reqDTO); | |
| 100 | + } | |
| 101 | + | |
| 102 | + @Override | |
| 103 | + public void notifyWxaOrderConfirmReceive(Integer userType, SocialWxaOrderNotifyConfirmReceiveReqDTO reqDTO) { | |
| 104 | + socialClientService.notifyWxaOrderConfirmReceive(userType, reqDTO); | |
| 105 | + } | |
| 106 | + | |
| 97 | 107 | } | ... | ... |
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; |
| 3 | 3 | import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo; |
| 4 | 4 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 5 | 5 | import cn.iocoder.yudao.module.system.api.social.dto.SocialWxQrcodeReqDTO; |
| 6 | +import cn.iocoder.yudao.module.system.api.social.dto.SocialWxaOrderNotifyConfirmReceiveReqDTO; | |
| 7 | +import cn.iocoder.yudao.module.system.api.social.dto.SocialWxaOrderUploadShippingInfoReqDTO; | |
| 6 | 8 | import cn.iocoder.yudao.module.system.api.social.dto.SocialWxaSubscribeMessageSendReqDTO; |
| 7 | 9 | import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientPageReqVO; |
| 8 | 10 | import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientSaveReqVO; |
| ... | ... | @@ -92,6 +94,22 @@ public interface SocialClientService { |
| 92 | 94 | */ |
| 93 | 95 | void sendSubscribeMessage(SocialWxaSubscribeMessageSendReqDTO reqDTO, String templateId, String openId); |
| 94 | 96 | |
| 97 | + /** | |
| 98 | + * 上传订单发货到微信小程序 | |
| 99 | + * | |
| 100 | + * @param userType 用户类型 | |
| 101 | + * @param reqDTO 请求 | |
| 102 | + */ | |
| 103 | + void uploadWxaOrderShippingInfo(Integer userType, SocialWxaOrderUploadShippingInfoReqDTO reqDTO); | |
| 104 | + | |
| 105 | + /** | |
| 106 | + * 通知订单收货到微信小程序 | |
| 107 | + * | |
| 108 | + * @param userType 用户类型 | |
| 109 | + * @param reqDTO 请求 | |
| 110 | + */ | |
| 111 | + void notifyWxaOrderConfirmReceive(Integer userType, SocialWxaOrderNotifyConfirmReceiveReqDTO reqDTO); | |
| 112 | + | |
| 95 | 113 | // =================== 客户端管理 =================== |
| 96 | 114 | |
| 97 | 115 | /** | ... | ... |
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; |
| 5 | 5 | import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl; |
| 6 | 6 | import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo; |
| 7 | 7 | import cn.binarywang.wx.miniapp.bean.WxMaSubscribeMessage; |
| 8 | +import cn.binarywang.wx.miniapp.bean.shop.request.shipping.*; | |
| 9 | +import cn.binarywang.wx.miniapp.bean.shop.response.WxMaOrderShippingInfoBaseResponse; | |
| 8 | 10 | import cn.binarywang.wx.miniapp.config.impl.WxMaRedisBetterConfigImpl; |
| 9 | 11 | import cn.binarywang.wx.miniapp.constant.WxMaConstants; |
| 10 | 12 | import cn.hutool.core.bean.BeanUtil; |
| 11 | 13 | import cn.hutool.core.collection.CollUtil; |
| 14 | +import cn.hutool.core.date.LocalDateTimeUtil; | |
| 12 | 15 | import cn.hutool.core.lang.Assert; |
| 16 | +import cn.hutool.core.util.DesensitizedUtil; | |
| 13 | 17 | import cn.hutool.core.util.ObjUtil; |
| 14 | 18 | import cn.hutool.core.util.ReflectUtil; |
| 15 | 19 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; |
| ... | ... | @@ -19,6 +23,8 @@ import cn.iocoder.yudao.framework.common.util.cache.CacheUtils; |
| 19 | 23 | import cn.iocoder.yudao.framework.common.util.http.HttpUtils; |
| 20 | 24 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 21 | 25 | import cn.iocoder.yudao.module.system.api.social.dto.SocialWxQrcodeReqDTO; |
| 26 | +import cn.iocoder.yudao.module.system.api.social.dto.SocialWxaOrderNotifyConfirmReceiveReqDTO; | |
| 27 | +import cn.iocoder.yudao.module.system.api.social.dto.SocialWxaOrderUploadShippingInfoReqDTO; | |
| 22 | 28 | import cn.iocoder.yudao.module.system.api.social.dto.SocialWxaSubscribeMessageSendReqDTO; |
| 23 | 29 | import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientPageReqVO; |
| 24 | 30 | import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientSaveReqVO; |
| ... | ... | @@ -54,14 +60,17 @@ import org.springframework.data.redis.core.StringRedisTemplate; |
| 54 | 60 | import org.springframework.stereotype.Service; |
| 55 | 61 | |
| 56 | 62 | import java.time.Duration; |
| 63 | +import java.time.LocalDateTime; | |
| 57 | 64 | import java.util.List; |
| 58 | 65 | import java.util.Map; |
| 59 | 66 | import java.util.Objects; |
| 60 | 67 | |
| 68 | +import static cn.hutool.core.date.DatePattern.UTC_MS_WITH_XXX_OFFSET_PATTERN; | |
| 61 | 69 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 62 | 70 | import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen; |
| 63 | 71 | import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; |
| 64 | 72 | import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; |
| 73 | +import static java.util.Collections.singletonList; | |
| 65 | 74 | |
| 66 | 75 | /** |
| 67 | 76 | * 社交应用 Service 实现类 |
| ... | ... | @@ -327,6 +336,64 @@ public class SocialClientServiceImpl implements SocialClientService { |
| 327 | 336 | return subscribeMessage; |
| 328 | 337 | } |
| 329 | 338 | |
| 339 | + @Override | |
| 340 | + public void uploadWxaOrderShippingInfo(Integer userType, SocialWxaOrderUploadShippingInfoReqDTO reqDTO) { | |
| 341 | + WxMaService service = getWxMaService(userType); | |
| 342 | + List<ShippingListBean> shippingList; | |
| 343 | + if (Objects.equals(reqDTO.getLogisticsType(), SocialWxaOrderUploadShippingInfoReqDTO.LOGISTICS_TYPE_EXPRESS)) { | |
| 344 | + shippingList = singletonList(ShippingListBean.builder() | |
| 345 | + .trackingNo(reqDTO.getLogisticsNo()) | |
| 346 | + .expressCompany(reqDTO.getExpressCompany()) | |
| 347 | + .itemDesc(reqDTO.getItemDesc()) | |
| 348 | + .contact(ContactBean.builder().receiverContact(DesensitizedUtil.mobilePhone(reqDTO.getReceiverContact())).build()) | |
| 349 | + .build()); | |
| 350 | + } else { | |
| 351 | + shippingList = singletonList(ShippingListBean.builder().itemDesc(reqDTO.getItemDesc()).build()); | |
| 352 | + } | |
| 353 | + WxMaOrderShippingInfoUploadRequest request = WxMaOrderShippingInfoUploadRequest.builder() | |
| 354 | + .orderKey(OrderKeyBean.builder() | |
| 355 | + .orderNumberType(2) // 使用原支付交易对应的微信订单号,即渠道单号 | |
| 356 | + .transactionId(reqDTO.getTransactionId()) | |
| 357 | + .build()) | |
| 358 | + .logisticsType(reqDTO.getLogisticsType()) // 配送方式 | |
| 359 | + .deliveryMode(1) // 统一发货 | |
| 360 | + .shippingList(shippingList) | |
| 361 | + .payer(PayerBean.builder().openid(reqDTO.getOpenid()).build()) | |
| 362 | + .uploadTime(LocalDateTimeUtil.format(LocalDateTime.now(), UTC_MS_WITH_XXX_OFFSET_PATTERN)) | |
| 363 | + .build(); | |
| 364 | + try { | |
| 365 | + WxMaOrderShippingInfoBaseResponse response = service.getWxMaOrderShippingService().upload(request); | |
| 366 | + if (response.getErrCode() != 0) { | |
| 367 | + log.error("[uploadWxaOrderShippingInfo][上传微信小程序发货信息失败:request({}) response({})]", request, response); | |
| 368 | + throw exception(SOCIAL_CLIENT_WEIXIN_MINI_APP_ORDER_UPLOAD_SHIPPING_INFO_ERROR, response.getErrMsg()); | |
| 369 | + } | |
| 370 | + log.info("[uploadWxaOrderShippingInfo][上传微信小程序发货信息成功:request({}) response({})]", request, response); | |
| 371 | + } catch (WxErrorException ex) { | |
| 372 | + log.error("[uploadWxaOrderShippingInfo][上传微信小程序发货信息失败:request({})]", request, ex); | |
| 373 | + throw exception(SOCIAL_CLIENT_WEIXIN_MINI_APP_ORDER_UPLOAD_SHIPPING_INFO_ERROR, ex.getError().getErrorMsg()); | |
| 374 | + } | |
| 375 | + } | |
| 376 | + | |
| 377 | + @Override | |
| 378 | + public void notifyWxaOrderConfirmReceive(Integer userType, SocialWxaOrderNotifyConfirmReceiveReqDTO reqDTO) { | |
| 379 | + WxMaService service = getWxMaService(userType); | |
| 380 | + WxMaOrderShippingInfoNotifyConfirmRequest request = WxMaOrderShippingInfoNotifyConfirmRequest.builder() | |
| 381 | + .transactionId(reqDTO.getTransactionId()) | |
| 382 | + .receivedTime(LocalDateTimeUtil.toEpochMilli(reqDTO.getReceivedTime())) | |
| 383 | + .build(); | |
| 384 | + try { | |
| 385 | + WxMaOrderShippingInfoBaseResponse response = service.getWxMaOrderShippingService().notifyConfirmReceive(request); | |
| 386 | + if (response.getErrCode() != 0) { | |
| 387 | + log.error("[notifyWxaOrderConfirmReceive][确认收货提醒到微信小程序失败:request({}) response({})]", request, response); | |
| 388 | + throw exception(SOCIAL_CLIENT_WEIXIN_MINI_APP_ORDER_NOTIFY_CONFIRM_RECEIVE_ERROR, response.getErrMsg()); | |
| 389 | + } | |
| 390 | + log.info("[notifyWxaOrderConfirmReceive][确认收货提醒到微信小程序成功:request({}) response({})]", request, response); | |
| 391 | + } catch (WxErrorException ex) { | |
| 392 | + log.error("[notifyWxaOrderConfirmReceive][确认收货提醒到微信小程序失败:request({})]", request, ex); | |
| 393 | + throw exception(SOCIAL_CLIENT_WEIXIN_MINI_APP_ORDER_NOTIFY_CONFIRM_RECEIVE_ERROR, ex.getError().getErrorMsg()); | |
| 394 | + } | |
| 395 | + } | |
| 396 | + | |
| 330 | 397 | /** |
| 331 | 398 | * 获得 clientId + clientSecret 对应的 WxMpService 对象 |
| 332 | 399 | * | ... | ... |
yudao-server/src/main/resources/application.yaml