package com.xiaopeng.order.service.impl; import cn.hutool.core.bean.BeanUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.google.common.collect.ImmutableMap; import com.xiaopeng.api.dto.account.GetUserCouponDto; import com.xiaopeng.api.dto.operation.LonLatDto; import com.xiaopeng.api.dto.order.GetOrderNumDto; import com.xiaopeng.api.dto.order.HistoryEndPointsDto; import com.xiaopeng.api.dto.order.HistoryStartPointsDto; import com.xiaopeng.api.dto.statistics.DailyCountDTO; import com.xiaopeng.api.dto.statistics.OrderStatisticsDTO; import com.xiaopeng.api.dto.statistics.StatisticsQueryDTO; import com.xiaopeng.api.dto.vehicleConvoy.BaseVehicleUserDto; import com.xiaopeng.api.feignClient.account.XpAppUserClient; import com.xiaopeng.api.feignClient.account.XpAppUserCouponClient; import com.xiaopeng.api.feignClient.operation.XpSystemSettingClient; import com.xiaopeng.api.feignClient.vehicleConvoy.UserVehicleClient; import com.xiaopeng.api.feignClient.vehicleConvoy.XpFleetClient; import com.xiaopeng.api.feignClient.vehicleConvoy.XpFleetVehicleClient; import com.xiaopeng.api.feignClient.vehicleConvoy.XpVehicleClient; import com.xiaopeng.api.feignClient.operation.XpPointClient; import com.xiaopeng.api.feignClient.operation.XpPricingClient; import com.xiaopeng.api.model.account.XpAppUser; import com.xiaopeng.api.model.operation.XpPricing; import com.xiaopeng.api.model.vehicleConvoy.XpFleet; import com.xiaopeng.api.model.vehicleConvoy.XpVehicle; import com.xiaopeng.api.model.order.XpOrder; import com.xiaopeng.api.model.order.XpOrderCancel; import com.xiaopeng.api.model.order.XpOrderEvaluate; import com.xiaopeng.api.model.order.XpOrderReassign; import com.xiaopeng.api.vo.account.UserCouponVo; import com.xiaopeng.api.vo.order.*; import com.xiaopeng.api.vo.vehicleConvoy.CancelTaxiPassengerVo; import com.xiaopeng.common.core.constant.CacheConstants; import com.xiaopeng.common.core.domain.R; import com.xiaopeng.common.core.exception.ServiceException; import com.xiaopeng.common.core.utils.DateUtils; import com.xiaopeng.common.core.utils.GeodesyUtil; import com.xiaopeng.common.core.utils.StringUtils; import com.xiaopeng.common.core.web.domain.BaseModel; import com.xiaopeng.common.core.web.page.PageInfo; import com.xiaopeng.common.redis.service.RedisService; import com.xiaopeng.common.security.utils.SecurityUtils; import com.xiaopeng.order.constant.I18nKeyConstant; import com.xiaopeng.order.job.jobs.EndTripJob; import com.xiaopeng.order.mangodb.model.OrderTrajectory; import com.xiaopeng.order.mangodb.service.OrderTrajectoryService; import com.xiaopeng.order.mangodb.model.OrderTrajectory; import com.xiaopeng.order.mapper.XpOrderMapper; import com.xiaopeng.order.model.dto.XpOrderCommentDto; import com.xiaopeng.order.model.dto.XpOrderPayDto; import com.xiaopeng.order.model.query.XpHistoryOrderListQuery; import com.xiaopeng.order.model.query.XpOrderPageQuery; import com.xiaopeng.order.model.vo.*; import com.xiaopeng.order.service.IXpOrderCancelService; import com.xiaopeng.order.service.IXpOrderEvaluateService; import com.xiaopeng.order.service.IXpOrderReassignService; import com.xiaopeng.order.service.IXpOrderService; import com.xiaopeng.order.job.base.QuartzManager; import com.xiaopeng.order.job.base.TimeJobType; import com.xiaopeng.order.job.jobs.OrderCancelJob; import io.swagger.annotations.ApiModelProperty; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.data.domain.Sort; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; import java.math.BigDecimal; import java.math.RoundingMode; import java.time.Duration; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; /** *

* 订单 服务实现类 *

* * @author mitao * @since 2026-02-03 */ @Slf4j @Service @RequiredArgsConstructor(onConstructor_ = @Lazy) public class XpOrderServiceImpl extends ServiceImpl implements IXpOrderService { private final XpAppUserClient xpAppUserClient; private final XpPointClient xpPointClient; private final XpVehicleClient xpVehicleClient; private final XpAppUserCouponClient xpAppUserCouponClient; private final XpFleetClient xpFleetClient; private final XpFleetVehicleClient xpFleetVehicleClient; private final UserVehicleClient userVehicleClient; private final IXpOrderEvaluateService xpOrderEvaluateService; private final IXpOrderCancelService xpOrderCancelService; private final IXpOrderReassignService xpOrderReassignService; private final RedisService redisService; private final XpPricingClient xpPricingClient; private final XpSystemSettingClient xpSystemSettingClient; private final OrderTrajectoryService orderTrajectoryService; @Autowired private MongoTemplate mongoTemplate; // @Autowired // private XpPricingUtil xpPricingUtil; @Override public OrderStatisticsDTO getOrderStatistics(StatisticsQueryDTO query) { OrderStatisticsDTO dto = new OrderStatisticsDTO(); // 1. 查询订单总数(状态为9=待评价或10=已完成) dto.setTotalCount(baseMapper.countCompletedOrders()); // 2. 查询时间范围内新增订单数 if (Objects.nonNull(query.getStartDate()) && Objects.nonNull(query.getEndDate())) { dto.setNewCount(baseMapper.countNewOrders(query.getStartDate(), query.getEndDate())); } else { dto.setNewCount(0L); } // 3. 查询总交易金额和总收入(pay_money累加) BigDecimal totalMoney = baseMapper.sumTotalMoney(); dto.setTotalMoney(Objects.nonNull(totalMoney) ? totalMoney : BigDecimal.ZERO); dto.setTotalIncome(Objects.nonNull(totalMoney) ? totalMoney : BigDecimal.ZERO); // 4. 查询近N天每日新增订单数(默认30天) Integer trendDays = Objects.nonNull(query.getTrendDays()) ? query.getTrendDays() : 30; LocalDate endDate = LocalDate.now(); LocalDate startDate = endDate.minusDays(trendDays - 1); List dailyCountList = baseMapper.getDailyOrderCount(startDate, endDate); dto.setDailyCountList(Objects.nonNull(dailyCountList) ? dailyCountList : new ArrayList<>()); return dto; } @Override public PageInfo pageList(XpOrderPageQuery query) { // 1. 构建查询条件 LambdaQueryWrapper wrapper = buildQueryWrapper(query); wrapper.orderByDesc(XpOrder::getCreateTime); // 2. 分页查询 PageInfo pageInfo = new PageInfo<>(query.getPageNum(), query.getPageSize()); PageInfo orderPage = this.page(pageInfo, wrapper); if (CollectionUtils.isEmpty(orderPage.getRecords())) { PageInfo resultPage = new PageInfo<>(query.getPageNum(), query.getPageSize()); resultPage.setTotal(0L); resultPage.setRecords(new ArrayList<>()); return resultPage; } // 3. 收集关联ID Set userIds = new HashSet<>(); Set vehicleIds = new HashSet<>(); Set fleetIds = new HashSet<>(); for (XpOrder order : orderPage.getRecords()) { if (Objects.nonNull(order.getAppUserId())) { userIds.add(order.getAppUserId()); } if (Objects.nonNull(order.getVehicleId())) { vehicleIds.add(order.getVehicleId()); } if (Objects.nonNull(order.getFleetId())) { fleetIds.add(order.getFleetId()); } } // 4. 批量查询关联数据 Map userMap = batchGetUsers(userIds); Map vehicleMap = batchGetVehicles(vehicleIds); Map fleetMap = batchGetFleets(fleetIds); // 5. 组装 VO List voList = orderPage.getRecords().stream() .map(order -> convertToPageVO(order, userMap, vehicleMap, fleetMap)) .collect(Collectors.toList()); // 6. 返回分页结果 PageInfo resultPage = new PageInfo<>(query.getPageNum(), query.getPageSize()); resultPage.setTotal(orderPage.getTotal()); resultPage.setRecords(voList); return resultPage; } @Override public XpOrderDetailVO getOrderDetail(Long id) { // 1. 查询订单基础信息 XpOrder order = this.getById(id); if (order == null) { throw new ServiceException("订单不存在"); } // 2. 转换为详情 VO XpOrderDetailVO vo = BeanUtil.toBean(order, XpOrderDetailVO.class); // 3. 获取用户信息 if (Objects.nonNull(order.getAppUserId())) { Map userMap = batchGetUsers(Collections.singleton(order.getAppUserId())); XpAppUser user = userMap.get(order.getAppUserId()); if (Objects.nonNull(user)) { vo.setNickName(user.getNickName()); vo.setPhoneNumber(user.getPhoneNumber()); } } // 4. 获取车辆信息 if (Objects.nonNull(order.getVehicleId())) { Map vehicleMap = batchGetVehicles(Collections.singleton(order.getVehicleId())); XpVehicle vehicle = vehicleMap.get(order.getVehicleId()); if (Objects.nonNull(vehicle)) { vo.setPlateNumber(vehicle.getPlateNumber()); vo.setVehicleModel(vehicle.getVehicleModel()); } } // 5. 获取车队信息 if (Objects.nonNull(order.getFleetId())) { Map fleetMap = batchGetFleets(Collections.singleton(order.getFleetId())); XpFleet fleet = fleetMap.get(order.getFleetId()); if (Objects.nonNull(fleet)) { vo.setFleetName(fleet.getFleetName()); } } // 6. 获取评价信息 LambdaQueryWrapper evaluateWrapper = new LambdaQueryWrapper<>(); evaluateWrapper.eq(XpOrderEvaluate::getOrderId, id); XpOrderEvaluate evaluate = xpOrderEvaluateService.getOne(evaluateWrapper); if (Objects.nonNull(evaluate)) { vo.setOrderRating(evaluate.getOrderRating()); } //TODO 7.查询mongodb 该订单上传的经纬度轨迹信息 vo.setOrderPositionList(new ArrayList<>()); return vo; } @Override public void deleteOrder(Long id) { // 1. 校验订单是否存在 XpOrder order = this.getById(id); if (Objects.isNull(order)) { throw new ServiceException("订单不存在", I18nKeyConstant.ORDER_NOT_EXISTS); } this.removeById(id); } @Override public List exportList(XpOrderPageQuery query) { // 1. 构建查询条件 LambdaQueryWrapper wrapper = buildQueryWrapper(query); // 2. 查询订单列表 List orderList = this.list(wrapper); if (CollectionUtils.isEmpty(orderList)) { return new ArrayList<>(); } // 3. 收集关联ID Set userIds = new HashSet<>(); Set vehicleIds = new HashSet<>(); Set fleetIds = new HashSet<>(); for (XpOrder order : orderList) { if (Objects.nonNull(order.getAppUserId())) { userIds.add(order.getAppUserId()); } if (Objects.nonNull(order.getVehicleId())) { vehicleIds.add(order.getVehicleId()); } if (Objects.nonNull(order.getFleetId())) { fleetIds.add(order.getFleetId()); } } // 4. 批量查询关联数据 Map userMap = batchGetUsers(userIds); Map vehicleMap = batchGetVehicles(vehicleIds); Map fleetMap = batchGetFleets(fleetIds); // 5. 组装 VO return orderList.stream() .map(order -> convertToPageVO(order, userMap, vehicleMap, fleetMap)) .collect(Collectors.toList()); } /** * 构建查询条件 */ private LambdaQueryWrapper buildQueryWrapper(XpOrderPageQuery query) { LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.like(StringUtils.isNotBlank(query.getOrderNumber()), XpOrder::getOrderNumber, query.getOrderNumber()) .eq(Objects.nonNull(query.getFleetId()), XpOrder::getFleetId, query.getFleetId()) .eq(Objects.nonNull(query.getOrderSource()), XpOrder::getOrderSource, query.getOrderSource()) .like(StringUtils.isNotBlank(query.getCityCode()), XpOrder::getCityCode, query.getCityCode()) .eq(Objects.nonNull(query.getStatus()), XpOrder::getStatus, query.getStatus()); if (Objects.nonNull(query.getStartTime())) { wrapper.ge(XpOrder::getCreateTime, LocalDateTime.of(query.getStartTime(), LocalTime.MIN)); } if (Objects.nonNull(query.getEndTime())) { wrapper.ge(XpOrder::getCreateTime, LocalDateTime.of(query.getEndTime(), LocalTime.MAX)); } wrapper.orderByDesc(XpOrder::getCreateTime); return wrapper; } /** * 批量获取用户信息 */ private Map batchGetUsers(Set userIds) { if (CollectionUtils.isEmpty(userIds)) { return new HashMap<>(); } try { R> result = xpAppUserClient.listByIds(new ArrayList<>(userIds)); if (Objects.nonNull(result) && Objects.nonNull(result.getData())) { return result.getData().stream() .collect(Collectors.toMap(XpAppUser::getId, Function.identity(), (a, b) -> a)); } } catch (Exception e) { log.error("批量获取用户信息失败: {}", e.getMessage()); } return new HashMap<>(); } /** * 批量获取车辆信息 */ private Map batchGetVehicles(Set vehicleIds) { if (CollectionUtils.isEmpty(vehicleIds)) { return new HashMap<>(); } try { R> result = xpVehicleClient.listByIds(new ArrayList<>(vehicleIds)); if (Objects.nonNull(result) && Objects.nonNull(result.getData())) { return result.getData().stream() .collect(Collectors.toMap(XpVehicle::getId, Function.identity(), (a, b) -> a)); } } catch (Exception e) { log.error("批量获取车辆信息失败: {}", e.getMessage()); } return new HashMap<>(); } /** * 批量获取车队信息 */ private Map batchGetFleets(Set fleetIds) { if (CollectionUtils.isEmpty(fleetIds)) { return new HashMap<>(); } try { R> result = xpFleetClient.listByIds(new ArrayList<>(fleetIds)); if (Objects.nonNull(result) && Objects.nonNull(result.getData())) { return result.getData().stream() .collect(Collectors.toMap(XpFleet::getId, Function.identity(), (a, b) -> a)); } } catch (Exception e) { log.error("批量获取车队信息失败: {}", e.getMessage()); } return new HashMap<>(); } @Transactional(rollbackFor = Exception.class) @Override public void cancelOrCloseOrder(Long orderId, Integer type) { // 1. 校验订单是否存在 XpOrder order = this.getById(orderId); if (Objects.isNull(order)) { throw new ServiceException("订单不存在"); } // 2. 校验操作类型 if (type != 1 && type != 2) { throw new ServiceException("操作类型错误,type只能为1或2"); } // 3. 校验订单状态是否允许操作 Integer currentStatus = order.getStatus(); //取消订单:在未接到乘客前都可以,关闭订单,在送达目的地可以操作关闭 if (type == 1) { // 取消订单:只有待接单(1)、待出发(2)、接驾中(3)状态可以取消 if (currentStatus != 1 && currentStatus != 2 && currentStatus != 3) { throw new ServiceException("当前订单状态不允许取消,只有待接单、待出发、接驾中状态的订单可以取消", I18nKeyConstant.ORDER_CANCEL_FAILED); } } else { // 关闭订单:只有待付款(8)、待评价(9)状态可以关闭 if (currentStatus != 7 && currentStatus != 8 && currentStatus != 9) { throw new ServiceException("当前订单状态不允许关闭,只有待付款、待评价状态的订单可以关闭", I18nKeyConstant.ORDER_CLOSE_FAILED); } } // 4. 执行操作 if (type == 1) { // 取消订单 cancelOrder(order); } else { // 关闭订单 closeOrder(order); } } /** * 转换为分页列表 VO */ private XpOrderPageVO convertToPageVO(XpOrder order, Map userMap, Map vehicleMap, Map fleetMap) { XpOrderPageVO vo = new XpOrderPageVO(); vo.setId(order.getId()); vo.setOrderNumber(order.getOrderNumber()); vo.setCreateTime(order.getCreateTime()); vo.setOrderSource(order.getOrderSource()); vo.setRideTime(order.getRideTime()); vo.setStartAddress(order.getStartAddress()); vo.setEndAddress(order.getEndAddress()); vo.setOrderMoney(order.getOrderMoney()); vo.setStatus(order.getStatus()); // 用户信息 if (Objects.nonNull(order.getAppUserId()) && userMap.containsKey(order.getAppUserId())) { XpAppUser user = userMap.get(order.getAppUserId()); vo.setNickName(user.getNickName()); vo.setPhoneNumber(user.getPhoneNumber()); } // 车辆信息 if (Objects.nonNull(order.getVehicleId()) && vehicleMap.containsKey(order.getVehicleId())) { XpVehicle vehicle = vehicleMap.get(order.getVehicleId()); vo.setPlateNumber(vehicle.getPlateNumber()); vo.setVehicleModel(vehicle.getVehicleModel()); } // 车队信息 if (Objects.nonNull(order.getFleetId()) && fleetMap.containsKey(order.getFleetId())) { XpFleet fleet = fleetMap.get(order.getFleetId()); vo.setFleetName(fleet.getFleetName()); } return vo; } /** * 取消订单 */ private void cancelOrder(XpOrder order) { // 1. 更新订单状态为已取消(0) order.setStatus(0); this.updateById(order); // 2. 保存取消记录 XpOrderCancel orderCancel = new XpOrderCancel(); orderCancel.setOrderId(order.getId()); orderCancel.setCancelSource(3); // 3=运营人员(后台管理接口固定为运营人员) orderCancel.setUserId(SecurityUtils.getUserId()); // 当前登录用户ID orderCancel.setCancelReason("运营人员取消订单"); orderCancel.setCreateBy(SecurityUtils.getLoginUser().getSysUser().getNickName()); xpOrderCancelService.save(orderCancel); log.info("订单取消成功,订单ID: {}, 操作人: {}", order.getId(), SecurityUtils.getUsername()); } /** * 关闭订单 */ private void closeOrder(XpOrder order) { // 更新订单状态为已完成(10) order.setStatus(10); this.updateById(order); log.info("订单关闭成功,订单ID: {}, 操作人: {}", order.getId(), SecurityUtils.getUsername()); } /** * 改派订单 */ @Transactional(rollbackFor = Exception.class) @Override public void reassignOrder(Long orderId, Long newVehicleId, String reassignReason) { // 1. 校验订单是否存在 XpOrder order = this.getById(orderId); if (Objects.isNull(order)) { throw new ServiceException("订单不存在"); } // 2. 校验订单状态(只有待接单、待出发、接驾中状态可以改派) Integer currentStatus = order.getStatus(); if (currentStatus != 1 && currentStatus != 2 && currentStatus != 3) { throw new ServiceException("当前订单状态不允许改派,只有待接单、待出发、接驾中状态的订单可以改派", I18nKeyConstant.ORDER_REASSIGN_FAILED); } // 3. 校验新旧车辆是否相同 if (Objects.equals(order.getVehicleId(), newVehicleId)) { throw new ServiceException("新车辆与原车辆相同,无需改派", I18nKeyConstant.ORDER_REASSIGN_VEHICLE_FAILED); } // 4. 查询新车辆信息并校验 R> vehicleResult = xpVehicleClient.listByIds(Collections.singletonList(newVehicleId)); if (Objects.isNull(vehicleResult) || Objects.isNull(vehicleResult.getData()) || vehicleResult.getData().isEmpty()) { throw new ServiceException("新车辆不存在", I18nKeyConstant.ORDER_REASSIGN_NEW_VEHICLE_NOT_EXISTS); } XpVehicle newVehicle = vehicleResult.getData().get(0); // 5. 校验新车辆的运营状态 // 车辆状态:1=待完善 2=待运营 3=保险/年检过期 // 运营状态:1=运营中 2=维修中 3=已收车 4=车辆异常 // 当前模式:1=接单 2=测试 // 模式状态:1=开始接单/开始测试 2=停止接单/停止测试 if (Objects.isNull(newVehicle.getStatus()) || newVehicle.getStatus() == 3) { throw new ServiceException("新车辆保险或年检已过期,无法接单", I18nKeyConstant.ORDER_REASSIGN_NEW_VEHICLE_STATUS_INVALID); } if (Objects.isNull(newVehicle.getOperatingStatus()) || newVehicle.getOperatingStatus() != 1) { throw new ServiceException("新车辆运营状态异常,无法接单", I18nKeyConstant.ORDER_REASSIGN_NEW_VEHICLE_OPERATING_STATUS_INVALID); } if (Objects.isNull(newVehicle.getAcceptOrTest()) || newVehicle.getAcceptOrTest() != 1) { throw new ServiceException("新车辆当前不在接单模式", I18nKeyConstant.ORDER_REASSIGN_NEW_VEHICLE_NOT_IN_ACCEPT_MODE); } if (Objects.isNull(newVehicle.getAcceptOrTestStatus()) || newVehicle.getAcceptOrTestStatus() != 1) { throw new ServiceException("新车辆未开始接单", I18nKeyConstant.ORDER_REASSIGN_NEW_VEHICLE_NOT_START_ACCEPT); } // 6. 保存原车辆和车队信息 Long oldVehicleId = order.getVehicleId(); Long oldFleetId = order.getFleetId(); // 7. 查询新车辆关联的车队ID Long newFleetId = null; try { R fleetIdResult = xpFleetVehicleClient.getFleetIdByVehicleId(newVehicleId); if (Objects.nonNull(fleetIdResult) && Objects.nonNull(fleetIdResult.getData())) { newFleetId = fleetIdResult.getData(); log.info("查询到新车辆ID: {} 关联的车队ID: {}", newVehicleId, newFleetId); } else { log.warn("新车辆ID: {} 未关联任何车队,将使用原车队ID: {}", newVehicleId, oldFleetId); newFleetId = oldFleetId; } } catch (Exception e) { log.error("查询新车辆关联的车队ID失败,车辆ID: {},错误: {}", newVehicleId, e.getMessage()); newFleetId = oldFleetId; } // 8. 如果旧订单的车队ID为空,尝试查询旧车辆的车队ID if (Objects.isNull(oldFleetId) && Objects.nonNull(oldVehicleId)) { try { R oldFleetIdResult = xpFleetVehicleClient.getFleetIdByVehicleId(oldVehicleId); if (Objects.nonNull(oldFleetIdResult) && Objects.nonNull(oldFleetIdResult.getData())) { oldFleetId = oldFleetIdResult.getData(); log.info("补充查询到旧车辆ID: {} 关联的车队ID: {}", oldVehicleId, oldFleetId); } } catch (Exception e) { log.warn("查询旧车辆关联的车队ID失败,车辆ID: {},错误: {}", oldVehicleId, e.getMessage()); } } // 9. 更新订单的车辆和车队信息 order.setVehicleId(newVehicleId); if (Objects.nonNull(newFleetId)) { order.setFleetId(newFleetId); } this.updateById(order); // 10. 保存改派记录 XpOrderReassign orderReassign = new XpOrderReassign(); orderReassign.setOrderId(orderId); orderReassign.setUserId(SecurityUtils.getUserId()); orderReassign.setOperationTime(LocalDateTime.now()); orderReassign.setOldVehicleId(oldVehicleId); orderReassign.setNewVehicleId(newVehicleId); orderReassign.setOldFleetId(oldFleetId); orderReassign.setNewFleetId(newFleetId); orderReassign.setReassignReason( StringUtils.isNotBlank(reassignReason) ? reassignReason : "运营人员改派订单" ); orderReassign.setCreateBy(SecurityUtils.getLoginUser().getSysUser().getNickName()); xpOrderReassignService.save(orderReassign); log.info("订单改派成功,订单ID: {}, 原车辆ID: {}, 新车辆ID: {}, 原车队ID: {}, 新车队ID: {}, 操作人: {}", orderId, oldVehicleId, newVehicleId, oldFleetId, newFleetId, SecurityUtils.getUsername()); } @Override public XpPointInfoVo getUserQuickCapsule(String uid) { try { // 使用Redis的sorted set(排行榜)获取第一个元素 // 获取分数最高的元素(默认按分数从高到低排序) Set result = redisService.redisTemplate.opsForZSet().reverseRange("history_order_" + uid, 0, 0); if (result != null && !result.isEmpty()) { // 排行榜中存储的是JSON字符串 String jsonString = result.iterator().next().toString(); // 使用Hutool的JSON工具解析为XpPointInfoVo对象 return cn.hutool.json.JSONUtil.toBean(jsonString, XpPointInfoVo.class); } } catch (Exception e) { log.error("获取用户快捷胶囊失败: {}", e.getMessage(), e); } return null; } @Override public List getHistoryStartPoints(String uid, double lon, double lat) { // List xpOrders = this.baseMapper.selectList(new LambdaQueryWrapper().eq(XpOrder::getAppUserId, uid).orderByDesc(BaseModel::getCreateTime).last("LIMIT 10")); if (xpOrders.isEmpty()) { return Collections.emptyList(); } // 获取点位信息 List startPointsIds = xpOrders.stream().map(XpOrder::getStartPointId).collect(Collectors.toList()); R> r = xpPointClient.getHistoryStartPoints(new HistoryStartPointsDto(startPointsIds, lon, lat)); return r.getData(); } @Override public List getHistoryEndPoints(String uid, double lon, double lat) { // List xpOrders = this.baseMapper.selectList(new LambdaQueryWrapper().eq(XpOrder::getAppUserId, uid).orderByDesc(BaseModel::getCreateTime).last("LIMIT 10")); if (xpOrders.isEmpty()) { return Collections.emptyList(); } // 获取点位信息 List endPointsIds = xpOrders.stream().map(XpOrder::getEndPointId).collect(Collectors.toList()); R> r = xpPointClient.getHistoryEndPoints(new HistoryEndPointsDto(endPointsIds, lon, lat)); return r.getData(); } /** * 获取历史订单列表 * * @param query * @param uid * @return */ @Override public XpOrderHistoryVO getHistoryOrderList(XpHistoryOrderListQuery query, String uid) { Page page = new Page<>(query.getPageNum(), query.getPageSize()); List list = this.baseMapper.getHistoryOrderList(page, query, uid); XpOrderHistoryVO xpOrderHistoryVO = new XpOrderHistoryVO(); xpOrderHistoryVO.setTotal((int) page.getTotal()); // 根据time分组 Map> map = list.stream().collect(Collectors.groupingBy(XpOrderHistoryListVO::getTime)); List dateVOS = map.entrySet().stream().map(entry -> { XpOrderHistoryDateVO xpOrderHistoryDateVO = new XpOrderHistoryDateVO(); xpOrderHistoryDateVO.setDateLabel(entry.getKey().toString()); xpOrderHistoryDateVO.setOrders(entry.getValue()); return xpOrderHistoryDateVO; }).collect(Collectors.toList()); xpOrderHistoryVO.setList(dateVOS); return xpOrderHistoryVO; } /** * 取消订单 * * @param orderId * @param uid */ @Override @Transactional public void cancelOrder(Long orderId, String uid) { XpOrder order = this.getById(orderId); if (order == null) { throw new RuntimeException("订单不存在"); } if (!order.getAppUserId().toString().equals(uid)) { throw new RuntimeException("无权限取消该订单"); } if (order.getStatus() > 5 || order.getStatus() < 1) { throw new RuntimeException("订单状态错误"); } order.setStatus(0); // 组装调用车机参数 CancelTaxiPassengerVo cancelTaxiPassengerVo = new CancelTaxiPassengerVo(); Long vehicleId = order.getVehicleId(); R xpVehicleR = xpVehicleClient.queryById(vehicleId); XpVehicle xpVehicle = xpVehicleR.getData(); if (xpVehicle == null) { throw new RuntimeException("车辆不存在"); } cancelTaxiPassengerVo.setVin(xpVehicle.getVehicleVin()); cancelTaxiPassengerVo.setUid(order.getAppUserId().toString()); cancelTaxiPassengerVo.setOrderNo(order.getOrderNumber()); // TODO 加配置文件取 cancelTaxiPassengerVo.setAppId(""); BaseVehicleUserDto baseVehicleUserDto = userVehicleClient.cancelTaxiPassenger(cancelTaxiPassengerVo); if (baseVehicleUserDto.getCode() != 200) { throw new RuntimeException("取消订单失败"); } baseMapper.updateById(order); XpOrderCancel xpOrderCancel = new XpOrderCancel(); xpOrderCancel.setOrderId(orderId); xpOrderCancel.setUserId(Long.valueOf(uid)); xpOrderCancel.setCancelSource(1); xpOrderCancel.setCancelReason("用户取消订单"); xpOrderCancelService.save(xpOrderCancel); // 设置当天的取消次数缓存 Object cacheObject = redisService.getCacheObject("CANCEL_ORDER_" + uid); if (cacheObject == null) { redisService.setCacheCancelOrder("CANCEL_ORDER_" + uid, 1); } else { Integer i = Integer.valueOf(cacheObject.toString()); redisService.setCacheCancelOrder("CANCEL_ORDER_" + uid, i + 1); } } @Override @Transactional public void cancelOrder(String orderId) { XpOrder order = this.getById(orderId); if (order == null) { throw new RuntimeException("订单不存在"); } order.setStatus(0); baseMapper.updateById(order); XpOrderCancel xpOrderCancel = new XpOrderCancel(); xpOrderCancel.setOrderId(Long.valueOf(orderId)); xpOrderCancel.setUserId(1l); xpOrderCancel.setCancelSource(4); xpOrderCancel.setCancelReason("系统取消订单"); xpOrderCancelService.save(xpOrderCancel); } /** * 评价订单 * * @param dto * @param uid */ @Override @Transactional public void comment(XpOrderCommentDto dto, String uid) { XpOrder xpOrder = this.getById(dto.getOrderId()); if (xpOrder == null) { throw new RuntimeException("订单不存在"); } if (!xpOrder.getAppUserId().toString().equals(uid)) { throw new RuntimeException("无权限评价该订单"); } if (xpOrder.getStatus() != 9) { throw new RuntimeException("订单状态错误"); } xpOrder.setStatus(10); baseMapper.updateById(xpOrder); XpOrderEvaluate evaluate = new XpOrderEvaluate(); evaluate.setOrderId(xpOrder.getId()); evaluate.setVehicleId(xpOrder.getVehicleId()); evaluate.setAppUserId(Long.valueOf(uid)); evaluate.setOrderRating(dto.getOrderRating()); evaluate.setEvaluateTime(LocalDateTime.now()); evaluate.setFeedbackContent(dto.getFeedbackContent()); evaluate.setFeedbackPictures(dto.getFeedbackPictures()); evaluate.setFeedbackTypeId(dto.getFeedbackTypeId()); xpOrderEvaluateService.save(evaluate); } @Override public R updateOrderStatus(Long orderId, Integer status) { XpOrder xpOrder = this.getById(orderId); if (Objects.isNull(xpOrder)) { return R.fail("订单不存在"); } if (xpOrder.getStatus() == 0) { return R.fail("订单已取消"); } xpOrder.setStatus(status); switch (status) { case 4: // 添加取消订单定时任务job Map cancelJobMaps = new ImmutableMap.Builder(). put("id", xpOrder.getId()) .build(); QuartzManager.addJob( OrderCancelJob.class, (OrderCancelJob.name + xpOrder.getId()).toUpperCase(), TimeJobType.ORDER_CANCEL, DateUtils.localDateTimeToDate(LocalDateTime.now().plusMinutes(5)), cancelJobMaps ); // 车辆到达上车点时间 xpOrder.setArrivalTime(LocalDateTime.now()); break; case 5: // 删除定时任务job QuartzManager.removeJob((OrderCancelJob.name + xpOrder.getId()).toUpperCase(), TimeJobType.ORDER_CANCEL); break; case 6: // 行程开始时间 xpOrder.setTripBegin(LocalDateTime.now()); break; case 7: // 查询自动结束行程时间 Integer arriveTime = xpSystemSettingClient.getOrderFinishTime().getData(); // 添加自动结束行程定时任务job Map endTripJobMaps = new ImmutableMap.Builder(). put("id", xpOrder.getId()) .build(); QuartzManager.addJob( EndTripJob.class, (EndTripJob.name + xpOrder.getId()).toUpperCase(), TimeJobType.END_TRIP, DateUtils.localDateTimeToDate(LocalDateTime.now().plusMinutes(arriveTime)), endTripJobMaps ); break; } this.updateById(xpOrder); return R.ok(); } @Override public void endTrip(Long orderId, String uid) { XpOrder order = this.getById(orderId); if (order == null) { throw new RuntimeException("订单不存在"); } if (order.getStatus() != 7) { throw new RuntimeException("订单状态错误"); } order.setStatus(8); // 计算实际价格 // 根据订单所属开通城市id 查询定价 R pricingByCityId = xpPricingClient.getPricingByCityId(order.getCityId()); XpPricing pricing = pricingByCityId.getData(); if (pricing == null) { throw new ServiceException("当前城市未配置定价规则", I18nKeyConstant.ORDER_PRICING_NOT_EXISTS); } // 查询订单轨迹 List trajectories = mongoTemplate.find( new Query(Criteria.where("orderId").is(orderId)) .with(Sort.by(Sort.Direction.ASC, "locatetime")), OrderTrajectory.class ); // 计算等待时间 LocalDateTime boardingTime = order.getBoardingTime();// 上车时间 LocalDateTime arrivalTime = order.getArrivalTime();// 车辆到达上车点时间 // 行程开始时间 Integer waitTime =Math.abs( (int) Duration.between(boardingTime, arrivalTime).getSeconds()); // 计算订单总行驶距离,单位米 Double totalDistance = 0.00D; // 行程总时间 秒 LocalDateTime tripBegin = order.getTripBegin(); LocalDateTime tripEnd = order.getTripEnd(); long seconds = Duration.between(tripBegin, tripEnd).getSeconds(); for (OrderTrajectory trajectory : trajectories) { } // XpPricingCardVo xpPricingCardVo = xpPricingUtil.calculationPrice( // order.getAppUserId().toString(),JSON.parseObject(pricing.getBasicPricingJson()), // JSON.parseArray(pricing.getStartingFareJson()), // pricing.getDynamicPricingJson(), // pricing.getIsDynamicPrice(), // totalDistance, // seconds, // order.getCityId() // ); // Long id = pricing.getId(); // Integer pricingType = pricing.getPricingType(); // String basicPricingJson = pricing.getBasicPricingJson(); // String startingFareJson = pricing.getStartingFareJson(); // String dynamicPricingJson = pricing.getDynamicPricingJson(); // String priceRule = pricing.getPriceRule(); // // 根据计价规则计算费用 // BigDecimal startMoney = BigDecimal.ZERO;// 起步价 // BigDecimal mileageMoney = BigDecimal.ZERO;// 里程费 // BigDecimal durationMoney = BigDecimal.ZERO;// 时长费 // BigDecimal waitMoney = BigDecimal.ZERO;// 等待费 // BigDecimal longDistancemoney = BigDecimal.ZERO;// 远途费 // BigDecimal redPacketMoney = BigDecimal.ZERO;// 红包抵扣金额 // BigDecimal couponMoney = BigDecimal.ZERO;// 优惠券抵扣金额 // if (pricingType == 1) { // // } else { // // } // if (org.springframework.util.StringUtils.hasLength(basicPricingJson)) { // // } // if (org.springframework.util.StringUtils.hasLength(startingFareJson)) { // // } // if (org.springframework.util.StringUtils.hasLength(dynamicPricingJson)) { // // } // if (org.springframework.util.StringUtils.hasLength(priceRule)) { // // } // 计算完价格后 挑选优惠券 GetUserCouponDto getUserCouponDto = new GetUserCouponDto(); getUserCouponDto.setUserId(Long.valueOf(uid)); getUserCouponDto.setTotalAmount(order.getOrderMoney().doubleValue()); R userCouponVoR = xpAppUserCouponClient.getUserCoupon(getUserCouponDto); UserCouponVo userCouponVo = userCouponVoR.getData(); if (userCouponVo != null) { order.setCouponId(userCouponVo.getId()); order.setCouponMoney(BigDecimal.valueOf(userCouponVo.getMoney())); } order.setTripEnd(LocalDateTime.now()); baseMapper.updateById(order); // 解绑用户与车辆关系 CancelTaxiPassengerVo cancelTaxiPassengerVo = new CancelTaxiPassengerVo(); XpVehicle vehicle = xpVehicleClient.queryById(order.getVehicleId()).getData(); if (vehicle != null) { cancelTaxiPassengerVo.setVin(vehicle.getVehicleVin()); cancelTaxiPassengerVo.setUid(uid); cancelTaxiPassengerVo.setOrderNo(order.getOrderNumber()); // cancelTaxiPassengerVo.setAppId(); userVehicleClient.cancelTaxiPassenger(cancelTaxiPassengerVo); } } @Override public R fareBreakdown(Long orderId, String uid) { XpOrder order = this.getById(orderId); // 查询订单轨迹 List trajectories = mongoTemplate.find( new Query(Criteria.where("orderId").is(orderId)) .with(Sort.by(Sort.Direction.ASC, "locatetime")), OrderTrajectory.class ); // 计算订单总行驶距离,单位米 Double totalDistance = 0.00D; for (OrderTrajectory trajectory : trajectories) { } FareBreakdownVo fareBreakdownVo = new FareBreakdownVo(); // fareBreakdownVo.setStart(); // fareBreakdownVo.setMileage(); // fareBreakdownVo.setDuration(); // fareBreakdownVo.setLongDistance(); // fareBreakdownVo.setRedPacketMoney(); // fareBreakdownVo.setCouponMoney(); // fareBreakdownVo.setOrderMoney(); // BeanUtils.copyProperties(order, fareBreakdownVo); // fareBreakdownVo.setTotalKilometers( // BigDecimal.valueOf(order.getStartMileage() + order.getMileageKilometers() + order.getLongDistance()) // ); // LocalDateTime createTime = order.getCreateTime(); // LocalDateTime tripEnd = order.getTripEnd(); // // 计算行程时间 单位分钟 // // // 计算时间差 // Duration duration = Duration.between(createTime, tripEnd); // // // 转换为分钟并保留一位小数 // BigDecimal minutes = BigDecimal.valueOf(duration.getSeconds()) // .divide(BigDecimal.valueOf(60), 1, RoundingMode.HALF_UP); // // fareBreakdownVo.setTotalTime(minutes); return R.ok(fareBreakdownVo); } @Override public R payment(XpOrderPayDto dto, String uid) { // mvp版本不做支付 使用优惠券抵扣为0元订单处理 处理订单状态即可 XpOrder order = this.getById(dto.getOrderId()); order.setStatus(9); baseMapper.updateById(order); return R.ok(); } /** * 添加订单 * * @param xpOrder * @return */ @Override public boolean addOrder(XpOrder xpOrder) { int insert = this.baseMapper.insert(xpOrder); if (insert <= 0) { return false; } return true; } @Override public XpVehicle queryVehicleByOrderId(Long orderId, String uid) { XpOrder xpOrder = this.getById(orderId); XpVehicle xpVehicle = xpVehicleClient.queryById(xpOrder.getVehicleId()).getData(); return xpVehicle; } /** * 获取订单数量 * * @param dto * @return */ @Override public Integer getOrderNum(GetOrderNumDto dto) { Long num = this.baseMapper.selectCount(new LambdaQueryWrapper().eq(XpOrder::getCityId, dto.getOpenCityId()).between(BaseModel::getCreateTime, dto.getTimeStr(), new Date())); return num.intValue(); } @Override public void saveVehicleLocation(OrderTrajectory orderTrajectory) { // 查询订单信息 XpOrder xpOrder = this.getById(orderTrajectory.getOrderId()); // 计算距离 // 获取上一次定位 String oldLocation = redisService.getCacheObject(CacheConstants.ORDER_OLD_LOCATION + orderTrajectory.getOrderId()); if(StringUtils.isEmpty(oldLocation)){ // 首次定位 xpOrder.setMileageKilometers(0D); this.updateById(xpOrder); orderTrajectory.setDistance(0D); // 存储mongo数据 orderTrajectoryService.create(orderTrajectory); // 存储首次定位 redisService.setCacheObject(CacheConstants.ORDER_OLD_LOCATION + orderTrajectory.getOrderId(), orderTrajectory.getLocation()); }else { Map distance = GeodesyUtil.getDistance(orderTrajectory.getLocation(), oldLocation); if(null != distance){ Double distance1 = distance.get("WGS84"); if(distance1 > 50 && xpOrder.getStatus() == 6){//大于50米表示在移动,且在服务中的时候记录里程 xpOrder.setMileageKilometers(BigDecimal.valueOf(xpOrder.getMileageKilometers()).add(new BigDecimal(distance1)).setScale(2, RoundingMode.HALF_EVEN).doubleValue()); this.updateById(xpOrder); // 存储本次距离 orderTrajectory.setDistance(distance1); // 存储mongo数据 orderTrajectoryService.create(orderTrajectory); } }else{ log.error("调用高德计算距离出错"); } } } /** * 获取当前用户正在执行的订单 * * @param uid * @return */ @Override public XpOrderDetailVO getActiveOrder(String uid) { XpOrderDetailVO xpOrderDetailVO = new XpOrderDetailVO(); XpOrder xpOrder = this.baseMapper.selectOne(new LambdaQueryWrapper().eq(XpOrder::getAppUserId, uid).in(XpOrder::getStatus, Arrays.asList(3, 4, 5, 6, 7, 8)).orderByDesc(BaseModel::getCreateTime).last("LIMIT 1")); if (xpOrder == null) { return xpOrderDetailVO; } BeanUtils.copyProperties(xpOrder, xpOrderDetailVO); // 算时间 if (xpOrder.getStatus() == 1) { R r = xpPricingClient.getWaitTime(new LonLatDto(xpOrder.getStartLon(), xpOrder.getStartLat())); if (r.getCode() == R.SUCCESS) { xpOrderDetailVO.setEstimatedTime(r.getData()); } else { throw new RuntimeException(r.getMsg()); } } return xpOrderDetailVO; } }