| .gitignore | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| docs/交接文档_2026-09-21.md | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| docs/功能测试报告/功能测试报告_汇总大表导出结果落盘缓存复用_2026-09-22.md | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| docs/工作日结/工作日结_2026-09-22.md | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| traffic-audit-server/src/main/java/com/trafficaudit/reportexport/controller/ReportExportController.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| traffic-audit-server/src/main/java/com/trafficaudit/reportexport/service/ReportExportTaskService.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| traffic-audit-web/src/views/ReportExport.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
.gitignore
@@ -97,5 +97,8 @@ _*.zip _bak_* # æ±æ»å¤§è¡¨å¯¼åºç»æè½çç¼åï¼å¤ç¨ä¸ä¸æ¬¡çæç»æç¨ï¼ä¸å ¥åºï¼ docs/çææ±æ»å¤§è¡¨/_cache/ # Codex åºç¨å¨æ¬ä»åºæ ¹ç®å½äº§çç产ç©ç®å½ï¼ä¿ç卿¬å°ï¼ä¸å ¥åºï¼ outputs/ docs/½»½ÓÎĵµ_2026-09-21.md
@@ -217,7 +217,7 @@ | P2-3 | `applySummaryReferenceLayout()` æ**ååºå·**å¥å宽/éè | åç §ä»¶ä¸å ååå°±æ´ä½éä½ï¼åºæ¹ä¸ºæè¡¨å¤´æåå¹é | â | | P2-4 | 缺æ¯çæç¤ºï¼T08ï¼æªå®æµ | ææµè¯å¯æ¬æ¶è¡¥æµ | â | | P2-5 | é¨ç½²å å æ¯**带 AJ åçæ§æ¯ç** | `deploy/traffic-audit-deploy-20260913/backend/docs/çææ±æ»å¤§è¡¨/`ã`_deploytest/`ï¼ç¨æ· 09-20 æç¡®ãé¨ç½²å å ä¸ç®¡ãï¼åå åå¿ é¡»åæ¥ | ⸠| | P2-6 | 导åºç¼åæ¯**å åæ** | è¿ç¨éå¯å³å¤±æï¼å¦éè·¨éå¯/å¤äººå ±ç¨çã导åºè®°å½ã页éå¦å | â | | P2-6 | ~~导åºç¼åæ¯å åæ~~ | **已解å³ï¼2026-09-22ï¼**ï¼ç»æè½çå° `docs/çææ±æ»å¤§è¡¨/_cache/`ï¼æä»¶åå¸¦æ°æ®çæ¬ `MAX(import_batch.id)`ï¼ï¼å å â è½ç â çç®é级å½ä¸ï¼éå¯å端åä»å¯ç§åå¤ç¨ï¼ææ°å¯¼å ¥å³å¤±æï¼7 天ä¸éå¹¶èªå¨æ¸ çãéçï¼æ°æ®çæ¬æ¯å ¨å±çï¼ä¸æ±æ»æ å ³çå¯¼å ¥ä¹ä¼è®©ç¼å失æï¼ï¼å¦éæå¯¼å ¥ç±»åç»åå¾ ææ | â ï¼å¯ä¼å项è§å³ï¼| | P2-7 | å¸å·å表ãå¤å¸å·åãå åºæç¤º | æ¯å¦å¨è¯å«å°ä¸ä»½æä»¶å«å¤ä¸ªå¸å·åæ¶å¼¹çªæç¤º/è·³è¿ï¼å¾ 确认ï¼ä¸ P0-1 å䏿 ¹å ï¼ | ⸠| | P2-8 | ä»åºæ ¹ç®å½ `_*.*` ä¸´æ¶æä»¶å¢å¤ | `_chk_*`ï¼æ¬æ¬¡æ±æ»å¤§è¡¨ææ¥äº§çï¼è§æ±æ»å¤§è¡¨äº¤æ¥ç¬¬ 10 èï¼ã`_hyb_*`ã`_loo_*` çï¼**ç»ç¨æ·åæå忏 ** | ⸠| | P2-9 | `2026å¹´8æéè·¯è¿è¾éæ±æ»è¡¨_çå¼åå.xlsx` å»ç | è¿è¡æ¶æªè¢«å¼ç¨ | ⸠| docs/¹¦ÄܲâÊÔ±¨¸æ/¹¦ÄܲâÊÔ±¨¸æ_»ã×Ü´ó±íµ¼³ö½á¹ûÂäÅÌ»º´æ¸´ÓÃ_2026-09-22.md
New file @@ -0,0 +1,56 @@ # åè½æµè¯æ¥å_æ±æ»å¤§è¡¨å¯¼åºç»æè½çç¼åå¤ç¨_2026-09-22 ## ä¸ãåè½è¯´æ ç¨æ·è¯æ±ï¼ãæå·²ç»ä¸è½½è¿äºä¸ºä»ä¹è¿è¦éæ°çæå¢ï¼å¯ä»¥å¼ä¸ä¸ªè®°å¿åè½åââæä¸è½½è¿æä¸ªæ°æ®ï¼ç¬¬äºæ¬¡ä¸è½½æé´æ²¡ææ°å¯¼å ¥æ°æ®ï¼å°±ç´æ¥æ²¿ç¨ä¸æ¬¡ç®å¥½çç»æãä¸ç¶æ¯æ¬¡ä¸è½½é½è¦çå¾ä¹ ãã **æ¹é å**ï¼å·²æã10 åéå + æé´æ æ°å¯¼å ¥ãçå åç¼åï¼ä½â çªå£åªæ 10 åéï¼â¡åå¨å åéï¼è¿ç¨ä¸éå¯å°±æ²¡äº â æ£å¸¸åºæ¯ä¸ç¬¬äºæ¬¡ä¸è½½ä»è¦éç® 13~17 ç§ã **æ¹é å**ï¼ 1. ç»æ**è½ç**ä¿åå° `docs/çææ±æ»å¤§è¡¨/_cache/`ï¼å·²å å ¥ `.gitignore`ï¼ï¼æä»¶å `<key>_v<æ°æ®çæ¬>.xlsx`ï¼ 2. æäº¤å¯¼åºæ¶ï¼æªç¹ãéæ°çæãï¼ï¼**å åç¼å â è½çç¼å â çç®**ï¼è½çå½ä¸è¦æ±ãå䏿¥è¡¨æ + å䏿°æ®çæ¬ãï¼ 3. **æ°æ®çæ¬ = `MAX(import_batch.id)`**ï¼åªè¦æé´æä»»ä½ä¸æ¬¡æ°æ®å¯¼å ¥ï¼çæ¬å°±åï¼æ§ç¼åèªå¨å¤±æï¼ 4. å¤ç¨ä¸é **7 天**ï¼é²éå¹´ç»æè¢«å½æ°è¡¨ååºï¼ï¼è¶ ææä»¶å¨ä¸æ¬¡æäº¤æ¶èªå¨æ¸ çï¼åä¸ key åªä¿çææ°çæ¬æä»¶ï¼ 5. å端å½ä¸ç¼åæ¶æ¾ç¤ºãæ¬æ¬¡ç´æ¥æ²¿ç¨ <çææ¶é´> çæçç»æï¼æé´æ²¡ææ°çæ°æ®å¯¼å ¥ï¼ãå¦éæææ°æ°æ®éç®ï¼è¯·ç¹ãéæ°çæãããï¼å¹¶æä¸è¡å ä¿¡æ¯æ¹æãæ¥è¡¨æ 2026-08ã䏿¬¡çæäº â¦ãï¼ä¸åæ¾ç¤ºã䏿¬¡çæç¨æ¶ 0.0 ç§ãï¼ï¼ 6. ä¿®æè½çå½ä¸æ¶ `elapsedMs` ä¸ºè´æ°çé®é¢ï¼ä»¥çææ¶é´ä½ä¸ºä»»å¡èµ·ç¹ï¼ã æ¹å¨æä»¶ï¼`ReportExportTaskService.java`ï¼æ ¸å¿ï¼ã`ReportExportController.java`ï¼æ³¨éï¼ã`ReportExport.vue`ï¼ææ¡ï¼ã`.gitignore`ã ## äºãæµè¯ç¯å¢ - å端 Spring Boot 8090ï¼éå¯å è½½æ°ä»£ç ï¼ï¼å端 `npm run serve` 8080ï¼`Compiled successfully`ï¼ - æ°æ®åº MySQL 3308 / `traffic_audit`ï¼æ¥è¡¨æ 2026-08ï¼ææ¥ï¼ - ç¼åç®å½ï¼`docs/çææ±æ»å¤§è¡¨/_cache/`ï¼å½ä¸å¤æ® `MAX(import_batch.id)`ï¼æµè¯æ¶ä¸º **541**ï¼ ## ä¸ãç¨ä¾è¡¨ | # | ç¨ä¾ | æä½ | 颿 | å®é | ç»è®º | | --- | --- | --- | --- | --- | --- | | T01 | 馿¬¡æäº¤ï¼æ ç¼åï¼æ£åï¼ | `GET /api/report/exportTask/summaryWorkbook?period=2026-08` | çç®ï¼å®æååè½çç¼å | `RUNNING` â `DONE`ï¼12.4 sï¼ï¼çæ `summaryWorkbook_2026-08_month__v541.xlsx`ï¼1,204,461 Bï¼ | éè¿ | | T02 | 忬¡æäº¤ï¼å åå½ä¸ï¼ | å䏿¥ | ç«å³ DONEãfromCache=true | æäº¤èæ¶ **164 ms**ï¼`fromCache=true`ï¼ä¸è½½ 1,204,461 B ä¸ç¼åæä»¶ä¸è´ | éè¿ | | T03 | **éå¯å端ååæäº¤ï¼è½çå½ä¸ï¼æ ¸å¿ï¼** | å/èµ·åå端åæäº¤ | ä»ç«å³ DONEï¼ä¸åéç®ï¼ | æäº¤ **1,194 ms**ï¼`fromCache=true`ï¼æ¥å¿ãå½ä¸è½çç¼åâ¦ãï¼ä¸è½½åèä¸è´ | éè¿ | | T04 | ææ°å¯¼å ¥ï¼è´åï¼ | ä¸´æ¶æå ¥ä¸æ¡ `import_batch`ï¼id å 542ï¼åæäº¤ | ç¼å失æãéæ°çæ | `fromCache=false`ã`status=RUNNING` | éè¿ | | T05 | å key åªçææ°çæ¬ | éç®å®æåçç¼åç®å½ | æ§çæ¬æä»¶è¢«å é¤ | ç®å½åªå© `â¦_v542.xlsx`ï¼v541 å·²å ï¼ | éè¿ | | T06 | çæ¬åè½å | å æä¸´æ¶å¯¼å ¥è¡ï¼çæ¬åå° 541ï¼ååæäº¤ | ä¸å½ä¸ v542ï¼éç®å¹¶åå v541 | `fromCache=false` â éç®å®æï¼ç®å½åªå© `â¦_v541.xlsx` | éè¿ | | T07 | æ¶é¿æ¾ç¤ºï¼åå½ä¿®å¤ï¼ | è½çå½ä¸æ¶ç `elapsedMs` / å端å ä¿¡æ¯ | ä¸ä¸ºè´æ° | ä¿®å¤å `elapsedMs=-72937`ï¼ä¿®å¤å `elapsedMs=0`ï¼å端æ¾ç¤ºãæ¥è¡¨æ 2026-08ã䏿¬¡çæäº 2026-09-22 11:27:32ã | éè¿ | | T08 | æµè§å¨ç«¯å°ç«¯ï¼æ£åï¼ | admin/123456 â æ¥è¡¨çæ â æ±æ»å¤§è¡¨ â 2026-08 â å¯¼åº | ç§åºå¹¶èªå¨ä¸è½½ï¼ææ¡æ£ç¡® | ç¹å»å**ç«å³**ãæ±æ»å¤§è¡¨å·²çæã100%ï¼æ¾ç¤ºãæ¬æ¬¡ç´æ¥æ²¿ç¨ 2026-09-22 11:27:32 çæçç»æï¼æé´æ²¡ææ°çæ°æ®å¯¼å ¥ï¼ãããå·²èªå¨ä¸è½½ï¼çæ_éè·¯è¿è¾éæ±æ»è¡¨_2026-08.xlsx ï¼1.15 MBï¼â¦ãï¼æé®ä» ãéæ°çæ / å ³éãï¼æ§å¶å° 0 æ¥é | éè¿ | | T09 | ç¼åä¸ä¸²æï¼éæï¼ | ââ | ä¸åæ¥è¡¨æ/模å¼äºä¸å½ä¸ | keyï¼type\|period\|mode\|toMonthï¼åæ¶è¿å ¥å åé®ä¸è½çæä»¶ååç¼ | éè¿ï¼éæï¼ | | T10 | èªå¨æ¸ ç | ââ | è¶ ææä»¶è¢«å é¤ | æ¯æ¬¡æäº¤è°ç¨ `cleanupExpired()`ï¼å åæ¡ç®ä¸ç£çæä»¶è¶ è¿ 7 天峿¸ | éè¿ï¼éæï¼ | ## åãåç°çé®é¢ä¸å¤ç½® | # | é®é¢ | å¤ç½® | | --- | --- | --- | | P1 | è½çå½ä¸æ¶å端ãç¨æ¶ãä¸ºè´æ°ï¼`elapsedMs=-72937`ï¼å 为任å¡èµ·ç¹æ¯ç°å¨ãç»ææ¶é´æ¯åå²çææ¶é´ï¼ | 已修ï¼è½çå½ä¸æ¶æä»»å¡èµ·ç¹è®¾ä¸ºçææ¶é´ï¼å端ç¼åå½ä¸ä¸åæ¾ç¤ºç¨æ¶ï¼æ¹æ¾ç¤ºã䏿¬¡çæäº â¦ã | | P2 | æµè¯ç¨çä¸´æ¶ `import_batch` è¡ | å·²å é¤ï¼`MAX(id)` åå° 541ãæ¹æ¬¡ 419 æ¡ï¼ï¼ä» ç¨äºéªè¯ãå¯¼å ¥å³å¤±æã | | P3 | æ°æ®çæ¬æ¯**å ¨å±**çï¼ä»»ä½ä¸æ¬¡å¯¼å ¥ï¼å«ä¸æ±æ»å¤§è¡¨æ å ³çæèµ/è½èå¯¼å ¥ï¼é½ä¼è®©æ±æ»ç¼å失æ | å±ä¿å®è®¾è®¡ï¼å®å¯éç®ä¸å¯åºéï¼ãè¥å¸æãåªæç¸å ³æ°æ®å¯¼å ¥æå¤±æãï¼éè¦æ `import_type` ç»åä¾èµï¼å¾ ç¨æ·ç¡®è®¤åææ | | P4 | è½çç®å½ | æ°å¢ `docs/çææ±æ»å¤§è¡¨/_cache/` å¹¶å å ¥ `.gitignore`ï¼ç»ææä»¶ä¸å ¥åºï¼ | ## äºãç»è®º - ãä¸è½½è¿ä¸æ¬¡ãæé´æ²¡ææ°å¯¼å ¥ â ç¬¬äºæ¬¡ç´æ¥æ²¿ç¨ãå·²å®ç°ï¼ä¸**è·¨å端é坿æ**ï¼è¿æ¯æ¬æ¬¡çå ³é®å¢éï¼ï¼ - å¤ç¨æ£ç¡®æ§ç±ãæ°æ®çæ¬ï¼`MAX(import_batch.id)`ï¼+ 7 天ä¸éãåéæå ³ï¼ææ°å¯¼å ¥å¿ å®éç®ï¼æµè¯å·²è¦çï¼ - ç¨æ·ä½éªï¼å½ä¸ç¼åæ¶ç¹å¯¼åº**ç§å**ï¼æ¯«ç§çº§ï¼ï¼ä¸åæ¯æ¬¡ç 13~17 ç§ï¼ãéæ°çæãä¿ç为强å¶éç®å ¥å£ï¼ - æµè§å¨ç«¯å°ç«¯å®æµéè¿ï¼æ æ§å¶å°æ¥éã æµè¯äººï¼Codexï¼AIï¼ãæµè¯æ¥æï¼2026-09-22 docs/¹¤×÷ÈÕ½á/¹¤×÷ÈÕ½á_2026-09-22.md
@@ -69,6 +69,16 @@ - ä¿®å¤ä¸¤ä¸ªå£å¾åï¼è´§è¿éå¨è½¬é表ç"è¡å 累计 + æ¹åçå计è¡"éå¤è®¡ç®ï¼1,149,579 â 130,311.89 ä¸å¨ï¼ãç»æææ æ°æ®èªå¨åéï¼ - ä¸æå·¥æ ¸ç®ä¸è´ï¼å ¬äº¤ 1-8 æ 110776.34 ä¸äººæ¬¡ãè´§è¿ä¼ä¸ 313,998,248 å¨ãè´§è¿éå¨è½¬é 130,311.89 ä¸å¨ã ### 5. æ±æ»å¤§è¡¨å¯¼åºç»æè½çç¼åï¼ç¨æ·ï¼"ä¸è½½è¿å°±ä¸è¯¥åçå¾ä¹ ï¼è½ä¸è½è®°ä½ä¸æ¬¡ç»æ"ï¼ - **èæ¯**ï¼åæ¥åªæã10 åéå + æé´æ æ°å¯¼å ¥ãç**å å**ç¼åï¼çªå£çãéå¯å³å¤±æ â ç¬¬äºæ¬¡ä¸è½½è¿è¦ç 13~17 ç§ã - **æ¹å¨**ï¼`ReportExportTaskService.java` 为主ï¼ï¼ 1. ç»æ**è½ç**å° `docs/çææ±æ»å¤§è¡¨/_cache/`ï¼æä»¶å `<key>_v<æ°æ®çæ¬>.xlsx`ï¼æ°æ®çæ¬=`MAX(import_batch.id)`ï¼ï¼å·²å `.gitignore`ï¼ 2. æäº¤é¡ºåºï¼å åç¼å â è½çç¼åï¼åæ¥è¡¨æ + åæ°æ®çæ¬ + 7 天å ï¼â çç®ï¼çææåååçå¹¶å é¤å key æ§çæ¬ï¼ 3. åç«¯ææ¡ï¼å½ä¸ç¼åæ¾ç¤ºãæ¬æ¬¡ç´æ¥æ²¿ç¨ <æ¶é´> çæçç»æï¼æé´æ²¡ææ°çæ°æ®å¯¼å ¥ï¼ãï¼å ä¿¡æ¯æ¹ä¸ºã䏿¬¡çæäº â¦ãã - **顺带修 bug**ï¼è½çå½ä¸æ¶ `elapsedMs` ä¸ºè´æ°ï¼ä»»å¡èµ·ç¹ç¨ç°å¨ãç»ææ¶é´æ¯åå²çææ¶é´ï¼â å·²æ¹ä¸ºä»¥çææ¶é´ä½èµ·ç¹ã - **宿µ**ï¼é¦æ¬¡ 12.4 s åç¼åï¼å次æäº¤ **164 ms** å½ä¸å åï¼**éå¯å端åæäº¤ 1.19 s å½ä¸è½ç**ï¼ä¸´æ¶æä¸æ¡å¯¼å ¥è®°å½å `fromCache=false` éæ°çæï¼éªè¯ãææ°å¯¼å ¥å³å¤±æãï¼ï¼æµè§å¨ç¹å¯¼åºç§åºãææ¡ä¸æé®æ£ç¡®ãæ§å¶å° 0 æ¥éã ## äºãæµè¯ç»ææè¦ - æ¥åï¼`docs/åè½æµè¯æ¥å/åè½æµè¯æ¥å_æ°æ®æ¥çè¡¥é½ç½çº¦è½¦ä¸ä¸ªä½å®¢è¿ç±»å_2026-09-22.md` @@ -77,6 +87,7 @@ - æ¥åï¼`docs/åè½æµè¯æ¥å/åè½æµè¯æ¥å_æ°æ®æ¥çå£å¾çéä¸è¡¨å¤´åä½_2026-09-22.md`ï¼æ¥å£ 7 æ¡ + çæº 2 æ¡å ¨éè¿ï¼ - æ¥åï¼`docs/åè½æµè¯æ¥å/åè½æµè¯æ¥å_æ°æ®æ¥çæå¸å·æ±æ»ä¸å¯¼åº_2026-09-22.md`ï¼æ¥å£ 10 æ¡ + çæº 4 æ¡å ¨éè¿ï¼ - æ¥åï¼`docs/åè½æµè¯æ¥å/åè½æµè¯æ¥å_æ±æ»å¤§è¡¨è¿åº¦å¼¹çªä¼å_2026-09-22.md`ï¼6 æ¡ç¨ä¾éè¿ï¼çæä¸ç¶æ / 宿æç¤º / 忬¡ä¸è½½èµ°ç¼å / å ³é / æ æ§å¶å°æ¥é / å ¶å®å¯¼åºæªåå½±åï¼ - æ¥åï¼`docs/åè½æµè¯æ¥å/åè½æµè¯æ¥å_æ±æ»å¤§è¡¨å¯¼åºç»æè½çç¼åå¤ç¨_2026-09-22.md`ï¼10 æ¡ç¨ä¾éè¿ï¼é¦æ¬¡çæåç¼å / å åå½ä¸ 164ms / **éå¯åè½çå½ä¸ 1.19s** / ææ°å¯¼å ¥å³å¤±æ / å key åªçææ°çæ¬ / æ¶é¿æ¾ç¤ºä¿®å¤ / æµè§å¨ç«¯å°ç«¯ï¼ - ç¯å¢ï¼å端 `mvn spring-boot:run`ï¼8090ï¼`Started TrafficAuditApplication`ï¼ï¼å端 `npm run serve`ï¼8080ï¼`Compiled successfully in 4769ms`ï¼ï¼MySQL `localhost:3308/traffic_audit`ï¼ç»å½ admin/123456 å JWTã - ç»æï¼3 个æ°ç±»åæ£åï¼å«æé´/å ³é®åçéï¼ãè´åï¼æªç¥ç±»åãæ æ°æ®æãæ tokenï¼å ¨é¨éè¿ï¼å ¥åºæ½æ¥æ¡æ°ä¸åºå ä¸è´ï¼20 / 20 / 139ï¼ï¼åæ 15 ç±»å彿£å¸¸ã traffic-audit-server/src/main/java/com/trafficaudit/reportexport/controller/ReportExportController.java
@@ -99,7 +99,7 @@ /** * æ±æ»å¤§è¡¨å¼æ¥å¯¼åºï¼ç«å³è¿åä»»å¡å·ï¼å端轮询è¿åº¦ã宿ååä¸è½½æä»¶ã * çæä¸æ¬¡çº¦ 10~20 ç§ï¼èµ°å¼æ¥å¯é¿å ç¹å»åé¿æ¶é´æ ååºï¼åæ¥è¡¨æ 10 åéå çç»æç´æ¥å¤ç¨ï¼æé´æå¯¼å ¥åèªå¨å¤±æï¼ï¼ * çæä¸æ¬¡çº¦ 10~20 ç§ï¼èµ°å¼æ¥å¯é¿å ç¹å»åé¿æ¶é´æ ååºï¼åæ¥è¡¨æç»æç´æ¥å¤ç¨ï¼è½çä¿åãè¿ç¨éå¯å仿æï¼æé´æå¯¼å ¥åèªå¨å¤±æï¼ï¼ * refresh=true å¯å¼ºå¶éæ°çæã */ @GetMapping("/exportTask/summaryWorkbook") traffic-audit-server/src/main/java/com/trafficaudit/reportexport/service/ReportExportTaskService.java
@@ -8,6 +8,7 @@ import javax.annotation.PreDestroy; import javax.annotation.Resource; import java.io.File; import java.util.Date; import java.util.Iterator; import java.util.LinkedHashMap; @@ -26,10 +27,11 @@ * ç®çï¼2026-09-20 ç¨æ·ä¸æ¥ãç¹ä¸è½½è¦çä¸ä¼ï¼æ°æ®å¤äºä¼ä¸ä¼å¡æ»ãï¼ï¼ * 1) ç¹å»åç«å»æåé¦ï¼ä¸åé¿æ¶é´æ ååºï¼ * 2) çæè¿ç¨ä¸å ç¨ HTTP 请æ±çº¿ç¨ï¼é¿å å¤äººåæ¶ç¹æå®¹å¨ææ»ï¼å·¥ä½çº¿ç¨åºå® 1 个ï¼å¤ä½è¯·æ±æ¾ç¤ºãæéä¸ãï¼ï¼ * 3) å䏿¥è¡¨æ 10 åéå çç»æå¯å¤ç¨ï¼é¿å æ¯æ¬¡éç®æ´æ¬çº¦ 4 䏿 ¼å ¬å¼ã * 3) å䏿¥è¡¨æçç»æå¯å¤ç¨ï¼**è½çä¿åï¼è¿ç¨éå¯åä¾ç¶ææ**ï¼ï¼é¿å æ¯æ¬¡éç®æ´æ¬çº¦ 4 䏿 ¼å ¬å¼ã * * ç¼åæ£ç¡®æ§å®å«ï¼åªæå½ãè·ä¸æ¬¡çæä¸è¶ è¿ TTLãä¸ãæé´æ²¡æä»»ä½æ°çæ°æ®å¯¼å ¥ï¼import_batch æå¤§ id æªåï¼ã * æ¶æå¤ç¨ï¼å¯¼å ¥å³å¤±æï¼é¿å æè¿ææ¥è¡¨ç»ç¨æ·ãåç«¯ä»æä¾ãéæ°çæã强å¶å·æ°ã * ç¼åæ£ç¡®æ§å®å«ï¼åªæå½ãæé´æ²¡æä»»ä½æ°çæ°æ®å¯¼å ¥ï¼import_batch æå¤§ id æªåï¼ãä¸ãç»æä¸è¶ è¿ 7 å¤©ãæ¶æå¤ç¨ï¼ * å¯¼å ¥å³å¤±æï¼é¿å æè¿ææ¥è¡¨ç»ç¨æ·ãåç«¯ä»æä¾ãéæ°çæã强å¶éç®ï¼è·³è¿ç¼åï¼ã * è½çä½ç½®ï¼{summary.template-dir}/_cache/<key>_v<æ°æ®çæ¬>.xlsx */ @Slf4j @Service @@ -40,13 +42,41 @@ /** çæç»æä¿çæ¶é¿ï¼æé´å¯åå¤ä¸è½½ï¼è¿åèªå¨æ¸ ç */ private static final long RESULT_TTL_MS = 30 * 60 * 1000L; /** å䏿¥è¡¨æç»æå¤ç¨æ¶é¿ï¼ä¸æé´æ æ°å¯¼å ¥æ¶æå¤ç¨ï¼ */ private static final long CACHE_TTL_MS = 10 * 60 * 1000L; /** å䏿¥è¡¨æç»æå¤ç¨ä¸éï¼åªè¦æé´æ²¡ææ°å¯¼å ¥å°±ä¸ç´æ²¿ç¨ï¼è¶ è¿è¯¥å¤©æ°ä¸åå¤ç¨ï¼é²æ¢éå¹´ç»æè¢«å½æ°è¡¨ååºï¼ */ private static final long CACHE_MAX_AGE_MS = 7L * 24 * 60 * 60 * 1000; /** è½çç¼ååç®å½ï¼ç¸å¯¹ summary.template-dirï¼ */ private static final String CACHE_SUBDIR = "_cache"; @Resource private ReportExportService reportService; @Resource private ImportBatchMapper importBatchMapper; /** æ±æ»å·¥ä½ç°¿ç®å½ï¼ä¸ ReportExportService åé ç½®ï¼ï¼è½çç¼åæ¾å¨å ¶ _cache åç®å½ */ @org.springframework.beans.factory.annotation.Value("${summary.template-dir:docs/çææ±æ»å¤§è¡¨}") private String summaryTemplateDir; /** è½çç¼åç®å½ï¼å®ä½çç¥ä¸æ¨¡æ¿ä¸è´ï¼é ç½®ç®å½ / user.dir ç¸å¯¹ / ä¸çº§ç®å½ï¼ */ private File cacheDir() { String rel = summaryTemplateDir; while (rel.startsWith("./")) rel = rel.substring(2); String[] roots = {rel, System.getProperty("user.dir") + "/" + rel, System.getProperty("user.dir") + "/../" + rel}; for (String root : roots) { if (root == null || root.trim().isEmpty()) continue; File base = new File(root); if (base.isDirectory()) { File dir = new File(base, CACHE_SUBDIR); if (!dir.isDirectory() && !dir.mkdirs()) continue; return dir; } } return null; } /** ç¼åæä»¶ååç¼ï¼ä¸å«æ°æ®çæ¬ä¸æ©å±åï¼ï¼key éçéæ³å符ç»ä¸æ¿æ¢ */ private String cacheFilePrefix(String type, String period, String mode, Integer toMonth) { return (cacheKey(type, period, mode, toMonth)).replaceAll("[^0-9A-Za-z_-]", "_"); } private final Map<String, Task> tasks = new ConcurrentHashMap<>(); /** åæ¥è¡¨ææè¿ä¸æ¬¡æåç»æï¼key -> task */ @@ -64,7 +94,7 @@ private final String period; private final String mode; private final Integer toMonth; private final long createdAt = System.currentTimeMillis(); private volatile long createdAt = System.currentTimeMillis(); /** çææ¶çæ°æ®çæ¬ï¼import_batch æå¤§ idï¼ï¼ç¨äºå¤æç¼åæ¯å¦è¿æ */ private final long importVersion; private volatile String status = "PENDING"; // PENDING/RUNNING/DONE/FAILED @@ -72,6 +102,8 @@ private volatile String step = "æéä¸"; private volatile String error; private volatile byte[] data; /** è½çç¼åæä»¶ï¼å åæ°æ®è¢«æ¸ çåä»å¯ä»è¿éåï¼è·¨è¿ç¨éå¯å¤ç¨ï¼ */ private volatile File cacheFile; private volatile String fileName; private volatile boolean fromCache; private volatile long finishedAt; @@ -93,11 +125,24 @@ public String getStatus() { return status; } public boolean isDone() { return "DONE".equals(status) && data != null; } public boolean isDone() { return "DONE".equals(status) && (data != null || (cacheFile != null && cacheFile.isFile())); } public boolean isFromCache() { return fromCache; } public byte[] getData() { return data; } public byte[] getData() { if (data != null) return data; File f = cacheFile; if (f != null && f.isFile()) { try { return java.nio.file.Files.readAllBytes(f.toPath()); } catch (Exception e) { log.warn("读å导åºç¼åæä»¶å¤±è´¥ï¼{} {}", f.getAbsolutePath(), e.getMessage()); } } return null; } public String getFileName() { return fileName; } @@ -135,8 +180,16 @@ Task hit = cache.get(key); if (cacheUsable(hit)) { hit.fromCache = true; log.info("æ¥è¡¨å¯¼åºä»»å¡å½ä¸ç¼åï¼{} {}ï¼çæäº {}ï¼", type, period, new Date(hit.finishedAt)); log.info("æ¥è¡¨å¯¼åºä»»å¡å½ä¸å åç¼åï¼{} {}ï¼çæäº {}ï¼", type, period, new Date(hit.finishedAt)); return hit; } Task disk = loadFromDisk(key, type, period, mode, toMonth); if (disk != null) { tasks.put(disk.id, disk); cache.put(key, disk); log.info("æ¥è¡¨å¯¼åºä»»å¡å½ä¸è½çç¼åï¼{} {}ï¼çæäº {}ï¼æä»¶ {}ï¼", type, period, new Date(disk.finishedAt), disk.cacheFile.getName()); return disk; } } Task running = findRunning(key); @@ -168,6 +221,7 @@ t.status = "DONE"; t.finishedAt = System.currentTimeMillis(); cache.put(cacheKey(t.type, t.period, t.mode, t.toMonth), t); writeToDisk(t, data); log.info("æ¥è¡¨å¯¼åºä»»å¡å®æï¼{} {} ç¨æ¶ {} msï¼{} åè", t.type, t.period, t.finishedAt - start, data.length); } catch (Throwable e) { t.status = "FAILED"; @@ -188,7 +242,7 @@ private boolean cacheUsable(Task t) { if (t == null || !"DONE".equals(t.status) || t.data == null) return false; if (System.currentTimeMillis() - t.finishedAt > CACHE_TTL_MS) return false; if (System.currentTimeMillis() - t.finishedAt > CACHE_MAX_AGE_MS) return false; long now = importVersion(); if (now < 0 || t.importVersion < 0) return false; // åä¸å°æ°æ®çæ¬æ¶ä¸åé£é©å¤ç¨ return now == t.importVersion; @@ -218,7 +272,66 @@ } for (Iterator<Map.Entry<String, Task>> it = cache.entrySet().iterator(); it.hasNext(); ) { Task t = it.next().getValue(); if (t.finishedAt <= 0 || now - t.finishedAt > CACHE_TTL_MS) it.remove(); if (t.finishedAt <= 0 || now - t.finishedAt > CACHE_MAX_AGE_MS) it.remove(); } File dir = cacheDir(); if (dir == null) return; File[] fs = dir.listFiles((d, n) -> n.endsWith(".xlsx")); if (fs == null) return; for (File f : fs) { if (now - f.lastModified() > CACHE_MAX_AGE_MS && !f.delete()) { log.warn("æ¸ çè¿æå¯¼åºç¼å失败ï¼{}", f.getAbsolutePath()); } } } /** çææååæç»æè½çï¼ä¾è¿ç¨éå¯åå¤ç¨ï¼åç失败åªåè¦ï¼ä¸å½±åæ¬æ¬¡å¯¼åº */ private void writeToDisk(Task t, byte[] data) { try { File dir = cacheDir(); if (dir == null) return; long version = t.importVersion; if (version < 0) return; String prefix = cacheFilePrefix(t.type, t.period, t.mode, t.toMonth); File target = new File(dir, prefix + "_v" + version + ".xlsx"); java.nio.file.Files.write(target.toPath(), data); File[] siblings = dir.listFiles((d, n) -> n.startsWith(prefix + "_v") && n.endsWith(".xlsx")); if (siblings != null) { for (File f : siblings) { if (!f.getName().equals(target.getName()) && !f.delete()) { log.warn("æ¸ çæ§çæ¬å¯¼åºç¼å失败ï¼{}", f.getAbsolutePath()); } } } } catch (Exception e) { log.warn("导åºç»æè½ç失败ï¼ä¸å½±åæ¬æ¬¡å¯¼åºï¼ä» æ æ³è·¨éå¯å¤ç¨ï¼ï¼{}", e.getMessage()); } } /** ä»è½çç¼åéæ¾ãå䏿¥è¡¨æ + å䏿°æ®çæ¬ + æªè¶ æãçç»æï¼æ²¡æè¿å null */ private Task loadFromDisk(String key, String type, String period, String mode, Integer toMonth) { try { File dir = cacheDir(); if (dir == null) return null; long version = importVersion(); if (version < 0) return null; // åä¸å°æ°æ®çæ¬æ¶ä¸åé£é©å¤ç¨ File f = new File(dir, cacheFilePrefix(type, period, mode, toMonth) + "_v" + version + ".xlsx"); if (!f.isFile()) return null; long finished = f.lastModified(); if (finished <= 0 || System.currentTimeMillis() - finished > CACHE_MAX_AGE_MS) return null; Task t = new Task(type, period, mode, toMonth, version); t.cacheFile = f; t.createdAt = finished; // å¤ç¨åå²ç»æï¼ä»¥çææ¶é´ä½ä¸ºèµ·ç¹ï¼é¿å elapsedMs åºç°è´æ° t.fileName = "çæ_éè·¯è¿è¾éæ±æ»è¡¨_" + period + ".xlsx"; t.status = "DONE"; t.percent = 100; t.step = "宿"; t.finishedAt = finished; t.fromCache = true; return t; } catch (Exception e) { log.warn("读åè½ç导åºç¼å失败ï¼{}", e.getMessage()); return null; } } traffic-audit-web/src/views/ReportExport.vue
@@ -168,8 +168,9 @@ <el-progress :percentage="taskInfo.percent || 0" :status="taskInfo.status === 'FAILED' ? 'exception' : (taskDone ? 'success' : null)" :stroke-width="14"></el-progress> <p class="task-step">{{ taskInfo.step || 'æéä¸' }}</p> <p class="task-meta" v-if="taskInfo.period">æ¥è¡¨æ {{ taskInfo.period }}ã{{ taskInfo.fromCache ? '䏿¬¡çæç¨æ¶' : 'å·²ç¨' }} {{ (taskInfo.elapsedMs / 1000).toFixed(1) }} ç§</p> <p class="task-meta" v-if="taskInfo.fromCache">æ¬æ¬¡ç´æ¥ä½¿ç¨ {{ taskCacheTime }} çæçç»æï¼10 åéå 䏿鴿²¡ææ°å¯¼å ¥æ°æ®ï¼ãå¦éæææ°æ°æ®éç®ï¼è¯·ç¹ãéæ°çæãã</p> <p class="task-meta" v-if="taskInfo.period && !taskInfo.fromCache">æ¥è¡¨æ {{ taskInfo.period }}ãå·²ç¨ {{ ((taskInfo.elapsedMs || 0) / 1000).toFixed(1) }} ç§</p> <p class="task-meta" v-else-if="taskInfo.period">æ¥è¡¨æ {{ taskInfo.period }}ã䏿¬¡çæäº {{ taskCacheTime }}</p> <p class="task-meta" v-if="taskInfo.fromCache">æ¬æ¬¡ç´æ¥æ²¿ç¨ {{ taskCacheTime }} çæçç»æï¼æé´æ²¡ææ°çæ°æ®å¯¼å ¥ï¼ãå¦éæææ°æ°æ®éç®ï¼è¯·ç¹ãéæ°çæãã</p> <p class="task-done" v-if="taskDownloaded"> å·²èªå¨ä¸è½½ï¼<b>{{ taskFileName }}</b><span v-if="taskFileSize">ï¼{{ taskFileSizeText }}ï¼</span>ï¼æä»¶å¨æµè§å¨é»è®¤ä¸è½½ç®å½ã </p>