4.1 KiB
4.1 KiB
Step 14 — Phase 4.6: 报告导出
依赖: Step 12(Merger)
参考设计: §14.2
目标
将 MergedReport 导出为 Markdown 格式报告。
新建文件
| # | 文件 | 说明 |
|---|---|---|
| 1 | src/utils/report.ts |
reportToMarkdown() |
src/utils/report.ts
import { MergedReport } from '../merger/merger';
function severityEmoji(severity: string): string {
switch (severity) {
case 'error': return '🔴';
case 'warning': return '🟡';
case 'info': return '🔵';
default: return '⚪';
}
}
function formatLine(line: number): string {
return `L${line + 1}`;
}
export function reportToMarkdown(report: MergedReport): string {
const lines: string[] = [];
lines.push('# 代码审查报告');
lines.push('');
lines.push(`**文件:** \`${report.filePath}\``);
lines.push(`**语言:** ${report.language}`);
lines.push(`**耗时:** ${(report.duration / 1000).toFixed(1)}s`);
if (report.adapterNames.length > 0) {
lines.push(`**分析工具:** ${report.adapterNames.join(', ')}`);
}
if (report.degraded) {
lines.push('');
lines.push('> ⚠️ 部分 AI 功能不可用,报告已降级');
}
if (report.errors.length > 0) {
lines.push('');
lines.push('## 错误');
for (const err of report.errors) {
lines.push(`- ${err}`);
}
}
lines.push('');
lines.push('---');
lines.push('');
const total = report.linterCount + report.customRuleCount + report.aiCount;
const errors = report.linterDiagnostics.filter(d => d.severity === 'error').length
+ report.customRuleDiagnostics.filter(d => d.severity === 'error').length
+ report.aiFindings.filter(f => f.severity === 'error').length;
const warnings = report.linterDiagnostics.filter(d => d.severity === 'warning').length
+ report.customRuleDiagnostics.filter(d => d.severity === 'warning').length
+ report.aiFindings.filter(f => f.severity === 'warning').length;
const infos = total - errors - warnings;
lines.push(`**总计:** ${total} | **错误:** ${errors} | **警告:** ${warnings} | **建议:** ${infos}`);
lines.push('');
if (report.linterDiagnostics.length > 0) {
lines.push(`## 🔧 静态分析 · ${report.linterCount} 个问题`);
lines.push('');
for (const diag of report.linterDiagnostics) {
lines.push(`- ${severityEmoji(diag.severity)} \`${diag.ruleId}\` ${formatLine(diag.range.start.line)}`);
lines.push(` ${diag.message}`);
if (diag.suggestion) {
lines.push(` 建议: ${diag.suggestion}`);
}
}
lines.push('');
}
if (report.customRuleDiagnostics.length > 0) {
lines.push(`## 📋 自定义规则 · ${report.customRuleCount} 个问题`);
lines.push('');
for (const diag of report.customRuleDiagnostics) {
lines.push(`- ${severityEmoji(diag.severity)} \`${diag.ruleId}\` ${formatLine(diag.range.start.line)}`);
lines.push(` ${diag.message}`);
}
lines.push('');
}
if (report.aiFindings.length > 0) {
lines.push(`## 🤖 AI 审查 · ${report.aiCount} 条建议`);
lines.push('');
for (const finding of report.aiFindings) {
lines.push(`- ${severityEmoji(finding.severity)} [AI] [${finding.category}] \`${finding.ruleId}\` ${formatLine(finding.line)}`);
lines.push(` **${finding.title}**`);
lines.push(` ${finding.description}`);
if (finding.suggestion) {
lines.push(` 建议: ${finding.suggestion}`);
}
if (finding.codeDiff) {
lines.push(' ```diff');
lines.push(` ${finding.codeDiff.split('\n').join('\n ')}`);
lines.push(' ```');
}
}
lines.push('');
}
if (total === 0) {
lines.push('✅ 未发现问题');
lines.push('');
}
return lines.join('\n');
}
关键逻辑
- 报告格式与设计 §14.2 一致
- 按来源分三个段:静态分析 / 自定义规则 / AI 审查
- 统计卡片:总计 + 按严重级别拆分
- AI 审查结果包含 category、codeDiff(diff 代码块)
- 降级和错误信息在报告顶部单独显示
验收
- 文件创建完成
npm run compile通过npm run lint通过