Merge remote-tracking branch 'origin/dev_New_pro' into dev_New_pro
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # æä»¶ä¸ä¼ åè½è¯´æ |
| | | |
| | | æ¬ææ¡£åºäºä»¥ä¸ä»£ç æ´çï¼ |
| | | |
| | | - `src/main/java/com/ruoyi/basic/utils/FileUtil.java` |
| | | - `src/main/java/com/ruoyi/basic/enums/ApplicationTypeEnum.java` |
| | | - `src/main/java/com/ruoyi/basic/enums/RecordTypeEnum.java` |
| | | - `src/main/java/com/ruoyi/project/common/CommonController.java` |
| | | - `src/main/java/com/ruoyi/basic/controller/StorageAttachmentController.java` |
| | | |
| | | ç¨äºè¯´ææ¬é¡¹ç®ä¸æä»¶ä¸ä¼ ãéä»¶ç»å®ãæä»¶é¢è§/ä¸è½½çæ´ä½è®¾è®¡ï¼ä»¥å `FileUtil` 䏿¯ä¸ªæ¹æ³çä½ç¨ã |
| | | |
| | | ## 1. æ´ä½è®¾è®¡ |
| | | |
| | | æ¬é¡¹ç®çæä»¶ä½ç³»åæä¸¤å±ï¼ |
| | | |
| | | - `storage_blob`ï¼åæä»¶å®ä½ä¿¡æ¯ |
| | | - åå§æä»¶å |
| | | - å¯ä¸æä»¶å `uidFilename` |
| | | - æä»¶è·¯å¾ `path` |
| | | - æä»¶å¤§å° `byteSize` |
| | | - æä»¶ç±»å `contentType` |
| | | - å
Œ
±è®¿é®æ è¯ `resourceKey` |
| | | - `storage_attachment`ï¼åæä»¶åä¸å¡è®°å½çå
³èå
³ç³» |
| | | - `application`ï¼æä»¶ç¨é |
| | | - `recordType`ï¼ä¸å¡è®°å½ç±»å |
| | | - `recordId`ï¼ä¸å¡è®°å½ä¸»é® |
| | | - `storageBlobId`ï¼å
³èçæä»¶ä¸»è¡¨ id |
| | | |
| | | å¯ä»¥çè§£ä¸ºï¼ |
| | | |
| | | - `storage_blob` è´è´£âæä»¶æ¬èº«â |
| | | - `storage_attachment` è´è´£âæä»¶æå¨åªæ¡ä¸å¡æ°æ®ä¸â |
| | | |
| | | ## 2. ä¸ä¼ æµç¨ |
| | | |
| | | ### 2.1 æ®éä¸ä¼ |
| | | |
| | | æ¥å£ï¼ |
| | | |
| | | - `POST /common/upload` |
| | | |
| | | æ§å¶å¨ä½ç½®ï¼ |
| | | |
| | | - `src/main/java/com/ruoyi/project/common/CommonController.java` |
| | | |
| | | å
¥åï¼ |
| | | |
| | | - 表ååæ®µåï¼`files` |
| | | - ç±»åï¼`List<MultipartFile>` |
| | | |
| | | 代ç é»è¾ï¼ |
| | | |
| | | 1. å端å
è°ç¨ `/common/upload` |
| | | 2. `CommonController.upload()` è°ç¨ `storageBlobService.upload(files, false)` |
| | | 3. æå¡å±ä¿åæä»¶å
æ°æ®å° `storage_blob` |
| | | 4. è¿å `StorageBlobVO` å表ï¼éé¢é常ä¼å¸¦ï¼ |
| | | - æä»¶ id |
| | | - åå§æä»¶å |
| | | - å¯ä¸æä»¶å |
| | | - é¢è§å°å `previewURL` |
| | | - ä¸è½½å°å `downloadURL` |
| | | |
| | | 说æï¼ |
| | | |
| | | - æ¤æ¶åªæ¯âä¸ä¼ äºæä»¶â |
| | | - è¿æ²¡æåå
·ä½ä¸å¡åæ®å»ºç«å
³ç³» |
| | | |
| | | ### 2.2 å
Œ
±ä¸ä¼ |
| | | |
| | | æ¥å£ï¼ |
| | | |
| | | - `POST /common/public/upload` |
| | | |
| | | 代ç é»è¾ï¼ |
| | | |
| | | - `CommonController.publicUpload()` è°ç¨ `storageBlobService.upload(files, true)` |
| | | |
| | | 说æï¼ |
| | | |
| | | - 该æ¥å£ä¸ä¼ çæä»¶èµ°âå
Œ
±æä»¶âæ¨¡å¼ |
| | | - æ§å¶å¨æ³¨éå·²æç¡®è¯´æï¼æ°¸ä¹
ææï¼æ
ç¨ |
| | | - å¯¹åº URL æå»ºæ¶ï¼å¯è½èµ° `publicKey` åæ°ï¼è䏿¯ä¸´æ¶ `token` |
| | | |
| | | ## 3. éä»¶ç»å®æµç¨ |
| | | |
| | | ä¸ä¼ 宿åï¼å¦æéè¦ææä»¶ç»å®å°ææ¡ä¸å¡è®°å½ï¼éè¦åè°ç¨éä»¶æ¥å£ã |
| | | |
| | | æ¥å£ï¼ |
| | | |
| | | - `POST /storageAttachment/add` |
| | | |
| | | æ§å¶å¨ä½ç½®ï¼ |
| | | |
| | | - `src/main/java/com/ruoyi/basic/controller/StorageAttachmentController.java` |
| | | |
| | | æ ¸å¿è¯·æ±å¯¹è±¡ï¼ |
| | | |
| | | - `StorageAttachmentDTO` |
| | | |
| | | å
¶ä¸ç»§æ¿äº `StorageAttachment`ï¼å¹¶é¢å¤å
å«ï¼ |
| | | |
| | | - `storageBlobDTOs`ï¼å¾
ç»å®çæä»¶å表 |
| | | |
| | | 常ç¨å段å«ä¹ï¼ |
| | | |
| | | - `application`ï¼æä»¶ç¨é |
| | | - `recordType`ï¼ä¸å¡ç±»å |
| | | - `recordId`ï¼ä¸å¡ä¸»é® |
| | | - `storageBlobDTOs[].id`ï¼ä¸ä¼ æååè¿åçæä»¶ id |
| | | |
| | | 示ä¾è¯·æ±ä½ï¼ |
| | | |
| | | ```json |
| | | { |
| | | "application": "file", |
| | | "recordType": "common_file", |
| | | "recordId": 1001, |
| | | "storageBlobDTOs": [ |
| | | { |
| | | "id": 12, |
| | | "application": "file" |
| | | }, |
| | | { |
| | | "id": 13, |
| | | "application": "file" |
| | | } |
| | | ] |
| | | } |
| | | ``` |
| | | |
| | | ç»å®é»è¾è¯´æï¼ |
| | | |
| | | 1. å
ä¸ä¼ æä»¶ï¼æ¿å° `storage_blob.id` |
| | | 2. åè°ç¨ `/storageAttachment/add` |
| | | 3. æå¡å±æç»ä¼éè¿ `FileUtil` ä¿å `storage_attachment` |
| | | 4. åç»å³å¯æä¸å¡è®°å½æ¥è¯¢åºè¯¥è®°å½ä¸çéä»¶ |
| | | |
| | | ## 4. æ¥è¯¢ä¸å é¤éä»¶ |
| | | |
| | | ### 4.1 æ¥è¯¢éä»¶å表 |
| | | |
| | | æ¥å£ï¼ |
| | | |
| | | - `GET /storageAttachment/list` |
| | | |
| | | 说æï¼ |
| | | |
| | | - æ `StorageAttachmentDTO` ä¸çæ¡ä»¶æ¥è¯¢ |
| | | - å¸¸è§æ¡ä»¶æ¯ `application`ã`recordType`ã`recordId` |
| | | - è¿åç»ææ¬è´¨ä¸æ¯åä¸å¡è®°å½å
³èåçæä»¶å表 |
| | | |
| | | ### 4.2 å é¤éä»¶ |
| | | |
| | | æ¥å£ï¼ |
| | | |
| | | - `DELETE /storageAttachment/delete` |
| | | |
| | | 请æ±ä½ï¼ |
| | | |
| | | - `List<Long> ids` |
| | | |
| | | 说æï¼ |
| | | |
| | | - è¿éç `ids` æ¯éä»¶å
³è表 idï¼ä¸è¬æ¯ `storage_attachment.id` |
| | | - å 餿¶é常ä¸ä»
ä¼å å
³èå
³ç³»ï¼ä¹ä¼è¿ä¸æ¥å é¤å¯¹åºæä»¶è®°å½ |
| | | |
| | | ## 5. é¢è§ä¸ä¸è½½æµç¨ |
| | | |
| | | ### 5.1 ä¸è½½æ¥å£ |
| | | |
| | | æ¥å£ï¼ |
| | | |
| | | - `GET /common/download/{fileName}` |
| | | |
| | | æ¯æä¸¤ç§è®¿é®æ¹å¼ï¼ |
| | | |
| | | - 临æ¶é¾æ¥ï¼`token` |
| | | - å
Œ
±é¾æ¥ï¼`publicKey` |
| | | |
| | | 代ç é»è¾ï¼ |
| | | |
| | | 1. å¦æè¯·æ±éæ `publicKey`ï¼èµ° `storageBlobService.getPublicFile(fileName, publicKey)` |
| | | 2. å¦åèµ° `storageBlobService.getFileByToken(fileName, token)` |
| | | 3. åå°å®é
æä»¶åï¼è°ç¨ `fileUtil.compressFile(file)` åå¾çå缩å¤ç |
| | | 4. 设置ä¸è½½ååºå¤´ï¼è¾åºæä»¶æµ |
| | | |
| | | ### 5.2 é¢è§æ¥å£ |
| | | |
| | | æ¥å£ï¼ |
| | | |
| | | - `GET /common/preview/{fileName}` |
| | | |
| | | æ¯æä¸¤ç§è®¿é®æ¹å¼ï¼ |
| | | |
| | | - 临æ¶é¾æ¥ï¼`token` |
| | | - å
Œ
±é¾æ¥ï¼`publicKey` |
| | | |
| | | 代ç é»è¾ï¼ |
| | | |
| | | 1. æ ¡éª `token` æ `publicKey` |
| | | 2. è·åæä»¶ |
| | | 3. è°ç¨ `fileUtil.compressFile(file)` |
| | | 4. æ ¹æ®æä»¶å
容类åè¿å inline é¢è§ |
| | | |
| | | ## 6. æä¸¾å«ä¹ |
| | | |
| | | ### 6.1 `ApplicationTypeEnum` |
| | | |
| | | ä½ç½®ï¼ |
| | | |
| | | - `src/main/java/com/ruoyi/basic/enums/ApplicationTypeEnum.java` |
| | | |
| | | å½åå®ä¹å¼ï¼ |
| | | |
| | | | æä¸¾ | type | 说æ | |
| | | |---|---|---| |
| | | | `IMAGE` | `image` | å¾çç±»æä»¶ | |
| | | | `FILE` | `file` | æ®éæä»¶ | |
| | | | `AFTER_FILE` | `after_file` | å®åç¸å
³æä»¶ | |
| | | | `BEFORE_FILE` | `before_file` | å®å/åç½®ç¸å
³æä»¶ | |
| | | | `APK` | `apk` | å®è£
å
æä»¶ | |
| | | |
| | | ä½ç¨ï¼ |
| | | |
| | | - ç¨äºåºåå䏿¡ä¸å¡è®°å½ä¸ï¼ä¸åç¨éçæä»¶ |
| | | - `FileUtil` çå¾å¤æ¥è¯¢ãå é¤ãä¿åæ¹æ³é½ä¼ç¨å°è¯¥å段 |
| | | |
| | | ### 6.2 `RecordTypeEnum` |
| | | |
| | | ä½ç½®ï¼ |
| | | |
| | | - `src/main/java/com/ruoyi/basic/enums/RecordTypeEnum.java` |
| | | |
| | | ä½ç¨ï¼ |
| | | |
| | | - ç¨äºæ è®°æä»¶å±äºåªç±»ä¸å¡è®°å½ |
| | | - ä¾å¦è´¨æ£ãéè´ã客æ·ãå®åãå°è´¦ãéç¥ã设å¤ç模å |
| | | - ä¸ä¼ 宿åï¼éä»¶æç»éè¿ `recordType + recordId` åä¸å¡æ°æ®å
³è |
| | | |
| | | 说æï¼ |
| | | |
| | | - 该æä¸¾å¼å¾å¤ï¼ææ¡£ä¸é个å±å¼ |
| | | - å®é
ä½¿ç¨æ¶å¿
é¡»ä¼ ä»£ç ä¸å·²å®ä¹ç `type` å¼ |
| | | - å¦ï¼ |
| | | - `common_file` |
| | | - `after_sales_service` |
| | | - `quality_inspect` |
| | | - `product` |
| | | - `notice` |
| | | |
| | | ## 7. `FileUtil` æ¹æ³è¯´æ |
| | | |
| | | `FileUtil` æ¯æ¬å¥æä»¶ä¸ä¼ ä½ç³»çæ ¸å¿å·¥å
·ç±»ï¼ä¸»è¦è´è´£ï¼ |
| | | |
| | | - æä»¶ä¸ä¸å¡è®°å½ç»å® |
| | | - æä»¶ä¸éä»¶å é¤ |
| | | - éä»¶æ¥è¯¢ |
| | | - é¢è§/ä¸è½½å°åçæ |
| | | - token ä½¿ç¨æ¬¡æ°æ§å¶ |
| | | - å¾çå缩 |
| | | |
| | | ä¸é¢æåè½åç»è¯´ææ¯ä¸ªæ¹æ³ã |
| | | |
| | | ### 7.1 ä¿åéä»¶å
³ç³» |
| | | |
| | | #### 1. `saveStorageAttachment(ApplicationTypeEnum application, RecordTypeEnum recordType, Long recordId, List<StorageBlobDTO> storageBlobDTOS)` |
| | | |
| | | ä½ç¨ï¼ |
| | | |
| | | - æâæä»¶ç¨é + è®°å½ç±»å + è®°å½ idâä¿åéä»¶å
³ç³» |
| | | |
| | | é»è¾ï¼ |
| | | |
| | | 1. æ ¡éª `application`ã`recordType`ã`recordId` |
| | | 2. å
å é¤è¿ç»ä¸å¡è®°å½ä¸çæ§éä»¶ |
| | | 3. ææ°ç `storageBlobDTOS` 转æ `storage_attachment` è®°å½åæ¹éæå
¥ |
| | | |
| | | éç¨åºæ¯ï¼ |
| | | |
| | | - ææ¡ä¸å¡æ°æ®éæ°ä¿åéä»¶ï¼æ§éä»¶æ´ä½æ¿æ¢ææ°éä»¶ |
| | | |
| | | #### 2. `saveStorageAttachmentByRecordTypeAndRecordId(String application, RecordTypeEnum recordType, Long recordId, List<StorageBlobDTO> storageBlobDTOS)` |
| | | |
| | | ä½ç¨ï¼ |
| | | |
| | | - æ `recordType + recordId` ä¿åéä»¶å
³ç³»ï¼`application` 坿å®ï¼ä¹å¯ä»æ¯ä¸ªæä»¶å¯¹è±¡é读å |
| | | |
| | | é»è¾ç¹ç¹ï¼ |
| | | |
| | | - 妿 `application == null`ï¼ä¼æ ¹æ® `storageBlobDTO.application` åå«å 餿§å
³ç³» |
| | | - 妿éä»¶å表为空ï¼ä¼ç´æ¥å é¤è¯¥ä¸å¡è®°å½çéä»¶å
³ç³» |
| | | - æå
¥æ¶ä¼èªå¨åå¡« `application` |
| | | |
| | | éç¨åºæ¯ï¼ |
| | | |
| | | - 䏿¬¡æäº¤éå¯è½å
å«å¤ç§ç¨éçéä»¶ |
| | | - æè
è°ç¨æ¹ä¸æ¹ä¾¿ç´æ¥ä¼ æä¸¾ç±»å |
| | | |
| | | ### 7.2 å é¤æä»¶ä¸»è¡¨ `storage_blob` |
| | | |
| | | #### 3. `deleteStorageBlobs(List<Long> storageBlobIds)` |
| | | |
| | | ä½ç¨ï¼ |
| | | |
| | | - ææä»¶ä¸»è¡¨ id æ¹éå é¤æä»¶è®°å½ |
| | | |
| | | #### 4. `deleteStorageBlobsByStorageAttachmentIds(List<Long> storageAttachmentIds)` |
| | | |
| | | ä½ç¨ï¼ |
| | | |
| | | - å
æ ¹æ®éä»¶å
³è id æ¥å° `storageBlobId` |
| | | - åå é¤å¯¹åºçæä»¶ä¸»è¡¨è®°å½ |
| | | |
| | | #### 5. `deleteStorageBlobsByApplicationAndRecordTypeAndRecordIds(ApplicationTypeEnum application, RecordTypeEnum recordType, List<Long> recordIds)` |
| | | |
| | | ä½ç¨ï¼ |
| | | |
| | | - æ ¹æ®ç¨éãè®°å½ç±»åãå¤ä¸ªä¸å¡ idï¼æ¹éå é¤å¯¹åºçæä»¶ä¸»è¡¨è®°å½ |
| | | |
| | | éç¨åºæ¯ï¼ |
| | | |
| | | - æ¹éå é¤æç±»ä¸å¡æ°æ®æ¶ï¼åæ¶æ¸
çéä»¶ |
| | | |
| | | #### 6. `deleteStorageBlobsByRecordTypeAndRecordId(RecordTypeEnum recordType, Long recordId)` |
| | | |
| | | ä½ç¨ï¼ |
| | | |
| | | - æ ¹æ® `recordType + recordId` å é¤è¯¥ä¸å¡è®°å½ä¸æææä»¶ä¸»è¡¨è®°å½ |
| | | |
| | | ### 7.3 å é¤éä»¶å
³ç³» `storage_attachment` |
| | | |
| | | #### 7. `deleteStorageAttachmentsByStorageAttachmentIds(List<Long> storageAttachmentIds)` |
| | | |
| | | ä½ç¨ï¼ |
| | | |
| | | - å
å é¤é件对åºçæä»¶ä¸»è¡¨è®°å½ |
| | | - åå é¤éä»¶å
³ç³»è¡¨è®°å½ |
| | | |
| | | #### 8. `deleteStorageAttachmentsByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum application, RecordTypeEnum recordType, Long recordId)` |
| | | |
| | | ä½ç¨ï¼ |
| | | |
| | | - å 餿å®ç¨éãæå®ä¸å¡è®°å½ä¸çéä»¶å
³ç³» |
| | | |
| | | ç¹ç¹ï¼ |
| | | |
| | | - ä¼å
å blobï¼åå attachment |
| | | |
| | | #### 9. `deleteStorageAttachmentsByRecordTypeAndRecordId(RecordTypeEnum recordType, Long recordId)` |
| | | |
| | | ä½ç¨ï¼ |
| | | |
| | | - å 餿å®ä¸å¡è®°å½ä¸å
¨é¨éä»¶å
³ç³»ï¼ä¸åºåç¨é |
| | | |
| | | #### 10. `deleteStorageAttachmentsByApplicationAndRecordTypeAndRecordIds(ApplicationTypeEnum application, RecordTypeEnum recordType, List<Long> recordIds)` |
| | | |
| | | ä½ç¨ï¼ |
| | | |
| | | - æå¤ä¸ªä¸å¡ id æ¹éå é¤éä»¶å
³ç³» |
| | | |
| | | ### 7.4 æ¥è¯¢éä»¶å
³ç³» |
| | | |
| | | #### 11. `getStorageAttachmentsByStorageAttachmentIds(List<Long> storageAttachmentIds)` |
| | | |
| | | ä½ç¨ï¼ |
| | | |
| | | - æ ¹æ®éä»¶å
³ç³» id æ¥è¯¢ `storage_attachment` è®°å½ |
| | | |
| | | #### 12. `getStorageAttachmentsByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum application, RecordTypeEnum recordType, Long recordId)` |
| | | |
| | | ä½ç¨ï¼ |
| | | |
| | | - æç¨éãä¸å¡ç±»åãä¸å¡ id æ¥è¯¢éä»¶å
³ç³» |
| | | |
| | | #### 13. `getStorageAttachmentsByRecordTypeAndRecordId(RecordTypeEnum recordType, Long recordId)` |
| | | |
| | | ä½ç¨ï¼ |
| | | |
| | | - æä¸å¡ç±»åãä¸å¡ id æ¥è¯¢éä»¶å
³ç³» |
| | | |
| | | ### 7.5 æ¥è¯¢æä»¶ä¿¡æ¯ `StorageBlobVO` |
| | | |
| | | #### 14. `getStorageBlobVOsByApplicationAndRecordTypeAndRecordId(StorageAttachmentDTO storageAttachmentDTO)` |
| | | |
| | | ä½ç¨ï¼ |
| | | |
| | | - éè¿ `StorageAttachmentDTO` æ¡ä»¶æ¥è¯¢æä»¶å表 |
| | | |
| | | ç¹ç¹ï¼ |
| | | |
| | | - `application` å¯é |
| | | - æç»è¿åçæ¯å¸¦é¢è§/ä¸è½½å°åç `StorageBlobVO` |
| | | |
| | | #### 15. `getStorageBlobVOsByStorageAttachmentIds(List<Long> storageAttachmentIds)` |
| | | |
| | | ä½ç¨ï¼ |
| | | |
| | | - æ ¹æ®éä»¶å
³ç³» id æ¥è¯¢æä»¶å表 |
| | | |
| | | ç¹ç¹ï¼ |
| | | |
| | | - ä¼èªå¨æå»ºï¼ |
| | | - `previewURL` |
| | | - `downloadURL` |
| | | - `storageAttachmentId` |
| | | |
| | | #### 16. `getStorageBlobVOsByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum application, RecordTypeEnum recordType, Long recordId)` |
| | | |
| | | ä½ç¨ï¼ |
| | | |
| | | - æç¨éãä¸å¡ç±»åãä¸å¡ id æ¥è¯¢æä»¶å表 |
| | | |
| | | #### 17. `getStorageBlobVOsByRecordTypeAndRecordId(RecordTypeEnum recordType, Long recordId)` |
| | | |
| | | ä½ç¨ï¼ |
| | | |
| | | - æä¸å¡ç±»åãä¸å¡ id æ¥è¯¢æä»¶å表 |
| | | |
| | | #### 18. `getStorageBlobVOsByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum application, RecordTypeEnum recordType, Long recordId, BigDecimal expired)` |
| | | |
| | | ä½ç¨ï¼ |
| | | |
| | | - å第 16 ä¸ªæ¹æ³ç±»ä¼¼ï¼ä½å¯ä»¥èªå®ä¹é¾æ¥è¿ææ¶é´ |
| | | |
| | | 说æï¼ |
| | | |
| | | - `expired` å使¯åé |
| | | - è¿åçé¢è§/ä¸è½½å°å伿è¿ä¸ªæ¶é´çæç¾å |
| | | |
| | | #### 19. `getStorageBlobVOsByStorageAttachmentIds(List<Long> storageAttachmentIds, BigDecimal expired)` |
| | | |
| | | ä½ç¨ï¼ |
| | | |
| | | - æ ¹æ®éä»¶å
³ç³» id æ¥è¯¢æä»¶å表ï¼å¹¶èªå®ä¹é¾æ¥è¿ææ¶é´ |
| | | |
| | | ### 7.6 æ¥è¯¢éä»¶è§å¾ `StorageAttachmentVO` |
| | | |
| | | #### 20. `getStorageAttachmentVOSByStorageAttachmentIds(List<Long> storageAttachmentIds)` |
| | | |
| | | ä½ç¨ï¼ |
| | | |
| | | - æ¥è¯¢éä»¶è§å¾å¯¹è±¡ |
| | | |
| | | ç¹ç¹ï¼ |
| | | |
| | | - æ¯æ¡éä»¶è®°å½éä¼åµå¥èªå·±ç `storageBlobVOS` |
| | | |
| | | #### 21. `getStorageAttachmentVOSByStorageAttachmentIds(List<Long> storageAttachmentIds, BigDecimal expired)` |
| | | |
| | | ä½ç¨ï¼ |
| | | |
| | | - æ ¹æ®éä»¶å
³ç³» id æ¥è¯¢éä»¶è§å¾ï¼å¹¶èªå®ä¹é¾æ¥è¿ææ¶é´ |
| | | |
| | | #### 22. `getStorageAttachmentVOSByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum application, RecordTypeEnum recordType, Long recordId)` |
| | | |
| | | ä½ç¨ï¼ |
| | | |
| | | - æä¸å¡ç»´åº¦æ¥è¯¢éä»¶è§å¾ |
| | | |
| | | #### 23. `getStorageAttachmentVOSByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum application, RecordTypeEnum recordType, Long recordId, BigDecimal expired)` |
| | | |
| | | ä½ç¨ï¼ |
| | | |
| | | - æä¸å¡ç»´åº¦æ¥è¯¢éä»¶è§å¾ï¼å¹¶èªå®ä¹é¾æ¥è¿ææ¶é´ |
| | | |
| | | ### 7.7 ä»
è·åé¢è§å°å |
| | | |
| | | #### 24. `getFilePreviewURLByStorageAttachmentIds(List<Long> storageAttachmentIds)` |
| | | |
| | | ä½ç¨ï¼ |
| | | |
| | | - æ ¹æ®éä»¶å
³ç³» id å表ï¼è¿åé¢è§å°åå表 |
| | | |
| | | #### 25. `getFilePreviewURLByStorageAttachmentIds(List<Long> storageAttachmentIds, BigDecimal expired)` |
| | | |
| | | ä½ç¨ï¼ |
| | | |
| | | - æ ¹æ®éä»¶å
³ç³» id å表ï¼è¿å带èªå®ä¹è¿ææ¶é´çé¢è§å°åå表 |
| | | |
| | | #### 26. `getFilePreviewURLByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum application, RecordTypeEnum recordType, Long recordId)` |
| | | |
| | | ä½ç¨ï¼ |
| | | |
| | | - æä¸å¡ç»´åº¦è¿åé¢è§å°åå表 |
| | | |
| | | #### 27. `getFilePreviewURLByApplicationAndRecordTypeAndRecordIdAndExpired(ApplicationTypeEnum application, RecordTypeEnum recordType, Long recordId, BigDecimal expired)` |
| | | |
| | | ä½ç¨ï¼ |
| | | |
| | | - æä¸å¡ç»´åº¦è¿å带èªå®ä¹è¿ææ¶é´çé¢è§å°åå表 |
| | | |
| | | ### 7.8 ä»
è·åä¸è½½å°å |
| | | |
| | | #### 28. `getFileDownloadURLByStorageAttachmentIds(List<Long> storageAttachmentIds)` |
| | | |
| | | ä½ç¨ï¼ |
| | | |
| | | - æ ¹æ®éä»¶å
³ç³» id å表ï¼è¿åä¸è½½å°åå表 |
| | | |
| | | #### 29. `getFileDownloadURLByStorageAttachmentIds(List<Long> storageAttachmentIds, BigDecimal expired)` |
| | | |
| | | ä½ç¨ï¼ |
| | | |
| | | - æ ¹æ®éä»¶å
³ç³» id å表ï¼è¿å带èªå®ä¹è¿ææ¶é´çä¸è½½å°åå表 |
| | | |
| | | #### 30. `getFileDownloadURLByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum application, RecordTypeEnum recordType, Long recordId)` |
| | | |
| | | ä½ç¨ï¼ |
| | | |
| | | - æä¸å¡ç»´åº¦è¿åä¸è½½å°åå表 |
| | | |
| | | #### 31. `getFileDownloadURLByApplicationAndRecordTypeAndRecordIdAndExpired(ApplicationTypeEnum application, RecordTypeEnum recordType, Long recordId, BigDecimal expired)` |
| | | |
| | | ä½ç¨ï¼ |
| | | |
| | | - æä¸å¡ç»´åº¦è¿å带èªå®ä¹è¿ææ¶é´çä¸è½½å°åå表 |
| | | |
| | | ### 7.9 æå»ºç¾å URL |
| | | |
| | | #### 32. `buildSignedPreviewUrl(StorageBlobVO storageBlob)` |
| | | |
| | | ä½ç¨ï¼ |
| | | |
| | | - 使ç¨ç³»ç»é»è®¤è¿ææ¶é´ï¼çæé¢è§é¾æ¥ |
| | | |
| | | å®é
è°ç¨ï¼ |
| | | |
| | | - å
é¨çä»·äºè°ç¨ `buildSignedUrl(storageBlob, "/preview/", properties.getExpired())` |
| | | |
| | | #### 33. `buildSignedDownloadUrl(StorageBlobVO storageBlob)` |
| | | |
| | | ä½ç¨ï¼ |
| | | |
| | | - 使ç¨ç³»ç»é»è®¤è¿ææ¶é´ï¼çæä¸è½½é¾æ¥ |
| | | |
| | | å®é
è°ç¨ï¼ |
| | | |
| | | - å
é¨çä»·äºè°ç¨ `buildSignedUrl(storageBlob, "/download/", properties.getExpired())` |
| | | |
| | | #### 34. `buildSignedUrl(StorageBlobVO storageBlob, String actionPath, BigDecimal expired)` |
| | | |
| | | ä½ç¨ï¼ |
| | | |
| | | - æå»ºç»ä¸ç带ç¾åé¢è§/ä¸è½½å°å |
| | | |
| | | æ¯æï¼ |
| | | |
| | | - `actionPath = "/preview/"` |
| | | - `actionPath = "/download/"` |
| | | |
| | | æ ¸å¿é»è¾ï¼ |
| | | |
| | | 1. æ ¡éªè·¯å¾åæ°åæä»¶ä¿¡æ¯ |
| | | 2. æ¼æ¥åºç¡è®¿é®å°å |
| | | 3. 妿 `expired == -1`ï¼ä¸çæ tokenï¼ç´æ¥èµ° `publicKey` |
| | | 4. å¦åçæå¸¦è¿ææ¶é´ç JWT token |
| | | 5. æ token çä½¿ç¨æ¬¡æ°ä¿¡æ¯åå
¥ Redis |
| | | 6. è¿åæç» URL |
| | | |
| | | éè¦è¯´æï¼ |
| | | |
| | | - `expired` åä½ä¸ºåé |
| | | - é»è®¤è¿ææ¶é´ä¸º 120 åé |
| | | - éæ°¸ä¹
龿¥ä¼åâè¿ææ¶é´ + ä½¿ç¨æ¬¡æ°éå¶âåéæ§å¶ |
| | | |
| | | ### 7.10 token ä½¿ç¨æ§å¶ |
| | | |
| | | #### 35. `cacheTokenUsage(String token, long expiredMillis)` |
| | | |
| | | ä½ç¨ï¼ |
| | | |
| | | - æ token ä½¿ç¨æ¬¡æ°åå§åå° Redis |
| | | |
| | | ç¹ç¹ï¼ |
| | | |
| | | - åå§å¼åå
¥ä¸º `0` |
| | | - TTL ä¸ token è¿ææ¶é´ä¿æä¸è´ |
| | | |
| | | 说æï¼ |
| | | |
| | | - è¿æ¯ç§ææ¹æ³ï¼ä¾ `buildSignedUrl()` å
é¨è°ç¨ |
| | | |
| | | #### 36. `buildTokenUsageKey(String token)` |
| | | |
| | | ä½ç¨ï¼ |
| | | |
| | | - ç»ä¸çæ Redis key |
| | | |
| | | æ ¼å¼ï¼ |
| | | |
| | | - `file:token:usage:{token}` |
| | | |
| | | 说æï¼ |
| | | |
| | | - è¿æ¯ç§ææ¹æ³ |
| | | |
| | | #### 37. `validateTokenUsage(String token)` |
| | | |
| | | ä½ç¨ï¼ |
| | | |
| | | - æ ¡éª token æ¯å¦è¿è½ç»§ç»ä½¿ç¨ |
| | | |
| | | æ ¸å¿é»è¾ï¼ |
| | | |
| | | 1. ä» Redis 读åå½åä½¿ç¨æ¬¡æ° |
| | | 2. å¦ææ²¡æå¼ï¼è®¤ä¸ºé¾æ¥å·²è¿ææå·²å¤±æ |
| | | 3. å¦æè¾¾å°ä¸éï¼ç«å³å é¤ Redis è®°å½å¹¶æ¥é |
| | | 4. å¦åèªå¢ä¸æ¬¡ä½¿ç¨æ¬¡æ° |
| | | 5. 妿èªå¢åè¾¾å°ä¸éï¼åå é¤ Redis è®°å½ |
| | | |
| | | 说æï¼ |
| | | |
| | | - è¯¥æ¹æ³é常ä¼å¨å®é
è®¿é®æä»¶æ¶ç±æå¡å±è°ç¨ |
| | | |
| | | #### 38. `resolveLimit()` |
| | | |
| | | ä½ç¨ï¼ |
| | | |
| | | - è§£æ token å¯ä½¿ç¨æ¬¡æ°ä¸é |
| | | |
| | | è§åï¼ |
| | | |
| | | - `properties.getUseLimit() <= 0` æ¶ï¼é»è®¤è¿å `10` |
| | | |
| | | 说æï¼ |
| | | |
| | | - è¿æ¯ç§ææ¹æ³ |
| | | |
| | | ### 7.11 è·¯å¾ä¸å缩 |
| | | |
| | | #### 39. `buildRelativePath()` |
| | | |
| | | ä½ç¨ï¼ |
| | | |
| | | - çææä»¶åå¨ç¸å¯¹è·¯å¾ |
| | | |
| | | æ ¼å¼ï¼ |
| | | |
| | | - `yyyy/MMdd` |
| | | |
| | | ä¾å¦ï¼ |
| | | |
| | | - `2026/0430` |
| | | |
| | | ç¨éï¼ |
| | | |
| | | - ä¸è¬ç¨äºææ¥æåç®å½ä¿åä¸ä¼ æä»¶ |
| | | |
| | | #### 40. `compressFile(File file)` |
| | | |
| | | ä½ç¨ï¼ |
| | | |
| | | - 对å¾çè¿è¡å缩ï¼éå¾çæä¸æ»¡è¶³æ¡ä»¶æ¶è¿ååæä»¶ |
| | | |
| | | å缩æ¡ä»¶ï¼ |
| | | |
| | | 1. å¼å¯äº `properties.getCompress()` |
| | | 2. æä»¶æ¯å¾ç |
| | | 3. æä»¶å¤§å°å¤§äº `properties.getNeedCompressSize()` |
| | | |
| | | å¤çé»è¾ï¼ |
| | | |
| | | 1. ç®æ æä»¶å为 `thumb_åæä»¶å` |
| | | 2. 妿å缩æä»¶å·²åå¨ï¼ç´æ¥å¤ç¨ |
| | | 3. ä½¿ç¨ `Thumbnailator` æå尺寸å缩ç»è´¨ |
| | | 4. 妿å缩失败ï¼é级è¿ååæä»¶ |
| | | |
| | | 说æï¼ |
| | | |
| | | - å½åä¸è½½åé¢è§æ¥å£é½ä¼è°ç¨è¿ä¸ªæ¹æ³ |
| | | |
| | | #### 41. `isImage(String fileName)` |
| | | |
| | | ä½ç¨ï¼ |
| | | |
| | | - ç®å夿æä»¶æ¯å¦æ¯å¾ç |
| | | |
| | | æ¯æåç¼ï¼ |
| | | |
| | | - `jpg` |
| | | - `jpeg` |
| | | - `png` |
| | | |
| | | 说æï¼ |
| | | |
| | | - è¿æ¯ç§ææ¹æ³ï¼ä¾ `compressFile()` ä½¿ç¨ |
| | | |
| | | ## 8. æ¨è使ç¨é¡ºåº |
| | | |
| | | ä¸å¡ä¸æå¸¸è§çæ¥å
¥é¡ºåºå¦ä¸ï¼ |
| | | |
| | | 1. å端ä¸ä¼ æä»¶å° `/common/upload` |
| | | 2. æ¿å°è¿åç»æä¸çæä»¶ id |
| | | 3. ä¸å¡ä¿åæ¶è°ç¨ `/storageAttachment/add` |
| | | 4. ä¼ å
¥ `application + recordType + recordId + storageBlobDTOs` |
| | | 5. åç»é¡µé¢åæ¾æ¶æä¸å¡æ¡ä»¶è°ç¨éä»¶æ¥è¯¢ |
| | | 6. å端使ç¨è¿åç `previewURL` æ `downloadURL` |
| | | |
| | | ## 9. å¸¸è§æ³¨æç¹ |
| | | |
| | | ### 9.1 å
ä¸ä¼ ï¼åç»å® |
| | | |
| | | - `/common/upload` åªè´è´£æä»¶å
¥åº |
| | | - `/storageAttachment/add` ææ¯åä¸å¡æ°æ®å»ºç«å
³ç³» |
| | | |
| | | ### 9.2 `application` å¾éè¦ |
| | | |
| | | - å䏿¡ `recordId` ä¸å¯è½æå¤ç»ä¸åç¨ééä»¶ |
| | | - å é¤åæ¥è¯¢æ¶ï¼ç»å¸¸ä¾èµ `application` |
| | | |
| | | ### 9.3 ä¸è½½é¾æ¥ä¸æ¯æ°¸ä¹
ææ |
| | | |
| | | - æ®é龿¥ä¸è¬éè¿ JWT token æ§å¶ |
| | | - åæ¶åè¿ææ¶é´åä½¿ç¨æ¬¡æ°éå¶ |
| | | |
| | | ### 9.4 å
Œ
±æä»¶è¦æ
ç¨ |
| | | |
| | | - `public/upload` ä¸ä¼ çæä»¶å¯èµ°æ°¸ä¹
å
¬å¼è®¿é® |
| | | - éåå
¬å¼èµæºï¼ä¸éåæææä»¶ |
| | | |
| | | ### 9.5 å¾çé¢è§/ä¸è½½å¯è½è¿åå缩æä»¶ |
| | | |
| | | - å½åæ§å¶å¨å¨ä¸è½½åé¢è§åé½ä¼è°ç¨ `compressFile()` |
| | | - 大å¾å¨è®¿é®æ¶å¯è½ä½¿ç¨å缩åç坿¬ |
| | | |
| | | ## 10. ä¸å¥è¯æ»ç» |
| | | |
| | | æ¬é¡¹ç®çæä»¶ä¸ä¼ æ¹æ¡æ¯â䏤鶿®µæ¨¡åâï¼ |
| | | |
| | | - 第ä¸é¶æ®µä¸ä¼ æä»¶ï¼çæ `storage_blob` |
| | | - 第äºé¶æ®µç»å®ä¸å¡ï¼çæ `storage_attachment` |
| | | |
| | | è `FileUtil` åè´è´£æâä¸ä¼ åçæä»¶âåæâ坿¥è¯¢ãå¯é¢è§ãå¯ä¸è½½ãå¯å é¤ã坿§æ¶æâç宿´éä»¶è½åã |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # éè´æºè½ä½å¤æä»¶åæå端èè°è¯´æ |
| | | |
| | | ## æµç¨è¯´æ |
| | | |
| | | å端已æ°å¢éè´æºè½ä½å¤æä»¶åæç¡®è®¤æµç¨ï¼ |
| | | |
| | | 1. å端ä¸ä¼ å¤ä¸ªéè´ç¸å
³æä»¶ï¼å¹¶éå¸¦ç¨æ·è¦æ±ã |
| | | 2. å端æåæä»¶å
容ï¼äº¤ç»éè´æºè½ä½åæã |
| | | 3. æºè½ä½è¿åå¾
客æ·ç¡®è®¤çç»æå JSONã |
| | | 4. å端å±ç¤ºæè¦ãé£é©ãç¼ºå¤±åæ®µåå¾
å¤çæ°æ®ã |
| | | 5. 客æ·ç¡®è®¤æè¡¥å
æ°æ®åï¼å端è°ç¨ç¡®è®¤æ¥å£ã |
| | | 6. åç«¯æ ¹æ®ç¡®è®¤åçæ°æ®æ§è¡å¯¹åºéè´ä¸å¡å¤çã |
| | | |
| | | åææ¥å£ä¸ä¼è½åºï¼åªæç¡®è®¤æ¥å£ä¼æ§è¡ä¸å¡å¤çã |
| | | |
| | | ## æ¥å£ 1ï¼éè´å¤æä»¶åæ |
| | | |
| | | ```http |
| | | POST /purchase-ai/analyze-files |
| | | Content-Type: multipart/form-data |
| | | ``` |
| | | |
| | | 请æ±åæ°ï¼ |
| | | |
| | | | åæ° | ç±»å | å¿
å¡« | 说æ | |
| | | | --- | --- | --- | --- | |
| | | | files | file[] | æ¯ | 夿件ä¸ä¼ åæ®µï¼å段åå¿
é¡»æ¯ `files` | |
| | | | message | string | å¦ | ç¨æ·è¦æ±ï¼ä¾å¦ï¼è¯·æ ¹æ®è¿äºéè´åååæç»æ´çéè´å°è´¦æ°æ® | |
| | | | memoryId | string | å¦ | ä¼è¯ IDï¼ä¸ä¼ æ¶å端ä¼èªå¨çæå
é¨ä¼è¯ | |
| | | |
| | | è¿åï¼ |
| | | |
| | | ```http |
| | | Content-Type: text/stream;charset=utf-8 |
| | | ``` |
| | | |
| | | å端éè¦æ¼æ¥å®æ´æµå¼ææ¬ååæ§è¡ `JSON.parse`ã |
| | | |
| | | è¿å JSON ç»æç¤ºä¾ï¼ |
| | | |
| | | ```json |
| | | { |
| | | "success": true, |
| | | "businessType": "purchase_ledger", |
| | | "action": "confirm_required", |
| | | "description": "å·²æ ¹æ®æä»¶æ´çåºéè´å°è´¦è稿ï¼è¯·ç¡®è®¤ã", |
| | | "confidence": 0.86, |
| | | "missingFields": [], |
| | | "warnings": [], |
| | | "payload": {}, |
| | | "preview": [] |
| | | } |
| | | ``` |
| | | |
| | | åæ®µè¯´æï¼ |
| | | |
| | | | åæ®µ | 说æ | |
| | | | --- | --- | |
| | | | success | æ¯å¦åææå | |
| | | | businessType | ä¸å¡ç±»åï¼`purchase_ledger`ã`payment_registration`ã`purchase_return_order`ã`unknown` | |
| | | | action | åºå®ä¸º `confirm_required` | |
| | | | description | ä¸æè¯´æ | |
| | | | confidence | 置信度ï¼0 å° 1 | |
| | | | missingFields | ç¼ºå¤±åæ®µï¼å端éè¦æç¤ºç¨æ·è¡¥å
| |
| | | | warnings | é£é©æç¤º | |
| | | | payload | å¾
客æ·ç¡®è®¤å¹¶æäº¤ç»ç¡®è®¤æ¥å£çæ°æ® | |
| | | | preview | ç»å®¢æ·ç¡®è®¤ç¨ç䏿æè¦ | |
| | | |
| | | ## æ¥å£ 2ï¼ç¡®è®¤å¹¶æ§è¡ä¸å¡å¤ç |
| | | |
| | | ```http |
| | | POST /purchase-ai/analyze-files/confirm |
| | | Content-Type: application/json |
| | | ``` |
| | | |
| | | 请æ±ä½ï¼ |
| | | |
| | | ```json |
| | | { |
| | | "businessType": "purchase_ledger", |
| | | "payload": { |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | å½åæ¯æç `businessType`ï¼ |
| | | |
| | | | businessType | 说æ | å端å¤ç | |
| | | | --- | --- | --- | |
| | | | purchase_ledger | éè´å°è´¦ | è°ç¨éè´å°è´¦æ°å¢/ç¼è¾ | |
| | | | payment_registration | 仿¬¾ç»è®° | è°ç¨ä»æ¬¾ç»è®°æ°å¢ | |
| | | | purchase_return_order | éè´éè´§å | è°ç¨éè´éè´§åæ°å¢ | |
| | | |
| | | 确认æ¥å£è¿åæ®é `AjaxResult`ã |
| | | |
| | | ## éè´å°è´¦ Payload çº¦å® |
| | | |
| | | éè´å°è´¦ç¡®è®¤æ¨è使ç¨ä¸¤ä¸ªéåï¼ |
| | | |
| | | ```json |
| | | { |
| | | "businessType": "purchase_ledger", |
| | | "payload": { |
| | | "purchaseLedgers": [] |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | åæ®µçº¦å®ï¼ |
| | | |
| | | - `purchaseLedgers` æ¾éè´è®¢å/éè´å°è´¦ä¸»è¡¨æ°æ®ï¼å段åå¿
é¡»ä¸ `PurchaseLedgerDto` ä¿æä¸è´ã |
| | | - 产åæç»æ¾å¨æ¯æ¡ `purchaseLedgers[i].productData` ä¸ï¼å¯¹åº `PurchaseLedgerDto` ç `private List<SalesLedgerProduct> productData;`ã |
| | | - é¡¶å± `payload.productData` ä»
ä½ä¸ºæ§æ ¼å¼å
¼å®¹ï¼ä¸å»ºè®®å端继ç»ä½¿ç¨ã |
| | | - æä»¶ä¸çâéè´åå·âå°±æ¯âéè´ååå·âï¼å端å¯ä»¥ç»ä¸æ å°æ `purchaseContractNumber`ã |
| | | - æä»¶ä¸çâéå®åå·âå°±æ¯âéå®ååå·âï¼å端å¯ä»¥ç»ä¸æ å°æ `salesContractNo`ã |
| | | - æ¥æåæ®µç»ä¸ä½¿ç¨ `yyyy-MM-dd`ï¼ä¾å¦ `2026-04-30`ï¼ä¸è¦æäº¤ `4/30/26`ã`2026/4/30`ã`2026å¹´4æ30æ¥` æå¸¦æ¶åç§çæ ¼å¼ã |
| | | - éè´å°è´¦ä¸éè¦åç«¯ä¼ å®¡æ¹äººï¼ä¸è¦æäº¤ `approveUserIds`ã`approverId`ã |
| | | - `missingFields` é¢å客æ·å±ç¤ºï¼åªæ¾ä¸æç¼ºå¤±é¡¹ï¼ä¾å¦ `ä¾åºååç§°`ã`å«ç¨åä»·`ï¼ä¸è¦å±ç¤ºè±æå段åã |
| | | - éè´å°è´¦ä¸å¡å¿
å¡«ï¼éè´ååå·ãä¾åºååç§°æä¾åºåIDã |
| | | - 产åæç»ä¸å¡å¿
å¡«ï¼äº§ååç§°ãè§æ ¼åå·ãåä½ãæ°éãå«ç¨åä»·ãå«ç¨æ»ä»·ï¼å¦æåªæå«ç¨æ»ä»·åæ°éï¼å端ä¼èªå¨è®¡ç®å«ç¨åä»·ï¼å¦æåªæå«ç¨åä»·åæ°éï¼å端ä¼èªå¨è®¡ç®å«ç¨æ»ä»·ã |
| | | - 产åæç»å¯éè¿ `purchaseContractNumber`ã`purchaseContractNo`ã`éè´ååå·`ã`éè´åå·`ã`éè´è®¢åå·` å
³è对åºéè´è®¢åï¼ä¹å¯éè¿ `salesContractNo`ã`salesContractNumber`ã`éå®ååå·`ã`éå®åå·`ã`éå®è®¢åå·` è¾
å©å¹é
ã |
| | | |
| | | `purchaseLedgers` åæ¡è®°å½å
许使ç¨ç `PurchaseLedgerDto` åæ®µï¼ |
| | | |
| | | ```text |
| | | entryDateStart, entryDateEnd, id, purchaseContractNumber, |
| | | supplierId, supplierName, isWhite, recorderId, recorderName, salesContractNo, |
| | | salesContractNoId, projectName, entryDate, executionDate, remarks, |
| | | attachmentMaterials, createdAt, updatedAt, salesLedgerId, hasChildren, Type, |
| | | productData, tempFileIds, SalesLedgerFiles, phoneNumber, businessPersonId, |
| | | productId, productModelId, invoiceNumber, invoiceAmount, ticketRegistrationId, |
| | | contractAmount, receiptPaymentAmount, unReceiptPaymentAmount, type, |
| | | paymentMethod, approvalStatus, templateName |
| | | ``` |
| | | |
| | | 示ä¾ï¼ |
| | | |
| | | ```json |
| | | { |
| | | "purchaseLedgers": [ |
| | | { |
| | | "purchaseContractNumber": "CG-2026-001", |
| | | "supplierName": "åé示ä¾ä¾åºå", |
| | | "salesContractNo": "XS-2026-001", |
| | | "projectName": "示ä¾é¡¹ç®", |
| | | "entryDate": "2026-04-30", |
| | | "executionDate": "2026-04-30", |
| | | "contractAmount": 120000, |
| | | "remarks": "ç±æä»¶åæçæï¼å¾
确认", |
| | | "productData": [ |
| | | { |
| | | "productCategory": "示ä¾äº§å", |
| | | "specificationModel": "åå·A", |
| | | "unit": "ä»¶", |
| | | "quantity": 10, |
| | | "taxInclusiveUnitPrice": 12000, |
| | | "taxInclusiveTotalPrice": 120000, |
| | | "type": 2 |
| | | } |
| | | ] |
| | | } |
| | | ] |
| | | } |
| | | ``` |
| | | |
| | | ## å端å¤ç建议 |
| | | |
| | | 1. ç¨æ·éæ©å¤ä¸ªæä»¶ï¼å¡«ååæè¦æ±ã |
| | | 2. ä½¿ç¨ `multipart/form-data` è°ç¨ `/purchase-ai/analyze-files`ã |
| | | 3. æ¼æ¥æµå¼è¿åææ¬ã |
| | | 4. 坹宿´ææ¬æ§è¡ `JSON.parse`ã |
| | | 5. å±ç¤º `preview`ã`warnings`ã`missingFields` å `payload`ã |
| | | 6. 妿 `missingFields` ä¸ä¸ºç©ºï¼å¼å¯¼ç¨æ·è¡¥å
æç¼è¾ `payload`ã |
| | | 7. ç¨æ·ç¡®è®¤åï¼å° `businessType` å确认åç `payload` æäº¤å° `/purchase-ai/analyze-files/confirm`ã |
| | | |
| | | ## 注æäºé¡¹ |
| | | |
| | | - æä»¶ä¸ä¼ åæ®µåå¿
é¡»æ¯ `files`ã |
| | | - åææ¥å£åªçæå¾
ç¡®è®¤æ°æ®ï¼ä¸ä¼æ§è¡ä¸å¡è½åºã |
| | | - 确认æ¥å£æä¼æ§è¡ä¸å¡å¤çã |
| | | - 妿 `payload` 缺å°å¿
è¦ä¸å¡ IDï¼ç¡®è®¤æ¥å£å¯è½è¿åä¸å¡æ ¡éªé误ã |
| | | - å端éè¦æ `missingFields` æç¡®å±ç¤ºç»ç¨æ·ã |
| | | - AI è¿åå
容æåæ³ JSON å¤çï¼ä¸è¦ææ®éèªç¶è¯è¨å±ç¤ºã |
| | |
| | | extractTimeRange(text) |
| | | ); |
| | | } |
| | | if (containsAny(text, "æµè½¬", "è¿åº¦", "èç¹", "æ¥å¿")) { |
| | | if (containsAny(text, "æµè½¬", "è¿åº¦", "èç¹", "æ¥å¿", "å¡å¨", "å¡å°", "å½å审æ¹äºº", "å¤çè®°å½")) { |
| | | return StringUtils.hasText(approveId) |
| | | ? approveTodoTools.getTodoProgress(memoryId, approveId) |
| | | : missingApproveId("todo_progress", "æ¥è¯¢å®¡æ¹è¿åº¦éè¦æä¾æµç¨ç¼å·ã"); |
| | |
| | | ? approveTodoTools.getTodoDetail(memoryId, approveId) |
| | | : missingApproveId("todo_detail", "æ¥è¯¢å®¡æ¹è¯¦æ
éè¦æä¾æµç¨ç¼å·ã"); |
| | | } |
| | | if (containsAny(text, "åæ¶å®¡æ ¸", "æ¤éå®¡æ ¸", "åéå®¡æ ¸")) { |
| | | if (containsAny(text, "åæ¶å®¡æ ¸", "æ¤éå®¡æ ¸", "åéå®¡æ ¸", "æ¤é审æ¹", "æ¤å审æ¹") |
| | | || (containsAny(text, "æ¤é", "æ¤å") && containsAny(text, "å®¡æ¹æä½", "å®¡æ ¸æä½"))) { |
| | | return StringUtils.hasText(approveId) |
| | | ? approveTodoTools.cancelReviewTodo(memoryId, approveId, extractTail(text, "åå ")) |
| | | ? approveTodoTools.cancelReviewTodo(memoryId, approveId, firstNonBlank(extractTail(text, "åå "), extractTail(text, "夿³¨"))) |
| | | : missingApproveId("cancel_review_action", "åæ¶å®¡æ ¸éè¦æä¾æµç¨ç¼å·ã"); |
| | | } |
| | | if (containsAny(text, "å é¤")) { |
| | | if (containsAny(text, "å é¤", "ç§»é¤")) { |
| | | return StringUtils.hasText(approveId) |
| | | ? approveTodoTools.deleteTodo(memoryId, approveId) |
| | | : missingApproveId("delete_action", "å é¤å®¡æ¹åéè¦æä¾æµç¨ç¼å·ã"); |
| | | } |
| | | if (containsAny(text, "驳å", "æç»")) { |
| | | return StringUtils.hasText(approveId) |
| | | ? approveTodoTools.reviewTodo(memoryId, approveId, "reject", extractTail(text, "åå ")) |
| | | ? approveTodoTools.reviewTodo(memoryId, approveId, "reject", firstNonBlank(extractTail(text, "åå "), extractTail(text, "夿³¨"))) |
| | | : missingApproveId("review_action", "驳å审æ¹éè¦æä¾æµç¨ç¼å·ã"); |
| | | } |
| | | if (containsAny(text, "å®¡æ ¸éè¿", "审æ¹éè¿", "éè¿å®¡æ¹", "åæå®¡æ¹", "审æ¹åæ")) { |
| | |
| | | && !containsAny(text, "æªéè¿", "éè¿ç", "审æ¹éè¿ç", "å®¡æ ¸éè¿ç")) { |
| | | return approveTodoTools.reviewTodo(memoryId, approveId, "approve", extractTail(text, "夿³¨")); |
| | | } |
| | | if (containsAny(text, "ä¿®æ¹")) { |
| | | if (containsAny(text, "ä¿®æ¹", "æ´æ°", "åæ´")) { |
| | | return StringUtils.hasText(approveId) |
| | | ? approveTodoTools.updateTodo( |
| | | memoryId, |
| | |
| | | extractValue(text, "夿³¨")) |
| | | : missingApproveId("update_action", "ä¿®æ¹å®¡æ¹åéè¦æä¾æµç¨ç¼å·ã"); |
| | | } |
| | | if (containsAny(text, "å表", "å¾
å", "æ¥è¯¢å®¡æ¹")) { |
| | | if (containsAny(text, "å表", "å¾
å", "æ¥è¯¢å®¡æ¹", "åæ®", "æµç¨", "å®¡æ¹æ¹")) { |
| | | return approveTodoTools.listTodos( |
| | | memoryId, |
| | | extractStatus(text), |
| | | extractApproveType(text), |
| | | extractKeyword(text), |
| | | extractLimit(text)); |
| | | extractLimit(text), |
| | | extractScope(text)); |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | private boolean isStatsIntent(String text) { |
| | | if (containsAny(text, "ç»è®¡", "åæ", "å¾è¡¨", "è¶å¿", "å æ¯", "æ±æ»", "æ»é")) { |
| | | if (containsAny(text, "ç»è®¡", "åæ", "å¾è¡¨", "è¶å¿", "å æ¯", "æ±æ»", "æ»é", "åå¸", "åæå¤å°", "æå¤å°")) { |
| | | return true; |
| | | } |
| | | boolean hasQueryWord = containsAny(text, "æ¥è¯¢", "æ¥ç", "çä¸", "çç", "è·å"); |
| | |
| | | if (containsAny(text, "å¾
å®¡æ ¸", "å¾
审æ¹")) { |
| | | return "pending"; |
| | | } |
| | | if (containsAny(text, "å®¡æ ¸ä¸")) { |
| | | if (containsAny(text, "å®¡æ ¸ä¸", "å¤çä¸", "å¤çä¸ç", "åçä¸")) { |
| | | return "processing"; |
| | | } |
| | | if (containsAny(text, "å·²éè¿", "å®¡æ ¸å®æ")) { |
| | | if (containsAny(text, "å·²éè¿", "éè¿", "å®¡æ ¸å®æ", "审æ¹å®æ")) { |
| | | return "approved"; |
| | | } |
| | | if (containsAny(text, "æªéè¿", "驳å")) { |
| | | if (containsAny(text, "æªéè¿", "驳å", "已驳å", "æç»")) { |
| | | return "rejected"; |
| | | } |
| | | if (containsAny(text, "éæ°æäº¤")) { |
| | |
| | | private String extractKeyword(String text) { |
| | | String cleaned = text |
| | | .replace("æ¥è¯¢", "") |
| | | .replace("æ¥ç", "") |
| | | .replace("ååº", "") |
| | | .replace("帮æ", "") |
| | | .replace("审æ¹", "") |
| | | .replace("åæ®", "") |
| | | .replace("å¾
å", "") |
| | | .replace("å表", "") |
| | | .replace("å10æ¡", "") |
| | |
| | | return matcher.find() ? matcher.group(2).trim() : null; |
| | | } |
| | | |
| | | private String extractScope(String text) { |
| | | if (containsAny(text, "æåèµ·", "ææäº¤", "æç³è¯·", "ç³è¯·äººæ¯æ")) { |
| | | return "applicant"; |
| | | } |
| | | if (containsAny(text, "å¾
æå®¡æ¹", "å¾
æå®¡æ ¸", "æå¤ç", "æå®¡æ¹", "å½åå¾
æ", "éè¦æå¤ç")) { |
| | | return "approver"; |
| | | } |
| | | return "related"; |
| | | } |
| | | |
| | | private String firstNonBlank(String first, String second) { |
| | | return StringUtils.hasText(first) ? first : second; |
| | | } |
| | | |
| | | private String missingApproveId(String type, String description) { |
| | | Map<String, Object> result = new LinkedHashMap<>(); |
| | | result.put("success", false); |
| | |
| | | } |
| | | String text = message.trim(); |
| | | |
| | | if (containsAny(text, "æè¡", "æå", "åå ", "åäº", "åå") && containsAny(text, "ç©æ", "产å", "åææ", "éè´éé¢", "éé¢")) { |
| | | return purchaseAgentTools.rankPurchaseMaterials( |
| | | memoryId, |
| | | extractStartDate(text), |
| | | extractEndDate(text), |
| | | text, |
| | | extractLimit(text) |
| | | ); |
| | | } |
| | | if (containsAny(text, "æªå
¥åº", "å¾
å
¥åº", "没æå
¥åº", "è¿æªå
¥åº")) { |
| | | return purchaseAgentTools.listUnstockedPurchaseOrders( |
| | | memoryId, |
| | | extractStartDate(text), |
| | | extractEndDate(text), |
| | | extractKeyword(text), |
| | | extractLimit(text) |
| | | ); |
| | | } |
| | | if (containsAny(text, "å°è´§å¼å¸¸", "å°è´§æå¼å¸¸", "å¼å¸¸å°è´§", "å°è´§é®é¢", "ä¾åºåå°è´§å¼å¸¸")) { |
| | | return purchaseAgentTools.listArrivalExceptions( |
| | | memoryId, |
| | | extractStartDate(text), |
| | | extractEndDate(text), |
| | | text, |
| | | extractLimit(text) |
| | | ); |
| | | } |
| | | if (containsAny(text, "å¾
仿¬¾", "æªä»æ¬¾", "æªä»æ¸
", "å¾
æ¯ä»", "åºä»")) { |
| | | return purchaseAgentTools.listPendingPaymentOrders( |
| | | memoryId, |
| | | extractStartDate(text), |
| | | extractEndDate(text), |
| | | extractKeyword(text), |
| | | extractLimit(text) |
| | | ); |
| | | } |
| | | if (containsAny(text, "éè´§", "éæ", "ææ¶")) { |
| | | return purchaseAgentTools.listPurchaseReturns( |
| | | memoryId, |
| | | extractStartDate(text), |
| | | extractEndDate(text), |
| | | extractKeyword(text), |
| | | extractLimit(text) |
| | | ); |
| | | } |
| | | if (isStatsIntent(text)) { |
| | | return purchaseAgentTools.getPurchaseStats( |
| | | memoryId, |
| | |
| | | if (containsAny(text, "详æ
", "æç»") && extractId(text) != null) { |
| | | return purchaseAgentTools.getPurchaseLedgerDetail(memoryId, extractId(text)); |
| | | } |
| | | if (containsAny(text, "å°è´¦", "éè´å", "åå", "å表", "æ¥è¯¢")) { |
| | | if (containsAny(text, "å°è´¦", "éè´å", "éè´è®¢å", "订å", "åå", "å表", "æ¥è¯¢")) { |
| | | return purchaseAgentTools.listPurchaseLedgers( |
| | | memoryId, |
| | | extractKeyword(text), |
| | |
| | | } |
| | | |
| | | private boolean isStatsIntent(String text) { |
| | | if (containsAny(text, "ç»è®¡", "åæ", "æ¥è¡¨", "æ±æ»", "è¶å¿", "æ°æ®çæ¿")) { |
| | | if (containsAny(text, "ç»è®¡", "åæ", "æ¥è¡¨", "æ±æ»", "è¶å¿", "æ°æ®çæ¿", "æ
åµ", "æå¤å°")) { |
| | | return true; |
| | | } |
| | | boolean queryWord = containsAny(text, "æ¥è¯¢", "æ¥ç", "çä¸", "çç", "è·å"); |
| | |
| | | .replace("æ¥è¯¢", "") |
| | | .replace("æ¥ç", "") |
| | | .replace("éè´", "") |
| | | .replace("éè´å", "") |
| | | .replace("éè´è®¢å", "") |
| | | .replace("订å", "") |
| | | .replace("å°è´¦", "") |
| | | .replace("å表", "") |
| | | .replace("åªäº", "") |
| | | .replace("ååº", "") |
| | | .replace("帮æ", "") |
| | | .replace("æè¿10æ¡", "") |
| | | .replace("å10æ¡", "") |
| | | .trim(); |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.ai.bean; |
| | | |
| | | import java.util.Map; |
| | | |
| | | public class PurchaseAiConfirmRequest { |
| | | |
| | | private String businessType; |
| | | |
| | | private Map<String, Object> payload; |
| | | |
| | | public String getBusinessType() { |
| | | return businessType; |
| | | } |
| | | |
| | | public void setBusinessType(String businessType) { |
| | | this.businessType = businessType; |
| | | } |
| | | |
| | | public Map<String, Object> getPayload() { |
| | | return payload; |
| | | } |
| | | |
| | | public void setPayload(Map<String, Object> payload) { |
| | | this.payload = payload; |
| | | } |
| | | } |
| | |
| | | package com.ruoyi.ai.config; |
| | | |
| | | import com.ruoyi.ai.store.MongoChatMemoryStore; |
| | | import dev.langchain4j.community.model.dashscope.QwenStreamingChatModel; |
| | | import dev.langchain4j.memory.chat.ChatMemoryProvider; |
| | | import dev.langchain4j.memory.chat.MessageWindowChatMemory; |
| | | import org.springframework.beans.factory.annotation.Value; |
| | | import org.springframework.context.annotation.Bean; |
| | | import org.springframework.context.annotation.Configuration; |
| | | |
| | |
| | | .chatMemoryStore(mongoChatMemoryStore) |
| | | .build(); |
| | | } |
| | | |
| | | @Bean("purchaseVisionStreamingChatModel") |
| | | QwenStreamingChatModel purchaseVisionStreamingChatModel( |
| | | @Value("${langchain4j.community.dashscope.streaming-chat-model.api-key}") String apiKey) { |
| | | return QwenStreamingChatModel.builder() |
| | | .apiKey(apiKey) |
| | | .modelName("qwen-vl-max") |
| | | .isMultimodalModel(true) |
| | | .build(); |
| | | } |
| | | } |
| | |
| | | package com.ruoyi.ai.controller; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.fasterxml.jackson.core.type.TypeReference; |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | import com.ruoyi.ai.assistant.PurchaseAgent; |
| | | import com.ruoyi.ai.assistant.PurchaseIntentExecutor; |
| | | import com.ruoyi.ai.bean.ChatForm; |
| | | import com.ruoyi.ai.bean.PurchaseAiConfirmRequest; |
| | | import com.ruoyi.ai.context.AiSessionUserContext; |
| | | import com.ruoyi.ai.service.AiChatSessionService; |
| | | import com.ruoyi.ai.service.AiFileTextExtractor; |
| | | import com.ruoyi.ai.store.MongoChatMemoryStore; |
| | | import com.ruoyi.basic.mapper.SupplierManageMapper; |
| | | import com.ruoyi.basic.pojo.SupplierManage; |
| | | import com.ruoyi.common.utils.SecurityUtils; |
| | | import com.ruoyi.common.utils.StringUtils; |
| | | import com.ruoyi.framework.security.LoginUser; |
| | | import com.ruoyi.framework.web.controller.BaseController; |
| | | import com.ruoyi.framework.web.domain.AjaxResult; |
| | | import com.ruoyi.purchase.dto.PurchaseLedgerDto; |
| | | import com.ruoyi.purchase.dto.PurchaseReturnOrderDto; |
| | | import com.ruoyi.purchase.pojo.PaymentRegistration; |
| | | import com.ruoyi.purchase.service.IPaymentRegistrationService; |
| | | import com.ruoyi.purchase.service.IPurchaseLedgerService; |
| | | import com.ruoyi.purchase.service.PurchaseReturnOrdersService; |
| | | import com.ruoyi.sales.pojo.SalesLedgerProduct; |
| | | import dev.langchain4j.data.image.Image; |
| | | import dev.langchain4j.data.message.AiMessage; |
| | | import dev.langchain4j.data.message.ChatMessage; |
| | | import dev.langchain4j.data.message.Content; |
| | | import dev.langchain4j.data.message.ImageContent; |
| | | import dev.langchain4j.data.message.SystemMessage; |
| | | import dev.langchain4j.data.message.TextContent; |
| | | import dev.langchain4j.data.message.UserMessage; |
| | | import dev.langchain4j.model.chat.StreamingChatLanguageModel; |
| | | import dev.langchain4j.model.chat.response.ChatResponse; |
| | | import dev.langchain4j.model.chat.response.StreamingChatResponseHandler; |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import org.springframework.beans.factory.annotation.Qualifier; |
| | | import org.springframework.web.bind.annotation.RequestParam; |
| | | import org.springframework.web.bind.annotation.DeleteMapping; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | 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 org.springframework.web.multipart.MultipartFile; |
| | | import reactor.core.publisher.Flux; |
| | | |
| | | import java.io.IOException; |
| | | import java.math.BigDecimal; |
| | | import java.math.RoundingMode; |
| | | import java.util.Base64; |
| | | import java.time.LocalDate; |
| | | import java.time.ZoneId; |
| | | import java.time.format.DateTimeFormatter; |
| | | import java.time.format.DateTimeParseException; |
| | | import java.util.ArrayList; |
| | | import java.util.Collections; |
| | | import java.util.Date; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.NoSuchElementException; |
| | | import java.util.UUID; |
| | | |
| | | @Tag(name = "éè´æºè½ä½") |
| | | @RestController |
| | | @RequestMapping("/purchase-ai") |
| | | public class PurchaseAiController extends BaseController { |
| | | |
| | | private static final String PURCHASE_FILE_ANALYZE_MEMORY_PREFIX = "purchase-file-analyze::"; |
| | | private static final int MAX_FILE_COUNT = 10; |
| | | private static final int MAX_SINGLE_FILE_TEXT_LENGTH = 8000; |
| | | private static final int MAX_TOTAL_FILE_TEXT_LENGTH = 30000; |
| | | |
| | | private final PurchaseAgent purchaseAgent; |
| | | private final PurchaseIntentExecutor purchaseIntentExecutor; |
| | | private final AiSessionUserContext aiSessionUserContext; |
| | | private final MongoChatMemoryStore mongoChatMemoryStore; |
| | | private final AiChatSessionService aiChatSessionService; |
| | | private final AiFileTextExtractor aiFileTextExtractor; |
| | | private final ObjectMapper objectMapper; |
| | | private final IPurchaseLedgerService purchaseLedgerService; |
| | | private final IPaymentRegistrationService paymentRegistrationService; |
| | | private final PurchaseReturnOrdersService purchaseReturnOrdersService; |
| | | private final SupplierManageMapper supplierManageMapper; |
| | | private final StreamingChatLanguageModel purchaseVisionStreamingChatModel; |
| | | |
| | | public PurchaseAiController(PurchaseAgent purchaseAgent, |
| | | PurchaseIntentExecutor purchaseIntentExecutor, |
| | | AiSessionUserContext aiSessionUserContext, |
| | | MongoChatMemoryStore mongoChatMemoryStore, |
| | | AiChatSessionService aiChatSessionService) { |
| | | AiChatSessionService aiChatSessionService, |
| | | AiFileTextExtractor aiFileTextExtractor, |
| | | ObjectMapper objectMapper, |
| | | IPurchaseLedgerService purchaseLedgerService, |
| | | IPaymentRegistrationService paymentRegistrationService, |
| | | PurchaseReturnOrdersService purchaseReturnOrdersService, |
| | | SupplierManageMapper supplierManageMapper, |
| | | @Qualifier("purchaseVisionStreamingChatModel") StreamingChatLanguageModel purchaseVisionStreamingChatModel) { |
| | | this.purchaseAgent = purchaseAgent; |
| | | this.purchaseIntentExecutor = purchaseIntentExecutor; |
| | | this.aiSessionUserContext = aiSessionUserContext; |
| | | this.mongoChatMemoryStore = mongoChatMemoryStore; |
| | | this.aiChatSessionService = aiChatSessionService; |
| | | this.aiFileTextExtractor = aiFileTextExtractor; |
| | | this.objectMapper = objectMapper; |
| | | this.purchaseLedgerService = purchaseLedgerService; |
| | | this.paymentRegistrationService = paymentRegistrationService; |
| | | this.purchaseReturnOrdersService = purchaseReturnOrdersService; |
| | | this.supplierManageMapper = supplierManageMapper; |
| | | this.purchaseVisionStreamingChatModel = purchaseVisionStreamingChatModel; |
| | | } |
| | | |
| | | @Operation(summary = "éè´å¯¹è¯") |
| | |
| | | .doOnError(ex -> aiChatSessionService.refreshSessionStats(memoryId, loginUser)); |
| | | } |
| | | |
| | | @Operation(summary = "éè´å¤æä»¶åæ") |
| | | @PostMapping(value = "/analyze-files", consumes = "multipart/form-data", produces = "text/stream;charset=utf-8") |
| | | public Flux<String> analyzeFiles(@RequestParam("files") MultipartFile[] files, |
| | | @RequestParam(value = "message", required = false) String message, |
| | | @RequestParam(value = "memoryId", required = false) String memoryId) { |
| | | if (files == null || files.length == 0) { |
| | | return Flux.just("filesä¸è½ä¸ºç©º"); |
| | | } |
| | | if (files.length > MAX_FILE_COUNT) { |
| | | return Flux.just("䏿¬¡æå¤åæ" + MAX_FILE_COUNT + "个æä»¶"); |
| | | } |
| | | |
| | | String rawMemoryId = StringUtils.hasText(memoryId) ? memoryId : UUID.randomUUID().toString(); |
| | | String finalMemoryId = rawMemoryId.startsWith(PURCHASE_FILE_ANALYZE_MEMORY_PREFIX) |
| | | ? rawMemoryId |
| | | : PURCHASE_FILE_ANALYZE_MEMORY_PREFIX + rawMemoryId; |
| | | |
| | | LoginUser loginUser = SecurityUtils.getLoginUser(); |
| | | aiSessionUserContext.bind(finalMemoryId, loginUser); |
| | | |
| | | String finalMessage = StringUtils.hasText(message) |
| | | ? message |
| | | : "请åæè¿äºéè´æä»¶ï¼æåå¯ç¨äºä¸å¡å¤ççæ°æ®ï¼å¹¶æ´çæå¾
客æ·ç¡®è®¤çæ ¼å¼"; |
| | | |
| | | String fileContent; |
| | | try { |
| | | fileContent = buildMultiFileContent(files); |
| | | } catch (IllegalArgumentException ex) { |
| | | return Flux.just(ex.getMessage()); |
| | | } catch (IOException ex) { |
| | | return Flux.just("æä»¶è¯»å失败"); |
| | | } |
| | | |
| | | if (!StringUtils.hasText(fileContent)) { |
| | | return Flux.just("æªæåå°æææä»¶å
容"); |
| | | } |
| | | |
| | | String userPrompt = buildPurchaseFileAnalyzePrompt(finalMessage, fileContent); |
| | | aiChatSessionService.touchSession(finalMemoryId, loginUser, "éè´å¤æä»¶åæ: " + finalMessage); |
| | | |
| | | if (containsImageFile(files)) { |
| | | return chatWithPurchaseVisionModel(finalMemoryId, userPrompt, files) |
| | | .doOnComplete(() -> aiChatSessionService.refreshSessionStats(finalMemoryId, loginUser)) |
| | | .doOnError(ex -> aiChatSessionService.refreshSessionStats(finalMemoryId, loginUser)); |
| | | } |
| | | |
| | | return Flux.defer(() -> purchaseAgent.chat(finalMemoryId, userPrompt)) |
| | | .onErrorResume(NoSuchElementException.class, ex -> { |
| | | mongoChatMemoryStore.deleteMessages(finalMemoryId); |
| | | return purchaseAgent.chat(finalMemoryId, userPrompt); |
| | | }) |
| | | .doOnComplete(() -> aiChatSessionService.refreshSessionStats(finalMemoryId, loginUser)) |
| | | .doOnError(ex -> aiChatSessionService.refreshSessionStats(finalMemoryId, loginUser)); |
| | | } |
| | | |
| | | @Operation(summary = "éè´å¤æä»¶åæç¡®è®¤å¤ç") |
| | | @PostMapping("/analyze-files/confirm") |
| | | public AjaxResult confirmAnalyzeResult(@RequestBody PurchaseAiConfirmRequest request) { |
| | | if (request == null || !StringUtils.hasText(request.getBusinessType())) { |
| | | return AjaxResult.error("businessTypeä¸è½ä¸ºç©º"); |
| | | } |
| | | if (request.getPayload() == null || request.getPayload().isEmpty()) { |
| | | return AjaxResult.error("payloadä¸è½ä¸ºç©º"); |
| | | } |
| | | |
| | | try { |
| | | String businessType = request.getBusinessType().trim(); |
| | | return switch (businessType) { |
| | | case "purchase_ledger" -> processPurchaseLedger(request.getPayload()); |
| | | case "payment_registration" -> processPaymentRegistration(request.getPayload()); |
| | | case "purchase_return_order" -> processPurchaseReturnOrder(request.getPayload()); |
| | | default -> AjaxResult.error("æä¸æ¯æè¯¥ä¸å¡ç±»å: " + businessType); |
| | | }; |
| | | } catch (Exception ex) { |
| | | return AjaxResult.error(toCustomerMessage(ex)); |
| | | } |
| | | } |
| | | |
| | | @Operation(summary = "éè´ä¼è¯å表") |
| | | @GetMapping("/history/sessions") |
| | | public AjaxResult listSessions() { |
| | |
| | | aiSessionUserContext.remove(memoryId); |
| | | return toAjax(aiChatSessionService.deleteCurrentUserSession(memoryId, SecurityUtils.getLoginUser())); |
| | | } |
| | | |
| | | private String buildMultiFileContent(MultipartFile[] files) throws IOException { |
| | | StringBuilder builder = new StringBuilder(); |
| | | int totalLength = 0; |
| | | for (MultipartFile file : files) { |
| | | String text = aiFileTextExtractor.extractText(file); |
| | | if (!StringUtils.hasText(text)) { |
| | | continue; |
| | | } |
| | | String limitedText = text.length() > MAX_SINGLE_FILE_TEXT_LENGTH |
| | | ? text.substring(0, MAX_SINGLE_FILE_TEXT_LENGTH) |
| | | : text; |
| | | if (totalLength + limitedText.length() > MAX_TOTAL_FILE_TEXT_LENGTH) { |
| | | int remain = MAX_TOTAL_FILE_TEXT_LENGTH - totalLength; |
| | | if (remain <= 0) { |
| | | break; |
| | | } |
| | | limitedText = limitedText.substring(0, remain); |
| | | } |
| | | builder.append("\n--- æä»¶: ") |
| | | .append(file.getOriginalFilename()) |
| | | .append(" ---\n") |
| | | .append(limitedText) |
| | | .append('\n'); |
| | | totalLength += limitedText.length(); |
| | | } |
| | | return builder.toString(); |
| | | } |
| | | |
| | | private boolean containsImageFile(MultipartFile[] files) { |
| | | for (MultipartFile file : files) { |
| | | if (aiFileTextExtractor.isImageFile(file)) { |
| | | return true; |
| | | } |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | private Flux<String> chatWithPurchaseVisionModel(String memoryId, String userPrompt, MultipartFile[] files) { |
| | | return Flux.create(sink -> { |
| | | try { |
| | | List<Content> contents = new ArrayList<>(); |
| | | contents.add(TextContent.from(userPrompt)); |
| | | for (MultipartFile file : files) { |
| | | if (!aiFileTextExtractor.isImageFile(file)) { |
| | | continue; |
| | | } |
| | | contents.add(TextContent.from("ä¸é¢è¿å¼ å¾çæä»¶åï¼" + file.getOriginalFilename())); |
| | | contents.add(ImageContent.from(Image.builder() |
| | | .base64Data(Base64.getEncoder().encodeToString(file.getBytes())) |
| | | .mimeType(resolveImageMimeType(file)) |
| | | .build())); |
| | | } |
| | | |
| | | List<ChatMessage> messages = List.of( |
| | | SystemMessage.from("ä½ æ¯éè´ä¸å¡æä»¶åæå©æãè¯·ä»ææ¬åå¾çä¸è¯å«éè´å°è´¦ãéè´äº§åæç»ã仿¬¾æéè´§ä¿¡æ¯ï¼åªè¾åºåæ³ JSONã"), |
| | | UserMessage.from(contents) |
| | | ); |
| | | purchaseVisionStreamingChatModel.chat(messages, new StreamingChatResponseHandler() { |
| | | @Override |
| | | public void onPartialResponse(String partialResponse) { |
| | | sink.next(partialResponse); |
| | | } |
| | | |
| | | @Override |
| | | public void onCompleteResponse(ChatResponse completeResponse) { |
| | | sink.complete(); |
| | | } |
| | | |
| | | @Override |
| | | public void onError(Throwable error) { |
| | | sink.error(error); |
| | | } |
| | | }); |
| | | } catch (Exception ex) { |
| | | sink.next("å¾çæä»¶è¯»å失败ï¼è¯·ç¡®è®¤å¾çæ ¼å¼ä¸º pngãjpgãjpegãwebp æ bmpï¼ä¸å¤§å°ä¸è¶
è¿10MB"); |
| | | sink.complete(); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | private String resolveImageMimeType(MultipartFile file) { |
| | | String contentType = file.getContentType(); |
| | | if (StringUtils.hasText(contentType) && contentType.startsWith("image/")) { |
| | | return contentType; |
| | | } |
| | | String filename = file.getOriginalFilename(); |
| | | String ext = ""; |
| | | if (StringUtils.hasText(filename) && filename.contains(".")) { |
| | | ext = filename.substring(filename.lastIndexOf('.') + 1).toLowerCase(); |
| | | } |
| | | return switch (ext) { |
| | | case "jpg", "jpeg" -> "image/jpeg"; |
| | | case "webp" -> "image/webp"; |
| | | case "bmp" -> "image/bmp"; |
| | | default -> "image/png"; |
| | | }; |
| | | } |
| | | |
| | | private String buildPurchaseFileAnalyzePrompt(String message, String fileContent) { |
| | | return """ |
| | | ä½ æ¯éè´ä¸å¡æä»¶åæå©æãè¯·ä¸¥æ ¼æ ¹æ®ç¨æ·ä¸ä¼ çå¤ä¸ªæä»¶åç¨æ·è¦æ±æåéè´ä¸å¡æ°æ®ã |
| | | |
| | | ç¨æ·è¦æ±: |
| | | %s |
| | | |
| | | è¾åºè¦æ±: |
| | | 1. åªè¾åºåæ³ JSONï¼ä¸è¦ Markdownï¼ä¸è¦é¢å¤è§£éã |
| | | 2. JSON é¡¶å±å段åºå®ä¸º: |
| | | - success: boolean |
| | | - businessType: purchase_ledger | payment_registration | purchase_return_order | unknown |
| | | - action: confirm_required |
| | | - description: ä¸æè¯´æ |
| | | - confidence: 0å°1çå°æ° |
| | | - missingFields: ç¼ºå¤±åæ®µä¸æåç§°æ°ç»ï¼é¢å客æ·å±ç¤ºï¼ä¸è¦è¾åºè±æå段å |
| | | - warnings: é£é©æç¤ºæ°ç» |
| | | - payload: å¾
客æ·ç¡®è®¤çæ°æ®ï¼å段åå¿
须使ç¨å端 DTO åæ®µå |
| | | - preview: ç»å®¢æ·ç¡®è®¤ç¨ç䏿æè¦æ°ç» |
| | | 3. 妿å¯å¤æä¸ºéè´å°è´¦ï¼businessType ä½¿ç¨ purchase_ledgerï¼payload.purchaseLedgers 为éè´è®¢å/éè´å°è´¦æ°ç»: |
| | | - purchaseLedgers: éè´è®¢å/éè´å°è´¦æ°ç»ï¼æ¯æ¡è®°å½å段åå¿
é¡»ä¸ PurchaseLedgerDto ä¿æä¸è´ |
| | | - 产åæç»å¿
é¡»æ¾å¨æ¯æ¡éè´å°è´¦è®°å½ç productData åæ®µä¸ï¼productData ç±»å为 List<SalesLedgerProduct> |
| | | - ä¸è¦ä¼å
ä½¿ç¨ payload é¡¶å± productDataï¼é¡¶å± productData ä»
ä½ä¸ºæ§æ ¼å¼å
¼å®¹ |
| | | - æä»¶éçâéè´åå·âå°±æ¯âéè´ååå·âï¼ç»ä¸æ å°ä¸º purchaseContractNumber |
| | | - æä»¶éçâéå®åå·âå°±æ¯âéå®ååå·âï¼ç»ä¸æ å°ä¸º salesContractNo |
| | | - æææ¥æå段å¿
é¡»ä½¿ç¨ yyyy-MM-ddï¼ä¾å¦ 2026-04-30ï¼ä¸è¦è¾åº 4/30/26ã2026/4/30ã2026å¹´4æ30æ¥ æå¸¦æ¶åç§çæ ¼å¼ |
| | | - éè´å°è´¦ä¸éè¦å¨ payload ä¸ä¼ 审æ¹äººï¼ä¸è¦è¾åº approveUserIdsãapproverId |
| | | - missingFields åªå¡«åä¸å¡å¿
填使 æ³è¯å«çåæ®µï¼ä¸è¦æ PurchaseLedgerDto çææç©ºåæ®µé½å为缺失ï¼ç¼ºå¤±é¡¹å¿
é¡»å䏿ï¼ä¾å¦âä¾åºååç§°ââå«ç¨åä»·âï¼ä¸è¦å supplierIdãtaxInclusiveUnitPrice |
| | | - éè´å°è´¦ä¸»è¡¨å¿
å¡«åæ®µä»
æè¿äºå¤æ: purchaseContractNumberãsupplierName æ supplierId |
| | | - productData æ¯æ¡äº§åå¿
å¡«åæ®µ: productCategoryãspecificationModelãunitãquantityãtaxInclusiveUnitPrice æ taxInclusiveTotalPriceï¼å¦æåªæå«ç¨æ»ä»·åæ°éï¼å¿
é¡»è®¡ç® taxInclusiveUnitPriceï¼å¦æåªæå«ç¨åä»·åæ°éï¼å¿
é¡»è®¡ç® taxInclusiveTotalPrice |
| | | - 产ååæ®µæéè´å¯¼å
¥æ¥å£ PurchaseLedgerProductImportDto 对é½: éè´åå·ã产å大类ãè§æ ¼åå·ãåä½ãæ°éãç¨çãå«ç¨åä»·ãå«ç¨æ»ä»·ãå票类åãæ¯å¦è´¨æ£ |
| | | - éè´äº§å type åºå®ä¸º 2 |
| | | - purchaseLedgers æ¯æ¡è®°å½åªä½¿ç¨è¿äº PurchaseLedgerDto åæ®µå: |
| | | entryDateStart, entryDateEnd, id, purchaseContractNumber, supplierId, supplierName, isWhite, recorderId, recorderName, salesContractNo, salesContractNoId, projectName, entryDate, executionDate, remarks, attachmentMaterials, createdAt, updatedAt, salesLedgerId, hasChildren, Type, productData, tempFileIds, SalesLedgerFiles, phoneNumber, businessPersonId, productId, productModelId, invoiceNumber, invoiceAmount, ticketRegistrationId, contractAmount, receiptPaymentAmount, unReceiptPaymentAmount, type, paymentMethod, approvalStatus, templateName |
| | | - productData æ¯æ¡äº§ååªä½¿ç¨è¿äº SalesLedgerProduct åæ®µå: |
| | | productCategory, specificationModel, unit, quantity, taxRate, taxInclusiveUnitPrice, taxInclusiveTotalPrice, taxExclusiveTotalPrice, invoiceType, productId, productModelId, isChecked, type |
| | | 4. 妿å¯å¤æä¸ºä»æ¬¾ç»è®°ï¼businessType ä½¿ç¨ payment_registrationï¼payload.records ä¸ºä»æ¬¾ç»è®°æ°ç»ï¼å段尽éå
å« purchaseLedgerIdãsalesLedgerProductIdãcurrentPaymentAmountãpaymentMethodãpaymentDateã |
| | | 5. 妿å¯å¤æä¸ºéè´éè´§ï¼businessType ä½¿ç¨ purchase_return_orderï¼payload æ PurchaseReturnOrderDto ç»ç»ï¼æç»æ¾ purchaseReturnOrderProductsDtosã |
| | | 6. 缺å°ä¸å¡å¤çå¿
须忮µæ¶ï¼ä¸è¦ç¼é IDï¼æåæ®µæ¾å
¥ missingFieldsï¼å¹¶ä»è¿åå¯ç¡®è®¤çèç¨¿æ°æ®ã |
| | | 7. ææä¸æå
å®¹ç´æ¥ä¿çï¼ä¸è¦è½¬ä¹æ Unicodeã |
| | | |
| | | æä»¶å
容: |
| | | %s |
| | | """.formatted(message, fileContent); |
| | | } |
| | | |
| | | private AjaxResult processPurchaseLedger(Map<String, Object> payload) throws Exception { |
| | | if (payload.containsKey("purchaseLedgers")) { |
| | | return processPurchaseLedgerBatch(payload); |
| | | } |
| | | |
| | | Map<String, Object> normalizedPayload = normalizePurchaseLedgerMap(payload); |
| | | PurchaseLedgerDto dto = objectMapper.convertValue(normalizedPayload, PurchaseLedgerDto.class); |
| | | AjaxResult ledgerResult = validatePurchaseLedger(dto, 0); |
| | | if (ledgerResult != null) { |
| | | return ledgerResult; |
| | | } |
| | | AjaxResult supplierResult = fillSupplierIdByName(dto); |
| | | if (supplierResult != null) { |
| | | return supplierResult; |
| | | } |
| | | AjaxResult productResult = validatePurchaseProducts(dto.getProductData(), 0); |
| | | if (productResult != null) { |
| | | return productResult; |
| | | } |
| | | int result = purchaseLedgerService.addOrEditPurchase(dto); |
| | | return AjaxResult.success("éè´å°è´¦å·²å¤ç", result); |
| | | } |
| | | |
| | | private AjaxResult processPurchaseLedgerBatch(Map<String, Object> payload) throws Exception { |
| | | List<Map<String, Object>> purchaseLedgers = toMapList(payload.get("purchaseLedgers")); |
| | | if (purchaseLedgers.isEmpty()) { |
| | | return AjaxResult.error("purchaseLedgersä¸è½ä¸ºç©º"); |
| | | } |
| | | |
| | | List<Map<String, Object>> topLevelProductData = toMapList(payload.get("productData")); |
| | | List<Map<String, Object>> results = new ArrayList<>(); |
| | | for (int i = 0; i < purchaseLedgers.size(); i++) { |
| | | Map<String, Object> ledgerMap = normalizePurchaseLedgerMap(purchaseLedgers.get(i)); |
| | | PurchaseLedgerDto dto = objectMapper.convertValue(ledgerMap, PurchaseLedgerDto.class); |
| | | AjaxResult ledgerResult = validatePurchaseLedger(dto, i); |
| | | if (ledgerResult != null) { |
| | | return ledgerResult; |
| | | } |
| | | AjaxResult supplierResult = fillSupplierIdByName(dto); |
| | | if (supplierResult != null) { |
| | | return supplierResult; |
| | | } |
| | | |
| | | List<SalesLedgerProduct> products = dto.getProductData(); |
| | | if (products == null || products.isEmpty()) { |
| | | products = matchProductsForLedger(ledgerMap, dto, topLevelProductData, purchaseLedgers.size() == 1); |
| | | dto.setProductData(products); |
| | | } |
| | | AjaxResult productResult = validatePurchaseProducts(products, i); |
| | | if (productResult != null) { |
| | | return productResult; |
| | | } |
| | | int result = purchaseLedgerService.addOrEditPurchase(dto); |
| | | |
| | | Map<String, Object> item = new LinkedHashMap<>(); |
| | | item.put("index", i); |
| | | item.put("purchaseContractNumber", dto.getPurchaseContractNumber()); |
| | | item.put("supplierId", dto.getSupplierId()); |
| | | item.put("supplierName", dto.getSupplierName()); |
| | | item.put("productCount", products.size()); |
| | | item.put("result", result); |
| | | results.add(item); |
| | | } |
| | | return AjaxResult.success("éè´å°è´¦å·²æ¹éå¤ç", results); |
| | | } |
| | | |
| | | private List<SalesLedgerProduct> matchProductsForLedger(Map<String, Object> ledgerMap, |
| | | PurchaseLedgerDto dto, |
| | | List<Map<String, Object>> productData, |
| | | boolean onlyOneLedger) { |
| | | List<SalesLedgerProduct> products = new ArrayList<>(); |
| | | for (Map<String, Object> productMap : productData) { |
| | | if (onlyOneLedger || productBelongsToLedger(productMap, ledgerMap, dto)) { |
| | | products.add(objectMapper.convertValue(normalizeSalesLedgerProductMap(productMap), SalesLedgerProduct.class)); |
| | | } |
| | | } |
| | | return products; |
| | | } |
| | | |
| | | private boolean productBelongsToLedger(Map<String, Object> productMap, Map<String, Object> ledgerMap, PurchaseLedgerDto dto) { |
| | | Long productPurchaseLedgerId = longValue(productMap, "purchaseLedgerId", "purchaseId", "éè´è®¢åid", "éè´å°è´¦id"); |
| | | if (productPurchaseLedgerId != null && dto.getId() != null && productPurchaseLedgerId.equals(dto.getId())) { |
| | | return true; |
| | | } |
| | | |
| | | Long productSalesLedgerId = longValue(productMap, "salesLedgerId"); |
| | | if (productSalesLedgerId != null && dto.getId() != null && productSalesLedgerId.equals(dto.getId())) { |
| | | return true; |
| | | } |
| | | |
| | | String productContractNo = stringValue(productMap, "purchaseContractNumber", "purchaseContractNo", "éè´ååå·", "éè´åå·", "éè´è®¢åå·"); |
| | | if (StringUtils.hasText(productContractNo) |
| | | && StringUtils.hasText(dto.getPurchaseContractNumber()) |
| | | && productContractNo.trim().equals(dto.getPurchaseContractNumber().trim())) { |
| | | return true; |
| | | } |
| | | |
| | | String ledgerContractNo = stringValue(ledgerMap, "purchaseContractNumber", "purchaseContractNo", "éè´ååå·", "éè´åå·", "éè´è®¢åå·"); |
| | | if (StringUtils.hasText(productContractNo) |
| | | && StringUtils.hasText(ledgerContractNo) |
| | | && productContractNo.trim().equals(ledgerContractNo.trim())) { |
| | | return true; |
| | | } |
| | | |
| | | String productSalesContractNo = stringValue(productMap, "salesContractNo", "salesContractNumber", "éå®ååå·", "éå®åå·", "éå®è®¢åå·"); |
| | | if (StringUtils.hasText(productSalesContractNo) |
| | | && StringUtils.hasText(dto.getSalesContractNo()) |
| | | && productSalesContractNo.trim().equals(dto.getSalesContractNo().trim())) { |
| | | return true; |
| | | } |
| | | |
| | | String ledgerSalesContractNo = stringValue(ledgerMap, "salesContractNo", "salesContractNumber", "éå®ååå·", "éå®åå·", "éå®è®¢åå·"); |
| | | if (StringUtils.hasText(productSalesContractNo) |
| | | && StringUtils.hasText(ledgerSalesContractNo) |
| | | && productSalesContractNo.trim().equals(ledgerSalesContractNo.trim())) { |
| | | return true; |
| | | } |
| | | |
| | | String productSupplierName = stringValue(productMap, "supplierName", "ä¾åºååç§°"); |
| | | return StringUtils.hasText(productSupplierName) |
| | | && StringUtils.hasText(dto.getSupplierName()) |
| | | && productSupplierName.trim().equals(dto.getSupplierName().trim()); |
| | | } |
| | | |
| | | private Map<String, Object> normalizePurchaseLedgerMap(Map<String, Object> source) { |
| | | Map<String, Object> target = new LinkedHashMap<>(); |
| | | copyPurchaseLedgerDtoFields(source, target); |
| | | putDtoFieldIfPresent(source, target, "entryDateStart", "å½å
¥å¼å§æ¥æ", "å½å
¥æ¥æå¼å§"); |
| | | putDtoFieldIfPresent(source, target, "entryDateEnd", "å½å
¥ç»ææ¥æ", "å½å
¥æ¥æç»æ"); |
| | | putDtoFieldIfPresent(source, target, "id", "éè´å°è´¦id", "éè´è®¢åid", "主é®"); |
| | | putDtoFieldIfPresent(source, target, "purchaseContractNumber", "purchaseContractNo", "éè´ååå·", "éè´åå·", "éè´è®¢åå·"); |
| | | putDtoFieldIfPresent(source, target, "supplierId", "ä¾åºåid", "ä¾åºåID", "ä¾åºååç§°id", "ä¾åºååç§°ID"); |
| | | putDtoFieldIfPresent(source, target, "supplierName", "ä¾åºå", "ä¾åºååç§°"); |
| | | putDtoFieldIfPresent(source, target, "isWhite", "æ¯å¦ç½åå"); |
| | | putDtoFieldIfPresent(source, target, "recorderId", "å½å
¥äººid", "å½å
¥äººID", "å½å
¥äººå§åid", "å½å
¥äººå§åID"); |
| | | putDtoFieldIfPresent(source, target, "recorderName", "å½å
¥äºº", "å½å
¥äººå§å"); |
| | | putDtoFieldIfPresent(source, target, "salesContractNo", "salesContractNumber", "éå®ååå·", "éå®åå·", "éå®è®¢åå·"); |
| | | putDtoFieldIfPresent(source, target, "salesContractNoId", "éå®ååå·id", "éå®ååå·ID", "éå®åå·id", "éå®åå·ID"); |
| | | putDtoFieldIfPresent(source, target, "projectName", "项ç®", "项ç®åç§°"); |
| | | putDtoFieldIfPresent(source, target, "entryDate", "å½å
¥æ¥æ"); |
| | | putDtoFieldIfPresent(source, target, "executionDate", "ç¾è®¢æ¥æ", "ååç¾è®¢æ¥æ"); |
| | | putDtoFieldIfPresent(source, target, "remarks", "夿³¨", "说æ"); |
| | | putDtoFieldIfPresent(source, target, "attachmentMaterials", "éä»¶ææ", "éä»¶ææè·¯å¾æåç§°"); |
| | | putDtoFieldIfPresent(source, target, "createdAt", "å建æ¶é´", "è®°å½å建æ¶é´"); |
| | | putDtoFieldIfPresent(source, target, "updatedAt", "æ´æ°æ¶é´", "è®°å½æåæ´æ°æ¶é´"); |
| | | putDtoFieldIfPresent(source, target, "salesLedgerId", "éå®å°è´¦id", "éå®å°è´¦ID", "å
³èéå®å°è´¦ä¸»è¡¨ä¸»é®"); |
| | | putDtoFieldIfPresent(source, target, "hasChildren", "æ¯å¦æå级", "æ¯å¦ææç»"); |
| | | putDtoFieldIfPresent(source, target, "Type", "å°è´¦ç±»å", "ä¸å¡ç±»å"); |
| | | putDtoFieldIfPresent(source, target, "productData", "products", "产åæç»", "éè´äº§åæç»"); |
| | | putDtoFieldIfPresent(source, target, "tempFileIds", "ä¸´æ¶æä»¶id", "ä¸´æ¶æä»¶ID", "ä¸´æ¶æä»¶ids"); |
| | | putDtoFieldIfPresent(source, target, "SalesLedgerFiles", "éä»¶å表", "éå®å°è´¦éä»¶"); |
| | | putDtoFieldIfPresent(source, target, "phoneNumber", "ä¸å¡åææºå·", "ææºå·"); |
| | | putDtoFieldIfPresent(source, target, "businessPersonId", "ä¸å¡åid", "ä¸å¡åID"); |
| | | putDtoFieldIfPresent(source, target, "productId", "产åid", "产åID"); |
| | | putDtoFieldIfPresent(source, target, "productModelId", "产åè§æ ¼id", "产åè§æ ¼ID"); |
| | | putDtoFieldIfPresent(source, target, "invoiceNumber", "å票å·", "å票å·ç "); |
| | | putDtoFieldIfPresent(source, target, "invoiceAmount", "å票éé¢", "å票éé¢ï¼å
ï¼"); |
| | | putDtoFieldIfPresent(source, target, "ticketRegistrationId", "æ¥ç¥¨ç»è®°id", "æ¥ç¥¨ç»è®°ID"); |
| | | putDtoFieldIfPresent(source, target, "contractAmount", "ååéé¢", "ååéé¢ï¼äº§åå«ç¨æ»ä»·ï¼"); |
| | | putDtoFieldIfPresent(source, target, "receiptPaymentAmount", "æ¥ç¥¨éé¢", "å·²æ¥ç¥¨éé¢", "å·²æ¥ç¥¨éé¢(å
)"); |
| | | putDtoFieldIfPresent(source, target, "unReceiptPaymentAmount", "æªæ¥ç¥¨éé¢", "æªæ¥ç¥¨éé¢(å
)"); |
| | | putDtoFieldIfPresent(source, target, "type", "æä»¶ç±»å"); |
| | | putDtoFieldIfPresent(source, target, "paymentMethod", "仿¬¾æ¹å¼"); |
| | | putDtoFieldIfPresent(source, target, "approvalStatus", "审æ¹ç¶æ"); |
| | | putDtoFieldIfPresent(source, target, "templateName", "模æ¿åç§°"); |
| | | target.remove("approveUserIds"); |
| | | target.remove("approverId"); |
| | | normalizeNestedProductData(target); |
| | | attachImportStyleProductData(source, target); |
| | | if (target.get("type") == null) { |
| | | target.put("type", 2); |
| | | } |
| | | target.putIfAbsent("entryDate", LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE)); |
| | | normalizePurchaseLedgerDateFields(target); |
| | | return target; |
| | | } |
| | | |
| | | private void attachImportStyleProductData(Map<String, Object> source, Map<String, Object> target) { |
| | | if (target.get("productData") != null) { |
| | | return; |
| | | } |
| | | Map<String, Object> productMap = normalizeSalesLedgerProductMap(source); |
| | | if (hasImportStyleProductData(productMap)) { |
| | | target.put("productData", List.of(productMap)); |
| | | } |
| | | } |
| | | |
| | | private boolean hasImportStyleProductData(Map<String, Object> productMap) { |
| | | return hasMapText(productMap, "productCategory") |
| | | || hasMapText(productMap, "specificationModel") |
| | | || productMap.get("quantity") != null |
| | | || productMap.get("taxInclusiveUnitPrice") != null |
| | | || productMap.get("taxInclusiveTotalPrice") != null; |
| | | } |
| | | |
| | | private boolean hasMapText(Map<String, Object> map, String key) { |
| | | Object value = map.get(key); |
| | | return value != null && StringUtils.hasText(String.valueOf(value)); |
| | | } |
| | | |
| | | private void normalizeNestedProductData(Map<String, Object> target) { |
| | | Object productDataValue = target.get("productData"); |
| | | if (productDataValue == null) { |
| | | return; |
| | | } |
| | | List<Map<String, Object>> productMaps = toMapList(productDataValue); |
| | | List<Map<String, Object>> normalizedProducts = new ArrayList<>(); |
| | | for (Map<String, Object> productMap : productMaps) { |
| | | normalizedProducts.add(normalizeSalesLedgerProductMap(productMap)); |
| | | } |
| | | target.put("productData", normalizedProducts); |
| | | } |
| | | |
| | | private Map<String, Object> normalizeSalesLedgerProductMap(Map<String, Object> source) { |
| | | Map<String, Object> target = new LinkedHashMap<>(); |
| | | copySalesLedgerProductFields(source, target); |
| | | putDtoFieldIfPresent(source, target, "productCategory", "产å大类", "产ååç§°", "产å", "åå", "ç©æåç§°"); |
| | | putDtoFieldIfPresent(source, target, "specificationModel", "è§æ ¼åå·", "åå·", "è§æ ¼", "产åè§æ ¼"); |
| | | putDtoFieldIfPresent(source, target, "unit", "åä½"); |
| | | putDtoFieldIfPresent(source, target, "quantity", "æ°é", "éè´æ°é"); |
| | | putDtoFieldIfPresent(source, target, "taxRate", "ç¨ç"); |
| | | putDtoFieldIfPresent(source, target, "taxInclusiveUnitPrice", "å«ç¨åä»·", "åä»·", "éè´åä»·", "å«ç¨ä»·æ ¼"); |
| | | putDtoFieldIfPresent(source, target, "taxInclusiveTotalPrice", "å«ç¨æ»ä»·", "æ»ä»·", "éè´éé¢", "éé¢", "ååéé¢"); |
| | | putDtoFieldIfPresent(source, target, "taxExclusiveTotalPrice", "ä¸å«ç¨æ»ä»·"); |
| | | putDtoFieldIfPresent(source, target, "invoiceType", "å票类å", "å票类å«"); |
| | | putDtoFieldIfPresent(source, target, "productId", "产åid", "产åID"); |
| | | putDtoFieldIfPresent(source, target, "productModelId", "产åè§æ ¼id", "产åè§æ ¼ID", "åå·id", "åå·ID"); |
| | | putDtoFieldIfPresent(source, target, "isChecked", "æ¯å¦è´¨æ£", "æ¯å¦è´¨æ£éª", "è´¨æ£"); |
| | | putDtoFieldIfPresent(source, target, "type", "å°è´¦ç±»å"); |
| | | normalizeProductAmounts(target); |
| | | target.putIfAbsent("type", 2); |
| | | return target; |
| | | } |
| | | |
| | | private void copySalesLedgerProductFields(Map<String, Object> source, Map<String, Object> target) { |
| | | String[] productFields = { |
| | | "id", "salesLedgerId", "warnNum", "productCategory", "specificationModel", "unit", |
| | | "speculativeTradingName", "quantity", "minStock", "taxRate", "taxInclusiveUnitPrice", |
| | | "taxInclusiveTotalPrice", "taxExclusiveTotalPrice", "invoiceType", "type", "ticketsNum", |
| | | "ticketsAmount", "futureTickets", "futureTicketsAmount", "invoiceNum", "noInvoiceNum", |
| | | "invoiceAmount", "noInvoiceAmount", "productId", "productModelId", "register", "registerDate", |
| | | "approveStatus", "pendingInvoiceTotal", "invoiceTotal", "pendingTicketsTotal", "ticketsTotal", |
| | | "isChecked", "isProduction" |
| | | }; |
| | | for (String field : productFields) { |
| | | if (source.containsKey(field)) { |
| | | target.put(field, source.get(field)); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private void normalizeProductAmounts(Map<String, Object> target) { |
| | | BigDecimal quantity = decimalValue(target.get("quantity")); |
| | | BigDecimal unitPrice = decimalValue(target.get("taxInclusiveUnitPrice")); |
| | | BigDecimal totalPrice = decimalValue(target.get("taxInclusiveTotalPrice")); |
| | | if (unitPrice == null && totalPrice != null && quantity != null && quantity.compareTo(BigDecimal.ZERO) != 0) { |
| | | target.put("taxInclusiveUnitPrice", totalPrice.divide(quantity, 6, RoundingMode.HALF_UP)); |
| | | } |
| | | if (totalPrice == null && unitPrice != null && quantity != null) { |
| | | target.put("taxInclusiveTotalPrice", unitPrice.multiply(quantity)); |
| | | } |
| | | BigDecimal taxRate = decimalValue(target.get("taxRate")); |
| | | totalPrice = decimalValue(target.get("taxInclusiveTotalPrice")); |
| | | if (target.get("taxExclusiveTotalPrice") == null && totalPrice != null && taxRate != null) { |
| | | BigDecimal divisor = BigDecimal.ONE.add(taxRate.divide(new BigDecimal("100"), 6, RoundingMode.HALF_UP)); |
| | | target.put("taxExclusiveTotalPrice", totalPrice.divide(divisor, 2, RoundingMode.HALF_UP)); |
| | | } |
| | | } |
| | | |
| | | private AjaxResult validatePurchaseProducts(List<SalesLedgerProduct> products, int ledgerIndex) { |
| | | if (products == null || products.isEmpty()) { |
| | | return null; |
| | | } |
| | | for (int i = 0; i < products.size(); i++) { |
| | | SalesLedgerProduct product = products.get(i); |
| | | String prefix = "第" + (ledgerIndex + 1) + "个éè´å°è´¦ç第" + (i + 1) + "æ¡äº§å"; |
| | | if (!StringUtils.hasText(product.getProductCategory())) { |
| | | return AjaxResult.error(prefix + "缺å°äº§ååç§°ï¼è¯·è¡¥å
åå确认"); |
| | | } |
| | | if (!StringUtils.hasText(product.getSpecificationModel())) { |
| | | return AjaxResult.error(prefix + "缺å°è§æ ¼åå·ï¼è¯·è¡¥å
åå确认"); |
| | | } |
| | | if (!StringUtils.hasText(product.getUnit())) { |
| | | return AjaxResult.error(prefix + "缺å°åä½ï¼è¯·è¡¥å
åå确认"); |
| | | } |
| | | if (product.getQuantity() == null) { |
| | | return AjaxResult.error(prefix + "ç¼ºå°æ°é"); |
| | | } |
| | | if (product.getTaxInclusiveUnitPrice() == null) { |
| | | return AjaxResult.error(prefix + "缺å°å«ç¨åä»·ï¼è¯·è¡¥å
åå确认"); |
| | | } |
| | | if (product.getTaxInclusiveTotalPrice() == null) { |
| | | return AjaxResult.error(prefix + "缺å°å«ç¨æ»ä»·ï¼è¯·è¡¥å
åå确认"); |
| | | } |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | private AjaxResult validatePurchaseLedger(PurchaseLedgerDto dto, int ledgerIndex) { |
| | | String prefix = "第" + (ledgerIndex + 1) + "个éè´å°è´¦"; |
| | | if (!StringUtils.hasText(dto.getPurchaseContractNumber())) { |
| | | return AjaxResult.error(prefix + "缺å°éè´ååå·ï¼è¯·è¡¥å
åå确认"); |
| | | } |
| | | if (dto.getSupplierId() == null && !StringUtils.hasText(dto.getSupplierName())) { |
| | | return AjaxResult.error(prefix + "缺å°ä¾åºååç§°ï¼è¯·è¡¥å
åå确认"); |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | private void normalizePurchaseLedgerDateFields(Map<String, Object> target) { |
| | | normalizeDateField(target, "entryDate"); |
| | | normalizeDateField(target, "executionDate"); |
| | | normalizeDateField(target, "createdAt"); |
| | | normalizeDateField(target, "updatedAt"); |
| | | } |
| | | |
| | | private void normalizeDateField(Map<String, Object> target, String fieldName) { |
| | | Object value = target.get(fieldName); |
| | | if (value == null) { |
| | | return; |
| | | } |
| | | String normalizedDate = normalizeDateValue(value); |
| | | if (StringUtils.hasText(normalizedDate)) { |
| | | target.put(fieldName, normalizedDate); |
| | | } |
| | | } |
| | | |
| | | private String normalizeDateValue(Object value) { |
| | | if (value instanceof Date date) { |
| | | return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate().format(DateTimeFormatter.ISO_LOCAL_DATE); |
| | | } |
| | | if (value instanceof Number number) { |
| | | return LocalDate.of(1899, 12, 30) |
| | | .plusDays(number.longValue()) |
| | | .format(DateTimeFormatter.ISO_LOCAL_DATE); |
| | | } |
| | | |
| | | String text = String.valueOf(value).trim(); |
| | | if (!StringUtils.hasText(text)) { |
| | | return null; |
| | | } |
| | | if (text.length() >= 10 && text.charAt(4) == '-' && text.charAt(7) == '-') { |
| | | return text.substring(0, 10); |
| | | } |
| | | |
| | | String normalizedText = text.replace("å¹´", "-") |
| | | .replace("æ", "-") |
| | | .replace("æ¥", "") |
| | | .replace(".", "-") |
| | | .replace("/", "-") |
| | | .trim(); |
| | | DateTimeFormatter[] formatters = { |
| | | DateTimeFormatter.ofPattern("yyyy-M-d"), |
| | | DateTimeFormatter.ofPattern("M-d-yyyy"), |
| | | DateTimeFormatter.ofPattern("M-d-yy") |
| | | }; |
| | | for (DateTimeFormatter formatter : formatters) { |
| | | try { |
| | | return LocalDate.parse(normalizedText, formatter).format(DateTimeFormatter.ISO_LOCAL_DATE); |
| | | } catch (DateTimeParseException ignored) { |
| | | // Try the next supported input pattern. |
| | | } |
| | | } |
| | | return text; |
| | | } |
| | | |
| | | private void copyPurchaseLedgerDtoFields(Map<String, Object> source, Map<String, Object> target) { |
| | | String[] dtoFields = { |
| | | "entryDateStart", "entryDateEnd", "id", "purchaseContractNumber", |
| | | "supplierId", "supplierName", "isWhite", "recorderId", "recorderName", "salesContractNo", |
| | | "salesContractNoId", "projectName", "entryDate", "executionDate", "remarks", "attachmentMaterials", |
| | | "createdAt", "updatedAt", "salesLedgerId", "hasChildren", "Type", "productData", "tempFileIds", |
| | | "SalesLedgerFiles", "phoneNumber", "businessPersonId", "productId", "productModelId", "invoiceNumber", |
| | | "invoiceAmount", "ticketRegistrationId", "contractAmount", "receiptPaymentAmount", |
| | | "unReceiptPaymentAmount", "type", "paymentMethod", "approvalStatus", "templateName" |
| | | }; |
| | | for (String field : dtoFields) { |
| | | if (source.containsKey(field)) { |
| | | target.put(field, source.get(field)); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private void putDtoFieldIfPresent(Map<String, Object> source, Map<String, Object> target, String dtoField, String... aliases) { |
| | | if (target.containsKey(dtoField) && target.get(dtoField) != null) { |
| | | return; |
| | | } |
| | | for (String alias : aliases) { |
| | | Object value = source.get(alias); |
| | | if (value != null && StringUtils.hasText(String.valueOf(value))) { |
| | | target.put(dtoField, value); |
| | | return; |
| | | } |
| | | } |
| | | } |
| | | |
| | | private List<Map<String, Object>> toMapList(Object value) { |
| | | if (value == null) { |
| | | return List.of(); |
| | | } |
| | | return objectMapper.convertValue(value, new TypeReference<List<Map<String, Object>>>() { |
| | | }); |
| | | } |
| | | |
| | | private String stringValue(Map<String, Object> map, String... keys) { |
| | | for (String key : keys) { |
| | | Object value = map.get(key); |
| | | if (value != null && StringUtils.hasText(String.valueOf(value))) { |
| | | return String.valueOf(value); |
| | | } |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | private Long longValue(Map<String, Object> map, String... keys) { |
| | | String value = stringValue(map, keys); |
| | | if (!StringUtils.hasText(value)) { |
| | | return null; |
| | | } |
| | | try { |
| | | return Long.parseLong(value.trim()); |
| | | } catch (NumberFormatException ignored) { |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | private BigDecimal decimalValue(Object value) { |
| | | if (value == null) { |
| | | return null; |
| | | } |
| | | if (value instanceof BigDecimal decimal) { |
| | | return decimal; |
| | | } |
| | | if (value instanceof Number number) { |
| | | return new BigDecimal(String.valueOf(number)); |
| | | } |
| | | String text = String.valueOf(value) |
| | | .replace(",", "") |
| | | .replace("ï¼", "") |
| | | .replace("å
", "") |
| | | .replace("ï¿¥", "") |
| | | .trim(); |
| | | if (!StringUtils.hasText(text)) { |
| | | return null; |
| | | } |
| | | try { |
| | | return new BigDecimal(text); |
| | | } catch (NumberFormatException ignored) { |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | private String toCustomerMessage(Exception ex) { |
| | | String message = ex.getMessage(); |
| | | if (!StringUtils.hasText(message)) { |
| | | return "å¤ç失败ï¼è¯·æ£æ¥ç¡®è®¤æ°æ®åéè¯"; |
| | | } |
| | | if (message.contains("tax_inclusive_unit_price")) { |
| | | return "å¤ç失败ï¼äº§åæç»ç¼ºå°å«ç¨åä»·ï¼è¯·è¡¥å
åå确认"; |
| | | } |
| | | if (message.contains("tax_inclusive_total_price")) { |
| | | return "å¤ç失败ï¼äº§åæç»ç¼ºå°å«ç¨æ»ä»·ï¼è¯·è¡¥å
åå确认"; |
| | | } |
| | | if (message.contains("entryDate")) { |
| | | return "å¤ç失败ï¼å½å
¥æ¥ææ ¼å¼ä¸æ£ç¡®ï¼è¯·ä½¿ç¨ yyyy-MM-ddï¼ä¾å¦ 2026-04-30"; |
| | | } |
| | | if (message.contains("supplier")) { |
| | | return "å¤ç失败ï¼ä¾åºåä¿¡æ¯ä¸å®æ´ï¼è¯·ç¡®è®¤ä¾åºååç§°æä¾åºåID"; |
| | | } |
| | | if (message.contains("SQL") || message.contains("java.") || message.contains("Exception")) { |
| | | return "å¤ç失败ï¼ç¡®è®¤æ°æ®ä¸å®æ´ææ ¼å¼ä¸æ£ç¡®ï¼è¯·æ£æ¥å¿
å¡«åæ®µåéè¯"; |
| | | } |
| | | return "å¤ç失败ï¼" + message; |
| | | } |
| | | |
| | | private AjaxResult fillSupplierIdByName(PurchaseLedgerDto dto) { |
| | | if (dto.getSupplierId() != null) { |
| | | return null; |
| | | } |
| | | if (!StringUtils.hasText(dto.getSupplierName())) { |
| | | return AjaxResult.error("ä¾åºåIDä¸è½ä¸ºç©ºï¼æªè¯å«å°ä¾åºååç§°ï¼æ æ³èªå¨å¹é
ä¾åºåID"); |
| | | } |
| | | |
| | | SupplierManage supplier = supplierManageMapper.selectOne(new LambdaQueryWrapper<SupplierManage>() |
| | | .eq(SupplierManage::getSupplierName, dto.getSupplierName().trim()) |
| | | .last("limit 1")); |
| | | if (supplier == null) { |
| | | return AjaxResult.error("æªæ¾å°ä¾åºåï¼" + dto.getSupplierName() + "ï¼è¯·å
ç»´æ¤ä¾åºåææå¨éæ©ä¾åºåID"); |
| | | } |
| | | dto.setSupplierId(supplier.getId()); |
| | | return null; |
| | | } |
| | | |
| | | private AjaxResult processPaymentRegistration(Map<String, Object> payload) { |
| | | Object recordsValue = payload.get("records"); |
| | | List<PaymentRegistration> records; |
| | | if (recordsValue == null) { |
| | | records = Collections.singletonList(objectMapper.convertValue(payload, PaymentRegistration.class)); |
| | | } else { |
| | | records = objectMapper.convertValue(recordsValue, new TypeReference<List<PaymentRegistration>>() { |
| | | }); |
| | | } |
| | | int result = paymentRegistrationService.insertPaymentRegistration(records); |
| | | return AjaxResult.success("仿¬¾ç»è®°å·²å¤ç", result); |
| | | } |
| | | |
| | | private AjaxResult processPurchaseReturnOrder(Map<String, Object> payload) { |
| | | PurchaseReturnOrderDto dto = objectMapper.convertValue(payload, PurchaseReturnOrderDto.class); |
| | | Boolean result = purchaseReturnOrdersService.add(dto); |
| | | return AjaxResult.success("éè´éè´§åå·²å¤ç", result); |
| | | } |
| | | } |
| | |
| | | if ("xls".equals(ext)) { |
| | | return extractXls(bytes); |
| | | } |
| | | if (isImage(ext)) { |
| | | return "å¾çæä»¶ï¼" + filename + "ï¼å·²ä¸ä¼ ï¼è¯·ç»åå¾çå
容è¯å«éè´åæ®ãè¡¨æ ¼å产åæç»ã"; |
| | | } |
| | | throw new IllegalArgumentException("æä¸æ¯æè¯¥æä»¶ç±»å: " + ext); |
| | | } |
| | | |
| | | public boolean isImageFile(MultipartFile file) { |
| | | if (file == null) { |
| | | return false; |
| | | } |
| | | return isImage(getExtension(file.getOriginalFilename())); |
| | | } |
| | | |
| | | private String extractDocx(byte[] bytes) throws IOException { |
| | |
| | | "txt", "md", "markdown", "json", "xml", "yaml", "yml", "csv", "log", "properties", |
| | | "java", "js", "ts", "vue", "html", "css", "sql", "py", "go", "sh", "bat"); |
| | | } |
| | | |
| | | private boolean isImage(String ext) { |
| | | return StringUtils.inStringIgnoreCase(ext, "png", "jpg", "jpeg", "webp", "bmp"); |
| | | } |
| | | } |
| | |
| | | this.aiSessionUserContext = aiSessionUserContext; |
| | | } |
| | | |
| | | @Tool(name = "æ¥è¯¢å®¡æ¹å¾
åå表", value = "æ¥è¯¢å½åç»å½äººç¸å
³ç审æ¹å¾
åï¼ä¼å
è¿åèªå·±å¾
å¤çç审æ¹ï¼æ¯ææç¶æãç±»åãå
³é®åè¿æ»¤ã") |
| | | @Tool(name = "æ¥è¯¢å®¡æ¹å¾
åå表", value = "æ¥è¯¢å½åç»å½äººç¸å
³ç审æ¹å¾
åï¼ä¼å
è¿åèªå·±å¾
å¤çç审æ¹ï¼æ¯ææç¶æãç±»åãå
³é®ååèå´è¿æ»¤ã") |
| | | public String listTodos(@ToolMemoryId String memoryId, |
| | | @P(value = "审æ¹ç¶æï¼å¯éå¼ï¼allãpendingãprocessingãapprovedãrejectedãresubmitted", required = false) String status, |
| | | @P(value = "审æ¹ç±»åç¼å·ï¼å¯ä¸ä¼ ", required = false) Integer approveType, |
| | | @P(value = "å
³é®åï¼å¯å¹é
æµç¨ç¼å·ãæ é¢ãç³è¯·äººãå½å审æ¹äºº", required = false) String keyword, |
| | | @P(value = "è¿åæ¡æ°ï¼é»è®¤10ï¼æå¤§20", required = false) Integer limit) { |
| | | @P(value = "è¿åæ¡æ°ï¼é»è®¤10ï¼æå¤§20", required = false) Integer limit, |
| | | @P(value = "æ¥è¯¢èå´ï¼å¯éå¼ï¼relatedãapplicantãapproverï¼related 表示å½åç¨æ·ç¸å
³ï¼applicant 表示æåèµ·çï¼approver 表示å¾
æå¤çç", required = false) String scope) { |
| | | |
| | | LoginUser loginUser = currentLoginUser(memoryId); |
| | | Long userId = loginUser.getUserId(); |
| | | Integer statusCode = parseStatus(status); |
| | | String normalizedScope = normalizeScope(scope); |
| | | |
| | | LambdaQueryWrapper<ApproveProcess> wrapper = new LambdaQueryWrapper<>(); |
| | | wrapper.eq(ApproveProcess::getApproveDelete, 0) |
| | | .ne(ApproveProcess::getApproveStatus, 2); |
| | | wrapper.eq(ApproveProcess::getApproveDelete, 0); |
| | | if (statusCode == null) { |
| | | wrapper.ne(ApproveProcess::getApproveStatus, 2); |
| | | } |
| | | |
| | | if (approveType != null) { |
| | | wrapper.eq(ApproveProcess::getApproveType, approveType); |
| | | } |
| | | // if (StringUtils.hasText(keyword)) { |
| | | // wrapper.and(w -> w.like(ApproveProcess::getApproveId, keyword) |
| | | // .or().like(ApproveProcess::getApproveReason, keyword) |
| | | // .or().like(ApproveProcess::getApproveUserName, keyword) |
| | | // .or().like(ApproveProcess::getApproveUserCurrentName, keyword)); |
| | | // } |
| | | if (statusCode != null && (statusCode == 0 || statusCode == 1)) { |
| | | if (StringUtils.hasText(keyword)) { |
| | | wrapper.and(w -> w.like(ApproveProcess::getApproveId, keyword) |
| | | .or().like(ApproveProcess::getApproveReason, keyword) |
| | | .or().like(ApproveProcess::getApproveUserName, keyword) |
| | | .or().like(ApproveProcess::getApproveUserCurrentName, keyword)); |
| | | } |
| | | if ("applicant".equals(normalizedScope)) { |
| | | wrapper.eq(ApproveProcess::getApproveUser, userId); |
| | | if (statusCode != null) { |
| | | wrapper.eq(ApproveProcess::getApproveStatus, statusCode); |
| | | } |
| | | } else if ("approver".equals(normalizedScope)) { |
| | | wrapper.eq(ApproveProcess::getApproveUserCurrentId, userId); |
| | | if (statusCode != null) { |
| | | wrapper.eq(ApproveProcess::getApproveStatus, statusCode); |
| | | } |
| | | } else if (statusCode != null && (statusCode == 0 || statusCode == 1)) { |
| | | wrapper.eq(ApproveProcess::getApproveUserCurrentId, userId); |
| | | wrapper.eq(ApproveProcess::getApproveStatus, statusCode); |
| | | } else { |
| | | wrapper.and(w -> w.eq(ApproveProcess::getApproveUser, userId) |
| | | .or().eq(ApproveProcess::getApproveUserCurrentId, userId) |
| | | .or().apply("FIND_IN_SET({0}, approve_user_ids)", userId)); |
| | | if (statusCode != null) { |
| | | wrapper.eq(ApproveProcess::getApproveStatus, statusCode); |
| | | } |
| | | } |
| | | |
| | | wrapper.orderByDesc(ApproveProcess::getCreateTime) |
| | |
| | | "count", items.size(), |
| | | "statusFilter", StringUtils.hasText(status) ? status : "all", |
| | | "approveType", approveType == null ? "" : approveType, |
| | | "keyword", keyword == null ? "" : keyword |
| | | "keyword", keyword == null ? "" : keyword, |
| | | "scope", normalizedScope |
| | | ), |
| | | Map.of("columns", todoColumns(), "items", items), |
| | | Map.of()); |
| | |
| | | }; |
| | | } |
| | | |
| | | private String normalizeScope(String scope) { |
| | | if (!StringUtils.hasText(scope)) { |
| | | return "related"; |
| | | } |
| | | return switch (scope.trim().toLowerCase()) { |
| | | case "applicant", "mine", "created", "initiated" -> "applicant"; |
| | | case "approver", "handler", "todo", "pending" -> "approver"; |
| | | default -> "related"; |
| | | }; |
| | | } |
| | | |
| | | private String approveStatusName(Integer status) { |
| | | if (status == null) { |
| | | return "æªç¥"; |
| | |
| | | import com.ruoyi.purchase.pojo.PaymentRegistration; |
| | | import com.ruoyi.purchase.pojo.PurchaseLedger; |
| | | import com.ruoyi.purchase.pojo.PurchaseReturnOrders; |
| | | import com.ruoyi.procurementrecord.mapper.InboundManagementMapper; |
| | | import com.ruoyi.procurementrecord.mapper.ProcurementRecordMapper; |
| | | import com.ruoyi.procurementrecord.pojo.InboundManagement; |
| | | import com.ruoyi.procurementrecord.pojo.ProcurementRecordStorage; |
| | | import com.ruoyi.sales.mapper.SalesLedgerProductMapper; |
| | | import com.ruoyi.sales.pojo.SalesLedgerProduct; |
| | | import dev.langchain4j.agent.tool.P; |
| | | import dev.langchain4j.agent.tool.Tool; |
| | | import dev.langchain4j.agent.tool.ToolMemoryId; |
| | |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Objects; |
| | | import java.util.Comparator; |
| | | import java.util.stream.Collectors; |
| | | |
| | | @Component |
| | |
| | | private final PaymentRegistrationMapper paymentRegistrationMapper; |
| | | private final InvoicePurchaseMapper invoicePurchaseMapper; |
| | | private final PurchaseReturnOrdersMapper purchaseReturnOrdersMapper; |
| | | private final SalesLedgerProductMapper salesLedgerProductMapper; |
| | | private final ProcurementRecordMapper procurementRecordMapper; |
| | | private final InboundManagementMapper inboundManagementMapper; |
| | | private final AiSessionUserContext aiSessionUserContext; |
| | | |
| | | public PurchaseAgentTools(PurchaseLedgerMapper purchaseLedgerMapper, |
| | | PaymentRegistrationMapper paymentRegistrationMapper, |
| | | InvoicePurchaseMapper invoicePurchaseMapper, |
| | | PurchaseReturnOrdersMapper purchaseReturnOrdersMapper, |
| | | SalesLedgerProductMapper salesLedgerProductMapper, |
| | | ProcurementRecordMapper procurementRecordMapper, |
| | | InboundManagementMapper inboundManagementMapper, |
| | | AiSessionUserContext aiSessionUserContext) { |
| | | this.purchaseLedgerMapper = purchaseLedgerMapper; |
| | | this.paymentRegistrationMapper = paymentRegistrationMapper; |
| | | this.invoicePurchaseMapper = invoicePurchaseMapper; |
| | | this.purchaseReturnOrdersMapper = purchaseReturnOrdersMapper; |
| | | this.salesLedgerProductMapper = salesLedgerProductMapper; |
| | | this.procurementRecordMapper = procurementRecordMapper; |
| | | this.inboundManagementMapper = inboundManagementMapper; |
| | | this.aiSessionUserContext = aiSessionUserContext; |
| | | } |
| | | |
| | |
| | | wrapper.ge(PurchaseLedger::getEntryDate, toDate(start)); |
| | | } |
| | | if (end != null) { |
| | | wrapper.le(PurchaseLedger::getEntryDate, toDate(end)); |
| | | wrapper.lt(PurchaseLedger::getEntryDate, toExclusiveEndDate(end)); |
| | | } |
| | | wrapper.orderByDesc(PurchaseLedger::getEntryDate, PurchaseLedger::getId).last("limit " + finalLimit); |
| | | |
| | |
| | | return jsonResponse(true, "purchase_stats", "å·²è¿åéè´ç»è®¡æ°æ®", summary, Map.of(), Map.of()); |
| | | } |
| | | |
| | | @Tool(name = "éè´ç©æé颿è¡", value = "ææ¶é´èå´ç»è®¡éè´ç©æé颿è¡ï¼å¯åçæ¬æéè´é颿åé åçç©æã") |
| | | public String rankPurchaseMaterials(@ToolMemoryId String memoryId, |
| | | @P(value = "å¼å§æ¥æ yyyy-MM-dd", required = false) String startDate, |
| | | @P(value = "ç»ææ¥æ yyyy-MM-dd", required = false) String endDate, |
| | | @P(value = "æ¶é´èå´æè¿°ï¼ä¾å¦æ¬æãè¿7天ãè¿30天", required = false) String timeRange, |
| | | @P(value = "è¿åæ¡æ°ï¼é»è®¤10ï¼æå¤§30", required = false) Integer limit) { |
| | | LoginUser loginUser = currentLoginUser(memoryId); |
| | | DateRange range = resolveDateRange(startDate, endDate, timeRange); |
| | | List<Long> ledgerIds = queryLedgers(loginUser, range).stream() |
| | | .map(PurchaseLedger::getId) |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.toList()); |
| | | if (ledgerIds.isEmpty()) { |
| | | return jsonResponse(true, "purchase_material_rank", "å½åæ¶é´èå´å
没æéè´ç©ææ°æ®ã", |
| | | rangeSummary(range, 0), Map.of("items", List.of()), Map.of()); |
| | | } |
| | | |
| | | List<SalesLedgerProduct> products = defaultList(salesLedgerProductMapper.selectList(new LambdaQueryWrapper<SalesLedgerProduct>() |
| | | .eq(SalesLedgerProduct::getType, 2) |
| | | .in(SalesLedgerProduct::getSalesLedgerId, ledgerIds))); |
| | | |
| | | Map<String, MaterialRankItem> grouped = new LinkedHashMap<>(); |
| | | for (SalesLedgerProduct product : products) { |
| | | String name = safe(product.getProductCategory()); |
| | | String model = safe(product.getSpecificationModel()); |
| | | String key = name + "|" + model; |
| | | MaterialRankItem item = grouped.computeIfAbsent(key, ignored -> new MaterialRankItem(name, model, safe(product.getUnit()))); |
| | | item.quantity = item.quantity.add(defaultDecimal(product.getQuantity())); |
| | | item.amount = item.amount.add(defaultDecimal(product.getTaxInclusiveTotalPrice())); |
| | | } |
| | | |
| | | List<Map<String, Object>> items = grouped.values().stream() |
| | | .sorted(Comparator.comparing((MaterialRankItem item) -> item.amount).reversed()) |
| | | .limit(normalizeLimit(limit)) |
| | | .map(MaterialRankItem::toMap) |
| | | .collect(Collectors.toList()); |
| | | |
| | | return jsonResponse(true, "purchase_material_rank", "å·²è¿åéè´ç©æé颿è¡ã", |
| | | rangeSummary(range, items.size()), Map.of("items", items), Map.of()); |
| | | } |
| | | |
| | | @Tool(name = "æ¥è¯¢æªå
¥åºéè´è®¢å", value = "æ¥è¯¢éè´è®¢åä¸ä»æå¾
å
¥åºæ°éçç©ææç»ã") |
| | | public String listUnstockedPurchaseOrders(@ToolMemoryId String memoryId, |
| | | @P(value = "å¼å§æ¥æ yyyy-MM-dd", required = false) String startDate, |
| | | @P(value = "ç»ææ¥æ yyyy-MM-dd", required = false) String endDate, |
| | | @P(value = "å
³é®åï¼å¯å¹é
éè´ååå·/ä¾åºå/ç©æ", required = false) String keyword, |
| | | @P(value = "è¿åæ¡æ°ï¼é»è®¤10ï¼æå¤§30", required = false) Integer limit) { |
| | | LoginUser loginUser = currentLoginUser(memoryId); |
| | | DateRange range = resolveDateRange(startDate, endDate, null); |
| | | List<PurchaseLedger> ledgers = queryLedgers(loginUser, range).stream() |
| | | .filter(ledger -> matchLedgerKeyword(ledger, keyword)) |
| | | .collect(Collectors.toList()); |
| | | Map<Long, PurchaseLedger> ledgerMap = ledgers.stream() |
| | | .filter(ledger -> ledger.getId() != null) |
| | | .collect(Collectors.toMap(PurchaseLedger::getId, ledger -> ledger, (a, b) -> a, LinkedHashMap::new)); |
| | | if (ledgerMap.isEmpty()) { |
| | | return jsonResponse(true, "purchase_unstocked_list", "æªæ¥è¯¢å°ç¬¦åæ¡ä»¶çéè´è®¢åã", |
| | | rangeSummary(range, 0), Map.of("items", List.of()), Map.of()); |
| | | } |
| | | |
| | | List<SalesLedgerProduct> products = defaultList(salesLedgerProductMapper.selectList(new LambdaQueryWrapper<SalesLedgerProduct>() |
| | | .eq(SalesLedgerProduct::getType, 2) |
| | | .in(SalesLedgerProduct::getSalesLedgerId, ledgerMap.keySet()))); |
| | | |
| | | List<Map<String, Object>> items = products.stream() |
| | | .filter(product -> matchProductKeyword(product, keyword)) |
| | | .map(product -> toUnstockedItem(product, ledgerMap.get(product.getSalesLedgerId()))) |
| | | .filter(Objects::nonNull) |
| | | .limit(normalizeLimit(limit)) |
| | | .collect(Collectors.toList()); |
| | | |
| | | return jsonResponse(true, "purchase_unstocked_list", "å·²è¿åæªå
¥åºéè´è®¢åã", |
| | | rangeSummary(range, items.size()), Map.of("items", items), Map.of()); |
| | | } |
| | | |
| | | @Tool(name = "æ¥è¯¢éè´å°è´§å¼å¸¸", value = "æ¥è¯¢å°è´§ç¶æå¼å¸¸æå¤æ³¨å
å«å¼å¸¸ä¿¡æ¯çå°è´§è®°å½ã") |
| | | public String listArrivalExceptions(@ToolMemoryId String memoryId, |
| | | @P(value = "å¼å§æ¥æ yyyy-MM-dd", required = false) String startDate, |
| | | @P(value = "ç»ææ¥æ yyyy-MM-dd", required = false) String endDate, |
| | | @P(value = "æ¶é´èå´æè¿°ï¼ä¾å¦è¿7å¤©ãæ¬æ", required = false) String timeRange, |
| | | @P(value = "è¿åæ¡æ°ï¼é»è®¤10ï¼æå¤§30", required = false) Integer limit) { |
| | | LoginUser loginUser = currentLoginUser(memoryId); |
| | | DateRange range = resolveDateRange(startDate, endDate, timeRange); |
| | | LambdaQueryWrapper<InboundManagement> wrapper = new LambdaQueryWrapper<>(); |
| | | applyTenantFilter(wrapper, loginUser.getTenantId(), InboundManagement::getTenantId); |
| | | wrapper.ge(InboundManagement::getArrivalTime, toDate(range.start())) |
| | | .lt(InboundManagement::getArrivalTime, toExclusiveEndDate(range.end())) |
| | | .and(w -> w.notLike(InboundManagement::getStatus, "æ£å¸¸") |
| | | .notLike(InboundManagement::getStatus, "宿") |
| | | .notLike(InboundManagement::getStatus, "å·²å°è´§") |
| | | .or().like(InboundManagement::getStatus, "å¼å¸¸") |
| | | .or().like(InboundManagement::getRemark, "å¼å¸¸") |
| | | .or().like(InboundManagement::getRemark, "é®é¢") |
| | | .or().like(InboundManagement::getRemark, "å»¶è¿") |
| | | .or().like(InboundManagement::getRemark, "ç缺")); |
| | | wrapper.orderByDesc(InboundManagement::getArrivalTime).last("limit " + normalizeLimit(limit)); |
| | | |
| | | List<Map<String, Object>> items = defaultList(inboundManagementMapper.selectList(wrapper)).stream() |
| | | .map(this::toArrivalItem) |
| | | .collect(Collectors.toList()); |
| | | return jsonResponse(true, "purchase_arrival_exception_list", "å·²è¿åéè´å°è´§å¼å¸¸è®°å½ã", |
| | | rangeSummary(range, items.size()), Map.of("items", items), Map.of()); |
| | | } |
| | | |
| | | @Tool(name = "æ¥è¯¢å¾
仿¬¾éè´å", value = "æ¥è¯¢ååéé¢å¤§äºå·²ä»æ¬¾éé¢çéè´åã") |
| | | public String listPendingPaymentOrders(@ToolMemoryId String memoryId, |
| | | @P(value = "å¼å§æ¥æ yyyy-MM-dd", required = false) String startDate, |
| | | @P(value = "ç»ææ¥æ yyyy-MM-dd", required = false) String endDate, |
| | | @P(value = "å
³é®åï¼å¯å¹é
éè´ååå·/ä¾åºå/项ç®å", required = false) String keyword, |
| | | @P(value = "è¿åæ¡æ°ï¼é»è®¤10ï¼æå¤§30", required = false) Integer limit) { |
| | | LoginUser loginUser = currentLoginUser(memoryId); |
| | | DateRange range = resolveDateRange(startDate, endDate, null); |
| | | List<Map<String, Object>> items = queryLedgers(loginUser, range).stream() |
| | | .filter(ledger -> matchLedgerKeyword(ledger, keyword)) |
| | | .map(ledger -> toPendingPaymentItem(loginUser, ledger)) |
| | | .filter(Objects::nonNull) |
| | | .sorted(Comparator.comparing(item -> (BigDecimal) item.get("pendingAmount"), Comparator.reverseOrder())) |
| | | .limit(normalizeLimit(limit)) |
| | | .collect(Collectors.toList()); |
| | | return jsonResponse(true, "purchase_pending_payment_list", "å·²è¿åå¾
仿¬¾éè´åã", |
| | | rangeSummary(range, items.size()), Map.of("items", items), Map.of()); |
| | | } |
| | | |
| | | @Tool(name = "æ¥è¯¢éè´éè´§æ
åµ", value = "ææ¶é´èå´æ¥è¯¢éè´éè´§åå表åéè´§éé¢ã") |
| | | public String listPurchaseReturns(@ToolMemoryId String memoryId, |
| | | @P(value = "å¼å§æ¥æ yyyy-MM-dd", required = false) String startDate, |
| | | @P(value = "ç»ææ¥æ yyyy-MM-dd", required = false) String endDate, |
| | | @P(value = "å
³é®åï¼å¯å¹é
éè´§åå·/夿³¨", required = false) String keyword, |
| | | @P(value = "è¿åæ¡æ°ï¼é»è®¤10ï¼æå¤§30", required = false) Integer limit) { |
| | | LoginUser loginUser = currentLoginUser(memoryId); |
| | | DateRange range = resolveDateRange(startDate, endDate, null); |
| | | LambdaQueryWrapper<PurchaseReturnOrders> wrapper = new LambdaQueryWrapper<>(); |
| | | applyDeptFilter(wrapper, loginUser.getCurrentDeptId(), PurchaseReturnOrders::getDeptId); |
| | | wrapper.ge(PurchaseReturnOrders::getPreparedAt, range.start()) |
| | | .le(PurchaseReturnOrders::getPreparedAt, range.end()); |
| | | if (StringUtils.hasText(keyword)) { |
| | | wrapper.and(w -> w.like(PurchaseReturnOrders::getNo, keyword) |
| | | .or().like(PurchaseReturnOrders::getRemark, keyword) |
| | | .or().like(PurchaseReturnOrders::getReturnUserName, keyword)); |
| | | } |
| | | wrapper.orderByDesc(PurchaseReturnOrders::getPreparedAt).last("limit " + normalizeLimit(limit)); |
| | | |
| | | List<PurchaseReturnOrders> returns = defaultList(purchaseReturnOrdersMapper.selectList(wrapper)); |
| | | BigDecimal totalAmount = returns.stream() |
| | | .map(PurchaseReturnOrders::getTotalAmount) |
| | | .filter(Objects::nonNull) |
| | | .reduce(BigDecimal.ZERO, BigDecimal::add); |
| | | Map<String, Object> summary = rangeSummary(range, returns.size()); |
| | | summary.put("returnAmount", totalAmount); |
| | | |
| | | return jsonResponse(true, "purchase_return_list", "å·²è¿åéè´éè´§æ
åµã", |
| | | summary, |
| | | Map.of("items", returns.stream().map(this::toReturnItem).collect(Collectors.toList())), |
| | | Map.of()); |
| | | } |
| | | |
| | | private List<PurchaseLedger> queryLedgers(LoginUser loginUser, DateRange range) { |
| | | LambdaQueryWrapper<PurchaseLedger> wrapper = new LambdaQueryWrapper<>(); |
| | | applyTenantFilter(wrapper, loginUser.getTenantId(), PurchaseLedger::getTenantId); |
| | | wrapper.ge(PurchaseLedger::getEntryDate, toDate(range.start())) |
| | | .le(PurchaseLedger::getEntryDate, toDate(range.end())); |
| | | .lt(PurchaseLedger::getEntryDate, toExclusiveEndDate(range.end())); |
| | | return defaultList(purchaseLedgerMapper.selectList(wrapper)); |
| | | } |
| | | |
| | | private Map<String, Object> rangeSummary(DateRange range, int count) { |
| | | Map<String, Object> summary = new LinkedHashMap<>(); |
| | | summary.put("timeRange", range.label()); |
| | | summary.put("startDate", range.start().toString()); |
| | | summary.put("endDate", range.end().toString()); |
| | | summary.put("count", count); |
| | | return summary; |
| | | } |
| | | |
| | | private boolean matchLedgerKeyword(PurchaseLedger ledger, String keyword) { |
| | | if (!StringUtils.hasText(keyword)) { |
| | | return true; |
| | | } |
| | | String text = keyword.trim(); |
| | | return safe(ledger.getPurchaseContractNumber()).contains(text) |
| | | || safe(ledger.getSupplierName()).contains(text) |
| | | || safe(ledger.getProjectName()).contains(text); |
| | | } |
| | | |
| | | private boolean matchProductKeyword(SalesLedgerProduct product, String keyword) { |
| | | if (!StringUtils.hasText(keyword)) { |
| | | return true; |
| | | } |
| | | String text = keyword.trim(); |
| | | return safe(product.getProductCategory()).contains(text) |
| | | || safe(product.getSpecificationModel()).contains(text); |
| | | } |
| | | |
| | | private Map<String, Object> toUnstockedItem(SalesLedgerProduct product, PurchaseLedger ledger) { |
| | | if (product == null || ledger == null || product.getId() == null) { |
| | | return null; |
| | | } |
| | | BigDecimal orderedQuantity = defaultDecimal(product.getQuantity()); |
| | | BigDecimal inboundQuantity = sumInboundQuantity(product.getId()); |
| | | BigDecimal pendingQuantity = orderedQuantity.subtract(inboundQuantity); |
| | | if (pendingQuantity.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return null; |
| | | } |
| | | Map<String, Object> item = new LinkedHashMap<>(); |
| | | item.put("purchaseLedgerId", ledger.getId()); |
| | | item.put("purchaseContractNumber", safe(ledger.getPurchaseContractNumber())); |
| | | item.put("supplierName", safe(ledger.getSupplierName())); |
| | | item.put("productCategory", safe(product.getProductCategory())); |
| | | item.put("specificationModel", safe(product.getSpecificationModel())); |
| | | item.put("unit", safe(product.getUnit())); |
| | | item.put("orderedQuantity", orderedQuantity); |
| | | item.put("inboundQuantity", inboundQuantity); |
| | | item.put("pendingInboundQuantity", pendingQuantity); |
| | | item.put("entryDate", formatDate(ledger.getEntryDate())); |
| | | return item; |
| | | } |
| | | |
| | | private BigDecimal sumInboundQuantity(Long salesLedgerProductId) { |
| | | List<ProcurementRecordStorage> records = defaultList(procurementRecordMapper.selectList(new LambdaQueryWrapper<ProcurementRecordStorage>() |
| | | .eq(ProcurementRecordStorage::getType, 1) |
| | | .eq(ProcurementRecordStorage::getSalesLedgerProductId, salesLedgerProductId))); |
| | | return records.stream() |
| | | .map(ProcurementRecordStorage::getInboundNum) |
| | | .filter(Objects::nonNull) |
| | | .reduce(BigDecimal.ZERO, BigDecimal::add); |
| | | } |
| | | |
| | | private Map<String, Object> toArrivalItem(InboundManagement item) { |
| | | Map<String, Object> map = new LinkedHashMap<>(); |
| | | map.put("id", item.getId()); |
| | | map.put("orderNo", safe(item.getOrderNo())); |
| | | map.put("arrivalNo", safe(item.getArrivalNo())); |
| | | map.put("supplierName", safe(item.getSupplierName())); |
| | | map.put("status", safe(item.getStatus())); |
| | | map.put("arrivalTime", formatDate(item.getArrivalTime())); |
| | | map.put("arrivalQuantity", safe(item.getArrivalQuantity())); |
| | | map.put("remark", safe(item.getRemark())); |
| | | return map; |
| | | } |
| | | |
| | | private Map<String, Object> toPendingPaymentItem(LoginUser loginUser, PurchaseLedger ledger) { |
| | | BigDecimal contractAmount = defaultDecimal(ledger.getContractAmount()); |
| | | BigDecimal paidAmount = sumPaymentAmount(loginUser, ledger.getId()); |
| | | BigDecimal pendingAmount = contractAmount.subtract(paidAmount); |
| | | if (pendingAmount.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return null; |
| | | } |
| | | Map<String, Object> item = toLedgerItem(ledger); |
| | | item.put("paidAmount", paidAmount); |
| | | item.put("pendingAmount", pendingAmount); |
| | | return item; |
| | | } |
| | | |
| | | private BigDecimal sumPaymentAmount(LoginUser loginUser, Long purchaseLedgerId) { |
| | | LambdaQueryWrapper<PaymentRegistration> wrapper = new LambdaQueryWrapper<>(); |
| | | applyTenantFilter(wrapper, loginUser.getTenantId(), PaymentRegistration::getTenantId); |
| | | wrapper.eq(PaymentRegistration::getPurchaseLedgerId, purchaseLedgerId); |
| | | return defaultList(paymentRegistrationMapper.selectList(wrapper)).stream() |
| | | .map(PaymentRegistration::getCurrentPaymentAmount) |
| | | .filter(Objects::nonNull) |
| | | .reduce(BigDecimal.ZERO, BigDecimal::add); |
| | | } |
| | | |
| | | private Map<String, Object> toReturnItem(PurchaseReturnOrders item) { |
| | | Map<String, Object> map = new LinkedHashMap<>(); |
| | | map.put("id", item.getId()); |
| | | map.put("no", safe(item.getNo())); |
| | | map.put("returnType", item.getReturnType()); |
| | | map.put("purchaseLedgerId", item.getPurchaseLedgerId()); |
| | | map.put("preparedAt", item.getPreparedAt() == null ? "" : item.getPreparedAt().toString()); |
| | | map.put("returnUserName", safe(item.getReturnUserName())); |
| | | map.put("totalAmount", item.getTotalAmount()); |
| | | map.put("remark", safe(item.getRemark())); |
| | | return map; |
| | | } |
| | | |
| | | private BigDecimal defaultDecimal(BigDecimal value) { |
| | | return value == null ? BigDecimal.ZERO : value; |
| | | } |
| | | |
| | | private List<PaymentRegistration> queryPayments(LoginUser loginUser, DateRange range) { |
| | | LambdaQueryWrapper<PaymentRegistration> wrapper = new LambdaQueryWrapper<>(); |
| | | applyTenantFilter(wrapper, loginUser.getTenantId(), PaymentRegistration::getTenantId); |
| | | wrapper.ge(PaymentRegistration::getPaymentDate, toDate(range.start())) |
| | | .le(PaymentRegistration::getPaymentDate, toDate(range.end())); |
| | | .lt(PaymentRegistration::getPaymentDate, toExclusiveEndDate(range.end())); |
| | | return defaultList(paymentRegistrationMapper.selectList(wrapper)); |
| | | } |
| | | |
| | |
| | | if (text.contains("è¿å个æ") || text.contains("æè¿å个æ") || text.contains("å个æ")) { |
| | | return new DateRange(today.minusDays(14), today, "è¿å个æ"); |
| | | } |
| | | java.util.regex.Matcher relativeMatcher = java.util.regex.Pattern.compile("(è¿|æè¿)(\\d+)(天|å¨|个æ|æ|å¹´)").matcher(text); |
| | | if (relativeMatcher.find()) { |
| | | int amount = Integer.parseInt(relativeMatcher.group(2)); |
| | | String unit = relativeMatcher.group(3); |
| | | LocalDate relativeStart = switch (unit) { |
| | | case "天" -> today.minusDays(Math.max(amount - 1L, 0)); |
| | | case "å¨" -> today.minusWeeks(Math.max(amount, 1)).plusDays(1); |
| | | case "个æ", "æ" -> today.minusMonths(Math.max(amount, 1)).plusDays(1); |
| | | case "å¹´" -> today.minusYears(Math.max(amount, 1)).plusDays(1); |
| | | default -> today.minusDays(29); |
| | | }; |
| | | return new DateRange(relativeStart, today, "è¿" + amount + unit); |
| | | } |
| | | return new DateRange(today.minusDays(29), today, "è¿30天"); |
| | | } |
| | | |
| | |
| | | |
| | | private Date toDate(LocalDate localDate) { |
| | | return Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant()); |
| | | } |
| | | |
| | | private Date toExclusiveEndDate(LocalDate localDate) { |
| | | return Date.from(localDate.plusDays(1).atStartOfDay(ZoneId.systemDefault()).toInstant()); |
| | | } |
| | | |
| | | private String formatDate(Date date) { |
| | |
| | | |
| | | private record DateRange(LocalDate start, LocalDate end, String label) { |
| | | } |
| | | |
| | | private static class MaterialRankItem { |
| | | private final String productCategory; |
| | | private final String specificationModel; |
| | | private final String unit; |
| | | private BigDecimal quantity = BigDecimal.ZERO; |
| | | private BigDecimal amount = BigDecimal.ZERO; |
| | | |
| | | private MaterialRankItem(String productCategory, String specificationModel, String unit) { |
| | | this.productCategory = productCategory; |
| | | this.specificationModel = specificationModel; |
| | | this.unit = unit; |
| | | } |
| | | |
| | | private Map<String, Object> toMap() { |
| | | Map<String, Object> map = new LinkedHashMap<>(); |
| | | map.put("productCategory", productCategory); |
| | | map.put("specificationModel", specificationModel); |
| | | map.put("unit", unit); |
| | | map.put("quantity", quantity); |
| | | map.put("amount", amount); |
| | | return map; |
| | | } |
| | | } |
| | | } |
| | |
| | | package com.ruoyi.approve.service.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; |
| | | import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.ruoyi.approve.vo.ApproveProcessVo; |
| | | import com.ruoyi.approve.bean.vo.ApproveGetAndUpdateVo; |
| | | import com.ruoyi.approve.bean.vo.ApproveProcessConfigNodeVo; |
| | | import com.ruoyi.approve.bean.vo.ApproveProcessVO; |
| | |
| | | import com.ruoyi.approve.service.ApproveProcessConfigNodeService; |
| | | import com.ruoyi.approve.service.IApproveNodeService; |
| | | import com.ruoyi.approve.service.IApproveProcessService; |
| | | import com.ruoyi.basic.enums.ApplicationTypeEnum; |
| | | import com.ruoyi.approve.vo.ApproveProcessVo; |
| | | import com.ruoyi.basic.enums.RecordTypeEnum; |
| | | import com.ruoyi.basic.utils.FileUtil; |
| | | import com.ruoyi.common.enums.FileNameType; |
| | |
| | | List<ApproveProcessConfigNodeVo> list = approveProcessConfigNodeService.listNode( approveProcessVO.getApproveType()); |
| | | List<Long> nodeIds = list.stream() |
| | | .map(ApproveProcessConfigNodeVo::getApproverId) |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.toList()); |
| | | if(list.isEmpty()) { |
| | | throw new RuntimeException("æµç¨ä¸åå¨"); |
| | | } |
| | | if (CollectionUtils.isEmpty(nodeIds)) { |
| | | autoPassPurchaseApproveIfNoApprover(approveProcessVO); |
| | | return; |
| | | } |
| | | List<SysUser> sysUsers = sysUserMapper.selectUserByIds(nodeIds); |
| | | if (CollectionUtils.isEmpty(sysUsers)) throw new RuntimeException("å®¡æ ¸ç¨æ·ä¸åå¨"); |
| | | if (sysDept == null) throw new RuntimeException("é¨é¨ä¸åå¨"); |
| | |
| | | } |
| | | } |
| | | |
| | | private void autoPassPurchaseApproveIfNoApprover(ApproveProcessVO approveProcessVO) { |
| | | if (!Objects.equals(approveProcessVO.getApproveType(), 5) |
| | | || !StringUtils.hasText(approveProcessVO.getApproveReason())) { |
| | | throw new RuntimeException("å®¡æ ¸ç¨æ·ä¸åå¨"); |
| | | } |
| | | purchaseLedgerMapper.update(null, new LambdaUpdateWrapper<PurchaseLedger>() |
| | | .eq(PurchaseLedger::getPurchaseContractNumber, approveProcessVO.getApproveReason()) |
| | | .set(PurchaseLedger::getApprovalStatus, 3)); |
| | | } |
| | | |
| | | @Override |
| | | public List<SysDept> selectDeptListByDeptIds(Long[] deptIds) { |
| | | List<SysDept> sysDeptList = new ArrayList<SysDept>(); |
| | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.ruoyi.basic.pojo.StorageBlob; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | import org.apache.ibatis.annotations.Param; |
| | | |
| | | /** |
| | | * <p> |
| | |
| | | @Mapper |
| | | public interface StorageBlobMapper extends BaseMapper<StorageBlob> { |
| | | |
| | | java.util.List<StorageBlob> selectOrphanBlobsByIdRange(@Param("lastId") long lastId, @Param("limit") int limit); |
| | | |
| | | int deleteByIdList(@Param("ids") java.util.List<Long> ids); |
| | | |
| | | java.util.List<String> selectExistingUidFilenames(@Param("fileNames") java.util.List<String> fileNames); |
| | | } |
| | |
| | | @Excel(name = "è§æ ¼åå·") |
| | | private String model; |
| | | |
| | | @Excel(name = "产åç¼ç ") |
| | | @TableField("product_code") |
| | | private String productCode; |
| | | |
| | | /** |
| | | * åä½ |
| | | */ |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.basic.task; |
| | | |
| | | import com.ruoyi.basic.mapper.StorageBlobMapper; |
| | | import com.ruoyi.basic.pojo.StorageBlob; |
| | | import com.ruoyi.common.config.FileProperties; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.scheduling.annotation.Scheduled; |
| | | import org.springframework.stereotype.Component; |
| | | import org.springframework.util.CollectionUtils; |
| | | import org.springframework.util.StringUtils; |
| | | |
| | | import java.io.File; |
| | | import java.util.ArrayDeque; |
| | | import java.util.ArrayList; |
| | | import java.util.Deque; |
| | | import java.util.HashSet; |
| | | import java.util.List; |
| | | import java.util.Set; |
| | | import java.util.concurrent.atomic.AtomicBoolean; |
| | | |
| | | /** |
| | | * æ¸
çæ ææä»¶å®æ¶ä»»å¡ã |
| | | */ |
| | | @Slf4j |
| | | @Component |
| | | @RequiredArgsConstructor |
| | | public class StorageBlobCleanupTask { |
| | | |
| | | private static final int DB_BATCH_SIZE = 500; |
| | | private static final int FILE_NAME_BATCH_SIZE = 1000; |
| | | |
| | | private final StorageBlobMapper storageBlobMapper; |
| | | private final FileProperties fileProperties; |
| | | |
| | | private final AtomicBoolean running = new AtomicBoolean(false); |
| | | |
| | | /** |
| | | * æ¯æ 1 å·åæ¨ 2 ç¹æ§è¡ä¸æ¬¡ï¼ |
| | | * 1. å é¤ storage_blob 䏿ªè¢« storage_attachment å
³èçè®°å½åå
¶æä»¶ |
| | | * 2. å é¤ç£çä¸ä¸åå¨äº storage_blob.uid_filename çæä»¶ |
| | | */ |
| | | @Scheduled(cron = "0 0 2 1 * ?") |
| | | public void cleanupUnusedStorageFiles() { |
| | | if (!running.compareAndSet(false, true)) { |
| | | log.warn("æä»¶æ¸
ç任塿£å¨æ§è¡ï¼æ¬æ¬¡è·³è¿"); |
| | | return; |
| | | } |
| | | |
| | | long start = System.currentTimeMillis(); |
| | | log.info("æä»¶æ¸
çä»»å¡å¼å§æ§è¡ï¼æ ¹ç®å½ï¼{}", fileProperties.getPath()); |
| | | try { |
| | | int removedBlobCount = cleanupOrphanStorageBlobs(); |
| | | int removedDiskFileCount = cleanupOrphanDiskFiles(); |
| | | long cost = System.currentTimeMillis() - start; |
| | | log.info("æä»¶æ¸
ç任塿§è¡å®æï¼å é¤å¤å¿ blob è®°å½ï¼{}ï¼å é¤ç£çæ ææä»¶ï¼{}ï¼èæ¶ï¼{} ms", |
| | | removedBlobCount, removedDiskFileCount, cost); |
| | | } catch (Exception e) { |
| | | log.error("æä»¶æ¸
ç任塿§è¡å¤±è´¥", e); |
| | | } finally { |
| | | running.set(false); |
| | | } |
| | | } |
| | | |
| | | private int cleanupOrphanStorageBlobs() { |
| | | long lastId = 0L; |
| | | int removedCount = 0; |
| | | |
| | | while (true) { |
| | | List<StorageBlob> orphanBlobs = storageBlobMapper.selectOrphanBlobsByIdRange(lastId, DB_BATCH_SIZE); |
| | | if (CollectionUtils.isEmpty(orphanBlobs)) { |
| | | break; |
| | | } |
| | | |
| | | List<Long> ids = new ArrayList<>(orphanBlobs.size()); |
| | | for (StorageBlob storageBlob : orphanBlobs) { |
| | | ids.add(storageBlob.getId()); |
| | | deleteBlobFiles(storageBlob); |
| | | } |
| | | storageBlobMapper.deleteByIdList(ids); |
| | | removedCount += ids.size(); |
| | | lastId = orphanBlobs.get(orphanBlobs.size() - 1).getId(); |
| | | |
| | | log.info("å·²å é¤ä¸æ¹å¤å¿ blobï¼batchSize={}ï¼lastId={}", ids.size(), lastId); |
| | | } |
| | | |
| | | return removedCount; |
| | | } |
| | | |
| | | private int cleanupOrphanDiskFiles() { |
| | | File rootDirectory = new File(fileProperties.getPath()); |
| | | if (!rootDirectory.exists() || !rootDirectory.isDirectory()) { |
| | | log.warn("æä»¶æ ¹ç®å½ä¸å卿䏿¯ç®å½ï¼è·³è¿ç£çæ¸
çï¼{}", fileProperties.getPath()); |
| | | return 0; |
| | | } |
| | | |
| | | int deletedCount = 0; |
| | | Deque<File> directories = new ArrayDeque<>(); |
| | | directories.push(rootDirectory); |
| | | |
| | | while (!directories.isEmpty()) { |
| | | File currentDirectory = directories.pop(); |
| | | File[] children = currentDirectory.listFiles(); |
| | | if (children == null || children.length == 0) { |
| | | continue; |
| | | } |
| | | |
| | | List<File> filesInDirectory = new ArrayList<>(); |
| | | for (File child : children) { |
| | | if (child.isDirectory()) { |
| | | directories.push(child); |
| | | } else if (child.isFile()) { |
| | | filesInDirectory.add(child); |
| | | } |
| | | } |
| | | |
| | | deletedCount += cleanupFilesInDirectory(filesInDirectory); |
| | | } |
| | | |
| | | return deletedCount; |
| | | } |
| | | |
| | | private int cleanupFilesInDirectory(List<File> filesInDirectory) { |
| | | if (CollectionUtils.isEmpty(filesInDirectory)) { |
| | | return 0; |
| | | } |
| | | |
| | | int deletedCount = 0; |
| | | for (int start = 0; start < filesInDirectory.size(); start += FILE_NAME_BATCH_SIZE) { |
| | | int end = Math.min(start + FILE_NAME_BATCH_SIZE, filesInDirectory.size()); |
| | | List<File> batchFiles = filesInDirectory.subList(start, end); |
| | | List<String> fileNames = new ArrayList<>(batchFiles.size()); |
| | | for (File file : batchFiles) { |
| | | fileNames.add(file.getName()); |
| | | } |
| | | |
| | | Set<String> existingFileNames = new HashSet<>(storageBlobMapper.selectExistingUidFilenames(fileNames)); |
| | | for (File file : batchFiles) { |
| | | if (!existingFileNames.contains(file.getName()) && safeDelete(file)) { |
| | | deletedCount++; |
| | | } |
| | | } |
| | | } |
| | | return deletedCount; |
| | | } |
| | | |
| | | private void deleteBlobFiles(StorageBlob storageBlob) { |
| | | File originalFile = resolveBlobFile(storageBlob); |
| | | safeDelete(originalFile); |
| | | |
| | | File compressedFile = resolveCompressedFile(originalFile); |
| | | safeDelete(compressedFile); |
| | | } |
| | | |
| | | private File resolveBlobFile(StorageBlob storageBlob) { |
| | | String basePath = fileProperties.getPath(); |
| | | if (!StringUtils.hasText(storageBlob.getPath())) { |
| | | return new File(basePath, storageBlob.getUidFilename()); |
| | | } |
| | | return new File(new File(basePath, storageBlob.getPath()), storageBlob.getUidFilename()); |
| | | } |
| | | |
| | | private File resolveCompressedFile(File originalFile) { |
| | | if (originalFile == null) { |
| | | return null; |
| | | } |
| | | File parent = originalFile.getParentFile(); |
| | | if (parent == null) { |
| | | return null; |
| | | } |
| | | return new File(parent, "thumb_" + originalFile.getName()); |
| | | } |
| | | |
| | | private boolean safeDelete(File file) { |
| | | if (file == null || !file.exists() || !file.isFile()) { |
| | | return false; |
| | | } |
| | | if (file.delete()) { |
| | | return true; |
| | | } |
| | | log.warn("å é¤æä»¶å¤±è´¥ï¼{}", file.getAbsolutePath()); |
| | | return false; |
| | | } |
| | | } |
| | |
| | | PURCHASE_STOCK_IN("7", "éè´-å
¥åº"), |
| | | QUALITYINSPECT_STOCK_IN("6", "è´¨æ£-åæ ¼å
¥åº"), |
| | | DEFECTIVE_PASS("11", "ä¸åæ ¼-è®©æ¥æ¾è¡"), |
| | | RETURN_HE_IN("14", "éå®éè´§-åæ ¼å
¥åº"); |
| | | RETURN_HE_IN("14", "éå®éè´§-åæ ¼å
¥åº"), |
| | | PICK_RETURN_IN("20", "éå®éè´§-åæ ¼å
¥åº"); |
| | | |
| | | |
| | | private final String code; |
| | |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.baomidou.mybatisplus.extension.service.IService; |
| | | import com.ruoyi.measuringinstrumentledger.pojo.MeasuringInstrumentLedgerRecord; |
| | | |
| | | import jakarta.servlet.http.HttpServletResponse; |
| | | import java.io.IOException; |
| | | |
| | | /** |
| | | * @author :yys |
| | |
| | | |
| | | void export(HttpServletResponse response); |
| | | |
| | | boolean updateMeasuringInstrumentLedgerRecord(MeasuringInstrumentLedgerRecord measuringInstrumentLedgerRecord) throws IOException; |
| | | boolean updateMeasuringInstrumentLedgerRecord(MeasuringInstrumentLedgerRecord measuringInstrumentLedgerRecord); |
| | | } |
| | |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.ruoyi.common.enums.FileNameType; |
| | | import com.ruoyi.common.utils.StringUtils; |
| | | import com.ruoyi.common.utils.poi.ExcelUtil; |
| | | import com.ruoyi.measuringinstrumentledger.mapper.MeasuringInstrumentLedgerMapper; |
| | | import com.ruoyi.measuringinstrumentledger.mapper.MeasuringInstrumentLedgerRecordMapper; |
| | | import com.ruoyi.measuringinstrumentledger.pojo.MeasuringInstrumentLedger; |
| | | import com.ruoyi.measuringinstrumentledger.pojo.MeasuringInstrumentLedgerRecord; |
| | | import com.ruoyi.measuringinstrumentledger.service.MeasuringInstrumentLedgerRecordService; |
| | | import com.ruoyi.other.mapper.TempFileMapper; |
| | | import com.ruoyi.other.pojo.TempFile; |
| | | import com.ruoyi.sales.mapper.CommonFileMapper; |
| | | import com.ruoyi.sales.pojo.CommonFile; |
| | | import jakarta.servlet.http.HttpServletResponse; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.apache.commons.io.FilenameUtils; |
| | | import org.springframework.beans.factory.annotation.Value; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.io.IOException; |
| | | import java.nio.file.Files; |
| | | import java.nio.file.Path; |
| | | import java.nio.file.Paths; |
| | | import java.nio.file.StandardCopyOption; |
| | | import java.time.LocalDate; |
| | | import java.time.LocalDateTime; |
| | | import java.time.format.DateTimeFormatter; |
| | | import java.util.Date; |
| | | import java.util.List; |
| | | import java.util.UUID; |
| | | |
| | | /** |
| | | * @author :yys |
| | |
| | | private final MeasuringInstrumentLedgerRecordMapper measuringInstrumentLedgerRecordMapper; |
| | | private final MeasuringInstrumentLedgerMapper measuringInstrumentLedgerMapper; |
| | | private final CommonFileMapper commonFileMapper; |
| | | private final TempFileMapper tempFileMapper; |
| | | |
| | | @Value("${file.upload-dir}") |
| | | private String uploadDir; |
| | | |
| | | @Override |
| | | public IPage<MeasuringInstrumentLedgerRecord> listPage(Page page, MeasuringInstrumentLedgerRecord measuringInstrumentLedgerRecord) { |
| | |
| | | } |
| | | |
| | | @Override |
| | | public boolean updateMeasuringInstrumentLedgerRecord(MeasuringInstrumentLedgerRecord measuringInstrumentLedgerRecord) throws IOException { |
| | | public boolean updateMeasuringInstrumentLedgerRecord(MeasuringInstrumentLedgerRecord measuringInstrumentLedgerRecord) { |
| | | MeasuringInstrumentLedgerRecord measuringInstrumentLedgerRecord1 = measuringInstrumentLedgerRecordMapper.selectById(measuringInstrumentLedgerRecord.getId()); |
| | | if (measuringInstrumentLedgerRecord1 == null) { |
| | | return false; |
| | |
| | | measuringInstrumentLedgerMapper.updateById(measuringInstrumentLedger); |
| | | } |
| | | measuringInstrumentLedgerRecordMapper.updateById(measuringInstrumentLedgerRecord); |
| | | // è®°å½éä»¶ç»å® |
| | | migrateTempFilesToFormal(measuringInstrumentLedgerRecord.getId(), measuringInstrumentLedgerRecord.getTempFileIds(), FileNameType.MEASURINGRecord.getValue()); |
| | | return true; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * å°ä¸´æ¶æä»¶è¿ç§»å°æ£å¼ç®å½ |
| | | * |
| | | * @param businessId ä¸å¡IDï¼éå®å°è´¦IDï¼ |
| | | * @param tempFileIds ä¸´æ¶æä»¶IDå表 |
| | | * @throws IOException æä»¶æä½å¼å¸¸ |
| | | */ |
| | | private void migrateTempFilesToFormal(Long businessId, List<String> tempFileIds,Integer fileType) throws IOException { |
| | | if (com.baomidou.mybatisplus.core.toolkit.CollectionUtils.isEmpty(tempFileIds)) { |
| | | return; |
| | | } |
| | | |
| | | // æå»ºæ£å¼ç®å½è·¯å¾ï¼æä¸å¡ç±»å忥æåç»ï¼ |
| | | String formalDir = uploadDir + LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE); |
| | | |
| | | Path formalDirPath = Paths.get(formalDir); |
| | | |
| | | // ç¡®ä¿æ£å¼ç®å½åå¨ï¼éå½åå»ºï¼ |
| | | if (!Files.exists(formalDirPath)) { |
| | | Files.createDirectories(formalDirPath); |
| | | } |
| | | |
| | | for (String tempFileId : tempFileIds) { |
| | | // æ¥è¯¢ä¸´æ¶æä»¶è®°å½ |
| | | TempFile tempFile = tempFileMapper.selectById(tempFileId); |
| | | if (tempFile == null) { |
| | | log.warn("ä¸´æ¶æä»¶ä¸åå¨ï¼è·³è¿å¤ç: {}", tempFileId); |
| | | continue; |
| | | } |
| | | |
| | | // æå»ºæ£å¼æä»¶åï¼å
å«ä¸å¡IDåæ¶é´æ³ï¼é¿å
å²çªï¼ |
| | | String originalFilename = tempFile.getOriginalName(); |
| | | String fileExtension = FilenameUtils.getExtension(originalFilename); |
| | | String formalFilename = businessId + "_" + |
| | | System.currentTimeMillis() + "_" + |
| | | UUID.randomUUID().toString().substring(0, 8) + |
| | | (StringUtils.hasText(fileExtension) ? "." + fileExtension : ""); |
| | | |
| | | Path formalFilePath = formalDirPath.resolve(formalFilename); |
| | | |
| | | try { |
| | | // æ§è¡æä»¶è¿ç§»ï¼ä½¿ç¨ååæä½ç¡®ä¿å®å
¨æ§ï¼ |
| | | // Files.move( |
| | | // Paths.get(tempFile.getTempPath()), |
| | | // formalFilePath, |
| | | // StandardCopyOption.REPLACE_EXISTING, |
| | | // StandardCopyOption.ATOMIC_MOVE |
| | | // ); |
| | | // ååç§»å¨å¤±è´¥ï¼ä½¿ç¨å¤å¶+å é¤ |
| | | Files.copy(Paths.get(tempFile.getTempPath()), formalFilePath, StandardCopyOption.REPLACE_EXISTING); |
| | | Files.deleteIfExists(Paths.get(tempFile.getTempPath())); |
| | | log.info("æä»¶è¿ç§»æå: {} -> {}", tempFile.getTempPath(), formalFilePath); |
| | | |
| | | // æ´æ°æä»¶è®°å½ï¼å
³èå°ä¸å¡IDï¼ |
| | | CommonFile fileRecord = new CommonFile(); |
| | | fileRecord.setCommonId(businessId); |
| | | fileRecord.setName(originalFilename); |
| | | fileRecord.setUrl(formalFilePath.toString()); |
| | | fileRecord.setCreateTime(LocalDateTime.now()); |
| | | fileRecord.setType(fileType); |
| | | commonFileMapper.insert(fileRecord); |
| | | |
| | | // å é¤ä¸´æ¶æä»¶è®°å½ |
| | | tempFileMapper.deleteById(tempFile); |
| | | |
| | | log.info("æä»¶è¿ç§»æå: {} -> {}", tempFile.getTempPath(), formalFilePath); |
| | | } catch (IOException e) { |
| | | log.error("æä»¶è¿ç§»å¤±è´¥: {}", tempFile.getTempPath(), e); |
| | | // å¯éæ©åæ»äºå¡æè®°å½å¤±è´¥æä»¶ |
| | | throw new IOException("æä»¶è¿ç§»å¼å¸¸", e); |
| | | } |
| | | } |
| | | } |
| | | } |
| | |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.ruoyi.common.enums.FileNameType; |
| | | import com.ruoyi.common.utils.StringUtils; |
| | | import com.ruoyi.common.utils.poi.ExcelUtil; |
| | | import com.ruoyi.measuringinstrumentledger.dto.MeasuringInstrumentLedgerDto; |
| | | import com.ruoyi.measuringinstrumentledger.mapper.MeasuringInstrumentLedgerMapper; |
| | |
| | | import com.ruoyi.measuringinstrumentledger.pojo.MeasuringInstrumentLedger; |
| | | import com.ruoyi.measuringinstrumentledger.pojo.MeasuringInstrumentLedgerRecord; |
| | | import com.ruoyi.measuringinstrumentledger.service.MeasuringInstrumentLedgerService; |
| | | import com.ruoyi.other.mapper.TempFileMapper; |
| | | import com.ruoyi.other.pojo.TempFile; |
| | | import com.ruoyi.project.system.domain.SysUser; |
| | | import com.ruoyi.project.system.mapper.SysUserMapper; |
| | | import com.ruoyi.sales.mapper.CommonFileMapper; |
| | |
| | | import jakarta.servlet.http.HttpServletResponse; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.apache.commons.io.FilenameUtils; |
| | | import org.springframework.beans.factory.annotation.Value; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.io.IOException; |
| | | import java.nio.file.Files; |
| | | import java.nio.file.Path; |
| | | import java.nio.file.Paths; |
| | | import java.nio.file.StandardCopyOption; |
| | | import java.time.LocalDate; |
| | | import java.time.LocalDateTime; |
| | | import java.time.format.DateTimeFormatter; |
| | | import java.util.ArrayList; |
| | | import java.util.Date; |
| | | import java.util.List; |
| | | import java.util.UUID; |
| | | import java.util.stream.Collectors; |
| | | |
| | | /** |
| | |
| | | |
| | | private final MeasuringInstrumentLedgerMapper measuringInstrumentLedgerMapper; |
| | | private final MeasuringInstrumentLedgerRecordMapper measuringInstrumentLedgerRecordMapper; |
| | | private final TempFileMapper tempFileMapper; |
| | | private final CommonFileMapper commonFileMapper; |
| | | private final SysUserMapper sysUserMapper; |
| | | |
| | | @Value("${file.upload-dir}") |
| | | private String uploadDir; |
| | | |
| | | @Override |
| | | public IPage<MeasuringInstrumentLedger> listPage(Page page, MeasuringInstrumentLedger measuringInstrumentLedger) { |
| | |
| | | collect = measuringInstrumentLedgerRecords.stream().map(MeasuringInstrumentLedgerRecord::getId).collect(Collectors.toList()); |
| | | } |
| | | collect.add(item.getId()); |
| | | LambdaQueryWrapper<CommonFile> salesLedgerFileWrapper = new LambdaQueryWrapper<>(); |
| | | salesLedgerFileWrapper.in(CommonFile::getCommonId, collect) |
| | | .in(CommonFile::getType,types); |
| | | List<CommonFile> commonFiles = commonFileMapper.selectList(salesLedgerFileWrapper); |
| | | item.setCommonFiles(commonFiles); |
| | | |
| | | }); |
| | | return measuringInstrumentLedgerIPage; |
| | | } |
| | |
| | | // if(!CollectionUtils.isEmpty(req.getTempFileIds())){ |
| | | // migrateTempFilesToFormal(measuringInstrumentLedger.getId(), req.getTempFileIds(), FileNameType.MEASURING.getValue()); |
| | | // } |
| | | // å°è´¦è®°å½ç»å®ä¸æ¬¡ |
| | | if(!CollectionUtils.isEmpty(req.getTempFileIds())){ |
| | | migrateTempFilesToFormal(measuringInstrumentLedgerRecord.getId(), req.getTempFileIds(), FileNameType.MEASURINGRecord.getValue()); |
| | | } |
| | | return true; |
| | | } |
| | | return false; |
| | |
| | | } |
| | | measuringInstrumentLedger.setUserName(sysUser.getUserName()); |
| | | measuringInstrumentLedgerMapper.insert(measuringInstrumentLedger); |
| | | if(!CollectionUtils.isEmpty(measuringInstrumentLedger.getTempFileIds())){ |
| | | migrateTempFilesToFormal(measuringInstrumentLedger.getId(), measuringInstrumentLedger.getTempFileIds(), FileNameType.MEASURING.getValue()); |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * å°ä¸´æ¶æä»¶è¿ç§»å°æ£å¼ç®å½ |
| | | * |
| | | * @param businessId ä¸å¡IDï¼éå®å°è´¦IDï¼ |
| | | * @param tempFileIds ä¸´æ¶æä»¶IDå表 |
| | | * @throws IOException æä»¶æä½å¼å¸¸ |
| | | */ |
| | | private void migrateTempFilesToFormal(Long businessId, List<String> tempFileIds,Integer fileType) throws IOException { |
| | | if (CollectionUtils.isEmpty(tempFileIds)) { |
| | | return; |
| | | } |
| | | |
| | | // æå»ºæ£å¼ç®å½è·¯å¾ï¼æä¸å¡ç±»å忥æåç»ï¼ |
| | | String formalDir = uploadDir + LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE); |
| | | |
| | | Path formalDirPath = Paths.get(formalDir); |
| | | |
| | | // ç¡®ä¿æ£å¼ç®å½åå¨ï¼éå½åå»ºï¼ |
| | | if (!Files.exists(formalDirPath)) { |
| | | Files.createDirectories(formalDirPath); |
| | | } |
| | | |
| | | for (String tempFileId : tempFileIds) { |
| | | // æ¥è¯¢ä¸´æ¶æä»¶è®°å½ |
| | | TempFile tempFile = tempFileMapper.selectById(tempFileId); |
| | | if (tempFile == null) { |
| | | log.warn("ä¸´æ¶æä»¶ä¸åå¨ï¼è·³è¿å¤ç: {}", tempFileId); |
| | | continue; |
| | | } |
| | | |
| | | // æå»ºæ£å¼æä»¶åï¼å
å«ä¸å¡IDåæ¶é´æ³ï¼é¿å
å²çªï¼ |
| | | String originalFilename = tempFile.getOriginalName(); |
| | | String fileExtension = FilenameUtils.getExtension(originalFilename); |
| | | String formalFilename = businessId + "_" + |
| | | System.currentTimeMillis() + "_" + |
| | | UUID.randomUUID().toString().substring(0, 8) + |
| | | (StringUtils.hasText(fileExtension) ? "." + fileExtension : ""); |
| | | |
| | | Path formalFilePath = formalDirPath.resolve(formalFilename); |
| | | |
| | | try { |
| | | // æ§è¡æä»¶è¿ç§»ï¼ä½¿ç¨ååæä½ç¡®ä¿å®å
¨æ§ï¼ |
| | | // Files.move( |
| | | // Paths.get(tempFile.getTempPath()), |
| | | // formalFilePath, |
| | | // StandardCopyOption.REPLACE_EXISTING, |
| | | // StandardCopyOption.ATOMIC_MOVE |
| | | // ); |
| | | // ååç§»å¨å¤±è´¥ï¼ä½¿ç¨å¤å¶+å é¤ |
| | | Files.copy(Paths.get(tempFile.getTempPath()), formalFilePath, StandardCopyOption.REPLACE_EXISTING); |
| | | Files.deleteIfExists(Paths.get(tempFile.getTempPath())); |
| | | log.info("æä»¶è¿ç§»æå: {} -> {}", tempFile.getTempPath(), formalFilePath); |
| | | |
| | | // æ´æ°æä»¶è®°å½ï¼å
³èå°ä¸å¡IDï¼ |
| | | CommonFile fileRecord = new CommonFile(); |
| | | fileRecord.setCommonId(businessId); |
| | | fileRecord.setName(originalFilename); |
| | | fileRecord.setUrl(formalFilePath.toString()); |
| | | fileRecord.setCreateTime(LocalDateTime.now()); |
| | | fileRecord.setType(fileType); |
| | | commonFileMapper.insert(fileRecord); |
| | | |
| | | // å é¤ä¸´æ¶æä»¶è®°å½ |
| | | tempFileMapper.deleteById(tempFile); |
| | | |
| | | log.info("æä»¶è¿ç§»æå: {} -> {}", tempFile.getTempPath(), formalFilePath); |
| | | } catch (IOException e) { |
| | | log.error("æä»¶è¿ç§»å¤±è´¥: {}", tempFile.getTempPath(), e); |
| | | // å¯éæ©åæ»äºå¡æè®°å½å¤±è´¥æä»¶ |
| | | throw new IOException("æä»¶è¿ç§»å¼å¸¸", e); |
| | | } |
| | | } |
| | | } |
| | | } |
| | |
| | | import com.baomidou.mybatisplus.core.toolkit.ObjectUtils; |
| | | import com.ruoyi.procurementrecord.mapper.ProcurementRecordMapper; |
| | | import com.ruoyi.procurementrecord.mapper.ProcurementRecordOutMapper; |
| | | import com.ruoyi.stock.dto.StockInRecordDto; |
| | | import com.ruoyi.stock.dto.StockInventoryDto; |
| | | import com.ruoyi.stock.dto.StockUninventoryDto; |
| | | import com.ruoyi.stock.mapper.StockInventoryMapper; |
| | |
| | | import com.ruoyi.stock.service.StockInventoryService; |
| | | import com.ruoyi.stock.service.StockOutRecordService; |
| | | import com.ruoyi.stock.service.StockUninventoryService; |
| | | import com.ruoyi.stock.service.impl.StockInRecordServiceImpl; |
| | | import com.ruoyi.stock.service.impl.StockOutRecordServiceImpl; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.Collections; |
| | | import java.util.HashMap; |
| | | import java.util.Map; |
| | | |
| | | @Component |
| | | @RequiredArgsConstructor |
| | |
| | | |
| | | /** |
| | | * ä¸åæ ¼å
¥åº |
| | | * |
| | | * @param productModelId |
| | | * @param quantity |
| | | * @param recordType |
| | | * @param recordId |
| | | */ |
| | | public void addUnStock(Long productModelId, BigDecimal quantity, String recordType,Long recordId) { |
| | | public void addUnStock(Long productModelId, BigDecimal quantity, String recordType, Long recordId) { |
| | | StockUninventoryDto stockUninventoryDto = new StockUninventoryDto(); |
| | | stockUninventoryDto.setRecordId(recordId); |
| | | stockUninventoryDto.setRecordType(String.valueOf(recordType)); |
| | |
| | | |
| | | /** |
| | | * ä¸åæ ¼åºåº |
| | | * |
| | | * @param productModelId |
| | | * @param quantity |
| | | * @param recordType |
| | | * @param recordId |
| | | */ |
| | | public void subtractUnStock(Long productModelId, BigDecimal quantity, Integer recordType,Long recordId) { |
| | | public void subtractUnStock(Long productModelId, BigDecimal quantity, Integer recordType, Long recordId) { |
| | | StockUninventoryDto stockUninventoryDto = new StockUninventoryDto(); |
| | | stockUninventoryDto.setRecordId(recordId); |
| | | stockUninventoryDto.setRecordType(String.valueOf(recordType)); |
| | |
| | | * @param recordType |
| | | * @param recordId |
| | | */ |
| | | public void addStock(Long productModelId, BigDecimal quantity, String recordType,Long recordId) { |
| | | public void addStock(Long productModelId, BigDecimal quantity, String recordType, Long recordId) { |
| | | StockInventoryDto stockInventoryDto = new StockInventoryDto(); |
| | | stockInventoryDto.setRecordId(recordId); |
| | | stockInventoryDto.setRecordType(String.valueOf(recordType)); |
| | |
| | | |
| | | /** |
| | | * åæ ¼åºåº |
| | | * |
| | | * @param productModelId |
| | | * @param quantity |
| | | * @param recordType |
| | | * @param recordId |
| | | */ |
| | | public void substractStock(Long productModelId, BigDecimal quantity, String recordType,Long recordId) { |
| | | public void substractStock(Long productModelId, BigDecimal quantity, String recordType, Long recordId) { |
| | | StockInventoryDto stockInventoryDto = new StockInventoryDto(); |
| | | stockInventoryDto.setRecordId(recordId); |
| | | stockInventoryDto.setRecordType(String.valueOf(recordType)); |
| | |
| | | } |
| | | |
| | | } |
| | | |
| | | public void deleteStockOutRecord(Long recordId, String recordType) { |
| | | StockOutRecord one = stockOutRecordService.getOne(new QueryWrapper<StockOutRecord>() |
| | | .lambda().eq(StockOutRecord::getRecordId, recordId) |
| | |
| | | |
| | | @Schema(description = "æ¥å·¥äººååç§°ï¼å¤ä¸ªä½¿ç¨éå·åé") |
| | | private String userNames; |
| | | |
| | | @Schema(description = "æ¯å¦ç»æï¼") |
| | | private Boolean endOrder; |
| | | } |
| | |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.List; |
| | | |
| | | @EqualsAndHashCode(callSuper = true) |
| | |
| | | |
| | | @Schema(description = "bomç¼å·") |
| | | private String bomNo; |
| | | |
| | | @Schema(description = "宿è¿åº¦") |
| | | private BigDecimal completionStatus; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.production.bean.vo; |
| | | |
| | | import com.ruoyi.production.pojo.ProductionOperationTask; |
| | | import com.ruoyi.production.pojo.ProductionOrderRoutingOperationParam; |
| | | import com.ruoyi.production.pojo.ProductionProductMain; |
| | | import com.ruoyi.production.pojo.ProductionProductOutput; |
| | | import com.ruoyi.quality.pojo.QualityInspect; |
| | | import com.ruoyi.quality.pojo.QualityInspectFile; |
| | | import com.ruoyi.quality.pojo.QualityInspectParam; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | import java.util.List; |
| | | |
| | | @Data |
| | | @Schema(name = "ProductionOrderWorkOrderDetailVo", description = "Production order work order/report/inspect detail") |
| | | public class ProductionOrderWorkOrderDetailVo { |
| | | |
| | | @Schema(description = "Production order info") |
| | | private ProductionOrderVo productionOrder; |
| | | |
| | | @Schema(description = "Work order list") |
| | | private List<WorkOrderDetail> workOrderList; |
| | | |
| | | @Data |
| | | @Schema(name = "WorkOrderDetail", description = "Work order detail") |
| | | public static class WorkOrderDetail { |
| | | |
| | | @Schema(description = "Work order info") |
| | | private ProductionOperationTask workOrder; |
| | | |
| | | @Schema(description = "Report list under current work order") |
| | | private List<ReportDetail> reportList; |
| | | } |
| | | |
| | | @Data |
| | | @Schema(name = "ReportDetail", description = "Production report detail") |
| | | public static class ReportDetail { |
| | | |
| | | @Schema(description = "Report main info") |
| | | private ProductionProductMain reportMain; |
| | | |
| | | @Schema(description = "Report output list") |
| | | private List<ProductionProductOutput> reportOutputList; |
| | | |
| | | @Schema(description = "Report process param list") |
| | | private List<ProductionOrderRoutingOperationParam> reportParamList; |
| | | |
| | | @Schema(description = "Inspect list under current report") |
| | | private List<InspectDetail> inspectList; |
| | | } |
| | | |
| | | @Data |
| | | @Schema(name = "InspectDetail", description = "Quality inspect detail") |
| | | public static class InspectDetail { |
| | | |
| | | @Schema(description = "Inspect main info") |
| | | private QualityInspect inspect; |
| | | |
| | | @Schema(description = "Inspect param list") |
| | | private List<QualityInspectParam> inspectParamList; |
| | | |
| | | @Schema(description = "Inspect attachment list") |
| | | private List<QualityInspectFile> inspectFileList; |
| | | } |
| | | } |
| | |
| | | import com.ruoyi.production.bean.vo.ProductionOrderPickVo; |
| | | import com.ruoyi.production.bean.vo.ProductionOrderVo; |
| | | import com.ruoyi.production.bean.vo.ProductionPlanVo; |
| | | import com.ruoyi.production.bean.vo.ProductionOrderWorkOrderDetailVo; |
| | | import com.ruoyi.production.pojo.ProductionOrder; |
| | | import com.ruoyi.production.service.ProductionOrderService; |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | |
| | | public R<List<ProductionOrderPickVo>> pick(@PathVariable Long productionOrderId) { |
| | | return R.ok(productionOrderService.pick(productionOrderId)); |
| | | } |
| | | |
| | | @GetMapping("/workOrder/detail/{productionOrderId}") |
| | | @Operation(summary = "Query work orders/reports/inspects by production order id") |
| | | public R<ProductionOrderWorkOrderDetailVo> getWorkOrderReportInspectDetail(@PathVariable Long productionOrderId) { |
| | | return R.ok(productionOrderService.getWorkOrderReportInspectDetail(productionOrderId)); |
| | | } |
| | | |
| | | @Operation(summary = "æ´æ°è®¢åç¶æ") |
| | | @PostMapping("/updateOrder") |
| | | public R updateOrder(@RequestBody ProductionOrderDto productionOrderDto) { |
| | | return R.ok(productionOrderService.updateOrder(productionOrderDto)); |
| | | } |
| | | } |
| | |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import jakarta.servlet.http.HttpServletResponse; |
| | | import lombok.AllArgsConstructor; |
| | | import org.springframework.security.access.prepost.PreAuthorize; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import java.util.List; |
| | |
| | | * @return |
| | | */ |
| | | @PostMapping("/addProductMain") |
| | | @PreAuthorize("@ss.hasPermi('productionProductMain:add')") |
| | | public R addProductMain(@RequestBody ProductionProductMainDto productionProductMainDto) { |
| | | return R.ok(productionProductMainService.addProductMain(productionProductMainDto)); |
| | | } |
| | |
| | | @DateTimeFormat(pattern = "yyyy-MM-dd") |
| | | private LocalDate planCompleteTime; |
| | | |
| | | @Schema(description = "ç¶æï¼1.å¾
å¼å§ 2.è¿è¡ä¸ 3.已宿 4.已忶ï¼") |
| | | @Schema(description = "ç¶æï¼1.å¾
å¼å§ 2.è¿è¡ä¸ 3.已宿 4.已忶 5.å·²ç»æï¼") |
| | | private Integer status; |
| | | |
| | | @Schema(description = "æ¯å¦ç»æï¼") |
| | | @TableField("is_end_order") |
| | | private Boolean endOrder; |
| | | } |
| | |
| | | import com.ruoyi.production.bean.vo.ProductionOrderPickVo; |
| | | import com.ruoyi.production.bean.vo.ProductionOrderVo; |
| | | import com.ruoyi.production.bean.vo.ProductionPlanVo; |
| | | import com.ruoyi.production.bean.vo.ProductionOrderWorkOrderDetailVo; |
| | | import com.ruoyi.production.pojo.ProductionOrder; |
| | | |
| | | import java.util.List; |
| | |
| | | List<ProductionPlanVo> getSource(Long id); |
| | | |
| | | List<ProductionOrderPickVo> pick(Long productionOrderId); |
| | | |
| | | ProductionOrderWorkOrderDetailVo getWorkOrderReportInspectDetail(Long productionOrderId); |
| | | |
| | | int updateOrder(ProductionOrderDto productionOrderDto); |
| | | } |
| | |
| | | import com.ruoyi.common.utils.StringUtils; |
| | | import com.ruoyi.production.bean.dto.ProductionOperationTaskDto; |
| | | import com.ruoyi.production.bean.vo.ProductionOperationTaskVo; |
| | | import com.ruoyi.production.mapper.ProductionOrderMapper; |
| | | import com.ruoyi.production.mapper.ProductionOperationTaskMapper; |
| | | import com.ruoyi.production.pojo.ProductionOrder; |
| | | import com.ruoyi.production.pojo.ProductionOperationTask; |
| | | import com.ruoyi.production.service.ProductionOperationTaskService; |
| | | import com.ruoyi.project.system.domain.SysUser; |
| | |
| | | public class ProductionOperationTaskServiceImpl extends ServiceImpl<ProductionOperationTaskMapper, ProductionOperationTask> implements ProductionOperationTaskService { |
| | | |
| | | private final SysUserMapper sysUserMapper; |
| | | private final ProductionOrderMapper productionOrderMapper; |
| | | |
| | | private final FileUtil fileUtil; |
| | | |
| | |
| | | return null; |
| | | } |
| | | ProductionOperationTaskVo vo = BeanUtil.copyProperties(item, ProductionOperationTaskVo.class); |
| | | if (item.getProductionOrderId() != null) { |
| | | ProductionOrder productionOrder = productionOrderMapper.selectById(item.getProductionOrderId()); |
| | | if (productionOrder != null) { |
| | | vo.setEndOrder(productionOrder.getEndOrder()); |
| | | } |
| | | } |
| | | fillUserNames(Collections.singletonList(vo)); |
| | | return vo; |
| | | } |
| | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.ruoyi.common.enums.StockInQualifiedRecordTypeEnum; |
| | | import com.ruoyi.common.exception.ServiceException; |
| | | import com.ruoyi.common.utils.StringUtils; |
| | | import com.ruoyi.production.bean.dto.ProductionOrderPickDto; |
| | |
| | | import com.ruoyi.stock.dto.StockInventoryDto; |
| | | import com.ruoyi.stock.mapper.StockInventoryMapper; |
| | | import com.ruoyi.stock.pojo.StockInventory; |
| | | import com.ruoyi.stock.service.StockInventoryService; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | |
| | | private final ProductionOperationTaskMapper productionOperationTaskMapper; |
| | | private final ProductionOrderPickRecordMapper productionOrderPickRecordMapper; |
| | | private final StockInventoryMapper stockInventoryMapper; |
| | | private final StockInventoryService stockInventoryService; |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | |
| | | List<String> batchNoList = resolveBatchNoList(resolvedDto); |
| | | String inventoryBatchNo = pickInventoryBatchNo(batchNoList); |
| | | String storedBatchNo = formatBatchNoStorage(batchNoList); |
| | | subtractInventory(resolvedDto.getProductModelId(), inventoryBatchNo, resolvedDto.getPickQuantity(), rowNo); |
| | | subtractInventory(resolvedDto.getProductModelId(), storedBatchNo, resolvedDto.getPickQuantity(), rowNo); |
| | | |
| | | ProductionOrderPick orderPick = new ProductionOrderPick(); |
| | | orderPick.setProductionOrderId(resolvedDto.getProductionOrderId()); |
| | |
| | | List<String> batchNoList = resolveBatchNoList(dto); |
| | | String inventoryBatchNo = pickInventoryBatchNo(batchNoList); |
| | | String storedBatchNo = formatBatchNoStorage(batchNoList); |
| | | subtractInventory(dto.getProductModelId(), inventoryBatchNo, dto.getPickQuantity(), rowNo); |
| | | subtractInventory(dto.getProductModelId(), storedBatchNo, dto.getPickQuantity(), rowNo); |
| | | |
| | | ProductionOrderPick orderPick = new ProductionOrderPick(); |
| | | orderPick.setProductionOrderId(dto.getProductionOrderId()); |
| | |
| | | List<String> batchNoList = resolveBatchNoList(dto); |
| | | String inventoryBatchNo = batchNoList.isEmpty() |
| | | ? resolveInventoryBatchNoFromStored(oldPick.getBatchNo()) |
| | | : pickInventoryBatchNo(batchNoList); |
| | | : formatBatchNoStorage(batchNoList); |
| | | BigDecimal feedingQuantity = dto.getFeedingQuantity(); |
| | | |
| | | subtractInventory(productModelId, inventoryBatchNo, feedingQuantity, rowNo); |
| | |
| | | if (sameStockKey) { |
| | | BigDecimal delta = newQuantity.subtract(oldQuantity); |
| | | if (delta.compareTo(BigDecimal.ZERO) > 0) { |
| | | subtractInventory(newProductModelId, newBatchNo, delta, rowNo); |
| | | subtractInventory(newProductModelId, newStoredBatchNo, delta, rowNo); |
| | | } else if (delta.compareTo(BigDecimal.ZERO) < 0) { |
| | | addInventory(oldProductModelId, oldBatchNo, delta.abs()); |
| | | } |
| | | } else { |
| | | addInventory(oldProductModelId, oldBatchNo, oldQuantity); |
| | | subtractInventory(newProductModelId, newBatchNo, newQuantity, rowNo); |
| | | subtractInventory(newProductModelId, newStoredBatchNo, newQuantity, rowNo); |
| | | } |
| | | |
| | | oldPick.setProductModelId(newProductModelId); |
| | |
| | | } |
| | | |
| | | private void subtractInventory(Long productModelId, String batchNo, BigDecimal quantity, int rowNo) { |
| | | StockInventory stockInventory = stockInventoryMapper.selectOne(buildStockWrapper(productModelId, batchNo)); |
| | | if (stockInventory == null) { |
| | | throw new ServiceException("第" + rowNo + "æ¡é¢æå¯¹åºåºåä¸åå¨"); |
| | | BigDecimal deductQuantity = defaultDecimal(quantity); |
| | | if (deductQuantity.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return; |
| | | } |
| | | BigDecimal availableQuantity = defaultDecimal(stockInventory.getQualitity()) |
| | | .subtract(defaultDecimal(stockInventory.getLockedQuantity())); |
| | | if (quantity.compareTo(availableQuantity) > 0) { |
| | | throw new ServiceException("第" + rowNo + "æ¡é¢æå¯ç¨åºåä¸è¶³"); |
| | | |
| | | List<String> batchNoList = parseBatchNoValue(batchNo); |
| | | if (batchNoList.isEmpty()) { |
| | | batchNoList = Collections.singletonList(null); |
| | | } |
| | | StockInventoryDto stockInventoryDto = new StockInventoryDto(); |
| | | stockInventoryDto.setProductModelId(productModelId); |
| | | stockInventoryDto.setBatchNo(batchNo); |
| | | stockInventoryDto.setQualitity(quantity); |
| | | int affected = stockInventoryMapper.updateSubtractStockInventory(stockInventoryDto); |
| | | if (affected <= 0) { |
| | | throw new ServiceException("第" + rowNo + "æ¡é¢ææ£ååºå失败"); |
| | | |
| | | Map<String, BigDecimal> availableQuantityMap = new LinkedHashMap<>(); |
| | | BigDecimal totalAvailableQuantity = BigDecimal.ZERO; |
| | | for (String currentBatchNo : batchNoList) { |
| | | StockInventory stockInventory = stockInventoryMapper.selectOne(buildStockWrapper(productModelId, currentBatchNo)); |
| | | BigDecimal availableQuantity = BigDecimal.ZERO; |
| | | if (stockInventory != null) { |
| | | availableQuantity = defaultDecimal(stockInventory.getQualitity()) |
| | | .subtract(defaultDecimal(stockInventory.getLockedQuantity())); |
| | | if (availableQuantity.compareTo(BigDecimal.ZERO) < 0) { |
| | | availableQuantity = BigDecimal.ZERO; |
| | | } |
| | | } |
| | | availableQuantityMap.put(currentBatchNo, availableQuantity); |
| | | totalAvailableQuantity = totalAvailableQuantity.add(availableQuantity); |
| | | } |
| | | |
| | | if (deductQuantity.compareTo(totalAvailableQuantity) > 0) { |
| | | BigDecimal shortQuantity = deductQuantity.subtract(totalAvailableQuantity); |
| | | throw new ServiceException("颿å¯ç¨åºåä¸è¶³ï¼å¯ç¨åºå为" + formatQuantity(totalAvailableQuantity) |
| | | + "ï¼è¿å·®" + formatQuantity(shortQuantity)); |
| | | } |
| | | |
| | | BigDecimal remainingQuantity = deductQuantity; |
| | | for (Map.Entry<String, BigDecimal> entry : availableQuantityMap.entrySet()) { |
| | | if (remainingQuantity.compareTo(BigDecimal.ZERO) <= 0) { |
| | | break; |
| | | } |
| | | BigDecimal availableQuantity = defaultDecimal(entry.getValue()); |
| | | if (availableQuantity.compareTo(BigDecimal.ZERO) <= 0) { |
| | | continue; |
| | | } |
| | | BigDecimal currentDeductQuantity = remainingQuantity.min(availableQuantity); |
| | | StockInventoryDto stockInventoryDto = new StockInventoryDto(); |
| | | stockInventoryDto.setProductModelId(productModelId); |
| | | stockInventoryDto.setBatchNo(entry.getKey()); |
| | | stockInventoryDto.setQualitity(currentDeductQuantity); |
| | | int affected = stockInventoryMapper.updateSubtractStockInventory(stockInventoryDto); |
| | | if (affected <= 0) { |
| | | throw new ServiceException("第" + rowNo + "æ¡é¢ææ£ååºå失败"); |
| | | } |
| | | remainingQuantity = remainingQuantity.subtract(currentDeductQuantity); |
| | | } |
| | | |
| | | if (remainingQuantity.compareTo(BigDecimal.ZERO) > 0) { |
| | | throw new ServiceException("第" + rowNo + "æ¡é¢ææ£ååºå失败ï¼å©ä½å¾
æ£åæ°é为" + formatQuantity(remainingQuantity)); |
| | | } |
| | | } |
| | | |
| | |
| | | if (addQuantity.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return; |
| | | } |
| | | StockInventory stockInventory = stockInventoryMapper.selectOne(buildStockWrapper(productModelId, batchNo)); |
| | | if (stockInventory == null) { |
| | | StockInventory newStockInventory = new StockInventory(); |
| | | newStockInventory.setProductModelId(productModelId); |
| | | newStockInventory.setBatchNo(batchNo); |
| | | newStockInventory.setQualitity(addQuantity); |
| | | newStockInventory.setLockedQuantity(BigDecimal.ZERO); |
| | | newStockInventory.setVersion(1); |
| | | stockInventoryMapper.insert(newStockInventory); |
| | | return; |
| | | } |
| | | StockInventoryDto stockInventoryDto = new StockInventoryDto(); |
| | | stockInventoryDto.setProductModelId(productModelId); |
| | | stockInventoryDto.setBatchNo(batchNo); |
| | | stockInventoryDto.setQualitity(addQuantity); |
| | | int affected = stockInventoryMapper.updateAddStockInventory(stockInventoryDto); |
| | | if (affected <= 0) { |
| | | throw new ServiceException("åºååé失败ï¼äº§åè§æ ¼ID=" + productModelId); |
| | | } |
| | | stockInventoryDto.setRecordType(String.valueOf(StockInQualifiedRecordTypeEnum.PICK_RETURN_IN.getCode())); |
| | | stockInventoryDto.setRecordId(0L); |
| | | stockInventoryService.addStockInRecordOnly(stockInventoryDto); |
| | | } |
| | | |
| | | private List<ProductionOrderPickDto> resolvePickItems(ProductionOrderPickDto dto) { |
| | |
| | | private BigDecimal defaultDecimal(BigDecimal value) { |
| | | return value == null ? BigDecimal.ZERO : value; |
| | | } |
| | | |
| | | private String formatQuantity(BigDecimal value) { |
| | | return defaultDecimal(value).stripTrailingZeros().toPlainString(); |
| | | } |
| | | } |
| | |
| | | import com.ruoyi.production.bean.vo.ProductionOrderPickVo; |
| | | import com.ruoyi.production.bean.vo.ProductionOrderVo; |
| | | import com.ruoyi.production.bean.vo.ProductionPlanVo; |
| | | import com.ruoyi.production.bean.vo.ProductionOrderWorkOrderDetailVo; |
| | | import com.ruoyi.production.enums.ProductOrderStatusEnum; |
| | | import com.ruoyi.production.mapper.*; |
| | | import com.ruoyi.production.pojo.*; |
| | | import com.ruoyi.quality.mapper.QualityInspectFileMapper; |
| | | import com.ruoyi.quality.mapper.QualityInspectMapper; |
| | | import com.ruoyi.quality.mapper.QualityInspectParamMapper; |
| | | import com.ruoyi.quality.pojo.QualityInspect; |
| | | import com.ruoyi.quality.pojo.QualityInspectFile; |
| | | import com.ruoyi.quality.pojo.QualityInspectParam; |
| | | import com.ruoyi.production.service.ProductionOrderService; |
| | | import com.ruoyi.sales.mapper.SalesLedgerMapper; |
| | | import com.ruoyi.sales.mapper.SalesLedgerProductMapper; |
| | |
| | | private final ProductionOrderBomMapper productionOrderBomMapper; |
| | | private final ProductionBomStructureMapper productionBomStructureMapper; |
| | | private final ProductionProductMainMapper productionProductMainMapper; |
| | | private final ProductionProductOutputMapper productionProductOutputMapper; |
| | | private final ProductionOrderPickMapper productionOrderPickMapper; |
| | | private final ProductionOrderPickRecordMapper productionOrderPickRecordMapper; |
| | | private final QualityInspectMapper qualityInspectMapper; |
| | | private final QualityInspectParamMapper qualityInspectParamMapper; |
| | | private final QualityInspectFileMapper qualityInspectFileMapper; |
| | | private final ProductionPlanMapper productionPlanMapper; |
| | | private final StockInventoryMapper stockInventoryMapper; |
| | | private final StorageAttachmentMapper storageAttachmentMapper; |
| | |
| | | List<TechnologyRoutingOperation> routingOperations = technologyRoutingOperationMapper.selectList( |
| | | Wrappers.<TechnologyRoutingOperation>lambdaQuery() |
| | | .eq(TechnologyRoutingOperation::getTechnologyRoutingId, technologyRouting.getId()) |
| | | .orderByAsc(TechnologyRoutingOperation::getDragSort) |
| | | .orderByAsc(TechnologyRoutingOperation::getId)); |
| | | .orderByDesc(TechnologyRoutingOperation::getDragSort) |
| | | .orderByDesc(TechnologyRoutingOperation::getId)); |
| | | Map<Long, String> operationNameMap = technologyOperationMapper.selectBatchIds( |
| | | routingOperations.stream() |
| | | .map(TechnologyRoutingOperation::getTechnologyOperationId) |
| | |
| | | .collect(Collectors.toSet())) |
| | | .stream() |
| | | .collect(Collectors.toMap(TechnologyOperation::getId, TechnologyOperation::getName, (a, b) -> a)); |
| | | Integer lastDragSort = routingOperations.stream() |
| | | .map(TechnologyRoutingOperation::getDragSort) |
| | | .filter(Objects::nonNull) |
| | | .max(Integer::compareTo) |
| | | .orElse(null); |
| | | for (TechnologyRoutingOperation sourceOperation : routingOperations) { |
| | | // 订åå·¥åºä¿åçæ¯å·¥èºå·¥åºå¿«ç
§ï¼åç»æ¥å·¥åªä¾èµå¿«ç
§ï¼ä¸åç´æ¥å¼ç¨å·¥èºä¸»æ°æ®ã |
| | | ProductionOrderRoutingOperation targetOperation = new ProductionOrderRoutingOperation(); |
| | |
| | | targetOperation.setOrderRoutingId(orderRouting.getId()); |
| | | targetOperation.setProductModelId(sourceOperation.getProductModelId()); |
| | | targetOperation.setDragSort(sourceOperation.getDragSort()); |
| | | targetOperation.setIsProduction(sourceOperation.getIsProduction()); |
| | | targetOperation.setIsQuality(sourceOperation.getIsQuality()); |
| | | targetOperation.setOperationName(operationNameMap.get(sourceOperation.getTechnologyOperationId())); |
| | | targetOperation.setTechnologyOperationId(sourceOperation.getTechnologyOperationId()); |
| | | productionOrderRoutingOperationMapper.insert(targetOperation); |
| | | |
| | | ProductionOperationTask task = new ProductionOperationTask(); |
| | | task.setProductionOrderRoutingOperationId(targetOperation.getId()); |
| | | task.setProductionOrderId(productionOrder.getId()); |
| | | task.setPlanQuantity(defaultDecimal(productionOrder.getQuantity())); |
| | | task.setCompleteQuantity(BigDecimal.ZERO); |
| | | task.setWorkOrderNo(generateNextTaskNo()); |
| | | task.setStatus(2); |
| | | productionOperationTaskMapper.insert(task); |
| | | boolean isLastOperation = lastDragSort != null && Objects.equals(sourceOperation.getDragSort(), lastDragSort); |
| | | if (isLastOperation || Boolean.TRUE.equals(targetOperation.getIsProduction())) { |
| | | ProductionOperationTask task = new ProductionOperationTask(); |
| | | task.setProductionOrderRoutingOperationId(targetOperation.getId()); |
| | | task.setProductionOrderId(productionOrder.getId()); |
| | | task.setPlanQuantity(defaultDecimal(productionOrder.getQuantity())); |
| | | task.setCompleteQuantity(BigDecimal.ZERO); |
| | | task.setWorkOrderNo(generateNextTaskNo()); |
| | | task.setStatus(2); |
| | | productionOperationTaskMapper.insert(task); |
| | | } |
| | | |
| | | List<TechnologyRoutingOperationParam> sourceParams = technologyRoutingOperationParamMapper.selectList( |
| | | Wrappers.<TechnologyRoutingOperationParam>lambdaQuery() |
| | |
| | | } |
| | | |
| | | @Override |
| | | public ProductionOrderWorkOrderDetailVo getWorkOrderReportInspectDetail(Long productionOrderId) { |
| | | if (productionOrderId == null) { |
| | | throw new ServiceException("productionOrderId can not be null"); |
| | | } |
| | | ProductionOrderVo orderInfo = getProductionOrderInfo(productionOrderId); |
| | | if (orderInfo == null) { |
| | | throw new ServiceException("production order not found"); |
| | | } |
| | | |
| | | ProductionOrderWorkOrderDetailVo detailVo = new ProductionOrderWorkOrderDetailVo(); |
| | | detailVo.setProductionOrder(orderInfo); |
| | | |
| | | List<ProductionOperationTask> workOrderList = productionOperationTaskMapper.selectList( |
| | | Wrappers.<ProductionOperationTask>lambdaQuery() |
| | | .eq(ProductionOperationTask::getProductionOrderId, productionOrderId) |
| | | .orderByAsc(ProductionOperationTask::getId)); |
| | | if (workOrderList == null || workOrderList.isEmpty()) { |
| | | detailVo.setWorkOrderList(Collections.emptyList()); |
| | | return detailVo; |
| | | } |
| | | |
| | | List<Long> workOrderIdList = workOrderList.stream() |
| | | .map(ProductionOperationTask::getId) |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.toList()); |
| | | List<ProductionProductMain> reportMainList = workOrderIdList.isEmpty() |
| | | ? Collections.emptyList() |
| | | : productionProductMainMapper.selectList( |
| | | Wrappers.<ProductionProductMain>lambdaQuery() |
| | | .in(ProductionProductMain::getProductionOperationTaskId, workOrderIdList) |
| | | .orderByAsc(ProductionProductMain::getId)); |
| | | Map<Long, List<ProductionProductMain>> reportMainMap = new LinkedHashMap<>(); |
| | | for (ProductionProductMain reportMain : reportMainList) { |
| | | if (reportMain == null || reportMain.getProductionOperationTaskId() == null) { |
| | | continue; |
| | | } |
| | | reportMainMap.computeIfAbsent(reportMain.getProductionOperationTaskId(), k -> new ArrayList<>()).add(reportMain); |
| | | } |
| | | |
| | | List<Long> reportMainIdList = reportMainList.stream() |
| | | .map(ProductionProductMain::getId) |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.toList()); |
| | | Map<Long, List<ProductionProductOutput>> reportOutputMap = new LinkedHashMap<>(); |
| | | Map<Long, List<ProductionOrderRoutingOperationParam>> reportParamMap = new LinkedHashMap<>(); |
| | | Map<Long, List<QualityInspect>> inspectMap = new LinkedHashMap<>(); |
| | | Map<Long, List<QualityInspectParam>> inspectParamMap = new LinkedHashMap<>(); |
| | | Map<Long, List<QualityInspectFile>> inspectFileMap = new LinkedHashMap<>(); |
| | | if (!reportMainIdList.isEmpty()) { |
| | | List<ProductionProductOutput> reportOutputList = productionProductOutputMapper.selectList( |
| | | Wrappers.<ProductionProductOutput>lambdaQuery() |
| | | .in(ProductionProductOutput::getProductionProductMainId, reportMainIdList) |
| | | .orderByAsc(ProductionProductOutput::getId)); |
| | | for (ProductionProductOutput reportOutput : reportOutputList) { |
| | | if (reportOutput == null) { |
| | | continue; |
| | | } |
| | | Long reportMainId = reportOutput.getProductionProductMainId() != null |
| | | ? reportOutput.getProductionProductMainId() |
| | | : reportOutput.getProductMainId(); |
| | | if (reportMainId == null) { |
| | | continue; |
| | | } |
| | | reportOutputMap.computeIfAbsent(reportMainId, k -> new ArrayList<>()).add(reportOutput); |
| | | } |
| | | |
| | | List<ProductionOrderRoutingOperationParam> reportParamList = productionOrderRoutingOperationParamMapper.selectList( |
| | | Wrappers.<ProductionOrderRoutingOperationParam>lambdaQuery() |
| | | .in(ProductionOrderRoutingOperationParam::getProductionProductMainId, reportMainIdList) |
| | | .orderByAsc(ProductionOrderRoutingOperationParam::getId)); |
| | | for (ProductionOrderRoutingOperationParam reportParam : reportParamList) { |
| | | if (reportParam == null || reportParam.getProductionProductMainId() == null) { |
| | | continue; |
| | | } |
| | | reportParamMap.computeIfAbsent(reportParam.getProductionProductMainId(), k -> new ArrayList<>()).add(reportParam); |
| | | } |
| | | |
| | | List<QualityInspect> inspectList = qualityInspectMapper.selectList( |
| | | Wrappers.<QualityInspect>lambdaQuery() |
| | | .in(QualityInspect::getProductMainId, reportMainIdList) |
| | | .orderByAsc(QualityInspect::getId)); |
| | | for (QualityInspect inspect : inspectList) { |
| | | if (inspect == null || inspect.getProductMainId() == null) { |
| | | continue; |
| | | } |
| | | inspectMap.computeIfAbsent(inspect.getProductMainId(), k -> new ArrayList<>()).add(inspect); |
| | | } |
| | | |
| | | List<Long> inspectIdList = inspectList.stream() |
| | | .map(QualityInspect::getId) |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.toList()); |
| | | if (!inspectIdList.isEmpty()) { |
| | | List<QualityInspectParam> inspectParamList = qualityInspectParamMapper.selectList( |
| | | Wrappers.<QualityInspectParam>lambdaQuery() |
| | | .in(QualityInspectParam::getInspectId, inspectIdList) |
| | | .orderByAsc(QualityInspectParam::getId)); |
| | | for (QualityInspectParam inspectParam : inspectParamList) { |
| | | if (inspectParam == null || inspectParam.getInspectId() == null) { |
| | | continue; |
| | | } |
| | | inspectParamMap.computeIfAbsent(inspectParam.getInspectId(), k -> new ArrayList<>()).add(inspectParam); |
| | | } |
| | | |
| | | List<QualityInspectFile> inspectFileList = qualityInspectFileMapper.selectList( |
| | | Wrappers.<QualityInspectFile>lambdaQuery() |
| | | .in(QualityInspectFile::getInspectId, inspectIdList) |
| | | .orderByAsc(QualityInspectFile::getId)); |
| | | for (QualityInspectFile inspectFile : inspectFileList) { |
| | | if (inspectFile == null || inspectFile.getInspectId() == null) { |
| | | continue; |
| | | } |
| | | inspectFileMap.computeIfAbsent(inspectFile.getInspectId(), k -> new ArrayList<>()).add(inspectFile); |
| | | } |
| | | } |
| | | } |
| | | |
| | | List<ProductionOrderWorkOrderDetailVo.WorkOrderDetail> workOrderDetailList = new ArrayList<>(); |
| | | for (ProductionOperationTask workOrder : workOrderList) { |
| | | ProductionOrderWorkOrderDetailVo.WorkOrderDetail workOrderDetail = new ProductionOrderWorkOrderDetailVo.WorkOrderDetail(); |
| | | workOrderDetail.setWorkOrder(workOrder); |
| | | |
| | | List<ProductionProductMain> workOrderReportMainList = reportMainMap.get(workOrder.getId()); |
| | | if (workOrderReportMainList == null || workOrderReportMainList.isEmpty()) { |
| | | workOrderDetail.setReportList(Collections.emptyList()); |
| | | workOrderDetailList.add(workOrderDetail); |
| | | continue; |
| | | } |
| | | |
| | | List<ProductionOrderWorkOrderDetailVo.ReportDetail> reportDetailList = new ArrayList<>(); |
| | | for (ProductionProductMain reportMain : workOrderReportMainList) { |
| | | Long reportMainId = reportMain.getId(); |
| | | ProductionOrderWorkOrderDetailVo.ReportDetail reportDetail = new ProductionOrderWorkOrderDetailVo.ReportDetail(); |
| | | reportDetail.setReportMain(reportMain); |
| | | reportDetail.setReportOutputList(reportOutputMap.getOrDefault(reportMainId, Collections.emptyList())); |
| | | reportDetail.setReportParamList(reportParamMap.getOrDefault(reportMainId, Collections.emptyList())); |
| | | |
| | | List<QualityInspect> reportInspectList = inspectMap.get(reportMainId); |
| | | if (reportInspectList == null || reportInspectList.isEmpty()) { |
| | | reportDetail.setInspectList(Collections.emptyList()); |
| | | } else { |
| | | List<ProductionOrderWorkOrderDetailVo.InspectDetail> inspectDetailList = new ArrayList<>(); |
| | | for (QualityInspect inspect : reportInspectList) { |
| | | ProductionOrderWorkOrderDetailVo.InspectDetail inspectDetail = new ProductionOrderWorkOrderDetailVo.InspectDetail(); |
| | | inspectDetail.setInspect(inspect); |
| | | inspectDetail.setInspectParamList(inspectParamMap.getOrDefault(inspect.getId(), Collections.emptyList())); |
| | | inspectDetail.setInspectFileList(inspectFileMap.getOrDefault(inspect.getId(), Collections.emptyList())); |
| | | inspectDetailList.add(inspectDetail); |
| | | } |
| | | reportDetail.setInspectList(inspectDetailList); |
| | | } |
| | | reportDetailList.add(reportDetail); |
| | | } |
| | | workOrderDetail.setReportList(reportDetailList); |
| | | workOrderDetailList.add(workOrderDetail); |
| | | } |
| | | |
| | | detailVo.setWorkOrderList(workOrderDetailList); |
| | | return detailVo; |
| | | } |
| | | |
| | | @Override |
| | | public List<ProductionOrderPickVo> pick(Long productionOrderId) { |
| | | if (productionOrderId == null) { |
| | | return Collections.emptyList(); |
| | |
| | | } |
| | | return new ArrayList<>(mergedPickMap.values()); |
| | | } |
| | | |
| | | @Override |
| | | public int updateOrder(ProductionOrderDto productionOrderDto) { |
| | | productionOrderDto.setStatus(5); |
| | | return baseMapper.updateById(productionOrderDto); |
| | | } |
| | | } |
| | |
| | | import com.ruoyi.project.system.mapper.SysUserMapper; |
| | | import com.ruoyi.quality.mapper.*; |
| | | import com.ruoyi.quality.pojo.*; |
| | | import com.ruoyi.stock.dto.StockInventoryDto; |
| | | import com.ruoyi.stock.service.StockInventoryService; |
| | | import com.ruoyi.technology.mapper.TechnologyOperationMapper; |
| | | import com.ruoyi.technology.mapper.TechnologyRoutingOperationMapper; |
| | | import com.ruoyi.technology.pojo.TechnologyOperation; |
| | |
| | | private final TechnologyRoutingOperationMapper technologyRoutingOperationMapper; |
| | | private final TechnologyOperationMapper technologyOperationMapper; |
| | | private final StockUtils stockUtils; |
| | | private final StockInventoryService stockInventoryService; |
| | | |
| | | @Override |
| | | public IPage<ProductionProductMainDto> listPageProductionProductMainDto(Page page, ProductionProductMainDto productionProductMainDto) { |
| | |
| | | }); |
| | | } |
| | | } else { |
| | | stockUtils.addStock(productModel.getId(), productQty, |
| | | StockInQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_IN.getCode(), productionProductMain.getId()); |
| | | StockInventoryDto stockInventoryDto = new StockInventoryDto(); |
| | | stockInventoryDto.setRecordId(productionProductMain.getId()); |
| | | stockInventoryDto.setRecordType(String.valueOf(StockInQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_IN.getCode())); |
| | | stockInventoryDto.setQualitity(productQty); |
| | | stockInventoryDto.setProductModelId(productModel.getId()); |
| | | stockInventoryService.addStockInRecordOnly(stockInventoryDto); |
| | | } |
| | | |
| | | productionOperationTask.setCompleteQuantity(defaultDecimal(productionOperationTask.getCompleteQuantity()).add(productQty)); |
| | |
| | | productionAccount.setSchedulingDate(LocalDateTime.now()); |
| | | productionAccountMapper.insert(productionAccount); |
| | | } |
| | | if (defaultDecimal(dto.getScrapQty()).compareTo(BigDecimal.ZERO) > 0) { |
| | | stockUtils.addUnStock(productModel.getId(), dto.getScrapQty(), |
| | | StockInUnQualifiedRecordTypeEnum.PRODUCTION_SCRAP.getCode(), productionProductMain.getId()); |
| | | } |
| | | // if (defaultDecimal(dto.getScrapQty()).compareTo(BigDecimal.ZERO) > 0) { |
| | | // stockUtils.addUnStock(productModel.getId(), dto.getScrapQty(), |
| | | // StockInUnQualifiedRecordTypeEnum.PRODUCTION_SCRAP.getCode(), productionProductMain.getId()); |
| | | // } |
| | | return true; |
| | | } |
| | | |
| | |
| | | import io.swagger.v3.oas.annotations.tags.Tag;
|
| | | import jakarta.servlet.http.HttpServletResponse;
|
| | | import lombok.AllArgsConstructor;
|
| | | import org.slf4j.Logger;
|
| | | import org.slf4j.LoggerFactory;
|
| | | import org.springframework.core.io.FileSystemResource;
|
| | | import org.springframework.http.ContentDisposition;
|
| | | import org.springframework.http.MediaType;
|
| | |
| | | @RestController
|
| | | @RequestMapping("/common")
|
| | | public class CommonController {
|
| | | private static final Logger log = LoggerFactory.getLogger(CommonController.class);
|
| | |
|
| | |
|
| | | private final StorageBlobService storageBlobService;
|
| | | private final FileUtil fileUtil;
|
| | |
|
| | |
|
| | | // /**
|
| | | // * éç¨ä¸è½½è¯·æ±
|
| | | // *
|
| | | // * @param fileName æä»¶åç§°
|
| | | // * @param delete æ¯å¦å é¤
|
| | | // */
|
| | | // @GetMapping("/download")
|
| | | // public void fileDownload(String fileName, Boolean delete, HttpServletResponse response, HttpServletRequest request)
|
| | | // {
|
| | | // try
|
| | | // {
|
| | | // if (!FileUtils.checkAllowDownload(fileName))
|
| | | // {
|
| | | // throw new Exception(StringUtils.format("æä»¶åç§°({})éæ³ï¼ä¸å
许ä¸è½½ã ", fileName));
|
| | | // }
|
| | | // String realFileName = fileName.substring(fileName.indexOf("_") + 1);
|
| | | //
|
| | | // response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
|
| | | // FileUtils.setAttachmentResponseHeader(response, realFileName);
|
| | | // FileUtils.writeBytes(fileName, response.getOutputStream());
|
| | |
|
| | | /// / if (delete)
|
| | | /// / {
|
| | | /// / FileUtils.deleteFile(fileName);
|
| | | /// / }
|
| | | // }
|
| | | // catch (Exception e)
|
| | | // {
|
| | | // log.error("ä¸è½½æä»¶å¤±è´¥", e);
|
| | | // }
|
| | | // }
|
| | | //
|
| | | // /**
|
| | | // * éç¨ä¸ä¼ 请æ±ï¼å个ï¼
|
| | | // */
|
| | | // @PostMapping("/upload")
|
| | | // public AjaxResult uploadFile(MultipartFile file) throws Exception
|
| | | // {
|
| | | // try
|
| | | // {
|
| | | // // ä¸ä¼ æä»¶è·¯å¾
|
| | | // String filePath = RuoYiConfig.getUploadPath();
|
| | | // // ä¸ä¼ å¹¶è¿åæ°æä»¶åç§°
|
| | | // String fileName = FileUploadUtils.upload(filePath, file);
|
| | | // String url = serverConfig.getUrl() + fileName;
|
| | | // AjaxResult ajax = AjaxResult.success();
|
| | | // ajax.put("url", url);
|
| | | // ajax.put("fileName", fileName);
|
| | | // ajax.put("newFileName", FileUtils.getName(fileName));
|
| | | // ajax.put("originalFilename", file.getOriginalFilename());
|
| | | // return ajax;
|
| | | // }
|
| | | // catch (Exception e)
|
| | | // {
|
| | | // return AjaxResult.error(e.getMessage());
|
| | | // }
|
| | | // }
|
| | | //
|
| | | // /**
|
| | | // * æ¬å°èµæºéç¨ä¸è½½
|
| | | // */
|
| | | // @GetMapping("/download/resource")
|
| | | // public void resourceDownload(String resource, HttpServletRequest request, HttpServletResponse response)
|
| | | // throws Exception
|
| | | // {
|
| | | // try
|
| | | // {
|
| | | // if (!FileUtils.checkAllowDownload(resource))
|
| | | // {
|
| | | // throw new Exception(StringUtils.format("èµæºæä»¶({})éæ³ï¼ä¸å
许ä¸è½½ã ", resource));
|
| | | // }
|
| | | // // æ¬å°èµæºè·¯å¾
|
| | | // String localPath = RuoYiConfig.getProfile();
|
| | | // // æ°æ®åºèµæºå°å
|
| | | // String downloadPath = localPath + StringUtils.substringAfter(resource, Constants.RESOURCE_PREFIX);
|
| | | // // ä¸è½½åç§°
|
| | | // String downloadName = StringUtils.substringAfterLast(downloadPath, "/");
|
| | | // response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
|
| | | // FileUtils.setAttachmentResponseHeader(response, downloadName);
|
| | | // FileUtils.writeBytes(downloadPath, response.getOutputStream());
|
| | | // }
|
| | | // catch (Exception e)
|
| | | // {
|
| | | // log.error("ä¸è½½æä»¶å¤±è´¥", e);
|
| | | // }
|
| | | // }
|
| | | @PostMapping({"/upload"})
|
| | | @Operation(summary = "æä»¶ä¸ä¼ ")
|
| | | public R upload(@RequestParam("files") List<MultipartFile> files) {
|
| | |
| | | import com.ruoyi.purchase.pojo.InvoicePurchase; |
| | | import com.ruoyi.purchase.service.IInvoicePurchaseService; |
| | | import com.ruoyi.sales.service.ICommonFileService; |
| | | import jakarta.servlet.http.HttpServletResponse; |
| | | import lombok.AllArgsConstructor; |
| | | import org.springframework.web.bind.annotation.*; |
| | | import org.springframework.web.multipart.MultipartFile; |
| | | |
| | | import jakarta.servlet.http.HttpServletResponse; |
| | | import java.io.IOException; |
| | | import java.util.List; |
| | | |
| | |
| | | return toAjax(invoicePurchaseService.delInvoice(ids)); |
| | | } |
| | | |
| | | @PostMapping("/upload") |
| | | public AjaxResult uploadFile(MultipartFile file, Long id, Integer type) { |
| | | try { |
| | | return AjaxResult.success(commonFileService.uploadFile(file, id, type)); |
| | | } catch (Exception e) { |
| | | return AjaxResult.error(e.getMessage()); |
| | | } |
| | | } |
| | | } |
| | |
| | | import com.ruoyi.purchase.service.ITicketRegistrationService; |
| | | import com.ruoyi.purchase.service.impl.PaymentRegistrationServiceImpl; |
| | | import com.ruoyi.sales.service.ICommonFileService; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import jakarta.servlet.http.HttpServletResponse; |
| | | import lombok.AllArgsConstructor; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.security.core.parameters.P; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.util.CollectionUtils; |
| | | import org.springframework.web.bind.annotation.*; |
| | | import org.springframework.web.multipart.MultipartFile; |
| | | |
| | | import jakarta.servlet.http.HttpServletResponse; |
| | | import java.io.IOException; |
| | | import java.util.List; |
| | | |
| | |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public AjaxResult delRegistration(@RequestBody Long[] ids) { |
| | | return toAjax(ticketRegistrationService.delRegistration(ids)); |
| | | } |
| | | |
| | | @PostMapping("/upload") |
| | | public AjaxResult uploadFile(MultipartFile file, Long id, Integer type) { |
| | | try { |
| | | return AjaxResult.success(commonFileService.uploadFile(file, id, type)); |
| | | } catch (Exception e) { |
| | | return AjaxResult.error(e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | |
| | | |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.fasterxml.jackson.annotation.JsonFormat; |
| | | import com.ruoyi.basic.dto.StorageBlobDTO; |
| | | import com.ruoyi.basic.dto.StorageBlobVO; |
| | | import com.ruoyi.framework.aspectj.lang.annotation.Excel; |
| | | import com.ruoyi.sales.pojo.CommonFile; |
| | | import com.ruoyi.sales.pojo.SalesLedgerProduct; |
| | |
| | | private String templateName; |
| | | @Schema(description = "审æ¹äººid") |
| | | private Integer approverId; |
| | | |
| | | private List<StorageBlobVO> storageBlobVOS; |
| | | private List<StorageBlobDTO> storageBlobDTOS; |
| | | } |
| | |
| | | |
| | | private final CommonFileMapper commonFileMapper; |
| | | |
| | | private final TempFileMapper tempFileMapper; |
| | | |
| | | @Value("${file.upload-dir}") |
| | | private String uploadDir; |
| | | |
| | | @Override |
| | | public List<InvoicePurchaseDto> selectInvoicePurchaseList(InvoicePurchaseDto invoicePurchaseDto) { |
| | |
| | | import com.baomidou.mybatisplus.core.toolkit.ObjectUtils; |
| | | import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.ruoyi.approve.bean.vo.ApproveProcessVO; |
| | | import com.ruoyi.approve.pojo.ApproveProcess; |
| | | import com.ruoyi.approve.service.impl.ApproveProcessServiceImpl; |
| | | import com.ruoyi.approve.bean.vo.ApproveProcessVO; |
| | | import com.ruoyi.basic.enums.ApplicationTypeEnum; |
| | | import com.ruoyi.basic.enums.RecordTypeEnum; |
| | | import com.ruoyi.basic.mapper.ProductMapper; |
| | | import com.ruoyi.basic.mapper.ProductModelMapper; |
| | | import com.ruoyi.basic.mapper.SupplierManageMapper; |
| | | import com.ruoyi.basic.pojo.Product; |
| | | import com.ruoyi.basic.pojo.ProductModel; |
| | | import com.ruoyi.basic.pojo.SupplierManage; |
| | | import com.ruoyi.basic.utils.FileUtil; |
| | | import com.ruoyi.common.enums.FileNameType; |
| | | import com.ruoyi.common.exception.base.BaseException; |
| | | import com.ruoyi.common.utils.SecurityUtils; |
| | |
| | | import com.ruoyi.framework.security.LoginUser; |
| | | import com.ruoyi.framework.web.domain.AjaxResult; |
| | | import com.ruoyi.other.mapper.TempFileMapper; |
| | | import com.ruoyi.other.pojo.TempFile; |
| | | import com.ruoyi.procurementrecord.mapper.ProcurementRecordMapper; |
| | | import com.ruoyi.procurementrecord.pojo.ProcurementRecordStorage; |
| | | import com.ruoyi.project.system.domain.SysUser; |
| | |
| | | import com.ruoyi.sales.service.impl.CommonFileServiceImpl; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.apache.commons.io.FilenameUtils; |
| | | import org.springframework.beans.BeanUtils; |
| | | import org.springframework.beans.factory.annotation.Value; |
| | | import org.springframework.data.redis.core.StringRedisTemplate; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.web.multipart.MultipartFile; |
| | | |
| | | import java.io.IOException; |
| | | import java.io.InputStream; |
| | | import java.math.BigDecimal; |
| | | import java.math.RoundingMode; |
| | | import java.nio.file.Files; |
| | | import java.nio.file.Path; |
| | | import java.nio.file.Paths; |
| | | import java.nio.file.StandardCopyOption; |
| | | import java.time.LocalDate; |
| | | import java.time.LocalDateTime; |
| | | import java.time.ZoneId; |
| | |
| | | private final QualityInspectParamMapper qualityInspectParamMapper; |
| | | private final ApproveProcessServiceImpl approveProcessService; |
| | | private final ProcurementRecordMapper procurementRecordStorageMapper; |
| | | |
| | | @Value("${file.upload-dir}") |
| | | private String uploadDir; |
| | | private final FileUtil fileUtil; |
| | | |
| | | @Override |
| | | public List<PurchaseLedger> selectPurchaseLedgerList(PurchaseLedger purchaseLedger) { |
| | |
| | | } |
| | | purchaseLedgerMapper.updateById(purchaseLedger); |
| | | } |
| | | // 6.éè´å®¡æ ¸æ°å¢ |
| | | // 6.éè´å®¡æ ¸æ°å¢ï¼å®¡æ¹ç®¡çæªé
ç½®éè´å®¡æ¹äººæ¶ï¼å®¡æ¹æå¡ä¼èªå¨ç½®ä¸ºå®¡æ¹éè¿ã |
| | | addApproveByPurchase(loginUser, purchaseLedger); |
| | | |
| | | // 4. å¤çåè¡¨æ°æ® |
| | |
| | | // } |
| | | // } |
| | | // 5. è¿ç§»ä¸´æ¶æä»¶å°æ£å¼ç®å½ |
| | | if (purchaseLedgerDto.getTempFileIds() != null && !purchaseLedgerDto.getTempFileIds().isEmpty()) { |
| | | migrateTempFilesToFormal(purchaseLedger.getId(), purchaseLedgerDto.getTempFileIds()); |
| | | } |
| | | fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.PURCHASE_LEDGER, purchaseLedger.getId(), purchaseLedgerDto.getStorageBlobDTOS()); |
| | | return 1; |
| | | } |
| | | |
| | |
| | | if (products == null || products.isEmpty()) { |
| | | throw new BaseException("产åä¿¡æ¯ä¸åå¨"); |
| | | } |
| | | Integer ledgerType = type == null ? 2 : type; |
| | | |
| | | // æåæ¶éææéè¦æ¥è¯¢çID |
| | | Set<Long> productIds = products.stream() |
| | |
| | | // æ§è¡æ´æ°æä½ |
| | | if (!updateList.isEmpty()) { |
| | | for (SalesLedgerProduct product : updateList) { |
| | | product.setType(type); |
| | | product.setType(ledgerType); |
| | | salesLedgerProductMapper.updateById(product); |
| | | } |
| | | } |
| | | // æ§è¡æå
¥æä½ |
| | | if (!insertList.isEmpty()) { |
| | | for (SalesLedgerProduct salesLedgerProduct : insertList) { |
| | | salesLedgerProduct.setType(type); |
| | | salesLedgerProduct.setType(ledgerType); |
| | | Date entryDate = purchaseLedger.getEntryDate(); |
| | | |
| | | LocalDateTime localDateTime = entryDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(); |
| | |
| | | if (salesLedgerId != null) { |
| | | // ç´æ¥æ´æ°æå®IDçè®°å½çcontractAmountåæ®µä¸ºtotalTaxInclusiveAmount |
| | | purchaseLedgerMapper.updateContractAmountById(salesLedgerId, totalTaxInclusiveAmount); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * å°ä¸´æ¶æä»¶è¿ç§»å°æ£å¼ç®å½ |
| | | * |
| | | * @param businessId ä¸å¡IDï¼éå®å°è´¦IDï¼ |
| | | * @param tempFileIds ä¸´æ¶æä»¶IDå表 |
| | | * @throws IOException æä»¶æä½å¼å¸¸ |
| | | */ |
| | | private void migrateTempFilesToFormal(Long businessId, List<String> tempFileIds) throws IOException { |
| | | if (CollectionUtils.isEmpty(tempFileIds)) { |
| | | return; |
| | | } |
| | | |
| | | // æå»ºæ£å¼ç®å½è·¯å¾ï¼æä¸å¡ç±»å忥æåç»ï¼ |
| | | String formalDir = uploadDir + LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE); |
| | | |
| | | Path formalDirPath = Paths.get(formalDir); |
| | | |
| | | // ç¡®ä¿æ£å¼ç®å½åå¨ï¼éå½åå»ºï¼ |
| | | if (!Files.exists(formalDirPath)) { |
| | | Files.createDirectories(formalDirPath); |
| | | } |
| | | |
| | | for (String tempFileId : tempFileIds) { |
| | | // æ¥è¯¢ä¸´æ¶æä»¶è®°å½ |
| | | TempFile tempFile = tempFileMapper.selectById(tempFileId); |
| | | if (tempFile == null) { |
| | | log.warn("ä¸´æ¶æä»¶ä¸åå¨ï¼è·³è¿å¤ç: {}", tempFileId); |
| | | continue; |
| | | } |
| | | |
| | | // æå»ºæ£å¼æä»¶åï¼å
å«ä¸å¡IDåæ¶é´æ³ï¼é¿å
å²çªï¼ |
| | | String originalFilename = tempFile.getOriginalName(); |
| | | String fileExtension = FilenameUtils.getExtension(originalFilename); |
| | | String formalFilename = businessId + "_" + |
| | | System.currentTimeMillis() + "_" + |
| | | UUID.randomUUID().toString().substring(0, 8) + |
| | | (StringUtils.hasText(fileExtension) ? "." + fileExtension : ""); |
| | | |
| | | Path formalFilePath = formalDirPath.resolve(formalFilename); |
| | | |
| | | try { |
| | | // æ§è¡æä»¶è¿ç§»ï¼ä½¿ç¨ååæä½ç¡®ä¿å®å
¨æ§ï¼ |
| | | // Files.move( |
| | | // Paths.get(tempFile.getTempPath()), |
| | | // formalFilePath, |
| | | // StandardCopyOption.REPLACE_EXISTING, |
| | | // StandardCopyOption.ATOMIC_MOVE |
| | | // ); |
| | | // ååç§»å¨å¤±è´¥ï¼ä½¿ç¨å¤å¶+å é¤ |
| | | Files.copy(Paths.get(tempFile.getTempPath()), formalFilePath, StandardCopyOption.REPLACE_EXISTING); |
| | | Files.deleteIfExists(Paths.get(tempFile.getTempPath())); |
| | | log.info("æä»¶è¿ç§»æå: {} -> {}", tempFile.getTempPath(), formalFilePath); |
| | | |
| | | // æ´æ°æä»¶è®°å½ï¼å
³èå°ä¸å¡IDï¼ |
| | | CommonFile fileRecord = new CommonFile(); |
| | | fileRecord.setCommonId(businessId); |
| | | fileRecord.setName(originalFilename); |
| | | fileRecord.setUrl(formalFilePath.toString()); |
| | | fileRecord.setCreateTime(LocalDateTime.now()); |
| | | fileRecord.setType(FileNameType.PURCHASE.getValue()); |
| | | commonFileMapper.insert(fileRecord); |
| | | |
| | | // å é¤ä¸´æ¶æä»¶è®°å½ |
| | | tempFileMapper.deleteById(tempFile); |
| | | |
| | | log.info("æä»¶è¿ç§»æå: {} -> {}", tempFile.getTempPath(), formalFilePath); |
| | | } catch (IOException e) { |
| | | log.error("æä»¶è¿ç§»å¤±è´¥: {}", tempFile.getTempPath(), e); |
| | | // å¯éæ©åæ»äºå¡æè®°å½å¤±è´¥æä»¶ |
| | | throw new IOException("æä»¶è¿ç§»å¼å¸¸", e); |
| | | } |
| | | } |
| | | } |
| | | |
| | |
| | | .eq(SalesLedgerProduct::getType, purchaseLedgerDto.getType()); |
| | | List<SalesLedgerProduct> products = salesLedgerProductMapper.selectList(productWrapper); |
| | | |
| | | // 3.æ¥è¯¢ä¸ä¼ æä»¶ |
| | | LambdaQueryWrapper<CommonFile> salesLedgerFileWrapper = new LambdaQueryWrapper<>(); |
| | | salesLedgerFileWrapper.eq(CommonFile::getCommonId, purchaseLedger.getId()) |
| | | .eq(CommonFile::getType,FileNameType.PURCHASE.getValue()); |
| | | List<CommonFile> salesLedgerFiles = commonFileMapper.selectList(salesLedgerFileWrapper); |
| | | |
| | | // 4. è½¬æ¢ DTO |
| | | PurchaseLedgerDto resultDto = new PurchaseLedgerDto(); |
| | |
| | | if (!products.isEmpty()) { |
| | | resultDto.setHasChildren(true); |
| | | resultDto.setProductData(products); |
| | | resultDto.setSalesLedgerFiles(salesLedgerFiles); |
| | | resultDto.setStorageBlobVOS(fileUtil.getStorageBlobVOsByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum.FILE, RecordTypeEnum.PURCHASE_LEDGER, purchaseLedger.getId())); |
| | | } |
| | | return resultDto; |
| | | } |
| | |
| | | if(salesLedger1 != null){ |
| | | salesLedger.setSalesLedgerId(salesLedger1.getId()); |
| | | } |
| | | // éè´å®¡æ ¸ |
| | | // éè¿æµç§°è·åç¨æ·ID |
| | | String[] split = salesLedger.getApproveUserIds().split("ï¼"); |
| | | List<Long> ids = new ArrayList<>(); |
| | | for (int i = 0; i < split.length; i++) { |
| | | SysUser sysUser = sysUserMapper.selectOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getNickName, split[i]) |
| | | .last("LIMIT 1")); |
| | | if (sysUser != null) { |
| | | ids.add(sysUser.getUserId()); |
| | | if (StringUtils.hasText(salesLedger.getApproveUserIds())) { |
| | | // éè´å®¡æ ¸ï¼åå²å¯¼å
¥æ¨¡æ¿ä¼ 审æ¹äººå§åæ¶ï¼ç»§ç»å
¼å®¹è½¬æ¢ä¸ºç¨æ·IDã |
| | | String[] split = salesLedger.getApproveUserIds().split("ï¼"); |
| | | List<Long> ids = new ArrayList<>(); |
| | | for (int i = 0; i < split.length; i++) { |
| | | SysUser sysUser = sysUserMapper.selectOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getNickName, split[i]) |
| | | .last("LIMIT 1")); |
| | | if (sysUser != null) { |
| | | ids.add(sysUser.getUserId()); |
| | | } |
| | | } |
| | | // å°éå转为å符串ï¼éå¼ |
| | | String collect = ids.stream().map(Object::toString).collect(Collectors.joining(",")); |
| | | salesLedger.setApproveUserIds(collect); |
| | | } |
| | | // å°éå转为å符串ï¼éå¼ |
| | | String collect = ids.stream().map(Object::toString).collect(Collectors.joining(",")); |
| | | salesLedger.setApproveUserIds(collect); |
| | | purchaseLedgerMapper.insert(salesLedger); |
| | | |
| | | for (PurchaseLedgerProductImportDto salesLedgerProductImportDto : salesLedgerProductImportDtos) { |
| | |
| | | } |
| | | |
| | | public void addApproveByPurchase(LoginUser loginUser,PurchaseLedger purchaseLedger) throws Exception { |
| | | if (loginUser == null) { |
| | | return; |
| | | } |
| | | ApproveProcessVO approveProcessVO = new ApproveProcessVO(); |
| | | approveProcessVO.setApproveType(5); |
| | | approveProcessVO.setApproveDeptId(loginUser.getCurrentDeptId()); |
| | |
| | | import com.ruoyi.common.utils.StringUtils; |
| | | import com.ruoyi.common.utils.bean.BeanUtils; |
| | | import com.ruoyi.other.mapper.TempFileMapper; |
| | | import com.ruoyi.other.pojo.TempFile; |
| | | import com.ruoyi.purchase.dto.PaymentRegistrationDto; |
| | | import com.ruoyi.purchase.dto.PurchaseLedgerDto; |
| | | import com.ruoyi.purchase.dto.TicketRegistrationDto; |
| | |
| | | import com.ruoyi.sales.service.ISalesLedgerProductService; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.apache.commons.io.FilenameUtils; |
| | | import org.springframework.beans.factory.annotation.Value; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import java.io.IOException; |
| | | import java.math.BigDecimal; |
| | | import java.nio.file.Files; |
| | | import java.nio.file.Path; |
| | | import java.nio.file.Paths; |
| | | import java.nio.file.StandardCopyOption; |
| | | import java.time.LocalDate; |
| | | import java.time.LocalDateTime; |
| | | import java.time.format.DateTimeFormatter; |
| | | import java.util.*; |
| | | import java.util.Arrays; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Set; |
| | | import java.util.stream.Collectors; |
| | | |
| | | /** |
| | |
| | | private final ISalesLedgerProductService salesLedgerProductService; |
| | | |
| | | private final PaymentRegistrationMapper paymentRegistrationMapper; |
| | | |
| | | @Value("${file.upload-dir}") |
| | | private String uploadDir; |
| | | |
| | | |
| | | @Override |
| | |
| | | throw new RuntimeException("产åå¼ç¥¨æ°é½ä¸º0ï¼è¯·æ£æ¥"); |
| | | } |
| | | } |
| | | // è¿ç§»ä¸´æ¶æä»¶å°æ£å¼ç®å½ |
| | | if (ticketRegistrationDto.getTempFileIds() != null && !ticketRegistrationDto.getTempFileIds().isEmpty()) { |
| | | migrateTempFilesToFormal(ticketRegistration.getId(), ticketRegistrationDto.getTempFileIds()); |
| | | } |
| | | return rowsAffected; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * å°ä¸´æ¶æä»¶è¿ç§»å°æ£å¼ç®å½ |
| | | * |
| | | * @param businessId ä¸å¡IDï¼éå®å°è´¦IDï¼ |
| | | * @param tempFileIds ä¸´æ¶æä»¶IDå表 |
| | | * @throws IOException æä»¶æä½å¼å¸¸ |
| | | */ |
| | | public void migrateTempFilesToFormal(Long businessId, List<String> tempFileIds) throws IOException { |
| | | if (CollectionUtils.isEmpty(tempFileIds)) { |
| | | return; |
| | | } |
| | | |
| | | // æå»ºæ£å¼ç®å½è·¯å¾ï¼æä¸å¡ç±»å忥æåç»ï¼ |
| | | String formalDir = uploadDir + LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE); |
| | | |
| | | Path formalDirPath = Paths.get(formalDir); |
| | | |
| | | // ç¡®ä¿æ£å¼ç®å½åå¨ï¼éå½åå»ºï¼ |
| | | if (!Files.exists(formalDirPath)) { |
| | | Files.createDirectories(formalDirPath); |
| | | } |
| | | |
| | | for (String tempFileId : tempFileIds) { |
| | | // æ¥è¯¢ä¸´æ¶æä»¶è®°å½ |
| | | TempFile tempFile = tempFileMapper.selectById(tempFileId); |
| | | if (tempFile == null) { |
| | | log.warn("ä¸´æ¶æä»¶ä¸åå¨ï¼è·³è¿å¤ç: {}", tempFileId); |
| | | continue; |
| | | } |
| | | |
| | | // æå»ºæ£å¼æä»¶åï¼å
å«ä¸å¡IDåæ¶é´æ³ï¼é¿å
å²çªï¼ |
| | | String originalFilename = tempFile.getOriginalName(); |
| | | String fileExtension = FilenameUtils.getExtension(originalFilename); |
| | | String baseName = FilenameUtils.getBaseName(originalFilename); |
| | | String formalFilename = businessId + "_" + |
| | | System.currentTimeMillis() + "_" + |
| | | UUID.randomUUID().toString().substring(0, 8) +baseName+ |
| | | (com.ruoyi.common.utils.StringUtils.hasText(fileExtension) ? "." + fileExtension : ""); |
| | | |
| | | Path formalFilePath = formalDirPath.resolve(formalFilename); |
| | | |
| | | try { |
| | | |
| | | // ååç§»å¨å¤±è´¥ï¼ä½¿ç¨å¤å¶+å é¤ |
| | | Files.copy(Paths.get(tempFile.getTempPath()), formalFilePath, StandardCopyOption.REPLACE_EXISTING); |
| | | Files.deleteIfExists(Paths.get(tempFile.getTempPath())); |
| | | log.info("æä»¶è¿ç§»æå: {} -> {}", tempFile.getTempPath(), formalFilePath); |
| | | |
| | | // æ´æ°æä»¶è®°å½ï¼å
³èå°ä¸å¡IDï¼ |
| | | CommonFile fileRecord = new CommonFile(); |
| | | fileRecord.setCommonId(businessId); |
| | | fileRecord.setName(originalFilename); |
| | | fileRecord.setUrl(formalFilePath.toString()); |
| | | fileRecord.setCreateTime(LocalDateTime.now()); |
| | | fileRecord.setType(4); |
| | | commonFileMapper.insert(fileRecord); |
| | | |
| | | log.info("æä»¶è¿ç§»æå: {} -> {}", tempFile.getTempPath(), formalFilePath); |
| | | } catch (IOException e) { |
| | | log.error("æä»¶è¿ç§»å¤±è´¥: {}", tempFile.getTempPath(), e); |
| | | // å¯éæ©åæ»äºå¡æè®°å½å¤±è´¥æä»¶ |
| | | throw new IOException("æä»¶è¿ç§»å¼å¸¸", e); |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | |
| | | import com.deepoove.poi.XWPFTemplate; |
| | | import com.deepoove.poi.config.Configure; |
| | | import com.ruoyi.common.enums.StockInQualifiedRecordTypeEnum; |
| | | import com.ruoyi.common.enums.StockOutQualifiedRecordTypeEnum; |
| | | import com.ruoyi.common.utils.HackLoopTableRenderPolicy; |
| | | import com.ruoyi.common.utils.poi.ExcelUtil; |
| | | import com.ruoyi.procurementrecord.service.ProcurementRecordService; |
| | |
| | | import com.ruoyi.quality.service.IQualityInspectParamService; |
| | | import com.ruoyi.quality.service.IQualityInspectService; |
| | | import com.ruoyi.sales.mapper.SalesLedgerProductMapper; |
| | | import com.ruoyi.stock.dto.StockInventoryDto; |
| | | import com.ruoyi.stock.service.StockInventoryService; |
| | | import jakarta.servlet.http.HttpServletResponse; |
| | | import lombok.AllArgsConstructor; |
| | | import org.springframework.beans.BeanUtils; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import jakarta.servlet.http.HttpServletResponse; |
| | | import java.io.InputStream; |
| | | import java.io.OutputStream; |
| | | import java.net.URLEncoder; |
| | |
| | | public class QualityInspectServiceImpl extends ServiceImpl<QualityInspectMapper, QualityInspect> implements IQualityInspectService { |
| | | |
| | | private final StockUtils stockUtils; |
| | | private final StockInventoryService stockInventoryService; |
| | | private QualityInspectMapper qualityInspectMapper; |
| | | |
| | | private IQualityInspectParamService qualityInspectParamService; |
| | |
| | | qualityUnqualifiedMapper.insert(qualityUnqualified); |
| | | } else { |
| | | //åæ ¼ç´æ¥å
¥åº |
| | | stockUtils.addStock(qualityInspect.getProductModelId(), qualityInspect.getQuantity(), StockInQualifiedRecordTypeEnum.QUALITYINSPECT_STOCK_IN.getCode(), qualityInspect.getId()); |
| | | // stockUtils.addStock(qualityInspect.getProductModelId(), qualityInspect.getQuantity(), StockInQualifiedRecordTypeEnum.QUALITYINSPECT_STOCK_IN.getCode(), qualityInspect.getId()); |
| | | //ä»
æ·»å å
¥åºè®°å½ |
| | | StockInventoryDto stockInventoryDto = new StockInventoryDto(); |
| | | stockInventoryDto.setRecordType(String.valueOf(StockInQualifiedRecordTypeEnum.QUALITYINSPECT_STOCK_IN.getCode())); |
| | | stockInventoryDto.setRecordId(qualityInspect.getId()); |
| | | stockInventoryDto.setProductModelId(qualityInspect.getProductModelId()); |
| | | stockInventoryDto.setQualitity(qualityInspect.getQuantity()); |
| | | stockInventoryService.addStockInRecordOnly(stockInventoryDto); |
| | | } |
| | | qualityInspect.setInspectState(1);//å·²æäº¤ |
| | | return qualityInspectMapper.updateById(qualityInspect); |
| | |
| | | @RequiredArgsConstructor |
| | | public class SafeTrainingController { |
| | | |
| | | private SafeTrainingService safeTrainingService; |
| | | private SafeTrainingDetailsService safeTrainingDetailsService; |
| | | private final SafeTrainingService safeTrainingService; |
| | | private final SafeTrainingDetailsService safeTrainingDetailsService; |
| | | |
| | | @GetMapping("/page") |
| | | @Operation(summary = "å页æ¥è¯¢") |
| | |
| | | package com.ruoyi.sales.service; |
| | | |
| | | import com.ruoyi.sales.pojo.CommonFile; |
| | | import org.springframework.web.multipart.MultipartFile; |
| | | |
| | | import java.io.IOException; |
| | | |
| | | public interface ICommonFileService { |
| | | |
| | | int deleteSalesLedgerByIds(Long[] ids); |
| | | |
| | | CommonFile uploadFile(MultipartFile file, Long id, Integer type) throws IOException; |
| | | |
| | | int delCommonFileByIds(Long[] ids); |
| | | } |
| | |
| | | package com.ruoyi.sales.service.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.ruoyi.other.mapper.TempFileMapper; |
| | | import com.ruoyi.other.pojo.TempFile; |
| | | import com.ruoyi.sales.mapper.CommonFileMapper; |
| | | import com.ruoyi.sales.pojo.CommonFile; |
| | | import com.ruoyi.sales.service.ICommonFileService; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.apache.commons.io.FilenameUtils; |
| | | import org.springframework.beans.factory.annotation.Value; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.web.multipart.MultipartFile; |
| | | |
| | | import java.io.IOException; |
| | | import java.nio.file.Files; |
| | | import java.nio.file.Path; |
| | | import java.nio.file.Paths; |
| | | import java.nio.file.StandardCopyOption; |
| | | import java.time.LocalDate; |
| | | import java.time.LocalDateTime; |
| | | import java.time.format.DateTimeFormatter; |
| | | import java.util.Arrays; |
| | | import java.util.List; |
| | | import java.util.UUID; |
| | | |
| | | @Service |
| | | @RequiredArgsConstructor |
| | |
| | | private final CommonFileMapper commonFileMapper; |
| | | |
| | | private final TempFileMapper tempFileMapper; |
| | | |
| | | @Value("${file.upload-dir}") |
| | | private String uploadDir; |
| | | |
| | | public List<CommonFile> getFileListByBusinessId(Long businessId,Integer type) { |
| | | return commonFileMapper.selectList(new LambdaQueryWrapper<CommonFile>().eq(CommonFile::getCommonId, businessId) |
| | |
| | | return commonFileMapper.deleteBatchIds(Arrays.asList(ids)); |
| | | } |
| | | |
| | | @Override |
| | | public CommonFile uploadFile(MultipartFile file, Long id, Integer type) throws IOException { |
| | | // 1. çææ£å¼æä»¶IDåè·¯å¾ |
| | | String tempId = UUID.randomUUID().toString(); |
| | | Path tempFilePath = Paths.get(uploadDir, tempId + "_" + file.getOriginalFilename()); |
| | | |
| | | // 2. ç¡®ä¿ç®å½åå¨ |
| | | Path parentDir = tempFilePath.getParent(); |
| | | if (parentDir != null) { |
| | | Files.createDirectories(parentDir); // éå½å建ç®å½ |
| | | } |
| | | |
| | | // 3. ä¿åæä»¶å°ç®å½ |
| | | file.transferTo(tempFilePath.toFile()); |
| | | |
| | | // 4. ä¿åæä»¶è®°å½ |
| | | CommonFile commonFile = new CommonFile(); |
| | | commonFile.setCommonId(id); |
| | | commonFile.setName(file.getOriginalFilename()); |
| | | commonFile.setUrl(tempFilePath.toString()); |
| | | commonFile.setType(type); |
| | | commonFileMapper.insert(commonFile); |
| | | return commonFile; |
| | | } |
| | | |
| | | @Override |
| | | public int delCommonFileByIds(Long[] ids) { |
| | | return commonFileMapper.deleteBatchIds(Arrays.asList(ids)); |
| | | } |
| | | |
| | | /** |
| | | * å°ä¸´æ¶æä»¶è¿ç§»å°æ£å¼ç®å½ |
| | | * |
| | | * @param businessId ä¸å¡IDï¼éå®å°è´¦IDï¼ |
| | | * @param tempFileIds ä¸´æ¶æä»¶IDå表 |
| | | * @throws IOException æä»¶æä½å¼å¸¸ |
| | | */ |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void migrateTempFilesToFormal(Long businessId, List<String> tempFileIds) throws IOException { |
| | | if (CollectionUtils.isEmpty(tempFileIds)) { |
| | | return; |
| | | } |
| | | |
| | | // æå»ºæ£å¼ç®å½è·¯å¾ï¼æä¸å¡ç±»å忥æåç»ï¼ |
| | | String formalDir = uploadDir + LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE); |
| | | |
| | | Path formalDirPath = Paths.get(formalDir); |
| | | |
| | | // ç¡®ä¿æ£å¼ç®å½åå¨ï¼éå½åå»ºï¼ |
| | | if (!Files.exists(formalDirPath)) { |
| | | Files.createDirectories(formalDirPath); |
| | | } |
| | | |
| | | for (String tempFileId : tempFileIds) { |
| | | // æ¥è¯¢ä¸´æ¶æä»¶è®°å½ |
| | | TempFile tempFile = tempFileMapper.selectById(tempFileId); |
| | | if (tempFile == null) { |
| | | log.warn("ä¸´æ¶æä»¶ä¸åå¨ï¼è·³è¿å¤ç: {}", tempFileId); |
| | | continue; |
| | | } |
| | | |
| | | // æå»ºæ£å¼æä»¶åï¼å
å«ä¸å¡IDåæ¶é´æ³ï¼é¿å
å²çªï¼ |
| | | String originalFilename = tempFile.getOriginalName(); |
| | | String fileExtension = FilenameUtils.getExtension(originalFilename); |
| | | String formalFilename = businessId + "_" + |
| | | System.currentTimeMillis() + "_" + |
| | | UUID.randomUUID().toString().substring(0, 8) + |
| | | (com.ruoyi.common.utils.StringUtils.hasText(fileExtension) ? "." + fileExtension : ""); |
| | | |
| | | Path formalFilePath = formalDirPath.resolve(formalFilename); |
| | | |
| | | try { |
| | | // æ§è¡æä»¶è¿ç§»ï¼ä½¿ç¨ååæä½ç¡®ä¿å®å
¨æ§ï¼ |
| | | // Files.move( |
| | | // Paths.get(tempFile.getTempPath()), |
| | | // formalFilePath, |
| | | // StandardCopyOption.REPLACE_EXISTING, |
| | | // StandardCopyOption.ATOMIC_MOVE |
| | | // ); |
| | | // ååç§»å¨å¤±è´¥ï¼ä½¿ç¨å¤å¶+å é¤ |
| | | Files.copy(Paths.get(tempFile.getTempPath()), formalFilePath, StandardCopyOption.REPLACE_EXISTING); |
| | | Files.deleteIfExists(Paths.get(tempFile.getTempPath())); |
| | | log.info("æä»¶è¿ç§»æå: {} -> {}", tempFile.getTempPath(), formalFilePath); |
| | | |
| | | // æ´æ°æä»¶è®°å½ï¼å
³èå°ä¸å¡IDï¼ |
| | | CommonFile fileRecord = new CommonFile(); |
| | | fileRecord.setCommonId(businessId); |
| | | fileRecord.setName(originalFilename); |
| | | fileRecord.setUrl(formalFilePath.toString()); |
| | | fileRecord.setCreateTime(LocalDateTime.now()); |
| | | fileRecord.setType(tempFile.getType()); |
| | | commonFileMapper.insert(fileRecord); |
| | | |
| | | log.info("æä»¶è¿ç§»æå: {} -> {}", tempFile.getTempPath(), formalFilePath); |
| | | } catch (IOException e) { |
| | | log.error("æä»¶è¿ç§»å¤±è´¥: {}", tempFile.getTempPath(), e); |
| | | // å¯éæ©åæ»äºå¡æè®°å½å¤±è´¥æä»¶ |
| | | throw new IOException("æä»¶è¿ç§»å¼å¸¸", e); |
| | | } |
| | | } |
| | | } |
| | | } |
| | |
| | | import com.ruoyi.framework.security.LoginUser; |
| | | import com.ruoyi.framework.web.domain.AjaxResult; |
| | | import com.ruoyi.other.mapper.TempFileMapper; |
| | | import com.ruoyi.other.pojo.TempFile; |
| | | import com.ruoyi.production.mapper.ProductionProductInputMapper; |
| | | import com.ruoyi.production.mapper.ProductionProductMainMapper; |
| | | import com.ruoyi.production.mapper.ProductionProductOutputMapper; |
| | |
| | | import com.ruoyi.sales.vo.SalesLedgerVo; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.apache.commons.io.FilenameUtils; |
| | | import org.jetbrains.annotations.Nullable; |
| | | import org.springframework.beans.BeanUtils; |
| | | import org.springframework.beans.factory.annotation.Value; |
| | |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.web.multipart.MultipartFile; |
| | | |
| | | import java.io.IOException; |
| | | import java.io.InputStream; |
| | | import java.lang.reflect.Field; |
| | | import java.math.BigDecimal; |
| | | import java.math.RoundingMode; |
| | | import java.nio.file.Files; |
| | | import java.nio.file.Path; |
| | | import java.nio.file.Paths; |
| | | import java.nio.file.StandardCopyOption; |
| | | import java.time.LocalDate; |
| | | import java.time.LocalDateTime; |
| | | import java.time.YearMonth; |
| | |
| | | private static final String LOCK_PREFIX = "contract_no_lock:"; |
| | | private static final long LOCK_WAIT_TIMEOUT = 10; // éçå¾
è¶
æ¶æ¶é´ï¼ç§ï¼ |
| | | private static final long LOCK_EXPIRE_TIME = 30; // éèªå¨è¿ææ¶é´ï¼ç§ï¼ |
| | | private final AccountIncomeService accountIncomeService; |
| | | private final SalesLedgerMapper salesLedgerMapper; |
| | | private final CustomerMapper customerMapper; |
| | | private final SalesLedgerProductMapper salesLedgerProductMapper; |
| | | private final SalesLedgerProductServiceImpl salesLedgerProductServiceImpl; |
| | | private final CommonFileMapper commonFileMapper; |
| | | private final TempFileMapper tempFileMapper; |
| | | private final ReceiptPaymentMapper receiptPaymentMapper; |
| | | private final ShippingInfoServiceImpl shippingInfoServiceImpl; |
| | | private final CommonFileServiceImpl commonFileService; |
| | |
| | | private final InvoiceLedgerMapper invoiceLedgerMapper; |
| | | private final InvoiceRegistrationProductMapper invoiceRegistrationProductMapper; |
| | | private final InvoiceRegistrationMapper invoiceRegistrationMapper; |
| | | private final ProductionProductMainMapper productionProductMainMapper; |
| | | private final ProductionProductOutputMapper productionProductOutputMapper; |
| | | private final ProductionProductInputMapper productionProductInputMapper; |
| | | private final QualityInspectMapper qualityInspectMapper; |
| | | private final ProductModelMapper productModelMapper; |
| | | private final RedisTemplate<String, String> redisTemplate; |
| | | private final SysDeptMapper sysDeptMapper; |
| | | @Value("${file.upload-dir}") |
| | | private String uploadDir; |
| | | private final ProductionProductMainService productionProductMainService; |
| | | private final PurchaseReturnOrderProductsMapper purchaseReturnOrderProductsMapper; |
| | | private final SysUserMapper sysUserMapper; |
| | |
| | | fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.SALES_LEDGER, salesLedger.getId(), salesLedgerDto.getStorageBlobDTOs()); |
| | | } |
| | | return 1; |
| | | } |
| | | |
| | | /** |
| | | * å°ä¸´æ¶æä»¶è¿ç§»å°æ£å¼ç®å½ |
| | | * |
| | | * @param businessId ä¸å¡IDï¼éå®å°è´¦IDï¼ |
| | | * @param tempFileIds ä¸´æ¶æä»¶IDå表 |
| | | * @throws IOException æä»¶æä½å¼å¸¸ |
| | | */ |
| | | private void migrateTempFilesToFormal(Long businessId, List<String> tempFileIds) throws IOException { |
| | | if (CollectionUtils.isEmpty(tempFileIds)) { |
| | | return; |
| | | } |
| | | |
| | | // æå»ºæ£å¼ç®å½è·¯å¾ï¼æä¸å¡ç±»å忥æåç»ï¼ |
| | | String formalDir = uploadDir + LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE); |
| | | |
| | | Path formalDirPath = Paths.get(formalDir); |
| | | |
| | | // ç¡®ä¿æ£å¼ç®å½åå¨ï¼éå½åå»ºï¼ |
| | | if (!Files.exists(formalDirPath)) { |
| | | Files.createDirectories(formalDirPath); |
| | | } |
| | | |
| | | for (String tempFileId : tempFileIds) { |
| | | // æ¥è¯¢ä¸´æ¶æä»¶è®°å½ |
| | | TempFile tempFile = tempFileMapper.selectById(tempFileId); |
| | | if (tempFile == null) { |
| | | log.warn("ä¸´æ¶æä»¶ä¸åå¨ï¼è·³è¿å¤ç: {}", tempFileId); |
| | | continue; |
| | | } |
| | | |
| | | // æå»ºæ£å¼æä»¶åï¼å
å«ä¸å¡IDåæ¶é´æ³ï¼é¿å
å²çªï¼ |
| | | String originalFilename = tempFile.getOriginalName(); |
| | | String fileExtension = FilenameUtils.getExtension(originalFilename); |
| | | String formalFilename = businessId + "_" + |
| | | System.currentTimeMillis() + "_" + |
| | | UUID.randomUUID().toString().substring(0, 8) + |
| | | (StringUtils.hasText(fileExtension) ? "." + fileExtension : ""); |
| | | |
| | | Path formalFilePath = formalDirPath.resolve(formalFilename); |
| | | |
| | | try { |
| | | // æ§è¡æä»¶è¿ç§»ï¼ä½¿ç¨ååæä½ç¡®ä¿å®å
¨æ§ï¼ |
| | | // Files.move( |
| | | // Paths.get(tempFile.getTempPath()), |
| | | // formalFilePath, |
| | | // StandardCopyOption.REPLACE_EXISTING, |
| | | // StandardCopyOption.ATOMIC_MOVE |
| | | // ); |
| | | // ååç§»å¨å¤±è´¥ï¼ä½¿ç¨å¤å¶+å é¤ |
| | | Files.copy(Paths.get(tempFile.getTempPath()), formalFilePath, StandardCopyOption.REPLACE_EXISTING); |
| | | Files.deleteIfExists(Paths.get(tempFile.getTempPath())); |
| | | log.info("æä»¶è¿ç§»æå: {} -> {}", tempFile.getTempPath(), formalFilePath); |
| | | |
| | | // æ´æ°æä»¶è®°å½ï¼å
³èå°ä¸å¡IDï¼ |
| | | CommonFile fileRecord = new CommonFile(); |
| | | fileRecord.setCommonId(businessId); |
| | | fileRecord.setName(originalFilename); |
| | | fileRecord.setUrl(formalFilePath.toString()); |
| | | fileRecord.setCreateTime(LocalDateTime.now()); |
| | | //éå® |
| | | fileRecord.setType(FileNameType.SALE.getValue()); |
| | | commonFileMapper.insert(fileRecord); |
| | | |
| | | // å é¤ä¸´æ¶æä»¶è®°å½ |
| | | tempFileMapper.deleteById(tempFile); |
| | | |
| | | log.info("æä»¶è¿ç§»æå: {} -> {}", tempFile.getTempPath(), formalFilePath); |
| | | } catch (IOException e) { |
| | | log.error("æä»¶è¿ç§»å¤±è´¥: {}", tempFile.getTempPath(), e); |
| | | // å¯éæ©åæ»äºå¡æè®°å½å¤±è´¥æä»¶ |
| | | throw new IOException("æä»¶è¿ç§»å¼å¸¸", e); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // æä»¶è¿ç§»æ¹æ³ |
| | |
| | | import com.ruoyi.basic.utils.FileUtil; |
| | | import com.ruoyi.common.enums.FileNameType; |
| | | import com.ruoyi.common.enums.StockOutQualifiedRecordTypeEnum; |
| | | import com.ruoyi.other.service.impl.TempFileServiceImpl; |
| | | import com.ruoyi.procurementrecord.utils.StockUtils; |
| | | import com.ruoyi.sales.dto.SalesLedgerProductDto; |
| | | import com.ruoyi.sales.dto.ShippingInfoDto; |
| | |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.apache.commons.collections4.CollectionUtils; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.util.Collections; |
| | |
| | | |
| | | private final ShippingInfoMapper shippingInfoMapper; |
| | | |
| | | private final TempFileServiceImpl tempFileService; |
| | | |
| | | private final SalesLedgerProductMapper salesLedgerProductMapper; |
| | | |
| | |
| | | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.core.toolkit.ObjectUtils; |
| | | import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.ruoyi.basic.mapper.ProductModelMapper; |
| | | import com.ruoyi.basic.pojo.ProductModel; |
| | | import com.ruoyi.common.enums.StockInQualifiedRecordTypeEnum; |
| | | import com.ruoyi.common.enums.StockInUnQualifiedRecordTypeEnum; |
| | | import com.ruoyi.common.exception.ServiceException; |
| | | import com.ruoyi.common.exception.base.BaseException; |
| | | import com.ruoyi.common.utils.StringUtils; |
| | | import com.ruoyi.common.utils.poi.ExcelUtil; |
| | | import com.ruoyi.framework.web.domain.R; |
| | |
| | | import com.ruoyi.stock.dto.StockUninventoryDto; |
| | | import com.ruoyi.stock.execl.StockInventoryExportData; |
| | | import com.ruoyi.stock.mapper.StockInventoryMapper; |
| | | import com.ruoyi.stock.pojo.StockInRecord; |
| | | import com.ruoyi.stock.pojo.StockInventory; |
| | | import com.ruoyi.stock.service.StockInRecordService; |
| | | import com.ruoyi.stock.service.StockInventoryService; |
| | | import com.ruoyi.stock.service.StockOutRecordService; |
| | | import com.ruoyi.stock.service.StockUninventoryService; |
| | | import jakarta.servlet.http.HttpServletResponse; |
| | | import lombok.AllArgsConstructor; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.web.multipart.MultipartFile; |
| | | |
| | | import jakarta.servlet.http.HttpServletResponse; |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDate; |
| | | import java.time.format.DateTimeFormatter; |
| | | import java.util.ArrayList; |
| | | import java.util.HashMap; |
| | | import java.util.List; |
| | |
| | | private StockOutRecordService stockOutRecordService; |
| | | private StockUninventoryService stockUninventoryService; |
| | | private SalesLedgerProductMapper salesLedgerProductMapper; |
| | | private ProductModelMapper productModelMapper; |
| | | @Override |
| | | public IPage<StockInventoryDto> pagestockInventory(Page page, StockInventoryDto stockInventoryDto) { |
| | | return stockInventoryMapper.pagestockInventory(page, stockInventoryDto); |
| | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Boolean addstockInventory(StockInventoryDto stockInventoryDto) { |
| | | String batchNo = StringUtils.trim(stockInventoryDto.getBatchNo()); |
| | | if (StringUtils.isEmpty(batchNo)) { |
| | | batchNo = generateAutoBatchNo(stockInventoryDto.getProductModelId()); |
| | | } |
| | | stockInventoryDto.setBatchNo(batchNo); |
| | | |
| | | LambdaQueryWrapper<StockInventory> eq = new QueryWrapper<StockInventory>().lambda() |
| | | .eq(StockInventory::getProductModelId, stockInventoryDto.getProductModelId()); |
| | | if (StringUtils.isEmpty(stockInventoryDto.getBatchNo())) { |
| | | eq.isNull(StockInventory::getBatchNo); |
| | | stockInventoryDto.setBatchNo(null); |
| | | } else { |
| | | eq.eq(StockInventory::getBatchNo, stockInventoryDto.getBatchNo()); |
| | | } |
| | | eq.eq(StockInventory::getBatchNo, stockInventoryDto.getBatchNo()); |
| | | //æ°å¢å
¥åºè®°å½åæ·»å åºå |
| | | StockInRecordDto stockInRecordDto = new StockInRecordDto(); |
| | | stockInRecordDto.setRecordId(stockInventoryDto.getRecordId()); |
| | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Boolean addStockInRecordOnly(StockInventoryDto stockInventoryDto) { |
| | | String batchNo = StringUtils.trim(stockInventoryDto.getBatchNo()); |
| | | if (StringUtils.isEmpty(batchNo)) { |
| | | batchNo = generateAutoBatchNo(stockInventoryDto.getProductModelId()); |
| | | } |
| | | stockInventoryDto.setBatchNo(batchNo); |
| | | |
| | | StockInRecordDto stockInRecordDto = new StockInRecordDto(); |
| | | stockInRecordDto.setRecordId(stockInventoryDto.getRecordId()); |
| | | stockInRecordDto.setRecordType(stockInventoryDto.getRecordType()); |
| | | stockInRecordDto.setStockInNum(stockInventoryDto.getQualitity()); |
| | | stockInRecordDto.setBatchNo(stockInventoryDto.getBatchNo()); |
| | | stockInRecordDto.setBatchNo(batchNo); |
| | | stockInRecordDto.setProductModelId(stockInventoryDto.getProductModelId()); |
| | | stockInRecordDto.setType("0"); |
| | | stockInRecordDto.setRemark(stockInventoryDto.getRemark()); |
| | |
| | | return true; |
| | | } |
| | | |
| | | //è§åçæï¼20260424-产åç¼å·-001 |
| | | private String generateAutoBatchNo(Long productModelId) { |
| | | if (productModelId == null) { |
| | | throw new ServiceException("产åè§æ ¼IDä¸è½ä¸ºç©º"); |
| | | } |
| | | ProductModel productModel = productModelMapper.selectById(productModelId); |
| | | if (productModel == null) { |
| | | throw new ServiceException("产åè§æ ¼ä¸åå¨ï¼ID=" + productModelId); |
| | | } |
| | | String productCode = StringUtils.trim(productModel.getProductCode()); |
| | | if (StringUtils.isEmpty(productCode)) { |
| | | throw new ServiceException("产åè§æ ¼æªç»´æ¤äº§åç¼ç ï¼ID=" + productModelId); |
| | | } |
| | | |
| | | String dateText = LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE); |
| | | String prefix = dateText + "-" + productCode + "-"; |
| | | int maxSequence = resolveMaxSequence(prefix); |
| | | int sequence = maxSequence + 1; |
| | | while (sequence < 1_000_000) { |
| | | String batchNo = prefix + String.format("%03d", sequence); |
| | | if (!isBatchNoExists(batchNo)) { |
| | | return batchNo; |
| | | } |
| | | sequence++; |
| | | } |
| | | throw new ServiceException("æ¹å·åºå·è¶
åºèå´ï¼è¯·æ£æ¥æ¹å·æ°æ®"); |
| | | } |
| | | |
| | | private int resolveMaxSequence(String prefix) { |
| | | int maxSequence = 0; |
| | | List<StockInventory> stockInventoryList = stockInventoryMapper.selectList( |
| | | Wrappers.<StockInventory>lambdaQuery() |
| | | .select(StockInventory::getBatchNo) |
| | | .likeRight(StockInventory::getBatchNo, prefix)); |
| | | for (StockInventory stockInventory : stockInventoryList) { |
| | | maxSequence = Math.max(maxSequence, parseSequence(stockInventory.getBatchNo(), prefix)); |
| | | } |
| | | |
| | | List<StockInRecord> stockInRecordList = stockInRecordService.list( |
| | | Wrappers.<StockInRecord>lambdaQuery() |
| | | .select(StockInRecord::getBatchNo) |
| | | .likeRight(StockInRecord::getBatchNo, prefix)); |
| | | for (StockInRecord stockInRecord : stockInRecordList) { |
| | | maxSequence = Math.max(maxSequence, parseSequence(stockInRecord.getBatchNo(), prefix)); |
| | | } |
| | | return maxSequence; |
| | | } |
| | | |
| | | private int parseSequence(String batchNo, String prefix) { |
| | | if (StringUtils.isEmpty(batchNo) || StringUtils.isEmpty(prefix) || !batchNo.startsWith(prefix)) { |
| | | return 0; |
| | | } |
| | | String sequenceText = batchNo.substring(prefix.length()); |
| | | if (StringUtils.isEmpty(sequenceText) || !sequenceText.matches("\\d+")) { |
| | | return 0; |
| | | } |
| | | try { |
| | | return Integer.parseInt(sequenceText); |
| | | } catch (NumberFormatException ignored) { |
| | | return 0; |
| | | } |
| | | } |
| | | |
| | | private boolean isBatchNoExists(String batchNo) { |
| | | if (StringUtils.isEmpty(batchNo)) { |
| | | return false; |
| | | } |
| | | Long inventoryCount = stockInventoryMapper.selectCount( |
| | | Wrappers.<StockInventory>lambdaQuery().eq(StockInventory::getBatchNo, batchNo)); |
| | | if (inventoryCount != null && inventoryCount > 0) { |
| | | return true; |
| | | } |
| | | return stockInRecordService.count( |
| | | Wrappers.<StockInRecord>lambdaQuery().eq(StockInRecord::getBatchNo, batchNo)) > 0; |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Boolean addStockOutRecordOnly(StockInventoryDto stockInventoryDto) { |
| | |
| | | /** |
| | | * ä¿®æ¹å·¥èºè·¯çº¿ã |
| | | */ |
| | | @PutMapping("editTechRoute") |
| | | @PutMapping("/editTechRoute") |
| | | @Operation(summary = "ä¿®æ¹å·¥èºè·¯çº¿") |
| | | public R edit(@RequestBody TechnologyRouting technologyRouting) { |
| | | return R.ok(technologyRoutingService.updateTechnologyRouting(technologyRouting)); |
| | |
| | | return R.ok(technologyRoutingService.removeTechnologyRouting(ids)); |
| | | } |
| | | |
| | | //TODO å¢å å·¥èºè·¯çº¿éä»¶ä¸ä¼ @éæµ·æ° |
| | | } |
| | |
| | | Wrappers.<TechnologyBomStructure>lambdaQuery() |
| | | .eq(TechnologyBomStructure::getBomId, technologyRouting.getBomId()) |
| | | .isNotNull(TechnologyBomStructure::getOperationId) |
| | | .orderByAsc(TechnologyBomStructure::getId) |
| | | .orderByDesc(TechnologyBomStructure::getId) |
| | | ); |
| | | if (bomStructures.isEmpty()) { |
| | | throw new ServiceException("bom产åç»æä¸ºç©ºï¼"); |
| | |
| | | |
| | | # æä»¶ä¸ä¼ é
ç½® |
| | | file: |
| | | temp-dir: D:/ruoyi/temp/uploads # 临æ¶ç®å½ |
| | | upload-dir: D:/ruoyi/prod/uploads # æ£å¼ç®å½ |
| | | temp-dir: D:/ruoyi/temp/uploads # 临æ¶ç®å½ åæå é¤ |
| | | upload-dir: D:/ruoyi/prod/uploads # æ£å¼ç®å½ åæå é¤ |
| | | path: C:/Users/12631/Desktop/download/uploads # ä¸ä¼ ç®å½ |
| | | urlPrefix: /common # 龿¥åç¼ |
| | | domain: http://127.0.0.1:7003 # åååç¼ |
| | |
| | | ä½ æ¯ä¸ä¸ªå®¡æ¹å¾
å婿ï¼è´è´£å®¡æ¹å¾
åçæ¥è¯¢ãå®¡æ ¸ãåæ¶å®¡æ ¸ãä¿®æ¹ãå é¤åç»è®¡åæã |
| | | ä½ æ¯ä¸ä¸ªå®¡æ¹å¾
å婿ï¼è´è´£åååå
¬å®¡æ¹å¾
åçæ¥è¯¢ãå®¡æ ¸ãåæ¶å®¡æ ¸ãä¿®æ¹ãå é¤åç»è®¡åæã |
| | | |
| | | å·¥ä½è¦æ±ï¼ |
| | | 1. ç¨æ·é®å¾
åå表ã审æ¹è¿åº¦ã审æ¹è¯¦æ
ãç»è®¡æ°æ®æ¶ï¼ä¼å
è°ç¨å·¥å
·ï¼ä¸è¦èé æ°æ®ã |
| | |
| | | 3. å®¡æ ¸å¨ä½éï¼`approve` 表示éè¿ï¼`reject` 表示驳åã |
| | | 4. ä¿®æ¹å®¡æ¹åæ¶ï¼å¦æç¨æ·æ²¡ææç¡®è¦ä¿®æ¹åªäºå段ï¼è¦å
追é®ç¼ºå¤±å段ï¼ä¸è¦çã |
| | | 5. å é¤ãå®¡æ ¸ãåæ¶å®¡æ ¸è¿ç±»å¨ä½å±äºç¶æåæ´ï¼æ§è¡åè¦æç¡®åé¦ç»æã |
| | | 6. é¤âæ¥è¯¢å®¡æ¹å¾
å详æ
âå¤ï¼å
¶ä»å·¥å
·é»è®¤è¿å JSONã |
| | | 7. 对äºè¿äº JSON å·¥å
·ï¼ä½ å¿
é¡»ç´æ¥è¾åºåå§ JSON å符串æ¬èº«ï¼ä¸è¦æ¹åï¼ä¸è¦é¢å¤è§£éï¼ä¸è¦å
裹 Markdown 代ç åï¼ä¸è¦å¨ JSON ååå 任使åã |
| | | 8. åªæâæ¥è¯¢å®¡æ¹å¾
å详æ
âè¿ä¸ªå·¥å
·å
许è¾åºèªç¶è¯è¨ææ¬ã |
| | | 9. 妿工å
·è¿åçæ¯ç»è®¡ JSONï¼ä¹åæ ·ç´æ¥è¾åºåå§ JSONï¼å
¶ä¸ `description`ã`summary`ã`charts` å·²ç»ä¾å端使ç¨ã |
| | | 10. åç使ç¨ä¸æï¼ä½å¨ JSON åºæ¯ä¸ï¼æç»è¾åºå¿
é¡»æ¯åæ³ JSON æ¬ä½ã |
| | | 6. ç¨æ·è¯´âåæ®ââæµç¨ââå®¡æ¹æ¹ââå¾
åâï¼é½æå®¡æ¹å¾
åçè§£ï¼ç¨æ·è¯´âå¡å¨åªä¸ªèç¹ââå½å审æ¹äººââæµè½¬è®°å½âï¼è°ç¨âæ¥è¯¢å®¡æ¹æµè½¬è®°å½âã |
| | | 7. ç¨æ·è¯´âæåèµ·çââææäº¤çââæç³è¯·çâï¼æ¥è¯¢èå´ä½¿ç¨ `applicant`ï¼ç¨æ·è¯´âå¾
æå®¡æ¹ââå½åå¾
æå¤çââéè¦æå¤çâï¼æ¥è¯¢èå´ä½¿ç¨ `approver`ï¼æ²¡ææç¡®èå´æ¶ä½¿ç¨ `related`ã |
| | | 8. ç¨æ·è¯´âå¤çä¸ââåçä¸âï¼ç¶æä½¿ç¨ `processing`ï¼è¯´âå¾
审æ¹ââå¾
å®¡æ ¸âï¼ç¶æä½¿ç¨ `pending`ï¼è¯´âéè¿ââå·²éè¿âï¼ç¶æä½¿ç¨ `approved`ï¼è¯´â驳åââæç»ââæªéè¿âï¼ç¶æä½¿ç¨ `rejected`ã |
| | | 9. ç¨æ·è¦æ±âè¿7天ââæ¬æââè¿30天ââåç±»ååå¸ââéè¿/驳å/å¤çä¸åæå¤å°âçç»è®¡å£å¾æ¶ï¼è°ç¨ç»è®¡å·¥å
·ã |
| | | 10. ç¨æ·è¯´â夿³¨åæââ夿³¨è¯·æ±è¡¥å
说æâæ¶ï¼æå¤æ³¨å
å®¹ä¼ ç»å®¡æ ¸å·¥å
·ç remarkï¼é©³åæ¶å¦ææ²¡æâåå â使â夿³¨âï¼ä¹ä½¿ç¨å¤æ³¨ã |
| | | 11. é¤âæ¥è¯¢å®¡æ¹å¾
å详æ
âå¤ï¼å
¶ä»å·¥å
·é»è®¤è¿å JSONã |
| | | 12. 对äºè¿äº JSON å·¥å
·ï¼ä½ å¿
é¡»ç´æ¥è¾åºåå§ JSON å符串æ¬èº«ï¼ä¸è¦æ¹åï¼ä¸è¦é¢å¤è§£éï¼ä¸è¦å
裹 Markdown 代ç åï¼ä¸è¦å¨ JSON ååå 任使åã |
| | | 13. åªæâæ¥è¯¢å®¡æ¹å¾
å详æ
âè¿ä¸ªå·¥å
·å
许è¾åºèªç¶è¯è¨ææ¬ã |
| | | 14. 妿工å
·è¿åçæ¯ç»è®¡ JSONï¼ä¹åæ ·ç´æ¥è¾åºåå§ JSONï¼å
¶ä¸ `description`ã`summary`ã`charts` å·²ç»ä¾å端使ç¨ã |
| | | 15. åç使ç¨ä¸æï¼ä½å¨ JSON åºæ¯ä¸ï¼æç»è¾åºå¿
é¡»æ¯åæ³ JSON æ¬ä½ã |
| | |
| | | <result column="deleted" property="deleted" /> |
| | | <result column="record_type" property="recordType" /> |
| | | <result column="record_id" property="recordId" /> |
| | | <result column="name" property="name" /> |
| | | <result column="application" property="application" /> |
| | | <result column="storage_blob_id" property="storageBlobId" /> |
| | | </resultMap> |
| | | |
| | | <!-- éç¨æ¥è¯¢ç»æå --> |
| | | <sql id="Base_Column_List"> |
| | | id, create_time, update_time, deleted, record_type, record_id, name, storage_blob_id |
| | | </sql> |
| | | |
| | | </mapper> |
| | | </mapper> |
| | |
| | | <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
| | | <mapper namespace="com.ruoyi.basic.mapper.StorageBlobMapper"> |
| | | |
| | | <!-- éç¨æ¥è¯¢æ å°ç»æ --> |
| | | <resultMap id="BaseResultMap" type="com.ruoyi.basic.pojo.StorageBlob"> |
| | | <id column="id" property="id" /> |
| | | <result column="create_time" property="createTime" /> |
| | | <result column="key" property="key" /> |
| | | <result column="content_type" property="contentType" /> |
| | | <result column="original_filename" property="originalFilename" /> |
| | | <result column="bucket_filename" property="bucketFilename" /> |
| | | <result column="bucket_name" property="bucketName" /> |
| | | <result column="byte_size" property="byteSize" /> |
| | | </resultMap> |
| | | <!-- éç¨æ¥è¯¢æ å°ç»æ --> |
| | | <resultMap id="BaseResultMap" type="com.ruoyi.basic.pojo.StorageBlob"> |
| | | <id column="id" property="id"/> |
| | | <result column="resource_key" property="resourceKey"/> |
| | | <result column="content_type" property="contentType"/> |
| | | <result column="original_filename" property="originalFilename"/> |
| | | <result column="uid_filename" property="uidFilename"/> |
| | | <result column="byte_size" property="byteSize"/> |
| | | <result column="path" property="path"/> |
| | | </resultMap> |
| | | |
| | | <!-- éç¨æ¥è¯¢ç»æå --> |
| | | <sql id="Base_Column_List"> |
| | | id, create_time, key, content_type, original_filename,bucket_filename,bucket_name, byte_size |
| | | </sql> |
| | | <!-- éç¨æ¥è¯¢ç»æå --> |
| | | <sql id="Base_Column_List"> |
| | | id, resource_key, content_type, original_filename, uid_filename, byte_size, path |
| | | </sql> |
| | | |
| | | </mapper> |
| | | <select id="selectOrphanBlobsByIdRange" resultMap="BaseResultMap"> |
| | | SELECT |
| | | <include refid="Base_Column_List"/> |
| | | FROM storage_blob sb |
| | | LEFT JOIN storage_attachment sa |
| | | ON sa.storage_blob_id = sb.id |
| | | AND sa.deleted = 0 |
| | | WHERE sb.id <![CDATA[>]]> #{lastId} |
| | | AND sa.id IS NULL |
| | | ORDER BY sb.id ASC |
| | | LIMIT #{limit} |
| | | </select> |
| | | |
| | | <delete id="deleteByIdList"> |
| | | DELETE FROM storage_blob |
| | | WHERE id IN |
| | | <foreach collection="ids" item="id" open="(" separator="," close=")"> |
| | | #{id} |
| | | </foreach> |
| | | </delete> |
| | | |
| | | <select id="selectExistingUidFilenames" resultType="java.lang.String"> |
| | | SELECT uid_filename |
| | | FROM storage_blob |
| | | WHERE uid_filename IN |
| | | <foreach collection="fileNames" item="fileName" open="(" separator="," close=")"> |
| | | #{fileName} |
| | | </foreach> |
| | | </select> |
| | | |
| | | </mapper> |
| | |
| | | <select id="pageProductionOperationTask" resultType="com.ruoyi.production.bean.vo.ProductionOperationTaskVo"> |
| | | select pot.*, |
| | | po.nps_no as npsNo, |
| | | po.is_end_order as endOrder, |
| | | p.product_name as productName, |
| | | pm.model as model, |
| | | pm.unit as unit, |
| | |
| | | po_sales.customerName, |
| | | p.product_name as productName, |
| | | pm.model as model, |
| | | po.is_end_order as endOrder, |
| | | tr.process_route_code as processRouteCode, |
| | | ROUND(po.complete_quantity / po.quantity * 100, 2) AS completionStatus, |
| | | tb.bom_no as bomNo |
| | | </sql> |
| | | |
| | |
| | | <select id="checkEmailUnique" parameterType="String" resultMap="SysUserResult"> |
| | | select user_id, email from sys_user where email = #{email} and del_flag = '0' limit 1 |
| | | </select> |
| | | <select id="selectUserByIds" resultType="com.ruoyi.project.system.domain.SysUser"> |
| | | <include refid="selectUserVo"/> |
| | | where u.user_id in <foreach collection="userIds" item="item" open="(" separator="," close=")"> |
| | | #{item} |
| | | </foreach> |
| | | and u.del_flag = '0' |
| | | </select> |
| | | <select id="selectUserByIds" resultType="com.ruoyi.project.system.domain.SysUser"> |
| | | <include refid="selectUserVo"/> |
| | | <where> |
| | | <if test="userIds != null and userIds.size > 0"> |
| | | and u.user_id in |
| | | <foreach collection="userIds" item="item" open="(" separator="," close=")"> |
| | | #{item} |
| | | </foreach> |
| | | </if> |
| | | and u.del_flag = '0' |
| | | </where> |
| | | </select> |
| | | <select id="selectRegistrantIds" resultType="com.ruoyi.project.system.domain.SysUser"> |
| | | SELECT user_id, nick_name FROM sys_user |
| | | <where> |
| | |
| | | å·¥ä½è§åï¼ |
| | | 1. ä¼å
è°ç¨å·¥å
·å½æ°è·åéè´å°è´¦ã仿¬¾ãå票ãéè´§çç»æåæ°æ®ã |
| | | 2. éå°âç»è®¡/åæ/æ¥è¡¨/ä»å¹´/æ¬æ/è¿XX天âçéæ±ï¼ä¼å
ç»åºç»è®¡ç»æåå
³é®ç»è®ºã |
| | | 3. æ æ³ç´æ¥å¾åºç»è®ºæ¶ï¼æç¡®è¯´æç¼ºå°åªäºå段æç鿡件ã |
| | | 4. ç»æç¨ç®æ´ä¸æåçï¼å
ç»ç»è®ºï¼åç»å
³é®æ°æ®ç¹ã |
| | | 5. ä¸è¦ç¼é éè´æ°æ®ï¼ææç»è®ºå¿
é¡»åºäºå·¥å
·è¿åã |
| | | 3. ç¨æ·é®âæ¬æéè´é颿åé åçç©æââéè´é颿è¡ââç©ææè¡âæ¶ï¼è°ç¨âéè´ç©æé颿è¡âã |
| | | 4. ç¨æ·é®âåªäºéè´è®¢åè¿æªå
¥åºââæªå
¥åºéè´åââå¾
å
¥åºè®¢åâæ¶ï¼è°ç¨âæ¥è¯¢æªå
¥åºéè´è®¢åâã |
| | | 5. ç¨æ·é®âæè¿7天ä¾åºåå°è´§å¼å¸¸ââå°è´§é®é¢ââå°è´§å¼å¸¸âæ¶ï¼è°ç¨âæ¥è¯¢éè´å°è´§å¼å¸¸âã |
| | | 6. ç¨æ·é®âå¾
仿¬¾éè´åââæªä»æ¬¾éè´åââæªä»æ¸
éè´è®¢åâæ¶ï¼è°ç¨âæ¥è¯¢å¾
仿¬¾éè´åâã |
| | | 7. ç¨æ·é®âæ¬æéè´éè´§æ
åµââéè´éè´§å表ââéæ/ææ¶æ
åµâæ¶ï¼è°ç¨âæ¥è¯¢éè´éè´§æ
åµâã |
| | | 8. ç»æç¨ç®æ´ä¸æåçï¼å
ç»ç»è®ºï¼åç»å
³é®æ°æ®ç¹ã |
| | | 9. ä¸è¦ç¼é éè´æ°æ®ï¼ææç»è®ºå¿
é¡»åºäºå·¥å
·è¿åã |
| | | 10. æ æ³ç´æ¥å¾åºç»è®ºæ¶ï¼æç¡®è¯´æç¼ºå°åªäºå段æç鿡件ã |