package com.trafficaudit.reportexport.calc; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.trafficaudit.common.util.RegionUtil; import com.trafficaudit.dataimport.entity.PassengerEnterpriseMonthly; import com.trafficaudit.dataimport.entity.PassengerIndividualMonthly; import com.trafficaudit.dataimport.mapper.PassengerEnterpriseMonthlyMapper; import com.trafficaudit.dataimport.mapper.PassengerIndividualMonthlyMapper; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; /** * 公路班线(含个体)口径计算服务(汇总大表数据驱动改造 M1-班线包车,2026-09-08)。 * * 口径(2026-07 与 _备份_2026年7月道路运输量汇总表_清理前_20260904.xlsx 班线包车页 * 企业/个体行 1-7 月逐格回归 0 差异,350 格全中): * 1. 班线包车页“客运量/旅客周转量”行 = h2031_enterprise_monthly 按 17 市州汇总 * (passenger_total / turnover_total,人 / 人公里)除以 10000,再并入个体客运 * (passenger_individual_monthly 同口径);“其中个体客运量/周转量”行 = 个体数除以 10000 * (当前仅黄石/襄阳/荆门/孝感/黄冈/随州有数)。 * 2. 全省行 = 17 市州之和,与母版“全省行=城市行求和”公式一致;中口径分析页“公路班线” * = 本口径全省合计(当月 1631.2013、累计 11204.5406 与母版 0 差异)。 * 3. 同比以母版缓存 2025 参照组为基准(Q1),不在本服务计算。 * * 输出:月 → 市州(含 PROVINCE=湖北省) → double[4] = * [客运量(含个体), 周转量(含个体), 个体客运量, 个体周转量], * 单位均为母版展示口径(万人 / 万人公里);企业单独量 = arr[0]-arr[2] 与 arr[1]-arr[3]。 */ @Service public class MidCalc { public static final String PROVINCE = "湖北省"; @Resource private PassengerEnterpriseMonthlyMapper passengerMapper; @Resource private PassengerIndividualMonthlyMapper individualMapper; /** 班线包车页数值矩阵:月 → 市州/全省 → [客运量,周转量,个体客运量,个体周转量] */ public Map> banxianMonthly(String yearPrefix, int limit) { Map> result = new LinkedHashMap<>(); List entRows = passengerMapper.selectList( new LambdaQueryWrapper().likeRight( PassengerEnterpriseMonthly::getReportPeriod, yearPrefix)); for (PassengerEnterpriseMonthly r : entRows) { int m = monthOf(r.getReportPeriod()); if (m < 1 || m > limit) continue; String city = RegionUtil.cityByCode(r.getRegionCode()); if (city == null) continue; double pax = nz(r.getPassengerTotal()); double turn = nz(r.getTurnoverTotal()); if (pax == 0.0 && turn == 0.0) { pax = nz(r.getPassengerClass1()) + nz(r.getPassengerClass2()) + nz(r.getPassengerClass3()) + nz(r.getPassengerClass4()) + nz(r.getPassengerCharter()); turn = nz(r.getTurnoverClass1()) + nz(r.getTurnoverClass2()) + nz(r.getTurnoverClass3()) + nz(r.getTurnoverClass4()) + nz(r.getTurnoverCharter()); } accumulate(result, m, city, pax / 10000.0, turn / 10000.0, 0.0, 0.0); } List indRows = individualMapper.selectList( new LambdaQueryWrapper().likeRight( PassengerIndividualMonthly::getReportPeriod, yearPrefix)); for (PassengerIndividualMonthly r : indRows) { int m = monthOf(r.getReportPeriod()); if (m < 1 || m > limit) continue; String city = RegionUtil.cityByCode(r.getRegionCode()); if (city == null) continue; double pax = nz(r.getPassengerCount()) / 10000.0; double turn = nz(r.getTurnover()) / 10000.0; accumulate(result, m, city, pax, turn, pax, turn); } return result; } private void accumulate(Map> result, int m, String city, double paxAll, double turnAll, double paxIndi, double turnIndi) { Map monthMap = result.computeIfAbsent(m, k -> new HashMap<>()); add(monthMap.computeIfAbsent(city, k -> new double[4]), paxAll, turnAll, paxIndi, turnIndi); add(monthMap.computeIfAbsent(PROVINCE, k -> new double[4]), paxAll, turnAll, paxIndi, turnIndi); } private void add(double[] arr, double paxAll, double turnAll, double paxIndi, double turnIndi) { arr[0] += paxAll; arr[1] += turnAll; arr[2] += paxIndi; arr[3] += turnIndi; } private int monthOf(String reportPeriod) { if (reportPeriod == null) return 0; String[] parts = reportPeriod.split("-"); if (parts.length < 2) return 0; try { return Integer.parseInt(parts[1]); } catch (NumberFormatException e) { return 0; } } private double nz(Double v) { return v == null ? 0.0 : v; } }