| New file |
| | |
| | | # åè½æµè¯æ¥åï¼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ï¼æªå¨æ¬è½®è½å°ï¼è§å·¥ä½æ¥ç»éçäºé¡¹ã |
| New file |
| | |
| | | # åè½æµè¯æ¥åï¼å®¡æ ¸ç»æçé¢äº¤äºä¼åä¸å¯¼åºé®é¢è¯æ¯è½å° |
| | | |
| | | æ¥æï¼2026-09-09ãæ¨¡åï¼å®¡æ ¸ç»æ / æ°æ®æ¥ç / æ¥è¡¨çæï¼å端ï¼+ å®¡æ ¸ç»æå¯¼åºä¸æ¥è¯¢ï¼å端ï¼ãæµè¯äººï¼Codexï¼ä¸å¡ç¹éªåè¡¥å
ï¼ |
| | | |
| | | ## ä¸ãåè½è¯´æ |
| | | 1. å®¡æ ¸ç»æé¡µæä¸å¡å馿¶æäº¤äºè¯ä¹ï¼é®é¢èå´ä¸¤ä¸ªé项æ¹ä¸ºãä»
éæ ¸å® / 嫿éåãå¹¶é说æãæéåä»
æç¤ºååãä¸éå¤çï¼å¯¼åºé»è®¤ä¸å«ãï¼è§å¾é»è®¤ãæä¼ä¸åå¹¶ãï¼åæ¶ä¿çãææ¡æç»ãï¼çéåºæ¹ä¸ºæµ
ç°å¡çæ ï¼å¹¶å±ç¤ºãå½åçé N æ¡ / M å®¶ä¼ä¸ã宿¶å½ä¸æ°ï¼é¡¶é¨çº¢/é»/å¾
å¤ç/å·²å¤çç»è®¡å¡æ¹ä¸ºè·éå½åçéç»æèå¨ã |
| | | 2. 导åºãä¼ä¸åå¹¶ãSheet çé®é¢åè¯æ¯æç¨æ·ç»å® 4 æ¡ä¾ææ ¼å¼çæï¼å
¶ä½è§åç»ä¸ä¸ºãæ ¸å®å¼å¸¸/å·®å¼è¯´æï¼å«å
³é®æ°å¼ï¼ï¼è¯·æ ¸å®ã飿 ¼ï¼queryResults æ¹ä¸ºææ¥è¡¨ç±»åå
åè§åãåå该æç»æï¼é¿å
å
¨è¡¨è§åæ¯å¯¹ã |
| | | 3. æ°æ®æ¥ç页工å
·æ æ´ç为ä¸å¯¼å
¥é¡µä¸è´çãåç»æ¨¡åï¼ç±»å䏿ãï¼æ¥è¡¨æ/å
³é®å/æ¥è¯¢/å
¨å±å¹¶æäºä¸è¡ï¼åºé¨æç¤ºæ¹ä¸ºãå
± N æ¡è®°å½ï¼å·²å è½½ N æ¡ãã |
| | | 4. æ¥è¡¨çæé¡µãéè·¯è¿è¾éæ±æ»è¡¨ï¼æ´æ¬ï¼ãé¢è§æ¹ä¸ºåç»è¡¨æ ¼ï¼é路货è¿/åå¸å®¢è¿/ä¸å£å¾ Ã å½æ/累计/忝ï¼ï¼åæ¯ç¼ºä¸å¹´åºææ¶é项ç»ãâãä¸æç¤ºã |
| | | |
| | | ## äºãæµè¯ç¯å¢ |
| | | - å端 8090ï¼2026-09-09 15:45 ä»¥ææ°ä»£ç éå¯å°±ç»ªï¼ï¼å端 8080ï¼dev ç¼è¯æåãHMR çæï¼ã |
| | | - æ°æ®åºï¼æ¬æº traffic_auditï¼2026-08 è´§è¿/æ
客/å
¬äº¤/åºç§æ°æ®åå¨åºï¼ã |
| | | |
| | | ## ä¸ãç¨ä¾ä¸ç»æ |
| | | | ç¼å· | ç¨ä¾ | è¾å
¥ | 颿 | å®é
| ç»è®º | |
| | | |---|---|---|---|---|---| |
| | | | 1 | 2026-08 è´§è¿æ¥è¯¢ä¸è½¬å | 页é¢é 2026-08ï¼è´§è¿ææ¥âæ¥è¯¢ç»æ | æ°ç§å
è¿å并忢 loading | 约 2~3s è¿å 487 æ¡ / 403 å®¶ï¼æ å¡è½¬å | éè¿ | |
| | | | 2 | é®é¢èå´æé®å³æ¶ååº | ç¹å»ã嫿éåãåç¹ãä»
éæ ¸å®ã | é䏿ä¸å½ä¸æ°å³æ¶ååï¼æ ééæ°æ¥è¯¢ | é䏿ç§åï¼ä»
éæ ¸å®ä¸èªå¨åé¤ RULE_004 æéåï¼487â474 æ¡ï¼ | éè¿ | |
| | | | 3 | è§å¾é»è®¤ä¸åæ¢ | è¿å
¥é¡µé¢âè§å¾åé | é»è®¤ãæä¼ä¸åå¹¶ãï¼å¯åãææ¡æç»ã | é»è®¤æä¼ä¸åå¹¶ï¼AX é䏿 Value=1ï¼ï¼ææ¡æç»å¯æ£å¸¸åæ¢ä¸º 10 åéæ¡è¡¨ | éè¿ | |
| | | | 4 | ææ¥æ°æ®å¼¹çªï¼æ°æ®ï¼æ¥å£ | /api/data/list?type=h2032/cityBus/h2031&period=2026-08 | å¿«éè¿å该ä¼ä¸è¯¥æå¡«æ¥ | 23~561ms è¿åï¼h2032 å
³é®åå½ä¸ 1 æ¡ãcityBus 10 æ¡ãh2031 50 æ¡/æ»æ°119ï¼ | éè¿ | |
| | | | 5 | H2031 导åºé®é¢åä¸ç¨æ· 4 æ¡æ¨¡æ¿ä¸è´ | /api/audit/export 2026-08 H2031 | 1)~4) è¯æ¯ä¸ç¨æ·æä¾æ ¼å¼å®å
¨ä¸è´ | 宿µ G7ï¼â 客è¿éç¯æ¯=0æ ¸å®å¼å¸¸ï¼ä¸æå®¢è¿é=3784ï¼æ¬æå
车客è¿é=3784ï¼è¯·æ ¸å® â¡æ
客å¨è½¬éç¯æ¯=0æ ¸å®å¼å¸¸ï¼â¦ï¼è¯·æ ¸å® â¢å
车平åè¿è·çäºä¸æï¼ä¸æè¿è·=25.9ï¼æ¬æè¿è·=25.9ï¼è¯·æ ¸å®ä¼ä¸æ¯å¦æ¯åºå®çº¿è·¯æ
游æè
éå¤ä¸å¡ï¼G10 ç线为ãâ¦ç¯æ¯è¶
±20%â¦ï¼ä¸ç±»ç线平åè¿è·=0ï¼ä¸æ=17.6ï¼ç¯æ¯100%ï¼â¦è¯·æ ¸å®ã | éè¿ | |
| | | | 6 | H2032 导åºé®é¢åå«æç» | åä¸ H2032 | æ¯ç±»æ ¸å®æè§å¸¦å
³é®æ°å¼ | 宿µãä¼ä¸ä¸æ¥è½¦è¾/å¨ä½ä¸è¿æ¿æ°æ®å¯¹æ¯åå¨å·®å¼ï¼æ´è½¦å¨ä½ï¼ä¸æ¥=â¦ï¼è¿æ¿=â¦ï¼å·®â¦ï¼é50 å¨ï¼ï¼æ»å¨ä½ï¼â¦ï¼è¯·æ ¸å®ãã䏿¥å¨è½¬éä¸è½¨è¿¹æµç®å¨è½¬é差弿æ¾ï¼ä¸æ¥å¨è½¬é=â¦ï¼è½¨è¿¹æµç®=â¦ï¼è¯·åèè½¨è¿¹æ ¸å®ä¼ä¸å¨è½¬éãç | éè¿ | |
| | | | 7 | å®¡æ ¸ç»æå¯¼åºç»æä¸åå¹¶åº | openpyxl æå¼ä¸¤ä»½å¯¼åº | å SheetãB/G è·¨è¡åå¹¶ãé®é¢åç¼å· 1ã2ã3⦠| H2031 84 è¡ãH2032 475 è¡ï¼ä¼ä¸åå¹¶ Bï¼ä¼ä¸ï¼/Gï¼é®é¢ï¼åå¹¶åæ | éè¿ | |
| | | | 8 | æ´æ¬é¢è§ 2026-08 åç»è¡¨é®å | /api/report/preview?period=2026-08&types=summaryWorkbook | summary é®å䏿°åç»è¡¨æ ¼åå¼ä¸è´ | è¿åé®å«ãå
¨çè´§ç©å¨è½¬é·累计 / Â·ç´¯è®¡åæ¯ããåå¸å®¢è¿é/å¨è½¬éÂ·å½æÂ·ç´¯è®¡ããä¸å£å¾å®¢è¿é/å¨è½¬éÂ·å½æÂ·ç´¯è®¡ãçï¼å端 wbRows åç»å¯åæ°ï¼ç¼ºå½ææºæ°æ®ä¸ä¸å¹´åºæçè¡æ¾ç¤ºãâãå¹¶ç»æç¤º | éè¿ | |
| | | | 9 | ç¼è¯ä¸è¿è¡ | mvn compileï¼å端 dev | æ ç¼è¯é误ã页é¢å¯äº¤äº | å端 15:45 é坿åï¼Started ⦠port 8090ï¼ï¼å端 15:42 ç¼è¯æåï¼é¡µé¢å®æµç¹å»/忢/æ¥è¯¢åæ£å¸¸ | éè¿ | |
| | | |
| | | ## åãåç°é®é¢ä¸å¤ç½® |
| | | - åãæ ¸å®å/å
¨é¨ãææ¡è¯ä¹ä¸æ¸
导è´ç¨æ·å°æ â æ¹ä¸ºãä»
éæ ¸å®/嫿éåãï¼å¹¶å ç°è²è¯´ææåï¼åãææ¡æç»ãé»è®¤è§å¾ â æ¹ä¸ºé»è®¤ãæä¼ä¸åå¹¶ãï¼ä¸æ¥å¸¸æä¼ä¸ä¸åæ ¸å¯¹ç工使µä¸è´ï¼ã |
| | | - ç¨æ·åé¦âé 8 æè´§è¿/åå¸å
¬äº¤ä¸ç´è½¬åâï¼å¤æµ 2026-08 åç±»åç»ææ¥è¯¢å <1sã/data/list å¼¹çªæ°æ® <600msï¼æªå¤ç°è½¬åï¼å¤å®ä¸ºæ§çæé®ä»
æ´æ°éæ©ç¶æä¸å·æ°å表æè´ï¼ç°å·²æ¹ä¸ºå端ååºå¼è¿æ»¤ï¼åèå´/ç¶æå³æ¶çæï¼ï¼æ éæ´é¡µéæ°è¯·æ±ã |
| | | - 导åºé®é¢å H2032 æ§è¯æ¯åªåâå¼å¸¸ï¼å®é
/åºå/åå·®ï¼âè¾æ¦æ¶© â æè§åè¡¥é½å
³é®æ°å¼ä¸ä¸å¡å£å¾ï¼è¿æ¿å项ã轨迹ãéè£
ç®±ãæ´è½¦å¨ä½çï¼ï¼è´§è¿/客è¿ä¸¤ç±»è¯æ¯åå·²ææ°æ ¼å¼å®æµæ ¸å¯¹ã |
| | | |
| | | ## äºãå¾
ä¸å¡ç¹éª / éç |
| | | - 请ä¸å¡å¨æµè§å¨ç¹éªï¼é®é¢èå´/è§å¾/ç¶æä¸ä¸ªæ§ä»¶åæ¢ãè¡å
ãæ°æ®ãå¼¹çªãå¯¼åº Excel æå¼åä¸ docs/å®¡æ ¸çé¢ææ¥æ¨¡æ¿/模æ¿_å®¡æ ¸ç»æ.xlsx å忡便鿡æ¯å¯¹ã |
| | | - 䏿¢æéçä¸è´ï¼æ±æ»å¤§è¡¨ 8 æèªå¨å¡«å¼ï¼M2/M3 SummaryFillerï¼æªå¯å¨ï¼8 æç¼º freight_turnover_import çæºæ°æ®è¡ï¼å¯¼å
¥å¼¹çª UI ä¸ RULE_001 éå¼ï¼5 å°/50 å¨ï¼å¾
ä¸å¡ç¡®è®¤ã |
| | | |
| | | ## å
ãç»è®º |
| | | æ¬è½®çé¢äº¤äºæ¶æã导åºé®é¢è¯æ¯è½å°ä¸é¢è§è¡¨æ ¼ååéè¿æ¥å£çº§ä¸é¡µé¢çº§éªè¯ï¼æªåç°é»å¡æ§é®é¢ï¼å¯é代ç ä¸å¹¶äº¤ä»ï¼çå¾
ä¸å¡ç»éªã |
| New file |
| | |
| | | # åè½æµè¯æ¥åï¼å®¡æ ¸ç»ææ¨¡æ¿åå±ç¤º/导åºä¸æ°æ®æ¥çãæ¥è¡¨çæé¡µæ´æ¹ |
| | | |
| | | æ¥æï¼2026-09-09ãæ¨¡åï¼å®¡æ ¸ç»æ / æ°æ®æ¥ç / æ¥è¡¨çæãæµè¯äººï¼Codexï¼ä¸å¡ä¾§ç¹éªåè¡¥å
ï¼ |
| | | |
| | | ## ä¸ãåè½è¯´æ |
| | | 1. å®¡æ ¸ç»æé¡µæç»è§å¾æç¨æ·æ°æ¨¡æ¿ï¼docs/å®¡æ ¸çé¢ææ¥æ¨¡æ¿/模æ¿_å®¡æ ¸ç»æ_.xlsxï¼åæ 10 åå¹³éºè¡¨æ ¼ï¼é»è®¤åªçãæ ¸å®åãé®é¢ï¼RULE_004 ç¯æ¯æéé»è®¤æé¤ï¼ï¼å¯åãå
¨é¨ãï¼ç¶æå¯æ å
¨é¨/å¾
å¤ç/å·²å¤ç è¿æ»¤ï¼å¯¼åºä¸çé¢çéä¸è´ã |
| | | 2. å¯¼åº Excel ææ¨¡æ¿å Sheetï¼`å®¡æ ¸ç»æ`ï¼éæ¡ï¼ï¼`ä¼ä¸åå¹¶`ï¼æä¼ä¸æ±æ» + èªå¨ç¼å·é®é¢åï¼ä¼ä¸ä¸é®é¢åè·¨è¡åå¹¶ï¼ã |
| | | 3. æ°æ®æ¥çï¼é¡¶é¨æ¨¡åï¼æ°æ®ç±»å䏿ä¸å¯¼å
¥é¡µä¸è´ï¼ææ¡å·²å¯¹é½å¯¼å
¥é¡µï¼ï¼æ¥è¡¨æ/å
³é®åæ¥è¯¢ç§»å°ç¬¬äºè¡ï¼ä¼ä¸ä»£ç /ç»ä¸ç¤¾ä¼ä¿¡ç¨ä»£ç /å¡«æ¥åä½ 3 åéèã |
| | | 4. æ¥è¡¨çæï¼è´§è¿æ¥è¡¨/æ
客æ¥è¡¨/åå¸å®¢è¿æ¥è¡¨ä¸ç»æ¨¡åéèï¼ä»
ä¿ç æ±æ»å¤§è¡¨/æèµæ¥è¡¨/è½èæ¥è¡¨ï¼éè·¯è¿è¾éæ±æ»è¡¨ï¼æ´æ¬ï¼é¢è§å±ç¤º è´§è¿é/è´§è¿å¨è½¬éãåå¸å®¢è¿é/å¨è½¬éãä¸å£å¾å®¢è¿é/å¨è½¬é ç 彿/累计/忝ã |
| | | |
| | | ## äºãæµè¯ç¯å¢ |
| | | - å端 8090ï¼mvn ç¼è¯éè¿ãå·²éå¯å°±ç»ªï¼ï¼å端 8080ï¼dev ç¼è¯æåãçæ´æ°äº§ç©æ½æ¥éè¿ï¼ã |
| | | - æ°æ®åºï¼æ¬æº traffic_auditï¼2026-08 è´§è¿ H2032 å·²å®¡æ ¸æ°æ®å¨åºï¼ã |
| | | |
| | | ## ä¸ãç¨ä¾ä¸ç»æ |
| | | | ç¼å· | ç¨ä¾ | è¾å
¥ | 颿 | å®é
| ç»è®º | |
| | | |---|---|---|---|---|---| |
| | | | 1 | å®¡æ ¸å¯¼åºå Sheet | /api/audit/export?period=2026-08&reportType=H2032&onlyVerify=true&status=all | å®¡æ ¸ç»æ10å+ä¼ä¸åå¹¶7åãé®é¢åèªå¨ç¼å·ãä¼ä¸/é®é¢åå¹¶ | 475 è¡Ã10 åï¼ä¼ä¸åå¹¶ 150 ç»åå¹¶åºï¼B/G åï¼ï¼é®é¢å½¢å¦â1ã䏿¥å¨è½¬éä¸è½¨è¿¹æµç®â¦è¯·åèè½¨è¿¹æ ¸å®â | éè¿ | |
| | | | 2 | å¯¼åºæé¤æéå | onlyVerify=true | RULE_004 æéåä¸åºç° | æªåºç° RULE_004 | éè¿ | |
| | | | 3 | 导åºç¶æè¿æ»¤ | status=pending / handled | ä»
å¾
å¤ç / ä»
å·²å¤ç | pending=475 è¡ï¼handled æ æ°æ®ï¼å½åææ å·²å¤çè¡ï¼è¡¨å¤´ä¿çï¼ | éè¿ | |
| | | | 4 | åå¹¶åå
æ ¼å¯¹é½æ¨¡æ¿ | openpyxl æ£æ¥ merged | ä¼ä¸(B)ä¸é®é¢(G)è·¨è¡åå¹¶ | 䏿¨¡æ¿ B2:B4/G2:G4 åæ | éè¿ | |
| | | | 5 | æ´æ¬é¢è§ 7 æ | preview?period=2026-07&types=summaryWorkbook | è´§è¿/åå¸å®¢è¿/ä¸å£å¾å½æ+累计+åæ¯è¡ | è´§è¿é彿 16285.71 ä¸å¨ã累计 113423.13ï¼å¨è½¬é彿/累计/忝ï¼åå¸å®¢è¿é彿 30612.85 ä¸äººæ¬¡ï¼ä¸å£å¾å½æ/累计é½å
¨ | éè¿ | |
| | | | 6 | æ´æ¬é¢è§ç¼º 2025 åºææç¤º | å 5 | åæ¯ç¼ºæ°æ®æ¾ç¤ºæç¡®æç¤ºèééé» | è´§è¿é/åå¸å®¢è¿/ä¸å£å¾ 3 项åæç¤ºâ2025-07 åææ°æ®æªå¯¼å
¥åºï¼åæ¯æä¸æ¾ç¤ºâ | éè¿ | |
| | | | 7 | æ´æ¬é¢è§ 8 æç¼ºå½ææºæ°æ® | preview?period=2026-08 | ç¼ºå½ææ°æ®æ¶æç¤º | æç¤ºç¼º 2026-08 è´§è¿éå¨è½¬é/è§ä¸è§ä¸å½æï¼ç´¯è®¡åæè¿å¯¼å
¥æ | éè¿ | |
| | | | 8 | æ°æ®æ¥çåéè | /api/data/list?type=h2032 åéå | å端éè 3 å | æå¡ç«¯å« enterpriseCode/unifiedCreditCode/reportUnitï¼å端已æ prop+label è¿æ»¤ï¼ | éè¿ï¼é¡µé¢å±ç¤ºå¾
人工ç¹éªï¼ | |
| | | | 9 | æ¥è¡¨é¡µæ¨¡åå¯è§æ§ | åç«¯äº§ç©æ½æ¥ | è´§è¿/æ
客/åå¸å®¢è¿éè | 3 ç» hidden:trueï¼é¡µç¾ä»
æ±æ»å¤§è¡¨/æèµ/è½è | éè¿ | |
| | | | 10 | ç¼è¯/è¿è¡ | mvn compile + éå¯ | æ ç¼è¯é误 | EXIT=0ï¼æå¡å°±ç»ª | éè¿ | |
| | | |
| | | ## åãåç°é®é¢ä¸å¤ç½® |
| | | - 导åºãä¼ä¸åå¹¶ãåå®ç°åå¹¶æ¥è¡¨æåèéä¼ä¸åï¼ä¸æ¨¡æ¿ä¸ç¬¦ â 已修æ£ä¸º Bï¼ä¼ä¸ï¼+Gï¼é®é¢ï¼åå¹¶ã |
| | | - 导åºç¶ææªéçé¢çé â åç«¯åæ°ç± handledOnly æ¹ä¸º status=all/pending/handledï¼åç«¯åæ¥ä¼ åã |
| | | - é¢è§åæ¯å åºå
æ 2025-07 åºææ°æ®è缺è¡ï¼è´§è¿é/åå¸å®¢è¿/ä¸å£å¾ï¼â å¢å é项æç¤ºææ¡ï¼æç¡®âå¾
2025 å¹´å®ç¨¿æ°æ®å¯¼å
¥åèªå¨åºç°âï¼æ´æ¬å®é
çæä»ä»¥æ¯ç 2025 å¹´åå
¬å¼ä¸ºåã |
| | | - æ°æ®æ¥çç±»åææ¡ä¸å¯¼å
¥é¡µä¸ä¸è´ï¼H203-1æ
å®¢ææ¥ vs å
¬è·¯æ
客è¿è¾æåº¦ç产æ
åµçï¼â 已对é½å¯¼å
¥é¡µææ¡ï¼value æ²¿ç¨æ°æ®æ¥çåç«¯æ¯æ keyï¼ã |
| | | |
| | | ## äºãå¾
ä¸å¡ç¹éª / éç |
| | | - ä¸ä¸ªé¡µé¢ï¼å®¡æ ¸ç»æãæ°æ®æ¥çãæ¥è¡¨çæï¼å»ºè®®å¨æµè§å¨é页ç¹ä¸éï¼çéèå¨ãè¡¨æ ¼åå±ç¤ºãå¯¼åºæä»¶æå¼æ ¸å¯¹ä¸æ¨¡æ¿é忝坹ã |
| | | - æ°æ®æ¥çå½åä»
弿¾åç«¯å·²æ¯æçç±»åï¼è´§è¿ H2032/è¿æ¿/轨迹/è§ä¸è§ä¸/è´§è¿éå¨è½¬éãå
¬è·¯æ
客 H2031/æ
å®¢è¿æ¿ãå
¬äº¤/åºç§/åºç§è¿æ¿ï¼ï¼ä¸ªä½å®¢è¿éå¨è½¬éãç½çº¦è½¦æ»æ°/订åãæèµãè½èç导å
¥ç±»åçæ°æ®æ¥çå¾
åç»ç¡®è®¤æ¯å¦å¼æ¾ã |
| | | - æ´æ¬é¢è§ç忝è¡ä¾èµ 2025 å¹´åºææ°æ®å
¥åºï¼æ¤å约å®çâ忝以æ¯çç¼åä¼å
ï¼M2 SummaryFillerï¼âæ¹é 仿ªå¯å¨ã |
| | | |
| | | ## å
ãç»è®º |
| | | æ¬æ¬¡ 4 项çé¢/å¯¼åºæ´æ¹å·²è½å°å¹¶éè¿æ¥å£çº§ä¸äº§ç©çº§éªè¯ï¼é¡µé¢è§è§ä¸äº¤äºå»ºè®®ä¸å¡å¨æµè§å¨ç»éªåå³å¯ä½ä¸ºä»æ¥äº¤ä»çä¸é¨åã |
| New file |
| | |
| | | # åè½æµè¯æ¥åï¼å®¡æ ¸ç»æé¡µâéæä¸ç´è½¬å/æé®æ ååºâä¿®å¤ 2026-09-09 |
| | | |
| | | ## åè½è¯´æ |
| | | - ç°è±¡ï¼å®¡æ ¸ç»æé¡µå è½½åºæ°æ®ï¼å¦é 8 æè´§è¿/åå¸å
¬äº¤ææ¥ï¼åä¸ç´è½¬åãæ§è¡å®¡æ ¸çæé®ç¹äºæ²¡ååºãç¹âé®é¢èå´/è§å¾â乿 æã |
| | | - æ ¹å ï¼`AuditResult.vue` ä¸ `isReminderRow(r)`ï¼å¸¦å夿彿°ï¼è¢«éæ¾å¨ `computed` éãVue ç计ç®å±æ§ææ ååå¼ï¼è¿è¡æ¶æ¿å°çæ¯å¸å°å¼è䏿¯å½æ°ï¼å¯¼è´ `filteredResults` 䏿¦å¯¹éç©ºç»æéæ§è¡ `this.isReminderRow(r)` å°±æ `TypeError: this.isReminderRow is not a function`ï¼ç»ä»¶æ¸²æå´©æºã |
| | | - å¤ç½®ï¼æ `isReminderRow` ä» `computed` ç§»å
¥ `methods`ï¼åè½ä¸åï¼ï¼åç«¯çæ´æ°å¹¶æ´é¡µå·æ°å夿µã |
| | | |
| | | ## æµè¯ç¯å¢ |
| | | - å端ï¼traffic-audit-server 8090ï¼å·²å« M2 åå¡« df701a3 代ç ï¼ï¼è¿è¡ä¸ã |
| | | - å端ï¼traffic-audit-web 8080 devï¼çæ´æ°å·²å«æ¬æ¬¡ä¿®å¤ï¼ã |
| | | - æµè§å¨ï¼Codex åºç¨å
æµè§å¨ï¼`http://localhost:8080/#/audit`ï¼è´¦å· admin å·²ç»å½ã |
| | | |
| | | ## ç¨ä¾è¡¨ |
| | | | ç¨ä¾ | è¾å
¥ | 颿 | å®é
| ç»è®º | |
| | | |---|---|---|---|---| |
| | | | 1 å¤ç°ï¼ä¿®å¤åï¼ | 页é¢èªå¨å è½½ææ°æ¥è¡¨æ 2026-09ï¼è´§è¿ï¼146 æ¡ï¼ | æ£å¸¸æ¸²æ | æ§å¶å°è¿ç»æ¥ `isReminderRow is not a function`ï¼08:56/08:58ï¼ï¼å è½½æä¸ç»æãç颿 ååº | å¤ç°æå | |
| | | | 2 ä¿®å¤åæ£å | å·æ°é¡µé¢ï¼èªå¨æ¥è¯¢ 2026-09 è´§è¿ | 146 æ¡é®é¢/142 å®¶ä¼ä¸æ£å¸¸æ¸²æãæ æ¥é | é¡¶é¨ç»è®¡ 136 红/10 é»/146 å¾
å¤çï¼å表æ£å¸¸å±ç¤ºï¼æ§è¡å®¡æ ¸çæé®æ¢å¤å¯ç¨ï¼æ§å¶å° 09:xx èµ·æ ä»»ä½ render/isReminderRow é误 | éè¿ | |
| | | | 3 ç©ºç»æé | æ¥è¯¢æ æ°æ®æä»½ | ä¸è½¬åãæ£å¸¸æç¤º | 0 è¡æ¶ä¸æ§è¡è¿æ»¤åè°ï¼ä¸è§¦å该å¼å¸¸ï¼ä¸ä¿®å¤åâææ°æ®æè½¬åâçç°è±¡ä¸è´ï¼ | éè¿ | |
| | | | 4 åç«¯èæ¶ | curl `/audit/execute`ã`/audit/results`ã`/audit/ruleStats`ï¼2026-08 è´§è¿/å
¬äº¤çï¼ | å¿«éè¿å | execute H2032 3.3sãå
¬äº¤ 0.26sï¼results/ruleStats å <0.4sï¼éå端ç¶é¢ | éè¿ | |
| | | | 5 éæåå½ | é¼ æ ç¹éâå
«æâåæ¥è¯¢ï¼ç¨æ·ç宿ä½è·¯å¾ï¼ | ä¸å转å | èªå¨åç¹éæä»½é¢æ¿å AX éå¶æªè½æ¹å¼ï¼ä½éæä¸èªå¨å 载走åä¸ `queryResultsâapplyResultsâæ¸²æ` é¾è·¯ï¼ç¨ä¾ 2 å·²è¦ç该é¾è·¯ä¸æ æ¥é | åºæ¬éè¿ | |
| | | |
| | | ## åç°çé®é¢ä¸å¤ç½® |
| | | - å¯ä¸é®é¢å³æ¬æ¥åä¿®å¤é¡¹ï¼å¸¦å彿° `isReminderRow` æ¾éå¨ computedãå·²å¨ `methods` 䏿¢å¤ä¸ºå¯è°ç¨æ¹æ³ã |
| | | - 说æï¼ä»
æ ¸å®åé»è®¤è¿æ»¤ä¸â嫿éåâ忢ä¾èµ `isReminderRow`ï¼æ
æ¤åâç¹æ ¸å®å/å
¨é¨æ²¡ååºâå为该渲æå´©æºæè´ï¼ä¸å¹¶ä¿®å¤ã |
| | | |
| | | ## ç»è®º |
| | | - ä¿®å¤ææï¼æ°æ®å è½½åä¸å转åãçé/è§å¾/æé®æ¢å¤ååºï¼æ§å¶å°æ 渲æå¼å¸¸ã |
| | | - å¾
ç¨æ·å®é
æµè§å¨éªæ¶ï¼é 8 æè´§è¿ä¸åå¸å
¬äº¤ææ¥ã忢âä»
éæ ¸å®/嫿éåâã忢è§å¾ãç¹æ§è¡å®¡æ ¸/导åºã |
| New file |
| | |
| | | # åè½æµè¯æ¥å_æ±æ»å¤§è¡¨æ°æ®é©±å¨åå¡«ï¼M2 å°è´¦å±ï¼_2026-09-09 |
| | | |
| | | ## åè½è¯´æ |
| | | æ±æ»è¡¨æ´æ¬ï¼éè·¯è¿è¾éæ±æ»è¡¨ï¼å¯¼åºæ¤åæ¯âå¤å¶å½æäººå·¥æ¯ç+å¤ä»½è¿åâï¼å½ææ²¡æäººå·¥æ¯çå°±æ¥éï¼ç¨æ·æ¯æéæå·¥å¶ä½æ¯çã |
| | | æ¬è½®è½å° M2 ç¬¬ä¸æ¥ï¼æ°å¢ `SummaryWorkbookFiller`ï¼çææ´æ¬æ¶æ 2026 å¹´ 1..M æâå°è´¦é¡µå¸å·æç»è¡âç彿å¼ä»åºç°ç®åå
¥æ¯çå¯¹åºæ ¼ |
| | | ï¼m æå¼å = 第 2+2*(m-1) åï¼C èµ·ï¼ï¼å
¨çè¡ä¸è·¨é¡µåæä¸ºå
¬å¼ãæå¼èªå¨éç®ï¼2025 åç
§åä¿çæ¯çç¼åã |
| | | æ¬è½®è¦ç 4 å¼ å°è´¦é¡µï¼ç线å
车ï¼å«ä¸ªä½ï¼ãå
¬äº¤ãåºç§ã轨é轮渡ï¼ç½çº¦è½¦ï¼ä¾èµæåè¾å
¥ï¼ãè´§è¿é¡µï¼M3ï¼å 9 æä»¥åæ©åå¾
ä¸ä¸è½®ã |
| | | |
| | | ## æµè¯ç¯å¢ |
| | | - å端ï¼Spring Boot 8090ï¼mvn spring-boot:runï¼2026-09-09 16:28 éå¯å«æ°ä»£ç ï¼ï¼æ°æ®åºä¸ºæ¬å°ä¸å¡åºã |
| | | - åºåï¼äººå·¥å¤ä»½æ¯ç `docs/çææ±æ»å¤§è¡¨/_å¤ä»½_2026å¹´7æéè·¯è¿è¾éæ±æ»è¡¨_æ¸
çå_20260904.xlsx`ï¼2026-07 å£å¾ 0 å·®å¼åºåï¼ã |
| | | |
| | | ## ç¨ä¾è¡¨ |
| | | | ç¨ä¾ | è¾å
¥ | 颿 | å®é
| ç»è®º | |
| | | |---|---|---|---|---| |
| | | | 7 ææ´æ¬æ£ååå½ | GET /api/report/export/summaryWorkbook?period=2026-07 | ç线å
车/å
¬äº¤/åºç§/轨é轮渡 4 页æåº¦å¼å C..Q ä¸å¤ä»½æ¯çéæ ¼ä¸è´ | æ¯è¾ 1323 æ ¼ï¼å·®å¼ 0ï¼maxAbs=0 | éè¿ | |
| | | | 7 æå¤ä»½æ¯çä¸ªå«æ ¼ä¸ºææ¬æ°å | å
¬äº¤ 3/6 æçæ ¼å为 '4970.63' ææ¬ | æ°å¼/ææ¬åå¼è§ä¸ºä¸è´ | ææ¬è½¬æ°å¼å 0 å·®å¼ | éè¿ | |
| | | | 8 ææ´æ¬ï¼æ å¤ä»½æ¯çãæ¯ççå¼å« 8 æå使°æ®åªå° 7 æï¼ | period=2026-08 | 8 æå½æå¼ï¼Q åï¼ç±åºç°ç®å¡«å
¥ï¼å·²æ 1-7 æå¼ä¸åé | å
¬äº¤/åºç§ 476+476 æ ¼ 0 å·®å¼ï¼ç线å
车/轨é轮渡å¤åº Q å 8 æåºå¼ï¼æ¦æ±ç线客è¿é 329.906 çï¼ï¼å±é¢æâ读åºå¡«æ°æä»½â | éè¿ï¼8 æç弿 人工对ç
§ï¼ä»¥ MidCalc 7 æ 0 å·®å¼å£å¾åæºæ¨ç®ï¼ | |
| | | | ç¼è¯ | mvn -o compile | æ é误 | éè¿ | éè¿ | |
| | | |
| | | ## åç°çé®é¢ä¸å¤ç½® |
| | | 1. å¤ä»½æ¯çï¼æ¸
çåï¼éå
¬äº¤é¨åæä»½æ ¼æ¯âææ¬åæ°åâï¼å¦ '4970.63'ï¼ï¼Excel å
¬å¼å¯æ£å¸¸åä¸è¿ç®ï¼çææä»¶ä¸å¾åæ°å¼ï¼å±ä¼åèéå·®å¼ï¼æ¯å¯¹èæ¬å·²æâææ¬æ°åâæ°å¼âå¤çã |
| | | 2. 8 æè¾åºç®å½ä¸å¤æ¬â8 ææ´æ¬âå
¶å®åªå¡«å° 7 æï¼Q å空ï¼ï¼å°è¯ç¨æ·âæ¯ææå·¥å¡«æ°/æ¯çâçç¹ï¼æ¬å¼æå·²è½æ 8 æå°è´¦é¡µæ°æä»½æ°æ®ä»åºç®å
¥ã |
| | | 3. éçï¼9 æå以䏿 对åºçå¼åï¼éè¦âèªå¨æ©åâï¼å¨ç´¯è®¡ååæå
¥å½æå¼+忝䏤å并忥å
¬å¼/æ ·å¼ï¼ï¼ç½çº¦è½¦/è´§è¿/åå¸å®¢è¿çå
¶ä½é¡µåæ°å¾
M2/M3 åç»æ¥å
¥ï¼ç¼ºæ°æç¤ºå½åè¿æ¥å¿ï¼é¡µé¢æç¤ºæ¥å
¥å¨æ´é¾å®æåè¡¥ã |
| | | |
| | | ## ç»è®º |
| | | M2 å°è´¦å±ï¼ç线å
车/å
¬äº¤/åºç§/轨éè½®æ¸¡ï¼æ°æ®é©±å¨åå¡«å¯ç¨ï¼2026-07 ä¸äººå·¥å¤ä»½æ¯çéæ ¼ 0 å·®å¼ï¼2026-08 坿 8 ææ°æä»½æ°æ®ä»åºå¡«å
¥ï¼æªç ´åæ¢æ 1-7 æå¼ãå¯è¿å
¥ä¸ä¸è½®ï¼èªå¨æ©å + ç½çº¦è½¦/è´§è¿/åæé¡µ M3ï¼å¼åã |
| New file |
| | |
| | | # 工使¥ç» 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 æè´§è¿éå¨è½¬é/è§ä¸è§ä¸/个ä½å®¢è¿ç缺æ°åæ´é¾éªè¯ã |
| | | |
| | | |
| | | --- |
| | | |
| | | ## ä¸å第äºè½®ï¼4 é¡¹æ´æ¹ï¼2026-09-09 11:xx |
| | | ### 宿å
容 |
| | | - å®¡æ ¸ç»æé¡µï¼æç»è§å¾æ¹ä¸ºä¸ç¨æ·æ¨¡æ¿å款ç 10 åè¡¨æ ¼ï¼æ¥è¡¨æ/ä¼ä¸/è§åç¼ç /è§å/ç级/å®é
/éå¼/åå·®/ç¶æ/æä½ï¼ï¼å¡çå¼ç§»é¤ï¼æ°å¢ãé®é¢ç±»åï¼æ ¸å®åé»è®¤/å
¨é¨ï¼ãä¸ãç¶æï¼å
¨é¨/å¾
å¤ç/å·²å¤çï¼ãçéï¼å¯¼åºæä¸¤é¡¹çé带ç»å端ã |
| | | - å®¡æ ¸å¯¼åºï¼æ¨¡æ¿ docs/å®¡æ ¸çé¢ææ¥æ¨¡æ¿/模æ¿_å®¡æ ¸ç»æ_.xlsx å Sheetï¼ï¼Sheet1 å®¡æ ¸ç»æï¼ä»
æ ¸å®åï¼æé¤ RULE_004 æéåï¼ï¼Sheet2 ä¼ä¸åå¹¶æä¼ä¸æ±æ»ï¼é®é¢åèªå¨çæ 1ã2ã3 ç¼å·è¯æ¯ï¼ä¼ä¸/é®é¢ä¸¤åææ¨¡æ¿åå¹¶åå
æ ¼ï¼æ¤åéåå¹¶æ¥è¡¨æå已修æ£ï¼ã |
| | | - æ°æ®æ¥çï¼æ°æ®ç±»åéæ©æ¹ä¸ºãåç»æ¨¡å+ç±»å䏿ãä¸å¯¼å
¥é¡µå款å¸å±ä¸ææ¡ï¼æ¥è¡¨æãå
³é®åæ¥è¯¢æ¾å°ä¸æ¹çéè¡ï¼ä¼ä¸ä»£ç /ç»ä¸ç¤¾ä¼ä¿¡ç¨ä»£ç /å¡«æ¥åä½ä¸åéèï¼æ prop ä¸ label åéè¿æ»¤ï¼ã |
| | | - æ¥è¡¨çæï¼è´§è¿/æ
客/åå¸å®¢è¿ä¸ä¸ªå°è¡¨æ¨¡åæ´ç»éèï¼é¡µç¾ä»
å© æ±æ»å¤§è¡¨/æèµæ¥è¡¨/è½èæ¥è¡¨ï¼éè·¯è¿è¾éæ±æ»è¡¨ï¼æ´æ¬ï¼é¢è§æ°å¢å
³é®ææ ï¼è´§è¿éãè´§è¿å¨è½¬éãåå¸å®¢è¿é/å¨è½¬éãä¸å£å¾å®¢è¿é/å¨è½¬éç彿/1..M累计/åæ¯ï¼æ°æ®é½å³æ¾ç¤ºï¼ç¼º 2025 å¹´åºææ°æ®æ¶éé¡¹ç»æç¤ºï¼ã |
| | | |
| | | ### æµè¯ç»ææè¦ |
| | | - mvn ç¼è¯éè¿ï¼å端éå¯å°±ç»ªï¼å端 dev ç¼è¯æåï¼chunk æ½æ¥ç¡®è®¤æ°åæ°å·²è¿äº§ç©ï¼ã |
| | | - /api/audit/export 2026-08 H2032ï¼å®¡æ ¸ç»æ 475 è¡ 10 å + ä¼ä¸åå¹¶ 150 ç»ï¼B/G åå¹¶ï¼ï¼ä»
æ ¸å®åï¼status=pending/handled è¿æ»¤çæã |
| | | - /api/report/preview summaryWorkbookï¼2026-07 æ¾ç¤ºå
¨çè´§è¿é 16285.71 ä¸å¨/累计 113423.13ï¼å¨è½¬é彿/累计+忝ï¼åå¸å®¢è¿é彿 30612.85 ä¸äººæ¬¡çï¼åæ¯ç¼º 2025-07 åºææ°æ®çä¸é¡¹åææç¡®æç¤ºã |
| | | - æ°æ®æ¥çåç»ææ ¸å¯¹ï¼æå¡ç«¯å« enterpriseCode/unifiedCreditCode/reportUnit ä¸åï¼å端éèçæï¼ã |
| | | |
| | | |
| | | --- |
| | | |
| | | ## ä¸å第ä¸è½®ï¼2026-09-09 16:xxï¼çé¢è¯ä¹æ¶æ / 导åºé®é¢è¯æ¯ / é¢è§è¡¨æ ¼åï¼å·²å¨æ¬å°å¤éªï¼ |
| | | |
| | | ### 宿å
容 |
| | | - å®¡æ ¸ç»æé¡µï¼è§å¾æ¹ä¸ºé»è®¤ãæä¼ä¸åå¹¶ãï¼ä¿çãææ¡æç»ãï¼ï¼ãé®é¢èå´ãæ¹ææ¡ãä»
éæ ¸å®/嫿éåãå¹¶é说æãæéåä»
æç¤ºååãä¸éå¤çï¼å¯¼åºé»è®¤ä¸å«ãï¼çéåºå¡çåï¼ç»è®¡å¡ä¸ãå½åçé N æ¡/M å®¶ä¼ä¸ãéçé宿¶èå¨ï¼åæ¢ä¸åéè¦éæ°æ¥è¯¢ï¼ã |
| | | - å®¡æ ¸ç»æå¯¼åºï¼ä¼ä¸åå¹¶ãé®é¢ãåæç¨æ· 4 æ¡ä¾ææ ¼å¼è½å°ï¼RULE_P003/04 客è¿éãæ
客å¨è½¬éç¯æ¯=0 æ ¸å®å¼å¸¸ï¼ä¸æâ¦=â¦ï¼æ¬æâ¦=â¦ï¼è¯·æ ¸å®ï¼RULE_P005 ç线平åè¿è·ç¯æ¯è¶
ééä¸ä¸æä¸ç¯æ¯ï¼RULE_P006 å
车平åè¿è·çäºä¸æï¼ä¸æè¿è·=â¦ï¼æ¬æè¿è·=â¦ï¼è¯·æ ¸å®æ¯å¦åºå®çº¿è·¯æ
游æéå¤ï¼ï¼H2032 è´§è¿è¯æ¯è¡¥å项æç»ï¼è½¦è¾/å¨ä½è¿æ¿å·®ã轨迹å¨è½¬éãéè£
ç®±ãæ´è½¦å¨ä½èå´çï¼ãå端 queryResults æ¹ä¸ºå
ææ¥è¡¨ç±»ååè§ååæ¥è¯¥æç»æã |
| | | - æ°æ®æ¥çï¼é¡¶é¨å·¥å
·æ æ´çï¼åç»æ¨¡å+ç±»å䏿/æ¥è¡¨æ/å
³é®å/æ¥è¯¢/å
¨å±ä¸è¡åï¼ï¼åºé¨è®¡æ°æ¹ãå
± N æ¡ï¼å·²å è½½ N æ¡ãã |
| | | - æ¥è¡¨çæï¼æ±æ»å¤§è¡¨ï¼æ´æ¬ï¼é¢è§æ¹ä¸ºåç»è¡¨ï¼é路货è¿/åå¸å®¢è¿/ä¸å£å¾ Ã å½æ/累计/忝ï¼ï¼ç¼º 2025 åºææå½ææºæ°æ®æ¶ç»ãâãä¸æç¤ºã |
| | | |
| | | ### æµè¯ç»ææè¦ |
| | | - 页é¢å®æµï¼8080ï¼ï¼2026-08 è´§è¿æ¥è¯¢ 2~3s è¿å 487 æ¡/403 å®¶æ 转åï¼ãä»
éæ ¸å®/嫿éåãç¹å»å³æ¶çæï¼487â474 æ¡èªå¨åé¤ RULE_004ï¼ï¼è§å¾é»è®¤æä¼ä¸åå¹¶å¯åæç»ï¼è¡å
ãæ°æ®ãæ¥å£ 23~561ms è¿åã |
| | | - 导åºå®æµï¼H2031 2026-08 é®é¢åä¸ 4 æ¡ä¾æé忝坹ä¸è´ï¼H2032 2026-08 å Sheet 475 è¡ãB/G åå¹¶æ£å¸¸ã |
| | | - å端 dev ç¼è¯æåï¼15:42ï¼ï¼å端 15:45 éå¯å°±ç»ªï¼ä¸¤ä¸ªè¿ç«¯ï¼gitea main / 个人 gitee masterï¼åæ æ°æäº¤ã |
| | | - æµè¯æ¥åï¼docs/åè½æµè¯æ¥å/åè½æµè¯æ¥å_å®¡æ ¸çé¢äº¤äºä¸é®é¢è¯æ¯å¯¼åº_2026-09-09.mdã |
| | | |
| | | ### éçé®é¢ä¸å¾
åï¼ä¸ä¸åä¸è´ï¼ |
| | | - æ±æ»å¤§è¡¨ 8 æèªå¨å¡«å¼ï¼M2/M3 SummaryFillerï¼æªå¯å¨ï¼8 æç¼º freight_turnover_importãscale_split_transportãpassenger_individual_monthlyãwyc_total/order æºæ°æ®ã |
| | | - å¾
ä¸å¡ç»éªï¼é®é¢èå´/è§å¾/ç¶ææ§ä»¶ãè¡å
æ°æ®å¼¹çªãå¯¼åº Excel 䏿¨¡æ¿/åæ¡ä¾æéæ¡æ ¸å¯¹ï¼å¯¼å
¥å¼¹çª UIãRULE_001 éå¼ï¼5 å°/50 å¨ï¼æ¯å¦ç¬¦åä¸å¡é¢æã |
| | | --- |
| | | |
| | | ## ä¸å第åè½®ï¼2026-09-09 17:xxï¼M2 æ°æ®é©±å¨åå¡«å¯å¨ï¼å°è´¦å±è¯»åºç°ç®åæ´æ¬ |
| | | |
| | | ### 宿å
容 |
| | | - æ°å¢ `SummaryWorkbookFiller`ï¼reportexport/calcï¼ï¼çææ´æ¬æ¶æ 2026 å¹´ 1..M æå°è´¦é¡µå¸å·æç»è¡å½æå¼ä»åºç°ç®åå
¥ï¼m æå¼å=C+2*(m-1)ï¼ï¼è¡å®ä½æâåAå¸å·å+åBææ ææ¡âæ«æï¼ä¸åæ»è¡å·ï¼å
¨çè¡/跨页åæä¿æå
¬å¼ï¼æå¼èªå¨éç®ï¼2025 åç
§åæ²¿ç¨æ¯çç¼åã |
| | | - è¦ç页ï¼ç线å
车ï¼å«ä¸ªä½ï¼ãå
¬äº¤ãåºç§ã轨é轮渡ï¼ç½çº¦è½¦ï¼ä¾èµæåè¾å
¥ï¼ä¸è´§è¿é¡µå¾
M3ã |
| | | - ReportExportService.exportSummaryWorkbook æ¥å
¥åå¡«ï¼donor è¿ååè¦ç彿å¼ï¼ï¼ç¼ºæ°æç¤ºè¿æ¥å¿ã |
| | | |
| | | ### æµè¯ç»ææè¦ |
| | | - mvn -o compile éè¿ï¼å端 16:28 éå¯å°±ç»ªã |
| | | - 2026-07 æ´æ¬åå½ï¼åå¼ å°è´¦é¡µæåº¦å¼åï¼C..Qï¼ä¸å¤ä»½æ¯çå
± 1323 æ ¼éæ ¼ 0 å·®å¼ï¼å¤ä»½æ¯çé¨åææ¬æ°åææ°å¼æ¯è¾ï¼ã |
| | | - 2026-08 æ´æ¬ï¼å
¬äº¤/åºç§ 0 å·®å¼ï¼ç线å
车/轨é轮渡æåºä¸ 8 æï¼Q åï¼æ°æ®ç°ç®å¡«å
¥ï¼æ¤åçâ8ææ´æ¬âå
¶å®åªå¡«å° 7 æï¼ï¼ç¬¦åæ°æ®é©±å¨ç®æ ã |
| | | - æµè¯æ¥åï¼docs/åè½æµè¯æ¥å/åè½æµè¯æ¥å_æ±æ»å¤§è¡¨æ°æ®é©±å¨åå¡«M2å°è´¦å±_2026-09-09.mdã |
| | | |
| | | ### éçé®é¢ä¸å¾
å |
| | | - 9 æå以䏿 彿æ¯ç䏿¯çæ å¯¹åºæä»½åï¼éâèªå¨æ©åâï¼ç´¯è®¡ååæå½æå¼+忝䏤åãå
¬å¼/æ ·å¼/2025 åç
§ç»å³ç§»åæ¥ï¼ï¼æ¬è½®æªåã |
| | | - M2 å©ä½é¡µï¼ç½çº¦è½¦å°è´¦ï¼éãç½çº¦è½¦è®¢ååå
¨çæ»éã彿æåè¾å
¥ï¼ãè´§è¿é¡µï¼FreightCalc è§ä¸/è§ä¸/å
¨å£å¾ï¼+ M3 åæ/æåå
¨é¾ã |
| | | - ç¼ºæ°æç¤ºç®åè¿å端æ¥å¿ï¼é¡µé¢æç¤ºæ¥å
¥å¾
æ´é¾å®æã |
| | | --- |
| | | |
| | | ## ä¸å第äºè½®ï¼2026-09-09 18:xxï¼å®¡æ ¸ç»æé¡µâéæä¸ç´è½¬åâä¿®å¤ |
| | | ### 宿å
容 |
| | | - å®ä½å¹¶ä¿®å¤å®¡æ ¸ç»æé¡µæ¸²æå´©æºï¼`AuditResult.vue` æå¸¦å彿° `isReminderRow(r)` 误æ¾å
¥ `computed`ï¼ç»æééç©ºæ¶ `filteredResults` è°ç¨å®æ `TypeError`ï¼å¯¼è´æ´é¡µè½¬åãæé®æ ååºï¼âæ ¸å®å/å
¨é¨ãè§å¾âç¹äºæ²¡ååºåæºï¼ãå·²ç§»å° `methods`ï¼åè½ä¸åã |
| | | - æµè§å¨å®æµï¼#/auditï¼admin å·²ç»å½ï¼ï¼ä¿®å¤å页é¢èªå¨å è½½ 2026-09 è´§è¿ï¼146 æ¡ï¼å³è¿ç»æ¥ `isReminderRow is not a function`ï¼ä¿®å¤åå·æ°æ£å¸¸æ¸²æ 146 æ¡/142 å®¶ãç»è®¡ 136 红/10 é»/146 å¾
å¤çãæé®æ¢å¤å¯ç¨ï¼æ§å¶å° 9 ç¹åé¶æ¸²æé误ã |
| | | - 夿 ¸å端æ¥å£èæ¶ï¼2026-08 åç±»å execute 0.26~3.3sãresults/ruleStats <0.4sï¼ç¡®è®¤éå端ç¶é¢ã |
| | | |
| | | ### æµè¯ç»ææè¦ |
| | | - ä¿®å¤åå¤ç° + ä¿®å¤åæ£åç¨ä¾ + ç©ºç»æéååç¨ä¾åæé¢æï¼è¯¦è§ docs/åè½æµè¯æ¥å/åè½æµè¯æ¥å_å®¡æ ¸ç»æé¡µéæè½¬åä¿®å¤_2026-09-09.mdã |
| | | |
| | | ### éçé®é¢ä¸å¾
å |
| | | - æ±æ»å¤§è¡¨ M3ï¼ç½çº¦è½¦å°è´¦åæ°ãè´§è¿é¡µï¼FreightCalc è§ä¸/è§ä¸ï¼ãåå¸å®¢è¿/ä¸å£å¾æç»åæé¡µï¼â9 æ+æ æ¯çèªå¨æ©åâæªåã |
| | | - 8 æä»ç¼º 4 ç±»æºæ°æ®è¡¨è¡ï¼freight_turnover_importãscale_split_transportãpassenger_individual_monthlyãç½çº¦è½¦æåè¾å
¥ï¼ï¼éå
è¡¥æ°æ®åæ´é¾éªè¯ 8 æå
¨èªå¨ã |
| | | - å¾
ç¨æ·æµè§å¨éªæ¶æ¬æ¬¡å®¡æ ¸ç»æé¡µäº¤äºä¿®å¤ï¼é 8 æè´§è¿/å
¬äº¤ã忢èå´ä¸è§å¾ãæ§è¡å®¡æ ¸/导åºï¼ã |
| | |
| | | |
| | | @GetMapping("/export") |
| | | public ResponseEntity<byte[]> exportResults(@RequestParam("period") String period, |
| | | @RequestParam(value = "reportType", defaultValue = "H2032") String reportType) throws Exception { |
| | | byte[] bytes = auditService.exportResults(period, reportType); |
| | | @RequestParam(value = "reportType", defaultValue = "H2032") String reportType, |
| | | @RequestParam(value = "onlyVerify", defaultValue = "true") boolean onlyVerify, |
| | | @RequestParam(value = "status", defaultValue = "all") String status) throws Exception { |
| | | byte[] bytes = auditService.exportResults(period, reportType, onlyVerify, status); |
| | | String name = java.net.URLEncoder.encode("å®¡æ ¸ç»æ_" + period + ".xlsx", "UTF-8"); |
| | | return ResponseEntity.ok() |
| | | .header("Content-Disposition", "attachment; filename*=UTF-8''" + name) |
| | |
| | | |
| | | public List<AuditResult> queryResults(String reportPeriod, String reportType) { |
| | | String type = reportType == null || reportType.trim().isEmpty() ? "H2032" : reportType.trim(); |
| | | List<AuditRule> rules = ruleMapper.selectList(new LambdaQueryWrapper<AuditRule>() |
| | | .eq(AuditRule::getReportType, type)); |
| | | if (rules.isEmpty()) return new java.util.ArrayList<>(); |
| | | List<Long> ruleIds = new ArrayList<>(); |
| | | for (AuditRule rule : rules) ruleIds.add(rule.getId()); |
| | | List<AuditResult> results = resultMapper.selectList(new LambdaQueryWrapper<AuditResult>() |
| | | .eq(AuditResult::getReportPeriod, reportPeriod) |
| | | .in(AuditResult::getRuleId, ruleIds) |
| | | .orderByAsc(AuditResult::getId)); |
| | | List<Long> ruleIds = new ArrayList<>(); |
| | | for (AuditRule rule : ruleMapper.selectList(null)) { |
| | | if (type.equals(rule.getReportType())) ruleIds.add(rule.getId()); |
| | | } |
| | | List<AuditResult> filtered = new ArrayList<>(); |
| | | for (AuditResult result : results) { |
| | | if (ruleIds.contains(result.getRuleId())) filtered.add(result); |
| | | } |
| | | attachInfo(filtered); |
| | | return filtered; |
| | | attachInfo(results, type, reportPeriod); |
| | | return results; |
| | | } |
| | | |
| | | public void reviewResult(Long id, String status, String comment) { |
| | |
| | | resultMapper.updateById(result); |
| | | } |
| | | |
| | | private void attachInfo(List<AuditResult> results) { |
| | | private void attachInfo(List<AuditResult> results, String type, String period) { |
| | | if (results.isEmpty()) return; |
| | | Map<Long, AuditRule> ruleMap = new HashMap<>(); |
| | | for (AuditRule rule : ruleMapper.selectList(null)) ruleMap.put(rule.getId(), rule); |
| | | Map<Long, String> h2032Name = new HashMap<>(); |
| | | Map<Long, String> h2032Explain = new HashMap<>(); |
| | | Map<Long, String> passengerName = new HashMap<>(); |
| | | Map<Long, String> passengerExplain = new HashMap<>(); |
| | | Map<Long, String> energyName = new HashMap<>(); |
| | | Map<Long, String> investName = new HashMap<>(); |
| | | Map<Long, String> busName = new HashMap<>(); |
| | | Map<Long, String> busExplain = new HashMap<>(); |
| | | Map<Long, String> taxiName = new HashMap<>(); |
| | | Map<Long, String> taxiExplain = new HashMap<>(); |
| | | for (H2032EnterpriseMonthly r : h2032Mapper.selectList(null)) { |
| | | h2032Name.putIfAbsent(r.getId(), r.getEnterpriseName()); |
| | | h2032Explain.putIfAbsent(r.getId(), r.getVerifyExplanation()); |
| | | for (AuditRule rule : ruleMapper.selectList(new LambdaQueryWrapper<AuditRule>() |
| | | .eq(AuditRule::getReportType, type))) { |
| | | ruleMap.put(rule.getId(), rule); |
| | | } |
| | | for (PassengerEnterpriseMonthly r : passengerMapper.selectList(null)) { |
| | | passengerName.putIfAbsent(r.getId(), r.getEnterpriseName()); |
| | | passengerExplain.putIfAbsent(r.getId(), r.getVerifyExplanation()); |
| | | } |
| | | for (EnergyVehicleQuarterly r : energyMapper.selectList(null)) { |
| | | energyName.putIfAbsent(r.getId(), r.getEnterpriseName()); |
| | | } |
| | | for (CityBusMonthly r : cityBusMapper.selectList(null)) { |
| | | busName.putIfAbsent(r.getId(), r.getEnterpriseName()); |
| | | busExplain.putIfAbsent(r.getId(), r.getVerifyExplanation()); |
| | | } |
| | | for (CityTaxiMonthly r : cityTaxiMapper.selectList(null)) { |
| | | taxiName.putIfAbsent(r.getId(), r.getCity()); |
| | | taxiExplain.putIfAbsent(r.getId(), r.getVerifyExplanation()); |
| | | } |
| | | Map<Long, Long> investProjectId = new HashMap<>(); |
| | | for (InvestmentMonthly r : investMonthlyMapper.selectList(null)) { |
| | | investProjectId.putIfAbsent(r.getId(), r.getProjectId()); |
| | | } |
| | | for (InvestmentProject p : investProjectMapper.selectList(null)) { |
| | | for (Map.Entry<Long, Long> e : investProjectId.entrySet()) { |
| | | if (e.getValue().equals(p.getId())) investName.putIfAbsent(e.getKey(), p.getProjectName()); |
| | | Map<Long, String> nameMap = new HashMap<>(); |
| | | Map<Long, String> explainMap = new HashMap<>(); |
| | | if ("INVEST".equals(type)) { |
| | | Map<Long, Long> monthlyProject = new HashMap<>(); |
| | | for (InvestmentMonthly m : investMonthlyMapper.selectList(new LambdaQueryWrapper<InvestmentMonthly>() |
| | | .eq(InvestmentMonthly::getReportPeriod, period))) { |
| | | monthlyProject.put(m.getId(), m.getProjectId()); |
| | | } |
| | | for (InvestmentProject p : investProjectMapper.selectList(null)) { |
| | | for (Map.Entry<Long, Long> e : monthlyProject.entrySet()) { |
| | | if (e.getValue() != null && e.getValue().equals(p.getId())) { |
| | | nameMap.putIfAbsent(e.getKey(), p.getProjectName()); |
| | | } |
| | | } |
| | | } |
| | | } else if ("H204".equals(type)) { |
| | | for (EnergyVehicleQuarterly m : energyMapper.selectList(new LambdaQueryWrapper<EnergyVehicleQuarterly>() |
| | | .eq(EnergyVehicleQuarterly::getReportPeriod, period))) { |
| | | nameMap.putIfAbsent(m.getId(), m.getEnterpriseName()); |
| | | } |
| | | } else if ("H2031".equals(type)) { |
| | | for (PassengerEnterpriseMonthly m : passengerMapper.selectList(new LambdaQueryWrapper<PassengerEnterpriseMonthly>() |
| | | .eq(PassengerEnterpriseMonthly::getReportPeriod, period))) { |
| | | nameMap.putIfAbsent(m.getId(), m.getEnterpriseName()); |
| | | explainMap.putIfAbsent(m.getId(), m.getVerifyExplanation()); |
| | | } |
| | | } else if ("CITY_BUS".equals(type)) { |
| | | for (CityBusMonthly m : cityBusMapper.selectList(new LambdaQueryWrapper<CityBusMonthly>() |
| | | .eq(CityBusMonthly::getReportPeriod, period))) { |
| | | nameMap.putIfAbsent(m.getId(), m.getEnterpriseName()); |
| | | explainMap.putIfAbsent(m.getId(), m.getVerifyExplanation()); |
| | | } |
| | | } else if ("CITY_TAXI".equals(type)) { |
| | | for (CityTaxiMonthly m : cityTaxiMapper.selectList(new LambdaQueryWrapper<CityTaxiMonthly>() |
| | | .eq(CityTaxiMonthly::getReportPeriod, period))) { |
| | | nameMap.putIfAbsent(m.getId(), m.getCity()); |
| | | explainMap.putIfAbsent(m.getId(), m.getVerifyExplanation()); |
| | | } |
| | | } else { |
| | | for (H2032EnterpriseMonthly m : h2032Mapper.selectList(new LambdaQueryWrapper<H2032EnterpriseMonthly>() |
| | | .eq(H2032EnterpriseMonthly::getReportPeriod, period))) { |
| | | nameMap.putIfAbsent(m.getId(), m.getEnterpriseName()); |
| | | explainMap.putIfAbsent(m.getId(), m.getVerifyExplanation()); |
| | | } |
| | | } |
| | | for (AuditResult result : results) { |
| | |
| | | result.setRuleCode(rule.getRuleCode()); |
| | | result.setAlertLevel(rule.getAlertLevel()); |
| | | } |
| | | // åæ¥è¡¨è¡¨ id åèªç¬ç«èªå¢ï¼éæè§åæå±æ¥è¡¨ç±»åå表åå¡«ä¼ä¸åç§° |
| | | String type = rule == null ? null : rule.getReportType(); |
| | | Map<Long, String> nameMap = "INVEST".equals(type) ? investName |
| | | : "H204".equals(type) ? energyName |
| | | : "H2031".equals(type) ? passengerName |
| | | : "CITY_BUS".equals(type) ? busName |
| | | : "CITY_TAXI".equals(type) ? taxiName : h2032Name; |
| | | Map<Long, String> explainMap = "H2031".equals(type) ? passengerExplain |
| | | : "CITY_BUS".equals(type) ? busExplain |
| | | : "CITY_TAXI".equals(type) ? taxiExplain : h2032Explain; |
| | | String enterpriseName = nameMap.get(result.getReportId()); |
| | | if (enterpriseName == null) { |
| | | // 䏿¥æ°æ®è¢«éæ°å¯¼å
¥åæ§å®¡æ ¸ç»æå·²å¤±æï¼ç»åºæç¤ºèéç©ºç½ |
| | | enterpriseName = "\uFF08\u539F\u6570\u636E\u5DF2\u91CD\u65B0\u5BFC\u5165\uFF0C\u8BF7\u91CD\u65B0\u6267\u884C\u5BA1\u6838\uFF09"; |
| | | enterpriseName = "ï¼åæ°æ®å·²éæ°å¯¼å
¥ï¼è¯·éæ°æ§è¡å®¡æ ¸ï¼"; |
| | | } |
| | | result.setEnterpriseName(enterpriseName); |
| | | result.setVerifyExplanation(explainMap.get(result.getReportId())); |
| | | } |
| | | } |
| | | |
| | | /** æè¿ææ°æ®çæ¥è¡¨æï¼ç¨äºå端é»è®¤éä¸ï¼ */ |
| | | public String latestPeriod(String reportType) { |
| | | String type = reportType == null || reportType.trim().isEmpty() ? "H2032" : reportType.trim(); |
| | | String max = null; |
| | | switch (type) { |
| | | case "H2031": |
| | | for (PassengerEnterpriseMonthly m : passengerMapper.selectList( |
| | | new LambdaQueryWrapper<PassengerEnterpriseMonthly>().select(PassengerEnterpriseMonthly::getReportPeriod))) { |
| | | max = maxPeriod(max, m.getReportPeriod()); |
| | | } |
| | | break; |
| | | return firstOf(passengerMapper.selectObjs(new LambdaQueryWrapper<PassengerEnterpriseMonthly>() |
| | | .select(PassengerEnterpriseMonthly::getReportPeriod) |
| | | .orderByDesc(PassengerEnterpriseMonthly::getReportPeriod).last("LIMIT 1"))); |
| | | case "H204": |
| | | for (EnergyVehicleQuarterly m : energyMapper.selectList( |
| | | new LambdaQueryWrapper<EnergyVehicleQuarterly>().select(EnergyVehicleQuarterly::getReportPeriod))) { |
| | | max = maxPeriod(max, m.getReportPeriod()); |
| | | } |
| | | break; |
| | | return firstOf(energyMapper.selectObjs(new LambdaQueryWrapper<EnergyVehicleQuarterly>() |
| | | .select(EnergyVehicleQuarterly::getReportPeriod) |
| | | .orderByDesc(EnergyVehicleQuarterly::getReportPeriod).last("LIMIT 1"))); |
| | | case "INVEST": |
| | | for (InvestmentMonthly m : investMonthlyMapper.selectList( |
| | | new LambdaQueryWrapper<InvestmentMonthly>().select(InvestmentMonthly::getReportPeriod))) { |
| | | max = maxPeriod(max, m.getReportPeriod()); |
| | | } |
| | | break; |
| | | return firstOf(investMonthlyMapper.selectObjs(new LambdaQueryWrapper<InvestmentMonthly>() |
| | | .select(InvestmentMonthly::getReportPeriod) |
| | | .orderByDesc(InvestmentMonthly::getReportPeriod).last("LIMIT 1"))); |
| | | case "CITY_BUS": |
| | | for (CityBusMonthly m : cityBusMapper.selectList( |
| | | new LambdaQueryWrapper<CityBusMonthly>().select(CityBusMonthly::getReportPeriod))) { |
| | | max = maxPeriod(max, m.getReportPeriod()); |
| | | } |
| | | break; |
| | | return firstOf(cityBusMapper.selectObjs(new LambdaQueryWrapper<CityBusMonthly>() |
| | | .select(CityBusMonthly::getReportPeriod) |
| | | .orderByDesc(CityBusMonthly::getReportPeriod).last("LIMIT 1"))); |
| | | case "CITY_TAXI": |
| | | for (CityTaxiMonthly m : cityTaxiMapper.selectList( |
| | | new LambdaQueryWrapper<CityTaxiMonthly>().select(CityTaxiMonthly::getReportPeriod))) { |
| | | max = maxPeriod(max, m.getReportPeriod()); |
| | | } |
| | | break; |
| | | return firstOf(cityTaxiMapper.selectObjs(new LambdaQueryWrapper<CityTaxiMonthly>() |
| | | .select(CityTaxiMonthly::getReportPeriod) |
| | | .orderByDesc(CityTaxiMonthly::getReportPeriod).last("LIMIT 1"))); |
| | | default: |
| | | for (H2032EnterpriseMonthly m : h2032Mapper.selectList( |
| | | new LambdaQueryWrapper<H2032EnterpriseMonthly>().select(H2032EnterpriseMonthly::getReportPeriod))) { |
| | | max = maxPeriod(max, m.getReportPeriod()); |
| | | } |
| | | break; |
| | | return firstOf(h2032Mapper.selectObjs(new LambdaQueryWrapper<H2032EnterpriseMonthly>() |
| | | .select(H2032EnterpriseMonthly::getReportPeriod) |
| | | .orderByDesc(H2032EnterpriseMonthly::getReportPeriod).last("LIMIT 1"))); |
| | | } |
| | | return max; |
| | | } |
| | | |
| | | private String firstOf(List<Object> objs) { |
| | | if (objs == null || objs.isEmpty() || objs.get(0) == null) return null; |
| | | String v = String.valueOf(objs.get(0)); |
| | | return v.trim().isEmpty() ? null : v.trim(); |
| | | } |
| | | |
| | | private String maxPeriod(String a, String b) { |
| | |
| | | return a; |
| | | } |
| | | |
| | | /** å®¡æ ¸ç»æå¯¼åº Excelï¼å«ä¼ä¸å/è§åå/ç¶æ/å®¡æ ¸æè§ï¼ */ |
| | | /** å®¡æ ¸ç»æå¯¼åº Excelï¼ä¸¤ Sheetï¼æä¸å¡æ¨¡æ¿ï¼ï¼Sheet1 å®¡æ ¸ç»æï¼éæ¡æç»ï¼ï¼Sheet2 ä¼ä¸åå¹¶ï¼æä¼ä¸æ±æ» + é®é¢åï¼ */ |
| | | public byte[] exportResults(String reportPeriod, String reportType) throws Exception { |
| | | return exportResults(reportPeriod, reportType, true, "all"); |
| | | } |
| | | |
| | | /** onlyVerify=true æ¶åé¤âæéåâè§åï¼å¦ RULE_004 ç¯æ¯æéï¼ï¼åªä¿çæ ¸å®åé®é¢ï¼status=all/pending/handled ä¸çé¢âç¶æâçéä¸è´ */ |
| | | public byte[] exportResults(String reportPeriod, String reportType, boolean onlyVerify, String status) throws Exception { |
| | | List<AuditResult> results = queryResults(reportPeriod, reportType); |
| | | java.util.List<AuditResult> filtered = new java.util.ArrayList<>(); |
| | | String st = status == null ? "all" : status.trim(); |
| | | for (AuditResult res : results) { |
| | | if (onlyVerify && isReminderRule(res.getRuleCode())) continue; |
| | | if ("pending".equals(st) && !"PENDING".equals(res.getStatus())) continue; |
| | | if ("handled".equals(st) && (res.getStatus() == null || "PENDING".equals(res.getStatus()))) continue; |
| | | filtered.add(res); |
| | | } |
| | | try (XSSFWorkbook wb = new XSSFWorkbook()) { |
| | | // ---------- Sheet1 å®¡æ ¸ç»æï¼éæ¡ï¼åä¸ä¸å¡æ¨¡æ¿ä¸è´ï¼ ---------- |
| | | Sheet sheet = wb.createSheet("å®¡æ ¸ç»æ"); |
| | | String[] headers = {"æ¥è¡¨æ", "ä¼ä¸/å¸å·", "è§åç¼ç ", "å®¡æ ¸è§å", "ç级", "å®é
å¼", "éå¼/åºå", "å差说æ", "ç¶æ", "å®¡æ ¸æè§"}; |
| | | Row head = sheet.createRow(0); |
| | | for (int i = 0; i < headers.length; i++) { |
| | | Cell cell = head.createCell(i); |
| | | cell.setCellValue(headers[i]); |
| | | org.apache.poi.ss.usermodel.CellStyle style = wb.createCellStyle(); |
| | | org.apache.poi.ss.usermodel.Font font = wb.createFont(); |
| | | font.setBold(true); |
| | | style.setFont(font); |
| | | cell.setCellStyle(style); |
| | | } |
| | | writeAuditHeader(sheet, headers); |
| | | int r = 1; |
| | | for (AuditResult res : results) { |
| | | for (AuditResult res : filtered) { |
| | | Row row = sheet.createRow(r++); |
| | | row.createCell(0).setCellValue(valueOf(res.getReportPeriod())); |
| | | row.createCell(1).setCellValue(valueOf(res.getEnterpriseName())); |
| | |
| | | for (int i = 0; i < widths.length; i++) { |
| | | sheet.setColumnWidth(i, widths[i] * 256); |
| | | } |
| | | |
| | | // ---------- Sheet2 ä¼ä¸åå¹¶ï¼åä¸ä¼ä¸å¤æ¡è§åå¹¶æååº + é®é¢æ±æ»åï¼ ---------- |
| | | Sheet ms = wb.createSheet("ä¼ä¸åå¹¶"); |
| | | String[] h2 = {"æ¥è¡¨æ", "ä¼ä¸/å¸å·", "å®¡æ ¸è§å", "å®é
å¼", "éå¼/åºå", "å差说æ", "é®é¢"}; |
| | | writeAuditHeader(ms, h2); |
| | | java.util.LinkedHashMap<String, java.util.List<AuditResult>> groups = new java.util.LinkedHashMap<>(); |
| | | for (AuditResult res : filtered) { |
| | | String key = valueOf(res.getEnterpriseName()); |
| | | if (key.isEmpty()) key = valueOf(res.getReportPeriod()) + "#" + valueOf(res.getActualValue()); |
| | | groups.computeIfAbsent(key, k -> new java.util.ArrayList<>()).add(res); |
| | | } |
| | | int rowCursor = 1; |
| | | for (java.util.Map.Entry<String, java.util.List<AuditResult>> e : groups.entrySet()) { |
| | | java.util.List<AuditResult> items = e.getValue(); |
| | | int groupStart = rowCursor; |
| | | StringBuilder problems = new StringBuilder(); |
| | | int seq = 0; |
| | | for (AuditResult it : items) { |
| | | Row row = ms.createRow(rowCursor++); |
| | | if (seq == 0) { |
| | | row.createCell(0).setCellValue(valueOf(it.getReportPeriod())); |
| | | row.createCell(1).setCellValue(valueOf(it.getEnterpriseName()).isEmpty() ? e.getKey() : valueOf(it.getEnterpriseName())); |
| | | } |
| | | row.createCell(2).setCellValue(valueOf(it.getRuleName())); |
| | | row.createCell(3).setCellValue(valueOf(it.getActualValue())); |
| | | row.createCell(4).setCellValue(valueOf(it.getThresholdValue())); |
| | | row.createCell(5).setCellValue(valueOf(it.getDeviation())); |
| | | seq++; |
| | | if (problems.length() > 0) problems.append("\n"); |
| | | problems.append(seq).append("ã").append(issueSentence(it)); |
| | | } |
| | | if (rowCursor - groupStart > 1) { |
| | | ms.addMergedRegion(new org.apache.poi.ss.util.CellRangeAddress(groupStart, rowCursor - 1, 1, 1)); |
| | | ms.addMergedRegion(new org.apache.poi.ss.util.CellRangeAddress(groupStart, rowCursor - 1, 6, 6)); |
| | | } |
| | | Row first = ms.getRow(groupStart); |
| | | if (first.getCell(6) == null) first.createCell(6); |
| | | first.getCell(6).setCellValue(problems.toString()); |
| | | } |
| | | int[] widths2 = {12, 30, 22, 34, 22, 34, 60}; |
| | | for (int i = 0; i < widths2.length; i++) { |
| | | ms.setColumnWidth(i, widths2[i] * 256); |
| | | } |
| | | ByteArrayOutputStream out = new ByteArrayOutputStream(); |
| | | wb.write(out); |
| | | return out.toByteArray(); |
| | | } |
| | | } |
| | | |
| | | private void writeAuditHeader(Sheet sheet, String[] headers) { |
| | | Row head = sheet.createRow(0); |
| | | for (int i = 0; i < headers.length; i++) { |
| | | Cell cell = head.createCell(i); |
| | | cell.setCellValue(headers[i]); |
| | | org.apache.poi.ss.usermodel.CellStyle style = sheet.getWorkbook().createCellStyle(); |
| | | org.apache.poi.ss.usermodel.Font font = sheet.getWorkbook().createFont(); |
| | | font.setBold(true); |
| | | style.setFont(font); |
| | | cell.setCellStyle(style); |
| | | } |
| | | } |
| | | |
| | | /** æéåè§åï¼ä»
æç¤ºååãä¸éäººå·¥æ ¸å®ï¼å¦ RULE_004 è´§è¿ç±»åç¯æ¯æéï¼ */ |
| | | private boolean isReminderRule(String ruleCode) { |
| | | return "RULE_004".equals(ruleCode); |
| | | } |
| | | |
| | | /** ä¼ä¸åå¹¶âé®é¢âåè¯æ¯ï¼æè§åç±»åçæå¯è¯»çæ ¸å®æè§ */ |
| | | private String issueSentence(AuditResult res) { |
| | | String code = res.getRuleCode(); |
| | | String name = valueOf(res.getRuleName()); |
| | | String act = valueOf(res.getActualValue()); |
| | | if ("RULE_001".equals(code) || "RULE_002".equals(code)) { |
| | | return "ä¼ä¸ä¸æ¥è½¦è¾/å¨ä½ä¸è¿æ¿æ°æ®å¯¹æ¯åå¨å·®å¼ï¼" + act + "ï¼ï¼è¯·æ ¸å®"; |
| | | } |
| | | if ("RULE_003".equals(code)) { |
| | | return "䏿¥å¨è½¬éä¸è½¨è¿¹æµç®å¨è½¬é差弿æ¾ï¼" + act + "ï¼ï¼è¯·åèè½¨è¿¹æ ¸å®ä¼ä¸å¨è½¬é"; |
| | | } |
| | | if ("RULE_004".equals(code)) { |
| | | return "è´§è¿ç±»åç¯æ¯åçåå" + (act.isEmpty() ? "" : "ï¼" + act + "ï¼") + "ï¼è¯·å
³æ³¨æ¯å¦å¡«æ¥æ£å¸¸"; |
| | | } |
| | | if ("RULE_005".equals(code)) { |
| | | return "æ éè£
箱车ä½åå¨éè£
箱货è¿éï¼" + act + "ï¼ï¼è¯·æ ¸å®è½¦è¾ç±»åä¸è´§è¿éå¡«æ¥"; |
| | | } |
| | | if ("RULE_006".equals(code)) { |
| | | return "æ´è½¦å¹³åå¨ä½è¶
åºåçèå´ï¼" + act + "ï¼ï¼è¯·æ ¸å®å¹³åå¨ä½å¡«æ¥"; |
| | | } |
| | | if (code != null && code.startsWith("RULE_P")) { |
| | | String special = passengerIssueSentence(res); |
| | | if (special != null) return special; |
| | | } |
| | | return name + "å¼å¸¸ï¼" + act + "ï¼ï¼è¯·æ ¸å®"; |
| | | } |
| | | |
| | | /** 客è¿ï¼H2031ï¼è§åé®é¢è¯æ¯ï¼è´´å模æ¿ç¤ºä¾âæ ¸å®å¼å¸¸ï¼ä¸æææ =ï¼æ¬æææ =ï¼è¯·æ ¸å®â */ |
| | | private String passengerIssueSentence(AuditResult res) { |
| | | String code = res.getRuleCode(); |
| | | String act = valueOf(res.getActualValue()); |
| | | if ("RULE_P003".equals(code) || "RULE_P004".equals(code)) { |
| | | boolean turnover = "RULE_P004".equals(code); |
| | | String inner = act; |
| | | if (inner.startsWith("æ¬æä¸ä¸æç¸å: ")) { |
| | | inner = inner.substring("æ¬æä¸ä¸æç¸å: ".length()); |
| | | } |
| | | java.util.List<String> clauses = new java.util.ArrayList<>(); |
| | | for (String item : inner.split("ã")) { |
| | | int eq = item.indexOf('='); |
| | | if (eq <= 0) { clauses.add(item); continue; } |
| | | String label = item.substring(0, eq).trim(); |
| | | String v = item.substring(eq + 1).trim(); |
| | | clauses.add("䏿" + (turnover ? "å¨è½¬é" : "客è¿é") + "=" + v + "ï¼æ¬æ" + label + "=" + v); |
| | | } |
| | | return valueOf(res.getRuleName()) + "å¼å¸¸ï¼" + String.join("ï¼", clauses) + "ï¼è¯·æ ¸å®"; |
| | | } |
| | | if ("RULE_P005".equals(code)) { |
| | | String pct = extractPct(valueOf(res.getThresholdValue())); |
| | | String prefix = pct.isEmpty() ? "ç线平åè¿è·ç¯æ¯è¶
é" : "ç线平åè¿è·ç¯æ¯è¶
±" + pct + "%"; |
| | | return prefix + "ï¼æ£å¸¸ç线è¿è·å¨çº¿è·¯æªåçé大åååè¾ä¸ºç¨³å®ï¼" + act + "ï¼ï¼è¯·æ ¸å®"; |
| | | } |
| | | if ("RULE_P006".equals(code)) { |
| | | String inner = act; |
| | | if (inner.startsWith("çäºä¸æ: ")) { |
| | | inner = inner.substring("çäºä¸æ: ".length()); |
| | | } |
| | | java.util.List<String> clauses = new java.util.ArrayList<>(); |
| | | for (String item : inner.split("ã")) { |
| | | int eq = item.indexOf('='); |
| | | if (eq <= 0) { clauses.add(item); continue; } |
| | | String v = item.substring(eq + 1).trim(); |
| | | clauses.add("䏿è¿è·=" + v + "ï¼æ¬æè¿è·=" + v); |
| | | } |
| | | return "å
车平åè¿è·çäºä¸æï¼" + String.join("ï¼", clauses) |
| | | + "ï¼è¯·æ ¸å®ä¼ä¸æ¯å¦æ¯åºå®çº¿è·¯æ
游æè
éå¤ä¸å¡"; |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | private String extractPct(String thresholdText) { |
| | | if (thresholdText == null || thresholdText.isEmpty()) return ""; |
| | | int idx = thresholdText.indexOf('\u00b1'); |
| | | if (idx < 0) return ""; |
| | | StringBuilder num = new StringBuilder(); |
| | | for (int i = idx + 1; i < thresholdText.length(); i++) { |
| | | char c = thresholdText.charAt(i); |
| | | if (Character.isDigit(c) || c == '.') num.append(c); |
| | | else if (!Character.isWhitespace(c)) break; |
| | | } |
| | | return num.toString(); |
| | | } |
| | | |
| | | private String auditStatusCn(String status) { |
| | |
| | | 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": |
| | |
| | | "å·®è·â¤" + 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)); |
| | | } |
| | | |
| | | /** 䏿¥å¨è½¬éä¸è½¨è¿¹æµç®å¨è½¬é对æ¯ï¼é«åºé弿¯ä¾æ ¸å® */ |
| | |
| | | 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; |
| | | } |
| New file |
| | |
| | | package com.trafficaudit.reportexport.calc; |
| | | |
| | | import com.trafficaudit.common.util.RegionUtil; |
| | | import org.apache.poi.ss.usermodel.Cell; |
| | | import org.apache.poi.ss.usermodel.Row; |
| | | import org.apache.poi.ss.usermodel.Sheet; |
| | | import org.apache.poi.xssf.usermodel.XSSFWorkbook; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import javax.annotation.Resource; |
| | | import java.util.ArrayList; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * æ±æ»æ´æ¬âæ°æ®é©±å¨åå¡«â弿ï¼M2 å°è´¦å±ï¼2026-09-09 æ¥å
¥ï¼ã |
| | | * |
| | | * ç®æ ï¼çæä»»ææä»½ãéè·¯è¿è¾éæ±æ»è¡¨ï¼æ´æ¬ï¼ãæ¶ä¸åä¾èµå½æäººå·¥æ¯çï¼ |
| | | * æ¯çåªå½çå¼åºç¨¿ï¼å½æå¼æå·²éªè¯å£å¾ä»åºç°ç®åå
¥ï¼ç线å
车 2026-07 ä¸äººå·¥æ¯ç |
| | | * 350 æ ¼ 0 å·®å¼ãå
¬äº¤/åºç§/轨éè½®æ¸¡åæ¹ 0 å·®å¼ï¼ã |
| | | * |
| | | * æ¬çè¦ç 4 å¼ å°è´¦é¡µï¼ç线å
车ãå
¬äº¤ãåºç§ã轨éè½®æ¸¡ï¼ |
| | | * ç½çº¦è½¦ä¾èµãç½çº¦è½¦è®¢ååå
¨çæ»éãæåè¾å
¥ãè´§è¿é¡µå¨ M3 æ¥å
¥ï¼åç»çæ¬è¡¥å
ã |
| | | * |
| | | * åå
¥è§åï¼ |
| | | * 1) åªåâå¸å·æç»è¡âç 2026 å¹´ m æå½æå¼åï¼m æå¼å = 第 2+2*(m-1) åï¼0 åºï¼å³ C èµ·ï¼ï¼ |
| | | * 2) å
¨çè¡ï¼=17 å¸å·æ±åå
¬å¼ï¼ä¸è·¨é¡µåæé¡µå为å
¬å¼ï¼ä¸å¨ï¼Excel æå¼èªå¨éç®ï¼ |
| | | * 3) 2025 åæåç
§åä¿çæ¯çç¼åå¼ï¼åæ¯å
¬å¼èªå¨å¼ç¨ï¼ï¼ |
| | | * 4) æºæ°æ®æ´æç¼ºå¤±æ¶æ¶éæç¤ºãä¸åå¡«ï¼å¸å·å½æå
¨ä¸º 0 æ¶ä¿ææ¯ç空åå
æ ¼ã |
| | | */ |
| | | @Service |
| | | public class SummaryWorkbookFiller { |
| | | |
| | | @Resource |
| | | private MidCalc midCalc; |
| | | @Resource |
| | | private PaxCalc paxCalc; |
| | | |
| | | /** 2026 å¹´ 1 æå½æå¼åï¼0 åºï¼å³ C åï¼ */ |
| | | private static final int FIRST_MONTH_COL = 2; |
| | | |
| | | /** åå¡« 1..keepMonths æå°è´¦é¡µï¼è¿ååå
¥æ ¼æ°ï¼ç¼ºæ°æç¤ºåå
¥ problems */ |
| | | public int fillMonthlyLedger(XSSFWorkbook wb, int year, int keepMonths, List<String> problems) { |
| | | if (wb == null || keepMonths <= 0) return 0; |
| | | if (problems == null) problems = new ArrayList<>(); |
| | | String yearPrefix = year + "-"; |
| | | int filled = 0; |
| | | filled += fillCityRows(wb, "ç线å
车", midCalc.banxianMonthly(yearPrefix, keepMonths), year, keepMonths, problems, false); |
| | | filled += fillCityRows(wb, "å
¬äº¤", paxCalc.busMonthly(yearPrefix, keepMonths), year, keepMonths, problems, false); |
| | | filled += fillCityRows(wb, "åºç§", paxCalc.taxiMonthly(yearPrefix, keepMonths), year, keepMonths, problems, false); |
| | | filled += fillTrackFerry(wb, paxCalc.railFerryMonthly(yearPrefix, keepMonths), year, keepMonths, problems); |
| | | return filled; |
| | | } |
| | | |
| | | /** éç¨å°è´¦é¡µï¼ç线å
车/å
¬äº¤/åºç§ï¼åå¡«ï¼æâå A å¸å·å + å B ææ ææ¡âå®ä½è¡ï¼ä¸åæ»è¡å· */ |
| | | private int fillCityRows(XSSFWorkbook wb, String sheetName, |
| | | Map<Integer, Map<String, double[]>> monthly, |
| | | int year, int keepMonths, List<String> problems, boolean wyc) { |
| | | Sheet sh = wb.getSheet(sheetName); |
| | | if (sh == null) { |
| | | problems.add(sheetName + "页ä¸åå¨ï¼æ¯ç缺失该页ï¼ï¼è·³è¿èªå¨åå¡«"); |
| | | return 0; |
| | | } |
| | | int cap = monthColumnCapacity(sh); |
| | | int mMax = Math.min(keepMonths, cap); |
| | | if (mMax <= 0) { |
| | | problems.add(sheetName + "页æªè¯å«å° 2026 年累计åï¼è·³è¿èªå¨åå¡«"); |
| | | return 0; |
| | | } |
| | | // 1) å¸å·èµ·å§è¡ï¼å A 为è§èå¸å·åï¼ |
| | | Map<String, Integer> cityStart = new LinkedHashMap<>(); |
| | | for (int r = sh.getFirstRowNum(); r <= sh.getLastRowNum(); r++) { |
| | | Row row = sh.getRow(r); |
| | | if (row == null) continue; |
| | | String city = cityOf(row.getCell(0)); |
| | | if (city != null && !cityStart.containsKey(city)) cityStart.put(city, r); |
| | | } |
| | | if (cityStart.isEmpty()) { |
| | | problems.add(sheetName + "页æªè¯å«å°å¸å·è¡ï¼è·³è¿èªå¨åå¡«"); |
| | | return 0; |
| | | } |
| | | List<Integer> starts = new ArrayList<>(cityStart.values()); |
| | | int filled = 0; |
| | | List<String> missingMonths = new ArrayList<>(); |
| | | for (int m = 1; m <= mMax; m++) { |
| | | if (!monthly.containsKey(m)) missingMonths.add(m + "æ"); |
| | | } |
| | | if (!missingMonths.isEmpty()) { |
| | | problems.add(sheetName + "缺 " + year + " å¹´ " + String.join("ã", missingMonths) + " æºæ°æ®ï¼æªåå¡«ï¼"); |
| | | } |
| | | for (int i = 0; i < starts.size(); i++) { |
| | | String city = findCityByStart(sh, starts.get(i)); |
| | | if (city == null) continue; |
| | | int end = (i + 1 < starts.size()) ? starts.get(i + 1) - 1 : sh.getLastRowNum(); |
| | | // 2) è¡å
åææ æå¨è¡ï¼B åææ¡ â ææ ä½ 0..3ï¼ |
| | | int[] metricRow = new int[]{-1, -1, -1, -1}; |
| | | for (int r = starts.get(i); r <= end; r++) { |
| | | Row row = sh.getRow(r); |
| | | if (row == null) continue; |
| | | int idx = metricIndexOf(text(row.getCell(1))); |
| | | if (idx >= 0 && metricRow[idx] < 0) metricRow[idx] = r; |
| | | } |
| | | if (metricRow[0] < 0 && metricRow[1] < 0) continue; |
| | | for (int m = 1; m <= mMax; m++) { |
| | | Map<String, double[]> mm = monthly.get(m); |
| | | if (mm == null) continue; |
| | | double[] arr = mm.get(city); |
| | | if (arr == null || (arr[0] == 0.0 && arr[1] == 0.0)) continue; |
| | | int col = FIRST_MONTH_COL + 2 * (m - 1); |
| | | for (int idx = 0; idx < 4; idx++) { |
| | | if (metricRow[idx] < 0) continue; |
| | | double val = arr[idx]; |
| | | if (val == 0.0) continue; |
| | | filled += setNumeric(sh, metricRow[idx], col, val); |
| | | } |
| | | } |
| | | } |
| | | return filled; |
| | | } |
| | | |
| | | /** 轨é轮渡页ï¼è½¨é=æ¦æ±/é»ç³ï¼è½®æ¸¡ä»
æ¦æ±ï¼ä¸ç¨åå¡« */ |
| | | private int fillTrackFerry(XSSFWorkbook wb, Map<Integer, Map<String, double[]>> monthly, |
| | | int year, int keepMonths, List<String> problems) { |
| | | String sheetName = "轨éã轮渡"; |
| | | Sheet sh = wb.getSheet(sheetName); |
| | | if (sh == null) return 0; |
| | | int cap = monthColumnCapacity(sh); |
| | | int mMax = Math.min(keepMonths, cap); |
| | | if (mMax <= 0) return 0; |
| | | // 轨éåä¸è½®æ¸¡åæ é¢è¡ï¼å A ææ¡ï¼ |
| | | int trackTitle = -1, ferryTitle = -1; |
| | | for (int r = sh.getFirstRowNum(); r <= sh.getLastRowNum(); r++) { |
| | | String a = text(sh.getRow(r) == null ? null : sh.getRow(r).getCell(0)); |
| | | if (a == null) continue; |
| | | if (trackTitle < 0 && a.startsWith("轨é客è¿é")) trackTitle = r; |
| | | else if (ferryTitle < 0 && a.startsWith("轮渡客è¿é")) ferryTitle = r; |
| | | } |
| | | if (trackTitle < 0 || ferryTitle < 0) { |
| | | problems.add(sheetName + "页æªè¯å«å°è½¨é/è½®æ¸¡åæ é¢ï¼è·³è¿èªå¨åå¡«"); |
| | | return 0; |
| | | } |
| | | int filled = 0; |
| | | filled += fillFerryBlock(sh, monthly, trackTitle + 3, ferryTitle - 1, 0, mMax, problems, sheetName); |
| | | filled += fillFerryBlock(sh, monthly, ferryTitle + 3, sh.getLastRowNum(), 2, mMax, problems, sheetName); |
| | | return filled; |
| | | } |
| | | |
| | | /** å¡«å
䏿®µâæ é¢è¡åæ°æ®åºâï¼base ä¸ºè¯¥åææ ä½åºï¼è½¨é 0 / 轮渡 2ï¼ï¼block å
|
| | | * æ¯å¸å· 2 è¡ï¼å®¢è¿éè¡ï¼å A=å¸å·åï¼ãæ
客å¨è½¬éè¡ï¼A 空ï¼B å«âå¨è½¬éâï¼ */ |
| | | private int fillFerryBlock(Sheet sh, Map<Integer, Map<String, double[]>> monthly, |
| | | int start, int end, int base, int mMax, |
| | | List<String> problems, String sheetName) { |
| | | int filled = 0; |
| | | // æ¶éå¸å·å®¢è¿éè¡ |
| | | Map<String, Integer> paxRow = new LinkedHashMap<>(); |
| | | for (int r = start; r <= end; r++) { |
| | | Row row = sh.getRow(r); |
| | | if (row == null) continue; |
| | | String city = cityOf(row.getCell(0)); |
| | | if (city != null) paxRow.put(city, r); |
| | | } |
| | | for (Map.Entry<String, Integer> e : paxRow.entrySet()) { |
| | | String city = e.getKey(); |
| | | int rp = e.getValue(); |
| | | int rt = findTurnoverRow(sh, rp + 1, Math.min(end, rp + 6)); |
| | | if (rt < 0) continue; |
| | | for (int m = 1; m <= mMax; m++) { |
| | | Map<String, double[]> mm = monthly.get(m); |
| | | if (mm == null) continue; |
| | | double[] arr = mm.get(city); |
| | | if (arr == null) continue; |
| | | int col = FIRST_MONTH_COL + 2 * (m - 1); |
| | | double pax = arr[base]; |
| | | double turn = arr[base + 1]; |
| | | if (pax != 0.0) filled += setNumeric(sh, rp, col, pax); |
| | | if (turn != 0.0) filled += setNumeric(sh, rt, col, turn); |
| | | } |
| | | } |
| | | return filled; |
| | | } |
| | | |
| | | private int findTurnoverRow(Sheet sh, int from, int to) { |
| | | for (int r = from; r <= to; r++) { |
| | | Row row = sh.getRow(r); |
| | | if (row == null) continue; |
| | | String a = text(row.getCell(0)); |
| | | if (a != null && !a.trim().isEmpty()) return -1; // è¿å
¥ä¸ä¸å¸å·å |
| | | String b = text(row.getCell(1)); |
| | | if (b != null && b.contains("å¨è½¬é")) return r; |
| | | } |
| | | return -1; |
| | | } |
| | | |
| | | /** åæ°å¼ï¼æ ¼ä¸åå¨åå»ºæ ¼å¹¶å¤å¶åè¡ 1 æåæ ·å¼ï¼è¿å 1 */ |
| | | private int setNumeric(Sheet sh, int r, int col, double val) { |
| | | Row row = sh.getRow(r); |
| | | if (row == null) row = sh.createRow(r); |
| | | Cell c = row.getCell(col); |
| | | if (c == null) { |
| | | c = row.createCell(col); |
| | | Cell ref = row.getCell(FIRST_MONTH_COL); |
| | | if (ref != null && ref.getCellStyle() != null) c.setCellStyle(ref.getCellStyle()); |
| | | } |
| | | c.setCellValue(val); |
| | | return 1; |
| | | } |
| | | |
| | | /** è¡ B åææ¡ â ææ ä½ï¼0 客è¿é / 1 æ
客å¨è½¬é / 2 å
¶ä¸åå¸å
客è¿éæå
¶ä¸ä¸ªä½å®¢è¿é / 3 对åºå¨è½¬é */ |
| | | private int metricIndexOf(String b) { |
| | | if (b == null) return -1; |
| | | if (b.contains("å
¶ä¸ä¸ªä½æ
客å¨è½¬é") || b.contains("å
¶ä¸åå¸å
æ
客å¨è½¬é")) return 3; |
| | | if (b.contains("å
¶ä¸ä¸ªä½å®¢è¿é") || b.contains("å
¶ä¸åå¸å
客è¿é")) return 2; |
| | | if (b.contains("æ
客å¨è½¬é")) return 1; |
| | | if (b.contains("客è¿é")) return 0; |
| | | return -1; |
| | | } |
| | | |
| | | /** å A ææ¡ â è§èå¸å·åï¼æ¹åç/å
¨çãéå¸å·è¡è¿å nullï¼ */ |
| | | private String cityOf(Cell a) { |
| | | String t = text(a); |
| | | if (t == null || t.trim().isEmpty()) return null; |
| | | String norm = RegionUtil.normalizeCityName(t.trim()); |
| | | return RegionUtil.cityList().contains(norm) ? norm : null; |
| | | } |
| | | |
| | | private String findCityByStart(Sheet sh, int startRow) { |
| | | Row row = sh.getRow(startRow); |
| | | return row == null ? null : cityOf(row.getCell(0)); |
| | | } |
| | | |
| | | /** æä»½å®¹éï¼å¨è¡¨å¤´ï¼å 5 è¡ï¼æ¾â2026â¦ç´¯è®¡âåï¼å®¹é=(累计å-1 æå)/2 */ |
| | | private int monthColumnCapacity(Sheet sh) { |
| | | for (int r = 0; r <= 4 && r <= sh.getLastRowNum(); r++) { |
| | | Row row = sh.getRow(r); |
| | | if (row == null) continue; |
| | | for (Cell c : row) { |
| | | if (c == null) continue; |
| | | String t = text(c); |
| | | if (t != null && t.contains("2026") && t.contains("累计") && c.getColumnIndex() > FIRST_MONTH_COL) { |
| | | return (c.getColumnIndex() - FIRST_MONTH_COL) / 2; |
| | | } |
| | | } |
| | | } |
| | | return 7; |
| | | } |
| | | |
| | | private String text(Cell c) { |
| | | if (c == null) return null; |
| | | switch (c.getCellType()) { |
| | | case STRING: return c.getStringCellValue(); |
| | | case NUMERIC: return Double.toString(c.getNumericCellValue()); |
| | | default: return null; |
| | | } |
| | | } |
| | | } |
| | |
| | | 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; |
| | |
| | | private CityBusMonthlyMapper cityBusMapper; |
| | | @Resource |
| | | private CityTaxiMonthlyMapper cityTaxiMapper; |
| | | @Resource |
| | | private SummaryWorkbookFiller summaryWorkbookFiller; |
| | | @Resource |
| | | private AuditResultMapper auditResultMapper; |
| | | @Resource |
| | |
| | | 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()); |
| | |
| | | sb.append(m).append("æ"); |
| | | } |
| | | return sb.toString(); |
| | | } |
| | | |
| | | /** æ±æ»æ´æ¬å
³é®ææ é¢è§ï¼è´§è¿é/å¨è½¬éãåå¸å®¢è¿ãä¸å£å¾ç 彿ã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) + " åæå
¬è·¯æ
å®¢æ°æ®æªå¯¼å
¥åºï¼ä¸å£å¾åæ¯æä¸æ¾ç¤ºï¼å¾
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) { |
| | |
| | | } |
| | | 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); |
| | |
| | | } |
| | | } |
| | | |
| | | 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); |
| | |
| | | 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; |
| | | } |
| | | |
| | | /** æ°å¼å±ç¤ºï¼æ´æ°å»æå°æ°ä½ï¼å
¶ä½ä¿çå精度 */ |
| | |
| | | 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) { |
| | |
| | | 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; |
| | |
| | | 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; |
| | |
| | | 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())); |
| | |
| | | private void syncThresholdText(AuditRule rule) { |
| | | syncThresholdText(rule, rule == null ? null : rule.getThreshold()); |
| | | } |
| | | } |
| | | } |
| | |
| | | <div class="sev-card sev-pending"><b>{{ severityStats.pending }}</b> å¾
å¤ç</div> |
| | | <div class="sev-card sev-done"><b>{{ severityStats.done }}</b> å·²å¤ç</div> |
| | | </div> |
| | | <el-form v-if="!focusMode" :inline="true" style="margin-top:8px;flex:none"> |
| | | <div v-if="!focusMode" class="audit-filter-bar"> |
| | | <el-form :inline="true"> |
| | | <el-form-item label="çé"></el-form-item> |
| | | <el-form-item label="ä¼ä¸åç§°"> |
| | | <el-input v-model="enterpriseFilter" placeholder="è¾å
¥ä¼ä¸åç§°å
³é®å" clearable style="width:200px" @keyup.enter.native="queryResults"></el-input> |
| | | </el-form-item> |
| | | <el-form-item label="å®¡æ ¸è§å"> |
| | | <el-select v-model="ruleFilter" placeholder="å
¨é¨è§å" clearable style="width:180px"> |
| | | <el-option v-for="rn in ruleOptions" :key="rn" :label="rn" :value="rn"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="é®é¢èå´"> |
| | | <el-radio-group v-model="issueScope" size="small" @change="updateBatchSelection"> |
| | | <el-radio-button label="verify">ä»
éæ ¸å®</el-radio-button> |
| | | <el-radio-button label="all">嫿éå</el-radio-button> |
| | | </el-radio-group> |
| | | <span class="hint">æéåä»
æç¤ºååãä¸éå¤çï¼å¯¼åºé»è®¤ä¸å«</span> |
| | | </el-form-item> |
| | | <el-form-item label="ç¶æ"> |
| | | <el-select v-model="statusScope" style="width:110px" @change="updateBatchSelection"> |
| | | <el-option label="å
¨é¨" value="all"></el-option> |
| | | <el-option label="å¾
å¤ç" value="pending"></el-option> |
| | | <el-option label="å·²å¤ç" value="handled"></el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="è§å¾"> |
| | |
| | | <el-button size="small" plain @click="statsCollapsed = !statsCollapsed">{{ statsCollapsed ? 'å±å¼' : 'æ¶èµ·' }}è§åç»è®¡</el-button> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <span style="color:#909399;font-size:12px">å
± {{ results.length }} æ¡é®é¢ï¼{{ mergedResults.length }} å®¶ä¼ä¸</span> |
| | | <span style="color:#303133;font-size:13px;font-weight:600">å½åçé {{ filteredResults.length }} æ¡ / {{ mergedResults.length }} å®¶ä¼ä¸</span> |
| | | <span style="color:#909399;font-size:12px;margin-left:8px">ï¼å
± {{ results.length }} æ¡é®é¢ï¼</span> |
| | | </el-form-item> |
| | | </el-form> |
| | | </div> |
| | | <div v-if="ruleStats.length && !statsCollapsed && !focusMode" class="rule-stats" style="flex:none"> |
| | | <div class="stats-cards"> |
| | | <div v-for="rs in sortedRuleStats" :key="rs.ruleId" class="stat-card" |
| | |
| | | å
± {{ results.length }} æ¡é®é¢ï¼{{ mergedResults.length }} å®¶ä¼ä¸ |
| | | </div> |
| | | <div class="table-area" style="flex:1;min-height:0"> |
| | | <!-- æç»è§å¾ï¼ä¸æ¡é®é¢ä¸å¼ å¡ç --> |
| | | <!-- æç»è§å¾ï¼ä¸æ¡é®é¢ä¸è¡ï¼ä¸å¯¼åºæ¨¡æ¿ Sheetâå®¡æ ¸ç»æâ忬¾ï¼ --> |
| | | <div v-if="viewMode === 'detail'" class="detail-cards-wrap" v-loading="loading"> |
| | | <div class="detail-toolbar"> |
| | | <el-checkbox :value="allSelected" :indeterminate="someSelected" @change="toggleSelectAll">å
¨éå½å {{ filteredResults.length }} æ¡</el-checkbox> |
| | | <span class="toolbar-tip">å¾éå坿¹é确认 / 忽ç¥</span> |
| | | </div> |
| | | <div v-for="row in filteredResults" :key="row.id" class="detail-card" |
| | | :class="{ 'card-red': row.alertLevel === 'RED', 'card-yellow': row.alertLevel === 'YELLOW', 'card-done': row.status !== 'PENDING' }"> |
| | | <div class="card-head"> |
| | | <el-checkbox v-model="row._selected" @change="updateBatchSelection"></el-checkbox> |
| | | <div class="card-ent"> |
| | | <div class="ent-name">{{ row.enterpriseName || row.city || 'æªç¥ä¼ä¸' }}</div> |
| | | <div class="ent-sub">{{ row.reportPeriod }}</div> |
| | | </div> |
| | | <el-tag :type="row.alertLevel === 'RED' ? 'danger' : 'warning'" size="small" effect="dark">{{ row.alertLevel }}</el-tag> |
| | | <el-tag :type="statusType(row.status)" size="small" effect="plain" class="status-tag"> |
| | | <i :class="statusIcon(row.status)"></i> {{ statusText(row.status) }} |
| | | </el-tag> |
| | | <span class="spacer"></span> |
| | | <el-link v-if="dataType(reportType)" type="primary" :underline="false" size="mini" @click="openMonthReport(row)">æ°æ®</el-link> |
| | | <el-button v-if="row.status === 'PENDING'" size="mini" type="success" icon="el-icon-check" round @click="review(row, 'CONFIRMED')">确认</el-button> |
| | | <el-button v-if="row.status === 'PENDING'" size="mini" type="info" icon="el-icon-close" round plain @click="review(row, 'IGNORED')">忽ç¥</el-button> |
| | | <span v-else class="done-text">å·²å¤ç</span> |
| | | <el-table :data="filteredResults" border stripe height="100%" |
| | | :header-cell-style="{ background: '#F5F7FA', color: '#303133', fontWeight: 600 }"> |
| | | <el-table-column width="40"> |
| | | <template slot-scope="{ row }"> |
| | | <el-checkbox v-model="row._selected" @change="updateBatchSelection"></el-checkbox> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="reportPeriod" label="æ¥è¡¨æ" width="90"></el-table-column> |
| | | <el-table-column label="ä¼ä¸/å¸å·" min-width="200" show-overflow-tooltip> |
| | | <template slot-scope="{ row }">{{ row.enterpriseName || row.city || 'æªç¥ä¼ä¸' }}</template> |
| | | </el-table-column> |
| | | <el-table-column prop="ruleCode" label="è§åç¼ç " width="100"></el-table-column> |
| | | <el-table-column prop="ruleName" label="å®¡æ ¸è§å" min-width="150" show-overflow-tooltip></el-table-column> |
| | | <el-table-column label="ç级" width="76"> |
| | | <template slot-scope="{ row }"> |
| | | <el-tag :type="row.alertLevel === 'RED' ? 'danger' : 'warning'" size="mini">{{ row.alertLevel }}</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="actualValue" label="å®é
å¼" min-width="230" show-overflow-tooltip></el-table-column> |
| | | <el-table-column prop="thresholdValue" label="éå¼/åºå" min-width="140" show-overflow-tooltip></el-table-column> |
| | | <el-table-column prop="deviation" label="å差说æ" min-width="170" show-overflow-tooltip></el-table-column> |
| | | <el-table-column label="ç¶æ" width="92"> |
| | | <template slot-scope="{ row }"> |
| | | <el-tag :type="statusType(row.status)" size="mini"> |
| | | <i :class="statusIcon(row.status)"></i> {{ statusText(row.status) }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="æä½" width="185" align="right"> |
| | | <template slot-scope="{ row }"> |
| | | <el-link v-if="dataType(reportType)" type="primary" :underline="false" size="mini" @click="openMonthReport(row)">æ°æ®</el-link> |
| | | <el-button v-if="row.status === 'PENDING'" size="mini" type="success" icon="el-icon-check" round @click="review(row, 'CONFIRMED')">确认</el-button> |
| | | <el-button v-if="row.status === 'PENDING'" size="mini" type="info" icon="el-icon-close" round plain @click="review(row, 'IGNORED')">忽ç¥</el-button> |
| | | <span v-else class="done-text">å·²å¤ç</span> |
| | | </template> |
| | | </el-table-column> |
| | | <div slot="empty"> |
| | | <el-empty description="å½åç鿡件䏿²¡æé®é¢æ°æ®"></el-empty> |
| | | </div> |
| | | <div class="card-body"> |
| | | <div class="rule-title"> |
| | | <b>{{ row.ruleName }}</b> |
| | | <span class="rule-code">{{ row.ruleCode }}</span> |
| | | </div> |
| | | <div class="card-details"> |
| | | <div class="detail-line"><span class="dt-label">å®é
</span><span class="dt-val">{{ row.actualValue }}</span></div> |
| | | <div class="detail-line"><span class="dt-label">åºå</span><span class="dt-val">{{ row.thresholdValue }}</span></div> |
| | | <div class="detail-line"><span class="dt-label">å·®å¼</span><span class="dt-val dt-warn">{{ row.deviation }}</span></div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div v-if="!filteredResults.length" class="empty-tip"> |
| | | <el-empty description="å½åç鿡件䏿²¡æé®é¢æ°æ®"></el-empty> |
| | | </div> |
| | | </el-table> |
| | | </div> |
| | | <!-- åå¹¶è§å¾ --> |
| | | <el-table v-else :data="mergedResults" border stripe v-loading="loading" height="100%" |
| | |
| | | data() { |
| | | return { |
| | | period: '', reportType: 'H2032', loading: false, marking: false, results: [], ruleStats: [], statsCollapsed: false, |
| | | enterpriseFilter: '', ruleFilter: '', viewMode: 'merged', batchSelection: [], focusMode: false, |
| | | enterpriseFilter: '', ruleFilter: '', viewMode: 'merged', issueScope: 'verify', statusScope: 'all', batchSelection: [], focusMode: false, |
| | | explainDrafts: {}, savingExplainId: null, expandedExplain: null, |
| | | monthVisible: false, monthLoading: false, monthType: '', monthPeriod: '', monthKeyword: '', monthRecords: [], monthColumns: [] |
| | | } |
| | |
| | | }, |
| | | severityStats() { |
| | | const s = { red: 0, yellow: 0, pending: 0, done: 0 } |
| | | this.results.forEach(r => { |
| | | this.filteredResults.forEach(r => { |
| | | if (r.alertLevel === 'RED') s.red++ |
| | | else if (r.alertLevel === 'YELLOW') s.yellow++ |
| | | if (r.status === 'PENDING') s.pending++ |
| | |
| | | filteredResults() { |
| | | const kw = (this.enterpriseFilter || '').trim() |
| | | const rule = this.ruleFilter |
| | | if (!kw && !rule) return this.results |
| | | const scope = this.issueScope || 'verify' |
| | | const st = this.statusScope || 'all' |
| | | return this.results.filter(r => { |
| | | if (kw && !(r.enterpriseName || '').includes(kw)) return false |
| | | if (rule && r.ruleName !== rule) return false |
| | | if (scope === 'verify' && this.isReminderRow(r)) return false |
| | | if (st === 'pending' && r.status !== 'PENDING') return false |
| | | if (st === 'handled' && (!r.status || r.status === 'PENDING')) return false |
| | | return true |
| | | }) |
| | | }, |
| | |
| | | } |
| | | }, |
| | | methods: { |
| | | isReminderRow(r) { |
| | | return !!(r && r.ruleCode === 'RULE_004') |
| | | }, |
| | | dataType(t) { return DATA_TYPE_MAP[t] || '' }, |
| | | explainType(t) { return EXPLAIN_TYPES.indexOf(t) >= 0 }, |
| | | openMonthReport(row) { |
| | |
| | | }, |
| | | exportExcel() { |
| | | if (!this.period) { this.$message.warning('è¯·éæ©æ¥è¡¨æ'); return } |
| | | window.open('/api/audit/export?period=' + this.period + '&reportType=' + this.reportType) |
| | | const q = 'period=' + this.period + '&reportType=' + this.reportType + '&onlyVerify=' + (this.issueScope !== 'all') + '&status=' + (this.statusScope || 'all') |
| | | window.open('/api/audit/export?' + q) |
| | | }, |
| | | review(row, status) { |
| | | if (status === 'IGNORED') { |
| | |
| | | .mr-sec-tab th { background: #F5F7FA; font-weight: 600; } |
| | | .mr-note { margin: 6px 12px 12px; background: #FDF6EC; border: 1px solid #FAECD8; border-radius: 4px; padding: 8px 10px; font-size: 12px; color: #8a6d3b; white-space: pre-wrap; word-break: break-all; } |
| | | .mr-note-line { margin: 2px 0; } |
| | | |
| | | .audit-filter-bar { background:#FAFAFA; border:1px solid #EBEEF5; border-radius:8px; padding:2px 12px 0; margin-top:8px; flex:none; } |
| | | .audit-filter-bar .el-form-item { margin-bottom:6px; } |
| | | .audit-filter-bar .el-form-item__label { color:#606266; font-weight:600; } |
| | | .hint { color:#909399; font-size:12px; margin-left:6px; white-space:nowrap; } |
| | | </style> |
| | |
| | | message: '' |
| | | } |
| | | this.importOne(row).then(() => { |
| | | this.showImportResult(row) |
| | | if ((row.type === 'investment' || row.type === 'investmentLogistics') && row.status === 'success') { |
| | | this.checkInvest(this.period) |
| | | } |
| | |
| | | }) |
| | | .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' |
| | |
| | | </el-aside> |
| | | <el-main> |
| | | <h3>导å
¥æ°æ®æ¥ç</h3> |
| | | <el-form :inline="true"> |
| | | <el-form-item label="æ°æ®ç±»å"> |
| | | <el-radio-group v-model="type" @change="loadData"> |
| | | <el-radio-button label="h2032">H203-2ææ¥</el-radio-button> |
| | | <el-radio-button label="transportAuth">è¿æ¿è½¦è¾</el-radio-button> |
| | | <el-radio-button label="trackMileage">轨迹éç¨</el-radio-button> |
| | | <el-radio-button label="scaleSplit">è§ä¸è§ä¸æå</el-radio-button> |
| | | <el-radio-button label="freightTurnover">è´§è¿éå¨è½¬é</el-radio-button> |
| | | <el-radio-button label="h2031">H203-1æ
å®¢ææ¥</el-radio-button> |
| | | <el-radio-button label="passengerAuth">æ
å®¢è¿æ¿è½¦è¾</el-radio-button> |
| | | <el-radio-button label="cityBus">åå¸å
¬äº¤ææ¥</el-radio-button> |
| | | <el-radio-button label="cityTaxi">巡游åºç§ææ¥</el-radio-button> |
| | | <el-radio-button label="cityTaxiAuth">åºç§è½¦è¿æ¿è½¦è¾</el-radio-button> |
| | | </el-radio-group> |
| | | </el-form-item> |
| | | <el-form-item label="æ¥è¡¨æ"> |
| | | <el-date-picker v-model="period" type="month" placeholder="å
¨é¨" value-format="yyyy-MM" clearable style="width:140px" @change="loadData"></el-date-picker> |
| | | </el-form-item> |
| | | <el-form-item label="å
³é®å"> |
| | | <el-input v-model="keyword" placeholder="ä¼ä¸/å¸å·åç§°" clearable style="width:200px" @keyup.enter.native="loadData"></el-input> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="loadData">æ¥è¯¢</el-button> |
| | | <el-button type="success" icon="el-icon-full-screen" @click="toggleFullscreen">å
¨å±æ¥ç</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | <div style="color:#909399;font-size:12px;margin-bottom:6px"> |
| | | å
± {{ total }} æ¡è®°å½ï¼å½å第 {{ page }} 页 |
| | | <div class="toolbar"> |
| | | <span class="toolbar-label">æ°æ®ç±»å</span> |
| | | <el-radio-group v-model="activeModule" size="small" @change="onModuleChange"> |
| | | <el-radio-button v-for="g in modules" :key="g.key" :label="g.key">{{ g.name }}</el-radio-button> |
| | | </el-radio-group> |
| | | <el-select v-model="type" size="small" style="width:250px" placeholder="éæ©æ°æ®ç±»å" @change="onTypeSelect"> |
| | | <el-option-group :label="activeTypes.name"> |
| | | <el-option v-for="t in activeTypes.types" :key="t.value" :label="t.label" :value="t.value"></el-option> |
| | | </el-option-group> |
| | | </el-select> |
| | | <span class="toolbar-sep"></span> |
| | | <el-date-picker v-model="period" type="month" placeholder="æ¥è¡¨æï¼å
¨é¨ï¼" value-format="yyyy-MM" clearable style="width:150px" @change="doQuery"></el-date-picker> |
| | | <el-input v-model="keyword" placeholder="ä¼ä¸/å¸å·åç§°" clearable style="width:180px" @keyup.enter.native="doQuery"></el-input> |
| | | <el-button type="primary" size="small" @click="doQuery">æ¥è¯¢</el-button> |
| | | <el-button size="small" icon="el-icon-full-screen" @click="toggleFullscreen">å
¨å±æ¥ç</el-button> |
| | | </div> |
| | | <div class="load-hint">å
± {{ total }} æ¡è®°å½ï¼å·²å è½½ {{ records.length }} æ¡</div> |
| | | <div class="table-wrap" ref="tableWrap"> |
| | | <div class="table-area"> |
| | | <el-table :data="records" border v-loading="loading" size="small" style="width:100%" height="100%"> |
| | | <el-table-column v-for="c in columns" :key="c.prop" :prop="c.prop" :label="c.label" :min-width="c.width" show-overflow-tooltip> |
| | | <el-table-column v-for="c in visibleColumns" :key="c.prop" :prop="c.prop" :label="c.label" :min-width="c.width" show-overflow-tooltip> |
| | | <template slot-scope="scope"> |
| | | <span>{{ formatCell(scope.row[c.prop]) }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | <el-pagination style="margin-top:15px;text-align:right" |
| | | background layout="total, prev, pager, next, sizes" |
| | | :total="total" :current-page.sync="page" :page-size.sync="size" |
| | | :page-sizes="[20, 50, 100]" @current-change="loadData" @size-change="loadData"> |
| | | </el-pagination> |
| | | </div> |
| | | |
| | | </el-main> |
| | | </el-container> |
| | | </el-container> |
| | |
| | | name: 'DataView', |
| | | data() { |
| | | return { |
| | | modules: [ |
| | | { |
| | | key: 'freight', name: 'é路货è¿', types: [ |
| | | { value: 'h2032', label: 'é路货ç©è¿è¾æåº¦ç产æ
åµ' }, |
| | | { value: 'transportAuth', label: 'è¿æ¿è½¦è¾' }, |
| | | { value: 'trackMileage', label: '轨迹éç¨' }, |
| | | { value: 'scaleSplit', label: 'è§ä¸è§ä¸æå' }, |
| | | { value: 'freightTurnover', label: 'è´§è¿éå¨è½¬é' } |
| | | ] |
| | | }, |
| | | { |
| | | key: 'passenger', name: 'å
¬è·¯æ
客', types: [ |
| | | { value: 'h2031', label: 'å
¬è·¯æ
客è¿è¾æåº¦ç产æ
åµ' }, |
| | | { value: 'passengerAuth', label: 'è¿æ¿è½¦è¾æ°' } |
| | | ] |
| | | }, |
| | | { |
| | | key: 'city', name: 'åå¸å®¢è¿', types: [ |
| | | { value: 'cityBus', label: 'åå¸å
Œ
±äº¤éæåº¦è¿è¥æ
åµ' }, |
| | | { value: 'cityTaxi', label: '巡游åºç§æ±½è½¦è¿è¥æå¡æ
嵿æ¥' }, |
| | | { value: 'cityTaxiAuth', label: 'åºç§è½¦è¿æ¿è½¦è¾' } |
| | | ] |
| | | } |
| | | ], |
| | | activeModule: 'freight', |
| | | type: 'h2032', |
| | | period: '', |
| | | keyword: '', |
| | | page: 1, |
| | | size: 20, |
| | | size: 50, |
| | | total: 0, |
| | | records: [], |
| | | columns: [], |
| | |
| | | loadingMore: false |
| | | } |
| | | }, |
| | | computed: { |
| | | activeTypes() { |
| | | const g = this.modules.find(x => x.key === this.activeModule) || this.modules[0] |
| | | return g |
| | | }, |
| | | visibleColumns() { |
| | | const hiddenProps = ['enterpriseCode', 'unifiedCreditCode', 'reportUnit'] |
| | | const hiddenLabels = ['ä¼ä¸ä»£ç ', 'ç»ä¸ç¤¾ä¼ä¿¡ç¨ä»£ç ', 'å¡«æ¥åä½'] |
| | | return (this.columns || []).filter(c => |
| | | hiddenProps.indexOf(c.prop) < 0 && hiddenLabels.indexOf(c.label) < 0) |
| | | } |
| | | }, |
| | | methods: { |
| | | onModuleChange(key) { |
| | | const g = this.modules.find(x => x.key === key) |
| | | if (!g || !g.types.length) return |
| | | if (!g.types.some(t => t.value === this.type)) { |
| | | this.type = g.types[0].value |
| | | } |
| | | this.doQuery() |
| | | }, |
| | | onTypeSelect() { this.doQuery() }, |
| | | doQuery() { |
| | | this.page = 1 |
| | | this.records = [] |
| | | this.loadData() |
| | | }, |
| | | loadData() { |
| | | this.loading = true |
| | | this.loadingMore = false |
| | |
| | | }).then(res => { |
| | | const d = res.data || {} |
| | | this.records = d.records || [] |
| | | this.columns = d.columns || [] |
| | | this.columns = (d.columns || []).filter(c => ['enterpriseCode', 'unifiedCreditCode', 'reportUnit'].indexOf(c.prop) < 0 && ['ä¼ä¸ä»£ç ', 'ç»ä¸ç¤¾ä¼ä¿¡ç¨ä»£ç ', 'å¡«æ¥åä½'].indexOf(c.label) < 0) |
| | | this.total = d.total || 0 |
| | | if (this.page > 1 && this.records.length === 0) { |
| | | this.page = 1 |
| | |
| | | this.records = this.records.concat(addRows) |
| | | this.page = nextPage |
| | | if (d.total != null) this.total = d.total |
| | | if (d.columns && d.columns.length) this.columns = d.columns |
| | | if (d.columns && d.columns.length) this.columns = d.columns.filter(c => ['enterpriseCode', 'unifiedCreditCode', 'reportUnit'].indexOf(c.prop) < 0 && ['ä¼ä¸ä»£ç ', 'ç»ä¸ç¤¾ä¼ä¿¡ç¨ä»£ç ', 'å¡«æ¥åä½'].indexOf(c.label) < 0) |
| | | }).catch(e => { |
| | | if (e && e._toast) return |
| | | this.$message.error('å è½½æ´å¤å¤±è´¥') |
| | |
| | | </script> |
| | | <style scoped> |
| | | .el-header { background: #409EFF; color: white; line-height: 60px; font-size: 18px; } |
| | | #app .table-wrap { |
| | | display: flex; |
| | | flex-direction: column; |
| | | height: calc(100vh - 330px); |
| | | min-height: 300px; |
| | | } |
| | | #app .table-area { |
| | | flex: 1 1 auto; |
| | | min-height: 0; |
| | | overflow: hidden; |
| | | } |
| | | #app .table-area .el-table { |
| | | width: 100%; |
| | | height: 100%; |
| | | } |
| | | #app .table-wrap .el-pagination { |
| | | flex: none; |
| | | margin-top: 8px; |
| | | } |
| | | #app .table-area:fullscreen { |
| | | padding: 20px; |
| | | background: #fff; |
| | | overflow: auto; |
| | | } |
| | | #app .table-wrap:fullscreen { |
| | | padding: 20px; |
| | | background: #fff; |
| | | overflow: auto; |
| | | display: flex; |
| | | flex-direction: column; |
| | | height: 100vh; |
| | | } |
| | | #app .table-wrap:fullscreen .table-area { |
| | | flex: 1 1 auto; |
| | | min-height: 0; |
| | | } |
| | | #app .table-wrap:fullscreen .el-pagination { |
| | | flex: none; |
| | | } |
| | | .type-row { margin-bottom: 10px; display: flex; align-items: center; gap: 10px; flex-wrap: wrap; } |
| | | .type-label { color: #606266; font-weight: 600; } |
| | | .filter-row { margin-bottom: 4px; } |
| | | #app .toolbar { display:flex; align-items:center; gap:10px; flex-wrap:wrap; padding:8px 12px; background:#FAFAFA; border:1px solid #EBEEF5; border-radius:8px; margin-bottom:8px; } |
| | | #app .toolbar-label { color:#606266; font-weight:600; white-space:nowrap; } |
| | | #app .toolbar-sep { width:1px; height:22px; background:#DCDFE6; margin:0 2px; } |
| | | #app .load-hint { color:#909399; font-size:12px; margin-bottom:6px; } |
| | | #app .table-wrap { display:flex; flex-direction:column; height:calc(100vh - 250px); min-height:300px; } |
| | | #app .table-area { flex:1 1 auto; min-height:0; overflow:hidden; } |
| | | #app .table-area .el-table { width:100%; height:100%; } |
| | | #app .table-area:fullscreen { padding:20px; background:#fff; overflow:auto; } |
| | | #app .table-wrap:fullscreen { padding:20px; background:#fff; overflow:auto; display:flex; flex-direction:column; height:100vh; } |
| | | #app .table-wrap:fullscreen .table-area { flex:1 1 auto; min-height:0; } |
| | | </style> |
| | |
| | | |
| | | <!-- 第äºè¡ï¼æ¥è¡¨ç±»å忢ï¼è´§è¿ / æ
客 / æèµ / è½è / åå¸å®¢è¿ï¼ï¼el-radio-group æ¿ä»£ el-tabsï¼è§é¿ el-tabs å¸å±æ é循ç¯ï¼ä¸ /import ä¿®å¤ä¸è´ï¼ --> |
| | | <el-radio-group v-model="activeKey" class="module-tabs"> |
| | | <el-radio-button v-for="m in modules" :key="m.key" :label="m.key"> |
| | | <el-radio-button v-for="m in visibleModules" :key="m.key" :label="m.key"> |
| | | {{ m.title }}<el-tag v-if="m.tag" size="mini" :type="m.tagType || 'info'" style="margin-left:6px">{{ m.tag }}</el-tag> |
| | | </el-radio-button> |
| | | </el-radio-group> |
| | |
| | | <span v-if="!it.ready" class="cov-bad" style="margin-left:10px">{{ it.note }}</span> |
| | | <span v-if="it.ready && it.audited === false" class="cov-bad" style="margin-left:10px">æªå®¡æ ¸</span> |
| | | </div> |
| | | <table class="preview-summary" v-if="it.summary && it.summary.length"> |
| | | <table class="preview-summary wb-table" v-if="it.key === 'summaryWorkbook' && it.summary && it.summary.length"> |
| | | <thead> |
| | | <tr><th style="width:44%">ææ </th><th style="width:30%">æ°æ®</th><th>忝</th></tr> |
| | | </thead> |
| | | <tbody> |
| | | <template v-for="(sec, si) in wbRows(it.summary)"> |
| | | <tr v-for="(row, ri) in sec.rows" :key="si + '-' + ri"> |
| | | <td v-if="ri === 0" :rowspan="sec.rows.length" class="wb-sec">{{ sec.name }}</td> |
| | | <td class="wb-label">{{ row.label }}</td> |
| | | <td class="wb-value">{{ row.value === null ? 'â' : fmtVal(row.value) }}</td> |
| | | <td class="wb-yoy" :class="{ 'yoy-empty': row.yoy === null }">{{ row.yoy === null ? 'â' : row.yoy }}</td> |
| | | </tr> |
| | | </template> |
| | | </tbody> |
| | | </table> |
| | | <div v-if="it.key === 'summaryWorkbook' && wbMissing(it.summary)" class="wb-note"> |
| | | é¨åææ ç¼ºå°ä¸å¹´åæï¼{{ wbPrevYear() }}ï¼åºææ°æ®ï¼åæ¯ææªè®¡ç® |
| | | </div> |
| | | <table class="preview-summary" v-else-if="it.summary && it.summary.length"> |
| | | <tbody> |
| | | <tr v-for="(s, i) in it.summary" :key="i"> |
| | | <td>{{ s.label }}</td> |
| | |
| | | ], checked: [] |
| | | }, |
| | | { |
| | | key: 'freight', title: 'è´§è¿æ¥è¡¨', items: [ |
| | | key: 'freight', title: 'è´§è¿æ¥è¡¨', hidden: true, items: [ |
| | | { key: 'cityDetail', label: 'è´§è¿éåå¸å·æç»', fileName: 'çæ_è´§è¿éåå¸å·æç».xlsx', status: null, statusNote: '' }, |
| | | { key: 'freightRank', label: 'è´§è¿éæå', fileName: 'çæ_è´§è¿éæå.xlsx', status: null, statusNote: '' }, |
| | | { key: 'turnoverRank', label: 'å¨è½¬éæå', fileName: 'çæ_å¨è½¬éæå.xlsx', status: null, statusNote: '' } |
| | | ], checked: [] |
| | | }, |
| | | { |
| | | key: 'passenger', title: 'æ
客æ¥è¡¨', items: [ |
| | | key: 'passenger', title: 'æ
客æ¥è¡¨', hidden: true, items: [ |
| | | { key: 'passengerCityDetail', label: 'å
¬è·¯æ
客åå¸å·', fileName: 'çæ_å
¬è·¯æ
客åå¸å·.xlsx', status: null, statusNote: '' }, |
| | | { key: 'passengerMidDetail', label: 'ä¸å£å¾æç»', fileName: 'çæ_å
¬è·¯æ
客ä¸å£å¾æç».xlsx', status: null, statusNote: '' }, |
| | | { key: 'passengerMidRank', label: 'ä¸å£å¾æå', fileName: 'çæ_å
¬è·¯æ
客ä¸å£å¾æå.xlsx', status: null, statusNote: '' }, |
| | | { key: 'passengerMidAnalysis', label: 'ä¸å£å¾åæ', fileName: 'çæ_å
¬è·¯æ
客ä¸å£å¾åæ.xlsx', status: null, statusNote: '' } |
| | | ], checked: [] |
| | | }, { |
| | | key: 'cityPassenger', title: 'åå¸å®¢è¿æ¥è¡¨', items: [ |
| | | key: 'cityPassenger', title: 'åå¸å®¢è¿æ¥è¡¨', hidden: true, items: [ |
| | | { key: 'cityBusDetail', label: 'åå¸å
¬äº¤å®¢è¿éåå¸å·æç»', fileName: 'çæ_åå¸å
¬äº¤å®¢è¿éåå¸å·æç».xlsx', status: null, statusNote: '' }, |
| | | { key: 'cityTaxiDetail', label: '巡游åºç§å®¢è¿éåå¸å·æç»', fileName: 'çæ_巡游åºç§å®¢è¿éåå¸å·æç».xlsx', status: null, statusNote: '' }, |
| | | { key: 'cityRailFerryDetail', label: '轨é轮渡客è¿éåå¸å·æç»', fileName: 'çæ_轨é轮渡客è¿éåå¸å·æç».xlsx', status: null, statusNote: '' }, |
| | |
| | | const s = sessionStorage.getItem('modules') |
| | | if (!s) return [] |
| | | return s.split(',').filter(Boolean) |
| | | }, |
| | | visibleModules() { |
| | | return this.modules.filter(m => !m.hidden) |
| | | }, |
| | | curModule() { |
| | | return this.modules.find(m => m.key === this.activeKey) || this.modules[0] |
| | |
| | | } |
| | | }, |
| | | created() { |
| | | const mine = this.myModules.filter(k => this.modules.some(m => m.key === k)) |
| | | this.activeKey = (mine.length ? mine : this.modules.map(m => m.key))[0] |
| | | const mine = this.myModules.filter(k => this.visibleModules.some(m => m.key === k)) |
| | | this.activeKey = (mine.length ? mine : this.visibleModules.map(m => m.key))[0] |
| | | this.ensureCycle() |
| | | this.refreshAll() |
| | | }, methods: { |
| | |
| | | } |
| | | return String(v) |
| | | }, |
| | | wbRows(summary) { |
| | | const get = (key) => { |
| | | const hit = (summary || []).find(x => x.label === key) |
| | | return hit && hit.value !== null && hit.value !== undefined && hit.value !== '' ? hit.value : null |
| | | } |
| | | const pct = (key) => { |
| | | const v = get(key) |
| | | return v === null ? null : this.fmtVal(v) + '%' |
| | | } |
| | | const mk = (defs) => defs.map(d => ({ |
| | | label: d[0], |
| | | value: get(d[1]), |
| | | yoy: d[2] ? pct(d[2]) : null |
| | | })) |
| | | return [ |
| | | { |
| | | name: 'é路货è¿', |
| | | rows: mk([ |
| | | ['å½æè´§è¿é(ä¸å¨)', 'å
¨çè´§è¿éÂ·å½æ'], |
| | | ['å½æè´§ç©å¨è½¬é(ä¸å¨)', 'å
¨çè´§ç©å¨è½¬éÂ·å½æ', 'å
¨çè´§ç©å¨è½¬éÂ·å½æåæ¯'], |
| | | ['累计货è¿é(ä¸å¨)', 'å
¨çè´§è¿é·累计'], |
| | | ['累计货ç©å¨è½¬é(ä¸å¨å
¬é)', 'å
¨çè´§ç©å¨è½¬é·累计', 'å
¨çè´§ç©å¨è½¬éÂ·ç´¯è®¡åæ¯'] |
| | | ]) |
| | | }, |
| | | { |
| | | name: 'åå¸å®¢è¿', |
| | | rows: mk([ |
| | | ['彿åå¸å®¢è¿é:', 'åå¸å®¢è¿éÂ·å½æ'], |
| | | ['彿åå¸å®¢è¿å¨è½¬é', 'åå¸å®¢è¿å¨è½¬éÂ·å½æ'], |
| | | ['累计åå¸å®¢è¿é:', 'åå¸å®¢è¿é·累计'], |
| | | ['累计åå¸å®¢è¿å¨è½¬é', 'åå¸å®¢è¿å¨è½¬é·累计'] |
| | | ]) |
| | | }, |
| | | { |
| | | name: 'ä¸å£å¾', |
| | | rows: mk([ |
| | | ['彿ä¸å£å¾å®¢è¿é', 'ä¸å£å¾å®¢è¿éÂ·å½æ'], |
| | | ['彿ä¸å£å¾å¨è½¬é', 'ä¸å£å¾å¨è½¬éÂ·å½æ'], |
| | | ['累计ä¸å£å¾å®¢è¿é', 'ä¸å£å¾å®¢è¿é·累计'], |
| | | ['累计ä¸å£å¾å¨è½¬é', 'ä¸å£å¾å¨è½¬é·累计'] |
| | | ]) |
| | | } |
| | | ] |
| | | }, |
| | | wbMissing(summary) { |
| | | let total = 0 |
| | | let have = 0 |
| | | ;(summary || []).forEach(x => { |
| | | if (String(x.label).indexOf('忝') >= 0) return |
| | | total++ |
| | | if (x.value !== null && x.value !== undefined && x.value !== '') have++ |
| | | }) |
| | | return total > 0 && have < total |
| | | }, |
| | | wbPrevYear() { |
| | | const m = /^(\d{4})-(\d{2})$/.exec(this.previewPeriod || '') |
| | | if (!m) return 'ä¸å¹´åæ' |
| | | return (Number(m[1]) - 1) + '-' + m[2] |
| | | }, |
| | | coverageText(it) { |
| | | const cov = it.monthCoverage || [] |
| | | if (!cov.length) return 'æ æ°æ®' |
| | |
| | | @media (max-width: 900px) { |
| | | .panel-head { flex-direction: column; align-items: flex-start; } |
| | | } |
| | | .wb-table th { background:#f5f7fa; } |
| | | .wb-sec { background:#F7F9FC; font-weight:600; text-align:center; color:#303133; } |
| | | .wb-label { color:#303133; } |
| | | .wb-value { text-align:right; font-variant-numeric: tabular-nums; } |
| | | .wb-yoy { text-align:right; color:#606266; } |
| | | .wb-yoy.yoy-empty { color:#C0C4CC; } |
| | | .wb-note { color:#E6A23C; font-size:12px; margin:2px 0 8px; } |
| | | </style> |