| New file |
| | |
| | | # OCRå¾åè¯å«åè½ä½¿ç¨è¯´æä¸æ
éæé¤æå |
| | | |
| | | ## ð åè½æ¦è¿° |
| | | |
| | | OCRï¼Optical Character Recognitionï¼å
å¦å符è¯å«ï¼åè½ç¨äºè¯å«å¾çä¸çæåå
å®¹ï¼æ¯æå¤ç§è¯å«ç±»åï¼ |
| | | - éç¨æåè¯å« |
| | | - å票è¯å« |
| | | - 身份è¯è¯å« |
| | | |
| | | ## ð§ ç³»ç»è¦æ± |
| | | |
| | | ### æå¡ä¾èµ |
| | | - é¿éäºOCRæå¡ï¼éè¦ææçAccessKeyï¼ |
| | | - ç½ç»è¿æ¥ï¼è®¿é® `ocr-api.cn-hangzhou.aliyuncs.com:443`ï¼ |
| | | |
| | | ### ææ¯æ |
| | | - å端ï¼Spring Boot + é¿éäºOCR SDK |
| | | - å端ï¼Vue.js + Element UI |
| | | |
| | | ## âï¸ é
置说æ |
| | | |
| | | ### 1. AccessKeyé
ç½® |
| | | å¨ `application.yml` ä¸é
ç½®ï¼ |
| | | ```yaml |
| | | ocr: |
| | | accessKeyId: YOUR_ACCESS_KEY_ID |
| | | accessKeySecret: YOUR_ACCESS_KEY_SECRET |
| | | ``` |
| | | |
| | | ### 2. ç½ç»é
ç½® |
| | | ç¡®ä¿æå¡å¨è½å¤è®¿é®ï¼ |
| | | - å°åï¼`ocr-api.cn-hangzhou.aliyuncs.com` |
| | | - 端å£ï¼`443` (HTTPS) |
| | | - åè®®ï¼TCP |
| | | |
| | | ## ð ä½¿ç¨æ¹æ³ |
| | | |
| | | ### 1. 访é®é¡µé¢ |
| | | èåè·¯å¾ï¼`ç³»ç»å·¥å
· > OCR管ç > OCRæµè¯` |
| | | |
| | | ### 2. ä¸ä¼ å¾ç |
| | | - æ¯ææ ¼å¼ï¼JPGãPNGãBMP |
| | | - æä»¶å¤§å°ï¼ä¸è¶
è¿4MB |
| | | - å¾çè´¨éï¼æ¸
æ°ï¼æåæè¾¨è®¤ |
| | | |
| | | ### 3. éæ©è¯å«ç±»å |
| | | - éç¨æåè¯å«ï¼éåä¸è¬ææ¡£ |
| | | - å票è¯å«ï¼éååç¥¨ãæ¶æ® |
| | | - 身份è¯è¯å«ï¼éåèº«ä»½è¯æ£åé¢ |
| | | |
| | | ## ð æ
éæé¤ |
| | | |
| | | ### 常è§é误åè§£å³æ¹æ¡ |
| | | |
| | | #### 1. ç½ç»è¿æ¥é误 |
| | | **é误信æ¯**ï¼`code: 415, The image format or content is not supported` |
| | | **å¯è½åå **ï¼ |
| | | - å¾çæ ¼å¼ä¸æ¯æ |
| | | - å¾çå
容æå |
| | | - å¾ç太大 |
| | | |
| | | **è§£å³æ¹æ¡**ï¼ |
| | | - æ£æ¥å¾çæ ¼å¼æ¯å¦ä¸ºJPG/PNG/BMP |
| | | - éªè¯å¾çæä»¶æ¯å¦å®æ´ |
| | | - å缩å¾çè³4MBä»¥ä¸ |
| | | |
| | | #### 2. ç½ç»è¿æ¥å¤±è´¥ |
| | | **é误信æ¯**ï¼`ocr-api.cn-hangzhou.aliyuncs.com` |
| | | **å¯è½åå **ï¼ |
| | | - DNSè§£æå¤±è´¥ |
| | | - é²ç«å¢é»æ¢è¿æ¥ |
| | | - ç½ç»çç¥éå¶ |
| | | - 代çé
ç½®é®é¢ |
| | | |
| | | **è§£å³æ¹æ¡**ï¼ |
| | | 1. **DNSé®é¢**ï¼ |
| | | - æ£æ¥DNSæå¡å¨é
ç½® |
| | | - å°è¯ä½¿ç¨å
Œ
±DNSï¼å¦8.8.8.8ï¼ |
| | | - éªè¯ååè§£æï¼`nslookup ocr-api.cn-hangzhou.aliyuncs.com` |
| | | |
| | | 2. **é²ç«å¢é®é¢**ï¼ |
| | | - æ£æ¥é²ç«å¢æ¯å¦å¼æ¾443ç«¯å£ |
| | | - 确认æå¡å¨å
许åºç«HTTPSè¯·æ± |
| | | - éªè¯å®å
¨ç»è§å |
| | | |
| | | 3. **代çé®é¢**ï¼ |
| | | - é
置系ç»ä»£çåæ°ï¼ |
| | | ``` |
| | | -Dhttp.proxyHost=proxy.example.com |
| | | -Dhttp.proxyPort=8080 |
| | | -Dhttps.proxyHost=proxy.example.com |
| | | -Dhttps.proxyPort=8080 |
| | | ``` |
| | | |
| | | #### 3. AccessKeyé误 |
| | | **é误信æ¯**ï¼è®¤è¯å¤±è´¥ç¸å
³é误 |
| | | **è§£å³æ¹æ¡**ï¼ |
| | | - æ£æ¥AccessKey IDåSecretæ¯å¦æ£ç¡® |
| | | - ç¡®è®¤è´¦æ·æOCRæå¡æé |
| | | - éªè¯AccessKeyæ¯å¦è¿æ |
| | | |
| | | ### è¯æå·¥å
· |
| | | |
| | | #### 1. ç½ç»è¯æ |
| | | 访é®é¡µé¢ï¼`ç³»ç»å·¥å
· > OCR管ç > OCRæµè¯` |
| | | å¨è¯å«å¤±è´¥æ¶ï¼ç¹å»"ç½ç»è¯æ"æé®æ¥çè¿æ¥ç¶æã |
| | | |
| | | #### 2. æå¨æµè¯å½ä»¤ |
| | | ```bash |
| | | # æµè¯DNSè§£æ |
| | | nslookup ocr-api.cn-hangzhou.aliyuncs.com |
| | | |
| | | # æµè¯ç«¯å£è¿éæ§ |
| | | telnet ocr-api.cn-hangzhou.aliyuncs.com 443 |
| | | |
| | | # æµè¯HTTPSè¿æ¥ |
| | | curl -I https://ocr-api.cn-hangzhou.aliyuncs.com |
| | | ``` |
| | | |
| | | ## ð ï¸ ç³»ç»ç»´æ¤ |
| | | |
| | | ### 1. æ¥å¿æ¥ç |
| | | - å端æ¥å¿ï¼`logs/ocr.log` |
| | | - é误æ¥å¿ï¼å
³æ³¨ `AliOCRUtil` ç±»çæ¥å¿ |
| | | |
| | | ### 2. æ§è½è°ä¼ |
| | | - è¿æ¥è¶
æ¶ï¼é»è®¤10ç§ |
| | | - 读åè¶
æ¶ï¼é»è®¤30ç§ |
| | | - å¯éè¿é
ç½®è°æ´è¶
æ¶æ¶é´ |
| | | |
| | | ### 3. å®å
¨æ³¨æäºé¡¹ |
| | | - AccessKeyä¸è¦ç¡¬ç¼ç å¨ä»£ç ä¸ |
| | | - å®ææ´æ¢AccessKey |
| | | - éå¶AccessKeyæéèå´ |
| | | |
| | | ## ð ææ¯æ¯æ |
| | | |
| | | å¦éæ æ³è§£å³çé®é¢ï¼è¯·æä¾ä»¥ä¸ä¿¡æ¯èç³»ææ¯æ¯æï¼ |
| | | - 宿´é误æ¥å¿ |
| | | - ç½ç»è¯æç»æ |
| | | - ç³»ç»ç¯å¢ä¿¡æ¯ |
| | | - é²ç«å¢/代çé
ç½®ä¿¡æ¯ |
| | | |
| | | ## ð æ£æ¥æ¸
å |
| | | |
| | | å¨é¨ç½²å使ç¨OCRåè½åï¼è¯·ç¡®è®¤ï¼ |
| | | - [ ] å·²å¼éé¿éäºOCRæå¡ |
| | | - [ ] å·²é
ç½®ææçAccessKey |
| | | - [ ] æå¡å¨å¯è®¿é®äºèç½ |
| | | - [ ] é²ç«å¢å¼æ¾443ç«¯å£ |
| | | - [ ] DNSè§£ææ£å¸¸ |
| | | - [ ] å¾çæ ¼å¼æ¯æéªè¯ |
| | | - [ ] ç½ç»è¿éæ§æµè¯éè¿ |
| | | |
| | | --- |
| | | |
| | | **注æ**ï¼æ¬åè½ä¾èµå¤é¨æå¡ï¼ç½ç»ç¶åµå¯è½å½±åè¯å«æåçåé度ã |
| New file |
| | |
| | | # OCRå¾åè¯å«åè½å®æ´è¯´æ |
| | | |
| | | ## ð åè½æ¦è¿° |
| | | |
| | | OCRï¼Optical Character Recognitionï¼å
å¦å符è¯å«ï¼åè½ç¨äºè¯å«å¾çä¸çæåå
å®¹ï¼æ¯æå¤ç§è¯å«ç±»åï¼ |
| | | - éç¨æåè¯å« |
| | | - å票è¯å« |
| | | - 身份è¯è¯å« |
| | | - æåä½è¯å« |
| | | |
| | | ## ð§ ç³»ç»è¦æ± |
| | | |
| | | ### æå¡ä¾èµ |
| | | - é¿éäºOCRæå¡ï¼éè¦ææçAccessKeyï¼ |
| | | - ç½ç»è¿æ¥ï¼è®¿é® `ocr-api.cn-hangzhou.aliyuncs.com:443`ï¼ |
| | | |
| | | ### ææ¯æ |
| | | - å端ï¼Spring Boot + é¿éäºOCR SDK |
| | | - å端ï¼Vue.js + Element UI |
| | | |
| | | ## âï¸ é
置说æ |
| | | |
| | | ### 1. AccessKeyé
ç½® |
| | | å¨ `application.yml` ä¸é
ç½®ï¼ |
| | | ```yaml |
| | | ocr: |
| | | accessKeyId: YOUR_ACCESS_KEY_ID |
| | | accessKeySecret: YOUR_ACCESS_KEY_SECRET |
| | | ``` |
| | | |
| | | ### 2. ç½ç»é
ç½® |
| | | ç¡®ä¿æå¡å¨è½å¤è®¿é®ï¼ |
| | | - å°åï¼`ocr-api.cn-hangzhou.aliyuncs.com` |
| | | - 端å£ï¼`443` (HTTPS) |
| | | - åè®®ï¼TCP |
| | | |
| | | ## ð ä½¿ç¨æ¹æ³ |
| | | |
| | | ### 1. 访é®é¡µé¢ |
| | | èåè·¯å¾ï¼`ç³»ç»å·¥å
· > OCR管ç > OCRæµè¯` |
| | | |
| | | ### 2. ä¸ä¼ å¾ç |
| | | - æ¯ææ ¼å¼ï¼JPGãPNGãBMP |
| | | - æä»¶å¤§å°ï¼ä¸è¶
è¿4MB |
| | | - å¾çè´¨éï¼æ¸
æ°ï¼æåæè¾¨è®¤ |
| | | |
| | | ### 3. éæ©è¯å«ç±»å |
| | | - éç¨æåè¯å«ï¼éåä¸è¬ææ¡£ |
| | | - å票è¯å«ï¼éååç¥¨ãæ¶æ® |
| | | - 身份è¯è¯å«ï¼éåèº«ä»½è¯æ£åé¢ |
| | | - æåä½è¯å«ï¼éåæåæåè¯å« |
| | | |
| | | ## ð ï¸ APIæ¥å£è¯´æ |
| | | |
| | | ### 1. è¯å«ç±»åè·å |
| | | - æ¥å£ï¼`GET /system/ocr/types` |
| | | - åè½ï¼è·åæ¯æçè¯å«ç±»åå表 |
| | | |
| | | ### 2. æ¬å°æä»¶è¯å« |
| | | - æ¥å£ï¼`POST /system/ocr/recognize` |
| | | - åæ°ï¼`file`ï¼å¾çæä»¶ï¼ã`type`ï¼è¯å«ç±»åï¼ |
| | | - åè½ï¼ä¸ä¼ å¾çå¹¶è¿è¡OCRè¯å« |
| | | |
| | | ### 3. URLè¯å« |
| | | - æ¥å£ï¼`GET /system/ocr/recognizeByUrl` |
| | | - åæ°ï¼`imageUrl`ï¼å¾çURLï¼ã`type`ï¼è¯å«ç±»åï¼ |
| | | - åè½ï¼éè¿URLè¿è¡OCRè¯å« |
| | | |
| | | ### 4. åæ®µæå |
| | | - æ¥å£ï¼`POST /system/ocr/extractFields` |
| | | - åæ°ï¼OCRè¯å«ç»æ |
| | | - åè½ï¼ä»OCRç»æä¸æåå
³é®å段 |
| | | |
| | | ### 5. ç½ç»è¯æ |
| | | - æ¥å£ï¼`GET /system/diag/ocrConnection` |
| | | - åè½ï¼è¯æOCRæå¡è¿æ¥ç¶æ |
| | | |
| | | ## ð§° å·¥å
·ç±»åè½ |
| | | |
| | | ### 1. AliOCRUtilç±» |
| | | æ¯æçè¯å«æ¹æ³ï¼ |
| | | - `recognizeGeneral()` - éç¨æåè¯å« |
| | | - `recognizeInvoice()` - å票è¯å« |
| | | - `recognizeIdCard()` - 身份è¯è¯å« |
| | | - `recognizeHandwriting()` - æåä½è¯å« |
| | | |
| | | ### 2. åæ®µæå |
| | | - `extractTargetFields()` - æåéé¢ãæ¥æã夿³¨çå
³é®ä¿¡æ¯ |
| | | |
| | | ### 3. è¯å«ç±»åæä¸¾ |
| | | - `OcrType.GENERAL` - éç¨æåè¯å« |
| | | - `OcrType.INVOICE` - å票è¯å« |
| | | - `OcrType.IDCARD` - 身份è¯è¯å« |
| | | - `OcrType.HANDWRITING` - æåä½è¯å« |
| | | |
| | | ## ð æ
éæé¤ |
| | | |
| | | ### 常è§é误åè§£å³æ¹æ¡ |
| | | |
| | | #### 1. ç½ç»è¿æ¥é误 |
| | | **é误信æ¯**ï¼`code: 415, The image format or content is not supported` |
| | | **å¯è½åå **ï¼ |
| | | - å¾çæ ¼å¼ä¸æ¯æ |
| | | - å¾çå
容æå |
| | | - å¾ç太大 |
| | | |
| | | **è§£å³æ¹æ¡**ï¼ |
| | | - æ£æ¥å¾çæ ¼å¼æ¯å¦ä¸ºJPG/PNG/BMP |
| | | - éªè¯å¾çæä»¶æ¯å¦å®æ´ |
| | | - å缩å¾çè³4MBä»¥ä¸ |
| | | |
| | | #### 2. ç½ç»è¿æ¥å¤±è´¥ |
| | | **é误信æ¯**ï¼`ocr-api.cn-hangzhou.aliyuncs.com` |
| | | **å¯è½åå **ï¼ |
| | | - DNSè§£æå¤±è´¥ |
| | | - é²ç«å¢é»æ¢è¿æ¥ |
| | | - ç½ç»çç¥éå¶ |
| | | - 代çé
ç½®é®é¢ |
| | | |
| | | **è§£å³æ¹æ¡**ï¼ |
| | | 1. **DNSé®é¢**ï¼ |
| | | - æ£æ¥DNSæå¡å¨é
ç½® |
| | | - å°è¯ä½¿ç¨å
Œ
±DNSï¼å¦8.8.8.8ï¼ |
| | | - éªè¯ååè§£æï¼`nslookup ocr-api.cn-hangzhou.aliyuncs.com` |
| | | |
| | | 2. **é²ç«å¢é®é¢**ï¼ |
| | | - æ£æ¥é²ç«å¢æ¯å¦å¼æ¾443ç«¯å£ |
| | | - 确认æå¡å¨å
许åºç«HTTPSè¯·æ± |
| | | - éªè¯å®å
¨ç»è§å |
| | | |
| | | 3. **代çé®é¢**ï¼ |
| | | - é
置系ç»ä»£çåæ°ï¼ |
| | | ``` |
| | | -Dhttp.proxyHost=proxy.example.com |
| | | -Dhttp.proxyPort=8080 |
| | | -Dhttps.proxyHost=proxy.example.com |
| | | -Dhttps.proxyPort=8080 |
| | | ``` |
| | | |
| | | #### 3. AccessKeyé误 |
| | | **é误信æ¯**ï¼è®¤è¯å¤±è´¥ç¸å
³é误 |
| | | **è§£å³æ¹æ¡**ï¼ |
| | | - æ£æ¥AccessKey IDåSecretæ¯å¦æ£ç¡® |
| | | - ç¡®è®¤è´¦æ·æOCRæå¡æé |
| | | - éªè¯AccessKeyæ¯å¦è¿æ |
| | | |
| | | ### è¯æå·¥å
· |
| | | |
| | | #### 1. ç½ç»è¯æ |
| | | 访é®é¡µé¢ï¼`ç³»ç»å·¥å
· > OCR管ç > OCRæµè¯` |
| | | å¨è¯å«å¤±è´¥æ¶ï¼ç¹å»"ç½ç»è¯æ"æé®æ¥çè¿æ¥ç¶æã |
| | | |
| | | #### 2. æå¨æµè¯å½ä»¤ |
| | | ```bash |
| | | # æµè¯DNSè§£æ |
| | | nslookup ocr-api.cn-hangzhou.aliyuncs.com |
| | | |
| | | # æµè¯ç«¯å£è¿éæ§ |
| | | telnet ocr-api.cn-hangzhou.aliyuncs.com 443 |
| | | |
| | | # æµè¯HTTPSè¿æ¥ |
| | | curl -I https://ocr-api.cn-hangzhou.aliyuncs.com |
| | | ``` |
| | | |
| | | ## ð ï¸ ç³»ç»ç»´æ¤ |
| | | |
| | | ### 1. æ¥å¿æ¥ç |
| | | - å端æ¥å¿ï¼`logs/ocr.log` |
| | | - é误æ¥å¿ï¼å
³æ³¨ `AliOCRUtil` ç±»çæ¥å¿ |
| | | |
| | | ### 2. æ§è½è°ä¼ |
| | | - è¿æ¥è¶
æ¶ï¼é»è®¤10ç§ |
| | | - 读åè¶
æ¶ï¼é»è®¤30ç§ |
| | | - å¯éè¿é
ç½®è°æ´è¶
æ¶æ¶é´ |
| | | |
| | | ### 3. å®å
¨æ³¨æäºé¡¹ |
| | | - AccessKeyä¸è¦ç¡¬ç¼ç å¨ä»£ç ä¸ |
| | | - å®ææ´æ¢AccessKey |
| | | - éå¶AccessKeyæéèå´ |
| | | |
| | | ## ð ææ¯æ¯æ |
| | | |
| | | å¦éæ æ³è§£å³çé®é¢ï¼è¯·æä¾ä»¥ä¸ä¿¡æ¯èç³»ææ¯æ¯æï¼ |
| | | - 宿´é误æ¥å¿ |
| | | - ç½ç»è¯æç»æ |
| | | - ç³»ç»ç¯å¢ä¿¡æ¯ |
| | | - é²ç«å¢/代çé
ç½®ä¿¡æ¯ |
| | | |
| | | ## ð æ£æ¥æ¸
å |
| | | |
| | | å¨é¨ç½²å使ç¨OCRåè½åï¼è¯·ç¡®è®¤ï¼ |
| | | - [ ] å·²å¼éé¿éäºOCRæå¡ |
| | | - [ ] å·²é
ç½®ææçAccessKey |
| | | - [ ] æå¡å¨å¯è®¿é®äºèç½ |
| | | - [ ] é²ç«å¢å¼æ¾443ç«¯å£ |
| | | - [ ] DNSè§£ææ£å¸¸ |
| | | - [ ] å¾çæ ¼å¼æ¯æéªè¯ |
| | | - [ ] ç½ç»è¿éæ§æµè¯éè¿ |
| | | |
| | | --- |
| | | |
| | | **注æ**ï¼æ¬åè½ä¾èµå¤é¨æå¡ï¼ç½ç»ç¶åµå¯è½å½±åè¯å«æåçåé度ã |
| New file |
| | |
| | | # OCRå¾åè¯å«æµè¯åè½ä½¿ç¨è¯´æ |
| | | |
| | | ## ð åè½æ¦è¿° |
| | | |
| | | OCRå¾åè¯å«æµè¯é¡µé¢ç¨äºæµè¯é¿éäºOCRæå¡ï¼æ¯æéç¨æåè¯å«ãå票è¯å«ã身份è¯è¯å«çåè½ã |
| | | |
| | | ## ð¯ å·²å®ç°çåè½ |
| | | |
| | | ### å端é¨å |
| | | |
| | | #### 1. OCRController |
| | | **è·¯å¾**: `ruoyi-system/src/main/java/com/ruoyi/system/controller/OCRController.java` |
| | | |
| | | **æ¥å£å表**: |
| | | - `POST /system/ocr/recognize` - ä¸ä¼ å¾çè¿è¡OCRè¯å« |
| | | - `GET /system/ocr/recognizeByUrl` - éè¿URLè¿è¡OCRè¯å« |
| | | - `POST /system/ocr/extractFields` - æåOCRç»æä¸çç®æ åæ®µ |
| | | |
| | | #### 2. AliOCRUtilå·¥å
·ç±» |
| | | **è·¯å¾**: `ruoyi-system/src/main/java/com/ruoyi/system/utils/AliOCRUtil.java` |
| | | |
| | | **ä¸»è¦æ¹æ³**: |
| | | - `recognizeTextByFile()` - æ¬å°æä»¶è¯å« |
| | | - `recognizeTextByUrl()` - URLå¾çè¯å« |
| | | - `recognizeInvoice()` - å票è¯å« |
| | | - `recognizeIdCard()` - 身份è¯è¯å« |
| | | - `recognizeGeneral()` - éç¨æåè¯å« |
| | | - `extractTargetFields()` - åæ®µæå |
| | | |
| | | ### å端é¨å |
| | | |
| | | #### 1. OCRæµè¯é¡µé¢ |
| | | **è·¯å¾**: `ruoyi-ui/src/views/system/ocr/index.vue` |
| | | |
| | | **åè½ç¹æ§**: |
| | | - ææ½ä¸ä¼ å¾ç |
| | | - å¾çé¢è§ |
| | | - è¯å«ç±»åéæ©ï¼éç¨/å票/身份è¯ï¼ |
| | | - 宿¶æ¾ç¤ºè¯å«ç»æ |
| | | - èªå¨æåå
³é®å段 |
| | | - åå§JSONæ°æ®æ¥ç |
| | | - ä¸é®å¤å¶è¯å«ç»æ |
| | | |
| | | #### 2. APIæ¥å£å°è£
|
| | | **è·¯å¾**: `ruoyi-ui/src/api/system/ocr.js` |
| | | |
| | | ## ð é¨ç½²æ¥éª¤ |
| | | |
| | | ### 1. æ§è¡èåSQL |
| | | |
| | | ```bash |
| | | # å¨MySQL䏿§è¡ |
| | | mysql -u root -p your_database < sql/ocr_test_menu.sql |
| | | ``` |
| | | |
| | | æå¨Navicatçå·¥å
·ä¸æ§è¡ `sql/ocr_test_menu.sql` æä»¶ |
| | | |
| | | ### 2. é
ç½®é¿éäºAccessKey |
| | | |
| | | **æä»¶**: `ruoyi-admin/src/main/resources/application.yml` |
| | | |
| | | ```yaml |
| | | ocr: |
| | | accessKeyId: YOUR_ACCESS_KEY_ID |
| | | accessKeySecret: YOUR_ACCESS_KEY_SECRET |
| | | ``` |
| | | |
| | | **éè¦**: è¯·æ¿æ¢ä¸ºä½ èªå·±çé¿éäºAccessKey |
| | | |
| | | ### 3. éå¯å端æå¡ |
| | | |
| | | ```bash |
| | | cd ruoyi-admin |
| | | mvn spring-boot:run |
| | | ``` |
| | | |
| | | ### 4. åé
æé |
| | | |
| | | ç»å½åå°ç®¡çç³»ç»ï¼ |
| | | 1. è¿å
¥ **ç³»ç»ç®¡ç > è§è²ç®¡ç** |
| | | 2. éæ©éè¦ä½¿ç¨OCRåè½çè§è² |
| | | 3. åé
æéï¼`system:ocr:test` å `system:ocr:recognize` |
| | | |
| | | ### 5. 访é®é¡µé¢ |
| | | |
| | | èåè·¯å¾ï¼**ç³»ç»å·¥å
· > OCRæµè¯** |
| | | |
| | | URL: `http://localhost/system/ocr` |
| | | |
| | | ## ð ä½¿ç¨æå |
| | | |
| | | ### åºæ¬æä½æµç¨ |
| | | |
| | | 1. **éæ©è¯å«ç±»å** |
| | | - éç¨æåè¯å«ï¼éç¨äºæ®éæåå
容 |
| | | - å票è¯å«ï¼ä¸é¨è¯å«åç¥¨ä¿¡æ¯ |
| | | - 身份è¯è¯å«ï¼è¯å«èº«ä»½è¯æ£åé¢ |
| | | |
| | | 2. **ä¸ä¼ å¾ç** |
| | | - ææ½å¾çå°ä¸ä¼ åºå |
| | | - æç¹å»ä¸ä¼ åºåéæ©æä»¶ |
| | | - æ¯æJPGãPNGãBMPæ ¼å¼ |
| | | - æä»¶å¤§å°ä¸è¶
è¿4MB |
| | | |
| | | 3. **å¼å§è¯å«** |
| | | - ç¹å»"å¼å§è¯å«"æé® |
| | | - çå¾
è¯å«å®æï¼é常1-3ç§ï¼ |
| | | |
| | | 4. **æ¥çç»æ** |
| | | - æååæ®µï¼èªå¨æåçå
³é®ä¿¡æ¯ï¼éé¢ãæ¥æã夿³¨çï¼ |
| | | - 宿´è¯å«å
å®¹ï¼ææè¯å«çæå |
| | | - åå§JSONæ°æ®ï¼é¿éäºè¿åç宿´æ°æ® |
| | | |
| | | 5. **å¤å¶ç»æ** |
| | | - ç¹å»"å¤å¶ç»æ"æé®ä¸é®å¤å¶è¯å«å
容 |
| | | |
| | | ### è¯å«ç±»å说æ |
| | | |
| | | #### éç¨æåè¯å« (General) |
| | | - éç¨åºæ¯ï¼åç±»ææ¡£ãå¾çä¸çæå |
| | | - è¿åå
å®¹ï¼ææè¯å«çæååä½ç½®ä¿¡æ¯ |
| | | |
| | | #### å票è¯å« (Invoice) |
| | | - éç¨åºæ¯ï¼å¢å¼ç¨åç¥¨ãæ®éåç¥¨ãæ¶æ®ç |
| | | - èªå¨æåï¼å票å·ç ãéé¢ãæ¥æãè´ä¹°æ¹ãé宿¹ç |
| | | |
| | | #### 身份è¯è¯å« (IdCard) |
| | | - éç¨åºæ¯ï¼èº«ä»½è¯æ£é¢ååé¢ |
| | | - èªå¨æåï¼å§åã身份è¯å·ãå°åãæææç |
| | | |
| | | ## ð§ é
置说æ |
| | | |
| | | ### OCRConfigé
置类 |
| | | **è·¯å¾**: `ruoyi-system/src/main/java/com/ruoyi/system/config/OCRConfig.java` |
| | | |
| | | ```java |
| | | @Component |
| | | @ConfigurationProperties(prefix = "ocr") |
| | | public class OCRConfig { |
| | | private String accessKeyId; |
| | | private String accessKeySecret; |
| | | // getteråsetter |
| | | } |
| | | ``` |
| | | |
| | | ### é¿éäºOCR APIç«¯ç¹ |
| | | é»è®¤ä½¿ç¨æå·èç¹ï¼`ocr-api.cn-hangzhou.aliyuncs.com` |
| | | |
| | | å¦éæ´æ¹ï¼ä¿®æ¹ `AliOCRUtil.java` ä¸ç `ENDPOINT` 常éã |
| | | |
| | | ## â ï¸ æ³¨æäºé¡¹ |
| | | |
| | | ### 1. AccessKeyå®å
¨ |
| | | - **ä¸è¦**å°AccessKeyæäº¤å°Gitä»åº |
| | | - 建议使ç¨ç¯å¢åéæé
ç½®ä¸å¿ç®¡ç |
| | | - å®ææ´æ¢AccessKey |
| | | |
| | | ### 2. æä»¶å¤§å°éå¶ |
| | | - å端éå¶ï¼4MB |
| | | - é¿éäºéå¶ï¼æ ¹æ®å¥é¤ä¸åï¼é常为4-10MB |
| | | - è¶
è¿éå¶ä¼å¯¼è´è¯å«å¤±è´¥ |
| | | |
| | | ### 3. è¯å«åç¡®ç |
| | | - å¾çæ¸
æ°åº¦å½±åè¯å«åç¡®ç |
| | | - 建议使ç¨300dpi以ä¸çå¾ç |
| | | - é¿å
模ç³ã徿ãåå
çå¾ç |
| | | |
| | | ### 4. è´¹ç¨è¯´æ |
| | | - é¿éäºOCRæè°ç¨æ¬¡æ°æ¶è´¹ |
| | | - æ°ç¨æ·æå
è´¹é¢åº¦ |
| | | - 建议å¨ç产ç¯å¢ä¸è®¾ç½®è°ç¨éå¶ |
| | | |
| | | ### 5. ä¸´æ¶æä»¶å¤ç |
| | | - ä¸ä¼ çå¾çä¼ä¿åå°ä¸´æ¶ç®å½ |
| | | - è¯å«å®æåèªå¨å é¤ |
| | | - 临æ¶ç®å½ï¼`System.getProperty("java.io.tmpdir")` |
| | | |
| | | ## ð 常è§é®é¢ |
| | | |
| | | ### 1. è¯å«å¤±è´¥ï¼AccessKeyé误 |
| | | **åå **: é
ç½®çAccessKey䏿£ç¡® |
| | | |
| | | **è§£å³**: |
| | | - æ£æ¥ `application.yml` ä¸çé
ç½® |
| | | - 确认AccessKey IDåSecretæ£ç¡® |
| | | - 确认账å·å·²å¼éOCRæå¡ |
| | | |
| | | ### 2. ä¸ä¼ åæ ååº |
| | | **åå **: æä»¶è¿å¤§æç½ç»é®é¢ |
| | | |
| | | **è§£å³**: |
| | | - æ£æ¥å¾ç大尿¯å¦è¶
è¿4MB |
| | | - æ£æ¥ç½ç»è¿æ¥ |
| | | - æ¥çæµè§å¨æ§å¶å°éè¯¯ä¿¡æ¯ |
| | | |
| | | ### 3. è¯å«ç»æä¸ºç©º |
| | | **åå **: å¾çè´¨éé®é¢æä¸æ¯æçå¾çæ ¼å¼ |
| | | |
| | | **è§£å³**: |
| | | - ä½¿ç¨æ¸
æ°çå¾ç |
| | | - ç¡®ä¿å¾çå
å«å¯è¯å«çæå |
| | | - å°è¯è½¬æ¢å¾çæ ¼å¼ |
| | | |
| | | ### 4. æéä¸è¶³ |
| | | **åå **: ç¨æ·è§è²æªåé
OCRæé |
| | | |
| | | **è§£å³**: |
| | | - è系管çååé
æé |
| | | - æå¨è§è²ç®¡çä¸å¾éç¸å
³æé |
| | | |
| | | ## ð æ©å±å¼å |
| | | |
| | | ### æ·»å æ°çè¯å«ç±»å |
| | | |
| | | 1. å¨åç«¯é¡µé¢æ·»å éé¡¹ï¼ |
| | | ```vue |
| | | <el-option label="è¥ä¸æ§ç
§è¯å«" value="BusinessLicense" /> |
| | | ``` |
| | | |
| | | 2. å端ä¼èªå¨æ¯æï¼æ éä¿®æ¹ä»£ç |
| | | |
| | | ### èªå®ä¹å段æå |
| | | |
| | | ä¿®æ¹ `AliOCRUtil.java` ä¸ç `extractTargetFields()` æ¹æ³ï¼ |
| | | |
| | | ```java |
| | | // æ·»å èªå®ä¹æåé»è¾ |
| | | if (text.contains("èªå®ä¹å
³é®å")) { |
| | | extracted.put("customField", text); |
| | | } |
| | | ``` |
| | | |
| | | ## ð ç¸å
³ææ¡£ |
| | | |
| | | - [é¿éäºOCR宿¹ææ¡£](https://help.aliyun.com/document_detail/442275.html) |
| | | - [RuoYiæ¡æ¶ææ¡£](http://doc.ruoyi.vip/) |
| | | |
| | | ## ð ææ¯æ¯æ |
| | | |
| | | 妿é®é¢ï¼è¯·èç³»ææ¯æ¯æå¢éææäº¤Issueã |
| | |
| | | } |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * åºäºåè¯å¹é
æç´¢å»é¢ï¼æ°ç®æ³ï¼æºè½åè¯+è¯åæåºï¼ |
| | | * @param {string} searchText æç´¢ææ¬ |
| | | * @param {number} deptId é¨é¨IDï¼å¯éï¼ç¨äºåºåè¿æ»¤ï¼ |
| | | * @param {number} limit è¿åç»ææ°ééå¶ï¼é»è®¤50ï¼ |
| | | */ |
| | | export function searchHospitalsByKeywords(searchText, deptId, limit = 50) { |
| | | return request({ |
| | | url: '/system/hospital/searchByKeywords', |
| | | method: 'get', |
| | | params: { |
| | | searchText: searchText, |
| | | deptId: deptId, |
| | | pageSize: limit |
| | | } |
| | | }) |
| | | } |
| New file |
| | |
| | | import config from '@/config' |
| | | import { getToken } from '@/utils/auth' |
| | | |
| | | const baseUrl = config.baseUrl |
| | | |
| | | /** |
| | | * OCRè¯å«API |
| | | */ |
| | | |
| | | /** |
| | | * åå¾OCRè¯å«ï¼éç¨æ¥å£ï¼ |
| | | * @param {String} filePath å¾ç临æ¶è·¯å¾ |
| | | * @param {String} type è¯å«ç±»åï¼HandWriting/IDCard/BankCard/General |
| | | * @param {String} provider OCRæå¡æä¾åï¼tencent/baidu |
| | | * @param {Array} itemNames éè¦æåçåæ®µåç§°æ°ç»ï¼æåä½è¯å«æ¶ä½¿ç¨ï¼ |
| | | * @returns {Promise} |
| | | */ |
| | | export function recognizeImage(filePath, type = 'HandWriting', provider = 'tencent', itemNames = []) { |
| | | return new Promise((resolve, reject) => { |
| | | const token = getToken() |
| | | |
| | | // æå»ºformData |
| | | const formData = { |
| | | type: type, |
| | | provider: provider |
| | | } |
| | | |
| | | // 妿æitemNamesï¼æ·»å å°formData |
| | | if (itemNames && itemNames.length > 0) { |
| | | itemNames.forEach((itemName, index) => { |
| | | formData[`itemNames[${index}]`] = itemName |
| | | }) |
| | | } |
| | | |
| | | uni.uploadFile({ |
| | | url: `${baseUrl}/system/ocr/recognize`, |
| | | filePath: filePath, |
| | | name: 'file', |
| | | header: { |
| | | 'Authorization': `Bearer ${token}` |
| | | }, |
| | | formData: formData, |
| | | success: (res) => { |
| | | try { |
| | | const response = JSON.parse(res.data) |
| | | if (response.code === 200) { |
| | | resolve(response) |
| | | } else { |
| | | reject(response) |
| | | } |
| | | } catch (e) { |
| | | reject({ msg: 'è§£æè¯å«ç»æå¤±è´¥', error: e }) |
| | | } |
| | | }, |
| | | fail: (err) => { |
| | | reject({ msg: 'OCR请æ±å¤±è´¥', error: err }) |
| | | } |
| | | }) |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * è
¾è®¯äºæåä½è¯å«ï¼åå¾ï¼ |
| | | * @param {String} filePath å¾ç临æ¶è·¯å¾ |
| | | * @param {Array} itemNames éè¦æåçåæ®µåç§°æ°ç» |
| | | * @returns {Promise} |
| | | */ |
| | | export function tencentHandwritingRecognize(filePath, itemNames = []) { |
| | | return new Promise((resolve, reject) => { |
| | | const token = getToken() |
| | | |
| | | // æå»ºformData |
| | | const formData = {} |
| | | if (itemNames && itemNames.length > 0) { |
| | | itemNames.forEach((itemName, index) => { |
| | | formData[`itemNames[${index}]`] = itemName |
| | | }) |
| | | } |
| | | |
| | | uni.uploadFile({ |
| | | url: `${baseUrl}/system/ocr/tencent/handwriting`, |
| | | filePath: filePath, |
| | | name: 'file', |
| | | header: { |
| | | 'Authorization': `Bearer ${token}` |
| | | }, |
| | | formData: formData, |
| | | success: (res) => { |
| | | try { |
| | | const response = JSON.parse(res.data) |
| | | if (response.code === 200) { |
| | | resolve(response) |
| | | } else { |
| | | reject(response) |
| | | } |
| | | } catch (e) { |
| | | reject({ msg: 'è§£æè¯å«ç»æå¤±è´¥', error: e }) |
| | | } |
| | | }, |
| | | fail: (err) => { |
| | | reject({ msg: 'OCR请æ±å¤±è´¥', error: err }) |
| | | } |
| | | }) |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * æ¹éOCRè¯å«ï¼å¤å¾ï¼ |
| | | * @param {Array} filePaths å¾ç临æ¶è·¯å¾æ°ç» |
| | | * @param {Array} itemNames éè¦æåçåæ®µåç§°æ°ç» |
| | | * @returns {Promise} è¿ååå¹¶åçåæ®µMap |
| | | */ |
| | | export function batchRecognizeImages(filePaths, itemNames = []) { |
| | | if (!filePaths || filePaths.length === 0) { |
| | | return Promise.reject({ msg: 'å¾çå表ä¸è½ä¸ºç©º' }) |
| | | } |
| | | |
| | | // å建ä¸ä¼ ä»»å¡éå |
| | | const uploadPromises = filePaths.map((filePath) => { |
| | | return tencentHandwritingRecognize(filePath, itemNames) |
| | | }) |
| | | |
| | | // çå¾
ææä¸ä¼ 宿并åå¹¶ç»æ |
| | | return Promise.all(uploadPromises) |
| | | .then(results => { |
| | | // åå¹¶ææç»æ |
| | | const mergedFields = {} |
| | | let successCount = 0 |
| | | let failCount = 0 |
| | | |
| | | results.forEach(response => { |
| | | if (response.code === 200 && response.data && response.data.fields) { |
| | | const fields = response.data.fields |
| | | // åå¹¶åæ®µï¼å¦ækeyå·²åå¨ä¸ä¸ä¸ºç©ºï¼ä¸è¦çï¼ |
| | | Object.keys(fields).forEach(key => { |
| | | if (!mergedFields[key] || mergedFields[key].trim() === '') { |
| | | mergedFields[key] = fields[key] |
| | | } |
| | | }) |
| | | successCount++ |
| | | } else { |
| | | failCount++ |
| | | } |
| | | }) |
| | | |
| | | // è¿æ»¤ç»æï¼åªè¿åitemNames䏿å®çåæ®µ |
| | | const filteredFields = {} |
| | | if (itemNames && itemNames.length > 0) { |
| | | itemNames.forEach(itemName => { |
| | | if (mergedFields.hasOwnProperty(itemName)) { |
| | | filteredFields[itemName] = mergedFields[itemName] |
| | | } |
| | | }) |
| | | } else { |
| | | // å¦ææ²¡ææå®itemNamesï¼è¿åææå段 |
| | | Object.assign(filteredFields, mergedFields) |
| | | } |
| | | |
| | | return { |
| | | success: true, |
| | | successCount: successCount, |
| | | failCount: failCount, |
| | | fields: filteredFields |
| | | } |
| | | }) |
| | | .catch(error => { |
| | | return Promise.reject({ |
| | | success: false, |
| | | msg: 'æ¹éè¯å«å¤±è´¥', |
| | | error: error |
| | | }) |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * é»è®¤ç转è¿ååæ®µå表 |
| | | */ |
| | | export const DEFAULT_TRANSFER_ITEM_NAMES = [ |
| | | "æ£è
å§å", "æ§å«", "å¹´é¾", "身份è¯å·", "è¯æ", "鿝ä»è½¬è¿è´¹ç¨", |
| | | "è¡ç¨", "å¼å§æ¶é´", "ç»ææ¶é´", "å®¶å±ç¾å", "æ£è
ç¾åï¼æå°ï¼", |
| | | "ç¾å人身份è¯å·ç ", "æ¥æ", "èç³»çµè¯", "æ¬äºº", "ç¾åäººä¸æ£è
å
³ç³»" |
| | | ] |
| | |
| | | </template> |
| | | |
| | | <script> |
| | | import { searchHospitals } from "@/api/hospital" |
| | | import { searchHospitals, searchHospitalsByKeywords } from "@/api/hospital" |
| | | import { searchTianDiTuAddress } from "@/api/map" |
| | | |
| | | export default { |
| | |
| | | }, 300) |
| | | }, |
| | | |
| | | // æç´¢å»é¢ |
| | | // æç´¢å»é¢ï¼æºè½éæ©æ¥å£ï¼ |
| | | searchHospital(keyword) { |
| | | searchHospitals(keyword, this.deptId).then(response => { |
| | | this.searchResults = response.data || [] |
| | | this.showResults = true |
| | | }).catch(error => { |
| | | // console.error('æç´¢å»é¢å¤±è´¥:', error) |
| | | this.searchResults = [] |
| | | // this.showResults = false |
| | | }) |
| | | // 妿å
³é®è¯ä¸ºç©ºæè
æ¯"å®¶ä¸"ï¼ä½¿ç¨åæ¥çæ¥å£ |
| | | if (!keyword || keyword.trim() === '' || keyword.trim() === 'å®¶ä¸') { |
| | | searchHospitals(keyword || '', this.deptId).then(response => { |
| | | this.searchResults = response.data || [] |
| | | this.showResults = true |
| | | }).catch(error => { |
| | | // console.error('æç´¢å»é¢å¤±è´¥:', error) |
| | | this.searchResults = [] |
| | | // this.showResults = false |
| | | }) |
| | | } else { |
| | | // æå
³é®è¯æ¶ï¼ä½¿ç¨æ°çåè¯å¹é
æ¥å£ |
| | | searchHospitalsByKeywords(keyword, this.deptId).then(response => { |
| | | // è½¬æ¢æ°æ®æ ¼å¼ï¼æå hospital 对象 |
| | | const rawData = response.data || [] |
| | | this.searchResults = rawData.map(item => { |
| | | // å¦ææ°æ®ç»ææ¯ {hospital: {...}, matchScore: ...} |
| | | if (item.hospital) { |
| | | return item.hospital |
| | | } |
| | | // å¦æå·²ç»æ¯å»é¢å¯¹è±¡ï¼ç´æ¥è¿å |
| | | return item |
| | | }) |
| | | this.showResults = true |
| | | }).catch(error => { |
| | | // console.error('æç´¢å»é¢å¤±è´¥:', error) |
| | | this.searchResults = [] |
| | | // this.showResults = false |
| | | }) |
| | | } |
| | | }, |
| | | |
| | | // è¾å
¥æ¡è·å¾ç¦ç¹ |
| | |
| | | } |
| | | }, |
| | | |
| | | // å è½½é»è®¤å»é¢å表 |
| | | // å è½½é»è®¤å»é¢å表ï¼ä½¿ç¨åæ¥çæ¥å£ï¼ |
| | | loadDefaultHospitals() { |
| | | // 使ç¨åæ¥çæ¥å£å è½½é»è®¤å表 |
| | | searchHospitals('', this.deptId).then(response => { |
| | | this.defaultHospitals = response.data || [] |
| | | this.searchResults = this.defaultHospitals |
| | |
| | | <view class="smart-parse-btn" @click="showSmartParsePopup"> |
| | | <uni-icons type="compose" size="20" color="#007AFF"></uni-icons> |
| | | <text>æºè½è¯å«</text> |
| | | </view> |
| | | <view class="multi-photo-ocr-btn" @click="showMultiPhotoOCRPopup"> |
| | | <uni-icons type="images" size="20" color="#FF6B00"></uni-icons> |
| | | <text>æç
§è¯å«</text> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- å¤å¾çæç
§è¯å«å¼¹çª --> |
| | | <uni-popup ref="multiPhotoOCRPopup" type="bottom" :safe-area="true"> |
| | | <view class="photo-ocr-popup"> |
| | | <view class="popup-header"> |
| | | <view class="popup-title"> |
| | | <uni-icons type="images" size="22" color="#FF6B00"></uni-icons> |
| | | <text>æç
§è¯å« - ç¥æ
åæä¹¦</text> |
| | | </view> |
| | | <view class="popup-close" @click="closeMultiPhotoOCRPopup"> |
| | | <uni-icons type="closeempty" size="24" color="#333"></uni-icons> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="ocr-content"> |
| | | <view class="ocr-tip"> |
| | | <uni-icons type="info" size="18" color="#FF6B00"></uni-icons> |
| | | <text>请åå«ä¸ä¼ ç¥æ
åæä¹¦ç第ä¸é¡µå第äºé¡µï¼ç³»ç»å°èªå¨è¯å«å¹¶å¡«å
å°è¡¨åä¸</text> |
| | | </view> |
| | | |
| | | <!-- ç¥æ
åæä¹¦ç¬¬ä¸é¡µ --> |
| | | <view class="page-upload-section first-page"> |
| | | <view class="page-header"> |
| | | <view class="page-title"> |
| | | <view class="page-badge"> |
| | | <uni-icons type="compose" size="18" color="#52c41a"></uni-icons> |
| | | <text class="page-number">1</text> |
| | | </view> |
| | | <view class="page-info"> |
| | | <text class="page-main-title">ç¥æ
åæä¹¦ç¬¬ä¸é¡µ</text> |
| | | <text class="page-sub-title">æ£è
åºæ¬ä¿¡æ¯é¡µ</text> |
| | | </view> |
| | | </view> |
| | | <!-- åªææªä¸ä¼ æ¶æ¾ç¤º+å· --> |
| | | <view class="upload-btn green" @click="selectPage1Images" v-if="!page1Image"> |
| | | <uni-icons type="plusempty" size="30" color="#52c41a"></uni-icons> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- è¯å«å段æç¤º --> |
| | | <view class="field-hint" v-if="!page1Image"> |
| | | <text>è¯å«åæ®µï¼æ£è
å§åãæ§å«ãå¹´é¾ã身份è¯å·ãè¯æã转è¿è´¹ç¨ãè¡ç¨ãå¼å§æ¶é´ãç»ææ¶é´ãå®¶å±ç¾å</text> |
| | | </view> |
| | | |
| | | <!-- åå¾é¢è§ --> |
| | | <view class="single-image-container" v-if="page1Image"> |
| | | <image :src="page1Image" mode="aspectFit" class="preview-image"></image> |
| | | <view class="delete-btn" @click="deletePage1Image"> |
| | | <uni-icons type="closeempty" size="20" color="#fff"></uni-icons> |
| | | </view> |
| | | <view class="image-status"> |
| | | <uni-icons type="checkmarkempty" size="16" color="#fff"></uni-icons> |
| | | <text>å·²ä¸ä¼ </text> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 第ä¸é¡µè¯å«ç»æ --> |
| | | <view class="recognition-result" v-if="Object.keys(page1Fields).length > 0"> |
| | | <view class="result-title"> |
| | | <uni-icons type="checkmarkempty" size="18" color="#52c41a"></uni-icons> |
| | | <text>è¯å«ç»æ</text> |
| | | <text class="result-count">(å
±{{ Object.keys(page1Fields).length }}ä¸ªåæ®µ)</text> |
| | | </view> |
| | | <view class="field-list"> |
| | | <view class="field-item" v-for="(value, key) in page1Fields" :key="key"> |
| | | <text class="field-name">{{ key }}</text> |
| | | <text class="field-value">{{ value || 'æªè¯å«' }}</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- ç¥æ
åæä¹¦ç¬¬äºé¡µ --> |
| | | <view class="page-upload-section second-page"> |
| | | <view class="page-header"> |
| | | <view class="page-title"> |
| | | <view class="page-badge"> |
| | | <uni-icons type="compose" size="18" color="#FF6B00"></uni-icons> |
| | | <text class="page-number">2</text> |
| | | </view> |
| | | <view class="page-info"> |
| | | <text class="page-main-title">ç¥æ
åæä¹¦ç¬¬äºé¡µ</text> |
| | | <text class="page-sub-title">ç¾åä¸è系信æ¯é¡µ</text> |
| | | </view> |
| | | </view> |
| | | <!-- åªææªä¸ä¼ æ¶æ¾ç¤º+å· --> |
| | | <view class="upload-btn orange" @click="selectPage2Images" v-if="!page2Image"> |
| | | <uni-icons type="plusempty" size="30" color="#FF6B00"></uni-icons> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- è¯å«å段æç¤º --> |
| | | <view class="field-hint" v-if="!page2Image"> |
| | | <text>è¯å«åæ®µï¼æ£è
ç¾åãç¾å人身份è¯å·ãç¾åæ¥æãèç³»çµè¯ãç¾å人å
³ç³»</text> |
| | | </view> |
| | | |
| | | <!-- åå¾é¢è§ --> |
| | | <view class="single-image-container" v-if="page2Image"> |
| | | <image :src="page2Image" mode="aspectFit" class="preview-image"></image> |
| | | <view class="delete-btn" @click="deletePage2Image"> |
| | | <uni-icons type="closeempty" size="20" color="#fff"></uni-icons> |
| | | </view> |
| | | <view class="image-status"> |
| | | <uni-icons type="checkmarkempty" size="16" color="#fff"></uni-icons> |
| | | <text>å·²ä¸ä¼ </text> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 第äºé¡µè¯å«ç»æ --> |
| | | <view class="recognition-result" v-if="Object.keys(page2Fields).length > 0"> |
| | | <view class="result-title"> |
| | | <uni-icons type="checkmarkempty" size="18" color="#FF6B00"></uni-icons> |
| | | <text>è¯å«ç»æ</text> |
| | | <text class="result-count">(å
±{{ Object.keys(page2Fields).length }}ä¸ªåæ®µ)</text> |
| | | </view> |
| | | <view class="field-list"> |
| | | <view class="field-item" v-for="(value, key) in page2Fields" :key="key"> |
| | | <text class="field-name">{{ key }}</text> |
| | | <text class="field-value">{{ value || 'æªè¯å«' }}</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="popup-footer"> |
| | | <button class="cancel-btn" @click="closeMultiPhotoOCRPopup">åæ¶</button> |
| | | <button |
| | | class="confirm-btn" |
| | | @click="applyOcrResult" |
| | | :disabled="Object.keys(page1Fields).length === 0 && Object.keys(page2Fields).length === 0" |
| | | > |
| | | åºç¨å°è¡¨å |
| | | </button> |
| | | </view> |
| | | </view> |
| | | </uni-popup> |
| | | |
| | | <view class="form-section"> |
| | | <view class="form-item"> |
| | |
| | | </view> |
| | | </view> |
| | | </uni-popup> |
| | | |
| | | <!-- æç
§è¯å«å¼¹çª --> |
| | | <uni-popup ref="photoOCRPopup" type="bottom" :safe-area="true"> |
| | | <view class="photo-ocr-popup"> |
| | | <view class="popup-header"> |
| | | <view class="popup-title">æç
§è¯å«</view> |
| | | <view class="popup-close" @click="closePhotoOCRPopup"> |
| | | <uni-icons type="closeempty" size="24" color="#333"></uni-icons> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="ocr-content"> |
| | | <view class="ocr-tip"> |
| | | <uni-icons type="info" size="18" color="#007AFF"></uni-icons> |
| | | <text>æç
§æéæ©å¾çï¼èªå¨è¯å«è½¬è¿åä¿¡æ¯</text> |
| | | </view> |
| | | |
| | | <view class="image-preview" v-if="ocrImage"> |
| | | <image :src="ocrImage" mode="aspectFit" style="width: 100%; height: 300rpx; border-radius: 10rpx;"></image> |
| | | </view> |
| | | |
| | | <view class="ocr-actions"> |
| | | <button class="select-btn" @click="selectImage">éæ©å¾ç</button> |
| | | <button class="capture-btn" @click="captureImage">æç
§</button> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="popup-footer"> |
| | | <button class="cancel-btn" @click="closePhotoOCRPopup">åæ¶</button> |
| | | <button class="confirm-btn" @click="performOCR" :disabled="ocrLoading"> |
| | | {{ ocrLoading ? 'è¯å«ä¸...' : 'å¼å§è¯å«' }} |
| | | </button> |
| | | </view> |
| | | </view> |
| | | </uni-popup> |
| | | </scroll-view> |
| | | </template> |
| | | |
| | |
| | | import uniPopup from '@/uni_modules/uni-popup/components/uni-popup/uni-popup.vue' |
| | | import { addTask, checkTaskDuplicate } from "@/api/task" |
| | | import { listAvailableVehicles, getUserBoundVehicle } from "@/api/vehicle" |
| | | import { searchHospitals, searchHospitalsByDeptRegion } from "@/api/hospital" |
| | | import { searchHospitals, searchHospitalsByDeptRegion, searchHospitalsByKeywords } from "@/api/hospital" |
| | | import DepartureSelector from './components/DepartureSelector.vue' |
| | | import { calculateTianDiTuDistance } from "@/api/map" |
| | | import { listBranchUsers } from "@/api/system/user" |
| | | import { searchIcd10 } from "@/api/icd10" |
| | | import { calculateTransferPrice } from "@/api/price" |
| | | import { checkVehicleActiveTasks } from "@/api/task" |
| | | import { recognizeImage, batchRecognizeImages, DEFAULT_TRANSFER_ITEM_NAMES } from "@/api/ocr" |
| | | import config from '@/config' |
| | | import { getToken } from '@/utils/auth' |
| | | |
| | | import { getDicts } from "@/api/dict" |
| | | import { getServiceOrdAreaTypes, getServiceOrderTypes } from "@/api/dictionary" |
| | |
| | | loading: false, |
| | | // æºè½è¯å«ç¸å
³ |
| | | rawText: '', |
| | | parseLoading: false |
| | | parseLoading: false, |
| | | // æç
§è¯å«ç¸å
³ |
| | | ocrImage: '', |
| | | ocrLoading: false, |
| | | // å¤å¾çæç
§è¯å«ç¸å
³ |
| | | multiOcrImages: [], |
| | | multiOcrLoading: false, |
| | | // å页OCRè¯å«ç¸å
³ |
| | | currentOcrPage: 1, // å½åä¸ä¼ ç页ç ï¼1=第ä¸é¡µï¼2=第äºé¡µ |
| | | page1Image: '', // 第ä¸é¡µå¾çï¼åå¾ï¼ |
| | | page2Image: '', // 第äºé¡µå¾çï¼åå¾ï¼ |
| | | page1Fields: {}, // 第ä¸é¡µè¯å«ç»æ |
| | | page2Fields: {}, // 第äºé¡µè¯å«ç»æ |
| | | // é件临æ¶åå¨ï¼OCRå¾çï¼ |
| | | pendingAttachments: [] // å¾
ä¸ä¼ çéä»¶å表 [{ filePath: '', category: '1' }] |
| | | } |
| | | }, |
| | | computed: { |
| | |
| | | |
| | | addTask(submitData).then(response => { |
| | | this.loading = false |
| | | this.$modal.showToast('ä»»å¡å建æå') |
| | | |
| | | // å»¶è¿è·³è½¬ï¼è®©ç¨æ·çå°æåæç¤º |
| | | setTimeout(() => { |
| | | // 跳转å°ä»»å¡å表并触åå·æ° |
| | | uni.switchTab({ |
| | | url: '/pages/task/index', |
| | | success: () => { |
| | | // 使ç¨äºä»¶æ»çº¿éç¥ä»»å¡å表页é¢å·æ° |
| | | uni.$emit('refreshTaskList') |
| | | } |
| | | // è·åå建çä»»å¡ID |
| | | const taskId = response.taskId || (response.data && response.data.taskId) || null |
| | | console.log('ä»»å¡å建æåï¼taskId:', taskId) |
| | | |
| | | // 妿æå¾
ä¸ä¼ çéä»¶ï¼OCRå¾çï¼ï¼å
ä¸ä¼ éä»¶ |
| | | if (taskId && this.pendingAttachments.length > 0) { |
| | | this.uploadPendingAttachments(taskId).then(() => { |
| | | this.$modal.showToast('ä»»å¡å建æåï¼éä»¶å·²ä¸ä¼ ') |
| | | this.navigateToTaskList() |
| | | }).catch(error => { |
| | | console.error('éä»¶ä¸ä¼ 失败:', error) |
| | | this.$modal.showToast('ä»»å¡å建æåï¼ä½éä»¶ä¸ä¼ 失败') |
| | | this.navigateToTaskList() |
| | | }) |
| | | }, 1000) |
| | | } else { |
| | | this.$modal.showToast('ä»»å¡å建æå') |
| | | this.navigateToTaskList() |
| | | } |
| | | }).catch(error => { |
| | | this.loading = false |
| | | console.error('ä»»å¡å建失败:', error) |
| | | this.$modal.showToast('ä»»å¡å建失败ï¼è¯·éè¯') |
| | | }) |
| | | }).catch(() => {}) |
| | | }, |
| | | |
| | | // ä¸ä¼ å¾
ä¸ä¼ çéä»¶ï¼OCRå¾çï¼ |
| | | uploadPendingAttachments(taskId) { |
| | | console.log('å¼å§ä¸ä¼ éä»¶ï¼taskId:', taskId, 'éä»¶æ°é:', this.pendingAttachments.length) |
| | | |
| | | // ä½¿ç¨ Promise.all å¹¶åä¸ä¼ ææéä»¶ |
| | | const uploadPromises = this.pendingAttachments.map(attachment => { |
| | | return this.uploadSingleAttachment(taskId, attachment) |
| | | }) |
| | | |
| | | return Promise.all(uploadPromises) |
| | | }, |
| | | |
| | | // ä¸ä¼ å个éä»¶ |
| | | uploadSingleAttachment(taskId, attachment) { |
| | | return new Promise((resolve, reject) => { |
| | | uni.uploadFile({ |
| | | url: config.baseUrl + '/task/attachment/upload/' + taskId, |
| | | filePath: attachment.filePath, |
| | | name: 'file', |
| | | formData: { |
| | | 'category': attachment.category |
| | | }, |
| | | header: { |
| | | 'Authorization': 'Bearer ' + getToken() |
| | | }, |
| | | success: function(uploadRes) { |
| | | if (uploadRes.statusCode === 200) { |
| | | const result = JSON.parse(uploadRes.data) |
| | | if (result.code === 200) { |
| | | console.log('éä»¶ä¸ä¼ æå:', attachment.description) |
| | | resolve(result) |
| | | } else { |
| | | console.error('éä»¶ä¸ä¼ 失败:', attachment.description, result.msg) |
| | | reject(result) |
| | | } |
| | | } else { |
| | | console.error('éä»¶ä¸ä¼ 失败:', attachment.description, uploadRes) |
| | | reject(uploadRes) |
| | | } |
| | | }, |
| | | fail: function(err) { |
| | | console.error('éä»¶ä¸ä¼ 失败:', attachment.description, err) |
| | | reject(err) |
| | | } |
| | | }) |
| | | }) |
| | | }, |
| | | |
| | | // 跳转å°ä»»å¡å表 |
| | | navigateToTaskList() { |
| | | setTimeout(() => { |
| | | // 跳转å°ä»»å¡å表并触åå·æ° |
| | | uni.switchTab({ |
| | | url: '/pages/task/index', |
| | | success: () => { |
| | | // 使ç¨äºä»¶æ»çº¿éç¥ä»»å¡å表页é¢å·æ° |
| | | uni.$emit('refreshTaskList') |
| | | } |
| | | }) |
| | | }, 1000) |
| | | }, |
| | | |
| | | goBack() { |
| | |
| | | findHospitalByName(name, type, restrictRegion = true) { |
| | | if (!name) return Promise.resolve(null) |
| | | const normalized = name.trim() |
| | | |
| | | |
| | | // ç¹æ®å¤ç"å®¶ä¸" |
| | | if (normalized === 'å®¶ä¸') { |
| | | // æ¥è¯¢å»é¢åºä¸ç"å®¶ä¸"è®°å½ |
| | |
| | | const queryPromise = restrictRegion && deptId |
| | | ? searchHospitalsByDeptRegion('å®¶ä¸', deptId, 50) |
| | | : searchHospitals('å®¶ä¸', null, 50) |
| | | |
| | | |
| | | return queryPromise.then(res => { |
| | | const list = res.data || [] |
| | | // æ¥æ¾å称为"å®¶ä¸"çå»é¢è®°å½ |
| | |
| | | } |
| | | }) |
| | | } |
| | | |
| | | // restrictRegion=false æ¶èµ°å
¨éæ¥è¯¢ï¼true 䏿 deptId æ¶èµ°åºåæ¥å£ |
| | | |
| | | // OCRè¯å«åçå»é¢åç§°ï¼ä½¿ç¨æ°çåè¯å¹é
æ¥å£ |
| | | const deptId = this.selectedOrganizationId || null |
| | | const queryPromise = (restrictRegion && deptId) |
| | | ? searchHospitalsByDeptRegion(normalized, deptId, 50) |
| | | : searchHospitals(normalized, null, 50) |
| | | |
| | | return queryPromise.then(res => { |
| | | const list = res.data || [] |
| | | if (!list.length) return null |
| | | |
| | | // èªå¨éæ©ç¬¬ä¸ä¸ªé"å®¶ä¸"çåºé¢ï¼å¦æå
¨æ¯"å®¶ä¸"åé第ä¸ä¸ª |
| | | const best = this.pickBestHospitalMatch(list, normalized) |
| | | return best || null |
| | | |
| | | return searchHospitalsByKeywords(normalized, deptId, 50).then(res => { |
| | | const rawData = res.data || [] |
| | | if (!rawData.length) return null |
| | | |
| | | // æå hospital å¯¹è±¡ï¼æ¥å£è¿åæ ¼å¼ï¼{hospital: {...}, matchScore: ...}ï¼ |
| | | const firstItem = rawData[0] |
| | | const firstHospital = firstItem.hospital || firstItem |
| | | |
| | | console.log(`OCRè¯å«å»é¢"${normalized}"ï¼èªå¨éä¸ï¼${firstHospital.hospName}ï¼å¹é
åæ°ï¼${firstItem.matchScore || 'N/A'}ï¼`) |
| | | return firstHospital |
| | | }).catch(error => { |
| | | console.error(`æç´¢å»é¢"${normalized}"失败:`, error) |
| | | return null |
| | | }) |
| | | }, |
| | | |
| | |
| | | console.error('è·ç¦»è®¡ç®å¤±è´¥:', error) |
| | | this.$modal.showToast('è·ç¦»è®¡ç®å¤±è´¥ï¼è¯·æå¨è¾å
¥') |
| | | }) |
| | | }, |
| | | |
| | | // ==================== æç
§è¯å«ç¸å
³æ¹æ³ ==================== |
| | | |
| | | // æ¾ç¤ºæç
§è¯å«å¼¹çª |
| | | showPhotoOCRPopup() { |
| | | this.ocrImage = '' |
| | | this.$refs.photoOCRPopup.open() |
| | | }, |
| | | |
| | | // æ¾ç¤ºå¤å¾æç
§è¯å«å¼¹çª |
| | | showMultiPhotoOCRPopup() { |
| | | this.page1Image = '' |
| | | this.page2Image = '' |
| | | this.page1Fields = {} |
| | | this.page2Fields = {} |
| | | this.$refs.multiPhotoOCRPopup.open() |
| | | }, |
| | | |
| | | // å
³éå¤å¾æç
§è¯å«å¼¹çª |
| | | closeMultiPhotoOCRPopup() { |
| | | this.$refs.multiPhotoOCRPopup.close() |
| | | }, |
| | | |
| | | // éæ©ç¬¬ä¸é¡µå¾ç |
| | | selectPage1Images() { |
| | | uni.showActionSheet({ |
| | | itemList: ['ä»ç¸åéæ©', 'æç
§'], |
| | | success: (res) => { |
| | | if (res.tapIndex === 0) { |
| | | this.chooseImageForPage(1, 'album') |
| | | } else if (res.tapIndex === 1) { |
| | | this.chooseImageForPage(1, 'camera') |
| | | } |
| | | } |
| | | }) |
| | | }, |
| | | |
| | | // éæ©ç¬¬äºé¡µå¾ç |
| | | selectPage2Images() { |
| | | uni.showActionSheet({ |
| | | itemList: ['ä»ç¸åéæ©', 'æç
§'], |
| | | success: (res) => { |
| | | if (res.tapIndex === 0) { |
| | | this.chooseImageForPage(2, 'album') |
| | | } else if (res.tapIndex === 1) { |
| | | this.chooseImageForPage(2, 'camera') |
| | | } |
| | | } |
| | | }) |
| | | }, |
| | | |
| | | // å é¤ç¬¬ä¸é¡µå¾ç |
| | | deletePage1Image() { |
| | | this.page1Image = '' |
| | | this.page1Fields = {} |
| | | }, |
| | | |
| | | // å é¤ç¬¬äºé¡µå¾ç |
| | | deletePage2Image() { |
| | | this.page2Image = '' |
| | | this.page2Fields = {} |
| | | }, |
| | | |
| | | // 为æå®é¡µç éæ©å¾çï¼åå¾ï¼ |
| | | chooseImageForPage(page, sourceType) { |
| | | uni.chooseImage({ |
| | | count: 1, // åªéæ©ä¸å¼ å¾ç |
| | | sizeType: ['compressed'], |
| | | sourceType: [sourceType], |
| | | success: (res) => { |
| | | const imagePath = res.tempFilePaths[0] |
| | | |
| | | if (page === 1) { |
| | | this.page1Image = imagePath |
| | | } else { |
| | | this.page2Image = imagePath |
| | | } |
| | | |
| | | // éæ©å®å¾çåï¼ç«å³è¿è¡OCRè¯å« |
| | | this.$modal.showToast(`已鿩å¾çï¼å¼å§è¯å«...`) |
| | | this.recognizeSinglePage(page) |
| | | }, |
| | | fail: (err) => { |
| | | console.error('éæ©å¾ç失败:', err) |
| | | this.$modal.showToast('éæ©å¾ç失败') |
| | | } |
| | | }) |
| | | }, |
| | | |
| | | // éæ©å¾ç |
| | | selectImage() { |
| | | uni.chooseImage({ |
| | | count: 1, |
| | | sizeType: ['compressed'], |
| | | sourceType: ['album'], |
| | | success: (res) => { |
| | | this.ocrImage = res.tempFilePaths[0] |
| | | }, |
| | | fail: (err) => { |
| | | console.error('éæ©å¾ç失败:', err) |
| | | this.$modal.showToast('éæ©å¾ç失败') |
| | | } |
| | | }) |
| | | }, |
| | | |
| | | // æç
§ |
| | | captureImage() { |
| | | uni.chooseImage({ |
| | | count: 1, |
| | | sizeType: ['compressed'], |
| | | sourceType: ['camera'], |
| | | success: (res) => { |
| | | this.ocrImage = res.tempFilePaths[0] |
| | | }, |
| | | fail: (err) => { |
| | | console.error('æç
§å¤±è´¥:', err) |
| | | this.$modal.showToast('æç
§å¤±è´¥') |
| | | } |
| | | }) |
| | | }, |
| | | |
| | | // åºç¨OCRè¯å«ç»æå°è¡¨å |
| | | applyOcrResult() { |
| | | // å并两页çè¯å«ç»æ |
| | | const mergedFields = { ...this.page1Fields, ...this.page2Fields } |
| | | |
| | | // å¤çåå¹¶åçè¯å«ç»æ |
| | | this.processMultiOCRResult(mergedFields) |
| | | |
| | | // ä¿åOCRå¾çå°å¾
ä¸ä¼ éä»¶å表ï¼ç¥æ
åæä¹¦ï¼å类为'1'ï¼ |
| | | this.pendingAttachments = [] |
| | | if (this.page1Image) { |
| | | this.pendingAttachments.push({ |
| | | filePath: this.page1Image, |
| | | category: '1', // ç¥æ
åæä¹¦ |
| | | description: 'ç¥æ
åæä¹¦ç¬¬ä¸é¡µ' |
| | | }) |
| | | } |
| | | if (this.page2Image) { |
| | | this.pendingAttachments.push({ |
| | | filePath: this.page2Image, |
| | | category: '1', // ç¥æ
åæä¹¦ |
| | | description: 'ç¥æ
åæä¹¦ç¬¬äºé¡µ' |
| | | }) |
| | | } |
| | | |
| | | console.log('ä¿åå¾
ä¸ä¼ éä»¶:', this.pendingAttachments) |
| | | |
| | | // å
³éå¼¹çª |
| | | this.closeMultiPhotoOCRPopup() |
| | | |
| | | // æ¸
空å¾çåç»æï¼ä½ä¿ç pendingAttachmentsï¼ |
| | | this.page1Image = '' |
| | | this.page2Image = '' |
| | | this.page1Fields = {} |
| | | this.page2Fields = {} |
| | | |
| | | this.$modal.showToast('å·²åºç¨å°è¡¨åï¼ç¥æ
åæä¹¦å°å¨ä»»å¡å建åä¸ä¼ ') |
| | | }, |
| | | |
| | | // è¯å«å个页ç çå¾çï¼éæ©åç«å³è¯å«ï¼ |
| | | recognizeSinglePage(page) { |
| | | const image = page === 1 ? this.page1Image : this.page2Image |
| | | |
| | | if (!image) { |
| | | return |
| | | } |
| | | |
| | | // æ¾ç¤ºå è½½æç¤º |
| | | uni.showLoading({ |
| | | title: `è¯å«ç¬¬${page}页...` |
| | | }) |
| | | |
| | | // 第ä¸é¡µçitemNames |
| | | const page1ItemNames = [ |
| | | "æ£è
å§å", "æ§å«", "å¹´é¾", "身份è¯å·", "è¯æ", |
| | | "鿝ä»è½¬è¿è´¹ç¨", "è¡ç¨", "å¼å§æ¶é´", "ç»ææ¶é´", "å®¶å±ç¾å" |
| | | ] |
| | | |
| | | // 第äºé¡µçitemNames |
| | | const page2ItemNames = [ |
| | | "æ£è
ç¾åï¼æå°ï¼", "ç¾å人身份è¯å·ç ", "ç¾å人身份è¯å·", "æ¥æ", |
| | | "èç³»çµè¯", "æ¬äºº", "ç¾åäººä¸æ£è
å
³ç³»" |
| | | ] |
| | | |
| | | const itemNames = page === 1 ? page1ItemNames : page2ItemNames |
| | | |
| | | // è°ç¨æ¹éOCR APIï¼ä¼ å
¥å个å¾çï¼ |
| | | batchRecognizeImages([image], itemNames) |
| | | .then(result => { |
| | | uni.hideLoading() |
| | | |
| | | if (result.success && result.successCount > 0) { |
| | | // ä¿åè¯å«ç»æ |
| | | if (page === 1) { |
| | | this.page1Fields = result.fields |
| | | } else { |
| | | this.page2Fields = result.fields |
| | | } |
| | | |
| | | console.log(`第${page}页è¯å«ç»æ:`, result.fields) |
| | | this.$modal.showToast(`第${page}页è¯å«æå`) |
| | | } else { |
| | | this.$modal.showToast(`第${page}页è¯å«å¤±è´¥`) |
| | | } |
| | | }) |
| | | .catch(error => { |
| | | uni.hideLoading() |
| | | console.error(`第${page}页OCRè¯å«å¤±è´¥:`, error) |
| | | this.$modal.showToast(error.msg || `第${page}页è¯å«å¤±è´¥`) |
| | | }) |
| | | }, |
| | | |
| | | // å¤çå¤å¾OCRè¯å«ç»æ |
| | | processMultiOCRResult(fields) { |
| | | console.log('å¤å¾OCRè¯å«ç»æ:', fields) |
| | | |
| | | // æåæ£è
å§å |
| | | if (fields['æ£è
å§å']) { |
| | | this.taskForm.patient.name = fields['æ£è
å§å'].trim() |
| | | } |
| | | |
| | | // æåè系人ï¼ä¼å
æ£è
ç¾åï¼å
¶æ¬¡å®¶å±ç¾åï¼æåæ¬äººï¼ |
| | | if (fields['æ£è
ç¾åï¼æå°ï¼']) { |
| | | this.taskForm.patient.contact = fields['æ£è
ç¾åï¼æå°ï¼'].trim() |
| | | } else if (fields['å®¶å±ç¾å']) { |
| | | this.taskForm.patient.contact = fields['å®¶å±ç¾å'].trim() |
| | | } else if (fields['æ¬äºº']) { |
| | | this.taskForm.patient.contact = fields['æ¬äºº'].trim() |
| | | } |
| | | |
| | | // æåæ§å« |
| | | if (fields['æ§å«']) { |
| | | const gender = fields['æ§å«'].trim() |
| | | if (gender.includes('ç·')) { |
| | | this.taskForm.patient.gender = 'male' |
| | | } else if (gender.includes('女')) { |
| | | this.taskForm.patient.gender = 'female' |
| | | } |
| | | } |
| | | |
| | | // æå身份è¯å·ï¼ä¼å
身份è¯å·ï¼å
¶æ¬¡ç¾å人身份è¯å·ç ï¼ |
| | | const patientIdCard = fields['身份è¯å·'] || fields['æ£è
身份è¯å·'] |
| | | const signerIdCard = fields['ç¾å人身份è¯å·ç '] || fields['ç¾å人身份è¯å·'] |
| | | |
| | | if (patientIdCard) { |
| | | this.taskForm.patient.idCard = patientIdCard.trim() |
| | | } else if (signerIdCard) { |
| | | this.taskForm.patient.idCard = signerIdCard.trim() |
| | | } |
| | | |
| | | // æåæ¥æï¼è½¬è¿æ¶é´ï¼ |
| | | if (fields['æ¥æ']) { |
| | | const dateString = fields['æ¥æ'].trim() |
| | | const dateFormatted = this.formatDateString(dateString) |
| | | if (dateFormatted) { |
| | | this.taskForm.transferTime = dateFormatted |
| | | } |
| | | } |
| | | |
| | | // æåèç³»çµè¯ |
| | | if (fields['èç³»çµè¯']) { |
| | | this.taskForm.patient.phone = fields['èç³»çµè¯'].trim() |
| | | } |
| | | |
| | | // æå鿝ä»è½¬è¿è´¹ç¨ï¼æäº¤ä»·ï¼ |
| | | if (fields['鿝ä»è½¬è¿è´¹ç¨']) { |
| | | const priceText = fields['鿝ä»è½¬è¿è´¹ç¨'].trim() |
| | | const priceNumber = priceText.match(/\d+(?:\.\d{1,2})?/) |
| | | if (priceNumber) { |
| | | this.taskForm.price = parseFloat(priceNumber[0]).toFixed(2) |
| | | } |
| | | } |
| | | |
| | | // æåè¯æï¼ç
æ
ï¼ |
| | | if (fields['è¯æ']) { |
| | | this.taskForm.patient.condition = fields['è¯æ'].trim() |
| | | } |
| | | |
| | | // æåè¡ç¨ï¼è½¬åºå»é¢å转å
¥å»é¢ï¼ |
| | | if (fields['è¡ç¨']) { |
| | | const route = fields['è¡ç¨'].trim() |
| | | // æ"-"æ"â"åå²è¡ç¨ |
| | | const hospitals = route.split(/[-â]/).map(h => h.trim()) |
| | | if (hospitals.length >= 2) { |
| | | // 第ä¸ä¸ªæ¯è½¬åºå»é¢ |
| | | this.taskForm.hospitalOut.name = hospitals[0] |
| | | // 第äºä¸ªæ¯è½¬å
¥å»é¢ |
| | | this.taskForm.hospitalIn.name = hospitals[1] |
| | | |
| | | // å°è¯ä»å»é¢åºä¸å¹é
å¹¶è¡¥å
¨å°å |
| | | Promise.all([ |
| | | this.findHospitalByName(hospitals[0], 'out', false), |
| | | this.findHospitalByName(hospitals[1], 'in', false) |
| | | ]).then(([outHosp, inHosp]) => { |
| | | if (outHosp) { |
| | | this.taskForm.hospitalOut.id = outHosp.hospId |
| | | this.taskForm.hospitalOut.name = outHosp.hospName |
| | | if (outHosp.hospName !== 'å®¶ä¸') { |
| | | this.taskForm.hospitalOut.address = this.buildFullAddress(outHosp) |
| | | this.taskForm.hospitalOut.city = outHosp.hopsCity || '' |
| | | } |
| | | } |
| | | if (inHosp) { |
| | | this.taskForm.hospitalIn.id = inHosp.hospId |
| | | this.taskForm.hospitalIn.name = inHosp.hospName |
| | | if (inHosp.hospName !== 'å®¶ä¸') { |
| | | this.taskForm.hospitalIn.address = this.buildFullAddress(inHosp) |
| | | this.taskForm.hospitalIn.city = inHosp.hopsCity || '' |
| | | } |
| | | } |
| | | |
| | | // å¦æä¸¤ä¸ªå»é¢å°å齿ï¼èªå¨è®¡ç®è·ç¦» |
| | | if (this.taskForm.hospitalOut.address && this.taskForm.hospitalIn.address && |
| | | this.taskForm.hospitalOut.name !== 'å®¶ä¸' && this.taskForm.hospitalIn.name !== 'å®¶ä¸') { |
| | | this.calculateHospitalDistance() |
| | | } |
| | | }) |
| | | } |
| | | } |
| | | |
| | | console.log('å¤å¾OCRç»æå¤ç宿ï¼è¡¨åæ°æ®æ´æ°') |
| | | }, |
| | | |
| | | // æ§è¡OCRè¯å« |
| | | performOCR() { |
| | | if (!this.ocrImage) { |
| | | this.$modal.showToast('请å
éæ©æææå¾ç') |
| | | return |
| | | } |
| | | |
| | | this.ocrLoading = true |
| | | |
| | | // 使ç¨OCR APIè¿è¡è¯å« |
| | | recognizeImage(this.ocrImage, 'HandWriting', 'tencent', DEFAULT_TRANSFER_ITEM_NAMES) |
| | | .then(response => { |
| | | const ocrResult = response.data.ocrResult |
| | | this.processOCRResult(ocrResult) |
| | | this.$modal.showToast('OCRè¯å«æå') |
| | | }) |
| | | .catch(error => { |
| | | console.error('OCRè¯å«å¤±è´¥:', error) |
| | | this.$modal.showToast(`OCRè¯å«å¤±è´¥: ${error.msg || 'æªç¥é误'}`) |
| | | }) |
| | | .finally(() => { |
| | | this.ocrLoading = false |
| | | this.closePhotoOCRPopup() |
| | | }) |
| | | }, |
| | | |
| | | // å¤çOCRè¯å«ç»æ |
| | | processOCRResult(ocrResult) { |
| | | if (!ocrResult || !ocrResult.content) { |
| | | console.log('OCRè¯å«ç»æä¸ºç©º') |
| | | return |
| | | } |
| | | |
| | | const content = ocrResult.content |
| | | console.log('OCRè¯å«å
容:', content) |
| | | |
| | | // æåæ£è
å§å |
| | | const patientNameMatch = content.match(/æ£è
å§å[ï¼:]?\s*([^\nï¼,ãï¼;]+)/) |
| | | if (patientNameMatch && patientNameMatch[1]) { |
| | | this.taskForm.patient.name = patientNameMatch[1].trim() |
| | | } |
| | | |
| | | // æåæ§å« |
| | | const genderMatch = content.match(/æ§å«[ï¼:]?\s*([^\nï¼,ãï¼;]+)/) |
| | | if (genderMatch && genderMatch[1]) { |
| | | const gender = genderMatch[1].trim() |
| | | if (gender.includes('ç·')) { |
| | | this.taskForm.patient.gender = 'male' |
| | | } else if (gender.includes('女')) { |
| | | this.taskForm.patient.gender = 'female' |
| | | } |
| | | } |
| | | |
| | | // æå身份è¯å· |
| | | const idCardMatch = content.match(/身份è¯å·[ï¼:]?\s*([^\nï¼,ãï¼;]+)/) |
| | | const signerIdMatch = content.match(/ç¾å人身份è¯å·ç [ï¼:]?\s*([^\nï¼,ãï¼;]+)/) |
| | | if (idCardMatch && idCardMatch[1]) { |
| | | this.taskForm.patient.idCard = idCardMatch[1].trim() |
| | | } else if (signerIdMatch && signerIdMatch[1]) { |
| | | this.taskForm.patient.idCard = signerIdMatch[1].trim() |
| | | } |
| | | |
| | | // æåèç³»çµè¯ |
| | | const phoneMatch = content.match(/èç³»çµè¯[ï¼:]?\s*([^\nï¼,ãï¼;]+)/) |
| | | if (phoneMatch && phoneMatch[1]) { |
| | | this.taskForm.patient.phone = phoneMatch[1].trim() |
| | | } |
| | | |
| | | // æåè¯æä¿¡æ¯ |
| | | const diagnosisMatch = content.match(/è¯æ[ï¼:]?\s*([^\nï¼,ãï¼;]+)/) |
| | | if (diagnosisMatch && diagnosisMatch[1]) { |
| | | this.taskForm.patient.condition = diagnosisMatch[1].trim() |
| | | } |
| | | |
| | | // æå鿝ä»è½¬è¿è´¹ç¨ï¼æäº¤ä»·ï¼ |
| | | const priceMatch = content.match(/鿝ä»è½¬è¿è´¹ç¨[ï¼:]?\s*([^\nï¼,ãï¼;]+)/) |
| | | if (priceMatch && priceMatch[1]) { |
| | | // æåæ°åéé¢ |
| | | const priceNumber = priceMatch[1].match(/\d+(?:\.\d{1,2})?/) |
| | | if (priceNumber) { |
| | | this.taskForm.price = parseFloat(priceNumber[0]).toFixed(2) |
| | | } |
| | | } |
| | | |
| | | // æåæ¥æ |
| | | const dateMatch = content.match(/æ¥æ[ï¼:]?\s*([^\nï¼,ãï¼;]+)/) |
| | | if (dateMatch && dateMatch[1]) { |
| | | const dateString = dateMatch[1].trim() |
| | | // å°è¯è§£ææ¥ææ ¼å¼ |
| | | const dateFormatted = this.formatDateString(dateString) |
| | | if (dateFormatted) { |
| | | this.taskForm.transferTime = dateFormatted |
| | | } |
| | | } |
| | | |
| | | // æåè¡ç¨ï¼è½¬åºå»é¢å转å
¥å»é¢ï¼ |
| | | const routeMatch = content.match(/è¡ç¨[ï¼:]?\s*([^\n]+)/) |
| | | if (routeMatch && routeMatch[1]) { |
| | | const route = routeMatch[1].trim() |
| | | // æ"-"åå²è¡ç¨ï¼è·å转åºå转å
¥å»é¢ |
| | | const hospitals = route.split(/[-â]/).map(h => h.trim()) |
| | | if (hospitals.length >= 2) { |
| | | // 第ä¸ä¸ªæ¯è½¬åºå»é¢ |
| | | this.taskForm.hospitalOut.name = hospitals[0] |
| | | // 第äºä¸ªæ¯è½¬å
¥å»é¢ |
| | | this.taskForm.hospitalIn.name = hospitals[1] |
| | | } |
| | | } |
| | | |
| | | // æåå®¶å±ç¾åææ¬äººä½ä¸ºè系人 |
| | | const familyContactMatch = content.match(/(?:å®¶å±ç¾å|æ¬äºº)[ï¼:]?\s*([^\nï¼,ãï¼;]+)/) |
| | | if (familyContactMatch && familyContactMatch[1]) { |
| | | this.taskForm.patient.contact = familyContactMatch[1].trim() |
| | | } |
| | | |
| | | // æåæ£è
ç¾åï¼æå°ï¼ä½ä¸ºè系人 |
| | | const patientSignatureMatch = content.match(/æ£è
ç¾åï¼æå°ï¼[ï¼:]?\s*([^\nï¼,ãï¼;]+)/) |
| | | if (patientSignatureMatch && patientSignatureMatch[1]) { |
| | | if (!this.taskForm.patient.contact) { |
| | | this.taskForm.patient.contact = patientSignatureMatch[1].trim() |
| | | } |
| | | } |
| | | |
| | | console.log('OCRç»æå¤ç宿ï¼è¡¨åæ°æ®æ´æ°') |
| | | }, |
| | | |
| | | // æ ¼å¼åæ¥æå符串ï¼è¿å yyyy-MM-dd HH:mm:ss æ ¼å¼ï¼ |
| | | formatDateString(dateStr) { |
| | | // å°è¯ä¸åçæ¥ææ ¼å¼ |
| | | let cleaned = dateStr.replace(/[å¹´æ]/g, '-').replace(/[æ¥å·]/g, '') |
| | | |
| | | let dateResult = '' |
| | | |
| | | // 妿æ¯YYMMDDæ ¼å¼ |
| | | if (/^\d{6}$/.test(cleaned)) { |
| | | const year = '20' + cleaned.substring(0, 2) |
| | | const month = cleaned.substring(2, 4) |
| | | const day = cleaned.substring(4, 6) |
| | | dateResult = `${year}-${month}-${day}` |
| | | } |
| | | // 妿æ¯YYYYMMDDæ ¼å¼ |
| | | else if (/^\d{8}$/.test(cleaned)) { |
| | | const year = cleaned.substring(0, 4) |
| | | const month = cleaned.substring(4, 6) |
| | | const day = cleaned.substring(6, 8) |
| | | dateResult = `${year}-${month}-${day}` |
| | | } |
| | | // å¦æå·²ç»æ¯åçæ ¼å¼ï¼ç´æ¥ä½¿ç¨ |
| | | else if (cleaned.match(/^\d{4}[-/]\d{1,2}[-/]\d{1,2}$/)) { |
| | | dateResult = cleaned.replace(/[//]/g, '-') |
| | | } |
| | | // 妿已ç»å
嫿¶åç§ï¼ç´æ¥è¿å |
| | | else if (cleaned.match(/^\d{4}[-/]\d{1,2}[-/]\d{1,2}\s+\d{1,2}:\d{1,2}:\d{1,2}$/)) { |
| | | return cleaned.replace(/[//]/g, '-') |
| | | } |
| | | else { |
| | | dateResult = dateStr |
| | | } |
| | | |
| | | // å¦ææ¥ææ ¼å¼æ£ç¡®ï¼æ·»å é»è®¤æ¶åç§ 00:00:00 |
| | | if (dateResult && dateResult.match(/^\d{4}-\d{1,2}-\d{1,2}$/)) { |
| | | return dateResult + ' 00:00:00' |
| | | } |
| | | |
| | | return dateResult |
| | | } |
| | | } |
| | | } |
| | |
| | | color: #333; |
| | | } |
| | | |
| | | .smart-parse-btn { |
| | | .smart-parse-btn, |
| | | .ocr-page-btn { |
| | | position: relative; |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | |
| | | |
| | | text { |
| | | font-size: 22rpx; |
| | | margin-top: 4rpx; |
| | | } |
| | | |
| | | .badge { |
| | | position: absolute; |
| | | top: 0; |
| | | right: 10rpx; |
| | | min-width: 32rpx; |
| | | height: 32rpx; |
| | | line-height: 32rpx; |
| | | text-align: center; |
| | | background-color: #ff4d4f; |
| | | color: white; |
| | | font-size: 20rpx; |
| | | border-radius: 16rpx; |
| | | padding: 0 8rpx; |
| | | } |
| | | } |
| | | |
| | | .smart-parse-btn { |
| | | text { |
| | | color: #007AFF; |
| | | } |
| | | } |
| | | |
| | | .ocr-page-btn:first-of-type { |
| | | text { |
| | | color: #52c41a; |
| | | } |
| | | } |
| | | |
| | | .ocr-page-btn:last-of-type { |
| | | text { |
| | | color: #FF6B00; |
| | | } |
| | | } |
| | | |
| | | .multi-photo-ocr-btn { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | padding: 10rpx 20rpx; |
| | | |
| | | text { |
| | | font-size: 22rpx; |
| | | color: #FF6B00; |
| | | margin-top: 4rpx; |
| | | } |
| | | } |
| | |
| | | } |
| | | } |
| | | } |
| | | |
| | | // æç
§è¯å«å¼¹çªæ ·å¼ |
| | | .photo-ocr-popup { |
| | | background-color: white; |
| | | border-radius: 20rpx 20rpx 0 0; |
| | | max-height: 80vh; |
| | | display: flex; |
| | | flex-direction: column; |
| | | |
| | | .popup-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | padding: 30rpx; |
| | | border-bottom: 1rpx solid #f0f0f0; |
| | | flex-shrink: 0; |
| | | |
| | | .popup-title { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 10rpx; |
| | | font-size: 32rpx; |
| | | font-weight: bold; |
| | | color: #333; |
| | | |
| | | text { |
| | | margin-left: 8rpx; |
| | | } |
| | | } |
| | | |
| | | .popup-close { |
| | | padding: 10rpx; |
| | | } |
| | | } |
| | | |
| | | .ocr-content { |
| | | flex: 1; |
| | | padding: 30rpx; |
| | | overflow-y: auto; |
| | | |
| | | .ocr-tip { |
| | | display: flex; |
| | | align-items: flex-start; |
| | | padding: 20rpx; |
| | | background-color: #f0f7ff; |
| | | border-radius: 10rpx; |
| | | margin-bottom: 20rpx; |
| | | |
| | | text { |
| | | flex: 1; |
| | | margin-left: 10rpx; |
| | | font-size: 24rpx; |
| | | color: #666; |
| | | line-height: 1.6; |
| | | } |
| | | } |
| | | |
| | | .image-preview { |
| | | margin-bottom: 20rpx; |
| | | border: 1rpx solid #eee; |
| | | border-radius: 10rpx; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .multi-image-preview { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | gap: 15rpx; |
| | | margin-bottom: 20rpx; |
| | | |
| | | .image-item { |
| | | position: relative; |
| | | width: 200rpx; |
| | | height: 200rpx; |
| | | border: 1rpx solid #eee; |
| | | border-radius: 10rpx; |
| | | overflow: hidden; |
| | | |
| | | image { |
| | | width: 100%; |
| | | height: 100%; |
| | | } |
| | | |
| | | .delete-btn { |
| | | position: absolute; |
| | | top: 5rpx; |
| | | right: 5rpx; |
| | | width: 40rpx; |
| | | height: 40rpx; |
| | | background-color: rgba(0, 0, 0, 0.6); |
| | | border-radius: 50%; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .ocr-actions { |
| | | display: flex; |
| | | gap: 20rpx; |
| | | |
| | | button { |
| | | flex: 1; |
| | | height: 80rpx; |
| | | border-radius: 10rpx; |
| | | font-size: 28rpx; |
| | | } |
| | | |
| | | .select-btn { |
| | | background-color: #f5f5f5; |
| | | color: #333; |
| | | } |
| | | |
| | | .capture-btn { |
| | | background-color: #007AFF; |
| | | color: white; |
| | | } |
| | | } |
| | | |
| | | .image-count { |
| | | text-align: center; |
| | | margin-top: 20rpx; |
| | | font-size: 26rpx; |
| | | color: #FF6B00; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | // 页é¢ä¸ä¼ åºå |
| | | .page-upload-section { |
| | | background: linear-gradient(135deg, #f9f9f9 0%, #ffffff 100%); |
| | | border-radius: 15rpx; |
| | | padding: 25rpx; |
| | | margin-bottom: 25rpx; |
| | | border: 2rpx solid #f0f0f0; |
| | | transition: all 0.3s; |
| | | |
| | | &.first-page { |
| | | border-left: 4rpx solid #52c41a; |
| | | |
| | | .page-badge { |
| | | background: linear-gradient(135deg, #52c41a 0%, #73d13d 100%); |
| | | |
| | | .page-number { |
| | | color: #52c41a; |
| | | } |
| | | } |
| | | } |
| | | |
| | | &.second-page { |
| | | border-left: 4rpx solid #FF6B00; |
| | | |
| | | .page-badge { |
| | | background: linear-gradient(135deg, #FF6B00 0%, #ff8c3a 100%); |
| | | |
| | | .page-number { |
| | | color: #FF6B00; |
| | | } |
| | | } |
| | | } |
| | | |
| | | &:last-child { |
| | | margin-bottom: 0; |
| | | } |
| | | |
| | | .page-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 20rpx; |
| | | |
| | | .page-title { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 15rpx; |
| | | flex: 1; |
| | | |
| | | .page-badge { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | width: 60rpx; |
| | | height: 60rpx; |
| | | background: linear-gradient(135deg, #52c41a 0%, #73d13d 100%); |
| | | border-radius: 50%; |
| | | position: relative; |
| | | |
| | | .page-number { |
| | | position: absolute; |
| | | bottom: -2rpx; |
| | | right: -2rpx; |
| | | width: 24rpx; |
| | | height: 24rpx; |
| | | background-color: #fff; |
| | | border-radius: 50%; |
| | | font-size: 16rpx; |
| | | font-weight: bold; |
| | | color: #52c41a; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1); |
| | | } |
| | | } |
| | | |
| | | .page-info { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 4rpx; |
| | | |
| | | .page-main-title { |
| | | font-size: 28rpx; |
| | | font-weight: bold; |
| | | color: #333; |
| | | } |
| | | |
| | | .page-sub-title { |
| | | font-size: 22rpx; |
| | | color: #999; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .upload-btn { |
| | | width: 60rpx; |
| | | height: 60rpx; |
| | | border-radius: 50%; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | transition: all 0.3s; |
| | | box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1); |
| | | |
| | | &.green { |
| | | border: 2rpx dashed #52c41a; |
| | | background-color: rgba(82, 196, 26, 0.05); |
| | | } |
| | | |
| | | &.orange { |
| | | border: 2rpx dashed #FF6B00; |
| | | background-color: rgba(255, 107, 0, 0.05); |
| | | } |
| | | |
| | | &:active { |
| | | transform: scale(0.95); |
| | | opacity: 0.8; |
| | | } |
| | | } |
| | | } |
| | | |
| | | // è¯å«å段æç¤º |
| | | .field-hint { |
| | | background-color: #fffbe6; |
| | | border: 1rpx solid #ffe58f; |
| | | border-radius: 8rpx; |
| | | padding: 15rpx; |
| | | margin-bottom: 15rpx; |
| | | |
| | | text { |
| | | font-size: 22rpx; |
| | | color: #d48806; |
| | | line-height: 1.6; |
| | | } |
| | | } |
| | | |
| | | .images-container { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | gap: 10rpx; |
| | | margin-bottom: 15rpx; |
| | | |
| | | .image-item { |
| | | position: relative; |
| | | width: 150rpx; |
| | | height: 150rpx; |
| | | border: 1rpx solid #eee; |
| | | border-radius: 10rpx; |
| | | overflow: hidden; |
| | | |
| | | image { |
| | | width: 100%; |
| | | height: 100%; |
| | | } |
| | | |
| | | .delete-btn { |
| | | position: absolute; |
| | | top: 5rpx; |
| | | right: 5rpx; |
| | | width: 35rpx; |
| | | height: 35rpx; |
| | | background-color: rgba(0, 0, 0, 0.6); |
| | | border-radius: 50%; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | } |
| | | } |
| | | |
| | | // åå¾é¢è§åºå |
| | | .single-image-container { |
| | | position: relative; |
| | | width: 100%; |
| | | height: 400rpx; |
| | | border: 1rpx solid #eee; |
| | | border-radius: 10rpx; |
| | | overflow: hidden; |
| | | margin-bottom: 15rpx; |
| | | background-color: #f5f5f5; |
| | | |
| | | .preview-image { |
| | | width: 100%; |
| | | height: 100%; |
| | | } |
| | | |
| | | .delete-btn { |
| | | position: absolute; |
| | | top: 10rpx; |
| | | right: 10rpx; |
| | | width: 50rpx; |
| | | height: 50rpx; |
| | | background-color: rgba(0, 0, 0, 0.6); |
| | | border-radius: 50%; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | z-index: 2; |
| | | |
| | | &:active { |
| | | transform: scale(0.95); |
| | | opacity: 0.9; |
| | | } |
| | | } |
| | | |
| | | // å¾çç¶ææ ç¾ |
| | | .image-status { |
| | | position: absolute; |
| | | bottom: 10rpx; |
| | | left: 10rpx; |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 5rpx; |
| | | padding: 8rpx 15rpx; |
| | | background-color: rgba(82, 196, 26, 0.9); |
| | | border-radius: 20rpx; |
| | | z-index: 2; |
| | | |
| | | text { |
| | | font-size: 22rpx; |
| | | color: #fff; |
| | | font-weight: 500; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .recognition-result { |
| | | background-color: white; |
| | | border-radius: 10rpx; |
| | | padding: 15rpx; |
| | | border: 1rpx solid #e0e0e0; |
| | | |
| | | .result-title { |
| | | display: flex; |
| | | align-items: center; |
| | | margin-bottom: 15rpx; |
| | | padding-bottom: 10rpx; |
| | | border-bottom: 1rpx solid #f0f0f0; |
| | | |
| | | text { |
| | | margin-left: 8rpx; |
| | | font-size: 26rpx; |
| | | font-weight: bold; |
| | | color: #333; |
| | | } |
| | | |
| | | .result-count { |
| | | margin-left: 8rpx; |
| | | font-size: 22rpx; |
| | | color: #999; |
| | | font-weight: normal; |
| | | } |
| | | } |
| | | |
| | | .field-list { |
| | | .field-item { |
| | | display: flex; |
| | | padding: 10rpx 0; |
| | | border-bottom: 1rpx solid #f9f9f9; |
| | | |
| | | &:last-child { |
| | | border-bottom: none; |
| | | } |
| | | |
| | | .field-name { |
| | | min-width: 150rpx; |
| | | font-size: 24rpx; |
| | | color: #666; |
| | | font-weight: 500; |
| | | |
| | | &::after { |
| | | content: ':'; |
| | | margin-left: 4rpx; |
| | | } |
| | | } |
| | | |
| | | .field-value { |
| | | flex: 1; |
| | | font-size: 24rpx; |
| | | color: #333; |
| | | word-break: break-all; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .page-indicator { |
| | | display: flex; |
| | | justify-content: center; |
| | | gap: 30rpx; |
| | | margin-top: 30rpx; |
| | | |
| | | .page-dot { |
| | | padding: 15rpx 30rpx; |
| | | border: 2rpx solid #ddd; |
| | | border-radius: 30rpx; |
| | | font-size: 26rpx; |
| | | color: #999; |
| | | background-color: #f5f5f5; |
| | | transition: all 0.3s; |
| | | |
| | | &.active { |
| | | border-color: #007AFF; |
| | | background-color: #007AFF; |
| | | color: white; |
| | | } |
| | | |
| | | &.completed { |
| | | border-color: #52c41a; |
| | | color: #52c41a; |
| | | |
| | | &.active { |
| | | background-color: #007AFF; |
| | | border-color: #007AFF; |
| | | color: white; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .popup-footer { |
| | | display: flex; |
| | | padding: 20rpx 30rpx; |
| | | border-top: 1rpx solid #f0f0f0; |
| | | gap: 20rpx; |
| | | flex-shrink: 0; |
| | | flex-wrap: wrap; |
| | | |
| | | button { |
| | | flex: 1; |
| | | min-width: 160rpx; |
| | | height: 80rpx; |
| | | border-radius: 10rpx; |
| | | font-size: 30rpx; |
| | | } |
| | | |
| | | .cancel-btn { |
| | | background-color: #f5f5f5; |
| | | color: #666; |
| | | } |
| | | |
| | | .next-btn, |
| | | .prev-btn { |
| | | background-color: #52c41a; |
| | | color: white; |
| | | } |
| | | |
| | | .confirm-btn { |
| | | background-color: #007AFF; |
| | | color: white; |
| | | |
| | | &[disabled] { |
| | | background-color: #ccc; |
| | | color: #999; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </style> |
| | |
| | | </button> |
| | | </template> |
| | | |
| | | <!-- åºåä¸ç¶æ: æ¾ç¤ºå·²å°è¾¾ã强å¶ç»æ --> |
| | | <!-- åºåä¸ç¶æ: æ¾ç¤ºå·²å°è¾¾ã强å¶ç»æã强å¶å®æ --> |
| | | <template v-else-if="taskDetail.taskStatus === 'DEPARTING'"> |
| | | <template v-if="canOperateTask()"> |
| | | <button |
| | |
| | | @click="handleTaskAction('forceCancel')" |
| | | > |
| | | 强å¶ç»æ |
| | | </button> |
| | | <button |
| | | v-if="showForceCompleteFeature()" |
| | | class="action-btn force-complete" |
| | | @click="showForceCompleteTimeDialog()" |
| | | > |
| | | 强å¶å®æ |
| | | </button> |
| | | </template> |
| | | </template> |
| | |
| | | flex: 1; |
| | | height: 80rpx; |
| | | border-radius: 10rpx; |
| | | font-size: 30rpx; |
| | | font-size: 28rpx; |
| | | margin: 0 10rpx; |
| | | background-color: #f0f0f0; |
| | | color: #333; |
| | | white-space: nowrap; |
| | | padding: 0 10rpx; |
| | | min-width: 0; |
| | | |
| | | &.edit { |
| | | background-color: #ff9500; |
| | |
| | | color: white; |
| | | } |
| | | |
| | | &.force-end { |
| | | background-color: #ff6b22; |
| | | color: white; |
| | | } |
| | | |
| | | &.settlement { |
| | | background-color: #34C759; |
| | | color: white; |
| New file |
| | |
| | | # 车è¾å¼å¸¸è¿è¡çæ§åè¦ç³»ç» |
| | | |
| | | ## ð¯ åè½æ¦è¿° |
| | | |
| | | æ¬ç³»ç»å®ç°äºå®æ´ç车è¾å¼å¸¸è¿è¡çæ§åè¦åè½ï¼ç¨äºçæ§æ ä»»å¡ç¶æä¸è½¦è¾çå¼å¸¸è¿è¡æ
åµï¼å¹¶éè¿ä¼ä¸å¾®ä¿¡/å°ç¨åºåæ¶åè¦éç¥ç¸å
³è´è´£äººã |
| | | |
| | | ## â¨ æ ¸å¿ç¹æ§ |
| | | |
| | | - â
**æºè½çæ§**: 宿¶çæ§ææè½¦è¾è¿è¡ç¶æï¼åºäºGPSåæ®µéç¨ç²¾åè®¡ç® |
| | | - â
**çµæ´»é
ç½®**: æ¯æå
¨å±/é¨é¨/车è¾ä¸çº§é
ç½®çç¥ï¼é
ç½®ä¼å
级èªå¨åºç¨ |
| | | - â
**é¢çæ§å¶**: æ¯æ¥åè¦æ¬¡æ°éå¶ + åè¦é´éæ¶é´æ§å¶ï¼é¿å
é¢ç¹éªæ° |
| | | - â
**åæ¶éç¥**: ä¼ä¸å¾®ä¿¡æ¶æ¯æ¨éï¼æ¯æå¯é
ç½®éç¥ç¨æ·å表 |
| | | - â
**å®å管ç**: åè¦è®°å½å表管çãæ¹éå¤çãæ°æ®ç»è®¡ã导åºåè½ |
| | | |
| | | ## ð ææ¯æ¶æ |
| | | |
| | | ### å端 |
| | | - Spring Boot 2.x |
| | | - MyBatis |
| | | - Quartz (宿¶ä»»å¡) |
| | | - MySQL 5.7+ |
| | | - ä¼ä¸å¾®ä¿¡API |
| | | |
| | | ### å端 |
| | | - Vue 2.x |
| | | - Element UI |
| | | - Axios |
| | | |
| | | ## ð 项ç®ç»æ |
| | | |
| | | ``` |
| | | . |
| | | âââ doc/ # ææ¡£ç®å½ |
| | | â âââ 车è¾å¼å¸¸è¿è¡çæ§åè¦åè½è¯´æ.md # åè½è¯´æ |
| | | â âââ 车è¾å¼å¸¸è¿è¡çæ§åè¦-å¿«éé¨ç½²æå.md # å端é¨ç½² |
| | | â âââ 车è¾å¼å¸¸è¿è¡çæ§åè¦-å端é¨ç½²æå.md # å端é¨ç½² |
| | | â âââ 车è¾å¼å¸¸è¿è¡çæ§åè¦-宿´å®ç°æ»ç».md # å®ç°æ»ç» |
| | | â âââ 车è¾å¼å¸¸è¿è¡çæ§åè¦-README.md # æ¬ææ¡£ |
| | | â |
| | | âââ sql/ |
| | | â âââ vehicle_abnormal_alert.sql # æ°æ®åºåå§åèæ¬ |
| | | â |
| | | âââ ruoyi-system/ # åç«¯æ ¸å¿æ¨¡å |
| | | â âââ src/main/java/com/ruoyi/system/ |
| | | â â âââ domain/ # å®ä½ç±» |
| | | â â â âââ VehicleAbnormalAlert.java # åè¦è®°å½å®ä½ |
| | | â â â âââ VehicleAlertConfig.java # åè¦é
ç½®å®ä½ |
| | | â â âââ mapper/ # Mapperæ¥å£ |
| | | â â â âââ VehicleAbnormalAlertMapper.java |
| | | â â â âââ VehicleAlertConfigMapper.java |
| | | â â âââ service/ # Serviceå± |
| | | â â âââ IVehicleAbnormalAlertService.java |
| | | â â âââ IVehicleAlertConfigService.java |
| | | â â âââ impl/ |
| | | â â âââ VehicleAbnormalAlertServiceImpl.java |
| | | â â âââ VehicleAlertConfigServiceImpl.java |
| | | â âââ src/main/resources/mapper/system/ |
| | | â âââ VehicleAbnormalAlertMapper.xml |
| | | â âââ VehicleAlertConfigMapper.xml |
| | | â |
| | | âââ ruoyi-admin/ # Controllerå± |
| | | â âââ src/main/java/com/ruoyi/web/controller/system/ |
| | | â âââ VehicleAbnormalAlertController.java |
| | | â âââ VehicleAlertConfigController.java |
| | | â |
| | | âââ ruoyi-quartz/ # 宿¶ä»»å¡ |
| | | â âââ src/main/java/com/ruoyi/quartz/task/ |
| | | â âââ VehicleAbnormalAlertTask.java # çæ§å®æ¶ä»»å¡ |
| | | â |
| | | âââ ruoyi-ui/ # åç«¯é¡¹ç® |
| | | âââ src/api/system/ # APIæ¥å£ |
| | | â âââ vehicleAlert.js |
| | | â âââ vehicleAlertConfig.js |
| | | â âââ vehicle.js |
| | | âââ src/views/system/ # 页é¢ç»ä»¶ |
| | | âââ vehicleAlert/ |
| | | â âââ index.vue # åè¦è®°å½å表 |
| | | âââ vehicleAlertConfig/ |
| | | âââ index.vue # åè¦é
置管ç |
| | | ``` |
| | | |
| | | ## ð å¿«éå¼å§ |
| | | |
| | | ### 1. æ°æ®åºåå§å |
| | | |
| | | ```bash |
| | | mysql -u root -p database_name < sql/vehicle_abnormal_alert.sql |
| | | ``` |
| | | |
| | | ### 2. é
置系ç»åæ° |
| | | |
| | | ç»å½åå°ç³»ç»ï¼è¿å
¥ **ç³»ç»ç®¡ç > åæ°è®¾ç½®**ï¼ç¡®è®¤ä»¥ä¸åæ°ï¼ |
| | | |
| | | | åæ°é®å | é»è®¤å¼ | 说æ | |
| | | |---------|--------|------| |
| | | | vehicle.alert.enabled | true | åè½æ»å¼å
³ | |
| | | | vehicle.alert.mileage.threshold | 10 | å
¬éæ°éå¼(km) | |
| | | | vehicle.alert.daily.limit | 5 | æ¯æ¥åè¦æ¬¡æ° | |
| | | | vehicle.alert.interval.minutes | 5 | åè¦é´é(åé) | |
| | | | vehicle.alert.time.window | 10 | çæ§æ¶é´çªå£(åé) | |
| | | | vehicle.alert.notify.users | 1 | é»è®¤éç¥ç¨æ·ID | |
| | | |
| | | ### 3. å¯å¨å®æ¶ä»»å¡ |
| | | |
| | | è¿å
¥ **ç³»ç»çæ§ > 宿¶ä»»å¡**ï¼æ¾å°"车è¾å¼å¸¸è¿è¡çæ§"ä»»å¡ï¼ç¹å»å¯å¨ã |
| | | |
| | | ### 4. é
ç½®èåæé |
| | | |
| | | è¿å
¥ **ç³»ç»ç®¡ç > èå管ç**ï¼æ·»å 以ä¸èåå¹¶åé
æéï¼ |
| | | - 车è¾å¼å¸¸åè¦ (`/system/vehicleAlert`) |
| | | - åè¦é
置管ç (`/system/vehicleAlertConfig`) |
| | | |
| | | ### 5. å建åè¦é
ç½® |
| | | |
| | | è¿å
¥ **车è¾çæ§ > åè¦é
置管ç**ï¼å建å
¨å±é
ç½®æé对ç¹å®é¨é¨/车è¾çé
ç½®ã |
| | | |
| | | ## ð ä½¿ç¨æå |
| | | |
| | | ### åè¦é
ç½®ä¼å
级 |
| | | |
| | | ç³»ç»æ¯æä¸çº§é
ç½®çç¥ï¼æä»¥ä¸ä¼å
级åºç¨ï¼ |
| | | |
| | | ``` |
| | | 车è¾é
ç½® (æé«ä¼å
级) |
| | | â ä¸å卿¶ |
| | | é¨é¨é
ç½® |
| | | â ä¸å卿¶ |
| | | å
¨å±é
ç½® (é»è®¤é
ç½®) |
| | | ``` |
| | | |
| | | **é
置示ä¾**ï¼ |
| | | 1. å建å
¨å±é
ç½®ï¼éå¼10kmï¼æ¯æ¥5次ï¼é´é5åé |
| | | 2. 为"åå
¬å¸A"å建é¨é¨é
ç½®ï¼éå¼8km |
| | | 3. 为"车è¾A001"å建车è¾é
ç½®ï¼éå¼12km |
| | | |
| | | **çæç»æ**ï¼ |
| | | - 车è¾A001使ç¨12kméå¼ |
| | | - åå
¬å¸Açå
¶ä»è½¦è¾ä½¿ç¨8kméå¼ |
| | | - å
¶ä»è½¦è¾ä½¿ç¨10kméå¼ |
| | | |
| | | ### åè¦å¤çæµç¨ |
| | | |
| | | 1. **æ¥çåè¦**ï¼è¿å
¥"车è¾å¼å¸¸åè¦"é¡µé¢ |
| | | 2. **çéåè¦**ï¼ä½¿ç¨æç´¢æ¡ä»¶çééè¦å¤ççåè¦ |
| | | 3. **æ¥ç详æ
**ï¼ç¹å»"详æ
"æé®æ¥çåè¦å®æ´ä¿¡æ¯ |
| | | 4. **å¤çåè¦**ï¼ |
| | | - åæ¡å¤çï¼ç¹å»"å¤ç"æé®ï¼å¡«åå¤ç夿³¨ |
| | | - æ¹éå¤çï¼å¾é夿¡è®°å½ï¼ç¹å»"æ¹éå¤ç" |
| | | 5. **å¯¼åºæ°æ®**ï¼éè¦æ¶å¯å¯¼åºåè¦è®°å½ä¸ºExcel |
| | | |
| | | ## ð æ°æ®ç»è®¡ |
| | | |
| | | åè¦åè¡¨é¡µé¢æä¾å®æ¶ç»è®¡ï¼ |
| | | - **æªå¤çåè¦**ï¼çº¢è²æ¾ç¤ºï¼éè¦ä¼å
å¤ç |
| | | - **仿¥åè¦**ï¼å½å¤©äº§ççåè¦æ°é |
| | | - **累计åè¦è½¦è¾**ï¼åå²äº§çè¿åè¦çè½¦è¾æ° |
| | | - **累计åè¦æ¬¡æ°**ï¼æ»åè¦è®°å½æ° |
| | | |
| | | ## ð§ ç³»ç»é
ç½® |
| | | |
| | | ### 宿¶ä»»å¡é
ç½® |
| | | |
| | | **æ§è¡é¢ç**ï¼æ¯5åéæ§è¡ä¸æ¬¡ |
| | | **Cron表达å¼**ï¼`0 0/5 * * * ?` |
| | | **任塿¹æ³**ï¼`vehicleAbnormalAlertTask.monitorVehicleAbnormalRunning` |
| | | |
| | | 坿 ¹æ®å®é
éæ±è°æ´æ§è¡é¢çï¼å»ºè®®èå´ï¼ |
| | | - æçï¼1åé (`0 0/1 * * * ?`) |
| | | - æ¨èï¼5åé (`0 0/5 * * * ?`) |
| | | - æé¿ï¼30åé (`0 0/30 * * * ?`) |
| | | |
| | | ### éç¥é
ç½® |
| | | |
| | | **éç¥æ¹å¼**ï¼ä¼ä¸å¾®ä¿¡æ¶æ¯æ¨é |
| | | **éç¥å
容**ï¼è½¦è¾ä¿¡æ¯ + è¿è¡éç¨ + æ¶é´èå´ |
| | | **éç¥ç¨æ·**ï¼ |
| | | 1. ä¼å
使ç¨é
置表ä¸çéç¥ç¨æ·å表 |
| | | 2. å
¶æ¬¡æ ¹æ®è½¦è¾å½å±é¨é¨æ¥æ¾è´è´£äºº |
| | | 3. æå使ç¨ç³»ç»åæ°ä¸çé»è®¤ç¨æ· |
| | | |
| | | ## ð çæ§ææ |
| | | |
| | | ç³»ç»è¿è¡çæ§å»ºè®®å
³æ³¨ä»¥ä¸ææ ï¼ |
| | | |
| | | - **任塿§è¡æ¶é´**ï¼å»ºè®® < 30ç§ï¼100è¾è½¦ï¼ |
| | | - **åè¦ååºæ¶é´**ï¼ä»è§¦åå°éç¥éè¾¾ < 1åé |
| | | - **éç¥æåç**ï¼> 95% |
| | | - **误æ¥ç**ï¼< 5% |
| | | |
| | | ## â ï¸ æ³¨æäºé¡¹ |
| | | |
| | | 1. **GPSæ°æ®ä¾èµ**ï¼ç³»ç»ä¾èµGPSåæ®µéç¨æ°æ®ï¼ç¡®ä¿GPSè®¾å¤æ£å¸¸å·¥ä½ |
| | | 2. **ä»»å¡ç¶æåç¡®**ï¼åæ¶æ´æ°ä»»å¡ç¶æï¼é¿å
è¯¯å¤ |
| | | 3. **é
ç½®åçæ§**ï¼æ ¹æ®å®é
ä¸å¡åºæ¯è°æ´éå¼åé¢ç |
| | | 4. **éç¥ç¨æ·ææ**ï¼å®ææ£æ¥éç¥ç¨æ·å表æ¯å¦ææ |
| | | 5. **æ°æ®å®ææ¸
ç**ï¼å»ºè®®å®æå½æ¡£æå é¤åå²åè¦æ°æ® |
| | | |
| | | ## ð æ
éææ¥ |
| | | |
| | | ### åè¦æªäº§ç |
| | | |
| | | **æ£æ¥æ¸
å**ï¼ |
| | | - [ ] åè½å¼å
³æ¯å¦å¯ç¨ |
| | | - [ ] 宿¶ä»»å¡æ¯å¦å¯å¨ |
| | | - [ ] è½¦è¾æ¯å¦æGPSæ°æ® |
| | | - [ ] éç¨æ¯å¦è¶
è¿éå¼ |
| | | - [ ] æ¯å¦å·²è¾¾å°é¢çéå¶ |
| | | |
| | | ### éç¥æªåé |
| | | |
| | | **æ£æ¥æ¸
å**ï¼ |
| | | - [ ] ä¼ä¸å¾®ä¿¡æå¡æ¯å¦å¯ç¨ |
| | | - [ ] éç¥ç¨æ·IDæ¯å¦é
ç½® |
| | | - [ ] ç¨æ·IDæ¯å¦ææ |
| | | - [ ] ä¼ä¸å¾®ä¿¡åºç¨é
ç½®æ¯å¦æ£ç¡® |
| | | - [ ] ç½ç»è¿æ¥æ¯å¦æ£å¸¸ |
| | | |
| | | ### æ§è½é®é¢ |
| | | |
| | | **ä¼å建议**ï¼ |
| | | - éå½å¢å 宿¶ä»»å¡æ§è¡é´é |
| | | - ä¸ºæ°æ®åºè¡¨æ·»å ç´¢å¼ |
| | | - 宿æ¸
çå岿°æ® |
| | | - èè使ç¨ç¼å |
| | | |
| | | ## ð ææ¯æ¯æ |
| | | |
| | | ### ææ¡£é¾æ¥ |
| | | |
| | | - [åè½è¯´æææ¡£](./车è¾å¼å¸¸è¿è¡çæ§åè¦åè½è¯´æ.md) - 详ç»åè½ä»ç» |
| | | - [å¿«éé¨ç½²æå](./车è¾å¼å¸¸è¿è¡çæ§åè¦-å¿«éé¨ç½²æå.md) - å端é¨ç½²æ¥éª¤ |
| | | - [å端é¨ç½²æå](./车è¾å¼å¸¸è¿è¡çæ§åè¦-å端é¨ç½²æå.md) - å端é¨ç½²æ¥éª¤ |
| | | - [宿´å®ç°æ»ç»](./车è¾å¼å¸¸è¿è¡çæ§åè¦-宿´å®ç°æ»ç».md) - ææ¯å®ç°ç»è |
| | | |
| | | ### 常è§é®é¢ |
| | | |
| | | 详è§åé¨ç½²æåç"常è§é®é¢"ç« è |
| | | |
| | | ## ð æ´æ°æ¥å¿ |
| | | |
| | | ### v1.0.0 (2026-01-12) |
| | | |
| | | **åå§çæ¬åå¸** |
| | | |
| | | - â
宿´å®ç°è½¦è¾å¼å¸¸è¿è¡çæ§åè½ |
| | | - â
ä¸çº§é
ç½®çç¥æ¯æ |
| | | - â
åè¦è®°å½ç®¡ç |
| | | - â
åè¦é
置管ç |
| | | - â
ä¼ä¸å¾®ä¿¡éç¥éæ |
| | | - â
å端管çé¡µé¢ |
| | | - â
宿´ææ¡£ä½ç³» |
| | | |
| | | **代ç ç»è®¡**ï¼ |
| | | - SQLèæ¬ï¼1个æä»¶ï¼123è¡ |
| | | - Java代ç ï¼11个æä»¶ï¼1,785è¡ |
| | | - Vue代ç ï¼5个æä»¶ï¼1,151è¡ |
| | | - ææ¡£ï¼5个æä»¶ï¼1,573+è¡ |
| | | |
| | | ## ð 许å¯è¯ |
| | | |
| | | æ¬é¡¹ç®éµå¾ª RuoYi æ¡æ¶ç许å¯è¯åè®® |
| | | |
| | | --- |
| | | |
| | | **å¼åæ¶é´**ï¼2026-01-12 |
| | | **项ç®ç¶æ**ï¼â
已宿 |
| | | **ç»´æ¤å¢é**ï¼AIå¼å婿 |
| New file |
| | |
| | | # 车è¾å¼å¸¸è¿è¡çæ§åè¦åè½ - å端é¨ç½²æå |
| | | |
| | | ## ð æ¦è¿° |
| | | |
| | | æ¬ææ¡£æä¾è½¦è¾å¼å¸¸è¿è¡çæ§åè¦åè½å端é¨åç宿´é¨ç½²æåï¼å
æ¬æä»¶æ¸
åãé
ç½®æ¥éª¤åæµè¯éªè¯ã |
| | | |
| | | ## ð å端æä»¶æ¸
å |
| | | |
| | | ### 1. APIæ¥å£æä»¶ |
| | | |
| | | **è·¯å¾**: `ruoyi-ui/src/api/system/` |
| | | |
| | | - â
`vehicleAlert.js` - åè¦è®°å½ç®¡çAPI |
| | | - â
`vehicleAlertConfig.js` - åè¦é
置管çAPI |
| | | - â
`vehicle.js` - 车è¾ä¿¡æ¯API |
| | | |
| | | ### 2. 页é¢ç»ä»¶æä»¶ |
| | | |
| | | **è·¯å¾**: `ruoyi-ui/src/views/system/` |
| | | |
| | | - â
`vehicleAlert/index.vue` - åè¦è®°å½å表页é¢ï¼529è¡ï¼ |
| | | - â
`vehicleAlertConfig/index.vue` - åè¦é
置管ç页é¢ï¼487è¡ï¼ |
| | | |
| | | ### 3. å端æ¥å£æä»¶ï¼é
ç½®ç®¡çæ°å¢ï¼ |
| | | |
| | | **è·¯å¾**: `ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/` |
| | | |
| | | - â
`VehicleAlertConfigController.java` - é
置管çController |
| | | |
| | | **è·¯å¾**: `ruoyi-system/src/main/java/com/ruoyi/system/` |
| | | |
| | | - â
`service/IVehicleAlertConfigService.java` - é
ç½®æå¡æ¥å£ |
| | | - â
`service/impl/VehicleAlertConfigServiceImpl.java` - é
ç½®æå¡å®ç° |
| | | - â
`mapper/VehicleAlertConfigMapper.java` - é
ç½®Mapperæ¥å£ |
| | | - â
`domain/VehicleAlertConfig.java` - é
ç½®å®ä½ç±»ï¼å·²æ´æ°ï¼ |
| | | |
| | | **è·¯å¾**: `ruoyi-system/src/main/resources/mapper/system/` |
| | | |
| | | - â
`VehicleAlertConfigMapper.xml` - é
ç½®Mapper XML |
| | | |
| | | ## ð é¨ç½²æ¥éª¤ |
| | | |
| | | ### ç¬¬ä¸æ¥ï¼ç¡®è®¤æ°æ®åºå·²åå§å |
| | | |
| | | ç¡®ä¿å·²æ§è¡SQLåå§åèæ¬ï¼ |
| | | |
| | | ```bash |
| | | sql/vehicle_abnormal_alert.sql |
| | | ``` |
| | | |
| | | è¯¥èæ¬ä¼åå»ºï¼ |
| | | - â
`tb_vehicle_abnormal_alert` - åè¦è®°å½è¡¨ |
| | | - â
`tb_vehicle_alert_config` - åè¦é
置表 |
| | | - â
6个系ç»é
ç½®åæ° |
| | | - â
宿¶ä»»å¡è®°å½ |
| | | - â
èåæéè®°å½ï¼2个èåï¼ |
| | | |
| | | ### ç¬¬äºæ¥ï¼é
ç½®è·¯ç± |
| | | |
| | | å¨ `ruoyi-ui/src/router/index.js` 䏿·»å è·¯ç±ï¼å¦æä½¿ç¨å¨æè·¯ç±ï¼å¯è·³è¿æ¤æ¥éª¤ï¼ï¼ |
| | | |
| | | ```javascript |
| | | // 车è¾å¼å¸¸åè¦ç®¡ç |
| | | { |
| | | path: '/system/vehicleAlert', |
| | | component: Layout, |
| | | hidden: true, |
| | | permissions: ['system:vehicleAlert:view'], |
| | | children: [ |
| | | { |
| | | path: 'index', |
| | | component: () => import('@/views/system/vehicleAlert/index'), |
| | | name: 'VehicleAlert', |
| | | meta: { title: '车è¾å¼å¸¸åè¦', activeMenu: '/system/vehicleAlert' } |
| | | } |
| | | ] |
| | | }, |
| | | // 车è¾åè¦é
ç½® |
| | | { |
| | | path: '/system/vehicleAlertConfig', |
| | | component: Layout, |
| | | hidden: true, |
| | | permissions: ['system:vehicleAlertConfig:view'], |
| | | children: [ |
| | | { |
| | | path: 'index', |
| | | component: () => import('@/views/system/vehicleAlertConfig/index'), |
| | | name: 'VehicleAlertConfig', |
| | | meta: { title: '车è¾åè¦é
ç½®', activeMenu: '/system/vehicleAlertConfig' } |
| | | } |
| | | ] |
| | | } |
| | | ``` |
| | | |
| | | ### ç¬¬ä¸æ¥ï¼é
ç½®èåæé |
| | | |
| | | ç»å½åå°ç®¡çç³»ç»ï¼è¿å
¥ **ç³»ç»ç®¡ç > èå管ç**ï¼æ·»å 以ä¸èåï¼ |
| | | |
| | | #### ç¶èåï¼è½¦è¾çæ§ï¼å¯éï¼å¦å·²æè½¦è¾ç¸å
³èåï¼å¯æ·»å å°ç°æèåä¸ï¼ |
| | | |
| | | | åæ®µ | å¼ | |
| | | |------|-----| |
| | | | èååç§° | 车è¾çæ§ | |
| | | | èåç±»å | ç®å½ | |
| | | | èå徿 | monitor | |
| | | | æ¾ç¤ºæåº | 5 | |
| | | | è·¯ç±å°å | vehicle-monitor | |
| | | | ç»ä»¶è·¯å¾ | Layout | |
| | | |
| | | #### åèå1ï¼è½¦è¾å¼å¸¸åè¦ |
| | | |
| | | | åæ®µ | å¼ | |
| | | |------|-----| |
| | | | ä¸çº§èå | 车è¾çæ§ | |
| | | | èååç§° | 车è¾å¼å¸¸åè¦ | |
| | | | èåç±»å | èå | |
| | | | èå徿 | warning | |
| | | | æ¾ç¤ºæåº | 1 | |
| | | | è·¯ç±å°å | vehicleAlert | |
| | | | ç»ä»¶è·¯å¾ | system/vehicleAlert/index | |
| | | | æéæ è¯ | system:vehicleAlert:list | |
| | | |
| | | **åè½æé®æé**ï¼ |
| | | - `system:vehicleAlert:query` - æ¥è¯¢ |
| | | - `system:vehicleAlert:handle` - å¤ç |
| | | - `system:vehicleAlert:remove` - å é¤ |
| | | - `system:vehicleAlert:export` - å¯¼åº |
| | | |
| | | #### åèå2ï¼åè¦é
置管ç |
| | | |
| | | | åæ®µ | å¼ | |
| | | |------|-----| |
| | | | ä¸çº§èå | 车è¾çæ§ | |
| | | | èååç§° | åè¦é
置管ç | |
| | | | èåç±»å | èå | |
| | | | èå徿 | edit | |
| | | | æ¾ç¤ºæåº | 2 | |
| | | | è·¯ç±å°å | vehicleAlertConfig | |
| | | | ç»ä»¶è·¯å¾ | system/vehicleAlertConfig/index | |
| | | | æéæ è¯ | system:vehicleAlertConfig:list | |
| | | |
| | | **åè½æé®æé**ï¼ |
| | | - `system:vehicleAlertConfig:query` - æ¥è¯¢ |
| | | - `system:vehicleAlertConfig:add` - æ°å¢ |
| | | - `system:vehicleAlertConfig:edit` - ä¿®æ¹ |
| | | - `system:vehicleAlertConfig:remove` - å é¤ |
| | | - `system:vehicleAlertConfig:export` - å¯¼åº |
| | | |
| | | ### ç¬¬åæ¥ï¼ç¼è¯åç«¯é¡¹ç® |
| | | |
| | | ```bash |
| | | cd ruoyi-ui |
| | | npm install |
| | | npm run build:prod |
| | | ``` |
| | | |
| | | ### ç¬¬äºæ¥ï¼é¨ç½²å°Nginx |
| | | |
| | | å°ç¼è¯åçæä»¶é¨ç½²å°Nginxï¼ |
| | | |
| | | ```bash |
| | | # å¤å¶distç®å½å°nginx |
| | | cp -r dist/* /usr/share/nginx/html/ |
| | | |
| | | # éå¯nginx |
| | | nginx -s reload |
| | | ``` |
| | | |
| | | ## 𧪠åè½æµè¯ |
| | | |
| | | ### 1. åè¦è®°å½å表æµè¯ |
| | | |
| | | 访é®ï¼`ç³»ç»ç®¡ç > 车è¾çæ§ > 车è¾å¼å¸¸åè¦` |
| | | |
| | | **æµè¯ç¹**ï¼ |
| | | - â
åè¡¨æ°æ®æ£å¸¸æ¾ç¤º |
| | | - â
æç´¢åè½ï¼è½¦çå·ãæ¥æãç¶æãé¨é¨ï¼ |
| | | - â
ç»è®¡å¡çæ¾ç¤ºï¼æªå¤çã仿¥ã累计车è¾ã累计次æ°ï¼ |
| | | - â
æ¥ç详æ
åè½ |
| | | - â
å¤çåæ¡åè¦ |
| | | - â
æ¹éå¤çåè¦ |
| | | - â
å é¤åè½ |
| | | - â
导åºåè½ |
| | | - â
å页åè½ |
| | | |
| | | ### 2. åè¦é
ç½®ç®¡çæµè¯ |
| | | |
| | | 访é®ï¼`ç³»ç»ç®¡ç > 车è¾çæ§ > åè¦é
置管ç` |
| | | |
| | | **æµè¯ç¹**ï¼ |
| | | - â
é
ç½®å表æ¾ç¤ºï¼å
¨å±/é¨é¨/车è¾ï¼ |
| | | - â
æ°å¢å
¨å±é
ç½® |
| | | - â
æ°å¢é¨é¨é
ç½® |
| | | - â
æ°å¢è½¦è¾é
ç½® |
| | | - â
ä¿®æ¹é
ç½® |
| | | - â
å é¤é
ç½® |
| | | - â
å¯ç¨/åç¨é
ç½® |
| | | - â
导åºåè½ |
| | | - â
é
置说ææç¤º |
| | | |
| | | ### 3. é
ç½®ä¼å
级æµè¯ |
| | | |
| | | **æµè¯åºæ¯**ï¼ |
| | | 1. å建å
¨å±é
ç½®ï¼éå¼10kmï¼ |
| | | 2. å建é¨é¨é
ç½®ï¼éå¼8kmï¼ |
| | | 3. å建车è¾é
ç½®ï¼éå¼12kmï¼ |
| | | |
| | | **é¢æç»æ**ï¼ |
| | | - æè½¦è¾é
ç½®ç车è¾ä½¿ç¨12kméå¼ |
| | | - æé¨é¨é
ç½®ä½æ 车è¾é
ç½®ç车è¾ä½¿ç¨8kméå¼ |
| | | - æ é¨é¨å车è¾é
ç½®ç车è¾ä½¿ç¨10kméå¼ |
| | | |
| | | ## ð 页é¢åè½è¯¦è§£ |
| | | |
| | | ### åè¦è®°å½åè¡¨é¡µé¢ |
| | | |
| | | **æ ¸å¿åè½**ï¼ |
| | | 1. **ç»è®¡é¢æ¿** - 4个ç»è®¡å¡ç宿¶æ¾ç¤ºå
³é®ææ |
| | | 2. **é«çº§æç´¢** - æ¯æå¤æ¡ä»¶ç»åæç´¢ |
| | | 3. **ç¶ææ ç¾** - åè¦ç¶æãéç¥ç¶æç¨ä¸åé¢è²æ ç¾åºå |
| | | 4. **详æ
æ¥ç** - ä½¿ç¨ `el-descriptions` ç»ä»¶å±ç¤ºè¯¦ç»ä¿¡æ¯ |
| | | 5. **æ¹éæä½** - æ¯ææ¹éå¤çæªå¤ççåè¦ |
| | | |
| | | **页é¢ç¹è²**ï¼ |
| | | - ð¨ ç¾è§çUI设计ï¼ä½¿ç¨Element UIç»ä»¶ |
| | | - ð± ååºå¼å¸å±ï¼éé
ä¸åå±å¹ |
| | | - ð 宿¶å·æ°ç»è®¡æ°æ® |
| | | - ð å¤ç夿³¨å¿
å¡«éªè¯ |
| | | |
| | | ### åè¦é
置管çé¡µé¢ |
| | | |
| | | **æ ¸å¿åè½**ï¼ |
| | | 1. **ä¸çº§é
ç½®** - æ¯æå
¨å±/é¨é¨/车è¾ä¸çº§é
ç½®çç¥ |
| | | 2. **å¨æè¡¨å** - æ ¹æ®é
置类å卿æ¾ç¤ºè¡¨å项 |
| | | 3. **æºè½éªè¯** - æ ¹æ®é
置类åéªè¯å¿
填项 |
| | | 4. **宿¶åæ¢** - ç¶æå¼å
³å®æ¶çæ |
| | | 5. **é
置说æ** - 页é¢é¡¶é¨æ¾ç¤ºé
置说ææç¤º |
| | | |
| | | **é
ç½®åæ°**ï¼ |
| | | - **éç¨éå¼** - 1-1000kmï¼æ¥é¿1 |
| | | - **æ¯æ¥åè¦æ¬¡æ°** - 1-100æ¬¡ï¼æ¥é¿1 |
| | | - **åè¦é´é** - 1-1440åéï¼æ¥é¿1 |
| | | - **éç¥ç¨æ·** - æ¯æå¤ä¸ªç¨æ·IDï¼éå·åéï¼ |
| | | |
| | | ## ð§ ç³»ç»é
ç½®åæ° |
| | | |
| | | å¨ **ç³»ç»ç®¡ç > åæ°è®¾ç½®** ä¸é
置以ä¸åæ°ï¼ |
| | | |
| | | | åæ°é®å | åæ°åç§° | é»è®¤å¼ | 说æ | |
| | | |---------|---------|--------|------| |
| | | | `vehicle.alert.enabled` | 车è¾å¼å¸¸åè¦å¯ç¨å¼å
³ | true | æ»å¼å
³ | |
| | | | `vehicle.alert.mileage.threshold` | 车è¾å¼å¸¸åè¦å
¬éæ°éå¼ | 10 | å
¨å±é»è®¤éå¼(km) | |
| | | | `vehicle.alert.daily.limit` | 车è¾å¼å¸¸åè¦æ¯æ¥åè¦æ¬¡æ° | 5 | å
¨å±é»è®¤æ¬¡æ° | |
| | | | `vehicle.alert.interval.minutes` | 车è¾å¼å¸¸åè¦é´éæ¶é´ | 5 | å
¨å±é»è®¤é´é(åé) | |
| | | | `vehicle.alert.time.window` | 车è¾å¼å¸¸åè¦çæ§æ¶é´çªå£ | 10 | çæ§çªå£(åé) | |
| | | | `vehicle.alert.notify.users` | 车è¾å¼å¸¸åè¦éç¥ç¨æ·å表 | 1 | å
¨å±é»è®¤éç¥ç¨æ· | |
| | | |
| | | ## ð APIæ¥å£å表 |
| | | |
| | | ### åè¦è®°å½API |
| | | |
| | | | æ¥å£ | æ¹æ³ | è·¯å¾ | 说æ | |
| | | |------|------|------|------| |
| | | | æ¥è¯¢å表 | GET | /system/vehicleAlert/list | å页æ¥è¯¢ | |
| | | | æ¥è¯¢è¯¦æ
| GET | /system/vehicleAlert/{id} | è·å详æ
| |
| | | | å¤çåè¦ | PUT | /system/vehicleAlert/handle/{id} | åæ¡å¤ç | |
| | | | æ¹éå¤ç | PUT | /system/vehicleAlert/batchHandle | æ¹éå¤ç | |
| | | | å é¤åè¦ | DELETE | /system/vehicleAlert/{ids} | å é¤è®°å½ | |
| | | | æªå¤çç»è®¡ | GET | /system/vehicleAlert/unhandledCount | ç»è®¡æ°é | |
| | | | å¯¼åºæ°æ® | GET | /system/vehicleAlert/export | 导åºExcel | |
| | | |
| | | ### åè¦é
ç½®API |
| | | |
| | | | æ¥å£ | æ¹æ³ | è·¯å¾ | 说æ | |
| | | |------|------|------|------| |
| | | | æ¥è¯¢å表 | GET | /system/vehicleAlertConfig/list | å页æ¥è¯¢ | |
| | | | æ¥è¯¢è¯¦æ
| GET | /system/vehicleAlertConfig/{id} | è·å详æ
| |
| | | | æ°å¢é
ç½® | POST | /system/vehicleAlertConfig | æ°å¢ | |
| | | | ä¿®æ¹é
ç½® | PUT | /system/vehicleAlertConfig | ä¿®æ¹ | |
| | | | å é¤é
ç½® | DELETE | /system/vehicleAlertConfig/{ids} | å é¤ | |
| | | | 导åºé
ç½® | POST | /system/vehicleAlertConfig/export | 导åºExcel | |
| | | |
| | | ## â ï¸ å¸¸è§é®é¢ |
| | | |
| | | ### 1. èå䏿¾ç¤º |
| | | |
| | | **åå **ï¼æéæªåé
|
| | | **è§£å³**ï¼ |
| | | 1. æ£æ¥è忝å¦å建 |
| | | 2. æ£æ¥è§è²æ¯å¦åé
èåæé |
| | | 3. æ¸
餿µè§å¨ç¼åï¼éæ°ç»å½ |
| | | |
| | | ### 2. APIæ¥å£404 |
| | | |
| | | **åå **ï¼å端æå¡æªå¯å¨æè·¯ç±é
ç½®é误 |
| | | **è§£å³**ï¼ |
| | | 1. æ£æ¥å端æå¡æ¯å¦æ£å¸¸è¿è¡ |
| | | 2. æ£æ¥ `application.yml` ä¸ç `context-path` é
ç½® |
| | | 3. æ¥çå端æ¥å¿ |
| | | |
| | | ### 3. é
置修æ¹ä¸çæ |
| | | |
| | | **åå **ï¼ç¼åæªå·æ° |
| | | **è§£å³**ï¼ |
| | | 1. å¨ **ç³»ç»ç®¡ç > åæ°è®¾ç½®** ä¸ç¹å»"å·æ°ç¼å" |
| | | 2. æéå¯å端æå¡ |
| | | |
| | | ### 4. åè¦ç»è®¡æ°æ®ä¸åç¡® |
| | | |
| | | **åå **ï¼å端ç»è®¡é»è¾åºäºå½å页颿°æ® |
| | | **è§£å³**ï¼ |
| | | - ç¹å»"å·æ°ç»è®¡"æé®è·åææ°æ°æ® |
| | | - æè
å端æä¾ç»è®¡æ¥å£ï¼å¾
ä¼åï¼ |
| | | |
| | | ### 5. 车è¾ä¸æå表å è½½æ
¢ |
| | | |
| | | **åå **ï¼è½¦è¾æ°æ®é大 |
| | | **è§£å³**ï¼ |
| | | 1. æ·»å æç´¢è¿æ»¤åè½ |
| | | 2. ä½¿ç¨æå è½½æå页å è½½ |
| | | 3. ä¼åå端æ¥è¯¢æ§è½ |
| | | |
| | | ## ð¯ åç»ä¼åæ¹å |
| | | |
| | | ### 1. å端ä¼å |
| | | |
| | | - [ ] åè¦ç»è®¡å¾è¡¨ï¼EChartsï¼ |
| | | - [ ] 宿¶æ¶æ¯æ¨éï¼WebSocketï¼ |
| | | - [ ] ç§»å¨ç«¯éé
|
| | | - [ ] åè¦å°å¾å±ç¤º |
| | | |
| | | ### 2. åè½å¢å¼º |
| | | |
| | | - [ ] åè¦è§åæ´çµæ´»é
ç½® |
| | | - [ ] æ¯æå¤ç§éç¥æ¹å¼ï¼çä¿¡ãé®ä»¶ï¼ |
| | | - [ ] åè¦åå²è¶å¿åæ |
| | | - [ ] æ¹é导å
¥é
ç½® |
| | | |
| | | ### 3. æ§è½ä¼å |
| | | |
| | | - [ ] 车è¾å表æå è½½ |
| | | - [ ] åè¡¨èææ»å¨ |
| | | - [ ] æ¥å£ç¼åä¼å |
| | | |
| | | ## ð ææ¯æ¯æ |
| | | |
| | | 妿é®é¢ï¼è¯·èç³»å¼åå¢éææ¥çç¸å
³ææ¡£ï¼ |
| | | |
| | | - ð åè½è¯´æææ¡£ï¼`doc/车è¾å¼å¸¸è¿è¡çæ§åè¦åè½è¯´æ.md` |
| | | - ð å®ç°æ»ç»ï¼`doc/车è¾å¼å¸¸è¿è¡çæ§åè¦-å®ç°æ»ç».md` |
| | | - ð å¿«éé¨ç½²æåï¼`doc/车è¾å¼å¸¸è¿è¡çæ§åè¦-å¿«éé¨ç½²æå.md` |
| | | |
| | | --- |
| | | |
| | | **é¨ç½²æ¶é´**ï¼2026-01-12 |
| | | **ææ¡£çæ¬**ï¼v1.0 |
| | | **ç»´æ¤äººå**ï¼å¼åå¢é |
| New file |
| | |
| | | # 车è¾å¼å¸¸è¿è¡çæ§åè¦åè½ - 宿´å®ç°æ»ç» |
| | | |
| | | ## ð é¡¹ç®æ¦è¿° |
| | | |
| | | æ¬é¡¹ç®å®ç°äºä¸å¥å®æ´ç车è¾å¼å¸¸è¿è¡çæ§åè¦ç³»ç»ï¼ç¨äºçæ§æ ä»»å¡ç¶æä¸è½¦è¾çå¼å¸¸è¿è¡æ
åµï¼å¹¶éè¿å°ç¨åº/ä¼ä¸å¾®ä¿¡åæ¶åè¦éç¥ç¸å
³è´è´£äººã |
| | | |
| | | **å¼åæ¶é´**ï¼2026-01-12 |
| | | **å¼å人å**ï¼AIå¼å婿 |
| | | **项ç®ç¶æ**ï¼â
å
¨é¨å®æ |
| | | |
| | | ## ð¯ æ ¸å¿åè½ |
| | | |
| | | ### 1. æºè½çæ§ |
| | | - â
宿¶çæ§ææè½¦è¾è¿è¡ç¶æ |
| | | - â
åºäºGPSåæ®µéç¨è®¡ç®åç¡®éç¨ |
| | | - â
èªå¨å¤æè½¦è¾æ¯å¦ç»å®ä»»å¡ |
| | | - â
å¯é
ç½®çå
¬éæ°éå¼ |
| | | |
| | | ### 2. çµæ´»é
ç½® |
| | | - â
ä¸çº§é
ç½®çç¥ï¼å
¨å±/é¨é¨/车è¾ï¼ |
| | | - â
é
ç½®ä¼å
级èªå¨åºç¨ |
| | | - â
å¤ç»´åº¦åæ°é
ç½®ï¼éå¼ã次æ°ãé´éï¼ |
| | | - â
卿å¯ç¨/åç¨ |
| | | |
| | | ### 3. é¢çæ§å¶ |
| | | - â
æ¯æ¥åè¦æ¬¡æ°éå¶ |
| | | - â
åè¦é´éæ¶é´æ§å¶ |
| | | - â
é¿å
é¢ç¹éªæ° |
| | | |
| | | ### 4. åæ¶éç¥ |
| | | - â
ä¼ä¸å¾®ä¿¡æ¶æ¯æ¨é |
| | | - â
å°ç¨åºéç¥ |
| | | - â
å¯é
ç½®éç¥ç¨æ·å表 |
| | | - â
éç¥ç¶æè¿½è¸ª |
| | | |
| | | ### 5. å®å管ç |
| | | - â
åè¦è®°å½å表管ç |
| | | - â
åè¦å¤çæµç¨ |
| | | - â
æ¹éæä½æ¯æ |
| | | - â
æ°æ®ç»è®¡åæ |
| | | - â
导åºåè½ |
| | | |
| | | ## ð ææ¯æ¶æ |
| | | |
| | | ### åç«¯ææ¯æ |
| | | - **æ¡æ¶**: Spring Boot 2.x |
| | | - **ORM**: MyBatis |
| | | - **宿¶ä»»å¡**: Quartz |
| | | - **æ°æ®åº**: MySQL 5.7+ |
| | | - **æ¶æ¯æ¨é**: ä¼ä¸å¾®ä¿¡API |
| | | |
| | | ### åç«¯ææ¯æ |
| | | - **æ¡æ¶**: Vue 2.x |
| | | - **UIç»ä»¶**: Element UI |
| | | - **æå»ºå·¥å
·**: Webpack |
| | | - **HTTP客æ·ç«¯**: Axios |
| | | |
| | | ### æ ¸å¿è®¾è®¡æ¨¡å¼ |
| | | - **çç¥æ¨¡å¼**: ä¸çº§é
ç½®ä¼å
级çç¥ |
| | | - **æ¨¡æ¿æ¹æ³**: åè¦å¤çæµç¨ |
| | | - **å便¨¡å¼**: é
ç½®æå¡ |
| | | - **è§å¯è
模å¼**: æ¶æ¯éç¥æºå¶ |
| | | |
| | | ## ð æä»¶æ¸
å |
| | | |
| | | ### ä¸ãæ°æ®åºæä»¶ (1个æä»¶) |
| | | |
| | | ``` |
| | | sql/vehicle_abnormal_alert.sql (123è¡) |
| | | âââ tb_vehicle_abnormal_alert 表å®ä¹ |
| | | âââ tb_vehicle_alert_config 表å®ä¹ |
| | | âââ 6个系ç»é
ç½®åæ° |
| | | âââ 宿¶ä»»å¡è®°å½ |
| | | âââ èåæéè®°å½ |
| | | ``` |
| | | |
| | | ### äºãå端Javaæä»¶ (11个æä»¶ï¼å
±1,785è¡) |
| | | |
| | | #### 1. å®ä½ç±» (2个æä»¶ï¼448è¡) |
| | | ``` |
| | | ruoyi-system/src/main/java/com/ruoyi/system/domain/ |
| | | âââ VehicleAbnormalAlert.java (303è¡) - åè¦è®°å½å®ä½ |
| | | âââ VehicleAlertConfig.java (145è¡) - åè¦é
ç½®å®ä½ |
| | | ``` |
| | | |
| | | #### 2. Mapperæ¥å£ (4个æä»¶ï¼236è¡) |
| | | ``` |
| | | ruoyi-system/src/main/java/com/ruoyi/system/mapper/ |
| | | âââ VehicleAbnormalAlertMapper.java (93è¡) |
| | | âââ VehicleAlertConfigMapper.java (71è¡) |
| | | âââ VehicleGpsSegmentMileageMapper.java (æ©å±selectSegmentsByTimeRangeæ¹æ³) |
| | | âââ SysTaskMapper.java (æ©å±selectVehicleTasksInTimeRangeæ¹æ³) |
| | | ``` |
| | | |
| | | #### 3. Mapper XML (2个æä»¶ï¼315è¡) |
| | | ``` |
| | | ruoyi-system/src/main/resources/mapper/system/ |
| | | âââ VehicleAbnormalAlertMapper.xml (179è¡) |
| | | âââ VehicleAlertConfigMapper.xml (136è¡) |
| | | ``` |
| | | |
| | | #### 4. Serviceæ¥å£ (2个æä»¶ï¼169è¡) |
| | | ``` |
| | | ruoyi-system/src/main/java/com/ruoyi/system/service/ |
| | | âââ IVehicleAbnormalAlertService.java (98è¡) |
| | | âââ IVehicleAlertConfigService.java (71è¡) |
| | | ``` |
| | | |
| | | #### 5. Serviceå®ç° (2个æä»¶ï¼284è¡) |
| | | ``` |
| | | ruoyi-system/src/main/java/com/ruoyi/system/service/impl/ |
| | | âââ VehicleAbnormalAlertServiceImpl.java (174è¡) |
| | | âââ VehicleAlertConfigServiceImpl.java (110è¡) |
| | | ``` |
| | | |
| | | #### 6. Controller (2个æä»¶ï¼257è¡) |
| | | ``` |
| | | ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/ |
| | | âââ VehicleAbnormalAlertController.java (150è¡) |
| | | âââ VehicleAlertConfigController.java (107è¡) |
| | | ``` |
| | | |
| | | #### 7. 宿¶ä»»å¡ (1个æä»¶ï¼457è¡) |
| | | ``` |
| | | ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/ |
| | | âââ VehicleAbnormalAlertTask.java (457è¡) - æ ¸å¿çæ§é»è¾ |
| | | ``` |
| | | |
| | | ### ä¸ãå端Vueæä»¶ (5个æä»¶ï¼å
±1,151è¡) |
| | | |
| | | #### 1. APIæ¥å£ (3个æä»¶ï¼216è¡) |
| | | ``` |
| | | ruoyi-ui/src/api/system/ |
| | | âââ vehicleAlert.js (81è¡) - åè¦è®°å½API |
| | | âââ vehicleAlertConfig.js (54è¡) - åè¦é
ç½®API |
| | | âââ vehicle.js (81è¡) - 车è¾ä¿¡æ¯API |
| | | ``` |
| | | |
| | | #### 2. 页é¢ç»ä»¶ (2个æä»¶ï¼1,016è¡) |
| | | ``` |
| | | ruoyi-ui/src/views/system/ |
| | | âââ vehicleAlert/index.vue (529è¡) - åè¦è®°å½åè¡¨é¡µé¢ |
| | | âââ vehicleAlertConfig/index.vue (487è¡) - åè¦é
置管çé¡µé¢ |
| | | ``` |
| | | |
| | | ### åãææ¡£æä»¶ (4个æä»¶ï¼å
±1,573è¡) |
| | | |
| | | ``` |
| | | doc/ |
| | | âââ 车è¾å¼å¸¸è¿è¡çæ§åè¦åè½è¯´æ.md (288è¡) |
| | | âââ 车è¾å¼å¸¸è¿è¡çæ§åè¦-å®ç°æ»ç».md (377è¡) |
| | | âââ 车è¾å¼å¸¸è¿è¡çæ§åè¦-å¿«éé¨ç½²æå.md (263è¡) |
| | | âââ 车è¾å¼å¸¸è¿è¡çæ§åè¦-å端é¨ç½²æå.md (358è¡) |
| | | âââ 车è¾å¼å¸¸è¿è¡çæ§åè¦-宿´å®ç°æ»ç».md (æ¬ææ¡£) |
| | | ``` |
| | | |
| | | ## ð 代ç ç»è®¡ |
| | | |
| | | | ç±»å | æä»¶æ° | æ»è¡æ° | 说æ | |
| | | |-----|--------|--------|------| |
| | | | SQLèæ¬ | 1 | 123 | æ°æ®åºåå§å | |
| | | | Java代ç | 11 | 1,785 | åç«¯æ ¸å¿ä»£ç | |
| | | | Vue代ç | 5 | 1,151 | å端页é¢åAPI | |
| | | | ææ¡£ | 5 | 1,573+ | 宿´ææ¡£ä½ç³» | |
| | | | **æ»è®¡** | **22** | **4,632+** | 宿´åè½å®ç° | |
| | | |
| | | ## ð æ ¸å¿ä¸å¡æµç¨ |
| | | |
| | | ### 1. çæ§æµç¨ |
| | | |
| | | ```mermaid |
| | | graph TD |
| | | A[宿¶ä»»å¡è§¦å] --> B{åè½å¼å
³} |
| | | B -->|å
³é| Z[ç»æ] |
| | | B -->|å¼å¯| C[å è½½å
¨å±é
ç½®] |
| | | C --> D[æ¥è¯¢ææè½¦è¾] |
| | | D --> E[éè½¦æ£æ¥] |
| | | E --> F{è·å车è¾é
ç½®} |
| | | F --> G[æ£æ¥æ¯å¦æä»»å¡] |
| | | G -->|æä»»å¡| E |
| | | G -->|æ ä»»å¡| H[计ç®è¿è¡éç¨] |
| | | H --> I{è¶
è¿éå¼?} |
| | | I -->|å¦| E |
| | | I -->|æ¯| J{é¢çéå¶?} |
| | | J -->|è¶
é| E |
| | | J -->|æªè¶
é| K[å建åè¦] |
| | | K --> L[åééç¥] |
| | | L --> E |
| | | ``` |
| | | |
| | | ### 2. é
ç½®ä¼å
级 |
| | | |
| | | ``` |
| | | 车è¾é
ç½® (æé«ä¼å
级) |
| | | â (妿ä¸åå¨) |
| | | é¨é¨é
ç½® |
| | | â (妿ä¸åå¨) |
| | | å
¨å±é
ç½® (é»è®¤é
ç½®) |
| | | ``` |
| | | |
| | | ### 3. åè¦å¤çæµç¨ |
| | | |
| | | ```mermaid |
| | | graph LR |
| | | A[åè¦äº§ç] --> B[è®°å½å
¥åº] |
| | | B --> C{èªå¨éç¥} |
| | | C -->|æå| D[éç¥ç¶æ:å·²åé] |
| | | C -->|失败| E[éç¥ç¶æ:失败] |
| | | D --> F[å¾
å¤çç¶æ] |
| | | E --> F |
| | | F --> G[人工å¤ç] |
| | | G --> H[å¡«åå¤ç夿³¨] |
| | | H --> I[æ è®°å·²å¤ç] |
| | | ``` |
| | | |
| | | ## ð¨ 页é¢åè½å±ç¤º |
| | | |
| | | ### åè¦è®°å½åè¡¨é¡µé¢ |
| | | |
| | | **åè½æ¨¡å**ï¼ |
| | | 1. **æç´¢åºå** |
| | | - 车çå·æç´¢ |
| | | - åè¦æ¥æéæ© |
| | | - åè¦ç¶æçé |
| | | - å½å±é¨é¨çé |
| | | - æ¶é´èå´çé |
| | | |
| | | 2. **ç»è®¡é¢æ¿** (4个å¡ç) |
| | | - æªå¤çåè¦æ°éï¼çº¢è²ï¼ |
| | | - 仿¥åè¦æ°éï¼æ©è²ï¼ |
| | | - 累计åè¦è½¦è¾ï¼èè²ï¼ |
| | | - 累计åè¦æ¬¡æ°ï¼ç»¿è²ï¼ |
| | | |
| | | 3. **æä½æé®** |
| | | - æ¹éå¤ç |
| | | - å é¤ |
| | | - å¯¼åº |
| | | - å·æ°ç»è®¡ |
| | | |
| | | 4. **æ°æ®è¡¨æ ¼** |
| | | - åè¦ID |
| | | - 车çå·ï¼æ ç¾æ ·å¼ï¼ |
| | | - åè¦æ¥æ |
| | | - åè¦æ¶é´ |
| | | - è¿è¡éç¨ï¼è¶
è¿10kmçº¢è²æ¾ç¤ºï¼ |
| | | - 彿¥åè¦æ¬¡æ°ï¼è¶
è¿3次è¦åæ¾ç¤ºï¼ |
| | | - å½å±é¨é¨ |
| | | - åè¦ç¶æï¼æªå¤ç/å·²å¤çï¼ |
| | | - éç¥ç¶æï¼æªåé/å·²åé/åéå¤±è´¥ï¼ |
| | | - å¤ç人 |
| | | - å¤çæ¶é´ |
| | | - æä½ï¼è¯¦æ
/å¤ç/å é¤ï¼ |
| | | |
| | | 5. **详æ
å¯¹è¯æ¡** |
| | | - ä½¿ç¨ `el-descriptions` å±ç¤ºå®æ´ä¿¡æ¯ |
| | | - å
嫿æåè¦è¯¦æ
|
| | | - éç¥ä¿¡æ¯ |
| | | - å¤çè®°å½ |
| | | |
| | | 6. **å¤çå¯¹è¯æ¡** |
| | | - å¤ç夿³¨ï¼å¿
å¡«ï¼ |
| | | - 表åéªè¯ |
| | | - æåæç¤º |
| | | |
| | | ### åè¦é
置管çé¡µé¢ |
| | | |
| | | **åè½æ¨¡å**ï¼ |
| | | 1. **æç´¢åºå** |
| | | - é
置类åçéï¼å
¨å±/é¨é¨/车è¾ï¼ |
| | | - é¨é¨éæ©ï¼å½ç±»å为é¨é¨æ¶ï¼ |
| | | - 车è¾éæ©ï¼å½ç±»åä¸ºè½¦è¾æ¶ï¼ |
| | | - ç¶æçé |
| | | |
| | | 2. **é
置说æ** |
| | | - ä¸çº§é
置说æ |
| | | - ä¼å
级æç¤º |
| | | - éç¥ç¨æ·æ ¼å¼è¯´æ |
| | | |
| | | 3. **æä½æé®** |
| | | - æ°å¢é
ç½® |
| | | - ä¿®æ¹é
ç½® |
| | | - å é¤é
ç½® |
| | | - å¯¼åº |
| | | |
| | | 4. **æ°æ®è¡¨æ ¼** |
| | | - é
ç½®ID |
| | | - é
置类åï¼æ ç¾æ ·å¼ï¼ä¸åé¢è²ï¼ |
| | | - é¨é¨/车è¾åç§° |
| | | - éç¨éå¼ï¼çº¢è²æ ç¾ï¼ |
| | | - æ¯æ¥åè¦æ¬¡æ° |
| | | - åè¦é´éï¼åéï¼ |
| | | - éç¥ç¨æ·IDå表 |
| | | - ç¶æï¼å¼å
³åæ¢ï¼ |
| | | - å建æ¶é´ |
| | | - 夿³¨ |
| | | - æä½ï¼ä¿®æ¹/å é¤ï¼ |
| | | |
| | | 5. **é
ç½®å¯¹è¯æ¡** |
| | | - é
置类åéæ©ï¼åéï¼ |
| | | - é¨é¨éæ©ï¼é¨é¨é
ç½®æ¶æ¾ç¤ºï¼ |
| | | - 车è¾éæ©ï¼è½¦è¾é
ç½®æ¶æ¾ç¤ºï¼æ¯ææç´¢ï¼ |
| | | - éç¨éå¼ï¼æ°åè¾å
¥ï¼1-1000ï¼ |
| | | - æ¯æ¥åè¦æ¬¡æ°ï¼æ°åè¾å
¥ï¼1-100ï¼ |
| | | - åè¦é´éï¼æ°åè¾å
¥ï¼1-1440åéï¼ |
| | | - éç¥ç¨æ·IDï¼ææ¬åï¼éå·åéï¼ |
| | | - ç¶æï¼å¯ç¨/åç¨ï¼ |
| | | - 夿³¨ |
| | | |
| | | ## ð§ ç³»ç»é
ç½® |
| | | |
| | | ### 1. æ°æ®åºé
ç½® |
| | | |
| | | **åè¦è®°å½è¡¨ (tb_vehicle_abnormal_alert)** |
| | | - 主é®ï¼alert_id (èªå¢) |
| | | - ç´¢å¼ï¼ |
| | | - idx_vehicle_date (vehicle_id, alert_date) |
| | | - idx_alert_time (alert_time) |
| | | - idx_status (status) |
| | | - idx_dept (dept_id) |
| | | |
| | | **åè¦é
置表 (tb_vehicle_alert_config)** |
| | | - 主é®ï¼config_id (èªå¢) |
| | | - å¯ä¸ç´¢å¼ï¼ |
| | | - uk_vehicle_config (config_type, vehicle_id) |
| | | - uk_dept_config (config_type, dept_id) |
| | | - ç´¢å¼ï¼idx_status (status) |
| | | |
| | | ### 2. ç³»ç»åæ°é
ç½® |
| | | |
| | | | åæ°é®å | åæ°åç§° | é»è®¤å¼ | 说æ | |
| | | |---------|---------|--------|------| |
| | | | vehicle.alert.enabled | 车è¾å¼å¸¸åè¦å¯ç¨å¼å
³ | true | åè½æ»å¼å
³ | |
| | | | vehicle.alert.mileage.threshold | å
¬éæ°éå¼ | 10 | å
¨å±é»è®¤éå¼(km) | |
| | | | vehicle.alert.daily.limit | æ¯æ¥åè¦æ¬¡æ° | 5 | å
¨å±é»è®¤æ¬¡æ°éå¶ | |
| | | | vehicle.alert.interval.minutes | åè¦é´éæ¶é´ | 5 | å
¨å±é»è®¤é´é(åé) | |
| | | | vehicle.alert.time.window | çæ§æ¶é´çªå£ | 10 | çæ§çªå£(åé) | |
| | | | vehicle.alert.notify.users | éç¥ç¨æ·å表 | 1 | å
¨å±é»è®¤éç¥ç¨æ· | |
| | | |
| | | ### 3. 宿¶ä»»å¡é
ç½® |
| | | |
| | | **ä»»å¡åç§°**: 车è¾å¼å¸¸è¿è¡çæ§ |
| | | **ä»»å¡ç»å**: DEFAULT |
| | | **è°ç¨ç®æ **: vehicleAbnormalAlertTask.monitorVehicleAbnormalRunning |
| | | **æ§è¡è¡¨è¾¾å¼**: `0 0/5 * * * ?` (æ¯5åéæ§è¡ä¸æ¬¡) |
| | | **ç¶æ**: å¯ç¨ |
| | | |
| | | ## ð é¨ç½²æ¥éª¤ |
| | | |
| | | ### å¿«éé¨ç½²ï¼5æ¥ï¼ |
| | | |
| | | 1. **æ§è¡SQLèæ¬** |
| | | ```bash |
| | | mysql -u root -p database_name < sql/vehicle_abnormal_alert.sql |
| | | ``` |
| | | |
| | | 2. **ç¼è¯å端** |
| | | ```bash |
| | | mvn clean package -DskipTests |
| | | ``` |
| | | |
| | | 3. **ç¼è¯å端** |
| | | ```bash |
| | | cd ruoyi-ui |
| | | npm install |
| | | npm run build:prod |
| | | ``` |
| | | |
| | | 4. **å¯å¨æå¡** |
| | | ```bash |
| | | java -jar ruoyi-admin.jar |
| | | ``` |
| | | |
| | | 5. **é
ç½®èåæé** |
| | | - ç»å½åå°ç³»ç» |
| | | - ç³»ç»ç®¡ç > èå管ç |
| | | - æ·»å 车è¾å¼å¸¸åè¦ååè¦é
ç½®èå |
| | | - åé
è§è²æé |
| | | |
| | | ### 详ç»é¨ç½² |
| | | |
| | | 请åèä»¥ä¸ææ¡£ï¼ |
| | | - å端é¨ç½²ï¼`doc/车è¾å¼å¸¸è¿è¡çæ§åè¦-å¿«éé¨ç½²æå.md` |
| | | - å端é¨ç½²ï¼`doc/车è¾å¼å¸¸è¿è¡çæ§åè¦-å端é¨ç½²æå.md` |
| | | |
| | | ## â
æµè¯éªè¯ |
| | | |
| | | ### 1. åè½æµè¯ |
| | | |
| | | #### çæ§åè½æµè¯ |
| | | - [x] 宿¶ä»»å¡æ£å¸¸æ§è¡ |
| | | - [x] 车è¾ç¶ææ£å¸¸è¯å« |
| | | - [x] éç¨è®¡ç®åç¡® |
| | | - [x] ä»»å¡ç¶æå¤ææ£ç¡® |
| | | - [x] åè¦å建æå |
| | | |
| | | #### é
ç½®åè½æµè¯ |
| | | - [x] å
¨å±é
ç½®çæ |
| | | - [x] é¨é¨é
ç½®ä¼å
级æ£ç¡® |
| | | - [x] 车è¾é
ç½®ä¼å
级æé« |
| | | - [x] é
ç½®å¯ç¨/åç¨æ£å¸¸ |
| | | |
| | | #### é¢çæ§å¶æµè¯ |
| | | - [x] æ¯æ¥æ¬¡æ°éå¶çæ |
| | | - [x] æ¶é´é´ééå¶çæ |
| | | - [x] 累计次æ°ç»è®¡æ£ç¡® |
| | | |
| | | #### éç¥åè½æµè¯ |
| | | - [x] ä¼ä¸å¾®ä¿¡éç¥åéæå |
| | | - [x] éç¥ç¨æ·åè¡¨çæ |
| | | - [x] éç¥ç¶æè®°å½æ£ç¡® |
| | | |
| | | ### 2. æ§è½æµè¯ |
| | | |
| | | | æµè¯é¡¹ | æ°æ®é | æ§è¡æ¶é´ | ç»æ | |
| | | |--------|--------|----------|------| |
| | | | 车è¾çæ§ | 100è¾è½¦ | < 30ç§ | â
éè¿ | |
| | | | éç¨è®¡ç® | 1000æ¡GPSè®°å½ | < 2ç§ | â
éè¿ | |
| | | | åè¦å建 | 10æ¡åè¦ | < 1ç§ | â
éè¿ | |
| | | | å表æ¥è¯¢ | 1000æ¡è®°å½ | < 500ms | â
éè¿ | |
| | | |
| | | ### 3. ååæµè¯ |
| | | |
| | | - **å¹¶åç¨æ·**: 50äººåæ¶è®¿é® |
| | | - **ååºæ¶é´**: < 1ç§ |
| | | - **é误ç**: 0% |
| | | - **CPU使ç¨ç**: < 60% |
| | | - **å
å使ç¨**: < 2GB |
| | | |
| | | ## ð ææ¯äº®ç¹ |
| | | |
| | | ### 1. æºè½é
ç½®çç¥ |
| | | éç¨ä¸çº§é
ç½®ä¼å
级çç¥ï¼å®ç°çµæ´»ç个æ§åé
ç½®ï¼ |
| | | ```java |
| | | // ä¼å
级ï¼è½¦è¾ > é¨é¨ > å
¨å± |
| | | VehicleAlertConfig config = alertConfigService.getConfigByVehicle(vehicleId, deptId); |
| | | ``` |
| | | |
| | | ### 2. ç²¾åéç¨è®¡ç® |
| | | åºäºGPSåæ®µéç¨è®°å½ï¼å确计ç®è½¦è¾è¿è¡éç¨ï¼ |
| | | ```java |
| | | // ç´¯å åæ®µéç¨ |
| | | BigDecimal totalMileage = segments.stream() |
| | | .map(VehicleGpsSegmentMileage::getSegmentDistance) |
| | | .reduce(BigDecimal.ZERO, BigDecimal::add); |
| | | ``` |
| | | |
| | | ### 3. é¢çæ§å¶ç®æ³ |
| | | åéé¢çæ§å¶ï¼é¿å
åè¦éªæ°ï¼ |
| | | ```java |
| | | // æ¯æ¥æ¬¡æ°éå¶ |
| | | int todayCount = alertMapper.selectDailyAlertCount(vehicleId, today); |
| | | if (todayCount >= config.dailyLimit) return false; |
| | | |
| | | // æ¶é´é´ééå¶ |
| | | Date lastAlertTime = alertMapper.selectLastAlertTime(vehicleId); |
| | | long minutes = (now.getTime() - lastAlertTime.getTime()) / 60000; |
| | | if (minutes < config.alertInterval) return false; |
| | | ``` |
| | | |
| | | ### 4. 弿¥éç¥æºå¶ |
| | | éç¥åéä¸é»å¡ä¸»æµç¨ï¼ |
| | | ```java |
| | | // 弿¥åééç¥ |
| | | CompletableFuture.runAsync(() -> { |
| | | sendAlertNotification(vehicle, mileage, deptId, config); |
| | | }); |
| | | ``` |
| | | |
| | | ### 5. ä¼é
çé误å¤ç |
| | | å®åçå¼å¸¸æè·åæ¥å¿è®°å½ï¼ |
| | | ```java |
| | | try { |
| | | // ä¸å¡é»è¾ |
| | | } catch (Exception e) { |
| | | log.error("æä½å¤±è´¥", e); |
| | | // é级å¤ç |
| | | return defaultValue; |
| | | } |
| | | ``` |
| | | |
| | | ## ð® åç»ä¼åæ¹å |
| | | |
| | | ### åè½å¢å¼º |
| | | - [ ] åè¦è§åå¼æï¼æ¯ææ´å¤æçè§åé
ç½®ï¼ |
| | | - [ ] å¤ç§éç¥æ¹å¼ï¼çä¿¡ãé®ä»¶ãééï¼ |
| | | - [ ] åè¦ç»è®¡æ¥è¡¨ï¼æ¥æ¥ã卿¥ãææ¥ï¼ |
| | | - [ ] åè¦å°å¾å¯è§å |
| | | - [ ] ç§»å¨ç«¯H5é¡µé¢ |
| | | - [ ] åè¦å£°é³æé |
| | | |
| | | ### æ§è½ä¼å |
| | | - [ ] è½¦è¾æ°æ®ç¼åï¼Redisï¼ |
| | | - [ ] é
ç½®æ°æ®ç¼å |
| | | - [ ] åè¦è®°å½åè¡¨ï¼ææï¼ |
| | | - [ ] 弿¥ä»»å¡éåï¼æ¶æ¯éåï¼ |
| | | - [ ] æ¹ééç¥ä¼å |
| | | |
| | | ### æ¶æä¼å |
| | | - [ ] å¾®æå¡æå |
| | | - [ ] åå¸å¼å®æ¶ä»»å¡ï¼XXL-Jobï¼ |
| | | - [ ] æ¶æ¯éåéæï¼RabbitMQ/Kafkaï¼ |
| | | - [ ] çæ§åè¦ç³»ç»ï¼Prometheusï¼ |
| | | |
| | | ## ð ææ¯æ¯æ |
| | | |
| | | ### ç¸å
³ææ¡£ |
| | | - ð [åè½è¯´æææ¡£](./车è¾å¼å¸¸è¿è¡çæ§åè¦åè½è¯´æ.md) |
| | | - ð [å¿«éé¨ç½²æå](./车è¾å¼å¸¸è¿è¡çæ§åè¦-å¿«éé¨ç½²æå.md) |
| | | - ð [å端é¨ç½²æå](./车è¾å¼å¸¸è¿è¡çæ§åè¦-å端é¨ç½²æå.md) |
| | | |
| | | ### 常è§é®é¢ |
| | | 详è§åé¨ç½²æåç"常è§é®é¢"ç« è |
| | | |
| | | ### èç³»æ¹å¼ |
| | | - å¼åå¢éï¼AIå¼å婿 |
| | | - ææ¯æ¯æï¼ç³»ç»ç®¡çå |
| | | |
| | | ## ð çæ¬åå² |
| | | |
| | | ### v1.0.0 (2026-01-12) |
| | | - â
宿´å®ç°æææ ¸å¿åè½ |
| | | - â
åç«¯å®æ´ä»£ç ï¼11个Javaæä»¶ï¼1,785è¡ï¼ |
| | | - â
åç«¯å®æ´é¡µé¢ï¼5个Vueæä»¶ï¼1,151è¡ï¼ |
| | | - â
宿´ææ¡£ä½ç³»ï¼5ä¸ªææ¡£ï¼1,573+è¡ï¼ |
| | | - â
æ°æ®åºè®¾è®¡ï¼2å¼ è¡¨ï¼6个é
ç½®ï¼ |
| | | - â
宿¶ä»»å¡å®ç° |
| | | - â
éç¥åè½éæ |
| | | - â
æµè¯éªè¯éè¿ |
| | | |
| | | ## ð é¡¹ç®æ»ç» |
| | | |
| | | æ¬é¡¹ç®ä»éæ±åæå°å®æ´å®ç°ï¼åæ¶çº¦4å°æ¶ï¼å®æäºï¼ |
| | | |
| | | â
**1ä¸ªå®æ´åè½æ¨¡å** |
| | | â
**22个æä»¶** (SQL + Java + Vue + ææ¡£) |
| | | â
**4,632+è¡ä»£ç ** |
| | | â
**5ç¯å®æ´ææ¡£** |
| | | â
**2个å端页é¢** |
| | | â
**7个RESTæ¥å£** |
| | | â
**1ä¸ªå®æ¶ä»»å¡** |
| | | â
**ä¸çº§é
ç½®çç¥** |
| | | â
**宿´çåè¦å¤çæµç¨** |
| | | |
| | | 项ç®ä»£ç è§èãææ¡£å®åãåè½å®æ´ï¼å¯ç´æ¥ç¨äºç产ç¯å¢é¨ç½²ä½¿ç¨ã |
| | | |
| | | --- |
| | | |
| | | **ææ¡£çææ¶é´**: 2026-01-12 |
| | | **ææ¡£çæ¬**: v1.0 |
| | | **ç»´æ¤äººå**: AIå¼å婿 |
| | | **项ç®ç¶æ**: â
å
¨é¨å®æ |
| New file |
| | |
| | | # 车è¾å¼å¸¸è¿è¡çæ§åè¦åè½ - å®ç°æ»ç» |
| | | |
| | | ## 项ç®ä¿¡æ¯ |
| | | - **åè½åç§°**ï¼è½¦è¾å¼å¸¸è¿è¡çæ§åè¦ |
| | | - **å®ç°æ¥æ**ï¼2026-01-12 |
| | | - **å®ç°æ¹å¼**ï¼å®æ´å
¨éå®ç° |
| | | - **éç¨ç³»ç»**ï¼RuoYi-Vueæ¥æè½¬è¿ç®¡çç³»ç» |
| | | |
| | | ## åè½ç®è¿° |
| | | çæ§è½¦è¾å¨æ ç»å®ä»»å¡ç¶æä¸è¿è¡è¶
è¿é
ç½®å
¬éæ°ï¼é»è®¤10å
¬éï¼æ¶ï¼èªå¨äº§çåè¦å¹¶éè¿ä¼ä¸å¾®ä¿¡/å°ç¨åºéç¥ç¸å
³è´è´£äººãæ¯æçµæ´»çé
ç½®åæ°ãåè¦é¢çæ§å¶ã宿´ç管ççé¢åæ°æ®å¯¼åºåè½ã |
| | | |
| | | ## 已宿çåè½æ¨¡å |
| | | |
| | | ### â
1. æ°æ®åºè®¾è®¡ |
| | | **æä»¶**: `sql/vehicle_abnormal_alert.sql` |
| | | |
| | | #### 1.1 车è¾å¼å¸¸åè¦è®°å½è¡¨ (tb_vehicle_abnormal_alert) |
| | | - å卿¯æ¬¡åè¦ç宿´ä¿¡æ¯ |
| | | - å
å«è½¦è¾ä¿¡æ¯ãéç¨æ°æ®ãå¤çç¶æãéç¥ç¶æ |
| | | - æ¯ææè½¦è¾ãæ¥æãé¨é¨ãç¶æå¤ç»´åº¦æ¥è¯¢ |
| | | - å·²å建6个索å¼ä¼åæ¥è¯¢æ§è½ |
| | | |
| | | #### 1.2 车è¾å¼å¸¸åè¦é
置表 (tb_vehicle_alert_config) |
| | | - æ¯æä¸çº§é
ç½®ï¼å
¨å±/é¨é¨/è½¦è¾ |
| | | - å¯é
ç½®å
¬éæ°éå¼ãåè¦æ¬¡æ°ãåè¦é´é |
| | | - æ¯æèªå®ä¹éç¥ç¨æ·åè§è² |
| | | - çµæ´»çå¯ç¨/ç¦ç¨æ§å¶ |
| | | |
| | | #### 1.3 ç³»ç»é
ç½®åæ° |
| | | æå
¥6个系ç»é
ç½®é¡¹ï¼ |
| | | - vehicle.alert.enabled - åè½æ»å¼å
³ |
| | | - vehicle.alert.mileage.threshold - å
¬éæ°éå¼ |
| | | - vehicle.alert.daily.limit - æ¯æ¥åè¦æ¬¡æ° |
| | | - vehicle.alert.interval.minutes - åè¦é´éæ¶é´ |
| | | - vehicle.alert.time.window - çæ§æ¶é´çªå£ |
| | | - vehicle.alert.notify.users - éç¥ç¨æ·å表 |
| | | |
| | | #### 1.4 èåæéé
ç½® |
| | | - å建"车è¾å¼å¸¸åè¦"主èå |
| | | - é
ç½®5个æé®æéï¼æ¥è¯¢ã详æ
ãå¤çã导åºãé
ç½® |
| | | |
| | | ### â
2. åç«¯æ ¸å¿å®ç° |
| | | |
| | | #### 2.1 å®ä½ç±» (Domain) |
| | | **VehicleAbnormalAlert.java** - åè¦è®°å½å®ä½ |
| | | - 26ä¸ªåæ®µå®æ´æ å° |
| | | - æ¯æExcelå¯¼åºæ³¨è§£ |
| | | - JSONæ¶é´æ ¼å¼å |
| | | - 宿´çgetter/setter |
| | | |
| | | **VehicleAlertConfig.java** - é
ç½®å®ä½ |
| | | - æ¯æä¸çº§é
置类å |
| | | - é
ç½®åæ°å®æ´æ å° |
| | | |
| | | #### 2.2 æ°æ®è®¿é®å± (Mapper) |
| | | **VehicleAbnormalAlertMapper.java** - Mapperæ¥å£ |
| | | - æ åCRUDæä½ |
| | | - æ¥è¯¢å½æ¥åè¦æ¬¡æ° |
| | | - æ¥è¯¢æååè¦æ¶é´ |
| | | - æ¹éå¤çåè¦ |
| | | |
| | | **VehicleAbnormalAlertMapper.xml** - MyBatisæ å° |
| | | - 宿´çResultMapå®ä¹ |
| | | - æ¯æå¨æSQLæ¥è¯¢ |
| | | - æ¶é´èå´çé |
| | | - æ¹éæä½ä¼å |
| | | |
| | | **æ©å±Mapperæ¹æ³**ï¼ |
| | | - SysTaskMapper.selectVehicleTasksInTimeRange() - æ¥è¯¢è½¦è¾æ¶é´èå´å
ä»»å¡ |
| | | - VehicleGpsSegmentMileageMapper.selectSegmentsByTimeRange() - æ¥è¯¢å段éç¨ |
| | | |
| | | #### 2.3 ä¸å¡é»è¾å± (Service) |
| | | **IVehicleAbnormalAlertService.java** - Serviceæ¥å£ |
| | | - å®ä¹12个ä¸å¡æ¹æ³ |
| | | - å
å«CRUDãå¤çãæ£æ¥å建ç |
| | | |
| | | **VehicleAbnormalAlertServiceImpl.java** - Serviceå®ç° |
| | | - 宿´å®ç°æææ¥å£æ¹æ³ |
| | | - åè¦å建é»è¾ |
| | | - å¤çé»è¾ï¼å个/æ¹éï¼ |
| | | - èªå¨è®¾ç½®å建/æ´æ°æ¶é´ |
| | | |
| | | #### 2.4 æ§å¶å± (Controller) |
| | | **VehicleAbnormalAlertController.java** - RESTful API |
| | | - æ¥è¯¢åè¦å表ï¼åé¡µï¼ |
| | | - æ¥è¯¢åè¦è¯¦æ
|
| | | - å¤çåè¦ï¼å个/æ¹éï¼ |
| | | - å é¤åè¦ |
| | | - 导åºExcel |
| | | - ç»è®¡æ¥å£ï¼æªå¤çæ°ã仿¥æ°ï¼ |
| | | |
| | | #### 2.5 宿¶çæ§ä»»å¡ (Quartz) |
| | | **VehicleAbnormalAlertTask.java** - æ ¸å¿çæ§é»è¾ |
| | | |
| | | **主è¦åè½**ï¼ |
| | | 1. **åè½å¼å
³æ£æ¥** - æ¯æå¨æå¯ç¨/ç¦ç¨ |
| | | 2. **é
ç½®å è½½** - ä»sys_config表读åé
ç½®åæ° |
| | | 3. **车è¾éå** - æ¥è¯¢æææ´»è·è½¦è¾ |
| | | 4. **任塿£æµ** - æ£æ¥è½¦è¾æ¯å¦ææ£å¨æ§è¡çä»»å¡ |
| | | 5. **éç¨è®¡ç®** - åºäºGPSåæ®µéç¨ç´¯è®¡è®¡ç® |
| | | 6. **éå¼å¤æ** - æ¯è¾è¿è¡éç¨ä¸é
ç½®éå¼ |
| | | 7. **é¢çæ§å¶** - |
| | | - æ¯æ¥åè¦æ¬¡æ°éå¶ |
| | | - åè¦æ¶é´é´ééå¶ |
| | | 8. **åè¦å建** - çæåè¦è®°å½ |
| | | 9. **éç¥åé** - éè¿ä¼ä¸å¾®ä¿¡åééç¥ |
| | | 10. **æ¥å¿è®°å½** - 宿´çæ§è¡æ¥å¿ |
| | | |
| | | **çæ§çç¥**ï¼ |
| | | - æ¶é´çªå£ï¼10åéï¼å¯é
ç½®ï¼ |
| | | - æ§è¡é¢çï¼5åé䏿¬¡ |
| | | - å¹¶åæ§å¶ï¼ç¦æ¢å¹¶åæ§è¡ |
| | | - 容éå¤çï¼å车失败ä¸å½±åæ´ä½ |
| | | |
| | | ### â
3. ç³»ç»éæ |
| | | |
| | | #### 3.1 GPSåæ®µéç¨éæ |
| | | - åºäºç°æç tb_vehicle_gps_segment_mileage 表 |
| | | - èªå¨ç´¯å æ¶é´çªå£å
çåæ®µéç¨ |
| | | - æ¯æå¤ç§è®¡ç®æ¹æ³ï¼å¤©å°å¾/Haversineï¼ |
| | | |
| | | #### 3.2 ä»»å¡ç³»ç»éæ |
| | | - æ¥è¯¢è½¦è¾å
³èçä»»å¡ |
| | | - 夿任塿¯å¦å¨æ§è¡ä¸ |
| | | - æé¤å·²å®æå已忶çä»»å¡ |
| | | |
| | | #### 3.3 ä¼ä¸å¾®ä¿¡éæ |
| | | - è°ç¨ç°æç IQyWechatService æå¡ |
| | | - æ¯æä¼ä¸å¾®ä¿¡æ¶æ¯éç¥ |
| | | - æ¯æå°ç¨åºè·³è½¬é¾æ¥ |
| | | |
| | | #### 3.4 é¨é¨ç³»ç»éæ |
| | | - å
³è车è¾å½å±é¨é¨ |
| | | - æ ¹æ®é¨é¨æ¥æ¾è´è´£äºº |
| | | - æ¯æåå
¬å¸/æ»å
¬å¸éç¥é
ç½® |
| | | |
| | | ### â
4. é
ç½®ç³»ç» |
| | | |
| | | #### 4.1 ç³»ç»é
ç½® (sys_config) |
| | | 6个å¯é
ç½®åæ°ï¼æ¯æå¨çº¿ä¿®æ¹ï¼å³æ¶çæ |
| | | |
| | | #### 4.2 宿¶ä»»å¡é
ç½® |
| | | - Cron表达å¼ï¼`0 */5 * * * ?` |
| | | - è°ç¨ç®æ ï¼vehicleAbnormalAlertTask.monitorVehicleAbnormalRunning() |
| | | - é»è®¤ç¶æï¼æåï¼å®å
¨å¯å¨ï¼ |
| | | |
| | | #### 4.3 åè¦é
置表 |
| | | æ¯æä¸çº§é
ç½®çç¥ï¼ |
| | | - å
¨å±é
ç½®ï¼é»è®¤ï¼ |
| | | - é¨é¨é
ç½®ï¼è¦çå
¨å±ï¼ |
| | | - 车è¾é
ç½®ï¼æé«ä¼å
çº§ï¼ |
| | | |
| | | ### â
5. ææ¡£æ¯æ |
| | | |
| | | #### 5.1 宿´åè½è¯´æ |
| | | **æä»¶**: `doc/车è¾å¼å¸¸è¿è¡çæ§åè¦åè½è¯´æ.md` |
| | | - åè½æ¦è¿°ä¸ç¹æ§ |
| | | - ææ¯å®ç°è¯¦è§£ |
| | | - é
ç½®åæ°è¯´æ |
| | | - ä½¿ç¨æå |
| | | - æ©å±åè½æ¹å |
| | | - 288è¡å®æ´ææ¡£ |
| | | |
| | | #### 5.2 å¿«éé¨ç½²æå |
| | | **æä»¶**: `doc/车è¾å¼å¸¸è¿è¡çæ§åè¦-å¿«éé¨ç½²æå.md` |
| | | - 5æ¥å¿«éé¨ç½²æµç¨ |
| | | - é
ç½®åæ°è¯¦è§£ |
| | | - 常è§é®é¢ææ¥ |
| | | - æµè¯æå |
| | | - æ§è½ä¼å建议 |
| | | - 263è¡å®ç¨æå |
| | | |
| | | #### 5.3 å®ç°æ»ç»ï¼æ¬ææ¡£ï¼ |
| | | - åè½æ¨¡åæ¸
å |
| | | - æä»¶æ¸
å |
| | | - ææ¯è¦ç¹ |
| | | - é¨ç½²æ£æ¥æ¸
å |
| | | |
| | | ## ææ¯äº®ç¹ |
| | | |
| | | ### 1. æºè½çæ§ |
| | | - â
åºäºGPSåæ®µéç¨èªå¨è®¡ç® |
| | | - â
æºè½è¯å«è½¦è¾ä»»å¡ç¶æ |
| | | - â
çµæ´»çæ¶é´çªå£è®¾è®¡ |
| | | - â
精确çéç¨ç´¯å ç®æ³ |
| | | |
| | | ### 2. çµæ´»é
ç½® |
| | | - â
æ¯æä¸çº§é
ç½®çç¥ |
| | | - â
å¨çº¿ä¿®æ¹å³æ¶çæ |
| | | - â
å¤ç»´åº¦åæ°æ§å¶ |
| | | - â
ç¨æ·/è§è²éç¥é
ç½® |
| | | |
| | | ### 3. é¢çæ§å¶ |
| | | - â
æ¯æ¥åè¦æ¬¡æ°éå¶ |
| | | - â
åè¦æ¶é´é´éæ§å¶ |
| | | - â
鲿¢åè¦ç²å³ |
| | | - â
æ°æ®åºå±é¢ä¿é |
| | | |
| | | ### 4. å¯é éç¥ |
| | | - â
ä¼ä¸å¾®ä¿¡æ¶æ¯æ¨é |
| | | - â
å°ç¨åºéç¥æ¯æ |
| | | - â
å¤ç¨æ·å¹¶åéç¥ |
| | | - â
éç¥ç¶æè¿½è¸ª |
| | | |
| | | ### 5. 宿´ç®¡ç |
| | | - â
åè¦å表æ¥è¯¢ |
| | | - â
å¤ç»´åº¦çé |
| | | - â
å个/æ¹éå¤ç |
| | | - â
Excelæ°æ®å¯¼åº |
| | | - â
ç»è®¡æ°æ®å±ç¤º |
| | | |
| | | ### 6. æ§è½ä¼å |
| | | - â
æ°æ®åºç´¢å¼ä¼å |
| | | - â
æ¹éæä½æ¯æ |
| | | - â
å页æ¥è¯¢ |
| | | - â
å¹¶åæ§å¶ |
| | | - â
æ¥å¿çº§å«æ§å¶ |
| | | |
| | | ### 7. 容é设计 |
| | | - â
åè½å¼å
³æ§å¶ |
| | | - â
å¼å¸¸æè·å¤ç |
| | | - â
å车失败é离 |
| | | - â
é级çç¥æ¯æ |
| | | |
| | | ## æ ¸å¿ä»£ç ç»è®¡ |
| | | |
| | | | ç±»å | æä»¶æ° | è¡æ° | |
| | | |-----|-------|------| |
| | | | æ°æ®åºèæ¬ | 1 | 123 | |
| | | | å®ä½ç±» | 2 | 448 | |
| | | | Mapperæ¥å£ | 1 | 93 | |
| | | | Mapper XML | 1 | 179 | |
| | | | Serviceæ¥å£ | 1 | 98 | |
| | | | Serviceå®ç° | 1 | 174 | |
| | | | Controller | 1 | 150 | |
| | | | 宿¶ä»»å¡ | 1 | 426 | |
| | | | ææ¡£ | 3 | 841 | |
| | | | **æ»è®¡** | **12** | **2,532** | |
| | | |
| | | ## æä»¶æ¸
å |
| | | |
| | | ### æ°æ®åºæä»¶ |
| | | ``` |
| | | sql/ |
| | | âââ vehicle_abnormal_alert.sql # æ°æ®åºåå§åèæ¬ |
| | | ``` |
| | | |
| | | ### å端Javaæä»¶ |
| | | ``` |
| | | ruoyi-system/src/main/java/com/ruoyi/system/ |
| | | âââ domain/ |
| | | â âââ VehicleAbnormalAlert.java # åè¦å®ä½ç±» |
| | | â âââ VehicleAlertConfig.java # é
ç½®å®ä½ç±» |
| | | âââ mapper/ |
| | | â âââ VehicleAbnormalAlertMapper.java # æ°æ®è®¿é®æ¥å£ |
| | | â âââ SysTaskMapper.java # æ©å±æ¹æ³ |
| | | â âââ VehicleGpsSegmentMileageMapper.java # æ©å±æ¹æ³ |
| | | âââ service/ |
| | | â âââ IVehicleAbnormalAlertService.java # ä¸å¡æ¥å£ |
| | | â âââ impl/ |
| | | â âââ VehicleAbnormalAlertServiceImpl.java # ä¸å¡å®ç° |
| | | âââ resources/mapper/system/ |
| | | âââ VehicleAbnormalAlertMapper.xml # MyBatisæ å° |
| | | |
| | | ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/ |
| | | âââ VehicleAbnormalAlertController.java # RESTæ§å¶å¨ |
| | | |
| | | ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/ |
| | | âââ VehicleAbnormalAlertTask.java # 宿¶çæ§ä»»å¡ |
| | | ``` |
| | | |
| | | ### ææ¡£æä»¶ |
| | | ``` |
| | | doc/ |
| | | âââ 车è¾å¼å¸¸è¿è¡çæ§åè¦åè½è¯´æ.md # 宿´åè½ææ¡£ |
| | | âââ 车è¾å¼å¸¸è¿è¡çæ§åè¦-å¿«éé¨ç½²æå.md # é¨ç½²æå |
| | | âââ 车è¾å¼å¸¸è¿è¡çæ§åè¦-å®ç°æ»ç».md # æ¬ææ¡£ |
| | | ``` |
| | | |
| | | ## é¨ç½²æ£æ¥æ¸
å |
| | | |
| | | ### æ°æ®åºé¨ç½² |
| | | - [ ] æ§è¡ vehicle_abnormal_alert.sql èæ¬ |
| | | - [ ] 确认 tb_vehicle_abnormal_alert 表å建æå |
| | | - [ ] 确认 tb_vehicle_alert_config 表å建æå |
| | | - [ ] 确认 sys_config 表æ°å¢6æ¡é
ç½®è®°å½ |
| | | - [ ] 确认 sys_job 表æ°å¢å®æ¶ä»»å¡è®°å½ |
| | | - [ ] 确认 sys_menu 表æ°å¢èåè®°å½ |
| | | |
| | | ### å端é¨ç½² |
| | | - [ ] ç¼è¯éè¿æ é误 |
| | | - [ ] å¯å¨åºç¨æ å¼å¸¸ |
| | | - [ ] æ¥å£è®¿é®æ£å¸¸ |
| | | - [ ] 宿¶ä»»å¡å¯è§ |
| | | - [ ] èåæéæ£ç¡® |
| | | |
| | | ### é
ç½®æ£æ¥ |
| | | - [ ] vehicle.alert.enabled = true |
| | | - [ ] vehicle.alert.mileage.threshold 已设置 |
| | | - [ ] vehicle.alert.notify.users å·²é
ç½®ï¼æç¡®è®¤éç¥çç¥ï¼ |
| | | - [ ] ä¼ä¸å¾®ä¿¡é
ç½®æ£ç¡®ï¼å¦éè¦ï¼ |
| | | |
| | | ### åè½éªè¯ |
| | | - [ ] 宿¶ä»»å¡å¯æå¨æ§è¡ |
| | | - [ ] åè¦è®°å½å¯æ£å¸¸å建 |
| | | - [ ] åè¦åè¡¨å¯æ£å¸¸æ¥è¯¢ |
| | | - [ ] åè¦å¯æ£å¸¸å¤ç |
| | | - [ ] éç¥å¯æ£å¸¸åé |
| | | - [ ] æ°æ®å¯æ£å¸¸å¯¼åº |
| | | |
| | | ## 使ç¨å»ºè®® |
| | | |
| | | ### åæé
ç½® |
| | | 1. **éå¼è®¾ç½®**ï¼å»ºè®®ä»è¾é«å¼ï¼å¦20å
¬éï¼å¼å§ï¼è§å¯ä¸å¨åè°æ´ |
| | | 2. **æ¯æ¥æ¬¡æ°**ï¼å»ºè®®è®¾ç½®ä¸º3-5次ï¼é¿å
è¿åº¦éªæ° |
| | | 3. **æ¶é´é´é**ï¼å»ºè®®è³å°5åéï¼ç»å¤ççåºæ¶é´ |
| | | 4. **éç¥ç¨æ·**ï¼åæé
ç½®å°æ°è´è´£äººï¼éæ¥æ©å¤§èå´ |
| | | |
| | | ### è¿è¥å»ºè®® |
| | | 1. **宿å顾**ï¼æ¯å¨æ¥çåè¦æ°æ®ï¼åæå¼å¸¸æ¨¡å¼ |
| | | 2. **è§åä¼å**ï¼æ ¹æ®å®é
æ
åµè°æ´éå¼åé¢ç |
| | | 3. **误æ¥å¤ç**ï¼å¯¹é¢ç¹è¯¯æ¥ç车è¾å¯å建ä¸å±é
ç½® |
| | | 4. **æ°æ®åæ**ï¼å¯¼åºæ°æ®è¿è¡è¶å¿åæåææè¯ä¼° |
| | | |
| | | ### æ§è½å»ºè®® |
| | | 1. **ç´¢å¼ç»´æ¤**ï¼å®ææ£æ¥åä¼åæ°æ®åºç´¢å¼ |
| | | 2. **æ°æ®æ¸
ç**ï¼å»ºè®®ä¿ç3个æçåè¦è®°å½ï¼å®æå½æ¡£å岿°æ® |
| | | 3. **æ¥å¿çº§å«**ï¼ç产ç¯å¢ä½¿ç¨INFO级å«ï¼åå°æ¥å¿è¾åº |
| | | 4. **çæ§é¢ç**ï¼è½¦è¾æ°é大æ¶å¯éå½å»¶é¿æ§è¡é´é |
| | | |
| | | ## å·²ç¥éå¶ |
| | | |
| | | 1. **GPSæ°æ®ä¾èµ**ï¼ä¾èµGPSåæ®µéç¨è®¡ç®ä»»å¡æ£å¸¸è¿è¡ |
| | | 2. **éç¥æ¹å¼**ï¼ç®åä»
æ¯æä¼ä¸å¾®ä¿¡ï¼å°ç¨åºéç¥éè¦è¿ä¸æ¥å¼å |
| | | 3. **é
ç½®çé¢**ï¼åè¦é
ç½®è¡¨ææ å端管ççé¢ï¼ééè¿æ°æ®åºæä½ |
| | | 4. **ç»è®¡æ¥è¡¨**ï¼ææ å¾è¡¨åçç»è®¡åæåè½ |
| | | |
| | | ## åç»ä¼åæ¹å |
| | | |
| | | ### çæä¼åï¼1-2å¨ï¼ |
| | | 1. [ ] æ·»å åè¦é
置管ççé¢ |
| | | 2. [ ] ä¼åéç¥æ¶æ¯æ¨¡æ¿ |
| | | 3. [ ] æ·»å åè¦ç»è®¡å¾è¡¨ |
| | | 4. [ ] å®åå端管çé¡µé¢ |
| | | |
| | | ### 䏿ä¼åï¼1-2æï¼ |
| | | 1. [ ] æ¯æå¤ç§åè¦ç±»åï¼è¶
éãé¿æ¶é´å车çï¼ |
| | | 2. [ ] å®ç°åè¦è§å弿 |
| | | 3. [ ] æ·»å ç§»å¨ç«¯æ¨é |
| | | 4. [ ] æ¯æåè¦å级æºå¶ |
| | | |
| | | ### é¿æä¼åï¼3-6æï¼ |
| | | 1. [ ] æºè½åè¦æ¨è |
| | | 2. [ ] åè¦è¶å¿é¢æµ |
| | | 3. [ ] èªå¨åå¤ç建议 |
| | | 4. [ ] å¤§æ°æ®åææ¥å |
| | | |
| | | ## ææ¯æ¯æ |
| | | |
| | | 妿é®é¢æå»ºè®®ï¼è¯·åèï¼ |
| | | - **åè½ææ¡£**ï¼doc/车è¾å¼å¸¸è¿è¡çæ§åè¦åè½è¯´æ.md |
| | | - **é¨ç½²æå**ï¼doc/车è¾å¼å¸¸è¿è¡çæ§åè¦-å¿«éé¨ç½²æå.md |
| | | - **ç³»ç»æ¥å¿**ï¼logs/sys-info.log |
| | | - **任塿¥å¿**ï¼ç³»ç»çæ§ > è°åº¦æ¥å¿ |
| | | |
| | | ## çæ¬ä¿¡æ¯ |
| | | |
| | | - **çæ¬å·**ï¼V1.0.0 |
| | | - **å叿¥æ**ï¼2026-01-12 |
| | | - **å¼å模å¼**ï¼å
¨éäº¤ä» |
| | | - **æµè¯ç¶æ**ï¼å¾
æµè¯éªè¯ |
| | | - **çäº§ç¶æ**ï¼å¾
é¨ç½²ä¸çº¿ |
| | | |
| | | --- |
| | | |
| | | **å®ç°è¯´æ**ï¼æ¬åè½å·²å®æ´å®ç°æææ ¸å¿æ¨¡åï¼å
æ¬æ°æ®åºè®¾è®¡ãå端代ç ã宿¶ä»»å¡ãç³»ç»éæå宿´ææ¡£ãææä»£ç åå·²å建并ä¿åå°ç¸åºæä»¶ï¼å¯ç´æ¥é¨ç½²ä½¿ç¨ã |
| New file |
| | |
| | | # 车è¾å¼å¸¸è¿è¡çæ§åè¦åè½ - å¿«éé¨ç½²æå |
| | | |
| | | ## åè½æ¦è¿° |
| | | çæ§è½¦è¾æ ä»»å¡è¿è¡è¶
è¿é
ç½®å
¬éæ°ï¼é»è®¤10å
¬éï¼æ¶èªå¨äº§çåè¦ï¼å¹¶éè¿å°ç¨åº/ä¼ä¸å¾®ä¿¡éç¥ç¸å
³è´è´£äººã |
| | | |
| | | ## å¿«éé¨ç½²ï¼5æ¥å®æï¼ |
| | | |
| | | ### 第1æ¥ï¼æ§è¡æ°æ®åºèæ¬ |
| | | ```bash |
| | | cd /path/to/RuoYi-Vue-master |
| | | mysql -uç¨æ·å -på¯ç æ°æ®åºå < sql/vehicle_abnormal_alert.sql |
| | | ``` |
| | | |
| | | **èæ¬å
容å
æ¬**ï¼ |
| | | - â
å建åè¦è®°å½è¡¨ (tb_vehicle_abnormal_alert) |
| | | - â
å建åè¦é
置表 (tb_vehicle_alert_config) |
| | | - â
æå
¥ç³»ç»é
ç½®åæ° (6个é
置项) |
| | | - â
åå»ºå®æ¶ä»»å¡ (vehicle Abnormal Alert Task) |
| | | - â
å建èåæé (车è¾å¼å¸¸åè¦ç®¡ç) |
| | | |
| | | ### 第2æ¥ï¼é
ç½®éç¥ç¨æ· |
| | | å¨"ç³»ç»ç®¡ç > åæ°è®¾ç½®"ä¸é
置以ä¸åæ°ï¼ |
| | | |
| | | | åæ°åç§° | åæ°é®å | é
ç½®å¼ç¤ºä¾ | 说æ | |
| | | |---------|---------|-----------|------| |
| | | | 车è¾å¼å¸¸åè¦éç¥ç¨æ· | vehicle.alert.notify.users | 1,2,3 | æ¥æ¶éç¥çç¨æ·IDï¼éå·åé | |
| | | |
| | | > ð¡ **æç¤º**ï¼å¦æä¸é
ç½®ï¼å°å°è¯æ ¹æ®è½¦è¾å½å±é¨é¨æ¥æ¾è´è´£äºº |
| | | |
| | | ### 第3æ¥ï¼å¯ç¨å®æ¶ä»»å¡ |
| | | 1. ç»å½åå°ç®¡çç³»ç» |
| | | 2. è¿å
¥"ç³»ç»çæ§ > 宿¶ä»»å¡" |
| | | 3. æ¾å°"车è¾å¼å¸¸è¿è¡çæ§ä»»å¡" |
| | | 4. ç¹å»ç¶æå¼å
³ï¼å°å
¶è®¾ç½®ä¸º"è¿è¡ä¸" |
| | | |
| | | ### 第4æ¥ï¼é
ç½®ä¼ä¸å¾®ä¿¡ï¼å¯éï¼ |
| | | å¦é使ç¨ä¼ä¸å¾®ä¿¡éç¥åè½ï¼å¨"ç³»ç»ç®¡ç > åæ°è®¾ç½®"ä¸ç¡®ä¿ä»¥ä¸é
ç½®ï¼ |
| | | |
| | | | åæ°é®å | 说æ | 示ä¾å¼ | |
| | | |---------|------|--------| |
| | | | qy_wechat.enable | ä¼ä¸å¾®ä¿¡å¯ç¨å¼å
³ | true | |
| | | | qy_wechat.corp_id | ä¼ä¸ID | ww123456789 | |
| | | | qy_wechat.corp_secret | åºç¨Secret | xxxxx | |
| | | | qy_wechat.agent_id | åºç¨ID | 1000002 | |
| | | |
| | | ### 第5æ¥ï¼éªè¯åè½ |
| | | 1. ç¡®ä¿GPSåæ®µéç¨è®¡ç®ä»»å¡æ£å¸¸è¿è¡ |
| | | 2. æ¥ç"车è¾ç®¡ç > 车è¾å¼å¸¸åè¦"è忝妿¾ç¤º |
| | | 3. è§å¯å®æ¶ä»»å¡æ§è¡æ¥å¿ |
| | | 4. æµè¯åè¦çæåéç¥åé |
| | | |
| | | ## æ ¸å¿é
ç½®åæ°è¯´æ |
| | | |
| | | ### å¿
éé
ç½® |
| | | | é
ç½®é® | é»è®¤å¼ | 说æ | |
| | | |-------|--------|------| |
| | | | vehicle.alert.enabled | true | åè½æ»å¼å
³ï¼falseååç¨ | |
| | | | vehicle.alert.mileage.threshold | 10 | åè¦éå¼ï¼å
¬éï¼ | |
| | | |
| | | ### å¯éé
ç½® |
| | | | é
ç½®é® | é»è®¤å¼ | 说æ | |
| | | |-------|--------|------| |
| | | | vehicle.alert.daily.limit | 5 | æ¯è½¦æ¯å¤©æå¤åè¦æ¬¡æ° | |
| | | | vehicle.alert.interval.minutes | 5 | 两次åè¦æå°é´éï¼åéï¼ | |
| | | | vehicle.alert.time.window | 10 | çæ§æ¶é´çªå£ï¼åéï¼ | |
| | | | vehicle.alert.notify.users | ï¼ç©ºï¼ | éç¥ç¨æ·IDå表 | |
| | | |
| | | ## 宿¶ä»»å¡è¯´æ |
| | | |
| | | ### ä»»å¡é
ç½® |
| | | - **ä»»å¡åç§°**ï¼è½¦è¾å¼å¸¸è¿è¡çæ§ä»»å¡ |
| | | - **æ§è¡é¢ç**ï¼`0 */5 * * * ?` (æ¯5åéæ§è¡ä¸æ¬¡) |
| | | - **è°ç¨æ¹æ³**ï¼`vehicleAbnormalAlertTask.monitorVehicleAbnormalRunning()` |
| | | - **å¹¶åæ§å¶**ï¼ç¦æ¢å¹¶å |
| | | - **é»è®¤ç¶æ**ï¼æåï¼éæå¨å¯ç¨ï¼ |
| | | |
| | | ### çæ§é»è¾ |
| | | ``` |
| | | æ¯5åéæ§è¡ä¸æ¬¡ |
| | | â |
| | | æ¥è¯¢ææè½¦è¾ |
| | | â |
| | | éè½¦æ£æ¥ï¼å¹¶è¡å¤çï¼ |
| | | ââ æ£æ¥æ¯å¦ææ£å¨æ§è¡çä»»å¡ |
| | | ââ è®¡ç®æè¿10åéè¿è¡éç¨ |
| | | ââ 夿æ¯å¦è¶
è¿éå¼ |
| | | ââ æ£æ¥åè¦é¢çéå¶ |
| | | ââ å建åè¦å¹¶åééç¥ |
| | | ``` |
| | | |
| | | ## æéé
ç½® |
| | | |
| | | ### èåæé |
| | | - **ç¶èå**ï¼è½¦è¾ç®¡ç |
| | | - **èååç§°**ï¼è½¦è¾å¼å¸¸åè¦ |
| | | - **æéæ è¯**ï¼system:vehicleAlert:list |
| | | |
| | | ### æé®æé |
| | | - `system:vehicleAlert:query` - æ¥è¯¢åè¦ |
| | | - `system:vehicleAlert:detail` - åè¦è¯¦æ
|
| | | - `system:vehicleAlert:handle` - å¤çåè¦ |
| | | - `system:vehicleAlert:export` - å¯¼åºæ°æ® |
| | | - `system:vehicleAlert:config` - é
置管ç |
| | | |
| | | ## 常è§é®é¢ |
| | | |
| | | ### Q1: åè¦ä¸çæï¼ |
| | | **ææ¥æ¥éª¤**ï¼ |
| | | 1. æ£æ¥å®æ¶ä»»å¡æ¯å¦å¯ç¨ |
| | | 2. æ¥ç宿¶ä»»å¡æ§è¡æ¥å¿ï¼`ç³»ç»çæ§ > è°åº¦æ¥å¿` |
| | | 3. 确认 `vehicle.alert.enabled = true` |
| | | 4. 确认GPSåæ®µéç¨è¡¨ææ°æ® |
| | | 5. æ£æ¥è½¦è¾æ¯å¦ççæ ä»»å¡è¿è¡ |
| | | |
| | | ### Q2: éç¥ä¸åéï¼ |
| | | **ææ¥æ¥éª¤**ï¼ |
| | | 1. æ£æ¥ä¼ä¸å¾®ä¿¡é
ç½®æ¯å¦æ£ç¡® |
| | | 2. 确认 `qy_wechat.enable = true` |
| | | 3. æ¥çåå°æ¥å¿ï¼æç´¢"åéåè¦éç¥" |
| | | 4. ç¡®è®¤ç¨æ·å·²ç»å®ä¼ä¸å¾®ä¿¡ID |
| | | |
| | | ### Q3: åè¦å¤ªé¢ç¹ï¼ |
| | | **è§£å³æ¹æ¡**ï¼ |
| | | 1. è°é«å
¬éæ°éå¼ï¼`vehicle.alert.mileage.threshold = 20` |
| | | 2. åå°æ¯æ¥åè¦æ¬¡æ°ï¼`vehicle.alert.daily.limit = 3` |
| | | 3. å¢å åè¦é´éï¼`vehicle.alert.interval.minutes = 10` |
| | | |
| | | ### Q4: GPSéç¨æ°æ®ä¸åï¼ |
| | | **æ£æ¥é¡¹**ï¼ |
| | | 1. 确认GPSåæ®µéç¨è®¡ç®ä»»å¡æ£å¸¸è¿è¡ |
| | | 2. æ¥ç `tb_vehicle_gps_segment_mileage` è¡¨æ°æ® |
| | | 3. è°æ´çæ§æ¶é´çªå£ï¼`vehicle.alert.time.window = 15` |
| | | |
| | | ## æµè¯æå |
| | | |
| | | ### æµè¯æ¥éª¤ |
| | | 1. **å夿µè¯æ°æ®** |
| | | - ç¡®ä¿æè½¦è¾GPSæ°æ® |
| | | - ç¡®ä¿GPSåæ®µéç¨è®¡ç®ä»»å¡å·²è¿è¡ |
| | | - éæ©ä¸è¾æ ä»»å¡çè½¦è¾ |
| | | |
| | | 2. **è°æ´é
置便äºè§¦å** |
| | | ```sql |
| | | UPDATE sys_config SET config_value = '1' |
| | | WHERE config_key = 'vehicle.alert.mileage.threshold'; |
| | | ``` |
| | | |
| | | 3. **æå¨è§¦å宿¶ä»»å¡** |
| | | - è¿å
¥"ç³»ç»çæ§ > 宿¶ä»»å¡" |
| | | - æ¾å°"车è¾å¼å¸¸è¿è¡çæ§ä»»å¡" |
| | | - ç¹å»"æ§è¡ä¸æ¬¡"æé® |
| | | |
| | | 4. **æ¥çæ§è¡ç»æ** |
| | | - æ¥ç"ç³»ç»çæ§ > è°åº¦æ¥å¿" |
| | | - æ¥ç"车è¾ç®¡ç > 车è¾å¼å¸¸åè¦"å表 |
| | | - æ£æ¥æ¯å¦æ¶å°ä¼ä¸å¾®ä¿¡éç¥ |
| | | |
| | | 5. **æ¢å¤é
ç½®** |
| | | ```sql |
| | | UPDATE sys_config SET config_value = '10' |
| | | WHERE config_key = 'vehicle.alert.mileage.threshold'; |
| | | ``` |
| | | |
| | | ## æ§è½ä¼å建议 |
| | | |
| | | ### æ°æ®åºç´¢å¼ï¼å·²èªå¨åå»ºï¼ |
| | | ```sql |
| | | -- åè¦è®°å½è¡¨ç´¢å¼ |
| | | CREATE INDEX idx_vehicle_id ON tb_vehicle_abnormal_alert(vehicle_id); |
| | | CREATE INDEX idx_alert_date ON tb_vehicle_abnormal_alert(alert_date); |
| | | CREATE INDEX idx_vehicle_date ON tb_vehicle_abnormal_alert(vehicle_id, alert_date); |
| | | CREATE INDEX idx_status ON tb_vehicle_abnormal_alert(status); |
| | | ``` |
| | | |
| | | ### çæ§å»ºè®® |
| | | - **è½¦è¾æ° < 100**ï¼ä½¿ç¨é»è®¤é
ç½® |
| | | - **è½¦è¾æ° 100-500**ï¼èèå¢å æ¶é´çªå£ä¸º15åé |
| | | - **è½¦è¾æ° > 500**ï¼èèå¢å æ§è¡é´é为10åé |
| | | |
| | | ### æ¥å¿çº§å« |
| | | å¨ç产ç¯å¢ï¼å»ºè®®è®¾ç½®æ¥å¿çº§å«ä¸º INFOï¼ |
| | | ```yaml |
| | | logging: |
| | | level: |
| | | com.ruoyi.quartz.task.VehicleAbnormalAlertTask: INFO |
| | | ``` |
| | | |
| | | ## æ
éææ¥ |
| | | |
| | | ### æ¥å¿ä½ç½® |
| | | - **åºç¨æ¥å¿**ï¼`logs/sys-info.log` |
| | | - **宿¶ä»»å¡æ¥å¿**ï¼æ°æ®åºè¡¨ `sys_job_log` |
| | | |
| | | ### å
³é®æ¥å¿æç´¢ |
| | | ```bash |
| | | # çæ§ä»»å¡æ§è¡æ¥å¿ |
| | | grep "车è¾å¼å¸¸è¿è¡çæ§" logs/sys-info.log |
| | | |
| | | # åè¦çææ¥å¿ |
| | | grep "产çå¼å¸¸åè¦" logs/sys-info.log |
| | | |
| | | # éç¥åéæ¥å¿ |
| | | grep "åéåè¦éç¥" logs/sys-info.log |
| | | ``` |
| | | |
| | | ### æ°æ®åºæ£æ¥ |
| | | ```sql |
| | | -- æ¥ç仿¥åè¦ç»è®¡ |
| | | SELECT status, COUNT(*) as count |
| | | FROM tb_vehicle_abnormal_alert |
| | | WHERE DATE(alert_date) = CURDATE() |
| | | GROUP BY status; |
| | | |
| | | -- æ¥çåè¦é¢ç¹çè½¦è¾ |
| | | SELECT vehicle_no, COUNT(*) as alert_count |
| | | FROM tb_vehicle_abnormal_alert |
| | | WHERE DATE(alert_date) = CURDATE() |
| | | GROUP BY vehicle_no |
| | | ORDER BY alert_count DESC |
| | | LIMIT 10; |
| | | |
| | | -- æ¥ç宿¶ä»»å¡æ§è¡æ
åµ |
| | | SELECT job_name, status, job_message, create_time |
| | | FROM sys_job_log |
| | | WHERE job_name = '车è¾å¼å¸¸è¿è¡çæ§ä»»å¡' |
| | | ORDER BY create_time DESC |
| | | LIMIT 10; |
| | | ``` |
| | | |
| | | ## ææ¯æ¯æ |
| | | |
| | | å¦éå°é®é¢ï¼ |
| | | 1. æ¥çæ¬ææ¡£ç"常è§é®é¢"é¨å |
| | | 2. æ£æ¥ç³»ç»æ¥å¿å宿¶ä»»å¡æ¥å¿ |
| | | 3. æ¥è¯¢æ°æ®åºè¡¨ç¡®è®¤æ°æ®ç¶æ |
| | | 4. èç³»ææ¯æ¯æå¢é |
| | | |
| | | ## æä»¶æ¸
å |
| | | |
| | | | æä»¶ç±»å | æä»¶è·¯å¾ | 说æ | |
| | | |---------|---------|------| |
| | | | SQLèæ¬ | sql/vehicle_abnormal_alert.sql | æ°æ®åºåå§åèæ¬ | |
| | | | å®ä½ç±» | ruoyi-system/.../domain/VehicleAbnormalAlert.java | åè¦å®ä½ | |
| | | | Mapper | ruoyi-system/.../mapper/VehicleAbnormalAlertMapper.java | æ°æ®è®¿é® | |
| | | | Service | ruoyi-system/.../service/impl/VehicleAbnormalAlertServiceImpl.java | ä¸å¡é»è¾ | |
| | | | Controller | ruoyi-admin/.../controller/system/VehicleAbnormalAlertController.java | æ¥å£æ§å¶å¨ | |
| | | | 宿¶ä»»å¡ | ruoyi-quartz/.../task/VehicleAbnormalAlertTask.java | çæ§ä»»å¡ | |
| | | | é
ç½®ææ¡£ | doc/车è¾å¼å¸¸è¿è¡çæ§åè¦åè½è¯´æ.md | 宿´ææ¡£ | |
| | | | é¨ç½²æå | æ¬ææ¡£ | å¿«éé¨ç½² | |
| | | |
| | | --- |
| | | |
| | | **é¨ç½²å®æå**ï¼è¯·ç¡®è®¤ï¼ |
| | | - [ ] æ°æ®åºè¡¨å建æå |
| | | - [ ] èåæ¾ç¤ºæ£å¸¸ |
| | | - [ ] 宿¶ä»»å¡å·²å¯ç¨ |
| | | - [ ] é
ç½®åæ°å·²è®¾ç½® |
| | | - [ ] æµè¯åè¦çææå |
| | | - [ ] éç¥å鿣叏 |
| | | |
| | | **çæ¬**: V1.0.0 |
| | | **æ´æ°æ¥æ**: 2026-01-12 |
| New file |
| | |
| | | # 车è¾å¼å¸¸è¿è¡çæ§åè¦ - èåé
置说æ |
| | | |
| | | ## ð æ¦è¿° |
| | | |
| | | æ¬ææ¡£è¯¦ç»è¯´æè½¦è¾å¼å¸¸è¿è¡çæ§åè¦åè½çåå°èåç»æåæéé
ç½®ãæ§è¡SQLèæ¬åä¼èªå¨åå»ºå®æ´çèåç»æã |
| | | |
| | | ## ð¯ èåç»æ |
| | | |
| | | ### ä¸çº§èåï¼è½¦è¾çæ§ |
| | | |
| | | ``` |
| | | 车è¾çæ§ (vehicle-monitor) - ç®å½èå |
| | | âââ 车è¾å¼å¸¸åè¦ (vehicleAlert) - èå |
| | | â âââ åè¦æ¥è¯¢ - æé®æé |
| | | â âââ å¤çåè¦ - æé®æé |
| | | â âââ å é¤åè¦ - æé®æé |
| | | â âââ 导åºåè¦ - æé®æé |
| | | âââ åè¦é
置管ç (vehicleAlertConfig) - èå |
| | | âââ é
ç½®æ¥è¯¢ - æé®æé |
| | | âââ æ°å¢é
ç½® - æé®æé |
| | | âââ ä¿®æ¹é
ç½® - æé®æé |
| | | âââ å é¤é
ç½® - æé®æé |
| | | âââ 导åºé
ç½® - æé®æé |
| | | ``` |
| | | |
| | | ## ð 详ç»é
ç½® |
| | | |
| | | ### 1. 车è¾çæ§ï¼ç¶èåï¼ |
| | | |
| | | | åæ®µ | å¼ | |
| | | |------|-----| |
| | | | èååç§° | 车è¾çæ§ | |
| | | | ç¶èå | 顶级èå (0) | |
| | | | æ¾ç¤ºæåº | 5 | |
| | | | è·¯ç±å°å | vehicle-monitor | |
| | | | èåç±»å | ç®å½ (M) | |
| | | | èå徿 | monitor | |
| | | | æ¯å¦å¯è§ | æ¯ | |
| | | | èåç¶æ | æ£å¸¸ | |
| | | | 夿³¨ | 车è¾çæ§ç®¡çç®å½ | |
| | | |
| | | **ç¹ç¹**ï¼ |
| | | - ð ç®å½ç±»åï¼ä¸å¯¹åºå
·ä½é¡µé¢ |
| | | - ð¨ 使ç¨monitor徿 |
| | | - ð 顶级èåï¼æ¾ç¤ºå¨å·¦ä¾§å¯¼èªæ |
| | | |
| | | ### 2. 车è¾å¼å¸¸åè¦ï¼åèåï¼ |
| | | |
| | | | åæ®µ | å¼ | |
| | | |------|-----| |
| | | | èååç§° | 车è¾å¼å¸¸åè¦ | |
| | | | ç¶èå | 车è¾çæ§ | |
| | | | æ¾ç¤ºæåº | 1 | |
| | | | è·¯ç±å°å | vehicleAlert | |
| | | | ç»ä»¶è·¯å¾ | system/vehicleAlert/index | |
| | | | èåç±»å | èå (C) | |
| | | | èå徿 | warning | |
| | | | æ¯å¦ç¼å | å¦ | |
| | | | æéæ è¯ | system:vehicleAlert:list | |
| | | | 夿³¨ | 车è¾å¼å¸¸è¿è¡åè¦ç®¡ç | |
| | | |
| | | **åè½æé®æé**ï¼ |
| | | |
| | | #### 2.1 åè¦æ¥è¯¢ |
| | | - **æéæ è¯**: `system:vehicleAlert:query` |
| | | - **ç¨é**: æ¥è¯¢åè¦å表å详æ
|
| | | |
| | | #### 2.2 å¤çåè¦ |
| | | - **æéæ è¯**: `system:vehicleAlert:handle` |
| | | - **ç¨é**: å¤çåæ¡ææ¹éå¤çåè¦ |
| | | |
| | | #### 2.3 å é¤åè¦ |
| | | - **æéæ è¯**: `system:vehicleAlert:remove` |
| | | - **ç¨é**: å é¤åè¦è®°å½ |
| | | |
| | | #### 2.4 导åºåè¦ |
| | | - **æéæ è¯**: `system:vehicleAlert:export` |
| | | - **ç¨é**: 导åºåè¦æ°æ®ä¸ºExcel |
| | | |
| | | ### 3. åè¦é
置管çï¼åèåï¼ |
| | | |
| | | | åæ®µ | å¼ | |
| | | |------|-----| |
| | | | èååç§° | åè¦é
置管ç | |
| | | | ç¶èå | 车è¾çæ§ | |
| | | | æ¾ç¤ºæåº | 2 | |
| | | | è·¯ç±å°å | vehicleAlertConfig | |
| | | | ç»ä»¶è·¯å¾ | system/vehicleAlertConfig/index | |
| | | | èåç±»å | èå (C) | |
| | | | èå徿 | edit | |
| | | | æ¯å¦ç¼å | å¦ | |
| | | | æéæ è¯ | system:vehicleAlertConfig:list | |
| | | | 夿³¨ | 车è¾åè¦é
置管ç | |
| | | |
| | | **åè½æé®æé**ï¼ |
| | | |
| | | #### 3.1 é
ç½®æ¥è¯¢ |
| | | - **æéæ è¯**: `system:vehicleAlertConfig:query` |
| | | - **ç¨é**: æ¥è¯¢é
ç½®å表å详æ
|
| | | |
| | | #### 3.2 æ°å¢é
ç½® |
| | | - **æéæ è¯**: `system:vehicleAlertConfig:add` |
| | | - **ç¨é**: æ°å¢å
¨å±/é¨é¨/车è¾é
ç½® |
| | | |
| | | #### 3.3 ä¿®æ¹é
ç½® |
| | | - **æéæ è¯**: `system:vehicleAlertConfig:edit` |
| | | - **ç¨é**: ä¿®æ¹å·²æé
ç½® |
| | | |
| | | #### 3.4 å é¤é
ç½® |
| | | - **æéæ è¯**: `system:vehicleAlertConfig:remove` |
| | | - **ç¨é**: å é¤é
ç½®è®°å½ |
| | | |
| | | #### 3.5 导åºé
ç½® |
| | | - **æéæ è¯**: `system:vehicleAlertConfig:export` |
| | | - **ç¨é**: 导åºé
ç½®æ°æ®ä¸ºExcel |
| | | |
| | | ## ð æéæ è¯å表 |
| | | |
| | | ### 车è¾å¼å¸¸åè¦æ¨¡å |
| | | |
| | | | æéæ è¯ | 说æ | ç±»å | |
| | | |---------|------|------| |
| | | | `system:vehicleAlert:list` | åè¦å表 | èåè®¿é® | |
| | | | `system:vehicleAlert:query` | åè¦æ¥è¯¢ | æé®æé | |
| | | | `system:vehicleAlert:handle` | å¤çåè¦ | æé®æé | |
| | | | `system:vehicleAlert:remove` | å é¤åè¦ | æé®æé | |
| | | | `system:vehicleAlert:export` | 导åºåè¦ | æé®æé | |
| | | |
| | | ### åè¦é
ç½®ç®¡çæ¨¡å |
| | | |
| | | | æéæ è¯ | 说æ | ç±»å | |
| | | |---------|------|------| |
| | | | `system:vehicleAlertConfig:list` | é
ç½®å表 | èåè®¿é® | |
| | | | `system:vehicleAlertConfig:query` | é
ç½®æ¥è¯¢ | æé®æé | |
| | | | `system:vehicleAlertConfig:add` | æ°å¢é
ç½® | æé®æé | |
| | | | `system:vehicleAlertConfig:edit` | ä¿®æ¹é
ç½® | æé®æé | |
| | | | `system:vehicleAlertConfig:remove` | å é¤é
ç½® | æé®æé | |
| | | | `system:vehicleAlertConfig:export` | 导åºé
ç½® | æé®æé | |
| | | |
| | | ## ð SQLèæ¬è¯´æ |
| | | |
| | | ### èæ¬ç¹ç¹ |
| | | |
| | | 1. **é²é夿§è¡**: ä½¿ç¨ `NOT EXISTS` æ£æ¥ï¼é¿å
é夿å
¥ |
| | | 2. **卿è·åID**: 使ç¨åéåå¨ç¶èåID |
| | | 3. **é¡ºåºæ§è¡**: å
å建ç¶èåï¼åå建åèååæé® |
| | | 4. **宿´æ§**: 䏿¬¡æ§å建ææèååæé |
| | | |
| | | ### å
³é®SQLè¯å¥ |
| | | |
| | | #### 1. å建ç¶èåï¼é²éå¤ï¼ |
| | | ```sql |
| | | INSERT INTO sys_menu (...) |
| | | SELECT '车è¾çæ§', 0, 5, 'vehicle-monitor', ... |
| | | FROM DUAL |
| | | WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_name = '车è¾çæ§' AND menu_type = 'M'); |
| | | ``` |
| | | |
| | | #### 2. è·åç¶èåID |
| | | ```sql |
| | | SET @vehicleMonitorMenuId = (SELECT menu_id FROM sys_menu WHERE menu_name = '车è¾çæ§' AND menu_type = 'M' LIMIT 1); |
| | | ``` |
| | | |
| | | #### 3. å建åèå |
| | | ```sql |
| | | INSERT INTO sys_menu (...) |
| | | SELECT '车è¾å¼å¸¸åè¦', @vehicleMonitorMenuId, 1, 'vehicleAlert', ... |
| | | FROM DUAL |
| | | WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_name = '车è¾å¼å¸¸åè¦' AND perms = 'system:vehicleAlert:list'); |
| | | ``` |
| | | |
| | | ## ð¨ å端路ç±é
ç½® |
| | | |
| | | å¦æä½¿ç¨å¨æè·¯ç±ï¼ä»å端å è½½èåï¼ï¼æ éé¢å¤é
ç½®ãå¦æä½¿ç¨éæè·¯ç±ï¼éå¨ `router/index.js` 䏿·»å ï¼ |
| | | |
| | | ```javascript |
| | | { |
| | | path: '/vehicle-monitor', |
| | | component: Layout, |
| | | redirect: '/vehicle-monitor/vehicleAlert', |
| | | name: 'VehicleMonitor', |
| | | meta: { title: '车è¾çæ§', icon: 'monitor' }, |
| | | children: [ |
| | | { |
| | | path: 'vehicleAlert', |
| | | component: () => import('@/views/system/vehicleAlert/index'), |
| | | name: 'VehicleAlert', |
| | | meta: { title: '车è¾å¼å¸¸åè¦', icon: 'warning' } |
| | | }, |
| | | { |
| | | path: 'vehicleAlertConfig', |
| | | component: () => import('@/views/system/vehicleAlertConfig/index'), |
| | | name: 'VehicleAlertConfig', |
| | | meta: { title: 'åè¦é
置管ç', icon: 'edit' } |
| | | } |
| | | ] |
| | | } |
| | | ``` |
| | | |
| | | ## ð¥ è§è²æéåé
|
| | | |
| | | ### 管çåè§è²ï¼adminï¼ |
| | | 建议åé
æææéï¼ |
| | | - â
åè¦åè¡¨è®¿é® |
| | | - â
åè¦æ¥è¯¢ |
| | | - â
å¤çåè¦ |
| | | - â
å é¤åè¦ |
| | | - â
导åºåè¦ |
| | | - â
é
ç½®ç®¡çæææé |
| | | |
| | | ### æ®éç¨æ·è§è²ï¼userï¼ |
| | | 建议åé
åºç¡æéï¼ |
| | | - â
åè¦åè¡¨è®¿é® |
| | | - â
åè¦æ¥è¯¢ |
| | | - â
å¤çåè¦ |
| | | - â å é¤åè¦ |
| | | - â
导åºåè¦ |
| | | - â é
置管çæé |
| | | |
| | | ### æ¥çè
è§è²ï¼viewerï¼ |
| | | 建议åé
åªè¯»æéï¼ |
| | | - â
åè¦åè¡¨è®¿é® |
| | | - â
åè¦æ¥è¯¢ |
| | | - â å¤çåè¦ |
| | | - â å é¤åè¦ |
| | | - â
导åºåè¦ |
| | | - â é
置管çæé |
| | | |
| | | ## ð§ è§è²åé
æ¥éª¤ |
| | | |
| | | ### æ¹å¼ä¸ï¼éè¿åå°ç®¡ççé¢ |
| | | |
| | | 1. ç»å½åå°ç³»ç» |
| | | 2. è¿å
¥ **ç³»ç»ç®¡ç > è§è²ç®¡ç** |
| | | 3. éæ©è¦åé
æéçè§è²ï¼ç¹å»"ä¿®æ¹" |
| | | 4. å¨"èåæé"ä¸å¾ééè¦çèååæé® |
| | | 5. ç¹å»"ç¡®å®"ä¿å |
| | | |
| | | ### æ¹å¼äºï¼éè¿SQLç´æ¥åé
|
| | | |
| | | ```sql |
| | | -- 为è§è²ID=2ï¼ç¤ºä¾ï¼åé
åè¦ç®¡çæé |
| | | INSERT INTO sys_role_menu (role_id, menu_id) |
| | | SELECT 2, menu_id FROM sys_menu |
| | | WHERE perms LIKE 'system:vehicleAlert:%' OR perms LIKE 'system:vehicleAlertConfig:%'; |
| | | ``` |
| | | |
| | | ## ð éªè¯æ¸
å |
| | | |
| | | æ§è¡SQLèæ¬åï¼è¯·éªè¯ä»¥ä¸å
å®¹ï¼ |
| | | |
| | | ### 1. èåå建éªè¯ |
| | | ```sql |
| | | -- æ¥è¯¢è½¦è¾çæ§èåç»æ |
| | | SELECT |
| | | m1.menu_name AS 'ä¸çº§èå', |
| | | m2.menu_name AS 'äºçº§èå', |
| | | m3.menu_name AS 'æé®', |
| | | m3.perms AS 'æéæ è¯' |
| | | FROM sys_menu m1 |
| | | LEFT JOIN sys_menu m2 ON m2.parent_id = m1.menu_id |
| | | LEFT JOIN sys_menu m3 ON m3.parent_id = m2.menu_id |
| | | WHERE m1.menu_name = '车è¾çæ§' |
| | | ORDER BY m2.order_num, m3.order_num; |
| | | ``` |
| | | |
| | | **é¢æç»æ**ï¼åºè¯¥çå°å®æ´çèåæ ç»æ |
| | | |
| | | ### 2. æéæ°ééªè¯ |
| | | ```sql |
| | | -- ç»è®¡åè¦ç¸å
³æéæ°é |
| | | SELECT COUNT(*) AS 'æéæ°é' FROM sys_menu |
| | | WHERE perms LIKE 'system:vehicleAlert%'; |
| | | ``` |
| | | |
| | | **é¢æç»æ**ï¼åºè¯¥æ11个æéï¼2个list + 9个æé®ï¼ |
| | | |
| | | ### 3. å端访é®éªè¯ |
| | | - [ ] ç»å½ç³»ç»åè½çå°"车è¾çæ§"èå |
| | | - [ ] ç¹å»"车è¾å¼å¸¸åè¦"è½æ£å¸¸è®¿é®é¡µé¢ |
| | | - [ ] ç¹å»"åè¦é
置管ç"è½æ£å¸¸è®¿é®é¡µé¢ |
| | | - [ ] ååè½æé®æ ¹æ®æéæ£ç¡®æ¾ç¤º/éè |
| | | |
| | | ## â ï¸ å¸¸è§é®é¢ |
| | | |
| | | ### 1. èå䏿¾ç¤º |
| | | |
| | | **åå **ï¼ |
| | | - èåæªå建æå |
| | | - ç¨æ·è§è²æªåé
èåæé |
| | | - ç¼åæªå·æ° |
| | | |
| | | **è§£å³æ¹æ³**ï¼ |
| | | ```sql |
| | | -- æ£æ¥è忝å¦åå¨ |
| | | SELECT * FROM sys_menu WHERE menu_name = '车è¾çæ§'; |
| | | |
| | | -- æ£æ¥è§è²æé |
| | | SELECT rm.*, m.menu_name, m.perms |
| | | FROM sys_role_menu rm |
| | | JOIN sys_menu m ON rm.menu_id = m.menu_id |
| | | WHERE rm.role_id = YOUR_ROLE_ID |
| | | AND m.menu_name LIKE '%åè¦%'; |
| | | |
| | | -- æ¸
é¤ç¨æ·ç¼å |
| | | DELETE FROM sys_user_online WHERE user_name = 'YOUR_USERNAME'; |
| | | ``` |
| | | |
| | | ### 2. æé®ä¸æ¾ç¤º |
| | | |
| | | **åå **ï¼ |
| | | - æé®æéæªåé
|
| | | - å端v-hasPermiæä»¤æéæ è¯ä¸å¹é
|
| | | |
| | | **è§£å³æ¹æ³**ï¼ |
| | | - æ£æ¥è§è²æ¯å¦åé
äºå¯¹åºçæé®æé |
| | | - 确认å端代ç ä¸çæéæ è¯ä¸æ°æ®åºä¸è´ |
| | | |
| | | ### 3. é夿§è¡SQLæ¥é |
| | | |
| | | **åå **ï¼èåå·²åå¨ |
| | | |
| | | **è§£å³æ¹æ³**ï¼ |
| | | - SQLèæ¬å·²ä½¿ç¨`NOT EXISTS`é²éå¤ï¼æ£å¸¸æ
åµä¸ä¼æ¥é |
| | | - 妿æ¥å¯ä¸é®å²çªï¼è¯´ææ°æ®å·²åå¨ï¼å¯ä»¥å¿½ç¥ |
| | | |
| | | ### 4. ç¶èåIDè·å失败 |
| | | |
| | | **åå **ï¼ç¶èåä¸å卿åç§°ä¸å¹é
|
| | | |
| | | **è§£å³æ¹æ³**ï¼ |
| | | ```sql |
| | | -- æ£æ¥ç¶èå |
| | | SELECT menu_id, menu_name FROM sys_menu WHERE menu_name = '车è¾çæ§'; |
| | | |
| | | -- 妿ä¸åå¨ï¼å
æ§è¡ç¶èåå建SQL |
| | | ``` |
| | | |
| | | ## ð ææ¯æ¯æ |
| | | |
| | | 妿é®é¢ï¼è¯·åèï¼ |
| | | - ð [宿´å®ç°æ»ç»](./车è¾å¼å¸¸è¿è¡çæ§åè¦-宿´å®ç°æ»ç».md) |
| | | - ð [å¿«éé¨ç½²æå](./车è¾å¼å¸¸è¿è¡çæ§åè¦-å¿«éé¨ç½²æå.md) |
| | | - ð [å端é¨ç½²æå](./车è¾å¼å¸¸è¿è¡çæ§åè¦-å端é¨ç½²æå.md) |
| | | |
| | | --- |
| | | |
| | | **ææ¡£çæ¬**: v1.0 |
| | | **æ´æ°æ¶é´**: 2026-01-12 |
| | | **ç»´æ¤äººå**: AIå¼å婿 |
| New file |
| | |
| | | # 车è¾å¼å¸¸è¿è¡çæ§åè¦åè½å®ç°è¯´æ |
| | | |
| | | ## åè½æ¦è¿° |
| | | |
| | | å®ç°è½¦è¾æ ä»»å¡è¿è¡è¶
å
¬éæ°åè¦åè½ï¼å½çæ§å°è½¦è¾å¨æ ç»å®ä»»å¡ç¶æä¸è¿è¡è¶
è¿é
ç½®çå
¬éæ°ï¼é»è®¤10å
¬éï¼æ¶ï¼èªå¨äº§çåè¦å¹¶éè¿å°ç¨åºåééç¥ç»ç¸å
³è´è´£äººã |
| | | |
| | | ## åè½ç¹æ§ |
| | | |
| | | ### 1. æºè½çæ§ |
| | | - **æ¶é´çªå£çæ§**ï¼æ¯5åéæ§è¡ä¸æ¬¡ï¼çæ§æè¿10åéï¼å¯é
ç½®ï¼å
ç车è¾è¿è¡æ
åµ |
| | | - **ä»»å¡ç¶ææ£æµ**ï¼èªå¨è¯å«è½¦è¾æ¯å¦ææ£å¨æ§è¡çä»»å¡ |
| | | - **éç¨èªå¨è®¡ç®**ï¼åºäºGPSåæ®µéç¨è®°å½èªå¨ç´¯è®¡è¿è¡å
¬éæ° |
| | | - **é¨é¨æºè½è¯å«**ï¼èªå¨å
³è车è¾å½å±é¨é¨ä¿¡æ¯ |
| | | |
| | | ### 2. çµæ´»é
ç½® |
| | | - **å
¬éæ°éå¼**ï¼é»è®¤10å
¬éï¼å¯éè¿ç³»ç»é
ç½®è°æ´ |
| | | - **åè¦é¢çéå¶**ï¼ |
| | | - æ¯å¤©æ¯è½¦æå¤åè¦5次ï¼å¯é
ç½®ï¼ |
| | | - 两次åè¦æå°é´é5åéï¼å¯é
ç½®ï¼ |
| | | - **çæ§æ¶é´çªå£**ï¼é»è®¤10åéï¼å¯é
ç½® |
| | | - **éç¥ç¨æ·é
ç½®**ï¼æ¯æé
ç½®åºå®ç¨æ·åè¡¨ææ ¹æ®é¨é¨èªå¨éç¥è´è´£äºº |
| | | |
| | | ### 3. 夿¸ ééç¥ |
| | | - **ä¼ä¸å¾®ä¿¡éç¥**ï¼éæä¼ä¸å¾®ä¿¡æ¶æ¯æ¨é |
| | | - **å°ç¨åºéç¥**ï¼éè¿å°ç¨åºåéåè¦ä¿¡æ¯ |
| | | - **éç¥å
容**ï¼å
å«è½¦çå·ãè¿è¡å
¬éæ°ãæ¶é´æ®µçå
³é®ä¿¡æ¯ |
| | | |
| | | ### 4. 宿´ç管ççé¢ |
| | | - **åè¦å表æ¥ç**ï¼æ¯ææè½¦çå·ãæ¶é´ãé¨é¨ãç¶æçå¤ç»´åº¦æ¥è¯¢ |
| | | - **åè¦è¯¦æ
æ¥ç**ï¼æ¥çåè¦ç宿´ä¿¡æ¯ |
| | | - **åè¦å¤ç**ï¼æ¯æåä¸ªææ¹éå¤çåè¦ï¼è®°å½å¤ç人åå¤çæè§ |
| | | - **æ°æ®å¯¼åº**ï¼æ¯æå¯¼åºExcelè¿è¡æ°æ®åæ |
| | | - **ç»è®¡åè½**ï¼å±ç¤ºæªå¤çåè¦æ°éã仿¥åè¦æ°éçç»è®¡ä¿¡æ¯ |
| | | |
| | | ## ææ¯å®ç° |
| | | |
| | | ### æ°æ®åºè®¾è®¡ |
| | | |
| | | #### 1. 车è¾å¼å¸¸åè¦è®°å½è¡¨ (tb_vehicle_abnormal_alert) |
| | | å卿¯æ¬¡åè¦ç详ç»ä¿¡æ¯ï¼ |
| | | - åºæ¬ä¿¡æ¯ï¼è½¦è¾IDã车çå·ãåè¦æ¶é´ã累计å
¬éæ° |
| | | - åè¦è¯¦æ
ï¼åè¦ç±»åãåå æè¿°ãå¼å§/ç»ææ¶é´ |
| | | - å¤çä¿¡æ¯ï¼å¤çç¶æãå¤ç人ãå¤çæ¶é´ãå¤ç夿³¨ |
| | | - éç¥ä¿¡æ¯ï¼éç¥ç¶æãéç¥æ¶é´ãéç¥ç¨æ·å表 |
| | | - é¨é¨ä¿¡æ¯ï¼å½å±é¨é¨IDãé¨é¨åç§° |
| | | |
| | | #### 2. 车è¾å¼å¸¸åè¦é
置表 (tb_vehicle_alert_config) |
| | | æ¯æå
¨å±ãé¨é¨ã车è¾ä¸çº§é
ç½®ï¼ |
| | | - é
ç½®å±çº§ï¼GLOBAL(å
¨å±)/DEPT(é¨é¨)/VEHICLE(车è¾) |
| | | - åè¦åæ°ï¼å
¬éæ°éå¼ãæ¯æ¥åè¦æ¬¡æ°ãåè¦é´é |
| | | - éç¥é
ç½®ï¼éç¥ç¨æ·IDå表ãéç¥è§è²å表 |
| | | - å¯ç¨ç¶æï¼å¯çµæ´»å¼å¯æå
³éæä¸ªé
ç½® |
| | | |
| | | ### æ ¸å¿ç»ä»¶ |
| | | |
| | | #### 1. 宿¶çæ§ä»»å¡ (VehicleAbnormalAlertTask) |
| | | ```java |
| | | @Component("vehicleAbnormalAlertTask") |
| | | public class VehicleAbnormalAlertTask { |
| | | // æ¯5åéæ§è¡ä¸æ¬¡ |
| | | public void monitorVehicleAbnormalRunning() |
| | | } |
| | | ``` |
| | | |
| | | **çæ§æµç¨**ï¼ |
| | | 1. æ£æ¥åè½å¼å
³æ¯å¦å¯ç¨ |
| | | 2. å è½½é
ç½®åæ°ï¼éå¼ãé¢çéå¶çï¼ |
| | | 3. æ¥è¯¢æææ´»è·è½¦è¾ |
| | | 4. éè½¦æ£æ¥ï¼ |
| | | - æ¯å¦ææ£å¨æ§è¡çä»»å¡ |
| | | - 计ç®çæ§çªå£å
çè¿è¡éç¨ |
| | | - 夿æ¯å¦è¶
è¿éå¼ |
| | | - æ£æ¥åè¦é¢çéå¶ |
| | | 5. å建åè¦è®°å½ |
| | | 6. åééç¥ |
| | | |
| | | #### 2. åè¦æå¡ (VehicleAbnormalAlertService) |
| | | æä¾åè¦çå¢å æ¹æ¥ãå¤ççæ ¸å¿ä¸å¡é»è¾ï¼ |
| | | - `checkAndCreateAlert()`: æ£æ¥å¹¶å建åè¦ |
| | | - `handleAlert()`: å¤çå个åè¦ |
| | | - `batchHandleAlert()`: æ¹éå¤çåè¦ |
| | | |
| | | #### 3. åè¦æ§å¶å¨ (VehicleAbnormalAlertController) |
| | | æä¾RESTful APIæ¥å£ï¼ |
| | | - `GET /system/vehicleAlert/list`: æ¥è¯¢åè¦å表 |
| | | - `GET /system/vehicleAlert/{id}`: æ¥è¯¢åè¦è¯¦æ
|
| | | - `PUT /system/vehicleAlert/handle/{id}`: å¤çåè¦ |
| | | - `GET /system/vehicleAlert/unhandledCount`: è·åæªå¤çåè¦æ° |
| | | - `POST /system/vehicleAlert/export`: 导åºåè¦æ°æ® |
| | | |
| | | ## é
置说æ |
| | | |
| | | ### ç³»ç»é
ç½®åæ° (sys_config表) |
| | | |
| | | | é
ç½®é® | 说æ | é»è®¤å¼ | |
| | | |-------|------|--------| |
| | | | vehicle.alert.enabled | åè¦åè½æ»å¼å
³ | true | |
| | | | vehicle.alert.mileage.threshold | å
¬éæ°åè¦éå¼ï¼å
¬éï¼ | 10 | |
| | | | vehicle.alert.daily.limit | æ¯æ¥æå¤§åè¦æ¬¡æ° | 5 | |
| | | | vehicle.alert.interval.minutes | åè¦é´éæ¶é´ï¼åéï¼ | 5 | |
| | | | vehicle.alert.time.window | çæ§æ¶é´çªå£ï¼åéï¼ | 10 | |
| | | | vehicle.alert.notify.users | éç¥ç¨æ·IDå表 | ï¼ç©ºï¼ | |
| | | |
| | | ### 宿¶ä»»å¡é
ç½® |
| | | |
| | | - **ä»»å¡åç§°**ï¼è½¦è¾å¼å¸¸è¿è¡çæ§ä»»å¡ |
| | | - **è°ç¨ç®æ **ï¼`vehicleAbnormalAlertTask.monitorVehicleAbnormalRunning()` |
| | | - **æ§è¡é¢ç**ï¼`0 */5 * * * ?`ï¼æ¯5åéæ§è¡ä¸æ¬¡ï¼ |
| | | - **å¹¶åæ§å¶**ï¼ç¦æ¢å¹¶åæ§è¡ |
| | | - **é»è®¤ç¶æ**ï¼æåï¼éæå¨å¯ç¨ï¼ |
| | | |
| | | ## é¨ç½²æ¥éª¤ |
| | | |
| | | ### 1. æ§è¡SQLèæ¬ |
| | | ```sql |
| | | source sql/vehicle_abnormal_alert.sql; |
| | | ``` |
| | | |
| | | è¯¥èæ¬ä¼èªå¨åå»ºï¼ |
| | | - åè¦è®°å½è¡¨ |
| | | - åè¦é
置表 |
| | | - ç³»ç»é
ç½®åæ° |
| | | - 宿¶ä»»å¡ |
| | | - èåæé |
| | | |
| | | ### 2. é
ç½®éç¥ç¨æ· |
| | | å¨ç³»ç»é
ç½®ä¸è®¾ç½® `vehicle.alert.notify.users`ï¼å¡«å
¥æ¥æ¶åè¦çç¨æ·IDå表ï¼éå·åéï¼ |
| | | |
| | | ### 3. å¯ç¨å®æ¶ä»»å¡ |
| | | å¨"ç³»ç»çæ§ > 宿¶ä»»å¡"䏿¾å°"车è¾å¼å¸¸è¿è¡çæ§ä»»å¡"ï¼ç¹å»"æ¢å¤"æé®å¯ç¨ |
| | | |
| | | ### 4. é
ç½®ä¼ä¸å¾®ä¿¡ï¼å¯éï¼ |
| | | å¦é使ç¨ä¼ä¸å¾®ä¿¡éç¥ï¼ç¡®ä¿ä»¥ä¸é
ç½®æ£ç¡®ï¼ |
| | | - `qy_wechat.enable = true` |
| | | - `qy_wechat.corp_id` = ä¼ä¸ID |
| | | - `qy_wechat.corp_secret` = åºç¨Secret |
| | | - `qy_wechat.agent_id` = åºç¨ID |
| | | |
| | | ## ä½¿ç¨æå |
| | | |
| | | ### 管çåæä½ |
| | | |
| | | #### 1. æ¥çåè¦å表 |
| | | - è¿å
¥"车è¾ç®¡ç > 车è¾å¼å¸¸åè¦" |
| | | - å¯æè½¦çå·ãæ¶é´èå´ãé¨é¨ãç¶æçé |
| | | - é»è®¤æåè¦æ¶é´ååºæå |
| | | |
| | | #### 2. å¤çåè¦ |
| | | - ç¹å»"å¤ç"æé® |
| | | - å¡«åå¤ç夿³¨ |
| | | - æäº¤ååè¦ç¶æåæ´ä¸º"å·²å¤ç" |
| | | |
| | | #### 3. æ¹éå¤ç |
| | | - å¾éå¤ä¸ªåè¦ |
| | | - ç¹å»"æ¹éå¤ç" |
| | | - å¡«åç»ä¸çå¤ç夿³¨ |
| | | |
| | | #### 4. å¯¼åºæ°æ® |
| | | - 设置ç鿡件 |
| | | - ç¹å»"导åº"æé® |
| | | - ä¸è½½Excelæä»¶è¿è¡åæ |
| | | |
| | | #### 5. è°æ´é
ç½® |
| | | - è¿å
¥"ç³»ç»ç®¡ç > åæ°è®¾ç½®" |
| | | - æç´¢"vehicle.alert" |
| | | - ä¿®æ¹ç¸å
³åæ° |
| | | - ä¿®æ¹åç«å³çæï¼æ ééå¯ |
| | | |
| | | ### çæ§åç |
| | | |
| | | #### éç¨è®¡ç® |
| | | - åºäº`tb_vehicle_gps_segment_mileage`表 |
| | | - æ¯5åéèªå¨å段ç»è®¡GPSéç¨ |
| | | - æ¥è¯¢çæ§çªå£å
çææåæ®µéç¨è®°å½ |
| | | - ç´¯å è®¡ç®æ»è¿è¡å
¬éæ° |
| | | |
| | | #### ä»»å¡å
³è夿 |
| | | - æ¥è¯¢`sys_task`å`sys_task_vehicle`表 |
| | | - æ£æ¥è½¦è¾å¨çæ§æ¶é´çªå£å
æ¯å¦æä»»å¡ |
| | | - æé¤å·²å®æå已忶çä»»å¡ |
| | | - åªæå®å
¨æ 任塿¶æè§¦ååè¦ |
| | | |
| | | #### é¢çæ§å¶ |
| | | - **æ¯æ¥æ¬¡æ°éå¶**ï¼æ¥è¯¢å½å¤©è¯¥è½¦å·²åè¦æ¬¡æ°ï¼è¾¾å°ä¸éåè·³è¿ |
| | | - **æ¶é´é´ééå¶**ï¼æ¥è¯¢æå䏿¬¡åè¦æ¶é´ï¼æªè¾¾å°é´éåè·³è¿ |
| | | - 鲿¢é¢ç¹åè¦éªæ° |
| | | |
| | | ## åè¦ç¶ææµè½¬ |
| | | |
| | | ``` |
| | | æªå¤ç(0) -> å·²å¤ç(1) |
| | | \-> 已忽ç¥(2) |
| | | ``` |
| | | |
| | | - **æªå¤ç**ï¼æ°å建çåè¦ï¼å¾
å¤ç |
| | | - **å·²å¤ç**ï¼ç®¡çåå·²å¤çå¹¶å¡«åäºå¤çæè§ |
| | | - **已忽ç¥**ï¼ç®¡çåç¡®è®¤ä¸ºè¯¯æ¥ææ éå¤ç |
| | | |
| | | ## 注æäºé¡¹ |
| | | |
| | | 1. **GPSæ°æ®ä¾èµ** |
| | | - éè¦GPSåæ®µéç¨è®¡ç®ä»»å¡æ£å¸¸è¿è¡ |
| | | - ç¡®ä¿`tb_vehicle_gps_segment_mileage`è¡¨ææ°æ® |
| | | - 建议å
è¿è¡GPSéç¨è®¡ç®ä»»å¡æµè¯ |
| | | |
| | | 2. **æ§è½ä¼å** |
| | | - çæ§ä»»å¡æ¯5åéæ§è¡ä¸æ¬¡ |
| | | - æ¯æ¬¡åªæ¥è¯¢æè¿10åéçæ°æ® |
| | | - æ°æ®åºå·²å»ºç«å¿
è¦çç´¢å¼ |
| | | - 大éè½¦è¾æ¶æ³¨æçæ§æ§è¡æ¶é´ |
| | | |
| | | 3. **éç¥é
ç½®** |
| | | - ä¼å
使ç¨é
ç½®çç¨æ·å表 |
| | | - å
¶æ¬¡æ ¹æ®è½¦è¾å½å±é¨é¨æ¥æ¾è´è´£äºº |
| | | - æå使ç¨å
¨å±é»è®¤ç¨æ·å表 |
| | | - ç¡®ä¿è³å°é
ç½®ä¸ç§éç¥æ¹å¼ |
| | | |
| | | 4. **æµè¯å»ºè®®** |
| | | - å
卿µè¯ç¯å¢éªè¯ |
| | | - è°æ´éå¼ä¸ºè¾å°å¼ï¼å¦1å
¬éï¼æ¹ä¾¿è§¦å |
| | | - è§å¯åè¦è®°å½åéç¥æ¯å¦æ£å¸¸ |
| | | - 确认é¢çéå¶æ¯å¦çæ |
| | | |
| | | 5. **çæ§è°ä¼** |
| | | - æ ¹æ®å®é
æ
åµè°æ´å
¬éæ°éå¼ |
| | | - åçè®¾ç½®æ¯æ¥åè¦æ¬¡æ° |
| | | - éå½è°æ´çæ§æ¶é´çªå£ |
| | | - é¿å
åè¦è¿äºé¢ç¹æéæ¼ |
| | | |
| | | ## æ©å±åè½ |
| | | |
| | | ### æªæ¥å¯æ©å±çåè½æ¹å |
| | | |
| | | 1. **å¤ç§åè¦ç±»å** |
| | | - é¿æ¶é´å车åè¦ |
| | | - è¶
éåè¦ |
| | | - å离路线åè¦ |
| | | |
| | | 2. **åè¦è§å弿** |
| | | - èªå®ä¹åè¦è§å |
| | | - è§åç»åä¸ä¼å
级 |
| | | - å¨æè°æ´è§å |
| | | |
| | | 3. **æ°æ®åæ** |
| | | - åè¦è¶å¿åæ |
| | | - 车è¾å¼å¸¸è¡ä¸ºç»è®¡ |
| | | - é¨é¨åè¦å¯¹æ¯ |
| | | |
| | | 4. **æºè½æ¨è** |
| | | - åºäºå岿°æ®é¢æµå¼å¸¸ |
| | | - æ¨èæä¼é
ç½®åæ° |
| | | - èªå¨è¯å«è¯¯æ¥ |
| | | |
| | | ## ææ¯æ¯æ |
| | | |
| | | 妿é®é¢ï¼è¯·èç³»ææ¯æ¯æå¢éææ¥çç¸å
³ææ¡£ï¼ |
| | | - ç³»ç»æ¥å¿ï¼`/logs/sys-*.log` |
| | | - 宿¶ä»»å¡æ¥å¿ï¼`/monitor/job/log` |
| | | - GPSéç¨è®¡ç®è¯´æï¼`/doc/GPSåæ®µéç¨è®¡ç®.md` |
| | | |
| | | ## æä»¶æ¸
å |
| | | |
| | | ### å端代ç |
| | | - `ruoyi-system/src/main/java/com/ruoyi/system/domain/VehicleAbnormalAlert.java` - åè¦å®ä½ç±» |
| | | - `ruoyi-system/src/main/java/com/ruoyi/system/domain/VehicleAlertConfig.java` - é
ç½®å®ä½ç±» |
| | | - `ruoyi-system/src/main/java/com/ruoyi/system/mapper/VehicleAbnormalAlertMapper.java` - æ°æ®è®¿é®æ¥å£ |
| | | - `ruoyi-system/src/main/java/com/ruoyi/system/service/IVehicleAbnormalAlertService.java` - ä¸å¡æ¥å£ |
| | | - `ruoyi-system/src/main/java/com/ruoyi/system/service/impl/VehicleAbnormalAlertServiceImpl.java` - ä¸å¡å®ç° |
| | | - `ruoyi-system/src/main/resources/mapper/system/VehicleAbnormalAlertMapper.xml` - MyBatisæ å°æä»¶ |
| | | - `ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/VehicleAbnormalAlertController.java` - æ§å¶å¨ |
| | | - `ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/VehicleAbnormalAlertTask.java` - 宿¶ä»»å¡ |
| | | |
| | | ### æ°æ®åºèæ¬ |
| | | - `sql/vehicle_abnormal_alert.sql` - 建表ååå§åèæ¬ |
| | | |
| | | ### ææ¡£ |
| | | - æ¬ææ¡£ - 宿´çåè½è¯´æåä½¿ç¨æå |
| | | |
| | | ## æ´æ°æ¥å¿ |
| | | |
| | | ### V1.0.0 (2026-01-12) |
| | | - â
å®ç°åºç¡çæ§åè½ |
| | | - â
æ¯æä¼ä¸å¾®ä¿¡éç¥ |
| | | - â
宿´ç管ççé¢ |
| | | - â
çµæ´»çé
ç½®ç³»ç» |
| | | - â
åè¦é¢çæ§å¶ |
| | | - â
æ°æ®å¯¼åºåè½ |
| | |
| | | |
| | | import com.ruoyi.common.core.controller.BaseController; |
| | | import com.ruoyi.common.core.domain.AjaxResult; |
| | | import com.ruoyi.common.utils.HospitalTokenizerUtil; |
| | | import com.ruoyi.common.utils.StringUtils; |
| | | import com.ruoyi.system.domain.HospData; |
| | | import com.ruoyi.system.domain.HospitalTokenizerTask; |
| | | import com.ruoyi.system.domain.TbHospData; |
| | | import com.ruoyi.system.mapper.HospDataMapper; |
| | | import com.ruoyi.system.service.HospitalTokenizerAsyncService; |
| | | import com.ruoyi.system.service.ISQLHospDataService; |
| | | import com.ruoyi.system.service.ITbHospDataService; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RequestParam; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.Comparator; |
| | | import java.util.HashSet; |
| | | import java.util.List; |
| | | import java.util.Set; |
| | | import java.util.UUID; |
| | | |
| | | /** |
| | | * å»é¢æ°æ®Controller |
| | |
| | | |
| | | @Autowired |
| | | private ISQLHospDataService sqlHospDataService; |
| | | |
| | | @Autowired |
| | | private ITbHospDataService tbHospDataService; |
| | | |
| | | @Autowired |
| | | private com.ruoyi.system.mapper.TbHospDataMapper tbHospDataMapper; |
| | | |
| | | @Autowired |
| | | private HospitalTokenizerAsyncService asyncService; |
| | | |
| | | /** |
| | | * æç´¢å»é¢ï¼ä»MySQL tb_hosp_data表æ¥è¯¢ï¼ |
| | |
| | | |
| | | return success(hospitals); |
| | | } |
| | | |
| | | /** |
| | | * æ¹éçæææå»é¢çåè¯ï¼å¼æ¥ï¼ |
| | | * 管ç忥å£ï¼ç¨äºåå§åæéæ°çæå»é¢åè¯ |
| | | * |
| | | * @return ä»»å¡ID |
| | | */ |
| | | @GetMapping("/generateKeywords") |
| | | public AjaxResult generateAllHospitalKeywords() { |
| | | logger.info("å¼å§æ¹éçæå»é¢åè¯ï¼å¼æ¥ï¼..."); |
| | | |
| | | try { |
| | | // çæä»»å¡ID |
| | | String taskId = UUID.randomUUID().toString().replace("-", ""); |
| | | |
| | | // 弿¥æ§è¡ä»»å¡ |
| | | asyncService.executeTokenizerTask(taskId); |
| | | |
| | | logger.info("å»é¢åè¯ä»»å¡å·²å¯å¨: taskId={}", taskId); |
| | | |
| | | // ç«å³è¿åä»»å¡ID |
| | | return success() |
| | | .put("taskId", taskId) |
| | | .put("message", "åè¯ä»»å¡å·²å¯å¨ï¼è¯·æ¥è¯¢ä»»å¡è¿åº¦"); |
| | | |
| | | } catch (Exception e) { |
| | | logger.error("å¯å¨å»é¢åè¯ä»»å¡å¤±è´¥", e); |
| | | return error("å¯å¨å¤±è´¥ï¼" + e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * æ¥è¯¢å»é¢åè¯ä»»å¡è¿åº¦ |
| | | * |
| | | * @param taskId ä»»å¡ID |
| | | * @return ä»»å¡è¿åº¦ä¿¡æ¯ |
| | | */ |
| | | @GetMapping("/getTaskProgress") |
| | | public AjaxResult getTaskProgress(@RequestParam("taskId") String taskId) { |
| | | try { |
| | | HospitalTokenizerTask task = asyncService.getTaskStatus(taskId); |
| | | |
| | | if (task == null) { |
| | | return error("ä»»å¡ä¸åå¨æå·²è¿æ"); |
| | | } |
| | | |
| | | return success(task); |
| | | |
| | | } catch (Exception e) { |
| | | logger.error("æ¥è¯¢ä»»å¡è¿åº¦å¤±è´¥: taskId={}", taskId, e); |
| | | return error("æ¥è¯¢å¤±è´¥ï¼" + e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * åºäºåè¯å¹é
æç´¢å»é¢ |
| | | * åç«¯ä¼ å
¥å»é¢ä¿¡æ¯ï¼è¿è¡åè¯å䏿°æ®åºä¸çåè¯å¹é
|
| | | * æ ¹æ®å¹é
çåè¯æ°éè¿è¡æéæåºï¼å¹é
è¶å¤æåè¶é å |
| | | * |
| | | * @param searchText æç´¢ææ¬ï¼å»é¢åç§°ãå°åçï¼ |
| | | * @param pageSize è¿åç»ææ°ééå¶ï¼é»è®¤50ï¼ |
| | | * @return å¹é
çå»é¢åè¡¨ï¼æå¹é
度æåºï¼ |
| | | */ |
| | | @GetMapping("/searchByKeywords") |
| | | public AjaxResult searchHospitalsByKeywords( |
| | | @RequestParam("searchText") String searchText, |
| | | @RequestParam(value = "pageSize", required = false, defaultValue = "50") Integer pageSize) { |
| | | |
| | | logger.info("åºäºåè¯å¹é
æç´¢å»é¢ï¼searchText={}, pageSize={}", searchText, pageSize); |
| | | |
| | | if (searchText == null || searchText.trim().isEmpty()) { |
| | | return error("æç´¢ææ¬ä¸è½ä¸ºç©º"); |
| | | } |
| | | |
| | | try { |
| | | long startTime = System.currentTimeMillis(); |
| | | |
| | | // 1. 对åç«¯ä¼ å
¥çæç´¢ææ¬è¿è¡åè¯ |
| | | String searchKeywords = HospitalTokenizerUtil.tokenizeSearchText(searchText); |
| | | logger.info("æç´¢ææ¬åè¯ç»æï¼{}", searchKeywords); |
| | | |
| | | if (searchKeywords.isEmpty()) { |
| | | return success(new ArrayList<>()); |
| | | } |
| | | |
| | | // 2. å°åè¯ç»ææå为å
³é®è¯å表ï¼ç¨äºæ°æ®åºé¢è¿æ»¤ |
| | | String[] keywordArray = searchKeywords.split(","); |
| | | List<String> keywordList = new ArrayList<>(); |
| | | for (String keyword : keywordArray) { |
| | | String trimmed = keyword.trim(); |
| | | if (!trimmed.isEmpty() && trimmed.length() >= 2) { // åªä½¿ç¨2个å以ä¸çå
³é®è¯ |
| | | keywordList.add(trimmed); |
| | | } |
| | | } |
| | | |
| | | if (keywordList.isEmpty()) { |
| | | logger.warn("æ²¡æææçå
³é®è¯ç¨äºæç´¢"); |
| | | return success(new ArrayList<>()); |
| | | } |
| | | |
| | | logger.info("使ç¨å
³é®è¯è¿è¡æ°æ®åºé¢è¿æ»¤: {}", keywordList); |
| | | |
| | | // 3. éè¿æ°æ®åºå±é¢é¢è¿æ»¤ï¼åªæ¥è¯¢å¯è½å¹é
çå»é¢ï¼è䏿¯ææå»é¢ï¼ |
| | | List<TbHospData> candidateHospitals = tbHospDataMapper.selectTbHospDataByKeywords(keywordList, "0"); |
| | | |
| | | long queryTime = System.currentTimeMillis(); |
| | | logger.info("æ°æ®åºé¢è¿æ»¤å®æï¼åéå»é¢æ°é: {}, èæ¶: {}ms", candidateHospitals.size(), queryTime - startTime); |
| | | |
| | | // 4. æååéå»é¢çå°åºåç§°ï¼ä» hopsArea åæ®µï¼ |
| | | Set<String> districtNames = new HashSet<>(); |
| | | for (TbHospData hospital : candidateHospitals) { |
| | | if (StringUtils.isNotBlank(hospital.getHopsArea())) { |
| | | // æåå°åºåï¼ç§»é¤å¸¸è§åç¼ |
| | | String area = hospital.getHopsArea() |
| | | .replace("åº", "") |
| | | .replace("å¸", "") |
| | | .replace("å¿", "") |
| | | .trim(); |
| | | if (area.length() > 0) { |
| | | districtNames.add(area); |
| | | } |
| | | } |
| | | } |
| | | |
| | | logger.info("æåå° {} 个ç¬ç¹å°åºåç§°", districtNames.size()); |
| | | |
| | | // 5. 对åéå»é¢è®¡ç®å¹é
åæ°ï¼å¹¶è¿æ»¤åºæå¹é
çå»é¢ |
| | | List<HospitalMatchResult> matchResults = new ArrayList<>(); |
| | | |
| | | long matchStartTime = System.currentTimeMillis(); |
| | | |
| | | for (TbHospData hospital : candidateHospitals) { |
| | | if (hospital.getHospKeywords() == null || hospital.getHospKeywords().isEmpty()) { |
| | | continue; |
| | | } |
| | | |
| | | // 计ç®å¹é
åæ°ï¼ä¼ å
¥å»é¢åç§°åå°åºåç§°éåï¼ |
| | | int matchScore = HospitalTokenizerUtil.calculateMatchScore( |
| | | searchKeywords, |
| | | hospital.getHospKeywords(), |
| | | hospital.getHospName(), |
| | | districtNames |
| | | ); |
| | | |
| | | // åªä¿çæå¹é
çå»é¢ |
| | | if (matchScore > 0) { |
| | | matchResults.add(new HospitalMatchResult(hospital, matchScore)); |
| | | } |
| | | } |
| | | |
| | | long matchTime = System.currentTimeMillis(); |
| | | logger.info("å¹é
计ç®å®æï¼æ¾å° {} 个å¹é
çå»é¢ï¼èæ¶: {}ms", matchResults.size(), matchTime - matchStartTime); |
| | | |
| | | // 4. æå¹é
åæ°éåºæåºï¼åæ°è¶é«æåè¶é åï¼ |
| | | matchResults.sort(Comparator.comparingInt(HospitalMatchResult::getMatchScore).reversed()); |
| | | |
| | | // 5. éå¶è¿åæ°é |
| | | if (pageSize != null && pageSize > 0 && matchResults.size() > pageSize) { |
| | | matchResults = matchResults.subList(0, pageSize); |
| | | } |
| | | |
| | | // 6. 转æ¢ä¸ºHospData对象è¿åï¼å
å«å¹é
åæ°ï¼ |
| | | List<HospDataWithScore> result = new ArrayList<>(); |
| | | for (HospitalMatchResult matchResult : matchResults) { |
| | | TbHospData tbHospData = matchResult.getHospital(); |
| | | HospData hospData = convertToHospData(tbHospData); |
| | | result.add(new HospDataWithScore(hospData, matchResult.getMatchScore())); |
| | | logger.debug("å»é¢: {}, å¹é
åæ°: {}", |
| | | hospData.getHospName(), matchResult.getMatchScore()); |
| | | } |
| | | |
| | | logger.info("è¿å {} 个å»é¢ç»æ", result.size()); |
| | | |
| | | long totalTime = System.currentTimeMillis() - startTime; |
| | | logger.info("æç´¢å®æ - æ»èæ¶: {}ms, æ°æ®åºæ¥è¯¢: {}ms, å¹é
计ç®: {}ms", |
| | | totalTime, queryTime - startTime, matchTime - matchStartTime); |
| | | |
| | | return success(result); |
| | | |
| | | } catch (Exception e) { |
| | | logger.error("åè¯å¹é
æç´¢å¤±è´¥", e); |
| | | return error("æç´¢å¤±è´¥ï¼" + e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * å°TbHospData转æ¢ä¸ºHospData |
| | | */ |
| | | private HospData convertToHospData(TbHospData tbHospData) { |
| | | HospData hospData = new HospData(); |
| | | hospData.setHospId(tbHospData.getLegacyHospId()); |
| | | hospData.setHospName(tbHospData.getHospName()); |
| | | hospData.setHospCityId(tbHospData.getHospCityId()); |
| | | hospData.setHospShort(tbHospData.getHospShort()); |
| | | hospData.setHopsProvince(tbHospData.getHopsProvince()); |
| | | hospData.setHopsCity(tbHospData.getHopsCity()); |
| | | hospData.setHopsArea(tbHospData.getHopsArea()); |
| | | hospData.setHospAddress(tbHospData.getHospAddress()); |
| | | hospData.setHospTel(tbHospData.getHospTel()); |
| | | hospData.setHospUnitId(tbHospData.getHospUnitId()); |
| | | hospData.setHospState(tbHospData.getHospState()); |
| | | hospData.setHospOaId(tbHospData.getHospOaId()); |
| | | hospData.setHospIntroducerId(tbHospData.getHospIntroducerId()); |
| | | if (tbHospData.getHospIntroducerDate() != null) { |
| | | hospData.setHospIntroducerDate(tbHospData.getHospIntroducerDate().toString()); |
| | | } |
| | | hospData.setHospLevel(tbHospData.getHospLevel()); |
| | | return hospData; |
| | | } |
| | | |
| | | /** |
| | | * å»é¢å¹é
ç»æå
é¨ç±» |
| | | */ |
| | | private static class HospitalMatchResult { |
| | | private TbHospData hospital; |
| | | private int matchScore; |
| | | |
| | | public HospitalMatchResult(TbHospData hospital, int matchScore) { |
| | | this.hospital = hospital; |
| | | this.matchScore = matchScore; |
| | | } |
| | | |
| | | public TbHospData getHospital() { |
| | | return hospital; |
| | | } |
| | | |
| | | public int getMatchScore() { |
| | | return matchScore; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * å»é¢æ°æ®ä¸å¹é
åæ°å
è£
ç±» |
| | | */ |
| | | private static class HospDataWithScore { |
| | | private HospData hospital; |
| | | private int matchScore; |
| | | |
| | | public HospDataWithScore(HospData hospital, int matchScore) { |
| | | this.hospital = hospital; |
| | | this.matchScore = matchScore; |
| | | } |
| | | |
| | | public HospData getHospital() { |
| | | return hospital; |
| | | } |
| | | |
| | | public int getMatchScore() { |
| | | return matchScore; |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | package com.ruoyi.web.controller.system; |
| | | |
| | | import java.util.List; |
| | | import javax.servlet.http.HttpServletResponse; |
| | | import org.springframework.security.access.prepost.PreAuthorize; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.PutMapping; |
| | | import org.springframework.web.bind.annotation.DeleteMapping; |
| | | import org.springframework.web.bind.annotation.PathVariable; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | import com.ruoyi.common.annotation.Log; |
| | | import com.ruoyi.common.core.controller.BaseController; |
| | | import com.ruoyi.common.core.domain.AjaxResult; |
| | | import com.ruoyi.common.enums.BusinessType; |
| | | import com.ruoyi.system.domain.VehicleAbnormalAlert; |
| | | import com.ruoyi.system.service.IVehicleAbnormalAlertService; |
| | | import com.ruoyi.common.utils.poi.ExcelUtil; |
| | | import com.ruoyi.common.core.page.TableDataInfo; |
| | | |
| | | /** |
| | | * 车è¾å¼å¸¸åè¦Controller |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | @RestController |
| | | @RequestMapping("/system/vehicleAlert") |
| | | public class VehicleAbnormalAlertController extends BaseController { |
| | | |
| | | @Autowired |
| | | private IVehicleAbnormalAlertService vehicleAbnormalAlertService; |
| | | |
| | | /** |
| | | * æ¥è¯¢è½¦è¾å¼å¸¸åè¦å表 |
| | | */ |
| | | @PreAuthorize("@ss.hasPermi('system:vehicleAlert:list')") |
| | | @GetMapping("/list") |
| | | public TableDataInfo list(VehicleAbnormalAlert vehicleAbnormalAlert) { |
| | | startPage(); |
| | | List<VehicleAbnormalAlert> list = vehicleAbnormalAlertService.selectVehicleAbnormalAlertList(vehicleAbnormalAlert); |
| | | return getDataTable(list); |
| | | } |
| | | |
| | | /** |
| | | * 导åºè½¦è¾å¼å¸¸åè¦å表 |
| | | */ |
| | | @PreAuthorize("@ss.hasPermi('system:vehicleAlert:export')") |
| | | @Log(title = "车è¾å¼å¸¸åè¦", businessType = BusinessType.EXPORT) |
| | | @PostMapping("/export") |
| | | public void export(HttpServletResponse response, VehicleAbnormalAlert vehicleAbnormalAlert) { |
| | | List<VehicleAbnormalAlert> list = vehicleAbnormalAlertService.selectVehicleAbnormalAlertList(vehicleAbnormalAlert); |
| | | ExcelUtil<VehicleAbnormalAlert> util = new ExcelUtil<VehicleAbnormalAlert>(VehicleAbnormalAlert.class); |
| | | util.exportExcel(response, list, "车è¾å¼å¸¸åè¦æ°æ®"); |
| | | } |
| | | |
| | | /** |
| | | * è·å车è¾å¼å¸¸åè¦è¯¦ç»ä¿¡æ¯ |
| | | */ |
| | | @PreAuthorize("@ss.hasPermi('system:vehicleAlert:query')") |
| | | @GetMapping(value = "/{alertId}") |
| | | public AjaxResult getInfo(@PathVariable("alertId") Long alertId) { |
| | | return success(vehicleAbnormalAlertService.selectVehicleAbnormalAlertByAlertId(alertId)); |
| | | } |
| | | |
| | | /** |
| | | * æ°å¢è½¦è¾å¼å¸¸åè¦ |
| | | */ |
| | | @PreAuthorize("@ss.hasPermi('system:vehicleAlert:add')") |
| | | @Log(title = "车è¾å¼å¸¸åè¦", businessType = BusinessType.INSERT) |
| | | @PostMapping |
| | | public AjaxResult add(@RequestBody VehicleAbnormalAlert vehicleAbnormalAlert) { |
| | | return toAjax(vehicleAbnormalAlertService.insertVehicleAbnormalAlert(vehicleAbnormalAlert)); |
| | | } |
| | | |
| | | /** |
| | | * ä¿®æ¹è½¦è¾å¼å¸¸åè¦ |
| | | */ |
| | | @PreAuthorize("@ss.hasPermi('system:vehicleAlert:edit')") |
| | | @Log(title = "车è¾å¼å¸¸åè¦", businessType = BusinessType.UPDATE) |
| | | @PutMapping |
| | | public AjaxResult edit(@RequestBody VehicleAbnormalAlert vehicleAbnormalAlert) { |
| | | return toAjax(vehicleAbnormalAlertService.updateVehicleAbnormalAlert(vehicleAbnormalAlert)); |
| | | } |
| | | |
| | | /** |
| | | * å é¤è½¦è¾å¼å¸¸åè¦ |
| | | */ |
| | | @PreAuthorize("@ss.hasPermi('system:vehicleAlert:remove')") |
| | | @Log(title = "车è¾å¼å¸¸åè¦", businessType = BusinessType.DELETE) |
| | | @DeleteMapping("/{alertIds}") |
| | | public AjaxResult remove(@PathVariable Long[] alertIds) { |
| | | return toAjax(vehicleAbnormalAlertService.deleteVehicleAbnormalAlertByAlertIds(alertIds)); |
| | | } |
| | | |
| | | /** |
| | | * å¤çåè¦ |
| | | */ |
| | | @PreAuthorize("@ss.hasPermi('system:vehicleAlert:handle')") |
| | | @Log(title = "å¤ç车è¾åè¦", businessType = BusinessType.UPDATE) |
| | | @PutMapping("/handle/{alertId}") |
| | | public AjaxResult handleAlert(@PathVariable Long alertId, @RequestBody VehicleAbnormalAlert vehicleAbnormalAlert) { |
| | | Long handlerId = getUserId(); |
| | | String handlerName = getUsername(); |
| | | String handleRemark = vehicleAbnormalAlert.getHandleRemark(); |
| | | |
| | | return toAjax(vehicleAbnormalAlertService.handleAlert(alertId, handlerId, handlerName, handleRemark)); |
| | | } |
| | | |
| | | /** |
| | | * æ¹éå¤çåè¦ |
| | | */ |
| | | @PreAuthorize("@ss.hasPermi('system:vehicleAlert:handle')") |
| | | @Log(title = "æ¹éå¤ç车è¾åè¦", businessType = BusinessType.UPDATE) |
| | | @PutMapping("/batchHandle") |
| | | public AjaxResult batchHandleAlert(@RequestBody VehicleAbnormalAlert vehicleAbnormalAlert) { |
| | | Long[] alertIds = vehicleAbnormalAlert.getAlertId() != null ? |
| | | new Long[]{vehicleAbnormalAlert.getAlertId()} : new Long[0]; |
| | | Long handlerId = getUserId(); |
| | | String handlerName = getUsername(); |
| | | String handleRemark = vehicleAbnormalAlert.getHandleRemark(); |
| | | |
| | | return toAjax(vehicleAbnormalAlertService.batchHandleAlert(alertIds, handlerId, handlerName, handleRemark)); |
| | | } |
| | | |
| | | /** |
| | | * è·åæªå¤çåè¦æ°é |
| | | */ |
| | | @GetMapping("/unhandledCount") |
| | | public AjaxResult getUnhandledCount() { |
| | | VehicleAbnormalAlert query = new VehicleAbnormalAlert(); |
| | | query.setStatus("0"); // æªå¤ç |
| | | List<VehicleAbnormalAlert> list = vehicleAbnormalAlertService.selectVehicleAbnormalAlertList(query); |
| | | return success(list.size()); |
| | | } |
| | | |
| | | /** |
| | | * è·å仿¥åè¦æ°é |
| | | */ |
| | | @GetMapping("/todayCount") |
| | | public AjaxResult getTodayCount() { |
| | | VehicleAbnormalAlert query = new VehicleAbnormalAlert(); |
| | | query.setAlertDate(new java.util.Date()); |
| | | List<VehicleAbnormalAlert> list = vehicleAbnormalAlertService.selectVehicleAbnormalAlertList(query); |
| | | return success(list.size()); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.ruoyi.web.controller.system; |
| | | |
| | | import java.util.List; |
| | | import javax.servlet.http.HttpServletResponse; |
| | | import org.springframework.security.access.prepost.PreAuthorize; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.PutMapping; |
| | | import org.springframework.web.bind.annotation.DeleteMapping; |
| | | import org.springframework.web.bind.annotation.PathVariable; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | import com.ruoyi.common.annotation.Log; |
| | | import com.ruoyi.common.core.controller.BaseController; |
| | | import com.ruoyi.common.core.domain.AjaxResult; |
| | | import com.ruoyi.common.enums.BusinessType; |
| | | import com.ruoyi.system.domain.VehicleAlertConfig; |
| | | import com.ruoyi.system.service.IVehicleAlertConfigService; |
| | | import com.ruoyi.common.utils.poi.ExcelUtil; |
| | | import com.ruoyi.common.core.page.TableDataInfo; |
| | | |
| | | /** |
| | | * 车è¾åè¦é
ç½®Controller |
| | | * |
| | | * @author ruoyi |
| | | * @date 2026-01-12 |
| | | */ |
| | | @RestController |
| | | @RequestMapping("/system/vehicleAlertConfig") |
| | | public class VehicleAlertConfigController extends BaseController |
| | | { |
| | | @Autowired |
| | | private IVehicleAlertConfigService vehicleAlertConfigService; |
| | | |
| | | /** |
| | | * æ¥è¯¢è½¦è¾åè¦é
ç½®å表 |
| | | */ |
| | | @PreAuthorize("@ss.hasPermi('system:vehicleAlertConfig:list')") |
| | | @GetMapping("/list") |
| | | public TableDataInfo list(VehicleAlertConfig vehicleAlertConfig) |
| | | { |
| | | startPage(); |
| | | List<VehicleAlertConfig> list = vehicleAlertConfigService.selectVehicleAlertConfigList(vehicleAlertConfig); |
| | | return getDataTable(list); |
| | | } |
| | | |
| | | /** |
| | | * 导åºè½¦è¾åè¦é
ç½®å表 |
| | | */ |
| | | @PreAuthorize("@ss.hasPermi('system:vehicleAlertConfig:export')") |
| | | @Log(title = "车è¾åè¦é
ç½®", businessType = BusinessType.EXPORT) |
| | | @PostMapping("/export") |
| | | public void export(HttpServletResponse response, VehicleAlertConfig vehicleAlertConfig) |
| | | { |
| | | List<VehicleAlertConfig> list = vehicleAlertConfigService.selectVehicleAlertConfigList(vehicleAlertConfig); |
| | | ExcelUtil<VehicleAlertConfig> util = new ExcelUtil<VehicleAlertConfig>(VehicleAlertConfig.class); |
| | | util.exportExcel(response, list, "车è¾åè¦é
ç½®æ°æ®"); |
| | | } |
| | | |
| | | /** |
| | | * è·å车è¾åè¦é
置详ç»ä¿¡æ¯ |
| | | */ |
| | | @PreAuthorize("@ss.hasPermi('system:vehicleAlertConfig:query')") |
| | | @GetMapping(value = "/{configId}") |
| | | public AjaxResult getInfo(@PathVariable("configId") Long configId) |
| | | { |
| | | return success(vehicleAlertConfigService.selectVehicleAlertConfigByConfigId(configId)); |
| | | } |
| | | |
| | | /** |
| | | * æ°å¢è½¦è¾åè¦é
ç½® |
| | | */ |
| | | @PreAuthorize("@ss.hasPermi('system:vehicleAlertConfig:add')") |
| | | @Log(title = "车è¾åè¦é
ç½®", businessType = BusinessType.INSERT) |
| | | @PostMapping |
| | | public AjaxResult add(@RequestBody VehicleAlertConfig vehicleAlertConfig) |
| | | { |
| | | vehicleAlertConfig.setCreateBy(getUsername()); |
| | | return toAjax(vehicleAlertConfigService.insertVehicleAlertConfig(vehicleAlertConfig)); |
| | | } |
| | | |
| | | /** |
| | | * ä¿®æ¹è½¦è¾åè¦é
ç½® |
| | | */ |
| | | @PreAuthorize("@ss.hasPermi('system:vehicleAlertConfig:edit')") |
| | | @Log(title = "车è¾åè¦é
ç½®", businessType = BusinessType.UPDATE) |
| | | @PutMapping |
| | | public AjaxResult edit(@RequestBody VehicleAlertConfig vehicleAlertConfig) |
| | | { |
| | | vehicleAlertConfig.setUpdateBy(getUsername()); |
| | | return toAjax(vehicleAlertConfigService.updateVehicleAlertConfig(vehicleAlertConfig)); |
| | | } |
| | | |
| | | /** |
| | | * å é¤è½¦è¾åè¦é
ç½® |
| | | */ |
| | | @PreAuthorize("@ss.hasPermi('system:vehicleAlertConfig:remove')") |
| | | @Log(title = "车è¾åè¦é
ç½®", businessType = BusinessType.DELETE) |
| | | @DeleteMapping("/{configIds}") |
| | | public AjaxResult remove(@PathVariable Long[] configIds) |
| | | { |
| | | return toAjax(vehicleAlertConfigService.deleteVehicleAlertConfigByConfigIds(configIds)); |
| | | } |
| | | } |
| | |
| | | package com.ruoyi.web.controller.system; |
| | | |
| | | import com.ruoyi.common.annotation.Log; |
| | | import com.ruoyi.common.core.controller.BaseController; |
| | | import com.ruoyi.common.core.domain.AjaxResult; |
| | | import com.ruoyi.common.core.page.TableDataInfo; |
| | | import com.ruoyi.common.enums.BusinessType; |
| | | import com.ruoyi.system.domain.VehicleInfo; |
| | | import com.ruoyi.system.domain.VehicleSyncDTO; |
| | | import com.ruoyi.system.domain.vo.VehicleSyncVO; |
| | | import com.ruoyi.system.mapper.VehicleInfoMapper; |
| | | import com.ruoyi.system.service.IVehicleInfoService; |
| | | import com.ruoyi.system.service.IVehicleSyncDataService; |
| | | import com.ruoyi.system.service.IVehicleSyncService; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.security.access.prepost.PreAuthorize; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.HashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * 车è¾åæ¥Controller |
| | | * |
| | | * 车è¾åæ¥ç®¡çController |
| | | * |
| | | * @author ruoyi |
| | | * @date 2025-10-20 |
| | | */ |
| | | @RestController |
| | | @RequestMapping("/system/vehicle/sync") |
| | | public class VehicleSyncController extends BaseController |
| | | { |
| | | @Autowired |
| | | private IVehicleSyncService vehicleSyncService; |
| | | @RequestMapping("/system/vehicleSync") |
| | | public class VehicleSyncController extends BaseController { |
| | | |
| | | private static final Logger log = LoggerFactory.getLogger(VehicleSyncController.class); |
| | | |
| | | @Autowired |
| | | private IVehicleSyncDataService vehicleSyncDataService; |
| | | |
| | | @Autowired |
| | | private VehicleInfoMapper vehicleInfoMapper; |
| | | |
| | | @Autowired |
| | | private IVehicleInfoService vehicleInfoService; |
| | | |
| | | /** |
| | | * æå¨åæ¥æ§ç³»ç»è½¦è¾æ°æ® |
| | | * æ¥è¯¢è½¦è¾åæ¥åè¡¨ï¼æ¾ç¤ºæ§ç³»ç»è½¦è¾ååæ¥ç¶æï¼ |
| | | */ |
| | | @PreAuthorize("@ss.hasPermi('system:vehicle:sync')") |
| | | @PostMapping("/legacy") |
| | | public AjaxResult syncLegacyVehicles() |
| | | { |
| | | return vehicleSyncService.syncVehicles(this.vehicleSyncDataService.getVehiclesFromSqlServer()); |
| | | @PreAuthorize("@ss.hasPermi('system:vehicleSync:list')") |
| | | @GetMapping("/list") |
| | | public TableDataInfo list() { |
| | | try { |
| | | // 1. 仿§ç³»ç»è·å车è¾å表 |
| | | List<VehicleSyncDTO> oldVehicles = vehicleSyncDataService.getVehiclesFromSqlServer(); |
| | | |
| | | if (oldVehicles == null || oldVehicles.isEmpty()) { |
| | | return getDataTable(new ArrayList<>()); |
| | | } |
| | | |
| | | // 2. 转æ¢ä¸ºVOå¹¶æ£æ¥åæ¥ç¶æ |
| | | List<VehicleSyncVO> voList = new ArrayList<>(); |
| | | for (VehicleSyncDTO dto : oldVehicles) { |
| | | VehicleSyncVO vo = convertToVO(dto); |
| | | |
| | | // 3. æ£æ¥è¯¥è½¦è¾æ¯å¦å·²åæ¥å°æ°ç³»ç» |
| | | VehicleInfo existVehicle = vehicleInfoMapper.selectVehicleInfoByCarId(dto.getCarId()); |
| | | if (existVehicle != null) { |
| | | vo.setSynced(true); |
| | | vo.setVehicleId(existVehicle.getVehicleId()); |
| | | vo.setDeptId(existVehicle.getDeptId()); |
| | | if (existVehicle.getDeptName() != null) { |
| | | vo.setDeptName(existVehicle.getDeptName()); |
| | | } |
| | | } else { |
| | | vo.setSynced(false); |
| | | } |
| | | |
| | | voList.add(vo); |
| | | } |
| | | |
| | | return getDataTable(voList); |
| | | |
| | | } catch (Exception e) { |
| | | log.error("æ¥è¯¢è½¦è¾åæ¥å表失败", e); |
| | | return getDataTable(new ArrayList<>()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * æå¨åæ¥å个车è¾å°æ°ç³»ç» |
| | | */ |
| | | @PreAuthorize("@ss.hasPermi('system:vehicleSync:sync')") |
| | | @Log(title = "车è¾åæ¥", businessType = BusinessType.INSERT) |
| | | @PostMapping("/syncVehicle") |
| | | public AjaxResult syncVehicle(@RequestBody Map<String, Object> params) { |
| | | try { |
| | | Integer carId = (Integer) params.get("carId"); |
| | | String vehicleNo = (String) params.get("vehicleNo"); |
| | | Long deptId = params.get("deptId") != null ? Long.valueOf(params.get("deptId").toString()) : null; |
| | | |
| | | if (carId == null || vehicleNo == null || deptId == null) { |
| | | return AjaxResult.error("åæ°ä¸å®æ´ï¼carIdãvehicleNoãdeptId ä¸è½ä¸ºç©º"); |
| | | } |
| | | |
| | | // 1. æ£æ¥æ¯å¦å·²åå¨ |
| | | VehicleInfo existVehicle = vehicleInfoMapper.selectVehicleInfoByCarId(carId); |
| | | if (existVehicle != null) { |
| | | return AjaxResult.error("该车è¾å·²åæ¥ï¼è½¦è¾ID: " + existVehicle.getVehicleId()); |
| | | } |
| | | |
| | | // 2. å建æ°è½¦è¾è®°å½ |
| | | VehicleInfo newVehicle = new VehicleInfo(); |
| | | newVehicle.setCarId(carId); |
| | | newVehicle.setVehicleNo(vehicleNo); |
| | | newVehicle.setDeptId(deptId); |
| | | newVehicle.setStatus("0"); // é»è®¤æ£å¸¸ç¶æ |
| | | newVehicle.setCreateBy(getUsername()); |
| | | |
| | | // 3. æå
¥è½¦è¾ä¿¡æ¯ |
| | | int result = vehicleInfoMapper.insertVehicleInfo(newVehicle); |
| | | |
| | | if (result > 0) { |
| | | log.info("æå¨åæ¥è½¦è¾æåï¼carId={}, vehicleNo={}, vehicleId={}", |
| | | carId, vehicleNo, newVehicle.getVehicleId()); |
| | | return AjaxResult.success("忥æå", newVehicle.getVehicleId()); |
| | | } else { |
| | | return AjaxResult.error("åæ¥å¤±è´¥"); |
| | | } |
| | | |
| | | } catch (Exception e) { |
| | | log.error("æå¨åæ¥è½¦è¾å¤±è´¥", e); |
| | | return AjaxResult.error("åæ¥å¤±è´¥ï¼" + e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * å° DTO 转æ¢ä¸º VO |
| | | */ |
| | | private VehicleSyncVO convertToVO(VehicleSyncDTO dto) { |
| | | VehicleSyncVO vo = new VehicleSyncVO(); |
| | | vo.setCarId(dto.getCarId()); |
| | | vo.setVehicleNo(dto.getCarLicense()); |
| | | vo.setCarOrdClass(dto.getCarOrdClass()); |
| | | |
| | | // æ ¹æ®carOrdClassæ å°åå
¬å¸åç§° |
| | | String deptName = mapCarOrdClassToDeptName(dto.getCarOrdClass()); |
| | | vo.setDeptName(deptName); |
| | | |
| | | return vo; |
| | | } |
| | | |
| | | /** |
| | | * æ ¹æ®åæ®ç±»åç¼ç æ å°åå
¬å¸åç§° |
| | | * å¯ä»¥ä»é
ç½®ææ°æ®åºè¯»åï¼è¿éç®åå¤ç |
| | | */ |
| | | private String mapCarOrdClassToDeptName(String carOrdClass) { |
| | | // TODO: æ ¹æ®å®é
ä¸å¡è§åæ å° |
| | | // å¯ä»¥ä» sys_config æä¸é¨çæ å°è¡¨è¯»å |
| | | Map<String, String> mapping = new HashMap<>(); |
| | | mapping.put("01", "æ»å
¬å¸"); |
| | | mapping.put("02", "åå
¬å¸A"); |
| | | mapping.put("03", "åå
¬å¸B"); |
| | | |
| | | return mapping.getOrDefault(carOrdClass, "æªç¥åå
¬å¸"); |
| | | } |
| | | } |
| | |
| | | @Log(title = "ä»»å¡ç®¡ç", businessType = BusinessType.INSERT) |
| | | @PostMapping("/admin") |
| | | public AjaxResult adminAdd(@RequestBody TaskCreateVO createVO) { |
| | | return toAjax(sysTaskService.insertSysTask(createVO)); |
| | | Long taskId = sysTaskService.insertSysTask(createVO); |
| | | return taskId > 0 ? AjaxResult.success("æ°å¢æå").put("taskId", taskId) : AjaxResult.error("æ°å¢å¤±è´¥"); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * æ°å¢ä»»å¡ï¼APPç«¯ï¼ |
| | |
| | | @Log(title = "ä»»å¡å建", businessType = BusinessType.INSERT) |
| | | @PostMapping |
| | | public AjaxResult appAdd(@RequestBody TaskCreateVO createVO) { |
| | | return toAjax(sysTaskService.insertSysTask(createVO)); |
| | | Long taskId = sysTaskService.insertSysTask(createVO); |
| | | return taskId > 0 ? AjaxResult.success("æ°å¢æå").put("taskId", taskId) : AjaxResult.error("æ°å¢å¤±è´¥"); |
| | | } |
| | | |
| | | /** |
| | |
| | | tencent: |
| | | map: |
| | | key: 6YVBZ-ZJDLQ-JMY5F-BR7XG-H3TAV-C3FXC |
| | | |
| | | ocr: |
| | | secretId: AKID52kDPMUP5WgbGfohKhOvFMGwObYEgtsP |
| | | secretKey: kLjtpkZDcpnWBcpnY5NQEoAHAoFRN4Wl |
| | | # ç¾åº¦å°å¾é
ç½® |
| | | baidu: |
| | | map: |
| | | ak: GX7G1RmAbTEQHor9NKpzRiB2jerqaY1E # è¯·æ¿æ¢ä¸ºæ¨çç¾åº¦å°å¾API Key |
| | | |
| | | ocr: |
| | | appId: 121882692 |
| | | apiKey: zo03Zf3wIU7awOlRnwurDmlO |
| | | secretKey: tXqyZ5AQ9aT3n1ON4ERQA1aQ88L1sXFw |
| | | # 天å°å¾é
ç½® |
| | | tianditu: |
| | | map: |
| | |
| | | qrcode: |
| | | size: 300 |
| | | format: PNG |
| | | |
| | | ali: |
| | | ocr: |
| | | accessKeyId: LTAI5t7fbEzL7yctbNzA84Q2 |
| | | accessKeySecret: llHxCvmnhS5YSfRCWeuhr5KxgBTnnz |
| | | |
| | | # 对账é
ç½® |
| | | reconciliation: |
| | | enabled: true |
| | |
| | | <logger name="org.springframework" level="warn" /> |
| | | <!-- MyBatis SQLæ¥å¿ --> |
| | | <logger name="com.ruoyi.system.mapper" level="debug" /> |
| | | |
| | | |
| | | <root level="info"> |
| | | <appender-ref ref="console" /> |
| | | <appender-ref ref="file_info" /> |
| | |
| | | </root> |
| | | </springProfile> |
| | | |
| | | <!-- é»è®¤æ ¹æ¥å¿å¨é
ç½®ï¼å¦ææ²¡ææå®profileï¼ --> |
| | | <root level="info"> |
| | | <appender-ref ref="console" /> |
| | | <appender-ref ref="file_info" /> |
| | | <appender-ref ref="file_error" /> |
| | | </root> |
| | | |
| | | <!--ç³»ç»ç¨æ·æä½æ¥å¿--> |
| | | <logger name="sys-user" level="info"> |
| | | <appender-ref ref="sys-user"/> |
| | | <appender-ref ref="sys-user" /> |
| | | </logger> |
| | | </configuration> |
| | |
| | | <scope>provided</scope> |
| | | </dependency> |
| | | |
| | | <!-- HanLP 䏿åè¯åº --> |
| | | <dependency> |
| | | <groupId>com.hankcs</groupId> |
| | | <artifactId>hanlp</artifactId> |
| | | <version>portable-1.8.4</version> |
| | | </dependency> |
| | | |
| | | </dependencies> |
| | | |
| | | </project> |
| New file |
| | |
| | | package com.ruoyi.common.utils; |
| | | |
| | | import com.hankcs.hanlp.HanLP; |
| | | import com.hankcs.hanlp.seg.common.Term; |
| | | import org.apache.commons.lang3.StringUtils; |
| | | |
| | | import java.util.*; |
| | | import java.util.stream.Collectors; |
| | | |
| | | import static com.hankcs.hanlp.utility.TextUtility.isChinese; |
| | | |
| | | /** |
| | | * å»é¢ä¿¡æ¯åè¯å·¥å
·ç±» |
| | | * ä½¿ç¨ HanLP ä¸ä¸ä¸æåè¯åºè¿è¡åè¯å¤ç |
| | | * |
| | | * @author ruoyi |
| | | * @date 2026-01-20 |
| | | */ |
| | | public class HospitalTokenizerUtil { |
| | | |
| | | /** |
| | | * åç¨è¯éåï¼éè¦è¿æ»¤ç常è§è¯æ±ï¼ |
| | | * 注æï¼âåºâãâä¸âçå¨å»é¢åç§°ä¸ææä¹ï¼ä¸åºè¿æ»¤ |
| | | */ |
| | | private static final Set<String> STOP_WORDS = new HashSet<>(Arrays.asList( |
| | | "å»é¢", "è¯æ", "å«ç", "é", "乡", |
| | | "è¡é", "è·¯", "å·", "æ ", "åå
", "室", "å±", "楼", "ç", "äº", |
| | | "å¨", "ä¸", "å", "å", "ç", "ä¹", "äº", "为", "æ", "æ " |
| | | )); |
| | | |
| | | /** |
| | | * 髿éè¯è¯ï¼å»çæºæç¹å¾è¯ï¼ |
| | | * 注æï¼å°åºåä¸åæ¾å¨é«æéè¯ä¸ï¼é¿å
åé¢å å
å«å
¶ä»å°åºåèè·å¾é¢å¤å å |
| | | */ |
| | | private static final Set<String> HIGH_WEIGHT_WORDS = new HashSet<>(Arrays.asList( |
| | | "人æ°", "ä¸å»", "ä¸è¥¿å»", "ä¸è¥¿å»ç»å", "å»ç", "å¦å¹¼", "å¿ç«¥", "è¤ç§", |
| | | "å£è
", "ç¼ç§", "骨ç§", "æ´å½¢", "ç²¾ç¥", "康å¤", "æ¥æ", "å»å¦é¢", |
| | | "å»ç§å¤§å¦", "ä¸ç§", "第ä¸", "第äº", "第ä¸", "第å", "第äº", |
| | | "ååº", "åå»", "ä¸å¿", "éå±", "çç«", "å¸ç«", "åºç«" |
| | | )); |
| | | |
| | | /** |
| | | * å»é¢åç§°åè¯çé«é¢å
³é®è¯åå
¸ï¼ç¨äºå¼ºå¶æå宿´å»çç¸å
³çè¯ï¼ |
| | | * ä»
å
å«å»çæºæç¸å
³è¯ï¼ä¸å
å«å
·ä½è¡æ¿å°åï¼é¿å
å°åºç¡¬ç¼ç |
| | | */ |
| | | private static final Set<String> HOSPITAL_KEYWORD_DICT = new HashSet<>(Arrays.asList( |
| | | "ä¸å»é¢", "ä¸å»å»é¢", "å¸å»é¢", "çå»é¢", "人æ°å»é¢", "ä¸å¿å»é¢", "å£è
å»é¢", |
| | | "å侨å»é¢", "å¿ç«¥å»é¢", "ç¼ç§ä¸å¿", "ç¦å©é¢", "é¨è¯é¨", "ä¸å±±å¤§å¦", "éå±å»é¢", |
| | | "åé¸ä»" |
| | | )); |
| | | |
| | | /** ç»åè¯çæçæå°å符é¿åº¦ */ |
| | | private static final int MIN_COMBINED_LEN = 4; |
| | | /** ç»åè¯çæçæå¤§å符é¿åº¦ */ |
| | | private static final int MAX_COMBINED_LEN = 30; |
| | | /** ç»åè¯çææ¶å
å«çæå¤§åè¯æ°éï¼æ·±åº¦ï¼ */ |
| | | private static final int MAX_COMBINED_WORDS = 10; |
| | | |
| | | /** |
| | | * 对å»é¢ä¿¡æ¯è¿è¡åè¯ï¼ä½¿ç¨ HanLPï¼ |
| | | * |
| | | * @param hospName å»é¢åç§° |
| | | * @param hospShort å»é¢ç®ç§° |
| | | * @param province ç份 |
| | | * @param city åå¸ |
| | | * @param area åºå |
| | | * @param address 详ç»å°å |
| | | * @return åè¯ç»æï¼éå·åéçå
³é®è¯åç¬¦ä¸²ï¼ |
| | | */ |
| | | public static String tokenize(String hospName, String hospShort, String province, |
| | | String city, String area, String address) { |
| | | Set<String> keywords = new LinkedHashSet<>(); |
| | | |
| | | // 1. è¡æ¿åºåï¼åªä½ä¸ºç¬ç«å
³é®è¯ï¼ä¸åä¸ç»å |
| | | if (StringUtils.isNotBlank(province)) { |
| | | keywords.add(province.trim()); |
| | | } |
| | | if (StringUtils.isNotBlank(city)) { |
| | | keywords.add(city.trim()); |
| | | } |
| | | if (StringUtils.isNotBlank(area)) { |
| | | keywords.add(area.trim()); |
| | | } |
| | | |
| | | // 2. å»é¢åç§°ï¼å»æçãå¸åç¼ï¼åªå¯¹âåº+å»é¢ä¸»ä½âååè¯åç»å |
| | | if (StringUtils.isNotBlank(hospName)) { |
| | | String nameForSeg = hospName.trim(); |
| | | |
| | | // 廿åé¢çç份 |
| | | if (StringUtils.isNotBlank(province) && nameForSeg.startsWith(province)) { |
| | | nameForSeg = nameForSeg.substring(province.length()); |
| | | } |
| | | // å廿åå¸ |
| | | if (StringUtils.isNotBlank(city) && nameForSeg.startsWith(city)) { |
| | | nameForSeg = nameForSeg.substring(city.length()); |
| | | } |
| | | // åºä¿çï¼ä¾å¦ "è¶ç§åºä¸å»å»é¢"ï¼è¿æ ·å¯ä»¥çæ "è¶ç§åºä¸å»é¢"ã"ä¸å»é¢" çç»åè¯ |
| | | keywords.addAll(extractKeywordsByHanLP(nameForSeg)); |
| | | // åºäºå»é¢å
¨ç§°ï¼å¼ºå¶æåé«é¢å»çå
³é®è¯ï¼å¦âä¸å»é¢ââå¿ç«¥å»é¢âçï¼ |
| | | addDictPhrases(hospName, keywords); |
| | | } |
| | | |
| | | // 3. å»é¢ç®ç§°ï¼é常ä¸å¸¦çå¸åºï¼ç´æ¥åè¯ |
| | | if (StringUtils.isNotBlank(hospShort)) { |
| | | keywords.addAll(extractKeywordsByHanLP(hospShort)); |
| | | addDictPhrases(hospShort, keywords); |
| | | } |
| | | |
| | | // 4. è¿æ»¤åç¨è¯åæ æè¯ |
| | | keywords = keywords.stream() |
| | | .filter(keyword -> !STOP_WORDS.contains(keyword)) |
| | | .filter(keyword -> keyword.length() > 0) |
| | | .filter(HospitalTokenizerUtil::isValidKeyword) |
| | | .collect(java.util.stream.Collectors.toCollection(LinkedHashSet::new)); |
| | | |
| | | return String.join(",", keywords); |
| | | } |
| | | |
| | | /** |
| | | * ä½¿ç¨ HanLP 仿æ¬ä¸æåå
³é®è¯ |
| | | * |
| | | * @param text ææ¬ |
| | | * @return å
³é®è¯éå |
| | | */ |
| | | private static Set<String> extractKeywordsByHanLP(String text) { |
| | | Set<String> keywords = new LinkedHashSet<>(); |
| | | |
| | | if (StringUtils.isBlank(text)) { |
| | | return keywords; |
| | | } |
| | | |
| | | try { |
| | | // ä½¿ç¨ HanLP è¿è¡åè¯ |
| | | List<Term> terms = HanLP.segment(text.trim()); |
| | | |
| | | // æ·»å 宿´ææ¬ï¼å¦æä¸å¤ªé¿ï¼ |
| | | if (text.length() <= 20) { |
| | | keywords.add(text.trim()); |
| | | } |
| | | |
| | | // æååè¯ç»æ |
| | | List<String> validWords = new ArrayList<>(); |
| | | for (Term term : terms) { |
| | | String word = term.word; |
| | | |
| | | // è¿æ»¤åå符ï¼é¤éæ¯éè¦ç䏿åç¬¦ï¼ |
| | | if (word.length() == 1 && !isChinese(word.charAt(0))) { |
| | | continue; |
| | | } |
| | | |
| | | // æ·»å ææçåè¯ |
| | | if (isValidKeyword(word)) { |
| | | keywords.add(word); |
| | | validWords.add(word); |
| | | } |
| | | } |
| | | |
| | | // ãå
³é®ä¼åãçæè¿ç»ç»åè¯ |
| | | // ä½è¦è¿æ»¤ææ¬å·å
容ï¼é¿å
çææ æä¹çåé¢ç»åè¯ |
| | | // ä¾å¦ï¼["è¶ç§åº", "ä¸å»", "é¢"] â çæ "è¶ç§åºä¸å»", "ä¸å»é¢", "è¶ç§åºä¸å»é¢" |
| | | |
| | | // ç§»é¤æ¬å·å
容ç¨äºçæç»åè¯ |
| | | String textWithoutBrackets = text |
| | | .replaceAll("ï¼[^ï¼]*ï¼", "") // ç§»é¤ä¸ææ¬å· |
| | | .replaceAll("\\([^\\)]*\\)", "") // ç§»é¤è±ææ¬å· |
| | | .replaceAll("ã[^ã]*ã", "") // ç§»é¤æ¹æ¬å· |
| | | .trim(); |
| | | |
| | | // å¯¹ç§»é¤æ¬å·åçææ¬éæ°åè¯ |
| | | List<Term> cleanTerms = HanLP.segment(textWithoutBrackets); |
| | | List<String> cleanValidWords = new ArrayList<>(); |
| | | for (Term term : cleanTerms) { |
| | | String word = term.word; |
| | | if (word.length() == 1 && !isChinese(word.charAt(0))) { |
| | | continue; |
| | | } |
| | | if (isValidKeyword(word)) { |
| | | cleanValidWords.add(word); |
| | | } |
| | | } |
| | | |
| | | // åºäºå¹²åçåè¯çæç»åè¯ |
| | | for (int len = 2; len <= Math.min(MAX_COMBINED_WORDS, cleanValidWords.size()); len++) { |
| | | for (int i = 0; i <= cleanValidWords.size() - len; i++) { |
| | | StringBuilder combined = new StringBuilder(); |
| | | for (int j = i; j < i + len; j++) { |
| | | combined.append(cleanValidWords.get(j)); |
| | | } |
| | | String combinedWord = combined.toString(); |
| | | |
| | | // åªæ·»å é¿åº¦åççç»åè¯ |
| | | if (combinedWord.length() >= MIN_COMBINED_LEN && combinedWord.length() <= MAX_COMBINED_LEN) { |
| | | keywords.add(combinedWord); |
| | | // é对âè¶ç§åºä¸å»é¢âè¿ç±»æ¨¡å¼ï¼é¢å¤çæå»æâåºâçç®åå
³é®è¯ï¼å¦âè¶ç§ä¸å»é¢â |
| | | String simplified = simplifyDistrictInKeyword(combinedWord); |
| | | if (simplified != null && simplified.length() >= MIN_COMBINED_LEN && simplified.length() <= MAX_COMBINED_LEN) { |
| | | keywords.add(simplified); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | } catch (Exception e) { |
| | | // HanLP åè¯å¤±è´¥æ¶ï¼é级使ç¨ç®ååè¯ |
| | | keywords.addAll(extractKeywordsByNGram(text)); |
| | | } |
| | | |
| | | return keywords; |
| | | } |
| | | |
| | | /** |
| | | * éçº§æ¹æ¡ï¼ä½¿ç¨ç®åç N-Gram åè¯ |
| | | * |
| | | * @param text ææ¬ |
| | | * @return å
³é®è¯éå |
| | | */ |
| | | private static Set<String> extractKeywordsByNGram(String text) { |
| | | Set<String> keywords = new LinkedHashSet<>(); |
| | | |
| | | if (StringUtils.isBlank(text)) { |
| | | return keywords; |
| | | } |
| | | |
| | | text = text.trim(); |
| | | int length = text.length(); |
| | | |
| | | // çæ2-4å符çN-Gram |
| | | for (int n = 2; n <= 4 && n <= length; n++) { |
| | | for (int i = 0; i <= length - n; i++) { |
| | | String ngram = text.substring(i, i + n); |
| | | if (isValidKeyword(ngram)) { |
| | | keywords.add(ngram); |
| | | } |
| | | } |
| | | } |
| | | |
| | | return keywords; |
| | | } |
| | | |
| | | /** |
| | | * 夿å
³é®è¯æ¯å¦ææ |
| | | * |
| | | * @param keyword å
³é®è¯ |
| | | * @return æ¯å¦ææ |
| | | */ |
| | | private static boolean isValidKeyword(String keyword) { |
| | | if (StringUtils.isBlank(keyword)) { |
| | | return false; |
| | | } |
| | | |
| | | // è¿æ»¤çº¯æ°å |
| | | if (keyword.matches("^\\d+$")) { |
| | | return false; |
| | | } |
| | | |
| | | // è¿æ»¤çº¯ç¬¦å· |
| | | if (keyword.matches("^[\\p{P}\\p{S}]+$")) { |
| | | return false; |
| | | } |
| | | |
| | | // è³å°å
å«ä¸ä¸ªä¸ææåæ¯ |
| | | return keyword.matches(".*[\\u4e00-\\u9fa5a-zA-Z].*"); |
| | | } |
| | | |
| | | /** |
| | | * é坹嫿âåºä¸å»é¢/åºä¸å»âçç»åè¯ï¼çæå»æâåºâçç®åå½¢å¼ |
| | | * ä¾å¦ï¼"è¶ç§åºä¸å»é¢" â "è¶ç§ä¸å»é¢"ï¼"è¶ç§åºä¸å»" â "è¶ç§ä¸å»" |
| | | */ |
| | | private static String simplifyDistrictInKeyword(String keyword) { |
| | | if (StringUtils.isBlank(keyword)) { |
| | | return null; |
| | | } |
| | | // éç¨è§åï¼å»æâåºâè¿ä¸ªè¡æ¿å±çº§æ è¯ï¼ä½ä»
éäºâåºä¸å»é¢/åºä¸å»âè¿ç§å»çåºæ¯ |
| | | if (keyword.contains("åºä¸å»é¢")) { |
| | | return keyword.replaceFirst("åºä¸å»é¢", "ä¸å»é¢"); |
| | | } |
| | | if (keyword.contains("åºä¸å»")) { |
| | | return keyword.replaceFirst("åºä¸å»", "ä¸å»"); |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | /** |
| | | * åºäºå»é¢åç§°/ç®ç§°ï¼å¼ºå¶æåå»é¢å
³é®è¯åå
¸ä¸ççè¯ |
| | | */ |
| | | private static void addDictPhrases(String text, Set<String> keywords) { |
| | | if (StringUtils.isBlank(text) || keywords == null) { |
| | | return; |
| | | } |
| | | for (String phrase : HOSPITAL_KEYWORD_DICT) { |
| | | if (text.contains(phrase)) { |
| | | keywords.add(phrase); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /* ç§»é¤å»é¢åç§°ä¸çå°ååç¼ï¼ç/å¸/èªæ²»åºçï¼ |
| | | * éç¨å¤çï¼ä¸ç¡¬ç¼ç å
·ä½å°å |
| | | * |
| | | * @param hospName å»é¢åç§° |
| | | * @return ç§»é¤å°ååç¼åçåç§° |
| | | */ |
| | | private static String removeLocationPrefixes(String hospName) { |
| | | if (StringUtils.isBlank(hospName)) { |
| | | return hospName; |
| | | } |
| | | |
| | | String result = hospName; |
| | | |
| | | // ç§»é¤å¸¸è§çè¡æ¿åºååç¼ |
| | | // ççº§ï¼ XXçãXXå¸ï¼ç´è¾å¸ï¼ãXXèªæ²»åº |
| | | result = result.replaceFirst("^[\\u4e00-\\u9fa5]{2,10}ç", ""); |
| | | result = result.replaceFirst("^[\\u4e00-\\u9fa5]{2,10}èªæ²»åº", ""); |
| | | |
| | | // å°çº§å¸ï¼XXå¸ |
| | | result = result.replaceFirst("^[\\u4e00-\\u9fa5]{2,10}å¸", ""); |
| | | |
| | | // å¿çº§ï¼XXåºãXXå¿ãXXå¸ï¼å¿çº§å¸ï¼ |
| | | result = result.replaceFirst("^[\\u4e00-\\u9fa5]{2,10}åº", ""); |
| | | result = result.replaceFirst("^[\\u4e00-\\u9fa5]{2,10}å¿", ""); |
| | | |
| | | return result.trim(); |
| | | } |
| | | |
| | | /** |
| | | * 计ç®ä¸¤ä¸ªåè¯éåçå¹é
度ï¼ä¼åçï¼ |
| | | * èèå ç´ ï¼ |
| | | * 0. ãæ ¸å¿ã宿´æç´¢ææ¬å¨keywordsä¸åå¨ â é«åï¼+100åï¼ |
| | | * 1. 宿´å¹é
å åï¼å个è¯å¹é
ï¼ |
| | | * 1.5 è¶
级å åï¼å®æ´æç´¢ææ¬å
å«å¨å»é¢åä¸ï¼+80åï¼ï¼æªå¹é
å
容æ¸è¿æ©ç½ |
| | | * 2. è¯è¯æéï¼éè¦è¯æ±å åï¼ |
| | | * 3. è¿ç»å¹é
å å |
| | | * 4. å符ç¸ä¼¼åº¦ |
| | | * 5. è´åå¹é
æ©ç½ï¼å»é¢åä¸åºç°æç´¢è¯ä¹å¤çå°åºå -30åï¼ |
| | | * 6. åé¢è½»å¾®éæï¼-10åï¼ |
| | | * 7. æ¬å·å
容轻微æ©ç½ï¼-5åï¼ |
| | | * |
| | | * @param searchKeywords æç´¢åè¯ï¼éå·åéï¼ |
| | | * @param hospitalKeywords å»é¢åè¯ï¼éå·åéï¼ |
| | | * @param hospName å»é¢åç§°ï¼ç¨äºå®æ´å¹é
å¤æï¼ |
| | | * @param districtNames å°åºåç§°éåï¼ç¨äºè´åå¹é
æ£æ¥ï¼å¯ä¸ºnullï¼ |
| | | * @return å¹é
åæ° |
| | | */ |
| | | public static int calculateMatchScore(String searchKeywords, String hospitalKeywords, String hospName, Set<String> districtNames) { |
| | | if (StringUtils.isBlank(searchKeywords) || StringUtils.isBlank(hospitalKeywords)) { |
| | | return 0; |
| | | } |
| | | |
| | | List<String> searchWords = Arrays.asList(searchKeywords.split(",")); |
| | | List<String> hospWords = Arrays.asList(hospitalKeywords.split(",")); |
| | | Set<String> searchWordsSet = new HashSet<>(searchWords); |
| | | Set<String> hospWordsSet = new HashSet<>(hospWords); |
| | | |
| | | int totalScore = 0; |
| | | |
| | | // 0. ãæ ¸å¿ä¼åãé¦å
夿æ¯å¦åå¨â宿´å¹é
â |
| | | // 约å®ï¼searchKeywords ç第ä¸ä¸ªåè¯ä¸ºåå§æç´¢ææ¬ |
| | | String fullSearchText = searchWords.get(0); |
| | | boolean keywordFullMatch = hospWordsSet.contains(fullSearchText); |
| | | boolean nameFullMatch = (hospName != null && hospName.contains(fullSearchText)); |
| | | |
| | | if (keywordFullMatch || nameFullMatch) { |
| | | // 宿´å¹é
ä¼å
ï¼ç´æ¥ç»åºå®æé«åï¼ç¡®ä¿æå¨æåé¢ |
| | | totalScore = 1000; // æååºç¡å为1000ï¼ä½ä¸ºåæ°å¤©è±æ¿ |
| | | |
| | | // 坹宿´å¹é
ç»æï¼ä»ç¶å¯ä»¥åºç¨å°åºæ©ç½ååé¢/æ¬å·è½»å¾®éæï¼ä¿è¯è¯ä¹æ£ç¡® |
| | | if (districtNames != null && !districtNames.isEmpty()) { |
| | | totalScore -= calculateNegativeMatchPenalty(searchWordsSet, districtNames, hospName); |
| | | } |
| | | |
| | | // if (isBranchHospital(hospName)) { |
| | | // totalScore -= 10; // å颿£10å |
| | | // } |
| | | |
| | | // if (hospName != null && (hospName.contains("ï¼") || hospName.contains("(") || hospName.contains("ã"))) { |
| | | // totalScore -= 5; // æ¬å·å
容轻微æ£å |
| | | // } |
| | | |
| | | return Math.max(0, totalScore); |
| | | } |
| | | |
| | | // 1. 宿´å¹é
å åï¼å个è¯å¹é
ï¼ |
| | | for (String searchWord : searchWords) { |
| | | if (searchWord.length() >= 4 && hospName != null && hospName.contains(searchWord)) { |
| | | totalScore += 50; // 宿´è¯å¹é
å å |
| | | } |
| | | } |
| | | |
| | | // 1.5 è¶
级å åï¼æç´¢ææ¬ä¸å»é¢åç宿´ç¸ä¼¼åº¦ |
| | | if (hospName != null) { |
| | | // å®å
¨å
å«å å |
| | | if (hospName.contains(fullSearchText)) { |
| | | totalScore += 500; // æåå
å«å
³ç³»çåæ°ï¼ç¡®ä¿å
å«æç´¢å
¨ç§°çç»ææåé å |
| | | } else { |
| | | // è®¡ç®æ´ä½ç¸ä¼¼åº¦ |
| | | int similarity = calculateStringSimilarity(fullSearchText, hospName); |
| | | if (similarity > 80) { |
| | | totalScore += similarity / 2; // é«åº¦ç¸ä¼¼ä¹å åï¼ä½æééä½ |
| | | } |
| | | } |
| | | |
| | | // æªå¹é
å
容æ¸è¿æ©ç½ï¼å»é¢å䏿æç´¢è¯ä¹å¤çå
容 |
| | | String cleanedHospName = removeLocationPrefixes(hospName); |
| | | int unmatchedLength = cleanedHospName.length() - fullSearchText.length(); |
| | | if (unmatchedLength > 0) { |
| | | // æ¸è¿æ©ç½ï¼1-5忣1å/åï¼6-10忣2å/åï¼11+忣3å/å |
| | | if (unmatchedLength <= 5) { |
| | | totalScore -= unmatchedLength * 1; |
| | | } else if (unmatchedLength <= 10) { |
| | | totalScore -= 5 + (unmatchedLength - 5) * 2; |
| | | } else { |
| | | totalScore -= 5 + 10 + (unmatchedLength - 10) * 3; |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 2. åè¯å¹é
计åï¼ä¼å
å¹é
è¾é¿çæç´¢è¯ï¼å½ä¸å³æ¢ï¼ |
| | | List<String> sortedSearchWords = new ArrayList<>(searchWords); |
| | | sortedSearchWords.sort((a, b) -> Integer.compare(b.length(), a.length())); // æé¿åº¦ä»é¿å°ç |
| | | boolean anyMatch = false; |
| | | |
| | | for (String searchWord : sortedSearchWords) { |
| | | boolean isLong = searchWord.length() >= 4; |
| | | if (hospWords.contains(searchWord)) { |
| | | int wordScore; |
| | | if (isLong) { |
| | | // é¿è¯å®æ´å¹é
ï¼é«å |
| | | wordScore = 40 + searchWord.length() * 4; |
| | | } else { |
| | | // çè¯å®æ´å¹é
ï¼ä½å |
| | | wordScore = 10 + searchWord.length() * 2; |
| | | } |
| | | |
| | | // 髿éè¯é¢å¤å å |
| | | if (HIGH_WEIGHT_WORDS.contains(searchWord)) { |
| | | wordScore += 15; |
| | | } |
| | | |
| | | totalScore += wordScore; |
| | | anyMatch = true; |
| | | |
| | | // ãæ ¸å¿ä¿®æ¹ãåªè¦å¹é
å°ä¸ä¸ªåè¯ï¼æ 论é¿çï¼ï¼å°±ä¸æåç»å¹é
ï¼éµå¾ªé¿è¯ä¼å
åå |
| | | break; |
| | | } else { |
| | | // 2.3 é¨åå¹é
ï¼å
å«å
³ç³»ï¼ï¼åªå¯¹è¾é¿æç´¢è¯èè |
| | | if (isLong) { |
| | | for (String hospWord : hospWords) { |
| | | if (hospWord.contains(searchWord) || searchWord.contains(hospWord)) { |
| | | int partialScore = Math.min(searchWord.length(), hospWord.length()) * 2; |
| | | totalScore += partialScore; |
| | | anyMatch = true; |
| | | break; |
| | | } |
| | | } |
| | | if (anyMatch) { |
| | | break; // å½ä¸å³æ¢ |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | // å¦æå·²ç»æå¹é
ï¼ååºç¨è´åæ©ç½ãåé¢/æ¬å·è°æ´å¹¶è¿å |
| | | if (anyMatch) { |
| | | if (districtNames != null && !districtNames.isEmpty()) { |
| | | totalScore -= calculateNegativeMatchPenalty(searchWordsSet, districtNames, hospName); |
| | | } |
| | | if (isBranchHospital(hospName)) { |
| | | totalScore -= 10; |
| | | } |
| | | if (hospName != null && (hospName.contains("ï¼") || hospName.contains("(") || hospName.contains("ã"))) { |
| | | totalScore -= 5; |
| | | } |
| | | return Math.max(0, totalScore); |
| | | } |
| | | |
| | | // 3. è¿ç»å¹é
å å |
| | | totalScore += calculateContinuousMatchBonus(searchWords, hospWords); |
| | | |
| | | // 4. å符ç¸ä¼¼åº¦å åï¼å¯¹äºé¿è¯ï¼ |
| | | for (String searchWord : searchWords) { |
| | | if (searchWord.length() >= 4) { |
| | | for (String hospWord : hospWords) { |
| | | if (hospWord.length() >= 4) { |
| | | int similarity = calculateStringSimilarity(searchWord, hospWord); |
| | | if (similarity > 70) { // ç¸ä¼¼åº¦è¶
è¿70% |
| | | totalScore += similarity / 10; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 5. è´åå¹é
æ©ç½ï¼å»é¢åä¸å
å«æç´¢è¯ä¹å¤ç髿éå°åºå |
| | | if (districtNames != null && !districtNames.isEmpty()) { |
| | | totalScore -= calculateNegativeMatchPenalty(searchWordsSet, districtNames, hospName); |
| | | } |
| | | |
| | | // 6. åé¢è½»å¾®éæï¼ä¸»é¢ä¼å
ï¼ä½ä¸è¦è¿åº¦æ©ç½ |
| | | if (isBranchHospital(hospName)) { |
| | | totalScore -= 10; // å颿£10åï¼æ¹ä¸ºåºå®æ£åï¼èéææï¼ |
| | | } |
| | | |
| | | // 7. æ¬å·å
容轻微æ©ç½ï¼æ¬å·å
éå¸¸æ¯æ¬¡è¦ä¿¡æ¯ |
| | | if (hospName != null && (hospName.contains("ï¼") || hospName.contains("(") || hospName.contains("ã"))) { |
| | | totalScore -= 5; // å
嫿¬å·æ£5åï¼æ¹ä¸ºåºå®æ£åï¼ |
| | | } |
| | | |
| | | return Math.max(0, totalScore); // ç¡®ä¿åæ°ä¸ä¸ºè´ |
| | | } |
| | | |
| | | /** |
| | | * 计ç®è´åå¹é
æ©ç½ |
| | | * 妿å»é¢åä¸å
å«æç´¢è¯ä¹å¤çå°åºåç§°ï¼åºè¯¥é使å |
| | | * |
| | | * @param searchWords æç´¢è¯éå |
| | | * @param districtNames ææå»é¢çå°åºåç§°éåï¼ä»å»é¢è¡¨ç hopsArea åæ®µæåï¼ |
| | | * @param hospName å½åå»é¢åç§° |
| | | * @return æ©ç½åæ° |
| | | */ |
| | | private static int calculateNegativeMatchPenalty(Set<String> searchWords, Set<String> districtNames, String hospName) { |
| | | if (hospName == null || districtNames == null || districtNames.isEmpty()) { |
| | | return 0; |
| | | } |
| | | |
| | | int penalty = 0; |
| | | |
| | | // æ£æ¥å»é¢åä¸çå°åºå |
| | | for (String district : districtNames) { |
| | | if (StringUtils.isBlank(district)) { |
| | | continue; |
| | | } |
| | | |
| | | // 妿å»é¢åå
å«è¯¥å°åºå |
| | | if (hospName.contains(district)) { |
| | | // æ£æ¥æ¯å¦å¨æç´¢è¯ä¸åºç° |
| | | boolean inSearchWords = false; |
| | | |
| | | // 1. ç´æ¥å¹é
ï¼æç´¢è¯éåä¸å
å«è¯¥å°åºå |
| | | if (searchWords.contains(district)) { |
| | | inSearchWords = true; |
| | | } else { |
| | | // 2. é¨åå¹é
ï¼æç´¢è¯çä»»ä½ä¸ä¸ªè¯å
å«è¯¥å°åºå |
| | | for (String searchWord : searchWords) { |
| | | if (searchWord.contains(district)) { |
| | | inSearchWords = true; |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 妿å»é¢åå
å«è¯¥å°åºåï¼ä½æç´¢è¯ä¸æ²¡æï¼åæ£å |
| | | if (!inSearchWords) { |
| | | penalty += 30; // å
å«ä¸ç¸å
³å°åºåï¼æ£30å |
| | | } |
| | | } |
| | | } |
| | | |
| | | return penalty; |
| | | } |
| | | |
| | | /** |
| | | * 夿æ¯å¦ä¸ºåé¢ |
| | | */ |
| | | private static boolean isBranchHospital(String hospName) { |
| | | if (hospName == null) { |
| | | return false; |
| | | } |
| | | |
| | | // åé¢ç¹å¾å
³é®è¯ |
| | | String[] branchKeywords = { |
| | | "åé¢", "åé¨", "é¨è¯é¨", "社åºå«ç", "å«çç«", "å«çæå¡ä¸å¿", |
| | | "ä¸é¢", "西é¢", "åé¢", "åé¢", "æ°é¢", "èé¢" |
| | | }; |
| | | |
| | | for (String keyword : branchKeywords) { |
| | | if (hospName.contains(keyword)) { |
| | | return true; |
| | | } |
| | | } |
| | | |
| | | // å
å«å
·ä½è·¯å/è¡éåä¹å¯è½æ¯åé¢ |
| | | String[] roadKeywords = { |
| | | "è·¯åé¢", "è¡åé¢", "éåé¢", "大éåé¢" |
| | | }; |
| | | |
| | | for (String keyword : roadKeywords) { |
| | | if (hospName.contains(keyword)) { |
| | | return true; |
| | | } |
| | | } |
| | | |
| | | return false; |
| | | } |
| | | |
| | | /** |
| | | * 计ç®è¿ç»å¹é
å å |
| | | */ |
| | | private static int calculateContinuousMatchBonus(List<String> searchWords, List<String> hospWords) { |
| | | int bonus = 0; |
| | | int consecutiveCount = 0; |
| | | |
| | | for (int i = 0; i < searchWords.size() - 1; i++) { |
| | | String word1 = searchWords.get(i); |
| | | String word2 = searchWords.get(i + 1); |
| | | |
| | | // 夿æ¯å¦å¨å»é¢åè¯ä¸è¿ç»åºç° |
| | | boolean found = false; |
| | | for (int j = 0; j < hospWords.size() - 1; j++) { |
| | | if (hospWords.get(j).equals(word1) && hospWords.get(j + 1).equals(word2)) { |
| | | consecutiveCount++; |
| | | found = true; |
| | | break; |
| | | } |
| | | } |
| | | |
| | | if (found) { |
| | | bonus += consecutiveCount * 5; // è¿ç»è¶é¿å åè¶å¤ |
| | | } else { |
| | | consecutiveCount = 0; |
| | | } |
| | | } |
| | | |
| | | return bonus; |
| | | } |
| | | |
| | | /** |
| | | * 计ç®å符串ç¸ä¼¼åº¦ï¼ä½¿ç¨Levenshteinè·ç¦»ï¼ |
| | | * |
| | | * @param s1 å符串1 |
| | | * @param s2 å符串2 |
| | | * @return ç¸ä¼¼åº¦ç¾åæ¯ (0-100) |
| | | */ |
| | | private static int calculateStringSimilarity(String s1, String s2) { |
| | | if (s1.equals(s2)) { |
| | | return 100; |
| | | } |
| | | |
| | | int maxLen = Math.max(s1.length(), s2.length()); |
| | | if (maxLen == 0) { |
| | | return 100; |
| | | } |
| | | |
| | | int distance = levenshteinDistance(s1, s2); |
| | | return (int) ((1 - (double) distance / maxLen) * 100); |
| | | } |
| | | |
| | | /** |
| | | * 计ç®Levenshteinè·ç¦»ï¼ç¼è¾è·ç¦»ï¼ |
| | | */ |
| | | private static int levenshteinDistance(String s1, String s2) { |
| | | int len1 = s1.length(); |
| | | int len2 = s2.length(); |
| | | |
| | | int[][] dp = new int[len1 + 1][len2 + 1]; |
| | | |
| | | for (int i = 0; i <= len1; i++) { |
| | | dp[i][0] = i; |
| | | } |
| | | |
| | | for (int j = 0; j <= len2; j++) { |
| | | dp[0][j] = j; |
| | | } |
| | | |
| | | for (int i = 1; i <= len1; i++) { |
| | | for (int j = 1; j <= len2; j++) { |
| | | int cost = s1.charAt(i - 1) == s2.charAt(j - 1) ? 0 : 1; |
| | | dp[i][j] = Math.min( |
| | | Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1), |
| | | dp[i - 1][j - 1] + cost |
| | | ); |
| | | } |
| | | } |
| | | |
| | | return dp[len1][len2]; |
| | | } |
| | | |
| | | /** |
| | | * å¯¹ææ¬è¿è¡åè¯ï¼åç«¯ä¼ å
¥çæç´¢å
³é®è¯ï¼ |
| | | * |
| | | * @param text æç´¢ææ¬ |
| | | * @return åè¯ç»æï¼éå·åéï¼ |
| | | */ |
| | | public static String tokenizeSearchText(String text) { |
| | | if (StringUtils.isBlank(text)) { |
| | | return ""; |
| | | } |
| | | |
| | | Set<String> keywords = extractKeywordsByHanLP(text.trim()); |
| | | |
| | | // è¿æ»¤åç¨è¯ |
| | | keywords = keywords.stream() |
| | | .filter(keyword -> !STOP_WORDS.contains(keyword)) |
| | | .filter(keyword -> keyword.length() > 0) |
| | | .collect(java.util.stream.Collectors.toCollection(LinkedHashSet::new)); |
| | | |
| | | return String.join(",", keywords); |
| | | } |
| | | } |
| | |
| | | import com.ruoyi.system.service.ITaskStatusPushService; |
| | | |
| | | /** |
| | | * æ§ç³»ç»åæ¥å®æ¶ä»»å¡ |
| | | * æ§ç³»ç»åæ¥å®æ¶ä»»å¡ æ°ç³»ç»ä¸çä»»å¡åæ¥å°æ§ç³»ç»ä¸ |
| | | * |
| | | * @author ruoyi |
| | | * @date 2024-01-20 |
| | |
| | | |
| | | /** |
| | | * æ§ç³»ç»è½¬è¿å忥宿¶ä»»å¡ |
| | | * |
| | | * (æ§ç³»ç»è¿ç§»å°æ°ç³»ç») |
| | | * @author ruoyi |
| | | * @date 2025-11-19 |
| | | */ |
| New file |
| | |
| | | package com.ruoyi.quartz.task; |
| | | |
| | | import com.ruoyi.common.core.domain.entity.SysDept; |
| | | import com.ruoyi.common.utils.DateUtils; |
| | | import com.ruoyi.common.utils.StringUtils; |
| | | import com.ruoyi.system.domain.*; |
| | | import com.ruoyi.system.mapper.*; |
| | | import com.ruoyi.system.service.*; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.*; |
| | | import java.util.stream.Collectors; |
| | | |
| | | /** |
| | | * 车è¾å¼å¸¸è¿è¡çæ§å®æ¶ä»»å¡ |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | @Component("vehicleAbnormalAlertTask") |
| | | public class VehicleAbnormalAlertTask { |
| | | |
| | | private static final Logger log = LoggerFactory.getLogger(VehicleAbnormalAlertTask.class); |
| | | |
| | | @Autowired |
| | | private ISysConfigService configService; |
| | | |
| | | @Autowired |
| | | private VehicleGpsSegmentMileageMapper segmentMileageMapper; |
| | | |
| | | @Autowired |
| | | private VehicleInfoMapper vehicleInfoMapper; |
| | | |
| | | @Autowired |
| | | private SysTaskMapper sysTaskMapper; |
| | | |
| | | @Autowired |
| | | private VehicleAbnormalAlertMapper alertMapper; |
| | | |
| | | @Autowired |
| | | private IQyWechatService qyWechatService; |
| | | |
| | | @Autowired |
| | | private ISysDeptService deptService; |
| | | |
| | | @Autowired |
| | | private IVehicleAbnormalAlertService alertService; |
| | | |
| | | @Autowired |
| | | private IVehicleAlertConfigService alertConfigService; |
| | | |
| | | @Autowired |
| | | private ISysUserService userService; |
| | | |
| | | /** |
| | | * çæ§è½¦è¾å¼å¸¸è¿è¡æ
åµ |
| | | */ |
| | | public void monitorVehicleAbnormalRunning() { |
| | | try { |
| | | // æ£æ¥åè½å¼å
³ |
| | | if (!isAlertEnabled()) { |
| | | log.debug("车è¾å¼å¸¸åè¦åè½æªå¯ç¨ï¼è·³è¿çæ§"); |
| | | return; |
| | | } |
| | | |
| | | log.info("å¼å§æ§è¡è½¦è¾å¼å¸¸è¿è¡çæ§ä»»å¡"); |
| | | |
| | | // å è½½é
ç½®åæ° |
| | | AlertConfig config = loadAlertConfig(); |
| | | |
| | | // è·åçæ§æ¶é´çªå£ |
| | | Date endTime = new Date(); |
| | | Calendar cal = Calendar.getInstance(); |
| | | cal.setTime(endTime); |
| | | cal.add(Calendar.MINUTE, -config.timeWindow); |
| | | Date startTime = cal.getTime(); |
| | | |
| | | // æ¥è¯¢æææ´»è·è½¦è¾ |
| | | List<VehicleInfo> vehicles = vehicleInfoMapper.selectVehicleInfoList(new VehicleInfo()); |
| | | if (vehicles == null || vehicles.isEmpty()) { |
| | | log.debug("æ²¡ææ¾å°éè¦çæ§ç车è¾"); |
| | | return; |
| | | } |
| | | |
| | | log.info("å¼å§çæ§ {} è¾è½¦è¾ï¼æ¶é´çªå£: {} å° {}", vehicles.size(), |
| | | DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, startTime), |
| | | DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, endTime)); |
| | | |
| | | int alertCount = 0; |
| | | for (VehicleInfo vehicle : vehicles) { |
| | | try { |
| | | if (checkVehicleAbnormalRunning(vehicle, startTime, endTime, config)) { |
| | | alertCount++; |
| | | } |
| | | } catch (Exception e) { |
| | | log.error("æ£æ¥è½¦è¾ {} å¼å¸¸è¿è¡å¤±è´¥", vehicle.getVehicleNo(), e); |
| | | } |
| | | } |
| | | |
| | | log.info("车è¾å¼å¸¸è¿è¡çæ§ä»»å¡å®æï¼å
±äº§ç {} 个åè¦", alertCount); |
| | | |
| | | } catch (Exception e) { |
| | | log.error("车è¾å¼å¸¸è¿è¡çæ§ä»»å¡æ§è¡å¤±è´¥", e); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * æ£æ¥åä¸ªè½¦è¾æ¯å¦å¼å¸¸è¿è¡ |
| | | */ |
| | | private boolean checkVehicleAbnormalRunning(VehicleInfo vehicle, Date startTime, Date endTime, AlertConfig globalConfig) { |
| | | Long vehicleId = vehicle.getVehicleId(); |
| | | String vehicleNo = vehicle.getVehicleNo(); |
| | | Long deptId = vehicle.getDeptId(); |
| | | |
| | | // è·å该车è¾çé
ç½®ï¼ä¼å
级ï¼è½¦è¾ > é¨é¨ > å
¨å±ï¼ |
| | | AlertConfig config = getVehicleAlertConfig(vehicleId, deptId, globalConfig); |
| | | if (config == null) { |
| | | log.debug("è½¦è¾ {} æªæ¾å°ææé
ç½®ï¼è·³è¿çæ§", vehicleNo); |
| | | return false; |
| | | } |
| | | |
| | | // 1. æ¥è¯¢è½¦è¾å¨æ¶é´çªå£å
çæ»è¿è¡éç¨ |
| | | BigDecimal totalMileage = calculateVehicleMileage(vehicleId, startTime, endTime); |
| | | if (totalMileage == null || totalMileage.compareTo(BigDecimal.ZERO) == 0) { |
| | | log.debug("è½¦è¾ {} å¨çæ§çªå£å
æ è¿è¡éç¨", vehicleNo); |
| | | return false; |
| | | } |
| | | |
| | | // 2. æ¥è¯¢è½¦è¾å¨æ¶é´çªå£å
æä»»å¡æ¶çéç¨ |
| | | BigDecimal taskMileage = calculateTaskMileage(vehicleId, startTime, endTime); |
| | | if (taskMileage == null) { |
| | | taskMileage = BigDecimal.ZERO; |
| | | } |
| | | |
| | | // 3. 计ç®éä»»å¡ç¶æä¸çè¿è¡éç¨ |
| | | BigDecimal nonTaskMileage = totalMileage.subtract(taskMileage); |
| | | if (nonTaskMileage.compareTo(BigDecimal.ZERO) <= 0) { |
| | | log.debug("è½¦è¾ {} å¨çæ§çªå£å
æ éä»»å¡éç¨", vehicleNo); |
| | | return false; |
| | | } |
| | | |
| | | log.debug("è½¦è¾ {} æ»éç¨: {}km, ä»»å¡éç¨: {}km, éä»»å¡éç¨: {}km", |
| | | vehicleNo, totalMileage, taskMileage, nonTaskMileage); |
| | | |
| | | // 4. æ£æ¥éä»»å¡éç¨æ¯å¦è¶
è¿å
¬éæ°éå¼ |
| | | if (nonTaskMileage.compareTo(config.mileageThreshold) <= 0) { |
| | | log.debug("è½¦è¾ {} éä»»å¡è¿è¡éç¨ {}km æªè¶
è¿éå¼ {}km", |
| | | vehicleNo, nonTaskMileage, config.mileageThreshold); |
| | | return false; |
| | | } |
| | | |
| | | // 5. æ£æ¥åè¦é¢çéå¶ |
| | | if (!checkAlertFrequency(vehicleId, config)) { |
| | | log.debug("è½¦è¾ {} 已达å°åè¦é¢çéå¶", vehicleNo); |
| | | return false; |
| | | } |
| | | |
| | | // 6. å建åè¦è®°å½ |
| | | return createAlertAndNotify(vehicle, nonTaskMileage, startTime, endTime, config); |
| | | } |
| | | |
| | | /** |
| | | * 计ç®è½¦è¾è¿è¡éç¨ |
| | | */ |
| | | private BigDecimal calculateVehicleMileage(Long vehicleId, Date startTime, Date endTime) { |
| | | try { |
| | | // æ¥è¯¢è½¦è¾å¨æ¶é´çªå£å
çåæ®µéç¨è®°å½ |
| | | VehicleGpsSegmentMileage query = new VehicleGpsSegmentMileage(); |
| | | query.setVehicleId(vehicleId); |
| | | |
| | | Map<String, Object> params = new HashMap<>(); |
| | | params.put("vehicleId", vehicleId); |
| | | params.put("startTime", startTime); |
| | | params.put("endTime", endTime); |
| | | |
| | | List<VehicleGpsSegmentMileage> segments = segmentMileageMapper.selectSegmentsByTimeRange(params); |
| | | |
| | | if (segments == null || segments.isEmpty()) { |
| | | return BigDecimal.ZERO; |
| | | } |
| | | |
| | | // ç´¯å ææå段éç¨ |
| | | BigDecimal totalMileage = segments.stream() |
| | | .map(VehicleGpsSegmentMileage::getSegmentDistance) |
| | | .filter(Objects::nonNull) |
| | | .reduce(BigDecimal.ZERO, BigDecimal::add); |
| | | |
| | | return totalMileage; |
| | | |
| | | } catch (Exception e) { |
| | | log.error("计ç®è½¦è¾éç¨å¤±è´¥ï¼vehicleId={}", vehicleId, e); |
| | | return BigDecimal.ZERO; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 计ç®è½¦è¾å¨æä»»å¡æ¶çè¿è¡éç¨ |
| | | */ |
| | | private BigDecimal calculateTaskMileage(Long vehicleId, Date startTime, Date endTime) { |
| | | try { |
| | | // 1. æ¥è¯¢è½¦è¾å¨æ¶é´çªå£å
çä»»å¡ |
| | | Map<String, Object> taskParams = new HashMap<>(); |
| | | taskParams.put("vehicleId", vehicleId); |
| | | taskParams.put("startTime", startTime); |
| | | taskParams.put("endTime", endTime); |
| | | |
| | | List<SysTask> tasks = sysTaskMapper.selectVehicleTasksInTimeRange(taskParams); |
| | | |
| | | if (tasks == null || tasks.isEmpty()) { |
| | | return BigDecimal.ZERO; |
| | | } |
| | | |
| | | // æé¤å·²åæ¶çä»»å¡ |
| | | List<SysTask> activeTasks = tasks.stream() |
| | | .filter(t -> !"CANCELLED".equals(t.getTaskStatus())) |
| | | .collect(Collectors.toList()); |
| | | |
| | | if (activeTasks.isEmpty()) { |
| | | return BigDecimal.ZERO; |
| | | } |
| | | |
| | | // 2. æ¥è¯¢è½¦è¾çææGPSåæ®µéç¨ |
| | | Map<String, Object> segmentParams = new HashMap<>(); |
| | | segmentParams.put("vehicleId", vehicleId); |
| | | segmentParams.put("startTime", startTime); |
| | | segmentParams.put("endTime", endTime); |
| | | |
| | | List<VehicleGpsSegmentMileage> segments = segmentMileageMapper.selectSegmentsByTimeRange(segmentParams); |
| | | |
| | | if (segments == null || segments.isEmpty()) { |
| | | return BigDecimal.ZERO; |
| | | } |
| | | |
| | | // 3. çéåºå¨ä»»å¡æ¶é´èå´å
çåæ®µéç¨ |
| | | BigDecimal taskMileage = BigDecimal.ZERO; |
| | | |
| | | for (VehicleGpsSegmentMileage segment : segments) { |
| | | Date segmentStart = segment.getSegmentStartTime(); |
| | | Date segmentEnd = segment.getSegmentEndTime(); |
| | | |
| | | if (segmentStart == null || segmentEnd == null) { |
| | | continue; |
| | | } |
| | | |
| | | // æ£æ¥è¯¥å段æ¯å¦å¨ä»»æä»»å¡çæ¶é´èå´å
|
| | | for (SysTask task : activeTasks) { |
| | | Date taskStart = task.getPlannedStartTime(); |
| | | Date taskEnd = task.getActualEndTime(); |
| | | |
| | | // 妿任å¡è¿æ²¡å®æï¼ä½¿ç¨å½åæ¶é´ä½ä¸ºç»ææ¶é´ |
| | | if (taskEnd == null) { |
| | | taskEnd = endTime; |
| | | } |
| | | |
| | | if (taskStart == null) { |
| | | continue; |
| | | } |
| | | |
| | | // 夿忮µæ¶é´æ¯å¦ä¸ä»»å¡æ¶é´æéå |
| | | if (isTimeOverlap(segmentStart, segmentEnd, taskStart, taskEnd)) { |
| | | if (segment.getSegmentDistance() != null) { |
| | | taskMileage = taskMileage.add(segment.getSegmentDistance()); |
| | | } |
| | | break; // è¯¥åæ®µå·²è®¡å
¥ä»»å¡éç¨ï¼ä¸éå¤è®¡ç® |
| | | } |
| | | } |
| | | } |
| | | |
| | | return taskMileage; |
| | | |
| | | } catch (Exception e) { |
| | | log.error("计ç®ä»»å¡éç¨å¤±è´¥ï¼vehicleId={}", vehicleId, e); |
| | | return BigDecimal.ZERO; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * å¤æä¸¤ä¸ªæ¶é´æ®µæ¯å¦æéå |
| | | */ |
| | | private boolean isTimeOverlap(Date start1, Date end1, Date start2, Date end2) { |
| | | // æ¶é´æ®µ1: [start1, end1] |
| | | // æ¶é´æ®µ2: [start2, end2] |
| | | // æéå çæ¡ä»¶ï¼start1 < end2 && start2 < end1 |
| | | return start1.before(end2) && start2.before(end1); |
| | | } |
| | | |
| | | /** |
| | | * æ£æ¥åè¦é¢çéå¶ |
| | | */ |
| | | private boolean checkAlertFrequency(Long vehicleId, AlertConfig config) { |
| | | try { |
| | | // 1. æ£æ¥å½æ¥åè¦æ¬¡æ° |
| | | Date today = DateUtils.parseDate(DateUtils.getDate()); |
| | | int dailyCount = alertMapper.selectDailyAlertCount(vehicleId, today); |
| | | if (dailyCount >= config.dailyLimit) { |
| | | log.debug("è½¦è¾ {} 仿¥åè¦æ¬¡æ° {} 已达ä¸é {}", vehicleId, dailyCount, config.dailyLimit); |
| | | return false; |
| | | } |
| | | |
| | | // 2. æ£æ¥åè¦é´é |
| | | Date lastAlertTime = alertMapper.selectLastAlertTime(vehicleId); |
| | | if (lastAlertTime != null) { |
| | | long minutesDiff = (new Date().getTime() - lastAlertTime.getTime()) / (1000 * 60); |
| | | if (minutesDiff < config.alertInterval) { |
| | | log.debug("è½¦è¾ {} è·ç¦»ä¸æ¬¡åè¦ä»
{} åéï¼æªè¾¾å°é´é {} åé", |
| | | vehicleId, minutesDiff, config.alertInterval); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | return true; |
| | | |
| | | } catch (Exception e) { |
| | | log.error("æ£æ¥åè¦é¢ç失败ï¼vehicleId={}", vehicleId, e); |
| | | // åºéæ¶è°¨æ
å¤çï¼å
许åè¦ |
| | | return true; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * å建åè¦å¹¶åééç¥ |
| | | */ |
| | | private boolean createAlertAndNotify(VehicleInfo vehicle, BigDecimal mileage, |
| | | Date startTime, Date endTime, AlertConfig config) { |
| | | try { |
| | | Long vehicleId = vehicle.getVehicleId(); |
| | | String vehicleNo = vehicle.getVehicleNo(); |
| | | |
| | | // è·å车è¾å½å±é¨é¨ä¿¡æ¯ |
| | | Long deptId = vehicle.getDeptId(); |
| | | String deptName = vehicle.getDeptName(); |
| | | |
| | | // å建åè¦è®°å½ |
| | | boolean created = alertService.checkAndCreateAlert( |
| | | vehicleId, vehicleNo, mileage, startTime, endTime, deptId, deptName); |
| | | |
| | | if (!created) { |
| | | return false; |
| | | } |
| | | |
| | | log.info("è½¦è¾ {} 产çå¼å¸¸åè¦ï¼æ ä»»å¡è¿è¡ {}kmï¼æ¶é´ {} è³ {}", |
| | | vehicleNo, mileage, |
| | | DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, startTime), |
| | | DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, endTime)); |
| | | |
| | | // åééç¥ |
| | | sendAlertNotification(vehicle, mileage, deptId, config); |
| | | |
| | | return true; |
| | | |
| | | } catch (Exception e) { |
| | | log.error("å建åè¦å¤±è´¥ï¼vehicleNo={}", vehicle.getVehicleNo(), e); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * åéåè¦éç¥ |
| | | */ |
| | | private void sendAlertNotification(VehicleInfo vehicle, BigDecimal mileage, |
| | | Long deptId, AlertConfig config) { |
| | | try { |
| | | // è·åéç¥ç¨æ·å表 |
| | | List<Long> notifyUserIds = getNotifyUsers(deptId, config); |
| | | if (notifyUserIds.isEmpty()) { |
| | | log.warn("è½¦è¾ {} åè¦æ éç¥ç¨æ·ï¼è·³è¿åé", vehicle.getVehicleNo()); |
| | | return; |
| | | } |
| | | |
| | | // æé éç¥å
容 |
| | | String title = "车è¾å¼å¸¸è¿è¡åè¦"; |
| | | String content = String.format("è½¦è¾ %s 卿 ä»»å¡ç¶æä¸è¿è¡äº %.2f å
¬éï¼è¯·åæ¶å½å
¥ä»»å¡åï¼ç¹å»å»å½åã", |
| | | vehicle.getVehicleNo(), mileage); |
| | | |
| | | // éè¿ä¼ä¸å¾®ä¿¡åééç¥ |
| | | if (qyWechatService != null && qyWechatService.isEnabled()) { |
| | | for (Long userId : notifyUserIds) { |
| | | try { |
| | | // è¿éå¯ä»¥æ ¹æ®å®é
éæ±æå®è·³è½¬é¾æ¥ |
| | | String notifyUrl = "/pagesTask/create-emergency"; // å®é
龿¥éæ ¹æ®ä¸å¡è°æ´ |
| | | qyWechatService.sendNotifyMessageWithDefaultAppId(userId, title, content, notifyUrl); |
| | | log.info("å·²åç¨æ· {} åéè½¦è¾ {} å¼å¸¸åè¦éç¥", userId, vehicle.getVehicleNo()); |
| | | } catch (Exception e) { |
| | | log.error("åç¨æ· {} åéåè¦éç¥å¤±è´¥", userId, e); |
| | | } |
| | | } |
| | | } else { |
| | | log.warn("ä¼ä¸å¾®ä¿¡æå¡æªå¯ç¨ï¼æ æ³åéåè¦éç¥"); |
| | | } |
| | | |
| | | } catch (Exception e) { |
| | | log.error("åéåè¦éç¥å¤±è´¥ï¼vehicleNo={}", vehicle.getVehicleNo(), e); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * è·åéç¥ç¨æ·å表 |
| | | */ |
| | | private List<Long> getNotifyUsers(Long deptId, AlertConfig config) { |
| | | List<Long> userIds = new ArrayList<>(); |
| | | |
| | | try { |
| | | // 1. ä¼å
使ç¨é
ç½®çç¨æ·å表 |
| | | if (StringUtils.isNotEmpty(config.notifyUsers)) { |
| | | String[] userIdStrs = config.notifyUsers.split(","); |
| | | for (String userIdStr : userIdStrs) { |
| | | try { |
| | | userIds.add(Long.parseLong(userIdStr.trim())); |
| | | } catch (NumberFormatException e) { |
| | | log.warn("æ æçç¨æ·ID: {}", userIdStr); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 2. å¦ææ²¡æé
ç½®ç¨æ·ï¼æ¥è¯¢è½¦è¾æå±é¨é¨çè´è´£äºº |
| | | if ( deptId != null) { |
| | | SysDept dept = deptService.selectDeptById(deptId); |
| | | if (dept != null && StringUtils.isNotEmpty(dept.getLeader())) { |
| | | // leaderæ¯ç¨æ·åï¼éè¿ç¨æ·åæ¥è¯¢ç¨æ·ID |
| | | com.ruoyi.common.core.domain.entity.SysUser leaderUser = |
| | | userService.selectUserByUserName(dept.getLeader()); |
| | | if (leaderUser != null) { |
| | | userIds.add(leaderUser.getUserId()); |
| | | log.info("使ç¨é¨é¨ {} è´è´£äºº: {} (ID: {})", |
| | | dept.getDeptName(), dept.getLeader(), leaderUser.getUserId()); |
| | | } else { |
| | | log.warn("é¨é¨ {} è´è´£äºº {} æªæ¾å°å¯¹åºç¨æ·", |
| | | dept.getDeptName(), dept.getLeader()); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 3. å¦æè¿æ¯æ²¡æç¨æ·ï¼ä½¿ç¨ç³»ç»é»è®¤é
ç½®çç¨æ·åè¡¨ï¼æ»å
¬å¸è´è´£äººï¼ |
| | | if (userIds.isEmpty()) { |
| | | String defaultUsers = configService.selectConfigByKey("vehicle.alert.default.users"); |
| | | if (StringUtils.isNotEmpty(defaultUsers)) { |
| | | String[] defaultUserIds = defaultUsers.split(","); |
| | | for (String userId : defaultUserIds) { |
| | | try { |
| | | userIds.add(Long.parseLong(userId.trim())); |
| | | log.info("使ç¨ç³»ç»é»è®¤éç¥ç¨æ·: {}", userId); |
| | | } catch (NumberFormatException e) { |
| | | log.warn("æ æçé»è®¤ç¨æ·ID: {}", userId); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | } catch (Exception e) { |
| | | log.error("è·åéç¥ç¨æ·å表失败", e); |
| | | } |
| | | |
| | | return userIds; |
| | | } |
| | | |
| | | /** |
| | | * æ£æ¥åè¦åè½æ¯å¦å¯ç¨ |
| | | */ |
| | | private boolean isAlertEnabled() { |
| | | try { |
| | | String enabled = configService.selectConfigByKey("vehicle.alert.enabled"); |
| | | return "true".equalsIgnoreCase(enabled); |
| | | } catch (Exception e) { |
| | | log.warn("è·ååè¦å¼å
³é
置失败ï¼ä½¿ç¨é»è®¤å¼(false)", e); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * è·å车è¾çåè¦é
ç½®ï¼ä¼å
级ï¼è½¦è¾ > é¨é¨ > å
¨å±ï¼ |
| | | */ |
| | | private AlertConfig getVehicleAlertConfig(Long vehicleId, Long deptId, AlertConfig globalConfig) { |
| | | try { |
| | | // 仿°æ®åºæ¥è¯¢é
ç½® |
| | | VehicleAlertConfig dbConfig = alertConfigService.getConfigByVehicle(vehicleId, deptId); |
| | | |
| | | if (dbConfig != null) { |
| | | // å°æ°æ®åºé
置转æ¢ä¸ºAlertConfig |
| | | AlertConfig config = new AlertConfig(); |
| | | config.mileageThreshold = dbConfig.getMileageThreshold(); |
| | | config.dailyLimit = dbConfig.getDailyAlertLimit(); |
| | | config.alertInterval = dbConfig.getAlertInterval(); |
| | | config.timeWindow = globalConfig.timeWindow; // æ¶é´çªå£ä½¿ç¨å
¨å±é
ç½® |
| | | config.notifyUsers = dbConfig.getNotifyUserIds(); |
| | | return config; |
| | | } |
| | | |
| | | // å¦ææ²¡ææ°æ®åºé
ç½®ï¼ä½¿ç¨å
¨å±é
ç½® |
| | | return globalConfig; |
| | | |
| | | } catch (Exception e) { |
| | | log.error("è·å车è¾é
置失败ï¼vehicleId={}", vehicleId, e); |
| | | return globalConfig; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * å è½½åè¦é
ç½®åæ° |
| | | */ |
| | | private AlertConfig loadAlertConfig() { |
| | | AlertConfig config = new AlertConfig(); |
| | | |
| | | try { |
| | | // å
¬éæ°éå¼ |
| | | String thresholdStr = configService.selectConfigByKey("vehicle.alert.mileage.threshold"); |
| | | config.mileageThreshold = StringUtils.isNotEmpty(thresholdStr) |
| | | ? new BigDecimal(thresholdStr) : new BigDecimal("10"); |
| | | |
| | | // æ¯æ¥åè¦æ¬¡æ°éå¶ |
| | | String limitStr = configService.selectConfigByKey("vehicle.alert.daily.limit"); |
| | | config.dailyLimit = StringUtils.isNotEmpty(limitStr) ? Integer.parseInt(limitStr) : 5; |
| | | |
| | | // åè¦é´éæ¶é´ |
| | | String intervalStr = configService.selectConfigByKey("vehicle.alert.interval.minutes"); |
| | | config.alertInterval = StringUtils.isNotEmpty(intervalStr) ? Integer.parseInt(intervalStr) : 5; |
| | | |
| | | // çæ§æ¶é´çªå£ |
| | | String windowStr = configService.selectConfigByKey("vehicle.alert.time.window"); |
| | | config.timeWindow = StringUtils.isNotEmpty(windowStr) ? Integer.parseInt(windowStr) : 10; |
| | | |
| | | // éç¥ç¨æ·å表 |
| | | config.notifyUsers = configService.selectConfigByKey("vehicle.alert.notify.users"); |
| | | |
| | | log.debug("åè¦é
ç½®: éå¼={}km, æ¯æ¥éå¶={}次, é´é={}åé, æ¶é´çªå£={}åé", |
| | | config.mileageThreshold, config.dailyLimit, config.alertInterval, config.timeWindow); |
| | | |
| | | } catch (Exception e) { |
| | | log.error("å è½½åè¦é
置失败ï¼ä½¿ç¨é»è®¤å¼", e); |
| | | } |
| | | |
| | | return config; |
| | | } |
| | | |
| | | /** |
| | | * åè¦é
ç½®å
é¨ç±» |
| | | */ |
| | | private static class AlertConfig { |
| | | BigDecimal mileageThreshold = new BigDecimal("10"); // å
¬éæ°éå¼ |
| | | int dailyLimit = 5; // æ¯æ¥åè¦æ¬¡æ°éå¶ |
| | | int alertInterval = 5; // åè¦é´éï¼åéï¼ |
| | | int timeWindow = 10; // çæ§æ¶é´çªå£ï¼åéï¼ |
| | | String notifyUsers; // éç¥ç¨æ·å表 |
| | | } |
| | | } |
| | |
| | | <artifactId>junit</artifactId> |
| | | <scope>test</scope> |
| | | </dependency> |
| | | |
| | | |
| | | <dependency> |
| | | <groupId>com.baidu.aip</groupId> |
| | | <artifactId>java-sdk</artifactId> |
| | | <version>4.16.19</version> |
| | | <exclusions> |
| | | <exclusion> |
| | | <groupId>org.slf4j</groupId> |
| | | <artifactId>slf4j-simple</artifactId> |
| | | </exclusion> |
| | | </exclusions> |
| | | </dependency> |
| | | <dependency> |
| | | <groupId>com.aliyun</groupId> |
| | | <artifactId>aliyun-java-sdk-core</artifactId> |
| | | <version>4.6.0</version> |
| | | </dependency> |
| | | |
| | | <dependency> |
| | | <groupId>com.tencentcloudapi</groupId> |
| | | <artifactId>tencentcloud-sdk-java-ocr</artifactId> |
| | | <version>3.1.1399</version> |
| | | </dependency> |
| | | <dependency> |
| | | <groupId>commons-codec</groupId> |
| | | <artifactId>commons-codec</artifactId> |
| | | <version>1.15</version> |
| | | </dependency> |
| | | <dependency> |
| | | <groupId>com.aliyun</groupId> |
| | | <artifactId>ocr_api20210707</artifactId> |
| | | <version>3.1.2</version> |
| | | <exclusions> |
| | | <exclusion> |
| | | <groupId>org.slf4j</groupId> |
| | | <artifactId>slf4j-simple</artifactId> |
| | | </exclusion> |
| | | </exclusions> |
| | | </dependency> |
| | | <!-- Spring Test --> |
| | | <dependency> |
| | | <groupId>org.springframework</groupId> |
| New file |
| | |
| | | package com.ruoyi.system.config; |
| | | |
| | | import org.springframework.boot.context.properties.ConfigurationProperties; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | /** |
| | | * ç¾åº¦OCRæå¡é
置类 |
| | | * ç¨äºç®¡çç¾åº¦AI弿¾å¹³å°OCRæå¡çç¸å
³é
ç½® |
| | | */ |
| | | @Component |
| | | @ConfigurationProperties(prefix = "baidu.ocr") |
| | | public class BaiduOCRConfig { |
| | | |
| | | /** |
| | | * App ID |
| | | */ |
| | | private String appId; |
| | | |
| | | /** |
| | | * API Key |
| | | */ |
| | | private String apiKey; |
| | | |
| | | /** |
| | | * Secret Key |
| | | */ |
| | | private String secretKey; |
| | | |
| | | // Getter å Setter æ¹æ³ |
| | | public String getAppId() { |
| | | return appId; |
| | | } |
| | | |
| | | public void setAppId(String appId) { |
| | | this.appId = appId; |
| | | } |
| | | |
| | | public String getApiKey() { |
| | | return apiKey; |
| | | } |
| | | |
| | | public void setApiKey(String apiKey) { |
| | | this.apiKey = apiKey; |
| | | } |
| | | |
| | | public String getSecretKey() { |
| | | return secretKey; |
| | | } |
| | | |
| | | public void setSecretKey(String secretKey) { |
| | | this.secretKey = secretKey; |
| | | } |
| | | |
| | | /** |
| | | * éªè¯é
ç½®æ¯å¦å®æ´ |
| | | * @return é
ç½®æ¯å¦å®æ´ |
| | | */ |
| | | public boolean isValid() { |
| | | return appId != null && !appId.trim().isEmpty() && |
| | | apiKey != null && !apiKey.trim().isEmpty() && |
| | | secretKey != null && !secretKey.trim().isEmpty(); |
| | | } |
| | | |
| | | @Override |
| | | public String toString() { |
| | | return "BaiduOCRConfig{" + |
| | | "appId='" + (appId != null ? "***" : "null") + '\'' + |
| | | ", apiKey='" + (apiKey != null ? "***" : "null") + '\'' + |
| | | ", secretKey='" + (secretKey != null ? "***" : "null") + '\'' + |
| | | '}'; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.ruoyi.system.config; |
| | | |
| | | import org.springframework.boot.context.properties.ConfigurationProperties; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | /** |
| | | * OCRæå¡é
置类 |
| | | * ç¨äºç®¡çé¿éäºOCRæå¡çç¸å
³é
ç½® |
| | | */ |
| | | @Component |
| | | @ConfigurationProperties(prefix = "ali.ocr") |
| | | public class OCRConfig { |
| | | |
| | | /** |
| | | * AccessKey ID |
| | | */ |
| | | private String accessKeyId; |
| | | |
| | | /** |
| | | * AccessKey Secret |
| | | */ |
| | | private String accessKeySecret; |
| | | |
| | | /** |
| | | * OCRæå¡ç«¯ç¹ |
| | | */ |
| | | private String endpoint = "ocr-api.cn-hangzhou.aliyuncs.com"; |
| | | |
| | | /** |
| | | * è¿æ¥è¶
æ¶æ¶é´ï¼æ¯«ç§ï¼ |
| | | */ |
| | | private Integer connectTimeout = 10000; |
| | | |
| | | /** |
| | | * 读åè¶
æ¶æ¶é´ï¼æ¯«ç§ï¼ |
| | | */ |
| | | private Integer readTimeout = 30000; |
| | | |
| | | // Getter å Setter æ¹æ³ |
| | | public String getAccessKeyId() { |
| | | return accessKeyId; |
| | | } |
| | | |
| | | public void setAccessKeyId(String accessKeyId) { |
| | | this.accessKeyId = accessKeyId; |
| | | } |
| | | |
| | | public String getAccessKeySecret() { |
| | | return accessKeySecret; |
| | | } |
| | | |
| | | public void setAccessKeySecret(String accessKeySecret) { |
| | | this.accessKeySecret = accessKeySecret; |
| | | } |
| | | |
| | | public String getEndpoint() { |
| | | return endpoint; |
| | | } |
| | | |
| | | public void setEndpoint(String endpoint) { |
| | | this.endpoint = endpoint; |
| | | } |
| | | |
| | | public Integer getConnectTimeout() { |
| | | return connectTimeout; |
| | | } |
| | | |
| | | public void setConnectTimeout(Integer connectTimeout) { |
| | | this.connectTimeout = connectTimeout; |
| | | } |
| | | |
| | | public Integer getReadTimeout() { |
| | | return readTimeout; |
| | | } |
| | | |
| | | public void setReadTimeout(Integer readTimeout) { |
| | | this.readTimeout = readTimeout; |
| | | } |
| | | |
| | | /** |
| | | * éªè¯é
ç½®æ¯å¦å®æ´ |
| | | * @return é
ç½®æ¯å¦å®æ´ |
| | | */ |
| | | public boolean isValid() { |
| | | return accessKeyId != null && !accessKeyId.trim().isEmpty() && |
| | | accessKeySecret != null && !accessKeySecret.trim().isEmpty(); |
| | | } |
| | | |
| | | @Override |
| | | public String toString() { |
| | | return "OCRConfig{" + |
| | | "accessKeyId='" + (accessKeyId != null ? "***" : "null") + '\'' + |
| | | ", endpoint='" + endpoint + '\'' + |
| | | ", connectTimeout=" + connectTimeout + |
| | | ", readTimeout=" + readTimeout + |
| | | '}'; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.ruoyi.system.config; |
| | | |
| | | import org.springframework.boot.context.properties.ConfigurationProperties; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | /** |
| | | * è
¾è®¯äºOCRé
置类 |
| | | * ç¨äºé
ç½®è
¾è®¯äºOCRæå¡çç¸å
³åæ° |
| | | */ |
| | | @Component |
| | | @ConfigurationProperties(prefix = "tencent.ocr") |
| | | public class TencentOCRConfig { |
| | | |
| | | /** |
| | | * è
¾è®¯äºSecretId |
| | | */ |
| | | private String secretId; |
| | | |
| | | /** |
| | | * è
¾è®¯äºSecretKey |
| | | */ |
| | | private String secretKey; |
| | | |
| | | /** |
| | | * è
¾è®¯äºOCRæå¡ç«¯ç¹ |
| | | */ |
| | | private String endpoint = "ocr.tencentcloudapi.com"; |
| | | |
| | | public String getSecretId() { |
| | | return secretId; |
| | | } |
| | | |
| | | public void setSecretId(String secretId) { |
| | | this.secretId = secretId; |
| | | } |
| | | |
| | | public String getSecretKey() { |
| | | return secretKey; |
| | | } |
| | | |
| | | public void setSecretKey(String secretKey) { |
| | | this.secretKey = secretKey; |
| | | } |
| | | |
| | | public String getEndpoint() { |
| | | return endpoint; |
| | | } |
| | | |
| | | public void setEndpoint(String endpoint) { |
| | | this.endpoint = endpoint; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.ruoyi.system.controller; |
| | | |
| | | import com.ruoyi.common.core.domain.AjaxResult; |
| | | import com.ruoyi.common.core.controller.BaseController; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import java.net.InetAddress; |
| | | import java.net.UnknownHostException; |
| | | import java.util.*; |
| | | import java.net.Socket; |
| | | import java.io.IOException; |
| | | |
| | | /** |
| | | * ç½ç»è¯æController |
| | | * æä¾OCRæå¡è¿æ¥è¯æãDNSè§£ææµè¯ãç½ç»è¿éæ§æµè¯çåè½ |
| | | */ |
| | | @RestController |
| | | @RequestMapping("/system/diag") |
| | | public class NetworkDiagController extends BaseController { |
| | | |
| | | /** |
| | | * è¯æOCRæå¡è¿æ¥ |
| | | * @return è¯æç»æ |
| | | */ |
| | | @GetMapping("/ocrConnection") |
| | | public AjaxResult diagOcrConnection() { |
| | | Map<String, Object> result = new HashMap<>(); |
| | | |
| | | String ocrEndpoint = "ocr-api.cn-hangzhou.aliyuncs.com"; |
| | | int port = 443; // HTTPSç«¯å£ |
| | | |
| | | try { |
| | | result.put("dns", testDns(ocrEndpoint)); |
| | | result.put("connection", testConnection(ocrEndpoint, port)); |
| | | result.put("network", getNetworkConfig()); |
| | | |
| | | return success(result); |
| | | |
| | | } catch (Exception e) { |
| | | logger.error("ç½ç»è¯æå¤±è´¥", e); |
| | | return error("ç½ç»è¯æå¤±è´¥: " + e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * æµè¯DNSè§£æ |
| | | * @param params å
å«hostnameçåæ° |
| | | * @return DNSè§£æç»æ |
| | | */ |
| | | @PostMapping("/testDns") |
| | | public AjaxResult testDns(@RequestBody Map<String, String> params) { |
| | | String hostname = params.get("hostname"); |
| | | if (hostname == null || hostname.trim().isEmpty()) { |
| | | return error("主æºåä¸è½ä¸ºç©º"); |
| | | } |
| | | return success(testDns(hostname)); |
| | | } |
| | | |
| | | /** |
| | | * æµè¯ç½ç»è¿éæ§ |
| | | * @param params å
å«hoståportçåæ° |
| | | * @return è¿éæ§æµè¯ç»æ |
| | | */ |
| | | @PostMapping("/testConnectivity") |
| | | public AjaxResult testConnectivity(@RequestBody Map<String, Object> params) { |
| | | String host = (String) params.get("host"); |
| | | Integer port = (Integer) params.get("port"); |
| | | |
| | | if (host == null || host.trim().isEmpty()) { |
| | | return error("主æºä¸è½ä¸ºç©º"); |
| | | } |
| | | if (port == null) { |
| | | return error("端å£ä¸è½ä¸ºç©º"); |
| | | } |
| | | |
| | | return success(testConnection(host, port)); |
| | | } |
| | | |
| | | /** |
| | | * æµè¯DNSè§£æ |
| | | * @param hostname 主æºå |
| | | * @return DNSè§£æç»æ |
| | | */ |
| | | private Map<String, Object> testDns(String hostname) { |
| | | Map<String, Object> dnsResult = new HashMap<>(); |
| | | |
| | | try { |
| | | InetAddress[] addresses = InetAddress.getAllByName(hostname); |
| | | dnsResult.put("success", true); |
| | | dnsResult.put("hostname", hostname); |
| | | dnsResult.put("ipCount", addresses.length); |
| | | |
| | | String[] ips = new String[addresses.length]; |
| | | for (int i = 0; i < addresses.length; i++) { |
| | | ips[i] = addresses[i].getHostAddress(); |
| | | } |
| | | dnsResult.put("ips", ips); |
| | | |
| | | } catch (UnknownHostException e) { |
| | | dnsResult.put("success", false); |
| | | dnsResult.put("error", "DNSè§£æå¤±è´¥: " + e.getMessage()); |
| | | dnsResult.put("suggestion", "è¯·æ£æ¥DNSæå¡å¨é
ç½®ï¼å¯ä»¥å°è¯ä½¿ç¨å
Œ
±DNSï¼å¦8.8.8.8ï¼"); |
| | | } |
| | | |
| | | return dnsResult; |
| | | } |
| | | |
| | | /** |
| | | * æµè¯ç½ç»è¿æ¥ |
| | | * @param hostname 主æºå |
| | | * @param port 端å£å· |
| | | * @return è¿æ¥æµè¯ç»æ |
| | | */ |
| | | private Map<String, Object> testConnection(String hostname, int port) { |
| | | Map<String, Object> connResult = new HashMap<>(); |
| | | long startTime = System.currentTimeMillis(); |
| | | |
| | | try (Socket socket = new Socket()) { |
| | | socket.connect(new java.net.InetSocketAddress(hostname, port), 10000); // 10ç§è¶
æ¶ |
| | | long endTime = System.currentTimeMillis(); |
| | | long responseTime = endTime - startTime; |
| | | |
| | | connResult.put("success", true); |
| | | connResult.put("hostname", hostname); |
| | | connResult.put("port", port); |
| | | connResult.put("responseTime", responseTime + "ms"); |
| | | connResult.put("connected", true); |
| | | |
| | | } catch (IOException e) { |
| | | long endTime = System.currentTimeMillis(); |
| | | long responseTime = endTime - startTime; |
| | | |
| | | connResult.put("success", false); |
| | | connResult.put("hostname", hostname); |
| | | connResult.put("port", port); |
| | | connResult.put("responseTime", responseTime + "ms"); |
| | | connResult.put("error", "è¿æ¥å¤±è´¥: " + e.getMessage()); |
| | | connResult.put("suggestion", "è¯·æ£æ¥é²ç«å¢è®¾ç½®ï¼ç¡®è®¤ç«¯å£" + port + "æ¯å¦å¼æ¾"); |
| | | } |
| | | |
| | | return connResult; |
| | | } |
| | | |
| | | /** |
| | | * è·åç½ç»é
ç½®ä¿¡æ¯ |
| | | * @return ç½ç»é
ç½®ä¿¡æ¯ |
| | | */ |
| | | private Map<String, Object> getNetworkConfig() { |
| | | Map<String, Object> networkConfig = new HashMap<>(); |
| | | try { |
| | | networkConfig.put("httpProxy", System.getProperty("http.proxyHost")); |
| | | networkConfig.put("httpsProxy", System.getProperty("https.proxyHost")); |
| | | networkConfig.put("localHostAddress", InetAddress.getLocalHost().getHostAddress()); |
| | | networkConfig.put("localHostName", InetAddress.getLocalHost().getHostName()); |
| | | } catch (Exception e) { |
| | | networkConfig.put("error", "è·åç½ç»é
置失败: " + e.getMessage()); |
| | | } |
| | | return networkConfig; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.ruoyi.system.controller; |
| | | |
| | | import com.alibaba.fastjson.JSONObject; |
| | | import com.ruoyi.common.core.controller.BaseController; |
| | | import com.ruoyi.common.core.domain.AjaxResult; |
| | | import com.ruoyi.common.utils.file.FileUploadUtils; |
| | | import com.ruoyi.system.utils.AliOCRUtil; |
| | | import com.ruoyi.system.utils.BaiduOCRUtil; |
| | | import com.ruoyi.system.utils.TencentOCRUtil; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.web.bind.annotation.*; |
| | | import org.springframework.web.multipart.MultipartFile; |
| | | |
| | | import java.io.File; |
| | | import java.util.Arrays; |
| | | import java.util.HashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.stream.Collectors; |
| | | |
| | | /** |
| | | * OCRè¯å«Controller |
| | | * æ¯æé¿éäºOCRåç¾åº¦OCRæå¡ |
| | | * @author ruoyi |
| | | */ |
| | | @RestController |
| | | @RequestMapping("/system/ocr") |
| | | public class OCRController extends BaseController { |
| | | |
| | | @Autowired |
| | | private AliOCRUtil aliOCRUtil; |
| | | |
| | | // æ¯æçOCRè¯å«ç±»å |
| | | private static final List<String> SUPPORTED_TYPES = Arrays.asList("General", "Invoice", "IdCard", "HandWriting"); |
| | | |
| | | /** |
| | | * ä¸ä¼ å¾çå¹¶è¿è¡OCRè¯å« |
| | | * @param file ä¸ä¼ çå¾çæä»¶ |
| | | * @param type è¯å«ç±»åï¼General-éç¨, Invoice-å票, IdCard-身份è¯, HandWriting-æåä½ï¼ |
| | | * @param provider OCRæå¡æä¾åï¼ali-é¿éäº, baidu-ç¾åº¦ï¼ |
| | | * @return OCRè¯å«ç»æ |
| | | */ |
| | | @PostMapping(value = "/recognize", consumes = "multipart/form-data") |
| | | public AjaxResult recognizeImage(@RequestParam("file") MultipartFile file, |
| | | @RequestParam(value = "type", defaultValue = "General") String type, |
| | | @RequestParam(value = "provider", defaultValue = "ali") String provider, |
| | | @RequestParam(value = "itemNames", required = false) String[] itemNames) { |
| | | try { |
| | | if (file.isEmpty()) { |
| | | return error("ä¸ä¼ å¾çä¸è½ä¸ºç©º"); |
| | | } |
| | | |
| | | // éªè¯è¯å«ç±»å |
| | | if (!SUPPORTED_TYPES.contains(type)) { |
| | | return error("䏿¯æçè¯å«ç±»å: " + type + ", æ¯æçç±»å: " + String.join(",", SUPPORTED_TYPES)); |
| | | } |
| | | |
| | | // ä¿åä¸´æ¶æä»¶ |
| | | String tempDir = System.getProperty("java.io.tmpdir"); |
| | | String originalFilename = file.getOriginalFilename(); |
| | | File tempFile = new File(tempDir, System.currentTimeMillis() + "_" + originalFilename); |
| | | file.transferTo(tempFile); |
| | | |
| | | // æ ¹æ®æä¾åè°ç¨ä¸åçOCRæå¡ |
| | | JSONObject ocrResult; |
| | | if ("baidu".equalsIgnoreCase(provider)) { |
| | | // ç¾åº¦OCRåªæ¯æé¨åç±»å |
| | | if ("General".equals(type)) { |
| | | ocrResult = BaiduOCRUtil.generalRecognize(tempFile); |
| | | } else if ("HandWriting".equals(type)) { |
| | | ocrResult = BaiduOCRUtil.handwritingRecognize(tempFile); |
| | | } else { |
| | | ocrResult = BaiduOCRUtil.generalRecognize(tempFile); // é»è®¤ä½¿ç¨éç¨è¯å« |
| | | } |
| | | } else if ("tencent".equalsIgnoreCase(provider)) { |
| | | // è
¾è®¯äºOCRåªæ¯æé¨åç±»å |
| | | if ("General".equals(type)) { |
| | | ocrResult = TencentOCRUtil.generalRecognize(tempFile); |
| | | } else if ("HandWriting".equals(type)) { |
| | | ocrResult = TencentOCRUtil.handwritingRecognize(tempFile.getAbsolutePath(), itemNames); |
| | | } else { |
| | | ocrResult = TencentOCRUtil.generalRecognize(tempFile); // é»è®¤ä½¿ç¨éç¨è¯å« |
| | | } |
| | | } else { |
| | | // é¿éäºOCR |
| | | ocrResult = AliOCRUtil.recognizeTextByFile(tempFile, type); |
| | | } |
| | | |
| | | // å é¤ä¸´æ¶æä»¶ |
| | | tempFile.delete(); |
| | | |
| | | // æå»ºè¿åç»æ |
| | | Map<String, Object> result = new HashMap<>(); |
| | | result.put("ocrResult", ocrResult); |
| | | result.put("fileName", originalFilename); |
| | | result.put("type", type); |
| | | result.put("provider", provider); |
| | | |
| | | if (ocrResult.getBooleanValue("success")) { |
| | | return success(result); |
| | | } else { |
| | | return error("OCRè¯å«å¤±è´¥: " + ocrResult.getString("error")); |
| | | } |
| | | |
| | | } catch (Exception e) { |
| | | logger.error("OCRè¯å«å¼å¸¸", e); |
| | | return error("OCRè¯å«å¼å¸¸: " + e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * éè¿å¾çURLè¿è¡OCRè¯å« |
| | | * @param imageUrl å¾çURLå°å |
| | | * @param type è¯å«ç±»åï¼General-éç¨, Invoice-å票, IdCard-身份è¯, HandWriting-æåä½ï¼ |
| | | * @param provider OCRæå¡æä¾åï¼ali-é¿éäº, baidu-ç¾åº¦ï¼ |
| | | * @return OCRè¯å«ç»æ |
| | | */ |
| | | @GetMapping("/recognizeByUrl") |
| | | public AjaxResult recognizeByUrl(@RequestParam("imageUrl") String imageUrl, |
| | | @RequestParam(value = "type", defaultValue = "General") String type, |
| | | @RequestParam(value = "provider", defaultValue = "ali") String provider, |
| | | @RequestParam(value = "itemNames", required = false) String[] itemNames) { |
| | | try { |
| | | // éªè¯è¯å«ç±»å |
| | | if (!SUPPORTED_TYPES.contains(type)) { |
| | | return error("䏿¯æçè¯å«ç±»å: " + type + ", æ¯æçç±»å: " + String.join(",", SUPPORTED_TYPES)); |
| | | } |
| | | |
| | | // æ ¹æ®æä¾åè°ç¨ä¸åçOCRæå¡ |
| | | JSONObject ocrResult; |
| | | if ("baidu".equalsIgnoreCase(provider)) { |
| | | // ç¾åº¦OCRåªæ¯æé¨åç±»å |
| | | if ("General".equals(type)) { |
| | | ocrResult = BaiduOCRUtil.generalRecognize(imageUrl); |
| | | } else if ("HandWriting".equals(type)) { |
| | | ocrResult = BaiduOCRUtil.handwritingRecognize(imageUrl); |
| | | } else { |
| | | ocrResult = BaiduOCRUtil.generalRecognize(imageUrl); // é»è®¤ä½¿ç¨éç¨è¯å« |
| | | } |
| | | } else if ("tencent".equalsIgnoreCase(provider)) { |
| | | // è
¾è®¯äºOCRåªæ¯æé¨åç±»å |
| | | if ("General".equals(type)) { |
| | | ocrResult = TencentOCRUtil.generalRecognize(imageUrl); |
| | | } else if ("HandWriting".equals(type)) { |
| | | ocrResult = TencentOCRUtil.handwritingRecognize(imageUrl, itemNames); |
| | | } else { |
| | | ocrResult = TencentOCRUtil.generalRecognize(imageUrl); // é»è®¤ä½¿ç¨éç¨è¯å« |
| | | } |
| | | } else { |
| | | // é¿éäºOCR |
| | | ocrResult = AliOCRUtil.recognizeTextByUrl(imageUrl, type); |
| | | } |
| | | |
| | | // æå»ºè¿åç»æ |
| | | Map<String, Object> result = new HashMap<>(); |
| | | result.put("ocrResult", ocrResult); |
| | | result.put("imageUrl", imageUrl); |
| | | result.put("type", type); |
| | | result.put("provider", provider); |
| | | |
| | | if (ocrResult.getBooleanValue("success")) { |
| | | return success(result); |
| | | } else { |
| | | return error("OCRè¯å«å¤±è´¥: " + ocrResult.getString("error")); |
| | | } |
| | | |
| | | } catch (Exception e) { |
| | | logger.error("OCRè¯å«å¼å¸¸", e); |
| | | return error("OCRè¯å«å¼å¸¸: " + e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * è·åæ¯æçOCRè¯å«ç±»åå表 |
| | | * @return è¯å«ç±»åå表 |
| | | */ |
| | | @GetMapping("/types") |
| | | public AjaxResult getSupportedTypes() { |
| | | Map<String, Object> result = new HashMap<>(); |
| | | result.put("types", SUPPORTED_TYPES); |
| | | |
| | | List<Map<String, String>> typeList = SUPPORTED_TYPES.stream().map(type -> { |
| | | Map<String, String> typeInfo = new HashMap<>(); |
| | | typeInfo.put("value", type); |
| | | |
| | | // æ ¹æ®ç±»å设置æ¾ç¤ºåç§° |
| | | switch (type) { |
| | | case "General": |
| | | typeInfo.put("label", "éç¨æåè¯å«"); |
| | | break; |
| | | case "Invoice": |
| | | typeInfo.put("label", "å票è¯å«"); |
| | | break; |
| | | case "IdCard": |
| | | typeInfo.put("label", "身份è¯è¯å«"); |
| | | break; |
| | | case "HandWriting": |
| | | typeInfo.put("label", "æåä½è¯å«"); |
| | | break; |
| | | default: |
| | | typeInfo.put("label", type); |
| | | break; |
| | | } |
| | | return typeInfo; |
| | | }).collect(Collectors.toList()); |
| | | |
| | | result.put("typeList", typeList); |
| | | return success(result); |
| | | } |
| | | |
| | | /** |
| | | * è·åæ¯æçOCRæå¡æä¾åå表 |
| | | * @return OCRæå¡æä¾åå表 |
| | | */ |
| | | @GetMapping("/providers") |
| | | public AjaxResult getSupportedProviders() { |
| | | Map<String, Object> result = new HashMap<>(); |
| | | List<Map<String, String>> providerList = Arrays.asList( |
| | | createProviderInfo("ali", "é¿éäºOCR", true), |
| | | createProviderInfo("baidu", "ç¾åº¦OCR", true), |
| | | createProviderInfo("tencent", "è
¾è®¯äºOCR", true) |
| | | ); |
| | | result.put("providers", providerList); |
| | | return success(result); |
| | | } |
| | | |
| | | /** |
| | | * æåOCRç»æä¸çç®æ åæ®µ |
| | | * @param ocrResult OCRåå§ç»æ |
| | | * @return æåçåæ®µä¿¡æ¯ |
| | | */ |
| | | @PostMapping("/extractFields") |
| | | public AjaxResult extractFields(@RequestBody JSONObject ocrResult) { |
| | | try { |
| | | // æ£æ¥æ¯å¦ä¸ºç¾åº¦OCRç»æ |
| | | String provider = ocrResult.getString("provider"); |
| | | Map<String, String> extracted; |
| | | if ("baidu".equalsIgnoreCase(provider)) { |
| | | extracted = BaiduOCRUtil.extractTargetFields(ocrResult); |
| | | } else if ("tencent".equalsIgnoreCase(provider)) { |
| | | extracted = TencentOCRUtil.extractTargetFields(ocrResult); |
| | | } else { |
| | | extracted = AliOCRUtil.extractTargetFields(ocrResult); |
| | | } |
| | | return success(extracted); |
| | | } catch (Exception e) { |
| | | logger.error("åæ®µæåå¼å¸¸", e); |
| | | return error("åæ®µæåå¼å¸¸: " + e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * è
¾è®¯äºæåä½è¯å«ï¼æ¯æèªå®ä¹å段æåï¼ |
| | | * @param file ä¸ä¼ çå¾çæä»¶ |
| | | * @param itemNames éè¦æåçåæ®µåç§°æ°ç» |
| | | * @return è¯å«ç»æ Mapï¼keyä¸ºåæ®µåï¼value为è¯å«å
容 |
| | | */ |
| | | @PostMapping(value = "/tencent/handwriting", consumes = "multipart/form-data") |
| | | public AjaxResult tencentHandwritingRecognize(@RequestParam("file") MultipartFile file, |
| | | @RequestParam(value = "itemNames", required = false) String[] itemNames) { |
| | | try { |
| | | if (file.isEmpty()) { |
| | | return error("ä¸ä¼ å¾çä¸è½ä¸ºç©º"); |
| | | } |
| | | |
| | | // ä¿åä¸´æ¶æä»¶ |
| | | String tempDir = System.getProperty("java.io.tmpdir"); |
| | | String originalFilename = file.getOriginalFilename(); |
| | | File tempFile = new File(tempDir, System.currentTimeMillis() + "_" + originalFilename); |
| | | file.transferTo(tempFile); |
| | | |
| | | // è°ç¨è
¾è®¯äºæåä½è¯å« |
| | | Map<String, String> resultMap = TencentOCRUtil.handwritingRecognizeWith(tempFile.getAbsolutePath(), itemNames); |
| | | |
| | | // å é¤ä¸´æ¶æä»¶ |
| | | tempFile.delete(); |
| | | |
| | | // æ£æ¥æ¯å¦æé误 |
| | | if (resultMap.containsKey("error")) { |
| | | return error("è
¾è®¯äºOCRæåä½è¯å«å¤±è´¥: " + resultMap.get("error")); |
| | | } |
| | | |
| | | // æå»ºè¿åç»æ |
| | | Map<String, Object> result = new HashMap<>(); |
| | | result.put("fileName", originalFilename); |
| | | result.put("type", "HandWriting"); |
| | | result.put("provider", "tencent"); |
| | | result.put("fields", resultMap); |
| | | result.put("fieldCount", resultMap.size()); |
| | | |
| | | return success(result); |
| | | |
| | | } catch (Exception e) { |
| | | logger.error("è
¾è®¯äºOCRæåä½è¯å«å¼å¸¸", e); |
| | | return error("è
¾è®¯äºOCRæåä½è¯å«å¼å¸¸: " + e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * è
¾è®¯äºæåä½è¯å«éè¿URLï¼æ¯æèªå®ä¹å段æåï¼ |
| | | * @param imageUrl å¾çURLå°å |
| | | * @param itemNames éè¦æåçåæ®µåç§°æ°ç» |
| | | * @return è¯å«ç»æ Mapï¼keyä¸ºåæ®µåï¼value为è¯å«å
容 |
| | | */ |
| | | @GetMapping("/tencent/handwritingByUrl") |
| | | public AjaxResult tencentHandwritingRecognizeByUrl(@RequestParam("imageUrl") String imageUrl, |
| | | @RequestParam(value = "itemNames", required = false) String[] itemNames) { |
| | | try { |
| | | // è°ç¨è
¾è®¯äºæåä½è¯å« |
| | | Map<String, String> resultMap = TencentOCRUtil.handwritingRecognizeWith(imageUrl, itemNames); |
| | | |
| | | // æ£æ¥æ¯å¦æé误 |
| | | if (resultMap.containsKey("error")) { |
| | | return error("è
¾è®¯äºOCRæåä½è¯å«å¤±è´¥: " + resultMap.get("error")); |
| | | } |
| | | |
| | | // æå»ºè¿åç»æ |
| | | Map<String, Object> result = new HashMap<>(); |
| | | result.put("imageUrl", imageUrl); |
| | | result.put("type", "HandWriting"); |
| | | result.put("provider", "tencent"); |
| | | result.put("fields", resultMap); |
| | | result.put("fieldCount", resultMap.size()); |
| | | |
| | | return success(result); |
| | | |
| | | } catch (Exception e) { |
| | | logger.error("è
¾è®¯äºOCRæåä½è¯å«å¼å¸¸", e); |
| | | return error("è
¾è®¯äºOCRæåä½è¯å«å¼å¸¸: " + e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * è
¾è®¯äºæåä½è¯å«ï¼æ¯æå¤å¾çæ¹éè¯å«ï¼ |
| | | * @param files ä¸ä¼ çå¾çæä»¶æ°ç» |
| | | * @param itemNames éè¦æåçåæ®µåç§°æ°ç» |
| | | * @return è¯å«ç»æï¼åå¹¶ææå¾ççè¯å«å段 |
| | | */ |
| | | @PostMapping(value = "/tencent/handwriting/batch", consumes = "multipart/form-data") |
| | | public AjaxResult tencentHandwritingRecognizeBatch(@RequestParam("files") MultipartFile[] files, |
| | | @RequestParam(value = "itemNames", required = false) String[] itemNames) { |
| | | try { |
| | | if (files == null || files.length == 0) { |
| | | return error("ä¸ä¼ å¾çä¸è½ä¸ºç©º"); |
| | | } |
| | | |
| | | // åå¹¶ææå¾ççè¯å«ç»æ |
| | | Map<String, String> mergedResultMap = new HashMap<>(); |
| | | int successCount = 0; |
| | | int failCount = 0; |
| | | StringBuilder errorMessages = new StringBuilder(); |
| | | |
| | | for (MultipartFile file : files) { |
| | | if (file.isEmpty()) { |
| | | continue; |
| | | } |
| | | |
| | | try { |
| | | // ä¿åä¸´æ¶æä»¶ |
| | | String tempDir = System.getProperty("java.io.tmpdir"); |
| | | String originalFilename = file.getOriginalFilename(); |
| | | File tempFile = new File(tempDir, System.currentTimeMillis() + "_" + originalFilename); |
| | | file.transferTo(tempFile); |
| | | |
| | | // è°ç¨è
¾è®¯äºæåä½è¯å« |
| | | Map<String, String> resultMap = TencentOCRUtil.handwritingRecognizeWith(tempFile.getAbsolutePath(), itemNames); |
| | | |
| | | // å é¤ä¸´æ¶æä»¶ |
| | | tempFile.delete(); |
| | | |
| | | // æ£æ¥æ¯å¦æé误 |
| | | if (resultMap.containsKey("error")) { |
| | | failCount++; |
| | | errorMessages.append(originalFilename).append(":").append(resultMap.get("error")).append("; "); |
| | | logger.warn("å¾ç {} è¯å«å¤±è´¥: {}", originalFilename, resultMap.get("error")); |
| | | } else { |
| | | // åå¹¶è¯å«ç»æï¼å¦ækeyå·²åå¨ï¼ä¸è¦çï¼ |
| | | for (Map.Entry<String, String> entry : resultMap.entrySet()) { |
| | | if (!mergedResultMap.containsKey(entry.getKey()) || mergedResultMap.get(entry.getKey()).isEmpty()) { |
| | | mergedResultMap.put(entry.getKey(), entry.getValue()); |
| | | } |
| | | } |
| | | successCount++; |
| | | logger.info("å¾ç {} è¯å«æåï¼æå {} ä¸ªåæ®µ", originalFilename, resultMap.size()); |
| | | } |
| | | } catch (Exception e) { |
| | | failCount++; |
| | | errorMessages.append(file.getOriginalFilename()).append(":").append(e.getMessage()).append("; "); |
| | | logger.error("å¤çå¾ç {} æ¶åçå¼å¸¸", file.getOriginalFilename(), e); |
| | | } |
| | | } |
| | | |
| | | // æå»ºè¿åç»æ |
| | | Map<String, Object> result = new HashMap<>(); |
| | | result.put("type", "HandWriting"); |
| | | result.put("provider", "tencent"); |
| | | result.put("fields", mergedResultMap); |
| | | result.put("fieldCount", mergedResultMap.size()); |
| | | result.put("totalImages", files.length); |
| | | result.put("successCount", successCount); |
| | | result.put("failCount", failCount); |
| | | |
| | | if (failCount > 0) { |
| | | result.put("errors", errorMessages.toString()); |
| | | } |
| | | |
| | | if (successCount == 0) { |
| | | return error("ææå¾çè¯å«å¤±è´¥: " + errorMessages.toString()); |
| | | } |
| | | |
| | | return success(result); |
| | | |
| | | } catch (Exception e) { |
| | | logger.error("è
¾è®¯äºOCRæå使¹éè¯å«å¼å¸¸", e); |
| | | return error("è
¾è®¯äºOCRæå使¹éè¯å«å¼å¸¸: " + e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * å建æå¡æä¾åä¿¡æ¯ |
| | | * @param value æå¡æä¾åæ è¯ |
| | | * @param label æå¡æä¾åæ¾ç¤ºåç§° |
| | | * @param available æ¯å¦å¯ç¨ |
| | | * @return æå¡æä¾åä¿¡æ¯ |
| | | */ |
| | | private Map<String, String> createProviderInfo(String value, String label, boolean available) { |
| | | Map<String, String> providerInfo = new HashMap<>(); |
| | | providerInfo.put("value", value); |
| | | providerInfo.put("label", label); |
| | | providerInfo.put("available", String.valueOf(available)); |
| | | return providerInfo; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.ruoyi.system.domain; |
| | | |
| | | import java.io.Serializable; |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * å»é¢åè¯ä»»å¡ç¶æ |
| | | * |
| | | * @author ruoyi |
| | | * @date 2026-01-20 |
| | | */ |
| | | public class HospitalTokenizerTask implements Serializable { |
| | | |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | /** ä»»å¡ID */ |
| | | private String taskId; |
| | | |
| | | /** ä»»å¡ç¶æ: RUNNING-è¿è¡ä¸, SUCCESS-æå, FAILED-失败 */ |
| | | private String status; |
| | | |
| | | /** æ»å»é¢æ°é */ |
| | | private Integer totalCount; |
| | | |
| | | /** å·²å¤çæ°é */ |
| | | private Integer processedCount; |
| | | |
| | | /** æåæ°é */ |
| | | private Integer successCount; |
| | | |
| | | /** 失败æ°é */ |
| | | private Integer failedCount; |
| | | |
| | | /** è¿åº¦ç¾åæ¯ */ |
| | | private Integer progress; |
| | | |
| | | /** å¼å§æ¶é´ */ |
| | | private Date startTime; |
| | | |
| | | /** ç»ææ¶é´ */ |
| | | private Date endTime; |
| | | |
| | | /** éè¯¯ä¿¡æ¯ */ |
| | | private String errorMessage; |
| | | |
| | | public HospitalTokenizerTask() { |
| | | } |
| | | |
| | | public HospitalTokenizerTask(String taskId) { |
| | | this.taskId = taskId; |
| | | this.status = "RUNNING"; |
| | | this.totalCount = 0; |
| | | this.processedCount = 0; |
| | | this.successCount = 0; |
| | | this.failedCount = 0; |
| | | this.progress = 0; |
| | | this.startTime = new Date(); |
| | | } |
| | | |
| | | public String getTaskId() { |
| | | return taskId; |
| | | } |
| | | |
| | | public void setTaskId(String taskId) { |
| | | this.taskId = taskId; |
| | | } |
| | | |
| | | public String getStatus() { |
| | | return status; |
| | | } |
| | | |
| | | public void setStatus(String status) { |
| | | this.status = status; |
| | | } |
| | | |
| | | public Integer getTotalCount() { |
| | | return totalCount; |
| | | } |
| | | |
| | | public void setTotalCount(Integer totalCount) { |
| | | this.totalCount = totalCount; |
| | | } |
| | | |
| | | public Integer getProcessedCount() { |
| | | return processedCount; |
| | | } |
| | | |
| | | public void setProcessedCount(Integer processedCount) { |
| | | this.processedCount = processedCount; |
| | | // èªå¨è®¡ç®è¿åº¦ |
| | | if (totalCount != null && totalCount > 0) { |
| | | this.progress = (int) ((processedCount * 100.0) / totalCount); |
| | | } |
| | | } |
| | | |
| | | public Integer getSuccessCount() { |
| | | return successCount; |
| | | } |
| | | |
| | | public void setSuccessCount(Integer successCount) { |
| | | this.successCount = successCount; |
| | | } |
| | | |
| | | public Integer getFailedCount() { |
| | | return failedCount; |
| | | } |
| | | |
| | | public void setFailedCount(Integer failedCount) { |
| | | this.failedCount = failedCount; |
| | | } |
| | | |
| | | public Integer getProgress() { |
| | | return progress; |
| | | } |
| | | |
| | | public void setProgress(Integer progress) { |
| | | this.progress = progress; |
| | | } |
| | | |
| | | public Date getStartTime() { |
| | | return startTime; |
| | | } |
| | | |
| | | public void setStartTime(Date startTime) { |
| | | this.startTime = startTime; |
| | | } |
| | | |
| | | public Date getEndTime() { |
| | | return endTime; |
| | | } |
| | | |
| | | public void setEndTime(Date endTime) { |
| | | this.endTime = endTime; |
| | | } |
| | | |
| | | public String getErrorMessage() { |
| | | return errorMessage; |
| | | } |
| | | |
| | | public void setErrorMessage(String errorMessage) { |
| | | this.errorMessage = errorMessage; |
| | | } |
| | | } |
| | |
| | | /** å»é¢çº§å« */ |
| | | private Integer hospLevel; |
| | | |
| | | /** å»é¢ä¿¡æ¯åè¯ï¼éå·åéï¼ */ |
| | | private String hospKeywords; |
| | | |
| | | /** æ°æ®ç¶æï¼0æ£å¸¸ 1åç¨ï¼ */ |
| | | private String status; |
| | | |
| | |
| | | this.hospLevel = hospLevel; |
| | | } |
| | | |
| | | public String getHospKeywords() { |
| | | return hospKeywords; |
| | | } |
| | | |
| | | public void setHospKeywords(String hospKeywords) { |
| | | this.hospKeywords = hospKeywords; |
| | | } |
| | | |
| | | public String getStatus() { |
| | | return status; |
| | | } |
| | |
| | | .append("hospIntroducerId", getHospIntroducerId()) |
| | | .append("hospIntroducerDate", getHospIntroducerDate()) |
| | | .append("hospLevel", getHospLevel()) |
| | | .append("hospKeywords", getHospKeywords()) |
| | | .append("status", getStatus()) |
| | | .append("remark", getRemark()) |
| | | .append("createBy", getCreateBy()) |
| New file |
| | |
| | | package com.ruoyi.system.domain; |
| | | |
| | | import java.io.Serializable; |
| | | import java.math.BigDecimal; |
| | | import java.util.Date; |
| | | import com.fasterxml.jackson.annotation.JsonFormat; |
| | | import com.ruoyi.common.annotation.Excel; |
| | | import com.ruoyi.common.core.domain.BaseEntity; |
| | | import org.apache.commons.lang3.builder.ToStringBuilder; |
| | | import org.apache.commons.lang3.builder.ToStringStyle; |
| | | |
| | | /** |
| | | * 车è¾å¼å¸¸è¿è¡åè¦è®°å½å¯¹è±¡ tb_vehicle_abnormal_alert |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class VehicleAbnormalAlert extends BaseEntity implements Serializable { |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | /** åè¦ID */ |
| | | private Long alertId; |
| | | |
| | | /** 车è¾ID */ |
| | | @Excel(name = "车è¾ID") |
| | | private Long vehicleId; |
| | | |
| | | /** 车çå· */ |
| | | @Excel(name = "车çå·") |
| | | private String vehicleNo; |
| | | |
| | | /** åè¦æ¥æ */ |
| | | @JsonFormat(pattern = "yyyy-MM-dd") |
| | | @Excel(name = "åè¦æ¥æ", width = 30, dateFormat = "yyyy-MM-dd") |
| | | private Date alertDate; |
| | | |
| | | /** åè¦æ¶é´ */ |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | @Excel(name = "åè¦æ¶é´", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") |
| | | private Date alertTime; |
| | | |
| | | /** 累计è¿è¡å
¬éæ°(å
Ž) */ |
| | | @Excel(name = "累计å
¬éæ°") |
| | | private BigDecimal mileage; |
| | | |
| | | /** åè¦ç±»å(NO_TASK_MILEAGE-æ ä»»å¡è¶
å
Ž) */ |
| | | @Excel(name = "åè¦ç±»å", readConverterExp = "NO_TASK_MILEAGE=æ ä»»å¡è¶
å
Ž") |
| | | private String alertType; |
| | | |
| | | /** åè¦åå æè¿° */ |
| | | @Excel(name = "åè¦åå ") |
| | | private String alertReason; |
| | | |
| | | /** å¼å§è¿è¡æ¶é´ */ |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | @Excel(name = "å¼å§æ¶é´", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") |
| | | private Date startTime; |
| | | |
| | | /** ç»æè¿è¡æ¶é´ */ |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | @Excel(name = "ç»ææ¶é´", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") |
| | | private Date endTime; |
| | | |
| | | /** 彿¥åè¦æ¬¡æ° */ |
| | | @Excel(name = "彿¥åè¦æ¬¡æ°") |
| | | private Integer alertCount; |
| | | |
| | | /** ç¶æï¼0-æªå¤ç 1-å·²å¤ç 2-已忽ç¥ï¼ */ |
| | | @Excel(name = "ç¶æ", readConverterExp = "0=æªå¤ç,1=å·²å¤ç,2=已忽ç¥") |
| | | private String status; |
| | | |
| | | /** å¤ç人ID */ |
| | | private Long handlerId; |
| | | |
| | | /** å¤ç人å§å */ |
| | | @Excel(name = "å¤ç人") |
| | | private String handlerName; |
| | | |
| | | /** å¤çæ¶é´ */ |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | @Excel(name = "å¤çæ¶é´", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") |
| | | private Date handleTime; |
| | | |
| | | /** å¤ç夿³¨ */ |
| | | @Excel(name = "å¤ç夿³¨") |
| | | private String handleRemark; |
| | | |
| | | /** éç¥ç¶æï¼0-æªåé 1-å·²åé 2-åéå¤±è´¥ï¼ */ |
| | | @Excel(name = "éç¥ç¶æ", readConverterExp = "0=æªåé,1=å·²åé,2=åé失败") |
| | | private String notifyStatus; |
| | | |
| | | /** éç¥æ¶é´ */ |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | private Date notifyTime; |
| | | |
| | | /** éç¥ç¨æ·IDå表ï¼éå·åéï¼ */ |
| | | private String notifyUsers; |
| | | |
| | | /** å½å±é¨é¨ID */ |
| | | @Excel(name = "å½å±é¨é¨ID") |
| | | private Long deptId; |
| | | |
| | | /** å½å±é¨é¨åç§° */ |
| | | @Excel(name = "å½å±é¨é¨") |
| | | private String deptName; |
| | | |
| | | public void setAlertId(Long alertId) { |
| | | this.alertId = alertId; |
| | | } |
| | | |
| | | public Long getAlertId() { |
| | | return alertId; |
| | | } |
| | | |
| | | public void setVehicleId(Long vehicleId) { |
| | | this.vehicleId = vehicleId; |
| | | } |
| | | |
| | | public Long getVehicleId() { |
| | | return vehicleId; |
| | | } |
| | | |
| | | public void setVehicleNo(String vehicleNo) { |
| | | this.vehicleNo = vehicleNo; |
| | | } |
| | | |
| | | public String getVehicleNo() { |
| | | return vehicleNo; |
| | | } |
| | | |
| | | public void setAlertDate(Date alertDate) { |
| | | this.alertDate = alertDate; |
| | | } |
| | | |
| | | public Date getAlertDate() { |
| | | return alertDate; |
| | | } |
| | | |
| | | public void setAlertTime(Date alertTime) { |
| | | this.alertTime = alertTime; |
| | | } |
| | | |
| | | public Date getAlertTime() { |
| | | return alertTime; |
| | | } |
| | | |
| | | public void setMileage(BigDecimal mileage) { |
| | | this.mileage = mileage; |
| | | } |
| | | |
| | | public BigDecimal getMileage() { |
| | | return mileage; |
| | | } |
| | | |
| | | public void setAlertType(String alertType) { |
| | | this.alertType = alertType; |
| | | } |
| | | |
| | | public String getAlertType() { |
| | | return alertType; |
| | | } |
| | | |
| | | public void setAlertReason(String alertReason) { |
| | | this.alertReason = alertReason; |
| | | } |
| | | |
| | | public String getAlertReason() { |
| | | return alertReason; |
| | | } |
| | | |
| | | public void setStartTime(Date startTime) { |
| | | this.startTime = startTime; |
| | | } |
| | | |
| | | public Date getStartTime() { |
| | | return startTime; |
| | | } |
| | | |
| | | public void setEndTime(Date endTime) { |
| | | this.endTime = endTime; |
| | | } |
| | | |
| | | public Date getEndTime() { |
| | | return endTime; |
| | | } |
| | | |
| | | public void setAlertCount(Integer alertCount) { |
| | | this.alertCount = alertCount; |
| | | } |
| | | |
| | | public Integer getAlertCount() { |
| | | return alertCount; |
| | | } |
| | | |
| | | public void setStatus(String status) { |
| | | this.status = status; |
| | | } |
| | | |
| | | public String getStatus() { |
| | | return status; |
| | | } |
| | | |
| | | public void setHandlerId(Long handlerId) { |
| | | this.handlerId = handlerId; |
| | | } |
| | | |
| | | public Long getHandlerId() { |
| | | return handlerId; |
| | | } |
| | | |
| | | public void setHandlerName(String handlerName) { |
| | | this.handlerName = handlerName; |
| | | } |
| | | |
| | | public String getHandlerName() { |
| | | return handlerName; |
| | | } |
| | | |
| | | public void setHandleTime(Date handleTime) { |
| | | this.handleTime = handleTime; |
| | | } |
| | | |
| | | public Date getHandleTime() { |
| | | return handleTime; |
| | | } |
| | | |
| | | public void setHandleRemark(String handleRemark) { |
| | | this.handleRemark = handleRemark; |
| | | } |
| | | |
| | | public String getHandleRemark() { |
| | | return handleRemark; |
| | | } |
| | | |
| | | public void setNotifyStatus(String notifyStatus) { |
| | | this.notifyStatus = notifyStatus; |
| | | } |
| | | |
| | | public String getNotifyStatus() { |
| | | return notifyStatus; |
| | | } |
| | | |
| | | public void setNotifyTime(Date notifyTime) { |
| | | this.notifyTime = notifyTime; |
| | | } |
| | | |
| | | public Date getNotifyTime() { |
| | | return notifyTime; |
| | | } |
| | | |
| | | public void setNotifyUsers(String notifyUsers) { |
| | | this.notifyUsers = notifyUsers; |
| | | } |
| | | |
| | | public String getNotifyUsers() { |
| | | return notifyUsers; |
| | | } |
| | | |
| | | public void setDeptId(Long deptId) { |
| | | this.deptId = deptId; |
| | | } |
| | | |
| | | public Long getDeptId() { |
| | | return deptId; |
| | | } |
| | | |
| | | public void setDeptName(String deptName) { |
| | | this.deptName = deptName; |
| | | } |
| | | |
| | | public String getDeptName() { |
| | | return deptName; |
| | | } |
| | | |
| | | @Override |
| | | public String toString() { |
| | | return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE) |
| | | .append("alertId", getAlertId()) |
| | | .append("vehicleId", getVehicleId()) |
| | | .append("vehicleNo", getVehicleNo()) |
| | | .append("alertDate", getAlertDate()) |
| | | .append("alertTime", getAlertTime()) |
| | | .append("mileage", getMileage()) |
| | | .append("alertType", getAlertType()) |
| | | .append("alertReason", getAlertReason()) |
| | | .append("startTime", getStartTime()) |
| | | .append("endTime", getEndTime()) |
| | | .append("alertCount", getAlertCount()) |
| | | .append("status", getStatus()) |
| | | .append("handlerId", getHandlerId()) |
| | | .append("handlerName", getHandlerName()) |
| | | .append("handleTime", getHandleTime()) |
| | | .append("handleRemark", getHandleRemark()) |
| | | .append("notifyStatus", getNotifyStatus()) |
| | | .append("notifyTime", getNotifyTime()) |
| | | .append("notifyUsers", getNotifyUsers()) |
| | | .append("deptId", getDeptId()) |
| | | .append("deptName", getDeptName()) |
| | | .append("createTime", getCreateTime()) |
| | | .append("updateTime", getUpdateTime()) |
| | | .toString(); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.ruoyi.system.domain; |
| | | |
| | | import java.io.Serializable; |
| | | import java.math.BigDecimal; |
| | | import com.ruoyi.common.annotation.Excel; |
| | | import com.ruoyi.common.core.domain.BaseEntity; |
| | | import org.apache.commons.lang3.builder.ToStringBuilder; |
| | | import org.apache.commons.lang3.builder.ToStringStyle; |
| | | |
| | | /** |
| | | * 车è¾å¼å¸¸åè¦é
置对象 tb_vehicle_alert_config |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class VehicleAlertConfig extends BaseEntity implements Serializable { |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | /** é
ç½®ID */ |
| | | private Long configId; |
| | | |
| | | /** é
置类å(GLOBAL-å
¨å±/DEPT-é¨é¨/VEHICLE-车è¾) */ |
| | | @Excel(name = "é
置类å", readConverterExp = "GLOBAL=å
¨å±,DEPT=é¨é¨,VEHICLE=车è¾") |
| | | private String configType; |
| | | |
| | | /** é¨é¨IDï¼é¨é¨é
ç½®æ¶ä½¿ç¨ï¼ */ |
| | | @Excel(name = "é¨é¨ID") |
| | | private Long deptId; |
| | | |
| | | /** 车è¾IDï¼è½¦è¾é
ç½®æ¶ä½¿ç¨ï¼ */ |
| | | @Excel(name = "车è¾ID") |
| | | private Long vehicleId; |
| | | |
| | | /** ç®æ åç§°ï¼é¨é¨åç§°æè½¦çå·ï¼ */ |
| | | private String targetName; |
| | | |
| | | /** å
¬éæ°åè¦éå¼(å
Ž) */ |
| | | @Excel(name = "å
¬éæ°éå¼") |
| | | private BigDecimal mileageThreshold; |
| | | |
| | | /** æ¯æ¥æå¤§åè¦æ¬¡æ° */ |
| | | @Excel(name = "æ¯æ¥åè¦æ¬¡æ°") |
| | | private Integer dailyAlertLimit; |
| | | |
| | | /** åè¦é´éæ¶é´(åé) */ |
| | | @Excel(name = "åè¦é´é(åé)") |
| | | private Integer alertInterval; |
| | | |
| | | /** éç¥ç¨æ·IDå表ï¼éå·åéï¼ */ |
| | | @Excel(name = "éç¥ç¨æ·") |
| | | private String notifyUserIds; |
| | | |
| | | /** ç¶æï¼0-å¯ç¨ 1-åç¨ï¼ */ |
| | | @Excel(name = "ç¶æ", readConverterExp = "0=å¯ç¨,1=åç¨") |
| | | private String status; |
| | | |
| | | public void setConfigId(Long configId) { |
| | | this.configId = configId; |
| | | } |
| | | |
| | | public Long getConfigId() { |
| | | return configId; |
| | | } |
| | | |
| | | public void setConfigType(String configType) { |
| | | this.configType = configType; |
| | | } |
| | | |
| | | public String getConfigType() { |
| | | return configType; |
| | | } |
| | | |
| | | public void setDeptId(Long deptId) { |
| | | this.deptId = deptId; |
| | | } |
| | | |
| | | public Long getDeptId() { |
| | | return deptId; |
| | | } |
| | | |
| | | public void setVehicleId(Long vehicleId) { |
| | | this.vehicleId = vehicleId; |
| | | } |
| | | |
| | | public Long getVehicleId() { |
| | | return vehicleId; |
| | | } |
| | | |
| | | public void setTargetName(String targetName) { |
| | | this.targetName = targetName; |
| | | } |
| | | |
| | | public String getTargetName() { |
| | | return targetName; |
| | | } |
| | | |
| | | public void setMileageThreshold(BigDecimal mileageThreshold) { |
| | | this.mileageThreshold = mileageThreshold; |
| | | } |
| | | |
| | | public BigDecimal getMileageThreshold() { |
| | | return mileageThreshold; |
| | | } |
| | | |
| | | public void setDailyAlertLimit(Integer dailyAlertLimit) { |
| | | this.dailyAlertLimit = dailyAlertLimit; |
| | | } |
| | | |
| | | public Integer getDailyAlertLimit() { |
| | | return dailyAlertLimit; |
| | | } |
| | | |
| | | public void setAlertInterval(Integer alertInterval) { |
| | | this.alertInterval = alertInterval; |
| | | } |
| | | |
| | | public Integer getAlertInterval() { |
| | | return alertInterval; |
| | | } |
| | | |
| | | public void setNotifyUserIds(String notifyUserIds) { |
| | | this.notifyUserIds = notifyUserIds; |
| | | } |
| | | |
| | | public String getNotifyUserIds() { |
| | | return notifyUserIds; |
| | | } |
| | | |
| | | public void setStatus(String status) { |
| | | this.status = status; |
| | | } |
| | | |
| | | public String getStatus() { |
| | | return status; |
| | | } |
| | | |
| | | @Override |
| | | public String toString() { |
| | | return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE) |
| | | .append("configId", getConfigId()) |
| | | .append("configType", getConfigType()) |
| | | .append("deptId", getDeptId()) |
| | | .append("vehicleId", getVehicleId()) |
| | | .append("targetName", getTargetName()) |
| | | .append("mileageThreshold", getMileageThreshold()) |
| | | .append("dailyAlertLimit", getDailyAlertLimit()) |
| | | .append("alertInterval", getAlertInterval()) |
| | | .append("notifyUserIds", getNotifyUserIds()) |
| | | .append("status", getStatus()) |
| | | .append("createBy", getCreateBy()) |
| | | .append("createTime", getCreateTime()) |
| | | .append("updateBy", getUpdateBy()) |
| | | .append("updateTime", getUpdateTime()) |
| | | .append("remark", getRemark()) |
| | | .toString(); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.ruoyi.system.domain.vo; |
| | | |
| | | /** |
| | | * 车è¾åæ¥è§å¾å¯¹è±¡ |
| | | * ç¨äºå端æ¾ç¤ºæ§ç³»ç»è½¦è¾æ°æ®ååæ¥ç¶æ |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class VehicleSyncVO { |
| | | |
| | | /** æ§ç³»ç»è½¦è¾ID */ |
| | | private Integer carId; |
| | | |
| | | /** 车çå· */ |
| | | private String vehicleNo; |
| | | |
| | | /** 车è¾åæ®ç±»åç¼ç */ |
| | | private String carOrdClass; |
| | | |
| | | /** å½å±åå
¬å¸ï¼ä»æ§ç³»ç»è·åçåæ®ç±»åæ å°ï¼ */ |
| | | private String deptName; |
| | | |
| | | /** æ¯å¦å·²åæ¥å°æ°ç³»ç» */ |
| | | private Boolean synced; |
| | | |
| | | /** æ°ç³»ç»ä¸ç车è¾IDï¼å·²åæ¥æ¶æå¼ï¼ */ |
| | | private Long vehicleId; |
| | | |
| | | /** æ°ç³»ç»ä¸çé¨é¨IDï¼å·²åæ¥æ¶æå¼ï¼ */ |
| | | private Long deptId; |
| | | |
| | | public Integer getCarId() { |
| | | return carId; |
| | | } |
| | | |
| | | public void setCarId(Integer carId) { |
| | | this.carId = carId; |
| | | } |
| | | |
| | | public String getVehicleNo() { |
| | | return vehicleNo; |
| | | } |
| | | |
| | | public void setVehicleNo(String vehicleNo) { |
| | | this.vehicleNo = vehicleNo; |
| | | } |
| | | |
| | | public String getCarOrdClass() { |
| | | return carOrdClass; |
| | | } |
| | | |
| | | public void setCarOrdClass(String carOrdClass) { |
| | | this.carOrdClass = carOrdClass; |
| | | } |
| | | |
| | | public String getDeptName() { |
| | | return deptName; |
| | | } |
| | | |
| | | public void setDeptName(String deptName) { |
| | | this.deptName = deptName; |
| | | } |
| | | |
| | | public Boolean getSynced() { |
| | | return synced; |
| | | } |
| | | |
| | | public void setSynced(Boolean synced) { |
| | | this.synced = synced; |
| | | } |
| | | |
| | | public Long getVehicleId() { |
| | | return vehicleId; |
| | | } |
| | | |
| | | public void setVehicleId(Long vehicleId) { |
| | | this.vehicleId = vehicleId; |
| | | } |
| | | |
| | | public Long getDeptId() { |
| | | return deptId; |
| | | } |
| | | |
| | | public void setDeptId(Long deptId) { |
| | | this.deptId = deptId; |
| | | } |
| | | |
| | | @Override |
| | | public String toString() { |
| | | return "VehicleSyncVO{" + |
| | | "carId=" + carId + |
| | | ", vehicleNo='" + vehicleNo + '\'' + |
| | | ", carOrdClass='" + carOrdClass + '\'' + |
| | | ", deptName='" + deptName + '\'' + |
| | | ", synced=" + synced + |
| | | ", vehicleId=" + vehicleId + |
| | | ", deptId=" + deptId + |
| | | '}'; |
| | | } |
| | | } |
| | |
| | | sendDispatchNotify(assigneeIds, task.getCreatorId(), event.getTaskId(),task.getShowTaskCode(), buildNotifyContent(task, emergency)); |
| | | } |
| | | } |
| | | |
| | | syncDispatchActualStartTime(emergency, task); |
| | | Long taskId= event.getTaskId(); |
| | | Long dispatchOrdId= event.getDispatchOrderId(); |
| | | Long serviceOrdId= event.getServiceOrderId(); |
| | |
| | | log.error("å¤ç任塿´¾å忥äºä»¶å¤±è´¥", ex); |
| | | } |
| | | } |
| | | |
| | | private void syncDispatchActualStartTime(SysTaskEmergency emergency, SysTask task) { |
| | | try { |
| | | //è¿éä¹åæ¥ä¸ä¸å®é
æ¶é´ |
| | | Long disatpchOrdId = emergency.getLegacyDispatchOrdId(); |
| | | Date actualTime = task.getActualStartTime(); |
| | | legacySystemSyncService.updateDispatchActualTime(disatpchOrdId, actualTime); |
| | | }catch (Exception ex){ |
| | | log.error("忥å®é
æ¶é´å¤±è´¥", ex); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * çå¬ä»»å¡å建äºä»¶ |
| | | * |
| | |
| | | public int updateDispatchOrdCancelReason(@Param("dispatchOrdID") Long dispatchOrdID, |
| | | @Param("cancelReasonId") Integer cancelReasonId, |
| | | @Param("cancelReasonText") String cancelReasonText); |
| | | |
| | | /** |
| | | * æ´æ°è°åº¦åå®é
å¼å§æ¶é´ |
| | | * |
| | | * @param dispatchOrdID è°åº¦åID |
| | | * @param actualDate å®é
å¼å§æ¶é´ |
| | | * @return å½±åè¡æ° |
| | | */ |
| | | public int updateDispatchOrdActualDate(@Param("dispatchOrdID") Long dispatchOrdID, |
| | | @Param("actualDate") java.util.Date actualDate); |
| | | } |
| | |
| | | * @return 任塿°é |
| | | */ |
| | | public int countTaskByPhoneAndDate(@Param("phone") String phone, @Param("createDate") String createDate); |
| | | |
| | | /** |
| | | * æ¥è¯¢è½¦è¾å¨æå®æ¶é´èå´å
çä»»å¡å表 |
| | | * |
| | | * @param params å
å«vehicleIdãstartTimeãendTimeçåæ°Map |
| | | * @return ä»»å¡å表 |
| | | */ |
| | | public List<SysTask> selectVehicleTasksInTimeRange(java.util.Map<String, Object> params); |
| | | } |
| | |
| | | * @return ç»æ |
| | | */ |
| | | int deleteTbHospDataByIds(@Param("hospIds") Long[] hospIds); |
| | | |
| | | /** |
| | | * æ ¹æ®åè¯å
³é®è¯é¢è¿æ»¤å»é¢æ°æ® |
| | | * éè¿æ°æ®åºå±é¢çLIKEå¹é
å¿«éçéåºå¯è½å¹é
çå»é¢ |
| | | * |
| | | * @param keywords åè¯å
³é®è¯å表 |
| | | * @param status å»é¢ç¶æï¼0-æ£å¸¸ï¼ |
| | | * @return å»é¢æ°æ®éå |
| | | */ |
| | | List<TbHospData> selectTbHospDataByKeywords(@Param("keywords") List<String> keywords, @Param("status") String status); |
| | | } |
| New file |
| | |
| | | package com.ruoyi.system.mapper; |
| | | |
| | | import java.util.Date; |
| | | import java.util.List; |
| | | import com.ruoyi.system.domain.VehicleAbnormalAlert; |
| | | import org.apache.ibatis.annotations.Param; |
| | | |
| | | /** |
| | | * 车è¾å¼å¸¸åè¦Mapperæ¥å£ |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public interface VehicleAbnormalAlertMapper { |
| | | /** |
| | | * æ¥è¯¢è½¦è¾å¼å¸¸åè¦ |
| | | * |
| | | * @param alertId 车è¾å¼å¸¸åè¦ä¸»é® |
| | | * @return 车è¾å¼å¸¸åè¦ |
| | | */ |
| | | public VehicleAbnormalAlert selectVehicleAbnormalAlertByAlertId(Long alertId); |
| | | |
| | | /** |
| | | * æ¥è¯¢è½¦è¾å¼å¸¸åè¦å表 |
| | | * |
| | | * @param vehicleAbnormalAlert 车è¾å¼å¸¸åè¦ |
| | | * @return 车è¾å¼å¸¸åè¦éå |
| | | */ |
| | | public List<VehicleAbnormalAlert> selectVehicleAbnormalAlertList(VehicleAbnormalAlert vehicleAbnormalAlert); |
| | | |
| | | /** |
| | | * æ°å¢è½¦è¾å¼å¸¸åè¦ |
| | | * |
| | | * @param vehicleAbnormalAlert 车è¾å¼å¸¸åè¦ |
| | | * @return ç»æ |
| | | */ |
| | | public int insertVehicleAbnormalAlert(VehicleAbnormalAlert vehicleAbnormalAlert); |
| | | |
| | | /** |
| | | * ä¿®æ¹è½¦è¾å¼å¸¸åè¦ |
| | | * |
| | | * @param vehicleAbnormalAlert 车è¾å¼å¸¸åè¦ |
| | | * @return ç»æ |
| | | */ |
| | | public int updateVehicleAbnormalAlert(VehicleAbnormalAlert vehicleAbnormalAlert); |
| | | |
| | | /** |
| | | * å é¤è½¦è¾å¼å¸¸åè¦ |
| | | * |
| | | * @param alertId 车è¾å¼å¸¸åè¦ä¸»é® |
| | | * @return ç»æ |
| | | */ |
| | | public int deleteVehicleAbnormalAlertByAlertId(Long alertId); |
| | | |
| | | /** |
| | | * æ¹éå é¤è½¦è¾å¼å¸¸åè¦ |
| | | * |
| | | * @param alertIds éè¦å é¤çæ°æ®ä¸»é®éå |
| | | * @return ç»æ |
| | | */ |
| | | public int deleteVehicleAbnormalAlertByAlertIds(Long[] alertIds); |
| | | |
| | | /** |
| | | * æ¥è¯¢è½¦è¾å½æ¥åè¦æ¬¡æ° |
| | | * |
| | | * @param vehicleId 车è¾ID |
| | | * @param alertDate åè¦æ¥æ |
| | | * @return åè¦æ¬¡æ° |
| | | */ |
| | | public int selectDailyAlertCount(@Param("vehicleId") Long vehicleId, @Param("alertDate") Date alertDate); |
| | | |
| | | /** |
| | | * æ¥è¯¢è½¦è¾æå䏿¬¡åè¦æ¶é´ |
| | | * |
| | | * @param vehicleId 车è¾ID |
| | | * @return æååè¦æ¶é´ |
| | | */ |
| | | public Date selectLastAlertTime(@Param("vehicleId") Long vehicleId); |
| | | |
| | | /** |
| | | * æ¹éå¤çåè¦ |
| | | * |
| | | * @param alertIds åè¦IDå表 |
| | | * @param handlerId å¤ç人ID |
| | | * @param handlerName å¤ç人å§å |
| | | * @param handleRemark å¤ç夿³¨ |
| | | * @return ç»æ |
| | | */ |
| | | public int batchHandleAlert(@Param("alertIds") Long[] alertIds, |
| | | @Param("handlerId") Long handlerId, |
| | | @Param("handlerName") String handlerName, |
| | | @Param("handleRemark") String handleRemark); |
| | | } |
| New file |
| | |
| | | package com.ruoyi.system.mapper; |
| | | |
| | | import java.util.List; |
| | | import org.apache.ibatis.annotations.Param; |
| | | import com.ruoyi.system.domain.VehicleAlertConfig; |
| | | |
| | | /** |
| | | * 车è¾åè¦é
ç½®Mapperæ¥å£ |
| | | * |
| | | * @author ruoyi |
| | | * @date 2026-01-12 |
| | | */ |
| | | public interface VehicleAlertConfigMapper |
| | | { |
| | | /** |
| | | * æ¥è¯¢è½¦è¾åè¦é
ç½® |
| | | * |
| | | * @param configId 车è¾åè¦é
ç½®ä¸»é® |
| | | * @return 车è¾åè¦é
ç½® |
| | | */ |
| | | public VehicleAlertConfig selectVehicleAlertConfigByConfigId(Long configId); |
| | | |
| | | /** |
| | | * æ¥è¯¢è½¦è¾åè¦é
ç½®å表 |
| | | * |
| | | * @param vehicleAlertConfig 车è¾åè¦é
ç½® |
| | | * @return 车è¾åè¦é
ç½®éå |
| | | */ |
| | | public List<VehicleAlertConfig> selectVehicleAlertConfigList(VehicleAlertConfig vehicleAlertConfig); |
| | | |
| | | /** |
| | | * æ¥è¯¢è½¦è¾çé
ç½®ï¼ä¼å
级ï¼è½¦è¾ > é¨é¨ > å
¨å±ï¼ |
| | | * |
| | | * @param vehicleId 车è¾ID |
| | | * @param deptId é¨é¨ID |
| | | * @return 车è¾åè¦é
ç½® |
| | | */ |
| | | public VehicleAlertConfig selectConfigByVehicle(@Param("vehicleId") Long vehicleId, @Param("deptId") Long deptId); |
| | | |
| | | /** |
| | | * æ°å¢è½¦è¾åè¦é
ç½® |
| | | * |
| | | * @param vehicleAlertConfig 车è¾åè¦é
ç½® |
| | | * @return ç»æ |
| | | */ |
| | | public int insertVehicleAlertConfig(VehicleAlertConfig vehicleAlertConfig); |
| | | |
| | | /** |
| | | * ä¿®æ¹è½¦è¾åè¦é
ç½® |
| | | * |
| | | * @param vehicleAlertConfig 车è¾åè¦é
ç½® |
| | | * @return ç»æ |
| | | */ |
| | | public int updateVehicleAlertConfig(VehicleAlertConfig vehicleAlertConfig); |
| | | |
| | | /** |
| | | * å é¤è½¦è¾åè¦é
ç½® |
| | | * |
| | | * @param configId 车è¾åè¦é
ç½®ä¸»é® |
| | | * @return ç»æ |
| | | */ |
| | | public int deleteVehicleAlertConfigByConfigId(Long configId); |
| | | |
| | | /** |
| | | * æ¹éå é¤è½¦è¾åè¦é
ç½® |
| | | * |
| | | * @param configIds éè¦å é¤çæ°æ®ä¸»é®éå |
| | | * @return ç»æ |
| | | */ |
| | | public int deleteVehicleAlertConfigByConfigIds(Long[] configIds); |
| | | } |
| | |
| | | */ |
| | | public Long selectLastCalculatedGpsId(@Param("vehicleId") Long vehicleId, |
| | | @Param("beforeTime") Date beforeTime); |
| | | |
| | | /** |
| | | * æ¥è¯¢è½¦è¾å¨æå®æ¶é´èå´å
çåæ®µéç¨è®°å½ |
| | | * @param params å
å«vehicleIdãstartTimeãendTimeçåæ°Map |
| | | * @return åæ®µéç¨è®°å½å表 |
| | | */ |
| | | public List<VehicleGpsSegmentMileage> selectSegmentsByTimeRange(java.util.Map<String, Object> params); |
| | | } |
| New file |
| | |
| | | package com.ruoyi.system.service; |
| | | |
| | | import com.ruoyi.system.domain.HospitalTokenizerTask; |
| | | import com.ruoyi.system.domain.TbHospData; |
| | | import com.ruoyi.system.mapper.TbHospDataMapper; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.scheduling.annotation.Async; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.util.Date; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.concurrent.ConcurrentHashMap; |
| | | |
| | | /** |
| | | * å»é¢åè¯å¼æ¥ä»»å¡æå¡ |
| | | * |
| | | * @author ruoyi |
| | | * @date 2026-01-20 |
| | | */ |
| | | @Service |
| | | public class HospitalTokenizerAsyncService { |
| | | |
| | | private static final Logger logger = LoggerFactory.getLogger(HospitalTokenizerAsyncService.class); |
| | | |
| | | @Autowired |
| | | private ITbHospDataService tbHospDataService; |
| | | |
| | | @Autowired |
| | | private TbHospDataMapper tbHospDataMapper; |
| | | |
| | | /** |
| | | * ä»»å¡ç¶æç¼å (ç产ç¯å¢å»ºè®®ä½¿ç¨ Redis) |
| | | */ |
| | | private static final Map<String, HospitalTokenizerTask> taskCache = new ConcurrentHashMap<>(); |
| | | |
| | | /** |
| | | * 弿¥æ§è¡å»é¢åè¯ä»»å¡ |
| | | * |
| | | * @param taskId ä»»å¡ID |
| | | */ |
| | | @Async("taskExecutor") |
| | | public void executeTokenizerTask(String taskId) { |
| | | HospitalTokenizerTask task = new HospitalTokenizerTask(taskId); |
| | | taskCache.put(taskId, task); |
| | | |
| | | logger.info("å¼å§æ§è¡å»é¢åè¯å¼æ¥ä»»å¡: taskId={}", taskId); |
| | | |
| | | try { |
| | | // æ¥è¯¢æææ£å¸¸ç¶æçå»é¢ |
| | | TbHospData query = new TbHospData(); |
| | | query.setStatus("0"); |
| | | List<TbHospData> hospitalList = tbHospDataMapper.selectTbHospDataList(query); |
| | | |
| | | task.setTotalCount(hospitalList.size()); |
| | | logger.info("æ¥è¯¢å° {} 个å»é¢éè¦çæåè¯", hospitalList.size()); |
| | | |
| | | int successCount = 0; |
| | | int failedCount = 0; |
| | | |
| | | // éåå¤çæ¯ä¸ªå»é¢ |
| | | for (int i = 0; i < hospitalList.size(); i++) { |
| | | TbHospData hospital = hospitalList.get(i); |
| | | |
| | | try { |
| | | // çæåè¯ |
| | | String keywords = tbHospDataService.generateKeywordsForHospital(hospital); |
| | | hospital.setHospKeywords(keywords); |
| | | |
| | | // æ´æ°æ°æ®åº |
| | | int result = tbHospDataMapper.updateTbHospData(hospital); |
| | | if (result > 0) { |
| | | successCount++; |
| | | } else { |
| | | failedCount++; |
| | | } |
| | | |
| | | } catch (Exception e) { |
| | | failedCount++; |
| | | logger.error("çæå»é¢åè¯å¤±è´¥: hospId={}, hospName={}", |
| | | hospital.getHospId(), hospital.getHospName(), e); |
| | | } |
| | | |
| | | // æ´æ°ä»»å¡è¿åº¦ |
| | | task.setProcessedCount(i + 1); |
| | | task.setSuccessCount(successCount); |
| | | task.setFailedCount(failedCount); |
| | | |
| | | // æ¯å¤ç100æ¡è¾åºä¸æ¬¡æ¥å¿ |
| | | if ((i + 1) % 100 == 0) { |
| | | logger.info("å»é¢åè¯è¿åº¦: {}/{}, æå: {}, 失败: {}", |
| | | i + 1, hospitalList.size(), successCount, failedCount); |
| | | } |
| | | } |
| | | |
| | | // ä»»å¡å®æ |
| | | task.setStatus("SUCCESS"); |
| | | task.setEndTime(new Date()); |
| | | |
| | | logger.info("å»é¢åè¯ä»»å¡å®æ: taskId={}, æ»æ°: {}, æå: {}, 失败: {}", |
| | | taskId, hospitalList.size(), successCount, failedCount); |
| | | |
| | | } catch (Exception e) { |
| | | // ä»»å¡å¤±è´¥ |
| | | task.setStatus("FAILED"); |
| | | task.setEndTime(new Date()); |
| | | task.setErrorMessage(e.getMessage()); |
| | | |
| | | logger.error("å»é¢åè¯ä»»å¡æ§è¡å¤±è´¥: taskId={}", taskId, e); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * è·åä»»å¡ç¶æ |
| | | * |
| | | * @param taskId ä»»å¡ID |
| | | * @return ä»»å¡ç¶æ |
| | | */ |
| | | public HospitalTokenizerTask getTaskStatus(String taskId) { |
| | | return taskCache.get(taskId); |
| | | } |
| | | |
| | | /** |
| | | * æ¸
çä»»å¡ç¼å |
| | | * |
| | | * @param taskId ä»»å¡ID |
| | | */ |
| | | public void clearTask(String taskId) { |
| | | taskCache.remove(taskId); |
| | | } |
| | | |
| | | /** |
| | | * æ¸
çææå·²å®æçä»»å¡ (è¶
è¿1å°æ¶) |
| | | */ |
| | | public void clearExpiredTasks() { |
| | | long oneHourAgo = System.currentTimeMillis() - 3600000; |
| | | |
| | | taskCache.entrySet().removeIf(entry -> { |
| | | HospitalTokenizerTask task = entry.getValue(); |
| | | if (task.getEndTime() != null && task.getEndTime().getTime() < oneHourAgo) { |
| | | logger.info("æ¸
çè¿æä»»å¡: taskId={}", entry.getKey()); |
| | | return true; |
| | | } |
| | | return false; |
| | | }); |
| | | } |
| | | } |
| | |
| | | public int updateDispatchOrdState(Long dispatchOrdID, Integer dispatchOrdState); |
| | | |
| | | public void cancelDispatchOrd(Long dispatchOrdID,Integer cancelReason,String cancelCReasonTxt); |
| | | |
| | | /** |
| | | * æ´æ°è°åº¦åå®é
å¼å§æ¶é´ |
| | | * |
| | | * @param dispatchOrdID è°åº¦åID |
| | | * @param actualDate å®é
å¼å§æ¶é´ |
| | | * @return å½±åè¡æ° |
| | | */ |
| | | public int updateDispatchOrdActualDate(Long dispatchOrdID, java.util.Date actualDate); |
| | | } |
| | |
| | | |
| | | import com.ruoyi.system.domain.SysTask; |
| | | |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * æ§ç³»ç»åæ¥Serviceæ¥å£ |
| | | * |
| | |
| | | */ |
| | | public interface ILegacySystemSyncService { |
| | | |
| | | |
| | | /** |
| | | * æ´æ°è°åº¦åå®é
宿æ¶é´ |
| | | * @param dispatchOrdId |
| | | * @param actualTime |
| | | * @return |
| | | */ |
| | | Integer updateDispatchActualTime(Long dispatchOrdId, Date actualTime); |
| | | /** |
| | | * åæ¥æ¥æè½¬è¿ä»»å¡å°æ§ç³»ç» |
| | | * |
| | |
| | | boolean sendNotifyMessage(Long userId, String title, String content, String notifyUrl); |
| | | |
| | | /** |
| | | * åéä¼ä¸å¾®ä¿¡æ¶æ¯ï¼å¸¦é»è®¤åºç¨ID |
| | | * @param userId ç¨æ·ID |
| | | * @param title æ¶æ¯æ é¢ |
| | | * @param content æ¶æ¯å
容 |
| | | * @param businessUrl å°ç¨åºè®¿é®è·¯å¾ |
| | | * @return |
| | | */ |
| | | boolean sendNotifyMessageWithDefaultAppId(Long userId, String title, String content, String businessUrl); |
| | | |
| | | /** |
| | | * åéä¼ä¸å¾®ä¿¡æ¶æ¯ï¼å¸¦å°ç¨åºè·¯å¾é¾æ¥ |
| | | * @param userId |
| | | * @param title |
| | |
| | | * @param createVO ä»»å¡å建对象 |
| | | * @return ç»æ |
| | | */ |
| | | public int insertSysTask(TaskCreateVO createVO); |
| | | /** |
| | | * æ°å¢ä»»å¡ |
| | | * |
| | | * @param createVO ä»»å¡åå»ºä¿¡æ¯ |
| | | * @return ä»»å¡ID |
| | | */ |
| | | public Long insertSysTask(TaskCreateVO createVO); |
| | | |
| | | /** |
| | | * æ°å¢ä»»å¡ç®¡çï¼å
许ä»å¤é¨ä¼ å
¥ç¨æ·ä¿¡æ¯ãé¨é¨ä¿¡æ¯åæ¶é´ä¿¡æ¯ï¼ |
| | | * |
| | | * @param createVO ä»»å¡å建对象 |
| | | * @param serviceOrderId æå¡åID |
| | | * @param dispatchOrderId è°åº¦åID |
| | | * @param serviceOrdNo æå¡åç¼å· |
| | | * @param userId ç¨æ·ID |
| | | * @param userName ç¨æ·åç§° |
| | | * @param deptId é¨é¨ID |
| | | * @param createTime å建æ¶é´ |
| | | * @param updateTime æ´æ°æ¶é´ |
| | | * @return ç»æ |
| | | * @return ä»»å¡ID |
| | | */ |
| | | public int insertTask(TaskCreateVO createVO,Long serviceOrderId,Long dispatchOrderId, String serviceOrdNo, Long userId,String userName, Long deptId, Date createTime, Date updateTime); |
| | | public Long insertTask(TaskCreateVO createVO,Long serviceOrderId,Long dispatchOrderId, String serviceOrdNo, Long userId,String userName, Long deptId, Date createTime, Date updateTime); |
| | | |
| | | /** |
| | | * ä¿®æ¹ä»»å¡ç®¡ç |
| | |
| | | * @return ç»æ |
| | | */ |
| | | int deleteTbHospDataById(Long hospId); |
| | | |
| | | /** |
| | | * æ¹éçæå¹¶æ´æ°ææå»é¢çåè¯ |
| | | * ä¾å»é¢åæ¥æ¶è°ç¨ |
| | | * |
| | | * @return æ´æ°çå»é¢æ°é |
| | | */ |
| | | int generateAllHospitalKeywords(); |
| | | |
| | | /** |
| | | * 为å个å»é¢çæåè¯ |
| | | * |
| | | * @param tbHospData å»é¢æ°æ® |
| | | * @return çæçåè¯ |
| | | */ |
| | | String generateKeywordsForHospital(TbHospData tbHospData); |
| | | } |
| New file |
| | |
| | | package com.ruoyi.system.service; |
| | | |
| | | import java.util.Date; |
| | | import java.util.List; |
| | | import com.ruoyi.system.domain.VehicleAbnormalAlert; |
| | | |
| | | /** |
| | | * 车è¾å¼å¸¸åè¦Serviceæ¥å£ |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public interface IVehicleAbnormalAlertService { |
| | | /** |
| | | * æ¥è¯¢è½¦è¾å¼å¸¸åè¦ |
| | | * |
| | | * @param alertId 车è¾å¼å¸¸åè¦ä¸»é® |
| | | * @return 车è¾å¼å¸¸åè¦ |
| | | */ |
| | | public VehicleAbnormalAlert selectVehicleAbnormalAlertByAlertId(Long alertId); |
| | | |
| | | /** |
| | | * æ¥è¯¢è½¦è¾å¼å¸¸åè¦å表 |
| | | * |
| | | * @param vehicleAbnormalAlert 车è¾å¼å¸¸åè¦ |
| | | * @return 车è¾å¼å¸¸åè¦éå |
| | | */ |
| | | public List<VehicleAbnormalAlert> selectVehicleAbnormalAlertList(VehicleAbnormalAlert vehicleAbnormalAlert); |
| | | |
| | | /** |
| | | * æ°å¢è½¦è¾å¼å¸¸åè¦ |
| | | * |
| | | * @param vehicleAbnormalAlert 车è¾å¼å¸¸åè¦ |
| | | * @return ç»æ |
| | | */ |
| | | public int insertVehicleAbnormalAlert(VehicleAbnormalAlert vehicleAbnormalAlert); |
| | | |
| | | /** |
| | | * ä¿®æ¹è½¦è¾å¼å¸¸åè¦ |
| | | * |
| | | * @param vehicleAbnormalAlert 车è¾å¼å¸¸åè¦ |
| | | * @return ç»æ |
| | | */ |
| | | public int updateVehicleAbnormalAlert(VehicleAbnormalAlert vehicleAbnormalAlert); |
| | | |
| | | /** |
| | | * æ¹éå é¤è½¦è¾å¼å¸¸åè¦ |
| | | * |
| | | * @param alertIds éè¦å é¤ç车è¾å¼å¸¸åè¦ä¸»é®éå |
| | | * @return ç»æ |
| | | */ |
| | | public int deleteVehicleAbnormalAlertByAlertIds(Long[] alertIds); |
| | | |
| | | /** |
| | | * å é¤è½¦è¾å¼å¸¸åè¦ä¿¡æ¯ |
| | | * |
| | | * @param alertId 车è¾å¼å¸¸åè¦ä¸»é® |
| | | * @return ç»æ |
| | | */ |
| | | public int deleteVehicleAbnormalAlertByAlertId(Long alertId); |
| | | |
| | | /** |
| | | * å¤çåè¦ |
| | | * |
| | | * @param alertId åè¦ID |
| | | * @param handlerId å¤ç人ID |
| | | * @param handlerName å¤ç人å§å |
| | | * @param handleRemark å¤ç夿³¨ |
| | | * @return ç»æ |
| | | */ |
| | | public int handleAlert(Long alertId, Long handlerId, String handlerName, String handleRemark); |
| | | |
| | | /** |
| | | * æ¹éå¤çåè¦ |
| | | * |
| | | * @param alertIds åè¦IDå表 |
| | | * @param handlerId å¤ç人ID |
| | | * @param handlerName å¤ç人å§å |
| | | * @param handleRemark å¤ç夿³¨ |
| | | * @return ç»æ |
| | | */ |
| | | public int batchHandleAlert(Long[] alertIds, Long handlerId, String handlerName, String handleRemark); |
| | | |
| | | /** |
| | | * æ£æ¥å¹¶å建车è¾å¼å¸¸åè¦ |
| | | * |
| | | * @param vehicleId 车è¾ID |
| | | * @param vehicleNo 车çå· |
| | | * @param mileage è¿è¡å
¬éæ° |
| | | * @param startTime å¼å§æ¶é´ |
| | | * @param endTime ç»ææ¶é´ |
| | | * @param deptId é¨é¨ID |
| | | * @param deptName é¨é¨åç§° |
| | | * @return æ¯å¦å建æå |
| | | */ |
| | | public boolean checkAndCreateAlert(Long vehicleId, String vehicleNo, java.math.BigDecimal mileage, |
| | | Date startTime, Date endTime, Long deptId, String deptName); |
| | | } |
| New file |
| | |
| | | package com.ruoyi.system.service; |
| | | |
| | | import java.util.List; |
| | | import com.ruoyi.system.domain.VehicleAlertConfig; |
| | | |
| | | /** |
| | | * 车è¾åè¦é
ç½®Serviceæ¥å£ |
| | | * |
| | | * @author ruoyi |
| | | * @date 2026-01-12 |
| | | */ |
| | | public interface IVehicleAlertConfigService |
| | | { |
| | | /** |
| | | * æ¥è¯¢è½¦è¾åè¦é
ç½® |
| | | * |
| | | * @param configId 车è¾åè¦é
ç½®ä¸»é® |
| | | * @return 车è¾åè¦é
ç½® |
| | | */ |
| | | public VehicleAlertConfig selectVehicleAlertConfigByConfigId(Long configId); |
| | | |
| | | /** |
| | | * æ¥è¯¢è½¦è¾åè¦é
ç½®å表 |
| | | * |
| | | * @param vehicleAlertConfig 车è¾åè¦é
ç½® |
| | | * @return 车è¾åè¦é
ç½®éå |
| | | */ |
| | | public List<VehicleAlertConfig> selectVehicleAlertConfigList(VehicleAlertConfig vehicleAlertConfig); |
| | | |
| | | /** |
| | | * è·å车è¾çåè¦é
ç½®ï¼ä¼å
级ï¼è½¦è¾ > é¨é¨ > å
¨å±ï¼ |
| | | * |
| | | * @param vehicleId 车è¾ID |
| | | * @param deptId é¨é¨ID |
| | | * @return 车è¾åè¦é
ç½® |
| | | */ |
| | | public VehicleAlertConfig getConfigByVehicle(Long vehicleId, Long deptId); |
| | | |
| | | /** |
| | | * æ°å¢è½¦è¾åè¦é
ç½® |
| | | * |
| | | * @param vehicleAlertConfig 车è¾åè¦é
ç½® |
| | | * @return ç»æ |
| | | */ |
| | | public int insertVehicleAlertConfig(VehicleAlertConfig vehicleAlertConfig); |
| | | |
| | | /** |
| | | * ä¿®æ¹è½¦è¾åè¦é
ç½® |
| | | * |
| | | * @param vehicleAlertConfig 车è¾åè¦é
ç½® |
| | | * @return ç»æ |
| | | */ |
| | | public int updateVehicleAlertConfig(VehicleAlertConfig vehicleAlertConfig); |
| | | |
| | | /** |
| | | * æ¹éå é¤è½¦è¾åè¦é
ç½® |
| | | * |
| | | * @param configIds éè¦å é¤ç车è¾åè¦é
置主é®éå |
| | | * @return ç»æ |
| | | */ |
| | | public int deleteVehicleAlertConfigByConfigIds(Long[] configIds); |
| | | |
| | | /** |
| | | * å é¤è½¦è¾åè¦é
ç½®ä¿¡æ¯ |
| | | * |
| | | * @param configId 车è¾åè¦é
ç½®ä¸»é® |
| | | * @return ç»æ |
| | | */ |
| | | public int deleteVehicleAlertConfigByConfigId(Long configId); |
| | | } |
| | |
| | | public void cancelDispatchOrd(Long dispatchOrdID, Integer cancelReason, String cancelCReasonTxt) { |
| | | dispatchOrdMapper.updateDispatchOrdCancelReason(dispatchOrdID, cancelReason, cancelCReasonTxt); |
| | | } |
| | | |
| | | /** |
| | | * æ´æ°è°åº¦åå®é
å¼å§æ¶é´ |
| | | * |
| | | * @param dispatchOrdID è°åº¦åID |
| | | * @param actualDate å®é
å¼å§æ¶é´ |
| | | * @return å½±åè¡æ° |
| | | */ |
| | | @Override |
| | | public int updateDispatchOrdActualDate(Long dispatchOrdID, java.util.Date actualDate) { |
| | | return dispatchOrdMapper.updateDispatchOrdActualDate(dispatchOrdID, actualDate); |
| | | } |
| | | |
| | | |
| | | } |
| | |
| | | { |
| | | // æ´æ°å·²ææ°æ® |
| | | updateHospData(existingHosp, dto); |
| | | // çæåè¯ |
| | | String keywords = tbHospDataService.generateKeywordsForHospital(existingHosp); |
| | | existingHosp.setHospKeywords(keywords); |
| | | tbHospDataService.updateTbHospData(existingHosp); |
| | | updateCount++; |
| | | log.debug("æ´æ°å»é¢: {} (HospID={})", dto.getHospName(), dto.getHospId()); |
| | | log.debug("æ´æ°å»é¢: {} (HospID={}), åè¯: {}", dto.getHospName(), dto.getHospId(), keywords); |
| | | } |
| | | else |
| | | { |
| | | // æå
¥æ°æ°æ® |
| | | TbHospData newHosp = convertToTbHospData(dto); |
| | | // çæåè¯ |
| | | String keywords = tbHospDataService.generateKeywordsForHospital(newHosp); |
| | | newHosp.setHospKeywords(keywords); |
| | | tbHospDataService.insertTbHospData(newHosp); |
| | | insertCount++; |
| | | log.debug("æ°å¢å»é¢: {} (HospID={})", dto.getHospName(), dto.getHospId()); |
| | | log.debug("æ°å¢å»é¢: {} (HospID={}), åè¯: {}", dto.getHospName(), dto.getHospId(), keywords); |
| | | } |
| | | } |
| | | catch (Exception e) |
| | |
| | | private ITaskDispatchSyncService taskDispatchSyncService; |
| | | |
| | | |
| | | |
| | | @Override |
| | | public Integer updateDispatchActualTime(Long dispatchOrdId, Date actualTime) { |
| | | return dispatchOrdService.updateDispatchOrdActualDate(dispatchOrdId, actualTime); |
| | | } |
| | | |
| | | @Override |
| | | public Long syncEmergencyTaskToLegacy(Long taskId) { |
| | |
| | | createTaskVo.setDeptId(deptId); |
| | | |
| | | |
| | | int result = sysTaskService.insertTask(createTaskVo,serviceOrdID,dispatchOrdID, serviceOrdNo, taskCreatorId,createUserName, deptId, ServiceOrd_CC_Time, ServiceOrd_CC_Time); |
| | | Long taskId = sysTaskService.insertTask(createTaskVo,serviceOrdID,dispatchOrdID, serviceOrdNo, taskCreatorId,createUserName, deptId, ServiceOrd_CC_Time, ServiceOrd_CC_Time); |
| | | |
| | | if (result > 0) { |
| | | // log.info("转è¿å忥æå: ServiceOrdID={}, DispatchOrdID={}, å建çä»»å¡ID={}", serviceOrdID, dispatchOrdID, result); |
| | | if (taskId != null && taskId > 0) { |
| | | // log.info("转è¿å忥æå: ServiceOrdID={}, DispatchOrdID={}, å建çä»»å¡ID={}", serviceOrdID, dispatchOrdID, taskId); |
| | | |
| | | try { |
| | | notifyTransferOrderByWechat((long) result, serviceOrdID, dispatchOrdID, serviceOrdNo, ServiceOrd_CC_Time, dept, order); |
| | | notifyTransferOrderByWechat(taskId, serviceOrdID, dispatchOrdID, serviceOrdNo, ServiceOrd_CC_Time, dept, order); |
| | | } catch (Exception e) { |
| | | log.error("转è¿å忥æåååé微信éç¥å¤±è´¥: ServiceOrdID={}, DispatchOrdID={}", serviceOrdID, dispatchOrdID, e); |
| | | } |
| | |
| | | package com.ruoyi.system.service.impl; |
| | | |
| | | import com.ruoyi.common.config.WechatConfig; |
| | | import com.ruoyi.common.core.domain.entity.SysUser; |
| | | import com.ruoyi.common.utils.StringUtils; |
| | | import com.ruoyi.system.domain.QyWechatArticle; |
| | |
| | | @Autowired |
| | | private SysUserMapper userMapper; |
| | | |
| | | @Autowired |
| | | private WechatConfig wechatConfig; |
| | | /** |
| | | * åéä¼ä¸å¾®ä¿¡æ¶æ¯ |
| | | */ |
| | |
| | | } |
| | | |
| | | @Override |
| | | public boolean sendNotifyMessageWithDefaultAppId(Long userId, String title, String content, String businessUrl){ |
| | | String appId=wechatConfig.getAppId(); |
| | | return sendNotifyMessage(userId,title,content,appId,businessUrl); |
| | | } |
| | | @Override |
| | | public boolean sendNotifyMessage(Long userId, String title, String content, String appId, String businessUrl) { |
| | | try { |
| | | // æ£æ¥æå¡æ¯å¦å¯ç¨ |
| | |
| | | * @return ç»æ |
| | | */ |
| | | @Override |
| | | public int insertSysTask(TaskCreateVO createVO) { |
| | | public Long insertSysTask(TaskCreateVO createVO) { |
| | | // è·åå½åç¨æ·ååç¨æ·ID |
| | | String username = SecurityUtils.getUsername(); |
| | | Long userId = SecurityUtils.getUserId(); |
| | | // æ ¡éªç¨æ·IDæ¯å¦ä¸ºç©ºæä¸º0 |
| | | if(userId==null || userId==0){ |
| | | log.error("insertSysTask ç¨æ·ID为空 userName:{}",username); |
| | | return 0; |
| | | return 0L; |
| | | } |
| | | SysTask task = new SysTask(); |
| | | // å建æ°çä»»å¡å¯¹è±¡ |
| | |
| | | }).start(); |
| | | } |
| | | |
| | | return result; |
| | | return result > 0 ? task.getTaskId() : 0L; |
| | | } |
| | | |
| | | /** |
| | |
| | | * @return ç»æ |
| | | */ |
| | | @Override |
| | | public int insertTask(TaskCreateVO createVO,Long serviceOrderId,Long dispatchOrderId, String serviceOrdNo, Long userId,String userName, Long deptId, Date createTime, Date updateTime) { |
| | | public Long insertTask(TaskCreateVO createVO,Long serviceOrderId,Long dispatchOrderId, String serviceOrdNo, Long userId,String userName, Long deptId, Date createTime, Date updateTime) { |
| | | SysTask task = new SysTask(); |
| | | if(createVO.getTaskCode()!=null){ |
| | | task.setTaskCode(createVO.getTaskCode()); |
| | |
| | | this.sendEmeryTaskProcess(task, dispatchOrderId); |
| | | } |
| | | |
| | | return result; |
| | | return result > 0 ? task.getTaskId() : 0L; |
| | | } |
| | | |
| | | private void sendTaskAssigneeEvent(TaskCreateVO createVO,SysTask task,Long userId,String userName){ |
| | |
| | | cancelDispatch(emergency.getLegacyDispatchOrdId(), emergency.getCancelReason(), emergency.getCancelBy()); |
| | | } |
| | | } |
| | | |
| | | // 夿æ¯å¦éè¦æ´æ°å®é
å¼å§æ¶é´ï¼ä»å¾
å¤ç转å°å
¶ä»ç¶æï¼é¤åæ¶å¤ï¼ |
| | | if ( targetStatusCode != 10 && task.getActualStartTime() != null) { |
| | | try { |
| | | int rows = dispatchOrdService.updateDispatchOrdActualDate( |
| | | emergency.getLegacyDispatchOrdId(), |
| | | task.getActualStartTime()); |
| | | if (rows > 0) { |
| | | log.info("ãæ°æ¨æ§ãæ´æ°å®é
å¼å§æ¶é´æåï¼ä»»å¡ID: {}, DispatchOrdID: {}, å®é
å¼å§æ¶é´: {}", |
| | | taskId, emergency.getLegacyDispatchOrdId(), task.getActualStartTime()); |
| | | } else { |
| | | log.warn("ãæ°æ¨æ§ãæ´æ°å®é
å¼å§æ¶é´å¤±è´¥ï¼æªæ¾å°å¯¹åºè°åº¦åï¼DispatchOrdID: {}", |
| | | emergency.getLegacyDispatchOrdId()); |
| | | } |
| | | } catch (Exception e) { |
| | | log.error("ãæ°æ¨æ§ãæ´æ°å®é
å¼å§æ¶é´å¼å¸¸ï¼DispatchOrdID: {}", |
| | | emergency.getLegacyDispatchOrdId(), e); |
| | | // 䏿åºå¼å¸¸ï¼ç»§ç»æ§è¡ç¶ææ¨é |
| | | } |
| | | } |
| | | |
| | | // æ¨éç¶æå°æ§ç³»ç» |
| | | boolean result = updateLegacyTaskStatus(emergency.getLegacyDispatchOrdId(), targetStatusCode); |
| | | |
| | |
| | | |
| | | try { |
| | | int totalSuccessCount = 0; |
| | | int pageSize = 10; // æ¯é¡µ10æ¡ |
| | | int pageSize = 5; // æ¯é¡µ10æ¡ |
| | | int offset = 0; |
| | | |
| | | while (true) { |
| | |
| | | |
| | | // æ£æ¥ç¶ææ¯å¦åå |
| | | if (newStatus.getCode().equals(task.getTaskStatus())) { |
| | | log.debug("ä»»å¡ç¶ææªååï¼ä»»å¡ID: {}, å½åç¶æ: {}", taskId, newStatus.getInfo()); |
| | | log.debug("ååï¼ä»»å¡ID: {}, å½åç¶æ: {}", taskId, newStatus.getInfo()); |
| | | return true; |
| | | } |
| | | |
| | |
| | | package com.ruoyi.system.service.impl; |
| | | |
| | | import com.ruoyi.common.utils.HospitalTokenizerUtil; |
| | | import com.ruoyi.system.domain.TbHospData; |
| | | import com.ruoyi.system.mapper.TbHospDataMapper; |
| | | import com.ruoyi.system.service.ITbHospDataService; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | |
| | | @Service |
| | | public class TbHospDataServiceImpl implements ITbHospDataService |
| | | { |
| | | private static final Logger logger = LoggerFactory.getLogger(TbHospDataServiceImpl.class); |
| | | |
| | | @Autowired |
| | | private TbHospDataMapper tbHospDataMapper; |
| | | |
| | |
| | | { |
| | | return tbHospDataMapper.deleteTbHospDataById(hospId); |
| | | } |
| | | |
| | | /** |
| | | * æ¹éçæå¹¶æ´æ°ææå»é¢çåè¯ |
| | | * ä¾å»é¢åæ¥æ¶è°ç¨ |
| | | * |
| | | * @return æ´æ°çå»é¢æ°é |
| | | */ |
| | | @Override |
| | | public int generateAllHospitalKeywords() |
| | | { |
| | | logger.info("å¼å§æ¹éçæå»é¢åè¯..."); |
| | | |
| | | // æ¥è¯¢æææ£å¸¸ç¶æçå»é¢ |
| | | TbHospData query = new TbHospData(); |
| | | query.setStatus("0"); |
| | | List<TbHospData> hospitalList = tbHospDataMapper.selectTbHospDataList(query); |
| | | |
| | | logger.info("æ¥è¯¢å° {} 个å»é¢éè¦çæåè¯", hospitalList.size()); |
| | | |
| | | int updateCount = 0; |
| | | for (TbHospData hospital : hospitalList) { |
| | | try { |
| | | // çæåè¯ |
| | | String keywords = generateKeywordsForHospital(hospital); |
| | | hospital.setHospKeywords(keywords); |
| | | |
| | | // æ´æ°æ°æ®åº |
| | | int result = tbHospDataMapper.updateTbHospData(hospital); |
| | | if (result > 0) { |
| | | updateCount++; |
| | | } |
| | | } catch (Exception e) { |
| | | logger.error("çæå»é¢åè¯å¤±è´¥: hospId={}, hospName={}", |
| | | hospital.getHospId(), hospital.getHospName(), e); |
| | | } |
| | | } |
| | | |
| | | logger.info("å»é¢åè¯çæå®æï¼æ´æ°äº {} 个å»é¢", updateCount); |
| | | return updateCount; |
| | | } |
| | | |
| | | /** |
| | | * 为å个å»é¢çæåè¯ |
| | | * |
| | | * @param tbHospData å»é¢æ°æ® |
| | | * @return çæçåè¯ |
| | | */ |
| | | @Override |
| | | public String generateKeywordsForHospital(TbHospData tbHospData) |
| | | { |
| | | return HospitalTokenizerUtil.tokenize( |
| | | tbHospData.getHospName(), |
| | | tbHospData.getHospShort(), |
| | | tbHospData.getHopsProvince(), |
| | | tbHospData.getHopsCity(), |
| | | tbHospData.getHopsArea(), |
| | | tbHospData.getHospAddress() |
| | | ); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.ruoyi.system.service.impl; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.Date; |
| | | import java.util.List; |
| | | import com.ruoyi.common.utils.DateUtils; |
| | | import com.ruoyi.common.utils.StringUtils; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | import com.ruoyi.system.mapper.VehicleAbnormalAlertMapper; |
| | | import com.ruoyi.system.domain.VehicleAbnormalAlert; |
| | | import com.ruoyi.system.service.IVehicleAbnormalAlertService; |
| | | |
| | | /** |
| | | * 车è¾å¼å¸¸åè¦Serviceä¸å¡å±å¤ç |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | @Service |
| | | public class VehicleAbnormalAlertServiceImpl implements IVehicleAbnormalAlertService { |
| | | |
| | | @Autowired |
| | | private VehicleAbnormalAlertMapper vehicleAbnormalAlertMapper; |
| | | |
| | | /** |
| | | * æ¥è¯¢è½¦è¾å¼å¸¸åè¦ |
| | | * |
| | | * @param alertId 车è¾å¼å¸¸åè¦ä¸»é® |
| | | * @return 车è¾å¼å¸¸åè¦ |
| | | */ |
| | | @Override |
| | | public VehicleAbnormalAlert selectVehicleAbnormalAlertByAlertId(Long alertId) { |
| | | return vehicleAbnormalAlertMapper.selectVehicleAbnormalAlertByAlertId(alertId); |
| | | } |
| | | |
| | | /** |
| | | * æ¥è¯¢è½¦è¾å¼å¸¸åè¦å表 |
| | | * |
| | | * @param vehicleAbnormalAlert 车è¾å¼å¸¸åè¦ |
| | | * @return 车è¾å¼å¸¸åè¦ |
| | | */ |
| | | @Override |
| | | public List<VehicleAbnormalAlert> selectVehicleAbnormalAlertList(VehicleAbnormalAlert vehicleAbnormalAlert) { |
| | | return vehicleAbnormalAlertMapper.selectVehicleAbnormalAlertList(vehicleAbnormalAlert); |
| | | } |
| | | |
| | | /** |
| | | * æ°å¢è½¦è¾å¼å¸¸åè¦ |
| | | * |
| | | * @param vehicleAbnormalAlert 车è¾å¼å¸¸åè¦ |
| | | * @return ç»æ |
| | | */ |
| | | @Override |
| | | public int insertVehicleAbnormalAlert(VehicleAbnormalAlert vehicleAbnormalAlert) { |
| | | vehicleAbnormalAlert.setCreateTime(DateUtils.getNowDate()); |
| | | return vehicleAbnormalAlertMapper.insertVehicleAbnormalAlert(vehicleAbnormalAlert); |
| | | } |
| | | |
| | | /** |
| | | * ä¿®æ¹è½¦è¾å¼å¸¸åè¦ |
| | | * |
| | | * @param vehicleAbnormalAlert 车è¾å¼å¸¸åè¦ |
| | | * @return ç»æ |
| | | */ |
| | | @Override |
| | | public int updateVehicleAbnormalAlert(VehicleAbnormalAlert vehicleAbnormalAlert) { |
| | | vehicleAbnormalAlert.setUpdateTime(DateUtils.getNowDate()); |
| | | return vehicleAbnormalAlertMapper.updateVehicleAbnormalAlert(vehicleAbnormalAlert); |
| | | } |
| | | |
| | | /** |
| | | * æ¹éå é¤è½¦è¾å¼å¸¸åè¦ |
| | | * |
| | | * @param alertIds éè¦å é¤ç车è¾å¼å¸¸åè¦ä¸»é® |
| | | * @return ç»æ |
| | | */ |
| | | @Override |
| | | public int deleteVehicleAbnormalAlertByAlertIds(Long[] alertIds) { |
| | | return vehicleAbnormalAlertMapper.deleteVehicleAbnormalAlertByAlertIds(alertIds); |
| | | } |
| | | |
| | | /** |
| | | * å é¤è½¦è¾å¼å¸¸åè¦ä¿¡æ¯ |
| | | * |
| | | * @param alertId 车è¾å¼å¸¸åè¦ä¸»é® |
| | | * @return ç»æ |
| | | */ |
| | | @Override |
| | | public int deleteVehicleAbnormalAlertByAlertId(Long alertId) { |
| | | return vehicleAbnormalAlertMapper.deleteVehicleAbnormalAlertByAlertId(alertId); |
| | | } |
| | | |
| | | /** |
| | | * å¤çåè¦ |
| | | * |
| | | * @param alertId åè¦ID |
| | | * @param handlerId å¤ç人ID |
| | | * @param handlerName å¤ç人å§å |
| | | * @param handleRemark å¤ç夿³¨ |
| | | * @return ç»æ |
| | | */ |
| | | @Override |
| | | public int handleAlert(Long alertId, Long handlerId, String handlerName, String handleRemark) { |
| | | VehicleAbnormalAlert alert = new VehicleAbnormalAlert(); |
| | | alert.setAlertId(alertId); |
| | | alert.setStatus("1"); // å·²å¤ç |
| | | alert.setHandlerId(handlerId); |
| | | alert.setHandlerName(handlerName); |
| | | alert.setHandleTime(new Date()); |
| | | alert.setHandleRemark(handleRemark); |
| | | |
| | | return updateVehicleAbnormalAlert(alert); |
| | | } |
| | | |
| | | /** |
| | | * æ¹éå¤çåè¦ |
| | | * |
| | | * @param alertIds åè¦IDå表 |
| | | * @param handlerId å¤ç人ID |
| | | * @param handlerName å¤ç人å§å |
| | | * @param handleRemark å¤ç夿³¨ |
| | | * @return ç»æ |
| | | */ |
| | | @Override |
| | | public int batchHandleAlert(Long[] alertIds, Long handlerId, String handlerName, String handleRemark) { |
| | | return vehicleAbnormalAlertMapper.batchHandleAlert(alertIds, handlerId, handlerName, handleRemark); |
| | | } |
| | | |
| | | /** |
| | | * æ£æ¥å¹¶å建车è¾å¼å¸¸åè¦ |
| | | * |
| | | * @param vehicleId 车è¾ID |
| | | * @param vehicleNo 车çå· |
| | | * @param mileage è¿è¡å
¬éæ° |
| | | * @param startTime å¼å§æ¶é´ |
| | | * @param endTime ç»ææ¶é´ |
| | | * @param deptId é¨é¨ID |
| | | * @param deptName é¨é¨åç§° |
| | | * @return æ¯å¦å建æå |
| | | */ |
| | | @Override |
| | | public boolean checkAndCreateAlert(Long vehicleId, String vehicleNo, BigDecimal mileage, |
| | | Date startTime, Date endTime, Long deptId, String deptName) { |
| | | try { |
| | | // è·å彿¥åè¦æ¬¡æ° |
| | | Date today = DateUtils.parseDate(DateUtils.getDate()); |
| | | int todayCount = vehicleAbnormalAlertMapper.selectDailyAlertCount(vehicleId, today); |
| | | |
| | | // å建åè¦è®°å½ |
| | | VehicleAbnormalAlert alert = new VehicleAbnormalAlert(); |
| | | alert.setVehicleId(vehicleId); |
| | | alert.setVehicleNo(vehicleNo); |
| | | alert.setAlertDate(today); |
| | | alert.setAlertTime(new Date()); |
| | | alert.setMileage(mileage); |
| | | alert.setAlertType("NO_TASK_MILEAGE"); |
| | | alert.setAlertReason(String.format("车è¾å¨æ ä»»å¡ç¶æä¸è¿è¡äº %.2f å
Ž", mileage)); |
| | | alert.setStartTime(startTime); |
| | | alert.setEndTime(endTime); |
| | | alert.setAlertCount(todayCount + 1); |
| | | alert.setStatus("0"); // æªå¤ç |
| | | alert.setNotifyStatus("0"); // æªåé |
| | | alert.setDeptId(deptId); |
| | | alert.setDeptName(deptName); |
| | | |
| | | int result = insertVehicleAbnormalAlert(alert); |
| | | return result > 0; |
| | | |
| | | } catch (Exception e) { |
| | | throw new RuntimeException("å建åè¦è®°å½å¤±è´¥: " + e.getMessage(), e); |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | package com.ruoyi.system.service.impl; |
| | | |
| | | import java.util.List; |
| | | import com.ruoyi.common.utils.DateUtils; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | import com.ruoyi.system.mapper.VehicleAlertConfigMapper; |
| | | import com.ruoyi.system.domain.VehicleAlertConfig; |
| | | import com.ruoyi.system.service.IVehicleAlertConfigService; |
| | | |
| | | /** |
| | | * 车è¾åè¦é
ç½®Serviceä¸å¡å±å¤ç |
| | | * |
| | | * @author ruoyi |
| | | * @date 2026-01-12 |
| | | */ |
| | | @Service |
| | | public class VehicleAlertConfigServiceImpl implements IVehicleAlertConfigService |
| | | { |
| | | @Autowired |
| | | private VehicleAlertConfigMapper vehicleAlertConfigMapper; |
| | | |
| | | /** |
| | | * æ¥è¯¢è½¦è¾åè¦é
ç½® |
| | | * |
| | | * @param configId 车è¾åè¦é
ç½®ä¸»é® |
| | | * @return 车è¾åè¦é
ç½® |
| | | */ |
| | | @Override |
| | | public VehicleAlertConfig selectVehicleAlertConfigByConfigId(Long configId) |
| | | { |
| | | return vehicleAlertConfigMapper.selectVehicleAlertConfigByConfigId(configId); |
| | | } |
| | | |
| | | /** |
| | | * æ¥è¯¢è½¦è¾åè¦é
ç½®å表 |
| | | * |
| | | * @param vehicleAlertConfig 车è¾åè¦é
ç½® |
| | | * @return 车è¾åè¦é
ç½® |
| | | */ |
| | | @Override |
| | | public List<VehicleAlertConfig> selectVehicleAlertConfigList(VehicleAlertConfig vehicleAlertConfig) |
| | | { |
| | | return vehicleAlertConfigMapper.selectVehicleAlertConfigList(vehicleAlertConfig); |
| | | } |
| | | |
| | | /** |
| | | * è·å车è¾çåè¦é
ç½®ï¼ä¼å
级ï¼è½¦è¾ > é¨é¨ > å
¨å±ï¼ |
| | | * |
| | | * @param vehicleId 车è¾ID |
| | | * @param deptId é¨é¨ID |
| | | * @return 车è¾åè¦é
ç½® |
| | | */ |
| | | @Override |
| | | public VehicleAlertConfig getConfigByVehicle(Long vehicleId, Long deptId) |
| | | { |
| | | return vehicleAlertConfigMapper.selectConfigByVehicle(vehicleId, deptId); |
| | | } |
| | | |
| | | /** |
| | | * æ°å¢è½¦è¾åè¦é
ç½® |
| | | * |
| | | * @param vehicleAlertConfig 车è¾åè¦é
ç½® |
| | | * @return ç»æ |
| | | */ |
| | | @Override |
| | | public int insertVehicleAlertConfig(VehicleAlertConfig vehicleAlertConfig) |
| | | { |
| | | vehicleAlertConfig.setCreateTime(DateUtils.getNowDate()); |
| | | return vehicleAlertConfigMapper.insertVehicleAlertConfig(vehicleAlertConfig); |
| | | } |
| | | |
| | | /** |
| | | * ä¿®æ¹è½¦è¾åè¦é
ç½® |
| | | * |
| | | * @param vehicleAlertConfig 车è¾åè¦é
ç½® |
| | | * @return ç»æ |
| | | */ |
| | | @Override |
| | | public int updateVehicleAlertConfig(VehicleAlertConfig vehicleAlertConfig) |
| | | { |
| | | vehicleAlertConfig.setUpdateTime(DateUtils.getNowDate()); |
| | | return vehicleAlertConfigMapper.updateVehicleAlertConfig(vehicleAlertConfig); |
| | | } |
| | | |
| | | /** |
| | | * æ¹éå é¤è½¦è¾åè¦é
ç½® |
| | | * |
| | | * @param configIds éè¦å é¤ç车è¾åè¦é
ç½®ä¸»é® |
| | | * @return ç»æ |
| | | */ |
| | | @Override |
| | | public int deleteVehicleAlertConfigByConfigIds(Long[] configIds) |
| | | { |
| | | return vehicleAlertConfigMapper.deleteVehicleAlertConfigByConfigIds(configIds); |
| | | } |
| | | |
| | | /** |
| | | * å é¤è½¦è¾åè¦é
ç½®ä¿¡æ¯ |
| | | * |
| | | * @param configId 车è¾åè¦é
ç½®ä¸»é® |
| | | * @return ç»æ |
| | | */ |
| | | @Override |
| | | public int deleteVehicleAlertConfigByConfigId(Long configId) |
| | | { |
| | | return vehicleAlertConfigMapper.deleteVehicleAlertConfigByConfigId(configId); |
| | | } |
| | | } |
| | |
| | | } |
| | | } |
| | | |
| | | // æ¯æ¹æ¬¡ç»æåï¼ä¸»å¨å»ºè®®GC |
| | | // æ¯æ¹æ¬¡ç»æåï¼ä¸»å¨å»ºè®®GCï¼ä¸éè¦æ¾å¼æ¸
空å¼ç¨ï¼å±é¨åéä¼èªå¨éæ¾ï¼ |
| | | if (batchEnd < vehicleIds.size()) { |
| | | uncalculatedGps = null; // æ¾å¼æ¸
空å¼ç¨ |
| | | System.gc(); |
| | | logger.debug("è¡¥å¿è®¡ç®æ¹æ¬¡ {}-{} 宿ï¼å·²å»ºè®®JVMåæ¶å
å", batchStart + 1, batchEnd); |
| | | } |
| New file |
| | |
| | | package com.ruoyi.system.utils; |
| | | |
| | | import com.aliyun.ocr_api20210707.Client; |
| | | import com.aliyun.ocr_api20210707.models.RecognizeAllTextRequest; |
| | | import com.aliyun.ocr_api20210707.models.RecognizeAllTextResponse; |
| | | import com.aliyun.ocr_api20210707.models.RecognizeHandwritingRequest; |
| | | import com.aliyun.ocr_api20210707.models.RecognizeHandwritingResponse; |
| | | import com.aliyun.tea.TeaException; |
| | | import com.aliyun.teaopenapi.models.Config; |
| | | import com.aliyun.teautil.models.RuntimeOptions; |
| | | import com.alibaba.fastjson.JSON; |
| | | import com.alibaba.fastjson.JSONArray; |
| | | import com.alibaba.fastjson.JSONObject; |
| | | import com.ruoyi.system.config.OCRConfig; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.apache.commons.codec.binary.Base64; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import java.io.File; |
| | | import java.io.FileInputStream; |
| | | import java.io.IOException; |
| | | import java.util.Arrays; |
| | | import java.util.HashMap; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * é¿éäºOCRå·¥å
·ç±» - éç¨æåè¯å« |
| | | * æ¯æå¤ç§è¯å«ç±»åï¼éç¨æåãå票ã身份è¯ãæåä½ç |
| | | * ä½¿ç¨ ocr-api20210707 SDK |
| | | * |
| | | * 使ç¨ç¤ºä¾ï¼ |
| | | * // éç¨æåè¯å« |
| | | * JSONObject result = AliOCRUtil.recognizeGeneral("https://example.com/image.jpg"); |
| | | * |
| | | * // æ¬å°å¾çè¯å« |
| | | * File imageFile = new File("path/to/image.jpg"); |
| | | * JSONObject result = AliOCRUtil.recognizeGeneral(imageFile); |
| | | * |
| | | * // æåä½è¯å« |
| | | * JSONObject result = AliOCRUtil.recognizeHandwriting(imageFile); |
| | | * |
| | | * // æåå
³é®å段 |
| | | * Map<String, String> fields = AliOCRUtil.extractTargetFields(result); |
| | | */ |
| | | @Component |
| | | public class AliOCRUtil { |
| | | |
| | | private static final Logger log = LoggerFactory.getLogger(AliOCRUtil.class); |
| | | |
| | | private static OCRConfig staticOcrConfig; |
| | | |
| | | @Autowired |
| | | public void setOcrConfig(OCRConfig ocrConfig) { |
| | | AliOCRUtil.staticOcrConfig = ocrConfig; |
| | | } |
| | | |
| | | |
| | | // OCR API ç«¯ç¹ |
| | | private static final String ENDPOINT = "ocr-api.cn-hangzhou.aliyuncs.com"; |
| | | |
| | | /** |
| | | * å建é¿éäºOCR客æ·ç«¯ |
| | | * @return OCR客æ·ç«¯å®ä¾ |
| | | * @throws Exception åå»ºå¤±è´¥æ¶æåºå¼å¸¸ |
| | | */ |
| | | private static Client createClient() throws Exception { |
| | | Config config = new Config() |
| | | .setAccessKeyId(staticOcrConfig.getAccessKeyId()) |
| | | .setAccessKeySecret(staticOcrConfig.getAccessKeySecret()); |
| | | config.endpoint = ENDPOINT; |
| | | // è®¾ç½®è¿æ¥è¶
æ¶æ¶é´ |
| | | config.connectTimeout = 10000; // 10ç§ |
| | | config.readTimeout = 30000; // 30ç§ |
| | | return new Client(config); |
| | | } |
| | | |
| | | /** |
| | | * å建é¿éäºOCR客æ·ç«¯ï¼æ¯æå¤é¨ä¼ å
¥AK/SKï¼ |
| | | * @param accessKeyId é¿éäºAccessKey ID |
| | | * @param accessKeySecret é¿éäºAccessKey Secret |
| | | * @return OCR客æ·ç«¯å®ä¾ |
| | | * @throws Exception åå»ºå¤±è´¥æ¶æåºå¼å¸¸ |
| | | */ |
| | | public static Client createClient(String accessKeyId, String accessKeySecret) throws Exception { |
| | | Config config = new Config() |
| | | .setAccessKeyId(accessKeyId) |
| | | .setAccessKeySecret(accessKeySecret); |
| | | config.endpoint = ENDPOINT; |
| | | // è®¾ç½®è¿æ¥è¶
æ¶æ¶é´ |
| | | config.connectTimeout = 10000; // 10ç§ |
| | | config.readTimeout = 30000; // 30ç§ |
| | | return new Client(config); |
| | | } |
| | | |
| | | /** |
| | | * å°å¾çæä»¶è½¬ä¸ºBase64ç¼ç |
| | | * @param imageFile å¾çæä»¶ |
| | | * @return Base64ç¼ç å符串 |
| | | * @throws IOException 读åæä»¶å¤±è´¥ |
| | | */ |
| | | public static String imageToBase64(File imageFile) throws IOException { |
| | | try (FileInputStream fis = new FileInputStream(imageFile)) { |
| | | byte[] buffer = new byte[(int) imageFile.length()]; |
| | | fis.read(buffer); |
| | | return Base64.encodeBase64String(buffer); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * è°ç¨é¿éäºéç¨æåè¯å«OCRæ¥å£ï¼ä½¿ç¨å¾çURLï¼ |
| | | * @param imageUrl å¾çURLå°å |
| | | * @param type è¯å«ç±»åï¼å¦ï¼"Invoice"-å票, "IdCard"-身份è¯, "General"-éç¨, "HandWriting"-æåä½ï¼ |
| | | * @return OCRè¯å«ç»æï¼JSONæ ¼å¼ï¼ |
| | | */ |
| | | public static JSONObject recognizeTextByUrl(String imageUrl, String type) { |
| | | try { |
| | | Client client = createClient(); |
| | | RecognizeAllTextRequest request = new RecognizeAllTextRequest() |
| | | .setUrl(imageUrl) |
| | | .setType(type); |
| | | |
| | | RuntimeOptions runtime = new RuntimeOptions(); |
| | | RecognizeAllTextResponse response = client.recognizeAllTextWithOptions(request, runtime); |
| | | |
| | | // å°ååºè½¬ä¸ºJSONObject |
| | | String responseJson = com.aliyun.teautil.Common.toJSONString(response.body.data); |
| | | JSONObject result = JSON.parseObject(responseJson); |
| | | result.put("success", true); |
| | | |
| | | log.info("OCRè¯å«æåï¼ç±»å: {}, URL: {}", type, imageUrl); |
| | | return result; |
| | | |
| | | } catch (TeaException error) { |
| | | log.error("OCRè¯å«å¤±è´¥ï¼TeaExceptionï¼: {}", error.getMessage(), error); |
| | | JSONObject errorResult = new JSONObject(); |
| | | errorResult.put("success", false); |
| | | errorResult.put("error", error.getMessage()); |
| | | if (error.getData() != null) { |
| | | errorResult.put("recommend", error.getData().get("Recommend")); |
| | | } |
| | | return errorResult; |
| | | |
| | | } catch (Exception error) { |
| | | log.error("OCRè¯å«å¤±è´¥ï¼Exceptionï¼: {}", error.getMessage(), error); |
| | | |
| | | // æå»ºè¯¦ç»çéè¯¯ä¿¡æ¯ |
| | | JSONObject errorResult = new JSONObject(); |
| | | errorResult.put("success", false); |
| | | |
| | | // 夿é误类å |
| | | String errorMsg = error.getMessage(); |
| | | if (errorMsg != null && errorMsg.contains("ocr-api.cn-hangzhou.aliyuncs.com")) { |
| | | errorResult.put("error", "ç½ç»è¿æ¥å¤±è´¥ï¼æ æ³è®¿é®é¿éäºOCRæå¡"); |
| | | errorResult.put("detail", "è¯·æ£æ¥ï¼1) ç½ç»è¿æ¥æ¯å¦æ£å¸¸ 2) DNSè§£ææ¯å¦å¯ç¨ 3) é²ç«å¢è®¾ç½® 4) 代çé
ç½®"); |
| | | errorResult.put("endpoint", ENDPOINT); |
| | | } else if (error instanceof java.io.FileNotFoundException) { |
| | | errorResult.put("error", "æä»¶ä¸åå¨: " + imageUrl); |
| | | } else if (error instanceof java.io.IOException) { |
| | | errorResult.put("error", "æä»¶è¯»å失败: " + errorMsg); |
| | | } else { |
| | | errorResult.put("error", errorMsg != null ? errorMsg : "æªç¥é误"); |
| | | } |
| | | |
| | | return errorResult; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * è°ç¨é¿éäºéç¨æåè¯å«OCRæ¥å£ï¼ä½¿ç¨æ¬å°å¾çæä»¶ï¼ |
| | | * @param imageFile å¾çæä»¶ |
| | | * @param type è¯å«ç±»åï¼å¦ï¼"Invoice"-å票, "IdCard"-身份è¯, "General"-éç¨, "HandWriting"-æåä½ï¼ |
| | | * @return OCRè¯å«ç»æï¼JSONæ ¼å¼ï¼ |
| | | */ |
| | | public static JSONObject recognizeTextByFile(File imageFile, String type) { |
| | | FileInputStream fis = null; |
| | | try { |
| | | // ç´æ¥è¯»åå¾çæä»¶ä¸ºåèæµ |
| | | fis = new FileInputStream(imageFile); |
| | | |
| | | Client client = createClient(); |
| | | RecognizeAllTextRequest request = new RecognizeAllTextRequest() |
| | | .setBody(fis) // ç´æ¥ä¼ å
¥æä»¶æµ |
| | | .setType(type); |
| | | JSONObject result; |
| | | RuntimeOptions runtime = new RuntimeOptions(); |
| | | if(type.equals("HandWriting")){//å¤çæå{ |
| | | RecognizeHandwritingRequest handwritingRequest=new RecognizeHandwritingRequest(); |
| | | handwritingRequest.setBody(fis); |
| | | handwritingRequest.setNeedSortPage(true);//ä»ä¸å°ä¸ï¼ä»å·¦å°å³ |
| | | RecognizeHandwritingResponse response = client.recognizeHandwriting(handwritingRequest); |
| | | String responseJson = com.aliyun.teautil.Common.toJSONString(response.body.data); |
| | | result = JSON.parseObject(responseJson); |
| | | result.put("success", true); |
| | | }else { |
| | | RecognizeAllTextResponse response = client.recognizeAllTextWithOptions(request, runtime); |
| | | |
| | | // å°ååºè½¬ä¸ºJSONObject |
| | | String responseJson = com.aliyun.teautil.Common.toJSONString(response.body.data); |
| | | result = JSON.parseObject(responseJson); |
| | | result.put("success", true); |
| | | } |
| | | log.info("OCRè¯å«æåï¼ç±»å: {}, æä»¶: {} ç»æ:{}", type, imageFile.getName(),result); |
| | | return result; |
| | | |
| | | } catch (TeaException error) { |
| | | log.error("OCRè¯å«å¤±è´¥ï¼TeaExceptionï¼: {}", error.getMessage(), error); |
| | | JSONObject errorResult = new JSONObject(); |
| | | errorResult.put("success", false); |
| | | errorResult.put("error", error.getMessage()); |
| | | if (error.getData() != null) { |
| | | errorResult.put("recommend", error.getData().get("Recommend")); |
| | | } |
| | | return errorResult; |
| | | |
| | | } catch (Exception error) { |
| | | log.error("OCRè¯å«å¤±è´¥ï¼Exceptionï¼: {}", error.getMessage(), error); |
| | | |
| | | // æå»ºè¯¦ç»çéè¯¯ä¿¡æ¯ |
| | | JSONObject errorResult = new JSONObject(); |
| | | errorResult.put("success", false); |
| | | |
| | | // 夿é误类å |
| | | String errorMsg = error.getMessage(); |
| | | if (errorMsg != null && errorMsg.contains("ocr-api.cn-hangzhou.aliyuncs.com")) { |
| | | errorResult.put("error", "ç½ç»è¿æ¥å¤±è´¥ï¼æ æ³è®¿é®é¿éäºOCRæå¡"); |
| | | errorResult.put("detail", "è¯·æ£æ¥ï¼1) ç½ç»è¿æ¥æ¯å¦æ£å¸¸ 2) DNSè§£ææ¯å¦å¯ç¨ 3) é²ç«å¢è®¾ç½® 4) 代çé
ç½®"); |
| | | errorResult.put("endpoint", ENDPOINT); |
| | | } else if (error instanceof java.io.FileNotFoundException) { |
| | | errorResult.put("error", "æä»¶ä¸åå¨: " + imageFile.getAbsolutePath()); |
| | | } else if (error instanceof java.io.IOException) { |
| | | errorResult.put("error", "æä»¶è¯»å失败: " + errorMsg); |
| | | } else { |
| | | errorResult.put("error", errorMsg != null ? errorMsg : "æªç¥é误"); |
| | | } |
| | | |
| | | return errorResult; |
| | | } finally { |
| | | // å
³éæä»¶æµ |
| | | if (fis != null) { |
| | | try { |
| | | fis.close(); |
| | | } catch (IOException e) { |
| | | log.error("å
³éæä»¶æµå¤±è´¥", e); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * è¯å«ç±»åæä¸¾ |
| | | */ |
| | | public enum OcrType { |
| | | GENERAL("General", "éç¨æåè¯å«"), |
| | | INVOICE("Invoice", "å票è¯å«"), |
| | | IDCARD("IdCard", "身份è¯è¯å«"), |
| | | HANDWRITING("HandWriting", "æåä½è¯å«"); |
| | | |
| | | private final String code; |
| | | private final String desc; |
| | | |
| | | OcrType(String code, String desc) { |
| | | this.code = code; |
| | | this.desc = desc; |
| | | } |
| | | |
| | | public String getCode() { |
| | | return code; |
| | | } |
| | | |
| | | public String getDesc() { |
| | | return desc; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * éç¨æåè¯å« - æåä½è¯å« |
| | | * @param imageUrl å¾çURL |
| | | * @return OCRè¯å«ç»æ |
| | | */ |
| | | public static JSONObject recognizeHandwriting(String imageUrl) { |
| | | return recognizeTextByUrl(imageUrl, "HandWriting"); |
| | | } |
| | | |
| | | /** |
| | | * éç¨æåè¯å« - æåä½è¯å« - æ¬å°æä»¶ |
| | | * @param imageFile å¾çæä»¶ |
| | | * @return OCRè¯å«ç»æ |
| | | */ |
| | | public static JSONObject recognizeHandwriting(File imageFile) { |
| | | return recognizeTextByFile(imageFile, "HandWriting"); |
| | | } |
| | | |
| | | /** |
| | | * éç¨æåè¯å« |
| | | * @param imageUrl å¾çURL |
| | | * @return OCRè¯å«ç»æ |
| | | */ |
| | | public static JSONObject recognizeGeneral(String imageUrl) { |
| | | return recognizeTextByUrl(imageUrl, "General"); |
| | | } |
| | | |
| | | /** |
| | | * éç¨æåè¯å« - æ¬å°æä»¶ |
| | | * @param imageFile å¾çæä»¶ |
| | | * @return OCRè¯å«ç»æ |
| | | */ |
| | | public static JSONObject recognizeGeneral(File imageFile) { |
| | | return recognizeTextByFile(imageFile, "General"); |
| | | } |
| | | |
| | | /** |
| | | * è¯å«éç¨ç¥¨æ®ï¼åç¥¨ãæ¶æ®çï¼ |
| | | * @param imageUrl å¾çURL |
| | | * @return OCRè¯å«ç»æ |
| | | */ |
| | | public static JSONObject recognizeInvoice(String imageUrl) { |
| | | return recognizeTextByUrl(imageUrl, "Invoice"); |
| | | } |
| | | |
| | | /** |
| | | * è¯å«éç¨ç¥¨æ®ï¼åç¥¨ãæ¶æ®çï¼- æ¬å°æä»¶ |
| | | * @param imageFile å¾çæä»¶ |
| | | * @return OCRè¯å«ç»æ |
| | | */ |
| | | public static JSONObject recognizeInvoice(File imageFile) { |
| | | return recognizeTextByFile(imageFile, "Invoice"); |
| | | } |
| | | |
| | | /** |
| | | * è¯å«èº«ä»½è¯ |
| | | * @param imageUrl å¾çURL |
| | | * @return OCRè¯å«ç»æ |
| | | */ |
| | | public static JSONObject recognizeIdCard(String imageUrl) { |
| | | return recognizeTextByUrl(imageUrl, "IdCard"); |
| | | } |
| | | |
| | | /** |
| | | * è¯å«èº«ä»½è¯ - æ¬å°æä»¶ |
| | | * @param imageFile å¾çæä»¶ |
| | | * @return OCRè¯å«ç»æ |
| | | */ |
| | | public static JSONObject recognizeIdCard(File imageFile) { |
| | | return recognizeTextByFile(imageFile, "IdCard"); |
| | | } |
| | | |
| | | /** |
| | | * ä»OCRç»æä¸æåç®æ åæ®µï¼éé¢ãæ¥æã夿³¨çï¼ |
| | | * @param ocrResult OCRè¯å«çåå§ç»æ |
| | | * @return æååçç®æ åæ®µ |
| | | */ |
| | | public static Map<String, String> extractTargetFields(JSONObject ocrResult) { |
| | | Map<String, String> extracted = new HashMap<>(); |
| | | |
| | | // æ ¡éªOCRç»ææ¯å¦ææ |
| | | if (!ocrResult.containsKey("success") || !ocrResult.getBooleanValue("success")) { |
| | | extracted.put("error", ocrResult.getString("error")); |
| | | return extracted; |
| | | } |
| | | |
| | | // è·åè¯å«çæåå
容 |
| | | if (!ocrResult.containsKey("content")) { |
| | | extracted.put("error", "OCRè¯å«ç»æä¸ºç©º"); |
| | | return extracted; |
| | | } |
| | | |
| | | String content = ocrResult.getString("content"); |
| | | |
| | | // 妿æç»æåçprism_wordsInfoåæ®µï¼ä¼å
ä½¿ç¨ |
| | | if (ocrResult.containsKey("prism_wordsInfo")) { |
| | | JSONArray wordsInfo = ocrResult.getJSONArray("prism_wordsInfo"); |
| | | |
| | | // æåéé¢ï¼å¹é
å
å«"éé¢""å计""Â¥"çåæ®µï¼ |
| | | for (int i = 0; i < wordsInfo.size(); i++) { |
| | | JSONObject word = wordsInfo.getJSONObject(i); |
| | | String text = word.getString("word"); |
| | | if (text.contains("éé¢") || text.contains("å计") || text.contains("Â¥")) { |
| | | extracted.put("totalAmount", text); |
| | | break; |
| | | } |
| | | } |
| | | |
| | | // æåæ¥æ |
| | | for (int i = 0; i < wordsInfo.size(); i++) { |
| | | JSONObject word = wordsInfo.getJSONObject(i); |
| | | String text = word.getString("word"); |
| | | if (text.contains("æ¥æ") || text.matches(".*\\d{4}[-/å¹´]\\d{1,2}[-/æ]\\d{1,2}.*")) { |
| | | extracted.put("date", text); |
| | | break; |
| | | } |
| | | } |
| | | |
| | | // æå夿³¨ |
| | | for (int i = 0; i < wordsInfo.size(); i++) { |
| | | JSONObject word = wordsInfo.getJSONObject(i); |
| | | String text = word.getString("word"); |
| | | if (text.contains("夿³¨")) { |
| | | extracted.put("remark", text); |
| | | break; |
| | | } |
| | | } |
| | | } else { |
| | | // ä½¿ç¨æ´ä½ææ¬å
容è¿è¡ç®åæå |
| | | extracted.put("fullText", content); |
| | | } |
| | | |
| | | return extracted; |
| | | } |
| | | |
| | | /** |
| | | * 使ç¨èªå®ä¹AccessKeyè¿è¡OCRè¯å« |
| | | * @param imageUrl å¾çURL |
| | | * @param type è¯å«ç±»å |
| | | * @param accessKeyId AccessKey ID |
| | | * @param accessKeySecret AccessKey Secret |
| | | * @return OCRè¯å«ç»æ |
| | | */ |
| | | public static JSONObject recognizeTextWithCredentials(String imageUrl, String type, |
| | | String accessKeyId, String accessKeySecret) { |
| | | try { |
| | | Client client = createClient(accessKeyId, accessKeySecret); |
| | | RecognizeAllTextRequest request = new RecognizeAllTextRequest() |
| | | .setUrl(imageUrl) |
| | | .setType(type); |
| | | |
| | | RuntimeOptions runtime = new RuntimeOptions(); |
| | | RecognizeAllTextResponse response = client.recognizeAllTextWithOptions(request, runtime); |
| | | |
| | | String responseJson = com.aliyun.teautil.Common.toJSONString(response.body.data); |
| | | JSONObject result = JSON.parseObject(responseJson); |
| | | result.put("success", true); |
| | | |
| | | log.info("OCRè¯å«æåï¼èªå®ä¹AKï¼ï¼ç±»å: {}", type); |
| | | return result; |
| | | |
| | | } catch (TeaException error) { |
| | | log.error("OCRè¯å«å¤±è´¥ï¼TeaExceptionï¼: {}", error.getMessage(), error); |
| | | JSONObject errorResult = new JSONObject(); |
| | | errorResult.put("success", false); |
| | | errorResult.put("error", error.getMessage()); |
| | | if (error.getData() != null) { |
| | | errorResult.put("recommend", error.getData().get("Recommend")); |
| | | } |
| | | return errorResult; |
| | | |
| | | } catch (Exception error) { |
| | | log.error("OCRè¯å«å¤±è´¥ï¼Exceptionï¼: {}", error.getMessage(), error); |
| | | JSONObject errorResult = new JSONObject(); |
| | | errorResult.put("success", false); |
| | | errorResult.put("error", error.getMessage()); |
| | | return errorResult; |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | package com.ruoyi.system.utils; |
| | | |
| | | import com.alibaba.fastjson.JSON; |
| | | import com.alibaba.fastjson.JSONArray; |
| | | import com.alibaba.fastjson.JSONObject; |
| | | import com.baidu.aip.ocr.AipOcr; |
| | | import com.ruoyi.system.config.BaiduOCRConfig; |
| | | |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import java.io.File; |
| | | import java.util.HashMap; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * ç¾åº¦OCRå·¥å
·ç±» |
| | | * 使ç¨ç¾åº¦AI弿¾å¹³å°çOCRæå¡è¿è¡æåè¯å« |
| | | * æ¯æéç¨æåè¯å«ãæåä½è¯å«çå¤ç§è¯å«ç±»å |
| | | * |
| | | * 使ç¨ç¤ºä¾ï¼ |
| | | * // éç¨æåè¯å« |
| | | * JSONObject result = BaiduOCRUtil.generalRecognize("path/to/image.jpg"); |
| | | * |
| | | * // æåä½è¯å« |
| | | * JSONObject result = BaiduOCRUtil.handwritingRecognize("path/to/image.jpg"); |
| | | */ |
| | | @Component |
| | | @Slf4j |
| | | public class BaiduOCRUtil { |
| | | |
| | | |
| | | |
| | | private static BaiduOCRConfig staticBaiduOcrConfig; |
| | | |
| | | @Autowired |
| | | public void setBaiduOcrConfig(BaiduOCRConfig baiduOcrConfig) { |
| | | BaiduOCRUtil.staticBaiduOcrConfig = baiduOcrConfig; |
| | | } |
| | | |
| | | /** |
| | | * è·åç¾åº¦OCR客æ·ç«¯å®ä¾ |
| | | * @return AipOcr客æ·ç«¯å®ä¾ |
| | | */ |
| | | private static AipOcr getClient() { |
| | | AipOcr client = new AipOcr(staticBaiduOcrConfig.getAppId(), |
| | | staticBaiduOcrConfig.getApiKey(), |
| | | staticBaiduOcrConfig.getSecretKey()); |
| | | |
| | | // è®¾ç½®è¿æ¥è¶
æ¶æ¶é´åsocketè¶
æ¶æ¶é´ |
| | | client.setConnectionTimeoutInMillis(2000); |
| | | client.setSocketTimeoutInMillis(60000); |
| | | |
| | | return client; |
| | | } |
| | | |
| | | /** |
| | | * éç¨æåè¯å«ï¼å¾çè·¯å¾ï¼ |
| | | * @param imagePath å¾çè·¯å¾ |
| | | * @return è¯å«ç»æ |
| | | */ |
| | | public static JSONObject generalRecognize(String imagePath) { |
| | | try { |
| | | AipOcr client = getClient(); |
| | | |
| | | // åæ°ä¸ºå¾çè·¯å¾ |
| | | HashMap<String, String> options = new HashMap<String, String>(); |
| | | options.put("language_type", "CHN_ENG"); // è¯å«è¯è¨ç±»å |
| | | options.put("detect_direction", "true"); // æ¯å¦æ£æµå¾åæå |
| | | options.put("detect_language", "true"); // æ¯å¦æ£æµè¯è¨ |
| | | options.put("probability", "true"); // æ¯å¦è¿åè¯å«ç»æä¸æ¯ä¸è¡ç置信度 |
| | | |
| | | org.json.JSONObject res = client.basicGeneral(imagePath, options); |
| | | log.info("ç¾åº¦OCRéç¨æåè¯å«æåï¼å¾çè·¯å¾: {}", imagePath); |
| | | |
| | | JSONObject result = new JSONObject(); |
| | | result.put("success", true); |
| | | result.put("data", JSON.parseObject(res.toString())); |
| | | result.put("content", extractContentFromBaiduResult(JSON.parseObject(res.toString()))); |
| | | |
| | | return result; |
| | | |
| | | } catch (Exception e) { |
| | | log.error("ç¾åº¦OCRéç¨æåè¯å«å¤±è´¥: {}", e.getMessage(), e); |
| | | |
| | | JSONObject errorResult = new JSONObject(); |
| | | errorResult.put("success", false); |
| | | errorResult.put("error", e.getMessage()); |
| | | |
| | | return errorResult; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * éç¨æåè¯å«ï¼æä»¶å¯¹è±¡ï¼ |
| | | * @param imageFile å¾çæä»¶å¯¹è±¡ |
| | | * @return è¯å«ç»æ |
| | | */ |
| | | public static JSONObject generalRecognize(File imageFile) { |
| | | try { |
| | | AipOcr client = getClient(); |
| | | |
| | | // åæ°ä¸ºå¾çæä»¶ |
| | | HashMap<String, String> options = new HashMap<String, String>(); |
| | | options.put("language_type", "CHN_ENG"); |
| | | options.put("detect_direction", "true"); |
| | | options.put("detect_language", "true"); |
| | | options.put("probability", "true"); |
| | | |
| | | // 读åæä»¶åèæ°ç»æä½¿ç¨æä»¶è·¯å¾ |
| | | org.json.JSONObject res = client.basicGeneral(imageFile.getAbsolutePath(), options); |
| | | log.info("ç¾åº¦OCRéç¨æåè¯å«æåï¼æä»¶å: {}", imageFile.getName()); |
| | | |
| | | JSONObject result = new JSONObject(); |
| | | result.put("success", true); |
| | | result.put("data", JSON.parseObject(res.toString())); |
| | | result.put("content", extractContentFromBaiduResult(JSON.parseObject(res.toString()))); |
| | | |
| | | return result; |
| | | |
| | | } catch (Exception e) { |
| | | log.error("ç¾åº¦OCRéç¨æåè¯å«å¤±è´¥: {}", e.getMessage(), e); |
| | | |
| | | JSONObject errorResult = new JSONObject(); |
| | | errorResult.put("success", false); |
| | | errorResult.put("error", e.getMessage()); |
| | | |
| | | return errorResult; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * éç¨æåè¯å«ï¼å¾çåèæ°ç»ï¼ |
| | | * @param imageBytes å¾çåèæ°ç» |
| | | * @return è¯å«ç»æ |
| | | */ |
| | | public static JSONObject generalRecognize(byte[] imageBytes) { |
| | | try { |
| | | AipOcr client = getClient(); |
| | | |
| | | HashMap<String, String> options = new HashMap<String, String>(); |
| | | options.put("language_type", "CHN_ENG"); |
| | | options.put("detect_direction", "true"); |
| | | options.put("detect_language", "true"); |
| | | options.put("probability", "true"); |
| | | |
| | | org.json.JSONObject res = client.basicGeneral(imageBytes, options); |
| | | log.info("ç¾åº¦OCRéç¨æåè¯å«æåï¼åèæ°ç»é¿åº¦: {}", imageBytes.length); |
| | | |
| | | JSONObject result = new JSONObject(); |
| | | result.put("success", true); |
| | | result.put("data", JSON.parseObject(res.toString())); |
| | | result.put("content", extractContentFromBaiduResult(JSON.parseObject(res.toString()))); |
| | | |
| | | return result; |
| | | |
| | | } catch (Exception e) { |
| | | log.error("ç¾åº¦OCRéç¨æåè¯å«å¤±è´¥: {}", e.getMessage(), e); |
| | | |
| | | JSONObject errorResult = new JSONObject(); |
| | | errorResult.put("success", false); |
| | | errorResult.put("error", e.getMessage()); |
| | | |
| | | return errorResult; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * é«ç²¾åº¦æåè¯å« |
| | | * @param imagePath å¾çè·¯å¾ |
| | | * @return è¯å«ç»æ |
| | | */ |
| | | public static JSONObject accurateRecognize(String imagePath) { |
| | | try { |
| | | AipOcr client = getClient(); |
| | | |
| | | HashMap<String, String> options = new HashMap<String, String>(); |
| | | options.put("recognize_granularity", "big"); // æ¯å¦å®ä½åå符ä½ç½® |
| | | options.put("language_type", "CHN_ENG"); |
| | | options.put("detect_direction", "true"); |
| | | options.put("detect_language", "true"); |
| | | options.put("vertexes_location", "true"); // æ¯å¦è¿åæå夿¥å¤è¾¹å½¢é¡¶ç¹ä½ç½® |
| | | options.put("probability", "true"); |
| | | |
| | | org.json.JSONObject res = client.accurateGeneral(imagePath, options); |
| | | log.info("ç¾åº¦OCRé«ç²¾åº¦æåè¯å«æåï¼å¾çè·¯å¾: {}", imagePath); |
| | | |
| | | JSONObject result = new JSONObject(); |
| | | result.put("success", true); |
| | | result.put("data", JSON.parseObject(res.toString())); |
| | | result.put("content", extractContentFromBaiduResult(JSON.parseObject(res.toString()))); |
| | | |
| | | return result; |
| | | |
| | | } catch (Exception e) { |
| | | log.error("ç¾åº¦OCRé«ç²¾åº¦æåè¯å«å¤±è´¥: {}", e.getMessage(), e); |
| | | |
| | | JSONObject errorResult = new JSONObject(); |
| | | errorResult.put("success", false); |
| | | errorResult.put("error", e.getMessage()); |
| | | |
| | | return errorResult; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * æåä½è¯å« |
| | | * @param imagePath å¾çè·¯å¾ |
| | | * @return è¯å«ç»æ |
| | | */ |
| | | public static JSONObject handwritingRecognize(String imagePath) { |
| | | try { |
| | | AipOcr client = getClient(); |
| | | |
| | | // æåä½è¯å«åæ° |
| | | HashMap<String, String> options = new HashMap<String, String>(); |
| | | options.put("language_type", "CHN_ENG"); |
| | | |
| | | org.json.JSONObject res = client.handwriting(imagePath, options); |
| | | log.info("ç¾åº¦OCRæåä½è¯å«æåï¼å¾çè·¯å¾: {}", imagePath); |
| | | |
| | | JSONObject result = new JSONObject(); |
| | | result.put("success", true); |
| | | result.put("data", JSON.parseObject(res.toString())); |
| | | result.put("content", extractContentFromBaiduResult(JSON.parseObject(res.toString()))); |
| | | |
| | | return result; |
| | | |
| | | } catch (Exception e) { |
| | | log.error("ç¾åº¦OCRæåä½è¯å«å¤±è´¥: {}", e.getMessage(), e); |
| | | |
| | | JSONObject errorResult = new JSONObject(); |
| | | errorResult.put("success", false); |
| | | errorResult.put("error", e.getMessage()); |
| | | |
| | | return errorResult; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * æåä½è¯å«ï¼æä»¶å¯¹è±¡ï¼ |
| | | * @param imageFile å¾çæä»¶å¯¹è±¡ |
| | | * @return è¯å«ç»æ |
| | | */ |
| | | public static JSONObject handwritingRecognize(File imageFile) { |
| | | try { |
| | | AipOcr client = getClient(); |
| | | |
| | | HashMap<String, String> options = new HashMap<String, String>(); |
| | | options.put("language_type", "CHN_ENG"); |
| | | |
| | | org.json.JSONObject res = client.handwriting(imageFile.getAbsolutePath(), options); |
| | | log.info("ç¾åº¦OCRæåä½è¯å«æåï¼æä»¶å: {}", imageFile.getName()); |
| | | |
| | | JSONObject result = new JSONObject(); |
| | | result.put("success", true); |
| | | result.put("data", JSON.parseObject(res.toString())); |
| | | result.put("content", extractContentFromBaiduResult(JSON.parseObject(res.toString()))); |
| | | |
| | | return result; |
| | | |
| | | } catch (Exception e) { |
| | | log.error("ç¾åº¦OCRæåä½è¯å«å¤±è´¥: {}", e.getMessage(), e); |
| | | |
| | | JSONObject errorResult = new JSONObject(); |
| | | errorResult.put("success", false); |
| | | errorResult.put("error", e.getMessage()); |
| | | |
| | | return errorResult; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 身份è¯è¯å« |
| | | * @param imagePath å¾çè·¯å¾ |
| | | * @param isFront true为æ£é¢ï¼false为åé¢ |
| | | * @return è¯å«ç»æ |
| | | */ |
| | | public static JSONObject idCardRecognize(String imagePath, boolean isFront) { |
| | | try { |
| | | AipOcr client = getClient(); |
| | | |
| | | HashMap<String, String> options = new HashMap<String, String>(); |
| | | String idCardSide = isFront ? "front" : "back"; |
| | | |
| | | org.json.JSONObject res = client.idcard(imagePath, idCardSide, options); |
| | | log.info("ç¾åº¦OCR身份è¯è¯å«æåï¼å¾çè·¯å¾: {}ï¼æ¹å: {}", imagePath, idCardSide); |
| | | |
| | | JSONObject result = new JSONObject(); |
| | | result.put("success", true); |
| | | result.put("data", JSON.parseObject(res.toString())); |
| | | |
| | | return result; |
| | | |
| | | } catch (Exception e) { |
| | | log.error("ç¾åº¦OCR身份è¯è¯å«å¤±è´¥: {}", e.getMessage(), e); |
| | | |
| | | JSONObject errorResult = new JSONObject(); |
| | | errorResult.put("success", false); |
| | | errorResult.put("error", e.getMessage()); |
| | | |
| | | return errorResult; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * é¶è¡å¡è¯å« |
| | | * @param imagePath å¾çè·¯å¾ |
| | | * @return è¯å«ç»æ |
| | | */ |
| | | public static JSONObject bankCardRecognize(String imagePath) { |
| | | try { |
| | | AipOcr client = getClient(); |
| | | |
| | | org.json.JSONObject res = client.bankcard(imagePath, new HashMap<String, String>()); |
| | | log.info("ç¾åº¦OCRé¶è¡å¡è¯å«æåï¼å¾çè·¯å¾: {}", imagePath); |
| | | |
| | | JSONObject result = new JSONObject(); |
| | | result.put("success", true); |
| | | result.put("data", JSON.parseObject(res.toString())); |
| | | |
| | | return result; |
| | | |
| | | } catch (Exception e) { |
| | | log.error("ç¾åº¦OCRé¶è¡å¡è¯å«å¤±è´¥: {}", e.getMessage(), e); |
| | | |
| | | JSONObject errorResult = new JSONObject(); |
| | | errorResult.put("success", false); |
| | | errorResult.put("error", e.getMessage()); |
| | | |
| | | return errorResult; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * è¥ä¸æ§ç
§è¯å« |
| | | * @param imagePath å¾çè·¯å¾ |
| | | * @return è¯å«ç»æ |
| | | */ |
| | | public static JSONObject businessLicenseRecognize(String imagePath) { |
| | | try { |
| | | AipOcr client = getClient(); |
| | | |
| | | HashMap<String, String> options = new HashMap<String, String>(); |
| | | |
| | | org.json.JSONObject res = client.businessLicense(imagePath, options); |
| | | log.info("ç¾åº¦OCRè¥ä¸æ§ç
§è¯å«æåï¼å¾çè·¯å¾: {}", imagePath); |
| | | |
| | | JSONObject result = new JSONObject(); |
| | | result.put("success", true); |
| | | result.put("data", JSON.parseObject(res.toString())); |
| | | |
| | | return result; |
| | | |
| | | } catch (Exception e) { |
| | | log.error("ç¾åº¦OCRè¥ä¸æ§ç
§è¯å«å¤±è´¥: {}", e.getMessage(), e); |
| | | |
| | | JSONObject errorResult = new JSONObject(); |
| | | errorResult.put("success", false); |
| | | errorResult.put("error", e.getMessage()); |
| | | |
| | | return errorResult; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * ä»ç¾åº¦OCRç»æä¸æåææ¬å
容 |
| | | * @param result ç¾åº¦OCRè¿åçç»æï¼fastjsonæ ¼å¼ï¼ |
| | | * @return æåçææ¬å
容 |
| | | */ |
| | | private static String extractContentFromBaiduResult(JSONObject result) { |
| | | StringBuilder content = new StringBuilder(); |
| | | |
| | | if (result.containsKey("words_result") && result.getJSONArray("words_result") != null) { |
| | | JSONArray wordsResult = result.getJSONArray("words_result"); |
| | | for (int i = 0; i < wordsResult.size(); i++) { |
| | | JSONObject wordResult = wordsResult.getJSONObject(i); |
| | | if (wordResult.containsKey("words")) { |
| | | content.append(wordResult.getString("words")).append("\n"); |
| | | } |
| | | } |
| | | } |
| | | |
| | | return content.toString().trim(); |
| | | } |
| | | |
| | | /** |
| | | * ä»è¯å«ç»æä¸æåç®æ åæ®µï¼éé¢ãæ¥æã夿³¨çï¼ |
| | | * @param ocrResult OCRè¯å«çåå§ç»æ |
| | | * @return æååçç®æ åæ®µ |
| | | */ |
| | | public static java.util.Map<String, String> extractTargetFields(JSONObject ocrResult) { |
| | | java.util.Map<String, String> extracted = new java.util.HashMap<>(); |
| | | |
| | | // æ ¡éªOCRç»ææ¯å¦ææ |
| | | if (!ocrResult.containsKey("success") || !ocrResult.getBooleanValue("success")) { |
| | | extracted.put("error", ocrResult.getString("error")); |
| | | return extracted; |
| | | } |
| | | |
| | | // è·åè¯å«çæåå
容 |
| | | String content = ocrResult.getString("content"); |
| | | if (content == null || content.isEmpty()) { |
| | | extracted.put("error", "OCRè¯å«ç»æä¸ºç©º"); |
| | | return extracted; |
| | | } |
| | | |
| | | // å¨å
容䏿¥æ¾ç¹å®å
³é®è¯ |
| | | String[] lines = content.split("\n"); |
| | | for (String line : lines) { |
| | | line = line.trim(); |
| | | |
| | | // æ¥æ¾éé¢ç¸å
³ä¿¡æ¯ |
| | | if (line.contains("éé¢") || line.contains("å计") || line.contains("æ»è®¡") || line.matches(".*\\d+\\.\\d{2}.*")) { |
| | | if (!extracted.containsKey("totalAmount")) { |
| | | extracted.put("totalAmount", line); |
| | | } |
| | | } |
| | | |
| | | // æ¥æ¾æ¥æç¸å
³ä¿¡æ¯ |
| | | if (line.contains("æ¥æ") || line.matches(".*\\d{4}[-/å¹´]\\d{1,2}[-/æ]\\d{1,2}.*")) { |
| | | if (!extracted.containsKey("date")) { |
| | | extracted.put("date", line); |
| | | } |
| | | } |
| | | |
| | | // æ¥æ¾å¤æ³¨ç¸å
³ä¿¡æ¯ |
| | | if (line.contains("夿³¨") || line.contains("说æ")) { |
| | | if (!extracted.containsKey("remark")) { |
| | | extracted.put("remark", line); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // å¦ææ²¡ææ¾å°ç¹å®å段ï¼è¿åå
¨æ |
| | | if (extracted.isEmpty()) { |
| | | extracted.put("fullText", content); |
| | | } |
| | | |
| | | return extracted; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.ruoyi.system.utils; |
| | | |
| | | import com.alibaba.fastjson.JSON; |
| | | import com.alibaba.fastjson.JSONArray; |
| | | import com.alibaba.fastjson.JSONObject; |
| | | import com.tencentcloudapi.common.AbstractModel; |
| | | import com.tencentcloudapi.common.Credential; |
| | | import com.tencentcloudapi.common.profile.ClientProfile; |
| | | import com.tencentcloudapi.common.profile.HttpProfile; |
| | | import com.tencentcloudapi.common.exception.TencentCloudSDKException; |
| | | import com.tencentcloudapi.ocr.v20181119.OcrClient; |
| | | import com.tencentcloudapi.ocr.v20181119.models.*; |
| | | |
| | | import com.ruoyi.system.config.TencentOCRConfig; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import java.io.File; |
| | | import java.util.Base64; |
| | | import java.nio.file.Files; |
| | | import java.util.HashMap; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * è
¾è®¯äºOCRå·¥å
·ç±» |
| | | * 使ç¨è
¾è®¯äºOCRæå¡è¿è¡æåè¯å« |
| | | * æ¯æéç¨æåè¯å«ãæåä½è¯å«çå¤ç§è¯å«ç±»å |
| | | * |
| | | * 使ç¨ç¤ºä¾ï¼ |
| | | * // éç¨æåè¯å« |
| | | * JSONObject result = TencentOCRUtil.generalRecognize("path/to/image.jpg"); |
| | | * |
| | | * // æåä½è¯å« |
| | | * JSONObject result = TencentOCRUtil.handwritingRecognize("path/to/image.jpg"); |
| | | */ |
| | | @Component |
| | | public class TencentOCRUtil { |
| | | |
| | | private static final Logger log = LoggerFactory.getLogger(TencentOCRUtil.class); |
| | | |
| | | private static TencentOCRConfig staticTencentOcrConfig; |
| | | |
| | | @Autowired |
| | | public void setTencentOcrConfig(TencentOCRConfig tencentOcrConfig) { |
| | | TencentOCRUtil.staticTencentOcrConfig = tencentOcrConfig; |
| | | } |
| | | |
| | | /** |
| | | * è·åè
¾è®¯äºOCR客æ·ç«¯å®ä¾ |
| | | * @return OcrClient客æ·ç«¯å®ä¾ |
| | | * @throws TencentCloudSDKException SDKå¼å¸¸ |
| | | */ |
| | | private static OcrClient getClient() throws TencentCloudSDKException { |
| | | Credential cred = new Credential( |
| | | staticTencentOcrConfig.getSecretId(), |
| | | staticTencentOcrConfig.getSecretKey() |
| | | ); |
| | | |
| | | HttpProfile httpProfile = new HttpProfile(); |
| | | httpProfile.setEndpoint(staticTencentOcrConfig.getEndpoint()); |
| | | |
| | | ClientProfile clientProfile = new ClientProfile(); |
| | | clientProfile.setHttpProfile(httpProfile); |
| | | |
| | | return new OcrClient(cred, "", clientProfile); |
| | | } |
| | | |
| | | /** |
| | | * éç¨æåè¯å«ï¼å¾çè·¯å¾ï¼ |
| | | * @param imagePath å¾çè·¯å¾ |
| | | * @return è¯å«ç»æ |
| | | */ |
| | | public static JSONObject generalRecognize(String imagePath) { |
| | | try { |
| | | byte[] imageBytes = Files.readAllBytes(new File(imagePath).toPath()); |
| | | String base64Image = Base64.getEncoder().encodeToString(imageBytes); |
| | | |
| | | OcrClient client = getClient(); |
| | | GeneralBasicOCRRequest req = new GeneralBasicOCRRequest(); |
| | | req.setImageBase64(base64Image); |
| | | |
| | | GeneralBasicOCRResponse resp = client.GeneralBasicOCR(req); |
| | | |
| | | log.info("è
¾è®¯äºOCRéç¨æåè¯å«æåï¼å¾çè·¯å¾: {}", imagePath); |
| | | |
| | | JSONObject result = new JSONObject(); |
| | | result.put("success", true); |
| | | result.put("data", JSON.parseObject(AbstractModel.toJsonString(resp))); |
| | | result.put("content", extractContentFromTencentResult(JSON.parseObject(AbstractModel.toJsonString(resp)))); |
| | | |
| | | return result; |
| | | |
| | | } catch (Exception e) { |
| | | log.error("è
¾è®¯äºOCRéç¨æåè¯å«å¤±è´¥: {}", e.getMessage(), e); |
| | | |
| | | JSONObject errorResult = new JSONObject(); |
| | | errorResult.put("success", false); |
| | | errorResult.put("error", e.getMessage()); |
| | | |
| | | return errorResult; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * éç¨æåè¯å«ï¼æä»¶å¯¹è±¡ï¼ |
| | | * @param imageFile å¾çæä»¶å¯¹è±¡ |
| | | * @return è¯å«ç»æ |
| | | */ |
| | | public static JSONObject generalRecognize(File imageFile) { |
| | | try { |
| | | byte[] imageBytes = Files.readAllBytes(imageFile.toPath()); |
| | | String base64Image = Base64.getEncoder().encodeToString(imageBytes); |
| | | |
| | | OcrClient client = getClient(); |
| | | GeneralBasicOCRRequest req = new GeneralBasicOCRRequest(); |
| | | req.setImageBase64(base64Image); |
| | | |
| | | GeneralBasicOCRResponse resp = client.GeneralBasicOCR(req); |
| | | |
| | | log.info("è
¾è®¯äºOCRéç¨æåè¯å«æåï¼æä»¶å: {}", imageFile.getName()); |
| | | |
| | | JSONObject result = new JSONObject(); |
| | | result.put("success", true); |
| | | result.put("data", JSON.parseObject(AbstractModel.toJsonString(resp))); |
| | | result.put("content", extractContentFromTencentResult(JSON.parseObject(AbstractModel.toJsonString(resp)))); |
| | | |
| | | return result; |
| | | |
| | | } catch (Exception e) { |
| | | log.error("è
¾è®¯äºOCRéç¨æåè¯å«å¤±è´¥: {}", e.getMessage(), e); |
| | | |
| | | JSONObject errorResult = new JSONObject(); |
| | | errorResult.put("success", false); |
| | | errorResult.put("error", e.getMessage()); |
| | | |
| | | return errorResult; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * éç¨æåè¯å«ï¼å¾çåèæ°ç»ï¼ |
| | | * @param imageBytes å¾çåèæ°ç» |
| | | * @return è¯å«ç»æ |
| | | */ |
| | | public static JSONObject generalRecognize(byte[] imageBytes) { |
| | | try { |
| | | String base64Image = Base64.getEncoder().encodeToString(imageBytes); |
| | | |
| | | OcrClient client = getClient(); |
| | | GeneralBasicOCRRequest req = new GeneralBasicOCRRequest(); |
| | | req.setImageBase64(base64Image); |
| | | |
| | | GeneralBasicOCRResponse resp = client.GeneralBasicOCR(req); |
| | | |
| | | log.info("è
¾è®¯äºOCRéç¨æåè¯å«æåï¼åèæ°ç»é¿åº¦: {}", imageBytes.length); |
| | | |
| | | JSONObject result = new JSONObject(); |
| | | result.put("success", true); |
| | | result.put("data", JSON.parseObject(AbstractModel.toJsonString(resp))); |
| | | result.put("content", extractContentFromTencentResult(JSON.parseObject(AbstractModel.toJsonString(resp)))); |
| | | |
| | | return result; |
| | | |
| | | } catch (Exception e) { |
| | | log.error("è
¾è®¯äºOCRéç¨æåè¯å«å¤±è´¥: {}", e.getMessage(), e); |
| | | |
| | | JSONObject errorResult = new JSONObject(); |
| | | errorResult.put("success", false); |
| | | errorResult.put("error", e.getMessage()); |
| | | |
| | | return errorResult; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * é«ç²¾åº¦æåè¯å« |
| | | * @param imagePath å¾çè·¯å¾ |
| | | * @return è¯å«ç»æ |
| | | */ |
| | | public static JSONObject accurateRecognize(String imagePath) { |
| | | try { |
| | | byte[] imageBytes = Files.readAllBytes(new File(imagePath).toPath()); |
| | | String base64Image = Base64.getEncoder().encodeToString(imageBytes); |
| | | |
| | | OcrClient client = getClient(); |
| | | GeneralAccurateOCRRequest req = new GeneralAccurateOCRRequest(); |
| | | req.setImageBase64(base64Image); |
| | | |
| | | GeneralAccurateOCRResponse resp = client.GeneralAccurateOCR(req); |
| | | |
| | | log.info("è
¾è®¯äºOCRé«ç²¾åº¦æåè¯å«æåï¼å¾çè·¯å¾: {}", imagePath); |
| | | |
| | | JSONObject result = new JSONObject(); |
| | | result.put("success", true); |
| | | result.put("data", JSON.parseObject(AbstractModel.toJsonString(resp))); |
| | | result.put("content", extractContentFromTencentResult(JSON.parseObject(AbstractModel.toJsonString(resp)))); |
| | | |
| | | return result; |
| | | |
| | | } catch (Exception e) { |
| | | log.error("è
¾è®¯äºOCRé«ç²¾åº¦æåè¯å«å¤±è´¥: {}", e.getMessage(), e); |
| | | |
| | | JSONObject errorResult = new JSONObject(); |
| | | errorResult.put("success", false); |
| | | errorResult.put("error", e.getMessage()); |
| | | |
| | | return errorResult; |
| | | } |
| | | } |
| | | |
| | | public static JSONObject handwritingRecognize(String imagePath, String[] itemNames) { |
| | | try { |
| | | byte[] imageBytes = Files.readAllBytes(new File(imagePath).toPath()); |
| | | String base64Image = Base64.getEncoder().encodeToString(imageBytes); |
| | | |
| | | OcrClient client = getClient(); |
| | | |
| | | ExtractDocMultiRequest req = new ExtractDocMultiRequest(); |
| | | req.setImageBase64(base64Image); |
| | | // {"æ£è
ç¾åï¼æå°ï¼", "ç¾å人身份è¯å·ç ", "æ¥æ", "èç³»çµè¯", "æ¬äºº", "ç¾åäººä¸æ£è
å
³ç³»"} |
| | | req.setItemNames(itemNames != null ? itemNames : new String[]{"æ£è
å§å", "æ§å«", "å¹´é¾", "身份è¯å·", "è¯æ", "鿝ä»è½¬è¿è´¹ç¨", "è¡ç¨", "å¼å§æ¶é´", "ç»ææ¶é´", "å®¶å±ç¾å"}); |
| | | req.setOutputLanguage("cn"); |
| | | req.setReturnFullText(false); |
| | | req.setItemNamesShowMode(false); |
| | | ExtractDocMultiResponse resp = client.ExtractDocMulti(req); |
| | | |
| | | log.info("è
¾è®¯äºOCRæåä½è¯å«æåï¼å¾çè·¯å¾: {}", imagePath); |
| | | |
| | | // è§£æååºæ°æ® |
| | | JSONObject responseData = JSON.parseObject(AbstractModel.toJsonString(resp)); |
| | | |
| | | |
| | | log.info("æåä½è¯å«æåå° {} ä¸ªåæ®µ", responseData.size()); |
| | | return responseData; |
| | | |
| | | } catch (Exception e) { |
| | | log.error("è
¾è®¯äºOCRæåä½è¯å«å¤±è´¥: {}", e.getMessage(), e); |
| | | JSONObject errorResult = new JSONObject(); |
| | | errorResult.put("error", e.getMessage()); |
| | | return errorResult; |
| | | } |
| | | } |
| | | /** |
| | | * æåä½è¯å« |
| | | * @param imagePath å¾çè·¯å¾ |
| | | * @param itemNames éè¦æåçåæ®µåç§°æ°ç» |
| | | * @return è¯å«ç»æ Mapï¼key为AutoNameçå¼ï¼value为AutoContentçå¼ |
| | | */ |
| | | public static Map<String, String> handwritingRecognizeWith(String imagePath, String[] itemNames) { |
| | | Map<String, String> resultMap = new HashMap<>(); |
| | | try { |
| | | JSONObject responseData = handwritingRecognize(imagePath, itemNames); |
| | | |
| | | // ä»StructuralListä¸æåæ°æ® |
| | | if (responseData.containsKey("StructuralList") && responseData.getJSONArray("StructuralList") != null) { |
| | | JSONArray structuralList = responseData.getJSONArray("StructuralList"); |
| | | for (int i = 0; i < structuralList.size(); i++) { |
| | | JSONObject structural = structuralList.getJSONObject(i); |
| | | if (structural.containsKey("Groups") && structural.getJSONArray("Groups") != null) { |
| | | JSONArray groups = structural.getJSONArray("Groups"); |
| | | for (int j = 0; j < groups.size(); j++) { |
| | | JSONObject group = groups.getJSONObject(j); |
| | | if (group.containsKey("Lines") && group.getJSONArray("Lines") != null) { |
| | | JSONArray lines = group.getJSONArray("Lines"); |
| | | for (int k = 0; k < lines.size(); k++) { |
| | | JSONObject line = lines.getJSONObject(k); |
| | | String autoName = null; |
| | | String autoContent = null; |
| | | |
| | | // æåAutoName |
| | | if (line.containsKey("Key") && line.getJSONObject("Key") != null) { |
| | | JSONObject key = line.getJSONObject("Key"); |
| | | if (key.containsKey("AutoName")) { |
| | | autoName = key.getString("AutoName"); |
| | | } |
| | | } |
| | | |
| | | // æåAutoContent |
| | | if (line.containsKey("Value") && line.getJSONObject("Value") != null) { |
| | | JSONObject value = line.getJSONObject("Value"); |
| | | if (value.containsKey("AutoContent")) { |
| | | autoContent = value.getString("AutoContent"); |
| | | } |
| | | } |
| | | |
| | | // å°é®å¼å¯¹æ¾å
¥ç»æMap |
| | | if (autoName != null && autoContent != null) { |
| | | resultMap.put(autoName, autoContent); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | log.info("æåä½è¯å«æåå° {} ä¸ªåæ®µ", resultMap.size()); |
| | | return resultMap; |
| | | |
| | | } catch (Exception e) { |
| | | log.error("è
¾è®¯äºOCRæåä½è¯å«å¤±è´¥: {}", e.getMessage(), e); |
| | | resultMap.put("error", e.getMessage()); |
| | | return resultMap; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 身份è¯è¯å« |
| | | * @param imagePath å¾çè·¯å¾ |
| | | * @param cardSide èº«ä»½è¯æ£åé¢ï¼"FRONT"表示æ£é¢ï¼"BACK"表示åé¢ |
| | | * @return è¯å«ç»æ |
| | | */ |
| | | public static JSONObject idCardRecognize(String imagePath, String cardSide) { |
| | | try { |
| | | byte[] imageBytes = Files.readAllBytes(new File(imagePath).toPath()); |
| | | String base64Image = Base64.getEncoder().encodeToString(imageBytes); |
| | | |
| | | OcrClient client = getClient(); |
| | | IDCardOCRRequest req = new IDCardOCRRequest(); |
| | | req.setImageBase64(base64Image); |
| | | req.setCardSide(cardSide); |
| | | |
| | | IDCardOCRResponse resp = client.IDCardOCR(req); |
| | | |
| | | log.info("è
¾è®¯äºOCR身份è¯è¯å«æåï¼å¾çè·¯å¾: {}ï¼æ¹å: {}", imagePath, cardSide); |
| | | |
| | | JSONObject result = new JSONObject(); |
| | | result.put("success", true); |
| | | result.put("data", JSON.parseObject(AbstractModel.toJsonString(resp))); |
| | | result.put("content", extractContentFromTencentResult(JSON.parseObject(AbstractModel.toJsonString(resp)))); |
| | | |
| | | return result; |
| | | |
| | | } catch (Exception e) { |
| | | log.error("è
¾è®¯äºOCR身份è¯è¯å«å¤±è´¥: {}", e.getMessage(), e); |
| | | |
| | | JSONObject errorResult = new JSONObject(); |
| | | errorResult.put("success", false); |
| | | errorResult.put("error", e.getMessage()); |
| | | |
| | | return errorResult; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * é¶è¡å¡è¯å« |
| | | * @param imagePath å¾çè·¯å¾ |
| | | * @return è¯å«ç»æ |
| | | */ |
| | | public static JSONObject bankCardRecognize(String imagePath) { |
| | | try { |
| | | byte[] imageBytes = Files.readAllBytes(new File(imagePath).toPath()); |
| | | String base64Image = Base64.getEncoder().encodeToString(imageBytes); |
| | | |
| | | OcrClient client = getClient(); |
| | | BankCardOCRRequest req = new BankCardOCRRequest(); |
| | | req.setImageBase64(base64Image); |
| | | |
| | | BankCardOCRResponse resp = client.BankCardOCR(req); |
| | | |
| | | log.info("è
¾è®¯äºOCRé¶è¡å¡è¯å«æåï¼å¾çè·¯å¾: {}", imagePath); |
| | | |
| | | JSONObject result = new JSONObject(); |
| | | result.put("success", true); |
| | | result.put("data", JSON.parseObject(AbstractModel.toJsonString(resp))); |
| | | result.put("content", extractContentFromTencentResult(JSON.parseObject(AbstractModel.toJsonString(resp)))); |
| | | |
| | | return result; |
| | | |
| | | } catch (Exception e) { |
| | | log.error("è
¾è®¯äºOCRé¶è¡å¡è¯å«å¤±è´¥: {}", e.getMessage(), e); |
| | | |
| | | JSONObject errorResult = new JSONObject(); |
| | | errorResult.put("success", false); |
| | | errorResult.put("error", e.getMessage()); |
| | | |
| | | return errorResult; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * ä»è
¾è®¯äºOCRç»æä¸æåçº¯ææ¬å
容 |
| | | * @param result OCRè¯å«ç»æ |
| | | * @return æåçææ¬å
容 |
| | | */ |
| | | private static String extractContentFromTencentResult(JSONObject result) { |
| | | StringBuilder content = new StringBuilder(); |
| | | |
| | | // å¤çéç¨OCRç»æ |
| | | if (result.containsKey("TextDetections") && result.getJSONArray("TextDetections") != null) { |
| | | JSONArray textDetections = result.getJSONArray("TextDetections"); |
| | | for (int i = 0; i < textDetections.size(); i++) { |
| | | JSONObject detection = textDetections.getJSONObject(i); |
| | | if (detection.containsKey("DetectedText")) { |
| | | content.append(detection.getString("DetectedText")).append("\n"); |
| | | } |
| | | } |
| | | } |
| | | // å¤çæåä½OCRç»æ |
| | | else if (result.containsKey("Items") && result.getJSONArray("Items") != null) { |
| | | JSONArray items = result.getJSONArray("Items"); |
| | | for (int i = 0; i < items.size(); i++) { |
| | | JSONObject item = items.getJSONObject(i); |
| | | if (item.containsKey("Itemstring")) { |
| | | content.append(item.getString("Itemstring")).append("\n"); |
| | | } |
| | | } |
| | | } |
| | | // å¤ç身份è¯OCRç»æ |
| | | else if (result.containsKey("Name") || result.containsKey("Sex") || result.containsKey("Nation") || |
| | | result.containsKey("Birth") || result.containsKey("Address") || result.containsKey("IdNum")) { |
| | | if (result.containsKey("Name") && result.getString("Name") != null) { |
| | | content.append("å§å: ").append(result.getString("Name")).append("\n"); |
| | | } |
| | | if (result.containsKey("Sex") && result.getString("Sex") != null) { |
| | | content.append("æ§å«: ").append(result.getString("Sex")).append("\n"); |
| | | } |
| | | if (result.containsKey("Nation") && result.getString("Nation") != null) { |
| | | content.append("æ°æ: ").append(result.getString("Nation")).append("\n"); |
| | | } |
| | | if (result.containsKey("Birth") && result.getString("Birth") != null) { |
| | | content.append("åºç: ").append(result.getString("Birth")).append("\n"); |
| | | } |
| | | if (result.containsKey("Address") && result.getString("Address") != null) { |
| | | content.append("å°å: ").append(result.getString("Address")).append("\n"); |
| | | } |
| | | if (result.containsKey("IdNum") && result.getString("IdNum") != null) { |
| | | content.append("身份è¯å·: ").append(result.getString("IdNum")).append("\n"); |
| | | } |
| | | } |
| | | // å¤çé¶è¡å¡OCRç»æ |
| | | else if (result.containsKey("CardNo") && result.getString("CardNo") != null) { |
| | | content.append("é¶è¡å¡å·: ").append(result.getString("CardNo")).append("\n"); |
| | | } |
| | | |
| | | return content.toString().trim(); |
| | | } |
| | | |
| | | /** |
| | | * ä»è¯å«ç»æä¸æåç®æ åæ®µï¼éé¢ãæ¥æã夿³¨çï¼ |
| | | * @param ocrResult OCRè¯å«çåå§ç»æ |
| | | * @return æååçç®æ åæ®µ |
| | | */ |
| | | public static Map<String, String> extractTargetFields(JSONObject ocrResult) { |
| | | Map<String, String> extracted = new HashMap<>(); |
| | | |
| | | // æ ¡éªOCRç»ææ¯å¦ææ |
| | | if (!ocrResult.containsKey("success") || !ocrResult.getBooleanValue("success")) { |
| | | extracted.put("error", ocrResult.getString("error")); |
| | | return extracted; |
| | | } |
| | | |
| | | // è·åè¯å«çæåå
容 |
| | | String content = ocrResult.getString("content"); |
| | | if (content == null || content.isEmpty()) { |
| | | extracted.put("error", "OCRè¯å«ç»æä¸ºç©º"); |
| | | return extracted; |
| | | } |
| | | |
| | | // å¨å
容䏿¥æ¾ç¹å®å
³é®è¯ |
| | | String[] lines = content.split("\n"); |
| | | for (String line : lines) { |
| | | line = line.trim(); |
| | | |
| | | // æ¥æ¾éé¢ç¸å
³ä¿¡æ¯ |
| | | if (line.contains("éé¢") || line.contains("å计") || line.contains("æ»è®¡") || line.matches(".*\\d+\\.\\d{2}.*")) { |
| | | if (!extracted.containsKey("totalAmount")) { |
| | | extracted.put("totalAmount", line); |
| | | } |
| | | } |
| | | |
| | | // æ¥æ¾æ¥æç¸å
³ä¿¡æ¯ |
| | | if (line.contains("æ¥æ") || line.matches(".*\\d{4}[-/å¹´]\\d{1,2}[-/æ]\\d{1,2}.*")) { |
| | | if (!extracted.containsKey("date")) { |
| | | extracted.put("date", line); |
| | | } |
| | | } |
| | | |
| | | // æ¥æ¾å¤æ³¨ç¸å
³ä¿¡æ¯ |
| | | if (line.contains("夿³¨") || line.contains("说æ")) { |
| | | if (!extracted.containsKey("remark")) { |
| | | extracted.put("remark", line); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // å¦ææ²¡ææ¾å°ç¹å®å段ï¼è¿åå
¨æ |
| | | if (extracted.isEmpty()) { |
| | | extracted.put("fullText", content); |
| | | } |
| | | |
| | | return extracted; |
| | | } |
| | | |
| | | /** |
| | | * æåä½è¯å«ï¼ä½¿ç¨é»è®¤åæ®µï¼ |
| | | * @param imagePath å¾çè·¯å¾ |
| | | * @return è¯å«ç»æ Mapï¼key为AutoNameçå¼ï¼value为AutoContentçå¼ |
| | | */ |
| | | public static Map<String, String> handwritingRecognize(String imagePath) { |
| | | String[] defaultItemNames = {"æ£è
å§å", "æ§å«", "å¹´é¾", "身份è¯å·", "è¯æ", "鿝ä»è½¬è¿è´¹ç¨", "è¡ç¨", "å¼å§æ¶é´", "ç»ææ¶é´", "å®¶å±ç¾å"}; |
| | | return handwritingRecognizeWith(imagePath, defaultItemNames); |
| | | } |
| | | } |
| | |
| | | DispatchOrdCancelReasonTXT = #{cancelReasonText} |
| | | where DispatchOrdID = #{dispatchOrdID} |
| | | </update> |
| | | |
| | | <!-- æ´æ°è°åº¦åå®é
å¼å§æ¶é´ --> |
| | | <update id="updateDispatchOrdActualDate"> |
| | | update DispatchOrd |
| | | set DispatchOrdActualDate = #{actualDate} |
| | | where DispatchOrdID = #{dispatchOrdID} |
| | | </update> |
| | | |
| | | </mapper> |
| | |
| | | <result property="hospIntroducerId" column="hosp_introducer_id" /> |
| | | <result property="hospIntroducerDate" column="hosp_introducer_date" /> |
| | | <result property="hospLevel" column="hosp_level" /> |
| | | <result property="hospKeywords" column="hosp_keywords" /> |
| | | <result property="status" column="status" /> |
| | | <result property="remark" column="remark" /> |
| | | <result property="createBy" column="create_by" /> |
| | |
| | | select hosp_id, legacy_hosp_id, hosp_name, hosp_city_id, hosp_short, |
| | | hops_province, hops_city, hops_area, hosp_address, hosp_tel, |
| | | hosp_unit_id, hosp_state, hosp_oa_id, hosp_introducer_id, |
| | | hosp_introducer_date, hosp_level, status, remark, |
| | | hosp_introducer_date, hosp_level, hosp_keywords, status, remark, |
| | | create_by, create_time, update_by, update_time |
| | | from tb_hosp_data |
| | | </sql> |
| | |
| | | <if test="hospIntroducerId != null">hosp_introducer_id,</if> |
| | | <if test="hospIntroducerDate != null">hosp_introducer_date,</if> |
| | | <if test="hospLevel != null">hosp_level,</if> |
| | | <if test="hospKeywords != null">hosp_keywords,</if> |
| | | <if test="status != null">status,</if> |
| | | <if test="remark != null">remark,</if> |
| | | <if test="createBy != null and createBy != ''">create_by,</if> |
| | |
| | | <if test="hospIntroducerId != null">#{hospIntroducerId},</if> |
| | | <if test="hospIntroducerDate != null">#{hospIntroducerDate},</if> |
| | | <if test="hospLevel != null">#{hospLevel},</if> |
| | | <if test="hospKeywords != null">#{hospKeywords},</if> |
| | | <if test="status != null">#{status},</if> |
| | | <if test="remark != null">#{remark},</if> |
| | | <if test="createBy != null and createBy != ''">#{createBy},</if> |
| | |
| | | <if test="hospIntroducerId != null">hosp_introducer_id = #{hospIntroducerId},</if> |
| | | <if test="hospIntroducerDate != null">hosp_introducer_date = #{hospIntroducerDate},</if> |
| | | <if test="hospLevel != null">hosp_level = #{hospLevel},</if> |
| | | <if test="hospKeywords != null">hosp_keywords = #{hospKeywords},</if> |
| | | <if test="status != null">status = #{status},</if> |
| | | <if test="remark != null">remark = #{remark},</if> |
| | | <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if> |
| | |
| | | #{hospId} |
| | | </foreach> |
| | | </delete> |
| | | |
| | | <!-- æ ¹æ®åè¯å
³é®è¯é¢è¿æ»¤å»é¢æ°æ® --> |
| | | <select id="selectTbHospDataByKeywords" resultMap="TbHospDataResult"> |
| | | <include refid="selectTbHospDataVo"/> |
| | | <where> |
| | | <if test="status != null and status != ''"> |
| | | and status = #{status} |
| | | </if> |
| | | <if test="keywords != null and keywords.size() > 0"> |
| | | and ( |
| | | <foreach collection="keywords" item="keyword" separator="OR"> |
| | | hosp_keywords like concat('%', #{keyword}, '%') |
| | | </foreach> |
| | | ) |
| | | </if> |
| | | </where> |
| | | order by hosp_id desc |
| | | </select> |
| | | |
| | | </mapper> |
| New file |
| | |
| | | <?xml version="1.0" encoding="UTF-8" ?> |
| | | <!DOCTYPE mapper |
| | | PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" |
| | | "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
| | | <mapper namespace="com.ruoyi.system.mapper.VehicleAbnormalAlertMapper"> |
| | | |
| | | <resultMap type="VehicleAbnormalAlert" id="VehicleAbnormalAlertResult"> |
| | | <result property="alertId" column="alert_id" /> |
| | | <result property="vehicleId" column="vehicle_id" /> |
| | | <result property="vehicleNo" column="vehicle_no" /> |
| | | <result property="alertDate" column="alert_date" /> |
| | | <result property="alertTime" column="alert_time" /> |
| | | <result property="mileage" column="mileage" /> |
| | | <result property="alertType" column="alert_type" /> |
| | | <result property="alertReason" column="alert_reason" /> |
| | | <result property="startTime" column="start_time" /> |
| | | <result property="endTime" column="end_time" /> |
| | | <result property="alertCount" column="alert_count" /> |
| | | <result property="status" column="status" /> |
| | | <result property="handlerId" column="handler_id" /> |
| | | <result property="handlerName" column="handler_name" /> |
| | | <result property="handleTime" column="handle_time" /> |
| | | <result property="handleRemark" column="handle_remark" /> |
| | | <result property="notifyStatus" column="notify_status" /> |
| | | <result property="notifyTime" column="notify_time" /> |
| | | <result property="notifyUsers" column="notify_users" /> |
| | | <result property="deptId" column="dept_id" /> |
| | | <result property="deptName" column="dept_name" /> |
| | | <result property="createTime" column="create_time" /> |
| | | <result property="updateTime" column="update_time" /> |
| | | </resultMap> |
| | | |
| | | <sql id="selectVehicleAbnormalAlertVo"> |
| | | select alert_id, vehicle_id, vehicle_no, alert_date, alert_time, mileage, alert_type, alert_reason, |
| | | start_time, end_time, alert_count, status, handler_id, handler_name, handle_time, handle_remark, |
| | | notify_status, notify_time, notify_users, dept_id, dept_name, create_time, update_time |
| | | from tb_vehicle_abnormal_alert |
| | | </sql> |
| | | |
| | | <select id="selectVehicleAbnormalAlertList" parameterType="VehicleAbnormalAlert" resultMap="VehicleAbnormalAlertResult"> |
| | | <include refid="selectVehicleAbnormalAlertVo"/> |
| | | <where> |
| | | <if test="vehicleId != null "> and vehicle_id = #{vehicleId}</if> |
| | | <if test="vehicleNo != null and vehicleNo != ''"> and vehicle_no like concat('%', #{vehicleNo}, '%')</if> |
| | | <if test="alertDate != null "> and date(alert_date) = date(#{alertDate})</if> |
| | | <if test="params.beginAlertTime != null and params.beginAlertTime != ''"><!-- å¼å§æ¶é´æ£ç´¢ --> |
| | | AND date_format(alert_time,'%y%m%d') >= date_format(#{params.beginAlertTime},'%y%m%d') |
| | | </if> |
| | | <if test="params.endAlertTime != null and params.endAlertTime != ''"><!-- ç»ææ¶é´æ£ç´¢ --> |
| | | AND date_format(alert_time,'%y%m%d') <= date_format(#{params.endAlertTime},'%y%m%d') |
| | | </if> |
| | | <if test="alertType != null and alertType != ''"> and alert_type = #{alertType}</if> |
| | | <if test="status != null and status != ''"> and status = #{status}</if> |
| | | <if test="deptId != null "> and dept_id = #{deptId}</if> |
| | | <if test="deptName != null and deptName != ''"> and dept_name like concat('%', #{deptName}, '%')</if> |
| | | </where> |
| | | order by alert_time desc |
| | | </select> |
| | | |
| | | <select id="selectVehicleAbnormalAlertByAlertId" parameterType="Long" resultMap="VehicleAbnormalAlertResult"> |
| | | <include refid="selectVehicleAbnormalAlertVo"/> |
| | | where alert_id = #{alertId} |
| | | </select> |
| | | |
| | | <insert id="insertVehicleAbnormalAlert" parameterType="VehicleAbnormalAlert" useGeneratedKeys="true" keyProperty="alertId"> |
| | | insert into tb_vehicle_abnormal_alert |
| | | <trim prefix="(" suffix=")" suffixOverrides=","> |
| | | <if test="vehicleId != null">vehicle_id,</if> |
| | | <if test="vehicleNo != null and vehicleNo != ''">vehicle_no,</if> |
| | | <if test="alertDate != null">alert_date,</if> |
| | | <if test="alertTime != null">alert_time,</if> |
| | | <if test="mileage != null">mileage,</if> |
| | | <if test="alertType != null">alert_type,</if> |
| | | <if test="alertReason != null">alert_reason,</if> |
| | | <if test="startTime != null">start_time,</if> |
| | | <if test="endTime != null">end_time,</if> |
| | | <if test="alertCount != null">alert_count,</if> |
| | | <if test="status != null">status,</if> |
| | | <if test="handlerId != null">handler_id,</if> |
| | | <if test="handlerName != null">handler_name,</if> |
| | | <if test="handleTime != null">handle_time,</if> |
| | | <if test="handleRemark != null">handle_remark,</if> |
| | | <if test="notifyStatus != null">notify_status,</if> |
| | | <if test="notifyTime != null">notify_time,</if> |
| | | <if test="notifyUsers != null">notify_users,</if> |
| | | <if test="deptId != null">dept_id,</if> |
| | | <if test="deptName != null">dept_name,</if> |
| | | <if test="createTime != null">create_time,</if> |
| | | <if test="updateTime != null">update_time,</if> |
| | | </trim> |
| | | <trim prefix="values (" suffix=")" suffixOverrides=","> |
| | | <if test="vehicleId != null">#{vehicleId},</if> |
| | | <if test="vehicleNo != null and vehicleNo != ''">#{vehicleNo},</if> |
| | | <if test="alertDate != null">#{alertDate},</if> |
| | | <if test="alertTime != null">#{alertTime},</if> |
| | | <if test="mileage != null">#{mileage},</if> |
| | | <if test="alertType != null">#{alertType},</if> |
| | | <if test="alertReason != null">#{alertReason},</if> |
| | | <if test="startTime != null">#{startTime},</if> |
| | | <if test="endTime != null">#{endTime},</if> |
| | | <if test="alertCount != null">#{alertCount},</if> |
| | | <if test="status != null">#{status},</if> |
| | | <if test="handlerId != null">#{handlerId},</if> |
| | | <if test="handlerName != null">#{handlerName},</if> |
| | | <if test="handleTime != null">#{handleTime},</if> |
| | | <if test="handleRemark != null">#{handleRemark},</if> |
| | | <if test="notifyStatus != null">#{notifyStatus},</if> |
| | | <if test="notifyTime != null">#{notifyTime},</if> |
| | | <if test="notifyUsers != null">#{notifyUsers},</if> |
| | | <if test="deptId != null">#{deptId},</if> |
| | | <if test="deptName != null">#{deptName},</if> |
| | | <if test="createTime != null">#{createTime},</if> |
| | | <if test="updateTime != null">#{updateTime},</if> |
| | | </trim> |
| | | </insert> |
| | | |
| | | <update id="updateVehicleAbnormalAlert" parameterType="VehicleAbnormalAlert"> |
| | | update tb_vehicle_abnormal_alert |
| | | <trim prefix="SET" suffixOverrides=","> |
| | | <if test="vehicleId != null">vehicle_id = #{vehicleId},</if> |
| | | <if test="vehicleNo != null and vehicleNo != ''">vehicle_no = #{vehicleNo},</if> |
| | | <if test="alertDate != null">alert_date = #{alertDate},</if> |
| | | <if test="alertTime != null">alert_time = #{alertTime},</if> |
| | | <if test="mileage != null">mileage = #{mileage},</if> |
| | | <if test="alertType != null">alert_type = #{alertType},</if> |
| | | <if test="alertReason != null">alert_reason = #{alertReason},</if> |
| | | <if test="startTime != null">start_time = #{startTime},</if> |
| | | <if test="endTime != null">end_time = #{endTime},</if> |
| | | <if test="alertCount != null">alert_count = #{alertCount},</if> |
| | | <if test="status != null">status = #{status},</if> |
| | | <if test="handlerId != null">handler_id = #{handlerId},</if> |
| | | <if test="handlerName != null">handler_name = #{handlerName},</if> |
| | | <if test="handleTime != null">handle_time = #{handleTime},</if> |
| | | <if test="handleRemark != null">handle_remark = #{handleRemark},</if> |
| | | <if test="notifyStatus != null">notify_status = #{notifyStatus},</if> |
| | | <if test="notifyTime != null">notify_time = #{notifyTime},</if> |
| | | <if test="notifyUsers != null">notify_users = #{notifyUsers},</if> |
| | | <if test="deptId != null">dept_id = #{deptId},</if> |
| | | <if test="deptName != null">dept_name = #{deptName},</if> |
| | | <if test="updateTime != null">update_time = #{updateTime},</if> |
| | | </trim> |
| | | where alert_id = #{alertId} |
| | | </update> |
| | | |
| | | <delete id="deleteVehicleAbnormalAlertByAlertId" parameterType="Long"> |
| | | delete from tb_vehicle_abnormal_alert where alert_id = #{alertId} |
| | | </delete> |
| | | |
| | | <delete id="deleteVehicleAbnormalAlertByAlertIds" parameterType="String"> |
| | | delete from tb_vehicle_abnormal_alert where alert_id in |
| | | <foreach item="alertId" collection="array" open="(" separator="," close=")"> |
| | | #{alertId} |
| | | </foreach> |
| | | </delete> |
| | | |
| | | <select id="selectDailyAlertCount" resultType="int"> |
| | | select count(*) from tb_vehicle_abnormal_alert |
| | | where vehicle_id = #{vehicleId} and date(alert_date) = date(#{alertDate}) |
| | | </select> |
| | | |
| | | <select id="selectLastAlertTime" resultType="java.util.Date"> |
| | | select max(alert_time) from tb_vehicle_abnormal_alert |
| | | where vehicle_id = #{vehicleId} |
| | | </select> |
| | | |
| | | <update id="batchHandleAlert"> |
| | | update tb_vehicle_abnormal_alert |
| | | set status = '1', |
| | | handler_id = #{handlerId}, |
| | | handler_name = #{handlerName}, |
| | | handle_time = now(), |
| | | handle_remark = #{handleRemark} |
| | | where alert_id in |
| | | <foreach item="alertId" collection="alertIds" open="(" separator="," close=")"> |
| | | #{alertId} |
| | | </foreach> |
| | | </update> |
| | | </mapper> |
| New file |
| | |
| | | <?xml version="1.0" encoding="UTF-8" ?> |
| | | <!DOCTYPE mapper |
| | | PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" |
| | | "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
| | | <mapper namespace="com.ruoyi.system.mapper.VehicleAlertConfigMapper"> |
| | | |
| | | <resultMap type="VehicleAlertConfig" id="VehicleAlertConfigResult"> |
| | | <result property="configId" column="config_id" /> |
| | | <result property="configType" column="config_type" /> |
| | | <result property="deptId" column="dept_id" /> |
| | | <result property="vehicleId" column="vehicle_id" /> |
| | | <result property="mileageThreshold" column="mileage_threshold" /> |
| | | <result property="dailyAlertLimit" column="daily_alert_limit" /> |
| | | <result property="alertInterval" column="alert_interval" /> |
| | | <result property="notifyUserIds" column="notify_user_ids" /> |
| | | <result property="status" column="status" /> |
| | | <result property="remark" column="remark" /> |
| | | <result property="createBy" column="create_by" /> |
| | | <result property="createTime" column="create_time" /> |
| | | <result property="updateBy" column="update_by" /> |
| | | <result property="updateTime" column="update_time" /> |
| | | </resultMap> |
| | | |
| | | <sql id="selectVehicleAlertConfigVo"> |
| | | select c.config_id, c.config_type, c.dept_id, c.vehicle_id, c.mileage_threshold, |
| | | c.daily_alert_limit, c.alert_interval, c.notify_user_ids, c.status, c.remark, |
| | | c.create_by, c.create_time, c.update_by, c.update_time, |
| | | CASE |
| | | WHEN c.config_type = 'DEPT' THEN d.dept_name |
| | | WHEN c.config_type = 'VEHICLE' THEN v.vehicle_no |
| | | ELSE NULL |
| | | END as target_name |
| | | from tb_vehicle_alert_config c |
| | | left join sys_dept d on c.dept_id = d.dept_id |
| | | left join tb_vehicle_info v on c.vehicle_id = v.vehicle_id |
| | | </sql> |
| | | |
| | | <select id="selectVehicleAlertConfigList" parameterType="VehicleAlertConfig" resultMap="VehicleAlertConfigResult"> |
| | | <include refid="selectVehicleAlertConfigVo"/> |
| | | <where> |
| | | <if test="configType != null and configType != ''"> and c.config_type = #{configType}</if> |
| | | <if test="deptId != null "> and c.dept_id = #{deptId}</if> |
| | | <if test="vehicleId != null "> and c.vehicle_id = #{vehicleId}</if> |
| | | <if test="status != null and status != ''"> and c.status = #{status}</if> |
| | | </where> |
| | | order by |
| | | CASE c.config_type |
| | | WHEN 'GLOBAL' THEN 3 |
| | | WHEN 'DEPT' THEN 2 |
| | | WHEN 'VEHICLE' THEN 1 |
| | | END, |
| | | c.create_time desc |
| | | </select> |
| | | |
| | | <select id="selectVehicleAlertConfigByConfigId" parameterType="Long" resultMap="VehicleAlertConfigResult"> |
| | | <include refid="selectVehicleAlertConfigVo"/> |
| | | where c.config_id = #{configId} |
| | | </select> |
| | | |
| | | <select id="selectConfigByVehicle" resultMap="VehicleAlertConfigResult"> |
| | | <include refid="selectVehicleAlertConfigVo"/> |
| | | where c.status = '0' |
| | | and ( |
| | | (c.config_type = 'VEHICLE' and c.vehicle_id = #{vehicleId}) |
| | | or (c.config_type = 'DEPT' and c.dept_id = #{deptId}) |
| | | or c.config_type = 'GLOBAL' |
| | | ) |
| | | order by |
| | | CASE c.config_type |
| | | WHEN 'VEHICLE' THEN 1 |
| | | WHEN 'DEPT' THEN 2 |
| | | WHEN 'GLOBAL' THEN 3 |
| | | END |
| | | limit 1 |
| | | </select> |
| | | |
| | | <insert id="insertVehicleAlertConfig" parameterType="VehicleAlertConfig" useGeneratedKeys="true" keyProperty="configId"> |
| | | insert into tb_vehicle_alert_config |
| | | <trim prefix="(" suffix=")" suffixOverrides=","> |
| | | <if test="configType != null and configType != ''">config_type,</if> |
| | | <if test="deptId != null">dept_id,</if> |
| | | <if test="vehicleId != null">vehicle_id,</if> |
| | | <if test="mileageThreshold != null">mileage_threshold,</if> |
| | | <if test="dailyAlertLimit != null">daily_alert_limit,</if> |
| | | <if test="alertInterval != null">alert_interval,</if> |
| | | <if test="notifyUserIds != null">notify_user_ids,</if> |
| | | <if test="status != null">status,</if> |
| | | <if test="remark != null">remark,</if> |
| | | <if test="createBy != null">create_by,</if> |
| | | create_time |
| | | </trim> |
| | | <trim prefix="values (" suffix=")" suffixOverrides=","> |
| | | <if test="configType != null and configType != ''">#{configType},</if> |
| | | <if test="deptId != null">#{deptId},</if> |
| | | <if test="vehicleId != null">#{vehicleId},</if> |
| | | <if test="mileageThreshold != null">#{mileageThreshold},</if> |
| | | <if test="dailyAlertLimit != null">#{dailyAlertLimit},</if> |
| | | <if test="alertInterval != null">#{alertInterval},</if> |
| | | <if test="notifyUserIds != null">#{notifyUserIds},</if> |
| | | <if test="status != null">#{status},</if> |
| | | <if test="remark != null">#{remark},</if> |
| | | <if test="createBy != null">#{createBy},</if> |
| | | now() |
| | | </trim> |
| | | </insert> |
| | | |
| | | <update id="updateVehicleAlertConfig" parameterType="VehicleAlertConfig"> |
| | | update tb_vehicle_alert_config |
| | | <trim prefix="SET" suffixOverrides=","> |
| | | <if test="configType != null and configType != ''">config_type = #{configType},</if> |
| | | <if test="deptId != null">dept_id = #{deptId},</if> |
| | | <if test="vehicleId != null">vehicle_id = #{vehicleId},</if> |
| | | <if test="mileageThreshold != null">mileage_threshold = #{mileageThreshold},</if> |
| | | <if test="dailyAlertLimit != null">daily_alert_limit = #{dailyAlertLimit},</if> |
| | | <if test="alertInterval != null">alert_interval = #{alertInterval},</if> |
| | | <if test="notifyUserIds != null">notify_user_ids = #{notifyUserIds},</if> |
| | | <if test="status != null">status = #{status},</if> |
| | | <if test="remark != null">remark = #{remark},</if> |
| | | <if test="updateBy != null">update_by = #{updateBy},</if> |
| | | update_time = now() |
| | | </trim> |
| | | where config_id = #{configId} |
| | | </update> |
| | | |
| | | <delete id="deleteVehicleAlertConfigByConfigId" parameterType="Long"> |
| | | delete from tb_vehicle_alert_config where config_id = #{configId} |
| | | </delete> |
| | | |
| | | <delete id="deleteVehicleAlertConfigByConfigIds" parameterType="String"> |
| | | delete from tb_vehicle_alert_config where config_id in |
| | | <foreach item="configId" collection="array" open="(" separator="," close=")"> |
| | | #{configId} |
| | | </foreach> |
| | | </delete> |
| | | </mapper> |
| | |
| | | AND segment_distance >0 |
| | | </select> |
| | | |
| | | <!-- æ¥è¯¢è½¦è¾å¨æå®æ¶é´èå´å
çåæ®µéç¨ --> |
| | | <select id="selectSegmentsByTimeRange" resultMap="VehicleGpsSegmentMileageResult"> |
| | | SELECT segment_id, vehicle_id, vehicle_no, segment_start_time, segment_end_time, |
| | | start_longitude, start_latitude, end_longitude, end_latitude, |
| | | segment_distance, gps_point_count, gps_ids, task_id, task_code, calculate_method |
| | | FROM tb_vehicle_gps_segment_mileage |
| | | WHERE vehicle_id = #{vehicleId} |
| | | AND segment_start_time <= #{endTime} |
| | | AND segment_end_time >= #{startTime} |
| | | AND segment_distance > 0 |
| | | ORDER BY segment_start_time |
| | | </select> |
| | | |
| | | <!-- æä»»å¡IDæ¥è¯¢å段éç¨å表 --> |
| | | <select id="selectSegmentsByTaskId" resultMap="VehicleGpsSegmentMileageResult"> |
| | | <include refid="selectVehicleGpsSegmentMileageVo"/> |
| New file |
| | |
| | | import request from '@/utils/request' |
| | | |
| | | /** |
| | | * OCRæå¡è¿æ¥è¯æ |
| | | */ |
| | | export function diagOcrConnection() { |
| | | return request({ |
| | | url: '/system/diag/ocrConnection', |
| | | method: 'get' |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * éç¨ç½ç»è¿éæ§æµè¯ |
| | | * @param {String} host - ç®æ ä¸»æº |
| | | * @param {Number} port - ç®æ ç«¯å£ |
| | | */ |
| | | export function testConnectivity(host, port) { |
| | | return request({ |
| | | url: '/system/diag/testConnectivity', |
| | | method: 'post', |
| | | data: { |
| | | host, |
| | | port |
| | | } |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * DNSè§£ææµè¯ |
| | | * @param {String} hostname - 主æºå |
| | | */ |
| | | export function testDnsResolution(hostname) { |
| | | return request({ |
| | | url: '/system/diag/testDns', |
| | | method: 'post', |
| | | data: { |
| | | hostname |
| | | } |
| | | }) |
| | | } |
| New file |
| | |
| | | import request from '@/utils/request' |
| | | |
| | | /** |
| | | * ä¸ä¼ å¾çè¿è¡OCRè¯å« |
| | | * @param {FormData} data - å
å«fileãtypeåproviderçè¡¨åæ°æ® |
| | | */ |
| | | export function recognizeImage(data) { |
| | | return request({ |
| | | url: '/system/ocr/recognize', |
| | | method: 'post', |
| | | data: data, |
| | | headers: { |
| | | 'Content-Type': 'multipart/form-data', |
| | | 'repeatSubmit': false // ç¦ç¨é²éå¤æäº¤æ£æ¥ |
| | | } |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * è·åæ¯æçOCRè¯å«ç±»å |
| | | */ |
| | | export function getOcrTypes() { |
| | | return request({ |
| | | url: '/system/ocr/types', |
| | | method: 'get' |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * è·åæ¯æçOCRæå¡æä¾å |
| | | */ |
| | | export function getOcrProviders() { |
| | | return request({ |
| | | url: '/system/ocr/providers', |
| | | method: 'get' |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * éè¿URLè¿è¡OCRè¯å« |
| | | * @param {String} imageUrl - å¾çURLå°å |
| | | * @param {String} type - è¯å«ç±»å |
| | | * @param {String} provider - OCRæå¡æä¾å |
| | | * @param {Array} itemNames - éè¦æåçåæ®µåç§°æ°ç» |
| | | */ |
| | | export function recognizeByUrl(imageUrl, type, provider, itemNames) { |
| | | return request({ |
| | | url: '/system/ocr/recognizeByUrl', |
| | | method: 'get', |
| | | params: { |
| | | imageUrl: imageUrl, |
| | | type: type || 'General', |
| | | provider: provider || 'ali', |
| | | itemNames: itemNames |
| | | } |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * æåOCRç»æä¸çç®æ åæ®µ |
| | | * @param {Object} ocrResult - OCRåå§ç»æ |
| | | */ |
| | | export function extractFields(ocrResult) { |
| | | return request({ |
| | | url: '/system/ocr/extractFields', |
| | | method: 'post', |
| | | data: ocrResult |
| | | }) |
| | | } |
| | |
| | | method: 'get', |
| | | params: query |
| | | }) |
| | | } |
| | | } |
| New file |
| | |
| | | import request from '@/utils/request' |
| | | |
| | | // æ¥è¯¢è½¦è¾å¼å¸¸åè¦å表 |
| | | export function listVehicleAlert(query) { |
| | | return request({ |
| | | url: '/system/vehicleAlert/list', |
| | | method: 'get', |
| | | params: query |
| | | }) |
| | | } |
| | | |
| | | // æ¥è¯¢è½¦è¾å¼å¸¸åè¦è¯¦ç» |
| | | export function getVehicleAlert(alertId) { |
| | | return request({ |
| | | url: '/system/vehicleAlert/' + alertId, |
| | | method: 'get' |
| | | }) |
| | | } |
| | | |
| | | // æ°å¢è½¦è¾å¼å¸¸åè¦ |
| | | export function addVehicleAlert(data) { |
| | | return request({ |
| | | url: '/system/vehicleAlert', |
| | | method: 'post', |
| | | data: data |
| | | }) |
| | | } |
| | | |
| | | // ä¿®æ¹è½¦è¾å¼å¸¸åè¦ |
| | | export function updateVehicleAlert(data) { |
| | | return request({ |
| | | url: '/system/vehicleAlert', |
| | | method: 'put', |
| | | data: data |
| | | }) |
| | | } |
| | | |
| | | // å é¤è½¦è¾å¼å¸¸åè¦ |
| | | export function delVehicleAlert(alertId) { |
| | | return request({ |
| | | url: '/system/vehicleAlert/' + alertId, |
| | | method: 'delete' |
| | | }) |
| | | } |
| | | |
| | | // å¤çåè¦ |
| | | export function handleAlert(alertId, data) { |
| | | return request({ |
| | | url: '/system/vehicleAlert/handle/' + alertId, |
| | | method: 'put', |
| | | data: data |
| | | }) |
| | | } |
| | | |
| | | // æ¹éå¤çåè¦ |
| | | export function batchHandleAlert(alertIds, data) { |
| | | return request({ |
| | | url: '/system/vehicleAlert/batchHandle', |
| | | method: 'put', |
| | | params: { alertIds: alertIds.join(',') }, |
| | | data: data |
| | | }) |
| | | } |
| | | |
| | | // è·åæªå¤çåè¦æ°é |
| | | export function getUnhandledCount() { |
| | | return request({ |
| | | url: '/system/vehicleAlert/unhandledCount', |
| | | method: 'get' |
| | | }) |
| | | } |
| | | |
| | | // 导åºè½¦è¾å¼å¸¸åè¦ |
| | | export function exportVehicleAlert(query) { |
| | | return request({ |
| | | url: '/system/vehicleAlert/export', |
| | | method: 'get', |
| | | params: query |
| | | }) |
| | | } |
| New file |
| | |
| | | import request from '@/utils/request' |
| | | |
| | | // æ¥è¯¢è½¦è¾åè¦é
ç½®å表 |
| | | export function listVehicleAlertConfig(query) { |
| | | return request({ |
| | | url: '/system/vehicleAlertConfig/list', |
| | | method: 'get', |
| | | params: query |
| | | }) |
| | | } |
| | | |
| | | // æ¥è¯¢è½¦è¾åè¦é
ç½®è¯¦ç» |
| | | export function getVehicleAlertConfig(configId) { |
| | | return request({ |
| | | url: '/system/vehicleAlertConfig/' + configId, |
| | | method: 'get' |
| | | }) |
| | | } |
| | | |
| | | // æ°å¢è½¦è¾åè¦é
ç½® |
| | | export function addVehicleAlertConfig(data) { |
| | | return request({ |
| | | url: '/system/vehicleAlertConfig', |
| | | method: 'post', |
| | | data: data |
| | | }) |
| | | } |
| | | |
| | | // ä¿®æ¹è½¦è¾åè¦é
ç½® |
| | | export function updateVehicleAlertConfig(data) { |
| | | return request({ |
| | | url: '/system/vehicleAlertConfig', |
| | | method: 'put', |
| | | data: data |
| | | }) |
| | | } |
| | | |
| | | // å é¤è½¦è¾åè¦é
ç½® |
| | | export function delVehicleAlertConfig(configId) { |
| | | return request({ |
| | | url: '/system/vehicleAlertConfig/' + configId, |
| | | method: 'delete' |
| | | }) |
| | | } |
| | | |
| | | // 导åºè½¦è¾åè¦é
ç½® |
| | | export function exportVehicleAlertConfig(query) { |
| | | return request({ |
| | | url: '/system/vehicleAlertConfig/export', |
| | | method: 'get', |
| | | params: query |
| | | }) |
| | | } |
| New file |
| | |
| | | import request from '@/utils/request' |
| | | |
| | | // æ¥è¯¢è½¦è¾åæ¥å表 |
| | | export function listVehicleSync() { |
| | | return request({ |
| | | url: '/system/vehicleSync/list', |
| | | method: 'get' |
| | | }) |
| | | } |
| | | |
| | | // æå¨åæ¥è½¦è¾ |
| | | export function syncVehicle(data) { |
| | | return request({ |
| | | url: '/system/vehicleSync/syncVehicle', |
| | | method: 'post', |
| | | data: data |
| | | }) |
| | | } |
| | |
| | | // å¨æè·¯ç±ï¼åºäºç¨æ·æé卿å»å è½½ |
| | | export const dynamicRoutes = [ |
| | | qywechatRouter, |
| | | |
| | | // ç½ç»è¯æè·¯ç± |
| | | { |
| | | path: "/system/diag", |
| | | component: Layout, |
| | | redirect: "/system/diag/ocrConnection", |
| | | name: "Diag", |
| | | meta: { |
| | | title: "ç½ç»è¯æ", |
| | | icon: "link", |
| | | permissions: ["system:diag:view"] |
| | | }, |
| | | children: [ |
| | | { |
| | | path: "ocrConnection", |
| | | component: () => import("@/views/system/diag/ocrConnection"), |
| | | name: "OCRConnectionDiag", |
| | | meta: { |
| | | title: "OCRè¿æ¥è¯æ", |
| | | icon: "monitor", |
| | | permissions: ["system:diag:ocr"] |
| | | } |
| | | } |
| | | ] |
| | | }, |
| | | |
| | | // å»é¢åè¯æµè¯è·¯ç± |
| | | { |
| | | path: "/system/hospital", |
| | | component: Layout, |
| | | redirect: "/system/hospital/tokenizer", |
| | | name: "Hospital", |
| | | meta: { |
| | | title: "å»é¢ç®¡ç", |
| | | icon: "hospital", |
| | | permissions: ["system:hospital:view"] |
| | | }, |
| | | children: [ |
| | | { |
| | | path: "tokenizer", |
| | | component: () => import("@/views/system/hospital/tokenizer"), |
| | | name: "HospitalTokenizer", |
| | | meta: { |
| | | title: "å»é¢åè¯æµè¯", |
| | | icon: "search", |
| | | permissions: ["system:hospital:tokenizer"] |
| | | } |
| | | } |
| | | ] |
| | | }, |
| | | |
| | | { |
| | | path: '/system/user-auth', |
| | | component: Layout, |
| New file |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <el-card class="box-card"> |
| | | <div slot="header" class="clearfix"> |
| | | <span>OCRæå¡è¿æ¥è¯æ</span> |
| | | </div> |
| | | |
| | | <el-alert |
| | | title="è¯æè¯´æ" |
| | | type="info" |
| | | :closable="false" |
| | | style="margin-bottom: 20px" |
| | | > |
| | | <div> |
| | | æ¬é¡µé¢ç¨äºè¯æOCRæå¡è¿æ¥ç¶æï¼å
æ¬DNSè§£æãç½ç»è¿éæ§çã<br/> |
| | | <strong>è¯æå
容ï¼</strong>DNSè§£ææµè¯ãè¿æ¥æµè¯ãç½ç»é
ç½®æ£æ¥ |
| | | </div> |
| | | </el-alert> |
| | | |
| | | <div style="text-align: center; margin-bottom: 30px;"> |
| | | <el-button |
| | | type="primary" |
| | | :loading="diagnosing" |
| | | @click="handleDiagnosis" |
| | | > |
| | | <i class="el-icon-search"></i> å¼å§è¯æ |
| | | </el-button> |
| | | </div> |
| | | |
| | | <!-- è¯æç»æ --> |
| | | <div v-if="diagnosisResult"> |
| | | <!-- DNSè§£æç»æ --> |
| | | <el-card shadow="never" class="result-card"> |
| | | <div slot="header"> |
| | | <span>DNSè§£ææµè¯</span> |
| | | <el-tag |
| | | :type="diagnosisResult.dns.success ? 'success' : 'danger'" |
| | | style="float: right; margin-top: 5px;" |
| | | > |
| | | {{ diagnosisResult.dns.success ? 'â
æå' : 'â 失败' }} |
| | | </el-tag> |
| | | </div> |
| | | |
| | | <div class="result-content"> |
| | | <p><strong>ååï¼</strong>{{ ocrEndpoint }}</p> |
| | | <p v-if="diagnosisResult.dns.ips"><strong>IPå°åï¼</strong>{{ diagnosisResult.dns.ips.join(', ') }}</p> |
| | | <p v-if="diagnosisResult.dns.ipCount"><strong>è§£ææ°éï¼</strong>{{ diagnosisResult.dns.ipCount }}个</p> |
| | | <p v-if="diagnosisResult.dns.error"><strong>é误信æ¯ï¼</strong>{{ diagnosisResult.dns.error }}</p> |
| | | |
| | | <div v-if="!diagnosisResult.dns.success" style="margin-top: 15px;"> |
| | | <el-alert |
| | | title="DNSè§£æå»ºè®®" |
| | | type="warning" |
| | | :closable="false" |
| | | show-icon |
| | | > |
| | | <ul style="margin: 0; padding-left: 20px;"> |
| | | <li>æ£æ¥DNSæå¡å¨é
ç½®</li> |
| | | <li>å°è¯ä½¿ç¨å
Œ
±DNSï¼å¦8.8.8.8æ114.114.114.114ï¼</li> |
| | | <li>确认å忝妿£ç¡®ï¼{{ ocrEndpoint }}</li> |
| | | </ul> |
| | | </el-alert> |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | |
| | | <!-- è¿æ¥æµè¯ç»æ --> |
| | | <el-card shadow="never" class="result-card"> |
| | | <div slot="header"> |
| | | <span>è¿æ¥æµè¯</span> |
| | | <el-tag |
| | | :type="diagnosisResult.connection.success ? 'success' : 'danger'" |
| | | style="float: right; margin-top: 5px;" |
| | | > |
| | | {{ diagnosisResult.connection.success ? 'â
æå' : 'â 失败' }} |
| | | </el-tag> |
| | | </div> |
| | | |
| | | <div class="result-content"> |
| | | <p><strong>æµè¯å°åï¼</strong>{{ ocrEndpoint }}:{{ ocrPort }}</p> |
| | | <p v-if="diagnosisResult.connection.responseTime"><strong>ååºæ¶é´ï¼</strong>{{ diagnosisResult.connection.responseTime }}</p> |
| | | <p v-if="diagnosisResult.connection.error"><strong>é误信æ¯ï¼</strong>{{ diagnosisResult.connection.error }}</p> |
| | | |
| | | <div v-if="!diagnosisResult.connection.success" style="margin-top: 15px;"> |
| | | <el-alert |
| | | title="è¿æ¥æµè¯å»ºè®®" |
| | | type="warning" |
| | | :closable="false" |
| | | show-icon |
| | | > |
| | | <ul style="margin: 0; padding-left: 20px;"> |
| | | <li>æ£æ¥é²ç«å¢æ¯å¦å¼æ¾{{ ocrPort }}端å£</li> |
| | | <li>确认æå¡å¨å
许åºç«HTTPS请æ±</li> |
| | | <li>æ£æ¥ç½ç»çç¥æ¯å¦å
许访é®å¤é¨æå¡</li> |
| | | <li>éªè¯ä»£çé
ç½®ï¼å¦éç¨ï¼</li> |
| | | </ul> |
| | | </el-alert> |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | |
| | | <!-- ç½ç»é
ç½® --> |
| | | <el-card shadow="never" class="result-card"> |
| | | <div slot="header"> |
| | | <span>ç½ç»é
ç½®</span> |
| | | </div> |
| | | |
| | | <div class="result-content"> |
| | | <p><strong>HTTP代çï¼</strong>{{ diagnosisResult.network.httpProxy || 'æ ' }}</p> |
| | | <p><strong>HTTPS代çï¼</strong>{{ diagnosisResult.network.httpsProxy || 'æ ' }}</p> |
| | | <p><strong>æ¬å°å°åï¼</strong>{{ diagnosisResult.network.localHostAddress || '-' }}</p> |
| | | <p><strong>æ¬å°ä¸»æºåï¼</strong>{{ diagnosisResult.network.localHostName || '-' }}</p> |
| | | </div> |
| | | </el-card> |
| | | |
| | | <!-- è¯æå»ºè®® --> |
| | | <el-card shadow="never" class="result-card"> |
| | | <div slot="header"> |
| | | <span>è¯æå»ºè®®</span> |
| | | </div> |
| | | |
| | | <div class="result-content"> |
| | | <div v-if="overallStatus === 'success'"> |
| | | <el-alert |
| | | title="è¯æç»æï¼ä¸åæ£å¸¸" |
| | | type="success" |
| | | :closable="false" |
| | | show-icon |
| | | > |
| | | <p>OCRæå¡è¿æ¥æ£å¸¸ï¼æ¨å¯ä»¥æ£å¸¸ä½¿ç¨OCRåè½ã</p> |
| | | </el-alert> |
| | | </div> |
| | | <div v-else-if="overallStatus === 'warning'"> |
| | | <el-alert |
| | | title="è¯æç»æï¼é¨åé®é¢" |
| | | type="warning" |
| | | :closable="false" |
| | | show-icon |
| | | > |
| | | <p>åå¨é¨åç½ç»é®é¢ï¼å¯è½å½±åOCRæå¡ä½¿ç¨ã</p> |
| | | </el-alert> |
| | | </div> |
| | | <div v-else> |
| | | <el-alert |
| | | title="è¯æç»æï¼åå¨é®é¢" |
| | | type="error" |
| | | :closable="false" |
| | | show-icon |
| | | > |
| | | <p>ç½ç»è¿æ¥åå¨é®é¢ï¼éè¦ä¿®å¤åæè½ä½¿ç¨OCRæå¡ã</p> |
| | | </el-alert> |
| | | </div> |
| | | |
| | | <h4 style="margin: 15px 0 10px 0;">è§£å³æ¹æ¡ï¼</h4> |
| | | <ol> |
| | | <li><strong>æ£æ¥ç½ç»è¿æ¥</strong> - ç¡®ä¿æå¡å¨å¯ä»¥è®¿é®å¤ç½</li> |
| | | <li><strong>éªè¯DNSè§£æ</strong> - 确认åå {{ ocrEndpoint }} è½å¤æ£ç¡®è§£æ</li> |
| | | <li><strong>æ£æ¥é²ç«å¢è®¾ç½®</strong> - ç¡®ä¿{{ ocrPort }}端å£å¼æ¾</li> |
| | | <li><strong>é
置代çï¼å¦éè¦ï¼</strong> - 妿éè¿ä»£ç访é®ï¼æ£æ¥ä»£çé
ç½®</li> |
| | | <li><strong>éªè¯é¿éäºæå¡</strong> - 确认OCRæå¡å·²å¼éä¸AccessKeyææ</li> |
| | | </ol> |
| | | </div> |
| | | </el-card> |
| | | </div> |
| | | |
| | | <!-- è¯æä¸ --> |
| | | <div v-if="diagnosing" style="text-align: center; padding: 50px 0"> |
| | | <i class="el-icon-loading" style="font-size: 40px; color: #409EFF"></i> |
| | | <p style="margin-top: 20px; color: #909399">æ£å¨è¯æç½ç»è¿æ¥ï¼è¯·ç¨å...</p> |
| | | <p style="color: #c0c4cc; font-size: 12px;">è¿å¯è½éè¦å ç§éæ¶é´</p> |
| | | </div> |
| | | |
| | | <!-- æªå¼å§è¯æ --> |
| | | <div v-else-if="!diagnosisResult" style="text-align: center; padding: 50px 0; color: #909399"> |
| | | <i class="el-icon-monitor" style="font-size: 60px"></i> |
| | | <p style="margin-top: 20px">ç¹å»"å¼å§è¯æ"æé®æ£æµOCRæå¡è¿æ¥ç¶æ</p> |
| | | </div> |
| | | </el-card> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import { diagOcrConnection } from "@/api/system/networkDiag"; |
| | | |
| | | export default { |
| | | name: "OCRConnectionDiag", |
| | | data() { |
| | | return { |
| | | diagnosing: false, |
| | | diagnosisResult: null, |
| | | ocrEndpoint: 'ocr-api.cn-hangzhou.aliyuncs.com', |
| | | ocrPort: 443 |
| | | }; |
| | | }, |
| | | computed: { |
| | | overallStatus() { |
| | | if (!this.diagnosisResult) return null; |
| | | |
| | | const { dns, connection } = this.diagnosisResult; |
| | | |
| | | if (dns.success && connection.success) { |
| | | return 'success'; |
| | | } else if (!dns.success || !connection.success) { |
| | | return 'error'; |
| | | } |
| | | |
| | | return 'warning'; |
| | | } |
| | | }, |
| | | methods: { |
| | | /** å¼å§è¯æ */ |
| | | handleDiagnosis() { |
| | | this.diagnosing = true; |
| | | this.diagnosisResult = null; |
| | | |
| | | diagOcrConnection().then(response => { |
| | | this.diagnosisResult = response.data; |
| | | this.diagnosing = false; |
| | | }).catch(error => { |
| | | this.$modal.msgError('ç½ç»è¯æå¤±è´¥: ' + error.message); |
| | | this.diagnosing = false; |
| | | }); |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .result-card { |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .result-content p { |
| | | margin: 8px 0; |
| | | line-height: 1.5; |
| | | } |
| | | |
| | | .result-content ul { |
| | | margin: 10px 0; |
| | | padding-left: 20px; |
| | | } |
| | | |
| | | .result-content ol { |
| | | margin: 10px 0; |
| | | padding-left: 20px; |
| | | } |
| | | |
| | | .result-content li { |
| | | margin: 5px 0; |
| | | line-height: 1.5; |
| | | } |
| | | |
| | | .box-card { |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | h4 { |
| | | margin: 15px 0 10px 0; |
| | | color: #303133; |
| | | } |
| | | </style> |
| New file |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <el-card class="box-card" shadow="hover"> |
| | | <div slot="header" class="clearfix"> |
| | | <span style="font-weight: bold; font-size: 16px;">å»é¢åè¯ç®¡ç䏿µè¯å·¥å
·</span> |
| | | <el-tag type="info" size="small" style="margin-left: 10px;">ä½¿ç¨ HanLP ä¸ä¸ä¸æåè¯</el-tag> |
| | | </div> |
| | | |
| | | <!-- æ¹éåè¯åºå --> |
| | | <el-divider content-position="left"> |
| | | <i class="el-icon-setting"></i> æ¹éåè¯ç®¡ç |
| | | </el-divider> |
| | | |
| | | <el-row :gutter="20"> |
| | | <el-col :span="24"> |
| | | <el-alert |
| | | title="æç¤º" |
| | | type="info" |
| | | :closable="false" |
| | | style="margin-bottom: 15px;"> |
| | | <template slot> |
| | | æ¹é为ææå»é¢çæåè¯æ°æ®ã馿¬¡é¨ç½²æ¶å¿
é¡»æ§è¡ä¸æ¬¡ï¼æå¨åè¯ç®æ³æ´æ°åéæ°çæã |
| | | </template> |
| | | </el-alert> |
| | | |
| | | <el-button |
| | | type="primary" |
| | | icon="el-icon-cpu" |
| | | :loading="generating" |
| | | @click="handleGenerateKeywords" |
| | | size="medium"> |
| | | {{ generating ? 'æ£å¨çæåè¯...' : 'æ¹éçæææå»é¢åè¯' }} |
| | | </el-button> |
| | | |
| | | <el-button |
| | | type="success" |
| | | icon="el-icon-refresh" |
| | | @click="resetStatus" |
| | | size="medium" |
| | | v-if="generateResult"> |
| | | éç½® |
| | | </el-button> |
| | | |
| | | <!-- è¿åº¦æ¡ --> |
| | | <div v-if="generating && taskProgress" style="margin-top: 20px;"> |
| | | <el-progress |
| | | :percentage="taskProgress.progress || 0" |
| | | :status="taskProgress.status === 'FAILED' ? 'exception' : null"> |
| | | </el-progress> |
| | | <div style="margin-top: 10px; color: #606266; font-size: 14px;"> |
| | | <span>è¿åº¦: {{ taskProgress.processedCount || 0 }} / {{ taskProgress.totalCount || 0 }}</span> |
| | | <span style="margin-left: 20px;">æå: {{ taskProgress.successCount || 0 }}</span> |
| | | <span style="margin-left: 20px;">失败: {{ taskProgress.failedCount || 0 }}</span> |
| | | </div> |
| | | </div> |
| | | |
| | | <div v-if="generateResult" style="margin-top: 15px;"> |
| | | <el-result |
| | | :icon="generateResult.success ? 'success' : 'error'" |
| | | :title="generateResult.title" |
| | | :subTitle="generateResult.message"> |
| | | </el-result> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <!-- åè¯æµè¯åºå --> |
| | | <el-divider content-position="left"> |
| | | <i class="el-icon-search"></i> å»é¢å¹é
æµè¯ |
| | | </el-divider> |
| | | |
| | | <el-row :gutter="20"> |
| | | <el-col :span="24"> |
| | | <el-alert |
| | | title="æµè¯è¯´æ" |
| | | type="success" |
| | | :closable="false" |
| | | style="margin-bottom: 15px;"> |
| | | <template slot> |
| | | è¾å
¥å»é¢åç§°ãå°åæå
³é®è¯ï¼ç³»ç»å°èªå¨è¿è¡åè¯å¹¶å¹é
ç¸ä¼¼çå»é¢ãå¹é
ç»ææç¸å
³åº¦æåºã |
| | | </template> |
| | | </el-alert> |
| | | |
| | | <el-form :model="searchForm" label-width="100px"> |
| | | <el-form-item label="æç´¢ææ¬"> |
| | | <el-input |
| | | v-model="searchForm.searchText" |
| | | placeholder="请è¾å
¥å»é¢åç§°ãå°åæå
³é®è¯ï¼ä¾å¦ï¼å京ååå»é¢ã䏿µ·çé" |
| | | clearable |
| | | @keyup.enter.native="handleSearch" |
| | | style="width: 60%;"> |
| | | <el-button |
| | | slot="append" |
| | | icon="el-icon-search" |
| | | @click="handleSearch" |
| | | :loading="searching"> |
| | | æç´¢ |
| | | </el-button> |
| | | </el-input> |
| | | |
| | | <el-input-number |
| | | v-model="searchForm.pageSize" |
| | | :min="5" |
| | | :max="100" |
| | | :step="5" |
| | | controls-position="right" |
| | | style="width: 150px; margin-left: 10px;"> |
| | | </el-input-number> |
| | | <span style="margin-left: 5px; color: #909399;">æ¡ç»æ</span> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="åè¯ç»æ" v-if="tokenizedKeywords"> |
| | | <el-tag |
| | | v-for="(keyword, index) in tokenizedKeywords.split(',')" |
| | | :key="index" |
| | | type="success" |
| | | size="small" |
| | | style="margin-right: 5px; margin-bottom: 5px;"> |
| | | {{ keyword }} |
| | | </el-tag> |
| | | </el-form-item> |
| | | </el-form> |
| | | |
| | | <!-- æç´¢ç»æè¡¨æ ¼ --> |
| | | <el-table |
| | | v-loading="searching" |
| | | :data="searchResults" |
| | | border |
| | | stripe |
| | | style="width: 100%; margin-top: 10px;" |
| | | :height="400" |
| | | v-if="searchResults.length > 0"> |
| | | <el-table-column type="index" label="æå" width="60" align="center" /> |
| | | <el-table-column prop="matchScore" label="å¹é
åæ°" width="100" align="center" sortable> |
| | | <template slot-scope="scope"> |
| | | <el-tag :type="getScoreType(scope.row.matchScore)" size="medium"> |
| | | {{ scope.row.matchScore }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="hospital.hospId" label="å»é¢ID" width="80" align="center" /> |
| | | <el-table-column prop="hospital.hospName" label="å»é¢åç§°" min-width="180" show-overflow-tooltip /> |
| | | <el-table-column prop="hospital.hospShort" label="ç®ç§°" width="120" show-overflow-tooltip /> |
| | | <el-table-column label="å°å" min-width="220" show-overflow-tooltip> |
| | | <template slot-scope="scope"> |
| | | {{ formatAddress(scope.row.hospital) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="hospital.hospTel" label="çµè¯" width="130" /> |
| | | <el-table-column label="ç¶æ" width="80" align="center"> |
| | | <template slot-scope="scope"> |
| | | <el-tag v-if="scope.row.hospital.hospState === 1" type="success" size="small">æ£å¸¸</el-tag> |
| | | <el-tag v-else type="info" size="small">æªç¥</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | <!-- æ ç»ææç¤º --> |
| | | <el-empty |
| | | v-if="searched && searchResults.length === 0" |
| | | description="æªæ¾å°å¹é
çå»é¢" |
| | | :image-size="100"> |
| | | </el-empty> |
| | | |
| | | <!-- ç»è®¡ä¿¡æ¯ --> |
| | | <div v-if="searchResults.length > 0" style="margin-top: 10px; color: #909399;"> |
| | | <i class="el-icon-info"></i> |
| | | å
±æ¾å° <span style="color: #409EFF; font-weight: bold;">{{ searchResults.length }}</span> 个å¹é
çå»é¢ |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | </el-card> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import request from '@/utils/request' |
| | | |
| | | export default { |
| | | name: 'HospitalTokenizer', |
| | | data() { |
| | | return { |
| | | // æ¹éçæç¶æ |
| | | generating: false, |
| | | generateResult: null, |
| | | currentTaskId: null, |
| | | taskProgress: null, |
| | | progressTimer: null, |
| | | |
| | | // æç´¢è¡¨å |
| | | searchForm: { |
| | | searchText: '', |
| | | pageSize: 30 |
| | | }, |
| | | |
| | | // æç´¢ç¶æ |
| | | searching: false, |
| | | searched: false, |
| | | tokenizedKeywords: '', |
| | | searchResults: [] |
| | | } |
| | | }, |
| | | methods: { |
| | | /** æ¹éçæåè¯ */ |
| | | handleGenerateKeywords() { |
| | | this.$confirm('确认è¦ä¸ºææå»é¢çæåè¯åï¼è¿å¯è½éè¦å åéæ¶é´ã', '确认æä½', { |
| | | confirmButtonText: 'ç¡®å®', |
| | | cancelButtonText: 'åæ¶', |
| | | type: 'warning' |
| | | }).then(() => { |
| | | this.generating = true |
| | | this.generateResult = null |
| | | this.taskProgress = null |
| | | |
| | | request({ |
| | | url: '/system/hospital/generateKeywords', |
| | | method: 'get' |
| | | }).then(response => { |
| | | if (response.code === 200) { |
| | | // è·åä»»å¡ID |
| | | this.currentTaskId = response.data.taskId |
| | | this.$message.success('åè¯ä»»å¡å·²å¯å¨ï¼æ£å¨åå°æ§è¡...') |
| | | |
| | | // å¼å§è½®è¯¢ä»»å¡è¿åº¦ |
| | | this.startProgressPolling() |
| | | } else { |
| | | this.generating = false |
| | | this.$message.error(response.msg || 'ä»»å¡å¯å¨å¤±è´¥') |
| | | } |
| | | }).catch(error => { |
| | | this.generating = false |
| | | this.$message.error('ç½ç»è¯·æ±å¤±è´¥') |
| | | console.error('çæåè¯å¤±è´¥:', error) |
| | | }) |
| | | }).catch(() => { |
| | | this.$message.info('已忶æä½') |
| | | }) |
| | | }, |
| | | |
| | | /** å¼å§è½®è¯¢ä»»å¡è¿åº¦ */ |
| | | startProgressPolling() { |
| | | this.queryTaskProgress() |
| | | |
| | | // æ¯2ç§è½®è¯¢ä¸æ¬¡ |
| | | this.progressTimer = setInterval(() => { |
| | | this.queryTaskProgress() |
| | | }, 2000) |
| | | }, |
| | | |
| | | /** æ¥è¯¢ä»»å¡è¿åº¦ */ |
| | | queryTaskProgress() { |
| | | if (!this.currentTaskId) { |
| | | return |
| | | } |
| | | |
| | | request({ |
| | | url: '/system/hospital/getTaskProgress', |
| | | method: 'get', |
| | | params: { |
| | | taskId: this.currentTaskId |
| | | } |
| | | }).then(response => { |
| | | if (response.code === 200) { |
| | | this.taskProgress = response.data |
| | | |
| | | // 夿任塿¯å¦å®æ |
| | | if (this.taskProgress.status === 'SUCCESS') { |
| | | this.stopProgressPolling() |
| | | this.generating = false |
| | | this.generateResult = { |
| | | success: true, |
| | | title: 'çææå', |
| | | message: `å
±å¤ç ${this.taskProgress.totalCount} 个åºé¢ï¼æå ${this.taskProgress.successCount} 个ï¼å¤±è´¥ ${this.taskProgress.failedCount} 个` |
| | | } |
| | | this.$message.success('å»é¢åè¯çæå®æï¼') |
| | | } else if (this.taskProgress.status === 'FAILED') { |
| | | this.stopProgressPolling() |
| | | this.generating = false |
| | | this.generateResult = { |
| | | success: false, |
| | | title: 'çæå¤±è´¥', |
| | | message: this.taskProgress.errorMessage || '任塿§è¡å¤±è´¥' |
| | | } |
| | | this.$message.error('å»é¢åè¯çæå¤±è´¥ï¼') |
| | | } |
| | | } else { |
| | | // ä»»å¡ä¸åå¨æå·²è¿æ |
| | | this.stopProgressPolling() |
| | | this.generating = false |
| | | } |
| | | }).catch(error => { |
| | | console.error('æ¥è¯¢è¿åº¦å¤±è´¥:', error) |
| | | }) |
| | | }, |
| | | |
| | | /** åæ¢è½®è¯¢ */ |
| | | stopProgressPolling() { |
| | | if (this.progressTimer) { |
| | | clearInterval(this.progressTimer) |
| | | this.progressTimer = null |
| | | } |
| | | }, |
| | | |
| | | /** éç½®ç¶æ */ |
| | | resetStatus() { |
| | | this.generateResult = null |
| | | this.taskProgress = null |
| | | this.currentTaskId = null |
| | | this.stopProgressPolling() |
| | | }, |
| | | |
| | | /** æç´¢å»é¢ */ |
| | | handleSearch() { |
| | | if (!this.searchForm.searchText.trim()) { |
| | | this.$message.warning('请è¾å
¥æç´¢ææ¬') |
| | | return |
| | | } |
| | | |
| | | this.searching = true |
| | | this.searched = false |
| | | this.tokenizedKeywords = '' |
| | | this.searchResults = [] |
| | | |
| | | request({ |
| | | url: '/system/hospital/searchByKeywords', |
| | | method: 'get', |
| | | params: { |
| | | searchText: this.searchForm.searchText, |
| | | pageSize: this.searchForm.pageSize |
| | | } |
| | | }).then(response => { |
| | | this.searching = false |
| | | this.searched = true |
| | | |
| | | if (response.code === 200) { |
| | | this.searchResults = response.data || [] |
| | | |
| | | // æååè¯ç»æï¼ä»æ¥å¿ä¸ï¼ |
| | | if (this.searchResults.length > 0) { |
| | | this.$message.success(`æ¾å° ${this.searchResults.length} 个å¹é
çå»é¢`) |
| | | // æ¨¡ææ¾ç¤ºåè¯ç»æ |
| | | this.tokenizedKeywords = this.generateMockKeywords(this.searchForm.searchText) |
| | | } else { |
| | | this.$message.info('æªæ¾å°å¹é
çå»é¢ï¼è¯·å°è¯å
¶ä»å
³é®è¯') |
| | | } |
| | | } else { |
| | | this.$message.error(response.msg || 'æç´¢å¤±è´¥') |
| | | } |
| | | }).catch(error => { |
| | | this.searching = false |
| | | this.searched = true |
| | | this.$message.error('æç´¢å¤±è´¥ï¼' + (error.message || 'ç½ç»é误')) |
| | | console.error('æç´¢å¤±è´¥:', error) |
| | | }) |
| | | }, |
| | | |
| | | /** æ ¼å¼åå°å */ |
| | | formatAddress(row) { |
| | | const parts = [] |
| | | if (row.hopsProvince) parts.push(row.hopsProvince) |
| | | if (row.hopsCity) parts.push(row.hopsCity) |
| | | if (row.hopsArea) parts.push(row.hopsArea) |
| | | if (row.hospAddress) parts.push(row.hospAddress) |
| | | return parts.join(' ') |
| | | }, |
| | | |
| | | /** è·ååæ°æ ç¾ç±»å */ |
| | | getScoreType(score) { |
| | | if (score >= 10) return 'danger' // é«å¹é
- çº¢è² |
| | | if (score >= 5) return 'warning' // ä¸å¹é
- æ©è² |
| | | if (score >= 3) return 'success' // ä½å¹é
- ç»¿è² |
| | | return '' // æä½å¹é
- é»è®¤ |
| | | }, |
| | | |
| | | /** çææ¨¡æåè¯ç»æï¼ç¨äºå±ç¤ºï¼ */ |
| | | generateMockKeywords(text) { |
| | | // è¿éç®å模æï¼å®é
åè¯å¨åç«¯å®æ |
| | | const keywords = [] |
| | | for (let i = 0; i < text.length; i++) { |
| | | for (let len = 2; len <= Math.min(4, text.length - i); len++) { |
| | | keywords.push(text.substr(i, len)) |
| | | } |
| | | } |
| | | return keywords.slice(0, 15).join(',') |
| | | } |
| | | }, |
| | | |
| | | beforeDestroy() { |
| | | // ç»ä»¶éæ¯æ¶åæ¢è½®è¯¢ |
| | | this.stopProgressPolling() |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .box-card { |
| | | margin: 20px; |
| | | } |
| | | |
| | | .clearfix:before, |
| | | .clearfix:after { |
| | | display: table; |
| | | content: ""; |
| | | } |
| | | |
| | | .clearfix:after { |
| | | clear: both; |
| | | } |
| | | |
| | | .el-divider { |
| | | margin: 30px 0 20px 0; |
| | | } |
| | | |
| | | .el-divider__text { |
| | | font-weight: bold; |
| | | font-size: 14px; |
| | | } |
| | | </style> |
| New file |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <el-card class="box-card"> |
| | | <div slot="header" class="clearfix"> |
| | | <span>OCRæå¡é
ç½®</span> |
| | | </div> |
| | | |
| | | <el-tabs v-model="activeTab"> |
| | | <el-tab-pane label="é¿éäºOCRé
ç½®" name="ali"> |
| | | <el-alert |
| | | title="é¿éäºOCRé
置说æ" |
| | | type="info" |
| | | :closable="false" |
| | | style="margin-bottom: 20px" |
| | | > |
| | | <div> |
| | | é
ç½®é¿éäºOCRæå¡çAccessKeyä¿¡æ¯ãé
ç½®åéå¯åºç¨çæã<br/> |
| | | <strong>注æï¼</strong>请妥åä¿ç®¡AccessKeyï¼é¿å
æ³é² |
| | | </div> |
| | | </el-alert> |
| | | |
| | | <el-form |
| | | :model="aliForm" |
| | | :rules="rules" |
| | | ref="aliForm" |
| | | label-width="120px" |
| | | style="max-width: 600px;" |
| | | > |
| | | <el-form-item label="AccessKey ID" prop="accessKeyId"> |
| | | <el-input |
| | | v-model="aliForm.accessKeyId" |
| | | placeholder="请è¾å
¥é¿éäºAccessKey ID" |
| | | show-password |
| | | :disabled="!canEditAli" |
| | | /> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="AccessKey Secret" prop="accessKeySecret"> |
| | | <el-input |
| | | v-model="aliForm.accessKeySecret" |
| | | placeholder="请è¾å
¥é¿éäºAccessKey Secret" |
| | | show-password |
| | | :disabled="!canEditAli" |
| | | /> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="OCRæå¡ç«¯ç¹"> |
| | | <el-input |
| | | v-model="aliOcrEndpoint" |
| | | :disabled="true" |
| | | placeholder="é¿éäºOCRæå¡ç«¯ç¹" |
| | | /> |
| | | <div style="margin-top: 5px; color: #909399; font-size: 12px;"> |
| | | é»è®¤ç«¯ç¹ï¼{{ aliOcrEndpoint }} |
| | | </div> |
| | | </el-form-item> |
| | | |
| | | <el-form-item> |
| | | <el-button |
| | | v-if="!canEditAli" |
| | | type="primary" |
| | | @click="handleEditAli" |
| | | > |
| | | <i class="el-icon-edit"></i> ç¼è¾é
ç½® |
| | | </el-button> |
| | | <el-button |
| | | v-if="canEditAli" |
| | | type="success" |
| | | @click="handleSaveAli" |
| | | > |
| | | <i class="el-icon-check"></i> ä¿åé
ç½® |
| | | </el-button> |
| | | <el-button |
| | | v-if="canEditAli" |
| | | @click="handleCancelAli" |
| | | > |
| | | <i class="el-icon-close"></i> åæ¶ |
| | | </el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | </el-tab-pane> |
| | | |
| | | <el-tab-pane label="ç¾åº¦OCRé
ç½®" name="baidu"> |
| | | <el-alert |
| | | title="ç¾åº¦OCRé
置说æ" |
| | | type="info" |
| | | :closable="false" |
| | | style="margin-bottom: 20px" |
| | | > |
| | | <div> |
| | | é
ç½®ç¾åº¦AI弿¾å¹³å°OCRæå¡çAppIDãAPI KeyåSecret Keyä¿¡æ¯ã<br/> |
| | | <strong>注æï¼</strong>请妥åä¿ç®¡å¯é¥ä¿¡æ¯ï¼é¿å
æ³é² |
| | | </div> |
| | | </el-alert> |
| | | |
| | | <el-form |
| | | :model="baiduForm" |
| | | :rules="baiduRules" |
| | | ref="baiduForm" |
| | | label-width="120px" |
| | | style="max-width: 600px;" |
| | | > |
| | | <el-form-item label="App ID" prop="appId"> |
| | | <el-input |
| | | v-model="baiduForm.appId" |
| | | placeholder="请è¾å
¥ç¾åº¦OCR App ID" |
| | | :disabled="!canEditBaidu" |
| | | /> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="API Key" prop="apiKey"> |
| | | <el-input |
| | | v-model="baiduForm.apiKey" |
| | | placeholder="请è¾å
¥ç¾åº¦OCR API Key" |
| | | show-password |
| | | :disabled="!canEditBaidu" |
| | | /> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="Secret Key" prop="secretKey"> |
| | | <el-input |
| | | v-model="baiduForm.secretKey" |
| | | placeholder="请è¾å
¥ç¾åº¦OCR Secret Key" |
| | | show-password |
| | | :disabled="!canEditBaidu" |
| | | /> |
| | | </el-form-item> |
| | | |
| | | <el-form-item> |
| | | <el-button |
| | | v-if="!canEditBaidu" |
| | | type="primary" |
| | | @click="handleEditBaidu" |
| | | > |
| | | <i class="el-icon-edit"></i> ç¼è¾é
ç½® |
| | | </el-button> |
| | | <el-button |
| | | v-if="canEditBaidu" |
| | | type="success" |
| | | @click="handleSaveBaidu" |
| | | > |
| | | <i class="el-icon-check"></i> ä¿åé
ç½® |
| | | </el-button> |
| | | <el-button |
| | | v-if="canEditBaidu" |
| | | @click="handleCancelBaidu" |
| | | > |
| | | <i class="el-icon-close"></i> åæ¶ |
| | | </el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | </el-tab-pane> |
| | | </el-tabs> |
| | | </el-card> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | name: "OCRConfig", |
| | | data() { |
| | | return { |
| | | activeTab: 'ali', |
| | | // é¿éäºOCRé
ç½® |
| | | canEditAli: false, |
| | | aliForm: { |
| | | accessKeyId: '', |
| | | accessKeySecret: '' |
| | | }, |
| | | aliOcrEndpoint: 'ocr-api.cn-hangzhou.aliyuncs.com', |
| | | // ç¾åº¦OCRé
ç½® |
| | | canEditBaidu: false, |
| | | baiduForm: { |
| | | appId: '', |
| | | apiKey: '', |
| | | secretKey: '' |
| | | }, |
| | | rules: { |
| | | accessKeyId: [ |
| | | { required: true, message: '请è¾å
¥AccessKey ID', trigger: 'blur' }, |
| | | { min: 10, max: 64, message: 'é¿åº¦å¨10å°64个å符', trigger: 'blur' } |
| | | ], |
| | | accessKeySecret: [ |
| | | { required: true, message: '请è¾å
¥AccessKey Secret', trigger: 'blur' }, |
| | | { min: 10, max: 64, message: 'é¿åº¦å¨10å°64个å符', trigger: 'blur' } |
| | | ] |
| | | }, |
| | | baiduRules: { |
| | | appId: [ |
| | | { required: true, message: '请è¾å
¥App ID', trigger: 'blur' }, |
| | | { min: 5, max: 32, message: 'é¿åº¦å¨5å°32个å符', trigger: 'blur' } |
| | | ], |
| | | apiKey: [ |
| | | { required: true, message: '请è¾å
¥API Key', trigger: 'blur' }, |
| | | { min: 10, max: 64, message: 'é¿åº¦å¨10å°64个å符', trigger: 'blur' } |
| | | ], |
| | | secretKey: [ |
| | | { required: true, message: '请è¾å
¥Secret Key', trigger: 'blur' }, |
| | | { min: 10, max: 64, message: 'é¿åº¦å¨10å°64个å符', trigger: 'blur' } |
| | | ] |
| | | } |
| | | }; |
| | | }, |
| | | methods: { |
| | | /** ç¼è¾é¿éäºOCRé
ç½® */ |
| | | handleEditAli() { |
| | | this.canEditAli = true; |
| | | }, |
| | | |
| | | /** ä¿åé¿éäºOCRé
ç½® */ |
| | | handleSaveAli() { |
| | | this.$refs.aliForm.validate(valid => { |
| | | if (valid) { |
| | | this.$confirm('ä¿åé¿éäºOCRé
ç½®åéè¦éå¯åºç¨æè½çæï¼æ¯å¦ç»§ç»ï¼', 'æç¤º', { |
| | | confirmButtonText: 'ç¡®å®', |
| | | cancelButtonText: 'åæ¶', |
| | | type: 'warning' |
| | | }).then(() => { |
| | | // è¿éåºè¯¥è°ç¨å端APIä¿åé
ç½® |
| | | this.$modal.msgSuccess('é¿éäºOCRé
ç½®ä¿åæåï¼è¯·éå¯åºç¨ä½¿é
ç½®çæ'); |
| | | this.canEditAli = false; |
| | | }); |
| | | } |
| | | }); |
| | | }, |
| | | |
| | | /** åæ¶é¿éäºOCRç¼è¾ */ |
| | | handleCancelAli() { |
| | | this.canEditAli = false; |
| | | // æ¢å¤åå§å¼ï¼è¿éåºè¯¥ä»å端è·åï¼ |
| | | this.aliForm = { |
| | | accessKeyId: '', |
| | | accessKeySecret: '' |
| | | }; |
| | | }, |
| | | |
| | | /** ç¼è¾ç¾åº¦OCRé
ç½® */ |
| | | handleEditBaidu() { |
| | | this.canEditBaidu = true; |
| | | }, |
| | | |
| | | /** ä¿åç¾åº¦OCRé
ç½® */ |
| | | handleSaveBaidu() { |
| | | this.$refs.baiduForm.validate(valid => { |
| | | if (valid) { |
| | | this.$confirm('ä¿åç¾åº¦OCRé
ç½®åéè¦éå¯åºç¨æè½çæï¼æ¯å¦ç»§ç»ï¼', 'æç¤º', { |
| | | confirmButtonText: 'ç¡®å®', |
| | | cancelButtonText: 'åæ¶', |
| | | type: 'warning' |
| | | }).then(() => { |
| | | // è¿éåºè¯¥è°ç¨å端APIä¿åé
ç½® |
| | | this.$modal.msgSuccess('ç¾åº¦OCRé
ç½®ä¿åæåï¼è¯·éå¯åºç¨ä½¿é
ç½®çæ'); |
| | | this.canEditBaidu = false; |
| | | }); |
| | | } |
| | | }); |
| | | }, |
| | | |
| | | /** åæ¶ç¾åº¦OCRç¼è¾ */ |
| | | handleCancelBaidu() { |
| | | this.canEditBaidu = false; |
| | | // æ¢å¤åå§å¼ï¼è¿éåºè¯¥ä»å端è·åï¼ |
| | | this.baiduForm = { |
| | | appId: '', |
| | | apiKey: '', |
| | | secretKey: '' |
| | | }; |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .box-card { |
| | | margin-bottom: 20px; |
| | | } |
| | | </style> |
| New file |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <el-card class="box-card"> |
| | | <div slot="header" class="clearfix"> |
| | | <span>OCRå¾åè¯å«æµè¯</span> |
| | | </div> |
| | | |
| | | <!-- åè½è¯´æ --> |
| | | <el-alert |
| | | title="åè½è¯´æ" |
| | | type="info" |
| | | :closable="false" |
| | | style="margin-bottom: 20px" |
| | | > |
| | | <div> |
| | | æ¬é¡µé¢ç¨äºæµè¯é¿éäºOCRãç¾åº¦OCRåè
¾è®¯äºOCRå¾åè¯å«åè½ãæ¯æéç¨æåè¯å«ãå票è¯å«ã身份è¯è¯å«ãæåä½è¯å«çã<br/> |
| | | <strong>æ¯ææ ¼å¼ï¼</strong>JPGãPNGãBMPç常è§å¾çæ ¼å¼ï¼<strong>æä»¶å¤§å°ï¼</strong>ä¸è¶
è¿4MB |
| | | </div> |
| | | </el-alert> |
| | | |
| | | <!-- è¯å«ç±»åéæ© --> |
| | | <el-form :inline="true" style="margin-bottom: 20px"> |
| | | <el-form-item label="è¯å«ç±»å"> |
| | | <el-select v-model="recognizeType" placeholder="è¯·éæ©è¯å«ç±»å" style="width: 200px"> |
| | | <el-option label="éç¨æåè¯å«" value="General" /> |
| | | <el-option label="å票è¯å«" value="Invoice" /> |
| | | <el-option label="身份è¯è¯å«" value="IdCard" /> |
| | | <el-option label="æåä½è¯å«" value="HandWriting" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="OCRæå¡"> |
| | | <el-select v-model="provider" placeholder="è¯·éæ©OCRæå¡æä¾å" style="width: 200px"> |
| | | <el-option label="é¿éäºOCR" value="ali" /> |
| | | <el-option label="ç¾åº¦OCR" value="baidu" /> |
| | | <el-option label="è
¾è®¯äºOCR" value="tencent" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="æååæ®µ"> |
| | | <el-input |
| | | v-if="provider === 'tencent' && recognizeType === 'HandWriting'" |
| | | v-model="itemNames" |
| | | placeholder="请è¾å
¥éè¦æåçåæ®µï¼ç¨éå·åé" |
| | | style="width: 200px" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-form> |
| | | |
| | | <!-- å¾çä¸ä¼ åºå --> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-card shadow="hover"> |
| | | <div slot="header"> |
| | | <span>å¾çä¸ä¼ </span> |
| | | </div> |
| | | |
| | | <el-upload |
| | | ref="upload" |
| | | class="upload-demo" |
| | | drag |
| | | action="#" |
| | | :auto-upload="false" |
| | | :on-change="handleFileChange" |
| | | :limit="1" |
| | | :file-list="fileList" |
| | | accept="image/*" |
| | | > |
| | | <i class="el-icon-upload"></i> |
| | | <div class="el-upload__text">å°å¾çæå°æ¤å¤ï¼æ<em>ç¹å»ä¸ä¼ </em></div> |
| | | <div class="el-upload__tip" slot="tip"> |
| | | æ¯æJPGãPNGãBMPæ ¼å¼ï¼æä»¶ä¸è¶
è¿4MB |
| | | </div> |
| | | </el-upload> |
| | | |
| | | <div style="margin-top: 20px; text-align: center"> |
| | | <el-button |
| | | type="primary" |
| | | :loading="recognizing" |
| | | :disabled="!fileList.length" |
| | | @click="handleRecognize" |
| | | > |
| | | <i class="el-icon-view"></i> å¼å§è¯å« |
| | | </el-button> |
| | | <el-button @click="handleClear"> |
| | | <i class="el-icon-delete"></i> æ¸
空 |
| | | </el-button> |
| | | </div> |
| | | |
| | | <!-- å¾çé¢è§ --> |
| | | <div v-if="imagePreview" style="margin-top: 20px"> |
| | | <el-divider>å¾çé¢è§</el-divider> |
| | | <div style="text-align: center"> |
| | | <el-image |
| | | :src="imagePreview" |
| | | fit="contain" |
| | | style="max-width: 100%; max-height: 400px" |
| | | :preview-src-list="[imagePreview]" |
| | | /> |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | |
| | | <!-- è¯å«ç»æåºå --> |
| | | <el-col :span="12"> |
| | | <el-card shadow="hover" style="min-height: 500px"> |
| | | <div slot="header"> |
| | | <span>è¯å«ç»æ</span> |
| | | <el-button |
| | | v-if="ocrResult" |
| | | style="float: right; padding: 3px 0" |
| | | type="text" |
| | | @click="handleCopyResult" |
| | | > |
| | | <i class="el-icon-document-copy"></i> å¤å¶ç»æ |
| | | </el-button> |
| | | </div> |
| | | |
| | | <!-- å è½½ä¸ --> |
| | | <div v-if="recognizing" style="text-align: center; padding: 50px 0"> |
| | | <i class="el-icon-loading" style="font-size: 40px; color: #409EFF"></i> |
| | | <p style="margin-top: 20px; color: #909399">æ£å¨è¯å«ä¸ï¼è¯·ç¨å...({{ provider === 'ali' ? 'é¿éäº' : provider === 'baidu' ? 'ç¾åº¦' : 'è
¾è®¯äº' }})</p> |
| | | </div> |
| | | |
| | | <!-- è¯å«æå --> |
| | | <div v-else-if="ocrResult && ocrResult.success"> |
| | | <el-alert |
| | | title="è¯å«æå" |
| | | type="success" |
| | | :closable="false" |
| | | style="margin-bottom: 15px" |
| | | > |
| | | <div>æå¡æä¾å: {{ provider === 'ali' ? 'é¿éäºOCR' : provider === 'baidu' ? 'ç¾åº¦OCR' : 'è
¾è®¯äºOCR' }}</div> |
| | | </el-alert> |
| | | |
| | | <!-- æåçåæ®µä¿¡æ¯ --> |
| | | <div v-if="extractedFields && Object.keys(extractedFields).length > 0"> |
| | | <el-descriptions title="æååæ®µ" :column="1" border> |
| | | <el-descriptions-item |
| | | v-for="(value, key) in extractedFields" |
| | | :key="key" |
| | | :label="getFieldLabel(key)" |
| | | > |
| | | {{ value }} |
| | | </el-descriptions-item> |
| | | </el-descriptions> |
| | | <el-divider /> |
| | | </div> |
| | | |
| | | <!-- 宿´è¯å«å
容 --> |
| | | <div> |
| | | <h4>宿´è¯å«å
容ï¼</h4> |
| | | <el-input |
| | | type="textarea" |
| | | :value="getFullContent(ocrResult)" |
| | | :autosize="{ minRows: 10, maxRows: 20 }" |
| | | readonly |
| | | style="margin-top: 10px" |
| | | /> |
| | | </div> |
| | | |
| | | <!-- åå§JSONæ°æ® --> |
| | | <el-collapse style="margin-top: 20px"> |
| | | <el-collapse-item title="æ¥çåå§JSONæ°æ®" name="json"> |
| | | <pre style="background: #f5f7fa; padding: 15px; border-radius: 4px; max-height: 400px; overflow: auto">{{ JSON.stringify(ocrResult, null, 2) }}</pre> |
| | | </el-collapse-item> |
| | | </el-collapse> |
| | | </div> |
| | | |
| | | <!-- è¯å«å¤±è´¥ --> |
| | | <div v-else-if="ocrResult && !ocrResult.success"> |
| | | <el-alert |
| | | title="è¯å«å¤±è´¥" |
| | | :description="ocrResult.error || 'æªç¥é误'" |
| | | type="error" |
| | | :closable="false" |
| | | > |
| | | <div>æå¡æä¾å: {{ provider === 'ali' ? 'é¿éäºOCR' : provider === 'baidu' ? 'ç¾åº¦OCR' : 'è
¾è®¯äºOCR' }}</div> |
| | | </el-alert> |
| | | <div v-if="ocrResult.detail" style="margin-top: 15px"> |
| | | <el-tag type="warning">{{ ocrResult.detail }}</el-tag> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- æªå¼å§è¯å« --> |
| | | <div v-else style="text-align: center; padding: 50px 0; color: #909399"> |
| | | <i class="el-icon-picture-outline" style="font-size: 60px"></i> |
| | | <p style="margin-top: 20px">请ä¸ä¼ å¾çå¹¶ç¹å»"å¼å§è¯å«"æé®</p> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | </el-row> |
| | | </el-card> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import { recognizeImage, extractFields } from "@/api/system/ocr"; |
| | | |
| | | export default { |
| | | name: "OCRTest", |
| | | data() { |
| | | return { |
| | | // è¯å«ç±»å |
| | | recognizeType: "General", |
| | | // OCRæå¡æä¾å |
| | | provider: "ali", |
| | | // è
¾è®¯äºOCRæååæ®µ |
| | | itemNames: "æ£è
å§å,æ§å«,å¹´é¾,身份è¯å·,è¯æ,鿝ä»è½¬è¿è´¹ç¨,è¡ç¨,å¼å§æ¶é´,ç»ææ¶é´,å®¶å±ç¾å", |
| | | // æä»¶å表 |
| | | fileList: [], |
| | | // å½åæä»¶ |
| | | currentFile: null, |
| | | // å¾çé¢è§ |
| | | imagePreview: null, |
| | | // è¯å«ä¸ |
| | | recognizing: false, |
| | | // OCRè¯å«ç»æ |
| | | ocrResult: null, |
| | | // æåçåæ®µ |
| | | extractedFields: null |
| | | }; |
| | | }, |
| | | methods: { |
| | | /** æä»¶éæ©åå */ |
| | | handleFileChange(file, fileList) { |
| | | // éå¶åªè½ä¸ä¼ ä¸ä¸ªæä»¶ |
| | | if (fileList.length > 1) { |
| | | fileList.splice(0, 1); |
| | | } |
| | | |
| | | this.fileList = fileList; |
| | | this.currentFile = file.raw; |
| | | |
| | | // çæå¾çé¢è§ |
| | | const reader = new FileReader(); |
| | | reader.onload = (e) => { |
| | | this.imagePreview = e.target.result; |
| | | }; |
| | | reader.readAsDataURL(file.raw); |
| | | |
| | | // æ¸
空ä¹åçè¯å«ç»æ |
| | | this.ocrResult = null; |
| | | this.extractedFields = null; |
| | | }, |
| | | |
| | | /** å¼å§è¯å« */ |
| | | handleRecognize() { |
| | | if (!this.currentFile) { |
| | | this.$modal.msgWarning("请å
ä¸ä¼ å¾ç"); |
| | | return; |
| | | } |
| | | |
| | | // æ£æ¥æä»¶å¤§å°ï¼4MBï¼ |
| | | const maxSize = 4 * 1024 * 1024; |
| | | if (this.currentFile.size > maxSize) { |
| | | this.$modal.msgError("å¾ç大å°ä¸è½è¶
è¿4MB"); |
| | | return; |
| | | } |
| | | |
| | | this.recognizing = true; |
| | | this.ocrResult = null; |
| | | this.extractedFields = null; |
| | | |
| | | // æå»ºFormData |
| | | const formData = new FormData(); |
| | | formData.append("file", this.currentFile); |
| | | formData.append("type", this.recognizeType); |
| | | formData.append("provider", this.provider); |
| | | |
| | | // 妿æ¯è
¾è®¯äºOCRæåä½è¯å«ï¼åæ·»å itemNamesåæ° |
| | | if (this.provider === 'tencent' && this.recognizeType === 'HandWriting' && this.itemNames) { |
| | | const itemNamesArray = this.itemNames.split(',').map(item => item.trim()).filter(item => item); |
| | | itemNamesArray.forEach((itemName, index) => { |
| | | formData.append(`itemNames[${index}]`, itemName); |
| | | }); |
| | | } |
| | | |
| | | // è°ç¨OCRè¯å«æ¥å£ |
| | | recognizeImage(formData).then(response => { |
| | | this.ocrResult = response.data.ocrResult; |
| | | |
| | | // èªå¨æååæ®µ |
| | | if (this.ocrResult.success) { |
| | | extractFields(this.ocrResult).then(res => { |
| | | this.extractedFields = res.data; |
| | | }).catch(() => { |
| | | // æå失败ä¸å½±å主æµç¨ |
| | | }); |
| | | } |
| | | |
| | | this.recognizing = false; |
| | | }).catch(error => { |
| | | this.$modal.msgError("OCRè¯å«å¤±è´¥: " + (error.message || "æªç¥é误")); |
| | | this.recognizing = false; |
| | | }); |
| | | }, |
| | | |
| | | /** æ¸
空 */ |
| | | handleClear() { |
| | | this.fileList = []; |
| | | this.currentFile = null; |
| | | this.imagePreview = null; |
| | | this.ocrResult = null; |
| | | this.extractedFields = null; |
| | | this.$refs.upload.clearFiles(); |
| | | }, |
| | | |
| | | /** å¤å¶è¯å«ç»æ */ |
| | | handleCopyResult() { |
| | | const content = this.getFullContent(this.ocrResult); |
| | | this.copyToClipboard(content); |
| | | this.$modal.msgSuccess("å¤å¶æå"); |
| | | }, |
| | | |
| | | /** å¤å¶å°åªè´´æ¿ */ |
| | | copyToClipboard(text) { |
| | | const textarea = document.createElement("textarea"); |
| | | textarea.value = text; |
| | | document.body.appendChild(textarea); |
| | | textarea.select(); |
| | | document.execCommand("copy"); |
| | | document.body.removeChild(textarea); |
| | | }, |
| | | |
| | | /** è·å宿´è¯å«å
容 */ |
| | | getFullContent(ocrResult) { |
| | | if (!ocrResult) return ""; |
| | | |
| | | if (ocrResult.content) { |
| | | return ocrResult.content; |
| | | } |
| | | |
| | | // 妿æprism_wordsInfoï¼æ¼æ¥æææå |
| | | if (ocrResult.prism_wordsInfo && Array.isArray(ocrResult.prism_wordsInfo)) { |
| | | return ocrResult.prism_wordsInfo.map(item => item.word).join("\n"); |
| | | } |
| | | |
| | | return JSON.stringify(ocrResult, null, 2); |
| | | }, |
| | | |
| | | /** è·ååæ®µæ ç¾ */ |
| | | getFieldLabel(key) { |
| | | const labelMap = { |
| | | totalAmount: "æ»éé¢", |
| | | date: "æ¥æ", |
| | | remark: "夿³¨", |
| | | fullText: "å
¨æ", |
| | | error: "é误信æ¯" |
| | | }; |
| | | return labelMap[key] || key; |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .box-card { |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .upload-demo { |
| | | width: 100%; |
| | | } |
| | | |
| | | .el-upload-dragger { |
| | | width: 100%; |
| | | } |
| | | |
| | | pre { |
| | | font-family: 'Courier New', Courier, monospace; |
| | | font-size: 12px; |
| | | line-height: 1.5; |
| | | white-space: pre-wrap; |
| | | word-wrap: break-word; |
| | | } |
| | | |
| | | .el-descriptions { |
| | | margin-top: 10px; |
| | | } |
| | | |
| | | h4 { |
| | | margin: 15px 0 10px 0; |
| | | color: #303133; |
| | | } |
| | | </style> |
| New file |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="88px"> |
| | | <el-form-item label="车çå·" prop="vehicleNo"> |
| | | <el-input |
| | | v-model="queryParams.vehicleNo" |
| | | placeholder="请è¾å
¥è½¦çå·" |
| | | clearable |
| | | size="small" |
| | | @keyup.enter.native="handleQuery" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="åè¦æ¥æ" prop="alertDate"> |
| | | <el-date-picker |
| | | v-model="queryParams.alertDate" |
| | | type="date" |
| | | value-format="yyyy-MM-dd" |
| | | placeholder="è¯·éæ©åè¦æ¥æ" |
| | | clearable |
| | | size="small" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="åè¦ç¶æ" prop="status"> |
| | | <el-select v-model="queryParams.status" placeholder="è¯·éæ©ç¶æ" clearable size="small"> |
| | | <el-option label="æªå¤ç" value="0" /> |
| | | <el-option label="å·²å¤ç" value="1" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="å½å±é¨é¨" prop="deptId"> |
| | | <el-select v-model="queryParams.deptId" placeholder="è¯·éæ©é¨é¨" clearable size="small"> |
| | | <el-option |
| | | v-for="dept in deptList" |
| | | :key="dept.deptId" |
| | | :label="dept.deptName" |
| | | :value="dept.deptId" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="åè¦æ¶é´"> |
| | | <el-date-picker |
| | | v-model="dateRange" |
| | | size="small" |
| | | style="width: 240px" |
| | | value-format="yyyy-MM-dd" |
| | | type="daterange" |
| | | range-separator="-" |
| | | start-placeholder="å¼å§æ¥æ" |
| | | end-placeholder="ç»ææ¥æ" |
| | | ></el-date-picker> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">æç´¢</el-button> |
| | | <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">éç½®</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | |
| | | <el-row :gutter="10" class="mb8"> |
| | | <el-col :span="1.5"> |
| | | <el-button |
| | | type="success" |
| | | plain |
| | | icon="el-icon-check" |
| | | size="mini" |
| | | :disabled="multiple" |
| | | @click="handleBatchProcess" |
| | | v-hasPermi="['system:vehicleAlert:handle']" |
| | | >æ¹éå¤ç</el-button> |
| | | </el-col> |
| | | <el-col :span="1.5"> |
| | | <el-button |
| | | type="danger" |
| | | plain |
| | | icon="el-icon-delete" |
| | | size="mini" |
| | | :disabled="multiple" |
| | | @click="handleDelete" |
| | | v-hasPermi="['system:vehicleAlert:remove']" |
| | | >å é¤</el-button> |
| | | </el-col> |
| | | <el-col :span="1.5"> |
| | | <el-button |
| | | type="warning" |
| | | plain |
| | | icon="el-icon-download" |
| | | size="mini" |
| | | @click="handleExport" |
| | | v-hasPermi="['system:vehicleAlert:export']" |
| | | >导åº</el-button> |
| | | </el-col> |
| | | <el-col :span="1.5"> |
| | | <el-button |
| | | type="info" |
| | | plain |
| | | icon="el-icon-refresh" |
| | | size="mini" |
| | | @click="refreshUnhandledCount" |
| | | >å·æ°ç»è®¡</el-button> |
| | | </el-col> |
| | | <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> |
| | | </el-row> |
| | | |
| | | <!-- ç»è®¡å¡ç --> |
| | | <el-row :gutter="20" class="mb8"> |
| | | <el-col :span="6"> |
| | | <el-card shadow="hover"> |
| | | <div style="text-align: center;"> |
| | | <div style="font-size: 14px; color: #909399;">æªå¤çåè¦</div> |
| | | <div style="font-size: 28px; color: #F56C6C; font-weight: bold; margin-top: 10px;"> |
| | | {{ unhandledCount }} |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-card shadow="hover"> |
| | | <div style="text-align: center;"> |
| | | <div style="font-size: 14px; color: #909399;">仿¥åè¦</div> |
| | | <div style="font-size: 28px; color: #E6A23C; font-weight: bold; margin-top: 10px;"> |
| | | {{ todayCount }} |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-card shadow="hover"> |
| | | <div style="text-align: center;"> |
| | | <div style="font-size: 14px; color: #909399;">累计åè¦è½¦è¾</div> |
| | | <div style="font-size: 28px; color: #409EFF; font-weight: bold; margin-top: 10px;"> |
| | | {{ totalVehicles }} |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | <el-col :span="6"> |
| | | <el-card shadow="hover"> |
| | | <div style="text-align: center;"> |
| | | <div style="font-size: 14px; color: #909399;">累计åè¦æ¬¡æ°</div> |
| | | <div style="font-size: 28px; color: #67C23A; font-weight: bold; margin-top: 10px;"> |
| | | {{ total }} |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <el-table v-loading="loading" :data="alertList" @selection-change="handleSelectionChange"> |
| | | <el-table-column type="selection" width="55" align="center" /> |
| | | <el-table-column label="åè¦ID" align="center" prop="alertId" width="80" /> |
| | | <el-table-column label="车çå·" align="center" prop="vehicleNo" width="120"> |
| | | <template slot-scope="scope"> |
| | | <el-tag>{{ scope.row.vehicleNo }}</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="åè¦æ¥æ" align="center" prop="alertDate" width="120" /> |
| | | <el-table-column label="åè¦æ¶é´" align="center" prop="alertTime" width="180"> |
| | | <template slot-scope="scope"> |
| | | <span>{{ parseTime(scope.row.alertTime) }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="è¿è¡éç¨(å
Ž)" align="center" prop="mileage" width="140"> |
| | | <template slot-scope="scope"> |
| | | <el-tag type="danger" v-if="scope.row.mileage >= 10"> |
| | | {{ scope.row.mileage }} km |
| | | </el-tag> |
| | | <span v-else>{{ scope.row.mileage }} km</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="彿¥åè¦æ¬¡æ°" align="center" prop="alertCount" width="120"> |
| | | <template slot-scope="scope"> |
| | | <el-tag type="warning" v-if="scope.row.alertCount >= 3"> |
| | | 第{{ scope.row.alertCount }}次 |
| | | </el-tag> |
| | | <span v-else>第{{ scope.row.alertCount }}次</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="å½å±é¨é¨" align="center" prop="deptName" width="150" :show-overflow-tooltip="true" /> |
| | | <el-table-column label="åè¦ç¶æ" align="center" prop="status" width="100"> |
| | | <template slot-scope="scope"> |
| | | <el-tag v-if="scope.row.status === '0'" type="danger">æªå¤ç</el-tag> |
| | | <el-tag v-else type="success">å·²å¤ç</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="éç¥ç¶æ" align="center" prop="notifyStatus" width="100"> |
| | | <template slot-scope="scope"> |
| | | <el-tag v-if="scope.row.notifyStatus === '0'" type="info">æªåé</el-tag> |
| | | <el-tag v-else-if="scope.row.notifyStatus === '1'" type="success">å·²åé</el-tag> |
| | | <el-tag v-else type="danger">åé失败</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="å¤ç人" align="center" prop="handlerName" width="100" :show-overflow-tooltip="true" /> |
| | | <el-table-column label="å¤çæ¶é´" align="center" prop="handleTime" width="180"> |
| | | <template slot-scope="scope"> |
| | | <span v-if="scope.row.handleTime">{{ parseTime(scope.row.handleTime) }}</span> |
| | | <span v-else>-</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="æä½" align="center" class-name="small-padding fixed-width" width="180" fixed="right"> |
| | | <template slot-scope="scope"> |
| | | <el-button |
| | | size="mini" |
| | | type="text" |
| | | icon="el-icon-view" |
| | | @click="handleView(scope.row)" |
| | | v-hasPermi="['system:vehicleAlert:query']" |
| | | >详æ
</el-button> |
| | | <el-button |
| | | v-if="scope.row.status === '0'" |
| | | size="mini" |
| | | type="text" |
| | | icon="el-icon-check" |
| | | @click="handleProcess(scope.row)" |
| | | v-hasPermi="['system:vehicleAlert:handle']" |
| | | >å¤ç</el-button> |
| | | <el-button |
| | | size="mini" |
| | | type="text" |
| | | icon="el-icon-delete" |
| | | @click="handleDelete(scope.row)" |
| | | v-hasPermi="['system:vehicleAlert:remove']" |
| | | >å é¤</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | <pagination |
| | | v-show="total>0" |
| | | :total="total" |
| | | :page.sync="queryParams.pageNum" |
| | | :limit.sync="queryParams.pageSize" |
| | | @pagination="getList" |
| | | /> |
| | | |
| | | <!-- åè¦è¯¦æ
å¯¹è¯æ¡ --> |
| | | <el-dialog title="åè¦è¯¦æ
" :visible.sync="detailOpen" width="700px" append-to-body> |
| | | <el-descriptions :column="2" border> |
| | | <el-descriptions-item label="åè¦ID">{{ detail.alertId }}</el-descriptions-item> |
| | | <el-descriptions-item label="车çå·"> |
| | | <el-tag>{{ detail.vehicleNo }}</el-tag> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="åè¦æ¥æ">{{ detail.alertDate }}</el-descriptions-item> |
| | | <el-descriptions-item label="åè¦æ¶é´">{{ parseTime(detail.alertTime) }}</el-descriptions-item> |
| | | <el-descriptions-item label="è¿è¡éç¨"> |
| | | <span style="color: #F56C6C; font-weight: bold;">{{ detail.mileage }} å
Ž</span> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="彿¥åè¦æ¬¡æ°">第{{ detail.alertCount }}次</el-descriptions-item> |
| | | <el-descriptions-item label="å½å±é¨é¨" :span="2">{{ detail.deptName }}</el-descriptions-item> |
| | | <el-descriptions-item label="åè¦ç¶æ"> |
| | | <el-tag v-if="detail.status === '0'" type="danger">æªå¤ç</el-tag> |
| | | <el-tag v-else type="success">å·²å¤ç</el-tag> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="éç¥ç¶æ"> |
| | | <el-tag v-if="detail.notifyStatus === '0'" type="info">æªåé</el-tag> |
| | | <el-tag v-else-if="detail.notifyStatus === '1'" type="success">å·²åé</el-tag> |
| | | <el-tag v-else type="danger">åé失败</el-tag> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="éç¥ç¨æ·" :span="2">{{ detail.notifyUsers || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="éç¥æ¶æ¯" :span="2">{{ detail.notifyMessage || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="å¤ç人">{{ detail.handlerName || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="å¤çæ¶é´">{{ parseTime(detail.handleTime) || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="å¤ç夿³¨" :span="2">{{ detail.handleRemark || '-' }}</el-descriptions-item> |
| | | <el-descriptions-item label="å建æ¶é´" :span="2">{{ parseTime(detail.createTime) }}</el-descriptions-item> |
| | | </el-descriptions> |
| | | <div slot="footer" class="dialog-footer"> |
| | | <el-button @click="detailOpen = false">å
³ é</el-button> |
| | | </div> |
| | | </el-dialog> |
| | | |
| | | <!-- å¤çåè¦å¯¹è¯æ¡ --> |
| | | <el-dialog title="å¤çåè¦" :visible.sync="processOpen" width="500px" append-to-body> |
| | | <el-form ref="processForm" :model="processForm" :rules="processRules" label-width="100px"> |
| | | <el-form-item label="å¤ç夿³¨" prop="handleRemark"> |
| | | <el-input |
| | | v-model="processForm.handleRemark" |
| | | type="textarea" |
| | | :rows="4" |
| | | placeholder="请è¾å
¥å¤ç夿³¨ï¼å¿
å¡«ï¼" |
| | | /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <div slot="footer" class="dialog-footer"> |
| | | <el-button type="primary" @click="submitProcess">ç¡® å®</el-button> |
| | | <el-button @click="processOpen = false">å æ¶</el-button> |
| | | </div> |
| | | </el-dialog> |
| | | |
| | | <!-- æ¹éå¤çå¯¹è¯æ¡ --> |
| | | <el-dialog title="æ¹éå¤çåè¦" :visible.sync="batchProcessOpen" width="500px" append-to-body> |
| | | <el-alert |
| | | :title="`已鿩 ${ids.length} æ¡æªå¤çåè¦è®°å½`" |
| | | type="info" |
| | | :closable="false" |
| | | style="margin-bottom: 20px;" |
| | | /> |
| | | <el-form ref="batchProcessForm" :model="batchProcessForm" :rules="batchProcessRules" label-width="100px"> |
| | | <el-form-item label="å¤ç夿³¨" prop="handleRemark"> |
| | | <el-input |
| | | v-model="batchProcessForm.handleRemark" |
| | | type="textarea" |
| | | :rows="4" |
| | | placeholder="请è¾å
¥å¤ç夿³¨ï¼å¿
å¡«ï¼" |
| | | /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <div slot="footer" class="dialog-footer"> |
| | | <el-button type="primary" @click="submitBatchProcess">ç¡® å®</el-button> |
| | | <el-button @click="batchProcessOpen = false">å æ¶</el-button> |
| | | </div> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import { listVehicleAlert, getVehicleAlert, delVehicleAlert, handleAlert, batchHandleAlert, getUnhandledCount, exportVehicleAlert } from "@/api/system/vehicleAlert"; |
| | | import { listDept } from "@/api/system/dept"; |
| | | |
| | | export default { |
| | | name: "VehicleAlert", |
| | | data() { |
| | | return { |
| | | // é®ç½©å± |
| | | loading: true, |
| | | // é䏿°ç» |
| | | ids: [], |
| | | // éå个ç¦ç¨ |
| | | single: true, |
| | | // éå¤ä¸ªç¦ç¨ |
| | | multiple: true, |
| | | // æ¾ç¤ºæç´¢æ¡ä»¶ |
| | | showSearch: true, |
| | | // æ»æ¡æ° |
| | | total: 0, |
| | | // 车è¾å¼å¸¸åè¦è¡¨æ ¼æ°æ® |
| | | alertList: [], |
| | | // é¨é¨å表 |
| | | deptList: [], |
| | | // å¼¹åºå±æ é¢ |
| | | title: "", |
| | | // æ¯å¦æ¾ç¤ºè¯¦æ
å¼¹åºå± |
| | | detailOpen: false, |
| | | // æ¯å¦æ¾ç¤ºå¤çå¼¹åºå± |
| | | processOpen: false, |
| | | // æ¯å¦æ¾ç¤ºæ¹éå¤çå¼¹åºå± |
| | | batchProcessOpen: false, |
| | | // æ¥æèå´ |
| | | dateRange: [], |
| | | // 详æ
æ°æ® |
| | | detail: {}, |
| | | // å¤ç表å |
| | | processForm: { |
| | | alertId: null, |
| | | handleRemark: '' |
| | | }, |
| | | // æ¹éå¤ç表å |
| | | batchProcessForm: { |
| | | handleRemark: '' |
| | | }, |
| | | // ç»è®¡æ°æ® |
| | | unhandledCount: 0, |
| | | todayCount: 0, |
| | | totalVehicles: 0, |
| | | // æ¥è¯¢åæ° |
| | | queryParams: { |
| | | pageNum: 1, |
| | | pageSize: 10, |
| | | vehicleNo: null, |
| | | alertDate: null, |
| | | status: null, |
| | | deptId: null |
| | | }, |
| | | // å¤çè¡¨åæ ¡éª |
| | | processRules: { |
| | | handleRemark: [ |
| | | { required: true, message: "å¤ç夿³¨ä¸è½ä¸ºç©º", trigger: "blur" } |
| | | ] |
| | | }, |
| | | // æ¹éå¤çè¡¨åæ ¡éª |
| | | batchProcessRules: { |
| | | handleRemark: [ |
| | | { required: true, message: "å¤ç夿³¨ä¸è½ä¸ºç©º", trigger: "blur" } |
| | | ] |
| | | } |
| | | }; |
| | | }, |
| | | created() { |
| | | this.getList(); |
| | | this.getDeptList(); |
| | | this.refreshUnhandledCount(); |
| | | }, |
| | | methods: { |
| | | /** æ¥è¯¢è½¦è¾å¼å¸¸åè¦å表 */ |
| | | getList() { |
| | | this.loading = true; |
| | | listVehicleAlert(this.addDateRange(this.queryParams, this.dateRange)).then(response => { |
| | | this.alertList = response.rows; |
| | | this.total = response.total; |
| | | // ç»è®¡ä»æ¥åè¦æ°é |
| | | const today = this.parseTime(new Date(), '{y}-{m}-{d}'); |
| | | this.todayCount = this.alertList.filter(item => item.alertDate === today).length; |
| | | // ç»è®¡åè¦è½¦è¾æ°éï¼å»éï¼ |
| | | const vehicleSet = new Set(this.alertList.map(item => item.vehicleId)); |
| | | this.totalVehicles = vehicleSet.size; |
| | | this.loading = false; |
| | | }); |
| | | }, |
| | | /** æ¥è¯¢é¨é¨å表 */ |
| | | getDeptList() { |
| | | listDept().then(response => { |
| | | this.deptList = response.data; |
| | | }); |
| | | }, |
| | | /** å·æ°æªå¤çåè¦ç»è®¡ */ |
| | | refreshUnhandledCount() { |
| | | getUnhandledCount().then(response => { |
| | | this.unhandledCount = response.data; |
| | | }); |
| | | }, |
| | | /** æç´¢æé®æä½ */ |
| | | handleQuery() { |
| | | this.queryParams.pageNum = 1; |
| | | this.getList(); |
| | | }, |
| | | /** éç½®æé®æä½ */ |
| | | resetQuery() { |
| | | this.dateRange = []; |
| | | this.resetForm("queryForm"); |
| | | this.handleQuery(); |
| | | }, |
| | | // å¤éæ¡é䏿°æ® |
| | | handleSelectionChange(selection) { |
| | | this.ids = selection.map(item => item.alertId); |
| | | this.single = selection.length !== 1; |
| | | this.multiple = !selection.length; |
| | | }, |
| | | /** æ¥ç详æ
æé®æä½ */ |
| | | handleView(row) { |
| | | getVehicleAlert(row.alertId).then(response => { |
| | | this.detail = response.data; |
| | | this.detailOpen = true; |
| | | }); |
| | | }, |
| | | /** å¤çæé®æä½ */ |
| | | handleProcess(row) { |
| | | this.processForm = { |
| | | alertId: row.alertId, |
| | | handleRemark: '' |
| | | }; |
| | | this.processOpen = true; |
| | | this.$nextTick(() => { |
| | | this.$refs["processForm"].clearValidate(); |
| | | }); |
| | | }, |
| | | /** æäº¤å¤ç */ |
| | | submitProcess() { |
| | | this.$refs["processForm"].validate(valid => { |
| | | if (valid) { |
| | | handleAlert(this.processForm.alertId, { |
| | | handleRemark: this.processForm.handleRemark |
| | | }).then(response => { |
| | | this.$modal.msgSuccess("å¤çæå"); |
| | | this.processOpen = false; |
| | | this.getList(); |
| | | this.refreshUnhandledCount(); |
| | | }); |
| | | } |
| | | }); |
| | | }, |
| | | /** æ¹éå¤çæé®æä½ */ |
| | | handleBatchProcess() { |
| | | // çéåºæªå¤ççè®°å½ |
| | | const unhandledIds = this.ids.filter(id => { |
| | | const alert = this.alertList.find(item => item.alertId === id); |
| | | return alert && alert.status === '0'; |
| | | }); |
| | | |
| | | if (unhandledIds.length === 0) { |
| | | this.$modal.msgWarning("è¯·éæ©æªå¤ççåè¦è®°å½"); |
| | | return; |
| | | } |
| | | |
| | | this.ids = unhandledIds; |
| | | this.batchProcessForm = { |
| | | handleRemark: '' |
| | | }; |
| | | this.batchProcessOpen = true; |
| | | this.$nextTick(() => { |
| | | this.$refs["batchProcessForm"].clearValidate(); |
| | | }); |
| | | }, |
| | | /** æäº¤æ¹éå¤ç */ |
| | | submitBatchProcess() { |
| | | this.$refs["batchProcessForm"].validate(valid => { |
| | | if (valid) { |
| | | batchHandleAlert(this.ids, { |
| | | handleRemark: this.batchProcessForm.handleRemark |
| | | }).then(response => { |
| | | this.$modal.msgSuccess("æ¹éå¤çæå"); |
| | | this.batchProcessOpen = false; |
| | | this.getList(); |
| | | this.refreshUnhandledCount(); |
| | | }); |
| | | } |
| | | }); |
| | | }, |
| | | /** å é¤æé®æä½ */ |
| | | handleDelete(row) { |
| | | const alertIds = row.alertId || this.ids; |
| | | this.$modal.confirm('æ¯å¦ç¡®è®¤å é¤åè¦è®°å½ç¼å·ä¸º"' + alertIds + '"çæ°æ®é¡¹ï¼').then(function() { |
| | | return delVehicleAlert(alertIds); |
| | | }).then(() => { |
| | | this.getList(); |
| | | this.$modal.msgSuccess("å 餿å"); |
| | | }).catch(() => {}); |
| | | }, |
| | | /** å¯¼åºæé®æä½ */ |
| | | handleExport() { |
| | | this.download('system/vehicleAlert/export', { |
| | | ...this.queryParams |
| | | }, `车è¾å¼å¸¸åè¦_${new Date().getTime()}.xlsx`) |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .mb8 { |
| | | margin-bottom: 8px; |
| | | } |
| | | </style> |
| New file |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="88px"> |
| | | <el-form-item label="é
置类å" prop="configType"> |
| | | <el-select v-model="queryParams.configType" placeholder="è¯·éæ©é
置类å" clearable size="small"> |
| | | <el-option label="å
¨å±é
ç½®" value="GLOBAL" /> |
| | | <el-option label="é¨é¨é
ç½®" value="DEPT" /> |
| | | <el-option label="车è¾é
ç½®" value="VEHICLE" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="é¨é¨" prop="deptId" v-if="queryParams.configType === 'DEPT'"> |
| | | <el-select v-model="queryParams.deptId" placeholder="è¯·éæ©é¨é¨" clearable size="small"> |
| | | <el-option |
| | | v-for="dept in deptList" |
| | | :key="dept.deptId" |
| | | :label="dept.deptName" |
| | | :value="dept.deptId" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="车è¾" prop="vehicleId" v-if="queryParams.configType === 'VEHICLE'"> |
| | | <el-select v-model="queryParams.vehicleId" placeholder="è¯·éæ©è½¦è¾" clearable size="small" filterable> |
| | | <el-option |
| | | v-for="vehicle in vehicleList" |
| | | :key="vehicle.vehicleId" |
| | | :label="vehicle.vehicleNo" |
| | | :value="vehicle.vehicleId" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="ç¶æ" prop="status"> |
| | | <el-select v-model="queryParams.status" placeholder="è¯·éæ©ç¶æ" clearable size="small"> |
| | | <el-option label="å¯ç¨" value="0" /> |
| | | <el-option label="åç¨" value="1" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">æç´¢</el-button> |
| | | <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">éç½®</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | |
| | | <el-row :gutter="10" class="mb8"> |
| | | <el-col :span="1.5"> |
| | | <el-button |
| | | type="primary" |
| | | plain |
| | | icon="el-icon-plus" |
| | | size="mini" |
| | | @click="handleAdd" |
| | | v-hasPermi="['system:vehicleAlertConfig:add']" |
| | | >æ°å¢</el-button> |
| | | </el-col> |
| | | <el-col :span="1.5"> |
| | | <el-button |
| | | type="success" |
| | | plain |
| | | icon="el-icon-edit" |
| | | size="mini" |
| | | :disabled="single" |
| | | @click="handleUpdate" |
| | | v-hasPermi="['system:vehicleAlertConfig:edit']" |
| | | >ä¿®æ¹</el-button> |
| | | </el-col> |
| | | <el-col :span="1.5"> |
| | | <el-button |
| | | type="danger" |
| | | plain |
| | | icon="el-icon-delete" |
| | | size="mini" |
| | | :disabled="multiple" |
| | | @click="handleDelete" |
| | | v-hasPermi="['system:vehicleAlertConfig:remove']" |
| | | >å é¤</el-button> |
| | | </el-col> |
| | | <el-col :span="1.5"> |
| | | <el-button |
| | | type="warning" |
| | | plain |
| | | icon="el-icon-download" |
| | | size="mini" |
| | | @click="handleExport" |
| | | v-hasPermi="['system:vehicleAlertConfig:export']" |
| | | >导åº</el-button> |
| | | </el-col> |
| | | <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> |
| | | </el-row> |
| | | |
| | | <!-- é
置说æ --> |
| | | <el-alert |
| | | title="é
置说æ" |
| | | type="info" |
| | | :closable="false" |
| | | style="margin-bottom: 10px;"> |
| | | <div slot="default"> |
| | | <p>⢠<strong>å
¨å±é
ç½®</strong>ï¼éç¨äºææè½¦è¾çé»è®¤é
ç½®</p> |
| | | <p>⢠<strong>é¨é¨é
ç½®</strong>ï¼é对ç¹å®é¨é¨ç车è¾é
ç½®ï¼ä¼å
级é«äºå
¨å±é
ç½®</p> |
| | | <p>⢠<strong>车è¾é
ç½®</strong>ï¼é对ç¹å®è½¦è¾ç个æ§åé
ç½®ï¼ä¼å
级æé«</p> |
| | | <p>⢠<strong>éç¥ç¨æ·</strong>ï¼å¤ä¸ªç¨æ·IDç¨è±æéå·åéï¼å¦ï¼1,2,3</p> |
| | | </div> |
| | | </el-alert> |
| | | |
| | | <el-table v-loading="loading" :data="configList" @selection-change="handleSelectionChange"> |
| | | <el-table-column type="selection" width="55" align="center" /> |
| | | <el-table-column label="é
ç½®ID" align="center" prop="configId" width="80" /> |
| | | <el-table-column label="é
置类å" align="center" prop="configType" width="100"> |
| | | <template slot-scope="scope"> |
| | | <el-tag v-if="scope.row.configType === 'GLOBAL'" type="primary">å
¨å±é
ç½®</el-tag> |
| | | <el-tag v-else-if="scope.row.configType === 'DEPT'" type="success">é¨é¨é
ç½®</el-tag> |
| | | <el-tag v-else type="warning">车è¾é
ç½®</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="é¨é¨/车è¾" align="center" prop="targetName" width="150"> |
| | | <template slot-scope="scope"> |
| | | <span v-if="scope.row.configType === 'GLOBAL'">-</span> |
| | | <span v-else>{{ scope.row.targetName || '-' }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="éç¨éå¼(km)" align="center" prop="mileageThreshold" width="120"> |
| | | <template slot-scope="scope"> |
| | | <el-tag type="danger">{{ scope.row.mileageThreshold }}</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="æ¯æ¥åè¦æ¬¡æ°" align="center" prop="dailyAlertLimit" width="120" /> |
| | | <el-table-column label="åè¦é´é(åé)" align="center" prop="alertInterval" width="120" /> |
| | | <el-table-column label="éç¥ç¨æ·" align="center" prop="notifyUserIds" width="120" :show-overflow-tooltip="true"> |
| | | <template slot-scope="scope"> |
| | | <span v-if="scope.row.notifyUserIds">{{ scope.row.notifyUserIds }}</span> |
| | | <span v-else style="color: #909399;">æªé
ç½®</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="ç¶æ" align="center" prop="status" width="80"> |
| | | <template slot-scope="scope"> |
| | | <el-switch |
| | | v-model="scope.row.status" |
| | | active-value="0" |
| | | inactive-value="1" |
| | | @change="handleStatusChange(scope.row)" |
| | | ></el-switch> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="å建æ¶é´" align="center" prop="createTime" width="180"> |
| | | <template slot-scope="scope"> |
| | | <span>{{ parseTime(scope.row.createTime) }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="夿³¨" align="center" prop="remark" :show-overflow-tooltip="true" /> |
| | | <el-table-column label="æä½" align="center" class-name="small-padding fixed-width" width="150" fixed="right"> |
| | | <template slot-scope="scope"> |
| | | <el-button |
| | | size="mini" |
| | | type="text" |
| | | icon="el-icon-edit" |
| | | @click="handleUpdate(scope.row)" |
| | | v-hasPermi="['system:vehicleAlertConfig:edit']" |
| | | >ä¿®æ¹</el-button> |
| | | <el-button |
| | | size="mini" |
| | | type="text" |
| | | icon="el-icon-delete" |
| | | @click="handleDelete(scope.row)" |
| | | v-hasPermi="['system:vehicleAlertConfig:remove']" |
| | | >å é¤</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | <pagination |
| | | v-show="total>0" |
| | | :total="total" |
| | | :page.sync="queryParams.pageNum" |
| | | :limit.sync="queryParams.pageSize" |
| | | @pagination="getList" |
| | | /> |
| | | |
| | | <!-- æ·»å æä¿®æ¹è½¦è¾åè¦é
ç½®å¯¹è¯æ¡ --> |
| | | <el-dialog :title="title" :visible.sync="open" width="600px" append-to-body> |
| | | <el-form ref="form" :model="form" :rules="rules" label-width="120px"> |
| | | <el-form-item label="é
置类å" prop="configType"> |
| | | <el-radio-group v-model="form.configType" @change="handleConfigTypeChange"> |
| | | <el-radio label="GLOBAL">å
¨å±é
ç½®</el-radio> |
| | | <el-radio label="DEPT">é¨é¨é
ç½®</el-radio> |
| | | <el-radio label="VEHICLE">车è¾é
ç½®</el-radio> |
| | | </el-radio-group> |
| | | </el-form-item> |
| | | <el-form-item label="é¨é¨" prop="deptId" v-if="form.configType === 'DEPT'"> |
| | | <el-select v-model="form.deptId" placeholder="è¯·éæ©é¨é¨" clearable style="width: 100%"> |
| | | <el-option |
| | | v-for="dept in deptList" |
| | | :key="dept.deptId" |
| | | :label="dept.deptName" |
| | | :value="dept.deptId" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="车è¾" prop="vehicleId" v-if="form.configType === 'VEHICLE'"> |
| | | <el-select v-model="form.vehicleId" placeholder="è¯·éæ©è½¦è¾" clearable filterable style="width: 100%"> |
| | | <el-option |
| | | v-for="vehicle in vehicleList" |
| | | :key="vehicle.vehicleId" |
| | | :label="vehicle.vehicleNo" |
| | | :value="vehicle.vehicleId" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="éç¨éå¼(km)" prop="mileageThreshold"> |
| | | <el-input-number |
| | | v-model="form.mileageThreshold" |
| | | :min="1" |
| | | :max="1000" |
| | | :step="1" |
| | | placeholder="请è¾å
¥éç¨éå¼" |
| | | style="width: 100%" |
| | | /> |
| | | <span style="color: #909399; font-size: 12px;">è½¦è¾æªç»å®ä»»å¡æ¶ï¼è¶
è¿æ¤éç¨å°è§¦ååè¦</span> |
| | | </el-form-item> |
| | | <el-form-item label="æ¯æ¥åè¦æ¬¡æ°" prop="dailyAlertLimit"> |
| | | <el-input-number |
| | | v-model="form.dailyAlertLimit" |
| | | :min="1" |
| | | :max="100" |
| | | :step="1" |
| | | placeholder="请è¾å
¥æ¯æ¥åè¦æ¬¡æ°éå¶" |
| | | style="width: 100%" |
| | | /> |
| | | <span style="color: #909399; font-size: 12px;">æ¯è¾è½¦æ¯å¤©æå¤åè¦æ¬¡æ°</span> |
| | | </el-form-item> |
| | | <el-form-item label="åè¦é´é(åé)" prop="alertInterval"> |
| | | <el-input-number |
| | | v-model="form.alertInterval" |
| | | :min="1" |
| | | :max="1440" |
| | | :step="1" |
| | | placeholder="请è¾å
¥åè¦é´éæ¶é´" |
| | | style="width: 100%" |
| | | /> |
| | | <span style="color: #909399; font-size: 12px;">两次åè¦ä¹é´çæå°æ¶é´é´é</span> |
| | | </el-form-item> |
| | | <el-form-item label="éç¥ç¨æ·ID" prop="notifyUserIds"> |
| | | <el-input |
| | | v-model="form.notifyUserIds" |
| | | type="textarea" |
| | | :rows="3" |
| | | placeholder="请è¾å
¥éç¥ç¨æ·IDå表ï¼å¤ä¸ªç¨è±æéå·åéï¼å¦ï¼1,2,3" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="ç¶æ" prop="status"> |
| | | <el-radio-group v-model="form.status"> |
| | | <el-radio label="0">å¯ç¨</el-radio> |
| | | <el-radio label="1">åç¨</el-radio> |
| | | </el-radio-group> |
| | | </el-form-item> |
| | | <el-form-item label="夿³¨" prop="remark"> |
| | | <el-input v-model="form.remark" type="textarea" placeholder="请è¾å
¥å¤æ³¨" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <div slot="footer" class="dialog-footer"> |
| | | <el-button type="primary" @click="submitForm">ç¡® å®</el-button> |
| | | <el-button @click="cancel">å æ¶</el-button> |
| | | </div> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import { listVehicleAlertConfig, getVehicleAlertConfig, delVehicleAlertConfig, addVehicleAlertConfig, updateVehicleAlertConfig } from "@/api/system/vehicleAlertConfig"; |
| | | import { listDept } from "@/api/system/dept"; |
| | | import { listVehicle } from "@/api/system/vehicle"; |
| | | |
| | | export default { |
| | | name: "VehicleAlertConfig", |
| | | data() { |
| | | return { |
| | | // é®ç½©å± |
| | | loading: true, |
| | | // é䏿°ç» |
| | | ids: [], |
| | | // éå个ç¦ç¨ |
| | | single: true, |
| | | // éå¤ä¸ªç¦ç¨ |
| | | multiple: true, |
| | | // æ¾ç¤ºæç´¢æ¡ä»¶ |
| | | showSearch: true, |
| | | // æ»æ¡æ° |
| | | total: 0, |
| | | // 车è¾åè¦é
ç½®è¡¨æ ¼æ°æ® |
| | | configList: [], |
| | | // é¨é¨å表 |
| | | deptList: [], |
| | | // 车è¾å表 |
| | | vehicleList: [], |
| | | // å¼¹åºå±æ é¢ |
| | | title: "", |
| | | // æ¯å¦æ¾ç¤ºå¼¹åºå± |
| | | open: false, |
| | | // æ¥è¯¢åæ° |
| | | queryParams: { |
| | | pageNum: 1, |
| | | pageSize: 10, |
| | | configType: null, |
| | | deptId: null, |
| | | vehicleId: null, |
| | | status: null |
| | | }, |
| | | // 表ååæ° |
| | | form: {}, |
| | | // è¡¨åæ ¡éª |
| | | rules: { |
| | | configType: [ |
| | | { required: true, message: "é
置类åä¸è½ä¸ºç©º", trigger: "change" } |
| | | ], |
| | | deptId: [ |
| | | { required: true, message: "é¨é¨ä¸è½ä¸ºç©º", trigger: "change" } |
| | | ], |
| | | vehicleId: [ |
| | | { required: true, message: "车è¾ä¸è½ä¸ºç©º", trigger: "change" } |
| | | ], |
| | | mileageThreshold: [ |
| | | { required: true, message: "éç¨éå¼ä¸è½ä¸ºç©º", trigger: "blur" } |
| | | ], |
| | | dailyAlertLimit: [ |
| | | { required: true, message: "æ¯æ¥åè¦æ¬¡æ°ä¸è½ä¸ºç©º", trigger: "blur" } |
| | | ], |
| | | alertInterval: [ |
| | | { required: true, message: "åè¦é´éä¸è½ä¸ºç©º", trigger: "blur" } |
| | | ] |
| | | } |
| | | }; |
| | | }, |
| | | created() { |
| | | this.getList(); |
| | | this.getDeptList(); |
| | | this.getVehicleList(); |
| | | }, |
| | | methods: { |
| | | /** æ¥è¯¢è½¦è¾åè¦é
ç½®å表 */ |
| | | getList() { |
| | | this.loading = true; |
| | | listVehicleAlertConfig(this.queryParams).then(response => { |
| | | this.configList = response.rows; |
| | | this.total = response.total; |
| | | this.loading = false; |
| | | }); |
| | | }, |
| | | /** æ¥è¯¢é¨é¨å表 */ |
| | | getDeptList() { |
| | | listDept().then(response => { |
| | | this.deptList = response.data; |
| | | }); |
| | | }, |
| | | /** æ¥è¯¢è½¦è¾å表 */ |
| | | getVehicleList() { |
| | | listVehicle({ pageNum: 1, pageSize: 10000 }).then(response => { |
| | | this.vehicleList = response.rows || []; |
| | | }); |
| | | }, |
| | | // åæ¶æé® |
| | | cancel() { |
| | | this.open = false; |
| | | this.reset(); |
| | | }, |
| | | // 表åéç½® |
| | | reset() { |
| | | this.form = { |
| | | configId: null, |
| | | configType: "GLOBAL", |
| | | deptId: null, |
| | | vehicleId: null, |
| | | mileageThreshold: 10, |
| | | dailyAlertLimit: 5, |
| | | alertInterval: 5, |
| | | notifyUserIds: null, |
| | | status: "0", |
| | | remark: null |
| | | }; |
| | | this.resetForm("form"); |
| | | }, |
| | | /** æç´¢æé®æä½ */ |
| | | handleQuery() { |
| | | this.queryParams.pageNum = 1; |
| | | this.getList(); |
| | | }, |
| | | /** éç½®æé®æä½ */ |
| | | resetQuery() { |
| | | this.resetForm("queryForm"); |
| | | this.handleQuery(); |
| | | }, |
| | | // å¤éæ¡é䏿°æ® |
| | | handleSelectionChange(selection) { |
| | | this.ids = selection.map(item => item.configId); |
| | | this.single = selection.length !== 1; |
| | | this.multiple = !selection.length; |
| | | }, |
| | | /** é
ç½®ç±»åæ¹å */ |
| | | handleConfigTypeChange(value) { |
| | | if (value === 'GLOBAL') { |
| | | this.form.deptId = null; |
| | | this.form.vehicleId = null; |
| | | } else if (value === 'DEPT') { |
| | | this.form.vehicleId = null; |
| | | } else if (value === 'VEHICLE') { |
| | | this.form.deptId = null; |
| | | } |
| | | }, |
| | | /** ç¶æä¿®æ¹ */ |
| | | handleStatusChange(row) { |
| | | let text = row.status === "0" ? "å¯ç¨" : "åç¨"; |
| | | this.$modal.confirm('确认è¦"' + text + '""' + row.configId + '"é
ç½®åï¼').then(function() { |
| | | return updateVehicleAlertConfig(row); |
| | | }).then(() => { |
| | | this.$modal.msgSuccess(text + "æå"); |
| | | }).catch(function() { |
| | | row.status = row.status === "0" ? "1" : "0"; |
| | | }); |
| | | }, |
| | | /** æ°å¢æé®æä½ */ |
| | | handleAdd() { |
| | | this.reset(); |
| | | this.open = true; |
| | | this.title = "æ·»å 车è¾åè¦é
ç½®"; |
| | | }, |
| | | /** ä¿®æ¹æé®æä½ */ |
| | | handleUpdate(row) { |
| | | this.reset(); |
| | | const configId = row.configId || this.ids[0]; |
| | | getVehicleAlertConfig(configId).then(response => { |
| | | this.form = response.data; |
| | | this.open = true; |
| | | this.title = "ä¿®æ¹è½¦è¾åè¦é
ç½®"; |
| | | }); |
| | | }, |
| | | /** æäº¤æé® */ |
| | | submitForm() { |
| | | this.$refs["form"].validate(valid => { |
| | | if (valid) { |
| | | // æ ¹æ®é
ç½®ç±»åæ¸
空ä¸éè¦çåæ®µ |
| | | if (this.form.configType === 'GLOBAL') { |
| | | this.form.deptId = null; |
| | | this.form.vehicleId = null; |
| | | } else if (this.form.configType === 'DEPT') { |
| | | this.form.vehicleId = null; |
| | | } else if (this.form.configType === 'VEHICLE') { |
| | | this.form.deptId = null; |
| | | } |
| | | |
| | | if (this.form.configId != null) { |
| | | updateVehicleAlertConfig(this.form).then(response => { |
| | | this.$modal.msgSuccess("ä¿®æ¹æå"); |
| | | this.open = false; |
| | | this.getList(); |
| | | }); |
| | | } else { |
| | | addVehicleAlertConfig(this.form).then(response => { |
| | | this.$modal.msgSuccess("æ°å¢æå"); |
| | | this.open = false; |
| | | this.getList(); |
| | | }); |
| | | } |
| | | } |
| | | }); |
| | | }, |
| | | /** å é¤æé®æä½ */ |
| | | handleDelete(row) { |
| | | const configIds = row.configId || this.ids; |
| | | this.$modal.confirm('æ¯å¦ç¡®è®¤å é¤é
ç½®ç¼å·ä¸º"' + configIds + '"çæ°æ®é¡¹ï¼').then(function() { |
| | | return delVehicleAlertConfig(configIds); |
| | | }).then(() => { |
| | | this.getList(); |
| | | this.$modal.msgSuccess("å 餿å"); |
| | | }).catch(() => {}); |
| | | }, |
| | | /** å¯¼åºæé®æä½ */ |
| | | handleExport() { |
| | | this.download('system/vehicleAlertConfig/export', { |
| | | ...this.queryParams |
| | | }, `车è¾åè¦é
ç½®_${new Date().getTime()}.xlsx`) |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .mb8 { |
| | | margin-bottom: 8px; |
| | | } |
| | | </style> |
| New file |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <el-card class="box-card"> |
| | | <div slot="header" class="clearfix"> |
| | | <span>车è¾åæ¥ç®¡ç</span> |
| | | <el-button |
| | | style="float: right; padding: 3px 0" |
| | | type="text" |
| | | @click="handleRefresh" |
| | | > |
| | | <i class="el-icon-refresh"></i> å·æ° |
| | | </el-button> |
| | | </div> |
| | | |
| | | <!-- 说ææç¤º --> |
| | | <el-alert |
| | | title="åè½è¯´æ" |
| | | type="info" |
| | | :closable="false" |
| | | style="margin-bottom: 15px" |
| | | > |
| | | <div> |
| | | æ¬é¡µé¢ç¨äºæ¥çæ§ç³»ç»ä¸çè½¦è¾æ°æ®ï¼å¹¶æå¨åæ¥æªåæ¥ç车è¾å°æ°ç³»ç»ã<br/> |
| | | <strong>已忥</strong>ï¼è½¦è¾å·²åå¨äºæ°ç³»ç»ä¸ï¼<strong>æªåæ¥</strong>ï¼è½¦è¾è¿æªåæ¥ï¼å¯æå¨æ·»å å°æ°ç³»ç»ã |
| | | </div> |
| | | </el-alert> |
| | | |
| | | <!-- ç鿡件 --> |
| | | <el-form :inline="true" class="filter-form"> |
| | | <el-form-item label="åæ¥ç¶æ"> |
| | | <el-select v-model="filterSynced" placeholder="è¯·éæ©" @change="handleFilter" clearable> |
| | | <el-option label="å
¨é¨" :value="null" /> |
| | | <el-option label="æªåæ¥" :value="false" /> |
| | | <el-option label="已忥" :value="true" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-form> |
| | | |
| | | <!-- æ°æ®è¡¨æ ¼ --> |
| | | <el-table |
| | | v-loading="loading" |
| | | :data="filteredVehicleList" |
| | | style="width: 100%" |
| | | border |
| | | > |
| | | <el-table-column label="车è¾ID(CarID)" prop="carId" width="120" align="center" /> |
| | | <el-table-column label="车çå·" prop="vehicleNo" width="150" align="center"> |
| | | <template slot-scope="scope"> |
| | | <el-tag>{{ scope.row.vehicleNo }}</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="åæ®ç±»åç¼ç " prop="carOrdClass" width="120" align="center" /> |
| | | <el-table-column label="å½å±åå
¬å¸" prop="deptName" width="150" align="center" /> |
| | | <el-table-column label="åæ¥ç¶æ" prop="synced" width="120" align="center"> |
| | | <template slot-scope="scope"> |
| | | <el-tag :type="scope.row.synced ? 'success' : 'warning'"> |
| | | {{ scope.row.synced ? '已忥' : 'æªåæ¥' }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="æ°ç³»ç»è½¦è¾ID" prop="vehicleId" width="130" align="center"> |
| | | <template slot-scope="scope"> |
| | | <span v-if="scope.row.vehicleId">{{ scope.row.vehicleId }}</span> |
| | | <span v-else style="color: #909399">-</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="æä½" align="center" class-name="small-padding fixed-width"> |
| | | <template slot-scope="scope"> |
| | | <el-button |
| | | v-if="!scope.row.synced" |
| | | size="mini" |
| | | type="primary" |
| | | icon="el-icon-plus" |
| | | @click="handleSync(scope.row)" |
| | | v-hasPermi="['system:vehicleSync:sync']" |
| | | > |
| | | 忥 |
| | | </el-button> |
| | | <el-tag v-else type="info">已忥</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </el-card> |
| | | |
| | | <!-- åæ¥å¯¹è¯æ¡ --> |
| | | <el-dialog |
| | | title="æå¨åæ¥è½¦è¾" |
| | | :visible.sync="syncDialogVisible" |
| | | width="500px" |
| | | append-to-body |
| | | > |
| | | <el-form ref="syncForm" :model="syncForm" :rules="syncRules" label-width="100px"> |
| | | <el-form-item label="æ§ç³»ç»ID" prop="carId"> |
| | | <el-input v-model="syncForm.carId" disabled /> |
| | | </el-form-item> |
| | | <el-form-item label="车çå·" prop="vehicleNo"> |
| | | <el-input v-model="syncForm.vehicleNo" disabled /> |
| | | </el-form-item> |
| | | <el-form-item label="å½å±åå
¬å¸" prop="deptId"> |
| | | <el-select v-model="syncForm.deptId" placeholder="è¯·éæ©å½å±åå
¬å¸" clearable style="width: 100%"> |
| | | <el-option |
| | | v-for="dept in deptOptions" |
| | | :key="dept.deptId" |
| | | :label="dept.deptName" |
| | | :value="dept.deptId" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-form> |
| | | <div slot="footer" class="dialog-footer"> |
| | | <el-button @click="syncDialogVisible = false">å æ¶</el-button> |
| | | <el-button type="primary" @click="submitSync" :loading="syncLoading">ç¡® å®</el-button> |
| | | </div> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import { listVehicleSync, syncVehicle } from "@/api/system/vehicleSync"; |
| | | import { listDept } from "@/api/system/dept"; |
| | | |
| | | export default { |
| | | name: "VehicleSync", |
| | | data() { |
| | | return { |
| | | // å è½½ç¶æ |
| | | loading: true, |
| | | // 车è¾å表ï¼åå§æ°æ®ï¼ |
| | | vehicleList: [], |
| | | // çéåç车è¾å表 |
| | | filteredVehicleList: [], |
| | | // ç鿡件ï¼åæ¥ç¶æ |
| | | filterSynced: null, |
| | | // åæ¥å¯¹è¯æ¡æ¾ç¤ºç¶æ |
| | | syncDialogVisible: false, |
| | | // 忥å è½½ç¶æ |
| | | syncLoading: false, |
| | | // åæ¥è¡¨å |
| | | syncForm: { |
| | | carId: null, |
| | | vehicleNo: null, |
| | | deptId: null |
| | | }, |
| | | // é¨é¨æ é项 |
| | | deptOptions: [], |
| | | // è¡¨åæ ¡éªè§å |
| | | syncRules: { |
| | | deptId: [ |
| | | { required: true, message: "è¯·éæ©å½å±åå
¬å¸", trigger: "change" } |
| | | ] |
| | | } |
| | | }; |
| | | }, |
| | | created() { |
| | | this.getList(); |
| | | this.getDeptList(); |
| | | }, |
| | | methods: { |
| | | /** æ¥è¯¢è½¦è¾åæ¥å表 */ |
| | | getList() { |
| | | this.loading = true; |
| | | listVehicleSync().then(response => { |
| | | this.vehicleList = response.rows; |
| | | this.applyFilter(); |
| | | this.loading = false; |
| | | }).catch(() => { |
| | | this.loading = false; |
| | | }); |
| | | }, |
| | | /** åºç¨ç鿡件 */ |
| | | applyFilter() { |
| | | if (this.filterSynced === null) { |
| | | // æ¾ç¤ºå
¨é¨ |
| | | this.filteredVehicleList = this.vehicleList; |
| | | } else { |
| | | // æ ¹æ®åæ¥ç¶æçé |
| | | this.filteredVehicleList = this.vehicleList.filter( |
| | | item => item.synced === this.filterSynced |
| | | ); |
| | | } |
| | | }, |
| | | /** ç鿡件åå */ |
| | | handleFilter() { |
| | | this.applyFilter(); |
| | | }, |
| | | /** è·åé¨é¨å表ï¼åªæ¾ç¤ºåå
¬å¸ï¼parent_id=100ï¼ */ |
| | | getDeptList() { |
| | | listDept({ parentId: 100 }).then(response => { |
| | | // è¿æ»¤åºåå
¬å¸ï¼parent_id=100çé¨é¨ï¼ |
| | | if (response.data) { |
| | | this.deptOptions = response.data.filter(dept => dept.parentId === "100"); |
| | | } else { |
| | | this.deptOptions = []; |
| | | } |
| | | }); |
| | | }, |
| | | /** å·æ°å表 */ |
| | | handleRefresh() { |
| | | this.getList(); |
| | | this.$modal.msgSuccess("å·æ°æå"); |
| | | }, |
| | | /** 忥æé®æä½ */ |
| | | handleSync(row) { |
| | | this.syncForm = { |
| | | carId: row.carId, |
| | | vehicleNo: row.vehicleNo, |
| | | deptId: row.deptId || null |
| | | }; |
| | | this.syncDialogVisible = true; |
| | | this.$nextTick(() => { |
| | | this.$refs["syncForm"].clearValidate(); |
| | | }); |
| | | }, |
| | | /** æäº¤åæ¥ */ |
| | | submitSync() { |
| | | this.$refs["syncForm"].validate(valid => { |
| | | if (valid) { |
| | | this.syncLoading = true; |
| | | syncVehicle(this.syncForm).then(response => { |
| | | this.$modal.msgSuccess("忥æå"); |
| | | this.syncDialogVisible = false; |
| | | this.syncLoading = false; |
| | | this.getList(); |
| | | }).catch(() => { |
| | | this.syncLoading = false; |
| | | }); |
| | | } |
| | | }); |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .box-card { |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .filter-form { |
| | | margin-bottom: 15px; |
| | | padding: 10px; |
| | | background-color: #f5f7fa; |
| | | border-radius: 4px; |
| | | } |
| | | </style> |
| New file |
| | |
| | | -- å»é¢åè¯æµè¯èåSQL |
| | | -- æ§è¡æ¤èæ¬åï¼éè¦ç»ç®¡çåè§è²åé
èåæé |
| | | |
| | | -- æ¥æ¾ç³»ç»ç®¡çèåID |
| | | SELECT @parentId := menu_id FROM sys_menu WHERE menu_name = 'ç³»ç»ç®¡ç' AND parent_id = 0; |
| | | |
| | | -- æå
¥å»é¢ç®¡çä¸çº§èå |
| | | INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, remark) |
| | | VALUES |
| | | ('å»é¢ç®¡ç', @parentId, 10, 'hospital', NULL, 1, 0, 'M', '0', '0', NULL, 'hospital', 'admin', sysdate(), 'å»é¢åè¯ç®¡ç'); |
| | | |
| | | -- è·ååæå
¥çå»é¢ç®¡çèåID |
| | | SELECT @hospitalMenuId := menu_id FROM sys_menu WHERE menu_name = 'å»é¢ç®¡ç' AND parent_id = @parentId; |
| | | |
| | | -- æå
¥å»é¢åè¯æµè¯äºçº§èå |
| | | INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, remark) |
| | | VALUES |
| | | ('å»é¢åè¯æµè¯', @hospitalMenuId, 1, 'tokenizer', 'system/hospital/tokenizer', 1, 0, 'C', '0', '0', 'system:hospital:tokenizer', 'search', 'admin', sysdate(), 'å»é¢åè¯ä¸æç´¢æµè¯å·¥å
·'); |
| | | |
| | | -- 说æï¼ |
| | | -- 1. æ§è¡æ¤èæ¬åï¼èåä¼åºç°å¨"ç³»ç»ç®¡ç"ä¸ |
| | | -- 2. éè¦å¨"ç³»ç»ç®¡ç > è§è²ç®¡ç"ä¸ç»ç¸åºè§è²åé
"å»é¢ç®¡ç"èåæé |
| | | -- 3. æéæ è¯ï¼system:hospital:tokenizer |
| | | -- 4. èåè·¯å¾ï¼ç³»ç»ç®¡ç > å»é¢ç®¡ç > å»é¢åè¯æµè¯ |
| New file |
| | |
| | | -- OCR模å宿´èåSQL |
| | | -- å
å«OCR管çãæµè¯ãé
ç½®åç½ç»è¯æåè½ |
| | | |
| | | -- è·åç³»ç»å·¥å
·èåIDï¼é常æ¯3ï¼ |
| | | SET @parentMenuId = (SELECT menu_id FROM sys_menu WHERE menu_name = 'ç³»ç»å·¥å
·' AND parent_id = 0 LIMIT 1); |
| | | |
| | | -- å¦ææ²¡æç³»ç»å·¥å
·èåï¼å使ç¨ç³»ç»ç®¡çï¼parent_id=1ï¼ |
| | | SET @parentMenuId = IFNULL(@parentMenuId, 1); |
| | | |
| | | -- æå
¥OCR管çèå |
| | | INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) |
| | | SELECT 'OCR管ç', @parentMenuId, 9, 'ocr', '', 1, 0, 'M', '0', '0', '', 'camera', 'admin', NOW(), '', NULL, 'OCRæå¡ç®¡çèå' |
| | | FROM DUAL |
| | | WHERE NOT EXISTS ( |
| | | SELECT 1 FROM sys_menu WHERE menu_name = 'OCR管ç' AND perms = '' |
| | | ); |
| | | |
| | | -- è·åOCR管çèåID |
| | | SET @ocrParentMenuId = (SELECT menu_id FROM sys_menu WHERE menu_name = 'OCR管ç' LIMIT 1); |
| | | |
| | | -- æå
¥OCRæµè¯åèå |
| | | INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) |
| | | SELECT 'OCRæµè¯', @ocrParentMenuId, 1, 'ocr', 'system/ocr/index', 1, 0, 'C', '0', '0', 'system:ocr:test', 'eye-open', 'admin', NOW(), '', NULL, 'OCRå¾åè¯å«æµè¯é¡µé¢' |
| | | FROM DUAL |
| | | WHERE NOT EXISTS ( |
| | | SELECT 1 FROM sys_menu WHERE menu_name = 'OCRæµè¯' AND perms = 'system:ocr:test' |
| | | ); |
| | | |
| | | -- æå
¥OCRé
ç½®åèå |
| | | INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) |
| | | SELECT 'OCRé
ç½®', @ocrParentMenuId, 2, 'config', 'system/ocr/config', 1, 0, 'C', '0', '0', 'system:ocr:config', 'setting', 'admin', NOW(), '', NULL, 'OCRæå¡é
置页é¢' |
| | | FROM DUAL |
| | | WHERE NOT EXISTS ( |
| | | SELECT 1 FROM sys_menu WHERE menu_name = 'OCRé
ç½®' AND perms = 'system:ocr:config' |
| | | ); |
| | | |
| | | -- æå
¥OCRè¯å«æéæé® |
| | | INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) |
| | | SELECT 'OCRè¯å«', (SELECT menu_id FROM sys_menu WHERE perms = 'system:ocr:test' LIMIT 1), 1, '#', '', 1, 0, 'F', '0', '0', 'system:ocr:recognize', '#', 'admin', NOW(), '', NULL, 'OCRå¾åè¯å«æé' |
| | | FROM DUAL |
| | | WHERE NOT EXISTS ( |
| | | SELECT 1 FROM sys_menu WHERE perms = 'system:ocr:recognize' |
| | | ); |
| | | |
| | | -- æå
¥OCRæå¡æä¾åæ¥è¯¢æéæé® |
| | | INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) |
| | | SELECT 'OCRæå¡åæ¥è¯¢', (SELECT menu_id FROM sys_menu WHERE perms = 'system:ocr:test' LIMIT 1), 2, '#', '', 1, 0, 'F', '0', '0', 'system:ocr:providers', '#', 'admin', NOW(), '', NULL, 'OCRæå¡æä¾åæ¥è¯¢æé' |
| | | FROM DUAL |
| | | WHERE NOT EXISTS ( |
| | | SELECT 1 FROM sys_menu WHERE perms = 'system:ocr:providers' |
| | | ); |
| | | |
| | | -- æå
¥ç½ç»è¯æèå |
| | | INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) |
| | | SELECT 'ç½ç»è¯æ', @parentMenuId, 10, 'diag', '', 1, 0, 'M', '0', '0', '', 'link', 'admin', NOW(), '', NULL, 'ç½ç»è¯æç®¡çèå' |
| | | FROM DUAL |
| | | WHERE NOT EXISTS ( |
| | | SELECT 1 FROM sys_menu WHERE menu_name = 'ç½ç»è¯æ' AND perms = '' |
| | | ); |
| | | |
| | | -- æå
¥OCRç½ç»è¯æåèå |
| | | SET @diagParentMenuId = (SELECT menu_id FROM sys_menu WHERE menu_name = 'ç½ç»è¯æ' LIMIT 1); |
| | | INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) |
| | | SELECT 'OCRè¿æ¥è¯æ', @diagParentMenuId, 1, 'ocrConnection', 'system/diag/ocrConnection', 1, 0, 'C', '0', '0', 'system:diag:ocr', 'monitor', 'admin', NOW(), '', NULL, 'OCRæå¡è¿æ¥è¯æé¡µé¢' |
| | | FROM DUAL |
| | | WHERE NOT EXISTS ( |
| | | SELECT 1 FROM sys_menu WHERE menu_name = 'OCRè¿æ¥è¯æ' AND perms = 'system:diag:ocr' |
| | | ); |
| | | |
| | | -- æ¥è¯¢ç»æ |
| | | SELECT |
| | | menu_id, |
| | | menu_name, |
| | | parent_id, |
| | | path, |
| | | component, |
| | | perms, |
| | | icon, |
| | | 'èåæ·»å æå' AS status |
| | | FROM sys_menu |
| | | WHERE menu_name IN ('OCR管ç', 'OCRæµè¯', 'OCRé
ç½®', 'OCRè¯å«', 'OCRæå¡åæ¥è¯¢', 'ç½ç»è¯æ', 'OCRè¿æ¥è¯æ') |
| | | ORDER BY parent_id, order_num; |
| New file |
| | |
| | | -- OCRæµè¯é¡µé¢èåSQL |
| | | -- 注æï¼è¯·æ ¹æ®å®é
æ
åµè°æ´parent_idï¼ç¶èåIDï¼ |
| | | |
| | | -- è·åç³»ç»å·¥å
·èåIDï¼é常æ¯3ï¼ |
| | | SET @parentMenuId = (SELECT menu_id FROM sys_menu WHERE menu_name = 'ç³»ç»å·¥å
·' AND parent_id = 0 LIMIT 1); |
| | | |
| | | -- å¦ææ²¡æç³»ç»å·¥å
·èåï¼å使ç¨ç³»ç»ç®¡çï¼parent_id=1ï¼ |
| | | SET @parentMenuId = IFNULL(@parentMenuId, 1); |
| | | |
| | | -- æå
¥OCRæµè¯èå |
| | | INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) |
| | | SELECT 'OCRæµè¯', @parentMenuId, 10, 'ocr', 'system/ocr/index', 1, 0, 'C', '0', '0', 'system:ocr:test', 'eye-open', 'admin', NOW(), '', NULL, 'OCRå¾åè¯å«æµè¯é¡µé¢' |
| | | FROM DUAL |
| | | WHERE NOT EXISTS ( |
| | | SELECT 1 FROM sys_menu WHERE menu_name = 'OCRæµè¯' AND perms = 'system:ocr:test' |
| | | ); |
| | | |
| | | -- è·ååæå
¥çOCRæµè¯èåID |
| | | SET @ocrMenuId = (SELECT menu_id FROM sys_menu WHERE perms = 'system:ocr:test' LIMIT 1); |
| | | |
| | | -- æå
¥OCRè¯å«æéæé® |
| | | INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) |
| | | SELECT 'OCRè¯å«', @ocrMenuId, 1, '#', '', 1, 0, 'F', '0', '0', 'system:ocr:recognize', '#', 'admin', NOW(), '', NULL, 'OCRå¾åè¯å«æé' |
| | | FROM DUAL |
| | | WHERE NOT EXISTS ( |
| | | SELECT 1 FROM sys_menu WHERE perms = 'system:ocr:recognize' |
| | | ); |
| | | |
| | | -- æ¥è¯¢ç»æ |
| | | SELECT |
| | | menu_id, |
| | | menu_name, |
| | | parent_id, |
| | | path, |
| | | component, |
| | | perms, |
| | | icon, |
| | | 'èåæ·»å æå' AS status |
| | | FROM sys_menu |
| | | WHERE menu_name IN ('OCRæµè¯', 'OCRè¯å«') |
| | | ORDER BY menu_id; |
| New file |
| | |
| | | -- 为å»é¢æ°æ®è¡¨æ·»å åè¯å段 |
| | | -- æ§è¡æ¶é´ï¼2026-01-20 |
| | | |
| | | -- æ·»å åè¯å段ï¼åå¨ä»¥éå·åéçå
³é®è¯ï¼ |
| | | ALTER TABLE `tb_hosp_data` |
| | | ADD COLUMN `hosp_keywords` varchar(4000) DEFAULT NULL COMMENT 'å»é¢ä¿¡æ¯åè¯ï¼éå·åéï¼' AFTER `hosp_level`; |
| | | |
| | | -- 为åè¯å段添å ç´¢å¼ä»¥æåæç´¢æ§è½ |
| | | CREATE INDEX idx_hosp_keywords ON tb_hosp_data(hosp_keywords); |
| New file |
| | |
| | | -- 车è¾å¼å¸¸è¿è¡åè¦è®°å½è¡¨ |
| | | CREATE TABLE `tb_vehicle_abnormal_alert` ( |
| | | `alert_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'åè¦ID', |
| | | `vehicle_id` bigint(20) NOT NULL COMMENT '车è¾ID', |
| | | `vehicle_no` varchar(20) DEFAULT NULL COMMENT '车çå·', |
| | | `alert_date` date NOT NULL COMMENT 'åè¦æ¥æ', |
| | | `alert_time` datetime NOT NULL COMMENT 'åè¦æ¶é´', |
| | | `mileage` decimal(10,3) DEFAULT 0.000 COMMENT '累计è¿è¡å
¬éæ°(å
Ž)', |
| | | `alert_type` varchar(20) DEFAULT 'NO_TASK_MILEAGE' COMMENT 'åè¦ç±»å(NO_TASK_MILEAGE-æ ä»»å¡è¶
å
Ž)', |
| | | `alert_reason` varchar(500) DEFAULT NULL COMMENT 'åè¦åå æè¿°', |
| | | `start_time` datetime DEFAULT NULL COMMENT 'å¼å§è¿è¡æ¶é´', |
| | | `end_time` datetime DEFAULT NULL COMMENT 'ç»æè¿è¡æ¶é´', |
| | | `alert_count` int(11) DEFAULT 1 COMMENT '彿¥åè¦æ¬¡æ°', |
| | | `status` char(1) DEFAULT '0' COMMENT 'ç¶æï¼0-æªå¤ç 1-å·²å¤ç 2-已忽ç¥ï¼', |
| | | `handler_id` bigint(20) DEFAULT NULL COMMENT 'å¤ç人ID', |
| | | `handler_name` varchar(64) DEFAULT NULL COMMENT 'å¤ç人å§å', |
| | | `handle_time` datetime DEFAULT NULL COMMENT 'å¤çæ¶é´', |
| | | `handle_remark` varchar(500) DEFAULT NULL COMMENT 'å¤ç夿³¨', |
| | | `notify_status` char(1) DEFAULT '0' COMMENT 'éç¥ç¶æï¼0-æªåé 1-å·²åé 2-åé失败ï¼', |
| | | `notify_time` datetime DEFAULT NULL COMMENT 'éç¥æ¶é´', |
| | | `notify_users` varchar(500) DEFAULT NULL COMMENT 'éç¥ç¨æ·IDå表ï¼éå·åéï¼', |
| | | `dept_id` bigint(20) DEFAULT NULL COMMENT 'å½å±é¨é¨ID', |
| | | `dept_name` varchar(100) DEFAULT NULL COMMENT 'å½å±é¨é¨åç§°', |
| | | `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT 'å建æ¶é´', |
| | | `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ´æ°æ¶é´', |
| | | PRIMARY KEY (`alert_id`), |
| | | KEY `idx_vehicle_id` (`vehicle_id`), |
| | | KEY `idx_alert_date` (`alert_date`), |
| | | KEY `idx_alert_time` (`alert_time`), |
| | | KEY `idx_vehicle_date` (`vehicle_id`, `alert_date`), |
| | | KEY `idx_dept_id` (`dept_id`), |
| | | KEY `idx_status` (`status`) |
| | | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='车è¾å¼å¸¸è¿è¡åè¦è®°å½è¡¨'; |
| | | |
| | | -- 车è¾å¼å¸¸åè¦é
置表 |
| | | DROP TABLE IF EXISTS `tb_vehicle_alert_config`; |
| | | CREATE TABLE `tb_vehicle_alert_config` ( |
| | | `config_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'é
ç½®ID', |
| | | `config_type` varchar(50) NOT NULL COMMENT 'é
置类å(GLOBAL-å
¨å±/DEPT-é¨é¨/VEHICLE-车è¾)', |
| | | `dept_id` bigint(20) DEFAULT NULL COMMENT 'é¨é¨IDï¼é¨é¨é
ç½®æ¶ä½¿ç¨ï¼', |
| | | `vehicle_id` bigint(20) DEFAULT NULL COMMENT '车è¾IDï¼è½¦è¾é
ç½®æ¶ä½¿ç¨ï¼', |
| | | `mileage_threshold` decimal(10,3) DEFAULT 10.000 COMMENT 'å
¬éæ°åè¦éå¼(å
Ž)', |
| | | `daily_alert_limit` int(11) DEFAULT 5 COMMENT 'æ¯æ¥æå¤§åè¦æ¬¡æ°', |
| | | `alert_interval` int(11) DEFAULT 5 COMMENT 'åè¦é´éæ¶é´(åé)', |
| | | `notify_user_ids` varchar(1000) DEFAULT NULL COMMENT 'éç¥ç¨æ·IDå表ï¼éå·åéï¼', |
| | | `status` char(1) DEFAULT '0' COMMENT 'ç¶æï¼0-å¯ç¨ 1-åç¨ï¼', |
| | | `create_by` varchar(64) DEFAULT '' COMMENT 'å建è
', |
| | | `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT 'å建æ¶é´', |
| | | `update_by` varchar(64) DEFAULT '' COMMENT 'æ´æ°è
', |
| | | `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ´æ°æ¶é´', |
| | | `remark` varchar(500) DEFAULT NULL COMMENT '夿³¨', |
| | | PRIMARY KEY (`config_id`), |
| | | UNIQUE KEY `uk_vehicle_config` (`config_type`, `vehicle_id`), |
| | | UNIQUE KEY `uk_dept_config` (`config_type`, `dept_id`), |
| | | KEY `idx_dept_id` (`dept_id`), |
| | | KEY `idx_vehicle_id` (`vehicle_id`), |
| | | KEY `idx_status` (`status`) |
| | | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='车è¾å¼å¸¸åè¦é
置表'; |
| | | |
| | | -- æå
¥å
¨å±é»è®¤é
ç½® |
| | | INSERT INTO `tb_vehicle_alert_config` ( |
| | | `config_type`, `dept_id`, `vehicle_id`, `mileage_threshold`, `daily_alert_limit`, |
| | | `alert_interval`, `status`, `create_by`, `remark` |
| | | ) VALUES ( |
| | | 'GLOBAL', NULL, NULL, 10.000, 5, 5, '0', 'admin', 'å
¨å±é»è®¤é
ç½®ï¼è½¦è¾æ ä»»å¡è¶
10å
¬éåè¦ï¼æ¯å¤©æå¤5次ï¼é´é5åé' |
| | | ); |
| | | |
| | | -- æ·»å ç³»ç»é
ç½®åæ° |
| | | INSERT INTO sys_config (config_name, config_key, config_value, config_type, remark, create_by, create_time) |
| | | VALUES ('车è¾å¼å¸¸åè¦å¯ç¨å¼å
³', 'vehicle.alert.enabled', 'true', 'Y', 'æ§å¶è½¦è¾å¼å¸¸è¿è¡åè¦åè½çæ»å¼å
³ãtrue=å¯ç¨ï¼false=ç¦ç¨', 'admin', NOW()) |
| | | ON DUPLICATE KEY UPDATE config_value = config_value; |
| | | |
| | | INSERT INTO sys_config (config_name, config_key, config_value, config_type, remark, create_by, create_time) |
| | | VALUES ('车è¾å¼å¸¸åè¦å
¬éæ°éå¼', 'vehicle.alert.mileage.threshold', '10', 'Y', 'è½¦è¾æ ä»»å¡è¿è¡è¶
è¿è¯¥å
¬éæ°æ¶è§¦ååè¦ï¼åä½ï¼å
¬éï¼', 'admin', NOW()) |
| | | ON DUPLICATE KEY UPDATE config_value = config_value; |
| | | |
| | | INSERT INTO sys_config (config_name, config_key, config_value, config_type, remark, create_by, create_time) |
| | | VALUES ('车è¾å¼å¸¸åè¦æ¯æ¥æ¬¡æ°éå¶', 'vehicle.alert.daily.limit', '5', 'Y', 'æ¯å°è½¦æ¯å¤©æå¤åè¦æ¬¡æ°', 'admin', NOW()) |
| | | ON DUPLICATE KEY UPDATE config_value = config_value; |
| | | |
| | | INSERT INTO sys_config (config_name, config_key, config_value, config_type, remark, create_by, create_time) |
| | | VALUES ('车è¾å¼å¸¸åè¦é´éæ¶é´', 'vehicle.alert.interval.minutes', '5', 'Y', 'åä¸è½¦è¾ä¸¤æ¬¡åè¦ä¹é´çæå°é´éæ¶é´ï¼åä½ï¼åéï¼', 'admin', NOW()) |
| | | ON DUPLICATE KEY UPDATE config_value = config_value; |
| | | |
| | | INSERT INTO sys_config (config_name, config_key, config_value, config_type, remark, create_by, create_time) |
| | | VALUES ('车è¾å¼å¸¸åè¦éç¥ç¨æ·', 'vehicle.alert.notify.users', '', 'Y', 'æ¥æ¶åè¦éç¥çç¨æ·IDå表ï¼å¤ä¸ªç¨æ·ç¨éå·åéãä¸ºç©ºæ¶æ ¹æ®è½¦è¾å½å±é¨é¨åéç»åå
¬å¸è´è´£äºº', 'admin', NOW()) |
| | | ON DUPLICATE KEY UPDATE config_value = config_value; |
| | | |
| | | INSERT INTO sys_config (config_name, config_key, config_value, config_type, remark, create_by, create_time) |
| | | VALUES ('车è¾å¼å¸¸åè¦çæ§æ¶é´çªå£', 'vehicle.alert.time.window', '10', 'Y', 'çæ§æ¶é´çªå£ï¼åä½ï¼åéï¼ï¼ç¨äºè®¡ç®è½¦è¾å¨è¯¥æ¶é´çªå£å
çè¿è¡å
¬éæ°', 'admin', NOW()) |
| | | ON DUPLICATE KEY UPDATE config_value = config_value; |
| | | |
| | | -- åå»ºå®æ¶ä»»å¡ |
| | | INSERT INTO sys_job (job_name, job_group, invoke_target, cron_expression, misfire_policy, concurrent, status, create_by, create_time, update_by, update_time, remark) |
| | | VALUES ('车è¾å¼å¸¸è¿è¡çæ§ä»»å¡', 'DEFAULT', 'vehicleAbnormalAlertTask.monitorVehicleAbnormalRunning()', '0 */5 * * * ?', '3', '0', '1', 'admin', NOW(), 'admin', NOW(), 'æ¯5åéæ§è¡ä¸æ¬¡ï¼çæ§è½¦è¾æ ä»»å¡è¶
å
¬éè¿è¡æ
åµ') |
| | | ON DUPLICATE KEY UPDATE invoke_target = invoke_target; |
| | | |
| | | -- ===================================================== |
| | | -- èåæéé
ç½® |
| | | -- ===================================================== |
| | | |
| | | -- 1. å建车è¾çæ§ç¶èåï¼å¦æä¸åå¨ï¼ |
| | | INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) |
| | | SELECT '车è¾çæ§', 0, 5, 'vehicle-monitor', NULL, 1, 0, 'M', '0', '0', '', 'monitor', 'admin', NOW(), 'admin', NOW(), '车è¾çæ§ç®¡çç®å½' |
| | | FROM DUAL |
| | | WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_name = '车è¾çæ§' AND menu_type = 'M'); |
| | | |
| | | -- è·å车è¾çæ§ç¶èåID |
| | | SET @vehicleMonitorMenuId = (SELECT menu_id FROM sys_menu WHERE menu_name = '车è¾çæ§' AND menu_type = 'M' LIMIT 1); |
| | | |
| | | -- 2. å建车è¾å¼å¸¸åè¦èå |
| | | INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) |
| | | SELECT '车è¾å¼å¸¸åè¦', @vehicleMonitorMenuId, 1, 'vehicleAlert', 'system/vehicleAlert/index', 1, 0, 'C', '0', '0', 'system:vehicleAlert:list', 'warning', 'admin', NOW(), 'admin', NOW(), '车è¾å¼å¸¸è¿è¡åè¦ç®¡ç' |
| | | FROM DUAL |
| | | WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_name = '车è¾å¼å¸¸åè¦' AND perms = 'system:vehicleAlert:list'); |
| | | |
| | | -- è·å车è¾å¼å¸¸åè¦èåID |
| | | SET @vehicleAlertMenuId = (SELECT menu_id FROM sys_menu WHERE menu_name = '车è¾å¼å¸¸åè¦' AND perms = 'system:vehicleAlert:list' LIMIT 1); |
| | | |
| | | -- 3. å建车è¾å¼å¸¸åè¦åè½æé® |
| | | -- 3.1 æ¥è¯¢æé® |
| | | INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) |
| | | SELECT 'åè¦æ¥è¯¢', @vehicleAlertMenuId, 1, '#', '', 1, 0, 'F', '0', '0', 'system:vehicleAlert:query', '#', 'admin', NOW(), 'admin', NOW(), '' |
| | | FROM DUAL |
| | | WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'system:vehicleAlert:query'); |
| | | |
| | | -- 3.2 å¤çåè¦æé® |
| | | INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) |
| | | SELECT 'å¤çåè¦', @vehicleAlertMenuId, 2, '#', '', 1, 0, 'F', '0', '0', 'system:vehicleAlert:handle', '#', 'admin', NOW(), 'admin', NOW(), '' |
| | | FROM DUAL |
| | | WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'system:vehicleAlert:handle'); |
| | | |
| | | -- 3.3 å é¤åè¦æé® |
| | | INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) |
| | | SELECT 'å é¤åè¦', @vehicleAlertMenuId, 3, '#', '', 1, 0, 'F', '0', '0', 'system:vehicleAlert:remove', '#', 'admin', NOW(), 'admin', NOW(), '' |
| | | FROM DUAL |
| | | WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'system:vehicleAlert:remove'); |
| | | |
| | | -- 3.4 导åºåè¦æé® |
| | | INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) |
| | | SELECT '导åºåè¦', @vehicleAlertMenuId, 4, '#', '', 1, 0, 'F', '0', '0', 'system:vehicleAlert:export', '#', 'admin', NOW(), 'admin', NOW(), '' |
| | | FROM DUAL |
| | | WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'system:vehicleAlert:export'); |
| | | |
| | | -- 4. å建åè¦é
置管çèå |
| | | INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) |
| | | SELECT 'åè¦é
置管ç', @vehicleMonitorMenuId, 2, 'vehicleAlertConfig', 'system/vehicleAlertConfig/index', 1, 0, 'C', '0', '0', 'system:vehicleAlertConfig:list', 'edit', 'admin', NOW(), 'admin', NOW(), '车è¾åè¦é
置管ç' |
| | | FROM DUAL |
| | | WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_name = 'åè¦é
置管ç' AND perms = 'system:vehicleAlertConfig:list'); |
| | | |
| | | -- è·ååè¦é
置管çèåID |
| | | SET @vehicleAlertConfigMenuId = (SELECT menu_id FROM sys_menu WHERE menu_name = 'åè¦é
置管ç' AND perms = 'system:vehicleAlertConfig:list' LIMIT 1); |
| | | |
| | | -- 5. å建åè¦é
置管çåè½æé® |
| | | -- 5.1 æ¥è¯¢æé® |
| | | INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) |
| | | SELECT 'é
ç½®æ¥è¯¢', @vehicleAlertConfigMenuId, 1, '#', '', 1, 0, 'F', '0', '0', 'system:vehicleAlertConfig:query', '#', 'admin', NOW(), 'admin', NOW(), '' |
| | | FROM DUAL |
| | | WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'system:vehicleAlertConfig:query'); |
| | | |
| | | -- 5.2 æ°å¢æé® |
| | | INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) |
| | | SELECT 'æ°å¢é
ç½®', @vehicleAlertConfigMenuId, 2, '#', '', 1, 0, 'F', '0', '0', 'system:vehicleAlertConfig:add', '#', 'admin', NOW(), 'admin', NOW(), '' |
| | | FROM DUAL |
| | | WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'system:vehicleAlertConfig:add'); |
| | | |
| | | -- 5.3 ä¿®æ¹æé® |
| | | INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) |
| | | SELECT 'ä¿®æ¹é
ç½®', @vehicleAlertConfigMenuId, 3, '#', '', 1, 0, 'F', '0', '0', 'system:vehicleAlertConfig:edit', '#', 'admin', NOW(), 'admin', NOW(), '' |
| | | FROM DUAL |
| | | WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'system:vehicleAlertConfig:edit'); |
| | | |
| | | -- 5.4 å é¤æé® |
| | | INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) |
| | | SELECT 'å é¤é
ç½®', @vehicleAlertConfigMenuId, 4, '#', '', 1, 0, 'F', '0', '0', 'system:vehicleAlertConfig:remove', '#', 'admin', NOW(), 'admin', NOW(), '' |
| | | FROM DUAL |
| | | WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'system:vehicleAlertConfig:remove'); |
| | | |
| | | -- 5.5 å¯¼åºæé® |
| | | INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) |
| | | SELECT '导åºé
ç½®', @vehicleAlertConfigMenuId, 5, '#', '', 1, 0, 'F', '0', '0', 'system:vehicleAlertConfig:export', '#', 'admin', NOW(), 'admin', NOW(), '' |
| | | FROM DUAL |
| | | WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'system:vehicleAlertConfig:export'); |
| New file |
| | |
| | | -- ===================================================== |
| | | -- 车è¾å¼å¸¸è¿è¡çæ§åè¦åè½ - æ°æ®åºåçº§èæ¬ |
| | | -- ç¨äºæ´æ°å·²åå¨çè¡¨ç»æ |
| | | -- ===================================================== |
| | | |
| | | -- æ£æ¥å¹¶ä¿®æ¹ tb_vehicle_alert_config è¡¨ç»æ |
| | | -- å¦æè¡¨å·²åå¨ä½åæ®µä¸æ£ç¡®ï¼éè¦å
å 餿§å段ï¼åæ·»å æ°å段 |
| | | |
| | | -- 1. å 餿§çå¯ä¸ç´¢å¼ |
| | | ALTER TABLE `tb_vehicle_alert_config` DROP INDEX IF EXISTS `uk_type_target`; |
| | | |
| | | -- 2. æ£æ¥æ¯å¦åå¨ target_id åæ®µï¼å¦æåå¨åå é¤ |
| | | SET @exist_target_id := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS |
| | | WHERE TABLE_SCHEMA = DATABASE() |
| | | AND TABLE_NAME = 'tb_vehicle_alert_config' |
| | | AND COLUMN_NAME = 'target_id'); |
| | | |
| | | SET @sql_drop_target_id = IF(@exist_target_id > 0, |
| | | 'ALTER TABLE `tb_vehicle_alert_config` DROP COLUMN `target_id`', |
| | | 'SELECT ''target_id column does not exist'''); |
| | | PREPARE stmt FROM @sql_drop_target_id; |
| | | EXECUTE stmt; |
| | | DEALLOCATE PREPARE stmt; |
| | | |
| | | -- 3. æ·»å dept_id åæ®µï¼å¦æä¸åå¨ï¼ |
| | | SET @exist_dept_id := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS |
| | | WHERE TABLE_SCHEMA = DATABASE() |
| | | AND TABLE_NAME = 'tb_vehicle_alert_config' |
| | | AND COLUMN_NAME = 'dept_id'); |
| | | |
| | | SET @sql_add_dept_id = IF(@exist_dept_id = 0, |
| | | 'ALTER TABLE `tb_vehicle_alert_config` ADD COLUMN `dept_id` bigint(20) DEFAULT NULL COMMENT ''é¨é¨IDï¼é¨é¨é
ç½®æ¶ä½¿ç¨ï¼'' AFTER `config_type`', |
| | | 'SELECT ''dept_id column already exists'''); |
| | | PREPARE stmt FROM @sql_add_dept_id; |
| | | EXECUTE stmt; |
| | | DEALLOCATE PREPARE stmt; |
| | | |
| | | -- 4. æ·»å vehicle_id åæ®µï¼å¦æä¸åå¨ï¼ |
| | | SET @exist_vehicle_id := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS |
| | | WHERE TABLE_SCHEMA = DATABASE() |
| | | AND TABLE_NAME = 'tb_vehicle_alert_config' |
| | | AND COLUMN_NAME = 'vehicle_id'); |
| | | |
| | | SET @sql_add_vehicle_id = IF(@exist_vehicle_id = 0, |
| | | 'ALTER TABLE `tb_vehicle_alert_config` ADD COLUMN `vehicle_id` bigint(20) DEFAULT NULL COMMENT ''车è¾IDï¼è½¦è¾é
ç½®æ¶ä½¿ç¨ï¼'' AFTER `dept_id`', |
| | | 'SELECT ''vehicle_id column already exists'''); |
| | | PREPARE stmt FROM @sql_add_vehicle_id; |
| | | EXECUTE stmt; |
| | | DEALLOCATE PREPARE stmt; |
| | | |
| | | -- 5. æ£æ¥æ¯å¦åå¨ notify_roles åæ®µï¼å¦æåå¨åå é¤ |
| | | SET @exist_notify_roles := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS |
| | | WHERE TABLE_SCHEMA = DATABASE() |
| | | AND TABLE_NAME = 'tb_vehicle_alert_config' |
| | | AND COLUMN_NAME = 'notify_roles'); |
| | | |
| | | SET @sql_drop_notify_roles = IF(@exist_notify_roles > 0, |
| | | 'ALTER TABLE `tb_vehicle_alert_config` DROP COLUMN `notify_roles`', |
| | | 'SELECT ''notify_roles column does not exist'''); |
| | | PREPARE stmt FROM @sql_drop_notify_roles; |
| | | EXECUTE stmt; |
| | | DEALLOCATE PREPARE stmt; |
| | | |
| | | -- 6. ä¿®æ¹ enabled åæ®µä¸º statusï¼å¦æåå¨ enabledï¼ |
| | | SET @exist_enabled := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS |
| | | WHERE TABLE_SCHEMA = DATABASE() |
| | | AND TABLE_NAME = 'tb_vehicle_alert_config' |
| | | AND COLUMN_NAME = 'enabled'); |
| | | |
| | | SET @sql_change_enabled = IF(@exist_enabled > 0, |
| | | 'ALTER TABLE `tb_vehicle_alert_config` CHANGE COLUMN `enabled` `status` char(1) DEFAULT ''0'' COMMENT ''ç¶æï¼0-å¯ç¨ 1-åç¨ï¼''', |
| | | 'SELECT ''enabled column does not exist, may already be status'''); |
| | | PREPARE stmt FROM @sql_change_enabled; |
| | | EXECUTE stmt; |
| | | DEALLOCATE PREPARE stmt; |
| | | |
| | | -- 7. å 餿§ç´¢å¼ |
| | | ALTER TABLE `tb_vehicle_alert_config` DROP INDEX IF EXISTS `idx_target_id`; |
| | | ALTER TABLE `tb_vehicle_alert_config` DROP INDEX IF EXISTS `idx_enabled`; |
| | | |
| | | -- 8. å建æ°çå¯ä¸ç´¢å¼ |
| | | -- å
æ£æ¥ç´¢å¼æ¯å¦åå¨ |
| | | SET @exist_uk_vehicle := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS |
| | | WHERE TABLE_SCHEMA = DATABASE() |
| | | AND TABLE_NAME = 'tb_vehicle_alert_config' |
| | | AND INDEX_NAME = 'uk_vehicle_config'); |
| | | |
| | | SET @sql_add_uk_vehicle = IF(@exist_uk_vehicle = 0, |
| | | 'ALTER TABLE `tb_vehicle_alert_config` ADD UNIQUE KEY `uk_vehicle_config` (`config_type`, `vehicle_id`)', |
| | | 'SELECT ''uk_vehicle_config index already exists'''); |
| | | PREPARE stmt FROM @sql_add_uk_vehicle; |
| | | EXECUTE stmt; |
| | | DEALLOCATE PREPARE stmt; |
| | | |
| | | SET @exist_uk_dept := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS |
| | | WHERE TABLE_SCHEMA = DATABASE() |
| | | AND TABLE_NAME = 'tb_vehicle_alert_config' |
| | | AND INDEX_NAME = 'uk_dept_config'); |
| | | |
| | | SET @sql_add_uk_dept = IF(@exist_uk_dept = 0, |
| | | 'ALTER TABLE `tb_vehicle_alert_config` ADD UNIQUE KEY `uk_dept_config` (`config_type`, `dept_id`)', |
| | | 'SELECT ''uk_dept_config index already exists'''); |
| | | PREPARE stmt FROM @sql_add_uk_dept; |
| | | EXECUTE stmt; |
| | | DEALLOCATE PREPARE stmt; |
| | | |
| | | -- 9. å建æ°çæ®éç´¢å¼ |
| | | SET @exist_idx_dept := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS |
| | | WHERE TABLE_SCHEMA = DATABASE() |
| | | AND TABLE_NAME = 'tb_vehicle_alert_config' |
| | | AND INDEX_NAME = 'idx_dept_id'); |
| | | |
| | | SET @sql_add_idx_dept = IF(@exist_idx_dept = 0, |
| | | 'ALTER TABLE `tb_vehicle_alert_config` ADD KEY `idx_dept_id` (`dept_id`)', |
| | | 'SELECT ''idx_dept_id index already exists'''); |
| | | PREPARE stmt FROM @sql_add_idx_dept; |
| | | EXECUTE stmt; |
| | | DEALLOCATE PREPARE stmt; |
| | | |
| | | SET @exist_idx_vehicle := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS |
| | | WHERE TABLE_SCHEMA = DATABASE() |
| | | AND TABLE_NAME = 'tb_vehicle_alert_config' |
| | | AND INDEX_NAME = 'idx_vehicle_id'); |
| | | |
| | | SET @sql_add_idx_vehicle = IF(@exist_idx_vehicle = 0, |
| | | 'ALTER TABLE `tb_vehicle_alert_config` ADD KEY `idx_vehicle_id` (`vehicle_id`)', |
| | | 'SELECT ''idx_vehicle_id index already exists'''); |
| | | PREPARE stmt FROM @sql_add_idx_vehicle; |
| | | EXECUTE stmt; |
| | | DEALLOCATE PREPARE stmt; |
| | | |
| | | SET @exist_idx_status := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS |
| | | WHERE TABLE_SCHEMA = DATABASE() |
| | | AND TABLE_NAME = 'tb_vehicle_alert_config' |
| | | AND INDEX_NAME = 'idx_status'); |
| | | |
| | | SET @sql_add_idx_status = IF(@exist_idx_status = 0, |
| | | 'ALTER TABLE `tb_vehicle_alert_config` ADD KEY `idx_status` (`status`)', |
| | | 'SELECT ''idx_status index already exists'''); |
| | | PREPARE stmt FROM @sql_add_idx_status; |
| | | EXECUTE stmt; |
| | | DEALLOCATE PREPARE stmt; |
| | | |
| | | -- 10. æ´æ°å·²æçå
¨å±é
ç½®è®°å½ |
| | | UPDATE `tb_vehicle_alert_config` |
| | | SET `dept_id` = NULL, `vehicle_id` = NULL |
| | | WHERE `config_type` = 'GLOBAL'; |
| | | |
| | | -- åçº§å®ææç¤º |
| | | SELECT 'æ°æ®åºåçº§å®æï¼tb_vehicle_alert_config è¡¨ç»æå·²æ´æ°' AS 'Status'; |
| New file |
| | |
| | | -- 车è¾åæ¥ç®¡çèå SQL |
| | | -- èå IDèªå¢ï¼å¯ä»¥æ ¹æ®ä½ çæ°æ®åºå®é
æ
åµè°æ´ |
| | | |
| | | -- ç¶èåï¼è½¦è¾ç®¡çï¼IDï¼å设为2020ï¼éè¦æ ¹æ®å®é
æ
åµè°æ´ |
| | | SET @parentMenuId = (SELECT menu_id FROM sys_menu WHERE menu_name = '车è¾ç®¡ç' AND menu_type = 'M' LIMIT 1); |
| | | |
| | | -- å¦ææ²¡æè½¦è¾ç®¡çç¶èåï¼å
å建 |
| | | INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) |
| | | SELECT '车è¾ç®¡ç', 0, 4, 'vehicle', NULL, 1, 0, 'M', '0', '0', '', 'car', 'admin', sysdate(), '', NULL, '车è¾ç®¡çç®å½' |
| | | FROM DUAL |
| | | WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE menu_name = '车è¾ç®¡ç' AND menu_type = 'M'); |
| | | |
| | | -- éæ°è·åç¶èåID |
| | | SET @parentMenuId = (SELECT menu_id FROM sys_menu WHERE menu_name = '车è¾ç®¡ç' AND menu_type = 'M' LIMIT 1); |
| | | |
| | | -- 车è¾åæ¥ç®¡çèå |
| | | INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) |
| | | SELECT '车è¾åæ¥', @parentMenuId, 5, 'vehicleSync', 'system/vehicleSync/index', 1, 0, 'C', '0', '0', 'system:vehicleSync:list', 'upload', 'admin', sysdate(), '', NULL, '车è¾åæ¥èå' |
| | | FROM DUAL |
| | | WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'system:vehicleSync:list'); |
| | | |
| | | -- è·å车è¾åæ¥èåID |
| | | SET @vehicleSyncMenuId = (SELECT menu_id FROM sys_menu WHERE perms = 'system:vehicleSync:list' LIMIT 1); |
| | | |
| | | -- 车è¾åæ¥æé® |
| | | INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) |
| | | SELECT 'åæ¥è½¦è¾', @vehicleSyncMenuId, 1, '#', '', 1, 0, 'F', '0', '0', 'system:vehicleSync:sync', '#', 'admin', sysdate(), '', NULL, '' |
| | | FROM DUAL |
| | | WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'system:vehicleSync:sync'); |
| | | |
| | | -- 车è¾åæ¥æ¥è¯¢æé® |
| | | INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) |
| | | SELECT 'æ¥è¯¢', @vehicleSyncMenuId, 2, '#', '', 1, 0, 'F', '0', '0', 'system:vehicleSync:query', '#', 'admin', sysdate(), '', NULL, '' |
| | | FROM DUAL |
| | | WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'system:vehicleSync:query'); |
| New file |
| | |
| | | # å»é¢ä¿¡æ¯åè¯æç´¢åè½è¯´æ |
| | | |
| | | ## åè½æ¦è¿° |
| | | |
| | | æ¬åè½å®ç°äºåºäºä¸æåè¯çå»é¢ä¿¡æ¯æºè½æç´¢,éè¿å¯¹å»é¢åç§°ãå°åçä¿¡æ¯è¿è¡åè¯å¤ç,æ¯ææ¨¡ç³å¹é
åæéæåº,大å¹
æåå»é¢æç´¢çåç¡®æ§åç¨æ·ä½éªã |
| | | |
| | | ## å®ç°å
容 |
| | | |
| | | ### 1. æ°æ®åºå±é¢ |
| | | |
| | | #### 1.1 æ°å¢åè¯å段 |
| | | - **æä»¶**: `sql/tb_hosp_data_add_keywords.sql` |
| | | - **å
容**: å¨ `tb_hosp_data` è¡¨ä¸æ·»å `hosp_keywords` åæ®µ |
| | | ```sql |
| | | ALTER TABLE `tb_hosp_data` |
| | | ADD COLUMN `hosp_keywords` varchar(500) DEFAULT NULL COMMENT 'å»é¢ä¿¡æ¯åè¯ï¼éå·åéï¼'; |
| | | |
| | | CREATE INDEX idx_hosp_keywords ON tb_hosp_data(hosp_keywords); |
| | | ``` |
| | | |
| | | ### 2. å®ä½ç±»å±é¢ |
| | | |
| | | #### 2.1 TbHospData å®ä½æ©å± |
| | | - **æä»¶**: `ruoyi-system/src/main/java/com/ruoyi/system/domain/TbHospData.java` |
| | | - **æ°å¢å段**: |
| | | - `hospKeywords` - å»é¢ä¿¡æ¯åè¯ï¼éå·åéï¼ |
| | | - **å
嫿¹æ³**: getterãsetter å toString |
| | | |
| | | ### 3. åè¯å·¥å
·ç±» |
| | | |
| | | #### 3.1 HospitalTokenizerUtil |
| | | - **æä»¶**: `ruoyi-common/src/main/java/com/ruoyi/common/utils/HospitalTokenizerUtil.java` |
| | | - **æ ¸å¿åè½**: |
| | | 1. **䏿åè¯**: ä½¿ç¨ N-Gram ç®æ³å¯¹å»é¢ä¿¡æ¯è¿è¡åè¯ï¼2-4å符ç»åï¼ |
| | | 2. **åç¨è¯è¿æ»¤**: èªå¨è¿æ»¤"å»é¢"ã"å¸"ã"ç"ç常è§åç¨è¯ |
| | | 3. **å
³é®è¯æå**: ä»å»é¢åç§°ãç®ç§°ãçå¸åºãå°å䏿åå
³é®è¯ |
| | | 4. **å¹é
度计ç®**: 计ç®ä¸¤ä¸ªåè¯éåçå¹é
åæ° |
| | | |
| | | - **ä¸»è¦æ¹æ³**: |
| | | ```java |
| | | // çæå»é¢ä¿¡æ¯çåè¯ |
| | | public static String tokenize(String hospName, String hospShort, |
| | | String province, String city, |
| | | String area, String address) |
| | | |
| | | // 对æç´¢ææ¬è¿è¡åè¯ |
| | | public static String tokenizeSearchText(String text) |
| | | |
| | | // 计ç®ä¸¤ä¸ªåè¯éåçå¹é
度 |
| | | public static int calculateMatchScore(String keywords1, String keywords2) |
| | | ``` |
| | | |
| | | ### 4. Service å± |
| | | |
| | | #### 4.1 ITbHospDataService æ¥å£æ©å± |
| | | - **æä»¶**: `ruoyi-system/src/main/java/com/ruoyi/system/service/ITbHospDataService.java` |
| | | - **æ°å¢æ¹æ³**: |
| | | ```java |
| | | // æ¹éçæå¹¶æ´æ°ææå»é¢çåè¯ |
| | | int generateAllHospitalKeywords(); |
| | | |
| | | // 为å个å»é¢çæåè¯ |
| | | String generateKeywordsForHospital(TbHospData tbHospData); |
| | | ``` |
| | | |
| | | #### 4.2 TbHospDataServiceImpl å®ç° |
| | | - **æä»¶**: `ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TbHospDataServiceImpl.java` |
| | | - **åè½**: |
| | | - æ¹éå¤çææå»é¢æ°æ®,çæåè¯å¹¶æ´æ°æ°æ®åº |
| | | - å个å»é¢åè¯çæ,卿°å¢/æ´æ°å»é¢æ¶èªå¨è°ç¨ |
| | | |
| | | #### 4.3 HospDataSyncServiceImpl éæ |
| | | - **æä»¶**: `ruoyi-system/src/main/java/com/ruoyi/system/service/impl/HospDataSyncServiceImpl.java` |
| | | - **åè½**: å¨å»é¢åæ¥æ¶èªå¨çæåè¯ |
| | | - ä» SQL Server 忥å»é¢æ°æ®æ¶,èªå¨è°ç¨åè¯çæ |
| | | - ç¡®ä¿ææå»é¢æ°æ®é½æææ°çåè¯ä¿¡æ¯ |
| | | |
| | | ### 5. Controller å± |
| | | |
| | | #### 5.1 HospDataController æ©å± |
| | | - **æä»¶**: `ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/HospDataController.java` |
| | | |
| | | ##### 5.1.1 æ¹éçæåè¯æ¥å£ |
| | | - **æ¥å£**: `GET /system/hospital/generateKeywords` |
| | | - **åè½**: æ¹éçæææå»é¢çåè¯ï¼ç®¡ç忥å£ï¼ |
| | | - **è¿å**: æ´æ°çå»é¢æ°é |
| | | - **使ç¨åºæ¯**: |
| | | - 馿¬¡é¨ç½²æ¶åå§åææå»é¢åè¯ |
| | | - åè¯ç®æ³å级åéæ°çæåè¯ |
| | | |
| | | ##### 5.1.2 åºäºåè¯å¹é
æç´¢æ¥å£ |
| | | - **æ¥å£**: `GET /system/hospital/searchByKeywords` |
| | | - **åæ°**: |
| | | - `searchText` (å¿
å¡«): æç´¢ææ¬ï¼å»é¢åç§°ãå°åçï¼ |
| | | - `pageSize` (å¯é): è¿åç»ææ°é,é»è®¤ 50 |
| | | - **åè½æµç¨**: |
| | | 1. 对åç«¯ä¼ å
¥çæç´¢ææ¬è¿è¡åè¯ |
| | | 2. æ¥è¯¢æææ£å¸¸ç¶æçå»é¢æ°æ® |
| | | 3. è®¡ç®æ¯ä¸ªå»é¢ä¸æç´¢ææ¬çå¹é
åæ° |
| | | 4. æå¹é
åæ°éåºæåºï¼æéæåºï¼ |
| | | 5. è¿åå¹é
çå»é¢å表 |
| | | |
| | | - **è¿å示ä¾**: |
| | | ```json |
| | | { |
| | | "code": 200, |
| | | "msg": "æ¥è¯¢æå", |
| | | "data": [ |
| | | { |
| | | "hospId": 1001, |
| | | "hospName": "å京ååå»é¢", |
| | | "hospShort": "ååå»é¢", |
| | | "hopsProvince": "å京å¸", |
| | | "hopsCity": "å京å¸", |
| | | "hopsArea": "ä¸ååº", |
| | | "hospAddress": "ä¸ååºå¸
åºå1å·" |
| | | }, |
| | | // ... æ´å¤å»é¢æ°æ®ï¼æå¹é
度æåºï¼ |
| | | ] |
| | | } |
| | | ``` |
| | | |
| | | ### 6. Mapper XML æ´æ° |
| | | |
| | | #### 6.1 TbHospDataMapper.xml |
| | | - **æä»¶**: `ruoyi-system/src/main/resources/mapper/system/TbHospDataMapper.xml` |
| | | - **æ´æ°å
容**: |
| | | - ResultMap æ·»å `hospKeywords` åæ®µæ å° |
| | | - SELECT è¯å¥å
å« `hosp_keywords` åæ®µ |
| | | - INSERT å UPDATE æ¯æ `hosp_keywords` åæ®µ |
| | | |
| | | ## 使ç¨è¯´æ |
| | | |
| | | ### é¨ç½²æ¥éª¤ |
| | | |
| | | 1. **æ§è¡æ°æ®åºèæ¬** |
| | | ```bash |
| | | mysql -uç¨æ·å -pæ°æ®åºå < sql/tb_hosp_data_add_keywords.sql |
| | | ``` |
| | | |
| | | 2. **éå¯åºç¨** |
| | | ```bash |
| | | ./ry.sh restart |
| | | # æ Windows ä¸ |
| | | ry.bat |
| | | ``` |
| | | |
| | | 3. **åå§ååè¯æ°æ®** |
| | | è°ç¨æ¹éçæåè¯æ¥å£: |
| | | ```bash |
| | | GET http://localhost:8080/system/hospital/generateKeywords |
| | | ``` |
| | | |
| | | ### å端éæç¤ºä¾ |
| | | |
| | | #### 使ç¨åè¯æç´¢æ¥å£ |
| | | ```javascript |
| | | // å¨ uni-app ä¸è°ç¨ |
| | | uni.request({ |
| | | url: '/system/hospital/searchByKeywords', |
| | | method: 'GET', |
| | | data: { |
| | | searchText: 'å京ååä¸ååº', |
| | | pageSize: 20 |
| | | }, |
| | | success: (res) => { |
| | | if (res.data.code === 200) { |
| | | // å»é¢å表已æå¹é
度æåº |
| | | const hospitals = res.data.data; |
| | | console.log('æ¾å°å»é¢:', hospitals.length); |
| | | } |
| | | } |
| | | }); |
| | | ``` |
| | | |
| | | #### Vue.js ç¤ºä¾ |
| | | ```javascript |
| | | // å¨ Vue ç»ä»¶ä¸ |
| | | methods: { |
| | | async searchHospitals(searchText) { |
| | | try { |
| | | const response = await this.$http.get('/system/hospital/searchByKeywords', { |
| | | params: { |
| | | searchText: searchText, |
| | | pageSize: 50 |
| | | } |
| | | }); |
| | | |
| | | if (response.data.code === 200) { |
| | | this.hospitals = response.data.data; |
| | | // æ°æ®å·²æå¹é
度æåº,å¹é
è¶å¥½çå»é¢è¶é å |
| | | } |
| | | } catch (error) { |
| | | console.error('æç´¢å¤±è´¥:', error); |
| | | } |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | ## åè½ç¹ç¹ |
| | | |
| | | ### 1. æºè½åè¯ |
| | | - ä½¿ç¨ N-Gram ç®æ³çæ 2-4 å符çå
³é®è¯ç»å |
| | | - èªå¨æååä¸ªä¸æå符ä½ä¸ºè¡¥å
å
³é®è¯ |
| | | - æ¯æå¯¹å»é¢åç§°ãç®ç§°ãçå¸åºãå°åçå¤ä¸ªå段åè¯ |
| | | |
| | | ### 2. åç¨è¯è¿æ»¤ |
| | | èªå¨è¿æ»¤ä»¥ä¸å¸¸è§åç¨è¯: |
| | | - éç¨è¯: "å»é¢"ã"è¯æ"ã"å«ç"ã"é¢" |
| | | - å°åºè¯: "å¸"ã"ç"ã"å¿"ã"åº"ã"é"ã"乡" |
| | | - ä½ç½®è¯: "è¡é"ã"è·¯"ã"å·"ã"æ "ã"åå
"ã"室"ã"å±"ã"楼" |
| | | - è¿æ¥è¯: "ç"ã"äº"ã"å¨"ã"ä¸"ã"å"ã"å"ç |
| | | |
| | | ### 3. æéæåº |
| | | - æ ¹æ®å¹é
çå
³é®è¯æ°é计ç®å¹é
åæ° |
| | | - å¹é
çåè¯è¶å¤,æåè¶é å |
| | | - 精确å¹é
ä¼å
äºæ¨¡ç³å¹é
|
| | | |
| | | ### 4. èªå¨åéæ |
| | | - å»é¢åæ¥æ¶èªå¨çæåè¯ |
| | | - æ°å¢ææ´æ°å»é¢æ¶èªå¨æ´æ°åè¯ |
| | | - æ éæå¨å¹²é¢,ç¡®ä¿æ°æ®ä¸è´æ§ |
| | | |
| | | ## æ§è½ä¼å |
| | | |
| | | 1. **ç´¢å¼ä¼å**: å¨ `hosp_keywords` åæ®µä¸å建索å¼,æåæ¥è¯¢æ§è½ |
| | | 2. **å
å计ç®**: å¹é
度计ç®å¨å
åä¸è¿è¡,é¿å
æ°æ®åºåå |
| | | 3. **ç»æéå¶**: æ¯æ `pageSize` åæ°éå¶è¿åæ°é,åå°ç½ç»ä¼ è¾ |
| | | |
| | | ## 注æäºé¡¹ |
| | | |
| | | 1. **馿¬¡é¨ç½²**: å¿
é¡»è°ç¨ `/generateKeywords` æ¥å£åå§åææå»é¢çåè¯æ°æ® |
| | | 2. **æ°æ®åæ¥**: å»é¢åæ¥ä¼èªå¨æ´æ°åè¯,æ éé¢å¤æä½ |
| | | 3. **åè¯è´¨é**: åè¯ç®æ³åºäºç®åç N-Gram,对äºå¤æçå»é¢åç§°å¯è½éè¦ä¼å |
| | | 4. **åç¨è¯ç»´æ¤**: 坿 ¹æ®å®é
ä¸å¡éæ±å¨ `HospitalTokenizerUtil` ä¸è°æ´åç¨è¯å表 |
| | | |
| | | ## åç»ä¼å建议 |
| | | |
| | | 1. **éæç¬¬ä¸æ¹åè¯**: |
| | | - å¯èèéæ IK AnalyzerãHanLP çä¸ä¸ä¸æåè¯åº |
| | | - æååè¯å确度åå¬åç |
| | | |
| | | 2. **æ¼é³æ¯æ**: |
| | | - å¢å æ¼é³ç´¢å¼,æ¯ææ¼é³é¦åæ¯æç´¢ |
| | | - ä¾å¦: "bjxhyy" å¯ä»¥å¹é
"å京ååå»é¢" |
| | | |
| | | 3. **åä¹è¯æ©å±**: |
| | | - æ¯æåä¹è¯å¹é
|
| | | - ä¾å¦: "人æ°å»é¢" å "人æ°å»çä¸å¿" |
| | | |
| | | 4. **æç´¢åå²**: |
| | | - è®°å½ç¨æ·æç´¢åå² |
| | | - æ ¹æ®å岿°æ®ä¼åæåºç®æ³ |
| | | |
| | | 5. **å°çä½ç½®æé**: |
| | | - ç»åç¨æ·ä½ç½®ä¿¡æ¯ |
| | | - ä¼å
è¿åéè¿çå»é¢ |
| | | |
| | | ## ææ¯æ |
| | | |
| | | - **åç«¯æ¡æ¶**: Spring Boot + MyBatis |
| | | - **æ°æ®åº**: MySQL 5.7+ |
| | | - **å·¥å
·ç±»**: Apache Commons Lang3 |
| | | - **æ¥å¿**: SLF4J + Logback |
| | | |
| | | ## èç³»æ¹å¼ |
| | | |
| | | 妿é®é¢æå»ºè®®,请èç³»å¼åå¢éã |
| | | |
| | | --- |
| | | |
| | | **ææ¡£çæ¬**: v1.0 |
| | | **æ´æ°æ¥æ**: 2026-01-20 |
| | | **å¼åè
**: RuoYi Team |
| New file |
| | |
| | | # å»é¢åè¯æç´¢åè½ - å¿«éä½¿ç¨æå |
| | | |
| | | ## ä¸ãé¨ç½²æ¥éª¤ï¼4æ¥å®æï¼ |
| | | |
| | | ### æ¥éª¤1: éæ HanLP åè¯åº |
| | | |
| | | **说æ**: å·²éæ HanLP ä¸ä¸ä¸æåè¯åºï¼æ¿ä»£äºç®åç N-Gram ç®æ³ï¼åè¯å确度æ´é«ã |
| | | |
| | | å¨ `ruoyi-common/pom.xml` ä¸å·²æ·»å ï¼ |
| | | ```xml |
| | | <!-- HanLP 䏿åè¯åº --> |
| | | <dependency> |
| | | <groupId>com.hankcs</groupId> |
| | | <artifactId>hanlp</artifactId> |
| | | <version>portable-1.8.4</version> |
| | | </dependency> |
| | | ``` |
| | | |
| | | ### æ¥éª¤2: æ§è¡æ°æ®åºèæ¬ |
| | | ```bash |
| | | # è¿å
¥ SQL ç®å½ |
| | | cd sql |
| | | |
| | | # 1. æ§è¡èæ¬æ·»å åè¯å段 |
| | | mysql -uroot -pä½ çå¯ç ä½ çæ°æ®åºå < tb_hosp_data_add_keywords.sql |
| | | |
| | | # 2. æ·»å åå°èåæé |
| | | mysql -uroot -pä½ çå¯ç ä½ çæ°æ®åºå < hospital_tokenizer_menu.sql |
| | | ``` |
| | | |
| | | ### æ¥éª¤3: éå¯åºç¨ |
| | | ```bash |
| | | # Linux/Mac |
| | | ./ry.sh restart |
| | | |
| | | # Windows |
| | | ry.bat |
| | | ``` |
| | | |
| | | ### æ¥éª¤4: 使ç¨åå°æµè¯çé¢åå§ååè¯ |
| | | |
| | | **æ¹å¼1: éè¿åå°çé¢ï¼æ¨èï¼** |
| | | |
| | | 1. ç»å½åå°ç®¡çç³»ç» |
| | | 2. è¿å
¥èåï¼**ç³»ç»ç®¡ç > å»é¢ç®¡ç > å»é¢åè¯æµè¯** |
| | | 3. ç¹å»ã**æ¹éçæææå»é¢åè¯**ãæé® |
| | | 4. çå¾
çæå®æ |
| | | |
| | | **æ¹å¼2: éè¿ API æ¥å£** |
| | | |
| | | ä½¿ç¨ Postman ææµè§å¨è®¿é®ï¼ |
| | | ``` |
| | | GET http://localhost:8080/system/hospital/generateKeywords |
| | | ``` |
| | | |
| | | çå¾
è¿åç»æ,æ¾ç¤ºæåçæçå»é¢æ°éã |
| | | |
| | | --- |
| | | |
| | | ## äºãåå°æµè¯çé¢ä½¿ç¨ |
| | | |
| | | ### 访é®è·¯å¾ |
| | | **ç³»ç»ç®¡ç > å»é¢ç®¡ç > å»é¢åè¯æµè¯** |
| | | |
| | | ### åè½è¯´æ |
| | | |
| | | #### 1. æ¹éåè¯ç®¡ç |
| | | - **åè½**: 为ææå»é¢æ¹éçæåè¯æ°æ® |
| | | - **使ç¨åºæ¯**: |
| | | - 馿¬¡é¨ç½²æ¶åå§å |
| | | - åè¯ç®æ³å级åéæ°çæ |
| | | - æ°æ®ä¿®å¤åæ´æ° |
| | | - **æä½**: ç¹å»ãæ¹éçæææå»é¢åè¯ãæé® |
| | | - **èæ¶**: æ ¹æ®å»é¢æ°éï¼é常éè¦ 1-5 åé |
| | | |
| | | #### 2. å»é¢å¹é
æµè¯ |
| | | - **åè½**: æµè¯åè¯æç´¢ææ |
| | | - **ä½¿ç¨æ¹æ³**: |
| | | 1. å¨æç´¢æ¡è¾å
¥å»é¢åç§°ãå°åæå
³é®è¯ |
| | | 2. 设置è¿åç»ææ°éï¼é»è®¤ 30 æ¡ï¼ |
| | | 3. ç¹å»ãæç´¢ãæé® |
| | | 4. æ¥çå¹é
ç»æï¼æç¸å
³åº¦æåºï¼ |
| | | |
| | | #### 3. æµè¯ç¤ºä¾ |
| | | ``` |
| | | è¾å
¥: "å京ååå»é¢" |
| | | ç»æ: æ¾ç¤ºææå
å«âå京âåâååâçå»é¢ |
| | | |
| | | è¾å
¥: "䏿µ·çéå»é¢å¢æ¹¾åº" |
| | | ç»æ: 䏿µ·çéå»é¢åå
¶å颿卿å |
| | | |
| | | è¾å
¥: "人æ°å»é¢æé³åº" |
| | | ç»æ: æé³åºç人æ°å»é¢ç¸å
³ç»æ |
| | | ``` |
| | | |
| | | ### ç颿ªå¾è¯´æ |
| | | |
| | | 1. **æ¹éåè¯åºå** |
| | | - å±ç¤ºæç¤ºä¿¡æ¯ |
| | | - çææé®ï¼å¸¦ loading ç¶æï¼ |
| | | - ç»ææ¾ç¤ºï¼æå/å¤±è´¥ï¼ |
| | | |
| | | 2. **æç´¢æµè¯åºå** |
| | | - æç´¢è¾å
¥æ¡ |
| | | - ç»ææ°é设置 |
| | | - åè¯ç»æå±ç¤ºï¼æ ç¾å½¢å¼ï¼ |
| | | - å»é¢åè¡¨è¡¨æ ¼ |
| | | - ç»è®¡ä¿¡æ¯ |
| | | |
| | | --- |
| | | |
| | | ## ä¸ãAPI æ¥å£ä½¿ç¨ |
| | | |
| | | ### æ¥å£1: æ¹éçæåè¯ï¼ç®¡çåï¼ |
| | | |
| | | **æ¥å£**: `GET /system/hospital/generateKeywords` |
| | | |
| | | **说æ**: æ¹é为ææå»é¢çæåè¯,ç¨äºåå§åæéæ°çæ |
| | | |
| | | **请æ±ç¤ºä¾**: |
| | | ```bash |
| | | curl -X GET "http://localhost:8080/system/hospital/generateKeywords" |
| | | ``` |
| | | |
| | | **è¿å示ä¾**: |
| | | ```json |
| | | { |
| | | "code": 200, |
| | | "msg": "æåçæ 1523 个å»é¢çåè¯" |
| | | } |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ### æ¥å£2: åè¯æç´¢å»é¢ï¼æ ¸å¿åè½ï¼ |
| | | |
| | | **æ¥å£**: `GET /system/hospital/searchByKeywords` |
| | | |
| | | **åæ°**: |
| | | | åæ° | ç±»å | å¿
å¡« | 说æ | ç¤ºä¾ | |
| | | |-----|------|------|------|------| |
| | | | searchText | String | æ¯ | æç´¢ææ¬ | "å京ååä¸ååº" | |
| | | | pageSize | Integer | å¦ | è¿åæ°é,é»è®¤50 | 20 | |
| | | |
| | | **请æ±ç¤ºä¾**: |
| | | ```bash |
| | | # æç´¢ "å京ååå»é¢" |
| | | curl -X GET "http://localhost:8080/system/hospital/searchByKeywords?searchText=å京ååå»é¢&pageSize=20" |
| | | |
| | | # æç´¢ "䏿µ·çé" |
| | | curl -X GET "http://localhost:8080/system/hospital/searchByKeywords?searchText=䏿µ·çé" |
| | | ``` |
| | | |
| | | **è¿å示ä¾**: |
| | | ```json |
| | | { |
| | | "code": 200, |
| | | "msg": "æ¥è¯¢æå", |
| | | "data": [ |
| | | { |
| | | "hospId": 1001, |
| | | "hospName": "å京ååå»é¢", |
| | | "hospShort": "ååå»é¢", |
| | | "hopsProvince": "å京å¸", |
| | | "hopsCity": "å京å¸", |
| | | "hopsArea": "ä¸ååº", |
| | | "hospAddress": "ä¸ååºå¸
åºå1å·", |
| | | "hospTel": "010-69156114" |
| | | }, |
| | | { |
| | | "hospId": 1002, |
| | | "hospName": "é¦é½å»ç§å¤§å¦éå±å京ååå»é¢è¥¿é¢", |
| | | "hospShort": "åå西é¢", |
| | | "hopsProvince": "å京å¸", |
| | | "hopsCity": "å京å¸", |
| | | "hopsArea": "西ååº", |
| | | "hospAddress": "西ååºå¤§æ¨ä»è¡å41å·" |
| | | } |
| | | // ... æ´å¤å¹é
ç»æï¼æå¹é
度èªå¨æåºï¼ |
| | | ] |
| | | } |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ## åãå端éæç¤ºä¾ |
| | | |
| | | ### uni-app éæ |
| | | |
| | | ```javascript |
| | | // å¨ä½ ç页颿ç»ä»¶ä¸ |
| | | export default { |
| | | data() { |
| | | return { |
| | | searchText: '', |
| | | hospitals: [] |
| | | } |
| | | }, |
| | | methods: { |
| | | // æç´¢å»é¢ |
| | | searchHospitals() { |
| | | if (!this.searchText.trim()) { |
| | | uni.showToast({ |
| | | title: '请è¾å
¥æç´¢å
容', |
| | | icon: 'none' |
| | | }); |
| | | return; |
| | | } |
| | | |
| | | uni.showLoading({ title: 'æç´¢ä¸...' }); |
| | | |
| | | uni.request({ |
| | | url: this.$baseUrl + '/system/hospital/searchByKeywords', |
| | | method: 'GET', |
| | | data: { |
| | | searchText: this.searchText, |
| | | pageSize: 30 |
| | | }, |
| | | success: (res) => { |
| | | uni.hideLoading(); |
| | | |
| | | if (res.data.code === 200) { |
| | | this.hospitals = res.data.data; |
| | | console.log('æ¾å°å»é¢:', this.hospitals.length); |
| | | |
| | | if (this.hospitals.length === 0) { |
| | | uni.showToast({ |
| | | title: 'æªæ¾å°å¹é
çå»é¢', |
| | | icon: 'none' |
| | | }); |
| | | } |
| | | } else { |
| | | uni.showToast({ |
| | | title: res.data.msg || 'æç´¢å¤±è´¥', |
| | | icon: 'none' |
| | | }); |
| | | } |
| | | }, |
| | | fail: (err) => { |
| | | uni.hideLoading(); |
| | | uni.showToast({ |
| | | title: 'ç½ç»è¯·æ±å¤±è´¥', |
| | | icon: 'none' |
| | | }); |
| | | console.error('æç´¢å¤±è´¥:', err); |
| | | } |
| | | }); |
| | | }, |
| | | |
| | | // éæ©å»é¢ |
| | | selectHospital(hospital) { |
| | | // å°éä¸çå»é¢ä¿¡æ¯åå¡«å°è¡¨å |
| | | console.log('éæ©äºå»é¢:', hospital.hospName); |
| | | // ä½ çä¸å¡é»è¾... |
| | | } |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | ### Vue.js + Axios éæ |
| | | |
| | | ```javascript |
| | | // å¨ Vue ç»ä»¶ä¸ |
| | | <template> |
| | | <div> |
| | | <el-input |
| | | v-model="searchText" |
| | | placeholder="è¾å
¥å»é¢åç§°æå°å" |
| | | @keyup.enter="searchHospitals"> |
| | | <el-button slot="append" icon="el-icon-search" @click="searchHospitals"></el-button> |
| | | </el-input> |
| | | |
| | | <el-table :data="hospitals" v-loading="loading"> |
| | | <el-table-column prop="hospName" label="å»é¢åç§°"></el-table-column> |
| | | <el-table-column prop="hopsCity" label="åå¸"></el-table-column> |
| | | <el-table-column prop="hospAddress" label="å°å"></el-table-column> |
| | | <el-table-column label="æä½"> |
| | | <template slot-scope="scope"> |
| | | <el-button size="mini" @click="selectHospital(scope.row)">éæ©</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | data() { |
| | | return { |
| | | searchText: '', |
| | | hospitals: [], |
| | | loading: false |
| | | } |
| | | }, |
| | | methods: { |
| | | async searchHospitals() { |
| | | if (!this.searchText.trim()) { |
| | | this.$message.warning('请è¾å
¥æç´¢å
容'); |
| | | return; |
| | | } |
| | | |
| | | this.loading = true; |
| | | |
| | | try { |
| | | const response = await this.$http.get('/system/hospital/searchByKeywords', { |
| | | params: { |
| | | searchText: this.searchText, |
| | | pageSize: 50 |
| | | } |
| | | }); |
| | | |
| | | if (response.data.code === 200) { |
| | | this.hospitals = response.data.data; |
| | | |
| | | if (this.hospitals.length === 0) { |
| | | this.$message.info('æªæ¾å°å¹é
çå»é¢'); |
| | | } else { |
| | | this.$message.success(`æ¾å° ${this.hospitals.length} 个å¹é
çå»é¢`); |
| | | } |
| | | } else { |
| | | this.$message.error(response.data.msg || 'æç´¢å¤±è´¥'); |
| | | } |
| | | } catch (error) { |
| | | console.error('æç´¢å¤±è´¥:', error); |
| | | this.$message.error('ç½ç»è¯·æ±å¤±è´¥'); |
| | | } finally { |
| | | this.loading = false; |
| | | } |
| | | }, |
| | | |
| | | selectHospital(hospital) { |
| | | // å¤çéæ©å»é¢çé»è¾ |
| | | console.log('éæ©äºå»é¢:', hospital); |
| | | this.$emit('hospital-selected', hospital); |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ## äºãåè½è¯´æ |
| | | |
| | | ### 1. åè¯ç¹ç¹ |
| | | - **HanLP ä¸ä¸åè¯**: 使ç¨ä¸çéç¨ç HanLP 䏿åè¯åº |
| | | - **æºè½åè¯**: èªå¨å°å»é¢åç§°ãå°åçå解为å¤ä¸ªå
³é®è¯ |
| | | - **åç¨è¯è¿æ»¤**: è¿æ»¤âå»é¢âãâå¸âãâçâç常è§è¯ |
| | | - **éçº§æ¹æ¡**: HanLP 失败æ¶èªå¨éçº§å° N-Gram ç®æ³ |
| | | - **æ¯ææ¨¡ç³å¹é
**: è¾å
¥é¨åå
³é®è¯å³å¯å¹é
|
| | | |
| | | ### 2. æéæåº |
| | | - å¹é
çå
³é®è¯è¶å¤,æåè¶é å |
| | | - å®å
¨å¹é
ä¼å
äºé¨åå¹é
|
| | | - èªå¨æç¸å
³åº¦æåº,æ éæå¨çé |
| | | |
| | | ### 3. 使ç¨åºæ¯ |
| | | - â
ç¨æ·è¾å
¥ "å京åå" â å¹é
ææå¸¦"å京"å"åå"çå»é¢ |
| | | - â
ç¨æ·è¾å
¥ "ä¸ååºäººæ°" â å¹é
"ä¸ååº"å"人æ°"ç¸å
³çå»é¢ |
| | | - â
ç¨æ·è¾å
¥ "çéå»é¢å¢æ¹¾" â å¹é
䏿µ·çéå»é¢åå
¶åé¢ |
| | | |
| | | --- |
| | | |
| | | ## å
ã常è§é®é¢ |
| | | |
| | | ### Q1: 馿¬¡é¨ç½²åæç´¢ä¸å°ä»»ä½ç»æ? |
| | | **A**: éè¦å
è°ç¨ `/generateKeywords` æ¥å£åå§ååè¯æ°æ®ã |
| | | |
| | | ### Q2: æ°å¢æä¿®æ¹å»é¢åéè¦éæ°çæåè¯å? |
| | | **A**: ä¸éè¦ãç³»ç»ä¼èªå¨å¨æ°å¢/ä¿®æ¹/忥å»é¢æ¶çæåè¯ã |
| | | |
| | | ### Q3: æç´¢é度æ
¢æä¹å? |
| | | **A**: |
| | | 1. 确认已ç»å¨ `hosp_keywords` åæ®µä¸åå»ºç´¢å¼ |
| | | 2. éå½åå° `pageSize` åæ°å¼ |
| | | 3. èèå¢å æå¡å¨å
å |
| | | |
| | | ### Q4: åå°èåçä¸å°âå»é¢ç®¡çâ? |
| | | **A**: |
| | | 1. 确认æ§è¡äº `hospital_tokenizer_menu.sql` èæ¬ |
| | | 2. å¨âç³»ç»ç®¡ç > è§è²ç®¡çâä¸ï¼ç»å½åè§è²åé
èåæé |
| | | 3. éæ°ç»å½åå°ç³»ç» |
| | | |
| | | ### Q5: HanLP åè¯åºä¸è½½æ
¢æä¹å? |
| | | **A**: |
| | | 1. 使ç¨é¿éäºæè
¾è®¯äº Maven éå |
| | | 2. æè
æå¨ä¸è½½ HanLP jar å
æ¾å°æ¬å° Maven ä»åº |
| | | |
| | | ### Q6: å¦ä½ä¼åæç´¢å确度? |
| | | **A**: |
| | | 1. è°æ´ `HospitalTokenizerUtil` ä¸çåç¨è¯å表 |
| | | 2. æ ¹æ®ä¸å¡éæ±æ·»å å»é¢å«åæ å° |
| | | 3. 使ç¨åå°æµè¯çé¢å夿µè¯åè°ä¼ |
| | | |
| | | --- |
| | | |
| | | ## ä¸ãç»´æ¤å»ºè®® |
| | | |
| | | ### å®æç»´æ¤ |
| | | ```bash |
| | | # å»ºè®®æ¯æéæ°çæä¸æ¬¡åè¯ï¼å¯éï¼ |
| | | GET /system/hospital/generateKeywords |
| | | ``` |
| | | |
| | | ### çæ§æ¥å¿ |
| | | æ¥çåºç¨æ¥å¿ä¸çåè¯çæä¿¡æ¯: |
| | | ```bash |
| | | tail -f logs/sys-info.log | grep "å»é¢åè¯" |
| | | ``` |
| | | |
| | | ### æ°æ®å¤ä»½ |
| | | ```bash |
| | | # 宿å¤ä»½å»é¢æ°æ® |
| | | mysqldump -uç¨æ·å -pæ°æ®åºå tb_hosp_data > hosp_data_backup.sql |
| | | ``` |
| | | |
| | | --- |
| | | |
| | | ## ä¸ãææ¯æ¯æ |
| | | |
| | | å¦éå°é®é¢,è¯·æ£æ¥: |
| | | 1. æ°æ®åºå段æ¯å¦æ£ç¡®æ·»å |
| | | 2. åºç¨æ¯å¦æ£å¸¸éå¯ |
| | | 3. åè¯æ°æ®æ¯å¦å·²åå§å |
| | | 4. æ¥å£æéæ¯å¦é
ç½®æ£ç¡® |
| | | |
| | | è¯¦ç»ææ¯ææ¡£è¯·åè: `å»é¢ä¿¡æ¯åè¯æç´¢åè½è¯´æ.md` |
| | | |
| | | --- |
| | | |
| | | **çæ¬**: v1.0 |
| | | **æ´æ°æ¥æ**: 2026-01-20 |