@hecom/codearts 0.1.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/.env.example ADDED
@@ -0,0 +1,20 @@
1
+ # 华为云CodeArts API 环境变量配置
2
+ # 复制此文件为 .env 并填入真实的配置值
3
+
4
+ # 华为云IAM认证端点(根据区域调整)
5
+ HUAWEI_CLOUD_IAM_ENDPOINT=https://iam.cn-north-4.myhuaweicloud.com
6
+ HUAWEI_CLOUD_REGION=cn-north-4
7
+
8
+ # IAM用户凭证
9
+ HUAWEI_CLOUD_USERNAME=your-iam-username
10
+ HUAWEI_CLOUD_PASSWORD=your-iam-password
11
+ HUAWEI_CLOUD_DOMAIN=your-domain-name
12
+
13
+ # CodeArts API基础URL(可选,默认根据区域自动生成)
14
+ CODEARTS_BASE_URL=https://projectman-ext.cn-north-4.myhuaweicloud.cn
15
+
16
+ PROJECT_ID=your-project-id
17
+
18
+ # 角色ID(支持多个,逗号分隔)
19
+ ROLE_ID=your-role-id
20
+ # 示例:ROLE_ID=1,2,3
package/README.md ADDED
@@ -0,0 +1,228 @@
1
+ # @hecom/codearts
2
+
3
+ 基于华为云 CodeArts API 的工时统计分析工具。
4
+
5
+ ## 快速开始
6
+
7
+ ### 1. 初始化
8
+
9
+ ```bash
10
+ npx @hecom/codearts
11
+ ```
12
+
13
+ 自动运行交互式配置向导,根据提示输入必要的配置项。配置文件将保存在 `~/.hecom-codearts/config.env`,配置一次后全局可用。
14
+
15
+ ### 2. 使用
16
+
17
+ ```bash
18
+ # 查看帮助
19
+ npx @hecom/codearts --help
20
+
21
+ # 生成今日工时日报
22
+ npx @hecom/codearts daily
23
+
24
+ # 生成当年工时统计
25
+ npx @hecom/codearts work-hour
26
+
27
+ # 生成指定日期/年份的统计
28
+ npx @hecom/codearts daily 2026-01-15
29
+ npx @hecom/codearts work-hour 2025
30
+
31
+ # 使用命令行参数覆盖配置
32
+ npx @hecom/codearts daily 2026-01-15 --project-id abc123 --role-id 1,2
33
+ npx @hecom/codearts work-hour 2025 --role-id 1,2,3
34
+
35
+ # 更新配置
36
+ npx @hecom/codearts config
37
+ ```
38
+
39
+ ---
40
+
41
+ ## 本地开发与调试
42
+
43
+ ### 环境要求
44
+
45
+ - Node.js >= 16.0.0
46
+ - npm >= 7.0.0
47
+
48
+ ### 安装依赖
49
+
50
+ ```bash
51
+ npm install
52
+ ```
53
+
54
+ ### 本地链接 CLI 工具
55
+
56
+ ```bash
57
+ npm run build
58
+ npm link
59
+ codearts --help
60
+ ```
61
+
62
+ ### 开发调试
63
+
64
+ ```bash
65
+ # 编译 TypeScript
66
+ npm run build
67
+
68
+ # 运行测试
69
+ npm test
70
+ ```
71
+
72
+ ### 高级配置
73
+
74
+ **配置优先级**:命令行参数 > 当前目录 .env > 全局配置 > 默认值
75
+
76
+ #### 项目级配置
77
+
78
+ 在项目目录创建 `.env` 文件(优先级高于全局配置):
79
+
80
+ ```bash
81
+ cp .env.example .env
82
+ ```
83
+
84
+ 修改 `.env` 文件中的配置:
85
+
86
+ ```env
87
+ # 华为云IAM认证端点(根据区域调整)
88
+ HUAWEI_CLOUD_IAM_ENDPOINT=https://iam.cn-north-4.myhuaweicloud.com
89
+ HUAWEI_CLOUD_REGION=cn-north-4
90
+
91
+ # IAM用户凭证
92
+ HUAWEI_CLOUD_USERNAME=your-iam-username
93
+ HUAWEI_CLOUD_PASSWORD=your-iam-password
94
+ HUAWEI_CLOUD_DOMAIN=your-domain-name
95
+
96
+ # 项目配置
97
+ CODEARTS_BASE_URL=https://projectman-ext.cn-north-4.myhuaweicloud.cn
98
+ PROJECT_ID=your-project-id
99
+ ROLE_ID=1,2,3 # 支持逗号分隔的多个角色ID
100
+ ```
101
+
102
+ **适用场景**: 团队协作,不同项目使用不同配置
103
+
104
+ #### 环境变量说明
105
+
106
+ | 变量名 | 说明 | 必填 | 默认值 |
107
+ | --------------------------- | ---------------------------------- | ---- | ---------------------------------------------------- |
108
+ | `HUAWEI_CLOUD_IAM_ENDPOINT` | IAM 认证端点 | 否 | `https://iam.cn-north-4.myhuaweicloud.com` |
109
+ | `HUAWEI_CLOUD_REGION` | 华为云区域 | 否 | `cn-north-4` |
110
+ | `HUAWEI_CLOUD_USERNAME` | IAM 用户名 | 是 | - |
111
+ | `HUAWEI_CLOUD_PASSWORD` | IAM 密码 | 是 | - |
112
+ | `HUAWEI_CLOUD_DOMAIN` | 华为云账号名 | 是 | - |
113
+ | `CODEARTS_BASE_URL` | CodeArts API 地址 | 否 | `https://projectman-ext.cn-north-4.myhuaweicloud.cn` |
114
+ | `PROJECT_ID` | 项目 ID | 是 | - |
115
+ | `ROLE_ID` | 角色 ID(支持逗号分隔,如: 1,2,3) | 是 | - |
116
+ | `TARGET_DATE` | 目标日期(YYYY-MM-DD) | 否 | 当天日期 |
117
+
118
+ ---
119
+
120
+ ## 项目信息
121
+
122
+ ### 项目描述
123
+
124
+ 这是一个使用 TypeScript/Node.js 构建的工时统计分析项目,通过华为云 CodeArts API 获取 issue、人员、工时等数据,并生成日报和年度工时统计报表。
125
+
126
+ ### 技术栈
127
+
128
+ - **语言**: TypeScript
129
+ - **运行环境**: Node.js
130
+ - **HTTP客户端**: Axios
131
+ - **测试框架**: Jest
132
+
133
+ ### 项目结构
134
+
135
+ ```
136
+ @hecom/codearts/
137
+ ├── src/
138
+ │ ├── bin/ # CLI 入口
139
+ │ │ └── cli.ts # Commander.js CLI 定义
140
+ │ ├── commands/ # 命令实现
141
+ │ │ ├── config.command.ts # 配置命令逻辑
142
+ │ │ ├── daily.command.ts # 日报命令逻辑
143
+ │ │ ├── work-hour.command.ts# 工时统计命令逻辑
144
+ │ │ └── index.ts # 命令导出
145
+ │ ├── services/ # API服务类
146
+ │ │ ├── api.service.ts # 华为云基础 API 封装
147
+ │ │ └── business.service.ts # 业务场景 API 封装
148
+ │ ├── utils/ # 工具函数
149
+ │ │ ├── global-config.ts # 全局配置查找
150
+ │ │ └── config-loader.ts # 配置加载器(CLI参数 > 环境变量)
151
+ │ ├── config/ # 配置文件
152
+ │ │ └── holidays.ts # 节假日配置与工作日计算
153
+ │ ├── types/ # TypeScript 类型定义
154
+ │ │ └── index.ts # API 契约与数据结构定义
155
+ │ └── index.ts # 模块导出
156
+ ├── bin/
157
+ │ └── codearts # CLI 可执行文件
158
+ ├── __tests__/ # 测试文件
159
+ ├── dist/ # 编译输出目录
160
+ ├── .env # 环境变量配置
161
+ ├── .env.example # 环境变量配置模板
162
+ ├── tsconfig.json # TypeScript配置
163
+ ├── jest.config.js # Jest测试配置
164
+ └── package.json # 项目配置
165
+ ```
166
+
167
+ ### 核心功能
168
+
169
+ #### 日报统计 (daily)
170
+
171
+ 生成指定日期的工时日报,包括:
172
+
173
+ - 按人员统计当日工时
174
+ - 统计 Bug 修复工时(自动合并同一 Bug 的多人工时)
175
+ - 统计活跃的工作项数量
176
+ - 计算工时完成率(基于角色成员数)
177
+ - 生成可读的日报格式输出
178
+
179
+ #### 年度工时统计 (work-hour)
180
+
181
+ 生成指定年份的工时统计报表,包括:
182
+
183
+ - 计算年度应计工作日(自动排除法定节假日和周末)
184
+ - 统计实际工时与应计工时的完成率
185
+ - 按人员生成工时明细表
186
+ - 按领域类型(需求、缺陷、任务等)统计工时分布
187
+ - 支持多角色统计(按角色分组,显示小计和总计)
188
+ - 生成 Markdown 格式的统计表格
189
+
190
+ ### API服务层
191
+
192
+ #### ApiService (api.service.ts)
193
+
194
+ 华为云 CodeArts 基础 API 封装:
195
+
196
+ - IAM Token 认证管理(自动获取和缓存)
197
+ - 项目管理 API(项目列表、成员查询)
198
+ - 迭代管理 API(迭代列表、迭代详情)
199
+ - 工作项 API(Issue 列表、详情、创建、更新)
200
+ - 工时管理 API(工时查询、提交)
201
+ - 请求日志(可选的 curl 风格日志输出)
202
+ - 完善的错误处理
203
+
204
+ #### BusinessService (business.service.ts)
205
+
206
+ 面向业务场景的高级 API 封装:
207
+
208
+ - `getMembersByRoleId()`: 通过角色获取人员列表
209
+ - `getActiveIterationsOnDate()`: 查询指定日期的活跃迭代
210
+ - `getWorkloadByIterationsAndUsers()`: 批量查询工作量
211
+ - `getAllIssuesByIteration()`: 查询迭代内所有 Issue(自动分页)
212
+ - `getDailyWorkHourStats()`: 获取日报工时统计
213
+ - `getAllWorkHourStats()`: 获取年度工时统计
214
+ - `addIssueNote()`: 添加工作项备注
215
+
216
+ ### 节假日配置 (config/holidays.ts)
217
+
218
+ - 支持中国法定节假日配置
219
+ - 支持调休工作日配置
220
+ - 支持大小周制度
221
+ - 自动计算年度应计工作日
222
+ - 判断指定日期是否为工作日
223
+
224
+ ---
225
+
226
+ ## License
227
+
228
+ MIT
package/bin/codearts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ require('../dist/bin/cli.js');
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,137 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
36
+ var __importDefault = (this && this.__importDefault) || function (mod) {
37
+ return (mod && mod.__esModule) ? mod : { "default": mod };
38
+ };
39
+ Object.defineProperty(exports, "__esModule", { value: true });
40
+ const commander_1 = require("commander");
41
+ const fs = __importStar(require("fs"));
42
+ const path = __importStar(require("path"));
43
+ const dotenv_1 = __importDefault(require("dotenv"));
44
+ const daily_command_1 = require("../commands/daily.command");
45
+ const config_command_1 = require("../commands/config.command");
46
+ const work_hour_command_1 = require("../commands/work-hour.command");
47
+ const global_config_1 = require("../utils/global-config");
48
+ // 读取 package.json 中的版本号
49
+ const packageJsonPath = path.join(__dirname, '../../package.json');
50
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
51
+ const version = packageJson.version || '0.0.1';
52
+ const program = new commander_1.Command();
53
+ program.name('codearts').description('华为云 CodeArts API 工时统计分析工具').version(version);
54
+ // 全局选项(环境变量覆盖)
55
+ program
56
+ .option('--project-id <id>', '项目 ID')
57
+ .option('--role-id <ids>', '角色 ID(支持逗号分隔,如: 1,2,3)')
58
+ .option('--username <username>', 'IAM 用户名')
59
+ .option('--password <password>', 'IAM 密码(建议使用 .env 文件)')
60
+ .option('--domain <domain>', '华为云账号名')
61
+ .option('--region <region>', '华为云区域')
62
+ .option('--iam-endpoint <url>', 'IAM 认证端点')
63
+ .option('--codearts-url <url>', 'CodeArts API 地址');
64
+ // config 命令 - 交互式配置向导
65
+ program
66
+ .command('config')
67
+ .description('交互式配置向导\n\n引导用户创建或更新全局配置文件')
68
+ .action(async () => {
69
+ await (0, config_command_1.configCommand)();
70
+ });
71
+ // daily 命令
72
+ program
73
+ .command('daily [date]')
74
+ .description('生成日报统计\n\n示例:\n $ codearts daily\n $ codearts daily 2026-01-15\n $ codearts daily --project-id abc123 --role-id 1,2')
75
+ .action(async (date) => {
76
+ const opts = program.opts();
77
+ await (0, daily_command_1.dailyCommand)(date, opts);
78
+ });
79
+ // work-hour 命令
80
+ program
81
+ .command('work-hour [year]')
82
+ .description('生成年度工时统计\n\n示例:\n $ codearts work-hour\n $ codearts work-hour 2025\n $ codearts work-hour 2025 --role-id 1,2,3')
83
+ .action(async (year) => {
84
+ const opts = program.opts();
85
+ await (0, work_hour_command_1.workHourCommand)(year, opts);
86
+ });
87
+ // 添加帮助信息
88
+ program.addHelpText('after', `
89
+ 环境变量:
90
+ 命令行参数优先于环境变量。可通过 .env 文件配置以下变量:
91
+
92
+ HUAWEI_CLOUD_IAM_ENDPOINT IAM 认证端点
93
+ HUAWEI_CLOUD_REGION 华为云区域
94
+ HUAWEI_CLOUD_USERNAME IAM 用户名
95
+ HUAWEI_CLOUD_PASSWORD IAM 密码
96
+ HUAWEI_CLOUD_DOMAIN 华为云账号名
97
+ CODEARTS_BASE_URL CodeArts API 地址
98
+ PROJECT_ID 项目 ID
99
+ ROLE_ID 角色 ID(支持逗号分隔)
100
+
101
+ 配置优先级:
102
+ 命令行参数 > 环境变量 > 默认值
103
+
104
+ 快速开始:
105
+ 1. 运行配置向导: codearts config
106
+ 2. 生成日报: codearts daily
107
+ 3. 生成年度工时统计: codearts work-hour
108
+
109
+ 更多信息:
110
+ https://github.com/hecom-rn/hecom-codearts
111
+ `);
112
+ // 检查配置并自动执行 config 命令
113
+ async function checkConfigAndRun() {
114
+ const args = process.argv.slice(2);
115
+ // 如果没有参数(直接执行 codearts),检测配置
116
+ if (args.length === 0) {
117
+ // 加载当前目录 .env 文件
118
+ dotenv_1.default.config();
119
+ // 检查是否有配置(全局配置或环境变量)
120
+ const hasGlobalConfig = (0, global_config_1.globalConfigExists)();
121
+ const hasEnvConfig = process.env.PROJECT_ID && process.env.ROLE_ID;
122
+ if (!hasGlobalConfig && !hasEnvConfig) {
123
+ // 没有配置,自动执行 config 命令
124
+ console.log('未检测到配置文件,启动配置向导...\n');
125
+ await (0, config_command_1.configCommand)();
126
+ return;
127
+ }
128
+ // 有配置,显示帮助信息
129
+ program.help();
130
+ }
131
+ // 有参数,正常解析命令
132
+ program.parse();
133
+ }
134
+ checkConfigAndRun().catch((error) => {
135
+ console.error('执行失败:', error);
136
+ process.exit(1);
137
+ });
@@ -0,0 +1,5 @@
1
+ /**
2
+ * 交互式配置向导命令
3
+ * 引导用户创建或更新全局配置文件
4
+ */
5
+ export declare function configCommand(): Promise<void>;
@@ -0,0 +1,109 @@
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.configCommand = configCommand;
7
+ const inquirer_1 = __importDefault(require("inquirer"));
8
+ const global_config_1 = require("../utils/global-config");
9
+ /**
10
+ * 交互式配置向导命令
11
+ * 引导用户创建或更新全局配置文件
12
+ */
13
+ async function configCommand() {
14
+ console.log('\n欢迎使用 Hecom CodeArts 配置向导');
15
+ console.log('='.repeat(60));
16
+ console.log('此向导将帮助您配置华为云 CodeArts API 访问凭证。');
17
+ console.log(`配置将保存到: ${(0, global_config_1.getGlobalConfigPath)()}\n`);
18
+ const existingConfig = (0, global_config_1.globalConfigExists)() ? (0, global_config_1.readGlobalConfig)() : {};
19
+ if ((0, global_config_1.globalConfigExists)()) {
20
+ const { overwrite } = await inquirer_1.default.prompt([
21
+ {
22
+ type: 'confirm',
23
+ name: 'overwrite',
24
+ message: '检测到已存在全局配置,是否覆盖?',
25
+ default: false,
26
+ },
27
+ ]);
28
+ if (!overwrite) {
29
+ console.log('\n已取消配置。');
30
+ return;
31
+ }
32
+ }
33
+ const answers = await inquirer_1.default.prompt([
34
+ {
35
+ type: 'input',
36
+ name: 'iamEndpoint',
37
+ message: 'IAM 认证端点:',
38
+ default: existingConfig.HUAWEI_CLOUD_IAM_ENDPOINT || 'https://iam.cn-north-4.myhuaweicloud.com',
39
+ },
40
+ {
41
+ type: 'input',
42
+ name: 'region',
43
+ message: '华为云区域:',
44
+ default: existingConfig.HUAWEI_CLOUD_REGION || 'cn-north-4',
45
+ },
46
+ {
47
+ type: 'input',
48
+ name: 'username',
49
+ message: 'IAM 用户名:',
50
+ default: existingConfig.HUAWEI_CLOUD_USERNAME || '',
51
+ validate: (input) => (input.trim() ? true : 'IAM 用户名不能为空'),
52
+ },
53
+ {
54
+ type: 'password',
55
+ name: 'password',
56
+ message: 'IAM 密码:',
57
+ mask: '*',
58
+ default: existingConfig.HUAWEI_CLOUD_PASSWORD || '',
59
+ validate: (input) => (input.trim() ? true : 'IAM 密码不能为空'),
60
+ },
61
+ {
62
+ type: 'input',
63
+ name: 'domain',
64
+ message: '华为云账号名:',
65
+ default: existingConfig.HUAWEI_CLOUD_DOMAIN || '',
66
+ validate: (input) => (input.trim() ? true : '华为云账号名不能为空'),
67
+ },
68
+ {
69
+ type: 'input',
70
+ name: 'codeartsUrl',
71
+ message: 'CodeArts API 地址:',
72
+ default: existingConfig.CODEARTS_BASE_URL || 'https://projectman-ext.cn-north-4.myhuaweicloud.cn',
73
+ },
74
+ {
75
+ type: 'input',
76
+ name: 'projectId',
77
+ message: '项目 ID:',
78
+ default: existingConfig.PROJECT_ID || '',
79
+ validate: (input) => (input.trim() ? true : '项目 ID 不能为空'),
80
+ },
81
+ {
82
+ type: 'input',
83
+ name: 'roleId',
84
+ message: '角色 ID(支持逗号分隔,如: 1,2,3):',
85
+ default: existingConfig.ROLE_ID || '',
86
+ validate: (input) => {
87
+ if (!input.trim()) {
88
+ return '角色 ID 不能为空';
89
+ }
90
+ const ids = input.split(',').map((id) => id.trim());
91
+ const allValid = ids.every((id) => /^\d+$/.test(id));
92
+ return allValid ? true : '角色 ID 必须是数字或逗号分隔的数字列表';
93
+ },
94
+ },
95
+ ]);
96
+ try {
97
+ (0, global_config_1.writeGlobalConfig)(answers);
98
+ console.log('\n✅ 全局配置已成功保存');
99
+ console.log(`配置文件位置: ${(0, global_config_1.getGlobalConfigPath)()}`);
100
+ console.log('\n您现在可以在任何目录使用以下命令:');
101
+ console.log(' codearts daily # 生成日报');
102
+ console.log(' codearts work-hour # 生成年度工时统计');
103
+ console.log('\n提示:配置文件包含敏感信息,请妥善保管。');
104
+ }
105
+ catch (error) {
106
+ console.error('\n❌ 保存配置文件失败:', error);
107
+ process.exit(1);
108
+ }
109
+ }
@@ -0,0 +1,10 @@
1
+ import { BusinessService } from '../services/business.service';
2
+ import { CliOptions } from '../utils/config-loader';
3
+ /**
4
+ * 生成单个角色的日报
5
+ */
6
+ export declare function generateDailyReport(businessService: BusinessService, projectId: string, roleId: number, targetDate: string): Promise<void>;
7
+ /**
8
+ * daily 命令入口
9
+ */
10
+ export declare function dailyCommand(date?: string, cliOptions?: CliOptions): Promise<void>;