| | |
| | | - å岿¼ç¤ºæ´¾å·¥åå¯è½ä¿çæ§ `T_CONTRACTOR` åç§°ï¼é管çå cj ç¨æ·å¯è½çä¸å°è¿äºåå²åï¼`admin` ä¸ååä½è¿æ»¤å½±åã |
| | | - `.codex-run`ãOffice éæä»¶ `requirement\~$åå§éæ±.docx` å `emergency-repair-web/public/æ°å»º ææ¬ææ¡£.txt` 屿¬å°èè°æä¸´æ¶æä»¶ï¼ä¸åºæäº¤ã |
| | | - æ¬è½®ä»£ç å·²æäº¤å¹¶æ¨éå
¬å¸ Git ä»åº `main` åæ¯ï¼æäº¤ `757dcb3`ï¼äº¤æ¥æä»¶åå¡«æäº¤è®°å½ãåç»å¯å¨ãæå»ºåæéå½ä»¤æ²¿ç¨ç¬¬ 16 èã |
| | | |
| | | ## 18. æ¶å°¾å¿«ç
§ï¼2026-10-06 å¨ä¸ï¼éæ± 20 èåæéç®¡çæ¶å°¾ï¼ |
| | | |
| | | **æ¬è½®å®æå
容** |
| | | - éæ± 20ï¼èåæé管çåæ¶ç§æ·æéåè§è²æéçåéææï¼æ¹ä¸ºåæ¥è®¾ç½®è§è²æéãå½åç¨æ·æ¬èº«æ²¡æç§æ·å½å±ï¼å æ¤ä¸åè®¾ç½®ç§æ·æéã |
| | | - æéæ ç»§ç»å±ç¤ºèåèç¹åæé®èç¹ï¼ä¸æ `menuType` è¿æ»¤ï¼åæ¢è§è²åï¼å·²ä¿åæéå¤éæ¡æ£å¸¸åæ¾ã |
| | | - èåæéæ¥å£åªä¿ç `roleId` / `roleMenuIds`ï¼å é¤ç§æ·èåææå `/api/system/tenants`ã |
| | | - ä¿®å¤éªè± ID ç»æµè§å¨ JSON è§£æå精度丢失çé®é¢ï¼å端è¿åå符串 IDï¼å端é䏿æå符串å¤çã |
| | | |
| | | **ä¸»è¦æä»¶** |
| | | - å端ï¼`emergency-repair-server/src/main/java/com/emergencyrepair/business/dto/Requests.java`ã`emergency-repair-server/src/main/java/com/emergencyrepair/system/controller/SystemController.java`ã`emergency-repair-server/src/main/java/com/emergencyrepair/system/service/SystemService.java`ã |
| | | - å端ï¼`emergency-repair-web/src/views/system/MenuPermission.vue`ã |
| | | |
| | | **éªè¯ç»æ** |
| | | - å端 `mvn -B -DskipTests compile` éè¿ï¼å端 `npm run build` éè¿ã |
| | | - æ¥å£ï¼`GET /api/system/menu-permissions?roleId=2106221661022490625` è¿åå符串å `roleMenuIds` å
± 14 æ¡ã |
| | | - æµè§å¨ï¼`node .codex-run/verify-menu-permission.js` è¾åº `hasTenantPanel: false`ã`buttonNodeCount: 76`ã`checkedMatchesSaved: true`ã`hasError: false`ï¼æªå¾ `.codex-run/menu-permission-verified.png`ã |
| | | - 页é¢ç¡®è®¤æé®èç¹ä¿çï¼åæ¢è§è²åæéå¾é坿£å¸¸åæ¾ã |
| | | |
| | | **å½åè¿è¡ç¶æï¼2026-10-06ï¼** |
| | | - å端 `http://localhost:8082`ï¼å端 `http://127.0.0.1:8091`ï¼DM8 `127.0.0.1:5236`ï¼Edge CDP `9222`ã |
| | | - ç»å½ `admin / admin123`ï¼æ°æ®åº `SYSDBA/SYSDBA`ï¼ä¸å¡ schema `LX`ã |
| | | - å端æ¥å¿ï¼`.codex-run/backend-20261006-menu-permission-fixed.out.log`ã`.codex-run/backend-20261006-menu-permission-fixed.err.log`ã |
| | | |
| | | **æ¥ææ³¨æ** |
| | | - ä¸è¦æäº¤ `.codex-run`ãOffice éæä»¶æ `emergency-repair-web/public/æ°å»º ææ¬ææ¡£.txt`ã |
| | | - æ¬è½®ä»£ç ä¸äº¤æ¥è®°å½ä¸å¹¶æäº¤å¹¶æ¨éå
¬å¸ Git ä»åº `main` 忝ã |
| | | |
| | | ## 19. æ¶å°¾å¿«ç
§ï¼2026-10-06 å¨ä¸ï¼éæ± 21-22 èåæéå
¥å£ä¸é误æ¥å¿ï¼ |
| | | |
| | | **æ¬è½®å®æå
容** |
| | | - éæ± 21ï¼ç§»é¤ç¬ç«âèåæé管çâèåï¼å¨è§è²ç®¡çæä½åæ°å¢âæé设置âæé®ï¼å¹¶å¤ç¨èå/æé®æéæ å¼¹çªï¼æ§ `/system/menu-permissions` è·¯ç±å页é¢å·²å é¤ã |
| | | - éæ± 22ï¼ç³»ç»ç®¡çæ°å¢âç³»ç»é
ç½® / æéé
ç½® / æ¥å¿ç®¡çâä¸ä¸ªäºçº§ç®å½ï¼ç»ç»æºæãæ¿ä¿®åä½ã产åç»ææ ã设å¤åºç¡åºãå·¥ç¨ä¸ä¸ãTéä¸ä¸ãä¸åæ§å¶å½å
¥ç³»ç»é
ç½®ï¼ç¨æ·ç®¡çãè§è²ç®¡çãèå管çå½å
¥æéé
ç½®ï¼ç»å½æ¥å¿ãæä½æ¥å¿ãé误æ¥å¿å½å
¥æ¥å¿ç®¡çã |
| | | - é误æ¥å¿ä¸ºæ°å¢åè½ï¼`GlobalExceptionHandler` æè·æªå¤ççéä¸å¡å¼å¸¸å¹¶å¼æ¥åå
¥ `LX.T_SYS_ERROR_LOG`ï¼ä¸å¡æ ¡éªå¼å¸¸ä¸è®°å½ãåè¡¨æ¯ææ¨¡åãå¼å¸¸ç±»åãæ¶é´çéï¼è¯¦æ
å±ç¤ºå®æ´å æ ã |
| | | - æ°åºåå§ååæ§åºè¿ç§»åä¼å 餿§èåå¹¶è¡¥é½äºçº§ç®å½çç¶åèåå
³ç³»ï¼æéæ ç»§ç»ä¿çèåèç¹åæé®èç¹ï¼è§è²åæ¢åå¤éæ¡å¯åæ¾ã |
| | | |
| | | **ä¸»è¦æä»¶** |
| | | - å端ï¼`emergency-repair-server/src/main/java/com/emergencyrepair/system/entity/SysErrorLog.java`ã`.../mapper/SysErrorLogMapper.java`ã`.../log/ErrorLogRecorder.java`ã`.../common/GlobalExceptionHandler.java`ã`.../bootstrap/DataInitializer.java`ã`.../bootstrap/DatabaseBootstrap.java`ã`.../system/controller/SystemController.java`ã`.../system/service/SystemService.java`ã`.../system/service/ButtonPermissionCatalog.java`ã |
| | | - å端ï¼`emergency-repair-web/src/views/system/RoleManage.vue`ã`RolePermissionDialog.vue`ã`ErrorLogManage.vue`ã`emergency-repair-web/src/router/index.js`ã`MenuManage.vue`ï¼`MenuPermission.vue` å·²å é¤ã |
| | | |
| | | **éªè¯ç»æ** |
| | | - å端 `mvn -B -DskipTests clean package` éè¿ï¼å端 `npm run build` éè¿ï¼ä»
ææ¢æå
ä½ç§¯åè¦ã |
| | | - `node .codex-run/verify-req21-22.js` éè¿ï¼ä¸ä¸ªäºçº§ç®å½åé误æ¥å¿è忣叏æ¾ç¤ºï¼æ§èåä¸åå¨ï¼è§è²ç®¡çâæé设置âå¼¹çªæé®èç¹æ°ä¸º 78 ä¸åæ¾æ£å¸¸ã |
| | | - 人为访é®éæ³é误æ¥å¿è¯¦æ
ID åï¼è®°å½èªå¨è½åºå¹¶å¯å¨é误æ¥å¿é¡µé¢æ¥çï¼éªè¯ç¨ä¸´æ¶è®°å½å·²ä»æ°æ®åºæ¸
çã |
| | | - æªå¾ï¼`.codex-run/role-permission-dialog-verified.png`ã`.codex-run/error-log-verified.png`ã |
| | | |
| | | **å½åè¿è¡ç¶æï¼2026-10-06ï¼** |
| | | - å端 `http://localhost:8082`ï¼å端 `http://127.0.0.1:8091`ï¼DM8 `127.0.0.1:5236`ï¼Edge CDP `9222`ã |
| | | - ç»å½ `admin / admin123`ï¼æ°æ®åº `SYSDBA/SYSDBA`ï¼ä¸å¡ schema `LX`ã |
| | | - å端æ¥å¿ï¼`.codex-run/backend-20261006-req21-22.out.log`ã`.codex-run/backend-20261006-req21-22.err.log`ã |
| | | |
| | | **æ¥ææ³¨æ** |
| | | - é误æ¥å¿å½åè¦çè¿å
¥ Spring MVC å
¨å±å¼å¸¸å¤ççå¼å¸¸ï¼å¯å¨é¶æ®µå¼å¸¸ã弿¥çº¿ç¨ä¸æªè¿å
¥è¯¥å¤çå¨çå¼å¸¸æä¸ä¼èªå¨è½åºã |
| | | - ä¸è¦æäº¤ `.codex-run`ãOffice éæä»¶æ `emergency-repair-web/public/æ°å»º ææ¬ææ¡£.txt`ã |
| | | - æ¬è½®ä»£ç ä¸äº¤æ¥è®°å½ä¸å¹¶æäº¤å¹¶æ¨éå
¬å¸ Git ä»åº `main` 忝ã |
| | | |
| | | ## 20. æ¶å°¾å¿«ç
§ï¼2026-10-06 å¨ä¸ï¼éæ± 23-25 ç³»ç»é
ç½®ãå®å·¥æ¥é
ä¸èåç¶èç¹ï¼ |
| | | |
| | | **æ¬è½®å®æå
容** |
| | | - éæ± 23ï¼ç³»ç»åç§°åçæ¬æ¹ä¸ºå
³é®è¯é
ç½®ï¼codename åå«ä¸º `rk_SystemName`ã`rk_SystemVer`ãç»å½é¡µã页头ãLogo æ¿ä»£ææ¬åæµè§å¨æ é¢ä½¿ç¨é
ç½®å¼ï¼é¡µå¤´çæ¬ä½¿ç¨çº¢è²æ¾ç¤ºã |
| | | - éæ± 24ï¼æ°å¢âå®å·¥ä¿¡æ¯æ¥é
âåè½ï¼èå `/completion/query`ï¼æ¥å£ `GET /api/work-orders/completion/query`ãæ¥è¯¢ç»æææ
éæ±æ»ï¼ä¸ä¸ªæ
éå±å¼å¤å¼ 派工åï¼æ
éç¼å·ãrk_Xå·ã设å¤ãå·¥ç¨åç§°åæåæ
åµã䏿¥åä½ã䏿¥æ¥æãåºå·è¿ 7 å使ç¨åå
æ ¼çºµååå¹¶ãæ¯å¼ æ´¾å·¥åçå®å·¥å¤çæ
åµåæ´æ¢å¨ææ¯æå¤æ¡æç»ã |
| | | - éæ± 24 çæ¥è¯¢æ¡ä»¶å
æ¬å
³é®è¯ãæ¿ä¿®åä½ãå®å·¥ç¶æåå®å·¥æ¥æãå
³é®è¯å¹é
æ
éãæ´¾å·¥åãå·¥ç¨ç¼å·ãæ¿ä¿®äººåæç»å
容ã |
| | | - éæ± 25ï¼èå管çç¼è¾æ¶ï¼âä¸çº§èåâæ¹ä¸ºå¯ç¼è¾çº§èéæ©å¨ï¼ç¼è¾æ¶æé¤å½åèååå
¶å
¨é¨ä¸çº§ï¼æé®ç±»ååªè½éæ©èåç±»åç¶èç¹ã |
| | | - éæ± 25 åç«¯åæ¥å¢å ç¶èç¹å卿§ãèªå¼ç¨å循ç¯å±çº§æ ¡éªï¼é¿å
éè¿æ¥å£æé 循ç¯èåæ ã |
| | | |
| | | **ä¸»è¦æä»¶** |
| | | åç«¯ï¼ |
| | | |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/business/dto/CompletionFaultQueryItem.java`ï¼æ°å¢ï¼ |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/business/controller/WorkOrderController.java` |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/business/service/BusinessService.java` |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/bootstrap/DataInitializer.java` |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/system/service/SystemService.java` |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/system/service/ButtonPermissionCatalog.java` |
| | | |
| | | åç«¯ï¼ |
| | | |
| | | - `emergency-repair-web/src/views/completion/CompletionQuery.vue`ï¼æ°å¢ï¼ |
| | | - `emergency-repair-web/src/views/system/MenuManage.vue` |
| | | - `emergency-repair-web/src/components/AppHeader.vue`ã`Login.vue`ã`RepairLogo.vue`ã`layout/AppLayout.vue`ã`router/index.js`ã`utils/sensitive.js`ã`main.js` |
| | | - `emergency-repair-web/public/sensitive-words.json`ã`public/index.html` |
| | | - `requirement/ææè¯åå
¸.json` |
| | | |
| | | **éªè¯ç»æ** |
| | | - å端 `mvn -B -DskipTests compile` éè¿ï¼å端 `npm run build` éè¿ï¼ä»
ææ¢æå
ä½ç§¯åè¦ã |
| | | - `node .codex-run/verify-req23-25.js`ï¼é¡µé¢æ é¢å页头使ç¨â临æ¢ä¿®ç®¡çç³»ç» / ç½ç»çâï¼çæ¬é¢è²ä¸º `rgb(255, 107, 107)`ï¼å®å·¥æ¥é
æ¾ç¤ºæ
é `GZ-2026-012`ãå¤çæ
åµå卿ï¼èåç¼è¾ç¶èç¹æ¾ç¤ºâæ
é管çâä¸å¯ç¼è¾ã |
| | | - æ¥å£å®æµï¼ä¸´æ¶å䏿
éæå
¥ä¸¤å¼ 派工åå两æ¡å®å·¥è®°å½åï¼`GET /api/work-orders/completion/query?keyword=ZZ-MERGE-TEST-20261006` è¿å 1 个æ
éã2 å¼ æ´¾å·¥åï¼æ¯æ¡å 1 æ¡å¤çæ
åµå 1 æ¡å¨æã |
| | | - æµè§å¨å并宿µï¼é¡µé¢çæ 2 å¼ æ´¾å·¥åè¡ï¼æ
éç¼å·åå
æ ¼åªåºç° 1 次ï¼7 个æ
éä¿¡æ¯ååæ `rowspan=2`ï¼ä¸¤å¼ ååèªçå¤çæ
åµå卿忣叏æ¾ç¤ºï¼æ ç½ç»å¼å¸¸ï¼æªå¾ `.codex-run/completion-query-merge-verified.png`ãä¸´æ¶æ°æ®åºè®°å½å·²æ¸
çã |
| | | - èåç¶èç¹æ¥å£å®æµï¼ä¸´æ¶å建ç¶åèåæåï¼æç¶èåç§»å¨å°å
¶åèåä¸è¿åâä¸çº§èåä¸è½éæ©å½åèåçä¸çº§âï¼æåèåç§»å¨å°å
¶ä»ç¶èç¹æåï¼ä¸´æ¶èåå·²æ¸
çã |
| | | |
| | | **å½åè¿è¡ç¶æï¼2026-10-06ï¼** |
| | | - å端 `http://localhost:8082`ï¼å端 `http://127.0.0.1:8091`ï¼DM8 `127.0.0.1:5236`ï¼Edge CDP `9222`ã |
| | | - ç»å½ `admin / admin123`ï¼æ°æ®åº `SYSDBA/SYSDBA`ï¼ä¸å¡ schema `LX`ã |
| | | - å端æ¥å¿ï¼`.codex-run/backend-20261006-req23-25-v2.out.log`ã`.codex-run/backend-20261006-req23-25-v2.err.log`ã |
| | | |
| | | **æ¥ææ³¨æ** |
| | | - å®å·¥ä¿¡æ¯æ¥é
å½ååªæ±æ»å·²ç»åå¨å®å·¥å¡«æ¥è®°å½ `T_COMPLETION_INFO` çæ´¾å·¥åï¼ä»
å®å·¥å¤ç使ªå¡«æ¥å®å·¥ä¿¡æ¯ç派工åä¸ä¼åºç°å¨æ¥é
ç»æä¸ã |
| | | - å®å·¥æ¥é
æ¥å£å½å䏿¬¡è¿åå
¨é¨å¹é
ç»æï¼æ²¡æåé¡µï¼æ°æ®éè¿ä¸æ¥å¢å¤§æ¶å»ºè®®å¢å å页æå¯¼åºçç¥ã |
| | | - ä¸è¦æäº¤ `.codex-run`ãOffice éæä»¶æ `emergency-repair-web/public/æ°å»º ææ¬ææ¡£.txt`ã |
| | | - æ¬è½®ä»£ç ãéæ±æä»¶å交æ¥è®°å½ä¸å¹¶æäº¤å¹¶æ¨éå
¬å¸ Git ä»åº `main` 忝ã |
| | | |
| | | ## 21. æ¶å°¾å¿«ç
§ï¼2026-10-06 å¨ä¸ï¼éæ± 21 æé设置èå´åæ¢ï¼ |
| | | |
| | | **æ¬è½®å®æå
容** |
| | | - è§è²ç®¡çâæé设置âå¼¹çªå¢å âå
¨é¨ / 已设置â忢ï¼äº¤äºæ¹å¼ä¸âç¨æ·è®¾ç½®âä¿æä¸è´ã |
| | | - âå
¨é¨âæ¾ç¤ºå®æ´èååæé®æéæ ï¼ä¿çâå
¨éâååææéåæ¾ã |
| | | - â已设置âä»
æ¾ç¤ºå½åè§è²å·²ææèç¹åå
¶ç¶çº§è·¯å¾ï¼æ²¡æå·²è®¾ç½®æéæ¶æ¾ç¤ºç©ºç¶ææç¤ºã |
| | | - 忢èå´æ¶ä¼ä¿çå½åå°æªä¿åçå¾éç¶æï¼ååâå
¨é¨â忢å¤å®æ´æ åå½åé䏿éã |
| | | - å¼¹çªæ¯æ¬¡éæ°æå¼é»è®¤åå°âå
¨é¨âã |
| | | |
| | | **ä¸»è¦æä»¶** |
| | | - `emergency-repair-web/src/views/system/RolePermissionDialog.vue` |
| | | - `requirement/æ¹è¿20261003.txt`ï¼è¡¥å
éæ± 21 çèå´åæ¢å£å¾ï¼ |
| | | |
| | | **éªè¯ç»æ** |
| | | - å端 `npm run build` éè¿ï¼ä»
ææ¢æå
ä½ç§¯åè¦ã |
| | | - `node .codex-run/verify-req21-22.js` éè¿ï¼å·¦ä¾§æ âèåæé管çâï¼è§è²ç®¡çåå¨âæé设置âã |
| | | - å
¨é¨è§å¾æ¾ç¤º 119 个å¤éæ¡ã13 个已å¾éã1 个åéï¼å·²è®¾ç½®è§å¾æ¾ç¤º 14 个èç¹ï¼å为å½å已设置èç¹æç¶çº§è·¯å¾ï¼ååå
¨é¨åå¾éç¶ææ¢å¤ï¼æ ç½ç»å¼å¸¸ã |
| | | - æªå¾ï¼`.codex-run/role-permission-assigned-scope.png`ã |
| | | |
| | | **å½åè¿è¡ç¶æï¼2026-10-06ï¼** |
| | | - å端 `http://localhost:8082`ï¼å端 `http://127.0.0.1:8091`ï¼DM8 `127.0.0.1:5236`ï¼Edge CDP `9222`ã |
| | | - ç»å½ `admin / admin123`ï¼æ°æ®åº `SYSDBA/SYSDBA`ï¼ä¸å¡ schema `LX`ã |
| | | |
| | | **æ¥ææ³¨æ** |
| | | - ä¸è¦æäº¤ `.codex-run`ãOffice éæä»¶æ `emergency-repair-web/public/æ°å»º ææ¬ææ¡£.txt`ã |
| | | - æ¬è½®ä»£ç ãéæ±æä»¶å交æ¥è®°å½ä¸å¹¶æäº¤å¹¶æ¨éå
¬å¸ Git ä»åº `main` 忝ã |
| | | ## 22. æ¶å°¾å¿«ç
§ï¼2026-10-06 å¨ä¸ï¼éæ± 26 æ¥å¿æ¥æèå´åéç¬¦ï¼ |
| | | |
| | | **æ¬è½®å®æå
容** |
| | | - ä¿®å¤ç»å½æ¥å¿ãæä½æ¥å¿ãé误æ¥å¿ä¸ä¸ªé¡µé¢æ¥æèå´ç»ä»¶ä¸é´âè³âæ¾ç¤ºä¸å®æ´çé®é¢ã |
| | | - ä¸ä¸ªé¡µé¢ç `.el-range-separator` åºå®ä¸º `18px` å¹¶å±
䏿¾ç¤ºï¼é¿å
Element UI é»è®¤ç¾åæ¯å®½åº¦å¨ 260px æ¥ææ§ä»¶ä¸è¢«å缩è£åã |
| | | |
| | | **ä¸»è¦æä»¶** |
| | | - `emergency-repair-web/src/views/system/LoginLogManage.vue` |
| | | - `emergency-repair-web/src/views/system/OperationLogManage.vue` |
| | | - `emergency-repair-web/src/views/system/ErrorLogManage.vue` |
| | | - `requirement/æ¹è¿20261003.txt`ï¼è¡¥å
éæ± 26ï¼ |
| | | |
| | | **éªè¯ç»æ** |
| | | - å端 `npm run build` éè¿ï¼ä»
ææ¢æå
ä½ç§¯åè¦ã |
| | | - `node .codex-run/verify-req26.js` éè¿ï¼ä¸ä¸ªæ¥å¿é¡µé¢åæ¾å°âè³âï¼å®é
宽度ã`clientWidth` å `scrollWidth` å为 `18px`ï¼è®¡ç®æ ·å¼ä¸º `flex-basis: 18px`ã`min-width: 18px`ãå±
ä¸ï¼æ ç½ç»å¼å¸¸ã |
| | | - æªå¾ï¼`.codex-run/login-log-date-separator.png`ã`.codex-run/operation-log-date-separator.png`ã`.codex-run/error-log-date-separator.png`ã |
| | | |
| | | **å½åè¿è¡ç¶æï¼2026-10-06ï¼** |
| | | - å端 `http://localhost:8082`ï¼å端 `http://127.0.0.1:8091`ï¼DM8 `127.0.0.1:5236`ï¼Edge CDP `9222`ã |
| | | - ç»å½ `admin / admin123`ï¼æ°æ®åº `SYSDBA/SYSDBA`ï¼ä¸å¡ schema `LX`ã |
| | | |
| | | **æ¥ææ³¨æ** |
| | | - ä¸è¦æäº¤ `.codex-run`ãOffice éæä»¶æ `emergency-repair-web/public/æ°å»º ææ¬ææ¡£.txt`ã |
| | | - æ¬è½®ä»£ç ãéæ±æä»¶å交æ¥è®°å½ä¸å¹¶æäº¤å¹¶æ¨éå
¬å¸ Git ä»åº `main` 忝ã |
| | | |
| | | ## 23. æ¶å°¾å¿«ç
§ï¼2026-10-06 å¨ä¸ï¼éæ± 27 设å¤éæ©èå´ï¼ |
| | | |
| | | **æ¬è½®å®æå
容** |
| | | - æ°å¢æ
ééæ© `rk_Xå·`ï¼äº§åç»ææ `side` ç±»åèç¹ï¼åï¼è®¾å¤éæ©å¨åªä»¥æéèç¹ä¸ºæ ¹å±ç¤ºå
¶ä¸çº§ï¼ä¸ååæ¶è¿ååå·æ ¹èç¹åæ´æ£µäº§åç»ææ ã |
| | | - 设å¤éæ©å¨æå¼éå®èå´æ¶èªå¨å±å¼è¿åç `side` æ ¹èç¹ï¼é¦å±ç´æ¥æ¾ç¤ºç¬¬ä¸å±ç³»ç»èç¹ï¼åç»ç»§ç»æåé»è¾æå è½½ã |
| | | - `xNo` 为空æ¶ä»è¿å宿´äº§åç»ææ ï¼ä¸å½±å产åç»ææ 管çå䏿 `rk_Xå·` çéç设å¤éæ©åºæ¯ã |
| | | |
| | | **ä¸»è¦æä»¶** |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/system/service/SystemService.java` |
| | | - `emergency-repair-web/src/components/ProductTreePicker.vue` |
| | | - `requirement/æ¹è¿20261003.txt`ï¼è¡¥å
éæ± 27ï¼ |
| | | |
| | | **éªè¯ç»æ** |
| | | - å端 `mvn -B -DskipTests compile`ã`mvn -B -DskipTests package` éè¿ï¼å端 `npm run build` éè¿ï¼ä»
ææ¢æå
ä½ç§¯åè¦ã |
| | | - `GET /api/system/tree/product/side-roots?xNo=203` ä¿®å¤ååªè¿å 1 ä¸ªæ ¹èç¹ï¼`id=1280000000000009729`ã`name=203`ã`layer=side`ã`level=2`ï¼`201 / 203 / 301 / æµè¯T` åå«åªè¿å对åºèç¹ã |
| | | - `node .codex-run/verify-req27.js` éè¿ï¼æ ¹èç¹ææ¬ä»
为 `203`ï¼èªå¨å±å¼ 1 ä¸ªæ ¹èç¹ï¼é¦å±æ¾ç¤ºå
¶ä¸çº§ç³»ç»èç¹ï¼æ ç½ç»å¼å¸¸ã |
| | | - 请æ±é¾è·¯ç¡®è®¤ï¼`/api/system/tree/product/side-roots?xNo=203` åç»§ç»è¯·æ± `/api/system/tree/product/children?parentId=1280000000000009729`ãæªå¾ï¼`.codex-run/req27-side-tree.png`ã |
| | | |
| | | **å½åè¿è¡ç¶æï¼2026-10-06ï¼** |
| | | - å端 `http://localhost:8082`ï¼PID `138164`ï¼å端 `http://127.0.0.1:8091`ï¼PID `148268`ï¼DM8 `127.0.0.1:5236`ï¼Edge CDP `9222`ã |
| | | - ç»å½ `admin / admin123`ï¼æ°æ®åº `SYSDBA/SYSDBA`ï¼ä¸å¡ schema `LX`ã |
| | | - å端æ¥å¿ï¼`.codex-run/backend-20261006-req27.out.log`ã`.codex-run/backend-20261006-req27.err.log`ã |
| | | |
| | | **æ¥ææ³¨æ** |
| | | - éæ± 26 å·²æäº¤å¹¶æ¨éï¼æäº¤ `f651ad5`ï¼`fix: show full date range separator in log filters`ï¼ãéæ± 27 å·²æäº¤å¹¶æ¨éï¼æäº¤ `f9114ff`ï¼`fix: limit equipment picker to selected X number`ï¼ã |
| | | - ä¸è¦æäº¤ `.codex-run`ãOffice éæä»¶æ `emergency-repair-web/public/æ°å»º ææ¬ææ¡£.txt`ã |
| | | - æ¬è½®ä»£ç ãéæ±æä»¶å交æ¥è®°å½ä¸å¹¶æäº¤å¹¶æ¨éå
¬å¸ Git ä»åº `main` 忝ã |
| | | |
| | | ## 24. æ¶å°¾å¿«ç
§ï¼2026-10-06 å¨ä¸ï¼èåå称被å¯å¨åå§åè¦çï¼ |
| | | |
| | | **é®é¢ä¸ä¿®å¤** |
| | | - æå·¥æç³»ç»é
ç½®ä¸çâ产åç»ææ 管çâæ¹ä¸ºâ产åç»ææ âåï¼å端éå¯ä¼æ¢å¤â管çâäºåã |
| | | - åå æ¯ `DataInitializer.ensureMenu()` æ¯æ¬¡å¯å¨é½ä¼æ¿ä»£ç ä¸çé»è®¤åç§°è¦çå·²åå¨èåã |
| | | - å·²æ¹ä¸ºåªå¨æ°å»ºèåæ¶ä½¿ç¨é»è®¤åç§°ï¼å·²åå¨èååªä¿®å¤ç¶èç¹ãæåºãç±»ååå¯è§æ§ï¼åç§°ç±èå管ç页é¢ç»´æ¤ã |
| | | - æ°å»ºæ°æ®åºçé»è®¤èååç§°åæ¥æ¹ä¸ºâ产åç»ææ âã |
| | | |
| | | **ä¸»è¦æä»¶** |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/bootstrap/DataInitializer.java` |
| | | |
| | | **éªè¯ç»æ** |
| | | - å端 `mvn -B -DskipTests compile`ã`mvn -B -DskipTests package` éè¿ã |
| | | - å°æ°æ®åºèå `id=2101493905517301763`ã`url=/system/product-tree` çåç§°æ¹ä¸ºâ产åç»ææ âåï¼ä½¿ç¨æ° jar éå¯åç«¯ï¼æ¥å£å次æ¥è¯¢ä»ä¸ºâ产åç»ææ âã |
| | | - æ°å端 PID `151580`ï¼çå¬ `8091`ï¼æ¥å¿ï¼`.codex-run/backend-20261006-menu-name.out.log`ã`.codex-run/backend-20261006-menu-name.err.log`ã |
| | |
| | | import java.util.LinkedHashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Set; |
| | | |
| | | @Component |
| | | @Order(2) |
| | |
| | | public void run(String... args) { |
| | | seedMenusAndRoles(); |
| | | ensureSystemMenus(); |
| | | ensureCompletionMenus(); |
| | | normalizeSensitiveTerms(); |
| | | normalizeEquipmentTerms(); |
| | | seedButtonMenus(); |
| | | ensureAdminRoleAssignment(); |
| | | ensureMajorConfigMenus(); |
| | | ensureRoleMenuAncestors(); |
| | | seedMajors(); |
| | | seedContractors(); |
| | | seedTrees(); |
| | |
| | | createMenu("dispatch", fault, "rk_XCBå©çæ´¾å", "/fault/dispatch", null, 1, 60); |
| | | createMenu("completion-list", fault, "å®å·¥æ
åµå¤ç", "/fault/completion", null, 1, 70); |
| | | createMenu("completion-report", fault, "å®å·¥ä¿¡æ¯ä¸æ¥", "/completion/report", null, 1, 80); |
| | | createMenu("completion-query", fault, "å®å·¥ä¿¡æ¯æ¥é
", "/completion/query", null, 1, 90); |
| | | |
| | | Long acceptance = createMenu("acceptance", null, "å®å·¥éªæ¶", null, "el-icon-circle-check", 0, 30); |
| | | createMenu("accept-application", acceptance, "éªæ¶ç³è¯·", "/acceptance/application", null, 1, 10); |
| | |
| | | createMenu("report-trend", reports, "æ
éæ°/å¤åå¤ä»¶æ¶è年度è¶å¿", "/reports/trend", null, 1, 50); |
| | | |
| | | Long system = createMenu("system", null, "ç³»ç»ç®¡ç", null, "el-icon-setting", 0, 50); |
| | | createMenu("organization", system, "ç»ç»æºæ", "/system/organization", null, 1, 5); |
| | | createMenu("contractors", system, "æ¿ä¿®åä½ç®¡ç", "/system/contractors", null, 1, 10); |
| | | createMenu("product-tree", system, "产åç»ææ 管ç", "/system/product-tree", null, 1, 20); |
| | | createMenu("base-equipment", system, "设å¤åºç¡åºç®¡ç", "/system/base-equipment", null, 1, 30); |
| | | createMenu("engineering-majors", system, "å·¥ç¨ä¸ä¸é
ç½®", "/system/engineering-majors", null, 1, 35); |
| | | createMenu("team-majors", system, "rk_Téä¸ä¸é
ç½®", "/system/team-majors", null, 1, 36); |
| | | createMenu("roles", system, "è§è²ç®¡ç", "/system/roles", null, 1, 40); |
| | | createMenu("menus", system, "èå管ç", "/system/menus", null, 1, 50); |
| | | createMenu("menu-permissions", system, "èåæé管ç", "/system/menu-permissions", null, 1, 60); |
| | | createMenu("three-member", system, "ä¸åæ§å¶", "/system/three-member", null, 1, 70); |
| | | Long systemConfig = createMenu("system-config", system, "ç³»ç»é
ç½®", null, "el-icon-s-tools", 0, 10); |
| | | createMenu("organization", systemConfig, "ç»ç»æºæ", "/system/organization", null, 1, 10); |
| | | createMenu("contractors", systemConfig, "æ¿ä¿®åä½ç®¡ç", "/system/contractors", null, 1, 20); |
| | | createMenu("product-tree", systemConfig, "产åç»ææ ", "/system/product-tree", null, 1, 30); |
| | | createMenu("base-equipment", systemConfig, "设å¤åºç¡åºç®¡ç", "/system/base-equipment", null, 1, 40); |
| | | createMenu("engineering-majors", systemConfig, "å·¥ç¨ä¸ä¸é
ç½®", "/system/engineering-majors", null, 1, 50); |
| | | createMenu("team-majors", systemConfig, "rk_Téä¸ä¸é
ç½®", "/system/team-majors", null, 1, 60); |
| | | createMenu("three-member", systemConfig, "ä¸åæ§å¶", "/system/three-member", null, 1, 70); |
| | | |
| | | Long permissionConfig = createMenu("permission-config", system, "æéé
ç½®", null, "el-icon-lock", 0, 20); |
| | | createMenu("users", permissionConfig, "ç¨æ·ç®¡ç", "/system/users", null, 1, 10); |
| | | createMenu("roles", permissionConfig, "è§è²ç®¡ç", "/system/roles", null, 1, 20); |
| | | createMenu("menus", permissionConfig, "èå管ç", "/system/menus", null, 1, 30); |
| | | |
| | | Long logConfig = createMenu("log-config", system, "æ¥å¿ç®¡ç", null, "el-icon-document", 0, 30); |
| | | createMenu("login-logs", logConfig, "ç»å½æ¥å¿", "/system/login-logs", null, 1, 10); |
| | | createMenu("operation-logs", logConfig, "æä½æ¥å¿", "/system/operation-logs", null, 1, 20); |
| | | createMenu("error-logs", logConfig, "é误æ¥å¿", "/system/error-logs", null, 1, 30); |
| | | } else { |
| | | for (SysMenu menu : menuMapper.selectList(null)) { |
| | | menuIds.put(menu.getUrl() == null ? "id-" + menu.getId() : menu.getUrl(), menu.getId()); |
| | |
| | | SysMenu system = menuMapper.selectOne(Wrappers.<SysMenu>lambdaQuery() |
| | | .eq(SysMenu::getName, "ç³»ç»ç®¡ç") |
| | | .last("LIMIT 1")); |
| | | Long pid = system == null ? 0L : system.getId(); |
| | | ensureMenu(pid, "å·¥ç¨ä¸ä¸é
ç½®", "/system/engineering-majors", 35); |
| | | ensureMenu(pid, "rk_Téä¸ä¸é
ç½®", "/system/team-majors", 36); |
| | | SysMenu config = menuMapper.selectOne(Wrappers.<SysMenu>lambdaQuery() |
| | | .eq(SysMenu::getName, "ç³»ç»é
ç½®") |
| | | .eq(SysMenu::getMenuType, 0) |
| | | .last("LIMIT 1")); |
| | | Long pid = config == null ? (system == null ? 0L : system.getId()) : config.getId(); |
| | | ensureMenu(pid, "å·¥ç¨ä¸ä¸é
ç½®", "/system/engineering-majors", 50); |
| | | ensureMenu(pid, "rk_Téä¸ä¸é
ç½®", "/system/team-majors", 60); |
| | | } |
| | | |
| | | private void ensureMenu(Long pid, String name, String url, int sort) { |
| | | if (menuMapper.selectCount(Wrappers.<SysMenu>lambdaQuery().eq(SysMenu::getUrl, url)) > 0) { |
| | | return; |
| | | private Long ensureGroupMenu(Long pid, String name, String icon, int sort) { |
| | | SysMenu menu = menuMapper.selectOne(Wrappers.<SysMenu>lambdaQuery() |
| | | .eq(SysMenu::getPid, pid) |
| | | .eq(SysMenu::getName, name) |
| | | .eq(SysMenu::getMenuType, 0) |
| | | .last("LIMIT 1")); |
| | | LocalDateTime now = LocalDateTime.now(); |
| | | if (menu == null) { |
| | | menu = new SysMenu(); |
| | | menu.setPid(pid); |
| | | menu.setName(name); |
| | | menu.setIcon(icon); |
| | | menu.setMenuType(0); |
| | | menu.setSortNo(sort); |
| | | menu.setVisible(1); |
| | | menu.setCreateTime(now); |
| | | menu.setUpdateTime(now); |
| | | menuMapper.insert(menu); |
| | | return menu.getId(); |
| | | } |
| | | SysMenu menu = new SysMenu(); |
| | | menu.setPid(pid); |
| | | menu.setName(name); |
| | | menu.setUrl(url); |
| | | menu.setMenuType(1); |
| | | menu.setSortNo(sort); |
| | | menu.setVisible(1); |
| | | menu.setCreateTime(LocalDateTime.now()); |
| | | menu.setUpdateTime(LocalDateTime.now()); |
| | | menuMapper.insert(menu); |
| | | boolean changed = !java.util.Objects.equals(menu.getIcon(), icon) |
| | | || !java.util.Objects.equals(menu.getSortNo(), sort) |
| | | || !java.util.Objects.equals(menu.getVisible(), 1); |
| | | if (changed) { |
| | | menu.setIcon(icon); |
| | | menu.setSortNo(sort); |
| | | menu.setVisible(1); |
| | | menu.setUpdateTime(now); |
| | | menuMapper.updateById(menu); |
| | | } |
| | | return menu.getId(); |
| | | } |
| | | |
| | | private Long ensureMenu(Long pid, String name, String url, int sort) { |
| | | SysMenu menu = menuMapper.selectOne(Wrappers.<SysMenu>lambdaQuery() |
| | | .eq(SysMenu::getUrl, url).last("LIMIT 1")); |
| | | LocalDateTime now = LocalDateTime.now(); |
| | | if (menu == null) { |
| | | menu = new SysMenu(); |
| | | menu.setPid(pid); |
| | | menu.setName(name); |
| | | menu.setUrl(url); |
| | | menu.setMenuType(1); |
| | | menu.setSortNo(sort); |
| | | menu.setVisible(1); |
| | | menu.setCreateTime(now); |
| | | menu.setUpdateTime(now); |
| | | menuMapper.insert(menu); |
| | | return menu.getId(); |
| | | } |
| | | // Existing menu names are user-managed; startup only repairs structural fields. |
| | | boolean changed = !java.util.Objects.equals(menu.getPid(), pid) |
| | | || !java.util.Objects.equals(menu.getSortNo(), sort) |
| | | || !java.util.Objects.equals(menu.getMenuType(), 1) |
| | | || !java.util.Objects.equals(menu.getVisible(), 1); |
| | | if (changed) { |
| | | menu.setPid(pid); |
| | | menu.setMenuType(1); |
| | | menu.setSortNo(sort); |
| | | menu.setVisible(1); |
| | | menu.setUpdateTime(now); |
| | | menuMapper.updateById(menu); |
| | | } |
| | | return menu.getId(); |
| | | } |
| | | |
| | | private void ensureCompletionMenus() { |
| | | SysMenu fault = menuMapper.selectOne(Wrappers.<SysMenu>lambdaQuery() |
| | | .eq(SysMenu::getName, "æ
é管ç") |
| | | .eq(SysMenu::getMenuType, 0) |
| | | .last("LIMIT 1")); |
| | | Long pid = fault == null ? 0L : fault.getId(); |
| | | ensureMenu(pid, "å®å·¥ä¿¡æ¯æ¥é
", "/completion/query", 90); |
| | | grantAdminMenus("/completion/query"); |
| | | } |
| | | |
| | | private void ensureSystemMenus() { |
| | | SysMenu system = menuMapper.selectOne(Wrappers.<SysMenu>lambdaQuery() |
| | | .eq(SysMenu::getName, "\u7cfb\u7edf\u7ba1\u7406").last("LIMIT 1")); |
| | | Long pid = system == null ? 0L : system.getId(); |
| | | ensureMenu(pid, "ç»ç»æºæ", "/system/organization", 5); |
| | | ensureMenu(pid, "\u7528\u6237\u7ba1\u7406", "/system/users", 37); |
| | | ensureMenu(pid, "\u767b\u5f55\u65e5\u5fd7", "/system/login-logs", 65); |
| | | ensureMenu(pid, "\u64cd\u4f5c\u65e5\u5fd7", "/system/operation-logs", 66); |
| | | grantAdminMenus("/system/organization", "/system/users", "/system/login-logs", "/system/operation-logs"); |
| | | Long systemConfig = ensureGroupMenu(pid, "ç³»ç»é
ç½®", "el-icon-s-tools", 10); |
| | | Long permissionConfig = ensureGroupMenu(pid, "æéé
ç½®", "el-icon-lock", 20); |
| | | Long logConfig = ensureGroupMenu(pid, "æ¥å¿ç®¡ç", "el-icon-document", 30); |
| | | |
| | | ensureMenu(systemConfig, "ç»ç»æºæ", "/system/organization", 10); |
| | | ensureMenu(systemConfig, "æ¿ä¿®åä½ç®¡ç", "/system/contractors", 20); |
| | | ensureMenu(systemConfig, "产åç»ææ ", "/system/product-tree", 30); |
| | | ensureMenu(systemConfig, "设å¤åºç¡åºç®¡ç", "/system/base-equipment", 40); |
| | | ensureMenu(systemConfig, "å·¥ç¨ä¸ä¸é
ç½®", "/system/engineering-majors", 50); |
| | | ensureMenu(systemConfig, "rk_Téä¸ä¸é
ç½®", "/system/team-majors", 60); |
| | | ensureMenu(systemConfig, "ä¸åæ§å¶", "/system/three-member", 70); |
| | | |
| | | ensureMenu(permissionConfig, "ç¨æ·ç®¡ç", "/system/users", 10); |
| | | ensureMenu(permissionConfig, "è§è²ç®¡ç", "/system/roles", 20); |
| | | ensureMenu(permissionConfig, "èå管ç", "/system/menus", 30); |
| | | |
| | | ensureMenu(logConfig, "ç»å½æ¥å¿", "/system/login-logs", 10); |
| | | ensureMenu(logConfig, "æä½æ¥å¿", "/system/operation-logs", 20); |
| | | ensureMenu(logConfig, "é误æ¥å¿", "/system/error-logs", 30); |
| | | |
| | | removeMenuByUrl("/system/menu-permissions"); |
| | | grantAdminMenuIds(systemConfig, permissionConfig, logConfig); |
| | | grantAdminMenus("/system/organization", "/system/contractors", "/system/product-tree", |
| | | "/system/base-equipment", "/system/engineering-majors", "/system/team-majors", |
| | | "/system/three-member", "/system/users", "/system/roles", "/system/menus", |
| | | "/system/login-logs", "/system/operation-logs", "/system/error-logs"); |
| | | } |
| | | |
| | | private void grantAdminMenus(String... urls) { |
| | |
| | | } |
| | | } |
| | | |
| | | private void grantAdminMenuIds(Long... menuIds) { |
| | | SysRole adminRole = roleMapper.selectOne(Wrappers.<SysRole>lambdaQuery() |
| | | .eq(SysRole::getCode, "SYS_ADMIN").last("LIMIT 1")); |
| | | if (adminRole == null) { |
| | | return; |
| | | } |
| | | for (Long menuId : menuIds) { |
| | | ensureRoleMenu(adminRole.getId(), menuId); |
| | | ensureTenantMenu(1L, menuId); |
| | | } |
| | | } |
| | | |
| | | private void removeMenuByUrl(String url) { |
| | | SysMenu menu = menuMapper.selectOne(Wrappers.<SysMenu>lambdaQuery() |
| | | .eq(SysMenu::getUrl, url).last("LIMIT 1")); |
| | | if (menu == null) { |
| | | return; |
| | | } |
| | | List<SysMenu> children = menuMapper.selectList(Wrappers.<SysMenu>lambdaQuery() |
| | | .eq(SysMenu::getPid, menu.getId())); |
| | | for (SysMenu child : children) { |
| | | removeMenuRelations(child.getId()); |
| | | menuMapper.deleteById(child.getId()); |
| | | } |
| | | removeMenuRelations(menu.getId()); |
| | | menuMapper.deleteById(menu.getId()); |
| | | } |
| | | |
| | | private void removeMenuRelations(Long menuId) { |
| | | roleMenuMapper.delete(Wrappers.<SysRoleMenu>lambdaQuery().eq(SysRoleMenu::getMenuId, menuId)); |
| | | tenantMenuMapper.delete(Wrappers.<SysTenantMenu>lambdaQuery().eq(SysTenantMenu::getMenuId, menuId)); |
| | | } |
| | | |
| | | private void ensureRoleMenuAncestors() { |
| | | Map<Long, SysMenu> menuById = menuMapper.selectList(null).stream() |
| | | .collect(java.util.stream.Collectors.toMap(SysMenu::getId, item -> item)); |
| | | List<SysRoleMenu> relations = new ArrayList<>(roleMenuMapper.selectList(null)); |
| | | Set<String> existing = relations.stream() |
| | | .map(item -> item.getRoleId() + "-" + item.getMenuId()) |
| | | .collect(java.util.stream.Collectors.toSet()); |
| | | for (SysRoleMenu relation : relations) { |
| | | SysMenu menu = menuById.get(relation.getMenuId()); |
| | | Long cursor = menu == null ? null : menu.getPid(); |
| | | int guard = 0; |
| | | while (cursor != null && cursor != 0L && guard++ < 32) { |
| | | String key = relation.getRoleId() + "-" + cursor; |
| | | if (existing.add(key)) { |
| | | ensureRoleMenu(relation.getRoleId(), cursor); |
| | | } |
| | | SysMenu parent = menuById.get(cursor); |
| | | cursor = parent == null ? null : parent.getPid(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Materializes the declarative button catalog as MENU_TYPE=2 rows. |
| | | * Existing rows are left untouched so manual menu edits are preserved. |
| | |
| | | "T_FAULT", "T_ENGINEERING_MAJOR", "T_TEAM_MAJOR", "T_CONTRACTOR", |
| | | "T_BASE_EQUIPMENT", "T_PRODUCT_TREE", "T_WORK_ORDER", "T_COMPLETION_INFO", |
| | | "T_ACCEPTANCE", "T_SYS_MENU", "T_SYS_ROLE", "T_SYS_ROLE_MENU", |
| | | "T_SYS_TENANT_MENU", "T_SYS_ROLE_USER", "T_SYS_LOGIN_LOG", "T_SYS_OPERATION_LOG" |
| | | "T_SYS_TENANT_MENU", "T_SYS_ROLE_USER", "T_SYS_LOGIN_LOG", "T_SYS_OPERATION_LOG", |
| | | "T_SYS_ERROR_LOG" |
| | | }; |
| | | private final JdbcTemplate jdbcTemplate; |
| | | |
| | |
| | | ensureIndex("IX_T_COMPLETION_WORK_ORDER", "CREATE INDEX LX.IX_T_COMPLETION_WORK_ORDER ON LX.T_COMPLETION_INFO(WORK_ORDER_ID)"); |
| | | ensureIndex("IX_T_SYS_LOGIN_LOG_TIME", "CREATE INDEX LX.IX_T_SYS_LOGIN_LOG_TIME ON LX.T_SYS_LOGIN_LOG(LOGIN_TIME)"); |
| | | ensureIndex("IX_T_SYS_OPERATION_LOG_TIME", "CREATE INDEX LX.IX_T_SYS_OPERATION_LOG_TIME ON LX.T_SYS_OPERATION_LOG(CREATE_TIME)"); |
| | | ensureIndex("IX_T_SYS_ERROR_LOG_TIME", "CREATE INDEX LX.IX_T_SYS_ERROR_LOG_TIME ON LX.T_SYS_ERROR_LOG(ERROR_TIME)"); |
| | | ensureIndex("IX_T_SYS_ROLE_USER_USER", "CREATE INDEX LX.IX_T_SYS_ROLE_USER_USER ON LX.T_SYS_ROLE_USER(USER_ID)"); |
| | | } |
| | | |
| | |
| | | ddl.put("T_SYS_ROLE_USER", "CREATE TABLE LX.T_SYS_ROLE_USER (ID BIGINT NOT NULL PRIMARY KEY, ROLE_ID BIGINT, USER_ID BIGINT, CREATE_TIME TIMESTAMP)"); |
| | | ddl.put("T_SYS_LOGIN_LOG", "CREATE TABLE LX.T_SYS_LOGIN_LOG (ID BIGINT NOT NULL PRIMARY KEY, USER_ID BIGINT, USER_NAME VARCHAR(120), NICK_NAME VARCHAR(120), DEPT_NAME VARCHAR(120), LOGIN_IP VARCHAR(200), STATUS VARCHAR(20), MESSAGE VARCHAR(500), LOGIN_TIME TIMESTAMP)"); |
| | | ddl.put("T_SYS_OPERATION_LOG", "CREATE TABLE LX.T_SYS_OPERATION_LOG (ID BIGINT NOT NULL PRIMARY KEY, USER_ID BIGINT, USER_NAME VARCHAR(120), NICK_NAME VARCHAR(120), MODULE VARCHAR(100), OPERATION_TYPE VARCHAR(40), TARGET_TYPE VARCHAR(100), TARGET_ID VARCHAR(80), DESCRIPTION VARCHAR(500), REQUEST_METHOD VARCHAR(10), REQUEST_URI VARCHAR(300), RESULT VARCHAR(20), CREATE_TIME TIMESTAMP)"); |
| | | ddl.put("T_SYS_ERROR_LOG", "CREATE TABLE LX.T_SYS_ERROR_LOG (ID BIGINT NOT NULL PRIMARY KEY, USER_ID BIGINT, USER_NAME VARCHAR(120), NICK_NAME VARCHAR(120), MODULE VARCHAR(100), REQUEST_METHOD VARCHAR(10), REQUEST_URI VARCHAR(500), EXCEPTION_TYPE VARCHAR(300), MESSAGE VARCHAR(2000), STACK_TRACE CLOB, ERROR_TIME TIMESTAMP)"); |
| | | return ddl; |
| | | } |
| | | } |
| | |
| | | package com.emergencyrepair.business.controller; |
| | | |
| | | import com.emergencyrepair.business.dto.Requests; |
| | | import com.emergencyrepair.business.dto.CompletionFaultQueryItem; |
| | | import com.emergencyrepair.business.entity.CompletionInfo; |
| | | import com.emergencyrepair.business.entity.WorkOrder; |
| | | import com.emergencyrepair.business.service.BusinessService; |
| | | import com.emergencyrepair.common.Result; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.format.annotation.DateTimeFormat; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.PathVariable; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | |
| | | import org.springframework.web.bind.annotation.RequestParam; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import java.time.LocalDate; |
| | | import java.util.List; |
| | | |
| | | @RestController |
| | |
| | | return Result.ok(businessService.listWorkOrders(status, keyword, completionReportable)); |
| | | } |
| | | |
| | | @GetMapping("/completion/query") |
| | | public Result<List<CompletionFaultQueryItem>> completionQuery( |
| | | @RequestParam(required = false) String keyword, |
| | | @RequestParam(required = false) String completionStatus, |
| | | @RequestParam(required = false) String contractorName, |
| | | @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate startDate, |
| | | @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate endDate) { |
| | | return Result.ok(businessService.listCompletionQueries(keyword, completionStatus, contractorName, startDate, endDate)); |
| | | } |
| | | |
| | | @GetMapping("/{id}") |
| | | public Result<WorkOrder> detail(@PathVariable Long id) { |
| | | return Result.ok(businessService.getWorkOrder(id)); |
| New file |
| | |
| | | package com.emergencyrepair.business.dto; |
| | | |
| | | import lombok.Data; |
| | | import com.fasterxml.jackson.annotation.JsonProperty; |
| | | |
| | | import java.time.LocalDate; |
| | | import java.time.LocalDateTime; |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | |
| | | @Data |
| | | public class CompletionFaultQueryItem { |
| | | private String faultId; |
| | | private String faultCode; |
| | | @JsonProperty("xNo") |
| | | private String xNo; |
| | | private String equipmentName; |
| | | private String parentSystem; |
| | | private String engineeringMajor; |
| | | private String teamMajor; |
| | | private String reportUnit; |
| | | private LocalDate reportDate; |
| | | private String owner; |
| | | private String engineeringNameDesc; |
| | | private String taskStatus; |
| | | private List<WorkOrderCompletionItem> workOrders = new ArrayList<>(); |
| | | |
| | | @Data |
| | | public static class WorkOrderCompletionItem { |
| | | private String workOrderId; |
| | | private String workOrderNo; |
| | | private String contractorName; |
| | | private String completionStatus; |
| | | private String engineeringNo; |
| | | private LocalDate startDate; |
| | | private LocalDate finishDate; |
| | | private String contractorPerson; |
| | | @JsonProperty("tInternalConstruction") |
| | | private String tInternalConstruction; |
| | | private String relatedProject; |
| | | private Integer durationDays; |
| | | private Integer personCount; |
| | | private String acceptanceMode; |
| | | private String qualityInfo; |
| | | private String reasonAnalysis; |
| | | private String faultDescription; |
| | | private String itemName; |
| | | private LocalDateTime submitTime; |
| | | private List<Requests.SolutionItem> solutions = new ArrayList<>(); |
| | | private List<Requests.MaterialItem> materials = new ArrayList<>(); |
| | | } |
| | | } |
| | |
| | | @Data |
| | | public static class MenuPermissionRequest { |
| | | private Long roleId; |
| | | private Long tenantId; |
| | | private List<Long> roleMenuIds = new ArrayList<>(); |
| | | private List<Long> tenantMenuIds = new ArrayList<>(); |
| | | } |
| | | @Data |
| | | public static class UserRoleRequest { |
| | |
| | | import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.emergencyrepair.business.dto.Requests; |
| | | import com.emergencyrepair.business.dto.CompletionFaultQueryItem; |
| | | import com.emergencyrepair.business.entity.Acceptance; |
| | | import com.emergencyrepair.business.entity.CompletionInfo; |
| | | import com.emergencyrepair.business.entity.Fault; |
| | |
| | | import java.util.Arrays; |
| | | import java.util.Collections; |
| | | import java.util.Collection; |
| | | import java.util.Comparator; |
| | | import java.util.Objects; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.LinkedHashSet; |
| | |
| | | return created; |
| | | } |
| | | |
| | | public List<CompletionFaultQueryItem> listCompletionQueries(String keyword, String completionStatus, |
| | | String contractorName, LocalDate startDate, |
| | | LocalDate endDate) { |
| | | List<CompletionInfo> completions = completionInfoMapper.selectList(Wrappers.<CompletionInfo>lambdaQuery() |
| | | .orderByDesc(CompletionInfo::getSubmitTime).orderByDesc(CompletionInfo::getUpdateTime)); |
| | | if (completions.isEmpty()) { |
| | | return Collections.emptyList(); |
| | | } |
| | | |
| | | Set<Long> workOrderIds = completions.stream().map(CompletionInfo::getWorkOrderId) |
| | | .filter(Objects::nonNull).collect(Collectors.toSet()); |
| | | if (workOrderIds.isEmpty()) { |
| | | return Collections.emptyList(); |
| | | } |
| | | Map<Long, WorkOrder> workOrderMap = workOrderMapper.selectBatchIds(workOrderIds).stream() |
| | | .collect(Collectors.toMap(WorkOrder::getId, item -> item)); |
| | | Set<Long> faultIds = workOrderMap.values().stream().map(WorkOrder::getFaultId) |
| | | .filter(Objects::nonNull).collect(Collectors.toSet()); |
| | | if (faultIds.isEmpty()) { |
| | | return Collections.emptyList(); |
| | | } |
| | | Map<Long, Fault> faultMap = faultMapper.selectBatchIds(faultIds).stream() |
| | | .collect(Collectors.toMap(Fault::getId, item -> item)); |
| | | |
| | | String normalizedKeyword = StringUtils.hasText(keyword) ? keyword.trim().toLowerCase() : null; |
| | | String normalizedContractor = StringUtils.hasText(contractorName) ? contractorName.trim().toLowerCase() : null; |
| | | Map<Long, CompletionFaultQueryItem> grouped = new LinkedHashMap<>(); |
| | | for (CompletionInfo completion : completions) { |
| | | WorkOrder workOrder = workOrderMap.get(completion.getWorkOrderId()); |
| | | if (workOrder == null) { |
| | | continue; |
| | | } |
| | | if (StringUtils.hasText(completionStatus) |
| | | && !completionStatus.equalsIgnoreCase(workOrder.getCompletionStatus())) { |
| | | continue; |
| | | } |
| | | if (normalizedContractor != null && !containsIgnoreCase(workOrder.getContractorName(), normalizedContractor)) { |
| | | continue; |
| | | } |
| | | LocalDate completionDate = completion.getFinishDate() != null |
| | | ? completion.getFinishDate() |
| | | : (completion.getSubmitTime() == null ? null : completion.getSubmitTime().toLocalDate()); |
| | | if (startDate != null && (completionDate == null || completionDate.isBefore(startDate))) { |
| | | continue; |
| | | } |
| | | if (endDate != null && (completionDate == null || completionDate.isAfter(endDate))) { |
| | | continue; |
| | | } |
| | | |
| | | Fault fault = faultMap.get(workOrder.getFaultId()); |
| | | if (fault == null) { |
| | | continue; |
| | | } |
| | | CompletionFaultQueryItem.WorkOrderCompletionItem workOrderItem = buildCompletionWorkOrderItem(workOrder, completion); |
| | | if (normalizedKeyword != null && !matchesCompletionKeyword(fault, workOrderItem, normalizedKeyword)) { |
| | | continue; |
| | | } |
| | | |
| | | CompletionFaultQueryItem item = grouped.get(fault.getId()); |
| | | if (item == null) { |
| | | item = new CompletionFaultQueryItem(); |
| | | item.setFaultId(String.valueOf(fault.getId())); |
| | | item.setFaultCode(fault.getFaultCode()); |
| | | item.setXNo(fault.getXNo()); |
| | | item.setEquipmentName(fault.getEquipmentName()); |
| | | item.setParentSystem(fault.getParentSystem()); |
| | | item.setEngineeringMajor(fault.getEngineeringMajor()); |
| | | item.setTeamMajor(fault.getTeamMajor()); |
| | | item.setReportUnit(fault.getReportUnit()); |
| | | item.setReportDate(fault.getReportDate()); |
| | | item.setOwner(fault.getOwner()); |
| | | item.setEngineeringNameDesc(fault.getEngineeringNameDesc()); |
| | | item.setTaskStatus(fault.getTaskStatus()); |
| | | grouped.put(fault.getId(), item); |
| | | } |
| | | item.getWorkOrders().add(workOrderItem); |
| | | } |
| | | |
| | | List<CompletionFaultQueryItem> result = new ArrayList<>(grouped.values()); |
| | | result.forEach(item -> item.getWorkOrders().sort(Comparator.comparing( |
| | | CompletionFaultQueryItem.WorkOrderCompletionItem::getWorkOrderNo, |
| | | Comparator.nullsLast(String::compareTo)))); |
| | | result.sort(Comparator.comparing(CompletionFaultQueryItem::getReportDate, |
| | | Comparator.nullsLast(Comparator.reverseOrder()))); |
| | | return result; |
| | | } |
| | | |
| | | private CompletionFaultQueryItem.WorkOrderCompletionItem buildCompletionWorkOrderItem( |
| | | WorkOrder workOrder, CompletionInfo completion) { |
| | | CompletionFaultQueryItem.WorkOrderCompletionItem item = new CompletionFaultQueryItem.WorkOrderCompletionItem(); |
| | | item.setWorkOrderId(String.valueOf(workOrder.getId())); |
| | | item.setWorkOrderNo(workOrder.getWorkOrderNo()); |
| | | item.setContractorName(workOrder.getContractorName()); |
| | | item.setCompletionStatus(workOrder.getCompletionStatus()); |
| | | item.setEngineeringNo(completion.getEngineeringNo()); |
| | | item.setStartDate(completion.getStartDate()); |
| | | item.setFinishDate(completion.getFinishDate()); |
| | | item.setContractorPerson(completion.getContractorPerson()); |
| | | item.setTInternalConstruction(completion.getTInternalConstruction()); |
| | | item.setRelatedProject(completion.getRelatedProject()); |
| | | item.setDurationDays(completion.getDurationDays()); |
| | | item.setPersonCount(completion.getPersonCount()); |
| | | item.setAcceptanceMode(completion.getAcceptanceMode()); |
| | | item.setQualityInfo(completion.getQualityInfo()); |
| | | item.setReasonAnalysis(completion.getReasonAnalysis()); |
| | | item.setFaultDescription(completion.getFaultDescription()); |
| | | item.setItemName(completion.getItemName()); |
| | | item.setSubmitTime(completion.getSubmitTime()); |
| | | List<Requests.SolutionItem> solutions = parseJsonList(completion.getSolutionsJson(), Requests.SolutionItem.class); |
| | | if (solutions.isEmpty() && StringUtils.hasText(completion.getSolution())) { |
| | | Requests.SolutionItem legacy = new Requests.SolutionItem(); |
| | | legacy.setContent(completion.getSolution()); |
| | | solutions.add(legacy); |
| | | } |
| | | item.setSolutions(solutions); |
| | | item.setMaterials(parseJsonList(completion.getConsumablesJson(), Requests.MaterialItem.class)); |
| | | return item; |
| | | } |
| | | |
| | | private boolean matchesCompletionKeyword(Fault fault, CompletionFaultQueryItem.WorkOrderCompletionItem item, |
| | | String keyword) { |
| | | if (containsIgnoreCase(fault.getFaultCode(), keyword) |
| | | || containsIgnoreCase(fault.getXNo(), keyword) |
| | | || containsIgnoreCase(fault.getEquipmentName(), keyword) |
| | | || containsIgnoreCase(fault.getEngineeringNameDesc(), keyword) |
| | | || containsIgnoreCase(fault.getReportUnit(), keyword) |
| | | || containsIgnoreCase(item.getWorkOrderNo(), keyword) |
| | | || containsIgnoreCase(item.getContractorName(), keyword) |
| | | || containsIgnoreCase(item.getEngineeringNo(), keyword) |
| | | || containsIgnoreCase(item.getItemName(), keyword) |
| | | || containsIgnoreCase(item.getContractorPerson(), keyword)) { |
| | | return true; |
| | | } |
| | | return item.getSolutions().stream().anyMatch(solution -> containsIgnoreCase(solution.getContent(), keyword)) |
| | | || item.getMaterials().stream().anyMatch(material -> containsIgnoreCase(material.getName(), keyword) |
| | | || containsIgnoreCase(material.getSpecification(), keyword)); |
| | | } |
| | | |
| | | private boolean containsIgnoreCase(String value, String keyword) { |
| | | return StringUtils.hasText(value) && value.toLowerCase().contains(keyword); |
| | | } |
| | | |
| | | private <T> List<T> parseJsonList(String json, Class<T> type) { |
| | | if (!StringUtils.hasText(json)) { |
| | | return new ArrayList<>(); |
| | | } |
| | | try { |
| | | return objectMapper.readValue(json, objectMapper.getTypeFactory().constructCollectionType(List.class, type)); |
| | | } catch (Exception exception) { |
| | | return new ArrayList<>(); |
| | | } |
| | | } |
| | | |
| | | public List<WorkOrder> listWorkOrders(String status, String keyword, boolean completionReportable) { |
| | | LambdaQueryWrapper<WorkOrder> query = Wrappers.lambdaQuery(); |
| | | if (completionReportable) { |
| | |
| | | package com.emergencyrepair.common; |
| | | |
| | | import com.emergencyrepair.system.log.ErrorLogRecorder; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.web.bind.annotation.ExceptionHandler; |
| | | import org.springframework.web.bind.annotation.RestControllerAdvice; |
| | | |
| | | import javax.servlet.http.HttpServletRequest; |
| | | |
| | | @RestControllerAdvice |
| | | @RequiredArgsConstructor |
| | | public class GlobalExceptionHandler { |
| | | private final ErrorLogRecorder errorLogRecorder; |
| | | |
| | | @ExceptionHandler(BusinessException.class) |
| | | public Result<Void> handleBusiness(BusinessException exception) { |
| | | return Result.error(exception.getMessage()); |
| | | } |
| | | |
| | | @ExceptionHandler(Exception.class) |
| | | public Result<Void> handleOther(Exception exception) { |
| | | public Result<Void> handleOther(Exception exception, HttpServletRequest request) { |
| | | errorLogRecorder.record(exception, request); |
| | | exception.printStackTrace(); |
| | | return Result.error(exception.getMessage() == null ? "ç³»ç»å¼å¸¸" : exception.getMessage()); |
| | | } |
| | |
| | | import com.emergencyrepair.business.entity.TeamMajor; |
| | | import com.emergencyrepair.common.Result; |
| | | import com.emergencyrepair.system.entity.SysMenu; |
| | | import com.emergencyrepair.system.entity.SysErrorLog; |
| | | import com.emergencyrepair.system.entity.SysLoginLog; |
| | | import com.emergencyrepair.system.entity.SysOperationLog; |
| | | import com.emergencyrepair.system.entity.SysRole; |
| | |
| | | @RequestParam(required = false) String startDate, |
| | | @RequestParam(required = false) String endDate) { |
| | | return Result.ok(systemService.operationLogPage(current, size, keyword, module, operationType, startDate, endDate)); |
| | | } |
| | | |
| | | @GetMapping("/error-logs") |
| | | public Result<Page<SysErrorLog>> errorLogs(@RequestParam(defaultValue = "1") int current, |
| | | @RequestParam(defaultValue = "20") int size, |
| | | @RequestParam(required = false) String keyword, |
| | | @RequestParam(required = false) String module, |
| | | @RequestParam(required = false) String exceptionType, |
| | | @RequestParam(required = false) String startDate, |
| | | @RequestParam(required = false) String endDate) { |
| | | return Result.ok(systemService.errorLogPage(current, size, keyword, module, exceptionType, startDate, endDate)); |
| | | } |
| | | |
| | | @GetMapping("/error-logs/{id}") |
| | | public Result<SysErrorLog> errorLog(@PathVariable Long id) { |
| | | return Result.ok(systemService.errorLog(id)); |
| | | } |
| | | |
| | | @PostMapping("/menu") |
| | |
| | | } |
| | | |
| | | @GetMapping("/menu-permissions") |
| | | public Result<Map<String, Object>> menuPermissions(@RequestParam(required = false) Long roleId, |
| | | @RequestParam(required = false) Long tenantId) { |
| | | return Result.ok(systemService.menuPermissions(roleId, tenantId)); |
| | | public Result<Map<String, Object>> menuPermissions(@RequestParam(required = false) Long roleId) { |
| | | return Result.ok(systemService.menuPermissions(roleId)); |
| | | } |
| | | |
| | | @PostMapping("/menu-permissions") |
| | | public Result<Void> saveMenuPermissions(@RequestBody Requests.MenuPermissionRequest request) { |
| | | systemService.saveMenuPermissions(request.getRoleId(), request.getTenantId(), |
| | | request.getRoleMenuIds(), request.getTenantMenuIds()); |
| | | systemService.saveMenuPermissions(request.getRoleId(), request.getRoleMenuIds()); |
| | | return Result.ok(); |
| | | } |
| | | |
| | | @GetMapping("/tenants") |
| | | public Result<List<Map<String, Object>>> tenants() { |
| | | return Result.ok(systemService.tenants()); |
| | | } |
| | | |
| | | @GetMapping("/three-member") |
| | | public Result<Map<String, Object>> threeMember() { |
| New file |
| | |
| | | package com.emergencyrepair.system.entity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.IdType; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import com.emergencyrepair.common.entity.BaseAuditEntity; |
| | | import com.fasterxml.jackson.databind.annotation.JsonSerialize; |
| | | import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; |
| | | import lombok.Data; |
| | | |
| | | import java.time.LocalDateTime; |
| | | |
| | | @Data |
| | | @TableName("LX.T_SYS_ERROR_LOG") |
| | | public class SysErrorLog extends BaseAuditEntity { |
| | | @TableId(value = "ID", type = IdType.ASSIGN_ID) |
| | | @JsonSerialize(using = ToStringSerializer.class) |
| | | private Long id; |
| | | @JsonSerialize(using = ToStringSerializer.class) |
| | | @TableField("USER_ID") private Long userId; |
| | | @TableField("USER_NAME") private String userName; |
| | | @TableField("NICK_NAME") private String nickName; |
| | | @TableField("MODULE") private String module; |
| | | @TableField("REQUEST_METHOD") private String requestMethod; |
| | | @TableField("REQUEST_URI") private String requestUri; |
| | | @TableField("EXCEPTION_TYPE") private String exceptionType; |
| | | @TableField("MESSAGE") private String message; |
| | | @TableField("STACK_TRACE") private String stackTrace; |
| | | @TableField("ERROR_TIME") private LocalDateTime errorTime; |
| | | } |
| New file |
| | |
| | | package com.emergencyrepair.system.log; |
| | | |
| | | import com.emergencyrepair.common.audit.CurrentUserContext; |
| | | import com.emergencyrepair.system.entity.SysErrorLog; |
| | | import com.emergencyrepair.system.mapper.SysErrorLogMapper; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.jdbc.core.JdbcTemplate; |
| | | import org.springframework.stereotype.Component; |
| | | import org.springframework.util.StringUtils; |
| | | |
| | | import javax.servlet.http.HttpServletRequest; |
| | | import java.io.PrintWriter; |
| | | import java.io.StringWriter; |
| | | import java.time.LocalDateTime; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * Persists unhandled server exceptions so administrators can inspect runtime bugs. |
| | | */ |
| | | @Component |
| | | @RequiredArgsConstructor |
| | | public class ErrorLogRecorder { |
| | | private static final int MAX_MESSAGE_LENGTH = 2000; |
| | | private static final int MAX_STACK_LENGTH = 60000; |
| | | |
| | | private final SysErrorLogMapper errorLogMapper; |
| | | private final JdbcTemplate jdbcTemplate; |
| | | |
| | | public void record(Exception exception, HttpServletRequest request) { |
| | | if (exception == null) { |
| | | return; |
| | | } |
| | | try { |
| | | String username = CurrentUserContext.get(); |
| | | Long userId = resolveUserId(username); |
| | | SysErrorLog log = new SysErrorLog(); |
| | | log.setUserId(userId); |
| | | log.setUserName(username); |
| | | log.setNickName(resolveNickName(userId)); |
| | | log.setModule(resolveModule(request)); |
| | | log.setRequestMethod(request == null ? null : limit(request.getMethod(), 10)); |
| | | log.setRequestUri(request == null ? null : limit(request.getRequestURI(), 500)); |
| | | log.setExceptionType(limit(exception.getClass().getName(), 300)); |
| | | log.setMessage(limit(resolveMessage(exception), MAX_MESSAGE_LENGTH)); |
| | | log.setStackTrace(limit(stackTrace(exception), MAX_STACK_LENGTH)); |
| | | log.setErrorTime(LocalDateTime.now()); |
| | | errorLogMapper.insert(log); |
| | | } catch (Exception ignored) { |
| | | // Error logging must never interfere with the original error response. |
| | | } |
| | | } |
| | | |
| | | private Long resolveUserId(String username) { |
| | | if (!StringUtils.hasText(username)) { |
| | | return null; |
| | | } |
| | | List<Long> ids = jdbcTemplate.query( |
| | | "SELECT USER_ID FROM CSICZB_ZB.SYS_USER WHERE USER_NAME = ? AND (DEL_FLAG = '0' OR DEL_FLAG IS NULL)", |
| | | new Object[]{username}, (rs, rowNum) -> rs.getLong(1)); |
| | | return ids.isEmpty() ? null : ids.get(0); |
| | | } |
| | | |
| | | private String resolveNickName(Long userId) { |
| | | if (userId == null) { |
| | | return null; |
| | | } |
| | | List<String> names = jdbcTemplate.query( |
| | | "SELECT NICK_NAME FROM CSICZB_ZB.SYS_USER WHERE USER_ID = ?", |
| | | new Object[]{userId}, (rs, rowNum) -> rs.getString(1)); |
| | | return names.isEmpty() ? null : names.get(0); |
| | | } |
| | | |
| | | private String resolveMessage(Exception exception) { |
| | | String message = exception.getMessage(); |
| | | return StringUtils.hasText(message) ? message : exception.getClass().getSimpleName(); |
| | | } |
| | | |
| | | private String stackTrace(Exception exception) { |
| | | StringWriter writer = new StringWriter(); |
| | | exception.printStackTrace(new PrintWriter(writer)); |
| | | return writer.toString(); |
| | | } |
| | | |
| | | private String resolveModule(HttpServletRequest request) { |
| | | if (request == null || !StringUtils.hasText(request.getRequestURI())) { |
| | | return "å
¶ä»"; |
| | | } |
| | | String uri = request.getRequestURI(); |
| | | if (uri.startsWith("/api/faults") || uri.startsWith("/api/fault")) return "æ
é管ç"; |
| | | if (uri.startsWith("/api/work-orders") || uri.startsWith("/api/completion")) return "å®å·¥ç®¡ç"; |
| | | if (uri.startsWith("/api/acceptances")) return "å®å·¥éªæ¶"; |
| | | if (uri.startsWith("/api/reports")) return "ç»è®¡æ¥è¡¨"; |
| | | if (uri.startsWith("/api/system/users")) return "ç¨æ·ç®¡ç"; |
| | | if (uri.startsWith("/api/system/roles")) return "è§è²ç®¡ç"; |
| | | if (uri.startsWith("/api/system/menu")) return "èå管ç"; |
| | | if (uri.startsWith("/api/system/contractors")) return "æ¿ä¿®åä½ç®¡ç"; |
| | | if (uri.startsWith("/api/system")) return "ç³»ç»ç®¡ç"; |
| | | return "å
¶ä»"; |
| | | } |
| | | |
| | | private String limit(String value, int maxLength) { |
| | | if (value == null || value.length() <= maxLength) { |
| | | return value; |
| | | } |
| | | return value.substring(0, maxLength); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.emergencyrepair.system.mapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.emergencyrepair.system.entity.SysErrorLog; |
| | | |
| | | public interface SysErrorLogMapper extends BaseMapper<SysErrorLog> { |
| | | } |
| | |
| | | put("/completion/report", |
| | | def("completion:report:save", "\u4fdd\u5b58"), |
| | | def("completion:report:submit", "\u63d0\u4ea4")); |
| | | put("/completion/query", |
| | | def("completion:query:query", "\u67e5\u8be2")); |
| | | put("/acceptance/application", |
| | | def("acceptance:application:query", "\u67e5\u8be2"), |
| | | def("acceptance:application:apply", "\u7533\u8bf7")); |
| | |
| | | put("/system/engineering-majors", def("system:major:add", "\u65b0\u589e"), def("system:major:edit", "\u7f16\u8f91"), def("system:major:delete", "\u5220\u9664")); |
| | | put("/system/team-majors", def("system:major:add", "\u65b0\u589e"), def("system:major:edit", "\u7f16\u8f91"), def("system:major:delete", "\u5220\u9664")); |
| | | put("/system/users", def("system:users:query", "\u67e5\u8be2"), def("system:users:assign", "\u89d2\u8272\u6388\u6743")); |
| | | put("/system/roles", def("system:roles:add", "\u65b0\u589e"), def("system:roles:edit", "\u7f16\u8f91"), def("system:roles:delete", "\u5220\u9664")); |
| | | put("/system/roles", def("system:roles:add", "\u65b0\u589e"), def("system:roles:edit", "\u7f16\u8f91"), def("system:roles:delete", "\u5220\u9664"), def("system:roles:assign", "\u7528\u6237\u8bbe\u7f6e")); |
| | | put("/system/roles", def("system:roles:add", "\u65b0\u589e"), def("system:roles:edit", "\u7f16\u8f91"), def("system:roles:delete", "\u5220\u9664"), def("system:roles:assign", "\u7528\u6237\u8bbe\u7f6e"), def("system:roles:permission", "\u6743\u9650\u8bbe\u7f6e")); |
| | | put("/system/menus", def("system:menus:add", "\u65b0\u589e"), def("system:menus:edit", "\u7f16\u8f91"), def("system:menus:delete", "\u5220\u9664"), def("system:menus:buttons", "\u83dc\u5355\u6309\u94ae\u540c\u6b65")); |
| | | put("/system/menu-permissions", def("system:menu-permissions:save", "\u4fdd\u5b58\u6388\u6743")); |
| | | put("/system/login-logs", def("system:login-logs:query", "\u67e5\u8be2"), def("system:login-logs:export", "\u5bfc\u51fa")); |
| | | put("/system/operation-logs", def("system:operation-logs:query", "\u67e5\u8be2"), def("system:operation-logs:export", "\u5bfc\u51fa")); |
| | | put("/system/error-logs", def("system:error-logs:query", "\u67e5\u8be2"), def("system:error-logs:detail", "\u8be6\u60c5")); |
| | | } |
| | | |
| | | private static void put(String url, ButtonDefinition... definitions) { |
| | |
| | | import com.emergencyrepair.business.mapper.TeamMajorMapper; |
| | | import com.emergencyrepair.common.BusinessException; |
| | | import com.emergencyrepair.system.entity.SysMenu; |
| | | import com.emergencyrepair.system.entity.SysErrorLog; |
| | | import com.emergencyrepair.system.entity.SysLoginLog; |
| | | import com.emergencyrepair.system.entity.SysOperationLog; |
| | | import com.emergencyrepair.system.entity.SysRole; |
| | |
| | | import com.emergencyrepair.system.entity.SysRoleUser; |
| | | import com.emergencyrepair.system.entity.SysTenantMenu; |
| | | import com.emergencyrepair.system.mapper.SysMenuMapper; |
| | | import com.emergencyrepair.system.mapper.SysErrorLogMapper; |
| | | import com.emergencyrepair.system.mapper.SysLoginLogMapper; |
| | | import com.emergencyrepair.system.mapper.SysOperationLogMapper; |
| | | import com.emergencyrepair.system.mapper.SysRoleMapper; |
| | |
| | | private final SysRoleUserMapper roleUserMapper; |
| | | private final SysLoginLogMapper loginLogMapper; |
| | | private final SysOperationLogMapper operationLogMapper; |
| | | private final SysErrorLogMapper errorLogMapper; |
| | | private final ButtonPermissionCatalog buttonPermissionCatalog; |
| | | private final JdbcTemplate jdbcTemplate; |
| | | private final EngineeringMajorMapper engineeringMajorMapper; |
| | |
| | | menu.setMenuType(menu.getId() == null ? 1 : menu.getMenuType()); |
| | | } |
| | | boolean isRoot = menu.getPid() == null || menu.getPid() == 0L; |
| | | if (menu.getMenuType() == 2) { |
| | | if (isRoot) { |
| | | throw new BusinessException("æé®ç±»ååªè½å»ºå¨èåç±»åä¸çº§"); |
| | | if (menu.getId() != null && !isRoot && menu.getId().equals(menu.getPid())) { |
| | | throw new BusinessException("ä¸çº§èåä¸è½éæ©å½åèå"); |
| | | } |
| | | SysMenu parent = null; |
| | | if (!isRoot) { |
| | | parent = menuMapper.selectById(menu.getPid()); |
| | | if (parent == null) { |
| | | throw new BusinessException("ä¸çº§èåä¸åå¨"); |
| | | } |
| | | SysMenu parent = menuMapper.selectById(menu.getPid()); |
| | | if (menu.getId() != null && isMenuDescendant(menu.getPid(), menu.getId())) { |
| | | throw new BusinessException("ä¸çº§èåä¸è½éæ©å½åèåçä¸çº§"); |
| | | } |
| | | } |
| | | if (menu.getMenuType() == 2) { |
| | | if (parent == null || parent.getMenuType() == null || parent.getMenuType() != 1) { |
| | | throw new BusinessException("æé®ç±»ååªè½å»ºå¨èåç±»åä¸çº§"); |
| | | } |
| | |
| | | } |
| | | menu.setUrl(null); |
| | | menu.setIcon(null); |
| | | } else if (!isRoot) { |
| | | SysMenu parent = menuMapper.selectById(menu.getPid()); |
| | | if (parent != null && parent.getMenuType() != null && parent.getMenuType() == 2) { |
| | | throw new BusinessException("æé®ç±»åä¸çº§ä¸è½å建èå"); |
| | | } |
| | | } else if (parent != null && parent.getMenuType() != null && parent.getMenuType() == 2) { |
| | | throw new BusinessException("æé®ç±»åä¸çº§ä¸è½å建èå"); |
| | | } |
| | | LocalDateTime now = LocalDateTime.now(); |
| | | menu.setUpdateTime(now); |
| | |
| | | menuMapper.updateById(menu); |
| | | } |
| | | return menuMapper.selectById(menu.getId()); |
| | | } |
| | | |
| | | private boolean isMenuDescendant(Long candidateId, Long ancestorId) { |
| | | Long cursor = candidateId; |
| | | int guard = 0; |
| | | while (cursor != null && cursor != 0L && guard++ < 64) { |
| | | if (cursor.equals(ancestorId)) { |
| | | return true; |
| | | } |
| | | SysMenu current = menuMapper.selectById(cursor); |
| | | cursor = current == null ? null : current.getPid(); |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | @Transactional |
| | |
| | | if (!StringUtils.hasText(xNo)) { |
| | | return productTreeRoots(); |
| | | } |
| | | List<ProductNode> rootNodes = productNodeMapper.selectList(Wrappers.<ProductNode>lambdaQuery() |
| | | .and(wrapper -> wrapper.isNull(ProductNode::getParentId).or().eq(ProductNode::getParentId, 0L)) |
| | | .orderByAsc(ProductNode::getSortNo).orderByAsc(ProductNode::getId)); |
| | | List<ProductNode> sideNodes = productNodeMapper.selectList(Wrappers.<ProductNode>lambdaQuery() |
| | | .in(ProductNode::getLayer, Arrays.asList("side", "SIDE")) |
| | | .eq(ProductNode::getName, xNo.trim()) |
| | | .orderByAsc(ProductNode::getSortNo).orderByAsc(ProductNode::getId)); |
| | | Map<Long, ProductNode> roots = uniqueProductNodes(rootNodes); |
| | | Map<Long, ProductNode> sides = uniqueProductNodes(sideNodes); |
| | | Set<Long> branchIds = new HashSet<>(); |
| | | branchIds.addAll(roots.keySet()); |
| | | branchIds.addAll(sides.keySet()); |
| | | Map<Long, Map<String, Object>> rootRows = new LinkedHashMap<>(); |
| | | for (ProductNode root : sortProductNodes(roots.values())) { |
| | | rootRows.put(root.getId(), mapProduct(root, branchIds)); |
| | | } |
| | | for (ProductNode side : sortProductNodes(sides.values())) { |
| | | ProductNode root = findProductRoot(side); |
| | | Map<String, Object> rootRow = root == null ? null : rootRows.get(root.getId()); |
| | | if (rootRow != null) { |
| | | children(rootRow).add(mapProduct(side, branchIds)); |
| | | } |
| | | } |
| | | return new ArrayList<>(rootRows.values()); |
| | | return mapProductTreeNodes(sideNodes, Collections.emptySet()); |
| | | } |
| | | |
| | | public List<String> productXNumbers() { |
| | |
| | | baseEquipmentMapper.deleteById(id); |
| | | } |
| | | |
| | | public List<Map<String, Object>> tenants() { |
| | | return Collections.singletonList(row("id", 1L, "name", "临æ¢ä¿®ç§æ·")); |
| | | } |
| | | |
| | | public Map<String, Object> menuPermissions(Long roleId, Long tenantId) { |
| | | List<Long> roleMenuIds = roleMenuMapper.selectList(Wrappers.<SysRoleMenu>lambdaQuery() |
| | | .eq(roleId != null, SysRoleMenu::getRoleId, roleId)) |
| | | .stream().map(SysRoleMenu::getMenuId).collect(Collectors.toList()); |
| | | List<Long> tenantMenuIds = tenantMenuMapper.selectList(Wrappers.<SysTenantMenu>lambdaQuery() |
| | | .eq(tenantId != null, SysTenantMenu::getTenantId, tenantId)) |
| | | .stream().map(SysTenantMenu::getMenuId).collect(Collectors.toList()); |
| | | List<Long> effective = roleMenuIds.stream().filter(tenantMenuIds::contains).collect(Collectors.toList()); |
| | | public Map<String, Object> menuPermissions(Long roleId) { |
| | | if (roleId == null) { |
| | | throw new BusinessException("è¯·éæ©è§è²"); |
| | | } |
| | | List<String> roleMenuIds = roleMenuMapper.selectList(Wrappers.<SysRoleMenu>lambdaQuery() |
| | | .eq(SysRoleMenu::getRoleId, roleId)) |
| | | .stream().map(SysRoleMenu::getMenuId).map(String::valueOf).collect(Collectors.toList()); |
| | | Map<String, Object> result = new LinkedHashMap<>(); |
| | | result.put("roleMenuIds", roleMenuIds); |
| | | result.put("tenantMenuIds", tenantMenuIds); |
| | | result.put("effectiveMenuIds", effective); |
| | | result.put("effectiveMenuIds", roleMenuIds); |
| | | return result; |
| | | } |
| | | |
| | | @Transactional |
| | | public void saveMenuPermissions(Long roleId, Long tenantId, List<Long> roleMenuIds, List<Long> tenantMenuIds) { |
| | | if (roleId == null || tenantId == null) { |
| | | throw new BusinessException("è¯·éæ©è§è²åç§æ·"); |
| | | public void saveMenuPermissions(Long roleId, List<Long> roleMenuIds) { |
| | | if (roleId == null) { |
| | | throw new BusinessException("è¯·éæ©è§è²"); |
| | | } |
| | | roleMenuMapper.delete(Wrappers.<SysRoleMenu>lambdaQuery().eq(SysRoleMenu::getRoleId, roleId)); |
| | | tenantMenuMapper.delete(Wrappers.<SysTenantMenu>lambdaQuery().eq(SysTenantMenu::getTenantId, tenantId)); |
| | | List<Long> effectiveRoleMenuIds = expandWithAncestors(roleMenuIds); |
| | | List<Long> effectiveTenantMenuIds = expandWithAncestors(tenantMenuIds); |
| | | for (Long menuId : effectiveRoleMenuIds) { |
| | | SysRoleMenu relation = new SysRoleMenu(); |
| | | relation.setRoleId(roleId); |
| | | relation.setMenuId(menuId); |
| | | relation.setCreateTime(LocalDateTime.now()); |
| | | roleMenuMapper.insert(relation); |
| | | } |
| | | for (Long menuId : effectiveTenantMenuIds) { |
| | | SysTenantMenu relation = new SysTenantMenu(); |
| | | relation.setTenantId(tenantId); |
| | | relation.setMenuId(menuId); |
| | | relation.setCreateTime(LocalDateTime.now()); |
| | | tenantMenuMapper.insert(relation); |
| | | } |
| | | } |
| | | |
| | |
| | | Set<Long> roleMenuIds = new LinkedHashSet<>(roleMenuMapper.selectList(Wrappers.<SysRoleMenu>lambdaQuery() |
| | | .in(SysRoleMenu::getRoleId, roleIds)) |
| | | .stream().map(SysRoleMenu::getMenuId).collect(Collectors.toList())); |
| | | Set<Long> tenantMenuIds = new LinkedHashSet<>(tenantMenuMapper.selectList(Wrappers.<SysTenantMenu>lambdaQuery() |
| | | .eq(SysTenantMenu::getTenantId, 1L)) |
| | | .stream().map(SysTenantMenu::getMenuId).collect(Collectors.toList())); |
| | | if (tenantMenuIds.isEmpty()) { |
| | | result.addAll(roleMenuIds); |
| | | } else { |
| | | for (Long menuId : roleMenuIds) { |
| | | if (tenantMenuIds.contains(menuId)) { |
| | | result.add(menuId); |
| | | } |
| | | } |
| | | } |
| | | result.addAll(roleMenuIds); |
| | | return result; |
| | | } |
| | | |
| | |
| | | } |
| | | } |
| | | final Set<Long> allowedIds = allowed; |
| | | Set<Long> visibleIds = new LinkedHashSet<>(expandWithAncestors(new ArrayList<>(allowed))); |
| | | List<SysMenu> navigable = listMenus().stream() |
| | | .filter(menu -> menu.getMenuType() == null || menu.getMenuType() != 2) |
| | | .filter(menu -> allowedIds.contains(menu.getId())) |
| | | .filter(menu -> visibleIds.contains(menu.getId())) |
| | | .collect(Collectors.toList()); |
| | | return buildTree(navigable); |
| | | } |
| | |
| | | return operationLogMapper.selectPage(page, wrapper); |
| | | } |
| | | |
| | | public Page<SysErrorLog> errorLogPage(int current, int size, String keyword, String module, String exceptionType, String startDate, String endDate) { |
| | | Page<SysErrorLog> page = new Page<>(current <= 0 ? 1 : current, size <= 0 ? 20 : size); |
| | | LambdaQueryWrapper<SysErrorLog> wrapper = Wrappers.lambdaQuery(); |
| | | wrapper.select(SysErrorLog::getId, SysErrorLog::getUserId, SysErrorLog::getUserName, |
| | | SysErrorLog::getNickName, SysErrorLog::getModule, SysErrorLog::getRequestMethod, |
| | | SysErrorLog::getRequestUri, SysErrorLog::getExceptionType, SysErrorLog::getMessage, |
| | | SysErrorLog::getErrorTime); |
| | | if (StringUtils.hasText(keyword)) { |
| | | String value = keyword.trim(); |
| | | wrapper.and(item -> item.like(SysErrorLog::getUserName, value) |
| | | .or().like(SysErrorLog::getNickName, value) |
| | | .or().like(SysErrorLog::getRequestUri, value) |
| | | .or().like(SysErrorLog::getExceptionType, value) |
| | | .or().like(SysErrorLog::getMessage, value)); |
| | | } |
| | | if (StringUtils.hasText(module)) { |
| | | wrapper.eq(SysErrorLog::getModule, module.trim()); |
| | | } |
| | | if (StringUtils.hasText(exceptionType)) { |
| | | wrapper.like(SysErrorLog::getExceptionType, exceptionType.trim()); |
| | | } |
| | | LocalDateTime start = parseStart(startDate); |
| | | LocalDateTime end = parseEnd(endDate); |
| | | if (start != null) { |
| | | wrapper.ge(SysErrorLog::getErrorTime, start); |
| | | } |
| | | if (end != null) { |
| | | wrapper.le(SysErrorLog::getErrorTime, end); |
| | | } |
| | | wrapper.orderByDesc(SysErrorLog::getErrorTime); |
| | | return errorLogMapper.selectPage(page, wrapper); |
| | | } |
| | | |
| | | public SysErrorLog errorLog(Long id) { |
| | | SysErrorLog log = errorLogMapper.selectById(id); |
| | | if (log == null) { |
| | | throw new BusinessException("é误æ¥å¿ä¸åå¨"); |
| | | } |
| | | return log; |
| | | } |
| | | |
| | | public Long resolveUserId(String username) { |
| | | if (!StringUtils.hasText(username)) { |
| | | return null; |
| | |
| | | <head> |
| | | <meta charset="utf-8"> |
| | | <meta name="viewport" content="width=device-width,initial-scale=1"> |
| | | <title>临æ¢ä¿®ç®¡çç³»ç»</title> |
| | | <title>rk_SystemName</title> |
| | | </head> |
| | | <body> |
| | | <noscript>请å¯ç¨ JavaScript å使ç¨ä¸´æ¢ä¿®ç®¡çç³»ç»ã</noscript> |
| | |
| | | { |
| | | "sensitive_words": [ |
| | | { |
| | | "codename": "rk_SystemName", |
| | | "real_name": "临æ¢ä¿®ç®¡çç³»ç»", |
| | | "scope": "ui", |
| | | "note": "System title shown in the login page and application header." |
| | | }, |
| | | { |
| | | "codename": "rk_SystemVer", |
| | | "real_name": "ç½ç»ç", |
| | | "scope": "ui", |
| | | "note": "System edition shown in red in the application header." |
| | | }, |
| | | { |
| | | "codename": "rk_Xå·", |
| | | "real_name": "å¿å·", |
| | | "scope": "ui", |
| | |
| | | <header class="app-header"> |
| | | <div class="brand"> |
| | | <repair-logo></repair-logo> |
| | | <span>临æ¢ä¿®ç®¡çç³»ç»</span> |
| | | <em>ç½ç»ç</em> |
| | | <span>rk_SystemName</span> |
| | | <em class="system-version">rk_SystemVer</em> |
| | | <span v-if="menuPathText" class="menu-path" :title="menuPathText">{{ menuPathText }}</span> |
| | | </div> |
| | | <div class="header-right"> |
| | |
| | | .brand { display: flex; flex: 1; align-items: center; min-width: 0; gap: 9px; font-size: 18px; font-weight: 600; } |
| | | .brand .repair-logo { color: #7fc4ff; font-size: 25px; transform: translateY(2px); } |
| | | .brand em { padding: 1px 6px; border: 1px solid rgba(255,255,255,.35); border-radius: 3px; font-size: 11px; font-style: normal; font-weight: 400; opacity: .85; } |
| | | .brand .system-version { border-color: rgba(255, 107, 107, .65); color: #ff6b6b; opacity: 1; } |
| | | .brand > .repair-logo, .brand > span:not(.menu-path), .brand > em { flex: 0 0 auto; } |
| | | .menu-path { flex: 0 1 auto; min-width: 0; padding-left: 14px; border-left: 1px solid rgba(255,255,255,.28); overflow: hidden; color: #d8e9f7; font-size: 13px; font-weight: 400; text-overflow: ellipsis; white-space: nowrap; } |
| | | .header-right { display: flex; flex: 0 0 auto; align-items: center; gap: 14px; font-size: 14px; } |
| | |
| | | return map |
| | | }, {}) |
| | | this.keyword = '' |
| | | this.$nextTick(() => { this.restoreChecked(); this.syncSelected() }) |
| | | this.$nextTick(() => { |
| | | this.expandLockedRoots(lockedXNo) |
| | | this.restoreChecked() |
| | | this.syncSelected() |
| | | }) |
| | | }).finally(() => { this.loading = false }) |
| | | }, |
| | | expandLockedRoots(lockedXNo) { |
| | | if (!lockedXNo || !this.$refs.tree) return |
| | | this.tree.forEach(node => { |
| | | const treeNode = this.$refs.tree.getNode(node.id) |
| | | if (treeNode) treeNode.expand() |
| | | }) |
| | | }, |
| | | loadNode(node, resolve) { |
| | | if (!node || !node.data || !node.data.id) { |
| | | resolve([]) |
| | |
| | | <template> |
| | | <img class="repair-logo" :src="logoSrc" alt="临æ¢ä¿®ç®¡çç³»ç»"> |
| | | <img class="repair-logo" :src="logoSrc" alt="rk_SystemName"> |
| | | </template> |
| | | |
| | | <script> |
| | |
| | | if (path === '/fault/report') return 'el-icon-edit-outline' |
| | | if (path.includes('/approval/') || path === '/fault/review') return 'el-icon-s-check' |
| | | if (path === '/fault/dispatch') return 'el-icon-s-promotion' |
| | | if (path.startsWith('/fault/completion') || path === '/completion/report') return 'el-icon-finished' |
| | | if (path.startsWith('/fault/completion') || path.startsWith('/completion/')) return 'el-icon-finished' |
| | | if (path.startsWith('/acceptance/')) return 'el-icon-circle-check' |
| | | if (path.startsWith('/reports/')) return 'el-icon-data-analysis' |
| | | if (path.startsWith('/system/')) return 'el-icon-setting' |
| | |
| | | Vue.use(ElementUI, { size: 'small' }) |
| | | Vue.config.productionTip = false |
| | | |
| | | Vue.prototype.$appName = '临æ¢ä¿®ç®¡çç³»ç»' |
| | | Vue.prototype.$appName = 'rk_SystemName' |
| | | |
| | | Vue.prototype.$hasPermission = function(code) { |
| | | const raw = sessionStorage.getItem('permissions') |
| | |
| | | { path: 'fault/dispatch', component: () => import('@/views/fault/DispatchPage.vue'), meta: { title: 'rk_XCBå©çæ´¾å', cacheName: 'DispatchPage' } }, |
| | | { path: 'fault/completion', component: () => import('@/views/completion/CompletionList.vue'), meta: { title: 'å®å·¥æ
åµå¤ç', cacheName: 'CompletionList' } }, |
| | | { path: 'completion/report', component: () => import('@/views/completion/CompletionReport.vue'), props: route => ({ workOrderId: route.query.workOrderId || null }), meta: { title: 'å®å·¥ä¿¡æ¯ä¸æ¥', cacheName: 'CompletionReport' } }, |
| | | { path: 'completion/query', component: () => import('@/views/completion/CompletionQuery.vue'), meta: { title: 'å®å·¥ä¿¡æ¯æ¥é
', cacheName: 'CompletionQuery' } }, |
| | | { path: 'acceptance/application', component: () => import('@/views/acceptance/AcceptanceList.vue'), props: { stage: 'APPLICATION' }, meta: { title: 'éªæ¶ç³è¯·', stage: 'APPLICATION', cacheName: 'AcceptanceList' } }, |
| | | { path: 'acceptance/inspector', component: () => import('@/views/acceptance/AcceptanceList.vue'), props: { stage: 'INSPECTOR' }, meta: { title: 'è´¨æ£åéªæ¶', stage: 'INSPECTOR', cacheName: 'AcceptanceList' } }, |
| | | { path: 'acceptance/leader', component: () => import('@/views/acceptance/AcceptanceList.vue'), props: { stage: 'LEADER' }, meta: { title: 'è´¨æ£è´è´£äººéªæ¶', stage: 'LEADER', cacheName: 'AcceptanceList' } }, |
| | |
| | | { path: 'system/roles', component: () => import('@/views/system/RoleManage.vue'), meta: { title: 'è§è²ç®¡ç', cacheName: 'RoleManage' } }, |
| | | { path: 'system/login-logs', component: () => import('@/views/system/LoginLogManage.vue'), meta: { title: 'ç»å½æ¥å¿', cacheName: 'LoginLogManage' } }, |
| | | { path: 'system/operation-logs', component: () => import('@/views/system/OperationLogManage.vue'), meta: { title: 'æä½æ¥å¿', cacheName: 'OperationLogManage' } }, |
| | | { path: 'system/error-logs', component: () => import('@/views/system/ErrorLogManage.vue'), meta: { title: 'é误æ¥å¿', cacheName: 'ErrorLogManage' } }, |
| | | { path: 'system/menus', component: () => import('@/views/system/MenuManage.vue'), meta: { title: 'èå管ç', cacheName: 'MenuManage' } }, |
| | | { path: 'system/menu-permissions', component: () => import('@/views/system/MenuPermission.vue'), meta: { title: 'èåæé', cacheName: 'MenuPermission' } }, |
| | | { path: 'system/three-member', component: () => import('@/views/system/ThreeMember.vue'), meta: { title: 'ä¸å管ç', cacheName: 'ThreeMember' } } |
| | | ] |
| | | } |
| | |
| | | }) |
| | | |
| | | replaceDom(document.body) |
| | | document.title = replaceSensitiveText(document.title) |
| | | watchDom() |
| | | } catch (error) { |
| | | // The application still works with development codenames if the dictionary is unavailable. |
| | |
| | | <div class="login-brand"> |
| | | <repair-logo></repair-logo> |
| | | <div> |
| | | <h1>临æ¢ä¿®ç®¡çç³»ç»</h1> |
| | | <h1>{{ $appName }}</h1> |
| | | </div> |
| | | </div> |
| | | <el-form ref="form" :model="form" :rules="rules" label-position="top" @keyup.enter.native="submit"> |
| New file |
| | |
| | | <template> |
| | | <div class="page"> |
| | | <div class="page-title"> |
| | | <div> |
| | | <h2>å®å·¥ä¿¡æ¯æ¥é
</h2> |
| | | <p>ææ
éæ±æ»ï¼å䏿
éçå¤å¼ 派工åå
±ç¨æ
éä¿¡æ¯ï¼å®å·¥å¤çæ
åµåæ´æ¢å¨ææå¤æ¡æç»å±ç¤º</p> |
| | | </div> |
| | | <el-tag type="info">å
± {{ rows.length }} 个æ
é</el-tag> |
| | | </div> |
| | | |
| | | <div class="toolbar"> |
| | | <el-input v-model="filters.keyword" placeholder="æ
éç¼å· / rk_Xå· / 派工åç¼å· / å·¥ç¨åç§° / æ¿ä¿®åä½" clearable style="width:380px" @keyup.enter.native="load"></el-input> |
| | | <el-input v-model="filters.contractorName" placeholder="æ¿ä¿®åä½" clearable style="width:190px" @keyup.enter.native="load"></el-input> |
| | | <el-select v-model="filters.completionStatus" placeholder="å®å·¥ç¶æ" clearable style="width:140px"> |
| | | <el-option v-for="(meta, key) in completionStatuses" :key="key" :label="meta.text" :value="key"></el-option> |
| | | </el-select> |
| | | <el-date-picker v-model="filters.completionDate" type="daterange" value-format="yyyy-MM-dd" range-separator="è³" start-placeholder="å®å·¥å¼å§æ¥æ" end-placeholder="å®å·¥ç»ææ¥æ" style="width:270px"></el-date-picker> |
| | | <el-button type="primary" icon="el-icon-search" v-permission="'completion:query:query'" @click="load">æ¥è¯¢</el-button> |
| | | <el-button icon="el-icon-refresh" @click="reset">éç½®</el-button> |
| | | </div> |
| | | |
| | | <div class="table-panel"> |
| | | <el-table v-loading="loading" :data="displayRows" border size="small" :span-method="spanMethod" row-key="rowKey"> |
| | | <el-table-column label="åºå·" width="62" align="center"> |
| | | <template slot-scope="{ row }">{{ row.faultIndex }}</template> |
| | | </el-table-column> |
| | | <el-table-column prop="faultCode" label="æ
éç¼å·" width="150"></el-table-column> |
| | | <el-table-column prop="xNo" label="rk_Xå·" width="72"></el-table-column> |
| | | <el-table-column prop="equipmentName" label="设å¤åç§°" width="130" show-overflow-tooltip></el-table-column> |
| | | <el-table-column prop="engineeringNameDesc" label="å·¥ç¨åç§°åæåæ
åµ" min-width="190" show-overflow-tooltip></el-table-column> |
| | | <el-table-column prop="reportUnit" label="䏿¥åä½" width="130" show-overflow-tooltip></el-table-column> |
| | | <el-table-column prop="reportDate" label="䏿¥æ¥æ" width="105"></el-table-column> |
| | | |
| | | <el-table-column prop="workOrderNo" label="派工åç¼å·" width="155"></el-table-column> |
| | | <el-table-column prop="contractorName" label="æ¿ä¿®åä½" width="150" show-overflow-tooltip></el-table-column> |
| | | <el-table-column label="å®å·¥ç¶æ" width="100"> |
| | | <template slot-scope="{ row }"> |
| | | <el-tag size="mini" :type="completionMeta(row.completionStatus).type">{{ completionMeta(row.completionStatus).text }}</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="engineeringNo" label="å·¥ç¨ç¼å·" width="145"></el-table-column> |
| | | <el-table-column label="å¼å·¥ / å®å·¥" width="190"> |
| | | <template slot-scope="{ row }">{{ formatPeriod(row.startDate, row.finishDate) }}</template> |
| | | </el-table-column> |
| | | <el-table-column prop="contractorPerson" label="æ¿ä¿®äºº" width="100"></el-table-column> |
| | | <el-table-column label="å®å·¥å¤çæ
åµ" min-width="260"> |
| | | <template slot-scope="{ row }"> |
| | | <div v-if="row.solutions && row.solutions.length" class="detail-list"> |
| | | <div v-for="(item, index) in row.solutions" :key="index" class="detail-line"> |
| | | <span class="detail-index">{{ index + 1 }}</span> |
| | | <span>{{ item.content || '-' }}</span> |
| | | </div> |
| | | </div> |
| | | <span v-else class="muted">-</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="æ´æ¢å¨æ" min-width="300"> |
| | | <template slot-scope="{ row }"> |
| | | <div v-if="row.materials && row.materials.length" class="detail-list"> |
| | | <div v-for="(item, index) in row.materials" :key="index" class="material-line"> |
| | | <span class="detail-index">{{ index + 1 }}</span> |
| | | <div> |
| | | <div><strong>{{ item.name || '-' }}</strong><span v-if="item.specification"> / {{ item.specification }}</span><span v-if="item.quantity != null"> Ã {{ item.quantity }}{{ item.unit || '' }}</span></div> |
| | | <div class="material-meta">{{ materialMeta(item) }}</div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <span v-else class="muted">-</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="qualityInfo" label="ä¿®åè´¨éæ
åµ" min-width="180" show-overflow-tooltip></el-table-column> |
| | | <el-table-column prop="reasonAnalysis" label="åå åæ" min-width="180" show-overflow-tooltip></el-table-column> |
| | | <el-table-column prop="submitTime" label="æäº¤æ¶é´" width="160"></el-table-column> |
| | | </el-table> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import api from '@/api' |
| | | import { completionStatuses, statusMeta } from '@/utils/dict' |
| | | |
| | | export default { |
| | | name: 'CompletionQuery', |
| | | data() { |
| | | return { |
| | | loading: false, |
| | | completionStatuses, |
| | | rows: [], |
| | | filters: { |
| | | keyword: '', |
| | | contractorName: '', |
| | | completionStatus: '', |
| | | completionDate: [] |
| | | } |
| | | } |
| | | }, |
| | | computed: { |
| | | displayRows() { |
| | | const rows = [] |
| | | this.rows.forEach((fault, faultIndex) => { |
| | | const workOrders = fault.workOrders || [] |
| | | const total = workOrders.length || 1 |
| | | if (!workOrders.length) { |
| | | rows.push({ ...fault, faultIndex: faultIndex + 1, faultRowspan: 1, rowKey: fault.faultId + '-empty' }) |
| | | return |
| | | } |
| | | workOrders.forEach((workOrder, index) => { |
| | | rows.push({ |
| | | ...fault, |
| | | ...workOrder, |
| | | faultIndex: faultIndex + 1, |
| | | faultRowspan: index === 0 ? total : 0, |
| | | rowKey: fault.faultId + '-' + workOrder.workOrderId |
| | | }) |
| | | }) |
| | | }) |
| | | return rows |
| | | } |
| | | }, |
| | | created() { |
| | | this.load() |
| | | }, |
| | | methods: { |
| | | load() { |
| | | this.loading = true |
| | | const range = this.filters.completionDate || [] |
| | | api.get('/work-orders/completion/query', { |
| | | params: { |
| | | keyword: this.filters.keyword || undefined, |
| | | contractorName: this.filters.contractorName || undefined, |
| | | completionStatus: this.filters.completionStatus || undefined, |
| | | startDate: range[0] || undefined, |
| | | endDate: range[1] || undefined |
| | | } |
| | | }).then(res => { |
| | | this.rows = res.data || [] |
| | | }).finally(() => { |
| | | this.loading = false |
| | | }) |
| | | }, |
| | | reset() { |
| | | this.filters = { keyword: '', contractorName: '', completionStatus: '', completionDate: [] } |
| | | this.load() |
| | | }, |
| | | completionMeta(value) { |
| | | return statusMeta(completionStatuses, value) |
| | | }, |
| | | spanMethod({ row, columnIndex }) { |
| | | if (columnIndex <= 6) { |
| | | return row.faultRowspan === 0 ? { rowspan: 0, colspan: 0 } : { rowspan: row.faultRowspan, colspan: 1 } |
| | | } |
| | | return { rowspan: 1, colspan: 1 } |
| | | }, |
| | | formatPeriod(startDate, finishDate) { |
| | | return [startDate || '-', finishDate || '-'].join(' è³ ') |
| | | }, |
| | | materialMeta(item) { |
| | | const parts = [] |
| | | if (item.technicalParameter) parts.push(item.technicalParameter) |
| | | if (item.source) parts.push('æ¥æºï¼' + item.source) |
| | | if (item.outboundNo) parts.push('åºåºåï¼' + item.outboundNo) |
| | | if (item.certificate) parts.push('åæ ¼è¯æï¼' + item.certificate) |
| | | if (item.remark) parts.push('夿³¨ï¼' + item.remark) |
| | | return parts.join('ï¼') || '-' |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .table-panel { |
| | | overflow-x: auto; |
| | | } |
| | | .detail-list { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 5px; |
| | | } |
| | | .detail-line, |
| | | .material-line { |
| | | display: flex; |
| | | align-items: flex-start; |
| | | gap: 6px; |
| | | line-height: 20px; |
| | | } |
| | | .material-line > div { |
| | | min-width: 0; |
| | | } |
| | | .detail-index { |
| | | flex: 0 0 auto; |
| | | min-width: 16px; |
| | | height: 16px; |
| | | margin-top: 2px; |
| | | border-radius: 50%; |
| | | color: #fff; |
| | | background: #7b8fa6; |
| | | font-size: 11px; |
| | | line-height: 16px; |
| | | text-align: center; |
| | | } |
| | | .material-meta { |
| | | color: #7b8a9a; |
| | | font-size: 12px; |
| | | } |
| | | .muted { |
| | | color: #a3afbc; |
| | | } |
| | | </style> |
| New file |
| | |
| | | <template> |
| | | <div class="page"> |
| | | <div class="page-title"> |
| | | <div><h2>é误æ¥å¿</h2><p>è®°å½ç¨åºè¿è¡ä¸çæªå¤çå¼å¸¸ï¼ä¾¿äºå®ä½åä¿®å¤ç¼ºé·</p></div> |
| | | </div> |
| | | <div class="table-panel"> |
| | | <div class="toolbar"> |
| | | <el-input v-model="filters.keyword" clearable placeholder="è´¦æ·å / å§å / 请æ±å°å / å¼å¸¸ä¿¡æ¯" style="width:280px" @keyup.enter.native="search"></el-input> |
| | | <el-select v-model="filters.module" clearable placeholder="模å" style="width:150px"> |
| | | <el-option v-for="item in modules" :key="item" :label="item" :value="item"></el-option> |
| | | </el-select> |
| | | <el-input v-model="filters.exceptionType" clearable placeholder="å¼å¸¸ç±»å" style="width:220px" @keyup.enter.native="search"></el-input> |
| | | <el-date-picker |
| | | v-model="filters.dates" |
| | | type="daterange" |
| | | value-format="yyyy-MM-dd" |
| | | range-separator="è³" |
| | | start-placeholder="å¼å§æ¥æ" |
| | | end-placeholder="ç»ææ¥æ" |
| | | style="width:260px" |
| | | ></el-date-picker> |
| | | <el-button type="primary" icon="el-icon-search" v-permission="'system:error-logs:query'" @click="search">æ¥è¯¢</el-button> |
| | | <el-button icon="el-icon-refresh-left" @click="reset">éç½®</el-button> |
| | | </div> |
| | | <el-table v-loading="loading" :data="rows" border stripe height="calc(100vh - 250px)"> |
| | | <el-table-column prop="errorTime" label="åçæ¶é´" width="180"></el-table-column> |
| | | <el-table-column prop="userName" label="è´¦æ·å" width="120"></el-table-column> |
| | | <el-table-column prop="nickName" label="å§å" width="110"></el-table-column> |
| | | <el-table-column prop="module" label="模å" width="120"></el-table-column> |
| | | <el-table-column prop="requestMethod" label="æ¹å¼" width="75" align="center"></el-table-column> |
| | | <el-table-column prop="requestUri" label="请æ±å°å" min-width="230" show-overflow-tooltip></el-table-column> |
| | | <el-table-column prop="exceptionType" label="å¼å¸¸ç±»å" min-width="220" show-overflow-tooltip></el-table-column> |
| | | <el-table-column prop="message" label="å¼å¸¸ä¿¡æ¯" min-width="280" show-overflow-tooltip></el-table-column> |
| | | <el-table-column label="æä½" width="80" fixed="right"> |
| | | <template slot-scope="{ row }"> |
| | | <el-button type="text" v-permission="'system:error-logs:detail'" @click="openDetail(row)">详æ
</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | <el-pagination |
| | | class="pager" |
| | | background |
| | | layout="total, sizes, prev, pager, next" |
| | | :total="total" |
| | | :current-page="current" |
| | | :page-size="size" |
| | | :page-sizes="[20, 50, 100]" |
| | | @current-change="handleCurrentChange" |
| | | @size-change="handleSizeChange" |
| | | ></el-pagination> |
| | | </div> |
| | | |
| | | <el-dialog title="é误详æ
" :visible.sync="detailVisible" width="900px" append-to-body> |
| | | <div v-loading="detailLoading"> |
| | | <div class="detail-meta"> |
| | | <span><b>åçæ¶é´ï¼</b>{{ detail.errorTime }}</span> |
| | | <span><b>æä½äººï¼</b>{{ detail.userName || '-' }}{{ detail.nickName ? ' / ' + detail.nickName : '' }}</span> |
| | | <span><b>模åï¼</b>{{ detail.module || '-' }}</span> |
| | | </div> |
| | | <div class="detail-meta"> |
| | | <span><b>请æ±ï¼</b>{{ (detail.requestMethod || '') + ' ' + (detail.requestUri || '') }}</span> |
| | | </div> |
| | | <div class="detail-meta"> |
| | | <span><b>å¼å¸¸ç±»åï¼</b>{{ detail.exceptionType }}</span> |
| | | </div> |
| | | <div class="detail-meta"> |
| | | <span><b>å¼å¸¸ä¿¡æ¯ï¼</b>{{ detail.message }}</span> |
| | | </div> |
| | | <pre class="stack-trace">{{ detail.stackTrace || 'æ å æ ä¿¡æ¯' }}</pre> |
| | | </div> |
| | | <template slot="footer"><el-button @click="detailVisible = false">å
³é</el-button></template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import api from '@/api' |
| | | export default { |
| | | name: 'ErrorLogManage', |
| | | data() { |
| | | return { |
| | | rows: [], |
| | | total: 0, |
| | | current: 1, |
| | | size: 20, |
| | | loading: false, |
| | | detailVisible: false, |
| | | detailLoading: false, |
| | | detail: {}, |
| | | modules: ['æ
é管ç', 'å®å·¥ç®¡ç', 'å®å·¥éªæ¶', 'ç»è®¡æ¥è¡¨', 'ç¨æ·ç®¡ç', 'è§è²ç®¡ç', 'èå管ç', 'æ¿ä¿®åä½ç®¡ç', 'ç³»ç»ç®¡ç', 'å
¶ä»'], |
| | | filters: { keyword: '', module: '', exceptionType: '', dates: [] } |
| | | } |
| | | }, |
| | | created() { this.load() }, |
| | | methods: { |
| | | load() { |
| | | this.loading = true |
| | | const dates = this.filters.dates || [] |
| | | api.get('/system/error-logs', { |
| | | params: { |
| | | keyword: this.filters.keyword || undefined, |
| | | module: this.filters.module || undefined, |
| | | exceptionType: this.filters.exceptionType || undefined, |
| | | startDate: dates[0] || undefined, |
| | | endDate: dates[1] || undefined, |
| | | current: this.current, |
| | | size: this.size |
| | | } |
| | | }).then(res => { |
| | | const data = res.data || {} |
| | | this.rows = data.records || [] |
| | | this.total = data.total || 0 |
| | | }).finally(() => { this.loading = false }) |
| | | }, |
| | | search() { this.current = 1; this.load() }, |
| | | reset() { this.filters = { keyword: '', module: '', exceptionType: '', dates: [] }; this.search() }, |
| | | openDetail(row) { |
| | | this.detailLoading = true |
| | | this.detail = {} |
| | | this.detailVisible = true |
| | | api.get('/system/error-logs/' + row.id).then(res => { |
| | | this.detail = res.data || {} |
| | | }).finally(() => { this.detailLoading = false }) |
| | | }, |
| | | handleCurrentChange(page) { this.current = page; this.load() }, |
| | | handleSizeChange(size) { this.size = size; this.current = 1; this.load() } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .toolbar { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; } |
| | | .toolbar ::v-deep .el-range-separator { flex: 0 0 18px; min-width: 18px; text-align: center; } |
| | | .pager { margin-top: 12px; text-align: right; } |
| | | .detail-meta { margin-bottom: 8px; color: #42566b; line-height: 22px; word-break: break-all; } |
| | | .detail-meta b { color: #26394d; } |
| | | .stack-trace { |
| | | max-height: 520px; |
| | | margin: 12px 0 0; |
| | | padding: 12px; |
| | | border: 1px solid #dce4ec; |
| | | border-radius: 4px; |
| | | color: #30445a; |
| | | background: #f7f9fb; |
| | | font-family: Consolas, "Courier New", monospace; |
| | | font-size: 12px; |
| | | line-height: 18px; |
| | | white-space: pre-wrap; |
| | | word-break: break-all; |
| | | overflow: auto; |
| | | } |
| | | </style> |
| | |
| | | </script> |
| | | <style scoped> |
| | | .toolbar { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; } |
| | | .toolbar ::v-deep .el-range-separator { flex: 0 0 18px; min-width: 18px; text-align: center; } |
| | | .pager { margin-top: 12px; text-align: right; } |
| | | </style> |
| | |
| | | |
| | | <el-dialog :title="form.id ? 'ç¼è¾èå' : 'æ°å¢èå'" :visible.sync="dialogVisible" width="640px" append-to-body> |
| | | <el-form ref="form" :model="form" :rules="rules" label-width="105px"> |
| | | <el-form-item label="ä¸çº§èå"><el-input :value="parentName" readonly></el-input></el-form-item> |
| | | <el-form-item label="ä¸çº§èå"> |
| | | <el-cascader v-model="form.pid" :options="parentOptions" :props="parentProps" :show-all-levels="false" filterable clearable placeholder="è¯·éæ©ä¸çº§èå" style="width:100%"></el-cascader> |
| | | </el-form-item> |
| | | <el-form-item label="èåç±»å"> |
| | | <el-radio-group v-model="form.menuType"> |
| | | <el-radio-button v-for="item in typeOptions" :key="item.value" :label="item.value">{{ item.label }}</el-radio-button> |
| | |
| | | </el-dialog> |
| | | |
| | | <el-dialog :title="'页颿é®é
ç½® - ' + (buttonMenu.name || '')" :visible.sync="buttonDialogVisible" width="620px" append-to-body> |
| | | <p class="dialog-tip">ä¸åæé®æ¥èªå端æé®ç®å½ï¼å¾éåèªå¨çææé®ç±»åèåï¼å¯å¨èåæé管ç䏿æç»è§è²ã</p> |
| | | <p class="dialog-tip">ä¸åæé®æ¥èªå端æé®ç®å½ï¼å¾éåèªå¨çææé®ç±»åèåï¼å¯å¨è§è²ç®¡ççæéè®¾ç½®ä¸ææç»è§è²ã</p> |
| | | <el-checkbox-group v-model="buttonSelected" class="button-list"> |
| | | <el-checkbox v-for="item in buttonAvailable" :key="item.code" :label="item.code" class="button-item"> |
| | | <span class="button-name">{{ item.name }}</span> |
| | |
| | | dialogVisible: false, |
| | | saving: false, |
| | | form: {}, |
| | | parent: null, |
| | | rules: { name: [{ required: true, message: '请è¾å
¥èååç§°', trigger: 'blur' }] }, |
| | | buttonDialogVisible: false, |
| | | buttonSaving: false, |
| | |
| | | } |
| | | }, |
| | | computed: { |
| | | parentName() { |
| | | if (!this.parent) return this.form.pid && this.form.pid !== 0 ? 'ä¸çº§èå' : 'æ ï¼ä¸çº§èåï¼' |
| | | return this.parent.name |
| | | parentProps() { |
| | | return { value: 'id', label: 'name', children: 'children', checkStrictly: true, emitPath: false, expandTrigger: 'hover' } |
| | | }, |
| | | selectedParent() { |
| | | const pid = String(this.form.pid || '0') |
| | | if (pid === '0') return null |
| | | return this.flat.find(item => String(item.id) === pid) || null |
| | | }, |
| | | parentOptions() { |
| | | const excluded = this.excludedMenuIds() |
| | | const allowedType = this.form.menuType === 2 ? 1 : 0 |
| | | const nodes = this.buildParentOptions(this.tree, allowedType, excluded) |
| | | return this.form.menuType === 2 ? nodes : [{ id: '0', name: 'æ ï¼ä¸çº§èåï¼', children: nodes }] |
| | | }, |
| | | typeOptions() { |
| | | if (this.parent && this.parent.menuType === 1) { |
| | | if (this.selectedParent && this.selectedParent.menuType === 1) { |
| | | return [{ label: 'æé®', value: 2 }] |
| | | } |
| | | if (this.parent && this.parent.menuType === 0) { |
| | | if (this.selectedParent && this.selectedParent.menuType === 0) { |
| | | return [{ label: 'ç®å½', value: 0 }, { label: 'èå', value: 1 }] |
| | | } |
| | | if (this.form.id && this.form.menuType === 2) { |
| | |
| | | return type === 2 ? 'warning' : type === 0 ? 'info' : 'primary' |
| | | }, |
| | | openCreate(parent) { |
| | | this.parent = parent || null |
| | | const menuType = parent && parent.menuType === 1 ? 2 : parent ? 1 : 0 |
| | | this.form = { |
| | | pid: parent ? parent.id : 0, |
| | | pid: parent ? String(parent.id) : '0', |
| | | name: '', |
| | | url: '', |
| | | icon: menuType === 2 ? '' : 'el-icon-tickets', |
| | |
| | | this.dialogVisible = true |
| | | }, |
| | | openEdit(row) { |
| | | this.parent = this.flat.find(item => item.id === row.pid) || null |
| | | this.form = { ...row } |
| | | this.form = { ...row, pid: row.pid == null || row.pid === 0 ? '0' : String(row.pid) } |
| | | this.dialogVisible = true |
| | | }, |
| | | save() { |
| | |
| | | return |
| | | } |
| | | const payload = { ...this.form } |
| | | if (payload.pid === null || payload.pid === undefined || payload.pid === '') payload.pid = '0' |
| | | if (payload.menuType === 2) { |
| | | payload.url = null |
| | | payload.icon = null |
| | |
| | | }).finally(() => { this.saving = false }) |
| | | }) |
| | | }, |
| | | excludedMenuIds() { |
| | | const excluded = new Set() |
| | | const walk = (nodes, underSelf) => { |
| | | ;(nodes || []).forEach(node => { |
| | | const next = underSelf || String(node.id) === String(this.form.id) |
| | | if (next) excluded.add(String(node.id)) |
| | | walk(node.children, next) |
| | | }) |
| | | } |
| | | walk(this.tree, false) |
| | | return excluded |
| | | }, |
| | | buildParentOptions(nodes, allowedType, excluded) { |
| | | return (nodes || []) |
| | | .filter(node => node.menuType === allowedType && !excluded.has(String(node.id))) |
| | | .map(node => ({ |
| | | id: String(node.id), |
| | | name: node.name, |
| | | children: this.buildParentOptions(node.children, allowedType, excluded) |
| | | })) |
| | | }, |
| | | remove(row) { |
| | | this.$confirm(`ç¡®å®å é¤â${row.name}âåï¼`, 'å é¤ç¡®è®¤', { type: 'warning' }) |
| | | .then(() => api.delete('/system/menu/' + row.id)) |
| | |
| | | </script> |
| | | <style scoped> |
| | | .toolbar { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; } |
| | | .toolbar ::v-deep .el-range-separator { flex: 0 0 18px; min-width: 18px; text-align: center; } |
| | | .pager { margin-top: 12px; text-align: right; } |
| | | </style> |
| | |
| | | <template> |
| | | <div class="page"> |
| | | <div class="page-title"> |
| | | <div><h2>è§è²ç®¡ç</h2><p>ç»´æ¤è§è²ä¿¡æ¯ï¼å¹¶å¯æè§è²æ¹é设置æå±ç¨æ·</p></div> |
| | | <div><h2>è§è²ç®¡ç</h2><p>ç»´æ¤è§è²ä¿¡æ¯ï¼å¹¶å¯æè§è²è®¾ç½®æå±ç¨æ·åèåæé®æé</p></div> |
| | | <el-button type="primary" icon="el-icon-plus" v-permission="'system:roles:add'" @click="openCreate">æ°å¢è§è²</el-button> |
| | | </div> |
| | | <div class="table-panel"> |
| | |
| | | <el-table-column prop="code" label="è§è²ç¼ç " width="170"></el-table-column> |
| | | <el-table-column prop="roleType" label="è§è²ç±»å" width="170"></el-table-column> |
| | | <el-table-column prop="remark" label="夿³¨" min-width="220" show-overflow-tooltip></el-table-column> |
| | | <el-table-column label="æä½" width="220" fixed="right"> |
| | | <el-table-column label="æä½" width="300" fixed="right"> |
| | | <template slot-scope="{ row }"> |
| | | <el-button type="text" v-permission="'system:roles:assign'" @click="openUserSetting(row)">ç¨æ·è®¾ç½®</el-button> |
| | | <el-button type="text" v-permission="'system:roles:permission'" @click="openPermissionSetting(row)">æé设置</el-button> |
| | | <el-button type="text" v-permission="'system:roles:edit'" @click="openEdit(row)">ç¼è¾</el-button> |
| | | <el-button type="text" class="danger-link" v-permission="'system:roles:delete'" @click="remove(row)">å é¤</el-button> |
| | | </template> |
| | |
| | | <el-button type="primary" :loading="userSaving" v-permission="'system:roles:assign'" @click="saveRoleUsers">ä¿å</el-button> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <role-permission-dialog :visible.sync="permissionDialogVisible" :role="currentRole"></role-permission-dialog> |
| | | </div> |
| | | </template> |
| | | <script> |
| | | import api from '@/api' |
| | | import RolePermissionDialog from './RolePermissionDialog.vue' |
| | | export default { |
| | | name: 'RoleManage', |
| | | components: { RolePermissionDialog }, |
| | | data() { |
| | | return { |
| | | rows: [], |
| | | dialogVisible: false, |
| | | permissionDialogVisible: false, |
| | | saving: false, |
| | | userDialogVisible: false, |
| | | userLoading: false, |
| | |
| | | this.$nextTick(this.restoreUserSelection) |
| | | }).finally(() => { this.userLoading = false }) |
| | | }, |
| | | openPermissionSetting(row) { |
| | | this.currentRole = { ...row } |
| | | this.permissionDialogVisible = true |
| | | }, |
| | | restoreUserSelection() { |
| | | const table = this.$refs.roleUserTable |
| | | if (!table) return |
| New file |
| | |
| | | <template> |
| | | <el-dialog |
| | | title="è§è²æé设置" |
| | | :visible.sync="dialogVisible" |
| | | width="860px" |
| | | append-to-body |
| | | :close-on-click-modal="false" |
| | | > |
| | | <div class="permission-head"> |
| | | <div> |
| | | <span class="role-label">å½åè§è²</span> |
| | | <strong>{{ role.name }}</strong> |
| | | <span class="role-code">{{ role.code }}</span> |
| | | </div> |
| | | <span class="selected-total">å·²éæé {{ selectedRoleIds.length }} 个</span> |
| | | </div> |
| | | <div class="permission-toolbar"> |
| | | <el-radio-group v-model="permissionScope" size="small" @change="handleScopeChange"> |
| | | <el-radio-button label="all">å
¨é¨</el-radio-button> |
| | | <el-radio-button label="assigned">已设置</el-radio-button> |
| | | </el-radio-group> |
| | | <el-checkbox v-if="permissionScope === 'all'" v-model="checkAll" @change="toggleAll">å
¨é</el-checkbox> |
| | | <span class="permission-tip">{{ permissionTip }}</span> |
| | | </div> |
| | | <div v-loading="loading" class="permission-tree"> |
| | | <el-tree |
| | | ref="permissionTree" |
| | | :data="displayMenus" |
| | | node-key="id" |
| | | show-checkbox |
| | | default-expand-all |
| | | empty-text="该è§è²å°æªè®¾ç½®æé" |
| | | :render-content="renderContent" |
| | | :props="{ label: 'name', children: 'children' }" |
| | | @check="syncCheckAllState" |
| | | ></el-tree> |
| | | </div> |
| | | <template slot="footer"> |
| | | <el-button @click="dialogVisible = false">åæ¶</el-button> |
| | | <el-button type="primary" :loading="saving" v-permission="'system:roles:permission'" @click="save">ä¿å</el-button> |
| | | </template> |
| | | </el-dialog> |
| | | </template> |
| | | |
| | | <script> |
| | | import api from '@/api' |
| | | export default { |
| | | name: 'RolePermissionDialog', |
| | | props: { |
| | | visible: { type: Boolean, default: false }, |
| | | role: { type: Object, default: () => ({}) } |
| | | }, |
| | | data() { |
| | | return { |
| | | menus: [], |
| | | flatMenus: [], |
| | | loading: false, |
| | | saving: false, |
| | | checkAll: false, |
| | | permissionScope: 'all', |
| | | selectedRoleIds: [] |
| | | } |
| | | }, |
| | | computed: { |
| | | dialogVisible: { |
| | | get() { return this.visible }, |
| | | set(value) { this.$emit('update:visible', value) } |
| | | }, |
| | | allMenuIds() { |
| | | return this.flatMenus.map(item => String(item.id)) |
| | | }, |
| | | displayMenus() { |
| | | if (this.permissionScope === 'all') return this.menus |
| | | const assigned = new Set(this.selectedRoleIds.map(String)) |
| | | const filterAssigned = nodes => (nodes || []).reduce((result, node) => { |
| | | const children = filterAssigned(node.children) |
| | | if (assigned.has(String(node.id)) || children.length) { |
| | | result.push({ ...node, children }) |
| | | } |
| | | return result |
| | | }, []) |
| | | return filterAssigned(this.menus) |
| | | }, |
| | | permissionTip() { |
| | | return this.permissionScope === 'all' |
| | | ? 'èååæé®æéåå¯å¾éï¼ä¿åå忢è§è²ä¼èªå¨åæ¾ã' |
| | | : 'ä»
æ¾ç¤ºå½åè§è²å·²è®¾ç½®çæéåå
¶ç¶çº§èç¹ã' |
| | | } |
| | | }, |
| | | watch: { |
| | | visible(value) { |
| | | if (value) this.open() |
| | | }, |
| | | }, |
| | | methods: { |
| | | open() { |
| | | if (!this.role || !this.role.id) return |
| | | this.loading = true |
| | | this.checkAll = false |
| | | this.permissionScope = 'all' |
| | | this.selectedRoleIds = [] |
| | | Promise.all([ |
| | | api.get('/system/menu/tree'), |
| | | api.get('/system/menu/list'), |
| | | api.get('/system/menu-permissions', { params: { roleId: this.role.id } }) |
| | | ]).then(([tree, flat, permissions]) => { |
| | | this.menus = tree.data || [] |
| | | this.flatMenus = flat.data || [] |
| | | const data = permissions.data || {} |
| | | this.selectedRoleIds = (data.roleMenuIds || []).map(String) |
| | | this.$nextTick(() => { |
| | | if (!this.$refs.permissionTree) return |
| | | this.$refs.permissionTree.setCheckedKeys(this.displayCheckedKeys(this.selectedRoleIds)) |
| | | this.syncCheckAllState() |
| | | }) |
| | | }).finally(() => { this.loading = false }) |
| | | |
| | | }, |
| | | handleScopeChange() { |
| | | this.selectedRoleIds = this.checkedMenuIds() |
| | | this.$nextTick(() => { |
| | | if (!this.$refs.permissionTree) return |
| | | this.$refs.permissionTree.setCheckedKeys(this.displayCheckedKeys(this.selectedRoleIds)) |
| | | this.syncCheckAllState() |
| | | }) |
| | | }, |
| | | renderContent(h, { data }) { |
| | | const label = data.menuType === 2 ? data.name + ' [æé® ' + (data.permissions || '') + ']' : data.name |
| | | return h('span', { class: { 'perm-button-node': data.menuType === 2 } }, label) |
| | | }, |
| | | displayCheckedKeys(ids) { |
| | | const selected = new Set((ids || []).map(String)) |
| | | const children = {} |
| | | const menuIds = new Set(this.flatMenus.map(item => String(item.id))) |
| | | this.flatMenus.forEach(item => { |
| | | const id = String(item.id) |
| | | const pid = item.pid == null ? '0' : String(item.pid) |
| | | if (!children[pid]) children[pid] = [] |
| | | children[pid].push(id) |
| | | }) |
| | | const result = [] |
| | | const collectFullySelected = id => { |
| | | if (!selected.has(id)) return false |
| | | const childIds = children[id] || [] |
| | | if (!childIds.length) { |
| | | result.push(id) |
| | | return true |
| | | } |
| | | const allChildrenSelected = childIds.map(collectFullySelected).every(Boolean) |
| | | if (allChildrenSelected) result.push(id) |
| | | return allChildrenSelected |
| | | } |
| | | this.flatMenus |
| | | .filter(item => item.pid == null || String(item.pid) === '0' || !menuIds.has(String(item.pid))) |
| | | .forEach(item => collectFullySelected(String(item.id))) |
| | | return result |
| | | }, |
| | | checkedMenuIds() { |
| | | const tree = this.$refs.permissionTree |
| | | if (!tree) return [] |
| | | return [...new Set([...(tree.getCheckedKeys(false) || []), ...(tree.getHalfCheckedKeys() || [])].map(String))] |
| | | }, |
| | | syncCheckAllState() { |
| | | const checked = this.checkedMenuIds() |
| | | this.selectedRoleIds = checked |
| | | this.checkAll = checked.length === this.allMenuIds.length |
| | | }, |
| | | toggleAll() { |
| | | if (!this.$refs.permissionTree) return |
| | | this.$refs.permissionTree.setCheckedKeys(this.checkAll ? this.allMenuIds : []) |
| | | this.$nextTick(this.syncCheckAllState) |
| | | }, |
| | | save() { |
| | | const roleMenuIds = this.checkedMenuIds() |
| | | this.saving = true |
| | | api.post('/system/menu-permissions', { roleId: this.role.id, roleMenuIds }).then(() => { |
| | | this.selectedRoleIds = roleMenuIds |
| | | this.$message.success('è§è²æéå·²ä¿å') |
| | | this.$emit('saved') |
| | | this.dialogVisible = false |
| | | }).finally(() => { this.saving = false }) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .permission-head { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | margin-bottom: 12px; |
| | | color: #31465d; |
| | | } |
| | | .role-label { |
| | | margin-right: 10px; |
| | | color: #7b8b9c; |
| | | font-size: 13px; |
| | | } |
| | | .role-code { |
| | | margin-left: 8px; |
| | | color: #93a2b1; |
| | | font-size: 12px; |
| | | } |
| | | .selected-total { |
| | | color: #2f658f; |
| | | font-size: 13px; |
| | | } |
| | | .permission-toolbar { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 12px; |
| | | margin-bottom: 10px; |
| | | } |
| | | .permission-tip { |
| | | color: #7b8a9a; |
| | | font-size: 12px; |
| | | } |
| | | .permission-tree { |
| | | height: 460px; |
| | | padding: 8px 10px; |
| | | border: 1px solid #dce4ec; |
| | | border-radius: 4px; |
| | | overflow: auto; |
| | | } |
| | | ::v-deep .perm-button-node { |
| | | color: #b8791f; |
| | | } |
| | | </style> |
| | |
| | | - é管çåå®å·¥å¡«æ¥çåä½å¹é
ç®åæ `DEPT_NAME`ï¼ä¸æ¯ `DEPT_ID`ï¼è¥åºç°ååé¨é¨ï¼åºæ¹ä¸º ID 精确å¹é
ã |
| | | - å岿¼ç¤ºæ´¾å·¥åå¯è½ä»ä¿åæ§ `T_CONTRACTOR` åç§°ï¼é管çå cj ç¨æ·å¯è½çä¸å°ï¼`admin` ä¸ååä½è¿æ»¤å½±åã |
| | | - `.codex-run`ãOffice éæä»¶ä»¥å `emergency-repair-web/public/æ°å»º ææ¬ææ¡£.txt` æ¯æ¬å°èè°æä¸´æ¶æä»¶ï¼ä¸åºæäº¤ã |
| | | |
| | | ## åäºã2026-10-06 èè°æ¶å°¾ï¼éæ± 20ï¼ |
| | | |
| | | | åºå· | éæ± | ç¶æ | å®ç°ä¸éªè¯ | |
| | | | --- | --- | --- | --- | |
| | | | 20 | èåæé管çåæ¶ç§æ·æéï¼ä»
ä¿çè§è²æéï¼æéå¤éæ¡åæ¾ | 已宿 | æéæ ä¿çèååæé®èç¹ï¼è§è²åæ¢åæ£å¸¸åæ¾ï¼æ¥å£ä»
å¤ç `roleId` / `roleMenuIds` | |
| | | |
| | | ### 12.1 主è¦åæ´æä»¶ |
| | | |
| | | åç«¯ï¼ |
| | | |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/business/dto/Requests.java` |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/system/controller/SystemController.java` |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/system/service/SystemService.java` |
| | | |
| | | åç«¯ï¼ |
| | | |
| | | - `emergency-repair-web/src/views/system/MenuPermission.vue` |
| | | |
| | | ### 12.2 éªè¯ç»æ |
| | | |
| | | - å端 `mvn -B -DskipTests compile` éè¿ï¼å端 `npm run build` éè¿ã |
| | | - `GET /api/system/menu-permissions?roleId=2106221661022490625` è¿åå符串å `roleMenuIds` å
± 14 æ¡ï¼é¿å
æµè§å¨è§£æéªè± ID æ¶ä¸¢å¤±ç²¾åº¦ã |
| | | - `node .codex-run/verify-menu-permission.js` è¾åº `hasTenantPanel: false`ã`buttonNodeCount: 76`ã`checkedMatchesSaved: true`ã`hasError: false`ã |
| | | - 页é¢ä¿çæé®èç¹ï¼è§è²åæ¢åæéæ å¤éæ¡åæ¾æ£å¸¸ï¼æªå¾ `.codex-run/menu-permission-verified.png`ã |
| | | |
| | | ### 12.3 æ¥ææ³¨æ |
| | | |
| | | - æ¬è½®ä»£ç ä¸äº¤æ¥è®°å½ä¸å¹¶æäº¤å¹¶æ¨éå
¬å¸ Git ä»åº `main` 忝ã |
| | | - `.codex-run`ãOffice éæä»¶å `emergency-repair-web/public/æ°å»º ææ¬ææ¡£.txt` ä»ä¸ºæ¬å°ä¸´æ¶æä»¶ï¼ä¸åºæäº¤ã |
| | | |
| | | ## åä¸ã2026-10-06 èè°æ¶å°¾ï¼éæ± 21-22ï¼ |
| | | |
| | | | åºå· | éæ± | ç¶æ | å®ç°ä¸éªè¯ | |
| | | | --- | --- | --- | --- | |
| | | | 21 | èåæéç®¡çæ¹ä¸ºè§è²ç®¡ççâæé设置âå
¥å£ | 已宿 | å é¤ç¬ç«èåãè·¯ç±å页é¢ï¼è§è²å表æ°å¢âæé设置âï¼å¼¹çªä¿çèååæé®èç¹å¹¶æ£å¸¸åæ¾ | |
| | | | 22 | ç³»ç»ç®¡çæ°å¢äºçº§èåç»ï¼æ°å¢é误æ¥å¿ | 已宿 | æ°å¢âç³»ç»é
ç½® / æéé
ç½® / æ¥å¿ç®¡çâç®å½å¹¶è¿ç§»èåï¼é误æ¥å¿è®°å½éä¸å¡å¼å¸¸ï¼æ¯æçéãå页åå æ 详æ
| |
| | | |
| | | ### 13.1 主è¦åæ´æä»¶ |
| | | |
| | | åç«¯ï¼ |
| | | |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/system/entity/SysErrorLog.java` |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/system/mapper/SysErrorLogMapper.java` |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/system/log/ErrorLogRecorder.java` |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/common/GlobalExceptionHandler.java` |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/bootstrap/DataInitializer.java` |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/bootstrap/DatabaseBootstrap.java` |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/system/controller/SystemController.java` |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/system/service/SystemService.java` |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/system/service/ButtonPermissionCatalog.java` |
| | | |
| | | åç«¯ï¼ |
| | | |
| | | - `emergency-repair-web/src/views/system/RoleManage.vue` |
| | | - `emergency-repair-web/src/views/system/RolePermissionDialog.vue` |
| | | - `emergency-repair-web/src/views/system/ErrorLogManage.vue` |
| | | - `emergency-repair-web/src/views/system/MenuManage.vue` |
| | | - `emergency-repair-web/src/router/index.js` |
| | | - å é¤ `emergency-repair-web/src/views/system/MenuPermission.vue` |
| | | |
| | | ### 13.2 éªè¯ç»æ |
| | | |
| | | - å端 `mvn -B -DskipTests clean package` éè¿ï¼å端 `npm run build` éè¿ï¼ä»
ææ¢æå
ä½ç§¯åè¦ã |
| | | - `node .codex-run/verify-req21-22.js` éè¿ï¼äºçº§ç®å½åé误æ¥å¿èåæ¾ç¤ºæ£å¸¸ï¼æ§âèåæé管çâä¸åå¨ï¼è§è²æéå¼¹çªæé®èç¹æ° 78ï¼æéåæ¾æ£å¸¸ã |
| | | - é误æ¥å¿å表ãå¼å¸¸ç±»åçéåå æ 详æ
页é¢éªè¯éè¿ï¼äººä¸ºè§¦åéæ³æ¥å£åæ°åæåè½åºï¼ä¸´æ¶éªè¯è®°å½å·²æ¸
çã |
| | | - æªå¾ï¼`.codex-run/role-permission-dialog-verified.png`ã`.codex-run/error-log-verified.png`ã |
| | | |
| | | ### 13.3 æ¥ææ³¨æ |
| | | |
| | | - é误æ¥å¿ç®åè¦çè¿å
¥ Spring MVC å
¨å±å¼å¸¸å¤ççå¼å¸¸ï¼å¯å¨é¶æ®µå¼å¸¸ã弿¥çº¿ç¨å¼å¸¸è¥ç»è¿è¯¥å¤çå¨ï¼æä¸ä¼èªå¨è®°å½ã |
| | | - æ¬è½®ä»£ç ä¸äº¤æ¥è®°å½ä¸å¹¶æäº¤å¹¶æ¨éå
¬å¸ Git ä»åº `main` 忝ã |
| | | - `.codex-run`ãOffice éæä»¶å `emergency-repair-web/public/æ°å»º ææ¬ææ¡£.txt` ä»ä¸ºæ¬å°ä¸´æ¶æä»¶ï¼ä¸åºæäº¤ã |
| | | |
| | | ## ååã2026-10-06 èè°æ¶å°¾ï¼éæ± 23-25ï¼ |
| | | |
| | | | åºå· | éæ± | ç¶æ | å®ç°ä¸éªè¯ | |
| | | | --- | --- | --- | --- | |
| | | | 23 | ç³»ç»åç§°ãçæ¬æ¹ä¸ºå
³é®è¯é
ç½®ï¼çæ¬çº¢è²æ¾ç¤º | 已宿 | codename 为 `rk_SystemName`ã`rk_SystemVer`ï¼ç»å½é¡µã页头ãLogo åæµè§å¨æ é¢ç»ä¸è¯»åé
ç½®ï¼é¡µå¤´çæ¬ä¸ºçº¢è² | |
| | | | 24 | å®å·¥ä¿¡æ¯æ¥é
ï¼å¤æ
éå夿¡å®å·¥æç»å±ç¤º | 已宿 | å¢å `/completion/query` å `GET /api/work-orders/completion/query`ï¼ææ
éæ±æ»ï¼å䏿
éçå¤å¼ 派工åå±å¼ä¸ºå¤è¡ï¼æ
éä¿¡æ¯åä½¿ç¨ `rowspan` 纵ååå¹¶ï¼å¤çæ
åµåå¨ææ¯æå¤æ¡ | |
| | | | 25 | èå管çç¼è¾æ¶å¯ä¿®æ¹ç¶èç¹ | 已宿 | ä¸çº§èåæ¹ä¸ºçº§èéæ©å¨ï¼æé¤èªèº«åå
¨é¨ä¸çº§ï¼å端å¢å ç¶èç¹ãèªå¼ç¨å循ç¯å±çº§æ ¡éª | |
| | | |
| | | ### 14.1 主è¦åæ´æä»¶ |
| | | |
| | | åç«¯ï¼ |
| | | |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/business/dto/CompletionFaultQueryItem.java`ï¼æ°å¢ï¼ |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/business/controller/WorkOrderController.java` |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/business/service/BusinessService.java` |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/bootstrap/DataInitializer.java` |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/system/service/SystemService.java` |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/system/service/ButtonPermissionCatalog.java` |
| | | |
| | | åç«¯ï¼ |
| | | |
| | | - `emergency-repair-web/src/views/completion/CompletionQuery.vue`ï¼æ°å¢ï¼ |
| | | - `emergency-repair-web/src/views/system/MenuManage.vue` |
| | | - `emergency-repair-web/src/components/AppHeader.vue`ã`Login.vue`ã`RepairLogo.vue`ã`layout/AppLayout.vue`ã`router/index.js`ã`utils/sensitive.js`ã`main.js` |
| | | - `emergency-repair-web/public/sensitive-words.json`ã`public/index.html` |
| | | - `requirement/ææè¯åå
¸.json` |
| | | |
| | | ### 14.2 å®å·¥ä¿¡æ¯æ¥é
å£å¾ |
| | | |
| | | - ç»æææ
éæ±æ»ï¼ä¸ææ´¾å·¥åæ±æ»ã |
| | | - ä¸ä¸ªæ
éæå¤å¼ 已填æ¥å®å·¥ä¿¡æ¯çæ´¾å·¥åæ¶ï¼è¡¨æ ¼å±å¼ä¸ºå¤è¡ï¼åºå·ãæ
éç¼å·ãrk_Xå·ã设å¤åç§°ãå·¥ç¨åç§°åæåæ
åµã䏿¥åä½ã䏿¥æ¥æè¿ 7 åå
±ç¨æ
éè¡ï¼ä½¿ç¨åå
æ ¼çºµååå¹¶ã |
| | | - 派工åç¼å·ãæ¿ä¿®åä½ãå®å·¥ç¶æãå·¥ç¨ç¼å·ãå¼å·¥/å®å·¥ãæ¿ä¿®äººãå®å·¥å¤çæ
åµãæ´æ¢å¨æãä¿®åè´¨éãåå åæãæäº¤æ¶é´ææ´¾å·¥åå±ç¤ºã |
| | | - æ¥è¯¢æ¯æå
³é®è¯ãæ¿ä¿®åä½ãå®å·¥ç¶æåå®å·¥æ¥æï¼å
³é®è¯åæ¶å¹é
æ
éãæ´¾å·¥åãå·¥ç¨ç¼å·ãæ¿ä¿®äººãå¤çæ
åµå卿ã |
| | | |
| | | ### 14.3 éªè¯ç»æ |
| | | |
| | | - å端 `mvn -B -DskipTests compile` éè¿ï¼å端 `npm run build` éè¿ï¼ä»
ææ¢æå
ä½ç§¯åè¦ã |
| | | - `node .codex-run/verify-req23-25.js` éè¿ï¼ç³»ç»åç§°ãçæ¬åçº¢è²æ ·å¼æ£å¸¸ï¼å®å·¥æ¥é
æ£å¸¸ï¼èåç¼è¾ç¶èç¹âæ
é管çâå¯ç¼è¾ã |
| | | - ä¸´æ¶æé å䏿
éä¸¤å¼ æ´¾å·¥åã两æ¡å®å·¥è®°å½ï¼æ¥å£è¿å 1 个æ
éèç¹å 2 å¼ æ´¾å·¥åï¼å带å¤çæ
åµå卿æç»ã |
| | | - æµè§å¨æ¾ç¤º 2 å¼ æ´¾å·¥åè¡ï¼æ
éç¼å·åªåºç° 1 次ï¼7 个æ
éä¿¡æ¯åå为 `rowspan=2`ï¼ä¸¤å¼ åçæç»åæ£å¸¸ï¼æªå¾ `.codex-run/completion-query-merge-verified.png`ãä¸´æ¶æ°æ®å·²æ¸
çã |
| | | - èå级èç¶èç¹æ¥å£å®æµå¯æ£å¸¸ç§»å¨åèåï¼éæ©èªèº«æä¸çº§æ¶è¢«å端æç»ï¼ä¸´æ¶èåå·²æ¸
çã |
| | | |
| | | ### 14.4 å½åæå¡ä¸æ¥ææ³¨æ |
| | | |
| | | - å端 `http://localhost:8082`ï¼å端 `http://127.0.0.1:8091`ï¼DM8 `127.0.0.1:5236`ï¼Edge è°è¯ç«¯å£ `9222`ã |
| | | - ç»å½ `admin / admin123`ï¼æ°æ®åº `SYSDBA/SYSDBA`ï¼ä¸å¡ schema `LX`ã |
| | | - å®å·¥æ¥é
å½ååªå±ç¤ºå·²æ `T_COMPLETION_INFO` å®å·¥å¡«æ¥è®°å½ç派工åï¼å¹¶ä¸æ¥å£æªå页ã |
| | | - æ¬è½®ä»£ç ãéæ±æä»¶å交æ¥è®°å½ä¸å¹¶æäº¤å¹¶æ¨éå
¬å¸ Git ä»åº `main` 忝ã |
| | | - `.codex-run`ãOffice éæä»¶å `emergency-repair-web/public/æ°å»º ææ¬ææ¡£.txt` ä»ä¸ºæ¬å°ä¸´æ¶æä»¶ï¼ä¸åºæäº¤ã |
| | | ## åäºã2026-10-06 èè°æ¶å°¾ï¼éæ± 21 æé设置èå´åæ¢ï¼ |
| | | |
| | | | åºå· | éæ±è¡¥å
| ç¶æ | å®ç°ä¸éªè¯ | |
| | | | --- | --- | --- | --- | |
| | | | 21 | æé设置å¢å âå
¨é¨ / 已设置â忢 | 已宿 | `RolePermissionDialog.vue` å¢å èå´åæ¢ï¼âå
¨é¨âæ¾ç¤ºå®æ´èååæé®æ å¹¶å¯å
¨éï¼â已设置âä»
æ¾ç¤ºå½åè§è²å·²ææèç¹åç¶çº§è·¯å¾ï¼åæ¢æ¶ä¿çæªä¿åå¾éï¼éæ°æå¼é»è®¤âå
¨é¨â | |
| | | |
| | | ### 15.1 主è¦åæ´æä»¶ |
| | | |
| | | - `emergency-repair-web/src/views/system/RolePermissionDialog.vue` |
| | | - `requirement/æ¹è¿20261003.txt`ï¼è¡¥å
éæ± 21 çèå´åæ¢å£å¾ï¼ |
| | | |
| | | ### 15.2 éªè¯ç»æ |
| | | |
| | | - å端 `npm run build` éè¿ï¼ä»
ææ¢æå
ä½ç§¯åè¦ã |
| | | - `node .codex-run/verify-req21-22.js` éè¿ï¼å·¦ä¾§æ âèåæé管çâï¼è§è²ç®¡çåå¨âæé设置âã |
| | | - å
¨é¨è§å¾æ¾ç¤º 119 个å¤éæ¡ã13 个已å¾éã1 个åéï¼å·²è®¾ç½®è§å¾æ¾ç¤º 14 个èç¹ï¼å为å½å已设置èç¹æç¶çº§è·¯å¾ï¼ååå
¨é¨åå¾éç¶ææ¢å¤ï¼æ ç½ç»å¼å¸¸ã |
| | | - æªå¾ï¼`.codex-run/role-permission-assigned-scope.png`ã |
| | | |
| | | ### 15.3 æ¥ææ³¨æ |
| | | |
| | | - å½åè¿è¡ï¼å端 `http://localhost:8082`ï¼å端 `http://127.0.0.1:8091`ï¼DM8 `127.0.0.1:5236`ï¼Edge CDP `9222`ãç»å½ `admin / admin123`ã |
| | | - æ¬è½®ä»£ç ãéæ±æä»¶å交æ¥è®°å½ä¸å¹¶æäº¤å¹¶æ¨éå
¬å¸ Git ä»åº `main` 忝ã |
| | | - `.codex-run`ãOffice éæä»¶å `emergency-repair-web/public/æ°å»º ææ¬ææ¡£.txt` ä»ä¸ºæ¬å°ä¸´æ¶æä»¶ï¼ä¸åºæäº¤ã |
| | | |
| | | ## åå
ã2026-10-06 èè°æ¶å°¾ï¼éæ± 26 æ¥å¿æ¥æèå´åéç¬¦ï¼ |
| | | |
| | | | åºå· | éæ± | ç¶æ | å®ç°ä¸éªè¯ | |
| | | | --- | --- | --- | --- | |
| | | | 26 | æ¥å¿ç®¡ççæ¥æç»ä»¶ä¸¤ä¸ªæ¥æä¸é´çâè³âæ¾ç¤ºä¸å®æ´ | 已宿 | ç»å½æ¥å¿ãæä½æ¥å¿ãé误æ¥å¿ç `.el-range-separator` åºå®ä¸º `18px` å¹¶å±
ä¸ï¼ä¸ä¸ªé¡µé¢å®é
宽度ã`clientWidth`ã`scrollWidth` å为 `18px` | |
| | | |
| | | ### 16.1 主è¦åæ´æä»¶ |
| | | |
| | | - `emergency-repair-web/src/views/system/LoginLogManage.vue` |
| | | - `emergency-repair-web/src/views/system/OperationLogManage.vue` |
| | | - `emergency-repair-web/src/views/system/ErrorLogManage.vue` |
| | | - `requirement/æ¹è¿20261003.txt`ï¼è¡¥å
éæ± 26ï¼ |
| | | |
| | | ### 16.2 éªè¯ç»æ |
| | | |
| | | - å端 `npm run build` éè¿ï¼ä»
ææ¢æå
ä½ç§¯åè¦ã |
| | | - `node .codex-run/verify-req26.js` éè¿ï¼ä¸ä¸ªæ¥å¿é¡µé¢åæ¾ç¤ºâè³âï¼æ è£åï¼æ ç½ç»å¼å¸¸ã |
| | | - æªå¾ï¼`.codex-run/login-log-date-separator.png`ã`.codex-run/operation-log-date-separator.png`ã`.codex-run/error-log-date-separator.png`ã |
| | | |
| | | ### 16.3 æ¥ææ³¨æ |
| | | |
| | | - å½åè¿è¡ï¼å端 `http://localhost:8082`ï¼å端 `http://127.0.0.1:8091`ï¼DM8 `127.0.0.1:5236`ï¼Edge CDP `9222`ãç»å½ `admin / admin123`ã |
| | | - æ¬è½®ä»£ç ãéæ±æä»¶å交æ¥è®°å½ä¸å¹¶æäº¤å¹¶æ¨éå
¬å¸ Git ä»åº `main` 忝ã |
| | | - `.codex-run`ãOffice éæä»¶å `emergency-repair-web/public/æ°å»º ææ¬ææ¡£.txt` ä»ä¸ºæ¬å°ä¸´æ¶æä»¶ï¼ä¸åºæäº¤ã |
| | | |
| | | ## åä¸ã2026-10-06 èè°æ¶å°¾ï¼éæ± 27 设å¤éæ©èå´ï¼ |
| | | |
| | | | åºå· | éæ± | ç¶æ | å®ç°ä¸éªè¯ | |
| | | | --- | --- | --- | --- | |
| | | | 27 | æ°å¢æ
ééæ© `rk_Xå·` åï¼è®¾å¤éæ©å¨åºåªæ¾ç¤ºæé `side` èç¹çä¸çº§ | 已宿 | å端åªè¿åæé `side` èç¹ä½ä¸ºæ ¹ï¼å端èªå¨å±å¼è¯¥æ ¹èç¹å¹¶ç»§ç»æå è½½ä¸çº§ï¼æ¥å£ä¸æµè§å¨éªè¯éè¿ | |
| | | |
| | | ### 17.1 主è¦åæ´æä»¶ |
| | | |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/system/service/SystemService.java` |
| | | - `emergency-repair-web/src/components/ProductTreePicker.vue` |
| | | - `requirement/æ¹è¿20261003.txt`ï¼è¡¥å
éæ± 27ï¼ |
| | | |
| | | ### 17.2 éªè¯ç»æ |
| | | |
| | | - å端 `mvn -B -DskipTests compile`ã`mvn -B -DskipTests package` éè¿ï¼å端 `npm run build` éè¿ï¼ä»
ææ¢æå
ä½ç§¯åè¦ã |
| | | - `GET /api/system/tree/product/side-roots?xNo=203` åªè¿å对åºç `side` æ ¹èç¹ï¼`201 / 203 / 301 / æµè¯T` ååå«éªè¯éè¿ã |
| | | - `node .codex-run/verify-req27.js` éè¿ï¼æ ¹èç¹ææ¬ä»
为 `203`ï¼èªå¨å±å¼ 1 ä¸ªæ ¹èç¹ï¼æ¾ç¤ºä¸çº§ç³»ç»èç¹ï¼æ ç½ç»å¼å¸¸ã |
| | | - 请æ±é¾è·¯ç¡®è®¤ï¼`side-roots?xNo=203` åè¯·æ± `children?parentId=1280000000000009729`ãæªå¾ï¼`.codex-run/req27-side-tree.png`ã |
| | | |
| | | ### 17.3 æ¥ææ³¨æ |
| | | |
| | | - å½åè¿è¡ï¼å端 `http://localhost:8082`ï¼PID `138164`ï¼å端 `http://127.0.0.1:8091`ï¼PID `148268`ï¼DM8 `127.0.0.1:5236`ï¼Edge CDP `9222`ãç»å½ `admin / admin123`ã |
| | | - éæ± 26 å·²æäº¤å¹¶æ¨éï¼æäº¤ `f651ad5`ï¼éæ± 27 å·²æäº¤å¹¶æ¨éï¼æäº¤ `f9114ff`ï¼`fix: limit equipment picker to selected X number`ï¼ã |
| | | - æ¬è½®ä»£ç ãéæ±æä»¶å交æ¥è®°å½ä¸å¹¶æäº¤å¹¶æ¨éå
¬å¸ Git ä»åº `main` 忝ã |
| | | - `.codex-run`ãOffice éæä»¶å `emergency-repair-web/public/æ°å»º ææ¬ææ¡£.txt` ä»ä¸ºæ¬å°ä¸´æ¶æä»¶ï¼ä¸åºæäº¤ã |
| | | |
| | | ## åå
«ã2026-10-06 èè°ä¿®å¤ï¼èåå称被å¯å¨åå§åè¦çï¼ |
| | | |
| | | | é®é¢ | ç¶æ | å®ç°ä¸éªè¯ | |
| | | | --- | --- | --- | |
| | | | ç³»ç»é
ç½®ä¸â产åç»ææ 管çâæå·¥æ¹ä¸ºâ产åç»ææ âåï¼å端éå¯åå åâ管çâ | å·²ä¿®å¤ | `DataInitializer.ensureMenu()` ä¸åè¦çå·²åå¨èååç§°ï¼æ°å»ºèåé»è®¤åç§°æ¹ä¸ºâ产åç»ææ âï¼æ¹ååé坿° jar åç§°ä¿æä¸å | |
| | | |
| | | ### 18.1 主è¦åæ´æä»¶ |
| | | |
| | | - `emergency-repair-server/src/main/java/com/emergencyrepair/bootstrap/DataInitializer.java` |
| | | |
| | | ### 18.2 éªè¯ç»æ |
| | | |
| | | - å端 `mvn -B -DskipTests compile`ã`mvn -B -DskipTests package` éè¿ã |
| | | - èåè®°å½ `id=2101493905517301763`ã`url=/system/product-tree` æ¹å为â产åç»ææ âåï¼æ° jar éå¯å¹¶å次æ¥è¯¢ï¼åç§°ä»ä¸ºâ产åç»ææ âã |
| | | - å½åå端 PID `151580`ï¼çå¬ `http://127.0.0.1:8091`ï¼å端ä»çå¬ `http://localhost:8082`ã |
| | |
| | | |
| | | 17ãè§è²ç®¡çç¨æ·è®¾ç½®å¢å ä¸ä¸ªå
¨é¨/å·²è®¾ç½®åæ¢åè½ |
| | | 18ãå¢å åè½æ¾ç¤ºç»ç»æºæï¼é¨é¨æ è¡¨ï¼ |
| | | 19ãæ¿ä¿®åä½åºè¯¥å°é¨é¨è¡¨éæ¾DEPT_TYPEå为cjçè®°å½ |
| | | 19ãæ¿ä¿®åä½åºè¯¥å°é¨é¨è¡¨éæ¾DEPT_TYPEå为cjçè®°å½ |
| | | |
| | | 20ãèåæé管çï¼åæé管çåºè¯¥å2æ¥ï¼ 第1æ¥è®¾ç½®ç§æ·æéï¼ç¬¬2æ¥è®¾ç½®è§è²æéï¼ ç®åç¨æ·æ¬èº«æ¯æ²¡æç§æ·çï¼æä»¥è¿ä¸ªç§æ·æé设置就没å¿
è¦äºï¼ä¿çè§è²æé设置就å¯ä»¥äºï¼æéæ£æ¥æ¡è¦åæ¾ |
| | | 21ãèåæé管çï¼è¿ä¸ªèåä¸è¦ï¼å¨è§è²ç®¡çå¢å ä¸ä¸ªæé设置æé®ï¼ç±»ä¼¼ç¨æ·è®¾ç½®é£ä¸ªæé®é£æ ·ï¼ä¹è±¡ç¨æ·è®¾ç½®å¢å 个å
¨é¨/已设置ç忢å¼å
³ |
| | | 22ãç³»ç»ç®¡çä¸é¢å¢å 2级èåç»èç¹ï¼ ç³»ç»é
ç½®ï¼é¤äºæéé
ç½®ãæ¥å¿ç®¡ç管çä¸é¢åªäºèåå¤ï¼å
¶ä»èåæ¾å°ç³»ç»é
ç½®ä¸é¢ï¼ãæéé
ç½®ï¼ç¨æ·ç®¡çãè§è²ç®¡çãèå管çï¼ãæ¥å¿ç®¡çï¼ç»å½æ¥å¿ãæä½æ¥å¿ãé误æ¥å¿ï¼ |
| | | é误æ¥å¿ä¸ºæ°å¢åè½ï¼è®°å½ç¨åºè¿è¡åºç°çbug |
| | | |
| | | 23ãç³»ç»åç§°ãç½ç»ççåæå
³é®è¯é
ç½®ï¼codenameåå«ä¸ºrk_SystemNameãrk_SystemVer çæ¬ç¨çº¢è²å使¾ç¤º |
| | | 24ãå¢å å®å·¥ä¿¡æ¯æ¥é
åè½ï¼å¨ä¸ä¸ªè¡¨å±ç¤ºå¤ä¸ªæ
éåå®å·¥ä¿¡æ¯å±ç¤ºï¼å
¶ä¸å®å·¥å¤çæ
åµãæ´æ¢å¨æé½æ¯å¤ä¸ªï¼ |
| | | 25ãèå管çç¼è¾æ¶ï¼åºè¯¥å¯ä»¥ä¿®æ¹ç¶èç¹ |
| | | |
| | | 26ãæ¥å¿ç®¡ççæ¥æç»ä»¶2ä¸ªæ¥æä¸é´é£ä¸ªâè³âæ¾ç¤ºä¸å®æ´ |
| | | 27ãæ°å¢æ
éé£ééäºrk_Xå·ï¼å¯¹åºäº§åç»ææ çsideç±»åï¼åï¼åé设å¤çæ¶åè¿æ¯æ¾ç¤ºæ´ä¸ªäº§åç»ææ ï¼åºè¯¥æ¾ç¤ºééèç¹çä¸çº§èç¹ |
| | | |
| | |
| | | { |
| | | "sensitive_words": [ |
| | | { |
| | | "codename": "rk_SystemName", |
| | | "real_name": "临æ¢ä¿®ç®¡çç³»ç»", |
| | | "scope": "documents,ui,code", |
| | | "note": "ç³»ç»åç§°ï¼ç»å½é¡µã页头念è§å¨æ é¢ç»ä¸ä½¿ç¨è¯¥é
ç½®" |
| | | }, |
| | | { |
| | | "codename": "rk_SystemVer", |
| | | "real_name": "ç½ç»ç", |
| | | "scope": "documents,ui,code", |
| | | "note": "ç³»ç»çæ¬ï¼é¡µå¤´ä¸ä½¿ç¨çº¢è²å使¾ç¤º" |
| | | }, |
| | | { |
| | | "codename": "rk_Xå·", |
| | | "real_name": "è·å·", |
| | | "scope": "documents,ui,code", |