package com.trafficaudit.reportexport.service; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.trafficaudit.common.util.RegionUtil; import com.trafficaudit.dataimport.entity.FreightTurnoverImport; import com.trafficaudit.dataimport.entity.H2032EnterpriseMonthly; import com.trafficaudit.dataimport.entity.ScaleSplitTransport; import com.trafficaudit.dataimport.mapper.FreightTurnoverImportMapper; import com.trafficaudit.dataimport.mapper.H2032EnterpriseMonthlyMapper; import com.trafficaudit.dataimport.mapper.ScaleSplitTransportMapper; import lombok.extern.slf4j.Slf4j; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellStyle; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.io.ByteArrayOutputStream; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 报表生成服务:3 类输出报表 * 货运量口径:合计=模板_货运量周转量(左半今年1-N月,右半去年1-12月);规上=H203-2按17市州汇总;规下=合计-规上 * 周转量口径:全部来自规上规下拆分表(用户手动上传,含忠实导入的全省行) */ @Slf4j @Service public class ReportExportService { @Resource private H2032EnterpriseMonthlyMapper h2032Mapper; @Resource private ScaleSplitTransportMapper scaleSplitMapper; @Resource private FreightTurnoverImportMapper freightTurnoverMapper; // ==================== 1. 生成_货运量分市州明细.xlsx ==================== public byte[] exportCityDetail(String period) throws Exception { int currentMonth = parseMonth(period); String year = period.substring(0, 4); Map> monthData = loadMonthData(period); Map provinceMonthMap = loadProvinceMonthMap(period); ScaleSplitTransport provinceCum = getProvinceCumulative(period); Map cumMap = loadCumulativeMap(period); Map> h2032Freight = loadH2032FreightByMonth(period); Map h2032FreightCum = loadH2032FreightCumulative(period); Map ftMap = loadFreightTurnover(period); XSSFWorkbook wb = new XSSFWorkbook(); Sheet sheet = wb.createSheet("货运量分市州明细"); Row r1 = sheet.createRow(0); r1.createCell(0).setCellValue("报表期:" + year + "年" + currentMonth + "月"); sheet.createRow(1); Row header = sheet.createRow(2); header.createCell(0).setCellValue("地区 名称"); header.createCell(1).setCellValue("指标"); CellStyle dateStyle = wb.createCellStyle(); dateStyle.setDataFormat(wb.getCreationHelper().createDataFormat().getFormat("yyyy-mm-dd")); for (int m = 1; m <= currentMonth; m++) { Cell c = header.createCell(2 + (m - 1) * 2); c.setCellValue(java.sql.Date.valueOf(year + "-" + pad(m) + "-01")); c.setCellStyle(dateStyle); header.createCell(3 + (m - 1) * 2).setCellValue(m + "月与去年同比"); } int cumCol = 2 + currentMonth * 2; header.createCell(cumCol).setCellValue("累计周转量"); header.createCell(cumCol + 1).setCellValue("同比"); sheet.createRow(3); int rowIdx = 4; String[][] provinceMetrics = { {"货运量 (万吨)", "freight", "total"}, {"货物周转量 (万吨公里)", "turnover", "total"}, {"其中规上货运量 (万吨)", "freight", "above"}, {"其中规上货物周转量 (万吨公里)", "turnover", "above"}, {"其中规下货运量 (万吨)", "freight", "below"}, {"其中规下货物周转量 (万吨公里)", "turnover", "below"} }; FreightTurnoverImport ftProvince = ftMap.get("湖北省"); for (String[] metric : provinceMetrics) { Row row = sheet.createRow(rowIdx++); row.createCell(0).setCellValue("全省"); row.createCell(1).setCellValue(metric[0]); for (int m = 1; m <= currentMonth; m++) { setNumeric(row, 2 + (m - 1) * 2, getProvinceMonthValue(provinceMonthMap, ftProvince, m, metric[1], metric[2], h2032Freight)); setNumeric(row, 3 + (m - 1) * 2, getProvinceMonthYoy(provinceMonthMap, ftProvince, m, metric[1], metric[2])); } setNumeric(row, cumCol, getProvinceCumValue(provinceCum, ftProvince, metric[1], metric[2], h2032FreightCum, currentMonth)); setNumeric(row, cumCol + 1, getProvinceCumYoy(provinceCum, ftProvince, metric[1], metric[2], currentMonth)); } for (String city : RegionUtil.cityList()) { String[][] metrics = { {"规上+规下货运量(万吨)", "freight", "total"}, {"规上货运量 (万吨)", "freight", "above"}, {"规下货运量 (万吨)", "freight", "below"}, {"规上+规下周转量 (万吨公里)", "turnover", "total"}, {"规上货物周转量 (万吨公里)", "turnover", "above"}, {"规下货物周转量 (万吨公里)", "turnover", "below"} }; FreightTurnoverImport ftCity = ftMap.get(city); ScaleSplitTransport cum = cumMap.get(city); for (String[] metric : metrics) { Row row = sheet.createRow(rowIdx++); row.createCell(0).setCellValue(RegionUtil.shortName(city)); row.createCell(1).setCellValue(metric[0]); for (int m = 1; m <= currentMonth; m++) { setNumeric(row, 2 + (m - 1) * 2, getCityMonthValue(monthData, ftCity, city, m, metric[1], metric[2], h2032Freight)); setNumeric(row, 3 + (m - 1) * 2, getCityMonthYoy(monthData, ftCity, city, m, metric[1], metric[2])); } setNumeric(row, cumCol, getCityCumValue(cum, ftCity, metric[1], metric[2], h2032FreightCum, city, currentMonth)); setNumeric(row, cumCol + 1, getCityCumYoy(cum, ftCity, metric[1], metric[2], currentMonth)); } } autoWidth(sheet, cumCol + 2); return toBytes(wb); } // ==================== 2. 生成_货运量排名.xlsx ==================== public byte[] exportFreightRank(String period) throws Exception { String year = period.substring(0, 4); int month = parseMonth(period); Map ftMap = loadFreightTurnover(period); Map h2032FreightCum = loadH2032FreightCumulative(period); Map h2032FreightYoy = computeH2032Yoy(period); Map above = new HashMap<>(); Map below = new HashMap<>(); Map total = new HashMap<>(); Map aboveYoy = new HashMap<>(); Map belowYoy = new HashMap<>(); Map totalYoy = new HashMap<>(); double provAbove = 0.0; for (Double v : h2032FreightCum.values()) { if (v != null) provAbove += v; } Double provAboveWan = round(provAbove / 10000.0, 4); Double provTotal = freightCum(ftMap.get("湖北省"), month); Double provBelow = provTotal == null ? null : round(provTotal - provAboveWan, 4); Double provTotalYoy = freightCumYoy(ftMap.get("湖北省"), month); for (String city : RegionUtil.cityList()) { Double a = h2032FreightCum.get(city); double aWan = a == null ? 0.0 : round(a / 10000.0, 4); Double t = freightCum(ftMap.get(city), month); above.put(city, aWan); total.put(city, t); below.put(city, t == null ? null : round(t - aWan, 4)); aboveYoy.put(city, h2032FreightYoy.get(city)); belowYoy.put(city, null); totalYoy.put(city, freightCumYoy(ftMap.get(city), month)); } XSSFWorkbook wb = new XSSFWorkbook(); Sheet sheet = wb.createSheet("货运量排名"); Row title = sheet.createRow(0); title.createCell(0).setCellValue(year + "年1-" + month + "月全省分市州累计完成公路货运量情况"); int rowIdx = 1; rowIdx = writeRankHead(sheet, rowIdx, "货运量", "吨"); rowIdx = writeFreightRankRows(sheet, rowIdx, null, above, below, total, aboveYoy, belowYoy, totalYoy, provAboveWan, provBelow, provTotal, null, null, provTotalYoy, true); for (String city : RegionUtil.cityList()) { rowIdx = writeFreightRankRows(sheet, rowIdx, city, above, below, total, aboveYoy, belowYoy, totalYoy, provAboveWan, provBelow, provTotal, null, null, provTotalYoy, false); } // 本月区块(当月标题 + 累计数据,与样例结构一致) sheet.createRow(rowIdx++); Row title2 = sheet.createRow(rowIdx++); title2.createCell(0).setCellValue(year + "年" + month + "月全省分市州累计完成" + "公路货运量情况"); rowIdx = writeRankHead(sheet, rowIdx, "货运量", "吨"); rowIdx = writeFreightRankRows(sheet, rowIdx, null, above, below, total, aboveYoy, belowYoy, totalYoy, provAboveWan, provBelow, provTotal, null, null, provTotalYoy, true); for (String city : RegionUtil.cityList()) { rowIdx = writeFreightRankRows(sheet, rowIdx, city, above, below, total, aboveYoy, belowYoy, totalYoy, provAboveWan, provBelow, provTotal, null, null, provTotalYoy, false); } // 末尾空行(与样例结构对齐) for (int i = 0; i < 4; i++) sheet.createRow(rowIdx++); autoWidth(sheet, 18); return toBytes(wb); } // ==================== 3. 生成_周转量排名.xlsx ==================== public byte[] exportTurnoverRank(String period) throws Exception { String year = period.substring(0, 4); int month = parseMonth(period); Map cumMap = loadCumulativeMap(period); ScaleSplitTransport province = getProvinceCumulative(period); XSSFWorkbook wb = new XSSFWorkbook(); Sheet sheet = wb.createSheet("周转量排名"); Row title = sheet.createRow(0); title.createCell(0).setCellValue(year + "年1-" + month + "月全省分市州累计完成公路货物运输周转量情况"); int rowIdx = 1; rowIdx = writeRankHead(sheet, rowIdx, "周转量", "吨公里"); rowIdx = writeTurnoverRankRows(sheet, rowIdx, null, cumMap, province, true); for (String city : RegionUtil.cityList()) { rowIdx = writeTurnoverRankRows(sheet, rowIdx, city, cumMap, province, false); } // 本月区块(当月标题 + 累计数据,与样例结构一致) sheet.createRow(rowIdx++); Row title2 = sheet.createRow(rowIdx++); title2.createCell(0).setCellValue(year + "年" + month + "月全省分市州累计完成" + "公路货物运输周转量情况"); rowIdx = writeRankHead(sheet, rowIdx, "周转量", "吨公里"); rowIdx = writeTurnoverRankRows(sheet, rowIdx, null, cumMap, province, true); for (String city : RegionUtil.cityList()) { rowIdx = writeTurnoverRankRows(sheet, rowIdx, city, cumMap, province, false); } // 末尾空行(与样例结构对齐) for (int i = 0; i < 4; i++) sheet.createRow(rowIdx++); autoWidth(sheet, 18); return toBytes(wb); } // ==================== 数据加载 ==================== /** 当月数据: city -> month -> record(不含全省) */ private Map> loadMonthData(String period) { Map> result = new HashMap<>(); List list = scaleSplitMapper.selectList( new LambdaQueryWrapper() .eq(ScaleSplitTransport::getPeriodType, "MONTH")); for (ScaleSplitTransport record : list) { if (record.getReportPeriod() == null) continue; if (record.getReportPeriod().compareTo(period) > 0) continue; String city = RegionUtil.normalizeCityName(record.getRegionName()); if (city == null) continue; if ("湖北省".equals(city)) continue; int month = parseMonth(record.getReportPeriod()); result.computeIfAbsent(city, k -> new HashMap<>()).put(month, record); } return result; } /** 累计数据: city -> 目标期及之前最近一期(不含全省) */ private Map loadCumulativeMap(String period) { Map result = new HashMap<>(); List list = scaleSplitMapper.selectList( new LambdaQueryWrapper() .eq(ScaleSplitTransport::getPeriodType, "CUMULATIVE")); for (ScaleSplitTransport record : list) { if (record.getReportPeriod() == null) continue; if (record.getReportPeriod().compareTo(period) > 0) continue; String city = RegionUtil.normalizeCityName(record.getRegionName()); if (city == null) continue; if ("湖北省".equals(city)) continue; ScaleSplitTransport existing = result.get(city); if (existing == null || record.getReportPeriod().compareTo(existing.getReportPeriod()) > 0) { result.put(city, record); } } return result; } /** 全省当月: month -> record(忠实读取导入的全省行) */ private Map loadProvinceMonthMap(String period) { Map result = new HashMap<>(); List list = scaleSplitMapper.selectList( new LambdaQueryWrapper() .eq(ScaleSplitTransport::getPeriodType, "MONTH")); for (ScaleSplitTransport record : list) { if (record.getReportPeriod() == null) continue; if (record.getReportPeriod().compareTo(period) > 0) continue; String name = RegionUtil.normalizeCityName(record.getRegionName()); if (!"湖北省".equals(name)) continue; int month = parseMonth(record.getReportPeriod()); result.put(month, record); } return result; } /** 全省累计(忠实读取导入的全省行,不求和) */ private ScaleSplitTransport getProvinceCumulative(String period) { List list = scaleSplitMapper.selectList( new LambdaQueryWrapper() .eq(ScaleSplitTransport::getPeriodType, "CUMULATIVE")); ScaleSplitTransport best = null; for (ScaleSplitTransport record : list) { if (record.getReportPeriod() == null) continue; if (record.getReportPeriod().compareTo(period) > 0) continue; String name = RegionUtil.normalizeCityName(record.getRegionName()); if (!"湖北省".equals(name)) continue; if (best == null || record.getReportPeriod().compareTo(best.getReportPeriod()) > 0) best = record; } return best; } /** H2032规上: city -> month -> 货运量(吨) */ private Map> loadH2032FreightByMonth(String period) { Map> result = new HashMap<>(); List list = h2032Mapper.selectList(null); for (H2032EnterpriseMonthly record : list) { if (record.getReportPeriod() == null || record.getReportPeriod().compareTo(period) > 0) continue; String city = RegionUtil.cityByCode(record.getRegionCode()); if (city == null) continue; int month = parseMonth(record.getReportPeriod()); double freight = record.getFreightTotal() == null ? 0.0 : record.getFreightTotal(); result.computeIfAbsent(city, k -> new HashMap<>()).merge(month, freight, Double::sum); } return result; } /** H2032规上累计: city -> 货运量(吨) */ private Map loadH2032FreightCumulative(String period) { Map result = new HashMap<>(); List list = h2032Mapper.selectList(null); for (H2032EnterpriseMonthly record : list) { if (record.getReportPeriod() == null || record.getReportPeriod().compareTo(period) > 0) continue; String city = RegionUtil.cityByCode(record.getRegionCode()); if (city == null) continue; double freight = record.getFreightTotal() == null ? 0.0 : record.getFreightTotal(); result.merge(city, freight, Double::sum); } return result; } /** H2032规上同比(需去年同期的H2032数据,暂无则为空) */ private Map computeH2032Yoy(String period) { Map result = new HashMap<>(); String lastYear = (Integer.parseInt(period.substring(0, 4)) - 1) + period.substring(4); Map current = loadH2032FreightCumulative(period); Map last = loadH2032FreightCumulative(lastYear); for (Map.Entry entry : current.entrySet()) { double lastVal = last.getOrDefault(entry.getKey(), 0.0); if (lastVal != 0.0) { result.put(entry.getKey(), (entry.getValue() - lastVal) / lastVal); } } return result; } /** 模板_货运量周转量: city -> 行数据(含湖北省) */ private Map loadFreightTurnover(String period) { Map result = new HashMap<>(); List list = freightTurnoverMapper.selectList( new LambdaQueryWrapper() .eq(FreightTurnoverImport::getReportPeriod, period)); for (FreightTurnoverImport e : list) { String city = RegionUtil.normalizeCityName(e.getRegionName()); if (city != null) result.put(city, e); } return result; } // ==================== 排名行写入 ==================== /** 排名表双行表头 */ private int writeRankHead(Sheet sheet, int rowIdx, String metric, String unit) { Row h1 = sheet.createRow(rowIdx++); h1.createCell(0).setCellValue("市州"); h1.createCell(1).setCellValue("规上" + metric); h1.createCell(4).setCellValue("规上增速"); h1.createCell(6).setCellValue("规下" + metric); h1.createCell(9).setCellValue("规下增速"); h1.createCell(11).setCellValue("合计" + metric); h1.createCell(14).setCellValue("合计增速"); h1.createCell(16).setCellValue("分市州规上规下占比"); Row h2 = sheet.createRow(rowIdx++); h2.createCell(1).setCellValue("累计完成 (万" + unit + ")"); h2.createCell(2).setCellValue("排名"); h2.createCell(3).setCellValue("占全省比重"); h2.createCell(4).setCellValue("同比"); h2.createCell(5).setCellValue("增速 排名"); h2.createCell(6).setCellValue("累计完成 (万" + unit + ")"); h2.createCell(7).setCellValue("排名"); h2.createCell(8).setCellValue("占全省比重"); h2.createCell(9).setCellValue("同比"); h2.createCell(10).setCellValue("增速排名"); h2.createCell(11).setCellValue("累计完成 (万" + unit + ")"); h2.createCell(12).setCellValue("排名"); h2.createCell(13).setCellValue("占全省比重"); h2.createCell(14).setCellValue("同比"); h2.createCell(15).setCellValue("增速排名"); return rowIdx; } /** 货运量排名行:规上=H2032累计、合计=模板累计、规下=合计-规上(单位:万吨) */ private int writeFreightRankRows(Sheet sheet, int rowIdx, String city, Map above, Map below, Map total, Map aboveYoy, Map belowYoy, Map totalYoy, Double provAbove, Double provBelow, Double provTotal, Double provAboveYoy, Double provBelowYoy, Double provTotalYoy, boolean isProvince) { Row row = sheet.createRow(rowIdx); row.createCell(0).setCellValue(isProvince ? "全省" : RegionUtil.shortName(city)); Double a = isProvince ? provAbove : above.get(city); Double b = isProvince ? provBelow : below.get(city); Double t = isProvince ? provTotal : total.get(city); Double ay = isProvince ? provAboveYoy : aboveYoy.get(city); Double by = isProvince ? provBelowYoy : belowYoy.get(city); Double ty = isProvince ? provTotalYoy : totalYoy.get(city); setNumeric(row, 1, a); setNumeric(row, 2, isProvince ? null : rankOfMap(above, city)); setNumeric(row, 3, isProvince ? null : ratioOf(a, provAbove)); setNumeric(row, 4, ay); setNumeric(row, 5, isProvince ? null : yoyRankOfMap(aboveYoy, city)); setNumeric(row, 6, b); setNumeric(row, 7, isProvince ? null : rankOfMap(below, city)); setNumeric(row, 8, isProvince ? null : ratioOf(b, provBelow)); setNumeric(row, 9, by); setNumeric(row, 10, isProvince ? null : yoyRankOfMap(belowYoy, city)); setNumeric(row, 11, t); setNumeric(row, 12, isProvince ? null : rankOfMap(total, city)); setNumeric(row, 13, isProvince ? null : ratioOf(t, provTotal)); setNumeric(row, 14, ty); setNumeric(row, 15, isProvince ? null : yoyRankOfMap(totalYoy, city)); if (t != null && t > 0 && a != null) { double share = round(a * 10.0 / t, 1); setNumeric(row, 16, share); setNumeric(row, 17, round(10.0 - share, 1)); } return rowIdx + 1; } private int writeTurnoverRankRows(Sheet sheet, int rowIdx, String city, Map cumMap, ScaleSplitTransport province, boolean isProvince) { if (province == null) province = new ScaleSplitTransport(); Row row = sheet.createRow(rowIdx); row.createCell(0).setCellValue(isProvince ? "全省" : RegionUtil.shortName(city)); ScaleSplitTransport cum = isProvince ? null : cumMap.get(city); Double above = isProvince ? province.getAboveScaleTurnover() : (cum == null ? null : cum.getAboveScaleTurnover()); Double below = isProvince ? province.getBelowScaleTurnover() : (cum == null ? null : cum.getBelowScaleTurnover()); Double total = isProvince ? province.getTotalTurnover() : (cum == null ? null : cum.getTotalTurnover()); Double provinceAbove = province.getAboveScaleTurnover(); Double provinceBelow = province.getBelowScaleTurnover(); Double provinceTotal = province.getTotalTurnover(); setNumeric(row, 1, above); setNumeric(row, 2, isProvince || cum == null ? null : rankOfTurnover(cumMap, city, 0)); setNumeric(row, 3, isProvince ? null : ratioOf(above, provinceAbove)); setNumeric(row, 4, isProvince ? province.getAboveScaleYoy() : (cum == null ? null : cum.getAboveScaleYoy())); setNumeric(row, 5, isProvince || cum == null ? null : yoyRankOfTurnover(cumMap, city, 0)); setNumeric(row, 6, below); setNumeric(row, 7, isProvince || cum == null ? null : rankOfTurnover(cumMap, city, 1)); setNumeric(row, 8, isProvince ? null : ratioOf(below, provinceBelow)); setNumeric(row, 9, isProvince ? province.getBelowScaleYoy() : (cum == null ? null : cum.getBelowScaleYoy())); setNumeric(row, 10, isProvince || cum == null ? null : yoyRankOfTurnover(cumMap, city, 1)); setNumeric(row, 11, total); setNumeric(row, 12, isProvince || cum == null ? null : rankOfTurnover(cumMap, city, 2)); setNumeric(row, 13, isProvince ? null : ratioOf(total, provinceTotal)); setNumeric(row, 14, isProvince ? province.getTotalYoy() : (cum == null ? null : cum.getTotalYoy())); setNumeric(row, 15, isProvince || cum == null ? null : yoyRankOfTurnover(cumMap, city, 2)); if (total != null && total > 0 && above != null) { double share = round(above * 10.0 / total, 1); setNumeric(row, 16, share); setNumeric(row, 17, round(10.0 - share, 1)); } return rowIdx + 1; } // ==================== 明细取值辅助 ==================== /** 全省当月值:周转量取拆分表,货运量取模板(合计)/H2032(规上)/差值(规下) */ private Double getProvinceMonthValue(Map provinceMonthMap, FreightTurnoverImport ft, int month, String metric, String scale, Map> h2032Freight) { if ("turnover".equals(metric)) { return getMetric(provinceMonthMap.get(month), metric, scale); } if ("total".equals(scale)) return freightMonth(ft, month); if ("above".equals(scale)) { double sum = 0.0; for (Map byMonth : h2032Freight.values()) { Double v = byMonth == null ? null : byMonth.get(month); if (v != null) sum += v; } return round(sum / 10000.0, 4); } Double total = freightMonth(ft, month); Double above = getProvinceMonthValue(provinceMonthMap, ft, month, "freight", "above", h2032Freight); if (total == null) return null; return total - above; } /** 全省当月同比:周转量取拆分表,货运量合计取模板(左半vs右半) */ private Double getProvinceMonthYoy(Map provinceMonthMap, FreightTurnoverImport ft, int month, String metric, String scale) { if ("turnover".equals(metric)) { return getYoyMetric(provinceMonthMap.get(month), metric, scale); } if ("total".equals(scale)) return freightYoy(ft, month); return null; } /** 全省累计:周转量取拆分表累计,货运量合计=模板1-N月之和,规上=H2032累计,规下=差值 */ private Double getProvinceCumValue(ScaleSplitTransport provinceCum, FreightTurnoverImport ft, String metric, String scale, Map h2032FreightCum, int monthCount) { if ("turnover".equals(metric)) { return getMetric(provinceCum, metric, scale); } if ("total".equals(scale)) return freightCum(ft, monthCount); if ("above".equals(scale)) { double sum = 0.0; for (Double v : h2032FreightCum.values()) { if (v != null) sum += v; } return round(sum / 10000.0, 4); } Double total = freightCum(ft, monthCount); Double above = getProvinceCumValue(provinceCum, ft, "freight", "above", h2032FreightCum, monthCount); if (total == null) return null; return total - above; } /** 全省累计同比:周转量取拆分表,货运量合计=模板累计同比 */ private Double getProvinceCumYoy(ScaleSplitTransport provinceCum, FreightTurnoverImport ft, String metric, String scale, int monthCount) { if ("turnover".equals(metric)) { return getYoyMetric(provinceCum, metric, scale); } if ("total".equals(scale)) return freightCumYoy(ft, monthCount); return null; } /** 市州当月值 */ private Double getCityMonthValue(Map> monthData, FreightTurnoverImport ft, String city, int month, String metric, String scale, Map> h2032Freight) { if ("turnover".equals(metric)) { return getMetric(getRecord(monthData, city, month), metric, scale); } if ("total".equals(scale)) return freightMonth(ft, month); if ("above".equals(scale)) { Map byMonth = h2032Freight.get(city); Double v = byMonth == null ? null : byMonth.get(month); return v == null ? 0.0 : round(v / 10000.0, 4); } Double total = freightMonth(ft, month); Double above = getCityMonthValue(monthData, ft, city, month, "freight", "above", h2032Freight); if (total == null) return null; return total - above; } /** 市州当月同比 */ private Double getCityMonthYoy(Map> monthData, FreightTurnoverImport ft, String city, int month, String metric, String scale) { if ("turnover".equals(metric)) { return getYoyMetric(getRecord(monthData, city, month), metric, scale); } if ("total".equals(scale)) return freightYoy(ft, month); return null; } /** 市州累计 */ private Double getCityCumValue(ScaleSplitTransport cum, FreightTurnoverImport ft, String metric, String scale, Map h2032FreightCum, String city, int monthCount) { if ("turnover".equals(metric)) { return cum == null ? null : getMetric(cum, metric, scale); } if ("total".equals(scale)) return freightCum(ft, monthCount); if ("above".equals(scale)) { Double v = h2032FreightCum.get(city); return v == null ? 0.0 : round(v / 10000.0, 4); } Double total = freightCum(ft, monthCount); Double above = getCityCumValue(cum, ft, "freight", "above", h2032FreightCum, city, monthCount); if (total == null) return null; return total - above; } /** 市州累计同比 */ private Double getCityCumYoy(ScaleSplitTransport cum, FreightTurnoverImport ft, String metric, String scale, int monthCount) { if ("turnover".equals(metric)) { return cum == null ? null : getYoyMetric(cum, metric, scale); } if ("total".equals(scale)) return freightCumYoy(ft, monthCount); return null; } // ==================== 模板_货运量周转量取值 ==================== private Double freightMonth(FreightTurnoverImport e, int m) { if (e == null) return null; switch (m) { case 1: return e.getFreightM01(); case 2: return e.getFreightM02(); case 3: return e.getFreightM03(); case 4: return e.getFreightM04(); case 5: return e.getFreightM05(); case 6: return e.getFreightM06(); case 7: return e.getFreightM07(); case 8: return e.getFreightM08(); case 9: return e.getFreightM09(); case 10: return e.getFreightM10(); case 11: return e.getFreightM11(); case 12: return e.getFreightM12(); default: return null; } } private Double lastFreightMonth(FreightTurnoverImport e, int m) { if (e == null) return null; switch (m) { case 1: return e.getLastFreightM01(); case 2: return e.getLastFreightM02(); case 3: return e.getLastFreightM03(); case 4: return e.getLastFreightM04(); case 5: return e.getLastFreightM05(); case 6: return e.getLastFreightM06(); case 7: return e.getLastFreightM07(); case 8: return e.getLastFreightM08(); case 9: return e.getLastFreightM09(); case 10: return e.getLastFreightM10(); case 11: return e.getLastFreightM11(); case 12: return e.getLastFreightM12(); default: return null; } } /** 模板货运量当月同比 = (今年m - 去年m) / 去年m */ private Double freightYoy(FreightTurnoverImport e, int m) { Double cur = freightMonth(e, m); Double last = lastFreightMonth(e, m); if (cur == null || last == null || last == 0.0) return null; return (cur - last) / last; } /** 模板货运量累计 = 左半 1..monthCount 之和 */ private Double freightCum(FreightTurnoverImport e, int monthCount) { if (e == null) return null; double sum = 0.0; boolean any = false; for (int m = 1; m <= monthCount; m++) { Double v = freightMonth(e, m); if (v != null) { sum += v; any = true; } } return any ? sum : null; } /** 模板去年货运量累计 = 右半 1..monthCount 之和(去年1-12月固定,取前N个月) */ private Double lastFreightCum(FreightTurnoverImport e, int monthCount) { if (e == null) return null; double sum = 0.0; boolean any = false; for (int m = 1; m <= monthCount; m++) { Double v = lastFreightMonth(e, m); if (v != null) { sum += v; any = true; } } return any ? sum : null; } /** 模板货运量累计同比 = (今年1-N累计 - 去年1-N累计) / 去年1-N累计 */ private Double freightCumYoy(FreightTurnoverImport e, int monthCount) { Double cur = freightCum(e, monthCount); Double last = lastFreightCum(e, monthCount); if (cur == null || last == null || last == 0.0) return null; return (cur - last) / last; } // ==================== 拆分表取值辅助 ==================== private Double getMetric(ScaleSplitTransport record, String metric, String scale) { if (record == null) return null; if ("freight".equals(metric)) { if ("above".equals(scale)) return record.getAboveScaleFreight(); if ("below".equals(scale)) return record.getBelowScaleFreight(); return record.getTotalFreight(); } else { if ("above".equals(scale)) return record.getAboveScaleTurnover(); if ("below".equals(scale)) return record.getBelowScaleTurnover(); return record.getTotalTurnover(); } } private Double getYoyMetric(ScaleSplitTransport record, String metric, String scale) { if (record == null) return null; if ("above".equals(scale)) return record.getAboveScaleYoy(); if ("below".equals(scale)) return record.getBelowScaleYoy(); return record.getTotalYoy(); } private ScaleSplitTransport getRecord(Map> monthData, String city, int month) { Map map = monthData.get(city); return map == null ? null : map.get(month); } private Integer rankOfMap(Map map, String city) { Double self = map.get(city); if (self == null) return null; int rank = 1; for (Double v : map.values()) { if (v != null && v > self) rank++; } return rank; } private Integer yoyRankOfMap(Map map, String city) { Double self = map.get(city); if (self == null) return null; int rank = 1; for (Double v : map.values()) { if (v != null && v > self) rank++; } return rank; } private Integer rankOfTurnover(Map cumMap, String city, int which) { Double self = null; for (Map.Entry e : cumMap.entrySet()) { ScaleSplitTransport r = e.getValue(); Double v = which == 0 ? r.getAboveScaleTurnover() : which == 1 ? r.getBelowScaleTurnover() : r.getTotalTurnover(); if (v == null) continue; if (RegionUtil.normalizeCityName(e.getKey()).equals(RegionUtil.normalizeCityName(city))) self = v; } if (self == null) return null; int rank = 1; for (ScaleSplitTransport r : cumMap.values()) { Double v = which == 0 ? r.getAboveScaleTurnover() : which == 1 ? r.getBelowScaleTurnover() : r.getTotalTurnover(); if (v != null && v > self) rank++; } return rank; } private Integer yoyRankOfTurnover(Map cumMap, String city, int which) { Double self = null; for (Map.Entry e : cumMap.entrySet()) { ScaleSplitTransport r = e.getValue(); Double v = which == 0 ? r.getAboveScaleYoy() : which == 1 ? r.getBelowScaleYoy() : r.getTotalYoy(); if (v == null) continue; if (RegionUtil.normalizeCityName(e.getKey()).equals(RegionUtil.normalizeCityName(city))) self = v; } if (self == null) return null; int rank = 1; for (ScaleSplitTransport r : cumMap.values()) { Double v = which == 0 ? r.getAboveScaleYoy() : which == 1 ? r.getBelowScaleYoy() : r.getTotalYoy(); if (v != null && v > self) rank++; } return rank; } // ==================== 通用辅助 ==================== private int parseMonth(String period) { return Integer.parseInt(period.split("-")[1]); } private String pad(int m) { return m < 10 ? "0" + m : String.valueOf(m); } private double round(double v, int scale) { double factor = Math.pow(10, scale); return Math.round(v * factor) / factor; } private Double ratioOf(Double part, Double total) { if (part == null || total == null || total == 0.0) return null; return part / total; } private void setNumeric(Row row, int idx, Double value) { Cell cell = row.createCell(idx); if (value != null) cell.setCellValue(value); } private void setNumeric(Row row, int idx, Integer value) { if (value != null) setNumeric(row, idx, value.doubleValue()); } private void autoWidth(Sheet sheet, int colCount) { for (int i = 0; i < colCount; i++) { sheet.setColumnWidth(i, 12 * 256); } sheet.setColumnWidth(0, 16 * 256); sheet.setColumnWidth(1, 22 * 256); } private byte[] toBytes(XSSFWorkbook wb) throws Exception { try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { wb.write(out); wb.close(); return out.toByteArray(); } } }