@hecom/codearts 0.2.0 → 0.2.2

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.
@@ -4,150 +4,208 @@ exports.workHourCommand = workHourCommand;
4
4
  const holidays_1 = require("../config/holidays");
5
5
  const business_service_1 = require("../services/business.service");
6
6
  const config_loader_1 = require("../utils/config-loader");
7
+ const csv_writer_1 = require("../utils/csv-writer");
8
+ const logger_1 = require("../utils/logger");
7
9
  /**
8
- * work-hour 命令入口
10
+ * 处理浮点数精度,保留2位小数
9
11
  */
10
- async function workHourCommand(year, cliOptions = {}) {
11
- try {
12
- const targetYear = year || new Date().getFullYear().toString();
13
- console.log(`开始获取 ${targetYear} 年工时统计...`);
14
- const { projectId, roleIds, config } = (0, config_loader_1.loadConfig)(cliOptions);
15
- const businessService = new business_service_1.BusinessService(config);
16
- // 收集所有角色的成员和工时数据
17
- const allMembers = [];
18
- const allUserStats = [];
19
- const allTypes = new Set();
20
- for (const roleId of roleIds) {
21
- const roleMembers = await businessService.getMembersByRoleId(projectId, roleId);
22
- if (roleMembers.length === 0) {
23
- console.log(`角色ID ${roleId} 未找到用户,跳过`);
24
- continue;
25
- }
26
- const roleName = roleMembers[0].role_name;
27
- allMembers.push(...roleMembers);
28
- const roleUserIds = roleMembers.map((member) => member.user_id);
29
- const stats = await businessService.getAllWorkHourStats(projectId, roleUserIds, `${targetYear}-01-01`, `${targetYear}-12-31`);
30
- // 为每个用户添加角色信息
31
- stats.userStats.forEach((userStat) => {
32
- allUserStats.push({
33
- ...userStat,
34
- roleName,
35
- roleId,
36
- });
37
- // 收集所有领域类型
38
- userStat.domainStats.forEach((domainStat) => {
39
- allTypes.add(domainStat.type);
40
- });
12
+ function roundToTwo(num) {
13
+ return Math.round(num * 100) / 100;
14
+ }
15
+ /**
16
+ * 查询年度工时数据
17
+ */
18
+ async function queryWorkHourReportData(businessService, projectId, roleIds, targetYear) {
19
+ const allMembers = [];
20
+ const allUserStats = [];
21
+ const allTypes = new Set();
22
+ for (const roleId of roleIds) {
23
+ const roleMembers = await businessService.getMembersByRoleId(projectId, roleId);
24
+ if (roleMembers.length === 0) {
25
+ logger_1.logger.warn(`角色ID ${roleId} 未找到用户,跳过`);
26
+ continue;
27
+ }
28
+ const roleName = roleMembers[0].role_name;
29
+ allMembers.push(...roleMembers);
30
+ const roleUserIds = roleMembers.map((member) => member.user_id);
31
+ const stats = await businessService.getAllWorkHourStats(projectId, roleUserIds, `${targetYear}-01-01`, `${targetYear}-12-31`);
32
+ stats.userStats.forEach((userStat) => {
33
+ allUserStats.push({
34
+ ...userStat,
35
+ roleName,
36
+ roleId,
37
+ });
38
+ userStat.domainStats.forEach((domainStat) => {
39
+ allTypes.add(domainStat.type);
41
40
  });
41
+ });
42
+ }
43
+ allUserStats.sort((a, b) => {
44
+ if (a.roleId !== b.roleId) {
45
+ return a.roleId - b.roleId;
42
46
  }
43
- if (allMembers.length === 0) {
44
- console.log('未找到任何角色的用户');
45
- return;
47
+ return a.userName.localeCompare(b.userName);
48
+ });
49
+ const expectedWorkdays = (0, holidays_1.calculateExpectedWorkdays)(targetYear);
50
+ const expectedHoursPerPerson = expectedWorkdays * 8;
51
+ const totalExpectedHours = expectedHoursPerPerson * allMembers.length;
52
+ const totalHours = roundToTwo(allUserStats.reduce((sum, userStat) => sum + userStat.totalHours, 0));
53
+ const totalEntries = allUserStats.reduce((sum, userStat) => sum + userStat.domainStats.reduce((s, d) => s + d.workHours.length, 0), 0);
54
+ const userStatsData = [];
55
+ const roleSubtotals = [];
56
+ const typeTotals = {};
57
+ allTypes.forEach((type) => {
58
+ typeTotals[type] = 0;
59
+ });
60
+ let currentRoleId = null;
61
+ let currentRoleName = '';
62
+ const roleSubtotalStats = {};
63
+ allUserStats.forEach((userStat, index) => {
64
+ if (currentRoleId !== null && currentRoleId !== userStat.roleId) {
65
+ const subtotalDomainStats = {};
66
+ let subtotal = 0;
67
+ allTypes.forEach((type) => {
68
+ subtotalDomainStats[type] = roleSubtotalStats[type] || 0;
69
+ subtotal = roundToTwo(subtotal + (roleSubtotalStats[type] || 0));
70
+ });
71
+ roleSubtotals.push({
72
+ roleName: currentRoleName,
73
+ domainStats: subtotalDomainStats,
74
+ total: subtotal,
75
+ });
76
+ Object.keys(roleSubtotalStats).forEach((key) => {
77
+ roleSubtotalStats[key] = 0;
78
+ });
46
79
  }
47
- // 按角色ID排序用户
48
- allUserStats.sort((a, b) => {
49
- if (a.roleId !== b.roleId) {
50
- return a.roleId - b.roleId;
51
- }
52
- return a.userName.localeCompare(b.userName);
53
- });
54
- const expectedWorkdays = (0, holidays_1.calculateExpectedWorkdays)(targetYear);
55
- const expectedHoursPerPerson = expectedWorkdays * 8;
56
- const totalExpectedHours = expectedHoursPerPerson * allMembers.length;
57
- // 计算总工时
58
- const totalHours = allUserStats.reduce((sum, userStat) => sum + userStat.totalHours, 0);
59
- const totalEntries = allUserStats.reduce((sum, userStat) => sum + userStat.domainStats.reduce((s, d) => s + d.workHours.length, 0), 0);
60
- console.log(`\n${targetYear}年工时统计报告`);
61
- console.log('='.repeat(80));
62
- console.log(`统计期间: ${targetYear}-01-01 至 ${targetYear}-12-31`);
63
- console.log(`统计角色: ${roleIds.length} 个角色`);
64
- console.log(`统计人数: ${allMembers.length} 人`);
65
- console.log(`应计工作日: ${expectedWorkdays} 天`);
66
- console.log(`应计工时: ${totalExpectedHours} 小时 (${expectedHoursPerPerson} 小时/人)`);
67
- console.log(`实际工时: ${totalHours} 小时`);
68
- console.log(`工时完成率: ${((totalHours / totalExpectedHours) * 100).toFixed(2)}%`);
69
- console.log(`工时条目: ${totalEntries} 条`);
70
- console.log('='.repeat(80));
71
- // 构建表格数据:人作为行,type作为列
72
- const tableData = {};
73
- const typeTotals = {};
74
- // 初始化总计
80
+ currentRoleId = userStat.roleId;
81
+ currentRoleName = userStat.roleName;
82
+ const domainStats = {};
75
83
  allTypes.forEach((type) => {
76
- typeTotals[type] = 0;
84
+ domainStats[type] = 0;
77
85
  });
78
- // 按角色分组并填充数据
79
- let currentRoleId = null;
80
- let currentRoleName = '';
81
- const roleSubtotals = {};
82
- allUserStats.forEach((userStat, index) => {
83
- // 检测角色变化,插入小计行
84
- if (currentRoleId !== null && currentRoleId !== userStat.roleId) {
85
- const subtotalRow = {};
86
- subtotalRow['角色'] = `${currentRoleName} 小计`;
87
- let subtotal = 0;
88
- allTypes.forEach((type) => {
89
- subtotalRow[type] = roleSubtotals[type] || 0;
90
- subtotal += roleSubtotals[type] || 0;
91
- });
92
- subtotalRow['合计'] = subtotal;
93
- tableData[`─ ${currentRoleName} 小计`] = subtotalRow;
94
- // 重置小计
95
- Object.keys(roleSubtotals).forEach((key) => {
96
- roleSubtotals[key] = 0;
97
- });
86
+ let userTotal = 0;
87
+ userStat.domainStats.forEach((domainStat) => {
88
+ domainStats[domainStat.type] = domainStat.totalHours;
89
+ userTotal = roundToTwo(userTotal + domainStat.totalHours);
90
+ typeTotals[domainStat.type] = roundToTwo(typeTotals[domainStat.type] + domainStat.totalHours);
91
+ if (!roleSubtotalStats[domainStat.type]) {
92
+ roleSubtotalStats[domainStat.type] = 0;
98
93
  }
99
- // 更新当前角色
100
- currentRoleId = userStat.roleId;
101
- currentRoleName = userStat.roleName;
102
- // 填充用户数据
103
- const row = {};
104
- row['角色'] = userStat.roleName;
105
- // 初始化所有类型为0
94
+ roleSubtotalStats[domainStat.type] = roundToTwo(roleSubtotalStats[domainStat.type] + domainStat.totalHours);
95
+ });
96
+ userStatsData.push({
97
+ userName: userStat.userName,
98
+ roleName: userStat.roleName,
99
+ roleId: userStat.roleId,
100
+ domainStats,
101
+ total: userTotal,
102
+ });
103
+ if (index === allUserStats.length - 1) {
104
+ const subtotalDomainStats = {};
105
+ let subtotal = 0;
106
106
  allTypes.forEach((type) => {
107
- row[type] = 0;
107
+ subtotalDomainStats[type] = roleSubtotalStats[type] || 0;
108
+ subtotal = roundToTwo(subtotal + (roleSubtotalStats[type] || 0));
108
109
  });
109
- // 填充实际工时
110
- let userTotal = 0;
111
- userStat.domainStats.forEach((domainStat) => {
112
- row[domainStat.type] = domainStat.totalHours;
113
- userTotal += domainStat.totalHours;
114
- typeTotals[domainStat.type] += domainStat.totalHours;
115
- // 累加到角色小计
116
- if (!roleSubtotals[domainStat.type]) {
117
- roleSubtotals[domainStat.type] = 0;
118
- }
119
- roleSubtotals[domainStat.type] += domainStat.totalHours;
110
+ roleSubtotals.push({
111
+ roleName: currentRoleName,
112
+ domainStats: subtotalDomainStats,
113
+ total: subtotal,
120
114
  });
121
- row['合计'] = userTotal;
122
- tableData[userStat.userName] = row;
123
- // 如果是最后一个用户,添加最后一个角色的小计
124
- if (index === allUserStats.length - 1) {
125
- const subtotalRow = {};
126
- subtotalRow['角色'] = `${currentRoleName} 小计`;
127
- let subtotal = 0;
128
- allTypes.forEach((type) => {
129
- subtotalRow[type] = roleSubtotals[type] || 0;
130
- subtotal += roleSubtotals[type] || 0;
131
- });
132
- subtotalRow['合计'] = subtotal;
133
- tableData[`─ ${currentRoleName} 小计`] = subtotalRow;
134
- }
135
- });
136
- // 添加总计行
137
- const totalRow = {};
138
- totalRow['角色'] = '━ 总计';
139
- let grandTotal = 0;
140
- allTypes.forEach((type) => {
141
- totalRow[type] = typeTotals[type];
142
- grandTotal += typeTotals[type];
143
- });
144
- totalRow['合计'] = grandTotal;
145
- tableData['━━ 总计'] = totalRow;
146
- console.log('\n工时统计表:');
147
- console.table(tableData);
115
+ }
116
+ });
117
+ const grandTotalDomainStats = {};
118
+ let grandTotal = 0;
119
+ allTypes.forEach((type) => {
120
+ grandTotalDomainStats[type] = typeTotals[type];
121
+ grandTotal = roundToTwo(grandTotal + typeTotals[type]);
122
+ });
123
+ return {
124
+ year: targetYear,
125
+ roleCount: roleIds.length,
126
+ memberCount: allMembers.length,
127
+ expectedWorkdays,
128
+ expectedHours: totalExpectedHours,
129
+ actualHours: totalHours,
130
+ completionRate: (totalHours / totalExpectedHours) * 100,
131
+ entryCount: totalEntries,
132
+ userStats: userStatsData,
133
+ roleSubtotals,
134
+ grandTotal: {
135
+ domainStats: grandTotalDomainStats,
136
+ total: grandTotal,
137
+ },
138
+ };
139
+ }
140
+ /**
141
+ * 控制台输出工时统计
142
+ */
143
+ function outputConsole(data) {
144
+ logger_1.logger.info(`\n${data.year}年工时统计 [${data.roleSubtotals.map((r) => r.roleName).join(', ')}]`);
145
+ logger_1.logger.info('='.repeat(80));
146
+ logger_1.logger.info(`统计人数: ${data.memberCount} 人`);
147
+ logger_1.logger.info(`应计工作日: ${data.expectedWorkdays} 天`);
148
+ logger_1.logger.info(`应计工时: ${data.expectedHours} 小时`);
149
+ logger_1.logger.info(`实际工时: ${data.actualHours} 小时`);
150
+ logger_1.logger.info(`工时完成率: ${data.completionRate.toFixed(2)}%`);
151
+ logger_1.logger.info('='.repeat(80));
152
+ // 构建表格数据
153
+ const tableData = {};
154
+ data.userStats.forEach((userStat) => {
155
+ const row = {
156
+ ...userStat.domainStats,
157
+ 合计: userStat.total,
158
+ };
159
+ tableData[userStat.userName] = row;
160
+ });
161
+ // 添加总计行
162
+ tableData['总计'] = {
163
+ ...data.grandTotal.domainStats,
164
+ 合计: data.grandTotal.total,
165
+ };
166
+ logger_1.logger.table(tableData);
167
+ }
168
+ /**
169
+ * CSV 文件输出
170
+ */
171
+ function outputCsv(data, targetYear) {
172
+ const domains = Object.keys(data.grandTotal.domainStats);
173
+ const csvLines = [];
174
+ csvLines.push(`用户,角色,${domains.join(',')},合计`);
175
+ data.userStats.forEach((userStat) => {
176
+ const domainValues = domains.map((domain) => userStat.domainStats[domain] || 0);
177
+ csvLines.push(`${userStat.userName},${userStat.roleName},${domainValues.join(',')},${userStat.total}`);
178
+ });
179
+ const filename = `work-hour-${targetYear}.csv`;
180
+ (0, csv_writer_1.writeCsvFile)(filename, csvLines, logger_1.logger);
181
+ }
182
+ /**
183
+ * JSON 输出(直接打印到控制台,供编程调用)
184
+ */
185
+ function outputJson(data) {
186
+ logger_1.logger.json(data);
187
+ }
188
+ /**
189
+ * work-hour 命令入口
190
+ */
191
+ async function workHourCommand(year, cliOptions = {}) {
192
+ try {
193
+ const targetYear = year || new Date().getFullYear().toString();
194
+ const { projectId, roleIds, config, outputFormat } = (0, config_loader_1.loadConfig)(cliOptions);
195
+ const businessService = new business_service_1.BusinessService(config);
196
+ const reportData = await queryWorkHourReportData(businessService, projectId, roleIds, targetYear);
197
+ if (outputFormat === 'console') {
198
+ outputConsole(reportData);
199
+ }
200
+ else if (outputFormat === 'csv') {
201
+ outputCsv(reportData, targetYear);
202
+ }
203
+ else if (outputFormat === 'json') {
204
+ outputJson(reportData);
205
+ }
148
206
  }
149
207
  catch (error) {
150
- console.error('执行过程中发生错误:', error);
208
+ logger_1.logger.error(`执行过程中发生错误:`, error);
151
209
  process.exit(1);
152
210
  }
153
211
  }
@@ -1,4 +1,4 @@
1
- import { ApiResponse, CachedToken, HuaweiCloudConfig, ListChildIssuesV2Response, ListIssuesV4Request, ListIssuesV4Response, ListProjectIterationsV4Request, ListProjectIterationsV4Response, ProjectListResponse, ProjectMemberListResponse, ProjectMemberQueryParams, ProjectQueryParams, ShowProjectWorkHoursRequest, ShowProjectWorkHoursResponse } from '../types';
1
+ import { ApiResponse, CachedToken, HuaweiCloudConfig, ListChildIssuesV2Response, ListChildIssuesV4Response, ListIssuesV4Request, ListIssuesV4Response, ListProjectIterationsV4Request, ListProjectIterationsV4Response, ProjectListResponse, ProjectMemberListResponse, ProjectMemberQueryParams, ProjectQueryParams, ShowProjectWorkHoursRequest, ShowProjectWorkHoursResponse } from '../types';
2
2
  /**
3
3
  * 华为云CodeArts API服务类
4
4
  * 支持IAM Token认证和CodeArts API调用
@@ -58,8 +58,15 @@ export declare class ApiService {
58
58
  /**
59
59
  * 查询子工作项 (ListChildIssuesV4)
60
60
  * 获取指定工作项的所有子工作项
61
+ *
62
+ * FIXME: 华为云接口有问题,只能查询 15 条数据
61
63
  */
62
- getChildIssues(projectId: string, issueId: string, pageSize?: number, pageNo?: number): Promise<ApiResponse<ListChildIssuesV2Response>>;
64
+ getChildIssues(projectId: string, issueId: string): Promise<ApiResponse<ListChildIssuesV4Response>>;
65
+ /**
66
+ * 查询子工作项 (ListChildIssuesV2)
67
+ * 获取指定工作项的所有子工作项
68
+ */
69
+ getChildIssuesV2(projectId: string, issueId: string, pageSize?: number, pageNo?: number): Promise<ApiResponse<ListChildIssuesV2Response>>;
63
70
  /**
64
71
  * 创建工作项
65
72
  */
@@ -313,11 +313,19 @@ class ApiService {
313
313
  /**
314
314
  * 查询子工作项 (ListChildIssuesV4)
315
315
  * 获取指定工作项的所有子工作项
316
+ *
317
+ * FIXME: 华为云接口有问题,只能查询 15 条数据
316
318
  */
317
- async getChildIssues(projectId, issueId, pageSize = 100, pageNo = 1) {
318
- // return this.request(`/v4/projects/${projectId}/issues/${issueId}/child`, {
319
- // method: 'GET',
320
- // });
319
+ async getChildIssues(projectId, issueId) {
320
+ return this.request(`/v4/projects/${projectId}/issues/${issueId}/child`, {
321
+ method: 'GET',
322
+ });
323
+ }
324
+ /**
325
+ * 查询子工作项 (ListChildIssuesV2)
326
+ * 获取指定工作项的所有子工作项
327
+ */
328
+ async getChildIssuesV2(projectId, issueId, pageSize = 100, pageNo = 1) {
321
329
  return this.request('/v2/issues/child-issue-list', {
322
330
  method: 'POST',
323
331
  data: {
@@ -1,4 +1,4 @@
1
- import { AllWorkHourStats, HuaweiCloudConfig, IssueItem, IssueItemV2, IterationInfo, ProjectMember, WorkHourStats, WorkProgressStats } from '../types';
1
+ import { AllWorkHourStats, HuaweiCloudConfig, IssueItem, IssueItemV2, IterationInfo, ProjectMember, ProjectRole, WorkHourStats, WorkProgressStats } from '../types';
2
2
  import { ApiService } from './api.service';
3
3
  /**
4
4
  * 业务服务类
@@ -19,6 +19,12 @@ export declare class BusinessService {
19
19
  * @returns 指定角色的成员列表
20
20
  */
21
21
  getMembersByRoleId(projectId: string, roleId: number): Promise<ProjectMember[]>;
22
+ /**
23
+ * 获取项目中的所有角色列表(去重)
24
+ * @param projectId 项目ID
25
+ * @returns 项目中的所有角色列表
26
+ */
27
+ getProjectRoles(projectId: string): Promise<ProjectRole[]>;
22
28
  /**
23
29
  * 获取指定日期之后的迭代列表
24
30
  * @param projectId 项目ID
@@ -86,4 +92,18 @@ export declare class BusinessService {
86
92
  * @returns 所有子工作项列表
87
93
  */
88
94
  getChildIssues(projectId: string, issueId: string): Promise<IssueItemV2[]>;
95
+ /**
96
+ * 验证 IAM 凭证是否有效
97
+ * @returns 验证结果,包含成功状态和可能的错误信息
98
+ */
99
+ validateCredentials(): Promise<{
100
+ success: boolean;
101
+ error?: string;
102
+ }>;
103
+ /**
104
+ * 获取项目列表
105
+ * @param limit 返回的项目数量限制,默认100
106
+ * @returns 项目列表
107
+ */
108
+ getProjects(limit?: number): Promise<import('../types').Project[]>;
89
109
  }
@@ -32,6 +32,45 @@ class BusinessService {
32
32
  const allMembers = membersResponse.data?.members || [];
33
33
  return allMembers.filter((member) => member.role_id === roleId);
34
34
  }
35
+ /**
36
+ * 获取项目中的所有角色列表(去重)
37
+ * @param projectId 项目ID
38
+ * @returns 项目中的所有角色列表
39
+ */
40
+ async getProjectRoles(projectId) {
41
+ // 分页获取所有成员
42
+ const allMembers = [];
43
+ const pageSize = 100;
44
+ let offset = 0;
45
+ let hasMore = true;
46
+ while (hasMore) {
47
+ const membersResponse = await this.apiService.getMembers(projectId, {
48
+ limit: pageSize,
49
+ offset: offset,
50
+ });
51
+ if (!membersResponse.success) {
52
+ throw new Error(`获取成员列表失败: ${membersResponse.error || '未知错误'}`);
53
+ }
54
+ const members = membersResponse.data?.members.filter((member) => member.role_id !== -1 && member.nick_name != null) || [];
55
+ allMembers.push(...members);
56
+ // 判断是否还有更多数据
57
+ const total = membersResponse.data?.total || 0;
58
+ offset += pageSize;
59
+ hasMore = offset < total;
60
+ }
61
+ // 使用 Map 去重,key 为 role_id
62
+ const rolesMap = new Map();
63
+ allMembers.forEach((member) => {
64
+ if (!rolesMap.has(member.role_id)) {
65
+ rolesMap.set(member.role_id, {
66
+ role_id: member.role_id,
67
+ role_name: member.role_name,
68
+ });
69
+ }
70
+ });
71
+ // 转换为数组并按 role_id 排序
72
+ return Array.from(rolesMap.values()).sort((a, b) => a.role_id - b.role_id);
73
+ }
35
74
  /**
36
75
  * 获取指定日期之后的迭代列表
37
76
  * @param projectId 项目ID
@@ -383,7 +422,7 @@ class BusinessService {
383
422
  let pageNo = 1;
384
423
  let hasMore = true;
385
424
  while (hasMore) {
386
- const response = await this.apiService.getChildIssues(projectId, issueId, pageSize, pageNo);
425
+ const response = await this.apiService.getChildIssuesV2(projectId, issueId, pageSize, pageNo);
387
426
  if (!response.success || !response.data) {
388
427
  throw new Error(`获取子工作项失败: ${response.error || '未知错误'}`);
389
428
  }
@@ -395,5 +434,31 @@ class BusinessService {
395
434
  }
396
435
  return allChildIssues;
397
436
  }
437
+ /**
438
+ * 验证 IAM 凭证是否有效
439
+ * @returns 验证结果,包含成功状态和可能的错误信息
440
+ */
441
+ async validateCredentials() {
442
+ try {
443
+ await this.apiService.refreshToken();
444
+ return { success: true };
445
+ }
446
+ catch (error) {
447
+ const errorMsg = error instanceof Error ? error.message : String(error);
448
+ return { success: false, error: errorMsg };
449
+ }
450
+ }
451
+ /**
452
+ * 获取项目列表
453
+ * @param limit 返回的项目数量限制,默认100
454
+ * @returns 项目列表
455
+ */
456
+ async getProjects(limit = 100) {
457
+ const response = await this.apiService.getProjects({ limit });
458
+ if (!response.success || !response.data) {
459
+ throw new Error(response.error || '获取项目列表失败');
460
+ }
461
+ return response.data.projects;
462
+ }
398
463
  }
399
464
  exports.BusinessService = BusinessService;
@@ -152,6 +152,10 @@ export interface ProjectMemberQueryParams {
152
152
  offset?: number;
153
153
  limit?: number;
154
154
  }
155
+ export interface ProjectRole {
156
+ role_id: number;
157
+ role_name: string;
158
+ }
155
159
  export interface ShowProjectWorkHoursRequest {
156
160
  begin_time?: string;
157
161
  end_time?: string;
@@ -521,3 +525,46 @@ export interface AllWorkHourStats {
521
525
  totalEntries: number;
522
526
  userStats: UserAllWorkHourStats[];
523
527
  }
528
+ /**
529
+ * 配置键枚举
530
+ * 用于类型安全的配置项访问
531
+ */
532
+ export declare enum ConfigKey {
533
+ HUAWEI_CLOUD_IAM_ENDPOINT = "HUAWEI_CLOUD_IAM_ENDPOINT",
534
+ HUAWEI_CLOUD_REGION = "HUAWEI_CLOUD_REGION",
535
+ HUAWEI_CLOUD_USERNAME = "HUAWEI_CLOUD_USERNAME",
536
+ HUAWEI_CLOUD_PASSWORD = "HUAWEI_CLOUD_PASSWORD",
537
+ HUAWEI_CLOUD_DOMAIN = "HUAWEI_CLOUD_DOMAIN",
538
+ CODEARTS_BASE_URL = "CODEARTS_BASE_URL",
539
+ PROJECT_ID = "PROJECT_ID",
540
+ ROLE_ID = "ROLE_ID"
541
+ }
542
+ /**
543
+ * 不可变配置键(只能通过 config 命令设置)
544
+ */
545
+ export declare const IMMUTABLE_CONFIG_KEYS: ConfigKey[];
546
+ /**
547
+ * 可变配置键(可以通过命令行参数覆盖)
548
+ */
549
+ export declare const MUTABLE_CONFIG_KEYS: ConfigKey[];
550
+ /**
551
+ * 类型安全的配置映射
552
+ */
553
+ export type ConfigMap = {
554
+ [ConfigKey.HUAWEI_CLOUD_IAM_ENDPOINT]: string;
555
+ [ConfigKey.HUAWEI_CLOUD_REGION]: string;
556
+ [ConfigKey.HUAWEI_CLOUD_USERNAME]: string;
557
+ [ConfigKey.HUAWEI_CLOUD_PASSWORD]: string;
558
+ [ConfigKey.HUAWEI_CLOUD_DOMAIN]: string;
559
+ [ConfigKey.CODEARTS_BASE_URL]: string;
560
+ [ConfigKey.PROJECT_ID]: string;
561
+ [ConfigKey.ROLE_ID]: string;
562
+ };
563
+ /**
564
+ * 部分配置映射(用于读取配置文件)
565
+ */
566
+ export type PartialConfigMap = Partial<ConfigMap>;
567
+ /**
568
+ * 输出格式类型
569
+ */
570
+ export type OutputFormat = 'console' | 'csv' | 'json';
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DefectAnalysisType = void 0;
3
+ exports.MUTABLE_CONFIG_KEYS = exports.IMMUTABLE_CONFIG_KEYS = exports.ConfigKey = exports.DefectAnalysisType = void 0;
4
4
  /**
5
5
  * 缺陷技术分析选项枚举
6
6
  * custom_field32(缺陷技术分析)字段的选项值枚举定义
@@ -23,3 +23,36 @@ var DefectAnalysisType;
23
23
  DefectAnalysisType["USAGE_AND_CONFIG"] = "\u4F7F\u7528\u53CA\u914D\u7F6E\u95EE\u9898";
24
24
  DefectAnalysisType["OTHER"] = "\u5176\u4ED6\u95EE\u9898";
25
25
  })(DefectAnalysisType || (exports.DefectAnalysisType = DefectAnalysisType = {}));
26
+ /**
27
+ * 配置键枚举
28
+ * 用于类型安全的配置项访问
29
+ */
30
+ var ConfigKey;
31
+ (function (ConfigKey) {
32
+ // 不可变配置(只能通过 config 命令设置)
33
+ ConfigKey["HUAWEI_CLOUD_IAM_ENDPOINT"] = "HUAWEI_CLOUD_IAM_ENDPOINT";
34
+ ConfigKey["HUAWEI_CLOUD_REGION"] = "HUAWEI_CLOUD_REGION";
35
+ ConfigKey["HUAWEI_CLOUD_USERNAME"] = "HUAWEI_CLOUD_USERNAME";
36
+ ConfigKey["HUAWEI_CLOUD_PASSWORD"] = "HUAWEI_CLOUD_PASSWORD";
37
+ ConfigKey["HUAWEI_CLOUD_DOMAIN"] = "HUAWEI_CLOUD_DOMAIN";
38
+ ConfigKey["CODEARTS_BASE_URL"] = "CODEARTS_BASE_URL";
39
+ ConfigKey["PROJECT_ID"] = "PROJECT_ID";
40
+ // 可变配置(可以通过命令行参数覆盖)
41
+ ConfigKey["ROLE_ID"] = "ROLE_ID";
42
+ })(ConfigKey || (exports.ConfigKey = ConfigKey = {}));
43
+ /**
44
+ * 不可变配置键(只能通过 config 命令设置)
45
+ */
46
+ exports.IMMUTABLE_CONFIG_KEYS = [
47
+ ConfigKey.HUAWEI_CLOUD_IAM_ENDPOINT,
48
+ ConfigKey.HUAWEI_CLOUD_REGION,
49
+ ConfigKey.HUAWEI_CLOUD_USERNAME,
50
+ ConfigKey.HUAWEI_CLOUD_PASSWORD,
51
+ ConfigKey.HUAWEI_CLOUD_DOMAIN,
52
+ ConfigKey.CODEARTS_BASE_URL,
53
+ ConfigKey.PROJECT_ID,
54
+ ];
55
+ /**
56
+ * 可变配置键(可以通过命令行参数覆盖)
57
+ */
58
+ exports.MUTABLE_CONFIG_KEYS = [ConfigKey.ROLE_ID];
@@ -1,22 +1,44 @@
1
- import { HuaweiCloudConfig } from '../types';
1
+ import { HuaweiCloudConfig, OutputFormat, PartialConfigMap } from '../types';
2
+ /**
3
+ * 获取全局配置文件路径
4
+ */
5
+ export declare function getConfigPath(): string;
6
+ /**
7
+ * 检查全局配置文件是否存在
8
+ */
9
+ export declare function configExists(): boolean;
10
+ /**
11
+ * 读取全局配置
12
+ */
13
+ export declare function readConfig(): PartialConfigMap;
14
+ /**
15
+ * 写入全局配置
16
+ * 支持动态配置项,自动按分组组织配置文件
17
+ */
18
+ export declare function writeConfig(config: PartialConfigMap): void;
19
+ /**
20
+ * 删除全局配置
21
+ */
22
+ export declare function deleteConfig(): void;
2
23
  export interface CliOptions {
3
- projectId?: string;
4
24
  roleId?: string;
5
- username?: string;
6
- password?: string;
7
- domain?: string;
8
- region?: string;
9
- iamEndpoint?: string;
10
- codeartsUrl?: string;
25
+ output?: string;
26
+ report?: boolean;
11
27
  }
12
28
  export interface LoadedConfig {
13
29
  projectId: string;
14
30
  roleIds: number[];
15
31
  config: HuaweiCloudConfig;
32
+ outputFormat: OutputFormat;
16
33
  }
17
34
  /**
18
- * 加载配置,优先级:命令行参数 > 当前目录 .env > 全局配置 > 默认值
35
+ * 加载配置,优先级:命令行参数 > 全局配置
19
36
  * @param cliOptions 命令行选项
20
37
  * @returns 加载的配置
21
38
  */
22
39
  export declare function loadConfig(cliOptions?: CliOptions): LoadedConfig;
40
+ /**
41
+ * 获取最终合并后的配置(用于显示)
42
+ * @returns 合并后的配置映射
43
+ */
44
+ export declare function getConfig(): PartialConfigMap;