feat(shop): 添加微信小程序发货信息自动同步功能
- 新增 ShopWechatShippingSyncService 接口及实现类 - 在订单发货时自动同步实物快递和无需物流的发货信息到微信后台 - 添加微信小程序 access_token 获取服务及缓存机制 - 优化订单发货逻辑,支持无需物流/自提订单的自动同步处理 - 添加详细的日志记录和异常处理机制 - 实现发货信息同步失败时的容错处理
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
package com.gxwebsoft.common.system.service;
|
||||
|
||||
/**
|
||||
* 微信小程序 access_token 获取服务(按租户)。
|
||||
*
|
||||
* <p>用于调用微信小程序开放接口(例如:上传发货信息)。</p>
|
||||
*/
|
||||
public interface WxMiniappAccessTokenService {
|
||||
|
||||
/**
|
||||
* 获取指定租户的小程序 access_token(内部带缓存)。
|
||||
*
|
||||
* @param tenantId 租户ID
|
||||
* @return access_token
|
||||
*/
|
||||
String getAccessToken(Integer tenantId);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
package com.gxwebsoft.common.system.service.impl;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.gxwebsoft.common.core.exception.BusinessException;
|
||||
import com.gxwebsoft.common.core.utils.RedisUtil;
|
||||
import com.gxwebsoft.common.system.service.WxMiniappAccessTokenService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static com.gxwebsoft.common.core.constants.RedisConstants.ACCESS_TOKEN_KEY;
|
||||
import static com.gxwebsoft.common.core.constants.RedisConstants.MP_WX_KEY;
|
||||
|
||||
/**
|
||||
* 微信小程序 access_token 获取实现(按租户)。
|
||||
*
|
||||
* <p>复用现有缓存结构:
|
||||
* <ul>
|
||||
* <li>小程序配置:Redis key = {@code mp-weixin:{tenantId}},value 为 JSON,包含 appId/appSecret</li>
|
||||
* <li>access_token:Redis key = {@code access-token:{tenantId}},value 为微信返回的 JSON 字符串</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class WxMiniappAccessTokenServiceImpl implements WxMiniappAccessTokenService {
|
||||
|
||||
@Resource
|
||||
private RedisUtil redisUtil;
|
||||
|
||||
@Override
|
||||
public String getAccessToken(Integer tenantId) {
|
||||
if (tenantId == null) {
|
||||
throw new BusinessException("tenantId 不能为空");
|
||||
}
|
||||
|
||||
final String tokenCacheKey = ACCESS_TOKEN_KEY + ":" + tenantId;
|
||||
|
||||
// 1) 优先从缓存取(兼容 JSON 或纯字符串 token 的历史格式)
|
||||
String cachedValue = redisUtil.get(tokenCacheKey);
|
||||
if (StrUtil.isNotBlank(cachedValue)) {
|
||||
try {
|
||||
JSONObject cachedJson = JSON.parseObject(cachedValue);
|
||||
String accessToken = cachedJson.getString("access_token");
|
||||
if (StrUtil.isNotBlank(accessToken)) {
|
||||
return accessToken;
|
||||
}
|
||||
} catch (Exception ignore) {
|
||||
// 旧格式:直接存 token
|
||||
return cachedValue;
|
||||
}
|
||||
}
|
||||
|
||||
// 2) 缓存没有则从租户配置获取 appId/appSecret
|
||||
final String wxConfigKey = MP_WX_KEY + tenantId;
|
||||
final String wxConfigValue = redisUtil.get(wxConfigKey);
|
||||
if (StrUtil.isBlank(wxConfigValue)) {
|
||||
throw new BusinessException("未找到微信小程序配置,请检查缓存key: " + wxConfigKey);
|
||||
}
|
||||
|
||||
JSONObject wxConfig;
|
||||
try {
|
||||
wxConfig = JSON.parseObject(wxConfigValue);
|
||||
} catch (Exception e) {
|
||||
throw new BusinessException("微信小程序配置格式错误: " + e.getMessage());
|
||||
}
|
||||
|
||||
final String appId = wxConfig.getString("appId");
|
||||
final String appSecret = wxConfig.getString("appSecret");
|
||||
if (StrUtil.isBlank(appId) || StrUtil.isBlank(appSecret)) {
|
||||
throw new BusinessException("微信小程序配置不完整(appId/appSecret)");
|
||||
}
|
||||
|
||||
// 3) 调用微信接口获取 token
|
||||
final String apiUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential"
|
||||
+ "&appid=" + appId + "&secret=" + appSecret;
|
||||
String result = HttpUtil.get(apiUrl);
|
||||
|
||||
JSONObject json = JSON.parseObject(result);
|
||||
if (json.containsKey("errcode") && json.getIntValue("errcode") != 0) {
|
||||
Integer errcode = json.getInteger("errcode");
|
||||
String errmsg = json.getString("errmsg");
|
||||
throw new BusinessException("获取小程序access_token失败: " + errmsg + " (errcode: " + errcode + ")");
|
||||
}
|
||||
|
||||
String accessToken = json.getString("access_token");
|
||||
Integer expiresIn = json.getInteger("expires_in");
|
||||
if (StrUtil.isBlank(accessToken)) {
|
||||
throw new BusinessException("获取小程序access_token失败: access_token为空");
|
||||
}
|
||||
|
||||
// 4) 缓存微信原始 JSON(与现有实现保持一致),提前5分钟过期
|
||||
long ttlSeconds = 7000L;
|
||||
if (expiresIn != null && expiresIn > 300) {
|
||||
ttlSeconds = expiresIn - 300L;
|
||||
}
|
||||
redisUtil.set(tokenCacheKey, result, ttlSeconds, TimeUnit.SECONDS);
|
||||
|
||||
log.info("获取小程序access_token成功 - tenantId={}, ttlSeconds={}", tenantId, ttlSeconds);
|
||||
return accessToken;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,6 +95,8 @@ public class ShopOrderController extends BaseController {
|
||||
@Resource
|
||||
private ShopOrderDeliveryService shopOrderDeliveryService;
|
||||
@Resource
|
||||
private ShopWechatShippingSyncService shopWechatShippingSyncService;
|
||||
@Resource
|
||||
private PaymentService paymentService;
|
||||
|
||||
@Operation(summary = "分页查询订单")
|
||||
@@ -274,6 +276,33 @@ public class ShopOrderController extends BaseController {
|
||||
}
|
||||
// 发货状态从“未发货(10)”变更为“已发货(20)”时,记录发货信息
|
||||
if (Objects.equals(shopOrderNow.getDeliveryStatus(), 10) && Objects.equals(shopOrder.getDeliveryStatus(), 20)) {
|
||||
// 1) 无需物流/自提:不走快递100下单,直接置为已发货并同步到微信后台
|
||||
if (shopOrder.getExpressId() == null || shopOrder.getExpressId() == 0) {
|
||||
ShopOrderDelivery shopOrderDelivery = new ShopOrderDelivery();
|
||||
shopOrderDelivery.setOrderId(shopOrder.getOrderId());
|
||||
shopOrderDelivery.setDeliveryMethod(20);
|
||||
shopOrderDelivery.setSendName(shopOrder.getSendName());
|
||||
shopOrderDelivery.setSendPhone(shopOrder.getSendPhone());
|
||||
shopOrderDelivery.setSendAddress(shopOrder.getSendAddress());
|
||||
shopOrderDeliveryService.save(shopOrderDelivery);
|
||||
|
||||
ShopOrder patch = new ShopOrder();
|
||||
patch.setOrderId(shopOrder.getOrderId());
|
||||
patch.setDeliveryStatus(20);
|
||||
patch.setDeliveryTime(LocalDateTime.now());
|
||||
shopOrderService.updateById(patch);
|
||||
|
||||
// 同步到微信后台(发货信息录入)
|
||||
ShopOrder syncOrder = shopOrderNow;
|
||||
syncOrder.setDeliveryStatus(20);
|
||||
syncOrder.setDeliveryTime(patch.getDeliveryTime());
|
||||
try {
|
||||
shopWechatShippingSyncService.uploadNoLogisticsShippingInfo(syncOrder);
|
||||
} catch (Exception e) {
|
||||
logger.warn("同步微信发货信息失败(无需物流,不影响发货成功) - orderId={}", shopOrder.getOrderId(), e);
|
||||
}
|
||||
} else {
|
||||
// 2) 实物快递:创建发货单并走快递100电子面单,发货成功后同步微信后台
|
||||
ShopOrderDelivery shopOrderDelivery = new ShopOrderDelivery();
|
||||
shopOrderDelivery.setOrderId(shopOrder.getOrderId());
|
||||
shopOrderDelivery.setDeliveryMethod(30);
|
||||
@@ -283,7 +312,11 @@ public class ShopOrderController extends BaseController {
|
||||
shopOrderDelivery.setSendAddress(shopOrder.getSendAddress());
|
||||
shopOrderDeliveryService.save(shopOrderDelivery);
|
||||
|
||||
shopOrderDeliveryService.setExpress(getLoginUser(), shopOrderDelivery, shopOrder);
|
||||
// 需要用订单的持久化字段(例如 addressId/tenantId/userId/transactionId),同时补齐临时的发货地址
|
||||
ShopOrder orderForDelivery = shopOrderNow;
|
||||
orderForDelivery.setSendAddress(shopOrder.getSendAddress());
|
||||
shopOrderDeliveryService.setExpress(getLoginUser(), shopOrderDelivery, orderForDelivery);
|
||||
}
|
||||
|
||||
}
|
||||
if (shopOrderService.updateById(shopOrder)) {
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.gxwebsoft.shop.service;
|
||||
|
||||
import com.gxwebsoft.shop.entity.ShopExpress;
|
||||
import com.gxwebsoft.shop.entity.ShopOrder;
|
||||
import com.gxwebsoft.shop.entity.ShopOrderDelivery;
|
||||
|
||||
/**
|
||||
* 微信小程序“发货信息管理”同步服务。
|
||||
*
|
||||
* <p>用于将系统内发货/无需物流状态同步到微信小程序后台,避免人工在后台录入。</p>
|
||||
*/
|
||||
public interface ShopWechatShippingSyncService {
|
||||
|
||||
/**
|
||||
* 实物快递发货同步到微信后台(上传运单号/快递公司)。
|
||||
*/
|
||||
boolean uploadExpressShippingInfo(ShopOrder order, ShopOrderDelivery orderDelivery, ShopExpress express);
|
||||
|
||||
/**
|
||||
* 无需物流/自提发货同步到微信后台(上传无需物流)。
|
||||
*/
|
||||
boolean uploadNoLogisticsShippingInfo(ShopOrder order);
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import com.gxwebsoft.common.core.web.PageParam;
|
||||
import com.gxwebsoft.common.core.web.PageResult;
|
||||
import com.kuaidi100.sdk.pojo.HttpResult;
|
||||
import com.kuaidi100.sdk.request.BOrderReq;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
@@ -27,6 +28,7 @@ import java.util.Map;
|
||||
* @since 2025-01-11 10:45:12
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class ShopOrderDeliveryServiceImpl extends ServiceImpl<ShopOrderDeliveryMapper, ShopOrderDelivery> implements ShopOrderDeliveryService {
|
||||
@Resource
|
||||
private ShopExpressService expressService;
|
||||
@@ -40,6 +42,8 @@ public class ShopOrderDeliveryServiceImpl extends ServiceImpl<ShopOrderDeliveryM
|
||||
private ShopGoodsService shopGoodsService;
|
||||
@Resource
|
||||
private ShopUserAddressService shopUserAddressService;
|
||||
@Resource
|
||||
private ShopWechatShippingSyncService shopWechatShippingSyncService;
|
||||
|
||||
@Override
|
||||
public PageResult<ShopOrderDelivery> pageRel(ShopOrderDeliveryParam param) {
|
||||
@@ -111,45 +115,12 @@ public class ShopOrderDeliveryServiceImpl extends ServiceImpl<ShopOrderDeliveryM
|
||||
order.setDeliveryTime(LocalDateTime.now());
|
||||
shopOrderService.updateById(order);
|
||||
|
||||
if (order.getPayType().equals(1)) {
|
||||
List<ShopOrderGoods> orderGoodsList = shopOrderGoodsService.getListByOrderId(order.getOrderId());
|
||||
// 上传小程序发货信息
|
||||
// WxMaOrderShippingInfoUploadRequest uploadRequest = new WxMaOrderShippingInfoUploadRequest();
|
||||
// uploadRequest.setLogisticsType(1);
|
||||
// uploadRequest.setDeliveryMode(1);
|
||||
//
|
||||
// OrderKeyBean orderKeyBean = new OrderKeyBean();
|
||||
// orderKeyBean.setOrderNumberType(2);
|
||||
// orderKeyBean.setTransactionId(order.getTransactionId());
|
||||
// uploadRequest.setOrderKey(orderKeyBean);
|
||||
//
|
||||
// List<ShippingListBean> shippingList = new ArrayList<>();
|
||||
// ShippingListBean shippingListBean = new ShippingListBean();
|
||||
// shippingListBean.setTrackingNo((String) bOrderData.get("kuaidinum"));
|
||||
// shippingListBean.setExpressCompany(express.getWxCode());
|
||||
// ContactBean contactBean = new ContactBean();
|
||||
// contactBean.setReceiverContact(user.getMobile());
|
||||
// shippingListBean.setContact(contactBean);
|
||||
//
|
||||
// ShopGoods shopGoods = shopGoodsService.getById(orderGoodsList.get(0).getGoodsId());
|
||||
//
|
||||
// String itemDesc = shopGoods.getName();
|
||||
// if (orderGoodsList.size() > 1) itemDesc += "等" + orderGoodsList.size() + "件商品";
|
||||
// shippingListBean.setItemDesc(itemDesc);
|
||||
// shippingList.add(shippingListBean);
|
||||
// uploadRequest.setShippingList(shippingList);
|
||||
//
|
||||
// uploadRequest.setUploadTime(new DateTime().toString(DatePattern.UTC_WITH_ZONE_OFFSET_PATTERN));
|
||||
//
|
||||
// PayerBean payerBean = new PayerBean();
|
||||
//
|
||||
// payerBean.setOpenid(user.getOpenid());
|
||||
// uploadRequest.setPayer(payerBean);
|
||||
//
|
||||
// WxMaService wxMaService = weChatController.wxMaService();
|
||||
// WxMaOrderShippingService wxMaOrderShippingService = new WxMaOrderShippingServiceImpl(wxMaService);
|
||||
// WxMaOrderShippingInfoBaseResponse response = wxMaOrderShippingService.upload(uploadRequest);
|
||||
// System.out.println("response" + response);
|
||||
// 同步发货信息到微信小程序后台(发货信息录入),避免人工录入
|
||||
try {
|
||||
shopWechatShippingSyncService.uploadExpressShippingInfo(order, orderDelivery, express);
|
||||
} catch (Exception e) {
|
||||
// 不影响本地发货流程,记录日志即可(可配合定时任务后补偿重试)
|
||||
log.warn("同步微信发货信息失败(不影响发货成功) - orderId={}", order.getOrderId(), e);
|
||||
}
|
||||
return new HashMap<>() {{
|
||||
put("res", true);
|
||||
|
||||
@@ -0,0 +1,231 @@
|
||||
package com.gxwebsoft.shop.service.impl;
|
||||
|
||||
import cn.binarywang.wx.miniapp.bean.shop.request.shipping.OrderKeyBean;
|
||||
import cn.binarywang.wx.miniapp.bean.shop.request.shipping.PayerBean;
|
||||
import cn.binarywang.wx.miniapp.bean.shop.request.shipping.ShippingListBean;
|
||||
import cn.binarywang.wx.miniapp.bean.shop.request.shipping.WxMaOrderShippingInfoUploadRequest;
|
||||
import cn.binarywang.wx.miniapp.constant.WxMaApiUrlConstants;
|
||||
import cn.hutool.core.date.DatePattern;
|
||||
import cn.hutool.core.date.DateTime;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.gxwebsoft.common.core.utils.RedisUtil;
|
||||
import com.gxwebsoft.common.system.entity.Payment;
|
||||
import com.gxwebsoft.common.system.entity.User;
|
||||
import com.gxwebsoft.common.system.service.UserService;
|
||||
import com.gxwebsoft.common.system.service.WxMiniappAccessTokenService;
|
||||
import com.gxwebsoft.shop.entity.ShopExpress;
|
||||
import com.gxwebsoft.shop.entity.ShopGoods;
|
||||
import com.gxwebsoft.shop.entity.ShopOrder;
|
||||
import com.gxwebsoft.shop.entity.ShopOrderDelivery;
|
||||
import com.gxwebsoft.shop.entity.ShopOrderGoods;
|
||||
import com.gxwebsoft.shop.service.ShopGoodsService;
|
||||
import com.gxwebsoft.shop.service.ShopOrderGoodsService;
|
||||
import com.gxwebsoft.shop.service.ShopWechatShippingSyncService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 同步系统发货信息到微信小程序后台(发货信息录入)。
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class ShopWechatShippingSyncServiceImpl implements ShopWechatShippingSyncService {
|
||||
|
||||
private static final int ORDER_NUMBER_TYPE_OUT_TRADE_NO = 1;
|
||||
private static final int ORDER_NUMBER_TYPE_TRANSACTION_ID = 2;
|
||||
|
||||
// 这两个值在项目原有注释代码中已经使用过(实物快递)。
|
||||
private static final int LOGISTICS_TYPE_PHYSICAL = 1;
|
||||
private static final int DELIVERY_MODE_EXPRESS = 1;
|
||||
|
||||
// 无需物流/自提:微信侧会在“发货信息录入”里变为已发货(具体枚举以微信接口为准)。
|
||||
private static final int LOGISTICS_TYPE_NO_LOGISTICS = 3;
|
||||
private static final int DELIVERY_MODE_NO_LOGISTICS = 3;
|
||||
|
||||
private static final Gson GSON = new Gson();
|
||||
|
||||
@Resource
|
||||
private WxMiniappAccessTokenService wxMiniappAccessTokenService;
|
||||
@Resource
|
||||
private UserService userService;
|
||||
@Resource
|
||||
private ShopOrderGoodsService shopOrderGoodsService;
|
||||
@Resource
|
||||
private ShopGoodsService shopGoodsService;
|
||||
@Resource
|
||||
private RedisUtil redisUtil;
|
||||
|
||||
@Override
|
||||
public boolean uploadExpressShippingInfo(ShopOrder order, ShopOrderDelivery orderDelivery, ShopExpress express) {
|
||||
if (order == null || order.getOrderId() == null) {
|
||||
return false;
|
||||
}
|
||||
if (orderDelivery == null || StrUtil.isBlank(orderDelivery.getExpressNo())) {
|
||||
log.warn("上传微信发货信息跳过:缺少运单号 - orderId={}", order.getOrderId());
|
||||
return false;
|
||||
}
|
||||
if (express == null || StrUtil.isBlank(express.getWxCode())) {
|
||||
log.warn("上传微信发货信息跳过:缺少微信快递公司编码(wxCode) - orderId={}", order.getOrderId());
|
||||
return false;
|
||||
}
|
||||
|
||||
List<ShippingListBean> shippingList = new ArrayList<>();
|
||||
ShippingListBean item = new ShippingListBean();
|
||||
item.setTrackingNo(orderDelivery.getExpressNo());
|
||||
item.setExpressCompany(express.getWxCode());
|
||||
item.setItemDesc(buildItemDesc(order.getOrderId()));
|
||||
shippingList.add(item);
|
||||
|
||||
return doUpload(order, LOGISTICS_TYPE_PHYSICAL, DELIVERY_MODE_EXPRESS, shippingList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean uploadNoLogisticsShippingInfo(ShopOrder order) {
|
||||
if (order == null || order.getOrderId() == null) {
|
||||
return false;
|
||||
}
|
||||
// 无需物流情况下通常不需要 shipping_list
|
||||
return doUpload(order, LOGISTICS_TYPE_NO_LOGISTICS, DELIVERY_MODE_NO_LOGISTICS, Collections.emptyList());
|
||||
}
|
||||
|
||||
private boolean doUpload(ShopOrder order, int logisticsType, int deliveryMode, List<ShippingListBean> shippingList) {
|
||||
// 仅对微信支付订单尝试同步(微信后台“待发货”来自微信支付交易)
|
||||
if (!ObjectUtil.equals(order.getPayType(), 1) && !ObjectUtil.equals(order.getPayType(), 102)) {
|
||||
return false;
|
||||
}
|
||||
if (!Boolean.TRUE.equals(order.getPayStatus())) {
|
||||
return false;
|
||||
}
|
||||
if (order.getTenantId() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// payer openid:必须是下单用户,不是后台操作员
|
||||
User buyer = userService.getByIdIgnoreTenant(order.getUserId());
|
||||
if (buyer == null || StrUtil.isBlank(buyer.getOpenid())) {
|
||||
log.warn("上传微信发货信息失败:买家openid为空 - orderId={}, userId={}", order.getOrderId(), order.getUserId());
|
||||
return false;
|
||||
}
|
||||
|
||||
String accessToken;
|
||||
try {
|
||||
accessToken = wxMiniappAccessTokenService.getAccessToken(order.getTenantId());
|
||||
} catch (Exception e) {
|
||||
log.error("获取小程序access_token失败 - orderId={}, tenantId={}", order.getOrderId(), order.getTenantId(), e);
|
||||
return false;
|
||||
}
|
||||
|
||||
OrderKeyBean orderKey = buildOrderKey(order);
|
||||
if (orderKey == null) {
|
||||
log.warn("上传微信发货信息跳过:无法构建order_key - orderId={}", order.getOrderId());
|
||||
return false;
|
||||
}
|
||||
|
||||
WxMaOrderShippingInfoUploadRequest uploadRequest = new WxMaOrderShippingInfoUploadRequest();
|
||||
uploadRequest.setOrderKey(orderKey);
|
||||
uploadRequest.setLogisticsType(logisticsType);
|
||||
uploadRequest.setDeliveryMode(deliveryMode);
|
||||
uploadRequest.setIsAllDelivered(true);
|
||||
if (shippingList != null && !shippingList.isEmpty()) {
|
||||
uploadRequest.setShippingList(shippingList);
|
||||
}
|
||||
uploadRequest.setUploadTime(new DateTime().toString(DatePattern.UTC_WITH_ZONE_OFFSET_PATTERN));
|
||||
|
||||
PayerBean payerBean = new PayerBean();
|
||||
payerBean.setOpenid(buyer.getOpenid());
|
||||
uploadRequest.setPayer(payerBean);
|
||||
|
||||
String url = WxMaApiUrlConstants.OrderShipping.UPLOAD_SHIPPING_INFO + "?access_token=" + accessToken;
|
||||
String body = GSON.toJson(uploadRequest);
|
||||
|
||||
try {
|
||||
String resp = HttpRequest.post(url)
|
||||
.header("Content-Type", "application/json")
|
||||
.body(body)
|
||||
.timeout(10000)
|
||||
.execute()
|
||||
.body();
|
||||
JsonObject json = JsonParser.parseString(resp).getAsJsonObject();
|
||||
int errcode = json.has("errcode") ? json.get("errcode").getAsInt() : -1;
|
||||
String errmsg = json.has("errmsg") ? json.get("errmsg").getAsString() : resp;
|
||||
if (errcode == 0) {
|
||||
log.info("✅ 微信发货信息同步成功 - orderId={}, logisticsType={}, deliveryMode={}",
|
||||
order.getOrderId(), logisticsType, deliveryMode);
|
||||
return true;
|
||||
}
|
||||
log.error("❌ 微信发货信息同步失败 - orderId={}, errcode={}, errmsg={}, req={}",
|
||||
order.getOrderId(), errcode, errmsg, body);
|
||||
return false;
|
||||
} catch (Exception e) {
|
||||
log.error("❌ 微信发货信息同步异常 - orderId={}, req={}", order.getOrderId(), body, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private OrderKeyBean buildOrderKey(ShopOrder order) {
|
||||
if (StrUtil.isNotBlank(order.getTransactionId())) {
|
||||
OrderKeyBean key = new OrderKeyBean();
|
||||
key.setOrderNumberType(ORDER_NUMBER_TYPE_TRANSACTION_ID);
|
||||
key.setTransactionId(order.getTransactionId());
|
||||
return key;
|
||||
}
|
||||
|
||||
// transactionId 为空时,尝试使用 out_trade_no + mchid
|
||||
if (StrUtil.isBlank(order.getOrderNo())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Payment payment = loadWechatPaymentConfig(order.getTenantId());
|
||||
if (payment == null || StrUtil.isBlank(payment.getMchId())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
OrderKeyBean key = new OrderKeyBean();
|
||||
key.setOrderNumberType(ORDER_NUMBER_TYPE_OUT_TRADE_NO);
|
||||
key.setOutTradeNo(order.getOrderNo());
|
||||
key.setMchId(payment.getMchId());
|
||||
return key;
|
||||
}
|
||||
|
||||
private Payment loadWechatPaymentConfig(Integer tenantId) {
|
||||
if (tenantId == null) {
|
||||
return null;
|
||||
}
|
||||
// 与微信支付回调一致:Payment:1:{tenantId}
|
||||
String key = "Payment:1:" + tenantId;
|
||||
try {
|
||||
return redisUtil.get(key, Payment.class);
|
||||
} catch (Exception e) {
|
||||
log.warn("读取支付配置失败 - key={}", key, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private String buildItemDesc(Integer orderId) {
|
||||
try {
|
||||
List<ShopOrderGoods> orderGoodsList = shopOrderGoodsService.getListByOrderId(orderId);
|
||||
if (orderGoodsList == null || orderGoodsList.isEmpty()) {
|
||||
return "订单商品";
|
||||
}
|
||||
ShopGoods shopGoods = shopGoodsService.getById(orderGoodsList.get(0).getGoodsId());
|
||||
String itemDesc = shopGoods != null && StrUtil.isNotBlank(shopGoods.getName()) ? shopGoods.getName() : "订单商品";
|
||||
if (orderGoodsList.size() > 1) {
|
||||
itemDesc += "等" + orderGoodsList.size() + "件商品";
|
||||
}
|
||||
return itemDesc;
|
||||
} catch (Exception e) {
|
||||
log.warn("构建微信发货 item_desc 失败 - orderId={}", orderId, e);
|
||||
return "订单商品";
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user