From 2f80b7085c4eabce06d3491306b75eecc275275f Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期四, 30 四月 2026 17:31:57 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/dev_New_pro' into dev_New_pro

---
 src/main/java/com/ruoyi/quality/service/impl/QualityInspectServiceImpl.java                                    |   15 
 src/main/java/com/ruoyi/production/controller/ProductionOperationTaskController.java                           |   29 
 src/main/java/com/ruoyi/purchase/service/impl/PurchaseLedgerServiceImpl.java                                   |  102 -
 src/main/java/com/ruoyi/procurementrecord/utils/StockUtils.java                                                |   17 
 src/main/java/com/ruoyi/production/service/impl/ProductionOperationTaskServiceImpl.java                        |  293 ++++
 src/main/resources/mapper/basic/StorageAttachmentMapper.xml                                                    |    8 
 src/main/java/com/ruoyi/sales/service/impl/ShippingInfoServiceImpl.java                                        |    3 
 src/main/java/com/ruoyi/production/bean/vo/ProductionAccountVo.java                                            |   59 
 src/main/java/com/ruoyi/technology/service/impl/TechnologyRoutingServiceImpl.java                              |    4 
 src/main/resources/mapper/production/ProductionAccountMapper.xml                                               |   87 +
 src/main/java/com/ruoyi/production/service/impl/ProductionOrderServiceImpl.java                                |  208 +++
 src/main/java/com/ruoyi/production/service/ProductionOrderService.java                                         |    5 
 src/main/java/com/ruoyi/basic/pojo/ProductModel.java                                                           |    4 
 src/main/java/com/ruoyi/stock/service/impl/StockInventoryServiceImpl.java                                      |  107 +
 src/main/java/com/ruoyi/production/bean/dto/ProductionAccountDto.java                                          |   63 +
 src/main/java/com/ruoyi/production/pojo/ProductionOrderRoutingOperationParam.java                              |    3 
 src/main/java/com/ruoyi/project/common/CommonController.java                                                   |   90 -
 src/main/java/com/ruoyi/production/controller/ProductionOrderController.java                                   |   13 
 src/main/java/com/ruoyi/production/service/impl/ProductionAccountServiceImpl.java                              |   69 +
 src/main/java/com/ruoyi/measuringinstrumentledger/service/impl/MeasuringInstrumentLedgerServiceImpl.java       |  105 -
 src/main/java/com/ruoyi/production/pojo/ProductionProductOutput.java                                           |    2 
 src/main/java/com/ruoyi/purchase/service/impl/InvoicePurchaseServiceImpl.java                                  |    3 
 src/main/java/com/ruoyi/production/service/impl/ProductionOrderRoutingOperationParamServiceImpl.java           |    5 
 src/main/java/com/ruoyi/purchase/controller/TicketRegistrationController.java                                  |   18 
 src/main/java/com/ruoyi/sales/service/impl/CommonFileServiceImpl.java                                          |  114 -
 src/main/java/com/ruoyi/production/mapper/ProductionProductMainMapper.java                                     |    4 
 src/main/java/com/ruoyi/technology/controller/TechnologyRoutingController.java                                 |    3 
 src/main/resources/mapper/production/ProductionOperationTaskMapper.xml                                         |   25 
 src/main/resources/mapper/basic/StorageBlobMapper.xml                                                          |   61 
 src/main/java/com/ruoyi/production/service/ProductionAccountService.java                                       |    7 
 src/main/java/com/ruoyi/purchase/service/impl/TicketRegistrationServiceImpl.java                               |   87 -
 src/main/java/com/ruoyi/production/bean/vo/ProductionOrderWorkOrderDetailVo.java                               |   66 +
 src/main/java/com/ruoyi/basic/mapper/StorageBlobMapper.java                                                    |    6 
 src/main/java/com/ruoyi/production/bean/dto/ProductionOperationTaskDto.java                                    |   24 
 FILE_UPLOAD_README.md                                                                                          |  734 ++++++++++++
 src/main/java/com/ruoyi/production/service/ProductionOperationTaskService.java                                 |    5 
 src/main/java/com/ruoyi/production/service/impl/ProductionOrderPickServiceImpl.java                            |  102 +
 src/main/java/com/ruoyi/production/bean/vo/ProductionOperationTaskVo.java                                      |    8 
 src/main/java/com/ruoyi/safe/controller/SafeTrainingController.java                                            |    4 
 src/main/java/com/ruoyi/production/bean/dto/ProductionProductMainDto.java                                      |   42 
 src/main/java/com/ruoyi/production/bean/vo/ProductionOrderVo.java                                              |    4 
 src/main/java/com/ruoyi/production/controller/ProductionProductMainController.java                             |    2 
 src/main/java/com/ruoyi/measuringinstrumentledger/service/MeasuringInstrumentLedgerRecordService.java          |    4 
 src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java                                         |   90 -
 src/main/java/com/ruoyi/common/enums/StockInQualifiedRecordTypeEnum.java                                       |    3 
 src/main/java/com/ruoyi/approve/service/impl/ApproveProcessServiceImpl.java                                    |    6 
 src/main/java/com/ruoyi/production/service/impl/ProductionProductMainServiceImpl.java                          |  209 +++
 src/main/java/com/ruoyi/basic/controller/StorageAttachmentController.java                                      |   18 
 src/main/java/com/ruoyi/purchase/dto/PurchaseLedgerDto.java                                                    |    5 
 src/main/java/com/ruoyi/sales/service/ICommonFileService.java                                                  |    6 
 src/main/java/com/ruoyi/basic/task/StorageBlobCleanupTask.java                                                 |  184 +++
 src/main/resources/mapper/production/ProductionOrderMapper.xml                                                 |    2 
 src/main/java/com/ruoyi/production/controller/ProductionAccountController.java                                 |   29 
 src/main/resources/application-dev-pro.yml                                                                     |    4 
 /dev/null                                                                                                      |  184 ---
 src/main/java/com/ruoyi/purchase/controller/InvoicePurchaseController.java                                     |   11 
 src/main/java/com/ruoyi/measuringinstrumentledger/service/impl/MeasuringInstrumentLedgerRecordServiceImpl.java |   97 -
 src/main/resources/mapper/production/ProductionProductMainMapper.xml                                           |   50 
 src/main/resources/mapper/system/SysUserMapper.xml                                                             |   19 
 src/main/java/com/ruoyi/production/pojo/ProductionOrder.java                                                   |    6 
 src/main/java/com/ruoyi/production/mapper/ProductionOperationTaskMapper.java                                   |    1 
 src/main/java/com/ruoyi/production/mapper/ProductionAccountMapper.java                                         |    6 
 62 files changed, 2,455 insertions(+), 1,089 deletions(-)

diff --git a/FILE_UPLOAD_README.md b/FILE_UPLOAD_README.md
new file mode 100644
index 0000000..558dc85
--- /dev/null
+++ b/FILE_UPLOAD_README.md
@@ -0,0 +1,734 @@
+# 鏂囦欢涓婁紶鍔熻兘璇存槑
+
+鏈枃妗e熀浜庝互涓嬩唬鐮佹暣鐞嗭細
+
+- `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>`
+
+浠g爜閫昏緫锛�
+
+1. 鍓嶇鍏堣皟鐢� `/common/upload`
+2. `CommonController.upload()` 璋冪敤 `storageBlobService.upload(files, false)`
+3. 鏈嶅姟灞備繚瀛樻枃浠跺厓鏁版嵁鍒� `storage_blob`
+4. 杩斿洖 `StorageBlobVO` 鍒楄〃锛岄噷闈㈤�氬父浼氬甫锛�
+   - 鏂囦欢 id
+   - 鍘熷鏂囦欢鍚�
+   - 鍞竴鏂囦欢鍚�
+   - 棰勮鍦板潃 `previewURL`
+   - 涓嬭浇鍦板潃 `downloadURL`
+
+璇存槑锛�
+
+- 姝ゆ椂鍙槸鈥滀笂浼犱簡鏂囦欢鈥�
+- 杩樻病鏈夊拰鍏蜂綋涓氬姟鍗曟嵁寤虹珛鍏崇郴
+
+### 2.2 鍏叡涓婁紶
+
+鎺ュ彛锛�
+
+- `POST /common/public/upload`
+
+浠g爜閫昏緫锛�
+
+- `CommonController.publicUpload()` 璋冪敤 `storageBlobService.upload(files, true)`
+
+璇存槑锛�
+
+- 璇ユ帴鍙d笂浼犵殑鏂囦欢璧扳�滃叕鍏辨枃浠垛�濇ā寮�
+- 鎺у埗鍣ㄦ敞閲婂凡鏄庣‘璇存槑锛氭案涔呮湁鏁堬紝鎱庣敤
+- 瀵瑰簲 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`
+
+浠g爜閫昏緫锛�
+
+1. 濡傛灉璇锋眰閲屾湁 `publicKey`锛岃蛋 `storageBlobService.getPublicFile(fileName, publicKey)`
+2. 鍚﹀垯璧� `storageBlobService.getFileByToken(fileName, token)`
+3. 鍙栧埌瀹為檯鏂囦欢鍚庯紝璋冪敤 `fileUtil.compressFile(file)` 鍋氬浘鐗囧帇缂╁鐞�
+4. 璁剧疆涓嬭浇鍝嶅簲澶达紝杈撳嚭鏂囦欢娴�
+
+### 5.2 棰勮鎺ュ彛
+
+鎺ュ彛锛�
+
+- `GET /common/preview/{fileName}`
+
+鏀寔涓ょ璁块棶鏂瑰紡锛�
+
+- 涓存椂閾炬帴锛歚token`
+- 鍏叡閾炬帴锛歚publicKey`
+
+浠g爜閫昏緫锛�
+
+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` 鍜屼笟鍔℃暟鎹叧鑱�
+
+璇存槑锛�
+
+- 璇ユ灇涓惧�煎緢澶氾紝鏂囨。涓嶉�愪釜灞曞紑
+- 瀹為檯浣跨敤鏃跺繀椤讳紶浠g爜涓凡瀹氫箟鐨� `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()`
+
+浣滅敤锛�
+
+- 瑙f瀽 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. 濡傛灉鍘嬬缉澶辫触锛岄檷绾ц繑鍥炲師鏂囦欢
+
+璇存槑锛�
+
+- 褰撳墠涓嬭浇鍜岄瑙堟帴鍙i兘浼氳皟鐢ㄨ繖涓柟娉�
+
+#### 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` 鍙礋璐f枃浠跺叆搴�
+- `/storageAttachment/add` 鎵嶆槸鍜屼笟鍔℃暟鎹缓绔嬪叧绯�
+
+### 9.2 `application` 寰堥噸瑕�
+
+- 鍚屼竴鏉� `recordId` 涓嬪彲鑳芥湁澶氱粍涓嶅悓鐢ㄩ�旈檮浠�
+- 鍒犻櫎鍜屾煡璇㈡椂锛岀粡甯镐緷璧� `application`
+
+### 9.3 涓嬭浇閾炬帴涓嶆槸姘镐箙鏈夋晥
+
+- 鏅�氶摼鎺ヤ竴鑸�氳繃 JWT token 鎺у埗
+- 鍚屾椂鍙楄繃鏈熸椂闂村拰浣跨敤娆℃暟闄愬埗
+
+### 9.4 鍏叡鏂囦欢瑕佹厧鐢�
+
+- `public/upload` 涓婁紶鐨勬枃浠跺彲璧版案涔呭叕寮�璁块棶
+- 閫傚悎鍏紑璧勬簮锛屼笉閫傚悎鏁忔劅鏂囦欢
+
+### 9.5 鍥剧墖棰勮/涓嬭浇鍙兘杩斿洖鍘嬬缉鏂囦欢
+
+- 褰撳墠鎺у埗鍣ㄥ湪涓嬭浇鍜岄瑙堝墠閮戒細璋冪敤 `compressFile()`
+- 澶у浘鍦ㄨ闂椂鍙兘浣跨敤鍘嬬缉鍚庣殑鍓湰
+
+## 10. 涓�鍙ヨ瘽鎬荤粨
+
+鏈」鐩殑鏂囦欢涓婁紶鏂规鏄�滀袱闃舵妯″瀷鈥濓細
+
+- 绗竴闃舵涓婁紶鏂囦欢锛岀敓鎴� `storage_blob`
+- 绗簩闃舵缁戝畾涓氬姟锛岀敓鎴� `storage_attachment`
+
+鑰� `FileUtil` 鍒欒礋璐f妸鈥滀笂浼犲悗鐨勬枃浠垛�濆彉鎴愨�滃彲鏌ヨ銆佸彲棰勮銆佸彲涓嬭浇銆佸彲鍒犻櫎銆佸彲鎺ф椂鏁堚�濈殑瀹屾暣闄勪欢鑳藉姏銆�
diff --git a/src/main/java/com/ruoyi/approve/service/impl/ApproveProcessServiceImpl.java b/src/main/java/com/ruoyi/approve/service/impl/ApproveProcessServiceImpl.java
index a93dc5d..85be19a 100644
--- a/src/main/java/com/ruoyi/approve/service/impl/ApproveProcessServiceImpl.java
+++ b/src/main/java/com/ruoyi/approve/service/impl/ApproveProcessServiceImpl.java
@@ -7,7 +7,6 @@
 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;
@@ -20,7 +19,7 @@
 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;
@@ -82,6 +81,9 @@
                 .map(ApproveProcessConfigNodeVo::getApproverId)
                 .filter(Objects::nonNull)
                 .collect(Collectors.toList());
+                if(list.isEmpty()) {
+            throw new RuntimeException("娴佺▼涓嶅瓨鍦�");
+        }
         if (CollectionUtils.isEmpty(nodeIds)) {
             autoPassPurchaseApproveIfNoApprover(approveProcessVO);
             return;
diff --git a/src/main/java/com/ruoyi/basic/controller/StorageAttachmentController.java b/src/main/java/com/ruoyi/basic/controller/StorageAttachmentController.java
index d79f911..79ae5d4 100644
--- a/src/main/java/com/ruoyi/basic/controller/StorageAttachmentController.java
+++ b/src/main/java/com/ruoyi/basic/controller/StorageAttachmentController.java
@@ -1,16 +1,10 @@
 package com.ruoyi.basic.controller;
 
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.ruoyi.basic.dto.StorageAttachmentDTO;
-import com.ruoyi.basic.dto.StorageBlobDTO;
-import com.ruoyi.basic.dto.SupplierManageDto;
-import com.ruoyi.basic.enums.ApplicationTypeEnum;
-import com.ruoyi.basic.enums.RecordTypeEnum;
-import com.ruoyi.basic.pojo.StorageAttachment;
 import com.ruoyi.basic.service.StorageAttachmentService;
-import com.ruoyi.common.constant.StorageAttachmentConstants;
-import com.ruoyi.common.enums.StorageAttachmentRecordType;
 import com.ruoyi.framework.web.domain.R;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
 import lombok.AllArgsConstructor;
 import org.springframework.web.bind.annotation.*;
 
@@ -18,26 +12,31 @@
 
 @RestController
 @AllArgsConstructor
-    @RequestMapping("/basic/storage_attachment")
+@Tag(name = "閫氱敤涓婁紶")
+@RequestMapping("/storageAttachment")
 public class StorageAttachmentController {
     private StorageAttachmentService storageAttachmentService;
 
     /**
      * 鍒嗛〉鏌ヨ閫氱敤鏂囦欢涓婁紶鐨勯檮浠朵俊鎭�
+     *
      * @param storageAttachmentDTO 鍏宠仈璁板綍淇℃伅
      * @return 鍒嗛〉缁撴灉
      */
     @GetMapping("/list")
+    @Operation(summary = "鍒嗛〉鏌ヨ閫氱敤鏂囦欢涓婁紶鐨勯檮浠朵俊鎭�")
     public R list(StorageAttachmentDTO storageAttachmentDTO) {
         return R.ok(storageAttachmentService.list(storageAttachmentDTO));
     }
 
     /**
      * 鍒犻櫎閫氱敤鏂囦欢涓婁紶鐨勯檮浠朵俊鎭�
+     *
      * @param ids 鏂囦欢id鍒楄〃
      * @return 鍒犻櫎缁撴灉
      */
     @DeleteMapping("/delete")
+    @Operation(summary = "鍒犻櫎閫氱敤鏂囦欢涓婁紶鐨勯檮浠朵俊鎭�")
     public R batchDelete(@RequestBody List<Long> ids) {
         return R.ok(storageAttachmentService.batchDeleteStorageAttachment(ids));
     }
@@ -46,6 +45,7 @@
      * 淇濆瓨閫氱敤鏂囦欢涓婁紶鐨勯檮浠朵俊鎭�
      */
     @PostMapping("/add")
+    @Operation(summary = "淇濆瓨閫氱敤鏂囦欢涓婁紶鐨勯檮浠朵俊鎭�")
     public R add(@RequestBody StorageAttachmentDTO storageAttachmentDTO) {
         storageAttachmentService.saveStorageAttachment(storageAttachmentDTO);
         return R.ok();
diff --git a/src/main/java/com/ruoyi/basic/mapper/StorageBlobMapper.java b/src/main/java/com/ruoyi/basic/mapper/StorageBlobMapper.java
index 756b4b9..5f84cb7 100644
--- a/src/main/java/com/ruoyi/basic/mapper/StorageBlobMapper.java
+++ b/src/main/java/com/ruoyi/basic/mapper/StorageBlobMapper.java
@@ -3,6 +3,7 @@
 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>
@@ -15,4 +16,9 @@
 @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);
 }
diff --git a/src/main/java/com/ruoyi/basic/pojo/ProductModel.java b/src/main/java/com/ruoyi/basic/pojo/ProductModel.java
index 0ec51b2..f0e9470 100644
--- a/src/main/java/com/ruoyi/basic/pojo/ProductModel.java
+++ b/src/main/java/com/ruoyi/basic/pojo/ProductModel.java
@@ -37,6 +37,10 @@
     @Excel(name = "瑙勬牸鍨嬪彿")
     private String model;
 
+    @Excel(name = "浜у搧缂栫爜")
+    @TableField("product_code")
+    private String productCode;
+
     /**
      * 鍗曚綅
      */
diff --git a/src/main/java/com/ruoyi/basic/task/StorageBlobCleanupTask.java b/src/main/java/com/ruoyi/basic/task/StorageBlobCleanupTask.java
new file mode 100644
index 0000000..d5ac456
--- /dev/null
+++ b/src/main/java/com/ruoyi/basic/task/StorageBlobCleanupTask.java
@@ -0,0 +1,184 @@
+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("鏂囦欢娓呯悊浠诲姟姝e湪鎵ц锛屾湰娆¤烦杩�");
+            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锛宐atchSize={}锛宭astId={}", 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;
+    }
+}
diff --git a/src/main/java/com/ruoyi/common/enums/StockInQualifiedRecordTypeEnum.java b/src/main/java/com/ruoyi/common/enums/StockInQualifiedRecordTypeEnum.java
index df0d011..6aecb29 100644
--- a/src/main/java/com/ruoyi/common/enums/StockInQualifiedRecordTypeEnum.java
+++ b/src/main/java/com/ruoyi/common/enums/StockInQualifiedRecordTypeEnum.java
@@ -12,7 +12,8 @@
     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;
diff --git a/src/main/java/com/ruoyi/measuringinstrumentledger/service/MeasuringInstrumentLedgerRecordService.java b/src/main/java/com/ruoyi/measuringinstrumentledger/service/MeasuringInstrumentLedgerRecordService.java
index bdb1135..2d7d38c 100644
--- a/src/main/java/com/ruoyi/measuringinstrumentledger/service/MeasuringInstrumentLedgerRecordService.java
+++ b/src/main/java/com/ruoyi/measuringinstrumentledger/service/MeasuringInstrumentLedgerRecordService.java
@@ -4,9 +4,7 @@
 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
@@ -25,5 +23,5 @@
 
     void export(HttpServletResponse response);
 
-    boolean updateMeasuringInstrumentLedgerRecord(MeasuringInstrumentLedgerRecord measuringInstrumentLedgerRecord) throws IOException;
+    boolean updateMeasuringInstrumentLedgerRecord(MeasuringInstrumentLedgerRecord measuringInstrumentLedgerRecord);
 }
diff --git a/src/main/java/com/ruoyi/measuringinstrumentledger/service/impl/MeasuringInstrumentLedgerRecordServiceImpl.java b/src/main/java/com/ruoyi/measuringinstrumentledger/service/impl/MeasuringInstrumentLedgerRecordServiceImpl.java
index b178d75..7104c1a 100644
--- a/src/main/java/com/ruoyi/measuringinstrumentledger/service/impl/MeasuringInstrumentLedgerRecordServiceImpl.java
+++ b/src/main/java/com/ruoyi/measuringinstrumentledger/service/impl/MeasuringInstrumentLedgerRecordServiceImpl.java
@@ -5,35 +5,21 @@
 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
@@ -47,10 +33,6 @@
     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) {
@@ -73,7 +55,7 @@
     }
 
     @Override
-    public boolean updateMeasuringInstrumentLedgerRecord(MeasuringInstrumentLedgerRecord measuringInstrumentLedgerRecord) throws IOException {
+    public boolean updateMeasuringInstrumentLedgerRecord(MeasuringInstrumentLedgerRecord measuringInstrumentLedgerRecord) {
         MeasuringInstrumentLedgerRecord measuringInstrumentLedgerRecord1 = measuringInstrumentLedgerRecordMapper.selectById(measuringInstrumentLedgerRecord.getId());
         if (measuringInstrumentLedgerRecord1 == null) {
             return false;
@@ -88,83 +70,6 @@
             measuringInstrumentLedgerMapper.updateById(measuringInstrumentLedger);
         }
         measuringInstrumentLedgerRecordMapper.updateById(measuringInstrumentLedgerRecord);
-        // 璁板綍闄勪欢缁戝畾
-        migrateTempFilesToFormal(measuringInstrumentLedgerRecord.getId(), measuringInstrumentLedgerRecord.getTempFileIds(), FileNameType.MEASURINGRecord.getValue());
         return true;
-    }
-
-
-    /**
-     * 灏嗕复鏃舵枃浠惰縼绉诲埌姝e紡鐩綍
-     *
-     * @param businessId  涓氬姟ID锛堥攢鍞彴璐D锛�
-     * @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;
-        }
-
-        // 鏋勫缓姝e紡鐩綍璺緞锛堟寜涓氬姟绫诲瀷鍜屾棩鏈熷垎缁勶級
-        String formalDir = uploadDir + LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE);
-
-        Path formalDirPath = Paths.get(formalDir);
-
-        // 纭繚姝e紡鐩綍瀛樺湪锛堥�掑綊鍒涘缓锛�
-        if (!Files.exists(formalDirPath)) {
-            Files.createDirectories(formalDirPath);
-        }
-
-        for (String tempFileId : tempFileIds) {
-            // 鏌ヨ涓存椂鏂囦欢璁板綍
-            TempFile tempFile = tempFileMapper.selectById(tempFileId);
-            if (tempFile == null) {
-                log.warn("涓存椂鏂囦欢涓嶅瓨鍦紝璺宠繃澶勭悊: {}", tempFileId);
-                continue;
-            }
-
-            // 鏋勫缓姝e紡鏂囦欢鍚嶏紙鍖呭惈涓氬姟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);
-            }
-        }
     }
 }
diff --git a/src/main/java/com/ruoyi/measuringinstrumentledger/service/impl/MeasuringInstrumentLedgerServiceImpl.java b/src/main/java/com/ruoyi/measuringinstrumentledger/service/impl/MeasuringInstrumentLedgerServiceImpl.java
index 72ddbaa..4025d75 100644
--- a/src/main/java/com/ruoyi/measuringinstrumentledger/service/impl/MeasuringInstrumentLedgerServiceImpl.java
+++ b/src/main/java/com/ruoyi/measuringinstrumentledger/service/impl/MeasuringInstrumentLedgerServiceImpl.java
@@ -6,7 +6,6 @@
 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;
@@ -14,8 +13,6 @@
 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;
@@ -23,22 +20,12 @@
 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;
 
 /**
@@ -52,12 +39,7 @@
 
     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) {
@@ -74,12 +56,6 @@
                 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;
     }
@@ -107,10 +83,6 @@
 //            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;
@@ -131,84 +103,7 @@
         }
         measuringInstrumentLedger.setUserName(sysUser.getUserName());
         measuringInstrumentLedgerMapper.insert(measuringInstrumentLedger);
-        if(!CollectionUtils.isEmpty(measuringInstrumentLedger.getTempFileIds())){
-            migrateTempFilesToFormal(measuringInstrumentLedger.getId(), measuringInstrumentLedger.getTempFileIds(), FileNameType.MEASURING.getValue());
-        }
         return true;
     }
 
-
-    /**
-     * 灏嗕复鏃舵枃浠惰縼绉诲埌姝e紡鐩綍
-     *
-     * @param businessId  涓氬姟ID锛堥攢鍞彴璐D锛�
-     * @param tempFileIds 涓存椂鏂囦欢ID鍒楄〃
-     * @throws IOException 鏂囦欢鎿嶄綔寮傚父
-     */
-    private void migrateTempFilesToFormal(Long businessId, List<String> tempFileIds,Integer fileType) throws IOException {
-        if (CollectionUtils.isEmpty(tempFileIds)) {
-            return;
-        }
-
-        // 鏋勫缓姝e紡鐩綍璺緞锛堟寜涓氬姟绫诲瀷鍜屾棩鏈熷垎缁勶級
-        String formalDir = uploadDir + LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE);
-
-        Path formalDirPath = Paths.get(formalDir);
-
-        // 纭繚姝e紡鐩綍瀛樺湪锛堥�掑綊鍒涘缓锛�
-        if (!Files.exists(formalDirPath)) {
-            Files.createDirectories(formalDirPath);
-        }
-
-        for (String tempFileId : tempFileIds) {
-            // 鏌ヨ涓存椂鏂囦欢璁板綍
-            TempFile tempFile = tempFileMapper.selectById(tempFileId);
-            if (tempFile == null) {
-                log.warn("涓存椂鏂囦欢涓嶅瓨鍦紝璺宠繃澶勭悊: {}", tempFileId);
-                continue;
-            }
-
-            // 鏋勫缓姝e紡鏂囦欢鍚嶏紙鍖呭惈涓氬姟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);
-            }
-        }
-    }
 }
diff --git a/src/main/java/com/ruoyi/other/controller/TempFileController.java b/src/main/java/com/ruoyi/other/controller/TempFileController.java
deleted file mode 100644
index 1175eb0..0000000
--- a/src/main/java/com/ruoyi/other/controller/TempFileController.java
+++ /dev/null
@@ -1,57 +0,0 @@
-package com.ruoyi.other.controller;
-
-
-import com.ruoyi.framework.web.domain.AjaxResult;
-import com.ruoyi.other.service.TempFileService;
-import com.ruoyi.purchase.dto.ProductRecordDto;
-import com.ruoyi.purchase.dto.TicketRegistrationDto;
-import com.ruoyi.purchase.service.ITicketRegistrationService;
-import com.ruoyi.purchase.service.impl.TicketRegistrationServiceImpl;
-import lombok.AllArgsConstructor;
-import org.springframework.web.bind.annotation.PostMapping;
-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;
-
-
-@RestController
-@RequestMapping("/file")
-@AllArgsConstructor
-public class TempFileController {
-
-    private TempFileService tempFileService;
-
-    private TicketRegistrationServiceImpl ticketRegistrationServiceImpl;
-
-    @PostMapping("/upload")
-    public AjaxResult uploadFile(MultipartFile file, Integer type) {
-        try {
-            return AjaxResult.success(tempFileService.uploadFile(file, type));
-        } catch (Exception e) {
-            return AjaxResult.error(e.getMessage());
-        }
-    }
-
-    @PostMapping("/uploadByCommon")
-    public AjaxResult uploadByCommon(MultipartFile file, Integer type, Long id) {
-        try {
-            return AjaxResult.success(tempFileService.uploadByCommon(file, type,id));
-        } catch (Exception e) {
-            return AjaxResult.error(e.getMessage());
-        }
-    }
-
-    @PostMapping("uploadFile")
-    public AjaxResult uploadFile(@RequestBody ProductRecordDto productRecordDto) {
-        try {
-            if (!productRecordDto.getTempFileIds().isEmpty()&&productRecordDto.getTicketRegistrationId() != null) {
-                ticketRegistrationServiceImpl.migrateTempFilesToFormal(productRecordDto.getTicketRegistrationId(), productRecordDto.getTempFileIds());
-            }
-        } catch (Exception e) {
-            return AjaxResult.error(e.getMessage());
-        }
-        return AjaxResult.success();
-    }
-
-}
diff --git a/src/main/java/com/ruoyi/other/service/TempFileService.java b/src/main/java/com/ruoyi/other/service/TempFileService.java
deleted file mode 100644
index 355e43c..0000000
--- a/src/main/java/com/ruoyi/other/service/TempFileService.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package com.ruoyi.other.service;
-
-import com.ruoyi.other.pojo.TempFile;
-import org.springframework.web.multipart.MultipartFile;
-
-import java.io.IOException;
-
-public interface TempFileService {
-    TempFile uploadFile(MultipartFile file,Integer type) throws IOException;
-
-    String uploadByCommon(MultipartFile file, Integer type, Long id) throws IOException;
-}
diff --git a/src/main/java/com/ruoyi/other/service/impl/TempFileServiceImpl.java b/src/main/java/com/ruoyi/other/service/impl/TempFileServiceImpl.java
deleted file mode 100644
index 093ddab..0000000
--- a/src/main/java/com/ruoyi/other/service/impl/TempFileServiceImpl.java
+++ /dev/null
@@ -1,184 +0,0 @@
-package com.ruoyi.other.service.impl;
-
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import com.ruoyi.common.utils.StringUtils;
-import com.ruoyi.other.mapper.TempFileMapper;
-import com.ruoyi.other.pojo.TempFile;
-import com.ruoyi.other.service.TempFileService;
-import com.ruoyi.sales.mapper.CommonFileMapper;
-import com.ruoyi.sales.pojo.CommonFile;
-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.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.Collections;
-import java.util.List;
-import java.util.UUID;
-
-@Service
-@Slf4j
-@RequiredArgsConstructor
-public class TempFileServiceImpl extends ServiceImpl<TempFileMapper, TempFile> implements TempFileService {
-
-    private final TempFileMapper tempFileMapper;
-    private final CommonFileMapper commonFileMapper;
-
-    @Value("${file.upload-dir}")
-    private String uploadDir;
-    @Value("${file.temp-dir}")
-    private String tempDir;
-
-    // 涓婁紶鍒颁复鏃剁洰褰�
-    @Override
-    public TempFile uploadFile(MultipartFile file,Integer type) throws IOException {
-        // 1. 鐢熸垚涓存椂鏂囦欢ID鍜岃矾寰�
-        String tempId = UUID.randomUUID().toString();
-        String originalFilename = file.getOriginalFilename();
-        if(originalFilename == null) throw new IOException("鏂囦欢鍚嶄笉鑳戒负绌�");
-//        URLEncoder urlEncoder = new URLEncoder();
-//        String encodedFilename = urlEncoder.encode(originalFilename, StandardCharsets.UTF_8);
-//        encodedFilename = encodedFilename.replaceAll("%2E",".");
-//        Path tempFilePath = Paths.get(tempDir, tempId + "_" + encodedFilename);
-
-        Path tempFilePath = Paths.get(tempDir, tempId + "_" + file.getOriginalFilename());
-
-        // 2. 纭繚鐩綍瀛樺湪
-        Path parentDir = tempFilePath.getParent();
-        if (parentDir != null) {
-            Files.createDirectories(parentDir); // 閫掑綊鍒涘缓鐩綍
-        }
-
-        // 3. 淇濆瓨鏂囦欢鍒颁复鏃剁洰褰�
-        file.transferTo(tempFilePath.toFile());
-
-        // 4. 淇濆瓨涓存椂鏂囦欢璁板綍
-        TempFile tempFileRecord = new TempFile();
-        tempFileRecord.setTempId(tempId);
-        tempFileRecord.setOriginalName(file.getOriginalFilename());
-        tempFileRecord.setTempPath(tempFilePath.toString());
-        tempFileRecord.setExpireTime(LocalDateTime.now().plusHours(2)); // 2灏忔椂鍚庤繃鏈�
-        tempFileRecord.setType(type);
-        tempFileRecord.setFileSize(file.getSize());
-        tempFileMapper.insert(tempFileRecord);
-        return tempFileRecord;
-    }
-
-    @Override
-    public String uploadByCommon(MultipartFile file, Integer type, Long id) throws  IOException{
-        TempFile tempFile = uploadFile(file, type);
-        if (tempFile != null) {
-            migrateTempFilesToFormal(id, Collections.singletonList(tempFile.getTempId()), type);
-            return tempFile.getTempPath();
-        }
-        return null;
-    }
-
-    /**
-     * 灏嗕复鏃舵枃浠惰縼绉诲埌姝e紡鐩綍
-     *
-     * @param businessId  涓氬姟ID锛堥攢鍞彴璐D锛�
-     * @param tempFileIds 涓存椂鏂囦欢ID鍒楄〃
-     * @param fileType     鏂囦欢绫诲瀷(鏉ヨ嚜FileNameType)
-     * @throws IOException 鏂囦欢鎿嶄綔寮傚父
-     */
-    public void migrateTempFilesToFormal(Long businessId, List<String> tempFileIds, Integer fileType) throws IOException {
-        if (com.baomidou.mybatisplus.core.toolkit.CollectionUtils.isEmpty(tempFileIds)) {
-            return;
-        }
-
-        // 鏋勫缓姝e紡鐩綍璺緞锛堟寜涓氬姟绫诲瀷鍜屾棩鏈熷垎缁勶級
-        String formalDir = uploadDir + LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE);
-
-        Path formalDirPath = Paths.get(formalDir);
-
-        // 纭繚姝e紡鐩綍瀛樺湪锛堥�掑綊鍒涘缓锛�
-        if (!Files.exists(formalDirPath)) {
-            Files.createDirectories(formalDirPath);
-        }
-
-        for (String tempFileId : tempFileIds) {
-            // 鏌ヨ涓存椂鏂囦欢璁板綍
-            TempFile tempFile = tempFileMapper.selectById(tempFileId);
-            if (tempFile == null) {
-                log.warn("涓存椂鏂囦欢涓嶅瓨鍦紝璺宠繃澶勭悊: {}", tempFileId);
-                continue;
-            }
-
-            // 鏋勫缓姝e紡鏂囦欢鍚嶏紙鍖呭惈涓氬姟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);
-                fileRecord.setFileSize(tempFile.getFileSize());
-                commonFileMapper.insert(fileRecord);
-
-                // 鍒犻櫎涓存椂鏂囦欢璁板綍
-                tempFileMapper.deleteById(tempFile);
-
-                log.info("鏂囦欢杩佺Щ鎴愬姛: {} -> {}", tempFile.getTempPath(), formalFilePath);
-            } catch (IOException e) {
-                log.error("鏂囦欢杩佺Щ澶辫触: {}", tempFile.getTempPath(), e);
-                // 鍙�夋嫨鍥炴粴浜嬪姟鎴栬褰曞け璐ユ枃浠�
-                throw new IOException("鏂囦欢杩佺Щ寮傚父", e);
-            }
-        }
-    }
-
-//    @Scheduled(cron = "0 0 3 * * ?") // 姣忓ぉ鍑屾櫒3鐐规墽琛�
-    public void cleanupExpiredTempFiles() {
-        LambdaQueryWrapper<TempFile> wrapper = new LambdaQueryWrapper<>();
-        wrapper.lt(TempFile::getExpireTime, LocalDateTime.now()); // expireTime < 褰撳墠鏃堕棿
-
-        List<TempFile> expiredFiles = tempFileMapper.selectList(wrapper);
-        for (TempFile file : expiredFiles) {
-            try {
-                // 鍒犻櫎鐗╃悊鏂囦欢
-                Files.deleteIfExists(Paths.get(file.getTempPath()));
-                // 鍒犻櫎鏁版嵁搴撹褰�
-                tempFileMapper.deleteById(file);
-                log.info("宸叉竻鐞嗚繃鏈熶复鏃舵枃浠�: {}", file.getTempPath());
-            } catch (IOException e) {
-                log.error("鍒犻櫎鏂囦欢澶辫触: {}", file.getTempPath(), e);
-                // 鍙�夋嫨璁板綍澶辫触鏃ュ織鎴栭噸璇�
-            }
-        }
-        log.info("杩囨湡涓存椂鏂囦欢娓呯悊瀹屾垚锛屽叡娓呯悊 {} 涓枃浠�", expiredFiles.size());
-    }
-}
diff --git a/src/main/java/com/ruoyi/procurementrecord/utils/StockUtils.java b/src/main/java/com/ruoyi/procurementrecord/utils/StockUtils.java
index 5c4f384..1434e24 100644
--- a/src/main/java/com/ruoyi/procurementrecord/utils/StockUtils.java
+++ b/src/main/java/com/ruoyi/procurementrecord/utils/StockUtils.java
@@ -4,7 +4,6 @@
 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;
@@ -14,15 +13,11 @@
 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
@@ -37,12 +32,13 @@
 
     /**
      * 涓嶅悎鏍煎叆搴�
+     *
      * @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));
@@ -53,12 +49,13 @@
 
     /**
      * 涓嶅悎鏍煎嚭搴�
+     *
      * @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));
@@ -74,7 +71,7 @@
      * @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));
@@ -85,12 +82,13 @@
 
     /**
      * 鍚堟牸鍑哄簱
+     *
      * @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));
@@ -115,6 +113,7 @@
         }
 
     }
+
     public void deleteStockOutRecord(Long recordId, String recordType) {
         StockOutRecord one = stockOutRecordService.getOne(new QueryWrapper<StockOutRecord>()
                 .lambda().eq(StockOutRecord::getRecordId, recordId)
diff --git a/src/main/java/com/ruoyi/production/bean/dto/ProductionAccountDto.java b/src/main/java/com/ruoyi/production/bean/dto/ProductionAccountDto.java
new file mode 100644
index 0000000..91b97ef
--- /dev/null
+++ b/src/main/java/com/ruoyi/production/bean/dto/ProductionAccountDto.java
@@ -0,0 +1,63 @@
+package com.ruoyi.production.bean.dto;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.ruoyi.production.pojo.ProductionAccount;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDate;
+
+@Data
+@Schema(name = "ProductionAccountDto", description = "production account query dto")
+public class ProductionAccountDto extends ProductionAccount {
+
+    @Schema(description = "sales contract no")
+    private String salesContractNo;
+
+    @Schema(description = "customer contract no")
+    private String customerContractNo;
+
+    @Schema(description = "project name")
+    private String projectName;
+
+    @Schema(description = "customer name")
+    private String customerName;
+
+    @Schema(description = "product category")
+    private String productCategory;
+
+    @Schema(description = "specification model")
+    private String specificationModel;
+
+    @Schema(description = "scheduling user id")
+    private Long schedulingUserId;
+
+    @Schema(description = "scheduling user name")
+    private String schedulingUserName;
+
+    @Schema(description = "process")
+    private String process;
+
+    @Schema(description = "date type(day/month)")
+    private String dateType;
+
+    @Schema(description = "day query date")
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    private LocalDate entryDate;
+
+    @Schema(description = "date range")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    private LocalDate[] dateRange;
+
+    @Schema(description = "start date")
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    private LocalDate entryDateStart;
+
+    @Schema(description = "end date")
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    private LocalDate entryDateEnd;
+}
diff --git a/src/main/java/com/ruoyi/production/bean/dto/ProductionOperationTaskDto.java b/src/main/java/com/ruoyi/production/bean/dto/ProductionOperationTaskDto.java
index d2653c6..4326c95 100644
--- a/src/main/java/com/ruoyi/production/bean/dto/ProductionOperationTaskDto.java
+++ b/src/main/java/com/ruoyi/production/bean/dto/ProductionOperationTaskDto.java
@@ -1,10 +1,34 @@
 package com.ruoyi.production.bean.dto;
 
 import com.ruoyi.production.pojo.ProductionOperationTask;
+import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
+
+import java.math.BigDecimal;
 
 @EqualsAndHashCode(callSuper = true)
 @Data
 public class ProductionOperationTaskDto extends ProductionOperationTask {
+
+    @Schema(description = "宸ュ簭鍚嶇О")
+    private String processName;
+
+    @Schema(description = "鐢熶骇璁㈠崟鍙�")
+    private String productOrderNpsNo;
+
+    @Schema(description = "浜у搧鍚嶇О")
+    private String productName;
+
+    @Schema(description = "瑙勬牸鍨嬪彿")
+    private String model;
+
+    @Schema(description = "鍗曚綅")
+    private String unit;
+
+    @Schema(description = "鎶ュ簾鏁伴噺")
+    private BigDecimal scrapQty;
+
+    @Schema(description = "瀹屾垚杩涘害")
+    private BigDecimal completionStatus;
 }
diff --git a/src/main/java/com/ruoyi/production/bean/dto/ProductionProductMainDto.java b/src/main/java/com/ruoyi/production/bean/dto/ProductionProductMainDto.java
index 1721e81..9fc8d86 100644
--- a/src/main/java/com/ruoyi/production/bean/dto/ProductionProductMainDto.java
+++ b/src/main/java/com/ruoyi/production/bean/dto/ProductionProductMainDto.java
@@ -12,57 +12,63 @@
 
 @EqualsAndHashCode(callSuper = true)
 @Data
-@Schema(name = "ProductionProductMainDto", description = "鐢熶骇鎶ュ伐鏌ヨ瀵硅薄")
+@Schema(name = "ProductionProductMainDto", description = "production report query dto")
 public class ProductionProductMainDto extends ProductionProductMain {
 
-    @Schema(description = "浜у搧宸ヨ壓璺嚎鏄庣粏ID")
+    @Schema(description = "product process route item id")
     private Long productProcessRouteItemId;
 
-    @Schema(description = "鐢熶骇鎶ュ伐琛╥d")
+    @Schema(description = "production report id")
     private Long productMainId;
 
-    @Schema(description = "绉熸埛ID")
+    @Schema(description = "tenant id")
     private Long tenantId;
 
-    @Schema(description = "宸ュ崟缂栧彿")
+    @Schema(description = "work order no")
     private String workOrderNo;
 
-    @Schema(description = "宸ュ崟鐘舵��")
+    @Schema(description = "work order status")
     private String workOrderStatus;
 
-    @Schema(description = "鏄电О")
+    @Schema(description = "nick name")
     private String nickName;
 
-    @Schema(description = "鏁伴噺")
+    @Schema(description = "quantity")
     private BigDecimal quantity;
 
-    @Schema(description = "鎶ュ簾鏁伴噺")
+    @Schema(description = "scrap quantity")
     private BigDecimal scrapQty;
 
-    @Schema(description = "浜у搧鍚嶇О")
+    @Schema(description = "product name")
     private String productName;
 
-    @Schema(description = "浜у搧鍨嬪彿鍚嶇О")
+    @Schema(description = "product model name")
     private String productModelName;
 
-    @Schema(description = "鍗曚綅")
+    @Schema(description = "unit")
     private String unit;
 
-    @Schema(description = "閿�鍞悎鍚岀紪鍙�")
+    @Schema(description = "sales contract no")
     private String salesContractNo;
 
-    @Schema(description = "鎺掍骇鏃ユ湡")
+    @Schema(description = "scheduling date")
     private LocalDate schedulingDate;
 
-    @Schema(description = "鎺掍骇浜哄憳鍚嶇О")
+    @Schema(description = "scheduling user name")
     private String schedulingUserName;
 
-    @Schema(description = "瀹㈡埛鍚嶇О")
+    @Schema(description = "customer name")
     private String customerName;
 
-    @Schema(description = "宸ュ簭")
+    @Schema(description = "process")
     private String process;
 
-    @Schema(description = "宸ュ簭鍙傛暟鍒楄〃")
+    @Schema(description = "salary quota")
+    private BigDecimal workHours;
+
+    @Schema(description = "wages")
+    private BigDecimal wages;
+
+    @Schema(description = "operation param list")
     private List<ProductionOrderRoutingOperationParam> productionOperationParamList;
 }
diff --git a/src/main/java/com/ruoyi/production/bean/vo/ProductionAccountVo.java b/src/main/java/com/ruoyi/production/bean/vo/ProductionAccountVo.java
new file mode 100644
index 0000000..5d86e7c
--- /dev/null
+++ b/src/main/java/com/ruoyi/production/bean/vo/ProductionAccountVo.java
@@ -0,0 +1,59 @@
+package com.ruoyi.production.bean.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+
+@Data
+@Schema(name = "ProductionAccountVo", description = "production account page result")
+public class ProductionAccountVo {
+
+    @Schema(description = "customer contract no")
+    private String customerContractNo;
+
+    @Schema(description = "project name")
+    private String projectName;
+
+    @Schema(description = "customer name")
+    private String customerName;
+
+    @Schema(description = "product category")
+    private String productCategory;
+
+    @Schema(description = "specification model")
+    private String specificationModel;
+
+    @Schema(description = "unit")
+    private String unit;
+
+    @Schema(description = "scheduling user id")
+    private Long schedulingUserId;
+
+    @Schema(description = "scheduling user name")
+    private String schedulingUserName;
+
+    @Schema(description = "wages")
+    private BigDecimal wages;
+
+    @Schema(description = "finished quantity")
+    private BigDecimal finishedNum;
+
+    @Schema(description = "salary quota")
+    private BigDecimal workHours;
+
+    @Schema(description = "output rate")
+    private String outputRate;
+
+    @Schema(description = "process")
+    private String process;
+
+    @Schema(description = "scheduling date")
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private LocalDate schedulingDate;
+
+    @Schema(description = "scheduling month(yyyy-MM)")
+    private String schedulingMonth;
+}
diff --git a/src/main/java/com/ruoyi/production/bean/vo/ProductionOperationTaskVo.java b/src/main/java/com/ruoyi/production/bean/vo/ProductionOperationTaskVo.java
index 0b56788..decdc39 100644
--- a/src/main/java/com/ruoyi/production/bean/vo/ProductionOperationTaskVo.java
+++ b/src/main/java/com/ruoyi/production/bean/vo/ProductionOperationTaskVo.java
@@ -26,9 +26,15 @@
     @Schema(description = "宸ュ簭鍚嶇О")
     private String operationName;
 
-    @Schema(description = "宸ュ崟绫诲瀷 姝e父 /杩斿伐杩斾慨")
+    @Schema(description = "宸ュ崟绫诲瀷 姝e父/杩斿伐杩斾慨")
     private String workOrderType;
 
     @Schema(description = "瀹屾垚杩涘害")
     private BigDecimal completionStatus;
+
+    @Schema(description = "鎶ュ伐浜哄憳鍚嶇О锛屽涓娇鐢ㄩ�楀彿鍒嗛殧")
+    private String userNames;
+
+    @Schema(description = "鏄惁缁撴潫锛�")
+    private Boolean endOrder;
 }
diff --git a/src/main/java/com/ruoyi/production/bean/vo/ProductionOrderVo.java b/src/main/java/com/ruoyi/production/bean/vo/ProductionOrderVo.java
index 0596973..f3b48a6 100644
--- a/src/main/java/com/ruoyi/production/bean/vo/ProductionOrderVo.java
+++ b/src/main/java/com/ruoyi/production/bean/vo/ProductionOrderVo.java
@@ -6,6 +6,7 @@
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 
+import java.math.BigDecimal;
 import java.util.List;
 
 @EqualsAndHashCode(callSuper = true)
@@ -32,4 +33,7 @@
 
     @Schema(description = "bom缂栧彿")
     private String bomNo;
+
+    @Schema(description = "瀹屾垚杩涘害")
+    private BigDecimal completionStatus;
 }
diff --git a/src/main/java/com/ruoyi/production/bean/vo/ProductionOrderWorkOrderDetailVo.java b/src/main/java/com/ruoyi/production/bean/vo/ProductionOrderWorkOrderDetailVo.java
new file mode 100644
index 0000000..ecbb0f2
--- /dev/null
+++ b/src/main/java/com/ruoyi/production/bean/vo/ProductionOrderWorkOrderDetailVo.java
@@ -0,0 +1,66 @@
+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;
+    }
+}
diff --git a/src/main/java/com/ruoyi/production/controller/ProductionAccountController.java b/src/main/java/com/ruoyi/production/controller/ProductionAccountController.java
index 9c34f2a..d87996f 100644
--- a/src/main/java/com/ruoyi/production/controller/ProductionAccountController.java
+++ b/src/main/java/com/ruoyi/production/controller/ProductionAccountController.java
@@ -1,5 +1,16 @@
 package com.ruoyi.production.controller;
 
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.framework.web.domain.R;
+import com.ruoyi.production.bean.dto.ProductionAccountDto;
+import com.ruoyi.production.bean.dto.ProductionProductMainDto;
+import com.ruoyi.production.bean.vo.ProductionAccountVo;
+import com.ruoyi.production.service.ProductionAccountService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
@@ -7,12 +18,24 @@
  * <p>
  * 鐢熶骇鏍哥畻琛� 鍓嶇鎺у埗鍣�
  * </p>
- *
- * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
- * @since 2026-04-21 03:55:52
  */
 @RestController
 @RequestMapping("/productionAccount")
+@RequiredArgsConstructor
+@Tag(name = "鐢熶骇鏍哥畻")
 public class ProductionAccountController {
 
+    private final ProductionAccountService productionAccountService;
+
+    @GetMapping("/listPage")
+    @Operation(summary = "鐢熶骇鏍哥畻鍒嗛〉鏌ヨ")
+    public R<IPage<ProductionAccountVo>> listPage(Page<ProductionAccountDto> page, ProductionAccountDto dto) {
+        return R.ok(productionAccountService.listPage(page, dto));
+    }
+
+    @GetMapping("/listProductionDetails")
+    @Operation(summary ="鏌ヨ宸ヤ汉鐢熶骇宸ヨ祫淇℃伅")
+    public R<IPage<ProductionProductMainDto>> listProductionDetails(ProductionAccountDto productionAccountDto, Page page) {
+        return R.ok(productionAccountService.listProductionDetails(productionAccountDto,page));
+    }
 }
diff --git a/src/main/java/com/ruoyi/production/controller/ProductionOperationTaskController.java b/src/main/java/com/ruoyi/production/controller/ProductionOperationTaskController.java
index e2c23ab..af6184a 100644
--- a/src/main/java/com/ruoyi/production/controller/ProductionOperationTaskController.java
+++ b/src/main/java/com/ruoyi/production/controller/ProductionOperationTaskController.java
@@ -6,18 +6,11 @@
 import com.ruoyi.production.bean.vo.ProductionOperationTaskVo;
 import com.ruoyi.production.pojo.ProductionOperationTask;
 import com.ruoyi.production.service.ProductionOperationTaskService;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.v3.oas.annotations.tags.Tag;
 import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.servlet.http.HttpServletResponse;
 import lombok.RequiredArgsConstructor;
-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.PostMapping;
-import org.springframework.web.bind.annotation.PutMapping;
-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.bind.annotation.*;
 
 import java.util.List;
 
@@ -71,4 +64,20 @@
         return R.ok(productionOperationTaskService.updateProductWorkOrder(dto));
     }
 
+    @Operation(summary = "鎸囨淳鎶ュ伐浜�")
+    @PostMapping("/assign")
+    public R<Boolean> assign(@RequestBody ProductionOperationTaskDto dto) {
+        return R.ok(productionOperationTaskService.assign(dto));
+    }
+
+    /**
+     * 宸ュ崟娴佽浆鍗′笅杞�
+     * @param response
+     * @param dto
+     */
+    @PostMapping("/down")
+    public void down(HttpServletResponse response, @RequestBody ProductionOperationTaskDto dto) {
+        productionOperationTaskService.down(response, dto);
+    }
+
 }
diff --git a/src/main/java/com/ruoyi/production/controller/ProductionOrderController.java b/src/main/java/com/ruoyi/production/controller/ProductionOrderController.java
index c152736..53a22a7 100644
--- a/src/main/java/com/ruoyi/production/controller/ProductionOrderController.java
+++ b/src/main/java/com/ruoyi/production/controller/ProductionOrderController.java
@@ -7,6 +7,7 @@
 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;
@@ -82,4 +83,16 @@
     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));
+    }
 }
diff --git a/src/main/java/com/ruoyi/production/controller/ProductionProductMainController.java b/src/main/java/com/ruoyi/production/controller/ProductionProductMainController.java
index 97fb2df..926a59f 100644
--- a/src/main/java/com/ruoyi/production/controller/ProductionProductMainController.java
+++ b/src/main/java/com/ruoyi/production/controller/ProductionProductMainController.java
@@ -9,6 +9,7 @@
 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;
@@ -50,6 +51,7 @@
      * @return
      */
     @PostMapping("/addProductMain")
+    @PreAuthorize("@ss.hasPermi('productionProductMain:add')")
     public R addProductMain(@RequestBody ProductionProductMainDto productionProductMainDto) {
         return R.ok(productionProductMainService.addProductMain(productionProductMainDto));
     }
diff --git a/src/main/java/com/ruoyi/production/mapper/ProductionAccountMapper.java b/src/main/java/com/ruoyi/production/mapper/ProductionAccountMapper.java
index e1ae36b..2cdc8d0 100644
--- a/src/main/java/com/ruoyi/production/mapper/ProductionAccountMapper.java
+++ b/src/main/java/com/ruoyi/production/mapper/ProductionAccountMapper.java
@@ -1,7 +1,11 @@
 package com.ruoyi.production.mapper;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.production.bean.dto.ProductionAccountDto;
 import com.ruoyi.production.bean.dto.UserAccountDto;
+import com.ruoyi.production.bean.vo.ProductionAccountVo;
 import com.ruoyi.production.pojo.ProductionAccount;
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Param;
@@ -19,6 +23,8 @@
  */
 @Mapper
 public interface ProductionAccountMapper extends BaseMapper<ProductionAccount> {
+    IPage<ProductionAccountVo> listPage(Page<ProductionAccountDto> page, @Param("c") ProductionAccountDto dto);
+
     UserAccountDto selectUserAccount(@Param("userId") Long userId, @Param("date") String date);
 
     List<Map<String, Object>> selectDailyWagesStats(@Param("startDate") String startDate, @Param("endDate") String endDate);
diff --git a/src/main/java/com/ruoyi/production/mapper/ProductionOperationTaskMapper.java b/src/main/java/com/ruoyi/production/mapper/ProductionOperationTaskMapper.java
index e23b611..4349537 100644
--- a/src/main/java/com/ruoyi/production/mapper/ProductionOperationTaskMapper.java
+++ b/src/main/java/com/ruoyi/production/mapper/ProductionOperationTaskMapper.java
@@ -40,4 +40,5 @@
                                                                            @Param("userId") Long userId,
                                                                            @Param("processIds") List<Long> processIds);
 
+    ProductionOperationTaskDto getProductWorkOrderFlowCard(@Param("id") Long id);
 }
diff --git a/src/main/java/com/ruoyi/production/mapper/ProductionProductMainMapper.java b/src/main/java/com/ruoyi/production/mapper/ProductionProductMainMapper.java
index c637caf..01b9912 100644
--- a/src/main/java/com/ruoyi/production/mapper/ProductionProductMainMapper.java
+++ b/src/main/java/com/ruoyi/production/mapper/ProductionProductMainMapper.java
@@ -3,8 +3,8 @@
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.production.bean.dto.ProductionAccountDto;
 import com.ruoyi.production.bean.dto.ProductionProductMainDto;
-import com.ruoyi.production.bean.dto.SalesLedgerProductionAccountingDto;
 import com.ruoyi.production.pojo.ProductionOrder;
 import com.ruoyi.production.pojo.ProductionProductMain;
 import org.apache.ibatis.annotations.Mapper;
@@ -30,7 +30,7 @@
      */
     ProductionOrder getOrderByMainId(@Param("productMainId") Long productMainId);
 
-    IPage<ProductionProductMainDto> listProductionDetails(@Param("ew") SalesLedgerProductionAccountingDto salesLedgerProductionAccountingDto, Page page);
+    IPage<ProductionProductMainDto> listProductionDetails(@Param("c") ProductionAccountDto productionAccountDto, Page page);
 
     ArrayList<Long> listMain(List<Long> idList);
 }
diff --git a/src/main/java/com/ruoyi/production/pojo/ProductionOrder.java b/src/main/java/com/ruoyi/production/pojo/ProductionOrder.java
index c7415b5..1f882b1 100644
--- a/src/main/java/com/ruoyi/production/pojo/ProductionOrder.java
+++ b/src/main/java/com/ruoyi/production/pojo/ProductionOrder.java
@@ -74,6 +74,10 @@
     @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;
 }
diff --git a/src/main/java/com/ruoyi/production/pojo/ProductionOrderRoutingOperationParam.java b/src/main/java/com/ruoyi/production/pojo/ProductionOrderRoutingOperationParam.java
index 752a839..04de53d 100644
--- a/src/main/java/com/ruoyi/production/pojo/ProductionOrderRoutingOperationParam.java
+++ b/src/main/java/com/ruoyi/production/pojo/ProductionOrderRoutingOperationParam.java
@@ -90,4 +90,7 @@
 
     @Schema(description = "鐢熶骇璁㈠崟宸ヨ壓璺嚎宸ュ簭ID")
     private Long productionOrderRoutingOperationId;
+
+    @Schema(description = "鐢熶骇鎶ュ伐琛↖D")
+    private Long productionProductMainId;
 }
diff --git a/src/main/java/com/ruoyi/production/pojo/ProductionProductOutput.java b/src/main/java/com/ruoyi/production/pojo/ProductionProductOutput.java
index badf048..5e1daef 100644
--- a/src/main/java/com/ruoyi/production/pojo/ProductionProductOutput.java
+++ b/src/main/java/com/ruoyi/production/pojo/ProductionProductOutput.java
@@ -23,7 +23,7 @@
     @Schema(description = "浜у搧id")
     private Long productModelId;
 
-    @Schema(description = "鎶ュ伐鏁伴噺(鎬绘暟閲�)")
+    @Schema(description = "鍚堟牸鏁伴噺")
     private BigDecimal quantity;
 
     @Schema(description = "鍒涘缓鏃堕棿")
diff --git a/src/main/java/com/ruoyi/production/service/ProductionAccountService.java b/src/main/java/com/ruoyi/production/service/ProductionAccountService.java
index 6c5ee53..a02c3a4 100644
--- a/src/main/java/com/ruoyi/production/service/ProductionAccountService.java
+++ b/src/main/java/com/ruoyi/production/service/ProductionAccountService.java
@@ -1,5 +1,10 @@
 package com.ruoyi.production.service;
 
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.production.bean.dto.ProductionAccountDto;
+import com.ruoyi.production.bean.dto.ProductionProductMainDto;
+import com.ruoyi.production.bean.vo.ProductionAccountVo;
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.ruoyi.production.pojo.ProductionAccount;
 
@@ -12,5 +17,7 @@
  * @since 2026-04-21 03:55:52
  */
 public interface ProductionAccountService extends IService<ProductionAccount> {
+    IPage<ProductionAccountVo> listPage(Page<ProductionAccountDto> page, ProductionAccountDto dto);
 
+    IPage<ProductionProductMainDto> listProductionDetails(ProductionAccountDto productionAccountDto, Page page);
 }
diff --git a/src/main/java/com/ruoyi/production/service/ProductionOperationTaskService.java b/src/main/java/com/ruoyi/production/service/ProductionOperationTaskService.java
index 4ff1e16..2735cc3 100644
--- a/src/main/java/com/ruoyi/production/service/ProductionOperationTaskService.java
+++ b/src/main/java/com/ruoyi/production/service/ProductionOperationTaskService.java
@@ -6,6 +6,7 @@
 import com.ruoyi.production.bean.dto.ProductionOperationTaskDto;
 import com.ruoyi.production.bean.vo.ProductionOperationTaskVo;
 import com.ruoyi.production.pojo.ProductionOperationTask;
+import jakarta.servlet.http.HttpServletResponse;
 
 import java.util.List;
 
@@ -23,4 +24,8 @@
     boolean removeProductionOperationTask(List<Long> ids);
 
     int updateProductWorkOrder(ProductionOperationTaskDto dto);
+
+    boolean assign(ProductionOperationTaskDto dto);
+
+    void down(HttpServletResponse response, ProductionOperationTaskDto dto);
 }
diff --git a/src/main/java/com/ruoyi/production/service/ProductionOrderService.java b/src/main/java/com/ruoyi/production/service/ProductionOrderService.java
index fa1186c..7e588dc 100644
--- a/src/main/java/com/ruoyi/production/service/ProductionOrderService.java
+++ b/src/main/java/com/ruoyi/production/service/ProductionOrderService.java
@@ -7,6 +7,7 @@
 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;
@@ -30,4 +31,8 @@
     List<ProductionPlanVo> getSource(Long id);
 
     List<ProductionOrderPickVo> pick(Long productionOrderId);
+
+    ProductionOrderWorkOrderDetailVo getWorkOrderReportInspectDetail(Long productionOrderId);
+
+    int updateOrder(ProductionOrderDto productionOrderDto);
 }
diff --git a/src/main/java/com/ruoyi/production/service/impl/ProductionAccountServiceImpl.java b/src/main/java/com/ruoyi/production/service/impl/ProductionAccountServiceImpl.java
index e8e3126..9c1843c 100644
--- a/src/main/java/com/ruoyi/production/service/impl/ProductionAccountServiceImpl.java
+++ b/src/main/java/com/ruoyi/production/service/impl/ProductionAccountServiceImpl.java
@@ -1,20 +1,73 @@
 package com.ruoyi.production.service.impl;
 
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.production.bean.dto.ProductionAccountDto;
+import com.ruoyi.production.bean.dto.ProductionProductMainDto;
+import com.ruoyi.production.bean.vo.ProductionAccountVo;
 import com.ruoyi.production.mapper.ProductionAccountMapper;
+import com.ruoyi.production.mapper.ProductionProductMainMapper;
 import com.ruoyi.production.pojo.ProductionAccount;
 import com.ruoyi.production.service.ProductionAccountService;
+import lombok.RequiredArgsConstructor;
 import org.springframework.stereotype.Service;
 
-/**
- * <p>
- * 鐢熶骇鏍哥畻琛� 鏈嶅姟瀹炵幇绫�
- * </p>
- *
- * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
- * @since 2026-04-21 03:55:52
- */
+import java.time.LocalDate;
+
 @Service
+@RequiredArgsConstructor
 public class ProductionAccountServiceImpl extends ServiceImpl<ProductionAccountMapper, ProductionAccount> implements ProductionAccountService {
 
+
+    private final ProductionProductMainMapper productionProductMainMapper;
+
+    @Override
+    public IPage<ProductionAccountVo> listPage(Page<ProductionAccountDto> page, ProductionAccountDto dto) {
+        ProductionAccountDto queryDto = normalizeDateQuery(dto);
+        return baseMapper.listPage(page, queryDto);
+    }
+
+    @Override
+    public IPage<ProductionProductMainDto> listProductionDetails(ProductionAccountDto dto, Page page) {
+        return productionProductMainMapper.listProductionDetails(normalizeDateQuery(dto), page);
+    }
+
+    private ProductionAccountDto normalizeDateQuery(ProductionAccountDto dto) {
+        if (dto == null) {
+            return new ProductionAccountDto();
+        }
+        LocalDate[] dateRange = dto.getDateRange();
+        if ((dto.getEntryDateStart() == null || dto.getEntryDateEnd() == null)
+                && dateRange != null
+                && dateRange.length > 0) {
+            if (dto.getEntryDateStart() == null) {
+                dto.setEntryDateStart(dateRange[0]);
+            }
+            if (dto.getEntryDateEnd() == null) {
+                dto.setEntryDateEnd(dateRange.length > 1 ? dateRange[1] : dateRange[0]);
+            }
+        }
+
+        String dateType = dto.getDateType();
+        if ("day".equalsIgnoreCase(dateType)) {
+            if (dto.getEntryDate() == null && dateRange != null && dateRange.length > 0) {
+                dto.setEntryDate(dateRange[0]);
+            }
+            if (dto.getEntryDate() == null) {
+                dto.setEntryDate(dto.getEntryDateStart());
+            }
+            dto.setEntryDateStart(null);
+            dto.setEntryDateEnd(null);
+        } else if ("month".equalsIgnoreCase(dateType)) {
+            if ((dto.getEntryDateStart() == null || dto.getEntryDateEnd() == null) && dto.getEntryDate() != null) {
+                LocalDate monthDate = dto.getEntryDate();
+                dto.setEntryDateStart(monthDate.withDayOfMonth(1));
+                dto.setEntryDateEnd(monthDate.withDayOfMonth(monthDate.lengthOfMonth()));
+            }
+            dto.setEntryDate(null);
+        }
+        return dto;
+    }
+
 }
diff --git a/src/main/java/com/ruoyi/production/service/impl/ProductionOperationTaskServiceImpl.java b/src/main/java/com/ruoyi/production/service/impl/ProductionOperationTaskServiceImpl.java
index 8553869..aad1350 100644
--- a/src/main/java/com/ruoyi/production/service/impl/ProductionOperationTaskServiceImpl.java
+++ b/src/main/java/com/ruoyi/production/service/impl/ProductionOperationTaskServiceImpl.java
@@ -1,42 +1,91 @@
 package com.ruoyi.production.service.impl;
 
 import cn.hutool.core.bean.BeanUtil;
+import com.alibaba.fastjson2.JSON;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
 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.deepoove.poi.XWPFTemplate;
+import com.deepoove.poi.data.PictureRenderData;
+import com.deepoove.poi.data.PictureType;
+import com.deepoove.poi.data.Pictures;
+import com.ruoyi.basic.dto.StorageAttachmentDTO;
+import com.ruoyi.basic.dto.StorageBlobVO;
+import com.ruoyi.basic.enums.RecordTypeEnum;
+import com.ruoyi.basic.utils.FileUtil;
+import com.ruoyi.common.config.FileProperties;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.MatrixToImageWriter;
+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;
+import com.ruoyi.project.system.mapper.SysUserMapper;
+import jakarta.servlet.http.HttpServletResponse;
 import lombok.RequiredArgsConstructor;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 
-import java.util.List;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URLEncoder;
+import java.util.*;
+import java.util.stream.Collectors;
 
 @Service
 @RequiredArgsConstructor
 public class ProductionOperationTaskServiceImpl extends ServiceImpl<ProductionOperationTaskMapper, ProductionOperationTask> implements ProductionOperationTaskService {
 
+    private final SysUserMapper sysUserMapper;
+    private final ProductionOrderMapper productionOrderMapper;
 
+    private final FileUtil fileUtil;
+
+    private final FileProperties fileProperties;
+
+    @Value("${file.temp-dir}")
+    private String tempDir;
 
     @Override
     public IPage<ProductionOperationTaskVo> pageProductionOperationTask(Page<ProductionOperationTaskDto> page, ProductionOperationTaskDto dto) {
         Page<ProductionOperationTaskVo> voPage = new Page<>(page.getCurrent(), page.getSize(), page.getTotal());
-        return baseMapper.pageProductionOperationTask(voPage, dto);
+        IPage<ProductionOperationTaskVo> result = baseMapper.pageProductionOperationTask(voPage, dto);
+        fillUserNames(result.getRecords());
+        return result;
     }
 
     @Override
     public List<ProductionOperationTaskVo> listProductionOperationTask(ProductionOperationTaskDto dto) {
-        return BeanUtil.copyToList(this.list(buildQueryWrapper(dto)), ProductionOperationTaskVo.class);
+        List<ProductionOperationTaskVo> result = BeanUtil.copyToList(this.list(buildQueryWrapper(dto)), ProductionOperationTaskVo.class);
+        fillUserNames(result);
+        return result;
     }
 
     @Override
     public ProductionOperationTaskVo getProductionOperationTaskInfo(Long id) {
         ProductionOperationTask item = this.getById(id);
-        return item == null ? null : BeanUtil.copyProperties(item, ProductionOperationTaskVo.class);
+        if (item == null) {
+            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;
     }
 
     @Override
@@ -47,6 +96,27 @@
     @Override
     public boolean removeProductionOperationTask(List<Long> ids) {
         return ids != null && !ids.isEmpty() && this.removeByIds(ids);
+    }
+
+    @Override
+    public int updateProductWorkOrder(ProductionOperationTaskDto dto) {
+        return baseMapper.updateById(dto);
+    }
+
+    @Override
+    public boolean assign(ProductionOperationTaskDto dto) {
+        if (dto == null || dto.getId() == null) {
+            throw new ServiceException("宸ュ崟ID涓嶈兘涓虹┖");
+        }
+
+        ProductionOperationTask update = new ProductionOperationTask();
+        update.setId(dto.getId());
+        update.setUserIds(dto.getUserIds());
+        int rows = baseMapper.updateById(update);
+        if (rows <= 0) {
+            throw new ServiceException("宸ュ崟涓嶅瓨鍦ㄦ垨宸插垹闄�");
+        }
+        return true;
     }
 
     private LambdaQueryWrapper<ProductionOperationTask> buildQueryWrapper(ProductionOperationTaskDto dto) {
@@ -62,8 +132,219 @@
                 .orderByDesc(ProductionOperationTask::getId);
     }
 
+    private void fillUserNames(List<ProductionOperationTaskVo> voList) {
+        if (voList == null || voList.isEmpty()) {
+            return;
+        }
+        Set<Long> userIdSet = new LinkedHashSet<>();
+        for (ProductionOperationTaskVo vo : voList) {
+            if (vo == null) {
+                continue;
+            }
+            userIdSet.addAll(parseUserIdList(vo.getUserIds(), false));
+        }
+        if (userIdSet.isEmpty()) {
+            return;
+        }
+        List<SysUser> userList = sysUserMapper.selectUsersByIds(new ArrayList<>(userIdSet));
+        if (userList == null || userList.isEmpty()) {
+            return;
+        }
+        Map<Long, String> userNameById = userList.stream()
+                .filter(item -> item.getUserId() != null)
+                .collect(Collectors.toMap(SysUser::getUserId, SysUser::getNickName, (left, right) -> left));
+
+        for (ProductionOperationTaskVo vo : voList) {
+            if (vo == null) {
+                continue;
+            }
+            List<Long> userIds = parseUserIdList(vo.getUserIds(), false);
+            if (userIds.isEmpty()) {
+                vo.setUserNames(null);
+                continue;
+            }
+            String userNames = userIds.stream()
+                    .map(userNameById::get)
+                    .filter(StringUtils::isNotBlank)
+                    .collect(Collectors.joining(","));
+            vo.setUserNames(userNames);
+        }
+    }
+
+    private List<Long> parseUserIdList(String userIds, boolean strict) {
+        if (StringUtils.isBlank(userIds)) {
+            if (strict) {
+                throw new ServiceException("userIds鏍煎紡涓嶆纭紝蹇呴』涓篔SON鏁板瓧鏁扮粍");
+            }
+            return new ArrayList<>();
+        }
+
+        String text = userIds.trim();
+        try {
+            List<Long> parsed = JSON.parseArray(text, Long.class);
+            LinkedHashSet<Long> idSet = parsed == null ? new LinkedHashSet<>() : parsed.stream()
+                    .filter(Objects::nonNull)
+                    .collect(Collectors.toCollection(LinkedHashSet::new));
+            if (strict && idSet.isEmpty()) {
+                throw new ServiceException("userIds鏍煎紡涓嶆纭紝蹇呴』涓篔SON鏁板瓧鏁扮粍");
+            }
+            return new ArrayList<>(idSet);
+        } catch (Exception e) {
+            if (strict) {
+                throw new ServiceException("userIds鏍煎紡涓嶆纭紝蹇呴』涓篔SON鏁板瓧鏁扮粍");
+            }
+            return new ArrayList<>();
+        }
+    }
+
     @Override
-    public int updateProductWorkOrder(ProductionOperationTaskDto dto) {
-        return baseMapper.updateById(dto);
+    public void down(HttpServletResponse response, ProductionOperationTaskDto dto) {
+        if (dto == null || dto.getId() == null) {
+            throw new ServiceException("宸ュ崟ID涓嶈兘涓虹┖");
+        }
+
+        ProductionOperationTaskDto taskDto = baseMapper.getProductWorkOrderFlowCard(dto.getId());
+        if (taskDto == null) {
+            throw new ServiceException("宸ュ崟涓嶅瓨鍦紝ID=" + dto.getId());
+        }
+        String codePath;
+        try {
+            codePath = new MatrixToImageWriter().code(taskDto.getId().toString(), tempDir);
+        } catch (Exception e) {
+            throw new ServiceException("鐢熸垚浜岀淮鐮佸け璐�");
+        }
+
+        List<Map<String, Object>> images = buildTaskAttachmentImages(taskDto.getId());
+        Map<String, Object> renderData = new HashMap<>();
+        renderData.put("process", taskDto.getProcessName());
+        renderData.put("workOrderNo", taskDto.getWorkOrderNo());
+        renderData.put("productOrderNpsNo", taskDto.getProductOrderNpsNo());
+        renderData.put("productName", taskDto.getProductName());
+        renderData.put("planQuantity", taskDto.getPlanQuantity());
+        renderData.put("model", taskDto.getModel());
+        renderData.put("completeQuantity", taskDto.getCompleteQuantity());
+        renderData.put("scrapQty", taskDto.getScrapQty());
+        renderData.put("planStartTime", taskDto.getPlanStartTime());
+        renderData.put("planEndTime", taskDto.getPlanEndTime());
+        renderData.put("actualStartTime", taskDto.getActualStartTime());
+        renderData.put("actualEndTime", taskDto.getActualEndTime());
+        renderData.put("twoCode", Pictures.ofLocal(codePath).create());
+        renderData.put("images", images.isEmpty() ? null : images);
+
+        try (InputStream inputStream = this.getClass().getResourceAsStream("/static/work-order-template.docx")) {
+            if (inputStream == null) {
+                throw new ServiceException("娴佽浆鍗℃ā鏉夸笉瀛樺湪");
+            }
+            XWPFTemplate template = XWPFTemplate.compile(inputStream).render(renderData);
+            response.setContentType("application/msword");
+            String fileName = URLEncoder.encode("娴佽浆鍗�", "UTF-8");
+            response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
+            response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".docx");
+            try (OutputStream os = response.getOutputStream()) {
+                template.write(os);
+                os.flush();
+            }
+        } catch (Exception e) {
+            throw new RuntimeException("瀵煎嚭澶辫触");
+        }
+    }
+
+    private List<Map<String, Object>> buildTaskAttachmentImages(Long taskId) {
+        List<Map<String, Object>> images = new ArrayList<>();
+        StorageAttachmentDTO storageAttachmentDTO = new StorageAttachmentDTO();
+        storageAttachmentDTO.setRecordType(RecordTypeEnum.PRODUCTION_OPERATION_TASK.getType());
+        storageAttachmentDTO.setRecordId(taskId);
+        List<StorageBlobVO> taskWorkOrderFiles =
+                fileUtil.getStorageBlobVOsByApplicationAndRecordTypeAndRecordId(storageAttachmentDTO);
+        if (CollectionUtils.isEmpty(taskWorkOrderFiles)) {
+            return images;
+        }
+        for (StorageBlobVO blobVO : taskWorkOrderFiles) {
+            if (blobVO == null) {
+                continue;
+            }
+            PictureType pictureType = resolvePictureType(blobVO);
+            if (pictureType == null) {
+                continue;
+            }
+            File imageFile = resolveImageFile(blobVO);
+            if (imageFile == null || !imageFile.exists() || !imageFile.isFile()) {
+                continue;
+            }
+            try (InputStream imageInputStream = new FileInputStream(imageFile)) {
+                Map<String, Object> image = new HashMap<>();
+                PictureRenderData pictureRenderData = Pictures.ofStream(imageInputStream, pictureType)
+                        .sizeInCm(17, 20)
+                        .create();
+                image.put("url", pictureRenderData);
+                images.add(image);
+            } catch (Exception ignored) {
+                // 鍗曚釜闄勪欢瑙f瀽澶辫触鏃惰烦杩囷紝閬垮厤褰卞搷鏁翠釜娴佽浆鍗″鍑�
+            }
+        }
+        return images;
+    }
+
+    private File resolveImageFile(StorageBlobVO blobVO) {
+        if (blobVO == null || StringUtils.isBlank(blobVO.getUidFilename())) {
+            return null;
+        }
+        if (StringUtils.isBlank(blobVO.getPath())) {
+            return new File(fileProperties.getPath(), blobVO.getUidFilename());
+        }
+        return new File(new File(fileProperties.getPath(), blobVO.getPath()), blobVO.getUidFilename());
+    }
+
+    private PictureType resolvePictureType(StorageBlobVO blobVO) {
+        if (blobVO == null) {
+            return null;
+        }
+        PictureType type = parsePictureTypeByFileName(blobVO.getOriginalFilename());
+        if (type != null) {
+            return type;
+        }
+        type = parsePictureTypeByFileName(blobVO.getUidFilename());
+        if (type != null) {
+            return type;
+        }
+        return parsePictureTypeByContentType(blobVO.getContentType());
+    }
+
+    private PictureType parsePictureTypeByFileName(String fileName) {
+        if (StringUtils.isBlank(fileName) || !fileName.contains(".")) {
+            return null;
+        }
+        try {
+            return PictureType.suggestFileType(fileName);
+        } catch (Exception ex) {
+            return null;
+        }
+    }
+
+    private PictureType parsePictureTypeByContentType(String contentType) {
+        if (StringUtils.isBlank(contentType)) {
+            return null;
+        }
+        String normalized = contentType.trim().toLowerCase(Locale.ROOT);
+        switch (normalized) {
+            case "image/jpeg":
+            case "image/jpg":
+            case "image/pjpeg":
+                return PictureType.JPEG;
+            case "image/png":
+                return PictureType.PNG;
+            case "image/gif":
+                return PictureType.GIF;
+            case "image/bmp":
+            case "image/x-ms-bmp":
+                return PictureType.BMP;
+            case "image/tiff":
+            case "image/tif":
+                return PictureType.TIFF;
+            case "image/svg+xml":
+                return PictureType.SVG;
+            default:
+                return null;
+        }
     }
 }
diff --git a/src/main/java/com/ruoyi/production/service/impl/ProductionOrderPickServiceImpl.java b/src/main/java/com/ruoyi/production/service/impl/ProductionOrderPickServiceImpl.java
index beedbb5..72a237e 100644
--- a/src/main/java/com/ruoyi/production/service/impl/ProductionOrderPickServiceImpl.java
+++ b/src/main/java/com/ruoyi/production/service/impl/ProductionOrderPickServiceImpl.java
@@ -3,6 +3,7 @@
 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;
@@ -18,6 +19,7 @@
 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;
@@ -46,6 +48,7 @@
     private final ProductionOperationTaskMapper productionOperationTaskMapper;
     private final ProductionOrderPickRecordMapper productionOrderPickRecordMapper;
     private final StockInventoryMapper stockInventoryMapper;
+    private final StockInventoryService stockInventoryService;
 
     @Override
     @Transactional(rollbackFor = Exception.class)
@@ -59,7 +62,7 @@
             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());
@@ -234,7 +237,7 @@
         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());
@@ -296,7 +299,7 @@
         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);
@@ -388,13 +391,13 @@
         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);
@@ -457,22 +460,61 @@
     }
 
     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));
         }
     }
 
@@ -481,25 +523,13 @@
         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("搴撳瓨鍥為��澶辫触锛屼骇鍝佽鏍糏D=" + productModelId);
-        }
+        stockInventoryDto.setRecordType(String.valueOf(StockInQualifiedRecordTypeEnum.PICK_RETURN_IN.getCode()));
+        stockInventoryDto.setRecordId(0L);
+        stockInventoryService.addStockInRecordOnly(stockInventoryDto);
     }
 
     private List<ProductionOrderPickDto> resolvePickItems(ProductionOrderPickDto dto) {
@@ -882,4 +912,8 @@
     private BigDecimal defaultDecimal(BigDecimal value) {
         return value == null ? BigDecimal.ZERO : value;
     }
+
+    private String formatQuantity(BigDecimal value) {
+        return defaultDecimal(value).stripTrailingZeros().toPlainString();
+    }
 }
diff --git a/src/main/java/com/ruoyi/production/service/impl/ProductionOrderRoutingOperationParamServiceImpl.java b/src/main/java/com/ruoyi/production/service/impl/ProductionOrderRoutingOperationParamServiceImpl.java
index 59094be..a99b194 100644
--- a/src/main/java/com/ruoyi/production/service/impl/ProductionOrderRoutingOperationParamServiceImpl.java
+++ b/src/main/java/com/ruoyi/production/service/impl/ProductionOrderRoutingOperationParamServiceImpl.java
@@ -75,6 +75,10 @@
         ProductionOrderRoutingOperationParam query = dto == null ? new ProductionOrderRoutingOperationParam() : dto;
         return Wrappers.<ProductionOrderRoutingOperationParam>lambdaQuery()
                 .eq(query.getId() != null, ProductionOrderRoutingOperationParam::getId, query.getId())
+                .eq(query.getProductionProductMainId() != null,
+                        ProductionOrderRoutingOperationParam::getProductionProductMainId, query.getProductionProductMainId())
+                .isNull(query.getProductionProductMainId() == null,
+                        ProductionOrderRoutingOperationParam::getProductionProductMainId)
                 .eq(query.getProductionOrderId() != null, ProductionOrderRoutingOperationParam::getProductionOrderId, query.getProductionOrderId())
                 .eq(query.getProductionOrderRoutingOperationId() != null, ProductionOrderRoutingOperationParam::getProductionOrderRoutingOperationId, query.getProductionOrderRoutingOperationId())
                 .eq(query.getTechnologyOperationId() != null,
@@ -148,6 +152,7 @@
     private void checkDuplicate(ProductionOrderRoutingOperationParam item) {
         boolean duplicate = productionOrderRoutingOperationParamMapper.selectCount(
                 Wrappers.<ProductionOrderRoutingOperationParam>lambdaQuery()
+                        .isNull(ProductionOrderRoutingOperationParam::getProductionProductMainId)
                         .eq(ProductionOrderRoutingOperationParam::getProductionOrderRoutingOperationId, item.getProductionOrderRoutingOperationId())
                         .eq(item.getTechnologyRoutingOperationParamId() != null,
                                 ProductionOrderRoutingOperationParam::getTechnologyRoutingOperationParamId, item.getTechnologyRoutingOperationParamId())
diff --git a/src/main/java/com/ruoyi/production/service/impl/ProductionOrderServiceImpl.java b/src/main/java/com/ruoyi/production/service/impl/ProductionOrderServiceImpl.java
index f994bb6..90c5e5c 100644
--- a/src/main/java/com/ruoyi/production/service/impl/ProductionOrderServiceImpl.java
+++ b/src/main/java/com/ruoyi/production/service/impl/ProductionOrderServiceImpl.java
@@ -19,9 +19,16 @@
 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;
@@ -51,8 +58,12 @@
     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;
@@ -219,8 +230,8 @@
         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)
@@ -228,6 +239,11 @@
                                 .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();
@@ -236,19 +252,23 @@
             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()
@@ -661,6 +681,168 @@
     }
 
     @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();
@@ -733,4 +915,10 @@
         }
         return new ArrayList<>(mergedPickMap.values());
     }
+
+    @Override
+    public int updateOrder(ProductionOrderDto productionOrderDto) {
+        productionOrderDto.setStatus(5);
+        return baseMapper.updateById(productionOrderDto);
+    }
 }
diff --git a/src/main/java/com/ruoyi/production/service/impl/ProductionProductMainServiceImpl.java b/src/main/java/com/ruoyi/production/service/impl/ProductionProductMainServiceImpl.java
index abbc525..150fc89 100644
--- a/src/main/java/com/ruoyi/production/service/impl/ProductionProductMainServiceImpl.java
+++ b/src/main/java/com/ruoyi/production/service/impl/ProductionProductMainServiceImpl.java
@@ -27,6 +27,8 @@
 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;
@@ -40,10 +42,14 @@
 import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
 import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Set;
 import java.util.stream.Collectors;
 
 @Service
@@ -72,10 +78,13 @@
     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) {
-        return productionProductMainMapper.listPageProductionProductMainDto(page, productionProductMainDto);
+        IPage<ProductionProductMainDto> result = productionProductMainMapper.listPageProductionProductMainDto(page, productionProductMainDto);
+        fillOperationParamList(result.getRecords());
+        return result;
     }
 
     @Override
@@ -85,9 +94,119 @@
 
     @Override
     public ProductionProductMainDto getProductionProductMainInfo(Long id) {
-        return productionProductMainMapper.listPageProductionProductMainDto(new Page<>(1, 1), new ProductionProductMainDto() {{
+        return listPageProductionProductMainDto(new Page<>(1, 1), new ProductionProductMainDto() {{
             setId(id);
         }}).getRecords().stream().findFirst().orElse(null);
+    }
+
+    private void fillOperationParamList(List<ProductionProductMainDto> recordList) {
+        if (recordList == null || recordList.isEmpty()) {
+            return;
+        }
+        Set<Long> mainIdSet = recordList.stream()
+                .map(ProductionProductMainDto::getId)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toCollection(LinkedHashSet::new));
+        if (mainIdSet.isEmpty()) {
+            recordList.forEach(item -> item.setProductionOperationParamList(Collections.emptyList()));
+            return;
+        }
+
+        List<ProductionOrderRoutingOperationParam> paramList = productionOrderRoutingOperationParamMapper.selectList(
+                Wrappers.<ProductionOrderRoutingOperationParam>lambdaQuery()
+                        .in(ProductionOrderRoutingOperationParam::getProductionProductMainId, mainIdSet)
+                        .orderByAsc(ProductionOrderRoutingOperationParam::getId));
+        Map<Long, List<ProductionOrderRoutingOperationParam>> paramGroupMap = new HashMap<>();
+        for (ProductionOrderRoutingOperationParam param : paramList) {
+            if (param == null || param.getProductionProductMainId() == null) {
+                continue;
+            }
+            paramGroupMap.computeIfAbsent(param.getProductionProductMainId(), key -> new ArrayList<>()).add(param);
+        }
+
+        Set<Long> missingMainIdSet = new LinkedHashSet<>();
+        for (ProductionProductMainDto item : recordList) {
+            Long mainId = item.getId();
+            if (mainId == null) {
+                item.setProductionOperationParamList(Collections.emptyList());
+                continue;
+            }
+            List<ProductionOrderRoutingOperationParam> params = paramGroupMap.get(mainId);
+            if (params != null && !params.isEmpty()) {
+                item.setProductionOperationParamList(params);
+                continue;
+            }
+            missingMainIdSet.add(mainId);
+        }
+        if (missingMainIdSet.isEmpty()) {
+            return;
+        }
+
+        // 鍏煎鍘嗗彶鏁版嵁锛氭棫鎶ュ伐璁板綍娌℃湁鎸夋姤宸D钀藉弬鏁板揩鐓ф椂锛屽洖閫�灞曠ず宸ュ簭妯℃澘鍙傛暟銆�
+        List<ProductionProductMain> mainList = productionProductMainMapper.selectBatchIds(missingMainIdSet);
+        Map<Long, Long> mainIdToTaskIdMap = mainList.stream()
+                .filter(Objects::nonNull)
+                .filter(item -> item.getId() != null)
+                .collect(Collectors.toMap(ProductionProductMain::getId,
+                        ProductionProductMain::getProductionOperationTaskId, (left, right) -> left));
+        Set<Long> taskIdSet = mainIdToTaskIdMap.values().stream()
+                .filter(Objects::nonNull)
+                .collect(Collectors.toCollection(LinkedHashSet::new));
+        if (taskIdSet.isEmpty()) {
+            for (ProductionProductMainDto item : recordList) {
+                if (item.getId() != null && missingMainIdSet.contains(item.getId())) {
+                    item.setProductionOperationParamList(Collections.emptyList());
+                }
+            }
+            return;
+        }
+
+        List<ProductionOperationTask> taskList = productionOperationTaskMapper.selectList(
+                Wrappers.<ProductionOperationTask>lambdaQuery()
+                        .in(ProductionOperationTask::getId, taskIdSet));
+        Map<Long, Long> taskIdToRoutingOperationIdMap = taskList.stream()
+                .filter(Objects::nonNull)
+                .filter(item -> item.getId() != null)
+                .collect(Collectors.toMap(ProductionOperationTask::getId,
+                        ProductionOperationTask::getProductionOrderRoutingOperationId, (left, right) -> left));
+        Set<Long> routingOperationIdSet = taskIdToRoutingOperationIdMap.values().stream()
+                .filter(Objects::nonNull)
+                .collect(Collectors.toCollection(LinkedHashSet::new));
+        if (routingOperationIdSet.isEmpty()) {
+            for (ProductionProductMainDto item : recordList) {
+                if (item.getId() != null && missingMainIdSet.contains(item.getId())) {
+                    item.setProductionOperationParamList(Collections.emptyList());
+                }
+            }
+            return;
+        }
+
+        List<ProductionOrderRoutingOperationParam> fallbackParamList = productionOrderRoutingOperationParamMapper.selectList(
+                Wrappers.<ProductionOrderRoutingOperationParam>lambdaQuery()
+                        .in(ProductionOrderRoutingOperationParam::getProductionOrderRoutingOperationId, routingOperationIdSet)
+                        .isNull(ProductionOrderRoutingOperationParam::getProductionProductMainId)
+                        .orderByAsc(ProductionOrderRoutingOperationParam::getId));
+        Map<Long, List<ProductionOrderRoutingOperationParam>> fallbackGroupMap = new HashMap<>();
+        for (ProductionOrderRoutingOperationParam param : fallbackParamList) {
+            if (param == null || param.getProductionOrderRoutingOperationId() == null) {
+                continue;
+            }
+            fallbackGroupMap.computeIfAbsent(param.getProductionOrderRoutingOperationId(), key -> new ArrayList<>()).add(param);
+        }
+
+        for (ProductionProductMainDto item : recordList) {
+            Long mainId = item.getId();
+            if (mainId == null || !missingMainIdSet.contains(mainId)) {
+                continue;
+            }
+            Long taskId = mainIdToTaskIdMap.get(mainId);
+            Long routingOperationId = taskId == null ? null : taskIdToRoutingOperationIdMap.get(taskId);
+            if (routingOperationId == null) {
+                item.setProductionOperationParamList(Collections.emptyList());
+                continue;
+            }
+            item.setProductionOperationParamList(fallbackGroupMap.getOrDefault(routingOperationId, Collections.emptyList()));
+        }
     }
 
     @Override
@@ -132,7 +251,6 @@
         if (productionOrder == null) {
             throw new ServiceException("鐢熶骇璁㈠崟涓嶅瓨鍦�");
         }
-        syncOperationParamInputValue(dto, routingOperation.getId());
         TechnologyRoutingOperation technologyRoutingOperation = technologyRoutingOperationMapper.selectById(routingOperation.getTechnologyRoutingOperationId());
         TechnologyOperation technologyOperation = technologyRoutingOperation == null ? null
                 : technologyOperationMapper.selectById(technologyRoutingOperation.getTechnologyOperationId());
@@ -149,11 +267,16 @@
         productionProductMain.setProductionOperationTaskId(taskId);
         productionProductMain.setStatus(0);
         productionProductMainMapper.insert(productionProductMain);
+        syncOperationParamInputValue(dto, routingOperation.getId(), productionProductMain.getId());
 
         List<ProductStructureDto> productStructureDtos = resolveInputStructures(
                 productionOrder.getId(), routingOperation, productModel.getId());
+       // 濡傛灉娌℃湁bom瀛愯妭鐐逛簡锛岄偅涔堟姇鍏ュ氨鏄粬鏈韩
         if (productStructureDtos.isEmpty()) {
-            throw new ServiceException("鏈壘鍒板綋鍓嶅伐搴忓搴旂殑BOM鎶曞叆鑺傜偣");
+            ProductStructureDto fallbackInput = new ProductStructureDto();
+            fallbackInput.setProductModelId(productModel.getId());
+            fallbackInput.setUnitQuantity(BigDecimal.ONE);
+            productStructureDtos.add(fallbackInput);
         }
         for (ProductStructureDto item : productStructureDtos) {
             // 褰撳墠瀹炵幇鎸夊伐搴忔垚鍝佺洿鎺ヤ綔涓烘姇鍏ワ紝鍚庣画鑻ユ帴鍏ラ鏂欒褰曞彲鍦ㄨ繖閲屾浛鎹㈡潵婧愩��
@@ -173,7 +296,8 @@
         productionProductOutput.setQuantity(defaultDecimal(dto.getQuantity()));
         productionProductOutput.setScrapQty(defaultDecimal(dto.getScrapQty()));
         productionProductOutputMapper.insert(productionProductOutput);
-        BigDecimal productQty = productionProductOutput.getQuantity().subtract(productionProductOutput.getScrapQty());
+        BigDecimal reportQty = defaultDecimal(productionProductOutput.getQuantity());
+        BigDecimal productQty = reportQty;
 
         List<ProductionOrderRoutingOperation> routingOperationList = productionOrderRoutingOperationMapper.selectList(
                 Wrappers.<ProductionOrderRoutingOperation>lambdaQuery()
@@ -199,7 +323,7 @@
                 qualityInspect.setProductModelId(productModel.getId());
                 qualityInspectMapper.insert(qualityInspect);
                 List<QualityTestStandard> qualityTestStandard = qualityTestStandardMapper.getQualityTestStandardByProductId(product.getId(), inspectType, process);
-                if (qualityTestStandard.size() > 0) {
+                if (!qualityTestStandard.isEmpty()) {
                     qualityInspect.setTestStandardId(qualityTestStandard.get(0).getId());
                     qualityInspectMapper.updateById(qualityInspect);
                     qualityTestStandardParamMapper.selectList(Wrappers.<QualityTestStandardParam>lambdaQuery()
@@ -213,8 +337,12 @@
                             });
                 }
             } 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));
@@ -263,27 +391,40 @@
             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;
     }
 
-    private void syncOperationParamInputValue(ProductionProductMainDto dto, Long productionOrderRoutingOperationId) {
-        if (dto == null || productionOrderRoutingOperationId == null) {
+    private void syncOperationParamInputValue(ProductionProductMainDto dto,
+                                              Long productionOrderRoutingOperationId,
+                                              Long productionProductMainId) {
+        if (dto == null || productionOrderRoutingOperationId == null || productionProductMainId == null) {
             return;
         }
         List<ProductionOrderRoutingOperationParam> paramList = dto.getProductionOperationParamList();
         if (paramList == null || paramList.isEmpty()) {
             return;
         }
+        Set<Long> sourceParamIdSet = paramList.stream()
+                .filter(Objects::nonNull)
+                .map(ProductionOrderRoutingOperationParam::getId)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toCollection(LinkedHashSet::new));
+        if (sourceParamIdSet.isEmpty()) {
+            return;
+        }
+
         List<ProductionOrderRoutingOperationParam> dbParamList = productionOrderRoutingOperationParamMapper.selectList(
                 Wrappers.<ProductionOrderRoutingOperationParam>lambdaQuery()
+                        .in(ProductionOrderRoutingOperationParam::getId, sourceParamIdSet)
                         .eq(ProductionOrderRoutingOperationParam::getProductionOrderRoutingOperationId, productionOrderRoutingOperationId));
         if (dbParamList == null || dbParamList.isEmpty()) {
             return;
         }
+
         Map<Long, ProductionOrderRoutingOperationParam> dbParamMap = dbParamList.stream()
                 .filter(item -> item != null && item.getId() != null)
                 .collect(Collectors.toMap(ProductionOrderRoutingOperationParam::getId, item -> item, (left, right) -> left));
@@ -295,14 +436,31 @@
             if (dbParam == null) {
                 throw new ServiceException("宸ュ簭鍙傛暟涓嶅瓨鍦ㄦ垨涓嶅睘浜庡綋鍓嶅伐鍗曞伐搴忥紝ID=" + param.getId());
             }
-            if (Objects.equals(dbParam.getInputValue(), param.getInputValue())) {
-                continue;
-            }
-            ProductionOrderRoutingOperationParam updateParam = new ProductionOrderRoutingOperationParam();
-            updateParam.setId(dbParam.getId());
-            updateParam.setInputValue(param.getInputValue());
-            productionOrderRoutingOperationParamMapper.updateById(updateParam);
+            productionOrderRoutingOperationParamMapper.insert(buildReportParamSnapshot(dbParam, param.getInputValue(), productionProductMainId));
         }
+    }
+
+    private ProductionOrderRoutingOperationParam buildReportParamSnapshot(ProductionOrderRoutingOperationParam source,
+                                                                          String inputValue,
+                                                                          Long productionProductMainId) {
+        ProductionOrderRoutingOperationParam target = new ProductionOrderRoutingOperationParam();
+        target.setProductionOrderId(source.getProductionOrderId());
+        target.setTechnologyRoutingOperationParamId(source.getTechnologyRoutingOperationParamId());
+        target.setParamCode(source.getParamCode());
+        target.setParamName(source.getParamName());
+        target.setParamType(source.getParamType());
+        target.setParamFormat(source.getParamFormat());
+        target.setUnit(source.getUnit());
+        target.setIsRequired(source.getIsRequired());
+        target.setRemark(source.getRemark());
+        target.setParamId(source.getParamId());
+        target.setTechnologyOperationId(source.getTechnologyOperationId());
+        target.setTechnologyOperationParamId(source.getTechnologyOperationParamId());
+        target.setStandardValue(source.getStandardValue());
+        target.setInputValue(inputValue);
+        target.setProductionOrderRoutingOperationId(source.getProductionOrderRoutingOperationId());
+        target.setProductionProductMainId(productionProductMainId);
+        return target;
     }
 
     private List<ProductStructureDto> resolveInputStructures(Long productionOrderId,
@@ -384,8 +542,8 @@
 
         ProductionOperationTask productionOperationTask = productionOperationTaskMapper.selectById(productionProductMain.getProductionOperationTaskId());
         if (productionOperationTask != null && productionProductOutput != null) {
-            BigDecimal validQuantity = defaultDecimal(productionProductOutput.getQuantity()).subtract(defaultDecimal(productionProductOutput.getScrapQty()));
-            productionOperationTask.setCompleteQuantity(defaultDecimal(productionOperationTask.getCompleteQuantity()).subtract(validQuantity));
+            BigDecimal reportQuantity = defaultDecimal(productionProductOutput.getQuantity());
+            productionOperationTask.setCompleteQuantity(defaultDecimal(productionOperationTask.getCompleteQuantity()).subtract(reportQuantity));
             productionOperationTask.setActualEndTime(null);
             if (defaultDecimal(productionOperationTask.getCompleteQuantity()).compareTo(BigDecimal.ZERO) <= 0) {
                 productionOperationTask.setCompleteQuantity(BigDecimal.ZERO);
@@ -406,7 +564,7 @@
                                 .eq(ProductionOrderRoutingOperation::getProductionOrderId, routingOperation.getProductionOrderId()));
                 boolean isLastOperation = routingOperation.getDragSort() != null && routingOperation.getDragSort().equals(routingOperationList.size());
                 if (isLastOperation) {
-                    BigDecimal newCompleteQty = defaultDecimal(productionOrder.getCompleteQuantity()).subtract(validQuantity);
+                    BigDecimal newCompleteQty = defaultDecimal(productionOrder.getCompleteQuantity()).subtract(reportQuantity);
                     productionOrder.setCompleteQuantity(newCompleteQty.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO : newCompleteQty);
                     productionOrder.setEndTime(null);
                 }
@@ -431,6 +589,9 @@
                 .eq(ProductionProductOutput::getProductionProductMainId, productionProductMain.getId()));
         productionProductInputMapper.delete(new LambdaQueryWrapper<ProductionProductInput>()
                 .eq(ProductionProductInput::getProductionProductMainId, productionProductMain.getId()));
+        productionOrderRoutingOperationParamMapper.delete(
+                Wrappers.<ProductionOrderRoutingOperationParam>lambdaQuery()
+                        .eq(ProductionOrderRoutingOperationParam::getProductionProductMainId, productionProductMain.getId()));
         stockUtils.deleteStockInRecord(productionProductMain.getId(), StockInUnQualifiedRecordTypeEnum.PRODUCTION_SCRAP.getCode());
         stockUtils.deleteStockInRecord(productionProductMain.getId(), StockInQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_IN.getCode());
         stockUtils.deleteStockOutRecord(productionProductMain.getId(), StockOutQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_OUT.getCode());
diff --git a/src/main/java/com/ruoyi/project/common/CommonController.java b/src/main/java/com/ruoyi/project/common/CommonController.java
index 9ee3c93..093e132 100644
--- a/src/main/java/com/ruoyi/project/common/CommonController.java
+++ b/src/main/java/com/ruoyi/project/common/CommonController.java
@@ -8,8 +8,6 @@
 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;
@@ -33,99 +31,11 @@
 @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) {
diff --git a/src/main/java/com/ruoyi/purchase/controller/InvoicePurchaseController.java b/src/main/java/com/ruoyi/purchase/controller/InvoicePurchaseController.java
index ba77143..afd9213 100644
--- a/src/main/java/com/ruoyi/purchase/controller/InvoicePurchaseController.java
+++ b/src/main/java/com/ruoyi/purchase/controller/InvoicePurchaseController.java
@@ -10,11 +10,10 @@
 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;
 
@@ -80,12 +79,4 @@
         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());
-        }
-    }
 }
diff --git a/src/main/java/com/ruoyi/purchase/controller/TicketRegistrationController.java b/src/main/java/com/ruoyi/purchase/controller/TicketRegistrationController.java
index 2395276..db628af 100644
--- a/src/main/java/com/ruoyi/purchase/controller/TicketRegistrationController.java
+++ b/src/main/java/com/ruoyi/purchase/controller/TicketRegistrationController.java
@@ -19,18 +19,15 @@
 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;
 
@@ -161,15 +158,6 @@
     @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());
-        }
     }
 
     /**
diff --git a/src/main/java/com/ruoyi/purchase/dto/PurchaseLedgerDto.java b/src/main/java/com/ruoyi/purchase/dto/PurchaseLedgerDto.java
index 3b52fe5..c6b1406 100644
--- a/src/main/java/com/ruoyi/purchase/dto/PurchaseLedgerDto.java
+++ b/src/main/java/com/ruoyi/purchase/dto/PurchaseLedgerDto.java
@@ -2,6 +2,8 @@
 
 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;
@@ -197,4 +199,7 @@
     private String templateName;
     @Schema(description = "瀹℃壒浜篿d")
     private Integer approverId;
+
+    private List<StorageBlobVO> storageBlobVOS;
+    private List<StorageBlobDTO> storageBlobDTOS;
 }
diff --git a/src/main/java/com/ruoyi/purchase/service/impl/InvoicePurchaseServiceImpl.java b/src/main/java/com/ruoyi/purchase/service/impl/InvoicePurchaseServiceImpl.java
index 0187823..8b76b1a 100644
--- a/src/main/java/com/ruoyi/purchase/service/impl/InvoicePurchaseServiceImpl.java
+++ b/src/main/java/com/ruoyi/purchase/service/impl/InvoicePurchaseServiceImpl.java
@@ -50,10 +50,7 @@
 
     private final CommonFileMapper commonFileMapper;
 
-    private final TempFileMapper tempFileMapper;
 
-    @Value("${file.upload-dir}")
-    private String uploadDir;
 
     @Override
     public List<InvoicePurchaseDto> selectInvoicePurchaseList(InvoicePurchaseDto invoicePurchaseDto) {
diff --git a/src/main/java/com/ruoyi/purchase/service/impl/PurchaseLedgerServiceImpl.java b/src/main/java/com/ruoyi/purchase/service/impl/PurchaseLedgerServiceImpl.java
index 8660993..c4a48cd 100644
--- a/src/main/java/com/ruoyi/purchase/service/impl/PurchaseLedgerServiceImpl.java
+++ b/src/main/java/com/ruoyi/purchase/service/impl/PurchaseLedgerServiceImpl.java
@@ -6,15 +6,18 @@
 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;
@@ -23,7 +26,6 @@
 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;
@@ -59,22 +61,15 @@
 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;
@@ -116,9 +111,7 @@
     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) {
@@ -199,9 +192,7 @@
 //            }
 //        }
         // 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;
     }
 
@@ -322,80 +313,6 @@
         }
     }
 
-    /**
-     * 灏嗕复鏃舵枃浠惰縼绉诲埌姝e紡鐩綍
-     *
-     * @param businessId  涓氬姟ID锛堥攢鍞彴璐D锛�
-     * @param tempFileIds 涓存椂鏂囦欢ID鍒楄〃
-     * @throws IOException 鏂囦欢鎿嶄綔寮傚父
-     */
-    private void migrateTempFilesToFormal(Long businessId, List<String> tempFileIds) throws IOException {
-        if (CollectionUtils.isEmpty(tempFileIds)) {
-            return;
-        }
-
-        // 鏋勫缓姝e紡鐩綍璺緞锛堟寜涓氬姟绫诲瀷鍜屾棩鏈熷垎缁勶級
-        String formalDir = uploadDir + LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE);
-
-        Path formalDirPath = Paths.get(formalDir);
-
-        // 纭繚姝e紡鐩綍瀛樺湪锛堥�掑綊鍒涘缓锛�
-        if (!Files.exists(formalDirPath)) {
-            Files.createDirectories(formalDirPath);
-        }
-
-        for (String tempFileId : tempFileIds) {
-            // 鏌ヨ涓存椂鏂囦欢璁板綍
-            TempFile tempFile = tempFileMapper.selectById(tempFileId);
-            if (tempFile == null) {
-                log.warn("涓存椂鏂囦欢涓嶅瓨鍦紝璺宠繃澶勭悊: {}", tempFileId);
-                continue;
-            }
-
-            // 鏋勫缓姝e紡鏂囦欢鍚嶏紙鍖呭惈涓氬姟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);
-            }
-        }
-    }
-
     @Override
     @Transactional(rollbackFor = Exception.class)
     public int deletePurchaseLedgerByIds(Long[] ids) {
@@ -493,11 +410,6 @@
                 .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();
@@ -505,7 +417,7 @@
         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;
     }
diff --git a/src/main/java/com/ruoyi/purchase/service/impl/TicketRegistrationServiceImpl.java b/src/main/java/com/ruoyi/purchase/service/impl/TicketRegistrationServiceImpl.java
index b0b2a59..a98d924 100644
--- a/src/main/java/com/ruoyi/purchase/service/impl/TicketRegistrationServiceImpl.java
+++ b/src/main/java/com/ruoyi/purchase/service/impl/TicketRegistrationServiceImpl.java
@@ -14,7 +14,6 @@
 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;
@@ -34,21 +33,17 @@
 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;
 
 /**
@@ -77,9 +72,6 @@
     private final ISalesLedgerProductService salesLedgerProductService;
 
     private final PaymentRegistrationMapper paymentRegistrationMapper;
-
-    @Value("${file.upload-dir}")
-    private String uploadDir;
 
 
     @Override
@@ -175,78 +167,7 @@
                 throw new RuntimeException("浜у搧寮�绁ㄦ暟閮戒负0锛岃妫�鏌�");
             }
         }
-        // 杩佺Щ涓存椂鏂囦欢鍒版寮忕洰褰�
-        if (ticketRegistrationDto.getTempFileIds() != null && !ticketRegistrationDto.getTempFileIds().isEmpty()) {
-            migrateTempFilesToFormal(ticketRegistration.getId(), ticketRegistrationDto.getTempFileIds());
-        }
         return rowsAffected;
-    }
-
-
-    /**
-     * 灏嗕复鏃舵枃浠惰縼绉诲埌姝e紡鐩綍
-     *
-     * @param businessId  涓氬姟ID锛堥攢鍞彴璐D锛�
-     * @param tempFileIds 涓存椂鏂囦欢ID鍒楄〃
-     * @throws IOException 鏂囦欢鎿嶄綔寮傚父
-     */
-    public void migrateTempFilesToFormal(Long businessId, List<String> tempFileIds) throws IOException {
-        if (CollectionUtils.isEmpty(tempFileIds)) {
-            return;
-        }
-
-        // 鏋勫缓姝e紡鐩綍璺緞锛堟寜涓氬姟绫诲瀷鍜屾棩鏈熷垎缁勶級
-        String formalDir = uploadDir + LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE);
-
-        Path formalDirPath = Paths.get(formalDir);
-
-        // 纭繚姝e紡鐩綍瀛樺湪锛堥�掑綊鍒涘缓锛�
-        if (!Files.exists(formalDirPath)) {
-            Files.createDirectories(formalDirPath);
-        }
-
-        for (String tempFileId : tempFileIds) {
-            // 鏌ヨ涓存椂鏂囦欢璁板綍
-            TempFile tempFile = tempFileMapper.selectById(tempFileId);
-            if (tempFile == null) {
-                log.warn("涓存椂鏂囦欢涓嶅瓨鍦紝璺宠繃澶勭悊: {}", tempFileId);
-                continue;
-            }
-
-            // 鏋勫缓姝e紡鏂囦欢鍚嶏紙鍖呭惈涓氬姟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);
-            }
-        }
     }
 
 
diff --git a/src/main/java/com/ruoyi/quality/service/impl/QualityInspectServiceImpl.java b/src/main/java/com/ruoyi/quality/service/impl/QualityInspectServiceImpl.java
index 1bedb6c..8c53500 100644
--- a/src/main/java/com/ruoyi/quality/service/impl/QualityInspectServiceImpl.java
+++ b/src/main/java/com/ruoyi/quality/service/impl/QualityInspectServiceImpl.java
@@ -9,7 +9,6 @@
 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;
@@ -24,12 +23,14 @@
 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;
@@ -43,6 +44,7 @@
 public class QualityInspectServiceImpl extends ServiceImpl<QualityInspectMapper, QualityInspect> implements IQualityInspectService {
 
     private final StockUtils stockUtils;
+    private final StockInventoryService stockInventoryService;
     private QualityInspectMapper qualityInspectMapper;
 
     private IQualityInspectParamService qualityInspectParamService;
@@ -98,7 +100,14 @@
             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);
diff --git a/src/main/java/com/ruoyi/safe/controller/SafeTrainingController.java b/src/main/java/com/ruoyi/safe/controller/SafeTrainingController.java
index ae8fafa..dd8b6f5 100644
--- a/src/main/java/com/ruoyi/safe/controller/SafeTrainingController.java
+++ b/src/main/java/com/ruoyi/safe/controller/SafeTrainingController.java
@@ -29,8 +29,8 @@
 @RequiredArgsConstructor
 public class SafeTrainingController {
 
-    private SafeTrainingService safeTrainingService;
-    private SafeTrainingDetailsService safeTrainingDetailsService;
+    private final SafeTrainingService safeTrainingService;
+    private final SafeTrainingDetailsService safeTrainingDetailsService;
 
     @GetMapping("/page")
     @Operation(summary = "鍒嗛〉鏌ヨ")
diff --git a/src/main/java/com/ruoyi/sales/service/ICommonFileService.java b/src/main/java/com/ruoyi/sales/service/ICommonFileService.java
index 398fc5d..6cba7dc 100644
--- a/src/main/java/com/ruoyi/sales/service/ICommonFileService.java
+++ b/src/main/java/com/ruoyi/sales/service/ICommonFileService.java
@@ -1,15 +1,9 @@
 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);
 }
diff --git a/src/main/java/com/ruoyi/sales/service/impl/CommonFileServiceImpl.java b/src/main/java/com/ruoyi/sales/service/impl/CommonFileServiceImpl.java
index f3aac49..eb36693 100644
--- a/src/main/java/com/ruoyi/sales/service/impl/CommonFileServiceImpl.java
+++ b/src/main/java/com/ruoyi/sales/service/impl/CommonFileServiceImpl.java
@@ -1,32 +1,17 @@
 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
@@ -36,9 +21,6 @@
     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)
@@ -66,105 +48,9 @@
         return commonFileMapper.deleteBatchIds(Arrays.asList(ids));
     }
 
-    @Override
-    public CommonFile uploadFile(MultipartFile file, Long id, Integer type) throws IOException {
-        // 1. 鐢熸垚姝e紡鏂囦欢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));
-    }
-
-    /**
-     * 灏嗕复鏃舵枃浠惰縼绉诲埌姝e紡鐩綍
-     *
-     * @param businessId  涓氬姟ID锛堥攢鍞彴璐D锛�
-     * @param tempFileIds 涓存椂鏂囦欢ID鍒楄〃
-     * @throws IOException 鏂囦欢鎿嶄綔寮傚父
-     */
-    @Transactional(rollbackFor = Exception.class)
-    public void migrateTempFilesToFormal(Long businessId, List<String> tempFileIds) throws IOException {
-        if (CollectionUtils.isEmpty(tempFileIds)) {
-            return;
-        }
-
-        // 鏋勫缓姝e紡鐩綍璺緞锛堟寜涓氬姟绫诲瀷鍜屾棩鏈熷垎缁勶級
-        String formalDir = uploadDir + LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE);
-
-        Path formalDirPath = Paths.get(formalDir);
-
-        // 纭繚姝e紡鐩綍瀛樺湪锛堥�掑綊鍒涘缓锛�
-        if (!Files.exists(formalDirPath)) {
-            Files.createDirectories(formalDirPath);
-        }
-
-        for (String tempFileId : tempFileIds) {
-            // 鏌ヨ涓存椂鏂囦欢璁板綍
-            TempFile tempFile = tempFileMapper.selectById(tempFileId);
-            if (tempFile == null) {
-                log.warn("涓存椂鏂囦欢涓嶅瓨鍦紝璺宠繃澶勭悊: {}", tempFileId);
-                continue;
-            }
-
-            // 鏋勫缓姝e紡鏂囦欢鍚嶏紙鍖呭惈涓氬姟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);
-            }
-        }
     }
 }
diff --git a/src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java b/src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java
index eb8fcce..ccbe035 100644
--- a/src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java
+++ b/src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java
@@ -29,7 +29,6 @@
 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;
@@ -48,7 +47,6 @@
 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;
@@ -58,15 +56,10 @@
 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;
@@ -89,13 +82,11 @@
     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;
@@ -103,15 +94,9 @@
     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;
@@ -621,81 +606,6 @@
             fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.SALES_LEDGER, salesLedger.getId(), salesLedgerDto.getStorageBlobDTOs());
         }
         return 1;
-    }
-
-    /**
-     * 灏嗕复鏃舵枃浠惰縼绉诲埌姝e紡鐩綍
-     *
-     * @param businessId  涓氬姟ID锛堥攢鍞彴璐D锛�
-     * @param tempFileIds 涓存椂鏂囦欢ID鍒楄〃
-     * @throws IOException 鏂囦欢鎿嶄綔寮傚父
-     */
-    private void migrateTempFilesToFormal(Long businessId, List<String> tempFileIds) throws IOException {
-        if (CollectionUtils.isEmpty(tempFileIds)) {
-            return;
-        }
-
-        // 鏋勫缓姝e紡鐩綍璺緞锛堟寜涓氬姟绫诲瀷鍜屾棩鏈熷垎缁勶級
-        String formalDir = uploadDir + LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE);
-
-        Path formalDirPath = Paths.get(formalDir);
-
-        // 纭繚姝e紡鐩綍瀛樺湪锛堥�掑綊鍒涘缓锛�
-        if (!Files.exists(formalDirPath)) {
-            Files.createDirectories(formalDirPath);
-        }
-
-        for (String tempFileId : tempFileIds) {
-            // 鏌ヨ涓存椂鏂囦欢璁板綍
-            TempFile tempFile = tempFileMapper.selectById(tempFileId);
-            if (tempFile == null) {
-                log.warn("涓存椂鏂囦欢涓嶅瓨鍦紝璺宠繃澶勭悊: {}", tempFileId);
-                continue;
-            }
-
-            // 鏋勫缓姝e紡鏂囦欢鍚嶏紙鍖呭惈涓氬姟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);
-            }
-        }
     }
 
     // 鏂囦欢杩佺Щ鏂规硶
diff --git a/src/main/java/com/ruoyi/sales/service/impl/ShippingInfoServiceImpl.java b/src/main/java/com/ruoyi/sales/service/impl/ShippingInfoServiceImpl.java
index 3f44734..08322a2 100644
--- a/src/main/java/com/ruoyi/sales/service/impl/ShippingInfoServiceImpl.java
+++ b/src/main/java/com/ruoyi/sales/service/impl/ShippingInfoServiceImpl.java
@@ -11,7 +11,6 @@
 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;
@@ -23,7 +22,6 @@
 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;
@@ -41,7 +39,6 @@
 
     private final ShippingInfoMapper shippingInfoMapper;
 
-    private final TempFileServiceImpl tempFileService;
 
     private final SalesLedgerProductMapper salesLedgerProductMapper;
 
diff --git a/src/main/java/com/ruoyi/stock/service/impl/StockInventoryServiceImpl.java b/src/main/java/com/ruoyi/stock/service/impl/StockInventoryServiceImpl.java
index 4d525c3..c6b32b4 100644
--- a/src/main/java/com/ruoyi/stock/service/impl/StockInventoryServiceImpl.java
+++ b/src/main/java/com/ruoyi/stock/service/impl/StockInventoryServiceImpl.java
@@ -4,12 +4,14 @@
 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;
@@ -21,18 +23,21 @@
 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;
@@ -55,6 +60,7 @@
     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);
@@ -69,14 +75,15 @@
     @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());
@@ -147,11 +154,17 @@
     @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());
@@ -159,6 +172,82 @@
         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) {
diff --git a/src/main/java/com/ruoyi/technology/controller/TechnologyRoutingController.java b/src/main/java/com/ruoyi/technology/controller/TechnologyRoutingController.java
index e433efa..9ab62e1 100644
--- a/src/main/java/com/ruoyi/technology/controller/TechnologyRoutingController.java
+++ b/src/main/java/com/ruoyi/technology/controller/TechnologyRoutingController.java
@@ -52,7 +52,7 @@
     /**
      * 淇敼宸ヨ壓璺嚎銆�
      */
-    @PutMapping("editTechRoute")
+    @PutMapping("/editTechRoute")
     @Operation(summary = "淇敼宸ヨ壓璺嚎")
     public R edit(@RequestBody TechnologyRouting technologyRouting) {
         return R.ok(technologyRoutingService.updateTechnologyRouting(technologyRouting));
@@ -67,5 +67,4 @@
         return R.ok(technologyRoutingService.removeTechnologyRouting(ids));
     }
 
-    //TODO 澧炲姞宸ヨ壓璺嚎闄勪欢涓婁紶 @闄堟捣鏉�
 }
diff --git a/src/main/java/com/ruoyi/technology/service/impl/TechnologyRoutingServiceImpl.java b/src/main/java/com/ruoyi/technology/service/impl/TechnologyRoutingServiceImpl.java
index a5c8fcc..4079f84 100644
--- a/src/main/java/com/ruoyi/technology/service/impl/TechnologyRoutingServiceImpl.java
+++ b/src/main/java/com/ruoyi/technology/service/impl/TechnologyRoutingServiceImpl.java
@@ -116,13 +116,13 @@
                 Wrappers.<TechnologyBomStructure>lambdaQuery()
                         .eq(TechnologyBomStructure::getBomId, technologyRouting.getBomId())
                         .isNotNull(TechnologyBomStructure::getOperationId)
-                        .orderByAsc(TechnologyBomStructure::getId)
+                        .orderByDesc(TechnologyBomStructure::getId)
         );
         if (bomStructures.isEmpty()) {
             throw new ServiceException("bom浜у搧缁撴瀯涓虹┖锛�");
         }
 
-        // 鍚屼竴涓� BOM 涓彲鑳介噸澶嶅紩鐢ㄧ浉鍚屽伐搴忥紝杩欓噷鎸夐娆″嚭鐜伴『搴忓幓閲嶃��
+        // 鍚屼竴涓� BOM 涓彲鑳介噸澶嶅紩鐢ㄧ浉鍚屽伐搴忥紝鎸夌収涓婁竴灞傜殑鐖惰妭鐐圭殑浜у搧鏄惁鐩稿悓鍜屽伐搴忔槸鍚︾浉鍚�
         Map<Long, TechnologyBomStructure> structureById = new HashMap<>();
         for (TechnologyBomStructure bomStructure : bomStructures) {
             if (bomStructure != null && bomStructure.getId() != null) {
diff --git a/src/main/resources/application-dev-pro.yml b/src/main/resources/application-dev-pro.yml
index 1552370..abb1950 100644
--- a/src/main/resources/application-dev-pro.yml
+++ b/src/main/resources/application-dev-pro.yml
@@ -254,8 +254,8 @@
 
 # 鏂囦欢涓婁紶閰嶇疆
 file:
-  temp-dir: D:/ruoyi/temp/uploads   # 涓存椂鐩綍
-  upload-dir: D:/ruoyi/prod/uploads # 姝e紡鐩綍
+  temp-dir: D:/ruoyi/temp/uploads   # 涓存椂鐩綍 鍚庢湡鍒犻櫎
+  upload-dir: D:/ruoyi/prod/uploads # 姝e紡鐩綍 鍚庢湡鍒犻櫎
   path: C:/Users/12631/Desktop/download/uploads # 涓婁紶鐩綍
   urlPrefix: /common # 閾炬帴鍓嶇紑
   domain: http://127.0.0.1:7003 # 鍩熷悕鍓嶇紑
diff --git a/src/main/resources/mapper/basic/StorageAttachmentMapper.xml b/src/main/resources/mapper/basic/StorageAttachmentMapper.xml
index a2cc6cf..d2b7b92 100644
--- a/src/main/resources/mapper/basic/StorageAttachmentMapper.xml
+++ b/src/main/resources/mapper/basic/StorageAttachmentMapper.xml
@@ -10,13 +10,9 @@
                     <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>
\ No newline at end of file
+</mapper>
diff --git a/src/main/resources/mapper/basic/StorageBlobMapper.xml b/src/main/resources/mapper/basic/StorageBlobMapper.xml
index 84e3b00..d8a03fa 100644
--- a/src/main/resources/mapper/basic/StorageBlobMapper.xml
+++ b/src/main/resources/mapper/basic/StorageBlobMapper.xml
@@ -2,21 +2,50 @@
 <!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>
\ No newline at end of file
+    <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>
diff --git a/src/main/resources/mapper/production/ProductionAccountMapper.xml b/src/main/resources/mapper/production/ProductionAccountMapper.xml
index 8b669e9..2841854 100644
--- a/src/main/resources/mapper/production/ProductionAccountMapper.xml
+++ b/src/main/resources/mapper/production/ProductionAccountMapper.xml
@@ -21,6 +21,93 @@
         <result column="dept_id" property="deptId" />
     </resultMap>
 
+    <select id="listPage" resultType="com.ruoyi.production.bean.vo.ProductionAccountVo">
+        select
+        group_concat(distinct p_parent.product_name order by p_parent.product_name separator ',') as productCategory,
+        group_concat(distinct pm.model order by pm.model separator ',') as specificationModel,
+        group_concat(distinct pm.unit order by pm.unit separator ',') as unit,
+        pa.scheduling_user_id as schedulingUserId,
+        pa.scheduling_user_name as schedulingUserName,
+        cast(sum(
+            ifnull(pa.work_hours, 0) * ifnull(pa.finished_num, 0) *
+            case
+                when substring_index(pm.model, '*', -1) regexp '^[0-9]+(\\.[0-9]+)?$'
+                then cast(substring_index(pm.model, '*', -1) as decimal(18,4))
+                else 1
+            end
+        ) as decimal(18,4)) as wages,
+        cast(sum(ifnull(pa.finished_num, 0)) as decimal(18,4)) as finishedNum,
+        cast(sum(ifnull(pa.work_hours, 0)) as decimal(18,4)) as workHours,
+        case
+            when sum(ifnull(ppo.quantity, 0) + ifnull(ppo.scrapQty, 0)) = 0 then '0%'
+            else concat(
+                cast(
+                    round(
+                        sum(ifnull(ppo.quantity, 0)) /
+                        sum(ifnull(ppo.quantity, 0) + ifnull(ppo.scrapQty, 0)) * 100, 2
+                    ) as char
+                ),
+                '%'
+            )
+        end as outputRate,
+        group_concat(distinct pa.technology_operation_name order by pa.technology_operation_name separator ',') as process,
+        case
+            when count(distinct date(pa.scheduling_date)) = 1 then min(date(pa.scheduling_date))
+            else null
+        end as schedulingDate,
+        case
+            when count(distinct date_format(pa.scheduling_date, '%Y-%m')) = 1 then min(date_format(pa.scheduling_date, '%Y-%m'))
+            else null
+        end as schedulingMonth
+        from production_account pa
+        left join production_product_main ppm on ppm.id = pa.production_product_main_id
+        left join production_operation_task pot on ppm.production_operation_task_id = pot.id
+        left join production_order po on pot.production_order_id = po.id
+        left join production_order_routing_operation poro on pot.production_order_routing_operation_id = poro.id
+        left join product_model pm on pm.id = ifnull(poro.product_model_id, po.product_model_id)
+        left join product p on pm.product_id = p.id
+        left join product p_parent on p_parent.id = p.parent_id
+        left join (
+            select production_product_main_id,
+                   cast(sum(ifnull(quantity, 0)) as decimal(18,4)) as quantity,
+                   cast(sum(ifnull(scrap_qty, 0)) as decimal(18,4)) as scrapQty
+            from production_product_output
+            group by production_product_main_id
+        ) ppo on ppo.production_product_main_id = ppm.id
+        <where>
+            <if test="c != null">
+                <if test="c.productCategory != null and c.productCategory != ''">
+                    and p_parent.product_name like concat('%', #{c.productCategory}, '%')
+                </if>
+                <if test="c.specificationModel != null and c.specificationModel != ''">
+                    and pm.model like concat('%', #{c.specificationModel}, '%')
+                </if>
+                <if test="c.schedulingUserId != null">
+                    and pa.scheduling_user_id = #{c.schedulingUserId}
+                </if>
+                <if test="c.schedulingUserName != null and c.schedulingUserName != ''">
+                    and pa.scheduling_user_name like concat('%', #{c.schedulingUserName}, '%')
+                </if>
+                <if test="c.process != null and c.process != ''">
+                    and pa.technology_operation_name like concat('%', #{c.process}, '%')
+                </if>
+                <if test="c.entryDate != null">
+                    and date(pa.scheduling_date) = #{c.entryDate}
+                </if>
+                <if test="c.entryDateStart != null">
+                    and date(pa.scheduling_date) &gt;= #{c.entryDateStart}
+                </if>
+                <if test="c.entryDateEnd != null">
+                    and date(pa.scheduling_date) &lt;= #{c.entryDateEnd}
+                </if>
+            </if>
+        </where>
+        group by pa.scheduling_user_id,
+        pa.scheduling_user_name
+        order by wages desc,
+        pa.scheduling_user_id asc
+    </select>
+
     <select id="selectUserAccount" resultType="com.ruoyi.production.bean.dto.UserAccountDto">
         select ifnull(sum(finished_num), 0) as accountBalance,
                ifnull(sum(work_hours), 0) as account
diff --git a/src/main/resources/mapper/production/ProductionOperationTaskMapper.xml b/src/main/resources/mapper/production/ProductionOperationTaskMapper.xml
index ebafa7a..0ccb06f 100644
--- a/src/main/resources/mapper/production/ProductionOperationTaskMapper.xml
+++ b/src/main/resources/mapper/production/ProductionOperationTaskMapper.xml
@@ -24,6 +24,7 @@
     <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,
@@ -120,4 +121,28 @@
         group by poro.technology_operation_id, poro.operation_name
     </select>
 
+    <select id="getProductWorkOrderFlowCard" resultType="com.ruoyi.production.bean.dto.ProductionOperationTaskDto">
+        SELECT pot.*,
+               poro.operation_name AS processName,
+               pm.model AS model,
+               pm.unit AS unit,
+               p.product_name AS productName,
+               po.nps_no AS productOrderNpsNo,
+               ROUND(IFNULL(pot.complete_quantity, 0) / NULLIF(pot.plan_quantity, 0) * 100, 2) AS completionStatus,
+               IFNULL(scrapStat.scrapQty, 0) AS scrapQty
+        FROM production_operation_task pot
+                 LEFT JOIN production_order po ON pot.production_order_id = po.id
+                 LEFT JOIN production_order_routing_operation poro ON pot.production_order_routing_operation_id = poro.id
+                 LEFT JOIN product_model pm ON pm.id = ifnull(poro.product_model_id, po.product_model_id)
+                 LEFT JOIN product p ON p.id = pm.product_id
+                 LEFT JOIN (
+            SELECT ppm.production_operation_task_id AS taskId,
+                   SUM(IFNULL(ppo.scrap_qty, 0)) AS scrapQty
+            FROM production_product_main ppm
+                     LEFT JOIN production_product_output ppo ON ppo.production_product_main_id = ppm.id
+            GROUP BY ppm.production_operation_task_id
+        ) scrapStat ON scrapStat.taskId = pot.id
+        WHERE pot.id = #{id}
+    </select>
+
 </mapper>
diff --git a/src/main/resources/mapper/production/ProductionOrderMapper.xml b/src/main/resources/mapper/production/ProductionOrderMapper.xml
index b279001..2ea7d33 100644
--- a/src/main/resources/mapper/production/ProductionOrderMapper.xml
+++ b/src/main/resources/mapper/production/ProductionOrderMapper.xml
@@ -49,7 +49,9 @@
         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>
 
diff --git a/src/main/resources/mapper/production/ProductionProductMainMapper.xml b/src/main/resources/mapper/production/ProductionProductMainMapper.xml
index 8dd4613..5a78a5b 100644
--- a/src/main/resources/mapper/production/ProductionProductMainMapper.xml
+++ b/src/main/resources/mapper/production/ProductionProductMainMapper.xml
@@ -95,17 +95,59 @@
                p.product_name as productName,
                pm.model as productModelName,
                pm.unit,
-               poro.operation_name as process,
+               pa.technology_operation_name as process,
                ifnull(ppo.quantity, 0) as quantity,
-               ifnull(ppo.scrap_qty, 0) as scrapQty
-        from production_product_main ppm
+               ifnull(ppo.scrap_qty, 0) as scrapQty,
+               date(pa.scheduling_date) as schedulingDate,
+               pa.scheduling_user_name as schedulingUserName,
+               cast(ifnull(pa.work_hours, 0) as decimal(18,4)) as workHours,
+               cast(
+                   ifnull(pa.work_hours, 0) * ifnull(pa.finished_num, 0) *
+                   case
+                       when substring_index(pm.model, '*', -1) regexp '^[0-9]+(\\.[0-9]+)?$'
+                       then cast(substring_index(pm.model, '*', -1) as decimal(18,4))
+                       else 1
+                   end
+                   as decimal(18,4)
+               ) as wages
+        from production_account pa
+                 left join production_product_main ppm on ppm.id = pa.production_product_main_id
                  left join production_operation_task pot on ppm.production_operation_task_id = pot.id
                  left join production_order po on pot.production_order_id = po.id
                  left join production_order_routing_operation poro on pot.production_order_routing_operation_id = poro.id
                  left join product_model pm on pm.id = ifnull(poro.product_model_id, po.product_model_id)
                  left join product p on pm.product_id = p.id
+                 left join product p_parent on p_parent.id = p.parent_id
                  left join production_product_output ppo on ppo.production_product_main_id = ppm.id
-        order by ppm.create_time desc
+        <where>
+            <if test="c != null">
+                <if test="c.productCategory != null and c.productCategory != ''">
+                    and p_parent.product_name like concat('%', #{c.productCategory}, '%')
+                </if>
+                <if test="c.specificationModel != null and c.specificationModel != ''">
+                    and pm.model like concat('%', #{c.specificationModel}, '%')
+                </if>
+                <if test="c.schedulingUserId != null">
+                    and pa.scheduling_user_id = #{c.schedulingUserId}
+                </if>
+                <if test="c.schedulingUserName != null and c.schedulingUserName != ''">
+                    and pa.scheduling_user_name like concat('%', #{c.schedulingUserName}, '%')
+                </if>
+                <if test="c.process != null and c.process != ''">
+                    and pa.technology_operation_name like concat('%', #{c.process}, '%')
+                </if>
+                <if test="c.entryDate != null">
+                    and date(pa.scheduling_date) = #{c.entryDate}
+                </if>
+                <if test="c.entryDateStart != null">
+                    and date(pa.scheduling_date) &gt;= #{c.entryDateStart}
+                </if>
+                <if test="c.entryDateEnd != null">
+                    and date(pa.scheduling_date) &lt;= #{c.entryDateEnd}
+                </if>
+            </if>
+        </where>
+        order by pa.scheduling_date desc, pa.id desc
     </select>
 
     <select id="listMain" resultType="java.lang.Long">
diff --git a/src/main/resources/mapper/system/SysUserMapper.xml b/src/main/resources/mapper/system/SysUserMapper.xml
index e4f818a..e27a224 100644
--- a/src/main/resources/mapper/system/SysUserMapper.xml
+++ b/src/main/resources/mapper/system/SysUserMapper.xml
@@ -153,13 +153,18 @@
 	<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>

--
Gitblit v1.9.3