feat(工作台): 细化计划进展工时证据

关键改动:

- 支持需求覆盖和调研方向开始工作记录

- 日报按计划记录开始时间和进度下次开始时间计算证据

- 更新版本编辑校验、项目展示和 workflow 说明

Co-Authored-By: Codex GPT-5 <codex@openai.com>
This commit is contained in:
Script Generator
2026-07-01 11:02:38 +08:00
parent e5ce2d221e
commit 8947aeaac7
18 changed files with 1125 additions and 94 deletions

View File

@@ -362,6 +362,123 @@ test('getWorkspaceDailyReport does not double count overlapping activity time ra
assert.equal(report.totalCount, 2);
});
test('getWorkspaceDailyReport uses plan record work start time for daily report hours', () => {
const report = getWorkspaceDailyReport({
activities: [
{
id: 'act-plan-record',
actorId: 'Alice',
date: '2026-06-26',
occurredAt: '2026-06-26T04:00:00.000Z',
sourceType: 'version_plan',
sourceId: 'plan-cross-day',
action: 'version_plan_requirement_progress',
category: 'progress',
title: 'Product plan',
summary: 'Updated product plan requirement progress',
metadata: {
workStartedAt: '2026-06-26T02:00:00.000Z',
},
},
],
worklogs: [],
workItems: [
{
id: 'plan-cross-day',
type: 'plan_product',
title: 'Product plan',
status: 'in_progress',
completed: false,
productName: 'FTB',
projectName: 'Project Management',
versionName: 'V1.0',
versionId: 'version-1',
extra: {
actualStartAt: '2026-06-25T01:00:00.000Z',
},
raw: {
id: 'plan-cross-day',
actualStartAt: '2026-06-25T01:00:00.000Z',
status: 'in_progress',
} as any,
},
],
userId: 'Alice',
date: '2026-06-26',
now: new Date('2026-06-26T08:00:00.000Z'),
});
assert.equal(report.totalHours, 2);
assert.equal(report.totalCount, 1);
});
test('getWorkspaceDailyReport uses previous progress next start time for dev task daily hours', () => {
const report = getWorkspaceDailyReport({
activities: [
{
id: 'act-dev-progress-day-1',
actorId: 'Alice',
date: '2026-07-01',
occurredAt: '2026-07-01T10:00:00.000Z',
sourceType: 'dev_task',
sourceId: 'task-cross-day-progress',
action: 'progress_note_added',
category: 'note',
title: 'Cross-day development',
summary: 'Updated today progress',
metadata: {
note: 'Finished the first part',
nextStartAt: '2026-07-02T01:30:00.000Z',
},
},
{
id: 'act-dev-progress-day-2',
actorId: 'Alice',
date: '2026-07-02',
occurredAt: '2026-07-02T10:00:00.000Z',
sourceType: 'dev_task',
sourceId: 'task-cross-day-progress',
action: 'progress_note_added',
category: 'note',
title: 'Cross-day development',
summary: 'Updated today progress',
metadata: {
note: 'Finished the second part',
nextStartAt: '2026-07-03T01:30:00.000Z',
},
},
],
worklogs: [],
workItems: [
{
id: 'task-cross-day-progress',
type: 'devTask',
title: 'Cross-day development',
status: 'in_progress',
completed: false,
productName: 'FTB',
projectName: 'Project Management',
versionName: 'V1.0',
versionId: 'version-1',
extra: {
actualStartAt: '2026-07-01T01:30:00.000Z',
},
raw: {
id: 'task-cross-day-progress',
actualStartAt: '2026-07-01T01:30:00.000Z',
status: 'in_progress',
} as any,
},
],
userId: 'Alice',
date: '2026-07-02',
now: new Date('2026-07-02T10:00:00.000Z'),
});
assert.equal(report.totalHours, 7.5);
assert.equal(report.totalCount, 1);
});
test('getWorkspaceDailyReport rebuilds activity evidence from persisted work item timestamps', () => {
const report = getWorkspaceDailyReport({
activities: [],
@@ -401,6 +518,96 @@ test('getWorkspaceDailyReport rebuilds activity evidence from persisted work ite
assert.equal(report.totalCount, 2);
});
test('getWorkspaceDailyReport treats same-requirement plan progress as today evidence', () => {
const report = getWorkspaceDailyReport({
activities: [
{
id: 'act-plan-requirement-progress',
actorId: 'Alice',
date: '2026-07-01',
occurredAt: '2026-07-01T03:00:00.000Z',
sourceType: 'version_plan',
sourceId: 'plan-product',
action: 'version_plan_requirement_progress',
category: 'progress',
title: 'Product plan',
summary: 'Updated requirement progress',
metadata: {
requirementId: 'req-shared',
workStartedAt: '2026-07-01T02:00:00.000Z',
},
},
],
worklogs: [],
workItems: [
{
id: 'plan-product',
type: 'plan_product',
title: 'Product plan',
status: 'in_progress',
completed: false,
productName: 'FTB',
projectName: 'Project Management',
versionName: 'V1.0',
versionId: 'version-1',
extra: {
actualStartAt: '2026-06-30T01:00:00.000Z',
},
raw: {
id: 'plan-product',
actualStartAt: '2026-06-30T01:00:00.000Z',
status: 'in_progress',
} as any,
},
{
id: 'task-same-requirement',
type: 'devTask',
title: 'Same requirement task',
status: 'in_progress',
completed: false,
productName: 'FTB',
projectName: 'Project Management',
versionName: 'V1.0',
versionId: 'version-1',
extra: {
actualStartAt: '2026-06-30T02:00:00.000Z',
},
raw: {
id: 'task-same-requirement',
requirementId: 'req-shared',
actualStartAt: '2026-06-30T02:00:00.000Z',
status: 'in_progress',
} as any,
},
{
id: 'task-other-requirement',
type: 'devTask',
title: 'Other requirement task',
status: 'in_progress',
completed: false,
productName: 'FTB',
projectName: 'Project Management',
versionName: 'V1.0',
versionId: 'version-1',
extra: {
actualStartAt: '2026-06-30T02:00:00.000Z',
},
raw: {
id: 'task-other-requirement',
requirementId: 'req-other',
actualStartAt: '2026-06-30T02:00:00.000Z',
status: 'in_progress',
} as any,
},
],
userId: 'Alice',
date: '2026-07-01',
now: new Date('2026-07-01T04:00:00.000Z'),
});
assert.deepEqual(report.needsProgressItems.map((item) => item.id), ['task-other-requirement']);
});
test('getWorkspaceDailyReport flags multi-day in-progress items without today progress', () => {
const report = getWorkspaceDailyReport({
activities,