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>
This commit is contained in:
@@ -6,7 +6,9 @@ export type BugSeverity = 'critical' | 'major' | 'minor' | 'trivial';
|
||||
export interface Bug {
|
||||
id: string;
|
||||
bugNo: string;
|
||||
versionId: string;
|
||||
testCaseId: string;
|
||||
requirementId?: string;
|
||||
title: string;
|
||||
description: string;
|
||||
severity: BugSeverity;
|
||||
|
||||
@@ -5,7 +5,8 @@ export type TestCaseStatus = 'pending' | 'running' | 'passed' | 'failed' | 'bloc
|
||||
export interface TestCase {
|
||||
id: string;
|
||||
caseNo: string;
|
||||
requirementId: string;
|
||||
versionId: string;
|
||||
requirementId?: string;
|
||||
title: string;
|
||||
description?: string;
|
||||
priority: Priority;
|
||||
|
||||
@@ -40,3 +40,66 @@ export function getVersionDisplayStatus(status: VersionStatus, currentStage?: st
|
||||
}
|
||||
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';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user