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 cn.hutool.core.util.DesensitizedUtil;
|
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.core.conditions.query.QueryWrapper;
|
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.WeatherDTO;
|
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.frdXfipEvent.XpFrdXfipEventCouponLineDTO;
|
import com.xiaopeng.api.dto.frdXfipEvent.XpFrdXfipEventIngestContentDTO;
|
import com.xiaopeng.api.dto.frdXfipEvent.XpFrdXfipEventIngestReqDTO;
|
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.*;
|
import com.xiaopeng.api.dto.payFront.*;
|
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.SysGroupMenuClient;
|
import com.xiaopeng.api.feignClient.account.UserResourcesClient;
|
import com.xiaopeng.api.feignClient.account.XpAppUserCollectAddrClient;
|
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.frdXfipEvent.XpFrdXfipEventClient;
|
import com.xiaopeng.api.feignClient.maintenance.XpTaskRuleClient;
|
import com.xiaopeng.api.feignClient.map.XpMapClient;
|
import com.xiaopeng.api.feignClient.map.XpStationClient;
|
import com.xiaopeng.api.feignClient.operation.*;
|
import com.xiaopeng.api.feignClient.order.XpCruiseOrderClient;
|
import com.xiaopeng.api.feignClient.payFront.XpPayFrontClient;
|
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.query.order.InvoiceOrdersQuery;
|
import com.xiaopeng.api.query.order.XpOrderInvoiceQuery;
|
import com.xiaopeng.api.vo.OrderType;
|
import com.xiaopeng.api.vo.account.CalculateDiscountAmountVo;
|
import com.xiaopeng.api.vo.account.LoginUser;
|
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.automaticPilot.protobuf.TaskActionType;
|
import com.xiaopeng.api.vo.frdXfipEvent.XpFrdXfipEventIngestRespVO;
|
import com.xiaopeng.api.vo.operation.NearestNextVo;
|
import com.xiaopeng.api.vo.operation.RouteVo;
|
import com.xiaopeng.api.vo.order.*;
|
import com.xiaopeng.api.vo.payFront.*;
|
import com.xiaopeng.api.vo.order.FareBreakdownVo;
|
import com.xiaopeng.api.vo.payFront.CreditOrderCreateVo;
|
import com.xiaopeng.api.vo.payFront.CreditPaySignQueryVo;
|
import com.xiaopeng.api.vo.payFront.CreditPrecreateVo;
|
import com.xiaopeng.api.vo.vehicleConvoy.AddTaxiPassengerVo;
|
import com.xiaopeng.api.vo.vehicleConvoy.BluetoothDeliverVo;
|
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.*;
|
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.service.TokenService;
|
import com.xiaopeng.common.security.utils.SecurityUtils;
|
import com.xiaopeng.order.constant.I18nKeyConstant;
|
import com.xiaopeng.order.dto.MileageDto;
|
import com.xiaopeng.order.dto.UserOrderListDto;
|
import com.xiaopeng.order.job.base.QuartzManager;
|
import com.xiaopeng.order.job.base.TimeJobType;
|
import com.xiaopeng.order.job.jobs.*;
|
import com.xiaopeng.api.model.order.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.*;
|
import com.xiaopeng.order.utils.holiday.HolidayUtil;
|
import com.xiaopeng.order.vo.*;
|
import com.xiaopeng.api.dto.order.UpdateInvoiceStatusDto;
|
import com.xiaopeng.api.dto.payFront.*;
|
import lombok.RequiredArgsConstructor;
|
import lombok.extern.slf4j.Slf4j;
|
import org.apache.shardingsphere.transaction.annotation.ShardingSphereTransactionType;
|
import org.apache.shardingsphere.transaction.core.TransactionType;
|
import org.gavaghan.geodesy.Ellipsoid;
|
import org.gavaghan.geodesy.GlobalCoordinates;
|
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.time.format.DateTimeFormatter;
|
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;
|
|
/**
|
* <p>
|
* 订单 服务实现类
|
* </p>
|
*
|
* @author mitao
|
* @since 2026-02-03
|
*/
|
@Slf4j
|
@Service
|
@RequiredArgsConstructor(onConstructor_ = @Lazy)
|
public class XpOrderServiceImpl extends ServiceImpl<XpOrderMapper, XpOrder> implements IXpOrderService {
|
|
private static final String REASSIGN_CANCEL_CONTEXT_PREFIX = "REASSIGN_CANCEL_CONTEXT:";
|
private static final long REASSIGN_CANCEL_CONTEXT_TTL_MINUTES = 5L;
|
|
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;
|
@Lazy
|
@Autowired
|
private IXpOrderEvaluateService xpOrderEvaluateService;
|
private final IXpOrderCancelService xpOrderCancelService;
|
@Lazy
|
@Autowired
|
private 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;
|
@Lazy
|
@Autowired
|
private IXpTestOrderService xpTestOrderService;
|
private final XpMapClient xpMapClient;
|
@Resource
|
private XpRoutePlanClient xpRoutePlanClient;
|
@Resource
|
@Lazy
|
private BootCommonService bootCommonService;
|
@Autowired
|
private SysGroupMenuClient sysGroupMenuClient;
|
@Autowired
|
private XpCruiseOrderClient xpCruiseOrderClient;
|
|
@Autowired
|
private MongoTemplate mongoTemplate;
|
@Autowired
|
private HolidayUtil holidayUtil;
|
|
@Autowired
|
private BluetoothUnlockTokenUtil bluetoothUnlockTokenUtil;
|
|
@Resource
|
private BluetoothUnlockClient bluetoothUnlockClient;
|
|
@Resource
|
private XpTradingStrategyClient tradingStrategyClient;
|
|
@Resource
|
private OfficialPartnerUtil officialPartnerUtil;
|
|
@Resource
|
private TokenService tokenService;
|
|
@Resource
|
private XpPayFrontClient xpPayFrontClient;
|
@Resource
|
private XpStationClient xpStationClient;
|
|
@Resource
|
private XpFrdXfipEventClient xpFrdXfipEventClient;
|
|
@Value("${robotaxi.appId:xp_robotaxi_api}")
|
private String appid;
|
|
@Resource(name = "hutoolSnowflake")
|
private Snowflake snowflake;
|
|
|
@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<DailyCountDTO> dailyCountList = baseMapper.getDailyOrderCount(startDate, endDate);
|
dto.setDailyCountList(Objects.nonNull(dailyCountList) ? dailyCountList : new ArrayList<>());
|
|
return dto;
|
}
|
|
@Override
|
public PageInfo<XpOrderPageVO> pageList(XpOrderPageQuery 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);
|
}
|
}
|
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<XpOrder> 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);
|
if (null != query.getActivityId()) {
|
wrapper.eq(XpOrder::getActivityId, query.getActivityId());
|
}
|
|
List<XpOrder> list = this.list(wrapper);
|
BigDecimal orderMoney = list.stream().filter(order -> Objects.nonNull(order.getOrderMoney())).map(XpOrder::getOrderMoney).collect(Collectors.toList()).stream().reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
|
BigDecimal couponMoney = list.stream().filter(order -> Objects.nonNull(order.getCouponMoney())).map(XpOrder::getCouponMoney).collect(Collectors.toList()).stream().reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
|
|
// 2. 分页查询
|
Page<XpOrder> page = new Page<>(query.getPageNum(), query.getPageSize());
|
Page<XpOrder> pageResult = this.page(page, wrapper);
|
|
if (CollectionUtils.isEmpty(pageResult.getRecords())) {
|
return buildEmptyOrderPage(query);
|
}
|
|
// 3. 收集关联ID
|
Set<Long> userIds = new HashSet<>();
|
Set<Long> vehicleIds = new HashSet<>();
|
Set<Long> 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<String, UcUserInfo> userInfoMap = batchGetUsers(userIds);
|
Map<Long, XpVehicle> vehicleMap = batchGetVehicles(vehicleIds);
|
Map<Long, XpFleet> fleetMap = batchGetFleets(fleetIds);
|
|
// 5. 组装 VO
|
List<XpOrderPageVO> voList = pageResult.getRecords().stream()
|
.map(order -> convertToPageVO(order, userInfoMap, vehicleMap, fleetMap, orderMoney, couponMoney))
|
.collect(Collectors.toList());
|
|
// 6. 返回分页结果
|
PageInfo<XpOrderPageVO> resultPage = new PageInfo<>(query.getPageNum(), query.getPageSize());
|
resultPage.setTotal(pageResult.getTotal());
|
resultPage.setRecords(voList);
|
return resultPage;
|
}
|
|
private PageInfo<XpOrderPageVO> buildEmptyOrderPage(XpOrderPageQuery query) {
|
PageInfo<XpOrderPageVO> 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<String, UcUserInfo> 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<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. 校验订单是否存在
|
XpOrder order = this.getById(id);
|
if (Objects.isNull(order)) {
|
throw new ServiceException("订单不存在", I18nKeyConstant.ORDER_NOT_EXISTS);
|
}
|
|
this.removeById(id);
|
}
|
|
@Override
|
public List<XpOrderPageVO> exportList(XpOrderExcelQuery query) {
|
// 1. 构建查询条件
|
LambdaQueryWrapper<XpOrder> wrapper = buildExcelQueryWrapper(query);
|
|
// 1.1 车牌号筛选:先查车辆服务获取车辆ID列表
|
if (StringUtils.isNotBlank(query.getPlateNumber())) {
|
R<List<XpVehicle>> vehicleResult = xpVehicleClient.listByPlateNumber(query.getPlateNumber());
|
if (vehicleResult == null || CollectionUtils.isEmpty(vehicleResult.getData())) {
|
return new ArrayList<>();
|
}
|
List<Long> 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<XpOrder> orderList = this.list(wrapper);
|
|
if (CollectionUtils.isEmpty(orderList)) {
|
return new ArrayList<>();
|
}
|
|
// 3. 收集关联ID
|
Set<Long> userIds = new HashSet<>();
|
Set<Long> vehicleIds = new HashSet<>();
|
Set<Long> 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<String, UcUserInfo> userInfoMap = batchGetUsers(userIds);
|
Map<Long, XpVehicle> vehicleMap = batchGetVehicles(vehicleIds);
|
Map<Long, XpFleet> fleetMap = batchGetFleets(fleetIds);
|
|
// 5. 组装 VO
|
return orderList.stream()
|
.map(order -> convertToPageVO(order, userInfoMap, vehicleMap, fleetMap, BigDecimal.ZERO, BigDecimal.ZERO))
|
.collect(Collectors.toList());
|
}
|
|
/**
|
* 构建查询条件
|
*/
|
private LambdaQueryWrapper<XpOrder> buildQueryWrapper(XpOrderPageQuery query) {
|
LambdaQueryWrapper<XpOrder> 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<XpOrder> buildExcelQueryWrapper(XpOrderExcelQuery query) {
|
LambdaQueryWrapper<XpOrder> 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<String, UcUserInfo> batchGetUsers(Set<Long> userIds) {
|
if (CollectionUtils.isEmpty(userIds)) {
|
return new HashMap<>();
|
}
|
return userResourcesClient.getUserListByUids(new ArrayList<>(userIds), false);
|
}
|
|
/**
|
* 批量获取车辆信息
|
*/
|
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, Boolean isFree) {
|
// 1. 校验订单是否存在
|
XpOrder order = this.getById(orderId);
|
if (Objects.isNull(order)) {
|
throw new ServiceException("订单不存在");
|
}
|
|
// 2. 校验操作类型 操作类型:1=取消订单,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 {
|
if (isFree == null) {
|
throw new ServiceException("是否豁免费用不能为空", I18nKeyConstant.ORDER_CLOSE_FAILED);
|
}
|
// 关闭订单: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);
|
if (isFree) {
|
this.setFree(order.getId());
|
}
|
}
|
}
|
|
/**
|
* 转换为分页列表 VO
|
*/
|
private XpOrderPageVO convertToPageVO(XpOrder order,
|
Map<String, UcUserInfo> userInfoMap,
|
Map<Long, XpVehicle> vehicleMap,
|
Map<Long, XpFleet> fleetMap,
|
BigDecimal orderMoney,
|
BigDecimal couponMoney) {
|
LoginUser loginUser = tokenService.getLoginUser();
|
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());
|
vo.setStartPointName(order.getStartPointName());
|
vo.setEndPointName(order.getEndPointName());
|
vo.setOrderTotalAmount(orderMoney);
|
vo.setDiscountTotalAmount(couponMoney);
|
|
// 用户信息
|
if (Objects.nonNull(order.getAppUserId()) && userInfoMap != null && userInfoMap.containsKey(order.getAppUserId().toString())) {
|
UcUserInfo userInfo = userInfoMap.get(order.getAppUserId().toString());
|
vo.setNickName(userInfo.getNickName());
|
if(!loginUser.getPhoneDesensitizationExempt()){
|
vo.setPhoneNumber(DesensitizedUtil.mobilePhone(userInfo.getMobile()));
|
}
|
|
}
|
|
// 车辆信息
|
if (Objects.nonNull(order.getVehicleId()) && vehicleMap.containsKey(order.getVehicleId())) {
|
XpVehicle vehicle = vehicleMap.get(order.getVehicleId());
|
vo.setPlateNumber(vehicle.getPlateNumber());
|
vo.setVehicleModel(vehicle.getVehicleModelName());
|
}
|
|
// 车队信息
|
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());
|
|
Long userId = order.getAppUserId();
|
Integer status = order.getStatus();
|
if (order.getStatus() == 1) {
|
order.setStatus(0);
|
Long appUserId = order.getAppUserId();
|
order.setAppUserId(null);
|
baseMapper.updateById(order);
|
order.setAppUserId(appUserId);
|
bootCommonService.setCache_orderStatus(order.getId(), order, null);
|
if(null == order.getOfficialPartnerId()){
|
//删除蓝牙秘钥
|
String token = redisService.getCacheObject("rt:bluetooth:unlock:order:" + order.getId());
|
if (StringUtils.isNotBlank(token)) {
|
redisService.deleteObject("rt:bluetooth:unlock:" + token);
|
redisService.deleteObject("rt:bluetooth:unlock:order:" + order.getId());
|
XpVehicle xpVehicle = xpVehicleClient.queryById(order.getVehicleId()).getData();
|
BluetoothDeliverVo vo = new BluetoothDeliverVo();
|
vo.setCredential(token);
|
String vin = SensitiveDataCryptoUtil.decryptIfPossible(xpVehicle.getVehicleVin());
|
vo.setVin(vin);
|
vo.setPassengerUid(order.getAppUserId());
|
BaseVehicleUserDto delete = bluetoothUnlockClient.delete(vo);
|
if (null == delete || delete.getCode() != 0) {
|
log.error("删除蓝牙解锁失败:{} {}", delete.getMsg(), order.getOrderNumber());
|
}
|
}
|
}
|
// 消息推送
|
Map<String, Object> params = new HashMap<>();
|
Map<String, Object> 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<String> 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<String> 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();
|
order.setStatus(OrderStatusEnum.CANCELED.getCode());
|
|
bootCommonService.setCache_orderStatus(order.getId(), order, null);
|
|
// 2. 保存取消记录
|
XpOrderCancel orderCancel = new XpOrderCancel();
|
orderCancel.setOrderId(order.getId());
|
orderCancel.setCancelSource(3); // 3=运营人员(后台管理接口固定为运营人员)
|
orderCancel.setUserId(SecurityUtils.getUserId()); // 当前登录用户ID
|
orderCancel.setCancelReason("运营人员取消订单");
|
xpOrderCancelService.save(orderCancel);
|
// 消息推送
|
Map<String, Object> params = new HashMap<>();
|
Map<String, Object> 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<String> 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<String> 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("运营人员取消订单成功,推送乘客消息完成");
|
|
if(null == order.getOfficialPartnerId()){
|
//判断还原车辆状态
|
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());
|
}
|
}
|
}
|
|
//查询车辆信息
|
R<XpVehicle> xpVehicleR = xpVehicleClient.queryById(order.getVehicleId());
|
if (xpVehicleR.getCode() == HttpStatus.SUCCESS && Objects.nonNull(xpVehicleR.getData())) {
|
XpVehicle xpVehicle = xpVehicleR.getData();
|
String vin = SensitiveDataCryptoUtil.decryptIfPossible(xpVehicle.getVehicleVin());
|
// 组装调用车机参数
|
CancelTaxiPassengerVo cancelTaxiPassengerVo = new CancelTaxiPassengerVo();
|
cancelTaxiPassengerVo.setVin(vin);
|
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(order, status);
|
|
} else {
|
log.info("查询车辆信息失败,订单ID: {}, 下发取消订单命令失败", order.getId());
|
}
|
log.info("订单取消成功,订单ID: {}, 操作人: {}", order.getId(), SecurityUtils.getUsername());
|
}else{
|
//流量接入-订单推送 推送取消订单
|
officialPartnerUtil.pushCancelOrder(order.getOfficialPartnerId(), orderCancel, order.getStatus());
|
}
|
|
}
|
|
/**
|
* 关闭订单
|
*/
|
private void closeOrder(XpOrder order) {
|
XpVehicle xpVehicle = xpVehicleClient.queryById(order.getVehicleId()).getData();
|
order.setStatus(7);
|
order.setGetoffTime(LocalDateTime.now());
|
// 解析下车地点
|
String vin = SensitiveDataCryptoUtil.decryptIfPossible(xpVehicle.getVehicleVin());
|
VehiclePosition vehiclePosition = mongoTemplate.findOne(new Query(Criteria.where("vin").is(vin)), 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());
|
Long appUserId = order.getAppUserId();
|
order.setAppUserId(null);
|
this.updateById(order);
|
|
bootCommonService.setCache_orderStatus(order.getId(), order, null);
|
|
// 消息推送
|
Map<String, Object> params1 = new HashMap<>();
|
Map<String, Object> 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<String> 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<String> 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<String, Object> params = new HashMap<>();
|
Map<String, Object> 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<String> 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<String> 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());
|
}
|
});
|
}
|
|
//结束行程,修改状态和计算费用
|
endTrip(order.getId(), appUserId.toString());
|
|
// 消息推送
|
Map<String, Object> params2 = new HashMap<>();
|
Map<String, Object> 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<String> 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<String> 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());
|
}
|
});
|
}
|
|
if(order.getOfficialPartnerId() == null){
|
//创建定时任务执行延迟驶离终点和下发巡游单
|
XpPoint point = xpPointClient.queryById(order.getEndPointId()).getData();
|
Integer parkingTimeLimit = point.getParkingTimeLimit();
|
//如果点位位于场站内,则使用场站的限停时间
|
if(null != point.getStationId()){
|
XpStation xpStation = xpStationClient.getXpStationById(point.getStationId()).getData();
|
parkingTimeLimit = xpStation.getParkingTimeLimit();
|
}
|
Map<String, ? extends Object> jobMaps =
|
new ImmutableMap.Builder<String, String>()
|
.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(parkingTimeLimit)),
|
jobMaps
|
);
|
}else{
|
//流量接入-订单推送 推送关闭订单
|
officialPartnerUtil.pushEndOrder(order.getOfficialPartnerId(), order, "运维人员关闭订单");
|
}
|
}
|
|
/**
|
* 改派订单
|
*/
|
@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<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);
|
|
if (Objects.isNull(newVehicle.getState()) || newVehicle.getState() != 0) {
|
throw new ServiceException("新车辆已接单,请重新选择", I18nKeyConstant.ORDER_REASSIGN_NEW_VEHICLE_NOT_FREE);
|
}
|
if (newVehicle.getOperatingStatus() != 1) {
|
throw new ServiceException("新车辆状态为非运营状态,请重新选择", I18nKeyConstant.ORDER_REASSIGN_NEW_VEHICLE_NOT_OPERATIONAL);
|
}
|
String vin = SensitiveDataCryptoUtil.decryptIfPossible(newVehicle.getVehicleVin());
|
VehiclePosition vehiclePosition = mongoTemplate.findOne(Query.query(Criteria.where("vin").is(vin)), VehiclePosition.class);
|
if (null == vehiclePosition) {
|
throw new ServiceException("未能获取到新车的定位数据,请重新选择", I18nKeyConstant.ORDER_REASSIGN_NEW_VEHICLE_NOT_POSITION);
|
}
|
if (vehiclePosition.getCurrentElectricity() < 20) {
|
throw new ServiceException("新车电量不足,请重新选择", I18nKeyConstant.ORDER_REASSIGN_NEW_VEHICLE_BATTERY_LOW);
|
}
|
|
// 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.setStartTaskId(order.getXpuStartTaskId());
|
cancelDto.setEndTaskId(order.getXpuEndTaskId());
|
cancelDto.setOrderId(order.getId().toString());
|
String vin1 = SensitiveDataCryptoUtil.decryptIfPossible(oldVehicle.getVehicleVin());
|
cancelDto.setVin(vin1);
|
cancelDto.setOperation_type(OrderOperationType.CLOUD_CANCEL_ORDER);
|
cancelDto.setCancelScene(CancelOrderSceneEnum.REASSIGN_OLD_VEHICLE.getCode());
|
orderMissionClient.cancelOrder(cancelDto);
|
cacheReassignCancelContext(orderId, oldVehicleId, newVehicleId,
|
vin1, currentStatus);
|
log.info("已取消原车辆订单指令,订单ID: {}, VIN: {}, cancelScene: {}",
|
orderId, vin1, cancelDto.getCancelScene());
|
|
// 6.1.2 解绑乘客与原车辆关系
|
if (Objects.nonNull(order.getAppUserId())) {
|
CancelTaxiPassengerVo cancelVo = new CancelTaxiPassengerVo();
|
cancelVo.setVin(vin1);
|
cancelVo.setUid(order.getAppUserId().toString());
|
cancelVo.setOrderNo(order.getOrderNumber());
|
cancelVo.setAppId(appid);
|
userVehicleClient.cancelTaxiPassenger(cancelVo);
|
log.info("已解绑乘客与原车辆关系,用户ID: {}, VIN: {}", order.getAppUserId(), vin1);
|
}
|
|
// 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<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.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(vin);
|
addVo.setUid(order.getAppUserId().toString());
|
addVo.setOrderNo(order.getOrderNumber());
|
addVo.setAppId(appid); // TODO 获取appId
|
userVehicleClient.addTaxiPassenger(addVo);
|
log.info("已绑定乘客与新车辆关系,用户ID: {}, VIN: {}", order.getAppUserId(), vin);
|
}
|
|
|
// 关闭巡游单子
|
R<XpCruiseOrder> stringR = xpCruiseOrderClient.cancelCruiseOrderByVin(vin);
|
if (stringR == null || stringR.getCode() != 200) {
|
log.info("关闭巡游单子失败 车牌号 {}", newVehicle.getPlateNumber());
|
}
|
log.info("关闭巡游单成功 {} {}", newVehicle.getPlateNumber(), order.getOrderNumber());
|
XpCruiseOrder cruiseOrder = stringR.getData();
|
// 下发命令
|
PushOrderDto pushOrderDto = new PushOrderDto();
|
pushOrderDto.setStartTaskId(order.getXpuStartTaskId());
|
pushOrderDto.setEndTaskId(order.getXpuEndTaskId());
|
pushOrderDto.setVin(vin);
|
pushOrderDto.setOrderId(order.getId().toString());
|
pushOrderDto.setStartWaypointId(order.getStartPointId());
|
pushOrderDto.setEndWaypointId(order.getEndPointId());
|
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);
|
if(null != cruiseOrder){
|
pushOrderDto.setLastOrderId("XU" + cruiseOrder.getId());
|
pushOrderDto.setLastXpuTaskId(cruiseOrder.getXpuTaskId());
|
}
|
pushOrderDto.setTaskActionType(TaskActionType.kTerminateForNewTask);
|
// 调用推送服务 orderId + carVin
|
orderMissionClient.pushOrder(pushOrderDto);
|
log.info("已下发订单到新车辆,订单ID: {}, VIN: {}", orderId, vin);
|
|
// 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<String, Object> params = new HashMap<>();
|
Map<String, Object> 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());
|
}
|
|
}
|
|
private void cacheReassignCancelContext(Long orderId, Long oldVehicleId, Long newVehicleId,
|
String oldVehicleVin, Integer preReassignStatus) {
|
JSONObject context = new JSONObject();
|
context.put("orderId", orderId);
|
context.put("oldVehicleId", oldVehicleId);
|
context.put("newVehicleId", newVehicleId);
|
context.put("oldVehicleVin", oldVehicleVin);
|
context.put("cancelScene", CancelOrderSceneEnum.REASSIGN_OLD_VEHICLE.getCode());
|
context.put("operationTime", System.currentTimeMillis());
|
context.put("preReassignStatus", preReassignStatus);
|
redisService.setCacheObject(REASSIGN_CANCEL_CONTEXT_PREFIX + orderId, context.toJSONString(),
|
REASSIGN_CANCEL_CONTEXT_TTL_MINUTES, TimeUnit.MINUTES);
|
log.info("记录改派取消上下文成功,orderId: {}, oldVehicleId: {}, newVehicleId: {}, preReassignStatus: {}",
|
orderId, oldVehicleId, newVehicleId, preReassignStatus);
|
}
|
|
/**
|
* 为原车辆创建返程巡游订单
|
* <p>
|
* 改派时,原车辆需要返回巡游路线:
|
* - 若最新巡游订单状态为已终止(status=2),返回原出发点
|
* - 若最新巡游订单状态为已完成(status=3),返回原终点
|
* - 其他状态,默认返回原出发点
|
* </p>
|
*
|
* @param order 当前订单信息
|
*/
|
private void createReturnCruiseOrder(Long vehicleId, XpOrder order) {
|
XpVehicle oldVehicle = xpVehicleClient.queryById(vehicleId).getData();
|
try {
|
// 查询该车辆最新的一条巡游订单
|
XpCruiseOrder lastCruise = xpCruiseOrderMapper.selectOne(
|
new LambdaQueryWrapper<XpCruiseOrder>()
|
.eq(XpCruiseOrder::getVehicleId, oldVehicle.getId())
|
.orderByDesc(BaseModel::getCreateTime)
|
.last("limit 1")
|
);
|
|
if (lastCruise == null) {
|
log.warn("原车辆没有巡游订单记录,跳过创建返程订单,车辆ID: {}", oldVehicle.getId());
|
return;
|
}
|
String vin1 = SensitiveDataCryptoUtil.decryptIfPossible(oldVehicle.getVehicleVin());
|
XpCruiseOrder returnOrder = new XpCruiseOrder();
|
returnOrder.setVehicleId(oldVehicle.getId());
|
returnOrder.setVehicleVin(vin1);
|
returnOrder.setFleetId(oldVehicle.getFleetId());
|
returnOrder.setOrderType(2); // 返程单
|
returnOrder.setStatus(1); // 进行中
|
returnOrder.setLastOrderId(lastCruise.getId());
|
returnOrder.setCreateTime(LocalDateTime.now());
|
returnOrder.setMissionId(lastCruise.getMissionId());
|
returnOrder.setXpuTaskId(snowflake.nextId() + "");
|
|
// 返程起点 = 当前订单的终点(车辆当前位置附近)
|
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.setEndTaskId(returnOrder.getXpuTaskId());
|
pushOrderDto.setVin(vin1);
|
pushOrderDto.setOrderId("XU" + returnOrder.getId());
|
pushOrderDto.setStartWaypointId(returnOrder.getStartPointId());
|
pushOrderDto.setEndWaypointId(returnOrder.getEndPointId());
|
pushOrderDto.setStartLatitude(returnOrder.getStartLat());
|
pushOrderDto.setStartLongitude(returnOrder.getStartLon());
|
pushOrderDto.setEndLatitude(returnOrder.getEndLat());
|
pushOrderDto.setEndLongitude(returnOrder.getEndLon());
|
pushOrderDto.setOrderType(OrderType.CRUISE_ORDER);
|
pushOrderDto.setStage(2);
|
if(null != lastCruise){
|
pushOrderDto.setLastOrderId("XU" + lastCruise.getId());
|
pushOrderDto.setLastXpuTaskId(lastCruise.getXpuTaskId());
|
}
|
pushOrderDto.setTaskActionType(TaskActionType.kTerminateForNewTask);
|
// 调用推送服务 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.info("获取用户快捷胶囊失败,起点不存在,uid:{}, startPointId:{}", uid, startPointId);
|
return null;
|
}
|
|
Integer minOrderDistance = getMinOrderDistanceSafely(uid, startPointId);
|
if (minOrderDistance == null) {
|
return null;
|
}
|
|
LocalDateTime now = LocalDateTime.now();
|
List<XpOrder> 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;
|
}
|
|
@Resource
|
private XpAppUserCollectAddrClient xpAppUserCollectAddrClient;
|
|
@Override
|
public NearestNextVo quickCapsule(String uid, Long startPointId) {
|
|
Long userId = Long.valueOf(uid);
|
// - 新用户处理: 历史订单数 < 3单或无匹配数据时,隐藏该胶囊。
|
Long validHistoryCount = this.baseMapper.countUserValidHistoryOrders(userId);
|
if (validHistoryCount == null || validHistoryCount < 3) {
|
return null;
|
}
|
|
LocalDateTime now = LocalDateTime.now();
|
LocalTime startMoment = now.minusHours(1).toLocalTime();
|
LocalTime endMoment = now.plusHours(1).toLocalTime();
|
LocalDate startDate = now.minusMonths(1).toLocalDate();
|
LocalDateTime endDate = now.plusMonths(1);
|
|
// 时段强相关目的地(场景触发)当前时间点(当前时间 ± 1小时)内,过去30天内前往次数最多的目的地。
|
List<Long> endPointId = this.baseMapper.timeSceneOrders(startDate, endDate, startMoment, endMoment, userId);
|
// 判定条件: 若当前时段内无明显的规律性目的地(例如:用户在周六下午偶尔出门,且目的地不固定),则推荐用户过去30天内订单总量最高的前1名地址(需排除当前起点)。
|
endPointId.addAll(this.baseMapper.frequencyOrders(startDate, endDate, startMoment, endMoment, userId));
|
|
Integer minOrderDistance = getMinOrderDistanceSafely(uid, startPointId);
|
//获取用户最短可发单距离失败
|
if (minOrderDistance == null) {
|
return null;
|
}
|
XpPoint start = getPointById(startPointId);
|
GlobalCoordinates startCoordinates = new GlobalCoordinates(start.getPointLon(), start.getPointLat());
|
List<Long> pointIdsByUserId = Optional.ofNullable(xpAppUserCollectAddrClient.getPointIdsByUserId(Long.valueOf(uid)).getData())
|
.orElse(new ArrayList<>());
|
for (Long pointId : endPointId) {
|
XpPoint endPoint = getPointById(pointId);
|
Integer parkingTimeLimit = endPoint.getParkingTimeLimit();
|
//如果点位位于场站内,则使用场站的限停时间
|
if(null != endPoint.getStationId()){
|
XpStation xpStation = xpStationClient.getXpStationById(endPoint.getStationId()).getData();
|
parkingTimeLimit = xpStation.getParkingTimeLimit();
|
}
|
// 计算直线距离,如果endPoint到startPoint的距离大于minOrderDistance,则返回该点
|
GlobalCoordinates endCoordinates = new GlobalCoordinates(endPoint.getPointLon(), endPoint.getPointLat());
|
double distanceMeter = GeodesyUtil.getDistanceMeter(startCoordinates, endCoordinates, Ellipsoid.WGS84);
|
if (distanceMeter > minOrderDistance) {
|
NearestNextVo ret = new NearestNextVo();
|
ret.setId(endPoint.getId());
|
ret.setName(endPoint.getPointName());
|
ret.setLatitude(endPoint.getPointLat());
|
ret.setLongitude(endPoint.getPointLon());
|
ret.setDistance(new BigDecimal(distanceMeter).setScale(2, RoundingMode.HALF_UP).doubleValue());
|
ret.setAddress(endPoint.getPointAddress());
|
ret.setTime(parkingTimeLimit);
|
ret.setElectronicAreaId(endPoint.getElectronicAreaId());
|
// 判断是否收藏
|
if (pointIdsByUserId.contains(endPoint.getId())) {
|
ret.setIsCollect(1);
|
}
|
return ret;
|
}
|
}
|
return null;
|
}
|
|
|
private RecommendationCandidate buildSceneCandidate(List<XpOrder> recentOrders, LocalDateTime now, XpPoint startPoint, Integer minOrderDistance) {
|
DaySceneType currentSceneType = resolveDaySceneType(now.toLocalDate());
|
Set<Integer> targetHours = buildTargetHours(now.getHour());
|
List<XpOrder> sceneOrders = recentOrders.stream()
|
.filter(order -> isSameScene(order, currentSceneType, targetHours))
|
.collect(Collectors.toList());
|
List<RecommendationCandidate> 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<XpOrder> recentOrders, XpPoint startPoint, Integer minOrderDistance) {
|
List<RecommendationCandidate> candidates = buildCandidates(recentOrders, startPoint, minOrderDistance);
|
if (candidates.isEmpty()) {
|
return null;
|
}
|
return candidates.get(0);
|
}
|
|
private List<RecommendationCandidate> buildCandidates(List<XpOrder> orders, XpPoint startPoint, Integer minOrderDistance) {
|
if (CollectionUtils.isEmpty(orders)) {
|
return Collections.emptyList();
|
}
|
Map<Long, RecommendationCandidate> 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<Integer> result = xpSystemSettingClient.getMinOrderDistance();
|
if (result == null || result.getCode() != R.SUCCESS || result.getData() == null) {
|
log.info("获取用户快捷胶囊失败,最小下单距离配置异常,uid:{}, startPointId:{}, result:{}", uid, startPointId, JSON.toJSONString(result));
|
return null;
|
}
|
return result.getData();
|
}
|
|
private XpPoint getPointById(Long pointId) {
|
if (pointId == null) {
|
return null;
|
}
|
R<XpPoint> 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<String, Double> 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<Integer> 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<Integer> buildTargetHours(int currentHour) {
|
Set<Integer> 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<XpHistoryStartPointsVo> getHistoryStartPoints(String uid, double lon, double lat, Long electronicAreaId) {
|
//
|
List<XpOrder> xpOrders = this.baseMapper.getHistoryStartPoints(Long.valueOf(uid), electronicAreaId);
|
// selectList(new LambdaQueryWrapper<XpOrder>().eq(XpOrder::getAppUserId, Long.parseLong(uid)).orderByDesc(BaseModel::getCreateTime).last("LIMIT 10"));
|
if (xpOrders.isEmpty()) {
|
return Collections.emptyList();
|
}
|
// 获取点位信息
|
List<Long> startPointsIds = xpOrders.stream().map(XpOrder::getStartPointId).collect(Collectors.toList());
|
|
R<List<XpHistoryStartPointsVo>> r = xpPointClient.getHistoryStartPoints(new HistoryStartPointsDto(startPointsIds, lon, lat, Long.valueOf(uid)));
|
|
|
List<XpHistoryStartPointsVo> 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());
|
}
|
);
|
}
|
|
List<XpHistoryStartPointsVo> objects = new ArrayList<XpHistoryStartPointsVo>();
|
if(!data.isEmpty()){
|
//收藏的数据
|
List<XpHistoryStartPointsVo> collect = data.stream().filter(s -> s.getIsCollect() == 1)
|
.sorted(Comparator.comparing(XpHistoryStartPointsVo::getDistance)).collect(Collectors.toList());
|
objects.addAll(collect);
|
//非收藏的数据
|
List<XpHistoryStartPointsVo> collect1 = data.stream().filter(s -> s.getIsCollect() == 2)
|
.sorted(Comparator.comparing(XpHistoryStartPointsVo::getDistance)).collect(Collectors.toList());
|
objects.addAll(collect1);
|
}
|
return objects;
|
}
|
|
@Override
|
public List<XpHistoryEndPointsVo> getHistoryEndPoints(String uid, double lon, double lat, Long electronicAreaId) {
|
//
|
List<XpOrder> xpOrders = this.baseMapper.getHistoryEndPoints(Long.valueOf(uid), electronicAreaId);
|
// selectList(new LambdaQueryWrapper<XpOrder>().eq(XpOrder::getAppUserId, Long.parseLong(uid)).orderByDesc(BaseModel::getCreateTime).last("LIMIT 10"));
|
if (xpOrders.isEmpty()) {
|
return Collections.emptyList();
|
}
|
// 获取点位信息
|
List<Long> endPointsIds = xpOrders.stream().map(XpOrder::getEndPointId).collect(Collectors.toList());
|
|
R<List<XpHistoryEndPointsVo>> r = xpPointClient.getHistoryEndPoints(new HistoryEndPointsDto(endPointsIds, lon, lat, Long.valueOf(uid)));
|
|
List<XpHistoryEndPointsVo> 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());
|
}
|
);
|
}
|
List<XpHistoryEndPointsVo> objects = new ArrayList<XpHistoryEndPointsVo>();
|
if(!data.isEmpty()){
|
//收藏的数据
|
List<XpHistoryEndPointsVo> collect = data.stream().filter(s -> s.getIsCollect() == 1)
|
.sorted(Comparator.comparing(XpHistoryEndPointsVo::getDistance)).collect(Collectors.toList());
|
objects.addAll(collect);
|
//非收藏的数据
|
List<XpHistoryEndPointsVo> collect1 = data.stream().filter(s -> s.getIsCollect() == 2)
|
.sorted(Comparator.comparing(XpHistoryEndPointsVo::getDistance)).collect(Collectors.toList());
|
objects.addAll(collect1);
|
}
|
return objects;
|
|
}
|
|
/**
|
* 获取历史订单列表
|
*
|
* @param query
|
* @param uid
|
* @return
|
*/
|
@Override
|
public XpOrderHistoryVO getHistoryOrderList(XpHistoryOrderListQuery query, String uid) {
|
Page<XpOrderHistoryListVO> page = new Page<>(query.getPageNum(), query.getPageSize());
|
List<XpOrderHistoryListVO> list = this.baseMapper.getHistoryOrderList(page, query, Long.parseLong(uid));
|
XpOrderHistoryVO xpOrderHistoryVO = new XpOrderHistoryVO();
|
if (list.isEmpty()) {
|
return xpOrderHistoryVO;
|
}
|
|
|
Set<Long> points = list.stream().map(XpOrderHistoryListVO::getStartPointId).collect(Collectors.toSet());
|
points.addAll(list.stream().map(XpOrderHistoryListVO::getEndPointId).collect(Collectors.toSet()));
|
|
|
// 查询 点位信息
|
R<List<XpPoint>> pointInfoVos = xpPointClient.getPointsInfo(points);
|
List<XpPoint> pointList = pointInfoVos.getData();
|
xpOrderHistoryVO.setTotal((int) page.getTotal());
|
|
// 根据time分组
|
Map<String, List<XpOrderHistoryListVO>> map = list.stream().collect(Collectors.groupingBy(XpOrderHistoryListVO::getTimeStr));
|
List<XpOrderHistoryDateVO> dateVOS = map.entrySet().stream().map(entry -> {
|
XpOrderHistoryDateVO xpOrderHistoryDateVO = new XpOrderHistoryDateVO();
|
xpOrderHistoryDateVO.setDateLabel(entry.getKey());
|
List<XpOrderHistoryListVO> 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) {
|
Integer parkingTimeLimit = xpPoint.getParkingTimeLimit();
|
//如果点位位于场站内,则使用场站的限停时间
|
if(null != xpPoint.getStationId()){
|
XpStation xpStation = xpStationClient.getXpStationById(xpPoint.getStationId()).getData();
|
parkingTimeLimit = xpStation.getParkingTimeLimit();
|
}
|
xpOrderHistoryListVO.setStartPointName(xpPoint.getPointName());
|
xpOrderHistoryListVO.setStartLat(xpPoint.getPointLat());
|
xpOrderHistoryListVO.setStartLon(xpPoint.getPointLon());
|
xpOrderHistoryListVO.setXpElectronicFenceAreaId(xpPoint.getElectronicAreaId());
|
xpOrderHistoryListVO.setStartParkingTimeLimit(parkingTimeLimit);
|
}
|
|
xpPoint = pointList.stream().filter(e -> e.getId().equals(xpOrderHistoryListVO.getEndPointId())).findFirst().orElse(null);
|
if (xpPoint != null) {
|
Integer parkingTimeLimit = xpPoint.getParkingTimeLimit();
|
//如果点位位于场站内,则使用场站的限停时间
|
if(null != xpPoint.getStationId()){
|
XpStation xpStation = xpStationClient.getXpStationById(xpPoint.getStationId()).getData();
|
parkingTimeLimit = xpStation.getParkingTimeLimit();
|
}
|
xpOrderHistoryListVO.setEndPointName(xpPoint.getPointName());
|
xpOrderHistoryListVO.setEndLat(xpPoint.getPointLat());
|
xpOrderHistoryListVO.setEndLon(xpPoint.getPointLon());
|
xpOrderHistoryListVO.setXpElectronicFenceAreaId(xpPoint.getElectronicAreaId());
|
xpOrderHistoryListVO.setEndParkingTimeLimit(parkingTimeLimit);
|
}
|
}
|
|
|
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().equals(Long.valueOf(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();
|
}
|
Integer status = order.getStatus();
|
|
order.setStatus(0);
|
Long appUserId = order.getAppUserId();
|
order.setAppUserId(null);
|
baseMapper.updateById(order);
|
order.setAppUserId(appUserId);
|
bootCommonService.setCache_orderStatus(order.getId(), order, null);
|
|
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());
|
|
if(null == order.getOfficialPartnerId()){
|
//判断还原车辆状态
|
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<XpVehicle> xpVehicleR = xpVehicleClient.queryById(order.getVehicleId());
|
XpVehicle xpVehicle = xpVehicleR.getData();
|
if (xpVehicle == null) {
|
throw new ServiceException("车辆不存在", I18nKeyConstant.VEHICLE_NOT_EXISTS);
|
}
|
//是否能取消行程
|
String vin = SensitiveDataCryptoUtil.decryptIfPossible(xpVehicle.getVehicleVin());
|
boolean cancelShow = this.isCancelShow(order, vin);
|
if (cancelShow) {
|
throw new ServiceException("当前订单状态不允许取消,行程即将完成", I18nKeyConstant.ORDER_CANCEL_FAILED);
|
}
|
|
// 组装调用车机参数
|
CancelTaxiPassengerVo cancelTaxiPassengerVo = new CancelTaxiPassengerVo();
|
cancelTaxiPassengerVo.setVin(vin);
|
cancelTaxiPassengerVo.setUid(uid);
|
cancelTaxiPassengerVo.setOrderNo(order.getOrderNumber());
|
cancelTaxiPassengerVo.setAppId(appid);
|
|
BaseVehicleUserDto baseVehicleUserDto = userVehicleClient.cancelTaxiPassenger(cancelTaxiPassengerVo);
|
if (baseVehicleUserDto == null) {
|
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());
|
}
|
|
//删除蓝牙秘钥
|
String token = redisService.getCacheObject("rt:bluetooth:unlock:order:" + order.getId());
|
if (StringUtils.isNotBlank(token)) {
|
redisService.deleteObject("rt:bluetooth:unlock:" + token);
|
redisService.deleteObject("rt:bluetooth:unlock:order:" + order.getId());
|
BluetoothDeliverVo vo = new BluetoothDeliverVo();
|
vo.setCredential(token);
|
vo.setVin(vin);
|
vo.setPassengerUid(order.getAppUserId());
|
BaseVehicleUserDto delete = bluetoothUnlockClient.delete(vo);
|
if (null == delete || delete.getCode() != 0) {
|
log.error("删除蓝牙解锁失败:{} {}", delete.getMsg(), order.getOrderNumber());
|
}
|
}
|
|
//车辆继续巡游
|
addCruiseMission(order, status);
|
}
|
}else{
|
//流量接入-订单推送 推送取消订单
|
officialPartnerUtil.pushCancelOrder(order.getOfficialPartnerId(), xpOrderCancel, order.getStatus());
|
}
|
}
|
|
@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);
|
order.setAppUserId(appUserId);
|
bootCommonService.setCache_orderStatus(order.getId(), order, null);
|
if(null == order.getOfficialPartnerId()){
|
//删除蓝牙秘钥
|
String token = redisService.getCacheObject("rt:bluetooth:unlock:order:" + order.getId());
|
if (StringUtils.isNotBlank(token)) {
|
redisService.deleteObject("rt:bluetooth:unlock:" + token);
|
redisService.deleteObject("rt:bluetooth:unlock:order:" + order.getId());
|
XpVehicle xpVehicle = xpVehicleClient.queryById(order.getVehicleId()).getData();
|
String vin = SensitiveDataCryptoUtil.decryptIfPossible(xpVehicle.getVehicleVin());
|
BluetoothDeliverVo vo = new BluetoothDeliverVo();
|
vo.setCredential(token);
|
vo.setVin(vin);
|
vo.setPassengerUid(order.getAppUserId());
|
BaseVehicleUserDto delete = bluetoothUnlockClient.delete(vo);
|
if (null == delete || delete.getCode() != 0) {
|
log.error("删除蓝牙解锁失败:{} {}", delete.getMsg(), order.getOrderNumber());
|
}
|
}
|
}
|
|
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<String, Object> params = new HashMap<>();
|
Map<String, Object> 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<String> 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<String> 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());
|
|
//流量接入-订单推送 推送取消订单
|
if(Objects.nonNull(order.getOfficialPartnerId())){
|
officialPartnerUtil.pushCancelOrder(order.getOfficialPartnerId(), xpOrderCancel, order.getStatus());
|
}
|
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);
|
bootCommonService.setCache_orderStatus(xpOrder.getId(), xpOrder, null);
|
|
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<String> 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();
|
String vin = SensitiveDataCryptoUtil.decryptIfPossible(xpVehicle.getVehicleVin());
|
VehiclePosition vehiclePosition = mongoTemplate.findOne(new Query(Criteria.where("vin").is(vin)), VehiclePosition.class);
|
// 订单的状态 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();
|
Integer parkingTimeLimit = xpPoint.getParkingTimeLimit();
|
//如果点位位于场站内,则使用场站的限停时间
|
if(null != xpPoint.getStationId()){
|
XpStation xpStation = xpStationClient.getXpStationById(xpPoint.getStationId()).getData();
|
parkingTimeLimit = xpStation.getParkingTimeLimit();
|
}
|
// 添加取消订单定时任务job 时间为点位限停时长
|
Map<String, ? extends Object> cancelJobMaps =
|
new ImmutableMap.Builder<String, Long>().
|
put("id", xpOrder.getId())
|
.build();
|
QuartzManager.addJob(
|
OrderCancelJob.class,
|
(OrderCancelJob.name + xpOrder.getId()).toUpperCase(),
|
TimeJobType.ORDER_CANCEL,
|
DateUtils.localDateTimeToDate(LocalDateTime.now().plusMinutes(parkingTimeLimit)),
|
cancelJobMaps
|
);
|
// 车辆到达上车点时间
|
xpOrder.setArrivalTime(LocalDateTime.now());
|
break;
|
case 5:
|
LocalDateTime now = LocalDateTime.now();
|
xpOrder.setBoardingTime(now);
|
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.setBoardingLon(x);
|
xpOrder.setBoardingLat(y);
|
xpOrder.setBoardingAddress(geocode.get("address"));
|
}
|
}
|
xpOrder.setRideTime(now);
|
//推送行起点到终点的驶轨迹给乘客端
|
sendRoute(appUserId.toString(), orderId, "ORDER_NAVIGATION_END_DATA", vin);
|
// 删除定时任务job
|
QuartzManager.removeJob((OrderCancelJob.name + xpOrder.getId()).toUpperCase(), TimeJobType.ORDER_CANCEL);
|
//乘客已上车,重新倒计时不开始行程取消订单
|
XpPoint xpPoint1 = xpPointClient.queryById(xpOrder.getStartPointId()).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(
|
OrderCancelJob.class,
|
(OrderCancelJob.name + xpOrder.getId()).toUpperCase(),
|
TimeJobType.ORDER_CANCEL,
|
DateUtils.localDateTimeToDate(LocalDateTime.now().plusMinutes(tradingStrategy.getBdNotBoardedTime())),
|
cancelJobMaps1
|
);
|
}
|
break;
|
case 6:
|
// 删除定时任务job
|
QuartzManager.removeJob((OrderCancelJob.name + xpOrder.getId()).toUpperCase(), TimeJobType.ORDER_CANCEL);
|
// 行程开始时间
|
xpOrder.setTripBegin(LocalDateTime.now());
|
//添加预估到达时间
|
String cacheObject = redisService.getCacheObject("ORDER_NAVIGATION_END_DATA:" + xpOrder.getAppUserId());
|
if (StringUtils.isNotBlank(cacheObject)) {
|
RouteVo routeVo = JSONObject.parseObject(cacheObject, RouteVo.class);
|
xpOrder.setEstimatedArrivalTime(LocalDateTime.now().plusSeconds(routeVo.getDuration()));
|
}
|
redisService.deleteObject("ORDER_NAVIGATION_START_DATA:" + xpOrder.getAppUserId());
|
break;
|
case 7:
|
xpOrder.setGetoffTime(LocalDateTime.now());
|
// 解析下车地点
|
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());
|
|
// 推送倒计时消息
|
Map<String, Object> params = new HashMap<>();
|
Map<String, Object> 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<String> 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<String> 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<String, ? extends Object> endTripJobMaps =
|
new ImmutableMap.Builder<String, String>()
|
.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();
|
Integer parkingTimeLimit2 = point.getParkingTimeLimit();
|
//如果点位位于场站内,则使用场站的限停时间
|
if(null != point.getStationId()){
|
XpStation xpStation = xpStationClient.getXpStationById(point.getStationId()).getData();
|
parkingTimeLimit2 = xpStation.getParkingTimeLimit();
|
}
|
Map<String, ? extends Object> jobMaps =
|
new ImmutableMap.Builder<String, String>()
|
.put("id", xpOrder.getId().toString())
|
.put("appid", appid)
|
.put("orderType", OrderType.USER_ORDER)
|
.build();
|
|
//判断是否有连环单
|
String json = redisService.getCacheObject(CacheConstants.nextOrder(xpOrder.getId().toString()));
|
if (StringUtils.isNotEmpty(json)) {
|
JSONObject jsonObject = JSON.parseObject(json);
|
Long id = jsonObject.getLong("orderId");
|
//判断订单状态
|
XpOrder xpOrder1 = this.getById(id);
|
if(2 == xpOrder1.getStatus()){
|
jobMaps = new ImmutableMap.Builder<String, String>()
|
.put("id", id.toString())
|
.put("oldOrderId", xpOrder.getId().toString())
|
.put("appid", appid)
|
.put("orderType", OrderType.USER_ORDER)
|
.build();
|
|
QuartzManager.addJob(
|
PushChainReactionOrderJob.class,
|
(PushChainReactionOrderJob.name + id).toUpperCase(),
|
TimeJobType.ADD_CHAIN_REACTION_ORDER_MISSION,
|
DateUtils.localDateTimeToDate(LocalDateTime.now().plusMinutes(parkingTimeLimit2)),
|
jobMaps
|
);
|
}
|
//连环派订单取消的情况,继续走巡游
|
if(0 == xpOrder1.getStatus()){
|
QuartzManager.addJob(
|
AddCruiseMissionJob.class,
|
(AddCruiseMissionJob.name + xpOrder.getId()).toUpperCase(),
|
TimeJobType.ADD_CRUISE_MISSION,
|
DateUtils.localDateTimeToDate(LocalDateTime.now().plusMinutes(parkingTimeLimit2)),
|
jobMaps
|
);
|
}
|
} else {
|
QuartzManager.addJob(
|
AddCruiseMissionJob.class,
|
(AddCruiseMissionJob.name + xpOrder.getId()).toUpperCase(),
|
TimeJobType.ADD_CRUISE_MISSION,
|
DateUtils.localDateTimeToDate(LocalDateTime.now().plusMinutes(parkingTimeLimit2)),
|
jobMaps
|
);
|
}
|
break;
|
default:
|
break;
|
}
|
xpOrder.setStatus(status);
|
xpOrder.setAppUserId(null);
|
this.updateById(xpOrder);
|
bootCommonService.setCache_orderStatus(xpOrder.getId(), xpOrder, null);
|
return R.ok();
|
}
|
|
|
/**
|
* 行驶中车辆异常,订单状态修改- 修改状态=7 再修改 金额-再将状态修改成8
|
*/
|
@Override
|
public R<String> 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();
|
String vin = SensitiveDataCryptoUtil.decryptIfPossible(xpVehicle.getVehicleVin());
|
Long appUserId = xpOrder.getAppUserId();
|
xpOrder.setCarAbnormal(true);//车辆是否故障 0-否 1-是
|
xpOrder.setGetoffTime(LocalDateTime.now());
|
// 解析下车地点
|
VehiclePosition vehiclePosition = mongoTemplate.findOne(new Query(Criteria.where("vin").is(vin)), 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());
|
|
xpOrder.setStatus(OrderStatusEnum.SEE_OFF_GUESTS.getCode());//待客下车
|
xpOrder.setAppUserId(null);
|
this.updateById(xpOrder);
|
//结束行程,修改状态和计算费用
|
this.endTrip(xpOrder.getId(), appUserId.toString());
|
|
//将支付金额修改成0 并将状态修改成 订单状态9
|
setFree(xpOrder.getId());
|
return R.ok();
|
}
|
|
/**
|
* 豁免
|
*/
|
public void setFree(Long id) {
|
|
//将支付金额修改成0 并将状态修改成 订单状态9
|
XpOrder xpOrder1 = new XpOrder();
|
xpOrder1.setId(id);
|
xpOrder1.setIsFree(true);//豁免
|
xpOrder1.setPayMoney(BigDecimal.ZERO);
|
xpOrder1.setStatus(OrderStatusEnum.PENDING_PAYMENT.getCode());
|
xpOrder1.setUpdateTime(LocalDateTime.now());
|
baseMapper.updateById(xpOrder1);
|
bootCommonService.setCache_orderStatus(xpOrder1.getId(), xpOrder1, null);
|
|
}
|
|
|
@Override
|
public Boolean endTrip(Long orderId, String uid) {
|
List<XpOrderDetail> xpOrderDetails = new ArrayList<>();
|
// 起步价格明细
|
List<FareBreakdownDetailVo> start = new ArrayList<>();
|
// 里程费价格明细
|
List<FareBreakdownDetailVo> mileage = new ArrayList<>();
|
// 时长费价格明细
|
List<FareBreakdownDetailVo> duration = new ArrayList<>();
|
// 远途费价格明细
|
List<FareBreakdownDetailVo> longDistance = new ArrayList<>();
|
XpOrder order = this.getById(orderId);
|
if (order == null) {
|
throw new RuntimeException("订单不存在");
|
}
|
|
if (order.getStatus() != 7) {
|
throw new RuntimeException("订单状态错误");
|
}
|
order.setStatus(8);
|
Long appUserId = order.getAppUserId();
|
order.setAppUserId(null);
|
order.setTripEnd(LocalDateTime.now());
|
baseMapper.updateById(order);
|
order.setAppUserId(appUserId);
|
bootCommonService.setCache_orderStatus(order.getId(), order, null);
|
//删除蓝牙秘钥
|
String token = redisService.getCacheObject("rt:bluetooth:unlock:order:" + orderId);
|
if(StringUtils.isNotBlank(token)){
|
redisService.deleteObject("rt:bluetooth:unlock:" + token);
|
redisService.deleteObject("rt:bluetooth:unlock:order:" + orderId);
|
XpVehicle xpVehicle = xpVehicleClient.queryById(order.getVehicleId()).getData();
|
String vin = SensitiveDataCryptoUtil.decryptIfPossible(xpVehicle.getVehicleVin());
|
BluetoothDeliverVo vo = new BluetoothDeliverVo();
|
vo.setCredential(token);
|
vo.setVin(vin);
|
vo.setPassengerUid(order.getAppUserId());
|
BaseVehicleUserDto delete = bluetoothUnlockClient.delete(vo);
|
if(null == delete || delete.getCode() != 200){
|
log.error("删除蓝牙解锁失败:{} {}", delete.getMsg(), order.getOrderNumber());
|
}
|
}
|
|
// 根据订单所属开通城市id 查询定价
|
XpPricing pricing = bootCommonService.getXpPricing(order.getCityId(), order.getVehicleId());
|
if (pricing == null) {
|
log.info("计算价格-获取定价规则失败 {} {}", orderId, order.getCityId());
|
throw new ServiceException("当前城市未配置定价规则", I18nKeyConstant.ORDER_PRICING_NOT_EXISTS);
|
}
|
log.info("计算价格-获取定价规则成功 {} {}", orderId, JSON.toJSONString(pricing));
|
|
|
// 费用
|
// 查询订单轨迹
|
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);
|
}
|
log.info("计算价格-获取导航规则 {} {}", orderId, JSON.toJSONString(gpsPoints));
|
|
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());
|
log.info("计算价格-计算一口价结果 {} {}", orderId, JSON.toJSONString(xpPricingCardVo));
|
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);
|
//时长费不是必填项
|
if (null != xpPricingCardVo.getDurationPrice() && xpPricingCardVo.getDurationPrice() > 0) {
|
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);
|
}
|
log.info("计算价格-保存费用明细 {} {}", orderId, JSON.toJSONString(xpOrderDetails));
|
|
// 默认费率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();
|
}
|
|
// 天气预报动态调价
|
JSONArray ws = jsonObject.getJSONArray("weatherRules");
|
if (null != ws) {
|
WeatherDTO data = xpMapClient.wether(order.getCityCode()).getData();
|
List<WeatherDTO.Lives> lives = data.getLives();
|
if (lives != null && !lives.isEmpty()) {
|
String w = lives.get(0).getWeather();
|
for (Object o : ws) {
|
JSONObject ele = JSONObject.parseObject(o.toString());
|
if (w.contains(ele.getString("weatherType")) || ele.getString("weatherType").contains(w)) {
|
double floatRate = ele.getDouble("ratio");
|
priceRate = new BigDecimal(priceRate).add(new BigDecimal(floatRate)).doubleValue();
|
break;
|
}
|
}
|
}
|
}
|
|
|
// 冷却时间
|
Integer time = getOrderNum(jsonObject, 6);
|
// 触发调价了
|
redisService.setCacheObject("USER_DYNAMIC_PRICE_" + order.getCityId() + "_" + uid, priceRate, time.longValue(), TimeUnit.MINUTES);
|
}
|
}
|
}
|
}
|
log.info("计算价格-计算费率 {} {}", orderId, priceRate);
|
order.setOrderMoney(totalPrice.multiply(new BigDecimal(priceRate)));
|
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<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() + "_" + 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();
|
}
|
|
// 天气预报动态调价
|
JSONArray ws = jsonObject.getJSONArray("weatherRules");
|
if (null != ws) {
|
WeatherDTO data = xpMapClient.wether(order.getCityCode()).getData();
|
List<WeatherDTO.Lives> lives = data.getLives();
|
if (lives != null && !lives.isEmpty()) {
|
String w = lives.get(0).getWeather();
|
for (Object o : ws) {
|
JSONObject ele = JSONObject.parseObject(o.toString());
|
if (w.contains(ele.getString("weatherType")) || ele.getString("weatherType").contains(w)) {
|
double floatRate = ele.getDouble("ratio");
|
priceRate = new BigDecimal(priceRate).add(new BigDecimal(floatRate)).doubleValue();
|
break;
|
}
|
}
|
}
|
}
|
|
|
// 冷却时间
|
Integer time = getOrderNum(jsonObject, 6);
|
// 触发调价了
|
redisService.setCacheObject("USER_DYNAMIC_PRICE_" + order.getCityId() + "_" + uid, priceRate, time.longValue(), TimeUnit.MINUTES);
|
}
|
}
|
}
|
}
|
log.info("计算价格-计算费率 {} {}", orderId, priceRate);
|
// 封装费用明细数据
|
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) {
|
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);
|
}
|
log.info("计算价格-计算费用明细 {} {}", orderId, JSON.toJSONString(xpOrderDetails));
|
|
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<UserCouponVo> 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
|
*/
|
@Override
|
public void addCruiseMission(Long vehicleId, XpOrder order) {
|
XpVehicle vehicle = xpVehicleClient.queryById(vehicleId).getData();
|
String vin = SensitiveDataCryptoUtil.decryptIfPossible(vehicle.getVehicleVin());
|
log.info("添加巡游订单 {}", vehicle.getPlateNumber());
|
if (vehicle.getState() != 0 || vehicle.getOperatingStatus() != 1) {
|
log.info("添加巡游单失败,车辆状态限制 {}", vehicle.getState());
|
return;
|
}
|
// 判断是否有计划
|
R<XpCruisePlanVehicle> xpCruisePlanR = xpCruisePlanClient.getCruisePlan(vehicle.getId());
|
if (xpCruisePlanR == null || xpCruisePlanR.getData() == null) {
|
log.info("添加巡游单失败,没有符合条件的巡游计划", vehicle.getPlateNumber());
|
return;
|
}
|
// 获取配置 根据配置判断是否该收车 如果是则找到收车点位进行收车操作
|
R<XpTaskRule> xpTaskRuleR = xpTaskRuleClient.queryDetail();
|
XpTaskRule xpTaskRule = xpTaskRuleR.getData();
|
|
// 生成新的巡游单子 回到最新巡游的初始起点位置
|
XpCruiseOrder xpCruiseOrders = xpCruiseOrderMapper.selectOne(new LambdaQueryWrapper<XpCruiseOrder>().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<XpCruiseOrder>().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(vin);
|
xpCruiseOrder.setOrderType(2);
|
// 返程起点是当前正常订单的终点
|
xpCruiseOrder.setStartPointId(order.getEndPointId());
|
xpCruiseOrder.setStartPointName(order.getEndPointName());
|
xpCruiseOrder.setStartLon(order.getEndLon());
|
xpCruiseOrder.setStartLat(order.getEndLat());
|
xpCruiseOrder.setXpuTaskId(snowflake.nextId() + "");
|
|
// 完成的直接走终点 终止的走起点
|
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<XpCruisePlanPoint> 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<XpCruiseOrder> 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.setEndTaskId(xpCruiseOrder.getXpuTaskId());
|
pushOrderDto.setVin(xpCruiseOrder.getVehicleVin());
|
pushOrderDto.setOrderId("XU" + xpCruiseOrder.getId());
|
pushOrderDto.setStartWaypointId(xpCruiseOrder.getStartPointId());
|
pushOrderDto.setEndWaypointId(xpCruiseOrder.getEndPointId());
|
pushOrderDto.setStartLatitude(xpCruiseOrder.getStartLat());
|
pushOrderDto.setStartLongitude(xpCruiseOrder.getStartLon());
|
pushOrderDto.setEndLatitude(xpCruiseOrder.getEndLat());
|
pushOrderDto.setEndLongitude(xpCruiseOrder.getEndLon());
|
pushOrderDto.setOrderType(OrderType.CRUISE_ORDER);
|
pushOrderDto.setStage(2);
|
if(null != order){
|
pushOrderDto.setLastOrderId(order.getId() + "");
|
pushOrderDto.setLastXpuTaskId(order.getXpuEndTaskId());
|
}
|
pushOrderDto.setTaskActionType(TaskActionType.kTerminateForNewTask);
|
orderMissionClient.pushOrder(pushOrderDto);
|
log.info("下发XUP巡游订单报文成功");
|
}
|
|
|
/**
|
* 取消订单后的继续巡游任务
|
*/
|
public void addCruiseMission(XpOrder order, Integer status) {
|
XpVehicle vehicle = xpVehicleClient.queryById(order.getVehicleId()).getData();
|
if (vehicle.getState() != 0 || vehicle.getOperatingStatus() != 1) {
|
return;
|
}
|
// 判断是否有计划
|
R<XpCruisePlanVehicle> xpCruisePlanR = xpCruisePlanClient.getCruisePlan(vehicle.getId());
|
if (xpCruisePlanR == null || xpCruisePlanR.getData() == null) {
|
log.info("未找到匹配的巡游计划,无法执行巡游订单 {}", vehicle.getPlateNumber());
|
log.info("给车端发送取消行程报文 {}", vehicle.getPlateNumber());
|
|
PushOrderDto cancelDto = new PushOrderDto();
|
cancelDto.setStartTaskId(order.getXpuStartTaskId());
|
cancelDto.setEndTaskId(order.getXpuEndTaskId());
|
cancelDto.setOrderId(order.getId().toString());
|
String vin1 = SensitiveDataCryptoUtil.decryptIfPossible(vehicle.getVehicleVin());
|
cancelDto.setVin(vin1);
|
cancelDto.setOperation_type(OrderOperationType.CLOUD_CANCEL_ORDER);
|
cancelDto.setCancelScene(CancelOrderSceneEnum.REASSIGN_OLD_VEHICLE.getCode());
|
orderMissionClient.cancelOrder(cancelDto);
|
return;
|
}
|
String vin = SensitiveDataCryptoUtil.decryptIfPossible(vehicle.getVehicleVin());
|
VehiclePosition vehiclePosition = mongoTemplate.findOne(new Query(Criteria.where("vin").is(vin)), VehiclePosition.class);
|
// 获取配置 根据配置判断是否该收车 如果是则找到收车点位进行收车操作
|
R<XpTaskRule> xpTaskRuleR = xpTaskRuleClient.queryDetail();
|
XpTaskRule xpTaskRule = xpTaskRuleR.getData();
|
|
// 生成新的巡游单子 回到最新巡游的初始起点位置
|
XpCruiseOrder xpCruiseOrders = xpCruiseOrderMapper.selectOne(new LambdaQueryWrapper<XpCruiseOrder>().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<XpCruiseOrder>().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(vin);
|
xpCruiseOrder.setOrderType(2);
|
xpCruiseOrder.setXpuTaskId(snowflake.nextId() + "");
|
// 返程起点是当前正常订单的终点
|
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<XpCruisePlanPoint> 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<XpCruiseOrder> 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.setEndTaskId(xpCruiseOrder.getXpuTaskId());
|
pushOrderDto.setVin(xpCruiseOrder.getVehicleVin());
|
pushOrderDto.setOrderId("XU" + xpCruiseOrder.getId());
|
pushOrderDto.setStartWaypointId(xpCruiseOrder.getStartPointId());
|
pushOrderDto.setEndWaypointId(xpCruiseOrder.getEndPointId());
|
pushOrderDto.setStartLatitude(xpCruiseOrder.getStartLat());
|
pushOrderDto.setStartLongitude(xpCruiseOrder.getStartLon());
|
pushOrderDto.setEndLatitude(xpCruiseOrder.getEndLat());
|
pushOrderDto.setEndLongitude(xpCruiseOrder.getEndLon());
|
pushOrderDto.setOrderType(OrderType.CRUISE_ORDER);
|
pushOrderDto.setStage(2);
|
if(null != order){
|
pushOrderDto.setLastOrderId(order.getId() + "");
|
pushOrderDto.setLastXpuTaskId(status < 5 ? order.getXpuStartTaskId() : order.getXpuEndTaskId());
|
}
|
pushOrderDto.setTaskActionType(status < 5 ? TaskActionType.kTerminateForNewTask : TaskActionType.kWaitPrevTaskFinish);
|
// 调用推送服务 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<OrderTrajectory> 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<FareBreakdownDetailVo> 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<FareBreakdownVo> fareBreakdown(com.xiaopeng.order.vo.FareBreakdownVo vo, String uid) {
|
XpOrder order = this.getById(vo.getOrderId());
|
//根据新的优惠券计算优惠金额
|
if (null != order.getCouponId()) {
|
CalculateDiscountAmountVo discountAmountVo = new CalculateDiscountAmountVo();
|
BigDecimal data = xpAppUserCouponClient.calculateDiscountAmount(discountAmountVo).getData();
|
order.setCouponId(vo.getUserCouponId());
|
order.setCouponMoney(data);
|
}
|
order.setAppUserId(null);
|
FareBreakdownVo res = new FareBreakdownVo();
|
res.setChangeMoneyFlag(order.getChangeMoneyFlag());
|
|
List<FareBreakdownDetailVo> start = new ArrayList<>();
|
List<FareBreakdownDetailVo> mileage = new ArrayList<>();
|
List<FareBreakdownDetailVo> duration = new ArrayList<>();
|
List<FareBreakdownDetailVo> longDistance = new ArrayList<>();
|
|
List<XpOrderDetail> xpOrderDetails = xpOrderDetailMapper.selectList(new LambdaQueryWrapper<XpOrderDetail>().eq(XpOrderDetail::getOrderId, vo.getOrderId()));
|
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.setUserCouponId(order.getCouponId());
|
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));
|
XpSystemSetting xpSystemSetting = xpSystemSettingClient.getSystemSettingOne().getData();
|
if (null != xpSystemSetting) {
|
res.setOperateFree(xpSystemSetting.getOperateFree());
|
}
|
return R.ok(res);
|
}
|
|
|
@Override
|
public R payment(XpOrderPayDto dto, String uid) {
|
|
|
XpOrder order = this.getById(dto.getOrderId());
|
|
HashMap<String, Object> resultMap = new HashMap<>();
|
|
// 1.0 免密 //查询是否开通免密
|
// 1先查用户是否申请 申请了拿到通过的businessOrderId
|
boolean signed = false;
|
CreditPaySignQueryDto creditPaySignQueryDto = new CreditPaySignQueryDto();
|
creditPaySignQueryDto.setUid(Long.valueOf(uid));
|
creditPaySignQueryDto.setBusinessId("xp-robotaxi");
|
creditPaySignQueryDto.setCategory("009001");
|
creditPaySignQueryDto.setBusinessOrderId(OrderCodeUtil.getOrderCode("MMZF"));
|
R<CreditPaySignQueryVo> creditPaySignQueryVoR = xpPayFrontClient.queryCreditPaySign(creditPaySignQueryDto);
|
if (creditPaySignQueryVoR.getCode()==200) {
|
CreditPaySignQueryVo data = creditPaySignQueryVoR.getData();
|
// 查看是否有签约的
|
long count = data.getSignList().stream().filter(e -> e.getStatus().equals("VALID")).count();
|
if (count > 0){
|
signed = true;
|
}
|
}
|
|
|
if(signed){
|
// y已签约 1.信用下单
|
CreditOrderCreateDto creditOrderCreateDto = new CreditOrderCreateDto();
|
creditOrderCreateDto.setUid(Long.valueOf(uid));
|
creditOrderCreateDto.setBusinessId("xp-robotaxi");
|
creditOrderCreateDto.setCategory("009001");
|
creditOrderCreateDto.setBusinessOrderId(OrderCodeUtil.getOrderCode("MMZF"));
|
creditOrderCreateDto.setSubject("小鹏集团|robotaxi");
|
creditOrderCreateDto.setPayType("13");
|
creditOrderCreateDto.setPayCallbackUrl("");
|
|
creditOrderCreateDto.setBusinessExtendData("{\"mainOrderId\":\"" +order.getOrderNumber() + "\"}");
|
|
|
|
// 信用下单
|
ArrayList<CreditOrderCreateDto.PostPayment> postPayments = new ArrayList<>(1);
|
CreditOrderCreateDto.PostPayment postPayment = new CreditOrderCreateDto.PostPayment();
|
postPayment.setName("收费标准");
|
postPayment.setDescription("行程价格动态浮动,具体详情可在费用详情或计价规则查看");
|
postPayments.add(postPayment);
|
creditOrderCreateDto.setPostPayments(postPayments);
|
R<CreditOrderCreateVo> creditOrderCreateVoR = xpPayFrontClient.createCreditOrder(creditOrderCreateDto);
|
if(creditOrderCreateVoR.getCode()==200){
|
CreditOrderCreateVo data = creditOrderCreateVoR.getData();
|
resultMap.put("status", 1);
|
resultMap.put("value", data);
|
|
order.setPayType(3);
|
order.setAuthOrderId(data.getAuthOrderId());
|
order.setBusinessOrderId(creditOrderCreateDto.getBusinessOrderId());
|
order.setMainOrderId(order.getOrderNumber());
|
|
return R.ok(resultMap);
|
}
|
|
}else {
|
|
// 未签约
|
// 调用预下单接口
|
CreditPrecreateDto creditPrecreateDto = new CreditPrecreateDto();
|
creditPrecreateDto.setUid(uid);
|
creditPrecreateDto.setBizId("xp-robotaxi");
|
creditPrecreateDto.setBizOrderId(OrderCodeUtil.getOrderCode("MMZF"));
|
creditPrecreateDto.setPayScene(99);
|
creditPrecreateDto.setAmount(order.getOrderMoney().multiply(new BigDecimal(100)).intValue());
|
creditPrecreateDto.setCategory("009001");
|
creditPrecreateDto.setTitle("信用下单");
|
creditPrecreateDto.setDetail("信用下单");
|
|
HashMap<String, String> businessExtend = new HashMap<>();
|
businessExtend.put("returnUrl", "");
|
businessExtend.put("notifyUrl", "");
|
creditPrecreateDto.setBusinessExtend(businessExtend);
|
|
creditPrecreateDto.setExtBusinessData2("{\"mainOrderId\":\"" +order.getOrderNumber() + "\"}");
|
|
R<CreditPrecreateVo> creditPrecreateVoR = xpPayFrontClient.precreate(creditPrecreateDto);
|
if (creditPrecreateVoR.getCode()==200) {
|
CreditPrecreateVo data = creditPrecreateVoR.getData();
|
resultMap.put("status", 2);
|
resultMap.put("value", data);
|
|
order.setPayType(1);
|
order.setBusinessOrderId(creditPrecreateDto.getBizOrderId());
|
order.setMainOrderId(order.getOrderNumber());
|
this.updateById(order);
|
|
return R.ok(resultMap);
|
}
|
}
|
|
|
|
// 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<BigDecimal> 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);
|
bootCommonService.setCache_orderStatus(order.getId(), order, null);
|
return R.fail();
|
}
|
|
/**
|
* 添加订单
|
*
|
* @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<XpOrder>().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<XpOrder>()
|
.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<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("调用高德计算距离出错");
|
}
|
}
|
}
|
|
@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<OrderTrajectory> trajectories = queryOrderTrajectories(orderId);
|
orderDetailVo.setTrajectories(trajectories);
|
|
// 起步价、里程费、时长费、远途费:按订单费用明细聚合,覆盖订单表字段
|
Map<Integer, BigDecimal> 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<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<XpOrderDetail> detailList = xpOrderDetailMapper.selectList(
|
new LambdaQueryWrapper<XpOrderDetail>()
|
.eq(XpOrderDetail::getOrderId, orderId)
|
.ne(XpOrderDetail::getIsDeleted, true)
|
);
|
if (CollectionUtils.isEmpty(detailList)) {
|
return Collections.emptyMap();
|
}
|
Map<Integer, BigDecimal> 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<XpOrderDetailVO.OrderPosition> convertTrajectoriesToPositions(List<OrderTrajectory> trajectories) {
|
if (CollectionUtils.isEmpty(trajectories)) {
|
return new ArrayList<>();
|
}
|
List<XpOrderDetailVO.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());
|
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<String> feeDescription(Long orderId, String uid) {
|
XpOrder order = this.getById(orderId);
|
XpPricing xpPricing = bootCommonService.getXpPricing(order.getCityId(), order.getVehicleId());
|
if (xpPricing == null) {
|
throw new ServiceException("当前城市未配置定价规则", I18nKeyConstant.ORDER_PRICING_NOT_EXISTS);
|
} else {
|
return R.ok(xpPricing.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<String, Object> 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<XpVehicle> byVehicleVin = xpVehicleClient.findByVehicleVin(dto.getXpVehicleVin());
|
XpVehicle vehicle = null;
|
String plateNumber = null;
|
if (byVehicleVin.getCode() == 200) {
|
vehicle = byVehicleVin.getData();
|
String vin = SensitiveDataCryptoUtil.decryptIfPossible(vehicle.getVehicleVin());
|
order.setVehicleId(vehicle.getId());
|
order.setFleetId(vehicle.getFleetId());
|
plateNumber = vehicle.getPlateNumber();
|
addTaxiPassengerVo.setVin(vin);
|
}
|
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);
|
order.setAppUserId(appUserId);
|
bootCommonService.setCache_orderStatus(order.getId(), order, null);
|
|
//构建推送消息
|
Map<String, Object> params = new HashMap<>();
|
Map<String, Object> 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<String> 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<String> 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(dto1), 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());
|
// origin.setLongitude(104.055499);
|
// origin.setLatitude(30.586747);
|
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<RouteVo> 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());
|
|
//生成蓝牙token
|
//生成过期时间 (接单时间+到达上车点预估时间+上车点等待时间+行程预估时间+下车点等待时间+30分钟)
|
XpPoint startPoint = xpPointClient.queryById(order.getStartPointId()).getData();
|
Object cacheObject = redisService.getCacheObject("ORDER_NAVIGATION_END_DATA:" + appUserId);
|
RouteVo routeVo1 = JSON.parseObject(cacheObject.toString(), RouteVo.class);
|
XpPoint endPoint = xpPointClient.queryById(order.getEndPointId()).getData();
|
Integer parkingTimeLimit = startPoint.getParkingTimeLimit();
|
//如果点位位于场站内,则使用场站的限停时间
|
if(null != startPoint.getStationId()){
|
XpStation xpStation = xpStationClient.getXpStationById(startPoint.getStationId()).getData();
|
parkingTimeLimit = xpStation.getParkingTimeLimit();
|
}
|
|
Integer parkingTimeLimit1 = endPoint.getParkingTimeLimit();
|
//如果点位位于场站内,则使用场站的限停时间
|
if(null != endPoint.getStationId()){
|
XpStation xpStation = xpStationClient.getXpStationById(endPoint.getStationId()).getData();
|
parkingTimeLimit1 = xpStation.getParkingTimeLimit();
|
}
|
LocalDateTime localDateTime = order.getAcceptanceTime().plusSeconds(routeVo.getDuration()).plusMinutes(parkingTimeLimit)
|
.plusSeconds(routeVo1.getDuration()).plusMinutes(parkingTimeLimit1).plusMinutes(30);
|
long expirationTime = localDateTime.toEpochSecond(ZoneOffset.of("+8")) * 1000;
|
//生成token
|
String token = bluetoothUnlockTokenUtil.generateBluetoothUnlockToken(order.getAppUserId(),
|
order.getId(), order.getVehicleId(), expirationTime);
|
log.info("生成蓝牙token:{} 有效期:{} 订单号: {}", token, expirationTime, order.getOrderNumber());
|
//存储缓存
|
String vin = SensitiveDataCryptoUtil.decryptIfPossible(vehicle.getVehicleVin());
|
JSONObject jsonObject = new JSONObject();
|
jsonObject.put("userId", order.getAppUserId());
|
jsonObject.put("orderId", order.getId());
|
jsonObject.put("vehicleId", order.getVehicleId());
|
jsonObject.put("expirationTime", expirationTime);
|
jsonObject.put("orderNumber", order.getOrderNumber());
|
jsonObject.put("vehicleVin", vin);
|
jsonObject.put("createTime", System.currentTimeMillis());
|
redisService.setCacheObject("rt:bluetooth:unlock:" + token, jsonObject.toJSONString(), expirationTime, TimeUnit.SECONDS);
|
redisService.setCacheObject("rt:bluetooth:unlock:order:" + order.getId(), token, expirationTime, TimeUnit.SECONDS);
|
//开始下发蓝牙token
|
BluetoothDeliverVo vo = new BluetoothDeliverVo();
|
vo.setCredential(token);
|
vo.setVin(vin);
|
vo.setPassengerUid(order.getAppUserId());
|
BaseVehicleUserDto deliver = bluetoothUnlockClient.deliver(vo);
|
log.info("下发蓝牙token结果:{}", JSON.toJSONString(deliver));
|
if(null == deliver){
|
throw new ServiceException("下发蓝牙token失败");
|
}
|
if(200 != deliver.getCode()){
|
log.error("下发蓝牙token失败:{} {}", deliver.getMsg(), order.getOrderNumber());
|
throw new ServiceException(deliver.getMsg());
|
}
|
log.info("下发蓝牙token成功:{} {}", deliver.getData(), order.getOrderNumber());
|
}
|
|
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<String, Object> 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<XpVehicle> 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<XpVehicle> 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<JSONObject> 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<String> 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<String> 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<XpOrder>().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<XpPoint> xpPointR = xpPointClient.queryById(xpOrder.getStartPointId());
|
if (xpPointR.getCode() == R.SUCCESS) {
|
XpPoint startPoint = xpPointR.getData();
|
Integer parkingTimeLimit = startPoint.getParkingTimeLimit();
|
//如果点位位于场站内,则使用场站的限停时间
|
if(null != startPoint.getStationId()){
|
XpStation xpStation = xpStationClient.getXpStationById(startPoint.getStationId()).getData();
|
parkingTimeLimit = xpStation.getParkingTimeLimit();
|
}
|
xpOrderDetailVO.setStartParkingTimeLimit(parkingTimeLimit);
|
}
|
|
R<XpPoint> xpPointR1 = xpPointClient.queryById(xpOrder.getEndPointId());
|
if (xpPointR1.getCode() == R.SUCCESS) {
|
XpPoint endPoint = xpPointR1.getData();
|
Integer parkingTimeLimit = endPoint.getParkingTimeLimit();
|
//如果点位位于场站内,则使用场站的限停时间
|
if(null != endPoint.getStationId()){
|
XpStation xpStation = xpStationClient.getXpStationById(endPoint.getStationId()).getData();
|
parkingTimeLimit = xpStation.getParkingTimeLimit();
|
}
|
xpOrderDetailVO.setEndParkingTimeLimit(parkingTimeLimit);
|
}
|
// // 算时间
|
// if (xpOrder.getStatus() == 1) {
|
// R<Integer> 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<XpVehicle> xpVehicleR = xpVehicleClient.queryById(xpOrder.getVehicleId());
|
XpVehicle vehicle = xpVehicleR.getData();
|
String vin = SensitiveDataCryptoUtil.decryptIfPossible(vehicle.getVehicleVin());
|
xpOrderDetailVO.setVehicleVin(vin);
|
xpOrderDetailVO.setVehicleModel(vehicle.getVehicleModelName());
|
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.setOpenTrunk(2);
|
xpOrderDetailVO.setOpenTrunk(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<XpOrder>().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<XpVehicle> xpVehicleR = xpVehicleClient.queryById(xpOrder.getVehicleId());
|
XpVehicle vehicle = xpVehicleR.getData();
|
String vin = SensitiveDataCryptoUtil.decryptIfPossible(vehicle.getVehicleVin());
|
xpOrderDetailVO.setVehicleVin(vin);
|
xpOrderDetailVO.setVehicleModel(vehicle.getVehicleModelName());
|
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;
|
|
//获取系统配置
|
XpSystemSetting systemSetting = xpSystemSettingClient.getSystemSettingOne().getData();
|
if (systemSetting == null) {
|
throw new ServiceException("获取行程配置错误");
|
}
|
//获取配置文件
|
int sylc = systemSetting.getXczSyDistance(); // 行程中剩余里程单位米 需要读取后台配置文件
|
int sysj = systemSetting.getXczSyTime();//行程中剩余时间 单位分钟 需要读取后台配置文件
|
// 减少3分钟 获取3分账内的 行程数据
|
Integer xczSpeedPerHour = systemSetting.getXczHmSpeedPerHour();//行程中豁免时速 km/h
|
Integer xczHmDuration = systemSetting.getXczHmDuration();//行程中豁免持续时间 分钟 需要读取后台配置文件
|
|
|
List<OrderTrajectory> orderTrajectories = queryOrderTrajectories(xpOrder.getId());
|
if (!orderTrajectories.isEmpty()) {// 减少3分钟 获取3分账内的 行程数据
|
//3千米/小时 转换成分钟 50米/分钟
|
BigDecimal suduDefault = new BigDecimal(xczSpeedPerHour);//3千米/小时
|
BigDecimal suduDefault1 = suduDefault.multiply(BigDecimal.valueOf(1000));
|
BigDecimal suduDefaultNew = suduDefault1.divide(BigDecimal.valueOf(60), 2, RoundingMode.HALF_EVEN);
|
|
LocalDateTime minutesBefore = LocalDateTime.now().minusMinutes(xczHmDuration);
|
long milliseconds = DateUtils.getMilliseconds(minutesBefore);
|
List<OrderTrajectory> 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(xczHmDuration), 2, RoundingMode.HALF_EVEN);//米/分钟
|
|
//特殊豁免:若系统检测到车辆在当前坐标时速 < 3km/h 且持续时间 > 3分钟(后台配置)(判定为极度拥堵或异常停滞),此时解除限制,允许用户修改目的地或新增途径点以寻找更近的下车位置。
|
if (xcsudu.compareTo(suduDefaultNew) < 0) {
|
return true;
|
}
|
}
|
|
try {
|
RouteVo routeVo = this.getRouteVo(xpOrder, vehicleVin);
|
if (routeVo.getDistance() < sylc) {
|
isCancel = true;
|
}
|
if (routeVo.getDuration() < sysj) {
|
isCancel = true;
|
}
|
} catch (Exception e) {
|
log.error("获取两个点位车的距离错误", e);
|
throw new ServiceException("获取两个点位车的距离错误");
|
}
|
|
|
return isCancel;
|
}
|
|
/**
|
* 获取两个点位车的距离 和时间
|
*
|
* @param xpOrder
|
* @param vehicleVin
|
* @return
|
*/
|
private RouteVo getRouteVo(XpOrder xpOrder, String vehicleVin) {
|
|
VehiclePosition vehiclePosition = mongoTemplate.findOne(new Query(Criteria.where("vin").is(vehicleVin)), VehiclePosition.class);
|
// 计算距离 和 时间
|
RouteAndFareDto routeAndFareDto = new RouteAndFareDto();
|
LonLatDto lonLatDto = new LonLatDto();
|
lonLatDto.setLongitude(vehiclePosition.getPoint().getX());//经度
|
lonLatDto.setLatitude(vehiclePosition.getPoint().getY());
|
routeAndFareDto.setOrigin(lonLatDto);
|
|
LonLatDto lonLatDto1 = new LonLatDto();
|
lonLatDto1.setLongitude(xpOrder.getEndLon());
|
lonLatDto1.setLatitude(xpOrder.getEndLat());
|
routeAndFareDto.setDestination(lonLatDto1);
|
|
// XpOpenCity xpOpenCity = xpOpenCityMapper.selectById(dto.getOpenCityId());
|
// xpRoutePlanClient.
|
XpRoutePlan xpRoutePlan = xpRoutePlanClient.getXpRoutePlanOpenCityId(xpOrder.getCityId()).getData();
|
if (xpRoutePlan != null) {
|
routeAndFareDto.setRoutePlanRule(xpRoutePlan.getRoutePlanRule());
|
}
|
R<RouteVo> 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<String, Object> params = new HashMap<>();
|
Map<String, Object> 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<String> 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<String> 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<XpOrder>()
|
.eq(XpOrder::getAppUserId, uid)
|
.eq(XpOrder::getStatus, 8)
|
);
|
return num.intValue();
|
}
|
|
|
/**
|
* 根据 UID 查询用户是否有 RT 订单 & 订单列表
|
*
|
* @param dto
|
* @return
|
*/
|
@Override
|
public List<UserOrderListVo> getUserOrderList(UserOrderListDto dto) {
|
List<XpOrder> list = this.list(new QueryWrapper<XpOrder>().eq("app_user_id", dto.getUid())
|
.eq("is_deleted", 0)
|
.eq(dto.getOrderStatus() != null, "status", dto.getOrderStatus())
|
.eq(dto.getOrderId() != null, "id", dto.getOrderId())
|
.last(" order by create_time desc limit " + dto.getPageNo() + "," + dto.getPageSize())
|
);
|
List<UserOrderListVo> collect = list.stream().map(order -> {
|
UserOrderListVo vo = new UserOrderListVo();
|
vo.setOrderId(order.getId());
|
vo.setOrderStatus(order.getStatus());
|
vo.setOrderCreatedAt(order.getCreateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
|
|
XpPoint startPoint = xpPointClient.queryById(order.getStartPointId()).getData();
|
vo.setPickupAddress(null != startPoint ? startPoint.getPointAddress() : null);
|
XpPoint endPoint = xpPointClient.queryById(order.getEndPointId()).getData();
|
vo.setDestAddress(null != endPoint ? endPoint.getPointAddress() : null);
|
vo.setTotalPrice(order.getOrderMoney());
|
return vo;
|
}).collect(Collectors.toList());
|
return collect;
|
}
|
|
|
/**
|
* 获取订单详情
|
*
|
* @param orderId
|
* @return
|
*/
|
@Override
|
public OrderInfoVo getOrderInfo(Long orderId) {
|
XpOrder xpOrder = this.getById(orderId);
|
OrderInfoVo vo = new OrderInfoVo();
|
vo.setOrderId(xpOrder.getId());
|
vo.setOrderNumber(xpOrder.getOrderNumber());
|
vo.setOrderStatus(xpOrder.getStatus());
|
XpPoint startPoint = xpPointClient.queryById(xpOrder.getStartPointId()).getData();
|
vo.setPickupAddress(startPoint.getPointAddress());
|
XpPoint endPoint = xpPointClient.queryById(xpOrder.getEndPointId()).getData();
|
vo.setDestAddress(endPoint.getPointAddress());
|
vo.setPullOverAddress(xpOrder.getGetoffAddress());
|
if (null != xpOrder.getEstimatedArrivalTime()) {
|
vo.setEstimatedArrivalTime(xpOrder.getEstimatedArrivalTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
|
}
|
if (null != xpOrder.getGetoffTime()) {
|
vo.setActualArrivalTime(xpOrder.getGetoffTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
|
}
|
if (null == xpOrder.getMileageKilometers()) {
|
vo.setActualMileage(BigDecimal.valueOf(xpOrder.getMileageKilometers()));
|
}
|
vo.setTotalPrice(xpOrder.getOrderMoney());
|
vo.setStartPrice(xpOrder.getStartMoney());
|
vo.setWaitFee(xpOrder.getWaitMoney());
|
vo.setMileageFee(xpOrder.getMileageMoney());
|
vo.setDurationFee(xpOrder.getDurationMoney());
|
vo.setLongDistanceFee(xpOrder.getLongDistancemoney());
|
vo.setCouponDiscount(xpOrder.getCouponMoney());
|
vo.setActualPayment(xpOrder.getPayMoney());
|
XpVehicle xpVehicle = xpVehicleClient.queryById(xpOrder.getVehicleId()).getData();
|
if (null != xpVehicle) {
|
String vin = SensitiveDataCryptoUtil.decryptIfPossible(xpVehicle.getVehicleVin());
|
vo.setVin(vin);
|
}
|
XpOrderCancel orderCancel = xpOrderCancelService.getOne(new QueryWrapper<XpOrderCancel>().eq("order_id", orderId));
|
if (null != orderCancel) {
|
vo.setCancelReasonType(orderCancel.getCancelReason());
|
}
|
return vo;
|
}
|
|
|
/**
|
*
|
* 合作商更新订单状态
|
*
|
* @param vo
|
*/
|
@Override
|
public R<?> officialPartnerUpdateOrderStatus(UpdateOrderStatusVo vo, Integer officialPartnerId) {
|
XpOrder xpOrder = this.getOne(new QueryWrapper<XpOrder>().eq("order_number", vo.getOrderNum()));
|
if (null == xpOrder) {
|
return R.fail("无效的订单编号");
|
}
|
if (xpOrder.getStatus() >= 8) {
|
return R.fail("订单已结束,无法修改");
|
}
|
|
switch (vo.getOrderStatus()) {
|
case 2:
|
if (xpOrder.getStatus() != 1) {
|
return R.fail("订单状态不允许改为已接单");
|
}
|
if (StringUtils.isEmpty(vo.getVehiclePlateNumber())) {
|
return R.fail("车牌号不能为空");
|
}
|
if (null == vo.getAcceptanceTime()) {
|
return R.fail("接单时间不能为空");
|
}
|
XpVehicle xpVehicle = xpVehicleClient.getVehicleByPlateNumber(vo.getVehiclePlateNumber(), officialPartnerId).getData();
|
if (null == xpVehicle) {
|
return R.fail("车牌号不存在");
|
}
|
xpOrder.setOfficialPartnerId(officialPartnerId);
|
xpOrder.setVehicleId(xpVehicle.getId());
|
xpOrder.setAcceptanceTime(vo.getAcceptanceTime());
|
redisService.setCacheObject("ORDER_STATUS:" + xpOrder.getId(), OrderStatusEnum.READY_DEPART.getCode(), 24L, TimeUnit.HOURS);
|
//将订单弹出队列
|
try {
|
redisService.popFirstElement(xpOrder.getQueueName());
|
} catch (Exception e) {
|
log.error("Redis弹出队列元素出错");
|
}
|
// 存mongo数据
|
xpPricingClient.updateXpGeoQueryEntity(xpOrder.getId().toString());
|
bootCommonService.setCache_orderStatus(xpOrder.getId(), xpOrder, null);
|
|
//生成前往上车点的导航数据
|
//查询车辆位置
|
String vin = SensitiveDataCryptoUtil.decryptIfPossible(xpVehicle.getVehicleVin());
|
VehiclePosition vehiclePosition = mongoTemplate.findOne(new Query(Criteria.where("vin").is(vin)), 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(xpOrder.getStartLon());
|
destination.setLatitude(xpOrder.getStartLat());
|
|
OrderRouteAndFareDto dto2 = new OrderRouteAndFareDto();
|
dto2.setOrderId(xpOrder.getId());
|
dto2.setOrigin(origin);
|
dto2.setDestination(destination);
|
dto2.setRouteType(1);
|
dto2.setVehicleId(xpVehicle.getId());
|
R<RouteVo> routeVoR = xpMapClient.getOrderDistanceAndDuration(dto2);
|
if (200 != routeVoR.getCode()) {
|
throw new RuntimeException("获取订单线路和运费失败");
|
}
|
RouteVo routeVo = routeVoR.getData();
|
//缓存数据,用于后续推给乘客导航线路
|
redisService.setCacheObject("ORDER_NAVIGATION_START_DATA:" + xpOrder.getAppUserId(), JSON.toJSONString(routeVo), 24L, TimeUnit.HOURS);
|
|
//推送乘客车辆到达预约点的导航数据
|
sendRoute(xpOrder.getAppUserId() + "", xpOrder.getId(), "ORDER_NAVIGATION_START_DATA", vin);
|
break;
|
case 3:
|
if (xpOrder.getStatus() != 2) {
|
return R.fail("订单状态不允许改为接驾中");
|
}
|
if (null == vo.getTripBegin()) {
|
return R.fail("行程开始时间不能为空");
|
}
|
xpOrder.setTripBegin(vo.getTripBegin());
|
redisService.setCacheObject("ORDER_STATUS:" + xpOrder.getId(), OrderStatusEnum.PICK_UP.getCode(), 24L, TimeUnit.HOURS);
|
break;
|
case 4:
|
if (xpOrder.getStatus() != 3) {
|
return R.fail("订单状态不允许改为到达上车点");
|
}
|
if (null == vo.getArrivalTime()) {
|
return R.fail("车辆到达上车点时间不能为空");
|
}
|
|
// 查询起点点位
|
Long startPointId = xpOrder.getStartPointId();
|
XpPoint xpPoint = xpPointClient.queryById(startPointId).getData();
|
Integer parkingTimeLimit = xpPoint.getParkingTimeLimit();
|
//如果点位位于场站内,则使用场站的限停时间
|
if(null != xpPoint.getStationId()){
|
XpStation xpStation = xpStationClient.getXpStationById(xpPoint.getStationId()).getData();
|
parkingTimeLimit = xpStation.getParkingTimeLimit();
|
}
|
// 添加取消订单定时任务job 时间为点位限停时长
|
Map<String, ? extends Object> cancelJobMaps =
|
new ImmutableMap.Builder<String, Long>().
|
put("id", xpOrder.getId())
|
.build();
|
QuartzManager.addJob(
|
OrderCancelJob.class,
|
(OrderCancelJob.name + xpOrder.getId()).toUpperCase(),
|
TimeJobType.ORDER_CANCEL,
|
DateUtils.localDateTimeToDate(LocalDateTime.now().plusMinutes(parkingTimeLimit)),
|
cancelJobMaps
|
);
|
|
xpOrder.setArrivalTime(vo.getArrivalTime());
|
redisService.setCacheObject("ORDER_STATUS:" + xpOrder.getId(), OrderStatusEnum.PICK_UP_GUESTS.getCode(), 24L, TimeUnit.HOURS);
|
break;
|
case 5:
|
if (xpOrder.getStatus() != 4) {
|
return R.fail("订单状态不允许改为乘客已上车");
|
}
|
if (null == vo.getBoardingLon()) {
|
return R.fail("上车点经度不能为空");
|
}
|
if (null == vo.getBoardingLat()) {
|
return R.fail("上车点纬度不能为空");
|
}
|
if (StringUtils.isEmpty(vo.getBoardingAddress())) {
|
return R.fail("上车点地址不能为空");
|
}
|
if (null == vo.getBoardingTime()) {
|
return R.fail("上车时间不能为空");
|
}
|
xpOrder.setBoardingLon(vo.getBoardingLon());
|
xpOrder.setBoardingLat(vo.getBoardingLat());
|
xpOrder.setBoardingAddress(vo.getBoardingAddress());
|
xpOrder.setBoardingTime(vo.getBoardingTime());
|
xpOrder.setRideTime(vo.getBoardingTime());
|
redisService.setCacheObject("ORDER_STATUS:" + xpOrder.getId(), OrderStatusEnum.PASSENGER_BOARDED.getCode(), 24L, TimeUnit.HOURS);
|
|
XpVehicle xpVehicle1 = xpVehicleClient.queryById(xpOrder.getVehicleId()).getData();
|
|
//推送行起点到终点的驶轨迹给乘客端
|
sendRoute(xpOrder.getAppUserId() + "", xpOrder.getId(), "ORDER_NAVIGATION_END_DATA", xpVehicle1.getVehicleVin());
|
// 删除定时任务job
|
QuartzManager.removeJob((OrderCancelJob.name + xpOrder.getId()).toUpperCase(), TimeJobType.ORDER_CANCEL);
|
//乘客已上车,重新倒计时不开始行程取消订单
|
XpPoint xpPoint1 = xpPointClient.queryById(xpOrder.getStartPointId()).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(
|
OrderCancelJob.class,
|
(OrderCancelJob.name + xpOrder.getId()).toUpperCase(),
|
TimeJobType.ORDER_CANCEL,
|
DateUtils.localDateTimeToDate(LocalDateTime.now().plusMinutes(tradingStrategy.getBdNotBoardedTime())),
|
cancelJobMaps1
|
);
|
}
|
break;
|
case 6:
|
if (xpOrder.getStatus() != 5) {
|
return R.fail("订单状态不允许改为服务中");
|
}
|
// 删除定时任务job
|
QuartzManager.removeJob((OrderCancelJob.name + xpOrder.getId()).toUpperCase(), TimeJobType.ORDER_CANCEL);
|
xpOrder.setTripBegin(vo.getTripBegin());
|
redisService.setCacheObject("ORDER_STATUS:" + xpOrder.getId(), OrderStatusEnum.IN_SERVICE.getCode(), 24L, TimeUnit.HOURS);
|
String cacheObject = redisService.getCacheObject("ORDER_NAVIGATION_END_DATA:" + xpOrder.getAppUserId());
|
if (StringUtils.isNotBlank(cacheObject)) {
|
RouteVo routeVo1 = JSONObject.parseObject(cacheObject, RouteVo.class);
|
xpOrder.setEstimatedArrivalTime(LocalDateTime.now().plusSeconds(routeVo1.getDuration()));
|
}
|
redisService.deleteObject("ORDER_NAVIGATION_START_DATA:" + xpOrder.getAppUserId());
|
break;
|
case 7:
|
if (xpOrder.getStatus() != 6) {
|
return R.fail("订单状态不允许改为待乘客下车");
|
}
|
if (null == vo.getTripEnd()) {
|
return R.fail("行程结束时间不能为空");
|
}
|
if (null == vo.getGetoffLon()) {
|
return R.fail("下车点经度不能为空");
|
}
|
if (null == vo.getGetoffLat()) {
|
return R.fail("下车点纬度不能为空");
|
}
|
if (StringUtils.isEmpty(vo.getGetoffAddress())) {
|
return R.fail("下车点地址不能为空");
|
}
|
if (null == vo.getGetoffTime()) {
|
return R.fail("下车时间不能为空");
|
}
|
xpOrder.setGetoffTime(vo.getGetoffTime());
|
xpOrder.setGetoffLon(vo.getGetoffLon());
|
xpOrder.setGetoffLat(vo.getGetoffLat());
|
xpOrder.setGetoffAddress(vo.getGetoffAddress());
|
xpOrder.setTripEnd(vo.getTripEnd());
|
//计算行驶总距离和时长
|
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());
|
|
// 推送倒计时消息
|
Map<String, Object> params = new HashMap<>();
|
Map<String, Object> 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<String> 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<String> 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<String, ? extends Object> endTripJobMaps =
|
new ImmutableMap.Builder<String, String>()
|
.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();
|
Integer parkingTimeLimit1 = point.getParkingTimeLimit();
|
//如果点位位于场站内,则使用场站的限停时间
|
if(null != point.getStationId()){
|
XpStation xpStation = xpStationClient.getXpStationById(point.getStationId()).getData();
|
parkingTimeLimit1 = xpStation.getParkingTimeLimit();
|
}
|
Map<String, ? extends Object> jobMaps =
|
new ImmutableMap.Builder<String, String>()
|
.put("id", xpOrder.getId().toString())
|
.put("appid", appid)
|
.put("orderType", OrderType.USER_ORDER)
|
.build();
|
|
//判断是否有连环单
|
String json = redisService.getCacheObject(CacheConstants.nextOrder(xpOrder.getId().toString()));
|
if (StringUtils.isNotEmpty(json)) {
|
JSONObject jsonObject = JSON.parseObject(json);
|
Long id = jsonObject.getLong("orderId");
|
jobMaps = new ImmutableMap.Builder<String, String>()
|
.put("id", id.toString())
|
.put("oldOrderId", xpOrder.getId().toString())
|
.put("appid", appid)
|
.put("orderType", OrderType.USER_ORDER)
|
.build();
|
|
QuartzManager.addJob(
|
PushChainReactionOrderJob.class,
|
(PushChainReactionOrderJob.name + id).toUpperCase(),
|
TimeJobType.ADD_CHAIN_REACTION_ORDER_MISSION,
|
DateUtils.localDateTimeToDate(LocalDateTime.now().plusMinutes(parkingTimeLimit1)),
|
jobMaps
|
);
|
} else {
|
QuartzManager.addJob(
|
AddCruiseMissionJob.class,
|
(AddCruiseMissionJob.name + xpOrder.getId()).toUpperCase(),
|
TimeJobType.ADD_CRUISE_MISSION,
|
DateUtils.localDateTimeToDate(LocalDateTime.now().plusMinutes(parkingTimeLimit1)),
|
jobMaps
|
);
|
}
|
redisService.setCacheObject("ORDER_STATUS:" + xpOrder.getId(), OrderStatusEnum.SEE_OFF_GUESTS.getCode(), 24L, TimeUnit.HOURS);
|
break;
|
default:
|
return R.fail("无效的订单状态");
|
}
|
xpOrder.setStatus(vo.getOrderStatus());
|
this.updateById(xpOrder);
|
//推送订单状态给乘客端
|
Map<String, Object> params = new HashMap<>();
|
Map<String, Object> content = new HashMap<>();
|
content.put("orderId", xpOrder.toString());
|
content.put("status", vo.getOrderStatus());
|
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(xpOrder.getAppUserId() + "");
|
dto1.setMessage(message);
|
R<String> 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<String> 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(dto1), xpOrder.getOrderNumber());
|
}
|
});
|
}
|
|
|
return R.ok();
|
}
|
|
@Override
|
public UserOrderInfoVo getOrderHistoryInfo(Long orderId) {
|
XpOrder xpOrder = this.getById(orderId);
|
UserOrderInfoVo vo = new UserOrderInfoVo();
|
vo.setOrderId(xpOrder.getId());
|
vo.setOrderCreatedAt(xpOrder.getCreateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
|
vo.setDispatchStartAt("");
|
vo.setDispatchSuccessAt(null == xpOrder.getAcceptanceTime() ? "" : xpOrder.getAcceptanceTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
|
vo.setVehicleArrivedAt(null == xpOrder.getArrivalTime() ? "" : xpOrder.getArrivalTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
|
vo.setFreeWaitExpiredAt("");
|
vo.setVehicleUnlockedAt("");
|
vo.setDoorOpenedAt(Arrays.asList(""));
|
vo.setDoorClosedAt(Arrays.asList(""));
|
vo.setTripConfirmedAt(null == xpOrder.getTripBegin() ? "" : xpOrder.getTripBegin().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
|
vo.setSafetybeltUnlockedAt(Arrays.asList(""));
|
vo.setArrivedDestinationAt("");
|
vo.setDropoffDoorOpenedAt(Arrays.asList(""));
|
vo.setDropoffTrunkOpenedAt(Arrays.asList(""));
|
vo.setDropoffDoorClosedAt(Arrays.asList(""));
|
vo.setTripEndedAt(null == xpOrder.getTripEnd() ? "" : xpOrder.getTripEnd().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
|
vo.setPaymentCompletedAt("");
|
XpOrderEvaluate orderEvaluate = xpOrderEvaluateService.getOne(new QueryWrapper<XpOrderEvaluate>().eq("order_id", xpOrder.getId()));
|
if(null != orderEvaluate){
|
vo.setUserRatedAt(orderEvaluate.getCreateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
|
}
|
XpOrderCancel xpOrderCancel = xpOrderCancelService.getOne(new QueryWrapper<XpOrderCancel>().eq("order_id", xpOrder.getId()));
|
if(null != xpOrderCancel){
|
vo.setOrderCancelledAt(xpOrderCancel.getCreateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
|
vo.setCancelReasonType(xpOrderCancel.getCancelSource() == 1 ? "用户取消" : xpOrderCancel.getCancelSource() == 4 ? "超时取消" : "系统取消");
|
}
|
vo.setSafetyAlertTriggeredAt("");
|
vo.setRemoteTakeoverAt("");
|
return vo;
|
}
|
|
/**
|
* 查询订单状态
|
* @param orderId
|
* @return
|
*/
|
@Override
|
public OrderStatusFromOrderIdVo queryOrderStatusFromOrderId(Long orderId) {
|
|
XpOrder order = this.getById(orderId);
|
if(order==null){
|
return null;
|
}
|
if(order.getPayType()==3){
|
// 免密查询 如果成功则调用结算
|
CreditOrderQueryDto dto = new CreditOrderQueryDto();
|
dto.setBusinessOrderId(OrderCodeUtil.getOrderCode("MMZF"));
|
dto.setAuthOrderId(order.getAuthOrderId());
|
dto.setPayType("13");
|
R<CreditOrderQueryVo> creditOrderQueryVoR = xpPayFrontClient.queryCreditOrder(dto);
|
if(creditOrderQueryVoR.getCode()==200){
|
CreditOrderQueryVo creditOrderQueryVo = creditOrderQueryVoR.getData();
|
if(creditOrderQueryVo.getCreditOrderStatus().equals("DONE")){
|
// 订单确认 需要吊起扣款
|
CreditOrderWxPayDto creditOrderWxPayDto = new CreditOrderWxPayDto();
|
creditOrderWxPayDto.setBusinessOrderId(OrderCodeUtil.getOrderCode("MMZF"));
|
creditOrderWxPayDto.setAuthOrderId(order.getAuthOrderId());
|
creditOrderWxPayDto.setPayCallbackUrl("13");
|
creditOrderWxPayDto.setTotalAmount( order.getOrderMoney().multiply(BigDecimal.valueOf(100)).longValue());
|
creditOrderWxPayDto.setSubject("小鹏集团|robotaxi");
|
creditOrderWxPayDto.setPayType("13");
|
|
List<CreditOrderCreateDto.PostPayment> postPayments = new ArrayList<>();
|
CreditOrderCreateDto.PostPayment postPayment = new CreditOrderCreateDto.PostPayment();
|
postPayment.setName("收费标准");
|
postPayment.setDescription("行程价格动态浮动,具体详情可在费用详情或计价规则查看");
|
postPayments.add(postPayment);
|
creditOrderWxPayDto.setPostPayments(postPayments);
|
R<CreditOrderWxPayVo> creditOrderWxPayVoR = xpPayFrontClient.creditOrderWxPay(creditOrderWxPayDto);
|
}
|
}
|
|
}else {
|
// 微信支付也需要查询下
|
PayOrderQueryDto queryDto = new PayOrderQueryDto();
|
queryDto.setBizId("xp-robotaxi");
|
queryDto.setBizOrderId(order.getBusinessOrderId());
|
R<PayOrderQueryVo> payOrderQueryVoR = xpPayFrontClient.queryPayOrder(queryDto);
|
if(payOrderQueryVoR.getCode()==200){
|
PayOrderQueryVo payOrderQueryVo = payOrderQueryVoR.getData();
|
if(payOrderQueryVo.getPayStatus()==1){
|
// 支付成功
|
order.setStatus(9);
|
order.setAppUserId(null);
|
order.setAuthOrderId(payOrderQueryVo.getPayOrderId());
|
|
if(payOrderQueryVo.getPayType()==13){
|
order.setPayType(3);
|
}
|
|
this.updateById(order);
|
|
// 财务推送
|
xpFrdXfipEventPush(order,payOrderQueryVo.getAmount(),payOrderQueryVo.getPayOrderId());
|
|
|
}
|
|
|
}
|
|
}
|
OrderStatusFromOrderIdVo orderStatusFromOrderIdVo = new OrderStatusFromOrderIdVo();
|
orderStatusFromOrderIdVo.setOrderId(order.getId());
|
orderStatusFromOrderIdVo.setStatus(order.getStatus());
|
return orderStatusFromOrderIdVo;
|
}
|
|
@Override
|
public void creditPayCall(XpOrder order) {
|
|
|
CreditOrderWxPayDto dto = new CreditOrderWxPayDto();
|
dto.setBusinessId("xp-robotaxi");
|
dto.setBusinessOrderId(order.getBusinessOrderId());
|
dto.setAuthOrderId(order.getAuthOrderId());
|
dto.setCategory("009001");
|
dto.setPayCallbackUrl("");
|
dto.setTotalAmount(order.getOrderMoney().multiply(BigDecimal.valueOf(100)).longValue());
|
dto.setSubject("小鹏集团|robotaxi");
|
dto.setPayType("13");
|
List<CreditOrderCreateDto.PostPayment> postPayments = new ArrayList<>();
|
CreditOrderCreateDto.PostPayment postPayment = new CreditOrderCreateDto.PostPayment();
|
postPayment.setName("收费标准");
|
postPayment.setDescription("行程价格动态浮动,具体详情可在费用详情或计价规则查看");
|
dto.setPostPayments(postPayments);
|
R<CreditOrderWxPayVo> creditOrderWxPayVoR = xpPayFrontClient.creditOrderWxPay(dto);
|
|
|
}
|
|
/**
|
* 对接财务数据推送
|
* @param order
|
*/
|
@Override
|
public void xpFrdXfipEventPush(XpOrder order,Long payFee,String orderId) {
|
|
if(order.getFrdXfipEvenetStatus()==1){
|
return;
|
}
|
|
XpFrdXfipEventIngestReqDTO xpFrdXfipEventIngestReqDTO = new XpFrdXfipEventIngestReqDTO();
|
xpFrdXfipEventIngestReqDTO.setEventId("RT_04");
|
xpFrdXfipEventIngestReqDTO.setBusinessLine("RT");
|
xpFrdXfipEventIngestReqDTO.setReqId(OrderCodeUtil.getOrderCode("FRDXFOPEVENT"));
|
|
XpFrdXfipEventIngestContentDTO xpFrdXfipEventIngestContentDTO = new XpFrdXfipEventIngestContentDTO();
|
xpFrdXfipEventIngestContentDTO.setCompanyCode("8000");
|
xpFrdXfipEventIngestContentDTO.setOrderType("SELF_OPERATED");
|
xpFrdXfipEventIngestContentDTO.setBusinessOrderNo(order.getMainOrderId());
|
xpFrdXfipEventIngestContentDTO.setSubOrderNo(order.getBusinessOrderId());
|
xpFrdXfipEventIngestContentDTO.setPaymentType("POST_PAY");
|
xpFrdXfipEventIngestContentDTO.setOrderStatus("结算完成");
|
xpFrdXfipEventIngestContentDTO.setSettlementDate(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")));
|
xpFrdXfipEventIngestContentDTO.setMaterialNo("XE2000398");
|
xpFrdXfipEventIngestContentDTO.setBusinessPartnerCode("5004901");
|
xpFrdXfipEventIngestContentDTO.setCurrency("CNY");
|
|
xpFrdXfipEventIngestContentDTO.setConfirmedRevenueAmount(order.getOrderMoney());
|
xpFrdXfipEventIngestContentDTO.setCashCollectedAmount(new BigDecimal(payFee).divide(BigDecimal.valueOf(100), 2,RoundingMode.HALF_UP));
|
|
xpFrdXfipEventIngestContentDTO.setCouponConsumedTotal(order.getCouponMoney()==null?BigDecimal.ZERO:order.getCouponMoney());
|
|
xpFrdXfipEventIngestContentDTO.setPaymentFlowNo(orderId);
|
xpFrdXfipEventIngestContentDTO.setPaymentFlowAmount(new BigDecimal(payFee).divide(BigDecimal.valueOf(100), 2,RoundingMode.HALF_UP));
|
xpFrdXfipEventIngestContentDTO.setPaymentChannel("WECHAT");
|
|
if(order.getCouponId()!=null){
|
ArrayList<XpFrdXfipEventCouponLineDTO> list = new ArrayList<>(1);
|
XpFrdXfipEventCouponLineDTO xpFrdXfipEventCouponLineDTO = new XpFrdXfipEventCouponLineDTO();
|
xpFrdXfipEventCouponLineDTO.setCouponId(String.valueOf(order.getCouponId()));
|
xpFrdXfipEventCouponLineDTO.setCouponType("ACTIVITY");
|
// xpFrdXfipEventCouponLineDTO.setCouponIssuerCompanyCode("5004901");
|
// xpFrdXfipEventCouponLineDTO.setSelfBorneAmount(order.getCouponMoney());
|
// xpFrdXfipEventCouponLineDTO.setThirdPartyBorneAmount(BigDecimal.ZERO);
|
xpFrdXfipEventCouponLineDTO.setCouponDeductionAmount(order.getCouponMoney());
|
xpFrdXfipEventCouponLineDTO.setCouponIssuerCompanyCode("8000");
|
list.add(xpFrdXfipEventCouponLineDTO);
|
xpFrdXfipEventIngestContentDTO.setCouponLines(list);
|
}
|
|
xpFrdXfipEventIngestReqDTO.setContent(xpFrdXfipEventIngestContentDTO);
|
R<XpFrdXfipEventIngestRespVO> ingest = xpFrdXfipEventClient.ingest(xpFrdXfipEventIngestReqDTO);
|
if(ingest.getCode()==200){
|
XpFrdXfipEventIngestRespVO data = ingest.getData();
|
if(!data.getReceiveStatus().equals("REJECTED")){
|
// 推送成功
|
order.setFrdXfipEvenetStatus(1);
|
order.setFrdXfipEvenetId(data.getEventId());
|
}else {
|
// 推送失败
|
order.setFrdXfipEvenetStatus(2);
|
order.setFrdXfipEvenetId(data.getEventId());
|
}
|
log.info("xpFrdXfipEventPush:{}",data);
|
}else {
|
// 重试1次
|
ingest = xpFrdXfipEventClient.ingest(xpFrdXfipEventIngestReqDTO);
|
XpFrdXfipEventIngestRespVO data = ingest.getData();
|
if(ingest.getCode()==200){
|
if(!data.getReceiveStatus().equals("REJECTED")){
|
// 推送成功
|
order.setFrdXfipEvenetStatus(1);
|
order.setFrdXfipEvenetId(data.getEventId());
|
}else {
|
// 推送失败
|
order.setFrdXfipEvenetStatus(2);
|
order.setFrdXfipEvenetId(data.getEventId());
|
}
|
log.info("xpFrdXfipEventPush:{}",data);
|
}else {
|
// 推送失败
|
order.setFrdXfipEvenetStatus(2);
|
order.setFrdXfipEvenetId(data.getEventId());
|
}
|
log.info("xpFrdXfipEventPush:{}",ingest.getMsg());
|
}
|
order.setAppUserId(null);
|
updateById(order);
|
}
|
|
|
/**
|
* 发票列表
|
* @param query
|
* @return
|
*/
|
@Override
|
public List<XpInvoiceVo> invoiceList(XpOrderInvoiceQuery query) {
|
|
DateTimeFormatter monthFormatter = DateTimeFormatter.ofPattern("yyyyMM");
|
LocalDateTime startTime = YearMonth.parse(query.getStartTime(), monthFormatter)
|
.atDay(1)
|
.atStartOfDay();
|
LocalDateTime endTime = YearMonth.parse(query.getEndTime(), monthFormatter)
|
.atEndOfMonth()
|
.atTime(23, 59, 59);
|
|
List<XpOrder> list =null;
|
// 通过uid 和时间查出来数据 先判断是否传入了分页
|
if (query.getPageNo()!=null && query.getPageSize()!=null) {
|
Page<XpOrder> page = new Page<>(query.getPageNo(), query.getPageSize());
|
Page<XpOrder> orderPage = this.baseMapper.selectPage(page, new LambdaQueryWrapper<XpOrder>().eq(XpOrder::getAppUserId, Long.valueOf(query.getUid())).ge(XpOrder::getStatus, 9).between(BaseModel::getCreateTime, startTime, endTime).orderByDesc(BaseModel::getCreateTime));
|
list = orderPage.getRecords();
|
}else {
|
list = this.baseMapper.selectList(new LambdaQueryWrapper<XpOrder>().eq(XpOrder::getAppUserId, Long.valueOf(query.getUid())).ge(XpOrder::getStatus, 9).between(BaseModel::getCreateTime, startTime, endTime).orderByDesc(BaseModel::getCreateTime));
|
}
|
|
if(list.isEmpty()){
|
return new ArrayList<>();
|
}
|
|
|
ArrayList<XpInvoiceVo> xpInvoiceVoList = new ArrayList<>(list.size());
|
|
R<List<XpVehicle>> r = xpVehicleClient.listByIds(list.stream().map(XpOrder::getVehicleId).collect(Collectors.toList()));
|
List<XpVehicle> vehicleList = r.getData();
|
|
// 数据组装
|
for (XpOrder xpOrder : list) {
|
XpInvoiceVo xpInvoiceVo = new XpInvoiceVo();
|
xpInvoiceVo.setUid(xpOrder.getAppUserId().toString());
|
xpInvoiceVo.setOrderNo(xpOrder.getOrderNumber());
|
xpInvoiceVo.setOrderTotalAmount(xpOrder.getOrderMoney());
|
xpInvoiceVo.setOrderDiscountAmount(xpOrder.getCouponMoney());
|
xpInvoiceVo.setOrderFinishTime(xpOrder.getTripEnd()!=null?String.valueOf(xpOrder.getTripEnd()):"");
|
xpInvoiceVo.setXpInvoiceFlag(true);
|
xpInvoiceVo.setInvoiceStatus(xpOrder.getInvoiceStatus());
|
XpInvoiceVo.Itinerary itinerary = new XpInvoiceVo.Itinerary();
|
// yyyy-MM-dd格式
|
itinerary.setTripStartDate(xpOrder.getAcceptanceTime()!=null?xpOrder.getAcceptanceTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")):"");
|
itinerary.setTripEndDate(xpOrder.getTripEnd()!=null?xpOrder.getTripEnd().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")):"");
|
|
|
XpVehicle xpVehicle = vehicleList.stream().filter(vehicle -> vehicle.getId().equals(xpOrder.getVehicleId())).findFirst().get();
|
itinerary.setCarType(xpVehicle.getVehicleModel());
|
itinerary.setTripStartDate(xpOrder.getBoardingTime()!=null?xpOrder.getBoardingTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")):"");
|
|
itinerary.setCity(xpOrder.getCityName());
|
itinerary.setStartPoint(xpOrder.getStartAddress());
|
itinerary.setEndPoint(xpOrder.getEndAddress());
|
itinerary.setMileage(null == xpOrder.getMileageKilometers() ? "0" : xpOrder.getMileageKilometers().toString());
|
itinerary.setAmount(xpOrder.getOrderMoney());
|
|
xpInvoiceVo.setItinerary(itinerary);
|
xpInvoiceVoList.add(xpInvoiceVo);
|
}
|
return xpInvoiceVoList;
|
}
|
|
|
/**
|
* 查询发票订单
|
* @param query
|
* @return
|
*/
|
@Override
|
public List<XpInvoiceVo> queryInvoiceOrders(InvoiceOrdersQuery query) {
|
if(query.getOrderNoList().isEmpty()){
|
return Collections.emptyList();
|
}
|
List<XpOrder> list = this.baseMapper.selectList(new LambdaQueryWrapper<XpOrder>().eq(XpOrder::getAppUserId, Long.valueOf(query.getUid())).ge(XpOrder::getStatus, 9).in(XpOrder::getOrderNumber, query.getOrderNoList()));
|
if(list.isEmpty()){
|
return Collections.emptyList();
|
}
|
|
|
ArrayList<XpInvoiceVo> xpInvoiceVoList = new ArrayList<>(list.size());
|
|
R<List<XpVehicle>> r = xpVehicleClient.listByIds(list.stream().map(XpOrder::getVehicleId).collect(Collectors.toList()));
|
List<XpVehicle> vehicleList = r.getData();
|
|
// 数据组装
|
for (XpOrder xpOrder : list) {
|
XpInvoiceVo xpInvoiceVo = new XpInvoiceVo();
|
xpInvoiceVo.setUid(xpOrder.getAppUserId().toString());
|
xpInvoiceVo.setOrderNo(xpOrder.getOrderNumber());
|
xpInvoiceVo.setOrderTotalAmount(xpOrder.getOrderMoney());
|
xpInvoiceVo.setOrderDiscountAmount(xpOrder.getCouponMoney());
|
xpInvoiceVo.setOrderFinishTime(String.valueOf(xpOrder.getTripEnd()));
|
xpInvoiceVo.setXpInvoiceFlag(true);
|
xpInvoiceVo.setInvoiceStatus(xpOrder.getInvoiceStatus());
|
XpInvoiceVo.Itinerary itinerary = new XpInvoiceVo.Itinerary();
|
// yyyy-MM-dd格式
|
itinerary.setTripStartDate(xpOrder.getAcceptanceTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
|
itinerary.setTripEndDate(xpOrder.getTripEnd().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
|
|
|
XpVehicle xpVehicle = vehicleList.stream().filter(vehicle -> vehicle.getId().equals(xpOrder.getVehicleId())).findFirst().get();
|
itinerary.setCarType(xpVehicle.getVehicleModel());
|
itinerary.setTripStartDate(xpOrder.getBoardingTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
|
|
itinerary.setCity(xpOrder.getCityName());
|
itinerary.setStartPoint(xpOrder.getStartAddress());
|
itinerary.setEndPoint(xpOrder.getEndAddress());
|
itinerary.setMileage(xpOrder.getMileageKilometers().toString());
|
itinerary.setAmount(xpOrder.getOrderMoney());
|
|
xpInvoiceVo.setItinerary(itinerary);
|
xpInvoiceVoList.add(xpInvoiceVo);
|
}
|
return xpInvoiceVoList;
|
}
|
|
|
/**
|
* 修改发票状态
|
* @param dto
|
*/
|
@Override
|
public void updateInvoiceStatus(UpdateInvoiceStatusDto dto) {
|
List<UpdateInvoiceStatusDto.Order> orderList = dto.getOrderList();
|
if(!orderList.isEmpty()){
|
List<String> orderNos = orderList.stream().map(UpdateInvoiceStatusDto.Order::getOrderNo).collect(Collectors.toList());
|
List<XpOrder> xpOrders = this.baseMapper.selectList(new LambdaQueryWrapper<XpOrder>().in(XpOrder::getOrderNumber, orderNos));
|
for (XpOrder xpOrder : xpOrders) {
|
UpdateInvoiceStatusDto.Order order = orderList.stream().filter(e -> e.getOrderNo().equals(xpOrder.getOrderNumber())).findFirst().orElse(null);
|
if(order!=null){
|
xpOrder.setInvoiceStatus(order.getInvoiceStatus());
|
xpOrder.setInvoiceId(dto.getRequestId());
|
xpOrder.setAppUserId(null);
|
this.updateById(xpOrder);
|
}
|
}
|
|
}
|
|
|
}
|
}
|