package com.xiaopeng.order.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.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.account.UcUserInfo;
import com.xiaopeng.api.dto.account.UseUserCouponDto;
import com.xiaopeng.api.dto.automaticPilot.PushOrderDto;
import com.xiaopeng.api.dto.operation.CruisePointDto;
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.order.PopOrderDto;
import com.xiaopeng.api.dto.pushMessage.SendMessageDTO;
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.dto.vehicleConvoy.UpdateVehicleStateDto;
import com.xiaopeng.api.feignClient.account.UserResourcesClient;
import com.xiaopeng.api.feignClient.account.XpAppUserCouponClient;
import com.xiaopeng.api.feignClient.automaticPilot.OrderMissionClient;
import com.xiaopeng.api.feignClient.maintenance.XpTaskRuleClient;
import com.xiaopeng.api.feignClient.operation.XpCruisePlanClient;
import com.xiaopeng.api.feignClient.operation.XpPointClient;
import com.xiaopeng.api.feignClient.operation.XpPricingClient;
import com.xiaopeng.api.feignClient.operation.XpSystemSettingClient;
import com.xiaopeng.api.feignClient.pushMessage.XpPushMessageClient;
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.model.maintenance.XpTaskRule;
import com.xiaopeng.api.model.operation.*;
import com.xiaopeng.api.model.order.*;
import com.xiaopeng.api.model.vehicleConvoy.XpFleet;
import com.xiaopeng.api.model.vehicleConvoy.XpVehicle;
import com.xiaopeng.api.vo.OrderType;
import com.xiaopeng.api.vo.account.UserCouponVo;
import com.xiaopeng.api.vo.automaticPilot.VehiclePosition;
import com.xiaopeng.api.vo.automaticPilot.protobuf.*;
import com.xiaopeng.api.vo.operation.RouteVo;
import com.xiaopeng.api.vo.order.*;
import com.xiaopeng.api.vo.vehicleConvoy.AddTaxiPassengerVo;
import com.xiaopeng.api.vo.vehicleConvoy.CancelTaxiPassengerVo;
import com.xiaopeng.common.core.constant.CacheConstants;
import com.xiaopeng.common.core.constant.HttpStatus;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.core.enums.OrderStatusEnum;
import com.xiaopeng.common.core.enums.PushMessageTypeEnum;
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.dto.MileageDto;
import com.xiaopeng.order.job.base.QuartzManager;
import com.xiaopeng.order.job.base.TimeJobType;
import com.xiaopeng.order.job.jobs.AddCruiseMissionJob;
import com.xiaopeng.order.job.jobs.EndTripJob;
import com.xiaopeng.order.job.jobs.OrderCancelJob;
import com.xiaopeng.order.mangodb.model.OrderTrajectory;
import com.xiaopeng.order.mangodb.service.OrderTrajectoryService;
import com.xiaopeng.order.mapper.XpCruiseOrderMapper;
import com.xiaopeng.order.mapper.XpOrderDetailMapper;
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.XpOrderExcelQuery;
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.utils.TaxiPricingDemoService;
import com.xiaopeng.order.utils.RegionCodeFilterUtil;
import com.xiaopeng.order.utils.XpPricingUtil;
import com.xiaopeng.order.utils.holiday.HolidayUtil;
import com.xiaopeng.order.vo.OrderDetailVo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.shardingsphere.transaction.annotation.ShardingSphereTransactionType;
import org.apache.shardingsphere.transaction.core.TransactionType;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
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.text.SimpleDateFormat;
import java.time.*;
import java.util.*;
import java.util.concurrent.TimeUnit;
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 UserResourcesClient userResourcesClient;
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;
private final XpPushMessageClient xpPushMessageClient;
private final OrderMissionClient orderMissionClient;
private final XpOrderDetailMapper xpOrderDetailMapper;
private final XpCruiseOrderMapper xpCruiseOrderMapper;
private final XpTaskRuleClient xpTaskRuleClient;
private final XpCruisePlanClient xpCruisePlanClient;
private final XpPricingUtil xpPricingUtil;
@Autowired
private MongoTemplate mongoTemplate;
@Autowired
private HolidayUtil holidayUtil;
@Value("${robotaxi.appId:xp_robotaxi_api}")
private String appid;
@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) {
// 0. 车牌号筛选:先查车辆服务获取车辆ID列表
List vehicleIdsFilter = null;
if (StringUtils.isNotBlank(query.getPlateNumber())) {
R> vehicleResult = xpVehicleClient.listByPlateNumber(query.getPlateNumber());
if (vehicleResult == null || CollectionUtils.isEmpty(vehicleResult.getData())) {
return buildEmptyOrderPage(query);
}
vehicleIdsFilter = vehicleResult.getData().stream()
.map(XpVehicle::getId)
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(vehicleIdsFilter)) {
return buildEmptyOrderPage(query);
}
}
if (StringUtils.isNotBlank(query.getNickName())) {
//根据手机号查询用户信息
UcUserInfo userByMobile = userResourcesClient.getUserByMobile(query.getNickName());
if (userByMobile == null || userByMobile.getId() == null) {
return buildEmptyOrderPage(query);
}
query.setUid(userByMobile.getUid());
}
// 1. 构建查询条件
LambdaQueryWrapper wrapper = buildQueryWrapper(query);
if (CollUtil.isNotEmpty(vehicleIdsFilter)) {
wrapper.in(XpOrder::getVehicleId, vehicleIdsFilter);
}
wrapper.eq(Objects.nonNull(query.getUid()), XpOrder::getAppUserId, query.getUid());
wrapper.orderByDesc(XpOrder::getCreateTime);
// 2. 分页查询
Page page = new Page<>(query.getPageNum(), query.getPageSize());
Page pageResult = this.page(page, wrapper);
if (CollectionUtils.isEmpty(pageResult.getRecords())) {
return buildEmptyOrderPage(query);
}
// 3. 收集关联ID
Set userIds = new HashSet<>();
Set vehicleIds = new HashSet<>();
Set fleetIds = new HashSet<>();
for (XpOrder order : pageResult.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 userInfoMap = batchGetUsers(userIds);
Map vehicleMap = batchGetVehicles(vehicleIds);
Map fleetMap = batchGetFleets(fleetIds);
// 5. 组装 VO
List voList = pageResult.getRecords().stream()
.map(order -> convertToPageVO(order, userInfoMap, vehicleMap, fleetMap))
.collect(Collectors.toList());
// 6. 返回分页结果
PageInfo resultPage = new PageInfo<>(query.getPageNum(), query.getPageSize());
resultPage.setTotal(pageResult.getTotal());
resultPage.setRecords(voList);
return resultPage;
}
private PageInfo buildEmptyOrderPage(XpOrderPageQuery query) {
PageInfo resultPage = new PageInfo<>(query.getPageNum(), query.getPageSize());
resultPage.setTotal(0L);
resultPage.setRecords(new ArrayList<>());
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 userInfoMap = batchGetUsers(Collections.singleton(order.getAppUserId()));
if (CollUtil.isNotEmpty(userInfoMap)) {
// 注意:Map 的 key 是 String 类型,需要将 Long 转换为 String
UcUserInfo userInfo = userInfoMap.get(order.getAppUserId().toString());
if (Objects.nonNull(userInfo)) {
vo.setNickName(userInfo.getNickName());
vo.setPhoneNumber(userInfo.getMobile());
}
}
}
// 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.orderByDesc(XpOrderEvaluate::getCreateTime).last(" limit 0,1"));
if (Objects.nonNull(evaluate)) {
vo.setOrderRating(evaluate.getOrderRating());
}
// 7. 查询mongodb 该订单上传的经纬度轨迹信息(按订单id)
List trajectories = queryOrderTrajectories(id);
vo.setOrderPositionList(convertTrajectoriesToPositions(trajectories));
// 8. 起步价、里程费、时长费、远途费:按订单费用明细聚合
Map feeMap = sumOrderDetailMoneyByDetailType(id);
vo.setStartMoney(feeMap.getOrDefault(1, BigDecimal.ZERO));
vo.setMileageMoney(feeMap.getOrDefault(2, BigDecimal.ZERO));
vo.setDurationMoney(feeMap.getOrDefault(3, BigDecimal.ZERO));
vo.setLongDistancemoney(feeMap.getOrDefault(4, BigDecimal.ZERO));
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(XpOrderExcelQuery query) {
// 1. 构建查询条件
LambdaQueryWrapper wrapper = buildExcelQueryWrapper(query);
// 1.1 车牌号筛选:先查车辆服务获取车辆ID列表
if (StringUtils.isNotBlank(query.getPlateNumber())) {
R> vehicleResult = xpVehicleClient.listByPlateNumber(query.getPlateNumber());
if (vehicleResult == null || CollectionUtils.isEmpty(vehicleResult.getData())) {
return new ArrayList<>();
}
List vehicleIdsFilter = vehicleResult.getData().stream()
.map(XpVehicle::getId)
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(vehicleIdsFilter)) {
return new ArrayList<>();
}
wrapper.in(XpOrder::getVehicleId, vehicleIdsFilter);
}
if (StringUtils.isNotBlank(query.getNickName())) {
//根据手机号查询用户信息
UcUserInfo userByMobile = userResourcesClient.getUserByMobile(query.getNickName());
if (userByMobile == null || userByMobile.getId() == null) {
return new ArrayList<>();
}
query.setUid(userByMobile.getUid());
}
wrapper.eq(Objects.nonNull(query.getUid()), XpOrder::getAppUserId, query.getUid());
wrapper.orderByDesc(XpOrder::getCreateTime);
// 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 userInfoMap = batchGetUsers(userIds);
Map vehicleMap = batchGetVehicles(vehicleIds);
Map fleetMap = batchGetFleets(fleetIds);
// 5. 组装 VO
return orderList.stream()
.map(order -> convertToPageVO(order, userInfoMap, 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())
.eq(Objects.nonNull(query.getStatus()), XpOrder::getStatus, query.getStatus())
.eq(Objects.nonNull(query.getUid()), XpOrder::getAppUserId, query.getUid());
RegionCodeFilterUtil.resolveCityCodeFilter(query.getCityCode())
.ifPresent(filter -> {
if (RegionCodeFilterUtil.MatchType.LIKE_RIGHT.equals(filter.getMatchType())) {
wrapper.likeRight(XpOrder::getCityCode, filter.getValue());
} else {
wrapper.eq(XpOrder::getCityCode, filter.getValue());
}
});
if (Objects.nonNull(query.getStartTime())) {
wrapper.ge(XpOrder::getCreateTime, LocalDateTime.of(query.getStartTime(), LocalTime.MIN));
}
if (Objects.nonNull(query.getEndTime())) {
wrapper.le(XpOrder::getCreateTime, LocalDateTime.of(query.getEndTime(), LocalTime.MAX));
}
wrapper.orderByDesc(XpOrder::getCreateTime);
return wrapper;
}
private LambdaQueryWrapper buildExcelQueryWrapper(XpOrderExcelQuery 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())
.eq(Objects.nonNull(query.getStatus()), XpOrder::getStatus, query.getStatus())
.eq(Objects.nonNull(query.getUid()), XpOrder::getAppUserId, query.getUid());
RegionCodeFilterUtil.resolveCityCodeFilter(query.getCityCode())
.ifPresent(filter -> {
if (RegionCodeFilterUtil.MatchType.LIKE_RIGHT.equals(filter.getMatchType())) {
wrapper.likeRight(XpOrder::getCityCode, filter.getValue());
} else {
wrapper.eq(XpOrder::getCityCode, filter.getValue());
}
});
if (Objects.nonNull(query.getStartTime())) {
wrapper.ge(XpOrder::getCreateTime, LocalDateTime.of(query.getStartTime(), LocalTime.MIN));
}
if (Objects.nonNull(query.getEndTime())) {
wrapper.le(XpOrder::getCreateTime, LocalDateTime.of(query.getEndTime(), LocalTime.MAX));
}
if (CollUtil.isNotEmpty(query.getIds())) {
wrapper.in(XpOrder::getId, query.getIds());
}
wrapper.orderByDesc(XpOrder::getCreateTime);
return wrapper;
}
/**
* 批量获取用户信息
*/
private Map batchGetUsers(Set userIds) {
if (CollectionUtils.isEmpty(userIds)) {
return new HashMap<>();
}
return userResourcesClient.getUserListByUids(new ArrayList<>(userIds), false);
}
/**
* 批量获取车辆信息
*/
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<>();
}
@Override
@Transactional
@ShardingSphereTransactionType(TransactionType.BASE)
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 && currentStatus != 4) {
throw new ServiceException("当前订单状态不允许取消,只有待接单、待出发、接驾中状态的订单可以取消", I18nKeyConstant.ORDER_CANCEL_FAILED);
}
} else {
// 关闭订单:0=已取消,1=待接单,2=待出发,3=接驾中,4=待客上车,5=乘客已上车,6=服务中 状态可以关闭
if (currentStatus == 7 || currentStatus == 8 || currentStatus == 9 || currentStatus == 10) {
throw new ServiceException("当前订单状态不允许关闭", I18nKeyConstant.ORDER_CLOSE_FAILED);
}
}
// 4. 执行操作
if (type == 1) {
// 取消订单
cancelOrder(order);
} else {
// 关闭订单
closeOrder(order);
}
}
/**
* 转换为分页列表 VO
*/
private XpOrderPageVO convertToPageVO(XpOrder order,
Map userInfoMap,
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()) && userInfoMap != null && userInfoMap.containsKey(order.getAppUserId().toString())) {
UcUserInfo userInfo = userInfoMap.get(order.getAppUserId().toString());
vo.setNickName(userInfo.getNickName());
vo.setPhoneNumber(userInfo.getMobile());
}
// 车辆信息
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) {
redisService.removeByPattern(order.getQueueName(), order.getOrderNumber());
//判断还原车辆状态
if (null == order.getVehicleId()) {
Object cacheObject1 = redisService.getCacheObject("CREATE_ORDER:" + order.getId().toString());
if (null != cacheObject1) {
com.alibaba.fastjson.JSONObject jsonObject = com.alibaba.fastjson.JSON.parseObject(cacheObject1.toString());
String vin = jsonObject.getString("vin");
XpVehicle vehicle = xpVehicleClient.findByVehicleVin(vin).getData();
if (null != vehicle && 0 != vehicle.getState()) {
vehicle.setState(0);
xpVehicleClient.updateVehicleById(vehicle);
}
}
}
Long userId = order.getAppUserId();
if (order.getStatus() == 1) {
order.setStatus(0);
order.setAppUserId(null);
baseMapper.updateById(order);
// 消息推送
Map params = new HashMap<>();
Map content = new HashMap<>();
content.put("orderId", order.getId().toString());
content.put("status", OrderStatusEnum.CANCELED.getCode());
params.put("type", PushMessageTypeEnum.ORDER_STATUS.getDesc());
params.put("content", com.alibaba.fastjson2.JSONObject.toJSONString(content));
params.put("timestamp", new Date().getTime() + "");
String message = JSONObject.toJSONString(params);
SendMessageDTO sendMessageDTO = new SendMessageDTO();
sendMessageDTO.setMessage(message);
sendMessageDTO.setAppUserId(String.valueOf(userId));
xpPushMessageClient.sendStatus(sendMessageDTO);
return;
}
// 1. 更新订单状态为已取消(0)
lambdaUpdate().set(XpOrder::getStatus, 0)
.eq(XpOrder::getId, order.getId()).update();
// 2. 保存取消记录
XpOrderCancel orderCancel = new XpOrderCancel();
orderCancel.setOrderId(order.getId());
orderCancel.setCancelSource(3); // 3=运营人员(后台管理接口固定为运营人员)
orderCancel.setUserId(SecurityUtils.getUserId()); // 当前登录用户ID
orderCancel.setCancelReason("运营人员取消订单");
xpOrderCancelService.save(orderCancel);
// 消息推送
Map params = new HashMap<>();
Map content = new HashMap<>();
content.put("orderId", order.getId().toString());
content.put("status", OrderStatusEnum.CANCELED.getCode());
params.put("type", PushMessageTypeEnum.ORDER_STATUS.getDesc());
params.put("content", com.alibaba.fastjson2.JSONObject.toJSONString(content));
params.put("timestamp", new Date().getTime() + "");
String message = JSONObject.toJSONString(params);
SendMessageDTO sendMessageDTO = new SendMessageDTO();
sendMessageDTO.setMessage(message);
sendMessageDTO.setAppUserId(String.valueOf(userId));
xpPushMessageClient.sendStatus(sendMessageDTO);
log.info("运营人员取消订单成功,推送乘客消息完成");
//查询车辆信息
R xpVehicleR = xpVehicleClient.queryById(order.getVehicleId());
if (xpVehicleR.getCode() == HttpStatus.SUCCESS && Objects.nonNull(xpVehicleR.getData())) {
XpVehicle xpVehicle = xpVehicleR.getData();
// 组装调用车机参数
CancelTaxiPassengerVo cancelTaxiPassengerVo = new CancelTaxiPassengerVo();
cancelTaxiPassengerVo.setVin(xpVehicle.getVehicleVin());
cancelTaxiPassengerVo.setUid(userId.toString());
cancelTaxiPassengerVo.setOrderNo(order.getOrderNumber());
cancelTaxiPassengerVo.setAppId(appid);
BaseVehicleUserDto baseVehicleUserDto = userVehicleClient.cancelTaxiPassenger(cancelTaxiPassengerVo);
if (baseVehicleUserDto == null) {
// TODO 本地无法调通
// throw new RuntimeException("取消订单失败");
}
//还原车辆状态
if(1 == xpVehicle.getState()){
xpVehicle.setState(0);
xpVehicleClient.updateVehicleById(xpVehicle);
}
// 下发命令
PushOrderDto pushOrderDto = new PushOrderDto();
pushOrderDto.setOrderId(order.getId().toString());
pushOrderDto.setVin(xpVehicle.getVehicleVin());
pushOrderDto.setOperation_type(OrderOperationType.CLOUD_CANCEL_ORDER);
orderMissionClient.cancelOrder(pushOrderDto);
log.info("运营人员取消订单成功,开始执行巡游订单 {}", order.getId());
//车辆继续巡游
addCruiseMission(xpVehicle);
} else {
log.info("查询车辆信息失败,订单ID: {}, 下发取消订单命令失败", order.getId());
}
log.info("订单取消成功,订单ID: {}, 操作人: {}", order.getId(), SecurityUtils.getUsername());
}
/**
* 关闭订单
*/
private void closeOrder(XpOrder order) {
// 更新订单状态为已完成(10)
this.lambdaUpdate().set(XpOrder::getStatus, 8)
.eq(XpOrder::getId, order.getId()).update();
// 消息推送
Map params = new HashMap<>();
Map content = new HashMap<>();
content.put("orderId", order.getId().toString());
content.put("status", OrderStatusEnum.APPROVAL_REJECTION.getCode());
params.put("type", PushMessageTypeEnum.ORDER_STATUS.getDesc());
params.put("content", com.alibaba.fastjson2.JSONObject.toJSONString(content));
params.put("timestamp", new Date().getTime() + "");
String message = JSONObject.toJSONString(params);
SendMessageDTO sendMessageDTO = new SendMessageDTO();
sendMessageDTO.setMessage(message);
sendMessageDTO.setAppUserId(String.valueOf(order.getAppUserId()));
xpPushMessageClient.sendStatus(sendMessageDTO);
//结束行程
endTrip(order.getId(), order.getAppUserId().toString());
log.info("运营人员取消订单成功,推送乘客消息完成");
log.info("订单关闭成功,订单ID: {}, 操作人: {}", order.getId(), SecurityUtils.getUsername());
}
/**
* 改派订单
*/
@Override
@Transactional
@ShardingSphereTransactionType(TransactionType.BASE)
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 != 2 && currentStatus != 3 && currentStatus != 4) {
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);
}
if (Objects.isNull(newVehicle.getState()) || newVehicle.getState() != 0) {
throw new ServiceException("新车辆已接单,请重新选择", I18nKeyConstant.ORDER_REASSIGN_NEW_VEHICLE_NOT_FREE);
}
// 6. 保存原车辆和车队信息
Long oldVehicleId = order.getVehicleId();
Long oldFleetId = order.getFleetId();
// 6.1 处理原车辆:取消订单指令、解绑乘客、更新状态、创建返程巡游订单
if (Objects.nonNull(oldVehicleId)) {
try {
R oldVehicleResult = xpVehicleClient.queryById(oldVehicleId);
if (oldVehicleResult != null && oldVehicleResult.getCode() == HttpStatus.SUCCESS && oldVehicleResult.getData() != null) {
XpVehicle oldVehicle = oldVehicleResult.getData();
// 6.1.1 取消原订单指令
PushOrderDto cancelDto = new PushOrderDto();
cancelDto.setOrderId(order.getId().toString());
cancelDto.setVin(oldVehicle.getVehicleVin());
cancelDto.setOperation_type(OrderOperationType.CLOUD_CANCEL_ORDER);
orderMissionClient.cancelOrder(cancelDto);
log.info("已取消原车辆订单指令,订单ID: {}, VIN: {}", orderId, oldVehicle.getVehicleVin());
// 6.1.2 解绑乘客与原车辆关系
if (Objects.nonNull(order.getAppUserId())) {
CancelTaxiPassengerVo cancelVo = new CancelTaxiPassengerVo();
cancelVo.setVin(oldVehicle.getVehicleVin());
cancelVo.setUid(order.getAppUserId().toString());
cancelVo.setOrderNo(order.getOrderNumber());
cancelVo.setAppId(appid);
userVehicleClient.cancelTaxiPassenger(cancelVo);
log.info("已解绑乘客与原车辆关系,用户ID: {}, VIN: {}", order.getAppUserId(), oldVehicle.getVehicleVin());
}
// 6.1.3 修改原车辆状态为空闲
oldVehicle.setState(0);
xpVehicleClient.updateVehicleById(oldVehicle);
log.info("已将原车辆状态设为空闲,车辆ID: {}", oldVehicleId);
// 6.1.4 为原车辆创建返程巡游订单
createReturnCruiseOrder(oldVehicle, order);
} else {
log.warn("查询原车辆信息失败,车辆ID: {}", oldVehicleId);
}
} catch (Exception e) {
log.error("处理原车辆失败,车辆ID: {},错误: {}", oldVehicleId, e.getMessage());
}
}
// 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());
// 11. 处理新车辆:绑定乘客、下发订单、更新状态
try {
// 11.1 绑定乘客与新车辆关系
if (Objects.nonNull(order.getAppUserId())) {
AddTaxiPassengerVo addVo = new AddTaxiPassengerVo();
addVo.setVin(newVehicle.getVehicleVin());
addVo.setUid(order.getAppUserId().toString());
addVo.setOrderNo(order.getOrderNumber());
addVo.setAppId(appid); // TODO 获取appId
userVehicleClient.addTaxiPassenger(addVo);
log.info("已绑定乘客与新车辆关系,用户ID: {}, VIN: {}", order.getAppUserId(), newVehicle.getVehicleVin());
}
// 11.2 下发订单到新车辆
PushOrderDto pushOrderDto = new PushOrderDto();
pushOrderDto.setVin(newVehicle.getVehicleVin());
pushOrderDto.setOrderId(order.getId().toString());
pushOrderDto.setStartLatitude(order.getStartLat());
pushOrderDto.setStartLongitude(order.getStartLon());
pushOrderDto.setEndLatitude(order.getEndLat());
pushOrderDto.setEndLongitude(order.getEndLon());
pushOrderDto.setOrderType(OrderType.USER_ORDER);
pushOrderDto.setStage(1);
// 调用推送服务 orderId + carVin
orderMissionClient.pushOrder(pushOrderDto);
log.info("已下发订单到新车辆,订单ID: {}, VIN: {}", orderId, newVehicle.getVehicleVin());
// 11.3 更新新车辆状态为已接单
newVehicle.setState(1);
xpVehicleClient.updateVehicleById(newVehicle);
log.info("已将新车辆状态设为已接单,车辆ID: {}", newVehicleId);
} catch (Exception e) {
log.error("处理新车辆失败,车辆ID: {},错误: {}", newVehicleId, e.getMessage());
}
}
/**
* 为原车辆创建返程巡游订单
*
* 改派时,原车辆需要返回巡游路线:
* - 若最新巡游订单状态为已终止(status=2),返回原出发点
* - 若最新巡游订单状态为已完成(status=3),返回原终点
* - 其他状态,默认返回原出发点
*
*
* @param oldVehicle 原车辆信息
* @param order 当前订单信息
*/
private void createReturnCruiseOrder(XpVehicle oldVehicle, XpOrder order) {
try {
// 查询该车辆最新的一条巡游订单
XpCruiseOrder lastCruise = xpCruiseOrderMapper.selectOne(
new LambdaQueryWrapper()
.eq(XpCruiseOrder::getVehicleId, oldVehicle.getId())
.orderByDesc(BaseModel::getCreateTime)
.last("limit 1")
);
if (lastCruise == null) {
log.warn("原车辆没有巡游订单记录,跳过创建返程订单,车辆ID: {}", oldVehicle.getId());
return;
}
XpCruiseOrder returnOrder = new XpCruiseOrder();
returnOrder.setVehicleId(oldVehicle.getId());
returnOrder.setVehicleVin(oldVehicle.getVehicleVin());
returnOrder.setFleetId(oldVehicle.getFleetId());
returnOrder.setOrderType(2); // 返程单
returnOrder.setStatus(1); // 进行中
returnOrder.setLastOrderId(lastCruise.getId());
returnOrder.setCreateTime(LocalDateTime.now());
returnOrder.setMissionId(lastCruise.getMissionId());
// 返程起点 = 当前订单的终点(车辆当前位置附近)
returnOrder.setStartPointId(order.getEndPointId());
returnOrder.setStartPointName(order.getEndPointName());
returnOrder.setStartLon(order.getEndLon());
returnOrder.setStartLat(order.getEndLat());
// 根据最新巡游订单状态决定返程终点
if (lastCruise.getStatus() != null && lastCruise.getStatus() == 2) {
// 已终止(status=2) -> 返回原出发点
returnOrder.setEndPointId(lastCruise.getStartPointId());
returnOrder.setEndPointName(lastCruise.getStartPointName());
returnOrder.setEndLon(lastCruise.getStartLon());
returnOrder.setEndLat(lastCruise.getStartLat());
returnOrder.setOrderReverse(lastCruise.getOrderReverse());
log.info("巡游订单状态为已终止,返回原出发点");
} else if (lastCruise.getStatus() != null && lastCruise.getStatus() == 3) {
// 已完成(status=3) -> 返回原终点
returnOrder.setEndPointId(lastCruise.getEndPointId());
returnOrder.setEndPointName(lastCruise.getEndPointName());
returnOrder.setEndLon(lastCruise.getEndLon());
returnOrder.setEndLat(lastCruise.getEndLat());
returnOrder.setOrderReverse(lastCruise.getOrderReverse());
log.info("巡游订单状态为已完成,返回原终点");
} else {
// 其他状态(进行中等),默认返回原出发点
returnOrder.setEndPointId(lastCruise.getStartPointId());
returnOrder.setEndPointName(lastCruise.getStartPointName());
returnOrder.setEndLon(lastCruise.getStartLon());
returnOrder.setEndLat(lastCruise.getStartLat());
returnOrder.setOrderReverse(lastCruise.getOrderReverse());
log.info("巡游订单状态为{},默认返回原出发点", lastCruise.getStatus());
}
xpCruiseOrderMapper.insert(returnOrder);
// 下发巡游订单到原车辆
PushOrderDto pushOrderDto = new PushOrderDto();
pushOrderDto.setVin(oldVehicle.getVehicleVin());
pushOrderDto.setOrderId("XU" + returnOrder.getId());
pushOrderDto.setStartLatitude(returnOrder.getStartLat());
pushOrderDto.setStartLongitude(returnOrder.getStartLon());
pushOrderDto.setEndLatitude(returnOrder.getEndLat());
pushOrderDto.setEndLongitude(returnOrder.getEndLon());
pushOrderDto.setOrderType(OrderType.CRUISE_ORDER);
pushOrderDto.setStage(2);
// 调用推送服务 orderId + carVin
orderMissionClient.pushOrder(pushOrderDto);
log.info("为原车辆创建返程巡游订单成功,订单ID: {}, 车辆ID: {}", returnOrder.getId(), oldVehicle.getId());
} catch (Exception e) {
log.error("创建返程巡游订单失败,车辆ID: {},错误: {}", oldVehicle.getId(), e.getMessage());
}
}
@Override
public XpPointInfoVo getUserQuickCapsule(String uid) {
try {
// 使用Redis的sorted set(排行榜)获取第一个元素
// 获取分数最高的元素(默认按分数从高到低排序)
Set