export type DevPlanTaskStatus = 'TODO' | 'IN_PROGRESS' | 'DEVELOPED' | 'RELEASED'; export type DevPlanVersionStatus = 'PENDING' | 'IN_PROGRESS' | 'TESTING' | 'RELEASED'; 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 = { 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'; } } /** 任务挂多个版本时取进度最高的对应状态 */ export function resolveTaskStatusFromVersions( versionStatuses: DevPlanVersionStatus[], ): DevPlanTaskStatus | null { if (!versionStatuses.length) return null; return versionStatuses .map(taskStatusFromVersionStatus) .reduce((highest, current) => TASK_STATUS_RANK[current] > TASK_STATUS_RANK[highest] ? current : highest, ); }