rk
4 天以前 21bd711a3756850299b443848181ee60708c6377
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.dao.business.model.Category;
import com.doumee.dao.business.model.DriverInfo;
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;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
@@ -38,10 +52,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实现
@@ -74,6 +86,18 @@
    @Autowired
    private CategoryMapper categoryMapper;
    @Autowired
    private OrdersMapper ordersMapper;
    @Autowired
    private RevenueMapper revenueMapper;
    @Autowired
    private OrdersDetailMapper ordersDetailMapper;
    @Autowired
    private SystemDictDataBiz systemDictDataBiz;
    @Override
    public Integer create(DriverInfo driverInfo) {
@@ -139,71 +163,41 @@
    @Override
    public PageData<DriverInfo> findPage(PageWrap<DriverInfo> pageWrap) {
        IPage<DriverInfo> page = new Page<>(pageWrap.getPage(), pageWrap.getCapacity());
        QueryWrapper<DriverInfo> queryWrapper = new QueryWrapper<>();
        MPJLambdaWrapper<DriverInfo> queryWrapper = new MPJLambdaWrapper<>();
        Utils.MP.blankToNull(pageWrap.getModel());
        pageWrap.getModel().setDeleted(Constants.ZERO);
        if (pageWrap.getModel().getId() != null) {
            queryWrapper.lambda().eq(DriverInfo::getId, pageWrap.getModel().getId());
        // 司机姓名/手机号(关键字模糊查询)
        if (StringUtils.isNotBlank(pageWrap.getModel().getKeyword())) {
            queryWrapper.and(w -> w
                    .like(DriverInfo::getName, pageWrap.getModel().getKeyword())
                    .or()
                    .like(DriverInfo::getTelephone, pageWrap.getModel().getKeyword()));
        }
        if (pageWrap.getModel().getDeleted() != null) {
            queryWrapper.lambda().eq(DriverInfo::getDeleted, pageWrap.getModel().getDeleted());
        // 车牌号
        if (StringUtils.isNotBlank(pageWrap.getModel().getCarCode())) {
            queryWrapper.like(DriverInfo::getCarCode, pageWrap.getModel().getCarCode());
        }
        if (pageWrap.getModel().getCreateUser() != null) {
            queryWrapper.lambda().eq(DriverInfo::getCreateUser, pageWrap.getModel().getCreateUser());
        }
        if (pageWrap.getModel().getCreateTime() != null) {
            queryWrapper.lambda().ge(DriverInfo::getCreateTime, Utils.Date.getStart(pageWrap.getModel().getCreateTime()));
            queryWrapper.lambda().le(DriverInfo::getCreateTime, Utils.Date.getEnd(pageWrap.getModel().getCreateTime()));
        }
        if (pageWrap.getModel().getUpdateUser() != null) {
            queryWrapper.lambda().eq(DriverInfo::getUpdateUser, pageWrap.getModel().getUpdateUser());
        }
        if (pageWrap.getModel().getUpdateTime() != null) {
            queryWrapper.lambda().ge(DriverInfo::getUpdateTime, Utils.Date.getStart(pageWrap.getModel().getUpdateTime()));
            queryWrapper.lambda().le(DriverInfo::getUpdateTime, Utils.Date.getEnd(pageWrap.getModel().getUpdateTime()));
        }
        if (pageWrap.getModel().getRemark() != null) {
            queryWrapper.lambda().eq(DriverInfo::getRemark, pageWrap.getModel().getRemark());
        }
        if (pageWrap.getModel().getName() != null) {
            queryWrapper.lambda().like(DriverInfo::getName, pageWrap.getModel().getName());
        }
        if (pageWrap.getModel().getTelephone() != null) {
            queryWrapper.lambda().like(DriverInfo::getTelephone, pageWrap.getModel().getTelephone());
        }
        if (pageWrap.getModel().getIdcard() != null) {
            queryWrapper.lambda().eq(DriverInfo::getIdcard, pageWrap.getModel().getIdcard());
        }
        if (pageWrap.getModel().getMaritalStatus() != null) {
            queryWrapper.lambda().eq(DriverInfo::getMaritalStatus, pageWrap.getModel().getMaritalStatus());
        }
        if (pageWrap.getModel().getCarType() != null) {
            queryWrapper.lambda().eq(DriverInfo::getCarType, pageWrap.getModel().getCarType());
        }
        if (pageWrap.getModel().getCarCode() != null) {
            queryWrapper.lambda().like(DriverInfo::getCarCode, pageWrap.getModel().getCarCode());
        }
        if (pageWrap.getModel().getCardStartDate() != null) {
            queryWrapper.lambda().ge(DriverInfo::getCardStartDate, Utils.Date.getStart(pageWrap.getModel().getCardStartDate()));
            queryWrapper.lambda().le(DriverInfo::getCardStartDate, Utils.Date.getEnd(pageWrap.getModel().getCardStartDate()));
        }
        if (pageWrap.getModel().getCardEndDate() != null) {
            queryWrapper.lambda().ge(DriverInfo::getCardEndDate, Utils.Date.getStart(pageWrap.getModel().getCardEndDate()));
            queryWrapper.lambda().le(DriverInfo::getCardEndDate, Utils.Date.getEnd(pageWrap.getModel().getCardEndDate()));
        }
        // 状态
        if (pageWrap.getModel().getStatus() != null) {
            queryWrapper.lambda().eq(DriverInfo::getStatus, pageWrap.getModel().getStatus());
            queryWrapper.eq(DriverInfo::getStatus, pageWrap.getModel().getStatus());
        }
        if (pageWrap.getModel().getAuditTime() != null) {
            queryWrapper.lambda().ge(DriverInfo::getAuditTime, Utils.Date.getStart(pageWrap.getModel().getAuditTime()));
            queryWrapper.lambda().le(DriverInfo::getAuditTime, Utils.Date.getEnd(pageWrap.getModel().getAuditTime()));
        // 审批状态
        if (pageWrap.getModel().getAuditStatus() != null) {
            queryWrapper.eq(DriverInfo::getAuditStatus, pageWrap.getModel().getAuditStatus());
        }
        if (pageWrap.getModel().getAuditUser() != null) {
            queryWrapper.lambda().eq(DriverInfo::getAuditUser, pageWrap.getModel().getAuditUser());
        // 创建日期范围
        if (pageWrap.getModel().getCreateTimeStart() != null) {
            queryWrapper.ge(DriverInfo::getCreateTime, Utils.Date.getStart(pageWrap.getModel().getCreateTimeStart()));
        }
        if (pageWrap.getModel().getMemberId() != null) {
            queryWrapper.lambda().eq(DriverInfo::getMemberId, pageWrap.getModel().getMemberId());
        if (pageWrap.getModel().getCreateTimeEnd() != null) {
            queryWrapper.le(DriverInfo::getCreateTime, Utils.Date.getEnd(pageWrap.getModel().getCreateTimeEnd()));
        }
        // 子查询:余额
        queryWrapper.selectAll(DriverInfo.class)
                .select(" ( select ifnull(sum(r.OPT_TYPE * r.AMOUNT),0) from revenue r where r.MEMBER_TYPE = 1 and r.MEMBER_ID= t.id and r.VAILD_STATUS = 1 ) as memberAmount ")
                .selectAs(Category::getName,DriverInfo::getCarTypeName)
                .leftJoin(Category.class, Category::getId,DriverInfo::getCarType);
        for (PageWrap.SortData sortData : pageWrap.getSorts()) {
            if (sortData.getDirection().equalsIgnoreCase(PageWrap.DESC)) {
                queryWrapper.orderByDesc(sortData.getProperty());
@@ -211,7 +205,11 @@
                queryWrapper.orderByAsc(sortData.getProperty());
            }
        }
        return PageData.from(driverInfoMapper.selectPage(page, queryWrapper));
        PageData<DriverInfo> pageData = PageData.from(driverInfoMapper.selectPage(page, queryWrapper));
        for (DriverInfo d : pageData.getRecords()) {
            d.setGender(Constants.getGenderByIdCard(d.getIdcard()));
        }
        return pageData;
    }
    @Override
@@ -309,15 +307,12 @@
            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.setDriverStatus(Constants.ZERO);
            member.setBusinessStatus(Constants.ZERO);
            member.setPassword(secure.encryptPassword(defaultPassword, salt));
            member.setSalt(salt);
            member.setWorkerIdentity(Constants.ZERO);
            member.setDriverIdentity(Constants.ZERO);
            member.setChefIdentity(Constants.ZERO);
            member.setAmount(Constants.ZERO.longValue());
            member.setTotalAmount(Constants.ZERO.longValue());
            member.setStatus(Constants.ZERO);
@@ -329,7 +324,7 @@
            member.setUseIdentity(Constants.ZERO);
            memberMapper.insert(member);
            // 创建司机基础信息(status=0,注册状态)
            // 创建司机基础信息
            DriverInfo driverInfo = new DriverInfo();
            driverInfo.setDeleted(Constants.ZERO);
            driverInfo.setCreateTime(now);
@@ -337,6 +332,7 @@
            driverInfo.setTelephone(telephone);
            driverInfo.setMemberId(member.getId());
            driverInfo.setStatus(Constants.ZERO);
            driverInfo.setAuditStatus(null);
            driverInfoMapper.insert(driverInfo);
        }
@@ -408,10 +404,9 @@
        if (Objects.isNull(driverInfo)) {
            throw new BusinessException(ResponseStatus.DATA_EMPTY);
        }
        // 状态校验:status=0(注册)或status=3(审批驳回)可提交认证
        if (driverInfo.getStatus() != null
                && !Constants.equalsInteger(driverInfo.getStatus(), Constants.ZERO)
                && !Constants.equalsInteger(driverInfo.getStatus(), Constants.THREE)) {
        // 状态校验:auditStatus=null(未提交)或auditStatus=2(审批驳回)可提交认证
        if (driverInfo.getAuditStatus() != null
                && !Constants.equalsInteger(driverInfo.getAuditStatus(), Constants.TWO)) {
            throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "当前状态不允许提交认证");
        }
        // 根据车辆类型判断是否需要驾驶证
@@ -458,7 +453,7 @@
                .set(DriverInfo::getCardEndDate, request.getCardEndDate())
                .set(DriverInfo::getIdcardImg, request.getIdcardImg())
                .set(DriverInfo::getIdcardImgBack, request.getIdcardImgBack())
                .set(DriverInfo::getStatus, Constants.ONE)
                .set(DriverInfo::getAuditStatus, Constants.ZERO)
                .set(DriverInfo::getUpdateTime, now)
                .set(DriverInfo::getAuditRemark, null)
                .set(DriverInfo::getAuditTime, null)
@@ -482,7 +477,7 @@
        // 更新会员司机认证状态为认证中
        memberMapper.update(new UpdateWrapper<Member>().lambda()
                .set(Member::getDriverStatus, Constants.ONE)
                .set(Member::getBusinessStatus, Constants.ONE)
                .set(Member::getUpdateTime, now)
                .eq(Member::getId, memberId));
    }
@@ -533,35 +528,106 @@
        if (Objects.isNull(driverInfo)) {
            throw new BusinessException(ResponseStatus.DATA_EMPTY);
        }
        // 只有状态为1(待审批)且已填写认证信息才能审批
        if (!Constants.equalsInteger(driverInfo.getStatus(), Constants.ONE)
        // 只有审批状态为0(待审批)且已填写认证信息才能审批
        if (!Constants.equalsInteger(driverInfo.getAuditStatus(), Constants.ZERO)
                || StringUtils.isBlank(driverInfo.getIdcard())) {
            throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "当前状态不允许审批");
        }
        Date now = new Date();
        // 审批结果:auditDTO.auditStatus 0=通过→driverInfo.status=2,1=拒绝→driverInfo.status=3
        Integer newStatus;
        // 审批结果:auditDTO.auditStatus 0=通过→auditStatus=1,1=拒绝→auditStatus=2
        Integer newAuditStatus;
        if (Constants.equalsInteger(auditDTO.getAuditStatus(), Constants.ZERO)) {
            newStatus = Constants.TWO;  // 审批通过
            newAuditStatus = Constants.ONE;  // 审批通过
            // 审批通过时司机定级为必填
            if (auditDTO.getDriverLevel() == null || auditDTO.getDriverLevel() < 1 || auditDTO.getDriverLevel() > 5) {
                throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "审批通过时必须填写司机定级");
            }
        } else if (Constants.equalsInteger(auditDTO.getAuditStatus(), Constants.ONE)) {
            newStatus = Constants.THREE;  // 审批驳回
            newAuditStatus = Constants.TWO;  // 审批驳回
        } else {
            throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "审批状态参数错误");
        }
        // 更新司机状态
        // 更新司机审批状态
        driverInfoMapper.update(new UpdateWrapper<DriverInfo>().lambda()
                .set(DriverInfo::getStatus, newStatus)
                .set(DriverInfo::getAuditStatus, newAuditStatus)
                .set(DriverInfo::getAuditTime, now)
                .set(DriverInfo::getAuditRemark, auditDTO.getAuditRemark())
                .set(DriverInfo::getAuditUser, auditDTO.getAuditUser())
                .set(auditDTO.getDriverLevel() != null, DriverInfo::getDriverLevel, auditDTO.getDriverLevel())
                .set(DriverInfo::getUpdateTime, now)
                .eq(DriverInfo::getId, auditDTO.getId()));
        // 更新会员司机认证状态:通过=2,驳回=3
        Integer driverStatus = Constants.equalsInteger(newStatus, Constants.TWO) ? Constants.TWO : Constants.THREE;
        Integer driverStatus = Constants.equalsInteger(newAuditStatus, Constants.ONE) ? Constants.TWO : Constants.THREE;
        memberMapper.update(new UpdateWrapper<Member>().lambda()
                .set(Member::getDriverStatus, driverStatus)
                .set(Member::getBusinessStatus, driverStatus)
                .set(Member::getUpdateTime, now)
                .eq(Member::getId, driverInfo.getMemberId()));
    }
    @Override
    public DriverInfo getDetail(Integer id) {
        DriverInfo driverInfo = driverInfoMapper.selectById(id);
        if (Objects.isNull(driverInfo) || Constants.equalsInteger(driverInfo.getDeleted(), Constants.ONE)) {
            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);
        driverInfo.setGender(Constants.getGenderByIdCard(driverInfo.getIdcard()));
        // 查询车辆类型名称和是否需要驾驶证
        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);
            }
        }
        // 查询照片列表:objType=6车辆照片、7驾驶证照片、8其他资料照片
        List<Multifile> multifileList = multifileMapper.selectList(new QueryWrapper<Multifile>().lambda()
                .eq(Multifile::getObjId, driverInfo.getId())
                .in(Multifile::getObjType, 6, 7, 8)
                .orderByAsc(Multifile::getSortnum));
        if (!CollectionUtils.isEmpty(multifileList)) {
            for (Multifile mf : multifileList) {
                mf.setFileurlFull(imgPrefix + mf.getFileurl());
                if (Constants.equalsInteger(mf.getObjType(), 6)) {
                    driverInfo.getCarImgList().add(mf);
                } else if (Constants.equalsInteger(mf.getObjType(), 7)) {
                    driverInfo.getLicenseImgList().add(mf);
                } else if (Constants.equalsInteger(mf.getObjType(), 8)) {
                    driverInfo.getOtherImgList().add(mf);
                }
            }
        }
        return driverInfo;
    }
    @Override
    public void changeStatus(ChangeStatusDTO dto) {
        if (dto.getId() == null) {
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "主键不能为空");
        }
        if (dto.getStatus() == null || (dto.getStatus() != 0 && dto.getStatus() != 1)) {
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "状态参数错误,0=启用;1=禁用");
        }
        DriverInfo driverInfo = driverInfoMapper.selectById(dto.getId());
        if (Objects.isNull(driverInfo) || Constants.equalsInteger(driverInfo.getDeleted(), Constants.ONE)) {
            throw new BusinessException(ResponseStatus.DATA_EMPTY);
        }
        driverInfo.setStatus(dto.getStatus());
        driverInfo.setUpdateTime(new Date());
        driverInfoMapper.updateById(driverInfo);
        // 联动修改会员状态(member主键与driver_info主键一致)
        memberMapper.update(new UpdateWrapper<Member>().lambda()
                .set(Member::getStatus, dto.getStatus())
                .set(Member::getUpdateTime, new Date())
                .eq(Member::getId, driverInfo.getId()));
    }
    /**
@@ -582,4 +648,534 @@
        }
    }
    @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(), "司机信息不存在");
        }
        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());
        }
        // 今日预计佣金:revenue表中今天的收入记录金额之和
        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);
        // 今日接单数:今天完成的订单数(acceptDriver=司机主键,状态=已完成)
        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(), "司机位置信息缺失,请先开启定位");
        }
        double driverLat = driver.getLatitude();
        double driverLng = driver.getLongitude();
        // 2. 预查物品等级对应的物品分类ID
        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),使用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))))";
        // 4. 构造MPJ查询
        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);
        // 加急 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) {
        // 查询订单(MPJ JOIN 门店名称+分类,距离计算使用Orders自带坐标)
        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;
    }
    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;
    }
    /**
     * 构建司机端订单列表共用VO(使用Orders自带坐标计算距离)
     *
     * @param order          订单实体(MPJ已填充depositShopName等关联字段)
     * @param driverLat      司机纬度
     * @param driverLng      司机经度
     * @param needDepositDist 是否需要计算距存件门店距离
     * @param now            当前时间
     * @param detailMap      订单物品明细Map(orderId → 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());
        // 存件门店(使用Orders自带坐标)
        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));
        }
        // 取件信息 + 联系电话(使用Orders自带坐标)
        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;
    }
}