| | |
| | | totalYoy.put(city, freightCumYoy(ftMap.get(city), month)); |
| | | } |
| | | |
| | | |
| | | // ===== 第二页(当月)数据:规上=H2032 当月、合计=模板_货运量周转量当月列、规下=差值;同比=去年同月 ===== |
| | | Map<String, Map<Integer, Double>> h2032MonthCur = loadH2032FreightByMonth(period); |
| | | String lastYearPeriod = (Integer.parseInt(year) - 1) + period.substring(4); |
| | | Map<String, Map<Integer, Double>> h2032MonthLast = loadH2032FreightByMonth(lastYearPeriod); |
| | | Map<String, Double> monthAbove = new HashMap<>(); |
| | | Map<String, Double> monthBelow = new HashMap<>(); |
| | | Map<String, Double> monthTotal = new HashMap<>(); |
| | | Map<String, Double> monthAboveYoy = new HashMap<>(); |
| | | Map<String, Double> monthBelowYoy = new HashMap<>(); |
| | | Map<String, Double> monthTotalYoy = new HashMap<>(); |
| | | double provMonthAbove = 0.0, provMonthAboveLast = 0.0; |
| | | for (String city : RegionUtil.cityList()) { |
| | | Double ma = monthFreightWan(h2032MonthCur, city, month); |
| | | Double maLast = monthFreightWan(h2032MonthLast, city, month); |
| | | Double mt = freightMonth(ftMap.get(city), month); |
| | | Double mtLast = lastFreightMonth(ftMap.get(city), month); |
| | | if (ma != null) provMonthAbove += ma; |
| | | if (maLast != null) provMonthAboveLast += maLast; |
| | | monthAbove.put(city, ma); |
| | | monthTotal.put(city, mt); |
| | | monthBelow.put(city, mt == null ? null : round(mt - (ma == null ? 0.0 : ma), 4)); |
| | | monthAboveYoy.put(city, growthYoy(ma, maLast)); |
| | | Double mb = monthBelow.get(city); |
| | | Double mbLast = mtLast == null ? null : round(mtLast - (maLast == null ? 0.0 : maLast), 4); |
| | | monthBelowYoy.put(city, growthYoy(mb, mbLast)); |
| | | monthTotalYoy.put(city, freightYoy(ftMap.get(city), month)); |
| | | } |
| | | Double provMonthAboveWan = provMonthAbove == 0.0 ? null : round(provMonthAbove, 4); |
| | | Double provMonthTotal = freightMonth(ftMap.get("湖北省"), month); |
| | | Double provMonthBelow = provMonthTotal == null ? null : round(provMonthTotal - provMonthAbove, 4); |
| | | Double provMonthAboveYoy = growthYoy(provMonthAbove == 0.0 ? null : provMonthAbove, provMonthAboveLast); |
| | | Double provMonthTotalLast = lastFreightMonth(ftMap.get("湖北省"), month); |
| | | Double provMonthBelowLast = provMonthTotalLast == null ? null |
| | | : round(provMonthTotalLast - provMonthAboveLast, 4); |
| | | Double provMonthBelowYoy = growthYoy(provMonthBelow, provMonthBelowLast); |
| | | Double provMonthTotalYoy = freightYoy(ftMap.get("湖北省"), month); |
| | | |
| | | // 以 docs/货运/生成_货运量排名.xlsx 为底稿:保留表头/合并/列宽/样式与 Q/R 占比公式,仅替换数据 |
| | | File template = resolveFreightTemplate("生成_货运量排名.xlsx"); |
| | | try (InputStream in = new FileInputStream(template); |
| | | XSSFWorkbook wb = new XSSFWorkbook(in)) { |
| | | Sheet sheet = wb.getSheetAt(0); |
| | | sheet.getRow(0).getCell(0).setCellValue(year + "年" + cumRange(month) + "全省分市州累计完成公路货运量情况"); |
| | | sheet.getRow(22).getCell(0).setCellValue(year + "年" + month + "月全省分市州累计完成公路货运量情况"); |
| | | sheet.getRow(22).getCell(0).setCellValue(year + "年" + month + "月全省分市州完成公路货运量情况"); |
| | | fillFreightRankBlock(sheet, 3, above, below, total, aboveYoy, belowYoy, totalYoy, |
| | | provAboveWan, provBelow, provTotal, provAboveYoy, provBelowYoy, provTotalYoy); |
| | | fillFreightRankBlock(sheet, 25, above, below, total, aboveYoy, belowYoy, totalYoy, |
| | | provAboveWan, provBelow, provTotal, provAboveYoy, provBelowYoy, provTotalYoy); |
| | | renameRankMonthHeader(sheet, 24); |
| | | fillFreightRankBlock(sheet, 25, monthAbove, monthBelow, monthTotal, |
| | | monthAboveYoy, monthBelowYoy, monthTotalYoy, |
| | | provMonthAboveWan, provMonthBelow, provMonthTotal, |
| | | provMonthAboveYoy, provMonthBelowYoy, provMonthTotalYoy); |
| | | try { |
| | | wb.getCreationHelper().createFormulaEvaluator().evaluateAll(); |
| | | } catch (Exception e) { |
| | |
| | | Map<String, ScaleSplitTransport> cumMap = loadCumulativeMap(period); |
| | | ScaleSplitTransport province = getProvinceCumulative(period); |
| | | |
| | | // 当月(第二页):周转量拆分表 MONTH 行(含当月同比) |
| | | Map<String, ScaleSplitTransport> monthTurnover = new HashMap<>(); |
| | | for (ScaleSplitTransport rec : scaleSplitMapper.selectList( |
| | | new LambdaQueryWrapper<ScaleSplitTransport>() |
| | | .eq(ScaleSplitTransport::getPeriodType, "MONTH") |
| | | .eq(ScaleSplitTransport::getReportPeriod, period))) { |
| | | String name = RegionUtil.normalizeCityName(rec.getRegionName()); |
| | | if (name != null) monthTurnover.put(name, rec); |
| | | } |
| | | ScaleSplitTransport provinceMonth = monthTurnover.remove("湖北省"); |
| | | |
| | | // 以 docs/货运/生成_周转量排名.xlsx 为底稿:保留表头/合并/列宽/样式,仅替换数据;Q/R 占比为模板缓存值,按新数据重算 |
| | | File template = resolveFreightTemplate("生成_周转量排名.xlsx"); |
| | | try (InputStream in = new FileInputStream(template); |
| | | XSSFWorkbook wb = new XSSFWorkbook(in)) { |
| | | Sheet sheet = wb.getSheetAt(0); |
| | | sheet.getRow(0).getCell(0).setCellValue(year + "年" + cumRange(month) + "全省分市州累计完成公路货物运输周转量情况"); |
| | | sheet.getRow(22).getCell(0).setCellValue(year + "年" + month + "月全省分市州累计完成公路货物运输周转量情况"); |
| | | sheet.getRow(22).getCell(0).setCellValue(year + "年" + month + "月全省分市州完成公路货物运输周转量情况"); |
| | | fillTurnoverRankBlock(sheet, 3, cumMap, province); |
| | | fillTurnoverRankBlock(sheet, 25, cumMap, province); |
| | | renameRankMonthHeader(sheet, 24); |
| | | fillTurnoverRankBlock(sheet, 25, monthTurnover, provinceMonth); |
| | | return toBytes(wb); |
| | | } |
| | | } |
| | |
| | | setFreightCell(row, cumCol + 1, cumYoy); |
| | | } |
| | | |
| | | /** H2032 规上某月合计(吨→万吨);无该月数据返回 null */ |
| | | private Double monthFreightWan(Map<String, Map<Integer, Double>> byCityMonth, String city, int month) { |
| | | Map<Integer, Double> byMonth = byCityMonth == null ? null : byCityMonth.get(city); |
| | | Double v = byMonth == null ? null : byMonth.get(month); |
| | | return v == null ? null : round(v / 10000.0, 4); |
| | | } |
| | | |
| | | /** 同比 = (本期-基期)/基期;任一期缺失或基期为 0 返回 null */ |
| | | private Double growthYoy(Double cur, Double last) { |
| | | if (cur == null || last == null || last == 0.0) return null; |
| | | return (cur - last) / last; |
| | | } |
| | | |
| | | /** 排名表第二页列头「累计完成」→「当月完成」(仅导出内存替换,模板文件不改) */ |
| | | private void renameRankMonthHeader(Sheet sheet, int headerRowIdx) { |
| | | Row row = sheet.getRow(headerRowIdx); |
| | | if (row == null) return; |
| | | for (int col : new int[]{1, 6, 11}) { |
| | | Cell c = row.getCell(col); |
| | | if (c == null || c.getCellType() != CellType.STRING) continue; |
| | | String s = c.getStringCellValue(); |
| | | if (s != null && s.contains("累计完成")) c.setCellValue(s.replace("累计完成", "当月完成")); |
| | | } |
| | | } |
| | | |
| | | /** 货运量排名块填充:模板 1-based r4 起(全省+17市州),Q/R 占比公式保留不动 */ |
| | | private void fillFreightRankBlock(Sheet sheet, int start, |
| | | Map<String, Double> above, Map<String, Double> below, Map<String, Double> total, |
| | |
| | | /** H2032规上: city -> month -> 货运量(吨) */ |
| | | private Map<String, Map<Integer, Double>> loadH2032FreightByMonth(String period) { |
| | | Map<String, Map<Integer, Double>> result = new HashMap<>(); |
| | | String yearPrefix = period.length() >= 4 ? period.substring(0, 4) : null; |
| | | List<H2032EnterpriseMonthly> list = h2032Mapper.selectList(null); |
| | | for (H2032EnterpriseMonthly record : list) { |
| | | if (record.getReportPeriod() == null || record.getReportPeriod().compareTo(period) > 0) continue; |
| | | // H2032 表同时存有往年月报,统计当年规上值时只取与报表期同年的记录,避免去年数据叠加 |
| | | if (yearPrefix != null && !record.getReportPeriod().startsWith(yearPrefix)) continue; |
| | | String city = RegionUtil.cityByCode(record.getRegionCode()); |
| | | if (city == null) continue; |
| | | int month = parseMonth(record.getReportPeriod()); |
| | |
| | | /** H2032规上累计: city -> 货运量(吨) */ |
| | | private Map<String, Double> loadH2032FreightCumulative(String period) { |
| | | Map<String, Double> result = new HashMap<>(); |
| | | String yearPrefix = period.length() >= 4 ? period.substring(0, 4) : null; |
| | | List<H2032EnterpriseMonthly> list = h2032Mapper.selectList(null); |
| | | for (H2032EnterpriseMonthly record : list) { |
| | | if (record.getReportPeriod() == null || record.getReportPeriod().compareTo(period) > 0) continue; |
| | | if (yearPrefix != null && !record.getReportPeriod().startsWith(yearPrefix)) continue; |
| | | String city = RegionUtil.cityByCode(record.getRegionCode()); |
| | | if (city == null) continue; |
| | | double freight = record.getFreightTotal() == null ? 0.0 : record.getFreightTotal(); |
| | |
| | | // 累计同比列(模板 #REF! → 库内去年 1..N 月累计同比数值) |
| | | fillMidCumYoyColumn(sheet, mid, currentYear, fillMonths, extended ? 27 : 15); |
| | | recalc(wb); |
| | | clearFormulaErrorsAll(wb); // 去年同期明细页残留 #REF! 同比公式 → 清空 |
| | | return toBytes(wb); |
| | | } |
| | | } |
| | |
| | | title.getCell(0).setCellValue(currentYear + "年1-" + maxMonth + "月全省分市州累计完成道路客运生产情况"); |
| | | } |
| | | } |
| | | // 右块(当月)标题 K1 |
| | | if (title != null && title.getCell(10) != null) { |
| | | title.getCell(10).setCellValue(currentYear + "年" + maxMonth + "月全省分市州完成道路客运生产情况"); |
| | | } |
| | | // 全省行 r3:B3/F3 为 SUM 公式保留;D3/H3 同比填值(0-based 列 3/7) |
| | | setValOrBlank(sheet, 2, 3, yoyOf(midCumClass(mid, currentYear, "湖北省", maxMonth, 0, true), |
| | | midCumClass(mid, currentYear - 1, "湖北省", maxMonth, 0, true))); |
| | | setValOrBlank(sheet, 2, 7, yoyOf(midCumClass(mid, currentYear, "湖北省", maxMonth, 0, false), |
| | | midCumClass(mid, currentYear - 1, "湖北省", maxMonth, 0, false))); |
| | | // 右块全省行 N3/R3:当月同比(L3/P3 为 SUM 公式保留) |
| | | setValOrBlank(sheet, 2, 13, yoyOf(midClassVal(mid, currentYear, maxMonth, "湖北省", 0, true), |
| | | midClassVal(mid, currentYear - 1, maxMonth, "湖北省", 0, true))); |
| | | setValOrBlank(sheet, 2, 17, yoyOf(midClassVal(mid, currentYear, maxMonth, "湖北省", 0, false), |
| | | midClassVal(mid, currentYear - 1, maxMonth, "湖北省", 0, false))); |
| | | // 市州行 r4-20:B/F 累计值、D/H 同比;C/E/G/I 排名公式保留 |
| | | List<String> cities = RegionUtil.CITY_LIST; |
| | | for (int i = 0; i < cities.size(); i++) { |
| | |
| | | setValOrBlank(sheet, r0, 3, yoyOf(pass, lastPass)); |
| | | setValOrBlank(sheet, r0, 5, turn == 0 ? null : round(turn, 4)); |
| | | setValOrBlank(sheet, r0, 7, yoyOf(turn, lastTurn)); |
| | | // 右块当月:L/P 值、N/R 当月同比(M/O/Q/S 排名公式保留) |
| | | double mPass = midClassVal(mid, currentYear, maxMonth, city, 0, true); |
| | | double mTurn = midClassVal(mid, currentYear, maxMonth, city, 0, false); |
| | | double mLastPass = midClassVal(mid, currentYear - 1, maxMonth, city, 0, true); |
| | | double mLastTurn = midClassVal(mid, currentYear - 1, maxMonth, city, 0, false); |
| | | setValOrBlank(sheet, r0, 11, mPass == 0 ? null : round(mPass, 4)); |
| | | setValOrBlank(sheet, r0, 13, mPass == 0 ? null : yoyOf(mPass, mLastPass)); |
| | | setValOrBlank(sheet, r0, 15, mTurn == 0 ? null : round(mTurn, 4)); |
| | | setValOrBlank(sheet, r0, 17, mTurn == 0 ? null : yoyOf(mTurn, mLastTurn)); |
| | | } |
| | | recalc(wb); |
| | | return toBytes(wb); |
| | |
| | | setValOrBlank(sheet, r0, 2, yoyOf(pass, lastPass)); |
| | | setValOrBlank(sheet, r0, 5, turn == 0 ? null : round(turn, 4)); |
| | | setValOrBlank(sheet, r0, 6, yoyOf(turn, lastTurn)); |
| | | } |
| | | // 下半块(当月):标题行9、数据行11-15(0-based 8、10-14) |
| | | Row bottomTitle = sheet.getRow(8); |
| | | if (bottomTitle != null && bottomTitle.getCell(0) != null) { |
| | | bottomTitle.getCell(0).setCellValue(currentYear + "年" + maxMonth + "月中口径客运量及周转量"); |
| | | } |
| | | for (int i = 0; i < names.length; i++) { |
| | | int r0 = 10 + i; |
| | | double pass = midClassVal(mid, currentYear, maxMonth, "湖北省", dims[i], true); |
| | | double turn = midClassVal(mid, currentYear, maxMonth, "湖北省", dims[i], false); |
| | | double lastPass = midClassVal(mid, currentYear - 1, maxMonth, "湖北省", dims[i], true); |
| | | double lastTurn = midClassVal(mid, currentYear - 1, maxMonth, "湖北省", dims[i], false); |
| | | setValOrBlank(sheet, r0, 1, pass == 0 ? null : round(pass, 4)); |
| | | setValOrBlank(sheet, r0, 2, pass == 0 ? null : yoyOf(pass, lastPass)); |
| | | setValOrBlank(sheet, r0, 5, turn == 0 ? null : round(turn, 4)); |
| | | setValOrBlank(sheet, r0, 6, turn == 0 ? null : yoyOf(turn, lastTurn)); |
| | | } |
| | | // 下半块占比公式由样例指向上半块(B$3/F$3),改写为本块总行(B$11/F$11),行12-15 |
| | | for (int i = 1; i < names.length; i++) { |
| | | int r0 = 10 + i; |
| | | Row rr = sheet.getRow(r0); |
| | | if (rr == null) continue; |
| | | Cell dc = rr.getCell(3); |
| | | if (dc != null && dc.getCellType() == CellType.FORMULA) { |
| | | dc.setCellFormula("B" + (r0 + 1) + "/B$11"); |
| | | } |
| | | Cell hc = rr.getCell(7); |
| | | if (hc != null && hc.getCellType() == CellType.FORMULA) { |
| | | hc.setCellFormula("F" + (r0 + 1) + "/F$11"); |
| | | } |
| | | } |
| | | recalc(wb); |
| | | return toBytes(wb); |
| | |
| | | fillEnergyTemplateSheet(s, agg); |
| | | } |
| | | recalc(wb); |
| | | clearFormulaErrorsAll(wb); // 无去年数据的同比/增速公式 #DIV/0! → 清空 |
| | | return toBytes(wb); |
| | | } |
| | | } |
| | |
| | | public byte[] exportCityBusDetail(String period, String mode) throws Exception { |
| | | int year = Integer.parseInt(period.split("-")[0]); |
| | | int month = monthOf(period, mode); |
| | | return exportCityByTemplate("导入模板_城市公交.xlsx", period, mode, |
| | | return exportCityByTemplate("生成_城市公交客运量分市州明细.xlsx", period, mode, |
| | | loadCityBusByMonth(year + "-", month), |
| | | loadCityBusByMonth((year - 1) + "-", month)); |
| | | } |
| | |
| | | public byte[] exportCityTaxiDetail(String period, String mode) throws Exception { |
| | | int year = Integer.parseInt(period.split("-")[0]); |
| | | int month = monthOf(period, mode); |
| | | return exportCityByTemplate("导入模板_巡游出租.xlsx", period, mode, |
| | | return exportCityByTemplate("生成_巡游出租客运量分市州明细.xlsx", period, mode, |
| | | loadCityTaxiByMonth(year + "-", month), |
| | | loadCityTaxiByMonth((year - 1) + "-", month)); |
| | | } |
| | |
| | | int year = Integer.parseInt(period.split("-")[0]); |
| | | int month = monthOf(period, mode); |
| | | Map<Integer, Map<String, double[]>> monthCity = loadCityRailFerryByMonth(year + "-", month); |
| | | File template = resolveTemplate("导入模板_轨道、轮渡.xlsx"); |
| | | File template = resolveTemplate("生成_轨道轮渡客运量分市州明细.xlsx"); |
| | | try (InputStream in = new FileInputStream(template); |
| | | XSSFWorkbook wb = new XSSFWorkbook(in)) { |
| | | Sheet sheet = wb.getSheetAt(0); |
| | |
| | | // 轮渡:客运量行 13(全省)/15(武汉);指标 2=轮渡客运量 3=轮渡周转量 |
| | | fillRailFerryBlock(sheet, monthCity, month, new String[][]{{"全省", "12"}, {"武汉市", "14"}}, 2); |
| | | recalc(wb); |
| | | clearFormulaErrorCells(sheet, 3, sheet.getLastRowNum(), 1, 39); |
| | | fixCityMonthHeaderText(sheet); |
| | | return toBytes(wb); |
| | | } |
| | | } |
| | |
| | | int year = Integer.parseInt(period.split("-")[0]); |
| | | int month = monthOf(period, mode); |
| | | Map<String, double[]> cum = loadCityPassengerCumulative(year + "-", month); |
| | | Map<String, double[]> monthMap = loadCityPassengerMonthMap(year + "-", month); |
| | | File template = resolveTemplate(templateFileName); |
| | | try (InputStream in = new FileInputStream(template); |
| | | XSSFWorkbook wb = new XSSFWorkbook(in)) { |
| | |
| | | List<String> areas = new java.util.ArrayList<>(); |
| | | areas.add("全省"); |
| | | areas.addAll(RegionUtil.cityList()); |
| | | fillCitySumBlock(sheet, cum, areas, 4, 0); // 客运量区(单位:万人次,POI 0 基) |
| | | fillCitySumBlock(sheet, cum, areas, 26, 1); // 周转量区(单位:万人次公里,POI 0 基) |
| | | fillCitySumBlock(sheet, cum, areas, 4, 0); // 累计客运量区(单位:万人次,POI 0 基,行5-22) |
| | | fillCitySumBlock(sheet, cum, areas, 26, 1); // 累计周转量区(单位:万人次公里,POI 0 基,行27-44) |
| | | // 当月块标题(0基行44/66)与数据(0基行48-65/70-87) |
| | | Row pTitle = sheet.getRow(44); |
| | | if (pTitle != null && pTitle.getCell(0) != null) { |
| | | pTitle.getCell(0).setCellValue("城市客运客运量分市州" + month + "月情况"); |
| | | } |
| | | Row tTitle = sheet.getRow(66); |
| | | if (tTitle != null && tTitle.getCell(0) != null) { |
| | | tTitle.getCell(0).setCellValue("城市客运客运周转量分市州" + month + "月情况"); |
| | | } |
| | | fillCitySumBlock(sheet, monthMap, areas, 48, 0); // 当月客运量区(行49-66) |
| | | fillCitySumBlock(sheet, monthMap, areas, 70, 1); // 当月周转量区(行71-88) |
| | | // 当月块排名/占比公式由累计块复制而来,RANK 范围与占比分母需改指当月块自身行 |
| | | repairCityMonthRankShare(sheet, 50, 66, 49); |
| | | repairCityMonthRankShare(sheet, 72, 88, 71); |
| | | recalc(wb); |
| | | clearFormulaErrorsAll(wb); // 无去年累计的占比/增速公式 #DIV/0! → 清空 |
| | | return toBytes(wb); |
| | | } |
| | | } |
| | |
| | | setDataCell(sheet, rowIdx, 24, 0); |
| | | rowIdx++; |
| | | } |
| | | } |
| | | |
| | | /** 当月块排名/占比公式修正:模板当月块由累计块复制而来,RANK 范围与占比分母需改指当月块自身行 */ |
| | | private void repairCityMonthRankShare(Sheet sheet, int firstDataRow1, int lastDataRow1, int provRow1) { |
| | | String[] groups = {"B", "G", "L", "Q"}; |
| | | String[] growth = {"E", "J", "O", "T"}; |
| | | for (int r = firstDataRow1; r <= lastDataRow1; r++) { |
| | | int r0 = r - 1; |
| | | for (int g = 0; g < 4; g++) { |
| | | String base = groups[g]; |
| | | String grow = growth[g]; |
| | | setFormulaIfFormula(sheet, r0, 2 + g * 5, |
| | | "RANK(" + base + r + "," + base + "$" + firstDataRow1 + ":" + base + "$" + lastDataRow1 + ")"); |
| | | setFormulaIfFormula(sheet, r0, 3 + g * 5, base + r + "/" + base + "$" + provRow1); |
| | | setFormulaIfFormula(sheet, r0, 5 + g * 5, |
| | | "RANK(" + grow + r + "," + grow + "$" + firstDataRow1 + ":" + grow + "$" + lastDataRow1 + ")"); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** 单元格原为公式时改写公式(排名/占比列),非公式单元格不动(保持模板式样) */ |
| | | private void setFormulaIfFormula(Sheet sheet, int rowIdx, int colIdx, String formula) { |
| | | Row row = sheet.getRow(rowIdx); |
| | | if (row == null) return; |
| | | Cell c = row.getCell(colIdx); |
| | | if (c == null || c.getCellType() != CellType.FORMULA) return; |
| | | c.setCellFormula(formula); |
| | | } |
| | | |
| | | /** 某年 1..limit 月城市客运累计:city → [公交客运量,公交周转量,出租客运量,出租周转量,轨道客运量,轨道周转量,轮渡客运量,轮渡周转量],含"全省" */ |
| | |
| | | return cum; |
| | | } |
| | | |
| | | /** 某年单月城市客运数据:city → 8 维数组(口径同累计),含"全省";= month 累计 - (month-1) 累计 */ |
| | | private Map<String, double[]> loadCityPassengerMonthMap(String yearPrefix, int month) { |
| | | Map<String, double[]> cum = loadCityPassengerCumulative(yearPrefix, month); |
| | | if (month <= 1) return cum; |
| | | Map<String, double[]> pre = loadCityPassengerCumulative(yearPrefix, month - 1); |
| | | Map<String, double[]> out = new HashMap<>(); |
| | | for (Map.Entry<String, double[]> e : cum.entrySet()) { |
| | | double[] prev = pre.get(e.getKey()); |
| | | double[] d = new double[8]; |
| | | for (int i = 0; i < 8; i++) { |
| | | d[i] = e.getValue()[i] - (prev == null ? 0 : prev[i]); |
| | | } |
| | | out.put(e.getKey(), d); |
| | | } |
| | | return out; |
| | | } |
| | | |
| | | // ==================== 城市客运 全省汇总(城市汇总_模板) ==================== |
| | | |
| | | /** 生成_城市客运汇总.xlsx(以 城市汇总_模板.xlsx 为底稿;2026 段填库内 1..N 月累计,同比清空,2025/2024 段保留模板历史参考值) */ |
| | |
| | | double taxiPax = prov[2], taxiTurn = prov[3]; |
| | | double railPax = prov[4], railTurn = prov[5]; |
| | | double ferryPax = prov[6], ferryTurn = prov[7]; |
| | | File template = resolveTemplate("导入模板_城市汇总.xlsx"); |
| | | File template = resolveTemplate("生成_城市客运汇总.xlsx"); |
| | | try (InputStream in = new FileInputStream(template); |
| | | XSSFWorkbook wb = new XSSFWorkbook(in)) { |
| | | Sheet sheet = wb.getSheetAt(0); |
| | |
| | | setDataCell(sheet, r, 2, 0); |
| | | setDataCell(sheet, r, 4, 0); |
| | | } |
| | | // 底部单月表(新版式):标题行11(0基10)、数据行13-19(0基12-18),填当月完成量 |
| | | Row mTitle = sheet.getRow(10); |
| | | if (mTitle != null && mTitle.getCell(0) != null) { |
| | | mTitle.getCell(0).setCellValue(year + "年" + month + "月全省完成城市客运量情况"); |
| | | } |
| | | double[] mProv = loadCityPassengerMonthMap(year + "-", month).getOrDefault("全省", new double[8]); |
| | | double mBusPax = mProv[0], mBusTurn = mProv[1]; |
| | | double mTaxiPax = mProv[2], mTaxiTurn = mProv[3]; |
| | | double mRailPax = mProv[4], mRailTurn = mProv[5]; |
| | | double mFerryPax = mProv[6], mFerryTurn = mProv[7]; |
| | | setDataCell(sheet, 12, 1, mBusPax + mTaxiPax + mRailPax + mFerryPax); // B13 总客运量 |
| | | setDataCell(sheet, 12, 3, mBusTurn + mTaxiTurn + mRailTurn + mFerryTurn); // D13 总周转量 |
| | | setDataCell(sheet, 13, 1, mBusPax); // B14 城市公交 |
| | | setDataCell(sheet, 13, 3, mBusTurn); // D14 |
| | | setDataCell(sheet, 14, 1, mTaxiPax); // B15 城市出租车 |
| | | setDataCell(sheet, 14, 3, mTaxiTurn); // D15 |
| | | setDataCell(sheet, 15, 1, mTaxiPax); // B16 其中巡游出租 |
| | | setDataCell(sheet, 15, 3, mTaxiTurn); // D16 |
| | | setZeroCell(sheet, 16, 1); // B17 城市网约车 |
| | | setZeroCell(sheet, 16, 3); // D17 |
| | | setDataCell(sheet, 17, 1, mRailPax); // B18 轨道 |
| | | setDataCell(sheet, 17, 3, mRailTurn); // D18 |
| | | setDataCell(sheet, 18, 1, mFerryPax); // B19 轮渡 |
| | | setDataCell(sheet, 18, 3, mFerryTurn);// D19 |
| | | for (int r = 12; r <= 18; r++) { // 单月表 C/E 同比:无上年单月源 → 清空样例 |
| | | setDataCell(sheet, r, 2, 0); |
| | | setDataCell(sheet, r, 4, 0); |
| | | } |
| | | recalc(wb); |
| | | clearFormulaErrorsAll(wb); // 无去年数据的同比公式 #DIV/0! → 清空 |
| | | return toBytes(wb); |
| | | } |
| | | } |
| | |
| | | addZipEntry(zos, "生成_城市公交客运量分市州明细.xlsx", exportCityBusDetail(period, mode)); |
| | | addZipEntry(zos, "生成_巡游出租客运量分市州明细.xlsx", exportCityTaxiDetail(period, mode)); |
| | | addZipEntry(zos, "生成_轨道轮渡客运量分市州明细.xlsx", exportCityRailFerryDetail(period, mode)); |
| | | addZipEntry(zos, "生成_城市客运客运量分市州累计.xlsx", exportCityPassengerCitySum("导入模板_城市分市州.xlsx", period, mode)); |
| | | addZipEntry(zos, "生成_城市客运各市州明细表.xlsx", exportCityPassengerCitySum("导入模板_城市客运各市州明细表.xlsx", period, mode)); |
| | | addZipEntry(zos, "生成_城市客运客运量分市州累计.xlsx", exportCityPassengerCitySum("生成_城市客运客运量分市州累计.xlsx", period, mode)); |
| | | addZipEntry(zos, "生成_城市客运各市州明细表.xlsx", exportCityPassengerCitySum("生成_城市客运各市州明细表.xlsx", period, mode)); |
| | | addZipEntry(zos, "生成_城市客运汇总.xlsx", exportCityPassengerSummary(period, mode)); |
| | | } |
| | | return bos.toByteArray(); |
| | |
| | | case "cityBusDetail": return exportCityBusDetail(period, mode); |
| | | case "cityTaxiDetail": return exportCityTaxiDetail(period, mode); |
| | | case "cityRailFerryDetail": return exportCityRailFerryDetail(period, mode); |
| | | case "cityPassengerCitySum": return exportCityPassengerCitySum("导入模板_城市分市州.xlsx", period, mode); |
| | | case "cityPassengerDetailSum": return exportCityPassengerCitySum("导入模板_城市客运各市州明细表.xlsx", period, mode); |
| | | case "cityPassengerCitySum": return exportCityPassengerCitySum("生成_城市客运客运量分市州累计.xlsx", period, mode); |
| | | case "cityPassengerDetailSum": return exportCityPassengerCitySum("生成_城市客运各市州明细表.xlsx", period, mode); |
| | | case "cityPassengerSummary": return exportCityPassengerSummary(period, mode); |
| | | default: return null; |
| | | } |
| | |
| | | } catch (Exception e) { |
| | | log.warn("城市客运模板公式求值失败: {}", e.getMessage()); |
| | | } |
| | | clearFormulaErrorCells(sheet, 4, sheet.getLastRowNum(), 3, 39); |
| | | fixCityMonthHeaderText(sheet); |
| | | return toBytes(wb); |
| | | } |
| | | } |
| | | |
| | | /** 清空求值结果为错误的公式单元格(模板跨簿同比公式残留 #REF! 时移除公式,避免导出显示错误) */ |
| | | private void clearFormulaErrorCells(Sheet sheet, int startRow, int endRow, int startCol, int endCol) { |
| | | for (int r = startRow; r <= endRow; r++) { |
| | | Row row = sheet.getRow(r); |
| | | if (row == null) continue; |
| | | for (int c = startCol; c <= endCol; c++) { |
| | | Cell cell = row.getCell(c); |
| | | if (cell == null || cell.getCellType() != CellType.FORMULA) continue; |
| | | try { |
| | | if (cell.getCachedFormulaResultType() == CellType.ERROR) cell.setBlank(); |
| | | } catch (Exception ignore) { |
| | | // 缓存类型读取失败时忽略 |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** 清空工作簿全部工作表内求值结果为错误的公式单元格(跨簿/跨表 #REF!、缺去年数据 #DIV/0! 等) */ |
| | | private void clearFormulaErrorsAll(XSSFWorkbook wb) { |
| | | for (int i = 0; i < wb.getNumberOfSheets(); i++) { |
| | | Sheet s = wb.getSheetAt(i); |
| | | clearFormulaErrorCells(s, 2, s.getLastRowNum(), 1, 60); |
| | | } |
| | | } |
| | | |
| | | /** 城市客运类模板表头月份列若为未格式化日期序列号(如 46082),改写为「YYYY年M月」文本 */ |
| | | private void fixCityMonthHeaderText(Sheet sheet) { |
| | | for (int r = 0; r <= Math.min(sheet.getLastRowNum(), 20); r++) { |
| | | Row row = sheet.getRow(r); |
| | | if (row == null) continue; |
| | | Cell nameCell = row.getCell(0); |
| | | String nameText = nameCell == null ? null : cellText(nameCell); |
| | | if (nameText == null || !nameText.contains("地区")) continue; |
| | | for (int c = 2; c <= 40; c += 2) { |
| | | Cell cell = row.getCell(c); |
| | | if (cell == null || cell.getCellType() != CellType.NUMERIC) continue; |
| | | double v = cell.getNumericCellValue(); |
| | | if (v < 40000 || v > 60000) continue; |
| | | java.util.Date d = org.apache.poi.ss.usermodel.DateUtil.getJavaDate(v); |
| | | cell.setCellValue(new java.text.SimpleDateFormat("yyyy年M月").format(d)); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** 模板文件定位:配置目录 → user.dir → user.dir/.. 逐级回退 */ |
| | | private File resolveTemplate(String fileName) throws Exception { |
| | | return resolveAnyTemplate(templateDir, fileName); |