Files
PROJECT-CONTORL/openspec/changes/archive/2025-12-29-add-document-management/design.md
beabigegg 3108fe1dff feat: implement document management module
- Backend (FastAPI):
  - Attachment and AttachmentVersion models with migration
  - FileStorageService with SHA-256 checksum validation
  - File type validation (whitelist/blacklist)
  - Full CRUD API with version control support
  - Audit trail integration for upload/download/delete
  - Configurable upload directory and file size limit

- Frontend (React + Vite):
  - AttachmentUpload component with drag & drop
  - AttachmentList component with download/delete
  - TaskAttachments combined component
  - Attachments service for API calls

- Testing:
  - 12 tests for storage service and API endpoints

- OpenSpec:
  - add-document-management change archived

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-29 22:03:05 +08:00

160 lines
4.4 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.

## Context
文件管理是專案系統的核心功能,需要考慮:
- 檔案存儲策略(本地 vs NAS
- 安全需求(加密、浮水印)
- 版本控制邏輯
- 大檔案處理
## Goals / Non-Goals
**Goals:**
- 提供任務層級的檔案附件功能
- 支援基本 CRUD 操作
- 整合現有 Audit Trail
- 為未來 NAS 整合預留擴展性
**Non-Goals:**
- 即時協作編輯(不在此範圍)
- 全文搜尋(未來功能)
- 檔案預覽(未來功能)
## Decisions
### 1. 檔案存儲策略
**Decision:** 使用本地檔案系統 + 環境變數配置路徑
**Rationale:**
- 開發階段使用本地存儲簡化設置
- 生產環境透過環境變數指向 NAS 掛載點
- 路徑結構:`{UPLOAD_DIR}/{project_id}/{task_id}/{attachment_id}/{version}/`
**Alternatives considered:**
- 直接 NAS 整合 - 開發環境設置複雜
- S3 相容存儲 - 增加外部依賴
### 2. 版本控制模型
**Decision:** 主表 + 版本歷史表分離
```
pjctrl_attachments (主表,存儲最新版本資訊)
├── id, task_id, filename, current_version, ...
pjctrl_attachment_versions (歷史表)
├── id, attachment_id, version, file_path, ...
```
**Rationale:**
- 主表快速查詢當前附件
- 歷史表保留所有版本
- 上傳同名檔案 → 建立新版本 → 更新主表 current_version
### 3. 加密策略
**Decision:** 使用 Fernet (基於 AES-128-CBC) 對稱加密
**Rationale:**
- Python cryptography 庫內建支援
- 自動處理 IV、padding、HMAC 驗證
- 比原生 AES-256 更安全(防止實作錯誤)
- 效能足夠(非大規模加密場景)
**實作方式:**
- 加密金鑰存儲於環境變數 `ENCRYPTION_KEY`
- 僅對機密專案的附件加密
- 加密狀態存於 `is_encrypted` 欄位
### 4. 浮水印策略
**Decision:** 下載時動態生成浮水印
**Rationale:**
- 不修改原始檔案
- 每次下載包含當下使用者資訊
- 使用 Pillow (圖片) 和 PyMuPDF (PDF) 處理
**浮水印內容:**
- 使用者姓名 + 工號
- 下載時間
- 機密等級(如適用)
### 5. 檔案大小限制
**Decision:** 預設 50MB可透過環境變數調整
```python
MAX_FILE_SIZE = int(os.getenv("MAX_FILE_SIZE_MB", 50)) * 1024 * 1024
```
**Rationale:**
- 避免記憶體溢出
- 大檔案使用串流處理
- 生產環境可依需求調整
## Data Model
```sql
-- 附件主表
pjctrl_attachments
├── id: UUID (PK)
├── task_id: UUID (FK -> tasks)
├── filename: VARCHAR(255) -- 顯示名稱
├── original_filename: VARCHAR(255) -- 原始上傳名稱
├── mime_type: VARCHAR(100)
├── file_size: BIGINT
├── current_version: INT DEFAULT 1
├── is_encrypted: BOOLEAN DEFAULT false
├── uploaded_by: UUID (FK -> users)
├── is_deleted: BOOLEAN DEFAULT false
├── created_at: TIMESTAMP
└── updated_at: TIMESTAMP
-- 版本歷史表
pjctrl_attachment_versions
├── id: UUID (PK)
├── attachment_id: UUID (FK -> attachments)
├── version: INT
├── file_path: VARCHAR(1000) -- 實際存儲路徑
├── file_size: BIGINT
├── checksum: VARCHAR(64) -- SHA-256
├── uploaded_by: UUID (FK -> users)
├── created_at: TIMESTAMP
└── INDEX (attachment_id, version)
```
## API Design
```
POST /api/tasks/{task_id}/attachments # 上傳附件
GET /api/tasks/{task_id}/attachments # 列出附件
GET /api/attachments/{id} # 取得附件資訊
GET /api/attachments/{id}/download # 下載附件
GET /api/attachments/{id}/download?version=2 # 下載特定版本
DELETE /api/attachments/{id} # 刪除附件(軟刪除)
GET /api/attachments/{id}/versions # 版本歷史
POST /api/attachments/{id}/restore/{version} # 回復特定版本
```
## Risks / Trade-offs
| Risk | Mitigation |
|------|------------|
| 大檔案記憶體溢出 | 使用串流上傳/下載 |
| 加密金鑰洩漏 | 僅存於環境變數,定期輪換 |
| 浮水印處理耗時 | 限制支援的檔案類型,非同步處理大檔案 |
| NAS 不可用 | 本地存儲 fallback監控告警 |
## Migration Plan
1. Phase 1: 建立模型、基本 CRUD、本地存儲
2. Phase 2: 版本控制
3. Phase 3: 加密與浮水印(可選)
## Open Questions
- [ ] NAS 掛載點路徑確認
- [ ] 生產環境加密金鑰管理方式KMS? Vault?
- [ ] 是否需要支援拖放上傳多檔案?