rk
昨天 996b2f16afaa271ce8aad6abf6858aa5db503eb3
代码生成
已添加3个文件
已修改14个文件
473 ■■■■ 文件已修改
server/services/src/main/java/com/doumee/config/jwt/WebMvcConfig.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/business/model/Orders.java 8 ●●●●● 补丁 | 查看 | 原始文档 | 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/ShopInfoMaintainDTO.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/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/ShopDetailVO.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/DriverInfoService.java 25 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/impl/DriverInfoServiceImpl.java 191 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/impl/OrdersServiceImpl.java 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/impl/ShopInfoServiceImpl.java 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/web/src/main/java/com/doumee/api/web/DriverInfoApi.java 32 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/web/src/main/java/com/doumee/api/web/PaymentCallback.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/web/src/main/java/com/doumee/api/web/ShopInfoApi.java 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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 {
@@ -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);
@@ -145,6 +148,7 @@
    public Boolean checkShopLogin(String token,HttpServletRequest request, HttpServletResponse response){
        System.out.println("门店token:=========>{}"+token);
        try {
            if(!token.startsWith(Constants.TWO+"")){
                throw new BusinessException(ResponseStatus.SHOP_TOKEN_EXCEED_TIME);
@@ -185,6 +189,7 @@
    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/dao/business/model/Orders.java
@@ -420,6 +420,14 @@
    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;
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/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/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/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/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,9 +12,10 @@
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;
import java.util.Map;
/**
 * å¸æœºæ³¨å†Œä¿¡æ¯Service定义
@@ -269,8 +271,25 @@
     * èŽ·å–å¸æœºè¿›è¡Œä¸­è®¢å•æ•°é‡
     *
     * @param driverId å¸æœºä¸»é”®
     * @return [已抢单数量, æ´¾é€ä¸­æ•°é‡]
     * @return è¿›è¡Œä¸­è®¢å•数量
     */
    Map<String, Integer> getActiveOrderCount(Integer driverId);
    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/impl/DriverInfoServiceImpl.java
@@ -30,6 +30,8 @@
import com.doumee.dao.business.OrderLogMapper;
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;
@@ -48,6 +50,7 @@
import org.springframework.util.CollectionUtils;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
@@ -845,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)
@@ -945,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());
@@ -963,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")
@@ -1062,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);
@@ -1107,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")
@@ -1164,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<>();
@@ -1177,6 +1190,7 @@
                DriverOrderDetailVO.OrderItem item = new DriverOrderDetailVO.OrderItem();
                item.setName(src.getName());
                item.setQuantity(src.getQuantity());
                item.setIsOversized(src.getIsOversized());
                detailItems.add(item);
            }
        }
@@ -1380,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));
@@ -1486,6 +1502,7 @@
        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);
@@ -1636,6 +1653,22 @@
        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) {
        double R = 6371;
        double dLat = Math.toRadians(lat2 - lat1);
@@ -1697,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
@@ -1711,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);
@@ -1804,7 +1862,7 @@
    }
    @Override
    public Map<String, Integer> getActiveOrderCount(Integer driverId) {
    public DriverActiveOrderCountVO getActiveOrderCount(Integer driverId) {
        // å·²æŠ¢å•(status=3)数量
        Long grabbed = ordersMapper.selectCount(new QueryWrapper<Orders>().lambda()
                .eq(Orders::getAcceptDriver, driverId)
@@ -1815,10 +1873,121 @@
                .eq(Orders::getAcceptDriver, driverId)
                .eq(Orders::getStatus, Constants.OrderStatus.delivering.getStatus())
                .eq(Orders::getDeleted, Constants.ZERO));
        Map<String, Integer> result = new HashMap<>();
        result.put("grabbedCount", grabbed != null ? grabbed.intValue() : 0);
        result.put("deliveringCount", delivering != null ? delivering.intValue() : 0);
        return result;
        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/OrdersServiceImpl.java
@@ -1526,7 +1526,7 @@
        wrapper.eq(status != null, Orders::getStatus, status)
                .in(statusList != null && !Constants.equalsInteger(combinedStatus, Constants.SEVEN), Orders::getStatus, statusList)
                .orderByDesc(Orders::getCreateTime);
                .orderByDesc(Orders::getId);
        IPage<Orders> orderPage = ordersMapper.selectJoinPage(p, Orders.class, wrapper);
        List<MyOrderVO> voList = new ArrayList<>();
@@ -1950,7 +1950,8 @@
                    .set(Orders::getTakeLocation, order.getDepositLocation())
                    .set(Orders::getTakeLocationRemark, order.getDepositLocationRemark())
                    .set(Orders::getTakeLat, order.getDepositLat())
                    .set(Orders::getTakeLgt, order.getDepositLgt());
                    .set(Orders::getTakeLgt, order.getDepositLgt())
                    .set(Orders::getExpectedTakeTime, new Date());
            if (Constants.equalsInteger(status, Constants.OrderStatus.accepted.getStatus())) {
                updateWrapper.lambda()
                        .set(Orders::getAcceptDriver, null)
@@ -2665,7 +2666,7 @@
            if (!shopId.equals(order.getDepositShopId())) {
                throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "该订单不属于当前门店,无法核销");
            }
            order.setStatus(Constants.OrderStatus.deposited.getStatus());
            order.setStatus(Constants.equalsInteger(order.getType(),Constants.ZERO)?Constants.OrderStatus.arrived.getStatus():Constants.OrderStatus.deposited.getStatus());
            order.setDepositTime(now);
            // é‡Šæ”¾å½“前核销码,生成新的核销码供取件时使用
            String verifyCode = order.getMemberVerifyCode();
@@ -3544,26 +3545,20 @@
    /**
     * å°±åœ°å¯„存逾期天数计算
     * è¿‡äº†é¢„计取件时间当天的24:00(次日00:00)后才算第一天
     * è¿‡äº†é¢„计取件日期的次日(只比较年月日)后开始计逾期天数
     */
    private int calcLocalOverdueDays(Date now, Date expectedTakeTime) {
        if (expectedTakeTime == null || !now.after(expectedTakeTime)) {
        if (expectedTakeTime == null) {
            return 0;
        }
        // åŸºå‡†æ—¶é—´ = é¢„计取件日期的次日 00:00(即当天24:00)
        // åªå–年月日
        Calendar baseCal = Calendar.getInstance();
        baseCal.setTime(expectedTakeTime);
        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);
@@ -3571,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);
@@ -3612,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
server/services/src/main/java/com/doumee/service/business/impl/ShopInfoServiceImpl.java
@@ -757,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;
    }
@@ -906,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;
        }
@@ -919,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;
    }
server/web/src/main/java/com/doumee/api/web/DriverInfoApi.java
@@ -12,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;
@@ -30,14 +33,13 @@
import javax.validation.Valid;
import java.util.List;
import java.util.Map;
/**
 * å¸æœºéªŒè¯ç ç™»å½•接口
 * @author rk
 * @date 2026/04/08
 */
@Api(tags = "司机验证码登录")
@Api(tags = "司机业务接口")
@Trace(exclude = true)
@RestController
@RequestMapping("/web/driverInfo")
@@ -158,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)
@@ -248,8 +250,30 @@
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", dataType = "String", name = "token", value = "用户token值", required = true)
    })
    public ApiResponse<Map<String, Integer>> activeOrderCount() {
    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/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/ShopInfoApi.java
@@ -33,7 +33,7 @@
 * @author rk
 * @date 2026/04/10
 */
@Api(tags = "门店入驻")
@Api(tags = "门店业务接口")
@RestController
@RequestMapping("/web/shopInfo")
public class ShopInfoApi extends ApiController {
@@ -60,6 +60,7 @@
        return ApiResponse.success(shopInfoService.getMyShop(this.getMemberId()));
    }
    @ApiOperation("附近门店分页列表")
    @PostMapping("/nearby")
    public ApiResponse<PageData<ShopNearbyVO>> nearby(@RequestBody @Validated PageWrap<ShopNearbyDTO> pageWrap) {
@@ -80,6 +81,16 @@
    })
    public ApiResponse<ShopCenterVO> getShopInfo() {
        return ApiResponse.success("查询成功", shopInfoService.getShopCenterInfo(getShopId()));
    }
    @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
@@ -105,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