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/dataimport/service/DataImportService.java | 290 +++++++++++++++++++++++++++++++++++++++++++++++-----------
1 files changed, 235 insertions(+), 55 deletions(-)
diff --git a/traffic-audit-server/src/main/java/com/trafficaudit/dataimport/service/DataImportService.java b/traffic-audit-server/src/main/java/com/trafficaudit/dataimport/service/DataImportService.java
index 41b922c..50b64f8 100644
--- a/traffic-audit-server/src/main/java/com/trafficaudit/dataimport/service/DataImportService.java
+++ b/traffic-audit-server/src/main/java/com/trafficaudit/dataimport/service/DataImportService.java
@@ -407,41 +407,20 @@
- /** 鍏矾鏃呭涓綋瀹㈣繍閲�/鍛ㄨ浆閲忥紙浼佷笟绾夸笅姹囨姤鏁版嵁锛屾寜鎵�灞炲湴鍖哄鍏ワ紝渚涘垎甯傚窞琛ㄤ釜浣撹濉厖锛� */
+ /** 鍏矾鏃呭涓綋瀹㈣繍閲�/鍛ㄨ浆閲忥紙瀵煎叆鍚庢寜甯傚窞姹囨�诲~鍏呭垎甯傚窞琛ㄤ釜浣撹锛夛紝鏀寔涓ょ妯℃澘锛�
+ * 鈶� 浼佷笟绾夸笅姹囨姤鏄庣粏锛堣〃澶村惈銆屼紒涓氬悕绉般�嶏級锛氶�愪紒涓氬叆搴擄紱
+ * 鈶� 鐪佸巺甯傚窞姹囨�昏〃銆婃箹鍖楃渷鍏矾鏃呭杩愯緭涓綋鎴锋湀搴︾敓浜ф儏鍐碉紙YYYY.M锛夈�嬶細
+ * sheet 鍐呬负銆屽叏鐪丯鏈堜釜浣撳杩愰噺姹囨�昏〃銆嶆爣棰樺潡 + 銆屽競宸�/涓綋杞﹁締鏁�/鈥�/涓綋瀹㈣繍閲�/涓綋鏃呭鍛ㄨ浆閲忋�嶈〃澶达紝
+ * 鎸夊競宸為�愯鍏ュ簱锛堝悓涓�甯傚窞澶氳濡� 鍘垮唴鐝嚎+璺ㄥ幙鐝嚎 鍧囧叆搴擄紝鎶ヨ〃渚ф寜甯傚窞鑷姩姹囨�伙級銆� */
public ImportResult importPassengerIndividual(MultipartFile file, String period) throws Exception {
List<PassengerIndividualMonthly> rows = new ArrayList<>();
try (Workbook wb = WorkbookFactory.create(file.getInputStream())) {
Sheet sheet = wb.getSheetAt(0);
Map<String, Integer> colMap = buildHeaderMap(sheet.getRow(0));
- Integer codeCol = colMap.get("鎵�灞炲湴鍖�");
- Integer entCol = colMap.get("浼佷笟浠g爜");
- Integer nameCol = colMap.get("浼佷笟鍚嶇О");
- Integer passCol = null;
- Integer turnCol = null;
- for (Map.Entry<String, Integer> e : colMap.entrySet()) {
- String k = e.getKey();
- if (k != null && k.contains("涓綋") && k.contains("瀹㈣繍閲�")) passCol = e.getValue();
- if (k != null && k.contains("涓綋") && (k.contains("鍛ㄨ浆閲�") || k.contains("鍛ㄨ浆"))) turnCol = e.getValue();
- }
- if (passCol == null && turnCol == null) {
- throw new RuntimeException("鏈壘鍒般�屼釜浣撳杩愰噺/涓綋鏃呭鍛ㄨ浆閲忋�嶅垪锛岃妫�鏌ユā鏉匡紙鍒楀悕闇�鍖呭惈鈥滀釜浣撳杩愰噺鈥濃�滀釜浣撳懆杞噺鈥濓級");
- }
- for (int r = 1; r <= sheet.getLastRowNum(); r++) {
- Row row = sheet.getRow(r);
- if (row == null) continue;
- String name = nameCol != null ? getString(row, nameCol) : null;
- if (name == null || name.trim().isEmpty()) continue;
- Double pass = passCol != null ? getDecimalOrNull(row, passCol) : null;
- Double turn = turnCol != null ? getDecimalOrNull(row, turnCol) : null;
- if ((pass == null || pass == 0.0) && (turn == null || turn == 0.0)) continue;
- PassengerIndividualMonthly entity = new PassengerIndividualMonthly();
- entity.setReportPeriod(period);
- entity.setRegionCode(codeCol != null ? getString(row, codeCol) : null);
- entity.setEnterpriseCode(entCol != null ? getString(row, entCol) : null);
- entity.setEnterpriseName(name.trim());
- entity.setPassengerCount(pass);
- entity.setTurnover(turn);
- rows.add(entity);
+ if (colMap.containsKey("浼佷笟鍚嶇О")) {
+ parsePassengerIndividualEnterprise(sheet, colMap, period, rows);
+ } else {
+ parsePassengerIndividualCitySheet(sheet, period, rows);
}
} catch (Exception e) {
log.error("Passenger individual parse error", e);
@@ -474,6 +453,101 @@
recordBatch(file.getOriginalFilename(), "PASSENGER_INDIVIDUAL", period, rows.size(), success, failDetails.size(), errorDetail);
log.info("Passenger individual imported: {} rows for {}", success, period);
return new ImportResult(success, failDetails.size(), failDetails);
+ }
+
+ /** 鏃х増涓綋瀹㈣繍閲忔ā鏉匡紙浼佷笟绾夸笅姹囨姤鏄庣粏锛氭墍灞炲湴鍖�/浼佷笟浠g爜/浼佷笟鍚嶇О/鈥︿釜浣撳杩愰噺/鈥︿釜浣撴梾瀹㈠懆杞噺锛� */
+ private void parsePassengerIndividualEnterprise(Sheet sheet, Map<String, Integer> colMap, String period,
+ List<PassengerIndividualMonthly> rows) {
+ Integer codeCol = colMap.get("鎵�灞炲湴鍖�");
+ Integer entCol = colMap.get("浼佷笟浠g爜");
+ Integer nameCol = colMap.get("浼佷笟鍚嶇О");
+ Integer passCol = null;
+ Integer turnCol = null;
+ for (Map.Entry<String, Integer> e : colMap.entrySet()) {
+ String k = e.getKey();
+ if (k != null && k.contains("涓綋") && k.contains("瀹㈣繍閲�")) passCol = e.getValue();
+ if (k != null && k.contains("涓綋") && (k.contains("鍛ㄨ浆閲�") || k.contains("鍛ㄨ浆"))) turnCol = e.getValue();
+ }
+ if (passCol == null && turnCol == null) {
+ throw new RuntimeException("鏈壘鍒般�屼釜浣撳杩愰噺/涓綋鏃呭鍛ㄨ浆閲忋�嶅垪锛岃妫�鏌ユā鏉匡紙鍒楀悕闇�鍖呭惈鈥滀釜浣撳杩愰噺鈥濃�滀釜浣撳懆杞噺鈥濓級");
+ }
+ for (int r = 1; r <= sheet.getLastRowNum(); r++) {
+ Row row = sheet.getRow(r);
+ if (row == null) continue;
+ String name = nameCol != null ? getString(row, nameCol) : null;
+ if (name == null || name.trim().isEmpty()) continue;
+ Double pass = passCol != null ? getDecimalOrNull(row, passCol) : null;
+ Double turn = turnCol != null ? getDecimalOrNull(row, turnCol) : null;
+ if ((pass == null || pass == 0.0) && (turn == null || turn == 0.0)) continue;
+ PassengerIndividualMonthly entity = new PassengerIndividualMonthly();
+ entity.setReportPeriod(period);
+ entity.setRegionCode(codeCol != null ? getString(row, codeCol) : null);
+ entity.setEnterpriseCode(entCol != null ? getString(row, entCol) : null);
+ entity.setEnterpriseName(name.trim());
+ entity.setPassengerCount(pass);
+ entity.setTurnover(turn);
+ rows.add(entity);
+ }
+ }
+
+ /** 鏂扮増甯傚窞姹囨�绘ā鏉匡細銆婂叏鐪丯鏈堜釜浣撳杩愰噺姹囨�昏〃銆嬫爣棰樿 鈫� 琛ㄥご琛岋紙甯傚窞/鈥�/涓綋瀹㈣繍閲�/涓綋鏃呭鍛ㄨ浆閲忥級鈫� 甯傚窞鏁版嵁琛� */
+ private void parsePassengerIndividualCitySheet(Sheet sheet, String period, List<PassengerIndividualMonthly> rows) {
+ int targetMonth;
+ try {
+ targetMonth = Integer.parseInt(period.split("-")[1]);
+ } catch (Exception e) {
+ throw new RuntimeException("鎶ヨ〃鏈熸牸寮忓簲涓� yyyy-MM锛�" + period);
+ }
+ int titleRow = -1;
+ for (int r = 0; r <= sheet.getLastRowNum(); r++) {
+ Row row = sheet.getRow(r);
+ if (row == null) continue;
+ String t = getString(row, 0);
+ if (t == null || !t.contains("鍏ㄧ渷") || !t.contains("鏈�")) continue;
+ java.util.regex.Matcher m = java.util.regex.Pattern.compile("鍏ㄧ渷(\\d+)鏈�").matcher(t);
+ if (!m.find()) continue;
+ if (Integer.parseInt(m.group(1)) == targetMonth) { titleRow = r; break; }
+ if (titleRow < 0) titleRow = r; // 鏍囬涓嶅惈鐩爣鏈堜唤鏃舵殏璁扮涓�涓爣棰樺潡鍏滃簳
+ }
+ if (titleRow < 0) {
+ throw new RuntimeException("鏈壘鍒般�屽叏鐪丯鏈堜釜浣撳杩愰噺姹囨�昏〃銆嶆爣棰樺潡锛岃浣跨敤銆婃ā鏉縚婀栧寳鐪佸叕璺梾瀹㈣繍杈撲釜浣撴埛鏈堝害鐢熶骇鎯呭喌锛圷YYY.M锛夈�嬫暣鐞嗗悗瀵煎叆");
+ }
+ Row header = sheet.getRow(titleRow + 1);
+ if (header == null) {
+ throw new RuntimeException("銆屽叏鐪�" + targetMonth + "鏈堚�︽眹鎬昏〃銆嶆爣棰樺悗缂哄皯琛ㄥご琛岋紙甯傚窞/涓綋瀹㈣繍閲�/涓綋鏃呭鍛ㄨ浆閲忥級");
+ }
+ int cityCol = -1, passCol = -1, turnCol = -1;
+ for (Cell c : header) {
+ String h = FORMATTER.formatCellValue(c).trim();
+ if (h.isEmpty()) continue;
+ if (h.contains("甯傚窞")) cityCol = c.getColumnIndex();
+ else if (h.contains("涓綋") && h.contains("瀹㈣繍閲�")) passCol = c.getColumnIndex();
+ else if (h.contains("涓綋") && (h.contains("鍛ㄨ浆閲�") || h.contains("鍛ㄨ浆"))) turnCol = c.getColumnIndex();
+ }
+ if (cityCol < 0 || (passCol < 0 && turnCol < 0)) {
+ throw new RuntimeException("鏈壘鍒般�屽競宸� / 涓綋瀹㈣繍閲� / 涓綋鏃呭鍛ㄨ浆閲忋�嶅垪锛岃浣跨敤绯荤粺妯℃澘鏁寸悊鍚庡鍏�");
+ }
+ int lastRow = Math.min(sheet.getLastRowNum(), titleRow + 40);
+ for (int r = titleRow + 2; r <= lastRow; r++) {
+ Row row = sheet.getRow(r);
+ if (row == null) continue;
+ String first = getString(row, cityCol);
+ if (first == null) break;
+ String t = first.trim();
+ if (t.isEmpty() || t.contains("鍚堣") || t.contains("鐜瘮") || t.contains("鍏ㄧ渷")) break;
+ String city = RegionUtil.normalizeCityName(t);
+ if (!RegionUtil.cityList().contains(city)) continue;
+ Double pass = passCol >= 0 ? getDecimalOrNull(row, passCol) : null;
+ Double turn = turnCol >= 0 ? getDecimalOrNull(row, turnCol) : null;
+ if ((pass == null || pass == 0.0) && (turn == null || turn == 0.0)) continue;
+ PassengerIndividualMonthly entity = new PassengerIndividualMonthly();
+ entity.setReportPeriod(period);
+ entity.setRegionCode(RegionUtil.codeOfCity(city));
+ entity.setEnterpriseName(city);
+ entity.setPassengerCount(pass);
+ entity.setTurnover(turn);
+ rows.add(entity);
+ }
}
/** 鍏矾鏃呭杩愭斂杞﹁締鏁帮紙鐙珛妯℃澘锛氫紒涓氫唬鐮�/浼佷笟鍚嶇О/杞﹁締鏁�/杞藉浣嶆暟锛� */
@@ -531,6 +605,10 @@
/** 鑳借�楁槑缁嗘瘡缁勫浐瀹�18鍒楋紝杞︾墝鍙疯捣濮嬪垪锛�0-based锛夛細3,21,39,57,75 */
private static final int[] ENERGY_GROUP_COLS = {3, 21, 39, 57, 75};
+ /** 鑳借�楁槑缁嗘ā鏉胯〃澶村叧閿垪锛�0-based锛夛細绋嬪簭鎸夊浐瀹氬垪浣嶈В鏋愶紝瑙f瀽鍓嶆牎楠岃〃澶达紝閿欎綅鐩存帴鎷掔粷 */
+ private static final int[] ENERGY_HEADER_COLS = {3, 4, 5, 21, 39, 57, 75};
+ private static final String[] ENERGY_HEADER_KEYWORDS = {"杞︾墝鍙�", "杞﹁締绫诲瀷", "鐕冩枡绫诲瀷", "杞︾墝鍙�", "杞︾墝鍙�", "杞︾墝鍙�", "杞︾墝鍙�"};
+
/** 鑳借�楀鍏ユ寜鎶ヨ〃鏈熶簰鏂ラ攣锛圚204 鏄庣粏涓庤繍鏀夸俊鎭叡鐢紝閬垮厤鍚屾姤琛ㄦ湡骞跺彂閲嶅浜掔浉瑕嗙洊锛� */
private Object energyLock(String period) {
return energyImportLocks.computeIfAbsent(period == null ? "" : period, k -> new Object());
@@ -543,6 +621,7 @@
// sheet0: 绗簩瀛e害璐ц溅鑳借�楁槑缁嗭紙姣忚宸﹀彸鍚�5缁勮溅杈嗭紝姣忕粍鍥哄畾18鍒楋級
Sheet sheet = wb.getSheetAt(0);
requireHeaderKeyword(sheet, "閬撹矾璐ц繍杞﹁締鑳芥簮娑堣�楁儏鍐�", "浼佷笟鍚嶇О", "杞︾墝鍙�");
+ requireEnergyHeaderLayout(sheet);
for (int r = 1; r <= sheet.getLastRowNum(); r++) {
Row row = sheet.getRow(r);
if (row == null) continue;
@@ -665,6 +744,12 @@
try (Workbook wb = WorkbookFactory.create(file.getInputStream())) {
Sheet sheet = wb.getSheetAt(0);
requireHeaderKeyword(sheet, "鑳借�楄溅杈嗚繍鏀夸俊鎭�", "杞︾墝鍙�");
+ Row authHeader = sheet.getRow(0);
+ String authH0 = authHeader == null ? null : FORMATTER.formatCellValue(authHeader.getCell(0)).trim();
+ if (authH0 == null || authH0.isEmpty() || !authH0.contains("杞︾墝鍙�")) {
+ throw new RuntimeException("鑳借�楄溅杈嗚繍鏀夸俊鎭〃澶翠笌绯荤粺妯℃澘涓嶄竴鑷达細绗� 1 鍒楀簲涓恒�岃溅鐗屽彿銆嶏紝瀹為檯涓恒��"
+ + (authH0 == null || authH0.isEmpty() ? "绌�" : authH0) + "銆嶃�傝浣跨敤绯荤粺妯℃澘銆婃ā鏉縚鑳借�楄溅杈嗚繍鏀夸俊鎭�.xlsx銆嬫暣鐞嗗悗鍐嶅鍏�");
+ }
for (int r = 1; r <= sheet.getLastRowNum(); r++) {
Row row = sheet.getRow(r);
if (row == null) continue;
@@ -730,25 +815,27 @@
// ========== 杩愭斂杞﹁締 ==========
+ /** 杩愭斂杞﹁締瀵煎叆锛氫笌 H203-2 鏈堟姤涓�鑷达紝鎸夎〃澶村悕绉板畾浣嶅垪锛屽垪椤哄簭鍙樺寲涔熻兘姝g‘瀵煎叆 */
public int importTransportAuth(MultipartFile file, String period) throws Exception {
List<TransportAuthVehicle> list = new ArrayList<>();
int failRows = 0;
try (Workbook wb = WorkbookFactory.create(file.getInputStream())) {
Sheet sheet = wb.getSheetAt(0);
- requireHeaderKeyword(sheet, "杩愭斂杞﹁締", "浼佷笟鍚嶇О");
+ requireHeaderCols(sheet, "杩愭斂杞﹁締", "浼佷笟鍚嶇О", "鎬荤壍寮曡溅鏁�", "鎬绘寕杞︽暟", "鍏跺畠杞﹁締鏁�", "鎸傝溅鎬诲惃浣�(鍚�)", "鍏跺畠杞﹁締鎬诲惃浣�(鍚�)");
+ Map<String, Integer> colMap = buildHeaderMap(sheet.getRow(0));
for (int r = 1; r <= sheet.getLastRowNum(); r++) {
Row row = sheet.getRow(r);
if (row == null) continue;
- String name = getString(row, 0);
+ String name = gs(row, colMap, "浼佷笟鍚嶇О");
if (name == null || name.trim().isEmpty()) continue;
TransportAuthVehicle entity = new TransportAuthVehicle();
entity.setReportPeriod(period);
entity.setEnterpriseName(name);
- entity.setTractorCount(getInt(row, 1));
- entity.setTrailerCount(getInt(row, 2));
- entity.setOtherCount(getInt(row, 3));
- entity.setTrailerTons(getDecimal(row, 4));
- entity.setOtherTons(getDecimal(row, 5));
+ entity.setTractorCount(gi(row, colMap, "鎬荤壍寮曡溅鏁�"));
+ entity.setTrailerCount(gi(row, colMap, "鎬绘寕杞︽暟"));
+ entity.setOtherCount(gi(row, colMap, "鍏跺畠杞﹁締鏁�"));
+ entity.setTrailerTons(gd(row, colMap, "鎸傝溅鎬诲惃浣�(鍚�)"));
+ entity.setOtherTons(gd(row, colMap, "鍏跺畠杞﹁締鎬诲惃浣�(鍚�)"));
list.add(entity);
}
} catch (Exception e) {
@@ -778,21 +865,23 @@
// ========== 杞ㄨ抗閲岀▼ ==========
+ /** 杞ㄨ抗閲岀▼瀵煎叆锛氫笌 H203-2 鏈堟姤涓�鑷达紝鎸夎〃澶村悕绉板畾浣嶅垪锛屽垪椤哄簭鍙樺寲涔熻兘姝g‘瀵煎叆 */
public int importTrackMileage(MultipartFile file, String period) throws Exception {
List<VehicleTrackMileage> list = new ArrayList<>();
int failRows = 0;
try (Workbook wb = WorkbookFactory.create(file.getInputStream())) {
Sheet sheet = wb.getSheetAt(0);
- requireHeaderKeyword(sheet, "杞ㄨ抗閲岀▼", "浼佷笟鍚嶇О");
+ requireHeaderCols(sheet, "杞ㄨ抗閲岀▼", "浼佷笟鍚嶇О", "鏈堝害鎬婚噷绋�(鍏噷)", "鏈夐噷绋嬬殑杞﹁締鏁�");
+ Map<String, Integer> colMap = buildHeaderMap(sheet.getRow(0));
for (int r = 1; r <= sheet.getLastRowNum(); r++) {
Row row = sheet.getRow(r);
if (row == null) continue;
- String name = getString(row, 1);
+ String name = gs(row, colMap, "浼佷笟鍚嶇О");
if (name == null || name.trim().isEmpty()) continue;
VehicleTrackMileage entity = new VehicleTrackMileage();
entity.setEnterpriseName(name);
- entity.setMonthlyMileage(getDecimal(row, 2));
- entity.setTrackedVehicles(getInt(row, 3));
+ entity.setMonthlyMileage(gd(row, colMap, "鏈堝害鎬婚噷绋�(鍏噷)"));
+ entity.setTrackedVehicles(gi(row, colMap, "鏈夐噷绋嬬殑杞﹁締鏁�"));
entity.setReportPeriod(period);
list.add(entity);
}
@@ -842,6 +931,20 @@
if (sheetName.contains("褰撴湀")) periodType = "MONTH";
else if (sheetName.contains("绱")) periodType = "CUMULATIVE";
if (periodType == null) continue;
+
+ // 鏍¢獙 sheet 甯冨眬锛氱2琛屽簲鍚�屽競宸�/瑙勪笂浼佷笟杩愯緭閲忋�嶏紝绗�3琛屽瓙琛ㄥご搴斿惈銆屽懆杞噺銆嶆垨銆岃揣杩愰噺銆嶏紙鎵╁睍甯冨眬锛�
+ Row headRow = sheet.getRow(1);
+ Row subHeadRow = sheet.getRow(2);
+ boolean layoutOk = headRow != null
+ && getString(headRow, 0) != null && getString(headRow, 0).contains("甯傚窞")
+ && getString(headRow, 1) != null && getString(headRow, 1).contains("瑙勪笂")
+ && subHeadRow != null
+ && ("璐ц繍閲�".equals(getString(subHeadRow, 0))
+ || (getString(subHeadRow, 1) != null && getString(subHeadRow, 1).contains("鍛ㄨ浆閲�")));
+ if (!layoutOk) {
+ throw new RuntimeException("鎵�閫夋暟鎹被鍨嬨�愯涓婅涓嬫媶鍒嗐�戜笌鏂囦欢鍐呭涓嶇锛歴heet銆�" + sheetName
+ + "銆嶆湭鎵惧埌銆屽競宸�/瑙勪笂浼佷笟杩愯緭閲�/鍛ㄨ浆閲忋�嶈〃澶达紝璇风‘璁ゆ槸鍚﹂�夐敊浜嗘暟鎹被鍨�");
+ }
// 妫�娴嬪瓙琛ㄥご锛氭槸鍚﹀寘鍚揣杩愰噺鍒楋紙鎵╁睍甯冨眬锛�
Row subHeader = sheet.getRow(2);
@@ -925,6 +1028,12 @@
int monthCount = Integer.parseInt(period.split("-")[1]);
try (Workbook wb = WorkbookFactory.create(file.getInputStream())) {
Sheet sheet = wb.getSheetAt(0);
+ Row header = sheet.getRow(0);
+ String h0 = header == null ? null : getString(header, 0);
+ String h1 = header == null ? null : getString(header, 1);
+ if (header == null || h0 == null || !h0.contains("璐ц繍閲�") || h1 == null || !h1.contains("鏈�")) {
+ throw new RuntimeException("鎵�閫夋暟鎹被鍨嬨�愯揣杩愰噺鍛ㄨ浆閲忋�戜笌鏂囦欢鍐呭涓嶇锛氳〃澶村簲涓恒�岃揣杩愰噺(涓囧惃)/1鏈堚��12鏈堛�嶅竷灞�锛岃纭鏄惁閫夐敊浜嗘暟鎹被鍨�");
+ }
boolean cityRowFound = false;
for (int r = 1; r <= Math.min(sheet.getLastRowNum(), 30); r++) {
Row rr = sheet.getRow(r);
@@ -1218,6 +1327,43 @@
}
}
throw new RuntimeException("鎵�閫夋暟鎹被鍨嬨��" + typeLabel + "銆戜笌鏂囦欢鍐呭涓嶇锛氳〃澶存湭鍖呭惈銆�" + String.join("銆戙��", keywords) + "銆戠瓑鍒楋紝璇风‘璁ゆ槸鍚﹂�夐敊浜嗘暟鎹被鍨�");
+ }
+
+ /** 鑳借�楁槑缁嗚〃澶村垪浣嶆牎楠岋細绯荤粺妯℃澘涓衡�滃墠3鍒� + 5缁劽�18鍒椻�濓紝绋嬪簭鎸夊浐瀹氬垪浣嶈В鏋愶紝琛ㄥご閿欎綅浼氬鑷存暣琛ㄩ敊涔憋紝閿欎綅鏃剁洿鎺ユ嫆缁� */
+ private void requireEnergyHeaderLayout(Sheet sheet) {
+ Row header = sheet == null ? null : sheet.getRow(0);
+ if (header == null) {
+ throw new RuntimeException("鑳借�楁槑缁嗚〃澶寸己澶憋紝璇蜂娇鐢ㄧ郴缁熸ā鏉裤�婃ā鏉縚閬撹矾璐ц繍杞﹁締鑳芥簮娑堣�楁儏鍐碉紙2026锛�.xlsx銆嬫暣鐞嗗悗鍐嶅鍏�");
+ }
+ for (int i = 0; i < ENERGY_HEADER_COLS.length; i++) {
+ int col = ENERGY_HEADER_COLS[i];
+ String keyword = ENERGY_HEADER_KEYWORDS[i];
+ Cell cell = header.getCell(col);
+ String hv = cell == null ? null : FORMATTER.formatCellValue(cell).trim();
+ if (hv == null || hv.isEmpty() || !hv.contains(keyword)) {
+ throw new RuntimeException("琛ㄥご涓庣郴缁熸ā鏉夸笉涓�鑷达細绗� " + (col + 1) + " 鍒楀簲涓恒��" + keyword + "銆嶏紝瀹為檯涓恒��"
+ + (hv == null || hv.isEmpty() ? "绌�" : hv) + "銆嶃�傝浣跨敤绯荤粺妯℃澘銆婃ā鏉縚閬撹矾璐ц繍杞﹁締鑳芥簮娑堣�楁儏鍐碉紙2026锛�.xlsx銆嬫暣鐞嗗悗鍐嶅鍏�");
+ }
+ }
+ }
+
+ /** 閫氱敤琛ㄥご鍒椾綅鏍¢獙锛氱▼搴忔寜鍥哄畾鍒椾綅瑙f瀽锛岃〃澶撮敊浣嶄細瀵艰嚧鏁磋〃閿欎贡锛岄敊浣嶆椂鐩存帴鎷掔粷瀵煎叆 */
+ private void requireHeaderLayout(Sheet sheet, String typeLabel, String templateHint,
+ int[] cols, String[] keywords) {
+ Row header = sheet == null ? null : sheet.getRow(0);
+ if (header == null) {
+ throw new RuntimeException("鎵�閫夋暟鎹被鍨嬨��" + typeLabel + "銆戞枃浠惰〃澶寸己澶憋紝璇蜂娇鐢ㄧ郴缁熸ā鏉裤��" + templateHint + "銆嬫暣鐞嗗悗鍐嶅鍏�");
+ }
+ for (int i = 0; i < cols.length; i++) {
+ int col = cols[i];
+ String keyword = keywords[i];
+ Cell cell = header.getCell(col);
+ String hv = cell == null ? null : FORMATTER.formatCellValue(cell).trim();
+ if (hv == null || hv.isEmpty() || !hv.contains(keyword)) {
+ throw new RuntimeException("琛ㄥご涓庣郴缁熸ā鏉夸笉涓�鑷达細绗� " + (col + 1) + " 鍒楀簲涓恒��" + keyword + "銆嶏紝瀹為檯涓恒��"
+ + (hv == null || hv.isEmpty() ? "绌�" : hv) + "銆嶃�傛墍閫夋暟鎹被鍨嬨��" + typeLabel + "銆戣浣跨敤绯荤粺妯℃澘銆�" + templateHint + "銆嬫暣鐞嗗悗鍐嶅鍏�");
+ }
+ }
}
/** 鎸夎〃澶村悕绉板缓绔嬪垪鍙锋槧灏勶紝鍏煎鏂版棫妯℃澘鍒椾綅宸紓 */
@@ -1792,8 +1938,10 @@
if (name == null || name.isEmpty()) return false;
String norm = name.replace(" ", "");
int month = Integer.parseInt(period.substring(5, 7));
+ String normDash = norm.replace('.', '-'); // "2026.8"/"2026.08" -> "2026-8"/"2026-08"
return norm.contains(month + "鏈�") || norm.contains(String.format("%02d", month) + "鏈�")
- || name.contains(period) || name.contains(period.replace("-0", "-"));
+ || name.contains(period) || name.contains(period.replace("-0", "-"))
+ || normDash.contains(period) || normDash.contains(period.replace("-0", "-"));
}
/** 瑙f瀽鍗曚釜甯傚窞鎶ヨ〃锛氶�� sheet锛堟姤琛ㄦ湡鍚嶄紭鍏� + 鏁版嵁琛屾暟锛夛紝鎸夊垪鍚嶆槧灏勬彁鍙栭」鐩 */
@@ -1808,6 +1956,11 @@
if (col == null) continue;
int cnt = countInvestRows(sh, col);
int score = cnt + (cnt > 0 && investSheetMatchesPeriod(sh, period) ? 1000000 : 0);
+ if (cnt > 0 && investSheetMatchesPeriod(sh, period)) {
+ // 褰撴湡鐪熷疄鎶ヨ〃 sheet 鍚嶉�氬父甯﹀勾浠斤紙濡� 2026.8/2026骞�8鏈堬級锛屽巻鍙瞫heet澶氫负绾湀浠藉悕锛�8鏈堬級锛氬甫骞翠唤鐨勪紭鍏�
+ String norm = sh.getSheetName() == null ? "" : sh.getSheetName().replace(" ", "");
+ if (norm.contains(period.substring(0, 4))) score += 100000;
+ }
if (score > bestScore) {
best = sh;
bestCol = col;
@@ -1852,10 +2005,12 @@
if (seq == null || seq.trim().isEmpty()) {
// 甯傚窞灏忚妭琛岋紙濡� "姝︽眽" "澶� 闂�"锛夛紝鏇存柊褰撳墠甯傚窞
String c = investmentCity(nt);
- if (c != null) currentCity = c;
+ if (c != null) { currentCity = c; continue; }
+ // 搴忓彿涓虹┖鐨勭湡瀹為」鐩锛堥儴鍒嗗競宸炴湀鎶ヤ笉濉簭鍙凤紝濡傜鍐滄灦鐗╂祦鍥級涔熸寜椤圭洰澶勭悊
+ if (!isLikelyInvestProjectName(nt)) continue;
+ } else if (!seq.trim().matches("\\d+(\\.\\d+)?")) {
continue;
}
- if (!seq.trim().matches("\\d+(\\.\\d+)?")) continue;
} else {
// 鏃犲簭鍙峰垪锛堥殢宸炵瓑锛夛細椤圭洰鍚嶉潪绌哄嵆椤圭洰琛岋紝甯傚窞灏忚妭琛岀敤 investmentCity 璇嗗埆
String c = investmentCity(nt);
@@ -1980,7 +2135,11 @@
if (nt.length() > 4) count++;
} else {
String seq = cellStr(row, seqCol);
- if (seq != null && seq.trim().matches("\\d+(\\.\\d+)?")) count++;
+ if (seq != null && seq.trim().matches("\\d+(\\.\\d+)?")) {
+ count++;
+ } else if ((seq == null || seq.trim().isEmpty()) && isLikelyInvestProjectName(nt)) {
+ count++;
+ }
}
}
return count;
@@ -2050,6 +2209,15 @@
log.error("load five year project names error", e);
}
return names;
+ }
+
+ /** 搴忓彿涓虹┖鐨勯潪甯傚窞琛屾槸鍚﹀儚椤圭洰琛岋細闀垮害>4 涓旈潪澶囨敞/璇存槑绛夋潅椤� */
+ private boolean isLikelyInvestProjectName(String nt) {
+ if (nt == null || nt.length() <= 4) return false;
+ if (investmentCity(nt) != null) return false;
+ return !nt.startsWith("澶囨敞") && !nt.startsWith("璇存槑") && !nt.startsWith("娉細")
+ && !nt.startsWith("濉〃") && !nt.startsWith("瀹℃牳") && !nt.startsWith("缁熻")
+ && !nt.startsWith("璐熻矗");
}
/** 椤圭洰鍚嶈鑼冨寲锛氬幓绌虹櫧/鎹㈣/鎷彿鍐呭/鏍囩偣/銆岄」鐩�嶅悗缂�锛岀敤浜庡崄浜斾簲娓呭崟鍖归厤 */
@@ -2228,12 +2396,18 @@
}
}
/** 鎶曡祫绯荤粺瀵煎嚭瀵煎叆锛堝鏍告瘮瀵瑰熀鍑嗭級锛屽浐瀹�8鍒楋細琛屽彿/鍗曚綅/鏃舵湡/璁″垝鎬绘姇璧�/鑷紑濮嬬疮璁�/鏈勾璁″垝/鑷勾鍒濈疮璁�/褰撴湀瀹屾垚 */
+ private static final int[] INVEST_SYSTEM_HEADER_COLS = {0, 1, 2, 3, 4, 5, 6, 7};
+ private static final String[] INVEST_SYSTEM_HEADER_KEYWORDS =
+ {"琛屽彿", "鍗曚綅", "鏃舵湡", "璁″垝鎬绘姇璧�", "鑷紑濮嬪缓璁�", "鏈勾璁″垝鎶曡祫", "鑷勾鍒�", "褰撴湀瀹屾垚鎶曡祫"};
+
public int importInvestmentSystem(MultipartFile file, String period) throws Exception {
List<InvestmentSystem> list = new ArrayList<>();
int failRows = 0;
try (Workbook wb = WorkbookFactory.create(file.getInputStream())) {
Sheet sheet = wb.getSheetAt(0);
requireHeaderKeyword(sheet, "鎶曡祫绯荤粺瀵煎嚭", "椤圭洰鍚嶇О", "璁″垝鎬绘姇璧�", "鍗曚綅");
+ requireHeaderLayout(sheet, "鎶曡祫绯荤粺瀵煎嚭", "妯℃澘_鏌ヨ缁撴灉(鎶曡祫绯荤粺-瀹㈣繍+鐗╂祦 2026.7).xls",
+ INVEST_SYSTEM_HEADER_COLS, INVEST_SYSTEM_HEADER_KEYWORDS);
for (int r = 1; r <= sheet.getLastRowNum(); r++) {
Row row = sheet.getRow(r);
if (row == null) continue;
@@ -2271,15 +2445,18 @@
}
/** 鍩庡競鍏氦鏈堟姤瀵煎叆锛堝煄甯傚叕鍏变氦閫氭湀搴﹁繍钀ユ儏鍐碉紝浼佷笟绾ф槑缁嗭紝鍙栧叕鍏辨苯鐢佃溅瀛楁锛� */
+ /** 鍩庡競鍏氦锛圲203锛夋ā鏉垮叧閿垪浣嶏紙0-based锛夛細鎵�灞炲湴鍖�/浼佷笟浠g爜/浼佷笟鍚嶇О/杩愯惀杞︽暟/瀹㈣繍閲�/杞ㄩ亾瀹㈣繍閲�/杞浮瀹㈣繍閲�/骞冲潎杩愯窛/濉〃浜�/鑱旂郴鐢佃瘽/鎶ュ嚭鏃ユ湡 */
+ private static final int[] CITY_BUS_HEADER_COLS = {0, 1, 2, 5, 8, 13, 17, 19, 24, 25, 26};
+ private static final String[] CITY_BUS_HEADER_KEYWORDS =
+ {"鎵�灞炲湴鍖�", "浼佷笟浠g爜", "浼佷笟鍚嶇О", "鍏叡姹界數杞杩愯惀杞︽暟", "鍏叡姹界數杞瀹㈣繍閲�", "杞ㄩ亾浜ら�歘瀹㈣繍閲�",
+ "鍩庡競瀹㈣繍杞浮_瀹㈣繍閲�", "鍏叡姹界數杞﹀钩鍧囪繍璺�", "濉〃浜�", "鑱旂郴鐢佃瘽", "鎶ュ嚭鏃ユ湡"};
+
public ImportResult importCityBus(MultipartFile file, String period) throws Exception {
List<CityBusRow> rows = new ArrayList<>();
try (Workbook wb = WorkbookFactory.create(file.getInputStream())) {
Sheet sheet = wb.getSheetAt(0);
- Row header = sheet.getRow(0);
- if (header == null || getString(header, 2) == null
- || !getString(header, 2).contains("浼佷笟鍚嶇О")) {
- throw new RuntimeException("鏈壘鍒拌〃澶磋锛堥渶鍖呭惈 浼佷笟鍚嶇О 鍒楋級");
- }
+ requireHeaderLayout(sheet, "鍩庡競鍏氦", "妯℃澘_浜や紒缁烾203琛╛鍩庡競鍏叡浜ら�氭湀搴﹁繍钀ユ儏鍐碉紙2026骞�7鏈堬級.xls",
+ CITY_BUS_HEADER_COLS, CITY_BUS_HEADER_KEYWORDS);
for (int r = 1; r <= sheet.getLastRowNum(); r++) {
Row row = sheet.getRow(r);
if (row == null) continue;
@@ -2383,15 +2560,18 @@
}
/** 宸℃父鍑虹鏈堟姤瀵煎叆锛堝競宸炵骇锛�17 琛岋紱鍚牳瀹炶鏄�/涓婃姤璇存槑/淇敼璁板綍锛� */
+ /** 宸℃父鍑虹锛圲402锛夋ā鏉垮叧閿垪浣嶏紙0-based锛夛細鎵�灞炲湴鍖�/浼佷笟浠g爜/浼佷笟鍚嶇О/杞藉杞︽鎬绘暟/瀹㈣繍閲�/鏃呭鍛ㄨ浆閲�/杩愯惀杞﹁締鏁�/骞冲潎杩愯窛/濉〃浜�/鑱旂郴鐢佃瘽/鎶ュ嚭鏃ユ湡 */
+ private static final int[] CITY_TAXI_HEADER_COLS = {0, 1, 2, 4, 6, 8, 10, 11, 15, 16, 17};
+ private static final String[] CITY_TAXI_HEADER_KEYWORDS =
+ {"鎵�灞炲湴鍖�", "浼佷笟浠g爜", "浼佷笟鍚嶇О", "杞藉杞︽鎬绘暟", "瀹㈣繍閲�", "鏃呭鍛ㄨ浆閲�", "杩愯惀杞﹁締鏁�",
+ "宸℃父鍑虹姹借溅骞冲潎杩愯窛", "濉〃浜�", "鑱旂郴鐢佃瘽", "鎶ュ嚭鏃ユ湡"};
+
public ImportResult importCityTaxi(MultipartFile file, String period) throws Exception {
List<TaxiRow> rows = new ArrayList<>();
try (Workbook wb = WorkbookFactory.create(file.getInputStream())) {
Sheet sheet = wb.getSheetAt(0);
- Row header = sheet.getRow(0);
- if (header == null || getString(header, 2) == null
- || !getString(header, 2).contains("浼佷笟鍚嶇О")) {
- throw new RuntimeException("鏈壘鍒拌〃澶磋锛堥渶鍖呭惈 浼佷笟鍚嶇О 鍒楋級");
- }
+ requireHeaderLayout(sheet, "宸℃父鍑虹", "妯℃澘_浜よ缁烾402琛�-宸℃父鍑虹姹借溅杩愯惀鏈嶅姟鎯呭喌鏈堟姤(2026).xls",
+ CITY_TAXI_HEADER_COLS, CITY_TAXI_HEADER_KEYWORDS);
for (int r = 1; r <= sheet.getLastRowNum(); r++) {
Row row = sheet.getRow(r);
if (row == null) continue;
--
Gitblit v1.9.1