- 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
197 lines
5.4 KiB
Markdown
197 lines
5.4 KiB
Markdown
# 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 助手 + 用户
|
||
- 审核:用户
|
||
|
||
|
||
|
||
|
||
|
||
|