| HANDOFF.md | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| docs/公路旅客/个体客运量汇总.xlsx | 补丁 | 查看 | 原始文档 | blame | 历史 | |
| docs/城市客运/导入模板_出租车信息(运政车辆信息).xlsx | 补丁 | 查看 | 原始文档 | blame | 历史 | |
| docs/年报/模板_公共汽电车运营情况.xls | 补丁 | 查看 | 原始文档 | blame | 历史 | |
| docs/年报/模板_公路旅客运输能源消耗情况.xls | 补丁 | 查看 | 原始文档 | blame | 历史 | |
| docs/年报/模板_单位基本情况.xls | 补丁 | 查看 | 原始文档 | blame | 历史 | |
| docs/年报/模板_城市轨道交通人员及运营情况.xls | 补丁 | 查看 | 原始文档 | blame | 历史 | |
| docs/年报/模板_城市轨道交通车辆及线路情况.xls | 补丁 | 查看 | 原始文档 | blame | 历史 | |
| docs/年报/模板_法人企业财务状况.xls | 补丁 | 查看 | 原始文档 | blame | 历史 | |
| traffic-audit-server/src/main/java/com/trafficaudit/dataimport/controller/DataImportController.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| traffic-audit-server/src/main/java/com/trafficaudit/dataimport/service/DataImportService.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| traffic-audit-server/src/main/java/com/trafficaudit/reportexport/service/ReportExportService.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| traffic-audit-web/src/views/DataImport.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
HANDOFF.md
@@ -4,7 +4,26 @@ > **æ¯å¤©ç»ææ¶**ï¼æå½å¤©è¿å±ã踩è¿çåãæ°éæ±ãæªå®æäºé¡¹æ´æ°å°æ¬æä»¶ï¼ç¶åå¯ä»¥æ¾å¿å¼æ°å¯¹è¯ã > **åä½çº¦å®ï¼2026-08-31ï¼**ï¼è¯´ãæäº¤ã= æ¬å° commit + æ¨éå°è¿ç«¯ gitï¼origin mainï¼Gitblitï¼ï¼ä¸ååªåæ¬å° commitã ## 仿¥ï¼2026-08-31ï¼å« 08-28 起积累记å½ï¼ ## 仿¥ï¼2026-09-01ï¼å« 08-31 起积累记å½ï¼ ### 9. 2026-09-01ï¼åäºå¹´æ¥åè½å¼åä¸ + æ°çæ¬æå åå¸ï¼å·²åå¸å°ç¨æ·ç¯å¢ï¼ - **git è¿å±ï¼ä»å¤© 4 ä¸ªæ°æäº¤ï¼**ï¼ - `92528d9`ï¼åäºï¼ï¼æ°æ®å¯¼å ¥é¡µæ¨¡æ¿å/ä¸è½½è·¯å¾å¯¹é½å®é æ¨¡æ¿æä»¶ï¼.xlsâ.xlsxã补个ä½å®¢è¿æ¨¡æ¿è·¯å¾ï¼ - `b1be169`ï¼ææ¹ï¼ï¼2 ä¸ªæ¨¡æ¿æä»¶å ¥åºæ¨éï¼docs/åå¸å®¢è¿/å¯¼å ¥æ¨¡æ¿_åºç§è½¦ä¿¡æ¯(è¿æ¿è½¦è¾ä¿¡æ¯).xlsxãdocs/å ¬è·¯æ 客/个ä½å®¢è¿éæ±æ».xlsxï¼åäºæ¬å°æ¾å¥½åææ¹æ ¸å¯¹åææ¯ä¾å ¥åºï¼ - `053ebfe`ï¼åäºï¼ï¼æ°æ®å¯¼å ¥é¡µåç»æ¹åï¼é路货è¿/ç«åºæèµ/è´§è¿è½è壿¥ï¼+ æ°å¢ãå¹´æ¥ãåç» 11 ç±»é项 - `0c16e56`ï¼åäºï¼ï¼6 ä¸ªå¹´æ¥æ¨¡æ¿å ¥åºï¼docs/å¹´æ¥/ï¼.xlsï¼ - **â ï¸ å¹´æ¥åè½åªæ UI 壳ï¼åäºå¼åä¸ï¼ææ¹çº¦å®ä¸å¨ï¼**å端 groups æ 11 个 annual* é项ï¼ä½ importApiMap æ URLãå端 DataImportController æ annual* 端ç¹ãtemplateName/templateUrl æ æ å°ã模æ¿åªæ 6/11 ç±» â éäºå¹´æ¥ç±»åæ æ³ä¸ä¼ /ä¸è½½ãåäºç»§ç»å¼åï¼åç»æåå³å¯ã - **æå åå¸ï¼å·²å®æï¼ç¨æ·å·²åå¸å°ç¨æ·ç¯å¢ï¼**ï¼å端 mvn clean packageï¼jar 57MBï¼+ å端 npm run buildï¼dist 38 æä»¶ï¼ï¼é¨ç½²å° D:\trafficAuditï¼app.jar æ¿æ¢ï¼æ§å å¤ä»½ app.jar.bak_20260901ï¼ãweb/ é建ãdocs åååæ¥ï¼æ°å¢ docs/å¹´æ¥ + 2 ä¸ªæ°æ¨¡æ¿ï¼ä¿ç ai_demo/usage_screenshotsï¼ï¼æ¬æºå¯å¨éªè¯ 8090ï¼Started TrafficAuditApplicationãé¦é¡µ 200ã/api/auth/captcha-config required=falseï¼éªè¯åå·²åã - **å¾ åä¸å**ï¼æ£å¼åå¸å app.captcha.required æ¹å trueï¼æµè§å¨å´©æºå¦å¨æ°å åç°ç logs/access.*.log + 客æ·ç«¯ F12ï¼æå¿å¾/AI RAG/ç»æµææ è¯ç®ä» è®°å½æªå®ç°ã ### 10. å ³æºäº¤æ¥å¿«ç §ï¼09-01 æï¼ - **å½åè¿è¡ç¶æ**ï¼dev åå端æªè¿è¡ï¼D:\trafficAudit ç app.jarï¼PID 28976ï¼14:40 ç¨æ·èªå·±å¯å¨ï¼å¨ 8090 è¿è¡ï¼å ³æºèªç¶åæ¢ãä¸éå¤çï¼MySQL80(3305)ãMySQL57(3306) å Runningï¼ææ¯ä¾ä¿çã - **git**ï¼å·¥ä½åºå¹²åï¼HEAD=`9b97b92`ï¼å·²åæ¥ origin/mainã - **仿¥äº¤ä»**ï¼æååäºä»£ç ï¼92528d9/053ebfe/0c16e56ï¼ã模æ¿å ¥åºï¼b1be169ï¼ãæå åå¸å° D:\trafficAudit å¹¶æ¬æºéªè¯éè¿ï¼æ§å å¤ä»½ app.jar.bak_20260901ï¼ï¼ç¨æ·å·²åå¸å°ç¨æ·ç¯å¢ï¼çæ¬å·å¨è¯¢å·²çå¤ï¼SemVerï¼é¦æ¬¡æ£å¼åå¸å® 1.0.0ï¼ã - **å¾ åï¼ä¸åï¼**ï¼â æ£å¼åå¸å app.captcha.required æ¹å trueï¼â¡æµè§å¨å´©æºå¦åç°ç logs/access.*.log + 客æ·ç«¯ F12ï¼â¢åäºå¹´æ¥åè½å¼åä¸ï¼ææ¹ä¸å¨ï¼ï¼â£æå¿å¾/AI RAG/ç»æµææ è¯ç®ä» è®°å½æªå®ç°ï¼â¤æ¯æ¥åæèªå¨åæªå建ã - **â ï¸ åå¸ååæå°åäº 2 ä¸ªæ°æäº¤**ï¼å ³æºååç°ï¼ï¼`fbc1c15` æ°æ®å¯¼å ¥ä¸æ¥è¡¨çæå¤é¡¹æ¹è¿ã`91d7eee` æèµç³»ç»åºåå¯¼å ¥å ¼å®¹æ¥è¯¢ç»æè¡¨å¤´ï¼è¡å·/åä½/æ¶æ/è®¡åæ»æèµï¼ãä»å¤© 11:43 æå åå¸ççæ¬ä¸å«è¿ä¸¤é¡¹ï¼ç¨æ·ç¯å¢å½åçæ¬è½å HEAD 2 个æäº¤ï¼ä¸æ¬¡åå¸åééæ°æå + éæ°æå ã ## åå²ï¼2026-08-31ï¼å« 08-28 起积累记å½ï¼ ### 1. ç»å½éªè¯ç ï¼æµè¯é¶æ®µå¯è·³è¿ï¼å·²å®æå¹¶éªè¯ï¼ - å端æ°å¢ `app.captcha.required` å¼å ³ï¼ç¼ºç `true`=å¯ç¨ï¼ï¼æ¬æº application.yml ä¸ example ç°ä¸º `false`ï¼æµè¯é¶æ®µè·³è¿ï¼**æ£å¼åå¸åæ¹å `true`**ï¼ã docs/¹«Â·ÂÿÍ/¸öÌå¿ÍÔËÁ¿»ã×Ü.xlsxBinary files differ
docs/³ÇÊпÍÔË/µ¼ÈëÄ£°å_³ö×â³µÐÅÏ¢(ÔËÕþ³µÁ¾ÐÅÏ¢).xlsxBinary files differ
docs/Ä걨/Ä£°å_¹«¹²Æûµç³µÔËÓªÇé¿ö.xlsBinary files differ
docs/Ä걨/Ä£°å_¹«Â·ÂÿÍÔËÊäÄÜÔ´ÏûºÄÇé¿ö.xlsBinary files differ
docs/Ä걨/Ä£°å_µ¥Î»»ù±¾Çé¿ö.xlsBinary files differ
docs/Ä걨/Ä£°å_³ÇÊйìµÀ½»Í¨ÈËÔ±¼°ÔËÓªÇé¿ö.xlsBinary files differ
docs/Ä걨/Ä£°å_³ÇÊйìµÀ½»Í¨³µÁ¾¼°Ïß·Çé¿ö.xlsBinary files differ
docs/Ä걨/Ä£°å_·¨ÈËÆóÒµ²ÆÎñ×´¿ö.xlsBinary files differ
traffic-audit-server/src/main/java/com/trafficaudit/dataimport/controller/DataImportController.java
@@ -267,6 +267,20 @@ } } /** 夿件æ¹éå¯¼å ¥ï¼åç«¯ä¸æ¬¡éæ©å¤ä¸ª Excel ä¸ä¼ ï¼type ä¸ºæ°æ®ç±»åï¼æèµç±»åæå¸å·å表解æï¼ */ @PostMapping("/batch") public Result<Object> importBatch(@RequestParam("type") String type, @RequestParam("period") String period, @RequestParam("files") List<MultipartFile> files) { try { Map<String, Object> result = importService.importBatchFiles(type, period, files); return Result.ok(result); } catch (Exception e) { log.error("batch import error", e); return Result.error("æ¹éå¯¼å ¥å¤±è´¥: " + e.getMessage()); } } /** æèµå¸å·å表æ¹éå¯¼å ¥ï¼M2ï¼ï¼æ«æ docs/æèµ/è¾å ¥/{ç±»å«}/{Xæ} ç®å½å ¨é¨å¸å·æ¥è¡¨ï¼category=客è¿ç«åº/ç©æµååº */ @PostMapping("/investCityBatch") public Result<Object> importInvestCityBatch(@RequestParam("period") String period, traffic-audit-server/src/main/java/com/trafficaudit/dataimport/service/DataImportService.java
@@ -168,6 +168,7 @@ Map<String, Integer> colMap; try (Workbook wb = WorkbookFactory.create(file.getInputStream())) { Sheet sheet = wb.getSheetAt(0); requireHeaderCols(sheet, "é路货ç©è¿è¾æåº¦ç产æ åµ", "ä¼ä¸åç§°", "è´§è¿é_æ»è®¡"); colMap = buildHeaderMap(sheet.getRow(0)); for (int r = 1; r <= sheet.getLastRowNum(); r++) { Row row = sheet.getRow(r); @@ -284,6 +285,7 @@ try (Workbook wb = WorkbookFactory.create(file.getInputStream())) { // sheet0: å ¬è·¯æ 客6ææ¥ Sheet sheet = wb.getSheetAt(0); requireHeaderCols(sheet, "å ¬è·¯æ 客è¿è¾æåº¦ç产æ åµ", "ä¼ä¸åç§°", "客è¿é_æ»è®¡"); Map<String, Integer> colMap = buildHeaderMap(sheet.getRow(0)); for (int r = 1; r <= sheet.getLastRowNum(); r++) { Row row = sheet.getRow(r); @@ -476,6 +478,7 @@ try (Workbook wb = WorkbookFactory.create(file.getInputStream())) { Sheet sheet = wb.getSheetAt(0); Map<String, Integer> colMap = buildHeaderMap(sheet.getRow(0)); requireHeaderCols(sheet, "æ å®¢è¿æ¿è½¦è¾æ°", "ä¼ä¸åç§°", "è½¦è¾æ°", "è½½å®¢ä½æ°"); for (int r = 1; r <= sheet.getLastRowNum(); r++) { Row row = sheet.getRow(r); if (row == null) continue; @@ -530,6 +533,7 @@ try (Workbook wb = WorkbookFactory.create(file.getInputStream())) { // sheet0: 第äºå£åº¦è´§è½¦è½èæç»ï¼æ¯è¡å·¦å³å5ç»è½¦è¾ï¼æ¯ç»åºå®18åï¼ Sheet sheet = wb.getSheetAt(0); requireHeaderKeyword(sheet, "é路货è¿è½¦è¾è½æºæ¶èæ åµ", "ä¼ä¸åç§°", "车çå·"); for (int r = 1; r <= sheet.getLastRowNum(); r++) { Row row = sheet.getRow(r); if (row == null) continue; @@ -648,6 +652,7 @@ List<EnergyAuthVehicle> authList = new ArrayList<>(); try (Workbook wb = WorkbookFactory.create(file.getInputStream())) { Sheet sheet = wb.getSheetAt(0); requireHeaderKeyword(sheet, "è½è车è¾è¿æ¿ä¿¡æ¯", "车çå·"); for (int r = 1; r <= sheet.getLastRowNum(); r++) { Row row = sheet.getRow(r); if (row == null) continue; @@ -716,6 +721,7 @@ int failRows = 0; try (Workbook wb = WorkbookFactory.create(file.getInputStream())) { Sheet sheet = wb.getSheetAt(0); requireHeaderKeyword(sheet, "è¿æ¿è½¦è¾", "ä¼ä¸åç§°"); for (int r = 1; r <= sheet.getLastRowNum(); r++) { Row row = sheet.getRow(r); if (row == null) continue; @@ -763,6 +769,7 @@ int failRows = 0; try (Workbook wb = WorkbookFactory.create(file.getInputStream())) { Sheet sheet = wb.getSheetAt(0); requireHeaderKeyword(sheet, "轨迹éç¨", "ä¼ä¸åç§°"); for (int r = 1; r <= sheet.getLastRowNum(); r++) { Row row = sheet.getRow(r); if (row == null) continue; @@ -806,6 +813,14 @@ List<ScaleSplitTransport> list = new ArrayList<>(); int failRows = 0; try (Workbook wb = WorkbookFactory.create(file.getInputStream())) { boolean hasPeriodSheet = false; for (int i = 0; i < wb.getNumberOfSheets(); i++) { String sn = wb.getSheetAt(i).getSheetName(); if (sn != null && (sn.contains("彿") || sn.contains("累计"))) { hasPeriodSheet = true; break; } } if (!hasPeriodSheet) { throw new RuntimeException("æéæ°æ®ç±»åãè§ä¸è§ä¸æåã䏿件å 容ä¸ç¬¦ï¼æªæ¾å°å« 彿/累计 ç sheetï¼è¯·ç¡®è®¤æ¯å¦ééäºæ°æ®ç±»å"); } for (int i = 0; i < wb.getNumberOfSheets(); i++) { Sheet sheet = wb.getSheetAt(i); String sheetName = sheet.getSheetName(); @@ -896,6 +911,18 @@ int monthCount = Integer.parseInt(period.split("-")[1]); try (Workbook wb = WorkbookFactory.create(file.getInputStream())) { Sheet sheet = wb.getSheetAt(0); boolean cityRowFound = false; for (int r = 1; r <= Math.min(sheet.getLastRowNum(), 30); r++) { Row rr = sheet.getRow(r); if (rr == null) continue; String nm = getString(rr, 0); if (nm == null || nm.trim().isEmpty()) continue; String region = RegionUtil.normalizeCityName(nm); if (RegionUtil.cityList().contains(region) || "æ¹åç".equals(region)) { cityRowFound = true; break; } } if (!cityRowFound) { throw new RuntimeException("æéæ°æ®ç±»åãè´§è¿éå¨è½¬éã䏿件å 容ä¸ç¬¦ï¼æªè¯å«å°å¸å·è¡ï¼é¦ååºä¸ºå¸å·åç§°ï¼ï¼è¯·ç¡®è®¤æ¯å¦ééäºæ°æ®ç±»å"); } Map<String, FreightTurnoverImport> freightMap = new HashMap<>(); Map<String, FreightTurnoverImport> turnoverMap = new HashMap<>(); Set<String> seen = new HashSet<>(); @@ -1152,6 +1179,33 @@ } } /** ç±»å-è¡¨å¤´æ ¡éªï¼æå¿ éååï¼è§£æå¨å®é ä¾èµçåï¼ï¼ç¼ºå¤±å³æ"ç±»å䏿件ä¸ç¬¦" */ private void requireHeaderCols(Sheet sheet, String typeLabel, String... cols) { Map<String, Integer> colMap = buildHeaderMap(sheet.getRow(0)); List<String> missing = new ArrayList<>(); for (String c : cols) { if (!colMap.containsKey(c)) missing.add(c); } if (!missing.isEmpty()) { throw new RuntimeException("æéæ°æ®ç±»åã" + typeLabel + "ã䏿件å 容ä¸ç¬¦ï¼è¡¨å¤´ç¼ºå°å¿ éåã" + String.join("ãã", missing) + "ãï¼è¯·ç¡®è®¤æ¯å¦ééäºæ°æ®ç±»å"); } } /** ç±»å-è¡¨å¤´æ ¡éªï¼æ«æè¡¨å¤´è¡ï¼å½ä¸ä»»ä¸å ³é®åå³éè¿ */ private void requireHeaderKeyword(Sheet sheet, String typeLabel, String... keywords) { Row header = sheet == null ? null : sheet.getRow(0); if (header != null) { for (Cell cell : header) { String v = FORMATTER.formatCellValue(cell); if (v == null || v.isEmpty()) continue; for (String k : keywords) { if (v.contains(k)) return; } } } throw new RuntimeException("æéæ°æ®ç±»åã" + typeLabel + "ã䏿件å 容ä¸ç¬¦ï¼è¡¨å¤´æªå å«ã" + String.join("ãã", keywords) + "ãçåï¼è¯·ç¡®è®¤æ¯å¦ééäºæ°æ®ç±»å"); } /** æè¡¨å¤´å称建ç«åå·æ å°ï¼å ¼å®¹æ°æ§æ¨¡æ¿åä½å·®å¼ */ private Map<String, Integer> buildHeaderMap(Row header) { Map<String, Integer> colMap = new HashMap<>(); @@ -1398,7 +1452,7 @@ } } if (headerRow < 0) { throw new RuntimeException("æªæ¾å°è¡¨å¤´è¡ï¼éå å« åºå·/项ç®åç§° åï¼"); throw new RuntimeException("æéæ°æ®ç±»åã" + (passenger ? "æèµææ¥ï¼å®¢è¿ç«åºï¼" : "æèµææ¥ï¼ç©æµååºï¼") + "ã䏿件å 容ä¸ç¬¦ï¼æªæ¾å°è¡¨å¤´è¡ï¼éå å« åºå·/项ç®åç§° åï¼ï¼è¯·ç¡®è®¤æ¯å¦ééäºæ°æ®ç±»å"); } for (int r = headerRow + 1; r <= sheet.getLastRowNum(); r++) { Row row = sheet.getRow(r); @@ -1553,11 +1607,19 @@ } fileResults.add(fr); } // å¹çæ¸ çï¼åç±»å«åæ¥è¡¨ææåº¦æ°æ® + INVEST å®¡æ ¸ç»æï¼é¡¹ç®ä¸»æ¡£è·¨æä¿çï¼æå upsertï¼ cleanInvestMonthly(category, period); // å¹çæ¸ çï¼ææ¬æ¬¡æä»¶æ¶åå¸å·æ¸ çæåº¦æ°æ® + INVEST å®¡æ ¸ç»æï¼é¡¹ç®ä¸»æ¡£è·¨æä¿çï¼æå upsertï¼ cleanInvestMonthlyByCity(category, period, rows); Map<String, Set<String>> knownCities = loadInvestKnownCities(category); Map<String, String> fileFirstMismatch = new LinkedHashMap<>(); int success = 0; List<String> failDetails = new ArrayList<>(); for (InvestCityRow item : rows) { String mismatch = investCityMismatchReason(item, knownCities); if (mismatch != null) { failDetails.add(mismatch); fileFirstMismatch.putIfAbsent(item.fileName, mismatch); continue; } try { saveInvestProjectRow(item.project, item.monthly); success++; @@ -1569,6 +1631,11 @@ failDetails.add(item.fileName + " 第 " + item.excelRow + " è¡ï¼" + item.project.getProjectName() + "ï¼ï¼" + reason); log.error("Invest city insert error: file {}, row {}", item.fileName, item.excelRow, e); } } // æé¦ä¸ªå¸å·éä½æç¤ºæå°å¯¹åºæä»¶ç»æä¸ï¼ä¾¿äºé¡µé¢å¼¹çªç´æ¥çå° for (Map<String, Object> fr2 : fileResults) { String msg = fileFirstMismatch.get(fr2.get("fileName")); if (msg != null && fr2.get("error") == null) fr2.put("error", msg); } String errorDetail = String.join("\n", failDetails); if (errorDetail.length() > 4000) errorDetail = errorDetail.substring(0, 4000) + "\nâ¦â¦"; @@ -1587,6 +1654,119 @@ result.put("fiveYearTemplateProjects", fiveYearNames.size()); result.put("fiveYearMatched", fiveYearMatched); log.info("Invest city {} {} imported: {} rows (fiveYear matched {}/{})", category, period, success, fiveYearMatched, fiveYearNames.size()); return result; } /** * 夿件æ¹éå¯¼å ¥ï¼åç«¯ä¸æ¬¡éæ©å¤ä¸ª Excel ä¸ä¼ ã * æèµç±»åï¼investment/investmentLogisticsï¼æå¸å·å表解æï¼æä»¶å/å¡«æ¥åä½è¯å«å¸å·ï¼åäºäºèªå¨æ è®°ï¼ï¼ * å ¶ä»ç±»åéä¸ªèµ°ç°æè§£æé»è¾ï¼importDirFileï¼ã */ public Map<String, Object> importBatchFiles(String type, String period, List<MultipartFile> files) throws Exception { if (files == null || files.isEmpty()) { throw new RuntimeException("æªæ¥æ¶å°ä»»ä½æä»¶"); } if ("investment".equals(type) || "investmentLogistics".equals(type)) { String category = "investment".equals(type) ? "客è¿ç«åº" : "ç©æµååº"; return importInvestCityFiles(period, category, files); } List<Map<String, Object>> fileResults = new ArrayList<>(); int totalSuccess = 0, totalFail = 0; for (MultipartFile f : files) { Map<String, Object> fr = new LinkedHashMap<>(); fr.put("fileName", f.getOriginalFilename()); try { ImportResult r = importDirFile(type, f, period); fr.put("success", r.getSuccess()); fr.put("fail", r.getFail()); if (r.getFail() > 0 && r.getFailDetails() != null && !r.getFailDetails().isEmpty()) { fr.put("error", r.getFailDetails().get(0)); } totalSuccess += r.getSuccess(); totalFail += r.getFail(); } catch (Exception e) { fr.put("success", 0); fr.put("fail", 0); fr.put("error", e.getMessage() == null ? e.toString() : e.getMessage()); totalFail++; } fileResults.add(fr); } Map<String, Object> result = new LinkedHashMap<>(); result.put("type", type); result.put("total", files.size()); result.put("success", totalSuccess); result.put("fail", totalFail); result.put("files", fileResults); return result; } /** æèµå¤æä»¶æ¹éï¼éæä»¶æå¸å·å表解æï¼ç»ä¸å¹çæ¸ çè½åºï¼æ¹æ¬¡æ±æ» */ public Map<String, Object> importInvestCityFiles(String period, String category, List<MultipartFile> files) throws Exception { boolean passenger = "客è¿ç«åº".equals(category); Set<String> fiveYearNames = loadFiveYearProjectNames(); List<InvestCityRow> rows = new ArrayList<>(); List<Map<String, Object>> fileResults = new ArrayList<>(); for (MultipartFile f : files) { Map<String, Object> fr = new LinkedHashMap<>(); fr.put("fileName", f.getOriginalFilename()); try (Workbook wb = WorkbookFactory.create(f.getInputStream())) { parseInvestCityWorkbook(wb, f.getOriginalFilename(), category, period, fiveYearNames, rows, fr); } catch (Exception e) { fr.put("success", 0); fr.put("error", e.getMessage() == null ? e.toString() : e.getMessage()); log.error("invest city file parse error: {}", f.getOriginalFilename(), e); } fileResults.add(fr); } String catDir = passenger ? "客è¿ï¼å®¢è¿ç«ï¼" : "ç©æµï¼ç©æµååºï¼"; cleanInvestMonthlyByCity(category, period, rows); Map<String, Set<String>> knownCities = loadInvestKnownCities(category); Map<String, String> fileFirstMismatch = new LinkedHashMap<>(); int success = 0; List<String> failDetails = new ArrayList<>(); for (InvestCityRow item : rows) { String mismatch = investCityMismatchReason(item, knownCities); if (mismatch != null) { failDetails.add(mismatch); fileFirstMismatch.putIfAbsent(item.fileName, mismatch); continue; } try { saveInvestProjectRow(item.project, item.monthly); success++; } catch (Exception e) { Throwable cause = e; while (cause.getCause() != null) cause = cause.getCause(); String reason = cause.getMessage(); if (reason == null || reason.trim().isEmpty()) reason = e.getMessage(); failDetails.add(item.fileName + " 第 " + item.excelRow + " è¡ï¼" + item.project.getProjectName() + "ï¼ï¼" + reason); log.error("Invest city insert error: file {}, row {}", item.fileName, item.excelRow, e); } } // æé¦ä¸ªå¸å·éä½æç¤ºæå°å¯¹åºæä»¶ç»æä¸ï¼ä¾¿äºé¡µé¢å¼¹çªç´æ¥çå° for (Map<String, Object> fr2 : fileResults) { String msg = fileFirstMismatch.get(fr2.get("fileName")); if (msg != null && fr2.get("error") == null) fr2.put("error", msg); } String errorDetail = String.join("\n", failDetails); if (errorDetail.length() > 4000) errorDetail = errorDetail.substring(0, 4000) + "\nâ¦â¦"; recordBatch("夿件æ¹éå¯¼å ¥ï¼" + catDir + "/" + period + "ï¼", "INVEST_" + category + "_CITY", period, rows.size(), success, failDetails.size(), errorDetail); int fiveYearMatched = 0; for (InvestCityRow item : rows) { if (Integer.valueOf(1).equals(item.project.getIsFiveYear())) fiveYearMatched++; } Map<String, Object> result = new LinkedHashMap<>(); result.put("total", rows.size()); result.put("success", success); result.put("fail", failDetails.size()); result.put("failDetails", failDetails); result.put("files", fileResults); result.put("fiveYearTemplateProjects", fiveYearNames.size()); result.put("fiveYearMatched", fiveYearMatched); log.info("Invest city multi-file {} {} imported: {} rows (fiveYear matched {}/{})", category, period, success, fiveYearMatched, fiveYearNames.size()); return result; } @@ -1627,6 +1807,7 @@ } String city = cityFromFileName(fileName); if (city == null) city = cityFromSheetHeader(best); fr.put("city", city == null ? "" : city); String currentCity = city; Map<String, Integer> col = bestCol; int nameCol = col.get("name"); @@ -1637,6 +1818,15 @@ Row row = best.getRow(r); if (row == null) continue; String name = cellStr(row, nameCol); String seq = hasSeq ? cellStr(row, seqCol) : null; // å¸å·å°èè¡ï¼é¨åæ±æ»è¡¨æå¸å·ååå¨åºå·åï¼å¦åå °æ±æ»è¡¨ A åå"æ¦æ±"ï¼ï¼éåæ ·è¯å« if (seq != null && !seq.trim().isEmpty()) { String seqCity = investmentCity(seq.trim()); if (seqCity != null) { currentCity = seqCity; continue; } } if (name == null || name.trim().isEmpty()) continue; String nt = name.trim().replaceAll("[\r\n]+", ""); if (nt.contains("å计") || nt.contains("æ»è®¡")) continue; @@ -1645,7 +1835,6 @@ if (nt.replace(" ", "").matches(".*å°\\s*计.*")) continue; if (nt.startsWith("å¡«æ¥") || nt.startsWith("åä½è´è´£äºº") || nt.matches("^\\d+ã.*")) continue; if (hasSeq) { String seq = cellStr(row, seqCol); if (seq == null || seq.trim().isEmpty()) { // å¸å·å°èè¡ï¼å¦ "æ¦æ±" "天 é¨"ï¼ï¼æ´æ°å½åå¸å· String c = investmentCity(nt); @@ -1884,7 +2073,7 @@ return null; } /** åç±»å«åæ¥è¡¨ææåº¦æ°æ® + INVEST å®¡æ ¸ç»ææ¸ çï¼å¹çï¼ */ /** åç±»å«åæ¥è¡¨ææåº¦æ°æ® + INVEST å®¡æ ¸ç»ææ¸ çï¼å¹çï¼å ¨çæ±æ»å¤§è¡¨å ¨éæ¸ çï¼ */ private void cleanInvestMonthly(String category, String period) { List<InvestmentProject> sameCat = investProjectMapper.selectList( new LambdaQueryWrapper<InvestmentProject>().eq(InvestmentProject::getCategory, category)); @@ -1895,15 +2084,60 @@ .eq(InvestmentMonthly::getReportPeriod, period) .in(InvestmentMonthly::getProjectId, catProjectIds)); } deleteInvestAuditResults(period, null); } /** æå¸å·æ¸ çï¼åªå 餿¬æ¬¡æä»¶æ¶åå¸å·ï¼å«æªè¯å«å¸å·ç null 项ç®ï¼çæåº¦æ°æ® + INVEST å®¡æ ¸ç»æ */ private void cleanInvestMonthlyByCity(String category, String period, List<InvestCityRow> rows) { Set<String> citySet = new HashSet<>(); boolean hasNullCity = false; for (InvestCityRow item : rows) { String c = item.project.getCity(); if (c == null || c.trim().isEmpty()) hasNullCity = true; else citySet.add(c.trim()); } if (citySet.isEmpty() && !hasNullCity) return; boolean cleanNullCity = hasNullCity; LambdaQueryWrapper<InvestmentProject> pw = new LambdaQueryWrapper<InvestmentProject>() .eq(InvestmentProject::getCategory, category); pw.and(w -> { if (!citySet.isEmpty()) w.in(InvestmentProject::getCity, citySet); if (cleanNullCity) { if (!citySet.isEmpty()) w.or(); w.isNull(InvestmentProject::getCity); } }); List<InvestmentProject> cityProjects = investProjectMapper.selectList(pw); java.util.Set<Long> catProjectIds = new HashSet<>(); for (InvestmentProject pc : cityProjects) catProjectIds.add(pc.getId()); if (catProjectIds.isEmpty()) return; List<InvestmentMonthly> oldMonthlies = investMonthlyMapper.selectList( new LambdaQueryWrapper<InvestmentMonthly>() .eq(InvestmentMonthly::getReportPeriod, period) .in(InvestmentMonthly::getProjectId, catProjectIds)); java.util.Set<Long> oldMonthlyIds = new HashSet<>(); for (InvestmentMonthly m : oldMonthlies) oldMonthlyIds.add(m.getId()); investMonthlyMapper.delete(new LambdaQueryWrapper<InvestmentMonthly>() .eq(InvestmentMonthly::getReportPeriod, period) .in(InvestmentMonthly::getProjectId, catProjectIds)); deleteInvestAuditResults(period, oldMonthlyIds); } /** æ¸ ç INVEST å®¡æ ¸ç»æï¼monthlyIds 为 null 表示该æé´å ¨éæ¸ çï¼ */ private void deleteInvestAuditResults(String period, java.util.Set<Long> monthlyIds) { List<Long> investRuleIds = new ArrayList<>(); for (com.trafficaudit.rulemanage.entity.AuditRule rule : ruleMapper.selectList(null)) { if ("INVEST".equals(rule.getReportType())) investRuleIds.add(rule.getId()); } if (!investRuleIds.isEmpty()) { auditResultMapper.delete(new LambdaQueryWrapper<AuditResult>() .eq(AuditResult::getReportPeriod, period) .in(AuditResult::getRuleId, investRuleIds)); if (investRuleIds.isEmpty()) return; LambdaQueryWrapper<AuditResult> w = new LambdaQueryWrapper<AuditResult>() .eq(AuditResult::getReportPeriod, period) .in(AuditResult::getRuleId, investRuleIds); if (monthlyIds != null) { if (monthlyIds.isEmpty()) return; w.in(AuditResult::getReportId, monthlyIds); } auditResultMapper.delete(w); } /** 项ç®ä¸»æ¡£ upsertï¼æ 项ç®å+ç±»å«ï¼+ æåº¦åå ¥ */ @@ -1922,6 +2156,32 @@ monthly.setProjectId(exist.getId()); } investMonthlyMapper.insert(monthly); } /** è½½å ¥æç±»å«ãè§èå项ç®å -> å·²å ¥åºå¸å·éåãï¼ç¨äºå¸å·ä¸è´æ§æ ¡éªï¼åç§°å¨å¤ä¸ªå¸å·é夿¶æ¾å¼æ ¡éªï¼ */ private Map<String, Set<String>> loadInvestKnownCities(String category) { Map<String, Set<String>> map = new HashMap<>(); List<InvestmentProject> list = investProjectMapper.selectList( new LambdaQueryWrapper<InvestmentProject>().eq(InvestmentProject::getCategory, category)); for (InvestmentProject p : list) { if (p.getCity() == null || p.getCity().trim().isEmpty()) continue; map.computeIfAbsent(normalizeProjectName(p.getProjectName()), k -> new HashSet<>()) .add(p.getCity().trim()); } return map; } /** å¸å·ä¸è´æ§æ ¡éªï¼åºä¸å¯ä¸å½å±å¸å·ä¸æ¬æ¬¡è§£æä¸ä¸è´æ¶è¿åé误说æï¼å¦åè¿å null */ private String investCityMismatchReason(InvestCityRow item, Map<String, Set<String>> knownCities) { String parsed = item.project.getCity(); if (parsed == null || parsed.trim().isEmpty()) return null; Set<String> known = knownCities.get(normalizeProjectName(item.project.getProjectName())); if (known == null || known.size() != 1) return null; String knownCity = known.iterator().next(); if (knownCity.equals(parsed.trim())) return null; return item.fileName + " 第 " + item.excelRow + " è¡ï¼" + item.project.getProjectName() + "ï¼ï¼å¸å·ä¸è´æ§æ ¡éªæªéè¿ââåºä¸è¯¥é¡¹ç®å½å±ã" + knownCity + "ãï¼æ¬æ¬¡æä»¶è§£æä¸ºã" + parsed.trim() + "ãï¼çä¼¼å¸å·éä½ï¼å·²è·³è¿å¯¼å ¥ï¼è¯·æ ¸å¯¹æºæä»¶"; } private String cellStr(Row row, int idx) { @@ -1959,6 +2219,7 @@ int failRows = 0; try (Workbook wb = WorkbookFactory.create(file.getInputStream())) { Sheet sheet = wb.getSheetAt(0); requireHeaderKeyword(sheet, "æèµç³»ç»å¯¼åº", "项ç®åç§°", "è®¡åæ»æèµ", "åä½"); for (int r = 1; r <= sheet.getLastRowNum(); r++) { Row row = sheet.getRow(r); if (row == null) continue; @@ -2198,6 +2459,7 @@ List<CityTaxiAuth> list = new ArrayList<>(); try (Workbook wb = WorkbookFactory.create(file.getInputStream())) { Sheet sheet = wb.getSheetAt(0); requireHeaderKeyword(sheet, "åºç§è½¦è¿æ¿è½¦è¾", "车ç"); for (int r = 1; r <= sheet.getLastRowNum(); r++) { Row row = sheet.getRow(r); if (row == null) continue; traffic-audit-server/src/main/java/com/trafficaudit/reportexport/service/ReportExportService.java
@@ -3221,11 +3221,11 @@ } /** 表1 项ç®è¡å¡«å ï¼æ¨¡æ¿è¡/æå ¥è¡å ±ç¨ï¼cityText 䏿¨¡æ¿ B åæ ¼å¼ä¸è´ï¼ */ private void fillInvestPlanProjectRow(HSSFRow row, InvestmentProject p, InvestmentMonthly m, int seq, String cityText) { private void fillInvestPlanProjectRow(HSSFRow row, InvestmentProject p, InvestmentMonthly m, int seq, String cityText, String displayName) { hssfNum(row, 0, (double) seq); hssfText(row, 1, cityText); hssfText(row, 2, valueOf(p.getCounty())); hssfText(row, 3, p.getProjectName()); hssfText(row, 3, displayName != null && !displayName.trim().isEmpty() ? displayName.trim() : p.getProjectName()); hssfText(row, 4, valueOf(p.getConstructNature())); hssfNum(row, 5, parseYearNum(p.getStartTime())); hssfNum(row, 6, parseYearNum(p.getEndTime())); @@ -3332,6 +3332,7 @@ // åä½éè¡æ¿æ¢ï¼å计/åæ é¢/å¸å·å°è/åç»/项ç®è¡ï¼æªå¹é çæ¨¡æ¿è¡æ¸ ç©ºæ ·ä¾æ°æ®ï¼ç»æ/åå¹¶/å宽ä¸åï¼ int seq = 0; int si = -1; java.util.Set<Long> usedProjectIds = new HashSet<>(); for (int r = 5; r <= lastRow; r++) { HSSFRow row = sheet.getRow(r); if (row == null) continue; @@ -3391,11 +3392,12 @@ // 项ç®è¡ï¼æ 模æ¿åç§°+å¸å·+åç±»å« å¹é DB 项ç®ï¼æªå¹é æ´è¡æ¸ 空 blankInvestRow(row, 25); if (blockCategory == null) continue; // èæ§è´§è½¦æ®µæ æ°æ®æº InvestmentProject p = matchInvest(projects, d, b, blockCategory); InvestmentProject p = matchInvest(projects, d, b, blockCategory, usedProjectIds); if (p == null) continue; usedProjectIds.add(p.getId()); if (sec != null) sec.matched.add(p.getId()); InvestmentMonthly m = mByProject.get(p.getId()); fillInvestPlanProjectRow(row, p, m, ++seq, b); fillInvestPlanProjectRow(row, p, m, ++seq, b, d); } // 卿æè¡ï¼DB é¡¹ç®æ° > 模æ¿é¢çè¡æ°æ¶ï¼å¨å¸å·å°èæ«å°¾è¡¥é½ï¼èªåºå䏿è¡ï¼é¿å è¡å·éä½ï¼ for (int i = sections.size() - 1; i >= 0; i--) { @@ -3415,7 +3417,7 @@ HSSFRow row = sheet.getRow(first + j); if (row == null) continue; InvestmentProject p = extra.get(j); fillInvestPlanProjectRow(row, p, mByProject.get(p.getId()), 0, p.getCity()); fillInvestPlanProjectRow(row, p, mByProject.get(p.getId()), 0, p.getCity(), p.getProjectName()); } } renumberInvestSeq(sheet, "investPlan"); @@ -3725,6 +3727,11 @@ /** å¨åéé¡¹ç®æ± 䏿åç§°å¹é DB 项ç®ï¼å¯é å¸å·/ç±»å« è¿æ»¤ + å¿åºåç¼æ¶æ§ï¼ï¼æªå½ä¸è¿å null */ private InvestmentProject matchInvest(List<InvestmentProject> pool, String name, String cityFull, String category) { return matchInvest(pool, name, cityFull, category, null); } /** 带已å ç¨æé¤çå¹é ï¼åä¸ DB 项ç®åªå 许填å ä¸è¡ï¼é¿å 模æ¿ä¸æ/äºæååè¡éå¤è¾åºï¼ */ private InvestmentProject matchInvest(List<InvestmentProject> pool, String name, String cityFull, String category, Set<Long> excludeIds) { if (name == null || name.trim().isEmpty()) return null; String norm = normInvestName(name); if (norm.isEmpty()) return null; @@ -3732,6 +3739,7 @@ String county = extractInvestCounty(name); List<InvestmentProject> cands = new java.util.ArrayList<>(); for (InvestmentProject p : pool) { if (excludeIds != null && excludeIds.contains(p.getId())) continue; if (category != null && !category.equals(p.getCategory())) continue; if (cityCanon != null && !cityCanon.equals(RegionUtil.normalizeCityName(p.getCity()))) continue; cands.add(p); traffic-audit-web/src/views/DataImport.vue
@@ -51,15 +51,15 @@ <el-button size="mini" type="primary" icon="el-icon-upload2" :loading="cityDirLoading" @click="importCityDir(moduleSel[g.key])">ä»ç®å½æ¹éå¯¼å ¥</el-button> </div> <div v-else-if="moduleSel[g.key]" class="module-file"> <div class="file-name" :title="fileNames[moduleSel[g.key]]">{{ fileNames[moduleSel[g.key]] || 'æªéæ©æä»¶' }}</div> <div v-else-if="moduleSel[g.key] && !isDirOnlyType(moduleSel[g.key])" class="module-file"> <div class="file-name" :title="fileNamesText(moduleSel[g.key])">{{ fileNamesText(moduleSel[g.key]) || 'æªéæ©æä»¶' }}</div> <div class="file-btns"> <el-upload action="#" :auto-upload="false" :show-file-list="false" accept=".xlsx,.xls" <el-upload action="#" :auto-upload="false" :show-file-list="false" multiple accept=".xlsx,.xls,.et" :ref="'up' + g.key" :on-change="f => onFilePicked(g.key, f)" style="display:inline-block"> <el-button size="mini" type="primary" plain icon="el-icon-folder-opened">éæ©æä»¶</el-button> </el-upload> <el-button size="mini" type="primary" icon="el-icon-upload2" :disabled="!files[moduleSel[g.key]]" @click="importNow(g.key)">ç«å³å¯¼å ¥</el-button> <el-button size="mini" type="primary" icon="el-icon-upload2" :disabled="!fileCount(moduleSel[g.key])" @click="importNow(g.key)">{{ fileCount(moduleSel[g.key]) > 1 ? 'æ¹éå¯¼å ¥ ' + fileCount(moduleSel[g.key]) + ' 个æä»¶' : 'ç«å³å¯¼å ¥' }}</el-button> </div> </div> </div> @@ -167,8 +167,8 @@ overview: {}, groups: [ { key: 'freight', name: 'è´§è¿', types: [ { value: 'h2032', label: 'H203-2ææ¥' }, key: 'freight', name: 'é路货è¿', types: [ { value: 'h2032', label: 'é路货ç©è¿è¾æåº¦ç产æ åµ' }, { value: 'transportAuth', label: 'è¿æ¿è½¦è¾' }, { value: 'trackMileage', label: '轨迹éç¨' }, { value: 'scaleSplit', label: 'è§ä¸è§ä¸æå' }, @@ -177,20 +177,20 @@ }, { key: 'passenger', name: 'å ¬è·¯æ 客', types: [ { value: 'passengerMonthly', label: 'H203-1æ å®¢ææ¥' }, { value: 'passengerMonthly', label: 'å ¬è·¯æ 客è¿è¾æåº¦ç产æ åµ' }, { value: 'passengerAuth', label: 'è¿æ¿è½¦è¾æ°' }, { value: 'passengerIndividual', label: '个ä½å®¢è¿é/å¨è½¬é' } ] }, { key: 'city', name: 'åå¸å®¢è¿', types: [ { value: 'cityBus', label: 'åå¸å ¬äº¤ææ¥' }, { value: 'cityTaxi', label: '巡游åºç§ææ¥' }, { value: 'cityBus', label: 'åå¸å ¬å ±äº¤éæåº¦è¿è¥æ åµ' }, { value: 'cityTaxi', label: '巡游åºç§æ±½è½¦è¿è¥æå¡æ 嵿æ¥' }, { value: 'cityTaxiAuth', label: 'åºç§è½¦è¿æ¿è½¦è¾' } ] }, { key: 'invest', name: 'æèµ', types: [ key: 'invest', name: 'ç«åºæèµ', types: [ { value: 'investment', label: 'æèµææ¥ï¼å®¢è¿ç«åºï¼' }, { value: 'investmentLogistics', label: 'æèµææ¥ï¼ç©æµååºï¼' }, { value: 'investmentSystem', label: 'æèµç³»ç»å¯¼åº' }, @@ -199,9 +199,24 @@ ] }, { key: 'energy', name: 'è½è', types: [ { value: 'energyMonthly', label: 'H204è½è壿¥' }, key: 'energy', name: 'è´§è¿è½è壿¥', types: [ { value: 'energyMonthly', label: 'é路货è¿è½¦è¾è½æºæ¶èæ åµ' }, { value: 'energyAuth', label: 'è½è车è¾è¿æ¿ä¿¡æ¯' } ] }, { key: 'annual', name: 'å¹´æ¥', types: [ { value: 'annualCompanyBasic', label: 'åä½åºæ¬æ åµ' }, { value: 'annualEnterpriseFinance', label: 'æ³äººä¼ä¸è´¢å¡ç¶åµ' }, { value: 'annualPassengerEnergy', label: 'å ¬è·¯æ 客è¿è¾è½æºæ¶èæ åµ' }, { value: 'annualBusOperation', label: 'å ¬å ±æ±½çµè½¦è¿è¥æ åµ' }, { value: 'annualMetroVehicles', label: 'åå¸è½¨é交é车è¾å线路æ åµ' }, { value: 'annualMetroStaff', label: 'åå¸è½¨é交é人ååè¿è¥æ åµ' }, { value: 'annualFerry', label: 'åå¸å®¢è¿è½®æ¸¡è¿è¥æ åµ' }, { value: 'annualPublicEnergy', label: 'å ¬å ±äº¤éè½æºæ¶èæ åµ' }, { value: 'annualCityInfrastructureOperators', label: 'åå¸å®¢è¿åºç¡è®¾æ½æ åµãåå¸å®¢è¿ç»è¥ä¸æ·æ åµ' }, { value: 'annualTaxiOwnership', label: '巡游åºç§æ±½è½¦è¥è¿è½¦è¾æ¥ææ åµ' }, { value: 'annualTaxiProduction', label: '巡游åºç§æ±½è½¦è¿è¾ç产æ åµ' } ] } ] @@ -240,7 +255,7 @@ investCityLogistics: 'âï¼æ¹éæ«æç®å½ï¼æ 鿍¡æ¿ï¼', cityBus: 'å¯¼å ¥_åå¸å ¬å ±äº¤éæåº¦è¿è¥æ åµï¼YYYYå¹´Mæï¼.xls/.xlsxï¼ä¼ä¸çº§æç»ï¼åå ¬å ±æ±½çµè½¦å段ï¼', cityTaxi: 'å¯¼å ¥_巡游åºç§æ±½è½¦è¿è¥æå¡æ 嵿æ¥ï¼YYYYå¹´Mæï¼.xlsï¼å¸å·çº§17è¡ï¼', cityTaxiAuth: 'å¯¼å ¥_åºç§è½¦ä¿¡æ¯(è¿æ¿è½¦è¾ä¿¡æ¯).xlsï¼è½¦è¾çº§æç»ï¼ç¨äºè¿è¥è½¦æ°å¯¹æ¯ï¼' cityTaxiAuth: 'å¯¼å ¥æ¨¡æ¿_åºç§è½¦ä¿¡æ¯(è¿æ¿è½¦è¾ä¿¡æ¯).xlsxï¼è½¦è¾çº§æç»ï¼ç¨äºè¿è¥è½¦æ°å¯¹æ¯ï¼' } return map[this.importType] || '' }, @@ -253,12 +268,12 @@ freightTurnover: 'è´§è¿/模æ¿_è´§è¿éå¨è½¬é.xlsx', passengerMonthly: 'å ¬è·¯æ 客/模æ¿_交ä¼ç»H203-1表_å ¬è·¯æ 客è¿è¾æåº¦ç产æ åµ.xlsx', passengerAuth: 'å ¬è·¯æ 客/模æ¿_å ¬è·¯æ å®¢è¿æ¿è½¦è¾æ°.xlsx', passengerIndividual: '', passengerIndividual: 'å ¬è·¯æ 客/个ä½å®¢è¿éæ±æ».xlsx', energyMonthly: 'è½è/模æ¿_é路货è¿è½¦è¾è½æºæ¶èæ åµï¼2026ï¼.xlsx', energyAuth: 'è½è/模æ¿_è½è车è¾è¿æ¿ä¿¡æ¯.xlsx', cityBus: 'åå¸å®¢è¿/å¯¼å ¥æ¨¡æ¿_åå¸å ¬äº¤.xlsx', cityTaxi: 'åå¸å®¢è¿/å¯¼å ¥æ¨¡æ¿_巡游åºç§.xlsx', cityTaxiAuth: 'åå¸å®¢è¿/å¯¼å ¥æ¨¡æ¿_åºç§è½¦ä¿¡æ¯(è¿æ¿è½¦è¾ä¿¡æ¯).xls', cityTaxiAuth: 'åå¸å®¢è¿/å¯¼å ¥æ¨¡æ¿_åºç§è½¦ä¿¡æ¯(è¿æ¿è½¦è¾ä¿¡æ¯).xlsx', investment: 'æèµ/è¾å ¥/客è¿ï¼å®¢è¿ç«ï¼/æ´æ°_7æå®¢è¿ç«æèµæç»è¡¨ï¼å¸å·æ±æ»å¤§è¡¨ï¼ç»é¢å¯¼ï¼.xlsx', investmentLogistics: 'æèµ/è¾å ¥/ç©æµï¼ç©æµååºï¼/æ´æ°_7æç©æµç«åºæèµæç».xls', investmentSystem: 'æèµ/模æ¿_æ¥è¯¢ç»æ(æèµç³»ç»-客è¿+ç©æµ 2026.7).xls' @@ -301,22 +316,31 @@ return map[t] || '' }, onFilePicked(key, file) { this.$set(this.files, this.moduleSel[key], file.raw) this.$set(this.fileNames, this.moduleSel[key], file.name) const type = this.moduleSel[key] if (!type) return const list = (this.files[type] || []).filter(f => f.name !== file.name) list.push(file.raw) this.$set(this.files, type, list) this.$set(this.fileNames, type, list.map(f => f.name)) const up = this.$refs['up' + key] if (up && up.clearFiles) up.clearFiles() }, importNow(key) { const type = this.moduleSel[key] if (!type) return if (this.isCityDirType(type)) { if (this.isDirOnlyType(type)) { this.importCityDir(type) this.moduleSel[key] = '' this.importType = '' return } const file = this.files[type] if (!file) { this.$message.warning('请å 为该类åéæ©æä»¶'); return } const fileList = this.files[type] || [] if (!fileList.length) { this.$message.warning('请å 为该类åéæ©æä»¶'); return } if (fileList.length > 1 || type === 'investment' || type === 'investmentLogistics') { this.batchImport(type, key, fileList) return } const file = fileList[0] if (!this.period) { this.$message.warning('请å éæ©æ¥è¡¨æ'); return } const g = this.groups.find(x => x.key === key) const t = g.types.find(x => x.value === type) @@ -342,10 +366,57 @@ this.$delete(this.fileNames, type) }) }, fileCount(type) { return (this.files[type] || []).length }, fileNamesText(type) { const list = this.fileNames[type] || [] return list.length ? 'å·²é ' + list.length + ' 个æä»¶ï¼' + list.join('ã') : '' }, batchImport(type, key, fileList) { if (!this.period) { this.$message.warning('请å éæ©æ¥è¡¨æ'); return } const fd = new FormData() fd.append('type', type) fd.append('period', this.period) fileList.forEach(f => fd.append('files', f)) const g = this.groups.find(x => x.key === key) const label = g ? g.name : type this.$message({ message: 'æ£å¨æ¹éå¯¼å ¥ ' + fileList.length + ' 个æä»¶â¦', duration: 3000 }) api.post('/import/batch', fd, { timeout: 600000 }) .then(res => { const d = (res && res.data) || {} if (!d.files) { this.$message.error((res && res.message) || 'æ¹éå¯¼å ¥å¤±è´¥') return } const files = d.files || [] const errCount = files.filter(f => f.error || f.success === 0).length const html = files.map(f => '<div style="margin:6px 0;line-height:1.6"><b>' + (f.fileName || '') + '</b>' + (f.city ? 'ï¼' + f.city + 'ï¼' : '') + 'ï¼è§£æ ' + (f.success || 0) + ' æ¡' + (f.error ? ' <span style="color:#F56C6C">' + f.error + '</span>' : '') + '</div>' ).join('') this.$alert(html, (label || type) + ' æ¹éå¯¼å ¥å®æï¼å ± ' + d.total + ' 个æä»¶' + (d.fail > 0 ? 'ï¼å¤±è´¥ ' + d.fail + ' æ¡' : '') + 'ï¼æå ' + d.success + ' æ¡ï¼', { dangerouslyUseHTMLString: true, type: errCount > 0 ? 'warning' : 'success', confirmButtonText: 'ç¥éäº' }) if (type === 'investment' || type === 'investmentLogistics') { this.checkInvest(this.period) } this.afterImport() this.files[type] = null this.$delete(this.fileNames, type) }) .catch(e => { if (e && e._toast) return this.$message.error((e.response && e.response.data && e.response.data.message) || 'æ¹éå¯¼å ¥å¤±è´¥') }) }, isCityDirType(t) { return t === 'investCityPassenger' || t === 'investCityLogistics' }, isDirOnlyType(t) { return t === 'investCityPassenger' || t === 'investCityLogistics' }, importCityDir(type) { if (!this.period) { this.$message.warning('请å éæ©æ¥è¡¨æ'); return } const category = type === 'investCityPassenger' ? '客è¿ç«åº' : 'ç©æµååº' const category = (type === 'investCityPassenger' || type === 'investment') ? '客è¿ç«åº' : 'ç©æµååº' const fd = new FormData() fd.append('period', this.period) fd.append('category', category)