@hecom/codearts 0.2.1 → 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.
- package/README.md +17 -5
- package/dist/bin/cli.js +25 -18
- package/dist/commands/bug.command.d.ts +0 -2
- package/dist/commands/bug.command.js +104 -60
- package/dist/commands/config.command.js +33 -32
- package/dist/commands/daily.command.d.ts +0 -5
- package/dist/commands/daily.command.js +265 -99
- package/dist/commands/work-hour.command.js +189 -131
- package/dist/types/index.d.ts +4 -0
- package/dist/utils/config-loader.d.ts +9 -6
- package/dist/utils/config-loader.js +19 -14
- package/dist/utils/csv-writer.d.ts +12 -0
- package/dist/utils/csv-writer.js +60 -0
- package/dist/utils/logger.d.ts +62 -0
- package/dist/utils/logger.js +98 -0
- package/package.json +1 -1
|
@@ -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
|
-
*
|
|
10
|
+
* 处理浮点数精度,保留2位小数
|
|
9
11
|
*/
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
|
|
84
|
+
domainStats[type] = 0;
|
|
77
85
|
});
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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
|
-
|
|
107
|
+
subtotalDomainStats[type] = roleSubtotalStats[type] || 0;
|
|
108
|
+
subtotal = roundToTwo(subtotal + (roleSubtotalStats[type] || 0));
|
|
108
109
|
});
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
grandTotal
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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
|
-
|
|
208
|
+
logger_1.logger.error(`执行过程中发生错误:`, error);
|
|
151
209
|
process.exit(1);
|
|
152
210
|
}
|
|
153
211
|
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,32 +1,35 @@
|
|
|
1
|
-
import { HuaweiCloudConfig, PartialConfigMap } from '../types';
|
|
1
|
+
import { HuaweiCloudConfig, OutputFormat, PartialConfigMap } from '../types';
|
|
2
2
|
/**
|
|
3
3
|
* 获取全局配置文件路径
|
|
4
4
|
*/
|
|
5
|
-
export declare function
|
|
5
|
+
export declare function getConfigPath(): string;
|
|
6
6
|
/**
|
|
7
7
|
* 检查全局配置文件是否存在
|
|
8
8
|
*/
|
|
9
|
-
export declare function
|
|
9
|
+
export declare function configExists(): boolean;
|
|
10
10
|
/**
|
|
11
11
|
* 读取全局配置
|
|
12
12
|
*/
|
|
13
|
-
export declare function
|
|
13
|
+
export declare function readConfig(): PartialConfigMap;
|
|
14
14
|
/**
|
|
15
15
|
* 写入全局配置
|
|
16
16
|
* 支持动态配置项,自动按分组组织配置文件
|
|
17
17
|
*/
|
|
18
|
-
export declare function
|
|
18
|
+
export declare function writeConfig(config: PartialConfigMap): void;
|
|
19
19
|
/**
|
|
20
20
|
* 删除全局配置
|
|
21
21
|
*/
|
|
22
|
-
export declare function
|
|
22
|
+
export declare function deleteConfig(): void;
|
|
23
23
|
export interface CliOptions {
|
|
24
24
|
roleId?: string;
|
|
25
|
+
output?: string;
|
|
26
|
+
report?: boolean;
|
|
25
27
|
}
|
|
26
28
|
export interface LoadedConfig {
|
|
27
29
|
projectId: string;
|
|
28
30
|
roleIds: number[];
|
|
29
31
|
config: HuaweiCloudConfig;
|
|
32
|
+
outputFormat: OutputFormat;
|
|
30
33
|
}
|
|
31
34
|
/**
|
|
32
35
|
* 加载配置,优先级:命令行参数 > 全局配置
|
|
@@ -33,11 +33,11 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.
|
|
37
|
-
exports.
|
|
38
|
-
exports.
|
|
39
|
-
exports.
|
|
40
|
-
exports.
|
|
36
|
+
exports.getConfigPath = getConfigPath;
|
|
37
|
+
exports.configExists = configExists;
|
|
38
|
+
exports.readConfig = readConfig;
|
|
39
|
+
exports.writeConfig = writeConfig;
|
|
40
|
+
exports.deleteConfig = deleteConfig;
|
|
41
41
|
exports.loadConfig = loadConfig;
|
|
42
42
|
exports.getConfig = getConfig;
|
|
43
43
|
const fs = __importStar(require("fs"));
|
|
@@ -61,20 +61,20 @@ function ensureConfigDir() {
|
|
|
61
61
|
/**
|
|
62
62
|
* 获取全局配置文件路径
|
|
63
63
|
*/
|
|
64
|
-
function
|
|
64
|
+
function getConfigPath() {
|
|
65
65
|
return CONFIG_FILE;
|
|
66
66
|
}
|
|
67
67
|
/**
|
|
68
68
|
* 检查全局配置文件是否存在
|
|
69
69
|
*/
|
|
70
|
-
function
|
|
70
|
+
function configExists() {
|
|
71
71
|
return fs.existsSync(CONFIG_FILE);
|
|
72
72
|
}
|
|
73
73
|
/**
|
|
74
74
|
* 读取全局配置
|
|
75
75
|
*/
|
|
76
|
-
function
|
|
77
|
-
if (!
|
|
76
|
+
function readConfig() {
|
|
77
|
+
if (!configExists()) {
|
|
78
78
|
return {};
|
|
79
79
|
}
|
|
80
80
|
const config = {};
|
|
@@ -127,7 +127,7 @@ const CONFIG_GROUPS = [
|
|
|
127
127
|
* 写入全局配置
|
|
128
128
|
* 支持动态配置项,自动按分组组织配置文件
|
|
129
129
|
*/
|
|
130
|
-
function
|
|
130
|
+
function writeConfig(config) {
|
|
131
131
|
ensureConfigDir();
|
|
132
132
|
// 构建配置文件头部
|
|
133
133
|
let content = `# Hecom CodeArts 全局配置文件`;
|
|
@@ -161,8 +161,8 @@ function writeGlobalConfig(config) {
|
|
|
161
161
|
/**
|
|
162
162
|
* 删除全局配置
|
|
163
163
|
*/
|
|
164
|
-
function
|
|
165
|
-
if (
|
|
164
|
+
function deleteConfig() {
|
|
165
|
+
if (configExists()) {
|
|
166
166
|
try {
|
|
167
167
|
fs.unlinkSync(CONFIG_FILE);
|
|
168
168
|
}
|
|
@@ -172,7 +172,7 @@ function deleteGlobalConfig() {
|
|
|
172
172
|
}
|
|
173
173
|
}
|
|
174
174
|
// 加载全局配置
|
|
175
|
-
const globalConfig =
|
|
175
|
+
const globalConfig = configExists() ? readConfig() : {};
|
|
176
176
|
/**
|
|
177
177
|
* 加载配置,优先级:命令行参数 > 全局配置
|
|
178
178
|
* @param cliOptions 命令行选项
|
|
@@ -201,6 +201,11 @@ function loadConfig(cliOptions = {}) {
|
|
|
201
201
|
if (!username || !password || !domain || !iamEndpoint || !region || !endpoint) {
|
|
202
202
|
throw new Error('缺少华为云认证信息,请先运行 `npx @hecom/codearts config` 创建配置');
|
|
203
203
|
}
|
|
204
|
+
// 处理输出格式
|
|
205
|
+
const outputFormat = (cliOptions.output || 'console');
|
|
206
|
+
if (!['console', 'csv', 'json'].includes(outputFormat)) {
|
|
207
|
+
throw new Error('输出格式必须是 console、csv 或 json 之一');
|
|
208
|
+
}
|
|
204
209
|
const config = {
|
|
205
210
|
iamEndpoint,
|
|
206
211
|
region,
|
|
@@ -209,7 +214,7 @@ function loadConfig(cliOptions = {}) {
|
|
|
209
214
|
password,
|
|
210
215
|
domainName: domain,
|
|
211
216
|
};
|
|
212
|
-
return { projectId, roleIds, config };
|
|
217
|
+
return { projectId, roleIds, config, outputFormat };
|
|
213
218
|
}
|
|
214
219
|
/**
|
|
215
220
|
* 获取最终合并后的配置(用于显示)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Logger } from './logger';
|
|
2
|
+
/**
|
|
3
|
+
* CSV 转义函数:处理双引号
|
|
4
|
+
*/
|
|
5
|
+
export declare function escapeCsv(value: string): string;
|
|
6
|
+
/**
|
|
7
|
+
* 写入 CSV 文件到当前工作目录
|
|
8
|
+
* @param filename 文件名
|
|
9
|
+
* @param lines CSV 行数组
|
|
10
|
+
* @param logger 日志记录器(可选)
|
|
11
|
+
*/
|
|
12
|
+
export declare function writeCsvFile(filename: string, lines: string[], logger?: Logger): void;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.escapeCsv = escapeCsv;
|
|
37
|
+
exports.writeCsvFile = writeCsvFile;
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
const path = __importStar(require("path"));
|
|
40
|
+
/**
|
|
41
|
+
* CSV 转义函数:处理双引号
|
|
42
|
+
*/
|
|
43
|
+
function escapeCsv(value) {
|
|
44
|
+
if (!value)
|
|
45
|
+
return '';
|
|
46
|
+
return value.replace(/"/g, '""');
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* 写入 CSV 文件到当前工作目录
|
|
50
|
+
* @param filename 文件名
|
|
51
|
+
* @param lines CSV 行数组
|
|
52
|
+
* @param logger 日志记录器(可选)
|
|
53
|
+
*/
|
|
54
|
+
function writeCsvFile(filename, lines, logger) {
|
|
55
|
+
const filepath = path.join(process.cwd(), filename);
|
|
56
|
+
fs.writeFileSync(filepath, lines.join('\n'), 'utf-8');
|
|
57
|
+
if (logger) {
|
|
58
|
+
logger.success(`CSV 文件已生成: ${filepath}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export type OutputFormat = 'console' | 'csv' | 'json';
|
|
2
|
+
/**
|
|
3
|
+
* 日志工具类(单例模式)
|
|
4
|
+
* 根据输出格式自动控制日志输出:
|
|
5
|
+
* - console/csv: 正常输出所有日志
|
|
6
|
+
* - json: 静默所有用户日志,只输出纯 JSON
|
|
7
|
+
*/
|
|
8
|
+
export declare class Logger {
|
|
9
|
+
private static instance;
|
|
10
|
+
private silent;
|
|
11
|
+
private constructor();
|
|
12
|
+
/**
|
|
13
|
+
* 获取 Logger 单例实例
|
|
14
|
+
*/
|
|
15
|
+
static getInstance(): Logger;
|
|
16
|
+
/**
|
|
17
|
+
* 设置输出格式,更新静默状态
|
|
18
|
+
* @param outputFormat 输出格式
|
|
19
|
+
*/
|
|
20
|
+
setOutputFormat(outputFormat: string | null | undefined): void;
|
|
21
|
+
/**
|
|
22
|
+
* 信息日志(输出到 stdout)
|
|
23
|
+
* json 模式下会被静默
|
|
24
|
+
*/
|
|
25
|
+
info(message: string, ...optionalParams: any[]): void;
|
|
26
|
+
/**
|
|
27
|
+
* 警告日志(输出到 stdout)
|
|
28
|
+
* json 模式下会被静默
|
|
29
|
+
*/
|
|
30
|
+
warn(message: string, ...optionalParams: any[]): void;
|
|
31
|
+
/**
|
|
32
|
+
* 成功日志(输出到 stdout)
|
|
33
|
+
* json 模式下会被静默
|
|
34
|
+
*/
|
|
35
|
+
success(message: string): void;
|
|
36
|
+
/**
|
|
37
|
+
* 错误日志(输出到 stderr)
|
|
38
|
+
* 始终输出,不受 silent 影响
|
|
39
|
+
*/
|
|
40
|
+
error(message: string, ...optionalParams: any[]): void;
|
|
41
|
+
/**
|
|
42
|
+
* 调试日志(输出到 stdout)
|
|
43
|
+
* json 模式下会被静默
|
|
44
|
+
*/
|
|
45
|
+
debug(message: string): void;
|
|
46
|
+
/**
|
|
47
|
+
* JSON 数据输出(输出到 stdout)
|
|
48
|
+
* 输出格式化的 JSON 字符串
|
|
49
|
+
* @param data 要输出的数据对象
|
|
50
|
+
*/
|
|
51
|
+
json(data: any): void;
|
|
52
|
+
/**
|
|
53
|
+
* 表格输出(输出到 stdout)
|
|
54
|
+
* json 模式下会被静默
|
|
55
|
+
*/
|
|
56
|
+
table(data: any): void;
|
|
57
|
+
/**
|
|
58
|
+
* 检查是否处于静默模式
|
|
59
|
+
*/
|
|
60
|
+
isSilent(): boolean;
|
|
61
|
+
}
|
|
62
|
+
export declare const logger: Logger;
|