build_campaign_timeline flattens a campaign's child Runs into offset-sorted
per-Run entries (distinct from the report's 12-bucket aggregation), reusing a
shared _run_window_offset口径 so both views place a run identically. Exposes
GET /campaigns/{id}/timeline and the api.ts type/call. (v0.6 ticket 06)
9.7 KiB
9.7 KiB
v0.6 Spec(UX 升级)— 评估活动页 UX 升级
状态: ready-for-agent
领域词汇: 见 CONTEXT.md「周期评估」章节(评估活动 / 服务周期窗口 / 活动计划 / 可用性)
关键决策: ADR-0002(通过率含执行失败)、ADR-0003(活动分期);本批不改判定/调度/报告聚合语义
批次说明: 本 spec 是 v0.6 评估活动功能线的 UX 升级批次(①倍速反向输入 ②计划时间轴预览 ④展开行时间轴),与 spec.md(v1 静态地基)并存。线③「活动分析 Agent」属新功能,另立 v0.7 spec。
工单: 本 spec 派生 v0.6/issues/06–10。
Problem Statement
评估活动(Campaign)的能力在 v0.6 静态地基已落地,但新增/查看活动的页面对用户不友好:
- 时间倍速是裸数字:新增活动时「时间倍速」是一个
InputNumber(默认 1,仅一句 tooltip),用户得自己心算「填多大倍速能让 24h 窗口几分钟跑完」。用户真正关心的是「加速后多久跑完」,而不是抽象的倍速值。 - 活动计划不直观:计划是一组
场景 + 偏移小时 + 次数的表单行,看不出「在这个窗口的哪些时段、跑哪个场景、跑几次」的时间分布全貌。 - 看不到活动过程:活动列表每行不可展开;跑完只能进报告抽屉看聚合曲线,无法直观回看「整个活动过程中,各个子 Run 在时间上如何依次发生、各自成败」。
Solution
对活动页做三项 UX 升级,不触碰判定、调度、报告聚合的任何语义:
- ① 倍速反向输入:新增活动时用户填「窗口长度」+「希望加速后多久跑完」,系统自动派生并只读展示时间倍速;正式线用「实时」开关(×1)。用户从此按「耗时」思考,而非「倍速」。
- ② 计划时间轴预览:保留(优化后的)行编辑器做精确录入,其上方增加一条只读时间轴预览,把计划条目按偏移画在代表窗口的水平轴上、颜色区分场景、标注次数,随表单实时刷新。
- ④ 展开行时间轴:活动列表每行可展开为一条横向窗口时间轴,把该活动的子 Run 按「加速后窗口偏移」定位成节点、颜色表状态、hover 显详情、点击跳该 Run 报告;进行中活动随现有轮询实时长出新节点。②与④共用同一套时间轴视觉语言。
User Stories
- 作为质量负责人,我想在新增活动时直接填「希望这个 24 小时窗口在多久内跑完」(如 1 小时),系统替我算好倍速,以便我不必心算倍速。
- 作为质量负责人,我想在填写时实时看到「加速后约 X 跑完」,以便确认压缩节奏符合预期。
- 作为质量负责人,我想在正式线一键选「实时」(真实墙钟、倍速 ×1),以便采到真实的凌晨/高峰表现。
- 作为质量负责人,当我把目标耗时填得比窗口还长(意味着倍速 <1、比真实还慢)时,我想被拦下或纠正,以便不产生无意义的减速活动。
- 作为质量负责人,我想在编辑活动计划时看到一条时间轴预览,把每个计划条目按其窗口偏移画出来,以便一眼看清「哪些时段跑哪个场景、跑几次」的分布。
- 作为质量负责人,我想计划预览用颜色区分不同场景、并标注每个条目的次数,以便区分密集与稀疏时段。
- 作为质量负责人,我想计划预览随我编辑表单实时更新,以便边填边看。
- 作为质量负责人,我想在活动列表里展开任意一行,看到这个活动的完整过程时间轴,以便回看整个服务周期里发生了什么。
- 作为质量负责人,我想过程时间轴把每个子 Run 按其「加速后窗口偏移」定位成节点,以便看清子 Run 在窗口内的时间分布。
- 作为质量负责人,我想每个节点用颜色表示状态(完成/失败/运行中/取消),以便一眼识别问题时段。
- 作为质量负责人,我想 hover 节点看到场景、通过率、时延,以便无需展开报告即可速览。
- 作为质量负责人,我想点击节点直接跳到该子 Run 的报告,以便下钻排查。
- 作为质量负责人,当我展开一个进行中的活动时,我想时间轴随轮询长出新派生的子 Run,以便实时观察活动推进。
- 作为质量负责人,我想活动列表/详情处也用「加速后约 X 完成」而非裸倍速展示,以便和新增表单的心智一致。
Implementation Decisions
① 倍速反向输入(前端为主,API 契约不变)
- 新增活动表单把「时间倍速」输入替换为两项:窗口长度(沿用预设下拉 6/12/24/48/72h)+ 目标加速耗时(数值 + 单位,默认分钟)。
- 前端派生
time_scale = window_seconds / target_real_seconds,作为只读展示(如「倍速 ×24 · 加速后约 1 小时跑完」),提交时仍在 payload 里发time_scale。后端POST /campaigns契约与Campaign.time_scale语义完全不变。 - 「实时」开关:选中即
target_real_seconds = window_seconds→time_scale = 1.0,并禁用目标耗时输入。 - 约束:目标耗时必须 ≤ 窗口长度(即
time_scale ≥ 1);违反时表单校验拦截(对应 US-4)。 - 派生与反算(由 scale+window 反推「加速后耗时」用于列表/详情展示)抽成纯前端 util(如
campaignTime.ts的deriveTimeScale/acceleratedDuration),集中口径、便于复用。
② 计划时间轴预览(纯前端)
- 新增一个只读时间轴预览组件,输入 = 表单当前的
window_seconds+plan条目 + 场景名映射,输出 = 一条水平窗口轴,条目按offset_seconds定位,颜色按scenario_id稳定分配,标注count。 - 不涉及任何后端改动;数据完全来自表单本地状态,随编辑实时重渲染。
- 与④的过程时间轴共享底层展示(见下「共享组件」)。
④ 展开行时间轴(新后端纯接缝 + 前端展开行)
- 新后端纯函数接缝
build_campaign_timeline(campaign, runs) -> list[CampaignTimelineEntry](放evaluation/report.py或紧邻的模块),每条含:run_id / scenario_id / scenario_name / offset_seconds(加速后窗口偏移,clamp 到 [0, window])/ status / pass_rate / avg_latency_ms / started_at。- 偏移计算复用现有报告口径:
offset = (run.started_at - campaign.started_at).total_seconds() * time_scale,clamp 到[0, window_seconds];未开始的子 Run(无 started_at)与取消 Run 的处理在此接缝内明确定义。 pass_rate直接取子 Runsummary.pass_rate(权威,只读不重算,沿 v0.5/v0.6 约定)。
- 偏移计算复用现有报告口径:
- 新端点
GET /campaigns/{id}/timeline→{entries: CampaignTimelineEntry[]},返回逐 Run 扁平数组(区别于报告的 12 桶聚合曲线)。 - 前端:活动列表行改为
expandable,展开区渲染横向窗口时间轴:子 Run 节点按offset_seconds定位、颜色=状态、hover 显场景/通过率/时延、点击跳/reports?run={run_id}。展开进行中活动时,随现有 5s 轮询刷新时间轴数据。
共享组件
- ②的计划预览与④的过程时间轴共用一个底层横向时间轴展示组件(窗口轴 + 定位标记 + 颜色 + hover),两处用不同数据适配:②喂「计划条目」,④喂「子 Run」。保证视觉语言一致,避免两套实现。
API 契约(新增项)
GET /campaigns/{id}/timeline→{ "entries": [ { "run_id", "scenario_id", "scenario_name", "offset_seconds", "status", "pass_rate", "avg_latency_ms", "started_at" } ] }。- 其余活动端点、
Campaign模型、time_scale语义均不变。
Testing Decisions
- 好测试只测外部行为:接缝喂构造好的输入、断言输出,不测内部实现细节。
- 主接缝:
build_campaign_timeline(纯函数) —— 唯一新增的可测后端接缝。单测覆盖:正常子 Run 的偏移计算(含time_scale压缩)、clamp 到窗口边界、未开始/取消/失败子 Run 的处理、字段映射(pass_rate 取自 summary、场景名映射)、空活动。Prior art:tests/unit/test_report.py/test_campaign_report.py(同样喂 campaign+runs 断言聚合 dict 的模式)。 - 端点集成测试:
GET /campaigns/{id}/timeline的 200/404、字段形状。Prior art:tests/integration/test_runs_api.py、现有 campaigns router 测试。 - 前端无测试框架:①倍速反向输入(派生/反算/实时开关/≤窗口校验)、②计划预览、④展开时间轴,均以
npx tsc --noEmit+npm run build+ 浏览器实操验证(含进行中活动的轮询刷新)。deriveTimeScale/acceleratedDuration写成纯函数,便于将来接入前端测试。
Out of Scope
- 线③ 活动分析 Agent + 服务质量改善建议:新功能,另立 v0.7 spec(含新增「分析」模型岗位、全局默认分析模型 + 活动可覆盖、多步分析 Agent、结构化分析报告、按需生成+缓存)。
- 计划的拖拽编辑:本批时间轴预览为只读;拖拽调偏移/点击空白新增等交互不做。
- 跨活动对比、活动列表分页/筛选增强,均不在本批。
Further Notes
- ②预览与④过程时间轴的共享底层组件是本批的关键复用点,也是唯一较有设计含量的前端接缝——先把它的输入接口设计干净(喂「带窗口偏移的标记数组」),两处适配即可。
- 列表/详情的「加速后约 X 完成」展示与新增表单的派生口径必须走同一个纯 util,避免两处算法漂移(呼应 v0.6 起「口径收敛到一处」的做法)。