2026-05-09 11:30:34 +08:00
|
|
|
|
import { SYSTEM_SERVICE_PREFIX } from '@/constants/service';
|
|
|
|
|
|
import { request } from '../request';
|
2026-05-15 10:06:51 +08:00
|
|
|
|
import { type ServiceRequestResult, mapServiceResult } from './shared';
|
2026-05-09 11:30:34 +08:00
|
|
|
|
|
|
|
|
|
|
const FILE_PREFIX = `${SYSTEM_SERVICE_PREFIX}/file`;
|
|
|
|
|
|
|
2026-05-15 10:06:51 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 拼接文件永久代理路径,用于富文本 <img src>。
|
|
|
|
|
|
*
|
|
|
|
|
|
* 后端 GET 接口匿名访问、Content-Disposition: inline,私有桶下也不会过期。
|
|
|
|
|
|
* 调用方拿到上传响应里的 configId + path 后直接调用本函数得到可写入 HTML 的 url。
|
|
|
|
|
|
*/
|
|
|
|
|
|
export function buildFileProxyUrl(configId: string, path: string) {
|
|
|
|
|
|
return `${FILE_PREFIX}/${configId}/get/${encodeURI(path)}`;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-12 21:41:39 +08:00
|
|
|
|
export interface UploadFileResult {
|
|
|
|
|
|
/** infra_file.id 的字符串形式(避免 Long 精度丢失) */
|
|
|
|
|
|
id: string;
|
2026-05-15 10:06:51 +08:00
|
|
|
|
/** 对象存储配置编号(字符串形式),与 path 一起拼接永久代理路径 */
|
|
|
|
|
|
configId: string;
|
|
|
|
|
|
/** 文件相对路径(含日期目录、文件名),与 configId 一起拼接永久代理路径 */
|
|
|
|
|
|
path: string;
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 文件访问 URL:私有桶带签名(24h 过期)、公开桶裸 URL。
|
|
|
|
|
|
* ⚠️ 仅供后端调试 / 历史兼容,禁止写进富文本 <img src> —— 会随签名过期导致回显失效。
|
|
|
|
|
|
* 富文本图片请用 buildFileProxyUrl(configId, path) 的返回值。
|
|
|
|
|
|
*/
|
2026-05-12 21:41:39 +08:00
|
|
|
|
url: string;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-15 10:06:51 +08:00
|
|
|
|
type UploadFileResponse = {
|
|
|
|
|
|
id: string | number;
|
|
|
|
|
|
configId: string | number;
|
|
|
|
|
|
path: string;
|
|
|
|
|
|
url: string;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-05-09 11:30:34 +08:00
|
|
|
|
/** 上传文件(模式一:后端中转) */
|
2026-05-15 10:06:51 +08:00
|
|
|
|
export async function uploadFile(file: File, directory?: string) {
|
2026-05-09 11:30:34 +08:00
|
|
|
|
const formData = new FormData();
|
|
|
|
|
|
formData.append('file', file);
|
|
|
|
|
|
if (directory) {
|
|
|
|
|
|
formData.append('directory', directory);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-15 10:06:51 +08:00
|
|
|
|
const result = await request<UploadFileResponse>({
|
2026-05-09 11:30:34 +08:00
|
|
|
|
url: `${FILE_PREFIX}/upload`,
|
|
|
|
|
|
method: 'post',
|
|
|
|
|
|
data: formData
|
|
|
|
|
|
});
|
2026-05-15 10:06:51 +08:00
|
|
|
|
|
|
|
|
|
|
return mapServiceResult(result as ServiceRequestResult<UploadFileResponse>, data => ({
|
|
|
|
|
|
id: String(data.id),
|
|
|
|
|
|
configId: String(data.configId),
|
|
|
|
|
|
path: data.path,
|
|
|
|
|
|
url: data.url
|
|
|
|
|
|
}));
|
2026-05-09 11:30:34 +08:00
|
|
|
|
}
|
2026-05-12 21:41:39 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 删除文件
|
|
|
|
|
|
*
|
|
|
|
|
|
* 业务表单"取消/关闭/标记删除"场景调用本接口清理孤儿文件。
|
|
|
|
|
|
* 删除已不存在的文件(后端返回错误码 `1001003001`)应由调用方视为成功并吞掉。
|
|
|
|
|
|
*/
|
|
|
|
|
|
export function deleteFile(id: string) {
|
|
|
|
|
|
return request<boolean>({
|
|
|
|
|
|
url: `${FILE_PREFIX}/delete`,
|
|
|
|
|
|
method: 'delete',
|
|
|
|
|
|
params: { id }
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 下载文件(流)
|
|
|
|
|
|
*
|
|
|
|
|
|
* 走后端代理接口 `/system/file/download?id=xxx`,由后端读取对象存储并以字节流返回。
|
|
|
|
|
|
* 私有桶下不要直接打开 `infra_file.url`,签名地址会过期。
|
|
|
|
|
|
*/
|
|
|
|
|
|
export function downloadFile(id: string) {
|
|
|
|
|
|
return request<Blob, 'blob'>({
|
|
|
|
|
|
url: `${FILE_PREFIX}/download`,
|
|
|
|
|
|
method: 'get',
|
|
|
|
|
|
params: { id },
|
|
|
|
|
|
responseType: 'blob'
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|