Files
2026Technology-Competition/docs/superpowers/specs/implementation-steps/14-phase4.6-report-export.md
T

143 lines
4.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Step 14 — Phase 4.6: 报告导出
**依赖**: Step 12Merger
**参考设计**: §14.2
## 目标
`MergedReport` 导出为 Markdown 格式报告。
## 新建文件
| # | 文件 | 说明 |
|---|------|------|
| 1 | `src/utils/report.ts` | `reportToMarkdown()` |
---
## `src/utils/report.ts`
```typescript
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、codeDiffdiff 代码块)
- 降级和错误信息在报告顶部单独显示
---
## 验收
- [ ] 文件创建完成
- [ ] `npm run compile` 通过
- [ ] `npm run lint` 通过