@spaceflow/review 0.60.0 → 0.62.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.61.0](https://github.com/Lydanne/spaceflow/compare/@spaceflow/review@0.60.0...@spaceflow/review@0.61.0) (2026-03-03)
|
|
4
|
+
|
|
5
|
+
### 代码重构
|
|
6
|
+
|
|
7
|
+
* **review:** 优化无效问题统计逻辑,排除已修复和已解决的问题 ([1de7b2a](https://github.com/Lydanne/spaceflow/commit/1de7b2a23fcc3ff73f679fc219342e111d96acf7))
|
|
8
|
+
|
|
9
|
+
### 其他修改
|
|
10
|
+
|
|
11
|
+
* **review-summary:** released version 0.28.0 [no ci] ([e131ed8](https://github.com/Lydanne/spaceflow/commit/e131ed83528ef8b45b3e3edaac5dab3389812323))
|
|
12
|
+
* **scripts:** released version 0.25.0 [no ci] ([88292c0](https://github.com/Lydanne/spaceflow/commit/88292c07b7787bcd4492c8b88cfb516b3e81d9be))
|
|
13
|
+
* **shell:** released version 0.25.0 [no ci] ([fc78e10](https://github.com/Lydanne/spaceflow/commit/fc78e10a0bab04d575867732b922a2a1989a594e))
|
|
14
|
+
|
|
15
|
+
## [0.60.0](https://github.com/Lydanne/spaceflow/compare/@spaceflow/review@0.59.0...@spaceflow/review@0.60.0) (2026-03-02)
|
|
16
|
+
|
|
17
|
+
### 代码重构
|
|
18
|
+
|
|
19
|
+
* **review:** 优化问题验证逻辑,将 resolved 状态纳入有效性判断 ([1e2302d](https://github.com/Lydanne/spaceflow/commit/1e2302d81cd0f653d606483ef6c9138143ca6d60))
|
|
20
|
+
|
|
21
|
+
### 代码格式
|
|
22
|
+
|
|
23
|
+
* 格式化代码并更新 Prettier 忽略规则 ([baed10e](https://github.com/Lydanne/spaceflow/commit/baed10e7cd91fda1285d7e2e0019d291cb563055))
|
|
24
|
+
|
|
25
|
+
### 其他修改
|
|
26
|
+
|
|
27
|
+
* **core:** released version 0.23.0 [no ci] ([07a2d7d](https://github.com/Lydanne/spaceflow/commit/07a2d7d51223aeb98161f91fa931b4cb63b03cda))
|
|
28
|
+
* **publish:** released version 0.47.0 [no ci] ([3f59345](https://github.com/Lydanne/spaceflow/commit/3f593450066c9138adac5b101edb8057a5de1ff6))
|
|
29
|
+
* **review-summary:** released version 0.27.0 [no ci] ([90ac2a4](https://github.com/Lydanne/spaceflow/commit/90ac2a44706ddb2dd231ea57d3734c7445565ee9))
|
|
30
|
+
* **scripts:** released version 0.24.0 [no ci] ([717de65](https://github.com/Lydanne/spaceflow/commit/717de6571faa2cb24f04b7493e7fd6d8404f2bd5))
|
|
31
|
+
* **shell:** released version 0.24.0 [no ci] ([5694d19](https://github.com/Lydanne/spaceflow/commit/5694d193f9207e41e840d9ebaa5a43e3527e6af8))
|
|
32
|
+
|
|
3
33
|
## [0.59.0](https://github.com/Lydanne/spaceflow/compare/@spaceflow/review@0.58.0...@spaceflow/review@0.59.0) (2026-03-02)
|
|
4
34
|
|
|
5
35
|
### 新特性
|
package/dist/index.js
CHANGED
|
@@ -1169,34 +1169,69 @@ class MarkdownFormatter {
|
|
|
1169
1169
|
if (summaries.length === 0) {
|
|
1170
1170
|
return "没有需要审查的文件";
|
|
1171
1171
|
}
|
|
1172
|
+
// 🟢 已修复 | 🔴 待处理error | 🟡 待处理warn | ⚪ 已解决(非代码修复)
|
|
1172
1173
|
const issuesByFile = new Map();
|
|
1173
1174
|
for (const issue of issues){
|
|
1174
1175
|
if (issue.valid === "false") continue;
|
|
1175
1176
|
const stats = issuesByFile.get(issue.file) || {
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1177
|
+
fixed: 0,
|
|
1178
|
+
pendingErrors: 0,
|
|
1179
|
+
pendingWarns: 0,
|
|
1180
|
+
resolved: 0
|
|
1179
1181
|
};
|
|
1180
1182
|
if (issue.fixed) {
|
|
1183
|
+
stats.fixed++;
|
|
1184
|
+
} else if (issue.resolved) {
|
|
1181
1185
|
stats.resolved++;
|
|
1182
1186
|
} else if (issue.severity === "error") {
|
|
1183
|
-
stats.
|
|
1187
|
+
stats.pendingErrors++;
|
|
1184
1188
|
} else {
|
|
1185
|
-
stats.
|
|
1189
|
+
stats.pendingWarns++;
|
|
1186
1190
|
}
|
|
1187
1191
|
issuesByFile.set(issue.file, stats);
|
|
1188
1192
|
}
|
|
1189
1193
|
const lines = [];
|
|
1190
|
-
lines.push("| 文件 | 🟢 | 🔴 | 🟡 |
|
|
1191
|
-
lines.push("
|
|
1194
|
+
lines.push("| 文件 | 总数 | 🟢 | 🔴 | 🟡 | ⚪ |");
|
|
1195
|
+
lines.push("|------|------|----|----|----|-----|");
|
|
1196
|
+
// 汇总统计
|
|
1197
|
+
let totalAll = 0;
|
|
1198
|
+
let totalFixed = 0;
|
|
1199
|
+
let totalPendingErrors = 0;
|
|
1200
|
+
let totalPendingWarns = 0;
|
|
1201
|
+
let totalResolved = 0;
|
|
1202
|
+
const fileSummaryLines = [];
|
|
1192
1203
|
for (const fileSummary of summaries){
|
|
1193
1204
|
const stats = issuesByFile.get(fileSummary.file) || {
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1205
|
+
fixed: 0,
|
|
1206
|
+
pendingErrors: 0,
|
|
1207
|
+
pendingWarns: 0,
|
|
1208
|
+
resolved: 0
|
|
1197
1209
|
};
|
|
1198
|
-
const
|
|
1199
|
-
|
|
1210
|
+
const fileTotal = stats.fixed + stats.pendingErrors + stats.pendingWarns + stats.resolved;
|
|
1211
|
+
totalAll += fileTotal;
|
|
1212
|
+
totalFixed += stats.fixed;
|
|
1213
|
+
totalPendingErrors += stats.pendingErrors;
|
|
1214
|
+
totalPendingWarns += stats.pendingWarns;
|
|
1215
|
+
totalResolved += stats.resolved;
|
|
1216
|
+
lines.push(`| \`${fileSummary.file}\` | ${fileTotal} | ${stats.fixed} | ${stats.pendingErrors} | ${stats.pendingWarns} | ${stats.resolved} |`);
|
|
1217
|
+
// 收集问题总结用于折叠块展示
|
|
1218
|
+
if (fileSummary.summary.trim()) {
|
|
1219
|
+
fileSummaryLines.push(`### \`${fileSummary.file}\``);
|
|
1220
|
+
fileSummaryLines.push(`${fileSummary.summary.trim()}`);
|
|
1221
|
+
fileSummaryLines.push("");
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
// 添加汇总行
|
|
1225
|
+
if (summaries.length > 1) {
|
|
1226
|
+
lines.push(`| **总计** | **${totalAll}** | **${totalFixed}** | **${totalPendingErrors}** | **${totalPendingWarns}** | **${totalResolved}** |`);
|
|
1227
|
+
}
|
|
1228
|
+
// 问题总结放到折叠块中
|
|
1229
|
+
if (fileSummaryLines.length > 0) {
|
|
1230
|
+
lines.push("");
|
|
1231
|
+
lines.push("<details>");
|
|
1232
|
+
lines.push("<summary>📝 问题总结</summary>\n");
|
|
1233
|
+
lines.push(...fileSummaryLines);
|
|
1234
|
+
lines.push("</details>");
|
|
1200
1235
|
}
|
|
1201
1236
|
return lines.join("\n");
|
|
1202
1237
|
}
|
|
@@ -1310,8 +1345,8 @@ class MarkdownFormatter {
|
|
|
1310
1345
|
`|------|------|`
|
|
1311
1346
|
];
|
|
1312
1347
|
lines.push(`| 总问题数 | ${stats.total} |`);
|
|
1313
|
-
lines.push(`|
|
|
1314
|
-
lines.push(`|
|
|
1348
|
+
lines.push(`| 🟢 已修复 | ${stats.fixed} |`);
|
|
1349
|
+
lines.push(`| ⚪ 已解决 | ${stats.resolved} |`);
|
|
1315
1350
|
lines.push(`| ❌ 无效 | ${stats.invalid} |`);
|
|
1316
1351
|
lines.push(`| ⚠️ 待处理 | ${stats.pending} |`);
|
|
1317
1352
|
lines.push(`| 修复率 | ${stats.fixRate}% |`);
|
|
@@ -1346,36 +1381,58 @@ class TerminalFormatter {
|
|
|
1346
1381
|
if (summaries.length === 0) {
|
|
1347
1382
|
return "没有需要审查的文件";
|
|
1348
1383
|
}
|
|
1384
|
+
// 🟢 已修复 | 🔴 待处理error | 🟡 待处理warn | ⚪ 已解决(非代码修复)
|
|
1349
1385
|
const issuesByFile = new Map();
|
|
1350
1386
|
for (const issue of issues){
|
|
1387
|
+
if (issue.valid === "false") continue;
|
|
1351
1388
|
const stats = issuesByFile.get(issue.file) || {
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1389
|
+
fixed: 0,
|
|
1390
|
+
pendingErrors: 0,
|
|
1391
|
+
pendingWarns: 0,
|
|
1392
|
+
resolved: 0
|
|
1355
1393
|
};
|
|
1356
1394
|
if (issue.fixed) {
|
|
1395
|
+
stats.fixed++;
|
|
1396
|
+
} else if (issue.resolved) {
|
|
1357
1397
|
stats.resolved++;
|
|
1358
1398
|
} else if (issue.severity === "error") {
|
|
1359
|
-
stats.
|
|
1399
|
+
stats.pendingErrors++;
|
|
1360
1400
|
} else {
|
|
1361
|
-
stats.
|
|
1401
|
+
stats.pendingWarns++;
|
|
1362
1402
|
}
|
|
1363
1403
|
issuesByFile.set(issue.file, stats);
|
|
1364
1404
|
}
|
|
1405
|
+
// 汇总统计
|
|
1406
|
+
let totalAll = 0;
|
|
1407
|
+
let totalFixed = 0;
|
|
1408
|
+
let totalPendingErrors = 0;
|
|
1409
|
+
let totalPendingWarns = 0;
|
|
1410
|
+
let totalResolved = 0;
|
|
1365
1411
|
const lines = [];
|
|
1366
1412
|
for (const fileSummary of summaries){
|
|
1367
1413
|
const stats = issuesByFile.get(fileSummary.file) || {
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1414
|
+
fixed: 0,
|
|
1415
|
+
pendingErrors: 0,
|
|
1416
|
+
pendingWarns: 0,
|
|
1417
|
+
resolved: 0
|
|
1371
1418
|
};
|
|
1372
|
-
const
|
|
1373
|
-
|
|
1374
|
-
|
|
1419
|
+
const fileTotal = stats.fixed + stats.pendingErrors + stats.pendingWarns + stats.resolved;
|
|
1420
|
+
totalAll += fileTotal;
|
|
1421
|
+
totalFixed += stats.fixed;
|
|
1422
|
+
totalPendingErrors += stats.pendingErrors;
|
|
1423
|
+
totalPendingWarns += stats.pendingWarns;
|
|
1424
|
+
totalResolved += stats.resolved;
|
|
1425
|
+
const totalText = fileTotal > 0 ? `${BOLD}${fileTotal} 问题${RESET}` : "";
|
|
1426
|
+
const fixedText = stats.fixed > 0 ? `${GREEN}🟢 ${stats.fixed} 已修复${RESET}` : "";
|
|
1427
|
+
const errorText = stats.pendingErrors > 0 ? `${RED}🔴 ${stats.pendingErrors} error${RESET}` : "";
|
|
1428
|
+
const warnText = stats.pendingWarns > 0 ? `${YELLOW}🟡 ${stats.pendingWarns} warn${RESET}` : "";
|
|
1429
|
+
const resolvedText = stats.resolved > 0 ? `⚪ ${stats.resolved} 已解决` : "";
|
|
1375
1430
|
const statsText = [
|
|
1376
|
-
|
|
1431
|
+
totalText,
|
|
1432
|
+
fixedText,
|
|
1377
1433
|
errorText,
|
|
1378
|
-
warnText
|
|
1434
|
+
warnText,
|
|
1435
|
+
resolvedText
|
|
1379
1436
|
].filter(Boolean).join(" / ");
|
|
1380
1437
|
if (statsText) {
|
|
1381
1438
|
lines.push(`${BOLD}${fileSummary.file}${RESET} (${statsText}): ${fileSummary.summary}`);
|
|
@@ -1383,6 +1440,18 @@ class TerminalFormatter {
|
|
|
1383
1440
|
lines.push(`${BOLD}${fileSummary.file}${RESET}: ${fileSummary.summary}`);
|
|
1384
1441
|
}
|
|
1385
1442
|
}
|
|
1443
|
+
// 添加汇总行
|
|
1444
|
+
if (summaries.length > 1) {
|
|
1445
|
+
lines.push("");
|
|
1446
|
+
const summaryParts = [
|
|
1447
|
+
`${BOLD}总计: ${totalAll} 问题${RESET}`
|
|
1448
|
+
];
|
|
1449
|
+
if (totalFixed > 0) summaryParts.push(`${GREEN}🟢 ${totalFixed} 已修复${RESET}`);
|
|
1450
|
+
if (totalPendingErrors > 0) summaryParts.push(`${RED}🔴 ${totalPendingErrors} error${RESET}`);
|
|
1451
|
+
if (totalPendingWarns > 0) summaryParts.push(`${YELLOW}🟡 ${totalPendingWarns} warn${RESET}`);
|
|
1452
|
+
if (totalResolved > 0) summaryParts.push(`⚪ ${totalResolved} 已解决`);
|
|
1453
|
+
lines.push(summaryParts.join(" / "));
|
|
1454
|
+
}
|
|
1386
1455
|
return lines.join("\n");
|
|
1387
1456
|
}
|
|
1388
1457
|
format(result, _options = {}) {
|
|
@@ -1437,8 +1506,8 @@ class TerminalFormatter {
|
|
|
1437
1506
|
`\n${BOLD}${CYAN}📊 ${title}:${RESET}`
|
|
1438
1507
|
];
|
|
1439
1508
|
lines.push(` 总问题数: ${stats.total}`);
|
|
1440
|
-
lines.push(` ${GREEN}
|
|
1441
|
-
lines.push(`
|
|
1509
|
+
lines.push(` ${GREEN}🟢 已修复: ${stats.fixed}${RESET}`);
|
|
1510
|
+
lines.push(` ⚪ 已解决: ${stats.resolved}`);
|
|
1442
1511
|
lines.push(` ${RED}❌ 无效: ${stats.invalid}${RESET}`);
|
|
1443
1512
|
lines.push(` ${YELLOW}⚠️ 待处理: ${stats.pending}${RESET}`);
|
|
1444
1513
|
lines.push(` 修复率: ${stats.fixRate}%`);
|
|
@@ -2242,6 +2311,8 @@ class ReviewService {
|
|
|
2242
2311
|
if (shouldLog(verbose, 1)) {
|
|
2243
2312
|
console.log(`📋 已有评论中存在 ${existingIssues.length} 个问题`);
|
|
2244
2313
|
}
|
|
2314
|
+
// 先同步最新的 resolved 状态,确保后续 invalidate/verify 能正确跳过已解决的问题
|
|
2315
|
+
await this.syncResolvedComments(owner, repo, prNumber, existingResult);
|
|
2245
2316
|
// 如果文件有变更,将该文件的历史问题标记为无效
|
|
2246
2317
|
// 简化策略:避免复杂的行号更新逻辑
|
|
2247
2318
|
const reviewConf = this.config.getPluginConfig("review");
|
|
@@ -2451,7 +2522,7 @@ class ReviewService {
|
|
|
2451
2522
|
const total = issues.length;
|
|
2452
2523
|
const fixed = issues.filter((i)=>i.fixed).length;
|
|
2453
2524
|
const resolved = issues.filter((i)=>i.resolved && !i.fixed).length;
|
|
2454
|
-
const invalid = issues.filter((i)=>i.valid === "false").length;
|
|
2525
|
+
const invalid = issues.filter((i)=>i.valid === "false" && !i.fixed && !i.resolved).length;
|
|
2455
2526
|
const pending = total - fixed - resolved - invalid;
|
|
2456
2527
|
const fixRate = total > 0 ? Math.round(fixed / total * 100 * 10) / 10 : 0;
|
|
2457
2528
|
const resolveRate = total > 0 ? Math.round((fixed + resolved) / total * 100 * 10) / 10 : 0;
|
|
@@ -3267,7 +3338,7 @@ ${fileChanges || "无"}`;
|
|
|
3267
3338
|
console.warn("⚠️ 更新 PR 标题失败:", error);
|
|
3268
3339
|
}
|
|
3269
3340
|
}
|
|
3270
|
-
// 获取已解决的评论,同步
|
|
3341
|
+
// 获取已解决的评论,同步 resolve 状态(在更新 review 之前)
|
|
3271
3342
|
await this.syncResolvedComments(owner, repo, prNumber, result);
|
|
3272
3343
|
// 获取评论的 reactions,同步 valid 状态(👎 标记为无效)
|
|
3273
3344
|
await this.syncReactionsToIssues(owner, repo, prNumber, result, verbose);
|
|
@@ -3703,12 +3774,16 @@ ${fileChanges || "无"}`;
|
|
|
3703
3774
|
/**
|
|
3704
3775
|
* 构建行级评论 Review 的 body(marker + 本轮统计 + 上轮回顾)
|
|
3705
3776
|
*/ buildLineReviewBody(issues, round, allIssues) {
|
|
3706
|
-
|
|
3707
|
-
const
|
|
3777
|
+
// 只统计待处理的问题(未修复且未解决)
|
|
3778
|
+
const pendingIssues = issues.filter((i)=>!i.fixed && !i.resolved && i.valid !== "false");
|
|
3779
|
+
const pendingErrors = pendingIssues.filter((i)=>i.severity === "error").length;
|
|
3780
|
+
const pendingWarns = pendingIssues.filter((i)=>i.severity === "warn").length;
|
|
3708
3781
|
const fileCount = new Set(issues.map((i)=>i.file)).size;
|
|
3782
|
+
const totalPending = pendingErrors + pendingWarns;
|
|
3709
3783
|
const badges = [];
|
|
3710
|
-
if (
|
|
3711
|
-
if (
|
|
3784
|
+
if (totalPending > 0) badges.push(`⚠️ ${totalPending}`);
|
|
3785
|
+
if (pendingErrors > 0) badges.push(`🔴 ${pendingErrors}`);
|
|
3786
|
+
if (pendingWarns > 0) badges.push(`🟡 ${pendingWarns}`);
|
|
3712
3787
|
const parts = [
|
|
3713
3788
|
REVIEW_LINE_COMMENTS_MARKER
|
|
3714
3789
|
];
|
|
@@ -3724,14 +3799,14 @@ ${fileChanges || "无"}`;
|
|
|
3724
3799
|
if (prevIssues.length > 0) {
|
|
3725
3800
|
const prevFixed = prevIssues.filter((i)=>i.fixed).length;
|
|
3726
3801
|
const prevResolved = prevIssues.filter((i)=>i.resolved && !i.fixed).length;
|
|
3727
|
-
const prevInvalid = prevIssues.filter((i)=>i.valid === "false").length;
|
|
3802
|
+
const prevInvalid = prevIssues.filter((i)=>i.valid === "false" && !i.fixed && !i.resolved).length;
|
|
3728
3803
|
const prevPending = prevIssues.length - prevFixed - prevResolved - prevInvalid;
|
|
3729
3804
|
parts.push("");
|
|
3730
3805
|
parts.push(`<details><summary>📊 Round ${round - 1} 回顾 (${prevIssues.length} 个问题)</summary>\n`);
|
|
3731
3806
|
parts.push(`| 状态 | 数量 |`);
|
|
3732
3807
|
parts.push(`|------|------|`);
|
|
3733
|
-
if (prevFixed > 0) parts.push(`|
|
|
3734
|
-
if (prevResolved > 0) parts.push(`|
|
|
3808
|
+
if (prevFixed > 0) parts.push(`| 🟢 已修复 | ${prevFixed} |`);
|
|
3809
|
+
if (prevResolved > 0) parts.push(`| ⚪ 已解决 | ${prevResolved} |`);
|
|
3735
3810
|
if (prevInvalid > 0) parts.push(`| ❌ 无效 | ${prevInvalid} |`);
|
|
3736
3811
|
if (prevPending > 0) parts.push(`| ⚠️ 待处理 | ${prevPending} |`);
|
|
3737
3812
|
parts.push(`\n</details>`);
|
package/package.json
CHANGED
|
@@ -130,35 +130,85 @@ export class MarkdownFormatter implements ReviewReportFormatter, ReviewReportPar
|
|
|
130
130
|
return "没有需要审查的文件";
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
-
|
|
133
|
+
// 🟢 已修复 | 🔴 待处理error | 🟡 待处理warn | ⚪ 已解决(非代码修复)
|
|
134
|
+
const issuesByFile = new Map<
|
|
135
|
+
string,
|
|
136
|
+
{ fixed: number; pendingErrors: number; pendingWarns: number; resolved: number }
|
|
137
|
+
>();
|
|
134
138
|
for (const issue of issues) {
|
|
135
139
|
if (issue.valid === "false") continue;
|
|
136
|
-
const stats = issuesByFile.get(issue.file) || {
|
|
140
|
+
const stats = issuesByFile.get(issue.file) || {
|
|
141
|
+
fixed: 0,
|
|
142
|
+
pendingErrors: 0,
|
|
143
|
+
pendingWarns: 0,
|
|
144
|
+
resolved: 0,
|
|
145
|
+
};
|
|
137
146
|
if (issue.fixed) {
|
|
147
|
+
stats.fixed++;
|
|
148
|
+
} else if (issue.resolved) {
|
|
138
149
|
stats.resolved++;
|
|
139
150
|
} else if (issue.severity === "error") {
|
|
140
|
-
stats.
|
|
151
|
+
stats.pendingErrors++;
|
|
141
152
|
} else {
|
|
142
|
-
stats.
|
|
153
|
+
stats.pendingWarns++;
|
|
143
154
|
}
|
|
144
155
|
issuesByFile.set(issue.file, stats);
|
|
145
156
|
}
|
|
146
157
|
|
|
147
158
|
const lines: string[] = [];
|
|
148
|
-
lines.push("| 文件 | 🟢 | 🔴 | 🟡 |
|
|
149
|
-
lines.push("
|
|
159
|
+
lines.push("| 文件 | 总数 | 🟢 | 🔴 | 🟡 | ⚪ |");
|
|
160
|
+
lines.push("|------|------|----|----|----|-----|");
|
|
161
|
+
|
|
162
|
+
// 汇总统计
|
|
163
|
+
let totalAll = 0;
|
|
164
|
+
let totalFixed = 0;
|
|
165
|
+
let totalPendingErrors = 0;
|
|
166
|
+
let totalPendingWarns = 0;
|
|
167
|
+
let totalResolved = 0;
|
|
150
168
|
|
|
169
|
+
const fileSummaryLines: string[] = [];
|
|
151
170
|
for (const fileSummary of summaries) {
|
|
152
|
-
const stats = issuesByFile.get(fileSummary.file) || {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
171
|
+
const stats = issuesByFile.get(fileSummary.file) || {
|
|
172
|
+
fixed: 0,
|
|
173
|
+
pendingErrors: 0,
|
|
174
|
+
pendingWarns: 0,
|
|
175
|
+
resolved: 0,
|
|
176
|
+
};
|
|
177
|
+
const fileTotal = stats.fixed + stats.pendingErrors + stats.pendingWarns + stats.resolved;
|
|
178
|
+
totalAll += fileTotal;
|
|
179
|
+
totalFixed += stats.fixed;
|
|
180
|
+
totalPendingErrors += stats.pendingErrors;
|
|
181
|
+
totalPendingWarns += stats.pendingWarns;
|
|
182
|
+
totalResolved += stats.resolved;
|
|
183
|
+
|
|
184
|
+
lines.push(
|
|
185
|
+
`| \`${fileSummary.file}\` | ${fileTotal} | ${stats.fixed} | ${stats.pendingErrors} | ${stats.pendingWarns} | ${stats.resolved} |`,
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
// 收集问题总结用于折叠块展示
|
|
189
|
+
if (fileSummary.summary.trim()) {
|
|
190
|
+
fileSummaryLines.push(`### \`${fileSummary.file}\``);
|
|
191
|
+
fileSummaryLines.push(`${fileSummary.summary.trim()}`);
|
|
192
|
+
fileSummaryLines.push("");
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// 添加汇总行
|
|
197
|
+
if (summaries.length > 1) {
|
|
157
198
|
lines.push(
|
|
158
|
-
`|
|
|
199
|
+
`| **总计** | **${totalAll}** | **${totalFixed}** | **${totalPendingErrors}** | **${totalPendingWarns}** | **${totalResolved}** |`,
|
|
159
200
|
);
|
|
160
201
|
}
|
|
161
202
|
|
|
203
|
+
// 问题总结放到折叠块中
|
|
204
|
+
if (fileSummaryLines.length > 0) {
|
|
205
|
+
lines.push("");
|
|
206
|
+
lines.push("<details>");
|
|
207
|
+
lines.push("<summary>📝 问题总结</summary>\n");
|
|
208
|
+
lines.push(...fileSummaryLines);
|
|
209
|
+
lines.push("</details>");
|
|
210
|
+
}
|
|
211
|
+
|
|
162
212
|
return lines.join("\n");
|
|
163
213
|
}
|
|
164
214
|
|
|
@@ -289,8 +339,8 @@ export class MarkdownFormatter implements ReviewReportFormatter, ReviewReportPar
|
|
|
289
339
|
const title = prNumber ? `PR #${prNumber} Review 状态统计` : "Review 状态统计";
|
|
290
340
|
const lines = [`## 📊 ${title}\n`, `| 指标 | 数量 |`, `|------|------|`];
|
|
291
341
|
lines.push(`| 总问题数 | ${stats.total} |`);
|
|
292
|
-
lines.push(`|
|
|
293
|
-
lines.push(`|
|
|
342
|
+
lines.push(`| 🟢 已修复 | ${stats.fixed} |`);
|
|
343
|
+
lines.push(`| ⚪ 已解决 | ${stats.resolved} |`);
|
|
294
344
|
lines.push(`| ❌ 无效 | ${stats.invalid} |`);
|
|
295
345
|
lines.push(`| ⚠️ 待处理 | ${stats.pending} |`);
|
|
296
346
|
lines.push(`| 修复率 | ${stats.fixRate}% |`);
|
|
@@ -28,26 +28,63 @@ export class TerminalFormatter implements ReviewReportFormatter {
|
|
|
28
28
|
return "没有需要审查的文件";
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
// 🟢 已修复 | 🔴 待处理error | 🟡 待处理warn | ⚪ 已解决(非代码修复)
|
|
32
|
+
const issuesByFile = new Map<
|
|
33
|
+
string,
|
|
34
|
+
{ fixed: number; pendingErrors: number; pendingWarns: number; resolved: number }
|
|
35
|
+
>();
|
|
32
36
|
for (const issue of issues) {
|
|
33
|
-
|
|
37
|
+
if (issue.valid === "false") continue;
|
|
38
|
+
const stats = issuesByFile.get(issue.file) || {
|
|
39
|
+
fixed: 0,
|
|
40
|
+
pendingErrors: 0,
|
|
41
|
+
pendingWarns: 0,
|
|
42
|
+
resolved: 0,
|
|
43
|
+
};
|
|
34
44
|
if (issue.fixed) {
|
|
45
|
+
stats.fixed++;
|
|
46
|
+
} else if (issue.resolved) {
|
|
35
47
|
stats.resolved++;
|
|
36
48
|
} else if (issue.severity === "error") {
|
|
37
|
-
stats.
|
|
49
|
+
stats.pendingErrors++;
|
|
38
50
|
} else {
|
|
39
|
-
stats.
|
|
51
|
+
stats.pendingWarns++;
|
|
40
52
|
}
|
|
41
53
|
issuesByFile.set(issue.file, stats);
|
|
42
54
|
}
|
|
43
55
|
|
|
56
|
+
// 汇总统计
|
|
57
|
+
let totalAll = 0;
|
|
58
|
+
let totalFixed = 0;
|
|
59
|
+
let totalPendingErrors = 0;
|
|
60
|
+
let totalPendingWarns = 0;
|
|
61
|
+
let totalResolved = 0;
|
|
62
|
+
|
|
44
63
|
const lines: string[] = [];
|
|
45
64
|
for (const fileSummary of summaries) {
|
|
46
|
-
const stats = issuesByFile.get(fileSummary.file) || {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
65
|
+
const stats = issuesByFile.get(fileSummary.file) || {
|
|
66
|
+
fixed: 0,
|
|
67
|
+
pendingErrors: 0,
|
|
68
|
+
pendingWarns: 0,
|
|
69
|
+
resolved: 0,
|
|
70
|
+
};
|
|
71
|
+
const fileTotal = stats.fixed + stats.pendingErrors + stats.pendingWarns + stats.resolved;
|
|
72
|
+
totalAll += fileTotal;
|
|
73
|
+
totalFixed += stats.fixed;
|
|
74
|
+
totalPendingErrors += stats.pendingErrors;
|
|
75
|
+
totalPendingWarns += stats.pendingWarns;
|
|
76
|
+
totalResolved += stats.resolved;
|
|
77
|
+
|
|
78
|
+
const totalText = fileTotal > 0 ? `${BOLD}${fileTotal} 问题${RESET}` : "";
|
|
79
|
+
const fixedText = stats.fixed > 0 ? `${GREEN}🟢 ${stats.fixed} 已修复${RESET}` : "";
|
|
80
|
+
const errorText =
|
|
81
|
+
stats.pendingErrors > 0 ? `${RED}🔴 ${stats.pendingErrors} error${RESET}` : "";
|
|
82
|
+
const warnText =
|
|
83
|
+
stats.pendingWarns > 0 ? `${YELLOW}🟡 ${stats.pendingWarns} warn${RESET}` : "";
|
|
84
|
+
const resolvedText = stats.resolved > 0 ? `⚪ ${stats.resolved} 已解决` : "";
|
|
85
|
+
const statsText = [totalText, fixedText, errorText, warnText, resolvedText]
|
|
86
|
+
.filter(Boolean)
|
|
87
|
+
.join(" / ");
|
|
51
88
|
|
|
52
89
|
if (statsText) {
|
|
53
90
|
lines.push(`${BOLD}${fileSummary.file}${RESET} (${statsText}): ${fileSummary.summary}`);
|
|
@@ -56,6 +93,17 @@ export class TerminalFormatter implements ReviewReportFormatter {
|
|
|
56
93
|
}
|
|
57
94
|
}
|
|
58
95
|
|
|
96
|
+
// 添加汇总行
|
|
97
|
+
if (summaries.length > 1) {
|
|
98
|
+
lines.push("");
|
|
99
|
+
const summaryParts = [`${BOLD}总计: ${totalAll} 问题${RESET}`];
|
|
100
|
+
if (totalFixed > 0) summaryParts.push(`${GREEN}🟢 ${totalFixed} 已修复${RESET}`);
|
|
101
|
+
if (totalPendingErrors > 0) summaryParts.push(`${RED}🔴 ${totalPendingErrors} error${RESET}`);
|
|
102
|
+
if (totalPendingWarns > 0) summaryParts.push(`${YELLOW}🟡 ${totalPendingWarns} warn${RESET}`);
|
|
103
|
+
if (totalResolved > 0) summaryParts.push(`⚪ ${totalResolved} 已解决`);
|
|
104
|
+
lines.push(summaryParts.join(" / "));
|
|
105
|
+
}
|
|
106
|
+
|
|
59
107
|
return lines.join("\n");
|
|
60
108
|
}
|
|
61
109
|
|
|
@@ -123,8 +171,8 @@ export class TerminalFormatter implements ReviewReportFormatter {
|
|
|
123
171
|
const title = prNumber ? `PR #${prNumber} Review 状态统计` : "Review 状态统计";
|
|
124
172
|
const lines = [`\n${BOLD}${CYAN}📊 ${title}:${RESET}`];
|
|
125
173
|
lines.push(` 总问题数: ${stats.total}`);
|
|
126
|
-
lines.push(` ${GREEN}
|
|
127
|
-
lines.push(`
|
|
174
|
+
lines.push(` ${GREEN}🟢 已修复: ${stats.fixed}${RESET}`);
|
|
175
|
+
lines.push(` ⚪ 已解决: ${stats.resolved}`);
|
|
128
176
|
lines.push(` ${RED}❌ 无效: ${stats.invalid}${RESET}`);
|
|
129
177
|
lines.push(` ${YELLOW}⚠️ 待处理: ${stats.pending}${RESET}`);
|
|
130
178
|
lines.push(` 修复率: ${stats.fixRate}%`);
|
package/src/review.service.ts
CHANGED
|
@@ -678,6 +678,9 @@ export class ReviewService {
|
|
|
678
678
|
console.log(`📋 已有评论中存在 ${existingIssues.length} 个问题`);
|
|
679
679
|
}
|
|
680
680
|
|
|
681
|
+
// 先同步最新的 resolved 状态,确保后续 invalidate/verify 能正确跳过已解决的问题
|
|
682
|
+
await this.syncResolvedComments(owner, repo, prNumber, existingResult);
|
|
683
|
+
|
|
681
684
|
// 如果文件有变更,将该文件的历史问题标记为无效
|
|
682
685
|
// 简化策略:避免复杂的行号更新逻辑
|
|
683
686
|
const reviewConf = this.config.getPluginConfig<ReviewConfig>("review");
|
|
@@ -978,7 +981,7 @@ export class ReviewService {
|
|
|
978
981
|
const total = issues.length;
|
|
979
982
|
const fixed = issues.filter((i) => i.fixed).length;
|
|
980
983
|
const resolved = issues.filter((i) => i.resolved && !i.fixed).length;
|
|
981
|
-
const invalid = issues.filter((i) => i.valid === "false").length;
|
|
984
|
+
const invalid = issues.filter((i) => i.valid === "false" && !i.fixed && !i.resolved).length;
|
|
982
985
|
const pending = total - fixed - resolved - invalid;
|
|
983
986
|
const fixRate = total > 0 ? Math.round((fixed / total) * 100 * 10) / 10 : 0;
|
|
984
987
|
const resolveRate = total > 0 ? Math.round(((fixed + resolved) / total) * 100 * 10) / 10 : 0;
|
|
@@ -1953,7 +1956,7 @@ ${fileChanges || "无"}`;
|
|
|
1953
1956
|
}
|
|
1954
1957
|
}
|
|
1955
1958
|
|
|
1956
|
-
// 获取已解决的评论,同步
|
|
1959
|
+
// 获取已解决的评论,同步 resolve 状态(在更新 review 之前)
|
|
1957
1960
|
await this.syncResolvedComments(owner, repo, prNumber, result);
|
|
1958
1961
|
|
|
1959
1962
|
// 获取评论的 reactions,同步 valid 状态(👎 标记为无效)
|
|
@@ -2503,13 +2506,17 @@ ${fileChanges || "无"}`;
|
|
|
2503
2506
|
round: number,
|
|
2504
2507
|
allIssues: ReviewIssue[],
|
|
2505
2508
|
): string {
|
|
2506
|
-
|
|
2507
|
-
const
|
|
2509
|
+
// 只统计待处理的问题(未修复且未解决)
|
|
2510
|
+
const pendingIssues = issues.filter((i) => !i.fixed && !i.resolved && i.valid !== "false");
|
|
2511
|
+
const pendingErrors = pendingIssues.filter((i) => i.severity === "error").length;
|
|
2512
|
+
const pendingWarns = pendingIssues.filter((i) => i.severity === "warn").length;
|
|
2508
2513
|
const fileCount = new Set(issues.map((i) => i.file)).size;
|
|
2509
2514
|
|
|
2515
|
+
const totalPending = pendingErrors + pendingWarns;
|
|
2510
2516
|
const badges: string[] = [];
|
|
2511
|
-
if (
|
|
2512
|
-
if (
|
|
2517
|
+
if (totalPending > 0) badges.push(`⚠️ ${totalPending}`);
|
|
2518
|
+
if (pendingErrors > 0) badges.push(`🔴 ${pendingErrors}`);
|
|
2519
|
+
if (pendingWarns > 0) badges.push(`🟡 ${pendingWarns}`);
|
|
2513
2520
|
|
|
2514
2521
|
const parts: string[] = [REVIEW_LINE_COMMENTS_MARKER];
|
|
2515
2522
|
parts.push(`### 🚀 Spaceflow Review · Round ${round}`);
|
|
@@ -2527,7 +2534,9 @@ ${fileChanges || "无"}`;
|
|
|
2527
2534
|
if (prevIssues.length > 0) {
|
|
2528
2535
|
const prevFixed = prevIssues.filter((i) => i.fixed).length;
|
|
2529
2536
|
const prevResolved = prevIssues.filter((i) => i.resolved && !i.fixed).length;
|
|
2530
|
-
const prevInvalid = prevIssues.filter(
|
|
2537
|
+
const prevInvalid = prevIssues.filter(
|
|
2538
|
+
(i) => i.valid === "false" && !i.fixed && !i.resolved,
|
|
2539
|
+
).length;
|
|
2531
2540
|
const prevPending = prevIssues.length - prevFixed - prevResolved - prevInvalid;
|
|
2532
2541
|
parts.push("");
|
|
2533
2542
|
parts.push(
|
|
@@ -2535,8 +2544,8 @@ ${fileChanges || "无"}`;
|
|
|
2535
2544
|
);
|
|
2536
2545
|
parts.push(`| 状态 | 数量 |`);
|
|
2537
2546
|
parts.push(`|------|------|`);
|
|
2538
|
-
if (prevFixed > 0) parts.push(`|
|
|
2539
|
-
if (prevResolved > 0) parts.push(`|
|
|
2547
|
+
if (prevFixed > 0) parts.push(`| 🟢 已修复 | ${prevFixed} |`);
|
|
2548
|
+
if (prevResolved > 0) parts.push(`| ⚪ 已解决 | ${prevResolved} |`);
|
|
2540
2549
|
if (prevInvalid > 0) parts.push(`| ❌ 无效 | ${prevInvalid} |`);
|
|
2541
2550
|
if (prevPending > 0) parts.push(`| ⚠️ 待处理 | ${prevPending} |`);
|
|
2542
2551
|
parts.push(`\n</details>`);
|