@hecom/codearts 0.3.2 → 0.4.1
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 +25 -4
- package/dist/charts/chart.interface.d.ts +5 -0
- package/dist/charts/chart.interface.js +2 -0
- package/dist/charts/index.d.ts +2 -0
- package/dist/charts/index.js +17 -0
- package/dist/charts/modules/__tests__/bug-by-developer-hours.test.d.ts +1 -0
- package/dist/charts/modules/__tests__/bug-by-developer-hours.test.js +54 -0
- package/dist/charts/modules/__tests__/bug-by-fix-duration.test.d.ts +1 -0
- package/dist/charts/modules/__tests__/bug-by-fix-duration.test.js +89 -0
- package/dist/charts/modules/__tests__/bug-open-priority-heatmap.test.d.ts +1 -0
- package/dist/charts/modules/__tests__/bug-open-priority-heatmap.test.js +61 -0
- package/dist/charts/modules/bug-by-assignee.d.ts +2 -0
- package/dist/charts/modules/bug-by-assignee.js +30 -0
- package/dist/charts/modules/bug-by-defect-analysis.d.ts +2 -0
- package/dist/charts/modules/bug-by-defect-analysis.js +30 -0
- package/dist/charts/modules/bug-by-developer-hours.d.ts +2 -0
- package/dist/charts/modules/bug-by-developer-hours.js +32 -0
- package/dist/charts/modules/bug-by-fix-duration.d.ts +2 -0
- package/dist/charts/modules/bug-by-fix-duration.js +46 -0
- package/dist/charts/modules/bug-by-module.d.ts +2 -0
- package/dist/charts/modules/bug-by-module.js +34 -0
- package/dist/charts/modules/bug-open-priority-heatmap.d.ts +2 -0
- package/dist/charts/modules/bug-open-priority-heatmap.js +74 -0
- package/dist/charts/renderer.d.ts +21 -0
- package/dist/charts/renderer.js +163 -0
- 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 +2 -0
- package/dist/commands/index.js +5 -1
- package/dist/commands/rebug.command.d.ts +5 -0
- package/dist/commands/rebug.command.js +113 -0
- package/dist/services/api.service.d.ts +10 -2
- package/dist/services/api.service.js +19 -0
- package/dist/services/business.service.d.ts +40 -1
- package/dist/services/business.service.js +226 -5
- package/dist/types/index.d.ts +107 -1
- package/dist/types/index.js +60 -1
- package/dist/utils/config-loader.d.ts +7 -6
- package/dist/utils/config-loader.js +13 -13
- package/package.json +5 -2
|
@@ -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的底层调用
|
|
@@ -102,4 +102,43 @@ export declare class BusinessService {
|
|
|
102
102
|
* @returns 项目列表
|
|
103
103
|
*/
|
|
104
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
|
+
* 根据迭代 ID 和终端类型查询有效 Bug(分页获取全量)
|
|
117
|
+
* @param projectId 项目ID
|
|
118
|
+
* @param iterationIds 迭代 ID 列表
|
|
119
|
+
* @param terminalTypes 终端类型列表(对应 custom_field24),为空则不过滤
|
|
120
|
+
* @returns Bug 类型工作项列表
|
|
121
|
+
*/
|
|
122
|
+
getBugsByIterationsAndTerminals(projectId: string, iterationIds: number[], terminalTypes: string[]): Promise<IssueItem[]>;
|
|
123
|
+
/**
|
|
124
|
+
* 批量获取自定义字段的选项
|
|
125
|
+
* @param projectId 项目ID
|
|
126
|
+
* @param customFieldIds 自定义字段ID列表
|
|
127
|
+
* @returns 自定义字段选项映射,key为字段ID,value为选项数组
|
|
128
|
+
*/
|
|
129
|
+
getCustomFieldOptions(projectId: string, customFieldIds: string[]): Promise<Record<string, string[]>>;
|
|
130
|
+
/**
|
|
131
|
+
* 修复缺陷工作项
|
|
132
|
+
* 根据填写的缺陷分析信息更新缺陷工作项,仅当问题为缺陷且状态为可处理状态时才更新
|
|
133
|
+
* @param projectId 项目ID
|
|
134
|
+
* @param issue 选中的缺陷工作项
|
|
135
|
+
* @param bugFixData 缺陷分析数据
|
|
136
|
+
*/
|
|
137
|
+
fixBug(projectId: string, issue: IssueItem, bugFixData: BugFixData): Promise<void>;
|
|
138
|
+
/**
|
|
139
|
+
* 将日期字符串解析为时间戳
|
|
140
|
+
* @param dateStr 日期字符串,格式:YYYY-MM-DD
|
|
141
|
+
* @returns 毫秒时间戳,如果解析失败返回 null
|
|
142
|
+
*/
|
|
143
|
+
private parseDateToTimestamp;
|
|
105
144
|
}
|
|
@@ -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,
|
|
@@ -168,8 +168,10 @@ class BusinessService {
|
|
|
168
168
|
};
|
|
169
169
|
}
|
|
170
170
|
stats[userId].count++;
|
|
171
|
-
//
|
|
172
|
-
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;
|
|
173
175
|
stats[userId].expectedHours += expectedHours;
|
|
174
176
|
stats[userId].actualHours += issue.actual_work_hours || 0;
|
|
175
177
|
// 累计总工时
|
|
@@ -279,7 +281,7 @@ class BusinessService {
|
|
|
279
281
|
const issuesResponse = await this.apiService.getIssues(projectId, {
|
|
280
282
|
iteration_ids: iterationIds,
|
|
281
283
|
assigned_ids: userIds,
|
|
282
|
-
tracker_ids: [
|
|
284
|
+
tracker_ids: [types_1.IssueTrackerId.STORY], // Story
|
|
283
285
|
include_deleted: false,
|
|
284
286
|
limit: pageSize,
|
|
285
287
|
offset: offset,
|
|
@@ -351,7 +353,7 @@ class BusinessService {
|
|
|
351
353
|
// Step 4: 为每个工时记录关联领域信息
|
|
352
354
|
const enrichedWorkHours = allWorkHours.map((workHour) => {
|
|
353
355
|
const issue = issueDetailsMap.get(workHour.issue_id);
|
|
354
|
-
const isBug = issue?.tracker?.id ===
|
|
356
|
+
const isBug = issue?.tracker?.id === types_1.IssueTrackerId.BUG;
|
|
355
357
|
const type = isBug ? issue?.tracker?.name || '' : issue?.domain?.name || '未分配领域';
|
|
356
358
|
return {
|
|
357
359
|
...workHour,
|
|
@@ -462,5 +464,224 @@ class BusinessService {
|
|
|
462
464
|
}
|
|
463
465
|
return response.data.projects;
|
|
464
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
|
+
* 根据迭代 ID 和终端类型查询有效 Bug(分页获取全量)
|
|
527
|
+
* @param projectId 项目ID
|
|
528
|
+
* @param iterationIds 迭代 ID 列表
|
|
529
|
+
* @param terminalTypes 终端类型列表(对应 custom_field24),为空则不过滤
|
|
530
|
+
* @returns Bug 类型工作项列表
|
|
531
|
+
*/
|
|
532
|
+
async getBugsByIterationsAndTerminals(projectId, iterationIds, terminalTypes) {
|
|
533
|
+
if (iterationIds.length === 0) {
|
|
534
|
+
return [];
|
|
535
|
+
}
|
|
536
|
+
const customFieldsFilter = terminalTypes.length > 0
|
|
537
|
+
? [{ custom_field: types_1.CustomFieldId.TERMINAL_TYPE, value: terminalTypes.join(',') }]
|
|
538
|
+
: [];
|
|
539
|
+
const allBugs = [];
|
|
540
|
+
const pageSize = 100;
|
|
541
|
+
let offset = 0;
|
|
542
|
+
let hasMore = true;
|
|
543
|
+
while (hasMore) {
|
|
544
|
+
const issuesResponse = await this.apiService.getIssues(projectId, {
|
|
545
|
+
tracker_ids: [types_1.IssueTrackerId.BUG],
|
|
546
|
+
iteration_ids: iterationIds,
|
|
547
|
+
custom_fields: customFieldsFilter.length > 0 ? customFieldsFilter : undefined,
|
|
548
|
+
include_deleted: false,
|
|
549
|
+
limit: pageSize,
|
|
550
|
+
offset,
|
|
551
|
+
});
|
|
552
|
+
if (!issuesResponse.success) {
|
|
553
|
+
throw new Error(`查询 Bug 失败: ${issuesResponse.error || '未知错误'}`);
|
|
554
|
+
}
|
|
555
|
+
const bugs = issuesResponse.data?.issues || [];
|
|
556
|
+
allBugs.push(...bugs);
|
|
557
|
+
const total = issuesResponse.data?.total || 0;
|
|
558
|
+
offset += pageSize;
|
|
559
|
+
hasMore = offset < total;
|
|
560
|
+
}
|
|
561
|
+
return allBugs.filter((bug) => bug.status?.id !== types_1.IssueStatusId.REJECTED);
|
|
562
|
+
}
|
|
563
|
+
/**
|
|
564
|
+
* 批量获取自定义字段的选项
|
|
565
|
+
* @param projectId 项目ID
|
|
566
|
+
* @param customFieldIds 自定义字段ID列表
|
|
567
|
+
* @returns 自定义字段选项映射,key为字段ID,value为选项数组
|
|
568
|
+
*/
|
|
569
|
+
async getCustomFieldOptions(projectId, customFieldIds) {
|
|
570
|
+
if (customFieldIds.length === 0) {
|
|
571
|
+
return {};
|
|
572
|
+
}
|
|
573
|
+
const response = await this.apiService.getCustomFields(projectId, customFieldIds);
|
|
574
|
+
if (!response.success || !response.data) {
|
|
575
|
+
throw new Error(`获取自定义字段信息失败: ${response.error || '未知错误'}`);
|
|
576
|
+
}
|
|
577
|
+
// 将自定义字段列表转换为 fieldId -> options 的映射
|
|
578
|
+
const optionsMap = {};
|
|
579
|
+
response.data.datas.forEach((field) => {
|
|
580
|
+
// 如果 options 为 null,返回空数组;否则将逗号分隔的字符串解析为数组
|
|
581
|
+
optionsMap[field.custom_field] = field.options
|
|
582
|
+
? field.options.split(',').map((option) => option.trim())
|
|
583
|
+
: [];
|
|
584
|
+
});
|
|
585
|
+
return optionsMap;
|
|
586
|
+
}
|
|
587
|
+
/**
|
|
588
|
+
* 修复缺陷工作项
|
|
589
|
+
* 根据填写的缺陷分析信息更新缺陷工作项,仅当问题为缺陷且状态为可处理状态时才更新
|
|
590
|
+
* @param projectId 项目ID
|
|
591
|
+
* @param issue 选中的缺陷工作项
|
|
592
|
+
* @param bugFixData 缺陷分析数据
|
|
593
|
+
*/
|
|
594
|
+
async fixBug(projectId, issue, bugFixData) {
|
|
595
|
+
const { defectAnalysis, problemReason, impactScope, introductionStage, releaseDate } = bugFixData;
|
|
596
|
+
// 验证工作项是否为缺陷
|
|
597
|
+
if (issue.tracker?.id !== types_1.IssueTrackerId.BUG) {
|
|
598
|
+
throw new Error('选择的工作项不是缺陷类型');
|
|
599
|
+
}
|
|
600
|
+
// 验证工作项状态是否为可处理状态
|
|
601
|
+
// 可处理状态:17(新问题)、15(重新打开)、1(新需求)
|
|
602
|
+
if (![types_1.IssueStatusId.NEW_ISSUE, types_1.IssueStatusId.REOPENED, types_1.IssueStatusId.NEW_REQUIREMENT].includes(issue.status?.id)) {
|
|
603
|
+
throw new Error('缺陷工作项状态不可处理');
|
|
604
|
+
}
|
|
605
|
+
// 构建更新请求体
|
|
606
|
+
const newCustomFields = [];
|
|
607
|
+
if (defectAnalysis) {
|
|
608
|
+
newCustomFields.push({
|
|
609
|
+
custom_field: 'custom_field32',
|
|
610
|
+
field_name: '缺陷技术分析',
|
|
611
|
+
value: defectAnalysis,
|
|
612
|
+
});
|
|
613
|
+
}
|
|
614
|
+
if (problemReason) {
|
|
615
|
+
newCustomFields.push({
|
|
616
|
+
custom_field: 'custom_field39',
|
|
617
|
+
field_name: '问题原因',
|
|
618
|
+
value: problemReason,
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
if (impactScope) {
|
|
622
|
+
newCustomFields.push({
|
|
623
|
+
custom_field: 'custom_field40',
|
|
624
|
+
field_name: '影响范围',
|
|
625
|
+
value: impactScope,
|
|
626
|
+
});
|
|
627
|
+
}
|
|
628
|
+
// 检查缺陷类型是否为客户反馈
|
|
629
|
+
const issueCustomFields = issue.new_custom_fields || [];
|
|
630
|
+
const issueDefectTypeField = issueCustomFields.find((field) => field?.custom_field === 'custom_field36' || field?.field_name === '缺陷类型');
|
|
631
|
+
const issueDefectType = issueDefectTypeField?.value || '';
|
|
632
|
+
const isCustomerFeedback = issueDefectType === '客户反馈';
|
|
633
|
+
if (isCustomerFeedback) {
|
|
634
|
+
if (introductionStage) {
|
|
635
|
+
newCustomFields.push({
|
|
636
|
+
custom_field: 'custom_field29',
|
|
637
|
+
field_name: '引入阶段',
|
|
638
|
+
value: introductionStage,
|
|
639
|
+
});
|
|
640
|
+
}
|
|
641
|
+
if (releaseDate) {
|
|
642
|
+
// 将日期字符串转换为时间戳
|
|
643
|
+
const releaseTimestamp = this.parseDateToTimestamp(releaseDate);
|
|
644
|
+
if (releaseTimestamp !== null) {
|
|
645
|
+
newCustomFields.push({
|
|
646
|
+
custom_field: 'custom_field18',
|
|
647
|
+
field_name: '发布日期',
|
|
648
|
+
value: String(releaseTimestamp),
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
// 只有在有自定义字段时才发送更新请求
|
|
654
|
+
if (newCustomFields.length > 0) {
|
|
655
|
+
const updateData = {
|
|
656
|
+
status_id: types_1.IssueStatusId.RESOLVED, // 设置状态为已解决
|
|
657
|
+
new_custom_fields: newCustomFields,
|
|
658
|
+
};
|
|
659
|
+
// 如果处理人不是创建人,则更新开发人员和处理人
|
|
660
|
+
if (issue.assigned_user?.id !== issue.creator?.id) {
|
|
661
|
+
Object.assign(updateData, {
|
|
662
|
+
developer_id: issue.assigned_user?.id, // 开发人员设置为当前处理人
|
|
663
|
+
assigned_id: issue.creator?.id, // 处理人设置为创建人
|
|
664
|
+
});
|
|
665
|
+
}
|
|
666
|
+
await this.apiService.updateIssue(projectId, String(issue.id), updateData);
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
/**
|
|
670
|
+
* 将日期字符串解析为时间戳
|
|
671
|
+
* @param dateStr 日期字符串,格式:YYYY-MM-DD
|
|
672
|
+
* @returns 毫秒时间戳,如果解析失败返回 null
|
|
673
|
+
*/
|
|
674
|
+
parseDateToTimestamp(dateStr) {
|
|
675
|
+
try {
|
|
676
|
+
const date = new Date(dateStr);
|
|
677
|
+
if (isNaN(date.getTime())) {
|
|
678
|
+
return null;
|
|
679
|
+
}
|
|
680
|
+
return date.getTime();
|
|
681
|
+
}
|
|
682
|
+
catch {
|
|
683
|
+
return null;
|
|
684
|
+
}
|
|
685
|
+
}
|
|
465
686
|
}
|
|
466
687
|
exports.BusinessService = BusinessService;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -265,10 +265,38 @@ 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
|
+
REJECTED = 6,// 已拒绝
|
|
275
|
+
PRODUCT_DESIGN = 7,// 产品设计
|
|
276
|
+
REVIEW_READY = 8,// 可评审
|
|
277
|
+
DEV_POOL = 9,// 开发池
|
|
278
|
+
DEVELOPING = 10,// 开发中
|
|
279
|
+
TEST_READY = 11,// 可提测
|
|
280
|
+
HANDOFF_EXPERIENCE = 12,// 转体验
|
|
281
|
+
ACCEPTED = 13,// 接受处理
|
|
282
|
+
VERIFIED = 14,// 已验证
|
|
283
|
+
REOPENED = 15,// 重新打开
|
|
284
|
+
POSTPONED = 16,// 延期
|
|
285
|
+
NEW_ISSUE = 17,// 新问题
|
|
286
|
+
CONVERTED_TO_REQUIREMENT = 18,// 转需求
|
|
287
|
+
PENDING_TEST = 19
|
|
288
|
+
}
|
|
268
289
|
export interface IssueTracker {
|
|
269
290
|
id: number;
|
|
270
291
|
name: string;
|
|
271
292
|
}
|
|
293
|
+
export declare enum IssueTrackerId {
|
|
294
|
+
TASK = 2,// 任务
|
|
295
|
+
BUG = 3,// 缺陷
|
|
296
|
+
EPIC = 5,// Epic
|
|
297
|
+
FEATURE = 6,// Feature
|
|
298
|
+
STORY = 7
|
|
299
|
+
}
|
|
272
300
|
export interface IssueItem {
|
|
273
301
|
actual_work_hours: number;
|
|
274
302
|
assigned_cc_user: IssueUser[];
|
|
@@ -331,6 +359,26 @@ export interface AddIssueNotesRequest {
|
|
|
331
359
|
projectUUId: string;
|
|
332
360
|
type?: string;
|
|
333
361
|
}
|
|
362
|
+
export interface UpdateIssueRequest {
|
|
363
|
+
actual_work_hours?: number;
|
|
364
|
+
assigned_id?: number;
|
|
365
|
+
begin_time?: string;
|
|
366
|
+
description?: string;
|
|
367
|
+
developer_id?: number;
|
|
368
|
+
domain_id?: number;
|
|
369
|
+
done_ratio?: number;
|
|
370
|
+
end_time?: string;
|
|
371
|
+
expected_work_hours?: number;
|
|
372
|
+
iteration_id?: number;
|
|
373
|
+
module_id?: number;
|
|
374
|
+
name?: string;
|
|
375
|
+
parent_issue_id?: number;
|
|
376
|
+
priority_id?: number;
|
|
377
|
+
severity_id?: number;
|
|
378
|
+
status_id?: number;
|
|
379
|
+
tracker_id?: number;
|
|
380
|
+
new_custom_fields?: IssueNewCustomField[];
|
|
381
|
+
}
|
|
334
382
|
export interface ListChildIssuesV4Response {
|
|
335
383
|
issues: IssueItem[];
|
|
336
384
|
total: number;
|
|
@@ -446,7 +494,32 @@ export interface ListChildIssuesV2Response {
|
|
|
446
494
|
/**
|
|
447
495
|
* 自定义字段类型枚举
|
|
448
496
|
*/
|
|
449
|
-
export type CustomFieldType = 'textbox' | 'textarea' | 'checkbox' | 'radio' | 'select' | 'date' | 'number';
|
|
497
|
+
export type CustomFieldType = 'textbox' | 'textarea' | 'checkbox' | 'radio' | 'select' | 'date' | 'number' | 'text' | 'textArea' | 'user' | 'time_date';
|
|
498
|
+
/**
|
|
499
|
+
* 缺陷相关自定义字段ID枚举
|
|
500
|
+
*/
|
|
501
|
+
export declare enum CustomFieldId {
|
|
502
|
+
FEEDBACK_PERSON = "custom_field20",// 反馈人
|
|
503
|
+
DEFECT_TECHNICAL_ANALYSIS = "custom_field32",// 缺陷技术分析
|
|
504
|
+
IMPACT_SCOPE = "custom_field40",// 影响范围
|
|
505
|
+
ENVIRONMENT = "custom_field30",// 环境
|
|
506
|
+
TERMINAL_TYPE = "custom_field24",// 终端类型
|
|
507
|
+
DEFECT_TYPE = "custom_field36",// 缺陷类型
|
|
508
|
+
PRODUCT_MODULE = "custom_field33",// 产品模块
|
|
509
|
+
CUSTOMER_FEEDBACK_NO = "custom_field22",// 客户反馈编号
|
|
510
|
+
TEST_CASE_COVERAGE = "custom_field34",// 测试用例覆盖
|
|
511
|
+
TEST_STAGE = "custom_field23",// 测试阶段
|
|
512
|
+
COMPANY_NAME = "custom_field17",// 企业名称
|
|
513
|
+
DEFECT_ROOT_CAUSE = "custom_field28",// 缺陷根源
|
|
514
|
+
PROBLEM_CAUSE_AND_SOLUTION = "custom_field39",// 问题原因及解决办法
|
|
515
|
+
RELEASE_TIME = "custom_field18",// 发布时间
|
|
516
|
+
INTRODUCTION_PHASE = "custom_field29",// 引入阶段
|
|
517
|
+
VERSION = "custom_field37",// 版本
|
|
518
|
+
TESTER = "custom_field26",// 测试人员
|
|
519
|
+
DEVELOPMENT_END = "custom_field16",// 开发端
|
|
520
|
+
AI_RELATED = "custom_field25",// AI相关
|
|
521
|
+
BRIEFING_TIME = "custom_field38"
|
|
522
|
+
}
|
|
450
523
|
/**
|
|
451
524
|
* 缺陷技术分析选项枚举
|
|
452
525
|
* custom_field32(缺陷技术分析)字段的选项值枚举定义
|
|
@@ -575,3 +648,36 @@ export interface AddIssueNotesResponse {
|
|
|
575
648
|
export interface AddIssueNotesResult {
|
|
576
649
|
issue: unknown;
|
|
577
650
|
}
|
|
651
|
+
export interface CurrentUserInfo {
|
|
652
|
+
id: number;
|
|
653
|
+
name: string;
|
|
654
|
+
nick_name: string;
|
|
655
|
+
user_id: string;
|
|
656
|
+
user_num_id: number;
|
|
657
|
+
domain_id: string;
|
|
658
|
+
domain_name: string;
|
|
659
|
+
email?: string;
|
|
660
|
+
phone?: string;
|
|
661
|
+
status?: number;
|
|
662
|
+
}
|
|
663
|
+
export interface CustomFieldOption {
|
|
664
|
+
custom_field: string;
|
|
665
|
+
type: CustomFieldType;
|
|
666
|
+
name: string;
|
|
667
|
+
options: string;
|
|
668
|
+
tracker_ids: number[];
|
|
669
|
+
create_time: string;
|
|
670
|
+
}
|
|
671
|
+
export interface GetCustomFieldsRequest {
|
|
672
|
+
custom_fields: string[];
|
|
673
|
+
}
|
|
674
|
+
export interface GetCustomFieldsResponse {
|
|
675
|
+
datas: CustomFieldOption[];
|
|
676
|
+
}
|
|
677
|
+
export interface BugFixData {
|
|
678
|
+
defectAnalysis?: string;
|
|
679
|
+
problemReason?: string;
|
|
680
|
+
impactScope?: string;
|
|
681
|
+
introductionStage?: string;
|
|
682
|
+
releaseDate?: string;
|
|
683
|
+
}
|
package/dist/types/index.js
CHANGED
|
@@ -1,6 +1,38 @@
|
|
|
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["REJECTED"] = 6] = "REJECTED";
|
|
13
|
+
IssueStatusId[IssueStatusId["PRODUCT_DESIGN"] = 7] = "PRODUCT_DESIGN";
|
|
14
|
+
IssueStatusId[IssueStatusId["REVIEW_READY"] = 8] = "REVIEW_READY";
|
|
15
|
+
IssueStatusId[IssueStatusId["DEV_POOL"] = 9] = "DEV_POOL";
|
|
16
|
+
IssueStatusId[IssueStatusId["DEVELOPING"] = 10] = "DEVELOPING";
|
|
17
|
+
IssueStatusId[IssueStatusId["TEST_READY"] = 11] = "TEST_READY";
|
|
18
|
+
IssueStatusId[IssueStatusId["HANDOFF_EXPERIENCE"] = 12] = "HANDOFF_EXPERIENCE";
|
|
19
|
+
IssueStatusId[IssueStatusId["ACCEPTED"] = 13] = "ACCEPTED";
|
|
20
|
+
IssueStatusId[IssueStatusId["VERIFIED"] = 14] = "VERIFIED";
|
|
21
|
+
IssueStatusId[IssueStatusId["REOPENED"] = 15] = "REOPENED";
|
|
22
|
+
IssueStatusId[IssueStatusId["POSTPONED"] = 16] = "POSTPONED";
|
|
23
|
+
IssueStatusId[IssueStatusId["NEW_ISSUE"] = 17] = "NEW_ISSUE";
|
|
24
|
+
IssueStatusId[IssueStatusId["CONVERTED_TO_REQUIREMENT"] = 18] = "CONVERTED_TO_REQUIREMENT";
|
|
25
|
+
IssueStatusId[IssueStatusId["PENDING_TEST"] = 19] = "PENDING_TEST";
|
|
26
|
+
})(IssueStatusId || (exports.IssueStatusId = IssueStatusId = {}));
|
|
27
|
+
// 工作项类型ID枚举
|
|
28
|
+
var IssueTrackerId;
|
|
29
|
+
(function (IssueTrackerId) {
|
|
30
|
+
IssueTrackerId[IssueTrackerId["TASK"] = 2] = "TASK";
|
|
31
|
+
IssueTrackerId[IssueTrackerId["BUG"] = 3] = "BUG";
|
|
32
|
+
IssueTrackerId[IssueTrackerId["EPIC"] = 5] = "EPIC";
|
|
33
|
+
IssueTrackerId[IssueTrackerId["FEATURE"] = 6] = "FEATURE";
|
|
34
|
+
IssueTrackerId[IssueTrackerId["STORY"] = 7] = "STORY";
|
|
35
|
+
})(IssueTrackerId || (exports.IssueTrackerId = IssueTrackerId = {}));
|
|
4
36
|
var IterationStatus;
|
|
5
37
|
(function (IterationStatus) {
|
|
6
38
|
IterationStatus["OPEN"] = "open";
|
|
@@ -8,6 +40,33 @@ var IterationStatus;
|
|
|
8
40
|
IterationStatus["IN_PROGRESS"] = "1";
|
|
9
41
|
IterationStatus["COMPLETED"] = "2";
|
|
10
42
|
})(IterationStatus || (exports.IterationStatus = IterationStatus = {}));
|
|
43
|
+
/**
|
|
44
|
+
* 缺陷相关自定义字段ID枚举
|
|
45
|
+
*/
|
|
46
|
+
var CustomFieldId;
|
|
47
|
+
(function (CustomFieldId) {
|
|
48
|
+
// Bug专用字段
|
|
49
|
+
CustomFieldId["FEEDBACK_PERSON"] = "custom_field20";
|
|
50
|
+
CustomFieldId["DEFECT_TECHNICAL_ANALYSIS"] = "custom_field32";
|
|
51
|
+
CustomFieldId["IMPACT_SCOPE"] = "custom_field40";
|
|
52
|
+
CustomFieldId["ENVIRONMENT"] = "custom_field30";
|
|
53
|
+
CustomFieldId["TERMINAL_TYPE"] = "custom_field24";
|
|
54
|
+
CustomFieldId["DEFECT_TYPE"] = "custom_field36";
|
|
55
|
+
CustomFieldId["PRODUCT_MODULE"] = "custom_field33";
|
|
56
|
+
CustomFieldId["CUSTOMER_FEEDBACK_NO"] = "custom_field22";
|
|
57
|
+
CustomFieldId["TEST_CASE_COVERAGE"] = "custom_field34";
|
|
58
|
+
CustomFieldId["TEST_STAGE"] = "custom_field23";
|
|
59
|
+
CustomFieldId["COMPANY_NAME"] = "custom_field17";
|
|
60
|
+
CustomFieldId["DEFECT_ROOT_CAUSE"] = "custom_field28";
|
|
61
|
+
CustomFieldId["PROBLEM_CAUSE_AND_SOLUTION"] = "custom_field39";
|
|
62
|
+
CustomFieldId["RELEASE_TIME"] = "custom_field18";
|
|
63
|
+
CustomFieldId["INTRODUCTION_PHASE"] = "custom_field29";
|
|
64
|
+
CustomFieldId["VERSION"] = "custom_field37";
|
|
65
|
+
CustomFieldId["TESTER"] = "custom_field26";
|
|
66
|
+
CustomFieldId["DEVELOPMENT_END"] = "custom_field16";
|
|
67
|
+
CustomFieldId["AI_RELATED"] = "custom_field25";
|
|
68
|
+
CustomFieldId["BRIEFING_TIME"] = "custom_field38";
|
|
69
|
+
})(CustomFieldId || (exports.CustomFieldId = CustomFieldId = {}));
|
|
11
70
|
/**
|
|
12
71
|
* 缺陷技术分析选项枚举
|
|
13
72
|
* custom_field32(缺陷技术分析)字段的选项值枚举定义
|
|
@@ -1,29 +1,30 @@
|
|
|
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 {
|
|
24
24
|
role?: string;
|
|
25
25
|
output?: string;
|
|
26
26
|
report?: boolean;
|
|
27
|
+
outputDir?: string;
|
|
27
28
|
}
|
|
28
29
|
export interface LoadedConfig {
|
|
29
30
|
projectId: string;
|
|
@@ -32,7 +33,7 @@ export interface LoadedConfig {
|
|
|
32
33
|
outputFormat: OutputFormat;
|
|
33
34
|
}
|
|
34
35
|
/**
|
|
35
|
-
* 加载配置,优先级:命令行参数 >
|
|
36
|
+
* 加载配置,优先级:命令行参数 > 配置文件
|
|
36
37
|
* @param cliOptions 命令行选项
|
|
37
38
|
* @returns 加载的配置
|
|
38
39
|
*/
|
|
@@ -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) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hecom/codearts",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "华为云 CodeArts 统计分析工具",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -17,7 +17,10 @@
|
|
|
17
17
|
"test": "jest --passWithNoTests",
|
|
18
18
|
"test:coverage": "jest --coverage --passWithNoTests",
|
|
19
19
|
"prepublishOnly": "npm run build",
|
|
20
|
-
"dev": "ts-node src/bin/cli.ts"
|
|
20
|
+
"dev": "ts-node src/bin/cli.ts",
|
|
21
|
+
"minor": "npm version minor -m \"Bump version to %s\"; git push --tags",
|
|
22
|
+
"patch": "npm version patch -m \"Bump version to %s\"; git push --tags",
|
|
23
|
+
"major": "npm version major -m \"Bump version to %s\"; git push --tags"
|
|
21
24
|
},
|
|
22
25
|
"keywords": [
|
|
23
26
|
"huawei-cloud",
|