rk
3 小时以前 552238172036acf08ccf36134282a06b5e21b936
代码生成
已修改31个文件
579 ■■■■ 文件已修改
server/admin/src/main/java/com/doumee/api/business/OrdersController.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/admin/src/main/java/com/doumee/api/business/PricingRuleController.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/admin/src/main/resources/application.yml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/db/db_change.sql 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/config/alipay/AlipayFundTransUniTransfer.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/core/utils/GeoUtils.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/core/utils/geocode/MapUtil.java 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/business/model/Orders.java 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/business/model/OrdersDetail.java 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/business/model/PricingRule.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/dto/CalculateLocalPriceDTO.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/dto/CalculateRemotePriceDTO.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/dto/LocalStoragePricingItemDTO.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/dto/LocalStoragePricingSaveDTO.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/dto/OrdersRefundPageDTO.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/vo/ItemPriceVO.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/vo/LocalStoragePricingVO.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/vo/MyOrderVO.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/vo/OrderDispatchVO.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/vo/OrdersExportVO.java 41 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/vo/OrdersRefundPageVO.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/AreasService.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/OrdersService.java 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/PricingRuleService.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/impl/AreasServiceImpl.java 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/impl/DriverInfoServiceImpl.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/impl/InvoiceRecordServiceImpl.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/impl/OrdersServiceImpl.java 223 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/impl/ShopInfoServiceImpl.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/impl/WithdrawalOrdersServiceImpl.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/web/src/main/java/com/doumee/api/web/ConfigApi.java 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/admin/src/main/java/com/doumee/api/business/OrdersController.java
@@ -94,9 +94,10 @@
        for (Orders o : records) {
            OrdersExportVO vo = new OrdersExportVO();
            vo.setCode(o.getCode());
            vo.setRelationOrderCode(o.getRelationOrderCode());
            vo.setGoodsInfo(o.getGoodsInfo());
            vo.setTypeName(o.getType() != null ? (o.getType() == 0 ? "就地存取" : "异地存取") : "");
            vo.setOrderLevel(Constants.getDriverLevelName(Objects.nonNull(o.getOrderLevel())?Integer.valueOf(o.getOrderLevel()):Constants.ZERO));
            vo.setTypeName(o.getType() != null ? (o.getType() == 0 ? "就地寄存" : "同城寄送") : "");
            vo.setOrderLevel(StringUtils.isNotBlank(o.getOrderLevel()) ? Constants.getDriverLevelName(Integer.valueOf(o.getOrderLevel())) : Constants.getDriverLevelName(Constants.ZERO));
            vo.setDeclaredFee(String.valueOf(Constants.getFormatMoney(o.getDeclaredFee())));
            vo.setBasicAmount(String.valueOf(Constants.getFormatMoney(o.getBasicAmount())));
            vo.setTotalAmount(String.valueOf(Constants.getFormatMoney(o.getTotalAmount())));
@@ -106,8 +107,8 @@
            vo.setOverdueAmount(String.valueOf(Constants.getFormatMoney(o.getOverdueAmount())));
            vo.setExceptionAmount(String.valueOf(Constants.getFormatMoney(o.getExceptionAmount())));
            vo.setDeductionAmount(String.valueOf(Constants.getFormatMoney(o.getDeductionAmount())));
            vo.setShopCompensationAmount(String.valueOf(Constants.getFormatMoney(o.getShopCompensationAmount())));
            vo.setExceptionFee(String.valueOf(Constants.getFormatMoney(o.getExceptionFee())));
            vo.setShopCompensationAmount(String.valueOf(Constants.getFormatMoney(o.getShopCompensationAmount())));
            vo.setStatusDesc(o.getStatusDesc());
            vo.setSettlementDesc(o.getSettlementStatus() != null ? (o.getSettlementStatus() == 1 ? "已结算" : "待结算") : "");
            vo.setPayTime(o.getPayTime());
server/admin/src/main/java/com/doumee/api/business/PricingRuleController.java
@@ -44,7 +44,7 @@
    @PreventRepeat
    @ApiOperation("批量保存就地存取规则")
    @ApiOperation("批量保存就地寄存规则")
    @PostMapping("/localStorage/batchSave")
    @RequiresPermissions("business:pricingRule:create")
    public ApiResponse batchSaveLocalStoragePricing(@RequestBody @Validated LocalStoragePricingSaveDTO request) {
@@ -52,7 +52,7 @@
        return ApiResponse.success(null);
    }
    @ApiOperation("查询就地存取规则列表")
    @ApiOperation("查询就地寄存规则列表")
    @GetMapping("/localStorage/list")
    @RequiresPermissions("business:pricingRule:query")
    public ApiResponse<List<LocalStoragePricingVO>> listLocalStoragePricing(@RequestParam Integer cityId) {
server/admin/src/main/resources/application.yml
@@ -12,7 +12,7 @@
spring:
  profiles:
    active: pro
    active: dev
  # JSON返回配置
  jackson:
    # 默认时区
server/services/db/db_change.sql
@@ -418,10 +418,10 @@
-- ============================================================
-- 2026/04/15 订单物品明细表添加就地存取单价字段
-- 2026/04/15 订单物品明细表添加就地寄存单价字段
-- ============================================================
ALTER TABLE `orders_detail` ADD COLUMN `LOCALLY_PRICE` BIGINT DEFAULT NULL COMMENT '就地存取单价(分/天)' AFTER `EXTRA_PRICE`;
ALTER TABLE `orders_detail` ADD COLUMN `LOCALLY_PRICE` BIGINT DEFAULT NULL COMMENT '就地寄存单价(分/天)' AFTER `EXTRA_PRICE`;
-- ============================================================
server/services/src/main/java/com/doumee/config/alipay/AlipayFundTransUniTransfer.java
@@ -74,11 +74,10 @@
        AlipayFundTransUniTransferModel data = new AlipayFundTransUniTransferModel();
        // 设置商家侧唯一订单号
        data.setOutBizNo(dto.getOutBizNo());
        // 设置订单总金额
        data.setTransAmount("0.1");
        // 设置描述特定的业务场景
        data.setOutBizNo(dto.getOutBizNo());
//        data.setTransAmount(dto.getTransAmount().setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString());
        // 设置订单总金额
        data.setTransAmount(dto.getTransAmount().setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString());
        data.setBizScene("DIRECT_TRANSFER");
        // 设置业务产品码
        data.setProductCode("TRANS_ACCOUNT_NO_PWD");
server/services/src/main/java/com/doumee/core/utils/GeoUtils.java
@@ -29,10 +29,10 @@
    }
    public static void main(String[] args) {
        double lat1 = 30.660622; // 纽约纬度
        double lon1 = 114.172; // 纽约经度
        double lat2 = 30.621857; // 洛杉矶纬度
        double lon2 = 114.12311; // 洛杉矶经度
        double lat1 = 31.969801; // 纽约纬度
        double lon1 = 118.797030; // 纽约经度
        double lat2 = 31.969175; // 洛杉矶纬度
        double lon2 = 118.797440; // 洛杉矶经度
        double distance = haversineDistance(lat1, lon1, lat2, lon2);
        System.out.printf("Distance: %.2f km%n", distance);
server/services/src/main/java/com/doumee/core/utils/geocode/MapUtil.java
@@ -1,8 +1,10 @@
package com.doumee.core.utils.geocode;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.doumee.core.utils.Http;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@@ -24,6 +26,9 @@
    /** 逆地理解析 */
    private static final String GEO_URL = "https://restapi.amap.com/v3/geocode/regeo";
    /** 正向地理解析 */
    private static final String GEOCODE_URL = "https://restapi.amap.com/v3/geocode/geo";
    /** 驾车路径规划 */
    private static final String DRIVING_URL = "https://restapi.amap.com/v3/direction/driving";
@@ -36,6 +41,52 @@
    }
    /**
     * 正向地理解析 - 根据地址获取经纬度
     *
     * @param address 地址文本(如"四川省成都市")
     * @return "lat,lng" 格式的经纬度字符串,解析失败返回 null
     */
    public static String geocode(String address) {
        try {
            String url = GEOCODE_URL
                    + "?key=" + amapKey
                    + "&address=" + URLEncoder.encode(address, "UTF-8");
            log.info("高德地图正向地理解析请求: address={}", address);
            JSONObject json = new Http().build(url)
                    .setConnectTimeout(5000)
                    .setReadTimeout(10000)
                    .get()
                    .toJSONObject();
            log.info("高德地图正向地理解析响应: {}", json);
            if (!"1".equals(json.getString("status"))) {
                log.warn("高德地图正向地理解析失败: {}", json.getString("info"));
                return null;
            }
            JSONArray geocodes = json.getJSONArray("geocodes");
            if (geocodes == null || geocodes.isEmpty()) {
                log.warn("高德地图正向地理解析无结果: address={}", address);
                return null;
            }
            String location = geocodes.getJSONObject(0).getString("location"); // lng,lat
            if (StringUtils.isBlank(location)) {
                return null;
            }
            String[] parts = location.split(",");
            // 转为 lat,lng 格式
            return parts[1] + "," + parts[0];
        } catch (Exception e) {
            log.error("高德地图正向地理解析异常: address={}", address, e);
            return null;
        }
    }
    /**
     * 逆地理解析 - 根据经纬度获取地址信息
     * 高德坐标系为 lng,lat(与腾讯 lat,lng 相反)
     *
server/services/src/main/java/com/doumee/dao/business/model/Orders.java
@@ -57,8 +57,8 @@
    @ExcelColumn(name = "会员主键")
    private Integer memberId;
    @ApiModelProperty(value = "寄存方式:0=就地存取;1=异地存取", example = "0")
    @ExcelColumn(name = "寄存方式", valueMapping = "0=就地存取;1=异地存取;")
    @ApiModelProperty(value = "寄存方式:0=就地寄存;1=同城寄送", example = "0")
    @ExcelColumn(name = "寄存方式", valueMapping = "0=就地寄存;1=同城寄送;")
    private Integer type;
    @ApiModelProperty(value = "是否转换订单:0=否;1=是(异地转就地)", example = "0")
@@ -80,6 +80,10 @@
    @ApiModelProperty(value = "关联主订单")
    @ExcelColumn(name = "关联主订单")
    private Integer relationOrderId;
    @TableField(exist = false)
    @ApiModelProperty(value = "关联订单号")
    private String relationOrderCode;
    @ApiModelProperty(value = "异常原因")
    @ExcelColumn(name = "异常原因")
@@ -133,7 +137,7 @@
    @ApiModelProperty(value = "取件纬度")
    private BigDecimal takeLgt;
    @ApiModelProperty(value = "距离(异地存取使用)")
    @ApiModelProperty(value = "距离(同城寄送使用)")
    @ExcelColumn(name = "距离")
    private BigDecimal distance;
server/services/src/main/java/com/doumee/dao/business/model/OrdersDetail.java
@@ -73,24 +73,24 @@
    @ExcelColumn(name = "单价")
    private Long unitPrice;
    @ApiModelProperty(value = "异地存取 - 配送起步里程")
    @ApiModelProperty(value = "同城寄送 - 配送起步里程")
    @ExcelColumn(name = "配送起步里程")
    private BigDecimal startDistance;
    @ApiModelProperty(value = "异地存取 - 配送起步里程每公里单价(分)")
    @ApiModelProperty(value = "同城寄送 - 配送起步里程每公里单价(分)")
    @ExcelColumn(name = "起步里程单价")
    private Long startPrice;
    @ApiModelProperty(value = "异地存取 - 超出首单里程公里数")
    @ApiModelProperty(value = "同城寄送 - 超出首单里程公里数")
    @ExcelColumn(name = "超出首单里程")
    private BigDecimal extraDistance;
    @ApiModelProperty(value = "异地存取 - 超出首单里程单价(分)")
    @ApiModelProperty(value = "同城寄送 - 超出首单里程单价(分)")
    @ExcelColumn(name = "超出首单里程单价")
    private Long extraPrice;
    @ApiModelProperty(value = "就地存取 - 单价(分)")
    @ExcelColumn(name = "就地存取 - 单价")
    @ApiModelProperty(value = "就地寄存 - 单价(分)")
    @ExcelColumn(name = "就地寄存 - 单价")
    private Long locallyPrice;
}
server/services/src/main/java/com/doumee/dao/business/model/PricingRule.java
@@ -48,8 +48,8 @@
    @ApiModelProperty(value = "城市主键(area_id)", example = "1")
    private Integer cityId;
    @ApiModelProperty(value = "类型:0=就地存取规则;1=异地存取规则;2=预计时效;3=门店注册押金;4=分成比例", example = "0")
    @ExcelColumn(name = "类型", index = 2, width = 10, valueMapping = "0=就地存取规则;1=异地存取规则;2=预计失效;3=门店注册押金;4=分成比例;")
    @ApiModelProperty(value = "类型:0=就地寄存规则;1=同城寄送规则;2=预计时效;3=门店注册押金;4=分成比例", example = "0")
    @ExcelColumn(name = "类型", index = 2, width = 10, valueMapping = "0=就地寄存规则;1=同城寄送规则;2=预计失效;3=门店注册押金;4=分成比例;")
    private Integer type;
    @ApiModelProperty(value = "参数1:type (0/1)=  关联 物品尺寸(category type =4);type (2) (默认 1标速达;2=极速达) ; type (3)  = 企业类型(0=企业;1=个人);type (4) = 类型(企业/个人/配送员)")
server/services/src/main/java/com/doumee/dao/dto/CalculateLocalPriceDTO.java
@@ -12,13 +12,13 @@
import java.util.List;
/**
 * 就地存取预估费用请求DTO
 * 就地寄存预估费用请求DTO
 *
 * @Author : Rk
 * @create 2026/4/14
 */
@Data
@ApiModel("就地存取预估费用请求")
@ApiModel("就地寄存预估费用请求")
public class CalculateLocalPriceDTO {
    @ApiModelProperty(value = "城市主键", required = true)
server/services/src/main/java/com/doumee/dao/dto/CalculateRemotePriceDTO.java
@@ -11,13 +11,13 @@
import java.util.List;
/**
 * 异地存取预估费用请求DTO
 * 同城寄送预估费用请求DTO
 *
 * @Author : Rk
 * @create 2026/4/14
 */
@Data
@ApiModel("异地存取预估费用请求")
@ApiModel("同城寄送预估费用请求")
public class CalculateRemotePriceDTO {
    @ApiModelProperty(value = "城市主键", required = true)
server/services/src/main/java/com/doumee/dao/dto/LocalStoragePricingItemDTO.java
@@ -8,12 +8,12 @@
import java.io.Serializable;
/**
 * 就地存取规则项
 * 就地寄存规则项
 * @author rk
 * @date 2026/04/08
 */
@Data
@ApiModel("就地存取规则项")
@ApiModel("就地寄存规则项")
public class LocalStoragePricingItemDTO implements Serializable {
    @ApiModelProperty(value = "物品规格主键(category.id, type=4)", required = true, example = "1")
server/services/src/main/java/com/doumee/dao/dto/LocalStoragePricingSaveDTO.java
@@ -11,12 +11,12 @@
import java.util.List;
/**
 * 就地存取规则批量保存请求
 * 就地寄存规则批量保存请求
 * @author rk
 * @date 2026/04/08
 */
@Data
@ApiModel("就地存取规则批量保存请求")
@ApiModel("就地寄存规则批量保存请求")
public class LocalStoragePricingSaveDTO implements Serializable {
    @ApiModelProperty(value = "城市主键", required = true, example = "1")
server/services/src/main/java/com/doumee/dao/dto/OrdersRefundPageDTO.java
@@ -23,7 +23,7 @@
    @ApiModelProperty(value = "物品信息")
    private String goodsInfo;
    @ApiModelProperty(value = "订单类型:0=就地存取;1=异地存取")
    @ApiModelProperty(value = "订单类型:0=就地寄存;1=同城寄送")
    private Integer orderType;
    @ApiModelProperty(value = "退款状态:0=退款中;1=退款成功;2=退款失败")
server/services/src/main/java/com/doumee/dao/vo/ItemPriceVO.java
@@ -34,7 +34,7 @@
    @ApiModelProperty("小计(分)")
    private Long subtotal;
    // ========== 异地存取额外字段 ==========
    // ========== 同城寄送额外字段 ==========
    @ApiModelProperty("起步距离(km)")
    private BigDecimal startDistance;
@@ -48,6 +48,6 @@
    @ApiModelProperty("超出距离单价(分)")
    private Long extraPrice;
    @ApiModelProperty("就地存取单价(分/天)")
    @ApiModelProperty("就地寄存单价(分/天)")
    private Long locallyPrice;
}
server/services/src/main/java/com/doumee/dao/vo/LocalStoragePricingVO.java
@@ -7,12 +7,12 @@
import java.io.Serializable;
/**
 * 就地存取规则列表返回
 * 就地寄存规则列表返回
 * @author rk
 * @date 2026/04/08
 */
@Data
@ApiModel("就地存取规则项(列表返回)")
@ApiModel("就地寄存规则项(列表返回)")
public class LocalStoragePricingVO implements Serializable {
    @ApiModelProperty(value = "规则主键")
server/services/src/main/java/com/doumee/dao/vo/MyOrderVO.java
@@ -23,7 +23,7 @@
    @ApiModelProperty(value = "订单编号")
    private String code;
    @ApiModelProperty(value = "寄存方式:0=就地存取;1=异地存取")
    @ApiModelProperty(value = "寄存方式:0=就地寄存;1=同城寄送")
    private Integer type;
    @ApiModelProperty(value = "就地寄存状态:0=待支付;1=待寄存;2=已寄存;5=待取件;6=存在逾期;7=已完成;96:订单关闭(退款);97:取消逾期;98=取消中;99=已取消;" +
server/services/src/main/java/com/doumee/dao/vo/OrderDispatchVO.java
@@ -21,7 +21,7 @@
    @ApiModelProperty(value = "实付金额(元)")
    private Double payAmountYuan;
    @ApiModelProperty(value = "配送方式:0=就地存取;1=异地存取")
    @ApiModelProperty(value = "配送方式:0=就地寄存;1=同城寄送")
    private Integer type;
    @ApiModelProperty(value = "配送方式描述")
server/services/src/main/java/com/doumee/dao/vo/OrdersExportVO.java
@@ -11,57 +11,60 @@
    @ExcelColumn(name = "订单编号", index = 0)
    private String code;
    @ExcelColumn(name = "物品信息", index = 1)
    @ExcelColumn(name = "关联订单号", index = 1)
    private String relationOrderCode;
    @ExcelColumn(name = "物品信息", index = 2)
    private String goodsInfo;
    @ExcelColumn(name = "类型", index = 2)
    @ExcelColumn(name = "类型", index = 3)
    private String typeName;
    @ExcelColumn(name = "订单级别", index = 3)
    @ExcelColumn(name = "订单级别", index = 4)
    private String orderLevel;
    @ExcelColumn(name = "物品保费(元)", index = 4)
    @ExcelColumn(name = "物品保费(元)", index = 5)
    private String declaredFee;
    @ExcelColumn(name = "基础服务费(元)", index = 5)
    @ExcelColumn(name = "基础服务费(元)", index = 6)
    private String basicAmount;
    @ExcelColumn(name = "订单总价(元)", index = 6)
    @ExcelColumn(name = "订单总价(元)", index = 7)
    private String totalAmount;
    @ExcelColumn(name = "实付现金(元)", index = 7)
    @ExcelColumn(name = "实付现金(元)", index = 8)
    private String payAmount;
    @ExcelColumn(name = "加急费(元)", index = 8)
    @ExcelColumn(name = "加急费(元)", index = 9)
    private String urgentAmount;
    @ExcelColumn(name = "退款金额(元)", index = 9)
    @ExcelColumn(name = "退款金额(元)", index = 10)
    private String refundAmount;
    @ExcelColumn(name = "超时金额(元)", index = 10)
    @ExcelColumn(name = "超时金额(元)", index = 11)
    private String overdueAmount;
    @ExcelColumn(name = "异常金额(元)", index = 11)
    @ExcelColumn(name = "异常金额(元)", index = 12)
    private String exceptionAmount;
    @ExcelColumn(name = "优惠券折扣(元)", index = 12)
    @ExcelColumn(name = "优惠券折扣(元)", index = 13)
    private String deductionAmount;
    @ExcelColumn(name = "门店补偿费用(元)", index = 13)
    private String shopCompensationAmount;
    @ExcelColumn(name = "司机补偿费用(元)", index = 14)
    private String exceptionFee;
    @ExcelColumn(name = "订单状态", index = 15)
    @ExcelColumn(name = "门店保管补贴(元)", index = 15)
    private String shopCompensationAmount;
    @ExcelColumn(name = "订单状态", index = 16)
    private String statusDesc;
    @ExcelColumn(name = "结算状态", index = 16)
    @ExcelColumn(name = "结算状态", index = 17)
    private String settlementDesc;
    @ExcelColumn(name = "支付时间", index = 17, dateFormat = "yyyy-MM-dd HH:mm:ss", width = 16)
    @ExcelColumn(name = "支付时间", index = 18, dateFormat = "yyyy-MM-dd HH:mm:ss", width = 16)
    private Date payTime;
    @ExcelColumn(name = "创建时间", index = 18, dateFormat = "yyyy-MM-dd HH:mm:ss", width = 16)
    @ExcelColumn(name = "创建时间", index = 19, dateFormat = "yyyy-MM-dd HH:mm:ss", width = 16)
    private Date createTime;
}
server/services/src/main/java/com/doumee/dao/vo/OrdersRefundPageVO.java
@@ -32,8 +32,8 @@
    @ApiModelProperty(value = "物品信息")
    private String goodsInfo;
    @ExcelColumn(name = "订单类型", valueMapping = "0=就地存取;1=异地存取;")
    @ApiModelProperty(value = "订单类型:0=就地存取;1=异地存取")
    @ExcelColumn(name = "订单类型", valueMapping = "0=就地寄存;1=同城寄送;")
    @ApiModelProperty(value = "订单类型:0=就地寄存;1=同城寄送")
    private Integer orderType;
    @ExcelColumn(name = "订单级别")
server/services/src/main/java/com/doumee/service/business/AreasService.java
@@ -159,4 +159,9 @@
     */
    void updateStatus(Areas areas);
    /**
     * 一次性:为所有城市级别区划填充经纬度到info字段
     */
    String fillCityLocation();
}
server/services/src/main/java/com/doumee/service/business/OrdersService.java
@@ -213,17 +213,17 @@
    BigDecimal calculateInsuranceFee(BigDecimal declaredValue);
    /**
     * 计算就地存取预估费用
     * 计算就地寄存预估费用
     *
     * @param dto 就地存取计价请求参数
     * @param dto 就地寄存计价请求参数
     * @return 价格计算结果
     */
    PriceCalculateVO calculateLocalPrice(CalculateLocalPriceDTO dto);
    /**
     * 计算异地存取预估费用
     * 计算同城寄送预估费用
     *
     * @param dto 异地存取计价请求参数
     * @param dto 同城寄送计价请求参数
     * @return 价格计算结果
     */
    PriceCalculateVO calculateRemotePrice(CalculateRemotePriceDTO dto);
@@ -451,7 +451,7 @@
    int notifyArrivalPickUp();
    /**
     * 订单异常处理:异地无取件门店已送达订单,创建就地存取新订单
     * 订单异常处理:异地无取件门店已送达订单,创建就地寄存新订单
     */
    void handleOrderException(HandleOrderExceptionDTO dto);
server/services/src/main/java/com/doumee/service/business/PricingRuleService.java
@@ -108,15 +108,15 @@
    long count(PricingRule pricingRule);
    /**
     * 批量保存就地存取规则
     * 批量保存就地寄存规则
     * @param request 批量保存请求
     */
    void batchSaveLocalStoragePricing(LocalStoragePricingSaveDTO request);
    /**
     * 查询就地存取规则列表
     * 查询就地寄存规则列表
     * @param cityId 城市主键
     * @return 就地存取规则列表
     * @return 就地寄存规则列表
     */
    List<LocalStoragePricingVO> listLocalStoragePricing(Integer cityId);
server/services/src/main/java/com/doumee/service/business/impl/AreasServiceImpl.java
@@ -12,6 +12,7 @@
import com.doumee.core.model.PageData;
import com.doumee.core.model.PageWrap;
import com.doumee.core.utils.PinYinUtil;
import com.doumee.core.utils.geocode.MapUtil;
import com.doumee.core.utils.Utils;
import com.doumee.dao.business.AreasMapper;
import com.doumee.dao.business.PricingRuleMapper;
@@ -19,7 +20,9 @@
import com.doumee.dao.business.model.PricingRule;
import com.doumee.service.business.AreasService;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.checkerframework.checker.units.qual.A;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
@@ -35,6 +38,7 @@
 * @author 江蹄蹄
 * @date 2023/02/15 08:55
 */
@Slf4j
@Service
public class AreasServiceImpl implements AreasService {
    public static   List<Areas> ALL_AREA_LIST;
@@ -71,6 +75,10 @@
        areas.setCreateDate(new Date());
        areasMapper.insert(areas);
        areas.setCode(areas.getId().toString());
        // 城市级区划自动填充经纬度
        if (Constants.equalsInteger(areas.getType(), Constants.ONE)) {
            areas.setInfo(geocodeCity(areas));
        }
        areasMapper.updateById(areas);
        //刷新缓存数据
        cacheData();
@@ -115,6 +123,14 @@
        Areas update = new Areas();
        update.setName(areas.getName());
        update.setSortnum(areas.getSortnum());
        // 城市级区划自动更新经纬度
        Areas existing = areasMapper.selectById(areas.getId());
        if (existing != null && Constants.equalsInteger(existing.getType(), Constants.ONE)) {
            String location = MapUtil.geocode(getParentName(existing.getParentId()) + areas.getName());
            if (location != null) {
                update.setInfo(location);
            }
        }
        areasMapper.update(update,wrapper);
        //刷新缓存数据
        cacheData();
@@ -598,4 +614,51 @@
        areasMapper.updateById(update);
    }
    @Override
    public String fillCityLocation() {
        Areas query = new Areas();
        query.setType(1);
        List<Areas> cities = areasMapper.selectList(new QueryWrapper<Areas>().lambda()
                .eq(Areas::getType, 1));
        int success = 0, fail = 0;
        for (Areas city : cities) {
            if (StringUtils.isNotBlank(city.getInfo())) {
                continue;
            }
            String parentName = "";
            if (city.getParentId() != null) {
                Areas parent = getById(city.getParentId());
                if (parent != null) {
                    parentName = parent.getName();
                }
            }
            String address = parentName + city.getName();
            String location = MapUtil.geocode(address);
            if (location != null) {
                city.setInfo(location);
                areasMapper.updateById(city);
                success++;
                log.info("城市经纬度填充成功: {} => {}", address, location);
            } else {
                fail++;
                log.warn("城市经纬度填充失败: {}", address);
            }
            try { Thread.sleep(200); } catch (InterruptedException ignored) {}
        }
        return "处理完成,成功: " + success + ",失败: " + fail + ",总计: " + cities.size();
    }
    private String getParentName(Integer parentId) {
        if (parentId == null) {
            return "";
        }
        Areas parent = getById(parentId);
        return parent != null ? parent.getName() : "";
    }
    private String geocodeCity(Areas city) {
        String address = getParentName(city.getParentId()) + city.getName();
        return MapUtil.geocode(address);
    }
}
server/services/src/main/java/com/doumee/service/business/impl/DriverInfoServiceImpl.java
@@ -226,6 +226,29 @@
        if (Objects.isNull(driverInfo)) {
            throw new BusinessException(ResponseStatus.DATA_EMPTY);
        }
        // 查询审批人名称
        if (driverInfo.getAuditUser() != null) {
            try {
                SystemUser auditUser = systemUserService.findById(driverInfo.getAuditUser());
                if (auditUser != null) {
                    driverInfo.setAuditUserName(auditUser.getRealname());
                }
            } catch (Exception e) {
                // 审批人已删除等异常忽略
            }
        }
        // 查询省市区信息
        if (driverInfo.getAreaId() != null) {
            Areas district = areasBiz.resolveArea(driverInfo.getAreaId());
            if (district != null) {
                driverInfo.setDistrictId(district.getId());
                driverInfo.setDistrictName(district.getName());
                driverInfo.setCityId(district.getCityId());
                driverInfo.setCityName(district.getCityName());
                driverInfo.setProvinceId(district.getProvinceId());
                driverInfo.setProvinceName(district.getProvinceName());
            }
        }
        return driverInfo;
    }
@@ -977,6 +1000,18 @@
                // 审批人已删除等异常忽略
            }
        }
        // 查询省市区信息
        if (driverInfo.getAreaId() != null) {
            Areas district = areasBiz.resolveArea(driverInfo.getAreaId());
            if (district != null) {
                driverInfo.setDistrictId(district.getId());
                driverInfo.setDistrictName(district.getName());
                driverInfo.setCityId(district.getCityId());
                driverInfo.setCityName(district.getCityName());
                driverInfo.setProvinceId(district.getProvinceId());
                driverInfo.setProvinceName(district.getProvinceName());
            }
        }
        return driverInfo;
    }
server/services/src/main/java/com/doumee/service/business/impl/InvoiceRecordServiceImpl.java
@@ -18,6 +18,9 @@
import com.doumee.dao.business.model.InvoiceRecord;
import com.doumee.dao.business.model.Orders;
import com.doumee.dao.dto.ApplyInvoiceDTO;
import com.doumee.dao.dto.invoice.InvoiceA0001DTO;
import com.doumee.dao.dto.invoice.InvoiceA0001ItemDTO;
import com.doumee.dao.dto.invoice.InvoiceA0001Request;
import com.doumee.dao.vo.InvoiceRecordSummaryVO;
import com.doumee.service.business.InvoiceRecordService;
import com.doumee.service.common.EmailService;
@@ -206,6 +209,40 @@
        record.setCreateTime(new Date());
        invoiceRecordMapper.insert(record);
        InvoiceA0001Request invoiceA0001Request = new InvoiceA0001Request();
        invoiceA0001Request.setAccess_token("");//请求token
        invoiceA0001Request.setServiceKey("ebi_InvoiceHandle_newBlueInvoice");
        InvoiceA0001DTO invoiceA0001DTO  = new InvoiceA0001DTO();
        invoiceA0001DTO.setData_resources("API");
        invoiceA0001DTO.setItype("026");
        invoiceA0001DTO.setNsrsbh("");//销售方纳税人识别号
        invoiceA0001DTO.setOrder_num("");//业务单据号;必须是唯一的
        invoiceA0001DTO.setZsfs("0");
        invoiceA0001DTO.setTspz("00");
        invoiceA0001DTO.setXsf_yhzh("");//销售方开户行名称与银行账号  自贡市XX银行XX街支行 8888888888
        invoiceA0001DTO.setXsf_mc("");//销售方名称
        invoiceA0001DTO.setXsf_nsrsbh("");//销售方纳税人识别号
        invoiceA0001DTO.setXsf_dzdh("");//销售方地址、电话 自贡市XX街6号悠悠大厦D8幢8单元8层 0830-66008888
        invoiceA0001DTO.setGmf_mc(""); //购买方名称
        invoiceA0001DTO.setKpr(""); //开票人
        invoiceA0001DTO.setJshj("");//价税合计;单位:元(2位小数) 价税合计=合计金额(不含税)+合计税额 注意:不能使用商品的单价、数量、税率、税额来进行累加,最后四舍五入,只能是总合计金额+合计税额
        invoiceA0001DTO.setHjje("");//合计金额 注意:不含税,单位:元(2位小数)
        invoiceA0001DTO.setHjse("");//合计税额单位:元(2位小数)
        List<InvoiceA0001ItemDTO> common_fpkj_xmxx = new ArrayList<>();
        InvoiceA0001ItemDTO invoiceA0001ItemDTO = new InvoiceA0001ItemDTO();
        invoiceA0001ItemDTO.setFphxz("0");
        invoiceA0001ItemDTO.setXmmc("");//项目名称
        invoiceA0001ItemDTO.setXmdj("");//项目单价 小数点后6位 注意:单价是含税单价,大于0的数字
        invoiceA0001ItemDTO.setXmsl("1");
        invoiceA0001ItemDTO.setXmje("");
        invoiceA0001ItemDTO.setSe("");
        invoiceA0001ItemDTO.setSl("");
        common_fpkj_xmxx.add(invoiceA0001ItemDTO);
        invoiceA0001DTO.setCommon_fpkj_xmxx(common_fpkj_xmxx);
        invoiceA0001Request.setData(invoiceA0001DTO);
        // 更新订单发票状态为申请中
        order.setInvoiceStatus(Constants.TWO);
        order.setUpdateTime(new Date());
server/services/src/main/java/com/doumee/service/business/impl/OrdersServiceImpl.java
@@ -236,10 +236,12 @@
                .selectAll(Orders.class)
                .selectAs(Category::getDetail, Orders::getOrderLevel)
                .select("s1.name", Orders::getDepositShopName)
                .leftJoin(Category.class, Category::getId, Orders::getGoodType)
                .select("o2.code", Orders::getRelationOrderCode)
                .leftJoin(Category.class, Category::getId, Orders::getGoodLevel)
                .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") ;
                .leftJoin("shop_info s2 on s2.id = t.TAKE_SHOP_ID")
                .leftJoin("orders o2 on o2.id = t.RELATION_ORDER_ID")
                ;
        Utils.MP.blankToNull(pageWrap.getModel());
        queryWrapper.eq(pageWrap.getModel().getDeleted() != null, Orders::getDeleted, pageWrap.getModel().getDeleted());
@@ -348,7 +350,7 @@
    }
    /**
     * 计算就地存取预估费用
     * 计算就地寄存预估费用
     *
     * 计算规则:
     * 1. 根据城市+物品类型 查询 pricing_rule(type=0),fieldA=categoryId, fieldB=单价(分/天)
@@ -357,7 +359,7 @@
     * 4. 保价费用 = 报价金额 × 保价费率(字典 INSURANCE_RATE),元转分
     * 5. 总价格 = 物品价格 + 保价费用
     *
     * @param dto 就地存取计价请求参数
     * @param dto 就地寄存计价请求参数
     * @return 价格计算结果
     */
    @Override
@@ -447,7 +449,7 @@
    }
    /**
     * 计算异地存取预估费用
     * 计算同城寄送预估费用
     *
     * 计算规则:
     * 1. 调用腾讯地图API计算寄件点与取件点的驾车距离(米→公里)
@@ -462,7 +464,7 @@
     * 7. 加急费用 = 物品价格 × 加急系数(字典 URGENT_COEFFICIENT)
     * 8. 总价格 = 物品价格 + 保价费用 + 加急费用
     *
     * @param dto 异地存取计价请求参数
     * @param dto 同城寄送计价请求参数
     * @return 价格计算结果
     */
    @Override
@@ -499,7 +501,7 @@
            ruleMap.put(r.getFieldA(), r);
        }
        // 查询就地存取计价规则 pricing_rule type=0,用于获取 locallyPrice
        // 查询就地寄存计价规则 pricing_rule type=0,用于获取 locallyPrice
        List<PricingRule> localRules = pricingRuleMapper.selectList(new QueryWrapper<PricingRule>().lambda()
                .eq(PricingRule::getDeleted, Constants.ZERO)
                .eq(PricingRule::getType, Constants.ZERO)
@@ -553,7 +555,7 @@
            long subtotal = unitPrice * item.getQuantity();
            // 就地存取单价
            // 就地寄存单价
            PricingRule localRule = localRuleMap.get(String.valueOf(item.getCategoryId()));
            Long locallyPrice = localRule != null ? Long.parseLong(localRule.getFieldB()) : null;
@@ -812,7 +814,7 @@
                throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "请选择取件店铺或输入自选取件地址");
            }
        } else {
            // 就地存取:取件门店同寄件门店
            // 就地寄存:取件门店同寄件门店
            takeShop = depositShop;
        }
@@ -862,6 +864,7 @@
        orders.setPayStatus(Constants.ZERO); // 未支付
        orders.setCommentStatus(Constants.ZERO); // 未评价
        orders.setSettlementStatus(Constants.ZERO); // 未结算
        orders.setExceptionStatus(Constants.ZERO); // 非异常
        orders.setDeleted(Constants.ZERO);
        orders.setCreateTime(now);
        orders.setUpdateTime(now);
@@ -1283,7 +1286,7 @@
        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.setTypeDesc(order.getType() != null && order.getType() == Constants.ONE ? "同城寄送" : "就地寄存");
        vo.setDetailList(buildDetailList(id));
        return vo;
@@ -1302,9 +1305,9 @@
            throw new BusinessException(ResponseStatus.DATA_EMPTY);
        }
        // 前置条件校验:异地存取 + 已寄存
        // 前置条件校验:同城寄送 + 已寄存
        if (!Constants.ONE.equals(order.getType())) {
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "仅支持异地存取订单派单");
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "仅支持同城寄送订单派单");
        }
        if (!Integer.valueOf(Constants.OrderStatus.deposited.getStatus()).equals(order.getStatus())) {
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "仅已寄存状态订单可派单");
@@ -1534,7 +1537,7 @@
     *
     * @param orders        订单实体(需要 totalAmount、cityId 已设置)
     * @param depositShop   寄件门店(需要 companyType)
     * @param takeShop      取件门店(需要 companyType,就地存取时与 depositShop 相同)
     * @param takeShop      取件门店(需要 companyType,就地寄存时与 depositShop 相同)
     */
    private void calculateAndSetFeeAllocation(Orders orders, ShopInfo depositShop, ShopInfo takeShop) {
        Long totalAmount = orders.getTotalAmount() != null ? orders.getTotalAmount() : 0L;
@@ -1746,8 +1749,13 @@
                // 评价状态
                vo.setCommentStatus(o.getCommentStatus());
                String dateStr = new SimpleDateFormat("dd").format(o.getPayTime() != null ? o.getPayTime() : new Date());
                String autoNumStr = String.format("%03d", o.getAutoNum() != null ? o.getAutoNum() : 0);
                String sort = o.getDepositShopId() + "-" + dateStr + "-" + autoNumStr;
                //序号
                vo.setSortnum(Constants.formatIntegerNum(o.getDepositShopId())+"-"+o.getId());
                vo.setSortnum(sort);
                if(o.getTakeShopId()!=null){
                    vo.setSortnumTake(Constants.formatIntegerNum(o.getTakeShopId())+"-"+o.getId());
                }
@@ -2164,6 +2172,10 @@
            throw new BusinessException(ResponseStatus.DATA_EMPTY);
        }
        if (Constants.equalsInteger(order.getExceptionStatus(), Constants.ONE)) {
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "异常订单无法取消");
        }
        Integer status = order.getStatus();
        if (status == null) {
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "订单状态异常");
@@ -2199,8 +2211,6 @@
                    "orderNo", order.getCode());
            // 短信通知会员:订单已取消
            Member cancelMember2 = memberMapper.selectById(memberId);
            sendSmsNotify(cancelMember2 != null ? cancelMember2.getTelephone() : null,
                    Constants.SmsNotify.MEMBER_CANCELLED, "orderNo", order.getCode());
            // 调用微信退款V3,全额退款
            String outRefundNo = ID.nextGUID();
@@ -2238,6 +2248,8 @@
                throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "退款失败,请联系客服处理");
            }
            ordersRefundMapper.insert(refund);
            sendSmsNotify(cancelMember2 != null ? cancelMember2.getTelephone() : null,
                    Constants.SmsNotify.MEMBER_CANCELLED, "orderNo", order.getCode());
            restoreCoupon(order);
            return;
        }
@@ -2406,9 +2418,10 @@
        if (Constants.equalsInteger(order.getManualRefund(), Constants.ONE)) {
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "该订单已手动退款");
        }
        // 异常订单不允许手动退款
        if (Constants.equalsInteger(order.getExceptionStatus(), Constants.ONE)) {
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "异常订单不支持手动退款");
        // 就地寄存异常订单不允许手动退款
        if (Constants.equalsInteger(order.getExceptionStatus(), Constants.ONE)
                && Constants.equalsInteger(order.getType(), Constants.ZERO)) {
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "就地寄存异常订单不支持手动退款");
        }
        // 2. 校验退款金额
@@ -2851,13 +2864,27 @@
        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;
                // 异常就地寄存订单:同时更新主订单实际逾期费用
                boolean isAbnormal = Constants.equalsInteger(order.getExceptionStatus(), Constants.ONE)
                        && Constants.equalsInteger(order.getType(), Constants.ZERO);
                if(isAbnormal){
                    long shopException = isAbnormal ? (order.getShopCompensationAmount() != null ? order.getShopCompensationAmount() : 0L) : 0L;
                    long driverException = isAbnormal ? (order.getExceptionFee() != null ? order.getExceptionFee() : 0L) : 0L;
                    long actualOverdueFee = isAbnormal ? (overdueFee - shopException - driverException) : 0L;
                    if(actualOverdueFee!=order.getOverdueAmount()){
                        order.setOverdueAmount(actualOverdueFee);
                    }
                }
                order.setOverdueStatus(Constants.TWO); // 2=已支付
                order.setTotalAmount(newTotal);
                order.setUpdateTime(now);
                ordersMapper.updateById(order);
                // 重算三方收益
                calculateAndSaveOrderFees(order.getId());
            }
@@ -3348,6 +3375,37 @@
            }
            // 寄存成功赠送优惠券
            giftOrderCoupon(order.getMemberId());
            // 异常订单寄存核销:标记原订单完成
            if (Constants.equalsInteger(order.getExceptionStatus(), Constants.ONE) && order.getRelationOrderId() != null) {
                Orders originalOrder = ordersMapper.selectById(order.getRelationOrderId());
                if (originalOrder != null) {
                    originalOrder.setStatus(Constants.OrderStatus.finished.getStatus());
                    originalOrder.setInvoiceStatus(Constants.ONE);
                    originalOrder.setSettlementStatus(Constants.ZERO);
                    originalOrder.setFinishTime(now);
                    originalOrder.setUpdateTime(now);
                    ordersMapper.updateById(originalOrder);
                    // 触发原订单收益计算
                    calculateAndSaveOrderFees(originalOrder.getId());
                    generateRevenueRecords(originalOrder.getId());
                    // 记录原订单日志
                    saveShopVerifyLog(originalOrder, Constants.OrderLogType.shopTake, "异常订单核销,原订单完成", remark, shopId);
                    // 通知会员:订单已完成
                    sendOrderNotice(originalOrder.getMemberId(), Constants.MemberOrderNotify.FINISHED, originalOrder.getId(),
                            "orderNo", originalOrder.getCode());
                    // 通知存件门店和取件门店
                    String settleDays = operationConfigBiz.getConfig().getSettlementDate();
                    notifyBothShops(originalOrder, Constants.ShopOrderNotify.FINISHED,
                            "orderNo", originalOrder.getCode(),
                            "settleDays", settleDays != null ? settleDays : "7");
                    // 通知司机:订单已完成
                    if (originalOrder.getAcceptDriver() != null) {
                        sendDriverNotice(originalOrder.getAcceptDriver(), Constants.DriverOrderNotify.FINISHED, originalOrder.getId(),
                                "orderNo", originalOrder.getCode(),
                                "settleDays", settleDays != null ? settleDays : "7");
                    }
                }
            }
        } else if (Constants.equalsInteger(status, Constants.OrderStatus.arrived.getStatus())) {
            // 异地寄存 + 无取件门店 → 无法核销(客户自取,无门店操作)
            if (Constants.equalsInteger(order.getType(), Constants.ONE) && order.getTakeShopId() == null) {
@@ -3516,30 +3574,7 @@
                sendOrderNotice(order.getMemberId(), Constants.MemberOrderNotify.WAIT_PICKUP_REMIND, order.getId(),
                        "orderNo", order.getCode(), "shopName", shopName);
            }
            // 异常订单寄存核销:标记原订单完成
            if (Constants.equalsInteger(order.getExceptionStatus(), Constants.ONE) && order.getRelationOrderId() != null) {
                Orders originalOrder = ordersMapper.selectById(order.getRelationOrderId());
                if (originalOrder != null) {
                    originalOrder.setStatus(Constants.OrderStatus.finished.getStatus());
                    originalOrder.setInvoiceStatus(Constants.ONE);
                    originalOrder.setFinishTime(now);
                    originalOrder.setUpdateTime(now);
                    ordersMapper.updateById(originalOrder);
                    // 触发原订单收益计算
                    calculateAndSaveOrderFees(originalOrder.getId());
                    generateRevenueRecords(originalOrder.getId());
                    // 通知会员:订单已完成
                    sendOrderNotice(originalOrder.getMemberId(), Constants.MemberOrderNotify.FINISHED, originalOrder.getId(),
                            "orderNo", originalOrder.getCode());
                    // 通知司机:订单已完成
                    if (originalOrder.getAcceptDriver() != null) {
                        String settleDays = operationConfigBiz.getConfig().getSettlementDate();
                        sendDriverNotice(originalOrder.getAcceptDriver(), Constants.DriverOrderNotify.FINISHED, originalOrder.getId(),
                                "orderNo", originalOrder.getCode(),
                                "settleDays", settleDays != null ? settleDays : "7");
                    }
                }
            }
        } else if (Constants.equalsInteger(status, Constants.OrderStatus.arrived.getStatus())) {
            // 异地寄存 + 无取件门店 → 无法核销(客户自取,无门店操作)
            if (Constants.equalsInteger(order.getType(), Constants.ONE) && order.getTakeShopId() == null) {
@@ -3767,13 +3802,17 @@
        }
        Long totalAmount = order.getTotalAmount() != null ? order.getTotalAmount() : 0L;
        Long shopCompensationAmount = order.getShopCompensationAmount() != null ? order.getShopCompensationAmount() : 0L;
        Long exceptionFee = order.getExceptionFee() != null ? order.getExceptionFee() : 0L;
        // 分成基数 = 总金额 - 门店异常金额 - 司机异常金额
        Long feeBase = totalAmount - shopCompensationAmount - exceptionFee;
        // 费率(为空时默认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 depositShopFee = new BigDecimal(totalAmount)
        Long depositShopFee = new BigDecimal(feeBase)
                .multiply(depositRate)
                .setScale(0, RoundingMode.HALF_UP)
                .longValue();
@@ -3783,14 +3822,14 @@
        if (Constants.equalsInteger(order.getType(), Constants.ONE)) {
            // 异地寄存:存件门店 + 司机
            driverFee = new BigDecimal(totalAmount)
            driverFee = new BigDecimal(feeBase)
                    .multiply(driverRate)
                    .setScale(0, RoundingMode.HALF_UP)
                    .longValue();
            // 异地寄存且有取件门店:加上取件门店收益
            if (order.getTakeShopId() != null) {
                takeShopFee = new BigDecimal(totalAmount)
                takeShopFee = new BigDecimal(feeBase)
                        .multiply(takeRate)
                        .setScale(0, RoundingMode.HALF_UP)
                        .longValue();
@@ -3828,6 +3867,29 @@
            }
        }
        // 异常订单:存件门店异常补偿收益(单独一条记录,type=5)
        if (Constants.equalsInteger(order.getExceptionStatus(), Constants.ONE)
                && order.getShopCompensationAmount() != null && order.getShopCompensationAmount() > 0
                && order.getDepositShopId() != null) {
            ShopInfo depositShop = shopInfoMapper.selectById(order.getDepositShopId());
            if (depositShop != null && depositShop.getId() != null) {
                Revenue shopExRevenue = new Revenue();
                shopExRevenue.setMemberId(depositShop.getId());
                shopExRevenue.setMemberType(Constants.TWO); // 2=门店
                shopExRevenue.setType(5); // 5=异常金额
                shopExRevenue.setOptType(Constants.ONE); // 1=收入
                shopExRevenue.setAmount(order.getShopCompensationAmount());
                shopExRevenue.setVaildStatus(Constants.ZERO);
                shopExRevenue.setObjId(orderId);
                shopExRevenue.setObjType(Constants.ZERO);
                shopExRevenue.setStatus(Constants.ZERO);
                shopExRevenue.setOrderNo(order.getCode());
                shopExRevenue.setDeleted(Constants.ZERO);
                shopExRevenue.setCreateTime(now);
                revenueMapper.insert(shopExRevenue);
            }
        }
        // 取件门店收益(异地寄存且有取件门店)
        if (takeShopFee > 0 && order.getTakeShopId() != null) {
            ShopInfo takeShop = shopInfoMapper.selectById(order.getTakeShopId());
@@ -3846,27 +3908,33 @@
            }
        }
        // 异常订单:司机异常补偿(异地 + 异常标记 + 有异常补偿金额)
        // 异常订单:司机异常补偿(异地 + 异常标记,从关联异常子订单获取exceptionFee)
        if (Constants.equalsInteger(order.getType(), Constants.ONE)
                && Constants.equalsInteger(order.getExceptionStatus(), Constants.ONE)
                && order.getExceptionFee() != null && order.getExceptionFee() > 0
                && order.getAcceptDriver() != null) {
            DriverInfo driver = driverInfoMapper.selectById(order.getAcceptDriver());
            if (driver != null && driver.getMemberId() != null) {
                Revenue exRevenue = new Revenue();
                exRevenue.setMemberId(driver.getMemberId());
                exRevenue.setMemberType(Constants.ONE); // 1=司机
                exRevenue.setType(5); // 5=异常金额
                exRevenue.setOptType(Constants.ONE); // 1=收入
                exRevenue.setAmount(order.getExceptionFee());
                exRevenue.setVaildStatus(Constants.ZERO);
                exRevenue.setObjId(orderId);
                exRevenue.setObjType(Constants.ZERO);
                exRevenue.setStatus(Constants.ZERO);
                exRevenue.setOrderNo(order.getCode());
                exRevenue.setDeleted(Constants.ZERO);
                exRevenue.setCreateTime(now);
                revenueMapper.insert(exRevenue);
            Orders childOrder = ordersMapper.selectOne(new QueryWrapper<Orders>().lambda()
                    .eq(Orders::getRelationOrderId, orderId)
                    .eq(Orders::getExceptionStatus, Constants.ONE)
                    .eq(Orders::getDeleted, Constants.ZERO)
                    .last("limit 1"));
            if (childOrder != null && childOrder.getExceptionFee() != null && childOrder.getExceptionFee() > 0) {
                DriverInfo driver = driverInfoMapper.selectById(order.getAcceptDriver());
                if (driver != null && driver.getMemberId() != null) {
                    Revenue exRevenue = new Revenue();
                    exRevenue.setMemberId(driver.getMemberId());
                    exRevenue.setMemberType(Constants.ONE); // 1=司机
                    exRevenue.setType(5); // 5=异常金额
                    exRevenue.setOptType(Constants.ONE); // 1=收入
                    exRevenue.setAmount(childOrder.getExceptionFee());
                    exRevenue.setVaildStatus(Constants.ZERO);
                    exRevenue.setObjId(orderId);
                    exRevenue.setObjType(Constants.ZERO);
                    exRevenue.setStatus(Constants.ZERO);
                    exRevenue.setOrderNo(order.getCode());
                    exRevenue.setDeleted(Constants.ZERO);
                    exRevenue.setCreateTime(now);
                    revenueMapper.insert(exRevenue);
                }
            }
        }
@@ -4100,8 +4168,7 @@
        if (Constants.equalsInteger(order.getExceptionStatus(), Constants.ONE)
                && order.getRelationOrderId() != null) {
            Orders originalOrder = ordersMapper.selectById(order.getRelationOrderId());
            long driverExceptionFee = (originalOrder != null && originalOrder.getExceptionFee() != null)
                    ? originalOrder.getExceptionFee() : 0L;
            long driverExceptionFee = order.getExceptionFee() != null ? order.getExceptionFee() : 0L;
            long shopExceptionFee = order.getShopCompensationAmount() != null ? order.getShopCompensationAmount() : 0L;
            long totalExceptionFee = driverExceptionFee + shopExceptionFee;
@@ -4115,13 +4182,14 @@
                overdueFee = totalExceptionFee;
                overdueDays = totalExceptionFee > 0 ? 1 : 0;
            } else {
                // 非当天取件:正常逾期计算 + 司机异常费用(不含门店异常费用)
                // 非当天取件:正常逾期计算 + 司机异常费用(不含门店异常费用),门店异常金额清零
                OverdueFeeVO normalOverdue = calculateOverdueFeeInternal(order, details);
                long normalFee = (normalOverdue != null && normalOverdue.getOverdueFee() != null)
                        ? normalOverdue.getOverdueFee() : 0L;
                overdueFee = normalFee + driverExceptionFee;
                overdueDays = (normalOverdue != null && normalOverdue.getOverdueDays() != null)
                        ? normalOverdue.getOverdueDays() : 0;
                order.setShopCompensationAmount(0L);
            }
            order.setConfirmArriveTime(now);
@@ -5108,14 +5176,14 @@
                targetLat = order.getDepositLat();
                targetLgt = order.getDepositLgt();
            } else if (Constants.equalsInteger(order.getStatus(), Constants.OrderStatus.arrived.getStatus())&&Constants.equalsInteger(order.getType(), Constants.ZERO)) {
                // 就地存取 → 对比存件门店
                // 就地寄存 → 对比存件门店
                if (!Constants.equalsInteger(order.getDepositShopId(), userId)) {
                    throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "无权操作该订单");
                }
                targetLat = order.getDepositLat();
                targetLgt = order.getDepositLgt();
            } else if ((Constants.equalsInteger(order.getStatus(), Constants.OrderStatus.delivering.getStatus())||Constants.equalsInteger(order.getStatus(), Constants.OrderStatus.arrived.getStatus()))&&Constants.equalsInteger(order.getType(), Constants.ONE)) {
                // 异地存取 → 对比取件门店
                // 同城寄送 → 对比取件门店
                if (!Constants.equalsInteger(order.getTakeShopId(), userId)) {
                    throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "无权操作该订单");
                }
@@ -5181,7 +5249,7 @@
        Date now = new Date();
        // ========== B. 创建新订单(就地存取) ==========
        // ========== B. 创建新订单(就地寄存) ==========
        String orderCode = "JC" + new java.text.SimpleDateFormat("yyyyMMddHHmmss").format(now)
                + String.format("%04d", new java.util.Random().nextInt(10000));
        String orderTradeNo = generateOrderTradeNo();
@@ -5222,7 +5290,7 @@
            }
        }
        // 存件门店分成占比(就地存取)
        // 存件门店分成占比(就地寄存)
        Integer cityId = Integer.valueOf(original.getCityId());
        boolean isCompany = Constants.equalsInteger(newShop.getCompanyType(), Constants.ONE);
        int fallbackFieldA = isCompany ? Constants.FIVE : Constants.SIX;
@@ -5232,7 +5300,7 @@
        newOrder.setCode(orderCode);
        newOrder.setOutTradeNo(orderTradeNo);
        newOrder.setMemberId(original.getMemberId());
        newOrder.setType(Constants.ZERO); // 就地存取
        newOrder.setType(Constants.ZERO); // 就地寄存
        newOrder.setCityId(original.getCityId());
        newOrder.setStatus(Constants.ONE); // 待寄存
        newOrder.setPayStatus(Constants.ONE); // 已支付
@@ -5283,8 +5351,8 @@
        newOrder.setDeclaredAmount(0L);
        newOrder.setDeclaredFee(0L);
        newOrder.setPrice(price);
        newOrder.setBasicAmount(basicAmount);
        newOrder.setEstimatedAmount(basicAmount);
        newOrder.setBasicAmount(0L);//);
        newOrder.setEstimatedAmount(0L);//basicAmount);
        newOrder.setTotalAmount(0L);
        newOrder.setPayAmount(0L);
        newOrder.setManualRefund(Constants.ZERO);
@@ -5299,6 +5367,8 @@
        // 门店补偿金额
        newOrder.setShopCompensationAmount(dto.getShopCompensation());
        // 司机异常补偿金额
        newOrder.setExceptionFee(dto.getDriverCompensation());
        newOrder.setRemark(dto.getRemark());
        newOrder.setPlatformSmsNotified(Constants.ZERO);
@@ -5355,7 +5425,6 @@
        // ========== E. 更新原订单 ==========
        ordersMapper.update(new UpdateWrapper<Orders>().lambda()
                .set(Orders::getExceptionStatus, Constants.ONE)
                .set(Orders::getExceptionFee, dto.getDriverCompensation())
                .set(Orders::getUpdateTime, now)
                .eq(Orders::getId, original.getId()));
server/services/src/main/java/com/doumee/service/business/impl/ShopInfoServiceImpl.java
@@ -1801,15 +1801,15 @@
    private List<String> validateCityPricingRules(Integer cityId) {
        List<String> errors = new ArrayList<>();
        // type=0 就地存取规则:至少1条,fieldB不为空
        // type=0 就地寄存规则:至少1条,fieldB不为空
        List<PricingRule> type0 = pricingRuleMapper.selectList(new QueryWrapper<PricingRule>().lambda()
                .eq(PricingRule::getDeleted, Constants.ZERO)
                .eq(PricingRule::getType, Constants.ZERO)
                .eq(PricingRule::getCityId, cityId));
        if (type0.isEmpty()) {
            errors.add("缺少就地存取规则");
            errors.add("缺少就地寄存规则");
        } else if (type0.stream().allMatch(r -> StringUtils.isBlank(r.getFieldB()))) {
            errors.add("就地存取规则未配置收费单价");
            errors.add("就地寄存规则未配置收费单价");
        }
        // type=1 异地寄送规则:至少1条,fieldB/C/D/E不为空
server/services/src/main/java/com/doumee/service/business/impl/WithdrawalOrdersServiceImpl.java
@@ -151,7 +151,7 @@
            // 司机端:关联 DriverInfo 表
            queryWrapper.selectAs(DriverInfo::getName, WithdrawalOrders::getMemberName)
                    .selectAs(DriverInfo::getTelephone, WithdrawalOrders::getMemberTelephone)
                    .leftJoin(DriverInfo.class, DriverInfo::getMemberId, WithdrawalOrders::getMemberId);
                    .leftJoin(DriverInfo.class, DriverInfo::getId, WithdrawalOrders::getMemberId);
        } else {
            // 店铺端 / 不筛选:关联 ShopInfo 表
            queryWrapper.selectAs(ShopInfo::getName, WithdrawalOrders::getShopName)
server/web/src/main/java/com/doumee/api/web/ConfigApi.java
@@ -91,6 +91,12 @@
        return ApiResponse.success("门店初始化 " + shopCount + " 条,司机初始化 " + driverCount + " 条");
    }
    @ApiOperation("一次性:为所有城市级别区划填充经纬度到info字段")
    @PostMapping("/fillCityLocation")
    public ApiResponse<String> fillCityLocation() {
        return ApiResponse.success(areasService.fillCityLocation());
    }
    @ApiOperation("全部区划树形查询")
    @PostMapping("/treeList")
    public ApiResponse<List<Areas>> treeList (@RequestBody AreasDto pageWrap) {
@@ -161,7 +167,7 @@
    }
    @LoginRequired
    @ApiOperation(value = "计算就地存取预估费用", notes = "根据城市、天数、物品类型和数量计算就地存取预估费用")
    @ApiOperation(value = "计算就地寄存预估费用", notes = "根据城市、天数、物品类型和数量计算就地寄存预估费用")
    @PostMapping("/calculateLocalPrice")
    public ApiResponse<PriceCalculateVO> calculateLocalPrice(@RequestBody @Valid CalculateLocalPriceDTO dto) {
        dto.setMemberId(getMemberId());
@@ -169,7 +175,7 @@
    }
    @LoginRequired
    @ApiOperation(value = "计算异地存取预估费用", notes = "根据距离、物品类型和数量计算异地存取预估费用")
    @ApiOperation(value = "计算同城寄送预估费用", notes = "根据距离、物品类型和数量计算同城寄送预估费用")
    @PostMapping("/calculateRemotePrice")
    public ApiResponse<PriceCalculateVO> calculateRemotePrice(@RequestBody @Valid CalculateRemotePriceDTO dto) {
        dto.setMemberId(getMemberId());