# 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` 响应头,让浏览器下载时恢复原始文件名: ```typescript 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. 环境变量配置 ```bash # 存储类型: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 助手 + 用户 - 审核:用户