rk
2 天以前 467fe3b3ec6aa9d449b094bdd9df4611323d88d1
server/services/src/main/java/com/doumee/service/business/impl/DriverInfoServiceImpl.java
@@ -4,6 +4,7 @@
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.doumee.biz.system.impl.AreasBizImpl;
import com.doumee.core.constants.Constants;
import com.doumee.core.constants.ResponseStatus;
import com.doumee.core.exception.BusinessException;
@@ -15,34 +16,29 @@
import com.doumee.dao.business.DriverInfoMapper;
import com.doumee.dao.business.MemberMapper;
import com.doumee.dao.business.MultifileMapper;
import com.doumee.dao.business.OrderCommentMapper;
import com.doumee.dao.business.SmsrecordMapper;
import com.doumee.dao.business.CategoryMapper;
import com.doumee.dao.business.OrdersMapper;
import com.doumee.dao.business.ShopInfoMapper;
import com.doumee.dao.business.OrdersDetailMapper;
import com.doumee.dao.business.RevenueMapper;
import com.doumee.dao.business.model.*;
import com.doumee.service.business.AreasService;
import com.doumee.biz.system.SystemDictDataBiz;
import com.doumee.biz.system.OperationConfigBiz;
import com.doumee.dao.business.OrderLogMapper;
import com.doumee.dao.business.model.Category;
import com.doumee.dao.business.model.DriverInfo;
import com.doumee.dao.business.model.OrderLog;
import com.doumee.dao.business.model.Member;
import com.doumee.dao.business.model.Multifile;
import com.doumee.dao.business.model.Smsrecord;
import com.doumee.dao.business.model.Orders;
import com.doumee.dao.business.model.OrdersDetail;
import com.doumee.dao.business.model.Revenue;
import com.doumee.dao.dto.*;
import com.doumee.dao.vo.AccountResponse;
import com.doumee.dao.vo.DriverCenterVO;
import com.doumee.dao.vo.DriverGrabOrderVO;
import com.doumee.dao.vo.DriverOrderDetailVO;
import com.doumee.core.utils.aliyun.AliSmsService;
import com.doumee.dao.business.model.Notice;
import com.doumee.service.business.DriverInfoService;
import com.doumee.service.business.NoticeService;
import com.alibaba.fastjson.JSONObject;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
@@ -59,6 +55,7 @@
 * @author rk
 * @date 2026/04/08
 */
@Slf4j
@Service
public class DriverInfoServiceImpl implements DriverInfoService {
@@ -90,6 +87,12 @@
    private OrdersMapper ordersMapper;
    @Autowired
    private ShopInfoMapper shopInfoMapper;
    @Autowired
    private OrderCommentMapper orderCommentMapper;
    @Autowired
    private RevenueMapper revenueMapper;
    @Autowired
@@ -106,6 +109,9 @@
    @Autowired
    private NoticeService noticeService;
    @Autowired
    private AreasBizImpl areasBiz;
    /**
     * 发送订单站内信通知
@@ -371,7 +377,7 @@
            member.setUpdateTime(now);
            member.setTelephone(telephone);
            member.setNickName(telephone.substring(0, 3) + "****" + telephone.substring(7));
            member.setName(telephone);
            member.setName(member.getNickName());
            member.setUserType(Constants.ONE);
            member.setBusinessStatus(Constants.ZERO);
            member.setPassword(secure.encryptPassword(defaultPassword, salt));
@@ -389,13 +395,15 @@
            // 创建司机基础信息
            DriverInfo driverInfo = new DriverInfo();
            driverInfo.setId(member.getId());
            driverInfo.setDeleted(Constants.ZERO);
            driverInfo.setCreateTime(now);
            driverInfo.setUpdateTime(now);
            driverInfo.setTelephone(telephone);
            driverInfo.setName(member.getNickName());
            driverInfo.setMemberId(member.getId());
            driverInfo.setStatus(Constants.ZERO);
            driverInfo.setAuditStatus(null);
            driverInfo.setAuditStatus(99);
            driverInfoMapper.insert(driverInfo);
        }
@@ -519,6 +527,7 @@
                .set(DriverInfo::getAliAccount, request.getAliAccount())
                .set(DriverInfo::getAliName, request.getAliName())
                .set(DriverInfo::getUpdateTime, now)
                .set(DriverInfo::getAuditStatus, Constants.ZERO)
                .set(DriverInfo::getAuditRemark, null)
                .set(DriverInfo::getAuditTime, null)
                .eq(DriverInfo::getId, driverInfo.getId()));
@@ -549,18 +558,39 @@
    @Override
    public DriverInfo getVerifyDetail(Integer memberId) {
        DriverInfo driverInfo = driverInfoMapper.selectOne(new QueryWrapper<DriverInfo>().lambda()
                .eq(DriverInfo::getMemberId, memberId)
                .eq(DriverInfo::getId, memberId)
                .eq(DriverInfo::getDeleted, Constants.ZERO)
                .last("limit 1"));
        if (Objects.isNull(driverInfo)) {
            throw new BusinessException(ResponseStatus.DATA_EMPTY);
        }
        // 拼接图片前缀
        String imgPrefix = "";
        try {
            imgPrefix = systemDictDataBiz.queryByCode(Constants.OSS, Constants.RESOURCE_PATH).getCode()
                    + systemDictDataBiz.queryByCode(Constants.OSS, Constants.DRIVER_FILES).getCode();
        } catch (Exception e) {
            // 未配置时忽略
        }
        driverInfo.setImgPrefix(imgPrefix);
        // 查询车辆类型名称和是否需要驾驶证
        if (driverInfo.getCarType() != null) {
            Category category = categoryMapper.selectById(driverInfo.getCarType());
            if (Objects.nonNull(category)) {
                driverInfo.setCarTypeName(category.getName());
                driverInfo.setNeedLicense(Constants.equalsInteger(Integer.valueOf(category.getOtherField()), Constants.ONE) ? Constants.ONE : Constants.ZERO);
            }
        }
        // 查询省市区信息
        if (driverInfo.getAreaId() != null) {
            Areas district = areasBiz.resolveArea(driverInfo.getAreaId());
            if (district != null) {
                driverInfo.setDistrictId(district.getId());
                driverInfo.setDistrictName(district.getName());
                driverInfo.setCityId(district.getCityId());
                driverInfo.setCityName(district.getCityName());
                driverInfo.setProvinceId(district.getProvinceId());
                driverInfo.setProvinceName(district.getProvinceName());
            }
        }
        // 查询照片列表
@@ -626,6 +656,20 @@
                .set(Member::getBusinessStatus, driverStatus)
                .set(Member::getUpdateTime, now)
                .eq(Member::getId, driverInfo.getMemberId()));
        // 短信通知
        if (Constants.equalsInteger(newAuditStatus, Constants.ONE)) {
            // 审批通过
            sendSmsNotify(driverInfo.getTelephone(),
                    Constants.SmsNotify.DRIVER_AUTH_APPROVED,
                    "driver", driverInfo.getName());
        } else if (Constants.equalsInteger(newAuditStatus, Constants.TWO)) {
            // 审批驳回
            sendSmsNotify(driverInfo.getTelephone(),
                    Constants.SmsNotify.DRIVER_AUTH_REJECTED,
                    "driver", driverInfo.getName(),
                    "reason", auditDTO.getAuditRemark() != null ? auditDTO.getAuditRemark() : "");
        }
    }
    @Override
@@ -764,8 +808,11 @@
        vo.setImgUrl(driver.getImgurl());
        vo.setCarCode(driver.getCarCode());
        vo.setScore(driver.getScore() != null ? driver.getScore().toPlainString() : "0");
        vo.setDriverLevel(driver.getDriverLevel());
        vo.setAuditStatus(driver.getAuditStatus());
        vo.setAuditRemark(driver.getAuditRemark());
        vo.setBalance(driver.getBalance() != null ? driver.getBalance() : 0L);
        vo.setAcceptingStatus(driver.getAcceptingStatus());
        // 头像全路径
        if (StringUtils.isNotBlank(driver.getImgurl())) {
            String imgPrefix = systemDictDataBiz.queryByCode(Constants.OSS, Constants.RESOURCE_PATH).getCode()
@@ -814,6 +861,54 @@
                .eq(Orders::getDeleted, Constants.ZERO)
                .eq(Orders::getStatus, Constants.OrderStatus.delivering.getStatus()));
        vo.setWaitDeliverCount(waitDeliverCount.intValue());
        return vo;
    }
    @Override
    public com.doumee.dao.vo.DriverStatsVO getDriverStats(Integer memberId) {
        DriverInfo driver = driverInfoMapper.selectOne(new QueryWrapper<DriverInfo>().lambda()
                .eq(DriverInfo::getId, memberId)
                .eq(DriverInfo::getDeleted, Constants.ZERO)
                .last("limit 1"));
        if (driver == null) {
            throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "司机信息不存在");
        }
        com.doumee.dao.vo.DriverStatsVO vo = new com.doumee.dao.vo.DriverStatsVO();
        // 累计佣金:type=0(完成订单) + vaildStatus=1(已入账)
        QueryWrapper<Revenue> totalWrapper = new QueryWrapper<>();
        totalWrapper.lambda()
                .eq(Revenue::getMemberId, memberId)
                .eq(Revenue::getMemberType, Constants.ONE)
                .eq(Revenue::getType, Constants.ZERO)
                .eq(Revenue::getVaildStatus, Constants.ONE)
                .eq(Revenue::getDeleted, Constants.ZERO);
        totalWrapper.select("IFNULL(SUM(AMOUNT),0) as amount");
        Revenue totalResult = revenueMapper.selectOne(totalWrapper);
        vo.setTotalCommission(totalResult != null && totalResult.getAmount() != null ? totalResult.getAmount() : 0L);
        // 待结算佣金:type=0(完成订单) + vaildStatus=0(入账中)
        QueryWrapper<Revenue> pendingWrapper = new QueryWrapper<>();
        pendingWrapper.lambda()
                .eq(Revenue::getMemberId, memberId)
                .eq(Revenue::getMemberType, Constants.ONE)
                .eq(Revenue::getType, Constants.ZERO)
                .eq(Revenue::getVaildStatus, Constants.ZERO)
                .eq(Revenue::getDeleted, Constants.ZERO);
        pendingWrapper.select("IFNULL(SUM(AMOUNT),0) as amount");
        Revenue pendingResult = revenueMapper.selectOne(pendingWrapper);
        vo.setPendingCommission(pendingResult != null && pendingResult.getAmount() != null ? pendingResult.getAmount() : 0L);
        // 订单总数
        Long totalOrderCount = ordersMapper.selectCount(new QueryWrapper<Orders>().lambda()
                .eq(Orders::getAcceptDriver, driver.getId())
                .eq(Orders::getDeleted, Constants.ZERO));
        vo.setTotalOrderCount(totalOrderCount.intValue());
        // 钱包余额
        vo.setBalance(driver.getBalance() != null ? driver.getBalance() : 0L);
        return vo;
    }
@@ -1114,10 +1209,59 @@
            }
        }
        // 异地寄存实时经纬度(按状态返回)
        if (Constants.ONE.equals(order.getType())) {
            // 司机自身经纬度
            vo.setDriverLng(driverLng);
            vo.setDriverLat(driverLat);
            // status=3(已接单):返回存件门店经纬度
            if (Constants.equalsInteger(order.getStatus(), Constants.OrderStatus.accepted.getStatus())) {
                if (order.getDepositShopId() != null) {
                    ShopInfo depositShop = shopInfoMapper.selectById(order.getDepositShopId());
                    if (depositShop != null) {
                        vo.setDepositShopLng(depositShop.getLongitude());
                        vo.setDepositShopLat(depositShop.getLatitude());
                    }
                }
            }
            // status=4(配送中):返回取件点经纬度
            if (Constants.equalsInteger(order.getStatus(), Constants.OrderStatus.delivering.getStatus())) {
                if (order.getTakeShopId() != null) {
                    ShopInfo takeShop = shopInfoMapper.selectById(order.getTakeShopId());
                    if (takeShop != null) {
                        vo.setTakeLng(takeShop.getLongitude());
                        vo.setTakeLat(takeShop.getLatitude());
                    }
                } else if (order.getTakeLgt() != null && order.getTakeLat() != null) {
                    vo.setTakeLng(order.getTakeLgt().doubleValue());
                    vo.setTakeLat(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));
        // 评价信息
        vo.setCommentStatus(order.getCommentStatus());
        if (Constants.equalsInteger(order.getCommentStatus(), Constants.ONE)) {
            vo.setCommentTime(order.getCommentTime());
            // 查询司机评价记录
            OrderComment driverComment = orderCommentMapper.selectOne(new QueryWrapper<OrderComment>().lambda()
                    .eq(OrderComment::getOrderId, orderId)
                    .eq(OrderComment::getTargetType, Constants.THREE)
                    .eq(OrderComment::getDeleted, Constants.ZERO)
                    .last("limit 1"));
            if (driverComment != null) {
                vo.setDriverScore(driverComment.getScore());
                vo.setCommentContent(driverComment.getContent());
            }
            // 评价附件图片
            vo.setCommentImages(getFileUrls(orderId, Constants.FileType.COMMENT_ATTACH.getKey(), imgPrefix));
        }
        return vo;
    }
@@ -1160,7 +1304,7 @@
        Date todayStart = cal.getTime();
        Long todayCancelCount = orderLogMapper.selectCount(new QueryWrapper<OrderLog>().lambda()
                .eq(OrderLog::getOptUserId, driver.getMemberId())
                .eq(OrderLog::getObjType, Constants.ORDER_LOG_CANCEL)
                .eq(OrderLog::getObjType, Constants.OrderLogType.driverCancel.getStatus())
                .eq(OrderLog::getOptUserType, Constants.ONE)
                .ge(OrderLog::getCreateTime, todayStart));
        if (todayCancelCount != null && todayCancelCount >= limit) {
@@ -1177,9 +1321,9 @@
        // 5. 写入取消日志
        OrderLog log = new OrderLog();
        log.setOrderId(orderId);
        log.setTitle("司机取消订单");
        log.setLogInfo(StringUtils.isNotBlank(reason) ? reason : "司机取消接单");
        log.setObjType(Constants.ORDER_LOG_CANCEL);
        log.setTitle(Constants.OrderLogType.driverCancel.getTitle());
        log.setLogInfo(Constants.OrderLogType.driverCancel.format(StringUtils.isNotBlank(reason) ? reason : "司机取消接单"));
        log.setObjType(Constants.OrderLogType.driverCancel.getStatus());
        log.setOptUserId(driver.getMemberId());
        log.setOptUserType(Constants.ONE);
        log.setOrderStatus(order.getStatus());
@@ -1251,9 +1395,9 @@
        // 6. 写入操作日志
        OrderLog log = new OrderLog();
        log.setOrderId(orderId);
        log.setTitle("司机抢单");
        log.setLogInfo("司机【" + driver.getName() + "】抢单成功");
        log.setObjType(Constants.ORDER_LOG_DRIVER_PICKUP);
        log.setTitle(Constants.OrderLogType.driverGrab.getTitle());
        log.setLogInfo(Constants.OrderLogType.driverGrab.format(driver.getName()));
        log.setObjType(Constants.OrderLogType.driverGrab.getStatus());
        log.setOptUserId(driver.getMemberId());
        log.setOptUserType(Constants.ONE);
        log.setOrderStatus(Constants.OrderStatus.accepted.getStatus());
@@ -1276,6 +1420,12 @@
        sendDriverNotice(driverId, Constants.DriverOrderNotify.WAIT_DELIVER, orderId,
                "orderNo", order.getCode(),
                "shopName", shopName != null ? shopName : "");
        // 短信通知司机:抢单成功,待取件
        String pickupAddress = order.getDepositShopAddress() != null ? order.getDepositShopAddress() : order.getDepositLocation();
        sendSmsNotify(driver.getTelephone(), Constants.SmsNotify.DRIVER_WAIT_PICKUP,
                "orderNo", order.getCode(),
                "address", pickupAddress != null ? pickupAddress : "");
    }
    @Override
@@ -1330,9 +1480,9 @@
        // 5. 写入操作日志
        OrderLog log = new OrderLog();
        log.setOrderId(orderId);
        log.setTitle("司机完成取件");
        log.setLogInfo("司机【" + driver.getName() + "】完成取件,开始派送");
        log.setObjType(Constants.ORDER_LOG_DRIVER_PICKUP);
        log.setTitle(Constants.OrderLogType.driverPickup.getTitle());
        log.setLogInfo(Constants.OrderLogType.driverPickup.format(driver.getName()));
        log.setObjType(Constants.OrderLogType.driverPickup.getStatus());
        log.setOptUserId(driver.getMemberId());
        log.setOptUserType(Constants.ONE);
        log.setOrderStatus(Constants.OrderStatus.delivering.getStatus());
@@ -1355,6 +1505,14 @@
        // 通知司机:已取件配送中
        sendDriverNotice(driverId, Constants.DriverOrderNotify.DELIVERING, orderId,
                "orderNo", order.getCode());
        // 短信通知会员:司机已取件,配送中
        Member deliveringMember = memberMapper.selectById(order.getMemberId());
        if (deliveringMember != null) {
            sendSmsNotify(deliveringMember.getTelephone(), Constants.SmsNotify.MEMBER_DELIVERING,
                    "orderNo", order.getCode(),
                    "name", driver.getName());
        }
    }
    @Override
@@ -1413,9 +1571,10 @@
        // 5. 写入操作日志
        OrderLog log = new OrderLog();
        log.setOrderId(orderId);
        log.setTitle("司机确认送达");
        log.setLogInfo(StringUtils.isNotBlank(dto.getRemark()) ? dto.getRemark() : "司机【" + driver.getName() + "】已送达");
        log.setObjType(Constants.ORDER_LOG_DRIVER_DELIVER);
        log.setTitle(Constants.OrderLogType.driverDeliver.getTitle());
        log.setLogInfo(Constants.OrderLogType.driverDeliver.format(
                StringUtils.isNotBlank(dto.getRemark()) ? dto.getRemark() : "司机【" + driver.getName() + "】已送达"));
        log.setObjType(Constants.OrderLogType.driverDeliver.getStatus());
        log.setOptUserId(driver.getMemberId());
        log.setOptUserType(Constants.ONE);
        log.setOrderStatus(Constants.OrderStatus.arrived.getStatus());
@@ -1567,4 +1726,99 @@
        return vo;
    }
    /**
     * 发送短信通知(失败不影响主业务)
     */
    private void sendSmsNotify(String phone, Constants.SmsNotify smsNotify, String... paramPairs) {
        if (StringUtils.isBlank(phone)) {
            return;
        }
        String content = smsNotify.format(paramPairs);
        try {
            JSONObject templateParam = new JSONObject();
            for (int i = 0; i < paramPairs.length - 1; i += 2) {
                templateParam.put(paramPairs[i], paramPairs[i + 1]);
            }
            boolean result = AliSmsService.sendSms(phone, smsNotify.getTemplateCode(),
                    templateParam.toJSONString());
            if (result) {
                log.info("短信发送成功: phone={}, template={}", phone, smsNotify.name());
            } else {
                log.warn("短信发送失败: phone={}, template={}", phone, smsNotify.name());
            }
            // 存储短信记录
            Smsrecord record = new Smsrecord();
            record.setPhone(phone);
            record.setContent(content);
            record.setType(Constants.ONE); // 1=订单通知
            record.setStatus(result ? Constants.ONE : Constants.ZERO);
            record.setCreateTime(new Date());
            record.setDeleted(Constants.ZERO);
            smsrecordMapper.insert(record);
        } catch (Exception e) {
            log.error("短信发送异常: phone={}, template={}, error={}", phone, smsNotify.name(), e.getMessage());
            try {
                Smsrecord record = new Smsrecord();
                record.setPhone(phone);
                record.setContent(content);
                record.setType(Constants.ONE);
                record.setStatus(Constants.ZERO);
                record.setCreateTime(new Date());
                record.setDeleted(Constants.ZERO);
                smsrecordMapper.insert(record);
            } catch (Exception ignored) {}
        }
    }
    @Override
    public void changePassword(Integer driverId, String newPassword, String token) {
        if (StringUtils.isBlank(newPassword)) {
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "密码不能为空");
        }
        // 校验密码必须同时包含字母和数字
        boolean hasLetter = newPassword.chars().anyMatch(Character::isLetter);
        boolean hasDigit = newPassword.chars().anyMatch(Character::isDigit);
        if (!hasLetter || !hasDigit) {
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "密码必须同时包含字母和数字");
        }
        // 查询司机对应的会员
        DriverInfo driverInfo = driverInfoMapper.selectById(driverId);
        if (driverInfo == null || driverInfo.getMemberId() == null) {
            throw new BusinessException(ResponseStatus.DATA_EMPTY);
        }
        Member member = memberMapper.selectById(driverInfo.getMemberId());
        if (member == null) {
            throw new BusinessException(ResponseStatus.DATA_EMPTY);
        }
        // 加密新密码并更新
        String salt = RandomStringUtils.randomAlphanumeric(6);
        String encryptPwd = secure.encryptPassword(newPassword, salt);
        memberMapper.update(new UpdateWrapper<Member>().lambda()
                .set(Member::getPassword, encryptPwd)
                .set(Member::getSalt, salt)
                .eq(Member::getId, member.getId()));
        // 清除token,强制重新登录
        if (StringUtils.isNotBlank(token)) {
            redisTemplate.delete(token);
        }
    }
    @Override
    public Map<String, Integer> getActiveOrderCount(Integer driverId) {
        // 已抢单(status=3)数量
        Long grabbed = ordersMapper.selectCount(new QueryWrapper<Orders>().lambda()
                .eq(Orders::getAcceptDriver, driverId)
                .eq(Orders::getStatus, Constants.OrderStatus.accepted.getStatus())
                .eq(Orders::getDeleted, Constants.ZERO));
        // 派送中(status=4)数量
        Long delivering = ordersMapper.selectCount(new QueryWrapper<Orders>().lambda()
                .eq(Orders::getAcceptDriver, driverId)
                .eq(Orders::getStatus, Constants.OrderStatus.delivering.getStatus())
                .eq(Orders::getDeleted, Constants.ZERO));
        Map<String, Integer> result = new HashMap<>();
        result.put("grabbedCount", grabbed != null ? grabbed.intValue() : 0);
        result.put("deliveringCount", delivering != null ? delivering.intValue() : 0);
        return result;
    }
}