MrShi
8 天以前 6f7f24efb43a64c79325a425dcd3e507d55bf876
Merge branch 'master' of http://139.186.142.91:10010/r/productDev/gtzxinglijicun
已添加5个文件
已修改57个文件
2473 ■■■■ 文件已修改
server/admin/src/main/java/com/doumee/api/business/OrdersController.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/admin/src/main/resources/application.yml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/api/common/PublicCloudController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/config/jwt/JwtTokenUtil.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/config/jwt/WebMvcConfig.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/core/constants/Constants.java 73 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/core/constants/ResponseStatus.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/core/utils/HttpsUtil.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/core/utils/jpush/JPushUtil.java 239 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/business/model/DriverInfo.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/business/model/OrderLog.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/business/model/Orders.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/business/model/OrdersRefund.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/dto/ConfirmArriveDTO.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/dto/DriverOrderPageDTO.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/dto/DriverPickupDTO.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/dto/DriverVerifyRequest.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/dto/MyOrderDTO.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/dto/ShopInfoMaintainDTO.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/dto/ShopVerifyDTO.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/vo/ActiveOrderTipVO.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/vo/DriverActiveOrderCountVO.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/vo/DriverCancelLimitVO.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/vo/DriverCenterVO.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/vo/DriverGrabOrderVO.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/vo/DriverOrderDetailVO.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/vo/DriverStatsVO.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/vo/MyOrderDetailVO.java 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/vo/MyOrderVO.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/vo/PlatformAboutVO.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/vo/ShopDetailVO.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/DriverInfoService.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/OrdersService.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/ShopInfoService.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/impl/AreasServiceImpl.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/impl/BannerServiceImpl.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/impl/CategoryServiceImpl.java 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/impl/DriverInfoServiceImpl.java 374 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/impl/MemberServiceImpl.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/impl/OrderLogServiceImpl.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/impl/OrdersServiceImpl.java 518 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/impl/RevenueServiceImpl.java 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/impl/ShopInfoServiceImpl.java 136 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/impl/WithdrawalOrdersServiceImpl.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/resources/application-dev.yml 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/resources/application-pro.yml 58 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/web/src/main/java/com/doumee/api/web/AccountApi.java 17 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/web/src/main/java/com/doumee/api/web/ConfigApi.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/web/src/main/java/com/doumee/api/web/DriverInfoApi.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/web/src/main/java/com/doumee/api/web/OrdersApi.java 21 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/web/src/main/java/com/doumee/api/web/PaymentCallback.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/web/src/main/java/com/doumee/api/web/RevenueApi.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/web/src/main/java/com/doumee/api/web/ShopInfoApi.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/web/src/main/java/com/doumee/api/web/WalletApi.java 29 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/web/src/main/resources/application.yml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
small-program/pages/delivery-order-detail/delivery-order-detail.vue 274 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
small-program/pages/evaluate/evaluate.vue 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
small-program/pages/itinerary/itinerary.vue 41 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
small-program/pages/luggage-storage/luggage-storage.vue 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
small-program/pages/mine/mine.vue 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
small-program/pages/payment-success/payment-success.vue 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
small-program/utils/http.api.js 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/admin/src/main/java/com/doumee/api/business/OrdersController.java
@@ -114,14 +114,6 @@
        return ApiResponse.success(null);
    }
    @ApiOperation("确认顾客到店")
    @PostMapping("/confirmArrived")
    @RequiresPermissions("business:orders:update")
    public ApiResponse confirmArrived(@RequestBody ConfirmArriveDTO dto) {
        ordersService.confirmCustomerArrived(dto.getOrderId(), dto.getShopId());
        return ApiResponse.success(null);
    }
    @ApiOperation("手动触发订单结算")
    @PostMapping("/settle")
    @RequiresPermissions("business:orders:update")
server/admin/src/main/resources/application.yml
@@ -12,7 +12,7 @@
spring:
  profiles:
    active: dev
    active: pro
  # JSON返回配置
  jackson:
    # é»˜è®¤æ—¶åŒº
server/services/src/main/java/com/doumee/api/common/PublicCloudController.java
@@ -84,7 +84,7 @@
                    // ç»çº¬åº¦æ°´å°å¤„理
                    InputStream uploadStream = is;
                    if (latitude != null && longitude != null) {
                    if (latitude != null && longitude != null && latitude > 0 && longitude > 0) {
                        try {
                            JSONObject geoResult = MapUtil.reverseGeocode(latitude, longitude);
                            String address = geoResult != null ? geoResult.getString("formatted_addresses") : "";
server/services/src/main/java/com/doumee/config/jwt/JwtTokenUtil.java
@@ -27,6 +27,10 @@
    public static final String HEADER_KEY = "token";
    public static final String SHOP_HEADER_KEY = "shopToken";
    public static final String MEMBER_ID = "MEMBER_ID";
    public static final String SHOP_ID = "SHOP_ID";
server/services/src/main/java/com/doumee/config/jwt/WebMvcConfig.java
@@ -10,6 +10,7 @@
import com.doumee.dao.business.model.Member;
import com.doumee.dao.business.model.ShopInfo;
import io.jsonwebtoken.JwtException;
import lombok.extern.log4j.Log4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
@@ -26,6 +27,7 @@
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Objects;
import java.util.logging.Logger;
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@@ -74,19 +76,19 @@
                    }
                }else if (beanType.isAnnotationPresent(LoginShopRequired.class)) {
                    //获取token
                    String token = request.getHeader(JwtTokenUtil.HEADER_KEY);  // ä»Ž http è¯·æ±‚头中取出 token
                    String token = request.getHeader(JwtTokenUtil.SHOP_HEADER_KEY);  // ä»Ž http è¯·æ±‚头中取出 token
                    if (StringUtils.isNotBlank(token)) {
                        checkShopLogin(request,response);
                        checkShopLogin(token,request,response);
                    } else {
                        throw new BusinessException(ResponseStatus.BE_OVERDUE.getCode(),"未登录");
                        throw new BusinessException(ResponseStatus.SHOP_BE_OVERDUE.getCode(),"未登录");
                    }
                }else if (handlerMethod.hasMethodAnnotation(LoginShopRequired.class)){
                    //获取token
                    String token = request.getHeader(JwtTokenUtil.HEADER_KEY);  // ä»Ž http è¯·æ±‚头中取出 token
                    String token = request.getHeader(JwtTokenUtil.SHOP_HEADER_KEY);  // ä»Ž http è¯·æ±‚头中取出 token
                    if (StringUtils.isNotBlank(token)) {
                        checkShopLogin(request,response);
                        checkShopLogin(token,request,response);
                    } else {
                        throw new BusinessException(ResponseStatus.BE_OVERDUE.getCode(),"未登录");
                        throw new BusinessException(ResponseStatus.SHOP_BE_OVERDUE.getCode(),"未登录");
                    }
                }else if (beanType.isAnnotationPresent(LoginDriverRequired.class)) {
                    //获取token
@@ -115,6 +117,7 @@
    public Boolean checkMemberLogin(HttpServletRequest request, HttpServletResponse response){
        String token = request.getHeader(JwtTokenUtil.HEADER_KEY);
        System.out.println("会员token:=========>{}"+token);
        try {
            if(!token.startsWith(Constants.ZERO+"")){
                throw new BusinessException(ResponseStatus.TOKEN_EXCEED_TIME);
@@ -144,19 +147,19 @@
    }
    public Boolean checkShopLogin(HttpServletRequest request, HttpServletResponse response){
        String token = request.getHeader(JwtTokenUtil.HEADER_KEY);
    public Boolean checkShopLogin(String token,HttpServletRequest request, HttpServletResponse response){
        System.out.println("门店token:=========>{}"+token);
        try {
            if(!token.startsWith(Constants.TWO+"")){
                throw new BusinessException(ResponseStatus.TOKEN_EXCEED_TIME);
                throw new BusinessException(ResponseStatus.SHOP_TOKEN_EXCEED_TIME);
            }
            String tokenRedis = (String) redisTemplate.opsForValue().get(Constants.REDIS_TOKEN_KEY+token);
            if(StringUtils.isBlank(tokenRedis)){
                throw new BusinessException(ResponseStatus.BE_OVERDUE);
                throw new BusinessException(ResponseStatus.SHOP_BE_OVERDUE);
            }
            ShopInfo shop = JSONObject.parseObject(tokenRedis, ShopInfo.class);
            if(Objects.isNull(shop)){
                throw new BusinessException(ResponseStatus.BE_OVERDUE);
                throw new BusinessException(ResponseStatus.SHOP_BE_OVERDUE);
            }
            String openid = shop.getOpenid();
            Integer shopId = getTokenId(token);
@@ -170,22 +173,23 @@
            }
            String dbOpenid = dao.queryForObject(" select ifnull(openid,'')  from shop_info where id  = ?", String.class, shopId);
            if(StringUtils.isBlank(dbOpenid)||!openid.equals(dbOpenid)){
                throw new BusinessException(ResponseStatus.TOKEN_EXCEED_TIME);
                throw new BusinessException(ResponseStatus.SHOP_TOKEN_EXCEED_TIME);
            }
            Integer count = dao.queryForObject("select count(1) from shop_info where id  = ?", Integer.class, shopId);
            if (count != null && count > 0) {
                request.setAttribute(JwtTokenUtil.SHOP_ID, shop.getId());
                return true;
            }else{
                throw new BusinessException(ResponseStatus.BE_OVERDUE.getCode(),"用户信息出错");
                throw new BusinessException(ResponseStatus.SHOP_BE_OVERDUE.getCode(),"用户信息出错");
            }
        } catch (IllegalArgumentException | JwtException e) {
            throw new BusinessException(ResponseStatus.BE_OVERDUE);
            throw new BusinessException(ResponseStatus.SHOP_BE_OVERDUE);
        }
    }
    public Boolean checkDriverLogin(HttpServletRequest request, HttpServletResponse response){
        String token = request.getHeader(JwtTokenUtil.HEADER_KEY);
        System.out.println("司机token:=========>{}"+token);
        try {
            if(!token.startsWith(Constants.ONE+"")){
                throw new BusinessException(ResponseStatus.TOKEN_EXCEED_TIME);
server/services/src/main/java/com/doumee/core/constants/Constants.java
@@ -50,6 +50,22 @@
    public static final String USER_AGREEMENT ="USER_AGREEMENT" ;
    public static final String PRIVACY_AGREEMENT ="PRIVACY_AGREEMENT" ;
    public static final String STORE_RISK_COMMITMENT = "STORE_RISK_COMMITMENT";
    public static final String STORE_COOPERATION_AGREEMENT = "STORE_COOPERATION_AGREEMENT";
    public static final String STORE_LUGGAGE_STORAGE_NOTICE = "STORE_LUGGAGE_STORAGE_NOTICE";
    public static final String STORE_PRIVACY_POLICY = "STORE_PRIVACY_POLICY";
    public static final String OWNER_LUGGAGE_STORAGE_NOTICE = "OWNER_LUGGAGE_STORAGE_NOTICE";
    public static final String OWNER_SERVICE_AGREEMENT = "OWNER_SERVICE_AGREEMENT";
    public static final String OWNER_RISK_COMMITMENT = "OWNER_RISK_COMMITMENT";
    public static final String ERRAND_RISK_COMMITMENT = "ERRAND_RISK_COMMITMENT";
    public static final String ERRAND_SERVICE_AGREEMENT = "ERRAND_SERVICE_AGREEMENT";
    public static final String ERRAND_LUGGAGE_STORAGE_NOTICE = "ERRAND_LUGGAGE_STORAGE_NOTICE";
    public static final String USER_SERVICE_AGREEMENT = "USER_SERVICE_AGREEMENT";
    public static final String USER_PRIVACY_POLICY = "USER_PRIVACY_POLICY";
    public static final String DRIVER_PRIVACY_POLICY = "DRIVER_PRIVACY_POLICY";
    public static final String PRICE_DESCRIPTION = "PRICE_DESCRIPTION";
    public static final String PROHIBITED_ITEMS = "PROHIBITED_ITEMS";
    public static final String ACCESS_ID="ACCESS_ID";
    public static final String BUCKETNAME = "BUCKETNAME";
@@ -64,6 +80,7 @@
    public static final Integer FOUR = 4;
    public static final Integer SIX = 6;
    public static final Integer FIVE = 5;
    public static final Integer SEVEN = 7;
    public static final String INENTITY_FILES = "INENTITY_FILES";
    public static final String MEMBER_FILES = "MEMBER_FILES";
    public static final String CATEGORY_FILES = "CATEGORY_FILES";
@@ -97,14 +114,6 @@
    public final static String GOODS_ORDER_CREATE_LOCK = "goods:order:create:lock:";
    // è®¢å•日志操作类型
    public static final int ORDER_LOG_DISPATCH = 1;         // æ´¾å•
    public static final int ORDER_LOG_URGENT_FEE = 2;       // åŠ æ€¥è´¹
    public static final int ORDER_LOG_ASSIGN_DRIVER = 3;    // æŒ‡æ´¾å¸æœº
    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";
@@ -311,12 +320,36 @@
    @Getter
    @AllArgsConstructor
    public enum OrderLogType {
        urgent(0, "平台加急", "平台加急,奖励金 {param} å…ƒã€‚"),
        dispatch(1, "平台指派", "平台指派司机 {param} æŽ¥å•。")
        urgent(2, "平台加急", "平台加急,奖励金【{param}】元"),
        assignDriver(3, "平台指派", "平台指派司机【{param}】接单,奖励金【{param1}】元"),
        memberCancel(4, "会员取消订单", "{param}"),
        driverCancel(4, "司机取消订单", "{param}"),
        systemCancel(4, "系统自动取消", "{param}"),
        systemComplete(4, "系统自动完成", "{param}"),
        memberCancelToConvert(4, "会员取消异地寄存", "{param}"),
        shopDeposit(8, "门店确认寄存", "门店【{param}】确认寄存"),
        shopTake(9, "门店确认取件", "门店【{param}】确认取件,订单完成"),
        shopOutStock(10, "门店确认出库", "{param}"),
        shopConfirmArriveOverdue(5, "确认顾客到店(逾期)", "{param}"),
        shopConfirmArrive(5, "确认顾客到店", "{param}"),
        driverGrab(6, "司机抢单", "司机【{param}】抢单成功"),
        driverPickup(6, "司机完成取件", "司机【{param}】完成取件,开始派送"),
        driverDeliver(7, "司机确认送达", "{param}"),
        ;
        private int status;
        private String title;
        private String statusInfo;
        private final int status;
        private final String title;
        private final String statusInfo;
        public String format(String... params) {
            String result = statusInfo;
            if (params != null && params.length > 0) {
                result = result.replace("{param}", params[0] != null ? params[0] : "");
            }
            for (int i = 1; i < params.length; i++) {
                result = result.replace("{param" + i + "}", params[i] != null ? params[i] : "");
            }
            return result;
        }
    }
    /**
@@ -376,7 +409,7 @@
                        if(Constants.equalsInteger(type,Constants.ZERO)){
                            return "待取件";
                        }else{
                            return Constants.equalsInteger(type,Constants.ONE)?"已到店":"已送达";
                            return "已送达";
                        }
                    }
                    return c.getValue();
@@ -397,9 +430,11 @@
        waitDeliver(2, "待配送", new int[]{OrderStatus.accepted.status}),
        waitReceive(3, "待收货", new int[]{ OrderStatus.delivering.status, OrderStatus.arrived.status}),
        finished(4, "已完成", new int[]{OrderStatus.finished.status}),
        refund(5, "退款", new int[]{OrderStatus.cancelled.status}),
        refund(5, "取消", new int[]{OrderStatus.cancelled.status}),
        home(6, "首页查询", new int[]{OrderStatus.waitPay.status, OrderStatus.waitDeposit.status, OrderStatus.deposited.status
                , OrderStatus.accepted.status, OrderStatus.delivering.status, OrderStatus.arrived.status})
                , OrderStatus.accepted.status, OrderStatus.delivering.status, OrderStatus.arrived.status}),
        shopHome(7, "门店首页查询", new int[]{OrderStatus.waitDeposit.status, OrderStatus.deposited.status
                , OrderStatus.delivering.status, OrderStatus.arrived.status})
        ;
        private final int key;
        private final String desc;
@@ -575,6 +610,12 @@
        MEMBER_ARRIVED("SMS_505875004", "会员端-已送达", "您的行李订单:{orderNo}已送到{address},请及时取件,取件码:{code}。"),
        MEMBER_DELIVERING("SMS_505935002", "会员端-配送中", "您的行李订单:{orderNo}已由司机{name}取件,正运往目的地。"),
        VERIFY_CODE("SMS_333770877", "验证码短信", "您的验证码为:{code},请勿泄露于他人!"),
        DRIVER_AUTH_REJECTED("SMS_505790115", "司机端-司机认证被拒绝", "尊敬的{driver},很遗憾,您的司机认证未通过审核。原因:{reason}。您可修改资料后重新提交。"),
        DRIVER_AUTH_APPROVED("SMS_505885083", "司机端-司机认证通过", "尊敬的{driver},恭喜您已通过平台司机认证审核。您可登录司机端APP开始接单,配送过程中请注意安全,祝您接单顺利!"),
        DRIVER_URGENT_DISPATCH("SMS_505885082", "司机端-加急派单", "您好,您有一个新的行李订单(编号:{orderNo})。起点:{address1},终点:{address2},配送费{money1}元(含加急费{money2}元)。请尽快确认订单任务。"),
        SHOP_AUTH_REJECTED("SMS_505925106", "门店端-资料审核被拒绝", "很遗憾,您的门店\"{storeName}\"未通过审核,原因:{reason},您可修改资料后重新提交。"),
        SHOP_AUTH_APPROVED_DEPOSIT("SMS_505705111", "门店端-审核通过需缴纳押金", "恭喜您!您的门店\"{storeName}\"已通过初步审核。请支付押金{money}元以完成入驻,支付后即可登录门店后台正式接单。"),
        SHOP_AUTH_SUCCESS("SMS_505810110", "门店端-成功入驻通知", "恭喜您!您的门店\"{storeName}\"已通过平台审核,正式入驻成功。您可登录商家后台开始接单,账号:{phone},初始密码:{password}(建议首次登录后修改)。"),
        ;
        private final String templateCode;
server/services/src/main/java/com/doumee/core/constants/ResponseStatus.java
@@ -30,6 +30,9 @@
    NOT_ALLOWED(5110, "不允许的操作"),
    BE_OVERDUE(5112, "未登录"),
    TOKEN_EXCEED_TIME(5113, "对不起,登录已失效!"),
    SHOP_BE_OVERDUE(51121, "未登录"),
    SHOP_TOKEN_EXCEED_TIME(51131, "对不起,登录已失效!"),
    ;
    private int code;
server/services/src/main/java/com/doumee/core/utils/HttpsUtil.java
@@ -26,6 +26,13 @@
            return connectionHttp(url, "POST", data, "application/json");
        }
    }
    public static String postJson(String url, String data, String authorization) {
        if(url.startsWith("https://")){
            return connection(url, "POST", data, "application/json", true, authorization);
        }else{
            return connectionHttp(url, "POST", data, "application/json", authorization);
        }
    }
    public static String uploadTempMedia(String urlString ,String fileUrl){
        HttpsURLConnection conn= null;
        try {
@@ -101,6 +108,9 @@
    }
    public static String connection(String url,String method,String data,String contentType,boolean ignoreSSL){
        return connection(url, method, data, contentType, ignoreSSL, null);
    }
    public static String connection(String url,String method,String data,String contentType,boolean ignoreSSL, String authorization){
        HttpsURLConnection connection = null;
        try {
            URL _url = new URL(url);
@@ -111,6 +121,9 @@
            connection.setUseCaches(false);
            if(contentType != null){
                connection.setRequestProperty("Content-Type", contentType);
            }
            if(authorization != null){
                connection.setRequestProperty("Authorization", authorization);
            }
            if(ignoreSSL){
@@ -172,6 +185,9 @@
        return null;
    }
    public static String connectionHttp(String url,String method,String data,String contentType ){
        return connectionHttp(url, method, data, contentType, null);
    }
    public static String connectionHttp(String url,String method,String data,String contentType, String authorization ){
        HttpURLConnection connection = null;
        try {
            URL _url = new URL(url);
@@ -183,6 +199,9 @@
            if(contentType != null){
                connection.setRequestProperty("Content-Type", contentType);
            }
            if(authorization != null){
                connection.setRequestProperty("Authorization", authorization);
            }
            connection.connect();
server/services/src/main/java/com/doumee/core/utils/jpush/JPushUtil.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,239 @@
package com.doumee.core.utils.jpush;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.doumee.core.utils.HttpsUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.Base64;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
 * æžå…‰æŽ¨é€å·¥å…·ç±»ï¼ˆåŸºäºŽJPush REST API)
 *
 * @author rk
 * @date 2026/04/24
 */
@Component
@Slf4j
public class JPushUtil {
    private static final String PUSH_URL = "https://bjapi.push.jiguang.cn/v3/push";
    private static String authHeader;
    private static boolean apnsProduction;
    @Value("${jpush.appKey:}")
    private String appKey;
    @Value("${jpush.masterSecret:}")
    private String masterSecret;
    @Value("${jpush.apnsProduction:false}")
    private boolean apnsProd;
    @PostConstruct
    public void init() {
        if (StringUtils.isNotBlank(appKey) && StringUtils.isNotBlank(masterSecret)) {
            authHeader = "Basic " + Base64.getEncoder().encodeToString((appKey + ":" + masterSecret).getBytes());
            apnsProduction = apnsProd;
            log.info("JPush initialized, appKey={}", appKey);
        } else {
            log.warn("JPush not configured, push disabled");
        }
    }
    // ==================== é€šçŸ¥æ æŽ¨é€ ====================
    /**
     * æŒ‰åˆ«åæŽ¨é€é€šçŸ¥
     *
     * @param alias   åˆ«åï¼ˆå¦‚会员ID/司机ID/门店ID)
     * @param title   é€šçŸ¥æ ‡é¢˜
     * @param content é€šçŸ¥å†…容
     * @param extras  é™„加字段(可选)
     * @return æ˜¯å¦æŽ¨é€æˆåŠŸ
     */
    public static boolean sendByAlias(String alias, String title, String content, Map<String, String> extras) {
        return sendByAliases(Collections.singletonList(alias), title, content, extras);
    }
    /**
     * æŒ‰åˆ«åæ‰¹é‡æŽ¨é€é€šçŸ¥
     */
    public static boolean sendByAliases(List<String> aliases, String title, String content, Map<String, String> extras) {
        if (!available() || aliases == null || aliases.isEmpty()) {
            return false;
        }
        JSONObject body = buildNotificationBody(aliases, "alias", title, content, extras);
        return doPush(body, "sendByAliases");
    }
    /**
     * æŒ‰RegistrationId推送通知
     */
    public static boolean sendByRegId(String regId, String title, String content, Map<String, String> extras) {
        return sendByRegIds(Collections.singletonList(regId), title, content, extras);
    }
    /**
     * æŒ‰RegistrationId批量推送通知
     */
    public static boolean sendByRegIds(List<String> regIds, String title, String content, Map<String, String> extras) {
        if (!available() || regIds == null || regIds.isEmpty()) {
            return false;
        }
        JSONObject body = buildNotificationBody(regIds, "registration_id", title, content, extras);
        return doPush(body, "sendByRegIds");
    }
    /**
     * å¹¿æ’­æŽ¨é€é€šçŸ¥ï¼ˆæŽ¨é€ç»™æ‰€æœ‰ç”¨æˆ·ï¼‰
     */
    public static boolean sendToAll(String title, String content, Map<String, String> extras) {
        if (!available()) {
            return false;
        }
        JSONObject body = buildNotificationBody(null, "all", title, content, extras);
        return doPush(body, "sendToAll");
    }
    // ==================== è‡ªå®šä¹‰æ¶ˆæ¯ï¼ˆé™é»˜æŽ¨é€ï¼Œä¸æ˜¾ç¤ºé€šçŸ¥æ ï¼‰ ====================
    /**
     * æŒ‰åˆ«åæŽ¨é€è‡ªå®šä¹‰æ¶ˆæ¯
     */
    public static boolean sendMessageByAlias(String alias, String content, Map<String, String> extras) {
        return sendMessagesByAlias(Collections.singletonList(alias), content, extras);
    }
    /**
     * æŒ‰åˆ«åæ‰¹é‡æŽ¨é€è‡ªå®šä¹‰æ¶ˆæ¯
     */
    public static boolean sendMessagesByAlias(List<String> aliases, String content, Map<String, String> extras) {
        if (!available() || aliases == null || aliases.isEmpty()) {
            return false;
        }
        JSONObject body = buildMessageBody(aliases, "alias", content, extras);
        return doPush(body, "sendMessageByAlias");
    }
    // ==================== å†…部方法 ====================
    private static boolean available() {
        return StringUtils.isNotBlank(authHeader);
    }
    /**
     * æž„建通知栏推送请求体
     */
    private static JSONObject buildNotificationBody(List<String> targets, String audienceType,
                                                    String title, String content, Map<String, String> extras) {
        JSONObject body = new JSONObject();
        // platform
        body.put("platform", "all");
        // audience
        if ("all".equals(audienceType)) {
            body.put("audience", "all");
        } else {
            JSONObject audience = new JSONObject();
            JSONArray arr = new JSONArray();
            arr.addAll(targets);
            audience.put(audienceType, arr);
            body.put("audience", audience);
        }
        // notification
        JSONObject notification = new JSONObject();
        // Android
        JSONObject android = new JSONObject();
        android.put("title", title);
        android.put("alert", content);
        if (extras != null && !extras.isEmpty()) {
            android.put("extras", mapToJson(extras));
        }
        notification.put("android", android);
        // iOS
        JSONObject ios = new JSONObject();
        ios.put("alert", content);
        ios.put("sound", "default");
        if (extras != null && !extras.isEmpty()) {
            ios.put("extras", mapToJson(extras));
        }
        notification.put("ios", ios);
        body.put("notification", notification);
        // options
        JSONObject options = new JSONObject();
        options.put("apns_production", apnsProduction);
        body.put("options", options);
        return body;
    }
    /**
     * æž„建自定义消息请求体(不显示通知栏)
     */
    private static JSONObject buildMessageBody(List<String> targets, String audienceType,
                                               String content, Map<String, String> extras) {
        JSONObject body = new JSONObject();
        body.put("platform", "all");
        JSONObject audience = new JSONObject();
        JSONArray arr = new JSONArray();
        arr.addAll(targets);
        audience.put(audienceType, arr);
        body.put("audience", audience);
        // message
        JSONObject message = new JSONObject();
        message.put("msg_content", content);
        if (extras != null && !extras.isEmpty()) {
            message.put("extras", mapToJson(extras));
        }
        body.put("message", message);
        JSONObject options = new JSONObject();
        options.put("apns_production", apnsProduction);
        body.put("options", options);
        return body;
    }
    /**
     * Map<String, String> â†’ JSONObject
     */
    private static JSONObject mapToJson(Map<String, String> map) {
        JSONObject json = new JSONObject();
        json.putAll(map);
        return json;
    }
    /**
     * æ‰§è¡ŒæŽ¨é€è¯·æ±‚
     */
    private static boolean doPush(JSONObject body, String action) {
        try {
            String response = HttpsUtil.postJson(PUSH_URL, body.toJSONString(), authHeader);
            log.info("JPush {}, response={}", action, response);
            if (StringUtils.isNotBlank(response)) {
                JSONObject resp = JSONObject.parseObject(response);
                return resp.containsKey("msg_id") || (resp.containsKey("statusCode") && resp.getIntValue("statusCode") == 0);
            }
            return false;
        } catch (Exception e) {
            log.error("JPush {} error", action, e);
            return false;
        }
    }
}
server/services/src/main/java/com/doumee/dao/business/model/DriverInfo.java
@@ -100,8 +100,8 @@
    @ExcelColumn(name = "状态", index = 8, width = 8, valueMapping = "0=启用;1=禁用;")
    private Integer status;
    @ApiModelProperty(value = "审批状态:0=待审批;1=审批通过;2=审批驳回", example = "0")
    @ExcelColumn(name = "审批状态", index = 9, width = 10, valueMapping = "0=待审批;1=审批通过;2=审批驳回")
    @ApiModelProperty(value = "审批状态:0=待审批;1=审批通过;2=审批驳回;3=已支付押金;99=未认证;", example = "0")
    @ExcelColumn(name = "审批状态", index = 9, width = 10, valueMapping = "审批状态:0=待审批;1=审批通过;2=审批驳回;3=已支付押金;99=未认证;")
    private Integer auditStatus;
    @ApiModelProperty(value = "OPENID(APP)")
@@ -203,4 +203,30 @@
    @JsonFormat(pattern = "yyyy-MM-dd")
    private Date createTimeEnd;
    // ---- çœå¸‚区信息(非数据库字段) ----
    @TableField(exist = false)
    @ApiModelProperty(value = "省份主键")
    private Integer provinceId;
    @TableField(exist = false)
    @ApiModelProperty(value = "省份名称")
    private String provinceName;
    @TableField(exist = false)
    @ApiModelProperty(value = "城市主键")
    private Integer cityId;
    @TableField(exist = false)
    @ApiModelProperty(value = "城市名称")
    private String cityName;
    @TableField(exist = false)
    @ApiModelProperty(value = "区县主键")
    private Integer districtId;
    @TableField(exist = false)
    @ApiModelProperty(value = "区县名称")
    private String districtName;
}
server/services/src/main/java/com/doumee/dao/business/model/OrderLog.java
@@ -10,6 +10,7 @@
import lombok.Data;
import java.util.Date;
import java.util.List;
/**
 * è®¢å•操作日志
@@ -76,4 +77,8 @@
    @ApiModelProperty(value = "操作人类型:0=用户;1=司机;2=门店;3=系统管理员", example = "0")
    private Integer optUserType;
    @TableField(exist = false)
    @ApiModelProperty(value = "操作类型(多选查询)")
    private List<Integer> objTypeList;
}
server/services/src/main/java/com/doumee/dao/business/model/Orders.java
@@ -419,4 +419,20 @@
    @ApiModelProperty(value = "物品等级贵重标识(关联查询:0=否;1=是)")
    private String c2OtherField;
    @TableField(exist = false)
    @ApiModelProperty(value = "物品等级名称(关联查询)")
    private String goodLevelName;
    @TableField(exist = false)
    @ApiModelProperty(value = "是否存在特大尺寸:0=否 1=是(关联查询)")
    private Integer hasOversized;
    @TableField(exist = false)
    @ApiModelProperty(value = "司机姓名(关联查询)")
    private String driverName;
    @TableField(exist = false)
    @ApiModelProperty(value = "司机电话(关联查询)")
    private String driverPhone;
}
server/services/src/main/java/com/doumee/dao/business/model/OrdersRefund.java
@@ -60,7 +60,7 @@
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date refundTime;
    @ApiModelProperty(value = "门店备注")
    @ApiModelProperty(value = "退款备注")
    private String refundRemark;
    @ApiModelProperty(value = "平台操作人(type=1使用)", example = "0")
server/services/src/main/java/com/doumee/dao/dto/ConfirmArriveDTO.java
@@ -19,7 +19,4 @@
    @ApiModelProperty(value = "订单主键", required = true)
    private Integer orderId;
    @NotNull(message = "门店主键不能为空")
    @ApiModelProperty(value = "当前操作门店主键", required = true)
    private Integer shopId;
}
server/services/src/main/java/com/doumee/dao/dto/DriverOrderPageDTO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,19 @@
package com.doumee.dao.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
 * å¸æœºè®¢å•分页查询请求
 * @author rk
 * @date 2026/04/25
 */
@Data
@ApiModel("司机订单分页查询请求")
public class DriverOrderPageDTO {
    @ApiModelProperty(value = "订单状态筛选:null=全部;3=待取件;4=配送中;7=已完成")
    private Integer status;
}
server/services/src/main/java/com/doumee/dao/dto/DriverPickupDTO.java
@@ -25,4 +25,10 @@
    @Size(min = 1, max = 3, message = "取件图片1-3å¼ ")
    @ApiModelProperty(value = "取件图片列表(最多3张)", required = true)
    private List<String> images;
    @ApiModelProperty(value = "备注")
    private String remark;
}
server/services/src/main/java/com/doumee/dao/dto/DriverVerifyRequest.java
@@ -1,5 +1,6 @@
package com.doumee.dao.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@@ -50,9 +51,11 @@
    private String carColor;
    @ApiModelProperty(value = "驾驶证有效期开始时间")
    @JsonFormat(pattern = "yyyy-MM-dd")
    private Date cardStartDate;
    @ApiModelProperty(value = "驾驶证有效期结束时间")
    @JsonFormat(pattern = "yyyy-MM-dd")
    private Date cardEndDate;
    @NotEmpty(message = "身份证正面照不能为空")
server/services/src/main/java/com/doumee/dao/dto/MyOrderDTO.java
@@ -18,7 +18,7 @@
    @ApiModelProperty(value = "订单状态(可选,不传查全部)", example = "0")
    private Integer status;
    @ApiModelProperty(value = "合并状态(可选,不传查全部): 0=待支付 1=待核验 2=待配送 3=待收货 4=已完成 5=退款", example = "0")
    @ApiModelProperty(value = "合并状态(可选,不传查全部): 0=待支付 1=待核验 2=待配送 3=待收货 4=已完成 5=退款 6会员首页 7门店待处理订单", example = "0")
    private Integer combinedStatus;
}
server/services/src/main/java/com/doumee/dao/dto/ShopInfoMaintainDTO.java
@@ -20,6 +20,9 @@
    @ApiModelProperty(value = "门店头像")
    private String coverImg;
    @ApiModelProperty(value = "门店头像(全路径)")
    private String coverImgUrl;
    @ApiModelProperty(value = "门店介绍")
    private String content;
server/services/src/main/java/com/doumee/dao/dto/ShopVerifyDTO.java
@@ -4,7 +4,7 @@
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.util.List;
@@ -18,9 +18,9 @@
@ApiModel("门店核销请求")
public class ShopVerifyDTO {
    @NotBlank(message = "核销码不能为空")
    @ApiModelProperty(value = "会员核销码", required = true)
    private String verifyCode;
    @NotNull(message = "订单主键不能为空")
    @ApiModelProperty(value = "订单主键", required = true)
    private Integer orderId;
    @Size(max = 3, message = "最多上传3张图片")
    @ApiModelProperty(value = "图片地址列表,最多3å¼ ")
server/services/src/main/java/com/doumee/dao/vo/ActiveOrderTipVO.java
@@ -21,6 +21,9 @@
    @ApiModelProperty(value = "订单状态")
    private Integer status;
    @ApiModelProperty(value = "订单状态文案")
    private String statusDesc;
    @ApiModelProperty(value = "状态提示文案")
    private String tip;
server/services/src/main/java/com/doumee/dao/vo/DriverActiveOrderCountVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
package com.doumee.dao.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
 * å¸æœºè¿›è¡Œä¸­è®¢å•数量
 * @author rk
 * @date 2026/04/25
 */
@Data
@ApiModel("司机进行中订单数量")
public class DriverActiveOrderCountVO {
    @ApiModelProperty(value = "已抢单数量", example = "0")
    private Integer grabbedCount;
    @ApiModelProperty(value = "派送中数量", example = "0")
    private Integer deliveringCount;
}
server/services/src/main/java/com/doumee/dao/vo/DriverCancelLimitVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,24 @@
package com.doumee.dao.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
 * å¸æœºä»Šæ—¥å–消次数
 * @author rk
 * @date 2026/04/25
 */
@Data
@ApiModel("司机今日取消次数")
public class DriverCancelLimitVO {
    @ApiModelProperty(value = "每日取消上限", example = "3")
    private Integer limit;
    @ApiModelProperty(value = "今日已取消次数", example = "1")
    private Integer used;
    @ApiModelProperty(value = "今日剩余可取消次数", example = "2")
    private Integer remain;
}
server/services/src/main/java/com/doumee/dao/vo/DriverCenterVO.java
@@ -28,6 +28,15 @@
    @ApiModelProperty(value = "服务评分")
    private String score;
    @ApiModelProperty(value = "司机定级:5=S 4=A 3=B 2=C 1=D")
    private Integer driverLevel;
    @ApiModelProperty(value = "审核状态:0=待审批 1=审批通过 2=审批驳回")
    private Integer auditStatus;
    @ApiModelProperty(value = "审核备注(驳回原因)")
    private String auditRemark;
    @ApiModelProperty(value = "今日预计佣金(分)")
    private Long todayCommission;
@@ -43,4 +52,8 @@
    @ApiModelProperty(value = "待配送订单数量")
    private Integer waitDeliverCount;
    @ApiModelProperty(value = "是否接单中:0=已下线;1=接单中", example = "0")
    private Integer acceptingStatus;
}
server/services/src/main/java/com/doumee/dao/vo/DriverGrabOrderVO.java
@@ -37,8 +37,14 @@
    @ApiModelProperty(value = "距存件门店距离(如 500m、1.2km)")
    private String depositDistance;
    @ApiModelProperty(value = "取件名称(门店名称或自定义地点)")
    @ApiModelProperty(value = "取件门店名称")
    private String takeName;
    @ApiModelProperty(value = "取件地址")
    private String takeAddress;
    @ApiModelProperty(value = "取件门店主键(有取件门店时返回)")
    private Integer takeShopId;
    @ApiModelProperty(value = "取件距离(如 500m、1.2km)")
    private String takeDistance;
@@ -58,6 +64,15 @@
    @ApiModelProperty(value = "是否贵重物品")
    private Boolean isValuable;
    @ApiModelProperty(value = "物品等级名称")
    private String goodLevelName;
    @ApiModelProperty(value = "是否存在特大尺寸:0=否 1=是")
    private Integer hasOversized;
    @ApiModelProperty(value = "司机取货码(待取货状态时返回)")
    private String driverVerifyCode;
    @Data
    @ApiModel("抢单大厅物品项")
    public static class OrderItem implements Serializable {
@@ -66,6 +81,9 @@
        @ApiModelProperty(value = "数量")
        private Integer quantity;
        @ApiModelProperty(value = "是否大件物品:0=否 1=是")
        private Integer isOversized;
    }
}
server/services/src/main/java/com/doumee/dao/vo/DriverOrderDetailVO.java
@@ -47,6 +47,12 @@
    @ApiModelProperty(value = "取件名称(门店名称或自定义地点)")
    private String takeName;
    @ApiModelProperty(value = "取件地址")
    private String takeAddress;
    @ApiModelProperty(value = "取件门店主键(有取件门店时返回)")
    private Integer takeShopId;
    @ApiModelProperty(value = "取件距离(如 500m、1.2km)")
    private String takeDistance;
@@ -64,6 +70,15 @@
    @ApiModelProperty(value = "是否贵重物品")
    private Boolean isValuable;
    @ApiModelProperty(value = "物品等级名称")
    private String goodLevelName;
    @ApiModelProperty(value = "是否存在特大尺寸:0=否 1=是")
    private Integer hasOversized;
    @ApiModelProperty(value = "司机取货码(待取货状态时返回)")
    private String driverVerifyCode;
    @ApiModelProperty(value = "导航纬度(status=2存件门店纬度,status=3/4取件纬度)")
    private Double navigateLat;
@@ -123,6 +138,9 @@
        @ApiModelProperty(value = "数量")
        private Integer quantity;
        @ApiModelProperty(value = "是否大件物品:0=否 1=是")
        private Integer isOversized;
    }
}
server/services/src/main/java/com/doumee/dao/vo/DriverStatsVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,29 @@
package com.doumee.dao.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
 * å¸æœºç´¯è®¡ç»Ÿè®¡
 *
 * @author rk
 * @date 2026/04/24
 */
@Data
@ApiModel("司机累计统计")
public class DriverStatsVO {
    @ApiModelProperty(value = "累计佣金(分)")
    private Long totalCommission;
    @ApiModelProperty(value = "待结算佣金(分)")
    private Long pendingCommission;
    @ApiModelProperty(value = "订单总数")
    private Integer totalOrderCount;
    @ApiModelProperty(value = "钱包余额(分)")
    private Long balance;
}
server/services/src/main/java/com/doumee/dao/vo/MyOrderDetailVO.java
@@ -1,5 +1,6 @@
package com.doumee.dao.vo;
import com.doumee.dao.business.model.OrdersRefund;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
@@ -122,6 +123,9 @@
    @ApiModelProperty(value = "保价保费(分)")
    private Long declaredFee;
    @ApiModelProperty(value = "是否加急:0=否;1=是")
    private Integer isUrgent;
    @ApiModelProperty(value = "加急费用(分)")
    private Long urgentAmount;
@@ -129,6 +133,10 @@
    private Long actualPayAmount;
    // ---- é€¾æœŸ ----
    //逾期状态: 0=未到店未逾期 1=未到店存在逾期 2=已到店未存在逾期 3=已到店待支付逾期 4=逾期已支付
    @ApiModelProperty(value = "逾期状态: 0=未到店未逾期 1=未到店存在逾期 2=已到店未逾期 3=已到店待支付逾期 4=逾期已支付")
    private Integer overdueStatus;
    @ApiModelProperty(value = "是否逾期")
    private Boolean overdue;
@@ -141,19 +149,12 @@
    // ---- é€€æ¬¾ï¼ˆstatus=96/99 æ—¶è¿”回) ----
    @ApiModelProperty(value = "退款金额(分)")
    private Long refundAmount;
    @ApiModelProperty(value = "退款申请时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date refundApplyTime;
    @ApiModelProperty(value = "退款时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date refundTime;
    @ApiModelProperty(value = "退款备注")
    private String refundRemark;
    @ApiModelProperty(value = "退款记录")
    private OrdersRefund refundInfo;
    // ---- æ ‡è®° ----
server/services/src/main/java/com/doumee/dao/vo/MyOrderVO.java
@@ -37,6 +37,9 @@
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date createTime;
    @ApiModelProperty(value = "订单备注")
    private String remark;
    @ApiModelProperty(value = "预计取件时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date expectedTakeTime;
@@ -69,6 +72,9 @@
    @ApiModelProperty(value = "取件门店地址(有取件门店时返回)")
    private String takeShopAddress;
    @ApiModelProperty(value = "取件门店联系电话(有取件门店时返回)")
    private String takeShopPhone;
    @ApiModelProperty(value = "取件地点(无取件门店时返回,用户自选)")
    private String takeLocation;
@@ -91,6 +97,9 @@
    // ---- é€¾æœŸ ----
    @ApiModelProperty(value = "逾期状态:0=未到店未逾期;1=未到店存在逾期;2=已到店未逾期;3=已到店待支付逾期;4=逾期已支付")
    private Integer overdueStatus;
    @ApiModelProperty(value = "是否逾期")
    private Boolean overdue;
@@ -110,6 +119,14 @@
    @ApiModelProperty(value = "当前门店角色:1=存件门店;2=取件门店(仅门店端返回)")
    private Integer shopRole;
    // ---- å¸æœºä¿¡æ¯ ----
    @ApiModelProperty(value = "司机姓名(异地寄存有司机时返回)")
    private String driverName;
    @ApiModelProperty(value = "司机电话(异地寄存有司机时返回)")
    private String driverPhone;
    // ---- è¯„ä»· ----
    @ApiModelProperty(value = "评价状态:0=未评价;1=已评价")
server/services/src/main/java/com/doumee/dao/vo/PlatformAboutVO.java
@@ -4,6 +4,8 @@
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Map;
/**
 * å¹³å°ä¿¡æ¯ï¼ˆå…³äºŽæˆ‘们、协议等)
 *
@@ -17,12 +19,6 @@
    @ApiModelProperty(value = "关于我们")
    private String aboutUs;
    @ApiModelProperty(value = "用户协议")
    private String userAgreement;
    @ApiModelProperty(value = "隐私协议")
    private String privacyAgreement;
    @ApiModelProperty(value = "规范须知")
    private String serverIntroduce;
    @ApiModelProperty(value = "协议须知列表,key=枚举编码,value=富文本内容")
    private Map<String, String> agreements;
}
server/services/src/main/java/com/doumee/dao/vo/ShopDetailVO.java
@@ -169,4 +169,7 @@
    @ApiModelProperty(value = "绑定开户会员头像")
    private String payMemberCoverImage;
    @ApiModelProperty(value = "门店头像(全路径)")
    private String shopAvatar;
}
server/services/src/main/java/com/doumee/service/business/DriverInfoService.java
@@ -4,6 +4,7 @@
import com.doumee.core.model.PageWrap;
import com.doumee.dao.dto.DriverActiveOrderDTO;
import com.doumee.dao.dto.DriverGrabOrderDTO;
import com.doumee.dao.dto.DriverOrderPageDTO;
import com.doumee.dao.business.model.DriverInfo;
import com.doumee.dao.dto.DriverLoginRequest;
import com.doumee.dao.dto.DriverDeliverDTO;
@@ -11,6 +12,8 @@
import com.doumee.dao.dto.DriverRegisterRequest;
import com.doumee.dao.dto.DriverVerifyRequest;
import com.doumee.dao.vo.AccountResponse;
import com.doumee.dao.vo.DriverActiveOrderCountVO;
import com.doumee.dao.vo.DriverGrabOrderVO;
import java.util.List;
@@ -191,6 +194,11 @@
    com.doumee.dao.vo.DriverCenterVO getDriverCenterInfo(Integer memberId);
    /**
     * å¸æœºç´¯è®¡ç»Ÿè®¡ï¼ˆç´¯è®¡ä½£é‡‘、待结算佣金、订单总数、钱包余额)
     */
    com.doumee.dao.vo.DriverStatsVO getDriverStats(Integer memberId);
    /**
     * å¸æœºæŠ¢å•大厅 - åˆ†é¡µæŸ¥è¯¢å¯æŠ¢è®¢å•
     *
     * @param memberId  å½“前登录会员主键
@@ -250,4 +258,38 @@
     */
    void confirmDeliver(Integer driverId, DriverDeliverDTO dto);
    /**
     * å¸æœºä¿®æ”¹å¯†ç 
     *
     * @param driverId    å¸æœºä¸»é”®
     * @param newPassword æ–°å¯†ç 
     * @param token       å½“前token(修改成功后清除)
     */
    void changePassword(Integer driverId, String newPassword, String token);
    /**
     * èŽ·å–å¸æœºè¿›è¡Œä¸­è®¢å•æ•°é‡
     *
     * @param driverId å¸æœºä¸»é”®
     * @return è¿›è¡Œä¸­è®¢å•数量
     */
    DriverActiveOrderCountVO getActiveOrderCount(Integer driverId);
    /**
     * å¸æœºè®¢å•分页查询
     *
     * @param driverId å¸æœºä¸»é”®
     * @param pageWrap åˆ†é¡µå‚数(model.status: null=全部, 3=待取件, 4=配送中, 7=已完成)
     * @return åˆ†é¡µç»“æžœ
     */
    PageData<DriverGrabOrderVO> driverOrderPage(Integer driverId, PageWrap<DriverOrderPageDTO> pageWrap);
    /**
     * æŸ¥è¯¢å¸æœºä»Šæ—¥å¯å–消次数
     *
     * @param driverId å¸æœºä¸»é”®
     * @return DriverCancelLimitVO
     */
    com.doumee.dao.vo.DriverCancelLimitVO getTodayCancelLimit(Integer driverId);
}
server/services/src/main/java/com/doumee/service/business/OrdersService.java
@@ -149,6 +149,26 @@
    void shopVerifyOrder(String verifyCode, Integer shopId, List<String> images, String remark);
    /**
     * é—¨åº—核销(自动识别会员核销码/司机核销码)
     *
     * @param verifyCode æ ¸é”€ç 
     * @param shopId     é—¨åº—主键
     * @param images     å›¾ç‰‡
     * @param remark     å¤‡æ³¨
     */
    void verifyOrder(String verifyCode, Integer shopId, List<String> images, String remark);
    /**
     * é—¨åº—通过订单主键核销
     *
     * @param orderId è®¢å•主键
     * @param shopId  é—¨åº—主键
     * @param images  å›¾ç‰‡
     * @param remark  å¤‡æ³¨
     */
    void verifyOrderByShopId(Integer orderId, Integer shopId, List<String> images, String remark);
    /**
     * é—¨åº—端查询订单详情
     * æ”¯æŒæŒ‰è®¢å•主键或核销码查询,复用会员端详情逻辑
     *
server/services/src/main/java/com/doumee/service/business/ShopInfoService.java
@@ -209,4 +209,13 @@
     */
    ShopSalesStatsVO getShopSalesStats(Integer shopId, Integer period);
    /**
     * é—¨åº—修改密码
     *
     * @param shopId      é—¨åº—主键
     * @param newPassword æ–°å¯†ç 
     * @param token       å½“前token(修改成功后清除)
     */
    void changePassword(Integer shopId, String newPassword, String token);
}
server/services/src/main/java/com/doumee/service/business/impl/AreasServiceImpl.java
@@ -14,7 +14,9 @@
import com.doumee.core.utils.PinYinUtil;
import com.doumee.core.utils.Utils;
import com.doumee.dao.business.AreasMapper;
import com.doumee.dao.business.PricingRuleMapper;
import com.doumee.dao.business.model.Areas;
import com.doumee.dao.business.model.PricingRule;
import com.doumee.service.business.AreasService;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import org.apache.commons.lang3.StringUtils;
@@ -45,6 +47,9 @@
    private AreasMapper areasMapper;
    @Autowired
    private PricingRuleMapper pricingRuleMapper;
    @Autowired
    RestTemplate restTemplate ;
@@ -63,6 +68,7 @@
            }
        }*/
        areas.setIsdeleted(Constants.ZERO);
        areas.setCreateDate(new Date());
        areasMapper.insert(areas);
        areas.setCode(areas.getId().toString());
        areasMapper.updateById(areas);
@@ -74,6 +80,11 @@
    @Override
    public void deleteById(Integer id) {
        areasMapper.deleteById(id);
        // åŒæ­¥åˆ é™¤è¯¥åŸŽå¸‚关联的计价规则配置
        PricingRule deleteRule = new PricingRule();
        deleteRule.setCityId(id);
        QueryWrapper<PricingRule> deleteWrapper = new QueryWrapper<>(deleteRule);
        pricingRuleMapper.delete(deleteWrapper);
        //刷新缓存数据
        cacheData();
    }
server/services/src/main/java/com/doumee/service/business/impl/BannerServiceImpl.java
@@ -161,6 +161,8 @@
                queryWrapper.orderByAsc(sortData.getProperty());
            }
        }
        // é»˜è®¤æŽ’序:排序码倒序、主键顺序
        queryWrapper.lambda().orderByDesc(Banner::getSortnum).orderByAsc(Banner::getId);
        PageData<Banner> result = PageData.from(bannerMapper.selectPage(page, queryWrapper));
        if (result != null && result.getRecords() != null) {
            String path = getBannerPath();
server/services/src/main/java/com/doumee/service/business/impl/CategoryServiceImpl.java
@@ -57,8 +57,11 @@
        if(Objects.isNull(category)
        || Objects.isNull(category.getType())
        || Objects.isNull(category.getName())
        || StringUtils.isBlank(category.getDetail())
        ){
            throw new BusinessException(ResponseStatus.BAD_REQUEST);
        }
        // type=2(寄存说明)detail不必填,其他类型必填
        if (!Constants.equalsInteger(category.getType(), Constants.TWO) && StringUtils.isBlank(category.getDetail())) {
            throw new BusinessException(ResponseStatus.BAD_REQUEST);
        }
        validateByType(category);
@@ -100,10 +103,13 @@
                || Objects.isNull(category.getId())
                || Objects.isNull(category.getType())
                || Objects.isNull(category.getName())
                || StringUtils.isBlank(category.getDetail())
        ){
            throw new BusinessException(ResponseStatus.BAD_REQUEST);
        }
        // type=2(寄存说明)detail不必填,其他类型必填
        if (!Constants.equalsInteger(category.getType(), Constants.TWO) && StringUtils.isBlank(category.getDetail())) {
            throw new BusinessException(ResponseStatus.BAD_REQUEST);
        }
        validateByType(category);
        LoginUserInfo loginUserInfo = (LoginUserInfo) SecurityUtils.getSubject().getPrincipal();
        category.setUpdateTime(new Date());
server/services/src/main/java/com/doumee/service/business/impl/DriverInfoServiceImpl.java
@@ -4,6 +4,7 @@
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.doumee.biz.system.impl.AreasBizImpl;
import com.doumee.core.constants.Constants;
import com.doumee.core.constants.ResponseStatus;
import com.doumee.core.exception.BusinessException;
@@ -22,27 +23,19 @@
import com.doumee.dao.business.ShopInfoMapper;
import com.doumee.dao.business.OrdersDetailMapper;
import com.doumee.dao.business.RevenueMapper;
import com.doumee.dao.business.model.*;
import com.doumee.service.business.AreasService;
import com.doumee.biz.system.SystemDictDataBiz;
import com.doumee.biz.system.OperationConfigBiz;
import com.doumee.dao.business.OrderLogMapper;
import com.doumee.dao.business.model.Category;
import com.doumee.dao.business.model.DriverInfo;
import com.doumee.dao.business.model.OrderLog;
import com.doumee.dao.business.model.OrderComment;
import com.doumee.dao.business.model.ShopInfo;
import com.doumee.dao.business.model.Member;
import com.doumee.dao.business.model.Multifile;
import com.doumee.dao.business.model.Smsrecord;
import com.doumee.dao.business.model.Orders;
import com.doumee.dao.business.model.OrdersDetail;
import com.doumee.dao.business.model.Revenue;
import com.doumee.dao.dto.*;
import com.doumee.dao.vo.AccountResponse;
import com.doumee.dao.vo.DriverActiveOrderCountVO;
import com.doumee.dao.vo.DriverCancelLimitVO;
import com.doumee.dao.vo.DriverCenterVO;
import com.doumee.dao.vo.DriverGrabOrderVO;
import com.doumee.dao.vo.DriverOrderDetailVO;
import com.doumee.core.utils.aliyun.AliSmsService;
import com.doumee.dao.business.model.Notice;
import com.doumee.service.business.DriverInfoService;
import com.doumee.service.business.NoticeService;
import com.alibaba.fastjson.JSONObject;
@@ -57,6 +50,7 @@
import org.springframework.util.CollectionUtils;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
@@ -118,6 +112,9 @@
    @Autowired
    private NoticeService noticeService;
    @Autowired
    private AreasBizImpl areasBiz;
    /**
     * å‘送订单站内信通知
@@ -383,7 +380,7 @@
            member.setUpdateTime(now);
            member.setTelephone(telephone);
            member.setNickName(telephone.substring(0, 3) + "****" + telephone.substring(7));
            member.setName(telephone);
            member.setName(member.getNickName());
            member.setUserType(Constants.ONE);
            member.setBusinessStatus(Constants.ZERO);
            member.setPassword(secure.encryptPassword(defaultPassword, salt));
@@ -401,13 +398,15 @@
            // åˆ›å»ºå¸æœºåŸºç¡€ä¿¡æ¯
            DriverInfo driverInfo = new DriverInfo();
            driverInfo.setId(member.getId());
            driverInfo.setDeleted(Constants.ZERO);
            driverInfo.setCreateTime(now);
            driverInfo.setUpdateTime(now);
            driverInfo.setTelephone(telephone);
            driverInfo.setName(member.getNickName());
            driverInfo.setMemberId(member.getId());
            driverInfo.setStatus(Constants.ZERO);
            driverInfo.setAuditStatus(null);
            driverInfo.setAuditStatus(99);
            driverInfoMapper.insert(driverInfo);
        }
@@ -531,6 +530,7 @@
                .set(DriverInfo::getAliAccount, request.getAliAccount())
                .set(DriverInfo::getAliName, request.getAliName())
                .set(DriverInfo::getUpdateTime, now)
                .set(DriverInfo::getAuditStatus, Constants.ZERO)
                .set(DriverInfo::getAuditRemark, null)
                .set(DriverInfo::getAuditTime, null)
                .eq(DriverInfo::getId, driverInfo.getId()));
@@ -561,18 +561,39 @@
    @Override
    public DriverInfo getVerifyDetail(Integer memberId) {
        DriverInfo driverInfo = driverInfoMapper.selectOne(new QueryWrapper<DriverInfo>().lambda()
                .eq(DriverInfo::getMemberId, memberId)
                .eq(DriverInfo::getId, memberId)
                .eq(DriverInfo::getDeleted, Constants.ZERO)
                .last("limit 1"));
        if (Objects.isNull(driverInfo)) {
            throw new BusinessException(ResponseStatus.DATA_EMPTY);
        }
        // æ‹¼æŽ¥å›¾ç‰‡å‰ç¼€
        String imgPrefix = "";
        try {
            imgPrefix = systemDictDataBiz.queryByCode(Constants.OSS, Constants.RESOURCE_PATH).getCode()
                    + systemDictDataBiz.queryByCode(Constants.OSS, Constants.DRIVER_FILES).getCode();
        } catch (Exception e) {
            // æœªé…ç½®æ—¶å¿½ç•¥
        }
        driverInfo.setImgPrefix(imgPrefix);
        // æŸ¥è¯¢è½¦è¾†ç±»åž‹åç§°å’Œæ˜¯å¦éœ€è¦é©¾é©¶è¯
        if (driverInfo.getCarType() != null) {
            Category category = categoryMapper.selectById(driverInfo.getCarType());
            if (Objects.nonNull(category)) {
                driverInfo.setCarTypeName(category.getName());
                driverInfo.setNeedLicense(Constants.equalsInteger(Integer.valueOf(category.getOtherField()), Constants.ONE) ? Constants.ONE : Constants.ZERO);
            }
        }
        // æŸ¥è¯¢çœå¸‚区信息
        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());
            }
        }
        // æŸ¥è¯¢ç…§ç‰‡åˆ—表
@@ -638,6 +659,20 @@
                .set(Member::getBusinessStatus, driverStatus)
                .set(Member::getUpdateTime, now)
                .eq(Member::getId, driverInfo.getMemberId()));
        // çŸ­ä¿¡é€šçŸ¥
        if (Constants.equalsInteger(newAuditStatus, Constants.ONE)) {
            // å®¡æ‰¹é€šè¿‡
            sendSmsNotify(driverInfo.getTelephone(),
                    Constants.SmsNotify.DRIVER_AUTH_APPROVED,
                    "driver", driverInfo.getName());
        } else if (Constants.equalsInteger(newAuditStatus, Constants.TWO)) {
            // å®¡æ‰¹é©³å›ž
            sendSmsNotify(driverInfo.getTelephone(),
                    Constants.SmsNotify.DRIVER_AUTH_REJECTED,
                    "driver", driverInfo.getName(),
                    "reason", auditDTO.getAuditRemark() != null ? auditDTO.getAuditRemark() : "");
        }
    }
    @Override
@@ -776,8 +811,11 @@
        vo.setImgUrl(driver.getImgurl());
        vo.setCarCode(driver.getCarCode());
        vo.setScore(driver.getScore() != null ? driver.getScore().toPlainString() : "0");
        vo.setDriverLevel(driver.getDriverLevel());
        vo.setAuditStatus(driver.getAuditStatus());
        vo.setAuditRemark(driver.getAuditRemark());
        vo.setBalance(driver.getBalance() != null ? driver.getBalance() : 0L);
        vo.setAcceptingStatus(driver.getAcceptingStatus());
        // å¤´åƒå…¨è·¯å¾„
        if (StringUtils.isNotBlank(driver.getImgurl())) {
            String imgPrefix = systemDictDataBiz.queryByCode(Constants.OSS, Constants.RESOURCE_PATH).getCode()
@@ -810,7 +848,7 @@
        Long todayOrderCount = ordersMapper.selectCount(new QueryWrapper<Orders>().lambda()
                .eq(Orders::getAcceptDriver, driver.getId())
                .eq(Orders::getDeleted, Constants.ZERO)
                .ge(Orders::getFinishTime, todayStart));
                .ge(Orders::getAcceptTime, todayStart));
        vo.setTodayOrderCount(todayOrderCount.intValue());
        // å¾…取货(已接单=3)
@@ -826,6 +864,54 @@
                .eq(Orders::getDeleted, Constants.ZERO)
                .eq(Orders::getStatus, Constants.OrderStatus.delivering.getStatus()));
        vo.setWaitDeliverCount(waitDeliverCount.intValue());
        return vo;
    }
    @Override
    public com.doumee.dao.vo.DriverStatsVO getDriverStats(Integer memberId) {
        DriverInfo driver = driverInfoMapper.selectOne(new QueryWrapper<DriverInfo>().lambda()
                .eq(DriverInfo::getId, memberId)
                .eq(DriverInfo::getDeleted, Constants.ZERO)
                .last("limit 1"));
        if (driver == null) {
            throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "司机信息不存在");
        }
        com.doumee.dao.vo.DriverStatsVO vo = new com.doumee.dao.vo.DriverStatsVO();
        // ç´¯è®¡ä½£é‡‘:type=0(完成订单) + vaildStatus=1(已入账)
        QueryWrapper<Revenue> totalWrapper = new QueryWrapper<>();
        totalWrapper.lambda()
                .eq(Revenue::getMemberId, memberId)
                .eq(Revenue::getMemberType, Constants.ONE)
                .eq(Revenue::getType, Constants.ZERO)
                .eq(Revenue::getVaildStatus, Constants.ONE)
                .eq(Revenue::getDeleted, Constants.ZERO);
        totalWrapper.select("IFNULL(SUM(AMOUNT),0) as amount");
        Revenue totalResult = revenueMapper.selectOne(totalWrapper);
        vo.setTotalCommission(totalResult != null && totalResult.getAmount() != null ? totalResult.getAmount() : 0L);
        // å¾…结算佣金:type=0(完成订单) + vaildStatus=0(入账中)
        QueryWrapper<Revenue> pendingWrapper = new QueryWrapper<>();
        pendingWrapper.lambda()
                .eq(Revenue::getMemberId, memberId)
                .eq(Revenue::getMemberType, Constants.ONE)
                .eq(Revenue::getType, Constants.ZERO)
                .eq(Revenue::getVaildStatus, Constants.ZERO)
                .eq(Revenue::getDeleted, Constants.ZERO);
        pendingWrapper.select("IFNULL(SUM(AMOUNT),0) as amount");
        Revenue pendingResult = revenueMapper.selectOne(pendingWrapper);
        vo.setPendingCommission(pendingResult != null && pendingResult.getAmount() != null ? pendingResult.getAmount() : 0L);
        // è®¢å•总数
        Long totalOrderCount = ordersMapper.selectCount(new QueryWrapper<Orders>().lambda()
                .eq(Orders::getAcceptDriver, driver.getId())
                .eq(Orders::getDeleted, Constants.ZERO));
        vo.setTotalOrderCount(totalOrderCount.intValue());
        // é’±åŒ…余额
        vo.setBalance(driver.getBalance() != null ? driver.getBalance() : 0L);
        return vo;
    }
@@ -862,10 +948,8 @@
            goodTypeIds = cats.stream().map(Category::getId).collect(Collectors.toList());
        }
        // 3. Haversine SQL公式:司机到存件门店距离(km),使用Orders自带坐标
        String depositDist = "(6371 * acos(cos(radians(" + driverLat + ")) * cos(radians(t.DEPOSIT_LGT)) "
                + "* cos(radians(t.DEPOSIT_LAT) - radians(" + driverLng + ")) "
                + "+ sin(radians(" + driverLat + ")) * sin(radians(t.DEPOSIT_LGT))))";
        // 3. ST_Distance_Sphere计算司机到存件门店距离(km),使用Orders自带坐标
        String depositDist = "(ST_Distance_Sphere(POINT(" + driverLng + ", " + driverLat + "), POINT(t.DEPOSIT_LGT, t.DEPOSIT_LAT)) / 1000)";
        // 4. æž„造MPJ查询
        IPage<Orders> page = new Page<>(pageWrap.getPage(), pageWrap.getCapacity());
@@ -880,6 +964,9 @@
                .select("s2.link_phone as takeShopLinkPhone")
                // ç‰©å“ç­‰çº§è´µé‡æ ‡è¯†
                .select("c2.other_field as c2OtherField")
                .select("c2.name as goodLevelName")
                // æ˜¯å¦å­˜åœ¨ç‰¹å¤§å°ºå¯¸
                .select("IF(EXISTS(SELECT 1 FROM orders_detail od JOIN category c3 ON c3.id = od.LUGGAGE_ID AND c3.TYPE = 4 AND c3.OTHER_FIELD = '1' WHERE od.ORDER_ID = t.ID AND od.DELETED = 0), 1, 0) as hasOversized")
                // JOIN
                .leftJoin("shop_info s1 on s1.id = t.DEPOSIT_SHOP_ID and s1.DELETED = 0")
                .leftJoin("shop_info s2 on s2.id = t.TAKE_SHOP_ID and s2.DELETED = 0")
@@ -979,13 +1066,15 @@
                .select("s2.address", Orders::getTakeShopAddress)
                .select("s2.link_phone as takeShopLinkPhone")
                .select("c2.other_field as c2OtherField")
                .select("c2.name as goodLevelName")
                .select("IF(EXISTS(SELECT 1 FROM orders_detail od JOIN category c3 ON c3.id = od.LUGGAGE_ID AND c3.TYPE = 4 AND c3.OTHER_FIELD = '1' WHERE od.ORDER_ID = t.ID AND od.DELETED = 0), 1, 0) as hasOversized")
                .leftJoin("shop_info s1 on s1.id = t.DEPOSIT_SHOP_ID and s1.DELETED = 0")
                .leftJoin("shop_info s2 on s2.id = t.TAKE_SHOP_ID and s2.DELETED = 0")
                .leftJoin("category c1 on c1.id = t.GOOD_TYPE and c1.DELETED = 0")
                .leftJoin("category c2 on c2.id = c1.RELATION_ID and c2.DELETED = 0 and c2.TYPE = 3")
                .eq(Orders::getAcceptDriver, driver.getId())
                .eq(Orders::getType, Constants.ONE)
                .eq(Orders::getStatus, dto.getStatus())
                .eq(Objects.nonNull(dto.getStatus()),Orders::getStatus, dto.getStatus())
                .eq(Orders::getDeleted, Constants.ZERO)
                .orderByAsc(Orders::getAcceptTime);
@@ -1024,6 +1113,7 @@
                .select("s2.address", Orders::getTakeShopAddress)
                .select("s2.link_phone as takeShopLinkPhone")
                .select("c2.other_field as c2OtherField")
                .select("IF(EXISTS(SELECT 1 FROM orders_detail od JOIN category c3 ON c3.id = od.LUGGAGE_ID AND c3.TYPE = 4 AND c3.OTHER_FIELD = '1' WHERE od.ORDER_ID = t.ID AND od.DELETED = 0), 1, 0) as hasOversized")
                .leftJoin("shop_info s1 on s1.id = t.DEPOSIT_SHOP_ID and s1.DELETED = 0")
                .leftJoin("shop_info s2 on s2.id = t.TAKE_SHOP_ID and s2.DELETED = 0")
                .leftJoin("category c1 on c1.id = t.GOOD_TYPE and c1.DELETED = 0")
@@ -1081,11 +1171,17 @@
        vo.setDepositShopAddress(base.getDepositShopAddress());
        vo.setDepositDistance(base.getDepositDistance());
        vo.setTakeName(base.getTakeName());
        vo.setTakeAddress(base.getTakeAddress());
        vo.setTakeShopId(base.getTakeShopId());
        vo.setTakeDistance(base.getTakeDistance());
        vo.setContactPhone(base.getContactPhone());
        vo.setDriverFee(base.getDriverFee());
        vo.setUrgentAmount(base.getUrgentAmount());
        vo.setIsValuable(base.getIsValuable());
        vo.setGoodLevelName(base.getGoodLevelName());
        vo.setHasOversized(base.getHasOversized());
        vo.setDriverVerifyCode(base.getDriverVerifyCode());
        // ç‰©å“æ˜Žç»†ï¼ˆè½¬æ¢ç±»åž‹ï¼‰
        List<DriverOrderDetailVO.OrderItem> detailItems = new ArrayList<>();
@@ -1094,6 +1190,7 @@
                DriverOrderDetailVO.OrderItem item = new DriverOrderDetailVO.OrderItem();
                item.setName(src.getName());
                item.setQuantity(src.getQuantity());
                item.setIsOversized(src.getIsOversized());
                detailItems.add(item);
            }
        }
@@ -1221,7 +1318,7 @@
        Date todayStart = cal.getTime();
        Long todayCancelCount = orderLogMapper.selectCount(new QueryWrapper<OrderLog>().lambda()
                .eq(OrderLog::getOptUserId, driver.getMemberId())
                .eq(OrderLog::getObjType, Constants.ORDER_LOG_CANCEL)
                .eq(OrderLog::getObjType, Constants.OrderLogType.driverCancel.getStatus())
                .eq(OrderLog::getOptUserType, Constants.ONE)
                .ge(OrderLog::getCreateTime, todayStart));
        if (todayCancelCount != null && todayCancelCount >= limit) {
@@ -1238,9 +1335,9 @@
        // 5. å†™å…¥å–消日志
        OrderLog log = new OrderLog();
        log.setOrderId(orderId);
        log.setTitle("司机取消订单");
        log.setLogInfo(StringUtils.isNotBlank(reason) ? reason : "司机取消接单");
        log.setObjType(Constants.ORDER_LOG_CANCEL);
        log.setTitle(Constants.OrderLogType.driverCancel.getTitle());
        log.setLogInfo(Constants.OrderLogType.driverCancel.format(StringUtils.isNotBlank(reason) ? reason : "司机取消接单"));
        log.setObjType(Constants.OrderLogType.driverCancel.getStatus());
        log.setOptUserId(driver.getMemberId());
        log.setOptUserType(Constants.ONE);
        log.setOrderStatus(order.getStatus());
@@ -1297,11 +1394,13 @@
        // 5. åŽŸå­æ›´æ–°ï¼šå¸¦ status=2 æ¡ä»¶é˜²æ­¢å¹¶å‘重复抢单
        Date now = new Date();
        String driverVerifyCode = generateVerifyCode();
        int rows = ordersMapper.update(new UpdateWrapper<Orders>().lambda()
                .set(Orders::getAcceptDriver, driverId)
                .set(Orders::getAcceptTime, now)
                .set(Orders::getAcceptType, 0) // 0=手动抢单
                .set(Orders::getStatus, Constants.OrderStatus.accepted.getStatus())
                .set(Orders::getDriverVerifyCode, driverVerifyCode)
                .set(Orders::getUpdateTime, now)
                .eq(Orders::getId, orderId)
                .eq(Orders::getStatus, Constants.TWO));
@@ -1312,9 +1411,9 @@
        // 6. å†™å…¥æ“ä½œæ—¥å¿—
        OrderLog log = new OrderLog();
        log.setOrderId(orderId);
        log.setTitle("司机抢单");
        log.setLogInfo("司机【" + driver.getName() + "】抢单成功");
        log.setObjType(Constants.ORDER_LOG_DRIVER_PICKUP);
        log.setTitle(Constants.OrderLogType.driverGrab.getTitle());
        log.setLogInfo(Constants.OrderLogType.driverGrab.format(driver.getName()));
        log.setObjType(Constants.OrderLogType.driverGrab.getStatus());
        log.setOptUserId(driver.getMemberId());
        log.setOptUserType(Constants.ONE);
        log.setOrderStatus(Constants.OrderStatus.accepted.getStatus());
@@ -1397,12 +1496,13 @@
        // 5. å†™å…¥æ“ä½œæ—¥å¿—
        OrderLog log = new OrderLog();
        log.setOrderId(orderId);
        log.setTitle("司机完成取件");
        log.setLogInfo("司机【" + driver.getName() + "】完成取件,开始派送");
        log.setObjType(Constants.ORDER_LOG_DRIVER_PICKUP);
        log.setTitle(Constants.OrderLogType.driverPickup.getTitle());
        log.setLogInfo(Constants.OrderLogType.driverPickup.format(driver.getName()));
        log.setObjType(Constants.OrderLogType.driverPickup.getStatus());
        log.setOptUserId(driver.getMemberId());
        log.setOptUserType(Constants.ONE);
        log.setOrderStatus(Constants.OrderStatus.delivering.getStatus());
        log.setRemark(dto.getRemark());
        log.setCreateTime(now);
        log.setDeleted(Constants.ZERO);
        orderLogMapper.insert(log);
@@ -1488,9 +1588,10 @@
        // 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.setTitle(Constants.OrderLogType.driverDeliver.getTitle());
        log.setLogInfo(Constants.OrderLogType.driverDeliver.format(
                StringUtils.isNotBlank(dto.getRemark()) ? dto.getRemark() : "司机【" + driver.getName() + "】已送达"));
        log.setObjType(Constants.OrderLogType.driverDeliver.getStatus());
        log.setOptUserId(driver.getMemberId());
        log.setOptUserType(Constants.ONE);
        log.setOrderStatus(Constants.OrderStatus.arrived.getStatus());
@@ -1550,6 +1651,22 @@
            return Math.round(km * 1000) + "m";
        }
        return String.format("%.1fkm", km);
    }
    /**
     * ç”Ÿæˆ6位数字核销码(Redis SETNX ä¿è¯å”¯ä¸€ï¼‰
     */
    private String generateVerifyCode() {
        Random random = new Random();
        String redisKey = Constants.REDIS_VERIFY_CODE_KEY;
        for (int i = 0; i < 200; i++) {
            String code = String.format("%06d", random.nextInt(1000000));
            Boolean success = redisTemplate.opsForValue().setIfAbsent(redisKey + code, "1", 24, TimeUnit.HOURS);
            if (success != null && success) {
                return code;
            }
        }
        throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "核销码生成失败,请重试");
    }
    private double haversine(double lat1, double lng1, double lat2, double lng2) {
@@ -1613,9 +1730,12 @@
        boolean hasTakeShop = order.getTakeShopId() != null && StringUtils.isNotBlank(order.getTakeShopName());
        if (hasTakeShop) {
            vo.setTakeName(order.getTakeShopName());
            vo.setTakeAddress(order.getTakeShopAddress());
            vo.setTakeShopId(order.getTakeShopId());
            vo.setContactPhone(order.getTakeShopLinkPhone());
        } else {
            vo.setTakeName(order.getTakeLocation());
            vo.setTakeAddress(order.getTakeLocationRemark());
            vo.setContactPhone(order.getTakePhone());
        }
        if (driverLat != null && driverLng != null
@@ -1627,14 +1747,36 @@
        // è´µé‡ç‰©å“
        vo.setIsValuable("1".equals(order.getC2OtherField()));
        vo.setGoodLevelName(order.getGoodLevelName());
        vo.setHasOversized(order.getHasOversized());
        // å¾…取货状态(status=3)返回司机取货码
        if (Constants.equalsInteger(order.getStatus(), Constants.THREE)||Constants.equalsInteger(order.getStatus(), Constants.FOUR)) {
            vo.setDriverVerifyCode(order.getDriverVerifyCode());
        }
        // ç‰©å“æ˜Žç»†
        List<OrdersDetail> details = detailMap.getOrDefault(order.getId(), Collections.emptyList());
        // æ‰¹é‡æŸ¥è¯¢æ¶‰åŠåˆ°çš„ luggageId å¯¹åº”çš„ category,判断是否大件
        Set<Integer> luggageIds = details.stream()
                .map(OrdersDetail::getLuggageId)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());
        Set<Integer> oversizedIds = new HashSet<>();
        if (!luggageIds.isEmpty()) {
            categoryMapper.selectList(new QueryWrapper<Category>().lambda()
                    .in(Category::getId, luggageIds)
                    .eq(Category::getType, Constants.FOUR)
                    .eq(Category::getOtherField, "1")
                    .eq(Category::getDeleted, Constants.ZERO))
                    .forEach(c -> oversizedIds.add(c.getId()));
        }
        List<DriverGrabOrderVO.OrderItem> items = new ArrayList<>();
        for (OrdersDetail detail : details) {
            DriverGrabOrderVO.OrderItem item = new DriverGrabOrderVO.OrderItem();
            item.setName(detail.getLuggageName());
            item.setQuantity(detail.getNum());
            item.setIsOversized(oversizedIds.contains(detail.getLuggageId()) ? Constants.ONE : Constants.ZERO);
            items.add(item);
        }
        vo.setItems(items);
@@ -1686,4 +1828,166 @@
        }
    }
    @Override
    public void changePassword(Integer driverId, String newPassword, String token) {
        if (StringUtils.isBlank(newPassword)) {
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "密码不能为空");
        }
        // æ ¡éªŒå¯†ç å¿…须同时包含字母和数字
        boolean hasLetter = newPassword.chars().anyMatch(Character::isLetter);
        boolean hasDigit = newPassword.chars().anyMatch(Character::isDigit);
        if (!hasLetter || !hasDigit) {
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "密码必须同时包含字母和数字");
        }
        // æŸ¥è¯¢å¸æœºå¯¹åº”的会员
        DriverInfo driverInfo = driverInfoMapper.selectById(driverId);
        if (driverInfo == null || driverInfo.getMemberId() == null) {
            throw new BusinessException(ResponseStatus.DATA_EMPTY);
        }
        Member member = memberMapper.selectById(driverInfo.getMemberId());
        if (member == null) {
            throw new BusinessException(ResponseStatus.DATA_EMPTY);
        }
        // åŠ å¯†æ–°å¯†ç å¹¶æ›´æ–°
        String salt = RandomStringUtils.randomAlphanumeric(6);
        String encryptPwd = secure.encryptPassword(newPassword, salt);
        memberMapper.update(new UpdateWrapper<Member>().lambda()
                .set(Member::getPassword, encryptPwd)
                .set(Member::getSalt, salt)
                .eq(Member::getId, member.getId()));
        // æ¸…除token,强制重新登录
        if (StringUtils.isNotBlank(token)) {
            redisTemplate.delete(token);
        }
    }
    @Override
    public DriverActiveOrderCountVO getActiveOrderCount(Integer driverId) {
        // å·²æŠ¢å•(status=3)数量
        Long grabbed = ordersMapper.selectCount(new QueryWrapper<Orders>().lambda()
                .eq(Orders::getAcceptDriver, driverId)
                .eq(Orders::getStatus, Constants.OrderStatus.accepted.getStatus())
                .eq(Orders::getDeleted, Constants.ZERO));
        // æ´¾é€ä¸­(status=4)数量
        Long delivering = ordersMapper.selectCount(new QueryWrapper<Orders>().lambda()
                .eq(Orders::getAcceptDriver, driverId)
                .eq(Orders::getStatus, Constants.OrderStatus.delivering.getStatus())
                .eq(Orders::getDeleted, Constants.ZERO));
        DriverActiveOrderCountVO vo = new DriverActiveOrderCountVO();
        vo.setGrabbedCount(grabbed != null ? grabbed.intValue() : 0);
        vo.setDeliveringCount(delivering != null ? delivering.intValue() : 0);
        return vo;
    }
    @Override
    public PageData<DriverGrabOrderVO> driverOrderPage(Integer driverId, PageWrap<DriverOrderPageDTO> pageWrap) {
        DriverInfo driver = driverInfoMapper.selectById(driverId);
        if (driver == null) {
            throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "司机信息不存在");
        }
        DriverOrderPageDTO model = pageWrap.getModel();
        Integer status = model != null ? model.getStatus() : null;
        // åˆæ³•状态校验
        List<Integer> validStatuses = Arrays.asList(
                Constants.OrderStatus.accepted.getStatus(),
                Constants.OrderStatus.delivering.getStatus(),
                Constants.OrderStatus.finished.getStatus());
        if (status != null && !validStatuses.contains(status)) {
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "状态只能为3(待取件)、4(配送中)、7(已完成)");
        }
        IPage<Orders> p = new Page<>(pageWrap.getPage(), pageWrap.getCapacity());
        MPJLambdaWrapper<Orders> wrapper = new MPJLambdaWrapper<>();
        wrapper.selectAll(Orders.class)
                .select("s1.name", Orders::getDepositShopName)
                .select("s1.address", Orders::getDepositShopAddress)
                .select("s2.name", Orders::getTakeShopName)
                .select("s2.address", Orders::getTakeShopAddress)
                .select("s2.link_phone as takeShopLinkPhone")
                .select("c2.other_field as c2OtherField")
                .select("c2.name as goodLevelName")
                .select("IF(EXISTS(SELECT 1 FROM orders_detail od JOIN category c3 ON c3.id = od.LUGGAGE_ID AND c3.TYPE = 4 AND c3.OTHER_FIELD = '1' WHERE od.ORDER_ID = t.ID AND od.DELETED = 0), 1, 0) as hasOversized")
                .leftJoin("shop_info s1 on s1.id = t.DEPOSIT_SHOP_ID and s1.DELETED = 0")
                .leftJoin("shop_info s2 on s2.id = t.TAKE_SHOP_ID and s2.DELETED = 0")
                .leftJoin("category c1 on c1.id = t.GOOD_TYPE and c1.DELETED = 0")
                .leftJoin("category c2 on c2.id = c1.RELATION_ID and c2.DELETED = 0 and c2.TYPE = 3")
                .eq(Orders::getAcceptDriver, driverId)
                .in(status == null, Orders::getStatus, validStatuses)
                .eq(status != null, Orders::getStatus, status)
                .eq(Orders::getDeleted, Constants.ZERO)
                .orderByDesc(Orders::getAcceptTime);
        IPage<Orders> orderPage = ordersMapper.selectJoinPage(p, Orders.class, wrapper);
        List<DriverGrabOrderVO> voList = new ArrayList<>();
        if (orderPage != null && orderPage.getRecords() != null) {
            // æ‰¹é‡æŸ¥ç‰©å“æ˜Žç»†
            List<Integer> orderIds = orderPage.getRecords().stream().map(Orders::getId).collect(Collectors.toList());
            Map<Integer, List<OrdersDetail>> detailMap = new HashMap<>();
            if (!orderIds.isEmpty()) {
                List<OrdersDetail> allDetails = ordersDetailMapper.selectList(
                        new QueryWrapper<OrdersDetail>().lambda()
                                .in(OrdersDetail::getOrderId, orderIds));
                for (OrdersDetail d : allDetails) {
                    detailMap.computeIfAbsent(d.getOrderId(), k -> new ArrayList<>()).add(d);
                }
            }
            Double driverLat = driver.getLatitude();
            Double driverLng = driver.getLongitude();
            Date now = new Date();
            for (Orders order : orderPage.getRecords()) {
                boolean needDepositDist = Constants.equalsInteger(order.getStatus(), Constants.OrderStatus.accepted.getStatus());
                voList.add(buildDriverOrderVO(order, driverLat, driverLng, needDepositDist, now, detailMap));
            }
        }
        IPage<DriverGrabOrderVO> vPage = new Page<>(pageWrap.getPage(), pageWrap.getCapacity());
        PageData<DriverGrabOrderVO> pageData = PageData.from(vPage);
        pageData.setRecords(voList);
        if (orderPage != null) {
            pageData.setTotal(orderPage.getTotal());
            pageData.setPage(orderPage.getCurrent());
            pageData.setCapacity(orderPage.getSize());
        }
        return pageData;
    }
    @Override
    public DriverCancelLimitVO getTodayCancelLimit(Integer driverId) {
        DriverInfo driver = driverInfoMapper.selectById(driverId);
        if (driver == null) {
            throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "司机信息不存在");
        }
        // æ¯æ—¥å–消上限
        String limitStr = operationConfigBiz.getConfig().getDriverDailyCancelLimit();
        int limit = 3;
        if (StringUtils.isNotBlank(limitStr)) {
            try { limit = Integer.parseInt(limitStr); } catch (NumberFormatException ignored) {}
        }
        // ä»Šæ—¥å·²å–消次数
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MILLISECOND, 0);
        Date todayStart = cal.getTime();
        Long todayCancelCount = orderLogMapper.selectCount(new QueryWrapper<OrderLog>().lambda()
                .eq(OrderLog::getOptUserId, driver.getMemberId())
                .eq(OrderLog::getObjType, Constants.OrderLogType.driverCancel.getStatus())
                .eq(OrderLog::getOptUserType, Constants.ONE)
                .ge(OrderLog::getCreateTime, todayStart));
        int used = todayCancelCount != null ? todayCancelCount.intValue() : 0;
        DriverCancelLimitVO vo = new DriverCancelLimitVO();
        vo.setLimit(limit);
        vo.setUsed(used);
        vo.setRemain(Math.max(limit - used, 0));
        return vo;
    }
}
server/services/src/main/java/com/doumee/service/business/impl/MemberServiceImpl.java
@@ -48,7 +48,9 @@
import javax.annotation.Resource;
import java.util.Arrays;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Random;
import java.util.concurrent.TimeUnit;
@@ -440,9 +442,34 @@
    public PlatformAboutVO getPlatformAboutUs(){
        PlatformAboutVO vo = new PlatformAboutVO();
        vo.setAboutUs(StringUtils.trimToNull(systemDictDataBiz.queryByCode(Constants.SYSTEM,Constants.ABOUT_US).getCode()));
        vo.setUserAgreement(StringUtils.trimToNull(systemDictDataBiz.queryByCode(Constants.SYSTEM,Constants.USER_AGREEMENT).getCode()));
        vo.setPrivacyAgreement(StringUtils.trimToNull(systemDictDataBiz.queryByCode(Constants.SYSTEM,Constants.PRIVACY_AGREEMENT).getCode()));
        vo.setServerIntroduce(StringUtils.trimToNull(systemDictDataBiz.queryByCode(Constants.SYSTEM,Constants.SERVER_INTRODUCE).getCode()));
        Map<String, String> agreements = new LinkedHashMap<>();
        String[] agreementKeys = {
                Constants.STORE_RISK_COMMITMENT,
                Constants.STORE_COOPERATION_AGREEMENT,
                Constants.STORE_LUGGAGE_STORAGE_NOTICE,
                Constants.STORE_PRIVACY_POLICY,
                Constants.OWNER_LUGGAGE_STORAGE_NOTICE,
                Constants.OWNER_SERVICE_AGREEMENT,
                Constants.OWNER_RISK_COMMITMENT,
                Constants.ERRAND_RISK_COMMITMENT,
                Constants.ERRAND_SERVICE_AGREEMENT,
                Constants.ERRAND_LUGGAGE_STORAGE_NOTICE,
                Constants.USER_SERVICE_AGREEMENT,
                Constants.USER_PRIVACY_POLICY,
                Constants.DRIVER_PRIVACY_POLICY,
                Constants.PRICE_DESCRIPTION,
                Constants.PROHIBITED_ITEMS
        };
        for (String key : agreementKeys) {
            try {
                String value = StringUtils.trimToNull(systemDictDataBiz.queryByCode(Constants.SYSTEM, key).getCode());
                agreements.put(key, value);
            } catch (Exception e) {
                agreements.put(key, null);
            }
        }
        vo.setAgreements(agreements);
        return vo;
    }
server/services/src/main/java/com/doumee/service/business/impl/OrderLogServiceImpl.java
@@ -121,6 +121,9 @@
        if (pageWrap.getModel().getOptUserId() != null) {
            queryWrapper.lambda().eq(OrderLog::getOptUserId, pageWrap.getModel().getOptUserId());
        }
        if (pageWrap.getModel().getObjTypeList() != null && !pageWrap.getModel().getObjTypeList().isEmpty()) {
            queryWrapper.lambda().in(OrderLog::getObjType, pageWrap.getModel().getObjTypeList());
        }
        for (PageWrap.SortData sortData : pageWrap.getSorts()) {
            if (sortData.getDirection().equalsIgnoreCase(PageWrap.DESC)) {
                queryWrapper.orderByDesc(sortData.getProperty());
@@ -128,6 +131,8 @@
                queryWrapper.orderByAsc(sortData.getProperty());
            }
        }
        // é»˜è®¤æŒ‰æ—¶é—´å€’序
        queryWrapper.lambda().orderByDesc(OrderLog::getCreateTime);
        return PageData.from(orderLogMapper.selectPage(page, queryWrapper));
    }
server/services/src/main/java/com/doumee/service/business/impl/OrdersServiceImpl.java
@@ -229,6 +229,7 @@
        queryWrapper.eq(pageWrap.getModel().getTakeShopId() != null, Orders::getTakeShopId, pageWrap.getModel().getTakeShopId());
        queryWrapper.and(pageWrap.getModel().getDriverKeyword() != null, i->i.like(DriverInfo::getName, pageWrap.getModel().getDriverKeyword())
                .or().like(DriverInfo::getTelephone, pageWrap.getModel().getDriverKeyword()));
        queryWrapper.eq(pageWrap.getModel().getSettlementStatus() != null, Orders::getSettlementStatus, pageWrap.getModel().getSettlementStatus());
        for (PageWrap.SortData sortData : pageWrap.getSorts()) {
            if (sortData.getDirection().equalsIgnoreCase(PageWrap.DESC)) {
                queryWrapper.orderByDesc(sortData.getProperty());
@@ -262,6 +263,7 @@
        queryWrapper.eq(pageWrap.getModel().getTakeShopId() != null, Orders::getTakeShopId, pageWrap.getModel().getTakeShopId());
        queryWrapper.and(pageWrap.getModel().getDriverKeyword() != null, i->i.like(DriverInfo::getName, pageWrap.getModel().getDriverKeyword())
                .or().like(DriverInfo::getTelephone, pageWrap.getModel().getDriverKeyword()));
        queryWrapper.eq(pageWrap.getModel().getSettlementStatus() != null, Orders::getSettlementStatus, pageWrap.getModel().getSettlementStatus());
        queryWrapper.select(
                "IFNULL(SUM(t.total_amount), 0) as total_amount_sum",
@@ -1109,13 +1111,12 @@
        String optUserName = getCurrentUserName();
        // åŠ æ€¥è´¹æ—¥å¿—ï¼ˆæ¯æ¬¡å•ç‹¬è®°å½•æœ¬æ¬¡åŠ æ€¥è´¹ï¼‰
        Constants.OrderLogType urgentLogType = Constants.OrderLogType.urgent;
        // åŠ æ€¥è´¹æ—¥å¿—
        OrderLog feeLog = new OrderLog();
        feeLog.setOrderId(order.getId());
        feeLog.setTitle(urgentLogType.getTitle());
        feeLog.setLogInfo(urgentLogType.getStatusInfo().replace("{param}", dto.getUrgentFee().toPlainString()));
        feeLog.setObjType(urgentLogType.getStatus());
        feeLog.setTitle(Constants.OrderLogType.urgent.getTitle());
        feeLog.setLogInfo(Constants.OrderLogType.urgent.format(dto.getUrgentFee().toPlainString()));
        feeLog.setObjType(Constants.OrderLogType.urgent.getStatus());
        feeLog.setOrderStatus(order.getStatus());
        feeLog.setOptUserType(3);
        feeLog.setOptUserName(optUserName);
@@ -1154,18 +1155,37 @@
            Member driver = memberMapper.selectById(dto.getDriverId());
            String driverName = driver != null ? driver.getName() : String.valueOf(dto.getDriverId());
            Constants.OrderLogType dispatchLogType = Constants.OrderLogType.dispatch;
            OrderLog driverLog = new OrderLog();
            driverLog.setOrderId(order.getId());
            driverLog.setTitle(dispatchLogType.getTitle());
            driverLog.setLogInfo(dispatchLogType.getStatusInfo().replace("{param}", driverName));
            driverLog.setObjType(dispatchLogType.getStatus());
            driverLog.setTitle(Constants.OrderLogType.assignDriver.getTitle());
            driverLog.setLogInfo(Constants.OrderLogType.assignDriver.format(driverName, dto.getUrgentFee().toPlainString()));
            driverLog.setObjType(Constants.OrderLogType.assignDriver.getStatus());
            driverLog.setOrderStatus(order.getStatus());
            driverLog.setOptUserType(3);
            driverLog.setOptUserName(optUserName);
            driverLog.setCreateTime(new Date());
            driverLog.setDeleted(Constants.ZERO);
            orderLogService.create(driverLog);
            // çŸ­ä¿¡é€šçŸ¥æŒ‡æ´¾å¸æœºï¼ˆåŠ æ€¥æ´¾å•ï¼‰
            DriverInfo driverInfo = driverInfoMapper.selectOne(new QueryWrapper<DriverInfo>().lambda()
                    .eq(DriverInfo::getMemberId, dto.getDriverId())
                    .eq(DriverInfo::getDeleted, Constants.ZERO)
                    .last("limit 1"));
            if (driverInfo != null) {
                String address1 = order.getDepositShopAddress() != null ? order.getDepositShopAddress() : order.getDepositShopName();
                String address2 = order.getTakeShopAddress() != null ? order.getTakeShopAddress() :
                        (order.getTakeLocation() != null ? order.getTakeLocation() : "");
                // é…é€è´¹ = å¸æœºé…é€è´¹ + åŠ æ€¥è´¹
                long totalDriverFee = (order.getDriverFee() != null ? order.getDriverFee() : 0L) + urgentFeeFen;
                sendSmsNotify(driverInfo.getTelephone(),
                        Constants.SmsNotify.DRIVER_URGENT_DISPATCH,
                        "orderNo", order.getCode(),
                        "address1", address1,
                        "address2", address2,
                        "money1", String.valueOf(totalDriverFee / 100.0),
                        "money2", String.valueOf(urgentFeeFen / 100.0));
            }
        }
        ordersMapper.update(updateWrapper);
@@ -1371,8 +1391,12 @@
                .select("s1.link_phone", Orders::getDepositShopLinkPhone)
                .select("s2.name", Orders::getTakeShopName)
                .select("s2.address", Orders::getTakeShopAddress)
                .select("s2.link_phone", Orders::getTakeShopLinkPhone)
                .select("d.name", Orders::getDriverName)
                .select("d.telephone", Orders::getDriverPhone)
                .leftJoin("shop_info s1 on s1.id = t.DEPOSIT_SHOP_ID")
                .leftJoin("shop_info s2 on s2.id = t.TAKE_SHOP_ID")
                .leftJoin("driver_info d on d.id = t.ACCEPT_DRIVER")
                .eq(Orders::getDeleted, Constants.ZERO)
                .eq(Orders::getMemberId, memberId)
                .eq(status != null, Orders::getStatus, status)
@@ -1407,10 +1431,15 @@
                    vo.setTakeShopId(o.getTakeShopId());
                    vo.setTakeShopName(o.getTakeShopName());
                    vo.setTakeShopAddress(o.getTakeShopAddress());
                    vo.setTakeShopPhone(o.getTakeShopLinkPhone());
                } else {
                    vo.setTakeLocation(o.getTakeLocation());
                    vo.setTakeLocationRemark(o.getTakeLocationRemark());
                }
                // å¸æœºä¿¡æ¯
                vo.setDriverName(o.getDriverName());
                vo.setDriverPhone(o.getDriverPhone());
                // å–件联系人
                vo.setTakeUser(o.getTakeUser());
@@ -1432,13 +1461,8 @@
                // ç‰©å“æ˜Žç»†
                vo.setDetailList(buildDetailList(details));
                // é€¾æœŸä¿¡æ¯ï¼ˆä»…待取件状态计算)
                if (Integer.valueOf(Constants.OrderStatus.arrived.getStatus()).equals(o.getStatus())) {
                    OverdueFeeVO overdueInfo = calculateOverdueFeeInternal(o, details);
                    vo.setOverdue(overdueInfo.getOverdue());
                    vo.setOverdueDays(overdueInfo.getOverdueDays());
                    vo.setOverdueFee(overdueInfo.getOverdueFee());
                }
                // é€¾æœŸçŠ¶æ€
                fillOverdueStatus(vo, o, details);
                voList.add(vo);
            }
        }
@@ -1478,13 +1502,31 @@
                .select("s1.link_phone", Orders::getDepositShopLinkPhone)
                .select("s2.name", Orders::getTakeShopName)
                .select("s2.address", Orders::getTakeShopAddress)
                .select("s2.link_phone", Orders::getTakeShopLinkPhone)
                .select("d.name", Orders::getDriverName)
                .select("d.telephone", Orders::getDriverPhone)
                .leftJoin("shop_info s1 on s1.id = t.DEPOSIT_SHOP_ID")
                .leftJoin("shop_info s2 on s2.id = t.TAKE_SHOP_ID")
                .eq(Orders::getPayStatus, Constants.ONE)
                .and(w -> w.eq(Orders::getDepositShopId, shopId).or().eq(Orders::getTakeShopId, shopId))
                .eq(status != null, Orders::getStatus, status)
                .in(statusList != null, Orders::getStatus, statusList)
                .orderByDesc(Orders::getCreateTime);
                .leftJoin("driver_info d on d.id = t.ACCEPT_DRIVER")
                .eq(Orders::getPayStatus, Constants.ONE);
        // é—¨åº—待处理订单:按业务环节区分门店角色
        if (combinedStatus != null && Constants.equalsInteger(combinedStatus, Constants.SEVEN)) {
            wrapper.and(w -> w
                    .and(w1 -> w1.eq(Orders::getDepositShopId, shopId)
                            .in(Orders::getStatus, Constants.OrderStatus.waitDeposit.getStatus(),
                                    Constants.OrderStatus.deposited.getStatus()))
                    .or(w2 -> w2.eq(Orders::getTakeShopId, shopId)
                            .in(Orders::getStatus, Constants.OrderStatus.delivering.getStatus(),
                                    Constants.OrderStatus.arrived.getStatus()))
            );
        } else {
            wrapper.and(w -> w.eq(Orders::getDepositShopId, shopId).or().eq(Orders::getTakeShopId, shopId));
        }
        wrapper.eq(status != null, Orders::getStatus, status)
                .in(statusList != null && !Constants.equalsInteger(combinedStatus, Constants.SEVEN), Orders::getStatus, statusList)
                .orderByDesc(Orders::getId);
        IPage<Orders> orderPage = ordersMapper.selectJoinPage(p, Orders.class, wrapper);
        List<MyOrderVO> voList = new ArrayList<>();
@@ -1499,6 +1541,7 @@
                        Constants.equalsInteger(o.getType(), Constants.ZERO)?o.getType():Objects.nonNull(o.getTakeShopId())?Constants.ONE:Constants.TWO)
                );
                vo.setCreateTime(o.getCreateTime());
                vo.setRemark(o.getRemark());
                vo.setExpectedTakeTime(o.getExpectedTakeTime());
                vo.setDepositShopName(o.getDepositShopName());
@@ -1515,10 +1558,15 @@
                if (o.getTakeShopId() != null) {
                    vo.setTakeShopName(o.getTakeShopName());
                    vo.setTakeShopAddress(o.getTakeShopAddress());
                    vo.setTakeShopPhone(o.getTakeShopLinkPhone());
                } else {
                    vo.setTakeLocation(o.getTakeLocation());
                    vo.setTakeLocationRemark(o.getTakeLocationRemark());
                }
                // å¸æœºä¿¡æ¯
                vo.setDriverName(o.getDriverName());
                vo.setDriverPhone(o.getDriverPhone());
                vo.setTakeUser(o.getTakeUser());
                vo.setTakePhone(o.getTakePhone());
@@ -1532,12 +1580,16 @@
                vo.setDetailList(buildDetailList(details));
                if (Integer.valueOf(Constants.OrderStatus.arrived.getStatus()).equals(o.getStatus())) {
                    OverdueFeeVO overdueInfo = calculateOverdueFeeInternal(o, details);
                    vo.setOverdue(overdueInfo.getOverdue());
                    vo.setOverdueDays(overdueInfo.getOverdueDays());
                    vo.setOverdueFee(overdueInfo.getOverdueFee());
                }
                // é€¾æœŸçŠ¶æ€
                fillOverdueStatus(vo, o, details);
//                if (Integer.valueOf(Constants.OrderStatus.arrived.getStatus()).equals(o.getStatus())) {
//                    OverdueFeeVO overdueInfo = calculateOverdueFeeInternal(o, details);
//                    vo.setOverdue(overdueInfo.getOverdue());
//                    vo.setOverdueDays(overdueInfo.getOverdueDays());
//                    vo.setOverdueFee(overdueInfo.getOverdueFee());
//                }
                voList.add(vo);
            }
        }
@@ -1583,7 +1635,7 @@
            }
        }
        if (order == null) {
            throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "订单不存在");
            throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "核销码无效");
        }
        return buildOrderDetailVO(order, false);
    }
@@ -1614,6 +1666,7 @@
        vo.setDeclaredAmount(order.getDeclaredAmount());
        vo.setDeclaredFee(order.getDeclaredFee());
        vo.setUrgentAmount(order.getUrgentAmount());
        vo.setIsUrgent(order.getIsUrgent());
        vo.setActualPayAmount(Constants.equalsInteger(order.getPayStatus(), Constants.ONE)?order.getPayAmount():order.getEstimatedAmount());
        // æ ‡è®°
@@ -1685,66 +1738,20 @@
                        .eq(OrdersDetail::getDeleted, Constants.ZERO));
        vo.setDetailList(buildDetailList(details));
        Integer orderStatus = order.getStatus();
        if(Constants.equalsInteger(orderStatus, Constants.FIVE)){
            // é€¾æœŸä¿¡æ¯
            OverdueFeeVO overdueInfo = calculateOverdueFeeInternal(order, details);
            if (Constants.ONE.equals(order.getType())
                    && order.getTakeShopId() != null) {
                // å¼‚地寄存 + æœ‰å–件门店:
                // æ ¹æ®è¡ŒæŽè½¬ç§»åˆ°åº—æ—¶é—´(arriveTime)当天晚上12点判断是否逾期
                if (order.getArriveTime() != null) {
                    Calendar arriveCal = Calendar.getInstance();
                    arriveCal.setTime(order.getArriveTime());
                    arriveCal.set(Calendar.HOUR_OF_DAY, 23);
                    arriveCal.set(Calendar.MINUTE, 59);
                    arriveCal.set(Calendar.SECOND, 59);
                    Date arriveEndOfDay = arriveCal.getTime();
                    boolean isOverdue = new Date().after(arriveEndOfDay);
                    vo.setOverdue(isOverdue);
                    if (isOverdue) {
                        vo.setOverdueDays(overdueInfo.getOverdueDays() > 0 ? overdueInfo.getOverdueDays() : 1);
                        vo.setOverdueFee(overdueInfo.getOverdueFee());
                    } else {
                        vo.setOverdueDays(0);
                        vo.setOverdueFee(0L);
                    }
                } else {
                    vo.setOverdue(false);
                    vo.setOverdueDays(0);
                    vo.setOverdueFee(0L);
                }
            } else if (Constants.ZERO.equals(order.getType())) {
                // å°±åœ°å¯„存:保持原逻辑
                vo.setOverdue(overdueInfo.getOverdue());
                vo.setOverdueDays(overdueInfo.getOverdueDays());
                vo.setOverdueFee(overdueInfo.getOverdueFee());
            }
        } else {
            vo.setOverdue(false);
            vo.setOverdueDays(0);
            vo.setOverdueFee(0L);
        }
        // é€¾æœŸçŠ¶æ€ï¼š0=未到店未逾期 1=未到店存在逾期 2=已到店未逾期 3=已到店待支付逾期 4=逾期已支付
        fillOverdueStatus(vo, order, details);
        // é€€æ¬¾ä¿¡æ¯ï¼ˆstatus=96关闭/99取消时返回)
        if (orderStatus != null &&
                (Constants.equalsInteger(orderStatus, Constants.OrderStatus.cancelled.getStatus()))) {
        // é€€æ¬¾ä¿¡æ¯ï¼ˆstatus=99取消时返回)
        if (order.getStatus() != null &&
                Constants.equalsInteger(order.getStatus(), Constants.OrderStatus.cancelled.getStatus())) {
            vo.setRefundApplyTime(order.getCancelTime());
            // æŸ¥è¯¢é€€æ¬¾è®°å½•获取退款金额和备注
            OrdersRefund ordersRefund = ordersRefundMapper.selectOne(
                    new QueryWrapper<OrdersRefund>().lambda()
                            .eq(OrdersRefund::getOrderId, order.getId())
                            .eq(OrdersRefund::getDeleted, Constants.ZERO)
                            .orderByDesc(OrdersRefund::getCreateTime)
                            .last("limit 1"));
            if (ordersRefund != null) {
                vo.setRefundAmount(ordersRefund.getRefundAmount() != null
                        ? ordersRefund.getRefundAmount() : order.getRefundAmount());
                vo.setRefundRemark(ordersRefund.getRemark());
                vo.setRefundTime(ordersRefund.getRefundTime());
            } else {
                vo.setRefundAmount(order.getRefundAmount());
            }
            vo.setRefundInfo(ordersRefund);
        }
        // æ ¸é”€ç 
@@ -1855,7 +1862,7 @@
            order.setStatus(Constants.OrderStatus.cancelled.getStatus());
            order.setCancelTime(now);
            ordersMapper.updateById(order);
            saveCancelLog(order, "会员取消订单(待支付)", reason, memberId);
            saveCancelLog(order, Constants.OrderLogType.memberCancel, "会员取消订单(待支付)", memberId);
            // çŸ­ä¿¡é€šçŸ¥ä¼šå‘˜ï¼šè®¢å•已取消
            Member cancelMember1 = memberMapper.selectById(memberId);
            sendSmsNotify(cancelMember1 != null ? cancelMember1.getTelephone() : null,
@@ -1871,7 +1878,7 @@
            order.setRefundAmount(order.getPayAmount());
            ordersMapper.updateById(order);
            saveCancelLog(order, "会员取消订单(待寄存,全额退款)", reason, memberId);
            saveCancelLog(order, Constants.OrderLogType.memberCancel, "会员取消订单(待寄存,全额退款)", memberId);
            // é€šçŸ¥ä¼šå‘˜ï¼šå·²å–消
            sendOrderNotice(memberId, Constants.MemberOrderNotify.CANCELLED, orderId,
                    "orderNo", order.getCode());
@@ -1926,23 +1933,38 @@
        // å·²å¯„å­˜/已接单:直接将订单类型改为就地寄存
        if (Constants.equalsInteger(status, Constants.OrderStatus.deposited.getStatus())
                || Constants.equalsInteger(status, Constants.OrderStatus.accepted.getStatus())) {
            order.setType(Constants.ZERO); // å°±åœ°å¯„å­˜
            ordersMapper.updateById(order);
            saveCancelLog(order, "会员取消异地寄存订单,转为就地寄存", reason, memberId);
            // é€šçŸ¥å­˜ä»¶é—¨åº—
            if (order.getDepositShopId() != null) {
                sendShopNotice(order.getDepositShopId(), Constants.ShopOrderNotify.REFUNDING, orderId,
                        "orderNo", order.getCode());
                ShopInfo depositShop = shopInfoMapper.selectById(order.getDepositShopId());
                sendSmsNotify(depositShop != null ? depositShop.getLinkPhone() : null,
                        Constants.SmsNotify.SHOP_REFUNDING, "orderNo", order.getCode());
            // å…ˆä¿å­˜åŽŸå¸æœºä¿¡æ¯ï¼Œç”¨äºŽåŽç»­é€šçŸ¥
            Integer originalDriverId = order.getAcceptDriver();
            DriverInfo originalDriver = originalDriverId != null ? driverInfoMapper.selectById(originalDriverId) : null;
            ShopInfo depositShop = shopInfoMapper.selectById(order.getDepositShopId());
            // å–件点信息更新为存件门店,使用 UpdateWrapper ç¡®ä¿ null å­—段也能生效
            UpdateWrapper<Orders> updateWrapper = new UpdateWrapper<>();
            updateWrapper.lambda()
                    .eq(Orders::getId, order.getId())
                    .set(Orders::getType, Constants.ZERO)
                    .set(Orders::getTakeShopId, order.getDepositShopId())
                    .set(Orders::getTakeShopName, order.getDepositShopName())
                    .set(Orders::getTakeShopAddress, order.getDepositShopAddress())
                    .set(Orders::getTakeShopLinkPhone, order.getDepositShopLinkPhone())
                    .set(Orders::getTakeLocation, order.getDepositLocation())
                    .set(Orders::getTakeLocationRemark, order.getDepositLocationRemark())
                    .set(Orders::getTakeLat, order.getDepositLat())
                    .set(Orders::getTakeLgt, order.getDepositLgt())
                    .set(Orders::getExpectedTakeTime, new Date());
            if (Constants.equalsInteger(status, Constants.OrderStatus.accepted.getStatus())) {
                updateWrapper.lambda()
                        .set(Orders::getAcceptDriver, null)
                        .set(Orders::getAcceptType, null)
                        .set(Orders::getAcceptTime, null);
            }
            ordersMapper.update(null, updateWrapper);
            saveCancelLog(order, Constants.OrderLogType.memberCancelToConvert, "会员取消异地寄存订单,转为就地寄存", memberId);
            // é€šçŸ¥å¸æœºï¼šè®¢å•已取消(已接单情况下司机需停止服务)
            if (order.getAcceptDriver() != null && Constants.equalsInteger(status, Constants.OrderStatus.accepted.getStatus())) {
                sendDriverNotice(order.getAcceptDriver(), Constants.DriverOrderNotify.REFUNDING, orderId,
            if (originalDriverId != null && Constants.equalsInteger(status, Constants.OrderStatus.accepted.getStatus())) {
                sendDriverNotice(originalDriverId, Constants.DriverOrderNotify.REFUNDING, orderId,
                        "orderNo", order.getCode());
                DriverInfo driver = driverInfoMapper.selectById(order.getAcceptDriver());
                sendSmsNotify(driver != null ? driver.getTelephone() : null,
                sendSmsNotify(originalDriver != null ? originalDriver.getTelephone() : null,
                        Constants.SmsNotify.DRIVER_REFUNDING, "orderNo", order.getCode());
            }
            return;
@@ -1954,12 +1976,12 @@
    /**
     * ä¿å­˜å–消订单操作日志
     */
    private void saveCancelLog(Orders order, String title, String reason, Integer memberId) {
    private void saveCancelLog(Orders order, Constants.OrderLogType logType, String reason, Integer memberId) {
        OrderLog log = new OrderLog();
        log.setOrderId(order.getId());
        log.setTitle(title);
        log.setLogInfo(reason);
        log.setObjType(Constants.ORDER_LOG_CANCEL);
        log.setTitle(logType.getTitle());
        log.setLogInfo(logType.format(reason));
        log.setObjType(logType.getStatus());
        log.setOrderStatus(order.getStatus());
        log.setOptUserId(memberId);
        log.setOptUserType(0); // 0=用户
@@ -1971,12 +1993,13 @@
    /**
     * ä¿å­˜é—¨åº—核销日志
     */
    private void saveShopVerifyLog(Orders order, String title, String logInfo, String remark, Integer shopId) {
    private void saveShopVerifyLog(Orders order, Constants.OrderLogType logType, String logInfo, String remark, Integer shopId) {
        OrderLog log = new OrderLog();
        log.setOrderId(order.getId());
        log.setTitle(title);
        log.setLogInfo(logInfo);
        log.setTitle(logType.getTitle());
        log.setLogInfo(logInfo != null ? logInfo : logType.getStatusInfo());
        log.setRemark(remark);
        log.setObjType(logType.getStatus());
        log.setOrderStatus(order.getStatus());
        log.setOptUserId(shopId);
        log.setOptUserType(2); // 2=门店
@@ -2307,6 +2330,15 @@
        shopInfo.setUpdateTime(now);
        shopInfoMapper.updateById(shopInfo);
        // çŸ­ä¿¡é€šçŸ¥é—¨åº—:成功入驻
        String rawPassword = shopInfo.getTelephone() != null && shopInfo.getTelephone().length() >= 6
                ? shopInfo.getTelephone().substring(shopInfo.getTelephone().length() - 6) + "@123456" : "";
        sendSmsNotify(shopInfo.getTelephone(),
                Constants.SmsNotify.SHOP_AUTH_SUCCESS,
                "storeName", shopInfo.getName(),
                "phone", shopInfo.getTelephone() != null ? shopInfo.getTelephone() : "",
                "password", rawPassword);
        // 6. æŠ¼é‡‘支付完成后,若城市未开通则自动开通
        if (shopInfo.getAreaId() != null) {
            Areas shopArea = areasBiz.resolveArea(shopInfo.getAreaId());
@@ -2588,6 +2620,129 @@
    @Override
    @Transactional(rollbackFor = {Exception.class, BusinessException.class})
    public void verifyOrder(String verifyCode, Integer shopId, List<String> images, String remark) {
        if (StringUtils.isBlank(verifyCode)) {
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "核销码不能为空");
        }
        // å…ˆæŸ¥ä¼šå‘˜æ ¸é”€ç 
        Orders query = new Orders();
        query.setMemberVerifyCode(verifyCode);
        query.setDeleted(Constants.ZERO);
        Orders byMemberCode = findOne(query);
        if (byMemberCode != null) {
            shopVerifyOrder(verifyCode, shopId, images, remark);
        } else {
            driverVerifyOrder(verifyCode, images, remark, shopId);
        }
    }
    @Override
    @Transactional(rollbackFor = {Exception.class, BusinessException.class})
    public void verifyOrderByShopId(Integer orderId, Integer shopId, List<String> images, String remark) {
        if (orderId == null) {
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "订单主键不能为空");
        }
        // æ ¹æ®è®¢å•主键查找订单
        Orders order = ordersMapper.selectOne(new QueryWrapper<Orders>().lambda()
                .eq(Orders::getId, orderId)
                .eq(Orders::getDeleted, Constants.ZERO)
                .last("limit 1"));
        if (order == null) {
            throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "订单不存在");
        }
        // æŸ¥è¯¢é—¨åº—名称用于日志
        String shopName = "";
        ShopInfo shopInfo = shopInfoMapper.selectById(shopId);
        if (shopInfo != null) {
            shopName = shopInfo.getName() != null ? shopInfo.getName() : "";
        }
        Integer status = order.getStatus();
        Date now = new Date();
        if (Constants.equalsInteger(status, Constants.OrderStatus.waitDeposit.getStatus())) {
            // å¾…寄存(1) â†’ å·²å¯„å­˜(2),两种类型通用
            // æ ¡éªŒå½“前门店是否为订单的存件门店
            if (!shopId.equals(order.getDepositShopId())) {
                throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "该订单不属于当前门店,无法核销");
            }
            order.setStatus(Constants.equalsInteger(order.getType(),Constants.ZERO)?Constants.OrderStatus.arrived.getStatus():Constants.OrderStatus.deposited.getStatus());
            order.setDepositTime(now);
            // é‡Šæ”¾å½“前核销码,生成新的核销码供取件时使用
            String verifyCode = order.getMemberVerifyCode();
            if (StringUtils.isNotBlank(verifyCode)) {
                releaseVerifyCode(verifyCode);
            }
            order.setMemberVerifyCode(generateVerifyCode());
            ordersMapper.updateById(order);
            // ä¿å­˜å¯„存图片(obj_type=2 è®¢å•寄存图片,最多3张)
            saveVerifyImages(order.getId(), images, Constants.FileType.ORDER_DEPOSIT.getKey(), shopId);
            // è®°å½•订单日志
            saveShopVerifyLog(order, Constants.OrderLogType.shopDeposit, Constants.OrderLogType.shopDeposit.format(shopName), remark, shopId);
            // é€šçŸ¥ä¼šå‘˜ï¼šé—¨åº—核销成功
            if (Constants.equalsInteger(order.getType(), Constants.ONE)) {
                // å¼‚地寄存 â†’ å¾…抢单
                sendOrderNotice(order.getMemberId(), Constants.MemberOrderNotify.WAIT_GRAB, order.getId(),
                        "orderNo", order.getCode());
            } else {
                // å°±åœ°å¯„å­˜ â†’ å¾…取件提醒
                sendOrderNotice(order.getMemberId(), Constants.MemberOrderNotify.WAIT_PICKUP_REMIND, order.getId(),
                        "orderNo", order.getCode(), "shopName", shopName);
            }
        } else if (Constants.equalsInteger(status, Constants.OrderStatus.arrived.getStatus())) {
            // å¼‚地寄存 + æ— å–件门店 â†’ æ— æ³•核销(客户自取,无门店操作)
            if (Constants.equalsInteger(order.getType(), Constants.ONE) && order.getTakeShopId() == null) {
                throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "该订单无取件门店,无法核销");
            }
            // æ ¡éªŒå–件门店与当前登录门店一致
            if (!shopId.equals(order.getTakeShopId())) {
                throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "该订单不属于当前门店,无法核销");
            }
            // æ ¡éªŒæ˜¯å¦å·²ç¡®è®¤é¡¾å®¢åˆ°åº—
            if (order.getConfirmArriveTime() == null) {
                throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "请先确认顾客到店");
            }
            // æ ¡éªŒæ˜¯å¦å­˜åœ¨å¾…处理的逾期费用
            if (Constants.equalsInteger(order.getOverdueStatus(), Constants.ONE)) {
                throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "存在逾期费用待处理,请先完成逾期费用支付");
            }
            // å¾…取件(5) â†’ å·²å®Œæˆ(7)
            order.setStatus(Constants.OrderStatus.finished.getStatus());
            order.setConfirmArriveTime(now);
            ordersMapper.updateById(order);
            // è®¢å•完成,释放核销码
            String verifyCode = order.getMemberVerifyCode();
            if (StringUtils.isNotBlank(verifyCode)) {
                releaseVerifyCode(verifyCode);
            }
            // ä¿å­˜å‡ºåº“图片(obj_type=13 é—¨åº—出库图片,最多3张)
            saveVerifyImages(order.getId(), images, Constants.FileType.STORE_OUT.getKey(), shopId);
            // ç”Ÿæˆæ”¶ç›Šè®°å½•
            calculateAndSaveOrderFees(order.getId());
            generateRevenueRecords(order.getId());
            // è®°å½•订单日志
            saveShopVerifyLog(order, Constants.OrderLogType.shopTake, Constants.OrderLogType.shopTake.format(shopName), remark, shopId);
            // é€šçŸ¥ä¼šå‘˜ï¼šè®¢å•已完成
            sendOrderNotice(order.getMemberId(), Constants.MemberOrderNotify.FINISHED, order.getId(),
                    "orderNo", order.getCode());
            // é€šçŸ¥å­˜ä»¶é—¨åº—和取件门店:订单已完成
            String settleDays = operationConfigBiz.getConfig().getSettlementDate();
            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(), "当前订单状态不允许核销");
        }
    }
    @Override
    @Transactional(rollbackFor = {Exception.class, BusinessException.class})
    public void shopVerifyOrder(String verifyCode, Integer shopId, List<String> images, String remark) {
        if (StringUtils.isBlank(verifyCode)) {
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "核销码不能为空");
@@ -2625,7 +2780,7 @@
            // ä¿å­˜å¯„存图片(obj_type=2 è®¢å•寄存图片,最多3张)
            saveVerifyImages(order.getId(), images, Constants.FileType.ORDER_DEPOSIT.getKey(), shopId);
            // è®°å½•订单日志
            saveShopVerifyLog(order, "门店确认寄存", "门店【" + shopName + "】确认寄存", remark, shopId);
            saveShopVerifyLog(order, Constants.OrderLogType.shopDeposit, Constants.OrderLogType.shopDeposit.format(shopName), remark, shopId);
            // é€šçŸ¥ä¼šå‘˜ï¼šé—¨åº—核销成功
            if (Constants.equalsInteger(order.getType(), Constants.ONE)) {
                // å¼‚地寄存 â†’ å¾…抢单
@@ -2634,7 +2789,7 @@
            } else {
                // å°±åœ°å¯„å­˜ â†’ å¾…取件提醒
                sendOrderNotice(order.getMemberId(), Constants.MemberOrderNotify.WAIT_PICKUP_REMIND, order.getId(),
                        "orderNo", order.getCode());
                        "orderNo", order.getCode(), "shopName", shopName);
            }
        } else if (Constants.equalsInteger(status, Constants.OrderStatus.arrived.getStatus())) {
            // å¼‚地寄存 + æ— å–件门店 â†’ æ— æ³•核销(客户自取,无门店操作)
@@ -2644,6 +2799,14 @@
            // æ ¡éªŒå–件门店与当前登录门店一致
            if (!shopId.equals(order.getTakeShopId())) {
                throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "该订单不属于当前门店,无法核销");
            }
            // æ ¡éªŒæ˜¯å¦å·²ç¡®è®¤é¡¾å®¢åˆ°åº—
            if (order.getConfirmArriveTime() == null) {
                throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "请先确认顾客到店");
            }
            // æ ¡éªŒæ˜¯å¦å­˜åœ¨å¾…处理的逾期费用
            if (Constants.equalsInteger(order.getOverdueStatus(), Constants.ONE)) {
                throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "存在逾期费用待处理,请先完成逾期费用支付");
            }
            // å¾…取件(5) â†’ å·²å®Œæˆ(7)
            order.setStatus(Constants.OrderStatus.finished.getStatus());
@@ -2657,7 +2820,7 @@
            calculateAndSaveOrderFees(order.getId());
            generateRevenueRecords(order.getId());
            // è®°å½•订单日志
            saveShopVerifyLog(order, "门店确认取件", "门店【" + shopName + "】确认取件,订单完成", remark, shopId);
            saveShopVerifyLog(order, Constants.OrderLogType.shopTake, Constants.OrderLogType.shopTake.format(shopName), remark, shopId);
            // é€šçŸ¥ä¼šå‘˜ï¼šè®¢å•已完成
            sendOrderNotice(order.getMemberId(), Constants.MemberOrderNotify.FINISHED, order.getId(),
                    "orderNo", order.getCode());
@@ -2770,7 +2933,7 @@
        if (order.getRefundAmount() != null && order.getRefundAmount() > 0) {
            logInfo += ",退款" + Constants.getFormatMoney(order.getRefundAmount()) + "元";
        }
        saveShopVerifyLog(order, "门店确认出库", logInfo, remark, shopId);
        saveShopVerifyLog(order, Constants.OrderLogType.shopOutStock, logInfo, remark, shopId);
        // é€šçŸ¥ä¼šå‘˜ï¼šè®¢å•已完成
        sendOrderNotice(order.getMemberId(), Constants.MemberOrderNotify.FINISHED, order.getId(),
                "orderNo", order.getCode());
@@ -3102,9 +3265,9 @@
            ordersMapper.updateById(order);
            // è®°å½•订单日志
            saveShopVerifyLog(order, "确认顾客到店(逾期)",
                    "门店【" + shopName + "】确认顾客到店,逾期" + overdueInfo.getOverdueDays()
                            + "天,逾期费用" + Constants.getFormatMoney(overdueInfo.getOverdueFee()) + "元",
            String overdueLogInfo = "门店【" + shopName + "】确认顾客到店,逾期" + overdueInfo.getOverdueDays()
                            + "天,逾期费用" + Constants.getFormatMoney(overdueInfo.getOverdueFee()) + "元";
            saveShopVerifyLog(order, Constants.OrderLogType.shopConfirmArriveOverdue, overdueLogInfo,
                    null, shopId);
        } else {
            // æœªé€¾æœŸï¼šæ ‡è®°é€¾æœŸçŠ¶æ€ä¸º0,订单保持当前状态
@@ -3148,7 +3311,7 @@
            if (order.getRefundAmount() != null && order.getRefundAmount() > 0) {
                logInfo += ",需退款" + Constants.getFormatMoney(order.getRefundAmount()) + "元";
            }
            saveShopVerifyLog(order, "确认顾客到店", logInfo, null, shopId);
            saveShopVerifyLog(order, Constants.OrderLogType.shopConfirmArrive, logInfo, null, shopId);
        }
    }
@@ -3225,6 +3388,62 @@
    }
    /**
    /**
     * å¡«å……逾期状态到 VO(MyOrderVO)
     * overdueStatus: 0=未到店未逾期 1=未到店存在逾期 2=已到店未逾期 3=已到店待支付逾期 4=逾期已支付
     */
    private void fillOverdueStatus(MyOrderVO vo, Orders order, List<OrdersDetail> details) {
        Integer[] result = calcOverdueStatus(order, details);
        vo.setOverdueStatus(result[0]);
        vo.setOverdue(result[1] == 1);
        vo.setOverdueDays(result[2]);
        vo.setOverdueFee(result[3].longValue());
    }
    /**
     * å¡«å……逾期状态到 VO(MyOrderDetailVO)
     */
    private void fillOverdueStatus(MyOrderDetailVO vo, Orders order, List<OrdersDetail> details) {
        Integer[] result = calcOverdueStatus(order, details);
        vo.setOverdueStatus(result[0]);
        vo.setOverdue(result[1] == 1);
        vo.setOverdueDays(result[2]);
        vo.setOverdueFee(result[3].longValue());
    }
    /**
     * è®¡ç®—逾期状态
     * @return [overdueStatus, isOverdue(0/1), overdueDays, overdueFee]
     */
    private Integer[] calcOverdueStatus(Orders order, List<OrdersDetail> details) {
        // 4=逾期已支付(订单 overdueStatus=2)
        if (Constants.equalsInteger(order.getOverdueStatus(), Constants.TWO)) {
            return new Integer[]{4, 1, order.getOverdueDays(), order.getOverdueAmount() != null ? order.getOverdueAmount().intValue() : 0};
        }
        // 3=已到店待支付逾期(订单 overdueStatus=1)
        if (Constants.equalsInteger(order.getOverdueStatus(), Constants.ONE)) {
            return new Integer[]{3, 1, order.getOverdueDays(), order.getOverdueAmount() != null ? order.getOverdueAmount().intValue() : 0};
        }
        // è®¡ç®—实时逾期
        OverdueFeeVO overdueInfo = calculateOverdueFeeInternal(order, details);
        boolean hasOverdue = overdueInfo.getOverdue() != null && overdueInfo.getOverdue()
                && overdueInfo.getOverdueFee() != null && overdueInfo.getOverdueFee() > 0;
        if (order.getConfirmArriveTime() != null) {
            // å·²åˆ°åº—
            int days = hasOverdue ? overdueInfo.getOverdueDays() : 0;
            long fee = hasOverdue ? overdueInfo.getOverdueFee() : 0L;
            return new Integer[]{2, hasOverdue ? 1 : 0, days, (int) fee};
        } else {
            // æœªåˆ°åº—
            if (hasOverdue) {
                return new Integer[]{1, 1, overdueInfo.getOverdueDays(), overdueInfo.getOverdueFee().intValue()};
            } else {
                return new Integer[]{0, 0, 0, 0};
            }
        }
    }
    /**
     * é€¾æœŸè´¹ç”¨å†…部计算(不查库,接受预查询的数据)
     * ä¾›åˆ†é¡µç­‰å·²æŸ¥è¯¢æ˜Žç»†çš„业务场景复用,避免重复查询
     */
@@ -3241,8 +3460,8 @@
        // ç‰©å“åŸºç¡€æ—¥è´¹ç”¨ = Î£(单价 Ã— æ•°é‡)
        long dailyBaseFee = 0L;
        for (OrdersDetail d : details) {
            dailyBaseFee += (d.getUnitPrice() != null ? d.getUnitPrice() : 0L)
                    * (d.getNum() != null ? d.getNum() : 0);
            dailyBaseFee += (d.getLocallyPrice() != null ? d.getLocallyPrice() : 0L)
                    * (d.getNum() != null ? d.getNum() : 1);
        }
        Date now = new Date();
@@ -3326,25 +3545,20 @@
    /**
     * å°±åœ°å¯„存逾期天数计算
     * è¿‡äº†é¢„计取件时间当天的12点后才算一天
     * è¿‡äº†é¢„计取件日期的次日(只比较年月日)后开始计逾期天数
     */
    private int calcLocalOverdueDays(Date now, Date expectedTakeTime) {
        if (expectedTakeTime == null || !now.after(expectedTakeTime)) {
        if (expectedTakeTime == null) {
            return 0;
        }
        // åŸºå‡†æ—¶é—´ = é¢„计取件日期的12:00
        // åªå–年月日
        Calendar baseCal = Calendar.getInstance();
        baseCal.setTime(expectedTakeTime);
        baseCal.set(Calendar.HOUR_OF_DAY, 12);
        baseCal.set(Calendar.HOUR_OF_DAY, 0);
        baseCal.set(Calendar.MINUTE, 0);
        baseCal.set(Calendar.SECOND, 0);
        baseCal.set(Calendar.MILLISECOND, 0);
        Date baseTime = baseCal.getTime();
        if (!now.after(baseTime)) {
            return 0;
        }
        // é€¾æœŸå¤©æ•° = å½“前日期 - åŸºå‡†æ—¥æœŸï¼ˆæŒ‰å¤©å–差)
        Calendar nowCal = Calendar.getInstance();
        nowCal.setTime(now);
        nowCal.set(Calendar.HOUR_OF_DAY, 0);
@@ -3352,40 +3566,31 @@
        nowCal.set(Calendar.SECOND, 0);
        nowCal.set(Calendar.MILLISECOND, 0);
        Calendar baseDateCal = Calendar.getInstance();
        baseDateCal.setTime(baseTime);
        baseDateCal.set(Calendar.HOUR_OF_DAY, 0);
        baseDateCal.set(Calendar.MINUTE, 0);
        baseDateCal.set(Calendar.SECOND, 0);
        baseDateCal.set(Calendar.MILLISECOND, 0);
        long diffMs = nowCal.getTimeInMillis() - baseDateCal.getTimeInMillis();
        int days = (int) (diffMs / (1000 * 60 * 60 * 24));
        return Math.max(days, 0);
        // åŸºå‡†æ—¥æœŸ = é¢„计取件日期的次日,当天及之前不算逾期
        if (nowCal.before(baseCal)) {
            return 0;
        }
        // é€¾æœŸå¤©æ•° = å½“前日期 - åŸºå‡†æ—¥æœŸ
        long diffMs = nowCal.getTimeInMillis() - baseCal.getTimeInMillis();
        return (int) (diffMs / (1000 * 60 * 60 * 24));
    }
    /**
     * å¼‚地寄存逾期天数计算
     * è¿‡äº†è½¬ç§»åˆ°åº—时间当天的晚上12点(24:00)后才算第一天
     * è¿‡äº†è½¬ç§»åˆ°åº—日期的次日(只比较年月日)后开始计逾期天数
     */
    private int calcRemoteOverdueDays(Date now, Date arriveTime) {
        if (arriveTime == null || !now.after(arriveTime)) {
        if (arriveTime == null) {
            return 0;
        }
        // åŸºå‡†æ—¶é—´ = è½¬ç§»åˆ°åº—日期的次日 00:00(即当天24:00)
        // åªå–年月日
        Calendar baseCal = Calendar.getInstance();
        baseCal.setTime(arriveTime);
        baseCal.set(Calendar.HOUR_OF_DAY, 0);
        baseCal.set(Calendar.MINUTE, 0);
        baseCal.set(Calendar.SECOND, 0);
        baseCal.set(Calendar.MILLISECOND, 0);
        baseCal.add(Calendar.DAY_OF_MONTH, 1); // æ¬¡æ—¥00:00 = å½“天24:00
        Date baseTime = baseCal.getTime();
        if (!now.after(baseTime)) {
            return 0;
        }
        // é€¾æœŸå¤©æ•° = å½“前日期 - åŸºå‡†æ—¥æœŸ
        Calendar nowCal = Calendar.getInstance();
        nowCal.setTime(now);
        nowCal.set(Calendar.HOUR_OF_DAY, 0);
@@ -3393,16 +3598,14 @@
        nowCal.set(Calendar.SECOND, 0);
        nowCal.set(Calendar.MILLISECOND, 0);
        Calendar baseDateCal = Calendar.getInstance();
        baseDateCal.setTime(baseTime);
        baseDateCal.set(Calendar.HOUR_OF_DAY, 0);
        baseDateCal.set(Calendar.MINUTE, 0);
        baseDateCal.set(Calendar.SECOND, 0);
        baseDateCal.set(Calendar.MILLISECOND, 0);
        long diffMs = nowCal.getTimeInMillis() - baseDateCal.getTimeInMillis();
        int days = (int) (diffMs / (1000 * 60 * 60 * 24));
        return Math.max(days, 0);
        // åŸºå‡†æ—¥æœŸ = åˆ°åº—日期的次日,当天及之前不算逾期
        baseCal.add(Calendar.DAY_OF_MONTH, 1);
        if (nowCal.before(baseCal)) {
            return 0;
        }
        // é€¾æœŸå¤©æ•° = å½“前日期 - åŸºå‡†æ—¥æœŸ
        long diffMs = nowCal.getTimeInMillis() - baseCal.getTimeInMillis();
        return (int) (diffMs / (1000 * 60 * 60 * 24));
    }
    @Override
@@ -3421,6 +3624,7 @@
        ActiveOrderTipVO vo = new ActiveOrderTipVO();
        vo.setOrderId(order.getId());
        vo.setStatus(order.getStatus());
        vo.setStatusDesc(Constants.OrderStatus.getDescByKey(order.getStatus(), order.getType()));
        // æž„建提示文案
        boolean isLocal = Constants.equalsInteger(order.getType(), Constants.ZERO);
@@ -3527,9 +3731,9 @@
                // å†™å…¥æ“ä½œæ—¥å¿—
                OrderLog orderLog = new OrderLog();
                orderLog.setOrderId(order.getId());
                orderLog.setTitle("系统自动取消(超时未支付)");
                orderLog.setLogInfo("订单超时" + autoCancelMinutes + "分钟未支付,系统自动取消");
                orderLog.setObjType(Constants.ORDER_LOG_CANCEL);
                orderLog.setTitle(Constants.OrderLogType.systemCancel.getTitle());
                orderLog.setLogInfo(Constants.OrderLogType.systemCancel.format("订单超时" + autoCancelMinutes + "分钟未支付,系统自动取消"));
                orderLog.setObjType(Constants.OrderLogType.systemCancel.getStatus());
                orderLog.setOrderStatus(Constants.OrderStatus.cancelled.getStatus());
                orderLog.setOptUserType(3); // 3=系统
                orderLog.setCreateTime(now);
@@ -3693,9 +3897,9 @@
                // è®°å½•操作日志
                OrderLog orderLog = new OrderLog();
                orderLog.setOrderId(order.getId());
                orderLog.setTitle("系统自动完成");
                orderLog.setLogInfo("订单已送达超过" + autoConfirmDays + "天未确认,系统自动完成");
                orderLog.setObjType(Constants.ORDER_LOG_CANCEL);
                orderLog.setTitle(Constants.OrderLogType.systemComplete.getTitle());
                orderLog.setLogInfo(Constants.OrderLogType.systemComplete.format("订单已送达超过" + autoConfirmDays + "天未确认,系统自动完成"));
                orderLog.setObjType(Constants.OrderLogType.systemComplete.getStatus());
                orderLog.setOrderStatus(Constants.OrderStatus.finished.getStatus());
                orderLog.setOptUserType(3); // 3=系统
                orderLog.setCreateTime(now);
server/services/src/main/java/com/doumee/service/business/impl/RevenueServiceImpl.java
@@ -10,14 +10,8 @@
import com.doumee.core.model.PageData;
import com.doumee.core.model.PageWrap;
import com.doumee.core.utils.Utils;
import com.doumee.dao.business.RevenueMapper;
import com.doumee.dao.business.WithdrawalOrdersMapper;
import com.doumee.dao.business.MemberMapper;
import com.doumee.dao.business.ShopInfoMapper;
import com.doumee.dao.business.model.Revenue;
import com.doumee.dao.business.model.ShopInfo;
import com.doumee.dao.business.model.Member;
import com.doumee.dao.business.model.WithdrawalOrders;
import com.doumee.dao.business.*;
import com.doumee.dao.business.model.*;
import com.doumee.dao.dto.RevenueQueryDTO;
import com.doumee.dao.vo.RevenueStatisticsVO;
import com.doumee.dao.vo.RevenueSummaryVO;
@@ -26,6 +20,7 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.sql.Driver;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -49,6 +44,9 @@
    @Autowired
    private MemberMapper memberMapper;
    @Autowired
    private DriverInfoMapper driverInfoMapper;
    @Override
    public Integer create(Revenue revenue) {
@@ -178,11 +176,11 @@
    @Override
    public RevenueStatisticsVO getDriverRevenueStatistics(Integer memberId) {
        Member member = memberMapper.selectById(memberId);
        if (member == null) {
            throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "用户不存在");
        DriverInfo driverInfo = driverInfoMapper.selectById(memberId);
        if (driverInfo == null) {
            throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "司机信息不存在");
        }
        return buildRevenueStatistics(memberId, Constants.ONE, member.getAmount());
        return buildRevenueStatistics(memberId, Constants.ONE, driverInfo.getBalance());
    }
    /**
server/services/src/main/java/com/doumee/service/business/impl/ShopInfoServiceImpl.java
@@ -14,6 +14,7 @@
import com.doumee.core.utils.Utils;
import com.doumee.biz.system.AreasBiz;
import com.doumee.biz.system.SystemDictDataBiz;
import com.doumee.core.utils.aliyun.AliSmsService;
import com.doumee.dao.business.MemberMapper;
import com.doumee.dao.business.MultifileMapper;
import com.doumee.dao.business.OrdersMapper;
@@ -36,6 +37,9 @@
import com.doumee.dao.vo.ShopWebDetailVO;
import com.doumee.service.business.AreasService;
import com.doumee.service.business.ShopInfoService;
import com.doumee.dao.business.SmsrecordMapper;
import com.doumee.dao.business.model.Smsrecord;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.RandomStringUtils;
@@ -52,6 +56,7 @@
 * @author rk
 * @date 2026/04/08
 */
@Slf4j
@Service
public class ShopInfoServiceImpl implements ShopInfoService {
@@ -84,6 +89,9 @@
    @Autowired
    private AreasService areasService;
    @Autowired
    private SmsrecordMapper smsrecordMapper;
    @Override
    public Integer create(ShopInfo shopInfo) {
        shopInfoMapper.insert(shopInfo);
@@ -197,6 +205,9 @@
        }
        if (pageWrap.getModel().getAreaId() != null) {
            queryWrapper.lambda().eq(ShopInfo::getAreaId, pageWrap.getModel().getAreaId());
        }
        if (pageWrap.getModel().getAddress() != null) {
            queryWrapper.lambda().like(ShopInfo::getAddress, pageWrap.getModel().getAddress());
        }
        if (pageWrap.getModel().getAuditStatus() != null) {
            queryWrapper.lambda().eq(ShopInfo::getAuditStatus, pageWrap.getModel().getAuditStatus());
@@ -320,6 +331,8 @@
            existing.setAuditTime(null);
            existing.setAuditUserId(null);
            existing.setAuditStatus(Constants.ZERO);
            // è¯»å–押金金额
            setDepositAmountFromPricingRule(existing);
            shopInfoMapper.updateById(existing);
            shopId = existing.getId();
        } else {
@@ -352,6 +365,8 @@
            shopInfo.setCreateTime(now);
            shopInfo.setUpdateTime(now);
            shopInfo.setRegionMemberId(member.getId());
            // è¯»å–押金金额
            setDepositAmountFromPricingRule(shopInfo);
            shopInfoMapper.insert(shopInfo);
            shopId = shopInfo.getId();
        }
@@ -445,17 +460,27 @@
                }
            }
            // 3. ä»ŽPricingRule读取押金金额
            PricingRule pricingRule = pricingRuleMapper.selectOne(new QueryWrapper<PricingRule>().lambda()
                    .eq(PricingRule::getDeleted, Constants.ZERO)
                    .eq(PricingRule::getType, Constants.THREE)
                    .eq(PricingRule::getFieldA, String.valueOf(shopInfo.getCompanyType()))
                    .last("limit 1"));
            if (pricingRule != null && StringUtils.isNotBlank(pricingRule.getFieldB())) {
                shopInfo.setDepositAmount(Long.parseLong(pricingRule.getFieldB()));
            }
            // 3. ä»ŽPricingRule读取押金金额(审批时更新)
            setDepositAmountFromPricingRule(shopInfo);
        }
        shopInfoMapper.updateById(shopInfo);
        // çŸ­ä¿¡é€šçŸ¥
        if (Constants.equalsInteger(newAuditStatus, Constants.ONE)) {
            // å®¡æ ¸é€šè¿‡ â†’ é€šçŸ¥ç¼´çº³æŠ¼é‡‘
            String depositMoney = shopInfo.getDepositAmount() != null
                    ? String.valueOf(shopInfo.getDepositAmount() / 100.0) : "0";
            sendSmsNotify(shopInfo.getTelephone(),
                    Constants.SmsNotify.SHOP_AUTH_APPROVED_DEPOSIT,
                    "storeName", shopInfo.getName(),
                    "money", depositMoney);
        } else if (Constants.equalsInteger(newAuditStatus, Constants.TWO)) {
            // å®¡æ ¸é©³å›ž
            sendSmsNotify(shopInfo.getTelephone(),
                    Constants.SmsNotify.SHOP_AUTH_REJECTED,
                    "storeName", shopInfo.getName(),
                    "reason", auditDTO.getAuditRemark() != null ? auditDTO.getAuditRemark() : "");
        }
    }
    @Override
@@ -732,6 +757,13 @@
            }
        }
        // é—¨åº—头像:优先使用 coverImg,为空则取门头照第一张
        if (StringUtils.isNotBlank(shopInfo.getCoverImg())) {
            vo.setShopAvatar(imgPrefix + shopInfo.getCoverImg());
        } else if (!CollectionUtils.isEmpty(vo.getStoreFrontImgUrls())) {
            vo.setShopAvatar(vo.getStoreFrontImgUrls().get(0));
        }
        return vo;
    }
@@ -881,8 +913,8 @@
    }
    @Override
    public ShopInfoMaintainDTO getShopMaintainInfo(Integer memberId) {
        ShopInfo shop = shopInfoMapper.selectById(memberId);
    public ShopInfoMaintainDTO getShopMaintainInfo(Integer shopId) {
        ShopInfo shop = shopInfoMapper.selectById(shopId);
        if (Objects.isNull(shop) || Constants.equalsInteger(shop.getDeleted(), Constants.ONE)) {
            return null;
        }
@@ -894,6 +926,10 @@
        dto.setDeliveryArea(shop.getDeliveryArea());
        dto.setShopHours(shop.getShopHours());
        dto.setBusinessType(shop.getBusinessType());
        // å¤´åƒå…¨è·¯å¾„
        if (StringUtils.isNotBlank(shop.getCoverImg())) {
            dto.setCoverImgUrl(getShopPrefix() + shop.getCoverImg());
        }
        return dto;
    }
@@ -1275,4 +1311,82 @@
        return errors;
    }
    /**
     * ä»ŽPricingRule读取押金金额并赋值到门店记录
     */
    private void setDepositAmountFromPricingRule(ShopInfo shopInfo) {
        if (shopInfo.getCompanyType() == null) {
            return;
        }
        PricingRule pricingRule = pricingRuleMapper.selectOne(new QueryWrapper<PricingRule>().lambda()
                .eq(PricingRule::getDeleted, Constants.ZERO)
                .eq(PricingRule::getType, Constants.THREE)
                .eq(PricingRule::getFieldA, String.valueOf(shopInfo.getCompanyType()))
                .last("limit 1"));
        if (pricingRule != null && StringUtils.isNotBlank(pricingRule.getFieldB())) {
            shopInfo.setDepositAmount(Long.parseLong(pricingRule.getFieldB()));
        }
    }
    /**
     * å‘送短信通知(失败不影响主业务)
     */
    private void sendSmsNotify(String phone, Constants.SmsNotify smsNotify, String... paramPairs) {
        if (StringUtils.isBlank(phone)) {
            return;
        }
        String content = smsNotify.format(paramPairs);
        try {
            JSONObject templateParam = new JSONObject();
            for (int i = 0; i < paramPairs.length - 1; i += 2) {
                templateParam.put(paramPairs[i], paramPairs[i + 1]);
            }
            boolean result = AliSmsService.sendSms(phone, smsNotify.getTemplateCode(),
                    templateParam.toJSONString());
            if (result) {
                log.info("短信发送成功: phone={}, template={}", phone, smsNotify.name());
            } else {
                log.warn("短信发送失败: phone={}, template={}", phone, smsNotify.name());
            }
            // å­˜å‚¨çŸ­ä¿¡è®°å½•
            Smsrecord smsRecord = new Smsrecord();
            smsRecord.setPhone(phone);
            smsRecord.setContent(content);
            smsRecord.setType(Constants.ONE);
            smsRecord.setStatus(result ? Constants.ONE : Constants.ZERO);
            smsRecord.setCreateTime(new Date());
            smsRecord.setDeleted(Constants.ZERO);
            smsrecordMapper.insert(smsRecord);
        } catch (Exception e) {
            log.error("短信发送异常: phone={}, template={}, error={}", phone, smsNotify.name(), e.getMessage());
        }
    }
    @Override
    public void changePassword(Integer shopId, String newPassword, String token) {
        if (StringUtils.isBlank(newPassword)) {
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "密码不能为空");
        }
        // æ ¡éªŒå¯†ç å¿…须同时包含字母和数字
        boolean hasLetter = newPassword.chars().anyMatch(Character::isLetter);
        boolean hasDigit = newPassword.chars().anyMatch(Character::isDigit);
        if (!hasLetter || !hasDigit) {
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "密码必须同时包含字母和数字");
        }
        // æŸ¥è¯¢é—¨åº—
        ShopInfo shopInfo = shopInfoMapper.selectById(shopId);
        if (shopInfo == null || Constants.equalsInteger(shopInfo.getDeleted(), Constants.ONE)) {
            throw new BusinessException(ResponseStatus.DATA_EMPTY);
        }
        // é‡æ–°ç”Ÿæˆsalt,加密新密码并更新
        String salt = RandomStringUtils.randomAlphabetic(6);
        shopInfo.setPassword(Utils.Secure.encryptPassword(newPassword, salt));
        shopInfo.setSalt(salt);
        shopInfoMapper.updateById(shopInfo);
        // æ¸…除token,强制重新登录
        if (StringUtils.isNotBlank(token)) {
            redisTemplate.delete(token);
        }
    }
}
server/services/src/main/java/com/doumee/service/business/impl/WithdrawalOrdersServiceImpl.java
@@ -182,6 +182,15 @@
        if (pageWrap.getModel().getCreateEndTime() != null) {
            queryWrapper.le(WithdrawalOrders::getCreateTime, Utils.Date.getEnd(pageWrap.getModel().getCreateEndTime()));
        }
        // é—¨åº—端:按门店名称或联系人模糊搜索
        if (StringUtils.isNotBlank(pageWrap.getModel().getShopName())) {
            queryWrapper.and(i -> i.like(ShopInfo::getName, pageWrap.getModel().getShopName())
                    .or().like(ShopInfo::getLinkName, pageWrap.getModel().getShopName()));
        }
        // å¸æœºç«¯ï¼šæŒ‰å¸æœºå§“名模糊搜索
        if (StringUtils.isNotBlank(pageWrap.getModel().getMemberName())) {
            queryWrapper.like(DriverInfo::getName, pageWrap.getModel().getMemberName());
        }
        for (PageWrap.SortData sortData : pageWrap.getSorts()) {
            if (sortData.getDirection().equalsIgnoreCase(PageWrap.DESC)) {
                queryWrapper.orderByDesc(sortData.getProperty());
server/services/src/main/resources/application-dev.yml
@@ -62,6 +62,12 @@
des_pwd: 123456SDFKDJF
jpush:
  appKey:
  masterSecret:
  apnsProduction: false
knife4j:
  enable: true
  basic:
server/services/src/main/resources/application-pro.yml
@@ -1,9 +1,9 @@
spring:
  # æ•°æ®æºé…ç½®
  datasource:
    url: jdbc:mysql://192.168.0.211:3306/gtzxinglijicun?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
    username: root
    password: Doumee@168
    url: jdbc:mysql://rm-bp1qxtf4wy100fur3yo.mysql.rds.aliyuncs.com:3306/gtzxinglijicun?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
    username: gtzxinglijicun
    password: gtzxljc@2026
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
  redis:
@@ -54,39 +54,39 @@
  pay:
    appId: wxb1b59320e803dc6c
    appSecret: eb93785c7bca3f0ff0364b0e26bfeb59
    mchId: 1229817002    #商户号
    mchKey: u4TSNtv0wFP7WRfnxBgijYOtRhS9FvlM #商户秘钥
    apiV3Key: 7tG4Vk9Zp2L8dXw5Jq0N3hR6yE1sF3cB #apiV3Key
    serialNumer: 3FE90C2F3D40A56E1C51926F31B8A8D22426CCE0 #商户证书序列号
    notifyUrl: http://xiaopiqiu2.natapp1.cc/web/wxPayNotify
    refundNotifyUrl: http://xiaopiqiu2.natapp1.cc/web/wxRefundNotify
    v3NotifyUrl: http://xiaopiqiu2.natapp1.cc/web/api/wxPayV3Notify
    v3RefundNotifyUrl: http://xiaopiqiu2.natapp1.cc/web/api/wxRefundV3Notify
    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
    pubKeyPath: D:\DouMee\1229817002_20220310_cert\pub_key.pem #商户支付公钥
    mchId: 1629568742    #商户号
    apiV3Key: NJTLJSTZYXZRGScaiwubuzichanbu666 #apiV3Key
    serialNumer: 25D19D18217F4588841E5CD1AA0D1533DE8AF84A #商户证书序列号
    privateKeyPath: pay/pro/wx/apiclient_key.pem
    v3NotifyUrl: https://llfc.lmpro.cn/nyx_web/web/api/wxPayV3Notify
    v3RefundNotifyUrl: https://llfc.lmpro.cn/nyx_web/web/api/wxRefundV3Notify
    typeId: jinkuai
alipay:
  pay:
    appId: 2021006146626440
    privateKey: MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC0zIy4Ej8yG34uQ8zECWu12INdTWYuvSudhrf1qtUrPx4FO9vDGc8NdPz63M3y+akWh9o8gsl7qQqOoB7o8gYebLTzF2ZP3ByxgkNRGUwZBIi0iL/28eq/2uDCWnqNBDEeFQpfWeRcErC28LCB6JSI7F6owH78LiU8pNO0pVvqwvcLovlVee4UDtLThBegUxkkHXq4EM9yutkfps1ruDiTGFYyTjS8g9BmNJ7rJoRSKTE85Gl8OYp1SuFbyurPEaFLBW9yGHbsqMqegys2PwV6PxZq3WBbFMiuWI5fane+VL2jyVoV36CYPS2GFjq0stuUHZvPL1uYk+bwAAei7a2JAgMBAAECggEBAJMnTX1gNJl28Qt82YPRWx6F292r6nguNfqftMi6Q4hQdgGyh5rTPcfpKSKRZvrVIz+YURMhLBZ/Ln2Ja78ThdFrjxewOvDS6XO830d5vIs8OnweNYgrvHJtFrR5afeuqr+eRnP3OTuLZtNvMWp6v7J2GFFnY7OjZ2Flkn4cfKSkqnLQnZ/oDKA/a40OclHRmaQZcS5YkGZqeR+NssanUDad89IVz7NU0kXTbaXjbGHyJscLr6hM3otdfrYsboJ5XHLOnnpSLptv3I0HSUeHBSdhAbP5Hlot+xOo6EbeougefmT2FZWaDA+kPHgajCOdZHW3v+13Xp+PioRW0IPcsLUCgYEA+11Fqm+tpP351tdkJyW+weTy0/OnvUuSVQP/0/kvzQ84kjfkxdoyTKe6kT8+K5b5dkCQhGRfWgb7/amKEe1loCQsujf7/6aebUgTevrOs96LErMT1fCqId1t/MWgXVFpzo8n2OTks2r0nleHY7C9VZiPIbGBQDbkJChIFRAiRXsCgYEAuCIg2p4RrlOG/i4/r1xKmOIwba9snTBnAB8/bri913xMy055OVF6P46hC6d/J25QNqYGODBBF4kmiBuco+vtuz6C94Uo3h/oYD3jetmwlAnRWjEl9Z3Pkf5cbE+o5KBZmUa+M7BTrBDutzdRDqX2SANvnoDWVF03teY9lcVNL8sCgYEAoFdeEhtNC/tKfKZG58XnCe4Oi+9YJ6LmRD7Z2RCSUl8MOhFXaHIINeekVfSeptWWab1DsoAIZvgflC6quUbS1bVdpqgBopFZa+JXMtJ8OjaSRipfU9BB5npGJ8C0y/Ib6TxeMbfIvz5RrhOtdIUQMWKwotCE3z5khz/+wxjYk7MCgYBrMesgeo9ehl/7T99hboA7GssIv+yiYhBEoOxjwAc9EK8AWNH3zXg20gjtaPh8cxsdhW/vfCAY3I5jBHgfcfU3YcAK6ymMjtTQWpc46MyEkmafdCdeIx45JvSVVZbEaplewzFtlARSEpV2ciytM276I43yn5ynBpGtQrmtnGxAGwKBgQCy8imu84IhC+BivLRYdsQ+hMsqs0nih4wfVPKWr6K64QO6CZ4riL76lppH8UBG5EXtRd6cjBbORgvQtr22OJxFcj8WuZjBvYvvCOsTE0jGMdiyBqx9k0PsLrtlVZPFP4kj9vDSFNIyAgIOiAmYY4kBtGfhQqRcDgZK+mgHY0zMbg==
    appCertPath: foo/pro/appCertPublicKey_2021006146626440.crt
    alipayPublicCertPath: pro/dev/alipayCertPublicKey_RSA2.crt
    rootCertPath: foo/pro/alipayRootCert.crt
    appId: 2021006147660139
    privateKey: MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDEeqqkZztBHfK+vpyBi6ejgDTHZuZ3yiuXds+lRBbMo/g24F5trH+oLHW0gMhSxihFfQBBIpVBXDsPQK4ZkhDWTaOBktnU1UMRoOEiaaZU6EiWy10ePFUmpdXpkCQEp7rc88OwI90p58S3+L+Ckak60WqNwJBdB5YGBaUunryBA78U4zW1KNY7JvoRnZDcFMQiczikwUzhO7EAi0yVrVpsVsc9s87mcS4uOJKx4qb2E83r9RJ1z30db+cIIZRiLP2oNZBLYzgKpOouE+uIgxhQzlh6cOASNZQulXuUjoT/+Y9w4njfl4TmKIXWcJFKIMc6kMiux9tTncpp0TqRwk1tAgMBAAECggEALkSYtJheusnbpRFr95G0i2sggqh3s1PXihZ/dXKgT9Z5GCsj8X3Cng7CNRxykBN73kk+axhCv56Bhej8Vqcv8ddcnqG/TEBgR+Fzws/QTIRau6/uILWic7RvuE2qPbJl7aw1s9/uL/UVPSGFr7CvgltYVUM4e7/Sk1529JCK4XJfoXP5tKJ3OaXssvaFnCKEU8IGQkjRG+lUZJhAHVtClGHtgrhevgRhy2zre5wp2qSa8d/MqrPruSYS02hn9b5Nl6i2PlUS6dGlJ4lrxYTG22ukYYoxAPNPS7gnvmveXonWP7b5tPhKRpZjnoySojz3WECUlhz/v8wM1cDrpq+GQQKBgQDsc7y2rlx4f77a7ORfb5/qWHCOJs1cIzggj0kJ7TgFGe71kbCQ5nywD/Fe5V9OwbW+DCxOME+SrrHeiK4axWiu5si/1JlurJoxNy+4k4ywk3ZA3Nv2aBhlPqfkwDhJ0z7Mgsq2c/YgnVddmSvKZoC39wA77ovks4GDxaBOt8N7PQKBgQDUuPGgzkwcgb60UdaxfMbacrPsW26vDxaE4ceuXo2m8KDiCIqkF2y9r6AdWMTgGGSJwOsk7+FP+21VdRivCg9HcOLWngveUc6xDIuqKHVpemMo3SdCF4Wqf96rRc3VOBr5cfIdWxeorZf5umMyKnIAjAFETOOrK7eLTTmjyLD98QKBgB82S+Plcklpu3zUpnS+nGJn2Du7fYI7F+6cW2zXBn0N5lA+Mgt+kVkAUcFQD9uqkF4M51BO6kIXk10nt6vLAT2NM1S3MKW+XQBAI6l+uKSaYpK/VL3bEdVThwAYK5X7L5/5Z97bwdKeUmkFjhVCoJ0oGrzOiWLgGymUzct2UHSVAoGBAMb+7Cs+Ub0pMrmFBY6r52pbey1Uq0pglvRgMmhQU7sjx50r2GaA81zPer15WVM5/nNPYaoALYqg7jrPe/PjOT/fvpR+7SNg7DZ8QftANfYiY7jKifst/gDt9ePLPS6FedZ4XcJQgOVu34jicAFx64vPbS/zrddm4iEScSVijRBBAoGAXCheERsx8+n16Us/DttXFUa1nc7+D8WR6buM1QMZgQCVF2qp3XtM+FusCKL4+q1+dtag8svLjJFp9QbaAXqX8Zk7rn8wUHbDloPTPy9XWgrPowyL9MPU+e/Rq8Hr6TWPDBd4TU64YzIEfBQYpJXfZbXhVYmK3o7xHXKB1x4vvEM=
    appCertPath: pay/pro/appCertPublicKey.crt
    alipayPublicCertPath: pay/pro/alipayCertPublicKey_RSA2.crt
    rootCertPath: pay/pro/alipayRootCert.crt
upload:
  type: blob
qiwei:
  serviceurl: https://wecom-qyapi.unilever-china.com/
  type: ftp
# è…¾è®¯åœ°å›¾apikey
tencent_key: WE3BZ-HN6WS-ONDOH-62QCV-MNL6F-5NFNE
tencent_key:
# é«˜å¾·åœ°å›¾apikey
geocode_map_key:
geocode_map_key: 9a6c1f0eff2e5aa91989ca9d4c21e262
qiwei:
  serviceurl: https://qyapi.weixin.qq.com
aes:
  encrypt:
    open: true # æ˜¯å¦å¼€å¯åР坆 true  or  false
    showLog: true # æ˜¯å¦æ‰“印加解密log true  or  false
    publicKey: mN4Yn8Or8r7SH1w3 # AES密钥
    privateKey:  # RSA私钥
    time: 600000
server/web/src/main/java/com/doumee/api/web/AccountApi.java
@@ -1,7 +1,9 @@
package com.doumee.api.web;
import com.doumee.config.jwt.JwtTokenUtil;
import com.doumee.core.annotation.LoginDriverRequired;
import com.doumee.core.annotation.LoginRequired;
import com.doumee.core.annotation.LoginShopRequired;
import com.doumee.core.annotation.trace.Trace;
import com.doumee.core.constants.Constants;
import com.doumee.core.model.ApiResponse;
@@ -92,19 +94,30 @@
    }
    @LoginRequired
    @LoginShopRequired
    @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);
        String token = this.getRequest().getHeader(JwtTokenUtil.SHOP_HEADER_KEY);
        memberService.logOut(token,getShopId(), Constants.TWO);
        return  ApiResponse.success("操作成功");
    }
    @LoginDriverRequired
    @ApiOperation(value = "司机退出登录", notes = "小程序端")
    @GetMapping("/logOutDriver")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", dataType = "String", name = "token", value = "用户token值", required = true),
    })
    public ApiResponse logOutDriver() {
        String token = this.getRequest().getHeader(JwtTokenUtil.HEADER_KEY);
        memberService.logOut(token,getDriverId(), Constants.ONE);
        return  ApiResponse.success("操作成功");
    }
    @LoginRequired
    @ApiOperation(value = "用户注销", notes = "小程序端")
    @GetMapping("/logOff")
server/web/src/main/java/com/doumee/api/web/ConfigApi.java
@@ -1,5 +1,6 @@
package com.doumee.api.web;
import com.doumee.core.annotation.LoginDriverRequired;
import com.doumee.core.annotation.LoginRequired;
import com.doumee.core.annotation.LoginShopRequired;
import com.doumee.core.annotation.trace.Trace;
@@ -184,7 +185,7 @@
        return ApiResponse.success("查询成功", noticeService.findPage(pageWrap));
    }
    @LoginRequired
    @LoginShopRequired
    @ApiOperation(value = "门店标记全部已读", notes = "标记当前用户所有未读通知为已读")
    @PostMapping("/shopReadAllNotice")
    @ApiImplicitParams({
@@ -219,4 +220,32 @@
    }
    @LoginDriverRequired
    @ApiOperation(value = "司机通知消息分页", notes = "未读优先,时间倒序")
    @PostMapping("/driverNoticePage")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", dataType = "String", name = "token", value = "用户token值", required = true)
    })
    public ApiResponse<PageData<Notice>> driverNoticePage(@RequestBody PageWrap<Notice> pageWrap) {
        if (pageWrap.getModel() == null) {
            pageWrap.setModel(new Notice());
        }
        pageWrap.getModel().setUserId(this.getDriverId());
        pageWrap.getModel().setUserType(Constants.ONE);
        return ApiResponse.success("查询成功", noticeService.findPage(pageWrap));
    }
    @LoginDriverRequired
    @ApiOperation(value = "司机标记全部已读", notes = "标记当前用户所有未读通知为已读")
    @PostMapping("/driverReadAllNotice")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", dataType = "String", name = "token", value = "用户token值", required = true)
    })
    public ApiResponse driverReadAllNotice() {
        noticeService.readAllNotice(Constants.ONE, this.getDriverId());
        return ApiResponse.success("操作成功");
    }
}
server/web/src/main/java/com/doumee/api/web/DriverInfoApi.java
@@ -3,6 +3,7 @@
import com.doumee.core.annotation.LoginDriverRequired;
import com.doumee.core.annotation.LoginRequired;
import com.doumee.core.annotation.trace.Trace;
import com.doumee.config.jwt.JwtTokenUtil;
import com.doumee.core.model.ApiResponse;
import com.doumee.core.model.PageData;
import com.doumee.core.model.PageWrap;
@@ -11,10 +12,13 @@
import com.doumee.dao.dto.DriverGrabOrderDTO;
import com.doumee.dao.dto.DriverLoginRequest;
import com.doumee.dao.dto.DriverDeliverDTO;
import com.doumee.dao.dto.DriverOrderPageDTO;
import com.doumee.dao.dto.DriverPickupDTO;
import com.doumee.dao.dto.DriverRegisterRequest;
import com.doumee.dao.dto.DriverVerifyRequest;
import com.doumee.dao.vo.AccountResponse;
import com.doumee.dao.vo.DriverActiveOrderCountVO;
import com.doumee.dao.vo.DriverCancelLimitVO;
import com.doumee.dao.vo.DriverCenterVO;
import com.doumee.dao.vo.DriverGrabOrderVO;
import com.doumee.dao.vo.DriverOrderDetailVO;
@@ -35,7 +39,7 @@
 * @author rk
 * @date 2026/04/08
 */
@Api(tags = "司机验证码登录")
@Api(tags = "司机业务接口")
@Trace(exclude = true)
@RestController
@RequestMapping("/web/driverInfo")
@@ -133,6 +137,17 @@
    @LoginDriverRequired
    @Trace
    @ApiOperation(value = "司机累计统计", notes = "累计佣金、待结算佣金、订单总数、钱包余额")
    @GetMapping("/stats")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", dataType = "String", name = "token", value = "用户token值", required = true)
    })
    public ApiResponse<com.doumee.dao.vo.DriverStatsVO> stats() {
        return ApiResponse.success("操作成功", driverInfoService.getDriverStats(this.getDriverId()));
    }
    @LoginDriverRequired
    @Trace
    @ApiOperation(value = "司机抢单大厅", notes = "分页查询可抢的异地寄存订单")
    @PostMapping("/grabOrderHall")
    @ApiImplicitParams({
@@ -145,7 +160,7 @@
    @LoginDriverRequired
    @Trace
    @ApiOperation(value = "司机抢单", notes = "对已寄存(status=2)的异地寄存订单发起抢单")
    @PostMapping("/grabOrder")
    @GetMapping("/grabOrder")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", dataType = "String", name = "token", value = "用户token值", required = true),
            @ApiImplicitParam(paramType = "query", dataType = "Integer", name = "orderId", value = "订单主键", required = true)
@@ -215,4 +230,50 @@
        return ApiResponse.success("操作成功");
    }
    @LoginDriverRequired
    @Trace
    @ApiOperation(value = "司机修改密码", notes = "新密码必须同时包含字母和数字,修改成功后需重新登录")
    @GetMapping("/changePassword")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", dataType = "String", name = "token", value = "用户token值", required = true),
    })
    public ApiResponse changePassword(@RequestParam String newPassword) {
        String token = this.getRequest().getHeader(JwtTokenUtil.HEADER_KEY);
        driverInfoService.changePassword(this.getDriverId(), newPassword, token);
        return ApiResponse.success("密码修改成功,请重新登录");
    }
    @LoginDriverRequired
    @Trace
    @ApiOperation(value = "进行中订单数量", notes = "返回已抢单和派送中的订单数量")
    @GetMapping("/activeOrderCount")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", dataType = "String", name = "token", value = "用户token值", required = true)
    })
    public ApiResponse<DriverActiveOrderCountVO> activeOrderCount() {
        return ApiResponse.success("操作成功", driverInfoService.getActiveOrderCount(this.getDriverId()));
    }
    @LoginDriverRequired
    @Trace
    @ApiOperation(value = "司机订单分页", notes = "查询司机的全部/待取件/配送中/已完成订单")
    @PostMapping("/orderPage")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", dataType = "String", name = "token", value = "用户token值", required = true)
    })
    public ApiResponse<PageData<DriverGrabOrderVO>> orderPage(@RequestBody PageWrap<DriverOrderPageDTO> pageWrap) {
        return ApiResponse.success("操作成功", driverInfoService.driverOrderPage(this.getDriverId(), pageWrap));
    }
    @LoginDriverRequired
    @Trace
    @ApiOperation(value = "今日可取消次数", notes = "返回司机今日取消次数上限、已取消次数、剩余可取消次数")
    @GetMapping("/cancelLimit")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", dataType = "String", name = "token", value = "用户token值", required = true)
    })
    public ApiResponse<DriverCancelLimitVO> cancelLimit() {
        return ApiResponse.success("操作成功", driverInfoService.getTodayCancelLimit(this.getDriverId()));
    }
}
server/web/src/main/java/com/doumee/api/web/OrdersApi.java
@@ -3,6 +3,7 @@
import com.doumee.core.annotation.LoginRequired;
import com.doumee.core.annotation.LoginShopRequired;
import com.doumee.core.annotation.trace.Trace;
import com.doumee.core.constants.Constants;
import com.doumee.core.model.ApiResponse;
import com.doumee.core.model.PageData;
import com.doumee.core.model.PageWrap;
@@ -10,7 +11,6 @@
import com.doumee.dao.dto.CommentOrderDTO;
import com.doumee.dao.dto.ConfirmArriveDTO;
import com.doumee.dao.dto.CreateOrderDTO;
import com.doumee.dao.dto.DriverVerifyDTO;
import com.doumee.dao.dto.ShopVerifyDTO;
import com.doumee.dao.dto.StoreOutDTO;
import com.doumee.dao.dto.MyOrderDTO;
@@ -147,24 +147,13 @@
    }
    @LoginShopRequired
    @ApiOperation(value = "门店核销收件", notes = "门店通过核销码确认收件/取件")
    @ApiOperation(value = "门店核销", notes = "门店通过订单主键确认寄存/取件")
    @PostMapping("/shopVerify")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", dataType = "String", name = "token", value = "门店token值", required = true)
    })
    public ApiResponse shopVerify(@RequestBody @Validated ShopVerifyDTO dto) {
        ordersService.shopVerifyOrder(dto.getVerifyCode(), getShopId(), dto.getImages(), dto.getRemark());
        return ApiResponse.success("核销成功");
    }
    @LoginShopRequired
    @ApiOperation(value = "核销司机码", notes = "异地寄存且有取件门店的订单,通过司机核销码确认到店")
    @PostMapping("/driverVerify")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", dataType = "String", name = "token", value = "用户token值", required = true)
    })
    public ApiResponse driverVerify(@RequestBody @Validated DriverVerifyDTO dto) {
        ordersService.driverVerifyOrder(dto.getVerifyCode(), dto.getImages(), dto.getRemark(), getShopId());
        ordersService.verifyOrderByShopId(dto.getOrderId(), getShopId(), dto.getImages(), dto.getRemark());
        return ApiResponse.success("核销成功");
    }
@@ -178,7 +167,9 @@
    })
    public ApiResponse<MyOrderDetailVO> shopDetail(@RequestParam(required = false) Integer orderId,
                                                    @RequestParam(required = false) String verifyCode) {
        return ApiResponse.success("查询成功", ordersService.findShopOrderDetail(orderId, verifyCode));
        MyOrderDetailVO myOrderDetailVO = ordersService.findShopOrderDetail(orderId, verifyCode);
        myOrderDetailVO.setMemberVerifyCode(null);
        return ApiResponse.success("查询成功", myOrderDetailVO);
    }
    @LoginShopRequired
server/web/src/main/java/com/doumee/api/web/PaymentCallback.java
@@ -22,6 +22,7 @@
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.refund.model.RefundNotification;
import com.wechat.pay.java.service.refund.model.Status;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
@@ -36,6 +37,7 @@
 * @Author : Rk
 * @create 2023/3/24 16:57
 */
@Api(tags = "支付回调业务接口")
@Slf4j
@RestController
@CrossOrigin
server/web/src/main/java/com/doumee/api/web/RevenueApi.java
@@ -1,5 +1,6 @@
package com.doumee.api.web;
import com.doumee.core.annotation.LoginDriverRequired;
import com.doumee.core.annotation.LoginRequired;
import com.doumee.core.annotation.LoginShopRequired;
import com.doumee.core.annotation.trace.Trace;
@@ -29,24 +30,24 @@
    @Autowired
    private RevenueService revenueService;
    @LoginRequired
    @LoginDriverRequired
    @ApiOperation(value = "司机流水分页", notes = "小程序端")
    @PostMapping("/driverPage")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", dataType = "String", name = "token", value = "用户token值", required = true),
    })
    public ApiResponse<PageData<Revenue>> driverPage(@RequestBody @Validated PageWrap<RevenueQueryDTO> pageWrap) {
        return ApiResponse.success("查询成功", revenueService.findDriverRevenuePage(pageWrap, getMemberId()));
        return ApiResponse.success("查询成功", revenueService.findDriverRevenuePage(pageWrap, getDriverId()));
    }
    @LoginRequired
    @LoginDriverRequired
    @ApiOperation(value = "司机流水收支统计", notes = "根据查询条件统计收入与支出")
    @PostMapping("/driverSummary")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", dataType = "String", name = "token", value = "用户token值", required = true),
    })
    public ApiResponse<RevenueSummaryVO> driverSummary(@RequestBody RevenueQueryDTO queryDTO) {
        return ApiResponse.success("查询成功", revenueService.getDriverRevenueSummary(queryDTO, getMemberId()));
        return ApiResponse.success("查询成功", revenueService.getDriverRevenueSummary(queryDTO, getDriverId()));
    }
    @LoginShopRequired
server/web/src/main/java/com/doumee/api/web/ShopInfoApi.java
@@ -2,6 +2,8 @@
import com.doumee.core.annotation.LoginRequired;
import com.doumee.core.annotation.LoginShopRequired;
import com.doumee.core.annotation.trace.Trace;
import com.doumee.config.jwt.JwtTokenUtil;
import com.doumee.core.model.ApiResponse;
import com.doumee.core.model.PageData;
import com.doumee.core.model.PageWrap;
@@ -31,7 +33,7 @@
 * @author rk
 * @date 2026/04/10
 */
@Api(tags = "门店入驻")
@Api(tags = "门店业务接口")
@RestController
@RequestMapping("/web/shopInfo")
public class ShopInfoApi extends ApiController {
@@ -58,6 +60,7 @@
        return ApiResponse.success(shopInfoService.getMyShop(this.getMemberId()));
    }
    @ApiOperation("附近门店分页列表")
    @PostMapping("/nearby")
    public ApiResponse<PageData<ShopNearbyVO>> nearby(@RequestBody @Validated PageWrap<ShopNearbyDTO> pageWrap) {
@@ -81,6 +84,16 @@
    }
    @LoginShopRequired
    @ApiOperation("门店端查询门店详情")
    @GetMapping("/shopDetail")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", dataType = "String", name = "token", value = "门店token值", required = true)
    })
    public ApiResponse<ShopDetailVO> shopDetail() {
        return ApiResponse.success("查询成功", shopInfoService.getShopDetail(getShopId()));
    }
    @LoginShopRequired
    @ApiOperation("门店销售统计")
    @GetMapping("/salesStats")
    @ApiImplicitParams({
@@ -95,7 +108,7 @@
    @ApiOperation("维护门店信息(支付押金后)")
    @PostMapping("/maintain")
    public ApiResponse maintain(@RequestBody ShopInfoMaintainDTO dto) {
        shopInfoService.maintainShopInfo(this.getMemberId(), dto);
        shopInfoService.maintainShopInfo(this.getShopId(), dto);
        return ApiResponse.success("操作成功");
    }
@@ -103,7 +116,7 @@
    @ApiOperation("查询门店维护信息")
    @PostMapping("/maintainInfo")
    public ApiResponse<ShopInfoMaintainDTO> maintainInfo() {
        return ApiResponse.success(shopInfoService.getShopMaintainInfo(this.getMemberId()));
        return ApiResponse.success(shopInfoService.getShopMaintainInfo(this.getShopId()));
    }
    @LoginRequired
@@ -116,4 +129,17 @@
        return ApiResponse.success("操作成功", ordersService.payShopDeposit(getMemberId()));
    }
    @LoginShopRequired
    @Trace
    @ApiOperation(value = "门店修改密码", notes = "新密码必须同时包含字母和数字,修改成功后需重新登录")
    @PostMapping("/changePassword")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", dataType = "String", name = "token", value = "门店token值", required = true),
    })
    public ApiResponse changePassword(@RequestParam String newPassword) {
        String token = this.getRequest().getHeader(JwtTokenUtil.HEADER_KEY);
        shopInfoService.changePassword(this.getShopId(), newPassword, token);
        return ApiResponse.success("密码修改成功,请重新登录");
    }
}
server/web/src/main/java/com/doumee/api/web/WalletApi.java
@@ -1,5 +1,6 @@
package com.doumee.api.web;
import com.doumee.core.annotation.LoginDriverRequired;
import com.doumee.core.annotation.LoginRequired;
import com.doumee.core.annotation.LoginShopRequired;
import com.doumee.core.model.ApiResponse;
@@ -40,14 +41,14 @@
    @Autowired
    private RevenueService revenueService;
    @LoginRequired
    @LoginDriverRequired
    @ApiOperation(value = "司机提现申请", notes = "小程序端")
    @PostMapping("/driverApply")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", dataType = "String", name = "token", value = "用户token值", required = true),
    })
    public ApiResponse driverApply(@RequestBody @Validated WithdrawalDTO dto) {
        withdrawalOrdersService.applyDriverWithdrawal(dto, getMemberId());
        withdrawalOrdersService.applyDriverWithdrawal(dto, getDriverId());
        return ApiResponse.success("提现申请已提交");
    }
@@ -72,24 +73,36 @@
        return ApiResponse.success("查询成功", revenueService.getShopRevenueStatistics(getShopId()));
    }
    @LoginRequired
    @LoginDriverRequired
    @ApiOperation(value = "司机收益统计", notes = "当前登录司机的收益/提现统计")
    @GetMapping("/driverStatistics")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", dataType = "String", name = "token", value = "用户token值", required = true)
    })
    public ApiResponse<RevenueStatisticsVO> driverStatistics() {
        return ApiResponse.success("查询成功", revenueService.getDriverRevenueStatistics(getMemberId()));
        return ApiResponse.success("查询成功", revenueService.getDriverRevenueStatistics(getDriverId()));
    }
    @LoginRequired
    @ApiOperation(value = "提现详情", notes = "根据提现主键查询详情(含审批人信息)")
    @GetMapping("/detail/{id}")
    @LoginShopRequired
    @ApiOperation(value = "门店提现详情", notes = "根据提现主键查询详情(含审批人信息)")
    @GetMapping("/shopDetail/{id}")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", dataType = "String", name = "token", value = "用户token值", required = true),
            @ApiImplicitParam(paramType = "path", dataType = "Integer", name = "id", value = "提现记录主键", required = true)
    })
    public ApiResponse<WithdrawalOrders> detail(@PathVariable Integer id) {
    public ApiResponse<WithdrawalOrders> shopDetail(@PathVariable Integer id) {
        return ApiResponse.success("查询成功", withdrawalOrdersService.findById(id));
    }
    @LoginDriverRequired
    @ApiOperation(value = "司机提现详情", notes = "根据提现主键查询详情(含审批人信息)")
    @GetMapping("/driverDetail/{id}")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", dataType = "String", name = "token", value = "用户token值", required = true),
            @ApiImplicitParam(paramType = "path", dataType = "Integer", name = "id", value = "提现记录主键", required = true)
    })
    public ApiResponse<WithdrawalOrders> driverDetail(@PathVariable Integer id) {
        return ApiResponse.success("查询成功", withdrawalOrdersService.findById(id));
    }
server/web/src/main/resources/application.yml
@@ -12,7 +12,7 @@
spring:
  profiles:
    active: dev
    active: pro
  # JSON返回配置
  jackson:
    # é»˜è®¤æ—¶åŒº
small-program/pages/delivery-order-detail/delivery-order-detail.vue
@@ -13,10 +13,12 @@
            <view class="nr-status">
                <text>{{info.statusName|| ''}}</text>
                <view class="nr-status-type1" v-if="info.type === 0">就地寄存</view>
                <view class="nr-status-type" v-else>同城寄送</view>
                <view class="nr-status-type"  v-if="info.type === 1">同城寄送</view>
                <view class="nr-status-type1"  v-if="info.type === 1 && info.isUrgent === 0">标速达</view>
                <view class="nr-status-type1"  v-if="info.type === 1 && info.isUrgent === 1">极速达</view>
            </view>
            <view class="nr-desc" v-if="info.statusDesc">{{info.statusDesc || ''}}</view>
            <view class="item" style="padding: 10rpx;">
            <view class="item" style="padding: 10rpx;" v-if="info.type ===1 && info.status===4">
                <map name="mapAddr"></map>
            </view>
            <view class="item">
@@ -30,7 +32,7 @@
                        </view>
                        <text>{{info.takeUser || ''}} {{info.takePhone || ''}}</text>
                    </view>
                    <view class="addr-item">
                    <view class="addr-item" v-if="info.type === 1">
                        <view class="ji bg1">收</view>
                        <view class="addr-item-top">
                            <text>{{info.takeShopName || info.takeLocation || ''}}</text>
@@ -39,10 +41,14 @@
                        <text>{{info.takeUser || ''}} {{info.takePhone || ''}}</text>
                    </view>
                </view>
                <view class="item-qrcode">
                    <image src="/static/image/btn_upload@2x.png" mode="widthFix"></image>
                <view class="item-qrcode" v-if=" (info.type ===0 && info.status >=1 &&info.status <7) || (info.type ===1 && (info.status ===1 || (info.takeShopId && info.status ===5)))">
                    <!-- <image src="/static/image/btn_upload@2x.png" mode="widthFix"></image> -->
                    <view style="width: 360rpx; height: 360rpx;">
                        <canvas canvas-id="qrcodeCanvas" id="qrcodeCanvas" style="width: 180px; height: 180px;"></canvas>
                        <!-- <image :src="qrcodeImage" mode="widthFix"></image> -->
                    </view>
                    <text>{{info.memberVerifyCode || ''}}</text>
                    <text>取件码</text>
                    <text @tap="copyCode(info.memberVerifyCode||'')">核销码</text>
                </view>
                <view class="item-x"></view>
                <view class="item-list">
@@ -56,32 +62,65 @@
                    </view>
                    <view class="item-list-row">
                        <view class="item-form-label">物品名称</view>
                        <view class="item-form-val">{{info.typeName || ''}}</view>
                        <view class="item-form-val">{{info.goodTypeName || ''}}</view>
                    </view>
                    <view class="item-list-row">
                        <view class="item-form-label">物品照片</view>
                        <view class="item-form-list" v-if="info.orderImages && info.orderImages.length">
                            <view class="item-form-list-row" v-for="(item,index) in info.orderImages" key="item">
                                <image :src="item" mode="widthFix" @click="previewImage(info.orderImages,index)"></image>
                            </view>
                            </view>
                        </view>
                    </view>
                </view>
            </view>
            <view class="item">
            <view class="item" v-if="info.refundInfo">
                <view class="tuikuan">
                    <view class="tuikuan-top">
                        <text>退款金额</text>
                        <text>Â¥{{((info.refundAmount || 0)/100).toFixed(2)}}</text>
                        <text>Â¥{{((info.refundInfo.refundAmount || 0)/100).toFixed(2)}}</text>
                    </view>
                    <view class="tuikuan-bottom">
                        <text v-if="info.refundStatus === 0">退款中</text>
                        <text v-if="info.refundStatus === 1">已退回您的支付账户</text>
                        <text v-if="info.refundStatus === 2">退款失败</text>
                        <text>{{info.refundTime || ''}}</text>
                        <text v-if="info.refundInfo.status === 0">退款中</text>
                        <text v-if="info.refundInfo.status  === 1">已退回您的支付账户</text>
                        <text v-if="info.refundInfo.status  === 2">退款失败</text>
                        <text v-if="info.refundInfo.status  === 1">{{info.refundInfo.refundTime || ''}}</text>
                        <text v-if="info.refundInfo.status  !== 1">{{info.refundInfo.createTime || ''}}</text>
                    </view>
                </view>
            </view>
            <view class="item" v-if="info.commentStatus === 1">
                <view class="comment">
                    <view class="comment-top">
                        <text>客户已评价:</text>
                    </view>
                    <view class="comment-score">
                        <view class="score-item" v-if="info.depositScore">寄件门店: <text style="color:#ffc533;font-size: 38rpx;margin-right: 10rpx;">★</text>{{info.depositScore || 1}} </view>
                        <view class="score-item"  v-if="info.driverScore">配送司机: <text style="color:#ffc533;font-size: 38rpx;margin-right: 10rpx;">★</text>{{info.driverScore || 1}} </view>
                        <view  class="score-item" v-if="info.takeScore">收件门店: <text style="color:#ffc533;font-size: 38rpx;margin-right: 10rpx;">★</text>{{info.takeScore || 1}} </view>
                    </view>
                    <view class="comment-row" v-if="info.commentImages && info.commentImages.length" >
                        <view class="comment-image"  v-for="(item,index) in info.commentImages" key="item">
                            <image :src="item" mode="widthFix" @click="previewImage(info.commentImages,index)"></image>
                        </view>
                    </view>
                </view>
            </view>
            <view class="item" v-if="info.refundInfo">
                <view class="tuikuan">
                    <view class="tuikuan-top">
                        <text>退款金额</text>
                        <text>Â¥{{((info.refundInfo.refundAmount || 0)/100).toFixed(2)}}</text>
                    </view>
                    <view class="tuikuan-bottom">
                        <text v-if="info.refundInfo.status === 0">退款中</text>
                        <text v-if="info.refundInfo.status  === 1">已退回您的支付账户</text>
                        <text v-if="info.refundInfo.status  === 2">退款失败</text>
                        <text v-if="info.refundInfo.status  === 1">{{info.refundInfo.refundTime || ''}}</text>
                        <text v-if="info.refundInfo.status  !== 1">{{info.refundInfo.createTime || ''}}</text>
                    </view>
                </view>
            </view>
            <view class="item" v-if="info.detailList">
                <view class="xl">
                    <view v-for="goods in info.detailList " :key="goods.luggageName" class="xl-item">
@@ -118,7 +157,7 @@
                        <view class="item-infos-item-left">订单编号:</view>
                        <view class="item-infos-item-right">
                            <text>{{info.code ||''}}</text>
                            <image src="/static/icon/ic_cppy@2x.png" mode="widthFix"></image>
                            <image src="/static/icon/ic_cppy@2x.png" mode="widthFix" @tap="copyCode(info.code||'')"></image>
                        </view>
                    </view>
                    <view class="item-infos-item">
@@ -140,7 +179,7 @@
                        </view>
                    </view>
                    <view class="item-infos-item" v-if="info.outTradeNo">
                        <view class="item-infos-item-left">交易号:</view>
                        <view class="item-infos-item-left">交易单号:</view>
                        <view class="item-infos-item-right">
                            <text>{{info.outTradeNo || ''}}</text>
                        </view>
@@ -157,37 +196,40 @@
                            <text>{{info.refundTime || ''}}</text>
                        </view>
                    </view>
                    <view class="item-infos-item" v-if="info.refundAmount">
                    <view class="item-infos-item" v-if="info.refundInfo">
                        <view class="item-infos-item-left">退款金额:</view>
                        <view class="item-infos-item-right">
                            <text>Â¥{{((info.refundAmount || 0)/100).toFixed(2)}}</text>
                            <text>Â¥{{((info.refundInfo.refundAmount || 0)/100).toFixed(2)}}</text>
                        </view>
                    </view>
                    <view class="item-infos-item"  v-if="info.refundRemark">
                        <view class="item-infos-item-left">退款原因:</view>
                        <view class="item-infos-item-right">
                            <text>{{info.refundRemark || ''}}</text>
                            <text>{{info.refundInfo.refundRemark || ''}}</text>
                        </view>
                    </view>
                </view>
            </view>
            <view style="width: 100%; height: calc(210rpx + env(safe-area-inset-bottom));"></view>
        </view>
        <view class="tips" v-if="info.overdue">
        <view class="tips" v-if="info.overdueStatus !==0">
            <image src="/static/icon/ic_waring@2x.png" mode="widthFix"></image>
            <text>已超过取件时间,超时费用:¥{{((info.overdueFee || 0)/100).toFixed(2)}}元</text>
            <text  v-if="info.overdueStatus ===1">已超过取件时间,已产生超时费用:¥{{((info.overdueFee || 0)/100).toFixed(2)}}元</text>
            <text  v-if="info.overdueStatus ===2">已超过取件时间,需要支付超时费用:¥{{((info.overdueFee || 0)/100).toFixed(2)}}元</text>
            <text  v-if="info.overdueStatus ===3">超过取件时间,已支付超时费用:¥{{((info.overdueFee || 0)/100).toFixed(2)}}元</text>
        </view> 
        <view cclass="footer" v-if="info.type===0">
        <view class="footer" v-if="info.type===0">
            <view class="footer-btns">
                <view class="btn kong" @click="contactPhone(info,0)" v-if="info.status ===1 || info.status==2 || info.status===98">联系门店</view>
                <view class="btn kong" v-if="info.status ===0 || info.status ===1"  @click="cancelOrder(info)">取消订单</view>
                <view class="btn kong" @click="deleteOrder(info)" v-if="info.status ===7 || info.status===96 || info.status == 99">删除订单</view>
                <view class="btn you"  @click="payOrder(info)" v-if="info.status ===0">立即支付</view>
                <view class="btn you" v-if="info.status >=1 &&info.status <7 " @click="openQrcode(info)" >核销码</view>
                <view class="btn you" @click="evaluateOrder(info)" v-if="info.status ===7 && !info.commentStatus ">评价订单</view>
<!--                 <view class="btn you" v-if="info.status >=1 &&info.status <7 " @click="openQrcode(info)" >核销码</view>
 -->            <view class="btn you" @click="evaluateOrder(info)" v-if="info.status ===7 && !info.commentStatus ">评价订单</view>
                <view class="btn you" @click="payOrderFee(info)" v-if="info.status ===5 && info.overdueStatus===2 ">立即支付</view>
            </view>
        </view>
        <view class="footer" v-else>
        <view class="footer" v-if="info.type===1">
            <view class="footer-btns">
                <view class="btn kong" @click="contactPhone(info,0)" v-if="info.status ===1 || info.status ==2">联系门店</view> 
                <view class="btn kong" @click="contactPhone(info,2)" v-if="info.status ===3 || info.status ===4 ">联系骑手</view>
@@ -196,22 +238,23 @@
                <view class="btn you" @click="payOrder(info)" v-if="info.status ===0">立即支付</view>
                <view class="btn kong" @click="deleteOrder(info)" v-if="info.status ===7 || info.status===96 || info.status == 99">删除订单</view>
                <view class="btn you" @click="cancelOrder(info)" v-if="info.status ===1">申请退款</view>
                <view class="btn you" v-if="info.status ===1 || (info.takeShopId && info.status ===5)" @click="openQrcode(info)" >核销码</view>
                <view class="btn you"  @click="doneOrder(info)"  v-if="!info.takeShopId && info.status ===5">确认收货</view>
<!--                 <view class="btn you" v-if="info.status ===1 || (info.takeShopId && info.status ===5)" @click="openQrcode(info)" >核销码</view>
 -->                <view class="btn you"  @click="doneOrder(info)"  v-if="!info.takeShopId && info.status ===5">确认收货</view>
                <view class="btn you"  @click="evaluateOrder(info)" v-if="info.status ===7 && !info.commentStatus ">评价订单</view>
                <view class="btn you" @click="payOrderFee(info)" v-if="info.status ===5 && info.overdueStatus ===2">立即支付</view>
            </view>
        </view>
        <u-popup :show="showPhone" round="15" mode="bottom" :safeAreaInsetBottom="true"  @close="contactPhone()" :closeable="true" :closeOnClickOverlay="true">
            <view class="phone">
                <view class="phone-head">
                    <view></view>
                    <text>联系客户</text>
                    <text>{{linkItem.title}}</text>
                </view>
                 <view class="phone-item" >
                     <view>
                        <image src="/static/icon/ic_call@2x.png" mode="widthFix" @click="contactPhoneDo()" ></image>
                        <text>{{linkinfo.linkname||''}} </text>
                        <text style="margin-left: 10px;"> {{linkinfo.linkphone||''}}</text>
                     <view  @click="contactPhoneDo()" >
                        <image src="/static/icon/ic_call@2x.png" mode="widthFix"></image>
                        <text>{{linkItem.linkname||''}} </text>
                        <text style="margin-left: 10px;"> {{linkItem.linkphone||''}}</text>
                    </view> 
                 </view>  
                 <view style="width: 100%; height: 30rpx;"></view>
@@ -261,24 +304,25 @@
                </view>
            </view>
        </u-popup>
        <u-popup :show="showQrcode" round="15" @close="openQrcode()"   :safeAreaInsetBottom="false" mode="bottom"  :closeable="true" :closeOnClickOverlay="false">
        <!-- <u-popup :show="showQrcode" round="15" @close="openQrcode()"   :safeAreaInsetBottom="false" mode="bottom"  :closeable="true" :closeOnClickOverlay="false">
            <view class="tc" style="height: 700rpx;width: 100%; ">
                <view class="tc-contemt" style="text-align: center;">
                    <view class="tc-contemt-title" style="text-align: center;">核销码</view>
                    <view class="qrcode-box">
                        <canvas canvas-id="qrcodeCanvas" id="qrcodeCanvas" style="width: 100px; height: 100px;"></canvas>
                        <canvas canvas-id="qrcodeCanvas1" id="qrcodeCanvas1" style="width: 100px; height: 100px;"></canvas>
                        <image class="qrcode-image" :src="qrcodeImage" mode="widthFix"></image>
                    </view>
                    <text class="pickup-code">{{ currentOrder.memberVerifyCode||'' }}</text>
                    <text class="pickup-tip" @tap="copyCode">点击复制自提码</text>
                    <text class="pickup-code">{{ info.memberVerifyCode||'' }}</text>
                    <text class="pickup-tip" @tap="copyCode(info.memberVerifyCode||'')">点击复制自提码</text>
                </view>
            </view>
        </u-popup>
        </u-popup> -->
    </view>
</template>
<script>
    import { mapState } from 'vuex'
    import drawQrcode from 'weapp-qrcode'
    export default {
        computed: {
@@ -293,7 +337,9 @@
                showDelete:false,
                showQrcode:false,
                showPay:false,
                showPhone:false
                showPhone:false,
                linkItem:{title:'',linkname:'',linkphone:''},
                qrcodeImage:null
            }
        },
        onShow() {
@@ -305,6 +351,7 @@
            this.showPay=false
            this.showPhone=false
            this.getUserDetail() 
            this.linkItem={title:'',linkname:'',linkphone:''}
        },
        onLoad(options) {
            this.id = options.id
@@ -351,6 +398,41 @@
                    uni.showToast({ title: '支付失败', icon: 'none' })
                })
            },
            payOrderFee(){
                var that = this;
                uni.showLoading({ title: '发起支付中...', mask: true })
                this.$u.api.payOverdueFee({
                    orderId: this.id
                }).then(res => {
                    uni.hideLoading()
                    if (res.code === 200 && res.data) {
                        let paymentData = res.data.response
                        uni.requestPayment({
                            provider: 'wxpay',
                            timeStamp: paymentData.timeStamp || '',
                            nonceStr: paymentData.nonceStr || '',
                            package: paymentData.package || '',
                            signType: paymentData.signType || 'MD5',
                            paySign: paymentData.paySign || '',
                            success: (res) => {
                                that.getUserDetail()
                            },
                            fail: (err) => {
                                if (err.errMsg.includes('cancel')) {
                                    uni.showToast({ title: '已取消支付', icon: 'none' })
                                } else {
                                    uni.showToast({ title: '支付失败', icon: 'none' })
                                }
                            }
                        })
                    } else {
                        uni.showToast({ title: res.msg || '支付失败', icon: 'none' })
                    }
                }).catch(err => {
                    uni.hideLoading()
                    uni.showToast({ title: '支付失败', icon: 'none' })
                })
            },
            doneOrder(item){
                this.showDone = !this.showDone
            },
@@ -362,13 +444,22 @@
                    that.getUserDetail()
                } 
            },
            contactPhone(){
            contactPhone(item,type){
                 this.showPhone = !this.showPhone
                 this.linkItem = {title:'联系骑手',linkname:'',linkphone:''}
                 if(type == 0){
                     this.linkItem = {title:'联系门店',linkname:this.info.depositShopName,linkphone:this.info.depositShopPhone}
                 }else if(type ==2){
                     this.linkItem = {title:'联系骑手',linkname:this.info.driverName,linkphone:this.info.driverPhone}
                 }else if(type ==1){
                     this.linkItem = {title:'联系门店',linkname:this.info.takeShopName,linkphone:this.info.takeShopPhone}
                 }
                  console.log("==================================",this.showPhone)
            },
            contactPhoneDo(){ 
                if(this.info.linkphone !=null && this.info.linkphone!=''){
                if(this.linkItem.linkphone !=null && this.linkItem.linkphone!=''){
                    uni.makePhoneCall({
                       phoneNumber: this.info.linkphone
                       phoneNumber: this.linkItem.linkphone
                    })
                }
            },
@@ -384,6 +475,7 @@
                if (res.code === 200 ) { 
                    this.showCancel = false
                    uni.$emit('updateOrder',{info:this.info,delete:0})
                    that.getUserDetail()
                } 
            },
            async deleteOrderDo(){
@@ -398,15 +490,15 @@
                    this.goBack()
                } 
            },
            copyCode() {
                if (!this.currentOrder || !this.info.memberVerifyCode) {
            copyCode(txt) {
                if (!txt) {
                    return
                }
                uni.setClipboardData({
                    data: this.info.memberVerifyCode,
                    data: txt,
                    success: () => {
                        uni.showToast({
                            title: '已复制自提码',
                            title: '已复制到粘贴板',
                            icon: 'none'
                        })
                    }
@@ -418,7 +510,7 @@
                if(!this.showQrcode){
                    this.showQrcode =true
                    drawQrcode({
                        canvasId: 'qrcodeCanvas',
                        canvasId: 'qrcodeCanvas1',
                        text: this.info.memberVerifyCode,
                        width: 100,
                        height: 100,
@@ -436,6 +528,32 @@
                    this.showQrcode = false
                }
            },
            async showQrcodeImg(){
                if((this.info.type ===0 && this.info.status >=1 &&this.info.status <7)
                || (this.info.type ===1 && (this.info.status ===1 || (this.info.takeShopId && this.info.status ===5)))){
                    if(this.qrcodeImage){
                        return
                    }
                    this.qrcodeImage=null
                    var that =this
                    drawQrcode({
                        canvasId: 'qrcodeCanvas',
                        text: this.info.memberVerifyCode,
                        width: 180,
                        height: 180,
                        correctLevel: 2
                    })
                    setTimeout(() => {
                        uni.canvasToTempFilePath({
                            canvasId: 'qrcodeCanvas',
                            success: (res) => {
                                that.qrcodeImage = res.tempFilePath
                            }
                        }, this)
                    }, 100)
                }
            },
            evaluateOrder(){
                uni.navigateTo({
                    url:"/pages/evaluate/evaluate?id="+this.info.id
@@ -450,6 +568,7 @@
                if (res.code === 200) { 
                    this.info = res.data 
                    uni.$emit('updateOrder',{info:this.info,delete:0})
                    this.showQrcodeImg()
                } 
            }
        }
@@ -470,7 +589,7 @@
            box-sizing: border-box;
            background: #FFE9E9;
            position: fixed;
            bottom: calc(100rpx + env(safe-area-inset-bottom));
            bottom: calc(30rpx + env(safe-area-inset-bottom));
            left: 0;
            display: flex;
            align-items: center;
@@ -571,7 +690,7 @@
                    margin-right: 16rpx;
                }
                .nr-status-type {
                    width: 164rpx;
                    width: 154rpx;
                    height: 38rpx;
                    display: flex;
                    align-items: center;
@@ -583,7 +702,7 @@
                    color: #FA8010;
                }
                .nr-status-type1 {
                    width: 188rpx;
                    width: 158rpx;
                    height: 38rpx;
                    display: flex;
                    align-items: center;
@@ -708,6 +827,63 @@
                        }
                    }
                }
                .comment{
                    width: 100%;
                    display: flex;
                    flex-direction: column;
                    .comment-top {
                        width: 100%;
                        display: flex;
                        align-items: center;
                        justify-content: space-between;
                        text {
                            &:nth-child(1) {
                                font-weight: 600;
                                font-size: 30rpx;
                                color: #222222;
                            }
                            &:nth-child(2) {
                                font-weight: 500;
                                font-size: 36rpx;
                                color: #FE2C2E;
                                &:before {
                                    content: "ï¿¥";
                                    font-weight: 500;
                                    font-size: 24rpx;
                                    color: #FE2C2E;
                                }
                            }
                        }
                    }
                    .comment-score{
                        // font-weight: 600;
                        font-size: 28rpx;
                        color: #333333;
                        margin: 10rpx 0;
                        display: flex;
                        width: 100%;
                        .score-item{
                            flex:1;
                            margin: 20rpx 0;
                        }
                    }
                    .comment-row{
                        display: flex;
                        flex-wrap: wrap;
                        .comment-image {
                            margin: 10rpx;
                            width: 100rpx;
                            height: 100rpx;
                            border-radius: 8rpx;
                            border:1px solid #f2f2f2;
                            overflow: hidden;
                            image {
                                width: 100%;
                            }
                        }
                    }
                }
                .tuikuan {
                    width: 100%;
                    display: flex;
small-program/pages/evaluate/evaluate.vue
@@ -197,9 +197,10 @@
                        })
                        try {
                            const uploadResults = await that.uploadFiles(tempFilePaths, maxCount)
                            const addrs = uploadResults.map(item => item.imgaddr)
                            const fullPaths = uploadResults.map(item => item.url || item.path || item)
                            that.photoList = [...that.photoList, ...fullPaths.map(url => ({ url }))]
                            that.form.images = [...that.form.images, ...fullPaths]
                            that.form.images = [...that.form.images, ...addrs]
                            uni.hideLoading()
                            uni.showToast({
                                title: '上传成功',
small-program/pages/itinerary/itinerary.vue
@@ -88,7 +88,7 @@
                    <view class="card-footer">
                        <view class="footer-actions" v-if="item.type===0">
                            <view class="footer-btn contact-btn" @click="contactPhone(item,0)" v-if="item.status ===1 || item.status==2 || item.status===98">联系门店</view>
                            <view class="footer-btn contact-btn" @click="contactPhone(item,0)" v-if="item.status ===1 ||item.status ===5 || item.status==2 || item.status===98">联系门店</view>
                             <view class="footer-btn contact-btn" v-if="item.status ===0 || item.status ===1"  @click="cancelOrder(item)">取消订单</view>
                            <view class="footer-btn contact-btn" @click="deleteOrder(item)" v-if="item.status ===7 || item.status===96 || item.status == 99">删除订单</view>
                            <view class="footer-btn primary-btn"  @click="payOrder(item)" v-if="item.status ===0">立即支付</view>
@@ -119,7 +119,7 @@
            <view class="phone">
                <view class="phone-head">
                    <view></view>
                    <text>联系客户</text>
                    <text>{{linkItem.title}}</text>
                </view>
                <!-- <view class="phone-item">
                    <view>
@@ -129,10 +129,10 @@
                     <view class="line" v-if="serverPhone && serverPhone.length"></view>
                </view> -->
                 <view class="phone-item" >
                     <view>
                        <image src="/static/icon/ic_call@2x.png" mode="widthFix" @click="contactPhoneDo()" ></image>
                        <text>{{linkItem.linkname||''}} </text>
                        <text style="margin-left: 10px;"> {{linkItem.linkphone||''}}</text>
                     <view @click="contactPhoneDo()">
                        <image src="/static/icon/ic_call@2x.png" mode="widthFix"  ></image>
                        <text  >{{linkItem.linkname||''}} </text>
                        <text  style="margin-left: 10px;"> {{linkItem.linkphone||''}}</text>
                    </view> 
                 </view>  
                 <view style="width: 100%; height: 30rpx;"></view>
@@ -226,6 +226,8 @@
                qrcodeImage:null,
                isLoadingMore: false,
                currentOrder:null,
                contanctType:0,
                linkItem:{title:'',linkname:'',linkphone:''},
                filterTabs: [
                    { label: '全部'  ,value: -1},
                    { label: '待支付',value: 0 },
@@ -246,10 +248,9 @@
        onLoad(options) {
            console.log(options )
            this.activeTab = -1
            if (options.status) {
            if (options.status != null && options.status != undefined) {
                this.activeTab =  Number(options.status)
            }
            this.getFirstPageData()
            var that =this
            uni.$on('updateOrder',function(data){
                console.log('监听到事件来自 update ï¼Œæºå¸¦å‚æ•° msg ä¸ºï¼š' ,data);
@@ -275,9 +276,18 @@
            this.showDelete=false
            this.showQrcode=false
            this.showPay=false
            this.contanctType =0
            this.showPhone=false
            this.loading=false
            this.currentOrder=null
            this.linkItem={title:'',linkname:'',linkphone:''}
            var orderStatus = uni.getStorageSync("orderStatus");
            console.log(orderStatus,"==================")
            if (orderStatus != null &&orderStatus != undefined) {
                this.activeTab =  Number(orderStatus)
            }
            uni.clearStorageSync("orderStatus")
            // this.getFirstPageData()
        },
        onReachBottom(){
            this.getDataList();
@@ -337,10 +347,20 @@
                    that.showDone()
                } 
            },
            contactPhone(item){
            contactPhone(item,type){
                 this.showPhone = !this.showPhone
                 this.linkItem = item ||{}
                this.linkItem = {title:'',linkname:'',linkphone:''}
                 console.log("==================================",this.showPhone)
                 if(!item){
                     return
                 }
                if(type == 0){
                    this.linkItem = {title:'联系门店',linkname:item.depositShopName,linkphone:item.depositShopPhone}
                }else if(type ==2){
                    this.linkItem = {title:'联系骑手',linkname:item.driverName,linkphone:item.driverPhone}
                }else if(type ==1){
                    this.linkItem = {title:'联系门店',linkname:item.takeShopName,linkphone:item.takeShopPhone}
                }
            },
            contactPhoneDo(){ 
                if(this.linkItem.linkphone !=null && this.linkItem.linkphone!=''){
@@ -685,6 +705,7 @@
        display: block;
        white-space: nowrap;
        overflow: hidden;
        text-align: center;
        text-overflow: ellipsis;
        max-width: 100%;  
    }
small-program/pages/luggage-storage/luggage-storage.vue
@@ -557,9 +557,10 @@
                        })
                        try {
                            const uploadResults = await this.uploadFiles(tempFilePaths, maxCount)
                            const addrs = uploadResults.map(item => item.imgaddr)
                            const fullPaths = uploadResults.map(item => item.url || item.path || item)
                            this.uploadedImages = [...this.uploadedImages, ...fullPaths.map(url => ({ url }))]
                            this.form.goodsImages = [...this.form.goodsImages, ...fullPaths]
                            this.form.goodsImages = [...this.form.goodsImages, ...addrs]
                            uni.hideLoading()
                            uni.showToast({
                                title: '上传成功',
small-program/pages/mine/mine.vue
@@ -26,13 +26,13 @@
                <view class="order-card section-card">
                    <view class="section-head">
                        <text class="section-title">我的订单</text>
                        <view class="more-wrap" @click="toAllOrders">
                        <view class="more-wrap" @click="toOrderDetail(-1)">
                            <text class="more-text">全部订单</text>
                            <u-icon name="arrow-right" size="13" color="#999999"></u-icon>
                        </view>
                    </view>
                    <view class="order-grid">
                        <view v-for="(item, index) in orderMenus" :key="index" class="order-item" @click="toOrderDetail(item.label)">
                        <view v-for="(item, index) in orderMenus" :key="index" class="order-item" @click="toOrderDetail(item.status)">
                            <image class="order-icon" :src="item.url" mode="widthFix"></image>
                            <view v-if="item.badge" class="order-badge">{{ item.badge }}</view>
                            <text class="order-label">{{ item.label }}</text>
@@ -83,9 +83,9 @@
            agreeChecked: true,
            userName: '',
            orderMenus: [
                { label: '待支付', url: '/static/icon/mine_ic_daifukuan@2x.png', badge: 0 },
                { label: '待收货', url: '/static/icon/mine_ic_daishouhuo@2x.png', badge: 0 },
                { label: '退款/售后', url: '/static/icon/mine_ic_tuikuan@2x.png', badge: 0 }
                { label: '待支付', url: '/static/icon/mine_ic_daifukuan@2x.png', badge: 0,status:0 },
                { label: '待收货', url: '/static/icon/mine_ic_daishouhuo@2x.png', badge: 0,status:3 },
                { label: '退款/售后', url: '/static/icon/mine_ic_tuikuan@2x.png', badge: 0 ,status:5}
            ],
            menuList: [
                { label: '门店入驻' },
@@ -121,8 +121,8 @@
            this.showAuthLogin = true
        },
        toAllOrders() {
            uni.navigateTo({
                url: '/pages/orders/orders'
            uni.switchTab({
                url: '/pages/itinerary/itinerary'
            })
        },
        handleMenuClick(item) {
@@ -145,8 +145,9 @@
            }
        },
        toOrderDetail(label) {
            uni.navigateTo({
                url: '/pages/orders/orders?status=' + label
            uni.setStorageSync("orderStatus",label)
            uni.switchTab({
                url: '/pages/itinerary/itinerary?status=' + label
            })
        },
        toSetting() {
small-program/pages/payment-success/payment-success.vue
@@ -16,13 +16,13 @@
            </view>
            <view class="pickup-card">
                <text class="pickup-title">自提码</text>
                <text class="pickup-title">核销码</text>
                <view class="qrcode-box">
                    <canvas canvas-id="qrcodeCanvas" id="qrcodeCanvas" style="width: 100px; height: 100px;"></canvas>
                    <image class="qrcode-image" :src="qrcodeImage" mode="widthFix"></image>
                </view>
                <text class="pickup-code">{{ orderDetail.memberVerifyCode }}</text>
                <text class="pickup-tip" @tap="copyCode">点击复制自提码</text>
                <text class="pickup-tip" @tap="copyCode">点击复制核销码</text>
            </view>
        </view>
    </view>
@@ -79,8 +79,8 @@
                })
            },
            viewOrder() {
                uni.redirectTo({
                    url: '/pages/itinerary/itinerary?orderId=' + this.orderId
                uni.navigateTo({
                    url:'/pages/delivery-order-detail/delivery-order-detail?userType=0&id='+this.orderId
                })
            },
            copyCode() {
@@ -96,7 +96,7 @@
                        })
                    }
                })
            }
            }
        }
    }
</script>
small-program/utils/http.api.js
@@ -47,8 +47,10 @@
    let confirmReceipt = (data = {}) => vm.$u.http.post('web/order/confirmReceipt/'+data.orderId, data);    // ä¼šå‘˜ç¡®è®¤æ”¶è´§ 
    let continuePayOrder = (data = {}) => vm.$u.http.post('web/order/continuePay/'+data.orderId, data);    //继续发起支付
    let orderComment = (data = {}) => vm.$u.http.post('web/order/comment', data);    //订单评价
    let payOverdueFee = (data = {}) => vm.$u.http.post('web/order/payOverdueFee/'+data.orderId, data);    //继续发起支付
    
    vm.$u.api = {
        payOverdueFee,
        orderComment, 
        confirmReceipt,
        cancelOrder,