50 lines
1.1 KiB
TypeScript
50 lines
1.1 KiB
TypeScript
import type { DevTask, DevTaskStatus } from './dev-task';
|
|
import { 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}」` };
|
|
}
|
|
|
|
const nowIso = (options.now ?? new Date()).toISOString();
|
|
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 };
|
|
}
|