Files
ftb-project-management/docs/superpowers/specs/2026-06-16-version-stats-two-metrics-design.md
Script Generator 1f5bed0c79 feat: 开发任务时间字段重构 + 双口径耗时 + 搜索 + 性能优化
核心变更:
- DevTask 字段重构:startDate/dueDate/estimateHours/actualHours 替换为 expectedStartAt/expectedEndAt/actualStartAt/actualEndAt(含时分),预计/实际工时按工作时段(9:00-12:00 + 13:00-18:00)派生计算
- 状态机自动化:到点自动切开发中、未到可手动开干、超期需填延后原因
- 新建 lib/work-hours.ts:calcWorkHours(工作时段过滤)、formatWorkHours(X h(Y 天))、calcTwoMetrics(日历/人力双口径)
- 新建 lib/dev-task-transitions.ts:状态切换守卫
- 三个 Tab 顶部统计:开发任务(预/日历/人力)、测试用例 / Bug(日历 / 人力);版本概览总人天投入改双行(日历总耗时 / 人力总投入)
- 三个 Tab 加搜索框:300ms debounce + 标题/编号模糊匹配
- 性能优化:requirementIds/categories/requirements/testCases 转 Map/Set 索引、Row 组件 React.memo
- 全局耗时显示统一带天数换算:8h(1天)、11h(1.4天)

新增文件: SearchInput.tsx, useDebouncedValue.ts, work-hours.ts, dev-task-transitions.ts, 设计文档

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-16 17:44:59 +08:00

6.4 KiB
Raw Permalink Blame History

版本统计「日历耗时 + 人力投入」双口径设计

Context

当前版本详情下的开发任务/测试用例/Bug 三个 Tab 顶部统计,以及概览页的"总人天投入",都用单条任务独立累加计算耗时。问题:

  • 任务 A09:0011:002h
  • 任务 B10:0012:002h

实际只占用了 09:0012:00 共 3h 工作时段,但当前累加算成 4h。

任务越多重叠越严重,"总耗时"会显著虚高,无法回答"这个版本实际走了几天"。

目标:顶部统计同时展示两个口径——

  • 日历耗时:合并区间后的真实工作时段占用(看版本实际走了多久)
  • 人力投入:单条独立累加(看总人力成本,原口径)

决策摘要

决策
算法位置 apps/web/lib/work-hours.ts 新增 mergeWorkHours + calcTwoMetrics
区间提取 各模块dev-task / test-case / bug暴露 xxxIntervals(items) 函数
顶部展示 三段式:"预 X / 日历 Y / 人力 Z"(开发任务);"日历 Y / 人力 Z"(测试用例 / Bug
概览 总人天 两行展示:日历总耗时 / 人力总投入(含调研/产品/UI/开发/测试/Bug 全程)
个人耗时排名 不动(按人汇总天然是人力维度)
PlanTab 顶部 不动(不在本次范围)
老聚合函数 保留(向后兼容,仍然是 manhours
Bug 区间 end 优先级 closedAt > resolvedAt > 终态 updatedAt > now

1. 合并区间算法

apps/web/lib/work-hours.ts 新增:

export interface TimeInterval {
  start: string;
  end: string;
}

// 合并所有重叠区间后,对每段独立调 calcWorkHours 求和
export function mergeWorkHours(intervals: TimeInterval[]): number;

// 双口径汇总
export function calcTwoMetrics(intervals: TimeInterval[]): {
  calendarHours: number;
  manhours: number;
};

算法:

  1. 过滤掉非法(!start || !end)或 end <= start 的区间
  2. start 升序排序
  3. 扫描合并:当前与 prev 重叠(cur.start <= prev.end)时合并 prev.end = max(prev.end, cur.end),否则把 prev push 出去
  4. 对最终的 N 个不重叠区间,逐个调 calcWorkHours(start, end) 求和 → calendarHours
  5. manhours = 原始 intervals 各自 calcWorkHours 之后求和(不合并)

精度 0.5h,与现有口径一致。未结束任务以 now 作为 end 参与合并。

验证用例

  • A(911) + B(1012) → 日历 3h人力 4h
  • A(911) + B(1315) + C(1014) → 合并后 915 减去午休 1213 = 5h人力 6h
  • 跨周末合并:周五 17周一 10 + 周二 911 → 工作时段过滤后两段独立算

2. 三个模块的 intervals 提取

apps/web/lib/dev-task.ts 新增:

export function devTaskIntervals(tasks: DevTask[], now?: Date): TimeInterval[];
// 取 [actualStartAt, actualEndAt ?? now],过滤无 actualStartAt 的

apps/web/lib/test-case.ts 新增:

export function testCaseIntervals(cases: TestCase[], now?: Date): TimeInterval[];
// 取 [startedAt, completedAt ?? now],过滤无 startedAt 的

apps/web/lib/bug.ts 新增:

export function bugIntervals(bugs: Bug[], now?: Date): TimeInterval[];
// 取 [createdAt, closedAt ?? resolvedAt ?? (status 终态时 updatedAt) ?? now]

apps/web/lib/version-plan.ts 新增(仅供概览总人天用):

export function planIntervals(plans: VersionPlan[], now?: Date): TimeInterval[];
// 取 [actualStartAt, completedAt ?? now],过滤无 actualStartAt 的

老聚合函数(aggregateDevTaskHours / aggregateTestCaseActualHours / aggregateBugActualHours)保留,仍然是 manhours 累加,向后兼容(项目顶部卡片、与我相关页继续用)。

3. 4 个统计点 UI 调整

(1) DevTaskTab 顶部

12任务 · 预 96h12天 · 日历 64h8天 / 人力 84h10.5天)
  • 染色:人力 > 预计 红、< 预计 绿、相等灰
  • 日历不染色(中性事实)

(2) TestCaseTab 顶部

40例 · 通过28 · 失败5 · 通过率85% · 日历 32h4天 / 人力 56h7天

(3) BugTab 顶部

Bug 12 个 · 待修复2 · 修复中3 · ... · 解决率75% · 日历 18h / 人力 24h

(4) 概览-总人天投入line 778当前一行 总人天投入 96h12人天 改为两行:

日历总耗时    104h13天
人力总投入    176h22人天

数据源plans + devTasks + testCases + bugs 的 intervals 全部合并后调 calcTwoMetrics

4. 不动的部分

  • 个人耗时排名(按人累加,本身就是人力维度)
  • 阶段耗时(项目维度)卡片(已经是日历口径,用 calcCalendar 算最早开始/最晚结束/天数差)
  • PlanTab调研/产品方案/UI 设计)顶部统计
  • 项目详情顶部 HoursStatCard
  • 与我相关页 DevTask 工时显示

数据流

DevTask + TestCase + Bug + Plan
   │
   ├── devTaskIntervals(tasks)
   ├── testCaseIntervals(cases)
   ├── bugIntervals(bugs)
   └── planIntervals(plans)
       │
       └── work-hours.calcTwoMetrics(intervals)
                │
                ├── DevTaskTab 顶部
                ├── TestCaseTab 顶部
                ├── BugTab 顶部
                └── 概览总人天卡片4 个 intervals 合并)

实施清单

新增/修改:

  1. apps/web/lib/work-hours.tsTimeInterval + mergeWorkHours + calcTwoMetrics
  2. apps/web/lib/dev-task.tsdevTaskIntervals
  3. apps/web/lib/test-case.tstestCaseIntervals
  4. apps/web/lib/bug.tsbugIntervals
  5. apps/web/lib/version-plan.tsplanIntervals
  6. apps/web/components/dev-task/DevTaskTab.tsx — 三段式
  7. apps/web/components/test-case/TestCaseTab.tsx — 加日历/人力两段
  8. apps/web/components/bug/BugTab.tsx — 加日历/人力两段
  9. apps/web/app/versions/[id]/page.tsx — 概览"总人天"改两行

验证

  1. 单条任务 09:0011:00日历=2h人力=2h
  2. 两条 A(911) + B(1012):日历=3h人力=4h
  3. 三条 A(911) + B(1315) + C(1014):日历=5h去午休人力=6h
  4. 跨周末合并:周五 17周一 10 与周二 911分两段算工作时段后再相加
  5. pnpm type-check + pnpm build 0 错误
  6. 浏览器进版本详情:开发/测试/Bug 三个 Tab 顶部三段式正确,概览总人天两行正确