feat:【MALL 商城】增加微信物流的对接(和社区同学,一起测试中。。。)

This commit is contained in:
YunaiV 2025-05-06 20:49:13 +08:00
parent 9847d4cdb1
commit a34de9f223
15 changed files with 396 additions and 5 deletions

View File

@ -48,4 +48,10 @@ public class TradeOrderProperties {
@NotNull(message = "评论超时时间不能为空") @NotNull(message = "评论超时时间不能为空")
private Duration commentExpireTime; private Duration commentExpireTime;
/**
* 是否同步订单状态到微信小程序
*/
@NotNull(message = "是否同步订单状态到微信小程序不能为空")
private Boolean statusSyncToWxaEnable;
} }

View File

@ -401,6 +401,11 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
.setOrderId(order.getId()).setUserId(order.getUserId()).setMessage(null)); .setOrderId(order.getId()).setUserId(order.getUserId()).setMessage(null));
// 4.2 发送订阅消息 // 4.2 发送订阅消息
getSelf().sendDeliveryOrderMessage(order, deliveryReqVO); 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 @Async
@ -499,15 +504,20 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
* @param order 订单 * @param order 订单
*/ */
private void receiveOrder0(TradeOrderDO order) { private void receiveOrder0(TradeOrderDO order) {
// 更新 TradeOrderDO 状态为已完成 // 1. 更新 TradeOrderDO 状态为已完成
LocalDateTime receiveTime = LocalDateTime.now();
int updateCount = tradeOrderMapper.updateByIdAndStatus(order.getId(), order.getStatus(), 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) { if (updateCount == 0) {
throw exception(ORDER_RECEIVE_FAIL_STATUS_NOT_DELIVERED); throw exception(ORDER_RECEIVE_FAIL_STATUS_NOT_DELIVERED);
} }
// 插入订单日志 // 2. 插入订单日志
TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), TradeOrderStatusEnum.COMPLETED.getStatus()); TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), TradeOrderStatusEnum.COMPLETED.getStatus());
// 3. 执行 TradeOrderHandler 后置处理
order.setStatus(TradeOrderStatusEnum.COMPLETED.getStatus()).setReceiveTime(receiveTime);
tradeOrderHandlers.forEach(handler -> handler.afterReceiveOrder(order));
} }
/** /**

View File

@ -62,6 +62,27 @@ public interface TradeOrderHandler {
*/ */
default void beforeDeliveryOrder(TradeOrderDO order) {} 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) {}
// ========== 公用方法 ========== // ========== 公用方法 ==========
/** /**

View File

@ -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
}

View File

@ -32,6 +32,10 @@ public class PayOrderRespDTO {
private String merchantOrderId; private String merchantOrderId;
// ========== 订单相关字段 ========== // ========== 订单相关字段 ==========
/**
* 商品标题
*/
private String subject;
/** /**
* 支付金额单位 * 支付金额单位
*/ */
@ -50,4 +54,15 @@ public class PayOrderRespDTO {
// ========== 渠道相关字段 ========== // ========== 渠道相关字段 ==========
/**
* 渠道用户编号
*
* 例如说微信 openid支付宝账号
*/
private String channelUserId;
/**
* 渠道订单号
*/
private String channelOrderNo;
} }

View File

@ -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", "钱包支付");
/**
* 编码
*
* 参考 <a href="https://www.pingxx.com/api/支付渠道属性值.html">支付渠道属性值</a>
*/
private final String code;
/**
* 名字
*/
private final String name;
}

View File

@ -179,7 +179,8 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
private PayOrderRespDTO doParseOrderNotifyV3(String body, Map<String, String> headers) throws WxPayException { private PayOrderRespDTO doParseOrderNotifyV3(String body, Map<String, String> headers) throws WxPayException {
// 1. 解析回调 // 1. 解析回调
SignatureHeader signatureHeader = getRequestHeader(headers); // SignatureHeader signatureHeader = getRequestHeader(headers);
SignatureHeader signatureHeader = null;
WxPayNotifyV3Result response = client.parseOrderNotifyV3Result(body, signatureHeader); WxPayNotifyV3Result response = client.parseOrderNotifyV3Result(body, signatureHeader);
WxPayNotifyV3Result.DecryptNotifyResult result = response.getResult(); WxPayNotifyV3Result.DecryptNotifyResult result = response.getResult();
// 2. 构建结果 // 2. 构建结果

View File

@ -65,4 +65,20 @@ public interface SocialClientApi {
*/ */
void sendWxaSubscribeMessage(SocialWxaSubscribeMessageSendReqDTO reqDTO); void sendWxaSubscribeMessage(SocialWxaSubscribeMessageSendReqDTO reqDTO);
/**
* 上传订单发货到微信小程序
*
* @param userType 用户类型
* @param reqDTO 请求
*/
void uploadWxaOrderShippingInfo(Integer userType, SocialWxaOrderUploadShippingInfoReqDTO reqDTO);
/**
* 通知订单收货到微信小程序
*
* @param userType 用户类型
* @param reqDTO 请求
*/
void notifyWxaOrderConfirmReceive(Integer userType, SocialWxaOrderNotifyConfirmReceiveReqDTO reqDTO);
} }

View File

@ -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 <a href="https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/shopping-order/normal-shopping-detail/uploadShoppingInfo.html">上传购物详情</a>
* @author 芋道源码
*/
@Data
public class SocialWxaOrderNotifyConfirmReceiveReqDTO {
/**
* 原支付交易对应的微信订单号
*/
@NotEmpty(message = "原支付交易对应的微信订单号不能为空")
private String transactionId;
/**
* 快递签收时间
*/
@NotNull(message = "快递签收时间不能为空")
private LocalDateTime receivedTime;
}

View File

@ -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 <a href="https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/shopping-order/normal-shopping-detail/uploadShoppingInfo.html">上传购物详情</a>
* @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 <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>
*/
private String expressCompany;
/**
* 商品信息
*/
@NotEmpty(message = "商品信息不能为空")
private String itemDesc;
/**
* 收件人手机号
*/
@NotEmpty(message = "收件人手机号")
private String receiverContact;
}

View File

@ -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_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_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_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_NOT_EXISTS = new ErrorCode(1_002_018_210, "社交客户端不存在");
ErrorCode SOCIAL_CLIENT_UNIQUE = new ErrorCode(1_002_018_211, "社交客户端已存在配置"); ErrorCode SOCIAL_CLIENT_UNIQUE = new ErrorCode(1_002_018_211, "社交客户端已存在配置");
// ========== OAuth2 客户端 1-002-020-000 ========= // ========== OAuth2 客户端 1-002-020-000 =========
ErrorCode OAUTH2_CLIENT_NOT_EXISTS = new ErrorCode(1_002_020_000, "OAuth2 客户端不存在"); ErrorCode OAUTH2_CLIENT_NOT_EXISTS = new ErrorCode(1_002_020_000, "OAuth2 客户端不存在");
ErrorCode OAUTH2_CLIENT_EXISTS = new ErrorCode(1_002_020_001, "OAuth2 客户端编号已存在"); ErrorCode OAUTH2_CLIENT_EXISTS = new ErrorCode(1_002_020_001, "OAuth2 客户端编号已存在");

View File

@ -94,4 +94,14 @@ public class SocialClientApiImpl implements SocialClientApi {
socialClientService.sendSubscribeMessage(reqDTO, template.getPriTmplId(), socialUser.getOpenid()); 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);
}
} }

View File

@ -3,6 +3,8 @@ package cn.iocoder.yudao.module.system.service.social;
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo; import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
import cn.iocoder.yudao.framework.common.pojo.PageResult; 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.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.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.SocialClientPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientSaveReqVO; 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); 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);
// =================== 客户端管理 =================== // =================== 客户端管理 ===================
/** /**

View File

@ -5,11 +5,15 @@ import cn.binarywang.wx.miniapp.api.WxMaSubscribeService;
import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl; import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl;
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo; import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
import cn.binarywang.wx.miniapp.bean.WxMaSubscribeMessage; 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.config.impl.WxMaRedisBetterConfigImpl;
import cn.binarywang.wx.miniapp.constant.WxMaConstants; import cn.binarywang.wx.miniapp.constant.WxMaConstants;
import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.DesensitizedUtil;
import cn.hutool.core.util.ObjUtil; import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.ReflectUtil; import cn.hutool.core.util.ReflectUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 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.http.HttpUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 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.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.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.SocialClientPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientSaveReqVO; 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 org.springframework.stereotype.Service;
import java.time.Duration; import java.time.Duration;
import java.time.LocalDateTime;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; 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.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen; 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.framework.common.util.json.JsonUtils.toJsonString;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
import static java.util.Collections.singletonList;
/** /**
* 社交应用 Service 实现类 * 社交应用 Service 实现类
@ -327,6 +336,64 @@ public class SocialClientServiceImpl implements SocialClientService {
return subscribeMessage; return subscribeMessage;
} }
@Override
public void uploadWxaOrderShippingInfo(Integer userType, SocialWxaOrderUploadShippingInfoReqDTO reqDTO) {
WxMaService service = getWxMaService(userType);
List<ShippingListBean> 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 对象 * 获得 clientId + clientSecret 对应的 WxMpService 对象
* *

View File

@ -299,6 +299,7 @@ yudao:
pay-expire-time: 2h # 支付的过期时间 pay-expire-time: 2h # 支付的过期时间
receive-expire-time: 14d # 收货的过期时间 receive-expire-time: 14d # 收货的过期时间
comment-expire-time: 7d # 评论的过期时间 comment-expire-time: 7d # 评论的过期时间
status-sync-to-wxa-enable: true # 是否同步订单状态到微信小程序
express: express:
client: kd_100 client: kd_100
kd-niao: kd-niao: