| docs/功能测试报告/功能测试报告_RULE001分项运政与阈值联动及列宽回归_2026-09-09.md | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| docs/工作日结/工作日结_2026-09-09.md | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| traffic-audit-server/src/main/java/com/trafficaudit/auditengine/service/AuditEngineService.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| traffic-audit-server/src/main/java/com/trafficaudit/reportexport/service/ReportExportService.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| traffic-audit-server/src/main/java/com/trafficaudit/rulemanage/controller/RuleManageController.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| traffic-audit-web/src/views/DataImport.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
docs/¹¦ÄܲâÊÔ±¨¸æ/¹¦ÄܲâÊÔ±¨¸æ_RULE001·ÖÏîÔËÕþÓëãÐÖµÁª¶¯¼°ÁÐ¿í»Ø¹é_2026-09-09.md
New file @@ -0,0 +1,58 @@ # åè½æµè¯æ¥åï¼RULE_001 åé¡¹è¿æ¿å¯¹æ¯ / éå¼è¯´æèå¨éç¨å / æ±æ»å¤§è¡¨å宽åå½ / å¯¼å ¥ç»æå¼¹çªï¼2026-09-09ï¼ ## 1. åè½è¯´æ æç¨æ· 2026-09-09 ä¸åå馿´æ¹åé¡¹ï¼ 1. **è´§è¿ææ¥ RULE_001 æ¹åé¡¹è¿æ¿å¯¹æ¯**ï¼çµå¼è½¦æ°/æè½¦æ°/æ´è½¦æ°ï¼è½¦ç±»å·®â¤5 å°ï¼ãæè½¦å¨ä½/æ´è½¦å¨ä½ï¼å¨ä½å·®â¤50 å¨ï¼ã䏿¥æ»å¨ä½ vs è¿æ¿æ»å¨ä½ï¼å·®â¤50 å¨ï¼ï¼ä»»ä¸åé¡¹è¶ é峿éï¼å RULE_002ï¼æ»éå¨ä½å¯¹æ¯ï¼å¹¶å ¥ RULE_001 ååç¨é²éå¤ã 2. **ææå®¡æ ¸è§åéå¼åå¨ â è§ååç§°/说æèå¨**ï¼ç±åæ¥åªæ¹â% åæ°åâæ¨å¹¿å°ä»»ææ°åçæ®µï¼å°/å¨/å ¬éçåä½åçé弿°åï¼ï¼å¹¶æ¯æâ5,5,50,5,50,50âè¿ç±»å¤æ®µéå¼é段èå¨ã 3. **æ±æ»å¤§è¡¨ï¼éè·¯è¿è¾éæ±æ»è¡¨æ´æ¬ï¼å宽å彿¯ç**ï¼å é¤å¯¼åºæ¶ `autoFitContentColumns`ï¼å 容èªéåºå 宽ï¼ï¼åå®½ä¸¥æ ¼éç¨æ¯çåå§å宽ã 4. **æ°æ®å¯¼å ¥çé¢å¯¼å ¥ç»æå¼¹çª**ï¼åæä»¶å¯¼å ¥æåå¼¹âæåæ¶æ¯ï¼å«è§£ææ¡æ°ï¼âã失败弹é误详æ ï¼æ¿ä»£åæ¥åªå¨åºé¨å表æ¾ç¤ºã ## 2. æµè¯ç¯å¢ - å端 Spring Boot 8090ï¼mvn å¢éç¼è¯éè¿åéå¯ï¼`Started TrafficAuditApplication` ç¡®è®¤ï¼ - å端 Vue dev server 8080ï¼`Compiled successfully` äº 10:24:53ï¼ - MySQL `traffic_audit`ï¼localhost:3308ï¼ï¼è§å表 `audit_rule`ãå®¡æ ¸ç»æ `audit_result` - æ°æ®æï¼2026-08ï¼è´§è¿ä¼ä¸ææ¥ 636 æ·ãè¿æ¿è½¦è¾ 635 æ·åå¨åºï¼ ## 3. ç¨ä¾è¡¨ï¼è¾å ¥/颿/å®é /ç»è®ºï¼ ### 3.1 RULE_001 åé¡¹è¿æ¿å¯¹æ¯ | ç¨ä¾ | è¾å ¥ | 颿 | å®é | ç»è®º | |---|---|---|---|---| | æ£å-è§åé ç½® | SQL æ´æ° RULE_001 为 DIFF_FIELDS + å æ®µ check_field/threshold | API /rules/list æ¾ç¤ºæ°åç§°/说æ/éå¼ | rule_name=âè½¦è¾æ°ä¸å¨ä½è¿æ¿å项对æ¯âï¼check_field=vehicleTractor,vehicleTrailer,tonsTrailer,vehicleWhole,tonsWhole,tonsTotalï¼threshold=5,5,50,5,50,50 | éè¿ | | æ£å-å®¡æ ¸æ§è¡ | POST /api/audit/execute?period=2026-08&reportType=H2032 | æ£å¸¸è¿åå½ä¸æç» | 4s è¿å 487 æ¡ç»æï¼å ¶ä¸ RULE_001 å½ä¸ 115 æ¡ | éè¿ | | æ£å-å½ä¸æ¶æ¯ | æ½æ¥ rule_id=1 ç actual_value | é项ååº ä¸æ¥/è¿æ¿/å·®/éï¼â¤200 å符 | ä¾ï¼âçµå¼è½¦æ°ï¼ä¸æ¥=120 å°ï¼è¿æ¿=114 å°ï¼å·®6 å°ï¼é5 å°ï¼ï¼æè½¦å¨ä½ï¼ä¸æ¥=26032.8 å¨ï¼è¿æ¿=25874.78 å¨ï¼å·®158.02 å¨ï¼é50 å¨ï¼â¦â | éè¿ | | è´å-æ¶æ¯è¶ é¿ | å¤åé¡¹åæ¶è¶ é | ä¸å actual_value 200 å符æ¥éæªæ | åæ¡æ¶æ¯â¤180 åç¬¦æ¼æ¥ï¼è¶ åºè¿½å âçNé¡¹è¶ éâï¼é¦è½®æ ä¿æ¤æ¶æ¾æ¥ Data truncationï¼å·²ä¿®å¤åæ§è¡æå | éè¿ | | å»é | RULE_002 åç¨ | 2026-08 ä¸å产ç rule_id=2 ç»æ | ç»æåå¸ä» rule_id 1/3/4/5/6ï¼æ 2 | éè¿ | ### 3.2 éå¼åå¨è¯´æèå¨ï¼åå¼âæ°å¼âå¤åååï¼ | ç¨ä¾ | è¾å ¥ | 颿 | å®é | ç»è®º | |---|---|---|---|---| | å°æ°å DIFF | RULE_P001 threshold 3â5 | 说æâå·®è·>3å°âââå·®è·>5å°â | æ´æ°å说æä¸ºâå·®è·>5å°éæ ¸å®â | éè¿ | | ç¾åæ°å | RULE_P005 threshold 20â30 | åç§°/说æ ±20%â±30% | åç§°âå¹³åè¿è·ç¯æ¯è¶ ±30%æ ¸å®âï¼è¯´æåæ¥ | éè¿ | | æ¯å¼å | RULE_003 threshold 1.4â1.5 | âé«åº40%âââé«åº50%â | è¯´ææ´æ°ä¸ºâé«åº50%éæ ¸å®â | éè¿ | | å¹ç/å¤å | 䏿¡å ¨é¨è¿å 3/20/1.4 | åç§°ä¸è¯´æåå°åå¼ | 夿 ¸å ¨é¨å¤åï¼æªæ±¡æè¯´æä¸æ å ³æ°å | éè¿ | ### 3.3 æ±æ»å¤§è¡¨å宽å彿¯ç | ç¨ä¾ | è¾å ¥ | 颿 | å®é | ç»è®º | |---|---|---|---|---| | æ£å-å¯¼åº | GET /api/report/export/summaryWorkbook?period=2026-08 | æ£å¸¸è¾åºæ´æ¬ | 3s å¯¼åº 938,586 åèï¼19 个页ç¾ä¸æ¯çä¸è´ | éè¿ | | åå®½å¯¹æ¯ | openpyxl é页é忝坹å宽 | 䏿¯çå宽ä¸è´ | 19 页ç¾å ± 12 å¤å·®å¼ï¼å ¨é¨ä¸ºâæ¯çæªæ¾å¼å®ä¹ãPOI è¡¥é»è®¤å®½âï¼å¼=æ¯çé»è®¤å®½ 8.8867/14.0 çï¼ï¼è§è§å宽ä¸è´ | éè¿ï¼æ å 容èªéåºå å®½ï¼ | | æ ·å¼ä¿ç | å¯¼åºæä»¶æ½æ¥ | æ°å¼ä¸¤ä½å°æ°ãåæ¶çéãè¿åéèè¡ãæå¼éç®ä¿ç | ä¿çï¼æ¢æé»è¾æªå¨ï¼ | éè¿ | ### 3.4 å¯¼å ¥ç»æå¼¹çª | ç¨ä¾ | è¾å ¥ | 颿 | å®é | ç»è®º | |---|---|---|---|---| | æ£å-代ç | importNow æååè°å¢å showImportResult(row) | æåå¼¹ `$message.success`ï¼å«å端è¿åââ¦å¯¼å ¥æåï¼å ± N æ¡âï¼ï¼å¤±è´¥å¼¹ `$alert` é误æç» | 代ç å·²è½å°ï¼å端ç¼è¯æåï¼å端åæä»¶æ¥å£åè¿å Result<String> ä¸ææ¶æ¯ | éè¿ï¼ç颿工ç¹éªå¾ ç¨æ·ç¡®è®¤ï¼ | | å ¼å®¹-æèµ/åå¸ç®å½ | åæ checkInvest/ç®å½æ¹éæµç¨ | ä¸éå¤å¼¹çª | ç®å½æ¹éä»èµ°å $alert æ±æ»ï¼ä¸åå½±å | éè¿ | ## 4. å ¥åºæ½æ¥ï¼ä¸æºæä»¶é项对é½ï¼ - `audit_rule`ï¼RULE_001ï¼id=1ï¼å·²å ¥åºæ°é ç½®ï¼RULE_002ï¼id=2ï¼`is_enabled=0`ãthreshold æ ¡æ£ä¸º 50 ä¸è¯´æä¸è´ã - `audit_result`ï¼2026-08 RULE_001 å½ä¸ 115 æ¡ï¼æ½æ¥ 8 å®¶ä¼ä¸ç actual_value å为âå项ï¼ä¸æ¥/è¿æ¿/å·®/éâç»æï¼æ°æ®ä¸ h2032_enterprise_monthly / transport_auth_vehicle åå£å¾ã ## 5. åç°çé®é¢ä¸å¤ç½® 1. æ°åé¡¹æ¶æ¯è¶ è¿ `audit_result.actual_value varchar(200)` â å·²å 180 åç¬¦æªæä¿æ¤ï¼ä¿ç馿®µ+âçNé¡¹è¶ éâæ±æ»ï¼ã 2. RULE_002 éå¼ 80 ä¸å ¶è¯´æâå·®è·>50âé¿æä¸ä¸è´ â å¹¶å ¥ RULE_001 å¹¶åç¨ï¼éå¼ç»ä¸ 50ã 3. è¿æ¿ä¾§åæ®µä» æ çµå¼/æè½¦/å ¶ä» ä¸åç±»ï¼H2032 çâæ´è½¦âæè¿æ¿âå ¶ä»è½¦âå£å¾æ¯å¯¹ï¼å·²å¨å½ä¸æ¶æ¯ä¸è¯´æä¸ä½ç°ï¼å¦éä¸âæ´è½¦ï¼å«éè£ ç®±ï¼âåç¬æ¯å¯¹éè¿æ¿æ°æ®ä¾§å¢å åç±»ã ## 6. ç»è®º åé¡¹æ´æ¹åè½éªè¯éè¿ï¼æ±æ»å¤§è¡¨ä»ä¸ºâå¤å¶æ¯ç+å¤ä»½è¿åâæºå¶ï¼8 ææ°æ®å¡«å ï¼M2/M3ï¼æªå¨æ¬è½®è½å°ï¼è§å·¥ä½æ¥ç»éçäºé¡¹ã docs/¹¤×÷ÈÕ½á/¹¤×÷ÈÕ½á_2026-09-09.md
New file @@ -0,0 +1,24 @@ # 工使¥ç» 2026-09-09 ## 仿¥å®æ - æ±æ»å¤§è¡¨ï¼å é¤å¯¼åºæ´æ¬æ¶çâå 容èªéåºå宽âï¼å宽å彿¯çåå§å®½åº¦ï¼ç» openpyxl é页éåæ¯å¯¹ä» å© POI è¡¥é»è®¤å®½ï¼æ å®è´¨å·®å¼ï¼ï¼åæ¶çé/è¿åéèè¡/两ä½å°æ°/æå¼éç®çæ¢æå¤çä¿çã - å®¡æ ¸è§åï¼ - RULE_001 æ¹ä¸ºâè½¦è¾æ°ä¸å¨ä½è¿æ¿å项对æ¯âï¼DIFF_FIELDSï¼ï¼çµå¼è½¦/æè½¦/æ´è½¦è½¦ç±»å·®â¤5 å°ï¼æè½¦å¨ä½/æ´è½¦å¨ä½å·®â¤50 å¨ï¼æ»å¨ä½å·®â¤50 å¨ï¼å½ä¸æ¶æ¯å¸¦ 䏿¥/è¿æ¿/å·®/é æç»å¹¶é² 200 å符溢åºï¼ - RULE_002 å¹¶å ¥ RULE_001 æ»å¨ä½æ®µååç¨ï¼éå¼ä¸è¯´æç»ä¸ä¸º 50ï¼ï¼ - è§åéå¼âåç§°/说æè卿¨å¹¿å°å ¨é¨è§åï¼æ¯æå°/å¨/å ¬éçéç¾åæ¯æ°åä¸å¤æ®µéå¼ï¼å¦ 5,5,50,5,50,50ï¼ï¼DIFF/æ¯å¼/ç¾åæ°ä¸ç±»å®æµååèå¨å¹¶å¤åæ æ±¡æã - æ°æ®å¯¼å ¥ï¼åæä»¶å¯¼å ¥æå/失败å¢å å¼¹çªæç¤ºï¼æåå«è§£ææ¡æ°ï¼å¤±è´¥åé误æç»ï¼ã - æµè¯æ¥åï¼docs/åè½æµè¯æ¥å/åè½æµè¯æ¥å_RULE001åé¡¹è¿æ¿ä¸éå¼èå¨åå宽åå½_2026-09-09.mdã ## æµè¯ç»ææè¦ - 2026-08 è´§è¿å®¡æ ¸æ´ææ§è¡ 4s è¿å 487 æ¡ï¼RULE_001 å½ä¸ 115 æ¡ï¼æ¶æ¯ç»ææ£ç¡®ï¼RULE_002 ä¸ååºç»æã - æ±æ»æ´æ¬å¯¼åº 2026-08 æåï¼938KB/19 页ç¾ï¼ï¼å宽䏿¯çä¸è´ã - å端 dev ç¼è¯æåï¼å¯¼å ¥å¼¹çªå»ºè®®ç¨æ·å¨çé¢ç¹éªä¸æ¬¡ã ## éçé®é¢ä¸å¾ å - **æ±æ»å¤§è¡¨ 8 æå¡«å¼ï¼æ°æ®é©±å¨ M2/M3ï¼å°æªè½å°**ï¼å½åæ´æ¬ä»æ¯âå¤å¶æ¯ç+å¤ä»½è¿åâï¼å¯¼å ¥ç 8 ææ°æ®ä¸ä¼èªå¨è¿æ´æ¬ï¼ä¸ä¸æ¥å®ç° SummaryFiller 读åºåå¡«ï¼å½æ/累计/忝/æåï¼ã - 8 æç¼ºæ°ï¼èªå¨å¡«å¼åç½®ï¼ï¼freight_turnover_importãscale_split_transportãpassenger_individual_monthlyãwyc_total/order åæ 2026-08 è¡ï¼è´§è¿ä¼ä¸ææ¥ãå ¬è·¯æ 客ãå ¬äº¤ã巡游åºç§å·²æ 08ï¼ã - è¿æ¿âæ´è½¦/å ¶ä»è½¦âåç±»ç²åº¦ï¼RULE_001 æ´è½¦æ®µç°æè¿æ¿ otherï¼çµå¼/æè½¦ä»¥å¤ï¼å£å¾æ¯å¯¹ï¼è¥ä¸å¡è¦æ±âæ´è½¦=ä¸å«éè£ ç®±âéè¿æ¿ä¾§æ°æ®é åã - å¾ ç¨æ·ç¹éªï¼å¯¼å ¥å¼¹çª UI ææï¼RULE_001 å½ä¸éå¼ï¼5 å°/50 å¨ï¼æ¯å¦ç¬¦åä¸å¡é¢æã ## ææ¥è®¡å - å¯å¨ M2 SummaryFillerï¼å 7 æåå½ diff=0ï¼å 8 æå®å¼¹ï¼ï¼è¡¥é½ 8 æè´§è¿éå¨è½¬é/è§ä¸è§ä¸/个ä½å®¢è¿ç缺æ°åæ´é¾éªè¯ã traffic-audit-server/src/main/java/com/trafficaudit/auditengine/service/AuditEngineService.java
@@ -1569,6 +1569,8 @@ switch (rule.getCompareType()) { case "DIFF": return checkDiff(rule, report, authMap); case "DIFF_FIELDS": return checkDiffFields(rule, report, authMap); case "RATIO": return checkRatio(rule, report, authMap, trackMap); case "MOM": @@ -1609,6 +1611,86 @@ "å·®è·â¤" + fmt(threshold), "å·®è·=" + fmt(diff)); } return null; } /** RULE_001 åé¡¹è¿æ¿å¯¹æ¯ï¼çµå¼è½¦/æè½¦/æ´è½¦è½¦è¾æ°ä¸æè½¦å¨ä½/æ´è½¦å¨ä½/æ»å¨ä½åå«ä¸è¿æ¿å¯¹æ¯ï¼ä»»ä¸åé¡¹è¶ é峿é */ private AuditResult checkDiffFields(AuditRule rule, H2032EnterpriseMonthly report, Map<String, TransportAuthVehicle> authMap) { TransportAuthVehicle auth = findAuth(authMap, report.getEnterpriseName()); if (auth == null) return null; String[] fields = rule.getCheckField() == null ? new String[0] : rule.getCheckField().split(","); String[] limits = rule.getThreshold() == null ? new String[0] : rule.getThreshold().split("[,;|]"); if (fields.length == 0 || limits.length != fields.length) return null; java.util.List<String> hits = new java.util.ArrayList<>(); double maxAbs = 0; for (int i = 0; i < fields.length; i++) { String field = fields[i].trim(); double limit; try { limit = Double.parseDouble(limits[i].trim()); } catch (Exception e) { return null; } double actual; double reference; String unit; switch (field) { case "vehicleTractor": actual = num(report.getVehicleTractor()); reference = num(auth.getTractorCount()); unit = "å°"; break; case "vehicleTrailer": actual = num(report.getVehicleTrailer()); reference = num(auth.getTrailerCount()); unit = "å°"; break; case "tonsTrailer": actual = nvl(report.getTonsTrailer()); reference = nvl(auth.getTrailerTons()); unit = "å¨"; break; case "vehicleWhole": actual = num(report.getVehicleWhole()); reference = num(auth.getOtherCount()); unit = "å°"; break; case "tonsWhole": actual = nvl(report.getTonsWhole()); reference = nvl(auth.getOtherTons()); unit = "å¨"; break; case "tonsTotal": actual = nvl(report.getTonsTotal()); reference = nvl(auth.getTrailerTons()) + nvl(auth.getOtherTons()); unit = "å¨"; break; default: return null; } double diff = Math.abs(actual - reference); maxAbs = Math.max(maxAbs, diff); if (diff > limit) { hits.add(h2032FieldLabel(field) + "ï¼ä¸æ¥=" + fmt(actual) + " " + unit + "ï¼è¿æ¿=" + fmt(reference) + " " + unit + "ï¼å·®" + fmt(diff) + " " + unit + "ï¼é" + fmt(limit) + " " + unit + "ï¼"); } } if (hits.isEmpty()) return null; StringBuilder detail = new StringBuilder(); for (int i = 0; i < hits.size(); i++) { String one = hits.get(i); if (detail.length() == 0) { detail.append(one); } else if (detail.length() + one.length() + 1 <= 180) { detail.append("ï¼").append(one); } else { detail.append("ï¼ç").append(hits.size() - i).append("é¡¹è¶ é"); break; } } return buildResult(rule, report, detail.toString(), "çµå¼è½¦/æè½¦/æ´è½¦è½¦è¾å·®â¤5å°ï¼æè½¦å¨ä½/æ´è½¦å¨ä½å·®â¤50å¨ï¼æ»å¨ä½å·®â¤50å¨", "è¶ åºé¡¹æ°=" + hits.size() + "ï¼æå¤§å·®=" + fmt(maxAbs)); } /** 䏿¥å¨è½¬éä¸è½¨è¿¹æµç®å¨è½¬é对æ¯ï¼é«åºé弿¯ä¾æ ¸å® */ @@ -1804,6 +1886,10 @@ labels.put("tonsWhole", "æ´è½¦å¨ä½"); labels.put("turnoverTotal", "å¨è½¬é"); labels.put("vehicleWhole", "æ´è½¦è½¦è¾æ°"); labels.put("vehicleTractor", "çµå¼è½¦æ°"); labels.put("vehicleTrailer", "æè½¦æ°"); labels.put("tonsTrailer", "æè½¦å¨ä½"); labels.put("tonsWhole", "æ´è½¦å¨ä½"); String v = labels.get(field); return v == null ? field : v; } traffic-audit-server/src/main/java/com/trafficaudit/reportexport/service/ReportExportService.java
@@ -3234,7 +3234,6 @@ dynamicSummaryYear(wb, year); cleanSummarySheetPresentation(wb); // åæ¶å页çéãè¿åéèè¡ï¼å¦åâç线å 车âç页çèµ·æ¥åç¼ºæ°æ®ï¼ applyTwoDecimalFormat(wb); // æ°å¼æ¾ç¤ºä¸¤ä½å°æ°ï¼ä¿çå ¨ç²¾åº¦ï¼ï¼% / æ¥æçæ¢ææ ·å¼ä¸æ¹å autoFitContentColumns(wb); // ææ°æ®èªå¨å 宽å宽ï¼åªå 宽ä¸ç¼©çªï¼è·³è¿åå¹¶æ 颿 ¼ï¼é²é¿ææ¬æååæçï¼ if (!wb.getCTWorkbook().isSetCalcPr()) wb.getCTWorkbook().addNewCalcPr(); wb.getCTWorkbook().getCalcPr().setFullCalcOnLoad(true); // è¿å/æ¹åå ¬å¼åæå¼å³éç® return toBytes(wb); @@ -4822,59 +4821,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); traffic-audit-server/src/main/java/com/trafficaudit/rulemanage/controller/RuleManageController.java
@@ -64,19 +64,22 @@ return Result.ok("å·²ä¿å"); } /** % åçæ°å¼ï¼åç§°/说æä¸ééå¼å¨æååççæ®µï¼% åå¯è½æ¯ ±ã>ãè¶ è¿ãé«åº çæè¿°è¯ï¼ä» æ¿æ¢æ°åæ¬èº«ï¼ */ private static final Pattern PCT_NUM = Pattern.compile("\\d+(?:\\.\\d+)?(?=%)"); /** åç§°/说æä¸ééå¼å¨æååçæ°åçæ®µï¼å¹é ä»»ææ°åï¼% ååãå°/å¨/å ¬éçåä½ååå¯ï¼ */ private static final Pattern NUM_TOKEN = Pattern.compile("\\d+(?:\\.\\d+)?"); /** è§£æéå¼å符串ä¸ç¬¬ä¸ä¸ªæ°å¼ï¼æ æ³è§£æè¿å null */ private Double parseThresholdNum(String threshold) { if (threshold == null || threshold.trim().isEmpty()) return null; java.util.regex.Matcher m = Pattern.compile("\\d+(?:\\.\\d+)?").matcher(threshold.trim()); if (!m.find()) return null; try { return Double.parseDouble(m.group()); } catch (Exception e) { return null; /** è§£æéå¼å符串ä¸çæææ°å¼ï¼æ¯æâ5,5,50âç夿®µéå¼ï¼å¦ RULE_001 åé¡¹è¿æ¿å¯¹æ¯ï¼ */ private java.util.List<Double> parseThresholdNums(String threshold) { java.util.List<Double> out = new java.util.ArrayList<>(); if (threshold == null || threshold.trim().isEmpty()) return out; java.util.regex.Matcher m = NUM_TOKEN.matcher(threshold.trim()); while (m.find()) { try { out.add(Double.parseDouble(m.group())); } catch (Exception e) { // å¿½ç¥æ æ³è§£æç段 } } return out; } /** æ°å¼å±ç¤ºï¼æ´æ°å»æå°æ°ä½ï¼å ¶ä½ä¿çå精度 */ @@ -86,10 +89,11 @@ return new java.math.BigDecimal(String.valueOf(v)).stripTrailingZeros().toPlainString(); } /** ç±é弿°å¼æ¨å¯¼â说ææåéåçæ¯ç¾åä¹å âçåéå£å¾ï¼ /** ç±é弿°å¼æ¨å¯¼âåç§°/说æéåçæ¯ç¾åä¹å âçåéå£å¾ï¼ * â é弿¬èº«å°±æ¯ç¾åæ°ï¼å¦ 20 â ±20%ï¼ï¼ * â¡ é弿¯å°æ°æ¯ä¾ï¼å¦ 0.02 â 2%ã0.5 â 50%ï¼ï¼æ¤æ¶ <1 æå¯ç¨ï¼ * ⢠é弿¯æ¯å¼ï¼å¦ 1.3 â é«åº30%ï¼ï¼æ¤æ¶ >1 æå¯ç¨ã */ * ⢠é弿¯æ¯å¼ï¼å¦ 1.3 â é«åº30%ï¼ï¼æ¤æ¶ >1 æå¯ç¨ï¼ * å ¶ä½æåæ°å¹é ï¼å°/å¨/å ¬éçéå¼åæ ·åºç°å¨æåä¸ï¼ã */ private double[] thresholdPercentCandidates(double v) { double[] cands; if (v > 0 && v < 1) { @@ -102,16 +106,23 @@ return cands; } /** ææ§éå¼å¹é ææ¬ä¸å·²æçç¾åæ¯ç段ï¼ä» æ¿æ¢â䏿§éå¼å¯¹å¾ä¸âçæ°å为æ°éå¼å¯¹åºæ°åï¼å¯¹ä¸ä¸çæ°åï¼å¦è¯´æéä¸é弿 å ³çæ°å¼ï¼ä¿æä¸å¨ */ /** æâæ§éå¼âæ°éå¼âéæ®µæ¹ååç§°/说æä¸çæ°åï¼ * åªæ¿æ¢ä¸æ§é弿䏿®µï¼æå ¶ç¾åæ¯å£å¾ï¼å¯¹å¾ä¸çæ°åï¼å¤æ®µéå¼ï¼éå·åéï¼é段对åºï¼ * ä¸é弿 å ³çæ°åï¼æ¥æãä¸è§åæ å ³çåºå®æ°å¼çï¼ä¿æä¸å¨ã */ private String decorateThresholdText(String text, String oldThreshold, String newThreshold) { if (text == null || text.trim().isEmpty()) return text; Double oldNum = parseThresholdNum(oldThreshold); Double newNum = parseThresholdNum(newThreshold); if (oldNum == null || newNum == null) return text; double[] oldCands = thresholdPercentCandidates(oldNum); double[] newCands = thresholdPercentCandidates(newNum); if (oldCands.length != newCands.length) return text; java.util.regex.Matcher m = PCT_NUM.matcher(text); java.util.List<Double> olds = parseThresholdNums(oldThreshold); java.util.List<Double> news = parseThresholdNums(newThreshold); if (olds.isEmpty() || olds.size() != news.size()) return text; int n = olds.size(); double[][] oc = new double[n][]; double[][] nc = new double[n][]; for (int i = 0; i < n; i++) { oc[i] = thresholdPercentCandidates(olds.get(i)); nc[i] = thresholdPercentCandidates(news.get(i)); if (oc[i].length != nc[i].length) return text; } java.util.regex.Matcher m = NUM_TOKEN.matcher(text); StringBuffer sb = new StringBuffer(); while (m.find()) { double token; @@ -121,10 +132,13 @@ continue; } String repl = null; for (int i = 0; i < oldCands.length; i++) { if (Math.abs(oldCands[i] - token) < 1e-9) { repl = fmtNum(newCands[i]); break; outer: for (int i = 0; i < n; i++) { for (int j = 0; j < oc[i].length; j++) { if (Math.abs(oc[i][j] - token) < 1e-9) { repl = fmtNum(nc[i][j]); break outer; } } } if (repl == null) continue; @@ -134,7 +148,7 @@ return sb.toString(); } /** åç§°/说æééå¼åæ¥ï¼update æ¶æ æ§âæ° æ¹åå¹¶æä¹ åï¼ */ /** åç§°/说æééå¼åæ¥ï¼update æ¶æ æ§âæ° æ¹åå¹¶æä¹ åï¼å¤æ®µéå¼åæ ·éæ®µèå¨ï¼ */ private void syncThresholdText(AuditRule rule, String oldThreshold) { if (rule == null || rule.getThreshold() == null) return; rule.setRuleName(decorateThresholdText(rule.getRuleName(), oldThreshold, rule.getThreshold())); @@ -145,4 +159,4 @@ private void syncThresholdText(AuditRule rule) { syncThresholdText(rule, rule == null ? null : rule.getThreshold()); } } } traffic-audit-web/src/views/DataImport.vue
@@ -372,6 +372,7 @@ message: '' } this.importOne(row).then(() => { this.showImportResult(row) if ((row.type === 'investment' || row.type === 'investmentLogistics') && row.status === 'success') { this.checkInvest(this.period) } @@ -466,6 +467,20 @@ }) .finally(() => { this.cityDirLoading = false }) }, showImportResult(row) { if (!row || !row.status) return const msg = (row.message || '').toString() if (row.status === 'success') { const text = (msg && msg.indexOf('å¯¼å ¥') !== -1) ? msg : 'æ°æ®å¯¼å ¥æå' this.$message({ message: text, type: 'success', duration: 5000 }) } else { this.$alert((msg || 'æ°æ®å¯¼å ¥å¤±è´¥ï¼è¯¦è§ä¸æ¹å¤±è´¥è®°å½').replace(/\n/g, '<br>'), 'æ°æ®å¯¼å ¥å¤±è´¥', { dangerouslyUseHTMLString: true, type: 'error', confirmButtonText: 'ç¥éäº' }) } }, importOne(row) { if (!this.period) { this.$message.warning('请å éæ©æ¥è¡¨æ'); return Promise.resolve() } row.status = 'uploading'