liujie
2026-02-13 d95513d23373acd3fa1106ac7836d048d77b7be6
Merge branch 'master' of http://120.76.84.145:10101/gitblit/r/java/Robotaxi
5个文件已修改
216 ■■■■■ 已修改文件
xp-robotaxi-operation-boot/pom.xml 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/controller/XpWhitelistController.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/impl/XpElectronicFenceServiceImpl.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/impl/XpOrderServiceImpl.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/utils/TaxiPricingDemoService.java 166 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/pom.xml
@@ -15,12 +15,18 @@
    </description>
    <dependencies>
        <!--mongodb-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-mongodb</artifactId>
        </dependency>
        <dependency>
            <groupId>com.belerweb</groupId>
            <artifactId>pinyin4j</artifactId>
            <version>2.5.1</version>
        </dependency>
        <dependency>
            <groupId>com.github.stuxuhai</groupId>
            <artifactId>jpinyin</artifactId>
            <version>1.1.8</version>
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/controller/XpWhitelistController.java
@@ -137,14 +137,13 @@
    /**
     * 批量冻结
     */
    @PutMapping("/freezeByIds")
    @GetMapping("/freezeByIds")
    @ResponseBody
    @ApiOperation(value = "批量冻结")
    public R freezeByIds(List<Long> ids) {
    public R freezeByIds(String ids) {
        SysUser sysUser = tokenService.getLoginUser().getSysUser();
        return xpWhitelistService.freezeByIds(ids, sysUser);
        List<Long> whiteIds = Arrays.stream(ids.split(",")).map(Long::parseLong).collect(Collectors.toList());
        return xpWhitelistService.freezeByIds(whiteIds, sysUser);
    }
    @ApiOperation(value = "白名单导入模板下载")
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/service/impl/XpElectronicFenceServiceImpl.java
@@ -12,6 +12,7 @@
import com.xiaopeng.api.model.operation.XpElectronicFenceArea;
import com.xiaopeng.api.model.operation.XpPoint;
import com.xiaopeng.api.query.operation.ElectronicPageListQuery;
import com.xiaopeng.api.model.operation.XpRegion;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.core.exception.ServiceException;
import com.xiaopeng.common.core.utils.DateUtils;
@@ -25,9 +26,15 @@
import com.xiaopeng.operation.mapper.XpElectronicFenceAreaMapper;
import com.xiaopeng.operation.mapper.XpElectronicFenceMapper;
import com.xiaopeng.operation.mapper.XpPointMapper;
import com.xiaopeng.api.query.operation.ElectronicPageListQuery;
import com.xiaopeng.operation.mapper.XpRegionMapper;
import com.xiaopeng.operation.service.IXpElectronicFenceService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xiaopeng.operation.service.IXpRegionService;
import com.xiaopeng.operation.utils.PolygonOverlapService;
import lombok.extern.slf4j.Slf4j;
import net.sourceforge.pinyin4j.PinyinHelper;
import net.sourceforge.pinyin4j.format.exception.BadHanyuPinyinOutputFormatCombination;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.Point;
import org.springframework.data.mongodb.core.MongoTemplate;
@@ -62,6 +69,8 @@
    private MongoTemplate mongoTemplate;
    @Autowired
    private XpPointMapper xpPointMapper;
    @Autowired
    private XpRegionMapper xpRegionMapper;
    @Override
    public PageInfo<XpElectronicFence> pageList(ElectronicPageListQuery query) {
@@ -90,7 +99,18 @@
        pageInfo.setRecords(list);
        return pageInfo;
    }
    public static String getPinyinInitials(String chinese) {
        StringBuilder pinyinInitials = new StringBuilder();
        for (char c : chinese.toCharArray()) {
            // 获取拼音数组(多音字会返回多个拼音)
            String[] pinyinArray = PinyinHelper.toHanyuPinyinStringArray(c);
            if (pinyinArray != null && pinyinArray.length > 0) {
                // 取第一个拼音的首字母
                pinyinInitials.append(pinyinArray[0].charAt(0));
            }
        }
        return pinyinInitials.toString();
    }
    @Override
    public R addElectronic(ElectronicFenceAddDto dto) {
        try {
@@ -142,7 +162,17 @@
                }
            }
            xpElectronicFence.setCityCode(dto.getCityCode());
            // 生成围栏id 规则:城市首字母-类型简写-4位自增流水号(如 SZ-OP-001),支持复制;数据停用或删除后,流水号不回收
            XpRegion xpRegion = xpRegionMapper.selectOne(new LambdaQueryWrapper<XpRegion>()
                    .eq(XpRegion::getCode, dto.getCityCode()));
            String name = xpRegion.getName();
            this.save(xpElectronicFence);
            // 根据城市中文名,截取前两位的首字母并且大写
            // 截取前两个字符
            String prefix = name.substring(0, 2);
            // 获取拼音首字母并大写
            String result = getPinyinInitials(prefix).toUpperCase();
            xpElectronicFence.setElectronicNumber(result + "-OP-" + String.format("%04d", xpElectronicFence.getId()));
            if (dto.getStatus() == 0) {
                xpElectronicFence.setStatus(0);
            } else if (dto.getStatus() == 1) {
@@ -216,7 +246,7 @@
                        xpGeoFencingPolygon.setFenceId(String.valueOf(xpElectronicFence.getId()));
                        xpGeoFencingPolygon.setName(xpElectronicFence.getElectronicName());
                        xpGeoFencingPolygon.setAreaId(String.valueOf(newElectronicFenceArea.getId()));
                        xpGeoFencingPolygon.setCityCode("510100");
                        xpGeoFencingPolygon.setCityCode(xpElectronicFence.getCityCode());
                        xpGeoFencingPolygon.setOperationStrategy(xpElectronicFence.getOperationStrategy());
                        // 将围栏点转换为GeoJSON多边形
                        GeoJsonPolygon geoJsonPolygon = convertToGeoJsonPolygon(newElectronicFenceArea.getFencePoints());
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/service/impl/XpOrderServiceImpl.java
@@ -922,6 +922,7 @@
        }
        // 计算起步价
//        // 计算实际价格
//        // 根据订单所属开通城市id 查询定价
//        R<XpPricing> pricingByCityId = xpPricingClient.getPricingByCityId(order.getCityId());
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/utils/TaxiPricingDemoService.java
@@ -1,5 +1,4 @@
package com.xiaopeng.order.utils;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalTime;
@@ -16,11 +15,11 @@
        PricingConfig config = setupMockConfig();
        Calendar cal = Calendar.getInstance();
        cal.set(2023, Calendar.OCTOBER, 10, 16, 50, 0);
        cal.set(2026, Calendar.MARCH, 13, 14, 40, 15);
        Date orderTime = cal.getTime();
        // 模拟总里程 25.0 km,以触发多级远途加价
        double totalMillige = 25.0;
        double totalMillige = 7.874;
        // 模拟 GPS 轨迹点
        List<GpsPoint> points = createMockGpsPoints(orderTime);
@@ -42,7 +41,7 @@
            BigDecimal totalMileage = BigDecimal.valueOf(totalMileageKm);
            PricingRule baseRule = config.findMatchedRule(orderTime);
            result.put("起步价(" + baseRule.ruleName + ")", baseRule.baseFare);
            result.put("起步价(" + baseRule.ruleName + ")"+"-"+baseRule.start+"-"+baseRule.end, baseRule.baseFare);
            if (totalMileage.compareTo(baseRule.baseKm) <= 0) {
                return formatMap(result);
@@ -51,6 +50,15 @@
            BigDecimal remainBaseM = baseRule.baseKm.multiply(new BigDecimal("1000"));
            BigDecimal remainBaseMs = baseRule.baseMin.multiply(new BigDecimal("60000"));
            BigDecimal currentTotalM = BigDecimal.ZERO;
            BigDecimal currentTotalMs = BigDecimal.ZERO;
            boolean isOverBaseM = false;
            boolean isOverBaseMs = false;
            // 超出里程和分钟数的集合
            Map<String, BigDecimal> bill = new LinkedHashMap<>();
            Map<String, BigDecimal> activeRuleNameRate = new LinkedHashMap<>();
            Set<String> activeRuleNameList = new HashSet<>();
            for (int i = 1; i < points.size(); i++) {
                GpsPoint curr = points.get(i);
@@ -59,65 +67,94 @@
                BigDecimal segM = BigDecimal.valueOf(curr.getDistance());
                BigDecimal segMs = BigDecimal.valueOf(curr.getLocatetime() - prev.getLocatetime());
                currentTotalM = currentTotalM.add(segM); // 累计当前已行驶的总里程
                currentTotalMs = currentTotalMs.add(segMs); // 累计当前已行驶的总时间
                PricingRule activeRule = config.findMatchedRule(new Date(curr.getLocatetime()));
                // 扣除起步额度逻辑
                BigDecimal billM = BigDecimal.ZERO;
                if (remainBaseM.compareTo(BigDecimal.ZERO) > 0) {
                    if (segM.compareTo(remainBaseM) > 0) {
                        billM = segM.subtract(remainBaseM);
                        remainBaseM = BigDecimal.ZERO;
                    } else {
                        remainBaseM = remainBaseM.subtract(segM);
                    }
                } else {
                    billM = segM;
                if (currentTotalM.compareTo(remainBaseM) > 0) {
//                    billM = currentTotalM.subtract(remainBaseM);
                    isOverBaseM = true;
                }
                BigDecimal billMs = BigDecimal.ZERO;
                if (remainBaseMs.compareTo(BigDecimal.ZERO) > 0) {
                    if (segMs.compareTo(remainBaseMs) > 0) {
                        billMs = segMs.subtract(remainBaseMs);
                        remainBaseMs = BigDecimal.ZERO;
                    } else {
                        remainBaseMs = remainBaseMs.subtract(segMs);
                    }
                } else {
                    billMs = segMs;
                if (currentTotalMs.compareTo(remainBaseMs) > 0) {
//                    billMs = currentTotalMs.subtract(remainBaseMs);
                    isOverBaseMs = true;
                }
                // 1. 基础费率计算
                BigDecimal distFee = billM.divide(new BigDecimal(1000), 10, RoundingMode.HALF_UP).multiply(activeRule.perKmRate);
                BigDecimal timeFee = billMs.divide(new BigDecimal(60000), 10, RoundingMode.HALF_UP).multiply(activeRule.perMinRate);
//                BigDecimal distFee = billM.divide(new BigDecimal(1000), 2, RoundingMode.HALF_UP).multiply(activeRule.perKmRate);
//                BigDecimal timeFee = billMs.multiply(activeRule.perMinRate);
                // 2. 远途加价计算 (基于区间判定)
                BigDecimal longExtra = calculateLongDistExtra(currentTotalM, billM, config.longSteps);
//                BigDecimal longExtra = calculateLongDistExtra(currentTotalM, billM, config.longSteps);
                BigDecimal totalSeg = distFee.add(timeFee).add(longExtra);
                result.put(activeRule.ruleName, result.getOrDefault(activeRule.ruleName, BigDecimal.ZERO).add(totalSeg));
//                BigDecimal totalSeg = distFee.add(timeFee).add(longExtra);
                // 超出里程
                if (isOverBaseM) {
                    bill.put(activeRule.ruleName + "-"+activeRule.start + "-" + activeRule.end + "_Ml", bill.getOrDefault(activeRule.ruleName + activeRule.start + "-" + activeRule.end + "_Ml", BigDecimal.ZERO).add(segM));
                }
                // 超出时间
                if (isOverBaseMs) {
                    bill.put(activeRule.ruleName + "-"+activeRule.start + "-" + activeRule.end + "_Ms", bill.getOrDefault(activeRule.ruleName + activeRule.start + "-" + activeRule.end + "_Ms", BigDecimal.ZERO).add(segMs));
                }
                // 若不存在规则名称,则添加
                activeRuleNameRate.computeIfAbsent(activeRule.ruleName + "-"+activeRule.start + "-" + activeRule.end + "_Ml", k -> activeRule.perKmRate); // 超出里程的加价
                activeRuleNameRate.computeIfAbsent(activeRule.ruleName + "-"+activeRule.start + "-" + activeRule.end + "_Ms",k ->activeRule.perMinRate); // 超出时间的加价
                activeRuleNameList.add(activeRule.ruleName + "-"+activeRule.start + "-" + activeRule.end + "_Ml");
                activeRuleNameList.add(activeRule.ruleName + "-"+activeRule.start + "-" + activeRule.end + "_Ms");
//                result.put(activeRule.ruleName, result.getOrDefault(activeRule.ruleName, BigDecimal.ZERO).add(totalSeg));
            }
            // 1. 基础费率计算
            for (String ruleName : activeRuleNameList) {
                String[] split = ruleName.split("_");
                if(ruleName.endsWith("_Ml")){
                    BigDecimal exceedValue = bill.get(ruleName);
                    if(Objects.nonNull(exceedValue)){
                        exceedValue = exceedValue.divide(new BigDecimal(1000), 2, RoundingMode.HALF_UP);
                    }else{
                        exceedValue = BigDecimal.ZERO;
                    }
                    BigDecimal exceedRate = activeRuleNameRate.get(ruleName);
                    result.put(split[0], result.getOrDefault(split[0], BigDecimal.ZERO).add(exceedValue.multiply(exceedRate)));
                }else{
                    BigDecimal exceedValue = bill.get(ruleName);
                    if(Objects.nonNull(exceedValue)){
                        exceedValue = exceedValue.divide(new BigDecimal(60000), 2, RoundingMode.HALF_UP);
                    }else{
                        exceedValue = BigDecimal.ZERO;
                    }
                    BigDecimal exceedRate = activeRuleNameRate.get(ruleName);
                    result.put(split[0], result.getOrDefault(split[0], BigDecimal.ZERO).add(exceedValue.multiply(exceedRate)));
                }
            }
            // 2. 远途加价计算 (基于区间判定)
            BigDecimal longExtra = calculateLongDistExtra(BigDecimal.valueOf(totalMileageKm), config.longSteps);
            result.put("远途费", longExtra);
            return formatMap(result);
        }
        /**
         * 远途加价核心逻辑:判断当前行驶到的里程处于哪个加价区间
         */
        private BigDecimal calculateLongDistExtra(BigDecimal currentTotalM, BigDecimal billM, List<LongRangeStep> steps) {
        private BigDecimal calculateLongDistExtra(BigDecimal billM, List<LongRangeStep> steps) {
            if (billM.compareTo(BigDecimal.ZERO) <= 0) return BigDecimal.ZERO;
            // 当前点所在的公里数
            BigDecimal currentKm = currentTotalM.divide(new BigDecimal(1000), 10, RoundingMode.HALF_UP);
            BigDecimal extraStart = BigDecimal.ZERO;
            BigDecimal extraRate = BigDecimal.ZERO;
            // 匹配区间规则
            for (LongRangeStep step : steps) {
                if (step.isMatch(currentKm)) {
                if (step.isMatch(billM)) {
                    extraRate = step.addPrice;
                    extraStart = step.numStart;
                    break;
                }
            }
            return billM.divide(new BigDecimal(1000), 10, RoundingMode.HALF_UP).multiply(extraRate);
            return billM.subtract(extraStart).multiply(extraRate);
        }
        private Map<String, BigDecimal> formatMap(Map<String, BigDecimal> map) {
@@ -150,11 +187,10 @@
        PricingRule night;
        List<PricingRule> normals = new ArrayList<>();
        List<LongRangeStep> longSteps = new ArrayList<>(); // 修改为区间列表
        boolean isHoliday = false;
        public PricingRule findMatchedRule(Date date) {
            LocalTime t = date.toInstant().atZone(ZoneId.systemDefault()).toLocalTime();
            if (isHoliday) return holiday;
            if (holiday.isMatch(t)) return holiday;
            if (peak.isMatch(t)) return peak;
            if (night.isMatch(t)) return night;
            for (PricingRule r : normals) if (r.isMatch(t)) return r;
@@ -193,17 +229,19 @@
    private static PricingConfig setupMockConfig() {
        PricingConfig config = new PricingConfig();
        config.peak = new PricingRule("高峰期", "17:00", "19:00", 15.0, 3.0, 5.0, 3.0, 0.8);
        config.night = new PricingRule("夜间行驶", "22:00", "05:00", 18.0, 3.0, 5.0, 3.5, 1.0);
        config.normals.add(new PricingRule("普通时段", "05:00", "17:00", 12.0, 3.0, 5.0, 2.4, 0.5));
        config.peak = new PricingRule("高峰期", "14:40", "14:46", 15.0, 3.0, 5.0, 3.0, 0.8);
        config.night = new PricingRule("夜间行驶", "14:47", "14:48", 18.0, 3.0, 5.0, 3.5, 1.0);
        config.holiday = new PricingRule("节假日", "18:47", "18:48", 18.0, 3.0, 5.0, 4, 2.0);
        config.normals.add(new PricingRule("普通时段", "14:46", "14:47", 10.0, 3.0, 5.0, 2.4, 0.5));
        config.normals.add(new PricingRule("普通时段", "14:48", "17:59", 10.0, 3.0, 5.0, 2.4, 0.5));
        // --- 远途加价区间配置 (关键修改) ---
        // 1. 超过 num1(12) 不足 num2(20), 加收 num3(1.2)
        config.longSteps.add(new LongRangeStep(12.0, 20.0, 1.2));
        config.longSteps.add(new LongRangeStep(5.0, 6.0, 1.2));
        // 2. 超过 num4(20) 不足 num5(35), 加收 num6(2.0)
        config.longSteps.add(new LongRangeStep(20.0, 35.0, 2.0));
        config.longSteps.add(new LongRangeStep(6.0, 7.0, 2.0));
        // 3. 超过 num7(35) 以后, 加收 num8(3.5) -> numEnd 传个极大值
        config.longSteps.add(new LongRangeStep(35.0, 999999.0, 3.5));
        config.longSteps.add(new LongRangeStep(7.0, 8.0, 3.5));
        return config;
    }
@@ -213,9 +251,49 @@
        long startTs = startTime.getTime();
        list.add(new GpsPoint(0, startTs));
        // 模拟行驶了 20km
        list.add(new GpsPoint(12000, startTs + 15 * 60 * 1000));
        list.add(new GpsPoint(8000, startTs + 25 * 60 * 1000));
        list.add(new GpsPoint(5000, startTs + 35 * 60 * 1000));
        list.add(new GpsPoint(120, startTs + 2 * 10 * 1000));
        list.add(new GpsPoint(204, startTs + 2 * 20 * 1000));
        list.add(new GpsPoint(231, startTs + 2 * 30 * 1000));
        list.add(new GpsPoint(252, startTs + 2 * 40 * 1000));
        list.add(new GpsPoint(188, startTs + 2 * 50 * 1000));
        list.add(new GpsPoint(150, startTs + 2 * 60 * 1000));
        list.add(new GpsPoint(126, startTs + 2 * 70 * 1000));
        list.add(new GpsPoint(138, startTs + 2 * 80 * 1000));
        list.add(new GpsPoint(119, startTs + 2 * 90 * 1000));
        list.add(new GpsPoint(147, startTs + 2 * 100 * 1000));
        list.add(new GpsPoint(155, startTs + 2 * 110 * 1000));
        list.add(new GpsPoint(182, startTs + 2 * 120 * 1000));
        list.add(new GpsPoint(172, startTs + 2 * 130 * 1000));
        list.add(new GpsPoint(202, startTs + 2 * 140 * 1000));
        list.add(new GpsPoint(162, startTs + 2 * 150 * 1000));
        list.add(new GpsPoint(182, startTs + 2 * 160 * 1000));
        list.add(new GpsPoint(132, startTs + 2 * 170 * 1000));
        list.add(new GpsPoint(142, startTs + 2 * 180 * 1000));
        list.add(new GpsPoint(262, startTs + 2 * 190 * 1000));
        list.add(new GpsPoint(242, startTs + 2 * 200 * 1000));
        list.add(new GpsPoint(282, startTs + 2 * 210 * 1000));
        list.add(new GpsPoint(222, startTs + 2 * 220 * 1000));
        list.add(new GpsPoint(212, startTs + 2 * 230 * 1000));
        list.add(new GpsPoint(142, startTs + 2 * 240 * 1000));
        list.add(new GpsPoint(262, startTs + 2 * 250 * 1000));
        list.add(new GpsPoint(169, startTs + 2 * 260 * 1000));
        list.add(new GpsPoint(170, startTs + 2 * 270 * 1000));
        list.add(new GpsPoint(180, startTs + 2 * 280 * 1000));
        list.add(new GpsPoint(165, startTs + 2 * 290 * 1000));
        list.add(new GpsPoint(180, startTs + 2 * 300 * 1000));
        list.add(new GpsPoint(198, startTs + 2 * 310 * 1000));
        list.add(new GpsPoint(182, startTs + 2 * 320 * 1000));
        list.add(new GpsPoint(182, startTs + 2 * 330 * 1000));
        list.add(new GpsPoint(182, startTs + 2 * 340 * 1000));
        list.add(new GpsPoint(182, startTs + 2 * 350 * 1000));
        list.add(new GpsPoint(182, startTs + 2 * 360 * 1000));
        list.add(new GpsPoint(182, startTs + 2 * 370 * 1000));
        list.add(new GpsPoint(182, startTs + 2 * 380 * 1000));
        list.add(new GpsPoint(182, startTs + 2 * 390 * 1000));
        list.add(new GpsPoint(182, startTs + 2 * 400 * 1000));
        list.add(new GpsPoint(182, startTs + 2 * 410 * 1000));
        list.add(new GpsPoint(182, startTs + 2 * 420 * 1000));
        list.add(new GpsPoint(182, startTs + 2 * 430 * 1000));
        return list;
    }
}