关键改动: - 增加版本表单、发布校验和调研方向进度规则 - 扩展小宝预警已读状态、风险签名和今日证据 - 补充组长权限、加班查看范围、活动记录与相关测试 Co-Authored-By: Codex GPT-5 <codex@openai.com>
116 lines
3.8 KiB
TypeScript
116 lines
3.8 KiB
TypeScript
import { isChinaWorkday } from './china-workday-calendar';
|
|
import { formatLocalDate } from './format';
|
|
|
|
export type VersionDevelopmentType = 'agile' | 'standard' | 'waterfall' | 'hotfix';
|
|
|
|
export interface VersionDevelopmentStageEstimate {
|
|
key: 'product_design' | 'development' | 'testing';
|
|
label: '产品设计' | '开发' | '测试';
|
|
workDays: number;
|
|
}
|
|
|
|
export interface VersionDevelopmentTypeOption {
|
|
key: VersionDevelopmentType;
|
|
label: string;
|
|
description: string;
|
|
stages: VersionDevelopmentStageEstimate[];
|
|
totalWorkDays: number;
|
|
}
|
|
|
|
export interface NewVersionFormState {
|
|
productId: string;
|
|
projectId: string;
|
|
versionNumber: string;
|
|
developmentType: VersionDevelopmentType | '';
|
|
expectedReleaseDate: string;
|
|
submitting: boolean;
|
|
}
|
|
|
|
export const VERSION_DEVELOPMENT_TYPE_OPTIONS: VersionDevelopmentTypeOption[] = [
|
|
makeDevelopmentTypeOption('agile', '敏捷迭代', '适合常规双周迭代', [
|
|
{ key: 'product_design', label: '产品设计', workDays: 2 },
|
|
{ key: 'development', label: '开发', workDays: 8 },
|
|
{ key: 'testing', label: '测试', workDays: 3 },
|
|
]),
|
|
makeDevelopmentTypeOption('standard', '常规项目', '适合需求较完整的普通版本', [
|
|
{ key: 'product_design', label: '产品设计', workDays: 3 },
|
|
{ key: 'development', label: '开发', workDays: 12 },
|
|
{ key: 'testing', label: '测试', workDays: 4 },
|
|
]),
|
|
makeDevelopmentTypeOption('waterfall', '瀑布项目', '适合方案先行的大范围交付', [
|
|
{ key: 'product_design', label: '产品设计', workDays: 5 },
|
|
{ key: 'development', label: '开发', workDays: 20 },
|
|
{ key: 'testing', label: '测试', workDays: 7 },
|
|
]),
|
|
makeDevelopmentTypeOption('hotfix', '紧急修复', '适合线上缺陷或很小范围改动', [
|
|
{ key: 'product_design', label: '产品设计', workDays: 0 },
|
|
{ key: 'development', label: '开发', workDays: 2 },
|
|
{ key: 'testing', label: '测试', workDays: 1 },
|
|
]),
|
|
];
|
|
|
|
export function canSubmitNewVersionForm(state: NewVersionFormState): boolean {
|
|
return Boolean(
|
|
state.productId &&
|
|
state.projectId &&
|
|
state.versionNumber &&
|
|
state.developmentType &&
|
|
state.expectedReleaseDate &&
|
|
!state.submitting,
|
|
);
|
|
}
|
|
|
|
export function getVersionDevelopmentTypeOption(type: VersionDevelopmentType): VersionDevelopmentTypeOption | undefined {
|
|
return VERSION_DEVELOPMENT_TYPE_OPTIONS.find((option) => option.key === type);
|
|
}
|
|
|
|
export function getRecommendedExpectedReleaseDate(
|
|
type: VersionDevelopmentType,
|
|
baseDate: Date = new Date(),
|
|
options: { productDesignCompleted?: boolean } = {},
|
|
): string {
|
|
const developmentType = getVersionDevelopmentTypeOption(type);
|
|
if (!developmentType) return '';
|
|
const totalWorkDays = getRecommendedWorkDays(developmentType, options);
|
|
return addChinaWorkDays(baseDate, totalWorkDays);
|
|
}
|
|
|
|
export function getRecommendedWorkDays(
|
|
option: VersionDevelopmentTypeOption,
|
|
options: { productDesignCompleted?: boolean } = {},
|
|
): number {
|
|
return option.stages.reduce((sum, stage) => {
|
|
if (stage.key === 'product_design' && options.productDesignCompleted) return sum;
|
|
return sum + stage.workDays;
|
|
}, 0);
|
|
}
|
|
|
|
function makeDevelopmentTypeOption(
|
|
key: VersionDevelopmentType,
|
|
label: string,
|
|
description: string,
|
|
stages: VersionDevelopmentStageEstimate[],
|
|
): VersionDevelopmentTypeOption {
|
|
return {
|
|
key,
|
|
label,
|
|
description,
|
|
stages,
|
|
totalWorkDays: stages.reduce((sum, stage) => sum + stage.workDays, 0),
|
|
};
|
|
}
|
|
|
|
function addChinaWorkDays(baseDate: Date, workDays: number): string {
|
|
const cursor = new Date(baseDate);
|
|
cursor.setHours(0, 0, 0, 0);
|
|
let remaining = Math.max(1, Math.round(workDays));
|
|
|
|
while (remaining > 0) {
|
|
if (isChinaWorkday(cursor)) remaining -= 1;
|
|
if (remaining <= 0) break;
|
|
cursor.setDate(cursor.getDate() + 1);
|
|
}
|
|
|
|
return formatLocalDate(cursor);
|
|
}
|