From df701a309e93aff0e04d172d6c12e8dfd52092eb Mon Sep 17 00:00:00 2001
From: zhizhijie <zhizhijie@users.noreply.gitee.com>
Date: 星期三, 09 九月 2026 16:33:38 +0800
Subject: [PATCH] feat: 09-09晚 M2台账层数据驱动回填——班线包车/公交/出租/轨道轮渡读库现算写整本(7月回归1323格0差异) + 测试报告与工作日结
---
traffic-audit-server/src/main/java/com/trafficaudit/reportexport/service/ReportExportService.java | 182 ++++++++++++++++++++++++++++++---------------
1 files changed, 122 insertions(+), 60 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 2f2ebe2..fb82de4 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
@@ -29,6 +29,9 @@
import com.trafficaudit.dataimport.mapper.PassengerEnterpriseMonthlyMapper;
import com.trafficaudit.dataimport.mapper.PassengerIndividualMonthlyMapper;
import com.trafficaudit.dataimport.mapper.ScaleSplitTransportMapper;
+import com.trafficaudit.reportexport.calc.MidCalc;
+import com.trafficaudit.reportexport.calc.PaxCalc;
+import com.trafficaudit.reportexport.calc.SummaryWorkbookFiller;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
@@ -92,6 +95,8 @@
private CityBusMonthlyMapper cityBusMapper;
@Resource
private CityTaxiMonthlyMapper cityTaxiMapper;
+ @Resource
+ private SummaryWorkbookFiller summaryWorkbookFiller;
@Resource
private AuditResultMapper auditResultMapper;
@Resource
@@ -334,12 +339,7 @@
if (resolveSummaryMother(period, mode) == null) {
problems.add("docs/鐢熸垚姹囨�诲ぇ琛� 涓嬬己灏戝彲澶嶅埗鐨勬瘝鐗堟眹鎬昏〃");
} else {
- Map<Integer, Map<String, double[]>> busM = loadCityBusByMonth(yearPrefix, month);
- Map<Integer, Map<String, double[]>> taxiM = loadCityTaxiByMonth(yearPrefix, month);
- Map<String, double[]> bc = busM.get(month);
- Map<String, double[]> tc = taxiM.get(month);
- if (bc != null && bc.get("鍏ㄧ渷") != null) addSum(summary, "鍩庡競鍏氦瀹㈣繍閲忥紙鍏ㄧ渷褰撴湀锛�", bc.get("鍏ㄧ渷")[0], "涓囦汉");
- if (tc != null && tc.get("鍏ㄧ渷") != null) addSum(summary, "宸℃父鍑虹瀹㈣繍閲忥紙鍏ㄧ渷褰撴湀锛�", tc.get("鍏ㄧ渷")[0], "涓囦汉");
+ summaryWorkbookPreview(period, yearNum, month, summary, problems);
}
} catch (Exception ex) {
problems.add(ex.getMessage());
@@ -441,6 +441,118 @@
sb.append(m).append("鏈�");
}
return sb.toString();
+ }
+
+ /** 姹囨�绘暣鏈叧閿寚鏍囬瑙堬細璐ц繍閲�/鍛ㄨ浆閲忋�佸煄甯傚杩愩�佷腑鍙e緞鐨� 褰撴湀銆�1..M 绱銆佸綋鏈堝悓姣斻�佺疮璁″悓姣旓紙鍚屾瘮鍙栧簱鍐呭幓骞村悓鏈燂紝缂哄垯鏄剧ず鈥旓級 */
+ private void summaryWorkbookPreview(String period, int year, int month,
+ List<Map<String, Object>> summary, List<String> problems) {
+ String yearPrefix = year + "-";
+ // ---- 璐ц繍锛堥噺=妯℃澘_璐ц繍閲忓懆杞噺鍏ㄧ渷琛岋紱鍛ㄨ浆閲�=瑙勪笂瑙勪笅鎷嗗垎琛ㄥ叏鐪佽锛� ----
+ FreightTurnoverImport ft = loadFreightTurnover(period).get("婀栧寳鐪�");
+ Double fm = ft == null ? null : freightMonth(ft, month);
+ Double fcum = ft == null ? null : freightCum(ft, month);
+ Double fy = ft == null ? null : freightYoy(ft, month);
+ Double fcy = ft == null ? null : freightCumYoy(ft, month);
+ addKpi(summary, "鍏ㄧ渷璐ц繍閲�", fm, fcum, fy, fcy, "涓囧惃");
+ if (fm != null && fy == null) {
+ problems.add((year - 1) + "-" + String.format("%02d", month) + " 鍚屾湡璐ц繍閲忔暟鎹湭瀵煎叆搴擄紝璐ц繍閲忓綋鏈�/绱鍚屾瘮鏆備笉鏄剧ず锛屽緟 2025 骞村畾绋挎暟鎹鍏ュ悗鑷姩鍑虹幇");
+ }
+ ScaleSplitTransport tm = loadProvinceMonthMap(period).get(month);
+ ScaleSplitTransport tc = getProvinceCumulative(period);
+ Double tvM = tm == null ? null : tm.getTotalTurnover();
+ Double tvC = tc == null ? null : tc.getTotalTurnover();
+ Double tvMY = tm == null ? null : getYoyMetric(tm, "turnover", "total");
+ Double tvCY = tc == null ? null : getYoyMetric(tc, "turnover", "total");
+ addKpi(summary, "鍏ㄧ渷璐х墿鍛ㄨ浆閲�", tvM, tvC, tvMY, tvCY, "涓囧惃鍏噷");
+ if (fm == null) problems.add("妯℃澘_璐ц繍閲忓懆杞噺缂� " + period + " 褰撴湀鏁版嵁锛岃揣杩愰噺浠呰兘鎻愪緵鎴嚦鏈�杩戝鍏ユ湀鐨勭疮璁�");
+ if (tvM == null) problems.add("瑙勪笂瑙勪笅鎷嗗垎缂� " + period + " 褰撴湀鏁版嵁锛屽懆杞噺/瑙勪笂瑙勪笅褰撴湀缂猴紝绱涓烘埅鑷虫渶杩戝鍏ユ湀");
+
+ // ---- 鍩庡競瀹㈣繍锛堝叕浜�+鍑虹+杞ㄩ亾+杞浮 鍏ㄧ渷灏忚锛涙寚鏍囦綅 0/2/4/6=瀹㈣繍锛�1/3/5/7=鍛ㄨ浆锛� ----
+ double[] cumM = cityCumProv(yearPrefix, month);
+ double[] cumPrev = cityCumProv(yearPrefix, month - 1);
+ double[] monthVec = diff8(cumM, cumPrev);
+ double cityPaxM = sumIdx(monthVec, new int[]{0, 2, 4, 6});
+ double cityTurnM = sumIdx(monthVec, new int[]{1, 3, 5, 7});
+ double cityPaxC = sumIdx(cumM, new int[]{0, 2, 4, 6});
+ double cityTurnC = sumIdx(cumM, new int[]{1, 3, 5, 7});
+ double[] lastCumM = cityCumProv((year - 1) + "-", month);
+ double[] lastCumPrev = cityCumProv((year - 1) + "-", month - 1);
+ double[] lastVec = diff8(lastCumM, lastCumPrev);
+ double lastPaxM = sumIdx(lastVec, new int[]{0, 2, 4, 6});
+ double lastPaxC = sumIdx(lastCumM, new int[]{0, 2, 4, 6});
+ double lastTurnM = sumIdx(lastVec, new int[]{1, 3, 5, 7});
+ double lastTurnC = sumIdx(lastCumM, new int[]{1, 3, 5, 7});
+ addKpiRaw(summary, "鍩庡競瀹㈣繍閲�", cityPaxM, cityPaxC, lastPaxM, lastPaxC, "涓囦汉娆�");
+ addKpiRaw(summary, "鍩庡競瀹㈣繍鍛ㄨ浆閲�", cityTurnM, cityTurnC, lastTurnM, lastTurnC, "涓囦汉鍏噷");
+
+ // ---- 涓彛寰勶紙鍏矾鏃呭浼佷笟 H203-1 鍏ㄧ渷锛屽崟浣嶆崲绠� /10000锛� ----
+ Map<Integer, Map<String, PassengerAgg>> data = loadPassengerAggMap();
+ double paxM = 0.0, paxC = 0.0, turnM = 0.0, turnC = 0.0;
+ double lastPaxM2 = 0.0, lastPaxC2 = 0.0, lastTurnM2 = 0.0, lastTurnC2 = 0.0;
+ for (int m = 1; m <= month; m++) {
+ PassengerAgg agg = aggOf(data, year, m, "婀栧寳鐪�");
+ if (agg != null) {
+ double pax = agg.passengerTotal / 10000.0;
+ double turn = agg.turnoverTotal / 10000.0;
+ if (m == month) { paxM = pax; turnM = turn; }
+ paxC += pax;
+ turnC += turn;
+ }
+ PassengerAgg last = aggOf(data, year - 1, m, "婀栧寳鐪�");
+ if (last != null) {
+ double lp = last.passengerTotal / 10000.0;
+ double lt = last.turnoverTotal / 10000.0;
+ if (m == month) { lastPaxM2 = lp; lastTurnM2 = lt; }
+ lastPaxC2 += lp;
+ lastTurnC2 += lt;
+ }
+ }
+ addKpiRaw(summary, "涓彛寰勫杩愰噺", paxM, paxC, lastPaxM2, lastPaxC2, "涓囦汉娆�");
+ addKpiRaw(summary, "涓彛寰勫懆杞噺", turnM, turnC, lastTurnM2, lastTurnC2, "涓囦汉鍏噷");
+
+ if (paxM <= 0 && paxC <= 0 && cityPaxC <= 0 && fm == null) {
+ problems.add("璐ц繍閲忓懆杞噺/瑙勪笂瑙勪笅/鍏矾鏃呭/鍩庡競瀹㈣繍褰撴湀鏁版嵁鍧囩己澶�");
+ }
+ if (cityPaxC > 0 && lastPaxC <= 0) {
+ problems.add((year - 1) + "-" + String.format("%02d", month) + " 鍚屾湡鍩庡競瀹㈣繍鏁版嵁鏈鍏ュ簱锛屽煄甯傚杩愬悓姣旀殏涓嶆樉绀猴紝寰� 2025 骞村畾绋挎暟鎹鍏ュ悗鑷姩鍑虹幇");
+ }
+ if (paxC > 0 && lastPaxC2 <= 0) {
+ problems.add((year - 1) + "-" + String.format("%02d", month) + " 鍚屾湡鍏矾鏃呭鏁版嵁鏈鍏ュ簱锛屼腑鍙e緞鍚屾瘮鏆備笉鏄剧ず锛屽緟 2025 骞村畾绋挎暟鎹鍏ュ悗鑷姩鍑虹幇");
+ }
+ }
+
+ /** 鍩庡競瀹㈣繍鍏ㄧ渷绱鍚戦噺锛�8 鎸囨爣锛氬叕浜ゅ杩�/鍛ㄨ浆銆佸嚭绉熷杩�/鍛ㄨ浆銆佽建閬撳杩�/鍛ㄨ浆銆佽疆娓″杩�/鍛ㄨ浆锛� */
+ private double[] cityCumProv(String yearPrefix, int month) {
+ if (month <= 0) return new double[8];
+ return loadCityPassengerCumulative(yearPrefix, month).getOrDefault("鍏ㄧ渷", new double[8]);
+ }
+
+ private double[] diff8(double[] a, double[] b) {
+ double[] out = new double[8];
+ for (int i = 0; i < 8; i++) out[i] = a[i] - b[i];
+ return out;
+ }
+
+ private double sumIdx(double[] arr, int[] idx) {
+ double sum = 0.0;
+ for (int i : idx) sum += (i < arr.length ? arr[i] : 0.0);
+ return sum;
+ }
+
+ /** 鐧惧垎姣斿悓姣旓紙鍊煎凡涓烘瘮鐜� 0.xx锛夛細杈撳嚭鍥涜 褰撴湀/绱/褰撴湀鍚屾瘮/绱鍚屾瘮 */
+ private void addKpi(List<Map<String, Object>> summary, String name, Double m, Double cum, Double yoyM, Double yoyC, String unit) {
+ if (m != null) addSum(summary, name + "路褰撴湀", round(m, 2), unit);
+ if (cum != null) addSum(summary, name + "路绱", round(cum, 2), unit);
+ if (yoyM != null) addSum(summary, name + "路褰撴湀鍚屾瘮", round(yoyM * 100.0, 1), "%");
+ if (yoyC != null) addSum(summary, name + "路绱鍚屾瘮", round(yoyC * 100.0, 1), "%");
+ }
+
+ /** 鍘熷鏁板�肩増锛堟湀/绱涓� 0 瑙嗕负鏃犳暟鎹笉鍑鸿锛� */
+ private void addKpiRaw(List<Map<String, Object>> summary, String name, double m, double cum, double lastM, double lastC, String unit) {
+ if (m > 0) addSum(summary, name + "路褰撴湀", round(m, 2), unit);
+ if (cum > 0) addSum(summary, name + "路绱", round(cum, 2), unit);
+ if (m > 0 && lastM > 0) addSum(summary, name + "路褰撴湀鍚屾瘮", round((m - lastM) / lastM * 100.0, 1), "%");
+ if (cum > 0 && lastC > 0) addSum(summary, name + "路绱鍚屾瘮", round((cum - lastC) / lastC * 100.0, 1), "%");
}
private void addSum(List<Map<String, Object>> summary, String label, Double value, String unit) {
@@ -3233,8 +3345,11 @@
}
dynamicSummaryYear(wb, year);
cleanSummarySheetPresentation(wb); // 鍙栨秷鍚勯〉绛涢�夈�佽繕鍘熼殣钘忚锛堝惁鍒欌�滅彮绾垮寘杞︹�濈瓑椤电湅璧锋潵鍍忕己鏁版嵁锛�
+ int effMonth = (toMonth != null && toMonth > 0 && toMonth < month) ? toMonth : month;
+ List<String> dbProblems = new ArrayList<>();
+ int dbFilled = summaryWorkbookFiller.fillMonthlyLedger(wb, year, effMonth, dbProblems);
+ log.info("姹囨�诲伐浣滅翱鏁版嵁椹卞姩鍥炲~瀹屾垚锛氬啓鍏ュ彴璐﹂〉褰撴湀鍊兼牸鏁�={}锛岀己鏁版彁绀�={}", dbFilled, dbProblems);
applyTwoDecimalFormat(wb); // 鏁板�兼樉绀轰袱浣嶅皬鏁帮紙淇濈暀鍏ㄧ簿搴︼級锛�% / 鏃ユ湡绛夋棦鏈夋牱寮忎笉鏀瑰彉
- autoFitContentColumns(wb); // 鎸夋暟鎹嚜鍔ㄥ姞瀹藉垪瀹斤紙鍙姞瀹戒笉缂╃獎锛岃烦杩囧悎骞舵爣棰樻牸锛岄槻闀挎枃鏈妸鍗曞垪鎾戠垎锛�
if (!wb.getCTWorkbook().isSetCalcPr()) wb.getCTWorkbook().addNewCalcPr();
wb.getCTWorkbook().getCalcPr().setFullCalcOnLoad(true); // 杩樺師/鏀瑰啓鍏紡鍚庢墦寮�鍗抽噸绠�
return toBytes(wb);
@@ -4822,59 +4937,6 @@
}
}
- private void autoFitContentColumns(org.apache.poi.ss.usermodel.Workbook wb) {
- if (wb == null) return;
- org.apache.poi.ss.usermodel.DataFormatter dfmt = new org.apache.poi.ss.usermodel.DataFormatter();
- for (int s = 0; s < wb.getNumberOfSheets(); s++) {
- org.apache.poi.ss.usermodel.Sheet sh = wb.getSheetAt(s);
- if (sh == null) continue;
- java.util.Set<String> merged = new java.util.HashSet<>();
- for (org.apache.poi.ss.util.CellRangeAddress ra : sh.getMergedRegions()) {
- for (int r = ra.getFirstRow(); r <= ra.getLastRow(); r++) {
- for (int c = ra.getFirstColumn(); c <= ra.getLastColumn(); c++) {
- merged.add(r + ":" + c);
- }
- }
- }
- int maxCol = -1;
- for (org.apache.poi.ss.usermodel.Row row : sh) {
- if (row == null) continue;
- maxCol = Math.max(maxCol, (int) row.getLastCellNum() - 1);
- }
- if (maxCol < 0) continue;
- int[] need = new int[maxCol + 1];
- for (org.apache.poi.ss.usermodel.Row row : sh) {
- if (row == null) continue;
- for (org.apache.poi.ss.usermodel.Cell cell : row) {
- int c = cell.getColumnIndex();
- if (c > maxCol || merged.contains(row.getRowNum() + ":" + c)) continue;
- String txt;
- try {
- txt = dfmt.formatCellValue(cell);
- } catch (Exception ignore) {
- continue;
- }
- if (txt == null || txt.isEmpty()) continue;
- int units = 0;
- for (int i = 0; i < txt.length(); i++) {
- units += isWideChar(txt.charAt(i)) ? 2 : 1;
- }
- if (units > need[c]) need[c] = Math.min(units, 34); // 闀挎枃鏈笉鎶婂崟鍒楁拺鐖�
- }
- }
- for (int c = 0; c <= maxCol; c++) {
- if (need[c] <= 0) continue;
- int target = Math.min(need[c] + 2, 46) * 256;
- if (target > sh.getColumnWidth(c)) sh.setColumnWidth(c, target);
- }
- }
- }
-
- /** CJK / 鍏ㄨ瀛楃鎸変袱鍊嶅搴﹁锛堣繎浼� Excel 鏄剧ず鍒楀锛� */
- private boolean isWideChar(char ch) {
- return (ch >= 0x2E80 && ch <= 0x9FFF) || (ch >= 0xF900 && ch <= 0xFAFF)
- || (ch >= 0xFF00 && ch <= 0xFFEF);
- }
private byte[] toBytes(XSSFWorkbook wb) throws Exception {
applyTwoDecimalFormat(wb);
--
Gitblit v1.9.1