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

179 lines
6.4 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.

# 版本统计「日历耗时 + 人力投入」双口径设计
## 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` 新增:
```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`** 新增:
```ts
export function devTaskIntervals(tasks: DevTask[], now?: Date): TimeInterval[];
// 取 [actualStartAt, actualEndAt ?? now],过滤无 actualStartAt 的
```
**`apps/web/lib/test-case.ts`** 新增:
```ts
export function testCaseIntervals(cases: TestCase[], now?: Date): TimeInterval[];
// 取 [startedAt, completedAt ?? now],过滤无 startedAt 的
```
**`apps/web/lib/bug.ts`** 新增:
```ts
export function bugIntervals(bugs: Bug[], now?: Date): TimeInterval[];
// 取 [createdAt, closedAt ?? resolvedAt ?? (status 终态时 updatedAt) ?? now]
```
**`apps/web/lib/version-plan.ts`** 新增(仅供概览总人天用):
```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.ts` `TimeInterval` + `mergeWorkHours` + `calcTwoMetrics`
2. `apps/web/lib/dev-task.ts` `devTaskIntervals`
3. `apps/web/lib/test-case.ts` `testCaseIntervals`
4. `apps/web/lib/bug.ts` `bugIntervals`
5. `apps/web/lib/version-plan.ts` `planIntervals`
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 顶部三段式正确概览总人天两行正确