Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 44 additions & 4 deletions src/cli/commands/analyze.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
import { printTrendReport } from './report/trend-printer'
import { printTeamAnalysis } from './report/printers/user-analysis-printer'
import { ensureCommitSamples } from '../common/commit-guard'
import { exportReport, ExportData, ExportFormat } from '../../utils/exporter'

type TimeRangeMode = 'all-time' | 'custom' | 'auto-last-commit' | 'fallback'

Expand Down Expand Up @@ -170,12 +171,13 @@ export class AnalyzeExecutor {
const isOpenSource = classification.projectType === ProjectType.OPEN_SOURCE

// ========== 步骤 4: 月度趋势分析 ==========
let trendResult: Awaited<ReturnType<typeof TrendAnalyzer.analyzeTrend>> | undefined
// 只有在分析时间跨度超过1个月时才显示趋势分析
if (effectiveSince && effectiveUntil && shouldShowTrendAnalysis(effectiveSince, effectiveUntil)) {
console.log()
const trendSpinner = ora('📈 正在进行月度趋势分析...').start()
try {
const trendResult = await TrendAnalyzer.analyzeTrend(
trendResult = await TrendAnalyzer.analyzeTrend(
path,
effectiveSince,
effectiveUntil,
Expand All @@ -195,20 +197,21 @@ export class AnalyzeExecutor {
}

// ========== 步骤 5: 团队工作模式分析 ==========
let teamResult: Awaited<ReturnType<typeof GitTeamAnalyzer.analyzeTeam>> | undefined
// 开源项目不显示团队工作模式分析
if (!isOpenSource && GitTeamAnalyzer.shouldAnalyzeTeam(options)) {
try {
const maxUsers = options.maxUsers ? parseInt(String(options.maxUsers), 10) : 30
const teamAnalysis = await GitTeamAnalyzer.analyzeTeam(
teamResult = await GitTeamAnalyzer.analyzeTeam(
collectOptions,
result.index996,
20, // minCommits
maxUsers,
false // silent
)

if (teamAnalysis) {
printTeamAnalysis(teamAnalysis)
if (teamResult) {
printTeamAnalysis(teamResult)
}
} catch (error) {
console.log(chalk.yellow('⚠️ 团队分析失败:'), (error as Error).message)
Expand All @@ -224,6 +227,43 @@ export class AnalyzeExecutor {
console.log(chalk.yellow(warningMessage))
}
}

// ========== 步骤 7: 导出报告 ==========
if (options.export) {
const format = options.export.toLowerCase() as ExportFormat
if (format !== 'json' && format !== 'markdown') {
console.error(chalk.red('❌ 不支持的导出格式:'), options.export, chalk.gray('(仅支持 json 或 markdown)'))
return
}
const repoName = path.split('/').filter(Boolean).pop() || 'unknown'
const defaultExt = format === 'json' ? 'json' : 'md'
const defaultName = `report.${defaultExt}`
const outputPath = options.output || defaultName

const exportData: ExportData = {
repoName,
repoPath: path,
generatedAt: new Date().toISOString(),
options: {
since: effectiveSince,
until: effectiveUntil,
self: options.self,
hours: options.hours,
halfHour: options.halfHour,
ignoreAuthor: options.ignoreAuthor,
ignoreMsg: options.ignoreMsg,
timezone: options.timezone,
},
result,
parsedData,
rawData,
classification,
trendResult,
teamAnalysis: teamResult ?? undefined,
}

exportReport(exportData, format, outputPath)
}
} catch (error) {
console.error(chalk.red('❌ 分析失败:'), (error as Error).message)
process.exit(1)
Expand Down
71 changes: 67 additions & 4 deletions src/cli/commands/multi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
} from './report'
import { printTrendReport } from './report/trend-printer'
import { printTeamAnalysis } from './report/printers/user-analysis-printer'
import { exportReport, ExportData, ExportFormat, MultiExportData } from '../../utils/exporter'

/**
* 判断是否应该启用节假日调休模式
Expand Down Expand Up @@ -317,6 +318,7 @@ export class MultiExecutor {
MultiComparisonPrinter.print(repoRecords)

// ========== 步骤 8: 月度趋势分析(默认启用) ==========
let trendResult: Awaited<ReturnType<typeof TrendAnalyzer.analyzeMultiRepoTrend>> | undefined
if (selectedRepos.length > 0) {
console.log()
const trendSpinner = ora('📈 正在进行多仓库汇总月度趋势分析...').start()
Expand All @@ -330,7 +332,7 @@ export class MultiExecutor {
trendSpinner.warn('没有成功的仓库数据,跳过趋势分析')
} else {
// 使用新的多仓库汇总趋势分析方法
const trendResult = await TrendAnalyzer.analyzeMultiRepoTrend(
trendResult = await TrendAnalyzer.analyzeMultiRepoTrend(
successfulRepoPaths,
effectiveSince ?? null,
effectiveUntil ?? null,
Expand All @@ -353,6 +355,7 @@ export class MultiExecutor {

// ========== 步骤 9: 团队工作模式分析(聚合所有仓库的数据)==========
// 开源项目不显示团队工作模式分析
let teamResult: Awaited<ReturnType<typeof MultiRepoTeamAnalyzer.analyzeAggregatedTeam>> | undefined
if (!hasOpenSourceProject && GitTeamAnalyzer.shouldAnalyzeTeam(options) && selectedRepos.length > 0) {
// 收集所有成功分析的仓库路径
const successfulRepoPaths = selectedRepos
Expand All @@ -374,16 +377,16 @@ export class MultiExecutor {
}

const maxUsers = options.maxUsers ? parseInt(String(options.maxUsers), 10) : 30
const teamAnalysis = await MultiRepoTeamAnalyzer.analyzeAggregatedTeam(
teamResult = await MultiRepoTeamAnalyzer.analyzeAggregatedTeam(
successfulRepoPaths,
collectOptions,
20, // minCommits(所有仓库总计≥20)
maxUsers,
result.index996 // 整体996指数
)

if (teamAnalysis) {
printTeamAnalysis(teamAnalysis)
if (teamResult) {
printTeamAnalysis(teamResult)
}
} catch (error) {
console.log(chalk.yellow('⚠️ 团队分析失败:'), (error as Error).message)
Expand All @@ -400,6 +403,66 @@ export class MultiExecutor {
console.log(chalk.yellow(warningMessage))
}
}

// ========== 步骤 11: 导出报告 ==========
if (options.export) {
const format = options.export.toLowerCase() as ExportFormat
if (format !== 'json' && format !== 'markdown') {
console.error(chalk.red('❌ 不支持的导出格式:'), options.export, chalk.gray('(仅支持 json 或 markdown)'))
return
}
const defaultExt = format === 'json' ? 'json' : 'md'
const defaultName = `report.${defaultExt}`
const outputPath = options.output || defaultName

const successfulRepoRecords = repoRecords.filter((r) => r.status === 'success')
const repoExportPromises = successfulRepoRecords.map(async (record) => {
const shouldEnableHolidayRepo = shouldEnableHolidayMode(record.data, options)
const parsedRepoData = await GitParser.parseGitData(
record.data,
options.hours,
effectiveSince,
effectiveUntil,
shouldEnableHolidayRepo.enabled
)
return {
repoName: record.repo.name,
repoPath: record.repo.path,
generatedAt: new Date().toISOString(),
options: {
since: effectiveSince,
until: effectiveUntil,
self: options.self,
hours: options.hours,
},
result: record.result,
parsedData: parsedRepoData,
rawData: record.data,
classification: record.classification,
} as ExportData
})

const resolvedRepos = await Promise.all(repoExportPromises)

const multiExportData: MultiExportData = {
generatedAt: new Date().toISOString(),
options: {
since: effectiveSince,
until: effectiveUntil,
self: options.self,
hours: options.hours,
},
repos: resolvedRepos,
mergedResult: result,
mergedParsedData: parsedData,
mergedRawData: mergedData,
repoRecords: successfulRepoRecords,
trendResult,
teamAnalysis: teamResult ?? undefined,
}

exportReport(multiExportData, format, outputPath)
}
} catch (error) {
console.error(chalk.red('❌ 多仓库分析失败:'), (error as Error).message)
process.exit(1)
Expand Down
13 changes: 13 additions & 0 deletions src/cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ export class CLIManager {
.option('--cn', '强制开启中国节假日调休模式(自动检测 +0800 时区)')
.option('--skip-user-analysis', '跳过团队工作模式分析')
.option('--max-users <number>', '最大分析用户数(默认30)', '30')
.option('-e, --export <format>', '导出报告格式 (json 或 markdown)')
.option('-o, --output <path>', '导出文件路径 (默认: report.json 或 report.md)')
.action(async (paths: string[], options: AnalyzeOptions, command: Command) => {
const mergedOptions = this.mergeGlobalOptions(options)

Expand Down Expand Up @@ -180,6 +182,8 @@ export class CLIManager {
ignoreAuthor: options.ignoreAuthor ?? globalOpts.ignoreAuthor,
ignoreMsg: options.ignoreMsg ?? globalOpts.ignoreMsg,
timezone: options.timezone ?? globalOpts.timezone,
export: options.export ?? globalOpts.export,
output: options.output ?? globalOpts.output,
}
}

Expand Down Expand Up @@ -325,6 +329,10 @@ ${chalk.bold('分析选项:')}
--ignore-author <regex> 排除匹配的作者 (例如: bot|jenkins)
--ignore-msg <regex> 排除匹配的提交消息 (例如: merge|lint)

${chalk.bold('导出选项:')}
-e, --export <format> 导出报告格式 (json 或 markdown)
-o, --output <path> 导出文件路径 (默认: report.json 或 report.md)

${chalk.bold('默认策略:')}
自动以最后一次提交为基准,回溯365天进行分析

Expand All @@ -347,6 +355,11 @@ ${chalk.bold('示例:')}
code996 --ignore-msg "^Merge" # 排除所有以 "Merge" 开头的提交消息
code996 --ignore-msg "merge|lint|format" # 排除多个关键词

${chalk.gray('# 导出报告')}
code996 -e json # 导出 JSON 格式
code996 -e markdown # 导出 Markdown 格式
code996 -e json -o data.json # 导出到指定路径

${chalk.bold('正则表达式语法说明:')}
- 使用 | 分隔多个模式 (例如: bot|jenkins)
- 使用 ^ 匹配开头 (例如: ^Merge)
Expand Down
2 changes: 2 additions & 0 deletions src/types/git-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,8 @@ export interface AnalyzeOptions {
skipUserAnalysis?: boolean // 是否跳过团队工作模式分析
maxUsers?: number // 最大分析用户数(默认30)
cn?: boolean // 强制开启中国节假日调休模式
export?: string // 导出格式 (json 或 markdown)
output?: string // 导出文件路径
}

/**
Expand Down
Loading