@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 +1 -1
- package/dist/bin/cli.js +14 -4
- package/dist/commands/config.command.d.ts +1 -1
- package/dist/commands/config.command.js +6 -6
- package/dist/commands/fix.command.d.ts +6 -0
- package/dist/commands/fix.command.js +220 -0
- package/dist/commands/index.d.ts +1 -0
- package/dist/commands/index.js +3 -1
- package/dist/services/api.service.d.ts +14 -2
- package/dist/services/api.service.js +28 -0
- package/dist/services/business.service.d.ts +33 -1
- package/dist/services/business.service.js +199 -5
- package/dist/types/index.d.ts +107 -1
- package/dist/types/index.js +53 -1
- package/dist/utils/config-loader.d.ts +6 -6
- package/dist/utils/config-loader.js +13 -13
- package/package.json +1 -1
package/README.md
CHANGED
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 命令
|
|
@@ -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) =>
|
|
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❌
|
|
327
|
+
logger_1.logger.error('\n❌ 配置文件不存在,请先运行 `npx @hecom/codearts config` 创建配置。');
|
|
328
328
|
process.exit(1);
|
|
329
329
|
}
|
|
330
330
|
// 查找对应的配置项
|
|
@@ -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
|
+
}
|
package/dist/commands/index.d.ts
CHANGED
package/dist/commands/index.js
CHANGED
|
@@ -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:
|
|
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: [
|
|
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
|
-
//
|
|
161
|
-
const expectedHours = issue.status?.id ===
|
|
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: [
|
|
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 ===
|
|
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;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -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
|
+
}
|
package/dist/types/index.js
CHANGED
|
@@ -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('
|
|
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(
|
|
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(
|
|
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) {
|