xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/controller/XpAppUserCouponController.java
@@ -84,7 +84,7 @@ /** * 获取优惠券列表 */ @PostMapping("/api/v1/coupon/list") @PostMapping("/api/v1/mini-program/coupon/list") @ApiOperation(value = "获取用户获取优惠券列表", tags = {"小程序-获取用户获取优惠券列表"}) @XpClientCheck(checkToken = false, checkOtpToken = true) public R<XpAppUserCouponVo> list(@RequestBody XpUserCouponListQuery query, HttpServletRequest request) { @@ -97,7 +97,7 @@ /** * 领取优惠券 */ @PostMapping("/api/v1/coupon/receive") @PostMapping("/api/v1/mini-program/coupon/receive") @ApiOperation(value = "领取优惠券", tags = {"小程序-领取优惠券"}) @XpClientCheck(checkToken = false, checkOtpToken = true) public R<String> receive(@RequestBody ReceiveCouponDto dto, HttpServletRequest request) { @@ -115,7 +115,7 @@ /** * 获取优惠券详情 */ @GetMapping("/api/v1/coupon/detail/{couponId}") @GetMapping("/api/v1/mini-program/coupon/detail/{couponId}") @ApiOperation(value = "获取用户获取优惠券详情", tags = {"小程序-获取用户获取优惠券详情"}) @XpClientCheck(checkToken = false, checkOtpToken = true) public R<XpAppUserCouponListVo> detail(@PathVariable("couponId") Long couponId, HttpServletRequest request) { @@ -143,7 +143,7 @@ /** * 获取优惠券列表total */ @GetMapping("/api/v1/coupon/total") @GetMapping("/api/v1/mini-program/coupon/total") @ApiOperation(value = "获取用户获取优惠券数量", tags = {"小程序-获取用户获取优惠券数量"}) @XpClientCheck(checkToken = false, checkOtpToken = true) public R<XpAppUserCouponTotalVo> total(HttpServletRequest request) { xp-robotaxi-account-boot/src/main/resources/mapper/account/XpAppUserCouponMapper.xml
@@ -24,20 +24,20 @@ end status FROM xp_app_user_coupon WHERE app_user_id = #{appUserId} WHERE app_user_id = #{uid} ) as t where 1=1 <if test="status != null"> and t.status = #{status} <if test="query.status != null"> and t.status = #{query.status} </if> <if test="status != null and status == 1"> <if test="query.status != null and query.status == 1"> order by t.expireTime ,t.discountAmount desc,t.createTime desc </if> <if test="status != null and status == 2"> <if test="query.status != null and query.status == 2"> order by t.useTime desc </if> <if test="status != null and status == 3"> <if test="query.status != null and query.status == 3"> order by t.expireTime desc </if> xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/order/XpOrderClient.java
@@ -61,7 +61,7 @@ * @param orderId 订单id */ @PostMapping("/inner/order/cancelOrder") R<?> cancelOrder(String orderId); R<?> cancelOrder(@RequestParam("orderId") String orderId); /** xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/vehicleConvoy/XpVehicleClient.java
@@ -40,7 +40,7 @@ * @return 车辆 */ @GetMapping("/inner/vehicle/queryById") R<XpVehicle> queryById(Long id); R<XpVehicle> queryById(@RequestParam Long id); /** * 修改车辆信息 * @@ -80,5 +80,5 @@ * @return */ @GetMapping("/inner/vehicle/findByVehicleVin") R<XpVehicle> findByVehicleVin(String vehicleVin); R<XpVehicle> findByVehicleVin(@RequestParam("vehicleVin") String vehicleVin); } xp-robotaxi-api/src/main/java/com/xiaopeng/api/model/order/XpOrderCancel.java
@@ -31,7 +31,7 @@ private static final long serialVersionUID = 1L; @ApiModelProperty(value = "主键") @TableId(value = "id", type = IdType.AUTO) @TableId(value = "id", type = IdType.ASSIGN_ID) private Long id; @ApiModelProperty(value = "订单id") xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/automaticPilot/VehiclePosition.java
@@ -24,7 +24,6 @@ /** * 车辆VIN */ @Id private String vin; /** * 车辆所属围栏id xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/order/FareBreakdownDetailVo.java
@@ -9,14 +9,14 @@ @Data @ApiModel("订单费用明细详情VO") public class FareBreakdownDetailVo { @ApiModelProperty(value = "type=1 普通时段 type=2 夜间行驶时段 type=3节假日时段") @ApiModelProperty(value = "type=1 普通时段 type=2 夜间行驶时段 3高峰时段 4节假日时段") private Integer type; @ApiModelProperty(value = "时段 例:18:00-19:00") private String time; @ApiModelProperty(value = "里程 单位公里") private BigDecimal mileage; @ApiModelProperty(value = "时间") private BigDecimal duration; private long duration; @ApiModelProperty(value = "金额") private BigDecimal money; } xp-robotaxi-automaticPilot-boot/src/main/java/com/xiaopeng/automaticPilot/controller/OrderMissionController.java
New file @@ -0,0 +1,47 @@ package com.xiaopeng.automaticPilot.controller; import com.xiaopeng.api.vo.automaticPilot.proto.down.ResponseMessage; import com.xiaopeng.api.vo.automaticPilot.proto.down.StatusCode; import com.xiaopeng.api.vo.automaticPilot.proto.down.cloudCreateOrder.RTTask; import com.xiaopeng.api.vo.automaticPilot.proto.down.cloudCreateOrder.TaskStatus; import com.xiaopeng.api.vo.automaticPilot.proto.down.cloudCreateOrder.TaskType; import com.xiaopeng.automaticPilot.util.XPUUtil; import com.xiaopeng.common.core.domain.R; import io.swagger.annotations.Api; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @Slf4j @RestController @RequestMapping("") @Api(tags = "订单任务Api") public class OrderMissionController { private final XPUUtil xpuUtil; public OrderMissionController(XPUUtil xpuUtil) { this.xpuUtil = xpuUtil; } @PostMapping("/inner/pushOrder") public R<Boolean> pushOrder(@RequestParam Long orderId) { RTTask task = new RTTask(); task.setOrder_id(orderId.toString()); // TODO id处理 已经数据是否存储 task.setTask_id(System.currentTimeMillis()+ ""); task.setTask_type(TaskType.ROBOTAXI); task.setStatus(TaskStatus.PENDING); ResponseMessage responseMessage = xpuUtil.cloudCreateOrder(task); if (responseMessage.getCode()== StatusCode.OK) { return R.ok(true); } return R.ok(false); } } xp-robotaxi-common/xp-common-core/src/main/java/com/xiaopeng/common/core/utils/DateUtils.java
@@ -214,6 +214,12 @@ ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault()); return Date.from(zdt.toInstant()); } /** * 增加 Date ==> LocalDate */ public static LocalDate toLocalDate(Date date){ return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); } /** * 计算两个日期之间相差的天数 xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/controller/XpElectronicFenceManagementController.java
@@ -96,7 +96,7 @@ /** * 复制电子围栏 */ @PutMapping("/copy") @GetMapping("/copy") @ResponseBody @ApiOperation(value = "复制电子围栏") @Transactional xp-robotaxi-operation-boot/pom.xml
@@ -115,6 +115,26 @@ <version>1.5.2-v2</version> </dependency> <!-- easypoi --> <!-- Apache POI 核心依赖 --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>5.2.3</version> </dependency> <!-- Apache POI OOXML 支持(用于 .xlsx 文件) --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>5.2.3</version> </dependency> <!-- Apache POI XMLBeans(可选,用于复杂 XML 处理) --> <dependency> <groupId>org.apache.xmlbeans</groupId> <artifactId>xmlbeans</artifactId> <version>5.1.1</version> </dependency> <dependency> <groupId>cn.afterturn</groupId> <artifactId>easypoi-base</artifactId> xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/constant/I18nKeyConstant.java
@@ -32,4 +32,7 @@ // 订单创建失败 String ORDER_CREATE_FAIL = "order.create.fail"; //开通城市已存在 String OPEN_CITY_EXISTS = "open.city.exists"; } xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/controller/XpElectronicFenceController.java
@@ -142,7 +142,7 @@ /** * 复制电子围栏 */ @PutMapping("/copy") @GetMapping("/copy") @ApiOperation(value = "复制电子围栏") @Transactional public R copy(Integer id) { xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/controller/XpOpenCityController.java
@@ -3,17 +3,20 @@ import com.xiaopeng.api.model.operation.XpOpenCity; import com.xiaopeng.common.core.domain.R; import com.xiaopeng.common.core.web.controller.BaseController; import com.xiaopeng.common.core.web.domain.AjaxResult; import com.xiaopeng.common.core.web.page.PageInfo; import com.xiaopeng.operation.dto.OpenCityAddDTO; import com.xiaopeng.operation.dto.OpenCityEditDTO; import com.xiaopeng.operation.query.OpenCityPageQuery; import com.xiaopeng.operation.service.XpOpenCityService; import com.xiaopeng.operation.vo.OpenCityListVO; import com.xiaopeng.operation.vo.XpOpenCityVo; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.*; import javax.validation.Valid; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -71,6 +74,63 @@ return R.ok(nameLists); } /** * 管理后台-开通城市管理-开通城市分页列表 * @param query * @return */ @ApiOperation(value = "开通城市分页列表", tags = {"管理后台-开通城市管理"}) @PostMapping("/api/v1/openCity/pageList") public R<PageInfo<XpOpenCity>> getPageList(@RequestBody OpenCityPageQuery query) { return R.ok(xpOpenCityService.getPageList(query)); } /** * 管理后台-开通城市管理-新增开通城市 * @param dto * @return */ @ApiOperation(value = "新增开通城市", tags = {"管理后台-开通城市管理"}) @PostMapping("/api/v1/openCity/add") public R<?> add(@Valid @RequestBody OpenCityAddDTO dto) { xpOpenCityService.add(dto); return R.ok(); } /** * 管理后台-开通城市管理-编辑开通城市 * @param dto * @return */ @ApiOperation(value = "编辑开通城市", tags = {"管理后台-开通城市管理"}) @PostMapping("/api/v1/openCity/edit") public R<?> add(@Valid @RequestBody OpenCityEditDTO dto) { xpOpenCityService.edit(dto); return R.ok(); } /** * 管理后台-开通城市管理-删除开通城市 * @param id * @return */ @ApiOperation(value = "删除开通城市", tags = {"管理后台-开通城市管理"}) @DeleteMapping("/api/v1/openCity/delete/{id}") public R<?> deleteById(@ApiParam(name = "id", value = "开通城市ID") @PathVariable Long id) { xpOpenCityService.deleteById(id); return R.ok(); } /** * 管理后台-开通城市管理-启用/禁用 * @param id * @param status * @return */ @ApiOperation(value = "启用/禁用", tags = {"管理后台-开通城市管理"}) @PostMapping("/api/v1/openCity/upDown") public R<?> upDown(Long id, Integer status) { xpOpenCityService.upDown(id, status); return R.ok(); } } xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/dto/OpenCityAddDTO.java
New file @@ -0,0 +1,45 @@ package com.xiaopeng.operation.dto; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; /** * @author MiTao * @date 2026-02-12 22:23 * */ @Data @ApiModel("新增开通城市数据传输对象") public class OpenCityAddDTO { @ApiModelProperty(value = "省份名称") @NotBlank(message = "省份名称不能为空") private String provinceName; @ApiModelProperty(value = "省份code") @NotBlank(message = "省份code名称不能为空") private String provinceCode; @ApiModelProperty(value = "城市名称") @NotBlank(message = "城市名称名称不能为空") private String cityName; @ApiModelProperty(value = "城市code") @NotBlank(message = "城市code名称不能为空") private String cityCode; @ApiModelProperty(value = "区县名称") private String districtName; @ApiModelProperty(value = "区县code") private String districtCode; @ApiModelProperty(value = "状态 1=启用 0=禁用") @NotNull(message = "状态不能为空") private Integer status; } xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/dto/OpenCityEditDTO.java
New file @@ -0,0 +1,22 @@ package com.xiaopeng.operation.dto; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode; import javax.validation.constraints.NotNull; /** * @author MiTao * @date 2026-02-12 22:25 * */ @Data @EqualsAndHashCode(callSuper = true) @ApiModel("开通城市编辑数据传输对象") public class OpenCityEditDTO extends OpenCityAddDTO{ @ApiModelProperty(value = "ID") @NotNull(message = "ID不能为空") private Long id; } xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/query/OpenCityPageQuery.java
New file @@ -0,0 +1,25 @@ package com.xiaopeng.operation.query; import com.xiaopeng.common.core.web.page.BasePage; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode; /** * @author MiTao * @date 2026-02-12 22:13 * */ @Data @EqualsAndHashCode(callSuper = true) @ApiModel("开通城市分页查询对象") public class OpenCityPageQuery extends BasePage { @ApiModelProperty(value = "城市名称") private String cityName; @ApiModelProperty(value = "状态 1=启用 0=禁用") private Integer status; } xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/XpOpenCityService.java
@@ -3,9 +3,14 @@ import com.baomidou.mybatisplus.extension.service.IService; import com.xiaopeng.api.model.operation.XpOpenCity; import com.xiaopeng.common.core.web.page.PageInfo; import com.xiaopeng.operation.dto.OpenCityAddDTO; import com.xiaopeng.operation.dto.OpenCityEditDTO; import com.xiaopeng.operation.query.OpenCityPageQuery; import com.xiaopeng.operation.vo.OpenCityListVO; import com.xiaopeng.operation.vo.XpOpenCityVo; import javax.validation.Valid; import java.util.List; /** @@ -28,4 +33,38 @@ */ List<OpenCityListVO> getOpenCityListForAdmin(); /** * 开通城市分页列表 * @return */ PageInfo<XpOpenCity> getPageList(OpenCityPageQuery query); /** * 新增开通城市 * * @param dto */ void add(@Valid OpenCityAddDTO dto); /** * 编辑开通城市 * * @param dto */ void edit(@Valid OpenCityEditDTO dto); /** * 删除开通城市 * @param id */ void deleteById(Long id); /** * 管理后台-开通城市管理-启用/禁用 * * @param id * @param status * @return */ void upDown(Long id, Integer status); } xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/impl/XpOpenCityServiceImpl.java
@@ -1,11 +1,19 @@ package com.xiaopeng.operation.service.impl; import cn.hutool.core.bean.BeanUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.xiaopeng.common.redis.service.RedisService; import com.xiaopeng.operation.mapper.XpOpenCityMapper; import com.xiaopeng.api.model.operation.XpOpenCity; import com.xiaopeng.common.core.exception.ServiceException; import com.xiaopeng.common.core.web.page.PageInfo; import com.xiaopeng.common.security.utils.SecurityUtils; import com.xiaopeng.operation.constant.I18nKeyConstant; import com.xiaopeng.operation.dto.OpenCityAddDTO; import com.xiaopeng.operation.dto.OpenCityEditDTO; import com.xiaopeng.operation.mapper.XpOpenCityMapper; import com.xiaopeng.operation.query.OpenCityPageQuery; import com.xiaopeng.operation.service.XpOpenCityService; import com.xiaopeng.operation.vo.OpenCityListVO; import com.xiaopeng.operation.vo.XpOpenCityNextVo; @@ -13,9 +21,9 @@ import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; /** @@ -80,4 +88,58 @@ }) .collect(Collectors.toList()); } @Override public PageInfo<XpOpenCity> getPageList(OpenCityPageQuery query) { Page<XpOpenCity> page = lambdaQuery().like(StringUtils.isNotBlank(query.getCityName()), XpOpenCity::getCityName, query.getCityName()) .eq(Objects.nonNull(query.getStatus()), XpOpenCity::getStatus, query.getStatus()) .orderByDesc(XpOpenCity::getCreateTime) .page(new Page<>(query.getPageNum(), query.getPageSize())); PageInfo<XpOpenCity> pageInfo = new PageInfo<>(); pageInfo.setRecords(page.getRecords()); page.setTotal(page.getTotal()); return pageInfo; } @Override public void add(OpenCityAddDTO dto) { checkIfExist(dto, null); XpOpenCity xpOpenCity = BeanUtil.copyProperties(dto, XpOpenCity.class); xpOpenCity.setCreateBy(SecurityUtils.getLoginUser().getSysUser().getNickName()); save(xpOpenCity); } /** * 校验开通城市是否已添加 * @param dto * @param excludeOpenCityId */ private void checkIfExist(OpenCityAddDTO dto, Long excludeOpenCityId) { Long count = lambdaQuery().eq(XpOpenCity::getProvinceCode, dto.getProvinceCode()) .eq(XpOpenCity::getCityCode, dto.getCityCode()) .eq(StringUtils.isNotBlank(dto.getDistrictCode()), XpOpenCity::getDistrictCode, dto.getDistrictCode()) .ne(Objects.nonNull(excludeOpenCityId), XpOpenCity::getId, excludeOpenCityId) .count(); if (count > 0) { throw new ServiceException("该城市已存在", I18nKeyConstant.OPEN_CITY_EXISTS); } } @Override public void edit(OpenCityEditDTO dto) { checkIfExist(dto, dto.getId()); XpOpenCity xpOpenCity = BeanUtil.copyProperties(dto, XpOpenCity.class); xpOpenCity.setUpdateBy(SecurityUtils.getLoginUser().getSysUser().getNickName()); updateById(xpOpenCity); } @Override public void deleteById(Long id) { removeById(id); } @Override public void upDown(Long id, Integer status) { lambdaUpdate().eq(XpOpenCity::getId, id).set(XpOpenCity::getStatus, status).update(); } } xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/utils/TaskOrderUtil.java
@@ -100,10 +100,11 @@ String firstElement = redisService.getFirstElement(name); if (firstElement != null) { String[] split = firstElement.toString().split("_"); // 如果市未处理 创建新任务 // 如果未处理 创建新任务 if (split[2].equals("0")) { String value = split[0] + "_" + split[1] + "_1" + "_"+split[3] + "_"+ split[4] + "_"+ split[5] + "_"+ split[6] + "_"+ split[7]; // 状态改为处理中 // redisService.updateFirst(name, split[0] + "_" + split[1] + "_1"+split[3]+split[4]+split[5]+split[6]); redisService.updateFirst(name, value); threadPoolTaskExecutor.submit(() -> { orderProcess(name, firstElement.toString()); }); @@ -611,8 +612,11 @@ // // VehiclePositionVo vehiclePositionVo1 = vehiclePositionVo; RLock lock = redissonClient.getLock("FIND_CAR_" + vehiclePositionVo.getVin()); lock.lock(10, TimeUnit.SECONDS); System.err.println("队列:"+quoteName+" 车辆:"+vehiclePositionVo.getVin()+"订单"+orderNo); try { // VehiclePosition vehiclePosition = vehiclePositionService.findByVehicleId(vehiclePositionVo.getVehicle_id()); R<XpVehicle> vehiclePositionR = xpVehicleClient.findByVehicleVin(vehiclePositionVo.getVin()); @@ -635,7 +639,7 @@ BaseVehicleUserDto baseVehicleUserDto = userVehicleClient.addTaxiPassenger(addTaxiPassengerVo); if (baseVehicleUserDto.getCode() == 200) { if (baseVehicleUserDto!=null && baseVehicleUserDto.getCode() == 200) { // TODO 下单 // 绑定成功 弹出这个队列第一个订单 redisService.popFirstElement(quoteName); xp-robotaxi-operation-boot/src/main/resources/i18n/messages.properties
@@ -11,4 +11,5 @@ fence.structure.not.exist=围栏构成不存在 fence.not.exist=围栏不存在 fence.status.cannot.copied=围栏启用状态不可复制; order.create.fail=订单创建失败 order.create.fail=订单创建失败 open.city.exists=该城市已存在 xp-robotaxi-operation-boot/src/main/resources/i18n/messages_en_US.properties
@@ -11,4 +11,5 @@ fence.structure.not.exist=The fence composition does not exist\ fence.status.cannot.copied=The enabled state of the fence cannot be copied; fence.not.exist=The fence does not exist. order.create.fail=Order creation failed order.create.fail=Order creation failed open.city.exists=This city already exists xp-robotaxi-operation-boot/src/main/resources/i18n/messages_zh_CN.properties
@@ -11,4 +11,5 @@ fence.structure.not.exist=围栏构成不存在 fence.not.exist=围栏不存在 fence.status.cannot.copied=围栏启用状态不可复制 order.create.fail=订单创建失败 order.create.fail=订单创建失败 open.city.exists=该城市已存在 xp-robotaxi-operation-boot/src/main/resources/mapper/operation/XpWhitelistOperlogMapper.xml
@@ -11,14 +11,14 @@ id,operation_name as operationName, operation_account as operationAccount, operation_type as operationType, operation_content as operationContent, operation_ip as operationIp, operation_account_id as operationAccountId, is_deleted as deleted, create_time as createTime, update_time as updateTime FROM xp_whitelist_operlog WHERE is_deleted = 0 <if test="account != null and account != ''"> AND operation_account LIKE CONCAT('%',#{account},'%') <if test="query.operationName != null and query.operationName != ''"> AND operation_name LIKE CONCAT('%',#{query.operationName},'%') </if> <if test="operationName != null and operationName != ''"> AND operation_name LIKE CONCAT('%',#{operationName},'%') <if test="query.operationType != null"> AND operation_type = #{query.operationType} </if> <if test="operationType != null"> AND operation_type = #{operationType} <if test="query.startTime != null and query.startTime != '' and query.endTime != null and query.endTime != ''"> and create_time between #{query.startTime} and #{query.endTime} </if> order by create_time desc </select> @@ -27,14 +27,14 @@ id,operation_name as operationName, operation_account as operationAccount, operation_type as operationType, operation_content as operationContent, operation_ip as operationIp, operation_account_id as operationAccountId, is_deleted as deleted, create_time as createTime, update_time as updateTime FROM xp_whitelist_operlog WHERE is_deleted = 0 <if test="account != null and account != ''"> AND operation_account LIKE CONCAT('%',#{account},'%') <if test="query.operationName != null and query.operationName != ''"> AND operation_name LIKE CONCAT('%',#{query.operationName},'%') </if> <if test="operationName != null and operationName != ''"> AND operation_name LIKE CONCAT('%',#{operationName},'%') <if test="query.operationType != null"> AND operation_type = #{query.operationType} </if> <if test="operationType != null"> AND operation_type = #{operationType} <if test="query.startTime != null and query.startTime != '' and query.endTime != null and query.endTime != ''"> and create_time between #{query.startTime} and #{query.endTime} </if> order by create_time desc </select> xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/controller/XpOrderController.java
@@ -138,7 +138,7 @@ * @return 操作结果 */ @PostMapping("/inner/order/cancelOrder") public R<?> cancelOrder(String orderId){ public R<?> cancelOrder(@RequestParam("orderId") String orderId){ xpOrderService.cancelOrder(orderId); return R.ok(); } xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/dto/MileageDto.java
New file @@ -0,0 +1,19 @@ package com.xiaopeng.order.dto; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import javax.validation.constraints.NotNull; @Data @ApiModel(value = "计算里程费") public class MileageDto { @ApiModelProperty(value = "时段标识") private Integer mileageTypeTemp; @ApiModelProperty(value = "num6") private String mileage; @ApiModelProperty(value = "num7") private String time; } xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/model/query/XpHistoryOrderListQuery.java
@@ -9,6 +9,6 @@ @ApiModel(value = "XpHistoryOrderListQuery", description = "历史订单列表查询参数") public class XpHistoryOrderListQuery extends BasePage { @ApiModelProperty(value = "订单状态 0=已取消,6=待支付,8=已完成") @ApiModelProperty(value = "订单状态9=待评价,10=已完成") private Integer status; } xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/impl/XpOrderServiceImpl.java
@@ -1,8 +1,8 @@ package com.xiaopeng.order.service.impl; import cn.hutool.core.bean.BeanUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; @@ -19,23 +19,22 @@ import com.xiaopeng.api.dto.vehicleConvoy.BaseVehicleUserDto; import com.xiaopeng.api.feignClient.account.XpAppUserClient; import com.xiaopeng.api.feignClient.account.XpAppUserCouponClient; import com.xiaopeng.api.feignClient.operation.XpPointClient; import com.xiaopeng.api.feignClient.operation.XpPricingClient; import com.xiaopeng.api.feignClient.operation.XpSystemSettingClient; import com.xiaopeng.api.feignClient.pushMessage.XpPushMessageClient; import com.xiaopeng.api.feignClient.vehicleConvoy.UserVehicleClient; import com.xiaopeng.api.feignClient.vehicleConvoy.XpFleetClient; import com.xiaopeng.api.feignClient.vehicleConvoy.XpFleetVehicleClient; import com.xiaopeng.api.feignClient.vehicleConvoy.XpVehicleClient; import com.xiaopeng.api.feignClient.operation.XpPointClient; import com.xiaopeng.api.feignClient.operation.XpPricingClient; import com.xiaopeng.api.model.account.XpAppUser; import com.xiaopeng.api.model.operation.XpPoint; import com.xiaopeng.api.model.operation.XpPricing; import com.xiaopeng.api.model.vehicleConvoy.XpFleet; import com.xiaopeng.api.model.vehicleConvoy.XpVehicle; import com.xiaopeng.api.model.order.XpOrder; import com.xiaopeng.api.model.order.XpOrderCancel; import com.xiaopeng.api.model.order.XpOrderEvaluate; import com.xiaopeng.api.model.order.XpOrderReassign; import com.xiaopeng.api.model.vehicleConvoy.XpFleet; import com.xiaopeng.api.model.vehicleConvoy.XpVehicle; import com.xiaopeng.api.vo.account.UserCouponVo; import com.xiaopeng.api.vo.order.*; import com.xiaopeng.api.vo.vehicleConvoy.CancelTaxiPassengerVo; @@ -52,10 +51,13 @@ import com.xiaopeng.common.redis.service.RedisService; import com.xiaopeng.common.security.utils.SecurityUtils; import com.xiaopeng.order.constant.I18nKeyConstant; import com.xiaopeng.order.dto.MileageDto; import com.xiaopeng.order.job.base.QuartzManager; import com.xiaopeng.order.job.base.TimeJobType; import com.xiaopeng.order.job.jobs.EndTripJob; import com.xiaopeng.order.job.jobs.OrderCancelJob; import com.xiaopeng.order.mangodb.model.OrderTrajectory; import com.xiaopeng.order.mangodb.service.OrderTrajectoryService; import com.xiaopeng.order.mangodb.model.OrderTrajectory; import com.xiaopeng.order.mapper.XpOrderMapper; import com.xiaopeng.order.model.dto.XpOrderCommentDto; import com.xiaopeng.order.model.dto.XpOrderPayDto; @@ -66,10 +68,7 @@ import com.xiaopeng.order.service.IXpOrderEvaluateService; import com.xiaopeng.order.service.IXpOrderReassignService; import com.xiaopeng.order.service.IXpOrderService; import com.xiaopeng.order.job.base.QuartzManager; import com.xiaopeng.order.job.base.TimeJobType; import com.xiaopeng.order.job.jobs.OrderCancelJob; import io.swagger.annotations.ApiModelProperty; import com.xiaopeng.order.utils.holiday.HolidayUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeanUtils; @@ -85,10 +84,8 @@ import java.math.BigDecimal; import java.math.RoundingMode; import java.time.Duration; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.text.SimpleDateFormat; import java.time.*; import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; @@ -124,6 +121,8 @@ @Autowired private MongoTemplate mongoTemplate; @Autowired private HolidayUtil holidayUtil; // @Autowired // private XpPricingUtil xpPricingUtil; @@ -757,6 +756,7 @@ xpOrderCancel.setUserId(1l); xpOrderCancel.setCancelSource(4); xpOrderCancel.setCancelReason("系统取消订单"); xpOrderCancel.setCreateTime(LocalDateTime.now()); xpOrderCancelService.save(xpOrderCancel); @@ -873,6 +873,14 @@ @Override public void endTrip(Long orderId, String uid) { // 起步价格明细 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("订单不存在"); @@ -882,77 +890,8 @@ throw new RuntimeException("订单状态错误"); } order.setStatus(8); // 计算实际价格 // 根据订单所属开通城市id 查询定价 R<XpPricing> pricingByCityId = xpPricingClient.getPricingByCityId(order.getCityId()); XpPricing pricing = pricingByCityId.getData(); if (pricing == null) { throw new ServiceException("当前城市未配置定价规则", I18nKeyConstant.ORDER_PRICING_NOT_EXISTS); } // 查询订单轨迹 List<OrderTrajectory> trajectories = mongoTemplate.find( new Query(Criteria.where("orderId").is(orderId)) .with(Sort.by(Sort.Direction.ASC, "locatetime")), OrderTrajectory.class ); // 计算等待时间 LocalDateTime boardingTime = order.getBoardingTime();// 上车时间 LocalDateTime arrivalTime = order.getArrivalTime();// 车辆到达上车点时间 // 行程开始时间 Integer waitTime =Math.abs( (int) Duration.between(boardingTime, arrivalTime).getSeconds()); // 计算订单总行驶距离,单位米 Double totalDistance = 0.00D; // 行程总时间 秒 LocalDateTime tripBegin = order.getTripBegin(); LocalDateTime tripEnd = order.getTripEnd(); long seconds = Duration.between(tripBegin, tripEnd).getSeconds(); for (OrderTrajectory trajectory : trajectories) { } // XpPricingCardVo xpPricingCardVo = xpPricingUtil.calculationPrice( // order.getAppUserId().toString(),JSON.parseObject(pricing.getBasicPricingJson()), // JSON.parseArray(pricing.getStartingFareJson()), // pricing.getDynamicPricingJson(), // pricing.getIsDynamicPrice(), // totalDistance, // seconds, // order.getCityId() // ); // Long id = pricing.getId(); // Integer pricingType = pricing.getPricingType(); // String basicPricingJson = pricing.getBasicPricingJson(); // String startingFareJson = pricing.getStartingFareJson(); // String dynamicPricingJson = pricing.getDynamicPricingJson(); // String priceRule = pricing.getPriceRule(); // // 根据计价规则计算费用 // BigDecimal startMoney = BigDecimal.ZERO;// 起步价 // BigDecimal mileageMoney = BigDecimal.ZERO;// 里程费 // BigDecimal durationMoney = BigDecimal.ZERO;// 时长费 // BigDecimal waitMoney = BigDecimal.ZERO;// 等待费 // BigDecimal longDistancemoney = BigDecimal.ZERO;// 远途费 // BigDecimal redPacketMoney = BigDecimal.ZERO;// 红包抵扣金额 // BigDecimal couponMoney = BigDecimal.ZERO;// 优惠券抵扣金额 // if (pricingType == 1) { // // } else { // // } // if (org.springframework.util.StringUtils.hasLength(basicPricingJson)) { // // } // if (org.springframework.util.StringUtils.hasLength(startingFareJson)) { // // } // if (org.springframework.util.StringUtils.hasLength(dynamicPricingJson)) { // // } // if (org.springframework.util.StringUtils.hasLength(priceRule)) { // // } // 计算完价格后 挑选优惠券 GetUserCouponDto getUserCouponDto = new GetUserCouponDto(); // 计算完价格后 挑选优惠券 GetUserCouponDto getUserCouponDto = new GetUserCouponDto(); getUserCouponDto.setUserId(Long.valueOf(uid)); getUserCouponDto.setTotalAmount(order.getOrderMoney().doubleValue()); R<UserCouponVo> userCouponVoR = xpAppUserCouponClient.getUserCoupon(getUserCouponDto); @@ -974,7 +913,557 @@ userVehicleClient.cancelTaxiPassenger(cancelTaxiPassengerVo); } // // 计算实际价格 // // 根据订单所属开通城市id 查询定价 // R<XpPricing> pricingByCityId = xpPricingClient.getPricingByCityId(order.getCityId()); // XpPricing pricing = pricingByCityId.getData(); // if (pricing == null) { // throw new ServiceException("当前城市未配置定价规则", I18nKeyConstant.ORDER_PRICING_NOT_EXISTS); // } // // 查询订单轨迹 // List<OrderTrajectory> trajectories = mongoTemplate.find( // new Query(Criteria.where("orderId").is(orderId)) // .with(Sort.by(Sort.Direction.ASC, "locatetime")), // OrderTrajectory.class // ); // // 计算等待时间 // LocalDateTime boardingTime = order.getBoardingTime();// 上车时间 // LocalDateTime arrivalTime = order.getArrivalTime();// 车辆到达上车点时间 // // 行程开始时间 // Integer waitTime = Math.abs((int) Duration.between(boardingTime, arrivalTime).getSeconds()); // // // 行程总时间 秒 // LocalDateTime tripBegin = order.getTripBegin(); // LocalDateTime tripEnd = order.getTripEnd(); // long seconds = Duration.between(tripBegin, tripEnd).getSeconds(); // // 默认费率1 // double priceRate = 1; // if (pricing.getIsDynamicPrice() == 1) { // // 先判断是否已经调价过了 // Object userDynamicPriceObj = redisService.getCacheObject("USER_DYNAMIC_PRICE_" + order.getCityId() + "_" + uid); // if (userDynamicPriceObj != null) { // // 是否调价过了 // priceRate = Double.parseDouble(userDynamicPriceObj.toString()); // } else { // // 没有调过 // //是否触发动态调价 redis获取 // Object dynamicPriceObj = redisService.getCacheObject("DYNAMIC_PRICE_" + order.getCityId()); // if (dynamicPriceObj != null) { // JSONObject jsonObject = JSONObject.parseObject(pricing.getDynamicPricingJson()); // // 触发动态调价 // // value=时间 // String timeStr = dynamicPriceObj.toString(); // // 判断当前距离这个时间范围的新增订单数(排除取消) / 上浮数量 算出应该增加的价格率 // Integer num = this.getOrderNum(new GetOrderNumDto(timeStr, order.getCityId())); // // X单上升一次调价 // Integer orderNum = getOrderNum(jsonObject, 4); // // 需要调价的次数 // int count = num / orderNum; // if (count > 0) { // // 计算每次上升的费率 // Integer upRate = getOrderNum(jsonObject, 5); // BigDecimal divide = new BigDecimal(upRate).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP); // for (int i = count; i > 0; i--) { // // 当前费率+(费率*上升百分比) // priceRate = new BigDecimal(priceRate).multiply(divide).add(new BigDecimal(priceRate)).setScale(2, RoundingMode.HALF_UP).doubleValue(); // } // // 冷却时间 // Integer time = getOrderNum(jsonObject, 6); // // 触发调价了 // redisService.setCacheObject("USER_DYNAMIC_PRICE_" + order.getCityId() + "_" + uid, priceRate, time.longValue(), TimeUnit.MINUTES); // } // // } // } // } // if (trajectories.isEmpty()) { // return; // } // // 起步价规则 // String startingFareJson = pricing.getStartingFareJson(); // JSONArray startingFareJsonArray = JSONArray.parseArray(startingFareJson); // // 基础信息规则 // String basicPricing = pricing.getBasicPricingJson(); // JSONObject basicPricingJson = JSONObject.parseObject(basicPricing); // // // 计算过后得到起步价 // String num3 = ""; // String num4 = ""; // String num5 = ""; // String num6 = ""; // String num7 = ""; // String startTime = ""; // String endTime = ""; // // 等待费设置 // String num4Wait = basicPricingJson.getString("num4"); // String num5Wait = basicPricingJson.getString("num5"); // // 远途费设置 // String numLong6 = basicPricingJson.getString("num6"); // String numLong7 = basicPricingJson.getString("num7"); // String numLong8 = basicPricingJson.getString("num8"); // String numLong9 = basicPricingJson.getString("num9"); // String numLong10 = basicPricingJson.getString("num10"); // String numLong11 = basicPricingJson.getString("num11"); // String numLong12 = basicPricingJson.getString("num12"); // String numLong13 = basicPricingJson.getString("num13"); // String numLong14 = basicPricingJson.getString("num14"); // String numLong15 = basicPricingJson.getString("num15"); // // 节假日基础价格规则 // String num29 = basicPricingJson.getString("num29"); // String num30 = basicPricingJson.getString("num30"); // String num31 = basicPricingJson.getString("num31"); // String num32 = basicPricingJson.getString("num32"); // String num33 = basicPricingJson.getString("num33"); // String num34 = basicPricingJson.getString("num34"); // // // 高峰期基础价格规则 // String num21 = basicPricingJson.getString("num21"); // String num22 = basicPricingJson.getString("num22"); // String num23 = basicPricingJson.getString("num23"); // String num24 = basicPricingJson.getString("num24"); // String num25 = basicPricingJson.getString("num25"); // String num26 = basicPricingJson.getString("num26"); // String num27 = basicPricingJson.getString("num27"); // String num28 = basicPricingJson.getString("num28"); // // 夜间行驶基础价格规则 // String num14 = basicPricingJson.getString("num14"); // String num15 = basicPricingJson.getString("num15"); // String num16 = basicPricingJson.getString("num16"); // String num17 = basicPricingJson.getString("num17"); // String num18 = basicPricingJson.getString("num18"); // String num19 = basicPricingJson.getString("num19"); // String num20 = basicPricingJson.getString("num20"); // // 起步里程 // Double startDistance = 0.00D; // // 临时超出起步里程的额外里程 // Double mileageDistance = 0.00D; // // 起步价所属时段 type 1 普通时段 2 夜间行驶时段 3 高峰时段 4 节假日时段 // Integer startType = 1; // // 里程费所属时段 type 0默认临时 1 普通时段 2 夜间行驶时段 3 高峰时段 4 节假日时段 // Integer mileageType = 0; // // 里程费所属时段临时字段 用于区分时段是否变化 type 0默认临时 1 普通时段 2 夜间行驶时段 3 高峰时段 4 节假日时段 // Integer mileageTypeTemp = 0; // // // 起步价时段开始时间 // Date startDate = DateUtils.toDate(order.getTripBegin()); // // 根据订单创建时间确定使用哪个起步价规则 // Date createTime = DateUtils.toDate(order.getCreateTime()); // // 起步价时段结束时间 // Date endDate = new Date(); // // 计算订单总行驶距离,单位米 // Double totalDistance = 0.00D; // // 起步价计算完成表示 // boolean startFlag = true; // SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm"); // if (pricing.getPricingType() == 1) { // // // 计算起步价 // for (Object o : startingFareJsonArray) { // JSONObject startingFare = (JSONObject) o; // Calendar calendarStart = Calendar.getInstance(); // calendarStart.setTime(createTime); // 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(createTime); // 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 (createTime.getTime() > calendarStart.getTimeInMillis() && createTime.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(createTime); // 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(createTime); // 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 (createTime.getTime() > calendarStart.getTimeInMillis() && createTime.getTime() < calendarEnd.getTimeInMillis() // ) { // num3 = num15; // num6 = num16; // num7 = num17; // startType = 2; // // } // // 判断是否为高峰期 // Calendar calendar1Start = Calendar.getInstance(); // calendar1Start.setTime(createTime); // 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(createTime); // 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(createTime); // 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(createTime); // 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 ((createTime.getTime() > calendar1Start.getTimeInMillis() && createTime.getTime() < calendar1End.getTimeInMillis()) // || (createTime.getTime() > calendar2Start.getTimeInMillis() && createTime.getTime() < calendar2End.getTimeInMillis())) { // num3 = num23; // num6 = num24; // num7 = num25; // startType = 3; // } // // 判断当前是否属于节假日 // boolean holiday = holidayUtil.isHoliday(DateUtils.toLocalDate(createTime)); // if (holiday) { // // 判断是否为节假日 起步价修改 // num3 = num29; // num6 = num30; // num7 = num31; // startType = 4; // } // // 查询起步价结束时间段 // for (OrderTrajectory trajectory : trajectories) { // Long locatetime = trajectory.getLocatetime(); // endDate = new Date(locatetime); // LocalDateTime end = DateUtils.dateToLocalDateTime(endDate); // if (end.isAfter(tripBegin)) { // // 行程开始 开始计算里程数 // if (startDistance >= Double.parseDouble(num4)) { // // 行驶里程大于基础配置里程 那么起步价时段计算完成 // break; // } // startDistance += trajectory.getDistance(); // } // } // // 起步价价格明细封装 // FareBreakdownDetailVo startDetail = new FareBreakdownDetailVo(); // startDetail.setType(startType); // startDetail.setTime(timeFormat.format(startDate) + "-" + timeFormat.format(endDate)); // startDetail.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(); // startDetail.setDuration(minutesDifference); // startDetail.setMoney(new BigDecimal(num3)); // start.add(startDetail); // // // // 计算起步价格、里程、时段、行驶时间 //// calculateStartingFare(trajectories, tripBegin, startFlag, startDistance, startingFareJsonArray, num3, num4, num5, endDate, order, num14, num15, num16, num17, startType, num21, num22, num23, num24, num25, num29, num30, num31, timeFormat, startDate, start); // // for (OrderTrajectory trajectory : trajectories) { //// FareBreakdownDetailVo mileageDetail = new FareBreakdownDetailVo(); //// mileageDetail.setType(); //// mileageDetail.setTime(); //// mileageDetail.setMileage(); //// mileageDetail.setDuration(); //// mileageDetail.setMoney(); // // Long locatetime = trajectory.getLocatetime(); // Date mileageDate = new Date(locatetime); // LocalDateTime begin = DateUtils.dateToLocalDateTime(mileageDate); // if (begin.isAfter(tripBegin)) { // totalDistance += trajectory.getDistance(); // if (totalDistance >= startDistance) { // // 开始累加起步里程 // mileageDistance += trajectory.getDistance(); // // 判断当前里程费属于什么时段 //// mileageTypeTemp // MileageDto param = new MileageDto(); // param.setMileage(num6); // param.setTime(num7); // param.setMileageTypeTemp(mileageTypeTemp); // MileageDto mileageDto = mileageType(param,startingFareJsonArray, mileageDate, begin, num14, num16, num17, num21, num22, num24, num25, num30, num31); // if (mileageType==0){ // // 首次 // mileageType = mileageDto.getMileageTypeTemp(); // mileageTypeTemp= mileageDto.getMileageTypeTemp(); // } else if (mileageType!=mileageTypeTemp){ // // 新时段 // FareBreakdownDetailVo fareBreakdownDetailVo = new FareBreakdownDetailVo(); // fareBreakdownDetailVo.setType(mileageType); // fareBreakdownDetailVo.setTime(timeFormat.format(endDate)+"-"+timeFormat.format(mileageDate)); // double md = mileageDistance / 1000; // fareBreakdownDetailVo.setMileage(new BigDecimal(md).setScale(2, RoundingMode.HALF_UP)); // // 计算startDate和endDate相差多少分钟 // // 将 Date 转换为 LocalDateTime // LocalDateTime startMileageTime = endDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(); // LocalDateTime endMileageTime = mileageDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(); // // // 计算时间差(单位:分钟) // long minutesDifferenceMileage = Duration.between(startMileageTime, endMileageTime).toMinutes(); // startDetail.setDuration(minutesDifference); // fareBreakdownDetailVo.setDuration(minutesDifferenceMileage); // // 如果md小数位是0 那么舍弃小数,如果小数位不为0 那么进1 // fareBreakdownDetailVo.setMileage(new BigDecimal(md).setScale(2, RoundingMode.HALF_UP)); // Integer temp = (int)md; // if (md> temp){ // temp=temp+1; // } // // 超出的里程 // fareBreakdownDetailVo.setMoney(new BigDecimal(temp*6)); // mileage.add(fareBreakdownDetailVo); // // 修改时段标识 // mileageType = mileageTypeTemp; // // 修改新时段开始时间 // endDate = mileageDate; // }else{ // // 时段未变化 // } // } // } // } // // 开始计算里程费用 // for (OrderTrajectory trajectory : trajectories) { // Long locatetime = trajectory.getLocatetime(); // Date mileageDate = new Date(locatetime); // LocalDateTime begin = DateUtils.dateToLocalDateTime(mileageDate); // // 行驶里程大于起步里程配置 开始计算里程和里程费 // if (begin.isAfter(tripBegin)) { // totalDistance += trajectory.getDistance(); // double v = totalDistance / 1000.0; // if (v >= Double.parseDouble(num5)) { // // 增加超出起步里程的里程数 // mileageDistance += trajectory.getDistance(); // // } // if (mileageType == 0) { // // 第一次计算里程费 // // } // } // } // } else { // // } // //// XpPricingCardVo xpPricingCardVo = xpPricingUtil.calculationPrice( //// order.getAppUserId().toString(),JSON.parseObject(pricing.getBasicPricingJson()), //// JSON.parseArray(pricing.getStartingFareJson()), //// pricing.getDynamicPricingJson(), //// pricing.getIsDynamicPrice(), //// totalDistance, //// seconds, //// order.getCityId() //// ); } 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(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 @@ -1072,7 +1561,7 @@ // 计算距离 // 获取上一次定位 String oldLocation = redisService.getCacheObject(CacheConstants.ORDER_OLD_LOCATION + orderTrajectory.getOrderId()); if(StringUtils.isEmpty(oldLocation)){ if (StringUtils.isEmpty(oldLocation)) { // 首次定位 xpOrder.setMileageKilometers(0D); this.updateById(xpOrder); @@ -1081,11 +1570,11 @@ orderTrajectoryService.create(orderTrajectory); // 存储首次定位 redisService.setCacheObject(CacheConstants.ORDER_OLD_LOCATION + orderTrajectory.getOrderId(), orderTrajectory.getLocation()); }else { } else { Map<String, Double> distance = GeodesyUtil.getDistance(orderTrajectory.getLocation(), oldLocation); if(null != distance){ if (null != distance) { Double distance1 = distance.get("WGS84"); if(distance1 > 50 && xpOrder.getStatus() == 6){//大于50米表示在移动,且在服务中的时候记录里程 if (distance1 > 50 && xpOrder.getStatus() == 6) {//大于50米表示在移动,且在服务中的时候记录里程 xpOrder.setMileageKilometers(BigDecimal.valueOf(xpOrder.getMileageKilometers()).add(new BigDecimal(distance1)).setScale(2, RoundingMode.HALF_EVEN).doubleValue()); this.updateById(xpOrder); // 存储本次距离 @@ -1093,7 +1582,7 @@ // 存储mongo数据 orderTrajectoryService.create(orderTrajectory); } }else{ } else { log.error("调用高德计算距离出错"); } } xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/utils/GpsPoint.java
New file @@ -0,0 +1,13 @@ package com.xiaopeng.order.utils; import lombok.Data; import java.util.Date; @Data public class GpsPoint { private Double distance; // 与上一点距离(米) private Long locatetime; // 毫秒时间戳 private Date createTime; // 创建日期 // ... getter & setter } xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/utils/TaxiPricingDemoService.java
New file @@ -0,0 +1,221 @@ package com.xiaopeng.order.utils; import java.math.BigDecimal; import java.math.RoundingMode; import java.time.LocalTime; import java.time.ZoneId; import java.util.*; import java.util.stream.Collectors; /** * 出租车计费引擎服务 */ public class TaxiPricingDemoService { public static void main(String[] args) { PricingConfig config = setupMockConfig(); Calendar cal = Calendar.getInstance(); cal.set(2023, Calendar.OCTOBER, 10, 16, 50, 0); Date orderTime = cal.getTime(); // 模拟总里程 25.0 km,以触发多级远途加价 double totalMillige = 25.0; // 模拟 GPS 轨迹点 List<GpsPoint> points = createMockGpsPoints(orderTime); TaxiPricingService service = new TaxiPricingService(); Map<String, BigDecimal> bill = service.calculateOrderPrice(orderTime, totalMillige, points, config); System.err.println("======= 订单计费账单 ======="); bill.forEach((detail, price) -> System.err.println(detail + ": " + price + " 元")); BigDecimal totalAmount = bill.values().stream().reduce(BigDecimal.ZERO, BigDecimal::add); System.err.println("--------------------------"); System.err.println("应付总额: " + totalAmount + " 元"); } public static class TaxiPricingService { public Map<String, BigDecimal> calculateOrderPrice(Date orderTime, double totalMileageKm, List<GpsPoint> points, PricingConfig config) { Map<String, BigDecimal> result = new LinkedHashMap<>(); BigDecimal totalMileage = BigDecimal.valueOf(totalMileageKm); PricingRule baseRule = config.findMatchedRule(orderTime); result.put("起步价(" + baseRule.ruleName + ")", baseRule.baseFare); if (totalMileage.compareTo(baseRule.baseKm) <= 0) { return formatMap(result); } BigDecimal remainBaseM = baseRule.baseKm.multiply(new BigDecimal("1000")); BigDecimal remainBaseMs = baseRule.baseMin.multiply(new BigDecimal("60000")); BigDecimal currentTotalM = BigDecimal.ZERO; for (int i = 1; i < points.size(); i++) { GpsPoint curr = points.get(i); GpsPoint prev = points.get(i - 1); BigDecimal segM = BigDecimal.valueOf(curr.getDistance()); BigDecimal segMs = BigDecimal.valueOf(curr.getLocatetime() - prev.getLocatetime()); currentTotalM = currentTotalM.add(segM); // 累计当前已行驶的总里程 PricingRule activeRule = config.findMatchedRule(new Date(curr.getLocatetime())); // 扣除起步额度逻辑 BigDecimal billM = BigDecimal.ZERO; if (remainBaseM.compareTo(BigDecimal.ZERO) > 0) { if (segM.compareTo(remainBaseM) > 0) { billM = segM.subtract(remainBaseM); remainBaseM = BigDecimal.ZERO; } else { remainBaseM = remainBaseM.subtract(segM); } } else { billM = segM; } BigDecimal billMs = BigDecimal.ZERO; if (remainBaseMs.compareTo(BigDecimal.ZERO) > 0) { if (segMs.compareTo(remainBaseMs) > 0) { billMs = segMs.subtract(remainBaseMs); remainBaseMs = BigDecimal.ZERO; } else { remainBaseMs = remainBaseMs.subtract(segMs); } } else { billMs = segMs; } // 1. 基础费率计算 BigDecimal distFee = billM.divide(new BigDecimal(1000), 10, RoundingMode.HALF_UP).multiply(activeRule.perKmRate); BigDecimal timeFee = billMs.divide(new BigDecimal(60000), 10, RoundingMode.HALF_UP).multiply(activeRule.perMinRate); // 2. 远途加价计算 (基于区间判定) BigDecimal longExtra = calculateLongDistExtra(currentTotalM, billM, config.longSteps); BigDecimal totalSeg = distFee.add(timeFee).add(longExtra); result.put(activeRule.ruleName, result.getOrDefault(activeRule.ruleName, BigDecimal.ZERO).add(totalSeg)); } return formatMap(result); } /** * 远途加价核心逻辑:判断当前行驶到的里程处于哪个加价区间 */ private BigDecimal calculateLongDistExtra(BigDecimal currentTotalM, BigDecimal billM, List<LongRangeStep> steps) { if (billM.compareTo(BigDecimal.ZERO) <= 0) return BigDecimal.ZERO; // 当前点所在的公里数 BigDecimal currentKm = currentTotalM.divide(new BigDecimal(1000), 10, RoundingMode.HALF_UP); BigDecimal extraRate = BigDecimal.ZERO; // 匹配区间规则 for (LongRangeStep step : steps) { if (step.isMatch(currentKm)) { extraRate = step.addPrice; break; } } return billM.divide(new BigDecimal(1000), 10, RoundingMode.HALF_UP).multiply(extraRate); } private Map<String, BigDecimal> formatMap(Map<String, BigDecimal> map) { return map.entrySet().stream().collect(Collectors.toMap( Map.Entry::getKey, e -> e.getValue().setScale(2, RoundingMode.HALF_UP), (a, b) -> b, LinkedHashMap::new)); } } // --- 远途区间模型 --- public static class LongRangeStep { BigDecimal numStart; // 超过公里数 BigDecimal numEnd; // 不足公里数 BigDecimal addPrice; // 加收单价 public LongRangeStep(double start, double end, double price) { this.numStart = BigDecimal.valueOf(start); this.numEnd = BigDecimal.valueOf(end); this.addPrice = BigDecimal.valueOf(price); } public boolean isMatch(BigDecimal currentKm) { // [numStart, numEnd) return currentKm.compareTo(numStart) >= 0 && currentKm.compareTo(numEnd) < 0; } } public static class PricingConfig { PricingRule holiday; PricingRule peak; PricingRule night; List<PricingRule> normals = new ArrayList<>(); List<LongRangeStep> longSteps = new ArrayList<>(); // 修改为区间列表 boolean isHoliday = false; public PricingRule findMatchedRule(Date date) { LocalTime t = date.toInstant().atZone(ZoneId.systemDefault()).toLocalTime(); if (isHoliday) return holiday; if (peak.isMatch(t)) return peak; if (night.isMatch(t)) return night; for (PricingRule r : normals) if (r.isMatch(t)) return r; return normals.get(0); } } public static class PricingRule { String ruleName; LocalTime start, end; BigDecimal baseFare, baseKm, baseMin, perKmRate, perMinRate; public PricingRule(String n, String s, String e, double bf, double bk, double bm, double pkr, double pmr) { this.ruleName = n; this.start = LocalTime.parse(s); this.end = LocalTime.parse(e); this.baseFare = BigDecimal.valueOf(bf); this.baseKm = BigDecimal.valueOf(bk); this.baseMin = BigDecimal.valueOf(bm); this.perKmRate = BigDecimal.valueOf(pkr); this.perMinRate = BigDecimal.valueOf(pmr); } public boolean isMatch(LocalTime now) { return start.isBefore(end) ? (!now.isBefore(start) && !now.isAfter(end)) : (!now.isBefore(start) || !now.isAfter(end)); } } public static class GpsPoint { private double distance; private long locatetime; public GpsPoint(double d, long t) { this.distance = d; this.locatetime = t; } public double getDistance() { return distance; } public long getLocatetime() { return locatetime; } } private static PricingConfig setupMockConfig() { PricingConfig config = new PricingConfig(); config.peak = new PricingRule("高峰期", "17:00", "19:00", 15.0, 3.0, 5.0, 3.0, 0.8); config.night = new PricingRule("夜间行驶", "22:00", "05:00", 18.0, 3.0, 5.0, 3.5, 1.0); config.normals.add(new PricingRule("普通时段", "05:00", "17:00", 12.0, 3.0, 5.0, 2.4, 0.5)); // --- 远途加价区间配置 (关键修改) --- // 1. 超过 num1(12) 不足 num2(20), 加收 num3(1.2) config.longSteps.add(new LongRangeStep(12.0, 20.0, 1.2)); // 2. 超过 num4(20) 不足 num5(35), 加收 num6(2.0) config.longSteps.add(new LongRangeStep(20.0, 35.0, 2.0)); // 3. 超过 num7(35) 以后, 加收 num8(3.5) -> numEnd 传个极大值 config.longSteps.add(new LongRangeStep(35.0, 999999.0, 3.5)); return config; } private static List<GpsPoint> createMockGpsPoints(Date startTime) { List<GpsPoint> list = new ArrayList<>(); long startTs = startTime.getTime(); list.add(new GpsPoint(0, startTs)); // 模拟行驶了 20km list.add(new GpsPoint(12000, startTs + 15 * 60 * 1000)); list.add(new GpsPoint(8000, startTs + 25 * 60 * 1000)); list.add(new GpsPoint(5000, startTs + 35 * 60 * 1000)); return list; } } xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/utils/TaxiPricingService.java
New file @@ -0,0 +1,185 @@ package com.xiaopeng.order.utils; import java.math.BigDecimal; import java.math.RoundingMode; import java.time.LocalTime; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 出租车计费引擎服务 */ public class TaxiPricingService { public static void main(String[] args) { } // 1. 定义规则模型 public static class PricingRule { String ruleName; // 时段名称(如:高峰期、深夜档、普通时段A) String ruleType; // 类型:NORMAL, PEAK, NIGHT, HOLIDAY LocalTime start; LocalTime end; BigDecimal perKmRate; // 每公里价格 BigDecimal perMinRate; // 每分钟价格 // 起步价相关(仅用于订单开始时的首个规则判定) BigDecimal baseFare; BigDecimal baseKm; BigDecimal baseMin; public PricingRule(String name, String type, String start, String end, double kmRate, double minRate, double bf, double bk, double bm) { this.ruleName = name; this.ruleType = type; this.start = LocalTime.parse(start); this.end = LocalTime.parse(end); this.perKmRate = BigDecimal.valueOf(kmRate); this.perMinRate = BigDecimal.valueOf(minRate); this.baseFare = BigDecimal.valueOf(bf); this.baseKm = BigDecimal.valueOf(bk); this.baseMin = BigDecimal.valueOf(bm); } // 跨天匹配核心算法 public boolean contains(LocalTime now) { if (start.isBefore(end)) { return !now.isBefore(start) && !now.isAfter(end); } else { // 处理跨天,如 22:00 - 05:00 return !now.isBefore(start) || !now.isAfter(end); } } } /** * 计算服务 */ public Map<String, BigDecimal> calculateOrderFee( List<GpsPoint> points, PricingRule holidayRule, PricingRule peakRule, PricingRule nightRule, List<PricingRule> normalRules, List<LongDistStep> longSteps, boolean isHoliday) { if (points == null || points.size() < 2) return new HashMap<>(); // 结果存放:Key为规则名称,Value为该时段产生的超出金额 Map<String, BigDecimal> report = new HashMap<>(); // 1. 初始化起步参数 (以第一条定位的时间点决定起步规则) GpsPoint startPoint = points.get(0); PricingRule initialRule = findMatchedRule(startPoint, isHoliday, holidayRule, peakRule, nightRule, normalRules); report.put("起步价(" + initialRule.ruleName + ")", initialRule.baseFare); // 剩余可抵扣起步额度 BigDecimal remainBaseMeters = initialRule.baseKm.multiply(new BigDecimal("1000")); BigDecimal remainBaseMillis = initialRule.baseMin.multiply(new BigDecimal("60000")); BigDecimal totalDistanceMeters = BigDecimal.ZERO; // 2. 循环处理每一段定位轨迹 for (int i = 1; i < points.size(); i++) { GpsPoint curr = points.get(i); GpsPoint prev = points.get(i - 1); // 段增量 BigDecimal segM = BigDecimal.valueOf(curr.getDistance()); BigDecimal segMs = BigDecimal.valueOf(curr.getLocatetime() - prev.getLocatetime()); totalDistanceMeters = totalDistanceMeters.add(segM); // 匹配当前定位点所属的规则 PricingRule activeRule = findMatchedRule(curr, isHoliday, holidayRule, peakRule, nightRule, normalRules); String key = activeRule.ruleName; // A. 扣除起步里程后的计费里程 BigDecimal billableM = BigDecimal.ZERO; if (remainBaseMeters.compareTo(BigDecimal.ZERO) > 0) { if (segM.compareTo(remainBaseMeters) > 0) { billableM = segM.subtract(remainBaseMeters); remainBaseMeters = BigDecimal.ZERO; } else { remainBaseMeters = remainBaseMeters.subtract(segM); } } else { billableM = segM; } // B. 扣除起步时长后的计费时长 BigDecimal billableMs = BigDecimal.ZERO; if (remainBaseMillis.compareTo(BigDecimal.ZERO) > 0) { if (segMs.compareTo(remainBaseMillis) > 0) { billableMs = segMs.subtract(remainBaseMillis); remainBaseMillis = BigDecimal.ZERO; } else { remainBaseMillis = remainBaseMillis.subtract(segMs); } } else { billableMs = segMs; } // C. 计算当前段金额 = (里程费) + (时长费) + (远途费) BigDecimal distFee = billableM.divide(new BigDecimal("1000"), 10, RoundingMode.HALF_UP).multiply(activeRule.perKmRate); BigDecimal timeFee = billableMs.divide(new BigDecimal("60000"), 10, RoundingMode.HALF_UP).multiply(activeRule.perMinRate); BigDecimal extraLongFee = calculateLongDistExtra(totalDistanceMeters, billableM, longSteps); BigDecimal segTotal = distFee.add(timeFee).add(extraLongFee); // D. 归纳到对应规则 report.put(key, report.getOrDefault(key, BigDecimal.ZERO).add(segTotal)); } // 3. 格式化输出:保留两位小数 return formatResult(report); } /** * 优先级判定逻辑:节假日 > 高峰期 > 夜间 > 普通1 > 普通2... */ private PricingRule findMatchedRule(GpsPoint p, boolean isHoliday, PricingRule h, PricingRule pk, PricingRule n, List<PricingRule> normals) { if (isHoliday) return h; // 将毫秒转为当地时间进行匹配 LocalTime time = new java.sql.Time(p.getLocatetime()).toLocalTime(); if (pk.contains(time)) return pk; if (n.contains(time)) return n; for (PricingRule rule : normals) { if (rule.contains(time)) return rule; } return normals.get(0); // 兜底逻辑 } private BigDecimal calculateLongDistExtra(BigDecimal totalM, BigDecimal segM, List<LongDistStep> steps) { if (segM.compareTo(BigDecimal.ZERO) <= 0) return BigDecimal.ZERO; BigDecimal totalKm = totalM.divide(new BigDecimal("1000"), 10, RoundingMode.HALF_UP); BigDecimal rate = BigDecimal.ZERO; for (LongDistStep step : steps) { if (totalKm.compareTo(step.kmThreshold) > 0) { rate = step.addPrice; break; } } return segM.divide(new BigDecimal("1000"), 10, RoundingMode.HALF_UP).multiply(rate); } private Map<String, BigDecimal> formatResult(Map<String, BigDecimal> map) { Map<String, BigDecimal> formatted = new HashMap<>(); map.forEach((k, v) -> formatted.put(k, v.setScale(2, RoundingMode.HALF_UP))); return formatted; } // 远途阶梯配置类 public static class LongDistStep { BigDecimal kmThreshold; BigDecimal addPrice; public LongDistStep(double km, double price) { this.kmThreshold = BigDecimal.valueOf(km); this.addPrice = BigDecimal.valueOf(price); } } } xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/utils/XpPricingUtil.java
@@ -207,7 +207,6 @@ // // // 时长 // xpPricingCardVo.setDuration(realTime); // // // 时长费 // xpPricingCardVo.setDurationPrice((exceedTime * rule.getDouble("num31"))); // @@ -357,300 +356,7 @@ // // return xpPricingCardVo; // } // /** // * 行程结束后计算费用 // * @param rule 基础规则 // * @param distance 里程 // * @param duration 时间 // * @param wait 时间 // * @param waitMoney 等待费 // * @param openCityId 开通城市id // * @param isDynamicPriceStatus 是否开启动态调价 // * @return // * @throws Exception // */ //// public XpPricingCardVo calculationFinalPrice(String uid,JSONObject rule, JSONArray startingFareJson, double distance, long duration, long wait, Double waitMoney,Integer isDynamicPriceStatus,Long openCityId,String dynamicPricingJson) { //// //// //// // 默认费率1 //// double priceRate=1; //// if(isDynamicPriceStatus==1){ //// // 先判断是否已经调价过了 //// Object userDynamicPriceObj = redisService.getCacheObject("USER_DYNAMIC_PRICE_" + openCityId + "_" + uid); //// if(userDynamicPriceObj!=null){ //// // 是否调价过了 //// priceRate = Double.parseDouble(userDynamicPriceObj.toString()); //// }else { //// // 没有调过 //// //是否触发动态调价 redis获取 //// Object dynamicPriceObj = redisService.getCacheObject("DYNAMIC_PRICE_" + openCityId); //// if(dynamicPriceObj!=null){ //// JSONObject jsonObject = JSONObject.parseObject(dynamicPricingJson); //// // 触发动态调价 //// // value=时间 //// String timeStr = dynamicPriceObj.toString(); //// // 判断当前距离这个时间范围的新增订单数(排除取消) / 上浮数量 算出应该增加的价格率 //// R<Integer> r = xpOrderClient.getOrderNum(timeStr); //// if(r.getData()==200){ //// int num = r.getData(); //// // X单上升一次调价 //// Integer orderNum = getOrderNum(jsonObject, 4); //// // 需要调价的次数 //// int count = num / orderNum; //// if(count>0){ //// // 计算每次上升的费率 //// Integer upRate = getOrderNum(jsonObject, 5); //// BigDecimal divide = new BigDecimal(upRate).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP); //// for (int i = count; i > 0; i--) { //// // 当前费率+(费率*上升百分比) //// priceRate = new BigDecimal(priceRate).multiply(divide).add(new BigDecimal(priceRate)).setScale(2, RoundingMode.HALF_UP).doubleValue(); //// } //// //// // 冷却时间 //// Integer time = getOrderNum(jsonObject, 6); //// // 触发调价了 //// redisService.setCacheObject("USER_DYNAMIC_PRICE_" + openCityId + "_" + uid, priceRate,time.longValue(), TimeUnit.MINUTES); //// } //// } //// //// } //// } //// //// } //// //// //// //// XpPricingCardVo xpPricingCardVo = new XpPricingCardVo(); //// double amount = 0; //// //等待费 //// Date date = new Date(); //// // 计算出是在那个时间段内 //// for (Object o : startingFareJson) { //// 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()) { //// rule.put("num1", startingFare.get("num3")); //// rule.put("num2", startingFare.get("num4")); //// rule.put("num3", startingFare.get("num5")); //// rule.put("num4", startingFare.get("num6")); //// rule.put("num5", startingFare.get("num7")); //// //// rule.put("startTime", startingFare.getString("num1")); //// rule.put("endTime", startingFare.getString("num2")); //// break; //// } //// } //// //// //// double realDistance = distance / 1000;//实际公里 //// double realTime = duration / 60;//实际时间 //// double exceedDistance = (realDistance - rule.getDouble("num2")) < 0 ? 0 : realDistance - rule.getDouble("num2");//超出起步里程的公里 //// double exceedTime = (realTime - rule.getDouble("num3")) < 0 ? 0 : realTime - rule.getDouble("num3");//超过起步分钟数的时间 //// double longDistanceOne = 0;//远途1段 //// double longDistanceTwo = 0;//远途2段 //// double longDistanceThree = 0;//远途3段 //// //// //// //夜间服务处理逻辑 //// Calendar calendarNight = Calendar.getInstance(); //// calendarNight.setTime(date); //// calendarNight.set(Calendar.HOUR_OF_DAY, Integer.valueOf(rule.getString("num14").split(" - ")[0].split(":")[0])); //// calendarNight.set(Calendar.MINUTE, Integer.valueOf(rule.getString("num14").split(" - ")[0].split(":")[1])); //// //// Calendar calendar = Calendar.getInstance(); //// calendar.setTime(date); //// calendar.set(Calendar.HOUR_OF_DAY, Integer.valueOf(rule.getString("num14").split(" - ")[1].split(":")[0])); //// calendar.set(Calendar.MINUTE, Integer.valueOf(rule.getString("num14").split(" - ")[1].split(":")[1])); //// //// // 先判断今日是否节假日 //// boolean holiday = holidayUtil.isHoliday(); //// if (holiday) { //// if (realDistance > rule.getDouble("num6")) { //// longDistanceOne = rule.getDouble("num32") * (realDistance - rule.getDouble("num6")); //// } //// if (realDistance > rule.getDouble("num9")) { //// longDistanceTwo = rule.getDouble("num33") * (realDistance - rule.getDouble("num9")); //// } //// if (realDistance > rule.getDouble("num12")) { //// longDistanceThree = rule.getDouble("num34") * (realDistance - rule.getDouble("num12")); //// } //// amount = rule.getDouble("num29") + (exceedDistance * rule.getDouble("num30")) + (exceedTime * rule.getDouble("num31")) + (wait * waitMoney) + longDistanceOne + longDistanceTwo + longDistanceThree; //// //// //// // 起步价 //// xpPricingCardVo.setStartPrice(rule.getDouble("num29")); //// // 时间段 //// xpPricingCardVo.setTime("节假日"); //// //// //// // 里程 //// xpPricingCardVo.setDistance(realDistance); //// //// // 里程费 //// double distancePrice = (exceedDistance * rule.getDouble("num30")) + longDistanceOne + longDistanceTwo + longDistanceThree; //// xpPricingCardVo.setDistancePrice(distancePrice); //// //// // 时长 //// xpPricingCardVo.setDuration(realTime); //// //// // 时长费 //// xpPricingCardVo.setDurationPrice((exceedTime * rule.getDouble("num31"))); //// //// //// } else { //// if (date.getTime() > calendarNight.getTimeInMillis() && date.getTime() < calendar.getTimeInMillis()) { //// if (realDistance > rule.getDouble("num6")) { //// longDistanceOne = rule.getDouble("num18") * (realDistance - rule.getDouble("num6")); //// } //// if (realDistance > rule.getDouble("num9")) { //// longDistanceTwo = rule.getDouble("num19") * (realDistance - rule.getDouble("num9")); //// } //// if (realDistance > rule.getDouble("num12")) { //// longDistanceThree = rule.getDouble("num20") * (realDistance - rule.getDouble("num12")); //// } //// amount = rule.getDouble("num15") + (exceedDistance * rule.getDouble("num16")) + (exceedTime * rule.getDouble("num17")) + (wait * waitMoney) + longDistanceOne + longDistanceTwo + longDistanceThree; //// //// //// // 起步价 //// xpPricingCardVo.setStartPrice(rule.getDouble("num15")); //// // 时间段 //// xpPricingCardVo.setTime(rule.getString("num14")); //// //// // 里程 //// xpPricingCardVo.setDistance(realDistance); //// //// // 里程费 //// double distancePrice = (exceedDistance * rule.getDouble("num16")) + longDistanceOne + longDistanceTwo + longDistanceThree; //// xpPricingCardVo.setDistancePrice(distancePrice); //// //// // 时长 //// xpPricingCardVo.setDuration(realTime); //// //// // 时长费 //// xpPricingCardVo.setDurationPrice((exceedTime * rule.getDouble("num17"))); //// //// //// } else { //// //// // 高峰处理 //// Calendar calendarStart = Calendar.getInstance(); //// calendarStart.setTime(date); //// calendarStart.set(Calendar.HOUR_OF_DAY, Integer.valueOf(rule.getString("num21").split(" - ")[0].split(":")[0])); //// calendarStart.set(Calendar.MINUTE, Integer.valueOf(rule.getString("num21").split(" - ")[0].split(":")[1])); //// //// Calendar calendarEnd = Calendar.getInstance(); //// calendarEnd.setTime(date); //// calendarEnd.set(Calendar.HOUR_OF_DAY, Integer.valueOf(rule.getString("num21").split(" - ")[1].split(":")[0])); //// calendarEnd.set(Calendar.MINUTE, Integer.valueOf(rule.getString("num21").split(" - ")[1].split(":")[1])); //// //// Calendar s2 = Calendar.getInstance(); //// s2.setTime(date); //// s2.set(Calendar.HOUR_OF_DAY, Integer.valueOf(rule.getString("num22").split(" - ")[0].split(":")[0])); //// s2.set(Calendar.MINUTE, Integer.valueOf(rule.getString("num22").split(" - ")[0].split(":")[1])); //// //// Calendar e2 = Calendar.getInstance(); //// e2.setTime(date); //// e2.set(Calendar.HOUR_OF_DAY, Integer.valueOf(rule.getString("num22").split(" - ")[1].split(":")[0])); //// e2.set(Calendar.MINUTE, Integer.valueOf(rule.getString("num22").split(" - ")[1].split(":")[1])); //// //高峰时段处理逻辑 //// if ((date.getTime() > calendarStart.getTimeInMillis() && date.getTime() < calendarEnd.getTimeInMillis()) || (date.getTime() > s2.getTimeInMillis() && date.getTime() < e2.getTimeInMillis())) { //// if (realDistance > rule.getDouble("num6")) { //// longDistanceOne = rule.getDouble("num26") * (realDistance - rule.getDouble("num6")); //// } //// if (realDistance > rule.getDouble("num9")) { //// longDistanceTwo = rule.getDouble("num27") * (realDistance - rule.getDouble("num9")); //// } //// if (realDistance > rule.getDouble("num12")) { //// longDistanceThree = rule.getDouble("num28") * (realDistance - rule.getDouble("num12")); //// } //// amount = rule.getDouble("num23") + (exceedDistance * rule.getDouble("num24")) + (exceedTime * rule.getDouble("num25")) + (wait * waitMoney) + longDistanceOne + longDistanceTwo + longDistanceThree; //// //// //// // 起步价 //// xpPricingCardVo.setStartPrice(rule.getDouble("num23")); //// // 时间段 //// if (date.getTime() > calendarStart.getTimeInMillis() && date.getTime() < calendarEnd.getTimeInMillis()) { //// xpPricingCardVo.setTime(rule.getString("num21")); //// } else { //// xpPricingCardVo.setTime(rule.getString("num22")); //// } //// //// //// // 里程 //// xpPricingCardVo.setDistance(realDistance); //// //// // 里程费 //// double distancePrice = (exceedDistance * rule.getDouble("num24")) + longDistanceOne + longDistanceTwo + longDistanceThree; //// xpPricingCardVo.setDistancePrice(distancePrice); //// //// // 时长 //// xpPricingCardVo.setDuration(realTime); //// //// // 时长费 //// xpPricingCardVo.setDurationPrice((exceedTime * rule.getDouble("num25"))); //// //// } else { //// //其他时间段的计算 //// if (realDistance > rule.getDouble("num6")) { //// longDistanceOne = rule.getDouble("num8") * (realDistance - rule.getDouble("num6")); //// } //// if (realDistance > rule.getDouble("num9")) { //// longDistanceTwo = rule.getDouble("num11") * (realDistance - rule.getDouble("num9")); //// } //// if (realDistance > rule.getDouble("num12")) { //// longDistanceThree = rule.getDouble("num13") * (realDistance - rule.getDouble("num12")); //// } //// amount = rule.getDouble("num1") + (exceedDistance * rule.getDouble("num4")) + (exceedTime * rule.getDouble("num5")) + (wait * waitMoney) + longDistanceOne + longDistanceTwo + longDistanceThree; //// //// //// // 起步价 //// xpPricingCardVo.setStartPrice(rule.getDouble("num1")); //// // 时间段 //// xpPricingCardVo.setTime(rule.getString("startTime") + " - " + rule.getString("endTime")); //// //// xpPricingCardVo.setDistancePriceNormal(rule.getDouble("num2")); //// //// xpPricingCardVo.setDurationPriceNormal(rule.getDouble("num3")); //// //// //// // 里程 //// xpPricingCardVo.setDistance(realDistance); //// //// // 里程费 //// double distancePrice = (exceedDistance * rule.getDouble("num4")) + longDistanceOne + longDistanceTwo + longDistanceThree; //// xpPricingCardVo.setDistancePrice(distancePrice); //// //// // 时长 //// xpPricingCardVo.setDuration(realTime); //// //// // 时长费 //// xpPricingCardVo.setDurationPrice((exceedTime * rule.getDouble("num5"))); //// //// } //// } //// } //// //// // 订单总额 //// Double startPrice = xpPricingCardVo.getStartPrice(); //// // 先减去起步价重新计算 //// amount = amount - startPrice; //// // 动态计算费用 //// startPrice= new BigDecimal(startPrice).multiply(new BigDecimal(priceRate)).setScale(2, RoundingMode.HALF_UP).doubleValue(); //// amount = amount+startPrice; //// xpPricingCardVo.setTotalPrice(amount); //// //// //// return xpPricingCardVo; //// } // // // // /** xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/utils/holiday/HolidayUtil.java
@@ -21,8 +21,7 @@ private RedisService redisService; public boolean isHoliday() { LocalDate now = LocalDate.now(); public boolean isHoliday(LocalDate now) { // 判断缓存里面是否有节假日算法 if (redisService.getCacheObject(CacheConstants.IS_HOLIDAY+now) != null && ("1").equals(redisService.getCacheObject(CacheConstants.IS_HOLIDAY+now).toString())) { xp-robotaxi-order-boot/src/main/resources/mapper/order/XpOrderMapper.xml
@@ -47,7 +47,7 @@ <select id="getHistoryOrderList" resultType="com.xiaopeng.order.model.vo.XpOrderHistoryListVO"> select start_point_name as originAddress, end_point_name as destinationAddress, DATE_FORMAT(create_time, '%H:%i') AS orderTime, pay_money as amount,create_time as time, id as orderId from xp_order where app_user_id = #{uid} id as orderId from xp_order where app_user_id = #{uid} and is_deleted = 0 and status in (9,10) <if test="query.status != null"> and status = #{query.status} </if> xp-robotaxi-vehicleConvoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/config/DataUpdateHandlerConfig.java
New file @@ -0,0 +1,72 @@ package com.xiaopeng.vehicleConvoy.config; import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; import com.xiaopeng.common.core.utils.StringUtils; import com.xiaopeng.common.security.utils.SecurityUtils; import lombok.extern.slf4j.Slf4j; import org.apache.ibatis.reflection.MetaObject; import org.springframework.context.annotation.Configuration; import java.time.LocalDateTime; /** * @author xiaochen * @ClassName DataUpdateInterceptor * @Description 数据更新操作处理 * @date 2021-12-15 * <p> * 注意,之前在此处注入了 JwtTokenUtils * <p> * 造成spring循环依赖,项目支棱不起来 */ @Slf4j @Configuration public class DataUpdateHandlerConfig implements MetaObjectHandler { /** * 新增数据执行 * * @param metaObject */ @Override public void insertFill(MetaObject metaObject) { String userName = ""; try{ // 获取登录信息 userName = SecurityUtils.getLoginUser().getSysUser().getNickName(); log.info("DataUpdateInterceptor insertFill"); } catch (Exception e) { log.error("DataUpdateInterceptor insertFill error:{}", e.getMessage()); } if (StringUtils.isNotBlank(userName)) { this.setFieldValByName("createBy", userName, metaObject); this.setFieldValByName("updateBy", userName, metaObject); } else { this.setFieldValByName("createBy", userName, metaObject); this.setFieldValByName("updateBy", userName, metaObject); } this.setFieldValByName("createTime", LocalDateTime.now(), metaObject); this.setFieldValByName("updateTime", LocalDateTime.now(), metaObject); this.setFieldValByName("createBy", userName, metaObject); } /** * 修改数据执行 * * @param metaObject */ @Override public void updateFill(MetaObject metaObject) { String userName = ""; try{ // 获取登录信息 userName = SecurityUtils.getLoginUser().getSysUser().getNickName(); log.info("DataUpdateInterceptor insertFill"); } catch (Exception e) { log.error("DataUpdateInterceptor insertFill error:{}", e.getMessage()); } this.setFieldValByName("updateTime", LocalDateTime.now(), metaObject); this.setFieldValByName("updateBy", userName, metaObject); } } xp-robotaxi-vehicleConvoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/controller/XpFleetController.java
@@ -56,7 +56,7 @@ * @param dto 添加车队DTO * @return 操作结果 */ @ApiOperation(value = "添加车队", tags = {"管理后台-车队管理"}) @ApiOperation(value = "添加车队", tags = {"管理后台-车队管理"}, notes = "打卡地列表接口在xp-robotaxi-maintenance-boot,人员列表在xp-robotaxi-account-boot") @PostMapping("/api/v1/fleet/add") public R<Void> add(@Valid @RequestBody FleetAddDTO dto) { return xpFleetService.add(dto); xp-robotaxi-vehicleConvoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/controller/XpFleetVehicleController.java
@@ -2,7 +2,6 @@ import com.xiaopeng.common.core.domain.R; import com.xiaopeng.vehicleConvoy.service.IXpFleetVehicleService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import org.springframework.web.bind.annotation.GetMapping; @@ -21,7 +20,6 @@ * @since 2026-02-03 */ @RestController @Api(tags = "车队车辆管理") public class XpFleetVehicleController { @Resource xp-robotaxi-vehicleConvoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/controller/XpVehicleController.java
@@ -90,7 +90,7 @@ * @return 车辆列表 */ @GetMapping("/inner/vehicle/queryById") public R<XpVehicle> queryById(Long id) { public R<XpVehicle> queryById(@RequestParam Long id) { return R.ok(xpVehicleService.getById(id)); } /** @@ -402,7 +402,7 @@ * @return */ @GetMapping("/inner/vehicle/findByVehicleVin") public R<XpVehicle> findByVehicleVin(String vehicleVin){ public R<XpVehicle> findByVehicleVin(@RequestParam("vehicleVin") String vehicleVin){ XpVehicle xpVehicle = xpVehicleService.getOne(new LambdaQueryWrapper<XpVehicle>().eq(XpVehicle::getVehicleVin, vehicleVin)); return R.ok(xpVehicle); } @@ -501,6 +501,14 @@ VehicleTemperatureDto vehicleTemperatureDto = xpVehicleService.queryTemperatureByOrderId(orderId,uid); return R.ok(vehicleTemperatureDto); } /** * 获取车辆不分页列表 * * @param plateNumber * @param fleetName * @return */ @ApiOperation(value = "获取车辆不分页列表", tags = {"管理后台-车队管理"}) @PostMapping("/api/v1/vehicle/list") @ApiImplicitParams({ xp-robotaxi-vehicleConvoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/mapper/XpVehicleMapper.java
@@ -87,5 +87,5 @@ * @param fleetName * @return */ List<XpVehicleVO> getList(String plateNumber, String fleetName); List<XpVehicleVO> getList(@Param("plateNumber") String plateNumber, @Param("fleetName") String fleetName); } xp-robotaxi-vehicleConvoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/service/impl/XpFleetServiceImpl.java
@@ -85,8 +85,8 @@ // 2. 校验运维人员绑定 validateUserBinding(dto.getOperationsUserIds(), null); // 3. 校验车辆绑定 validateVehicleBinding(dto.getVehicleIds(), null); // 3. 处理车辆绑定(自动解绑已绑定的车辆) handleVehicleBinding(dto.getVehicleIds(), null); // 4. 插入车队主表记录 XpFleet fleet = new XpFleet(); @@ -189,12 +189,12 @@ } /** * 校验车辆绑定 * 处理车辆绑定(自动解绑已绑定的车辆) * * @param vehicleIds 车辆ID列表 * @param excludeFleetId 排除的车队ID(编辑时使用) */ private void validateVehicleBinding(List<Long> vehicleIds, Long excludeFleetId) { private void handleVehicleBinding(List<Long> vehicleIds, Long excludeFleetId) { if (CollectionUtils.isEmpty(vehicleIds)) { return; } @@ -207,15 +207,8 @@ } XpFleetVehicle fleetVehicle = xpFleetVehicleService.getOne(wrapper); if (fleetVehicle != null) { // 查询车牌号和车队名称 R<XpVehicle> vehicleResult = xpVehicleClient.queryById(vehicleId); XpFleet fleet = this.getById(fleetVehicle.getFleetId()); String plateNumber = vehicleResult.getData() != null ? vehicleResult.getData().getPlateNumber() : "未知"; String fleetName = fleet != null ? fleet.getFleetName() : "未知"; List<Object> argList = new ArrayList<>(); argList.add(plateNumber); argList.add(fleetName); throw new ServiceException("车辆[" + plateNumber + "]已绑定车队[" + fleetName + "]", I18nKeyConstant.FLEET_VEHICLE_BOUND_V1, argList.toArray()); // 车辆已绑定其他车队,自动解绑 xpFleetVehicleService.lambdaUpdate().set(XpFleetVehicle::getVehicleId, fleetVehicle.getVehicleId()).remove(); } } } @@ -472,13 +465,24 @@ @Override public void addVehicle(Long fleetId, Long vehicleId) { // 查询是否已关联 // 1. 检查是否已关联当前车队 Long count = xpFleetVehicleService.lambdaQuery().eq(XpFleetVehicle::getFleetId, fleetId) .eq(XpFleetVehicle::getVehicleId, vehicleId).count(); if (count > 0) { throw new ServiceException("当前车队已和该车辆关联", I18nKeyConstant.FLEET_VEHICLE_BOUND_V2); } //保存关联关系 // 2. 检查是否已绑定其他车队,如果是则自动解绑 XpFleetVehicle existingBinding = xpFleetVehicleService.lambdaQuery() .eq(XpFleetVehicle::getVehicleId, vehicleId) .ne(XpFleetVehicle::getFleetId, fleetId) .one(); if (existingBinding != null) { // 车辆已绑定其他车队,自动解绑 xpFleetVehicleService.lambdaUpdate().eq(XpFleetVehicle::getVehicleId, vehicleId).remove(); } // 3. 保存新的关联关系 XpFleetVehicle xpFleetVehicle = new XpFleetVehicle(); xpFleetVehicle.setFleetId(fleetId); xpFleetVehicle.setVehicleId(vehicleId); xp-robotaxi-vehicleConvoy-boot/src/main/resources/mapper/vehicleConvoy/XpVehicleMapper.xml
@@ -109,7 +109,51 @@ ORDER BY create_time DESC </select> <select id="getList" resultType="com.xiaopeng.vehicleConvoy.model.vo.XpVehicleVO"> select xv.id, xv.vehicle_code, xv.vehicle_vin, xv.plate_number, xv.vehicle_model, xv.vehicle_color, xv.vehicle_pictures, xv.procurement_source, xv.remarks, xv.annual_inspection_date, xv.insurance_company, xv.insurance_phone, xv.insurance_end_time, xv.inspection_attachments, xv.policy_information, xv.electronic_id, xv.operation_qualification, xv.qualification_expire_time, xv.state, xv.status, xv.operating_status, xv.warehousing_time, xv.vehicle_type, xv.accept_or_test, xv.accept_or_test_status, xv.current_electricity, xv.is_deleted, xv.create_time, xv.update_time, xv.create_by, xv.update_by, xf.fleet_name from xp_vehicle xv left join xp_fleet_vehicle xfv on xv.id = xfv.vehicle_id left join xp_fleet xf on xfv.fleet_id = xf.id <where> xv.is_deleted = 0 <if test="plateNumber != null and plateNumber != ''"> and xv.plate_number like concat('%', #{plateNumber}, '%') </if> <if test="fleetName != null and fleetName != ''"> and xf.fleet_name like concat('%', #{fleetName}, '%') </if> </where> order by xv.create_time desc </select> </mapper>