xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/constant/I18nKeyConstant.java
@@ -35,4 +35,16 @@ //开通城市已存在 String OPEN_CITY_EXISTS = "open.city.exists"; //定价已存在 String PRICING_EXISTS = "pricing.exists"; //定价不存在 String PRICING_NOT_EXIST = "pricing.not.exist"; //价格配置json格式错误 String PRICING_JSON_INVALID = "pricing.json.invalid"; //价格规则不能为空 String PRICING_RULE_EMPTY = "pricing.rule.empty"; } xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/controller/XpPricingController.java
@@ -2,17 +2,23 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.xiaopeng.api.dto.operation.LonLatDto; import com.xiaopeng.api.model.operation.XpGeoQueryEntity; import com.xiaopeng.api.model.operation.XpPricing; 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.api.dto.map.RouteAndFareDto; import com.xiaopeng.api.dto.operation.LonLatDto; import com.xiaopeng.api.model.operation.XpPricing; import com.xiaopeng.api.vo.operation.RouteAndFareVo; import com.xiaopeng.common.core.domain.R; import com.xiaopeng.common.core.exception.ServiceException; import com.xiaopeng.common.core.web.controller.BaseController; import com.xiaopeng.common.core.web.page.PageInfo; import com.xiaopeng.operation.constant.I18nKeyConstant; import com.xiaopeng.operation.dto.PricingAddDTO; import com.xiaopeng.operation.dto.PricingSetPriceDTO; import com.xiaopeng.operation.dto.PricingSetRuleDTO; import com.xiaopeng.operation.dto.XpOrderDto; import com.xiaopeng.operation.query.PricingPageListQuery; import com.xiaopeng.operation.service.IXpGeoQueryEntityService; import com.xiaopeng.operation.service.XpPricingService; import com.xiaopeng.api.vo.operation.RouteAndFareVo; import com.xiaopeng.operation.vo.PricingPageListVO; import com.xiaopeng.operation.vo.SaveOrderVo; import com.xiaopeng.unifiedverification.interceptor.XpClientCheck; import io.swagger.annotations.Api; @@ -23,7 +29,6 @@ import javax.servlet.http.HttpServletRequest; import javax.validation.Valid; import java.util.Date; /** * <p> @@ -116,5 +121,53 @@ return R.ok(saveOrderVo); } @PostMapping("/api/v1/pricing/pageList") @ApiOperation(value = "管理后台-定价管理分页列表", tags = {"管理后台-定价管理"}) public R<PageInfo<PricingPageListVO>> pageList(@RequestBody PricingPageListQuery query) { return R.ok(xpPricingService.pageList(query)); } @GetMapping("/api/v1/pricing/queryById") @ApiOperation(value = "管理后台-定价管理-详情", tags = {"管理后台-定价管理"}, response = XpPricing.class) public R<XpPricing> queryById(@RequestParam Long id) { XpPricing pricing = xpPricingService.getById(id); if (pricing == null) { throw new ServiceException("定价不存在", I18nKeyConstant.PRICING_NOT_EXIST); } return R.ok(pricing); } @PostMapping("/api/v1/pricing/add") @ApiOperation(value = "管理后台-新增定价", tags = {"管理后台-定价管理"}) public R<?> add(@Valid @RequestBody PricingAddDTO dto) { xpPricingService.add(dto); return R.ok(); } @PostMapping("/api/v1/pricing/setPrice") @ApiOperation(value = "管理后台-定价管理-设置价格", tags = {"管理后台-定价管理"}) public R<?> setPrice(@Valid @RequestBody PricingSetPriceDTO dto) { xpPricingService.setPrice(dto); return R.ok(); } @PostMapping("/api/v1/pricing/setRule") @ApiOperation(value = "管理后台-计费规则富文本配置", tags = {"管理后台-定价管理"}) public R<?> setRule(@Valid @RequestBody PricingSetRuleDTO dto) { xpPricingService.setRule(dto); return R.ok(); } @DeleteMapping("/api/v1/pricing/deleteById") @ApiOperation(value = "管理后台-定价管理-删除", tags = {"管理后台-定价管理"}) public R<?> deleteById(@RequestParam Long id) { XpPricing pricing = xpPricingService.getById(id); if (pricing == null) { throw new ServiceException("定价不存在", I18nKeyConstant.PRICING_NOT_EXIST); } xpPricingService.removeById(id); return R.ok(); } } xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/dto/PricingAddDTO.java
New file @@ -0,0 +1,39 @@ package com.xiaopeng.operation.dto; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import javax.validation.constraints.Max; import javax.validation.constraints.Min; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; /** * 定价新增数据传输对象 */ @Data @ApiModel(value = "定价新增数据传输对象") public class PricingAddDTO { @ApiModelProperty(value = "车型", required = true) @NotBlank(message = "车型不能为空") private String vehicleModel; @ApiModelProperty(value = "渠道", required = true) @NotBlank(message = "渠道不能为空") private String channel; @ApiModelProperty(value = "开通城市ID", required = true) @NotNull(message = "开通城市ID不能为空") private Long cityId; @ApiModelProperty(value = "说明") private String remark; @ApiModelProperty(value = "计费规则 1动态计费 2一口价", required = true) @NotNull(message = "计费规则不能为空") @Min(value = 1, message = "计费规则不合法") @Max(value = 2, message = "计费规则不合法") private Integer pricingType; } xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/dto/PricingSetPriceDTO.java
New file @@ -0,0 +1,32 @@ 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; /** * 定价设置价格数据传输对象 */ @Data @ApiModel(value = "定价设置价格数据传输对象") public class PricingSetPriceDTO { @ApiModelProperty(value = "定价ID", required = true) @NotNull(message = "定价ID不能为空") private Long id; @ApiModelProperty(value = "基础价格json配置", required = true) @NotBlank(message = "基础价格json配置不能为空") private String basicPricingJson; @ApiModelProperty(value = "起步价json配置", required = true) @NotBlank(message = "起步价json配置不能为空") private String startingFareJson; @ApiModelProperty(value = "动态价格json配置", required = true) @NotBlank(message = "动态价格json配置不能为空") private String dynamicPricingJson; } xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/dto/PricingSetRuleDTO.java
New file @@ -0,0 +1,24 @@ 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; /** * 定价计费规则富文本配置数据传输对象 */ @Data @ApiModel(value = "定价计费规则富文本配置数据传输对象") public class PricingSetRuleDTO { @ApiModelProperty(value = "定价ID", required = true) @NotNull(message = "定价ID不能为空") private Long id; @ApiModelProperty(value = "价格规则", required = true) @NotBlank(message = "价格规则不能为空") private String priceRule; } xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/mapper/XpPricingMapper.java
@@ -2,7 +2,12 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.xiaopeng.api.model.operation.XpPricing; import com.xiaopeng.common.core.web.page.PageInfo; import com.xiaopeng.operation.query.PricingPageListQuery; import com.xiaopeng.operation.vo.PricingPageListVO; import org.apache.ibatis.annotations.Param; /** * <p> @@ -14,4 +19,13 @@ */ public interface XpPricingMapper extends BaseMapper<XpPricing> { /** * 定价管理分页列表 * * @param pageInfo 分页信息 * @param query 查询条件 * @return 分页数据 */ Page<PricingPageListVO> pageList(@Param("pageInfo") PageInfo<PricingPageListVO> pageInfo, @Param("query") PricingPageListQuery query); } xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/query/PricingPageListQuery.java
New file @@ -0,0 +1,22 @@ 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; /** * 定价管理分页查询对象 */ @Data @EqualsAndHashCode(callSuper = true) @ApiModel(value = "定价管理分页查询对象") public class PricingPageListQuery extends BasePage { @ApiModelProperty(value = "车型") private String vehicleModel; @ApiModelProperty(value = "城市") private String cityName; } xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/XpPricingService.java
@@ -2,10 +2,16 @@ import com.baomidou.mybatisplus.extension.service.IService; import com.xiaopeng.api.model.operation.XpPricing; import com.xiaopeng.api.dto.map.RouteAndFareDto; import com.xiaopeng.api.model.operation.XpPricing; import com.xiaopeng.api.vo.operation.RouteAndFareVo; import com.xiaopeng.common.core.web.page.PageInfo; import com.xiaopeng.operation.dto.PricingAddDTO; import com.xiaopeng.operation.dto.PricingSetPriceDTO; import com.xiaopeng.operation.dto.PricingSetRuleDTO; import com.xiaopeng.operation.dto.XpOrderDto; import com.xiaopeng.operation.query.PricingPageListQuery; import com.xiaopeng.operation.vo.PricingPageListVO; /** * <p> @@ -45,4 +51,33 @@ */ Long saveOrder(XpOrderDto dto,String uid); /** * 管理后台-定价管理分页列表 * * @param query 查询条件 * @return 分页结果 */ PageInfo<PricingPageListVO> pageList(PricingPageListQuery query); /** * 管理后台-新增定价 * * @param dto 新增参数 */ void add(PricingAddDTO dto); /** * 管理后台-定价管理-设置价格 * * @param dto 设置参数 */ void setPrice(PricingSetPriceDTO dto); /** * 管理后台-计费规则富文本配置 * * @param dto 配置参数 */ void setRule(PricingSetRuleDTO dto); } xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/impl/XpPricingServiceImpl.java
@@ -4,6 +4,7 @@ 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; import com.xiaopeng.api.dto.account.GetUserCouponDto; import com.xiaopeng.api.dto.map.RouteAndFareDto; @@ -12,7 +13,6 @@ import com.xiaopeng.api.feignClient.account.XpAppUserCouponClient; import com.xiaopeng.api.feignClient.map.XpMapClient; import com.xiaopeng.api.feignClient.order.XpOrderClient; import com.xiaopeng.api.feignClient.vehicleConvoy.XpVehicleClient; import com.xiaopeng.api.model.account.XpAppUser; import com.xiaopeng.api.model.operation.*; import com.xiaopeng.api.model.order.XpOrder; @@ -26,23 +26,27 @@ import com.xiaopeng.common.core.exception.ServiceException; import com.xiaopeng.common.core.utils.DateUtils; import com.xiaopeng.common.core.utils.uuid.UUID; import com.xiaopeng.common.core.web.page.PageInfo; import com.xiaopeng.common.redis.service.RedisService; import com.xiaopeng.operation.constant.I18nKeyConstant; import com.xiaopeng.operation.dto.PricingAddDTO; import com.xiaopeng.operation.dto.PricingSetPriceDTO; import com.xiaopeng.operation.dto.PricingSetRuleDTO; import com.xiaopeng.operation.dto.XpOrderDto; import com.xiaopeng.api.model.operation.XpGeoQueryEntity; import com.xiaopeng.operation.mapper.*; import com.xiaopeng.operation.query.PricingPageListQuery; import com.xiaopeng.operation.service.IXpGeoQueryEntityService; import com.xiaopeng.operation.service.XpPricingService; import com.xiaopeng.operation.utils.XpPricingUtil; import com.xiaopeng.operation.utils.holiday.HolidayUtil; import com.xiaopeng.operation.utils.map.FastDistanceCalculator; import com.xiaopeng.operation.vo.PricingPageListVO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.mongodb.core.geo.GeoJsonPoint; import org.springframework.stereotype.Service; import javax.annotation.PreDestroy; import javax.annotation.Resource; import java.time.Duration; import java.time.LocalDateTime; import java.util.*; import java.util.concurrent.*; @@ -614,5 +618,78 @@ return false; } @Override public PageInfo<PricingPageListVO> pageList(PricingPageListQuery query) { PageInfo<PricingPageListVO> pageInfo = new PageInfo<>(query.getPageNum(), query.getPageSize()); Page<PricingPageListVO> page = this.baseMapper.pageList(pageInfo, query); pageInfo.setRecords(page.getRecords()); pageInfo.setTotal(page.getTotal()); return pageInfo; } @Override public void add(PricingAddDTO dto) { XpOpenCity openCity = xpOpenCityMapper.selectById(dto.getCityId()); if (openCity == null) { throw new ServiceException("开通城市不存在"); } Long count = this.lambdaQuery() .eq(XpPricing::getVehicleModel, dto.getVehicleModel()) .eq(XpPricing::getChannel, dto.getChannel()) .eq(XpPricing::getCityId, dto.getCityId().intValue()) .count(); if (count != null && count > 0) { throw new ServiceException("该车型/渠道/城市的定价已存在", I18nKeyConstant.PRICING_EXISTS); } XpPricing pricing = new XpPricing(); pricing.setVehicleModel(dto.getVehicleModel()); pricing.setChannel(dto.getChannel()); pricing.setCityId(dto.getCityId().intValue()); pricing.setRemark(dto.getRemark()); pricing.setPricingType(dto.getPricingType()); pricing.setBasicPricingJson("{}"); pricing.setStartingFareJson("[]"); pricing.setDynamicPricingJson("{}"); pricing.setIsDynamicPrice(0); this.save(pricing); } @Override public void setPrice(PricingSetPriceDTO dto) { XpPricing pricing = this.getById(dto.getId()); if (pricing == null) { throw new ServiceException("定价不存在", I18nKeyConstant.PRICING_NOT_EXIST); } try { JSONObject.parseObject(dto.getBasicPricingJson()); JSONArray.parseArray(dto.getStartingFareJson()); JSONObject.parseObject(dto.getDynamicPricingJson()); } catch (Exception e) { throw new ServiceException("价格配置JSON格式不正确", I18nKeyConstant.PRICING_JSON_INVALID); } XpPricing update = new XpPricing(); update.setId(dto.getId()); update.setBasicPricingJson(dto.getBasicPricingJson()); update.setStartingFareJson(dto.getStartingFareJson()); update.setDynamicPricingJson(dto.getDynamicPricingJson()); this.updateById(update); } @Override public void setRule(PricingSetRuleDTO dto) { XpPricing pricing = this.getById(dto.getId()); if (pricing == null) { throw new ServiceException("定价不存在", I18nKeyConstant.PRICING_NOT_EXIST); } XpPricing update = new XpPricing(); update.setId(dto.getId()); update.setPriceRule(dto.getPriceRule()); this.updateById(update); } } xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/vo/PricingPageListVO.java
New file @@ -0,0 +1,33 @@ package com.xiaopeng.operation.vo; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.io.Serializable; import java.util.Date; /** * 定价管理分页列表项 */ @Data @ApiModel(value = "定价管理分页列表项") public class PricingPageListVO implements Serializable { private static final long serialVersionUID = 1L; @ApiModelProperty(value = "创建时间") private Date createTime; @ApiModelProperty(value = "车型") private String vehicleModel; @ApiModelProperty(value = "使用城市") private String useCity; @ApiModelProperty(value = "渠道") private String channel; @ApiModelProperty(value = "计费规则 1动态计费 2一口价") private Integer pricingType; } xp-robotaxi-operation-boot/src/main/resources/i18n/messages.properties
@@ -12,4 +12,8 @@ fence.not.exist=围栏不存在 fence.status.cannot.copied=围栏启用状态不可复制; order.create.fail=订单创建失败 open.city.exists=该城市已存在 open.city.exists=该城市已存在 pricing.exists=该车型/渠道/城市的定价已存在 pricing.not.exist=定价不存在 pricing.json.invalid=价格配置JSON格式不正确 pricing.rule.empty=价格规则不能为空 xp-robotaxi-operation-boot/src/main/resources/i18n/messages_en_US.properties
@@ -12,4 +12,8 @@ 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 open.city.exists=This city already exists open.city.exists=This city already exists pricing.exists=Pricing for this vehicle model/channel/city already exists pricing.not.exist=Pricing does not exist pricing.json.invalid=Invalid JSON format for pricing configuration pricing.rule.empty=Price rule cannot be empty xp-robotaxi-operation-boot/src/main/resources/i18n/messages_zh_CN.properties
@@ -12,4 +12,8 @@ fence.not.exist=围栏不存在 fence.status.cannot.copied=围栏启用状态不可复制 order.create.fail=订单创建失败 open.city.exists=该城市已存在 open.city.exists=该城市已存在 pricing.exists=该车型/渠道/城市的定价已存在 pricing.not.exist=定价不存在 pricing.json.invalid=价格配置JSON格式不正确 pricing.rule.empty=价格规则不能为空 xp-robotaxi-operation-boot/src/main/resources/mapper/operation/XpPricingMapper.xml
@@ -2,6 +2,30 @@ <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.xiaopeng.operation.mapper.XpPricingMapper"> <select id="pageList" resultType="com.xiaopeng.operation.vo.PricingPageListVO"> SELECT p.create_time AS createTime, p.vehicle_model AS vehicleModel, CASE WHEN oc.district_name IS NOT NULL AND oc.district_name != '' THEN CONCAT(oc.city_name, '->', oc.district_name) ELSE oc.city_name END AS useCity, p.channel AS channel, p.pricing_type AS pricingType FROM xp_pricing p LEFT JOIN xp_open_city oc ON p.city_id = oc.id WHERE p.is_deleted = 0 <if test="query.vehicleModel != null and query.vehicleModel != ''"> AND p.vehicle_model LIKE CONCAT('%', #{query.vehicleModel}, '%') </if> <if test="query.cityName != null and query.cityName != ''"> AND ( oc.city_name LIKE CONCAT('%', #{query.cityName}, '%') OR oc.district_name LIKE CONCAT('%', #{query.cityName}, '%') ) </if> ORDER BY p.create_time DESC </select> </mapper>