@hecom/codearts 0.3.1 → 0.4.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/README.md CHANGED
@@ -31,7 +31,7 @@ npx @hecom/codearts bug-rate "迭代1,迭代2"
31
31
  ### 3. 更新配置
32
32
 
33
33
  ```bash
34
- # 更新全局配置
34
+ # 更新配置文件
35
35
  npx @hecom/codearts config
36
36
 
37
37
  # 单独更新角色配置
package/dist/bin/cli.js CHANGED
@@ -40,6 +40,7 @@ const path = __importStar(require("path"));
40
40
  const bug_command_1 = require("../commands/bug.command");
41
41
  const config_command_1 = require("../commands/config.command");
42
42
  const daily_command_1 = require("../commands/daily.command");
43
+ const fix_command_1 = require("../commands/fix.command");
43
44
  const work_hour_command_1 = require("../commands/work-hour.command");
44
45
  const config_loader_1 = require("../utils/config-loader");
45
46
  const console_1 = require("../utils/console");
@@ -57,7 +58,7 @@ program
57
58
  // config 命令 - 交互式配置向导
58
59
  const configCmd = program
59
60
  .command('config')
60
- .description('交互式配置向导,引导用户创建或更新全局配置文件')
61
+ .description('交互式配置向导,引导用户创建或更新配置文件')
61
62
  .action(async () => {
62
63
  (0, console_1.showLogo)();
63
64
  await (0, config_command_1.configCommand)();
@@ -85,7 +86,7 @@ availableConfigs.forEach((configItem) => {
85
86
  // daily 命令
86
87
  program
87
88
  .command('daily [date]')
88
- .description('每日工时统计(默认日期为当天)')
89
+ .description('每日工时统计(默认日期为当天),日期格式:YYYY-MM-DD')
89
90
  .option('-r, --report', '显示总结报告', false)
90
91
  .action(async (date, options, command) => {
91
92
  const cliOptions = { ...command.parent.opts(), report: options.report };
@@ -95,7 +96,7 @@ program
95
96
  // work-hour 命令
96
97
  program
97
98
  .command('work-hour [year]')
98
- .description('年度工时统计(默认当前年份)')
99
+ .description('年度工时统计(默认当前年份),年份格式:YYYY')
99
100
  .action(async (year, options, command) => {
100
101
  const cliOptions = command.parent.opts();
101
102
  logger_1.logger.setOutputFormat(cliOptions.output);
@@ -110,13 +111,22 @@ program
110
111
  logger_1.logger.setOutputFormat(cliOptions.output);
111
112
  await (0, bug_command_1.bugCommand)(cliOptions);
112
113
  });
114
+ // fix 命令
115
+ program
116
+ .command('fix')
117
+ .description('交互式修复 bug,填写相关信息')
118
+ .action(async (options, command) => {
119
+ const cliOptions = command.parent.opts();
120
+ logger_1.logger.setOutputFormat(cliOptions.output);
121
+ await (0, fix_command_1.fixCommand)(cliOptions);
122
+ });
113
123
  // 检查配置并自动执行 config 命令
114
124
  async function checkConfigAndRun() {
115
125
  const args = process.argv.slice(2);
116
126
  // 如果没有参数(直接执行 codearts),检测配置
117
127
  if (args.length === 0) {
118
128
  (0, console_1.showLogo)();
119
- // 检查是否有全局配置
129
+ // 检查是否有配置文件
120
130
  const hasConfig = (0, config_loader_1.configExists)();
121
131
  if (!hasConfig) {
122
132
  // 没有配置,自动执行 config 命令
@@ -10,7 +10,7 @@ interface ProjectConfigItem {
10
10
  }
11
11
  /**
12
12
  * 交互式配置向导命令
13
- * 引导用户创建或更新全局配置文件
13
+ * 引导用户创建或更新配置文件
14
14
  */
15
15
  export declare function configCommand(): Promise<void>;
16
16
  /**
@@ -139,7 +139,7 @@ async function inputPassword() {
139
139
  }
140
140
  /**
141
141
  * 交互式配置向导命令
142
- * 引导用户创建或更新全局配置文件
142
+ * 引导用户创建或更新配置文件
143
143
  */
144
144
  async function configCommand() {
145
145
  logger_1.logger.info('欢迎使用 Hecom CodeArts 配置向导');
@@ -149,7 +149,7 @@ async function configCommand() {
149
149
  const existingConfig = (0, config_loader_1.configExists)() ? (0, config_loader_1.readConfig)() : {};
150
150
  if ((0, config_loader_1.configExists)()) {
151
151
  const overwrite = await (0, prompts_1.confirm)({
152
- message: '检测到已存在全局配置,是否覆盖?',
152
+ message: '检测到已存在配置文件,是否覆盖?',
153
153
  default: true,
154
154
  });
155
155
  if (!overwrite) {
@@ -188,9 +188,9 @@ async function configCommand() {
188
188
  validate: (inputValue) => (inputValue.trim() ? true : 'CodeArts API 地址不能为空'),
189
189
  }),
190
190
  domain: await (0, prompts_1.input)({
191
- message: '华为云账号名:',
191
+ message: '租户名/原华为云账号:',
192
192
  default: existingConfig[types_1.ConfigKey.HUAWEI_CLOUD_DOMAIN] || '',
193
- validate: (inputValue) => (inputValue.trim() ? true : '华为云账号名不能为空'),
193
+ validate: (inputValue) => inputValue.trim() ? true : '租户名/原华为云账号不能为空',
194
194
  }),
195
195
  username: await (0, prompts_1.input)({
196
196
  message: 'IAM 用户名:',
@@ -308,7 +308,7 @@ async function configCommand() {
308
308
  };
309
309
  try {
310
310
  (0, config_loader_1.writeConfig)(finalConfig);
311
- logger_1.logger.success('\n✅ 全局配置已成功保存');
311
+ logger_1.logger.success('\n✅ 配置完成!');
312
312
  logger_1.logger.info(`配置文件位置: ${(0, config_loader_1.getConfigPath)()}`);
313
313
  logger_1.logger.info('\n提示:配置文件包含敏感信息,请妥善保管。');
314
314
  }
@@ -324,7 +324,7 @@ async function configCommand() {
324
324
  async function updateProjectConfigCommand(configKey) {
325
325
  // 检查配置文件是否存在
326
326
  if (!(0, config_loader_1.configExists)()) {
327
- logger_1.logger.error('\n❌ 全局配置文件不存在,请先运行 `npx @hecom/codearts config` 创建配置。');
327
+ logger_1.logger.error('\n❌ 配置文件不存在,请先运行 `npx @hecom/codearts config` 创建配置。');
328
328
  process.exit(1);
329
329
  }
330
330
  // 查找对应的配置项
@@ -0,0 +1,6 @@
1
+ import { CliOptions } from '../utils/config-loader';
2
+ /**
3
+ * Fix 命令:交互式修复 bug,填写缺陷分析信息
4
+ * @param cliOptions 命令行选项
5
+ */
6
+ export declare function fixCommand(cliOptions?: CliOptions): Promise<void>;
@@ -0,0 +1,220 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.fixCommand = fixCommand;
7
+ const prompts_1 = require("@inquirer/prompts");
8
+ const ora_1 = __importDefault(require("ora"));
9
+ const business_service_1 = require("../services/business.service");
10
+ const types_1 = require("../types");
11
+ const config_loader_1 = require("../utils/config-loader");
12
+ const console_1 = require("../utils/console");
13
+ const logger_1 = require("../utils/logger");
14
+ /**
15
+ * Fix 命令:交互式修复 bug,填写缺陷分析信息
16
+ * @param cliOptions 命令行选项
17
+ */
18
+ async function fixCommand(cliOptions) {
19
+ try {
20
+ // Step 1: 加载配置
21
+ const { projectId, config } = (0, config_loader_1.loadConfig)(cliOptions);
22
+ const businessService = new business_service_1.BusinessService(config);
23
+ if (!projectId) {
24
+ logger_1.logger.error('项目 ID 未配置,请先执行 codearts config');
25
+ return;
26
+ }
27
+ // Step 2: 加载 bug 列表和自定义字段选项
28
+ const spinner = (0, ora_1.default)('正在加载数据...').start();
29
+ let bugList;
30
+ let customFieldOptions;
31
+ try {
32
+ bugList = await businessService.getCurrentUserBugs(projectId);
33
+ customFieldOptions = await businessService.getCustomFieldOptions(projectId, [
34
+ types_1.CustomFieldId.DEFECT_TECHNICAL_ANALYSIS,
35
+ types_1.CustomFieldId.INTRODUCTION_PHASE,
36
+ ]);
37
+ spinner.succeed('数据加载完成');
38
+ }
39
+ catch (error) {
40
+ spinner.fail('数据加载失败');
41
+ logger_1.logger.error(`${String(error)}`);
42
+ return;
43
+ }
44
+ // Step 3: 检查 bug 列表是否为空
45
+ if (bugList.length === 0) {
46
+ logger_1.logger.warn('当前用户没有分配的 bug');
47
+ return;
48
+ }
49
+ // Step 4: 让用户选择 bug
50
+ const selectedBug = await selectBug(bugList);
51
+ if (!selectedBug) {
52
+ logger_1.logger.info('操作取消');
53
+ return;
54
+ }
55
+ // Step 5: 填写缺陷分析信息
56
+ const bugFixData = {};
57
+ // 5.1: 选择缺陷技术分析(必填)
58
+ const defectAnalysisOptions = customFieldOptions[types_1.CustomFieldId.DEFECT_TECHNICAL_ANALYSIS] || [];
59
+ if (defectAnalysisOptions.length === 0) {
60
+ logger_1.logger.error('无法获取缺陷技术分析选项');
61
+ return;
62
+ }
63
+ bugFixData.defectAnalysis = await (0, prompts_1.select)({
64
+ message: '请选择缺陷技术分析(必填)',
65
+ choices: defectAnalysisOptions.map((option) => ({
66
+ name: option,
67
+ value: option,
68
+ })),
69
+ });
70
+ // 5.2: 填写问题原因及解决办法(可选)
71
+ const problemReason = await (0, prompts_1.input)({
72
+ message: '请输入问题原因及解决办法(可选,留空跳过)',
73
+ });
74
+ if (problemReason.trim()) {
75
+ bugFixData.problemReason = problemReason;
76
+ }
77
+ // 5.3: 填写影响范围(可选)
78
+ const impactScope = await (0, prompts_1.input)({
79
+ message: '请输入影响范围(可选,留空跳过)',
80
+ });
81
+ if (impactScope.trim()) {
82
+ bugFixData.impactScope = impactScope;
83
+ }
84
+ // Step 6: 检查是否为客户反馈 bug
85
+ const isCustomerFeedback = checkIsCustomerFeedback(selectedBug);
86
+ if (isCustomerFeedback) {
87
+ // 6.1: 选择引入阶段(必填)
88
+ const introductionPhaseOptions = customFieldOptions[types_1.CustomFieldId.INTRODUCTION_PHASE] || [];
89
+ if (introductionPhaseOptions.length === 0) {
90
+ logger_1.logger.error('无法获取引入阶段选项');
91
+ return;
92
+ }
93
+ bugFixData.introductionStage = await (0, prompts_1.select)({
94
+ message: '请选择引入阶段(必填)',
95
+ choices: introductionPhaseOptions
96
+ .sort()
97
+ .reverse()
98
+ .map((option) => ({
99
+ name: option,
100
+ value: option,
101
+ })),
102
+ });
103
+ // 6.2: 输入发布时间(必填)
104
+ let releaseDate;
105
+ let validDate = false;
106
+ while (!validDate) {
107
+ releaseDate = await (0, prompts_1.input)({
108
+ message: '请输入发布时间(必填,格式:YYYY/M/D)',
109
+ validate: (value) => {
110
+ if (!value) {
111
+ return '发布时间不能为空';
112
+ }
113
+ const dateRegex = /^\d{4}\/\d{1,2}\/\d{1,2}$/;
114
+ if (!dateRegex.test(value)) {
115
+ return '日期格式不正确,请使用 YYYY/M/D 格式';
116
+ }
117
+ const date = new Date(value);
118
+ if (isNaN(date.getTime())) {
119
+ return '请输入有效的日期';
120
+ }
121
+ return true;
122
+ },
123
+ });
124
+ validDate = true;
125
+ bugFixData.releaseDate = releaseDate;
126
+ }
127
+ }
128
+ // Step 7: 确认并提交
129
+ const confirmed = await showSummaryAndConfirm(selectedBug, bugFixData, isCustomerFeedback);
130
+ if (!confirmed) {
131
+ logger_1.logger.info('操作已取消');
132
+ return;
133
+ }
134
+ // Step 8: 调用 API 更新 bug
135
+ const updateSpinner = (0, ora_1.default)('正在保存缺陷信息...').start();
136
+ try {
137
+ await businessService.fixBug(projectId, selectedBug, bugFixData);
138
+ updateSpinner.succeed('缺陷信息保存成功!');
139
+ logger_1.logger.info(`\nBug 链接:${(0, console_1.issueLink)(projectId, String(selectedBug.id))}`);
140
+ // Step 9: 询问是否继续修复
141
+ const continueIterate = await (0, prompts_1.confirm)({
142
+ message: '是否继续修复?',
143
+ default: false,
144
+ });
145
+ if (continueIterate) {
146
+ await fixCommand(cliOptions);
147
+ }
148
+ }
149
+ catch (error) {
150
+ updateSpinner.fail('保存失败');
151
+ logger_1.logger.error(`${String(error)}`);
152
+ }
153
+ }
154
+ catch (error) {
155
+ if (error instanceof Error && error.name === 'ExitPromptError') {
156
+ logger_1.logger.info('操作取消');
157
+ }
158
+ else {
159
+ logger_1.logger.error(`执行 fix 命令失败: ${String(error)}`);
160
+ }
161
+ }
162
+ }
163
+ /**
164
+ * 让用户选择一个 bug
165
+ * @param bugList bug 列表
166
+ * @returns 选中的 bug,或 null 如果用户取消
167
+ */
168
+ async function selectBug(bugList) {
169
+ const bugChoices = bugList.map((bug) => {
170
+ const statusName = bug.status?.name || '未知状态';
171
+ const name = bug.name.length > 40 ? `${bug.name.slice(0, 47)}...` : bug.name;
172
+ const label = `[${statusName}] ${name}`;
173
+ return {
174
+ name: label,
175
+ value: bug,
176
+ };
177
+ });
178
+ const selectedBug = await (0, prompts_1.select)({
179
+ message: '请选择要修复的 bug',
180
+ choices: bugChoices,
181
+ pageSize: 10,
182
+ });
183
+ return selectedBug || null;
184
+ }
185
+ /**
186
+ * 检查 bug 是否为客户反馈类型
187
+ * @param bug bug 工作项
188
+ * @returns 是否为客户反馈
189
+ */
190
+ function checkIsCustomerFeedback(bug) {
191
+ const customFields = bug.new_custom_fields || [];
192
+ const defectTypeField = customFields.find((field) => field?.custom_field === types_1.CustomFieldId.DEFECT_TYPE || field?.field_name === '缺陷类型');
193
+ const defectType = defectTypeField?.value || '';
194
+ return defectType === '客户反馈';
195
+ }
196
+ /**
197
+ * 显示总结信息并确认提交
198
+ * @param bug 选中的 bug
199
+ * @param bugFixData 填写的缺陷信息
200
+ * @param isCustomerFeedback 是否为客户反馈
201
+ * @returns 用户是否确认提交
202
+ */
203
+ async function showSummaryAndConfirm(bug, bugFixData, isCustomerFeedback) {
204
+ logger_1.logger.info('\n========== 填写信息总结 ==========\n');
205
+ logger_1.logger.info(`Bug ID: #${bug.id}`);
206
+ logger_1.logger.info(`Bug 标题: ${bug.name}`);
207
+ logger_1.logger.info(`缺陷技术分析: ${bugFixData.defectAnalysis || '未填写'}`);
208
+ logger_1.logger.info(`问题原因: ${bugFixData.problemReason || '未填写'}`);
209
+ logger_1.logger.info(`影响范围: ${bugFixData.impactScope || '未填写'}`);
210
+ if (isCustomerFeedback) {
211
+ logger_1.logger.info(`引入阶段: ${bugFixData.introductionStage || '未填写'}`);
212
+ logger_1.logger.info(`发布时间: ${bugFixData.releaseDate || '未填写'}`);
213
+ }
214
+ logger_1.logger.info('\n==================================\n');
215
+ const confirmed = await (0, prompts_1.confirm)({
216
+ message: '确认提交以上信息?',
217
+ default: true,
218
+ });
219
+ return confirmed;
220
+ }
@@ -2,3 +2,4 @@ export { dailyCommand } from './daily.command';
2
2
  export { workHourCommand } from './work-hour.command';
3
3
  export { configCommand } from './config.command';
4
4
  export { bugCommand } from './bug.command';
5
+ export { fixCommand } from './fix.command';
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.bugCommand = exports.configCommand = exports.workHourCommand = exports.dailyCommand = void 0;
3
+ exports.fixCommand = exports.bugCommand = exports.configCommand = exports.workHourCommand = exports.dailyCommand = void 0;
4
4
  var daily_command_1 = require("./daily.command");
5
5
  Object.defineProperty(exports, "dailyCommand", { enumerable: true, get: function () { return daily_command_1.dailyCommand; } });
6
6
  var work_hour_command_1 = require("./work-hour.command");
@@ -9,3 +9,5 @@ var config_command_1 = require("./config.command");
9
9
  Object.defineProperty(exports, "configCommand", { enumerable: true, get: function () { return config_command_1.configCommand; } });
10
10
  var bug_command_1 = require("./bug.command");
11
11
  Object.defineProperty(exports, "bugCommand", { enumerable: true, get: function () { return bug_command_1.bugCommand; } });
12
+ var fix_command_1 = require("./fix.command");
13
+ Object.defineProperty(exports, "fixCommand", { enumerable: true, get: function () { return fix_command_1.fixCommand; } });
@@ -1,4 +1,4 @@
1
- import { ApiResponse, CachedToken, HuaweiCloudConfig, ListChildIssuesV2Response, ListChildIssuesV4Response, ListIssuesV4Request, ListIssuesV4Response, ListProjectIterationsV4Request, ListProjectIterationsV4Response, ProjectListResponse, ProjectMemberListResponse, ProjectMemberQueryParams, ProjectQueryParams, ShowProjectWorkHoursRequest, ShowProjectWorkHoursResponse } from '../types';
1
+ import { AddIssueNotesRequest, AddIssueNotesResponse, ApiResponse, CachedToken, CurrentUserInfo, GetCustomFieldsResponse, HuaweiCloudConfig, ListChildIssuesV2Response, ListChildIssuesV4Response, ListIssuesV4Request, ListIssuesV4Response, ListProjectIterationsV4Request, ListProjectIterationsV4Response, ProjectListResponse, ProjectMemberListResponse, ProjectMemberQueryParams, ProjectQueryParams, ShowProjectWorkHoursRequest, ShowProjectWorkHoursResponse, UpdateIssueRequest } from '../types';
2
2
  /**
3
3
  * 华为云CodeArts API服务类
4
4
  * 支持IAM Token认证和CodeArts API调用
@@ -71,10 +71,14 @@ export declare class ApiService {
71
71
  * 创建工作项
72
72
  */
73
73
  createIssue(projectId: string, issueData: unknown): Promise<ApiResponse<unknown>>;
74
+ /**
75
+ * 工作项添加评论
76
+ */
77
+ addIssueNotes(params: AddIssueNotesRequest): Promise<ApiResponse<AddIssueNotesResponse>>;
74
78
  /**
75
79
  * 更新工作项
76
80
  */
77
- updateIssue(projectId: string, issueId: string, issueData: unknown): Promise<ApiResponse<unknown>>;
81
+ updateIssue(projectId: string, issueId: string, issueData: UpdateIssueRequest): Promise<ApiResponse<unknown>>;
78
82
  /**
79
83
  * 删除工作项
80
84
  */
@@ -91,10 +95,18 @@ export declare class ApiService {
91
95
  * 获取项目成员列表
92
96
  */
93
97
  getMembers(projectId: string, params?: ProjectMemberQueryParams): Promise<ApiResponse<ProjectMemberListResponse>>;
98
+ /**
99
+ * 获取当前用户信息
100
+ */
101
+ showCurUserInfo(): Promise<ApiResponse<CurrentUserInfo>>;
94
102
  /**
95
103
  * 按用户查询工时(单项目)
96
104
  */
97
105
  showProjectWorkHours(projectId: string, params?: ShowProjectWorkHoursRequest): Promise<ApiResponse<ShowProjectWorkHoursResponse>>;
106
+ /**
107
+ * 获取自定义字段信息
108
+ */
109
+ getCustomFields(projectId: string, customFieldIds: string[]): Promise<ApiResponse<GetCustomFieldsResponse>>;
98
110
  /**
99
111
  * 获取当前Token信息(用于调试)
100
112
  */
@@ -347,6 +347,15 @@ class ApiService {
347
347
  data: issueData,
348
348
  });
349
349
  }
350
+ /**
351
+ * 工作项添加评论
352
+ */
353
+ async addIssueNotes(params) {
354
+ return this.request('/v2/issues/update-issue-notes', {
355
+ method: 'POST',
356
+ data: params,
357
+ });
358
+ }
350
359
  /**
351
360
  * 更新工作项
352
361
  */
@@ -394,6 +403,14 @@ class ApiService {
394
403
  },
395
404
  });
396
405
  }
406
+ /**
407
+ * 获取当前用户信息
408
+ */
409
+ async showCurUserInfo() {
410
+ return this.request('/v4/user', {
411
+ method: 'GET',
412
+ });
413
+ }
397
414
  /**
398
415
  * 按用户查询工时(单项目)
399
416
  */
@@ -407,6 +424,17 @@ class ApiService {
407
424
  },
408
425
  });
409
426
  }
427
+ /**
428
+ * 获取自定义字段信息
429
+ */
430
+ async getCustomFields(projectId, customFieldIds) {
431
+ return this.request(`/v4/projects/${projectId}/issues/custom-fields`, {
432
+ method: 'POST',
433
+ data: {
434
+ custom_fields: customFieldIds,
435
+ },
436
+ });
437
+ }
410
438
  /**
411
439
  * 获取当前Token信息(用于调试)
412
440
  */
@@ -1,4 +1,4 @@
1
- import { AllWorkHourStats, HuaweiCloudConfig, IssueItem, IssueItemV2, IterationInfo, ProjectMember, ProjectRole, WorkHourStats, WorkProgressStats } from '../types';
1
+ import { AllWorkHourStats, BugFixData, HuaweiCloudConfig, IssueItem, IssueItemV2, IterationInfo, ProjectMember, ProjectRole, WorkHourStats, WorkProgressStats } from '../types';
2
2
  /**
3
3
  * 业务服务类
4
4
  * 提供面向业务场景的高级操作,封装ApiService的底层调用
@@ -43,6 +43,7 @@ export declare class BusinessService {
43
43
  * @returns Task类型的工作项列表
44
44
  */
45
45
  getWorkloadByIterationsAndUsers(projectId: string, iterationIds: number[], userIds: string[]): Promise<IssueItem[]>;
46
+ addIssueNote(projectId: string, issueId: number, content: string): Promise<unknown>;
46
47
  /**
47
48
  * 统计工作项进度信息
48
49
  * @param issues 工作项列表
@@ -101,4 +102,35 @@ export declare class BusinessService {
101
102
  * @returns 项目列表
102
103
  */
103
104
  getProjects(limit?: number): Promise<import('../types').Project[]>;
105
+ /**
106
+ * 查询当前用户的所有 Bug
107
+ * @param projectId 项目ID
108
+ * @param options 查询选项
109
+ * @param options.hasIteration 是否有迭代(true: 有迭代, false: 无迭代, undefined: 不过滤)
110
+ * @returns Bug 类型的工作项列表,按创建时间倒序排列
111
+ */
112
+ getCurrentUserBugs(projectId: string, options?: {
113
+ hasIteration?: boolean;
114
+ }): Promise<IssueItem[]>;
115
+ /**
116
+ * 批量获取自定义字段的选项
117
+ * @param projectId 项目ID
118
+ * @param customFieldIds 自定义字段ID列表
119
+ * @returns 自定义字段选项映射,key为字段ID,value为选项数组
120
+ */
121
+ getCustomFieldOptions(projectId: string, customFieldIds: string[]): Promise<Record<string, string[]>>;
122
+ /**
123
+ * 修复缺陷工作项
124
+ * 根据填写的缺陷分析信息更新缺陷工作项,仅当问题为缺陷且状态为可处理状态时才更新
125
+ * @param projectId 项目ID
126
+ * @param issue 选中的缺陷工作项
127
+ * @param bugFixData 缺陷分析数据
128
+ */
129
+ fixBug(projectId: string, issue: IssueItem, bugFixData: BugFixData): Promise<void>;
130
+ /**
131
+ * 将日期字符串解析为时间戳
132
+ * @param dateStr 日期字符串,格式:YYYY-MM-DD
133
+ * @returns 毫秒时间戳,如果解析失败返回 null
134
+ */
135
+ private parseDateToTimestamp;
104
136
  }
@@ -124,7 +124,7 @@ class BusinessService {
124
124
  }
125
125
  const issuesResponse = await this.apiService.getIssues(projectId, {
126
126
  iteration_ids: iterationIds,
127
- tracker_ids: [2], // 2=Task(任务), 7=Story
127
+ tracker_ids: [types_1.IssueTrackerId.TASK], // Task
128
128
  assigned_ids: userIds,
129
129
  limit: 100,
130
130
  offset: 0,
@@ -134,6 +134,17 @@ class BusinessService {
134
134
  }
135
135
  return issuesResponse.data?.issues || [];
136
136
  }
137
+ async addIssueNote(projectId, issueId, content) {
138
+ const result = await this.apiService.addIssueNotes({
139
+ projectUUId: projectId,
140
+ id: String(issueId),
141
+ notes: content,
142
+ });
143
+ if (result.data?.status === 'success') {
144
+ return result.data.result.issue;
145
+ }
146
+ throw new Error(`添加工作项备注失败: ${result.data?.status || '未知错误'}`);
147
+ }
137
148
  /**
138
149
  * 统计工作项进度信息
139
150
  * @param issues 工作项列表
@@ -157,8 +168,10 @@ class BusinessService {
157
168
  };
158
169
  }
159
170
  stats[userId].count++;
160
- // 如果状态是已关闭(id=5),expectedHours 取实际工时,否则取预估工时
161
- const expectedHours = issue.status?.id === 5 ? issue.actual_work_hours || 0 : issue.expected_work_hours || 0;
171
+ // 如果状态是已关闭,expectedHours 取实际工时,否则取预估工时
172
+ const expectedHours = issue.status?.id === types_1.IssueStatusId.CLOSED
173
+ ? issue.actual_work_hours || 0
174
+ : issue.expected_work_hours || 0;
162
175
  stats[userId].expectedHours += expectedHours;
163
176
  stats[userId].actualHours += issue.actual_work_hours || 0;
164
177
  // 累计总工时
@@ -268,7 +281,7 @@ class BusinessService {
268
281
  const issuesResponse = await this.apiService.getIssues(projectId, {
269
282
  iteration_ids: iterationIds,
270
283
  assigned_ids: userIds,
271
- tracker_ids: [7], // 7=Story
284
+ tracker_ids: [types_1.IssueTrackerId.STORY], // Story
272
285
  include_deleted: false,
273
286
  limit: pageSize,
274
287
  offset: offset,
@@ -340,7 +353,7 @@ class BusinessService {
340
353
  // Step 4: 为每个工时记录关联领域信息
341
354
  const enrichedWorkHours = allWorkHours.map((workHour) => {
342
355
  const issue = issueDetailsMap.get(workHour.issue_id);
343
- const isBug = issue?.tracker?.id === 3; // 3=Bug
356
+ const isBug = issue?.tracker?.id === types_1.IssueTrackerId.BUG;
344
357
  const type = isBug ? issue?.tracker?.name || '' : issue?.domain?.name || '未分配领域';
345
358
  return {
346
359
  ...workHour,
@@ -451,5 +464,186 @@ class BusinessService {
451
464
  }
452
465
  return response.data.projects;
453
466
  }
467
+ /**
468
+ * 查询当前用户的所有 Bug
469
+ * @param projectId 项目ID
470
+ * @param options 查询选项
471
+ * @param options.hasIteration 是否有迭代(true: 有迭代, false: 无迭代, undefined: 不过滤)
472
+ * @returns Bug 类型的工作项列表,按创建时间倒序排列
473
+ */
474
+ async getCurrentUserBugs(projectId, options = {}) {
475
+ // Step 1: 获取当前用户信息
476
+ const userInfoResponse = await this.apiService.showCurUserInfo();
477
+ if (!userInfoResponse.success || !userInfoResponse.data) {
478
+ throw new Error(`获取当前用户信息失败: ${userInfoResponse.error || '未知错误'}`);
479
+ }
480
+ const currentUserId = userInfoResponse.data.user_id;
481
+ // Step 2: 分页查询所有 Bug(tracker_id = 3 为 Bug)
482
+ const allBugs = [];
483
+ const pageSize = 100;
484
+ let offset = 0;
485
+ let hasMore = true;
486
+ while (hasMore) {
487
+ const issuesResponse = await this.apiService.getIssues(projectId, {
488
+ assigned_ids: [currentUserId],
489
+ tracker_ids: [types_1.IssueTrackerId.BUG], // Bug
490
+ status_ids: [
491
+ types_1.IssueStatusId.NEW_ISSUE,
492
+ types_1.IssueStatusId.REOPENED,
493
+ types_1.IssueStatusId.NEW_REQUIREMENT,
494
+ ], // 可处理状态
495
+ include_deleted: false,
496
+ limit: pageSize,
497
+ offset: offset,
498
+ });
499
+ if (!issuesResponse.success) {
500
+ throw new Error(`获取Bug列表失败: ${issuesResponse.error || '未知错误'}`);
501
+ }
502
+ const bugs = issuesResponse.data?.issues || [];
503
+ allBugs.push(...bugs);
504
+ // 判断是否还有更多数据
505
+ const total = issuesResponse.data?.total || 0;
506
+ offset += pageSize;
507
+ hasMore = offset < total;
508
+ }
509
+ // Step 3: 按照迭代状态过滤
510
+ let filteredBugs = allBugs;
511
+ if (options.hasIteration !== undefined) {
512
+ filteredBugs = allBugs.filter((bug) => {
513
+ const hasIteration = bug.iteration && bug.iteration.id > 0;
514
+ return options.hasIteration ? hasIteration : !hasIteration;
515
+ });
516
+ }
517
+ // Step 4: 按创建时间倒序排列
518
+ filteredBugs.sort((a, b) => {
519
+ const timeA = new Date(a.created_time).getTime();
520
+ const timeB = new Date(b.created_time).getTime();
521
+ return timeB - timeA; // 倒序
522
+ });
523
+ return filteredBugs;
524
+ }
525
+ /**
526
+ * 批量获取自定义字段的选项
527
+ * @param projectId 项目ID
528
+ * @param customFieldIds 自定义字段ID列表
529
+ * @returns 自定义字段选项映射,key为字段ID,value为选项数组
530
+ */
531
+ async getCustomFieldOptions(projectId, customFieldIds) {
532
+ if (customFieldIds.length === 0) {
533
+ return {};
534
+ }
535
+ const response = await this.apiService.getCustomFields(projectId, customFieldIds);
536
+ if (!response.success || !response.data) {
537
+ throw new Error(`获取自定义字段信息失败: ${response.error || '未知错误'}`);
538
+ }
539
+ // 将自定义字段列表转换为 fieldId -> options 的映射
540
+ const optionsMap = {};
541
+ response.data.datas.forEach((field) => {
542
+ // 如果 options 为 null,返回空数组;否则将逗号分隔的字符串解析为数组
543
+ optionsMap[field.custom_field] = field.options
544
+ ? field.options.split(',').map((option) => option.trim())
545
+ : [];
546
+ });
547
+ return optionsMap;
548
+ }
549
+ /**
550
+ * 修复缺陷工作项
551
+ * 根据填写的缺陷分析信息更新缺陷工作项,仅当问题为缺陷且状态为可处理状态时才更新
552
+ * @param projectId 项目ID
553
+ * @param issue 选中的缺陷工作项
554
+ * @param bugFixData 缺陷分析数据
555
+ */
556
+ async fixBug(projectId, issue, bugFixData) {
557
+ const { defectAnalysis, problemReason, impactScope, introductionStage, releaseDate } = bugFixData;
558
+ // 验证工作项是否为缺陷
559
+ if (issue.tracker?.id !== types_1.IssueTrackerId.BUG) {
560
+ throw new Error('选择的工作项不是缺陷类型');
561
+ }
562
+ // 验证工作项状态是否为可处理状态
563
+ // 可处理状态:17(新问题)、15(重新打开)、1(新需求)
564
+ if (![types_1.IssueStatusId.NEW_ISSUE, types_1.IssueStatusId.REOPENED, types_1.IssueStatusId.NEW_REQUIREMENT].includes(issue.status?.id)) {
565
+ throw new Error('缺陷工作项状态不可处理');
566
+ }
567
+ // 构建更新请求体
568
+ const newCustomFields = [];
569
+ if (defectAnalysis) {
570
+ newCustomFields.push({
571
+ custom_field: 'custom_field32',
572
+ field_name: '缺陷技术分析',
573
+ value: defectAnalysis,
574
+ });
575
+ }
576
+ if (problemReason) {
577
+ newCustomFields.push({
578
+ custom_field: 'custom_field39',
579
+ field_name: '问题原因',
580
+ value: problemReason,
581
+ });
582
+ }
583
+ if (impactScope) {
584
+ newCustomFields.push({
585
+ custom_field: 'custom_field40',
586
+ field_name: '影响范围',
587
+ value: impactScope,
588
+ });
589
+ }
590
+ // 检查缺陷类型是否为客户反馈
591
+ const issueCustomFields = issue.new_custom_fields || [];
592
+ const issueDefectTypeField = issueCustomFields.find((field) => field?.custom_field === 'custom_field36' || field?.field_name === '缺陷类型');
593
+ const issueDefectType = issueDefectTypeField?.value || '';
594
+ const isCustomerFeedback = issueDefectType === '客户反馈';
595
+ if (isCustomerFeedback) {
596
+ if (introductionStage) {
597
+ newCustomFields.push({
598
+ custom_field: 'custom_field29',
599
+ field_name: '引入阶段',
600
+ value: introductionStage,
601
+ });
602
+ }
603
+ if (releaseDate) {
604
+ // 将日期字符串转换为时间戳
605
+ const releaseTimestamp = this.parseDateToTimestamp(releaseDate);
606
+ if (releaseTimestamp !== null) {
607
+ newCustomFields.push({
608
+ custom_field: 'custom_field18',
609
+ field_name: '发布日期',
610
+ value: String(releaseTimestamp),
611
+ });
612
+ }
613
+ }
614
+ }
615
+ // 只有在有自定义字段时才发送更新请求
616
+ if (newCustomFields.length > 0) {
617
+ const updateData = {
618
+ status_id: types_1.IssueStatusId.RESOLVED, // 设置状态为已解决
619
+ new_custom_fields: newCustomFields,
620
+ };
621
+ // 如果处理人不是创建人,则更新开发人员和处理人
622
+ if (issue.assigned_user?.id !== issue.creator?.id) {
623
+ Object.assign(updateData, {
624
+ developer_id: issue.assigned_user?.id, // 开发人员设置为当前处理人
625
+ assigned_id: issue.creator?.id, // 处理人设置为创建人
626
+ });
627
+ }
628
+ await this.apiService.updateIssue(projectId, String(issue.id), updateData);
629
+ }
630
+ }
631
+ /**
632
+ * 将日期字符串解析为时间戳
633
+ * @param dateStr 日期字符串,格式:YYYY-MM-DD
634
+ * @returns 毫秒时间戳,如果解析失败返回 null
635
+ */
636
+ parseDateToTimestamp(dateStr) {
637
+ try {
638
+ const date = new Date(dateStr);
639
+ if (isNaN(date.getTime())) {
640
+ return null;
641
+ }
642
+ return date.getTime();
643
+ }
644
+ catch {
645
+ return null;
646
+ }
647
+ }
454
648
  }
455
649
  exports.BusinessService = BusinessService;
@@ -265,10 +265,31 @@ export interface IssueStatus {
265
265
  id: number;
266
266
  name: string;
267
267
  }
268
+ export declare enum IssueStatusId {
269
+ NEW_REQUIREMENT = 1,// 新需求
270
+ IN_PROGRESS = 2,// 进行中
271
+ RESOLVED = 3,// 已解决
272
+ TESTING = 4,// 测试中
273
+ CLOSED = 5,// 已关闭
274
+ PRODUCT_DESIGN = 7,// 产品设计
275
+ REVIEW_READY = 8,// 可评审
276
+ DEV_POOL = 9,// 开发池
277
+ DEVELOPING = 10,// 开发中
278
+ TEST_READY = 11,// 可提测
279
+ REOPENED = 15,// 重新打开
280
+ NEW_ISSUE = 17
281
+ }
268
282
  export interface IssueTracker {
269
283
  id: number;
270
284
  name: string;
271
285
  }
286
+ export declare enum IssueTrackerId {
287
+ TASK = 2,// 任务
288
+ BUG = 3,// 缺陷
289
+ EPIC = 5,// Epic
290
+ FEATURE = 6,// Feature
291
+ STORY = 7
292
+ }
272
293
  export interface IssueItem {
273
294
  actual_work_hours: number;
274
295
  assigned_cc_user: IssueUser[];
@@ -331,6 +352,26 @@ export interface AddIssueNotesRequest {
331
352
  projectUUId: string;
332
353
  type?: string;
333
354
  }
355
+ export interface UpdateIssueRequest {
356
+ actual_work_hours?: number;
357
+ assigned_id?: number;
358
+ begin_time?: string;
359
+ description?: string;
360
+ developer_id?: number;
361
+ domain_id?: number;
362
+ done_ratio?: number;
363
+ end_time?: string;
364
+ expected_work_hours?: number;
365
+ iteration_id?: number;
366
+ module_id?: number;
367
+ name?: string;
368
+ parent_issue_id?: number;
369
+ priority_id?: number;
370
+ severity_id?: number;
371
+ status_id?: number;
372
+ tracker_id?: number;
373
+ new_custom_fields?: IssueNewCustomField[];
374
+ }
334
375
  export interface ListChildIssuesV4Response {
335
376
  issues: IssueItem[];
336
377
  total: number;
@@ -446,7 +487,32 @@ export interface ListChildIssuesV2Response {
446
487
  /**
447
488
  * 自定义字段类型枚举
448
489
  */
449
- export type CustomFieldType = 'textbox' | 'textarea' | 'checkbox' | 'radio' | 'select' | 'date' | 'number';
490
+ export type CustomFieldType = 'textbox' | 'textarea' | 'checkbox' | 'radio' | 'select' | 'date' | 'number' | 'text' | 'textArea' | 'user' | 'time_date';
491
+ /**
492
+ * 缺陷相关自定义字段ID枚举
493
+ */
494
+ export declare enum CustomFieldId {
495
+ FEEDBACK_PERSON = "custom_field20",// 反馈人
496
+ DEFECT_TECHNICAL_ANALYSIS = "custom_field32",// 缺陷技术分析
497
+ IMPACT_SCOPE = "custom_field40",// 影响范围
498
+ ENVIRONMENT = "custom_field30",// 环境
499
+ TERMINAL_TYPE = "custom_field24",// 终端类型
500
+ DEFECT_TYPE = "custom_field36",// 缺陷类型
501
+ PRODUCT_MODULE = "custom_field33",// 产品模块
502
+ CUSTOMER_FEEDBACK_NO = "custom_field22",// 客户反馈编号
503
+ TEST_CASE_COVERAGE = "custom_field34",// 测试用例覆盖
504
+ TEST_STAGE = "custom_field23",// 测试阶段
505
+ COMPANY_NAME = "custom_field17",// 企业名称
506
+ DEFECT_ROOT_CAUSE = "custom_field28",// 缺陷根源
507
+ PROBLEM_CAUSE_AND_SOLUTION = "custom_field39",// 问题原因及解决办法
508
+ RELEASE_TIME = "custom_field18",// 发布时间
509
+ INTRODUCTION_PHASE = "custom_field29",// 引入阶段
510
+ VERSION = "custom_field37",// 版本
511
+ TESTER = "custom_field26",// 测试人员
512
+ DEVELOPMENT_END = "custom_field16",// 开发端
513
+ AI_RELATED = "custom_field25",// AI相关
514
+ BRIEFING_TIME = "custom_field38"
515
+ }
450
516
  /**
451
517
  * 缺陷技术分析选项枚举
452
518
  * custom_field32(缺陷技术分析)字段的选项值枚举定义
@@ -568,3 +634,43 @@ export interface ConsoleTotal<T> {
568
634
  totalMap: [string, string | (() => string)][];
569
635
  list: T[];
570
636
  }
637
+ export interface AddIssueNotesResponse {
638
+ result: AddIssueNotesResult;
639
+ status: string;
640
+ }
641
+ export interface AddIssueNotesResult {
642
+ issue: unknown;
643
+ }
644
+ export interface CurrentUserInfo {
645
+ id: number;
646
+ name: string;
647
+ nick_name: string;
648
+ user_id: string;
649
+ user_num_id: number;
650
+ domain_id: string;
651
+ domain_name: string;
652
+ email?: string;
653
+ phone?: string;
654
+ status?: number;
655
+ }
656
+ export interface CustomFieldOption {
657
+ custom_field: string;
658
+ type: CustomFieldType;
659
+ name: string;
660
+ options: string;
661
+ tracker_ids: number[];
662
+ create_time: string;
663
+ }
664
+ export interface GetCustomFieldsRequest {
665
+ custom_fields: string[];
666
+ }
667
+ export interface GetCustomFieldsResponse {
668
+ datas: CustomFieldOption[];
669
+ }
670
+ export interface BugFixData {
671
+ defectAnalysis?: string;
672
+ problemReason?: string;
673
+ impactScope?: string;
674
+ introductionStage?: string;
675
+ releaseDate?: string;
676
+ }
@@ -1,6 +1,31 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ConfigKey = exports.DefectAnalysisType = exports.IterationStatus = void 0;
3
+ exports.ConfigKey = exports.DefectAnalysisType = exports.CustomFieldId = exports.IterationStatus = exports.IssueTrackerId = exports.IssueStatusId = void 0;
4
+ // 工作项状态ID枚举
5
+ var IssueStatusId;
6
+ (function (IssueStatusId) {
7
+ IssueStatusId[IssueStatusId["NEW_REQUIREMENT"] = 1] = "NEW_REQUIREMENT";
8
+ IssueStatusId[IssueStatusId["IN_PROGRESS"] = 2] = "IN_PROGRESS";
9
+ IssueStatusId[IssueStatusId["RESOLVED"] = 3] = "RESOLVED";
10
+ IssueStatusId[IssueStatusId["TESTING"] = 4] = "TESTING";
11
+ IssueStatusId[IssueStatusId["CLOSED"] = 5] = "CLOSED";
12
+ IssueStatusId[IssueStatusId["PRODUCT_DESIGN"] = 7] = "PRODUCT_DESIGN";
13
+ IssueStatusId[IssueStatusId["REVIEW_READY"] = 8] = "REVIEW_READY";
14
+ IssueStatusId[IssueStatusId["DEV_POOL"] = 9] = "DEV_POOL";
15
+ IssueStatusId[IssueStatusId["DEVELOPING"] = 10] = "DEVELOPING";
16
+ IssueStatusId[IssueStatusId["TEST_READY"] = 11] = "TEST_READY";
17
+ IssueStatusId[IssueStatusId["REOPENED"] = 15] = "REOPENED";
18
+ IssueStatusId[IssueStatusId["NEW_ISSUE"] = 17] = "NEW_ISSUE";
19
+ })(IssueStatusId || (exports.IssueStatusId = IssueStatusId = {}));
20
+ // 工作项类型ID枚举
21
+ var IssueTrackerId;
22
+ (function (IssueTrackerId) {
23
+ IssueTrackerId[IssueTrackerId["TASK"] = 2] = "TASK";
24
+ IssueTrackerId[IssueTrackerId["BUG"] = 3] = "BUG";
25
+ IssueTrackerId[IssueTrackerId["EPIC"] = 5] = "EPIC";
26
+ IssueTrackerId[IssueTrackerId["FEATURE"] = 6] = "FEATURE";
27
+ IssueTrackerId[IssueTrackerId["STORY"] = 7] = "STORY";
28
+ })(IssueTrackerId || (exports.IssueTrackerId = IssueTrackerId = {}));
4
29
  var IterationStatus;
5
30
  (function (IterationStatus) {
6
31
  IterationStatus["OPEN"] = "open";
@@ -8,6 +33,33 @@ var IterationStatus;
8
33
  IterationStatus["IN_PROGRESS"] = "1";
9
34
  IterationStatus["COMPLETED"] = "2";
10
35
  })(IterationStatus || (exports.IterationStatus = IterationStatus = {}));
36
+ /**
37
+ * 缺陷相关自定义字段ID枚举
38
+ */
39
+ var CustomFieldId;
40
+ (function (CustomFieldId) {
41
+ // Bug专用字段
42
+ CustomFieldId["FEEDBACK_PERSON"] = "custom_field20";
43
+ CustomFieldId["DEFECT_TECHNICAL_ANALYSIS"] = "custom_field32";
44
+ CustomFieldId["IMPACT_SCOPE"] = "custom_field40";
45
+ CustomFieldId["ENVIRONMENT"] = "custom_field30";
46
+ CustomFieldId["TERMINAL_TYPE"] = "custom_field24";
47
+ CustomFieldId["DEFECT_TYPE"] = "custom_field36";
48
+ CustomFieldId["PRODUCT_MODULE"] = "custom_field33";
49
+ CustomFieldId["CUSTOMER_FEEDBACK_NO"] = "custom_field22";
50
+ CustomFieldId["TEST_CASE_COVERAGE"] = "custom_field34";
51
+ CustomFieldId["TEST_STAGE"] = "custom_field23";
52
+ CustomFieldId["COMPANY_NAME"] = "custom_field17";
53
+ CustomFieldId["DEFECT_ROOT_CAUSE"] = "custom_field28";
54
+ CustomFieldId["PROBLEM_CAUSE_AND_SOLUTION"] = "custom_field39";
55
+ CustomFieldId["RELEASE_TIME"] = "custom_field18";
56
+ CustomFieldId["INTRODUCTION_PHASE"] = "custom_field29";
57
+ CustomFieldId["VERSION"] = "custom_field37";
58
+ CustomFieldId["TESTER"] = "custom_field26";
59
+ CustomFieldId["DEVELOPMENT_END"] = "custom_field16";
60
+ CustomFieldId["AI_RELATED"] = "custom_field25";
61
+ CustomFieldId["BRIEFING_TIME"] = "custom_field38";
62
+ })(CustomFieldId || (exports.CustomFieldId = CustomFieldId = {}));
11
63
  /**
12
64
  * 缺陷技术分析选项枚举
13
65
  * custom_field32(缺陷技术分析)字段的选项值枚举定义
@@ -1,23 +1,23 @@
1
1
  import { ConfigMap, HuaweiCloudConfig, OutputFormat } from '../types';
2
2
  /**
3
- * 获取全局配置文件路径
3
+ * 获取配置文件路径
4
4
  */
5
5
  export declare function getConfigPath(): string;
6
6
  /**
7
- * 检查全局配置文件是否存在
7
+ * 检查配置文件是否存在
8
8
  */
9
9
  export declare function configExists(): boolean;
10
10
  /**
11
- * 读取全局配置
11
+ * 读取配置
12
12
  */
13
13
  export declare function readConfig(): Partial<ConfigMap>;
14
14
  /**
15
- * 写入全局配置
15
+ * 写入配置
16
16
  * 支持动态配置项,自动按分组组织配置文件
17
17
  */
18
18
  export declare function writeConfig(config: Partial<ConfigMap>): void;
19
19
  /**
20
- * 删除全局配置
20
+ * 删除配置
21
21
  */
22
22
  export declare function deleteConfig(): void;
23
23
  export interface CliOptions {
@@ -32,7 +32,7 @@ export interface LoadedConfig {
32
32
  outputFormat: OutputFormat;
33
33
  }
34
34
  /**
35
- * 加载配置,优先级:命令行参数 > 全局配置
35
+ * 加载配置,优先级:命令行参数 > 配置文件
36
36
  * @param cliOptions 命令行选项
37
37
  * @returns 加载的配置
38
38
  */
@@ -45,7 +45,7 @@ const os = __importStar(require("os"));
45
45
  const path = __importStar(require("path"));
46
46
  const types_1 = require("../types");
47
47
  /**
48
- * 全局配置管理工具
48
+ * 配置管理工具
49
49
  * 配置文件存储在用户主目录下的 .hecom-codearts 目录
50
50
  */
51
51
  const CONFIG_DIR = path.join(os.homedir(), '.hecom-codearts');
@@ -59,19 +59,19 @@ function ensureConfigDir() {
59
59
  }
60
60
  }
61
61
  /**
62
- * 获取全局配置文件路径
62
+ * 获取配置文件路径
63
63
  */
64
64
  function getConfigPath() {
65
65
  return CONFIG_FILE;
66
66
  }
67
67
  /**
68
- * 检查全局配置文件是否存在
68
+ * 检查配置文件是否存在
69
69
  */
70
70
  function configExists() {
71
71
  return fs.existsSync(CONFIG_FILE);
72
72
  }
73
73
  /**
74
- * 读取全局配置
74
+ * 读取配置
75
75
  */
76
76
  function readConfig() {
77
77
  if (!configExists()) {
@@ -98,7 +98,7 @@ function readConfig() {
98
98
  }
99
99
  }
100
100
  catch (error) {
101
- console.error('读取全局配置文件失败:', error);
101
+ console.error('读取配置文件失败:', error);
102
102
  }
103
103
  return config;
104
104
  }
@@ -124,13 +124,13 @@ const CONFIG_GROUPS = [
124
124
  },
125
125
  ];
126
126
  /**
127
- * 写入全局配置
127
+ * 写入配置
128
128
  * 支持动态配置项,自动按分组组织配置文件
129
129
  */
130
130
  function writeConfig(config) {
131
131
  ensureConfigDir();
132
132
  // 构建配置文件头部
133
- let content = `# Hecom CodeArts 全局配置文件`;
133
+ let content = `# Hecom CodeArts 配置文件`;
134
134
  // 记录已写入的配置项
135
135
  const writtenKeys = new Set();
136
136
  // 按分组写入配置
@@ -155,11 +155,11 @@ function writeConfig(config) {
155
155
  fs.writeFileSync(CONFIG_FILE, content, 'utf-8');
156
156
  }
157
157
  catch (error) {
158
- throw new Error(`写入全局配置文件失败: ${error}`);
158
+ throw new Error(`写入配置文件失败: ${error}`);
159
159
  }
160
160
  }
161
161
  /**
162
- * 删除全局配置
162
+ * 删除配置
163
163
  */
164
164
  function deleteConfig() {
165
165
  if (configExists()) {
@@ -167,19 +167,19 @@ function deleteConfig() {
167
167
  fs.unlinkSync(CONFIG_FILE);
168
168
  }
169
169
  catch (error) {
170
- throw new Error(`删除全局配置文件失败: ${error}`);
170
+ throw new Error(`删除配置文件失败: ${error}`);
171
171
  }
172
172
  }
173
173
  }
174
- // 加载全局配置
174
+ // 加载配置
175
175
  const globalConfig = configExists() ? readConfig() : {};
176
176
  /**
177
- * 加载配置,优先级:命令行参数 > 全局配置
177
+ * 加载配置,优先级:命令行参数 > 配置文件
178
178
  * @param cliOptions 命令行选项
179
179
  * @returns 加载的配置
180
180
  */
181
181
  function loadConfig(cliOptions = {}) {
182
- // 命令行参数 > 全局配置
182
+ // 命令行参数 > 配置文件
183
183
  const projectId = globalConfig[types_1.ConfigKey.PROJECT_ID];
184
184
  const roleIdStr = cliOptions.role || globalConfig[types_1.ConfigKey.ROLE_ID];
185
185
  if (!projectId) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hecom/codearts",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "description": "华为云 CodeArts 统计分析工具",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",