Files
ftb-project-management/apps/web/lib/dev-task-workflow.ts
2026-06-30 11:00:07 +08:00

58 lines
1.5 KiB
TypeScript

import type { DevTask, DevTaskStatus } from './dev-task';
import { canStartDevTask, canTransition } from './dev-task';
export interface DevTaskWorkflowResult {
ok: boolean;
patch?: Partial<DevTask>;
message?: string;
}
export interface DevTaskTransitionOptions {
now?: Date;
delayReason?: string;
}
export function normalizeDevTaskOnCreate(task: DevTask): DevTask {
return {
...task,
status: 'todo',
actualStartAt: undefined,
actualEndAt: undefined,
};
}
export function applyDevTaskTransition(
task: DevTask,
to: DevTaskStatus,
options: DevTaskTransitionOptions = {},
): DevTaskWorkflowResult {
if (!canTransition(task.status, to)) {
return { ok: false, message: `不允许从「${task.status}」流转到「${to}` };
}
if (to === 'submitted' && task.isBlocked) {
return { ok: false, message: '任务仍处于阻塞中,请先解除阻塞后再提测' };
}
const nowIso = (options.now ?? new Date()).toISOString();
if (to === 'in_progress' && !canStartDevTask(task)) {
return { ok: false, message: '开始开发前需要先领取并填写预计开始和预计截止时间' };
}
const patch: Partial<DevTask> = {
status: to,
aiDraft: false,
};
if (to === 'in_progress' && !task.actualStartAt) {
patch.actualStartAt = nowIso;
if (options.delayReason?.trim()) patch.delayReason = options.delayReason.trim();
}
if (to === 'submitted') {
patch.actualEndAt = nowIso;
}
return { ok: true, patch };
}