小程序自动发货

This commit is contained in:
Kroytz committed 2026-07-11 06:55:10 +08:00
1 parent b7fe79b347
commit 88563423fa
3 files changed
+88 -3

No files matched your search

@@ -38,6 +38,8 @@ public interface ErrorCodeConstants {
ErrorCode STORE_ORDER_DESK_NOT = new ErrorCode(1008007023, "当前桌号不存在"); ErrorCode STORE_ORDER_DESK_NOT = new ErrorCode(1008007023, "当前桌号不存在");
ErrorCode STORE_ORDER_OUT_OF_DELIVERY_RANGE = new ErrorCode(1008007024, "当前收货地址不在配送范围内"); ErrorCode STORE_ORDER_OUT_OF_DELIVERY_RANGE = new ErrorCode(1008007024, "当前收货地址不在配送范围内");
ErrorCode STORE_ORDER_MIN_PRICE_NOT_MET = new ErrorCode(1008007025, "未达到外卖起送价"); ErrorCode STORE_ORDER_MIN_PRICE_NOT_MET = new ErrorCode(1008007025, "未达到外卖起送价");
ErrorCode WECHAT_SHIPPING_DELIVERY_NO_REQUIRED = new ErrorCode(1008007026, "请填写同城配送单号");
ErrorCode WECHAT_SHIPPING_UPLOAD_FAILED = new ErrorCode(1008007027, "微信发货信息上报失败");
@@ -3,6 +3,11 @@ package co.yixiang.yshop.module.order.service.storeorder;
import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.binarywang.wx.miniapp.api.WxMaService;
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 co.yixiang.yshop.framework.common.constant.ShopConstants; import co.yixiang.yshop.framework.common.constant.ShopConstants;
import co.yixiang.yshop.framework.common.enums.OrderInfoEnum; import co.yixiang.yshop.framework.common.enums.OrderInfoEnum;
import co.yixiang.yshop.framework.common.enums.PayIdEnum; import co.yixiang.yshop.framework.common.enums.PayIdEnum;
@@ -30,6 +35,8 @@ import co.yixiang.yshop.module.order.enums.OrderLogEnum;
import co.yixiang.yshop.module.order.enums.PayTypeEnum; import co.yixiang.yshop.module.order.enums.PayTypeEnum;
import co.yixiang.yshop.module.order.enums.UpdateOrderEnum; import co.yixiang.yshop.module.order.enums.UpdateOrderEnum;
import co.yixiang.yshop.module.order.service.storeorderstatus.StoreOrderStatusService; import co.yixiang.yshop.module.order.service.storeorderstatus.StoreOrderStatusService;
import co.yixiang.yshop.module.pay.dal.dataobject.merchantdetails.MerchantDetailsDO;
import co.yixiang.yshop.module.pay.service.merchantdetails.MerchantDetailsService;
import co.yixiang.yshop.module.product.service.storeproduct.AppStoreProductService; import co.yixiang.yshop.module.product.service.storeproduct.AppStoreProductService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.egzosn.pay.common.bean.RefundOrder; import com.egzosn.pay.common.bean.RefundOrder;
@@ -37,6 +44,7 @@ import com.egzosn.pay.common.bean.RefundResult;
import com.egzosn.pay.spring.boot.core.PayServiceManager; import com.egzosn.pay.spring.boot.core.PayServiceManager;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import org.redisson.api.RBlockingDeque; import org.redisson.api.RBlockingDeque;
import org.redisson.api.RDelayedQueue; import org.redisson.api.RDelayedQueue;
import org.redisson.api.RedissonClient; import org.redisson.api.RedissonClient;
@@ -48,6 +56,9 @@ import org.springframework.validation.annotation.Validated;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -89,6 +100,10 @@ public class StoreOrderServiceImpl implements StoreOrderService {
private AppStoreProductService appStoreProductService; private AppStoreProductService appStoreProductService;
@Resource @Resource
private PayServiceManager manager; private PayServiceManager manager;
@Resource
private WxMaService wxMaService;
@Resource
private MerchantDetailsService merchantDetailsService;
@Resource @Resource
private AsyncStoreOrderService asyncStoreOrderService; private AsyncStoreOrderService asyncStoreOrderService;
@@ -119,7 +134,8 @@ public class StoreOrderServiceImpl implements StoreOrderService {
//发货自取模式 直接收货 //发货自取模式 直接收货
if(UpdateOrderEnum.ORDER_SEND.getValue().equals(updateReqVO.getUpdateType()) if(UpdateOrderEnum.ORDER_SEND.getValue().equals(updateReqVO.getUpdateType())
&& updateObj.getOrderType().equals(OrderLogEnum.ORDER_TAKE_IN.getValue())){ && updateObj2.getOrderType().equals(OrderLogEnum.ORDER_TAKE_IN.getValue())){
uploadWechatShippingInfo(updateObj2, null);
this.takeStoreOrder(updateObj.getId()); this.takeStoreOrder(updateObj.getId());
//todo 异步打印小票 商业版本才有 官网购买:https://www.yixiang.co //todo 异步打印小票 商业版本才有 官网购买:https://www.yixiang.co
@@ -127,14 +143,19 @@ public class StoreOrderServiceImpl implements StoreOrderService {
} }
//发货-外卖模式 //发货-外卖模式
if(UpdateOrderEnum.ORDER_SEND.getValue().equals(updateReqVO.getUpdateType()) if(UpdateOrderEnum.ORDER_SEND.getValue().equals(updateReqVO.getUpdateType())
&& updateObj.getOrderType().equals(OrderLogEnum.ORDER_TAKE_OUT.getValue())){ && updateObj2.getOrderType().equals(OrderLogEnum.ORDER_TAKE_OUT.getValue())){
if (StrUtil.isBlank(updateObj.getDeliveryId())) {
throw exception(WECHAT_SHIPPING_DELIVERY_NO_REQUIRED);
}
updateObj.setDeliveryName(StrUtil.emptyToDefault(updateObj.getDeliveryName(), "同城配送"));
uploadWechatShippingInfo(updateObj2, updateObj.getDeliveryId());
updateObj.setStatus(OrderInfoEnum.STATUS_1.getValue()); updateObj.setStatus(OrderInfoEnum.STATUS_1.getValue());
//todo 异步打印小票 商业版本才有 官网购买:https://www.yixiang.co //todo 异步打印小票 商业版本才有 官网购买:https://www.yixiang.co
} }
//堂食模式 //堂食模式
if(UpdateOrderEnum.ORDER_SEND.getValue().equals(updateReqVO.getUpdateType()) if(UpdateOrderEnum.ORDER_SEND.getValue().equals(updateReqVO.getUpdateType())
&& updateObj.getOrderType().equals(OrderLogEnum.ORDER_TAKE_DESK.getValue())){ && updateObj2.getOrderType().equals(OrderLogEnum.ORDER_TAKE_DESK.getValue())){
//todo 异步打印小票 商业版本才有 官网购买:https://www.yixiang.co //todo 异步打印小票 商业版本才有 官网购买:https://www.yixiang.co
@@ -175,6 +196,60 @@ public class StoreOrderServiceImpl implements StoreOrderService {
} }
private void uploadWechatShippingInfo(StoreOrderDO order, String deliveryNo) {
if (!PayTypeEnum.WEIXIN.getValue().equals(order.getPayType())) {
return;
}
boolean sameCityDelivery = OrderLogEnum.ORDER_TAKE_OUT.getValue().equals(order.getOrderType());
if (sameCityDelivery && StrUtil.isBlank(deliveryNo)) {
throw exception(WECHAT_SHIPPING_DELIVERY_NO_REQUIRED);
}
MemberUserDO payer = userService.getUser(order.getUid());
if (payer == null || StrUtil.isBlank(payer.getRoutineOpenid())) {
log.warn("[uploadWechatShippingInfo][skip non-miniapp orderId={}]", order.getOrderId());
return;
}
MerchantDetailsDO merchant = merchantDetailsService.getMerchantDetails(PayIdEnum.WX_MINIAPP.getValue());
if (merchant == null || StrUtil.isBlank(merchant.getMchId())) {
log.warn("[uploadWechatShippingInfo][skip missing miniapp merchant config, orderId={}]", order.getOrderId());
return;
}
String itemDesc = buildItemDesc(order.getId());
ShippingListBean shipping = ShippingListBean.builder().itemDesc(itemDesc).build();
if (sameCityDelivery) {
shipping.setTrackingNo(deliveryNo);
}
WxMaOrderShippingInfoUploadRequest request = WxMaOrderShippingInfoUploadRequest.builder()
.orderKey(OrderKeyBean.builder().orderNumberType(1).mchId(merchant.getMchId()).outTradeNo(order.getOrderId()).build())
.logisticsType(sameCityDelivery ? 2 : 4)
.deliveryMode(1)
.shippingList(List.of(shipping))
.uploadTime(OffsetDateTime.now(ZoneId.of("Asia/Shanghai")).format(DateTimeFormatter.ISO_OFFSET_DATE_TIME))
.payer(PayerBean.builder().openid(payer.getRoutineOpenid()).build())
.build();
try {
wxMaService.getWxMaOrderShippingService().upload(request);
} catch (WxErrorException e) {
log.error("[uploadWechatShippingInfo][orderId={} upload failed]", order.getOrderId(), e);
throw exception(WECHAT_SHIPPING_UPLOAD_FAILED);
}
}
private String buildItemDesc(Long orderId) {
List<StoreOrderCartInfoDO> items = storeOrderCartInfoMapper.selectList(new LambdaQueryWrapper<StoreOrderCartInfoDO>()
.eq(StoreOrderCartInfoDO::getOid, orderId));
StringBuilder desc = new StringBuilder();
for (StoreOrderCartInfoDO item : items) {
if (desc.length() > 0) {
desc.append('、');
}
desc.append(item.getTitle()).append('*').append(item.getNumber());
}
return StrUtil.subPre(StrUtil.emptyToDefault(desc.toString(), "商品"), 120);
}
@Override @Override
public void deleteStoreOrder(Long id) { public void deleteStoreOrder(Long id) {
// 校验存在 // 校验存在
@@ -19,6 +19,14 @@
<el-form-item label="地址" prop="orderType" v-if="formData.orderType == 'takeout'"> <el-form-item label="地址" prop="orderType" v-if="formData.orderType == 'takeout'">
<span>{{ formData.realName }}{{ formData.userPhone }}{{ formData.userAddress }}</span> <span>{{ formData.realName }}{{ formData.userPhone }}{{ formData.userAddress }}</span>
</el-form-item> </el-form-item>
<el-form-item
v-if="formData.orderType == 'takeout'"
label="同城配送单号"
prop="deliveryId"
:rules="[{ required: true, message: '同城配送单号不能为空', trigger: 'blur' }]"
>
<el-input v-model="formData.deliveryId" placeholder="请输入同城配送单号" class="input-width" />
</el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
<el-button @click="submitForm" type="primary" :disabled="formLoading">打 印</el-button> <el-button @click="submitForm" type="primary" :disabled="formLoading">打 印</el-button>