package com.xiaopeng.order.service.impl;
|
|
import cn.hutool.core.bean.BeanUtil;
|
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.lang.Snowflake;
|
import com.alibaba.fastjson2.JSON;
|
import com.alibaba.fastjson2.JSONArray;
|
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.automaticPilot.PushOrderDto;
|
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.feignClient.account.UserResourcesClient;
|
import com.xiaopeng.api.feignClient.account.XpAppUserCouponClient;
|
import com.xiaopeng.api.feignClient.automaticPilot.OrderMissionClient;
|
import com.xiaopeng.api.feignClient.maintenance.XpTaskRuleClient;
|
import com.xiaopeng.api.feignClient.map.XpMapClient;
|
import com.xiaopeng.api.feignClient.operation.*;
|
import com.xiaopeng.api.feignClient.pushMessage.XpPushMessageClient;
|
import com.xiaopeng.api.feignClient.vehicleConvoy.UserVehicleClient;
|
import com.xiaopeng.api.feignClient.vehicleConvoy.XpFleetClient;
|
import com.xiaopeng.api.feignClient.vehicleConvoy.XpFleetVehicleClient;
|
import com.xiaopeng.api.feignClient.vehicleConvoy.XpVehicleClient;
|
import com.xiaopeng.api.model.map.XpTestPoint;
|
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.automaticPilot.VehiclePosition;
|
import com.xiaopeng.api.vo.automaticPilot.protobuf.OrderOperationType;
|
import com.xiaopeng.api.vo.operation.RouteVo;
|
import com.xiaopeng.api.vo.order.FareBreakdownDetailVo;
|
import com.xiaopeng.api.vo.order.XpPricingCardVo;
|
import com.xiaopeng.common.core.constant.CacheConstants;
|
import com.xiaopeng.common.core.constant.HttpStatus;
|
import com.xiaopeng.common.core.domain.R;
|
import com.xiaopeng.common.core.enums.OrderStatusEnum;
|
import com.xiaopeng.common.core.exception.ServiceException;
|
import com.xiaopeng.common.core.utils.DateUtils;
|
import com.xiaopeng.common.core.utils.GeodesyUtil;
|
import com.xiaopeng.common.core.utils.StringUtils;
|
import com.xiaopeng.common.core.web.domain.BaseModel;
|
import com.xiaopeng.common.core.web.page.PageInfo;
|
import com.xiaopeng.common.redis.service.RedisService;
|
import com.xiaopeng.common.security.utils.SecurityUtils;
|
import com.xiaopeng.order.constant.I18nKeyConstant;
|
import com.xiaopeng.order.job.base.QuartzManager;
|
import com.xiaopeng.order.job.base.TimeJobType;
|
import com.xiaopeng.order.job.jobs.EndTripJob;
|
import com.xiaopeng.order.job.jobs.TestOrderCancelJob;
|
import com.xiaopeng.api.model.order.OrderTrajectory;
|
import com.xiaopeng.order.mapper.XpCruiseOrderMapper;
|
import com.xiaopeng.order.mapper.XpTestOrderDetailMapper;
|
import com.xiaopeng.order.mapper.XpTestOrderMapper;
|
import com.xiaopeng.order.model.query.XpTestOrderPageQuery;
|
import com.xiaopeng.order.model.vo.AddTestOrderVo;
|
import com.xiaopeng.order.model.vo.XpTestOrderDetailVO;
|
import com.xiaopeng.order.model.vo.XpTestOrderPageVO;
|
import com.xiaopeng.order.service.*;
|
import com.xiaopeng.order.utils.GDMapGeocodingUtil;
|
import com.xiaopeng.order.utils.TaxiPricingDemoService;
|
import com.xiaopeng.order.utils.XpPricingUtil;
|
import com.xiaopeng.order.utils.holiday.HolidayUtil;
|
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.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.time.LocalDateTime;
|
import java.time.LocalTime;
|
import java.time.ZoneOffset;
|
import java.util.*;
|
import java.util.concurrent.TimeUnit;
|
import java.util.function.Function;
|
import java.util.stream.Collectors;
|
|
/**
|
* <p>
|
* 订单 服务实现类
|
* </p>
|
*
|
* @author mitao
|
* @since 2026-02-03
|
*/
|
@Slf4j
|
@Service
|
@RequiredArgsConstructor(onConstructor_ = @Lazy)
|
public class XpTestOrderServiceImpl extends ServiceImpl<XpTestOrderMapper, XpTestOrder> implements IXpTestOrderService {
|
|
private final UserResourcesClient userResourcesClient;
|
private final XpTestPointClient xpTestPointClient;
|
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 IXpTestOrderCancelService xpTestOrderCancelService;
|
private final IXpOrderReassignService xpOrderReassignService;
|
private final RedisService redisService;
|
private final XpPricingClient xpPricingClient;
|
private final XpSystemSettingClient xpSystemSettingClient;
|
private final XpPushMessageClient xpPushMessageClient;
|
private final OrderMissionClient orderMissionClient;
|
private final XpTestOrderDetailMapper xpTestOrderDetailMapper;
|
private final XpCruiseOrderMapper xpCruiseOrderMapper;
|
private final XpTaskRuleClient xpTaskRuleClient;
|
private final XpCruisePlanClient xpCruisePlanClient;
|
private final XpPricingUtil xpPricingUtil;
|
private final GDMapGeocodingUtil gdMapGeocodingUtil;
|
private final XpOpenCityClient openCityClient;
|
private final XpMapClient xpMapClient;
|
private final XpRoutePlanClient xpRoutePlanClient;
|
private final XpTestPlanClient xpTestPlanClient;
|
private final HolidayUtil holidayUtil;
|
private final MongoTemplate mongoTemplate;
|
private final XpElectronicFenceClient xpElectronicFenceClient;
|
private final XpElectronicClient xpElectronicClient;
|
private final XpTestPointClient testPointClient;
|
|
|
@Resource
|
private BootCommonService bootCommonService;
|
|
@Resource
|
private XpTradingStrategyClient tradingStrategyClient;
|
|
@Resource(name = "hutoolSnowflake")
|
private Snowflake snowflake;
|
|
|
|
|
/**
|
* 添加测试单
|
* @param query
|
*/
|
@Override
|
public void addTestOrder(AddTestOrderVo query) {
|
Long startTestPointId = query.getStartTestPointId();
|
Long endTestPointId = query.getEndTestPointId();
|
XpTestPoint xpPoint = testPointClient.queryById(startTestPointId).getData();
|
XpTestPoint endXpPoint = testPointClient.queryById(endTestPointId).getData();
|
if(xpPoint ==null || xpPoint.getOperationStatus()!=2){
|
throw new ServiceException("起站点已停止运营", 200,I18nKeyConstant.NO_CURRENT_POINT);
|
}
|
if(endXpPoint ==null || endXpPoint.getOperationStatus()!=2){
|
throw new ServiceException("终站点已停止运营", 200,I18nKeyConstant.NO_CURRENT_POINT);
|
}
|
// 是否在运营区域时间内
|
XpElectronicFenceArea xpElectronicFenceArea = xpElectronicClient.queryElectronicFenceAreaById(xpPoint.getElectronicAreaId()).getData();
|
XpElectronicFence xpElectronicFence = xpElectronicFenceClient.selectById(xpElectronicFenceArea.getElectronicId().toString()).getData();
|
String operationStrategy = xpElectronicFence.getOperationStrategy();
|
Boolean inOperationTime = isInOperationTime(operationStrategy);
|
if (!inOperationTime && xpElectronicFence.getFailureType()!=1) {
|
throw new ServiceException("当前区域未开通服务", I18nKeyConstant.NO_CURRENT_REGION);
|
}
|
|
// 判断点位是否在运营区域内
|
boolean inTime = xpTestPointClient.nowInTimePoint(startTestPointId).getData();
|
if (!inTime) {
|
throw new ServiceException("当前点位未开通服务", I18nKeyConstant.NO_CURRENT_REGION);
|
}
|
inTime = xpTestPointClient.nowInTimePoint(endTestPointId).getData();
|
if (!inTime) {
|
throw new ServiceException("当前点位未开通服务", I18nKeyConstant.NO_CURRENT_REGION);
|
}
|
|
Map<String, String> geocode = gdMapGeocodingUtil.geocode(xpPoint.getPointLon().toString(), xpPoint.getPointLat().toString());
|
String districtCode = geocode.get("districtCode");
|
String cityCode = geocode.get("cityCode");
|
String provinceCode = geocode.get("provinceCode");
|
XpOpenCity openCity = openCityClient.getXpOpenCity(provinceCode, cityCode, districtCode).getData();
|
if (openCity == null) {
|
throw new ServiceException("当前区域未开通服务", I18nKeyConstant.NO_CURRENT_REGION);
|
}
|
XpPricing xpPricing = bootCommonService.getXpPricing(openCity.getId(),null);
|
if (xpPricing == null) {
|
throw new ServiceException("当前区域未开通服务", I18nKeyConstant.NO_CURRENT_REGION);
|
}
|
|
// 算距离
|
RouteAndFareDto routeAndFareDto = new RouteAndFareDto();
|
LonLatDto lonLatDto = new LonLatDto();
|
lonLatDto.setLongitude(xpPoint.getPointLon());
|
lonLatDto.setLatitude(xpPoint.getPointLat());
|
routeAndFareDto.setOrigin(lonLatDto);
|
LonLatDto lonLatDto1 = new LonLatDto();
|
lonLatDto1.setLongitude(endXpPoint.getPointLon());
|
lonLatDto1.setLatitude(endXpPoint.getPointLat());
|
routeAndFareDto.setDestination(lonLatDto1);
|
routeAndFareDto.setOpenCityId(openCity.getId());
|
XpRoutePlan xpRoutePlan = xpRoutePlanClient.getXpRoutePlanOpenCityId(openCity.getId()).getData();
|
if(xpRoutePlan!=null&& !"null".equals(xpRoutePlan.getRoutePlanRule())){
|
routeAndFareDto.setRoutePlanRule(Integer.valueOf(xpRoutePlan.getRoutePlanRule()));
|
}
|
// 调用地图 获取时间
|
R<RouteVo> distanceAndDuration = xpMapClient.getDistanceAndDuration(routeAndFareDto);
|
if (distanceAndDuration.getCode() != 200) {
|
throw new ServiceException("获取导肮信息失败", I18nKeyConstant.PATH_NOT_FOUND);
|
}
|
RouteVo routeVo = distanceAndDuration.getData();
|
|
|
// 生成订单数据
|
XpTestOrder xpOrder = new XpTestOrder();
|
xpOrder.setTestPlanId(query.getTestPlanId());
|
xpOrder.setOrderType(query.getOrderType());
|
xpOrder.setMileageKilometers(0.0);
|
xpOrder.setOrderNumber("XP" + System.currentTimeMillis());
|
xpOrder.setCityId(openCity.getId());
|
xpOrder.setPlacementLat(xpPoint.getPointLat());
|
xpOrder.setPlacementLon(xpPoint.getPointLon());
|
xpOrder.setPlacementAddress(xpPoint.getPointAddress());
|
xpOrder.setStartLon(xpPoint.getPointLon());
|
xpOrder.setStartLat(xpPoint.getPointLat());
|
xpOrder.setBoardingLon(xpPoint.getPointLon());
|
xpOrder.setBoardingLat(xpPoint.getPointLat());
|
xpOrder.setBoardingAddress(xpPoint.getPointAddress());
|
|
xpOrder.setEndLon(endXpPoint.getPointLon());
|
xpOrder.setEndLat(endXpPoint.getPointLat());
|
|
xpOrder.setStartAddress(xpPoint.getPointAddress());
|
xpOrder.setEndAddress(endXpPoint.getPointAddress());
|
|
xpOrder.setXpElectronicFenceAreaId(xpPoint.getElectronicAreaId());
|
xpOrder.setStartTestPointId(startTestPointId);
|
xpOrder.setEndTestPointId(endTestPointId);
|
xpOrder.setStartTestPointName(xpPoint.getPointName());
|
xpOrder.setEndTestPointName(endXpPoint.getPointName());
|
xpOrder.setStatus(1);
|
xpOrder.setOrderSource(1);
|
xpOrder.setOpenTrunk(2);
|
xpOrder.setXpuStartTaskId(snowflake.nextId() + "");
|
xpOrder.setXpuEndTaskId(snowflake.nextId() + "");
|
|
// 当前时段
|
String currentTimeSlot = DateUtils.getCurrentTimeSlot();
|
xpOrder.setTimeSlot(currentTimeSlot);
|
xpOrder.setCreateTime(LocalDateTime.now());
|
|
xpOrder.setCityCode(openCity.getCityCode());
|
List<Long> vehicleIds = query.getVehicleIds();
|
|
for (Long vehicleId : vehicleIds) {
|
xpOrder.setId(null);
|
XpVehicle xpVehicle = xpVehicleClient.queryById(vehicleId).getData();
|
xpOrder.setVehicleId(vehicleId);
|
xpOrder.setFleetId(xpVehicle.getFleetId());
|
this.save(xpOrder);
|
redisService.setCacheObject("TEST_ORDER_STATUS:" + xpOrder.getId(), 1, 24L, TimeUnit.HOURS);
|
//缓存导航数据,用于下单后推送导航给车端和乘客端
|
redisService.setCacheObject("TEST_ORDER_NAVIGATION_END_DATA:" + vehicleId, JSON.toJSONString(routeVo), 24L, TimeUnit.HOURS);
|
//下发XPU消息
|
PushOrderDto pushOrderDto = new PushOrderDto();
|
pushOrderDto.setStartTaskId(xpOrder.getXpuStartTaskId());
|
pushOrderDto.setEndTaskId(xpOrder.getXpuEndTaskId());
|
pushOrderDto.setVin(xpVehicle.getVehicleVin());
|
pushOrderDto.setOrderId("TEST" + xpOrder.getId());
|
pushOrderDto.setStartWaypointId(xpOrder.getStartTestPointId());
|
pushOrderDto.setEndWaypointId(xpOrder.getEndTestPointId());
|
pushOrderDto.setStartLatitude(xpOrder.getStartLat());
|
pushOrderDto.setStartLongitude(xpOrder.getStartLon());
|
pushOrderDto.setEndLatitude(xpOrder.getEndLat());
|
pushOrderDto.setEndLongitude(xpOrder.getEndLon());
|
pushOrderDto.setEndPoiName(xpOrder.getEndTestPointName());
|
pushOrderDto.setOrderType(xpOrder.getOrderType() == 1 ? OrderType.TEST_ORDER : OrderType.TEST_CRUISE_ORDER);
|
pushOrderDto.setStage(1);
|
// 调用推送服务 orderId + carVin
|
orderMissionClient.pushOrder(pushOrderDto);
|
}
|
}
|
|
private Boolean isInOperationTime(String operationStrategy) {
|
JSONArray jsonArray = JSONArray.parseArray(operationStrategy);
|
Integer holidayToday = holidayUtil.isHolidayToday();
|
for (Object o : jsonArray) {
|
JSONObject jsonObject = JSONObject.parseObject(o.toString());
|
String type = jsonObject.getString("type");
|
if (type.equals(holidayToday.toString())) {
|
JSONArray time = jsonObject.getJSONArray("time");
|
// 判断当前时间 是否在time时间段中
|
for (Object object : time) {
|
String[] timeArray = object.toString().split("-");
|
String startTime = timeArray[0];
|
String endTime = timeArray[1];
|
boolean currentTimeInRange = DateUtils.isCurrentTimeInRange(startTime, endTime);
|
if (currentTimeInRange) {
|
return true;
|
}
|
}
|
} else if (type.equals("3")) {
|
// 判断是什么类型 0每天 1-7 想起
|
String typeStr = jsonObject.getString("typeStr");
|
// 查看今天周几
|
Integer week = DateUtils.getWeek();
|
// 包含每天 或者 包含当天
|
if (typeStr.contains("0") || typeStr.contains(week.toString())) {
|
JSONArray time = jsonObject.getJSONArray("time");
|
// 判断当前时间 是否在time时间段中
|
for (Object object : time) {
|
String[] timeArray = object.toString().split("-");
|
String startTime = timeArray[0];
|
String endTime = timeArray[1];
|
boolean currentTimeInRange = DateUtils.isCurrentTimeInRange(startTime, endTime);
|
if (currentTimeInRange) {
|
return true;
|
}
|
}
|
}
|
|
}
|
}
|
return false;
|
}
|
|
|
|
@Override
|
public PageInfo<XpTestOrderPageVO> pageList(XpTestOrderPageQuery query) {
|
// 0. 车牌号筛选:先查车辆服务获取车辆ID列表
|
List<Long> vehicleIdsFilter = null;
|
if (StringUtils.isNotBlank(query.getPlateNumber())) {
|
R<List<XpVehicle>> 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);
|
}
|
}
|
// 1. 构建查询条件
|
LambdaQueryWrapper<XpTestOrder> wrapper = buildQueryWrapper(query);
|
wrapper.eq(XpTestOrder::getTestPlanId, query.getTestPlanId());
|
if (CollUtil.isNotEmpty(vehicleIdsFilter)) {
|
wrapper.in(XpTestOrder::getVehicleId, vehicleIdsFilter);
|
}
|
wrapper.orderByDesc(XpTestOrder::getCreateTime);
|
|
// 2. 分页查询
|
Page<XpTestOrder> page = new Page<>(query.getPageNum(), query.getPageSize());
|
Page<XpTestOrder> pageResult = this.page(page, wrapper);
|
|
if (CollectionUtils.isEmpty(pageResult.getRecords())) {
|
return buildEmptyOrderPage(query);
|
}
|
|
// 3. 收集关联ID
|
Set<Long> vehicleIds = new HashSet<>();
|
Set<Long> fleetIds = new HashSet<>();
|
for (XpTestOrder order : pageResult.getRecords()) {
|
if (Objects.nonNull(order.getVehicleId())) {
|
vehicleIds.add(order.getVehicleId());
|
}
|
if (Objects.nonNull(order.getFleetId())) {
|
fleetIds.add(order.getFleetId());
|
}
|
}
|
|
// 4. 批量查询关联数据
|
Map<Long, XpVehicle> vehicleMap = batchGetVehicles(vehicleIds);
|
Map<Long, XpFleet> fleetMap = batchGetFleets(fleetIds);
|
|
// 5. 组装 VO
|
List<XpTestOrderPageVO> voList = pageResult.getRecords().stream()
|
.map(order -> convertToPageVO(order, vehicleMap, fleetMap))
|
.collect(Collectors.toList());
|
|
// 6. 返回分页结果
|
PageInfo<XpTestOrderPageVO> resultPage = new PageInfo<>(query.getPageNum(), query.getPageSize());
|
resultPage.setTotal(pageResult.getTotal());
|
resultPage.setRecords(voList);
|
return resultPage;
|
}
|
|
private PageInfo<XpTestOrderPageVO> buildEmptyOrderPage(XpTestOrderPageQuery query) {
|
PageInfo<XpTestOrderPageVO> resultPage = new PageInfo<>(query.getPageNum(), query.getPageSize());
|
resultPage.setTotal(0L);
|
resultPage.setRecords(new ArrayList<>());
|
return resultPage;
|
}
|
|
@Override
|
public XpTestOrderDetailVO getOrderDetail(Long id) {
|
// 1. 查询订单基础信息
|
XpTestOrder order = this.getById(id);
|
if (order == null) {
|
throw new ServiceException("订单不存在");
|
}
|
|
// 2. 转换为详情 VO
|
XpTestOrderDetailVO vo = BeanUtil.toBean(order, XpTestOrderDetailVO.class);
|
|
// 4. 获取车辆信息
|
if (Objects.nonNull(order.getVehicleId())) {
|
Map<Long, XpVehicle> vehicleMap = batchGetVehicles(Collections.singleton(order.getVehicleId()));
|
XpVehicle vehicle = vehicleMap.get(order.getVehicleId());
|
if (Objects.nonNull(vehicle)) {
|
vo.setPlateNumber(vehicle.getPlateNumber());
|
vo.setVehicleModel(vehicle.getVehicleModelName());
|
}
|
}
|
|
// 5. 获取车队信息
|
if (Objects.nonNull(order.getFleetId())) {
|
Map<Long, XpFleet> fleetMap = batchGetFleets(Collections.singleton(order.getFleetId()));
|
XpFleet fleet = fleetMap.get(order.getFleetId());
|
if (Objects.nonNull(fleet)) {
|
vo.setFleetName(fleet.getFleetName());
|
}
|
}
|
|
// 6. 获取评价信息
|
LambdaQueryWrapper<XpOrderEvaluate> 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<OrderTrajectory> trajectories = queryOrderTrajectories(id);
|
vo.setOrderPositionList(convertTrajectoriesToPositions(trajectories));
|
|
// 8. 起步价、里程费、时长费、远途费:按订单费用明细聚合
|
Map<Integer, BigDecimal> 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. 校验订单是否存在
|
XpTestOrder order = this.getById(id);
|
if (Objects.isNull(order)) {
|
throw new ServiceException("订单不存在", I18nKeyConstant.ORDER_NOT_EXISTS);
|
}
|
|
this.removeById(id);
|
}
|
|
|
/**
|
* 构建查询条件
|
*/
|
private LambdaQueryWrapper<XpTestOrder> buildQueryWrapper(XpTestOrderPageQuery query) {
|
LambdaQueryWrapper<XpTestOrder> wrapper = new LambdaQueryWrapper<>();
|
|
wrapper.like(StringUtils.isNotBlank(query.getOrderNumber()), XpTestOrder::getOrderNumber, query.getOrderNumber())
|
.eq(Objects.nonNull(query.getStatus()), XpTestOrder::getStatus, query.getStatus());
|
if (Objects.nonNull(query.getStartTime())) {
|
wrapper.ge(XpTestOrder::getCreateTime, LocalDateTime.of(query.getStartTime(), LocalTime.MIN));
|
}
|
if (Objects.nonNull(query.getEndTime())) {
|
wrapper.le(XpTestOrder::getCreateTime, LocalDateTime.of(query.getEndTime(), LocalTime.MAX));
|
}
|
wrapper.orderByDesc(XpTestOrder::getCreateTime);
|
return wrapper;
|
}
|
|
/**
|
* 批量获取车辆信息
|
*/
|
private Map<Long, XpVehicle> batchGetVehicles(Set<Long> vehicleIds) {
|
if (CollectionUtils.isEmpty(vehicleIds)) {
|
return new HashMap<>();
|
}
|
try {
|
R<List<XpVehicle>> 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<Long, XpFleet> batchGetFleets(Set<Long> fleetIds) {
|
if (CollectionUtils.isEmpty(fleetIds)) {
|
return new HashMap<>();
|
}
|
try {
|
R<List<XpFleet>> 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. 校验订单是否存在
|
XpTestOrder 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 XpTestOrderPageVO convertToPageVO(XpTestOrder order,
|
Map<Long, XpVehicle> vehicleMap,
|
Map<Long, XpFleet> fleetMap) {
|
XpTestOrderPageVO vo = new XpTestOrderPageVO();
|
vo.setId(order.getId());
|
vo.setOrderType(order.getOrderType());
|
vo.setOrderNumber(order.getOrderNumber());
|
vo.setCreateTime(order.getCreateTime());
|
vo.setOrderSource(order.getOrderSource());
|
vo.setStartAddress(order.getStartAddress());
|
vo.setEndAddress(order.getEndAddress());
|
vo.setStatus(order.getStatus());
|
|
// 车辆信息
|
if (Objects.nonNull(order.getVehicleId()) && vehicleMap.containsKey(order.getVehicleId())) {
|
XpVehicle vehicle = vehicleMap.get(order.getVehicleId());
|
vo.setPlateNumber(vehicle.getPlateNumber());
|
}
|
|
// 车队信息
|
if (Objects.nonNull(order.getFleetId()) && fleetMap.containsKey(order.getFleetId())) {
|
XpFleet fleet = fleetMap.get(order.getFleetId());
|
vo.setFleetName(fleet.getFleetName());
|
}
|
|
return vo;
|
}
|
|
/**
|
* 取消订单
|
*/
|
private void cancelOrder(XpTestOrder order) {
|
//判断还原车辆状态
|
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());
|
}
|
}
|
}
|
// 1. 更新订单状态为已取消(0)
|
lambdaUpdate().set(XpTestOrder::getStatus, 0)
|
.eq(XpTestOrder::getId, order.getId()).update();
|
|
// 2. 保存取消记录
|
XpTestOrderCancel orderCancel = new XpTestOrderCancel();
|
orderCancel.setOrderId(order.getId());
|
orderCancel.setCancelSource(3); // 3=运营人员(后台管理接口固定为运营人员)
|
orderCancel.setUserId(SecurityUtils.getUserId()); // 当前登录用户ID
|
orderCancel.setCancelReason("运营人员取消订单");
|
xpTestOrderCancelService.save(orderCancel);
|
|
//查询车辆信息
|
R<XpVehicle> xpVehicleR = xpVehicleClient.queryById(order.getVehicleId());
|
if (xpVehicleR.getCode() == HttpStatus.SUCCESS && Objects.nonNull(xpVehicleR.getData())) {
|
XpVehicle xpVehicle = xpVehicleR.getData();
|
//还原车辆状态
|
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("TEST" + order.getId().toString());
|
pushOrderDto.setVin(xpVehicle.getVehicleVin());
|
pushOrderDto.setOperation_type(OrderOperationType.CLOUD_CANCEL_ORDER);
|
orderMissionClient.cancelOrder(pushOrderDto);
|
log.info("运营人员取消订单成功,开始执行巡游订单 {}", order.getId());
|
} else {
|
log.info("查询车辆信息失败,订单ID: {}, 下发取消订单命令失败", order.getId());
|
}
|
log.info("订单取消成功,订单ID: {}, 操作人: {}", order.getId(), SecurityUtils.getUsername());
|
}
|
|
/**
|
* 关闭订单
|
*/
|
private void closeOrder(XpTestOrder 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<String, String> geocode = gdMapGeocodingUtil.geocode(String.valueOf(x), String.valueOf(y));
|
order.setGetoffAddress(geocode.get("address"));
|
}
|
}
|
//计算行驶总距离和时长
|
List<OrderTrajectory> 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());
|
order.setStartTestPointId(null);
|
this.updateById(order);
|
redisService.setCacheObject("TEST_ORDER_STATUS:" + order.getId(), 7, 24L, TimeUnit.HOURS);
|
|
//给XPU推送订单取消的报文
|
PushOrderDto pushOrderDto = new PushOrderDto();
|
pushOrderDto.setOrderId("TEST" + order.getId().toString());
|
pushOrderDto.setVin(xpVehicle.getVehicleVin());
|
pushOrderDto.setOperation_type(OrderOperationType.CLOUD_CANCEL_ORDER);
|
orderMissionClient.cancelOrder(pushOrderDto);
|
|
//结束行程,修改状态和计算费用
|
endTrip(order.getId());
|
}
|
|
|
|
public void carAbnormalUpdateOrderStatus(Long orderId) {
|
XpTestOrder order = this.getById(orderId);
|
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<String, String> geocode = gdMapGeocodingUtil.geocode(String.valueOf(x), String.valueOf(y));
|
order.setGetoffAddress(geocode.get("address"));
|
}
|
}
|
//计算行驶总距离和时长
|
List<OrderTrajectory> 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());
|
order.setStartTestPointId(null);
|
this.updateById(order);
|
redisService.setCacheObject("TEST_ORDER_STATUS:" + order.getId(), 7, 24L, TimeUnit.HOURS);
|
|
//给XPU推送订单取消的报文
|
PushOrderDto pushOrderDto = new PushOrderDto();
|
pushOrderDto.setOrderId("TEST" + order.getId().toString());
|
pushOrderDto.setVin(xpVehicle.getVehicleVin());
|
pushOrderDto.setOperation_type(OrderOperationType.CLOUD_CANCEL_ORDER);
|
orderMissionClient.cancelOrder(pushOrderDto);
|
|
//结束行程,修改状态和计算费用
|
endTrip(order.getId());
|
|
//将支付金额修改成0 并将状态修改成 订单状态9
|
setFree(order.getId());
|
}
|
|
|
|
/**
|
* 豁免
|
*/
|
public void setFree(Long id) {
|
|
//将支付金额修改成0 并将状态修改成 订单状态9
|
XpTestOrder xpOrder1 = new XpTestOrder();
|
xpOrder1.setId(id);
|
xpOrder1.setIsFree(true);//豁免
|
xpOrder1.setPayMoney(BigDecimal.ZERO);
|
xpOrder1.setStatus(OrderStatusEnum.PENDING_PAYMENT.getCode());
|
xpOrder1.setUpdateTime(LocalDateTime.now());
|
baseMapper.updateById(xpOrder1);
|
}
|
|
|
/**
|
* 改派订单
|
*/
|
@Override
|
@Transactional
|
@ShardingSphereTransactionType(TransactionType.BASE)
|
public void reassignOrder(Long orderId, Long newVehicleId, String reassignReason) {
|
// 1. 校验订单是否存在
|
XpTestOrder 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<List<XpVehicle>> vehicleResult = xpVehicleClient.listByIds(Collections.singletonList(newVehicleId));
|
if (Objects.isNull(vehicleResult) || Objects.isNull(vehicleResult.getData()) || vehicleResult.getData().isEmpty()) {
|
throw new ServiceException("新车辆不存在", I18nKeyConstant.ORDER_REASSIGN_NEW_VEHICLE_NOT_EXISTS);
|
}
|
XpVehicle newVehicle = vehicleResult.getData().get(0);
|
|
// 5. 校验新车辆的运营状态
|
// 车辆状态:1=待完善 2=待运营 3=保险/年检过期
|
// 运营状态:1=运营中 2=维修中 3=已收车 4=车辆异常
|
// 当前模式:1=接单 2=测试
|
// 模式状态:1=开始接单/开始测试 2=停止接单/停止测试
|
// if (Objects.isNull(newVehicle.getStatus()) || newVehicle.getStatus() == 3) {
|
// throw new ServiceException("新车辆保险或年检已过期,无法接单", I18nKeyConstant.ORDER_REASSIGN_NEW_VEHICLE_STATUS_INVALID);
|
// }
|
if (Objects.isNull(newVehicle.getOperatingStatus()) || newVehicle.getOperatingStatus() != 1) {
|
throw new ServiceException("新车辆运营状态异常,无法接单", I18nKeyConstant.ORDER_REASSIGN_NEW_VEHICLE_OPERATING_STATUS_INVALID);
|
}
|
if (Objects.isNull(newVehicle.getAcceptOrTest()) || newVehicle.getAcceptOrTest() != 1) {
|
throw new ServiceException("新车辆当前不在接单模式", I18nKeyConstant.ORDER_REASSIGN_NEW_VEHICLE_NOT_IN_ACCEPT_MODE);
|
}
|
if (Objects.isNull(newVehicle.getAcceptOrTestStatus()) || newVehicle.getAcceptOrTestStatus() != 1) {
|
throw new ServiceException("新车辆未开始接单", I18nKeyConstant.ORDER_REASSIGN_NEW_VEHICLE_NOT_START_ACCEPT);
|
}
|
if (Objects.isNull(newVehicle.getState()) || newVehicle.getState() != 0) {
|
throw new ServiceException("新车辆已接单,请重新选择", I18nKeyConstant.ORDER_REASSIGN_NEW_VEHICLE_NOT_FREE);
|
}
|
|
// 6. 保存原车辆和车队信息
|
Long oldVehicleId = order.getVehicleId();
|
Long oldFleetId = order.getFleetId();
|
|
// 6.1 处理原车辆:取消订单指令、解绑乘客、更新状态、创建返程巡游订单
|
if (Objects.nonNull(oldVehicleId)) {
|
try {
|
R<XpVehicle> oldVehicleResult = xpVehicleClient.queryById(oldVehicleId);
|
if (oldVehicleResult != null && oldVehicleResult.getCode() == HttpStatus.SUCCESS && oldVehicleResult.getData() != null) {
|
XpVehicle oldVehicle = oldVehicleResult.getData();
|
|
// 6.1.1 取消原订单指令
|
PushOrderDto cancelDto = new PushOrderDto();
|
cancelDto.setOrderId("TEST" + order.getId().toString());
|
cancelDto.setVin(oldVehicle.getVehicleVin());
|
cancelDto.setOperation_type(OrderOperationType.CLOUD_CANCEL_ORDER);
|
orderMissionClient.cancelOrder(cancelDto);
|
log.info("已取消原车辆订单指令,订单ID: {}, VIN: {}", orderId, oldVehicle.getVehicleVin());
|
|
// 6.1.3 修改原车辆状态为空闲
|
XpVehicle vehicle1 = new XpVehicle();
|
vehicle1.setId(oldVehicle.getId());
|
vehicle1.setState(0);
|
xpVehicleClient.updateVehicleById(vehicle1);
|
log.info("改派订单,车辆状态修改0 {}", oldVehicle.getPlateNumber());
|
log.info("已将原车辆状态设为空闲,车辆ID: {}", oldVehicleId);
|
|
} else {
|
log.warn("查询原车辆信息失败,车辆ID: {}", oldVehicleId);
|
}
|
} catch (Exception e) {
|
log.error("处理原车辆失败,车辆ID: {},错误: {}", oldVehicleId, e.getMessage());
|
}
|
}
|
|
// 7. 查询新车辆关联的车队ID
|
Long newFleetId = null;
|
try {
|
R<Long> 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<Long> 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.setStartTestPointId(null);
|
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.2 下发订单到新车辆
|
PushOrderDto pushOrderDto = new PushOrderDto();
|
pushOrderDto.setStartTaskId(order.getXpuStartTaskId());
|
pushOrderDto.setEndTaskId(order.getXpuEndTaskId());
|
pushOrderDto.setVin(newVehicle.getVehicleVin());
|
pushOrderDto.setOrderId("TEST" + order.getId());
|
pushOrderDto.setStartWaypointId(order.getStartTestPointId());
|
pushOrderDto.setEndWaypointId(order.getEndTestPointId());
|
pushOrderDto.setStartLatitude(order.getStartLat());
|
pushOrderDto.setStartLongitude(order.getStartLon());
|
pushOrderDto.setEndLatitude(order.getEndLat());
|
pushOrderDto.setEndLongitude(order.getEndLon());
|
pushOrderDto.setEndPoiName(order.getEndTestPointName());
|
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);
|
|
} catch (Exception e) {
|
log.error("处理新车辆失败,车辆ID: {},错误: {}", newVehicleId, e.getMessage());
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@Override
|
public R<String> updateOrderStatus(Long orderId, Integer status) {
|
XpTestOrder 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();
|
if(xpOrder.getOrderType() == 1){
|
switch (status) {
|
case 4:
|
// 查询起点点位
|
Long startPointId = xpOrder.getStartTestPointId();
|
XpTestPoint xpPoint = xpTestPointClient.queryById(startPointId).getData();
|
// 添加取消订单定时任务job 时间为点位限停时长
|
Map<String, ? extends Object> cancelJobMaps =
|
new ImmutableMap.Builder<String, Long>().
|
put("id", xpOrder.getId())
|
.build();
|
QuartzManager.addJob(
|
TestOrderCancelJob.class,
|
(TestOrderCancelJob.name + xpOrder.getId()).toUpperCase(),
|
TimeJobType.TEST_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);
|
// 删除定时任务job
|
QuartzManager.removeJob((TestOrderCancelJob.name + xpOrder.getId()).toUpperCase(), TimeJobType.TEST_ORDER_CANCEL);
|
//乘客已上车,重新倒计时不开始行程取消订单
|
XpTestPoint xpPoint1 = xpTestPointClient.queryById(xpOrder.getStartTestPointId()).getData();
|
XpTradingStrategy tradingStrategy = tradingStrategyClient.getTradingStrategyByCityCode(xpPoint1.getCityCode()).getData();
|
if(null != tradingStrategy){
|
Map<String, ? extends Object> cancelJobMaps1 =
|
new ImmutableMap.Builder<String, Long>()
|
.put("id", xpOrder.getId())
|
.build();
|
QuartzManager.addJob(
|
TestOrderCancelJob.class,
|
(TestOrderCancelJob.name + xpOrder.getId()).toUpperCase(),
|
TimeJobType.TEST_ORDER_CANCEL,
|
DateUtils.localDateTimeToDate(LocalDateTime.now().plusMinutes(tradingStrategy.getBdNotBoardedTime())),
|
cancelJobMaps1
|
);
|
}
|
break;
|
case 6:
|
// 删除定时任务job
|
QuartzManager.removeJob((TestOrderCancelJob.name + xpOrder.getId()).toUpperCase(), TimeJobType.TEST_ORDER_CANCEL);
|
// 行程开始时间
|
xpOrder.setTripBegin(LocalDateTime.now());
|
redisService.deleteObject("TEST_ORDER_NAVIGATION_START_DATA:" + xpOrder.getVehicleId());
|
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<String, String> geocode = gdMapGeocodingUtil.geocode(String.valueOf(x), String.valueOf(y));
|
xpOrder.setGetoffAddress(geocode.get("address"));
|
}
|
}
|
//计算行驶总距离和时长
|
List<OrderTrajectory> 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());
|
|
//创建定时结束订单和等待结束后下发巡游单的定时任务
|
Integer arriveTime = xpSystemSettingClient.getOrderFinishTime().getData();
|
// 添加自动结束行程定时任务job
|
Map<String, ? extends Object> endTripJobMaps =
|
new ImmutableMap.Builder<String, String>()
|
.put("id", xpOrder.getId().toString())
|
.put("orderType", OrderType.TEST_ORDER)
|
.build();
|
QuartzManager.addJob(
|
EndTripJob.class,
|
(EndTripJob.name + xpOrder.getId()).toUpperCase(),
|
TimeJobType.END_TRIP,
|
DateUtils.localDateTimeToDate(LocalDateTime.now().plusMinutes(arriveTime)),
|
endTripJobMaps
|
);
|
break;
|
}
|
|
xpOrder.setStatus(status);
|
xpOrder.setStartTestPointId(null);
|
this.updateById(xpOrder);
|
redisService.setCacheObject("TEST_ORDER_STATUS:" + xpOrder.getId(), status, 24L, TimeUnit.HOURS);
|
}else{
|
//测试巡游单,只需要修改状态
|
if(status == 10){
|
xpOrder.setStatus(status);
|
xpOrder.setStartTestPointId(null);
|
this.updateById(xpOrder);
|
redisService.setCacheObject("TEST_ORDER_STATUS:" + xpOrder.getId(), status, 24L, TimeUnit.HOURS);
|
}
|
}
|
return R.ok();
|
}
|
|
@Override
|
public Boolean endTrip(Long orderId) {
|
List<XpTestOrderDetail> xpOrderDetails = new ArrayList<>();
|
// 起步价格明细
|
List<FareBreakdownDetailVo> start = new ArrayList<>();
|
// 里程费价格明细
|
List<FareBreakdownDetailVo> mileage = new ArrayList<>();
|
// 时长费价格明细
|
List<FareBreakdownDetailVo> duration = new ArrayList<>();
|
// 远途费价格明细
|
List<FareBreakdownDetailVo> longDistance = new ArrayList<>();
|
XpTestOrder order = this.getById(orderId);
|
if (order == null) {
|
throw new RuntimeException("订单不存在");
|
}
|
|
if (order.getStatus() != 7) {
|
throw new RuntimeException("订单状态错误");
|
}
|
order.setStatus(8);
|
order.setStartTestPointId(null);
|
order.setTripEnd(LocalDateTime.now());
|
baseMapper.updateById(order);
|
redisService.setCacheObject("TEST_ORDER_STATUS:" + order.getId(), 8, 24L, TimeUnit.HOURS);
|
|
// 根据订单所属开通城市id 查询定价
|
XpPricing pricing = bootCommonService.getXpPricing(order.getCityId(),order.getVehicleId());
|
if (pricing == null) {
|
throw new ServiceException("当前城市未配置定价规则", I18nKeyConstant.ORDER_PRICING_NOT_EXISTS);
|
}
|
// 费用
|
// 查询订单轨迹
|
List<OrderTrajectory> trajectories = mongoTemplate.find(
|
new Query(Criteria.where("orderId").is(orderId))
|
.with(Sort.by(Sort.Direction.ASC, "locatetime")),
|
OrderTrajectory.class
|
);
|
List<TaxiPricingDemoService.GpsPoint> 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();
|
|
JSONObject basicPricingJson = JSONObject.parseObject(pricing.getBasicPricingJson());
|
|
XpPricingCardVo xpPricingCardVo = xpPricingUtil.calculationFixedPrice(order.getVehicleId().toString(), basicPricingJson, null, mileageKilometers, duration1, 0, 0.0, pricing.getIsDynamicPrice(), order.getCityId(), pricing.getDynamicPricingJson());
|
XpTestOrderDetail xpOrderDetail = new XpTestOrderDetail();
|
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 XpTestOrderDetail();
|
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 XpTestOrderDetail();
|
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 (XpTestOrderDetail xpOrderDetail1 : xpOrderDetails) {
|
totalPrice = totalPrice.add(xpOrderDetail1.getMoney());
|
xpTestOrderDetailMapper.insert(xpOrderDetail1);
|
}
|
order.setOrderMoney(totalPrice);
|
order.setStartTestPointId(null);
|
baseMapper.updateById(order);
|
} else {
|
//动态计费
|
if (pricing.getPricingType() == 1) {
|
|
for (int i = 0; i < startingFareJsonArray.size(); i++) {
|
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<String, BigDecimal> 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() + "_" + order.getVehicleId());
|
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() + "_" + order.getVehicleId(), priceRate, time.longValue(), TimeUnit.MINUTES);
|
}
|
|
}
|
}
|
|
}
|
// 封装费用明细数据
|
for (Map.Entry<String, BigDecimal> 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) {
|
XpTestOrderDetail xpOrderDetail = new XpTestOrderDetail();
|
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) {
|
XpTestOrderDetail xpOrderDetail = new XpTestOrderDetail();
|
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) {
|
XpTestOrderDetail xpOrderDetail = new XpTestOrderDetail();
|
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) {
|
XpTestOrderDetail xpOrderDetail = new XpTestOrderDetail();
|
BeanUtils.copyProperties(fareBreakdownDetailVo, xpOrderDetail);
|
xpOrderDetail.setOrderId(orderId);
|
xpOrderDetail.setDetailType(3);
|
xpOrderDetail.setCreateTime(LocalDateTime.now());
|
mileageMoney = mileageMoney.add(fareBreakdownDetailVo.getMoney());
|
xpOrderDetails.add(xpOrderDetail);
|
}
|
|
|
for (XpTestOrderDetail xpOrderDetail : xpOrderDetails) {
|
xpTestOrderDetailMapper.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.setStartTestPointId(null);
|
baseMapper.updateById(order);
|
|
}
|
order.setStartTestPointId(null);
|
baseMapper.updateById(order);
|
return true;
|
}
|
|
|
|
|
/**
|
* 获取配置 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;
|
}
|
|
|
/**
|
* 获取订单数量
|
*
|
* @param dto
|
* @return
|
*/
|
@Override
|
public Integer getOrderNum(GetOrderNumDto dto) {
|
Long num = this.baseMapper.selectCount(new LambdaQueryWrapper<XpTestOrder>().eq(XpTestOrder::getCityId, dto.getOpenCityId()).between(BaseModel::getCreateTime, dto.getTimeStr(), new Date()));
|
return num.intValue();
|
}
|
|
|
@Override
|
public void saveVehicleLocation(OrderTrajectory orderTrajectory) {
|
// 查询订单信息
|
Integer status = redisService.getCacheObject("TEST_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<String, Double> 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("调用高德计算距离出错");
|
}
|
}
|
}
|
|
|
/**
|
* 查询订单轨迹(mongodb)
|
*
|
* @param orderId 订单id
|
* @return 轨迹列表(按 locatetime 升序)
|
*/
|
private List<OrderTrajectory> queryOrderTrajectories(Long orderId) {
|
if (orderId == null) {
|
return new ArrayList<>();
|
}
|
List<OrderTrajectory> trajectories = mongoTemplate.find(
|
new Query(Criteria.where("orderId").is(orderId))
|
.with(Sort.by(Sort.Direction.ASC, "locatetime")),
|
OrderTrajectory.class
|
);
|
return trajectories;
|
}
|
|
/**
|
* 订单费用明细聚合:按 detail_type 汇总 money
|
*
|
* <p>detail_type:1=起步价 2=里程费 3=时长费 4=远途费</p>
|
*/
|
private Map<Integer, BigDecimal> sumOrderDetailMoneyByDetailType(Long orderId) {
|
if (orderId == null) {
|
return Collections.emptyMap();
|
}
|
List<XpTestOrderDetail> detailList = xpTestOrderDetailMapper.selectList(
|
new LambdaQueryWrapper<XpTestOrderDetail>()
|
.eq(XpTestOrderDetail::getOrderId, orderId)
|
.ne(XpTestOrderDetail::getIsDeleted, true)
|
);
|
if (CollectionUtils.isEmpty(detailList)) {
|
return Collections.emptyMap();
|
}
|
Map<Integer, BigDecimal> sumMap = new HashMap<>();
|
for (XpTestOrderDetail 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<XpTestOrderDetailVO.OrderPosition> convertTrajectoriesToPositions(List<OrderTrajectory> trajectories) {
|
if (CollectionUtils.isEmpty(trajectories)) {
|
return new ArrayList<>();
|
}
|
List<XpTestOrderDetailVO.OrderPosition> 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());
|
XpTestOrderDetailVO.OrderPosition position = new XpTestOrderDetailVO.OrderPosition();
|
position.setLongitude(longitude);
|
position.setLatitude(latitude);
|
positions.add(position);
|
} catch (Exception e) {
|
// ignore invalid point
|
}
|
}
|
return positions;
|
}
|
|
|
|
|
@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);
|
}
|
|
XpTestOrder order = this.getById(id);
|
|
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(XpTestOrder::getOrderMoney, amount)
|
.set(XpTestOrder::getChangeMoneyFlag, Boolean.TRUE)
|
.eq(XpTestOrder::getId, id)
|
.eq(XpTestOrder::getStatus, pendingPaymentStatus)
|
.update();
|
if (!updated) {
|
throw new ServiceException("订单改价失败,请稍后重试", I18nKeyConstant.ORDER_CHANGE_AMOUNT_FAILED);
|
}
|
}
|
|
@Override
|
public R<?> cancelTestOrderById(Long orderId) {
|
XpTestOrder xpTestOrder = this.getById(orderId);
|
if (xpTestOrder != null) {
|
xpTestOrder.setStatus(0);
|
xpTestOrder.setStartTestPointId(null);
|
this.updateById(xpTestOrder);
|
XpVehicle xpVehicle = xpVehicleClient.queryById(xpTestOrder.getVehicleId()).getData();
|
PushOrderDto pushOrderDto = new PushOrderDto();
|
pushOrderDto.setVin(xpVehicle.getVehicleVin());
|
pushOrderDto.setOrderId("TEST" + xpTestOrder.getId());
|
pushOrderDto.setOperation_type(OrderOperationType.CLOUD_CANCEL_ORDER);
|
R<Boolean> booleanR = orderMissionClient.cancelOrder(pushOrderDto);
|
if(booleanR==null || !booleanR.getData()){
|
log.error("XPU取消测试巡游订单失败");
|
return R.fail("XPU取消测试巡游订单失败");
|
}
|
return R.ok();
|
}
|
return R.fail("获取订单失败");
|
}
|
}
|