rk
3 天以前 2ab42edae9d271f5f464b7be475e217752dacb38
代码生成
已修改24个文件
498 ■■■■ 文件已修改
server/admin/src/main/resources/application.yml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/db/db_change.sql 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/config/alipay/AlipayFundTransUniTransfer.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/config/jwt/JwtTokenUtil.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/config/wx/WxMiniConfig.java 65 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/config/wx/WxPayProperties.java 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/config/wx/WxPayV3Service.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/core/constants/Constants.java 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/business/model/WithdrawalOrders.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/vo/MyOrderVO.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/vo/OrderItemVO.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/DriverInfoService.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/MemberService.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/ShopInfoService.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/impl/DriverInfoServiceImpl.java 115 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/impl/MemberServiceImpl.java 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/impl/OrdersServiceImpl.java 100 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/impl/ShopInfoServiceImpl.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/impl/WithdrawalOrdersServiceImpl.java 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/resources/application-dev.yml 27 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/web/src/main/java/com/doumee/api/web/AccountApi.java 26 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/web/src/main/java/com/doumee/api/web/DriverInfoApi.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/web/src/main/java/com/doumee/api/web/OrdersApi.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/web/src/main/resources/application.yml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/admin/src/main/resources/application.yml
@@ -3,7 +3,7 @@
  port: 10010
# 项目信息配置
project:
  name: 近快
  name: 高铁行李寄存
  version: 1.0.0
  # 环境,生产环境production,开发环境development
  env: production
server/services/db/db_change.sql
@@ -5,6 +5,12 @@
-- ============================================================
-- 2026/04/22 提现记录表增加支付宝实名姓名字段
-- ============================================================
ALTER TABLE `withdrawal_orders` ADD COLUMN `ALI_NAME` VARCHAR(50) DEFAULT NULL COMMENT '支付宝实名姓名' AFTER `ALI_ACCOUNT`;
-- ============================================================
-- 2026/04/20 商户与司机增加支付宝实名姓名字段
-- ============================================================
ALTER TABLE `shop_info` ADD COLUMN `ALI_NAME` VARCHAR(50) DEFAULT NULL COMMENT '支付宝实名姓名' AFTER `ALI_ACCOUNT`;
server/services/src/main/java/com/doumee/config/alipay/AlipayFundTransUniTransfer.java
@@ -188,7 +188,7 @@
        // 设置转账业务的标题
        data.setOrderTitle("佣金报酬");
        // 设置原支付宝业务单号
        data.setOriginalOrderId("20190620110075000006640000063056");
        data.setOriginalOrderId(dto.getOutBizNo());
        // 设置收款方信息
        data.setOrderTitle("佣金报酬");
        Participant payeeInfo = new Participant();
server/services/src/main/java/com/doumee/config/jwt/JwtTokenUtil.java
@@ -119,7 +119,7 @@
     * @param token 原令牌
     * @return 新令牌
     */
    public void logoutForH5(String token) {
    public void logout(String token) {
        try {
            //删除老的token
            redisTemplate.delete(Constants.REDIS_TOKEN_KEY+token);
server/services/src/main/java/com/doumee/config/wx/WxMiniConfig.java
@@ -8,19 +8,20 @@
import com.github.binarywang.wxpay.constant.WxPayConstants;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.RSAPublicKeyConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RSAPublicKeyNotificationConfig;
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
import com.wechat.pay.java.service.refund.RefundService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.StreamUtils;
import javax.annotation.PostConstruct;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
/**
 * 微信小程序组件
@@ -50,7 +51,7 @@
    @PostConstruct
    void init() {
        this.load_WxMaService();
        this.load_wxPayService();
//        this.load_wxPayService();
        this.load_wxPayV3Service();
//        this.load_wxAppPayService();
    }
@@ -70,48 +71,44 @@
    /**
     * 初始化微信小程序支付 V2
     */
    public void load_wxPayService() {
        WxPayConfig payConfig = new WxPayConfig();
        payConfig.setTradeType(WxPayConstants.TradeType.JSAPI);
        payConfig.setSignType(WxPayConstants.SignType.MD5);
        payConfig.setAppId(StringUtils.trimToNull(wxPayProperties.getAppId()));
        payConfig.setMchId(StringUtils.trimToNull(wxPayProperties.getMchId()));
        payConfig.setMchKey(StringUtils.trimToNull(wxPayProperties.getMchKey()));
        payConfig.setKeyPath(StringUtils.trimToNull(wxPayProperties.getKeyPath()));
        payConfig.setNotifyUrl(StringUtils.trimToNull(wxPayProperties.getNotifyUrl()));
        WxPayService wxPayService = new WxPayServiceImpl();
        wxPayService.setConfig(payConfig);
        this.wxPayService = wxPayService;
    }
//    public void load_wxPayService() {
//        WxPayConfig payConfig = new WxPayConfig();
//        payConfig.setTradeType(WxPayConstants.TradeType.JSAPI);
//        payConfig.setSignType(WxPayConstants.SignType.MD5);
//        payConfig.setAppId(StringUtils.trimToNull(wxPayProperties.getAppId()));
//        payConfig.setMchId(StringUtils.trimToNull(wxPayProperties.getMchId()));
//        payConfig.setMchKey(StringUtils.trimToNull(wxPayProperties.getMchKey()));
//        payConfig.setKeyPath(StringUtils.trimToNull(wxPayProperties.getKeyPath()));
//        payConfig.setNotifyUrl(StringUtils.trimToNull(wxPayProperties.getNotifyUrl()));
//        WxPayService wxPayService = new WxPayServiceImpl();
//        wxPayService.setConfig(payConfig);
//        this.wxPayService = wxPayService;
//    }
    /**
     * 初始化微信支付 V3(JSAPI + 退款 + 回调验签)
     * 使用平台证书模式(自动下载和管理微信平台证书)
     */
    public void load_wxPayV3Service() {
        try {
            Config config =
                    new RSAPublicKeyConfig.Builder()
                            .merchantId(wxPayProperties.getMchId()) //微信支付的商户号
                            .privateKeyFromPath(wxPayProperties.getPrivateKeyPath()) // 商户API证书私钥的存放路径
                            .merchantSerialNumber(wxPayProperties.getSerialNumer()) //商户API证书序列号
                            .publicKeyFromPath(wxPayProperties.getPubKeyPath()) //微信支付公钥的存放路径
                            .publicKeyId(wxPayProperties.getPublicKeyId()) //微信支付公钥ID
                            .apiV3Key(wxPayProperties.getApiV3Key()) //APIv3密钥
                            .build();
            // 从 classpath 读取商户私钥
            ClassPathResource keyResource = new ClassPathResource(StringUtils.trimToNull(wxPayProperties.getPrivateKeyPath()));
            InputStream keyStream = keyResource.getInputStream();
            String privateKey = StreamUtils.copyToString(keyStream, StandardCharsets.UTF_8);
            keyStream.close();
            // 支付公钥配置(用于回调验签)
            RSAPublicKeyNotificationConfig notifyConfig = new RSAPublicKeyNotificationConfig.Builder()
                    .publicKeyFromPath(wxPayProperties.getPubKeyPath())
                    .publicKeyId(wxPayProperties.getPublicKeyId())
                    .apiV3Key(wxPayProperties.getApiV3Key())
            RSAAutoCertificateConfig config = new RSAAutoCertificateConfig.Builder()
                    .merchantId(StringUtils.trimToNull(wxPayProperties.getMchId()))
                    .privateKey(privateKey)
                    .merchantSerialNumber(StringUtils.trimToNull(wxPayProperties.getSerialNumer()))
                    .apiV3Key(StringUtils.trimToNull(wxPayProperties.getApiV3Key()))
                    .build();
            v3JsapiService = new JsapiServiceExtension.Builder().config(config).build();
            v3RefundService = new RefundService.Builder().config(config).build();
            v3NotificationParser = new NotificationParser(notifyConfig);
            v3NotificationParser = new NotificationParser(config);
            log.info("微信支付V3初始化成功");
            log.info("微信支付V3初始化成功(平台证书模式)");
        } catch (Exception e) {
            log.error("微信支付V3初始化失败: {}", e.getMessage(), e);
        }
server/services/src/main/java/com/doumee/config/wx/WxPayProperties.java
@@ -34,12 +34,12 @@
    /**
     * 支付API密钥
     */
    private String mchKey;
//    private String mchKey;
    /**
     * 支付回调地址
     */
    private String notifyUrl;
//    private String notifyUrl;
    /**
     * V3支付回调地址
@@ -54,7 +54,7 @@
    /**
     * 支付证书(p12)
     */
    private String keyPath;
//    private String keyPath;
@@ -73,18 +73,18 @@
    /**
     * 退款回调
     */
    private String refundNotifyUrl;
//    private String refundNotifyUrl;
    /**
     * 商户支付公钥
     */
    private String pubKeyPath;
//    private String pubKeyPath;
    /**
     * 支付秘钥
     */
    private String privateCertPath;
//    private String privateCertPath;
    /**
     * 支付key
@@ -95,6 +95,6 @@
    /**
     * 微信支付公钥ID
     */
    private String publicKeyId;
//    private String publicKeyId;
}
server/services/src/main/java/com/doumee/config/wx/WxPayV3Service.java
@@ -121,7 +121,7 @@
            return result;
        } catch (Exception e) {
            log.error("微信支付V3退款失败: {}", e.getMessage(), e);
            throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "退款失败:" + e.getMessage());
            throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "退款失败:请联系管理员");
        }
    }
server/services/src/main/java/com/doumee/core/constants/Constants.java
@@ -102,6 +102,7 @@
    public static final int ORDER_LOG_CANCEL = 4;           // 取消订单
    public static final int ORDER_LOG_CONFIRM_ARRIVE = 5;   // 确认顾客到店
    public static final int ORDER_LOG_DRIVER_PICKUP = 6;    // 司机完成取件
    public static final int ORDER_LOG_DRIVER_DELIVER = 7;  // 司机确认送达
    public static final String SUCCESS = "SUCCESS";
    public static final String FAIL = "FAIL";
@@ -369,9 +370,16 @@
            return null;
        }
        public static String getDescByKey(int index) {
        public static String getDescByKey(int index,int type) {
            for (OrderStatus c : OrderStatus.values()) {
                if (c.getKey() == index) {
                    if (c.getKey() == 5) {
                        if(Constants.equalsInteger(type,Constants.ZERO)){
                            return "待取件";
                        }else{
                            return Constants.equalsInteger(type,Constants.ONE)?"已到店":"已送达";
                        }
                    }
                    return c.getValue();
                }
            }
@@ -516,6 +524,41 @@
    }
    /**
     * 司机订单站内信通知枚举
     * title: 通知标题
     * content: 通知文案模板,占位符用 {xxx} 表示
     */
    @Getter
    @AllArgsConstructor
    public enum DriverOrderNotify {
        WAIT_DELIVER("waitDeliver", "订单待配送", "您已抢单成功,订单:{orderNo}请按时到{shopName}取件"),
        DELIVERING("delivering", "配送中", "行李订单:{orderNo}已取件,正在配送中,请按时送达"),
        ARRIVED("arrived", "已送达", "行李订单:{orderNo}已送达{destination},请联系用户确认签收"),
        FINISHED("finished", "订单已完成", "行李订单:{orderNo}已完成,相关订单结算会在{settleDays}个工作日内结算"),
        EVALUATED("evaluated", "订单已评价", "行李订单:{orderNo}用户已完成评价,可前往订单查看评价内容"),
        REFUNDING("refunding", "退款中", "行李订单:{orderNo}用户已提交退款申请,该订单任务已取消,请勿前往。"),
        SETTLED("settled", "订单已结算", "行李订单:{orderNo}平台已完成结算,金额为{amount}元,请注意查收。"),
        CANCELLED("cancelled", "订单取消成功", "行李订单:{orderNo}已帮您取消,您今日还可主动取消{cancelLimit}次订单,请合理安排接单。")
        ;
        private final String key;
        private final String title;
        private final String content;
        /**
         * 格式化通知内容
         * @param params 键值对,如 "orderNo","123" 交替传入
         */
        public String format(String... params) {
            String result = this.content;
            for (int i = 0; i < params.length - 1; i += 2) {
                result = result.replace("{" + params[i] + "}", params[i + 1]);
            }
            return result;
        }
    }
    /**
     * 得到request对象
     *
     * @return
server/services/src/main/java/com/doumee/dao/business/model/WithdrawalOrders.java
@@ -96,6 +96,9 @@
    @ApiModelProperty(value = "支付宝提现账户")
    private String aliAccount;
    @ApiModelProperty(value = "支付宝实名姓名")
    private String aliName;
    @ApiModelProperty(value = "审批操作人(关联system_user)", example = "1")
    private Integer userId;
server/services/src/main/java/com/doumee/dao/vo/MyOrderVO.java
@@ -26,8 +26,12 @@
    @ApiModelProperty(value = "寄存方式:0=就地存取;1=异地存取")
    private Integer type;
    @ApiModelProperty(value = "订单状态")
    @ApiModelProperty(value = "就地寄存状态:0=待支付;1=待寄存;2=已寄存;5=待取件;6=存在逾期;7=已完成;96:订单关闭(退款);97:取消逾期;98=取消中;99=已取消;" +
            "            异地寄存状态:0=待支付;1=待寄存;2=已寄存;3=已接单;4=派送中;5=已到店/已送达;6=存在逾期;7=已完成;96:订单关闭(退款);97:取消逾期;98=取消中;99=已取消")
    private Integer status;
    @ApiModelProperty(value = "状态文案")
    private String statusName;
    @ApiModelProperty(value = "创建时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@@ -37,7 +41,13 @@
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date expectedTakeTime;
    @ApiModelProperty(value = "会员取件码")
    private String memberVerifyCode;
    // ---- 存件门店 ----
    @ApiModelProperty(value = "存件门店主键")
    private Integer depositShopId;
    @ApiModelProperty(value = "存件门店名称")
    private String depositShopName;
@@ -49,6 +59,9 @@
    private String depositShopPhone;
    // ---- 取件信息 ----
    @ApiModelProperty(value = "取件门店主键(有取件门店时返回)")
    private Integer takeShopId;
    @ApiModelProperty(value = "取件门店名称(有取件门店时返回)")
    private String takeShopName;
@@ -70,7 +83,7 @@
    // ---- 费用 ----
    @ApiModelProperty(value = "报价保费(分)")
    @ApiModelProperty(value = "保价保费(分)")
    private Long declaredFee;
    @ApiModelProperty(value = "预估费用(分)")
@@ -96,4 +109,9 @@
    @ApiModelProperty(value = "当前门店角色:1=存件门店;2=取件门店(仅门店端返回)")
    private Integer shopRole;
    // ---- 评价 ----
    @ApiModelProperty(value = "评价状态:0=未评价;1=已评价")
    private Integer commentStatus;
}
server/services/src/main/java/com/doumee/dao/vo/OrderItemVO.java
@@ -25,9 +25,9 @@
    @ApiModelProperty(value = "数量")
    private Integer num;
    @ApiModelProperty(value = "单价(元)")
    private Double unitPriceYuan;
    @ApiModelProperty(value = "单价(分)")
    private Long unitPrice;
    @ApiModelProperty(value = "小计费用(元)")
    private Double subtotal;
    @ApiModelProperty(value = "小计费用(分)")
    private Long subtotal;
}
server/services/src/main/java/com/doumee/service/business/DriverInfoService.java
@@ -6,6 +6,7 @@
import com.doumee.dao.dto.DriverGrabOrderDTO;
import com.doumee.dao.business.model.DriverInfo;
import com.doumee.dao.dto.DriverLoginRequest;
import com.doumee.dao.dto.DriverDeliverDTO;
import com.doumee.dao.dto.DriverPickupDTO;
import com.doumee.dao.dto.DriverRegisterRequest;
import com.doumee.dao.dto.DriverVerifyRequest;
@@ -241,4 +242,12 @@
     */
    void confirmPickup(Integer driverId, DriverPickupDTO dto);
    /**
     * 司机确认送达(异地寄存无取件门店),订单状态从派送中(4)变为已送达(5)
     *
     * @param driverId 司机主键
     * @param dto      送达请求参数
     */
    void confirmDeliver(Integer driverId, DriverDeliverDTO dto);
}
server/services/src/main/java/com/doumee/service/business/MemberService.java
@@ -149,7 +149,7 @@
    PlatformAboutVO getPlatformAboutUs();
    void logOut(String token,Integer memberId);
    void logOut(String token,Integer memberId,Integer userType);
    void logOff(String token,Integer memberId);
server/services/src/main/java/com/doumee/service/business/ShopInfoService.java
@@ -193,10 +193,10 @@
    ShopLoginVO shopPasswordLogin(ShopLoginDTO dto);
    /**
     * 门店静默登录(根据openid)
     * @param openid
     * 门店静默登录(默认用户携带的openid进行查询)
     * @param memberId
     * @return 登录结果
     */
    ShopLoginVO shopSilentLogin(String openid);
    ShopLoginVO shopSilentLogin(Integer memberId);
}
server/services/src/main/java/com/doumee/service/business/impl/DriverInfoServiceImpl.java
@@ -141,6 +141,27 @@
        noticeService.create(notice);
    }
    /**
     * 发送司机站内信通知
     */
    private void sendDriverNotice(Integer driverId, Constants.DriverOrderNotify notify, Integer orderId, String... params) {
        DriverInfo driver = driverInfoMapper.selectById(driverId);
        if (driver == null || driver.getMemberId() == null) {
            return;
        }
        Notice notice = new Notice();
        notice.setUserType(1); // 1=司机
        notice.setUserId(driver.getMemberId());
        notice.setTitle(notify.getTitle());
        notice.setContent(notify.format(params));
        notice.setObjId(orderId);
        notice.setObjType(0); // 0=订单
        notice.setStatus(0);  // 0=未读
        notice.setIsdeleted(Constants.ZERO);
        notice.setCreateDate(new Date());
        noticeService.create(notice);
    }
    @Override
    public Integer create(DriverInfo driverInfo) {
        driverInfoMapper.insert(driverInfo);
@@ -1169,6 +1190,12 @@
        // 通知会员:司机变更
        sendOrderNotice(order.getMemberId(), Constants.MemberOrderNotify.DRIVER_CHANGED, orderId,
                "orderNo", order.getCode());
        // 通知司机:取消成功
        int remainLimit = limit - (todayCancelCount != null ? todayCancelCount.intValue() + 1 : 1);
        sendDriverNotice(driverId, Constants.DriverOrderNotify.CANCELLED, orderId,
                "orderNo", order.getCode(),
                "cancelLimit", String.valueOf(Math.max(remainLimit, 0)));
    }
    @Override
@@ -1243,6 +1270,12 @@
            sendShopNotice(order.getDepositShopId(), Constants.ShopOrderNotify.WAIT_PICKUP, orderId,
                    "orderNo", order.getCode());
        }
        // 通知司机:抢单成功
        String shopName = order.getDepositShopName() != null ? order.getDepositShopName() : order.getDepositLocation();
        sendDriverNotice(driverId, Constants.DriverOrderNotify.WAIT_DELIVER, orderId,
                "orderNo", order.getCode(),
                "shopName", shopName != null ? shopName : "");
    }
    @Override
@@ -1318,6 +1351,88 @@
                    "orderNo", order.getCode(),
                    "driverName", driver.getName());
        }
        // 通知司机:已取件配送中
        sendDriverNotice(driverId, Constants.DriverOrderNotify.DELIVERING, orderId,
                "orderNo", order.getCode());
    }
    @Override
    @Transactional
    public void confirmDeliver(Integer driverId, DriverDeliverDTO dto) {
        Integer orderId = dto.getOrderId();
        // 1. 校验司机
        DriverInfo driver = driverInfoMapper.selectById(driverId);
        if (driver == null) {
            throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "司机信息不存在");
        }
        // 2. 校验订单
        Orders order = ordersMapper.selectById(orderId);
        if (order == null || Constants.ONE.equals(order.getDeleted())) {
            throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "订单不存在");
        }
        if (!Constants.ONE.equals(order.getType())) {
            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(), "当前订单状态不允许确认送达");
        }
        if (!driverId.equals(order.getAcceptDriver())) {
            throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "无权操作该订单");
        }
        // 3. 保存送达图片
        Date now = new Date();
        if (dto.getImages() != null && !dto.getImages().isEmpty()) {
            int sortNum = 0;
            for (String url : dto.getImages()) {
                Multifile multifile = new Multifile();
                multifile.setObjId(orderId);
                multifile.setObjType(Constants.FileType.DRIVER_DONE.getKey());
                multifile.setType(Constants.ZERO);
                multifile.setFileurl(url);
                multifile.setIsdeleted(Constants.ZERO);
                multifile.setCreateDate(now);
                multifile.setSortnum(sortNum++);
                multifileMapper.insert(multifile);
            }
        }
        // 4. 更新订单状态为已送达(5)
        ordersMapper.update(new UpdateWrapper<Orders>().lambda()
                .set(Orders::getStatus, Constants.OrderStatus.arrived.getStatus())
                .set(Orders::getArriveTime, now)
                .set(Orders::getUpdateTime, now)
                .eq(Orders::getId, orderId));
        // 5. 写入操作日志
        OrderLog log = new OrderLog();
        log.setOrderId(orderId);
        log.setTitle("司机确认送达");
        log.setLogInfo(StringUtils.isNotBlank(dto.getRemark()) ? dto.getRemark() : "司机【" + driver.getName() + "】已送达");
        log.setObjType(Constants.ORDER_LOG_DRIVER_DELIVER);
        log.setOptUserId(driver.getMemberId());
        log.setOptUserType(Constants.ONE);
        log.setOrderStatus(Constants.OrderStatus.arrived.getStatus());
        log.setCreateTime(now);
        log.setDeleted(Constants.ZERO);
        orderLogMapper.insert(log);
        // 6. 通知会员:订单已送达(无取件门店)
        String destination = StringUtils.isNotBlank(order.getTakeShopAddress()) ? order.getTakeShopAddress() : "目的地";
        sendOrderNotice(order.getMemberId(), Constants.MemberOrderNotify.ARRIVED_NO_SHOP, orderId,
                "orderNo", order.getCode(),
                "destination", destination);
        // 通知司机:已送达
        sendDriverNotice(driverId, Constants.DriverOrderNotify.ARRIVED, orderId,
                "orderNo", order.getCode(),
                "destination", destination);
    }
    private List<String> getFileUrls(Integer orderId, int objType, String prefix) {
server/services/src/main/java/com/doumee/service/business/impl/MemberServiceImpl.java
@@ -447,9 +447,13 @@
    }
    @Override
    public void logOut(String token,Integer memberId){
        memberMapper.update(new UpdateWrapper<Member>().lambda().setSql(" openid = null ").eq(Member::getId,memberId));
        jwtTokenUtil.logoutForH5(token);
    public void logOut(String token,Integer userId,Integer userType){
        if(Constants.equalsInteger(userType,Constants.ZERO)){
            memberMapper.update(new UpdateWrapper<Member>().lambda().setSql(" openid = null ").eq(Member::getId,userId));
        }else if(Constants.equalsInteger(userType,Constants.TWO)){
            shopInfoMapper.update(new UpdateWrapper<ShopInfo>().lambda().setSql(" openid = null ").eq(ShopInfo::getId,userId));
        }
        jwtTokenUtil.logout(token);
    }
server/services/src/main/java/com/doumee/service/business/impl/OrdersServiceImpl.java
@@ -294,7 +294,7 @@
            return BigDecimal.ZERO;
        }
        String rateStr = systemDictDataBiz.queryByCode(Constants.OPERATION_CONFIG, Constants.OP_INSURANCE_RATE).getCode();
        BigDecimal rate = new BigDecimal(rateStr);
        BigDecimal rate = new BigDecimal(rateStr).divide(new BigDecimal("100"), 4, BigDecimal.ROUND_HALF_UP);
        return declaredValue.multiply(rate).setScale(2, BigDecimal.ROUND_HALF_UP);
    }
@@ -342,9 +342,11 @@
        List<Category> categories = categoryMapper.selectBatchIds(categoryIds);
        Map<Integer, String> categoryNameMap = new HashMap<>();
        Map<Integer, String> categoryDetailMap = new HashMap<>();
        Map<Integer, String> categoryOtherFieldMap = new HashMap<>();
        for (Category c : categories) {
            categoryNameMap.put(c.getId(), c.getName());
            categoryDetailMap.put(c.getId(), c.getDetail());
            categoryOtherFieldMap.put(c.getId(),c.getOtherField());
        }
        // 计算每项物品费用:小计 = 单价 × 数量 × 天数
@@ -364,7 +366,7 @@
            ItemPriceVO vo = new ItemPriceVO();
            vo.setCategoryId(item.getCategoryId());
            vo.setCategoryName(categoryNameMap.getOrDefault(item.getCategoryId(), ""));
            vo.setDetail(categoryDetailMap.get(item.getCategoryId()));
            vo.setDetail(categoryOtherFieldMap.get(item.getCategoryId()));
            vo.setQuantity(item.getQuantity());
            vo.setUnitPrice(unitPrice);
            vo.setLocallyPrice(unitPrice);
@@ -462,9 +464,11 @@
        List<Category> categories = categoryMapper.selectBatchIds(categoryIds);
        Map<Integer, String> categoryNameMap = new HashMap<>();
        Map<Integer, String> categoryDetailMap = new HashMap<>();
        Map<Integer, String> categoryOtherFieldMap = new HashMap<>();
        for (Category c : categories) {
            categoryNameMap.put(c.getId(), c.getName());
            categoryDetailMap.put(c.getId(), c.getDetail());
            categoryOtherFieldMap.put(c.getId(),c.getOtherField());
        }
        // 3. 逐项计算运费:起步价 + 超出部分阶梯价
@@ -503,7 +507,7 @@
            ItemPriceVO vo = new ItemPriceVO();
            vo.setCategoryId(item.getCategoryId());
            vo.setCategoryName(categoryNameMap.getOrDefault(item.getCategoryId(), ""));
            vo.setDetail(categoryDetailMap.get(item.getCategoryId()));
            vo.setDetail(categoryOtherFieldMap.get(item.getCategoryId()));
            vo.setQuantity(item.getQuantity());
            vo.setUnitPrice(unitPrice);
            vo.setLocallyPrice(locallyPrice);
@@ -1189,9 +1193,9 @@
                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));
                long unitPriceFen = d.getUnitPrice() != null ? d.getUnitPrice() : 0L;
                item.setUnitPrice(unitPriceFen);
                item.setSubtotal(unitPriceFen * (d.getNum() != null ? d.getNum() : 0));
                items.add(item);
            }
        }
@@ -1375,16 +1379,22 @@
                vo.setCode(o.getCode());
                vo.setType(o.getType());
                vo.setStatus(o.getStatus());
                vo.setStatusName(Constants.OrderStatus.getDescByKey(o.getStatus(),
                        Constants.equalsInteger(o.getType(), Constants.ZERO)?o.getType():Objects.nonNull(o.getTakeShopId())?Constants.ONE:Constants.TWO)
                );
                vo.setCreateTime(o.getCreateTime());
                vo.setExpectedTakeTime(o.getExpectedTakeTime());
                vo.setMemberVerifyCode(o.getMemberVerifyCode());
                // 存件门店(关联查询直接取值)
                vo.setDepositShopId(o.getDepositShopId());
                vo.setDepositShopName(o.getDepositShopName());
                vo.setDepositShopLinkName(o.getDepositShopLinkName());
                vo.setDepositShopPhone(o.getDepositShopLinkPhone());
                // 取件信息:有取件门店取门店,无则取用户自选取件点
                if (o.getTakeShopId() != null) {
                    vo.setTakeShopId(o.getTakeShopId());
                    vo.setTakeShopName(o.getTakeShopName());
                    vo.setTakeShopAddress(o.getTakeShopAddress());
                } else {
@@ -1399,6 +1409,9 @@
                // 费用(分)
                vo.setDeclaredFee(o.getDeclaredFee());
                vo.setEstimatedAmount(o.getEstimatedAmount());
                // 评价状态
                vo.setCommentStatus(o.getCommentStatus());
                // 查询物品明细(一次查询,同时用于物品列表和逾期计算)
                List<OrdersDetail> details = ordersDetailMapper.selectList(
@@ -1472,6 +1485,9 @@
                vo.setCode(o.getCode());
                vo.setType(o.getType());
                vo.setStatus(o.getStatus());
                vo.setStatusName(Constants.OrderStatus.getDescByKey(o.getStatus(),
                        Constants.equalsInteger(o.getType(), Constants.ZERO)?o.getType():Objects.nonNull(o.getTakeShopId())?Constants.ONE:Constants.TWO)
                );
                vo.setCreateTime(o.getCreateTime());
                vo.setExpectedTakeTime(o.getExpectedTakeTime());
@@ -1687,11 +1703,6 @@
            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(), "订单状态异常");
@@ -1699,7 +1710,7 @@
        Date now = new Date();
        // 待支付:直接取消
        // 待支付:直接取消(不限订单类型)
        if (Constants.equalsInteger(status, Constants.OrderStatus.waitPay.getStatus())) {
            order.setStatus(Constants.OrderStatus.cancelled.getStatus());
            order.setCancelTime(now);
@@ -1708,7 +1719,7 @@
            return;
        }
        // 待寄存:直接取消,全额退款
        // 待寄存:直接取消,全额退款(不限订单类型)
        if (Constants.equalsInteger(status, Constants.OrderStatus.waitDeposit.getStatus())) {
            // 记录退款信息
            OrdersRefund refund = new OrdersRefund();
@@ -1737,6 +1748,11 @@
            return;
        }
        // 已寄存/已接单:仅异地寄存可取消
        if (!Constants.equalsInteger(order.getType(), Constants.ONE)) {
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "仅异地寄存订单可取消");
        }
        // 已寄存/已接单:进入取消中状态
        if (Constants.equalsInteger(status, Constants.OrderStatus.deposited.getStatus())
                || Constants.equalsInteger(status, Constants.OrderStatus.accepted.getStatus())) {
@@ -1747,6 +1763,11 @@
            // 通知存件门店:退款申请
            if (order.getDepositShopId() != null) {
                sendShopNotice(order.getDepositShopId(), Constants.ShopOrderNotify.REFUNDING, orderId,
                        "orderNo", order.getCode());
            }
            // 通知司机:订单退款中(已接单情况下司机需停止服务)
            if (order.getAcceptDriver() != null && Constants.equalsInteger(order.getStatus(), Constants.OrderStatus.accepted.getStatus())) {
                sendDriverNotice(order.getAcceptDriver(), Constants.DriverOrderNotify.REFUNDING, orderId,
                        "orderNo", order.getCode());
            }
            return;
@@ -1833,6 +1854,27 @@
        if (order.getTakeShopId() != null) {
            sendShopNotice(order.getTakeShopId(), notify, order.getId(), params);
        }
    }
    /**
     * 发送司机站内信通知
     */
    private void sendDriverNotice(Integer driverId, Constants.DriverOrderNotify notify, Integer orderId, String... params) {
        DriverInfo driver = driverInfoMapper.selectById(driverId);
        if (driver == null || driver.getMemberId() == null) {
            return;
        }
        Notice notice = new Notice();
        notice.setUserType(1); // 1=司机
        notice.setUserId(driver.getMemberId());
        notice.setTitle(notify.getTitle());
        notice.setContent(notify.format(params));
        notice.setObjId(orderId);
        notice.setObjType(0); // 0=订单
        notice.setStatus(0);  // 0=未读
        notice.setIsdeleted(Constants.ZERO);
        notice.setCreateDate(new Date());
        noticeService.create(notice);
    }
    @Override
@@ -2178,6 +2220,14 @@
                    "orderNo", order.getCode(),
                    "amount", String.valueOf(Constants.getFormatMoney(
                            order.getTotalAmount() != null ? order.getTotalAmount() : 0L)));
            // 通知司机:订单已结算
            if (order.getAcceptDriver() != null) {
                sendDriverNotice(order.getAcceptDriver(), Constants.DriverOrderNotify.SETTLED, order.getId(),
                        "orderNo", order.getCode(),
                        "amount", String.valueOf(Constants.getFormatMoney(
                                order.getDriverFee() != null ? order.getDriverFee() : 0L)));
            }
        }
    }
@@ -2282,6 +2332,12 @@
        // 通知存件门店和取件门店:订单已评价
        notifyBothShops(order, Constants.ShopOrderNotify.EVALUATED,
                "orderNo", order.getCode());
        // 通知司机:订单已评价
        if (order.getAcceptDriver() != null) {
            sendDriverNotice(order.getAcceptDriver(), Constants.DriverOrderNotify.EVALUATED, order.getId(),
                    "orderNo", order.getCode());
        }
    }
    /**
@@ -2424,6 +2480,12 @@
            notifyBothShops(order, Constants.ShopOrderNotify.FINISHED,
                    "orderNo", order.getCode(),
                    "settleDays", settleDays != null ? settleDays : "7");
            // 通知司机:订单已完成
            if (order.getAcceptDriver() != null) {
                sendDriverNotice(order.getAcceptDriver(), Constants.DriverOrderNotify.FINISHED, order.getId(),
                        "orderNo", order.getCode(),
                        "settleDays", settleDays != null ? settleDays : "7");
            }
        } else {
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "当前订单状态不允许核销");
        }
@@ -2529,6 +2591,12 @@
        notifyBothShops(order, Constants.ShopOrderNotify.FINISHED,
                "orderNo", order.getCode(),
                "settleDays", settleDays != null ? settleDays : "7");
        // 通知司机:订单已完成
        if (order.getAcceptDriver() != null) {
            sendDriverNotice(order.getAcceptDriver(), Constants.DriverOrderNotify.FINISHED, order.getId(),
                    "orderNo", order.getCode(),
                    "settleDays", settleDays != null ? settleDays : "7");
        }
    }
    @Override
@@ -2575,6 +2643,12 @@
        notifyBothShops(order, Constants.ShopOrderNotify.FINISHED,
                "orderNo", order.getCode(),
                "settleDays", settleDays != null ? settleDays : "7");
        // 通知司机:订单已完成
        if (order.getAcceptDriver() != null) {
            sendDriverNotice(order.getAcceptDriver(), Constants.DriverOrderNotify.FINISHED, order.getId(),
                    "orderNo", order.getCode(),
                    "settleDays", settleDays != null ? settleDays : "7");
        }
    }
    @Override
server/services/src/main/java/com/doumee/service/business/impl/ShopInfoServiceImpl.java
@@ -1059,12 +1059,13 @@
    }
    @Override
    public ShopLoginVO shopSilentLogin(String openid) {
        if (StringUtils.isBlank(openid)) {
            throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "openid不能为空");
    public ShopLoginVO shopSilentLogin(Integer memberId) {
        Member member = memberMapper.selectById(memberId);
        if(Objects.isNull(member)||StringUtils.isBlank(member.getOpenid())){
            throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "当前登录会员身份异常,请联系管理员!");
        }
        ShopInfo shop = shopInfoMapper.selectOne(new QueryWrapper<ShopInfo>().lambda()
                .eq(ShopInfo::getOpenid, openid)
                .eq(ShopInfo::getOpenid, member.getOpenid())
                .eq(ShopInfo::getDeleted, Constants.ZERO)
                .last("limit 1"));
        if (shop == null) {
server/services/src/main/java/com/doumee/service/business/impl/WithdrawalOrdersServiceImpl.java
@@ -258,7 +258,7 @@
        String doneInfo = null;
        if (Constants.ONE.equals(dto.getStatus())) {
            String aliAccount = order.getAliAccount();
            String aliName = null;
            String aliName = order.getAliName();
            // 从司机或门店获取支付宝账户和实名姓名
            if (StringUtils.isBlank(aliAccount)) {
@@ -478,6 +478,7 @@
        order.setType(Constants.ZERO);
        order.setOutBillNo(billNo);
        order.setAliAccount(dto.getAliAccount());
        order.setAliName(driver.getAliName());
        order.setDeleted(Constants.ZERO);
        order.setCreateTime(now);
        order.setUpdateTime(now);
@@ -529,6 +530,7 @@
        order.setType(Constants.ZERO);
        order.setOutBillNo(billNo);
        order.setAliAccount(dto.getAliAccount());
        order.setAliName(shop.getAliName());
        order.setDeleted(Constants.ZERO);
        order.setCreateTime(now);
        order.setUpdateTime(now);
server/services/src/main/resources/application-dev.yml
@@ -83,34 +83,15 @@
########################微信支付相关配置########################
wx:
  pay:
#    appId: wxcd2b89fd2ff065f8
#    appSecret: 3462fa186da7cb06c544df8d8664b63a
#    mchId: 1229817002
#    mchKey: u4TSNtv0wFP7WRfnxBgijYOtRhS9FvlM
#    notifyUrl: http://xiaopiqiu2.natapp1.cc/web/api/wxPayNotify
#    keyPath: D:\DouMee\dmkjWxcert\apiclient_cert.p12
    appId: wxb1b59320e803dc6c
    appSecret: eb93785c7bca3f0ff0364b0e26bfeb59
    mchId: 1229817002    #商户号
    mchKey: u4TSNtv0wFP7WRfnxBgijYOtRhS9FvlM #商户秘钥
    apiV3Key: 7tG4Vk9Zp2L8dXw5Jq0N3hR6yE1sF3cB #apiV3Key
    serialNumer: 3FE90C2F3D40A56E1C51926F31B8A8D22426CCE0 #商户证书序列号
    publicKeyId: PUB_KEY_ID_0112298170022025071700291836000600
    pubKeyPath: D:\DouMee\1229817002_20220310_cert\pub_key.pem #商户支付公钥
    keyPath: D:\DouMee\1229817002_20220310_cert\apiclient_cert.p12
    privateCertPath: D:\DouMee\1229817002_20220310_cert\apiclient_cert.pem
    privateKeyPath: D:\DouMee\1229817002_20220310_cert\apiclient_key.pem
    notifyUrl: http://xiaopiqiu2.natapp1.cc/web/wxPayNotify
    refundNotifyUrl: http://xiaopiqiu2.natapp1.cc/web/wxRefundNotify
    mchId: 1629568742    #商户号
    apiV3Key: NJTLJSTZYXZRGScaiwubuzichanbu666 #apiV3Key
    serialNumer: 25D19D18217F4588841E5CD1AA0D1533DE8AF84A #商户证书序列号
    privateKeyPath: pay/pro/wx/apiclient_key.pem
    v3NotifyUrl: http://xiaopiqiu2.natapp1.cc/web/api/wxPayV3Notify
    v3RefundNotifyUrl: http://xiaopiqiu2.natapp1.cc/web/api/wxRefundV3Notify
#    appId: wx6264b4f3a697cbe8
#    appSecret: 23734577e8978138c946b727f0394027
#    mchId: 1629568742
#    mchKey: NJTLJSTZYXZRGScaiwubuzichanbu666
#    notifyUrl: https://test.doumee.cn/dmmall_web_api/web/api/wxPayNotify
#    keyPath: D:\DouMee\gtxljcWxcert\apiclient_cert.p12
alipay:
  pay:
server/web/src/main/java/com/doumee/api/web/AccountApi.java
@@ -3,6 +3,7 @@
import com.doumee.config.jwt.JwtTokenUtil;
import com.doumee.core.annotation.LoginRequired;
import com.doumee.core.annotation.trace.Trace;
import com.doumee.core.constants.Constants;
import com.doumee.core.model.ApiResponse;
import com.doumee.dao.business.model.Member;
import com.doumee.dao.dto.ShopLoginDTO;
@@ -68,27 +69,42 @@
        return ApiResponse.success("操作成功", shopInfoService.shopPasswordLogin(dto));
    }
    @LoginRequired
    @ApiOperation(value = "门店静默登录", notes = "根据openid自动登录门店,未绑定则返回空")
    @GetMapping("/shopSilentLogin")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "query", dataType = "String", name = "openid", value = "微信openid", required = true)
            @ApiImplicitParam(paramType = "header", dataType = "String", name = "token", value = "用户token值", required = true),
    })
    public ApiResponse<ShopLoginVO> shopSilentLogin(@RequestParam String openid) {
        return ApiResponse.success("操作成功", shopInfoService.shopSilentLogin(openid));
    public ApiResponse<ShopLoginVO> shopSilentLogin() {
        return ApiResponse.success("操作成功", shopInfoService.shopSilentLogin(getMemberId()));
    }
    @LoginRequired
    @ApiOperation(value = "退出登录", notes = "小程序端")
    @ApiOperation(value = "会员退出登录", notes = "小程序端")
    @GetMapping("/logOut")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", dataType = "String", name = "token", value = "用户token值", required = true),
    })
    public ApiResponse logOut() {
        String token = this.getRequest().getHeader(JwtTokenUtil.HEADER_KEY);
        memberService.logOut(token,getMemberId());
        memberService.logOut(token,getMemberId(), Constants.ZERO);
        return  ApiResponse.success("操作成功");
    }
    @LoginRequired
    @ApiOperation(value = "门店退出登录", notes = "小程序端")
    @GetMapping("/logOutShop")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", dataType = "String", name = "token", value = "用户token值", required = true),
    })
    public ApiResponse logOutShop() {
        String token = this.getRequest().getHeader(JwtTokenUtil.HEADER_KEY);
        memberService.logOut(token,getShopId(), Constants.TWO);
        return  ApiResponse.success("操作成功");
    }
    @LoginRequired
    @ApiOperation(value = "用户注销", notes = "小程序端")
    @GetMapping("/logOff")
server/web/src/main/java/com/doumee/api/web/DriverInfoApi.java
@@ -10,6 +10,7 @@
import com.doumee.dao.dto.DriverActiveOrderDTO;
import com.doumee.dao.dto.DriverGrabOrderDTO;
import com.doumee.dao.dto.DriverLoginRequest;
import com.doumee.dao.dto.DriverDeliverDTO;
import com.doumee.dao.dto.DriverPickupDTO;
import com.doumee.dao.dto.DriverRegisterRequest;
import com.doumee.dao.dto.DriverVerifyRequest;
@@ -202,4 +203,16 @@
        return ApiResponse.success("操作成功");
    }
    @LoginDriverRequired
    @Trace
    @ApiOperation(value = "司机确认送达", notes = "异地寄存无取件门店订单,派送中(status=4)时确认送达,订单进入已送达(status=5)")
    @PostMapping("/confirmDeliver")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", dataType = "String", name = "token", value = "用户token值", required = true)
    })
    public ApiResponse confirmDeliver(@RequestBody @Valid DriverDeliverDTO dto) {
        driverInfoService.confirmDeliver(this.getDriverId(), dto);
        return ApiResponse.success("操作成功");
    }
}
server/web/src/main/java/com/doumee/api/web/OrdersApi.java
@@ -216,11 +216,11 @@
    @LoginRequired
    @ApiOperation(value = "会员确认收货", notes = "异地寄存且无取件门店的订单,送达后确认收货标记订单完成")
    @PostMapping("/confirmReceipt")
    @PostMapping("/confirmReceipt/{orderId}")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", dataType = "String", name = "token", value = "用户token值", required = true)
    })
    public ApiResponse confirmReceipt(@RequestParam Integer orderId) {
    public ApiResponse confirmReceipt(@PathVariable Integer orderId) {
        ordersService.memberConfirmReceipt(orderId, getMemberId());
        return ApiResponse.success("确认收货成功");
    }
server/web/src/main/resources/application.yml
@@ -3,7 +3,7 @@
  port: 10011
# 项目信息配置
project:
  name: 近快
  name: 高铁行李寄存
  version: 1.0.0
  # 环境,生产环境production,开发环境development
  env: development