liujie
2026-02-13 98bf8c1d207a6830d6a4269513019f48c24245c1
Merge branch 'master' of http://120.76.84.145:10101/gitblit/r/java/Robotaxi
1个文件已添加
4个文件已修改
269 ■■■■■ 已修改文件
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/controller/XpElectronicFenceManagementController.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/pom.xml 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/controller/XpElectronicFenceController.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-operation-boot/src/main/resources/mapper/operation/XpWhitelistOperlogMapper.xml 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/utils/TaxiPricingDemoService.java 221 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/controller/XpElectronicFenceManagementController.java
@@ -96,7 +96,7 @@
    /**
     * 复制电子围栏
     */
    @PutMapping("/copy")
    @GetMapping("/copy")
    @ResponseBody
    @ApiOperation(value = "复制电子围栏")
    @Transactional
xp-robotaxi-operation-boot/pom.xml
@@ -115,6 +115,26 @@
            <version>1.5.2-v2</version>
        </dependency>
        <!-- easypoi -->
        <!-- Apache POI 核心依赖 -->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>5.2.3</version>
        </dependency>
        <!-- Apache POI OOXML 支持(用于 .xlsx 文件) -->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>5.2.3</version>
        </dependency>
        <!-- Apache POI XMLBeans(可选,用于复杂 XML 处理) -->
        <dependency>
            <groupId>org.apache.xmlbeans</groupId>
            <artifactId>xmlbeans</artifactId>
            <version>5.1.1</version>
        </dependency>
        <dependency>
            <groupId>cn.afterturn</groupId>
            <artifactId>easypoi-base</artifactId>
xp-robotaxi-operation-boot/src/main/java/com/xiaopeng/operation/controller/XpElectronicFenceController.java
@@ -142,7 +142,7 @@
    /**
     * 复制电子围栏
     */
    @PutMapping("/copy")
    @GetMapping("/copy")
    @ApiOperation(value = "复制电子围栏")
    @Transactional
    public R copy(Integer id) {
xp-robotaxi-operation-boot/src/main/resources/mapper/operation/XpWhitelistOperlogMapper.xml
@@ -11,14 +11,14 @@
        id,operation_name as operationName, operation_account as operationAccount, operation_type as operationType, operation_content as operationContent, operation_ip as operationIp, operation_account_id as operationAccountId, is_deleted as deleted, create_time as createTime, update_time as updateTime
            FROM xp_whitelist_operlog
        WHERE is_deleted = 0
        <if test="account != null and account != ''">
            AND operation_account LIKE CONCAT('%',#{account},'%')
        <if test="query.operationName != null and query.operationName != ''">
            AND operation_name LIKE CONCAT('%',#{query.operationName},'%')
        </if>
        <if test="operationName != null and operationName != ''">
            AND operation_name LIKE CONCAT('%',#{operationName},'%')
        <if test="query.operationType != null">
            AND operation_type = #{query.operationType}
        </if>
        <if test="operationType != null">
            AND operation_type = #{operationType}
        <if test="query.startTime != null and query.startTime != '' and query.endTime != null and query.endTime != ''">
            and create_time between #{query.startTime} and #{query.endTime}
        </if>
        order by create_time desc
    </select>
@@ -27,14 +27,14 @@
        id,operation_name as operationName, operation_account as operationAccount, operation_type as operationType, operation_content as operationContent, operation_ip as operationIp, operation_account_id as operationAccountId, is_deleted as deleted, create_time as createTime, update_time as updateTime
        FROM xp_whitelist_operlog
        WHERE is_deleted = 0
        <if test="account != null and account != ''">
            AND operation_account LIKE CONCAT('%',#{account},'%')
        <if test="query.operationName != null and query.operationName != ''">
            AND operation_name LIKE CONCAT('%',#{query.operationName},'%')
        </if>
        <if test="operationName != null and operationName != ''">
            AND operation_name LIKE CONCAT('%',#{operationName},'%')
        <if test="query.operationType != null">
            AND operation_type = #{query.operationType}
        </if>
        <if test="operationType != null">
            AND operation_type = #{operationType}
        <if test="query.startTime != null and query.startTime != '' and query.endTime != null and query.endTime != ''">
            and create_time between #{query.startTime} and #{query.endTime}
        </if>
        order by create_time desc
    </select>
xp-robotaxi-order-boot/src/main/java/com/xiaopeng/order/utils/TaxiPricingDemoService.java
New file
@@ -0,0 +1,221 @@
package com.xiaopeng.order.utils;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalTime;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;
/**
 * 出租车计费引擎服务
 */
public class TaxiPricingDemoService {
    public static void main(String[] args) {
        PricingConfig config = setupMockConfig();
        Calendar cal = Calendar.getInstance();
        cal.set(2023, Calendar.OCTOBER, 10, 16, 50, 0);
        Date orderTime = cal.getTime();
        // 模拟总里程 25.0 km,以触发多级远途加价
        double totalMillige = 25.0;
        // 模拟 GPS 轨迹点
        List<GpsPoint> points = createMockGpsPoints(orderTime);
        TaxiPricingService service = new TaxiPricingService();
        Map<String, BigDecimal> bill = service.calculateOrderPrice(orderTime, totalMillige, points, config);
        System.err.println("======= 订单计费账单 =======");
        bill.forEach((detail, price) -> System.err.println(detail + ": " + price + " 元"));
        BigDecimal totalAmount = bill.values().stream().reduce(BigDecimal.ZERO, BigDecimal::add);
        System.err.println("--------------------------");
        System.err.println("应付总额: " + totalAmount + " 元");
    }
    public static class TaxiPricingService {
        public Map<String, BigDecimal> calculateOrderPrice(Date orderTime, double totalMileageKm, List<GpsPoint> points, PricingConfig config) {
            Map<String, BigDecimal> result = new LinkedHashMap<>();
            BigDecimal totalMileage = BigDecimal.valueOf(totalMileageKm);
            PricingRule baseRule = config.findMatchedRule(orderTime);
            result.put("起步价(" + baseRule.ruleName + ")", baseRule.baseFare);
            if (totalMileage.compareTo(baseRule.baseKm) <= 0) {
                return formatMap(result);
            }
            BigDecimal remainBaseM = baseRule.baseKm.multiply(new BigDecimal("1000"));
            BigDecimal remainBaseMs = baseRule.baseMin.multiply(new BigDecimal("60000"));
            BigDecimal currentTotalM = BigDecimal.ZERO;
            for (int i = 1; i < points.size(); i++) {
                GpsPoint curr = points.get(i);
                GpsPoint prev = points.get(i - 1);
                BigDecimal segM = BigDecimal.valueOf(curr.getDistance());
                BigDecimal segMs = BigDecimal.valueOf(curr.getLocatetime() - prev.getLocatetime());
                currentTotalM = currentTotalM.add(segM); // 累计当前已行驶的总里程
                PricingRule activeRule = config.findMatchedRule(new Date(curr.getLocatetime()));
                // 扣除起步额度逻辑
                BigDecimal billM = BigDecimal.ZERO;
                if (remainBaseM.compareTo(BigDecimal.ZERO) > 0) {
                    if (segM.compareTo(remainBaseM) > 0) {
                        billM = segM.subtract(remainBaseM);
                        remainBaseM = BigDecimal.ZERO;
                    } else {
                        remainBaseM = remainBaseM.subtract(segM);
                    }
                } else {
                    billM = segM;
                }
                BigDecimal billMs = BigDecimal.ZERO;
                if (remainBaseMs.compareTo(BigDecimal.ZERO) > 0) {
                    if (segMs.compareTo(remainBaseMs) > 0) {
                        billMs = segMs.subtract(remainBaseMs);
                        remainBaseMs = BigDecimal.ZERO;
                    } else {
                        remainBaseMs = remainBaseMs.subtract(segMs);
                    }
                } else {
                    billMs = segMs;
                }
                // 1. 基础费率计算
                BigDecimal distFee = billM.divide(new BigDecimal(1000), 10, RoundingMode.HALF_UP).multiply(activeRule.perKmRate);
                BigDecimal timeFee = billMs.divide(new BigDecimal(60000), 10, RoundingMode.HALF_UP).multiply(activeRule.perMinRate);
                // 2. 远途加价计算 (基于区间判定)
                BigDecimal longExtra = calculateLongDistExtra(currentTotalM, billM, config.longSteps);
                BigDecimal totalSeg = distFee.add(timeFee).add(longExtra);
                result.put(activeRule.ruleName, result.getOrDefault(activeRule.ruleName, BigDecimal.ZERO).add(totalSeg));
            }
            return formatMap(result);
        }
        /**
         * 远途加价核心逻辑:判断当前行驶到的里程处于哪个加价区间
         */
        private BigDecimal calculateLongDistExtra(BigDecimal currentTotalM, BigDecimal billM, List<LongRangeStep> steps) {
            if (billM.compareTo(BigDecimal.ZERO) <= 0) return BigDecimal.ZERO;
            // 当前点所在的公里数
            BigDecimal currentKm = currentTotalM.divide(new BigDecimal(1000), 10, RoundingMode.HALF_UP);
            BigDecimal extraRate = BigDecimal.ZERO;
            // 匹配区间规则
            for (LongRangeStep step : steps) {
                if (step.isMatch(currentKm)) {
                    extraRate = step.addPrice;
                    break;
                }
            }
            return billM.divide(new BigDecimal(1000), 10, RoundingMode.HALF_UP).multiply(extraRate);
        }
        private Map<String, BigDecimal> formatMap(Map<String, BigDecimal> map) {
            return map.entrySet().stream().collect(Collectors.toMap(
                    Map.Entry::getKey, e -> e.getValue().setScale(2, RoundingMode.HALF_UP), (a, b) -> b, LinkedHashMap::new));
        }
    }
    // --- 远途区间模型 ---
    public static class LongRangeStep {
        BigDecimal numStart; // 超过公里数
        BigDecimal numEnd;   // 不足公里数
        BigDecimal addPrice; // 加收单价
        public LongRangeStep(double start, double end, double price) {
            this.numStart = BigDecimal.valueOf(start);
            this.numEnd = BigDecimal.valueOf(end);
            this.addPrice = BigDecimal.valueOf(price);
        }
        public boolean isMatch(BigDecimal currentKm) {
            // [numStart, numEnd)
            return currentKm.compareTo(numStart) >= 0 && currentKm.compareTo(numEnd) < 0;
        }
    }
    public static class PricingConfig {
        PricingRule holiday;
        PricingRule peak;
        PricingRule night;
        List<PricingRule> normals = new ArrayList<>();
        List<LongRangeStep> longSteps = new ArrayList<>(); // 修改为区间列表
        boolean isHoliday = false;
        public PricingRule findMatchedRule(Date date) {
            LocalTime t = date.toInstant().atZone(ZoneId.systemDefault()).toLocalTime();
            if (isHoliday) return holiday;
            if (peak.isMatch(t)) return peak;
            if (night.isMatch(t)) return night;
            for (PricingRule r : normals) if (r.isMatch(t)) return r;
            return normals.get(0);
        }
    }
    public static class PricingRule {
        String ruleName;
        LocalTime start, end;
        BigDecimal baseFare, baseKm, baseMin, perKmRate, perMinRate;
        public PricingRule(String n, String s, String e, double bf, double bk, double bm, double pkr, double pmr) {
            this.ruleName = n;
            this.start = LocalTime.parse(s);
            this.end = LocalTime.parse(e);
            this.baseFare = BigDecimal.valueOf(bf);
            this.baseKm = BigDecimal.valueOf(bk);
            this.baseMin = BigDecimal.valueOf(bm);
            this.perKmRate = BigDecimal.valueOf(pkr);
            this.perMinRate = BigDecimal.valueOf(pmr);
        }
        public boolean isMatch(LocalTime now) {
            return start.isBefore(end) ? (!now.isBefore(start) && !now.isAfter(end)) : (!now.isBefore(start) || !now.isAfter(end));
        }
    }
    public static class GpsPoint {
        private double distance;
        private long locatetime;
        public GpsPoint(double d, long t) { this.distance = d; this.locatetime = t; }
        public double getDistance() { return distance; }
        public long getLocatetime() { return locatetime; }
    }
    private static PricingConfig setupMockConfig() {
        PricingConfig config = new PricingConfig();
        config.peak = new PricingRule("高峰期", "17:00", "19:00", 15.0, 3.0, 5.0, 3.0, 0.8);
        config.night = new PricingRule("夜间行驶", "22:00", "05:00", 18.0, 3.0, 5.0, 3.5, 1.0);
        config.normals.add(new PricingRule("普通时段", "05:00", "17:00", 12.0, 3.0, 5.0, 2.4, 0.5));
        // --- 远途加价区间配置 (关键修改) ---
        // 1. 超过 num1(12) 不足 num2(20), 加收 num3(1.2)
        config.longSteps.add(new LongRangeStep(12.0, 20.0, 1.2));
        // 2. 超过 num4(20) 不足 num5(35), 加收 num6(2.0)
        config.longSteps.add(new LongRangeStep(20.0, 35.0, 2.0));
        // 3. 超过 num7(35) 以后, 加收 num8(3.5) -> numEnd 传个极大值
        config.longSteps.add(new LongRangeStep(35.0, 999999.0, 3.5));
        return config;
    }
    private static List<GpsPoint> createMockGpsPoints(Date startTime) {
        List<GpsPoint> list = new ArrayList<>();
        long startTs = startTime.getTime();
        list.add(new GpsPoint(0, startTs));
        // 模拟行驶了 20km
        list.add(new GpsPoint(12000, startTs + 15 * 60 * 1000));
        list.add(new GpsPoint(8000, startTs + 25 * 60 * 1000));
        list.add(new GpsPoint(5000, startTs + 35 * 60 * 1000));
        return list;
    }
}