| | |
| | | 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.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实现 |
| | |
| | | |
| | | @Autowired |
| | | private CategoryMapper categoryMapper; |
| | | |
| | | @Autowired |
| | | private OrdersMapper ordersMapper; |
| | | |
| | | @Autowired |
| | | private RevenueMapper revenueMapper; |
| | | |
| | | @Autowired |
| | | private OrdersDetailMapper ordersDetailMapper; |
| | | |
| | | @Autowired |
| | | private SystemDictDataBiz systemDictDataBiz; |
| | |
| | | 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); |
| | |
| | | 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) { |
| | | // 未配置时忽略 |
| | | } |
| | |
| | | } |
| | | } |
| | | |
| | | @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; |
| | | } |
| | | |
| | | } |