feat(版本): 优化概览与只读状态

关键改动:

- 增加需求排序和版本只读状态规则及测试

- 完善版本概览阶段耗时、项目页和工作台展示

- 优化小宝预警请求节流、建议状态和风险过滤

Co-Authored-By: Codex GPT-5 <codex@openai.com>
This commit is contained in:
Script Generator
2026-06-30 18:18:18 +08:00
parent 3d3d56697a
commit eef3c8f000
32 changed files with 1072 additions and 289 deletions

View File

@@ -6,6 +6,10 @@ import {
calcPersonalEffortRanking,
calcStageEffortMetrics,
calcVersionOverviewEffortTotals,
buildVersionTimelineSummary,
formatVersionOverviewDateTime,
getVersionCardDefaultExpanded,
mergeStageProgressWithEffort,
} from './version-overview';
import type { VersionPlan } from './version-plan';
import type { DevTask } from './dev-task';
@@ -131,6 +135,60 @@ test('calcStageEffortMetrics returns actual hours and AI estimates per stage', (
assert.equal(metrics.bug.actualHours, 0.5);
});
test('mergeStageProgressWithEffort keeps progress state and adds stage effort fields', () => {
const metrics = calcStageEffortMetrics({
plans: [],
devTasks: [
devTask({
actualStartAt: '2026-06-24T09:00:00',
actualEndAt: '2026-06-24T14:00:00',
estimateHours: 4.5,
aiEstimateHours: 3.25,
}),
],
testCases: [
testCase({
startedAt: '2026-06-25T09:00:00',
completedAt: '2026-06-25T11:00:00',
estimateHours: 2.25,
aiEstimateHours: 1.5,
}),
],
bugs: [],
});
const merged = mergeStageProgressWithEffort(
{
dev: { percent: 50, status: 'active' },
testing: { percent: 100, status: 'done' },
},
metrics,
);
assert.equal(merged.dev.percent, 50);
assert.equal(merged.dev.status, 'active');
assert.equal(merged.dev.actualHours, 4);
assert.equal(merged.dev.estimateHours, 4.5);
assert.equal(merged.dev.aiEstimateHours, 3.25);
assert.equal(merged.dev.showEstimates, true);
assert.equal(merged.testing.percent, 100);
assert.equal(merged.testing.status, 'done');
assert.equal(merged.testing.actualHours, 2);
assert.equal(merged.testing.estimateHours, 2.25);
assert.equal(merged.testing.aiEstimateHours, 1.5);
assert.equal(merged.requirement.percent, 0);
assert.equal(merged.requirement.status, 'idle');
assert.equal(merged.requirement.actualHours, 0);
});
test('getVersionCardDefaultExpanded expands only in-progress versions by default', () => {
assert.equal(getVersionCardDefaultExpanded('developing'), true);
assert.equal(getVersionCardDefaultExpanded('released'), false);
assert.equal(getVersionCardDefaultExpanded('closed'), false);
assert.equal(getVersionCardDefaultExpanded('paused'), false);
assert.equal(getVersionCardDefaultExpanded('planned'), false);
});
test('calcStageEffortMetrics uses updatedAt for terminal test cases missing completedAt', () => {
const metrics = calcStageEffortMetrics({
plans: [],
@@ -188,6 +246,58 @@ test('calcVersionOverviewEffortTotals sums actual hours and overtime records sep
assert.equal(totals.overtimeHours, 3.3);
});
test('buildVersionTimelineSummary uses the same timing sources as version detail', () => {
const summary = buildVersionTimelineSummary({
status: 'closed',
startDate: '2026-06-20',
expectedReleaseDate: '2026-06-27',
releaseDate: '2026-06-30T10:00:00',
plans: [
plan({
type: 'research',
actualStartAt: '2026-06-22T09:00:00',
completedAt: '2026-06-22T18:00:00',
}),
],
devTasks: [
devTask({
actualStartAt: '2026-06-23T09:00:00',
actualEndAt: '2026-06-23T12:00:00',
}),
],
testCases: [
testCase({
startedAt: '2026-06-24T09:00:00',
completedAt: '2026-06-24T11:00:00',
}),
],
bugs: [
bug({
status: 'closed',
createdAt: '2026-06-25T10:00:00',
resolvedAt: '2026-06-25T16:00:00',
closedAt: undefined,
updatedAt: '2026-06-25T17:00:00',
}),
],
now: new Date('2026-06-26T18:00:00'),
});
assert.equal(summary.actualStartIso, '2026-06-22T09:00:00');
assert.equal(summary.expectedReleaseIso, '2026-06-27');
assert.equal(summary.actualReleaseIso, '2026-06-30T10:00:00');
assert.equal(summary.actualEndIso, '2026-06-25T16:00:00');
assert.equal(summary.isTerminalVersion, true);
assert.equal(summary.actualHours, 30);
assert.equal(summary.overdueDays, 3);
});
test('formatVersionOverviewDateTime matches the version detail display format', () => {
assert.equal(formatVersionOverviewDateTime('2026-06-22T09:30:00'), '2026-06-22 09:30');
assert.equal(formatVersionOverviewDateTime('2026-06-27'), '2026-06-27');
assert.equal(formatVersionOverviewDateTime(null), '-');
});
test('calcPersonalEffortRanking includes bug work and sorts by total hours', () => {
const overtimeRecords: OvertimeRecord[] = [
{