Files
AIclinicalresearch/docs/01-平台基础层/02-存储服务/OSS集成开发记录-2026-01-22.md

5.4 KiB
Raw Blame History

OSS 集成开发记录

日期2026-01-22 版本v1.0 状态MVP 阶段完成


📋 开发目标

将阿里云 OSS 集成到平台中,替代本地文件存储,为 PKB个人知识库等业务模块提供可靠的云端文件持久化能力。


完成的工作

1. 基础设施层

1.1 OSS Bucket 创建

Bucket 名称 用途 权限 加密
ai-clinical-data 生产环境数据存储 私有 SSE-OSS
ai-clinical-data-dev 开发环境数据存储 私有
ai-clinical-static 生产环境静态资源 公共读
ai-clinical-static-dev 开发环境静态资源 公共读

1.2 RAM 账号配置

  • 创建专用 RAM 用户:aiclinical-oss
  • 配置 AccessKey ID/Secret
  • 授权 OSS 操作权限

2. 后端存储适配器

2.1 架构设计

StorageAdapter (接口)
    ├── OSSAdapter      (阿里云 OSS 实现)
    └── LocalAdapter    (本地文件系统实现)

StorageFactory (工厂类)
    └── getInstance()   根据 STORAGE_TYPE 返回对应适配器

2.2 核心文件

文件 说明
src/common/storage/StorageAdapter.ts 存储适配器接口定义
src/common/storage/OSSAdapter.ts OSS 实现上传、下载、删除、签名URL
src/common/storage/LocalAdapter.ts 本地存储实现
src/common/storage/StorageFactory.ts 工厂类,根据环境变量创建实例
src/common/storage/index.ts 统一导出

2.3 关键功能

  • 上传文件storage.upload(key, buffer)
  • 下载文件storage.download(key)
  • 删除文件storage.delete(key)
  • 获取签名URLstorage.getSignedUrl(key, expires, originalFilename)
  • 检查文件存在storage.exists(key)

2.4 签名URL与原始文件名

通过 Content-Disposition 响应头,让浏览器下载时恢复原始文件名:

getSignedUrl(key: string, expires: number = 900, originalFilename?: string): string {
  const options: OSS.SignatureUrlOptions = { expires };
  if (originalFilename) {
    options.response = {
      'content-disposition': `attachment; filename="${encodeURIComponent(originalFilename)}"`
    };
  }
  return this.client.signatureUrl(key, options);
}

3. PKB 模块集成

3.1 目录结构规范

tenants/{tenantId}/users/{userId}/pkb/{kbId}/{uuid}.{ext}

示例:

tenants/yizhengxun/users/user-001/pkb/kb-001/9f206cc1c1ac4478.pdf

3.2 代码修改

documentController.ts

  • 新增 generatePkbStorageKey() 函数生成存储路径
  • 上传流程:先上传到 OSS再调用 documentService

documentService.ts

  • uploadDocument() 接收 storageKey 参数并存储
  • deleteDocument() 删除 OSS 文件

3.3 数据库字段重命名

原字段名 新字段名 说明
difyDocumentId storageKey 存储 OSS 路径(数据库列名保持 dify_document_id 以避免迁移)

4. 环境变量配置

# 存储类型oss 或 local
STORAGE_TYPE=oss

# OSS 配置
OSS_REGION=oss-cn-beijing
OSS_BUCKET=ai-clinical-data-dev
OSS_BUCKET_STATIC=ai-clinical-static-dev
OSS_ACCESS_KEY_ID=LTAI5tBHkL39GjdLfcr77Y3f
OSS_ACCESS_KEY_SECRET=********
OSS_INTERNAL=false  # 本地开发用公网,生产用内网

5. 文档产出

文档 路径
MVP 实施方案 docs/01-平台基础层/02-存储服务/OSS存储实施方案-MVP版.md
开发规范 docs/04-开发规范/11-OSS存储开发规范.md
账号配置信息 docs/01-平台基础层/02-存储服务/OSS账号与配置信息.md

🧪 测试结果

OSS 适配器测试

✅ 上传成功
✅ 文件存在检查
✅ 下载成功(内容匹配)
✅ 签名URL生成含原始文件名
✅ 删除成功

PKB 文档上传测试

✅ 文件上传到 OSS
✅ 数据库记录 storageKey
⚠️ 删除文档时遇到 pg-boss 队列冲突(与 OSS 无关)

🔧 待解决问题

问题 优先级 说明
pg-boss 队列重复初始化 导致服务启动报错
删除文档功能验证 需解决 pg-boss 后继续测试
前端文件预览/下载 需实现签名URL获取接口
Tool C / RVW / ASL 集成 按实施方案逐步推进

📊 技术决策记录

决策 1文件命名使用 UUID

  • 原因:防止文件名冲突、避免特殊字符问题、增加安全性
  • 方案:上传时生成 UUID 作为 OSS Key原始文件名存数据库

决策 2保留 local 存储模式

  • 原因:支持医疗机构私有化部署(数据不出内网)
  • 方案:通过 STORAGE_TYPE 环境变量切换

决策 3复用 difyDocumentId 字段

  • 原因Dify 已移除,该字段可复用存储 OSS 路径
  • 方案Prisma 字段重命名为 storageKey@map 保持原列名

决策 4文件大小限制 30MB

  • 原因:平衡用户体验和服务器内存安全
  • 方案30MB 以下用 toBuffer(),超大文件考虑流式处理

📅 后续计划

  1. Phase 2.1:完成 PKB 删除功能验证
  2. Phase 2.2:实现前端文件预览/下载签名URL
  3. Phase 3:集成 Tool C、RVW、ASL 模块
  4. Phase 4:生产环境部署与内网 Endpoint 配置

👥 参与人员

  • 开发AI 助手 + 用户
  • 审核:用户