xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/controller/UserSubscribeMessageController.java
New file @@ -0,0 +1,63 @@ package com.xiaopeng.account.controller; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.xiaopeng.account.service.IUserSubscribeMessageService; import com.xiaopeng.account.vo.UserSubscribeMessageVo; import com.xiaopeng.api.feignClient.pushMessage.SubscribeMessageClient; import com.xiaopeng.api.model.account.UserSubscribeMessage; import com.xiaopeng.common.core.domain.R; import com.xiaopeng.common.core.utils.StringUtils; import com.xiaopeng.unifiedverification.interceptor.XpClientCheck; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import java.util.List; @RestController @RequestMapping("") public class UserSubscribeMessageController { @Resource private IUserSubscribeMessageService userSubscribeMessageService; @Resource private SubscribeMessageClient subscribeMessageClient; @PostMapping("/api/v1/mini-program/subscribeMessage/saveUserSubscribeMessage") @ApiOperation(value = "授权消息模板", tags = {"小程序-优惠券"}) @XpClientCheck(checkToken = false, checkOtpToken = true) public R<?> saveUserSubscribeMessage(@RequestBody UserSubscribeMessageVo vo, HttpServletRequest request){ String uid = request.getHeader("XP-Uid"); String openid = subscribeMessageClient.getOpenId(vo.getJscode()).getData(); if(StringUtils.isEmpty(openid)){ return R.fail("获取openid失败"); } List<String> templateIds = vo.getTemplateIds(); userSubscribeMessageService.remove(new LambdaQueryWrapper<UserSubscribeMessage>() .eq(UserSubscribeMessage::getUserId, uid).in(UserSubscribeMessage::getTemplateId, templateIds)); for (String templateId : templateIds) { UserSubscribeMessage userSubscribeMessage = new UserSubscribeMessage(); userSubscribeMessage.setUserId(Long.valueOf(uid)); userSubscribeMessage.setTemplateId(templateId); userSubscribeMessage.setStatus(1); userSubscribeMessage.setOpenId(openid); userSubscribeMessage.setDeleted(false); userSubscribeMessageService.save(userSubscribeMessage); } return R.ok(); } @GetMapping("/logan/inner/v1/subscribeMessage/getUserOpenId/{v}") public R<String> getUserOpenId(@PathVariable("uid") Integer uid){ UserSubscribeMessage one = userSubscribeMessageService.getOne(new LambdaQueryWrapper<UserSubscribeMessage>() .eq(UserSubscribeMessage::getUserId, uid).last(" limit 1")); return R.ok(null != one ? one.getOpenId() : ""); } } xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/controller/XpAppUserController.java
@@ -1,17 +1,21 @@ package com.xiaopeng.account.controller; import com.xiaopeng.account.model.dto.CollectAddrDTO; import com.xiaopeng.account.service.IXpAppUserService; import com.xiaopeng.api.dto.statistics.StatisticsQueryDTO; import com.xiaopeng.api.dto.statistics.UserStatisticsDTO; import com.xiaopeng.api.model.account.XpAppUser; import com.xiaopeng.common.core.domain.R; import com.xiaopeng.unifiedverification.interceptor.XpClientCheck; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.springframework.util.CollectionUtils; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import java.util.ArrayList; import java.util.List; @@ -69,4 +73,34 @@ XpAppUser xpAppUser = xpAppUserService.getById(id); return R.ok(xpAppUser); } //-----------------------------------------------app接口------------------------------------------ /** * 获取免密结果 */ @PostMapping("/api/v1/mini-program/appUser/getPayPwd") @ApiOperation(value = "个人中心-获取免密结果", tags = {"小程序-获取免密结果"}) @XpClientCheck(checkToken = false, checkOtpToken = true) public R<Boolean> appUserGetPayPwd(HttpServletRequest request) { // uid已通过拦截器otp校验,可以直接使用 String uid = request.getHeader("XP-Uid"); //调用其他接口获取 免密结果 Boolean is = true; return R.ok(is); } /** * 设置免密结果 */ @PostMapping("/api/v1/mini-program/appUser/setPayPwd") @ApiOperation(value = "个人中心-设置免密结果", tags = {"小程序-设置免密结果"}) @XpClientCheck(checkToken = false, checkOtpToken = true) public R<?> appUserSetPayPwd(HttpServletRequest request) { // uid已通过拦截器otp校验,可以直接使用 String uid = request.getHeader("XP-Uid"); //调用其他接口设置免密结果 return R.ok(); } //-----------------------------------------------app接口------------------------------------------ } xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/mapper/UserSubscribeMessageMapper.java
New file @@ -0,0 +1,7 @@ package com.xiaopeng.account.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.xiaopeng.api.model.account.UserSubscribeMessage; public interface UserSubscribeMessageMapper extends BaseMapper<UserSubscribeMessage> { } xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/service/IUserSubscribeMessageService.java
New file @@ -0,0 +1,7 @@ package com.xiaopeng.account.service; import com.baomidou.mybatisplus.extension.service.IService; import com.xiaopeng.api.model.account.UserSubscribeMessage; public interface IUserSubscribeMessageService extends IService<UserSubscribeMessage> { } xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/service/impl/UserSubscribeMessageServiceImpl.java
New file @@ -0,0 +1,12 @@ package com.xiaopeng.account.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.xiaopeng.account.mapper.UserSubscribeMessageMapper; import com.xiaopeng.account.service.IUserSubscribeMessageService; import com.xiaopeng.api.model.account.UserSubscribeMessage; import org.springframework.stereotype.Service; @Service public class UserSubscribeMessageServiceImpl extends ServiceImpl<UserSubscribeMessageMapper, UserSubscribeMessage> implements IUserSubscribeMessageService { } xp-robotaxi-account-boot/src/main/java/com/xiaopeng/account/vo/UserSubscribeMessageVo.java
New file @@ -0,0 +1,19 @@ package com.xiaopeng.account.vo; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.util.List; @Data @ApiModel public class UserSubscribeMessageVo { @ApiModelProperty("授权消息模板ID") private List<String> templateIds; @ApiModelProperty("微信jscode") private String jscode; } xp-robotaxi-account-boot/src/main/resources/mapper/account/UserSubscribeMessageMapper.xml
New file @@ -0,0 +1,7 @@ <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.xiaopeng.account.mapper.UserSubscribeMessageMapper"> </mapper> xp-robotaxi-api/src/main/java/com/xiaopeng/api/dto/map/RouteAndFareDto.java
@@ -9,6 +9,10 @@ @lombok.Data @ApiModel(description = "线路和运费Dto") public class RouteAndFareDto { @ApiModelProperty(value = "订单id") private Long orderId; @ApiModelProperty(value = "出发地") @NotNull(message = "出发地不能为空") private LonLatDto origin; xp-robotaxi-api/src/main/java/com/xiaopeng/api/dto/order/OrderChangeDto.java
New file @@ -0,0 +1,51 @@ package com.xiaopeng.api.dto.order; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import javax.validation.constraints.NotNull; @Data public class OrderChangeDto { @ApiModelProperty(value = "订单Id") @NotNull(message = "订单Id不能为空") private Long orderId; @ApiModelProperty("终点站点id") @NotNull(message = "终点站点id不能为空") private Long endPointId; /* @ApiModelProperty(value = "目的地经度") @NotNull(message = "目的地经度不能为空") private Double endLon; @ApiModelProperty(value = "目的地纬度") @NotNull(message = "目的地纬度不能为空") private Double endLat; @ApiModelProperty(value = "目的地地点") @NotNull(message = "目的地地点不能为空") private String endAddress; */ @ApiModelProperty(value = "优惠券id") private Long couponId; @ApiModelProperty("多路线快照 ID(选路线下单时与 selectedRouteId 同时传)") private String navigationSnapshotId; @ApiModelProperty("选中的路径唯一标识(高德 routeID)") private String selectedRouteId; @ApiModelProperty("选中路线对应的高德 strategy,用于推单队列;多路线下单时由服务端写入") private Integer selectedRoutePlanRule; @ApiModelProperty(value = "距离 米") private Integer distance = 0; @ApiModelProperty(value = "时间 秒") private Integer duration = 0; } xp-robotaxi-api/src/main/java/com/xiaopeng/api/factory/account/UserSubscribeMessageClientFallbackFactory.java
New file @@ -0,0 +1,25 @@ package com.xiaopeng.api.factory.account; import com.xiaopeng.api.feignClient.account.UserRoutePlanRuleClient; import com.xiaopeng.api.feignClient.account.UserSubscribeMessageClient; import com.xiaopeng.common.core.domain.R; import lombok.extern.slf4j.Slf4j; import org.springframework.cloud.openfeign.FallbackFactory; import org.springframework.stereotype.Component; @Slf4j @Component public class UserSubscribeMessageClientFallbackFactory implements FallbackFactory<UserSubscribeMessageClient> { @Override public UserSubscribeMessageClient create(Throwable cause) { return new UserSubscribeMessageClient() { @Override public R<String> getUserOpenId(Integer uid) { log.error("获取用户openid失败:" + cause); return R.fail("获取用户openid失败"); } }; } } xp-robotaxi-api/src/main/java/com/xiaopeng/api/factory/operation/XpPricingClientFallbackFactory.java
@@ -28,7 +28,7 @@ } @Override public R<XpPricing> getPricingByCid(Long cityId) { public R<XpPricing> getPricingByCid(Long cityId,String vehicleModel) { log.error("根据开通城市获取定价失败", cause); return R.ok(null); } xp-robotaxi-api/src/main/java/com/xiaopeng/api/factory/pushMessage/SubscribeMessageClientFallbackFactory.java
New file @@ -0,0 +1,24 @@ package com.xiaopeng.api.factory.pushMessage; import com.xiaopeng.api.feignClient.pushMessage.SubscribeMessageClient; import com.xiaopeng.common.core.domain.R; import lombok.extern.slf4j.Slf4j; import org.springframework.cloud.openfeign.FallbackFactory; import org.springframework.stereotype.Component; @Slf4j @Component public class SubscribeMessageClientFallbackFactory implements FallbackFactory<SubscribeMessageClient> { @Override public SubscribeMessageClient create(Throwable cause) { return new SubscribeMessageClient(){ @Override public R<String> getOpenId(String code) { log.error("获取openid失败: {}", cause.getMessage()); return R.fail("获取openid失败"); } }; } } xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/account/UserSubscribeMessageClient.java
New file @@ -0,0 +1,25 @@ package com.xiaopeng.api.feignClient.account; import com.xiaopeng.api.factory.account.UserRoutePlanRuleClientFallbackFactory; import com.xiaopeng.api.factory.account.UserSubscribeMessageClientFallbackFactory; import com.xiaopeng.common.core.domain.R; import com.xiaopeng.common.core.dto.ServiceNameConstantsEnum; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @FeignClient(contextId = "UserSubscribeMessageClient", value = ServiceNameConstantsEnum.XP_ACCOUNT_SERVICE, fallbackFactory = UserSubscribeMessageClientFallbackFactory.class) public interface UserSubscribeMessageClient { /** * 根据用户id获取openid * @param uid * @return */ @GetMapping("/logan/inner/v1/subscribeMessage/getUserOpenId/{v}") R<String> getUserOpenId(@PathVariable("uid") Integer uid); } xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/operation/XpPricingClient.java
@@ -22,7 +22,7 @@ // 根据开通城市 获取定价 @GetMapping("/logan/inner/v1/getPricingByCid") R<XpPricing> getPricingByCid(@RequestParam Long cityId); R<XpPricing> getPricingByCid(@RequestParam Long cityId,@RequestParam(required = false) String vehicleModelId); /** * 保存XpGeoQueryEntity xp-robotaxi-api/src/main/java/com/xiaopeng/api/feignClient/pushMessage/SubscribeMessageClient.java
New file @@ -0,0 +1,24 @@ package com.xiaopeng.api.feignClient.pushMessage; import com.xiaopeng.api.factory.pushMessage.SubscribeMessageClientFallbackFactory; import com.xiaopeng.common.core.domain.R; import com.xiaopeng.common.core.dto.ServiceNameConstantsEnum; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @FeignClient(contextId = "SubscribeMessageClient", value = ServiceNameConstantsEnum.XP_PUSH_MESSAGE_SERVICE, fallbackFactory = SubscribeMessageClientFallbackFactory.class) public interface SubscribeMessageClient { /** * 根据微信jscode获取openid * @param code * @return */ @GetMapping("/logan/inner/v1/subscribeMessage/getOpenId/{code}") R<String> getOpenId(@PathVariable("code") String code); } xp-robotaxi-api/src/main/java/com/xiaopeng/api/model/account/UserSubscribeMessage.java
New file @@ -0,0 +1,42 @@ package com.xiaopeng.api.model.account; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.xiaopeng.common.core.web.domain.BaseModel; import lombok.Data; /** * 用户订阅消息 */ @Data @TableName("xp_user_subscribe_message") public class UserSubscribeMessage extends BaseModel { //生成实体类字段 /** * 主键 */ @TableId(value = "id", type = IdType.AUTO) private Long id; /** * 用户id */ private Long userId; /** * 微信openid */ private String openId; /** * 消息模板id */ private String templateId; /** * 授权状态(0=否,1=是) */ private Integer status; } xp-robotaxi-api/src/main/java/com/xiaopeng/api/model/order/XpOrder.java
@@ -36,7 +36,7 @@ @ApiModelProperty(value = "用户id") @TableField(updateStrategy = FieldStrategy.NEVER) private Long appUserId; @ApiModelProperty(value = "用户id") @ApiModelProperty(value = "开通城市id") private Long cityId; @ApiModelProperty(value = "车辆id") private Long vehicleId; @@ -183,7 +183,7 @@ @ApiModelProperty(value = "下单城市编码") private String cityCode; @ApiModelProperty(value = "下单站点id") @ApiModelProperty(value = "起点站点id") @TableField("start_point_id") private Long startPointId; @@ -191,7 +191,7 @@ @TableField("end_point_id") private Long endPointId; @ApiModelProperty(value = "下单站点名称") @ApiModelProperty(value = "起点站点名称") @TableField("start_point_name") private String startPointName; @@ -239,4 +239,8 @@ @ApiModelProperty(value = "车辆是否故障 0-否 1-是") private Boolean carAbnormal; @ApiModelProperty(value = "订单修改目的地的次数 默认0") private Integer orderChange; } xp-robotaxi-api/src/main/java/com/xiaopeng/api/model/order/XpOrderWaypoint.java
New file @@ -0,0 +1,46 @@ package com.xiaopeng.api.model.order; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.experimental.Accessors; import javax.validation.constraints.NotNull; @Data @EqualsAndHashCode(callSuper = false) @Accessors(chain = true) @TableName("xp_order_waypoint") @ApiModel(value="xp_order_waypoint", description="订单途经点") public class XpOrderWaypoint { private static final long serialVersionUID = 1L; @TableId(value = "id", type = IdType.NONE) private Long id; @ApiModelProperty(value = "订单id") @NotNull(message = "订单id不能为空") private Long orderId; @ApiModelProperty(value = "途经点点位id") @NotNull(message = "途经点点位id不能为空") private Long pointId; @ApiModelProperty(value = "途经点名称") @NotNull(message = "途经点名称不能为空") private String pointName; @ApiModelProperty(value = "经度") @NotNull(message = "经度不能为空") private Double longitude; @ApiModelProperty(value = "纬度") @NotNull(message = "纬度不能为空") private Double latitude; } xp-robotaxi-api/src/main/java/com/xiaopeng/api/model/vehicleConvoy/XpVehicle.java
@@ -47,7 +47,7 @@ @ApiModelProperty(value = "车牌号") private String plateNumber; @ApiModelProperty(value = "型号") @ApiModelProperty(value = "型号 关联车辆型号表id") private String vehicleModel; @ApiModelProperty(value = "车辆颜色") xp-robotaxi-api/src/main/java/com/xiaopeng/api/service/BootCommonService.java
New file @@ -0,0 +1,59 @@ package com.xiaopeng.api.service; import com.xiaopeng.api.feignClient.operation.XpPricingClient; import com.xiaopeng.api.feignClient.order.XpOrderClient; import com.xiaopeng.api.feignClient.vehicleConvoy.XpVehicleClient; import com.xiaopeng.api.model.operation.XpPricing; import com.xiaopeng.api.model.order.XpOrder; import com.xiaopeng.api.model.vehicleConvoy.XpVehicle; import com.xiaopeng.common.core.domain.R; import com.xiaopeng.common.core.exception.ServiceException; import org.springframework.stereotype.Service; import javax.annotation.Resource; @Service public class BootCommonService { @Resource private XpPricingClient xpPricingClient; @Resource private XpVehicleClient xpVehicleClient; @Resource private XpOrderClient xpOrderClient; public XpPricing getXpPricing(Long orderId) { XpOrder xpOrder = xpOrderClient.queryOrderById(orderId).getData(); return getXpPricing(xpOrder.getCityId(),xpOrder.getVehicleId()); } public XpPricing getXpPricing(Long cityId, Long vehicleId) { String vehicleModelId = null; if(vehicleId != null){ XpVehicle xpVehicle = xpVehicleClient.queryById(vehicleId).getData(); if(null != xpVehicle){ vehicleModelId = xpVehicle.getVehicleModel(); } } R<XpPricing> pricingByCityId = xpPricingClient.getPricingByCid(cityId,vehicleModelId); return pricingByCityId.getData(); } public String getVehicleModelId(Long orderId) { if(orderId!= null){ R<XpOrder> xpOrderR = xpOrderClient.queryOrderById(orderId); if (xpOrderR.getCode() != R.SUCCESS || xpOrderR.getData() == null) { throw new ServiceException("订单不存在"); } XpVehicle xpVehicle = xpVehicleClient.queryById(xpOrderR.getData().getVehicleId()).getData(); return xpVehicle.getVehicleModel(); } return null; } } xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/operation/RouteAndFareVo.java
@@ -30,6 +30,9 @@ @ApiModelProperty(value = "是否开启试运营(0=否,1=是)") private Integer operateFree; @ApiModelProperty(value = "策略id") private Integer amapStrategy; } xp-robotaxi-api/src/main/java/com/xiaopeng/api/vo/pushMessage/SendSubscribeMessageVo.java
New file @@ -0,0 +1,26 @@ package com.xiaopeng.api.vo.pushMessage; import lombok.Data; @Data public class SendSubscribeMessageVo { /** * 用户id */ private Long userId; /** * 消息模板 */ private String templateId; /** * 消息内容 */ private String json; /** * 跳转页面 */ private String page; } xp-robotaxi-api/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -53,4 +53,6 @@ com.xiaopeng.api.factory.map.AdcFencePushClientFallbackFactory com.xiaopeng.api.factory.account.UserRoutePlanRuleClientFallbackFactory com.xiaopeng.api.factory.order.XpOrderReassignClientFallbackFactory com.xiaopeng.api.factory.map.XpStationClientFallbackFactory com.xiaopeng.api.factory.map.XpStationClientFallbackFactory com.xiaopeng.api.factory.pushMessage.SubscribeMessageClientFallbackFactory com.xiaopeng.api.factory.account.UserSubscribeMessageClientFallbackFactory xp-robotaxi-common/xp-common-core/src/main/java/com/xiaopeng/common/core/utils/DateUtils.java
@@ -443,4 +443,17 @@ return count; } /** * 获取指定时间的 毫秒 * @return */ public static long getMilliseconds(LocalDateTime localDateTime){ // 转换为毫秒时间戳(需指定时区) Instant instant = localDateTime.atZone(ZoneId.systemDefault()).toInstant(); long timestamp = instant.toEpochMilli(); return timestamp; } } xp-robotaxi-itinerary-boot/src/main/java/com/xiaopeng/itinerary/controller/ActiveOrderController.java
New file @@ -0,0 +1,78 @@ package com.xiaopeng.itinerary.controller; import com.xiaopeng.api.dto.map.RouteAndFareDto; import com.xiaopeng.api.dto.order.OrderChangeDto; import com.xiaopeng.api.vo.operation.RouteAndFareVo; import com.xiaopeng.common.core.domain.R; import com.xiaopeng.itinerary.service.ActiveOrderService; import com.xiaopeng.itinerary.service.XpPricingService; import com.xiaopeng.unifiedverification.interceptor.XpClientCheck; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.validation.Valid; import java.util.List; /** * <p> * 订单行程 * </p> */ @Slf4j @RestController @RequestMapping("") @Api(tags = "费用管理") public class ActiveOrderController { @Resource private ActiveOrderService activeOrderService; @Resource private XpPricingService xpPricingService; /** * 在行程中修改目的地-获取路线和费用 * @param routeAndFareDto * @param request * @return */ @PostMapping("/api/v1/mini-program/activeOrder/route-and-fare") @ApiOperation(value = "导航-获取路线和费用", tags = {"小程序-获取路线和费用"}, response = RouteAndFareVo.class) @XpClientCheck(checkToken = false, checkOtpToken = true) public R<List<RouteAndFareVo>> getRouteAndFare(@RequestBody @Valid RouteAndFareDto routeAndFareDto, HttpServletRequest request) { if(null == routeAndFareDto.getOrderId()){ return R.fail("订单id不能为空"); } // uid已通过拦截器otp校验,可以直接使用 String uid = request.getHeader("XP-Uid"); return R.ok(xpPricingService.getRouteAndFare(routeAndFareDto, uid)); } /** * 行程中 修改目的地 确认按钮 * 修改目的地, * 1.重新确认路线 * 2.确认价格 */ @ApiOperation(value = "行程中 修改目的地 确认按钮", notes = "", tags = {"小程序-行程中修改目的地"}) @PostMapping("/api/v1/mini-program/order/orderChange") @XpClientCheck(checkToken = false, checkOtpToken = true) public R<?> orderChange(@RequestBody @Validated OrderChangeDto dto, HttpServletRequest request) { String uid = request.getHeader("XP-Uid"); activeOrderService.orderChange(dto, uid); return R.ok(); } } xp-robotaxi-itinerary-boot/src/main/java/com/xiaopeng/itinerary/controller/XpPricingController.java
@@ -7,6 +7,7 @@ import com.xiaopeng.api.dto.map.RoutePreferenceSaveDto; import com.xiaopeng.api.dto.map.RouteStaleCheckDto; import com.xiaopeng.api.dto.map.SelectNavigationRouteDto; import com.xiaopeng.api.dto.order.OrderChangeDto; import com.xiaopeng.api.model.order.XpOrder; import com.xiaopeng.api.vo.operation.MultiRouteAndFareVo; import com.xiaopeng.api.vo.operation.RoutePreferenceItemVo; @@ -18,6 +19,7 @@ import com.xiaopeng.common.core.web.controller.BaseController; import com.xiaopeng.common.redis.service.RedisService; import com.xiaopeng.itinerary.dto.XpOrderDto; import com.xiaopeng.itinerary.service.ActiveOrderService; import com.xiaopeng.itinerary.service.IXpGeoQueryEntityService; import com.xiaopeng.itinerary.service.XpPricingService; import com.xiaopeng.api.vo.HotFixInfoVo; @@ -31,6 +33,7 @@ import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.validation.Valid; import java.util.List; @@ -137,6 +140,10 @@ return R.ok(saveOrderVo); } /** * 获取热修复信息 * @param xp-robotaxi-itinerary-boot/src/main/java/com/xiaopeng/itinerary/dto/XpOrderDto.java
@@ -48,6 +48,9 @@ @NotNull(message = "下单地址不能为空") private String placementAddress; @ApiModelProperty("路线规划规则") @NotNull(message = "路线规划规则不能为空") private Integer routePlanRule; @ApiModelProperty("用户优惠券主键:null 或 0 表示不使用券;>0 为具体券 id") private Long userCouponId; xp-robotaxi-itinerary-boot/src/main/java/com/xiaopeng/itinerary/service/ActiveOrderService.java
New file @@ -0,0 +1,13 @@ package com.xiaopeng.itinerary.service; import com.xiaopeng.api.dto.order.OrderChangeDto; /** * 行驶中的订单 */ public interface ActiveOrderService { void orderChange(OrderChangeDto dto, String uid); } xp-robotaxi-itinerary-boot/src/main/java/com/xiaopeng/itinerary/service/XpPricingService.java
@@ -60,6 +60,8 @@ */ String saveOrder(XpOrderDto dto, String uid); Boolean isInOperationTime(String operationStrategy); /** * 多策略路径规划合并、计价与快照 */ xp-robotaxi-itinerary-boot/src/main/java/com/xiaopeng/itinerary/service/impl/ActiveOrderServiceImpl.java
New file @@ -0,0 +1,226 @@ package com.xiaopeng.itinerary.service.impl; import com.alibaba.fastjson2.JSON; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.xiaopeng.api.dto.order.OrderChangeDto; import com.xiaopeng.api.feignClient.map.XpMapClient; import com.xiaopeng.api.feignClient.operation.XpPointClient; import com.xiaopeng.api.feignClient.order.XpOrderClient; import com.xiaopeng.api.model.operation.XpElectronicFence; import com.xiaopeng.api.model.operation.XpElectronicFenceArea; import com.xiaopeng.api.model.operation.XpPoint; import com.xiaopeng.api.model.operation.XpRoutePlan; import com.xiaopeng.api.model.order.XpOrder; import com.xiaopeng.api.vo.operation.RouteOptionVo; import com.xiaopeng.api.vo.operation.RouteVo; import com.xiaopeng.common.core.constant.CacheConstants; import com.xiaopeng.common.core.domain.R; import com.xiaopeng.common.core.exception.ServiceException; import com.xiaopeng.common.redis.service.RedisService; import com.xiaopeng.itinerary.constant.I18nKeyConstant; import com.xiaopeng.itinerary.mapper.XpElectronicFenceAreaMapper; import com.xiaopeng.itinerary.mapper.XpElectronicFenceMapper; import com.xiaopeng.itinerary.mapper.XpOpenCityMapper; import com.xiaopeng.itinerary.mapper.XpRoutePlanMapper; import com.xiaopeng.itinerary.navigation.MultiRouteSnapshotPayload; import com.xiaopeng.itinerary.service.ActiveOrderService; import com.xiaopeng.itinerary.service.XpPointService; import com.xiaopeng.itinerary.service.XpPricingService; import com.xiaopeng.itinerary.utils.map.GDMapGeocodingUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.time.LocalDateTime; import java.util.*; import java.util.concurrent.TimeUnit; @Slf4j @Service public class ActiveOrderServiceImpl implements ActiveOrderService { @Resource private GDMapGeocodingUtil gdMapGeocodingUtil; @Resource private XpOpenCityMapper xpOpenCityMapper; @Resource private XpOrderClient xpOrderClient; @Resource private XpPricingService xpPricingService; @Resource private XpMapClient xpMapClient; @Resource private XpElectronicFenceAreaMapper xpElectronicFenceAreaMapper; @Autowired private XpElectronicFenceMapper xpElectronicFenceMapper; @Autowired private XpPointService xpPointService; @Resource private XpPointClient xpPointClient; @Resource private RedisService redisService; @Resource private XpRoutePlanMapper xpRoutePlanMapper; /* public void getRouteAndFareParamValidate(RouteAndFareDto routeAndFareDto, String userId) { //获取订单信息 R<XpOrder> xpOrderR = xpOrderClient.queryOrderById(routeAndFareDto.getOrderId()); if (xpOrderR.getCode() != R.SUCCESS || xpOrderR.getData() == null) { throw new ServiceException("订单不存在"); } // 1. 基础数据查询 Map<String, String> geocode = gdMapGeocodingUtil.geocode(routeAndFareDto.getOrigin().getLongitude().toString(), routeAndFareDto.getOrigin().getLatitude().toString()); if(null == geocode){ throw new ServiceException("获取城市信息失败", I18nKeyConstant.CITY_NOT_FOUND); } Long cityCode = routeAndFareDto.getOpenCityId(); XpOpenCity openCities = xpOpenCityMapper.selectById(cityCode); if (openCities == null ) { throw new ServiceException("获取城市信息失败", I18nKeyConstant.CITY_NOT_FOUND); } XpPricing xpPricing = xpPricingService.getOne(new LambdaQueryWrapper<XpPricing>().eq(XpPricing::getCityId, routeAndFareDto.getOpenCityId())); if (xpPricing == null) { throw new ServiceException("当前区域未开通服务", I18nKeyConstant.NO_CURRENT_REGION); } routeAndFareDto.setRoutePlanRule(xpOrderR.getData().getRoutePlanRule()); // 算距离 R<RouteVo> distanceAndDuration = xpMapClient.getDistanceAndDuration(routeAndFareDto); if (distanceAndDuration.getCode() != 200) { throw new ServiceException("获取导肮信息失败", I18nKeyConstant.PATH_NOT_FOUND); } RouteVo routeVo = distanceAndDuration.getData(); } */ @Override public void orderChange(OrderChangeDto dto, String uid){ //获取订单信息 R<XpOrder> xpOrderR = xpOrderClient.queryOrderById(dto.getOrderId()); if (xpOrderR.getCode() != R.SUCCESS || xpOrderR.getData() == null) { throw new ServiceException("订单不存在"); } Integer orderChange = 2;//todo yyy 从配置文件读取 修改订单目的地次数 XpOrder order = xpOrderR.getData(); //判断订单修改的次数 if(order.getOrderChange() > orderChange){ throw new ServiceException("目的地修改次数已超上限"); } // 是否在运营区域时间内 XpElectronicFenceArea xpElectronicFenceArea = xpElectronicFenceAreaMapper.selectById(order.getXpElectronicFenceAreaId()); XpElectronicFence xpElectronicFence = xpElectronicFenceMapper.selectById(xpElectronicFenceArea.getElectronicId()); String operationStrategy = xpElectronicFence.getOperationStrategy(); Boolean inOperationTime = xpPricingService.isInOperationTime(operationStrategy); if (!inOperationTime && xpElectronicFence.getFailureType()!=1) { log.info("当前区域暂未开通,下单失败 {}", uid); throw new ServiceException("当前区域未开通服务", I18nKeyConstant.NO_CURRENT_REGION); } boolean inTime = xpPointClient.nowInTimePoint(dto.getEndPointId()).getData(); if (!inTime) { throw new ServiceException("终点点位未开通服务", I18nKeyConstant.NO_CURRENT_REGION); } // 终点 XpPoint endXpPoint = xpPointService.getById(dto.getEndPointId()); if(endXpPoint ==null || endXpPoint.getOperationStatus()!=2){ throw new ServiceException("该站点已停止运营", 200,I18nKeyConstant.NO_CURRENT_POINT); } //修改订单数据 XpOrder xpOrder = new XpOrder(); xpOrder.setId(order.getId()); xpOrder.setEndLon(endXpPoint.getPointLon()); xpOrder.setEndLat(endXpPoint.getPointLat()); xpOrder.setEndAddress(endXpPoint.getPointAddress()); xpOrder.setEndPointId(dto.getEndPointId()); xpOrder.setEndPointName(endXpPoint.getPointName()); Long couponRef = dto.getCouponId(); xpOrder.setCouponId(couponRef != null && couponRef > 0L ? couponRef : null); xpOrder.setUpdateTime(LocalDateTime.now()); xpOrder.setOrderChange(order.getOrderChange()+1); xpOrderClient.updateOrder(xpOrder); //获取距离和时间 this.getDD(uid,dto); //将修改信息存入到redis, // 获取起点经纬度 XpPoint xpPoint = xpPointService.getById(order.getStartPointId()); XpRoutePlan xpRoutePlan = xpRoutePlanMapper.selectOne(new LambdaQueryWrapper<XpRoutePlan>().eq(XpRoutePlan::getAuditStatus, 2).eq(XpRoutePlan::getCityCode, order.getCityCode())); final Integer planRuleForQueue = dto.getSelectedRoutePlanRule() != null ? dto.getSelectedRoutePlanRule() : (xpRoutePlan != null ? xpRoutePlan.getRoutePlanRule() : null); // order.getId() + "_" + xpPoint.getPointLon() + "," + xpPoint.getPointLat() + "_0" + "_" + uid + "_" + order.getOrderNumber() + "_" + dto.getDistance() + "_" + dto.getDuration() + "_" + null StringJoiner points = new StringJoiner("_"); points.add(order.getId().toString()); points.add(xpPoint.getPointLon() + "," + xpPoint.getPointLat()); points.add("0"); points.add(uid); points.add(order.getOrderNumber()); points.add(dto.getDistance()+""); points.add(dto.getDuration()+""); String key = CacheConstants.QUEUE_POINT_ID + "-" + xpPoint.getId() + "-" + xpPoint.getPointLon() + "," + xpPoint.getPointLat(); if (planRuleForQueue == null) { redisService.addToTail(key,points+"_"+null); } else { redisService.addToTail(key,points+"_"+planRuleForQueue); } log.info("订单推单:{} {}", key, redisService.getCacheList(key)); xpOrder.setQueueName(key); xpOrderClient.updateOrder(xpOrder); } /** * 获取距离和时间 * @param uid * @param dto */ public void getDD (String uid, OrderChangeDto dto ){ Integer distance = 0; Integer duration = 0; if (dto.getNavigationSnapshotId() != null && dto.getSelectedRouteId() != null) { String snap = redisService.getCacheObject(CacheConstants.ORDER_MULTI_ROUTE_SNAPSHOT + uid + ":" + dto.getNavigationSnapshotId()); if (snap != null) { MultiRouteSnapshotPayload snapPayload = JSON.parseObject(snap, MultiRouteSnapshotPayload.class); RouteOptionVo opt = snapPayload.getRoutes().stream() .filter(r2 -> dto.getSelectedRouteId().equals(r2.getPathFingerprint())) .findFirst() .orElse(null); if (opt != null && opt.getRoute() != null) { distance = opt.getRoute().getDistance(); duration = opt.getRoute().getDuration(); redisService.setCacheObject("ORDER_NAVIGATION_END_DATA:" + uid, JSON.toJSONString(opt.getRoute()), 24L, TimeUnit.HOURS); if (dto.getSelectedRoutePlanRule() == null) { dto.setSelectedRoutePlanRule(opt.getPrimaryRoutePlanRule()); } } } } if (distance == 0 && duration == 0) { Object cacheObject = redisService.getCacheObject("ORDER_NAVIGATION_END_DATA:" + uid); if (cacheObject != null) { RouteVo routeVo = JSON.parseObject(cacheObject.toString(), RouteVo.class); distance = routeVo.getDistance(); duration = routeVo.getDuration(); } } dto.setDistance(distance); dto.setDuration(duration); } } xp-robotaxi-itinerary-boot/src/main/java/com/xiaopeng/itinerary/service/impl/XpPricingServiceImpl.java
@@ -7,21 +7,21 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.xiaopeng.api.dto.account.GetUserCouponDto; import com.xiaopeng.api.dto.map.OrderRouteAndFareDto; import com.xiaopeng.api.dto.map.RouteAndFareDto; import com.xiaopeng.api.dto.map.RouteStaleCheckDto; import com.xiaopeng.api.dto.map.SelectNavigationRouteDto; import com.xiaopeng.api.dto.operation.LonLatDto; import com.xiaopeng.api.enums.RouteStrategyKind; import com.xiaopeng.api.dto.pushMessage.SendMessageDTO; import com.xiaopeng.api.feignClient.account.XpAppUserClient; import com.xiaopeng.api.feignClient.account.XpAppUserCouponClient; import com.xiaopeng.api.feignClient.map.XpMapClient; import com.xiaopeng.api.feignClient.operation.XpPointClient; import com.xiaopeng.api.feignClient.order.XpOrderClient; import com.xiaopeng.api.feignClient.pushMessage.XpPushMessageClient; import com.xiaopeng.api.feignClient.vehicleConvoy.XpVehicleClient; import com.xiaopeng.api.model.operation.*; import com.xiaopeng.api.model.order.XpOrder; import com.xiaopeng.api.model.vehicleConvoy.XpVehicle; import com.xiaopeng.api.service.BootCommonService; import com.xiaopeng.api.vo.account.UserCouponVo; import com.xiaopeng.api.vo.operation.FareVo; import com.xiaopeng.api.vo.operation.MultiRouteAndFareVo; @@ -85,6 +85,8 @@ private XpMapClient xpMapClient; @Resource private BootCommonService bootCommonService; @Resource private XpOpenCityMapper xpOpenCityMapper; @Resource @@ -101,6 +103,8 @@ @Resource private XpOrderClient xpOrderClient; @Resource private XpVehicleClient xpVehicleClient; @Resource private XpTradingStrategyMapper xpTradingStrategyMapper; @@ -200,6 +204,17 @@ return hour + "-" + (hour + 1); } public XpPricing getInfo(Long cityId,String vehicleModel){ LambdaQueryWrapper<XpPricing> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(XpPricing::getDeleted, 0); queryWrapper.eq(XpPricing::getCityId, cityId); if(vehicleModel != null){ queryWrapper.eq(XpPricing::getVehicleModel, vehicleModel); } return super.getOne(queryWrapper,true); } @Override public List<RouteAndFareVo> getRouteAndFare(RouteAndFareDto routeAndFareDto, String userId) { @@ -213,7 +228,6 @@ // RouteAndFareVo RouteAndFareVo = JSONObject.parseObject(jsonStr, RouteAndFareVo.class); // return (RouteAndFareVo); // } ArrayList<RouteAndFareVo> routeAndFareVos = new ArrayList<>(3); // 1. 基础数据查询 @@ -226,6 +240,15 @@ if (openCities == null ) { throw new ServiceException("获取城市信息失败", I18nKeyConstant.CITY_NOT_FOUND); } //根据订单获取车型 获取定价策略 String vehicleModel = bootCommonService.getVehicleModelId(routeAndFareDto.getOrderId()); XpPricing xpPricing = this.getInfo(routeAndFareDto.getOpenCityId(),vehicleModel); if (xpPricing == null) { throw new ServiceException("当前区域未开通服务", I18nKeyConstant.NO_CURRENT_REGION); } XpOpenCity xpOpenCity = openCities; routeAndFareDto.setOpenCityId(xpOpenCity.getId()); List<XpRoutePlan> xpRoutePlan = xpRoutePlanMapper.selectList(new LambdaQueryWrapper<XpRoutePlan>().eq(XpRoutePlan::getAuditStatus, 2).eq(XpRoutePlan::getCityCode, cityCode)); @@ -233,7 +256,6 @@ // 找出用户偏好 Object cacheObject = redisService.getCacheObject(CacheConstants.USER_ROUTE_PREFERENCE + userId); for (XpRoutePlan routePlan : xpRoutePlan) { if (xpRoutePlan != null) { routeAndFareDto.setRoutePlanRule(routePlan.getRoutePlanRule()); @@ -246,12 +268,6 @@ // keep city default } } XpPricing xpPricing = this.baseMapper.selectOne(new LambdaQueryWrapper<XpPricing>().eq(XpPricing::getCityId, routeAndFareDto.getOpenCityId())); if (xpPricing == null) { throw new ServiceException("当前区域未开通服务", I18nKeyConstant.NO_CURRENT_REGION); } // 算距离 R<RouteVo> distanceAndDuration = xpMapClient.getDistanceAndDuration(routeAndFareDto); @@ -409,7 +425,7 @@ // 11. 生成订单ID并缓存 String id = CacheConstants.ORD + System.currentTimeMillis() + "_" + UUID.randomUUID().toString().substring(0, 8); redisService.setCacheObject(CacheConstants.ORD + id + userId, xpPricingCardVo, 1l, TimeUnit.DAYS); redisService.setCacheObject(CacheConstants.ORD + id + userId, xpPricingCardVo, 1L, TimeUnit.DAYS); fareVo.setId(id); // 12. 设置费用信息 @@ -433,7 +449,7 @@ } } routeAndFareVo.setRoutePlanName(routePlan.getPlanName()); routeAndFareVo.setAmapStrategy(routePlan.getRoutePlanRule()); routeAndFareVos.add(routeAndFareVo); @@ -612,24 +628,23 @@ xpOrder.setPlacementAddress(dto.getPlacementAddress()); xpOrder.setCityId(dto.getOpenCityId()); xpOrder.setXpElectronicFenceAreaId(dto.getXpElectronicFenceAreaId()); xpOrder.setStartPointId(dto.getStartPointId()); xpOrder.setStartLon(xpPoint.getPointLon()); xpOrder.setStartLat(xpPoint.getPointLat()); xpOrder.setBoardingLon(xpPoint.getPointLon()); xpOrder.setBoardingLat(xpPoint.getPointLat()); xpOrder.setBoardingAddress(xpPoint.getPointAddress()); xpOrder.setStartAddress(xpPoint.getPointAddress()); xpOrder.setStartPointName(xpPoint.getPointName()); xpOrder.setEndPointId(dto.getEndPointId()); xpOrder.setEndLon(endXpPoint.getPointLon()); xpOrder.setEndLat(endXpPoint.getPointLat()); xpOrder.setStartAddress(xpPoint.getPointAddress()); xpOrder.setEndAddress(endXpPoint.getPointAddress()); xpOrder.setXpElectronicFenceAreaId(dto.getXpElectronicFenceAreaId()); xpOrder.setStartPointId(dto.getStartPointId()); xpOrder.setEndPointId(dto.getEndPointId()); xpOrder.setStartPointName(xpPoint.getPointName()); xpOrder.setEndPointName(endXpPoint.getPointName()); // 当前时段 String currentTimeSlot = DateUtils.getCurrentTimeSlot(); @@ -732,7 +747,6 @@ xpGeoQueryEntity.setStatus(0); xpGeoQueryEntity.setTimestamp(System.currentTimeMillis()); geoQueryEntityService.save(xpGeoQueryEntity); return orderId.toString(); } } @@ -760,8 +774,8 @@ return orderId.toString(); } private Boolean isInOperationTime(String operationStrategy) { @Override public Boolean isInOperationTime(String operationStrategy) { JSONArray jsonArray = JSONArray.parseArray(operationStrategy); Integer holidayToday = holidayUtil.isHolidayToday(); for (Object o : jsonArray) { xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/controller/XpTaskController.java
@@ -300,5 +300,4 @@ return R.ok(); } } xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/service/impl/XpCruiseCheckInRuleServiceImpl.java
@@ -50,7 +50,7 @@ @Override public AppXpCruiseCheckInRuleVo getMyXpCruiseCheckInRule(Long uid) { List<XpCruiseCheckInRule> list = this.list(new LambdaQueryWrapper<XpCruiseCheckInRule>().eq(XpCruiseCheckInRule::getDeleted, 0) .last(" and (now() BETWEEN start_time and end_time or a.cyclic_mode = 1)")); .last(" and (now() BETWEEN start_time and end_time or cyclic_mode = 1)")); if(list.isEmpty()){ return null; } xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/controller/XpPricingController.java
@@ -80,12 +80,19 @@ } // 根据开通城市 获取定价 @GetMapping("/logan/inner/v1/getPricingByCid") public R<XpPricing> getPricingByCid(@RequestParam Long cityId) { XpPricing pricing = xpPricingService.lambdaQuery().eq(XpPricing::getCityId, cityId).last("limit 1").one(); public R<XpPricing> getPricingByCid(@RequestParam Long cityId,@RequestParam(required = false) String vehicleModelId) { LambdaQueryWrapper<XpPricing> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(XpPricing::getCityId, cityId); if(null != vehicleModelId){ queryWrapper.eq(XpPricing::getVehicleModel, vehicleModelId); } XpPricing pricing = xpPricingService.getOne(queryWrapper); return R.ok(pricing); } @GetMapping("/base/v1/mini-program/pricing/rules/{openCityId}") @XpClientCheck(checkToken = false, checkOtpToken = true) @ApiOperation(value = "获取费用规则(弃用)", tags = {"小程序-根据开通城市获取费用规则(弃用)"}, response = String.class) xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/controller/ActiveOrderController.java
New file @@ -0,0 +1,146 @@ package com.xiaopeng.order.controller; import com.xiaopeng.api.dto.QueryOrderRouteDTO; import com.xiaopeng.api.feignClient.operation.XpPointClient; import com.xiaopeng.api.feignClient.vehicleConvoy.XpVehicleClient; import com.xiaopeng.api.model.operation.XpPoint; import com.xiaopeng.api.model.order.XpOrder; import com.xiaopeng.api.model.vehicleConvoy.XpVehicle; import com.xiaopeng.api.vo.operation.RouteVo; import com.xiaopeng.common.core.domain.R; import com.xiaopeng.order.model.vo.XpOrderDetailVO; import com.xiaopeng.order.service.IXpOrderService; import com.xiaopeng.unifiedverification.interceptor.XpClientCheck; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.RequiredArgsConstructor; import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.Nullable; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import java.time.Duration; import java.time.LocalDateTime; @RestController @Api(tags = "订单") @RequiredArgsConstructor(onConstructor_ = @Lazy) public class ActiveOrderController { @Autowired private MongoTemplate mongoTemplate; @Resource XpOrderController xpOrderController; @Resource private final IXpOrderService xpOrderService; @Resource private final XpPointClient xpPointClient; @Resource private final XpVehicleClient xpVehicleClient; @Nullable private R<XpOrder> getOrderTripEnd(String orderId) { XpOrder xpOrder = xpOrderService.getById(orderId); if(xpOrder == null){ return R.fail("订单不存在"); } if(xpOrder.getTripEnd() != null){ Duration duration = Duration.between(xpOrder.getTripEnd(), LocalDateTime.now()); if(duration.toHours()>=24){ return R.fail("行程结束已超过24小时查看行程信息,请联系客服"); } } return R.ok(xpOrder); } /** * 分享行程接口1-订单查询实时轨迹路线 */ @ApiOperation(value = "分享行程接口1 订单查询实时轨迹路线") @GetMapping("/base/v1/mini-program/orderActive/share/queryOrderRoute") @XpClientCheck(checkToken = false, checkOtpToken = false) public R<RouteVo> queryOrderRoute(@RequestParam String orderId, HttpServletRequest request) { // 查询订单信息 if(StringUtils.isBlank(orderId) ){ return R.fail("参数不能为空"); } R r = getOrderTripEnd(orderId); if (r != null && r.getCode() != 200) { return r; } QueryOrderRouteDTO dto = new QueryOrderRouteDTO(); return xpOrderController.queryOrderRoute(dto,request); } /** * 分享行程接口2-订单查询轨迹路线 */ @ApiOperation(value = "分享行程接口2-订单查询轨迹路线") @GetMapping("/base/v1/mini-program/orderActive/share/queryOrderTrajectory") @XpClientCheck(checkToken = false, checkOtpToken = false) public R<RouteVo> queryOrderTrajectory(@RequestParam String orderId,HttpServletRequest request) { if(StringUtils.isBlank(orderId) ){ return R.fail("参数不能为空"); } R r = getOrderTripEnd(orderId); if (r != null && r.getCode() != 200) { return r; } return xpOrderController.queryOrderTrajectory(orderId,request); } /** * 分享行程接口3-订单信息 */ @ApiOperation(value = "分享行程接口3-订单查询实时轨迹路线") @GetMapping("/base/v1/mini-program/orderActive/share/orderInfo") @XpClientCheck(checkToken = false, checkOtpToken = false) public R<XpOrderDetailVO> shareOrderInfo(@RequestParam String orderId, HttpServletRequest request) { // 查询订单信息 if(orderId == null){ return R.fail("参数不能为空"); } R<XpOrder> r = getOrderTripEnd(orderId); if (r != null && r.getCode() != 200) { return R.fail(r.getMsg()); } XpOrder xpOrder = r.getData(); XpOrderDetailVO xpOrderDetailVO = new XpOrderDetailVO(); BeanUtils.copyProperties(xpOrder, xpOrderDetailVO); R<XpPoint> xpPointR = xpPointClient.queryById(xpOrder.getStartPointId()); if (xpPointR.getCode() == R.SUCCESS) { XpPoint startPoint = xpPointR.getData(); xpOrderDetailVO.setStartParkingTimeLimit(startPoint.getParkingTimeLimit()); } R<XpPoint> xpPointR1 = xpPointClient.queryById(xpOrder.getEndPointId()); if (xpPointR1.getCode() == R.SUCCESS) { XpPoint endPoint = xpPointR1.getData(); xpOrderDetailVO.setEndParkingTimeLimit(endPoint.getParkingTimeLimit()); } if (xpOrder.getVehicleId() != null) { R<XpVehicle> xpVehicleR = xpVehicleClient.queryById(xpOrder.getVehicleId()); XpVehicle vehicle = xpVehicleR.getData(); xpOrderDetailVO.setVehicleId(vehicle.getId()); xpOrderDetailVO.setVehicleVin(vehicle.getVehicleVin()); xpOrderDetailVO.setVehicleModel(vehicle.getVehicleModel()); xpOrderDetailVO.setVehicleColor(vehicle.getVehicleColor()); xpOrderDetailVO.setPlateNumber(vehicle.getPlateNumber()); xpOrderDetailVO.setVehiclePictures(vehicle.getVehiclePictures()); } return R.ok(xpOrderDetailVO); } } xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/controller/XpOrderController.java
@@ -51,6 +51,7 @@ import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -79,7 +80,7 @@ private final RedisService redisService; @Autowired private MongoTemplate mongoTemplate; @Autowired @Resource private XpVehicleClient xpVehicleClient; /** @@ -106,7 +107,7 @@ if (!list.isEmpty()) { return R.ok(list.get(0).getId()); } return R.ok(0l); return R.ok(0L); } @@ -391,6 +392,17 @@ return R.ok(xpOrderService.getActiveOrder(uid)); } /** * 获取正在进行中的订单_行程管理 */ @ApiOperation(value = "获取正在进行中的订单_行程管理", tags = {"小程序-获取正在进行中的订单_行程管理"}) @GetMapping("/api/v1/mini-program/order/active_itineraryManage") @XpClientCheck(checkToken = false, checkOtpToken = true) public R<XpOrderDetailVO> getActiveOrder_itineraryManage(HttpServletRequest request) { // uid已通过拦截器otp校验,可以直接使用 String uid = request.getHeader("XP-Uid"); return R.ok(xpOrderService.getActiveOrder_itineraryManage(uid)); } /** * 查询历史订单列表 @@ -421,7 +433,6 @@ * + * 取消订单 */ @PutMapping("/api/v1/mini-program/order/cancel/{orderId}") @ApiOperation(value = "取消订单", tags = {"小程序-取消订单"}) @XpClientCheck(checkToken = false, checkOtpToken = true) @@ -555,6 +566,9 @@ @PostMapping("/api/v1/mini-program/order/queryOrderRoute") @XpClientCheck(checkToken = false, checkOtpToken = true) public R<RouteVo> queryOrderRoute(@Validated @RequestBody QueryOrderRouteDTO dto, HttpServletRequest request) { if(dto.getOrderId() == null){ return R.fail("参数不能为空"); } String uid = request.getHeader("XP-Uid"); // 查询订单信息 XpOrder xpOrder = xpOrderService.getById(dto.getOrderId()); @@ -584,7 +598,9 @@ @GetMapping("/api/v1/mini-program/order/queryOrderTrajectory") @XpClientCheck(checkToken = false, checkOtpToken = true) public R<RouteVo> queryOrderTrajectory(@RequestParam String orderId, HttpServletRequest request) { String uid = request.getHeader("XP-Uid"); if(orderId == null){ return R.fail("参数不能为空"); } List<OrderTrajectory> trajectories = mongoTemplate.find( new Query(Criteria.where("orderId").is(Long.valueOf(orderId))) .with(Sort.by(Sort.Direction.ASC, "locatetime")), @@ -621,4 +637,7 @@ return R.ok((int)count); } } xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/controller/XpOrderWaypointController.java
New file @@ -0,0 +1,51 @@ package com.xiaopeng.order.controller; import com.xiaopeng.api.model.order.XpOrder; import com.xiaopeng.api.model.order.XpOrderWaypoint; import com.xiaopeng.common.core.domain.R; import com.xiaopeng.order.service.IXpOrderService; import com.xiaopeng.order.service.IXpOrderWaypointService; import com.xiaopeng.unifiedverification.interceptor.XpClientCheck; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Lazy; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; @RestController @Api(tags = "订单途经点") @RequiredArgsConstructor(onConstructor_ = @Lazy) public class XpOrderWaypointController { @Resource private final IXpOrderWaypointService xpOrderWaypointService; private final IXpOrderService xpOrderService; /** * 添加订单途径点 */ @PostMapping("/api/v1/mini-program/orderWaypoint/save") @ApiOperation(value = "添加订单途径点", tags = {"小程序-添加订单途径点"}) @XpClientCheck(checkToken = false, checkOtpToken = true) public R save(@Validated @RequestBody XpOrderWaypoint entity) { XpOrder xpOrder = xpOrderService.getById(entity.getOrderId()); if(xpOrder == null){ return R.fail("订单不存在"); } entity.setId(null); boolean b = xpOrderWaypointService.save(entity); if(b){ return R.ok(); } return R.fail("订单不存在"); } } xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/mapper/XpOrderWaypointMapper.java
New file @@ -0,0 +1,16 @@ package com.xiaopeng.order.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.xiaopeng.api.model.order.XpOrderWaypoint; /** * <p> * 订单支付记录 Mapper 接口 * </p> * * @author mitao * @since 2026-02-03 */ public interface XpOrderWaypointMapper extends BaseMapper<XpOrderWaypoint> { } xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/model/vo/XpOrderDetailVO.java
@@ -2,13 +2,12 @@ import com.baomidou.mybatisplus.annotation.TableField; import com.fasterxml.jackson.annotation.JsonFormat; import com.xiaopeng.api.dto.operation.LonLatDto; import com.xiaopeng.api.model.order.XpOrderWaypoint; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.math.BigDecimal; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.List; @@ -47,6 +46,8 @@ @ApiModelProperty(value = "下单城市名称") private String cityName; @ApiModelProperty(value = "下单城市编码") private String cityCode; @ApiModelProperty(value = "预计时间") @@ -131,6 +132,11 @@ @ApiModelProperty(value = "车辆id") private Long vehicleId; @ApiModelProperty(value = "车辆vin码") private String vehicleVin; @ApiModelProperty(value = "车辆颜色") private String vehicleColor; @ApiModelProperty(value = "车牌号") private String plateNumber; @@ -205,4 +211,8 @@ } @ApiModelProperty(value = "后备箱状态(1=打开,2=关闭)") private Integer openTrunk; @ApiModelProperty(value = "途径点信息") private List<XpOrderWaypoint> xpOrderWaypoints; } xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/IXpOrderService.java
@@ -226,6 +226,8 @@ R<String> feeDescription(Long orderId, String uid); XpOrderDetailVO getActiveOrder_itineraryManage(String uid); /** * 订单改价 * @param id xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/IXpOrderWaypointService.java
New file @@ -0,0 +1,16 @@ package com.xiaopeng.order.service; import com.baomidou.mybatisplus.extension.service.IService; import com.xiaopeng.api.model.order.XpOrderWaypoint; /** * <p> * 订单支付记录 服务类 * </p> * * @author mitao * @since 2026-02-03 */ public interface IXpOrderWaypointService extends IService<XpOrderWaypoint> { } xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/impl/XpOrderServiceImpl.java
@@ -16,7 +16,7 @@ import com.xiaopeng.api.dto.automaticPilot.PushOrderDto; import com.xiaopeng.api.dto.automaticPilot.VehicleEventDTO; import com.xiaopeng.api.dto.map.OrderRouteAndFareDto; import com.xiaopeng.api.dto.operation.CruisePointDto; import com.xiaopeng.api.dto.map.RouteAndFareDto; import com.xiaopeng.api.dto.operation.LonLatDto; import com.xiaopeng.api.dto.order.GetOrderNumDto; import com.xiaopeng.api.dto.order.HistoryEndPointsDto; @@ -27,18 +27,13 @@ import com.xiaopeng.api.dto.statistics.OrderStatisticsDTO; import com.xiaopeng.api.dto.statistics.StatisticsQueryDTO; import com.xiaopeng.api.dto.vehicleConvoy.BaseVehicleUserDto; import com.xiaopeng.api.dto.vehicleConvoy.UpdateVehicleStateDto; import com.xiaopeng.api.dto.vehicleConvoy.VehicleTemperatureDto; import com.xiaopeng.api.feignClient.account.UserResourcesClient; import com.xiaopeng.api.feignClient.account.XpAppUserCouponClient; import com.xiaopeng.api.feignClient.automaticPilot.OrderMissionClient; import com.xiaopeng.api.feignClient.automaticPilot.VehicleBuriedPointClient; import com.xiaopeng.api.feignClient.maintenance.XpTaskRuleClient; import com.xiaopeng.api.feignClient.map.XpMapClient; import com.xiaopeng.api.feignClient.operation.XpCruisePlanClient; import com.xiaopeng.api.feignClient.operation.XpPointClient; import com.xiaopeng.api.feignClient.operation.XpPricingClient; import com.xiaopeng.api.feignClient.operation.XpSystemSettingClient; import com.xiaopeng.api.feignClient.operation.*; import com.xiaopeng.api.feignClient.pushMessage.XpPushMessageClient; import com.xiaopeng.api.feignClient.vehicleConvoy.*; import com.xiaopeng.api.model.maintenance.XpTaskRule; @@ -46,6 +41,7 @@ import com.xiaopeng.api.model.order.*; import com.xiaopeng.api.model.vehicleConvoy.XpFleet; import com.xiaopeng.api.model.vehicleConvoy.XpVehicle; import com.xiaopeng.api.service.BootCommonService; import com.xiaopeng.api.vo.OrderType; import com.xiaopeng.api.vo.account.UserCouponVo; import com.xiaopeng.api.vo.automaticPilot.VehiclePosition; @@ -92,7 +88,6 @@ import com.xiaopeng.order.utils.GDMapGeocodingUtil; import com.xiaopeng.order.utils.TaxiPricingDemoService; import com.xiaopeng.order.utils.RegionCodeFilterUtil; import com.xiaopeng.order.utils.TaxiPricingDemoService; import com.xiaopeng.order.utils.XpPricingUtil; import com.xiaopeng.order.utils.holiday.HolidayUtil; import com.xiaopeng.order.vo.OrderDetailVo; @@ -163,6 +158,10 @@ private final VehicleBuriedPointClient vehicleBuriedPointClient; private final IXpTestOrderService xpTestOrderService; private final XpMapClient xpMapClient; @Resource private XpRoutePlanClient xpRoutePlanClient; @Resource private BootCommonService bootCommonService; @Autowired @@ -1591,11 +1590,11 @@ @ShardingSphereTransactionType(TransactionType.BASE) public void cancelOrder(Long orderId, String uid) { XpOrder order = this.getById(orderId); if (order.getStatus() == 0) { return; } if (order == null) { throw new ServiceException("订单不存在", I18nKeyConstant.ORDER_NOT_EXISTS); } if (order.getStatus() == 0) { return; } if (!order.getAppUserId().toString().equals(uid)) { throw new ServiceException("订单不存在", I18nKeyConstant.ORDER_NOT_EXISTS); @@ -1603,7 +1602,6 @@ if (order.getStatus() > 5 || order.getStatus() < 1) { throw new ServiceException("订单状态错误", I18nKeyConstant.ORDER_STATUS_ERROR); } try { redisService.removeByPattern(order.getQueueName(), order.getOrderNumber()); } catch (Exception e) { @@ -1625,39 +1623,20 @@ } } } if (order.getStatus() == 1) { order.setStatus(0); order.setAppUserId(null); baseMapper.updateById(order); redisService.setCacheObject("ORDER_STATUS:" + order.getId(), 0, 24L, TimeUnit.HOURS); return; } /* order.setStatus(0); order.setAppUserId(null); baseMapper.updateById(order); redisService.setCacheObject("ORDER_STATUS:" + order.getId(), 0, 24L, TimeUnit.HOURS); XpOrderCancel xpOrderCancel = new XpOrderCancel(); xpOrderCancel.setOrderId(orderId); xpOrderCancel.setUserId(Long.valueOf(uid)); xpOrderCancel.setCancelSource(1); xpOrderCancel.setCancelReason("用户取消订单"); xpOrderCancelService.save(xpOrderCancel); */ // 移除mongo数据 xpPricingClient.deleteXpGeoQueryEntity(order.getId().toString()); // 下发命令 if (null != order.getVehicleId()) { else{// 下发命令 R<XpVehicle> xpVehicleR = xpVehicleClient.queryById(order.getVehicleId()); // 组装调用车机参数 CancelTaxiPassengerVo cancelTaxiPassengerVo = new CancelTaxiPassengerVo(); XpVehicle xpVehicle = xpVehicleR.getData(); if (xpVehicle == null) { throw new ServiceException("车辆不存在", I18nKeyConstant.VEHICLE_NOT_EXISTS); } //是否能取消行程 boolean cancelShow = this.isCancelShow(order,xpVehicle.getVehicleVin()); if(cancelShow){ throw new ServiceException("当前订单状态不允许取消,行程即将完成", I18nKeyConstant.ORDER_CANCEL_FAILED); } // 组装调用车机参数 CancelTaxiPassengerVo cancelTaxiPassengerVo = new CancelTaxiPassengerVo(); cancelTaxiPassengerVo.setVin(xpVehicle.getVehicleVin()); cancelTaxiPassengerVo.setUid(uid); cancelTaxiPassengerVo.setOrderNo(order.getOrderNumber()); @@ -1688,7 +1667,19 @@ //车辆继续巡游 addCruiseMission(xpVehicle.getId()); } order.setStatus(0); order.setAppUserId(null); baseMapper.updateById(order); redisService.setCacheObject("ORDER_STATUS:" + order.getId(), 0, 24L, TimeUnit.HOURS); XpOrderCancel xpOrderCancel = new XpOrderCancel(); xpOrderCancel.setOrderId(orderId); xpOrderCancel.setUserId(Long.valueOf(uid)); xpOrderCancel.setCancelSource(1); xpOrderCancel.setCancelReason("用户取消订单"); xpOrderCancelService.save(xpOrderCancel); // 移除mongo数据 xpPricingClient.deleteXpGeoQueryEntity(order.getId().toString()); } @Override @@ -2058,11 +2049,13 @@ redisService.setCacheObject("ORDER_STATUS:" + order.getId(), 8, 24L, TimeUnit.HOURS); // 根据订单所属开通城市id 查询定价 R<XpPricing> pricingByCityId = xpPricingClient.getPricingByCid(order.getCityId()); XpPricing pricing = pricingByCityId.getData(); XpPricing pricing = bootCommonService.getXpPricing(order.getCityId(),order.getVehicleId()); if (pricing == null) { throw new ServiceException("当前城市未配置定价规则", I18nKeyConstant.ORDER_PRICING_NOT_EXISTS); } // 费用 // 查询订单轨迹 List<OrderTrajectory> trajectories = mongoTemplate.find( @@ -2434,7 +2427,7 @@ * * @param order */ @Override public void addCruiseMission(Long vehicleId, XpOrder order) { XpVehicle vehicle = xpVehicleClient.queryById(vehicleId).getData(); log.info("添加巡游订单 {}", vehicle.getPlateNumber()); @@ -3136,12 +3129,11 @@ @Override public R<String> feeDescription(Long orderId, String uid) { XpOrder order = this.getById(orderId); R<XpPricing> pricingByCityId = xpPricingClient.getPricingByCid(order.getCityId()); XpPricing pricing = pricingByCityId.getData(); if (pricing == null) { XpPricing xpPricing = bootCommonService.getXpPricing(order.getCityId(), order.getVehicleId()); if (xpPricing == null) { throw new ServiceException("当前城市未配置定价规则", I18nKeyConstant.ORDER_PRICING_NOT_EXISTS); } else { return R.ok(pricing.getPriceRule()); return R.ok(xpPricing.getPriceRule()); } } @@ -3478,6 +3470,9 @@ if (xpOrder.getVehicleId() != null) { R<XpVehicle> xpVehicleR = xpVehicleClient.queryById(xpOrder.getVehicleId()); XpVehicle vehicle = xpVehicleR.getData(); xpOrderDetailVO.setVehicleVin(vehicle.getVehicleVin()); xpOrderDetailVO.setVehicleModel(vehicle.getVehicleModel()); xpOrderDetailVO.setVehicleColor(vehicle.getVehicleColor()); xpOrderDetailVO.setPlateNumber(vehicle.getPlateNumber()); xpOrderDetailVO.setVehiclePictures(vehicle.getVehiclePictures()); } @@ -3489,24 +3484,118 @@ xpOrder.setAppUserId(null); this.updateById(xpOrder); } Boolean cancelShow = true; //是否能取消行程 xpOrderDetailVO.setCancelShow(cancelShow); 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(); xpOrderDetailVO.setVehicleVin(vehicle.getVehicleVin()); xpOrderDetailVO.setVehicleModel(vehicle.getVehicleModel()); xpOrderDetailVO.setVehicleColor(vehicle.getVehicleColor()); xpOrderDetailVO.setPlateNumber(vehicle.getPlateNumber()); xpOrderDetailVO.setVehiclePictures(vehicle.getVehiclePictures()); } Boolean cancelShow = isCancelShow(xpOrder,xpOrderDetailVO.getVehicleVin()); //是否能取消行程 xpOrderDetailVO.setCancelShow(cancelShow); return xpOrderDetailVO; } /** * 取消行程:若处于“送驾中”且离目的地小于10分钟路程(后台配置),【取消行程】按钮应置灰; * 修改行程: * 1. 当前剩余里程 < 800米 (后台配置)或 剩余时间 < 5分钟(后台配置)(以先到者为准),禁止修改,按钮置灰。 * 2. 单次行程最多允许修改 2次 (后台配置)目的地,防恶意高频调用 * 特殊豁免:若系统检测到车辆在当前坐标时速 < 3km/h 且持续时间 > 3分钟(后台配置)(判定为极度拥堵或异常停滞),此时解除限制,允许用户修改目的地或新增途径点以寻找更近的下车位置。 * @return */ private Boolean isCancelShow(){ private boolean isCancelShow(XpOrder xpOrder,String vehicleVin) { boolean isCancel = false; //获取配置文件 int sylc = 800;//单位米 todo yyy 需要读取后台配置文件 int sysj = 5;//单位分钟 todo yyy 需要读取后台配置文件 // 减少3分钟 获取3分账内的 行程数据 long minute =3;//todo yyy 需要读取后台配置文件 return true; List<OrderTrajectory> orderTrajectories = queryOrderTrajectories(xpOrder.getId()); if(!orderTrajectories.isEmpty()){// 减少3分钟 获取3分账内的 行程数据 //3千米/小时 转换成分钟 50米/分钟 BigDecimal suduDefault = new BigDecimal("3");//3千米/小时 BigDecimal suduDefault1 = suduDefault.multiply(BigDecimal.valueOf(1000)) ; BigDecimal suduDefaultNew =suduDefault1.divide(BigDecimal.valueOf(60),2,RoundingMode.HALF_EVEN); LocalDateTime minutesBefore = LocalDateTime.now().minusMinutes(minute); long milliseconds = DateUtils.getMilliseconds(minutesBefore); List<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(minute),2, RoundingMode.HALF_EVEN);//米/分钟 //特殊豁免:若系统检测到车辆在当前坐标时速 < 3km/h 且持续时间 > 3分钟(后台配置)(判定为极度拥堵或异常停滞),此时解除限制,允许用户修改目的地或新增途径点以寻找更近的下车位置。 if(xcsudu.compareTo(suduDefaultNew)<0){ return true; } } RouteVo routeVo = this.getRouteVo(xpOrder, vehicleVin); if(routeVo.getDistance() <sylc){ isCancel = true; } if(routeVo.getDuration() < sysj){ isCancel = true; } 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(); } xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/impl/XpOrderWaypointServiceImpl.java
New file @@ -0,0 +1,24 @@ package com.xiaopeng.order.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.xiaopeng.api.model.order.XpOrderWaypoint; import com.xiaopeng.order.mapper.XpOrderWaypointMapper; import com.xiaopeng.order.service.IXpOrderWaypointService; import org.springframework.stereotype.Service; /** * <p> * 订单支付记录 服务实现类 * </p> * * @author mitao * @since 2026-02-03 */ @Service public class XpOrderWaypointServiceImpl extends ServiceImpl<XpOrderWaypointMapper, XpOrderWaypoint> implements IXpOrderWaypointService { } xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/impl/XpTestOrderServiceImpl.java
@@ -9,12 +9,10 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.google.common.collect.ImmutableMap; import com.xiaopeng.api.dto.account.GetUserCouponDto; import com.xiaopeng.api.dto.automaticPilot.PushOrderDto; import com.xiaopeng.api.dto.map.RouteAndFareDto; import com.xiaopeng.api.dto.operation.LonLatDto; import com.xiaopeng.api.dto.order.GetOrderNumDto; import com.xiaopeng.api.dto.pushMessage.SendMessageDTO; import com.xiaopeng.api.feignClient.account.UserResourcesClient; import com.xiaopeng.api.feignClient.account.XpAppUserCouponClient; import com.xiaopeng.api.feignClient.automaticPilot.OrderMissionClient; @@ -31,16 +29,14 @@ import com.xiaopeng.api.model.order.*; import com.xiaopeng.api.model.vehicleConvoy.XpFleet; import com.xiaopeng.api.model.vehicleConvoy.XpVehicle; import com.xiaopeng.api.service.BootCommonService; import com.xiaopeng.api.vo.OrderType; import com.xiaopeng.api.vo.SystemEventType; 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.operation.RouteVo; import com.xiaopeng.api.vo.order.FareBreakdownDetailVo; import com.xiaopeng.api.vo.order.XpPricingCardVo; import com.xiaopeng.common.core.constant.CacheConstants; import com.xiaopeng.common.core.constant.Constants; import com.xiaopeng.common.core.constant.HttpStatus; import com.xiaopeng.common.core.domain.R; import com.xiaopeng.common.core.exception.ServiceException; @@ -54,7 +50,6 @@ import com.xiaopeng.order.constant.I18nKeyConstant; import com.xiaopeng.order.job.base.QuartzManager; import com.xiaopeng.order.job.base.TimeJobType; import com.xiaopeng.order.job.jobs.AddCruiseMissionJob; import com.xiaopeng.order.job.jobs.EndTripJob; import com.xiaopeng.order.job.jobs.OrderCancelJob; import com.xiaopeng.order.mangodb.model.OrderTrajectory; @@ -88,14 +83,13 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; import javax.annotation.Resource; import java.math.BigDecimal; import java.math.RoundingMode; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.ZoneOffset; import java.util.*; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.function.Function; import java.util.stream.Collectors; @@ -145,6 +139,8 @@ private final XpTestPointClient testPointClient; @Resource private BootCommonService bootCommonService; @@ -192,7 +188,7 @@ if (openCity == null) { throw new ServiceException("当前区域未开通服务", I18nKeyConstant.NO_CURRENT_REGION); } XpPricing xpPricing = xpPricingClient.getPricingByCid(openCity.getId()).getData(); XpPricing xpPricing = bootCommonService.getXpPricing(openCity.getId(),null); if (xpPricing == null) { throw new ServiceException("当前区域未开通服务", I18nKeyConstant.NO_CURRENT_REGION); } @@ -995,8 +991,7 @@ redisService.setCacheObject("TEST_ORDER_STATUS:" + order.getId(), 8, 24L, TimeUnit.HOURS); // 根据订单所属开通城市id 查询定价 R<XpPricing> pricingByCityId = xpPricingClient.getPricingByCid(order.getCityId()); XpPricing pricing = pricingByCityId.getData(); XpPricing pricing = bootCommonService.getXpPricing(order.getCityId(),order.getVehicleId()); if (pricing == null) { throw new ServiceException("当前城市未配置定价规则", I18nKeyConstant.ORDER_PRICING_NOT_EXISTS); } xp-robotaxi-push-message-boot/src/main/java/com/xiaopeng/pushMessage/controller/SubscribeMessageController.java
New file @@ -0,0 +1,51 @@ package com.xiaopeng.pushMessage.controller; import com.aliyun.core.annotation.Path; import com.xiaopeng.api.feignClient.account.UserSubscribeMessageClient; import com.xiaopeng.api.vo.pushMessage.SendSubscribeMessageVo; import com.xiaopeng.common.core.domain.R; import com.xiaopeng.pushMessage.utils.SubscribeMessageUtil; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import java.util.Map; @RestController @RequestMapping("") public class SubscribeMessageController { @Resource private SubscribeMessageUtil subscribeMessageUtil; @Resource private UserSubscribeMessageClient userSubscribeMessageClient; /** * 根据微信jscode获取openid * @param code * @return */ @GetMapping("/logan/inner/v1/subscribeMessage/getOpenId/{code}") public R<String> getOpenId(@PathVariable("code") String code){ Map<String, Object> map = subscribeMessageUtil.code2Session(code); if(null != map){ return R.ok(map.get("openid").toString()); } return R.fail("获取openid失败"); } /** * 发送订阅消息 * @param vo * @return */ @PostMapping("/logan/inner/v1/subscribeMessage/sendSubscribeMessage") public R<?> sendSubscribeMessage(@RequestBody SendSubscribeMessageVo vo){ String openid = userSubscribeMessageClient.getUserOpenId(vo.getUserId().intValue()).getData(); return subscribeMessageUtil.send(vo.getTemplateId(), vo.getPage(), openid, vo.getJson(), "developer", "zh_CN"); } } xp-robotaxi-push-message-boot/src/main/java/com/xiaopeng/pushMessage/model/Category.java
New file @@ -0,0 +1,18 @@ package com.xiaopeng.pushMessage.model; import lombok.Data; /** * 订阅消息类目 */ @Data public class Category { /** * 类目id */ private String id; /** * 类目名称 */ private String name; } xp-robotaxi-push-message-boot/src/main/java/com/xiaopeng/pushMessage/model/KeywordEnum.java
New file @@ -0,0 +1,17 @@ package com.xiaopeng.pushMessage.model; import lombok.Data; import java.util.List; @Data public class KeywordEnum { /** * 枚举参数的 key */ private String keywordCode; /** * 枚举参数值范围列表 */ private List<String> enumValueList; } xp-robotaxi-push-message-boot/src/main/java/com/xiaopeng/pushMessage/model/MessageTemplate.java
New file @@ -0,0 +1,36 @@ package com.xiaopeng.pushMessage.model; import lombok.Data; import java.util.List; /** * 消息模板 */ @Data public class MessageTemplate { /** * 添加至帐号下的模板 id,发送小程序订阅消息时所需 */ private String priTmplId; /** * 模版标题 */ private String title; /** * 模版内容 */ private String content; /** * 模板内容示例 */ private String example; /** * 模版类型,2 为一次性订阅,3 为长期订阅 */ private Integer type; /** * 枚举参数值范围 */ private List<KeywordEnum> keywordEnumValueList; } xp-robotaxi-push-message-boot/src/main/java/com/xiaopeng/pushMessage/model/PubTemplateKeywords.java
New file @@ -0,0 +1,26 @@ package com.xiaopeng.pushMessage.model; import lombok.Data; /** * 公共消息模板关键字 */ @Data public class PubTemplateKeywords { /** * 关键词 id,选用模板时需要 */ private Integer kid; /** * 关键词内容 */ private String name; /** * 关键词内容对应的示例 */ private String example; /** * 参数类型 */ private String rule; } xp-robotaxi-push-message-boot/src/main/java/com/xiaopeng/pushMessage/model/PubTemplatetitles.java
New file @@ -0,0 +1,26 @@ package com.xiaopeng.pushMessage.model; import lombok.Data; /** * 功能模板 */ @Data public class PubTemplatetitles { /** * 模版标题 id */ private Integer tid; /** * 模版标题 */ private String title; /** * 模版类型,2 为一次性订阅,3 为长期订阅 */ private Integer type; /** * 模版所属类目 id */ private Integer categoryId; } xp-robotaxi-push-message-boot/src/main/java/com/xiaopeng/pushMessage/utils/SubscribeMessageUtil.java
New file @@ -0,0 +1,301 @@ package com.xiaopeng.pushMessage.utils; import cn.hutool.http.HttpRequest; import cn.hutool.http.HttpResponse; import cn.hutool.http.HttpUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.xiaopeng.common.core.domain.R; import com.xiaopeng.common.core.utils.StringUtils; import com.xiaopeng.common.redis.service.RedisService; import com.xiaopeng.pushMessage.model.Category; import com.xiaopeng.pushMessage.model.MessageTemplate; import com.xiaopeng.pushMessage.model.PubTemplateKeywords; import com.xiaopeng.pushMessage.model.PubTemplatetitles; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import java.util.*; import java.util.concurrent.TimeUnit; /** * 订阅消息 */ @Slf4j @Component public class SubscribeMessageUtil { @Value("${wx.appletsAppid}") private String wxAppletsAppid; @Value("${wx.appletsAppSecret}") private String wxAppletsAppSecret; @Autowired private RedisService redisService; /** * 获取微信小程序token * @return */ public String getWxAppletsAccessToken() { String accessToken = redisService.getCacheObject("wxAppletsAccessToken"); if(StringUtils.isNotEmpty(accessToken)){ return accessToken; } String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + wxAppletsAppid + "&secret=" + wxAppletsAppSecret; HttpRequest get = HttpUtil.createGet(url); HttpResponse execute = get.execute(); if(execute.getStatus() != 200){ log.info("获取微信小程序access_token失败:{}", execute.body()); throw new RuntimeException("获取微信小程序access_token失败"); } JSONObject jsonObject = JSON.parseObject(execute.body()); redisService.setCacheObject("wxAppletsAccessToken", jsonObject.getString("access_token"), 7000L, TimeUnit.SECONDS); return jsonObject.getString("access_token"); } /** * 小程序使用jscode获取openid * @param jscode * @return */ public Map<String, Object> code2Session(String jscode) { String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + wxAppletsAppid + "&secret=" + wxAppletsAppSecret + "&js_code=" + jscode + "&grant_type=authorization_code"; HttpRequest get = HttpUtil.createGet(url); HttpResponse execute = get.execute(); if(execute.getStatus() != 200){ log.info("小程序使用jscode获取openid失败:{}", execute.body()); throw new RuntimeException("小程序使用jscode获取openid失败"); } JSONObject jsonObject = JSON.parseObject(execute.body()); int errcode = jsonObject.getIntValue("errcode"); Map<String, Object> map = new HashMap<>(); if(errcode == 0){//成功 map.put("openid", jsonObject.getString("openid")); map.put("sessionKey", jsonObject.getString("session_key")); map.put("unionid", jsonObject.getString("unionid")); return map; } throw new RuntimeException(jsonObject.getString("errmsg")); } /** * 获取消息类目 * @return */ public List<Category> getCategory(){ String url = "https://api.weixin.qq.com/wxaapi/newtmpl/getcategory?access_token=" + getWxAppletsAccessToken(); HttpRequest get = HttpUtil.createGet(url); HttpResponse execute = get.execute(); if(execute.getStatus() != 200){ log.info("获取消息类目失败:{}", execute.body()); throw new RuntimeException("获取消息类目失败"); } JSONObject jsonObject = JSON.parseObject(execute.body()); Integer errcode = jsonObject.getInteger("errcode"); if(0 != errcode){ log.info("获取消息类目失败:{}", jsonObject.getString("errmsg")); throw new RuntimeException(jsonObject.getString("errmsg")); } JSONArray data = jsonObject.getJSONArray("data"); List<Category> categories = data.toJavaList(Category.class); return categories; } /** * 获取所属类目下的公共模板 * @param ids * @return */ public List<PubTemplatetitles> getPubTemplateTitles(String ids){ List<PubTemplatetitles> pubTemplatetitles = new ArrayList<>(); Integer start = 0; while (true){ String url = "https://api.weixin.qq.com/wxaapi/newtmpl/getpubtemplatetitles?access_token=" + getWxAppletsAccessToken(); Map<String, Object> params = new HashMap<>(); params.put("ids", ids); params.put("start", start + ""); params.put("limit", 30); HttpRequest get = HttpUtil.createGet(url); get.form(params); HttpResponse execute = get.execute(); if(execute.getStatus() != 200){ log.info("获取公共模板失败:{}", execute.body()); throw new RuntimeException("获取公共模板失败"); } JSONObject jsonObject = JSON.parseObject(execute.body()); Integer errcode = jsonObject.getInteger("errcode"); if(0 != errcode){ log.info("获取公共模板失败:{}", jsonObject.getString("errmsg")); throw new RuntimeException(jsonObject.getString("errmsg")); } JSONArray data = jsonObject.getJSONArray("data"); List<PubTemplatetitles> pubTemplatetitles1 = data.toJavaList(PubTemplatetitles.class); if(pubTemplatetitles1.size() == 0){ break; } pubTemplatetitles.addAll(pubTemplatetitles1); start += 30; } return pubTemplatetitles; } /** * 获取关键词列表 * @param tid * @return */ public List<PubTemplateKeywords> getPubTemplateKeywords(String tid){ String url = "https://api.weixin.qq.com/wxaapi/newtmpl/getpubtemplatekeywords?access_token=" + getWxAppletsAccessToken(); Map<String, Object> params = new HashMap<>(); params.put("tid", tid); HttpRequest get = HttpUtil.createGet(url); get.form(params); HttpResponse execute = get.execute(); if(execute.getStatus() != 200){ log.info("获取关键词列表失败:{}", execute.body()); throw new RuntimeException("获取关键词列表失败"); } JSONObject jsonObject = JSON.parseObject(execute.body()); Integer errcode = jsonObject.getInteger("errcode"); if(0 != errcode){ log.info("获取关键词列表失败:{}", jsonObject.getString("errmsg")); throw new RuntimeException(jsonObject.getString("errmsg")); } JSONArray data = jsonObject.getJSONArray("data"); List<PubTemplateKeywords> pubTemplateKeywords = data.toJavaList(PubTemplateKeywords.class); return pubTemplateKeywords; } /** * 获取个人模板列表 * @return */ public List<MessageTemplate> getMessageTemplateList(){ String url = "https://api.weixin.qq.com/wxaapi/newtmpl/gettemplate?access_token=" + getWxAppletsAccessToken(); HttpRequest get = HttpUtil.createGet(url); HttpResponse execute = get.execute(); if(execute.getStatus() != 200){ log.info("获取个人模板列表失败:{}", execute.body()); throw new RuntimeException("获取个人模板列表失败"); } JSONObject jsonObject = JSON.parseObject(execute.body()); Integer errcode = jsonObject.getInteger("errcode"); if(0 != errcode){ log.info("获取个人模板列表失败:{}", jsonObject.getString("errmsg")); throw new RuntimeException(jsonObject.getString("errmsg")); } JSONArray data = jsonObject.getJSONArray("data"); List<MessageTemplate> messageTemplates = data.toJavaList(MessageTemplate.class); return messageTemplates; } /** * 添加消息模板 * @param tid 模板标题 id * @param kidList 开发者自行组合好的模板关键词列表,关键词顺序可以自由搭配(例如 [3,5,4] 或 [4,5,3]),最多支持5个,最少2个关键词组合 * @param sceneDesc 服务场景描述,15个字以内 * @return */ public String addTemplate(String tid, String[] kidList, String sceneDesc){ String url = "https://api.weixin.qq.com/wxaapi/newtmpl/addtemplate?access_token=" + getWxAppletsAccessToken(); Map<String, Object> params = new HashMap<>(); params.put("tid", tid); params.put("kidList", kidList); params.put("sceneDesc", sceneDesc); HttpRequest post = HttpUtil.createPost(url); post.body(JSON.toJSONString(params)); HttpResponse execute = post.execute(); if(execute.getStatus() != 200){ log.info("添加消息模板失败:{}", execute.body()); throw new RuntimeException("添加消息模板失败"); } JSONObject jsonObject = JSON.parseObject(execute.body()); Integer errcode = jsonObject.getInteger("errcode"); if(0 != errcode){ log.info("添加消息模板失败:{}", jsonObject.getString("errmsg")); throw new RuntimeException(jsonObject.getString("errmsg")); } String priTmplId = jsonObject.getString("priTmplId"); return priTmplId; } /** * 删除模板 * @param priTmplId 模板id * @return */ public R delTemplate(String priTmplId){ String url = "https://api.weixin.qq.com/wxaapi/newtmpl/deltemplate?access_token=" + getWxAppletsAccessToken(); Map<String, Object> params = new HashMap<>(); params.put("priTmplId", priTmplId); HttpRequest post = HttpUtil.createPost(url); post.body(JSON.toJSONString(params)); HttpResponse execute = post.execute(); if (execute.getStatus() != 200){ log.info("删除模板失败:{}", execute.body()); throw new RuntimeException("删除模板失败"); } JSONObject jsonObject = JSON.parseObject(execute.body()); Integer errcode = jsonObject.getInteger("errcode"); if(0 != errcode){ log.info("删除模板失败:{}", jsonObject.getString("errmsg")); throw new RuntimeException(jsonObject.getString("errmsg")); } return R.ok(); } /** * 发送消息 * @param template_id 所需下发的订阅模板id * @param page 点击模板卡片后的跳转页面,仅限本小程序内的页面。支持带参数,(示例index?foo=bar)。该字段不填则模板无跳转 * @param touser 接收者(用户)的 openid * @param data 模板内容,格式形如 { "key1": { "value": any }, "key2": { "value": any } }的object * @param miniprogram_state 跳转小程序类型:developer为开发版;trial为体验版;formal为正式版;默认为正式版 * @param lang 进入小程序查看”的语言类型,支持zh_CN(简体中文)、en_US(英文)、zh_HK(繁体中文)、zh_TW(繁体中文),默认为zh_CN * @return */ public R send(String template_id, String page, String touser, String data, String miniprogram_state, String lang){ String url = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=" + getWxAppletsAccessToken(); Map<String, Object> params = new HashMap<>(); params.put("template_id", template_id); params.put("page", page); params.put("touser", touser); params.put("data", JSON.parseObject(data)); params.put("miniprogram_state", miniprogram_state); params.put("lang", lang); HttpRequest post = HttpUtil.createPost(url); post.body(JSON.toJSONString(params)); HttpResponse execute = post.execute(); if(execute.getStatus() != 200){ log.info("发送消息失败:{}", execute.body()); throw new RuntimeException("发送消息失败"); } JSONObject jsonObject = JSON.parseObject(execute.body()); Integer errcode = jsonObject.getInteger("errcode"); if(0 != errcode){ log.info("发送消息失败:{}", jsonObject.getString("errmsg")); throw new RuntimeException(jsonObject.getString("errmsg")); } return R.ok(); } }