“mitao”
2026-02-14 6c0ffe28157728b62a0983b4a0ed993591f3d6a8
feat(management):定价管理
5个文件已添加
9个文件已修改
409 ■■■■■ 已修改文件
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/constant/I18nKeyConstant.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/controller/XpPricingController.java 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/dto/PricingAddDTO.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/dto/PricingSetPriceDTO.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/dto/PricingSetRuleDTO.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/mapper/XpPricingMapper.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/query/PricingPageListQuery.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/XpPricingService.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/impl/XpPricingServiceImpl.java 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/vo/PricingPageListVO.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/resources/i18n/messages.properties 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/resources/i18n/messages_en_US.properties 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/resources/i18n/messages_zh_CN.properties 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/resources/mapper/operation/XpPricingMapper.xml 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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>