核心变更: - 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>
6.4 KiB
6.4 KiB
版本统计「日历耗时 + 人力投入」双口径设计
Context
当前版本详情下的开发任务/测试用例/Bug 三个 Tab 顶部统计,以及概览页的"总人天投入",都用单条任务独立累加计算耗时。问题:
- 任务 A:09:00–11:00(2h)
- 任务 B:10:00–12:00(2h)
实际只占用了 09:00–12: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;
};
算法:
- 过滤掉非法(
!start || !end)或end <= start的区间 - 按
start升序排序 - 扫描合并:当前与 prev 重叠(
cur.start <= prev.end)时合并prev.end = max(prev.end, cur.end),否则把 prev push 出去 - 对最终的 N 个不重叠区间,逐个调
calcWorkHours(start, end)求和 →calendarHours manhours= 原始 intervals 各自calcWorkHours之后求和(不合并)
精度 0.5h,与现有口径一致。未结束任务以 now 作为 end 参与合并。
验证用例:
- A(9–11) + B(10–12) → 日历 3h,人力 4h
- A(9–11) + B(13–15) + C(10–14) → 合并后 9–15 减去午休 12–13 = 5h;人力 6h
- 跨周末合并:周五 17–周一 10 + 周二 9–11 → 工作时段过滤后两段独立算
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任务 · 预 96h(12天) · 日历 64h(8天) / 人力 84h(10.5天)
- 染色:人力 > 预计 红、< 预计 绿、相等灰
- 日历不染色(中性事实)
(2) TestCaseTab 顶部:
40例 · 通过28 · 失败5 · 通过率85% · 日历 32h(4天) / 人力 56h(7天)
(3) BugTab 顶部:
Bug 12 个 · 待修复2 · 修复中3 · ... · 解决率75% · 日历 18h / 人力 24h
(4) 概览-总人天投入(line 778):当前一行 总人天投入 96h(12人天) 改为两行:
日历总耗时 104h(13天)
人力总投入 176h(22人天)
数据源: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 合并)
实施清单
新增/修改:
apps/web/lib/work-hours.ts—TimeInterval+mergeWorkHours+calcTwoMetricsapps/web/lib/dev-task.ts—devTaskIntervalsapps/web/lib/test-case.ts—testCaseIntervalsapps/web/lib/bug.ts—bugIntervalsapps/web/lib/version-plan.ts—planIntervalsapps/web/components/dev-task/DevTaskTab.tsx— 三段式apps/web/components/test-case/TestCaseTab.tsx— 加日历/人力两段apps/web/components/bug/BugTab.tsx— 加日历/人力两段apps/web/app/versions/[id]/page.tsx— 概览"总人天"改两行
验证
- 单条任务 09:00–11:00:日历=2h,人力=2h
- 两条 A(9–11) + B(10–12):日历=3h,人力=4h
- 三条 A(9–11) + B(13–15) + C(10–14):日历=5h(去午休),人力=6h
- 跨周末合并:周五 17–周一 10 与周二 9–11,分两段算工作时段后再相加
pnpm type-check+pnpm build0 错误- 浏览器进版本详情:开发/测试/Bug 三个 Tab 顶部三段式正确,概览总人天两行正确