From c2053d7017625f270577bc0f6c523d04f52db6ea Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期五, 24 七月 2026 14:20:29 +0800
Subject: [PATCH] refactor(crm): 重构文件上传功能,替换原有的fileUrls字段为统一的StorageAttachmentApi
---
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/attendance/HrmAttendanceExceptionDO.java | 5
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/document/vo/MesPdDocumentSaveReqVO.java | 5
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/document/MesPdDocumentServiceImpl.java | 16
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/approval/vo/HrmLeaveApplicationSaveReqVO.java | 5
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/task/MesProTaskController.java | 8
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/CrmFollowUpRecordController.java | 7
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/vo/CrmFollowUpRecordSaveReqVO.java | 6
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/HrmAttendanceExceptionController.java | 4
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/quotation/vo/CrmSaleQuotationSaveReqVO.java | 4
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/order/ErpPurchaseOrderSaveReqVO.java | 3
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/approval/vo/HrmLeaveApplicationRespVO.java | 6
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/approval/HrmTransferApplicationServiceImpl.java | 12
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/quotation/vo/CrmSaleQuotationRespVO.java | 5
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/approval/HrmResignationApplicationServiceImpl.java | 12
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/HrmAttendanceExceptionRespVO.java | 6
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/order/ErpPurchaseOrderRespVO.java | 4
docs/file_upload_guide.md | 324 ++++------
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkOrderProcessServiceImpl.java | 27
.claude/rules/file-upload.md | 120 ++++
sql/mysql/hrm_module_init.sql | 5
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/request/ErpPurchaseRequestSaveReqVO.java | 3
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/approval/vo/HrmResignationApplicationRespVO.java | 6
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/api/item/MesMdItemApiImpl.java | 29 +
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/quotation/CrmSaleQuotationDO.java | 4
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/md/item/MesMdItemMapper.java | 21
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/workorder/MesProWorkOrderBomMapper.java | 6
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/followup/CrmFollowUpRecordServiceImpl.java | 11
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/workorder/MesProWorkOrderBomDO.java | 6
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/approval/HrmLeaveApplicationServiceImpl.java | 16
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/archive/vo/MesPdArchiveDetailRespVO.java | 5
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseOrderDO.java | 4
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/document/vo/MesPdDocumentRespVO.java | 7
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderRespVO.java | 4
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/task/vo/MesProTaskRespVO.java | 3
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/controller/admin/item/MdmItemController.java | 11
docs/mdm_item_sync_mes_optimization.md | 97 +++
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseRequestDO.java | 5
docs/附件上传模块汇总.md | 131 ++++
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderSaveReqVO.java | 3
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/service/item/MdmItemServiceImpl.java | 17
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/quotation/CrmSaleQuotationController.java | 5
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/HrmSalaryPaymentController.java | 7
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pd/document/MesPdDocumentDO.java | 4
docs/attachment_upload_reform.md | 168 +++++
yudao-module-mes-api/src/main/java/cn/iocoder/yudao/module/mes/api/item/MesMdItemApi.java | 30 +
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/HrmAttendanceExceptionSaveReqVO.java | 5
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/approval/HrmResignationApplicationDO.java | 5
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/salary/HrmSalaryPaymentServiceImpl.java | 5
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/salary/HrmSalaryPaymentDO.java | 5
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/followup/bo/CrmFollowUpCreateReqBO.java | 5
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/request/ErpPurchaseRequestRespVO.java | 3
docs/attachment_upload_frontend_integration.md | 306 ++++++++++
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/sale/ErpSaleOrderDO.java | 4
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/storage/dto/StorageBlobRespDTO.java | 16
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/followup/CrmFollowUpRecordDO.java | 6
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/quotation/CrmSaleQuotationServiceImpl.java | 12
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/approval/HrmLeaveApplicationDO.java | 5
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/HrmSalaryPaymentRespVO.java | 6
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/attendance/HrmAttendanceExceptionServiceImpl.java | 11
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/controller/admin/item/vo/MdmItemRespVO.java | 3
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/process/MesProWorkOrderProcessRespVO.java | 5
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/document/MesPdDocumentController.java | 7
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/storage/StorageAttachmentApi.java | 17
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/approval/vo/HrmResignationApplicationSaveReqVO.java | 5
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/approval/HrmTransferApplicationDO.java | 5
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/approval/vo/HrmTransferApplicationSaveReqVO.java | 5
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/approval/vo/HrmTransferApplicationRespVO.java | 6
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/storage/StorageAttachmentApiImpl.java | 62 ++
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/vo/CrmFollowUpRecordRespVO.java | 5
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/MesProWorkOrderProcessController.java | 36 +
70 files changed, 1,393 insertions(+), 344 deletions(-)
diff --git a/.claude/rules/file-upload.md b/.claude/rules/file-upload.md
new file mode 100644
index 0000000..c90d46a
--- /dev/null
+++ b/.claude/rules/file-upload.md
@@ -0,0 +1,120 @@
+# 鏂囦欢涓婁紶瑙勫垯
+
+## 鏍稿績鍘熷垯锛圓I 蹇呴』涓ユ牸閬靛畧锛�
+
+**鎵�鏈夋枃浠朵笂浼犲繀椤昏蛋 `yudao-module-system` 妯″潡鐨� Storage API + Attachment 涓棿琛ㄦ満鍒躲��**
+
+**绂佹鍦ㄤ换浣曚笟鍔℃ā鍧楄嚜琛岃璁℃枃浠朵笂浼犳柟妗堬紝鍖呮嫭浣嗕笉闄愪簬锛氭坊鍔� `file_url` 鍒椼�佹柊澧� upload 鎺ュ彛銆佹敞鍏� FileApi銆�**
+
+## 鏂囦欢涓庝笟鍔$殑鍏宠仈鏂瑰紡
+
+鏂囦欢涓庝笟鍔¤褰曠殑鍏宠仈**鍙兘閫氳繃 `system_storage_attachment` 涓棿琛�**锛屼笉鍏佽鍦ㄤ笟鍔¤〃涓坊鍔� `file_url`銆乣file_urls`銆乣attachment_url` 绛夊瓧绗︿覆瀛楁瀛樺偍鏂囦欢鍦板潃銆�
+
+鍏宠仈娴佺▼锛�
+1. 鍓嶇璋冪敤 `POST /system/storage-blob/upload` 涓婁紶鏂囦欢 鈫� 鑾峰緱 `blobId`
+2. 鍓嶇璋冪敤 `POST /system/storage-attachment/bind` 缁戝畾鏂囦欢鍒颁笟鍔¤褰曪紙浼犲叆 `recordType` + `recordId` + `blobItems`锛�
+3. 鍓嶇璋冪敤 `GET /system/storage-attachment/list` 鏌ヨ闄勪欢
+4. 鍓嶇璋冪敤 `DELETE /system/storage-attachment/delete` 鍒犻櫎闄勪欢
+
+## 姝g‘鎺ュ彛锛坰ystem 妯″潡锛�
+
+| 鏂规硶 | 璺緞 | 璇存槑 |
+|------|------|------|
+| POST | `/system/storage-blob/upload` | 涓婁紶鏂囦欢锛坄files` 鍙傛暟锛宮ultipart锛� |
+| POST | `/system/storage-blob/public-upload` | 鍏叡鏂囦欢涓婁紶锛堟案涔呮湁鏁堬級 |
+| GET | `/system/storage-blob/preview/{fileName}` | 鏂囦欢棰勮 |
+| GET | `/system/storage-blob/download/{fileName}` | 鏂囦欢涓嬭浇 |
+| GET | `/system/storage-attachment/list` | 鏌ヨ闄勪欢鍒楄〃 |
+| POST | `/system/storage-attachment/bind` | 缁戝畾闄勪欢鍒颁笟鍔¤褰� |
+| DELETE | `/system/storage-attachment/delete` | 鎵归噺鍒犻櫎闄勪欢 |
+
+## 绂佹琛屼负锛圓I 涓ョ鎵ц浠ヤ笅鎿嶄綔锛�
+
+### 鏁版嵁搴撳眰闈�
+- 鉂� 绂佹鍦ㄤ笟鍔¤〃娣诲姞 `file_url`銆乣file_urls`銆乣attachment_url` 绛夋枃浠跺湴鍧�鍒�
+- 鉂� 绂佹鍦ㄤ笟鍔¤〃娣诲姞浠讳綍鐢ㄤ簬瀛樺偍鏂囦欢淇℃伅鐨� JSON 鍒�
+
+### Java 鍚庣灞傞潰
+- 鉂� 绂佹鍦� DO 绫绘坊鍔� `fileUrl`銆乣fileUrls`銆乣attachmentUrl` 绛夊瓧娈�
+- 鉂� 绂佹鍦� SaveReqVO / RespVO 娣诲姞鏂囦欢鍦板潃瀛楁
+- 鉂� 绂佹鍦� MES/CRM/ERP/HRM 绛変笟鍔℃ā鍧楁柊澧� `@PostMapping("/upload")` 鎺ュ彛
+- 鉂� 绂佹鍦� Controller 涓娇鐢� `MultipartFile` 鍙傛暟锛圗xcel 瀵煎叆闄ゅ锛�
+- 鉂� 绂佹娉ㄥ叆鎴栬皟鐢� `cn.iocoder.yudao.module.infra.api.file.FileApi`
+
+### 鍓嶇灞傞潰
+- 鉂� 绂佹璋冪敤 `/infra/file/upload` 鎴� `/infra/file/presigned-url`
+- 鉂� 绂佹璋冪敤鏃х増 `src/api/infra/file/index.ts` 涓殑 `uploadFile()` 鍑芥暟
+
+## 甯歌 AI 骞昏妯″紡锛堝繀椤昏瘑鍒苟鎷掔粷锛�
+
+浠ヤ笅鏄� AI 瀹规槗骞昏鐢熸垚鐨勮璁℃ā寮忥紝閬囧埌绫讳技闇�姹傛椂鐩存帴鎷掔粷锛�
+
+1. **"鍔犱釜 fileUrl 瀛楁灏辫"** 鈫� 閿欒锛屽繀椤荤敤 attachment 涓棿琛�
+2. **"鍙傝�冨叾瀹冩ā鍧楃殑 fileUrl 瀛楁"** 鈫� 閿欒锛屽叾瀹冩ā鍧楃殑 fileUrl 涔熷簲琚垹闄�
+3. **"鍦� Controller 鍔犱釜 upload 鎺ュ彛"** 鈫� 閿欒锛岀粺涓�璧� system 妯″潡
+4. **"鐢� FileApi 涓婁紶鏂囦欢"** 鈫� 閿欒锛孎ileApi 鏄� infra 妯″潡鐨勬棫鎺ュ彛
+5. **"鍦ㄨ〃鍗曚繚瀛樻帴鍙e姞 MultipartFile 鍙傛暟"** 鈫� 閿欒锛屼笂浼犲拰淇濆瓨鍒嗙
+
+## 姝g‘绀轰緥
+
+### 鍓嶇璋冪敤
+
+```typescript
+import { uploadFile, bindAttachments, listAttachments } from '#/api/system/storage';
+
+// 1. 涓婁紶鏂囦欢
+const blobs = await uploadFile([file]);
+const blobId = blobs[0].id;
+
+// 2. 淇濆瓨涓氬姟璁板綍鍚庯紝缁戝畾闄勪欢
+await bindAttachments({
+ application: 'file',
+ recordType: 'erp_purchase_order',
+ recordId: savedRecord.id,
+ blobItems: [{ blobId }],
+});
+
+// 3. 鏌ヨ闄勪欢
+const attachments = await listAttachments({
+ recordType: 'erp_purchase_order',
+ recordId: savedRecord.id,
+});
+```
+
+### recordType 鍛藉悕瑙勮寖
+
+鏍煎紡锛歚{妯″潡鍓嶇紑}_{涓氬姟琛ㄥ悕}`
+
+| 妯″潡 | recordType |
+|------|------------|
+| ERP 閲囪喘璁㈠崟 | `erp_purchase_order` |
+| ERP 閲囪喘鐢宠 | `erp_purchase_request` |
+| ERP 閿�鍞鍗� | `erp_sale_order` |
+| ERP 鏀舵鍗� | `erp_finance_receipt` |
+| ERP 浠樻鍗� | `erp_finance_payment` |
+| HRM 璇峰亣 | `hrm_leave_application` |
+| HRM 绂昏亴 | `hrm_resignation_application` |
+| HRM 璋冨姩 | `hrm_transfer_application` |
+| MES 閲囪喘璁㈠崟 | `mes_purchase_order` |
+| MES 宸ュ崟 | `mes_pro_work_order` |
+| CRM 瀹㈡埛 | `crm_customer` |
+
+## AI 鑷煡娓呭崟
+
+鍦ㄥ疄鐜颁换浣曟枃浠朵笂浼犵浉鍏抽渶姹傚墠锛岄�愰」纭锛�
+
+- [ ] 鏄惁浣跨敤浜� `/system/storage-blob/upload` 涓婁紶锛�
+- [ ] 鏄惁浣跨敤浜� `/system/storage-attachment/bind` 鍏宠仈锛�
+- [ ] 鏄惁娌℃湁鍦ㄤ笟鍔¤〃涓坊鍔犳枃浠跺湴鍧�鍒楋紵
+- [ ] 鏄惁娌℃湁鍦� DO/VO 涓坊鍔犳枃浠跺湴鍧�瀛楁锛�
+- [ ] 鏄惁娌℃湁鍒涘缓鏂扮殑 upload Controller 鎺ュ彛锛�
+- [ ] 鏄惁娌℃湁浣跨敤 infra 妯″潡鐨� FileApi锛�
+
+浠ヤ笂 6 椤瑰叏閮ㄤ负"鏄�"鎵嶅彲缁х画銆�
+
+## 涓轰粈涔�
+
+- 瀛樺偍閰嶇疆锛堟湰鍦�/S3锛夊湪 system 妯″潡缁熶竴绠$悊
+- 闄勪欢缁戝畾鏈哄埗锛坰torage-attachment锛夋彁渚� `recordType + recordId` 鐨勬爣鍑嗗叧鑱旀柟寮忥紝鏀寔澶氭枃浠躲�佺敤閫斿垎绫�
+- infra 妯″潡鐨� `/infra/file/*` 鏄棫鐗堟帴鍙o紝瀛樺湪鏂囦欢涓庝笟鍔¤褰曟棤娉曞叧鑱旂殑闂
+- 涓氬姟琛ㄥ瓨鍌� file_url 瀛楃涓叉棤娉曟敮鎸佸鏂囦欢锛屼笖鏂囦欢涓庝笟鍔¤褰曚箣闂寸己涔忔爣鍑嗗寲鐨勫叧鑱旂鐞�
\ No newline at end of file
diff --git a/docs/attachment_upload_frontend_integration.md b/docs/attachment_upload_frontend_integration.md
new file mode 100644
index 0000000..d8fafd3
--- /dev/null
+++ b/docs/attachment_upload_frontend_integration.md
@@ -0,0 +1,306 @@
+# 鏂囦欢涓婁紶鏀归�� - 鍓嶇鑱旇皟鏂规
+
+## 鍚庣鍙樻洿鎽樿
+
+鎵�鏈夋ā鍧楃殑鏂囦欢涓婁紶宸蹭粠 `fileUrl` 瀛楃涓插瓧娈垫敼涓� `system_storage_attachment` 涓棿琛ㄦ満鍒讹細
+
+| 鍙樻洿 | 鏃ф柟寮� | 鏂版柟寮� |
+|------|--------|--------|
+| 涓婁紶 | 鐩存帴瀛� fileUrl | POST `/system/storage-blob/upload` 鈫� 鑾峰彇 blobId |
+| 淇濆瓨 | SaveReqVO.fileUrl | SaveReqVO.blobIds (List\<Long\>) |
+| 鏌ヨ | RespVO.fileUrl | RespVO.attachmentList (List\<StorageBlob\>) |
+| 鍒犻櫎 | 鏃� | 鍚庣 Service 鑷姩鍚屾鍒犻櫎闄勪欢 |
+
+## 娑夊強椤甸潰
+
+### HRM 妯″潡锛�5涓〉闈級
+
+| 椤甸潰 | 鐩綍 | recordType |
+|------|------|------------|
+| 璇峰亣鐢宠 | `src/views/hrm/leave/` | `hrm_leave_application` |
+| 绂昏亴鐢宠 | `src/views/hrm/resignation/` | `hrm_resignation_application` |
+| 璋冨矖鐢宠 | `src/views/hrm/transfer/` | `hrm_transfer_application` |
+| 鑰冨嫟寮傚父鐢宠 | `src/views/hrm/attendance/exception/` | `hrm_attendance_exception` |
+| 钖祫鍙戞斁 | `src/views/hrm/salary/payment/` | `hrm_salary_payment` |
+
+### CRM 妯″潡锛�2涓〉闈級
+
+| 椤甸潰 | 鐩綍 | recordType |
+|------|------|------------|
+| 閿�鍞姤浠峰崟 | `src/views/crm/saleQuotation/` | `crm_sale_quotation` |
+| 璺熻繘璁板綍 | `src/views/crm/followup/` | `crm_follow_up_record` |
+
+### MES 妯″潡锛�1涓〉闈級
+
+| 椤甸潰 | 鐩綍 | recordType |
+|------|------|------------|
+| 璁捐璧勬枡 | `src/views/mes/pd/project/modules/document-form.vue` | `mes_pd_document` |
+
+---
+
+## API 璇存槑
+
+### 閫氱敤鏂囦欢 API锛堝凡鏈夛紝鏃犻渶鏂板锛�
+
+| 鏂规硶 | 璺緞 | 璇存槑 |
+|------|------|------|
+| POST | `/system/storage-blob/upload` | 涓婁紶鏂囦欢锛坢ultipart锛宍files` 鍙傛暟锛� |
+| GET | `/system/storage-attachment/list` | 鏌ヨ闄勪欢鍒楄〃锛坄recordType` + `recordId`锛� |
+| POST | `/system/storage-attachment/bind` | 缁戝畾闄勪欢鍒颁笟鍔¤褰� |
+| DELETE | `/system/storage-attachment/delete` | 鎵归噺鍒犻櫎闄勪欢 |
+
+鍓嶇宸叉湁瀵瑰簲鐨� API 灏佽鍦� `src/api/system/storage/index.ts`锛�
+- `uploadFile(files)` 鈫� `StorageBlob[]`
+- `listAttachments({ recordType, recordId })` 鈫� `StorageBlob[]`
+- `bindAttachments({ application, recordType, recordId, blobItems })` 鈫� `boolean`
+- `deleteAttachments(ids)` 鈫� `boolean`
+
+### 鍚勬帴鍙� SaveReqVO 鏂板瀛楁
+
+鎵�鏈夋ā鍧楃殑鍒涘缓/鏇存柊鎺ュ彛鍧囨柊澧� `blobIds` 鍙傛暟锛�
+
+| 鎺ュ彛 | 鏂板鍙傛暟 |
+|------|----------|
+| 鎵�鏈夋ā鍧� create/update | `blobIds: number[]` |
+
+### 鍚勬帴鍙� RespVO 鏂板瀛楁
+
+鎵�鏈夋ā鍧楃殑璇︽儏鏌ヨ鎺ュ彛鍧囨柊澧� `attachmentList` 瀛楁锛�
+
+| 鎺ュ彛 | 鏂板瀛楁 | 绫诲瀷 |
+|------|----------|------|
+| GET /get锛堣鎯咃級 | `attachmentList` | `StorageBlob[]` |
+
+**StorageBlob 缁撴瀯锛�**
+```typescript
+interface StorageBlob {
+ id: number; // blob ID
+ url?: string; // 鏂囦欢璁块棶 URL
+ previewURL?: string; // 棰勮鍦板潃
+ name?: string; // 鍘熸枃浠跺悕
+ downloadURL?: string; // 涓嬭浇鍦板潃
+ application?: string; // 鏂囦欢鐢ㄩ��
+ storageAttachmentId?: number; // 闄勪欢鍏宠仈 ID
+}
+```
+
+---
+
+## 鍓嶇淇敼姝ラ锛堟瘡涓ā鍧楅�氱敤锛�
+
+### 姝ラ 1锛氫慨鏀� API 绫诲瀷瀹氫箟
+
+淇敼 `src/api/{module}/index.ts` 涓殑鎺ュ彛瀹氫箟锛�
+
+```typescript
+// 鉁� 寮曞叆 StorageBlob 绫诲瀷
+import type { SystemStorageApi } from '#/api/system/storage';
+
+export namespace XxxApi {
+ export interface Xxx {
+ // ... 鍏朵粬瀛楁涓嶅彉 ...
+
+ // 鉂� 鍒犻櫎锛歠ileUrl?: string;
+ // 鉁� 鏂板锛�
+ blobIds?: number[]; // 淇濆瓨鏃朵紶鍏ョ殑闄勪欢 blobId 鍒楄〃
+ attachmentList?: SystemStorageApi.StorageBlob[]; // 鏌ヨ鏃惰繑鍥炵殑闄勪欢鍒楄〃
+ }
+}
+```
+
+娑夊強鐨勬枃浠讹細
+
+| 鏂囦欢 | 鍒犻櫎瀛楁 | 鏂板瀛楁 |
+|------|---------|---------|
+| `src/api/hrm/leave/index.ts` | `fileUrl` | `blobIds`, `attachmentList` |
+| `src/api/hrm/resignation/index.ts` | `fileUrl` | `blobIds`, `attachmentList` |
+| `src/api/hrm/transfer/index.ts` | `fileUrl` | `blobIds`, `attachmentList` |
+| `src/api/hrm/attendance/exception/index.ts` | `fileUrl` | `blobIds`, `attachmentList` |
+| `src/api/hrm/salary/payment/index.ts` | `fileUrl` | `blobIds`, `attachmentList` |
+| `src/api/crm/saleQuotation/index.ts` | `fileUrl` | `blobIds`, `attachmentList` |
+| `src/api/crm/followup/index.ts` | `fileUrls` | `blobIds`, `attachmentList` |
+| `src/api/mes/pd/document/index.ts` | `fileUrl` | `blobIds`, `attachmentList` |
+
+### 姝ラ 2锛氫慨鏀硅〃鍗曢〉闈紙form.vue锛�
+
+**鏂板/缂栬緫妯″紡**闇�瑕佹坊鍔犳枃浠朵笂浼犵粍浠讹紝娴佺▼锛�
+
+```
+鐢ㄦ埛閫夋嫨鏂囦欢 鈫� uploadFile(files) 涓婁紶 鈫� 鑾峰緱 blobId[]
+鈫� 淇濆瓨鏃跺皢 blobIds 鍙戠粰鍚庣 鈫� 鍚庣鑷姩 bindAttachments
+```
+
+鏍稿績浠g爜妯″紡锛�
+
+```vue
+<script lang="ts" setup>
+import { ref } from 'vue';
+import { uploadFile } from '#/api/system/storage';
+import type { UploadChangeParam } from 'ant-design-vue';
+
+const blobIds = ref<number[]>([]);
+const fileList = ref([]);
+
+// 鏂囦欢涓婁紶澶勭悊
+async function handleUpload(info: UploadChangeParam) {
+ if (info.file.status === 'done') {
+ // 鏂囦欢涓婁紶鎴愬姛锛屾敹闆� blobId
+ const blobs = info.file.response;
+ if (blobs?.length) {
+ blobIds.value.push(blobs[0].id);
+ }
+ }
+}
+
+// 淇濆瓨鏃�
+async function handleSave() {
+ const data = await formApi.getValues();
+ data.blobIds = blobIds.value; // 灏� blobIds 浼犵粰鍚庣
+ await createXxx(data);
+}
+
+// 缂栬緫鏃跺洖鏄惧凡鏈夐檮浠�
+async function loadDetail(id: number) {
+ const detail = await getXxx(id);
+ if (detail.attachmentList) {
+ fileList.value = detail.attachmentList.map(item => ({
+ uid: String(item.id),
+ name: item.name || '',
+ status: 'done',
+ url: item.url,
+ }));
+ blobIds.value = detail.attachmentList.map(item => item.id);
+ }
+}
+</script>
+
+<template>
+ <a-upload
+ v-model:file-list="fileList"
+ :action="`${baseURL}/system/storage-blob/upload`"
+ multiple
+ @change="handleUpload"
+ >
+ <a-button>
+ <upload-outlined />
+ 涓婁紶闄勪欢
+ </a-button>
+ </a-upload>
+</template>
+```
+
+**鎴栬�呬娇鐢ㄥ凡缁忓皝瑁呯殑 uploadFile 鍑芥暟閰嶅悎鎵嬪姩涓婁紶锛�**
+- 鎺ㄨ崘鏂瑰紡锛氬湪鎻愪氦鍓嶅厛璋冪敤 `uploadFile()` 涓婁紶鎵�鏈夋枃浠讹紝鎷垮埌 blobIds 鍚庝竴璧锋彁浜�
+- 澶囬�夋柟寮忥細浣跨敤 a-upload 缁勪欢鐨勮嚜鍔ㄤ笂浼狅紝鍦� `@change` 涓敹闆� blobId
+
+### 姝ラ 3锛氫慨鏀硅鎯呭睍绀�
+
+鍦ㄨ鎯�/鏌ョ湅妯″紡涓嬪睍绀洪檮浠跺垪琛紙鍙锛夛細
+
+```vue
+<template>
+ <!-- 闄勪欢灞曠ず -->
+ <div v-if="attachmentList?.length">
+ <div class="text-gray-500 mb-2">闄勪欢</div>
+ <div v-for="item in attachmentList" :key="item.id">
+ <a :href="item.url" target="_blank">{{ item.name }}</a>
+ </div>
+ </div>
+</template>
+```
+
+---
+
+## 瀛楁灞曠ず瑙勫垯
+
+| 瀛楁 | 灞曠ず浣嶇疆 | 璇存槑 |
+|------|----------|------|
+| `blobIds` | 琛ㄥ崟锛堜笉灞曠ず锛� | 闅愯棌瀛楁锛屼繚瀛樻椂浼犵粰鍚庣 |
+| `attachmentList` | 璇︽儏椤点�佽〃鍗曠紪杈戝洖鏄� | 鏄剧ず鏂囦欢鍒楄〃锛屾敮鎸侀瑙�/涓嬭浇 |
+
+## 涓氬姟瑙勫垯璇存槑
+
+| 鍦烘櫙 | 瑙勫垯 |
+|------|------|
+| 鍒涘缓鏃� | 鍏堣皟鐢� `uploadFile()` 涓婁紶鏂囦欢鎷� blobId锛屽啀鎶� blobIds 浼犵粰 create 鎺ュ彛 |
+| 鏇存柊鏃� | 鍙互鏂板/鍒犻櫎闄勪欢锛屽悗绔敹鍒� blobIds 涓嶄负 null 鏃跺厛鍒犲悗缁� |
+| 鏌ョ湅鏃� | 璇︽儏鎺ュ彛杩斿洖 `attachmentList`锛岀洿鎺ユ覆鏌� |
+| 鍒犻櫎鏃� | 鍚庣鑷姩澶勭悊闄勪欢鍒犻櫎锛屽墠绔棤闇�棰濆鎿嶄綔 |
+| 鍒楄〃/鍒嗛〉 | 涓嶈繑鍥� `attachmentList`锛堟�ц兘鑰冭檻锛夛紝浠呰鎯呮帴鍙h繑鍥� |
+
+## 鍚勬ā鍧楃壒娈婅鏄�
+
+### HRM 璇峰亣/绂昏亴/璋冨矖
+- 琛ㄥ崟椤碉細`src/views/hrm/xxx/modules/form.vue`
+- 杩欎笁涓ā鍧楃粨鏋勭浉鍚岋紝鍙傝�冭鍋囧凡瀹屾垚鐨勯泦鎴愭柟寮�
+- 鍒涘缓鏃� `blobIds` 闅忚〃鍗曟暟鎹竴璧峰彂閫�
+- 鏇存柊鏃� `blobIds` 涓嶄负 null 鏃跺悗绔噸鏂扮粦瀹�
+
+### HRM 鑰冨嫟寮傚父鐢宠
+- 琛ㄥ崟椤碉細`src/views/hrm/attendance/exception/modules/form.vue`
+- 鐩墠鍙湁 create 鍜� update銆乨elete锛屾敞鎰� update 鏃朵篃闇�瑕佸鐞嗛檮浠堕噸缁�
+
+### HRM 钖祫鍙戞斁
+- 鍒楄〃椤碉細`src/views/hrm/salary/payment/index.vue`
+- 璇︽儏椤碉細`src/views/hrm/salary/payment/modules/detail.vue`
+- 钖祫鍙戞斁鏄悗绔嚜鍔ㄧ敓鎴愮殑锛屾病鏈夊垱寤�/缂栬緫琛ㄥ崟
+- **鍙渶鍦ㄨ鎯呴〉灞曠ず attachmentList**
+- 鍒犻櫎鏃跺悗绔嚜鍔ㄥ悓姝ュ垹闄ら檮浠�
+
+### CRM 閿�鍞姤浠峰崟
+- 琛ㄥ崟椤碉細`src/views/crm/saleQuotation/modules/form.vue`
+- 鍒楄〃椤电殑 items 鏄墿鏂欐槑缁嗭紝闄勪欢灞曠ず鍦ㄨ鎯�/缂栬緫寮圭獥涓�
+- 琛ㄥ崟宸茬粡浣跨敤 `useVbenModal` + `useVbenForm`锛岄渶瑕佸湪涓婁紶鍖哄煙鍔犲叆 a-upload
+
+### CRM 璺熻繘璁板綍
+- 琛ㄥ崟椤碉細`src/views/crm/followup/modules/form.vue`
+- 涔嬪墠浣跨敤 `fileUrls: string[]` 瀛樺偍闄勪欢鍦板潃锛岀幇鍦ㄦ敼涓� blobIds
+- 鍘熸潵鍙兘鏈夊浘鐗�/闄勪欢涓婁紶鍖哄煙锛岄渶瑕佹敼涓� storage API
+
+### MES 璁捐璧勬枡
+- 琛ㄥ崟椤碉細`src/views/mes/pd/project/modules/document-form.vue`
+- 璁捐璧勬枡鏈韩灏辨槸鏂囦欢涓婁紶鍦烘櫙锛堣祫鏂欑被鍨� + 鏂囦欢涓婁紶锛�
+- 娉ㄦ剰锛氳璁¤祫鏂欑殑"鏂囦欢"鏄笟鍔″唴瀹癸紝鑰� attachment 鏄檮鍔犵殑鍙傝�冩枃浠�
+- 鍘熸湁鐨� `fileUrl` 鏄祫鏂欐枃浠惰嚜韬殑鍦板潃锛岄渶纭鏄惁涔熼渶瑕佹敼涓� attachment 鏂瑰紡
+
+---
+
+## 鏂囦欢涓婁紶娴佺▼鏃跺簭
+
+```
+鈹屸攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹� 鈹屸攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹� 鈹屸攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+鈹� 鍓嶇 鈹� 鈹� system 鈹� 鈹� 涓氬姟妯″潡 鈹�
+鈹� 鈹� 鈹� 妯″潡 鈹� 鈹� 鈹�
+鈹斺攢鈹�鈹�鈹�鈹攢鈹�鈹�鈹�鈹� 鈹斺攢鈹�鈹�鈹�鈹攢鈹�鈹�鈹�鈹�鈹� 鈹斺攢鈹�鈹�鈹�鈹攢鈹�鈹�鈹�鈹�鈹�
+ 鈹� 鈹� 鈹�
+ 鈹� 1.閫夋嫨鏂囦欢 鈹� 鈹�
+ 鈹傗攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�>鈹� 鈹�
+ 鈹� 2.杩斿洖 blobId 鈹� 鈹�
+ 鈹�<鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹� 鈹�
+ 鈹� 鈹� 鈹�
+ 鈹� 3.save(blobIds) 鈹�
+ 鈹傗攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�>鈹�
+ 鈹� 鈹� 鈹�
+ 鈹� 鈹� 4.bind(blobIds)鈹�鈹�> 鍐呴儴璋冪敤
+ 鈹� 鈹�<鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+ 鈹� 鈹� 鈹�
+ 鈹� 5.杩斿洖鎴愬姛 鈹� 鈹�
+ 鈹�<鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+ 鈹� 鈹� 鈹�
+ 鈹� 6.鏌ョ湅璇︽儏 鈹� 鈹�
+ 鈹傗攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�>鈹�
+ 鈹� 鈹� 鈹�
+ 鈹� 7.杩斿洖 attachmentList 鈹�
+ 鈹�<鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+```
+
+## 娉ㄦ剰浜嬮」
+
+1. **涓婁紶鏃舵満**锛氭枃浠朵笂浼犲簲鐙珛浜庝笟鍔′繚瀛橈紝鍏堜笂浼犳嬁 blobId锛屽啀淇濆瓨涓氬姟鏁版嵁
+2. **缂栬緫鍥炴樉**锛氱紪杈戞椂蹇呴』閫氳繃 GET /get 鎺ュ彛鎷垮埌 attachmentList 鍥炴樉鍒颁笂浼犵粍浠�
+3. **澶ф枃浠跺鐞�**锛氬鏋滄枃浠惰緝澶э紝闇�瑕佸湪淇濆瓨鍓嶇瓑寰呬笂浼犲畬鎴愶紙loading 鐘舵�侊級
+4. **鍒犻櫎闄勪欢**锛氬墠绔彲浠ラ�氳繃 `deleteAttachments([attachmentId])` 鍒犻櫎鍗曚釜闄勪欢锛屼絾涓嶅己鍒讹紝鍥犱负淇濆瓨鏃� blobIds 涓嶄负 null 浼氳Е鍙戝悗绔噸鏂扮粦瀹�
+5. **钖祫鍙戞斁鐗规畩**锛氳柂璧勫彂鏀炬棤鍒涘缓/缂栬緫琛ㄥ崟锛屽彧闇�鍦ㄥ叾璇︽儏椤靛睍绀� attachmentList
+6. **MES 璁捐璧勬枡鐗规畩**锛歚MesPdDocumentDO` 鍘熸湁鐨� `fileUrl` 瀛楁宸茶鍒犻櫎锛屽鏋滆繖涓瓧娈垫槸浣滀负涓氬姟鏂囦欢鍦板潃锛堣�岄潪闄勪欢锛夛紝鍙兘闇�瑕佸崟鐙瘎浼版槸鍚﹂渶瑕佷繚鐣欐枃浠跺瓨鍌�
+7. **璺熻繘璁板綍鐗规畩**锛氳窡杩涜褰曚箣鍓嶇敤 `fileUrls: string[]`锛堟暟缁勶級锛岀幇鍦ㄦ敼涓� `blobIds: number[]`锛屾帴鍙e眰闈㈠彉鍖栬緝澶�
+8. **鍒楄〃椤典笉灞曠ず闄勪欢**锛氫负鎬ц兘鑰冭檻锛屽垪琛�/鍒嗛〉鎺ュ彛涓嶈繑鍥� attachmentList锛屽彧鍦ㄨ鎯呮帴鍙h繑鍥�
diff --git a/docs/attachment_upload_reform.md b/docs/attachment_upload_reform.md
new file mode 100644
index 0000000..31a638c
--- /dev/null
+++ b/docs/attachment_upload_reform.md
@@ -0,0 +1,168 @@
+# 闄勪欢涓婁紶妯″潡鏀归�� - 鍓嶇鑱旇皟鏂规
+
+## 姒傝堪
+
+鏈鏀归�犵粺涓�鎵�鏈夋ā鍧楃殑鏂囦欢涓婁紶锛�
+
+1. **鍚庣**锛氬垹闄や笟鍔¤〃涓殑 `file_url` 鍒楋紝鍏ㄩ儴鏀圭敤 `system_storage_attachment` 涓棿琛ㄥ叧鑱�
+2. **鍓嶇**锛欶ileUpload 缁勪欢缁熶竴浣跨敤 `/system/storage-blob/upload`锛岄檮浠堕�氳繃 attachment API 鍏宠仈
+
+## 鍚庣鍙樻洿
+
+### 宸插垹闄� file_url 鍒楃殑琛�
+
+| 琛ㄥ悕 | 妯″潡 |
+|------|------|
+| `erp_purchase_order` | ERP 閲囪喘璁㈠崟 |
+| `erp_purchase_request` | ERP 閲囪喘鐢宠 |
+| `erp_sale_order` | ERP 閿�鍞鍗� |
+| `hrm_leave_application` | HRM 璇峰亣 |
+| `hrm_resignation_application` | HRM 绂昏亴 |
+| `hrm_transfer_application` | HRM 璋冨姩 |
+
+### 宸蹭粠 Java 绫讳腑鍒犻櫎 fileUrl 瀛楁
+
+- 6 涓� DO 绫伙紙ErpPurchaseOrderDO銆丒rpPurchaseRequestDO銆丒rpSaleOrderDO銆丠rmLeaveApplicationDO銆丠rmResignationApplicationDO銆丠rmTransferApplicationDO锛�
+- 6 涓� SaveReqVO 绫�
+- 6 涓� RespVO 绫�
+
+### 鏂囦欢鍏宠仈鏂瑰紡
+
+鏂囦欢鍙兘閫氳繃 `system_storage_attachment` 涓棿琛ㄥ叧鑱旓細
+
+| API | 璇存槑 |
+|-----|------|
+| `POST /system/storage-blob/upload` | 涓婁紶鏂囦欢锛坄files` 鍙傛暟锛宮ultipart锛� |
+| `POST /system/storage-attachment/bind` | 缁戝畾闄勪欢鍒颁笟鍔¤褰� |
+| `GET /system/storage-attachment/list` | 鏌ヨ闄勪欢锛堟寜 recordType + recordId锛� |
+| `DELETE /system/storage-attachment/delete` | 鎵归噺鍒犻櫎闄勪欢 |
+
+## 娑夊強椤甸潰
+
+### 涓�銆佹牳蹇冧笂浼犵粍浠舵敼鍔�
+
+**鏂囦欢**锛歚src/components/upload/use-upload.ts`
+
+| 鏀瑰姩椤� | 鏃у�� | 鏂板�� |
+|--------|------|------|
+| 涓婁紶鎺ュ彛 | `POST /infra/file/upload` / S3 鐩翠紶 | `POST /system/storage-blob/upload` |
+| 璇锋眰鏂瑰紡 | 鑷畾涔� `createFile0()` / PUT S3 | `FormData` + `requestClient.post()` |
+| uploadUrl | `${apiURL}/infra/file/upload` | `${apiURL}/system/storage-blob/upload` |
+
+### 浜屻�佷笂浼犻檺鍒舵斁寮�
+
+浠ヤ笅妯″潡鐨� `FileUpload` / `ImageUpload` 缁勪欢宸茬Щ闄� `maxNumber`銆乣maxSize`銆乣accept` 闄愬埗锛�
+
+#### ERP 妯″潡锛�13 涓枃浠讹級
+
+| 鏂囦欢 | 瀛楁 |
+|------|------|
+| `src/views/erp/purchase/order/data.ts` | `fileUrl` |
+| `src/views/erp/purchase/request/data.ts` | `fileUrl` |
+| `src/views/erp/purchase/return/data.ts` | `fileUrl` |
+| `src/views/erp/purchase/in/data.ts` | `fileUrl` |
+| `src/views/erp/sale/order/data.ts` | `fileUrl` |
+| `src/views/erp/sale/return/data.ts` | `fileUrl` |
+| `src/views/erp/sale/out/data.ts` | `fileUrl` |
+| `src/views/erp/stock/in/data.ts` | `fileUrl` |
+| `src/views/erp/stock/out/data.ts` | `fileUrl` |
+| `src/views/erp/stock/move/data.ts` | `fileUrl` |
+| `src/views/erp/stock/check/data.ts` | `fileUrl` |
+| `src/views/erp/finance/receipt/data.ts` | `fileUrl` |
+| `src/views/erp/finance/payment/data.ts` | `fileUrl` |
+
+#### ImageUpload 闄愬埗鏀惧紑锛�5 澶勶級
+
+| 鏂囦欢 | 瀛楁 |
+|------|------|
+| `src/views/im/manager/face/pack/data.ts` | `icon` |
+| `src/views/im/manager/face/pack/data.ts` | `url` |
+| `src/views/im/manager/channel/material/data.ts` | `coverUrl` |
+| `src/views/im/manager/channel/list/data.ts` | `avatar` |
+| `src/views/mes/md/item/modules/product-media-list.vue` | `url` |
+
+#### MES 鏂囨。闄愬埗鏀惧紑
+
+| 鏂囦欢 | 瀛楁 |
+|------|------|
+| `src/views/mes/pd/project/modules/document-form.vue` | `fileUrl` |
+
+### 涓夈�丠RM 闄勪欢涓婁紶淇
+
+3 涓� HRM 妯″潡鐨�"闄勪欢"瀛楁浠� `Input` 鏀逛负 `FileUpload`锛�
+
+| 鏂囦欢 | 瀛楁 | 鏀瑰姩 |
+|------|------|------|
+| `src/views/hrm/leave/data.ts` | `fileUrl` | `component: 'Input'` 鈫� `component: 'FileUpload'` |
+| `src/views/hrm/resignation/data.ts` | `fileUrl` | `component: 'Input'` 鈫� `component: 'FileUpload'` |
+| `src/views/hrm/transfer/data.ts` | `fileUrl` | `component: 'Input'` 鈫� `component: 'FileUpload'` |
+
+## API
+
+| 鏂规硶 | 璺緞 | 璇存槑 |
+|------|------|------|
+| POST | `/system/storage-blob/upload` | 涓婁紶鏂囦欢锛宍files`锛坢ultipart 鏁扮粍锛� |
+| POST | `/system/storage-attachment/bind` | 缁戝畾闄勪欢鍒颁笟鍔¤褰� |
+| GET | `/system/storage-attachment/list` | 鏌ヨ涓氬姟璁板綍鐨勯檮浠跺垪琛� |
+| DELETE | `/system/storage-attachment/delete` | 鎵归噺鍒犻櫎闄勪欢 |
+
+**涓婁紶鍝嶅簲**锛�
+
+```json
+[
+ {
+ "id": 123,
+ "url": "https://xxx.com/u_abc123.pdf",
+ "previewURL": "https://xxx.com/preview/u_abc123.pdf",
+ "name": "鍚堝悓.pdf",
+ "downloadURL": "https://xxx.com/download/u_abc123.pdf"
+ }
+]
+```
+
+**缁戝畾璇锋眰**锛�
+
+```json
+{
+ "application": "file",
+ "recordType": "mes_purchase_order",
+ "recordId": 1,
+ "blobItems": [{ "blobId": 123 }]
+}
+```
+
+## 鏍囧噯涓婁紶娴佺▼
+
+1. 鐢ㄦ埛閫夋嫨鏂囦欢 鈫� `POST /system/storage-blob/upload` 杩斿洖 `[{ id, url, name }]`
+2. 涓氬姟璁板綍淇濆瓨鏃� 鈫� `POST /system/storage-attachment/bind` 鍏宠仈 blobId
+3. 璇︽儏/缂栬緫椤� 鈫� `GET /system/storage-attachment/list` 鍔犺浇宸叉湁闄勪欢
+4. 鐢ㄦ埛鍒犻櫎闄勪欢 鈫� `DELETE /system/storage-attachment/delete`
+
+## 鍓嶇 API 灏佽
+
+鏂� API 妯″潡浣嶄簬 `src/api/system/storage/index.ts`锛�
+
+```typescript
+import { uploadFile, listAttachments, bindAttachments, deleteAttachments } from '#/api/system/storage';
+```
+
+## recordType 鍛藉悕瑙勮寖
+
+鏍煎紡锛歚{妯″潡鍓嶇紑}_{涓氬姟琛ㄥ悕}`
+
+| 妯″潡 | recordType | 璇存槑 |
+|------|------------|------|
+| MES 閲囪喘璁㈠崟 | `mes_purchase_order` | 閲囪喘璁㈠崟闄勪欢 |
+| MES 宸ュ崟 | `mes_pro_work_order` | 鐢熶骇宸ュ崟闄勪欢 |
+| MES 璁惧 | `mes_dv_machinery` | 璁惧妗f闄勪欢 |
+| MES 鏂囨。 | `mes_pd_document` | 椤圭洰鏂囨。闄勪欢 |
+| CRM 瀹㈡埛 | `crm_customer` | 瀹㈡埛闄勪欢 |
+| ERP 閲囪喘璁㈠崟 | `erp_purchase_order` | ERP 閲囪喘璁㈠崟闄勪欢 |
+
+## 娉ㄦ剰浜嬮」
+
+- FileUpload 缁勪欢浣跨敤 `POST /system/storage-blob/upload` 涓婁紶鏂囦欢锛孎ormData 瀛楁鍚嶄负 `files`
+- 鏂囦欢涓婁紶鍚庝粎鏆傚瓨锛屽繀椤昏皟鐢� bind 鎺ュ彛鍏宠仈鍒颁笟鍔¤褰�
+- 鏂囦欢棰勮浣跨敤鍝嶅簲涓殑 `previewURL` 鎴� `url` 瀛楁
+- 绂佹浣跨敤鏃х増 `/infra/file/*` 鎺ュ彛
+- HRM 璇峰亣/绂昏亴/璋冨姩椤甸潰鐨勯檮浠跺瓧娈电幇鍦ㄥ彲浠ヤ娇鐢� FileUpload 缁勪欢涓婁紶闄勪欢
diff --git a/docs/file_upload_guide.md b/docs/file_upload_guide.md
index c8a659e..21b9505 100644
--- a/docs/file_upload_guide.md
+++ b/docs/file_upload_guide.md
@@ -2,243 +2,171 @@
## 姒傝堪
-鏈郴缁熸彁渚涗簡**缁熶竴鐨勬枃浠朵笂浼犳湇鍔�**锛屾敮鎸佸绉嶅瓨鍌ㄦ柟寮忥紙鏈湴銆丼3/OSS銆佹暟鎹簱銆丗TP/SFTP锛夈��
+鏈郴缁熶娇鐢� `yudao-module-system` 妯″潡鐨� **Storage API** 浣滀负缁熶竴鐨勬枃浠朵笂浼犳湇鍔★紝鏀寔澶氱瀛樺偍鏂瑰紡锛堟湰鍦般�丼3/OSS锛夈��
-> **閲嶈**锛氭墍鏈変笟鍔℃ā鍧楀繀椤讳娇鐢ㄩ�氱敤鏂囦欢涓婁紶鎺ュ彛锛�**绂佹涓烘瘡涓ā鍧楁柊澧炰笂浼犳帴鍙�**銆�
+> **閲嶈**锛氭墍鏈変笟鍔℃ā鍧楀繀椤讳娇鐢� system 妯″潡鐨� Storage API锛�**绂佹浣跨敤 infra 妯″潡鐨� `/infra/file/*` 鎺ュ彛锛岀姝负姣忎釜妯″潡鏂板涓婁紶鎺ュ彛**銆�
---
-## 涓�銆侀�氱敤涓婁紶鎺ュ彛
+## 涓�銆侀�氱敤鎺ュ彛
-### 1.1 鎺ュ彛淇℃伅
+### 1.1 鏂囦欢涓婁紶
| 鏂规硶 | 璺緞 | 璇存槑 |
|------|------|------|
-| POST | `/infra/file/upload` | 閫氱敤鏂囦欢涓婁紶 |
+| POST | `/system/storage-blob/upload` | 閫氱敤鏂囦欢涓婁紶 |
-### 1.2 璇锋眰鍙傛暟
+**璇锋眰鍙傛暟**锛歚files`锛圡ultipartFile 鏁扮粍锛�
+
+**鍝嶅簲**锛�
+
+```json
+[
+ {
+ "id": 123,
+ "resourceKey": "abc123",
+ "contentType": "application/pdf",
+ "originalFilename": "鍚堝悓.pdf",
+ "uidFilename": "u_abc123.pdf",
+ "byteSize": 102400,
+ "path": "/storage/2024/01/abc123.pdf",
+ "previewURL": "https://xxx.com/preview/u_abc123.pdf",
+ "url": "https://xxx.com/u_abc123.pdf",
+ "name": "鍚堝悓.pdf",
+ "downloadURL": "https://xxx.com/download/u_abc123.pdf"
+ }
+]
+```
+
+### 1.2 闄勪欢缁戝畾
+
+| 鏂规硶 | 璺緞 | 璇存槑 |
+|------|------|------|
+| POST | `/system/storage-attachment/bind` | 灏嗗凡涓婁紶鏂囦欢缁戝畾鍒颁笟鍔¤褰� |
+
+**璇锋眰鍙傛暟**锛�
| 鍙傛暟 | 绫诲瀷 | 蹇呭~ | 璇存槑 |
|------|------|------|------|
-| file | MultipartFile | 鏄� | 涓婁紶鐨勬枃浠� |
-| directory | String | 鍚� | 瀛樺偍鐩綍锛屽 `purchase-request`銆乣contract` |
+| application | String | 鍚� | 鏂囦欢鐢ㄩ�旓紝濡� `file` |
+| recordType | String | 鏄� | 鍏宠仈璁板綍绫诲瀷锛屽 `mes_purchase_order` |
+| recordId | Long | 鏄� | 鍏宠仈璁板綍 ID |
+| blobItems | List\<BlobItem\> | 鏄� | 寰呯粦瀹氭枃浠跺垪琛� |
-### 1.3 鍝嶅簲绀轰緥
+`BlobItem`锛�
-```json
-{
- "code": 0,
- "data": "https://xxx.com/xxx.pdf",
- "msg": "鎿嶄綔鎴愬姛"
-}
-```
+| 鍙傛暟 | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+|------|------|------|------|
+| blobId | Long | 鏄� | 涓婁紶杩斿洖鐨勬枃浠� ID |
+| application | String | 鍚� | 瑕嗙洊澶栧眰鐨勬枃浠剁敤閫� |
-杩斿洖鍊� `data` 鍗充负鏂囦欢璁块棶 URL锛屽彲鐩存帴瀛樺偍鍒版暟鎹簱銆�
+### 1.3 鏌ヨ闄勪欢鍒楄〃
+
+| 鏂规硶 | 璺緞 | 璇存槑 |
+|------|------|------|
+| GET | `/system/storage-attachment/list` | 鏌ヨ涓氬姟璁板綍鐨勯檮浠� |
+
+**璇锋眰鍙傛暟**锛�
+
+| 鍙傛暟 | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+|------|------|------|------|
+| recordType | String | 鏄� | 鍏宠仈璁板綍绫诲瀷 |
+| recordId | Long | 鏄� | 鍏宠仈璁板綍 ID |
+| application | String | 鍚� | 鎸夌敤閫旇繃婊� |
+
+### 1.4 鍒犻櫎闄勪欢
+
+| 鏂规硶 | 璺緞 | 璇存槑 |
+|------|------|------|
+| DELETE | `/system/storage-attachment/delete` | 鎵归噺鍒犻櫎闄勪欢 |
+
+**璇锋眰浣�**锛氭枃浠� ID 鏁扮粍 `[1, 2, 3]`
---
## 浜屻�佸墠绔泦鎴�
-### 2.1 涓婁紶缁勪欢绀轰緥
+### 2.1 浣跨敤灏佽濂界殑 API
-```html
-<el-upload
- :action="uploadUrl"
- :headers="headers"
- :data="{ directory: 'purchase-request' }"
- :on-success="handleUploadSuccess"
- :before-upload="beforeUpload"
->
- <el-button type="primary">鐐瑰嚮涓婁紶</el-button>
-</el-upload>
+```typescript
+import { bindAttachments, listAttachments, uploadFile } from '#/api/system/storage';
+
+// 1. 涓婁紶鏂囦欢
+const handleUpload = async (file: File) => {
+ const blobs = await uploadFile([file]);
+ // 鑾峰彇 blob ID
+ const blobId = blobs[0]!.id;
+ return blobId;
+};
+
+// 2. 缁戝畾鍒颁笟鍔¤褰�
+const handleBind = async (blobIds: number[], recordId: number) => {
+ await bindAttachments({
+ application: 'file',
+ recordType: 'mes_pro_work_order',
+ recordId,
+ blobItems: blobIds.map(id => ({ blobId: id })),
+ });
+};
+
+// 3. 鏌ヨ闄勪欢鍒楄〃
+const handleLoad = async (recordId: number) => {
+ const attachments = await listAttachments({
+ recordType: 'mes_pro_work_order',
+ recordId,
+ });
+ return attachments;
+};
```
-### 2.2 閰嶇疆璇存槑
+### 2.2 鏍囧噯娴佺▼
-```js
-data() {
- return {
- // 閫氱敤涓婁紶鎺ュ彛
- uploadUrl: process.env.VUE_APP_BASE_API + '/infra/file/upload',
- // 璁よ瘉澶�
- headers: { Authorization: 'Bearer ' + getToken() },
- }
-},
-methods: {
- handleUploadSuccess(response) {
- if (response.code === 0) {
- this.form.fileUrl = response.data // 淇濆瓨鏂囦欢 URL
- }
- },
- beforeUpload(file) {
- // 涓氬姟灞傛牎楠屾枃浠剁被鍨嬪拰澶у皬
- const isLt10M = file.size / 1024 / 1024 < 10
- if (!isLt10M) {
- this.$message.error('鏂囦欢澶у皬涓嶈兘瓒呰繃 10MB!')
- }
- return isLt10M
- }
-}
-```
+1. 鐢ㄦ埛閫夋嫨鏂囦欢 鈫� 璋冪敤 `POST /system/storage-blob/upload` 涓婁紶
+2. 涓氬姟璁板綍淇濆瓨鏃� 鈫� 璋冪敤 `POST /system/storage-attachment/bind` 缁戝畾闄勪欢鍒拌褰�
+3. 璇︽儏/缂栬緫椤� 鈫� 璋冪敤 `GET /system/storage-attachment/list` 鍔犺浇宸叉湁闄勪欢
+4. 鐢ㄦ埛鍒犻櫎闄勪欢 鈫� 璋冪敤 `DELETE /system/storage-attachment/delete`
+
+### 2.3 displayUpload 缁勪欢
+
+椤圭洰涓凡鏈夋枃浠朵笂浼犳樉绀虹粍浠讹紝鍙洿鎺ヤ娇鐢ㄧ郴缁� Storage API銆�
---
-## 涓夈�佺洰褰曡鑼�
+## 涓夈�乺ecordType 鍛藉悕瑙勮寖
-鍚勪笟鍔℃ā鍧楅�氳繃 `directory` 鍙傛暟鍖哄垎瀛樺偍鐩綍锛�**绂佹鏂板涓氬姟涓婁紶鎺ュ彛**锛�
+鏍煎紡锛歚{妯″潡鍓嶇紑}_{涓氬姟琛ㄥ悕}`
-| 妯″潡 | directory | 璇存槑 |
-|------|-----------|------|
-| 鐢ㄦ埛澶村儚 | `avatar` | 鐢ㄦ埛澶村儚鍥剧墖 |
-| 鍚堝悓闄勪欢 | `contract` | 鍚堝悓鐩稿叧鏂囦欢 |
-| 閲囪喘鐢宠 | `purchase-request` | 閲囪喘鐢宠闄勪欢 |
-| 閲囪喘璁㈠崟 | `purchase-order` | 閲囪喘璁㈠崟闄勪欢 |
-| 浜у搧鍥剧墖 | `product` | 浜у搧鐩稿叧鍥剧墖 |
-| 閫氱敤闄勪欢 | `attachment` | 閫氱敤涓氬姟闄勪欢 |
-
-> **鍛藉悕瑙勮寖**锛氫娇鐢ㄥ皬鍐欏瓧姣嶅拰杩炲瓧绗︼紝濡� `purchase-request`锛屼笌妯″潡鍚嶄繚鎸佷竴鑷淬��
+| 妯″潡 | recordType | 璇存槑 |
+|------|------------|------|
+| MES 閲囪喘璁㈠崟 | `mes_purchase_order` | 閲囪喘璁㈠崟闄勪欢 |
+| MES 宸ュ崟 | `mes_pro_work_order` | 鐢熶骇宸ュ崟闄勪欢 |
+| MES 璁惧 | `mes_dv_machinery` | 璁惧妗f闄勪欢 |
+| MES 鏂囨。 | `mes_pd_document` | 椤圭洰鏂囨。闄勪欢 |
+| CRM 瀹㈡埛 | `crm_customer` | 瀹㈡埛闄勪欢 |
+| ERP 浜у搧 | `erp_product` | 浜у搧闄勪欢 |
---
-## 鍥涖�佸悗绔泦鎴愶紙鐗规畩鍦烘櫙锛�
+## 鍥涖�佺姝㈣涓�
-### 4.1 鍚庣闇�瑕佷富鍔ㄤ笂浼犳枃浠舵椂
-
-鏋佸皯鏁板満鏅笅锛屽悗绔渶瑕佷富鍔ㄤ笂浼犳枃浠讹紙濡傜敓鎴愭姤琛ㄥ悗淇濆瓨锛夛紝鍙敞鍏� `FileApi`锛�
-
-```java
-@Resource
-private FileApi fileApi;
-
-// 涓婁紶鏂囦欢
-String url = fileApi.createFile(bytes, "鎶ヨ〃.xlsx", "report", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
-```
-
-### 4.2 鑾峰彇绉佹湁鏂囦欢棰勭鍚嶅湴鍧�
-
-```java
-// 鐢熸垚涓存椂璁块棶鍦板潃锛�1灏忔椂鏈夋晥
-String presignedUrl = fileApi.presignGetUrl(fileUrl, 3600);
-```
+- 鉂� 绂佹璋冪敤 `/infra/file/upload` 鎴� `/infra/file/presigned-url`
+- 鉂� 绂佹鍦� MES/CRM/ERP 绛変笟鍔℃ā鍧� Controller 鏂板 `@PostMapping("/upload")`
+- 鉂� 绂佹娉ㄥ叆 `cn.iocoder.yudao.module.infra.api.file.FileApi`
+- 鉂� 绂佹浣跨敤鏃х増 `src/api/infra/file/index.ts` 涓殑 `uploadFile()` 鍑芥暟
---
-## 浜斻�佸瓨鍌ㄩ厤缃�
+## 浜斻�佸墠绔� API 妯″潡浣嶇疆
-瀛樺偍閰嶇疆閫氳繃绠$悊鍚庡彴鍔ㄦ�侀厤缃紝鏃犻渶淇敼浠g爜锛�
+`src/api/system/storage/index.ts`
-1. 璁块棶 **鍩虹璁炬柦 > 鏂囦欢绠$悊 > 鏂囦欢閰嶇疆**
-2. 鏂板閰嶇疆锛岄�夋嫨瀛樺偍绫诲瀷骞跺~鍐欏弬鏁�
-3. 鐐瑰嚮"璁剧疆涓轰富閰嶇疆"婵�娲讳娇鐢�
-
-### 鏀寔鐨勫瓨鍌ㄧ被鍨�
-
-| 绫诲瀷 | 璇存槑 | 閫傜敤鍦烘櫙 |
-|------|------|----------|
-| DB | 瀛樺偍鍒版暟鎹簱 | 灏忔枃浠躲�佷复鏃舵枃浠� |
-| LOCAL | 鏈湴纾佺洏瀛樺偍 | 鍐呯綉閮ㄧ讲銆佸紑鍙戠幆澧� |
-| FTP/SFTP | FTP 鏈嶅姟鍣� | 鍏煎鑰佺郴缁� |
-| S3 | 闃块噷浜� OSS / 鑵捐浜� COS / MinIO | 鐢熶骇鐜 |
+瀵煎嚭鍑芥暟锛歚uploadFile`銆乣listAttachments`銆乣bindAttachments`銆乣deleteAttachments`
---
-## 鍏�佹枃浠跺瓨鍌ㄦ柟寮�
+## 鍏�佹敞鎰忎簨椤�
-### 6.1 澶氭枃浠跺瓨鍌紙鎺ㄨ崘锛�
-
-浣跨敤 `List<String>` + `StringListTypeHandler`锛屽弬鑰� CRM 璺熻繘璁板綍妯″潡锛�
-
-**瀹炰綋绫伙細**
-
-```java
-@TableName(value = "crm_follow_up_record", autoResultMap = true)
-public class CrmFollowUpRecordDO extends BaseDO {
-
- /**
- * 鍥剧墖
- */
- @TableField(typeHandler = StringListTypeHandler.class)
- private List<String> picUrls;
-
- /**
- * 闄勪欢
- */
- @TableField(typeHandler = StringListTypeHandler.class)
- private List<String> fileUrls;
-}
-```
-
-**鏁版嵁搴撳瓧娈�**锛歚VARCHAR`锛屽瓨鍌� JSON 鏁扮粍鏍煎紡濡� `["url1","url2","url3"]`
-
-**VO 绫伙細**
-
-```java
-@Schema(description = "闄勪欢")
-private List<String> fileUrls;
-```
-
-**鍓嶇浣跨敤锛�**
-
-```js
-// 涓婁紶鎴愬姛鍚庤拷鍔�
-handleUploadSuccess(response) {
- if (response.code === 0) {
- if (!this.form.fileUrls) {
- this.form.fileUrls = []
- }
- this.form.fileUrls.push(response.data)
- }
-}
-
-// 鍒犻櫎鏂囦欢
-handleRemove(index) {
- this.form.fileUrls.splice(index, 1)
-}
-```
-
-### 6.2 鍗曟枃浠跺瓨鍌�
-
-```java
-// 瀹炰綋绫�
-private String fileUrl;
-
-// VO 绫�
-@Schema(description = "闄勪欢鍦板潃")
-private String fileUrl;
-```
-
-### 6.3 `infra_file` 琛ㄧ殑浣滅敤
-
-绯荤粺 `infra_file` 琛ㄨ褰曟墍鏈変笂浼犳枃浠剁殑鍏冩暟鎹紙name銆乸ath銆乽rl銆乼ype銆乻ize锛夛紝涓嶄笌涓氬姟鍏宠仈锛屼粎鐢ㄤ簬锛�
-- 鏂囦欢绠$悊鍚庡彴鏌ョ湅涓婁紶璁板綍
-- 缁熻瀛樺偍浣跨敤鎯呭喌
-
----
-
-## 涓冦�佹敞鎰忎簨椤�
-
-1. **浣跨敤閫氱敤鎺ュ彛**锛氭墍鏈夋枃浠朵笂浼犲繀椤讳娇鐢� `/infra/file/upload`锛岀姝㈡柊澧炰笟鍔′笂浼犳帴鍙�
-2. **鍖哄垎鐩綍**锛氶�氳繃 `directory` 鍙傛暟鍖哄垎涓氬姟妯″潡锛屼究浜庣鐞嗗拰娓呯悊
-3. **鏂囦欢澶у皬**锛氶粯璁ら檺鍒� 10MB锛屽彲鍦� Nginx 鎴栧簲鐢ㄥ眰璋冩暣
-4. **绫诲瀷鏍¢獙**锛氬墠绔簲鏍¢獙鍏佽鐨勬枃浠剁被鍨嬶紝闃叉涓婁紶鎭舵剰鏂囦欢
-5. **URL 瀛樺偍**锛氫笂浼犳垚鍔熷悗锛屽皢杩斿洖鐨� URL 瀛樺偍鍒颁笟鍔¤〃鐨� `fileUrl` 瀛楁
-
----
-
-## 涓冦�佸父瑙侀棶棰�
-
-**Q: 涓轰粈涔堜笉鑳戒负姣忎釜妯″潡鏂板涓婁紶鎺ュ彛锛�**
-
-A: 缁熶竴鎺ュ彛渚夸簬锛�
-- 缁熶竴绠$悊瀛樺偍閰嶇疆
-- 缁熶竴鏉冮檺鎺у埗鍜屽璁�
-- 閬垮厤浠g爜閲嶅
-- 鍚庣画缁存姢鍜岃縼绉�
-
-**Q: 濡備綍鍖哄垎涓嶅悓妯″潡鐨勬枃浠讹紵**
-
-A: 浣跨敤 `directory` 鍙傛暟锛屾枃浠朵細瀛樺偍鍦ㄥ搴旂洰褰曚笅銆�
-
-**Q: 鍓嶇濡備綍闄愬埗鏂囦欢绫诲瀷锛�**
-
-A: 鍦� `beforeUpload` 鏂规硶涓牎楠� `file.type` 鎴� `file.name` 鍚庣紑銆�
+1. **浣跨敤閫氱敤鎺ュ彛**锛氭墍鏈変笂浼犺蛋 system 妯″潡锛岄�氳繃 `recordType` 鍖哄垎涓氬姟
+2. **涓婁紶鍚庣粦瀹�**锛氭枃浠朵笂浼犲彧鏄殏瀛橈紝蹇呴』璋冪敤 bind 鎺ュ彛鍏宠仈鍒颁笟鍔¤褰�
+3. **鏂囦欢棰勮**锛氫娇鐢ㄥ搷搴斾腑鐨� `previewURL` 鎴� `url` 瀛楁
+4. **鏂囦欢涓嬭浇**锛氫娇鐢� `GET /system/storage-blob/download/{fileName}?token=xxx`
diff --git a/docs/mdm_item_sync_mes_optimization.md b/docs/mdm_item_sync_mes_optimization.md
new file mode 100644
index 0000000..fd6f6ba
--- /dev/null
+++ b/docs/mdm_item_sync_mes_optimization.md
@@ -0,0 +1,97 @@
+# MDM 鐗╂枡鍚屾 MES 浼樺寲 - 鍓嶇鑱旇皟鏂规
+
+## 娑夊強椤甸潰
+
+- 鍩虹鏁版嵁 > MDM > 鐗╂枡绠$悊锛堝垪琛ㄩ〉锛�
+- 鍩虹鏁版嵁 > MDM > 鐗╂枡绠$悊锛堟柊澧�/缂栬緫寮圭獥锛�
+
+## 鍙樻洿姒傝堪
+
+1. **绉婚櫎 `syncMes` 澶嶉�夋**锛氬垱寤�/缂栬緫鐗╂枡鏃朵笉鍐嶉渶瑕佹墜鍔ㄥ嬀閫�"鍚屾鍒� MES"锛屽悗绔嚜鍔ㄥ悓姝�
+2. **鍒楄〃鏂板鍚屾鐘舵�佸垪**锛氭樉绀虹墿鏂欐槸鍚﹀凡鍚屾鍒� MES
+3. **鐘舵��/鍒犻櫎瀹炴椂鍚屾**锛歁DM 渚ф洿鏂扮姸鎬佹垨鍒犻櫎鐗╂枡鏃讹紝鑷姩鍚屾鍒� MES
+
+## API 鍙樻洿
+
+### 1. GET `/mdm/item/page` - 鍝嶅簲鏂板瀛楁
+
+**鍝嶅簲瀛楁鏂板锛�**
+
+| 瀛楁 | 绫诲瀷 | 璇存槑 |
+|------|------|------|
+| syncedMes | Boolean | 鏄惁宸插悓姝ュ埌 MES銆倀rue=宸插悓姝ワ紝false/null=鏈悓姝� |
+
+**鍝嶅簲绀轰緥锛�**
+
+```json
+{
+ "code": 0,
+ "data": {
+ "list": [
+ {
+ "id": 1,
+ "code": "ITEM001",
+ "name": "铻轰笣",
+ "syncedMes": true,
+ "...": "..."
+ },
+ {
+ "id": 2,
+ "code": "ITEM002",
+ "name": "铻烘瘝",
+ "syncedMes": false,
+ "...": "..."
+ }
+ ],
+ "total": 100
+ }
+}
+```
+
+### 2. GET `/mdm/item/get` - 鍝嶅簲鏂板瀛楁
+
+鍚屼笂锛宍syncedMes` 琛ㄧず璇ョ墿鏂欐槸鍚﹀凡鍚屾鍒� MES銆�
+
+## 瀛楁灞曠ず瑙勫垯
+
+| 瀛楁 | 灞曠ず浣嶇疆 | 璇存槑 |
+|------|----------|------|
+| syncedMes | 鍒楄〃鏂板鍒� | true 鏃舵樉绀虹豢鑹�"宸插悓姝�"鏍囩锛宖alse 鏃舵樉绀虹伆鑹�"鏈悓姝�"鏍囩 |
+
+## 涓氬姟瑙勫垯璇存槑
+
+| 鍦烘櫙 | 瑙勫垯 |
+|------|------|
+| 鏂板鐗╂枡 | 鍚庣鑷姩鍚屾鍒� MES锛屽垪琛ㄤ腑 syncedMes 涓� true |
+| 缂栬緫鐗╂枡 | 鍚庣鑷姩鍚屾鍒� MES |
+| 鏇存柊鐗╂枡鐘舵�� | 鍚庣鑷姩鍚屾鐘舵�佸埌 MES锛堝惎鐢�/绂佺敤锛� |
+| 鍒犻櫎鐗╂枡 | 鍚庣鑷姩鍚屾鍒犻櫎 MES 涓殑瀵瑰簲璁板綍锛堝瀛樺湪锛� |
+| 琛ㄥ崟涓殑 syncMes 瀛楁 | 鍓嶇鍙繚鐣欎絾涓嶅啀鐢熸晥锛屽缓璁殣钘�"鍚屾鍒� MES"寮�鍏� |
+
+## 鍓嶇淇敼鐐�
+
+### 1. TypeScript 绫诲瀷瀹氫箟
+
+`src/api/mdm/item/index.ts` 涓� `MdmItemApi.Item` 鎺ュ彛鏂板 `syncedMes?: boolean` 瀛楁銆�
+
+### 2. 鍒楄〃鏂板鍚屾鐘舵�佸垪
+
+`src/views/basicData/mdm/data.ts` 涓� `useGridColumns()` 鏂板涓�鍒楋細
+
+- field: `syncedMes`
+- title: "鍚屾鐘舵��"
+- 浣跨敤鎻掓Ы娓叉煋 Tag 缁勪欢
+
+### 3. 鍒楄〃椤垫ā鏉�
+
+`src/views/basicData/mdm/index.vue` 涓柊澧� `syncedMes` 鎻掓Ы妯℃澘锛屽睍绀哄悓姝ョ姸鎬佹爣绛俱��
+
+### 4. 琛ㄥ崟绉婚櫎 syncMes 寮�鍏筹紙鍙�夛級
+
+`src/views/basicData/mdm/data.ts` 涓� `useFormSchema()` 鍙殣钘忔垨绉婚櫎 `syncMes` 琛ㄥ崟椤癸紝鍚庣宸插拷鐣ユ鍙傛暟銆�
+
+## 娉ㄦ剰浜嬮」
+
+- 鍘嗗彶鏁版嵁涓湭鍚屾鍒� MES 鐨勭墿鏂欙紝`syncedMes` 涓� false锛涘彲閫氳繃缂栬緫鐗╂枡瑙﹀彂鑷姩鍚屾
+- MES 鍚屾澶辫触浼氬洖婊� MDM 鎿嶄綔锛堜繚鎸佹暟鎹竴鑷存�э級锛屽墠绔渶鍏虫敞鎺ュ彛杩斿洖鐨勯敊璇俊鎭�
+- `syncedMes` 瀛楁鍦ㄨ鎯呴〉锛坓etItem锛変篃浼氳繑鍥烇紝鍙敤浜庤鎯呴〉鏄剧ず鍚屾鐘舵��
diff --git "a/docs/\351\231\204\344\273\266\344\270\212\344\274\240\346\250\241\345\235\227\346\261\207\346\200\273.md" "b/docs/\351\231\204\344\273\266\344\270\212\344\274\240\346\250\241\345\235\227\346\261\207\346\200\273.md"
new file mode 100644
index 0000000..1287927
--- /dev/null
+++ "b/docs/\351\231\204\344\273\266\344\270\212\344\274\240\346\250\241\345\235\227\346\261\207\346\200\273.md"
@@ -0,0 +1,131 @@
+# 闄勪欢涓婁紶妯″潡姹囨��
+
+## 涓�銆佹牳蹇冧笂浼犵粍浠�
+
+璺緞锛歚src/components/upload/`
+
+| 缁勪欢 | 鏂囦欢 | 鐢ㄩ�� |
+|---|---|---|
+| `FileUpload` | `file-upload.vue` | 閫氱敤鏂囦欢涓婁紶锛屾枃鏈垪琛ㄥ睍绀猴紝鏀寔鎷栨嫿銆佺被鍨�/澶у皬鏍¢獙 |
+| `ImageUpload` | `image-upload.vue` | 鍥剧墖涓婁紶锛屽崱鐗囬瑙堟ā寮忥紝鏀寔鍥剧墖棰勮寮圭獥 |
+| `InputUpload` | `input-upload.vue` | 杈撳叆妗�+涓婁紶缁勫悎锛屼笂浼犳寜閽湪杈撳叆妗嗗悗缂�鎴栨枃鏈煙鍙充笅瑙� |
+| `useUpload()` | `use-upload.ts` | 鏍稿績缁勫悎鍑芥暟锛岀粺涓�浣跨敤 system 妯″潡 Storage API锛圥OST `/system/storage-blob/upload`锛� |
+| `CropperAvatar` | `src/components/cropper/cropper-avatar.vue` | 澶村儚瑁佸壀涓婁紶 |
+
+### 涓婁紶妯″紡
+
+- **鏈嶅姟绔笂浼�**锛歅OST `/system/storage-blob/upload`锛岀粺涓�浣跨敤 system 妯″潡 Storage API
+- **闄勪欢缁戝畾**锛歅OST `/system/storage-attachment/bind`锛屽皢涓婁紶鐨勬枃浠跺叧鑱斿埌涓氬姟璁板綍
+
+### 琛ㄥ崟閫傞厤鍣ㄦ敞鍐�
+
+璺緞锛歚src/adapter/component/index.ts`
+
+娉ㄥ唽浜� `Upload`銆乣FileUpload`銆乣ImageUpload`銆乣PreviewUpload` 鍥涚琛ㄥ崟缁勪欢绫诲瀷锛屽彲鍦ㄨ〃鍗晄chema涓洿鎺ヤ娇鐢ㄣ��
+
+---
+
+## 浜屻�丗ileUpload 浣跨敤妯″潡锛堥檮浠朵笂浼狅級
+
+### ERP 妯″潡
+
+| 妯″潡 | 鏂囦欢 | 瀛楁 | 闄愬埗 |
+|---|---|---|---|
+| 閲囪喘璁㈠崟 | `src/views/erp/purchase/order/data.ts` | `fileUrl` | 鏃犻檺鍒� |
+| 閲囪喘鐢宠 | `src/views/erp/purchase/request/data.ts` | `fileUrl` | 鏃犻檺鍒� |
+| 閲囪喘閫�璐� | `src/views/erp/purchase/return/data.ts` | `fileUrl` | 鏃犻檺鍒� |
+| 閲囪喘鍏ュ簱 | `src/views/erp/purchase/in/data.ts` | `fileUrl` | 鏃犻檺鍒� |
+| 閿�鍞鍗� | `src/views/erp/sale/order/data.ts` | `fileUrl` | 鏃犻檺鍒� |
+| 閿�鍞��璐� | `src/views/erp/sale/return/data.ts` | `fileUrl` | 鏃犻檺鍒� |
+| 閿�鍞嚭搴� | `src/views/erp/sale/out/data.ts` | `fileUrl` | 鏃犻檺鍒� |
+| 搴撳瓨鍏ュ簱 | `src/views/erp/stock/in/data.ts` | `fileUrl` | 鏃犻檺鍒� |
+| 搴撳瓨鍑哄簱 | `src/views/erp/stock/out/data.ts` | `fileUrl` | 鏃犻檺鍒� |
+| 搴撳瓨璋冩嫧 | `src/views/erp/stock/move/data.ts` | `fileUrl` | 鏃犻檺鍒� |
+| 搴撳瓨鐩樼偣 | `src/views/erp/stock/check/data.ts` | `fileUrl` | 鏃犻檺鍒� |
+| 鏀舵鍗� | `src/views/erp/finance/receipt/data.ts` | `fileUrl` | 鏃犻檺鍒� |
+| 浠樻鍗� | `src/views/erp/finance/payment/data.ts` | `fileUrl` | 鏃犻檺鍒� |
+
+### CRM 妯″潡
+
+| 妯″潡 | 鏂囦欢 | 瀛楁 | 闄愬埗 |
+|---|---|---|---|
+| 璺熻繘璁板綍 | `src/views/crm/followup/data.ts` | `fileUrls` | 鈥� |
+
+### MES 妯″潡
+
+| 妯″潡 | 鏂囦欢 | 瀛楁 | 闄愬埗 |
+|---|---|---|---|
+| 椤圭洰鏂囨。 | `src/views/mes/pd/project/modules/document-form.vue` | `fileUrl` | 鏃犻檺鍒�, directory:mes/pd |
+
+---
+
+## 涓夈�両mageUpload 浣跨敤妯″潡锛堝浘鐗囦笂浼狅級
+
+| 妯″潡 | 鏂囦欢 | 瀛楁 | 鐢ㄩ�� |
+|---|---|---|---|
+| AI瑙掕壊绠$悊 | `src/views/ai/model/chatRole/data.ts` | `avatar` | 瑙掕壊澶村儚 |
+| CRM璺熻繘璁板綍 | `src/views/crm/followup/data.ts` | `picUrls` | 鍥剧墖 |
+| 鍩虹婕旂ず | `src/views/infra/demo/demo01/data.ts` | `avatar` | 澶村儚 |
+| OAuth2搴旂敤 | `src/views/system/oauth2/client/data.ts` | `logo` | 搴旂敤鍥炬爣 |
+| IM琛ㄦ儏鍖� | `src/views/im/manager/face/pack/data.ts` | `icon` | 灏侀潰 |
+| IM琛ㄦ儏鍖� | `src/views/im/manager/face/pack/data.ts` | `url` | 琛ㄦ儏鍥� |
+| IM棰戦亾绱犳潗 | `src/views/im/manager/channel/material/data.ts` | `coverUrl` | 灏侀潰鍥� |
+| IM棰戦亾鍒楄〃 | `src/views/im/manager/channel/list/data.ts` | `avatar` | 棰戦亾澶村儚 |
+| MES浜у搧濯掍綋 | `src/views/mes/md/item/modules/product-media-list.vue` | `url` | 浜у搧鍥剧墖 |
+| BPM娴佺▼琛ㄥ崟 | `src/views/bpm/model/form/modules/basic-info.vue` | 鍥炬爣瀛楁 | 琛ㄥ崟鍥炬爣 |
+
+---
+
+## 鍥涖�乁pload 浣跨敤妯″潡锛圗xcel瀵煎叆锛�
+
+| 妯″潡 | 鏂囦欢 | 鐢ㄩ�� | API |
+|---|---|---|---|
+| 绯荤粺鐢ㄦ埛 | `src/views/system/user/modules/import-form.vue` | Excel瀵煎叆鐢ㄦ埛 | `POST /system/user/import` |
+| CRM瀹㈡埛 | `src/views/crm/customer/modules/import-form.vue` | Excel瀵煎叆瀹㈡埛 | `POST /crm/customer/import` |
+| MES鐗╂枡 | `src/views/mes/md/item/modules/import-form.vue` | Excel瀵煎叆鐗╂枡 | `POST /mes/md/item/import` |
+| MES璁惧 | `src/views/mes/dv/machinery/modules/import-form.vue` | Excel瀵煎叆璁惧 | `POST /mes/dv/machinery/import` |
+| HRM鑰冨嫟 | `src/views/hrm/attendance/record/modules/import-form.vue` | Excel瀵煎叆鑰冨嫟 | `POST /hrm/attendance/record/import` |
+
+---
+
+## 浜斻�佸叾浠栦笂浼犲満鏅�
+
+| 妯″潡 | 鏂囦欢 | 鐢ㄩ�� | 瀹炵幇鏂瑰紡 |
+|---|---|---|---|
+| AI鐭ヨ瘑搴� | `src/views/ai/knowledge/document/form/modules/upload-step.vue` | 鏂囨。涓婁紶锛圱XT/PDF/DOC绛夛紝15MB锛� | `UploadDragger` |
+| AI鑱婂ぉ | `src/views/ai/chat/index/modules/message/file-upload.vue` | 鏂囦欢涓婁紶锛�5涓�/10MB锛� | 鍘熺敓input + `useUpload().httpRequest()` |
+| IM鑱婂ぉ | `src/views/im/home/composables/useMediaUploader.ts` | 鍥剧墖/鏂囦欢/璇煶/瑙嗛涓婁紶 | `uploadFile()` API |
+| IM娑堟伅杈撳叆 | `src/views/im/home/pages/conversation/components/input/message-input.vue` | 閫夋嫨鏂囦欢鍙戦�� | 鍘熺敓input + `useMediaUploader` |
+| IM琛ㄦ儏涓婁紶 | `src/views/im/home/pages/conversation/components/input/face-picker.vue` | 鑷畾涔夎〃鎯呬笂浼� | `uploadFile()` API |
+| BPM绛惧悕 | `src/views/bpm/processInstance/detail/modules/signature.vue` | 鎵嬪啓绛惧悕涓婁紶 | `uploadFile()` API锛坆ase64杞枃浠讹級 |
+| 鐢ㄦ埛澶村儚 | `src/views/_core/profile/modules/profile-user.vue` | 澶村儚瑁佸壀涓婁紶 | `CropperAvatar` + `useUpload().httpRequest()` |
+| TinyMCE | `src/components/tinymce/img-upload.vue` | 瀵屾枃鏈浘鐗囦笂浼� | `Upload`缁勪欢 |
+| Tiptap | `src/packages/effects/plugins/src/tiptap/tiptap.vue` | 瀵屾枃鏈浘鐗囦笂浼� | Tiptap鎵╁睍 |
+| MES渚涘簲鍟� | `src/api/mes/md/vendor/index.ts` | Excel瀵煎叆渚涘簲鍟� | `POST /mes/md-vendor/import` |
+| MES瀹㈡埛 | `src/api/mes/md/client/index.ts` | Excel瀵煎叆MES瀹㈡埛 | `POST /mes/md-client/import` |
+| MES椤圭洰 | `src/api/mes/pd/project/index.ts` | 椤圭洰鏂囨。涓婁紶 | `PUT /mes/pd/project/upload` |
+
+---
+
+## 鍏�丠RM 闄勪欢涓婁紶锛堝凡淇锛�
+
+HRM 妯″潡鐨�"闄勪欢"瀛楁宸蹭粠 `Input` 鏀逛负 `FileUpload` 缁勪欢銆�
+
+| 妯″潡 | 鏂囦欢 | 瀛楁 | 缁勪欢 | 鐘舵�� |
+|---|---|---|---|---|
+| 璇峰亣 | `src/views/hrm/leave/data.ts` | `fileUrl` | `FileUpload` | 宸蹭慨澶� |
+| 绂昏亴 | `src/views/hrm/resignation/data.ts` | `fileUrl` | `FileUpload` | 宸蹭慨澶� |
+| 璋冨姩 | `src/views/hrm/transfer/data.ts` | `fileUrl` | `FileUpload` | 宸蹭慨澶� |
+
+---
+
+## 涓冦�佷笂浼犵浉鍏矨PI
+
+| 鍑芥暟 | 绔偣 | 鐢ㄩ�� |
+|---|---|---|
+| `uploadFile()` | `POST /system/storage-blob/upload` | 缁熶竴鏂囦欢涓婁紶 |
+| `bindAttachments()` | `POST /system/storage-attachment/bind` | 缁戝畾闄勪欢鍒颁笟鍔¤褰� |
+| `listAttachments()` | `GET /system/storage-attachment/list` | 鏌ヨ闄勪欢鍒楄〃 |
+| `deleteAttachments()` | `DELETE /system/storage-attachment/delete` | 鎵归噺鍒犻櫎闄勪欢 |
+
+API瀹氫箟鏂囦欢锛歚src/api/system/storage/index.ts`
diff --git a/sql/mysql/hrm_module_init.sql b/sql/mysql/hrm_module_init.sql
index 2510c7f..57c4d35 100644
--- a/sql/mysql/hrm_module_init.sql
+++ b/sql/mysql/hrm_module_init.sql
@@ -122,7 +122,6 @@
`status` tinyint NOT NULL DEFAULT 0 COMMENT '鐘舵�侊細0-鑽夌锛�10-瀹℃壒涓紝20-閫氳繃锛�30-椹冲洖锛�40-鎾ら攢',
`process_instance_id` varchar(64) DEFAULT NULL COMMENT 'BPM娴佺▼瀹炰緥ID',
`approve_time` datetime DEFAULT NULL COMMENT '瀹℃壒瀹屾垚鏃堕棿',
- `file_url` varchar(500) DEFAULT NULL COMMENT '闄勪欢鍦板潃',
`remark` varchar(500) DEFAULT NULL COMMENT '澶囨敞',
`creator` varchar(64) DEFAULT '' COMMENT '鍒涘缓鑰�',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
@@ -150,7 +149,6 @@
`status` tinyint NOT NULL DEFAULT 0 COMMENT '鐘舵�侊細0-鑽夌锛�10-瀹℃壒涓紝20-閫氳繃锛�30-椹冲洖锛�40-鎾ら攢',
`process_instance_id` varchar(64) DEFAULT NULL COMMENT 'BPM娴佺▼瀹炰緥ID',
`approve_time` datetime DEFAULT NULL COMMENT '瀹℃壒瀹屾垚鏃堕棿',
- `file_url` varchar(500) DEFAULT NULL COMMENT '闄勪欢鍦板潃',
`remark` varchar(500) DEFAULT NULL COMMENT '澶囨敞',
`creator` varchar(64) DEFAULT '' COMMENT '鍒涘缓鑰�',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
@@ -178,7 +176,6 @@
`status` tinyint NOT NULL DEFAULT 0 COMMENT '鐘舵�侊細0-鑽夌锛�10-瀹℃壒涓紝20-閫氳繃锛�30-椹冲洖锛�40-鎾ら攢',
`process_instance_id` varchar(64) DEFAULT NULL COMMENT 'BPM娴佺▼瀹炰緥ID',
`approve_time` datetime DEFAULT NULL COMMENT '瀹℃壒瀹屾垚鏃堕棿',
- `file_url` varchar(500) DEFAULT NULL COMMENT '闄勪欢鍦板潃',
`remark` varchar(500) DEFAULT NULL COMMENT '澶囨敞',
`creator` varchar(64) DEFAULT '' COMMENT '鍒涘缓鑰�',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
@@ -255,7 +252,6 @@
`status` tinyint NOT NULL DEFAULT 0 COMMENT '鐘舵�侊細0-鑽夌锛�10-瀹℃壒涓紝20-閫氳繃锛�30-椹冲洖锛�40-鎾ら攢',
`process_instance_id` varchar(64) DEFAULT NULL COMMENT 'BPM娴佺▼瀹炰緥ID',
`approve_time` datetime DEFAULT NULL COMMENT '瀹℃壒瀹屾垚鏃堕棿',
- `file_url` varchar(500) DEFAULT NULL COMMENT '闄勪欢鍦板潃',
`remark` varchar(500) DEFAULT NULL COMMENT '澶囨敞',
`creator` varchar(64) DEFAULT '' COMMENT '鍒涘缓鑰�',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
@@ -359,7 +355,6 @@
`payment_method` tinyint NOT NULL COMMENT '鍙戞斁鏂瑰紡锛�1-閾惰杞处锛�2-鐜伴噾',
`payment_time` datetime NOT NULL COMMENT '鍙戞斁鏃堕棿',
`status` tinyint NOT NULL DEFAULT 0 COMMENT '鐘舵�侊細0-寰呭彂鏀撅紝10-宸插彂鏀�',
- `file_url` varchar(500) DEFAULT NULL COMMENT '閾惰鍥炲崟闄勪欢',
`remark` varchar(500) DEFAULT NULL COMMENT '澶囨敞',
`creator` varchar(64) DEFAULT '' COMMENT '鍒涘缓鑰�',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/CrmFollowUpRecordController.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/CrmFollowUpRecordController.java
index 6739ee2..f09026a 100644
--- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/CrmFollowUpRecordController.java
+++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/CrmFollowUpRecordController.java
@@ -17,6 +17,7 @@
import cn.iocoder.yudao.module.crm.service.contact.CrmContactService;
import cn.iocoder.yudao.module.crm.service.followup.CrmFollowUpRecordService;
import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
+import cn.iocoder.yudao.module.system.api.storage.StorageAttachmentApi;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import io.swagger.v3.oas.annotations.Operation;
@@ -58,6 +59,8 @@
@Resource
private AdminUserApi adminUserApi;
+ @Resource
+ private StorageAttachmentApi storageAttachmentApi;
@PostMapping("/create")
@Operation(summary = "鍒涘缓璺熻繘璁板綍")
@@ -85,7 +88,9 @@
getLoginUserId(), CrmPermissionLevelEnum.READ)) {
throw exception(CRM_PERMISSION_DENIED, CrmBizTypeEnum.getNameByType(followUpRecord.getBizType()));
}
- return success(BeanUtils.toBean(followUpRecord, CrmFollowUpRecordRespVO.class));
+ CrmFollowUpRecordRespVO respVO = BeanUtils.toBean(followUpRecord, CrmFollowUpRecordRespVO.class);
+ respVO.setAttachmentList(storageAttachmentApi.listAttachments("crm_follow_up_record", id));
+ return success(respVO);
}
@GetMapping("/page")
diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/vo/CrmFollowUpRecordRespVO.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/vo/CrmFollowUpRecordRespVO.java
index 075c3b3..9306ce5 100644
--- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/vo/CrmFollowUpRecordRespVO.java
+++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/vo/CrmFollowUpRecordRespVO.java
@@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.crm.controller.admin.followup.vo;
+import cn.iocoder.yudao.module.system.api.storage.dto.StorageBlobRespDTO;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessRespVO;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
@@ -48,8 +49,8 @@
@Schema(description = "鍥剧墖")
private List<String> picUrls;
- @Schema(description = "闄勪欢")
- private List<String> fileUrls;
+ @Schema(description = "闄勪欢鍒楄〃")
+ private List<StorageBlobRespDTO> attachmentList;
@Schema(description = "鍒涘缓浜�", example = "1024")
@ExcelProperty("鍒涘缓浜�")
diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/vo/CrmFollowUpRecordSaveReqVO.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/vo/CrmFollowUpRecordSaveReqVO.java
index ce14897..4550df6 100644
--- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/vo/CrmFollowUpRecordSaveReqVO.java
+++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/vo/CrmFollowUpRecordSaveReqVO.java
@@ -35,6 +35,9 @@
@NotNull(message = "涓嬫鑱旂郴鏃堕棿涓嶈兘涓虹┖")
private LocalDateTime nextTime;
+ @Schema(description = "闄勪欢blobId鍒楄〃", example = "[1, 2, 3]")
+ private List<Long> blobIds;
+
@Schema(description = "鍏宠仈鐨勫晢鏈虹紪鍙锋暟缁�")
private List<Long> businessIds;
@Schema(description = "鍏宠仈鐨勮仈绯讳汉缂栧彿鏁扮粍")
@@ -42,7 +45,4 @@
@Schema(description = "鍥剧墖 URL 鍒楄〃")
private List<String> picUrls;
- @Schema(description = "闄勪欢 URL 鍒楄〃")
- private List<String> fileUrls;
-
}
\ No newline at end of file
diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/quotation/CrmSaleQuotationController.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/quotation/CrmSaleQuotationController.java
index 2bad9a7..542f3e0 100644
--- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/quotation/CrmSaleQuotationController.java
+++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/quotation/CrmSaleQuotationController.java
@@ -22,6 +22,7 @@
import cn.iocoder.yudao.module.mdm.api.unit.dto.MdmUnitMeasureRespDTO;
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
+import cn.iocoder.yudao.module.system.api.storage.StorageAttachmentApi;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import io.swagger.v3.oas.annotations.Operation;
@@ -65,6 +66,8 @@
private MdmUnitMeasureApi mdmUnitMeasureApi;
@Resource
private CrmCustomerService customerService;
+ @Resource
+ private StorageAttachmentApi storageAttachmentApi;
@PostMapping("/create")
@Operation(summary = "鍒涘缓閿�鍞姤浠峰崟")
@@ -177,6 +180,8 @@
}
// 3. 濉厖鍏宠仈淇℃伅
fillSaleQuotationRelatedFields(respVO);
+ // 4. 濉厖闄勪欢鍒楄〃
+ respVO.setAttachmentList(storageAttachmentApi.listAttachments("crm_sale_quotation", saleQuotation.getId()));
return respVO;
}
diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/quotation/vo/CrmSaleQuotationRespVO.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/quotation/vo/CrmSaleQuotationRespVO.java
index d9d037f..ad95d47 100644
--- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/quotation/vo/CrmSaleQuotationRespVO.java
+++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/quotation/vo/CrmSaleQuotationRespVO.java
@@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.crm.controller.admin.quotation.vo;
+import cn.iocoder.yudao.module.system.api.storage.dto.StorageBlobRespDTO;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
@@ -103,8 +104,8 @@
@ExcelProperty("鍏宠仈鍚堝悓鍗曞彿")
private String contractNo;
- @Schema(description = "闄勪欢鍦板潃", example = "https://www.baidu.com")
- private String fileUrl;
+ @Schema(description = "闄勪欢鍒楄〃")
+ private List<StorageBlobRespDTO> attachmentList;
@Schema(description = "澶囨敞", example = "澶囨敞淇℃伅")
@ExcelProperty("澶囨敞")
diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/quotation/vo/CrmSaleQuotationSaveReqVO.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/quotation/vo/CrmSaleQuotationSaveReqVO.java
index a235d19..50323c1 100644
--- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/quotation/vo/CrmSaleQuotationSaveReqVO.java
+++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/quotation/vo/CrmSaleQuotationSaveReqVO.java
@@ -53,8 +53,8 @@
@Schema(description = "绋庣巼", example = "13.00")
private BigDecimal taxRate;
- @Schema(description = "闄勪欢鍦板潃", example = "https://www.baidu.com")
- private String fileUrl;
+ @Schema(description = "闄勪欢blobId鍒楄〃", example = "[1, 2, 3]")
+ private List<Long> blobIds;
@Schema(description = "澶囨敞", example = "澶囨敞淇℃伅")
private String remark;
diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/followup/CrmFollowUpRecordDO.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/followup/CrmFollowUpRecordDO.java
index a01d47a..1202116 100644
--- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/followup/CrmFollowUpRecordDO.java
+++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/followup/CrmFollowUpRecordDO.java
@@ -73,12 +73,6 @@
@TableField(typeHandler = StringListTypeHandler.class)
private List<String> picUrls;
/**
- * 闄勪欢
- */
- @TableField(typeHandler = StringListTypeHandler.class)
- private List<String> fileUrls;
-
- /**
* 鍏宠仈鐨勫晢鏈虹紪鍙锋暟缁�
*
* 鍏宠仈 {@link CrmBusinessDO#getId()}
diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/quotation/CrmSaleQuotationDO.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/quotation/CrmSaleQuotationDO.java
index 01329f2..0cb8e82 100644
--- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/quotation/CrmSaleQuotationDO.java
+++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/quotation/CrmSaleQuotationDO.java
@@ -125,10 +125,6 @@
*/
private String contractNo;
/**
- * 闄勪欢鍦板潃
- */
- private String fileUrl;
- /**
* 澶囨敞
*/
private String remark;
diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/followup/CrmFollowUpRecordServiceImpl.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/followup/CrmFollowUpRecordServiceImpl.java
index 31c62d3..69d88ca 100644
--- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/followup/CrmFollowUpRecordServiceImpl.java
+++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/followup/CrmFollowUpRecordServiceImpl.java
@@ -18,6 +18,7 @@
import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
import cn.iocoder.yudao.module.crm.service.followup.bo.CrmFollowUpCreateReqBO;
import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
+import cn.iocoder.yudao.module.system.api.storage.StorageAttachmentApi;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
@@ -60,6 +61,8 @@
@Resource
@Lazy
private CrmCustomerService customerService;
+ @Resource
+ private StorageAttachmentApi storageAttachmentApi;
@Override
@CrmPermission(bizTypeValue = "#createReqVO.bizType", bizId = "#createReqVO.bizId", level = CrmPermissionLevelEnum.WRITE)
@@ -67,6 +70,8 @@
// 1. 鍒涘缓鏇磋繘璁板綍
CrmFollowUpRecordDO record = BeanUtils.toBean(createReqVO, CrmFollowUpRecordDO.class);
crmFollowUpRecordMapper.insert(record);
+ storageAttachmentApi.bindAttachments("file", "crm_follow_up_record",
+ record.getId(), createReqVO.getBlobIds());
// 2. 鏇存柊 bizId 瀵瑰簲鐨勮褰�
if (ObjUtil.equal(CrmBizTypeEnum.CRM_CUSTOMER.getType(), record.getBizType())) { // 鏇存柊瀹㈡埛璺熻繘淇℃伅
@@ -113,12 +118,18 @@
throw exception(FOLLOW_UP_RECORD_DELETE_DENIED);
}
+ // 鍒犻櫎闄勪欢
+ storageAttachmentApi.deleteAttachmentsByRecord("crm_follow_up_record", id);
// 鍒犻櫎
crmFollowUpRecordMapper.deleteById(id);
}
@Override
public void deleteFollowUpRecordByBiz(Integer bizType, Long bizId) {
+ List<CrmFollowUpRecordDO> records = crmFollowUpRecordMapper.selectListByBiz(bizType, List.of(bizId));
+ for (CrmFollowUpRecordDO record : records) {
+ storageAttachmentApi.deleteAttachmentsByRecord("crm_follow_up_record", record.getId());
+ }
crmFollowUpRecordMapper.deleteByBiz(bizType, bizId);
}
diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/followup/bo/CrmFollowUpCreateReqBO.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/followup/bo/CrmFollowUpCreateReqBO.java
index dec219e..e6590bb 100644
--- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/followup/bo/CrmFollowUpCreateReqBO.java
+++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/followup/bo/CrmFollowUpCreateReqBO.java
@@ -57,11 +57,6 @@
*/
private List<String> picUrls;
/**
- * 闄勪欢
- */
- private List<String> fileUrls;
-
- /**
* 鍏宠仈鐨勫晢鏈虹紪鍙锋暟缁�
*
* 鍏宠仈 {@link CrmBusinessDO#getId()}
diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/quotation/CrmSaleQuotationServiceImpl.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/quotation/CrmSaleQuotationServiceImpl.java
index 99dda59..c7bab33 100644
--- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/quotation/CrmSaleQuotationServiceImpl.java
+++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/quotation/CrmSaleQuotationServiceImpl.java
@@ -23,6 +23,7 @@
import cn.iocoder.yudao.module.crm.api.customer.CrmCustomerApi;
import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
+import cn.iocoder.yudao.module.system.api.storage.StorageAttachmentApi;
import cn.iocoder.yudao.module.mdm.api.item.MdmItemApi;
import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO;
import jakarta.annotation.Resource;
@@ -70,6 +71,8 @@
@Resource
private CrmPermissionService permissionService;
@Resource
+ private StorageAttachmentApi storageAttachmentApi;
+ @Resource
private CrmContractService contractService;
@Override
@@ -100,6 +103,8 @@
.setBizType(CrmBizTypeEnum.CRM_SALE_QUOTATION.getType())
.setBizId(saleQuotation.getId())
.setLevel(CrmPermissionLevelEnum.OWNER.getLevel()));
+ storageAttachmentApi.bindAttachments("file", "crm_sale_quotation",
+ saleQuotation.getId(), createReqVO.getBlobIds());
return saleQuotation.getId();
}
@@ -123,6 +128,11 @@
saleQuotationMapper.updateById(updateObj);
// 2.2 鏇存柊鎶ヤ环鏄庣粏
updateQuotationProductList(updateReqVO.getId(), quotationProducts);
+ if (updateReqVO.getBlobIds() != null) {
+ storageAttachmentApi.deleteAttachmentsByRecord("crm_sale_quotation", updateReqVO.getId());
+ storageAttachmentApi.bindAttachments("file", "crm_sale_quotation",
+ updateReqVO.getId(), updateReqVO.getBlobIds());
+ }
}
@Override
@@ -141,6 +151,8 @@
// 2. 閬嶅巻鍒犻櫎
saleQuotations.forEach(saleQuotation -> {
+ // 2.0 鍒犻櫎闄勪欢鍏宠仈
+ storageAttachmentApi.deleteAttachmentsByRecord("crm_sale_quotation", saleQuotation.getId());
// 2.1 鍒犻櫎鎶ヤ环鍗�
saleQuotationMapper.deleteById(saleQuotation.getId());
// 2.2 鍒犻櫎鎶ヤ环鏄庣粏
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/order/ErpPurchaseOrderRespVO.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/order/ErpPurchaseOrderRespVO.java
index dae576f..7692ec1 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/order/ErpPurchaseOrderRespVO.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/order/ErpPurchaseOrderRespVO.java
@@ -63,10 +63,6 @@
@Schema(description = "瀹氶噾閲戦锛屽崟浣嶏細鍏�", requiredMode = Schema.RequiredMode.REQUIRED, example = "7127")
private BigDecimal depositPrice;
- @Schema(description = "闄勪欢鍦板潃", example = "https://www.iocoder.cn")
- @ExcelProperty("闄勪欢鍦板潃")
- private String fileUrl;
-
@Schema(description = "澶囨敞", example = "浣犵寽")
@ExcelProperty("澶囨敞")
private String remark;
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/order/ErpPurchaseOrderSaveReqVO.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/order/ErpPurchaseOrderSaveReqVO.java
index d259001..81a46ee 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/order/ErpPurchaseOrderSaveReqVO.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/order/ErpPurchaseOrderSaveReqVO.java
@@ -32,9 +32,6 @@
@Schema(description = "瀹氶噾閲戦锛屽崟浣嶏細鍏�", example = "7127")
private BigDecimal depositPrice;
- @Schema(description = "闄勪欢鍦板潃", example = "https://www.iocoder.cn")
- private String fileUrl;
-
@Schema(description = "澶囨敞", example = "浣犵寽")
private String remark;
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/request/ErpPurchaseRequestRespVO.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/request/ErpPurchaseRequestRespVO.java
index 40fcab4..a0dd200 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/request/ErpPurchaseRequestRespVO.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/request/ErpPurchaseRequestRespVO.java
@@ -64,9 +64,6 @@
@Schema(description = "鍏ュ簱鐘舵�侊紙0:鏈叆搴� 1:閮ㄥ垎鍏ュ簱 2:鍏ㄩ儴鍏ュ簱锛�", example = "0")
private Integer inStatus;
- @Schema(description = "闄勪欢鍦板潃", example = "https://xxx.com/xxx.pdf")
- private String fileUrl;
-
@Schema(description = "澶囨敞", example = "澶囨敞")
private String remark;
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/request/ErpPurchaseRequestSaveReqVO.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/request/ErpPurchaseRequestSaveReqVO.java
index 97b9f8b..a405917 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/request/ErpPurchaseRequestSaveReqVO.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/vo/request/ErpPurchaseRequestSaveReqVO.java
@@ -33,9 +33,6 @@
@Schema(description = "瀹氶噾閲戦", example = "1000.00")
private BigDecimal depositPrice;
- @Schema(description = "闄勪欢鍦板潃", example = "https://xxx.com/xxx.pdf")
- private String fileUrl;
-
@Schema(description = "澶囨敞", example = "澶囨敞")
private String remark;
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderRespVO.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderRespVO.java
index f545a7b..4c7add4 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderRespVO.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderRespVO.java
@@ -72,10 +72,6 @@
@Schema(description = "瀹氶噾閲戦锛屽崟浣嶏細鍏�", requiredMode = Schema.RequiredMode.REQUIRED, example = "7127")
private BigDecimal depositPrice;
- @Schema(description = "闄勪欢鍦板潃", example = "https://www.iocoder.cn")
- @ExcelProperty("闄勪欢鍦板潃")
- private String fileUrl;
-
@Schema(description = "澶囨敞", example = "浣犵寽")
@ExcelProperty("澶囨敞")
private String remark;
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderSaveReqVO.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderSaveReqVO.java
index dcf7c68..841f14a 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderSaveReqVO.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderSaveReqVO.java
@@ -41,9 +41,6 @@
@Schema(description = "瀹氶噾閲戦锛屽崟浣嶏細鍏�", example = "7127")
private BigDecimal depositPrice;
- @Schema(description = "闄勪欢鍦板潃", example = "https://www.iocoder.cn")
- private String fileUrl;
-
@Schema(description = "澶囨敞", example = "浣犵寽")
private String remark;
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseOrderDO.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseOrderDO.java
index 88a67a8..48266e3 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseOrderDO.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseOrderDO.java
@@ -92,10 +92,6 @@
private BigDecimal depositPrice;
/**
- * 闄勪欢鍦板潃
- */
- private String fileUrl;
- /**
* 澶囨敞
*/
private String remark;
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseRequestDO.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseRequestDO.java
index ec47b9e..de4cde0 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseRequestDO.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseRequestDO.java
@@ -105,11 +105,6 @@
private BigDecimal depositPrice;
/**
- * 闄勪欢鍦板潃
- */
- private String fileUrl;
-
- /**
* 澶囨敞
*/
private String remark;
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/sale/ErpSaleOrderDO.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/sale/ErpSaleOrderDO.java
index be719e0..12e8daa 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/sale/ErpSaleOrderDO.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/sale/ErpSaleOrderDO.java
@@ -106,10 +106,6 @@
private BigDecimal depositPrice;
/**
- * 闄勪欢鍦板潃
- */
- private String fileUrl;
- /**
* 澶囨敞
*/
private String remark;
diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/approval/vo/HrmLeaveApplicationRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/approval/vo/HrmLeaveApplicationRespVO.java
index c69563e..23fd6dc 100644
--- a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/approval/vo/HrmLeaveApplicationRespVO.java
+++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/approval/vo/HrmLeaveApplicationRespVO.java
@@ -3,8 +3,10 @@
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
+import cn.iocoder.yudao.module.system.api.storage.dto.StorageBlobRespDTO;
import java.math.BigDecimal;
import java.time.LocalDateTime;
+import java.util.List;
@Schema(description = "绠$悊鍚庡彴 - 璇峰亣鐢宠 Response VO")
@Data
@@ -58,8 +60,8 @@
@Schema(description = "瀹℃壒瀹屾垚鏃堕棿", example = "2024-01-01 10:00:00")
private LocalDateTime approveTime;
- @Schema(description = "闄勪欢鍦板潃", example = "https://xxx.com/xxx.pdf")
- private String fileUrl;
+ @Schema(description = "闄勪欢鍒楄〃")
+ private List<StorageBlobRespDTO> attachmentList;
@Schema(description = "澶囨敞", example = "澶囨敞淇℃伅")
private String remark;
diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/approval/vo/HrmLeaveApplicationSaveReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/approval/vo/HrmLeaveApplicationSaveReqVO.java
index ffc32a1..4409bb0 100644
--- a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/approval/vo/HrmLeaveApplicationSaveReqVO.java
+++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/approval/vo/HrmLeaveApplicationSaveReqVO.java
@@ -7,6 +7,7 @@
import java.math.BigDecimal;
import java.time.LocalDateTime;
+import java.util.List;
@Schema(description = "绠$悊鍚庡彴 - 璇峰亣鐢宠鏂板/淇敼 Request VO")
@Data
@@ -41,8 +42,8 @@
@NotEmpty(message = "璇峰亣鍘熷洜涓嶈兘涓虹┖")
private String reason;
- @Schema(description = "闄勪欢鍦板潃", example = "https://xxx.com/xxx.pdf")
- private String fileUrl;
+ @Schema(description = "闄勪欢blobId鍒楄〃", example = "[1, 2, 3]")
+ private List<Long> blobIds;
@Schema(description = "澶囨敞", example = "澶囨敞淇℃伅")
private String remark;
diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/approval/vo/HrmResignationApplicationRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/approval/vo/HrmResignationApplicationRespVO.java
index 8ee359e..3c7aaa6 100644
--- a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/approval/vo/HrmResignationApplicationRespVO.java
+++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/approval/vo/HrmResignationApplicationRespVO.java
@@ -3,8 +3,10 @@
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
+import cn.iocoder.yudao.module.system.api.storage.dto.StorageBlobRespDTO;
import java.time.LocalDate;
import java.time.LocalDateTime;
+import java.util.List;
@Schema(description = "绠$悊鍚庡彴 - 绂昏亴鐢宠 Response VO")
@Data
@@ -70,8 +72,8 @@
@Schema(description = "瀹℃壒瀹屾垚鏃堕棿", example = "2024-01-01 10:00:00")
private LocalDateTime approveTime;
- @Schema(description = "闄勪欢鍦板潃", example = "https://xxx.com/xxx.pdf")
- private String fileUrl;
+ @Schema(description = "闄勪欢鍒楄〃")
+ private List<StorageBlobRespDTO> attachmentList;
@Schema(description = "澶囨敞", example = "澶囨敞淇℃伅")
private String remark;
diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/approval/vo/HrmResignationApplicationSaveReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/approval/vo/HrmResignationApplicationSaveReqVO.java
index acdafab..fb19057 100644
--- a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/approval/vo/HrmResignationApplicationSaveReqVO.java
+++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/approval/vo/HrmResignationApplicationSaveReqVO.java
@@ -6,6 +6,7 @@
import lombok.Data;
import java.time.LocalDate;
+import java.util.List;
@Schema(description = "绠$悊鍚庡彴 - 绂昏亴鐢宠鏂板/淇敼 Request VO")
@Data
@@ -38,8 +39,8 @@
@Schema(description = "浜ゆ帴璇存槑", example = "浜ゆ帴璇存槑")
private String handoverRemark;
- @Schema(description = "闄勪欢鍦板潃", example = "https://xxx.com/xxx.pdf")
- private String fileUrl;
+ @Schema(description = "闄勪欢blobId鍒楄〃", example = "[1, 2, 3]")
+ private List<Long> blobIds;
@Schema(description = "澶囨敞", example = "澶囨敞淇℃伅")
private String remark;
diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/approval/vo/HrmTransferApplicationRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/approval/vo/HrmTransferApplicationRespVO.java
index 1930b30..bb3e292 100644
--- a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/approval/vo/HrmTransferApplicationRespVO.java
+++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/approval/vo/HrmTransferApplicationRespVO.java
@@ -3,8 +3,10 @@
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
+import cn.iocoder.yudao.module.system.api.storage.dto.StorageBlobRespDTO;
import java.time.LocalDate;
import java.time.LocalDateTime;
+import java.util.List;
@Schema(description = "绠$悊鍚庡彴 - 璋冨矖鐢宠 Response VO")
@Data
@@ -70,8 +72,8 @@
@Schema(description = "瀹℃壒瀹屾垚鏃堕棿", example = "2024-01-01 10:00:00")
private LocalDateTime approveTime;
- @Schema(description = "闄勪欢鍦板潃", example = "https://xxx.com/xxx.pdf")
- private String fileUrl;
+ @Schema(description = "闄勪欢鍒楄〃")
+ private List<StorageBlobRespDTO> attachmentList;
@Schema(description = "澶囨敞", example = "澶囨敞淇℃伅")
private String remark;
diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/approval/vo/HrmTransferApplicationSaveReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/approval/vo/HrmTransferApplicationSaveReqVO.java
index 5c89ef3..293a5ce 100644
--- a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/approval/vo/HrmTransferApplicationSaveReqVO.java
+++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/approval/vo/HrmTransferApplicationSaveReqVO.java
@@ -6,6 +6,7 @@
import lombok.Data;
import java.time.LocalDate;
+import java.util.List;
@Schema(description = "绠$悊鍚庡彴 - 璋冨矖鐢宠鏂板/淇敼 Request VO")
@Data
@@ -43,8 +44,8 @@
@NotEmpty(message = "璋冨矖鍘熷洜涓嶈兘涓虹┖")
private String reason;
- @Schema(description = "闄勪欢鍦板潃", example = "https://xxx.com/xxx.pdf")
- private String fileUrl;
+ @Schema(description = "闄勪欢blobId鍒楄〃", example = "[1, 2, 3]")
+ private List<Long> blobIds;
@Schema(description = "澶囨敞", example = "澶囨敞淇℃伅")
private String remark;
diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/HrmAttendanceExceptionController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/HrmAttendanceExceptionController.java
index 33c9f4a..3ca789b 100644
--- a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/HrmAttendanceExceptionController.java
+++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/HrmAttendanceExceptionController.java
@@ -13,6 +13,7 @@
import cn.iocoder.yudao.module.hrm.service.employee.HrmEmployeeService;
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
+import cn.iocoder.yudao.module.system.api.storage.StorageAttachmentApi;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
@@ -39,6 +40,8 @@
private HrmEmployeeService employeeService;
@Resource
private DeptApi deptApi;
+ @Resource
+ private StorageAttachmentApi storageAttachmentApi;
@PostMapping("/create")
@Operation(summary = "鏂板鑰冨嫟寮傚父鐢宠")
@@ -87,6 +90,7 @@
respVO.setDeptName(dept.getName());
}
}
+ respVO.setAttachmentList(storageAttachmentApi.listAttachments("hrm_attendance_exception", id));
return success(respVO);
}
diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/HrmAttendanceExceptionRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/HrmAttendanceExceptionRespVO.java
index 61f903d..6d41a13 100644
--- a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/HrmAttendanceExceptionRespVO.java
+++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/HrmAttendanceExceptionRespVO.java
@@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo;
+import cn.iocoder.yudao.module.system.api.storage.dto.StorageBlobRespDTO;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
@@ -9,6 +10,7 @@
import java.time.LocalDate;
import java.time.LocalDateTime;
+import java.util.List;
@Schema(description = "绠$悊鍚庡彴 - 鑰冨嫟寮傚父鐢宠 Response VO")
@Data
@@ -66,8 +68,8 @@
@ExcelProperty("瀹℃壒瀹屾垚鏃堕棿")
private LocalDateTime approveTime;
- @Schema(description = "闄勪欢鍦板潃")
- private String fileUrl;
+ @Schema(description = "闄勪欢鍒楄〃")
+ private List<StorageBlobRespDTO> attachmentList;
@Schema(description = "澶囨敞")
@ExcelProperty("澶囨敞")
diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/HrmAttendanceExceptionSaveReqVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/HrmAttendanceExceptionSaveReqVO.java
index 7bc5dda..64dd7d0 100644
--- a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/HrmAttendanceExceptionSaveReqVO.java
+++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/attendance/vo/HrmAttendanceExceptionSaveReqVO.java
@@ -7,6 +7,7 @@
import java.time.LocalDate;
import java.time.LocalDateTime;
+import java.util.List;
@Schema(description = "绠$悊鍚庡彴 - 鑰冨嫟寮傚父鐢宠鏂板/淇敼 Request VO")
@Data
@@ -30,8 +31,8 @@
@NotEmpty(message = "鐢宠鍘熷洜涓嶈兘涓虹┖")
private String reason;
- @Schema(description = "闄勪欢鍦板潃", example = "https://xxx.com/xxx.jpg")
- private String fileUrl;
+ @Schema(description = "闄勪欢blobId鍒楄〃", example = "[1, 2, 3]")
+ private List<Long> blobIds;
@Schema(description = "澶囨敞", example = "澶囨敞淇℃伅")
private String remark;
diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/HrmSalaryPaymentController.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/HrmSalaryPaymentController.java
index 817f4f0..ebb1fdd 100644
--- a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/HrmSalaryPaymentController.java
+++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/HrmSalaryPaymentController.java
@@ -15,6 +15,7 @@
import cn.iocoder.yudao.module.hrm.service.salary.HrmSalaryPaymentService;
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
+import cn.iocoder.yudao.module.system.api.storage.StorageAttachmentApi;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
@@ -41,6 +42,8 @@
private HrmEmployeeService employeeService;
@Resource
private DeptApi deptApi;
+ @Resource
+ private StorageAttachmentApi storageAttachmentApi;
@GetMapping("/page")
@Operation(summary = "鑾峰緱钖祫鍙戞斁鍒嗛〉")
@@ -56,7 +59,9 @@
@PreAuthorize("@ss.hasPermission('hrm:salary:query')")
public CommonResult<HrmSalaryPaymentRespVO> getSalaryPayment(@RequestParam("id") Long id) {
HrmSalaryPaymentDO salaryPayment = salaryPaymentService.getSalaryPayment(id);
- return success(BeanUtils.toBean(salaryPayment, HrmSalaryPaymentRespVO.class));
+ HrmSalaryPaymentRespVO respVO = BeanUtils.toBean(salaryPayment, HrmSalaryPaymentRespVO.class);
+ respVO.setAttachmentList(storageAttachmentApi.listAttachments("hrm_salary_payment", id));
+ return success(respVO);
}
@PostMapping("/generate")
diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/HrmSalaryPaymentRespVO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/HrmSalaryPaymentRespVO.java
index 4fc9536..bb8bc14 100644
--- a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/HrmSalaryPaymentRespVO.java
+++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/controller/admin/salary/vo/HrmSalaryPaymentRespVO.java
@@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.hrm.controller.admin.salary.vo;
+import cn.iocoder.yudao.module.system.api.storage.dto.StorageBlobRespDTO;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
@@ -9,6 +10,7 @@
import java.math.BigDecimal;
import java.time.LocalDateTime;
+import java.util.List;
@Schema(description = "绠$悊鍚庡彴 - 钖祫鍙戞斁 Response VO")
@Data
@@ -49,8 +51,8 @@
@DictFormat("hrm_payment_status")
private Integer status;
- @Schema(description = "閾惰鍥炲崟闄勪欢")
- private String fileUrl;
+ @Schema(description = "闄勪欢鍒楄〃")
+ private List<StorageBlobRespDTO> attachmentList;
@Schema(description = "澶囨敞")
@ExcelProperty("澶囨敞")
diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/approval/HrmLeaveApplicationDO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/approval/HrmLeaveApplicationDO.java
index fb6d477..ceb56b0 100644
--- a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/approval/HrmLeaveApplicationDO.java
+++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/approval/HrmLeaveApplicationDO.java
@@ -95,11 +95,6 @@
private LocalDateTime approveTime;
/**
- * 闄勪欢鍦板潃
- */
- private String fileUrl;
-
- /**
* 澶囨敞
*/
private String remark;
diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/approval/HrmResignationApplicationDO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/approval/HrmResignationApplicationDO.java
index a2be939..9059bcf 100644
--- a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/approval/HrmResignationApplicationDO.java
+++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/approval/HrmResignationApplicationDO.java
@@ -108,11 +108,6 @@
private java.time.LocalDateTime approveTime;
/**
- * 闄勪欢鍦板潃
- */
- private String fileUrl;
-
- /**
* 澶囨敞
*/
private String remark;
diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/approval/HrmTransferApplicationDO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/approval/HrmTransferApplicationDO.java
index 7314a86..1378eae 100644
--- a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/approval/HrmTransferApplicationDO.java
+++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/approval/HrmTransferApplicationDO.java
@@ -105,11 +105,6 @@
private java.time.LocalDateTime approveTime;
/**
- * 闄勪欢鍦板潃
- */
- private String fileUrl;
-
- /**
* 澶囨敞
*/
private String remark;
diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/attendance/HrmAttendanceExceptionDO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/attendance/HrmAttendanceExceptionDO.java
index 8e9a4e9..db403d1 100644
--- a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/attendance/HrmAttendanceExceptionDO.java
+++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/attendance/HrmAttendanceExceptionDO.java
@@ -83,11 +83,6 @@
private LocalDateTime approveTime;
/**
- * 闄勪欢鍦板潃
- */
- private String fileUrl;
-
- /**
* 澶囨敞
*/
private String remark;
diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/salary/HrmSalaryPaymentDO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/salary/HrmSalaryPaymentDO.java
index ae7af88..2075954 100644
--- a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/salary/HrmSalaryPaymentDO.java
+++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/dataobject/salary/HrmSalaryPaymentDO.java
@@ -66,11 +66,6 @@
private Integer status;
/**
- * 閾惰鍥炲崟闄勪欢
- */
- private String fileUrl;
-
- /**
* 澶囨敞
*/
private String remark;
diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/approval/HrmLeaveApplicationServiceImpl.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/approval/HrmLeaveApplicationServiceImpl.java
index e2f527c..bda7f3a 100644
--- a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/approval/HrmLeaveApplicationServiceImpl.java
+++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/approval/HrmLeaveApplicationServiceImpl.java
@@ -20,6 +20,7 @@
import cn.iocoder.yudao.module.hrm.enums.HrmLeaveTypeEnum;
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
+import cn.iocoder.yudao.module.system.api.storage.StorageAttachmentApi;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import jakarta.annotation.Resource;
@@ -65,6 +66,8 @@
@Resource
private BpmCategoryService bpmCategoryService;
@Resource
+ private StorageAttachmentApi storageAttachmentApi;
+ @Resource
private BpmProcessDefinitionService bpmProcessDefinitionService;
@Override
@@ -88,6 +91,9 @@
application.setDeptId(createReqVO.getDeptId() != null ? createReqVO.getDeptId() : user.getDeptId());
application.setStatus(HrmAuditStatusEnum.DRAFT.getStatus());
leaveApplicationMapper.insert(application);
+ // 缁戝畾闄勪欢
+ storageAttachmentApi.bindAttachments("file", "hrm_leave_application",
+ application.getId(), createReqVO.getBlobIds());
return application.getId();
}
@@ -115,6 +121,12 @@
// 鏇存柊
HrmLeaveApplicationDO updateObj = BeanUtils.toBean(updateReqVO, HrmLeaveApplicationDO.class);
leaveApplicationMapper.updateById(updateObj);
+ // 閲嶆柊缁戝畾闄勪欢
+ if (updateReqVO.getBlobIds() != null) {
+ storageAttachmentApi.deleteAttachmentsByRecord("hrm_leave_application", updateReqVO.getId());
+ storageAttachmentApi.bindAttachments("file", "hrm_leave_application",
+ updateReqVO.getId(), updateReqVO.getBlobIds());
+ }
}
@Override
@@ -125,6 +137,8 @@
if (!Objects.equals(application.getStatus(), HrmAuditStatusEnum.DRAFT.getStatus())) {
throw exception(LEAVE_APPLICATION_UPDATE_FAIL_NOT_DRAFT);
}
+ // 鍒犻櫎闄勪欢鍏宠仈
+ storageAttachmentApi.deleteAttachmentsByRecord("hrm_leave_application", id);
// 鍒犻櫎
leaveApplicationMapper.deleteById(id);
}
@@ -167,6 +181,8 @@
respVO.setLeaveTypeName(HrmLeaveTypeEnum.getNameByStatus(application.getLeaveType()));
// 濉厖瀹℃壒鐘舵�佸悕绉�
respVO.setStatusName(HrmAuditStatusEnum.getNameByStatus(application.getStatus()));
+ // 濉厖闄勪欢鍒楄〃
+ respVO.setAttachmentList(storageAttachmentApi.listAttachments("hrm_leave_application", id));
return respVO;
}
diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/approval/HrmResignationApplicationServiceImpl.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/approval/HrmResignationApplicationServiceImpl.java
index 3548495..b0858ce 100644
--- a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/approval/HrmResignationApplicationServiceImpl.java
+++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/approval/HrmResignationApplicationServiceImpl.java
@@ -19,6 +19,7 @@
import cn.iocoder.yudao.module.hrm.enums.HrmResignationTypeEnum;
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
+import cn.iocoder.yudao.module.system.api.storage.StorageAttachmentApi;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import jakarta.annotation.Resource;
@@ -69,6 +70,8 @@
@Resource
private BpmCategoryService bpmCategoryService;
@Resource
+ private StorageAttachmentApi storageAttachmentApi;
+ @Resource
private BpmProcessDefinitionService bpmProcessDefinitionService;
@Override
@@ -95,6 +98,8 @@
application.setStatus(HrmAuditStatusEnum.DRAFT.getStatus());
application.setHandoverStatus(HANDOVER_STATUS_NOT_STARTED);
resignationApplicationMapper.insert(application);
+ storageAttachmentApi.bindAttachments("file", "hrm_resignation_application",
+ application.getId(), createReqVO.getBlobIds());
return application.getId();
}
@@ -118,6 +123,11 @@
}
HrmResignationApplicationDO updateObj = BeanUtils.toBean(updateReqVO, HrmResignationApplicationDO.class);
resignationApplicationMapper.updateById(updateObj);
+ if (updateReqVO.getBlobIds() != null) {
+ storageAttachmentApi.deleteAttachmentsByRecord("hrm_resignation_application", updateReqVO.getId());
+ storageAttachmentApi.bindAttachments("file", "hrm_resignation_application",
+ updateReqVO.getId(), updateReqVO.getBlobIds());
+ }
}
@Override
@@ -126,6 +136,7 @@
if (!Objects.equals(application.getStatus(), HrmAuditStatusEnum.DRAFT.getStatus())) {
throw exception(RESIGNATION_APPLICATION_UPDATE_FAIL_NOT_DRAFT);
}
+ storageAttachmentApi.deleteAttachmentsByRecord("hrm_resignation_application", id);
resignationApplicationMapper.deleteById(id);
}
@@ -159,6 +170,7 @@
respVO.setResignationTypeName(HrmResignationTypeEnum.getNameByStatus(application.getResignationType()));
respVO.setHandoverStatusName(getHandoverStatusName(application.getHandoverStatus()));
respVO.setStatusName(HrmAuditStatusEnum.getNameByStatus(application.getStatus()));
+ respVO.setAttachmentList(storageAttachmentApi.listAttachments("hrm_resignation_application", id));
return respVO;
}
diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/approval/HrmTransferApplicationServiceImpl.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/approval/HrmTransferApplicationServiceImpl.java
index d63e58a..be91a86 100644
--- a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/approval/HrmTransferApplicationServiceImpl.java
+++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/approval/HrmTransferApplicationServiceImpl.java
@@ -19,6 +19,7 @@
import cn.iocoder.yudao.module.hrm.enums.HrmTransferTypeEnum;
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.dept.PostApi;
+import cn.iocoder.yudao.module.system.api.storage.StorageAttachmentApi;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import cn.iocoder.yudao.module.system.api.dept.dto.PostRespDTO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
@@ -69,6 +70,8 @@
@Resource
private BpmCategoryService bpmCategoryService;
@Resource
+ private StorageAttachmentApi storageAttachmentApi;
+ @Resource
private BpmProcessDefinitionService bpmProcessDefinitionService;
@Override
@@ -99,6 +102,8 @@
application.setUserId(userId);
application.setStatus(HrmAuditStatusEnum.DRAFT.getStatus());
transferApplicationMapper.insert(application);
+ storageAttachmentApi.bindAttachments("file", "hrm_transfer_application",
+ application.getId(), createReqVO.getBlobIds());
return application.getId();
}
@@ -129,6 +134,11 @@
}
HrmTransferApplicationDO updateObj = BeanUtils.toBean(updateReqVO, HrmTransferApplicationDO.class);
transferApplicationMapper.updateById(updateObj);
+ if (updateReqVO.getBlobIds() != null) {
+ storageAttachmentApi.deleteAttachmentsByRecord("hrm_transfer_application", updateReqVO.getId());
+ storageAttachmentApi.bindAttachments("file", "hrm_transfer_application",
+ updateReqVO.getId(), updateReqVO.getBlobIds());
+ }
}
@Override
@@ -137,6 +147,7 @@
if (!Objects.equals(application.getStatus(), HrmAuditStatusEnum.DRAFT.getStatus())) {
throw exception(TRANSFER_APPLICATION_UPDATE_FAIL_NOT_DRAFT);
}
+ storageAttachmentApi.deleteAttachmentsByRecord("hrm_transfer_application", id);
transferApplicationMapper.deleteById(id);
}
@@ -170,6 +181,7 @@
fillPostNames(respVO, application);
respVO.setTransferTypeName(HrmTransferTypeEnum.getNameByStatus(application.getTransferType()));
respVO.setStatusName(HrmAuditStatusEnum.getNameByStatus(application.getStatus()));
+ respVO.setAttachmentList(storageAttachmentApi.listAttachments("hrm_transfer_application", id));
return respVO;
}
diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/attendance/HrmAttendanceExceptionServiceImpl.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/attendance/HrmAttendanceExceptionServiceImpl.java
index 89cfe32..0196b9b 100644
--- a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/attendance/HrmAttendanceExceptionServiceImpl.java
+++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/attendance/HrmAttendanceExceptionServiceImpl.java
@@ -9,6 +9,7 @@
import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmAttendanceExceptionSaveReqVO;
import cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.HrmAttendanceExceptionDO;
import cn.iocoder.yudao.module.hrm.dal.mysql.attendance.HrmAttendanceExceptionMapper;
+import cn.iocoder.yudao.module.system.api.storage.StorageAttachmentApi;
import cn.iocoder.yudao.module.hrm.dal.redis.HrmNoRedisDAO;
import cn.iocoder.yudao.module.hrm.enums.HrmAuditStatusEnum;
import jakarta.annotation.Resource;
@@ -39,6 +40,8 @@
@Resource
private BpmProcessInstanceApi bpmProcessInstanceApi;
@Resource
+ private StorageAttachmentApi storageAttachmentApi;
+ @Resource
private BpmProcessDefinitionService bpmProcessDefinitionService;
@Override
@@ -47,6 +50,8 @@
attendanceException.setNo(generateNo());
attendanceException.setStatus(HrmAuditStatusEnum.DRAFT.getStatus());
attendanceExceptionMapper.insert(attendanceException);
+ storageAttachmentApi.bindAttachments("file", "hrm_attendance_exception",
+ attendanceException.getId(), createReqVO.getBlobIds());
return attendanceException.getId();
}
@@ -59,11 +64,17 @@
validateAttendanceExceptionExists(updateReqVO.getId());
HrmAttendanceExceptionDO updateObj = BeanUtils.toBean(updateReqVO, HrmAttendanceExceptionDO.class);
attendanceExceptionMapper.updateById(updateObj);
+ if (updateReqVO.getBlobIds() != null) {
+ storageAttachmentApi.deleteAttachmentsByRecord("hrm_attendance_exception", updateReqVO.getId());
+ storageAttachmentApi.bindAttachments("file", "hrm_attendance_exception",
+ updateReqVO.getId(), updateReqVO.getBlobIds());
+ }
}
@Override
public void deleteAttendanceException(Long id) {
validateAttendanceExceptionExists(id);
+ storageAttachmentApi.deleteAttachmentsByRecord("hrm_attendance_exception", id);
attendanceExceptionMapper.deleteById(id);
}
diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/salary/HrmSalaryPaymentServiceImpl.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/salary/HrmSalaryPaymentServiceImpl.java
index 264a15c..b262fb6 100644
--- a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/salary/HrmSalaryPaymentServiceImpl.java
+++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/salary/HrmSalaryPaymentServiceImpl.java
@@ -11,6 +11,7 @@
import cn.iocoder.yudao.module.hrm.dal.mysql.salary.HrmSalaryPaymentDetailMapper;
import cn.iocoder.yudao.module.hrm.dal.mysql.salary.HrmSalaryPaymentMapper;
import cn.iocoder.yudao.module.hrm.dal.redis.HrmNoRedisDAO;
+import cn.iocoder.yudao.module.system.api.storage.StorageAttachmentApi;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -40,6 +41,8 @@
private HrmSalaryCalculationMapper salaryCalculationMapper;
@Resource
private HrmNoRedisDAO noRedisDAO;
+ @Resource
+ private StorageAttachmentApi storageAttachmentApi;
@Override
public PageResult<HrmSalaryPaymentDO> getSalaryPaymentPage(HrmSalaryPaymentPageReqVO pageReqVO) {
@@ -280,6 +283,8 @@
for (HrmSalaryPaymentDetailDO detail : details) {
salaryPaymentDetailMapper.deleteById(detail.getId());
}
+ // 鍒犻櫎闄勪欢鍏宠仈
+ storageAttachmentApi.deleteAttachmentsByRecord("hrm_salary_payment", id);
// 鍒犻櫎鍙戞斁鍙拌处
salaryPaymentMapper.deleteById(id);
}
diff --git a/yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/controller/admin/item/MdmItemController.java b/yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/controller/admin/item/MdmItemController.java
index d271a28..282132b 100644
--- a/yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/controller/admin/item/MdmItemController.java
+++ b/yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/controller/admin/item/MdmItemController.java
@@ -18,6 +18,7 @@
import cn.iocoder.yudao.module.mdm.service.item.MdmItemService;
import cn.iocoder.yudao.module.mdm.service.unit.MdmUnitMeasureService;
import cn.iocoder.yudao.module.mdm.service.warehouse.MdmWarehouseService;
+import cn.iocoder.yudao.module.mes.api.item.MesMdItemApi;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
@@ -41,6 +42,9 @@
@Resource
private MdmItemService itemService;
+
+ @Resource
+ private MesMdItemApi mesMdItemApi;
@Resource
private MdmItemCategoryService itemCategoryService;
@@ -105,6 +109,8 @@
enrichItemRespVO(resp);
// 濉厖鎵规灞炴�ч厤缃�
enrichItemBatchConfig(resp);
+ // 濉厖鍚屾鐘舵��
+ resp.setSyncedMes(mesMdItemApi.getSyncedMdmItemIds(Set.of(id)).contains(id));
}
return success(resp);
}
@@ -122,6 +128,9 @@
convertSet(pageResult.getList(), MdmItemDO::getUnitMeasureId));
// 杞崲
List<MdmItemRespVO> respList = BeanUtils.toBean(pageResult.getList(), MdmItemRespVO.class);
+ // 鎵归噺鏌ヨ鍚屾鐘舵��
+ Set<Long> allIds = convertSet(respList, MdmItemRespVO::getId);
+ Set<Long> syncedIds = mesMdItemApi.getSyncedMdmItemIds(allIds);
// 鎵归噺鍏宠仈鏌ヨ杈呭崟浣嶅悕绉�
Set<Long> unitIds = new HashSet<>();
respList.forEach(resp -> {
@@ -161,6 +170,8 @@
if (warehouse != null) {
resp.setWarehouseName(warehouse.getName());
}
+ // 鍚屾鐘舵��
+ resp.setSyncedMes(syncedIds.contains(resp.getId()));
});
return success(new PageResult<>(respList, pageResult.getTotal()));
}
diff --git a/yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/controller/admin/item/vo/MdmItemRespVO.java b/yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/controller/admin/item/vo/MdmItemRespVO.java
index c8674e3..b9709d3 100644
--- a/yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/controller/admin/item/vo/MdmItemRespVO.java
+++ b/yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/controller/admin/item/vo/MdmItemRespVO.java
@@ -132,6 +132,9 @@
@Schema(description = "澶囨敞", example = "澶囨敞淇℃伅")
private String remark;
+ @Schema(description = "鏄惁宸插悓姝ュ埌 MES", example = "true")
+ private Boolean syncedMes;
+
@Schema(description = "鍒涘缓鏃堕棿", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
diff --git a/yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/service/item/MdmItemServiceImpl.java b/yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/service/item/MdmItemServiceImpl.java
index 503a0e4..14990a0 100644
--- a/yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/service/item/MdmItemServiceImpl.java
+++ b/yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/service/item/MdmItemServiceImpl.java
@@ -77,11 +77,9 @@
// 淇濆瓨鎵规閰嶇疆锛堝鏋滄湁锛�
saveBatchConfig(item.getId(), createReqVO);
- // 鍚屾鍒� MES锛堝嬀閫� syncMes 鏃讹級
+ // 濮嬬粓鍚屾鍒� MES
createReqVO.setId(item.getId());
- if (Boolean.TRUE.equals(createReqVO.getSyncMes())) {
- syncItemToMes(createReqVO);
- }
+ syncItemToMes(createReqVO);
return item.getId();
}
@@ -100,10 +98,8 @@
// 淇濆瓨鎵规閰嶇疆锛堝鏋滄湁锛�
saveBatchConfig(updateReqVO.getId(), updateReqVO);
- // 鍚屾鍒� MES锛堝嬀閫� syncMes 鏃讹級
- if (Boolean.TRUE.equals(updateReqVO.getSyncMes())) {
- syncItemToMes(updateReqVO);
- }
+ // 濮嬬粓鍚屾鍒� MES
+ syncItemToMes(updateReqVO);
}
private void validateItemSaveData(MdmItemSaveReqVO reqVO) {
@@ -165,11 +161,14 @@
}
@Override
+ @Transactional(rollbackFor = Exception.class)
public void updateItemStatus(Long id, Integer status) {
// 鏍¢獙瀛樺湪
validateItemExists(id);
// 鏇存柊鐘舵��
itemMapper.updateById(new MdmItemDO().setId(id).setStatus(status));
+ // 鍚屾鐘舵�佸埌 MES
+ mesMdItemApi.updateItemStatus(id, status);
}
@Override
@@ -181,6 +180,8 @@
validateItemNoReference(id);
// 杞垹闄ゅ綋鍓嶈褰�
itemMapper.deleteById(id);
+ // 鍚屾鍒犻櫎鍒� MES
+ mesMdItemApi.deleteItem(id);
}
/**
diff --git a/yudao-module-mes-api/src/main/java/cn/iocoder/yudao/module/mes/api/item/MesMdItemApi.java b/yudao-module-mes-api/src/main/java/cn/iocoder/yudao/module/mes/api/item/MesMdItemApi.java
index a683eaf..aa030ce 100644
--- a/yudao-module-mes-api/src/main/java/cn/iocoder/yudao/module/mes/api/item/MesMdItemApi.java
+++ b/yudao-module-mes-api/src/main/java/cn/iocoder/yudao/module/mes/api/item/MesMdItemApi.java
@@ -2,6 +2,9 @@
import cn.iocoder.yudao.module.mes.api.item.dto.MesMdItemSyncReqDTO;
+import java.util.Collection;
+import java.util.Set;
+
/**
* MES 鐗╂枡浜у搧 API 鎺ュ彛
*
@@ -29,4 +32,31 @@
*/
void updateItem(MesMdItemSyncReqDTO syncReqDTO);
+ /**
+ * 鍒犻櫎 MES 鐗╂枡浜у搧锛堝悓姝ョ敤锛�
+ *
+ * 鎸� MDM 鐗╂枡 ID 鏌ユ壘 MES 璁板綍骞跺垹闄わ紝涓嶅瓨鍦ㄥ垯蹇界暐銆�
+ *
+ * @param mdmItemId MDM 鐗╂枡 ID
+ */
+ void deleteItem(Long mdmItemId);
+
+ /**
+ * 鏇存柊 MES 鐗╂枡浜у搧鐘舵�侊紙鍚屾鐢級
+ *
+ * 鎸� MDM 鐗╂枡 ID 鏌ユ壘 MES 璁板綍骞舵洿鏂扮姸鎬侊紝涓嶅瓨鍦ㄥ垯蹇界暐銆�
+ *
+ * @param mdmItemId MDM 鐗╂枡 ID
+ * @param status 鐘舵��
+ */
+ void updateItemStatus(Long mdmItemId, Integer status);
+
+ /**
+ * 鑾峰彇宸插悓姝ュ埌 MES 鐨� MDM 鐗╂枡 ID 闆嗗悎
+ *
+ * @param mdmItemIds MDM 鐗╂枡 ID 鍒楄〃
+ * @return 瀛樺湪浜� MES 涓殑 MDM 鐗╂枡 ID 闆嗗悎
+ */
+ Set<Long> getSyncedMdmItemIds(Collection<Long> mdmItemIds);
+
}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/api/item/MesMdItemApiImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/api/item/MesMdItemApiImpl.java
index fe63450..c314980 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/api/item/MesMdItemApiImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/api/item/MesMdItemApiImpl.java
@@ -7,6 +7,7 @@
import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemTypeDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.unitmeasure.MesMdUnitMeasureDO;
+import cn.iocoder.yudao.module.mes.dal.mysql.md.item.MesMdItemMapper;
import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemBatchConfigService;
import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemTypeService;
@@ -15,6 +16,9 @@
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
+
+import java.util.Collection;
+import java.util.Set;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
@@ -34,6 +38,8 @@
@Resource
private MesMdItemService itemService;
+ @Resource
+ private MesMdItemMapper itemMapper;
@Resource
private MesMdItemTypeService itemTypeService;
@Resource
@@ -106,6 +112,29 @@
return saveReqVO;
}
+ @Override
+ public void deleteItem(Long mdmItemId) {
+ log.info("[deleteItem] 鍚屾鍒犻櫎 MES 鐗╂枡浜у搧锛宮dmItemId={}", mdmItemId);
+ MesMdItemDO existItem = itemService.getItemByMdmItemId(mdmItemId);
+ if (existItem != null) {
+ itemService.deleteItem(existItem.getId());
+ }
+ }
+
+ @Override
+ public void updateItemStatus(Long mdmItemId, Integer status) {
+ log.info("[updateItemStatus] 鍚屾鏇存柊 MES 鐗╂枡浜у搧鐘舵�侊紝mdmItemId={}, status={}", mdmItemId, status);
+ MesMdItemDO existItem = itemService.getItemByMdmItemId(mdmItemId);
+ if (existItem != null) {
+ itemService.updateItemStatus(existItem.getId(), status);
+ }
+ }
+
+ @Override
+ public Set<Long> getSyncedMdmItemIds(Collection<Long> mdmItemIds) {
+ return itemMapper.selectMdmItemIdsByMdmItemIds(mdmItemIds);
+ }
+
/**
* 淇濆瓨鎵规閰嶇疆
*/
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/archive/vo/MesPdArchiveDetailRespVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/archive/vo/MesPdArchiveDetailRespVO.java
index 1ced7b7..46e8a87 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/archive/vo/MesPdArchiveDetailRespVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/archive/vo/MesPdArchiveDetailRespVO.java
@@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.archive.vo;
+import cn.iocoder.yudao.module.system.api.storage.dto.StorageBlobRespDTO;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
import cn.iocoder.yudao.module.mes.enums.DictTypeConstants;
@@ -122,8 +123,8 @@
@Schema(description = "鏂囦欢鍚�", example = "design.pdf")
private String fileName;
- @Schema(description = "鏂囦欢URL", example = "/files/design.pdf")
- private String fileUrl;
+ @Schema(description = "闄勪欢鍒楄〃")
+ private List<StorageBlobRespDTO> attachmentList;
@Schema(description = "涓婁紶浜�", example = "1")
private Long uploadUserId;
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/document/MesPdDocumentController.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/document/MesPdDocumentController.java
index 3c84e72..d5ef60b 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/document/MesPdDocumentController.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/document/MesPdDocumentController.java
@@ -13,6 +13,7 @@
import cn.iocoder.yudao.module.mes.controller.admin.pd.document.vo.MesPdDocumentSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.document.MesPdDocumentDO;
import cn.iocoder.yudao.module.mes.service.pd.document.MesPdDocumentService;
+import cn.iocoder.yudao.module.system.api.storage.StorageAttachmentApi;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import io.swagger.v3.oas.annotations.Operation;
@@ -45,6 +46,8 @@
@Resource
private AdminUserApi adminUserApi;
+ @Resource
+ private StorageAttachmentApi storageAttachmentApi;
@PostMapping("/create")
@Operation(summary = "鍒涘缓璁捐璧勬枡")
@@ -79,7 +82,9 @@
if (document == null) {
return success(null);
}
- return success(buildDocumentRespVOList(Collections.singletonList(document)).get(0));
+ MesPdDocumentRespVO respVO = buildDocumentRespVOList(Collections.singletonList(document)).get(0);
+ respVO.setAttachmentList(storageAttachmentApi.listAttachments("mes_pd_document", id));
+ return success(respVO);
}
@GetMapping("/page")
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/document/vo/MesPdDocumentRespVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/document/vo/MesPdDocumentRespVO.java
index 54668b6..f462b80 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/document/vo/MesPdDocumentRespVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/document/vo/MesPdDocumentRespVO.java
@@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.mes.controller.admin.pd.document.vo;
+import cn.iocoder.yudao.module.system.api.storage.dto.StorageBlobRespDTO;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
import cn.iocoder.yudao.module.mes.enums.DictTypeConstants;
@@ -9,6 +10,7 @@
import lombok.Data;
import java.time.LocalDateTime;
+import java.util.List;
@Schema(description = "绠$悊鍚庡彴 - MES 璁捐璧勬枡 Response VO")
@Data
@@ -50,9 +52,8 @@
@ExcelProperty("涓婁紶鏃堕棿")
private LocalDateTime uploadTime;
- @Schema(description = "鏂囦欢URL", example = "https://xxx/file.pdf")
- @ExcelProperty("鏂囦欢URL")
- private String fileUrl;
+ @Schema(description = "闄勪欢鍒楄〃")
+ private List<StorageBlobRespDTO> attachmentList;
@Schema(description = "澶囨敞", example = "娴嬭瘯澶囨敞")
@ExcelProperty("澶囨敞")
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/document/vo/MesPdDocumentSaveReqVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/document/vo/MesPdDocumentSaveReqVO.java
index cbbc65e..56048e4 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/document/vo/MesPdDocumentSaveReqVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pd/document/vo/MesPdDocumentSaveReqVO.java
@@ -6,6 +6,7 @@
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import java.time.LocalDateTime;
+import java.util.List;
@Schema(description = "绠$悊鍚庡彴 - MES 璁捐璧勬枡鏂板/淇敼 Request VO")
@Data
@@ -37,8 +38,8 @@
@Schema(description = "涓婁紶鏃堕棿")
private LocalDateTime uploadTime;
- @Schema(description = "鏂囦欢URL", example = "https://xxx/file.pdf")
- private String fileUrl;
+ @Schema(description = "闄勪欢blobId鍒楄〃", example = "[1, 2, 3]")
+ private List<Long> blobIds;
@Schema(description = "澶囨敞", example = "娴嬭瘯澶囨敞")
private String remark;
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/task/MesProTaskController.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/task/MesProTaskController.java
index 7269c32..19dd295 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/task/MesProTaskController.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/task/MesProTaskController.java
@@ -254,15 +254,19 @@
convertSet(itemMap.values(), MesMdItemDO::getUnitMeasureId));
Map<Long, CrmCustomerRespDTO> customerMap = customerApi.getCustomerMap(
convertSet(list, MesProTaskDO::getClientId));
- // 宸ュ簭鐨� checkFlag锛氭壒閲忔煡璇㈠悗鏋勫缓 routeId -> processId -> checkFlag 鐨勫弻灞� Map
+ // 宸ュ簭鐨� checkFlag + backflushFlag锛氭壒閲忔煡璇㈠悗鏋勫缓 routeId -> processId -> flag 鐨勫弻灞� Map
Set<Long> routeIds = convertSet(list, MesProTaskDO::getRouteId);
Map<Long, Map<Long, Boolean>> routeProcessCheckFlagMap = new HashMap<>();
+ Map<Long, Map<Long, Boolean>> routeProcessBackflushFlagMap = new HashMap<>();
if (CollUtil.isNotEmpty(routeIds)) {
List<MesProRouteProcessDO> allRouteProcesses = routeProcessService.getRouteProcessListByRouteIds(routeIds);
for (MesProRouteProcessDO rp : allRouteProcesses) {
routeProcessCheckFlagMap
.computeIfAbsent(rp.getRouteId(), k -> new HashMap<>())
.put(rp.getProcessId(), Boolean.TRUE.equals(rp.getCheckFlag()));
+ routeProcessBackflushFlagMap
+ .computeIfAbsent(rp.getRouteId(), k -> new HashMap<>())
+ .put(rp.getProcessId(), Boolean.TRUE.equals(rp.getBackflushFlag()));
}
}
// 鎶ュ伐鍦ㄩ�旈噺锛氭寜浠诲姟鑱氬悎 feedbackQuantity锛堣崏绋�/瀹℃壒涓�/寰呮楠� = 鍦ㄩ�旓紱宸插畬鎴� = 宸插鎵癸級
@@ -303,6 +307,8 @@
vo.setClientName(customer.getName()));
findAndThen(routeProcessCheckFlagMap, task.getRouteId(), processCheckMap ->
findAndThen(processCheckMap, task.getProcessId(), vo::setCheckFlag));
+ findAndThen(routeProcessBackflushFlagMap, task.getRouteId(), processBackflushMap ->
+ findAndThen(processBackflushMap, task.getProcessId(), vo::setBackflushFlag));
return vo;
});
}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/task/vo/MesProTaskRespVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/task/vo/MesProTaskRespVO.java
index b6f3266..eb3c490 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/task/vo/MesProTaskRespVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/task/vo/MesProTaskRespVO.java
@@ -121,6 +121,9 @@
@Schema(description = "鏄惁璐ㄦ锛堟淳鐢熻嚜宸ヨ壓璺嚎宸ュ簭 checkFlag锛�", example = "true")
private Boolean checkFlag;
+ @Schema(description = "鏄惁鍊掑啿锛堟淳鐢熻嚜宸ヨ壓璺嚎宸ュ簭 backflushFlag锛�", example = "true")
+ private Boolean backflushFlag;
+
@Schema(description = "澶囨敞", example = "澶囨敞")
private String remark;
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/MesProWorkOrderProcessController.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/MesProWorkOrderProcessController.java
index f9260ad..398cda6 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/MesProWorkOrderProcessController.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/MesProWorkOrderProcessController.java
@@ -6,14 +6,17 @@
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.process.MesProProcessBomItemVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.process.MesProWorkOrderProcessPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.process.MesProWorkOrderProcessRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.process.MesProWorkOrderProcessSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.process.MesProProcessDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderBomDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderProcessDO;
import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
import cn.iocoder.yudao.module.mes.service.pro.process.MesProProcessService;
+import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderBomService;
import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderProcessService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
@@ -37,6 +40,8 @@
@Resource
private MesProWorkOrderProcessService workOrderProcessService;
+ @Resource
+ private MesProWorkOrderBomService workOrderBomService;
@Resource
private MesProProcessService processService;
@Resource
@@ -135,8 +140,32 @@
Map<Long, MesMdItemDO> itemMap = CollUtil.isNotEmpty(outputItemIds)
? itemService.getItemMap(outputItemIds) : Collections.emptyMap();
- // 2. 鎷兼帴 VO
- return BeanUtils.toBean(list, MesProWorkOrderProcessRespVO.class, vo -> {
+ // 2. 鏌ヨ鎶曟枡BOM锛堟寜宸ュ簭鍒嗙粍锛�
+ Set<Long> workOrderIds = convertSet(list, MesProWorkOrderProcessDO::getWorkOrderId);
+ workOrderIds.removeAll(Collections.singleton(null));
+ Map<Long, List<MesProProcessBomItemVO>> bomItemsByProcessId = new HashMap<>();
+ Set<Long> allBomItemIds = new HashSet<>();
+ for (Long woId : workOrderIds) {
+ List<MesProWorkOrderBomDO> bomList = workOrderBomService.getWorkOrderBomListByWorkOrderId(woId);
+ if (CollUtil.isNotEmpty(bomList)) {
+ allBomItemIds.addAll(convertSet(bomList, MesProWorkOrderBomDO::getItemId));
+ for (MesProWorkOrderBomDO bom : bomList) {
+ bomItemsByProcessId.computeIfAbsent(bom.getProcessId(), k -> new ArrayList<>())
+ .add(BeanUtils.toBean(bom, MesProProcessBomItemVO.class));
+ }
+ }
+ }
+ // 濉厖鎶曟枡鐗╂枡缂栫爜鍜屽悕绉�
+ Map<Long, MesMdItemDO> bomItemMap = CollUtil.isNotEmpty(allBomItemIds)
+ ? itemService.getItemMap(allBomItemIds) : Collections.emptyMap();
+ bomItemsByProcessId.values().forEach(vos -> vos.forEach(vo -> {
+ MapUtils.findAndThen(bomItemMap, vo.getItemId(), item -> {
+ vo.setItemCode(item.getCode()).setItemName(item.getName());
+ });
+ }));
+
+ // 3. 鎷兼帴 VO
+ List<MesProWorkOrderProcessRespVO> result = BeanUtils.toBean(list, MesProWorkOrderProcessRespVO.class, vo -> {
MapUtils.findAndThen(processMap, vo.getProcessId(), process -> {
vo.setProcessCode(process.getCode()).setProcessName(process.getName());
});
@@ -147,6 +176,9 @@
vo.setOutputItemCode(item.getCode()).setOutputItemName(item.getName());
});
});
+ // 璁剧疆姣忎釜宸ュ簭鍚勮嚜鐨勬姇鏂橞OM
+ result.forEach(vo -> vo.setBomItems(bomItemsByProcessId.getOrDefault(vo.getProcessId(), Collections.emptyList())));
+ return result;
}
}
\ No newline at end of file
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/process/MesProWorkOrderProcessRespVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/process/MesProWorkOrderProcessRespVO.java
index bda4a62..e2806fd 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/process/MesProWorkOrderProcessRespVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/process/MesProWorkOrderProcessRespVO.java
@@ -2,10 +2,12 @@
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
+import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.process.MesProProcessBomItemVO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
+import java.util.List;
@Schema(description = "绠$悊鍚庡彴 - MES 鐢熶骇宸ュ崟宸ュ簭 Response VO")
@Data
@@ -79,6 +81,9 @@
@ExcelProperty("澶囨敞")
private String remark;
+ @Schema(description = "宸ュ簭鎶曟枡BOM鏄庣粏")
+ private List<MesProProcessBomItemVO> bomItems;
+
@Schema(description = "鍒涘缓鏃堕棿", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("鍒涘缓鏃堕棿")
private LocalDateTime createTime;
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pd/document/MesPdDocumentDO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pd/document/MesPdDocumentDO.java
index 92f4e0f..8ee2ba7 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pd/document/MesPdDocumentDO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pd/document/MesPdDocumentDO.java
@@ -65,10 +65,6 @@
*/
private LocalDateTime uploadTime;
/**
- * 鏂囦欢URL
- */
- private String fileUrl;
- /**
* 澶囨敞
*/
private String remark;
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/workorder/MesProWorkOrderBomDO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/workorder/MesProWorkOrderBomDO.java
index db63e65..09be14c 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/workorder/MesProWorkOrderBomDO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/workorder/MesProWorkOrderBomDO.java
@@ -36,6 +36,12 @@
*/
private Long workOrderId;
/**
+ * 宸ュ簭缂栧彿
+ *
+ * 鍏宠仈 {@link cn.iocoder.yudao.module.mes.dal.dataobject.pro.process.MesProProcessDO#getId()}
+ */
+ private Long processId;
+ /**
* BOM 鐗╂枡缂栧彿
*
* 鍏宠仈 {@link MesMdItemDO#getId()}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/md/item/MesMdItemMapper.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/md/item/MesMdItemMapper.java
index e6c2fad..81d024d 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/md/item/MesMdItemMapper.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/md/item/MesMdItemMapper.java
@@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.mes.dal.mysql.md.item;
+import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
@@ -8,6 +9,12 @@
import org.apache.ibatis.annotations.Mapper;
import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
/**
* MES 鐗╂枡浜у搧 Mapper
@@ -47,4 +54,18 @@
return selectOne(MesMdItemDO::getMdmItemId, mdmItemId);
}
+ /**
+ * 鎵归噺鏌ヨ宸插瓨鍦ㄧ殑 MDM 鐗╂枡 ID 闆嗗悎
+ *
+ * @param mdmItemIds MDM 鐗╂枡 ID 鍒楄〃
+ * @return 瀛樺湪浜� mes_md_item 涓殑 mdmItemId 闆嗗悎
+ */
+ default Set<Long> selectMdmItemIdsByMdmItemIds(Collection<Long> mdmItemIds) {
+ if (CollUtil.isEmpty(mdmItemIds)) {
+ return Collections.emptySet();
+ }
+ List<MesMdItemDO> list = selectList(MesMdItemDO::getMdmItemId, mdmItemIds);
+ return convertSet(list, MesMdItemDO::getMdmItemId);
+ }
+
}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/workorder/MesProWorkOrderBomMapper.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/workorder/MesProWorkOrderBomMapper.java
index 28f6f60..93c8eca 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/workorder/MesProWorkOrderBomMapper.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/workorder/MesProWorkOrderBomMapper.java
@@ -27,6 +27,12 @@
return selectList(MesProWorkOrderBomDO::getWorkOrderId, workOrderId);
}
+ default List<MesProWorkOrderBomDO> selectListByWorkOrderIdAndProcessId(Long workOrderId, Long processId) {
+ return selectList(new LambdaQueryWrapperX<MesProWorkOrderBomDO>()
+ .eq(MesProWorkOrderBomDO::getWorkOrderId, workOrderId)
+ .eqIfPresent(MesProWorkOrderBomDO::getProcessId, processId));
+ }
+
default void deleteByWorkOrderId(Long workOrderId) {
delete(MesProWorkOrderBomDO::getWorkOrderId, workOrderId);
}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/document/MesPdDocumentServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/document/MesPdDocumentServiceImpl.java
index c733e24..d1d8172 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/document/MesPdDocumentServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/document/MesPdDocumentServiceImpl.java
@@ -7,6 +7,7 @@
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.document.MesPdDocumentDO;
import cn.iocoder.yudao.module.mes.dal.mysql.pd.document.MesPdDocumentMapper;
import cn.iocoder.yudao.module.mes.service.pd.project.MesPdProjectService;
+import cn.iocoder.yudao.module.system.api.storage.StorageAttachmentApi;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
@@ -32,6 +33,8 @@
@Resource
@Lazy
private MesPdProjectService projectService;
+ @Resource
+ private StorageAttachmentApi storageAttachmentApi;
@Override
public Long createDocument(MesPdDocumentSaveReqVO createReqVO) {
@@ -40,6 +43,8 @@
// 鎻掑叆
MesPdDocumentDO document = BeanUtils.toBean(createReqVO, MesPdDocumentDO.class);
documentMapper.insert(document);
+ storageAttachmentApi.bindAttachments("file", "mes_pd_document",
+ document.getId(), createReqVO.getBlobIds());
return document.getId();
}
@@ -50,12 +55,19 @@
// 鏇存柊
MesPdDocumentDO updateObj = BeanUtils.toBean(updateReqVO, MesPdDocumentDO.class);
documentMapper.updateById(updateObj);
+ if (updateReqVO.getBlobIds() != null) {
+ storageAttachmentApi.deleteAttachmentsByRecord("mes_pd_document", updateReqVO.getId());
+ storageAttachmentApi.bindAttachments("file", "mes_pd_document",
+ updateReqVO.getId(), updateReqVO.getBlobIds());
+ }
}
@Override
public void deleteDocument(Long id) {
// 鏍¢獙瀛樺湪
validateDocumentExists(id);
+ // 鍒犻櫎闄勪欢
+ storageAttachmentApi.deleteAttachmentsByRecord("mes_pd_document", id);
// 鍒犻櫎
documentMapper.deleteById(id);
}
@@ -77,6 +89,10 @@
@Override
public void deleteByProjectId(Long projectId) {
+ List<MesPdDocumentDO> documents = documentMapper.selectListByProjectId(projectId);
+ for (MesPdDocumentDO document : documents) {
+ storageAttachmentApi.deleteAttachmentsByRecord("mes_pd_document", document.getId());
+ }
documentMapper.deleteByProjectId(projectId);
}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkOrderProcessServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkOrderProcessServiceImpl.java
index e626b2e..9ee4e74 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkOrderProcessServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkOrderProcessServiceImpl.java
@@ -191,32 +191,27 @@
}
/**
- * 浠庡伐搴忓垪琛ㄤ腑鎻愬彇 BOM 鎶曟枡锛屽悎骞跺幓閲嶅悗淇濆瓨鍒板伐鍗� BOM 琛�
+ * 浠庡伐搴忓垪琛ㄤ腑鎻愬彇 BOM 鎶曟枡锛屾寜宸ュ簭鍒嗗埆淇濆瓨鍒板伐鍗� BOM 琛�
*/
private void saveWorkOrderBomFromProcesses(Long workOrderId, List<MesProWorkOrderProcessSaveReqVO> list) {
- // 鏀堕泦鎵�鏈夊伐搴忕殑 bomItems锛屾寜 itemId 鍘婚噸锛堜繚鐣欑涓�涓嚭鐜扮殑 quantity锛�
- java.util.Map<Long, MesProWorkOrderProcessSaveReqVO.BomItem> merged = new java.util.LinkedHashMap<>();
+ workOrderBomService.deleteWorkOrderBomByWorkOrderId(workOrderId);
+ List<MesProWorkOrderBomDO> bomList = new ArrayList<>();
for (MesProWorkOrderProcessSaveReqVO item : list) {
if (CollUtil.isNotEmpty(item.getBomItems())) {
for (MesProWorkOrderProcessSaveReqVO.BomItem bomItem : item.getBomItems()) {
if (bomItem.getItemId() != null) {
- merged.putIfAbsent(bomItem.getItemId(), bomItem);
+ bomList.add(MesProWorkOrderBomDO.builder()
+ .workOrderId(workOrderId)
+ .processId(item.getProcessId())
+ .itemId(bomItem.getItemId())
+ .quantity(bomItem.getQuantity())
+ .remark(bomItem.getRemark())
+ .build());
}
}
}
}
- // 鍒犻櫎鏃� BOM锛屾壒閲忔彃鍏ユ柊 BOM
- workOrderBomService.deleteWorkOrderBomByWorkOrderId(workOrderId);
- if (CollUtil.isNotEmpty(merged)) {
- List<MesProWorkOrderBomDO> bomList = new ArrayList<>(merged.size());
- for (MesProWorkOrderProcessSaveReqVO.BomItem bomItem : merged.values()) {
- bomList.add(MesProWorkOrderBomDO.builder()
- .workOrderId(workOrderId)
- .itemId(bomItem.getItemId())
- .quantity(bomItem.getQuantity())
- .remark(bomItem.getRemark())
- .build());
- }
+ if (CollUtil.isNotEmpty(bomList)) {
workOrderBomMapper.insertBatch(bomList);
}
}
diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/storage/StorageAttachmentApi.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/storage/StorageAttachmentApi.java
new file mode 100644
index 0000000..3d26f6e
--- /dev/null
+++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/storage/StorageAttachmentApi.java
@@ -0,0 +1,17 @@
+package cn.iocoder.yudao.module.system.api.storage;
+
+import cn.iocoder.yudao.module.system.api.storage.dto.StorageBlobRespDTO;
+
+import java.util.List;
+
+public interface StorageAttachmentApi {
+
+ void bindAttachments(String application, String recordType, Long recordId, List<Long> blobIds);
+
+ List<StorageBlobRespDTO> listAttachments(String recordType, Long recordId);
+
+ void deleteAttachments(List<Long> attachmentIds);
+
+ void deleteAttachmentsByRecord(String recordType, Long recordId);
+
+}
diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/storage/StorageAttachmentApiImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/storage/StorageAttachmentApiImpl.java
new file mode 100644
index 0000000..914c89d
--- /dev/null
+++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/storage/StorageAttachmentApiImpl.java
@@ -0,0 +1,62 @@
+package cn.iocoder.yudao.module.system.api.storage;
+
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.module.system.api.storage.dto.StorageBlobRespDTO;
+import cn.iocoder.yudao.module.system.controller.admin.storage.vo.SystemStorageAttachmentListReqVO;
+import cn.iocoder.yudao.module.system.controller.admin.storage.vo.SystemStorageAttachmentSaveReqVO;
+import cn.iocoder.yudao.module.system.controller.admin.storage.vo.SystemStorageBlobRespVO;
+import cn.iocoder.yudao.module.system.service.storage.SystemStorageAttachmentService;
+import jakarta.annotation.Resource;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Service
+public class StorageAttachmentApiImpl implements StorageAttachmentApi {
+
+ @Resource
+ private SystemStorageAttachmentService systemStorageAttachmentService;
+
+ @Override
+ public void bindAttachments(String application, String recordType, Long recordId, List<Long> blobIds) {
+ if (blobIds == null || blobIds.isEmpty()) {
+ return;
+ }
+ SystemStorageAttachmentSaveReqVO reqVO = new SystemStorageAttachmentSaveReqVO();
+ reqVO.setApplication(application != null ? application : "file");
+ reqVO.setRecordType(recordType);
+ reqVO.setRecordId(recordId);
+ List<SystemStorageAttachmentSaveReqVO.BlobItem> blobItems = new ArrayList<>();
+ for (Long blobId : blobIds) {
+ SystemStorageAttachmentSaveReqVO.BlobItem item = new SystemStorageAttachmentSaveReqVO.BlobItem();
+ item.setBlobId(blobId);
+ blobItems.add(item);
+ }
+ reqVO.setBlobItems(blobItems);
+ systemStorageAttachmentService.bindAttachments(reqVO);
+ }
+
+ @Override
+ public List<StorageBlobRespDTO> listAttachments(String recordType, Long recordId) {
+ SystemStorageAttachmentListReqVO reqVO = new SystemStorageAttachmentListReqVO();
+ reqVO.setRecordType(recordType);
+ reqVO.setRecordId(recordId);
+ List<SystemStorageBlobRespVO> list = systemStorageAttachmentService.listAttachments(reqVO);
+ return BeanUtils.toBean(list, StorageBlobRespDTO.class);
+ }
+
+ @Override
+ public void deleteAttachments(List<Long> attachmentIds) {
+ if (attachmentIds == null || attachmentIds.isEmpty()) {
+ return;
+ }
+ systemStorageAttachmentService.deleteAttachments(attachmentIds);
+ }
+
+ @Override
+ public void deleteAttachmentsByRecord(String recordType, Long recordId) {
+ systemStorageAttachmentService.deleteAttachmentsByRecordTypeAndRecordId(recordType, recordId);
+ }
+
+}
diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/storage/dto/StorageBlobRespDTO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/storage/dto/StorageBlobRespDTO.java
new file mode 100644
index 0000000..c0a5724
--- /dev/null
+++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/storage/dto/StorageBlobRespDTO.java
@@ -0,0 +1,16 @@
+package cn.iocoder.yudao.module.system.api.storage.dto;
+
+import lombok.Data;
+
+@Data
+public class StorageBlobRespDTO {
+
+ private Long id;
+ private String url;
+ private String previewURL;
+ private String name;
+ private String downloadURL;
+ private String application;
+ private Long storageAttachmentId;
+
+}
--
Gitblit v1.9.3