Files
ftb-project-management/apps/web/lib/version-status.ts
Script Generator 93f6c33109 refactor: 压平数据模型 — 版本为执行主线,需求降为语义标签
核心改动:
- TestCase 主归属 versionId,requirementId 改为可选语义标签
- Bug 新增 versionId(直接挂版本)+ requirementId(可选追溯)
- 新增 calcVersionExecutionStatus() 版本执行态推导函数
  规则:DevTask全部提测→已提测,TestCase全部通过+Bug全关→可发布
- 概览页展示推导出的执行态标签
- BugTab 改为直接 versionId 查询(不再走 testCase→req 链路)
- 统计卡片"参与人员"替换为"未关闭Bug"

心智模型:
  版本(执行线)→ DevTask/TestCase/Bug
  需求(语义线)→ 解释为什么做,不驱动流程

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-12 14:07:46 +08:00

106 lines
3.4 KiB
TypeScript

export type VersionStatus = 'developing' | 'planned' | 'released' | 'paused' | 'closed';
export const VERSION_STATUS_LABEL: Record<VersionStatus, string> = {
developing: '进行中',
planned: '规划中',
released: '已发布',
paused: '已暂停',
closed: '已关闭',
};
export const VERSION_STATUS_DOT: Record<VersionStatus, string> = {
developing: 'bg-blue-500',
planned: 'bg-orange-500',
released: 'bg-zinc-300',
paused: 'bg-purple-400',
closed: 'bg-zinc-400',
};
export const VERSION_STATUS_BG: Record<VersionStatus, string> = {
developing: 'bg-blue-500/10 text-blue-600',
planned: 'bg-orange-500/10 text-orange-600',
released: 'bg-zinc-100 text-zinc-600',
paused: 'bg-purple-100 text-purple-600',
closed: 'bg-zinc-100 text-zinc-500',
};
export const STAGE_LABEL: Record<string, string> = {
requirement: '调研中',
product_design: '产品设计中',
ui_design: 'UI设计中',
dev: '开发中',
integration: '联调中',
testing: '测试中',
released: '已发布',
};
export function getVersionDisplayStatus(status: VersionStatus, currentStage?: string): string {
if (status === 'developing' && currentStage && STAGE_LABEL[currentStage]) {
return STAGE_LABEL[currentStage];
}
return VERSION_STATUS_LABEL[status];
}
// === 版本执行态推导 ===
export type VersionExecutionStatus = 'planning' | 'developing' | 'submitted' | 'testing' | 'ready' | 'released';
export const EXECUTION_STATUS_LABEL: Record<VersionExecutionStatus, string> = {
planning: '规划中',
developing: '开发中',
submitted: '已提测',
testing: '测试中',
ready: '可发布',
released: '已发布',
};
export const EXECUTION_STATUS_COLOR: Record<VersionExecutionStatus, string> = {
planning: 'bg-zinc-100 text-zinc-600',
developing: 'bg-blue-50 text-blue-600',
submitted: 'bg-orange-50 text-orange-600',
testing: 'bg-purple-50 text-purple-600',
ready: 'bg-emerald-50 text-emerald-600',
released: 'bg-zinc-100 text-zinc-500',
};
export interface ExecutionInput {
manualStatus?: VersionStatus;
devTasks: { status: string }[];
testCases: { status: string }[];
bugs: { status: string }[];
}
export function calcVersionExecutionStatus(input: ExecutionInput): VersionExecutionStatus {
const { manualStatus, devTasks, testCases, bugs } = input;
if (manualStatus === 'released') return 'released';
if (manualStatus === 'planned' || manualStatus === 'paused' || manualStatus === 'closed') return 'planning';
// fallback: 没有 DevTask 则停留 planning
if (devTasks.length === 0) return 'planning';
const allDevDone = devTasks.every((t) => t.status === 'done');
const allDevSubmitted = devTasks.every((t) => t.status === 'submitted' || t.status === 'done');
// 判断是否可发布
if (testCases.length > 0) {
const allPassed = testCases.every((c) => c.status === 'passed');
const allBugsClosed = bugs.every((b) => b.status === 'closed' || b.status === 'rejected');
if (allPassed && allBugsClosed) return 'ready';
}
// 测试中(有用例在执行)
if (allDevSubmitted && testCases.length > 0) {
const hasRunning = testCases.some((c) => c.status === 'running' || c.status === 'passed' || c.status === 'failed' || c.status === 'blocked');
if (hasRunning) return 'testing';
}
// 已提测
if (allDevSubmitted) return 'submitted';
// 开发完成但未全部提测
if (allDevDone) return 'submitted';
return 'developing';
}