63 lines
1.4 KiB
TypeScript
63 lines
1.4 KiB
TypeScript
import type { Bug, BugLog, BugStatus } from './bug';
|
|
import { canBugTransition } from './bug';
|
|
|
|
export interface BugTransitionOptions {
|
|
now?: Date;
|
|
resolution?: string;
|
|
}
|
|
|
|
export interface BugWorkflowResult {
|
|
ok: boolean;
|
|
patch?: Partial<Bug>;
|
|
message?: string;
|
|
}
|
|
|
|
function makeBugLog(
|
|
action: BugLog['action'],
|
|
operator: string,
|
|
nowIso: string,
|
|
from?: string,
|
|
to?: string,
|
|
remark?: string,
|
|
): BugLog {
|
|
return {
|
|
id: `log-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`,
|
|
action,
|
|
fromValue: from,
|
|
toValue: to,
|
|
operator,
|
|
remark,
|
|
createdAt: nowIso,
|
|
};
|
|
}
|
|
|
|
export function applyBugTransition(
|
|
bug: Bug,
|
|
to: BugStatus,
|
|
operator: string,
|
|
options: BugTransitionOptions = {},
|
|
): BugWorkflowResult {
|
|
if (!canBugTransition(bug.status, to)) {
|
|
return { ok: false, message: `不允许从「${bug.status}」流转到「${to}」` };
|
|
}
|
|
|
|
const nowIso = (options.now ?? new Date()).toISOString();
|
|
const patch: Partial<Bug> = { status: to };
|
|
|
|
if (to === 'fixed') patch.resolvedAt = nowIso;
|
|
if (to === 'closed') patch.closedAt = nowIso;
|
|
if (options.resolution?.trim()) patch.resolution = options.resolution.trim();
|
|
|
|
const log = makeBugLog(
|
|
to === 'fixed' ? 'resolve' : 'status_change',
|
|
operator,
|
|
nowIso,
|
|
bug.status,
|
|
to,
|
|
options.resolution,
|
|
);
|
|
patch.logs = [...(bug.logs || []), log];
|
|
|
|
return { ok: true, patch };
|
|
}
|