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 com.alibaba.fastjson2.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.automaticPilot.VehicleEventDTO;
import com.xiaopeng.api.dto.map.OrderRouteAndFareDto;
import com.xiaopeng.api.dto.map.RouteAndFareDto;
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.feignClient.account.UserResourcesClient;
import com.xiaopeng.api.feignClient.account.XpAppUserCouponClient;
import com.xiaopeng.api.feignClient.automaticPilot.OrderMissionClient;
import com.xiaopeng.api.feignClient.automaticPilot.VehicleBuriedPointClient;
import com.xiaopeng.api.feignClient.maintenance.XpTaskRuleClient;
import com.xiaopeng.api.feignClient.map.XpMapClient;
import com.xiaopeng.api.feignClient.operation.*;
import com.xiaopeng.api.feignClient.pushMessage.XpPushMessageClient;
import com.xiaopeng.api.feignClient.vehicleConvoy.*;
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.OrderOperationType;
import com.xiaopeng.api.vo.automaticPilot.protobuf.OrderStatus;
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.Constants;
import com.xiaopeng.common.core.constant.HttpStatus;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.core.enums.CancelOrderSceneEnum;
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.*;
import com.xiaopeng.order.utils.GDMapGeocodingUtil;
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.geo.GeoJsonPoint;
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 javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.time.*;
import java.util.*;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
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;
private final GDMapGeocodingUtil gdMapGeocodingUtil;
private final VehicleBuriedPointClient vehicleBuriedPointClient;
private final IXpTestOrderService xpTestOrderService;
private final XpMapClient xpMapClient;
@Resource
private XpRoutePlanClient xpRoutePlanClient;
@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.fastjson2.JSONObject jsonObject = com.alibaba.fastjson2.JSON.parseObject(cacheObject1.toString());
String vin = jsonObject.getString("vin");
XpVehicle vehicle = xpVehicleClient.findByVehicleVin(vin).getData();
if (null != vehicle && 0 != vehicle.getState()) {
XpVehicle vehicle1 = new XpVehicle();
vehicle1.setId(vehicle.getId());
vehicle1.setState(0);
xpVehicleClient.updateVehicleById(vehicle1);
log.info("管理后台取消订单,车辆状态修改0 {}", vehicle.getPlateNumber());
}
}
}
Long userId = order.getAppUserId();
if (order.getStatus() == 1) {
order.setStatus(0);
order.setAppUserId(null);
baseMapper.updateById(order);
redisService.setCacheObject("ORDER_STATUS:" + order.getId(), 0, 24L, TimeUnit.HOURS);
// 消息推送
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", 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));
R stringR = xpPushMessageClient.sendStatus(sendMessageDTO);
//推送失败,重试3次
if(stringR.getCode() != Constants.SUCCESS){
ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());
executor.execute(() -> {
for (int i = 0; i < 3; i++) {
R stringR1 = xpPushMessageClient.sendStatus(sendMessageDTO);
if(stringR1.getCode() == Constants.SUCCESS){
break;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
log.info("运维人员取消订单推送订单状态消息失败:{} {} {}", stringR1.getMsg(), JSON.toJSONString(sendMessageDTO), order.getOrderNumber());
}
});
}
return;
}
// 1. 更新订单状态为已取消(0)
lambdaUpdate().set(XpOrder::getStatus, 0)
.eq(XpOrder::getId, order.getId()).update();
redisService.setCacheObject("ORDER_STATUS:" + order.getId(), 0, 24L, TimeUnit.HOURS);
// 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", 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));
R stringR = xpPushMessageClient.sendStatus(sendMessageDTO);
//推送失败,重试3次
if(stringR.getCode() != Constants.SUCCESS){
ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());
executor.execute(() -> {
for (int i = 0; i < 3; i++) {
R stringR1 = xpPushMessageClient.sendStatus(sendMessageDTO);
if(stringR1.getCode() == Constants.SUCCESS){
break;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
log.info("运维人员取消订单推送订单状态消息失败:{} {} {}", stringR1.getMsg(), JSON.toJSONString(sendMessageDTO), order.getOrderNumber());
}
});
}
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 vehicle1 = new XpVehicle();
vehicle1.setId(xpVehicle.getId());
vehicle1.setState(0);
xpVehicleClient.updateVehicleById(vehicle1);
log.info("管理后台取消订单,车辆状态修改0 {}", xpVehicle.getPlateNumber());
}
// 下发命令
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.getId());
} else {
log.info("查询车辆信息失败,订单ID: {}, 下发取消订单命令失败", order.getId());
}
log.info("订单取消成功,订单ID: {}, 操作人: {}", order.getId(), SecurityUtils.getUsername());
}
/**
* 关闭订单
*/
private void closeOrder(XpOrder order) {
XpVehicle xpVehicle = xpVehicleClient.queryById(order.getVehicleId()).getData();
order.setStatus(7);
order.setGetoffTime(LocalDateTime.now());
// 解析下车地点
VehiclePosition vehiclePosition = mongoTemplate.findOne(new Query(Criteria.where("vin").is(xpVehicle.getVehicleVin())), VehiclePosition.class);
if(Objects.nonNull(vehiclePosition)){
GeoJsonPoint point = vehiclePosition.getPoint();
if(Objects.nonNull(point)){
double x = point.getX();
double y = point.getY();
Map geocode = gdMapGeocodingUtil.geocode(String.valueOf(x), String.valueOf(y));
order.setGetoffAddress(geocode.get("address"));
}
}
//计算行驶总距离和时长
List orderTrajectoryList = mongoTemplate.find(new Query().addCriteria(Criteria.where("orderId").is(order.getId())), OrderTrajectory.class);
Double reduce = orderTrajectoryList.stream().map(OrderTrajectory::getDistance).reduce(0.0, Double::sum);
order.setMileageKilometers(new BigDecimal(reduce).divide(new BigDecimal(1000)).setScale(4, RoundingMode.HALF_EVEN).doubleValue());
long duration = LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8")) - order.getTripBegin().toEpochSecond(ZoneOffset.of("+8"));
order.setDuration(new BigDecimal(duration).divide(new BigDecimal(60), 2, RoundingMode.HALF_EVEN).doubleValue());
Long appUserId = order.getAppUserId();
order.setAppUserId(null);
this.updateById(order);
redisService.setCacheObject("ORDER_STATUS:" + order.getId(), 7, 24L, TimeUnit.HOURS);
// 消息推送
Map params1 = new HashMap<>();
Map content1 = new HashMap<>();
content1.put("orderId", order.getId().toString());
content1.put("status", OrderStatusEnum.SEE_OFF_GUESTS.getCode());
params1.put("type", PushMessageTypeEnum.ORDER_STATUS.getDesc());
params1.put("content", JSON.toJSONString(content1));
params1.put("timestamp", new Date().getTime() + "");
String message1 = JSON.toJSONString(params1);
SendMessageDTO sendMessageDTO1 = new SendMessageDTO();
sendMessageDTO1.setMessage(message1);
sendMessageDTO1.setAppUserId(appUserId.toString());
R stringR1 = xpPushMessageClient.sendStatus(sendMessageDTO1);
//推送失败,重试3次
if(stringR1.getCode() != Constants.SUCCESS){
ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());
executor.execute(() -> {
for (int i = 0; i < 3; i++) {
R stringR2 = xpPushMessageClient.sendStatus(sendMessageDTO1);
if(stringR2.getCode() == Constants.SUCCESS){
break;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
log.info("运维人员关闭订单推送订单状态消息失败:{} {} {}", stringR2.getMsg(), JSON.toJSONString(sendMessageDTO1), order.getOrderNumber());
}
});
}
// 推送倒计时消息
Map params = new HashMap<>();
Map content = new HashMap<>();
content.put("orderId", order.getId().toString());
content.put("isOpen", 1);
params.put("type", PushMessageTypeEnum.OPEN_TRUNK.getDesc());
params.put("content", JSON.toJSONString(content));
params.put("timestamp", new Date().getTime() + "");
String message = JSON.toJSONString(params);
SendMessageDTO sendMessageDTO = new SendMessageDTO();
sendMessageDTO.setMessage(message);
sendMessageDTO.setAppUserId(appUserId.toString());
R stringR = xpPushMessageClient.sendStatus(sendMessageDTO);
//推送失败,重试3次
if(stringR.getCode() != Constants.SUCCESS){
ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());
executor.execute(() -> {
for (int i = 0; i < 3; i++) {
R stringR2 = xpPushMessageClient.sendStatus(sendMessageDTO);
if(stringR2.getCode() == Constants.SUCCESS){
break;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
log.info("推送倒计时消息失败:{} {} {}", stringR2.getMsg(), JSON.toJSONString(sendMessageDTO), order.getOrderNumber());
}
});
}
//给XPU推送订单取消的报文
PushOrderDto pushOrderDto = new PushOrderDto();
pushOrderDto.setOrderId(order.getId().toString());
pushOrderDto.setVin(xpVehicle.getVehicleVin());
pushOrderDto.setOperation_type(OrderOperationType.CLOUD_CANCEL_ORDER);
orderMissionClient.cancelOrder(pushOrderDto);
//结束行程,修改状态和计算费用
endTrip(order.getId(), appUserId.toString());
// 消息推送
Map params2 = new HashMap<>();
Map content2 = new HashMap<>();
content2.put("orderId", order.getId().toString());
content2.put("status", OrderStatusEnum.APPROVAL_REJECTION.getCode());
params2.put("type", PushMessageTypeEnum.ORDER_STATUS.getDesc());
params2.put("content", JSON.toJSONString(content2));
params2.put("timestamp", new Date().getTime() + "");
String message2 = JSON.toJSONString(params2);
SendMessageDTO sendMessageDTO2 = new SendMessageDTO();
sendMessageDTO2.setMessage(message2);
sendMessageDTO2.setAppUserId(appUserId.toString());
R stringR2 = xpPushMessageClient.sendStatus(sendMessageDTO2);
//推送失败,重试3次
if(stringR2.getCode() != Constants.SUCCESS){
ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());
executor.execute(() -> {
for (int i = 0; i < 3; i++) {
R stringR3 = xpPushMessageClient.sendStatus(sendMessageDTO2);
if(stringR3.getCode() == Constants.SUCCESS){
break;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
log.info("运维人员关闭订单推送订单状态消息失败:{} {} {}", stringR3.getMsg(), JSON.toJSONString(sendMessageDTO1), order.getOrderNumber());
}
});
}
//创建定时任务执行延迟驶离终点和下发巡游单
XpPoint point = xpPointClient.queryById(order.getEndPointId()).getData();
Map jobMaps =
new ImmutableMap.Builder()
.put("id", order.getId().toString())
.put("appid", appid)
.build();
QuartzManager.addJob(
AddCruiseMissionJob.class,
(AddCruiseMissionJob.name + order.getId()).toUpperCase(),
TimeJobType.ADD_CRUISE_MISSION,
DateUtils.localDateTimeToDate(LocalDateTime.now().plusMinutes(point.getParkingTimeLimit())),
jobMaps
);
}
/**
* 改派订单
*/
@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);
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.setTaskId(IdUtil.getSnowflakeNextIdStr());
cancelDto.setOrderId(order.getId().toString());
cancelDto.setVin(oldVehicle.getVehicleVin());
cancelDto.setOperation_type(OrderOperationType.CLOUD_CANCEL_ORDER);
cancelDto.setCancelScene(CancelOrderSceneEnum.REASSIGN_OLD_VEHICLE.getCode());
orderMissionClient.cancelOrder(cancelDto);
log.info("已取消原车辆订单指令,订单ID: {}, VIN: {}, taskId: {}, cancelScene: {}",
orderId, oldVehicle.getVehicleVin(), cancelDto.getTaskId(), cancelDto.getCancelScene());
// 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 修改原车辆状态为空闲
XpVehicle vehicle1 = new XpVehicle();
vehicle1.setId(oldVehicle.getId());
vehicle1.setState(0);
xpVehicleClient.updateVehicleById(vehicle1);
log.info("管理后台改派订单,车辆状态修改0 {}", oldVehicle.getPlateNumber());
log.info("已将原车辆状态设为空闲,车辆ID: {}", oldVehicleId);
// 6.1.4 为原车辆创建返程巡游订单
createReturnCruiseOrder(oldVehicle.getId(), 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);
}
//重置订单状态为【待接单】
order.setStatus(1);
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.setEndPoiName(order.getEndPointName());
pushOrderDto.setOrderType(OrderType.USER_ORDER);
pushOrderDto.setStage(1);
// 调用推送服务 orderId + carVin
orderMissionClient.pushOrder(pushOrderDto);
log.info("已下发订单到新车辆,订单ID: {}, VIN: {}", orderId, newVehicle.getVehicleVin());
// 11.3 更新新车辆状态为已接单
XpVehicle vehicle1 = new XpVehicle();
vehicle1.setId(newVehicle.getId());
vehicle1.setState(1);
xpVehicleClient.updateVehicleById(vehicle1);
log.info("管理后台改派订单,车辆状态修改1 {}", newVehicle.getPlateNumber());
log.info("已将新车辆状态设为已接单,车辆ID: {}", newVehicleId);
//12.向小程序用户推送订单改派通知
Map params = new HashMap<>();
Map content = new HashMap<>();
content.put("orderId", order.getId().toString());
content.put("status", order.getStatus());
params.put("type", PushMessageTypeEnum.ORDER_REASSIGN.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);
} catch (Exception e) {
log.error("处理新车辆失败,车辆ID: {},错误: {}", newVehicleId, e.getMessage());
}
}
/**
* 为原车辆创建返程巡游订单
*
* 改派时,原车辆需要返回巡游路线:
* - 若最新巡游订单状态为已终止(status=2),返回原出发点
* - 若最新巡游订单状态为已完成(status=3),返回原终点
* - 其他状态,默认返回原出发点
*
*
* @param order 当前订单信息
*/
private void createReturnCruiseOrder(Long vehicleId, XpOrder order) {
XpVehicle oldVehicle = xpVehicleClient.queryById(vehicleId).getData();
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, Long startPointId) {
try {
Long userId = Long.valueOf(uid);
// 新用户或历史样本过少时直接隐藏胶囊,避免偶发订单干扰推荐。
Long validHistoryCount = this.baseMapper.countUserValidHistoryOrders(userId);
if (validHistoryCount == null || validHistoryCount < 3) {
return null;
}
XpPoint startPoint = getPointById(startPointId);
if (startPoint == null) {
log.warn("获取用户快捷胶囊失败,起点不存在,uid:{}, startPointId:{}", uid, startPointId);
return null;
}
Integer minOrderDistance = getMinOrderDistanceSafely(uid, startPointId);
if (minOrderDistance == null) {
return null;
}
LocalDateTime now = LocalDateTime.now();
List recentOrders = this.baseMapper.listUserRecentValidOrders(userId, now.minusDays(30));
if (CollectionUtils.isEmpty(recentOrders)) {
return null;
}
// 先走“同日别 + 当前时间前后1小时”的场景推荐,命不中再退化到30天高频。
RecommendationCandidate sceneCandidate = buildSceneCandidate(recentOrders, now, startPoint, minOrderDistance);
if (sceneCandidate != null) {
return sceneCandidate.getPointInfo();
}
RecommendationCandidate fallbackCandidate = buildHighFrequencyCandidate(recentOrders, startPoint, minOrderDistance);
if (fallbackCandidate != null) {
return fallbackCandidate.getPointInfo();
}
} catch (Exception e) {
log.error("获取用户快捷胶囊失败: {}", e.getMessage(), e);
}
return null;
}
private RecommendationCandidate buildSceneCandidate(List recentOrders, LocalDateTime now, XpPoint startPoint, Integer minOrderDistance) {
DaySceneType currentSceneType = resolveDaySceneType(now.toLocalDate());
Set targetHours = buildTargetHours(now.getHour());
List sceneOrders = recentOrders.stream()
.filter(order -> isSameScene(order, currentSceneType, targetHours))
.collect(Collectors.toList());
List candidates = buildCandidates(sceneOrders, startPoint, minOrderDistance);
if (candidates.isEmpty()) {
return null;
}
RecommendationCandidate top1 = candidates.get(0);
int secondCount = candidates.size() > 1 ? candidates.get(1).getCount() : 0;
// 只有 top1 至少命中2次且严格领先第二名,才认为“场景规律”足够明显。
if (top1.getCount() >= 2 && top1.getCount() > secondCount) {
return top1;
}
return null;
}
private RecommendationCandidate buildHighFrequencyCandidate(List recentOrders, XpPoint startPoint, Integer minOrderDistance) {
List candidates = buildCandidates(recentOrders, startPoint, minOrderDistance);
if (candidates.isEmpty()) {
return null;
}
return candidates.get(0);
}
private List buildCandidates(List orders, XpPoint startPoint, Integer minOrderDistance) {
if (CollectionUtils.isEmpty(orders)) {
return Collections.emptyList();
}
Map candidateMap = new HashMap<>();
for (XpOrder order : orders) {
Long endPointId = order.getEndPointId();
if (endPointId == null || endPointId.equals(startPoint.getId())) {
continue;
}
// 候选点位在创建阶段就完成“点位存在 + 距离阈值”过滤,后续只累加频次。
RecommendationCandidate candidate = candidateMap.computeIfAbsent(endPointId, id -> createCandidate(id, startPoint, minOrderDistance));
if (candidate == null || !candidate.isAvailable()) {
continue;
}
candidate.increment(order.getCreateTime());
}
return candidateMap.values().stream()
.filter(Objects::nonNull)
.filter(RecommendationCandidate::isAvailable)
.filter(candidate -> candidate.getCount() > 0)
.sorted(Comparator.comparingInt(RecommendationCandidate::getCount).reversed()
.thenComparing(RecommendationCandidate::getLatestCreateTime, Comparator.nullsLast(Comparator.reverseOrder())))
.collect(Collectors.toList());
}
private RecommendationCandidate createCandidate(Long pointId, XpPoint startPoint, Integer minOrderDistance) {
XpPoint point = getPointById(pointId);
if (point == null) {
return null;
}
Double distance = getStraightDistance(startPoint, point);
if (distance == null || distance <= minOrderDistance) {
return RecommendationCandidate.unavailable();
}
XpPointInfoVo pointInfoVo = new XpPointInfoVo();
pointInfoVo.setId(point.getId());
pointInfoVo.setPointName(point.getPointName());
pointInfoVo.setPointLon(point.getPointLon());
pointInfoVo.setPointLat(point.getPointLat());
return RecommendationCandidate.available(pointInfoVo);
}
private Integer getMinOrderDistanceSafely(String uid, Long startPointId) {
R result = xpSystemSettingClient.getMinOrderDistance();
if (result == null || result.getCode() != R.SUCCESS || result.getData() == null) {
log.error("获取用户快捷胶囊失败,最小下单距离配置异常,uid:{}, startPointId:{}, result:{}", uid, startPointId, JSON.toJSONString(result));
return null;
}
return result.getData();
}
private XpPoint getPointById(Long pointId) {
if (pointId == null) {
return null;
}
R pointResult = xpPointClient.queryById(pointId);
if (pointResult == null || pointResult.getCode() != R.SUCCESS) {
return null;
}
return pointResult.getData();
}
private Double getStraightDistance(XpPoint startPoint, XpPoint endPoint) {
if (startPoint == null || endPoint == null || startPoint.getPointLon() == null || startPoint.getPointLat() == null
|| endPoint.getPointLon() == null || endPoint.getPointLat() == null) {
return null;
}
Map distanceMap = GeodesyUtil.getDistance(startPoint.getPointLon() + "," + startPoint.getPointLat(),
endPoint.getPointLon() + "," + endPoint.getPointLat());
if (distanceMap == null) {
return null;
}
return distanceMap.get("WGS84");
}
private boolean isSameScene(XpOrder order, DaySceneType currentSceneType, Set targetHours) {
if (order.getCreateTime() == null || order.getTimeSlot() == null) {
return false;
}
// 场景命中必须同时满足“同类日期”和“当前小时前后1小时窗口”。
if (resolveDaySceneType(order.getCreateTime().toLocalDate()) != currentSceneType) {
return false;
}
Integer slotHour = parseTimeSlotStartHour(order.getTimeSlot());
return slotHour != null && targetHours.contains(slotHour);
}
private Integer parseTimeSlotStartHour(String timeSlot) {
if (StringUtils.isBlank(timeSlot) || !timeSlot.contains("-")) {
return null;
}
try {
return Integer.valueOf(timeSlot.split("-")[0]);
} catch (Exception ex) {
log.warn("解析时段失败,timeSlot:{}", timeSlot);
return null;
}
}
private Set buildTargetHours(int currentHour) {
Set targetHours = new HashSet<>();
targetHours.add((currentHour + 23) % 24);
targetHours.add(currentHour);
targetHours.add((currentHour + 1) % 24);
return targetHours;
}
private DaySceneType resolveDaySceneType(LocalDate date) {
if (holidayUtil.isHoliday(date)) {
return DaySceneType.HOLIDAY;
}
DayOfWeek dayOfWeek = date.getDayOfWeek();
if (dayOfWeek == DayOfWeek.SATURDAY || dayOfWeek == DayOfWeek.SUNDAY) {
return DaySceneType.NON_WORKDAY;
}
return DaySceneType.WORKDAY;
}
private enum DaySceneType {
WORKDAY,
NON_WORKDAY,
HOLIDAY
}
private static class RecommendationCandidate {
private final XpPointInfoVo pointInfo;
private final boolean available;
private int count;
private LocalDateTime latestCreateTime;
private RecommendationCandidate(XpPointInfoVo pointInfo, boolean available) {
this.pointInfo = pointInfo;
this.available = available;
}
public static RecommendationCandidate available(XpPointInfoVo pointInfo) {
return new RecommendationCandidate(pointInfo, true);
}
public static RecommendationCandidate unavailable() {
return new RecommendationCandidate(null, false);
}
public void increment(LocalDateTime createTime) {
this.count++;
if (createTime != null && (this.latestCreateTime == null || createTime.isAfter(this.latestCreateTime))) {
this.latestCreateTime = createTime;
}
}
public XpPointInfoVo getPointInfo() {
return pointInfo;
}
public boolean isAvailable() {
return available;
}
public int getCount() {
return count;
}
public LocalDateTime getLatestCreateTime() {
return latestCreateTime;
}
}
@Override
public List getHistoryStartPoints(String uid, double lon, double lat,Long electronicAreaId) {
//
List xpOrders = this.baseMapper.getHistoryStartPoints(Long.valueOf(uid),electronicAreaId);
// selectList(new LambdaQueryWrapper().eq(XpOrder::getAppUserId, Long.parseLong(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,Long.valueOf(uid)));
List data = r.getData();
for (XpHistoryStartPointsVo datum : data) {
if (datum.getOperationStatus() == 2) {
datum.setOperationStatus(1);
}
// 保留2位数小数
Double distance = datum.getDistance();
datum.setDistance(BigDecimal.valueOf(distance).setScale(2, RoundingMode.HALF_UP).doubleValue());
xpOrders.stream().filter(e -> e.getStartPointId().equals(datum.getId())).findFirst().ifPresent(
order -> {
datum.setNum(order.getNum());
}
);
}
data = data.stream().sorted(Comparator.comparingInt(XpHistoryStartPointsVo::getNum).reversed()).collect(Collectors.toList());
return data;
}
@Override
public List getHistoryEndPoints(String uid, double lon, double lat,Long electronicAreaId) {
//
List xpOrders = this.baseMapper.getHistoryEndPoints(Long.valueOf(uid), electronicAreaId);
// selectList(new LambdaQueryWrapper().eq(XpOrder::getAppUserId, Long.parseLong(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,Long.valueOf(uid)));
List data = r.getData();
for (XpHistoryEndPointsVo datum : data) {
// 保留2位数小数
Double distance = datum.getDistance();
datum.setDistance(BigDecimal.valueOf(distance).setScale(2, RoundingMode.HALF_UP).doubleValue());
xpOrders.stream().filter(e -> e.getEndPointId().equals(datum.getId())).findFirst().ifPresent(
order -> {
datum.setNum(order.getNum());
}
);
}
data = data.stream().sorted(Comparator.comparingInt(XpHistoryEndPointsVo::getNum).reversed()).collect(Collectors.toList());
return data;
}
/**
* 获取历史订单列表
*
* @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, Long.parseLong(uid));
XpOrderHistoryVO xpOrderHistoryVO = new XpOrderHistoryVO();
if (list.isEmpty()) {
return xpOrderHistoryVO;
}
Set points = list.stream().map(XpOrderHistoryListVO::getStartPointId).collect(Collectors.toSet());
points.addAll(list.stream().map(XpOrderHistoryListVO::getEndPointId).collect(Collectors.toSet()));
// 查询 点位信息
R> pointInfoVos = xpPointClient.getPointsInfo(points);
List pointList = pointInfoVos.getData();
xpOrderHistoryVO.setTotal((int) page.getTotal());
// 根据time分组
Map> map = list.stream().collect(Collectors.groupingBy(XpOrderHistoryListVO::getTimeStr));
List dateVOS = map.entrySet().stream().map(entry -> {
XpOrderHistoryDateVO xpOrderHistoryDateVO = new XpOrderHistoryDateVO();
xpOrderHistoryDateVO.setDateLabel(entry.getKey());
List value = entry.getValue();
value = value.stream().sorted(Comparator.comparing(XpOrderHistoryListVO::getTime).reversed()).collect(Collectors.toList());
for (XpOrderHistoryListVO xpOrderHistoryListVO : value) {
XpPoint xpPoint = pointList.stream().filter(e -> e.getId().equals(xpOrderHistoryListVO.getStartPointId())).findFirst().orElse(null);
if (xpPoint != null) {
xpOrderHistoryListVO.setStartPointName(xpPoint.getPointName());
xpOrderHistoryListVO.setStartLat(xpPoint.getPointLat());
xpOrderHistoryListVO.setStartLon(xpPoint.getPointLon());
xpOrderHistoryListVO.setXpElectronicFenceAreaId(xpPoint.getElectronicAreaId());
xpOrderHistoryListVO.setStartParkingTimeLimit(xpPoint.getParkingTimeLimit());
}
xpPoint = pointList.stream().filter(e -> e.getId().equals(xpOrderHistoryListVO.getEndPointId())).findFirst().orElse(null);
if (xpPoint != null) {
xpOrderHistoryListVO.setEndPointName(xpPoint.getPointName());
xpOrderHistoryListVO.setEndLat(xpPoint.getPointLat());
xpOrderHistoryListVO.setEndLon(xpPoint.getPointLon());
xpOrderHistoryListVO.setXpElectronicFenceAreaId(xpPoint.getElectronicAreaId());
xpOrderHistoryListVO.setEndParkingTimeLimit(xpPoint.getParkingTimeLimit());
}
}
xpOrderHistoryDateVO.setOrders(value);
return xpOrderHistoryDateVO;
}).collect(Collectors.toList());
dateVOS = dateVOS.stream().sorted(Comparator.comparing(XpOrderHistoryDateVO::getDateLabel).reversed()).collect(Collectors.toList());
xpOrderHistoryVO.setList(dateVOS);
return xpOrderHistoryVO;
}
/**
* 取消订单
*
* @param orderId
* @param uid
*/
@Override
@Transactional
@ShardingSphereTransactionType(TransactionType.BASE)
public void cancelOrder(Long orderId, String uid) {
XpOrder order = this.getById(orderId);
if (order == null) {
throw new ServiceException("订单不存在", I18nKeyConstant.ORDER_NOT_EXISTS);
}
if (order.getStatus() == 0) {
return;
}
if (!order.getAppUserId().toString().equals(uid)) {
throw new ServiceException("订单不存在", I18nKeyConstant.ORDER_NOT_EXISTS);
}
if (order.getStatus() > 5 || order.getStatus() < 1) {
throw new ServiceException("订单状态错误", I18nKeyConstant.ORDER_STATUS_ERROR);
}
try {
redisService.removeByPattern(order.getQueueName(), order.getOrderNumber());
} catch (Exception e) {
e.printStackTrace();
}
//判断还原车辆状态
if (null == order.getVehicleId()) {
Object cacheObject1 = redisService.getCacheObject("CREATE_ORDER:" + order.getId().toString());
if (null != cacheObject1) {
JSONObject jsonObject = JSON.parseObject(cacheObject1.toString());
String vin = jsonObject.getString("vin");
XpVehicle vehicle = xpVehicleClient.findByVehicleVin(vin).getData();
if (null != vehicle && 0 != vehicle.getState()) {
XpVehicle vehicle1 = new XpVehicle();
vehicle1.setId(vehicle.getId());
vehicle1.setState(0);
xpVehicleClient.updateVehicleById(vehicle1);
log.info("用户取消订单,车辆状态修改0 {}", vehicle.getPlateNumber());
}
}
}
else{// 下发命令
R xpVehicleR = xpVehicleClient.queryById(order.getVehicleId());
XpVehicle xpVehicle = xpVehicleR.getData();
if (xpVehicle == null) {
throw new ServiceException("车辆不存在", I18nKeyConstant.VEHICLE_NOT_EXISTS);
}
//是否能取消行程
boolean cancelShow = this.isCancelShow(order,xpVehicle.getVehicleVin());
if(cancelShow){
throw new ServiceException("当前订单状态不允许取消,行程即将完成", I18nKeyConstant.ORDER_CANCEL_FAILED);
}
// 组装调用车机参数
CancelTaxiPassengerVo cancelTaxiPassengerVo = new CancelTaxiPassengerVo();
cancelTaxiPassengerVo.setVin(xpVehicle.getVehicleVin());
cancelTaxiPassengerVo.setUid(uid);
cancelTaxiPassengerVo.setOrderNo(order.getOrderNumber());
cancelTaxiPassengerVo.setAppId(appid);
BaseVehicleUserDto baseVehicleUserDto = userVehicleClient.cancelTaxiPassenger(cancelTaxiPassengerVo);
if (baseVehicleUserDto == null) {
// TODO 本地无法调通
// throw new RuntimeException("取消订单失败");
}
//还原车辆状态
if (1 == xpVehicle.getState()) {
XpVehicle vehicle1 = new XpVehicle();
vehicle1.setId(xpVehicle.getId());
vehicle1.setState(0);
xpVehicleClient.updateVehicleById(vehicle1);
log.info("用户取消订单,车辆状态修改0 {}", xpVehicle.getPlateNumber());
}
//下发XPU报文
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.getId());
}
order.setStatus(0);
order.setAppUserId(null);
baseMapper.updateById(order);
redisService.setCacheObject("ORDER_STATUS:" + order.getId(), 0, 24L, TimeUnit.HOURS);
XpOrderCancel xpOrderCancel = new XpOrderCancel();
xpOrderCancel.setOrderId(orderId);
xpOrderCancel.setUserId(Long.valueOf(uid));
xpOrderCancel.setCancelSource(1);
xpOrderCancel.setCancelReason("用户取消订单");
xpOrderCancelService.save(xpOrderCancel);
// 移除mongo数据
xpPricingClient.deleteXpGeoQueryEntity(order.getId().toString());
}
@Override
@Transactional
@ShardingSphereTransactionType(TransactionType.BASE)
public XpOrder cancelOrder(String orderId) {
XpOrder order = this.getById(orderId);
if (order == null) {
throw new ServiceException("订单不存在", I18nKeyConstant.ORDER_NOT_EXISTS);
}
Long appUserId = order.getAppUserId();
order.setStatus(0);
order.setAppUserId(null);
baseMapper.updateById(order);
redisService.setCacheObject("ORDER_STATUS:" + order.getId(), 0, 24L, TimeUnit.HOURS);
XpOrderCancel xpOrderCancel = new XpOrderCancel();
xpOrderCancel.setOrderId(Long.valueOf(orderId));
xpOrderCancel.setUserId(1l);
xpOrderCancel.setCancelSource(4);
xpOrderCancel.setCancelReason("系统取消订单");
xpOrderCancel.setCreateTime(LocalDateTime.now());
xpOrderCancelService.save(xpOrderCancel);
redisService.removeByPattern(order.getQueueName(), order.getOrderNumber());
// 消息推送
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", 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(appUserId));
R stringR = xpPushMessageClient.sendStatus(sendMessageDTO);
//推送失败,重试3次
if(stringR.getCode() != Constants.SUCCESS){
ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());
executor.execute(() -> {
for (int i = 0; i < 3; i++) {
R stringR1 = xpPushMessageClient.sendStatus(sendMessageDTO);
if(stringR1.getCode() == Constants.SUCCESS){
break;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
log.info("定时任务取消订单推送订单状态消息失败:{} {} {}", stringR1.getMsg(), JSON.toJSONString(sendMessageDTO), order.getOrderNumber());
}
});
}
log.info("定时任务取消订单推送消息完毕");
// 移除mongo数据
xpPricingClient.deleteXpGeoQueryEntity(order.getId().toString());
return order;
}
/**
* 评价订单
*
* @param dto
* @param uid
*/
@Override
@Transactional
@ShardingSphereTransactionType(TransactionType.BASE)
public void comment(XpOrderCommentDto dto, String uid) {
XpOrder xpOrder = this.getById(dto.getOrderId());
if (xpOrder == null) {
throw new ServiceException("订单不存在", I18nKeyConstant.ORDER_NOT_EXISTS);
}
if (!xpOrder.getAppUserId().toString().equals(uid)) {
throw new ServiceException("订单不存在", I18nKeyConstant.ORDER_NOT_EXISTS);
}
if (xpOrder.getStatus() != 9) {
throw new ServiceException("订单状态错误", I18nKeyConstant.ORDER_STATUS_ERROR);
}
xpOrder.setAppUserId(null);
xpOrder.setStatus(10);
baseMapper.updateById(xpOrder);
redisService.setCacheObject("ORDER_STATUS:" + xpOrder.getId(), 10, 24L, TimeUnit.HOURS);
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());
evaluate.setCreateTime(LocalDateTime.now());
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("订单已取消");
}
if(xpOrder.getStatus() >= 8){
return R.fail("订单已结束");
}
Long appUserId = xpOrder.getAppUserId();
// 查询车辆
XpVehicle xpVehicle = xpVehicleClient.queryById(xpOrder.getVehicleId()).getData();
// 订单的状态 status =1 ( 状态 0=已取消,1=待接单,2=待出发,3=接驾中,4=待客上车,5=乘客已上车,6=服务中,7=待客下车,8=待付款,9=待评价,10=已完成)
switch (status) {
case 0://去掉订单 优惠卷需要返回 @todo yyy 这里可能不需要这个步骤
//取消订单会将优惠卷 从占用修改成未使用
if(xpOrder.getCouponId() != null){
UseUserCouponDto useUserCouponDto = new UseUserCouponDto();
useUserCouponDto.setUserCouponId(xpOrder.getCouponId() );
xpAppUserCouponClient.backUserCoupon(useUserCouponDto);
}
break;
case 4:
// 查询起点点位
Long startPointId = xpOrder.getStartPointId();
XpPoint xpPoint = xpPointClient.queryById(startPointId).getData();
// 添加取消订单定时任务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(xpPoint.getParkingTimeLimit())),
cancelJobMaps
);
// 车辆到达上车点时间
xpOrder.setArrivalTime(LocalDateTime.now());
break;
case 5:
LocalDateTime now = LocalDateTime.now();
xpOrder.setBoardingTime(now);
xpOrder.setRideTime(now);
//推送行起点到终点的驶轨迹给乘客端
sendRoute(appUserId.toString(), orderId, "ORDER_NAVIGATION_END_DATA", xpVehicle.getVehicleVin());
// 删除定时任务job
QuartzManager.removeJob((OrderCancelJob.name + xpOrder.getId()).toUpperCase(), TimeJobType.ORDER_CANCEL);
break;
case 6:
// 行程开始时间
xpOrder.setTripBegin(LocalDateTime.now());
redisService.deleteObject("ORDER_NAVIGATION_START_DATA:" + xpOrder.getAppUserId());
break;
case 7:
xpOrder.setGetoffTime(LocalDateTime.now());
// 解析下车地点
VehiclePosition vehiclePosition = mongoTemplate.findOne(new Query(Criteria.where("vin").is(xpVehicle.getVehicleVin())), VehiclePosition.class);
if(Objects.nonNull(vehiclePosition)){
GeoJsonPoint point = vehiclePosition.getPoint();
if(Objects.nonNull(point)){
double x = point.getX();
double y = point.getY();
Map geocode = gdMapGeocodingUtil.geocode(String.valueOf(x), String.valueOf(y));
xpOrder.setGetoffAddress(geocode.get("address"));
}
}
//计算行驶总距离和时长
List orderTrajectoryList = mongoTemplate.find(new Query().addCriteria(Criteria.where("orderId").is(xpOrder.getId())), OrderTrajectory.class);
Double reduce = orderTrajectoryList.stream().map(OrderTrajectory::getDistance).reduce(0.0, Double::sum);
xpOrder.setMileageKilometers(new BigDecimal(reduce).divide(new BigDecimal(1000)).setScale(4, RoundingMode.HALF_EVEN).doubleValue());
long duration = LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8")) - xpOrder.getTripBegin().toEpochSecond(ZoneOffset.of("+8"));
xpOrder.setDuration(new BigDecimal(duration).divide(new BigDecimal(60), 2, RoundingMode.HALF_EVEN).doubleValue());
// 推送倒计时消息
Map params = new HashMap<>();
Map content = new HashMap<>();
content.put("orderId", xpOrder.getId().toString());
content.put("isOpen", 1);
params.put("type", PushMessageTypeEnum.OPEN_TRUNK.getDesc());
params.put("content", 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(xpOrder.getAppUserId()));
R stringR = xpPushMessageClient.sendStatus(sendMessageDTO);
//推送失败,重试3次
if(stringR.getCode() != Constants.SUCCESS){
ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());
executor.execute(() -> {
for (int i = 0; i < 3; i++) {
R stringR1 = xpPushMessageClient.sendStatus(sendMessageDTO);
if(stringR1.getCode() == Constants.SUCCESS){
break;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
log.info("推送倒计时消息失败:{} {} {}", stringR1.getMsg(), JSON.toJSONString(sendMessageDTO), xpOrder.getOrderNumber());
}
});
}
//创建定时结束订单和等待结束后下发巡游单的定时任务
Integer arriveTime = xpSystemSettingClient.getOrderFinishTime().getData();
// 添加自动结束行程定时任务job
Map endTripJobMaps =
new ImmutableMap.Builder()
.put("id", xpOrder.getId().toString())
.put("appid", appid)
.put("orderType", OrderType.USER_ORDER)
.build();
QuartzManager.addJob(
EndTripJob.class,
(EndTripJob.name + xpOrder.getId()).toUpperCase(),
TimeJobType.END_TRIP,
DateUtils.localDateTimeToDate(LocalDateTime.now().plusMinutes(arriveTime)),
endTripJobMaps
);
// 添加车辆到达终点后限停时间到后驶离的定时任务
//时间到后将车辆状态回滚到空闲、添加巡游订单,解绑用户和车辆关系,车辆驶离点位
XpPoint point = xpPointClient.queryById(xpOrder.getEndPointId()).getData();
Map jobMaps =
new ImmutableMap.Builder()
.put("id", xpOrder.getId().toString())
.put("appid", appid)
.put("orderType", OrderType.USER_ORDER)
.build();
Long id = redisService.getCacheObject(CacheConstants.nextOrder(xpOrder.getId().toString()));
if(id != null){
XpOrder nextOrder = getById(id);
XpVehicle nextVehicle = xpVehicleClient.queryById(nextOrder.getVehicleId()).getData();
PushOrderDto pushOrderDto = new PushOrderDto();
pushOrderDto.setOrderId(nextOrder.getId().toString());
pushOrderDto.setStartLatitude(nextOrder.getStartLat());
pushOrderDto.setStartLongitude(nextOrder.getStartLon());
pushOrderDto.setEndLatitude(nextOrder.getEndLat());
pushOrderDto.setEndLongitude(nextOrder.getEndLon());
pushOrderDto.setEndPoiName(nextOrder.getEndPointName());
pushOrderDto.setOrderType(OrderType.USER_ORDER);
pushOrderDto.setStage(1);
pushOrderDto.setVin(nextVehicle.getVehicleVin());
orderMissionClient.pushOrder(pushOrderDto);
QuartzManager.addJob(
AddCruiseMissionJob.class,
(AddCruiseMissionJob.name + nextOrder.getId()).toUpperCase(),
TimeJobType.ADD_CRUISE_MISSION,
DateUtils.localDateTimeToDate(LocalDateTime.now().plusMinutes(point.getParkingTimeLimit())),
jobMaps
);
} else {
QuartzManager.addJob(
AddCruiseMissionJob.class,
(AddCruiseMissionJob.name + xpOrder.getId()).toUpperCase(),
TimeJobType.ADD_CRUISE_MISSION,
DateUtils.localDateTimeToDate(LocalDateTime.now().plusMinutes(point.getParkingTimeLimit())),
jobMaps
);
}
break;
default:
break;
}
xpOrder.setStatus(status);
xpOrder.setAppUserId(null);
this.updateById(xpOrder);
redisService.setCacheObject("ORDER_STATUS:" + xpOrder.getId(), status, 24L, TimeUnit.HOURS);
return R.ok();
}
/**
* 行驶中车辆异常,订单状态修改- 修改状态=7 再修改 金额-再将状态修改成8
*/
@Override
public R carAbnormalUpdateOrderStatus(Long orderId){
XpOrder xpOrder = this.getById(orderId);
if (Objects.isNull(xpOrder)) {
return R.fail("订单不存在");
}
if (xpOrder.getStatus() == 0) {
return R.fail("订单已取消");
}
if(xpOrder.getStatus() >= 8){
return R.fail("订单已结束");
}
// 查询车辆
XpVehicle xpVehicle = xpVehicleClient.queryById(xpOrder.getVehicleId()).getData();
Long appUserId = xpOrder.getAppUserId();
xpOrder.setCarAbnormal(true);//车辆是否故障 0-否 1-是
xpOrder.setGetoffTime(LocalDateTime.now());
// 解析下车地点
VehiclePosition vehiclePosition = mongoTemplate.findOne(new Query(Criteria.where("vin").is(xpVehicle.getVehicleVin())), VehiclePosition.class);
if(Objects.nonNull(vehiclePosition)){
GeoJsonPoint point = vehiclePosition.getPoint();
if(Objects.nonNull(point)){
double x = point.getX();
double y = point.getY();
Map geocode = gdMapGeocodingUtil.geocode(String.valueOf(x), String.valueOf(y));
xpOrder.setGetoffAddress(geocode.get("address"));
}
}
//计算行驶总距离和时长
List orderTrajectoryList = mongoTemplate.find(new Query().addCriteria(Criteria.where("orderId").is(xpOrder.getId())), OrderTrajectory.class);
Double reduce = orderTrajectoryList.stream().map(OrderTrajectory::getDistance).reduce(0.0, Double::sum);
xpOrder.setMileageKilometers(new BigDecimal(reduce).divide(new BigDecimal(1000)).setScale(4, RoundingMode.HALF_EVEN).doubleValue());
long duration = LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8")) - xpOrder.getTripBegin().toEpochSecond(ZoneOffset.of("+8"));
xpOrder.setDuration(new BigDecimal(duration).divide(new BigDecimal(60), 2, RoundingMode.HALF_EVEN).doubleValue());
xpOrder.setStatus(OrderStatusEnum.SEE_OFF_GUESTS.getCode());//待客下车
xpOrder.setAppUserId(null);
this.updateById(xpOrder);
//结束行程,修改状态和计算费用
this.endTrip(xpOrder.getId(),appUserId.toString());
//将支付金额修改成0 并将状态修改成 订单状态9
XpOrder xpOrder1 = new XpOrder();
xpOrder1.setId(xpOrder.getId());
xpOrder1.setPayMoney(BigDecimal.ZERO);
xpOrder1.setStatus(OrderStatusEnum.PENDING_PAYMENT.getCode());
xpOrder1.setUpdateTime(LocalDateTime.now());
baseMapper.updateById(xpOrder1);
redisService.setCacheObject("ORDER_STATUS:" + xpOrder.getId(), xpOrder1.getStatus(), 24L, TimeUnit.HOURS);
return R.ok();
}
@Override
public Boolean endTrip(Long orderId, String uid) {
List xpOrderDetails = new ArrayList<>();
// 起步价格明细
List start = new ArrayList<>();
// 里程费价格明细
List mileage = new ArrayList<>();
// 时长费价格明细
List duration = new ArrayList<>();
// 远途费价格明细
List longDistance = new ArrayList<>();
XpOrder order = this.getById(orderId);
if (order == null) {
throw new RuntimeException("订单不存在");
}
if (order.getStatus() != 7) {
throw new RuntimeException("订单状态错误");
}
order.setStatus(8);
order.setAppUserId(null);
order.setTripEnd(LocalDateTime.now());
baseMapper.updateById(order);
redisService.setCacheObject("ORDER_STATUS:" + order.getId(), 8, 24L, TimeUnit.HOURS);
// 根据订单所属开通城市id 查询定价
R pricingByCityId = xpPricingClient.getPricingByCid(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
);
List gpsPoints = new ArrayList<>();
for (OrderTrajectory trajectory : trajectories) {
TaxiPricingDemoService.GpsPoint point = new TaxiPricingDemoService.GpsPoint(trajectory.getDistance(), trajectory.getLocatetime());
gpsPoints.add(point);
}
TaxiPricingDemoService.PricingConfig config = new TaxiPricingDemoService.PricingConfig();
JSONArray startingFareJsonArray = JSONArray.parseArray(pricing.getStartingFareJson());
LocalDateTime boardingTime = order.getBoardingTime();
Date date = DateUtils.localDateTimeToDate(boardingTime);
double paramNum4 = 0.0;
double paramNum5 = 0.0;
//一口价
if (pricing.getPricingType() == 2) {
Double mileageKilometers = order.getMileageKilometers();
Double duration1 = order.getDuration();
com.alibaba.fastjson2.JSONObject basicPricingJson = com.alibaba.fastjson2.JSONObject.parseObject(pricing.getBasicPricingJson());
XpPricingCardVo xpPricingCardVo = xpPricingUtil.calculationFixedPrice(uid, basicPricingJson, null, mileageKilometers, duration1, 0, 0.0, pricing.getIsDynamicPrice(), order.getCityId(), pricing.getDynamicPricingJson());
XpOrderDetail xpOrderDetail = new XpOrderDetail();
xpOrderDetail.setDetailType(1);
xpOrderDetail.setOrderId(orderId);
xpOrderDetail.setType(1);
xpOrderDetail.setMileage(new BigDecimal(mileageKilometers).setScale(2, RoundingMode.HALF_UP));
String currentTimeSlot = DateUtils.getCurrentTimeSlot();
xpOrderDetail.setTime(currentTimeSlot);
xpOrderDetail.setDuration(duration1);
xpOrderDetail.setMoney(new BigDecimal(xpPricingCardVo.getStartPrice()).setScale(2, RoundingMode.HALF_UP));
xpOrderDetails.add(xpOrderDetail);
xpOrderDetail = new XpOrderDetail();
xpOrderDetail.setDetailType(2);
xpOrderDetail.setOrderId(orderId);
xpOrderDetail.setType(1);
xpOrderDetail.setMoney(new BigDecimal(xpPricingCardVo.getDistancePrice()).setScale(2,RoundingMode.HALF_UP));
xpOrderDetails.add(xpOrderDetail);
xpOrderDetail = new XpOrderDetail();
xpOrderDetail.setDetailType(3);
xpOrderDetail.setOrderId(orderId);
xpOrderDetail.setType(1);
xpOrderDetail.setMoney(new BigDecimal(xpPricingCardVo.getDurationPrice()).setScale(2, RoundingMode.HALF_UP));
xpOrderDetails.add(xpOrderDetail);
BigDecimal totalPrice =BigDecimal.ZERO;
for (XpOrderDetail xpOrderDetail1 : xpOrderDetails) {
totalPrice = totalPrice.add(xpOrderDetail1.getMoney());
xpOrderDetailMapper.insert(xpOrderDetail1);
}
order.setOrderMoney(totalPrice);
order.setAppUserId(null);
baseMapper.updateById(order);
}
else {
//动态计费
if (pricing.getPricingType() == 1) {
for (int i = 0; i < startingFareJsonArray.size(); i++) {
com.alibaba.fastjson2.JSONObject jsonObject = startingFareJsonArray.getJSONObject(i);
String num1 = jsonObject.getString("num1");
String num2 = jsonObject.getString("num2");
String num3 = jsonObject.getString("num3");
String num4 = jsonObject.getString("num4");
String num5 = jsonObject.getString("num5");
String num6 = jsonObject.getString("num6");
String num7 = jsonObject.getString("num7");
config.normals.add(new TaxiPricingDemoService.PricingRule("普通时段", num1, num2, Double.parseDouble(num3), Double.parseDouble(num4), Double.parseDouble(num5), Double.parseDouble(num6), Double.parseDouble(num7)));
// 判断当前是那个时段
Calendar calendarStart = Calendar.getInstance();
calendarStart.setTime(date);
calendarStart.set(Calendar.HOUR_OF_DAY, Integer.valueOf(jsonObject.getString("num1").split(":")[0]));
calendarStart.set(Calendar.MINUTE, Integer.valueOf(jsonObject.getString("num1").split(":")[1]));
Calendar calendarEnd = Calendar.getInstance();
calendarEnd.setTime(date);
calendarEnd.set(Calendar.HOUR_OF_DAY, Integer.valueOf(jsonObject.getString("num2").split(":")[0]));
calendarEnd.set(Calendar.MINUTE, Integer.valueOf(jsonObject.getString("num2").split(":")[1]));
// 找到符合条件的直接跳出循环
if (date.getTime() > calendarStart.getTimeInMillis() && date.getTime() < calendarEnd.getTimeInMillis()) {
paramNum4 = jsonObject.getDouble("num4");
paramNum5 = jsonObject.getDouble("num5");
break;
}
}
JSONObject basicPricingJson = JSONObject.parseObject(pricing.getBasicPricingJson());
config.peaks.add(new TaxiPricingDemoService.PricingRule("高峰期", basicPricingJson.getString("num21").split(" - ")[0], basicPricingJson.getString("num21").split(" - ")[1], Double.parseDouble(basicPricingJson.getString("num23")), paramNum4, paramNum5, Double.parseDouble(basicPricingJson.getString("num24")), Double.parseDouble(basicPricingJson.getString("num25"))));
config.peaks.add(new TaxiPricingDemoService.PricingRule("高峰期", basicPricingJson.getString("num22").split(" - ")[0], basicPricingJson.getString("num22").split(" - ")[1], Double.parseDouble(basicPricingJson.getString("num23")), paramNum4, paramNum5, Double.parseDouble(basicPricingJson.getString("num24")), Double.parseDouble(basicPricingJson.getString("num25"))));
config.night = new TaxiPricingDemoService.PricingRule("夜间行驶", basicPricingJson.getString("num14").split(" - ")[0], basicPricingJson.getString("num14").split(" - ")[1], Double.parseDouble(basicPricingJson.getString("num15")), paramNum4, paramNum5, Double.parseDouble(basicPricingJson.getString("num16")), Double.parseDouble(basicPricingJson.getString("num17")));
config.holiday = new TaxiPricingDemoService.PricingRule("节假日", "00:00", "23:59", Double.parseDouble(basicPricingJson.getString("num29")), paramNum4, paramNum5, Double.parseDouble(basicPricingJson.getString("num30")), Double.parseDouble(basicPricingJson.getString("num31")));
// --- 远途加价区间配置 (关键修改) ---
// 1. 超过 num1(12) 不足 num2(20), 加收 num3(1.2)
config.longSteps.add(new TaxiPricingDemoService.LongRangeStep(paramNum4, Double.parseDouble(basicPricingJson.getString("num7")), Double.parseDouble(basicPricingJson.getString("num8"))));
// 2. 超过 num4(20) 不足 num5(35), 加收 num6(2.0)
config.longSteps.add(new TaxiPricingDemoService.LongRangeStep(Double.parseDouble(basicPricingJson.getString("num9")), Double.parseDouble(basicPricingJson.getString("num10")), Double.parseDouble(basicPricingJson.getString("num11"))));
// 3. 超过 num7(35) 以后, 加收 num8(3.5) -> numEnd 传个极大值
config.longSteps.add(new TaxiPricingDemoService.LongRangeStep(Double.parseDouble(basicPricingJson.getString("num12")), 999999, Double.parseDouble(basicPricingJson.getString("num13"))));
}
TaxiPricingDemoService.TaxiPricingService service = new TaxiPricingDemoService.TaxiPricingService();
Integer holidayToday = holidayUtil.isHolidayToday();
Map bill = service.calculateOrderPrice(DateUtils.toDate(order.getBoardingTime()), order.getMileageKilometers(), gpsPoints, config, holidayToday);
log.info("======= 订单计费账单 =======");
bill.forEach((detail, price) -> log.info(detail + ": " + price + " 元"));
BigDecimal totalAmount = bill.values().stream().reduce(BigDecimal.ZERO, BigDecimal::add);
log.info("--------------------------");
log.info("应付总额: " + totalAmount + " 元");
BigDecimal startMoney = BigDecimal.ZERO;
// 默认费率1
double priceRate = 1;
if (pricing.getIsDynamicPrice() == 1) {
// 先判断是否已经调价过了
Object userDynamicPriceObj = redisService.getCacheObject("USER_DYNAMIC_PRICE_" + order.getCityId() + "_" + uid);
if (userDynamicPriceObj != null) {
// 是否调价过了
priceRate = Double.parseDouble(userDynamicPriceObj.toString());
} else {
// 没有调过
//是否触发动态调价 redis获取
Object dynamicPriceObj = redisService.getCacheObject("DYNAMIC_PRICE_" + order.getCityId());
if (dynamicPriceObj != null) {
JSONObject jsonObject = JSONObject.parseObject(pricing.getDynamicPricingJson());
// 触发动态调价
// value=时间
String timeStr = dynamicPriceObj.toString();
// 判断当前距离这个时间范围的新增订单数(排除取消) / 上浮数量 算出应该增加的价格率
Integer num = this.getOrderNum(new GetOrderNumDto(timeStr, order.getCityId()));
// X单上升一次调价
Integer orderNum = getOrderNum(jsonObject, 4);
// 需要调价的次数
int count = num / orderNum;
if (count > 0) {
// 计算每次上升的费率
Integer upRate = getOrderNum(jsonObject, 5);
BigDecimal divide = new BigDecimal(upRate).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP);
for (int i = count; i > 0; i--) {
// 当前费率+(费率*上升百分比)
priceRate = new BigDecimal(priceRate).multiply(divide).add(new BigDecimal(priceRate)).setScale(2, RoundingMode.HALF_UP).doubleValue();
}
// 冷却时间
Integer time = getOrderNum(jsonObject, 6);
// 触发调价了
redisService.setCacheObject("USER_DYNAMIC_PRICE_" + order.getCityId() + "_" + uid, priceRate, time.longValue(), TimeUnit.MINUTES);
}
}
}
}
// 封装费用明细数据
for (Map.Entry stringBigDecimalEntry : bill.entrySet()) {
// 封装起步价
FareBreakdownDetailVo startDetail = new FareBreakdownDetailVo();
String key = stringBigDecimalEntry.getKey();
if (key.contains("起步价")) {
startDetail.setTime(key.split("\\|")[1]);
startDetail.setMoney(stringBigDecimalEntry.getValue());
startMoney = startMoney.add(stringBigDecimalEntry.getValue());
if (key.contains("普通时段")) {
startDetail.setType(1);
} else if (key.contains("夜间")) {
startDetail.setType(2);
} else if (key.contains("高峰")) {
startDetail.setType(3);
} else if (key.contains("节假")) {
startDetail.setType(4);
}
String durationAndMileage = key.split("\\|")[2];
startDetail.setDuration(Double.valueOf(durationAndMileage.split("&")[0]));
startDetail.setMileage(new BigDecimal(durationAndMileage.split("&")[1]).multiply(BigDecimal.valueOf(1000)));
start.add(startDetail);
}
// 封装里程费
if (!key.contains("起步") && !key.contains("时长") && !key.contains("远途") && !key.contains("distance")) {
FareBreakdownDetailVo mileageDetail = new FareBreakdownDetailVo();
if (key.contains("普通时段")) {
mileageDetail.setType(1);
String key1 = "distance普通时段|" + key.split("\\|")[1];
String key2 = "普通时段|" + key.split("\\|")[1];
BigDecimal distance = bill.get(key1);
BigDecimal money = bill.get(key2);
mileageDetail.setMileage(distance);
order.setStartMoney(money.multiply(BigDecimal.valueOf(priceRate)));
mileageDetail.setMoney(money.multiply(BigDecimal.valueOf(priceRate)));
} else if (key.contains("夜间行驶")) {
mileageDetail.setType(2);
String key1 = "distance夜间行驶|" + key.split("\\|")[1];
String key2 = "夜间行驶|" + key.split("\\|")[1];
BigDecimal distance = bill.get(key1);
BigDecimal money = bill.get(key2);
mileageDetail.setMileage(distance);
order.setStartMoney(money.multiply(BigDecimal.valueOf(priceRate)));
mileageDetail.setMoney(money.multiply(BigDecimal.valueOf(priceRate)));
} else if (key.contains("高峰期")) {
mileageDetail.setType(3);
String key1 = "distance高峰期|" + key.split("\\|")[1];
String key2 = "高峰期|" + key.split("\\|")[1];
BigDecimal distance = bill.get(key1);
BigDecimal money = bill.get(key2);
mileageDetail.setMileage(distance);
order.setStartMoney(money.multiply(BigDecimal.valueOf(priceRate)));
mileageDetail.setMoney(money.multiply(BigDecimal.valueOf(priceRate)));
} else if (key.contains("节假")) {
mileageDetail.setType(4);
String key1 = "distance节假日|" + key.split("\\|")[1];
String key2 = "节假日|" + key.split("\\|")[1];
BigDecimal distance = bill.get(key1);
BigDecimal money = bill.get(key2);
mileageDetail.setMileage(distance);
order.setStartMoney(money.multiply(BigDecimal.valueOf(priceRate)));
mileageDetail.setMoney(money.multiply(BigDecimal.valueOf(priceRate)));
}
mileage.add(mileageDetail);
}
// 封装时长费
FareBreakdownDetailVo timeDetail = new FareBreakdownDetailVo();
if (key.contains("时长费")) {
if(key.contains("普通时段")){
timeDetail.setType(1);
}
if(key.contains("夜间行驶")){
timeDetail.setType(2);
}
if(key.contains("高峰")){
timeDetail.setType(3);
}
if(key.contains("节假")){
timeDetail.setType(4);
}
timeDetail.setMoney(stringBigDecimalEntry.getValue());
timeDetail.setDuration(Double.valueOf(key.split("&")[1]));
duration.add(timeDetail);
}
// 封装远途费
FareBreakdownDetailVo longDetail = new FareBreakdownDetailVo();
if (key.contains("远途费")) {
longDetail.setType(startDetail.getType());
longDetail.setMoney(stringBigDecimalEntry.getValue());
// longDetail.setMileage(new BigDecimal(key.split("&")[1]));
longDistance.add(longDetail);
}
}
for (FareBreakdownDetailVo fareBreakdownDetailVo : start) {
XpOrderDetail xpOrderDetail = new XpOrderDetail();
BeanUtils.copyProperties(fareBreakdownDetailVo, xpOrderDetail);
xpOrderDetail.setOrderId(orderId);
xpOrderDetail.setDetailType(1);
xpOrderDetail.setCreateTime(LocalDateTime.now());
xpOrderDetails.add(xpOrderDetail);
}
BigDecimal mileageMoney = BigDecimal.ZERO;
for (FareBreakdownDetailVo fareBreakdownDetailVo : mileage) {
XpOrderDetail xpOrderDetail = new XpOrderDetail();
BeanUtils.copyProperties(fareBreakdownDetailVo, xpOrderDetail);
xpOrderDetail.setOrderId(orderId);
xpOrderDetail.setDetailType(2);
xpOrderDetail.setCreateTime(LocalDateTime.now());
mileageMoney = mileageMoney.add(fareBreakdownDetailVo.getMoney());
xpOrderDetails.add(xpOrderDetail);
}
order.setMileageMoney(mileageMoney);
for (FareBreakdownDetailVo fareBreakdownDetailVo : longDistance) {
XpOrderDetail xpOrderDetail = new XpOrderDetail();
BeanUtils.copyProperties(fareBreakdownDetailVo, xpOrderDetail);
xpOrderDetail.setOrderId(orderId);
xpOrderDetail.setDetailType(4);
xpOrderDetail.setCreateTime(LocalDateTime.now());
mileageMoney = mileageMoney.add(fareBreakdownDetailVo.getMoney());
xpOrderDetails.add(xpOrderDetail);
}
for (FareBreakdownDetailVo fareBreakdownDetailVo : duration) {
XpOrderDetail xpOrderDetail = new XpOrderDetail();
BeanUtils.copyProperties(fareBreakdownDetailVo, xpOrderDetail);
xpOrderDetail.setOrderId(orderId);
xpOrderDetail.setDetailType(3);
xpOrderDetail.setCreateTime(LocalDateTime.now());
mileageMoney = mileageMoney.add(fareBreakdownDetailVo.getMoney());
xpOrderDetails.add(xpOrderDetail);
}
for (XpOrderDetail xpOrderDetail : xpOrderDetails) {
xpOrderDetailMapper.insert(xpOrderDetail);
}
for (FareBreakdownDetailVo fareBreakdownDetailVo : duration) {
if (order.getDurationMoney() == null) {
order.setDurationMoney(BigDecimal.ZERO);
}
BigDecimal add = order.getDurationMoney().add(fareBreakdownDetailVo.getMoney());
order.setDurationMoney(add);
}
BigDecimal orderMoney = startMoney.add(mileageMoney).add(order.getDurationMoney() != null ? order.getDurationMoney() : BigDecimal.ZERO)
.add(order.getLongDistancemoney() != null ? order.getLongDistancemoney() : BigDecimal.ZERO)
.add(order.getWaitMoney() != null ? order.getWaitMoney() : BigDecimal.ZERO);
order.setOrderMoney(orderMoney);
order.setAppUserId(null);
baseMapper.updateById(order);
}
if (order.getCouponId() != null) {
// UseUserCouponDto useUserCouponDto = new UseUserCouponDto();
// useUserCouponDto.setUserId(Long.valueOf(uid));
// useUserCouponDto.setTotalAmount(order.getOrderMoney().doubleValue());
// useUserCouponDto.setOpenCityId(order.getCityId());
// useUserCouponDto.setOrderId(orderId);
// useUserCouponDto.setUserCouponId(order.getCouponId());
// xpAppUserCouponClient.useUserCoupon(useUserCouponDto);
}
// 计算完价格后 挑选优惠券
GetUserCouponDto getUserCouponDto = new GetUserCouponDto();
getUserCouponDto.setUserId(Long.valueOf(uid));
getUserCouponDto.setTotalAmount(order.getOrderMoney().doubleValue());
getUserCouponDto.setUserCouponId(order.getCouponId());
getUserCouponDto.setOpenCityId(order.getCityId());
R userCouponVoR = xpAppUserCouponClient.getUserCoupon(getUserCouponDto);
UserCouponVo userCouponVo = userCouponVoR.getData();
// 提示是否换券
boolean tip =false;
if (userCouponVo != null) {
// 重新选券
if(userCouponVo.getStatus()==2){
getUserCouponDto.setUserId(Long.valueOf(uid));
getUserCouponDto.setTotalAmount(order.getOrderMoney().doubleValue());
getUserCouponDto.setUserCouponId(order.getCouponId());
getUserCouponDto.setOpenCityId(order.getCityId());
userCouponVoR = xpAppUserCouponClient.getUserCoupon(getUserCouponDto);
userCouponVo = userCouponVoR.getData();
tip=true;
}
order.setCouponId(userCouponVo.getId());
order.setCouponMoney(BigDecimal.valueOf(userCouponVo.getMoney()));
}
order.setAppUserId(null);
baseMapper.updateById(order);
return tip;
}
/**
* 添加巡游任务下发命令
*
* @param order
*/
public void addCruiseMission(Long vehicleId, XpOrder order) {
XpVehicle vehicle = xpVehicleClient.queryById(vehicleId).getData();
log.info("添加巡游订单 {}", vehicle.getPlateNumber());
if (vehicle.getState() != 0 || vehicle.getOperatingStatus() != 1) {
log.info("添加巡游单失败,车辆状态限制 {}", vehicle.getState());
return;
}
// 判断是否有计划
R xpCruisePlanR = xpCruisePlanClient.getCruisePlan(vehicle.getId());
if (xpCruisePlanR == null || xpCruisePlanR.getData() == null) {
log.info("添加巡游单失败,没有符合条件的巡游计划", vehicle.getPlateNumber());
return;
}
// 获取配置 根据配置判断是否该收车 如果是则找到收车点位进行收车操作
R xpTaskRuleR = xpTaskRuleClient.queryDetail();
XpTaskRule xpTaskRule = xpTaskRuleR.getData();
// 生成新的巡游单子 回到最新巡游的初始起点位置
XpCruiseOrder xpCruiseOrders = xpCruiseOrderMapper.selectOne(new LambdaQueryWrapper().eq(XpCruiseOrder::getVehicleId, vehicle.getId()).eq(XpCruiseOrder::getStatus, 2).orderByDesc(BaseModel::getCreateTime).last("limit 1"));
int temp = 0;
if (xpCruiseOrders == null) {
temp = 1;
//特殊情况 可能没有产生终止的订单 (到站点就被正常接单了)所以找正常订单
xpCruiseOrders = xpCruiseOrderMapper.selectOne(new LambdaQueryWrapper().eq(XpCruiseOrder::getVehicleId, vehicle.getId()).eq(XpCruiseOrder::getStatus, 3).orderByDesc(BaseModel::getCreateTime).last("limit 1"));
}
XpCruiseOrder xpCruiseOrder = new XpCruiseOrder();
xpCruiseOrder.setVehicleId(vehicle.getId());
xpCruiseOrder.setFleetId(vehicle.getFleetId());
xpCruiseOrder.setVehicleVin(vehicle.getVehicleVin());
xpCruiseOrder.setOrderType(2);
// 返程起点是当前正常订单的终点
xpCruiseOrder.setStartPointId(order.getEndPointId());
xpCruiseOrder.setStartPointName(order.getEndPointName());
xpCruiseOrder.setStartLon(order.getEndLon());
xpCruiseOrder.setStartLat(order.getEndLat());
// 完成的直接走终点 终止的走起点
if(null != xpCruiseOrders){
if (temp == 0) {
//当上一笔单有起点点位,才需要将上一笔巡游单的起点作为本次的终点导航
//上一笔单没有起点点位,统一用后面的巡游计划的第一个点位作为本次的终点导航
if(null != xpCruiseOrders.getStartPointId()){
xpCruiseOrder.setEndPointId(xpCruiseOrders.getStartPointId());
xpCruiseOrder.setEndPointName(xpCruiseOrders.getStartPointName());
xpCruiseOrder.setEndLon(xpCruiseOrders.getStartLon());
xpCruiseOrder.setEndLat(xpCruiseOrders.getStartLat());
}
} else {
xpCruiseOrder.setEndPointId(xpCruiseOrders.getEndPointId());
xpCruiseOrder.setEndPointName(xpCruiseOrders.getEndPointName());
xpCruiseOrder.setEndLon(xpCruiseOrders.getEndLon());
xpCruiseOrder.setEndLat(xpCruiseOrders.getEndLat());
}
xpCruiseOrder.setOrderReverse(xpCruiseOrders.getOrderReverse());
xpCruiseOrder.setLastOrderId(xpCruiseOrders.getId());
xpCruiseOrder.setMissionId(xpCruiseOrders.getMissionId());
}
//所有没有赋值终点点位的情况,全部导航到巡游计划的第一个点位开始执行巡游任务
if(null == xpCruiseOrder.getEndPointId()){
// 先找计划
List planPointList = xpCruisePlanClient.getCruisePlanPointList(vehicle.getId()).getData();
XpCruisePlanPoint xpCruisePlanPoint = planPointList.get(0);
Long endPointId = xpCruisePlanPoint.getPointId();
XpPoint endPoint = xpPointClient.queryById(endPointId).getData();
xpCruiseOrder.setEndPointId(endPointId);
xpCruiseOrder.setEndPointName(endPoint.getPointName());
xpCruiseOrder.setEndLon(endPoint.getPointLon());
xpCruiseOrder.setEndLat(endPoint.getPointLat());
xpCruiseOrder.setMissionId(xpCruisePlanPoint.getCruisePlanId());
xpCruiseOrder.setOrderReverse(1);
}
xpCruiseOrder.setStatus(0);
xpCruiseOrder.setCreateTime(LocalDateTime.now());
// 判断当前时分秒是否大于收车时间&&收车配置开启
if(null != xpTaskRule && xpTaskRule.getVehicleCollection()){
LocalTime vehicleCollectionTime = xpTaskRule.getVehicleCollectionTime();
if (vehicleCollectionTime.isBefore(LocalTime.now())) {
XpVehicle vehicle1 = new XpVehicle();
vehicle1.setId(vehicle.getId());
vehicle1.setOperatingStatus(3);
xpVehicleClient.updateVehicleById(vehicle1);
//找到车辆的收车点位 更新终点点位
R returnCruisePoint = xpCruisePlanClient.findReturnCruisePoint(xpCruiseOrder);
if(200 != returnCruisePoint.getCode()){
xpCruiseOrder = returnCruisePoint.getData();
xpCruiseOrder.setOrderType(2);
xpCruiseOrder.setOrderReverse(1);
}
}
}
xpCruiseOrderMapper.insert(xpCruiseOrder);
log.info("添加巡游单成功 {}", xpCruiseOrder.getId());
//下发XUP巡游订单报文
PushOrderDto pushOrderDto = new PushOrderDto();
pushOrderDto.setVin(xpCruiseOrder.getVehicleVin());
pushOrderDto.setOrderId("XU" + xpCruiseOrder.getId());
pushOrderDto.setStartLatitude(xpCruiseOrder.getStartLat());
pushOrderDto.setStartLongitude(xpCruiseOrder.getStartLon());
pushOrderDto.setEndLatitude(xpCruiseOrder.getEndLat());
pushOrderDto.setEndLongitude(xpCruiseOrder.getEndLon());
pushOrderDto.setOrderType(OrderType.CRUISE_ORDER);
pushOrderDto.setStage(2);
orderMissionClient.pushOrder(pushOrderDto);
log.info("下发XUP巡游订单报文成功");
}
/**
* 取消订单后的继续巡游任务
* @param vehicleId
*/
public void addCruiseMission(Long vehicleId) {
XpVehicle vehicle = xpVehicleClient.queryById(vehicleId).getData();
if (vehicle.getState() != 0 || vehicle.getOperatingStatus() != 1) {
return;
}
// 判断是否有计划
R xpCruisePlanR = xpCruisePlanClient.getCruisePlan(vehicle.getId());
if (xpCruisePlanR == null || xpCruisePlanR.getData() == null) {
log.info("未找到匹配的巡游计划,无法执行巡游订单 {}", vehicle.getPlateNumber());
return;
}
VehiclePosition vehiclePosition = mongoTemplate.findOne(new Query(Criteria.where("vin").is(vehicle.getVehicleVin())), VehiclePosition.class);
// 获取配置 根据配置判断是否该收车 如果是则找到收车点位进行收车操作
R xpTaskRuleR = xpTaskRuleClient.queryDetail();
XpTaskRule xpTaskRule = xpTaskRuleR.getData();
// 生成新的巡游单子 回到最新巡游的初始起点位置
XpCruiseOrder xpCruiseOrders = xpCruiseOrderMapper.selectOne(new LambdaQueryWrapper().eq(XpCruiseOrder::getVehicleId, vehicle.getId()).eq(XpCruiseOrder::getStatus, 2).orderByDesc(BaseModel::getCreateTime).last("limit 1"));
int temp = 0;
if (xpCruiseOrders == null) {
temp = 1;
//特殊情况 可能没有产生终止的订单 (到站点就被正常接单了)所以找正常订单
xpCruiseOrders = xpCruiseOrderMapper.selectOne(new LambdaQueryWrapper().eq(XpCruiseOrder::getVehicleId, vehicle.getId()).eq(XpCruiseOrder::getStatus, 3).orderByDesc(BaseModel::getCreateTime).last("limit 1"));
}
XpCruiseOrder xpCruiseOrder = new XpCruiseOrder();
xpCruiseOrder.setVehicleId(vehicle.getId());
xpCruiseOrder.setFleetId(vehicle.getFleetId());
xpCruiseOrder.setVehicleVin(vehicle.getVehicleVin());
xpCruiseOrder.setOrderType(2);
// 返程起点是当前正常订单的终点
xpCruiseOrder.setStartPointId(null);
xpCruiseOrder.setStartPointName(null);
xpCruiseOrder.setStartLon(vehiclePosition.getPoint().getX());
xpCruiseOrder.setStartLat(vehiclePosition.getPoint().getY());
// 完成的直接走终点 终止的走起点
if(null != xpCruiseOrders){
if (temp == 0) {
//当上一笔单有起点点位,才需要将上一笔巡游单的起点作为本次的终点导航
//上一笔单没有起点点位,统一用后面的巡游计划的第一个点位作为本次的终点导航
if(null != xpCruiseOrders.getStartPointId()){
xpCruiseOrder.setEndPointId(xpCruiseOrders.getStartPointId());
xpCruiseOrder.setEndPointName(xpCruiseOrders.getStartPointName());
xpCruiseOrder.setEndLon(xpCruiseOrders.getStartLon());
xpCruiseOrder.setEndLat(xpCruiseOrders.getStartLat());
}
} else {
xpCruiseOrder.setEndPointId(xpCruiseOrders.getEndPointId());
xpCruiseOrder.setEndPointName(xpCruiseOrders.getEndPointName());
xpCruiseOrder.setEndLon(xpCruiseOrders.getEndLon());
xpCruiseOrder.setEndLat(xpCruiseOrders.getEndLat());
}
xpCruiseOrder.setOrderReverse(xpCruiseOrders.getOrderReverse());
xpCruiseOrder.setLastOrderId(xpCruiseOrders.getId());
xpCruiseOrder.setMissionId(xpCruiseOrders.getMissionId());
}
//所有没有赋值终点点位的情况,全部导航到巡游计划的第一个点位开始执行巡游任务
if(null == xpCruiseOrder.getEndPointId()){
// 先找计划
List planPointList = xpCruisePlanClient.getCruisePlanPointList(vehicle.getId()).getData();
XpCruisePlanPoint xpCruisePlanPoint = planPointList.get(0);
Long endPointId = xpCruisePlanPoint.getPointId();
XpPoint endPoint = xpPointClient.queryById(endPointId).getData();
xpCruiseOrder.setEndPointId(endPointId);
xpCruiseOrder.setEndPointName(endPoint.getPointName());
xpCruiseOrder.setEndLon(endPoint.getPointLon());
xpCruiseOrder.setEndLat(endPoint.getPointLat());
xpCruiseOrder.setMissionId(xpCruisePlanPoint.getCruisePlanId());
xpCruiseOrder.setOrderReverse(1);
}
xpCruiseOrder.setStatus(0);
xpCruiseOrder.setCreateTime(LocalDateTime.now());
// 判断当前时分秒是否大于收车时间&&收车配置开启
if(null != xpTaskRule && xpTaskRule.getVehicleCollection()){
LocalTime vehicleCollectionTime = xpTaskRule.getVehicleCollectionTime();
if (vehicleCollectionTime.isBefore(LocalTime.now())) {
XpVehicle vehicle1 = new XpVehicle();
vehicle1.setId(vehicle.getId());
vehicle1.setOperatingStatus(3);
xpVehicleClient.updateVehicleById(vehicle1);
//找到车辆的收车点位 更新终点点位
R returnCruisePoint = xpCruisePlanClient.findReturnCruisePoint(xpCruiseOrder);
if(200 != returnCruisePoint.getCode()){
xpCruiseOrder = returnCruisePoint.getData();
xpCruiseOrder.setOrderType(2);
xpCruiseOrder.setOrderReverse(1);
}
}
}
xpCruiseOrderMapper.insert(xpCruiseOrder);
log.info("巡游订单创建成功,开始执行巡游订单 {}", JSON.toJSONString(xpCruiseOrder));
//下发XPU消息报文
PushOrderDto pushOrderDto = new PushOrderDto();
pushOrderDto.setVin(xpCruiseOrder.getVehicleVin());
pushOrderDto.setOrderId("XU" + xpCruiseOrder.getId());
pushOrderDto.setStartLatitude(xpCruiseOrder.getStartLat());
pushOrderDto.setStartLongitude(xpCruiseOrder.getStartLon());
pushOrderDto.setEndLatitude(xpCruiseOrder.getEndLat());
pushOrderDto.setEndLongitude(xpCruiseOrder.getEndLon());
pushOrderDto.setOrderType(OrderType.CRUISE_ORDER);
pushOrderDto.setStage(2);
// 调用推送服务 orderId + carVin
log.info("开始推送XPU巡游订单 {}", xpCruiseOrder.getVehicleVin());
orderMissionClient.pushOrder(pushOrderDto);
}
private MileageDto mileageType(MileageDto param, JSONArray startingFareJsonArray, Date mileageDate, LocalDateTime begin, String num14, String num16, String num17, String num21, String num22, String num24, String num25, String num30, String num31) {
// 计算出是在哪个普通时间段内
for (Object o : startingFareJsonArray) {
JSONObject startingFare = (JSONObject) o;
Calendar calendarStart = Calendar.getInstance();
calendarStart.setTime(mileageDate);
calendarStart.set(Calendar.HOUR_OF_DAY, Integer.valueOf(startingFare.getString("num1").split(":")[0]));
calendarStart.set(Calendar.MINUTE, Integer.valueOf(startingFare.getString("num1").split(":")[1]));
Calendar calendarEnd = Calendar.getInstance();
calendarEnd.setTime(mileageDate);
calendarEnd.set(Calendar.HOUR_OF_DAY, Integer.valueOf(startingFare.getString("num2").split(":")[0]));
calendarEnd.set(Calendar.MINUTE, Integer.valueOf(startingFare.getString("num2").split(":")[1]));
// 找到符合条件的直接跳出循环
if (mileageDate.getTime() > calendarStart.getTimeInMillis() && mileageDate.getTime() < calendarEnd.getTimeInMillis()) {
param.setMileage(startingFare.getString("num6"));
param.setTime(startingFare.getString("num7"));
param.setMileageTypeTemp(1);
break;
}
}
// 根据时间确定是否使用基础信息配置
LocalDateTime createTime = begin;
Date date = DateUtils.toDate(createTime);
LocalDate localDate = DateUtils.toLocalDate(date);
// 判断是否为夜间行驶 获取里程费用
Calendar calendarStart = Calendar.getInstance();
calendarStart.setTime(date);
calendarStart.set(Calendar.HOUR_OF_DAY, Integer.valueOf(num14.split("-")[0].split(":")[0]));
calendarStart.set(Calendar.MINUTE, Integer.valueOf(num14.split("-")[0].split(":")[1]));
Calendar calendarEnd = Calendar.getInstance();
calendarEnd.setTime(date);
calendarEnd.set(Calendar.HOUR_OF_DAY, Integer.valueOf(num14.split("-")[1].split(":")[0]));
calendarEnd.set(Calendar.MINUTE, Integer.valueOf(num14.split("-")[1].split(":")[1]));
if (date.getTime() > calendarStart.getTimeInMillis() && date.getTime() < calendarEnd.getTimeInMillis()
) {
param.setMileage(num16);
param.setTime(num17);
param.setMileageTypeTemp(2);
}
// 判断是否为高峰期 获取里程费用
Calendar calendar1Start = Calendar.getInstance();
calendar1Start.setTime(date);
calendar1Start.set(Calendar.HOUR_OF_DAY, Integer.valueOf(num21.split("-")[0].split(":")[0]));
calendar1Start.set(Calendar.MINUTE, Integer.valueOf(num21.split("-")[0].split(":")[1]));
Calendar calendar1End = Calendar.getInstance();
calendar1End.setTime(date);
calendar1End.set(Calendar.HOUR_OF_DAY, Integer.valueOf(num21.split("-")[1].split(":")[0]));
calendar1End.set(Calendar.MINUTE, Integer.valueOf(num21.split("-")[1].split(":")[1]));
Calendar calendar2Start = Calendar.getInstance();
calendar2Start.setTime(date);
calendar2Start.set(Calendar.HOUR_OF_DAY, Integer.valueOf(num22.split("-")[0].split("-")[0].split(":")[0]));
calendar2Start.set(Calendar.MINUTE, Integer.valueOf(num22.split("-")[0].split("-")[0].split(":")[1]));
Calendar calendar2End = Calendar.getInstance();
calendar2End.setTime(date);
calendar2End.set(Calendar.HOUR_OF_DAY, Integer.valueOf(num22.split("-")[1].split(":")[0]));
calendar2End.set(Calendar.MINUTE, Integer.valueOf(num22.split("-")[1].split(":")[1]));
if ((date.getTime() > calendar1Start.getTimeInMillis() && date.getTime() < calendar1End.getTimeInMillis())
|| (date.getTime() > calendar2Start.getTimeInMillis() && date.getTime() < calendar2End.getTimeInMillis())) {
param.setMileage(num24);
param.setTime(num25);
param.setMileageTypeTemp(3);
}
// 判断当前是否属于节假日 获取里程费用
boolean holiday = holidayUtil.isHoliday(localDate);
if (holiday) {
// 判断是否为节假日 起步价修改
param.setMileage(num30);
param.setTime(num31);
param.setMileageTypeTemp(4);
}
return param;
}
private void calculateStartingFare(List trajectories, LocalDateTime tripBegin, boolean startFlag, Double startDistance, JSONArray startingFareJsonArray, String num3, String num4, String num5, Date endDate, XpOrder order, String num14, String num15, String num16, String num17, Integer startType, String num21, String num22, String num23, String num24, String num25, String num29, String num30, String num31, SimpleDateFormat timeFormat, Date startDate, List start) {
String startTime;
String num6;
String num7;
String endTime;
// 根据订单创建时间确定是否使用基础信息配置
LocalDateTime createTime = order.getCreateTime();
Date date = DateUtils.toDate(createTime);
LocalDate localDate = DateUtils.toLocalDate(date);
for (Object o : startingFareJsonArray) {
JSONObject startingFare = (JSONObject) o;
Calendar calendarStart = Calendar.getInstance();
calendarStart.setTime(date);
calendarStart.set(Calendar.HOUR_OF_DAY, Integer.valueOf(startingFare.getString("num1").split(":")[0]));
calendarStart.set(Calendar.MINUTE, Integer.valueOf(startingFare.getString("num1").split(":")[1]));
Calendar calendarEnd = Calendar.getInstance();
calendarEnd.setTime(date);
calendarEnd.set(Calendar.HOUR_OF_DAY, Integer.valueOf(startingFare.getString("num2").split(":")[0]));
calendarEnd.set(Calendar.MINUTE, Integer.valueOf(startingFare.getString("num2").split(":")[1]));
// 找到符合条件的直接跳出循环
if (date.getTime() > calendarStart.getTimeInMillis() && date.getTime() < calendarEnd.getTimeInMillis()) {
num3 = startingFare.getString("num3");
num4 = startingFare.getString("num4");
num5 = startingFare.getString("num5");
num6 = startingFare.getString("num6");
num7 = startingFare.getString("num7");
startTime = startingFare.getString("num1");
endTime = startingFare.getString("num2");
break;
}
}
// 判断是否为夜间行驶 起步价修改
Calendar calendarStart = Calendar.getInstance();
calendarStart.setTime(date);
calendarStart.set(Calendar.HOUR_OF_DAY, Integer.valueOf(num14.split("-")[0].split(":")[0]));
calendarStart.set(Calendar.MINUTE, Integer.valueOf(num14.split("-")[0].split(":")[1]));
Calendar calendarEnd = Calendar.getInstance();
calendarEnd.setTime(date);
calendarEnd.set(Calendar.HOUR_OF_DAY, Integer.valueOf(num14.split("-")[1].split(":")[0]));
calendarEnd.set(Calendar.MINUTE, Integer.valueOf(num14.split("-")[1].split(":")[1]));
if (date.getTime() > calendarStart.getTimeInMillis() && date.getTime() < calendarEnd.getTimeInMillis()
) {
num3 = num15;
num6 = num16;
num7 = num17;
startType = 2;
}
// 判断是否为高峰期
Calendar calendar1Start = Calendar.getInstance();
calendar1Start.setTime(date);
calendar1Start.set(Calendar.HOUR_OF_DAY, Integer.valueOf(num21.split("-")[0].split(":")[0]));
calendar1Start.set(Calendar.MINUTE, Integer.valueOf(num21.split("-")[0].split(":")[1]));
Calendar calendar1End = Calendar.getInstance();
calendar1End.setTime(date);
calendar1End.set(Calendar.HOUR_OF_DAY, Integer.valueOf(num21.split("-")[1].split(":")[0]));
calendar1End.set(Calendar.MINUTE, Integer.valueOf(num21.split("-")[1].split(":")[1]));
Calendar calendar2Start = Calendar.getInstance();
calendar2Start.setTime(date);
calendar2Start.set(Calendar.HOUR_OF_DAY, Integer.valueOf(num22.split("-")[0].split("-")[0].split(":")[0]));
calendar2Start.set(Calendar.MINUTE, Integer.valueOf(num22.split("-")[0].split("-")[0].split(":")[1]));
Calendar calendar2End = Calendar.getInstance();
calendar2End.setTime(date);
calendar2End.set(Calendar.HOUR_OF_DAY, Integer.valueOf(num22.split("-")[1].split(":")[0]));
calendar2End.set(Calendar.MINUTE, Integer.valueOf(num22.split("-")[1].split(":")[1]));
// 判断是否为高峰期 起步价,里程费,时长费修改
if ((date.getTime() > calendar1Start.getTimeInMillis() && date.getTime() < calendar1End.getTimeInMillis())
|| (date.getTime() > calendar2Start.getTimeInMillis() && date.getTime() < calendar2End.getTimeInMillis())) {
num3 = num23;
num6 = num24;
num7 = num25;
startType = 3;
}
// 判断当前是否属于节假日
boolean holiday = holidayUtil.isHoliday(localDate);
if (holiday) {
// 判断是否为节假日 起步价修改
num3 = num29;
num6 = num30;
num7 = num31;
startType = 4;
}
// 起步价价格明细封装
FareBreakdownDetailVo fareBreakdownDetailVo = new FareBreakdownDetailVo();
fareBreakdownDetailVo.setType(startType);
fareBreakdownDetailVo.setTime(timeFormat.format(startDate) + "-" + timeFormat.format(endDate));
fareBreakdownDetailVo.setMileage(new BigDecimal(startDistance / 1000).setScale(2, RoundingMode.HALF_UP));
// 计算startDate和endDate相差多少分钟
// 将 Date 转换为 LocalDateTime
LocalDateTime startDateTime = startDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
LocalDateTime endDateTime = endDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
// 计算时间差(单位:分钟)
long minutesDifference = Duration.between(startDateTime, endDateTime).toMinutes();
fareBreakdownDetailVo.setDuration((double) minutesDifference);
fareBreakdownDetailVo.setMoney(new BigDecimal(num3));
start.add(fareBreakdownDetailVo);
}
/**
* 获取配置 4每X单 5上升X% 6冷却
*
* @param rule
* @param num
* @return
*/
private Integer getOrderNum(JSONObject rule, int num) {
Integer param = rule.getInteger("num" + num);
return param;
}
@Override
public R fareBreakdown(Long orderId, String uid) {
XpOrder order = this.getById(orderId);
order.setAppUserId(null);
FareBreakdownVo res = new FareBreakdownVo();
res.setChangeMoneyFlag(order.getChangeMoneyFlag());
List start = new ArrayList<>();
List mileage = new ArrayList<>();
List duration = new ArrayList<>();
List longDistance = new ArrayList<>();
List xpOrderDetails = xpOrderDetailMapper.selectList(new LambdaQueryWrapper().eq(XpOrderDetail::getOrderId, orderId));
for (XpOrderDetail xpOrderDetail : xpOrderDetails) {
FareBreakdownDetailVo detail = new FareBreakdownDetailVo();
detail.setType(xpOrderDetail.getType());
detail.setTime(xpOrderDetail.getTime());
detail.setMileage(xpOrderDetail.getMileage());
detail.setDuration((xpOrderDetail.getDuration() == null ? 0 : xpOrderDetail.getDuration())*60);
detail.setMoney(xpOrderDetail.getMoney());
if (xpOrderDetail.getDetailType() == 1) {
start.add(detail);
} else if(xpOrderDetail.getDetailType()==2){
mileage.add(detail);
}else if(xpOrderDetail.getDetailType()==3){
duration.add(detail);
}else if(xpOrderDetail.getDetailType()==4){
longDistance.add(detail);
}
}
res.setStart(start);
res.setMileage(mileage);
res.setDuration(duration);
res.setLongDistance(longDistance);
res.setRedPacketMoney(order.getRedPacketMoney());
res.setCouponMoney(order.getCouponMoney());
res.setOrderMoney(order.getOrderMoney());
res.setKilometer(order.getMileageKilometers() == null ? BigDecimal.ZERO : BigDecimal.valueOf(order.getMileageKilometers()).multiply(BigDecimal.valueOf(1000)));
LocalDateTime createTime = order.getCreateTime();
LocalDateTime tripEnd = order.getTripEnd();
if (tripEnd == null) {
tripEnd = LocalDateTime.now();
order.setTripEnd(tripEnd);
this.updateById(order);
}
res.setTime(new BigDecimal(order.getDuration() == null ? 0 : order.getDuration()).multiply(new BigDecimal(60)).setScale(0, RoundingMode.HALF_UP));
return R.ok(res);
}
@Override
public R payment(XpOrderPayDto dto, String uid) {
// mvp版本不做支付 使用优惠券抵扣为0元订单处理 处理订单状态即可
XpOrder order = this.getById(dto.getOrderId());
order.setStatus(9);
order.setAppUserId(null);
if(order.getCouponId()!=null){
UseUserCouponDto useUserCouponDto = new UseUserCouponDto();
useUserCouponDto.setUserCouponId(order.getCouponId());
R decimalR = xpAppUserCouponClient.useUserCoupon(useUserCouponDto);
if(decimalR.getCode()==200){
order.setCouponMoney(decimalR.getData());
if(order.getCouponMoney().compareTo(order.getOrderMoney())>0){
order.setPayMoney(BigDecimal.ZERO);
order.setCouponMoney(order.getOrderMoney());
}else {
order.setPayMoney(order.getOrderMoney().subtract(decimalR.getData()));
}
}
}
baseMapper.updateById(order);
redisService.setCacheObject("ORDER_STATUS:" + order.getId(), 9, 24L, TimeUnit.HOURS);
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);
if (xpOrder == null || xpOrder.getVehicleId() == null) {
return null;
}
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 Integer countActiveOrdersByCityId(Long cityId) {
Long num = this.baseMapper.selectCount(new LambdaQueryWrapper()
.eq(XpOrder::getCityId, cityId)
.eq(XpOrder::getStatus, 1));
return num.intValue();
}
@Override
public void saveVehicleLocation(OrderTrajectory orderTrajectory) {
// 查询订单信息
Integer status = redisService.getCacheObject("ORDER_STATUS:" + orderTrajectory.getOrderId());
if(status < 6 || status > 8){
return;
}
// 计算距离
// 获取上一次定位
String oldLocation = redisService.getCacheObject(CacheConstants.ORDER_OLD_LOCATION + orderTrajectory.getOrderId());
if (StringUtils.isEmpty(oldLocation)) {
orderTrajectory.setDistance(0D);
// 存储mongo数据
mongoTemplate.save(orderTrajectory);
// 存储首次定位
redisService.setCacheObject(CacheConstants.ORDER_OLD_LOCATION + orderTrajectory.getOrderId(), orderTrajectory.getLocation(), 1L, TimeUnit.HOURS);
} else {
Map distance = GeodesyUtil.getDistance(orderTrajectory.getLocation(), oldLocation);
if (null != distance) {
Double distance1 = distance.get("WGS84");
// 存储本次距离
orderTrajectory.setDistance(distance1);
// 存储mongo数据
mongoTemplate.save(orderTrajectory);
// 更新定位
redisService.setCacheObject(CacheConstants.ORDER_OLD_LOCATION + orderTrajectory.getOrderId(), orderTrajectory.getLocation(), 1L, TimeUnit.HOURS);
} else {
log.error("调用高德计算距离出错");
}
}
}
@Override
public OrderDetailVo orderDetail(Long orderId, String uid) {
OrderDetailVo orderDetailVo = new OrderDetailVo();
XpOrder order = this.getById(orderId);
BeanUtils.copyProperties(order, orderDetailVo);
// 查询车辆信息
XpVehicle vehicle = xpVehicleClient.queryById(order.getVehicleId()).getData();
if (vehicle != null) {
orderDetailVo.setVehicleCode(vehicle.getVehicleCode());
orderDetailVo.setPlateNumber(vehicle.getPlateNumber());
orderDetailVo.setVehicleColor(vehicle.getVehicleColor());
orderDetailVo.setVehiclePictures(vehicle.getVehiclePictures());
}
// 查询评价信息
XpOrderEvaluate evaluate = xpOrderEvaluateService.lambdaQuery().eq(XpOrderEvaluate::getOrderId, orderId)
.last("limit 1").one();
if (evaluate != null) {
orderDetailVo.setFeedbackPictures(evaluate.getFeedbackPictures());
orderDetailVo.setFeedbackContent(evaluate.getFeedbackContent());
orderDetailVo.setOrderRating(evaluate.getOrderRating());
if (evaluate.getFeedbackTypeId() != null) {
XpFeedbackType feedbackType = xpSystemSettingClient.getFeedbackTypeById(evaluate.getFeedbackTypeId()).getData();
orderDetailVo.setFeedbackTypeName(feedbackType.getFeedbackType());
}
}
List trajectories = queryOrderTrajectories(orderId);
orderDetailVo.setTrajectories(trajectories);
// 起步价、里程费、时长费、远途费:按订单费用明细聚合,覆盖订单表字段
Map feeMap = sumOrderDetailMoneyByDetailType(orderId);
orderDetailVo.setStartMoney(feeMap.getOrDefault(1, BigDecimal.ZERO));
orderDetailVo.setMileageMoney(feeMap.getOrDefault(2, BigDecimal.ZERO));
orderDetailVo.setDurationMoney(feeMap.getOrDefault(3, BigDecimal.ZERO));
orderDetailVo.setLongDistancemoney(feeMap.getOrDefault(4, BigDecimal.ZERO));
if (order.getBoardingTime() != null && order.getTripEnd() != null) {
// 计算两个时间相差多少分钟
double exactMinutes = Duration.between(order.getBoardingTime(), order.getTripEnd()).toMillis() / (1000.0 * 60);
exactMinutes = new BigDecimal(exactMinutes).setScale(2, RoundingMode.HALF_UP).doubleValue();
orderDetailVo.setOrderTime(exactMinutes);
} else {
orderDetailVo.setOrderTime(0.0);
}
orderDetailVo.setTotalMileage(order.getMileageKilometers() != null ? new BigDecimal(order.getMileageKilometers() * 1000).setScale(2, RoundingMode.HALF_UP).doubleValue() : 0D);
orderDetailVo.setOrderTime(new BigDecimal(null == order.getDuration() ? 0 : order.getDuration()).multiply(new BigDecimal(60)).setScale(0, RoundingMode.HALF_EVEN).doubleValue());
return orderDetailVo;
}
/**
* 查询订单轨迹(mongodb)
*
* @param orderId 订单id
* @return 轨迹列表(按 locatetime 升序)
*/
private List queryOrderTrajectories(Long orderId) {
if (orderId == null) {
return new ArrayList<>();
}
List trajectories = mongoTemplate.find(
new Query(Criteria.where("orderId").is(orderId))
.with(Sort.by(Sort.Direction.ASC, "locatetime")),
OrderTrajectory.class
);
return trajectories;
}
/**
* 订单费用明细聚合:按 detail_type 汇总 money
*
* detail_type:1=起步价 2=里程费 3=时长费 4=远途费
*/
private Map sumOrderDetailMoneyByDetailType(Long orderId) {
if (orderId == null) {
return Collections.emptyMap();
}
List detailList = xpOrderDetailMapper.selectList(
new LambdaQueryWrapper()
.eq(XpOrderDetail::getOrderId, orderId)
.ne(XpOrderDetail::getIsDeleted, true)
);
if (CollectionUtils.isEmpty(detailList)) {
return Collections.emptyMap();
}
Map sumMap = new HashMap<>();
for (XpOrderDetail detail : detailList) {
if (detail == null || detail.getDetailType() == null) {
continue;
}
BigDecimal money = detail.getMoney() == null ? BigDecimal.ZERO : detail.getMoney();
sumMap.put(detail.getDetailType(), sumMap.getOrDefault(detail.getDetailType(), BigDecimal.ZERO).add(money));
}
return sumMap;
}
/**
* 将轨迹点转换为订单经纬度列表
*/
private List convertTrajectoriesToPositions(List trajectories) {
if (CollectionUtils.isEmpty(trajectories)) {
return new ArrayList<>();
}
List positions = new ArrayList<>();
for (OrderTrajectory trajectory : trajectories) {
if (trajectory == null || StringUtils.isBlank(trajectory.getLocation())) {
continue;
}
String[] parts = trajectory.getLocation().split(",");
if (parts.length != 2) {
continue;
}
try {
double longitude = Double.parseDouble(parts[0].trim());
double latitude = Double.parseDouble(parts[1].trim());
XpOrderDetailVO.OrderPosition position = new XpOrderDetailVO.OrderPosition();
position.setLongitude(longitude);
position.setLatitude(latitude);
positions.add(position);
} catch (Exception e) {
// ignore invalid point
}
}
return positions;
}
@Override
public R feeDescription(Long orderId, String uid) {
XpOrder order = this.getById(orderId);
R pricingByCityId = xpPricingClient.getPricingByCid(order.getCityId());
XpPricing pricing = pricingByCityId.getData();
if (pricing == null) {
throw new ServiceException("当前城市未配置定价规则", I18nKeyConstant.ORDER_PRICING_NOT_EXISTS);
} else {
return R.ok(pricing.getPriceRule());
}
}
/**
* 成功操作下发命令后调用 用于弹出redis队列的获取成功的数据
*
* @param dto
*/
@Override
@Transactional
@ShardingSphereTransactionType(TransactionType.BASE)
public void popOrder(PopOrderDto dto) {
log.info("收到XPU消息响应后的业务处理 {}", dto.getOrderId());
String orderType = dto.getOrderType();
Long orderId = dto.getOrderId();
//巡游单处理逻辑
if (OrderType.CRUISE_ORDER.equals(orderType)) {
XpCruiseOrder xpCruiseOrder = xpCruiseOrderMapper.selectById(orderId);
if (xpCruiseOrder != null) {
xpCruiseOrder.setStatus(1);
xpCruiseOrder.setVehicleId(null);
xpCruiseOrderMapper.updateById(xpCruiseOrder);
log.info("修改巡游单状态,开始执行巡游单 {}", dto.getOrderId());
// 封装埋点消息实体
VehicleEventDTO vehicleEventDTO = new VehicleEventDTO();
vehicleEventDTO.setVin(dto.getXpVehicleVin());
vehicleEventDTO.setXpuId("");
vehicleEventDTO.setOrderId(String.valueOf(dto.getOrderId()));
vehicleEventDTO.setEventKey("RT_xpu_vehicle_trace_upload");
vehicleEventDTO.setEventTime(System.currentTimeMillis());
Map attributes = new HashMap<>();
attributes.put("vin",vehicleEventDTO.getVin());
attributes.put("order_id",vehicleEventDTO.getOrderId());
attributes.put("order_type","cruise_order");
attributes.put("timestamp",vehicleEventDTO.getEventTime());
attributes.put("current_main_status", OrderStatus.ORDER_STARTED);
attributes.put("current_sub_status","");
VehiclePosition vehiclePosition = mongoTemplate.findOne(new Query(Criteria.where("vin").is(dto.getXpVehicleVin())), VehiclePosition.class);
if(Objects.nonNull(vehiclePosition)){
attributes.put("odometer_km",vehiclePosition.getTotalOdometer());
}else {
attributes.put("odometer_km","");
}
vehicleEventDTO.setAttributes(attributes);
vehicleBuriedPointClient.sendVehicleEvent(vehicleEventDTO);
log.info("巡游单埋点消息,开始执行推送: {}", dto.getOrderId());
}
}
//乘客单处理逻辑
if (OrderType.USER_ORDER.equals(orderType)) {
XpOrder order = this.getById(orderId);
Long appUserId = order.getAppUserId();
//订单为待接单状态
if (order.getStatus() < 4) {
AddTaxiPassengerVo addTaxiPassengerVo = new AddTaxiPassengerVo();
order.setAcceptanceTime(LocalDateTime.now());
R byVehicleVin = xpVehicleClient.findByVehicleVin(dto.getXpVehicleVin());
XpVehicle vehicle = null;
if (byVehicleVin.getCode() == 200) {
vehicle = byVehicleVin.getData();
order.setVehicleId(vehicle.getId());
order.setFleetId(vehicle.getFleetId());
addTaxiPassengerVo.setVin(vehicle.getVehicleVin());
}
order.setStatus(2);
// Redis弹出这个队列元素
// value 订单Id_经纬度_状态_用户id_订单编号_距离_时间
try {
redisService.popFirstElement(order.getQueueName());
} catch (Exception e) {
log.error("Redis弹出队列元素出错");
}
// 存mongo数据
xpPricingClient.updateXpGeoQueryEntity(order.getId().toString());
// 修改车辆信息
XpVehicle vehicle1 = new XpVehicle();
vehicle1.setId(vehicle.getId());
vehicle1.setState(1);
xpVehicleClient.updateVehicleById(vehicle1);
log.info("推单成功后的处理逻辑,车辆状态修改1 {}", vehicle.getPlateNumber());
//绑定用户和车辆关系
addTaxiPassengerVo.setUid(appUserId.toString());
order.setAppUserId(null);
this.updateById(order);
redisService.setCacheObject("ORDER_STATUS:" + order.getId(), 2, 24L, TimeUnit.HOURS);
//构建推送消息
Map params = new HashMap<>();
Map content = new HashMap<>();
content.put("orderId", orderId.toString());
content.put("status", 2);
params.put("type", PushMessageTypeEnum.ORDER_STATUS.getDesc());
params.put("content", JSONObject.toJSONString(content));
params.put("timestamp", System.currentTimeMillis() + "");
String message = JSONObject.toJSONString(params);
SendMessageDTO dto1 = new SendMessageDTO();
dto1.setAppUserId(appUserId + "");
dto1.setMessage(message);
R stringR = xpPushMessageClient.sendStatus(dto1);
//推送失败,重试3次
if(stringR.getCode() != Constants.SUCCESS){
ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());
executor.execute(() -> {
for (int i = 0; i < 3; i++) {
R stringR1 = xpPushMessageClient.sendStatus(dto1);
if(stringR1.getCode() == Constants.SUCCESS){
break;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
log.info("推送订单状态消息失败:{} {} {}", stringR1.getMsg(), JSON.toJSONString(dto), order.getOrderNumber());
}
});
}
redisService.setCacheObject("ORDER_STATUS:" + order.getId(), 2, 24L, TimeUnit.HOURS);
addTaxiPassengerVo.setOrderNo(order.getOrderNumber());
addTaxiPassengerVo.setAppId(appid);
userVehicleClient.addTaxiPassenger(addTaxiPassengerVo);
//生成前往上车点的导航数据
//查询车辆位置
VehiclePosition vehiclePosition = mongoTemplate.findOne(new Query(Criteria.where("vin").is(dto.getXpVehicleVin())), VehiclePosition.class);
if (Objects.isNull(vehiclePosition)) {
throw new RuntimeException("车辆位置信息不存在");
}
LonLatDto destination = new LonLatDto();
LonLatDto origin = new LonLatDto();
//查询司机当前位置到起点的线路
origin.setLongitude(vehiclePosition.getPoint().getX());
origin.setLatitude(vehiclePosition.getPoint().getY());
destination.setLongitude(order.getStartLon());
destination.setLatitude(order.getStartLat());
OrderRouteAndFareDto dto2 = new OrderRouteAndFareDto();
dto2.setOrderId(orderId);
dto2.setOrigin(origin);
dto2.setDestination(destination);
dto2.setRouteType(1);
dto2.setVehicleId(vehicle.getId());
R routeVoR = xpMapClient.getOrderDistanceAndDuration(dto2);
if (200 != routeVoR.getCode()) {
throw new RuntimeException("获取订单线路和运费失败");
}
RouteVo routeVo = routeVoR.getData();
//缓存数据,用于后续推给乘客导航线路
redisService.setCacheObject("ORDER_NAVIGATION_START_DATA:" + appUserId, JSON.toJSONString(routeVo), 24L, TimeUnit.HOURS);
//推送乘客车辆到达预约点的导航数据
sendRoute(addTaxiPassengerVo.getUid(), orderId, "ORDER_NAVIGATION_START_DATA", dto.getXpVehicleVin());
}
if(order.getStatus() == 2){
// 封装埋点消息实体
VehicleEventDTO vehicleEventDTO = new VehicleEventDTO();
vehicleEventDTO.setVin(dto.getXpVehicleVin());
vehicleEventDTO.setXpuId("");
vehicleEventDTO.setOrderId(String.valueOf(dto.getOrderId()));
vehicleEventDTO.setEventKey("RT_xpu_vehicle_trace_upload");
vehicleEventDTO.setEventTime(System.currentTimeMillis());
Map attributes = new HashMap<>();
attributes.put("vin",vehicleEventDTO.getVin());
attributes.put("order_id",vehicleEventDTO.getOrderId());
attributes.put("order_type","user_order");
attributes.put("timestamp",vehicleEventDTO.getEventTime());
attributes.put("current_main_status",OrderStatus.ORDER_EN_ROUTE_TO_PICKUP);
attributes.put("current_sub_status","");
VehiclePosition vehiclePosition = mongoTemplate.findOne(new Query(Criteria.where("vin").is(dto.getXpVehicleVin())), VehiclePosition.class);
if(Objects.nonNull(vehiclePosition)){
attributes.put("odometer_km",vehiclePosition.getTotalOdometer());
}else {
attributes.put("odometer_km","");
}
vehicleEventDTO.setAttributes(attributes);
vehicleBuriedPointClient.sendVehicleEvent(vehicleEventDTO);
log.info("用户单埋点消息,开始执行推送: {}", dto.getOrderId());
}
}
//测试订单
if (OrderType.TEST_ORDER.equals(orderType)) {
XpTestOrder xpTestOrder = xpTestOrderService.getById(orderId);
//订单为待接单状态
if (xpTestOrder.getStatus() < 4) {
xpTestOrder.setAcceptanceTime(LocalDateTime.now());
R byVehicleVin = xpVehicleClient.findByVehicleVin(dto.getXpVehicleVin());
XpVehicle vehicle = null;
if (byVehicleVin.getCode() == 200) {
vehicle = byVehicleVin.getData();
xpTestOrder.setVehicleId(vehicle.getId());
xpTestOrder.setFleetId(vehicle.getFleetId());
}
xpTestOrder.setStatus(2);
// 修改车辆信息
XpVehicle vehicle1 = new XpVehicle();
vehicle1.setId(vehicle.getId());
vehicle1.setState(1);
xpVehicleClient.updateVehicleById(vehicle1);
log.info("推单成功后的处理逻辑,车辆状态修改1 {}", vehicle.getPlateNumber());
xpTestOrder.setStartTestPointId(null);
xpTestOrderService.updateById(xpTestOrder);
redisService.setCacheObject("TEST_ORDER_STATUS:" + xpTestOrder.getId(), 2, 24L, TimeUnit.HOURS);
}
}
if (OrderType.TEST_CRUISE_ORDER.equals(orderType)) {
XpTestOrder xpTestOrder = xpTestOrderService.getById(orderId);
//订单为待接单状态
if (xpTestOrder.getStatus() < 4) {
xpTestOrder.setAcceptanceTime(LocalDateTime.now());
R byVehicleVin = xpVehicleClient.findByVehicleVin(dto.getXpVehicleVin());
XpVehicle vehicle = null;
if (byVehicleVin.getCode() == 200) {
vehicle = byVehicleVin.getData();
xpTestOrder.setVehicleId(vehicle.getId());
xpTestOrder.setFleetId(vehicle.getFleetId());
}
xpTestOrder.setStatus(6);
xpTestOrder.setStartTestPointId(null);
xpTestOrderService.updateById(xpTestOrder);
redisService.setCacheObject("TEST_ORDER_STATUS:" + xpTestOrder.getId(), 6, 24L, TimeUnit.HOURS);
}
}
}
/**
* 发送乘客导航路径数据
*
* @param appUserId
* @param keyStr
*/
public void sendRoute(String appUserId, Long orderId, String keyStr, String vin) {
VehiclePosition vehiclePosition = mongoTemplate.findOne(new Query(Criteria.where("vin").is(vin)), VehiclePosition.class);
Object cacheObject = redisService.getCacheObject(keyStr + ":" + appUserId);
if (null != cacheObject) {
RouteVo routeVo = JSON.parseObject(cacheObject.toString(), RouteVo.class);
JSONObject jsonObject = new JSONObject();
jsonObject.put("type", "ORDER_ROUTE");
jsonObject.put("timestamp", System.currentTimeMillis());
JSONObject content = new JSONObject();
content.put("orderId", orderId.toString());
content.put("routeType", "ORDER_NAVIGATION_START_DATA".equals(keyStr) ? 1 : 2);
content.put("direction", null != vehiclePosition ? vehiclePosition.getCourse() : 90);
JSONObject route = new JSONObject();
route.put("distance", routeVo.getDistance());
route.put("duration", routeVo.getDuration());
route.put("routeID", routeVo.getRouteID());
List collect = routeVo.getPolyline().stream().map(item -> {
JSONObject point = new JSONObject();
point.put("latitude", item.getLatitude());
point.put("longitude", item.getLongitude());
return point;
}).collect(Collectors.toList());
route.put("polyline", JSON.toJSONString(collect));
content.put("route", route.toJSONString());
jsonObject.put("content", content.toJSONString());
SendMessageDTO messageDTO = new SendMessageDTO();
messageDTO.setAppUserId(appUserId);
messageDTO.setMessage(jsonObject.toJSONString());
R stringR = xpPushMessageClient.sendRoute(messageDTO);
XpOrder xpOrder = this.getById(orderId);
//推送失败,重试3次
if(stringR.getCode() != Constants.SUCCESS){
ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());
executor.execute(() -> {
for (int i = 0; i < 3; i++) {
R stringR1 = xpPushMessageClient.sendStatus(messageDTO);
if(stringR1.getCode() == Constants.SUCCESS){
break;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
log.info("推送乘客端导航数据消息失败:{} {} {}", stringR1.getMsg(), JSON.toJSONString(messageDTO), xpOrder.getOrderNumber());
}
});
}
}
}
/**
* 获取当前用户正在执行的订单
*
* @param uid
* @return
*/
@Override
public XpOrderDetailVO getActiveOrder(String uid) {
XpOrderDetailVO xpOrderDetailVO = new XpOrderDetailVO();
XpOrder xpOrder = this.baseMapper.selectOne(new LambdaQueryWrapper().eq(XpOrder::getAppUserId, Long.parseLong(uid)).in(XpOrder::getStatus, Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8)).orderByDesc(BaseModel::getCreateTime).last("LIMIT 1"));
if (xpOrder == null) {
return null;
}
BeanUtils.copyProperties(xpOrder, xpOrderDetailVO);
R xpPointR = xpPointClient.queryById(xpOrder.getStartPointId());
if (xpPointR.getCode() == R.SUCCESS) {
XpPoint startPoint = xpPointR.getData();
xpOrderDetailVO.setStartParkingTimeLimit(startPoint.getParkingTimeLimit());
}
R xpPointR1 = xpPointClient.queryById(xpOrder.getEndPointId());
if (xpPointR1.getCode() == R.SUCCESS) {
XpPoint endPoint = xpPointR1.getData();
xpOrderDetailVO.setEndParkingTimeLimit(endPoint.getParkingTimeLimit());
}
// // 算时间
// 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());
// }
//
// }
if (xpOrder.getVehicleId() != null) {
R xpVehicleR = xpVehicleClient.queryById(xpOrder.getVehicleId());
XpVehicle vehicle = xpVehicleR.getData();
xpOrderDetailVO.setVehicleVin(vehicle.getVehicleVin());
xpOrderDetailVO.setVehicleModel(vehicle.getVehicleModel());
xpOrderDetailVO.setVehicleColor(vehicle.getVehicleColor());
xpOrderDetailVO.setPlateNumber(vehicle.getPlateNumber());
xpOrderDetailVO.setVehiclePictures(vehicle.getVehiclePictures());
}
Integer status = xpOrder.getStatus();
if(status > 2 && status < 8){
Boolean data = xpVehicleClient.getVehicleTrunkOpenStatus(xpOrder.getAppUserId(), xpOrder.getVehicleId()).getData();
xpOrder.setOpenTrunk(data ? 1 : 2);
xpOrderDetailVO.setOpenTrunk(data ? 1 : 2);
xpOrder.setAppUserId(null);
this.updateById(xpOrder);
}
return xpOrderDetailVO;
}
@Override
public XpOrderDetailVO getActiveOrder_itineraryManage(String uid){
XpOrderDetailVO xpOrderDetailVO = new XpOrderDetailVO();
XpOrder xpOrder = this.baseMapper.selectOne(new LambdaQueryWrapper().eq(XpOrder::getAppUserId, Long.parseLong(uid)).in(XpOrder::getStatus, Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8)).orderByDesc(BaseModel::getCreateTime).last("LIMIT 1"));
if (xpOrder == null) {
return null;
}
BeanUtils.copyProperties(xpOrder, xpOrderDetailVO);
if (xpOrder.getVehicleId() != null) {
R xpVehicleR = xpVehicleClient.queryById(xpOrder.getVehicleId());
XpVehicle vehicle = xpVehicleR.getData();
xpOrderDetailVO.setVehicleVin(vehicle.getVehicleVin());
xpOrderDetailVO.setVehicleModel(vehicle.getVehicleModel());
xpOrderDetailVO.setVehicleColor(vehicle.getVehicleColor());
xpOrderDetailVO.setPlateNumber(vehicle.getPlateNumber());
xpOrderDetailVO.setVehiclePictures(vehicle.getVehiclePictures());
}
Boolean cancelShow = isCancelShow(xpOrder,xpOrderDetailVO.getVehicleVin()); //是否能取消行程
xpOrderDetailVO.setCancelShow(cancelShow);
return xpOrderDetailVO;
}
/**
* 取消行程:若处于“送驾中”且离目的地小于10分钟路程(后台配置),【取消行程】按钮应置灰;
* 修改行程:
* 1. 当前剩余里程 < 800米 (后台配置)或 剩余时间 < 5分钟(后台配置)(以先到者为准),禁止修改,按钮置灰。
* 2. 单次行程最多允许修改 2次 (后台配置)目的地,防恶意高频调用
* 特殊豁免:若系统检测到车辆在当前坐标时速 < 3km/h 且持续时间 > 3分钟(后台配置)(判定为极度拥堵或异常停滞),此时解除限制,允许用户修改目的地或新增途径点以寻找更近的下车位置。
* @return
*/
private boolean isCancelShow(XpOrder xpOrder,String vehicleVin) {
boolean isCancel = false;
//获取配置文件
int sylc = 800;//单位米 todo yyy 需要读取后台配置文件
int sysj = 5;//单位分钟 todo yyy 需要读取后台配置文件
// 减少3分钟 获取3分账内的 行程数据
long minute =3;//todo yyy 需要读取后台配置文件
List orderTrajectories = queryOrderTrajectories(xpOrder.getId());
if(!orderTrajectories.isEmpty()){// 减少3分钟 获取3分账内的 行程数据
//3千米/小时 转换成分钟 50米/分钟
BigDecimal suduDefault = new BigDecimal("3");//3千米/小时
BigDecimal suduDefault1 = suduDefault.multiply(BigDecimal.valueOf(1000)) ;
BigDecimal suduDefaultNew =suduDefault1.divide(BigDecimal.valueOf(60),2,RoundingMode.HALF_EVEN);
LocalDateTime minutesBefore = LocalDateTime.now().minusMinutes(minute);
long milliseconds = DateUtils.getMilliseconds(minutesBefore);
List orderTrajectoriesNew = new ArrayList<>();
for (OrderTrajectory orderTrajectory : orderTrajectories) {
if(orderTrajectory.getLocatetime() > milliseconds){
orderTrajectoriesNew.add(orderTrajectory);
}
}
/*
获取最近3分钟的 数据 速度、平均
*/
//计算行驶总距离和时长
Double reduce = orderTrajectoriesNew.stream().map(OrderTrajectory::getDistance).reduce(0.0, Double::sum);
BigDecimal xcsudu = new BigDecimal(reduce).divide(new BigDecimal(minute),2, RoundingMode.HALF_EVEN);//米/分钟
//特殊豁免:若系统检测到车辆在当前坐标时速 < 3km/h 且持续时间 > 3分钟(后台配置)(判定为极度拥堵或异常停滞),此时解除限制,允许用户修改目的地或新增途径点以寻找更近的下车位置。
if(xcsudu.compareTo(suduDefaultNew)<0){
return true;
}
}
RouteVo routeVo = this.getRouteVo(xpOrder, vehicleVin);
if(routeVo.getDistance() distanceAndDuration = xpMapClient.getDistanceAndDuration(routeAndFareDto);
if(distanceAndDuration.getCode()!=200){
throw new ServiceException("未找到路径", I18nKeyConstant.PATH_NOT_FOUND);
}
return distanceAndDuration.getData();
}
@Override
public void changeAmount(Long id, BigDecimal amount) {
if (Objects.isNull(id)) {
throw new ServiceException("订单ID不能为空", I18nKeyConstant.ORDER_CHANGE_AMOUNT_ID_REQUIRED);
}
if (Objects.isNull(amount)) {
throw new ServiceException("订单金额不能为空", I18nKeyConstant.ORDER_CHANGE_AMOUNT_AMOUNT_REQUIRED);
}
if (amount.compareTo(BigDecimal.ZERO) < 0) {
throw new ServiceException("订单金额不能小于0", I18nKeyConstant.ORDER_CHANGE_AMOUNT_AMOUNT_INVALID);
}
XpOrder order = this.getById(id);
Long appUserId = order.getAppUserId();
if (Objects.isNull(order)) {
throw new ServiceException("订单不存在", I18nKeyConstant.ORDER_NOT_EXISTS);
}
final Integer pendingPaymentStatus = 8;
if (!Objects.equals(order.getStatus(), pendingPaymentStatus)) {
throw new ServiceException("当前订单状态不允许改价,只有待付款状态的订单可以改价", I18nKeyConstant.ORDER_CHANGE_AMOUNT_STATUS_INVALID);
}
if (Objects.nonNull(order.getOrderMoney()) && order.getOrderMoney().compareTo(amount) == 0) {
return;
}
boolean updated = this.lambdaUpdate()
.set(XpOrder::getOrderMoney, amount)
.set(XpOrder::getChangeMoneyFlag, Boolean.TRUE)
.eq(XpOrder::getId, id)
.eq(XpOrder::getStatus, pendingPaymentStatus)
.update();
if (!updated) {
throw new ServiceException("订单改价失败,请稍后重试", I18nKeyConstant.ORDER_CHANGE_AMOUNT_FAILED);
}
// 消息推送
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_UPDATE_PRICE.getDesc());
params.put("content", 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(appUserId));
R stringR = xpPushMessageClient.sendStatus(sendMessageDTO);
//推送失败,重试3次
if(stringR.getCode() != Constants.SUCCESS){
ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());
executor.execute(() -> {
for (int i = 0; i < 3; i++) {
R stringR1 = xpPushMessageClient.sendStatus(sendMessageDTO);
if(stringR1.getCode() == Constants.SUCCESS){
break;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
log.info("运维人员修改订单价格推送订单状态消息失败:{} {} {}", stringR1.getMsg(), JSON.toJSONString(sendMessageDTO), order.getOrderNumber());
}
});
}
log.info("订单修改价格推送消息完毕");
}
/**
* 获取待支付订单数量
* @param uid
* @return
*/
@Override
public Integer getWaitPayOrderNum(String uid) {
Long num = this.baseMapper.selectCount(
new LambdaQueryWrapper()
.eq(XpOrder::getAppUserId, uid)
.eq(XpOrder::getStatus, 8)
);
return num.intValue();
}
}