微信发货接口对接,逻辑编写

This commit is contained in:
Jack 2025-06-06 01:10:48 +08:00
parent dfbd7cd2b5
commit 29e195492b
7 changed files with 211 additions and 20 deletions

View File

@ -0,0 +1,34 @@
/*
* Copyright (c) 2024. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
* Morbi non lorem porttitor neque feugiat blandit. Ut vitae ipsum eget quam lacinia accumsan.
* Etiam sed turpis ac ipsum condimentum fringilla. Maecenas magna.
* Proin dapibus sapien vel ante. Aliquam erat volutpat. Pellentesque sagittis ligula eget metus.
* Vestibulum commodo. Ut rhoncus gravida arcu.
*/
package com.suisung.mall.common.pojo.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@AllArgsConstructor
@NoArgsConstructor
/**
* 标准的地址实体类
*/
public class WxOrderBaseInfoDTO implements Serializable {
private Integer store_id; // 店铺Id
private String out_trade_no; // 商户订单号
private String transaction_id; // 原支付交易对应的微信订单号
private String subject; // 订单标题
private Integer user_id; // 用户Id
private String openid; // 微信 openId
private String lkl_merchant_no; // 拉卡拉商户号
private String lkl_term_no; // 拉卡拉终端号
private String da_mobile; // 收货人手机
private String da_name; // 收货人姓名
}

View File

@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.suisung.mall.common.modules.order.ShopOrderBase;
import com.suisung.mall.common.modules.order.dto.MchOrderInfoDTO;
import com.suisung.mall.common.pojo.dto.WxOrderBaseInfoDTO;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
@ -69,4 +70,13 @@ public interface ShopOrderBaseMapper extends BaseMapper<ShopOrderBase> {
* @return
*/
IPage<MchOrderInfoDTO> selectMchOrderPageList(@Param("storeId") Integer storeId, @Param("keyword") String keyword, @Param("delivery") Integer delivery, @Param("status") Integer status, @Param("expireSeconds") Long expireSeconds, IPage<MchOrderInfoDTO> page);
/**
* 获取微信发货订单基本信息
*
* @param orderId
* @return
*/
WxOrderBaseInfoDTO getWxOrderBaseInfo(@Param("orderId") String orderId);
}

View File

@ -7,6 +7,7 @@ import com.suisung.mall.common.modules.order.ShopOrderBase;
import com.suisung.mall.common.modules.order.ShopOrderInfo;
import com.suisung.mall.common.modules.order.ShopOrderLogistics;
import com.suisung.mall.common.modules.order.dto.MchOrderInfoDTO;
import com.suisung.mall.common.pojo.dto.WxOrderBaseInfoDTO;
import com.suisung.mall.common.pojo.req.SFCreateOrderReq;
import com.suisung.mall.core.web.service.IBaseService;
import com.suisung.mall.shop.order.vo.OrdeListVo;
@ -556,4 +557,12 @@ public interface ShopOrderBaseService extends IBaseService<ShopOrderBase> {
* @return
*/
JSONObject mchOrderCountByStoreId(Integer storeId);
/**
* 获取微信发货订单基本信息
*
* @param orderId
* @return
*/
WxOrderBaseInfoDTO getWxOrderBaseInfo(String orderId);
}

View File

@ -57,6 +57,7 @@ import com.suisung.mall.common.modules.product.ShopProductValidPeriod;
import com.suisung.mall.common.modules.store.*;
import com.suisung.mall.common.modules.user.*;
import com.suisung.mall.common.pojo.dto.StandardAddressDTO;
import com.suisung.mall.common.pojo.dto.WxOrderBaseInfoDTO;
import com.suisung.mall.common.pojo.req.*;
import com.suisung.mall.common.pojo.to.MsgTO;
import com.suisung.mall.common.pojo.to.PayMoneyTO;
@ -8641,6 +8642,21 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
return jsonObject;
}
/**
* 获取微信发货订单基本信息
*
* @param orderId
* @return
*/
@Override
public WxOrderBaseInfoDTO getWxOrderBaseInfo(String orderId) {
if (StrUtil.isBlank(orderId)) {
return null;
}
return shopOrderBaseMapper.getWxOrderBaseInfo(orderId);
}
/**
* 取货单号格式化
*

View File

@ -16,12 +16,13 @@ public interface WxOrderShippingService {
* 上传发货信息到微信
* 参考https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/business-capabilities/order-shipping/order-shipping.html#%E4%B8%80%E3%80%81%E5%8F%91%E8%B4%A7%E4%BF%A1%E6%81%AF%E5%BD%95%E5%85%A5%E6%8E%A5%E5%8F%A3
*
* @param orderId 订单ID
* @param expressNo 快递公司编号
* @param trackingNo 物流单号
* @param logisticsType 物流模式发货方式枚举值1实体物流配送采用快递公司进行实体物流配送形式 2同城配送 3虚拟商品虚拟商品例如话费充值点卡等无实体配送形式 4用户自提
* @param orderId 订单ID
* @param expressNo 快递公司编号
* @param trackingNo 物流单号
* @return 返回上传结果包含成功状态和错误消息
*/
Pair<Boolean, String> uploadShippingInfoToWx(String orderId, String expressNo, String trackingNo);
Pair<Boolean, String> uploadShippingInfoToWx(Integer logisticsType, String orderId, String expressNo, String trackingNo);
/**
* 通知微信用户确认收货

View File

@ -9,15 +9,23 @@
package com.suisung.mall.shop.wechat.service.impl;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import com.suisung.mall.common.pojo.dto.WxOrderBaseInfoDTO;
import com.suisung.mall.common.utils.DateTimeUtils;
import com.suisung.mall.common.utils.RestTemplateHttpUtil;
import com.suisung.mall.shop.order.service.ShopOrderBaseService;
import com.suisung.mall.shop.wechat.service.WxOrderShippingService;
import com.suisung.mall.shop.wechat.utils.WxUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.util.Pair;
import org.springframework.stereotype.Service;
import org.springframework.web.client.HttpClientErrorException;
import java.time.LocalDateTime;
@Slf4j
@Service
@ -27,20 +35,87 @@ public class WxOrderShippingServiceImpl implements WxOrderShippingService {
@Autowired
private WxUtil wxUtil;
@Lazy
@Autowired
private ShopOrderBaseService shopOrderBaseService;
/**
* 上传发货信息到微信
* 参考https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/business-capabilities/order-shipping/order-shipping.html#%E4%B8%80%E3%80%81%E5%8F%91%E8%B4%A7%E4%BF%A1%E6%81%AF%E5%BD%95%E5%85%A5%E6%8E%A5%E5%8F%A3
*
* @param orderId 订单ID
* @param expressNo 快递公司编号
* @param trackingNo 物流单号
* @param logisticsType 物流模式发货方式枚举值1实体物流配送采用快递公司进行实体物流配送形式 2同城配送 3虚拟商品虚拟商品例如话费充值点卡等无实体配送形式 4用户自提
* @param orderId 订单ID
* @param expressNo 快递公司编号
* @param trackingNo 物流单号
* @return 返回上传结果包含成功状态和错误消息
*/
@Override
public Pair<Boolean, String> uploadShippingInfoToWx(String orderId, String expressNo, String trackingNo) {
return null;
public Pair<Boolean, String> uploadShippingInfoToWx(Integer logisticsType, String orderId, String expressNo, String trackingNo) {
final String PARAMETER_ERROR = "必要参数不能为空";
final String ACCESS_TOKEN_ERROR = "获取AccessToken失败";
final String ORDER_RECORD_ERROR = "无法获取订单记录";
final String SHIPPING_INFO_ERROR = "发货信息录入失败";
if (StringUtils.isAnyBlank(orderId, expressNo, trackingNo)) {
log.error(PARAMETER_ERROR);
return Pair.of(false, PARAMETER_ERROR);
}
try {
String accessToken = wxUtil.getAccessToken();
if (StrUtil.isBlank(accessToken)) {
log.error("{}订单ID: {}", ACCESS_TOKEN_ERROR, orderId);
return Pair.of(false, ACCESS_TOKEN_ERROR);
}
WxOrderBaseInfoDTO orderBaseInfo = shopOrderBaseService.getWxOrderBaseInfo(orderId);
if (orderBaseInfo == null) {
log.error("{}订单ID: {}", ORDER_RECORD_ERROR, orderId);
return Pair.of(false, ORDER_RECORD_ERROR);
}
JSONObject paramsJSON = new JSONObject()
.set("order_key.order_number_type", 2)
.set("order_key.transaction_id", orderBaseInfo.getTransaction_id())
.set("delivery_mode", 1)
.set("logistics_type", logisticsType)
.set("shipping_list", new JSONArray())
.set("upload_time", DateTimeUtils.formatDateTime(LocalDateTime.now(), "yyyy-MM-dd'T'HH:mm:ss.SSSXXX"))
.set("payer.openid", orderBaseInfo.getOpenid());
JSONObject respObj = RestTemplateHttpUtil.sendPost(
"https://api.weixin.qq.com/wxa/sec/order/upload_shipping_info?access_token=" + accessToken,
null, paramsJSON, JSONObject.class
);
if (respObj == null) {
log.error("{}订单ID: {}, 返回结果为空", SHIPPING_INFO_ERROR, orderId);
return Pair.of(false, SHIPPING_INFO_ERROR + ",返回结果为空");
}
int errCode = respObj.getInt("errcode", -1);
if (errCode != 0) {
String errorMsg = respObj.getStr("errmsg", "未知错误");
log.error("{}订单ID: {}, 错误码: {}, 错误信息: {}", SHIPPING_INFO_ERROR, orderId, errCode, errorMsg);
return Pair.of(false, SHIPPING_INFO_ERROR + ": " + errorMsg);
}
log.info("发货信息录入成功, 订单ID: {}", orderId);
return Pair.of(true, "发货信息录入成功");
} catch (HttpClientErrorException e) {
log.error("HTTP请求错误订单ID: {}, 状态码: {}, 错误信息: {}", orderId, e.getStatusCode(), e.getMessage(), e);
return Pair.of(false, "HTTP请求错误: " + e.getMessage());
} catch (IllegalArgumentException e) {
log.error("参数错误订单ID: {}, 错误信息: {}", orderId, e.getMessage(), e);
return Pair.of(false, "参数错误: " + e.getMessage());
} catch (Exception e) {
log.error("{}订单ID: {}, 错误信息: {}", SHIPPING_INFO_ERROR, orderId, e.getMessage(), e);
return Pair.of(false, SHIPPING_INFO_ERROR + ": " + e.getMessage());
}
}
/**
* 通知微信用户确认收货
* 参考https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/business-capabilities/order-shipping/order-shipping.html#%E4%BA%94%E3%80%81%E7%A1%AE%E8%AE%A4%E6%94%B6%E8%B4%A7%E6%8F%90%E9%86%92%E6%8E%A5%E5%8F%A3
@ -55,34 +130,48 @@ public class WxOrderShippingServiceImpl implements WxOrderShippingService {
}
try {
// Cache or reuse accessToken to reduce redundant API calls
String accessToken = wxUtil.getAccessToken();
if (StrUtil.isBlank(accessToken)) {
log.error("获取AccessToken失败订单ID: {}", orderId);
return Pair.of(false, "获取AccessToken失败");
}
JSONObject paramsJSON = new JSONObject();
paramsJSON.put("transaction_id", orderId);
paramsJSON.put("merchant_id", orderId);
paramsJSON.put("merchant_trade_no", orderId);
paramsJSON.put("received_time", System.currentTimeMillis() / 1000);
WxOrderBaseInfoDTO orderBaseInfo = shopOrderBaseService.getWxOrderBaseInfo(orderId);
if (orderBaseInfo == null) {
log.error("无法获取订单记录订单ID: {}", orderId);
return Pair.of(false, "无法获取订单记录");
}
JSONObject paramsJSON = new JSONObject()
.set("transaction_id", orderBaseInfo.getTransaction_id())
.set("merchant_id", orderBaseInfo.getLkl_merchant_no())
.set("merchant_trade_no", orderId)
.set("received_time", System.currentTimeMillis() / 1000);
JSONObject respObj = RestTemplateHttpUtil.sendPost(
"https://api.weixin.qq.com/wxa/sec/order/notify_confirm_receive?access_token=" + accessToken,
null, paramsJSON, JSONObject.class
);
JSONObject respObj = RestTemplateHttpUtil.sendPost("https://api.weixin.qq.com/wxa/sec/order/notify_confirm_receive?access_token=" + accessToken, null, paramsJSON, JSONObject.class);
if (respObj == null) {
log.error("通知微信用户确认收货失败订单ID: {}, 返回结果为空", orderId);
return Pair.of(false, "通知微信用户确认收货失败,返回结果为空");
}
if (respObj.getInt("errcode") != 0) {
String errorMsg = respObj.getStr("errmsg");
log.error("通知微信用户确认收货失败订单ID: {}, 错误信息: {}", orderId, errorMsg);
int errCode = respObj.getInt("errcode", -1); // Default to -1 if errcode is missing
if (errCode != 0) {
String errorMsg = respObj.getStr("errmsg", "未知错误");
log.error("通知微信用户确认收货失败订单ID: {}, 错误码: {}, 错误信息: {}", orderId, errCode, errorMsg);
return Pair.of(false, "通知微信用户确认收货失败: " + errorMsg);
}
log.info("通知微信用户确认收货成功,订单号:{}", orderId);
log.info("通知微信用户确认收货成功, 订单ID: {}", orderId);
return Pair.of(true, "通知微信用户确认收货成功");
} catch (HttpClientErrorException e) {
log.error("HTTP请求错误订单ID: {}, 状态码: {}, 错误信息: {}", orderId, e.getStatusCode(), e.getMessage(), e);
return Pair.of(false, "HTTP请求错误: " + e.getMessage());
} catch (Exception e) {
log.error("通知微信确认收货失败订单ID: {}, 错误信息: {}", orderId, e.getMessage(), e);
return Pair.of(false, "通知微信确认收货失败: " + e.getMessage());

View File

@ -8,6 +8,20 @@
buyer_user_id, buyer_user_name, order_state_id, easy_pay_id
</sql>
<resultMap id="WxOrderBaseInfoResult" type="com.suisung.mall.common.pojo.dto.WxOrderBaseInfoDTO">
<!--订单对象映射-->
<result property="store_id" column="store_id"/>
<result property="out_trade_no" column="out_trade_no"/>
<result property="transaction_id" column="transaction_id"/>
<result property="subject" column="subject"/>
<result property="user_id" column="user_id"/>
<result property="openid" column="openid"/>
<result property="lkl_merchant_no" column="lkl_merchant_no"/>
<result property="lkl_term_no" column="lkl_term_no"/>
<result property="da_mobile" column="da_mobile"/>
<result property="da_name" column="da_name"/>
</resultMap>
<select id="getList" resultType="java.util.Map">
SELECT a.order_id,
a.order_time,
@ -737,4 +751,22 @@
</where>
order by ob.order_time desc
</select>
<select id="getWxOrderBaseInfo" resultMap="WxOrderBaseInfoResult">
SELECT a.store_id,
a.order_id as out_trade_no,
c.deposit_trade_no as transaction_id,
c.deposit_subject as subject,
c.user_id,
c.deposit_buyer_id as openid,
d.lkl_merchant_no,
d.lkl_term_no,
e.da_mobile,
e.da_name
FROM shop_order_base a
JOIN pay_consume_deposit c on a.order_id = c.order_id
JOIN shop_store_base d on a.store_id = d.store_id
LEFT JOIN shop_order_delivery_address e on a.order_id = e.order_id
where a.order_id = #{orderId}
</select>
</mapper>