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
---
docs/file_upload_guide.md | 324 +++++++++++++++++++++---------------------------------
1 files changed, 126 insertions(+), 198 deletions(-)
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`
--
Gitblit v1.9.3