Files
quyun-v2/backend/specs/spec01-backlog.md

172 lines
7.9 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.
# Spec01 可执行 Backlog按接口/表/状态机/验收用例)
本文从 `backend/specs/spec01-gap-analysis.md` 的“差异点/未实现项”拆解为可落地的 backlog。每条尽量可独立开发、可验收、可回滚。
## 0. 约定(用于所有条目)
- **P0/P1/P2**优先级从高到低P0=阻塞核心目标P1=重要增强P2=可延后。
- **验收方式**默认用“service 测试 + http 层冒烟”覆盖;涉及对象存储/转码的条目允许先用 mock/本地 minio 方案。
- **租户隔离硬约束**:任何带 `id` 的资源访问都必须校验 `tenant_id` 边界。
## Epic A公开内容/游客访问(当前未落地)
当前 `/t/:tenantCode/v1/*` 默认强制“登录 + 必须是租户成员”,不满足 spec01 的“游客可浏览公开内容(若允许)”。
### A1P0, Middleware/API增加“公开读接口路由组”
- **新增路由组**(建议二选一):
1) `GET /t/:tenantCode/v1/public/contents`(公开列表)
2) `GET /t/:tenantCode/v1/public/contents/:contentID`(公开详情)
3) `GET /t/:tenantCode/v1/public/contents/:contentID/preview`(公开试看资源)
- **中间件策略**
-`TenantResolve`
- 可选 `TenantOptionalAuth`:有 token 则解析写 ctx无 token 允许继续(用于展示“已购/作者/已登录”差异)。
- **响应语义**
- 仅返回 `visibility=public``status=published` 的内容;
- `HasAccess` 在公开接口里定义为:`free || owner || purchased`(若无登录则恒为 false除非 free=真)。
- **验收用例**
- 未登录可拉取公开内容列表/详情/preview
- 未登录访问 `visibility=tenant_only/private` 返回权限错误或 404按统一策略定
- 已登录但非成员:公开内容可读;非公开内容不可读;购买/余额接口不可用。
### A2P1, State/Rule明确 `visibility` 与主资源访问关系
- **规则固化**(写入接口文档 + tests
- `visibility` 只控制“内容详情是否可见”主资源main role仍需 `free/owner/purchased`
- `public + free` 是否允许游客看正片:需要业务明确(建议允许,减少“公开但看不了”的困惑)。
- **验收用例**
- `public + price=0`:游客能拿到 main assets
- `public + price>0`:游客不能拿到 main assets但能拿到 preview assets。
## Epic BMediaAsset 上传/处理全链路(当前缺失)
已有 `media_assets``content_assets` 表,但缺少“上传→处理→对外下发”的闭环接口与状态机。
### B1P0, API上传初始化申请上传凭证/直传参数
- **新增接口**tenant_admin
- `POST /t/:tenantCode/v1/admin/media_assets/upload_init`
- **请求字段**
- `type`video/audio/image
- `content_type`mime可选
- `file_size`(可选,用于限额)
- `sha256`(可选,用于去重/审计)
- **返回字段**(按存储 provider 定):
- `asset_id`
- `upload_url` / `form_fields`S3 POST policy/ `headers`
- `expires_at`
- **DB**
- 创建 `media_assets(status=uploaded, provider/bucket/object_key/meta)`
- `object_key` 由后端生成,避免客户端指定路径。
- **验收用例**
- tenant_admin 调用成功返回可用上传信息;
- member/非 admin 调用被拒绝;
- asset 必须绑定正确 `tenant_id/user_id`
### B2P0, API/State上传完成回调触发处理并进入 processing
- **新增接口**tenant_admin 或 system
- `POST /t/:tenantCode/v1/admin/media_assets/:assetID/upload_complete`
- **行为**
- 校验 `asset.status=uploaded`
- 写入必要 metaduration/width/height 可后置);
- 状态迁移:`uploaded -> processing`
- 触发异步处理(先允许 stub写入任务表或发消息
- **验收用例**
- 重复调用幂等(第二次返回同一结果,不重复触发任务);
- 非法状态迁移返回明确错误码status conflict
### B3P1, API查询资源详情与列表
- **新增接口**tenant_admin
- `GET /t/:tenantCode/v1/admin/media_assets`
- `GET /t/:tenantCode/v1/admin/media_assets/:assetID`
- **查询字段**
- `status/type/created_at` 过滤;分页。
- **验收用例**
- 只能查本租户资源;
- deleted_at 过滤策略一致(默认不返回已删除)。
### B4P1, State Machine固化 media_assets 状态机与允许迁移
- **状态集合**`uploaded/processing/ready/failed/deleted`
- **允许迁移**
- uploaded → processing
- processing → ready | failed
- ready/failed → deleted软删
- **验收用例**
- 任意越权迁移失败;
- ready 才允许绑定到 `content_assets`(见 Epic C
## Epic C资源下发与防直链当前为“返回对象信息”未形成安全下发
目前内容资源接口返回 `models.MediaAsset`,可能包含 `bucket/object_key` 等内部定位信息spec01 希望通过“短时效播放凭证/签名 URL/token”下发并且 preview 与 main 资源彻底区分。
### C1P0, API/DTO资源下发改为“签名 URL/Token”响应
- **调整接口返回 DTO**
- `GET /t/:tenantCode/v1/contents/:contentID/preview`
- `GET /t/:tenantCode/v1/contents/:contentID/assets`
- **响应字段建议**
- `asset_id`
- `type`
- `play_url`(短时效)
- `expires_at`
- `meta`(可展示字段的白名单,如 duration/width/height
- **实现要点**
- 后端对 providerminio/s3/oss生成签名 URL
- 绝不返回可长期复用的直链或裸 object_key除非配置允许且仅内网
- **验收用例**
- 返回的 URL 具备过期时间;
- 无权限时不返回任何可用播放地址;
- 日志/审计中记录 tenant_id/content_id/user_id/role/asset_id。
### C2P1, Rule/Validation校验 preview 必须独立产物
- **约束**(二选一落库方式):
1) `media_assets.meta.variant=preview/main`(或 `is_preview`
2) 新增列 `media_assets.variant`(枚举)
- **绑定校验**
- `content_assets.role=preview` 只能绑定 `variant=preview`
- `role=main` 只能绑定 `variant=main`
- **验收用例**
- 用 main 资源绑定 preview 被拒绝;
- preview 秒数只对 preview 下发生效。
> 备注(已选定实现方式):本项目采用 **新增列 `media_assets.variant`**,并对取值做 CHECK 约束main/preview
## Epic D异步退款/风控预留(当前 `refunding` 未使用)
### D1P2, State Machine引入 `refunding` 并定义状态迁移
- **订单状态机补齐**
- paid → refunding → refunded | failed
- **接口语义**
- `POST refund` 返回 `refunding`
- 单独的 job/worker 完成 `credit_refund + revoke access + status->refunded`
- **验收用例**
- 重复退款请求幂等;
- refunding 期间不得重复扣款/重复回收权益;
- 失败可重试(明确重试幂等键策略)。
## Epic E审计字段结构化操作者/业务引用结构化)
### E1P1, DB/APItenant_ledgers 增加操作者字段与业务引用字段
- **DB 变更**(建议):
- `tenant_ledgers.operator_user_id bigint NULL`
- `tenant_ledgers.biz_ref_type varchar(32) NULL`order/refund/etc
- `tenant_ledgers.biz_ref_id bigint NULL`
-`(tenant_id, biz_ref_type, biz_ref_id, type)` 做唯一约束(或与 idempotency_key 二选一作为主幂等源)。
- **验收用例**
- 购买/退款/调账等敏感 ledger 必须写入 operator_user_idadmin/buyer/system
- 后台可按 operator_user_id 检索敏感操作流水。
## 1. 建议交付顺序(最小闭环)
1) A1 → A2先把公开读能力与语义定死
2) B1 → B2 → B4上传/处理状态机闭环;任务系统可先 stub
3) C1 → C2把资源下发安全化再强制 preview 独立产物)
4) E1审计增强避免后续追溯成本
5) D1如确需异步退款/风控,再引入)