6个文件已添加
3 文件已重命名
9个文件已修改
1个文件已删除
| | |
| | | > **æ¯å¤©ç»ææ¶**ï¼æå½å¤©è¿å±ã踩è¿çåãæ°éæ±ãæªå®æäºé¡¹æ´æ°å°æ¬æä»¶ï¼ç¶åå¯ä»¥æ¾å¿å¼æ°å¯¹è¯ã |
| | | > **åä½çº¦å®ï¼2026-08-31ï¼**ï¼è¯´ãæäº¤ã= æ¬å° commit + æ¨éå°è¿ç«¯ gitï¼origin mainï¼Gitblitï¼ï¼ä¸ååªåæ¬å° commitã |
| | | |
| | | ### 10-12ï¼14ï¼ï¼æ±æ»å¤§è¡¨ãç¼ºæ°æ¸
åå¼¹çª + çæä»¶ãæ°æ®ç¼ºå¤±æ¸
åã页ã+ 使£å鳿§ä¿®å¤ + å
¬äº¤ 9æç¼ºè¡æ ¸æ¥ãå¾
éªæ¶ã |
| | | |
| | | - **å¨å **ï¼ç¨æ·è¦æ±æç¼ºæ°æå°å°é¢ä¸ï¼P0-3 å©ä½ï¼ï¼å¹¶å¨çæä»¶éç»ä¸é¡µãæ°æ®ç¼ºå¤±æ¸
åãã |
| | | - **æ¹å¨ï¼3 å¤ï¼**ï¼ |
| | | 1. å端 `ReportExportService.appendMissingDataSheet(...)`ï¼çææ¶å°¾å¨äº§ç©æ«å°¾è¿½å ãæ°æ®ç¼ºå¤±æ¸
åã页ï¼ç¬¬ 20 页ï¼ï¼åãä¸ãç¼ºæ°æ¸
å / äºãæ°æ®ä½æ£ / ä¸ãå
¶å®æç¤ºãï¼åæ¥è¡¨æ+çææ¶é´+æ¡æ°ï¼åªè¯»æç¤ºé¡µãä¸åä¸è®¡ç®ï¼æ¯çè¥ç±äº§ç©æ´¾ç已带åå页ä¼å
ç§»é¤å建ã |
| | | 2. å端 `ReportExport.vue`ï¼ç¹ã导åº/çæãå
è° `GET /api/report/summaryWorkbook/missing` + `freightCheck`ï¼å½ä¸åå¼¹çªï¼å两段ã坿»å¨ï¼ãä»è¦çæ / åæ¶ãï¼ä¸¤é¡¹çç©ºãæ¥å£å¼å¸¸æå¹´æ¥æ¨¡å¼ â ç´æ¥çæã |
| | | 3. `FreightReadinessCheck` ä¿®å鳿§ï¼â¡å
¨å£å¾æ´ææªå¯¼æ¶ä¸åæ `0âè§ä¸` æ¥ãè§ä¸ä¸ºè´ãï¼ä¸é¢å·²æ¥ã缺â¡å
¨å£å¾ãï¼â 2026-09 å½ä¸ 68â34ï¼2026-08 ä» 0ã |
| | | - **宿µï¼dev 8090/8080ï¼ä»
çæ/åªè¯»ï¼**ï¼missing(2026-09)=4 æ¡ï¼è´§è¿â¡â¢ãç½çº¦è½¦è®¢åãç½çº¦è½¦ pinï¼ï¼freightCheck(2026-09)=34ã2026-08=0ï¼å¼æ¥çæ 2026-09ï¼refresh=trueï¼â 1,303,490 B / 20 页 / æ¸
å 39 æ¡ï¼4+34+1ï¼ï¼è½çç¼å忥巿°ä¸º `_cache/summaryWorkbook_2026-09_month__vc0e2afa47b51bc04.xlsx`ï¼å端 `Compiled successfully`ãbundle `/js/6.js` 已嫿°ä»£ç ï¼å¹´æ¥æ¨¡å¼ä¸¤æ¥å£è¿å空ã |
| | | - **æ¥åï¼æ¬å°ãä¸å
¥åºï¼**ï¼`docs/åè½æµè¯æ¥å/åè½æµè¯æ¥å_æ±æ»å¤§è¡¨ç¼ºæ°æ¸
åå¼¹çªä¸çæä»¶é¡µ_2026-10-12.md`ãç¶æï¼**ãå¾
éªæ¶ã**ã |
| | | - **ãå¾
éªæ¶ / å¾
å®å¤º / å¾
å¤çãä¸é¡¹**ï¼ |
| | | 1. ãå¾
éªæ¶ã导åºåå¼¹çª + çæä»¶ãæ°æ®ç¼ºå¤±æ¸
åã页ï¼äº§ç© `docs/çææ±æ»å¤§è¡¨/è¾åº/çæ_éè·¯è¿è¾éæ±æ»è¡¨_2026-09.xlsx` æ«é¡µï¼ï¼ |
| | | 2. ãå¾
ç¨æ·å®å¤ºãæåé¡µç¼ºæ°æçç©ºï¼æ°å¼åç空符å颿ï¼ä½ãè´§è¿å¨è½¬éæåã`R` å仿¾ç¤º **10.00**ï¼æ¯ç `=10-Q` å¨ `Q` æ¸
空åç®åºï¼æºä¾§ç©ºè¡¨æ æ¤å
¬å¼ï¼ï¼æ¯å¦ä¸å¹¶æ¸
ç©ºæ¬æ¬¡**æªæ¹**ï¼ |
| | | 3. ãå¾
å¤çãå
¬äº¤ 2026-09 å° 1 è¡å±**æºæä»¶ç¼ºè¡**ââ`éå·å¸Â·42138100005·广水å¸å®é¡ºå
¬äº¤æéå
¬å¸` å¨ 6/7 ææºè¡¨ç¬¬ 119 è¡åå¨ã9 ææºè¡¨æ²¡æï¼`import_batch#551` è®°å½ 141/141/0ï¼é导å
¥è·³è¿ï¼â éåæ°æ®æ¥æºè¡¥ä»¶æç¡®è®¤ä¼ä¸åæ¥ï¼**æªå¯¼å
¥ä»»ä½æ°æ®**ã |
| | | - **é¡ºå¸¦å¤æ ¸**ï¼09 ææ¯ç 9 æåæ°å¼æ 误ï¼`ç线å
车!S9=20647.5363`ã`å
¬äº¤!S8=61618.09`ã`åºç§!S6=32787.72`ã`ä¸å£å¾æç»!S5=3517.9161`ï¼å
¨ç°¿ `46266` åªå© 3 ä¸ªåæ³è¡¨å¤´è¡ï¼ã |
| | | - **ç¯å¢æç¤º**ï¼æ¬æºæ®ç 1 个æ çªå£ `EXCEL.EXE`ï¼PID 23540ï¼`taskkill` æç»è®¿é®ï¼ï¼å·²ç¡®è®¤**æ¯çæªè¢«é**ï¼è¯»åæµè¯éè¿ï¼ï¼ä¸æ¬¡ç¨ Excel COM åå
夿¥è¯¥è¿ç¨ã |
| | | |
| | | ### 10-12ï¼13ï¼ï¼ä¸çå
³æºäº¤æ¥ â æ±æ»å¤§è¡¨ç¼ºæ°/æ¯çèæ ¼ä¿®å¤å·²æäº¤æ¨éï¼ä¸ä¸æ¥å¾
å䏿°ä¼è¯èµ·æ¥ |
| | | |
| | | **æ¥æå
¥å£ï¼æ°ä¼è¯å
读è¿ä¸å¤ï¼**ï¼â æ ¹ç®å½ `AGENTS.md`ï¼â¡ æ¬æä»¶é¡¶é¨ï¼æ¬è + 10-12ï¼11ï¼ï¼12ï¼ï¼ï¼â¢ ä¸ä¸ä¼è¯æ é¢ã10.8æ¥æ¯å¯¹æ±æ»å¤§è¡¨åä¸é¡¹ç»è®¡ççæé»è¾ãï¼å ä¸ä¸æè¶
鿥é䏿ï¼å
¶å·¥ä½å·²ç±æ¬èè¿æ¡çº¿åå®å¹¶è¡¥æäº¤ï¼ã |
| | | |
| | | - **ä»å¤©è¿æ¡çº¿å·²å®æå¹¶æ¨éï¼origin mainï¼æ¬å°ä¸è¿ç«¯ä¸è´ï¼**ï¼ |
| | | - `fc00eb7` æ©åè¡¨å¤´è¡æ¹æåºåæä»½åå¤å® + æ¸
ç 9 ææ¯çè¢«åææ¥æ `46266` ç 13 æ ¼ï¼ |
| | | - `ae47753` ç¼ºæ°æ¸
å代ç å
¥åº + ç¼ºæ°æä¸æ²¿ç¨ä¸ä¸ææ§å¼ï¼æåé¡µææºä¾§ç空/æ´åç空ï¼ï¼ |
| | | - `7f958f5` æ±æ»å¤§è¡¨çææ°æ®éªè¯æ¹æ³ææï¼è¡¥æäº¤ï¼ï¼`f2056aa` åå¹¶åäº 10-08 é路走å¿å¾æäº¤ï¼HANDOFF é¡¶é¨å²çªå·²æ"åæ¹é½ç"è§£å³ï¼é¡ºåºï¼zyj 线åè â åäº 10-08/10-07 åèï¼ã |
| | | - **å½åç¶æ**ï¼æ¬æº dev åå端å¨è·ï¼8090/8080ï¼ï¼åºå
2026-09 æ â H2032ãå
¬äº¤ãåºç§ãç线å
车ï¼**缺**â¡å
¨å£å¾ãâ¢æå表ãç½çº¦è½¦ï¼ç¨æ· 10-12 说æ"å
¶ä»æ°æ®ææ¶è¿æ²¡æ"ï¼ï¼9 ææ¯ç `docs\çææ±æ»å¤§è¡¨\2026å¹´9æéè·¯è¿è¾éæ±æ»è¡¨.xlsx` 已修好ï¼å¤ä»½ `_bak_2026å¹´9ææ¯ç_æ¸
çèæ ¼å_20261012.xlsx`ï¼ï¼ææ°äº§ç©å¨ `docs\çææ±æ»å¤§è¡¨\è¾åº\çæ_éè·¯è¿è¾éæ±æ»è¡¨_2026-09.xlsx`ï¼æªè·è¸ªãä¸å
¥åºï¼ã |
| | | - **ãå¾
éªæ¶ã两项ï¼è¯·ç¨æ·ç页é¢éæ°çæç 2026-09ï¼**ï¼â æå页å¨**ç¼ºæ°ææ´ä½ç空**ï¼ä¸åæ¾ç¤ºä¸ä¸ææ§å¼ï¼æ¯å¦ç¬¦å颿ï¼è¥è¦"ä¿çä¸ä¸æå¼"å¯åé `copyRankValues` ç两æ¡ç空é»è¾ï¼â¡ 9 ææ¯ç 13 æ ¼æ¸
çåç 9 æåæ°å¼ï¼ç线å
车 20647.5363ãå
¬äº¤ 61618.09ãåºç§ 32787.72ãä¸å£å¾æç» 3517.9161ï¼ã |
| | | - **ä¸ä¸æ¥å¾
åï¼æä¼å
级ï¼**ï¼â ç¼ºæ°æ¸
åç"导åºåå¼¹çª + çæä»¶ãæ°æ®ç¼ºå¤±æ¸
åã页"ï¼P0-3 å©ä½ï¼ä¸ B4 使£åå¹¶ï¼ï¼â¡ å
¬äº¤ 9 æ 141 è¡ vs 1-8 æ 142 è¡ä»å¾
æ ¸ï¼ç¨æ·è¯´å
è¿æ ·ï¼ï¼â¢ ç¨æ·è¥è¦åå°"**ä¸é¡¹ç»è®¡ / èåæ¥å¿«æ¥**"飿¡çº¿ï¼å
读 `docs/ä¸é¡¹ç»è®¡/éè¦è忥/` ä¸ AGENTS.md ç"éè¦èåæ¥å¿«æ¥ä¸åææ¥åå£å¾"ï¼â£ åå¸åå¸å·/éè·¯è¿è¾å¨è½¬é 34 个 `#DIV/0!` æç¨æ· 10-12ï¼7ï¼å³å®**ä¸å¨**ã |
| | | - **ç¯å¢/åï¼ä¸æ¬¡ç´æ¥ç¨ï¼**ï¼`mvn`ã`start-dev.ps1`/`stop-dev.ps1`ãExcel COMã`git` æäº¤æ¨éé½é `require_escalated`ï¼`.git` æ²ç®±å
åªè¯»ï¼node 访é®ç¨æ·ç®å½è¢«æï¼ï¼**Excel COM åå
ç¡®è®¤æ²¡ææ çªå£ EXCEL æ®çè¿ç¨**ï¼ä¼ææ¯çéæåªè¯»ãSave éé»å¤±æï¼ä»å¤©è¸©è¿ï¼å¤çè§ 10-12ï¼11ï¼ï¼ï¼`_tmp\_fix\` ä¸çäºå¯å¤ç¨èæ¬ï¼`expand.ps1` æ©åã`gen.ps1` çæã`repair_master.ps1` æ¯ç精修ã`verify_*.py` æ ¡éªï¼ï¼æªè·è¸ªãæçº¦å®ä¸å ã |
| | | - **æ°æ®è¾¹ç**ï¼æ¬è½®**æªå¯¼å
¥ãæªè¦çãæªé导任ä½ä¸å¡æ°æ®**ï¼åªåçæä¸åªè¯»æ ¸å¯¹ï¼ã |
| | | ### 10-12ï¼12ï¼ï¼æ±æ»å¤§è¡¨ã缺æ°ä¸çä¸ä¸ææ§å¼ãï¼æåé¡µææºä¾§ç空ï¼+ ç¼ºæ°æ¸
å代ç å
¥åºãå¾
éªæ¶ã |
| | | |
| | | - **èæ¯**ï¼ç¨æ·é®"çæè¡¨éå¾å¤æ°æ®é½æ¯ 8 æçå§"ï¼åæ¶åºéçç¼ºæ°æ¸
å代ç ä¸ç´æªæäº¤ï¼æ¬æºæ²ç®±æ
éæé´åçï¼æ²ç®±æ¢å¤åè¡¥æäº¤ï¼ã |
| | | - **å£å¾æ¹å¨ï¼3 å¤ï¼åä¸ä»¶äºï¼ç¼ºæ°ä¸çä¸ä¸ææ§å¼ï¼**ï¼ |
| | | 1. `loadCumulativeMap` / `getProvinceCumulative`ï¼â¢è§ä¸è§ä¸æå表**åªè®¤ç®æ æ**ï¼åå®ç°å
许"ç®æ æåä¹åæè¿ä¸æ"â ä¼é黿¿ä¸ä¸æçæ°å½æ¬æï¼ï¼ |
| | | 2. `copyRankValues`ï¼æºä¾§ï¼ç¬ç«æå表ï¼ç¼ºæ°æ¶**ç®æ æ ¼ç空**ï¼ä¸åæ²¿ç¨æ¯çä¸ä¸æ¥è¡¨æçæ§å¼ï¼æºä¾§æåæ ç¾ä¸æ¸
ï¼ï¼ |
| | | 3. æåå**æ´å缺æ°**ï¼æºä¾§ä¸ä¸ªæ°é½æ²¡æï¼å¦ 9 æç¼ºâ¡â¢æ¶çãè´§è¿å¨è½¬éæåãï¼â æ´ååªçæ é¢ä¸å¸å·åï¼é¿å
æ¯çèªå¸¦ RANK/å æ¯å
¬å¼å¨ç©ºåä¸ç®åº"æåå
¨ 1 + å æ¯ #DIV/0!"ã |
| | | - **ææï¼2026-09 宿µï¼äº§ç© `docs\çææ±æ»å¤§è¡¨\è¾åº\çæ_éè·¯è¿è¾éæ±æ»è¡¨_2026-09.xlsx`ï¼**ï¼ |
| | | - ãè´§è¿å¨è½¬éæåãæ´åç空 ââ ä¸ç¨æ·æ¥å¸¸æ ¸å¯¹çãçæ_å¨è½¬éæåã空æä¸è´ âï¼ |
| | | - ãè´§è¿éæåã9 æåï¼è§ä¸=â 宿°ï¼å
¨ç **3575.1211**ï¼ãè§ä¸/å计ç空ï¼ç¼ºâ¡ï¼ï¼å æ¯åæ²¿ç¨æºä¾§å
¬å¼ï¼å
¶ä¸ 72 个 #DIV/0! æ¯ç¬ç«æå表æ¬èº«å°±æçï¼ï¼ |
| | | - æ åå½ï¼å
¬äº¤ 9 æå®¢è¿é **13476.35**ãåå¸å
12021.00ãåºç§å®¢è¿é 5626.81 ä¸åºå
Σ17 å¸å·éä½ä¸è´ âï¼å
¶ä½é¡µä¸ä¸ä¸ç产ç©å·®å¼ä»
æ¥èªæ¯çèæ ¼æ¸
çä¸ç¼åå·æ°ï¼ã |
| | | - **åæ¶è¡¥æäº¤**ï¼æ²ç®±æ
éæé´éçï¼å·²æ 10-12ï¼4ï¼æ¥åéªè¯è¿ï¼ï¼`reportexport/calc/SummaryMissingDataCheck.java`ï¼åªè¯»ç¼ºæ°æ¸
åï¼ã`GET /api/report/summaryWorkbook/missing`ãå¯¼åºæ¥å¿ `dbProblems` æ¥å
¥ï¼æ¬æ¬¡å¯¼åºæ¥å¿å·²å¯è§"ç¼ºæ°æç¤º=[â¦]"ï¼ã |
| | | - **éç/å¾
确认**ï¼â åå¸åå¸å·ãéè·¯è¿è¾å¨è½¬éç 34 个 #DIV/0! æ 10-12ï¼7ï¼ç¨æ·å³å®ä¸å¨ï¼â¡ **æå页å¨ç¼ºæ°ææ´ä½ç空æ¯å¦ç¬¦å颿ï¼è¯·éªæ¶æ¶ç¡®è®¤**ï¼è¥è¦"ä¿çä¸ä¸æå¼"çå£å¾ï¼å¯åé第 2/3 æ¡ï¼ã |
| | | - **æ¥åï¼æ¬å°ãä¸å
¥åºï¼**ï¼`docs/åè½æµè¯æ¥å/åè½æµè¯æ¥å_æ±æ»å¤§è¡¨ç¼ºæ°ä¸çä¸ä¸ææ§å¼_2026-10-12.md`ãç¶æï¼**ãå¾
éªæ¶ã**ã |
| | | ### 10-12ï¼11ï¼ï¼æ©å表头è¡äºæ¬¡ä¿®æ£ + 9 ææ¯ç被æ©ååææ¥æç 13 æ ¼æ¸
çãå¾
éªæ¶ã |
| | | |
| | | - **ç°è±¡ï¼ç¨æ·æ¥åï¼**ï¼çæè¡¨é"å¾å¤æ°æ®æ¯ 8 æç"ãææ¥åç° **9 ææ¯çéæ 13 ä¸ªæ°æ®æ ¼è¢«åæäº 9 ææä»½æ¥æåºå `46266`ï¼= 2026-09-01ï¼**ï¼` è´§è¿!S11/T11`ã`ç线å
车!S9/T9`ã`å
¬äº¤!S8/T8`ã`åºç§!S6/T6`ã`ä¸å£å¾æç»!S5/T5`ï¼ä¸æ¸¸ç¼å被带åï¼`ç线å
车!S5`ã`ä¸å£å¾æç»!S99/S100/S104/S105` ç乿¯ 46266ï¼ã |
| | | - **æ ¹å **ï¼æ©åå¨ `headerRowsOf` é¦çåªè¦æ±"æ´è¡ â¥2 个æä»½æ ¼"ï¼èåºç§ç¬¬ 6 è¡ãç线å
车第 3 è¡è¿ç±»**æ°æ®è¡**鿰好æ 2 ä¸ªåæ¥æåºåçæ°å¼ â è¢«è¯¯å¤æè¡¨å¤´è¡ â æ©åæ¶æ"æä»½æ¥æ"åè¿äºæ°æ®è¡ï¼`cloneHeaderCell` 对æ°å¼æ ¼å `targetMonth` çæ¥æï¼ã |
| | | - **ä¿®æ³**ï¼æ¹ä¸º**å
å"æä»½è¡¨å¤´æ ¼æå¤çé£ä¸è¡"ä½åºåï¼æå®çæä»½å彿 åå**ï¼åªæè¿äºåä¸å¤§åæ¯æä»½æ ¼çè¡æç®è¡¨å¤´è¡ã轨éã轮渡ä¸é¡µä¸¤ä¸ªåç `[1,10]` ä¿æ âï¼` è´§è¿/å
¬äº¤/ç½çº¦è½¦/ä¸å£å¾æç»` ä»ä¸ºåè¡è¡¨å¤´ âã |
| | | - **æ¯çä¿®å¤ï¼ç¨ Excel COMï¼æªæ´æ¬éåï¼**ï¼13 æ ¼ææ©åè§å精修ï¼è¯¥çç©ºçæ¸
空ãå
¬å¼æè§åè¡¥å `=S12+S13` / `=S11/AN11-1` / `=S10+S14+â¦`ï¼+ `CalculateFull` æ´æ¬éç®å·æ°ç¼åï¼ä¿®å¤åå¤ä»½ `_bak_2026å¹´9ææ¯ç_æ¸
çèæ ¼å_20261012.xlsx`ãä¿®å¤åå
¨ç°¿åªå© 3 个 `46266` ç**åæ³è¡¨å¤´æä»½æ ¼**ï¼åå¸å®¢è¿ S2ãå
¬äº¤ S3ãåºç§ S3ï¼ã |
| | | - **éªè¯**ï¼éè· 2026-08â9 ææ©åï¼æ¥å¿è¡¨å¤´è¡ ` è´§è¿[2] ç线å
车[1] åå¸å®¢è¿[1] å
¬äº¤[2] åºç§[2] ç½çº¦è½¦[2] 轨éã轮渡[1,10] ä¸å£å¾æç»[2]`ï¼ï¼æ°æ¨ä»¶å¨è¿ 13 æ ¼ä¸å
¨é¨æ£ç¡®ï¼éè· 2026-09 çæï¼äº§ç© `docs\çææ±æ»å¤§è¡¨\è¾åº\çæ_éè·¯è¿è¾éæ±æ»è¡¨_2026-09.xlsx` ä¸ 13 æ ¼æ¢å¤ä¸ºçå® 9 æå¼ï¼`ç线å
车!S9=20647.5363`ã`å
¬äº¤!S8=61618.09`ã`åºç§!S6=32787.72`ï¼ï¼**æ 46266 æ®ç**ã |
| | | - **æ¥åï¼æ¬å°ãä¸å
¥åºï¼**ï¼`docs/åè½æµè¯æ¥å/åè½æµè¯æ¥å_æ©å表头è¡åºååä¿®æ£ä¸æ¯çèæ ¼æ¸
ç_2026-10-12.md`ãç¶æï¼**ãå¾
éªæ¶ã**ã |
| | | ### 10-12ï¼3ï¼ï¼ä¿®ã9 æäº§ç©ä¸¢ 8 æå / ç´¯è®¡æ¼æãââæ©åæ¹ä¸º**ä»¥å½æäº§ç©ä¸ºåº**ãå¾
éªæ¶ã |
| | | |
| | | - **ç°è±¡ï¼ç¨æ·æ¥åï¼**ï¼çæç 9 ææ±æ»å¤§è¡¨é **8 æåå
¨ç©º**ï¼ç´¯è®¡æ¼æ 8 æï¼è§ä¸è´§è¿é累计æ¾ç¤º 31309.58ï¼æ£ç¡®åºä¸º 1-7æ 27734.46 + 8æ 3665.36 + 9æ 3575.12 = **34974.95**ï¼ã |
| | | - **æ ¹å ï¼éå±è¯å®ï¼**ï¼â æ¯ç**ä¸å¸¦"彿å"çå¼ä¸å
¬å¼**ââ`fillFreightSheet` æç¡®"åªåå¡«ç®æ æå½æï¼1..ç®æ æ-1 以æ¯ç为å"ï¼å
¨çè¡å
¬å¼ç±çææ¶æå½æè¡¥ï¼â¡ 8 æçæ°æ®åªè½å¨**8 æäº§ç©**éãæªå忝çï¼â¢ 9 ææ¯çæ¯**ä» 8 ææ¯çæ©å**æ¥ç â ç»§æ¿"8 æå空" â çææ¶ååªè¡¥ 9 æ â 8 ææ°¸ä¹
空缺ã累计 `=C+E+â¦+Q(空)+S` æ¼æã |
| | | - **ä¿®å¤**ï¼æ©åå¨**ä¼å
以ãå½æäº§ç©ã为åº**ï¼äº§ç©å«å½æåçå¼ä¸å
¬å¼ï¼ââå该æ**ææ°äº§ç©**ï¼å¯¼åºè½çç¼å `_cache/summaryWorkbook_<period>_month__v*.xlsx`ï¼å¤æ ¹ç®å½å®ä½ï¼ï¼åä¸å°æå鿝çå¹¶æ WARNï¼å¦ä¿®ä½æ£è§å误æ¥ï¼â¡å
¨å£å¾ç¼ºå¤±æ¶ä¸åæ¥"è§ä¸ä¸ºè´"ï¼æ¹æ¥"缺â¡å
¨å£å¾ â è§ä¸æ æ³è®¡ç®"ï¼å¦å 9 æåªæè§ä¸æ¶ä¼å¯¹æ¯ä¸ªå¸å·å·å±ï¼ã |
| | | - **å
³é®å®ç°ç¹**ï¼äº§ç©ç¼åå®ä½å¿
é¡»ç¨ 3 åéæ ¹ç®å½ï¼`summaryTemplateDir` â `user.dir/â¦` â `user.dir/../â¦`ï¼ââé¦çç¨åç¸å¯¹è·¯å¾ï¼å `spring-boot:run` çå·¥ä½ç®å½æ¯**模åç®å½**èæ¾ä¸å°ï¼éé»åéå°äºæ¯çï¼è¡¨ç°ä¸º"æ¹äºä½è¿æ¯ç©º"ï¼ã |
| | | - **éªè¯**ï¼`docs/çææ±æ»å¤§è¡¨/è¾åº/_æ©å_2026å¹´9æ_å¾
确认_v3.xlsx`ï¼æ¥å¿ç¡®è®¤ base=**产ç©** `summaryWorkbook_2026-08_month__v37â¦xlsx`ï¼ï¼ |
| | | - 8 æåæå¼ï¼è´§è¿ å
¨å£å¾ **16888.7615** / è§ä¸ **3665.3638** / è§ä¸ **13223.3977**ï¼å
¬äº¤å
¨ç **12315.37**ï¼ |
| | | - 累计å
¬å¼ **å« 8 æä¸ 9 æ**ï¼`=C5+E5+â¦+Q5+S5`ï¼ç´¯è®¡åæ¯åæ¯ `SUM(X5,Z5,â¦,AL5,AN5)`ï¼2025-01~09ï¼ï¼ |
| | | - 9 æåçç©ºï¼æ 9 ææºæ°æ®ï¼ï¼7 个æ é¢é¡µæ é¢ **1-9æ / 9æ** æ£ç¡®ã |
| | | - **å£å¾ç»è®ºï¼åè¿è®¾è®¡ï¼**ï¼**æ°æ¯çä¸å¾ä»"å½æäº§ç©"æ´¾ç**ï¼äº§ç©å¸¦å½ææ°æ®ä¸å
¬å¼ï¼ï¼ä¸å仿§æ¯çæ´¾çââå¦åæ¯æé½ä¼ä¸¢"彿å"ï¼ç´¯è®¡éææ¼æ°ã |
| | | - **éç**ï¼â å¾
ç¨æ·ç¡®è®¤æ¯å¦æ v3 è½ç为æ£å¼ 9 ææ¯çï¼ä¼å
å¤ä»½æå·¥çï¼ï¼â¡ æ©å产ç©ä¸è¢«éåçå
¬å¼ææ ç¼åå¼ï¼åªåå
¬å¼ + `fullCalcOnLoad`ï¼Excel/WPS æå¼èªå¨ç®ï¼ââ建议补"æ´æ¬éç®åç¼å"ï¼â¢ `ä¸å£å¾æç»` 表头è¡è¯å«è¿å `[2,4]`ï¼éæ ¸æ¥ã |
| | | - **æ¥åï¼æ¬å°ãä¸å
¥åºï¼**ï¼`docs/åè½æµè¯æ¥å/åè½æµè¯æ¥å_æ©åä»¥å½æäº§ç©ä¸ºåº_2026-10-12.md`ãç¶æï¼**ãå¾
éªæ¶ã**ã |
| | | ### 10-12ï¼4ï¼ï¼æ©åå
¬å¼å
é两å¤ä¿®æ£ + 9 ææ¯çè½ç + 端å°ç«¯éªè¯éè¿ãå¾
éªæ¶ã |
| | | |
| | | - **ä¿®åé®é¢ï¼å䏿¹æ¬¡çä¸ä¸ªï¼**ï¼â å
é"彿å¼å"æ¶å
¬å¼æªç¸å¯¹å³ç§»ï¼æ°ååæ `=Q7+Q9`ï¼åº `=S7+S9`ï¼â å
¨çè¡åå°ä¸ææ°ï¼â¡ å
é"彿忝å"æ¶ååæåæ§æä»½ï¼`=Q5/AN5-1`ï¼åº `=S5/AN5-1`ï¼â ç®çæ¯"䏿å¼Ã·å»å¹´åæ"ï¼â¢ POI `setCellFormula` **䏿¥åå导 `=`** â æ¥å£ 500ï¼`FormulaParseException`ï¼Â§6.3 åå²åï¼ã |
| | | - **ä¿®æ³**ï¼æ°å¢ `shiftRefsAll`ï¼å
鿶坹"æ¬é¡µ/å·²æ©å页"å¼ç¨**æ æ¡ä»¶ +2**ï¼ç¸å¯¹å¤å¶ï¼ä¸ `cloneYoyCell`ï¼ä¸å
éï¼ç´æ¥å `æ¬æå¼/表ä¸å®ä½çå»å¹´åæ-1`ï¼å³ C3 å£å¾ï¼ï¼å
¬å¼ä¸²å»æå导 `=`ã |
| | | - **9 ææ¯çè½çï¼ç¨æ·å·²ç¡®è®¤ï¼**ï¼æå·¥çå¤ä»½ä¸º `_bak_2026å¹´9ææ¯ç_æå·¥ç_20261012.xlsx`ï¼æ°æ¯ç = "8 æäº§ç© â æ©å"ï¼1,083,567 Bï¼è¾åºç®å½å½åçé¡ºï¼ææ°ä»¶ `_æ©å_2026å¹´9æ_å¾
确认.xlsx`ï¼æ§çå `_bak_` åç¼ï¼ã |
| | | - **端å°ç«¯éªè¯ï¼ç¨æ°æ¯ççæ 2026-09ï¼**ï¼ |
| | | - è´§è¿ 8 æåæå¼ï¼å
¨å£å¾ **16888.7615**ãè§ä¸ **3665.3638**ãè§ä¸ **13223.3977**ï¼ |
| | | - 9 æåï¼å
¨å£å¾ 3575.1211ï¼â¡è¡¨æªå¯¼ï¼åªæâ è§ä¸ï¼ãè§ä¸ 3575.1211ï¼ |
| | | - **累计ï¼å
¨å£å¾ 133887.0138ï¼=1-8æ 130311.89 + 9æ 3575.12ï¼ãè§ä¸ 34974.9459ï¼=27734.46+3665.36+3575.12ï¼ãè§ä¸ 98912.0679** â 䏿ç®ä¸è´ï¼ |
| | | - å
¬äº¤ï¼8 æ 12315.37ã累计 110776.34 âã |
| | | - **éç**ï¼â æ©å产ç©è¢«éåçå
¬å¼æ ç¼åå¼ï¼å»ºè®®è¡¥"æ´æ¬éç®åç¼å"ï¼ï¼â¡ `ä¸å£å¾æç»` 表头è¡è¯å«è¿å `[2,4]` å¾
æ ¸æ¥ï¼â¢ â¡è¡¨ï¼å
¨å£å¾ï¼9 ææªå¯¼å
¥åï¼"å
¨å£å¾"è¡æ¾ç¤ºçäºè§ä¸ï¼å
¬å¼ =è§ä¸+è§ä¸ãè§ä¸ä¸ºç©ºï¼ï¼ä½æ£å·²æç¤ºï¼å¯¼æ°æ®åèªå¨æ£å¸¸ã |
| | | - **æ¥åï¼æ¬å°ãä¸å
¥åºï¼**ï¼`docs/åè½æµè¯æ¥å/åè½æµè¯æ¥å_æ©åå
¬å¼å
éä¿®æ£ä¸9æéªè¯_2026-10-12.md`ãç¶æï¼**ãå¾
éªæ¶ã**ã |
| | | ### 10-12ï¼5ï¼ï¼ä¿®ã9 æå
¬äº¤/åºç§ææ¥å¯¼ä¸è¿å»ãââ导å
¥æ¹æè¡¨å¤´åå®ä½åãå¾
éªæ¶ã |
| | | |
| | | - **ç°è±¡**ï¼ç¨æ·å¯¼å
¥ 9 æå
¬äº¤/åºç§ææ¥åå¤å¤±è´¥ï¼`import_batch` æ è®°å½ã`city_bus_monthly`/`city_taxi_monthly` æ 2026-09 è¡ â **è§£æé¶æ®µå³æé**ï¼æ²¡è¿ååºï¼ã |
| | | - **æ ¹å ï¼å®æµä¸ä»½æä»¶ï¼**ï¼9 ææ¨¡æ¿æ¯ 7 æ**å°åå¯¼è´æ´ä½å·¦ç§»**ââå
¬äº¤ 68â**66** åï¼å° `ç»ä¸ç¤¾ä¼ä¿¡ç¨ä»£ç `ã`å¡«æ¥åä½`ï¼ç¬¬ 3 å起左移 2ï¼ï¼åºç§ 41â**40** åï¼å° `å¡«æ¥åä½`ï¼å·¦ç§» 1ï¼ã导å
¥å¨åæ¯**æåºå®åå·åå¼ + åºå®å使 ¡éª** â æ ¡éªç´æ¥æã䏿件å
容ä¸ç¬¦ããå¿
éåæåé½å¨ï¼æ¯åä½ä¸å¹é
ï¼ä¸åå¼ä¹æåºå®åå·ï¼ä¸æ¹ä¼æ´ä½è¯»éåã |
| | | - **æ¹å¨ï¼æ I4 å£å¾å
¼å®¹æ¨¡æ¿å·®å¼ï¼**ï¼æ°å¢ `colByName(hdr, fallback, name[, occurrence])`ï¼æè¡¨å¤´åååï¼æªå½ä¸åéååºå®åå·ï¼ä¸ `requireHeaderColsByName(...)`ï¼åªæ ¡éªå¿
éåæååå¨ã䏿 ¡éªåä½ï¼ï¼`importCityBus`/`importCityTaxi` ææå段æ¹ä¸ºæååå¼ï¼å
¬äº¤éå¤è¡¨å¤´ãå
¶ä¸ï¼åå¸å
ãï¼2 次ï¼ç¨åºç°åºå·åºåï¼`stopCity`=1ã`passengerCity`=2ï¼ãæ å°åèªå®æµè¡¨å¤´ï¼å
¬äº¤ 42 åæ®µãåºç§ 37 åæ®µå
¨é¨å½ä¸ï¼ã |
| | | - **éªè¯**ï¼åä½å·®å¼å·²å®ä½ï¼åç§°æ å°å®å¤ï¼42/42ã37/37ï¼ï¼æ ¡éªæ¾å®½ä»ä¿ç"ç缺å"çè´åæ¦æªï¼ç¼è¯éè¿ãå端已éå¯ï¼PID 40592ï¼13:17:38ï¼ã |
| | | - **å¾
å**ï¼**è¯·ç¨æ·å¨é¡µé¢é导è¿ä¸¤ä¸ª 9 ææä»¶**ï¼å©æä¸ä»£å¯¼ï¼ï¼å¯¼å
¥å婿ååªè¯»æ½æ¥ï¼è¡æ°/å¸å·/䏿ºæä»¶éå对é½ï¼å°¤å
¶"åå¸å
/åé
å乡"å£å¾å段ï¼ã |
| | | - **æ¥åï¼æ¬å°ãä¸å
¥åºï¼**ï¼`docs/åè½æµè¯æ¥å/åè½æµè¯æ¥å_å
¬äº¤åºç§å¯¼å
¥æè¡¨å¤´åå®ä½_2026-10-12.md`ãç¶æï¼**ãå¾
éªæ¶ã**ã |
| | | ### 10-12ï¼6ï¼ï¼ä¿®ãåå¸åå¸å·å½æåæ é¢åå¨ 7 æãââæ é¢åå
¥è¡å·å·®ä¸è¡ç bugãå¾
éªæ¶ã |
| | | |
| | | - **ç°è±¡**ï¼9 æäº§ç©ï¼8 æäº§ç©åæ ·ï¼é `åå¸åå¸å·` æ¾ç¤º"åå¸å®¢è¿å®¢è¿éåå¸å·**7æ**æ
åµ"ï¼æ é¢ä¸éæä»½æ´æ°ã |
| | | - **æ ¹å ï¼ä»£ç è¡å·å·®ä¸è¡ï¼**ï¼æ¯çé彿åç**æ é¢å¨ `A45`ãåä½å¨ `A46`**ï¼å¨è½¬é忝 `A67`/`A68`ï¼ï¼ä½ç¨åºåçæ¯ 0 åº `45`/`67` = **Excel 第 46/68 è¡** â â çæ é¢è¡ `A45/A67` 仿ªæ´æ°ï¼ä¸ç´å卿¯çæ§æä»½ï¼ï¼â¡ ç¨åºçæ é¢**è¦çäº"åä½ï¼ä¸äººæ¬¡/ä¸äººæ¬¡å
¬é"è¡**ã累计å `0/22` æ£ç¡®ï¼å
¶ä½é¡µï¼å叿±æ» `0/11`ãä¸å£å¾åæ `0/9`ãéè·¯è¿è¾å¨è½¬é `0/22`ãä¸¤å¼ æåï¼è¡å·åå·²æ ¸å¯¹æ 误ã |
| | | - **æ¹å¨**ï¼â 代ç `smSetTitle(sh,45,â¦)`â`44`ã`smSetTitle(sh,67,â¦)`â`66`ï¼â¡ æ¯ç被è¦çç `A46/A68` ç¨ **XML ç²¾åæ¹æ ¼**æ¢å¤ä¸º"åä½ï¼ä¸äººæ¬¡ / åä½ï¼ä¸äººæ¬¡å
¬é"ï¼å¤ç¨æä»¶å
æ¢æå
±äº«å符串索å¼ï¼ä¸æ´æ¬éåï¼ã |
| | | - **éªè¯ï¼çæ 2026-09 宿µï¼**ï¼`A45/A67` = `åå¸å®¢è¿å®¢è¿é/客è¿å¨è½¬éåå¸å·**9æ**æ
åµ` âï¼`A46/A68` = `åä½ï¼ä¸äººæ¬¡ / åä½ï¼ä¸äººæ¬¡å
¬é` âï¼ç´¯è®¡å `A1/A2/A23/A24` æ£å¸¸ âã |
| | | - **éçï¼å¾
ç¨æ·ç¡®è®¤ååï¼**ï¼â `åå¸åå¸å·` S50:S66 ä¸ `éè·¯è¿è¾å¨è½¬é` I27:I43 å **17 个 `#DIV/0!`**ï¼å æ¯åæ¯=å
¨çè¡å¨æ æ°æ®æè¢«åæ 0ï¼â 建议"çææ¶å°¾ç»ä¸é误å¼çç½ + æ¯çå æ¯å
¬å¼å IFERROR å
åº"ï¼â¡ `ä¸å£å¾åæ` 9 æ**ç½çº¦è½¦è¡**å¼ 0/忝 â1ï¼9 æç½çº¦è½¦æ°æ®æªå¯¼ï¼å±ç¼ºæ°ï¼ï¼â¢ `ä¸å£å¾æç»` D107/D108/F107/F108 为硬ç¼ç `0` å
¬å¼ï¼æ¯çæ¢æï¼ï¼â£ **ç¨æ·æåºç"æ ¼å¼è°æ´"å°æªç»åºå
·ä½ä½ç½®**ï¼å¾
æä¾å忥æ¯çã |
| | | - **æ¥åï¼æ¬å°ãä¸å
¥åºï¼**ï¼`docs/åè½æµè¯æ¥å/åè½æµè¯æ¥å_åå¸åå¸å·å½ææ é¢è¡å·ä¿®æ£_2026-10-12.md`ãç¶æï¼**ãå¾
éªæ¶ã**ã |
| | | ### 10-12ï¼7ï¼ï¼ç¨æ·å³å®ââ以ä¸ä¸é¡¹**æä¸å¤ç**ï¼è®°å½ï¼å¿åä½ä¸ºå¾
åæåºï¼ |
| | | |
| | | - **æç¡®å³å®ï¼2026-10-12ï¼**ï¼ä»¥ä¸ä¸é¡¹ç¨æ·å³å®**䏿¹**ï¼åç»ä¼è¯ä¸è¦å彿å¾
åé夿åºï¼è¥ç¨æ·ä»¥åä¸»å¨æèµ·åè®®ã |
| | | 1. `åå¸åå¸å·` S50:S66 ä¸ `éè·¯è¿è¾å¨è½¬é` I27:I43 ç 34 个 `#DIV/0!`ï¼å æ¯å
¬å¼åæ¯=å
¨çè¡å¨æ æ°æ®æè¢«åæ 0ï¼ââä¸å"é误å¼çç½/IFERROR å
åº"ã |
| | | 2. `ä¸å£å¾åæ` 9 æç½çº¦è½¦è¡ï¼å¼ 0ã忝 â1ï¼å 9 æç½çº¦è½¦æ°æ®æªå¯¼ï¼ââä¸é¢å¤å¤çï¼çæ°æ®å°ä½èªç¶æ¢å¤ã |
| | | 3. è¡¨æ ¼æ ¼å¼è°æ´åæ¥æ¯çââç¨æ·ä¸åæä¾å
·ä½æ¹å¨ï¼æ¯çä¸åæ ¼å¼åæ¥ã |
| | | - **æ¬æ¥å·²å®æå¹¶å¾
éªæ¶çæ¹å¨ï¼æ±æ»ï¼**ï¼Step 0 è¡¨å¤´å¨æå®ä½ï¼`6d1ac61`ï¼ãè´§è¿ä½æ£ 6 æ¡è§åï¼`91f6583`ï¼ãæ©åä»¥å½æäº§ç©ä¸ºåºï¼`fb269f6`ï¼ãæ©åå
¬å¼å
éä¿®æ£ + 9 ææ¯çè½çï¼`3b3652e`ï¼ãå
¬äº¤/åºç§å¯¼å
¥æè¡¨å¤´åå®ä½ï¼`797f03b`ï¼ãåå¸åå¸å·å½ææ é¢è¡å·ä¿®æ£ï¼`64f6466`ï¼ã |
| | | ### 10-12ï¼8ï¼ï¼æç¨æ·è¦æ±æ¶æä¸ºãä¸ä¸ªæ¯çã+ æ¯çæ ¼å¼æç¨æ·è°æ´çãå¾
éªæ¶ã |
| | | |
| | | - **ç¨æ·è¦æ±ï¼2026-10-12ï¼**ï¼æ ¼å¼æç¨æ·æå·¥è°æ´è¿çãçæ_éè·¯è¿è¾éæ±æ»è¡¨_2026-09 (3).xlsxãæ¥ï¼æ¯çåªè¦ä¸ä¸ªã |
| | | - **已宿**ï¼ |
| | | 1. **æ¯çæ¶æä¸ºä¸æ¬**ï¼`docs/çææ±æ»å¤§è¡¨/` ä¸åªç `2026å¹´9æéè·¯è¿è¾éæ±æ»è¡¨.xlsx`ï¼æ 7 ææ¯çã8 ææ¯çã8æçå¼ååã9æè¯åä»¶ã**çå¼åç
§ä»¶ãçæ_éè·¯è¿è¾éæ±æ»è¡¨.xlsxã** å
± 5 ä»¶ç§»å
¥ `docs/çææ±æ»å¤§è¡¨/彿¡£/`ï¼`_å¤ä»½_`/`_bak_` æ A3 ä»çåå°ä½åå½å¯¹ç
§ï¼ä¸ç®æ¯çï¼ã |
| | | 2. **åç¨åç
§ä»¶**ï¼`exportSummaryWorkbook` ä¸åè°ç¨ `applySummaryReferenceLayout`ï¼`widthApplied=0`ï¼ï¼**çå¼ä¸å¾ä»¥å½ææ¯ç为å**ï¼æ¥å¿ä¸ååºç°"å¥ç¨åç
§ä»¶å宽"ï¼ã |
| | | 3. **ç¨æ·æ ·å¼å¥å
¥æ¯ç**ï¼ç¨ Excel COM 精确å¥ç¨ï¼109 å宽 + 3 è¡é« + 1348 æ ¼åå· + 32 æ ¼åå
æ ¼æ ¼å¼ï¼ï¼å¹¶å»ææ¯ç `fullCalcOnLoad` 以å
Excel æå¼å³éç®ã |
| | | 4. **éªè¯**ï¼çæ 2026-09 â **产ç©ä¸æ¯ççå宽/è¡é«/åå·å®å
¨ä¸è´ï¼å·®å¼ 0ï¼** âï¼ä¸ä¸ä¸çäº§ç©æ¯ **ç»æå·®å¼ 0** âã |
| | | - **䏤夿®çï¼åä¸å¯è§ï¼å·²æ ¸å®ï¼**ï¼â å宽 226 å¤ **±0.01**ï¼Excel æ 1/256 å符å宽度ï¼âï¼â¡ 32 个**ç©ºç½æ ¼/åå¹¶åºè¦çæ ¼**çå¡«å
ä¸åä½è²ï¼Excel ä¿åå¯¹ç©ºæ ¼æ ¼å¼çå¤çå·®å¼ï¼âã |
| | | - **ä¸å¤éç¨æ·ææ¿çæ°å¼åå**ï¼æ¬æ¬¡çæä»¶ `ä¸å£å¾æå` æ **46 æ ¼**ï¼åæ¯/æåï¼å¦ `D3` 0.0581â**0.0402**ã`E4` 第10â第**9**ï¼ä¸ä¸ä¸çä¸åï¼**æ°å¼ä¸ãä¸å£å¾åæã页éä½ä¸è´**ï¼`B3=35028.0636`ã`C3=0.04015856`ï¼ââ峿å页䏿ç»/åæé¡µå£å¾ç»ä¸ï¼åæ§å¼æ¯è¯¥é¡µå¨æ¯çéçæ§ç¼åï¼ã**ä¿çæ°å¼ / åéæ§å¼å¾
ç¨æ·ç¡®è®¤**ã |
| | | - **彿¡£è¯´æ**ï¼`彿¡£/` å
5 ä»¶å岿¯ç/çå¼ä»¶ä»å¯éæ¶ååï¼æªå é¤ï¼ï¼æ¯çç®å½ä»¥ååªæ¾"彿䏿¬"ã |
| | | - **ç¶æ**ï¼**ãå¾
éªæ¶ã**ã |
| | | ### 10-12ï¼9ï¼ï¼æ©åå¨å ãæ´æ¬éç®åç¼åã+ æç¡®ä¿çä¸å£å¾æåæ°å¼ãå¾
éªæ¶ã |
| | | |
| | | - **é®é¢å®ä½ï¼ç¨æ·é®"为ä»ä¹äº§çè¿ 46 æ ¼å·®å¼"ï¼**ï¼æ¯çé**大éå
¬å¼åªæå
¬å¼ã没æç¼åå¼**ï¼POI åå
¬å¼ä¸å¸¦ç¼åï¼ãå®è¯ï¼æ¯çç» Excel ä¿ååå **约 2.47 䏿 ¼**ç±"空"å"æå¼"ï¼å¦ ` è´§è¿!U5: 空â130311.8927`ï¼ã`ä¸å£å¾æå` ç忝/æåç±ç¨åº**读æ¯çç¼å**计ç®ï¼æ¹æ¡ Aï¼â ç¼å空æ¶ç®æ§å¼ `0.0581`ï¼ç¼åè¡¥é½åç® `0.0402`ï¼**ä¸ãä¸å£å¾åæãéä½ä¸è´**ï¼ã**æ°å¼ææ¯æ°æ®å®æ´æ¶çæ£ç¡®ç»æ**ã |
| | | - **æ¹å¨**ï¼`SummaryLayoutExpander` æ°å¢ `recalcAndCache(wb)`ï¼`clearAllCachedResultValues()` + `evaluateAll()` ååç¼åï¼å¤±è´¥ä»
WARNï¼ï¼å¨ `expand()` ä¸ `fixTitles()` ä¸è°ç¨ã |
| | | - **éªè¯**ï¼æ©å 2026-08 â å
¬å¼æ ¼ **39,961** 个ï¼**空ç¼åä»
4 æ ¼**ï¼å
¨å¨ ` è´§è¿`ï¼POI æ æ³æ±è§£çå¤é¨å¼ç¨ï¼Excel æå¼ä¼ç®ï¼ï¼ä¿®å¤ååç±»æ¯ç约 2.47 䏿 ¼æ ç¼åã |
| | | - **ç¨æ·å³å®**ï¼`ä¸å£å¾æå` é£ 46 æ ¼**ä¿çæ°å¼**ï¼ä¸ä¸å£å¾åæä¸è´ï¼å£å¾ç»ä¸ï¼ã |
| | | - **æ¥åï¼æ¬å°ãä¸å
¥åºï¼**ï¼`docs/åè½æµè¯æ¥å/åè½æµè¯æ¥å_æ©åæ´æ¬éç®åç¼å_2026-10-12.md`ãç¶æï¼**ãå¾
éªæ¶ã**ã |
| | | ### 10-12ï¼10ï¼ï¼9 æå¯¼å
¥æ ¸éª + åºè¡¨æ½æ¥éè¿ï¼9 æä»ç¼ºæ°æ®æ¸
åãå¾
éªæ¶ã |
| | | |
| | | - **9 æå¯¼å
¥æ ¸éªï¼åªè¯»æ¥åºï¼**ï¼åå¸å
¬äº¤ **141 è¡**ã巡游åºç§ **17 è¡**ãå
¬è·¯æ
客 H2031 **767 è¡**ãè´§è¿ä¼ä¸ H2032 **636 è¡** â å
¨é¨æåï¼**å
¬äº¤/åºç§ç"æè¡¨å¤´åå®ä½"æ¹å¨å®æµææ** âï¼`import_batch` 551/552 å¯è§ï¼ãä»ç¼º 9 æï¼**â¡ãè´§è¿éå¨è½¬é表ãï¼å
¨å£å¾ï¼0 è¡ãâ¢è§ä¸è§ä¸æå表 0 è¡ãç½çº¦è½¦è®¢å/å
¨ç pin 0/0ã个ä½å®¢è¿ 0 è¡**ï¼ç¨æ· 2026-10-12 说æï¼å
¶ä»æ°æ®ææ¶è¿æ²¡æï¼ã |
| | | - **åºè¡¨æ½æ¥ï¼äº§ç©è½ `docs/çææ±æ»å¤§è¡¨/è¾åº/çæ_éè·¯è¿è¾éæ±æ»è¡¨_2026-09.xlsx`ï¼**ï¼å
¬äº¤ 9 æå®¢è¿é **13476.35**ãåå¸å
**12021.00**ãåºç§å®¢è¿é **5626.81**ãåå¸å
**5139.61** ââ ä¸åºå
Σ17 å¸å·**éä½ä¸è´** âï¼ç´¯è®¡å·²å« 9 æï¼å
¬äº¤ç´¯è®¡ **124252.69** = 1-8æ 110776.34 + 9æ 13476.35ï¼âãå â¡æªå¯¼ï¼9 æã è´§è¿ã页"å
¨å£å¾"æçäºè§ä¸ï¼3575.1211ï¼ã"è§ä¸"为 0 ââ å±ç¼ºæ°ï¼éç®éã |
| | | - **å°å¾
å**ï¼å
¬äº¤ 9 æ **141 è¡** vs 1-8 æåæ **142 è¡**ï¼å° 1 è¡ï¼ï¼å¾
æ ¸æ¯æºæä»¶å°è¡è¿æ¯å¯¼å
¥è·³è¿ï¼å¯è½æ¯ç©ºè¡/æ±æ»è¡ï¼ââç¨æ·è¯´å
è¿æ ·ï¼çå¾
以åã |
| | | - **æ¬æºéå¶**ï¼å©ææ²ç®±ä¸è½å `D:\ä¸è½½`ï¼çæä»¶åªè½è½å¨ä»åºå
ï¼`docs/çææ±æ»å¤§è¡¨/è¾åº/`ï¼ï¼æ£å¼ä¸è½½è¯·å¨é¡µé¢ç¹çæ/ä¸è½½ã |
| | | - **ç¶æ**ï¼**ãå¾
éªæ¶ã**ã |
| | | ## ææ°ï¼2026-10-12ï¼zyj çº¿ï¼ |
| | | |
| | | ### 10-12ï¼æ©åå¨è¡¥ãæ 颿æ¯çææ¬¡éåã+ ä¿®æ£ 8 ææ¯çæ é¢ãå¾
éªæ¶ã |
| | | |
| | | - **ç¨æ·åé¦**ï¼9 æçå¼ä»¶ä¸ 8 ææ¯çéï¼7 个页ç¾çæ é¢ä»æ¯ `1-7æ / 7æ`ï¼è´§è¿éæåãè´§è¿å¨è½¬éæåãå叿±æ»ãåå¸åå¸å·ãä¸å£å¾æåãä¸å£å¾åæãéè·¯è¿è¾å¨è½¬éï¼ã |
| | | - **æ ¹å ï¼ä¸¤æ¡ï¼**ï¼â æå·²ç¡®è®¤å£å¾è¿ 7 é¡µæ¯ C 类页ââ**æ é¢ç±çæç¨åºå¨åºè¡¨æ¶ç°å**ï¼`ReportExportService :969 / :1011 / :2707 / :2891 / :4446 / :4498 / :4499 / :4604` åæ period åï¼ï¼æ¯çéçæåä¸åä¸åºè¡¨ï¼æä»¥æ©åå¨åæ¬æ²¡å¨å®ä»¬ï¼â¡ **æçå®ç°ç¡®å®æ¼äº"æ é¢ +1"è¿ä¸æ¥**ââãæ©åé
ç½®Â·æ ¸å¯¹çãå¨ä½ 7 åäº"æ é¢ï¼é¡µé¢æ 颿份+1ï¼éé¡µæ ¸å¯¹ï¼"ï¼æåªåäº 8 个å°è´¦é¡µç表头å
éã |
| | | - **ä¿®å¤**ï¼æ©å卿°å¢ `applyTitles(...)`ââæ**æ¯çææ¬¡**éåæ é¢ï¼ç´¯è®¡=`1-Mæ`ã彿=`Mæ`ï¼ï¼åªå¤ç 7 个æ é¢é¡µã**å½ä¸æ 颿 è®°ææ¹**ï¼é¿å
误伤 ` è´§è¿!A1` è¿ç±»å¤æ³¨ï¼ï¼4 ä½å¹´ä»½ä¸å¹¶æç®æ å¹´éåï¼æ°å¢åªè¯»æ¥å£åæ° `?titlesOnly=true`ï¼åªæ¹æ é¢ä¸æ©åï¼ã |
| | | - **8 ææ¯ç已修æ£**ï¼7 页æ é¢ `1-7æ/7æ` â **`1-8æ/8æ`**ï¼ä¿®æ£åå¤ä»½ `docs/çææ±æ»å¤§è¡¨/_bak_2026å¹´8ææ¯ç_æ é¢ä¿®æ£å_20261012.xlsx`ï¼ï¼19 页ç¾ä¸å
¬å¼æ½æ¥æ æï¼`ä¸å£å¾æç»!C5`ã`å
¬äº¤!C5` å
¨çè¡å
¬å¼ã` è´§è¿!A1` 夿³¨åæ£å¸¸ï¼ã |
| | | - **9 æçå¼éè·**ï¼7 页æ é¢ â **`1-9æ/9æ`**ï¼äº§åº `docs/çææ±æ»å¤§è¡¨/è¾åº/_æ©å_2026å¹´9æ_å¾
确认_v2.xlsx`ï¼1,037,071 Bï¼ã |
| | | - **䏿工 9 ææ¯ç夿¯**ï¼851 æ¡å·®å¼ = 8 个å°è´¦é¡µ 836 æ¡ï¼æ²¿ç¨ 10-10 åç±»ï¼ç´¯è®¡/忝æ¯å¦æ©å° 9 æ 548ãåé符 153ã表åå¼å· 130ãæµ®ç¹å°¾å·® 1ï¼ï¼ **æ é¢é¡µ 15 æ¡**ï¼æå·¥ä»¶æ é¢ä¸ºæ»åç `1-8æ/7æ`ï¼ã |
| | | - **踩å**ï¼éè·æ¶ç®æ æä»¶è¢« Excel/WPS å ç¨ï¼`being used by another process`ï¼â å
è½ `_v2` å½åï¼ç¨æ·å
³éåå¯ç¨ååéåã |
| | | - **æ¥åï¼æ¬å°ãä¸å
¥åºï¼**ï¼`docs/åè½æµè¯æ¥å/åè½æµè¯æ¥å_æ±æ»å¤§è¡¨æ©å卿 é¢ä¿®æ£_2026-10-12.md`ï¼æ¥ç» `docs/工使¥ç»/工使¥ç»_2026-10-12.md`ã |
| | | - **å¾
ç¨æ·éªæ¶/确认**ï¼â æ é¢å£å¾æ"**æ é¢ææ¬¡=æ¯çææ¬¡**"ï¼8æâ1-8æ/8æã9æâ1-9æ/9æï¼æ¯å¦æ£ç¡®ï¼â¡ `_æ©å_2026å¹´9æ_å¾
确认_v2.xlsx` æ¯å¦æ¹å为æ£å¼åï¼ååé£ä»½æ¯æ§å
容ã被 WPS åè¿ï¼æªç»åæä¸å¨ï¼ï¼â¢ 累计/ç´¯è®¡åæ¯"æ¯å¦æ©å° 9 æ"ï¼äº§åº=å·²æ©ãæå·¥çæªæ©ï¼ã |
| | | - **ç¶æ**ï¼**ãå¾
éªæ¶ã**ã |
| | | ### 10-12ï¼2ï¼ï¼è´§è¿å£å¾ã导åºååºæ°ä½æ£ãï¼B4ï¼6 æ¡è§åè½å°ãå¾
éªæ¶ã |
| | | |
| | | - **èæ¯**ï¼è§ä¸ = â¡ãè´§è¿éå¨è½¬é表ãå
¨å£å¾ â â H203-2 è§ä¸Ã·10000ï¼æ¯**åå**âââ æ¼æ¥ä¼è®©è§ä¸èé«ãæç«¯è¿ä¼ç®åºè´æ°ãç¨æ· 2026-10-12 åææè¿ç»è§åå è¿ B4ã |
| | | - **å®ç°**ï¼æ°å¢ `reportexport/calc/FreightReadinessCheck.java`ï¼åªè¯»ï¼ï¼æ¥å
¥ `exportSummaryWorkbook` çç¼ºæ°æ¶é `dbProblems`ï¼éå¯¼åºæ¥å¿è¾åºï¼ï¼æ°å¢åªè¯»æ¥å£ `GET /api/report/summaryWorkbook/freightCheck?period=YYYY-MM`ã |
| | | - **è§å**ï¼â æå¸å·è§ä¸ä¸ºè´ï¼â¡ â¡æå
¨å£å¾ãâ æ è§ä¸è®°å½ï¼çæ¼æ¥âè§ä¸=å
¨å£å¾ï¼ï¼â¢ â¡å
¨å£å¾ < â è§ä¸ï¼â£ Σ17 å¸å· â â¡"æ¹åç"è¡ï¼å®¹å·® 0.01ï¼ï¼â¤ **æ¬æä¸ä¸æå®å
¨ç¸å**ï¼çéå¤å¯¼å
¥ï¼ä¸¤ä¾§ â¥50 ä¸å¨/ä¸å¨å
¬éï¼ï¼â¥ **忝å¼å¸¸**ï¼|å»å¹´åæâæ¬æ| > 50%ï¼ä¸¤ä¾§ â¥50ï¼ï¼â¥' 累计å¾ç¨½ï¼â å¨è½¬é 1..M æ±å ÷10000 vs â¢æå表 CUMULATIVEï¼ã |
| | | - **éå¼ä¿®æ£ï¼å®æµé©±å¨ï¼éè¦ï¼**ï¼é¦çç¨"è§ä¸ç¯æ¯ > 30%" â 2026-02 å½ä¸ 22 æ¡ã2026-03 å½ä¸ 32 æ¡ï¼å¤æ ¸ç¡®è®¤**å
¨æ¯æ¥èå£èæ§**ï¼åå¸å· 2 æ â30~â40%ã3 æ +30~+60%ï¼ï¼éæ°æ®é误 â æ¹ä¸º"éå¤å¯¼å
¥ç¹å¾ + 忝>50%"ï¼å¹¶ç»å¤æ®å **ç»å¯¹é¨æ§ 50 ä¸å¨/ä¸å¨å
¬é**ï¼é¿å
ç¥åæ¶æåºï¼20~46 ä¸å¨ï¼è¿ç±»å°åºæ°åªå£°é¿ææ¥è¦ã |
| | | - **宿µ**ï¼2026-01~08 **å
¨æ 0 å½ä¸**ï¼æ 误æ¥ï¼ï¼â¡è¡¨ `last_freight_m08`/`last_turnover_m08` **18/18 è¡æå¼** â 忝è§åç¡®å®åä¸è®¡ç®ï¼é空è·ï¼ã |
| | | - **æ¥åï¼æ¬å°ãä¸å
¥åºï¼**ï¼`docs/åè½æµè¯æ¥å/åè½æµè¯æ¥å_è´§è¿åºæ°ä½æ£B4å
æ¡è§å_2026-10-12.md`ï¼æ¥ç»åæ¥ä¸¤æ¡ã |
| | | - **å¾
ç¨æ·éªæ¶/确认**ï¼â éå¼ï¼ç»å¯¹é¨æ§ 50ã忝 50%ï¼æ¯å¦åéï¼â¡ æ¯å¦ç°å¨å B4 å©ä½é¨åââ导åºåå¼¹çª + çæä»¶ãæ°æ®ç¼ºå¤±æ¸
åã页ï¼P0-3 å©ä½ï¼ã |
| | | - **ç¶æ**ï¼**ãå¾
éªæ¶ã**ãåªè¯»åè½ï¼æªæ¹å¨ä¸å¡æ°æ®ã |
| | | ### 10-12ï¼3ï¼ï¼çææ°æ®ãéªè¯æ¹æ³ãææï¼äºæ®µé¾æ¡ + ååæºç²åºï¼ |
| | | |
| | | - **ææ**ï¼`docs/æ±æ»å¤§è¡¨çææ°æ®éªè¯æ¹æ³_2026-10-12.md`ï¼åç"å¦ä½éªè¯ç¨åºçæçæ°æ®æ¯å¦æ£ç¡®"ï¼ã |
| | | - **å£å¾**ï¼æé®é¢ææ 4 é®ï¼æºæ°æ® / ç®æ³ / çå¼ / ä¸å®ç¨¿ä¸è´ï¼ï¼æ¯æ®µé
ä¸ä¸ª**ç¬ç«äºè¯¥æ®µçåç
§**ï¼é¾æ¡äºæ®µ L0 æºæä»¶âåºãL1 åºâå£å¾å¼ãL2 å£å¾å¼âåå
æ ¼ãL3 åå
æ ¼âæ´æ¬ãL4 跨页åæé¡µå¾ç¨½ï¼**éè¿æ å L0~L4 已忻**ï¼ä»»ä¸å±æªè¦çï¼ç»è®ºä¸å¾å"å·²éªè¯"ï¼ã |
| | | - **å¯¹æ¢æ"åå±éªè¯æ³"ï¼10-08/10-12 ä¼è¯ï¼çä¸å¤è¡¥å¼º**ï¼â è¡¥ L0 æºæ°æ®å±ä¸**ååæºç²åº**ââ"çæä»¶ vs å®ç¨¿"卿¯çä¸å®ç¨¿åæºæ¶æ¥ä¸åºæºæ°æ®é®é¢ï¼ä»æ¥ 2025-11 两å¤å³ä¾ï¼ï¼â¡ **使£éè¿ â æ°æ®é½å
¨**ï¼å¼å¸¸æ¸
åä¸ç¼ºæ°æ¸
ååå¼è¾åºï¼ä»æ¥ 2026-08 使£ 0 å½ä¸ï¼ä½ 9 æä»¶ 8 æåä»ç©ºï¼ï¼â¢ å¿
é¡»ä¸é¨å䏿¬¡ãåºå
å¼ vs æ¯çç¼åãå
¨é diffï¼10-09 å¾
åï¼ââ**åºè¡¨å£å¾ä¸ååº**ä¸**éªè¯å¿
é¡»æ¯ä¸æ¬¡**å¹¶ä¸çç¾ã |
| | | - **ä½è¯**ï¼2026-08 vs 9/13 å®ç¨¿ï¼1020 æ¡ â 11 æ¡å
³æ³¨ï¼ç®æ³å±éè¿ï¼ï¼åºå
2025 vs æ¯ç 2025ï¼å
¬äº¤/åºç§/轨é轮渡 100% ä¸è´ï¼3 å¤éä¸å¡ç¡®è®¤ï¼æ¥åè§ `docs/åè½æµè¯æ¥å/åè½æµè¯æ¥å_åºå
2025䏿¯ç2025éæ ¼æ ¸å¯¹_2026-10-12.md`ï¼ã |
| | | - **ç¶æ**ï¼ææ¡£ç±»ï¼æ ééªæ¶ã |
| | | - **B4 å
³èï¼å·²æ ¸ï¼**ï¼10-12 ä¸åçè´§è¿åºæ°ä½æ£ B4 å·²ç±å¦ä¸ä¼è¯æäº¤ï¼`91f6583`ï¼æ¥å `docs/åè½æµè¯æ¥å/åè½æµè¯æ¥å_è´§è¿åºæ°ä½æ£B4å
æ¡è§å_2026-10-12.md`ï¼ï¼æ¬æ¬¡ 2026-08 æ ¸å¯¹å®é
è°ç¨äºå®ç `GET /api/report/summaryWorkbook/freightCheck`ï¼2026-01~08 å
¨æ 0 å½ä¸ï¼ã |
| | | ### 10-12ï¼4ï¼ï¼æ±æ»å¤§è¡¨ãç¼ºæ°æ¸
åãè½å°ï¼åªè¯»æ¥å£ + æ¥å
¥å¯¼åºæ¥å¿ï¼ãå¾
éªæ¶ã |
| | | |
| | | - **å¨å **ï¼ç¨æ·è¦æ±æã缺æ°ãæ¾å¼æå°å°é¢ä¸ââ彿¥ä¸¤ä¸ªçå®é®é¢ï¼9 æçæä»¶ 8 æå为空ã9 æå¨è½¬éå
¨ 0ï¼é½å±"该æçæ°æ²¡æ"ï¼è `freightCheck` 使£å½æ¶ **0 å½ä¸**ï¼ä¸¤è
å¿
é¡»åå¼ã |
| | | - **å®ç°**ï¼æ°å¢ `reportexport/calc/SummaryMissingDataCheck.java`ï¼åªè¯»ï¼ï¼æ¥å£ `GET /api/report/summaryWorkbook/missing?period=YYYY-MM`ï¼æ¥å
¥ `exportSummaryWorkbook` ç `dbProblems`ï¼éå¯¼åºæ¥å¿è¾åºï¼ã |
| | | - **å£å¾**ï¼â éæå填页ï¼ç线å
车/å
¬äº¤/åºç§/轨éè½®æ¸¡ï¼æ¥ 1..ç®æ æ æ¥æºæ¯å¦ææ°ã17 å¸å·æ¯å¦é½å
¨ï¼â¡ è´§è¿/ç½çº¦è½¦åªåå¡«ç®æ æï¼æ¥ç®æ æåé¡¹æ¥æºï¼è´§è¿â +â¡+â¢ãç½çº¦è½¦è®¢å+å
¨ç pin+åºç§å¹³åè¿è·ï¼ï¼â¢ è¿ä¸¤é¡µçåå²æç±æ¯çæ¿æ
ââ**æ¯çä¸åæå¤ä»½æ¯çï¼donorï¼é½æ²¡ææ°**æç®ç¼ºå£ï¼å¦åä¼è¯¯æ¥ 7 æé£ç§"éª¨æ¶æ¯ç + donor è¿å"ï¼ã |
| | | - **宿µ**ï¼ç¬ç« 8099 å®ä¾ï¼æªææ° 8090ï¼æµå®å·²å
³ï¼`mvn -o compile` exit=0ï¼ï¼2026-08 **0 æ¡**ã2026-07 **0 æ¡**ã2026-09 **11 æ¡**ï¼ç线å
车/å
¬äº¤/轨é轮渡/åºç§ æ æº 4 æ¡ + è´§è¿â¡â¢ 2 æ¡ + ç½çº¦è½¦ 3 æ¡ + æ¯ç 8 æå空 2 æ¡ï¼ââå½ä¸çæ£æ¯å½æ¥ä¸¤ä¸ªçå®é®é¢ã |
| | | - **ä¿®æ£**ï¼é¦çåªæ¯æ¯ç â è¯¯æ¥ 2026-07 两æ¡ï¼å·²æçæé¾å®ç°è¡¥ä¸ donor æ¯å¯¹ï¼å¤æµ 0 æ¡ã |
| | | - **æ¥åï¼æ¬å°ãä¸å
¥åºï¼**ï¼`docs/åè½æµè¯æ¥å/åè½æµè¯æ¥å_æ±æ»å¤§è¡¨ç¼ºæ°æ¸
å_2026-10-12.md`ã |
| | | - **å¾
å**ï¼å¯¼åºåå¼¹çª + çæä»¶ãæ°æ®ç¼ºå¤±æ¸
åã页ï¼ä¸ B4 ç P0-3 åå¹¶ï¼ã |
| | | - **ç¶æ**ï¼**ãå¾
éªæ¶ã**ï¼åªè¯»åè½ï¼æªè½çãæªæ¹ä¸å¡æ°æ®ï¼ã |
| | | ## ææ°ï¼2026-10-10ï¼zyj çº¿ï¼ |
| | | |
| | | ### 10-10ï¼Step 1 æ©åå¨è½å° + 8â9 æçå¼é¦è½®äº§åºãå¾
éªæ¶ã |
| | |
| | | - **å¡ç¹æªå**ï¼`resolveSummaryMother`ï¼`ReportExportService.java:4576`ï¼åªç²¾ç¡®å¹é
ã{year}å¹´{month}æéè·¯è¿è¾éæ±æ»è¡¨.xlsxãï¼ç®æ ææ æ¯ç峿¥éï¼9 ææ¯çä»ä¸ºç¨æ· 09-29 æå·¥æ©å产ç©ï¼æ¬æºæªå
¥åºï¼ã |
| | | - **å¾
确认**ï¼â æ©åèæ¬ç¨ 9 ææå·¥çè¿æ¯ 8 æçï¼â¡ æ¯å¦ä¸æ¬¡å»ºæ 12 ææ»¡çå¼ï¼æ¹æ¡ v1 建议满çå¼ï¼ã |
| | | - **ç¶æ**ï¼æ¬æ®µä¸ M0 è§æ ¼å为ãå¾
éªæ¶ãï¼æ¬è½®æªæ¹å¨ä»»ä½ä»£ç ãå·¥ä½åºæªè·è¸ªç `docs/çææ±æ»å¤§è¡¨/2026å¹´9æéè·¯è¿è¾éæ±æ»è¡¨*.xlsx` ä¸ `docs/确认.docx` æçº¦å®ä¿çã |
| | | ## ææ°ï¼2026-10-08 é路客è¿éèµ°å¿å¾10æ3æ¥æ ç¾é¿è®©ï¼ |
| | | |
| | | ### 10-08ï¼é路客è¿éèµ°å¿å¾ 10æ3æ¥ `72.4` æ ç¾ä¸ç§»ï¼é¿å
被 `70.6` 鮿¡ |
| | | |
| | | - **éæ±ï¼ç¨æ·ï¼**ï¼èµ°å¿å¾å
¶ä½é¨åä¸å¾ä¸å¨ï¼åªæ `âåä¸âåæé路客è¿éæ
åµ` å¾ä¸ 10æ3æ¥ `72.4` çæ ç¾ä»ç¹çå³è¾¹ç§»å°ç¹æ£ä¸æ¹ï¼è¯¥ä½ç½®åæ¬ç©ºç½ï¼æ¾å³è¾¹ä¼è¢« 2024 å¹´ç»¿è² `70.6` æ ç¾åä½ä¸é¨å导è´çä¸å
¨ã |
| | | - **æ°æ®èæ¯ï¼2026 åä¸é路客è¿éï¼ä¸äººæ¬¡ï¼**ï¼2026 å¹´ 10æ3æ¥ = 72.4ï¼2025 年忥 = 72.3ï¼2024 年忥 = 70.6ã䏿¡çº¿åæ¥æ°å¼é常æ¥è¿ï¼èªå¨å¸å±æ `72.4` æ¾å°äºç¹å³ä¾§ã |
| | | - **æ ¹å **ï¼æ ç¾èªå¨å¸å±ï¼`HolidayTrendChartService.drawXYItemLabels` 卿è§åï¼åªå¯¹âææ°å¼æåºåç¸é»ç两个æ ç¾âåéå æ©ç½ï¼`72.4` ä¸ `70.6` ä¹é´éç `72.3`ï¼äºè
ä¸ç¸é»ï¼éå æªè¢«æ©ç½ï¼å æ¤å
许äºç¸è¦çã |
| | | - **æ¹å¨**ï¼`HolidayTrendChartService.lineChart` æ°å¢å¯é `List<LineLabelOverride> labelOverrides` å
¥åï¼é
å `LineLabelAnchor{ABOVE,BELOW,LEFT,RIGHT}`ï¼ï¼å¯ææå® `(seriesIndex,itemIndex)` çæ ç¾åºå®å°æä¸ªæ¹ä½ï¼ä¸ä¼ åæ¶å®å
¨èµ°åèªå¨å¸å±ï¼æ¸²æéåèä¸åã`HolidayTrendService` ä»
对éè·¯å¾ä¼ 䏿¡ overrideï¼`seriesIndex=0`ï¼2026 å¹´ï¼ã`itemIndex=2`ï¼10æ3æ¥ï¼ã`ABOVE`ã |
| | | - **éªè¯**ï¼`mvn -o test` 17/17 éè¿ï¼æ°å¢æ ç¾éç¹åæµï¼ç©º/ä¸å¹é
override 䏿¹å渲æãABOVE é«äº BELOWï¼ï¼çå®åºç«¯å°ç«¯éæ°çæèµ°å¿å¾ï¼15 å¼ å¾ä¸ä»
`image5.png`ï¼éè·¯å¾ï¼SHA-256 ååï¼å
¶ä½ 14 å¼ éåèä¸è´ï¼éè·¯å¾åååç´ å·®å
å´ç `x 313-436, y 395-438`ï¼æ£æ¯è¯¥æ ç¾åºåï¼æ¬å° OCR 确认 `72.4` 宿´å¯è¯»ãä½äºç¹ï¼yâ427ï¼ä¸æ¹ï¼ä¸åä¸ `70.6`ï¼y431-450ï¼éå ã |
| | | - **è¾¹ç**ï¼åªæ¹äºéè·¯è¿ä¸å¼ å¾çè¿ä¸å¤æ ç¾ï¼å¿«æ¥ãæ°æ®å¯¼å
¥ãå
¶å®ç»è®¡é»è¾åæªè§¦ç¢°ï¼æªå¯¼å
¥/è¦ç/é导任ä½ä¸å¡æ°æ®ã |
| | | - **éå¯**ï¼ä¸ºè®©æé®ä¸è½½ç¨ä¸æ°ä»£ç ï¼å·²ç¨ `start-dev.ps1` éå¯å端ï¼å端æªå¨ï¼ï¼å¯ç´æ¥å¨ç³»ç»ééæ°ä¸è½½èµ°å¿å¾éªæ¶ã |
| | | - **å¾
å/é£é©**ï¼è¿æ¡ override ç»å® `seriesIndex=0`ï¼2026 å¹´ï¼ä¸ `itemIndex=2`ï¼10æ3æ¥ï¼ï¼è¥åç»éè·¯å¾ç³»åé¡ºåºæå¤©æ°å£å¾è°æ´ï¼éè¦åæ¥æ ¸å¯¹ãå
¶ä½å¹´ä»½/æ¥æå¦ååºç°æ ç¾é®æ¡ï¼å¯å¤ç¨è¯¥ override æºå¶ï¼ä½ééå¾äººå·¥ç¡®è®¤ååå ã |
| | | - **éªæ¶**ï¼ç¨æ· 2026-10-08 å·²çå¾ç¡®è®¤ææå¯ä»¥ã |
| | | |
| | | ## åå²ï¼2026-10-08 èµ°å¿å¾éå½äºè§æµç«å¾å¤©æ°ä¿®æ£ï¼ |
| | | |
| | | |
| | | |
| | | ### 10-08ï¼è§æµç«é度/æ¥æ¤åº¦èµ°å¿å¾æ¹ç¨ç³»ç»é
ç½®åæå¤©æ°ï¼å·²éªæ¶ï¼commit e958585ï¼ |
| | | |
| | | - **éæ±ï¼ç¨æ·ï¼**ï¼èµ°å¿å¾æåä¸¤å¼ å¾âæ®éå½çéè§æµç«å¹³åæºå¨è½¦é度âââ¦å¹³åæ¥æ¤åº¦âåªå«ä»å¹´æ°æ®ï¼æ¨ªè½´å¤©æ°ä¸åºæ¯åºå® 8 天ï¼èåºä¸ºç³»ç»é
ç½®çåæå¤©æ°ã |
| | | - **æ¹å¨**ï¼`HolidayTrendChartService.lineChart` æ°å¢å¯ä¼ `dayLabels` çéè½½ï¼é»è®¤ä»ä¸º `DAY_LABELS` 8 天ï¼å
¶ä½ 13 å¼ å¾é¶æ¹å¨ï¼ï¼`HolidayTrendService.observationData` æ¹ä¸ºæ `configService.findHolidayDays(year, holidayType)`ï¼`day_index>=1`ï¼ç天æ°ä¸æ¥æçææ°æ®ç¹å横轴æ ç¾ï¼æåä¸¤å¼ å¾ä¼ å
¥è¯¥æ ç¾ã |
| | | - **é
ç½®ä¾æ®**ï¼`holiday_calendar` 2026 åä¸ `day_index 1..7`ï¼7 天ï¼`day_index 0` = 09-30 ç¯æ¯åºæï¼ï¼2025 = 8 天ã2024 = 7 天ã |
| | | - **éªè¯**ï¼`mvn -o test` 16/16 éè¿ï¼æ°å¢èªå®ä¹æ¨ªè½´æ ç¾åå
æµè¯ï¼ï¼çå®äºåªè¯»çæèµ°å¿å¾ï¼ä¸ç¨æ·å·²ä¸è½½çéå¾å¯¹æ¯ï¼image1~13 ç SHA-256 å®å
¨ä¸è´ï¼ä»
image14ï¼é度ï¼ãimage15ï¼æ¥æ¤åº¦ï¼ååï¼OCR 确认æ°å¾æ¨ªè½´ä¸º 10æ1æ¥~10æ7æ¥ãæ 10æ8æ¥ã |
| | | - **è¾¹ç**ï¼å
¶ä½å¾ä»åºå® 8 天å£å¾ï¼æªæ¹ï¼æªå¯¼å
¥/è¦ç/é导任ä½ä¸å¡æ°æ®ã |
| | | - **éå¯**ï¼ä¸ºè®©æé®ä¸è½½ç¨ä¸æ°ä»£ç ï¼å·²ç¨ `start-dev.ps1` éå¯å端ï¼å端æªå¨ï¼ï¼é坿¶é´ 2026-10-08 10:38:53ï¼å¯ç´æ¥å¨ç³»ç»ééæ°ä¸è½½èµ°å¿å¾éªæ¶ã |
| | | - **éªæ¶**ï¼ç¨æ· 2026-10-08 å¨ç³»ç»ééæ°ä¸è½½èµ°å¿å¾åå®¡æ ¸ï¼éªæ¶éè¿ã |
| | | |
| | | ## åå²ï¼2026-10-08 ä¸çåå
³æºå¿«ç
§ï¼ |
| | | |
| | | ### 10-08ï¼èµ°å¿å¾ `(14)` ä¸å·²éªæ¶ `(12)` æ¯å¯¹ï¼å
容ä¸è´ï¼ |
| | | |
| | | - **ç¨æ·ä»»å¡**ï¼æ¯å¯¹ç³»ç»éæ°ä¸è½½ç `C:\Users\jcxiong\Downloads\2026å¹´åä¸åæå
¨ç综åè¿è¾å®¢æµéèµ°å¿å¾ (14).docx` 䏿¤åéªæ¶éè¿ç `(12)`ã |
| | | - **æ¯å¯¹æ¹æ³**ï¼æ DOCX å
é项æ¯å¯¹ã`(12)`ã`(13)`ã`(14)` ç 31 个å
鍿件ï¼å«æ£æ XMLãæ ·å¼ãå
³ç³»æä»¶åå
¨é¨å¾è¡¨å¾çï¼é个 SHA-256 å®å
¨ç¸åã |
| | | - **æ¯å¯¹ç»æ**ï¼ä¸ä¸ªæä»¶æ´ä½ SHA-256 ä¸åï¼ä»
å ZIP 容卿¶é´æ³çæå
å
æ°æ®ä¸åï¼æä»¶é¿åº¦ç¸åï¼å·®å¼ä¸º 234 个åèï¼éä¸å¨ 3 åèæ¶é´æ³æ®µï¼ææ¡£å
容没æä»»ä½å·®å¼ã |
| | | - **ç»è®º**ï¼`(14)` å°±æ¯å·²éªæ¶ç `(12)` åä¸çæ¬ï¼å¾è¡¨æ°å¼ãæ ç¾ãçå¼åæªååï¼ä¸éè¦éæ°éªæ¶ã |
| | | - **è¾¹ç**ï¼åªè¯»æ¯å¯¹ï¼æªä¿®æ¹ä¸è½½æä»¶ã代ç ãç³»ç»æ°æ®æä¸å¡åºã |
| | | - **å½åè¿è¡**ï¼2026-10-08 éå¯åï¼å·²ç¨ `start-dev.ps1` æ¢å¤ dev ç¯å¢ï¼å端 `8080`ãå端 `8090` åå¨çå¬å¹¶è®¿é®æ£å¸¸ãGit 卿¬æ¬¡äº¤æ¥æäº¤å为 `HEAD = origin/main = c7b6553`ï¼å·¥ä½åºæ å·²è·è¸ªæ¹å¨ï¼åªå©çº¦å®ä¸å
¥åºç `_tmp_*`ã`_tmp_pack_smoke_trend.docx` åè§æµç«åå§ XLSã |
| | | - **æ°æ®ä¸ä¸ä¸æ¥**ï¼å½åä¸å¡åºä»åªè¦çå° `2026-10-06`ï¼`2026-10-07` æªå¯¼å
¥ãæ°å¯¹è¯å
读 `AGENTS.md` + `HANDOFF.md`ï¼ç 10-07 æ°æ®é½å
¨åï¼å
åªè¯»æ ¸å¯¹æº Excel åå¼å¸¸é¡¹ï¼å¾å°ç¨æ·æç¡®æç¤ºåå导å
¥ææ¹å¨ï¼åçææç»èµ°å¿å¾ååææ¥åãæçº¿å¾æ¨ªè½´ä»æå·²ç¡®è®¤çåºå® 8 天å£å¾ï¼ä¸è½æ¹ææå¹´åº¦æªæã |
| | | |
| | | ### 10-08ï¼åäºåææ¥åè¶å¿æ ¸å¯¹ï¼åªè¯»ï¼æªæ¹å¨ææ¡£ï¼ |
| | | |
| | | - **ä»»å¡**ï¼æ ¸å¯¹ `D:\03_å
¬å¸\02_项ç®\27_æ¹åçéè·¯è¿è¾äºä¸åå±ä¸å¿\10_ä¸å¡\99_ä¸é¡¹ç»è®¡\01_éè¦è忥\2026\åä¸\2026å¹´åä¸.docx` çèµ°å¿ç»è®ºæ¯å¦ææ°æ®æ¯æãç¨æ·æç¡®å
åªçè¶å¿ï¼ä¸çº ç»åææªç»ææ¶çå
·ä½æ°å¼ï¼æä¸å»å¹´çå¾ä¸ä½ä¸ºå¤æä¾æ®ã |
| | | - **å½åæ°æ®è¾¹ç**ï¼åªè¯»æ¥è¯¢ç¡®è®¤ `holiday_passenger_flow`ã`holiday_vehicle_flow`ã`observation_station_flow` ååªè¦çå° `2026-10-06`ï¼`2026-10-07` å°æªå¯¼å
¥ãæ¬è½®æªæ§è¡ä»»ä½å¯¼å
¥ãè¦çæé导ã |
| | | - **æ»ä½ç»è®º**ï¼æ¥åä¸»çº¿åºæ¬æç«ã10 æ 1 æ¥è³ 6 æ¥è·¨åºåæ»éä¸ºé¦æ¥é«å¼ã2 æ¥è³ 3 æ¥ä¸éã4 æ¥è³ 5 æ¥åå°é«ä½ã6 æ¥å°å¹
åè½ï¼é路客è¿ç¨³å®ãéè·¯æåæ¥ææ¯è¾å¯ä¸å¢é¿ä¸æ½®æ±æ§ææ¾ãæ°´è·¯åæ¯ä¸éä¸ä¸å¹´å³°å¼åå¨ 10 æ 5 æ¥ååãèªé©¾ä¸»ä½å°ä½åå½ç鿥æµéé«äº 300 ä¸è¾æ¬¡ï¼åææ°æ®æ¯æã |
| | | - **éè¦æ¶ç´§çç»è®º**ï¼é´é¨å¯¼è´ä¸éç®ååªè½è¯´ææ¶é´å»åï¼ä¸è½åç¬è¯æå æï¼âåæç§¯å鿱鿾âè¯æ®ä¸è¶³ï¼éè·¯è½¦æµæ»éåªæ 10 æ 4 æ¥ææ¾ååï¼5 æ¥åºæ¬æå¹³ã6 æ¥ååè½ï¼ä¸è½åæ 4 æ¥è³ 7 æ¥æç»é«ä½ï¼âç鿏¸ãå¨è¾¹æ¸¸ã乡æå¾®åº¦åâåªè½è¯æçééè¡æ´»è·ï¼éææ
ã路线æ OD æ°æ®ï¼âå¿ååå乡çé客è¿é§æ§/å
åºâéç»ææ°æ®ï¼2025 ä¸ 2026 åªè½è¯´æ»ä½å½¢æç¸è¿ï¼ä¸æ¯æ¥é´èå¥ä¸è´ã |
| | | - **å¾
10-07 æ°æ®é½å
¨åå®ç¨¿**ï¼7 天æ»éãæ¥åãåæ¹å¼å æ¯ãå³°å¼è°·å¼ãè¿ç¨é«äºåºè¡ãéè·¯åæ°èªå°¾æ¥ãå
¨å¨æåæ¯ãéè¦å£å¾ï¼2025 年为 8 天ã2026 å¹´ç®å为 7 天ï¼åæ¯å¿
é¡»æç¸åæ¥æèå´æ¯è¾ï¼ä¸è½ç´æ¥ç¨ 7 天对 8 天ã |
| | | - **è¾¹ç**ï¼æªä¿®æ¹åäºç Wordï¼æªæ¹å¨ç³»ç»æ°æ®ï¼æªçææ°æ¥åæèµ°å¿å¾ãåç°é®é¢å
æ¥åï¼å¾å°ç¨æ·æç¡®æç¤ºååæ¹ã |
| | | - **Git/è¿è¡**ï¼`HEAD = origin/main = ca0bbe1`ï¼æ å·²è·è¸ªæä»¶æªæäº¤æ¹å¨ãdev å端 8090ãå端 8080 å¨å
³æºåä¿æè¿è¡ï¼éå
³æºèªå¨åæ¢ï¼éå¯åæ `AGENTS.md` ç¨ `start-dev.ps1` å¯å¨ã |
| | | |
| | | ## åå²ï¼2026-10-07 ä¸åï¼æ°å¼ä¼è¯äº¤æ¥ï¼ |
| | | |
| | | ### 10-07 å
³æºå¿«ç
§ï¼å½å交æ¥ï¼ |
| | | |
| | | - **Git ç¶æ**ï¼`HEAD` ä¸ `origin/main` å为 `179544a`ï¼å·²è·è¸ªæä»¶æ æªæäº¤æ¹å¨ãå·¥ä½åºä»
ä¿çæçº¦å®ä¸å
¥åºç `_tmp_*` 临æ¶ç®å½ã`_tmp_pack_smoke_trend.docx` åä¸ä»½è§æµç«åå§ XLSã |
| | | - **æ¬è½®æ´æ£**ï¼æè¯¯å°æçº¿å¾åºå® 8 å¤©å¤æä¸ºç¼ºé·ï¼æäº¤ `8ad75d5` æ
èªæ¹æäºæå¹´åº¦é
ç½®æªåï¼ç¨æ·æç¡®çº æ£åï¼å·²ç¨ `179544a` æ´ä½åéå¹¶æ¨éãå½å代ç ç¶æçåé误修æ¹åç `c21fbdd`ã |
| | | - **已确认å£å¾ï¼ä¸å¾åæ¹ï¼**ï¼æçº¿å¾æ¨ªè½´åºå® 8 天ï¼2025 å¹´æ 10 æ 8 æ¥çæ°æ®ç¹å横轴ä½ç½®ï¼2024ã2026 å¹´è¯¥æ¥æ ç¹ä½æ¨ªè½´ä¿ç 10 æ 8 æ¥ï¼ç´¯è®¡æ²çº¿åªè¿æ¥å®é
ææ°æ®çæ¥æï¼ç¼ºæ¥ä¸å»¶ç»ï¼è§æµç«é彿¥è¯¢è¦ç 10 æ 1 æ¥è³ 10 æ 8 æ¥ã |
| | | - **éè¯¯äº§ç©æé**ï¼`C:\Users\jcxiong\Downloads\2026å¹´åä¸åæå
¨ç综åè¿è¾å®¢æµéèµ°å¿å¾ï¼7天ç-å¾
éªæ¶ï¼.docx` æ¯é误æ¹å¨æ¶çæçæä»¶ï¼ä¸å¾ä½¿ç¨ï¼æªç»ç¨æ·æç¤ºä¸å é¤ã |
| | | - **è¿è¡ç¶æ**ï¼å
³æºåå端 `8090`ãå端 `8080` ä»çå¬ï¼éå
³æºèªå¨åæ¢ï¼ä¸æºåæ `AGENTS.md` ç¨ `start-dev.ps1` å¯å¨ã |
| | | - **æ°æ®è¾¹ç**ï¼æ¬è½®æªå¯¼å
¥ãè¦çæé导任ä½ä¸å¡æ°æ®ã |
| | | - **䏿¬¡æ¥æ**ï¼å
è¯»æ ¹ç®å½ `AGENTS.md` + æ¬æä»¶ï¼è¥ç¨æ·æä¾äº 10 æ 7 æ¥æ°æ®ï¼å
åªè¯»æ ¸éªï¼åç°å¼å¸¸å
æ¥åï¼å¾å°æç¡®æç¤ºååæ§è¡ä»»ä½ä¿®æ¹ã导å
¥ææäº¤ã |
| | | |
| | | ### 10-07ï¼æ¯æ¥å®¢æµææå¾å³ä¾§æ ç¾å¸å±ä¿®å¤ï¼éªæ¶éè¿ï¼ |
| | | |
| | | - **é®é¢**ï¼`image13`ãæ¯æ¥å®¢æµææååãå³ä¾§å¤ç½®ç¾åæ¯æ ç¾å¨ç¸é»æ¥æä¹é´éå 4 å¤ï¼æ¹ä¸ºä¸¤ååå¼çº¿äº¤éï¼ä¸ªå«å¼çº¿åä½ç»¿è²åå
æ°å¼ã |
| | | - **æç»è§å**ï¼åªæè²åå
æ¾ä¸ä¸çæ ç¾è¿å
¥å³ä¾§å¤ç½®åºï¼å¤ç½®æ ç¾æåå§å¾ä¾åºå·æåºï¼å³éè·¯âéè·¯âæ°èªâæ°´è·¯ï¼æå¤ 2 åï¼æâå
è¡ååãæ¯è¡ä»å·¦å°å³âå¡«å
ï¼1 个æ ç¾æ´ä½å±
ä¸ï¼2 个æ ç¾ä¸¤ååå±
ä¸ï¼3 个æ ç¾ä¸æåæ ç¾è·¨ä¸¤åå±
ä¸ï¼4 个æ ç¾ä¸º 2Ã2 æ ¼å±
ä¸ï¼ä¸¤åå¸å±ä¸ç»å¼çº¿ï¼é¢è²å¯¹åºè²åã |
| | | - **å®ç°**ï¼`HolidayTrendChartService` å°å³ä¾§æ ç¾å¸¦ä» 74 å å®½å° 140ï¼`HorizontalStackCallout` è®°å½åå§ `seriesIndex` å¹¶æå
¶æåºï¼ä»
å¯¹æ¯æ¥å®¢æµææå¾å¯ç¨ä¸¤åå±
ä¸å¸å±ï¼å
¬è·¯åºè¡ç»æä»èµ°ååå带å¼çº¿é»è¾ã |
| | | - **éªè¯**ï¼çå®åºåªè¯»çæ 2026-10-07 èµ°å¿å¾ï¼ä¸åçç¸æ¯ 15 å¼ å¾ä¸ä»
`image13` ååï¼å
¶ä½ 14 å¼ SHA-256 å®å
¨ä¸è´ï¼å³ä¾§æ ç¾åº OCR éå ä» 4 é为 0ï¼æ å¼çº¿ãå端å
¨é `mvn -q test -f traffic-audit-server\pom.xml` éè¿ã |
| | | - **éªæ¶**ï¼ç¨æ· 2026-10-07 éªæ¶ `(12).docx` éè¿ã |
| | | - **åç»**ï¼10-07 æ°æ®é½å
¨åï¼æç³»ç»é
ç½®çåæå¤©æ°ï¼7 天ï¼çææç»çèµ°å¿å¾ï¼ç¨äºåææ¥åï¼æ¬è½®ä¸è°æ´åæ¥å¤©æ°æ¥æºã |
| | | - **æ°æ®è¾¹ç**ï¼æªå¯¼å
¥ãè¦çæé导任ä½ä¸å¡æ°æ®ã |
| | | |
| | | ### 10-07ï¼æ®éå½çé 2025-10-05 è°æ´å¼æ´æ£ï¼å·²æ ¸å¯¹ï¼ |
| | | |
| | | - **èæ¯**ï¼10-05 åé¤ 3 ä¸ªç¯æ¯éå¹
è¶
è¿ 50% çç«ç¹ï¼éåºãæ¨å®¶åºã䏿´¥ç«ï¼å 3 个 10-05 缺失ç«ç¹ï¼é¾æ³ãåé²äº¤è°ç«ã大å
¸æ²³ï¼ã |
| | | - **ç«ç¹ç¼å·**ï¼éåº `S101L110420115`ãæ¨å®¶åº `S120L105420702`ã䏿´¥ç« `G242L104420322`ã龿³ `S216L103420506`ãåé²äº¤è°ç« `S223L117421024`ã大å
¸æ²³ `S464L105422828`ã |
| | | - **忥åé¤å计ï¼ä¸å°å®¢æµéï¼è¾æ¬¡ï¼**ï¼2026-10-04 `16,484`ï¼2026-10-05 `4,398`ï¼2025-10-05 `14,239`ï¼2024-10-05 `11,907`ãè°æ´æ¶ç¼ºå¤±ç«ç¹æ 0 å¤çã |
| | | - **ç³»ç»æ´æ£**ï¼`holiday_vehicle_adjustment` ç 2025-10-05 è°æ´å¼å·²ç± `322.7801` æ¹ä¸º `332.7801`ï¼æ´æ°æ¶é´ 2026-10-07 08:45:52ï¼ï¼2024-10-05 `289.5557`ã2025-10-04 `337.5009`ã2024-10-04 `322.5277` ä¸åã |
| | | - **å·¥ä½ç°¿æ´æ£**ï¼`C:\Users\jcxiong\Downloads\2026å¹´å䏿°æ® (4).xlsx`ï¼ä¿®æ¹æ¶é´ 08:46:52ï¼ä¸ `æ®éå½çé!F7=332.7801`ãèå¨ç»æå·²æ£ç¡®éç®ï¼`æ®éå½çé!G7=-4.8%`ï¼`äººåæµå¨éæ±æ»` ç `E7=1460.97`ã`F7=-0.3%`ã`K7=1372.85`ã`Q7=1290.04`ã`R7=-1.0%`ã`V7=-4.28%`ã |
| | | - **å
¬å¼æ´æ£å·²æ ¸å®**ï¼`æ®éå½çé!E8=(D8-D7)/D7`ã`G8=D8/F8-1`ã`I8=D8/H8-1`ã`E9=(D9-D8)/D8`ã |
| | | - **10-06 è¾¹çï¼éè¦ï¼**ï¼å·¥ä½ç°¿ç®ååªæéè·¯ `C9=96.2`ãæ°´è·¯ `C8=5.4932`ãé«éå
¬è·¯ `C8=206.1206`ï¼æ°èª `C9`ãéè·¯ `C8`ãæ®éå½çé `D8` ä»ä¸ºç©ºãå æ¤ `äººåæµå¨éæ±æ»!C8=596.38264` åªæ¯é¨åå计ï¼`D8=-59.06%` 以åå `-100%/#DIV/0!` é½ä¸å¯ç¨äºå¿«æ¥ï¼è¡¥é½ä¸é¡¹åéç®ã |
| | | - **æ°æ®è¾¹ç**ï¼æ¬è½®åªè¯»æ ¸å¯¹ï¼æªå¯¼å
¥ãè¦çæé导任ä½ä¸å¡æ°æ®ã |
| | | |
| | | ### 10-07ï¼Codex è¾å
¥å¡é¡¿ææ¥ä¸äº¤æ¥å»ºè®® |
| | | |
| | | - å½å线ç¨ä» 10-05 è¿ç»ä½¿ç¨è³ä»ï¼rollout 约 `32.17 MB`ï¼å« `3,751` æ¡ `response_item`ã`3,469` æ¡ `event_msg`ã`10` 次 compactionï¼ç¨æ·å·²å³å®æ°å¼å¯¹è¯ç»§ç»ã |
| | | - å·²æé¤ `codex-plus-plus-manager --show-update`ï¼æªè¿è¡ï¼ï¼éè¯é£æ´ guard å·²å®è£
ï¼`dropped=859` ä¸è¿ç» 10 ç§ä¸å¢é¿ï¼å½å没ææç»éè¯é£æ´ã |
| | | - è§æµå° Codex 主è¿ç¨ç´¯è®¡çº¦ `4.34 h` CPUãæ¸²æè¿ç¨çº¦ `1.28 h`ï¼çæ¶å³°å¼ä¸»è¿ç¨çº¦ `116%` åæ ¸ã渲æçº¦ `44%`ï¼å±äºé´æé«è´è½½ã建议æ°å¯¹è¯ç»§ç»ï¼è¥ä»å¡ï¼å®å
¨éåºå¹¶éå¯ Codexã |
| | | - **æ°å¯¹è¯æ¥æ**ï¼å
è¯»æ ¹ç®å½ `AGENTS.md` + æ¬æä»¶ãå½å git `HEAD` ä¸ `origin/main` å为 `9b56330`ï¼å·²è·è¸ªæä»¶æ æ¹å¨ï¼dev å端 `8080`ãå端 `8090` æ£å¨è¿è¡ã |
| | | |
| | | ## åå²ï¼2026-10-07 æ©é´å
³æºå¿«ç
§ï¼ |
| | | |
| | | ### 10-07 å
³æºå¿«ç
§ |
| | | |
| | | - **å½åç¶æ**ï¼æ¬è½®ä»
åå
³æºäº¤æ¥ï¼æ 代ç ãæµè¯å£å¾æä¸å¡æ°æ®åæ´ãä¸ä¸ä»»å¡âæçº¿å¾çºµåé´è·å é« 20%âå·²äº 10-06 éªæ¶ï¼æäº¤ä¸º `54e10ae fix(holiday): å 大æçº¿å¾çºµåé´è·å¹¶åæ¥ Word 尺寸`ã |
| | | - **Git**ï¼`main` ç `HEAD` ä¸ `origin/main` å为 `54e10aee11aa4a10926df02782ca07d41eeb9b98`ï¼æ æªæäº¤çå·²è·è¸ªæä»¶ï¼å·¥ä½åºä»
ææçº¦å®ä¸å
¥åºç `_tmp_*` 临æ¶ç®å½/æä»¶ã`_tmp_pack_smoke_trend.docx` åä¸ä»½è§æµç«åå§ XLSã |
| | | - **è¿è¡ç¶æ**ï¼å
³æºåæ¬æº dev åå端ä»å¯è®¿é®ï¼å端 8080ãå端 8090 åè¿å 200ï¼ï¼éè®¡ç®æºå
³æºä¼èªå¨åæ¢ï¼éå¯åæ `AGENTS.md` ç¨ `start-dev.ps1` å¯å¨ã |
| | | - **æ¬å°çå**ï¼æçº¿å¾çºµåé´è·çæµè¯æ¥åå工使¥ç»å·²åå
¥ `docs/åè½æµè¯æ¥å/`ã`docs/工使¥ç»/`ï¼åæçº¦å®ä¸å
¥åºã |
| | | - **䏿¬¡æ¥æ**ï¼å
è¯»æ ¹ç®å½ `AGENTS.md` + æ¬æä»¶ï¼ä»åºçº§å¾
åä»è§ç¬¬ä¸èï¼æ¬è½®æ æ°å¢å¾
åã |
| | | - **æ°æ®è¾¹ç**ï¼æªå¯¼å
¥ãè¦çæé导任ä½ä¸å¡æ°æ®ã |
| | | |
| | | ## ææ°ï¼2026-10-06ï¼ |
| | | |
| | |
| | | |
| | | |
| | | - 2026-08-31ï¼ä¸çåï¼ï¼æ°æ®å¯¼å
¥é¡µéæéªæ¶éè¿ï¼commit 791e70f å·²æ¨éï¼è¯¦è§ç¬¬ 7 èï¼ãæ¹é å
¨ç¨ Python æ¿æ¢åæä»¶ï¼apply_patch æ¬æºå¿
è´¥ï¼ï¼å
³é®åï¼Vue æ°æ¹æ³è¯¯æ¾ methods å¤ â render TypeErrorãel-table å±å¼è¡ä¸æ¸²æï¼[Vue warn] åªå¨ console ä¸å¨ pageerrorï¼éªè¯ç¨ puppeteer æ¦æª /api/import/batches è¿å mock å¤±è´¥æ°æ®ç´æ¥éªæç»è¡¨ï¼ç宿 å¤±è´¥æ°æ®éªãææ å¯¼å
¥å¤±è´¥è®°å½ã空æã |
| | | |
| | | - 2026-10-09ï¼ææ¥èçãäººåæµå¨éæ
åµã表å¶ä½ **2026 åä¸åæ**çï¼è¾åº `D:\03_å
¬å¸\02_项ç®\27_æ¹åçéè·¯è¿è¾äºä¸åå±ä¸å¿\10_ä¸å¡\99_ä¸é¡¹ç»è®¡\01_éè¦è忥\2026\åä¸\2026å¹´åä¸åæäººåæµå¨éæ
åµ.xlsx`ï¼åæ¥è模æ¿å¤ä»½å¨ `_tmp_flow_table\template_backup.xlsx`ï¼æªå ç¨æ·æä»¶ï¼ãç»æï¼æ é¢è¡ + ä¸çº§å并表头 + `å计` è¡ + 10æ1æ¥~10æ7æ¥éæ¥è¡ï¼7 ä¸ªææ ï¼å
¨ç¤¾ä¼è·¨åºå / éè·¯ / å
¬è·¯äººåæµå¨éãä¸åæµå¨éå计ãé«éå
¬è·¯åæ®éå½çééè¥ä¸æ§å°å®¢è½¦äººååºè¡éãå
¬è·¯è¥ä¸æ§å®¢è¿éã/ æ°´è·¯ / æ°èªï¼æ¯ä¸ªé½ç»ãæ°é / 忝2025å¹´ / 忝2024å¹´ãï¼å³æå表çã忝2019å¹´ãæ¹ä¸ºã忝2024å¹´ãå¹¶ç»æ¯ä¸ªææ è¡¥é½ç¬¬äºä¸ªåæ¯åï¼A~V å
± 22 åï¼ãæ°æ®åèªç³»ç» `GET /api/holiday/overview?holidayType=NATIONAL_DAY`ï¼2024/2025/2026ï¼ï¼å¹¶éé¡¹å¯¹é½ 10æ7æ¥æºæä»¶ `2026å¹´å䏿°æ®10æ7æ¥.xlsx` ç `äººåæµå¨éæ±æ»` ä¸ éè·¯/æ°´è·¯/æ°èª/éè·¯ å页ï¼å
¨é¨ä¸è´ï¼7 天å计æ»é 10115.56 ä¸äººæ¬¡ã忝2025 -4.46%ã忝2024 +4.25%ï¼ãå£å¾æç弿 ¡éªéè¿ï¼å
¬è·¯äººåæµå¨é = éè¥ä¸æ§å°å®¢è½¦ + å
¬è·¯è¥ä¸æ§ï¼å
¨ç¤¾ä¼ = å
¬è·¯ + éè·¯ + æ°´è·¯ + æ°èªãçæèæ¬ `_tmp_flow_table/nodework/build_xlsx.mjs`ï¼artifact-toolï¼ä¸´æ¶ç®å½ä¸å
¥åºï¼ã |
| | | |
| | | - 2026-10-09ï¼è¡¥ï¼ï¼ä¸è¡¨æ A4 æå°åéé
ââå宽æ¶çªï¼æ¥æå 7ãæ°éå 7.5ã忝å 6.8ï¼+ å·¥ä½è¡¨æå°è®¾ç½®ï¼A4ãæ¨ªåã缩æ¾ã1 页宽Ã1 页é«ããæ°´å¹³å±
ä¸ã页边è·å·¦å³ 0.35â³/ä¸ä¸ 0.4â³ï¼ãç¨ Excel å®é
å¯¼åº PDF 宿µ **æ£å¥½ 1 页**ï¼A4 横å 841.92Ã595.32 ptï¼ï¼æ°å¼/ç¾åæ¯æ `####`ãæ æªæãæ³¨ï¼artifact-tool æ æå°è®¾ç½® APIï¼pageSetup/printOptions/pageMargins æ¯å¯¼åºå注å
¥ XLSX ç `xl/worksheets/sheet1.xml`ï¼èæ¬ `_tmp_flow_table/print_setup.py`ï¼ä¸´æ¶ä¸å
¥åºï¼ã |
| | | |
| | | - 2026-10-09ï¼ç»ï¼ï¼æå馿¹åºåä¸äººåæµå¨é表第 2 çï¼è¾åº `D:\03_å
¬å¸\02_项ç®\27_æ¹åçéè·¯è¿è¾äºä¸åå±ä¸å¿\10_ä¸å¡\99_ä¸é¡¹ç»è®¡\01_éè¦è忥\2026\åä¸\2026å¹´åä¸åæäººåæµå¨éæ
åµ2.xlsx`ï¼å表 A1:S13ï¼ãæ¹å¨ï¼â 表头顺åºè°æ´ä¸º å
¨ç¤¾ä¼ / å
¬è·¯ / éè·¯ / æ°´è·¯ / æ°èªï¼å
¬è·¯æåï¼ï¼â¡å»æãå
¬è·¯äººåæµå¨é·æµå¨éå计ãååï¼åªç é«éå
¬è·¯åæ®éå½çééè¥ä¸æ§å°å®¢è½¦äººååºè¡é ä¸ å
¬è·¯è¥ä¸æ§å®¢è¿éï¼â¢æ°å¢ãæ¥åãè¡ï¼è¡åºï¼åè®¡ãæ¥åã10æ1æ¥~7æ¥ï¼ï¼â£ãå计ãè¡åæ¯2025 æ¹ä¸º 2026å¹´7天å计 ÷ 2025å¹´8天å计ï¼2025å¹´å½åºåæä¸º10æ1-8æ¥å
±8天ï¼ï¼åæ¯2024 ä¸åï¼æ¥åç忝æåå¹´å¤©æ°æç®ï¼2026ã2024为7天ï¼2025为8天ï¼ãæ°å¼ï¼åè®¡åæ¯2025 å
¨ç¤¾ä¼ -14.37%ãéè¥ä¸æ§å°å®¢è½¦ -14.82%ãå
¬è·¯è¥ä¸æ§ -17.45%ãéè·¯ -3.33%ãæ°´è·¯ -23.76%ãæ°èª -17.12%ï¼æ¥å忝2025 å
¨ç¤¾ä¼ -2.13%ãæ é¢æ²¿ç¨ç¨æ·çæ¬ã2026å¹´âåä¸âåæå
¨ç¤¾ä¼è·¨åºåäººåæµå¨éæ
åµããä»ä¸º A4 横å 1 页ï¼å¯¼åº PDF 宿µï¼ãæ°æ®æºååï¼ç³»ç» overview + 10æ7æ¥æºæä»¶æ ¸å¯¹ï¼2025 第8æ¥åç³»ç» dayIndex=8ï¼ã |
| | | |
| | | - 2026-10-12ï¼äº¤æ¥å¿«ç
§ï¼ç¨æ·å夿°å¼å¯¹è¯ï¼ï¼çµèéå¯åå·²ç¨ `start-dev.ps1` æèµ·åå端ï¼èæ¬è¾åºå端 OKãå端 OKï¼åç«¯è®¿é® http://localhost:8080ï¼å端 8090ï¼æ¥å¿åå«è§ `backend.log` / `traffic-audit-web/frontend.log`ãå½å git 忝 `main`ï¼æ å·²è·è¸ªæä»¶æ¹å¨ï¼å·¥ä½åºæ 10-09 çæçæªå
¥åºä¸´æ¶ç®å½/æä»¶ï¼`_tmp_doc_compare/`ã`_tmp_flow_table/`ã`_tmp_holiday_verify/`ã`_tmp_rail_label/`ã`_tmp_pack_smoke_trend.docx`ï¼åç¨æ·æ°æ® `docs/ä¸é¡¹ç»è®¡/éè¦è忥/è§æµç«åæ¥è°æ¥æ°æ® (2026å¹´10æ1æ¥).xls`ï¼ä¸è¦å é¤æè¯¯æäº¤ãæ¬è½®æªæ¹ä»£ç ãæªè·æµè¯ãæªæäº¤ã |
| | | |
| | | - 2026-10-12ï¼ä¸å¡çç¹æ ¸æ¥ï¼åªè¯»ï¼ï¼ä¼ä¸ä¸æ¥çãååª-è¿å®ããååªå°è¿å®ã两个å°åå·²åæ ¸ãå¯ç¡®è®¤ãååªãå¨å
´å±±å¿æ°´æå¯ºéååªä¸å¸¦ï¼ååªæåºãååªç£·ç¿éè¿ï¼ï¼ååªç£·ç¿å
¬å¼èµæè¿æ¶åæ¦åä¹¡ç³æ±æï¼ãè¿å®ãæå®æå¸è¿å®å¿ï¼ãååªãå¨å®æ/å
´å±±å
¬å¼å°å䏿ªæ¥å°ææå°ç¹ï¼å¤æå¤§æ¦çæ¯ãååªãçéåæç®ç§°ï¼å¦æãå
´å±±ååªç©æµæéå
¬å¸ã注åå¨å³¡å£éæ³æ¹æºªæï¼ä½å
¬å¸åä¸è½çåå°åï¼ãææ°´æå¯ºéååªå°è¿å®å¿åçå
¬å¼éè·¯æµç®ï¼åç¨çº¦ 106.7 kmï¼ä¼ä¸åå¡« 201 kmï¼ä¸è½ç´æ¥è®¤å®é误ï¼éä¼ä¸è¡¥å
å
·ä½è£
è´§ç¹ãå¸è´§ç¹ãåç¨/å¾è¿åç»è¡è·¯çº¿ååæ ¸ãç³»ç» `h2032_enterprise_monthly` çå®¡æ ¸è¯´æä¸æªçä¸è¿ä¸¤å¤å°ç¹åæã |
| New file |
| | |
| | | # æ±æ»å¤§è¡¨ãçææ°æ®æ¯å¦æ£ç¡®ãéªè¯æ¹æ³ï¼2026-10-12ï¼ |
| | | |
| | | > ç¨éï¼åç"ç¨åºçæçæ°æ®å¯¹ä¸å¯¹"ã |
| | | > **ä¸å¥è¯åå**ï¼æ¯ä¸æ®µé½è¦æä¸ä¸ª**ç¬ç«äºè¯¥æ®µçåç
§**ââä¸¤è¾¹åæºï¼å°±éªä¸åºä¸è¥¿ã |
| | | > æ¬æ¹æ³æ¯ 2026-10-12 ç¨ä¸¤ç»ç宿°æ®è·åºæ¥å¹¶ä¿®æ£è¿çï¼è§ç¬¬ä¸èï¼ã |
| | | |
| | | ## ä¸ãå
æé®é¢æå¼ï¼å¦å"æ£ç¡®"没æå®ä¹ï¼ |
| | | |
| | | "ç¨åºçæçæ°æ®æ¯å¦æ£ç¡®"å®é
æ¯ 4 个ä¸åçé®é¢ï¼åç
§å责任人é½ä¸åï¼ |
| | | |
| | | | # | é®é¢ | åç
§ï¼å¿
é¡»ç¬ç«äºè¢«éªå¯¹è±¡ï¼ | è°è´è´£ | ç¨åºè½èªè¯å | |
| | | |---|---|---|---|---| |
| | | | 1 | æºæ°æ®å¯¹ä¸å¯¹ | æºæä»¶ / çéä¸åè¡¨æ¨¡æ¿ | æ°æ®ãä¸å¡ | ä¸è½ | |
| | | | 2 | ç®æ³ãå£å¾å¯¹ä¸å¯¹ | æå SQL å¤ç®ï¼ä¸çæ¯çãä¸çå®ç¨¿ï¼ | ç¨åº | è½ | |
| | | | 3 | ååãçå¼å¯¹ä¸å¯¹ | ä¸ä¸ææ¯çï¼åæºå¯¹æ¯ï¼è¦æ±"ä¸è¯¥å¨çæ ¼æªå"ï¼ | ç¨åº | è½ | |
| | | | 4 | ä¸äººå·¥å®ç¨¿ä¸è´å | æå·¥å®ç¨¿ï¼å¿
é¡»è®°çæ¬ + sha256ï¼ | 忹夿 ¸ | é¨å | |
| | | |
| | | æä»¥åé®"æ°æ®å¯¹ä¸å¯¹"没æçæ¡ï¼å
é®"**åªä¸æ®µ**"ã |
| | | |
| | | ## äºãäºæ®µé¾æ¡ä¸åèªçéªè¯ææ®µï¼å«å¤±è´¥æ¨¡å¼ï¼ |
| | | |
| | | ### L0 æºæä»¶ â åº |
| | | - **åç
§**ï¼æºæä»¶æ¬èº«ã |
| | | - **ææ®µ**ï¼è¡æ°ï¼`import_batch` ç total/success/fail vs 表å
`COUNT(*)`ï¼ï¼éè¡æ½æ¥ï¼æ¯å¸å· 1~2 æ¡ + è¾¹çï¼æå¤§ãæå°ã缺æ¥å¸å·ï¼ï¼å使 ¸å¯¹ï¼å¨ vs ä¸å¨ï¼ï¼å计å¾ç¨½ï¼Î£17 å¸å· = æºè¡¨"æ¹åç"è¡ï¼ã |
| | | - **å
¸å失败**ï¼æææ¼å¯¼ï¼åéä½ï¼åä½å·® 10000ï¼éå¤å¯¼å
¥å å ï¼"ææéå¸å·å¼ä¸ç¸é»æå®å
¨ç¸å"æ¯éå¤å¯¼å
¥çæçº¹ï¼ã |
| | | - **å·¥å
·**ï¼`_compare_freight.py` é£ç±»"æä»¶ vs åº"èæ¬ï¼`import_batch`ã |
| | | |
| | | ### L1 åº â å£å¾å¼ï¼**æå¼å¾æå
¥çä¸å±**ï¼ |
| | | - **åç
§**ï¼æå£å¾åæ SQL/èæ¬**èªå·±ç®ä¸é**ï¼ä¸ä¾èµæ¯çãä¸ä¾èµå®ç¨¿ã |
| | | - **ææ®µ**ï¼â å¼å¸¸ä½æ£ `GET /api/report/summaryWorkbook/freightCheck?period=YYYY-MM`ï¼åå为è´ãâ æ¼æ¥ãΣå¸å·â çè¡ãç¯æ¯å¼å¸¸ã累计å¾ç¨½ï¼ï¼â¡ **ç¼ºæ°æ¸
å**ï¼ç®æ æè¯¥æçæä»½/å¸å·æ¯å¦é½å
¨ï¼ï¼â¢ éç¹å¤ç®ï¼å
¨ç + æ½ 2 个å¸å·ï¼è¦ç è§ä¸/è§ä¸/å计/å¨è½¬é/ä¸å£å¾åç»´/åå¸å
ï¼ã |
| | | - **å
¸å失败**ï¼è§ä¸ååä¸ºè´æèé«ï¼å
¨çè¡ä¸ Σå¸å·ä¸éåï¼æ¥æºè¡¨ç¼ºæã |
| | | - **注æ**ï¼**使£éè¿ â æ°æ®é½å
¨**ã2026-08 使£ 0 å½ä¸ï¼ä½ 9 æçæä»¶ç 8 æåä»ç©ºã9 æå¨è½¬éä»ä¸º 0 â å¼å¸¸æ¸
åä¸ç¼ºæ°æ¸
åå¿
é¡»**åå¼è¾åº**ã |
| | | |
| | | ### L2 å£å¾å¼ â åå
æ ¼ï¼ååå±ï¼ |
| | | - **åç
§**ï¼ä¸ä¸ææ¯ç / ä¸ä¸æçæä»¶ã |
| | | - **ææ®µ**ï¼éæ ¼ diffï¼**弿¨¡å¼ä¸å
¬å¼æ¨¡å¼åè·ä¸é**ï¼ç®æ æåæ¯å¦ççåè¿å»äºï¼ä¸æå以ååæ¯å¦ 0 ååã |
| | | - **å
¸å失败**ï¼å½æå没åï¼æ¬æ¬¡å®è¯ï¼è´§è¿ãç½çº¦è½¦åªåå¡«"ç®æ æ"ï¼æ¯çç¼ºä¸æå°±è¡¥ä¸ä¸ï¼ï¼ä¸æå被éç®å²æï¼å
¬å¼æé¾ã |
| | | |
| | | ### L3 åå
æ ¼ â æ´æ¬ï¼åºè¡¨å±ï¼ |
| | | - **åç
§**ï¼æå·¥å®ç¨¿ï¼**å¿
须记å½çæ¬ + sha256**ï¼åªå¨å®ç¨¿æªæ»åæ¶ææï¼ã |
| | | - **ææ®µ**ï¼`python docs\å·¥å
·\æ±æ»å¤§è¡¨å·®å¼æ¯å¯¹.py <çæä»¶> <æå·¥å®ç¨¿> <è¾åºåç¼>`ï¼åªå¤è¯» **å®è´¨æ°å¼å·®å¼ / éå¤ç(鿰弿æ¬)** 两类ï¼å·®å¼æé¢ç»è®°ç½åååç±»ã |
| | | - **å
¸å失败**ï¼æ"å®ç¨¿æ»å"彿"ç¨åºåå½"ï¼æ¬æ¬¡ï¼å®ç¨¿æ¯ 9/13 çï¼ç¼º 09-28 ä¹åçæå·¥æ¹å¨ â å¤åº 364 æ¡æå·¥ä¾§éè¯¯å¼ + 10 æ¡å·²ç¡®è®¤é¡¹ï¼ã |
| | | |
| | | ### L4 跨页åæé¡µå¾ç¨½ï¼**æå®¹æè¢«æ¼**ï¼ |
| | | - **åç
§**ï¼å¾ç¨½å¼æ¬èº«ã |
| | | - **ææ®µ**ï¼`å叿±æ»`ã`åå¸åå¸å·`ã`ä¸å£å¾æç»`ã`éè·¯è¿è¾å¨è½¬é` æ¯é¡µç»ä¸æ¡å¾ç¨½å¼èªæ£ï¼ä¾ï¼éè·¯è¿è¾å¨è½¬é `B = Gï¼è´§è¿é¡µè§ä¸+è§ä¸å¨è½¬éï¼ + L/2ï¼åå¸å®¢è¿éï¼ + Q/10ï¼å
¬è·¯æ
客å¨è½¬éï¼`ã |
| | | - **å
¸å失败**ï¼æºé¡µé½å¯¹ãåæé¡µå¼ç¨éåãæ¬æ¬¡ 2025-03/04 ç½çº¦è½¦æåçå½±åé¢ï¼å°±æ¯é "æå页 vs æç»é¡µä¸¤æ¡é¾è·¯å¯¹ä¸ä¸"æ´é²çã |
| | | |
| | | ## ä¸ãååæºç²åºï¼**æéè¦ç䏿¡**ï¼ |
| | | |
| | | 仿¥å®è¯ï¼**"çæä»¶ vs æå·¥å®ç¨¿"è¿ä¸å±ï¼å¨æ¯çä¸å®ç¨¿åæºæ¶ï¼æºæ°æ®æ¬èº«çéæ°¸è¿æ¥ä¸åºæ¥ã** |
| | | - 2025-11 è´§è¿å®æå¨è½¬éå·® 34.30ï¼2 æ ¼ï¼ãç线å
车个ä½å¨è½¬éå·®ï¼5 æ ¼ï¼ï¼å¨"çæä»¶ vs å®ç¨¿"鿝 **0 æ¡**ï¼åªæ"**åºç°ç® vs æ¯çç¼å**"ææ´é²ã |
| | | - æä»¥ï¼**å¿
é¡»ä¸é¨å䏿¬¡"åºå
å¼ vs æ¯çç¼åå¼"çå
¨é diff**ââè¿ä¹æ£æ¯ 2026-10-09 交æ¥å为"éå
å"çå¾
åï¼æ¯"è½ä¸è½å读åº"çå¯ä¸åç½®æ¡ä»¶ã |
| | | - æªè¾è¦åæ¸
ï¼**åºè¡¨å£å¾**ä¸"åºä¸è½æ¿ä»£æ¯çç¼å"ï¼F8/Q1ï¼ï¼**éªè¯æ¹æ³**ä¸å¿
é¡»ä¸é¨æ¯ä¸æ¬¡ã两è
ä¸çç¾ã |
| | | |
| | | ## åãæä½é¡ºåºï¼ç
§æå³å¯ï¼ |
| | | |
| | | 1. **å»ç»**ï¼è®° æ¯ç sha256ãå
³é®æºè¡¨ `max(report_period)` ä¸è¡æ°ãå端è¿ç¨ PID + å¯å¨æ¶é´ã |
| | | ï¼"夿æ¯å¦æ¢äºæ°è¿ç¨"å¿
é¡»ç `netstat -ano` ççå®çå¬ PID + æ¥å¿å¯å¨è¡ï¼ä¸è½åªä¿¡å¯åèæ¬è¾åºãï¼ |
| | | 2. å¼å¸¸ä½æ£ + ç¼ºæ°æ¸
åï¼**å两份è¾åº**ï¼ã |
| | | 3. å£å¾å¤ç®éç¹ï¼å
¨ç + 2 å¸å·ï¼è³å°è¦ç è§ä¸/è§ä¸/å计/å¨è½¬é/ä¸å£å¾/åå¸å
ï¼ã |
| | | 4. çå¼èªæ£ï¼å½æåå·²åï¼ä¸æå以åå 0 ååï¼`#REF!` æ°ä¸å¢ï¼æ é¢ææ¬¡ = ç®æ æï¼ç´¯è®¡ = 1..Mã |
| | | 5. ä¸å®ç¨¿éæ ¼ diffï¼åªç两类ï¼ï¼å·®å¼æç½åååç±»ã |
| | | 6. åæé¡µå¾ç¨½ï¼L4ï¼ã |
| | | 7. åç»è®ºï¼éè¿é¡¹ / æªéè¿é¡¹ / **æªè¦ç项**ï¼æç¡®è¯´æ¸
åªå 屿²¡éªï¼ã |
| | | |
| | | ## äºãéè¿æ åï¼å»ºè®®åæ»ï¼ |
| | | |
| | | - **L0**ï¼è¡æ°ä¸å¾ç¨½ä¸è´ï¼0 å·®å¼ï¼ã |
| | | - **L1**ï¼ä½æ£ 0 å½ä¸ + ç¼ºæ°æ¸
åä¸ºç©ºï¼æç¼ºæ°å·²ç¡®è®¤ä¸ºä¸å¡äºå®ï¼+ éç¹å¤ç® 0 å·®å¼ã |
| | | - **L2**ï¼ä¸æå以åå 0 ååï¼å½æååå
¥æ ¼æ° = 颿ã |
| | | - **L3**ï¼å®ç¨¿ diff ç"å®è´¨æ°å¼å·®å¼"åªå«ç½åå项ã |
| | | - **L4**ï¼å¾ç¨½å¼ 0 å·®å¼ã |
| | | - **ä»»ä¸å±æªè¦ç â ç»è®ºåªè½å"æªè¦ç"ï¼ä¸å¾å"å·²éªè¯"ã** |
| | | |
| | | ## å
ã䏿¡çºªå¾ |
| | | |
| | | 1. **ç¬ç«åç
§**ï¼åæºç两ç§äº§ç©äºæ¯çäºæ²¡éªã |
| | | 2. **䏿¬¡åªæ¢ä¸ä¸ªåé**ï¼æ¢ä»£ç / æ¢æ°æ® / æ¢æ¯çåå¼éªï¼å¦å差弿 æ³å½å ã |
| | | 3. **å·®å¼å
åç±»åç»è®º**ï¼ç»æ/æ ¼å¼/æµ®ç¹å°¾å·®åªè®¡æ°ï¼åªæ"å®è´¨æ°å¼å·®å¼ + éå¤ç"è¿æ¸
åãç½ååï¼`USER_EDITS`ã已确认项ï¼é¢ç»è®°ï¼ä¸è¦æ¯æ¬¡éæ°å¤è¯»ã |
| | | |
| | | ## ä¸ãæ¬æ¹æ³çå®è¯ï¼2026-10-12ï¼ |
| | | |
| | | 1. **2026-08 çæä»¶ vs 9/13 æå·¥å®ç¨¿**ï¼1020 æ¡æç»éåªæ 11 æ¡è¦å
³æ³¨ï¼10 æ¡è´§è¿ 2022 ç´¯è®¡åæ¯ãä¸å¡å·²ç¡®è®¤è½¯ä»¶å¯¹ã+ 1 æ¡æå·¥ä¾§èå符ï¼ï¼å
¶ä½å
¨æ¯çº¦å®å·®å¼ï¼ç»æ/è稿å/ææ¬åæ°å 354/忴尾差/æå·¥ä¾§éè¯¯å¼ 364ï¼â **ç®æ³å±éè¿**ã |
| | | 2. **åºå
2025 vs æ¯ç 2025**ï¼å
¬äº¤ãåºç§ã轨é轮渡 **100% ä¸è´**ï¼è´§è¿ãç线å
车é¤ä¸ªå«æ ¼ä¸è´ï¼æ¥åº 3 å¤éä¸å¡ç¡®è®¤ï¼ |
| | | - ç½çº¦è½¦ 2025-03/04ãå
¶ä¸åå¸å
ãå£å¾ 60 æ ¼ï¼å¦æ 12 æ 12 æ ¼å°å·®ï¼ââ2026-09-21 å·²å®ä½ï¼æ¯çç¨äºå¦ä¸ä»½æ§çåºç§åå¸å
è¾å
¥ï¼ï¼ |
| | | - è´§è¿ å®æ 2025-11 å¨è½¬é 2 æ ¼ï¼å·® 34.30ï¼ï¼ |
| | | - ç线å
车 2025-11 ä¸ªä½æ
客å¨è½¬é 5 æ ¼ã |
| | | å两å¤ä¸º**æºæ°æ®å±æ°åç°**ï¼æ¤åæ ä»»ä½è®°å½ï¼å 为å岿 ¸å¯¹é½å¨åæºç"çæä»¶ vs å®ç¨¿"å±ï¼ã |
| | |
| | | } |
| | | } |
| | | |
| | | /** åªæ ¡éªãå¿
éåæååå¨ãï¼ä¸æ ¡éªåä½ï¼ï¼ç¨äºå
¼å®¹åäºç模æ¿çåä½å·®å¼ */ |
| | | private void requireHeaderColsByName(Sheet sheet, String typeLabel, String templateHint, String[] keywords) { |
| | | Row header = sheet == null ? null : sheet.getRow(0); |
| | | if (header == null) { |
| | | throw new RuntimeException("æéæ°æ®ç±»åã" + typeLabel + "ãæä»¶è¡¨å¤´ç¼ºå¤±ï¼è¯·ä½¿ç¨ç³»ç»æ¨¡æ¿ã" + templateHint + "ãæ´çåå导å
¥"); |
| | | } |
| | | List<String> missing = new ArrayList<>(); |
| | | for (String keyword : keywords) { |
| | | if (findColByKeywords(header, keyword) < 0) missing.add(keyword); |
| | | } |
| | | if (!missing.isEmpty()) { |
| | | throw new RuntimeException("æéæ°æ®ç±»åã" + typeLabel + "ã䏿件å
容ä¸ç¬¦ï¼è¡¨å¤´ç¼ºå°å¿
éåã" |
| | | + String.join("ãã", missing) + "ãï¼è¯·ç¡®è®¤æ¯å¦ééäºæ°æ®ç±»åï¼æ¨¡æ¿ï¼" + templateHint + "ï¼"); |
| | | } |
| | | } |
| | | |
| | | /** 表头å â åå·ï¼é¦æ¬¡åºç°ï¼ï¼æªå½ä¸æ¶åé fallbackï¼ä¿æå¯¹æ§æ¨¡æ¿å
¼å®¹ */ |
| | | private int colByName(Row header, int fallback, String name) { |
| | | return colByName(header, fallback, name, 1); |
| | | } |
| | | |
| | | /** 表头å â åå·ï¼ç¬¬ occurrence 次åºç°ï¼ï¼æªå½ä¸æ¶åé fallback */ |
| | | private int colByName(Row header, int fallback, String name, int occurrence) { |
| | | if (header == null) return fallback; |
| | | int hit = 0; |
| | | for (int c = 0; c <= header.getLastCellNum(); c++) { |
| | | Cell cell = header.getCell(c); |
| | | if (cell == null) continue; |
| | | String v = FORMATTER.formatCellValue(cell).trim(); |
| | | if (v.contains(name)) { |
| | | hit++; |
| | | if (hit == occurrence) return c; |
| | | } |
| | | } |
| | | return fallback; |
| | | } |
| | | |
| | | /** æè¡¨å¤´å
³é®åå®ä½åå·ï¼ä»ç¬¬ 1 åå峿«ï¼å½ä¸ä»»ä¸å
³é®åå³è¿å该åï¼æªå½ä¸è¿å -1 */ |
| | | private int findColByKeywords(Row header, String... keywords) { |
| | | if (header == null) return -1; |
| | |
| | | List<CityBusRow> rows = new ArrayList<>(); |
| | | try (Workbook wb = WorkbookFactory.create(file.getInputStream())) { |
| | | Sheet sheet = wb.getSheetAt(0); |
| | | requireHeaderLayout(sheet, "åå¸å
¬äº¤", "模æ¿_交ä¼ç»U203表_åå¸å
Œ
±äº¤éæåº¦è¿è¥æ
åµï¼2026å¹´7æï¼.xls", |
| | | CITY_BUS_HEADER_COLS, CITY_BUS_HEADER_KEYWORDS); |
| | | Row hdr = sheet.getRow(0); |
| | | requireHeaderColsByName(sheet, "åå¸å
¬äº¤", "模æ¿_交ä¼ç»U203表_åå¸å
Œ
±äº¤éæåº¦è¿è¥æ
åµï¼2026å¹´7æï¼.xls", CITY_BUS_HEADER_KEYWORDS); |
| | | for (int r = 1; r <= sheet.getLastRowNum(); r++) { |
| | | Row row = sheet.getRow(r); |
| | | if (row == null) continue; |
| | | String name = getString(row, 2); |
| | | String name = getString(row, colByName(hdr, 2, "ä¼ä¸åç§°")); |
| | | if (name == null || name.trim().isEmpty()) continue; |
| | | CityBusMonthly item = new CityBusMonthly(); |
| | | item.setReportPeriod(period); |
| | | item.setRegionCode(getString(row, 0)); |
| | | item.setCity(RegionUtil.cityByCode(getString(row, 0))); |
| | | item.setEnterpriseCode(getString(row, 1)); |
| | | item.setRegionCode(getString(row, colByName(hdr, 0, "æå±å°åº"))); |
| | | item.setCity(RegionUtil.cityByCode(getString(row, colByName(hdr, 0, "æå±å°åº")))); |
| | | item.setEnterpriseCode(getString(row, colByName(hdr, 1, "ä¼ä¸ä»£ç "))); |
| | | item.setEnterpriseName(name.trim()); |
| | | item.setOpVehicles(getDecimal(row, 5)); |
| | | item.setStopTotal(getDecimal(row, 6)); |
| | | item.setStopCity(getDecimal(row, 7)); |
| | | item.setPassengerVolume(getDecimal(row, 8)); |
| | | item.setPassengerCity(getDecimal(row, 9)); |
| | | item.setTurnover(getDecimal(row, 10)); |
| | | item.setTurnoverCity(getDecimal(row, 11)); |
| | | item.setRailPassengerVolume(getDecimal(row, 13)); |
| | | item.setRailTurnover(getDecimal(row, 14)); |
| | | item.setFerryPassengerVolume(getDecimal(row, 17)); |
| | | item.setFerryTurnover(getDecimal(row, 18)); |
| | | item.setAvgDistance(getDecimal(row, 19)); |
| | | item.setAvgDistanceCity(getDecimal(row, 20)); |
| | | item.setContactPerson(getString(row, 24)); |
| | | item.setContactPhone(getString(row, 25)); |
| | | item.setReportDate(getDateString(row, 26)); |
| | | item.setRateOpVehiclesMom(getDecimal(row, 29)); |
| | | item.setRatePassengerMom(getDecimal(row, 30)); |
| | | item.setRatePassengerCityMom(getDecimal(row, 31)); |
| | | item.setRateChengxiangMom(getDecimal(row, 32)); |
| | | item.setRateChengxiangTurnoverMom(getDecimal(row, 33)); |
| | | item.setAvgDistanceMomChange(getDecimal(row, 34)); |
| | | item.setAvgDistanceCityMomChange(getDecimal(row, 35)); |
| | | item.setDailyPassengerPerVehicle(getDecimal(row, 36)); |
| | | item.setRateOpVehiclesYoy(getDecimal(row, 39)); |
| | | item.setRatePassengerYoy(getDecimal(row, 40)); |
| | | item.setRatePassengerCityYoy(getDecimal(row, 41)); |
| | | item.setRateChengxiangYoy(getDecimal(row, 42)); |
| | | item.setRateChengxiangTurnoverYoy(getDecimal(row, 43)); |
| | | item.setStopCityRatio(getDecimal(row, 44)); |
| | | item.setPassengerCityRatio(getDecimal(row, 45)); |
| | | item.setChengxiangRatio(getDecimal(row, 46)); |
| | | item.setRateTurnoverYoy(getDecimal(row, 47)); |
| | | item.setRateTurnoverMom(getDecimal(row, 48)); |
| | | item.setPassengerChengxiang(getDecimal(row, 49)); |
| | | item.setTurnoverChengxiang(getDecimal(row, 50)); |
| | | item.setAvgDistanceChengxiang(getDecimal(row, 51)); |
| | | item.setVerifyExplanation(getString(row, 65)); |
| | | item.setReportNote(getString(row, 66)); |
| | | item.setModifyRecord(getString(row, 67)); |
| | | item.setOpVehicles(getDecimal(row, colByName(hdr, 5, "å
Œ
±æ±½çµè½¦_è¿è¥è½¦æ°"))); |
| | | item.setStopTotal(getDecimal(row, colByName(hdr, 6, "è¿è¥ç«ç¹æ»ä¸ªæ°"))); |
| | | item.setStopCity(getDecimal(row, colByName(hdr, 7, "å
¶ä¸ï¼åå¸å
"))); |
| | | item.setPassengerVolume(getDecimal(row, colByName(hdr, 8, "å
Œ
±æ±½çµè½¦_客è¿é"))); |
| | | item.setPassengerCity(getDecimal(row, colByName(hdr, 9, "å
¶ä¸ï¼åå¸å
", 2))); |
| | | item.setTurnover(getDecimal(row, colByName(hdr, 10, "æ°å¢å
Œ
±æ±½çµè½¦æ
客å¨è½¬é"))); |
| | | item.setTurnoverCity(getDecimal(row, colByName(hdr, 11, "æ°å¢å
Œ
±æ±½çµè½¦æ
客å¨è½¬é_å
¶ä¸åå¸å
"))); |
| | | item.setRailPassengerVolume(getDecimal(row, colByName(hdr, 13, "轨é交é_客è¿é"))); |
| | | item.setRailTurnover(getDecimal(row, colByName(hdr, 14, "轨é交é_æ
客å¨è½¬é"))); |
| | | item.setFerryPassengerVolume(getDecimal(row, colByName(hdr, 17, "åå¸å®¢è¿è½®æ¸¡_客è¿é"))); |
| | | item.setFerryTurnover(getDecimal(row, colByName(hdr, 18, "åå¸å®¢è¿è½®æ¸¡æ
客å¨è½¬é"))); |
| | | item.setAvgDistance(getDecimal(row, colByName(hdr, 19, "å
Œ
±æ±½çµè½¦å¹³åè¿è·"))); |
| | | item.setAvgDistanceCity(getDecimal(row, colByName(hdr, 20, "å
Œ
±æ±½çµè½¦åå¸å
å¹³åè¿è·"))); |
| | | item.setContactPerson(getString(row, colByName(hdr, 24, "填表人"))); |
| | | item.setContactPhone(getString(row, colByName(hdr, 25, "èç³»çµè¯"))); |
| | | item.setReportDate(getDateString(row, colByName(hdr, 26, "æ¥åºæ¥æ"))); |
| | | item.setRateOpVehiclesMom(getDecimal(row, colByName(hdr, 29, "å
Œ
±æ±½çµè½¦_è¿è¥è½¦æ°ç¯æ¯å¢é"))); |
| | | item.setRatePassengerMom(getDecimal(row, colByName(hdr, 30, "å
Œ
±æ±½çµè½¦_客è¿éç¯æ¯å¢é"))); |
| | | item.setRatePassengerCityMom(getDecimal(row, colByName(hdr, 31, "åå¸å
客è¿éç¯æ¯"))); |
| | | item.setRateChengxiangMom(getDecimal(row, colByName(hdr, 32, "åé
å乡客è¿éç¯æ¯å¢é"))); |
| | | item.setRateChengxiangTurnoverMom(getDecimal(row, colByName(hdr, 33, "åé
å乡æ
客å¨è½¬éç¯æ¯å¢é"))); |
| | | item.setAvgDistanceMomChange(getDecimal(row, colByName(hdr, 34, "å¹³åè¿è·ç¯æ¯å¢åï¼å
¬éï¼"))); |
| | | item.setAvgDistanceCityMomChange(getDecimal(row, colByName(hdr, 35, "å¹³åè¿è·_åå¸å
ç¯æ¯å¢åï¼å
¬éï¼"))); |
| | | item.setDailyPassengerPerVehicle(getDecimal(row, colByName(hdr, 36, "å
Œ
±æ±½çµè½¦_å车æ¥å客è¿é"))); |
| | | item.setRateOpVehiclesYoy(getDecimal(row, colByName(hdr, 39, "å
Œ
±æ±½çµè½¦_è¿è¥è½¦æ°åæ¯å¢é"))); |
| | | item.setRatePassengerYoy(getDecimal(row, colByName(hdr, 40, "å
Œ
±æ±½çµè½¦_客è¿é忝å¢é"))); |
| | | item.setRatePassengerCityYoy(getDecimal(row, colByName(hdr, 41, "客è¿é_åå¸å
忝å¢é"))); |
| | | item.setRateChengxiangYoy(getDecimal(row, colByName(hdr, 42, "åé
å乡客è¿é忝å¢é"))); |
| | | item.setRateChengxiangTurnoverYoy(getDecimal(row, colByName(hdr, 43, "åé
å乡æ
客å¨è½¬é忝å¢é"))); |
| | | item.setStopCityRatio(getDecimal(row, colByName(hdr, 44, "åå¸å
è¿è¥ç«ç¹å æ¯"))); |
| | | item.setPassengerCityRatio(getDecimal(row, colByName(hdr, 45, "åå¸å
客è¿éå æ¯"))); |
| | | item.setChengxiangRatio(getDecimal(row, colByName(hdr, 46, "åé
å乡客è¿éå æ¯"))); |
| | | item.setRateTurnoverYoy(getDecimal(row, colByName(hdr, 47, "æ
客å¨è½¬é忝å¢é"))); |
| | | item.setRateTurnoverMom(getDecimal(row, colByName(hdr, 48, "æ
客å¨è½¬éç¯æ¯å¢é"))); |
| | | item.setPassengerChengxiang(getDecimal(row, colByName(hdr, 49, "åé
å乡客è¿é"))); |
| | | item.setTurnoverChengxiang(getDecimal(row, colByName(hdr, 50, "åé
å乡æ
客å¨è½¬é"))); |
| | | item.setAvgDistanceChengxiang(getDecimal(row, colByName(hdr, 51, "åé
å乡平åè¿è·"))); |
| | | item.setVerifyExplanation(getString(row, colByName(hdr, 65, "æ ¸å®æ§åå è§£é"))); |
| | | item.setReportNote(getString(row, colByName(hdr, 66, "䏿¥è¯´æ"))); |
| | | item.setModifyRecord(getString(row, colByName(hdr, 67, "ä¿®æ¹è®°å½"))); |
| | | rows.add(new CityBusRow(r + 1, item)); |
| | | } |
| | | } catch (Exception e) { |
| | |
| | | List<TaxiRow> rows = new ArrayList<>(); |
| | | try (Workbook wb = WorkbookFactory.create(file.getInputStream())) { |
| | | Sheet sheet = wb.getSheetAt(0); |
| | | requireHeaderLayout(sheet, "巡游åºç§", "模æ¿_交è¡ç»U402表-巡游åºç§æ±½è½¦è¿è¥æå¡æ
嵿æ¥(2026).xls", |
| | | CITY_TAXI_HEADER_COLS, CITY_TAXI_HEADER_KEYWORDS); |
| | | Row hdr = sheet.getRow(0); |
| | | requireHeaderColsByName(sheet, "巡游åºç§", "模æ¿_交è¡ç»U402表-巡游åºç§æ±½è½¦è¿è¥æå¡æ
嵿æ¥(2026).xls", CITY_TAXI_HEADER_KEYWORDS); |
| | | for (int r = 1; r <= sheet.getLastRowNum(); r++) { |
| | | Row row = sheet.getRow(r); |
| | | if (row == null) continue; |
| | | String name = getString(row, 2); |
| | | String name = getString(row, colByName(hdr, 2, "ä¼ä¸åç§°")); |
| | | if (name == null || name.trim().isEmpty()) continue; |
| | | CityTaxiMonthly item = new CityTaxiMonthly(); |
| | | item.setReportPeriod(period); |
| | | item.setRegionCode(getString(row, 0)); |
| | | item.setCity(cityOf(getString(row, 0), name)); |
| | | item.setTripTotal(getDecimal(row, 4)); |
| | | item.setTripCity(getDecimal(row, 5)); |
| | | item.setPassengerVolume(getDecimal(row, 6)); |
| | | item.setPassengerCity(getDecimal(row, 7)); |
| | | item.setTurnover(getDecimal(row, 8)); |
| | | item.setTurnoverCity(getDecimal(row, 9)); |
| | | item.setOpVehicles(getDecimal(row, 10)); |
| | | item.setAvgDistance(getDecimal(row, 11)); |
| | | item.setAvgDistanceCity(getDecimal(row, 12)); |
| | | item.setContactPerson(getString(row, 15)); |
| | | item.setContactPhone(getString(row, 16)); |
| | | item.setReportDate(getDateString(row, 17)); |
| | | item.setDailyTripsPerVehicle(getDecimal(row, 18)); |
| | | item.setDailyPassengerPerVehicle(getDecimal(row, 19)); |
| | | item.setPassengersPerTrip(getDecimal(row, 20)); |
| | | item.setRateOpVehiclesMom(getDecimal(row, 21)); |
| | | item.setRateOpVehiclesYoy(getDecimal(row, 22)); |
| | | item.setRateTripMom(getDecimal(row, 23)); |
| | | item.setRateTripYoy(getDecimal(row, 24)); |
| | | item.setRatePassengerMom(getDecimal(row, 25)); |
| | | item.setRatePassengerYoy(getDecimal(row, 26)); |
| | | item.setAvgDistanceMomChange(getDecimal(row, 27)); |
| | | item.setPassengersPerTripCity(getDecimal(row, 28)); |
| | | item.setRateTripCityMom(getDecimal(row, 29)); |
| | | item.setRatePassengerCityMom(getDecimal(row, 30)); |
| | | item.setAvgDistanceCityMomChange(getDecimal(row, 31)); |
| | | item.setTripCityRatio(getDecimal(row, 32)); |
| | | item.setPassengerCityRatio(getDecimal(row, 33)); |
| | | item.setPassengersPerTripChengxiang(getDecimal(row, 34)); |
| | | item.setRateTripChengxiangMom(getDecimal(row, 35)); |
| | | item.setRatePassengerChengxiangMom(getDecimal(row, 36)); |
| | | item.setAvgDistanceChengxiang(getDecimal(row, 37)); |
| | | item.setVerifyExplanation(getString(row, 38)); |
| | | item.setReportNote(getString(row, 39)); |
| | | item.setModifyRecord(getString(row, 40)); |
| | | item.setRegionCode(getString(row, colByName(hdr, 0, "æå±å°åº"))); |
| | | item.setCity(cityOf(getString(row, colByName(hdr, 0, "æå±å°åº")), name)); |
| | | item.setTripTotal(getDecimal(row, colByName(hdr, 4, "è½½å®¢è½¦æ¬¡æ»æ°"))); |
| | | item.setTripCity(getDecimal(row, colByName(hdr, 5, "è½½å®¢è½¦æ¬¡æ»æ°_åå¸å
"))); |
| | | item.setPassengerVolume(getDecimal(row, colByName(hdr, 6, "客è¿é"))); |
| | | item.setPassengerCity(getDecimal(row, colByName(hdr, 7, "客è¿é_å
¶ä¸åå¸å
"))); |
| | | item.setTurnover(getDecimal(row, colByName(hdr, 8, "æ
客å¨è½¬é"))); |
| | | item.setTurnoverCity(getDecimal(row, colByName(hdr, 9, "æ
客å¨è½¬é_åå¸å
"))); |
| | | item.setOpVehicles(getDecimal(row, colByName(hdr, 10, "è¿è¥è½¦è¾æ°"))); |
| | | item.setAvgDistance(getDecimal(row, colByName(hdr, 11, "巡游åºç§æ±½è½¦å¹³åè¿è·"))); |
| | | item.setAvgDistanceCity(getDecimal(row, colByName(hdr, 12, "åå¸å
å¹³åè¿è·"))); |
| | | item.setContactPerson(getString(row, colByName(hdr, 15, "填表人"))); |
| | | item.setContactPhone(getString(row, colByName(hdr, 16, "èç³»çµè¯"))); |
| | | item.setReportDate(getDateString(row, colByName(hdr, 17, "æ¥åºæ¥æ"))); |
| | | item.setDailyTripsPerVehicle(getDecimal(row, colByName(hdr, 18, "å车æ¥å载客车次"))); |
| | | item.setDailyPassengerPerVehicle(getDecimal(row, colByName(hdr, 19, "å车æ¥å客è¿é"))); |
| | | item.setPassengersPerTrip(getDecimal(row, colByName(hdr, 20, "å车次载客人æ°"))); |
| | | item.setRateOpVehiclesMom(getDecimal(row, colByName(hdr, 21, "è¿è¥è½¦æ°ç¯æ¯å¢é"))); |
| | | item.setRateOpVehiclesYoy(getDecimal(row, colByName(hdr, 22, "è¿è¥è½¦æ°åæ¯å¢é"))); |
| | | item.setRateTripMom(getDecimal(row, colByName(hdr, 23, "è½½å®¢è½¦æ¬¡æ»æ°ç¯æ¯å¢é"))); |
| | | item.setRateTripYoy(getDecimal(row, colByName(hdr, 24, "è½½å®¢è½¦æ¬¡åæ¯å¢é"))); |
| | | item.setRatePassengerMom(getDecimal(row, colByName(hdr, 25, "客è¿éç¯æ¯å¢é"))); |
| | | item.setRatePassengerYoy(getDecimal(row, colByName(hdr, 26, "客è¿é忝å¢é"))); |
| | | item.setAvgDistanceMomChange(getDecimal(row, colByName(hdr, 27, "å¹³åè¿è·ç¯æ¯å¢å"))); |
| | | item.setPassengersPerTripCity(getDecimal(row, colByName(hdr, 28, "å车次载客人æ°_åå¸å
"))); |
| | | item.setRateTripCityMom(getDecimal(row, colByName(hdr, 29, "è½½å®¢è½¦æ¬¡æ»æ°ç¯æ¯å¢é_åå¸å
"))); |
| | | item.setRatePassengerCityMom(getDecimal(row, colByName(hdr, 30, "客è¿éç¯æ¯å¢é_åå¸å
"))); |
| | | item.setAvgDistanceCityMomChange(getDecimal(row, colByName(hdr, 31, "å¹³åè¿è·_åå¸å
ç¯æ¯å¢å"))); |
| | | item.setTripCityRatio(getDecimal(row, colByName(hdr, 32, "è½½å®¢è½¦æ¬¡æ»æ°_åå¸å
å æ¯"))); |
| | | item.setPassengerCityRatio(getDecimal(row, colByName(hdr, 33, "客è¿é_åå¸å
å æ¯"))); |
| | | item.setPassengersPerTripChengxiang(getDecimal(row, colByName(hdr, 34, "å车次载客人æ°_åé
å乡"))); |
| | | item.setRateTripChengxiangMom(getDecimal(row, colByName(hdr, 35, "è½½å®¢è½¦æ¬¡æ»æ°_åé
åä¹¡ç¯æ¯å¢é"))); |
| | | item.setRatePassengerChengxiangMom(getDecimal(row, colByName(hdr, 36, "客è¿é_åé
åä¹¡ç¯æ¯å¢é"))); |
| | | item.setAvgDistanceChengxiang(getDecimal(row, colByName(hdr, 37, "å¹³åè¿è·_åé
å乡"))); |
| | | item.setVerifyExplanation(getString(row, colByName(hdr, 38, "æ ¸å®æ§åå è§£é"))); |
| | | item.setReportNote(getString(row, colByName(hdr, 39, "䏿¥è¯´æ"))); |
| | | item.setModifyRecord(getString(row, colByName(hdr, 40, "ä¿®æ¹è®°å½"))); |
| | | rows.add(new TaxiRow(r + 1, item)); |
| | | } |
| | | } catch (Exception e) { |
| | |
| | | private static final Color AXIS_COLOR = Color.BLACK; |
| | | private static final Color MUTED_TEXT_COLOR = new Color(102, 102, 102); |
| | | private static final double UNIT_LABEL_BAND = 58.0; |
| | | private static final double RIGHT_LABEL_BAND = 74.0; |
| | | private static final double RIGHT_LABEL_BAND = 140.0; |
| | | private static final double ITEM_LABEL_GAP = 10.0; |
| | | private static final float HALO_OUTLINE_WIDTH = 4.0f; |
| | | private static final int PERCENT_ITEM_LABEL_DECIMALS = 1; |
| | |
| | | |
| | | public byte[] lineChart(String yAxisLabel, List<ChartSeries> series, boolean percentAxis, |
| | | int valueDecimals, int width, int height) { |
| | | return lineChart(yAxisLabel, series, percentAxis, valueDecimals, DAY_LABELS, width, height); |
| | | } |
| | | |
| | | public byte[] lineChart(String yAxisLabel, List<ChartSeries> series, boolean percentAxis, |
| | | int valueDecimals, List<String> dayLabels, int width, int height) { |
| | | return lineChart(yAxisLabel, series, percentAxis, valueDecimals, dayLabels, width, height, |
| | | Collections.emptyList()); |
| | | } |
| | | |
| | | /** |
| | | * Renders a line chart and pins the listed labels to a fixed anchor. The overrides are |
| | | * opt-in: a chart that passes an empty list keeps the automatic placement unchanged. |
| | | */ |
| | | public byte[] lineChart(String yAxisLabel, List<ChartSeries> series, boolean percentAxis, |
| | | int valueDecimals, List<String> dayLabels, int width, int height, |
| | | List<LineLabelOverride> labelOverrides) { |
| | | List<String> axisLabels = (dayLabels == null || dayLabels.isEmpty()) ? DAY_LABELS : dayLabels; |
| | | XYSeriesCollection dataset = new XYSeriesCollection(); |
| | | for (int seriesIndex = 0; seriesIndex < series.size(); seriesIndex++) { |
| | | ChartSeries item = series.get(seriesIndex); |
| | |
| | | plot.setRangeGridlinesVisible(false); |
| | | plot.setDomainTickBandPaint(null); |
| | | plot.setRangeTickBandPaint(null); |
| | | SymbolAxis domainAxis = new SymbolAxis(null, DAY_LABELS.toArray(new String[0])); |
| | | SymbolAxis domainAxis = new SymbolAxis(null, axisLabels.toArray(new String[0])); |
| | | domainAxis.setGridBandPaint(new Color(0, 0, 0, 0)); |
| | | domainAxis.setGridBandAlternatePaint(new Color(0, 0, 0, 0)); |
| | | domainAxis.setLowerMargin(0.04); |
| | |
| | | } |
| | | plot.setRenderer(renderer); |
| | | return render(chart, width, scaleLineChartHeight(height), |
| | | yAxisLabel, true, percentAxis, valueDecimals); |
| | | yAxisLabel, true, percentAxis, valueDecimals, labelOverrides); |
| | | } |
| | | |
| | | public byte[] pieChart(String valueSuffix, List<ChartCategory> categories, |
| | |
| | | |
| | | private byte[] render(JFreeChart chart, int width, int height, String unitLabel, |
| | | boolean axisChart, boolean percentItemLabels, int valueDecimals) { |
| | | return render(chart, width, height, unitLabel, axisChart, percentItemLabels, |
| | | valueDecimals, Collections.emptyList()); |
| | | } |
| | | |
| | | private byte[] render(JFreeChart chart, int width, int height, String unitLabel, |
| | | boolean axisChart, boolean percentItemLabels, int valueDecimals, |
| | | List<LineLabelOverride> labelOverrides) { |
| | | try { |
| | | reserveUnitLabelBand(chart, axisChart, unitLabel); |
| | | ByteArrayOutputStream out = new ByteArrayOutputStream(); |
| | |
| | | drawDateLabel(graphics, info.getPlotInfo().getDataArea()); |
| | | } |
| | | drawXYItemLabels(graphics, image, chart, |
| | | info.getPlotInfo().getDataArea(), percentItemLabels, unitLabel, valueDecimals); |
| | | info.getPlotInfo().getDataArea(), percentItemLabels, unitLabel, valueDecimals, |
| | | labelOverrides); |
| | | drawStackedBarLabels(graphics, image, chart, |
| | | info.getPlotInfo().getDataArea(), percentItemLabels); |
| | | } finally { |
| | |
| | | occupied.add(new Rectangle2D.Double(textX, baseline - metrics.getAscent(), |
| | | width, metrics.getHeight())); |
| | | } else { |
| | | horizontalCallouts.add(new HorizontalStackCallout(Math.min(startX, endX), |
| | | horizontalCallouts.add(new HorizontalStackCallout(row, Math.min(startX, endX), |
| | | Math.max(startX, endX), centerY, text, seriesColor(row))); |
| | | } |
| | | } else { |
| | |
| | | } |
| | | } |
| | | if (horizontal) { |
| | | drawHorizontalStackCallouts(graphics, horizontalCallouts, dataArea, metrics, occupied); |
| | | drawHorizontalStackCallouts(graphics, horizontalCallouts, dataArea, metrics, occupied, |
| | | rows >= 3); |
| | | } else { |
| | | for (int column = 0; column < columns; column++) { |
| | | drawStackCalloutColumn(graphics, image, callouts.get(column), column, columns, |
| | |
| | | private void drawHorizontalStackCallouts(Graphics2D graphics, |
| | | List<HorizontalStackCallout> items, |
| | | Rectangle2D dataArea, FontMetrics metrics, |
| | | List<Rectangle2D> occupied) { |
| | | List<Rectangle2D> occupied, |
| | | boolean packIntoColumns) { |
| | | if (items.isEmpty()) return; |
| | | Map<Long, List<HorizontalStackCallout>> rows = new LinkedHashMap<>(); |
| | | for (HorizontalStackCallout item : items) { |
| | |
| | | Collections.sort(row, new Comparator<HorizontalStackCallout>() { |
| | | @Override |
| | | public int compare(HorizontalStackCallout first, HorizontalStackCallout second) { |
| | | return Double.compare(first.getStartX(), second.getStartX()); |
| | | return Integer.compare(first.getSeriesIndex(), second.getSeriesIndex()); |
| | | } |
| | | }); |
| | | int widest = 0; |
| | | double columnX = 0.0; |
| | | for (HorizontalStackCallout item : row) { |
| | | widest = Math.max(widest, metrics.stringWidth(item.getText())); |
| | | columnX = Math.max(columnX, item.getEndX() + 8.0); |
| | | if (!packIntoColumns) { |
| | | int widest = 0; |
| | | double columnX = 0.0; |
| | | for (HorizontalStackCallout item : row) { |
| | | widest = Math.max(widest, metrics.stringWidth(item.getText())); |
| | | columnX = Math.max(columnX, item.getEndX() + 8.0); |
| | | } |
| | | // Keep the label column inside the reserved right margin. |
| | | columnX = Math.min(columnX, dataArea.getMaxX() + RIGHT_LABEL_BAND - widest); |
| | | int height = metrics.getHeight(); |
| | | double step = height + 3.0; |
| | | double centerY = row.get(0).getY(); |
| | | double top = centerY - (step * row.size()) / 2.0 + (step - height) / 2.0; |
| | | for (int i = 0; i < row.size(); i++) { |
| | | HorizontalStackCallout item = row.get(i); |
| | | int width = metrics.stringWidth(item.getText()); |
| | | double x = columnX; |
| | | double y = top + i * step; |
| | | graphics.setColor(item.getColor()); |
| | | graphics.setStroke(new BasicStroke(1.4f, BasicStroke.CAP_ROUND, |
| | | BasicStroke.JOIN_ROUND)); |
| | | graphics.drawLine((int) Math.round(item.getEndX()), (int) Math.round(item.getY()), |
| | | (int) Math.round(x - 3.0), (int) Math.round(y + height / 2.0)); |
| | | drawLabelWithHalo(graphics, item.getText(), |
| | | new Rectangle2D.Double(x, y, width, height), metrics, item.getColor()); |
| | | occupied.add(new Rectangle2D.Double(x - 2.0, y - 2.0, |
| | | width + 4.0, height + 4.0)); |
| | | } |
| | | continue; |
| | | } |
| | | // Keep the label column inside the reserved right margin. |
| | | columnX = Math.min(columnX, dataArea.getMaxX() + RIGHT_LABEL_BAND - widest); |
| | | // Small slices are labelled outside the bar. Packing those labels |
| | | // into two columns keeps each day's callouts inside its own row. |
| | | int columnCount = row.size() <= 1 ? 1 : 2; |
| | | int rowCount = (int) Math.ceil(row.size() / (double) columnCount); |
| | | double[] columnWidths = new double[columnCount]; |
| | | for (int i = 0; i < row.size(); i++) { |
| | | int column = columnCount == 1 ? 0 : i % columnCount; |
| | | columnWidths[column] = Math.max(columnWidths[column], |
| | | metrics.stringWidth(row.get(i).getText())); |
| | | } |
| | | double columnGap = 10.0; |
| | | double blockWidth = columnGap * (columnCount - 1); |
| | | for (double columnWidth : columnWidths) blockWidth += columnWidth; |
| | | double blockLeft = dataArea.getMaxX() + 8.0; |
| | | double blockRight = dataArea.getMaxX() + RIGHT_LABEL_BAND; |
| | | blockLeft += Math.max(0.0, (blockRight - blockLeft - blockWidth) / 2.0); |
| | | double[] columnCenters = new double[columnCount]; |
| | | double cursor = blockLeft; |
| | | for (int column = 0; column < columnCount; column++) { |
| | | columnCenters[column] = cursor + columnWidths[column] / 2.0; |
| | | cursor += columnWidths[column] + columnGap; |
| | | } |
| | | int height = metrics.getHeight(); |
| | | double step = height + 3.0; |
| | | double centerY = row.get(0).getY(); |
| | | double top = centerY - (step * row.size()) / 2.0 + (step - height) / 2.0; |
| | | double rowStep = height + 3.0; |
| | | double totalHeight = rowCount * height + (rowCount - 1) * (rowStep - height); |
| | | double top = row.get(0).getY() - totalHeight / 2.0; |
| | | for (int i = 0; i < row.size(); i++) { |
| | | HorizontalStackCallout item = row.get(i); |
| | | int column = columnCount == 1 ? 0 : i % columnCount; |
| | | int rowIndex = columnCount == 1 ? i : i / columnCount; |
| | | int itemsInRow = Math.min(columnCount, row.size() - rowIndex * columnCount); |
| | | int width = metrics.stringWidth(item.getText()); |
| | | double x = columnX; |
| | | double y = top + i * step; |
| | | graphics.setColor(item.getColor()); |
| | | graphics.setStroke(new BasicStroke(1.4f, BasicStroke.CAP_ROUND, |
| | | BasicStroke.JOIN_ROUND)); |
| | | graphics.drawLine((int) Math.round(item.getEndX()), (int) Math.round(item.getY()), |
| | | (int) Math.round(x - 3.0), (int) Math.round(y + height / 2.0)); |
| | | double centerX = itemsInRow == 1 && columnCount > 1 |
| | | ? blockLeft + blockWidth / 2.0 |
| | | : columnCenters[column]; |
| | | double x = centerX - width / 2.0; |
| | | double y = top + rowIndex * rowStep; |
| | | drawLabelWithHalo(graphics, item.getText(), |
| | | new Rectangle2D.Double(x, y, width, height), metrics, item.getColor()); |
| | | occupied.add(new Rectangle2D.Double(x - 2.0, y - 2.0, |
| | | width + 4.0, height + 4.0)); |
| | | } |
| | | |
| | | } |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | private static class HorizontalStackCallout { |
| | | private final int seriesIndex; |
| | | private final double startX; |
| | | private final double endX; |
| | | private final double y; |
| | | private final String text; |
| | | private final Color color; |
| | | |
| | | private HorizontalStackCallout(double startX, double endX, double y, |
| | | private HorizontalStackCallout(int seriesIndex, double startX, double endX, double y, |
| | | String text, Color color) { |
| | | this.seriesIndex = seriesIndex; |
| | | this.startX = startX; |
| | | this.endX = endX; |
| | | this.y = y; |
| | |
| | | this.color = color; |
| | | } |
| | | |
| | | private int getSeriesIndex() { return seriesIndex; } |
| | | private double getStartX() { return startX; } |
| | | private double getEndX() { return endX; } |
| | | private double getY() { return y; } |
| | |
| | | |
| | | private void drawXYItemLabels(Graphics2D graphics, BufferedImage image, JFreeChart chart, |
| | | Rectangle2D dataArea, boolean percentItemLabels, |
| | | String unitLabel, int valueDecimals) { |
| | | String unitLabel, int valueDecimals, |
| | | List<LineLabelOverride> labelOverrides) { |
| | | if (!(chart.getPlot() instanceof XYPlot) || dataArea == null |
| | | || dataArea.getWidth() <= 0 || dataArea.getHeight() <= 0) { |
| | | return; |
| | |
| | | List<List<LineLabelCandidate>> candidateSets = new ArrayList<>(); |
| | | for (DayLabel label : labels) { |
| | | List<LineLabelCandidate> candidates = lineLabelCandidates( |
| | | label, metrics, dataArea); |
| | | label, metrics, dataArea, |
| | | forcedAnchor(labelOverrides, label.getSeriesIndex(), itemIndex)); |
| | | if (candidates.isEmpty()) { |
| | | candidateSets.clear(); |
| | | break; |
| | |
| | | } |
| | | |
| | | private List<LineLabelCandidate> lineLabelCandidates(DayLabel label, FontMetrics metrics, |
| | | Rectangle2D dataArea) { |
| | | Rectangle2D dataArea, |
| | | LineLabelAnchor forcedAnchor) { |
| | | List<LineLabelCandidate> result = new ArrayList<>(); |
| | | int width = metrics.stringWidth(label.getText()); |
| | | int height = metrics.getHeight(); |
| | |
| | | double farHorizontalOffset = horizontalOffset + 18.0; |
| | | double verticalOffset = ITEM_LABEL_GAP + height / 2.0; |
| | | double farVerticalOffset = verticalOffset + height + 6.0; |
| | | if (forcedAnchor != null) { |
| | | switch (forcedAnchor) { |
| | | case ABOVE: |
| | | addLabelCandidate(result, x, y, width, height, 0.0, -verticalOffset, dataArea); |
| | | break; |
| | | case BELOW: |
| | | addLabelCandidate(result, x, y, width, height, 0.0, verticalOffset, dataArea); |
| | | break; |
| | | case LEFT: |
| | | addLabelCandidate(result, x, y, width, height, -horizontalOffset, 0.0, dataArea); |
| | | break; |
| | | case RIGHT: |
| | | addLabelCandidate(result, x, y, width, height, horizontalOffset, 0.0, dataArea); |
| | | break; |
| | | default: |
| | | break; |
| | | } |
| | | return result; |
| | | } |
| | | addLabelCandidate(result, x, y, width, height, 0.0, -verticalOffset, dataArea); |
| | | addLabelCandidate(result, x, y, width, height, 0.0, verticalOffset, dataArea); |
| | | addLabelCandidate(result, x, y, width, height, horizontalOffset, 0.0, dataArea); |
| | |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * Fixed positions a data label can be pinned to, relative to its own point. |
| | | */ |
| | | public enum LineLabelAnchor { |
| | | ABOVE, BELOW, LEFT, RIGHT |
| | | } |
| | | |
| | | /** |
| | | * Opt-in request that pins one data label (series + item) to a fixed anchor. |
| | | */ |
| | | public static class LineLabelOverride { |
| | | private final int seriesIndex; |
| | | private final int itemIndex; |
| | | private final LineLabelAnchor anchor; |
| | | |
| | | public LineLabelOverride(int seriesIndex, int itemIndex, LineLabelAnchor anchor) { |
| | | this.seriesIndex = seriesIndex; |
| | | this.itemIndex = itemIndex; |
| | | this.anchor = anchor; |
| | | } |
| | | |
| | | public int getSeriesIndex() { return seriesIndex; } |
| | | public int getItemIndex() { return itemIndex; } |
| | | public LineLabelAnchor getAnchor() { return anchor; } |
| | | } |
| | | |
| | | private static LineLabelAnchor forcedAnchor(List<LineLabelOverride> labelOverrides, |
| | | int seriesIndex, int itemIndex) { |
| | | if (labelOverrides == null) return null; |
| | | for (LineLabelOverride override : labelOverrides) { |
| | | if (override.getSeriesIndex() == seriesIndex && override.getItemIndex() == itemIndex) { |
| | | return override.getAnchor(); |
| | | } |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | private void addLabelCandidate(List<LineLabelCandidate> result, double x, double y, |
| | | int width, int height, double dx, double dy, |
| | | Rectangle2D dataArea) { |
| | |
| | | import java.io.InputStream; |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDate; |
| | | import java.time.format.DateTimeFormatter; |
| | | import java.util.ArrayList; |
| | | import java.util.Arrays; |
| | | import java.util.Collections; |
| | |
| | | blocks.add(new HolidayTrendWordService.ChartBlock( |
| | | "â" + holidayLabel + "âåæé路客è¿éæ
åµ", |
| | | chartService.lineChart("ä¸äººæ¬¡", |
| | | series(years, yearData, item -> item.rail), false, 1100, 445), |
| | | // Pin the 2026 rail label for day 3 above its point so the |
| | | // 2024 label (70.6) no longer covers it; other charts keep |
| | | // automatic placement. |
| | | series(years, yearData, item -> item.rail), false, 2, |
| | | HolidayTrendChartService.DAY_LABELS, 1100, 445, |
| | | Collections.singletonList(new HolidayTrendChartService.LineLabelOverride( |
| | | 0, 2, HolidayTrendChartService.LineLabelAnchor.ABOVE))), |
| | | lineImageSize(5264785L, 2129155L))); |
| | | blocks.add(new HolidayTrendWordService.ChartBlock( |
| | | "â" + holidayLabel + "âåææ°´è·¯å®¢è¿éæ
åµ", |
| | |
| | | blocks.add(new HolidayTrendWordService.ChartBlock( |
| | | prefix + "æ®éå½çéè§æµç«å¹³åæºå¨è½¦é度", |
| | | chartService.lineChart("å
¬é/å°æ¶", |
| | | Collections.singletonList(series("å¹³åé度", observation.speed)), false, 1100, 590), |
| | | Collections.singletonList(series("å¹³åé度", observation.speed)), false, 2, |
| | | observation.labels, 1100, 590), |
| | | lineImageSize(5268595L, 2821305L))); |
| | | blocks.add(new HolidayTrendWordService.ChartBlock( |
| | | prefix + "æ®éå½çéè§æµç«å¹³åæ¥æ¤åº¦", |
| | | chartService.lineChart("æ¥æ¤åº¦", |
| | | Collections.singletonList(series("平忥æ¤åº¦", observation.congestion)), false, 1100, 590), |
| | | Collections.singletonList(series("平忥æ¤åº¦", observation.congestion)), false, 2, |
| | | observation.labels, 1100, 590), |
| | | lineImageSize(5268595L, 2821305L))); |
| | | |
| | | File template = resolveTemplateFile(); |
| | |
| | | private ObservationData observationData(int year, String holidayType) { |
| | | List<HolidayCalendar> days = configService.findHolidayDays(year, holidayType); |
| | | if (days.isEmpty()) { |
| | | return new ObservationData(nulls(), nulls()); |
| | | return new ObservationData(nulls(), nulls(), HolidayTrendChartService.DAY_LABELS); |
| | | } |
| | | LocalDate start = days.get(0).getHolidayDate(); |
| | | LocalDate end = start.plusDays(HolidayTrendChartService.DAY_LABELS.size() - 1L); |
| | | LocalDate end = days.get(days.size() - 1).getHolidayDate(); |
| | | List<ObservationDailyOperation> rows = observationStationFlowMapper.selectDailyOperations(start, end); |
| | | Map<LocalDate, ObservationDailyOperation> byDate = new LinkedHashMap<>(); |
| | | for (ObservationDailyOperation row : rows) { |
| | | byDate.put(row.getObsDate(), row); |
| | | } |
| | | DateTimeFormatter labelFormat = DateTimeFormatter.ofPattern("Mædæ¥"); |
| | | List<BigDecimal> speed = new ArrayList<>(); |
| | | List<BigDecimal> congestion = new ArrayList<>(); |
| | | for (int i = 0; i < HolidayTrendChartService.DAY_LABELS.size(); i++) { |
| | | ObservationDailyOperation row = byDate.get(start.plusDays(i)); |
| | | List<String> labels = new ArrayList<>(); |
| | | for (HolidayCalendar day : days) { |
| | | LocalDate date = day.getHolidayDate(); |
| | | ObservationDailyOperation row = byDate.get(date); |
| | | speed.add(row == null ? null : row.getAverageSpeed()); |
| | | congestion.add(row == null ? null : row.getAverageCongestion()); |
| | | labels.add(date.format(labelFormat)); |
| | | } |
| | | return new ObservationData(speed, congestion); |
| | | return new ObservationData(speed, congestion, labels); |
| | | } |
| | | |
| | | private HolidayTrendChartService.ChartSeries series(String name, List<BigDecimal> values) { |
| | |
| | | private static class ObservationData { |
| | | private final List<BigDecimal> speed; |
| | | private final List<BigDecimal> congestion; |
| | | private final List<String> labels; |
| | | |
| | | private ObservationData(List<BigDecimal> speed, List<BigDecimal> congestion) { |
| | | private ObservationData(List<BigDecimal> speed, List<BigDecimal> congestion, List<String> labels) { |
| | | this.speed = speed; |
| | | this.congestion = congestion; |
| | | this.labels = labels; |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | package com.trafficaudit.reportexport.calc; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.trafficaudit.common.util.RegionUtil; |
| | | import com.trafficaudit.dataimport.entity.FreightTurnoverImport; |
| | | import com.trafficaudit.dataimport.entity.H2032EnterpriseMonthly; |
| | | import com.trafficaudit.dataimport.entity.ScaleSplitTransport; |
| | | import com.trafficaudit.dataimport.mapper.FreightTurnoverImportMapper; |
| | | import com.trafficaudit.dataimport.mapper.H2032EnterpriseMonthlyMapper; |
| | | import com.trafficaudit.dataimport.mapper.ScaleSplitTransportMapper; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import javax.annotation.Resource; |
| | | import java.util.ArrayList; |
| | | import java.util.HashMap; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * è´§è¿å£å¾ã导åºååºæ°ä½æ£ãï¼B4ï¼2026-10-12 æ°å¢ 6 æ¡è§åï¼ã |
| | | * |
| | | * <p>èæ¯ï¼è§ä¸ = å
¨å£å¾ï¼â¡ãè´§è¿éå¨è½¬é表ãï¼â è§ä¸ï¼â H203-2 ä¼ä¸ææ¥Ã·10000ï¼ââæ¯**åå**ï¼ |
| | | * å æ¤â æ¼æ¥/缺æ¥ä¼è®©è§ä¸èé«ï¼æç«¯æ
åµè¿ä¼ç®åºè´æ°ãæ¬ç±»æè¿äºå¼å¸¸å¨å¯¼åºåæåºæ¥ï¼ |
| | | * 便¥å¿/å¼¹çª/ãæ°æ®ç¼ºå¤±æ¸
åã页使ç¨ï¼åä¸å¥ç»æä¹ä¾çæä¸»é¾ç dbProblems å¤ç¨ï¼ã |
| | | * |
| | | * <p>è§åï¼ |
| | | * <ol> |
| | | * <li>æå¸å·è§ä¸ä¸ºè´ï¼è´§éæå¨è½¬éï¼ï¼</li> |
| | | * <li>â¡è¯¥å¸å·æå
¨å£å¾ãâ æ è§ä¸è®°å½ â è§ä¸çäºå
¨å£å¾ï¼ææ¾èé«ï¼</li> |
| | | * <li>â¡å
¨å£å¾å°äºâ è§ä¸ï¼é»è¾ä¸å¯è½ï¼ï¼</li> |
| | | * <li>Σ17 å¸å· â â¡ç"æ¹åç"è¡ï¼è¶
容差 0.01ï¼ï¼</li> |
| | | * <li>æå¸å·å
¨å£å¾ãæ¬æä¸ä¸æå®å
¨ç¸åãï¼çéå¤å¯¼å
¥ï¼ä¸¤ä¾§ ⥠50 ä¸å¨/ä¸å¨å
¬éï¼ï¼</li> |
| | | * <li>æå¸å·å
¨å£å¾åæ¯å¼å¸¸ï¼|å»å¹´åæâæ¬æ| > 50% ä¸ä¸¤ä¾§ ⥠50 ä¸å¨/ä¸å¨å
¬éï¼ï¼</li> |
| | | * <li>累计å¾ç¨½ï¼â å¨è½¬é 1..M ææ±å ÷10000 ä¸â¢ãæå表ãCUMULATIVE.above_scale_turnover ä¸ä¸è´ï¼è¶
容差 0.01ï¼ã</li> |
| | | * </ol> |
| | | */ |
| | | @Slf4j |
| | | @Service |
| | | public class FreightReadinessCheck { |
| | | |
| | | private static final double TOL = 0.01; |
| | | private static final double YOY_RATIO = 0.50; |
| | | /** ç¯æ¯æ£æ¥çç»å¯¹é¨æ§ï¼ä¸¤ä¾§é½è¦ â¥ è¯¥å¼æå¤å¼å¸¸ï¼é¿å
ç¥åæ¶æåºè¿ç±»å°åºæ°åªå£°ï¼ */ |
| | | private static final double CHAIN_FLOOR = 50.0; |
| | | |
| | | @Resource |
| | | private H2032EnterpriseMonthlyMapper h2032Mapper; |
| | | @Resource |
| | | private FreightTurnoverImportMapper freightTurnoverMapper; |
| | | @Resource |
| | | private ScaleSplitTransportMapper scaleSplitMapper; |
| | | |
| | | public List<String> check(String period) { |
| | | List<String> issues = new ArrayList<>(); |
| | | if (period == null || period.length() < 7) return issues; |
| | | int year = Integer.parseInt(period.substring(0, 4)); |
| | | int month = Integer.parseInt(period.substring(5, 7)); |
| | | |
| | | // â è§ä¸ï¼å¨ï¼æå¸å·+æä»½ |
| | | Map<String, Map<Integer, Double>> aboveFreight = new HashMap<>(); |
| | | Map<String, Map<Integer, Double>> aboveTurnover = new HashMap<>(); |
| | | for (H2032EnterpriseMonthly rec : h2032Mapper.selectList( |
| | | new LambdaQueryWrapper<H2032EnterpriseMonthly>() |
| | | .likeRight(H2032EnterpriseMonthly::getReportPeriod, year))) { |
| | | String rp = rec.getReportPeriod(); |
| | | if (rp == null || rp.compareTo(period) > 0) continue; |
| | | String city = RegionUtil.cityByCode(rec.getRegionCode()); |
| | | if (city == null) continue; |
| | | int m = Integer.parseInt(rp.substring(5, 7)); |
| | | aboveFreight.computeIfAbsent(city, k -> new HashMap<>()) |
| | | .merge(m, rec.getFreightTotal() == null ? 0.0 : rec.getFreightTotal(), Double::sum); |
| | | aboveTurnover.computeIfAbsent(city, k -> new HashMap<>()) |
| | | .merge(m, rec.getTurnoverTotal() == null ? 0.0 : rec.getTurnoverTotal(), Double::sum); |
| | | } |
| | | |
| | | // â¡ å
¨å£å¾ï¼å¸å·è¡ + æ¹åçè¡ï¼å½æï¼ |
| | | Map<String, FreightTurnoverImport> ft = new HashMap<>(); |
| | | FreightTurnoverImport province = null; |
| | | for (FreightTurnoverImport e : freightTurnoverMapper.selectList( |
| | | new LambdaQueryWrapper<FreightTurnoverImport>() |
| | | .eq(FreightTurnoverImport::getReportPeriod, period))) { |
| | | String city = RegionUtil.normalizeCityName(e.getRegionName()); |
| | | if (city == null) continue; |
| | | if (FreightCalc.PROVINCE.equals(city)) province = e; |
| | | else ft.put(city, e); |
| | | } |
| | | // 䏿å
¨å£å¾ï¼ç¨äºç¯æ¯ï¼ |
| | | String prevPeriod = prevPeriod(period); |
| | | Map<String, FreightTurnoverImport> ftPrev = new HashMap<>(); |
| | | for (FreightTurnoverImport e : freightTurnoverMapper.selectList( |
| | | new LambdaQueryWrapper<FreightTurnoverImport>() |
| | | .eq(FreightTurnoverImport::getReportPeriod, prevPeriod))) { |
| | | String city = RegionUtil.normalizeCityName(e.getRegionName()); |
| | | if (city != null && !FreightCalc.PROVINCE.equals(city)) ftPrev.put(city, e); |
| | | } |
| | | |
| | | // ⢠æå表ï¼å½æ/累计 è§ä¸ä¸å计å¨è½¬é |
| | | Map<String, ScaleSplitTransport> splitCum = new HashMap<>(); |
| | | for (ScaleSplitTransport rec : scaleSplitMapper.selectList( |
| | | new LambdaQueryWrapper<ScaleSplitTransport>() |
| | | .eq(ScaleSplitTransport::getReportPeriod, period))) { |
| | | String city = RegionUtil.normalizeCityName(rec.getRegionName()); |
| | | if (city == null || FreightCalc.PROVINCE.equals(city)) continue; |
| | | if ("CUMULATIVE".equals(rec.getPeriodType())) splitCum.put(city, rec); |
| | | } |
| | | |
| | | double sumFreight = 0.0, sumTurnover = 0.0; |
| | | for (String city : RegionUtil.cityList()) { |
| | | FreightTurnoverImport row = ft.get(city); |
| | | Double totalF = row == null ? null : monthField(row, month, true); |
| | | Double totalT = row == null ? null : monthField(row, month, false); |
| | | Double abF = aboveFreight.containsKey(city) ? aboveFreight.get(city).get(month) : null; |
| | | abF = abF == null ? null : abF / 10000.0; |
| | | Double abT = aboveTurnover.containsKey(city) && aboveTurnover.get(city).get(month) != null |
| | | ? aboveTurnover.get(city).get(month) / 10000.0 : null; |
| | | |
| | | if (totalF != null) sumFreight += totalF; |
| | | if (totalT != null) sumTurnover += totalT; |
| | | |
| | | if (totalF == null && abF == null) continue; // 䏤侧齿²¡æ° â 交ç»ç¼ºæ°æ¸
åï¼å¦ç®ï¼ |
| | | if (totalF == null && abF != null) { |
| | | issues.add(String.format("è´§è¿ä½æ£ï¼%s %s 缺â¡å
¨å£å¾ï¼è§ä¸ %.4f å¨ãå
¨å£å¾ç¼ºï¼â è§ä¸æ æ³è®¡ç®" |
| | | + "ï¼éå
导ãè´§è¿éå¨è½¬é表ãå½ææ¡£ï¼", period, city, abF)); |
| | | } |
| | | if (totalT == null && abT != null) { |
| | | issues.add(String.format("è´§è¿ä½æ£ï¼%s %s 缺â¡å
¨å£å¾å¨è½¬éï¼è§ä¸ %.4f å¨ãå
¨å£å¾ç¼ºï¼â è§ä¸æ æ³è®¡ç®", |
| | | period, city, abT)); |
| | | } |
| | | // 2026-10-12ï¼â¡å
¨å£å¾ç¼ºï¼æªå¯¼ï¼æ¶ä¸åæ¥ãè§ä¸ä¸ºè´ãââä¸é¢å·²åç¬æ¥ã缺â¡å
¨å£å¾ãï¼ |
| | | // è¥è¿é仿 0 â è§ä¸ ç®ï¼ä¼å¯¹æ¯ä¸ªå¸å·å¤å·ä¸æ¡å鳿§ï¼å¼ä¸æ¯è´ãæ¯ç¼ºï¼ï¼æçé®é¢æ·¹æ²¡ã |
| | | if (totalF != null) { |
| | | double belowF = totalF - (abF == null ? 0 : abF); |
| | | if (belowF < -TOL) issues.add(String.format("è´§è¿ä½æ£ï¼%s %s è§ä¸è´§è¿é为è´ï¼å
¨å£å¾ %.4f â è§ä¸ %.4f = %.4fï¼", period, city, totalF, abF, belowF)); |
| | | } |
| | | if (totalT != null) { |
| | | double belowT = totalT - (abT == null ? 0 : abT); |
| | | if (belowT < -TOL) issues.add(String.format("è´§è¿ä½æ£ï¼%s %s è§ä¸å¨è½¬é为è´ï¼å
¨å£å¾ %.4f â è§ä¸ %.4f = %.4fï¼", period, city, totalT, abT, belowT)); |
| | | } |
| | | if (totalF != null && abF == null) issues.add(String.format("è´§è¿ä½æ£ï¼%s %s æå
¨å£å¾ä½æ è§ä¸è®°å½ â è§ä¸çäºå
¨å£å¾ï¼%.4fï¼ï¼çâ æ¼æ¥", period, city, totalF)); |
| | | if (totalF != null && abF != null && totalF < abF) issues.add(String.format("è´§è¿ä½æ£ï¼%s %s å
¨å£å¾ %.4f å°äºè§ä¸ %.4f", period, city, totalF, abF)); |
| | | |
| | | FreightTurnoverImport prev = ftPrev.get(city); |
| | | if (prev != null) { |
| | | int prevMonth = month == 1 ? 12 : month - 1; |
| | | Double prevF = monthField(prev, prevMonth, true); |
| | | Double prevT = monthField(prev, prevMonth, false); |
| | | if (totalF != null && sameValue(prevF, totalF) && Math.abs(totalF) >= CHAIN_FLOOR) { |
| | | issues.add(String.format("è´§è¿ä½æ£ï¼%s %s å
¨å£å¾è´§è¿éæ¬æä¸ä¸æå®å
¨ç¸åï¼%.4fï¼ï¼çéå¤å¯¼å
¥", period, city, totalF)); |
| | | } |
| | | if (totalT != null && sameValue(prevT, totalT) && Math.abs(totalT) >= CHAIN_FLOOR) { |
| | | issues.add(String.format("è´§è¿ä½æ£ï¼%s %s å
¨å£å¾å¨è½¬éæ¬æä¸ä¸æå®å
¨ç¸åï¼%.4fï¼ï¼çéå¤å¯¼å
¥", period, city, totalT)); |
| | | } |
| | | } |
| | | if (row != null) { |
| | | Double lastF = lastMonthField(row, month, true); |
| | | Double lastT = lastMonthField(row, month, false); |
| | | if (lastF != null && totalF != null && Math.abs(lastF) >= CHAIN_FLOOR && Math.abs(totalF) >= CHAIN_FLOOR) { |
| | | double ratio = totalF / lastF - 1.0; |
| | | if (Math.abs(ratio) > YOY_RATIO) { |
| | | issues.add(String.format("è´§è¿ä½æ£ï¼%s %s å
¨å£å¾è´§è¿é忝å¼å¸¸ï¼å»å¹´åæ %.4f -> æ¬æ %.4fï¼%.1f%%ï¼", period, city, lastF, totalF, ratio * 100)); |
| | | } |
| | | } |
| | | if (lastT != null && totalT != null && Math.abs(lastT) >= CHAIN_FLOOR && Math.abs(totalT) >= CHAIN_FLOOR) { |
| | | double ratio = totalT / lastT - 1.0; |
| | | if (Math.abs(ratio) > YOY_RATIO) { |
| | | issues.add(String.format("è´§è¿ä½æ£ï¼%s %s å
¨å£å¾å¨è½¬é忝å¼å¸¸ï¼å»å¹´åæ %.4f -> æ¬æ %.4fï¼%.1f%%ï¼", period, city, lastT, totalT, ratio * 100)); |
| | | } |
| | | } |
| | | } |
| | | |
| | | ScaleSplitTransport cum = splitCum.get(city); |
| | | if (cum != null && cum.getAboveScaleTurnover() != null) { |
| | | Map<Integer, Double> buckets = aboveTurnover.get(city); |
| | | if (buckets != null) { |
| | | double sum = 0.0; |
| | | for (int m = 1; m <= month; m++) sum += buckets.getOrDefault(m, 0.0); |
| | | double fromH2032 = sum / 10000.0; |
| | | double fromSplit = cum.getAboveScaleTurnover(); |
| | | if (Math.abs(fromH2032 - fromSplit) > TOL) { |
| | | issues.add(String.format("è´§è¿ä½æ£ï¼%s %s è§ä¸å¨è½¬é累计å¾ç¨½ä¸ä¸è´ï¼â éææ±å %.4f vs â¢ç´¯è®¡ %.4fï¼", period, city, fromH2032, fromSplit)); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | if (province != null) { |
| | | Double pf = monthField(province, month, true); |
| | | Double pt = monthField(province, month, false); |
| | | if (pf != null && Math.abs(pf - sumFreight) > TOL) { |
| | | issues.add(String.format("è´§è¿ä½æ£ï¼%s Σ17å¸å·è´§è¿é %.4f â â¡è¡¨æ¹åçè¡ %.4fï¼å·® %.4fï¼", period, sumFreight, pf, sumFreight - pf)); |
| | | } |
| | | if (pt != null && Math.abs(pt - sumTurnover) > TOL) { |
| | | issues.add(String.format("è´§è¿ä½æ£ï¼%s Σ17å¸å·å¨è½¬é %.4f â â¡è¡¨æ¹åçè¡ %.4fï¼å·® %.4fï¼", period, sumTurnover, pt, sumTurnover - pt)); |
| | | } |
| | | } |
| | | if (!issues.isEmpty()) { |
| | | log.warn("è´§è¿ä½æ£ï¼B4ï¼ï¼{} å½ä¸ {} æ¡é®é¢", period, issues.size()); |
| | | } |
| | | return issues; |
| | | } |
| | | |
| | | /** â¡è¡¨æ æå· åå½æè´§è¿é/å¨è½¬éï¼ä¸ FreightCalc åä¸åæ³ï¼ */ |
| | | private Double monthField(FreightTurnoverImport e, int m, boolean freight) { |
| | | switch (m) { |
| | | case 1: return freight ? e.getFreightM01() : e.getTurnoverM01(); |
| | | case 2: return freight ? e.getFreightM02() : e.getTurnoverM02(); |
| | | case 3: return freight ? e.getFreightM03() : e.getTurnoverM03(); |
| | | case 4: return freight ? e.getFreightM04() : e.getTurnoverM04(); |
| | | case 5: return freight ? e.getFreightM05() : e.getTurnoverM05(); |
| | | case 6: return freight ? e.getFreightM06() : e.getTurnoverM06(); |
| | | case 7: return freight ? e.getFreightM07() : e.getTurnoverM07(); |
| | | case 8: return freight ? e.getFreightM08() : e.getTurnoverM08(); |
| | | case 9: return freight ? e.getFreightM09() : e.getTurnoverM09(); |
| | | case 10: return freight ? e.getFreightM10() : e.getTurnoverM10(); |
| | | case 11: return freight ? e.getFreightM11() : e.getTurnoverM11(); |
| | | case 12: return freight ? e.getFreightM12() : e.getTurnoverM12(); |
| | | default: return null; |
| | | } |
| | | } |
| | | |
| | | /** â¡è¡¨å»å¹´åæå¼ */ |
| | | private Double lastMonthField(FreightTurnoverImport e, int m, boolean freight) { |
| | | switch (m) { |
| | | case 1: return freight ? e.getLastFreightM01() : e.getLastTurnoverM01(); |
| | | case 2: return freight ? e.getLastFreightM02() : e.getLastTurnoverM02(); |
| | | case 3: return freight ? e.getLastFreightM03() : e.getLastTurnoverM03(); |
| | | case 4: return freight ? e.getLastFreightM04() : e.getLastTurnoverM04(); |
| | | case 5: return freight ? e.getLastFreightM05() : e.getLastTurnoverM05(); |
| | | case 6: return freight ? e.getLastFreightM06() : e.getLastTurnoverM06(); |
| | | case 7: return freight ? e.getLastFreightM07() : e.getLastTurnoverM07(); |
| | | case 8: return freight ? e.getLastFreightM08() : e.getLastTurnoverM08(); |
| | | case 9: return freight ? e.getLastFreightM09() : e.getLastTurnoverM09(); |
| | | case 10: return freight ? e.getLastFreightM10() : e.getLastTurnoverM10(); |
| | | case 11: return freight ? e.getLastFreightM11() : e.getLastTurnoverM11(); |
| | | case 12: return freight ? e.getLastFreightM12() : e.getLastTurnoverM12(); |
| | | default: return null; |
| | | } |
| | | } |
| | | |
| | | private boolean sameValue(Double a, Double b) { |
| | | if (a == null || b == null) return false; |
| | | return Math.abs(a - b) < 1e-9; |
| | | } |
| | | |
| | | private String prevPeriod(String period) { |
| | | int y = Integer.parseInt(period.substring(0, 4)); |
| | | int m = Integer.parseInt(period.substring(5, 7)); |
| | | if (m == 1) return (y - 1) + "-12"; |
| | | return String.format("%04d-%02d", y, m - 1); |
| | | } |
| | | |
| | | public String describe(String period) { |
| | | List<String> issues = check(period); |
| | | return issues.isEmpty() ? "è´§è¿ä½æ£ï¼æªåç°é®é¢" : String.join("\n", issues); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.trafficaudit.reportexport.calc; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.trafficaudit.common.util.RegionUtil; |
| | | import com.trafficaudit.dataimport.entity.CityBusMonthly; |
| | | import com.trafficaudit.dataimport.entity.CityTaxiMonthly; |
| | | import com.trafficaudit.dataimport.entity.FreightTurnoverImport; |
| | | import com.trafficaudit.dataimport.entity.H2032EnterpriseMonthly; |
| | | import com.trafficaudit.dataimport.entity.PassengerEnterpriseMonthly; |
| | | import com.trafficaudit.dataimport.entity.ScaleSplitTransport; |
| | | import com.trafficaudit.dataimport.entity.WycOrderMonthly; |
| | | import com.trafficaudit.dataimport.entity.WycTotalMonthly; |
| | | import com.trafficaudit.dataimport.mapper.CityBusMonthlyMapper; |
| | | import com.trafficaudit.dataimport.mapper.CityTaxiMonthlyMapper; |
| | | import com.trafficaudit.dataimport.mapper.FreightTurnoverImportMapper; |
| | | import com.trafficaudit.dataimport.mapper.H2032EnterpriseMonthlyMapper; |
| | | import com.trafficaudit.dataimport.mapper.PassengerEnterpriseMonthlyMapper; |
| | | import com.trafficaudit.dataimport.mapper.ScaleSplitTransportMapper; |
| | | import com.trafficaudit.dataimport.mapper.WycOrderMonthlyMapper; |
| | | import com.trafficaudit.dataimport.mapper.WycTotalMonthlyMapper; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.apache.poi.openxml4j.util.ZipSecureFile; |
| | | import org.apache.poi.ss.usermodel.Cell; |
| | | import org.apache.poi.ss.usermodel.CellType; |
| | | import org.apache.poi.ss.usermodel.Row; |
| | | import org.apache.poi.ss.usermodel.Sheet; |
| | | import org.apache.poi.ss.usermodel.Workbook; |
| | | import org.apache.poi.ss.usermodel.WorkbookFactory; |
| | | import org.springframework.beans.factory.annotation.Value; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import javax.annotation.Resource; |
| | | import java.io.File; |
| | | import java.io.FileInputStream; |
| | | import java.io.InputStream; |
| | | import java.util.ArrayList; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.LinkedHashSet; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Set; |
| | | |
| | | /** |
| | | * æ±æ»å¤§è¡¨ãç¼ºæ°æ¸
åãï¼åªè¯»ï¼2026-10-12 æ°å¢ï¼ç¨æ·è¦æ±ï¼ã |
| | | * |
| | | * <p>ä¸ãè´§è¿åºæ°ä½æ£ã{@link FreightReadinessCheck} äºè¡¥ãå¿
é¡»åå¼ï¼ |
| | | * é£ä¸ªæ¥ãä¸å¯è½çæ°ãï¼åå为è´ãΣå¸å·â çè¡â¦ï¼ï¼æ¬ç±»æ¥ã该æçæ°æ²¡æãã |
| | | * 2026-08 使£ 0 å½ä¸ãä½ 2026-09 åºè¡¨æ¶ 8 æåä»ä¸ºç©º ââ å°±æ¯åªæåè
ãæ²¡æåè
çåæã |
| | | * |
| | | * <p>æ£æ¥é¡¹ï¼ |
| | | * <ol> |
| | | * <li>ç¨åºéæä»åºåå¡«ç页ï¼ç线å
车ãå
¬äº¤ãåºç§ã轨é轮渡ï¼ï¼1..ç®æ æ éææ¥æ¥æºæ¯å¦ææ°ã17 å¸å·æ¯å¦é½å
¨ï¼</li> |
| | | * <li>ç¨åºåªåå¡«ç®æ æç页ï¼è´§è¿ãç½çº¦è½¦ï¼ï¼ç®æ æä¸é¡¹æ¥æºæ¯å¦é½å
¨ï¼</li> |
| | | * <li>ä¸è¿°ä¸¤é¡µç 1..ç®æ æ-1 ç±æ¯çæ¿æ
ï¼æ¯ç该æå¼åå
¨ç©ºååºè¡¨å³ç¼ºï¼åºéææ°ãç¨åºä¸åå¡«ï¼ã</li> |
| | | * </ol> |
| | | * |
| | | * <p>æ¬ç±»åªè¯»ã䏿å¼å¸¸ï¼å¼å¸¸è½¬ææ¸
åæåï¼ï¼ä¾å¯¼åºæ¥å¿ / å¼¹çª / çæä»¶ãæ°æ®ç¼ºå¤±æ¸
åã页使ç¨ã |
| | | */ |
| | | @Slf4j |
| | | @Service |
| | | public class SummaryMissingDataCheck { |
| | | |
| | | @Resource |
| | | private PassengerEnterpriseMonthlyMapper h2031Mapper; // ç线å
车ï¼å
¬è·¯æ
客ä¼ä¸ææ¥ï¼ |
| | | @Resource |
| | | private CityBusMonthlyMapper cityBusMapper; // å
¬äº¤ + 轨é/轮渡 |
| | | @Resource |
| | | private CityTaxiMonthlyMapper cityTaxiMapper; // åºç§ï¼ç½çº¦è½¦æåä¹ç¨å®çå¹³åè¿è·ï¼ |
| | | @Resource |
| | | private H2032EnterpriseMonthlyMapper h2032Mapper; // è´§è¿è§ä¸ï¼â ï¼ |
| | | @Resource |
| | | private FreightTurnoverImportMapper freightTurnoverMapper; // è´§è¿å
¨å£å¾ï¼â¡ï¼ |
| | | @Resource |
| | | private ScaleSplitTransportMapper scaleSplitMapper; // è§ä¸è§ä¸æåï¼â¢ï¼ |
| | | @Resource |
| | | private WycOrderMonthlyMapper wycOrderMapper; // ç½çº¦è½¦è®¢å + å
¨ç pin |
| | | @Resource |
| | | private WycTotalMonthlyMapper wycTotalMapper; // ç½çº¦è½¦ pin ä¸è¡¨ï¼å
åºï¼ |
| | | |
| | | @Value("${summary.template-dir:docs/çææ±æ»å¤§è¡¨}") |
| | | private String summaryTemplateDir; |
| | | |
| | | /** ç¨åºåªåå¡«ãç®æ æãç页 ââ æ´æ©æä»½ç±æ¯çæ¿æ
*/ |
| | | private static final String[] TARGET_ONLY_PAGES = {" è´§è¿", "ç½çº¦è½¦"}; |
| | | |
| | | public List<String> check(String period) { |
| | | List<String> issues = new ArrayList<>(); |
| | | if (period == null || period.length() < 7) return issues; |
| | | int year; |
| | | int month; |
| | | try { |
| | | year = Integer.parseInt(period.substring(0, 4)); |
| | | month = Integer.parseInt(period.substring(5, 7)); |
| | | } catch (Exception e) { |
| | | return issues; |
| | | } |
| | | // 1) éæå填页ï¼1..ç®æ æ |
| | | for (int m = 1; m <= month; m++) { |
| | | String p = String.format("%04d-%02d", year, m); |
| | | checkH2031Month(p, issues); |
| | | checkCityBusMonth(p, issues); |
| | | checkRailFerryMonth(p, issues); |
| | | checkCityTaxiMonth(p, issues); |
| | | } |
| | | // 2) åªåå¡«ç®æ æç两页ï¼ç®æ ææ¥æº |
| | | String target = String.format("%04d-%02d", year, month); |
| | | checkFreightTarget(target, issues); |
| | | checkWycTarget(target, issues); |
| | | // 3) è¿ä¸¤é¡µçåå²æï¼æ¯çæ¯å¦ä¸ºç©º |
| | | checkMotherHistory(target, year, month, issues); |
| | | if (!issues.isEmpty()) log.warn("ç¼ºæ°æ¸
åï¼{}ï¼ï¼å½ä¸ {} æ¡", period, issues.size()); |
| | | return issues; |
| | | } |
| | | |
| | | // ---------- åé¡µæ£æ¥ ---------- |
| | | |
| | | private void checkH2031Month(String p, List<String> issues) { |
| | | try { |
| | | List<PassengerEnterpriseMonthly> rows = h2031Mapper.selectList( |
| | | new LambdaQueryWrapper<PassengerEnterpriseMonthly>().eq(PassengerEnterpriseMonthly::getReportPeriod, p)); |
| | | if (rows.isEmpty()) { |
| | | issues.add(String.format("缺æ°ï¼ç线å
车 %s æ ãå
¬è·¯æ
客ä¼ä¸ææ¥ãæ¥æº â 该æå®¢è¿é/å¨è½¬éãä¸å£å¾ãå
¬è·¯ç线ãå°ä¸ºç©º", p)); |
| | | return; |
| | | } |
| | | Set<String> got = new LinkedHashSet<>(); |
| | | for (PassengerEnterpriseMonthly r : rows) { |
| | | String c = RegionUtil.cityByCode(r.getRegionCode()); |
| | | if (c != null) got.add(c); |
| | | } |
| | | addMissingCities("ç线å
车", p, got, issues); |
| | | } catch (Exception e) { |
| | | issues.add("ç¼ºæ°æ£æ¥å¼å¸¸ï¼ç线å
车 " + p + " :: " + e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | private void checkCityBusMonth(String p, List<String> issues) { |
| | | try { |
| | | List<CityBusMonthly> rows = cityBusMapper.selectList( |
| | | new LambdaQueryWrapper<CityBusMonthly>().eq(CityBusMonthly::getReportPeriod, p)); |
| | | if (rows.isEmpty()) { |
| | | issues.add(String.format("缺æ°ï¼å
¬äº¤ %s æ ãåå¸å
¬äº¤ææ¥ãæ¥æº â å
¬äº¤é¡µä¸ºç©ºãåå¸å®¢è¿é¡µå°ç¼ºå
¬äº¤é¨å", p)); |
| | | return; |
| | | } |
| | | addMissingCities("å
¬äº¤", p, citySetOf(rows), issues); |
| | | } catch (Exception e) { |
| | | issues.add("ç¼ºæ°æ£æ¥å¼å¸¸ï¼å
¬äº¤ " + p + " :: " + e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | private void checkRailFerryMonth(String p, List<String> issues) { |
| | | try { |
| | | List<CityBusMonthly> rows = cityBusMapper.selectList( |
| | | new LambdaQueryWrapper<CityBusMonthly>().eq(CityBusMonthly::getReportPeriod, p)); |
| | | if (rows.isEmpty()) { |
| | | issues.add(String.format("缺æ°ï¼è½¨éã轮渡 %s æ ãåå¸å
¬äº¤ææ¥ãæ¥æºï¼è½¨é/轮渡å表ï¼â 该页为空", p)); |
| | | return; |
| | | } |
| | | boolean rail = false; |
| | | boolean ferry = false; |
| | | for (CityBusMonthly r : rows) { |
| | | if (r.getRailPassengerVolume() != null) rail = true; |
| | | if (r.getFerryPassengerVolume() != null) ferry = true; |
| | | } |
| | | List<String> miss = new ArrayList<>(); |
| | | if (!rail) miss.add("轨é"); |
| | | if (!ferry) miss.add("轮渡"); |
| | | if (!miss.isEmpty()) { |
| | | issues.add(String.format("缺æ°ï¼è½¨éã轮渡 %s ç %s åæ®µå
¨ç©º â 该åå°ä¸ºç©º", |
| | | p, String.join("ã", miss))); |
| | | } |
| | | } catch (Exception e) { |
| | | issues.add("ç¼ºæ°æ£æ¥å¼å¸¸ï¼è½¨éã轮渡 " + p + " :: " + e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | private void checkCityTaxiMonth(String p, List<String> issues) { |
| | | try { |
| | | List<CityTaxiMonthly> rows = cityTaxiMapper.selectList( |
| | | new LambdaQueryWrapper<CityTaxiMonthly>().eq(CityTaxiMonthly::getReportPeriod, p)); |
| | | if (rows.isEmpty()) { |
| | | issues.add(String.format("缺æ°ï¼åºç§ %s æ ã巡游åºç§æ±½è½¦ææ¥ãæ¥æº â åºç§é¡µä¸ºç©ºï¼ä¸ç½çº¦è½¦æååä¸å°å¹³åè¿è·", p)); |
| | | return; |
| | | } |
| | | Set<String> got = new LinkedHashSet<>(); |
| | | for (CityTaxiMonthly r : rows) { |
| | | String c = RegionUtil.normalizeCityName(r.getCity()); |
| | | if (c != null && !c.isEmpty()) got.add(c); |
| | | } |
| | | addMissingCities("åºç§", p, got, issues); |
| | | } catch (Exception e) { |
| | | issues.add("ç¼ºæ°æ£æ¥å¼å¸¸ï¼åºç§ " + p + " :: " + e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | private void checkFreightTarget(String p, List<String> issues) { |
| | | try { |
| | | // â H203-2 ä¼ä¸ææ¥ï¼è§ä¸è´§è¿é/å¨è½¬éï¼ |
| | | List<H2032EnterpriseMonthly> h = h2032Mapper.selectList( |
| | | new LambdaQueryWrapper<H2032EnterpriseMonthly>().eq(H2032EnterpriseMonthly::getReportPeriod, p)); |
| | | if (h.isEmpty()) { |
| | | issues.add(String.format("缺æ°ï¼è´§è¿ %s æ â ãH203-2 é路货ç©è¿è¾æåº¦ç产æ
åµãâ è§ä¸è´§è¿é/å¨è½¬éåä¸å°", p)); |
| | | } else { |
| | | Set<String> got = new LinkedHashSet<>(); |
| | | for (H2032EnterpriseMonthly r : h) { |
| | | String c = RegionUtil.cityByCode(r.getRegionCode()); |
| | | if (c != null) got.add(c); |
| | | } |
| | | addMissingCities("è´§è¿â è§ä¸", p, got, issues); |
| | | } |
| | | // â¡ è´§è¿éå¨è½¬é表ï¼å计 = è§ä¸+è§ä¸ï¼ |
| | | List<FreightTurnoverImport> f = freightTurnoverMapper.selectList( |
| | | new LambdaQueryWrapper<FreightTurnoverImport>().eq(FreightTurnoverImport::getReportPeriod, p)); |
| | | if (f.isEmpty()) { |
| | | issues.add(String.format("缺æ°ï¼è´§è¿ %s æ â¡ãè´§è¿éå¨è½¬é表ãâ å计货è¿é/å¨è½¬éãè§ä¸ï¼ååï¼å
¨é¨åä¸å°", p)); |
| | | } else { |
| | | Set<String> got = new LinkedHashSet<>(); |
| | | for (FreightTurnoverImport r : f) { |
| | | String c = RegionUtil.normalizeCityName(r.getRegionName()); |
| | | if (c != null && !RegionUtil.cityList().contains(c) && !"æ¹åç".equals(c)) continue; |
| | | if (c != null && RegionUtil.cityList().contains(c)) got.add(c); |
| | | } |
| | | addMissingCities("è´§è¿â¡å
¨å£å¾", p, got, issues); |
| | | } |
| | | // ⢠æå表ï¼è§ä¸/å计å¨è½¬éï¼ï¼å½æ + 累计 |
| | | List<ScaleSplitTransport> s = scaleSplitMapper.selectList( |
| | | new LambdaQueryWrapper<ScaleSplitTransport>().eq(ScaleSplitTransport::getReportPeriod, p)); |
| | | if (s.isEmpty()) { |
| | | issues.add(String.format("缺æ°ï¼è´§è¿ %s æ â¢ãè§ä¸è§ä¸æå表ãâ è§ä¸/å计å¨è½¬éåä¸å°ï¼è¯¥æå¨è½¬éå°ä¸ºç©º/0ï¼", p)); |
| | | } else { |
| | | Set<String> month = new LinkedHashSet<>(); |
| | | Set<String> cum = new LinkedHashSet<>(); |
| | | for (ScaleSplitTransport r : s) { |
| | | String c = RegionUtil.normalizeCityName(r.getRegionName()); |
| | | if (c == null || !RegionUtil.cityList().contains(c)) continue; |
| | | if ("MONTH".equals(r.getPeriodType())) month.add(c); |
| | | else if ("CUMULATIVE".equals(r.getPeriodType())) cum.add(c); |
| | | } |
| | | addMissingCities("è´§è¿â¢æå(彿)", p, month, issues); |
| | | addMissingCities("è´§è¿â¢æå(累计)", p, cum, issues); |
| | | } |
| | | } catch (Exception e) { |
| | | issues.add("ç¼ºæ°æ£æ¥å¼å¸¸ï¼è´§è¿ " + p + " :: " + e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | private void checkWycTarget(String p, List<String> issues) { |
| | | try { |
| | | List<WycOrderMonthly> orders = wycOrderMapper.selectList( |
| | | new LambdaQueryWrapper<WycOrderMonthly>().eq(WycOrderMonthly::getReportPeriod, p)); |
| | | boolean okOrders = false; |
| | | boolean okPin = false; |
| | | if (!orders.isEmpty()) { |
| | | WycOrderMonthly o = orders.get(0); |
| | | double sum = nz(o.getOrderWuhan()) + nz(o.getOrderHuangshi()) + nz(o.getOrderShiyan()) + nz(o.getOrderYichang()) |
| | | + nz(o.getOrderXiangyang()) + nz(o.getOrderEzhou()) + nz(o.getOrderJingmen()) + nz(o.getOrderXiaogan()) |
| | | + nz(o.getOrderJingzhou()) + nz(o.getOrderHuanggang()) + nz(o.getOrderXianning()) + nz(o.getOrderSuizhou()) |
| | | + nz(o.getOrderEnshi()) + nz(o.getOrderXiantao()) + nz(o.getOrderQianjiang()) + nz(o.getOrderTianmen()) |
| | | + nz(o.getOrderShennong()); |
| | | okOrders = sum > 0; |
| | | okPin = nz(o.getPinK()) > 0 && nz(o.getPinCK()) > 0 && nz(o.getPinZ()) > 0 && nz(o.getPinCZ()) > 0; |
| | | } |
| | | if (!okOrders) { |
| | | issues.add(String.format("缺æ°ï¼ç½çº¦è½¦ %s æ è®¢åæ°æ®ï¼wyc_order_monthly 17 å¸å·è®¢åï¼â æåæ æ³è®¡ç®ï¼è¯¥é¡µå°ä¸ºç©º", p)); |
| | | } |
| | | if (!okPin) { |
| | | List<WycTotalMonthly> tot = wycTotalMapper.selectList( |
| | | new LambdaQueryWrapper<WycTotalMonthly>().eq(WycTotalMonthly::getReportPeriod, p)); |
| | | boolean okTot = !tot.isEmpty() |
| | | && nz(tot.get(0).getPinK()) > 0 && nz(tot.get(0).getPinCK()) > 0 |
| | | && nz(tot.get(0).getPinZ()) > 0 && nz(tot.get(0).getPinCZ()) > 0; |
| | | if (!okTot) { |
| | | issues.add(String.format("缺æ°ï¼ç½çº¦è½¦ %s 缺å
¨ç pinï¼order è¡¨ä¸ wyc_total_monthly 齿²¡æï¼â æåæ æ³è®¡ç®", p)); |
| | | } |
| | | } |
| | | List<CityTaxiMonthly> taxi = cityTaxiMapper.selectList( |
| | | new LambdaQueryWrapper<CityTaxiMonthly>().eq(CityTaxiMonthly::getReportPeriod, p)); |
| | | Set<String> got = new LinkedHashSet<>(); |
| | | for (CityTaxiMonthly r : taxi) { |
| | | String c = RegionUtil.normalizeCityName(r.getCity()); |
| | | if (c != null && !c.isEmpty()) got.add(c); |
| | | } |
| | | addMissingCities("ç½çº¦è½¦(ä¾èµåºç§å¹³åè¿è·)", p, got, issues); |
| | | } catch (Exception e) { |
| | | issues.add("ç¼ºæ°æ£æ¥å¼å¸¸ï¼ç½çº¦è½¦ " + p + " :: " + e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | /** è´§è¿/ç½çº¦è½¦çå岿ï¼1..M-1ï¼ç±æ¯çæ¿æ
ï¼æ¯çæ²¡ææ¶æãåæå¤ä»½æ¯çãè¿åï¼ï¼ |
| | | * åªææ¯çä¸å¤ä»½æ¯ç齿²¡ææ°ï¼æç®çæ£çåºè¡¨ç¼ºå£ï¼å¦åä¼è¯¯æ¥ 7 æé£ç§ãéª¨æ¶æ¯ç + donor è¿åãçæ
åµï¼ã */ |
| | | private void checkMotherHistory(String period, int year, int month, List<String> issues) { |
| | | File mother = resolveMother(year, month); |
| | | if (mother == null) { |
| | | issues.add(String.format("缺æ°ï¼æªæ¾å°æ¯çã%då¹´%dæéè·¯è¿è¾éæ±æ»è¡¨.xlsxãï¼%sï¼", year, month, summaryTemplateDir)); |
| | | return; |
| | | } |
| | | File donor = resolveDonor(year, month); |
| | | double saved = ZipSecureFile.getMinInflateRatio(); |
| | | ZipSecureFile.setMinInflateRatio(0.0001); |
| | | try (InputStream in = new FileInputStream(mother); |
| | | Workbook wb = WorkbookFactory.create(in); |
| | | InputStream in2 = (donor != null) ? new FileInputStream(donor) : null; |
| | | Workbook dwb = (in2 != null) ? WorkbookFactory.create(in2) : null) { |
| | | for (String pageName : TARGET_ONLY_PAGES) { |
| | | List<String> blankMonths = new ArrayList<>(); |
| | | for (int m = 1; m < month; m++) { |
| | | if (monthHasData(wb, pageName, year, m)) continue; |
| | | if (dwb != null && monthHasData(dwb, pageName, year, m)) continue; |
| | | blankMonths.add(String.format("%då¹´%dæ", year, m)); |
| | | } |
| | | if (!blankMonths.isEmpty()) { |
| | | issues.add(String.format("缺æ°ï¼%s æ¯çï¼%s æï¼ç %s å¼åå
¨ç©º%sï¼èç¨åºåªåå¡«ç®æ æ â åºè¡¨è¯¥æå°ä¸ºç©ºï¼åºéå¯è½ææ°æ®ï¼", |
| | | pageName.trim(), period, String.join("ã", blankMonths), |
| | | donor == null ? "" : "ï¼åæå¤ä»½æ¯ç乿²¡æï¼")); |
| | | } |
| | | } |
| | | } catch (Exception e) { |
| | | issues.add("ç¼ºæ°æ£æ¥å¼å¸¸ï¼è¯»æ¯ç " + mother.getName() + " :: " + e.getMessage()); |
| | | } finally { |
| | | ZipSecureFile.setMinInflateRatio(saved); |
| | | } |
| | | } |
| | | |
| | | /** 该工ä½ç°¿è¯¥é¡µã该年该æçãå¼åãæ¯å¦å·²ææ°å¼ï¼è¡¨å¤´è¡ä¹å¤ï¼ */ |
| | | private boolean monthHasData(Workbook wb, String pageName, int year, int m) { |
| | | Sheet sheet = findSheet(wb, pageName); |
| | | if (sheet == null) return false; |
| | | int[] ref = detectYearMonthCols(sheet, year).get(m); |
| | | if (ref == null) return false; |
| | | return countNumericInColumn(sheet, ref[1], ref[0]) > 0; |
| | | } |
| | | |
| | | private Sheet findSheet(Workbook wb, String pageName) { |
| | | Sheet s = wb.getSheet(pageName); |
| | | if (s == null && " è´§è¿".equals(pageName)) s = wb.getSheet("è´§è¿"); |
| | | return s; |
| | | } |
| | | |
| | | /** åæå¤ä»½æ¯çï¼æ¸
çåç宿´ææ¯çï¼ï¼å½å _å¤ä»½_{å¹´}å¹´{æ}æâ¦éè·¯è¿è¾éæ±æ»è¡¨ */ |
| | | private File resolveDonor(int year, int month) { |
| | | String rel = summaryTemplateDir; |
| | | while (rel.startsWith("./")) rel = rel.substring(2); |
| | | String[] roots = {summaryTemplateDir, System.getProperty("user.dir") + "/" + rel, |
| | | System.getProperty("user.dir") + "/../" + rel}; |
| | | for (String root : roots) { |
| | | if (root == null || root.trim().isEmpty()) continue; |
| | | File dir = new File(root); |
| | | File[] files = dir.listFiles((d, name) -> name.startsWith("_å¤ä»½_") |
| | | && name.contains(year + "å¹´" + month + "æ") && name.contains("éè·¯è¿è¾éæ±æ»è¡¨")); |
| | | if (files != null && files.length > 0) return files[0]; |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | // ---------- å·¥å
· ---------- |
| | | |
| | | private Set<String> citySetOf(List<CityBusMonthly> rows) { |
| | | Set<String> got = new LinkedHashSet<>(); |
| | | for (CityBusMonthly r : rows) { |
| | | String c = RegionUtil.normalizeCityName(r.getCity()); |
| | | if (c != null && !c.isEmpty()) got.add(c); |
| | | } |
| | | return got; |
| | | } |
| | | |
| | | private void addMissingCities(String page, String period, Set<String> got, List<String> issues) { |
| | | List<String> miss = new ArrayList<>(); |
| | | for (String c : RegionUtil.cityList()) { |
| | | if (!got.contains(c)) miss.add(c); |
| | | } |
| | | if (!miss.isEmpty()) { |
| | | issues.add(String.format("缺æ°ï¼%s %s 缺å¸å·ï¼%s", page, period, String.join("ã", miss))); |
| | | } |
| | | } |
| | | |
| | | /** æ¯çéå®ä½ç®æ å¹´åæãå¼åãï¼è¿å æ -> [表头è¡(0åº), å(0åº)]ï¼å
¼å®¹ææ¬ã2026å¹´Mæã䏿¥æåºåå· */ |
| | | private Map<Integer, int[]> detectYearMonthCols(Sheet sheet, int year) { |
| | | Map<Integer, int[]> out = new LinkedHashMap<>(); |
| | | int lastRow = Math.min(5, sheet.getLastRowNum()); |
| | | for (int r = 0; r <= lastRow; r++) { |
| | | Row row = sheet.getRow(r); |
| | | if (row == null) continue; |
| | | for (Cell c : row) { |
| | | Integer m = monthOfCell(c, year); |
| | | if (m != null && !out.containsKey(m)) { |
| | | out.put(m, new int[]{r, c.getColumnIndex()}); |
| | | } |
| | | } |
| | | } |
| | | return out; |
| | | } |
| | | |
| | | private Integer monthOfCell(Cell c, int year) { |
| | | try { |
| | | if (c == null) return null; |
| | | if (c.getCellType() == CellType.STRING) { |
| | | String s = c.getStringCellValue(); |
| | | if (s == null) return null; |
| | | java.util.regex.Matcher mt = java.util.regex.Pattern |
| | | .compile("(20\\d\\d)\\s*å¹´\\s*(\\d{1,2})\\s*æ").matcher(s); |
| | | if (mt.find()) { |
| | | if (Integer.parseInt(mt.group(1)) != year) return null; |
| | | int m = Integer.parseInt(mt.group(2)); |
| | | return (m >= 1 && m <= 12) ? m : null; |
| | | } |
| | | return null; |
| | | } |
| | | if (c.getCellType() == CellType.NUMERIC) { |
| | | double v = c.getNumericCellValue(); |
| | | if (v < 20000 || v > 60000 || v != Math.floor(v)) return null; |
| | | java.util.Date d = org.apache.poi.ss.usermodel.DateUtil.getJavaDate(v); |
| | | java.util.Calendar cal = java.util.Calendar.getInstance(); |
| | | cal.setTime(d); |
| | | if (cal.get(java.util.Calendar.YEAR) != year) return null; |
| | | return cal.get(java.util.Calendar.MONTH) + 1; |
| | | } |
| | | } catch (Exception ignore) { |
| | | // éæä»½è¡¨å¤´ |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | /** ç»è®¡æåï¼è·³è¿è¡¨å¤´è¡ï¼é空æ°å¼æ ¼æ° */ |
| | | private int countNumericInColumn(Sheet sheet, int col, int headerRow) { |
| | | int n = 0; |
| | | for (int r = 0; r <= sheet.getLastRowNum(); r++) { |
| | | if (r == headerRow) continue; |
| | | Row row = sheet.getRow(r); |
| | | if (row == null) continue; |
| | | Cell c = row.getCell(col); |
| | | if (c == null) continue; |
| | | if (c.getCellType() == CellType.NUMERIC || c.getCellType() == CellType.FORMULA) n++; |
| | | } |
| | | return n; |
| | | } |
| | | |
| | | private File resolveMother(int year, int month) { |
| | | String rel = summaryTemplateDir; |
| | | while (rel.startsWith("./")) rel = rel.substring(2); |
| | | String name = year + "å¹´" + month + "æéè·¯è¿è¾éæ±æ»è¡¨.xlsx"; |
| | | String[] roots = {summaryTemplateDir, System.getProperty("user.dir") + "/" + rel, |
| | | System.getProperty("user.dir") + "/../" + rel}; |
| | | for (String root : roots) { |
| | | if (root == null || root.trim().isEmpty()) continue; |
| | | File f = new File(root, name); |
| | | if (f.exists() && f.isFile()) return f; |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | private double nz(Double v) { |
| | | return v == null ? 0.0 : v; |
| | | } |
| | | } |
| | |
| | | package com.trafficaudit.reportexport.controller; |
| | | |
| | | import com.trafficaudit.common.Result; |
| | | import com.trafficaudit.reportexport.calc.FreightReadinessCheck; |
| | | import com.trafficaudit.reportexport.calc.SummaryMissingDataCheck; |
| | | import com.trafficaudit.reportexport.service.ReportExportService; |
| | | import com.trafficaudit.reportexport.service.ReportExportTaskService; |
| | | import com.trafficaudit.system.service.OperationLogService; |
| | |
| | | |
| | | @Resource |
| | | private ReportExportService reportService; |
| | | @Resource |
| | | private FreightReadinessCheck freightReadinessCheck; |
| | | @Resource |
| | | private SummaryMissingDataCheck missingDataCheck; |
| | | @Resource |
| | | private ReportExportTaskService exportTaskService; |
| | | @Resource |
| | |
| | | } |
| | | |
| | | /** çæ_éè·¯è¿è¾éæ±æ»è¡¨.xlsxï¼æ±æ»å·¥ä½ç°¿ P0-P1 骨æ¶ï¼æ´æ¬å¤å¶æ¯çï¼é¡µç¾/å
¬å¼/2025 ç¼åä¿çï¼ */ |
| | | /** è´§è¿å£å¾å¯¼åºå使£ï¼B4ï¼ï¼åªè¯»è¿åå½ä¸çé®é¢æ¸
åã */ |
| | | @GetMapping("/summaryWorkbook/freightCheck") |
| | | public Result<java.util.List<String>> freightCheck(@RequestParam("period") String period) { |
| | | return Result.ok(freightReadinessCheck.check(period)); |
| | | } |
| | | |
| | | /** æ±æ»å¤§è¡¨ç¼ºæ°æ¸
åï¼åªè¯»ï¼ï¼ååºç®æ æåºè¡¨ä¼ç¼ºçæä»½/å¸å·/æ¥æºï¼ä¸ freightCheck äºè¡¥ï¼ã */ |
| | | @GetMapping("/summaryWorkbook/missing") |
| | | public Result<java.util.List<String>> missingCheck(@RequestParam("period") String period) { |
| | | return Result.ok(missingDataCheck.check(period)); |
| | | } |
| | | |
| | | /** æ±æ»å¤§è¡¨æææ©åï¼Step 1ï¼åªåçå¼ãæ°æ®ç空ï¼ï¼è¿åä¸ä¸æçå¼ xlsxã */ |
| | | @GetMapping("/export/summaryWorkbook/expand") |
| | | public ResponseEntity<byte[]> expandSummaryWorkbook(@RequestParam("period") String period) throws Exception { |
| | | public ResponseEntity<byte[]> expandSummaryWorkbook(@RequestParam("period") String period, |
| | | @RequestParam(value = "titlesOnly", defaultValue = "false") boolean titlesOnly) throws Exception { |
| | | if (titlesOnly) { |
| | | byte[] titled = reportService.fixSummaryTitles(period); |
| | | return buildResponse(titled, String.format("æ é¢ä¿®æ£_éè·¯è¿è¾éæ±æ»è¡¨_%s.xlsx", period)); |
| | | } |
| | | byte[] data = reportService.expandSummaryWorkbook(period); |
| | | return buildResponse(data, String.format("æ©å_éè·¯è¿è¾éæ±æ»è¡¨_%s_å¾
确认.xlsx", period)); |
| | | } |
| | |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.apache.poi.ss.usermodel.Cell; |
| | | import org.apache.poi.ss.usermodel.CellType; |
| | | import org.apache.poi.ss.usermodel.FormulaEvaluator; |
| | | import org.apache.poi.ss.usermodel.Row; |
| | | import org.apache.poi.ss.usermodel.Sheet; |
| | | import org.apache.poi.ss.util.CellRangeAddress; |
| | |
| | | |
| | | private static final int HEADER_SCAN_ROWS = 15; |
| | | |
| | | /** æ é¢ç±ç¨åºå¨åºè¡¨æ¶ç°åç 7 个页ç¾ï¼æ¯çéçæ é¢æåéææ¯çææ¬¡éåï¼ã */ |
| | | private static final List<String> TITLE_PAGES = Arrays.asList( |
| | | "è´§è¿éæå", "è´§è¿å¨è½¬éæå", "å叿±æ»", "åå¸åå¸å·", "ä¸å£å¾æå", "ä¸å£å¾åæ", "éè·¯è¿è¾å¨è½¬é"); |
| | | |
| | | /** æ é¢è¯å«æ è®°ï¼å½ä¸å
¶ä¸ææ¹åï¼é¿å
è¯¯ä¼¤å¤æ³¨çæåï¼ã */ |
| | | private static final List<String> TITLE_MARKERS = Arrays.asList( |
| | | "å
¨çåå¸å·ç´¯è®¡å®æå
¬è·¯è´§è¿éæ
åµ", "å
¨çåå¸å·å®æå
¬è·¯è´§è¿éæ
åµ", |
| | | "å
¨çåå¸å·ç´¯è®¡å®æå
¬è·¯è´§ç©è¿è¾å¨è½¬éæ
åµ", "å
¨çåå¸å·å®æå
¬è·¯è´§ç©è¿è¾å¨è½¬éæ
åµ", |
| | | "å
¨çç´¯è®¡å®æåå¸å®¢è¿éæ
åµ", "å
¨ç宿åå¸å®¢è¿éæ
åµ", |
| | | "åå¸å·1-", "å
¨çåå¸å·ç´¯è®¡å®æé路客è¿ç产æ
åµ", "å
¨çåå¸å·å®æé路客è¿ç产æ
åµ", |
| | | "ä¸å£å¾å®¢è¿éåå¨è½¬é", |
| | | "å
¨çç´¯è®¡å®æäº¤éå¨è½¬éæ
åµ", "å
¨çå®æäº¤éå¨è½¬éæ
åµ"); |
| | | |
| | | /** å
¬å¼éç A1 å¼ç¨ï¼å¯å¸¦å·¥ä½è¡¨åç¼ï¼ã */ |
| | | private static final Pattern REF = Pattern.compile( |
| | | "(?:('([^']+)'|([A-Za-z_\\u4e00-\\u9fff][A-Za-z0-9_.\\u4e00-\\u9fff\u3001]*))!)?(\\$?)([A-Z]{1,3})(\\$?)(\\d+)"); |
| | |
| | | if (sh == null || expanded.contains(sh.getSheetName())) continue; |
| | | shiftQualifiedRefs(sh, insertColOf); |
| | | } |
| | | recalcAndCache(wb); |
| | | int titleFixed = applyTitles(wb, year, targetMonth); |
| | | if (titleFixed > 0) { |
| | | log.info("æ©åï¼æ 颿 {}å¹´{}æ éå {} æ ¼", year, targetMonth, titleFixed); |
| | | } |
| | | wb.setForceFormulaRecalculation(true); |
| | | ByteArrayOutputStream out = new ByteArrayOutputStream(); |
| | | wb.write(out); |
| | |
| | | } catch (Exception e) { |
| | | throw new IllegalStateException("æ±æ»å¤§è¡¨æ©å失败: " + e.getMessage(), e); |
| | | } |
| | | } |
| | | |
| | | /** åªæ 7 个æ é¢é¡µçæ 颿ç»å®ææ¬¡éåï¼ç¨äºä¿®æ£æ¯çéæ»åçæ é¢ï¼ã */ |
| | | public byte[] fixTitles(byte[] source, int year, int month) { |
| | | try (XSSFWorkbook wb = new XSSFWorkbook(new ByteArrayInputStream(source))) { |
| | | recalcAndCache(wb); |
| | | int n = applyTitles(wb, year, month); |
| | | wb.setForceFormulaRecalculation(true); |
| | | ByteArrayOutputStream out = new ByteArrayOutputStream(); |
| | | wb.write(out); |
| | | log.info("æ±æ»å¤§è¡¨æ é¢ä¿®æ£å®æï¼{}å¹´{}æï¼æ¹å {} æ ¼", year, month, n); |
| | | return out.toByteArray(); |
| | | } catch (Exception e) { |
| | | throw new IllegalStateException("æ±æ»å¤§è¡¨æ é¢ä¿®æ£å¤±è´¥: " + e.getMessage(), e); |
| | | } |
| | | } |
| | | |
| | | /** æ´æ¬éç®å¹¶æç»æåå
¥ç¼åå¼ï¼å¦åæ¯çéåªæå
¬å¼æ²¡æå¼ï¼åç»è¯»åä¼æç©ºå¼å½ 0ï¼2026-10-12 ä¿®ï¼ */ |
| | | private void recalcAndCache(XSSFWorkbook wb) { |
| | | try { |
| | | FormulaEvaluator ev = wb.getCreationHelper().createFormulaEvaluator(); |
| | | ev.clearAllCachedResultValues(); |
| | | ev.evaluateAll(); |
| | | } catch (Exception e) { |
| | | log.warn("æ©åï¼æ´æ¬éç®æªå®æï¼å
¬å¼ä»å¨ï¼Excel æå¼ä¼åç®ï¼ï¼{}", e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | /** æ 7 个页ç¾çæ 颿åæã累计=1-Mæã彿=Mæãéåï¼å½ä¸æ è®°ææ¹ï¼é¿å
è¯¯ä¼¤å¤æ³¨ã */ |
| | | private int applyTitles(XSSFWorkbook wb, int year, int month) { |
| | | int changed = 0; |
| | | for (String page : TITLE_PAGES) { |
| | | Sheet sh = wb.getSheet(page); |
| | | if (sh == null) continue; |
| | | int lastRow = Math.min(sh.getLastRowNum(), 40); |
| | | for (int r = 0; r <= lastRow; r++) { |
| | | Row row = sh.getRow(r); |
| | | if (row == null) continue; |
| | | for (int c = 0; c <= 1; c++) { |
| | | Cell cell = row.getCell(c); |
| | | if (cell == null || cell.getCellType() != CellType.STRING) continue; |
| | | String t = cell.getStringCellValue(); |
| | | if (t == null || !looksLikeTitle(t)) continue; |
| | | String nt = t.replaceAll("(\\d{4})å¹´", year + "å¹´") |
| | | .replaceAll("(\\d{1,2})æ", month + "æ"); |
| | | if (!nt.equals(t)) { |
| | | cell.setCellValue(nt); |
| | | changed++; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | return changed; |
| | | } |
| | | |
| | | private boolean looksLikeTitle(String t) { |
| | | for (String m : TITLE_MARKERS) { |
| | | if (t.contains(m)) return true; |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | /** 对å页æ©åï¼è¿åæå
¥ä½ç½®ï¼0 åºåå·ï¼ã */ |
| | |
| | | if (headerRows.contains(r)) continue; |
| | | Row row = sh.getRow(r); |
| | | if (row == null) continue; |
| | | cloneDataCell(row, srcVal, insertCol, shiftable, insertCol); |
| | | cloneDataCell(row, srcYoy, insertCol + 1, shiftable, insertCol); |
| | | cloneDataCell(row, srcVal, insertCol, shiftable); |
| | | cloneYoyCell(row, srcYoy, insertCol + 1, insertCol, lastYearSameMonth + 2); |
| | | } |
| | | // 5) 累计åè¡¥å½æé¡¹ï¼ç´¯è®¡åæ¯åæ¯è¡¥å»å¹´åæå |
| | | int cumCol = insertCol + 2, cumYoyCol = insertCol + 3; |
| | |
| | | return insertCol; |
| | | } |
| | | |
| | | /** 表头è¡ï¼0 åºï¼ï¼å«ãå¹´+æã表头åå
æ ¼çè¡ï¼è½¨éã轮渡æä¸¤ä¸ªå â 两è¡ã */ |
| | | /** |
| | | * 表头è¡ï¼0 åºï¼ï¼è½¨éã轮渡è¿ç±»ä¸é¡µä¸¤ä¸ªåçæä¸¤è¡ã |
| | | * |
| | | * <p>2026-10-12 äºæ¬¡ä¿®æ£ï¼å
åãæä»½è¡¨å¤´æ ¼æå¤çé£ä¸è¡ãä½åºåï¼æå®çæä»½åå½ä½æ ååï¼ |
| | | * åªæè¿äºåä¸å¤§åæ¯æä»½æ ¼çè¡æç®è¡¨å¤´è¡ãåçåªè¦æ±ãæ´è¡ â¥2 个æä»½æ ¼ãï¼ |
| | | * ä¼ææ°æ®è¡éæ°å¥½è½ææ¥æåºåçæ°å¼å½æè¡¨å¤´ â æ©åæ¶ææä»½æ¥æåè¿æ°æ®è¡ |
| | | * ï¼2026-09 æ¯ç ` è´§è¿!S11`ã`åºç§!S6` ç 13 æ ¼è¢«åæ 46266ï¼ã |
| | | */ |
| | | private List<Integer> headerRowsOf(Sheet sh) { |
| | | List<Integer> rows = new ArrayList<>(); |
| | | int lastRow = Math.min(sh.getLastRowNum(), sh.getFirstRowNum() + HEADER_SCAN_ROWS); |
| | | for (int r = sh.getFirstRowNum(); r <= lastRow; r++) { |
| | | int first = sh.getFirstRowNum(); |
| | | int lastRow = Math.min(sh.getLastRowNum(), first + HEADER_SCAN_ROWS); |
| | | List<Integer> base = new ArrayList<>(); |
| | | for (int r = first; r <= lastRow; r++) { |
| | | Row row = sh.getRow(r); |
| | | if (row == null) continue; |
| | | for (int c = row.getFirstCellNum(); c >= 0 && c < row.getLastCellNum(); c++) { |
| | | if (yearMonthOf(row.getCell(c)) != null) { |
| | | rows.add(r); |
| | | break; |
| | | } |
| | | List<Integer> cols = monthColsOf(row); |
| | | if (cols.size() > base.size()) base = cols; |
| | | } |
| | | List<Integer> rows = new ArrayList<>(); |
| | | if (base.size() < 2) return rows; |
| | | int need = Math.max(2, (base.size() + 1) / 2); |
| | | for (int r = first; r <= lastRow; r++) { |
| | | Row row = sh.getRow(r); |
| | | if (row == null) continue; |
| | | int hit = 0; |
| | | for (int c : base) { |
| | | if (yearMonthOf(row.getCell(c)) != null) hit++; |
| | | } |
| | | if (hit >= need) rows.add(r); |
| | | } |
| | | return rows; |
| | | } |
| | | |
| | | /** è¡å
ææãå¹´+æãè¡¨å¤´æ ¼çåå·ï¼0 åºï¼ */ |
| | | private List<Integer> monthColsOf(Row row) { |
| | | List<Integer> cols = new ArrayList<>(); |
| | | for (int c = row.getFirstCellNum(); c >= 0 && c < row.getLastCellNum(); c++) { |
| | | if (yearMonthOf(row.getCell(c)) != null) cols.add(c); |
| | | } |
| | | return cols; |
| | | } |
| | | |
| | | private Map<Integer, Integer> monthCols(Sheet sh, int year) { |
| | |
| | | } |
| | | } |
| | | |
| | | private void cloneDataCell(Row row, int srcCol, int dstCol, Set<String> shiftable, int insertCol) { |
| | | private void cloneDataCell(Row row, int srcCol, int dstCol, Set<String> shiftable) { |
| | | Cell s = row.getCell(srcCol); |
| | | if (s == null) return; |
| | | Cell d = getOrCreate(row, dstCol); |
| | | d.setBlank(); |
| | | d.setCellStyle(s.getCellStyle()); |
| | | if (s.getCellType() == CellType.FORMULA) { |
| | | d.setCellFormula(shiftRefs(s.getCellFormula(), insertCol, shiftable)); |
| | | d.setCellFormula(shiftRefsAll(s.getCellFormula(), shiftable)); |
| | | } |
| | | } |
| | | |
| | | /** 彿忝åï¼ä¸å
é䏿å
¬å¼ï¼ç´æ¥æãæ¬æå¼ / å»å¹´åæ â 1ãéåï¼å»å¹´åæåå·²æè¡¨å¤´å®ä½ï¼ */ |
| | | private void cloneYoyCell(Row row, int srcCol, int dstCol, int valCol0, int denCol0) { |
| | | Cell s = row.getCell(srcCol); |
| | | if (s == null) return; |
| | | Cell d = getOrCreate(row, dstCol); |
| | | d.setBlank(); |
| | | d.setCellStyle(s.getCellStyle()); |
| | | if (s.getCellType() == CellType.FORMULA) { |
| | | int excelRow = row.getRowNum() + 1; |
| | | d.setCellFormula(colLetter(valCol0) + excelRow + "/" + colLetter(denCol0) + excelRow + "-1"); |
| | | } |
| | | } |
| | | |
| | | /** ç¸å¯¹å¤å¶ç¨çå¹³ç§»ï¼ææ¬é¡µ/å·²æ©å页çå¼ç¨æ´ä½ +2ï¼ä¸ååå·éå¼å¤æï¼ */ |
| | | private String shiftRefsAll(String formula, Set<String> shiftable) { |
| | | Matcher m = REF.matcher(formula); |
| | | StringBuffer sb = new StringBuffer(); |
| | | while (m.find()) { |
| | | String sheet = m.group(2) != null ? m.group(2) : m.group(3); |
| | | if ((sheet != null && !shiftable.contains(sheet)) || isFunctionName(formula, m)) { |
| | | m.appendReplacement(sb, Matcher.quoteReplacement(m.group(0))); |
| | | continue; |
| | | } |
| | | int colIdx = colIndex(m.group(5)); |
| | | m.appendReplacement(sb, Matcher.quoteReplacement(rebuild(m, sheet, colIdx + 2))); |
| | | } |
| | | m.appendTail(sb); |
| | | return sb.toString(); |
| | | } |
| | | |
| | | private void shiftAllRefs(Sheet sh, int insertCol, Set<String> shiftable) { |
| | | for (int r = 0; r <= sh.getLastRowNum(); r++) { |
| | | Row row = sh.getRow(r); |
| | |
| | | import com.trafficaudit.reportexport.calc.MidCalc; |
| | | import com.trafficaudit.reportexport.calc.PaxCalc; |
| | | import com.trafficaudit.reportexport.calc.SummaryWorkbookFiller; |
| | | import com.trafficaudit.reportexport.calc.FreightReadinessCheck; |
| | | import com.trafficaudit.reportexport.calc.SummaryMissingDataCheck; |
| | | import com.trafficaudit.reportexport.layout.SummaryLayoutExpander; |
| | | import com.trafficaudit.reportexport.calc.WycSplitCalc; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.apache.poi.ss.usermodel.Cell; |
| | | import org.apache.poi.ss.usermodel.CellStyle; |
| | | import org.apache.poi.ss.usermodel.CellType; |
| | | import org.apache.poi.ss.usermodel.Font; |
| | | import org.apache.poi.ss.usermodel.HorizontalAlignment; |
| | | import org.apache.poi.ss.usermodel.IndexedColors; |
| | | import org.apache.poi.ss.usermodel.Row; |
| | | import org.apache.poi.ss.usermodel.Sheet; |
| | | import org.apache.poi.ss.usermodel.VerticalAlignment; |
| | | import org.apache.poi.ss.usermodel.FormulaEvaluator; |
| | | import org.apache.poi.xssf.usermodel.XSSFSheet; |
| | | import org.apache.poi.xssf.usermodel.XSSFWorkbook; |
| | |
| | | private SummaryWorkbookFiller summaryWorkbookFiller; |
| | | @Resource |
| | | private SummaryLayoutExpander summaryLayoutExpander; |
| | | @Resource |
| | | private FreightReadinessCheck freightReadinessCheck; |
| | | @Resource |
| | | private SummaryMissingDataCheck missingDataCheck; |
| | | @Resource |
| | | private WycSplitCalc wycSplitCalc; |
| | | @Resource |
| | |
| | | Map<String, ScaleSplitTransport> result = new HashMap<>(); |
| | | List<ScaleSplitTransport> list = scaleSplitMapper.selectList( |
| | | new LambdaQueryWrapper<ScaleSplitTransport>() |
| | | .eq(ScaleSplitTransport::getPeriodType, "CUMULATIVE")); |
| | | .eq(ScaleSplitTransport::getPeriodType, "CUMULATIVE") |
| | | .eq(ScaleSplitTransport::getReportPeriod, period)); // 2026-10-12ï¼åªè®¤ç®æ æï¼ç¼ºæ°ç空ãä¸éé»åéä¸ä¸æ |
| | | for (ScaleSplitTransport record : list) { |
| | | if (record.getReportPeriod() == null) continue; |
| | | if (record.getReportPeriod().compareTo(period) > 0) continue; |
| | | if (!period.equals(record.getReportPeriod())) continue; |
| | | String city = RegionUtil.normalizeCityName(record.getRegionName()); |
| | | if (city == null) continue; |
| | | if ("æ¹åç".equals(city)) continue; |
| | |
| | | private ScaleSplitTransport getProvinceCumulative(String period) { |
| | | List<ScaleSplitTransport> list = scaleSplitMapper.selectList( |
| | | new LambdaQueryWrapper<ScaleSplitTransport>() |
| | | .eq(ScaleSplitTransport::getPeriodType, "CUMULATIVE")); |
| | | .eq(ScaleSplitTransport::getPeriodType, "CUMULATIVE") |
| | | .eq(ScaleSplitTransport::getReportPeriod, period)); // 2026-10-12ï¼åä¸ï¼åªè®¤ç®æ æï¼ |
| | | ScaleSplitTransport best = null; |
| | | for (ScaleSplitTransport record : list) { |
| | | if (record.getReportPeriod() == null) continue; |
| | | if (record.getReportPeriod().compareTo(period) > 0) continue; |
| | | if (!period.equals(record.getReportPeriod())) continue; |
| | | String name = RegionUtil.normalizeCityName(record.getRegionName()); |
| | | if (!"æ¹åç".equals(name)) continue; |
| | | if (best == null || record.getReportPeriod().compareTo(best.getReportPeriod()) > 0) best = record; |
| | |
| | | if (month >= 12) { |
| | | throw new RuntimeException("æ©åï¼12 æä¹åéæ°å¼ä¸æ¬ï¼è·¨å¹´è§åå¦è¡å¤ç"); |
| | | } |
| | | // 2026-10-12ï¼æ©å以ãå½æäº§ç©ã为åºï¼äº§ç©å«å½æåçå¼ä¸å
¬å¼ï¼æ¯ççå½æåæ¯ç©ºçï¼ |
| | | // ç´æ¥æ¿æ¯çæ©åä¼æå½ææ°æ®ä¸¢æç©ºæ´ï¼å¯¼è´ç´¯è®¡æ¼æï¼ã产ç©åèªå¯¼åºè½çç¼åï¼ç¼ºäº§ç©æ¶å鿝çå¹¶åè¦ã |
| | | File source = resolveLatestProduct(period); |
| | | boolean fromProduct = source != null; |
| | | if (source == null) { |
| | | source = resolveSummaryMother(period, "month"); |
| | | if (source != null) { |
| | | log.warn("æ±æ»å¤§è¡¨æ©åï¼æªæ¾å° {} ç产ç©ç¼åï¼åéç¨æ¯çæ©åââåå²åé缺å°å½ææ°æ®ï¼ç´¯è®¡å¯è½æ¼æ", period); |
| | | } |
| | | } |
| | | if (source == null) { |
| | | throw new RuntimeException("æªæ¾å°æ±æ»è¡¨æ¯çæäº§ç©ï¼docs/çææ±æ»å¤§è¡¨ï¼ï¼" + year + "å¹´" + month + "æéè·¯è¿è¾éæ±æ»è¡¨.xlsx"); |
| | | } |
| | | try (InputStream in = new FileInputStream(source)) { |
| | | byte[] out = summaryLayoutExpander.expand(org.apache.poi.util.IOUtils.toByteArray(in), year, month + 1); |
| | | log.info("æ±æ»å¤§è¡¨æ©å宿ï¼{}{} -> {}å¹´{}æçå¼ï¼åªåçå¼ã彿åç空ï¼", |
| | | fromProduct ? "产ç©" : "æ¯ç", source.getName(), year, month + 1); |
| | | return out; |
| | | } |
| | | } |
| | | |
| | | /** å该æãææ°äº§ç©ãï¼å¯¼åºè½çç¼å _cache/summaryWorkbook_<period>_month_v*.xlsxï¼æä¿®æ¹æ¶é´åææ°ï¼ */ |
| | | private File resolveLatestProduct(String period) { |
| | | try { |
| | | String rel = summaryTemplateDir; |
| | | while (rel != null && rel.startsWith("./")) rel = rel.substring(2); |
| | | String[] roots = { |
| | | summaryTemplateDir, |
| | | System.getProperty("user.dir") + "/" + rel, |
| | | System.getProperty("user.dir") + "/../" + rel |
| | | }; |
| | | String prefix = "summaryWorkbook_" + period + "_month_"; |
| | | for (String root : roots) { |
| | | if (root == null || root.trim().isEmpty()) continue; |
| | | File dir = new File(root, "_cache"); |
| | | if (!dir.isDirectory()) continue; |
| | | File[] files = dir.listFiles((d, name) -> name.startsWith(prefix) && name.endsWith(".xlsx")); |
| | | if (files == null || files.length == 0) continue; |
| | | File latest = files[0]; |
| | | for (File f : files) { |
| | | if (f.lastModified() > latest.lastModified()) latest = f; |
| | | } |
| | | return latest; |
| | | } |
| | | return null; |
| | | } catch (Exception e) { |
| | | log.warn("æ±æ»å¤§è¡¨æ©åï¼æ¥æ¾äº§ç©ç¼å失败ï¼å¿½ç¥ï¼ï¼{}", e.getMessage()); |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | /** åªä¿®æ£å½ææ¯çé 7 个æ é¢é¡µçæ 颿åï¼ææ¯çææ¬¡éåï¼ç¨äºä¿®å¤æ»åæ é¢ï¼ã */ |
| | | public byte[] fixSummaryTitles(String period) throws Exception { |
| | | if (period == null || !period.contains("-")) { |
| | | throw new RuntimeException("æ é¢ä¿®æ£ä»
æ¯æææï¼è¯·æå® period=YYYY-MM"); |
| | | } |
| | | int year = Integer.parseInt(period.split("-")[0]); |
| | | int month = monthOf(period, "month"); |
| | | File mother = resolveSummaryMother(period, "month"); |
| | | if (mother == null) { |
| | | throw new RuntimeException("æªæ¾å°æ±æ»è¡¨æ¯çï¼docs/çææ±æ»å¤§è¡¨ï¼ï¼" + year + "å¹´" + month + "æéè·¯è¿è¾éæ±æ»è¡¨.xlsx"); |
| | | } |
| | | try (InputStream in = new FileInputStream(mother)) { |
| | | byte[] out = summaryLayoutExpander.expand(org.apache.poi.util.IOUtils.toByteArray(in), year, month + 1); |
| | | log.info("æ±æ»å¤§è¡¨æ©å宿ï¼{}å¹´{}ææ¯ç -> {}å¹´{}æçå¼ï¼åªåçå¼ãæ°æ®ç空ï¼", year, month, year, month + 1); |
| | | return out; |
| | | return summaryLayoutExpander.fixTitles(org.apache.poi.util.IOUtils.toByteArray(in), year, month); |
| | | } |
| | | } |
| | | |
| | |
| | | int effMonth = (toMonth != null && toMonth > 0 && toMonth < month) ? toMonth : month; |
| | | List<String> dbProblems = new ArrayList<>(); |
| | | String effPeriod = String.format("%04d-%02d", year, effMonth); |
| | | dbProblems.addAll(freightReadinessCheck.check(effPeriod)); // B4ï¼è´§è¿å£å¾å¯¼åºå使£ï¼6 æ¡è§åï¼ |
| | | dbProblems.addAll(missingDataCheck.check(effPeriod)); // ç¼ºæ°æ¸
åï¼è¯¥æçæ°æ²¡æï¼ |
| | | reportProgress(progress, 22, "åå¡«å½ææ°æ®"); |
| | | int dbFilled = summaryWorkbookFiller.fillMonthlyLedger(wb, effPeriod, year, effMonth, dbProblems); |
| | | int yoyFilled = summaryWorkbookFiller.ensureMonthYoyFormulas(wb, year, effMonth); |
| | |
| | | reportProgress(progress, 45, "å·æ°æå页ä¸è·¨é¡µåæé¡µ"); |
| | | int rankRefreshed = refreshSummaryRankSheets(wb, effPeriod, "month"); |
| | | int cumulativeRefreshed = refreshSummaryCumulativeSheets(wb, year, effMonth); // åå¼ è·¨é¡µåæé¡µéæ¥è¡¨æå·æ° |
| | | int widthApplied = applySummaryReferenceLayout(wb); |
| | | // 2026-10-12ï¼åç¨ãå¥ç¨å¤é¨åç
§ä»¶ãçæ_éè·¯è¿è¾éæ±æ»è¡¨.xlsxãå宽ãââçå¼ä¸å¾ä»¥å½ææ¯ç为åï¼ |
| | | // é¿å
æ¯çä¸åç
§ä»¶ä¸¤å¤ç»´æ¤ï¼æ¯ç已并å
¥åç
§ä»¶çå宽/éèï¼ã |
| | | int widthApplied = 0; |
| | | log.info("æ±æ»å·¥ä½ç°¿æ°æ®é©±å¨å填宿ï¼åå
¥å½æå¼æ ¼æ°={}ï¼è¡¥å彿忝å
¬å¼æ ¼æ°={}ï¼è¡¥å彿å计å
¬å¼æ ¼æ°={}ï¼" |
| | | + "ä¿®æ£ç´¯è®¡åæ¯åæ¯æ ¼æ°={}ï¼å·æ°æå页æ°å¼æ ¼æ°={}ï¼å·æ°å页ï¼å叿±æ»/åå¸åå¸å·/ä¸å£å¾åæ/éè·¯è¿è¾å¨è½¬éï¼æ°å¼æ ¼æ°={}ï¼" |
| | | + "æãçæ_éè·¯è¿è¾éæ±æ»è¡¨ã对é½å宽忰={}ï¼ç¼ºæ°æç¤º={}", |
| | |
| | | log.info("æ±æ»å·¥ä½ç°¿å
¬å¼éç®å®æï¼æå {} æ ¼ï¼å¤±è´¥ {} æ ¼ï¼å¤±è´¥æ ¼ä¿çæ¯çæ§ç¼åå¼ï¼æå¼æ¶æ fullCalcOnLoad éç®ï¼", recalc[0], recalc[1]); |
| | | if (!wb.getCTWorkbook().isSetCalcPr()) wb.getCTWorkbook().addNewCalcPr(); |
| | | wb.getCTWorkbook().getCalcPr().setFullCalcOnLoad(true); // è¿å/æ¹åå
¬å¼åæå¼å³éç® |
| | | appendMissingDataSheet(wb, effPeriod, dbProblems); // 2026-10-12ï¼ç¼ºæ°/使£ç»æåè¿çæä»¶ï¼ç¨æ·è¦æ±ï¼ |
| | | reportProgress(progress, 95, "ååºæä»¶"); |
| | | return toBytes(wb); |
| | | } finally { |
| | | ZipSecureFile.setMinInflateRatio(savedZipRatio); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * çæä»¶ãæ°æ®ç¼ºå¤±æ¸
åã页ï¼2026-10-12ï¼ç¨æ·è¦æ±ï¼ã |
| | | * |
| | | * <p>ææ¬æ¬¡å¯¼åºçãç¼ºæ°æ¸
åãä¸ãè´§è¿åºæ°ä½æ£ãç»ææ¾å¼åè¿äº§ç©ï¼ç¨æ·æ¿å°æä»¶å³å¯çå°æ¬æ¥è¡¨æ |
| | | * æåªäºæ°æ²¡å°ãåªäºæ°ä¸åçï¼ä¸å¿
ç¿»å¯¼åºæ¥å¿ãåªè¯»æç¤ºé¡µï¼ä¸å䏿¬è¡¨è®¡ç®ã |
| | | */ |
| | | private void appendMissingDataSheet(XSSFWorkbook wb, String period, List<String> problems) { |
| | | String sheetName = "æ°æ®ç¼ºå¤±æ¸
å"; |
| | | int old = wb.getSheetIndex(sheetName); |
| | | if (old >= 0) wb.removeSheetAt(old); // æ¯çç±ãå½æäº§ç©ãæ´¾çæ¶å¯è½å·²å¸¦æ¬é¡µï¼å
ç§»é¤é¿å
éå |
| | | XSSFSheet sh = wb.createSheet(sheetName); |
| | | |
| | | CellStyle titleStyle = wb.createCellStyle(); |
| | | Font titleFont = wb.createFont(); |
| | | titleFont.setBold(true); |
| | | titleFont.setFontHeightInPoints((short) 14); |
| | | titleStyle.setFont(titleFont); |
| | | |
| | | CellStyle metaStyle = wb.createCellStyle(); |
| | | Font metaFont = wb.createFont(); |
| | | metaFont.setFontHeightInPoints((short) 10); |
| | | metaFont.setColor(IndexedColors.GREY_50_PERCENT.getIndex()); |
| | | metaStyle.setFont(metaFont); |
| | | |
| | | CellStyle sectionStyle = wb.createCellStyle(); |
| | | Font sectionFont = wb.createFont(); |
| | | sectionFont.setBold(true); |
| | | sectionStyle.setFont(sectionFont); |
| | | |
| | | CellStyle bodyStyle = wb.createCellStyle(); |
| | | bodyStyle.setWrapText(true); |
| | | bodyStyle.setVerticalAlignment(VerticalAlignment.TOP); |
| | | |
| | | List<String> missing = new ArrayList<>(); |
| | | List<String> check = new ArrayList<>(); |
| | | List<String> other = new ArrayList<>(); |
| | | if (problems != null) { |
| | | for (String p : problems) { |
| | | if (p == null || p.trim().isEmpty()) continue; |
| | | if (p.startsWith("缺æ°")) missing.add(p); |
| | | else if (p.startsWith("è´§è¿ä½æ£")) check.add(p); |
| | | else other.add(p); |
| | | } |
| | | } |
| | | |
| | | int r = 0; |
| | | Row tRow = sh.createRow(r++); |
| | | Cell tCell = tRow.createCell(0); |
| | | tCell.setCellValue("æ°æ®ç¼ºå¤±æ¸
å"); |
| | | tCell.setCellStyle(titleStyle); |
| | | |
| | | Row mRow = sh.createRow(r++); |
| | | Cell mCell = mRow.createCell(0); |
| | | mCell.setCellValue("æ¥è¡¨æ " + period + "ãçææ¶é´ " |
| | | + new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm").format(new java.util.Date()) |
| | | + "ãå
± " + (missing.size() + check.size() + other.size()) + " æ¡"); |
| | | mCell.setCellStyle(metaStyle); |
| | | |
| | | Row nRow = sh.createRow(r++); |
| | | Cell nCell = nRow.createCell(0); |
| | | nCell.setCellValue("æ¬é¡µç±ç¨åºèªå¨çæï¼åªè¯»æç¤ºï¼ï¼ä¸ãç¼ºæ°æ¸
åï¼è¯¥æçæ°æ²¡æï¼äºãæ°æ®ä½æ£ï¼æ°å¼ä¸åçï¼å¦è§ä¸ä¸ºè´ï¼ãä¸å䏿¬è¡¨è®¡ç®ã"); |
| | | nCell.setCellStyle(metaStyle); |
| | | |
| | | r++; |
| | | r = writeProblemSection(sh, r, "ä¸ãç¼ºæ°æ¸
åï¼è¯¥æçæ°æ²¡æï¼", missing, sectionStyle, bodyStyle); |
| | | r = writeProblemSection(sh, r, "äºãæ°æ®ä½æ£ï¼æ°å¼ä¸åçï¼", check, sectionStyle, bodyStyle); |
| | | if (!other.isEmpty()) { |
| | | r = writeProblemSection(sh, r, "ä¸ãå
¶å®æç¤º", other, sectionStyle, bodyStyle); |
| | | } |
| | | |
| | | sh.setColumnWidth(0, 6 * 256); |
| | | sh.setColumnWidth(1, 120 * 256); |
| | | sh.createFreezePane(0, 3); |
| | | } |
| | | |
| | | /** åä¸ä¸ªæ¸
åååºï¼æ é¢ + éæ¡ï¼ï¼è¿åä¸ä¸ä¸ªå¯åè¡å· */ |
| | | private int writeProblemSection(XSSFSheet sh, int rowIdx, String title, List<String> lines, |
| | | CellStyle sectionStyle, CellStyle bodyStyle) { |
| | | Row hr = sh.createRow(rowIdx++); |
| | | Cell hc = hr.createCell(0); |
| | | hc.setCellValue(title); |
| | | hc.setCellStyle(sectionStyle); |
| | | sh.addMergedRegion(new CellRangeAddress(rowIdx - 1, rowIdx - 1, 0, 1)); |
| | | if (lines.isEmpty()) { |
| | | Row er = sh.createRow(rowIdx++); |
| | | er.createCell(0).setCellValue("â"); |
| | | er.createCell(1).setCellValue("æ "); |
| | | return rowIdx; |
| | | } |
| | | int i = 1; |
| | | for (String line : lines) { |
| | | Row row = sh.createRow(rowIdx++); |
| | | row.createCell(0).setCellValue(i++); |
| | | Cell c = row.createCell(1); |
| | | c.setCellValue(line); |
| | | c.setCellStyle(bodyStyle); |
| | | } |
| | | return rowIdx; |
| | | } |
| | | |
| | | /** 导åºè¿åº¦åè°ï¼å¼æ¥å¯¼åºä»»å¡ç¨ï¼ï¼åè°èªèº«å¼å¸¸ä¸å½±åå¯¼åº */ |
| | |
| | | int colOff = dstTitle.getColumnIndex() - sc0; |
| | | int h = rankBlockHeight(src, sr0, sc0); |
| | | int w = rankBlockWidth(src, sr0, sc0); |
| | | // 2026-10-12ï¼æ´å缺æ°ï¼æºä¾§ä¸ä¸ªæ°å¼é½æ²¡æï¼å¦ 9 æç¼ºâ¡â¢æ¶çå¨è½¬éæåï¼â ç®æ ååªçæ é¢ä¸æ ç¾ï¼ |
| | | // æ°æ®/æå/å æ¯ä¸å¾ç空ï¼å¦åæ¯çç RANK/å æ¯å
¬å¼ä¼å¨ç©ºåä¸ç®åºãå
¨ 1 + #DIV/0!ãã |
| | | boolean blockHasData = false; |
| | | for (int r = 1; r < h && !blockHasData; r++) { |
| | | Row sr = src.getRow(sr0 + r); |
| | | if (sr == null) continue; |
| | | for (int c = 0; c < w; c++) { |
| | | Cell sc0c = sr.getCell(sc0 + c); |
| | | if (sc0c != null && rankNumeric(sc0c, ev) != null) { |
| | | blockHasData = true; |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | for (int r = 0; r < h; r++) { |
| | | Row srcRow = src.getRow(sr0 + r); |
| | | if (srcRow == null) continue; |
| | | Row dstRow = dst.getRow(sr0 + r + rowOff); |
| | | if (dstRow == null) dstRow = dst.createRow(sr0 + r + rowOff); |
| | | for (int c = 0; c < w; c++) { |
| | | Cell sc = srcRow.getCell(sc0 + c); |
| | | if (sc == null) continue; |
| | | int dc = sc0 + c + colOff; |
| | | Cell sc = srcRow.getCell(sc0 + c); |
| | | if (!blockHasData && r > 0) { |
| | | // æ´å缺æ°ï¼åªä¿çæ ç¾ï¼å¸å·å/ææ åï¼ï¼æ°æ®ä¸æå/å æ¯éç®ç»æä¸å¾ç空 |
| | | if (sc == null || !isRankLabel(sc)) clearRankCell(dstRow, dc); |
| | | continue; |
| | | } |
| | | if (sc == null) { |
| | | // 2026-10-12ï¼æºä¾§æ´æ ¼ç¼ºå¤± = ç¼ºæ° â ç®æ æ ¼æã缺æ°çç©ºãæ¸
空ï¼ä¸åæ²¿ç¨æ¯çä¸ä¸æ¥è¡¨æçæ§å¼ï¼ |
| | | if (r > 0) clearRankCell(dstRow, dc); |
| | | continue; |
| | | } |
| | | if (r == 0) { |
| | | // æ é¢è¡ï¼åæ¥åæ 颿æ¡ãæ¯çæ 颿¯ inlineStrï¼POI ç setCellValue åªå <v>ã |
| | | // 䏿´æ° <is>ï¼Excel ä¼ç»§ç»æ¾ç¤ºæ§æä»½ï¼å¿
é¡»å
setBlank æ¸
æååã |
| | |
| | | continue; |
| | | } |
| | | Double v = rankNumeric(sc, ev); |
| | | if (v == null) continue; |
| | | if (v == null) { |
| | | // 2026-10-12ï¼æºä¾§æ°å¼ä½ä¸ºç©ºï¼å¦ç¼ºâ¡å
¨å£å¾æ¶æå表çãè§ä¸ãåï¼= ç¼ºæ° â çç©ºï¼ |
| | | // æºä¾§æ¯æ ç¾ææ¬ï¼å¸å·å/ææ åï¼æ¶ä¸æ¯ç¼ºæ°ï¼ä¿çæ¯çèªå¸¦å
容ã |
| | | if (!isRankLabel(sc)) clearRankCell(dstRow, dc); |
| | | continue; |
| | | } |
| | | if (dcCell == null) dcCell = rankCell(dstRow, dc); |
| | | dcCell.setCellValue(v); |
| | | copied++; |
| | |
| | | Cell left = col > 0 ? row.getCell(col - 1) : null; |
| | | if (left != null) c.setCellStyle(left.getCellStyle()); |
| | | return c; |
| | | } |
| | | |
| | | /** |
| | | * ç®æ æ ¼æã缺æ°çç©ºãæ¸
空ã |
| | | * |
| | | * <p>2026-10-12ï¼è¿éè¿æ¯çèªå¸¦çæå/å æ¯å
¬å¼æ ¼ä¸èµ·æ¸
ï¼ç¼ºæ°æä¸æ¾ç¤º"ä¸ä¸ææ®çå¼ + å
¨ 1 æå + #DIV/0!"ï¼ï¼ |
| | | * ä¸ãçæ_è´§è¿éæå/å¨è½¬éæå/ä¸å£å¾æåãè¿äºç¨æ·æ¥å¸¸æ ¸å¯¹ä»¶ç空æä¸è´ï¼ä¸ææºä¾§ææ°æ¶ææºä¾§éå忥ã |
| | | * æåæ ç¾æ ¼ä¸å¨ã |
| | | */ |
| | | private void clearRankCell(Row dstRow, int col) { |
| | | Cell c = dstRow.getCell(col); |
| | | if (c == null) return; |
| | | if (c.getCellType() != CellType.STRING) c.setBlank(); |
| | | } |
| | | |
| | | /** æºä¾§æ¯å¦ä¸ºæåæ ç¾ï¼å¸å·å/ææ åï¼ââæ ç¾ä¸æ¯ç¼ºæ°ï¼ä¸æ¸
ç©ºç®æ æ ¼ */ |
| | | private boolean isRankLabel(Cell c) { |
| | | if (c.getCellType() != CellType.STRING) return false; |
| | | String v = c.getStringCellValue(); |
| | | if (v == null) return false; |
| | | String x = v.replace(",", "").trim(); |
| | | if (x.isEmpty()) return false; |
| | | try { |
| | | Double.parseDouble(x); |
| | | return false; |
| | | } catch (NumberFormatException ignore) { |
| | | return true; |
| | | } |
| | | } |
| | | |
| | | /** å
¬å¼å
ææ A1 å¼ç¨æ´ä½å¹³ç§»ï¼å +dColãè¡ +dRowï¼SUM/RANK ç彿°å忥æ¬å·ä¸ä¼è¢«å¹é
ï¼ */ |
| | |
| | | if (sh == null) return 0; |
| | | smSetTitle(sh, 0, 0, "åå¸å®¢è¿å®¢è¿éåå¸å·1-" + month + "æç´¯è®¡æ
åµ"); |
| | | smSetTitle(sh, 22, 0, "åå¸å®¢è¿å®¢è¿å¨è½¬éåå¸å·1-" + month + "æç´¯è®¡æ
åµ"); |
| | | smSetTitle(sh, 45, 0, "åå¸å®¢è¿å®¢è¿éåå¸å·" + month + "ææ
åµ"); |
| | | smSetTitle(sh, 67, 0, "åå¸å®¢è¿å®¢è¿å¨è½¬éåå¸å·" + month + "ææ
åµ"); |
| | | smSetTitle(sh, 44, 0, "åå¸å®¢è¿å®¢è¿éåå¸å·" + month + "ææ
åµ"); // 0åº44 = Excel 第45è¡ï¼æ¯çéæ é¢å¨ A45ï¼A46 æ¯"åä½"è¡ï¼ |
| | | smSetTitle(sh, 66, 0, "åå¸å®¢è¿å®¢è¿å¨è½¬éåå¸å·" + month + "ææ
åµ"); // 0åº66 = Excel 第67è¡ï¼åä¸ï¼ |
| | | int n = 0; |
| | | n += smWriteCityByRegionBlock(sh, 4, true, curCum, lastCum); |
| | | n += smWriteCityByRegionBlock(sh, 26, false, curCum, lastCum); |
| | |
| | | assertFalse(java.util.Arrays.equals(twoDecimals, threeDecimals)); |
| | | } |
| | | |
| | | @Test |
| | | void rendersLineChartWithConfiguredDayLabels() { |
| | | HolidayTrendChartService service = new HolidayTrendChartService(); |
| | | HolidayTrendChartService.ChartSeries congestion = new HolidayTrendChartService.ChartSeries(); |
| | | congestion.setName("\u5e73\u5747\u62e5\u6324\u5ea6"); |
| | | congestion.setValues(java.util.Arrays.asList( |
| | | new BigDecimal("0.50"), new BigDecimal("0.48"), new BigDecimal("0.47"), |
| | | new BigDecimal("0.49"), new BigDecimal("0.49"), new BigDecimal("0.49"), |
| | | new BigDecimal("0.47"))); |
| | | java.util.List<String> sevenDays = java.util.Arrays.asList( |
| | | "10\u67081\u65e5", "10\u67082\u65e5", "10\u67083\u65e5", "10\u67084\u65e5", |
| | | "10\u67085\u65e5", "10\u67086\u65e5", "10\u67087\u65e5"); |
| | | |
| | | byte[] custom = service.lineChart("\u62e5\u6324\u5ea6", |
| | | java.util.Collections.singletonList(congestion), false, 2, sevenDays, 1100, 590); |
| | | byte[] defaults = service.lineChart("\u62e5\u6324\u5ea6", |
| | | java.util.Collections.singletonList(congestion), false, 2, 1100, 590); |
| | | |
| | | assertPng(custom); |
| | | assertPng(defaults); |
| | | assertFalse(java.util.Arrays.equals(custom, defaults), |
| | | "custom day labels should change the rendered axis"); |
| | | } |
| | | |
| | | @Test |
| | | void pinsRequestedLineLabelToFixedAnchor() throws Exception { |
| | | HolidayTrendChartService service = new HolidayTrendChartService(); |
| | | HolidayTrendChartService.ChartSeries series = new HolidayTrendChartService.ChartSeries(); |
| | | series.setName("2026\u5e74"); |
| | | series.setValues(java.util.Collections.singletonList(new BigDecimal("72.4"))); |
| | | java.util.List<HolidayTrendChartService.ChartSeries> single = |
| | | java.util.Collections.singletonList(series); |
| | | |
| | | byte[] plain = service.lineChart("\u4e07\u4eba\u6b21", single, false, 2, 1100, 590); |
| | | byte[] emptyOverrides = service.lineChart("\u4e07\u4eba\u6b21", single, false, 2, |
| | | HolidayTrendChartService.DAY_LABELS, 1100, 590, |
| | | java.util.Collections.emptyList()); |
| | | assertArrayEquals(plain, emptyOverrides, |
| | | "an empty override list must keep the automatic placement"); |
| | | |
| | | byte[] unmatched = service.lineChart("\u4e07\u4eba\u6b21", single, false, 2, |
| | | HolidayTrendChartService.DAY_LABELS, 1100, 590, |
| | | java.util.Collections.singletonList(new HolidayTrendChartService.LineLabelOverride( |
| | | 0, 7, HolidayTrendChartService.LineLabelAnchor.ABOVE))); |
| | | assertArrayEquals(plain, unmatched, |
| | | "an override for a label that does not exist must not change rendering"); |
| | | |
| | | byte[] above = service.lineChart("\u4e07\u4eba\u6b21", single, false, 2, |
| | | HolidayTrendChartService.DAY_LABELS, 1100, 590, |
| | | java.util.Collections.singletonList(new HolidayTrendChartService.LineLabelOverride( |
| | | 0, 0, HolidayTrendChartService.LineLabelAnchor.ABOVE))); |
| | | byte[] below = service.lineChart("\u4e07\u4eba\u6b21", single, false, 2, |
| | | HolidayTrendChartService.DAY_LABELS, 1100, 590, |
| | | java.util.Collections.singletonList(new HolidayTrendChartService.LineLabelOverride( |
| | | 0, 0, HolidayTrendChartService.LineLabelAnchor.BELOW))); |
| | | |
| | | assertPng(above); |
| | | assertPng(below); |
| | | assertFalse(java.util.Arrays.equals(above, below), |
| | | "ABOVE and BELOW anchors must render differently"); |
| | | assertTrue(meanSeriesColorY(above) < meanSeriesColorY(below), |
| | | "the ABOVE anchor must place the label higher than the BELOW anchor"); |
| | | } |
| | | |
| | | private double meanSeriesColorY(byte[] data) throws Exception { |
| | | BufferedImage image = ImageIO.read(new ByteArrayInputStream(data)); |
| | | assertNotNull(image); |
| | | int target = 0xC0504D; |
| | | long count = 0; |
| | | long sum = 0; |
| | | for (int y = 0; y < image.getHeight() - 60; y++) { |
| | | for (int x = 0; x < image.getWidth(); x++) { |
| | | if ((image.getRGB(x, y) & 0xFFFFFF) == target) { |
| | | count++; |
| | | sum += y; |
| | | } |
| | | } |
| | | } |
| | | assertTrue(count > 0, "expected series-coloured pixels for the item label"); |
| | | return (double) sum / count; |
| | | } |
| | | |
| | | private void assertNoLargeLightGrayBackground(byte[] data) throws Exception { |
| | | BufferedImage image = ImageIO.read(new ByteArrayInputStream(data)); |
| | | int matches = 0; |
| | |
| | | <el-button size="small" v-else type="primary" @click="closeTask">å
³é</el-button> |
| | | </span> |
| | | </el-dialog> |
| | | <!-- æ±æ»å¤§è¡¨å¯¼åºåç¼ºæ°æ¸
åå¼¹çªï¼2026-10-12 ç¨æ·è¦æ±ï¼ç¼ºæ°æå°å°é¢ä¸ï¼ --> |
| | | <el-dialog title="çæåæç¤º - æ°æ®ç¼ºå¤±æ¸
å" :visible.sync="missDialogVisible" width="720px" top="6vh"> |
| | | <div class="miss-body"> |
| | | <p class="miss-lead">æ¥è¡¨æ <b>{{ missDialogPeriod }}</b>ï¼ä»¥ä¸æ°æ®æ¬æ¬¡çæä¼ç¼ºå¤±/å¼å¸¸ï¼çæä»¶æ«å°¾ä¹ä¼éãæ°æ®ç¼ºå¤±æ¸
åã页ã</p> |
| | | <template v-if="missDialogMissing.length"> |
| | | <div class="miss-sec">ä¸ãç¼ºæ°æ¸
åï¼è¯¥æçæ°æ²¡æï¼ãå
± {{ missDialogMissing.length }} æ¡</div> |
| | | <ul class="miss-list"><li v-for="(t, i) in missDialogMissing" :key="'m' + i">{{ t }}</li></ul> |
| | | </template> |
| | | <template v-if="missDialogCheck.length"> |
| | | <div class="miss-sec">äºãæ°æ®ä½æ£ï¼æ°å¼ä¸åçï¼ãå
± {{ missDialogCheck.length }} æ¡</div> |
| | | <ul class="miss-list"><li v-for="(t, i) in missDialogCheck" :key="'c' + i">{{ t }}</li></ul> |
| | | </template> |
| | | <p class="miss-tip">缺æ°å¤ä¸ºæºæ°æ®å°æªå¯¼å
¥ï¼å¯¼å
¥åéæ°çæå³å¯ï¼ä½æ£é¡¹è¯·æ ¸å¯¹æºæä»¶ã</p> |
| | | </div> |
| | | <span slot="footer"> |
| | | <el-button size="small" @click="missDialogVisible = false">åæ¶</el-button> |
| | | <el-button size="small" type="primary" @click="confirmMissDialog">ä»è¦çæ</el-button> |
| | | </span> |
| | | </el-dialog> |
| | | </el-main> |
| | | </el-container> |
| | | </el-container> |
| | |
| | | taskFileName: '', |
| | | taskFileSize: 0, |
| | | taskKey: 'summaryWorkbook', |
| | | // 导åºåç¼ºæ°æ¸
åå¼¹çªï¼2026-10-12ï¼ï¼ç¼ºæ°/使£æå°å°é¢ä¸ï¼ç¡®è®¤ååçæ |
| | | missDialogVisible: false, |
| | | missDialogPeriod: '', |
| | | missDialogMissing: [], |
| | | missDialogCheck: [], |
| | | missDialogRefresh: false, |
| | | modules: [ |
| | | { |
| | | key: 'summary', title: 'æ±æ»å¤§è¡¨', items: [ |
| | |
| | | }) |
| | | .catch(err => this.failWith(err)) |
| | | }, |
| | | // 导åºåç¼ºæ°æ£æ¥ï¼è°åªè¯»æ¥å£æç¼ºæ°/使£ç»ææå°ç¨æ·é¢åï¼ç¡®è®¤ååèµ°å¼æ¥çæä»»å¡ |
| | | generateSummaryWorkbook(refresh) { |
| | | if (!this.period) { this.$message.warning('请å
鿩卿åæä»½'); return } |
| | | const params = { period: this.period } |
| | | Promise.all([ |
| | | api.get('/report/summaryWorkbook/missing', { params, timeout: 60000 }).catch(() => null), |
| | | api.get('/report/summaryWorkbook/freightCheck', { params, timeout: 60000 }).catch(() => null) |
| | | ]).then(res => { |
| | | const miss = (res[0] && res[0].code === 200 && res[0].data) || [] |
| | | const chk = (res[1] && res[1].code === 200 && res[1].data) || [] |
| | | if (!miss.length && !chk.length) { this.startSummaryTask(refresh); return } |
| | | this.missDialogPeriod = this.period |
| | | this.missDialogMissing = miss |
| | | this.missDialogCheck = chk |
| | | this.missDialogRefresh = refresh |
| | | this.missDialogVisible = true |
| | | }) |
| | | }, |
| | | confirmMissDialog() { |
| | | this.missDialogVisible = false |
| | | this.startSummaryTask(this.missDialogRefresh) |
| | | }, |
| | | restartTask() { this.startSummaryTask(true) }, |
| | | closeTask() { this.taskVisible = false; this.stopTaskPolling(); this.loading = '' }, |
| | | // çæä¸æ¶èµ·å¼¹çªï¼ä»»å¡ç»§ç»å¨åå°æ§è¡ï¼å®æåä»ä¼èªå¨ä¸è½½ï¼ä¸åæ¥åæ¥å¯¼åºçãç¹å®å³ä¸è½½ãä¸è´ï¼ |
| | |
| | | const doExport = () => { |
| | | if (items.length === 1) { |
| | | // æ±æ»å¤§è¡¨çæä¸æ¬¡çº¦ 10~20 ç§ï¼èµ°å¼æ¥ä»»å¡ï¼è¿åº¦å¼¹çªï¼ï¼é¿å
ç¹å»åé¿æ¶é´æ ååº |
| | | if (items[0].key === 'summaryWorkbook') { this.startSummaryTask(false); return } |
| | | if (items[0].key === 'summaryWorkbook') { this.generateSummaryWorkbook(false); return } |
| | | api.get('/report/export/' + items[0].key, { params, responseType: 'blob', timeout: 120000 }) |
| | | .then(res => { this.downloadBlob(res, this.fileNameOf(items[0])); this.$message.success('æ¥è¡¨å¯¼åºæå'); this.loading = '' }) |
| | | .catch(err => this.failWith(err)) |
| | |
| | | .task-meta { margin: 2px 0; font-size: 12px; color:#909399; line-height: 1.6; } |
| | | .task-error { margin: 8px 0 0; font-size: 13px; color:#F56C6C; word-break: break-all; } |
| | | .task-done { margin: 8px 0 0; font-size: 13px; color:#67C23A; line-height: 1.6; } |
| | | /* 导åºåç¼ºæ°æ¸
åå¼¹çª */ |
| | | .miss-body { max-height: 62vh; overflow-y: auto; } |
| | | .miss-lead { font-size: 13px; color: #303133; margin: 0 0 10px; line-height: 1.7; } |
| | | .miss-sec { font-size: 13px; font-weight: 600; color: #F56C6C; margin: 10px 0 4px; } |
| | | .miss-list { margin: 0; padding-left: 20px; font-size: 13px; color: #303133; line-height: 1.7; } |
| | | .miss-list li { margin: 2px 0; } |
| | | .miss-tip { font-size: 12px; color: #909399; margin: 10px 0 0; } |
| | | </style> |
| | | |
| | | <style> |