- Add one-click research protocol generation with streaming output - Implement Word document export via Pandoc integration - Add dynamic dual-panel layout with resizable split pane - Implement collapsible content for StatePanel stages - Add conversation history management with title auto-update - Fix scroll behavior, markdown rendering, and UI layout issues - Simplify conversation creation logic for reliability
5.4 KiB
5.4 KiB
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) - 获取签名URL:
storage.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(),超大文件考虑流式处理
📅 后续计划
- Phase 2.1:完成 PKB 删除功能验证
- Phase 2.2:实现前端文件预览/下载(签名URL)
- Phase 3:集成 Tool C、RVW、ASL 模块
- Phase 4:生产环境部署与内网 Endpoint 配置
👥 参与人员
- 开发:AI 助手 + 用户
- 审核:用户