From c9f0611f690b3fb0cb120f5b6799f94977d3f129 Mon Sep 17 00:00:00 2001
From: rk <94314517@qq.com>
Date: 星期一, 20 四月 2026 11:34:23 +0800
Subject: [PATCH] 代码生成

---
 server/services/src/main/java/com/doumee/service/business/impl/DriverInfoServiceImpl.java |  649 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 642 insertions(+), 7 deletions(-)

diff --git a/server/services/src/main/java/com/doumee/service/business/impl/DriverInfoServiceImpl.java b/server/services/src/main/java/com/doumee/service/business/impl/DriverInfoServiceImpl.java
index 11ec88f..8110145 100644
--- a/server/services/src/main/java/com/doumee/service/business/impl/DriverInfoServiceImpl.java
+++ b/server/services/src/main/java/com/doumee/service/business/impl/DriverInfoServiceImpl.java
@@ -17,19 +17,33 @@
 import com.doumee.dao.business.MultifileMapper;
 import com.doumee.dao.business.SmsrecordMapper;
 import com.doumee.dao.business.CategoryMapper;
+import com.doumee.dao.business.OrdersMapper;
+import com.doumee.dao.business.OrdersDetailMapper;
+import com.doumee.dao.business.RevenueMapper;
 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.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.vo.AccountResponse;
+import com.doumee.dao.vo.DriverCenterVO;
+import com.doumee.dao.vo.DriverGrabOrderVO;
+import com.doumee.dao.vo.DriverOrderDetailVO;
 import com.doumee.dao.dto.AuditDTO;
 import com.doumee.dao.dto.ChangeStatusDTO;
 import com.doumee.dao.dto.DriverLoginRequest;
 import com.doumee.dao.dto.DriverRegisterRequest;
 import com.doumee.dao.dto.DriverVerifyRequest;
-import com.doumee.service.business.AliSmsService;
+import com.doumee.dao.dto.DriverActiveOrderDTO;
+import com.doumee.dao.dto.DriverGrabOrderDTO;
+import com.doumee.core.utils.aliyun.AliSmsService;
 import com.doumee.service.business.DriverInfoService;
 import com.alibaba.fastjson.JSONObject;
 import com.github.yulichang.wrapper.MPJLambdaWrapper;
@@ -41,10 +55,8 @@
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.CollectionUtils;
 
-import java.util.Calendar;
-import java.util.Date;
-import java.util.List;
-import java.util.Objects;
+import java.util.*;
+import java.util.stream.Collectors;
 
 /**
  * 鍙告満娉ㄥ唽淇℃伅Service瀹炵幇
@@ -79,7 +91,22 @@
     private CategoryMapper categoryMapper;
 
     @Autowired
+    private OrdersMapper ordersMapper;
+
+    @Autowired
+    private RevenueMapper revenueMapper;
+
+    @Autowired
+    private OrdersDetailMapper ordersDetailMapper;
+
+    @Autowired
     private SystemDictDataBiz systemDictDataBiz;
+
+    @Autowired
+    private OrderLogMapper orderLogMapper;
+
+    @Autowired
+    private OperationConfigBiz operationConfigBiz;
 
     @Override
     public Integer create(DriverInfo driverInfo) {
@@ -289,7 +316,7 @@
             member.setCreateTime(now);
             member.setUpdateTime(now);
             member.setTelephone(telephone);
-            member.setNickName(telephone);
+            member.setNickName(telephone.substring(0, 3) + "****" + telephone.substring(7));
             member.setName(telephone);
             member.setUserType(Constants.ONE);
             member.setBusinessStatus(Constants.ZERO);
@@ -556,7 +583,7 @@
         String imgPrefix = "";
         try {
             imgPrefix = systemDictDataBiz.queryByCode(Constants.OSS, Constants.RESOURCE_PATH).getCode()
-                    + systemDictDataBiz.queryByCode(Constants.SYSTEM, Constants.DRIVER_FILES).getCode();
+                    + systemDictDataBiz.queryByCode(Constants.OSS, Constants.DRIVER_FILES).getCode();
         } catch (Exception e) {
             // 鏈厤缃椂蹇界暐
         }
@@ -630,4 +657,612 @@
         }
     }
 
+    @Override
+    public void updateAcceptingStatus(Integer memberId, Integer status) {
+        if (!Constants.ZERO.equals(status) && !Constants.ONE.equals(status)) {
+            throw new BusinessException(ResponseStatus.BAD_REQUEST);
+        }
+        DriverInfo driver = driverInfoMapper.selectOne(new QueryWrapper<DriverInfo>().lambda()
+                .eq(DriverInfo::getMemberId, memberId)
+                .eq(DriverInfo::getDeleted, Constants.ZERO)
+                .last("limit 1"));
+        if (driver == null) {
+            throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "鍙告満淇℃伅涓嶅瓨鍦�");
+        }
+        if (!Constants.THREE.equals(driver.getAuditStatus())) {
+            throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "璇峰厛瀹屾垚鎶奸噾鏀粯");
+        }
+        driverInfoMapper.update(new UpdateWrapper<DriverInfo>().lambda()
+                .set(DriverInfo::getAcceptingStatus, status)
+                .eq(DriverInfo::getId, driver.getId()));
+    }
+
+    @Override
+    public void updateLocation(Integer memberId, Double longitude, Double latitude) {
+        if (longitude == null || latitude == null) {
+            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "缁忕含搴︿笉鑳戒负绌�");
+        }
+        DriverInfo driver = driverInfoMapper.selectOne(new QueryWrapper<DriverInfo>().lambda()
+                .eq(DriverInfo::getMemberId, memberId)
+                .eq(DriverInfo::getDeleted, Constants.ZERO)
+                .last("limit 1"));
+        if (driver == null) {
+            throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "鍙告満淇℃伅涓嶅瓨鍦�");
+        }
+        driverInfoMapper.update(new UpdateWrapper<DriverInfo>().lambda()
+                .set(DriverInfo::getLongitude, longitude)
+                .set(DriverInfo::getLatitude, latitude)
+                .eq(DriverInfo::getId, driver.getId()));
+    }
+
+    @Override
+    public DriverCenterVO getDriverCenterInfo(Integer memberId) {
+        DriverInfo driver = driverInfoMapper.selectOne(new QueryWrapper<DriverInfo>().lambda()
+                .eq(DriverInfo::getMemberId, memberId)
+                .eq(DriverInfo::getDeleted, Constants.ZERO)
+                .last("limit 1"));
+        if (driver == null) {
+            throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "鍙告満淇℃伅涓嶅瓨鍦�");
+        }
+        DriverCenterVO vo = new DriverCenterVO();
+        vo.setName(driver.getName());
+        vo.setImgUrl(driver.getImgurl());
+        vo.setCarCode(driver.getCarCode());
+        vo.setScore(driver.getScore() != null ? driver.getScore().toPlainString() : "0");
+        vo.setBalance(driver.getBalance() != null ? driver.getBalance() : 0L);
+
+        // 澶村儚鍏ㄨ矾寰�
+        if (StringUtils.isNotBlank(driver.getImgurl())) {
+            String imgPrefix = systemDictDataBiz.queryByCode(Constants.OSS, Constants.RESOURCE_PATH).getCode()
+                    + systemDictDataBiz.queryByCode(Constants.OSS, Constants.MEMBER_FILES).getCode();
+            vo.setFullImgUrl(imgPrefix + driver.getImgurl());
+        }
+
+        // 浠婃棩棰勮浣i噾锛歳evenue琛ㄤ腑浠婂ぉ鐨勬敹鍏ヨ褰曢噾棰濅箣鍜�
+        Date now = new Date();
+        Calendar cal = Calendar.getInstance();
+        cal.setTime(now);
+        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();
+
+        QueryWrapper<Revenue> revenueWrapper = new QueryWrapper<>();
+        revenueWrapper.lambda()
+                .eq(Revenue::getMemberId, memberId)
+                .eq(Revenue::getMemberType, Constants.ONE)
+                .eq(Revenue::getOptType, Constants.ONE)
+                .eq(Revenue::getDeleted, Constants.ZERO)
+                .ge(Revenue::getCreateTime, todayStart);
+        revenueWrapper.select("IFNULL(SUM(AMOUNT),0) as amount");
+        Revenue sumResult = revenueMapper.selectOne(revenueWrapper);
+        vo.setTodayCommission(sumResult != null && sumResult.getAmount() != null ? sumResult.getAmount() : 0L);
+
+        // 浠婃棩鎺ュ崟鏁帮細浠婂ぉ瀹屾垚鐨勮鍗曟暟锛坅cceptDriver=鍙告満涓婚敭锛岀姸鎬�=宸插畬鎴愶級
+        Long todayOrderCount = ordersMapper.selectCount(new QueryWrapper<Orders>().lambda()
+                .eq(Orders::getAcceptDriver, driver.getId())
+                .eq(Orders::getDeleted, Constants.ZERO)
+                .ge(Orders::getFinishTime, todayStart));
+        vo.setTodayOrderCount(todayOrderCount.intValue());
+
+        // 寰呭彇璐э紙宸叉帴鍗�=3锛�
+        Long waitPickCount = ordersMapper.selectCount(new QueryWrapper<Orders>().lambda()
+                .eq(Orders::getAcceptDriver, driver.getId())
+                .eq(Orders::getDeleted, Constants.ZERO)
+                .eq(Orders::getStatus, Constants.OrderStatus.accepted.getStatus()));
+        vo.setWaitPickCount(waitPickCount.intValue());
+
+        // 寰呴厤閫侊紙娲鹃�佷腑=4锛�
+        Long waitDeliverCount = ordersMapper.selectCount(new QueryWrapper<Orders>().lambda()
+                .eq(Orders::getAcceptDriver, driver.getId())
+                .eq(Orders::getDeleted, Constants.ZERO)
+                .eq(Orders::getStatus, Constants.OrderStatus.delivering.getStatus()));
+        vo.setWaitDeliverCount(waitDeliverCount.intValue());
+
+        return vo;
+    }
+
+    @Override
+    public PageData<DriverGrabOrderVO> grabOrderHall(Integer memberId, PageWrap<DriverGrabOrderDTO> pageWrap) {
+        DriverGrabOrderDTO dto = pageWrap.getModel();
+
+        // 1. 鑾峰彇鍙告満瀹氫綅
+        DriverInfo driver = driverInfoMapper.selectOne(new QueryWrapper<DriverInfo>().lambda()
+                .eq(DriverInfo::getMemberId, memberId)
+                .eq(DriverInfo::getDeleted, Constants.ZERO)
+                .last("limit 1"));
+        if (driver == null || driver.getLatitude() == null || driver.getLongitude() == null) {
+            throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "鍙告満浣嶇疆淇℃伅缂哄け锛岃鍏堝紑鍚畾浣�");
+        }
+        // 鏍¢獙鍙告満宸叉敮浠樻娂閲�
+        if (!Constants.THREE.equals(driver.getAuditStatus())) {
+            throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "璇峰厛瀹屾垚鎶奸噾鏀粯");
+        }
+        double driverLat = driver.getLatitude();
+        double driverLng = driver.getLongitude();
+
+        // 2. 棰勬煡鐗╁搧绛夌骇瀵瑰簲鐨勭墿鍝佸垎绫籌D
+        List<Integer> goodTypeIds = null;
+        if (dto != null && dto.getGradeId() != null) {
+            List<Category> cats = categoryMapper.selectList(new QueryWrapper<Category>().lambda()
+                    .eq(Category::getDeleted, Constants.ZERO)
+                    .eq(Category::getType, Constants.TWO)
+                    .eq(Category::getRelationId, dto.getGradeId()));
+            if (cats.isEmpty()) {
+                return emptyPage(pageWrap);
+            }
+            goodTypeIds = cats.stream().map(Category::getId).collect(Collectors.toList());
+        }
+
+        // 3. Haversine SQL鍏紡锛氬徃鏈哄埌瀛樹欢闂ㄥ簵璺濈(km)锛屼娇鐢∣rders鑷甫鍧愭爣
+        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))))";
+
+        // 4. 鏋勯�燤PJ鏌ヨ
+        IPage<Orders> page = 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")
+                // 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")
+                .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");
+
+        // 鏍稿績鏉′欢
+        wrapper.eq(Orders::getType, Constants.ONE)
+                .eq(Orders::getStatus, Constants.TWO)
+                .eq(Orders::getDeleted, Constants.ZERO);
+
+        // 鍙告満绾у埆 >= 璁㈠崟鐗╁搧绾у埆
+        if (driver.getDriverLevel() != null) {
+            wrapper.apply("t.GOOD_LEVEL <= {0}", driver.getDriverLevel());
+        }
+
+        // 鍔犳�� OR 鍦ㄩ厤閫佽寖鍥村唴
+        wrapper.and(w -> w
+                .eq(Orders::getIsUrgent, Constants.ONE)
+                .or()
+                .apply(depositDist + " <= s1.delivery_area"));
+
+        // 鐢ㄦ埛璺濈杩囨护
+        if (dto != null && dto.getDistance() != null && dto.getDistance() > 0) {
+            double maxKm = dto.getDistance() / 1000.0;
+            wrapper.apply(depositDist + " <= {0}", maxKm);
+        }
+
+        // 鐗╁搧绛夌骇杩囨护
+        if (goodTypeIds != null && !goodTypeIds.isEmpty()) {
+            wrapper.in(Orders::getGoodType, goodTypeIds);
+        }
+
+        // 鎺掑簭
+        Integer sortType = (dto != null) ? dto.getSortType() : null;
+        if (sortType != null && sortType == Constants.TWO) {
+            wrapper.last("ORDER BY " + depositDist + " ASC");
+        } else {
+            wrapper.orderByDesc(Orders::getCreateTime);
+        }
+
+        IPage<Orders> result = ordersMapper.selectJoinPage(page, Orders.class, wrapper);
+
+        // 5. 鎵归噺鏌ヨ鐗╁搧鏄庣粏
+        List<Integer> orderIds = result.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);
+            }
+        }
+
+        // 6. 鏋勫缓VO锛堜娇鐢ㄥ叡鐢ㄦ柟娉曪級
+        Date now = new Date();
+        List<DriverGrabOrderVO> voList = new ArrayList<>();
+        for (Orders order : result.getRecords()) {
+            voList.add(buildDriverOrderVO(order, driverLat, driverLng, true, now, detailMap));
+        }
+
+        // 7. 鎵嬪姩鍒嗛〉
+        PageData<DriverGrabOrderVO> pageData = new PageData<>(result.getCurrent(), result.getSize());
+        pageData.setTotal(result.getTotal());
+        pageData.setRecords(voList);
+        return pageData;
+    }
+
+    @Override
+    public List<DriverGrabOrderVO> activeOrders(Integer memberId, DriverActiveOrderDTO dto) {
+        if (dto == null || dto.getStatus() == null ||
+                (!Constants.equalsInteger(dto.getStatus(), Constants.THREE) && !Constants.equalsInteger(dto.getStatus(), Constants.FOUR))) {
+            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "璁㈠崟鐘舵�佸彧鑳戒负3(宸叉姠鍗�)鎴�4(娲鹃�佷腑)");
+        }
+
+        // 鑾峰彇鍙告満淇℃伅
+        DriverInfo driver = driverInfoMapper.selectOne(new QueryWrapper<DriverInfo>().lambda()
+                .eq(DriverInfo::getMemberId, memberId)
+                .eq(DriverInfo::getDeleted, Constants.ZERO)
+                .last("limit 1"));
+        if (driver == null) {
+            throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "鍙告満淇℃伅涓嶅瓨鍦�");
+        }
+
+        boolean needDepositDist = Constants.equalsInteger(dto.getStatus(), Constants.THREE);
+        Double driverLat = driver.getLatitude();
+        Double driverLng = driver.getLongitude();
+
+        // MPJ鏌ヨ
+        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")
+                .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(Orders::getDeleted, Constants.ZERO)
+                .orderByAsc(Orders::getAcceptTime);
+
+        List<Orders> ordersList = ordersMapper.selectJoinList(Orders.class, wrapper);
+
+        // 鎵归噺鏌ョ墿鍝佹槑缁�
+        List<Integer> orderIds = ordersList.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);
+            }
+        }
+
+        // 鏋勫缓VO锛堜娇鐢ㄥ叡鐢ㄦ柟娉曪級
+        Date now = new Date();
+        List<DriverGrabOrderVO> voList = new ArrayList<>();
+        for (Orders order : ordersList) {
+            voList.add(buildDriverOrderVO(order, driverLat, driverLng, needDepositDist, now, detailMap));
+        }
+
+        return voList;
+    }
+
+    @Override
+    public DriverOrderDetailVO driverOrderDetail(Integer driverId, Integer orderId) {
+        // 鏌ヨ璁㈠崟锛圡PJ JOIN 闂ㄥ簵鍚嶇О+鍒嗙被锛岃窛绂昏绠椾娇鐢∣rders鑷甫鍧愭爣锛�
+        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")
+                .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::getId, orderId)
+                .eq(Orders::getDeleted, Constants.ZERO);
+        Orders order = ordersMapper.selectJoinOne(Orders.class, wrapper);
+        if (order == null) {
+            throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "璁㈠崟涓嶅瓨鍦�");
+        }
+
+        // 鏉冮檺鏍¢獙锛氭姠鍗曞ぇ鍘呭彲瑙�(status=2, type=1) 鎴� 宸叉姠鍗�/娲鹃�佷腑(acceptDriver=driverId)
+        boolean canView = false;
+        if (Constants.equalsInteger(order.getStatus(), Constants.TWO) && Constants.equalsInteger(order.getType(), Constants.ONE)) {
+            canView = true;
+        }
+        if (driverId != null && driverId.equals(order.getAcceptDriver())) {
+            canView = true;
+        }
+        if (!canView) {
+            throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "鏃犳潈鏌ョ湅璇ヨ鍗�");
+        }
+
+        // 鑾峰彇鍙告満浣嶇疆锛堢敤浜庤窛绂昏绠楋級
+        Double driverLat = null;
+        Double driverLng = null;
+        if (driverId != null) {
+            DriverInfo driver = driverInfoMapper.selectById(driverId);
+            if (driver != null) {
+                driverLat = driver.getLatitude();
+                driverLng = driver.getLongitude();
+            }
+        }
+
+        // 鐗╁搧鏄庣粏
+        List<OrdersDetail> details = ordersDetailMapper.selectList(
+                new QueryWrapper<OrdersDetail>().lambda()
+                        .eq(OrdersDetail::getOrderId, orderId));
+        Map<Integer, List<OrdersDetail>> detailMap = new HashMap<>();
+        for (OrdersDetail d : details) {
+            detailMap.computeIfAbsent(d.getOrderId(), k -> new ArrayList<>()).add(d);
+        }
+
+        // 浣跨敤鍏辩敤鏂规硶鏋勫缓鍩虹VO瀛楁
+        Date now = new Date();
+        DriverGrabOrderVO base = buildDriverOrderVO(order, driverLat, driverLng, true, now, detailMap);
+
+        // 鏋勫缓璇︽儏VO
+        DriverOrderDetailVO vo = new DriverOrderDetailVO();
+        vo.setId(base.getId());
+        vo.setCode(base.getCode());
+        vo.setRemainMinutes(base.getRemainMinutes());
+        vo.setIsUrgent(base.getIsUrgent());
+        vo.setDepositShopName(base.getDepositShopName());
+        vo.setDepositShopAddress(base.getDepositShopAddress());
+        vo.setDepositDistance(base.getDepositDistance());
+        vo.setTakeName(base.getTakeName());
+        vo.setTakeDistance(base.getTakeDistance());
+        vo.setContactPhone(base.getContactPhone());
+        vo.setDriverFee(base.getDriverFee());
+        vo.setUrgentAmount(base.getUrgentAmount());
+        vo.setIsValuable(base.getIsValuable());
+
+        // 鐗╁搧鏄庣粏锛堣浆鎹㈢被鍨嬶級
+        List<DriverOrderDetailVO.OrderItem> detailItems = new ArrayList<>();
+        if (base.getItems() != null) {
+            for (DriverGrabOrderVO.OrderItem src : base.getItems()) {
+                DriverOrderDetailVO.OrderItem item = new DriverOrderDetailVO.OrderItem();
+                item.setName(src.getName());
+                item.setQuantity(src.getQuantity());
+                detailItems.add(item);
+            }
+        }
+        vo.setItems(detailItems);
+
+        // 璇︽儏鐗规湁瀛楁
+        vo.setStatus(order.getStatus());
+        vo.setStatusDesc(getStatusDesc(order.getStatus()));
+
+        // 瀹㈡埛淇℃伅
+        String customerInfo = "";
+        if (StringUtils.isNotBlank(order.getTakeUser())) {
+            customerInfo = order.getTakeUser();
+        }
+        if (StringUtils.isNotBlank(order.getTakePhone()) && order.getTakePhone().length() >= 4) {
+            customerInfo += "锛堟墜鏈哄熬鍙�" + order.getTakePhone().substring(order.getTakePhone().length() - 4) + "锛�";
+        }
+        vo.setCustomerInfo(customerInfo);
+
+        // 瀵艰埅缁忕含搴︼紙浣跨敤Orders鑷甫鍧愭爣锛�
+        if (Constants.equalsInteger(order.getStatus(), Constants.TWO)) {
+            if (order.getDepositLgt() != null && order.getDepositLat() != null) {
+                vo.setNavigateLat(order.getDepositLgt().doubleValue());
+                vo.setNavigateLng(order.getDepositLat().doubleValue());
+            }
+        } else {
+            if (order.getTakeLgt() != null && order.getTakeLat() != null) {
+                vo.setNavigateLat(order.getTakeLgt().doubleValue());
+                vo.setNavigateLng(order.getTakeLat().doubleValue());
+            }
+        }
+
+        // 涓嬪崟闄勪欢鍥剧墖
+        String imgPrefix = systemDictDataBiz.queryByCode(Constants.OSS, Constants.RESOURCE_PATH).getCode()
+                + systemDictDataBiz.queryByCode(Constants.OSS, Constants.MEMBER_FILES).getCode();
+        vo.setOrderImages(getFileUrls(orderId, Constants.FileType.ORDER_FILE.getKey(), imgPrefix));
+
+        return vo;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void cancelOrder(Integer driverId, Integer orderId, String reason) {
+        // 1. 鏌ヨ鍙告満淇℃伅
+        DriverInfo driver = driverInfoMapper.selectById(driverId);
+        if (driver == null) {
+            throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "鍙告満淇℃伅涓嶅瓨鍦�");
+        }
+
+        // 2. 鏍¢獙璁㈠崟
+        Orders order = ordersMapper.selectById(orderId);
+        if (order == null || Constants.ONE.equals(order.getDeleted())) {
+            throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "璁㈠崟涓嶅瓨鍦�");
+        }
+        if (!Constants.ONE.equals(order.getType())) {
+            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "浠呭紓鍦板瘎瀛樿鍗曞彲鍙栨秷");
+        }
+        if (!Constants.TWO.equals(order.getStatus())) {
+            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "褰撳墠璁㈠崟鐘舵�佷笉鍏佽鍙栨秷");
+        }
+        if (!driverId.equals(order.getAcceptDriver())) {
+            throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "鏃犳潈鎿嶄綔璇ヨ鍗�");
+        }
+
+        // 3. 姣忔棩鍙栨秷娆℃暟闄愬埗
+        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.ORDER_LOG_CANCEL)
+                .eq(OrderLog::getOptUserType, Constants.ONE)
+                .ge(OrderLog::getCreateTime, todayStart));
+        if (todayCancelCount != null && todayCancelCount >= limit) {
+            throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "浠婃棩鍙栨秷娆℃暟宸茶揪涓婇檺");
+        }
+
+        // 4. 閲嶇疆璁㈠崟鍙告満瀛楁锛堜繚鎸乻tatus=2锛岄噴鏀惧洖鎶㈠崟澶у巺锛�
+        ordersMapper.update(new UpdateWrapper<Orders>().lambda()
+                .set(Orders::getAcceptDriver, null)
+                .set(Orders::getAcceptTime, null)
+                .set(Orders::getAcceptType, null)
+                .eq(Orders::getId, orderId));
+
+        // 5. 鍐欏叆鍙栨秷鏃ュ織
+        OrderLog log = new OrderLog();
+        log.setOrderId(orderId);
+        log.setTitle("鍙告満鍙栨秷璁㈠崟");
+        log.setLogInfo(StringUtils.isNotBlank(reason) ? reason : "鍙告満鍙栨秷鎺ュ崟");
+        log.setObjType(Constants.ORDER_LOG_CANCEL);
+        log.setOptUserId(driver.getMemberId());
+        log.setOptUserType(Constants.ONE);
+        log.setOrderStatus(order.getStatus());
+        log.setCreateTime(new Date());
+        log.setDeleted(Constants.ZERO);
+        orderLogMapper.insert(log);
+    }
+
+    private List<String> getFileUrls(Integer orderId, int objType, String prefix) {
+        List<Multifile> files = multifileMapper.selectList(
+                new QueryWrapper<Multifile>().lambda()
+                        .eq(Multifile::getObjId, orderId)
+                        .eq(Multifile::getObjType, objType)
+                        .eq(Multifile::getIsdeleted, Constants.ZERO)
+                        .orderByAsc(Multifile::getSortnum));
+        List<String> urls = new ArrayList<>();
+        if (files != null) {
+            for (Multifile f : files) {
+                if (StringUtils.isNotBlank(f.getFileurl())) {
+                    urls.add(prefix + f.getFileurl());
+                }
+            }
+        }
+        return urls;
+    }
+
+    private String getStatusDesc(Integer status) {
+        if (status == null) return "";
+        switch (status) {
+            case 0: return "寰呮敮浠�";
+            case 1: return "寰呭瘎瀛�";
+            case 2: return "宸插瘎瀛�";
+            case 3: return "宸叉帴鍗�";
+            case 4: return "娲鹃�佷腑";
+            case 5: return "寰呭彇浠�";
+            case 7: return "宸插畬鎴�";
+            case 96: return "璁㈠崟鍏抽棴";
+            case 98: return "鍙栨秷涓�";
+            case 99: return "宸插彇娑�";
+            default: return "";
+        }
+    }
+
+    private String formatDistance(double km) {
+        if (km < 1) {
+            return Math.round(km * 1000) + "m";
+        }
+        return String.format("%.1fkm", km);
+    }
+
+    private double haversine(double lat1, double lng1, double lat2, double lng2) {
+        double R = 6371;
+        double dLat = Math.toRadians(lat2 - lat1);
+        double dLng = Math.toRadians(lng2 - lng1);
+        double a = Math.sin(dLat / 2) * Math.sin(dLat / 2)
+                + Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2))
+                * Math.sin(dLng / 2) * Math.sin(dLng / 2);
+        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
+        return R * c;
+    }
+
+    private <T> PageData<T> emptyPage(PageWrap<?> pageWrap) {
+        PageData<T> pd = new PageData<>(pageWrap.getPage(), pageWrap.getCapacity());
+        pd.setTotal(0);
+        pd.setRecords(new ArrayList<>());
+        return pd;
+    }
+
+    /**
+     * 鏋勫缓鍙告満绔鍗曞垪琛ㄥ叡鐢╒O锛堜娇鐢∣rders鑷甫鍧愭爣璁$畻璺濈锛�
+     *
+     * @param order          璁㈠崟瀹炰綋锛圡PJ宸插~鍏卍epositShopName绛夊叧鑱斿瓧娈碉級
+     * @param driverLat      鍙告満绾害
+     * @param driverLng      鍙告満缁忓害
+     * @param needDepositDist 鏄惁闇�瑕佽绠楄窛瀛樹欢闂ㄥ簵璺濈
+     * @param now            褰撳墠鏃堕棿
+     * @param detailMap      璁㈠崟鐗╁搧鏄庣粏Map锛坥rderId 鈫� detailList锛�
+     * @return DriverGrabOrderVO
+     */
+    private DriverGrabOrderVO buildDriverOrderVO(Orders order, Double driverLat, Double driverLng,
+            boolean needDepositDist, Date now, Map<Integer, List<OrdersDetail>> detailMap) {
+        DriverGrabOrderVO vo = new DriverGrabOrderVO();
+        vo.setId(order.getId());
+        vo.setCode(order.getCode());
+
+        // 鍓╀綑鏃堕暱
+        if (order.getEstimatedDeliveryTime() != null) {
+            long diffMs = order.getEstimatedDeliveryTime().getTime() - now.getTime();
+            vo.setRemainMinutes(diffMs > 0 ? diffMs / (60 * 1000) : 0L);
+        } else {
+            vo.setRemainMinutes(0L);
+        }
+
+        vo.setIsUrgent(order.getIsUrgent());
+        vo.setDriverFee(order.getDriverFee());
+        vo.setUrgentAmount(order.getUrgentAmount());
+
+        // 瀛樹欢闂ㄥ簵锛堜娇鐢∣rders鑷甫鍧愭爣锛�
+        vo.setDepositShopName(order.getDepositShopName());
+        vo.setDepositShopAddress(order.getDepositShopAddress());
+        if (needDepositDist && driverLat != null && driverLng != null
+                && order.getDepositLgt() != null && order.getDepositLat() != null) {
+            double distKm = haversine(driverLat, driverLng,
+                    order.getDepositLgt().doubleValue(), order.getDepositLat().doubleValue());
+            vo.setDepositDistance(formatDistance(distKm));
+        }
+
+        // 鍙栦欢淇℃伅 + 鑱旂郴鐢佃瘽锛堜娇鐢∣rders鑷甫鍧愭爣锛�
+        boolean hasTakeShop = order.getTakeShopId() != null && StringUtils.isNotBlank(order.getTakeShopName());
+        if (hasTakeShop) {
+            vo.setTakeName(order.getTakeShopName());
+            vo.setContactPhone(order.getTakeShopLinkPhone());
+        } else {
+            vo.setTakeName(order.getTakeLocation());
+            vo.setContactPhone(order.getTakePhone());
+        }
+        if (driverLat != null && driverLng != null
+                && order.getTakeLgt() != null && order.getTakeLat() != null) {
+            double takeDist = haversine(driverLat, driverLng,
+                    order.getTakeLgt().doubleValue(), order.getTakeLat().doubleValue());
+            vo.setTakeDistance(formatDistance(takeDist));
+        }
+
+        // 璐甸噸鐗╁搧
+        vo.setIsValuable("1".equals(order.getC2OtherField()));
+
+        // 鐗╁搧鏄庣粏
+        List<OrdersDetail> details = detailMap.getOrDefault(order.getId(), Collections.emptyList());
+        List<DriverGrabOrderVO.OrderItem> items = new ArrayList<>();
+        for (OrdersDetail detail : details) {
+            DriverGrabOrderVO.OrderItem item = new DriverGrabOrderVO.OrderItem();
+            item.setName(detail.getLuggageName());
+            item.setQuantity(detail.getNum());
+            items.add(item);
+        }
+        vo.setItems(items);
+
+        return vo;
+    }
+
 }

--
Gitblit v1.9.3