From 55642c818f14bf8cf52c98e6858014bd8dc3d3a7 Mon Sep 17 00:00:00 2001
From: rk <94314517@qq.com>
Date: 星期四, 16 四月 2026 20:10:58 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/master'

---
 server/services/src/main/java/com/doumee/service/business/impl/OrdersServiceImpl.java | 2739 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 2,733 insertions(+), 6 deletions(-)

diff --git a/server/services/src/main/java/com/doumee/service/business/impl/OrdersServiceImpl.java b/server/services/src/main/java/com/doumee/service/business/impl/OrdersServiceImpl.java
index 414e48e..5224b0f 100644
--- a/server/services/src/main/java/com/doumee/service/business/impl/OrdersServiceImpl.java
+++ b/server/services/src/main/java/com/doumee/service/business/impl/OrdersServiceImpl.java
@@ -4,23 +4,57 @@
 import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.doumee.biz.system.AreasBiz;
+import com.doumee.biz.system.OperationConfigBiz;
+import com.doumee.biz.system.SystemDictDataBiz;
+import com.doumee.config.wx.WxMiniConfig;
+import com.doumee.config.wx.WxMiniUtilService;
 import com.doumee.core.constants.Constants;
 import com.doumee.core.constants.ResponseStatus;
 import com.doumee.core.exception.BusinessException;
 import com.doumee.core.model.PageData;
 import com.doumee.core.model.PageWrap;
+import com.doumee.core.utils.DateUtil;
+import com.doumee.core.utils.Tencent.MapUtil;
 import com.doumee.core.utils.Utils;
-import com.doumee.dao.business.OrdersMapper;
-import com.doumee.dao.business.model.Category;
-import com.doumee.dao.business.model.Orders;
+import com.doumee.dao.business.*;
+import com.doumee.dao.business.model.*;
+import com.doumee.dao.system.SystemUserMapper;
+import com.doumee.dao.system.model.SystemDictData;
+import com.doumee.dao.system.model.SystemUser;
+import com.doumee.dao.dto.CalculateLocalPriceDTO;
+import com.doumee.dao.dto.CalculateRemotePriceDTO;
+import com.doumee.dao.dto.CommentOrderDTO;
+import com.doumee.dao.dto.CreateOrderDTO;
+import com.doumee.dao.dto.DispatchDTO;
+import com.doumee.dao.dto.MyOrderDTO;
+import com.doumee.dao.dto.OrderItemDTO;
+import com.doumee.dao.vo.*;
+import com.doumee.service.business.OrderLogService;
 import com.doumee.service.business.OrdersService;
+import com.doumee.service.business.AreasService;
+import com.github.binarywang.wxpay.bean.request.BaseWxPayRequest;
+import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
+import com.github.binarywang.wxpay.exception.WxPayException;
+import com.github.xiaoymin.knife4j.core.util.CollectionUtils;
 import com.github.yulichang.wrapper.MPJLambdaWrapper;
+import com.alibaba.fastjson.JSONObject;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
 
-import java.util.List;
-import java.util.Objects;
+import javax.servlet.http.HttpServletRequest;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
 
 /**
  * 瀵勫瓨璁㈠崟淇℃伅Service瀹炵幇
@@ -32,6 +66,62 @@
 
     @Autowired
     private OrdersMapper ordersMapper;
+
+    @Autowired
+    private MemberMapper memberMapper;
+
+    @Autowired
+    private ShopInfoMapper shopInfoMapper;
+
+    @Autowired
+    private DriverInfoMapper driverInfoMapper;
+
+    @Autowired
+    private CategoryMapper categoryMapper;
+
+    @Autowired
+    private MultifileMapper multifileMapper;
+
+    @Autowired
+    private OrdersDetailMapper ordersDetailMapper;
+
+    @Autowired
+    private SystemDictDataBiz systemDictDataBiz;
+
+    @Autowired
+    private OrderLogService orderLogService;
+
+    @Autowired
+    private OrdersRefundMapper ordersRefundMapper;
+
+    @Autowired
+    private OtherOrdersMapper otherOrdersMapper;
+
+    @Autowired
+    private OrderCommentMapper orderCommentMapper;
+
+    @Autowired
+    private RevenueMapper revenueMapper;
+
+    @Autowired
+    private WxMiniUtilService wxMiniUtilService;
+
+    @Autowired
+    private SystemUserMapper systemUserMapper;
+
+    @Autowired
+    private PricingRuleMapper pricingRuleMapper;
+    @Autowired
+    private RedisTemplate<String, Object> redisTemplate;
+
+    @Autowired
+    private AreasBiz areasBiz;
+
+    @Autowired
+    private OperationConfigBiz operationConfigBiz;
+
+    @Autowired
+    private AreasService areasService;
 
     @Override
     public Integer create(Orders orders) {
@@ -102,11 +192,18 @@
         MPJLambdaWrapper<Orders> queryWrapper = new MPJLambdaWrapper<Orders>()
                 .selectAll(Orders.class)
                 .selectAs(Category::getDetail, Orders::getOrderLevel)
-                .leftJoin(Category.class, Category::getId, Orders::getGoodType);
+                .select("s1.name", Orders::getDepositShopName)
+                .leftJoin(Category.class, Category::getId, Orders::getGoodType)
+                .leftJoin(DriverInfo.class, DriverInfo::getId, Orders::getAcceptDriver)
+                .leftJoin("shop_info s1 on s1.id = t.DEPOSIT_SHOP_ID")
+                .leftJoin("shop_info s2 on s2.id = t.TAKE_SHOP_ID");
+                ;
         Utils.MP.blankToNull(pageWrap.getModel());
         pageWrap.getModel().setDeleted(Constants.ZERO);
         queryWrapper.eq(pageWrap.getModel().getDeleted() != null, Orders::getDeleted, pageWrap.getModel().getDeleted());
         queryWrapper.like(StringUtils.isNotBlank(pageWrap.getModel().getCode()), Orders::getCode, pageWrap.getModel().getCode());
+        queryWrapper.like(StringUtils.isNotBlank(pageWrap.getModel().getDepositShopName()), "s1.name", pageWrap.getModel().getDepositShopName());
+        queryWrapper.like(StringUtils.isNotBlank(pageWrap.getModel().getTakeShopName()),  "s2.name",  pageWrap.getModel().getTakeShopName());
         queryWrapper.like(StringUtils.isNotBlank(pageWrap.getModel().getGoodsInfo()), Orders::getGoodsInfo, pageWrap.getModel().getGoodsInfo());
         queryWrapper.ge(pageWrap.getModel().getCreateStartTime() != null, Orders::getCreateTime, Utils.Date.getStart(pageWrap.getModel().getCreateStartTime()));
         queryWrapper.le(pageWrap.getModel().getCreateEndTime() != null, Orders::getCreateTime, Utils.Date.getEnd(pageWrap.getModel().getCreateEndTime()));
@@ -114,6 +211,8 @@
         queryWrapper.eq(pageWrap.getModel().getType() != null, Orders::getType, pageWrap.getModel().getType());
         queryWrapper.eq(pageWrap.getModel().getStatus() != null, Orders::getStatus, pageWrap.getModel().getStatus());
         queryWrapper.eq(pageWrap.getModel().getTakeShopId() != null, Orders::getTakeShopId, pageWrap.getModel().getTakeShopId());
+        queryWrapper.and(pageWrap.getModel().getDriverKeyword() != null, i->i.like(DriverInfo::getName, pageWrap.getModel().getDriverKeyword())
+                .or().like(DriverInfo::getTelephone, pageWrap.getModel().getDriverKeyword()));
         for (PageWrap.SortData sortData : pageWrap.getSorts()) {
             if (sortData.getDirection().equalsIgnoreCase(PageWrap.DESC)) {
                 queryWrapper.orderByDesc(sortData.getProperty());
@@ -125,9 +224,2637 @@
     }
 
     @Override
+    public OrderSummaryVO findSummary(PageWrap<Orders> pageWrap) {
+        // 鏋勫缓涓巉indPage鐩稿悓鐨勬煡璇㈡潯浠�
+        MPJLambdaWrapper<Orders> queryWrapper = new MPJLambdaWrapper<Orders>()
+                .leftJoin(Category.class, Category::getId, Orders::getGoodType)
+                .leftJoin(DriverInfo.class, DriverInfo::getId, Orders::getAcceptDriver)
+                .leftJoin("shop_info s1 on s1.id = t.DEPOSIT_SHOP_ID")
+                .leftJoin("shop_info s2 on s2.id = t.TAKE_SHOP_ID");
+        Utils.MP.blankToNull(pageWrap.getModel());
+        pageWrap.getModel().setDeleted(Constants.ZERO);
+        queryWrapper.eq(pageWrap.getModel().getDeleted() != null, Orders::getDeleted, pageWrap.getModel().getDeleted());
+        queryWrapper.like(StringUtils.isNotBlank(pageWrap.getModel().getCode()), Orders::getCode, pageWrap.getModel().getCode());
+        queryWrapper.like(StringUtils.isNotBlank(pageWrap.getModel().getDepositShopName()), "s1.name", pageWrap.getModel().getDepositShopName());
+        queryWrapper.like(StringUtils.isNotBlank(pageWrap.getModel().getTakeShopName()),  "s2.name",  pageWrap.getModel().getTakeShopName());
+        queryWrapper.like(StringUtils.isNotBlank(pageWrap.getModel().getGoodsInfo()), Orders::getGoodsInfo, pageWrap.getModel().getGoodsInfo());
+        queryWrapper.ge(pageWrap.getModel().getCreateStartTime() != null, Orders::getCreateTime, Utils.Date.getStart(pageWrap.getModel().getCreateStartTime()));
+        queryWrapper.le(pageWrap.getModel().getCreateEndTime() != null, Orders::getCreateTime, Utils.Date.getEnd(pageWrap.getModel().getCreateEndTime()));
+        queryWrapper.eq(pageWrap.getModel().getDepositShopId() != null, Orders::getDepositShopId, pageWrap.getModel().getDepositShopId());
+        queryWrapper.eq(pageWrap.getModel().getType() != null, Orders::getType, pageWrap.getModel().getType());
+        queryWrapper.eq(pageWrap.getModel().getStatus() != null, Orders::getStatus, pageWrap.getModel().getStatus());
+        queryWrapper.eq(pageWrap.getModel().getTakeShopId() != null, Orders::getTakeShopId, pageWrap.getModel().getTakeShopId());
+        queryWrapper.and(pageWrap.getModel().getDriverKeyword() != null, i->i.like(DriverInfo::getName, pageWrap.getModel().getDriverKeyword())
+                .or().like(DriverInfo::getTelephone, pageWrap.getModel().getDriverKeyword()));
+
+        queryWrapper.select(
+                "IFNULL(SUM(t.total_amount), 0) as total_amount_sum",
+                "IFNULL(SUM(CASE WHEN t.settlement_status = 1 THEN t.total_amount ELSE 0 END), 0) as settled_total_amount_sum",
+                "IFNULL(SUM(t.driver_fee), 0) as driver_fee_sum",
+                "IFNULL(SUM(CASE WHEN t.settlement_status = 1 THEN t.driver_fee ELSE 0 END), 0) as settled_driver_fee_sum"
+        );
+        queryWrapper.groupBy("1=1");
+
+        List<Map<String, Object>> result = ordersMapper.selectJoinMaps(queryWrapper);
+        OrderSummaryVO vo = new OrderSummaryVO();
+        if (result != null && !result.isEmpty()) {
+            Map<String, Object> row = result.get(0);
+            vo.setTotalAmountSum(toLong(row.get("total_amount_sum")));
+            vo.setSettledTotalAmountSum(toLong(row.get("settled_total_amount_sum")));
+            vo.setDriverFeeSum(toLong(row.get("driver_fee_sum")));
+            vo.setSettledDriverFeeSum(toLong(row.get("settled_driver_fee_sum")));
+        } else {
+            vo.setTotalAmountSum(0L);
+            vo.setSettledTotalAmountSum(0L);
+            vo.setDriverFeeSum(0L);
+            vo.setSettledDriverFeeSum(0L);
+        }
+        return vo;
+    }
+
+    private Long toLong(Object val) {
+        if (val == null) return 0L;
+        if (val instanceof Number) return ((Number) val).longValue();
+        return Long.parseLong(val.toString());
+    }
+
+    @Override
+    public BigDecimal calculateInsuranceFee(BigDecimal declaredValue) {
+        if (declaredValue == null || declaredValue.compareTo(BigDecimal.ZERO) <= 0) {
+            return BigDecimal.ZERO;
+        }
+        String rateStr = systemDictDataBiz.queryByCode(Constants.OPERATION_CONFIG, Constants.OP_INSURANCE_RATE).getCode();
+        BigDecimal rate = new BigDecimal(rateStr);
+        return declaredValue.multiply(rate).setScale(2, BigDecimal.ROUND_HALF_UP);
+    }
+
+    /**
+     * 璁$畻灏卞湴瀛樺彇棰勪及璐圭敤
+     *
+     * 璁$畻瑙勫垯锛�
+     * 1. 鏍规嵁鍩庡競+鐗╁搧绫诲瀷 鏌ヨ pricing_rule(type=0)锛宖ieldA=categoryId, fieldB=鍗曚环(鍒�/澶�)
+     * 2. 姣忛」灏忚 = 鍗曚环 脳 鏁伴噺 脳 澶╂暟
+     * 3. 鐗╁搧浠锋牸 = 鍚勯」灏忚涔嬪拰
+     * 4. 淇濅环璐圭敤 = 鎶ヤ环閲戦 脳 淇濅环璐圭巼锛堝瓧鍏� INSURANCE_RATE锛夛紝鍏冭浆鍒�
+     * 5. 鎬讳环鏍� = 鐗╁搧浠锋牸 + 淇濅环璐圭敤
+     *
+     * @param dto 灏卞湴瀛樺彇璁′环璇锋眰鍙傛暟
+     * @return 浠锋牸璁$畻缁撴灉
+     */
+    @Override
+    public PriceCalculateVO calculateLocalPrice(CalculateLocalPriceDTO dto) {
+        // 澶╂暟鏍¢獙锛屾渶灏�1澶�
+        int days = dto.getEstimatedDepositDays() != null && dto.getEstimatedDepositDays() > 0
+                ? dto.getEstimatedDepositDays() : 1;
+
+        // 鏀堕泦鎵�鏈夌墿鍝佺被鍨婭D
+        List<Integer> categoryIds = new ArrayList<>();
+        for (OrderItemDTO item : dto.getItems()) {
+            categoryIds.add(item.getCategoryId());
+        }
+
+        // 鎵归噺鏌ヨ璁′环瑙勫垯 pricing_rule type=0锛歠ieldA=categoryId, fieldB=鍗曚环(鍒�/澶�)
+        List<String> fieldAList = new ArrayList<>();
+        for (Integer cid : categoryIds) {
+            fieldAList.add(String.valueOf(cid));
+        }
+        List<PricingRule> rules = pricingRuleMapper.selectList(new QueryWrapper<PricingRule>().lambda()
+                .eq(PricingRule::getDeleted, Constants.ZERO)
+                .eq(PricingRule::getType, Constants.ZERO)
+                .eq(PricingRule::getCityId, dto.getCityId())
+                .in(PricingRule::getFieldA, fieldAList));
+        Map<String, PricingRule> ruleMap = new HashMap<>();
+        for (PricingRule r : rules) {
+            ruleMap.put(r.getFieldA(), r);
+        }
+
+        // 鎵归噺鏌ヨ鐗╁搧绫诲瀷鍚嶇О
+        List<Category> categories = categoryMapper.selectBatchIds(categoryIds);
+        Map<Integer, String> categoryNameMap = new HashMap<>();
+        Map<Integer, String> categoryDetailMap = new HashMap<>();
+        for (Category c : categories) {
+            categoryNameMap.put(c.getId(), c.getName());
+            categoryDetailMap.put(c.getId(), c.getDetail());
+        }
+
+        // 璁$畻姣忛」鐗╁搧璐圭敤锛氬皬璁� = 鍗曚环 脳 鏁伴噺 脳 澶╂暟
+        List<ItemPriceVO> itemList = new ArrayList<>();
+        long itemPriceTotal = 0L;
+
+        for (OrderItemDTO item : dto.getItems()) {
+            PricingRule rule = ruleMap.get(String.valueOf(item.getCategoryId()));
+            if (rule == null) {
+                throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(),
+                        "鏈壘鍒拌鍩庡競鐗╁搧绫诲瀷鐨勮浠疯鍒�");
+            }
+
+            long unitPrice = Long.parseLong(rule.getFieldB());
+            long subtotal = unitPrice * item.getQuantity() * days;
+
+            ItemPriceVO vo = new ItemPriceVO();
+            vo.setCategoryId(item.getCategoryId());
+            vo.setCategoryName(categoryNameMap.getOrDefault(item.getCategoryId(), ""));
+            vo.setDetail(categoryDetailMap.get(item.getCategoryId()));
+            vo.setQuantity(item.getQuantity());
+            vo.setUnitPrice(unitPrice);
+            vo.setLocallyPrice(unitPrice);
+            vo.setSubtotal(subtotal);
+            itemList.add(vo);
+
+            itemPriceTotal += subtotal;
+        }
+
+        // 淇濅环璐圭敤锛氭姤浠烽噾棰� 脳 淇濅环璐圭巼(瀛楀吀 INSURANCE_RATE)锛屽厓鈫掑垎
+        long insuranceFeeFen = 0L;
+        if (Boolean.TRUE.equals(dto.getInsured()) && dto.getDeclaredAmount() != null) {
+            BigDecimal insuranceFeeYuan = calculateInsuranceFee(dto.getDeclaredAmount());
+            insuranceFeeFen = insuranceFeeYuan.multiply(new BigDecimal(100)).longValue();
+        }
+
+        // 鎬讳环鏍� = 鐗╁搧浠锋牸 + 淇濅环璐圭敤
+        long totalPrice = itemPriceTotal + insuranceFeeFen;
+
+        PriceCalculateVO result = new PriceCalculateVO();
+        result.setItemList(itemList);
+        result.setItemPrice(itemPriceTotal);
+        result.setInsuranceFee(insuranceFeeFen);
+        result.setTotalPrice(totalPrice);
+        result.setDays(days);
+        result.setUrgentFee(0L);
+        return result;
+    }
+
+    /**
+     * 璁$畻寮傚湴瀛樺彇棰勪及璐圭敤
+     *
+     * 璁$畻瑙勫垯锛�
+     * 1. 璋冪敤鑵捐鍦板浘API璁$畻瀵勪欢鐐逛笌鍙栦欢鐐圭殑椹捐溅璺濈(绫斥啋鍏噷)
+     * 2. 鏍规嵁鍩庡競+鐗╁搧绫诲瀷 鏌ヨ pricing_rule(type=1)锛�
+     *    fieldB=璧锋璺濈(km), fieldC=璧锋浠�(鍒�), fieldD=瓒呭嚭璺濈鍗曚綅(km), fieldE=瓒呭嚭璺濈鍗曚环(鍒�)
+     * 3. 姣忛」杩愯垂鍗曚环锛�
+     *    - 璺濈 鈮� 璧锋璺濈 鈫� 鍗曚环 = 璧锋浠�
+     *    - 璺濈 > 璧锋璺濈 鈫� 鍗曚环 = 璧锋浠� + ceil((璺濈-璧锋璺濈)/瓒呭嚭璺濈鍗曚綅) 脳 瓒呭嚭璺濈鍗曚环
+     * 4. 灏忚 = 杩愯垂鍗曚环 脳 鏁伴噺
+     * 5. 鐗╁搧浠锋牸 = 鍚勯」灏忚涔嬪拰
+     * 6. 淇濅环璐圭敤 = 鎶ヤ环閲戦 脳 淇濅环璐圭巼锛堝瓧鍏� INSURANCE_RATE锛夛紝鍏冭浆鍒�
+     * 7. 鍔犳�ヨ垂鐢� = 鐗╁搧浠锋牸 脳 鍔犳�ョ郴鏁帮紙瀛楀吀 URGENT_COEFFICIENT锛�
+     * 8. 鎬讳环鏍� = 鐗╁搧浠锋牸 + 淇濅环璐圭敤 + 鍔犳�ヨ垂鐢�
+     *
+     * @param dto 寮傚湴瀛樺彇璁′环璇锋眰鍙傛暟
+     * @return 浠锋牸璁$畻缁撴灉
+     */
+    @Override
+    public PriceCalculateVO calculateRemotePrice(CalculateRemotePriceDTO dto) {
+        // 1. 璋冪敤鑵捐鍦板浘璺濈鐭╅樀API璁$畻椹捐溅璺濈
+        String from = dto.getFromLat() + "," + dto.getFromLgt();
+        String to = dto.getToLat() + "," + dto.getToLgt();
+        JSONObject distanceResult = MapUtil.distanceSingle("driving", from, to);
+        BigDecimal distance = distanceResult.getBigDecimal("distance");
+        // distance 鍗曚綅涓虹背锛岃浆涓哄叕閲�
+        BigDecimal distanceKm = distance.divide(new BigDecimal(1000), 2, RoundingMode.HALF_UP);
+
+        // 鏀堕泦鎵�鏈夌墿鍝佺被鍨婭D
+        List<Integer> categoryIds = new ArrayList<>();
+        for (OrderItemDTO item : dto.getItems()) {
+            categoryIds.add(item.getCategoryId());
+        }
+
+        // 2. 鎵归噺鏌ヨ閰嶉�佽浠疯鍒� pricing_rule type=1
+        List<String> fieldAList = new ArrayList<>();
+        for (Integer cid : categoryIds) {
+            fieldAList.add(String.valueOf(cid));
+        }
+        List<PricingRule> rules = pricingRuleMapper.selectList(new QueryWrapper<PricingRule>().lambda()
+                .eq(PricingRule::getDeleted, Constants.ZERO)
+                .eq(PricingRule::getType, Constants.ONE)
+                .eq(PricingRule::getCityId, dto.getCityId())
+                .in(PricingRule::getFieldA, fieldAList));
+        Map<String, PricingRule> ruleMap = new HashMap<>();
+        for (PricingRule r : rules) {
+            ruleMap.put(r.getFieldA(), r);
+        }
+
+        // 鏌ヨ灏卞湴瀛樺彇璁′环瑙勫垯 pricing_rule type=0锛岀敤浜庤幏鍙� locallyPrice
+        List<PricingRule> localRules = pricingRuleMapper.selectList(new QueryWrapper<PricingRule>().lambda()
+                .eq(PricingRule::getDeleted, Constants.ZERO)
+                .eq(PricingRule::getType, Constants.ZERO)
+                .eq(PricingRule::getCityId, dto.getCityId())
+                .in(PricingRule::getFieldA, fieldAList));
+        Map<String, PricingRule> localRuleMap = new HashMap<>();
+        for (PricingRule r : localRules) {
+            localRuleMap.put(r.getFieldA(), r);
+        }
+
+        // 鎵归噺鏌ヨ鐗╁搧绫诲瀷鍚嶇О
+        List<Category> categories = categoryMapper.selectBatchIds(categoryIds);
+        Map<Integer, String> categoryNameMap = new HashMap<>();
+        Map<Integer, String> categoryDetailMap = new HashMap<>();
+        for (Category c : categories) {
+            categoryNameMap.put(c.getId(), c.getName());
+            categoryDetailMap.put(c.getId(), c.getDetail());
+        }
+
+        // 3. 閫愰」璁$畻杩愯垂锛氳捣姝ヤ环 + 瓒呭嚭閮ㄥ垎闃舵浠�
+        List<ItemPriceVO> itemList = new ArrayList<>();
+        long itemPriceTotal = 0L;
+
+        for (OrderItemDTO item : dto.getItems()) {
+            PricingRule rule = ruleMap.get(String.valueOf(item.getCategoryId()));
+            if (rule == null) {
+                throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(),
+                        "鏈壘鍒拌鍩庡競鐗╁搧绫诲瀷鐨勯厤閫佽浠疯鍒�");
+            }
+
+            // fieldB=璧锋璺濈(km), fieldC=璧锋浠�(鍒�), fieldD=瓒呭嚭璺濈鍗曚綅(km), fieldE=瓒呭嚭璺濈鍗曚环(鍒�)
+            BigDecimal startDistance = new BigDecimal(rule.getFieldB());
+            long startPrice = Long.parseLong(rule.getFieldC());
+            BigDecimal extraDistanceUnit = new BigDecimal(rule.getFieldD());
+            long extraPricePerUnit = Long.parseLong(rule.getFieldE());
+
+            // 闃舵璁′环锛氳窛绂� 鈮� 璧锋璺濈鍙栬捣姝ヤ环锛岃秴鍑烘寜 ceil(瓒呭嚭璺濈/鍗曚綅) 脳 鍗曚环绱姞
+            long unitPrice;
+            if (distanceKm.compareTo(startDistance) <= 0) {
+                unitPrice = startPrice;
+            } else {
+                BigDecimal extraKm = distanceKm.subtract(startDistance);
+                BigDecimal extraCount = extraKm.divide(extraDistanceUnit, 0, RoundingMode.CEILING);
+                unitPrice = startPrice + extraCount.longValue() * extraPricePerUnit;
+            }
+
+            long subtotal = unitPrice * item.getQuantity();
+
+            // 灏卞湴瀛樺彇鍗曚环
+            PricingRule localRule = localRuleMap.get(String.valueOf(item.getCategoryId()));
+            Long locallyPrice = localRule != null ? Long.parseLong(localRule.getFieldB()) : null;
+
+            ItemPriceVO vo = new ItemPriceVO();
+            vo.setCategoryId(item.getCategoryId());
+            vo.setCategoryName(categoryNameMap.getOrDefault(item.getCategoryId(), ""));
+            vo.setDetail(categoryDetailMap.get(item.getCategoryId()));
+            vo.setQuantity(item.getQuantity());
+            vo.setUnitPrice(unitPrice);
+            vo.setLocallyPrice(locallyPrice);
+            vo.setSubtotal(subtotal);
+            vo.setStartDistance(startDistance);
+            vo.setStartPrice(startPrice);
+            vo.setExtraDistance(extraDistanceUnit);
+            vo.setExtraPrice(extraPricePerUnit);
+            itemList.add(vo);
+
+            itemPriceTotal += subtotal;
+        }
+
+        // 4. 淇濅环璐圭敤锛氭姤浠烽噾棰� 脳 淇濅环璐圭巼(瀛楀吀 INSURANCE_RATE)锛屽厓鈫掑垎
+        long insuranceFeeFen = 0L;
+        if (Boolean.TRUE.equals(dto.getInsured()) && dto.getDeclaredAmount() != null) {
+            BigDecimal insuranceFeeYuan = calculateInsuranceFee(dto.getDeclaredAmount());
+            insuranceFeeFen = insuranceFeeYuan.multiply(new BigDecimal(100)).longValue();
+        }
+
+        // 5. 鍔犳�ヨ垂鐢細鐗╁搧浠锋牸 脳 鍔犳�ョ郴鏁�(瀛楀吀 URGENT_COEFFICIENT)
+        long urgentFeeFen = 0L;
+        if (Boolean.TRUE.equals(dto.getUrgent())) {
+            String urgentRateStr = systemDictDataBiz.queryByCode(
+                    Constants.OPERATION_CONFIG, Constants.OP_URGENT_COEFFICIENT).getCode();
+            BigDecimal urgentRate = new BigDecimal(urgentRateStr);
+            urgentFeeFen = new BigDecimal(itemPriceTotal).multiply(urgentRate)
+                    .setScale(0, RoundingMode.HALF_UP).longValue();
+        }
+
+        // 6. 鎬讳环鏍� = 鐗╁搧浠锋牸 + 淇濅环璐圭敤 + 鍔犳�ヨ垂鐢�
+        long totalPrice = itemPriceTotal + insuranceFeeFen + urgentFeeFen;
+
+        PriceCalculateVO result = new PriceCalculateVO();
+        result.setItemList(itemList);
+        result.setItemPrice(itemPriceTotal);
+        result.setInsuranceFee(insuranceFeeFen);
+        result.setUrgentFee(urgentFeeFen);
+        result.setTotalPrice(totalPrice);
+        result.setDistance(distanceKm);
+        return result;
+    }
+
+    @Override
     public long count(Orders orders) {
         QueryWrapper<Orders> wrapper = new QueryWrapper<>(orders);
         return ordersMapper.selectCount(wrapper);
     }
 
+    /**
+     * 鍒涘缓璁㈠崟
+     *
+     * 涓氬姟娴佺▼锛�
+     * 1. 鍙傛暟鏍¢獙锛氬繀濉瓧娈点�佹椂闂撮『搴忋�佺墿鍝佷笉閲嶅銆佸紓鍦板繀濉湇鍔℃椂鏁�
+     * 2. 鏌ヨ瀵勪欢搴楅摵淇℃伅锛堣幏鍙栫粡绾害銆佸湴鍧�锛�
+     * 3. 璋冪敤璁′环鎺ュ彛璁$畻璐圭敤锛坈alculateLocalPrice / calculateRemotePrice锛�
+     * 4. 鐢熸垚璁㈠崟缂栧彿锛欽C + yyyyMMddHHmmss + 4浣嶉殢鏈烘暟
+     * 5. 鍒涘缓璁㈠崟涓昏〃 Orders锛堢姸鎬�=寰呮敮浠橈級
+     * 6. 鍒涘缓璁㈠崟鏄庣粏琛� OrdersDetail锛堟瘡椤圭墿鍝佸昂瀵革級
+     * 7. 鍒涘缓闄勪欢璁板綍 Multifile锛堢墿鍝佸浘鐗� objType=12锛�
+     *
+     * @param dto      鍒涘缓璁㈠崟璇锋眰鍙傛暟
+     * @param memberId 褰撳墠鐧诲綍浼氬憳ID
+     * @return 璁㈠崟ID
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public PayResponse createOrder(CreateOrderDTO dto, Integer memberId) {
+        String lockKey  = Constants.GOODS_ORDER_CREATE_LOCK + memberId;
+        //鍒ゆ柇鍓嶇鏄惁鍦ㄥ悓涓�椤甸潰鍒涘缓浜嗕袱娆¤鍗�
+        if (redisTemplate.hasKey(lockKey)) {
+            throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(),"姝e湪鍒涘缓璁㈠崟锛岃鍕块噸澶嶈皟鐢紒");
+        } else {
+            redisTemplate.opsForValue().set(lockKey, "", 5, TimeUnit.SECONDS);
+        }
+        Date now = new Date();
+        // ========== 1. 鍙傛暟鏍¢獙 ==========
+        // 棰勮鍒板簵瀛樹欢鏃堕棿蹇呴』灏忎簬棰勮鍒板簵鍙栦欢鏃堕棿
+        java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm");
+        Date depositTime;
+        Date takeTime;
+        try {
+            depositTime = sdf.parse(dto.getExpectedDepositTime());
+            takeTime = sdf.parse(dto.getExpectedTakeTime());
+        } catch (java.text.ParseException e) {
+            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "鏃堕棿鏍煎紡閿欒锛屾纭牸寮忥細yyyy-MM-dd HH:mm");
+        }
+        if (!depositTime.before(takeTime)) {
+            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "棰勮鍒板簵瀛樹欢鏃堕棿蹇呴』灏忎簬棰勮鍒板簵鍙栦欢鏃堕棿");
+        }
+
+        // 鐗╁搧灏哄涓嶈兘閲嶅
+        List<Integer> categoryIds = new ArrayList<>();
+        for (OrderItemDTO item : dto.getItems()) {
+            if (categoryIds.contains(item.getCategoryId())) {
+                throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "鐗╁搧灏哄涓嶈兘閲嶅");
+            }
+            categoryIds.add(item.getCategoryId());
+        }
+
+        // 鐗╁搧鍥剧墖鏈�澶�3寮�
+        if (dto.getGoodsImages() != null && dto.getGoodsImages().size() > 3) {
+            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "鐗╁搧鍥剧墖鏈�澶�3寮�");
+        }
+
+        // ========== 2. 鏍¢獙鐗╁搧绫诲瀷 ==========
+        Category goodTypeCategory = categoryMapper.selectById(dto.getGoodType());
+        if (goodTypeCategory == null || Constants.equalsInteger(goodTypeCategory.getDeleted(), Constants.ONE)) {
+            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "鐗╁搧绫诲瀷涓嶅瓨鍦�");
+        }
+        if (!Constants.equalsInteger(goodTypeCategory.getType(), Constants.TWO)) {
+            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "鐗╁搧绫诲瀷鍙傛暟閿欒");
+        }
+
+        // ========== 3. 鏌ヨ瀵勪欢搴楅摵淇℃伅 ==========
+        ShopInfo depositShop = shopInfoMapper.selectById(dto.getDepositShopId());
+        if (depositShop == null) {
+            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "瀵勪欢搴楅摵涓嶅瓨鍦�");
+        }
+
+        // ========== 4. 璁$畻璐圭敤 ==========
+
+        // 寮傚湴瀵勫瓨锛氭牎楠屽彇浠剁偣
+        BigDecimal takeLat = null;
+        BigDecimal takeLgt = null;
+        String takeLocationValue = null;
+        ShopInfo takeShop = null;
+        if (Constants.ONE.equals(dto.getType())) {
+            // 寮傚湴蹇呭~鏈嶅姟鏃舵晥
+            if (dto.getIsUrgent() == null) {
+                throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "寮傚湴瀵勫瓨鏈嶅姟鏃舵晥涓嶈兘涓虹┖");
+            }
+            // 鍙栦欢鐐癸細搴楅摵 or 鑷�夌偣锛岃嚦灏戞彁渚涗竴缁�
+            if (dto.getTakeShopId() != null) {
+                takeShop = shopInfoMapper.selectById(dto.getTakeShopId());
+                if (takeShop == null) {
+                    throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "鍙栦欢搴楅摵涓嶅瓨鍦�");
+                }
+                takeLat = BigDecimal.valueOf(takeShop.getLatitude());
+                takeLgt = BigDecimal.valueOf(takeShop.getLongitude());
+                takeLocationValue = takeShop.getAddress();
+            } else if (dto.getTakeLat() != null && dto.getTakeLgt() != null && StringUtils.isNotBlank(dto.getTakeLocation())) {
+                takeLat = dto.getTakeLat();
+                takeLgt = dto.getTakeLgt();
+                takeLocationValue = dto.getTakeLocation();
+            } else {
+                throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "璇烽�夋嫨鍙栦欢搴楅摵鎴栬緭鍏ヨ嚜閫夊彇浠跺湴鍧�");
+            }
+        } else {
+            // 灏卞湴瀛樺彇锛氬彇浠堕棬搴楀悓瀵勪欢闂ㄥ簵
+            takeShop = depositShop;
+        }
+
+        // ========== 3. 璁$畻璐圭敤 ==========
+        PriceCalculateVO priceResult;
+        if (Constants.ZERO.equals(dto.getType())) {
+            // 灏卞湴瀵勫瓨锛氳绠楀ぉ鏁�
+            long diffMs = takeTime.getTime() - depositTime.getTime();
+            int days = (int) Math.max(1, (diffMs / (1000 * 60 * 60 * 24)) + 1);
+
+            CalculateLocalPriceDTO priceDTO = new CalculateLocalPriceDTO();
+            priceDTO.setCityId(dto.getCityId());
+            priceDTO.setEstimatedDepositDays(days);
+            priceDTO.setItems(dto.getItems());
+            priceDTO.setInsured(dto.getDeclaredAmount() != null && dto.getDeclaredAmount().compareTo(BigDecimal.ZERO) > 0);
+            priceDTO.setDeclaredAmount(dto.getDeclaredAmount());
+            priceResult = calculateLocalPrice(priceDTO);
+        } else {
+            // 寮傚湴瀵勫瓨
+            CalculateRemotePriceDTO priceDTO = new CalculateRemotePriceDTO();
+            priceDTO.setCityId(dto.getCityId());
+            priceDTO.setFromLat(BigDecimal.valueOf(depositShop.getLatitude()));
+            priceDTO.setFromLgt(BigDecimal.valueOf(depositShop.getLongitude()));
+            priceDTO.setToLat(takeLat);
+            priceDTO.setToLgt(takeLgt);
+            priceDTO.setItems(dto.getItems());
+            priceDTO.setInsured(dto.getDeclaredAmount() != null && dto.getDeclaredAmount().compareTo(BigDecimal.ZERO) > 0);
+            priceDTO.setDeclaredAmount(dto.getDeclaredAmount());
+            priceDTO.setUrgent(Constants.ONE.equals(dto.getIsUrgent()));
+            priceResult = calculateRemotePrice(priceDTO);
+        }
+
+        // ========== 5. 鐢熸垚璁㈠崟缂栧彿 ==========
+        String orderCode = "JC" + new java.text.SimpleDateFormat("yyyyMMddHHmmss").format(now)
+                + String.format("%04d", new java.util.Random().nextInt(10000));
+        // 鐢熸垚32浣嶅敮涓�绗笁鏂硅鍗曠紪鍙�
+        String orderTradeNo = generateOrderTradeNo();
+
+        // ========== 6. 鍒涘缓璁㈠崟涓昏〃 ==========
+        Orders orders = new Orders();
+        orders.setCode(orderCode);
+        orders.setOutTradeNo(orderTradeNo);
+        orders.setMemberId(memberId);
+        orders.setType(dto.getType());
+        orders.setCityId(String.valueOf(dto.getCityId()));
+        orders.setStatus(Constants.ZERO); // 寰呮敮浠�
+        orders.setPayStatus(Constants.ZERO); // 鏈敮浠�
+        orders.setCommentStatus(Constants.ZERO); // 鏈瘎浠�
+        orders.setSettlementStatus(Constants.ZERO); // 鏈粨绠�
+        orders.setDeleted(Constants.ZERO);
+        orders.setCreateTime(now);
+        orders.setUpdateTime(now);
+
+        // 瀵勪欢淇℃伅
+        orders.setDepositShopId(dto.getDepositShopId());
+        // 瀛樹欢鍦扮偣锛氱渷甯傚尯鍏ㄨ矾寰� + 鍦板潃鎻忚堪
+        String depositLocationRemark = depositShop.getAddress();
+        if (depositShop.getAreaId() != null) {
+            Areas depositArea = areasBiz.resolveArea(depositShop.getAreaId());
+            if (depositArea != null) {
+                depositLocationRemark = depositArea.getProvinceName() + depositArea.getCityName() + depositArea.getName() + depositShop.getAddress();
+            }
+        }
+        orders.setDepositLocation(depositLocationRemark);
+        orders.setDepositLocationRemark(depositShop.getAddress());
+        orders.setDepositLat(BigDecimal.valueOf(depositShop.getLatitude()));
+        orders.setDepositLgt(BigDecimal.valueOf(depositShop.getLongitude()));
+
+        // 鍙栦欢淇℃伅
+        orders.setTakeUser(dto.getTakeUser());
+        orders.setTakePhone(dto.getTakePhone());
+        orders.setExpectedDepositTime(depositTime);
+        orders.setExpectedTakeTime(takeTime);
+        // 璁$畻棰勮瀛樻斁澶╂暟
+        long dayDiff = (takeTime.getTime() - depositTime.getTime()) / (1000 * 60 * 60 * 24);
+        orders.setEstimatedDepositDays((int) Math.max(1, dayDiff + 1));
+
+        if (Constants.ONE.equals(dto.getType())) {
+            // 寮傚湴锛氬彇浠剁偣淇℃伅
+            orders.setTakeShopId(dto.getTakeShopId());
+            orders.setTakeLocation(takeLocationValue);
+            orders.setTakeLat(takeLat);
+            orders.setTakeLgt(takeLgt);
+            orders.setIsUrgent(dto.getIsUrgent());
+        } else {
+            // 灏卞湴锛氬彇浠剁偣鍚屽瘎浠跺簵閾�
+            orders.setTakeShopId(dto.getDepositShopId());
+            orders.setTakeLocation(depositShop.getAddress());
+            orders.setTakeLat(BigDecimal.valueOf(depositShop.getLatitude()));
+            orders.setTakeLgt(BigDecimal.valueOf(depositShop.getLongitude()));
+            orders.setIsUrgent(Constants.ZERO);
+        }
+
+        // 鐗╁搧淇℃伅
+        orders.setGoodType(dto.getGoodType());
+        // 鎷兼帴鐗╁搧淇℃伅锛氱墿鍝佺被鍨嬪悕绉般�佸昂瀵稿悕绉�*鏁伴噺锛堟暟缁勫瓧绗︿覆锛�
+        List<String> goodsParts = new ArrayList<>();
+        for (ItemPriceVO itemVO : priceResult.getItemList()) {
+            goodsParts.add(itemVO.getCategoryName() + "*" + itemVO.getQuantity());
+        }
+        orders.setGoodsInfo(goodTypeCategory.getName() + "銆�" + String.join("锛�", goodsParts));
+        orders.setRemark(dto.getRemark());
+        orders.setSelfTake(Constants.ZERO);
+
+        // 璐圭敤淇℃伅(鍒�)
+        orders.setBasicAmount(priceResult.getItemPrice());
+        orders.setEstimatedAmount(priceResult.getTotalPrice());
+        orders.setTotalAmount(priceResult.getTotalPrice());
+        orders.setUrgentAmount(priceResult.getUrgentFee());
+        if (dto.getDeclaredAmount() != null && dto.getDeclaredAmount().compareTo(BigDecimal.ZERO) > 0) {
+            orders.setDeclaredAmount(dto.getDeclaredAmount().multiply(new BigDecimal(100)).longValue());
+        } else {
+            orders.setDeclaredAmount(0L);
+        }
+        orders.setDeclaredFee(priceResult.getInsuranceFee());
+        orders.setPrice(priceResult.getItemPrice());
+
+        // 钖叕璁$畻涓庡崰姣斿瓨鍌�
+        calculateAndSetFeeAllocation(orders, depositShop, takeShop);
+
+        ordersMapper.insert(orders);
+        Integer orderId = orders.getId();
+
+        // ========== 7. 鍒涘缓璁㈠崟鏄庣粏 ==========
+        for (ItemPriceVO itemVO : priceResult.getItemList()) {
+            OrdersDetail detail = new OrdersDetail();
+            detail.setOrderId(orderId);
+            detail.setLuggageId(itemVO.getCategoryId());
+            detail.setLuggageName(itemVO.getCategoryName());
+            detail.setLuggageDetail(itemVO.getDetail());
+            detail.setNum(itemVO.getQuantity());
+            detail.setUnitPrice(itemVO.getUnitPrice());
+            detail.setStartDistance(itemVO.getStartDistance());
+            detail.setStartPrice(itemVO.getStartPrice());
+            detail.setExtraDistance(itemVO.getExtraDistance());
+            detail.setExtraPrice(itemVO.getExtraPrice());
+            detail.setLocallyPrice(itemVO.getLocallyPrice());
+            detail.setDeleted(Constants.ZERO);
+            detail.setCreateTime(now);
+            ordersDetailMapper.insert(detail);
+        }
+        // ========== 8. 淇濆瓨鐗╁搧鍥剧墖闄勪欢 ==========
+        if (dto.getGoodsImages() != null && !dto.getGoodsImages().isEmpty()) {
+            int sortNum = 1;
+            for (String imgUrl : dto.getGoodsImages()) {
+                Multifile multifile = new Multifile();
+                multifile.setObjId(orderId);
+                multifile.setObjType(Constants.FileType.ORDER_FILE.getKey());
+                multifile.setType(Constants.ZERO);
+                multifile.setFileurl(imgUrl);
+                multifile.setIsdeleted(Constants.ZERO);
+                multifile.setCreateDate(now);
+                multifile.setSortnum(sortNum++);
+                multifileMapper.insert(multifile);
+            }
+        }
+        // ========== 9. 鍞よ捣寰俊鏀粯 ==========
+        Member member = memberMapper.selectById(memberId);
+        if (member == null || StringUtils.isBlank(member.getOpenid())) {
+            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "鐢ㄦ埛淇℃伅寮傚父锛屾棤娉曞彂璧锋敮浠�");
+        }
+        PayResponse payResponse = wxPay(orders, member.getOpenid(), Constants.OrdersAttach.STORAGE_ORDER);
+        payResponse.setLockKey(lockKey);
+        return payResponse;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public PayResponse continuePay(Integer orderId, Integer memberId) {
+        // 1. 鏌ヨ璁㈠崟
+        Orders orders = ordersMapper.selectById(orderId);
+        if (Objects.isNull(orders) || Constants.equalsInteger(orders.getDeleted(), Constants.ONE)) {
+            throw new BusinessException(ResponseStatus.DATA_EMPTY);
+        }
+        // 2. 鏍¢獙璁㈠崟褰掑睘
+        if (!Constants.equalsInteger(orders.getMemberId(), memberId)) {
+            throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "鏃犳潈鎿嶄綔璇ヨ鍗�");
+        }
+        // 3. 鏍¢獙璁㈠崟鐘舵�侊細浠呭緟鏀粯鍙户缁敮浠�
+        if (!Constants.equalsInteger(orders.getStatus(), Constants.ZERO)) {
+            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "褰撳墠璁㈠崟鐘舵�佷笉鏀寔缁х画鏀粯");
+        }
+        // 4. 鏍¢獙鏀粯閲戦
+        if (orders.getTotalAmount() == null || orders.getTotalAmount() <= 0) {
+            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "璁㈠崟閲戦寮傚父锛屾棤娉曞彂璧锋敮浠�");
+        }
+        // 5. 閲嶆柊鐢熸垚绗笁鏂硅鍗曠紪鍙凤紙閬垮厤閲嶅锛�
+        String orderTradeNo = generateOrderTradeNo();
+        orders.setOutTradeNo(orderTradeNo);
+        orders.setUpdateTime(new Date());
+        ordersMapper.updateById(orders);
+        // 6. 鍞よ捣寰俊鏀粯
+        Member member = memberMapper.selectById(memberId);
+        if (member == null || StringUtils.isBlank(member.getOpenid())) {
+            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "鐢ㄦ埛淇℃伅寮傚父锛屾棤娉曞彂璧锋敮浠�");
+        }
+        return wxPay(orders, member.getOpenid(), Constants.OrdersAttach.STORAGE_ORDER);
+    }
+
+    /**
+     * 鍞よ捣寰俊灏忕▼搴忔敮浠�
+     *
+     * @param orders       璁㈠崟瀹炰綋锛堥渶瑕� code銆乼otalAmount锛�
+     * @param openid       鐢ㄦ埛寰俊openid
+     * @param ordersAttach 璁㈠崟鏀粯绫诲瀷
+     * @return PayResponse 鍖呭惈寰俊璋冭捣鍙傛暟鍜岃鍗曚富閿�
+     */
+    private PayResponse wxPay(Orders orders, String openid, Constants.OrdersAttach ordersAttach) {
+        try {
+            WxPayUnifiedOrderRequest request = new WxPayUnifiedOrderRequest();
+            request.setBody(ordersAttach.getName());
+            request.setAttach(ordersAttach.getKey());
+            request.setOutTradeNo(orders.getOutTradeNo());
+            // totalAmount 鍗曚綅涓哄垎锛學eChat Pay setTotalFee 涔熸槸鍒嗭紝鐩存帴杞琲nt
+            long totalFee = orders.getTotalAmount() != null ? orders.getTotalAmount() : 0L;
+            request.setTotalFee((int) totalFee);
+            request.setTimeStart(DateUtil.DateToString(new Date(), "yyyyMMddHHmmss"));
+            request.setSpbillCreateIp(Constants.getIpAddr());
+            request.setOpenid(openid);
+
+            Object response = WxMiniConfig.wxPayService.createOrder(request);
+
+            PayResponse payResponse = new PayResponse();
+            payResponse.setResponse(response);
+            payResponse.setOrderId(orders.getId());
+            return payResponse;
+        } catch (WxPayException e) {
+            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "鏀粯璋冭捣澶辫触锛�" + e.getMessage());
+        }
+    }
+
+
+
+
+
+    @Override
+    public OrderDetailVO findDetail(Integer id) {
+        Orders order = ordersMapper.selectById(id);
+        if (Objects.isNull(order)) {
+            throw new BusinessException(ResponseStatus.DATA_EMPTY);
+        }
+
+        OrderDetailVO vo = new OrderDetailVO();
+        vo.setOrder(order);
+
+        // 鍥剧墖璺緞鍓嶇紑
+        String imgPrefix = getOrdersPrefix();
+
+        // 涓嬪崟鍥剧墖 (type=12)
+        vo.setOrderFiles(getFileUrls(id, Constants.FileType.ORDER_FILE.getKey(), imgPrefix));
+
+        // 浼氬憳淇℃伅
+        if (order.getMemberId() != null) {
+            Member member = memberMapper.selectById(order.getMemberId());
+            if (member != null) {
+                vo.setMemberName(member.getName());
+                vo.setMemberPhone(member.getTelephone());
+            }
+        }
+
+        // 瀵勫瓨闂ㄥ簵淇℃伅
+        if (order.getDepositShopId() != null) {
+            ShopInfo depositShop = shopInfoMapper.selectById(order.getDepositShopId());
+            if (depositShop != null) {
+                vo.setDepositShopName(depositShop.getName());
+                vo.setDepositShopPhone(depositShop.getLinkPhone());
+            }
+        }
+
+        // 鍙栦欢闂ㄥ簵淇℃伅
+        if (order.getTakeShopId() != null) {
+            ShopInfo takeShop = shopInfoMapper.selectById(order.getTakeShopId());
+            if (takeShop != null) {
+                vo.setTakeShopName(takeShop.getName());
+                vo.setTakeShopAddress(takeShop.getAddress());
+                vo.setTakeShopPhone(takeShop.getLinkPhone());
+            }
+        }
+
+        // 鎺ュ崟鍙告満淇℃伅
+        if (order.getAcceptDriver() != null) {
+            DriverInfo driverInfo = driverInfoMapper.selectById(order.getAcceptDriver());
+            if (driverInfo != null) {
+                vo.setDriverName(driverInfo.getName());
+            }
+        }
+
+        // 閰嶉�侀檮浠跺浘鐗�
+        vo.setDepositImages(getFileUrls(id, Constants.FileType.ORDER_DEPOSIT.getKey(), imgPrefix));
+        vo.setStoreInImages(getFileUrls(id, Constants.FileType.ORDER_TAKE.getKey(), imgPrefix));
+        vo.setDriverTakeImages(getFileUrls(id, Constants.FileType.DRIVER_TAKE.getKey(), imgPrefix));
+        vo.setDriverDoneImages(getFileUrls(id, Constants.FileType.DRIVER_DONE.getKey(), imgPrefix));
+        vo.setStoreOutImages(getFileUrls(id, Constants.FileType.STORE_OUT.getKey(), imgPrefix));
+        // 鐗╁搧鏄庣粏
+        vo.setDetailList(buildDetailList(id));
+        Category category = categoryMapper.selectById(order.getGoodType());
+        if(CollectionUtils.isNotEmpty(vo.getDetailList())&&Objects.nonNull(category)){
+            for (OrderItemVO v:vo.getDetailList()) {
+                v.setTypeName(category.getName());
+            }
+        }
+
+        // 鍙栨秷/閫�娆剧姸鎬佹椂鏌ヨ閫�娆捐褰�
+        Integer status = order.getStatus();
+        if (status != null && (status == Constants.OrderStatus.overdue.getStatus()
+                || status == Constants.OrderStatus.closed.getStatus()
+                || status == Constants.OrderStatus.cancelOverdue.getStatus()
+                || status == Constants.OrderStatus.cancelling.getStatus()
+                || status == Constants.OrderStatus.cancelled.getStatus())) {
+            OrdersRefund ordersRefund = ordersRefundMapper.selectOne(
+                    new QueryWrapper<OrdersRefund>().lambda()
+                            .eq(OrdersRefund::getOrderId, id)
+                            .eq(OrdersRefund::getDeleted, Constants.ZERO)
+                            .orderByDesc(OrdersRefund::getCreateTime)
+                            .last("limit 1"));
+            if (ordersRefund != null) {
+                vo.setOrdersRefund(ordersRefund);
+                // 閫�娆炬柟寮忥細1=骞冲彴鐩存帴鍙栨秷 鈫� 杩斿洖骞冲彴鎿嶄綔浜哄悕绉�
+                if (Constants.equalsInteger(ordersRefund.getType(), Constants.ONE) && ordersRefund.getUserId() != null) {
+                    // userId 鍏宠仈 system_user 琛紝鏌ヨ鎿嶄綔浜哄悕绉�
+                    vo.setPlatformUserName(getPlatformUserName(ordersRefund.getUserId()));
+                }
+                // 閫�娆炬柟寮忥細2=宸插瓨浠剁敵璇峰彇娑� 鈫� 杩斿洖閫�娆惧彇浠跺浘鐗� (multifile objType=14)
+                if (Constants.equalsInteger(ordersRefund.getType(), Constants.TWO)) {
+                    vo.setRefundTakeImages(getFileUrls(id, Constants.FileType.REFUND_TAKE.getKey(), imgPrefix));
+                }
+            }
+        }
+
+        return vo;
+    }
+
+    @Override
+    public OrderDispatchVO findDispatchInfo(Integer id) {
+        Orders order = ordersMapper.selectById(id);
+        if (Objects.isNull(order)) {
+            throw new BusinessException(ResponseStatus.DATA_EMPTY);
+        }
+
+        OrderDispatchVO vo = new OrderDispatchVO();
+        vo.setCode(order.getCode());
+        vo.setPayAmountYuan(order.getPayAmount() != null ? Constants.getFormatMoney(order.getPayAmount()) : 0);
+        vo.setType(order.getType());
+        vo.setTypeDesc(order.getType() != null && order.getType() == Constants.ONE ? "寮傚湴瀛樺彇" : "灏卞湴瀛樺彇");
+        vo.setDetailList(buildDetailList(id));
+
+        return vo;
+    }
+
+    @Override
+    @Transactional(rollbackFor = {Exception.class, BusinessException.class})
+    public void dispatch(DispatchDTO dto) {
+        // 鍙傛暟鏍¢獙
+        if (dto == null || dto.getOrderId() == null || dto.getUrgentFee() == null) {
+            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "璁㈠崟涓婚敭鍜屽姞鎬ヨ垂鐢ㄤ笉鑳戒负绌�");
+        }
+
+        Orders order = ordersMapper.selectById(dto.getOrderId());
+        if (Objects.isNull(order)) {
+            throw new BusinessException(ResponseStatus.DATA_EMPTY);
+        }
+
+        // 鍓嶇疆鏉′欢鏍¢獙锛氬紓鍦板瓨鍙� + 宸插瘎瀛�
+        if (!Constants.ONE.equals(order.getType())) {
+            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "浠呮敮鎸佸紓鍦板瓨鍙栬鍗曟淳鍗�");
+        }
+        if (!Integer.valueOf(Constants.OrderStatus.deposited.getStatus()).equals(order.getStatus())) {
+            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "浠呭凡瀵勫瓨鐘舵�佽鍗曞彲娲惧崟");
+        }
+
+        String optUserName = getCurrentUserName();
+
+        // 鍔犳�ヨ垂鏃ュ織锛堟瘡娆″崟鐙褰曟湰娆″姞鎬ヨ垂锛�
+        Constants.OrderLogType urgentLogType = Constants.OrderLogType.urgent;
+        OrderLog feeLog = new OrderLog();
+        feeLog.setOrderId(order.getId());
+        feeLog.setTitle(urgentLogType.getTitle());
+        feeLog.setLogInfo(urgentLogType.getStatusInfo().replace("{param}", dto.getUrgentFee().toPlainString()));
+        feeLog.setObjType(urgentLogType.getStatus());
+        feeLog.setOrderStatus(order.getStatus());
+        feeLog.setOptUserType(3);
+        feeLog.setOptUserName(optUserName);
+        feeLog.setCreateTime(new Date());
+        feeLog.setDeleted(Constants.ZERO);
+        orderLogService.create(feeLog);
+
+        // 鍔犳�ヨ垂鐢� 鍏冣啋鍒�
+        long urgentFeeFen = dto.getUrgentFee().multiply(new BigDecimal(100)).longValue();
+
+        // 浣跨敤 UpdateWrapper 绮剧‘鏇存柊锛屼笉褰卞搷鍏朵粬瀛楁
+        com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper<Orders> updateWrapper =
+                new UpdateWrapper<Orders>().lambda()
+                .eq(Orders::getId, order.getId())
+                .set(Orders::getIsUrgent, Constants.ONE)
+                .set(Orders::getPlatformRewardAmount, urgentFeeFen)
+                .set(Orders::getUpdateTime, new Date());
+
+        // 寮傚湴瀵勫瓨涓旀湁鍙栦欢闂ㄥ簵鏃讹紝鐢熸垚鍙告満鏍搁攢鐮�
+        if (order.getTakeShopId() != null) {
+            String driverVerifyCode = generateVerifyCode();
+            updateWrapper.set(Orders::getDriverVerifyCode, driverVerifyCode);
+        }
+
+        // 澶囨敞
+        if (StringUtils.isNotBlank(dto.getRemark())) {
+            updateWrapper.set(Orders::getRemark, dto.getRemark());
+        }
+
+        // 鎸囨淳鍙告満锛堥潪蹇呭~锛�
+        if (dto.getDriverId() != null) {
+            updateWrapper.set(Orders::getAssignDriverId, dto.getDriverId());
+
+            Member driver = memberMapper.selectById(dto.getDriverId());
+            String driverName = driver != null ? driver.getName() : String.valueOf(dto.getDriverId());
+
+            Constants.OrderLogType dispatchLogType = Constants.OrderLogType.dispatch;
+            OrderLog driverLog = new OrderLog();
+            driverLog.setOrderId(order.getId());
+            driverLog.setTitle(dispatchLogType.getTitle());
+            driverLog.setLogInfo(dispatchLogType.getStatusInfo().replace("{param}", driverName));
+            driverLog.setObjType(dispatchLogType.getStatus());
+            driverLog.setOrderStatus(order.getStatus());
+            driverLog.setOptUserType(3);
+            driverLog.setOptUserName(optUserName);
+            driverLog.setCreateTime(new Date());
+            driverLog.setDeleted(Constants.ZERO);
+            orderLogService.create(driverLog);
+        }
+
+        ordersMapper.update(updateWrapper);
+    }
+
+    private String getCurrentUserName() {
+        try {
+            com.doumee.core.model.LoginUserInfo user =
+                    (com.doumee.core.model.LoginUserInfo) org.apache.shiro.SecurityUtils.getSubject().getPrincipal();
+            return user != null ? user.getUsername() : "绯荤粺";
+        } catch (Exception e) {
+            return "绯荤粺";
+        }
+    }
+
+    /**
+     * 鏋勫缓璁㈠崟鐗╁搧鏄庣粏鍒楄〃
+     */
+    private List<OrderItemVO> buildDetailList(Integer orderId) {
+        List<OrdersDetail> details = ordersDetailMapper.selectList(
+                new QueryWrapper<OrdersDetail>().lambda()
+                        .eq(OrdersDetail::getOrderId, orderId)
+                        .eq(OrdersDetail::getDeleted, Constants.ZERO));
+        return buildDetailList(details);
+    }
+
+    /**
+     * 鏍规嵁宸叉煡璇㈢殑鏄庣粏鏋勫缓鐗╁搧鍒楄〃锛堥伩鍏嶉噸澶嶆煡璇級
+     */
+    private List<OrderItemVO> buildDetailList(List<OrdersDetail> details) {
+        List<OrderItemVO> items = new ArrayList<>();
+        if (details != null) {
+            for (OrdersDetail d : details) {
+                OrderItemVO item = new OrderItemVO();
+                item.setLuggageName(d.getLuggageName());
+                item.setLuggageDetail(d.getLuggageDetail());
+                item.setNum(d.getNum());
+                double unitPriceYuan = d.getUnitPrice() != null ? Constants.getFormatMoney(d.getUnitPrice()) : 0;
+                item.setUnitPriceYuan(unitPriceYuan);
+                item.setSubtotal(unitPriceYuan * (d.getNum() != null ? d.getNum() : 0));
+                items.add(item);
+            }
+        }
+        return items;
+    }
+
+    /**
+     * 鐢熸垚32浣嶅敮涓�绗笁鏂硅鍗曠紪鍙凤紙鏃堕棿鎴�17浣� + 闅忔満鏁�15浣嶏級
+     */
+    private String generateOrderTradeNo() {
+        return new java.text.SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date())
+                + String.format("%015d", Math.abs(new java.util.Random().nextLong() % 1000000000000000L));
+    }
+
+    /**
+     * 鐢熸垚6浣嶆暟瀛楁牳閿�鐮侊紙Redis楠岄噸锛�
+     * 浣跨敤 SETNX 鎶㈠崰锛屼繚璇佸敮涓�锛涗娇鐢ㄥ畬姣曞悗璋冪敤 releaseVerifyCode 浠� Redis 绉婚櫎
+     */
+    private String generateVerifyCode() {
+        Random random = new Random();
+        String redisKey = Constants.REDIS_VERIFY_CODE_KEY;
+        for (int i = 0; i < 200; i++) {
+            String code = String.format("%06d", random.nextInt(1000000));
+            Boolean success = redisTemplate.opsForValue().setIfAbsent(redisKey + code, "1", 24, TimeUnit.HOURS);
+            if (success != null && success) {
+                return code;
+            }
+        }
+        throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "鏍搁攢鐮佺敓鎴愬け璐ワ紝璇烽噸璇�");
+    }
+
+    /**
+     * 閲婃斁鏍搁攢鐮佸崰浣嶏紙鏍搁攢瀹屾垚鍚庤皟鐢紝绉婚櫎 Redis 涓殑 key锛�
+     */
+    public void releaseVerifyCode(String code) {
+        if (StringUtils.isNotBlank(code)) {
+            redisTemplate.delete(Constants.REDIS_VERIFY_CODE_KEY + code);
+        }
+    }
+
+    private String getOrdersPrefix() {
+        try {
+            return systemDictDataBiz.queryByCode(Constants.OSS, Constants.RESOURCE_PATH).getCode()
+                    + systemDictDataBiz.queryByCode(Constants.OSS, Constants.ORDERS_FILES).getCode();
+        } catch (Exception e) {
+            return "";
+        }
+    }
+
+    private String getPlatformUserName(Integer userId) {
+        SystemUser user = systemUserMapper.selectById(userId);
+        return user != null ? user.getRealname() : null;
+    }
+
+    private List<String> getFileUrls(Integer orderId, int objType, String prefix) {
+        List<Multifile> files = multifileMapper.selectList(
+                new QueryWrapper<Multifile>().lambda()
+                        .eq(Multifile::getObjId, orderId)
+                        .eq(Multifile::getObjType, objType)
+                        .eq(Multifile::getIsdeleted, Constants.ZERO)
+                        .orderByAsc(Multifile::getSortnum));
+        List<String> urls = new ArrayList<>();
+        if (files != null) {
+            for (Multifile f : files) {
+                if (StringUtils.isNotBlank(f.getFileurl())) {
+                    urls.add(prefix + f.getFileurl());
+                }
+            }
+        }
+        return urls;
+    }
+
+    /**
+     * 璁$畻骞惰缃鍗曡柂閰垎閰嶏紙鍙告満銆佸瓨浠堕棬搴椼�佸彇浠堕棬搴楋級
+     * 浠� pricing_rule (type=4) 璇诲彇鍒嗘垚姣斾緥锛屾牴鎹棬搴椾紒涓�/涓汉绫诲瀷鍖哄垎
+     *
+     * @param orders        璁㈠崟瀹炰綋锛堥渶瑕� totalAmount銆乧ityId 宸茶缃級
+     * @param depositShop   瀵勪欢闂ㄥ簵锛堥渶瑕� companyType锛�
+     * @param takeShop      鍙栦欢闂ㄥ簵锛堥渶瑕� companyType锛屽氨鍦板瓨鍙栨椂涓� depositShop 鐩稿悓锛�
+     */
+    private void calculateAndSetFeeAllocation(Orders orders, ShopInfo depositShop, ShopInfo takeShop) {
+        Long totalAmount = orders.getTotalAmount() != null ? orders.getTotalAmount() : 0L;
+        if (totalAmount <= 0) {
+            orders.setDriverFee(0L);
+            orders.setDepositShopFee(0L);
+            orders.setTakeShopFee(0L);
+            orders.setDriverFeeRata(BigDecimal.ZERO);
+            orders.setDepositShopFeeRata(BigDecimal.ZERO);
+            orders.setTakeShopFeeRata(BigDecimal.ZERO);
+            return;
+        }
+        Integer cityId = Integer.valueOf(orders.getCityId());
+
+        // 鍙告満鍗犳瘮锛歠ieldA=4锛堥厤閫佸憳锛�
+        BigDecimal driverRata = getRevenueShareRata(cityId, Constants.FOUR);
+        // 瀵勪欢闂ㄥ簵鍗犳瘮锛歠ieldA=0(浼佷笟瀵�)/1(涓汉瀵�)
+        int depositFieldA = Constants.equalsInteger(depositShop.getCompanyType(), Constants.ONE) ? Constants.ZERO : Constants.ONE;
+        BigDecimal depositShopRata = getRevenueShareRata(cityId, depositFieldA);
+        // 鍙栦欢闂ㄥ簵鍗犳瘮锛歠ieldA=2(浼佷笟鍙�)/3(涓汉鍙�)
+        int takeFieldA = Constants.equalsInteger(takeShop.getCompanyType(), Constants.ONE) ? Constants.TWO : Constants.THREE;
+        BigDecimal takeShopRata = getRevenueShareRata(cityId, takeFieldA);
+
+        // 璁$畻钖叕锛堝垎锛夛細totalAmount 涓哄垎锛宺ata 涓烘瘮渚嬪�硷紙濡� 0.15 琛ㄧず 15%锛�
+        long driverFee = new BigDecimal(totalAmount).multiply(driverRata).longValue();
+        long depositShopFee = new BigDecimal(totalAmount).multiply(depositShopRata).longValue();
+        long takeShopFee = totalAmount - driverFee - depositShopFee;
+
+        orders.setDriverFee(driverFee);
+        orders.setDepositShopFee(depositShopFee);
+        orders.setTakeShopFee(takeShopFee);
+        orders.setDriverFeeRata(driverRata);
+        orders.setDepositShopFeeRata(depositShopRata);
+        orders.setTakeShopFeeRata(takeShopRata);
+    }
+
+    /**
+     * 浠� pricing_rule 琛ㄨ幏鍙栧垎鎴愭瘮渚嬶紙type=4锛�
+     *
+     * @param cityId   鍩庡競涓婚敭
+     * @param fieldA   绫诲瀷锛�0=浼佷笟瀵�, 1=涓汉瀵�, 2=浼佷笟鍙�, 3=涓汉鍙�, 4=閰嶉�佸憳
+     * @return 鍒嗘垚姣斾緥锛堝 0.15 琛ㄧず 15%锛�
+     */
+    private BigDecimal getRevenueShareRata(Integer cityId, int fieldA) {
+        PricingRule rule = pricingRuleMapper.selectOne(new QueryWrapper<PricingRule>().lambda()
+                .eq(PricingRule::getDeleted, Constants.ZERO)
+                .eq(PricingRule::getType, Constants.FOUR)
+                .eq(PricingRule::getCityId, cityId)
+                .eq(PricingRule::getFieldA, String.valueOf(fieldA))
+                .last("limit 1"));
+        if (rule != null && StringUtils.isNotBlank(rule.getFieldC())) {
+            return new BigDecimal(rule.getFieldC());
+        }
+        return BigDecimal.ZERO;
+    }
+
+    @Override
+    public PageData<MyOrderVO> findMyOrderPage(PageWrap<MyOrderDTO> pageWrap, Integer memberId) {
+        MyOrderDTO model = pageWrap.getModel();
+        Integer status = model != null ? model.getStatus() : null;
+        Integer combinedStatus = model != null ? model.getCombinedStatus() : null;
+
+        // 瑙f瀽鍚堝苟鐘舵�佷负鍏蜂綋鐘舵�佸垪琛�
+        List<Integer> statusList = null;
+        if (combinedStatus != null) {
+            Constants.OrderCombinedStatus combined = Constants.OrderCombinedStatus.getByKey(combinedStatus);
+            if (combined != null) {
+                statusList = new ArrayList<>();
+                for (int s : combined.getStatuses()) {
+                    statusList.add(s);
+                }
+            }
+        }
+
+        IPage<Orders> p = new Page<>(pageWrap.getPage(), pageWrap.getCapacity());
+        MPJLambdaWrapper<Orders> wrapper = new MPJLambdaWrapper<Orders>()
+                .selectAll(Orders.class)
+                .select("s1.name", Orders::getDepositShopName)
+                .select("s1.link_name", Orders::getDepositShopLinkName)
+                .select("s1.link_phone", Orders::getDepositShopLinkPhone)
+                .select("s2.name", Orders::getTakeShopName)
+                .select("s2.address", Orders::getTakeShopAddress)
+                .leftJoin("shop_info s1 on s1.id = t.DEPOSIT_SHOP_ID")
+                .leftJoin("shop_info s2 on s2.id = t.TAKE_SHOP_ID")
+                .eq(Orders::getDeleted, Constants.ZERO)
+                .eq(Orders::getMemberId, memberId)
+                .eq(status != null, Orders::getStatus, status)
+                .in(statusList != null, Orders::getStatus, statusList)
+                .orderByDesc(Orders::getCreateTime);
+
+        IPage<Orders> orderPage = ordersMapper.selectJoinPage(p, Orders.class, wrapper);
+        List<MyOrderVO> voList = new ArrayList<>();
+        if (orderPage != null && orderPage.getRecords() != null) {
+
+            for (Orders o : orderPage.getRecords()) {
+                MyOrderVO vo = new MyOrderVO();
+                vo.setId(o.getId());
+                vo.setCode(o.getCode());
+                vo.setType(o.getType());
+                vo.setStatus(o.getStatus());
+                vo.setCreateTime(o.getCreateTime());
+                vo.setExpectedTakeTime(o.getExpectedTakeTime());
+
+                // 瀛樹欢闂ㄥ簵锛堝叧鑱旀煡璇㈢洿鎺ュ彇鍊硷級
+                vo.setDepositShopName(o.getDepositShopName());
+                vo.setDepositShopLinkName(o.getDepositShopLinkName());
+                vo.setDepositShopPhone(o.getDepositShopLinkPhone());
+
+                // 鍙栦欢淇℃伅锛氭湁鍙栦欢闂ㄥ簵鍙栭棬搴楋紝鏃犲垯鍙栫敤鎴疯嚜閫夊彇浠剁偣
+                if (o.getTakeShopId() != null) {
+                    vo.setTakeShopName(o.getTakeShopName());
+                    vo.setTakeShopAddress(o.getTakeShopAddress());
+                } else {
+                    vo.setTakeLocation(o.getTakeLocation());
+                    vo.setTakeLocationRemark(o.getTakeLocationRemark());
+                }
+
+                // 鍙栦欢鑱旂郴浜�
+                vo.setTakeUser(o.getTakeUser());
+                vo.setTakePhone(o.getTakePhone());
+
+                // 璐圭敤锛堝垎锛�
+                vo.setDeclaredFee(o.getDeclaredFee());
+                vo.setEstimatedAmount(o.getEstimatedAmount());
+
+                // 鏌ヨ鐗╁搧鏄庣粏锛堜竴娆℃煡璇紝鍚屾椂鐢ㄤ簬鐗╁搧鍒楄〃鍜岄�炬湡璁$畻锛�
+                List<OrdersDetail> details = ordersDetailMapper.selectList(
+                        new QueryWrapper<OrdersDetail>().lambda()
+                                .eq(OrdersDetail::getOrderId, o.getId())
+                                .eq(OrdersDetail::getDeleted, Constants.ZERO));
+
+                // 鐗╁搧鏄庣粏
+                vo.setDetailList(buildDetailList(details));
+
+                // 閫炬湡淇℃伅锛堜粎寰呭彇浠剁姸鎬佽绠楋級
+                if (Integer.valueOf(Constants.OrderStatus.arrived.getStatus()).equals(o.getStatus())) {
+                    OverdueFeeVO overdueInfo = calculateOverdueFeeInternal(o, details);
+                    vo.setOverdue(overdueInfo.getOverdue());
+                    vo.setOverdueDays(overdueInfo.getOverdueDays());
+                    vo.setOverdueFee(overdueInfo.getOverdueFee());
+                }
+                voList.add(vo);
+            }
+        }
+
+        IPage<MyOrderVO> vPage = new Page<>(pageWrap.getPage(), pageWrap.getCapacity());
+        PageData<MyOrderVO> pageData = PageData.from(vPage);
+        pageData.setRecords(voList);
+        pageData.setTotal(orderPage.getTotal());
+        pageData.setPage(orderPage.getCurrent());
+        pageData.setCapacity(orderPage.getSize());
+        return pageData;
+    }
+
+    @Override
+    public PageData<MyOrderVO> findShopOrderPage(PageWrap<MyOrderDTO> pageWrap, Integer shopId) {
+        MyOrderDTO model = pageWrap.getModel();
+        Integer status = model != null ? model.getStatus() : null;
+        Integer combinedStatus = model != null ? model.getCombinedStatus() : null;
+
+        // 瑙f瀽鍚堝苟鐘舵�佷负鍏蜂綋鐘舵�佸垪琛�
+        List<Integer> statusList = null;
+        if (combinedStatus != null) {
+            Constants.OrderCombinedStatus combined = Constants.OrderCombinedStatus.getByKey(combinedStatus);
+            if (combined != null) {
+                statusList = new ArrayList<>();
+                for (int s : combined.getStatuses()) {
+                    statusList.add(s);
+                }
+            }
+        }
+
+        IPage<Orders> p = new Page<>(pageWrap.getPage(), pageWrap.getCapacity());
+        MPJLambdaWrapper<Orders> wrapper = new MPJLambdaWrapper<Orders>()
+                .selectAll(Orders.class)
+                .select("s1.name", Orders::getDepositShopName)
+                .select("s1.link_name", Orders::getDepositShopLinkName)
+                .select("s1.link_phone", Orders::getDepositShopLinkPhone)
+                .select("s2.name", Orders::getTakeShopName)
+                .select("s2.address", Orders::getTakeShopAddress)
+                .leftJoin("shop_info s1 on s1.id = t.DEPOSIT_SHOP_ID")
+                .leftJoin("shop_info s2 on s2.id = t.TAKE_SHOP_ID")
+                .eq(Orders::getPayStatus, Constants.ONE)
+                .and(w -> w.eq(Orders::getDepositShopId, shopId).or().eq(Orders::getTakeShopId, shopId))
+                .eq(status != null, Orders::getStatus, status)
+                .in(statusList != null, Orders::getStatus, statusList)
+                .orderByDesc(Orders::getCreateTime);
+
+        IPage<Orders> orderPage = ordersMapper.selectJoinPage(p, Orders.class, wrapper);
+        List<MyOrderVO> voList = new ArrayList<>();
+        if (orderPage != null && orderPage.getRecords() != null) {
+            for (Orders o : orderPage.getRecords()) {
+                MyOrderVO vo = new MyOrderVO();
+                vo.setId(o.getId());
+                vo.setCode(o.getCode());
+                vo.setType(o.getType());
+                vo.setStatus(o.getStatus());
+                vo.setCreateTime(o.getCreateTime());
+                vo.setExpectedTakeTime(o.getExpectedTakeTime());
+
+                vo.setDepositShopName(o.getDepositShopName());
+                vo.setDepositShopLinkName(o.getDepositShopLinkName());
+                vo.setDepositShopPhone(o.getDepositShopLinkPhone());
+
+                // 闂ㄥ簵瑙掕壊锛氬瓨浠堕棬搴�=1锛屽彇浠堕棬搴�=2
+                if (Constants.equalsInteger(o.getDepositShopId(), shopId)) {
+                    vo.setShopRole(Constants.ONE);
+                } else if (Constants.equalsInteger(o.getTakeShopId(), shopId)) {
+                    vo.setShopRole(Constants.TWO);
+                }
+
+                if (o.getTakeShopId() != null) {
+                    vo.setTakeShopName(o.getTakeShopName());
+                    vo.setTakeShopAddress(o.getTakeShopAddress());
+                } else {
+                    vo.setTakeLocation(o.getTakeLocation());
+                    vo.setTakeLocationRemark(o.getTakeLocationRemark());
+                }
+
+                vo.setTakeUser(o.getTakeUser());
+                vo.setTakePhone(o.getTakePhone());
+                vo.setDeclaredFee(o.getDeclaredFee());
+                vo.setEstimatedAmount(o.getEstimatedAmount());
+
+                List<OrdersDetail> details = ordersDetailMapper.selectList(
+                        new QueryWrapper<OrdersDetail>().lambda()
+                                .eq(OrdersDetail::getOrderId, o.getId())
+                                .eq(OrdersDetail::getDeleted, Constants.ZERO));
+
+                vo.setDetailList(buildDetailList(details));
+
+                if (Integer.valueOf(Constants.OrderStatus.arrived.getStatus()).equals(o.getStatus())) {
+                    OverdueFeeVO overdueInfo = calculateOverdueFeeInternal(o, details);
+                    vo.setOverdue(overdueInfo.getOverdue());
+                    vo.setOverdueDays(overdueInfo.getOverdueDays());
+                    vo.setOverdueFee(overdueInfo.getOverdueFee());
+                }
+                voList.add(vo);
+            }
+        }
+
+        IPage<MyOrderVO> vPage = new Page<>(pageWrap.getPage(), pageWrap.getCapacity());
+        PageData<MyOrderVO> pageData = PageData.from(vPage);
+        pageData.setRecords(voList);
+        pageData.setTotal(orderPage.getTotal());
+        pageData.setPage(orderPage.getCurrent());
+        pageData.setCapacity(orderPage.getSize());
+        return pageData;
+    }
+
+    @Override
+    public MyOrderDetailVO findMyOrderDetail(Integer id, Integer memberId) {
+        Orders order = ordersMapper.selectOne(new QueryWrapper<Orders>().lambda()
+                .eq(Orders::getId, id)
+                .eq(Orders::getMemberId, memberId)
+                .eq(Orders::getDeleted, Constants.ZERO));
+        if (order == null) {
+            throw new BusinessException(ResponseStatus.DATA_EMPTY);
+        }
+        return buildOrderDetailVO(order, true);
+    }
+
+    @Override
+    public MyOrderDetailVO findShopOrderDetail(Integer orderId, String verifyCode) {
+        Orders order = null;
+        if (orderId != null) {
+            order = ordersMapper.selectOne(new QueryWrapper<Orders>().lambda()
+                    .eq(Orders::getId, orderId)
+                    .eq(Orders::getDeleted, Constants.ZERO));
+        } else if (StringUtils.isNotBlank(verifyCode)) {
+            order = ordersMapper.selectOne(new QueryWrapper<Orders>().lambda()
+                    .eq(Orders::getMemberVerifyCode, verifyCode)
+                    .eq(Orders::getDeleted, Constants.ZERO)
+                    .last("limit 1"));
+            if (order == null) {
+                order = ordersMapper.selectOne(new QueryWrapper<Orders>().lambda()
+                        .eq(Orders::getDriverVerifyCode, verifyCode)
+                        .eq(Orders::getDeleted, Constants.ZERO)
+                        .last("limit 1"));
+            }
+        }
+        if (order == null) {
+            throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "璁㈠崟涓嶅瓨鍦�");
+        }
+        return buildOrderDetailVO(order, false);
+    }
+
+    /**
+     * 鏋勫缓璁㈠崟璇︽儏VO锛堜細鍛樼/闂ㄥ簵绔鐢級
+     *
+     * @param order          璁㈠崟瀹炰綋
+     * @param memberViewMode true=浼氬憳绔紙鎸夋潯浠惰繑鍥炴牳閿�鐮侊級锛宖alse=闂ㄥ簵绔紙濮嬬粓杩斿洖鏍搁攢鐮侊級
+     */
+    private MyOrderDetailVO buildOrderDetailVO(Orders order, boolean memberViewMode) {
+        MyOrderDetailVO vo = new MyOrderDetailVO();
+        vo.setId(order.getId());
+        vo.setStatus(order.getStatus());
+        vo.setType(order.getType());
+        vo.setCode(order.getCode());
+        vo.setOutTradeNo(order.getOutTradeNo());
+        vo.setRemark(order.getRemark());
+        vo.setCreateTime(order.getCreateTime());
+        vo.setPayTime(order.getPayTime());
+        vo.setExpectedDepositTime(order.getExpectedDepositTime());
+        vo.setExpectedTakeTime(order.getExpectedTakeTime());
+        vo.setArriveTime(order.getArriveTime());
+
+        // 璐圭敤锛堝垎锛�
+        vo.setBasicAmount(order.getBasicAmount());
+        vo.setDeclaredAmount(order.getDeclaredAmount());
+        vo.setDeclaredFee(order.getDeclaredFee());
+        vo.setUrgentAmount(order.getUrgentAmount());
+        vo.setActualPayAmount(order.getPayAmount());
+
+        // 鏍囪
+        vo.setExceptionStatus(order.getExceptionStatus());
+
+        // 鏄惁瓒呭嚭鍙栦欢鏃堕棿
+        vo.setPastTakeTime(order.getExpectedTakeTime() != null && new Date().after(order.getExpectedTakeTime()));
+
+        // 璁㈠崟鐘舵�佹弿杩� + 鍊掕鏃�
+        vo.setStatusDesc(buildStatusDesc(order));
+        if (Constants.equalsInteger(order.getStatus(), Constants.OrderStatus.waitPay.getStatus())) {
+            vo.setPayCountdownMs(calcPayCountdownMs(order));
+        }
+
+        // 瀛樹欢闂ㄥ簵
+        if (order.getDepositShopId() != null) {
+            ShopInfo depositShop = shopInfoMapper.selectById(order.getDepositShopId());
+            if (depositShop != null) {
+                vo.setDepositShopName(depositShop.getName());
+                vo.setDepositShopLinkName(depositShop.getLinkName());
+                vo.setDepositShopPhone(depositShop.getLinkPhone());
+                vo.setDepositShopAddress(depositShop.getAddress());
+            }
+        }
+
+        // 鍙栦欢淇℃伅
+        if (order.getTakeShopId() != null) {
+            ShopInfo takeShop = shopInfoMapper.selectById(order.getTakeShopId());
+            if (takeShop != null) {
+                vo.setTakeShopName(takeShop.getName());
+                vo.setTakeShopAddress(takeShop.getAddress());
+            }
+        } else {
+            vo.setTakeLocation(order.getTakeLocation());
+            vo.setTakeLocationRemark(order.getTakeLocationRemark());
+        }
+
+        // 鍙栦欢鑱旂郴浜�
+        vo.setTakeUser(order.getTakeUser());
+        vo.setTakePhone(order.getTakePhone());
+
+        // 鐗╁搧绫诲瀷鍚嶇О
+        if (order.getGoodType() != null) {
+            Category category = categoryMapper.selectById(order.getGoodType());
+            if (category != null) {
+                vo.setGoodTypeName(category.getName());
+            }
+        }
+
+        // 涓嬪崟鐓х墖
+        String imgPrefix = getOrdersPrefix();
+        vo.setOrderImages(getFileUrls(order.getId(), Constants.FileType.ORDER_FILE.getKey(), imgPrefix));
+
+        // 鐗╁搧鏄庣粏
+        List<OrdersDetail> details = ordersDetailMapper.selectList(
+                new QueryWrapper<OrdersDetail>().lambda()
+                        .eq(OrdersDetail::getOrderId, order.getId())
+                        .eq(OrdersDetail::getDeleted, Constants.ZERO));
+        vo.setDetailList(buildDetailList(details));
+
+        // 閫炬湡淇℃伅
+        OverdueFeeVO overdueInfo = calculateOverdueFeeInternal(order, details);
+        vo.setOverdue(overdueInfo.getOverdue());
+        vo.setOverdueDays(overdueInfo.getOverdueDays());
+        vo.setOverdueFee(overdueInfo.getOverdueFee());
+
+        // 鏍搁攢鐮�
+        Integer status = order.getStatus();
+        if (memberViewMode) {
+            // 浼氬憳绔細寰呭瘎瀛�(1)杩斿洖锛涘緟鍙栦欢(5)鏃讹紝灏卞湴瀵勫瓨鏃犲彇浠堕棬搴椾笉杩斿洖
+            boolean returnCode = false;
+            if (Constants.equalsInteger(status, Constants.OrderStatus.waitDeposit.getStatus())) {
+                returnCode = true;
+            } else if (Constants.equalsInteger(status, Constants.OrderStatus.arrived.getStatus())) {
+                if (!(Constants.equalsInteger(order.getType(), Constants.ZERO) && order.getTakeShopId() == null)) {
+                    returnCode = true;
+                }
+            }
+            if (returnCode) {
+                vo.setMemberVerifyCode(order.getMemberVerifyCode());
+            }
+        } else {
+            // 闂ㄥ簵绔細濮嬬粓杩斿洖浼氬憳鏍搁攢鐮�
+            vo.setMemberVerifyCode(order.getMemberVerifyCode());
+        }
+
+        return vo;
+    }
+
+    @Override
+    @Transactional(rollbackFor = {Exception.class, BusinessException.class})
+    public void cancelOrder(Integer orderId, Integer memberId, String reason) {
+        Orders order = ordersMapper.selectOne(new QueryWrapper<Orders>().lambda()
+                .eq(Orders::getId, orderId)
+                .eq(Orders::getMemberId, memberId)
+                .eq(Orders::getDeleted, Constants.ZERO));
+        if (order == null) {
+            throw new BusinessException(ResponseStatus.DATA_EMPTY);
+        }
+
+        // 浠呭紓鍦板瘎瀛樺彲鍙栨秷
+        if (!Constants.equalsInteger(order.getType(), Constants.ONE)) {
+            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "浠呭紓鍦板瘎瀛樿鍗曞彲鍙栨秷");
+        }
+
+        Integer status = order.getStatus();
+        if (status == null) {
+            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "璁㈠崟鐘舵�佸紓甯�");
+        }
+
+        Date now = new Date();
+
+        // 寰呮敮浠橈細鐩存帴鍙栨秷
+        if (Constants.equalsInteger(status, Constants.OrderStatus.waitPay.getStatus())) {
+            order.setStatus(Constants.OrderStatus.cancelled.getStatus());
+            order.setCancelTime(now);
+            ordersMapper.updateById(order);
+            saveCancelLog(order, "浼氬憳鍙栨秷璁㈠崟锛堝緟鏀粯锛�", reason, memberId);
+            return;
+        }
+
+        // 寰呭瘎瀛橈細鐩存帴鍙栨秷锛屽叏棰濋��娆�
+        if (Constants.equalsInteger(status, Constants.OrderStatus.waitDeposit.getStatus())) {
+            // 璁板綍閫�娆句俊鎭�
+            OrdersRefund refund = new OrdersRefund();
+            refund.setOrderId(orderId);
+            refund.setType(0); // 鏈瘎瀛樼洿鎺ュ彇娑�
+            refund.setCancelInfo(reason);
+            refund.setCreateTime(now);
+            refund.setDeleted(Constants.ZERO);
+
+            // 璋冪敤寰俊閫�娆撅紝鍏ㄩ閫�娆�
+            String refundCode = wxMiniUtilService.wxRefund(order.getOutTradeNo(), order.getPayAmount(), order.getPayAmount());
+            refund.setRefundCode(refundCode);
+            refund.setRefundTime(new Date());
+            ordersRefundMapper.insert(refund);
+
+            order.setStatus(Constants.OrderStatus.cancelled.getStatus());
+            order.setCancelTime(now);
+            order.setRefundAmount(order.getPayAmount());
+            ordersMapper.updateById(order);
+
+            saveCancelLog(order, "浼氬憳鍙栨秷璁㈠崟锛堝緟瀵勫瓨锛屽叏棰濋��娆撅級", reason, memberId);
+            return;
+        }
+
+        // 宸插瘎瀛�/宸叉帴鍗曪細杩涘叆鍙栨秷涓姸鎬�
+        if (Constants.equalsInteger(status, Constants.OrderStatus.deposited.getStatus())
+                || Constants.equalsInteger(status, Constants.OrderStatus.accepted.getStatus())) {
+            order.setStatus(Constants.OrderStatus.cancelling.getStatus());
+            order.setCancelTime(now);
+            ordersMapper.updateById(order);
+            saveCancelLog(order, "浼氬憳鐢宠鍙栨秷璁㈠崟锛堝凡瀵勫瓨/宸叉帴鍗曪級", reason, memberId);
+            return;
+        }
+
+        throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "褰撳墠璁㈠崟鐘舵�佷笉鍏佽鍙栨秷");
+    }
+
+    /**
+     * 淇濆瓨鍙栨秷璁㈠崟鎿嶄綔鏃ュ織
+     */
+    private void saveCancelLog(Orders order, String title, String reason, Integer memberId) {
+        OrderLog log = new OrderLog();
+        log.setOrderId(order.getId());
+        log.setTitle(title);
+        log.setLogInfo(reason);
+        log.setObjType(Constants.ORDER_LOG_CANCEL);
+        log.setOrderStatus(order.getStatus());
+        log.setOptUserId(memberId);
+        log.setOptUserType(0); // 0=鐢ㄦ埛
+        log.setCreateTime(new Date());
+        log.setDeleted(Constants.ZERO);
+        orderLogService.create(log);
+    }
+
+    /**
+     * 淇濆瓨闂ㄥ簵鏍搁攢鏃ュ織
+     */
+    private void saveShopVerifyLog(Orders order, String title, String logInfo, String remark, Integer shopId) {
+        OrderLog log = new OrderLog();
+        log.setOrderId(order.getId());
+        log.setTitle(title);
+        log.setLogInfo(logInfo);
+        log.setRemark(remark);
+        log.setOrderStatus(order.getStatus());
+        log.setOptUserId(shopId);
+        log.setOptUserType(2); // 2=闂ㄥ簵
+        log.setCreateTime(new Date());
+        log.setDeleted(Constants.ZERO);
+        orderLogService.create(log);
+    }
+
+    @Override
+    @Transactional(rollbackFor = {Exception.class, BusinessException.class})
+    public void handleStorageOrderPayNotify(String outTradeNo, String wxTradeNo) {
+        Orders order = ordersMapper.selectOne(new QueryWrapper<Orders>().lambda()
+                .eq(Orders::getOutTradeNo, outTradeNo)
+                .eq(Orders::getDeleted, Constants.ZERO)
+                .last("limit 1"));
+        if (order == null) {
+            throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "璁㈠崟涓嶅瓨鍦�: " + outTradeNo);
+        }
+        // 骞傜瓑锛氬凡鏀粯鍒欒烦杩�
+        if (Constants.equalsInteger(order.getPayStatus(), Constants.ONE)) {
+            return;
+        }
+        Date now = new Date();
+        order.setStatus(Constants.OrderStatus.waitDeposit.getStatus()); // 寰呭瘎瀛�
+        order.setPayStatus(Constants.ONE); // 宸叉敮浠�
+        order.setPayTime(now);
+        order.setWxExternalNo(wxTradeNo);
+        order.setUpdateTime(now);
+        // 鐢熸垚浼氬憳鏍搁攢鐮�
+        order.setMemberVerifyCode(generateVerifyCode());
+        ordersMapper.updateById(order);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public PayResponse payOverdueFee(Integer orderId, Integer memberId) {
+        // 1. 鏌ヨ瀵勫瓨璁㈠崟
+        Orders order = ordersMapper.selectOne(new QueryWrapper<Orders>().lambda()
+                .eq(Orders::getId, orderId)
+                .eq(Orders::getMemberId, memberId)
+                .eq(Orders::getDeleted, Constants.ZERO));
+        if (order == null) {
+            throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "璁㈠崟涓嶅瓨鍦�");
+        }
+        // 2. 鏍¢獙鐘舵�侊細寰呭彇浠�(5) + 閫炬湡(1)
+        if (!Constants.equalsInteger(order.getStatus(), Constants.OrderStatus.arrived.getStatus())) {
+            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "褰撳墠璁㈠崟鐘舵�佷笉鏀寔閫炬湡鏀粯");
+        }
+        if (!Constants.equalsInteger(order.getOverdueStatus(), Constants.ONE)) {
+            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "璇ヨ鍗曚笉瀛樺湪閫炬湡璐圭敤");
+        }
+        if (order.getOverdueAmount() == null || order.getOverdueAmount() <= 0) {
+            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "閫炬湡璐圭敤寮傚父锛屾棤娉曞彂璧锋敮浠�");
+        }
+        // 3. 鏌ヨ浼氬憳
+        Member member = memberMapper.selectById(memberId);
+        if (member == null || StringUtils.isBlank(member.getOpenid())) {
+            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "鐢ㄦ埛淇℃伅寮傚父锛屾棤娉曞彂璧锋敮浠�");
+        }
+        // 4. 鍒涘缓閫炬湡璐圭敤璁㈠崟
+        String outTradeNo = generateOrderTradeNo();
+        Date now = new Date();
+        OtherOrders otherOrders = new OtherOrders();
+        otherOrders.setType(Constants.TWO); // 2=閫炬湡璐圭敤璁㈠崟
+        otherOrders.setMemberId(memberId);
+        otherOrders.setOrderId(orderId);
+        otherOrders.setPayAccount(order.getOverdueAmount());
+        otherOrders.setPayStatus(Constants.ZERO);
+        otherOrders.setCode("OD" + new java.text.SimpleDateFormat("yyyyMMddHHmmss").format(now) + orderId);
+        otherOrders.setOutTradeNo(outTradeNo);
+        otherOrders.setDeleted(Constants.ZERO);
+        otherOrders.setCreateTime(now);
+        otherOrdersMapper.insert(otherOrders);
+
+        // 5. 鍞よ捣寰俊鏀粯
+        return wxPayForOtherOrder(otherOrders, member.getOpenid(), Constants.OrdersAttach.OVERDUE_FEE);
+    }
+
+    @Override
+    @Transactional(rollbackFor = {Exception.class, BusinessException.class})
+    public void handleOverdueFeePayNotify(String outTradeNo, String wxTradeNo) {
+        // 1. 鏌ユ壘閫炬湡璐圭敤璁㈠崟
+        OtherOrders otherOrders = otherOrdersMapper.selectOne(new QueryWrapper<OtherOrders>().lambda()
+                .eq(OtherOrders::getOutTradeNo, outTradeNo)
+                .eq(OtherOrders::getDeleted, Constants.ZERO)
+                .last("limit 1"));
+        if (otherOrders == null) {
+            throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "閫炬湡璐圭敤璁㈠崟涓嶅瓨鍦�: " + outTradeNo);
+        }
+        // 2. 骞傜瓑锛氬凡鏀粯鍒欒烦杩�
+        if (Constants.equalsInteger(otherOrders.getPayStatus(), Constants.ONE)) {
+            return;
+        }
+        Date now = new Date();
+        // 3. 鏇存柊閫炬湡璐圭敤璁㈠崟鐘舵��
+        otherOrders.setPayStatus(Constants.ONE);
+        otherOrders.setPayTime(now);
+        otherOrders.setWxExternalNo(wxTradeNo);
+        otherOrders.setUpdateTime(now);
+        otherOrdersMapper.updateById(otherOrders);
+
+        // 4. 鏇存柊瀵勫瓨璁㈠崟閫炬湡鐘舵�佷负宸叉敮浠�(2)锛屾洿鏂版�婚噾棰濓紝閲嶇畻涓夋柟鏀剁泭
+        if (otherOrders.getOrderId() != null) {
+            Orders order = ordersMapper.selectById(otherOrders.getOrderId());
+            if (order != null) {
+                order.setOverdueStatus(Constants.TWO); // 2=宸叉敮浠�
+                // 鎬婚噾棰� = 鍘熼噾棰� + 閫炬湡璐圭敤
+                Long overdueFee = otherOrders.getPayAccount() != null ? otherOrders.getPayAccount() : 0L;
+                long newTotal = (order.getTotalAmount() != null ? order.getTotalAmount() : 0L) + overdueFee;
+                order.setTotalAmount(newTotal);
+                order.setUpdateTime(now);
+                ordersMapper.updateById(order);
+                // 閲嶇畻涓夋柟鏀剁泭
+                calculateAndSaveOrderFees(order.getId());
+            }
+        }
+    }
+
+    @Override
+    public void deleteMyOrder(Integer orderId, Integer memberId) {
+        Orders order = ordersMapper.selectById(orderId);
+        if (order == null || !Constants.equalsInteger(order.getDeleted(), Constants.ZERO)) {
+            throw new BusinessException(ResponseStatus.DATA_EMPTY);
+        }
+        if (!Constants.equalsInteger(order.getMemberId(), memberId)) {
+            throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "鏃犳潈鎿嶄綔姝よ鍗�");
+        }
+        // 浠呭凡瀹屾垚(7)銆佸凡鍙栨秷(99)銆佸凡閫�娆�(96)鍙垹闄�
+        int status = Constants.formatIntegerNum(order.getStatus());
+        if (status != Constants.OrderStatus.finished.getStatus()
+                && status != Constants.OrderStatus.cancelled.getStatus()
+                && status != Constants.OrderStatus.closed.getStatus()) {
+            throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "褰撳墠璁㈠崟鐘舵�佷笉鍙垹闄�");
+        }
+        ordersMapper.update(new UpdateWrapper<Orders>().lambda()
+                .set(Orders::getDeleted, Constants.ONE)
+                .set(Orders::getUpdateTime, new Date())
+                .eq(Orders::getId, orderId));
+    }
+
+    @Override
+    @Transactional(rollbackFor = {Exception.class, BusinessException.class})
+    public PayResponse payShopDeposit(Integer shopId) {
+        // 1. 鏌ヨ闂ㄥ簵淇℃伅
+        ShopInfo shopInfo = shopInfoMapper.selectById(shopId);
+        if (shopInfo == null) {
+            throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "闂ㄥ簵涓嶅瓨鍦�");
+        }
+        // 2. 鏍¢獙鐘舵�侊細瀹℃壒閫氳繃(1)鎵嶈兘鏀粯鎶奸噾
+        if (!Constants.equalsInteger(shopInfo.getAuditStatus(), Constants.ONE)) {
+            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "褰撳墠闂ㄥ簵鐘舵�佷笉鏀寔鏀粯鎶奸噾");
+        }
+        if (shopInfo.getDepositAmount() == null || shopInfo.getDepositAmount() <= 0) {
+            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "鎶奸噾閲戦寮傚父锛屾棤娉曞彂璧锋敮浠�");
+        }
+        // 3. 鏌ヨ浼氬憳openid
+        Member member = memberMapper.selectById(shopInfo.getRegionMemberId());
+        if (member == null || StringUtils.isBlank(member.getOpenid())) {
+            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "鐢ㄦ埛淇℃伅寮傚父锛屾棤娉曞彂璧锋敮浠�");
+        }
+        // 4. 鍒涘缓鎶奸噾璁㈠崟
+        String outTradeNo = generateOrderTradeNo();
+        Date now = new Date();
+        OtherOrders otherOrders = new OtherOrders();
+        otherOrders.setType(Constants.ZERO); // 0=搴楅摵鎶奸噾璁㈠崟
+        otherOrders.setMemberId(shopInfo.getRegionMemberId());
+        otherOrders.setPayAccount(shopInfo.getDepositAmount());
+        otherOrders.setPayStatus(Constants.ZERO);
+        otherOrders.setCode("SD" + new java.text.SimpleDateFormat("yyyyMMddHHmmss").format(now) + shopId);
+        otherOrders.setOutTradeNo(outTradeNo);
+        otherOrders.setDeleted(Constants.ZERO);
+        otherOrders.setCreateTime(now);
+        otherOrdersMapper.insert(otherOrders);
+
+        // 5. 鍞よ捣寰俊鏀粯
+        return wxPayForOtherOrder(otherOrders, member.getOpenid(), Constants.OrdersAttach.SHOP_DEPOSIT);
+    }
+
+    @Override
+    @Transactional(rollbackFor = {Exception.class, BusinessException.class})
+    public void handleShopDepositPayNotify(String outTradeNo, String wxTradeNo) {
+        // 1. 鏌ユ壘鎶奸噾璁㈠崟
+        OtherOrders otherOrders = otherOrdersMapper.selectOne(new QueryWrapper<OtherOrders>().lambda()
+                .eq(OtherOrders::getOutTradeNo, outTradeNo)
+                .eq(OtherOrders::getDeleted, Constants.ZERO)
+                .last("limit 1"));
+        if (otherOrders == null) {
+            throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "鎶奸噾璁㈠崟涓嶅瓨鍦�: " + outTradeNo);
+        }
+        // 2. 骞傜瓑锛氬凡鏀粯鍒欒烦杩�
+        if (Constants.equalsInteger(otherOrders.getPayStatus(), Constants.ONE)) {
+            return;
+        }
+        Date now = new Date();
+        // 3. 鏇存柊鎶奸噾璁㈠崟鐘舵��
+        otherOrders.setPayStatus(Constants.ONE);
+        otherOrders.setPayTime(now);
+        otherOrders.setWxExternalNo(wxTradeNo);
+        otherOrders.setUpdateTime(now);
+        otherOrdersMapper.updateById(otherOrders);
+
+        // 4. 鏌ヨ闂ㄥ簵淇℃伅锛堥�氳繃娉ㄥ唽浼氬憳涓婚敭鍏宠仈锛�
+        ShopInfo shopInfo = shopInfoMapper.selectOne(new QueryWrapper<ShopInfo>().lambda()
+                .eq(ShopInfo::getRegionMemberId, otherOrders.getMemberId())
+                .eq(ShopInfo::getDeleted, Constants.ZERO)
+                .last("limit 1"));
+        if (shopInfo == null) {
+            throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "闂ㄥ簵涓嶅瓨鍦�");
+        }
+        // 5. 鏇存柊闂ㄥ簵鐘舵�侊細宸叉敮浠樻娂閲�
+        shopInfo.setAuditStatus(Constants.THREE); // 3=宸叉敮浠樻娂閲�
+        shopInfo.setPayStatus(Constants.ONE);
+        shopInfo.setPayTime(now);
+        shopInfo.setWxExternalNo(wxTradeNo);
+        shopInfo.setCode(otherOrders.getCode());
+        Member member = memberMapper.selectById(otherOrders.getMemberId());
+        if (member != null) {
+            shopInfo.setPayMemberOpenId(member.getOpenid());
+        }
+        shopInfo.setUpdateTime(now);
+        shopInfoMapper.updateById(shopInfo);
+
+        // 6. 鎶奸噾鏀粯瀹屾垚鍚庯紝鑻ュ煄甯傛湭寮�閫氬垯鑷姩寮�閫�
+        if (shopInfo.getAreaId() != null) {
+            Areas shopArea = areasBiz.resolveArea(shopInfo.getAreaId());
+            if (shopArea != null && shopArea.getParentId() != null) {
+                Areas cityArea = areasBiz.resolveArea(shopArea.getParentId());
+                if (cityArea != null && !Constants.equalsInteger(cityArea.getStatus(), Constants.ONE)) {
+                    cityArea.setStatus(Constants.ONE);
+                    cityArea.setEditDate(now);
+                    areasService.updateById(cityArea);
+                    areasService.cacheData();
+                }
+            }
+        }
+    }
+
+    @Override
+    @Transactional(rollbackFor = {Exception.class, BusinessException.class})
+    public void settleOrders() {
+        // 1. 璇诲彇缁撶畻澶╂暟閰嶇疆
+        SystemDictData settlementConfig = systemDictDataBiz.queryByCode(Constants.OPERATION_CONFIG, Constants.OP_SETTLEMENT_DATE);
+        if (settlementConfig == null || StringUtils.isBlank(settlementConfig.getCode())) {
+            return;
+        }
+        int days = Integer.parseInt(settlementConfig.getCode());
+        // 缁撶畻鎴鏃堕棿 = 褰撳墠鏃堕棿 - N澶�
+        Calendar cal = Calendar.getInstance();
+        cal.add(Calendar.DAY_OF_MONTH, -days);
+        Date deadline = cal.getTime();
+
+        // 2. 鏌ヨ宸插畬鎴愮殑寰呯粨绠楄鍗曪紙瀹屾垚鏃堕棿 <= 鎴鏃堕棿锛�
+        List<Orders> ordersList = ordersMapper.selectList(new QueryWrapper<Orders>().lambda()
+                .eq(Orders::getDeleted, Constants.ZERO)
+                .eq(Orders::getStatus, Constants.OrderStatus.finished.getStatus())
+                .eq(Orders::getSettlementStatus, Constants.ZERO)
+                .le(Orders::getFinishTime, deadline));
+        if (ordersList == null || ordersList.isEmpty()) {
+            return;
+        }
+
+        Date now = new Date();
+        for (Orders order : ordersList) {
+            // 3. 鏇存柊璁㈠崟缁撶畻鐘舵��
+            ordersMapper.update(new UpdateWrapper<Orders>().lambda()
+                    .set(Orders::getSettlementStatus, Constants.ONE)
+                    .set(Orders::getSettlementTime, now)
+                    .set(Orders::getUpdateTime, now)
+                    .eq(Orders::getId, order.getId()));
+
+            // 4. 鏌ヨ鍏宠仈鐨勫緟鍏ヨ处 Revenue 璁板綍
+            List<Revenue> revenues = revenueMapper.selectList(new QueryWrapper<Revenue>().lambda()
+                    .eq(Revenue::getObjId, order.getId())
+                    .eq(Revenue::getObjType, Constants.ZERO)
+                    .eq(Revenue::getVaildStatus, Constants.ZERO)
+                    .eq(Revenue::getDeleted, Constants.ZERO));
+
+            for (Revenue revenue : revenues) {
+                Long amount = revenue.getAmount() != null ? revenue.getAmount() : 0L;
+                // 鏇存柊 Revenue 涓哄凡鍏ヨ处
+                revenueMapper.update(new UpdateWrapper<Revenue>().lambda()
+                        .set(Revenue::getVaildStatus, Constants.ONE)
+                        .set(Revenue::getUpdateTime, now)
+                        .eq(Revenue::getId, revenue.getId()));
+
+                // 鏍规嵁 memberType 鏇存柊浣欓
+                if (Constants.equalsInteger(revenue.getMemberType(), Constants.ONE)) {
+                    // 鍙告満锛氶�氳繃 memberId 鏌� DriverInfo锛屾洿鏂� balance / totalBalance
+                    DriverInfo driver = driverInfoMapper.selectOne(new QueryWrapper<DriverInfo>().lambda()
+                            .eq(DriverInfo::getMemberId, revenue.getMemberId())
+                            .eq(DriverInfo::getDeleted, Constants.ZERO)
+                            .last("limit 1"));
+                    if (driver != null) {
+                        driverInfoMapper.update(new UpdateWrapper<DriverInfo>().lambda()
+                                .setSql(" BALANCE = IFNULL(BALANCE, 0) + " + amount)
+                                .setSql(" TOTAL_BALANCE = IFNULL(TOTAL_BALANCE, 0) + " + amount)
+                                .eq(DriverInfo::getId, driver.getId()));
+                    }
+                } else if (Constants.equalsInteger(revenue.getMemberType(), Constants.TWO)) {
+                    // 闂ㄥ簵锛氶�氳繃 memberId 鏌� ShopInfo(regionMemberId)锛屾洿鏂� balance / totalBalance
+                    ShopInfo shop = shopInfoMapper.selectOne(new QueryWrapper<ShopInfo>().lambda()
+                            .eq(ShopInfo::getRegionMemberId, revenue.getMemberId())
+                            .eq(ShopInfo::getDeleted, Constants.ZERO)
+                            .last("limit 1"));
+                    if (shop != null) {
+                        shopInfoMapper.update(new UpdateWrapper<ShopInfo>().lambda()
+                                .setSql(" BALANCE = IFNULL(BALANCE, 0) + " + amount)
+                                .setSql(" TOTAL_BALANCE = IFNULL(TOTAL_BALANCE, 0) + " + amount)
+                                .eq(ShopInfo::getId, shop.getId()));
+                    }
+                }
+            }
+        }
+    }
+
+    @Override
+    @Transactional(rollbackFor = {Exception.class, BusinessException.class})
+    public void commentOrder(CommentOrderDTO dto, Integer memberId) {
+        // 1. 鏍¢獙璁㈠崟
+        Orders order = ordersMapper.selectById(dto.getOrderId());
+        if (order == null || Constants.equalsInteger(order.getDeleted(), Constants.ONE)) {
+            throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "璁㈠崟涓嶅瓨鍦�");
+        }
+        if (!Constants.equalsInteger(order.getMemberId(), memberId)) {
+            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "鏃犳潈璇勪环璇ヨ鍗�");
+        }
+        if (!Constants.equalsInteger(order.getStatus(), Constants.OrderStatus.finished.getStatus())) {
+            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "褰撳墠璁㈠崟鐘舵�佷笉鏀寔璇勪环");
+        }
+        if (Constants.equalsInteger(order.getCommentStatus(), Constants.ONE)) {
+            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "璇ヨ鍗曞凡璇勪环");
+        }
+
+        // 2. 寮傚湴瀵勫瓨璁㈠崟锛氬彇浠堕棬搴楀拰鍙告満璇勫垎鏍¢獙
+        boolean isRemote = Constants.equalsInteger(order.getType(), Constants.ONE);
+        if (isRemote) {
+            if (dto.getDriverScore() == null) {
+                throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "寮傚湴瀵勫瓨璁㈠崟蹇呴』璇勪环鍙告満");
+            }
+            if (order.getTakeShopId() != null && dto.getTakeScore() == null) {
+                throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "璇疯瘎浠峰彇浠堕棬搴�");
+            }
+        }
+
+        Date now = new Date();
+
+        // 3. 鏇存柊璁㈠崟璇勪环鐘舵��
+        order.setCommentStatus(Constants.ONE);
+        order.setCommentInfo(dto.getContent());
+        order.setCommentDepositLevel(dto.getDepositScore());
+        order.setCommentTakeLevel(dto.getTakeScore());
+        order.setCommentDriverLevel(dto.getDriverScore());
+        order.setCommentTime(now);
+        order.setUpdateTime(now);
+        ordersMapper.updateById(order);
+
+        // 4. 鍒涘缓璇勪环璁板綍
+        // 4.1 瀛樹欢闂ㄥ簵
+        OrderComment depositComment = new OrderComment();
+        depositComment.setOrderId(order.getId());
+        depositComment.setOrderCode(order.getCode());
+        depositComment.setMemberId(memberId);
+        depositComment.setTargetType(Constants.ONE); // 1=瀛樹欢闂ㄥ簵
+        depositComment.setTargetId(order.getDepositShopId());
+        depositComment.setScore(dto.getDepositScore());
+        depositComment.setContent(dto.getContent());
+        depositComment.setDeleted(Constants.ZERO);
+        depositComment.setCreateTime(now);
+        orderCommentMapper.insert(depositComment);
+
+        // 4.2 鍙栦欢闂ㄥ簵锛堝紓鍦板瘎瀛樹笖鏈夊彇浠堕棬搴楋級
+        if (isRemote && order.getTakeShopId() != null && dto.getTakeScore() != null) {
+            OrderComment takeComment = new OrderComment();
+            takeComment.setOrderId(order.getId());
+            takeComment.setOrderCode(order.getCode());
+            takeComment.setMemberId(memberId);
+            takeComment.setTargetType(Constants.TWO); // 2=鍙栦欢闂ㄥ簵
+            takeComment.setTargetId(order.getTakeShopId());
+            takeComment.setScore(dto.getTakeScore());
+            takeComment.setContent(dto.getContent());
+            takeComment.setDeleted(Constants.ZERO);
+            takeComment.setCreateTime(now);
+            orderCommentMapper.insert(takeComment);
+        }
+
+        // 4.3 鍙告満锛堝紓鍦板瘎瀛橈級
+        if (isRemote && order.getAcceptDriver() != null && dto.getDriverScore() != null) {
+            OrderComment driverComment = new OrderComment();
+            driverComment.setOrderId(order.getId());
+            driverComment.setOrderCode(order.getCode());
+            driverComment.setMemberId(memberId);
+            driverComment.setTargetType(Constants.THREE); // 3=鍙告満
+            driverComment.setTargetId(order.getAcceptDriver());
+            driverComment.setScore(dto.getDriverScore());
+            driverComment.setContent(dto.getContent());
+            driverComment.setDeleted(Constants.ZERO);
+            driverComment.setCreateTime(now);
+            orderCommentMapper.insert(driverComment);
+        }
+
+        // 5. 鏇存柊闂ㄥ簵/鍙告満骞冲潎璇勫垎
+        updateTargetScore(Constants.ONE, order.getDepositShopId());
+        if (isRemote && order.getTakeShopId() != null) {
+            updateTargetScore(Constants.TWO, order.getTakeShopId());
+        }
+        if (isRemote && order.getAcceptDriver() != null) {
+            updateTargetScore(Constants.THREE, order.getAcceptDriver());
+        }
+    }
+
+    /**
+     * 鏇存柊璇勪环瀵硅薄锛堥棬搴�/鍙告満锛夌殑骞冲潎璇勫垎
+     */
+    private void updateTargetScore(Integer targetType, Integer targetId) {
+        List<OrderComment> comments = orderCommentMapper.selectList(new QueryWrapper<OrderComment>().lambda()
+                .eq(OrderComment::getDeleted, Constants.ZERO)
+                .eq(OrderComment::getTargetType, targetType)
+                .eq(OrderComment::getTargetId, targetId));
+        if (comments.isEmpty()) {
+            return;
+        }
+        double avg = comments.stream()
+                .mapToInt(OrderComment::getScore)
+                .average()
+                .orElse(0.0);
+        BigDecimal score = BigDecimal.valueOf(avg).setScale(1, BigDecimal.ROUND_HALF_UP);
+        Date now = new Date();
+        if (Constants.equalsInteger(targetType, Constants.ONE) || Constants.equalsInteger(targetType, Constants.TWO)) {
+            ShopInfo shopInfo = shopInfoMapper.selectById(targetId);
+            if (shopInfo != null) {
+                shopInfo.setScore(score);
+                shopInfo.setUpdateTime(now);
+                shopInfoMapper.updateById(shopInfo);
+            }
+        } else if (Constants.equalsInteger(targetType, Constants.THREE)) {
+            DriverInfo driverInfo = driverInfoMapper.selectById(targetId);
+            if (driverInfo != null) {
+                driverInfo.setScore(score);
+                driverInfo.setUpdateTime(now);
+                driverInfoMapper.updateById(driverInfo);
+            }
+        }
+    }
+
+    /**
+     * 鍞よ捣寰俊鏀粯锛堝叾浠栬鍗曪級
+     */
+    private PayResponse wxPayForOtherOrder(OtherOrders otherOrders, String openid, Constants.OrdersAttach ordersAttach) {
+        try {
+            WxPayUnifiedOrderRequest request = new WxPayUnifiedOrderRequest();
+            request.setBody(ordersAttach.getName());
+            request.setAttach(ordersAttach.getKey());
+            request.setOutTradeNo(otherOrders.getOutTradeNo());
+            long totalFee = otherOrders.getPayAccount() != null ? otherOrders.getPayAccount() : 0L;
+            request.setTotalFee((int) totalFee);
+            request.setTimeStart(DateUtil.DateToString(new Date(), "yyyyMMddHHmmss"));
+            request.setSpbillCreateIp(Constants.getIpAddr());
+            request.setOpenid(openid);
+
+            Object response = WxMiniConfig.wxPayService.createOrder(request);
+
+            PayResponse payResponse = new PayResponse();
+            payResponse.setResponse(response);
+            payResponse.setOrderId(otherOrders.getId());
+            return payResponse;
+        } catch (WxPayException e) {
+            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "鏀粯璋冭捣澶辫触锛�" + e.getMessage());
+        }
+    }
+
+    @Override
+    @Transactional(rollbackFor = {Exception.class, BusinessException.class})
+    public void shopVerifyOrder(String verifyCode, Integer shopId, List<String> images, String remark) {
+        if (StringUtils.isBlank(verifyCode)) {
+            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "鏍搁攢鐮佷笉鑳戒负绌�");
+        }
+        // 鏍规嵁鏍搁攢鐮佹煡鎵捐鍗曪紙浼氬憳鏍搁攢鐮侊級
+        Orders order = ordersMapper.selectOne(new QueryWrapper<Orders>().lambda()
+                .eq(Orders::getMemberVerifyCode, verifyCode)
+                .eq(Orders::getDeleted, Constants.ZERO)
+                .last("limit 1"));
+        if (order == null) {
+            throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "鏍搁攢鐮佹棤鏁�");
+        }
+
+        // 鏌ヨ闂ㄥ簵鍚嶇О鐢ㄤ簬鏃ュ織
+        String shopName = "";
+        ShopInfo shopInfo = shopInfoMapper.selectById(shopId);
+        if (shopInfo != null) {
+            shopName = shopInfo.getName() != null ? shopInfo.getName() : "";
+        }
+
+        Integer status = order.getStatus();
+        Date now = new Date();
+        if (Constants.equalsInteger(status, Constants.OrderStatus.waitDeposit.getStatus())) {
+            // 寰呭瘎瀛�(1) 鈫� 宸插瘎瀛�(2)锛屼袱绉嶇被鍨嬮�氱敤
+            // 鏍¢獙褰撳墠闂ㄥ簵鏄惁涓鸿鍗曠殑瀛樹欢闂ㄥ簵
+            if (!shopId.equals(order.getDepositShopId())) {
+                throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "璇ヨ鍗曚笉灞炰簬褰撳墠闂ㄥ簵锛屾棤娉曟牳閿�");
+            }
+            order.setStatus(Constants.OrderStatus.deposited.getStatus());
+            order.setDepositTime(now);
+            // 閲婃斁褰撳墠鏍搁攢鐮侊紝鐢熸垚鏂扮殑鏍搁攢鐮佷緵鍙栦欢鏃朵娇鐢�
+            releaseVerifyCode(verifyCode);
+            order.setMemberVerifyCode(generateVerifyCode());
+            ordersMapper.updateById(order);
+            // 淇濆瓨瀵勫瓨鍥剧墖锛坥bj_type=2 璁㈠崟瀵勫瓨鍥剧墖锛屾渶澶�3寮狅級
+            saveVerifyImages(order.getId(), images, Constants.FileType.ORDER_DEPOSIT.getKey(), shopId);
+            // 璁板綍璁㈠崟鏃ュ織
+            saveShopVerifyLog(order, "闂ㄥ簵纭瀵勫瓨", "闂ㄥ簵銆�" + shopName + "銆戠‘璁ゅ瘎瀛�", remark, shopId);
+        } else if (Constants.equalsInteger(status, Constants.OrderStatus.arrived.getStatus())) {
+            // 寮傚湴瀵勫瓨 + 鏃犲彇浠堕棬搴� 鈫� 鏃犳硶鏍搁攢锛堝鎴疯嚜鍙栵紝鏃犻棬搴楁搷浣滐級
+            if (Constants.equalsInteger(order.getType(), Constants.ONE) && order.getTakeShopId() == null) {
+                throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "璇ヨ鍗曟棤鍙栦欢闂ㄥ簵锛屾棤娉曟牳閿�");
+            }
+            // 鏍¢獙鍙栦欢闂ㄥ簵涓庡綋鍓嶇櫥褰曢棬搴椾竴鑷�
+            if (!shopId.equals(order.getTakeShopId())) {
+                throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "璇ヨ鍗曚笉灞炰簬褰撳墠闂ㄥ簵锛屾棤娉曟牳閿�");
+            }
+            // 寰呭彇浠�(5) 鈫� 宸插畬鎴�(7)
+            order.setStatus(Constants.OrderStatus.finished.getStatus());
+            order.setConfirmArriveTime(now);
+            ordersMapper.updateById(order);
+            // 璁㈠崟瀹屾垚锛岄噴鏀炬牳閿�鐮�
+            releaseVerifyCode(verifyCode);
+            // 淇濆瓨鍑哄簱鍥剧墖锛坥bj_type=13 闂ㄥ簵鍑哄簱鍥剧墖锛屾渶澶�3寮狅級
+            saveVerifyImages(order.getId(), images, Constants.FileType.STORE_OUT.getKey(), shopId);
+            // 鐢熸垚鏀剁泭璁板綍
+            calculateAndSaveOrderFees(order.getId());
+            generateRevenueRecords(order.getId());
+            // 璁板綍璁㈠崟鏃ュ織
+            saveShopVerifyLog(order, "闂ㄥ簵纭鍙栦欢", "闂ㄥ簵銆�" + shopName + "銆戠‘璁ゅ彇浠讹紝璁㈠崟瀹屾垚", remark, shopId);
+        } else {
+            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "褰撳墠璁㈠崟鐘舵�佷笉鍏佽鏍搁攢");
+        }
+    }
+
+    @Override
+    @Transactional(rollbackFor = {Exception.class, BusinessException.class})
+    public void confirmStoreOut(Integer orderId, Integer shopId, List<String> images, String remark) {
+        // 1. 鏌ヨ璁㈠崟
+        Orders order = ordersMapper.selectById(orderId);
+        if (order == null || Constants.equalsInteger(order.getDeleted(), Constants.ONE)) {
+            throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "璁㈠崟涓嶅瓨鍦�");
+        }
+        // 2. 鏍¢獙鐘舵�侊細寰呭彇浠�(5)
+        if (!Constants.equalsInteger(order.getStatus(), Constants.OrderStatus.arrived.getStatus())) {
+            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "褰撳墠璁㈠崟鐘舵�佷笉鍏佽鍑哄簱");
+        }
+        // 3. 鏍¢獙閫炬湡鐘舵�侊細0=鏈�炬湡 鎴� 2=宸叉敮浠�
+        if (order.getOverdueStatus() != null && Constants.equalsInteger(order.getOverdueStatus(), Constants.ONE)) {
+            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "璁㈠崟瀛樺湪閫炬湡鏈敮浠樿垂鐢紝璇峰厛瀹屾垚閫炬湡璐圭敤鏀粯");
+        }
+        // 4. 鏍¢獙纭鍒板簵鏃堕棿涓嶄负绌�
+        if (order.getConfirmArriveTime() == null) {
+            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "璁㈠崟灏氭湭纭鍒板簵锛屾棤娉曞嚭搴�");
+        }
+        // 5. 鏍¢獙闂ㄥ簵涓庤鍗曞叧绯�
+        if (Constants.equalsInteger(order.getType(), Constants.ZERO)) {
+            // 灏卞湴瀵勫瓨锛氬彇浠堕棬搴楀嵆瀛樹欢闂ㄥ簵
+            if (!shopId.equals(order.getDepositShopId())) {
+                throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "璇ヨ鍗曚笉灞炰簬褰撳墠闂ㄥ簵");
+            }
+        } else {
+            // 寮傚湴瀵勫瓨锛氭牎楠屽彇浠堕棬搴�
+            if (order.getTakeShopId() == null) {
+                throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "璇ヨ鍗曟棤鍙栦欢闂ㄥ簵锛屾棤娉曞嚭搴�");
+            }
+            if (!shopId.equals(order.getTakeShopId())) {
+                throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "璇ヨ鍗曚笉灞炰簬褰撳墠闂ㄥ簵");
+            }
+        }
+
+        // 6. 鏌ヨ闂ㄥ簵鍚嶇О
+        String shopName = "";
+        ShopInfo shopInfo = shopInfoMapper.selectById(shopId);
+        if (shopInfo != null) {
+            shopName = shopInfo.getName() != null ? shopInfo.getName() : "";
+        }
+
+        // 7. 鏇存柊璁㈠崟鐘舵�佷负宸插畬鎴�
+        Date now = new Date();
+        order.setStatus(Constants.OrderStatus.finished.getStatus());
+        order.setFinishTime(now);
+        order.setUpdateTime(now);
+        ordersMapper.updateById(order);
+
+        // 8. 閲婃斁鏍搁攢鐮�
+        if (StringUtils.isNotBlank(order.getMemberVerifyCode())) {
+            releaseVerifyCode(order.getMemberVerifyCode());
+        }
+
+        // 9. 淇濆瓨鍑哄簱鍥剧墖锛坥bj_type=13 闂ㄥ簵鍑哄簱鍥剧墖锛屾渶澶�3寮狅級
+        saveVerifyImages(order.getId(), images, Constants.FileType.STORE_OUT.getKey(), shopId);
+
+        // 10. 濡傛灉瀛樺湪閫�娆鹃噾棰濓紝鍏堜繚瀛橀��娆捐褰曞啀璋冪敤寰俊閫�娆�
+        //    閫�娆捐褰曞湪閫�娆捐皟鐢ㄥ墠钀藉簱锛岄伩鍏嶉��娆炬垚鍔熶絾鏈湴寮傚父瀵艰嚧鏃犺褰�
+        if (order.getRefundAmount() != null && order.getRefundAmount() > 0
+                && StringUtils.isNotBlank(order.getOutTradeNo())
+                && order.getPayAmount() != null && order.getPayAmount() > 0) {
+            OrdersRefund refundRecord = new OrdersRefund();
+            refundRecord.setOrderId(orderId);
+            refundRecord.setType(3); // 鍑哄簱閫�娆�
+            refundRecord.setCreateTime(now);
+            refundRecord.setRefundRemark(remark);
+            refundRecord.setDeleted(Constants.ZERO);
+            ordersRefundMapper.insert(refundRecord);
+
+            // 璋冪敤寰俊閫�娆撅紙鏀惧湪鏈�鍚庯紝纭繚鍓嶇疆鎿嶄綔鍏ㄩ儴鎴愬姛锛�
+            String refundCode = wxMiniUtilService.wxRefund(
+                    order.getOutTradeNo(), order.getPayAmount(), order.getRefundAmount());
+
+            // 閫�娆炬垚鍔熷悗鍥炲~閫�娆惧崟鍙峰拰鏃堕棿
+            refundRecord.setRefundCode(refundCode);
+            refundRecord.setRefundTime(new Date());
+            ordersRefundMapper.updateById(refundRecord);
+        }
+
+        // 11. 鐢熸垚鏀剁泭璁板綍
+        calculateAndSaveOrderFees(orderId);
+        generateRevenueRecords(orderId);
+
+        // 12. 璁板綍璁㈠崟鏃ュ織
+        String logInfo = "闂ㄥ簵銆�" + shopName + "銆戠‘璁ゅ嚭搴擄紝璁㈠崟瀹屾垚";
+        if (order.getRefundAmount() != null && order.getRefundAmount() > 0) {
+            logInfo += "锛岄��娆�" + Constants.getFormatMoney(order.getRefundAmount()) + "鍏�";
+        }
+        saveShopVerifyLog(order, "闂ㄥ簵纭鍑哄簱", logInfo, remark, shopId);
+    }
+
+    @Override
+    public void calculateAndSaveOrderFees(Integer orderId) {
+        Orders order = ordersMapper.selectById(orderId);
+        if (order == null || Constants.equalsInteger(order.getDeleted(), Constants.ONE)) {
+            throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "璁㈠崟涓嶅瓨鍦�");
+        }
+
+        Long totalAmount = order.getTotalAmount() != null ? order.getTotalAmount() : 0L;
+        // 璐圭巼锛堜负绌烘椂榛樿0锛�
+        BigDecimal depositRate = order.getDepositShopFeeRata() != null ? order.getDepositShopFeeRata() : BigDecimal.ZERO;
+        BigDecimal takeRate = order.getTakeShopFeeRata() != null ? order.getTakeShopFeeRata() : BigDecimal.ZERO;
+        BigDecimal driverRate = order.getDriverFeeRata() != null ? order.getDriverFeeRata() : BigDecimal.ZERO;
+        Long exceptionFeeVal = order.getExceptionFee() != null ? order.getExceptionFee() : 0L;
+
+        //瀛樹欢闂ㄥ簵鏀剁泭
+        Long depositShopFee = new BigDecimal(totalAmount)
+                .multiply(depositRate)
+                .setScale(0, RoundingMode.HALF_UP)
+                .longValue();
+
+        Long takeShopFee = 0L;
+        Long driverFee = 0L;
+
+        if (Constants.equalsInteger(order.getType(), Constants.TWO)) {
+            // 寮傚湴瀵勫瓨锛氬瓨浠堕棬搴� + 鍙告満
+            driverFee = new BigDecimal(totalAmount)
+                    .multiply(driverRate)
+                    .setScale(0, RoundingMode.HALF_UP)
+                    .longValue()
+                    + exceptionFeeVal;
+
+            // 寮傚湴瀵勫瓨涓旀湁鍙栦欢闂ㄥ簵锛氬姞涓婂彇浠堕棬搴楁敹鐩�
+            if (order.getTakeShopId() != null) {
+                takeShopFee = new BigDecimal(totalAmount)
+                        .multiply(takeRate)
+                        .setScale(0, RoundingMode.HALF_UP)
+                        .longValue();
+            }
+        }
+
+        ordersMapper.update(new UpdateWrapper<Orders>().lambda()
+                .eq(Orders::getId, orderId)
+                .set(Orders::getDepositShopFee, depositShopFee)
+                .set(Orders::getTakeShopFee, takeShopFee)
+                .set(Orders::getDriverFee, driverFee)
+                .set(Orders::getUpdateTime, new Date()));
+    }
+
+    /**
+     * 鐢熸垚闂ㄥ簵/鍙告満鏀剁泭璁板綍锛堟湭缁撶畻锛�
+     * 璁㈠崟瀹屾垚鏃惰皟鐢紝璇诲彇璁㈠崟涓婂凡璁$畻濂界殑璐圭敤瀛楁
+     */
+    private void generateRevenueRecords(Integer orderId) {
+        Orders order = ordersMapper.selectById(orderId);
+        if (order == null) {
+            return;
+        }
+        Date now = new Date();
+        Long depositShopFee = order.getDepositShopFee() != null ? order.getDepositShopFee() : 0L;
+        Long takeShopFee = order.getTakeShopFee() != null ? order.getTakeShopFee() : 0L;
+        Long driverFee = order.getDriverFee() != null ? order.getDriverFee() : 0L;
+
+        // 瀛樹欢闂ㄥ簵鏀剁泭
+        if (depositShopFee > 0 && order.getDepositShopId() != null) {
+            ShopInfo depositShop = shopInfoMapper.selectById(order.getDepositShopId());
+            if (depositShop != null && depositShop.getRegionMemberId() != null) {
+                revenueMapper.insert(buildRevenue(depositShop.getRegionMemberId(), Constants.TWO,
+                        depositShopFee, orderId, order.getCode()));
+            }
+        }
+
+        // 鍙栦欢闂ㄥ簵鏀剁泭锛堝紓鍦板瘎瀛樹笖鏈夊彇浠堕棬搴楋級
+        if (takeShopFee > 0 && order.getTakeShopId() != null) {
+            ShopInfo takeShop = shopInfoMapper.selectById(order.getTakeShopId());
+            if (takeShop != null && takeShop.getRegionMemberId() != null) {
+                revenueMapper.insert(buildRevenue(takeShop.getRegionMemberId(), Constants.TWO,
+                        takeShopFee, orderId, order.getCode()));
+            }
+        }
+
+        // 鍙告満鏀剁泭锛堝紓鍦板瘎瀛橈級
+        if (driverFee > 0 && order.getAcceptDriver() != null) {
+            DriverInfo driver = driverInfoMapper.selectById(order.getAcceptDriver());
+            if (driver != null && driver.getMemberId() != null) {
+                revenueMapper.insert(buildRevenue(driver.getMemberId(), Constants.ONE,
+                        driverFee, orderId, order.getCode()));
+            }
+        }
+    }
+
+    /**
+     * 鏋勫缓鏀剁泭璁板綍
+     */
+    private Revenue buildRevenue(Integer memberId, Integer memberType, Long amount, Integer orderId, String orderNo) {
+        Revenue revenue = new Revenue();
+        revenue.setMemberId(memberId);
+        revenue.setMemberType(memberType); // 1=鍙告満, 2=闂ㄥ簵
+        revenue.setType(Constants.ZERO); // 0=瀹屾垚璁㈠崟
+        revenue.setOptType(Constants.ONE); // 1=鏀跺叆
+        revenue.setAmount(amount);
+        revenue.setVaildStatus(Constants.ZERO); // 0=鍏ヨ处涓紙鏈粨绠楋級
+        revenue.setObjId(orderId);
+        revenue.setObjType(Constants.ZERO); // 0=璁㈠崟涓氬姟
+        revenue.setStatus(Constants.ZERO); // 0=鎴愬姛
+        revenue.setOrderNo(orderNo);
+        revenue.setDeleted(Constants.ZERO);
+        revenue.setCreateTime(new Date());
+        return revenue;
+    }
+
+    @Override
+    @Transactional(rollbackFor = {Exception.class, BusinessException.class})
+    public void driverVerifyOrder(String verifyCode, List<String> images, String remark, Integer driverId) {
+        if (StringUtils.isBlank(verifyCode)) {
+            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "鏍搁攢鐮佷笉鑳戒负绌�");
+        }
+        // 鏍规嵁鍙告満鏍搁攢鐮佹煡鎵捐鍗�
+        Orders order = ordersMapper.selectOne(new QueryWrapper<Orders>().lambda()
+                .eq(Orders::getDriverVerifyCode, verifyCode)
+                .eq(Orders::getDeleted, Constants.ZERO)
+                .last("limit 1"));
+        if (order == null) {
+            throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "鏍搁攢鐮佹棤鏁�");
+        }
+
+        // 浠呭紓鍦板瘎瀛� + 鏈夊彇浠堕棬搴� + 娲鹃�佷腑(4) 鍙牳閿�
+        if (!Constants.equalsInteger(order.getType(), Constants.ONE)) {
+            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "浠呭紓鍦板瘎瀛樿鍗曟敮鎸佸徃鏈烘牳閿�");
+        }
+        if (order.getTakeShopId() == null) {
+            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "璇ヨ鍗曟棤鍙栦欢闂ㄥ簵锛屾棤闇�鍙告満鏍搁攢");
+        }
+        if (!Constants.equalsInteger(order.getStatus(), Constants.OrderStatus.delivering.getStatus())) {
+            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "褰撳墠璁㈠崟鐘舵�佷笉鍏佽鏍搁攢");
+        }
+
+        // 娲鹃�佷腑(4) 鈫� 宸插埌搴�(5)
+        order.setStatus(Constants.OrderStatus.arrived.getStatus());
+        order.setArriveTime(new Date());
+        if (StringUtils.isNotBlank(remark)) {
+            order.setRemark(remark);
+        }
+        ordersMapper.updateById(order);
+
+        // 閲婃斁鍙告満鏍搁攢鐮�
+        releaseVerifyCode(verifyCode);
+
+        // 淇濆瓨闄勪欢锛坥bj_type=3 闂ㄥ簵鍏ュ簱鍥剧墖锛屾渶澶�3寮狅級
+        saveVerifyImages(order.getId(), images, Constants.FileType.ORDER_TAKE.getKey(), driverId);
+    }
+
+    /**
+     * 淇濆瓨鏍搁攢闄勪欢鍒� multifile 琛�
+     *
+     * @param orderId  璁㈠崟涓婚敭
+     * @param images   鍥剧墖鍦板潃鍒楄〃锛堟渶澶�3寮狅級
+     * @param objType  闄勪欢绫诲瀷
+     * @param creator  鍒涘缓浜虹紪鐮�
+     */
+    private void saveVerifyImages(Integer orderId, List<String> images, int objType, Integer creator) {
+        if (images == null || images.isEmpty()) return;
+        List<String> saveImages = images.size() > 3 ? images.subList(0, 3) : images;
+        Date now = new Date();
+        int sortNum = 1;
+        for (String imgUrl : saveImages) {
+            if (StringUtils.isBlank(imgUrl)) continue;
+            Multifile multifile = new Multifile();
+            multifile.setObjId(orderId);
+            multifile.setObjType(objType);
+            multifile.setFileurl(imgUrl);
+            multifile.setType(Constants.ZERO);
+            multifile.setCreator(creator);
+            multifile.setCreateDate(now);
+            multifile.setIsdeleted(Constants.ZERO);
+            multifile.setSortnum(sortNum++);
+            multifileMapper.insert(multifile);
+        }
+    }
+
+    @Override
+    @Transactional(rollbackFor = {Exception.class, BusinessException.class})
+    public void confirmCustomerArrived(Integer orderId, Integer shopId) {
+        // 1. 鏌ヨ璁㈠崟
+        Orders order = ordersMapper.selectById(orderId);
+        if (order == null || Constants.equalsInteger(order.getDeleted(), Constants.ONE)) {
+            throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "璁㈠崟涓嶅瓨鍦�");
+        }
+
+        // 2. 鏍¢獙璁㈠崟鐘舵�侊細寰呭彇浠�(5)
+        if (!Constants.equalsInteger(order.getStatus(), Constants.OrderStatus.arrived.getStatus())) {
+            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "褰撳墠璁㈠崟鐘舵�佷笉鍏佽璇ユ搷浣�");
+        }
+
+        // 3. 鏍¢獙闂ㄥ簵涓庤鍗曞叧绯�
+        if (Constants.equalsInteger(order.getType(), Constants.ONE) && order.getTakeShopId() != null) {
+            // 寮傚湴瀵勫瓨鏈夊彇浠堕棬搴楋細鏍¢獙鍙栦欢闂ㄥ簵
+            if (!shopId.equals(order.getTakeShopId())) {
+                throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "璇ヨ鍗曚笉灞炰簬褰撳墠闂ㄥ簵");
+            }
+        } else if (Constants.equalsInteger(order.getType(), Constants.ZERO)) {
+            // 灏卞湴瀵勫瓨锛氭牎楠屽瓨浠堕棬搴楋紙鍙栦欢闂ㄥ簵鍚屽瓨浠堕棬搴楋級
+            if (!shopId.equals(order.getDepositShopId())) {
+                throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "璇ヨ鍗曚笉灞炰簬褰撳墠闂ㄥ簵");
+            }
+        } else {
+            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "璇ヨ鍗曟棤鍙栦欢闂ㄥ簵锛屾棤娉曠‘璁ゅ埌搴�");
+        }
+
+        // 4. 鏌ヨ闂ㄥ簵鍚嶇О锛堢敤浜庢棩蹇楋級
+        String shopName = "";
+        ShopInfo shopInfo = shopInfoMapper.selectById(shopId);
+        if (shopInfo != null) {
+            shopName = shopInfo.getName() != null ? shopInfo.getName() : "";
+        }
+
+        // 5. 璁$畻閫炬湡璐圭敤
+        List<OrdersDetail> details = ordersDetailMapper.selectList(
+                new QueryWrapper<OrdersDetail>().lambda()
+                        .eq(OrdersDetail::getOrderId, orderId)
+                        .eq(OrdersDetail::getDeleted, Constants.ZERO));
+        OverdueFeeVO overdueInfo = calculateOverdueFeeInternal(order, details);
+
+        Date now = new Date();
+
+        if (overdueInfo.getOverdue() && overdueInfo.getOverdueDays() > 0) {
+            // 瀛樺湪閫炬湡锛氭爣璁伴�炬湡鐘舵�侊紝璁㈠崟淇濇寔褰撳墠鐘舵��
+            order.setConfirmArriveTime(now);
+            order.setOverdueStatus(Constants.ONE);
+            order.setOverdueDays(overdueInfo.getOverdueDays());
+            order.setOverdueAmount(overdueInfo.getOverdueFee());
+            order.setUpdateTime(now);
+            ordersMapper.updateById(order);
+
+            // 璁板綍璁㈠崟鏃ュ織
+            saveShopVerifyLog(order, "纭椤惧鍒板簵锛堥�炬湡锛�",
+                    "闂ㄥ簵銆�" + shopName + "銆戠‘璁ら【瀹㈠埌搴楋紝閫炬湡" + overdueInfo.getOverdueDays()
+                            + "澶╋紝閫炬湡璐圭敤" + Constants.getFormatMoney(overdueInfo.getOverdueFee()) + "鍏�",
+                    null, shopId);
+        } else {
+            // 鏈�炬湡锛氭爣璁伴�炬湡鐘舵�佷负0锛岃鍗曚繚鎸佸綋鍓嶇姸鎬�
+            order.setConfirmArriveTime(now);
+            order.setOverdueStatus(Constants.ZERO);
+
+            // 灏卞湴瀵勫瓨锛氳绠楁槸鍚﹂渶瑕侀��娆�
+            if (Constants.equalsInteger(order.getType(), Constants.ZERO) && !CollectionUtils.isEmpty(details)) {
+                int actualDays = calcActualDepositDays(now, order.getDepositTime());
+                order.setDepositDays(actualDays);
+
+                int estimatedDays = order.getEstimatedDepositDays() != null ? order.getEstimatedDepositDays() : 1;
+                int refundDays = estimatedDays - actualDays;
+                if (refundDays > 0) {
+                    // 閫�娆鹃噾棰� = 閫�娆惧ぉ鏁� 脳 危(鐗╁搧鍗曚环 脳 鏁伴噺)
+                    long dailyBaseFee = 0L;
+                    for (OrdersDetail d : details) {
+                        dailyBaseFee += (d.getUnitPrice() != null ? d.getUnitPrice() : 0L)
+                                * (d.getNum() != null ? d.getNum() : 0);
+                    }
+                    long refundAmount = (long) refundDays * dailyBaseFee;
+                    order.setRefundAmount(refundAmount);
+                }
+            }
+
+            order.setUpdateTime(now);
+            ordersMapper.updateById(order);
+
+            // 閫�娆惧鑷存�婚噾棰濆彉鍖栵紝閲嶇畻涓夋柟鏀剁泭
+            if (order.getRefundAmount() != null && order.getRefundAmount() > 0) {
+                long newTotal = (order.getTotalAmount() != null ? order.getTotalAmount() : 0L) - order.getRefundAmount();
+                order.setTotalAmount(newTotal);
+                ordersMapper.update(new UpdateWrapper<Orders>().lambda()
+                        .eq(Orders::getId, orderId)
+                        .set(Orders::getTotalAmount, newTotal));
+                calculateAndSaveOrderFees(orderId);
+            }
+
+            // 璁板綍璁㈠崟鏃ュ織
+            String logInfo = "闂ㄥ簵銆�" + shopName + "銆戠‘璁ら【瀹㈠埌搴楋紝鏈�炬湡";
+            if (order.getRefundAmount() != null && order.getRefundAmount() > 0) {
+                logInfo += "锛岄渶閫�娆�" + Constants.getFormatMoney(order.getRefundAmount()) + "鍏�";
+            }
+            saveShopVerifyLog(order, "纭椤惧鍒板簵", logInfo, null, shopId);
+        }
+    }
+
+    /**
+     * 鏋勫缓璁㈠崟鐘舵�佹弿杩�
+     */
+    private String buildStatusDesc(Orders order) {
+        boolean isLocal = Constants.equalsInteger(order.getType(), Constants.ZERO);
+        Integer status = order.getStatus();
+        if (status == null) return "";
+
+        if (Constants.equalsInteger(status, Constants.OrderStatus.waitPay.getStatus())) {
+            String minutes = "";
+            try {
+                minutes = operationConfigBiz.getConfig().getAutoCancelTime();
+            } catch (Exception ignored) {}
+            return "璇峰湪" + (StringUtils.isNotBlank(minutes) ? minutes : "") + "鍒嗛挓鍐呭畬鎴愭敮浠橈紝瓒呮椂璁㈠崟灏嗚嚜鍔ㄥ彇娑�";
+        }
+        if (Constants.equalsInteger(status, Constants.OrderStatus.waitDeposit.getStatus())) {
+            return isLocal ? "璁㈠崟宸叉敮浠橈紝璇风瓑寰呴棬搴楃‘璁ゆ帴鍗�" : "璁㈠崟宸叉敮浠橈紝璇风瓑寰呴棬搴楃‘璁ゆ帴鍗�";
+        }
+        if (Constants.equalsInteger(status, Constants.OrderStatus.deposited.getStatus())) {
+            return isLocal ? "琛屾潕宸插瘎瀛橈紝璇峰嚟鍙栦欢鐮佸墠寰�鎸囧畾闂ㄥ簵鍙栦欢" : "闂ㄥ簵宸叉帴鍗曪紝姝e湪涓烘偍瀹夋帓鍙栦欢鍙告満";
+        }
+        if (Constants.equalsInteger(status, Constants.OrderStatus.accepted.getStatus())) {
+            return isLocal ? "琛屾潕宸插瘎瀛橈紝璇峰嚟鍙栦欢鐮佸墠寰�鎸囧畾闂ㄥ簵鍙栦欢" : "宸叉湁鍙告満鎶㈠崟锛屾鍓嶅線鍙栦欢鍦扮偣";
+        }
+        if (Constants.equalsInteger(status, Constants.OrderStatus.delivering.getStatus())) {
+            return "鍙告満宸插彇浠讹紝姝h繍寰�鐩殑鍦�";
+        }
+        if (Constants.equalsInteger(status, Constants.OrderStatus.arrived.getStatus())) {
+            return "琛屾潕宸查�佽揪鏈嶅姟鐐癸紝璇峰強鏃跺墠寰�鍙栦欢";
+        }
+        if (Constants.equalsInteger(status, Constants.OrderStatus.finished.getStatus())) {
+            if (Constants.equalsInteger(order.getCommentStatus(), Constants.ONE)) {
+                return "鎰熻阿鎮ㄧ殑鐢ㄥ績璇勪环锛岀鎮ㄥ嚭琛岄『鍒╋紝鏃呴�旀剦蹇紒";
+            }
+            return "璁㈠崟宸插畬鎴愶紝鎰熻阿鎮ㄧ殑鏀寔锛岃瀵规湰娆℃湇鍔″仛鍑鸿瘎浠�";
+        }
+        if (Constants.equalsInteger(status, Constants.OrderStatus.cancelled.getStatus())) {
+            return "璁㈠崟宸插彇娑堬紝鎰熻阿鎮ㄧ殑鏀寔锛屾杩庝笅娆″啀浼氾紒";
+        }
+        if (Constants.equalsInteger(status, Constants.OrderStatus.cancelling.getStatus())) {
+            return "閫�娆剧敵璇峰凡鎻愪氦锛屽钩鍙颁細灏藉揩涓烘偍澶勭悊閫�娆�";
+        }
+        if (Constants.equalsInteger(status, Constants.OrderStatus.closed.getStatus())) {
+            return "閫�娆惧凡鎴愬姛鍘熻矾杩斿洖锛岃娉ㄦ剰鏌ユ敹";
+        }
+        return "";
+    }
+
+    /**
+     * 璁$畻鏀粯鍊掕鏃舵绉�
+     */
+    private Long calcPayCountdownMs(Orders order) {
+        try {
+            String minutesStr = operationConfigBiz.getConfig().getAutoCancelTime();
+            if (StringUtils.isBlank(minutesStr)) return -1L;
+            int minutes = Integer.parseInt(minutesStr);
+            long deadline = order.getCreateTime().getTime() + minutes * 60 * 1000L;
+            long remaining = deadline - System.currentTimeMillis();
+            return remaining > 0 ? remaining : -1L;
+        } catch (Exception e) {
+            return -1L;
+        }
+    }
+    public OverdueFeeVO calculateOverdueFee(Integer orderId) {
+        Orders order = ordersMapper.selectById(orderId);
+        if (order == null || Constants.equalsInteger(order.getDeleted(), Constants.ONE)) {
+            throw new BusinessException(ResponseStatus.DATA_EMPTY);
+        }
+
+        // 鏌ヨ璁㈠崟鏄庣粏
+        List<OrdersDetail> details = ordersDetailMapper.selectList(
+                new QueryWrapper<OrdersDetail>().lambda()
+                        .eq(OrdersDetail::getOrderId, orderId)
+                        .eq(OrdersDetail::getDeleted, Constants.ZERO));
+
+        return calculateOverdueFeeInternal(order, details);
+    }
+
+    /**
+     * 閫炬湡璐圭敤鍐呴儴璁$畻锛堜笉鏌ュ簱锛屾帴鍙楅鏌ヨ鐨勬暟鎹級
+     * 渚涘垎椤电瓑宸叉煡璇㈡槑缁嗙殑涓氬姟鍦烘櫙澶嶇敤锛岄伩鍏嶉噸澶嶆煡璇�
+     */
+    private OverdueFeeVO calculateOverdueFeeInternal(Orders order, List<OrdersDetail> details) {
+        if (CollectionUtils.isEmpty(details)) {
+            OverdueFeeVO vo = new OverdueFeeVO();
+            vo.setOverdue(false);
+            vo.setOverdueDays(0);
+            vo.setOverdueFee(0L);
+            vo.setDailyBaseFee(0L);
+            return vo;
+        }
+
+        // 鐗╁搧鍩虹鏃ヨ垂鐢� = 危(鍗曚环 脳 鏁伴噺)
+        long dailyBaseFee = 0L;
+        for (OrdersDetail d : details) {
+            dailyBaseFee += (d.getUnitPrice() != null ? d.getUnitPrice() : 0L)
+                    * (d.getNum() != null ? d.getNum() : 0);
+        }
+
+        Date now = new Date();
+        int overdueDays;
+        long overdueFee;
+
+        if (Constants.equalsInteger(order.getType(), Constants.ZERO)) {
+            // ========== 灏卞湴瀵勫瓨 ==========
+            overdueDays = calcLocalOverdueDays(now, order.getExpectedTakeTime());
+            overdueFee = (long) overdueDays * dailyBaseFee;
+
+            OverdueFeeVO vo = new OverdueFeeVO();
+            vo.setOverdue(overdueDays > 0);
+            vo.setOverdueDays(overdueDays);
+            vo.setOverdueFee(overdueFee);
+            vo.setDailyBaseFee(dailyBaseFee);
+            return vo;
+
+        } else {
+            // ========== 寮傚湴瀵勫瓨 ==========
+            // 鏉′欢锛氬瓨鍦ㄥ彇浠堕棬搴� 涓� 璁㈠崟澶勪簬宸插埌搴楃姸鎬�(5)
+            if (order.getTakeShopId() == null
+                    || !Constants.equalsInteger(order.getStatus(), Constants.FIVE)) {
+                OverdueFeeVO vo = new OverdueFeeVO();
+                vo.setOverdue(false);
+                vo.setOverdueDays(0);
+                vo.setOverdueFee(0L);
+                vo.setDailyBaseFee(dailyBaseFee);
+                vo.setDiscountRate(null);
+                return vo;
+            }
+
+            overdueDays = calcRemoteOverdueDays(now, order.getArriveTime());
+
+            // 鎶樻墸姣旂巼
+            String discountStr = operationConfigBiz.getConfig().getUnpickedDiscount();
+            BigDecimal discountRate = StringUtils.isNotBlank(discountStr)
+                    ? new BigDecimal(discountStr) : BigDecimal.ONE;
+
+            overdueFee = new BigDecimal(overdueDays)
+                    .multiply(new BigDecimal(dailyBaseFee))
+                    .multiply(discountRate)
+                    .setScale(0, RoundingMode.HALF_UP)
+                    .longValue();
+
+            OverdueFeeVO vo = new OverdueFeeVO();
+            vo.setOverdue(overdueDays > 0);
+            vo.setOverdueDays(overdueDays);
+            vo.setOverdueFee(overdueFee);
+            vo.setDailyBaseFee(dailyBaseFee);
+            vo.setDiscountRate(discountStr);
+            return vo;
+        }
+    }
+
+    /**
+     * 璁$畻瀹為檯瀵勫瓨澶╂暟锛坉epositTime 鍒� now 鐨勫ぉ鏁板樊锛屾渶灏�1澶╋級
+     */
+    private int calcActualDepositDays(Date now, Date depositTime) {
+        if (depositTime == null || now == null) {
+            return 1;
+        }
+        Calendar depositCal = Calendar.getInstance();
+        depositCal.setTime(depositTime);
+        depositCal.set(Calendar.HOUR_OF_DAY, 0);
+        depositCal.set(Calendar.MINUTE, 0);
+        depositCal.set(Calendar.SECOND, 0);
+        depositCal.set(Calendar.MILLISECOND, 0);
+
+        Calendar nowCal = Calendar.getInstance();
+        nowCal.setTime(now);
+        nowCal.set(Calendar.HOUR_OF_DAY, 0);
+        nowCal.set(Calendar.MINUTE, 0);
+        nowCal.set(Calendar.SECOND, 0);
+        nowCal.set(Calendar.MILLISECOND, 0);
+
+        long diffMs = nowCal.getTimeInMillis() - depositCal.getTimeInMillis();
+        int days = (int) (diffMs / (1000 * 60 * 60 * 24));
+        return Math.max(days, 1);
+    }
+
+    /**
+     * 灏卞湴瀵勫瓨閫炬湡澶╂暟璁$畻
+     * 杩囦簡棰勮鍙栦欢鏃堕棿褰撳ぉ鐨�12鐐瑰悗鎵嶇畻涓�澶�
+     */
+    private int calcLocalOverdueDays(Date now, Date expectedTakeTime) {
+        if (expectedTakeTime == null || !now.after(expectedTakeTime)) {
+            return 0;
+        }
+        // 鍩哄噯鏃堕棿 = 棰勮鍙栦欢鏃ユ湡鐨�12:00
+        Calendar baseCal = Calendar.getInstance();
+        baseCal.setTime(expectedTakeTime);
+        baseCal.set(Calendar.HOUR_OF_DAY, 12);
+        baseCal.set(Calendar.MINUTE, 0);
+        baseCal.set(Calendar.SECOND, 0);
+        baseCal.set(Calendar.MILLISECOND, 0);
+        Date baseTime = baseCal.getTime();
+
+        if (!now.after(baseTime)) {
+            return 0;
+        }
+        // 閫炬湡澶╂暟 = 褰撳墠鏃ユ湡 - 鍩哄噯鏃ユ湡锛堟寜澶╁彇宸級
+        Calendar nowCal = Calendar.getInstance();
+        nowCal.setTime(now);
+        nowCal.set(Calendar.HOUR_OF_DAY, 0);
+        nowCal.set(Calendar.MINUTE, 0);
+        nowCal.set(Calendar.SECOND, 0);
+        nowCal.set(Calendar.MILLISECOND, 0);
+
+        Calendar baseDateCal = Calendar.getInstance();
+        baseDateCal.setTime(baseTime);
+        baseDateCal.set(Calendar.HOUR_OF_DAY, 0);
+        baseDateCal.set(Calendar.MINUTE, 0);
+        baseDateCal.set(Calendar.SECOND, 0);
+        baseDateCal.set(Calendar.MILLISECOND, 0);
+
+        long diffMs = nowCal.getTimeInMillis() - baseDateCal.getTimeInMillis();
+        int days = (int) (diffMs / (1000 * 60 * 60 * 24));
+        return Math.max(days, 0);
+    }
+
+    /**
+     * 寮傚湴瀵勫瓨閫炬湡澶╂暟璁$畻
+     * 杩囦簡杞Щ鍒板簵鏃堕棿褰撳ぉ鐨勬櫄涓�12鐐癸紙24:00锛夊悗鎵嶇畻绗竴澶�
+     */
+    private int calcRemoteOverdueDays(Date now, Date arriveTime) {
+        if (arriveTime == null || !now.after(arriveTime)) {
+            return 0;
+        }
+        // 鍩哄噯鏃堕棿 = 杞Щ鍒板簵鏃ユ湡鐨勬鏃� 00:00锛堝嵆褰撳ぉ24:00锛�
+        Calendar baseCal = Calendar.getInstance();
+        baseCal.setTime(arriveTime);
+        baseCal.set(Calendar.HOUR_OF_DAY, 0);
+        baseCal.set(Calendar.MINUTE, 0);
+        baseCal.set(Calendar.SECOND, 0);
+        baseCal.set(Calendar.MILLISECOND, 0);
+        baseCal.add(Calendar.DAY_OF_MONTH, 1); // 娆℃棩00:00 = 褰撳ぉ24:00
+        Date baseTime = baseCal.getTime();
+
+        if (!now.after(baseTime)) {
+            return 0;
+        }
+        // 閫炬湡澶╂暟 = 褰撳墠鏃ユ湡 - 鍩哄噯鏃ユ湡
+        Calendar nowCal = Calendar.getInstance();
+        nowCal.setTime(now);
+        nowCal.set(Calendar.HOUR_OF_DAY, 0);
+        nowCal.set(Calendar.MINUTE, 0);
+        nowCal.set(Calendar.SECOND, 0);
+        nowCal.set(Calendar.MILLISECOND, 0);
+
+        Calendar baseDateCal = Calendar.getInstance();
+        baseDateCal.setTime(baseTime);
+        baseDateCal.set(Calendar.HOUR_OF_DAY, 0);
+        baseDateCal.set(Calendar.MINUTE, 0);
+        baseDateCal.set(Calendar.SECOND, 0);
+        baseDateCal.set(Calendar.MILLISECOND, 0);
+
+        long diffMs = nowCal.getTimeInMillis() - baseDateCal.getTimeInMillis();
+        int days = (int) (diffMs / (1000 * 60 * 60 * 24));
+        return Math.max(days, 0);
+    }
+
 }

--
Gitblit v1.9.3