关键改动: - 支持需求覆盖和调研方向开始工作记录 - 日报按计划记录开始时间和进度下次开始时间计算证据 - 更新版本编辑校验、项目展示和 workflow 说明 Co-Authored-By: Codex GPT-5 <codex@openai.com>
369 lines
9.6 KiB
TypeScript
369 lines
9.6 KiB
TypeScript
import type { Bug, BugStatus } from './bug';
|
||
import type { DevTask, DevTaskStatus } from './dev-task';
|
||
import type { TestCase, TestCaseStatus } from './test-case';
|
||
import { REQUIREMENT_COVERAGE_LABEL, type VersionPlan, type VersionPlanLog } from './version-plan';
|
||
import type { WorkActivityDraft, WorkActivitySourceType } from './work-activity';
|
||
|
||
const PLAN_TYPE_LABEL: Record<VersionPlan['type'], string> = {
|
||
research: '调研',
|
||
product: '产品方案',
|
||
ui: 'UI设计',
|
||
};
|
||
|
||
export function makeVersionPlanCreatedActivity(plan: VersionPlan, actorId: string): WorkActivityDraft {
|
||
return {
|
||
actorId,
|
||
sourceType: 'version_plan',
|
||
sourceId: plan.id,
|
||
action: 'version_plan_created',
|
||
category: 'creation',
|
||
title: plan.title,
|
||
summary: `新建${PLAN_TYPE_LABEL[plan.type]}:${plan.title}`,
|
||
};
|
||
}
|
||
|
||
export function makeVersionPlanStartedActivity(plan: VersionPlan, actorId: string): WorkActivityDraft {
|
||
return {
|
||
actorId,
|
||
sourceType: 'version_plan',
|
||
sourceId: plan.id,
|
||
action: 'version_plan_started',
|
||
category: 'progress',
|
||
title: plan.title,
|
||
summary: `开始${PLAN_TYPE_LABEL[plan.type]}:${plan.title}`,
|
||
};
|
||
}
|
||
|
||
export function makeVersionPlanCompletedActivity(plan: VersionPlan, actorId: string): WorkActivityDraft {
|
||
return {
|
||
actorId,
|
||
sourceType: 'version_plan',
|
||
sourceId: plan.id,
|
||
action: 'version_plan_completed',
|
||
category: 'delivery',
|
||
title: plan.title,
|
||
summary: `完成${PLAN_TYPE_LABEL[plan.type]}:${plan.title}`,
|
||
};
|
||
}
|
||
|
||
export function makeVersionPlanRequirementProgressActivity(plan: VersionPlan, log: VersionPlanLog): WorkActivityDraft | undefined {
|
||
if (log.type !== 'requirement_progress') return undefined;
|
||
if (plan.type !== 'product' && plan.type !== 'ui') return undefined;
|
||
|
||
const requirementLabel = [log.requirementCode, log.requirementTitle].filter(Boolean).join(' ') || '需求';
|
||
const statusLabel = log.coverageStatus ? REQUIREMENT_COVERAGE_LABEL[log.coverageStatus] : '进度更新';
|
||
|
||
return {
|
||
actorId: log.actor,
|
||
sourceType: 'version_plan',
|
||
sourceId: plan.id,
|
||
action: 'version_plan_requirement_progress',
|
||
category: 'progress',
|
||
title: plan.title,
|
||
summary: `推进${PLAN_TYPE_LABEL[plan.type]}需求:${requirementLabel}(${statusLabel})`,
|
||
occurredAt: log.createdAt,
|
||
metadata: {
|
||
requirementId: log.requirementId,
|
||
requirementCode: log.requirementCode,
|
||
requirementTitle: log.requirementTitle,
|
||
coverageStatus: log.coverageStatus,
|
||
completedContent: log.completedContent,
|
||
remainingContent: log.remainingContent,
|
||
workStartedAt: log.workStartedAt,
|
||
},
|
||
};
|
||
}
|
||
|
||
export function makeVersionPlanResearchDirectionProgressActivity(plan: VersionPlan, log: VersionPlanLog): WorkActivityDraft | undefined {
|
||
if (log.type !== 'research_direction_progress') return undefined;
|
||
if (plan.type !== 'research') return undefined;
|
||
|
||
const directionTitle = log.directionTitle || '调研方向';
|
||
const statusLabel = log.coverageStatus ? REQUIREMENT_COVERAGE_LABEL[log.coverageStatus] : '进度更新';
|
||
|
||
return {
|
||
actorId: log.actor,
|
||
sourceType: 'version_plan',
|
||
sourceId: plan.id,
|
||
action: 'version_plan_research_direction_progress',
|
||
category: 'progress',
|
||
title: plan.title,
|
||
summary: `推进调研方向:${directionTitle}(${statusLabel})`,
|
||
occurredAt: log.createdAt,
|
||
metadata: {
|
||
directionTaskId: log.directionTaskId,
|
||
directionTitle: log.directionTitle,
|
||
coverageStatus: log.coverageStatus,
|
||
completedContent: log.completedContent,
|
||
remainingContent: log.remainingContent,
|
||
workStartedAt: log.workStartedAt,
|
||
},
|
||
};
|
||
}
|
||
|
||
export interface ProgressNoteActivityInput {
|
||
actorId: string;
|
||
sourceType: WorkActivitySourceType;
|
||
sourceId: string;
|
||
title: string;
|
||
note: string;
|
||
nextStartAt: string;
|
||
blocker?: string;
|
||
helperId?: string;
|
||
delayRisk?: string;
|
||
}
|
||
|
||
export function makeProgressNoteActivity(data: ProgressNoteActivityInput): WorkActivityDraft {
|
||
const note = data.note.trim();
|
||
const blocker = data.blocker?.trim() || undefined;
|
||
const helperId = data.helperId?.trim() || undefined;
|
||
const delayRisk = data.delayRisk?.trim() || undefined;
|
||
const nextStartAt = data.nextStartAt.trim();
|
||
const details = [
|
||
note,
|
||
blocker ? `阻塞:${blocker}` : '',
|
||
helperId ? `需协助:${helperId}` : '',
|
||
delayRisk ? `延期风险:${delayRisk}` : '',
|
||
].filter(Boolean);
|
||
|
||
return {
|
||
actorId: data.actorId,
|
||
sourceType: data.sourceType,
|
||
sourceId: data.sourceId,
|
||
action: 'progress_note_added',
|
||
category: blocker || delayRisk ? 'risk' : 'note',
|
||
title: data.title,
|
||
summary: `补充进展:${details.join(';')}`,
|
||
metadata: {
|
||
note,
|
||
blocker,
|
||
helperId,
|
||
delayRisk,
|
||
nextStartAt: nextStartAt || undefined,
|
||
},
|
||
};
|
||
}
|
||
|
||
export function makeDevTaskCreatedActivity(task: DevTask, actorId: string): WorkActivityDraft {
|
||
return {
|
||
actorId,
|
||
sourceType: 'dev_task',
|
||
sourceId: task.id,
|
||
action: 'dev_task_created',
|
||
category: 'creation',
|
||
title: task.title,
|
||
summary: `新建开发任务:${task.title}`,
|
||
};
|
||
}
|
||
|
||
export function makeDevTaskStatusActivity(
|
||
task: DevTask,
|
||
fromStatus: DevTaskStatus,
|
||
toStatus: DevTaskStatus,
|
||
actorId: string,
|
||
): WorkActivityDraft | undefined {
|
||
const base = {
|
||
actorId,
|
||
sourceType: 'dev_task' as const,
|
||
sourceId: task.id,
|
||
title: task.title,
|
||
metadata: { fromStatus, toStatus },
|
||
};
|
||
|
||
if (toStatus === 'in_progress') {
|
||
return {
|
||
...base,
|
||
action: 'dev_task_started',
|
||
category: 'progress',
|
||
summary: `开始开发:${task.title}`,
|
||
};
|
||
}
|
||
|
||
if (toStatus === 'testing') {
|
||
return {
|
||
...base,
|
||
action: 'dev_task_self_testing',
|
||
category: 'progress',
|
||
summary: `进入自测:${task.title}`,
|
||
};
|
||
}
|
||
|
||
if (toStatus === 'submitted') {
|
||
return {
|
||
...base,
|
||
action: 'dev_task_submitted',
|
||
category: 'delivery',
|
||
summary: `已提测开发任务:${task.title}`,
|
||
};
|
||
}
|
||
|
||
return undefined;
|
||
}
|
||
|
||
export function makeDevTaskBlockedActivity(
|
||
task: DevTask,
|
||
actorId: string,
|
||
reason?: string,
|
||
helperId?: string,
|
||
): WorkActivityDraft {
|
||
return {
|
||
actorId,
|
||
sourceType: 'dev_task',
|
||
sourceId: task.id,
|
||
action: 'dev_task_blocked',
|
||
category: 'risk',
|
||
title: task.title,
|
||
summary: `标记阻塞:${reason?.trim() || task.title}`,
|
||
metadata: {
|
||
blocker: reason?.trim() || undefined,
|
||
helperId: helperId?.trim() || undefined,
|
||
},
|
||
};
|
||
}
|
||
|
||
export function makeDevTaskUnblockedActivity(task: DevTask, actorId: string): WorkActivityDraft {
|
||
return {
|
||
actorId,
|
||
sourceType: 'dev_task',
|
||
sourceId: task.id,
|
||
action: 'dev_task_unblocked',
|
||
category: 'progress',
|
||
title: task.title,
|
||
summary: `解除阻塞:${task.title}`,
|
||
};
|
||
}
|
||
|
||
export function makeBugCreatedActivity(bug: Bug, actorId: string): WorkActivityDraft {
|
||
return {
|
||
actorId,
|
||
sourceType: 'bug',
|
||
sourceId: bug.id,
|
||
action: 'bug_created',
|
||
category: 'creation',
|
||
title: bug.title,
|
||
summary: `新建 Bug:${bug.title}`,
|
||
};
|
||
}
|
||
|
||
export function makeTestCaseCreatedActivity(testCase: TestCase, actorId: string): WorkActivityDraft {
|
||
return {
|
||
actorId,
|
||
sourceType: 'test_case',
|
||
sourceId: testCase.id,
|
||
action: 'test_case_created',
|
||
category: 'creation',
|
||
title: testCase.title,
|
||
summary: `新建测试用例:${testCase.title}`,
|
||
};
|
||
}
|
||
|
||
export function makeTestCaseStatusActivity(
|
||
testCase: TestCase,
|
||
fromStatus: TestCaseStatus,
|
||
toStatus: TestCaseStatus,
|
||
actorId: string,
|
||
): WorkActivityDraft | undefined {
|
||
const base = {
|
||
actorId,
|
||
sourceType: 'test_case' as const,
|
||
sourceId: testCase.id,
|
||
title: testCase.title,
|
||
metadata: { fromStatus, toStatus },
|
||
};
|
||
|
||
if (toStatus === 'running') {
|
||
return {
|
||
...base,
|
||
action: 'test_case_started',
|
||
category: 'progress',
|
||
summary: `开始测试:${testCase.title}`,
|
||
};
|
||
}
|
||
|
||
if (toStatus === 'passed') {
|
||
return {
|
||
...base,
|
||
action: 'test_case_passed',
|
||
category: 'delivery',
|
||
summary: `测试通过:${testCase.title}`,
|
||
};
|
||
}
|
||
|
||
if (toStatus === 'failed') {
|
||
return {
|
||
...base,
|
||
action: 'test_case_failed',
|
||
category: 'risk',
|
||
summary: `测试不通过:${testCase.title}`,
|
||
};
|
||
}
|
||
|
||
if (toStatus === 'blocked') {
|
||
return {
|
||
...base,
|
||
action: 'test_case_blocked',
|
||
category: 'risk',
|
||
summary: `测试阻塞:${testCase.title}`,
|
||
};
|
||
}
|
||
|
||
return undefined;
|
||
}
|
||
|
||
export function makeBugStatusActivity(
|
||
bug: Bug,
|
||
fromStatus: BugStatus,
|
||
toStatus: BugStatus,
|
||
actorId: string,
|
||
): WorkActivityDraft | undefined {
|
||
const base = {
|
||
actorId,
|
||
sourceType: 'bug' as const,
|
||
sourceId: bug.id,
|
||
title: bug.title,
|
||
metadata: { fromStatus, toStatus },
|
||
};
|
||
|
||
if (toStatus === 'fixing') {
|
||
return {
|
||
...base,
|
||
action: 'bug_fixing',
|
||
category: 'progress',
|
||
summary: `开始修复 Bug:${bug.title}`,
|
||
};
|
||
}
|
||
|
||
if (toStatus === 'fixed') {
|
||
return {
|
||
...base,
|
||
action: 'bug_fixed',
|
||
category: 'delivery',
|
||
summary: `已修复 Bug:${bug.title}`,
|
||
};
|
||
}
|
||
|
||
if (toStatus === 'closed') {
|
||
return {
|
||
...base,
|
||
action: 'bug_closed',
|
||
category: 'delivery',
|
||
summary: `已关闭 Bug:${bug.title}`,
|
||
};
|
||
}
|
||
|
||
return undefined;
|
||
}
|
||
|
||
export function makeBugTransferredActivity(bug: Bug, actorId: string, toAssigneeId: string): WorkActivityDraft {
|
||
return {
|
||
actorId,
|
||
sourceType: 'bug',
|
||
sourceId: bug.id,
|
||
action: 'bug_transferred',
|
||
category: 'progress',
|
||
title: bug.title,
|
||
summary: `转交 Bug:${bug.title} → ${toAssigneeId}`,
|
||
metadata: { toAssigneeId },
|
||
};
|
||
}
|