feat(review): 重构项目级单方案验收协作

This commit is contained in:
yuzhe
2026-07-22 16:41:03 +08:00
parent 6091d61612
commit e7e268d4eb
45 changed files with 1830 additions and 812 deletions

View File

@@ -2,83 +2,72 @@
## 系统边界
Delivery Desk 是单体 Web 应用:React 前端调用 Express API,前后端共享 `shared/types.ts` 类型。开发环境使用 SQLite 和本地上传目录正式环境使用 PostgreSQL 和腾讯云 COS。
Delivery Desk 是 React + Express 单体应用,前后端共享 `shared/types.ts`。开发环境使用 SQLite 和本地上传目录正式环境使用 PostgreSQL 和腾讯云 COS。
```mermaid
flowchart LR
browser["浏览器"] --> app["Express / React 应用"]
app --> database["SQLite 或 PostgreSQL"]
app --> local["本地 uploads开发"]
app --> cos["腾讯云 COS(正式)"]
customer["客户验收链接"] --> app
client["外部 API 客户端"] --> app
browser["工作台 / 客户浏览器"] --> app["Express + React"]
api["外部 API 客户端"] --> app
app --> db["SQLite / PostgreSQL"]
app --> storage["本地 uploads / 腾讯云 COS"]
```
## 业务层级
## 产品层级
```text
运营组
└── 项目
└── 作品交付集
└── 作品
└── 验收轮次
└── 候选稿15 个)
└── 作品
└── 验收轮次(每轮一个方案)
```
- 一个运营组只能有一位组管理员,可以有多位光影叙事
- 平台管理员可以有多位,不属于固定运营组
- 普通工作台账号只能读写所属运营组的数据;平台管理员可跨组管理
- 客户会话只绑定一个项目,不能跨项目浏览
- 平台级 API Key 可创建项目;项目级 API Key 只能操作指定项目
## 运行结构
- `src/`React 页面、组件、状态和 API 客户端。
- `api/routes/`HTTP 路由与输入校验。
- `api/services/`:作品、存储等业务编排。
- `api/repositories/`:查询封装。
- `api/database.ts`SQLite/PostgreSQL 统一查询接口和事务。
- `api/db.ts`SQLite 初始化及增量迁移。
- `db/postgres/schema.sql`PostgreSQL 当前完整 schema。
- `shared/types.ts`:前后端共享领域类型。
`DATABASE_URL` 存在时使用 PostgreSQL否则使用 SQLite。两套数据库必须保持相同业务约束涉及表或字段的修改必须同时更新 `api/db.ts``db/postgres/schema.sql` 及迁移验证脚本。
- 客户会话绑定项目,不能跨项目访问
- 项目级 API Key 只能访问绑定项目;平台级 Key 可跨组管理项目
- 历史作品交付集不再是产品层级。`collections` 表仅作为旧数据和旧 URL 的迁移兼容容器
- `work_versions` 继续保存每轮内容快照,但与 `review_rounds` 强制一对一
- 升级时如检测到旧的一轮多方案数据,会把额外方案拆成只读的独立历史轮次,保留图片、批注、验收事件和当前活动方案,再建立一轮一方案唯一约束
## 主要数据表
| 表 | 用途 |
|---|---|
| `operation_groups` | 运营组及启停状态 |
| `users` / `sessions` | 工作台账号、角色和登录会话 |
| `customer_sessions` | 客户项目级验收会话 |
| `projects` | 项目、客户访问密码和访问期限 |
| `collections` | 项目下的作品交付集 |
| `notes` | 作品当前状态、活动轮次和选中稿 |
| `review_rounds` | 验收轮次完成状态和选中候选稿 |
| `work_versions` | 各候选稿的标题、正文、标签和验收状态快照 |
| `images` | 候选稿图片、顺序存储提供方和对象 Key |
| `operation_groups` | 运营组及状态 |
| `users` / `sessions` | 工作台账号、角色和会话 |
| `customer_sessions` | 项目级客户会话 |
| `projects` | 项目、客户访问配置和自动验收状态 |
| `collections` | 迁移期内部兼容容器,不属于产品层级 |
| `notes` | 作品当前状态、活动轮次和项目归属 |
| `review_rounds` | 验收轮次完成原因 |
| `work_versions` | 单轮内容快照;每轮恰好一条 |
| `images` | 轮次图片、顺序存储信息 |
| `annotations` | 图片坐标批注 |
| `text_annotations` | 标题正文的版本级批注 |
| `work_comments` | 作品总体反馈与回复 |
| `review_events` | 提交、修、通过重新打开等验收记录 |
| `api_keys` | 平台级项目级 API Key 哈希与状态 |
| `storage_configs` | 加密后的 COS 配置及启用状态 |
| `audit_logs` | 管理业务操作审计 |
| `text_annotations` | 标题正文和 Tag 选区批注与文本上下文 |
| `work_comments` | 作品总体反馈 |
| `review_events` | 提交、退修、通过重新打开记录 |
| `api_keys` | 平台级/项目级 API Key 哈希 |
| `storage_configs` | 加密后的 COS 配置 |
| `audit_logs` | 管理业务审计 |
## 存储流程
## 状态计算
平台管理员在管理页新增 COS 配置。SecretId 和 SecretKey 使用 `COS_CONFIG_ENCRYPTION_KEY` 派生的 AES-256-GCM 密钥加密后写入数据库,读取配置的接口不会返回明文
作品状态为 `draft``pending``changes_requested``approved`。只有活动轮次可新增批注和作出验收决定;新轮次会锁定旧轮次。客户通过活动轮次后作品为已通过,退修后运营通过新轮次提交修改
启用配置前会在目标桶的 `.delivery-desk-check/` 路径依次上传、读取并删除一个临时对象。启用后,新上传文件写入
项目验收状态自动计算
```text
<path-prefix>/originals/YYYY/MM/<uuid>.<ext>
```
- 没有非草稿作品:`draft`
- 存在未通过作品:`reviewing`
- 所有非草稿作品通过:`completed`
- 人工归档:`archived`
未启用 COS 时,上传文件保存在本地 `uploads/`。图片 URL 按产品约定为公开随机地址,不提供对象级访问鉴权
完成项目为只读。新增作品、创建新轮次或由管理员重新打开作品时,项目恢复为验收中;已关闭或归档项目始终只读。开放反馈不会阻止通过;通过时仍为开放的反馈会标记为随该轮验收关闭,历史内容保留
## 验收状态
## 批注模型
作品状态为 `draft``pending``changes_requested``approved`。一个验收轮次可包含 15 个候选稿;单稿退修时,其他待验收稿仍可继续验收。客户选中并通过任意一稿后,作品即通过,同轮其他稿标记为未选用,历史轮次只读。已通过作品只能由平台管理员或所属组管理员填写原因后重新打开,历史事件保留
- 作品缩略图只展示现有坐标标记,不能新增坐标批注;点击标记会联动打开验收协作面板中的对应反馈
- 点击图片打开悬浮图片窗格;只有该窗格可以新增坐标批注,并支持原图查看、缩放和前后切换。点击窗格外会同时关闭图片窗格和验收协作面板。
- 标题、正文和 Tag 批注保存 `start_offset``end_offset``selected_text` 及前后文,提交时校验选区仍与轮次快照一致。
- `GET /api/works/:workId/annotations` 按轮次返回图片批注、文字批注、总体反馈和验收事件。
作品交付集状态由其中非草稿作品自动计算:没有已提交作品时为 `draft`,存在未通过作品时为 `reviewing`,全部已提交作品通过时为 `completed``archived` 是人工状态,自动计算不会覆盖。完成后客户页面只读;新增作品、新验收轮次或重新打开作品会自动恢复为验收中。
## 存储
平台管理员可在管理页保存和测试 COS 配置。SecretId/SecretKey 使用 `COS_CONFIG_ENCRYPTION_KEY` 派生的 AES-256-GCM 密钥加密,读取接口不返回明文。连接测试会上传、读取并删除临时对象。外部 API 提供的公开图片 URL 只保存地址,不下载、不转存 COS。