diff --git a/docs/superpowers/specs/2026-07-10-independent-spread-metric-filter-design.md b/docs/superpowers/specs/2026-07-10-independent-spread-metric-filter-design.md new file mode 100644 index 0000000..013a1bd --- /dev/null +++ b/docs/superpowers/specs/2026-07-10-independent-spread-metric-filter-design.md @@ -0,0 +1,172 @@ +# 传播指标独立视频口径筛选设计 + +## 背景 + +当前工具栏把视频类型、是否指派、是否排除营销流量和时间范围作为一套全局视频口径,再把完播率和互动率阈值同时应用到这套口径上。 + +正确的业务关系应为:用户先选择筛选指标,每个已选指标再分别配置阈值和一套独立视频口径。例如,完播率可以使用“星图视频、只看指派、近30天”,互动率同时使用“个人视频、近90天”。 + +## 目标 + +- 当前只提供完播率和互动率两个可选筛选指标。 +- 默认不选择任何指标,不改变现有导出和提交结果。 +- 支持同时选择两个指标。 +- 每个已选指标必须填写阈值并配置一套独立视频口径。 +- 同时启用多个指标时,达人必须满足全部指标规则才会被保留。 +- CSV 导出和提交批次复用相同的筛选结果。 + +## 非目标 + +- 不新增完播率和互动率之外的筛选指标。 +- 不修改传播数据 CSV 的可选字段和字段命名。 +- 不修改批次提交接口或批次 payload。 +- 不修改星图 `get_author_spread_info` 的接口参数和响应字段映射。 +- 不调整导出范围、选择达人、字段选择等其他工具栏能力。 + +## 前端交互 + +### 初始状态 + +“传播指标筛选”区域显示完播率、互动率两个复选框,默认均未选中。 + +未选中任何指标时: + +- 不显示指标规则行。 +- 不调用传播指标接口。 +- 导出 CSV 和提交批次保持原行为。 + +### 动态规则行 + +勾选一个指标后,显示该指标的规则行。规则行从左到右包含: + +1. 指标名称。 +2. `>=` 阈值输入框和 `%` 单位。 +3. 视频类型:个人视频或星图视频。 +4. 是否只看指派:不限指派或只看指派。 +5. 是否排除营销流量:不排除营销或排除营销。 +6. 时间范围:近30天或近90天。 + +新规则行沿用现有默认口径:个人视频、不限指派、不排除营销、近30天。阈值默认为空,必须由用户填写。 + +取消勾选指标后,移除并丢弃对应规则,该指标不参与筛选。重新勾选时按上述默认值创建新规则,不恢复之前输入的阈值和口径。 + +同时勾选完播率和互动率时,显示两条互相独立的规则行。界面明确提示“全部规则都达标才保留达人”。 + +### 个人视频约束 + +每条规则独立应用现有个人视频约束。选择个人视频后: + +- `onlyAssign` 自动设为 `false`,显示“不限指派”。 +- `flowType` 自动设为 `0`,显示“不排除营销”。 +- 是否指派和营销流量两个下拉框禁用。 +- 时间范围仍可选择近30天或近90天。 + +切换回星图视频后,重新启用是否指派和营销流量下拉框。 + +### 输入校验 + +每个已选指标都必须填写一个大于或等于 `0` 的有限数值阈值。阈值为空、非数字或小于 `0` 时: + +- 阻止导出 CSV 或提交批次。 +- 在对应指标规则行展示校验状态。 +- 工具栏状态区域给出可理解的错误提示。 + +未选中的指标不参与校验。 + +## 数据模型 + +筛选模型从“一套全局口径 + 多个可选阈值”改为规则数组: + +```ts +type SpreadFilterMetric = "finishRate" | "interactionRate"; + +interface SpreadMetricFilterRule { + config: SpreadInfoConfig; + metric: SpreadFilterMetric; + threshold: number; +} + +interface SpreadThresholdFilter { + rules: SpreadMetricFilterRule[]; +} +``` + +每条规则完整表达“用哪套视频口径读取哪个指标,并与什么阈值比较”。同一指标最多出现一条规则。 + +`SpreadInfoConfig` 从 `spread-info.ts` 移到 `types.ts`,由工具栏、筛选模型和传播接口客户端共同引用,避免公共筛选类型反向依赖接口实现模块。 + +传播数据导出仍保留完整的 `MappedSpreadInfoResponse` 和七项传播指标映射。筛选规则类型只收窄筛选入口,不影响已有 CSV 导出能力。 + +## 筛选执行 + +### 处理顺序 + +1. 从工具栏读取已选指标,生成 `SpreadMetricFilterRule[]`。 +2. 没有规则时直接返回原达人集合。 +3. 按现有导出范围或已选达人规则收集候选达人。 +4. 对每个达人按规则中的 `config` 分组。 +5. 每个唯一 `config` 调用一次 `get_author_spread_info`。 +6. 从返回快照中读取每条规则对应的指标。 +7. 使用显示百分数值与阈值做 `>=` 比较。 +8. 对同一达人的所有规则执行 AND 汇总。 +9. 只有全部规则通过的达人进入 CSV 或批次 payload。 + +### 请求复用 + +如果完播率和互动率使用完全相同的视频口径,同一达人只请求一次接口,并从同一响应中分别读取 `play_over_rate.value` 和 `interact_rate.value`。 + +如果两条规则的视频口径不同,同一达人分别请求两次。 + +视频口径是否相同由 `type`、`onlyAssign`、`flowType` 和 `range` 四个规范化后的参数共同决定。个人视频规则必须在分组前规范化为 `onlyAssign=false`、`flowType=0`。 + +### 指标映射 + +- 完播率规则读取映射后的 `finishRate`,来源为 `data.play_over_rate.value`。 +- 互动率规则读取映射后的 `interactionRate`,来源为 `data.interact_rate.value`。 +- 两个接口值继续沿用现有基点百分比格式化逻辑,再与用户输入的显示百分数比较。 + +## 失败处理 + +- 达人缺少传播指标请求所需 ID:该达人不满足筛选。 +- 单次请求失败、超时或返回非成功状态:依赖该口径的规则不通过。 +- 响应缺少已选指标:对应规则不通过。 +- 一条规则不通过:该达人因 AND 关系被排除。 +- 单个达人失败不终止整批导出或提交,其他达人继续处理。 +- 没有达人满足规则时,保持现有空结果处理方式,不扩大本次改动范围。 + +## 组件与代码边界 + +- `plugin-toolbar.ts`:指标复选框、动态规则行、个人视频联动、逐行校验和规则读取。 +- `types.ts`:共享的 `SpreadInfoConfig`、筛选指标联合类型、单条规则类型和规则数组模型。 +- `spread-info.ts`:复用现有请求构造与响应映射;提供单指标规则比较或等价的可测试纯函数。 +- `index.ts`:按唯一口径加载快照、对每个达人执行规则 AND 判断,并让导出和提交共享该流程。 +- 现有 CSV 字段定义、批次 payload 构造和外部接口契约不变。 + +## 测试与验收 + +### 工具栏测试 + +- 默认两个指标均未选中,不显示规则行。 +- 勾选或取消完播率、互动率时,正确新增或移除对应规则行。 +- 两个规则行可以保存不同阈值和不同视频口径。 +- 个人视频自动规范化并禁用受限选项;星图视频重新启用选项。 +- 已选指标缺少合法阈值时返回逐行校验错误。 +- 未选择任何指标时读取到空规则集。 + +### 纯逻辑测试 + +- 单规则按对应指标和显示百分数正确比较。 +- 两条规则均通过时返回通过。 +- 任意一条规则不通过或缺少指标值时返回不通过。 +- 相同视频口径生成相同分组键,不同口径生成不同分组键。 +- 个人视频在分组前强制规范化固定参数。 + +### 集成测试 + +- 未选择指标时,导出和提交均不调用传播指标筛选请求。 +- 两个指标使用不同口径时,每个达人发起两个请求并按 AND 过滤。 +- 两个指标使用相同口径时,每个达人只发起一个请求并复用响应。 +- 请求失败、缺少传播 ID 或缺少指标值的达人被排除,不中断整批处理。 +- CSV 导出和批次提交得到相同的保留达人集合。 +- 现有传播数据导出、工具栏挂载和批次 payload 测试继续通过。 +- 项目类型检查、测试和构建通过。