Pre Merge pull request !145 from Lcp/pr

pull/145/MERGE
Lcp 2024-10-13 09:03:52 +00:00 committed by Gitee
commit 8ea2d47fba
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
19 changed files with 562 additions and 4 deletions

View File

@ -129,6 +129,23 @@
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-spring-boot-starter-monitor</artifactId>
</dependency>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>wx-java-mp-spring-boot-starter</artifactId> <!-- 微信登录(公众号) -->
</dependency>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>wx-java-miniapp-spring-boot-starter</artifactId> <!-- 微信登录(小程序) -->
</dependency>
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-spring-boot-starter-biz-pay</artifactId>
<version>2.2.0-snapshot</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>

View File

@ -0,0 +1,144 @@
package cn.iocoder.yudao.module.trade.controller.admin.wx;
import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaMessage;
import cn.binarywang.wx.miniapp.message.WxMaMessageHandler;
import cn.binarywang.wx.miniapp.message.WxMaMessageRouter;
import cn.binarywang.wx.miniapp.message.WxMaXmlOutMessage;
import cn.binarywang.wx.miniapp.util.crypt.WxMaCryptUtils;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.module.pay.api.order.PayOrderApi;
import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
import cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService;
import cn.iocoder.yudao.module.trade.service.order.TradeOrderUpdateService;
import com.fasterxml.jackson.core.type.TypeReference;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import jakarta.annotation.security.PermitAll;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.session.WxSessionManager;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
/**
*
* <p>
* <a href="https://developers.weixin.qq.com/miniprogram/dev/framework/server-ability/message-push.html"></a>
*/
@RestController
@RequestMapping("/trade/wx")
@Slf4j
public class wxMaMessageController {
@Resource
private WxMaService wxMaService;
@Resource
private TradeOrderQueryService tradeOrderQueryService;
@Resource
private TradeOrderUpdateService tradeOrderUpdateService;
@Resource
private PayOrderApi payOrderApi;
private WxMaMessageRouter router;
@PostConstruct
private void initRouter() {
router = new WxMaMessageRouter(wxMaService);
router
.rule().async(false).event("trade_manage_order_settlement").handler(TradeManageOrderSettlementMessageHandler).end();
}
@RequestMapping("/message")
@Operation(summary = "接收微信推送消息")
@PermitAll
public ResponseEntity<?> receiver(@RequestBody(required = false) String body, @RequestParam Map<String, String> params) {
String signature = params.get("signature");
String nonce = params.get("nonce");
String timestamp = params.get("timestamp");
// 检查消息签名
if (!wxMaService.checkSignature(timestamp, nonce, signature)) {
return ResponseEntity.ok("非法请求");
}
log.info("[wxMaMessageController][收到微信推送消息 请求参数({}) 请求体({})]", body, params);
//开发者服务器验证请求
if (params.containsKey("echostr")) {
return ResponseEntity.ok(params.get("echostr"));
}
WxMaMessage message;
Map<String, Object> context;
if ("JSON".equals(wxMaService.getWxMaConfig().getMsgDataFormat())) {
message = WxMaMessage.fromJson(body);
//明文传输
if (!params.containsKey("encrypt_type")) {
context = JsonUtils.parseObject(body, new TypeReference<Map<String, Object>>() {
});
} else {//加密传输
String decrypt = new WxMaCryptUtils(wxMaService.getWxMaConfig()).decrypt(message.getEncrypt());
context = JsonUtils.parseObject(decrypt, new TypeReference<Map<String, Object>>() {
});
}
} else {
//明文传输
if (!params.containsKey("encrypt_type")) {
message = WxMaMessage.fromXml(body);
} else {//加密传输
String msgSignature = params.get("msg_signature");
message = WxMaMessage.fromEncryptedXml(body, wxMaService.getWxMaConfig(), timestamp, nonce, msgSignature);
}
context = message.getAllFieldsMap();
}
router.route(message, context);
return ResponseEntity.ok("");
}
/**
* trade_manage_order_settlement
* <p>
* <a href="https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/business-capabilities/order-shipping/order-shipping.html"></a>
*/
private final WxMaMessageHandler TradeManageOrderSettlementMessageHandler = new WxMaMessageHandler() {
@Override
public WxMaXmlOutMessage handle(WxMaMessage message, Map<String, Object> context, WxMaService wxMaService, WxSessionManager wxSessionManager) {
log.info("[wxMessageHandler][收到(trade_manage_order_settlement)类型事件推送]");
String transactionId = context.get("transaction_id").toString();
//包含用户收货时间
if (context.containsKey("confirm_receive_time")) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//确认收货时间
String confirmReceiveTime = dateFormat.format(new Date(Long.parseLong(context.get("confirm_receive_time").toString()) * 1000));
//收货方式(1-自动确认,2-手动确认)
Integer confirmReceiveMethod = Integer.parseInt(context.get("confirm_receive_method").toString());
//用交易单号获取支付订单
log.info("[wxMessageHandler][收到确认收货信息 TransactionId({}) 收货时间({}) 收货方式({})]", transactionId, confirmReceiveTime, confirmReceiveMethod);
System.out.println("进入处理收货:" + transactionId);
PayOrderRespDTO payOrder = payOrderApi.getOrder(transactionId).getCheckedData();
//用支付订单获取交易订单
TradeOrderDO tradeOrderDO = tradeOrderQueryService.getOrder(Long.parseLong(payOrder.getMerchantOrderId()));
//复用会员收货逻辑
tradeOrderUpdateService.receiveOrderByMember(tradeOrderDO.getUserId(), tradeOrderDO.getId());
//TODO: 是否需要同步微信端的收货时间和系统的收货时间
}
return null;
}
};
}

View File

@ -2,7 +2,10 @@ package cn.iocoder.yudao.module.trade.controller.app.order;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
import cn.iocoder.yudao.module.pay.api.notify.dto.PayOrderNotifyReqDTO;
import cn.iocoder.yudao.module.pay.api.order.PayOrderApi;
import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO;
import cn.iocoder.yudao.module.trade.controller.app.order.vo.*;
import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderItemCommentCreateReqVO;
import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderItemRespVO;
@ -11,6 +14,7 @@ import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;
import cn.iocoder.yudao.module.trade.framework.delivery.wx.WxMaOrderShippingProperties;
import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;
import cn.iocoder.yudao.module.trade.service.aftersale.AfterSaleService;
import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService;
@ -54,6 +58,12 @@ public class AppTradeOrderController {
@Resource
private TradePriceService priceService;
@Resource
private PayOrderApi payOrderApi;
@Resource
private WxMaOrderShippingProperties wxMaOrderShippingProperties;
@Resource
private TradeOrderProperties tradeOrderProperties;
@ -113,8 +123,13 @@ public class AppTradeOrderController {
// 2.2 查询物流公司
DeliveryExpressDO express = order.getLogisticsId() != null && order.getLogisticsId() > 0 ?
deliveryExpressService.getDeliveryExpress(order.getLogisticsId()) : null;
//小程序订单查询渠道项
PayOrderRespDTO payOrder = PayChannelEnum.WX_LITE.getCode().equals(order.getPayChannelCode())?
payOrderApi.getOrder(order.getPayOrderId()).getCheckedData() : null;
// 2.3 最终组合
return success(TradeOrderConvert.INSTANCE.convert02(order, orderItems, tradeOrderProperties, express));
return success(TradeOrderConvert.INSTANCE.convert02(order, orderItems, tradeOrderProperties, express, payOrder,
wxMaOrderShippingProperties.getIsMaTradeManaged()));
}
@GetMapping("/get-express-track-list")
@ -168,6 +183,14 @@ public class AppTradeOrderController {
return success(true);
}
@PutMapping("/wx-receive")
@Operation(summary = "微信小程序确认交易订单收货")
public CommonResult<Boolean> receiveWxOrder(@RequestParam("channelOrderNo") String channelOrderNo) {
PayOrderRespDTO payOrder = payOrderApi.getOrder(channelOrderNo).getCheckedData();
tradeOrderUpdateService.receiveOrderByMember(getLoginUserId(), Long.valueOf(payOrder.getMerchantOrderId()));
return success(true);
}
@DeleteMapping("/cancel")
@Operation(summary = "取消交易订单")
@Parameter(name = "id", description = "交易订单编号")

View File

@ -59,9 +59,16 @@ public class AppTradeOrderDetailRespVO {
@Schema(description = "支付渠道", example = "wx_lite_pay")
private String payChannelCode;
@Schema(description = "支付渠道名", example = "微信小程序支付")
private String payChannelName;
@Schema(description = "支付渠道订单号", example = "4200008888197001018888888888")
private String channelOrderNo;
@Schema(description = "是否开启微信小程序发货信息管理")
private Boolean isMaTradeManaged;
@Schema(description = "商品原价(总)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000")
private Integer totalPrice;
@ -91,6 +98,9 @@ public class AppTradeOrderDetailRespVO {
@Schema(description = "发货物流单号", example = "1024")
private String logisticsNo;
@Schema(description = "微信物流查询id")
private String waybillToken;
@Schema(description = "发货时间")
private LocalDateTime deliveryTime;

View File

@ -45,6 +45,9 @@ public class AppTradeOrderPageItemRespVO {
@Schema(description = "配送方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer deliveryType;
@Schema(description = "微信物流查询id")
private String waybillToken;
/**
*
*/

View File

@ -10,6 +10,7 @@ import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils;
import cn.iocoder.yudao.module.member.api.address.dto.MemberAddressRespDTO;
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO;
import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO;
import cn.iocoder.yudao.module.pay.enums.DictTypeConstants;
import cn.iocoder.yudao.module.product.api.comment.dto.ProductCommentCreateReqDTO;
import cn.iocoder.yudao.module.product.api.property.dto.ProductPropertyValueDetailRespDTO;
@ -174,7 +175,9 @@ public interface TradeOrderConvert {
default AppTradeOrderDetailRespVO convert02(TradeOrderDO order, List<TradeOrderItemDO> orderItems,
TradeOrderProperties tradeOrderProperties,
DeliveryExpressDO express) {
DeliveryExpressDO express,
PayOrderRespDTO payOrder,
Boolean isMaTradeManaged) {
AppTradeOrderDetailRespVO orderVO = convert3(order, orderItems);
orderVO.setPayExpireTime(order.getCreateTime().plus(tradeOrderProperties.getPayExpireTime()));
if (StrUtil.isNotEmpty(order.getPayChannelCode())) {
@ -185,6 +188,10 @@ public interface TradeOrderConvert {
if (express != null) {
orderVO.setLogisticsId(express.getId()).setLogisticsName(express.getName());
}
//处理支付渠道订单号
if(payOrder != null)
orderVO.setChannelOrderNo(payOrder.getChannelOrderNo());
orderVO.setIsMaTradeManaged(isMaTradeManaged);
return orderVO;
}

View File

@ -210,6 +210,10 @@ public class TradeOrderDO extends BaseDO {
* logisticsNo ""
*/
private String logisticsNo;
/**
* id
*/
private String waybillToken;
/**
*
*/

View File

@ -0,0 +1,54 @@
package cn.iocoder.yudao.module.trade.framework.delivery.wx;
import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.hutool.core.util.StrUtil;
import com.binarywang.spring.starter.wxjava.miniapp.properties.WxMaProperties;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import lombok.Data;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.error.WxRuntimeException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
@Configuration
@Data
public class WxMaOrderShippingProperties {
@Resource
private WxMaService wxMaService;
@Resource
private WxMaProperties wxMaProperties;
/**
*
*/
private Boolean isMaTradeManaged = false;
/**
* 使
*/
@Value("${wx.miniapp.plugin.logistics}")
private Boolean useMaLogisticsPlugin;
/**
*
*/
@Value("${wx.miniapp.plugin.order-detail-path}")
private String orderDetailPath;
/**
*
*/
@PostConstruct
private void checkMpTradeManaged(){
try {
if (StrUtil.isNotBlank(wxMaProperties.getAppid()))
isMaTradeManaged = wxMaService.getWxMaOrderShippingService().isTradeManaged(wxMaProperties.getAppid()).getTradeManaged();
}catch (WxErrorException e){
throw new WxRuntimeException(e.getError().getErrorMsg());
}
}
}

View File

@ -0,0 +1,25 @@
package cn.iocoder.yudao.module.trade.framework.delivery.wx.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
*
*<p>
* <a href="https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/business-capabilities/order-shipping/order-shipping.html">
*
*/
@Getter
@AllArgsConstructor
public enum WxLogisticsTypeEnum {
EXPRESS(1,"物流配送"),
INTRA_CITY(2,"同城配送"),
VIRTUAL(3,"虚拟商品"),
PICK_UP(4,"用户自提");
private final Integer code;
private final String desc;
}

View File

@ -0,0 +1,24 @@
package cn.iocoder.yudao.module.trade.framework.delivery.wx.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
*
*<p>
* <a href="https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/business-capabilities/order-shipping/order-shipping.html">
*
*/
@Getter
@AllArgsConstructor
public enum WxMaDeliveryModeEnum {
UNIFIED_DELIVERY(1,"统一发货"),
SPLIT_DELIVERY(2,"分拆发货");
private final Integer code;
private final String desc;
}

View File

@ -0,0 +1,24 @@
package cn.iocoder.yudao.module.trade.framework.delivery.wx.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
*
*<p>
* <a href="https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/business-capabilities/order-shipping/order-shipping.html">
*
*/
@Getter
@AllArgsConstructor
public enum WxOrderNumberType {
MCH(1,"商户号和商户侧单号"),
TRANSACTION(2,"微信支付单号");
private final Integer code;
private final String desc;
}

View File

@ -1,5 +1,9 @@
package cn.iocoder.yudao.module.trade.service.order;
import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.delivery.TraceWaybillRequest;
import cn.binarywang.wx.miniapp.bean.delivery.WaybillGoodsInfo;
import cn.binarywang.wx.miniapp.bean.shop.request.shipping.*;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.lang.Assert;
@ -12,6 +16,7 @@ import cn.hutool.extra.spring.SpringUtil;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
import cn.iocoder.yudao.module.member.api.address.MemberAddressApi;
import cn.iocoder.yudao.module.member.api.address.dto.MemberAddressRespDTO;
import cn.iocoder.yudao.module.pay.api.order.PayOrderApi;
@ -42,6 +47,10 @@ import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderMapper;
import cn.iocoder.yudao.module.trade.dal.redis.no.TradeNoRedisDAO;
import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum;
import cn.iocoder.yudao.module.trade.enums.order.*;
import cn.iocoder.yudao.module.trade.framework.delivery.wx.WxMaOrderShippingProperties;
import cn.iocoder.yudao.module.trade.framework.delivery.wx.enums.WxLogisticsTypeEnum;
import cn.iocoder.yudao.module.trade.framework.delivery.wx.enums.WxMaDeliveryModeEnum;
import cn.iocoder.yudao.module.trade.framework.delivery.wx.enums.WxOrderNumberType;
import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;
import cn.iocoder.yudao.module.trade.framework.order.core.annotations.TradeOrderLog;
import cn.iocoder.yudao.module.trade.framework.order.core.utils.TradeOrderLogUtils;
@ -57,15 +66,20 @@ import cn.iocoder.yudao.module.trade.service.price.calculator.TradePriceCalculat
import jakarta.annotation.Resource;
import jakarta.validation.constraints.NotNull;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
@ -104,6 +118,12 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
@Resource
private TradeMessageService tradeMessageService;
@Resource
private WxMaService wxMaService;
@Resource
private WxMaOrderShippingProperties wxMaOrderShippingProperties;
@Resource
private PayOrderApi payOrderApi;
@Resource
@ -368,6 +388,13 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
throw exception(ORDER_DELIVERY_FAIL_DELIVERY_TYPE_NOT_EXPRESS);
}
PayOrderRespDTO payOrder = null;
//小程序发货时获取支付单
if(PayChannelEnum.WX_LITE.getCode().equals(order.getPayChannelCode())
&& (wxMaOrderShippingProperties.getIsMaTradeManaged() || wxMaOrderShippingProperties.getUseMaLogisticsPlugin())) {
payOrder = payOrderApi.getOrder(order.getPayOrderId()).getCheckedData();
}
// 2. 更新订单为已发货
TradeOrderDO updateOrderObj = new TradeOrderDO();
// 2.1 快递发货
@ -375,12 +402,23 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
if (ObjectUtil.notEqual(deliveryReqVO.getLogisticsId(), TradeOrderDO.LOGISTICS_ID_NULL)) {
express = deliveryExpressService.validateDeliveryExpress(deliveryReqVO.getLogisticsId());
updateOrderObj.setLogisticsId(deliveryReqVO.getLogisticsId()).setLogisticsNo(deliveryReqVO.getLogisticsNo());
//使用微信小程序物流查询插件
if(payOrder!=null && wxMaOrderShippingProperties.getUseMaLogisticsPlugin()){
try {
String waybillToken = getWxTraceWaybillToken(order.getId(), deliveryReqVO.getLogisticsNo(), express, order.getReceiverMobile(), payOrder);
updateOrderObj.setWaybillToken(waybillToken);
}catch (WxErrorException e) {
log.error("[WxMaLogisticsPlugin][微信小程序物流插件查询id获取发生异常,异常信息({})]",e.getMessage());
throw new RuntimeException(e);
}
}
} else {
// 2.2 无需发货
updateOrderObj.setLogisticsId(0L).setLogisticsNo("");
}
LocalDateTime deliveryTime = LocalDateTime.now();
// 执行更新
updateOrderObj.setStatus(TradeOrderStatusEnum.DELIVERED.getStatus()).setDeliveryTime(LocalDateTime.now());
updateOrderObj.setStatus(TradeOrderStatusEnum.DELIVERED.getStatus()).setDeliveryTime(deliveryTime);
int updateCount = tradeOrderMapper.updateByIdAndStatus(order.getId(), order.getStatus(), updateOrderObj);
if (updateCount == 0) {
throw exception(ORDER_DELIVERY_FAIL_STATUS_NOT_UNDELIVERED);
@ -396,6 +434,113 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
.setOrderId(order.getId()).setUserId(order.getUserId()).setMessage(null));
// 4.2 发送订阅消息
getSelf().sendDeliveryOrderMessage(order, deliveryReqVO);
//小程序发货信息录入,放最后确保数据库事务完成
if(payOrder!= null && wxMaOrderShippingProperties.getIsMaTradeManaged())
wxMaOrderUpload(order.getId(), updateOrderObj, express, order.getReceiverMobile(), payOrder, deliveryTime);
}
/**
*
*
* @param orderId id
* @param updateOrderObj
* @param express
* @param receiverMobile
* @param payOrder
* @param deliveryTime
*/
private void wxMaOrderUpload(Long orderId, TradeOrderDO updateOrderObj, DeliveryExpressDO express,
String receiverMobile, PayOrderRespDTO payOrder, LocalDateTime deliveryTime) {
//构建订单信息
OrderKeyBean orderKey = new OrderKeyBean(WxOrderNumberType.TRANSACTION.getCode(),payOrder.getChannelOrderNo(),null,null);
//默认为快递发货
int logistics_type = WxLogisticsTypeEnum.EXPRESS.getCode();
if(ObjectUtil.equal(updateOrderObj.getLogisticsId(), TradeOrderDO.LOGISTICS_ID_NULL)){
//无需发货的场景
//TODO: 无需发货只设置为虚拟发货
logistics_type = WxLogisticsTypeEnum.VIRTUAL.getCode();
}
//构建物流信息列表,合并发货所以只有一条记录
List<ShippingListBean> shippingList = new ArrayList<>(){{
add(
ShippingListBean.builder()
.trackingNo(updateOrderObj.getLogisticsNo())
.expressCompany(express!=null?express.getCode():null)
.itemDesc(payOrder.getBody())
.contact(new ContactBean(null,receiverMobile))
.build()
);
}};
//根据接口要求生成时间字符串,同步平台生成的时间
ZonedDateTime dateTime = ZonedDateTime.of(deliveryTime, ZoneId.of("Asia/Shanghai"));
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyy-MM-dd'T'HH:mm:ss.SSSXXX");
String upload_time = dateTime.format(formatter);
//构建请求
WxMaOrderShippingInfoUploadRequest request = WxMaOrderShippingInfoUploadRequest.builder()
.orderKey(orderKey)
.deliveryMode(WxMaDeliveryModeEnum.UNIFIED_DELIVERY.getCode())//仅考虑合并发货情况
.logisticsType(logistics_type)
.isAllDelivered(true)
.shippingList(shippingList)
.uploadTime(upload_time)
.payer(new PayerBean(payOrder.getChannelUserId()))
.build();
try{
//上传发货信息
wxMaService.getWxMaOrderShippingService().upload(request);
//设置订单详情路径
wxMaService.getWxMaOrderShippingService().setMsgJumpPath(wxMaOrderShippingProperties.getOrderDetailPath()+"?id="+orderId);
} catch (WxErrorException e) {
log.error("[WxMaOrderShipping][微信小程序发货信息录入发生异常,异常信息({})]",e.getMessage());
throw new RuntimeException(e);
}
}
/**
* id(waybillToken)
*
* @param orderId id
* @param logisticsNo
* @param express
* @param receiverMobile
* @param payOrder
* @return id
*/
private String getWxTraceWaybillToken(Long orderId, String logisticsNo, DeliveryExpressDO express,
String receiverMobile, PayOrderRespDTO payOrder) throws WxErrorException {
List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(orderId);
List<WaybillGoodsInfo.GoodsItem> goodsItems = orderItems
.stream()
.map(item->{
WaybillGoodsInfo.GoodsItem goodsItem = new WaybillGoodsInfo.GoodsItem();
goodsItem.setGoodsName(item.getSpuName());
goodsItem.setGoodsImgUrl(item.getPicUrl());
return goodsItem;
})
.collect(Collectors.toList());
//构建请求
//TODO: WxJava 4.6.0暂时不支持设置快递公司代码, 之后最好加上
TraceWaybillRequest request = TraceWaybillRequest.builder()
.openid(payOrder.getChannelUserId())
.senderPhone("")
.receiverPhone(receiverMobile)
//.deliveryId(express.getCode())
.waybillId(logisticsNo)
.goodsInfo(new WaybillGoodsInfo(goodsItems))
.transId(payOrder.getChannelOrderNo())
.orderDetailPath(wxMaOrderShippingProperties.getOrderDetailPath() + "?id=" + orderId)
.build();
return wxMaService.getWxMaImmediateDeliveryService().traceWaybill(request).getWaybillToken();
}
@Async

View File

@ -125,6 +125,41 @@ logging:
cn.iocoder.yudao.module.trade.dal.mysql: debug
org.springframework.context.support.PostProcessorRegistrationDelegate: ERROR # TODO 芋艿先禁用Spring Boot 3.X 存在部分错误的 WARN 提示
--- #################### 微信公众号、小程序相关配置 ####################
wx:
mp: # 公众号配置(必填),参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-mp-spring-boot-starter/README.md 文档
# app-id: wx041349c6f39b268b # 测试号(牛希尧提供的)
# secret: 5abee519483bc9f8cb37ce280e814bd0
app-id: wx5b23ba7a5589ecbb # 测试号(自己的)
secret: 2a7b3b20c537e52e74afd395eb85f61f
# app-id: wxa69ab825b163be19 # 测试号Kongdy 提供的)
# secret: bd4f9fab889591b62aeac0d7b8d8b4a0
# 存储配置,解决 AccessToken 的跨节点的共享
config-storage:
type: RedisTemplate # 采用 RedisTemplate 操作 Redis会自动从 Spring 中获取
key-prefix: wx # Redis Key 的前缀
http-client-type: HttpClient # 采用 HttpClient 请求微信公众号平台
miniapp: # 小程序配置(必填),参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-miniapp-spring-boot-starter/README.md 文档
# appid: wx62056c0d5e8db250 # 测试号(牛希尧提供的)
# secret: 333ae72f41552af1e998fe1f54e1584a
# appid: wx63c280fe3248a3e7 # wenhualian的接口测试号
# secret: 6f270509224a7ae1296bbf1c8cb97aed
# appid: wxc4598c446f8a9cb3 # 测试号Kongdy 提供的)
# secret: 4a1a04e07f6a4a0751b39c3064a92c8b
appid: wx66186af0759f47c9 # 测试号puhui 提供的)
secret: 3218bcbd112cbc614c7264ceb20144ac
config-storage:
type: RedisTemplate # 采用 RedisTemplate 操作 Redis会自动从 Spring 中获取
key-prefix: wa # Redis Key 的前缀
http-client-type: HttpClient # 采用 HttpClient 请求微信公众号平台
token: # 微信小程序消息推送服务Token
aes-key: # 微信小程序消息推送服务AES kEY
msg-data-format: JSON # 微信小程序消息推送服务推送信息格式,只适配了JSON
plugin:
logistics: true
order-detail-path: /pages/order/detail
--- #################### 芋道相关配置 ####################
# 芋道配置项,设置当前项目所有自定义的配置

View File

@ -122,6 +122,7 @@ yudao:
tenant: # 多租户相关配置项
enable: true
ignore-urls:
- /admin-api/trade/wx/** # 接收微信消息推送,不携带租户编号
ignore-tables:
trade:
order:

View File

@ -30,6 +30,12 @@ public interface PayOrderApi {
@PermitAll
CommonResult<PayOrderRespDTO> getOrder(@RequestParam("id") Long id);
@GetMapping(PREFIX + "/get-by-channel")
@Operation(summary = "获得支付单")
@Parameter(name = "channelOrderNo", description = "渠道订单号", required = true)
@PermitAll
CommonResult<PayOrderRespDTO> getOrder(@RequestParam("channelOrderNo") String channelOrderNo);
@PutMapping(PREFIX + "/update-price")
@Operation(summary = "更新支付订单价格")
@Parameters({

View File

@ -28,6 +28,14 @@ public class PayOrderRespDTO {
* A PayMerchantDO
*/
private String merchantOrderId;
/**
*
*/
private String subject;
/**
*
*/
private String body;
// ========== 订单相关字段 ==========
/**
@ -36,11 +44,19 @@ public class PayOrderRespDTO {
private Integer price;
/**
*
*
* {@link PayOrderStatusEnum}
*/
private Integer status;
// ========== 渠道相关字段 ==========
/**
*
* openid
*/
private String channelUserId;
/**
*
*/
private String channelOrderNo;
}

View File

@ -1,6 +1,7 @@
package cn.iocoder.yudao.module.pay.api.order;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO;
import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO;
import cn.iocoder.yudao.module.pay.convert.order.PayOrderConvert;
@ -31,6 +32,13 @@ public class PayOrderApiImpl implements PayOrderApi {
return success(PayOrderConvert.INSTANCE.convert2(order));
}
@Override
@TenantIgnore
public CommonResult<PayOrderRespDTO> getOrder(String channelOrderNo) {
PayOrderDO order = payOrderService.getOrder(channelOrderNo);
return success(PayOrderConvert.INSTANCE.convert2(order));
}
@Override
public CommonResult<Boolean> updatePayOrderPrice(Long id, Integer payPrice) {
payOrderService.updatePayOrderPrice(id, payPrice);

View File

@ -31,6 +31,13 @@ public interface PayOrderService {
*/
PayOrderDO getOrder(Long id);
/**
*
*
* @param channalOrderNo
* @return
*/
PayOrderDO getOrder(String channalOrderNo);
/**
*
*

View File

@ -80,6 +80,11 @@ public class PayOrderServiceImpl implements PayOrderService {
return orderMapper.selectById(id);
}
@Override
public PayOrderDO getOrder(String channelOrderNo) {
return orderMapper.selectOne(PayOrderDO::getChannelOrderNo, channelOrderNo);
}
@Override
public PayOrderDO getOrder(Long appId, String merchantOrderId) {
return orderMapper.selectByAppIdAndMerchantOrderId(appId, merchantOrderId);