package com.trafficaudit.reportexport.calc; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.trafficaudit.dataimport.entity.CityBusMonthly; import com.trafficaudit.dataimport.entity.CityTaxiMonthly; import com.trafficaudit.dataimport.mapper.CityBusMonthlyMapper; import com.trafficaudit.dataimport.mapper.CityTaxiMonthlyMapper; 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)。 * * 口径与既有多表导出 loadCityBusByMonth / loadCityTaxiByMonth / loadCityRailFerryByMonth 完全一致 * (决策基线 A2/B4/B5/C7;2026-07 与 _备份…_清理前_20260904.xlsx 公交/出租/轨道轮渡台账页 O 列回归 0 差异): * - 公交/出租:按月 → 市州 → [客运量, 旅客周转量, 城市内客运量, 城市内旅客周转量],按 DB city 字段分组,"全省"=全部市州合计; * - 轨道/轮渡:同一 city_bus_monthly 表的 rail / ferry 字段(轨道=武汉/黄石,轮渡=武汉;全省=合计)。 * * 输出为“月 → 市州 → double[4]”;累计由写回层对 1..m 月求和。同比按 Q1 以母版缓存 2025 参照组为基准,不在本服务计算。 */ @Service public class PaxCalc { public static final String PROVINCE = "全省"; @Resource private CityBusMonthlyMapper cityBusMapper; @Resource private CityTaxiMonthlyMapper cityTaxiMapper; /** 公交:月 → 市州 → [客运量,周转量,城市内客运量,城市内周转量] */ public Map> busMonthly(String yearPrefix, int limit) { Map> result = new LinkedHashMap<>(); List rows = cityBusMapper.selectList( new LambdaQueryWrapper().likeRight(CityBusMonthly::getReportPeriod, yearPrefix)); for (CityBusMonthly r : rows) { int m = monthOf(r.getReportPeriod()); if (m < 1 || m > limit) continue; String city = r.getCity() == null ? "未知" : r.getCity(); accumulate(result, city, m, nz(r.getPassengerVolume()), nz(r.getTurnover()), nz(r.getPassengerCity()), nz(r.getTurnoverCity())); } return result; } /** 巡游出租:月 → 市州 → [客运量,周转量,城市内客运量,城市内周转量] */ public Map> taxiMonthly(String yearPrefix, int limit) { Map> result = new LinkedHashMap<>(); List rows = cityTaxiMapper.selectList( new LambdaQueryWrapper().likeRight(CityTaxiMonthly::getReportPeriod, yearPrefix)); for (CityTaxiMonthly r : rows) { int m = monthOf(r.getReportPeriod()); if (m < 1 || m > limit) continue; String city = r.getCity() == null ? "未知" : r.getCity(); accumulate(result, city, m, nz(r.getPassengerVolume()), nz(r.getTurnover()), nz(r.getPassengerCity()), nz(r.getTurnoverCity())); } return result; } /** 轨道/轮渡:月 → 市州 → [轨道客运量,轨道周转量,轮渡客运量,轮渡周转量] */ public Map> railFerryMonthly(String yearPrefix, int limit) { Map> result = new LinkedHashMap<>(); List rows = cityBusMapper.selectList( new LambdaQueryWrapper().likeRight(CityBusMonthly::getReportPeriod, yearPrefix)); for (CityBusMonthly r : rows) { int m = monthOf(r.getReportPeriod()); if (m < 1 || m > limit) continue; String city = r.getCity() == null ? "未知" : r.getCity(); accumulate(result, city, m, nz(r.getRailPassengerVolume()), nz(r.getRailTurnover()), nz(r.getFerryPassengerVolume()), nz(r.getFerryTurnover())); } return result; } private void accumulate(Map> result, String city, int m, double v0, double v1, double v2, double v3) { Map cityMap = result.computeIfAbsent(m, k -> new HashMap<>()); double[] arr = cityMap.computeIfAbsent(city, k -> new double[4]); arr[0] += v0; arr[1] += v1; arr[2] += v2; arr[3] += v3; double[] prov = cityMap.computeIfAbsent(PROVINCE, k -> new double[4]); prov[0] += v0; prov[1] += v1; prov[2] += v2; prov[3] += v3; } 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; } }