@spaceflow/review-summary 0.20.0 → 0.22.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,41 @@
1
1
  # Changelog
2
2
 
3
+ ## [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
+
5
+ ### 修复BUG
6
+
7
+ * **core:** 重构配置 Schema 生成逻辑,使用 SpaceflowConfigSchema 作为基础 ([c73eb1c](https://github.com/Lydanne/spaceflow/commit/c73eb1ce5b6f212b8a932a15224db7e63822f8d0))
8
+
9
+ ### 测试用例
10
+
11
+ * **review:** 增强 AI 评论识别和过滤功能的测试覆盖 ([bda706b](https://github.com/Lydanne/spaceflow/commit/bda706b99aab113521afe6bcd386a590811e20a6))
12
+
13
+ ### 其他修改
14
+
15
+ * **core:** released version 0.19.0 [no ci] ([c8bfe6b](https://github.com/Lydanne/spaceflow/commit/c8bfe6ba20893e2c3cd383ed7e7d3217b0492eb6))
16
+ * **publish:** released version 0.43.0 [no ci] ([1074b9c](https://github.com/Lydanne/spaceflow/commit/1074b9c5fb21a447093ef23300c451d790710b33))
17
+ * **review:** released version 0.51.0 [no ci] ([c93be78](https://github.com/Lydanne/spaceflow/commit/c93be78f6f1df9cb5e3515cee58cda65cad1b00f))
18
+ * **review:** released version 0.52.0 [no ci] ([c86406f](https://github.com/Lydanne/spaceflow/commit/c86406f6934d5de4f198eadff66ee6c3f7cfbe0d))
19
+ * **review:** released version 0.53.0 [no ci] ([5a6af03](https://github.com/Lydanne/spaceflow/commit/5a6af03c260060ac1b1901bb7273f501ca0037c7))
20
+ * **review:** 移除 .spaceflow 目录及其配置文件 ([64b310d](https://github.com/Lydanne/spaceflow/commit/64b310d8a77614a259a8d7588a09169626efb3ae))
21
+ * **scripts:** released version 0.20.0 [no ci] ([e1fac49](https://github.com/Lydanne/spaceflow/commit/e1fac49257bf4a5902c5884ec0e054384a7859d6))
22
+ * **shell:** released version 0.20.0 [no ci] ([8b69b53](https://github.com/Lydanne/spaceflow/commit/8b69b5340fe99973add2bea3e7d53f2082d0da54))
23
+
24
+ ## [0.20.0](https://github.com/Lydanne/spaceflow/compare/@spaceflow/review-summary@0.19.0...@spaceflow/review-summary@0.20.0) (2026-02-27)
25
+
26
+ ### 新特性
27
+
28
+ * **core:** 为 Gitea 和 GitHub 适配器完善已解决评论列表功能 ([7134c83](https://github.com/Lydanne/spaceflow/commit/7134c83b0b440bdfb688d93e86c9552302bf45b2))
29
+ * **review:** 增强已解决问题同步功能,支持记录解决者 ([2c74996](https://github.com/Lydanne/spaceflow/commit/2c74996471e003f8666f8ccec715590f0f64c017))
30
+
31
+ ### 其他修改
32
+
33
+ * **core:** released version 0.18.0 [no ci] ([7d931eb](https://github.com/Lydanne/spaceflow/commit/7d931eba98a172cf6ad365f09b780251ad35b212))
34
+ * **publish:** released version 0.42.0 [no ci] ([61ac6b2](https://github.com/Lydanne/spaceflow/commit/61ac6b233564550d35e84759eff60a9e04181c46))
35
+ * **review:** released version 0.50.0 [no ci] ([cff42fa](https://github.com/Lydanne/spaceflow/commit/cff42fafcc588d0c497d9e0e4750620262adcfec))
36
+ * **scripts:** released version 0.19.0 [no ci] ([e198652](https://github.com/Lydanne/spaceflow/commit/e198652a1dcbd137dcd0fe4d7a2f404e12a991bc))
37
+ * **shell:** released version 0.19.0 [no ci] ([2dc2597](https://github.com/Lydanne/spaceflow/commit/2dc25974cfeac9c80d03a601e722133bccb25086))
38
+
3
39
  ## [0.19.0](https://github.com/Lydanne/spaceflow/compare/@spaceflow/review-summary@0.18.0...@spaceflow/review-summary@0.19.0) (2026-02-27)
4
40
 
5
41
  ### 修复BUG
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spaceflow/review-summary",
3
- "version": "0.20.0",
3
+ "version": "0.22.0",
4
4
  "description": "Spaceflow 审查统计命令,根据时间范围统计 PR 贡献情况,按人员汇总并排序",
5
5
  "license": "MIT",
6
6
  "author": "Lydanne",
@@ -14,12 +14,15 @@
14
14
  },
15
15
  "type": "module",
16
16
  "main": "./dist/index.js",
17
+ "dependencies": {
18
+ "@spaceflow/review": "0.57.0"
19
+ },
17
20
  "devDependencies": {
18
21
  "@types/node": "^22.15.0",
19
22
  "@spaceflow/cli": "0.38.0"
20
23
  },
21
24
  "peerDependencies": {
22
- "@spaceflow/core": "0.18.0"
25
+ "@spaceflow/core": "0.20.0"
23
26
  },
24
27
  "spaceflow": {
25
28
  "type": "flow",
@@ -1,6 +1,7 @@
1
1
  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
+ import { MarkdownFormatter, type ReviewIssue } from "@spaceflow/review";
4
5
  import { writeFileSync } from "fs";
5
6
  import { join } from "path";
6
7
  import type {
@@ -11,24 +12,32 @@ import type {
11
12
  UserStats,
12
13
  OutputTarget,
13
14
  TimePreset,
15
+ WeightedScoreWeights,
16
+ CommitBasedWeights,
17
+ ReviewSummaryConfig,
18
+ ScoreStrategy,
14
19
  } from "./types";
15
20
 
16
- /** 分数权重配置 */
17
- const SCORE_WEIGHTS = {
18
- /** 每个 PR 的基础分 */
21
+ /** 加权模式默认权重 */
22
+ const DEFAULT_WEIGHTED_WEIGHTS: Required<WeightedScoreWeights> = {
19
23
  prBase: 10,
20
- /** 每 100 行新增代码的分数 */
21
24
  additionsPer100: 2,
22
- /** 每 100 行删除代码的分数 */
23
25
  deletionsPer100: 1,
24
- /** 每个变更文件的分数 */
25
26
  changedFile: 0.5,
26
- /** 每个未修复问题的扣分 */
27
27
  issueDeduction: 3,
28
- /** 每个已修复问题的加分 */
29
28
  fixedBonus: 1,
30
29
  };
31
30
 
31
+ /** 分数累计模式默认权重 */
32
+ const DEFAULT_COMMIT_BASED_WEIGHTS: Required<CommitBasedWeights> = {
33
+ validCommit: 5,
34
+ errorDeduction: 2,
35
+ warnDeduction: 1,
36
+ errorFixedBonus: 1,
37
+ warnFixedBonus: 0.5,
38
+ minCommitLines: 5,
39
+ };
40
+
32
41
  /**
33
42
  * 周期统计服务
34
43
  */
@@ -140,7 +149,8 @@ export class PeriodSummaryService {
140
149
  } catch {
141
150
  // 如果获取文件失败,使用默认值
142
151
  }
143
- const { issueCount, fixedCount } = await this.extractIssueStats(owner, repo, pr.number!);
152
+ const issueStats = await this.extractIssueStats(owner, repo, pr.number!);
153
+ const validCommitCount = await this.countValidCommits(owner, repo, pr.number!);
144
154
  return {
145
155
  number: pr.number!,
146
156
  title: pr.title ?? "",
@@ -149,8 +159,8 @@ export class PeriodSummaryService {
149
159
  additions,
150
160
  deletions,
151
161
  changedFiles,
152
- issueCount,
153
- fixedCount,
162
+ ...issueStats,
163
+ validCommitCount,
154
164
  description: this.extractDescription(pr),
155
165
  };
156
166
  }
@@ -162,31 +172,125 @@ export class PeriodSummaryService {
162
172
  owner: string,
163
173
  repo: string,
164
174
  prNumber: number,
165
- ): Promise<{ issueCount: number; fixedCount: number }> {
175
+ ): Promise<{
176
+ issueCount: number;
177
+ fixedCount: number;
178
+ errorCount: number;
179
+ warnCount: number;
180
+ fixedErrors: number;
181
+ fixedWarns: number;
182
+ }> {
183
+ const empty = { issueCount: 0, fixedCount: 0, errorCount: 0, warnCount: 0, fixedErrors: 0, fixedWarns: 0 };
166
184
  try {
167
185
  const comments = await this.gitProvider.listIssueComments(owner, repo, prNumber);
168
- let issueCount = 0;
169
- let fixedCount = 0;
186
+ // 优先从 review 模块嵌入的结构化数据中精确提取
187
+ const formatter = new MarkdownFormatter();
170
188
  for (const comment of comments) {
171
189
  const body = comment.body ?? "";
172
- const issueMatch = body.match(/发现\s*(\d+)\s*个问题/);
173
- if (issueMatch) {
174
- issueCount = Math.max(issueCount, parseInt(issueMatch[1], 10));
175
- }
176
- const fixedMatch = body.match(/已修复[::]\s*(\d+)/);
177
- if (fixedMatch) {
178
- fixedCount = Math.max(fixedCount, parseInt(fixedMatch[1], 10));
190
+ const parsed = formatter.parse(body);
191
+ if (parsed?.result?.issues) {
192
+ return this.computeIssueStatsFromReviewIssues(parsed.result.issues);
179
193
  }
180
- const statsMatch = body.match(/🔴\s*(\d+).*🟡\s*(\d+)/);
181
- if (statsMatch) {
182
- const errorCount = parseInt(statsMatch[1], 10);
183
- const warnCount = parseInt(statsMatch[2], 10);
184
- issueCount = Math.max(issueCount, errorCount + warnCount);
194
+ }
195
+ // 回退:没有结构化数据时,从评论文本正则提取(兼容旧数据)
196
+ return this.extractIssueStatsFromText(comments);
197
+ } catch {
198
+ return empty;
199
+ }
200
+ }
201
+
202
+ /**
203
+ * 从 ReviewIssue 列表中精确计算各类问题统计
204
+ */
205
+ protected computeIssueStatsFromReviewIssues(issues: ReviewIssue[]): {
206
+ issueCount: number;
207
+ fixedCount: number;
208
+ errorCount: number;
209
+ warnCount: number;
210
+ fixedErrors: number;
211
+ fixedWarns: number;
212
+ } {
213
+ const errorCount = issues.filter((i) => i.severity === "error").length;
214
+ const warnCount = issues.filter((i) => i.severity === "warn").length;
215
+ const fixedErrors = issues.filter((i) => i.severity === "error" && i.fixed).length;
216
+ const fixedWarns = issues.filter((i) => i.severity === "warn" && i.fixed).length;
217
+ return {
218
+ issueCount: issues.length,
219
+ fixedCount: fixedErrors + fixedWarns,
220
+ errorCount,
221
+ warnCount,
222
+ fixedErrors,
223
+ fixedWarns,
224
+ };
225
+ }
226
+
227
+ /**
228
+ * 回退:从评论文本正则提取问题统计(兼容无结构化数据的旧评论)
229
+ */
230
+ protected extractIssueStatsFromText(
231
+ comments: { body?: string }[],
232
+ ): {
233
+ issueCount: number;
234
+ fixedCount: number;
235
+ errorCount: number;
236
+ warnCount: number;
237
+ fixedErrors: number;
238
+ fixedWarns: number;
239
+ } {
240
+ let issueCount = 0;
241
+ let fixedCount = 0;
242
+ let errorCount = 0;
243
+ let warnCount = 0;
244
+ for (const comment of comments) {
245
+ const body = comment.body ?? "";
246
+ const issueMatch = body.match(/发现\s*(\d+)\s*个问题/);
247
+ if (issueMatch) {
248
+ issueCount = Math.max(issueCount, parseInt(issueMatch[1], 10));
249
+ }
250
+ const fixedMatch = body.match(/已修复[::]\s*(\d+)/);
251
+ if (fixedMatch) {
252
+ fixedCount = Math.max(fixedCount, parseInt(fixedMatch[1], 10));
253
+ }
254
+ const statsMatch = body.match(/🔴\s*(\d+).*🟡\s*(\d+)/);
255
+ if (statsMatch) {
256
+ errorCount = Math.max(errorCount, parseInt(statsMatch[1], 10));
257
+ warnCount = Math.max(warnCount, parseInt(statsMatch[2], 10));
258
+ issueCount = Math.max(issueCount, errorCount + warnCount);
259
+ }
260
+ }
261
+ // 文本模式无法区分修复类型,统一设为 0
262
+ return { issueCount, fixedCount, errorCount, warnCount, fixedErrors: 0, fixedWarns: 0 };
263
+ }
264
+
265
+ /**
266
+ * 统计 PR 中有效 commit 数量(逐 commit 获取行数判断)
267
+ */
268
+ protected async countValidCommits(owner: string, repo: string, prNumber: number): Promise<number> {
269
+ const config = this.getStrategyConfig();
270
+ const minLines = config.commitBasedWeights?.minCommitLines ?? DEFAULT_COMMIT_BASED_WEIGHTS.minCommitLines;
271
+ try {
272
+ const commits = await this.gitProvider.getPullRequestCommits(owner, repo, prNumber);
273
+ let validCount = 0;
274
+ for (const commit of commits) {
275
+ if (!commit.sha) continue;
276
+ // 跳过 merge commit(commit message 以 "Merge" 开头)
277
+ if (commit.commit?.message?.startsWith("Merge")) continue;
278
+ try {
279
+ const commitInfo = await this.gitProvider.getCommit(owner, repo, commit.sha);
280
+ const totalLines = (commitInfo.files ?? []).reduce(
281
+ (sum, file) => sum + (file.additions ?? 0) + (file.deletions ?? 0),
282
+ 0,
283
+ );
284
+ if (totalLines >= minLines) {
285
+ validCount++;
286
+ }
287
+ } catch {
288
+ // 获取单个 commit 失败,跳过
185
289
  }
186
290
  }
187
- return { issueCount, fixedCount };
291
+ return validCount;
188
292
  } catch {
189
- return { issueCount: 0, fixedCount: 0 };
293
+ return 0;
190
294
  }
191
295
  }
192
296
 
@@ -216,6 +320,11 @@ export class PeriodSummaryService {
216
320
  totalChangedFiles: 0,
217
321
  totalIssues: 0,
218
322
  totalFixed: 0,
323
+ totalErrors: 0,
324
+ totalWarns: 0,
325
+ totalFixedErrors: 0,
326
+ totalFixedWarns: 0,
327
+ totalValidCommits: 0,
219
328
  score: 0,
220
329
  features: [],
221
330
  prs: [],
@@ -228,33 +337,82 @@ export class PeriodSummaryService {
228
337
  userStats.totalChangedFiles += pr.changedFiles;
229
338
  userStats.totalIssues += pr.issueCount;
230
339
  userStats.totalFixed += pr.fixedCount;
340
+ userStats.totalErrors += pr.errorCount;
341
+ userStats.totalWarns += pr.warnCount;
342
+ userStats.totalFixedErrors += pr.fixedErrors;
343
+ userStats.totalFixedWarns += pr.fixedWarns;
344
+ userStats.totalValidCommits += pr.validCommitCount;
231
345
  if (pr.description) {
232
346
  userStats.features.push(pr.description);
233
347
  }
234
348
  userStats.prs.push(pr);
235
349
  }
350
+ this.applyScoreStrategy(userMap);
351
+ return userMap;
352
+ }
353
+
354
+ /**
355
+ * 获取当前评分策略配置
356
+ */
357
+ protected getStrategyConfig(): ReviewSummaryConfig {
358
+ try {
359
+ return this.config.get<ReviewSummaryConfig>("review-summary") ?? {};
360
+ } catch {
361
+ return {};
362
+ }
363
+ }
364
+
365
+ /**
366
+ * 根据配置的策略计算所有用户的分数
367
+ */
368
+ protected applyScoreStrategy(userMap: Map<string, UserStats>): void {
369
+ const config = this.getStrategyConfig();
370
+ const strategy: ScoreStrategy = config.strategy ?? "weighted";
236
371
  for (const userStats of userMap.values()) {
237
- userStats.score = this.calculateScore(userStats);
372
+ switch (strategy) {
373
+ case "commit-based":
374
+ userStats.score = this.calculateCommitBasedScore(userStats, config);
375
+ break;
376
+ case "weighted":
377
+ default:
378
+ userStats.score = this.calculateWeightedScore(userStats, config);
379
+ break;
380
+ }
238
381
  }
239
- return userMap;
240
382
  }
241
383
 
242
384
  /**
243
- * 计算用户综合分数
385
+ * 加权模式:计算用户综合分数
244
386
  */
245
- protected calculateScore(stats: UserStats): number {
246
- const prScore = stats.prCount * SCORE_WEIGHTS.prBase;
247
- const additionsScore = (stats.totalAdditions / 100) * SCORE_WEIGHTS.additionsPer100;
248
- const deletionsScore = (stats.totalDeletions / 100) * SCORE_WEIGHTS.deletionsPer100;
249
- const filesScore = stats.totalChangedFiles * SCORE_WEIGHTS.changedFile;
387
+ protected calculateWeightedScore(stats: UserStats, config: ReviewSummaryConfig): number {
388
+ const weights = { ...DEFAULT_WEIGHTED_WEIGHTS, ...config.scoreWeights };
389
+ const prScore = stats.prCount * weights.prBase;
390
+ const additionsScore = (stats.totalAdditions / 100) * weights.additionsPer100;
391
+ const deletionsScore = (stats.totalDeletions / 100) * weights.deletionsPer100;
392
+ const filesScore = stats.totalChangedFiles * weights.changedFile;
250
393
  const unfixedIssues = stats.totalIssues - stats.totalFixed;
251
- const issueDeduction = unfixedIssues * SCORE_WEIGHTS.issueDeduction;
252
- const fixedBonus = stats.totalFixed * SCORE_WEIGHTS.fixedBonus;
394
+ const issueDeduction = unfixedIssues * weights.issueDeduction;
395
+ const fixedBonus = stats.totalFixed * weights.fixedBonus;
253
396
  const totalScore =
254
397
  prScore + additionsScore + deletionsScore + filesScore - issueDeduction + fixedBonus;
255
398
  return Math.max(0, Math.round(totalScore * 10) / 10);
256
399
  }
257
400
 
401
+ /**
402
+ * 分数累计模式:按有效 commit 加分,按 error/warn 扣分
403
+ */
404
+ protected calculateCommitBasedScore(stats: UserStats, config: ReviewSummaryConfig): number {
405
+ const weights = { ...DEFAULT_COMMIT_BASED_WEIGHTS, ...config.commitBasedWeights };
406
+ const commitScore = stats.totalValidCommits * weights.validCommit;
407
+ const errorDeduction = stats.totalErrors * weights.errorDeduction;
408
+ const warnDeduction = stats.totalWarns * weights.warnDeduction;
409
+ const fixedBonus =
410
+ stats.totalFixedErrors * weights.errorFixedBonus +
411
+ stats.totalFixedWarns * weights.warnFixedBonus;
412
+ const totalScore = commitScore - errorDeduction - warnDeduction + fixedBonus;
413
+ return Math.max(0, Math.round(totalScore * 10) / 10);
414
+ }
415
+
258
416
  /**
259
417
  * 按分数排序用户统计
260
418
  */
@@ -497,9 +655,12 @@ export class PeriodSummaryService {
497
655
  content: string,
498
656
  ): Promise<{ type: OutputTarget; location: string }> {
499
657
  const title = `📊 周期统计报告: ${result.period.since} ~ ${result.period.until}`;
658
+ const config = this.getStrategyConfig();
659
+ const labelName = config.issueLabel ?? "report";
500
660
  const issue: Issue = await this.gitProvider.createIssue(context.owner, context.repo, {
501
661
  title,
502
662
  body: content,
663
+ labels: [labelName],
503
664
  });
504
665
  const location = issue.html_url ?? `#${issue.number}`;
505
666
  if (shouldLog(context.verbose, 1)) {
package/src/types.ts CHANGED
@@ -71,6 +71,16 @@ export interface PrStats {
71
71
  issueCount: number;
72
72
  /** 已修复的问题数 */
73
73
  fixedCount: number;
74
+ /** error 级别问题数 */
75
+ errorCount: number;
76
+ /** warn 级别问题数 */
77
+ warnCount: number;
78
+ /** 已修复的 error 问题数 */
79
+ fixedErrors: number;
80
+ /** 已修复的 warn 问题数 */
81
+ fixedWarns: number;
82
+ /** 有效 commit 数(单个 commit 新增+删除 >= minCommitLines 行) */
83
+ validCommitCount: number;
74
84
  /** PR 描述/功能摘要 */
75
85
  description: string;
76
86
  }
@@ -91,6 +101,16 @@ export interface UserStats {
91
101
  totalIssues: number;
92
102
  /** 总已修复问题数 */
93
103
  totalFixed: number;
104
+ /** 总 error 级别问题数 */
105
+ totalErrors: number;
106
+ /** 总 warn 级别问题数 */
107
+ totalWarns: number;
108
+ /** 总已修复 error 数 */
109
+ totalFixedErrors: number;
110
+ /** 总已修复 warn 数 */
111
+ totalFixedWarns: number;
112
+ /** 总有效 commit 数 */
113
+ totalValidCommits: number;
94
114
  /** 综合分数 */
95
115
  score: number;
96
116
  /** 功能摘要列表 */
@@ -99,6 +119,56 @@ export interface UserStats {
99
119
  prs: PrStats[];
100
120
  }
101
121
 
122
+ /** 评分策略类型 */
123
+ export type ScoreStrategy = "weighted" | "commit-based";
124
+
125
+ /** 加权模式权重配置 */
126
+ export interface WeightedScoreWeights {
127
+ /** 每个 PR 的基础分,默认 10 */
128
+ prBase?: number;
129
+ /** 每 100 行新增代码的分数,默认 2 */
130
+ additionsPer100?: number;
131
+ /** 每 100 行删除代码的分数,默认 1 */
132
+ deletionsPer100?: number;
133
+ /** 每个变更文件的分数,默认 0.5 */
134
+ changedFile?: number;
135
+ /** 每个未修复问题的扣分,默认 3 */
136
+ issueDeduction?: number;
137
+ /** 每个已修复问题的加分,默认 1 */
138
+ fixedBonus?: number;
139
+ }
140
+
141
+ /** 分数累计模式权重配置 */
142
+ export interface CommitBasedWeights {
143
+ /** 每个有效 commit 的加分,默认 5 */
144
+ validCommit?: number;
145
+ /** 每个 error 问题的扣分,默认 2 */
146
+ errorDeduction?: number;
147
+ /** 每个 warn 问题的扣分,默认 1 */
148
+ warnDeduction?: number;
149
+ /** 修复一个 error 问题的加分,默认为 errorDeduction 的一半(1) */
150
+ errorFixedBonus?: number;
151
+ /** 修复一个 warn 问题的加分,默认为 warnDeduction 的一半(0.5) */
152
+ warnFixedBonus?: number;
153
+ /** 有效 commit 的最低代码行数(新增+删除),默认 5 */
154
+ minCommitLines?: number;
155
+ }
156
+
157
+ /** @deprecated 使用 WeightedScoreWeights 代替 */
158
+ export type ScoreWeights = WeightedScoreWeights;
159
+
160
+ /** review-summary 扩展配置 */
161
+ export interface ReviewSummaryConfig {
162
+ /** 评分策略,默认 "weighted" */
163
+ strategy?: ScoreStrategy;
164
+ /** 加权模式权重配置(strategy 为 "weighted" 时生效) */
165
+ scoreWeights?: WeightedScoreWeights;
166
+ /** 分数累计模式权重配置(strategy 为 "commit-based" 时生效) */
167
+ commitBasedWeights?: CommitBasedWeights;
168
+ /** 创建 Issue 时添加的标签名称,默认 "report" */
169
+ issueLabel?: string;
170
+ }
171
+
102
172
  /** 周期统计结果 */
103
173
  export interface PeriodSummaryResult {
104
174
  /** 统计周期 */