import type { VersionPlan } from './version-plan'; export interface PlanResultPayload { resultType?: 'link' | 'file'; resultTitle?: string; resultUrl?: string; resultFileName?: string; resultFileData?: string; } export interface PlanCompletionState { checklistTotal: number; checklistCompleted: number; requirementTotal: number; requirementCompleted: number; hasResult: boolean; canSubmitResult: boolean; canComplete: boolean; missingReasons: string[]; } export function hasPlanResult(plan: PlanResultPayload): boolean { const hasTitle = Boolean(plan.resultTitle?.trim()); if (!hasTitle || !plan.resultType) return false; if (plan.resultType === 'link') return Boolean(plan.resultUrl?.trim()); return Boolean(plan.resultFileData || plan.resultFileName); } function requiresRequirementCoverage(plan: VersionPlan): boolean { return plan.type === 'product' || plan.type === 'ui'; } function requiresChecklist(plan: VersionPlan): boolean { return plan.type === 'research'; } export function getPlanCompletionState(plan: VersionPlan): PlanCompletionState { const tasks = plan.tasks ?? []; const checklistTotal = tasks.length; const checklistCompleted = tasks.filter((task) => task.status === 'completed').length; const linked = plan.linkedRequirementIds ?? []; const completed = new Set(plan.completedRequirementIds ?? []); const requirementTotal = linked.length; const requirementCompleted = linked.filter((id) => completed.has(id)).length; const missingReasons: string[] = []; if (requiresChecklist(plan) && checklistTotal === 0) missingReasons.push('缺少子任务'); if (requiresChecklist(plan) && checklistTotal > 0 && checklistCompleted < checklistTotal) missingReasons.push('子任务未全部完成'); if (requiresRequirementCoverage(plan) && requirementTotal > 0 && requirementCompleted < requirementTotal) { missingReasons.push('关联需求未全部覆盖'); } const canSubmitResult = missingReasons.length === 0; const hasResult = hasPlanResult(plan); if (canSubmitResult && !hasResult) missingReasons.push('尚未提交成果'); return { checklistTotal, checklistCompleted, requirementTotal, requirementCompleted, hasResult, canSubmitResult, canComplete: canSubmitResult && hasResult, missingReasons, }; } export function canTogglePlanChecklist(plan: VersionPlan, now: Date = new Date()): boolean { return plan.status === 'in_progress' || (plan.status === 'pending' && Boolean(plan.startTime) && new Date(plan.startTime) <= now); } export function canEditPlanRequirementCoverage(plan: VersionPlan, now: Date = new Date()): boolean { return canTogglePlanChecklist(plan, now); }