Files
AIclinicalresearch/docs/01-平台基础层/02-存储服务/OSS存储架构规划与最佳实践 V5.md

148 lines
5.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# **OSS 存储架构规划与最佳实践 (V5.0)**
**文档状态:** 已发布 (v5.0 \- 极简生存版)
**适用项目:** AIclinicalresearch
**核心原则:** **不做过早优化,只做必要隔离**
**最后更新:** 2026-01-22
## **1\. 架构核心决策 (The MVP Way)**
我们摒弃一切复杂的云原生概念,怎么简单怎么来。
1. **上传方式**:全线 **后端流式上传** (stream.pipeline)。前端只负责 Form Data 提交。
2. **权限管理**SAE 实例绑定 **AliyunOSSFullAccess**。不搞自定义策略。
3. **成本策略****全标准存储**。不搞分层,不搞归档,直到月账单超过 100 元。
## **2\. Bucket 物理规划**
为了**安全底线**(防止生产数据被误删、防止患者数据裸奔),我们保留 **4 个 Bucket** 的设计。这是唯一的坚持。
| 环境 | Bucket 名称 (示例) | 权限 (ACL) | 说明 |
| :---- | :---- | :---- | :---- |
| **生产** | **ai-clinical-data** | **私有** | 核心数据。**仅配置跨域(CORS)用于GET**。 |
| **生产** | **ai-clinical-static** | **公共读** | 头像、Logo、RAG切片图。**配置跨域(CORS)**。 |
| **开发** | **ai-clinical-data-dev** | **私有** | 开发测试用。 |
| **开发** | **ai-clinical-static-dev** | **公共读** | 开发测试用。 |
**配置项 (仅需配这 2 项)**
1. **CORS**:来源 \*,允许 Methods GET。
2. **生命周期**:仅针对 temp/ 目录设置 1 天删除。其他不用管。
## **3\. 极简目录结构 (Flat & Simple)**
基于**全员租户化**的管理逻辑,所有数据根目录统一为 tenants/。
后端生成 Key 的逻辑统一为tenants/{TenantID}/{Scope}/{Module}/{UUID}.{ext}
### **3.1 核心数据 Bucket (ai-clinical-data)**
\# 1\. 用户私有数据 (User-Level Data)
\# 逻辑:归属于特定租户下的特定用户
\# 结构: tenants/{tenantId}/users/{userId}/{module}/{uuid}.pdf
tenants/t999/users/u123/pkb/a1b2c3d4.pdf \# PKB 文献 (个人)
tenants/t999/users/u123/asl/e5f6g7h8.pdf \# ASL 文献 (个人)
tenants/t999/users/u123/rvw/i9j0k1l2.docx \# RVW 稿件 (个人)
tenants/t999/users/u123/ssa/m3n4o5p6.xlsx \# 统计数据 (个人)
\# 2\. 租户共享数据 (Tenant-Level Shared Data)
\# 逻辑:归属于租户,通常由管理员上传或全员共享
\# 结构: tenants/{tenantId}/shared/{module}/{uuid}.pdf
tenants/t999/shared/ekb/q7r8s9t0.pdf \# 租户知识库 (EKB)
tenants/t999/shared/emr/u1v2w3x4.json \# 原始病历数据 (EMR)
\# 3\. 临时中转区 (唯一配置生命周期的目录)
\# 结构: temp/{date}/{uuid}.xlsx
temp/20260122/y5z6a7b8.xlsx \# Tool C 上传 / ASL 导入 / 临时导出
\# (OSS配置: 1天后自动删除)
\# 4\. 系统级文件
system/templates/gcp\_guide.pdf \# 系统模板
### **3.2 静态资源 Bucket (ai-clinical-static)**
\# 1\. RAG 切片图 (混淆文件名)
\# 结构: rag/{uuid}.png
rag/f9e8d7c6b5a4.png
\# 2\. 头像
avatars/u123.png
## **4\. 后端实现指南 (Node.js)**
### **4.1 环境变量 (SAE)**
不需要复杂的 AK/SK 管理,直接用 SAE 的 RAM 角色(需绑定 AliyunOSSFullAccess
\# 只有这 3 个变量是必须的
OSS\_REGION=oss-cn-beijing
OSS\_ENDPOINT=oss-cn-beijing-internal.aliyuncs.com \# 内网地址,省流量费
OSS\_BUCKET\_DATA=ai-clinical-data
### **4.2 核心代码 (Common Service)**
不要过度封装,一个简单的 StorageService 足矣。
// common/storage/storage.service.ts
import OSS from 'ali-oss';
import { pipeline } from 'stream/promises';
// 使用 STS 或 SAE 自动注入的凭证,或者直接配 AK/SK (MVP阶段最简单)
const client \= new OSS({
region: process.env.OSS\_REGION,
accessKeyId: process.env.OSS\_ACCESS\_KEY\_ID, // 简单起见MVP先用 AK
accessKeySecret: process.env.OSS\_ACCESS\_KEY\_SECRET,
bucket: process.env.OSS\_BUCKET\_DATA,
internal: true, // 开启内网模式
});
export const StorageService \= {
// 1\. 流式上传 (核心方法)
async uploadStream(key: string, stream: any) {
// usePutObject 接口支持 stream
return await client.putStream(key, stream);
},
// 2\. 获取临时链接 (私有文件预览)
getSignedUrl(key: string) {
return client.signatureUrl(key, { expires: 3600 });
},
// 3\. 删除文件
async delete(key: string) {
return await client.delete(key);
}
};
### **4.3 业务调用范例**
// Tool C 上传接口
fastify.post('/upload', async (req, reply) \=\> {
const data \= await req.file();
// 极简 Key 生成
const key \= \`temp/${Date.now()}/${data.filename}\`;
// 一行代码上传,无需 try-catch 复杂逻辑 (全局 Error Handler 会捕获)
await StorageService.uploadStream(key, data.file);
return { url: key }; // 返回 Key 即可,前端不需要知道真实 URL
});
## **5\. 2人团队的“生死红线”**
在极简模式下,只有这 3 条规则必须死守,其他都可以妥协:
1. **内网连接**SAE 必须配 \-internal 的 Endpoint。一旦配成公网上传大文件会卡死且扣费。
2. **私有存储**ai-clinical-data 必须是 **Private**。这是医疗底线,不能为了省代码而裸奔。
3. **流式处理**:代码里必须用 pipe / putStream。严禁 await file.toBuffer(),否则 2 人的小服务器一定会内存溢出崩掉。
## **6\. 成本预警 (Cost Watch)**
MVP 阶段不配置复杂的归档策略,但需关注一条警戒线:
* **警戒线**:当 OSS 月账单超过 **100 元** (约 500GB 存储) 时。
* **行动**:此时再去研究“生命周期管理”和“归档存储”。在此之前,专注于业务代码。