@spaceflow/review-summary 0.22.0 → 0.24.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 +78 -0
- package/package.json +5 -3
- package/src/review-summary.service.ts +162 -19
- package/src/types.ts +37 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,83 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.23.0](https://github.com/Lydanne/spaceflow/compare/@spaceflow/review-summary@0.22.0...@spaceflow/review-summary@0.23.0) (2026-03-02)
|
|
4
|
+
|
|
5
|
+
### 新特性
|
|
6
|
+
|
|
7
|
+
* **actions:** 增强对 Gitea 平台的环境变量支持 ([21ba3da](https://github.com/Lydanne/spaceflow/commit/21ba3daa182083ecaec0113de91caf2ab1068000))
|
|
8
|
+
* **actions:** 支持 Gitea 作为 Git Provider ([4c88b36](https://github.com/Lydanne/spaceflow/commit/4c88b36d97ab49a322bac571385b0e57029676ff))
|
|
9
|
+
* **core:** 增强对 Gitea 平台的支持并修复类型转换 ([bfbb45c](https://github.com/Lydanne/spaceflow/commit/bfbb45c521035c80f0e81f5572d5aa1b554405f7))
|
|
10
|
+
* **publish:** 支持 Gitea 环境变量作为 CI 配置来源 ([cd1ba77](https://github.com/Lydanne/spaceflow/commit/cd1ba778617731ef85f9851883122175a99d72ac))
|
|
11
|
+
* **review-summary:** 新增 defect-rate 缺陷率评分策略 ([2777f4d](https://github.com/Lydanne/spaceflow/commit/2777f4da6d364cbc801792f4fa7ac6323da5e1bc))
|
|
12
|
+
* **review-summary:** 新增 issue-based 评分策略并更新文档 ([b6d923e](https://github.com/Lydanne/spaceflow/commit/b6d923e060aafd0b298a39d6fdc3e2f76facf1f3))
|
|
13
|
+
* **review:** 支持从 Gitea Actions 事件文件解析 PR 编号 ([09e2e58](https://github.com/Lydanne/spaceflow/commit/09e2e58d84f773b8a3184feb73b90cb110993ebd))
|
|
14
|
+
|
|
15
|
+
### 文档更新
|
|
16
|
+
|
|
17
|
+
* **docs:** 更新 review-summary 命令的 CI 配置示例 ([8b2b2c5](https://github.com/Lydanne/spaceflow/commit/8b2b2c55ed497619cbb91bd4fd2e5124b6f5ac37))
|
|
18
|
+
* **docs:** 更新 review-summary 命令的 CI 集成示例 ([60c5b70](https://github.com/Lydanne/spaceflow/commit/60c5b70695aa6a4c9cae1e3936670b34d5486792))
|
|
19
|
+
* **docs:** 更新环境变量参考文档,增加 GitLab 支持并优化 Gitea 说明 ([a02f8e0](https://github.com/Lydanne/spaceflow/commit/a02f8e0309368334bc77a331f65338435657eddb))
|
|
20
|
+
* **review-summary:** 更新错误信息以支持 Gitea 环境变量 ([cd02818](https://github.com/Lydanne/spaceflow/commit/cd028183fe7e14d993566e8dc49849b07d2540d9))
|
|
21
|
+
* **scripts:** 更新错误信息以支持 Gitea 环境变量 ([9a9d7f2](https://github.com/Lydanne/spaceflow/commit/9a9d7f20a63bea4cc13d284ed41bc9c4f4aa6595))
|
|
22
|
+
* **shell:** 更新错误信息以支持 Gitea 环境变量 ([72c51d7](https://github.com/Lydanne/spaceflow/commit/72c51d792c140d44d8aa3cb05666de72d170a185))
|
|
23
|
+
* 修复 GitHub Actions 配置示例的 Markdown 渲染问题 ([01daec5](https://github.com/Lydanne/spaceflow/commit/01daec551f2e23ab70357f88b7789897922064a7))
|
|
24
|
+
|
|
25
|
+
### 测试用例
|
|
26
|
+
|
|
27
|
+
* **core:** 修复测试用例中的导入和模拟函数实现 ([0137d4b](https://github.com/Lydanne/spaceflow/commit/0137d4b400f272ce7f7ba74fc5b37fcc453ed717))
|
|
28
|
+
|
|
29
|
+
### 其他修改
|
|
30
|
+
|
|
31
|
+
* **core:** released version 0.21.0 [no ci] ([7fa4381](https://github.com/Lydanne/spaceflow/commit/7fa438124cc27316ad5f37d5cbacc848ebd3b9df))
|
|
32
|
+
* **publish:** released version 0.45.0 [no ci] ([9bd3bbe](https://github.com/Lydanne/spaceflow/commit/9bd3bbe725a3362f762a1407c6a92a993359dfe5))
|
|
33
|
+
* **review:** released version 0.58.0 [no ci] ([790dc5f](https://github.com/Lydanne/spaceflow/commit/790dc5f4b38eba28df6f6e4414dd9c536d5a6377))
|
|
34
|
+
* **scripts:** released version 0.23.0 [no ci] ([2f18d22](https://github.com/Lydanne/spaceflow/commit/2f18d2274e83b65ce006dceed47a985942c8dd1d))
|
|
35
|
+
* **shell:** released version 0.23.0 [no ci] ([0668aa9](https://github.com/Lydanne/spaceflow/commit/0668aa97671ca235509bef547503c301237324f9))
|
|
36
|
+
|
|
37
|
+
## [0.22.0](https://github.com/Lydanne/spaceflow/compare/@spaceflow/review-summary@0.21.0...@spaceflow/review-summary@0.22.0) (2026-03-02)
|
|
38
|
+
|
|
39
|
+
### 新特性
|
|
40
|
+
|
|
41
|
+
* **core:** Gitea适配器支持通过标签名称创建Issue ([cf10eda](https://github.com/Lydanne/spaceflow/commit/cf10eda5d025c560cc0d8e3826dad40716020d5c))
|
|
42
|
+
* **review-summary:** commit-based 评分新增修复问题加分机制,支持从 review 模块精确提取问题统计 ([304bf81](https://github.com/Lydanne/spaceflow/commit/304bf81ab475b280ab5f4011159bc697861bddf6))
|
|
43
|
+
* **review-summary:** 支持为周期统计报告 Issue 配置自定义标签 ([df1cc61](https://github.com/Lydanne/spaceflow/commit/df1cc61167851ff7106104914319f43f73ba8902))
|
|
44
|
+
* **review-summary:** 支持通过配置文件自定义评分权重 ([53e1a37](https://github.com/Lydanne/spaceflow/commit/53e1a371288aea6ceac63b03fda99eea1739be4b))
|
|
45
|
+
* **review-summary:** 新增 commit-based 评分策略,支持按有效 commit 累计计分 ([111c0d6](https://github.com/Lydanne/spaceflow/commit/111c0d6b9d87d12096e0edb69a11eceff55b79c1))
|
|
46
|
+
* **review:** 为行级评论 Review 添加统计信息摘要 ([58d5b37](https://github.com/Lydanne/spaceflow/commit/58d5b37ba54daa24bd2f8396318fedc87f388c74))
|
|
47
|
+
* **review:** 优化问题统计展示,按 severity 分级显示 error/warn 数量 ([bcb2608](https://github.com/Lydanne/spaceflow/commit/bcb26086589a67e815db075f3001209904572926))
|
|
48
|
+
* **review:** 保留历史行级评论,为每轮 Review 生成独立评论并添加上轮回顾 ([de431a0](https://github.com/Lydanne/spaceflow/commit/de431a09b4e3b5e1ada9ee5f1ee65786d22b6ff9))
|
|
49
|
+
* **review:** 支持用户手动 resolve 评论并在报告中区分 AI 修复与手动解决 ([c968b65](https://github.com/Lydanne/spaceflow/commit/c968b65c850bc68de3f4409aa3b5294e5a0311ff))
|
|
50
|
+
* **review:** 新增 MCP 工具支持从目录批量加载代码审查规则 ([289a836](https://github.com/Lydanne/spaceflow/commit/289a83650f1e222482fcbaaa69fb5ea562c5a4c2))
|
|
51
|
+
* **review:** 新增解决率统计指标,区分修复率和解决率的计算维度 ([436541f](https://github.com/Lydanne/spaceflow/commit/436541fce605319da562445a81242a8feb257df9))
|
|
52
|
+
|
|
53
|
+
### 修复BUG
|
|
54
|
+
|
|
55
|
+
* **review:** 修复率计算仅统计 AI 修复的问题,排除手动解决的问题 ([12b3415](https://github.com/Lydanne/spaceflow/commit/12b3415749c9d8523e8b23365fbb39fc7657ff1d))
|
|
56
|
+
* **review:** 修正 PR 评论标题中的 emoji 显示问题 ([bcdc946](https://github.com/Lydanne/spaceflow/commit/bcdc9467bf7970c9acd3ea00303bcae5eaff131f))
|
|
57
|
+
|
|
58
|
+
### 代码重构
|
|
59
|
+
|
|
60
|
+
* **review:** 抽取规则加载和问题验证逻辑为独立方法,优化代码复用性 ([7ea02ba](https://github.com/Lydanne/spaceflow/commit/7ea02ba86e369bc130c69c561195634072cc060a))
|
|
61
|
+
|
|
62
|
+
### 文档更新
|
|
63
|
+
|
|
64
|
+
* **docs:** 为 review-summary 命令文档补充 Issue 输出配置说明 ([196fa94](https://github.com/Lydanne/spaceflow/commit/196fa94ad1ed2dbadbdcb332ef26cf1fe7fcd8d7))
|
|
65
|
+
* **review-summary:** 完善文档,新增时间预设、评分算法及输出示例说明 ([fb04685](https://github.com/Lydanne/spaceflow/commit/fb04685dde4157f0a1a2f8edaf1fb3c125280e27))
|
|
66
|
+
* **review:** 完善 review 命令文档,新增审查流程、多轮审查、问题生命周期等核心机制说明 ([d6b2a20](https://github.com/Lydanne/spaceflow/commit/d6b2a20802ab98e5ddb01937c0fe8b268c403c6f))
|
|
67
|
+
|
|
68
|
+
### 其他修改
|
|
69
|
+
|
|
70
|
+
* **core:** released version 0.20.0 [no ci] ([b7ed239](https://github.com/Lydanne/spaceflow/commit/b7ed239455244cd96f2b59ef67886dd0bfc057a8))
|
|
71
|
+
* **publish:** released version 0.44.0 [no ci] ([5b29159](https://github.com/Lydanne/spaceflow/commit/5b29159b2f0129d2ce81329cf48734d3d56b226e))
|
|
72
|
+
* **review:** released version 0.54.0 [no ci] ([252269a](https://github.com/Lydanne/spaceflow/commit/252269a299f9e580b858e04814e7d9a13fed7736))
|
|
73
|
+
* **review:** released version 0.55.0 [no ci] ([0245743](https://github.com/Lydanne/spaceflow/commit/02457439788dd70925b91118f7d5936a61d0e0de))
|
|
74
|
+
* **review:** released version 0.56.0 [no ci] ([2481dec](https://github.com/Lydanne/spaceflow/commit/2481dec141b0d5f444b5815ab9598378ac3e0b12))
|
|
75
|
+
* **review:** released version 0.57.0 [no ci] ([238a831](https://github.com/Lydanne/spaceflow/commit/238a83165fa1810a9429b8d6a66a1f75c477ce22))
|
|
76
|
+
* **scripts:** released version 0.21.0 [no ci] ([1f0a213](https://github.com/Lydanne/spaceflow/commit/1f0a2139d155807451dc968de8213bafe2e4edb8))
|
|
77
|
+
* **scripts:** released version 0.22.0 [no ci] ([f482504](https://github.com/Lydanne/spaceflow/commit/f48250486906016b414a7b00aabac342c1399045))
|
|
78
|
+
* **shell:** released version 0.21.0 [no ci] ([b619af7](https://github.com/Lydanne/spaceflow/commit/b619af741e16053868a2eedd41f56d50134954d8))
|
|
79
|
+
* **shell:** released version 0.22.0 [no ci] ([e716369](https://github.com/Lydanne/spaceflow/commit/e716369f57bfa20e710d354245c54d3a80e701f4))
|
|
80
|
+
|
|
3
81
|
## [0.21.0](https://github.com/Lydanne/spaceflow/compare/@spaceflow/review-summary@0.20.0...@spaceflow/review-summary@0.21.0) (2026-03-02)
|
|
4
82
|
|
|
5
83
|
### 修复BUG
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spaceflow/review-summary",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.24.0",
|
|
4
4
|
"description": "Spaceflow 审查统计命令,根据时间范围统计 PR 贡献情况,按人员汇总并排序",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Lydanne",
|
|
@@ -15,14 +15,16 @@
|
|
|
15
15
|
"type": "module",
|
|
16
16
|
"main": "./dist/index.js",
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"
|
|
18
|
+
"micromatch": "^4.0.8",
|
|
19
|
+
"@spaceflow/review": "0.58.0"
|
|
19
20
|
},
|
|
20
21
|
"devDependencies": {
|
|
22
|
+
"@types/micromatch": "^4.0.9",
|
|
21
23
|
"@types/node": "^22.15.0",
|
|
22
24
|
"@spaceflow/cli": "0.38.0"
|
|
23
25
|
},
|
|
24
26
|
"peerDependencies": {
|
|
25
|
-
"@spaceflow/core": "0.
|
|
27
|
+
"@spaceflow/core": "0.21.0"
|
|
26
28
|
},
|
|
27
29
|
"spaceflow": {
|
|
28
30
|
"type": "flow",
|
|
@@ -2,6 +2,7 @@ import { GitProviderService, shouldLog, normalizeVerbose } from "@spaceflow/core
|
|
|
2
2
|
import type { IConfigReader } from "@spaceflow/core";
|
|
3
3
|
import type { PullRequest, Issue, CiConfig } from "@spaceflow/core";
|
|
4
4
|
import { MarkdownFormatter, type ReviewIssue } from "@spaceflow/review";
|
|
5
|
+
import micromatch from "micromatch";
|
|
5
6
|
import { writeFileSync } from "fs";
|
|
6
7
|
import { join } from "path";
|
|
7
8
|
import type {
|
|
@@ -14,6 +15,8 @@ import type {
|
|
|
14
15
|
TimePreset,
|
|
15
16
|
WeightedScoreWeights,
|
|
16
17
|
CommitBasedWeights,
|
|
18
|
+
IssueBasedWeights,
|
|
19
|
+
DefectRateWeights,
|
|
17
20
|
ReviewSummaryConfig,
|
|
18
21
|
ScoreStrategy,
|
|
19
22
|
} from "./types";
|
|
@@ -38,6 +41,24 @@ const DEFAULT_COMMIT_BASED_WEIGHTS: Required<CommitBasedWeights> = {
|
|
|
38
41
|
minCommitLines: 5,
|
|
39
42
|
};
|
|
40
43
|
|
|
44
|
+
/** issue-based 模式默认权重 */
|
|
45
|
+
const DEFAULT_ISSUE_BASED_WEIGHTS: Required<IssueBasedWeights> = {
|
|
46
|
+
minBase: 60,
|
|
47
|
+
maxBase: 100,
|
|
48
|
+
capLines: 1000,
|
|
49
|
+
errorDeduction: 8,
|
|
50
|
+
warnDeduction: 3,
|
|
51
|
+
errorFixedBonus: 5,
|
|
52
|
+
warnFixedBonus: 2,
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/** defect-rate 模式默认权重 */
|
|
56
|
+
const DEFAULT_DEFECT_RATE_WEIGHTS: Required<DefectRateWeights> = {
|
|
57
|
+
errorPenalty: 0.3,
|
|
58
|
+
warnPenalty: 0.1,
|
|
59
|
+
fixedDiscount: 0.05,
|
|
60
|
+
};
|
|
61
|
+
|
|
41
62
|
/**
|
|
42
63
|
* 周期统计服务
|
|
43
64
|
*/
|
|
@@ -64,7 +85,7 @@ export class PeriodSummaryService {
|
|
|
64
85
|
const ciConf = this.config.get<CiConfig>("ci");
|
|
65
86
|
const repository = ciConf?.repository;
|
|
66
87
|
if (!repository) {
|
|
67
|
-
throw new Error("缺少仓库配置,请通过 --repository 参数或环境变量 GITHUB_REPOSITORY 指定");
|
|
88
|
+
throw new Error("缺少仓库配置,请通过 --repository 参数或环境变量 GITHUB_REPOSITORY / GITEA_REPOSITORY 指定");
|
|
68
89
|
}
|
|
69
90
|
const parts = repository.split("/");
|
|
70
91
|
owner = parts[0];
|
|
@@ -139,8 +160,13 @@ export class PeriodSummaryService {
|
|
|
139
160
|
let additions = 0;
|
|
140
161
|
let deletions = 0;
|
|
141
162
|
let changedFiles = 0;
|
|
163
|
+
const includes = this.resolveIncludes();
|
|
142
164
|
try {
|
|
143
|
-
const
|
|
165
|
+
const allFiles = await this.gitProvider.getPullRequestFiles(owner, repo, pr.number!);
|
|
166
|
+
const files =
|
|
167
|
+
includes.length > 0
|
|
168
|
+
? allFiles.filter((f) => micromatch.isMatch(f.filename ?? "", includes, { matchBase: true }))
|
|
169
|
+
: allFiles;
|
|
144
170
|
changedFiles = files.length;
|
|
145
171
|
for (const file of files) {
|
|
146
172
|
additions += file.additions ?? 0;
|
|
@@ -351,6 +377,22 @@ export class PeriodSummaryService {
|
|
|
351
377
|
return userMap;
|
|
352
378
|
}
|
|
353
379
|
|
|
380
|
+
/**
|
|
381
|
+
* 解析文件过滤 glob 模式:优先使用 review-summary.includes,fallback 到 review.includes
|
|
382
|
+
*/
|
|
383
|
+
protected resolveIncludes(): string[] {
|
|
384
|
+
const config = this.getStrategyConfig();
|
|
385
|
+
if (config.includes && config.includes.length > 0) {
|
|
386
|
+
return config.includes;
|
|
387
|
+
}
|
|
388
|
+
try {
|
|
389
|
+
const reviewConfig = this.config.get<{ includes?: string[] }>("review");
|
|
390
|
+
return reviewConfig?.includes ?? [];
|
|
391
|
+
} catch {
|
|
392
|
+
return [];
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
354
396
|
/**
|
|
355
397
|
* 获取当前评分策略配置
|
|
356
398
|
*/
|
|
@@ -370,6 +412,15 @@ export class PeriodSummaryService {
|
|
|
370
412
|
const strategy: ScoreStrategy = config.strategy ?? "weighted";
|
|
371
413
|
for (const userStats of userMap.values()) {
|
|
372
414
|
switch (strategy) {
|
|
415
|
+
case "defect-rate": {
|
|
416
|
+
const rate = this.calculateDefectRate(userStats, config);
|
|
417
|
+
userStats.defectRate = rate;
|
|
418
|
+
userStats.score = Math.round((100 - rate) * 10) / 10;
|
|
419
|
+
break;
|
|
420
|
+
}
|
|
421
|
+
case "issue-based":
|
|
422
|
+
userStats.score = this.calculateIssueBasedScore(userStats, config);
|
|
423
|
+
break;
|
|
373
424
|
case "commit-based":
|
|
374
425
|
userStats.score = this.calculateCommitBasedScore(userStats, config);
|
|
375
426
|
break;
|
|
@@ -398,6 +449,67 @@ export class PeriodSummaryService {
|
|
|
398
449
|
return Math.max(0, Math.round(totalScore * 10) / 10);
|
|
399
450
|
}
|
|
400
451
|
|
|
452
|
+
/**
|
|
453
|
+
* issue-based 模式:逐 PR 计算基础分(对数缩放) + 问题扣分/修复加分
|
|
454
|
+
*/
|
|
455
|
+
protected calculateIssueBasedScore(stats: UserStats, config: ReviewSummaryConfig): number {
|
|
456
|
+
const weights = { ...DEFAULT_ISSUE_BASED_WEIGHTS, ...config.issueBasedWeights };
|
|
457
|
+
let totalScore = 0;
|
|
458
|
+
for (const pr of stats.prs) {
|
|
459
|
+
const baseScore = this.calculatePrBaseScore(
|
|
460
|
+
pr.additions + pr.deletions,
|
|
461
|
+
weights.minBase,
|
|
462
|
+
weights.maxBase,
|
|
463
|
+
weights.capLines,
|
|
464
|
+
);
|
|
465
|
+
const deduction =
|
|
466
|
+
pr.errorCount * weights.errorDeduction + pr.warnCount * weights.warnDeduction;
|
|
467
|
+
const bonus =
|
|
468
|
+
pr.fixedErrors * weights.errorFixedBonus + pr.fixedWarns * weights.warnFixedBonus;
|
|
469
|
+
totalScore += Math.max(0, baseScore - deduction + bonus);
|
|
470
|
+
}
|
|
471
|
+
return Math.round(totalScore * 10) / 10;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* 计算单个 PR 的基础分(对数缩放,映射到 [minBase, maxBase] 区间)
|
|
476
|
+
*/
|
|
477
|
+
protected calculatePrBaseScore(
|
|
478
|
+
totalLines: number,
|
|
479
|
+
minBase: number,
|
|
480
|
+
maxBase: number,
|
|
481
|
+
capLines: number,
|
|
482
|
+
): number {
|
|
483
|
+
if (totalLines <= 0) return minBase;
|
|
484
|
+
const ratio = Math.min(1, Math.log2(1 + totalLines) / Math.log2(1 + capLines));
|
|
485
|
+
return minBase + (maxBase - minBase) * ratio;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
* defect-rate 模式:基于问题密度(每百行代码)计算缺陷率
|
|
490
|
+
*/
|
|
491
|
+
protected calculateDefectRate(stats: UserStats, config: ReviewSummaryConfig): number {
|
|
492
|
+
const weights = { ...DEFAULT_DEFECT_RATE_WEIGHTS, ...config.defectRateWeights };
|
|
493
|
+
if (stats.prs.length === 0) return 0;
|
|
494
|
+
let complianceSum = 0;
|
|
495
|
+
let counted = 0;
|
|
496
|
+
for (const pr of stats.prs) {
|
|
497
|
+
// 跳过 merge PR(标题以 "Merge" 开头),合并操作不纳入缺陷率统计
|
|
498
|
+
if (pr.title.startsWith("Merge")) continue;
|
|
499
|
+
const totalLines = pr.additions + pr.deletions;
|
|
500
|
+
const per100 = Math.max(1, totalLines / 100);
|
|
501
|
+
const penalty =
|
|
502
|
+
(pr.errorCount * weights.errorPenalty + pr.warnCount * weights.warnPenalty) / per100;
|
|
503
|
+
const recovery = (pr.fixedErrors + pr.fixedWarns) * weights.fixedDiscount;
|
|
504
|
+
const compliance = Math.min(1, Math.max(0, 1 - penalty + recovery));
|
|
505
|
+
complianceSum += compliance;
|
|
506
|
+
counted++;
|
|
507
|
+
}
|
|
508
|
+
if (counted === 0) return 0;
|
|
509
|
+
const avgCompliance = complianceSum / counted;
|
|
510
|
+
return Math.round((1 - avgCompliance) * 1000) / 10;
|
|
511
|
+
}
|
|
512
|
+
|
|
401
513
|
/**
|
|
402
514
|
* 分数累计模式:按有效 commit 加分,按 error/warn 扣分
|
|
403
515
|
*/
|
|
@@ -417,6 +529,12 @@ export class PeriodSummaryService {
|
|
|
417
529
|
* 按分数排序用户统计
|
|
418
530
|
*/
|
|
419
531
|
protected sortUserStats(userMap: Map<string, UserStats>): UserStats[] {
|
|
532
|
+
const config = this.getStrategyConfig();
|
|
533
|
+
if (config.strategy === "defect-rate") {
|
|
534
|
+
return Array.from(userMap.values()).sort(
|
|
535
|
+
(a, b) => (a.defectRate ?? 0) - (b.defectRate ?? 0),
|
|
536
|
+
);
|
|
537
|
+
}
|
|
420
538
|
return Array.from(userMap.values()).sort((a, b) => b.score - a.score);
|
|
421
539
|
}
|
|
422
540
|
|
|
@@ -551,18 +669,32 @@ export class PeriodSummaryService {
|
|
|
551
669
|
lines.push("");
|
|
552
670
|
lines.push(`🏆 贡献者排名`);
|
|
553
671
|
lines.push(`${"─".repeat(60)}`);
|
|
554
|
-
const
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
672
|
+
const isDefectRate = result.userStats.some((u) => u.defectRate !== undefined);
|
|
673
|
+
const header = isDefectRate
|
|
674
|
+
? [
|
|
675
|
+
"排名".padEnd(4),
|
|
676
|
+
"用户".padEnd(15),
|
|
677
|
+
"PR数".padStart(5),
|
|
678
|
+
"新增".padStart(8),
|
|
679
|
+
"删除".padStart(8),
|
|
680
|
+
"问题".padStart(5),
|
|
681
|
+
"缺陷率".padStart(8),
|
|
682
|
+
].join(" │ ")
|
|
683
|
+
: [
|
|
684
|
+
"排名".padEnd(4),
|
|
685
|
+
"用户".padEnd(15),
|
|
686
|
+
"PR数".padStart(5),
|
|
687
|
+
"新增".padStart(8),
|
|
688
|
+
"删除".padStart(8),
|
|
689
|
+
"问题".padStart(5),
|
|
690
|
+
"分数".padStart(8),
|
|
691
|
+
].join(" │ ");
|
|
563
692
|
lines.push(header);
|
|
564
693
|
lines.push("─".repeat(60));
|
|
565
694
|
result.userStats.forEach((user, index) => {
|
|
695
|
+
const lastCol = isDefectRate
|
|
696
|
+
? `${(user.defectRate ?? 0).toFixed(1)}%`.padStart(8)
|
|
697
|
+
: user.score.toFixed(1).padStart(8);
|
|
566
698
|
const row = [
|
|
567
699
|
`#${index + 1}`.padEnd(4),
|
|
568
700
|
user.username.slice(0, 15).padEnd(15),
|
|
@@ -570,7 +702,7 @@ export class PeriodSummaryService {
|
|
|
570
702
|
`+${user.totalAdditions}`.padStart(8),
|
|
571
703
|
`-${user.totalDeletions}`.padStart(8),
|
|
572
704
|
String(user.totalIssues).padStart(5),
|
|
573
|
-
|
|
705
|
+
lastCol,
|
|
574
706
|
].join(" │ ");
|
|
575
707
|
lines.push(row);
|
|
576
708
|
});
|
|
@@ -603,13 +735,24 @@ export class PeriodSummaryService {
|
|
|
603
735
|
lines.push("");
|
|
604
736
|
lines.push(`## 🏆 贡献者排名`);
|
|
605
737
|
lines.push("");
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
lines.push(
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
738
|
+
const isDefectRate = result.userStats.some((u) => u.defectRate !== undefined);
|
|
739
|
+
if (isDefectRate) {
|
|
740
|
+
lines.push(`| 排名 | 用户 | PR数 | 新增 | 删除 | 问题 | 缺陷率 |`);
|
|
741
|
+
lines.push(`|------|------|------|------|------|------|--------|`);
|
|
742
|
+
result.userStats.forEach((user, index) => {
|
|
743
|
+
lines.push(
|
|
744
|
+
`| #${index + 1} | ${user.username} | ${user.prCount} | +${user.totalAdditions} | -${user.totalDeletions} | ${user.totalIssues} | ${(user.defectRate ?? 0).toFixed(1)}% |`,
|
|
745
|
+
);
|
|
746
|
+
});
|
|
747
|
+
} else {
|
|
748
|
+
lines.push(`| 排名 | 用户 | PR数 | 新增 | 删除 | 问题 | 分数 |`);
|
|
749
|
+
lines.push(`|------|------|------|------|------|------|------|`);
|
|
750
|
+
result.userStats.forEach((user, index) => {
|
|
751
|
+
lines.push(
|
|
752
|
+
`| #${index + 1} | ${user.username} | ${user.prCount} | +${user.totalAdditions} | -${user.totalDeletions} | ${user.totalIssues} | ${user.score.toFixed(1)} |`,
|
|
753
|
+
);
|
|
754
|
+
});
|
|
755
|
+
}
|
|
613
756
|
lines.push("");
|
|
614
757
|
lines.push(`## 📋 功能摘要`);
|
|
615
758
|
lines.push("");
|
package/src/types.ts
CHANGED
|
@@ -113,6 +113,8 @@ export interface UserStats {
|
|
|
113
113
|
totalValidCommits: number;
|
|
114
114
|
/** 综合分数 */
|
|
115
115
|
score: number;
|
|
116
|
+
/** 缺陷率(0-100),0 表示零缺陷,100 表示全部违规,仅 defect-rate 策略时填充 */
|
|
117
|
+
defectRate?: number;
|
|
116
118
|
/** 功能摘要列表 */
|
|
117
119
|
features: string[];
|
|
118
120
|
/** 该用户的 PR 列表 */
|
|
@@ -120,7 +122,7 @@ export interface UserStats {
|
|
|
120
122
|
}
|
|
121
123
|
|
|
122
124
|
/** 评分策略类型 */
|
|
123
|
-
export type ScoreStrategy = "weighted" | "commit-based";
|
|
125
|
+
export type ScoreStrategy = "weighted" | "commit-based" | "issue-based" | "defect-rate";
|
|
124
126
|
|
|
125
127
|
/** 加权模式权重配置 */
|
|
126
128
|
export interface WeightedScoreWeights {
|
|
@@ -157,14 +159,48 @@ export interface CommitBasedWeights {
|
|
|
157
159
|
/** @deprecated 使用 WeightedScoreWeights 代替 */
|
|
158
160
|
export type ScoreWeights = WeightedScoreWeights;
|
|
159
161
|
|
|
162
|
+
/** issue-based 模式权重配置 */
|
|
163
|
+
export interface IssueBasedWeights {
|
|
164
|
+
/** PR 基础分下限,默认 60 */
|
|
165
|
+
minBase?: number;
|
|
166
|
+
/** PR 基础分上限,默认 100 */
|
|
167
|
+
maxBase?: number;
|
|
168
|
+
/** 代码量封顶行数(additions + deletions),超过此值基础分为 maxBase,默认 1000 */
|
|
169
|
+
capLines?: number;
|
|
170
|
+
/** 每个 error 问题的扣分,默认 8 */
|
|
171
|
+
errorDeduction?: number;
|
|
172
|
+
/** 每个 warn 问题的扣分,默认 3 */
|
|
173
|
+
warnDeduction?: number;
|
|
174
|
+
/** 修复一个 error 问题的加分,默认 5 */
|
|
175
|
+
errorFixedBonus?: number;
|
|
176
|
+
/** 修复一个 warn 问题的加分,默认 2 */
|
|
177
|
+
warnFixedBonus?: number;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/** defect-rate 模式权重配置 */
|
|
181
|
+
export interface DefectRateWeights {
|
|
182
|
+
/** 每百行代码 1 个 error 降低的合规度,默认 0.3 */
|
|
183
|
+
errorPenalty?: number;
|
|
184
|
+
/** 每百行代码 1 个 warn 降低的合规度,默认 0.1 */
|
|
185
|
+
warnPenalty?: number;
|
|
186
|
+
/** 修复 1 个问题恢复的合规度,默认 0.05 */
|
|
187
|
+
fixedDiscount?: number;
|
|
188
|
+
}
|
|
189
|
+
|
|
160
190
|
/** review-summary 扩展配置 */
|
|
161
191
|
export interface ReviewSummaryConfig {
|
|
162
192
|
/** 评分策略,默认 "weighted" */
|
|
163
193
|
strategy?: ScoreStrategy;
|
|
194
|
+
/** 文件过滤 glob 模式,未设置时 fallback 读取 review.includes,支持 ! 排除模式 */
|
|
195
|
+
includes?: string[];
|
|
164
196
|
/** 加权模式权重配置(strategy 为 "weighted" 时生效) */
|
|
165
197
|
scoreWeights?: WeightedScoreWeights;
|
|
166
198
|
/** 分数累计模式权重配置(strategy 为 "commit-based" 时生效) */
|
|
167
199
|
commitBasedWeights?: CommitBasedWeights;
|
|
200
|
+
/** issue-based 模式权重配置(strategy 为 "issue-based" 时生效) */
|
|
201
|
+
issueBasedWeights?: IssueBasedWeights;
|
|
202
|
+
/** defect-rate 模式权重配置(strategy 为 "defect-rate" 时生效) */
|
|
203
|
+
defectRateWeights?: DefectRateWeights;
|
|
168
204
|
/** 创建 Issue 时添加的标签名称,默认 "report" */
|
|
169
205
|
issueLabel?: string;
|
|
170
206
|
}
|