From 573e11132bd4a404934c03f9fd0802200e2ccd30 Mon Sep 17 00:00:00 2001
From: zhizhijie <zhizhijie@users.noreply.gitee.com>
Date: 星期四, 03 九月 2026 08:59:57 +0800
Subject: [PATCH] feat: 报表新版式(上累计+下当月)与导入校验增强、I004 保护 + 09-02 测试文档
---
traffic-audit-server/src/main/java/com/trafficaudit/reportexport/service/ReportExportService.java | 300 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
1 files changed, 285 insertions(+), 15 deletions(-)
diff --git a/traffic-audit-server/src/main/java/com/trafficaudit/reportexport/service/ReportExportService.java b/traffic-audit-server/src/main/java/com/trafficaudit/reportexport/service/ReportExportService.java
index 945a762..c1c3bd1 100644
--- a/traffic-audit-server/src/main/java/com/trafficaudit/reportexport/service/ReportExportService.java
+++ b/traffic-audit-server/src/main/java/com/trafficaudit/reportexport/service/ReportExportService.java
@@ -665,17 +665,58 @@
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) {
@@ -693,15 +734,27 @@
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 涓哄簳绋匡細淇濈暀琛ㄥご/鍚堝苟/鍒楀/鏍峰紡锛屼粎鏇挎崲鏁版嵁锛決/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);
}
}
@@ -994,6 +1047,31 @@
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,
@@ -1163,9 +1241,12 @@
/** 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());
@@ -1178,9 +1259,11 @@
/** 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();
@@ -1945,6 +2028,7 @@
// 绱鍚屾瘮鍒楋紙妯℃澘 #REF! 鈫� 搴撳唴鍘诲勾 1..N 鏈堢疮璁″悓姣旀暟鍊硷級
fillMidCumYoyColumn(sheet, mid, currentYear, fillMonths, extended ? 27 : 15);
recalc(wb);
+ clearFormulaErrorsAll(wb); // 鍘诲勾鍚屾湡鏄庣粏椤垫畫鐣� #REF! 鍚屾瘮鍏紡 鈫� 娓呯┖
return toBytes(wb);
}
}
@@ -2195,11 +2279,20 @@
title.getCell(0).setCellValue(currentYear + "骞�1-" + maxMonth + "鏈堝叏鐪佸垎甯傚窞绱瀹屾垚閬撹矾瀹㈣繍鐢熶骇鎯呭喌");
}
}
+ // 鍙冲潡锛堝綋鏈堬級鏍囬 K1
+ if (title != null && title.getCell(10) != null) {
+ title.getCell(10).setCellValue(currentYear + "骞�" + maxMonth + "鏈堝叏鐪佸垎甯傚窞瀹屾垚閬撹矾瀹㈣繍鐢熶骇鎯呭喌");
+ }
// 鍏ㄧ渷琛� r3锛欱3/F3 涓� SUM 鍏紡淇濈暀锛汥3/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锛欱/F 绱鍊笺�丏/H 鍚屾瘮锛汣/E/G/I 鎺掑悕鍏紡淇濈暀
List<String> cities = RegionUtil.CITY_LIST;
for (int i = 0; i < cities.size(); i++) {
@@ -2213,6 +2306,15 @@
setValOrBlank(sheet, r0, 3, yoyOf(pass, lastPass));
setValOrBlank(sheet, r0, 5, turn == 0 ? null : round(turn, 4));
setValOrBlank(sheet, r0, 7, yoyOf(turn, lastTurn));
+ // 鍙冲潡褰撴湀锛歀/P 鍊笺�丯/R 褰撴湀鍚屾瘮锛圡/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);
@@ -2277,6 +2379,36 @@
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 + "鏈堜腑鍙e緞瀹㈣繍閲忓強鍛ㄨ浆閲�");
+ }
+ 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锛夛紝鏀瑰啓涓烘湰鍧楁�昏锛圔$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);
@@ -2372,6 +2504,7 @@
fillEnergyTemplateSheet(s, agg);
}
recalc(wb);
+ clearFormulaErrorsAll(wb); // 鏃犲幓骞存暟鎹殑鍚屾瘮/澧為�熷叕寮� #DIV/0! 鈫� 娓呯┖
return toBytes(wb);
}
}
@@ -2508,7 +2641,7 @@
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));
}
@@ -2562,7 +2695,7 @@
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));
}
@@ -2606,7 +2739,7 @@
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);
@@ -2616,6 +2749,8 @@
// 杞浮锛氬杩愰噺琛� 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);
}
}
@@ -2704,6 +2839,7 @@
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)) {
@@ -2712,9 +2848,24 @@
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锛�
+ // 褰撴湀鍧楁帓鍚�/鍗犳瘮鍏紡鐢辩疮璁″潡澶嶅埗鑰屾潵锛孯ANK 鑼冨洿涓庡崰姣斿垎姣嶉渶鏀规寚褰撴湀鍧楄嚜韬
+ repairCityMonthRankShare(sheet, 50, 66, 49);
+ repairCityMonthRankShare(sheet, 72, 88, 71);
recalc(wb);
+ clearFormulaErrorsAll(wb); // 鏃犲幓骞寸疮璁$殑鍗犳瘮/澧為�熷叕寮� #DIV/0! 鈫� 娓呯┖
return toBytes(wb);
}
}
@@ -2744,6 +2895,33 @@
setDataCell(sheet, rowIdx, 24, 0);
rowIdx++;
}
+ }
+
+ /** 褰撴湀鍧楁帓鍚�/鍗犳瘮鍏紡淇锛氭ā鏉垮綋鏈堝潡鐢辩疮璁″潡澶嶅埗鑰屾潵锛孯ANK 鑼冨洿涓庡崰姣斿垎姣嶉渶鏀规寚褰撴湀鍧楄嚜韬 */
+ 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 鈫� [鍏氦瀹㈣繍閲�,鍏氦鍛ㄨ浆閲�,鍑虹瀹㈣繍閲�,鍑虹鍛ㄨ浆閲�,杞ㄩ亾瀹㈣繍閲�,杞ㄩ亾鍛ㄨ浆閲�,杞浮瀹㈣繍閲�,杞浮鍛ㄨ浆閲廬锛屽惈"鍏ㄧ渷" */
@@ -2784,6 +2962,23 @@
return cum;
}
+ /** 鏌愬勾鍗曟湀鍩庡競瀹㈣繍鏁版嵁锛歝ity 鈫� 8 缁存暟缁勶紙鍙e緞鍚岀疮璁★級锛屽惈"鍏ㄧ渷"锛�= 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 娈典繚鐣欐ā鏉垮巻鍙插弬鑰冨�硷級 */
@@ -2795,7 +2990,7 @@
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);
@@ -2819,7 +3014,36 @@
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);
}
}
@@ -2833,8 +3057,8 @@
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();
@@ -2911,8 +3135,8 @@
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;
}
@@ -2996,10 +3220,56 @@
} 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锛夛紝鏀瑰啓涓恒�孻YYY骞碝鏈堛�嶆枃鏈� */
+ 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骞碝鏈�").format(d));
+ }
+ }
+ }
+
/** 妯℃澘鏂囦欢瀹氫綅锛氶厤缃洰褰� 鈫� user.dir 鈫� user.dir/.. 閫愮骇鍥為�� */
private File resolveTemplate(String fileName) throws Exception {
return resolveAnyTemplate(templateDir, fileName);
--
Gitblit v1.9.1