Files
dukang/packages/domain/src/dev-plan.ts
T
2026-09-02 06:28:47 +08:00

102 lines
3.0 KiB
TypeScript

export type DevPlanTaskStatus = 'TODO' | 'IN_PROGRESS' | 'DEVELOPED' | 'RELEASED' | 'STOPPED';
export type DevPlanVersionStatus = 'PENDING' | 'IN_PROGRESS' | 'TESTING' | 'RELEASED' | 'STOPPED';
export function computeDurationMinutes(
devStartedAt: Date | null | undefined,
devCompletedAt: Date | null | undefined,
): number | null {
if (!devStartedAt || !devCompletedAt) return null;
const ms = devCompletedAt.getTime() - devStartedAt.getTime();
if (ms < 0) return null;
return Math.round(ms / 60000);
}
export function versionStatusTimestamps(
prev: DevPlanVersionStatus,
next: DevPlanVersionStatus,
current: {
devStartedAt?: Date | null;
devCompletedAt?: Date | null;
releasedAt?: Date | null;
},
): {
devStartedAt?: Date | null;
devCompletedAt?: Date | null;
releasedAt?: Date | null;
durationMinutes?: number | null;
} {
const patch: {
devStartedAt?: Date | null;
devCompletedAt?: Date | null;
releasedAt?: Date | null;
durationMinutes?: number | null;
} = {};
const now = new Date();
if (next === 'IN_PROGRESS' && !current.devStartedAt) {
patch.devStartedAt = now;
}
if (next === 'TESTING' && !current.devCompletedAt) {
patch.devCompletedAt = now;
}
if (next === 'RELEASED' && !current.releasedAt) {
patch.releasedAt = now;
}
const started = patch.devStartedAt ?? current.devStartedAt ?? null;
const completed = patch.devCompletedAt ?? current.devCompletedAt ?? null;
const duration = computeDurationMinutes(started, completed);
if (duration != null) patch.durationMinutes = duration;
void prev;
return patch;
}
export function taskCompletedAtOnStatus(
prev: DevPlanTaskStatus,
next: DevPlanTaskStatus,
currentCompletedAt?: Date | null,
): Date | null | undefined {
if (next === 'RELEASED' && !currentCompletedAt) return new Date();
void prev;
return undefined;
}
const TASK_STATUS_RANK: Record<DevPlanTaskStatus, number> = {
STOPPED: -1,
TODO: 0,
IN_PROGRESS: 1,
DEVELOPED: 2,
RELEASED: 3,
};
/** 版本状态 → 任务状态:待开始/开发中/测试中/已发布/已停止 ↔ 待处理/进行中/已开发/已发布/已停止 */
export function taskStatusFromVersionStatus(status: DevPlanVersionStatus): DevPlanTaskStatus {
switch (status) {
case 'PENDING':
return 'TODO';
case 'IN_PROGRESS':
return 'IN_PROGRESS';
case 'TESTING':
return 'DEVELOPED';
case 'RELEASED':
return 'RELEASED';
case 'STOPPED':
return 'STOPPED';
}
}
/** 任务挂多个版本时取进度最高的对应状态;仅当全部版本已停止时任务才为已停止 */
export function resolveTaskStatusFromVersions(
versionStatuses: DevPlanVersionStatus[],
): DevPlanTaskStatus | null {
if (!versionStatuses.length) return null;
const active = versionStatuses.filter((s) => s !== 'STOPPED');
if (!active.length) return 'STOPPED';
return active
.map(taskStatusFromVersionStatus)
.reduce((highest, current) =>
TASK_STATUS_RANK[current] > TASK_STATUS_RANK[highest] ? current : highest,
);
}