@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 +20 -0
- package/README.md +228 -0
- package/bin/codearts +2 -0
- package/dist/bin/cli.d.ts +2 -0
- package/dist/bin/cli.js +137 -0
- package/dist/commands/config.command.d.ts +5 -0
- package/dist/commands/config.command.js +109 -0
- package/dist/commands/daily.command.d.ts +10 -0
- package/dist/commands/daily.command.js +184 -0
- package/dist/commands/index.d.ts +3 -0
- package/dist/commands/index.js +9 -0
- package/dist/commands/work-hour.command.d.ts +5 -0
- package/dist/commands/work-hour.command.js +153 -0
- package/dist/config/holidays.d.ts +60 -0
- package/dist/config/holidays.js +177 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +23 -0
- package/dist/services/api.service.d.ts +98 -0
- package/dist/services/api.service.js +405 -0
- package/dist/services/business.service.d.ts +69 -0
- package/dist/services/business.service.js +330 -0
- package/dist/types/index.d.ts +522 -0
- package/dist/types/index.js +2 -0
- package/dist/utils/config-loader.d.ts +22 -0
- package/dist/utils/config-loader.js +56 -0
- package/dist/utils/global-config.d.ts +24 -0
- package/dist/utils/global-config.js +153 -0
- package/package.json +70 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { AddIssueNotesRequest, AddIssueNotesResponse, ApiResponse, CachedToken, HuaweiCloudConfig, ListIssuesV4Request, ListIssuesV4Response, ListProjectIterationsV4Request, ListProjectIterationsV4Response, ProjectListResponse, ProjectMemberListResponse, ProjectMemberQueryParams, ProjectQueryParams, ShowProjectWorkHoursRequest, ShowProjectWorkHoursResponse } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* 华为云CodeArts API服务类
|
|
4
|
+
* 支持IAM Token认证和CodeArts API调用
|
|
5
|
+
*/
|
|
6
|
+
export declare class ApiService {
|
|
7
|
+
private client;
|
|
8
|
+
private iamClient;
|
|
9
|
+
private config;
|
|
10
|
+
private cachedToken;
|
|
11
|
+
private enableLogging;
|
|
12
|
+
constructor(config: HuaweiCloudConfig);
|
|
13
|
+
/**
|
|
14
|
+
* 打印curl风格的请求日志
|
|
15
|
+
*/
|
|
16
|
+
private logCurlRequest;
|
|
17
|
+
/**
|
|
18
|
+
* 设置请求和响应拦截器
|
|
19
|
+
*/
|
|
20
|
+
private setupInterceptors;
|
|
21
|
+
/**
|
|
22
|
+
* 获取IAM Token
|
|
23
|
+
*/
|
|
24
|
+
private getIamToken;
|
|
25
|
+
/**
|
|
26
|
+
* 检查Token是否有效(距离过期时间超过5分钟)
|
|
27
|
+
*/
|
|
28
|
+
private isTokenValid;
|
|
29
|
+
/**
|
|
30
|
+
* 获取有效的Token(自动处理缓存和刷新)
|
|
31
|
+
*/
|
|
32
|
+
private getValidToken;
|
|
33
|
+
/**
|
|
34
|
+
* 通用请求方法
|
|
35
|
+
*/
|
|
36
|
+
private request;
|
|
37
|
+
/**
|
|
38
|
+
* 设置CodeArts API的基础URL
|
|
39
|
+
*/
|
|
40
|
+
setCodeArtsBaseUrl(baseUrl: string): void;
|
|
41
|
+
/**
|
|
42
|
+
* 获取项目列表
|
|
43
|
+
*/
|
|
44
|
+
getProjects(params?: ProjectQueryParams): Promise<ApiResponse<ProjectListResponse>>;
|
|
45
|
+
/**
|
|
46
|
+
* 获取指定项目的详细信息
|
|
47
|
+
*/
|
|
48
|
+
getProjectById(projectId: string): Promise<ApiResponse<unknown>>;
|
|
49
|
+
/**
|
|
50
|
+
* 高级查询工作项 (ListIssuesV4)
|
|
51
|
+
* 根据筛选条件查询工作项
|
|
52
|
+
*/
|
|
53
|
+
getIssues(projectId: string, params?: ListIssuesV4Request): Promise<ApiResponse<ListIssuesV4Response>>;
|
|
54
|
+
/**
|
|
55
|
+
* 获取指定工作项的详细信息
|
|
56
|
+
*/
|
|
57
|
+
getIssueById(projectId: string, issueId: string): Promise<ApiResponse<unknown>>;
|
|
58
|
+
/**
|
|
59
|
+
* 创建工作项
|
|
60
|
+
*/
|
|
61
|
+
createIssue(projectId: string, issueData: unknown): Promise<ApiResponse<unknown>>;
|
|
62
|
+
/**
|
|
63
|
+
* 更新工作项
|
|
64
|
+
*/
|
|
65
|
+
updateIssue(projectId: string, issueId: string, issueData: unknown): Promise<ApiResponse<unknown>>;
|
|
66
|
+
/**
|
|
67
|
+
* 删除工作项
|
|
68
|
+
*/
|
|
69
|
+
deleteIssue(projectId: string, issueId: string): Promise<ApiResponse<unknown>>;
|
|
70
|
+
/**
|
|
71
|
+
* 获取项目的迭代列表
|
|
72
|
+
*/
|
|
73
|
+
getIterations(projectId: string, params?: ListProjectIterationsV4Request): Promise<ApiResponse<ListProjectIterationsV4Response>>;
|
|
74
|
+
/**
|
|
75
|
+
* 获取指定迭代的详细信息
|
|
76
|
+
*/
|
|
77
|
+
getIterationById(projectId: string, iterationId: string): Promise<ApiResponse<unknown>>;
|
|
78
|
+
/**
|
|
79
|
+
* 获取项目成员列表
|
|
80
|
+
*/
|
|
81
|
+
getMembers(projectId: string, params?: ProjectMemberQueryParams): Promise<ApiResponse<ProjectMemberListResponse>>;
|
|
82
|
+
/**
|
|
83
|
+
* 按用户查询工时(单项目)
|
|
84
|
+
*/
|
|
85
|
+
showProjectWorkHours(projectId: string, params?: ShowProjectWorkHoursRequest): Promise<ApiResponse<ShowProjectWorkHoursResponse>>;
|
|
86
|
+
/**
|
|
87
|
+
* 工作项添加评论
|
|
88
|
+
*/
|
|
89
|
+
addIssueNotes(params: AddIssueNotesRequest): Promise<ApiResponse<AddIssueNotesResponse>>;
|
|
90
|
+
/**
|
|
91
|
+
* 获取当前Token信息(用于调试)
|
|
92
|
+
*/
|
|
93
|
+
getTokenInfo(): CachedToken | null;
|
|
94
|
+
/**
|
|
95
|
+
* 手动刷新Token
|
|
96
|
+
*/
|
|
97
|
+
refreshToken(): Promise<string>;
|
|
98
|
+
}
|
|
@@ -0,0 +1,405 @@
|
|
|
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.ApiService = void 0;
|
|
7
|
+
const axios_1 = __importDefault(require("axios"));
|
|
8
|
+
/**
|
|
9
|
+
* 华为云CodeArts API服务类
|
|
10
|
+
* 支持IAM Token认证和CodeArts API调用
|
|
11
|
+
*/
|
|
12
|
+
class ApiService {
|
|
13
|
+
constructor(config) {
|
|
14
|
+
this.cachedToken = null;
|
|
15
|
+
this.config = {
|
|
16
|
+
...config,
|
|
17
|
+
};
|
|
18
|
+
this.enableLogging = config.enableLogging ?? false;
|
|
19
|
+
// 初始化IAM客户端(用于获取Token)
|
|
20
|
+
this.iamClient = axios_1.default.create({
|
|
21
|
+
baseURL: this.config.iamEndpoint,
|
|
22
|
+
headers: {
|
|
23
|
+
'Content-Type': 'application/json',
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
// 初始化主客户端(用于调用CodeArts API)
|
|
27
|
+
this.client = axios_1.default.create({
|
|
28
|
+
baseURL: this.config.endpoint,
|
|
29
|
+
headers: {
|
|
30
|
+
'Content-Type': 'application/json',
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
this.setupInterceptors();
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* 打印curl风格的请求日志
|
|
37
|
+
*/
|
|
38
|
+
logCurlRequest(config, clientType = 'CodeArts') {
|
|
39
|
+
if (!this.enableLogging) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const baseUrl = config.baseURL || '';
|
|
43
|
+
const url = config.url?.startsWith('http') ? config.url : `${baseUrl}${config.url}`;
|
|
44
|
+
const method = (config.method || 'GET').toUpperCase();
|
|
45
|
+
let curlCmd = `curl -X ${method}`;
|
|
46
|
+
// 添加请求头
|
|
47
|
+
if (config.headers && typeof config.headers === 'object') {
|
|
48
|
+
Object.entries(config.headers).forEach(([key, value]) => {
|
|
49
|
+
if (value && typeof value === 'string') {
|
|
50
|
+
// 对敏感信息进行脱敏处理
|
|
51
|
+
let headerValue = value;
|
|
52
|
+
if (key.toLowerCase().includes('token') || key.toLowerCase().includes('auth')) {
|
|
53
|
+
headerValue = value.length > 8 ? `${value.substring(0, 8)}...` : '***';
|
|
54
|
+
}
|
|
55
|
+
curlCmd += ` \\\n -H "${key}: ${headerValue}"`;
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
// 添加查询参数
|
|
60
|
+
let finalUrl = url;
|
|
61
|
+
if (config.params && Object.keys(config.params).length > 0) {
|
|
62
|
+
const searchParams = new URLSearchParams();
|
|
63
|
+
Object.entries(config.params).forEach(([key, value]) => {
|
|
64
|
+
if (value !== undefined && value !== null) {
|
|
65
|
+
searchParams.append(key, String(value));
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
const queryString = searchParams.toString();
|
|
69
|
+
if (queryString) {
|
|
70
|
+
const separator = url.includes('?') ? '&' : '?';
|
|
71
|
+
finalUrl = `${url}${separator}${queryString}`;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
curlCmd += ` \\\n "${finalUrl}"`;
|
|
75
|
+
// 添加请求体
|
|
76
|
+
if (config.data) {
|
|
77
|
+
let dataStr = '';
|
|
78
|
+
if (typeof config.data === 'string') {
|
|
79
|
+
dataStr = config.data;
|
|
80
|
+
}
|
|
81
|
+
else if (typeof config.data === 'object') {
|
|
82
|
+
dataStr = JSON.stringify(config.data, null, 2);
|
|
83
|
+
}
|
|
84
|
+
// 如果数据太长,进行截断显示
|
|
85
|
+
if (dataStr.length > 500) {
|
|
86
|
+
const truncated = dataStr.substring(0, 500);
|
|
87
|
+
curlCmd += ` \\\n -d '${truncated}...'`;
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
curlCmd += ` \\\n -d '${dataStr}'`;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const emoji = clientType === 'IAM' ? '🔐' : '🔄';
|
|
94
|
+
console.log(`\n${emoji} ${clientType}请求 [${method}]:`);
|
|
95
|
+
console.log(curlCmd);
|
|
96
|
+
console.log('');
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* 设置请求和响应拦截器
|
|
100
|
+
*/
|
|
101
|
+
setupInterceptors() {
|
|
102
|
+
// IAM客户端拦截器
|
|
103
|
+
this.iamClient.interceptors.request.use((config) => {
|
|
104
|
+
// 打印curl风格的IAM请求日志
|
|
105
|
+
this.logCurlRequest(config, 'IAM');
|
|
106
|
+
return config;
|
|
107
|
+
}, (error) => {
|
|
108
|
+
console.error('IAM请求错误:', error);
|
|
109
|
+
return Promise.reject(error);
|
|
110
|
+
});
|
|
111
|
+
this.iamClient.interceptors.response.use((response) => {
|
|
112
|
+
return response;
|
|
113
|
+
}, (error) => {
|
|
114
|
+
if (this.enableLogging) {
|
|
115
|
+
console.error('IAM响应错误:', error.response?.data || error.message);
|
|
116
|
+
}
|
|
117
|
+
return Promise.reject(error);
|
|
118
|
+
});
|
|
119
|
+
// 主客户端拦截器
|
|
120
|
+
this.client.interceptors.request.use(async (config) => {
|
|
121
|
+
// 自动添加Token到请求头
|
|
122
|
+
const token = await this.getValidToken();
|
|
123
|
+
if (token) {
|
|
124
|
+
config.headers['X-Auth-Token'] = token;
|
|
125
|
+
}
|
|
126
|
+
// 添加项目ID到请求头(如果有)
|
|
127
|
+
if (this.cachedToken?.projectId) {
|
|
128
|
+
config.headers['X-Project-Id'] = this.cachedToken.projectId;
|
|
129
|
+
}
|
|
130
|
+
// 打印curl风格的请求日志
|
|
131
|
+
this.logCurlRequest(config);
|
|
132
|
+
return config;
|
|
133
|
+
}, (error) => {
|
|
134
|
+
console.error('CodeArts请求错误:', error);
|
|
135
|
+
return Promise.reject(error);
|
|
136
|
+
});
|
|
137
|
+
this.client.interceptors.response.use((response) => {
|
|
138
|
+
return response;
|
|
139
|
+
}, async (error) => {
|
|
140
|
+
const originalRequest = error.config;
|
|
141
|
+
// 如果是401错误且没有重试过,尝试刷新Token
|
|
142
|
+
if (error.response?.status === 401 && !originalRequest._retry) {
|
|
143
|
+
originalRequest._retry = true;
|
|
144
|
+
try {
|
|
145
|
+
this.cachedToken = null; // 清除缓存的Token
|
|
146
|
+
const newToken = await this.getValidToken();
|
|
147
|
+
if (newToken) {
|
|
148
|
+
originalRequest.headers['X-Auth-Token'] = newToken;
|
|
149
|
+
return this.client(originalRequest);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
catch (refreshError) {
|
|
153
|
+
console.error('刷新Token失败:', refreshError);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
console.error('CodeArts响应错误:', error.response?.data || error.message);
|
|
157
|
+
return Promise.reject(error);
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* 获取IAM Token
|
|
162
|
+
*/
|
|
163
|
+
async getIamToken() {
|
|
164
|
+
const requestBody = {
|
|
165
|
+
auth: {
|
|
166
|
+
identity: {
|
|
167
|
+
methods: ['password'],
|
|
168
|
+
password: {
|
|
169
|
+
user: {
|
|
170
|
+
name: this.config.username,
|
|
171
|
+
password: this.config.password,
|
|
172
|
+
domain: {
|
|
173
|
+
name: this.config.domainName,
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
scope: {
|
|
179
|
+
project: {
|
|
180
|
+
name: this.config.region,
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
};
|
|
185
|
+
try {
|
|
186
|
+
const response = await this.iamClient.post('/v3/auth/tokens', requestBody);
|
|
187
|
+
const token = response.headers['x-subject-token'];
|
|
188
|
+
if (!token) {
|
|
189
|
+
throw new Error('未能从响应头获取到Token');
|
|
190
|
+
}
|
|
191
|
+
const tokenData = response.data.token;
|
|
192
|
+
const expiresAt = new Date(tokenData.expires_at);
|
|
193
|
+
const issuedAt = new Date(tokenData.issued_at);
|
|
194
|
+
return {
|
|
195
|
+
token,
|
|
196
|
+
expiresAt,
|
|
197
|
+
issuedAt,
|
|
198
|
+
projectId: tokenData.project?.id,
|
|
199
|
+
projectName: tokenData.project?.name,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
catch (error) {
|
|
203
|
+
if (axios_1.default.isAxiosError(error)) {
|
|
204
|
+
const errorMsg = error.response?.data?.error?.message || error.message;
|
|
205
|
+
throw new Error(`获取IAM Token失败: ${errorMsg}`);
|
|
206
|
+
}
|
|
207
|
+
throw new Error(`获取IAM Token失败: ${String(error)}`);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* 检查Token是否有效(距离过期时间超过5分钟)
|
|
212
|
+
*/
|
|
213
|
+
isTokenValid(token) {
|
|
214
|
+
const now = new Date();
|
|
215
|
+
const timeToExpire = token.expiresAt.getTime() - now.getTime();
|
|
216
|
+
const fiveMinutes = 5 * 60 * 1000; // 5分钟的毫秒数
|
|
217
|
+
return timeToExpire > fiveMinutes;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* 获取有效的Token(自动处理缓存和刷新)
|
|
221
|
+
*/
|
|
222
|
+
async getValidToken() {
|
|
223
|
+
if (this.cachedToken && this.isTokenValid(this.cachedToken)) {
|
|
224
|
+
return this.cachedToken.token;
|
|
225
|
+
}
|
|
226
|
+
this.cachedToken = await this.getIamToken();
|
|
227
|
+
return this.cachedToken.token;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* 通用请求方法
|
|
231
|
+
*/
|
|
232
|
+
async request(url, options = {}) {
|
|
233
|
+
try {
|
|
234
|
+
const config = {
|
|
235
|
+
url,
|
|
236
|
+
method: options.method || 'GET',
|
|
237
|
+
headers: options.headers,
|
|
238
|
+
params: options.params,
|
|
239
|
+
data: options.data,
|
|
240
|
+
};
|
|
241
|
+
const response = await this.client.request(config);
|
|
242
|
+
return {
|
|
243
|
+
success: true,
|
|
244
|
+
data: response.data,
|
|
245
|
+
message: 'Request successful',
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
catch (error) {
|
|
249
|
+
if (axios_1.default.isAxiosError(error)) {
|
|
250
|
+
return {
|
|
251
|
+
success: false,
|
|
252
|
+
data: null,
|
|
253
|
+
error: error.response?.data?.error_msg || error.response?.data?.message || error.message,
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
return {
|
|
257
|
+
success: false,
|
|
258
|
+
data: null,
|
|
259
|
+
error: String(error),
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* 设置CodeArts API的基础URL
|
|
265
|
+
*/
|
|
266
|
+
setCodeArtsBaseUrl(baseUrl) {
|
|
267
|
+
this.client.defaults.baseURL = baseUrl;
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* 获取项目列表
|
|
271
|
+
*/
|
|
272
|
+
async getProjects(params) {
|
|
273
|
+
return this.request('/v4/projects', {
|
|
274
|
+
method: 'GET',
|
|
275
|
+
params: {
|
|
276
|
+
offset: 0,
|
|
277
|
+
limit: 10,
|
|
278
|
+
...params,
|
|
279
|
+
},
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* 获取指定项目的详细信息
|
|
284
|
+
*/
|
|
285
|
+
async getProjectById(projectId) {
|
|
286
|
+
return this.request(`/v4/projects/${projectId}`, {
|
|
287
|
+
method: 'GET',
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* 高级查询工作项 (ListIssuesV4)
|
|
292
|
+
* 根据筛选条件查询工作项
|
|
293
|
+
*/
|
|
294
|
+
async getIssues(projectId, params) {
|
|
295
|
+
return this.request(`/v4/projects/${projectId}/issues`, {
|
|
296
|
+
method: 'POST',
|
|
297
|
+
data: {
|
|
298
|
+
offset: 0,
|
|
299
|
+
limit: 100,
|
|
300
|
+
query_type: 'backlog',
|
|
301
|
+
...params,
|
|
302
|
+
},
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* 获取指定工作项的详细信息
|
|
307
|
+
*/
|
|
308
|
+
async getIssueById(projectId, issueId) {
|
|
309
|
+
return this.request(`/v4/projects/${projectId}/issues/${issueId}`, {
|
|
310
|
+
method: 'GET',
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* 创建工作项
|
|
315
|
+
*/
|
|
316
|
+
async createIssue(projectId, issueData) {
|
|
317
|
+
return this.request(`/v4/projects/${projectId}/issues`, {
|
|
318
|
+
method: 'POST',
|
|
319
|
+
data: issueData,
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* 更新工作项
|
|
324
|
+
*/
|
|
325
|
+
async updateIssue(projectId, issueId, issueData) {
|
|
326
|
+
return this.request(`/v4/projects/${projectId}/issues/${issueId}`, {
|
|
327
|
+
method: 'PUT',
|
|
328
|
+
data: issueData,
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* 删除工作项
|
|
333
|
+
*/
|
|
334
|
+
async deleteIssue(projectId, issueId) {
|
|
335
|
+
return this.request(`/v4/projects/${projectId}/issues/${issueId}`, {
|
|
336
|
+
method: 'DELETE',
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* 获取项目的迭代列表
|
|
341
|
+
*/
|
|
342
|
+
async getIterations(projectId, params) {
|
|
343
|
+
return this.request(`/v4/projects/${projectId}/iterations`, {
|
|
344
|
+
method: 'GET',
|
|
345
|
+
params: params,
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* 获取指定迭代的详细信息
|
|
350
|
+
*/
|
|
351
|
+
async getIterationById(projectId, iterationId) {
|
|
352
|
+
return this.request(`/v4/projects/${projectId}/iterations/${iterationId}`, {
|
|
353
|
+
method: 'GET',
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* 获取项目成员列表
|
|
358
|
+
*/
|
|
359
|
+
async getMembers(projectId, params) {
|
|
360
|
+
return this.request(`/v4/projects/${projectId}/members`, {
|
|
361
|
+
method: 'GET',
|
|
362
|
+
params: {
|
|
363
|
+
offset: 0,
|
|
364
|
+
limit: 100,
|
|
365
|
+
...params,
|
|
366
|
+
},
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* 按用户查询工时(单项目)
|
|
371
|
+
*/
|
|
372
|
+
async showProjectWorkHours(projectId, params) {
|
|
373
|
+
return this.request(`/v4/projects/${projectId}/work-hours`, {
|
|
374
|
+
method: 'POST',
|
|
375
|
+
data: {
|
|
376
|
+
offset: 0,
|
|
377
|
+
limit: 10,
|
|
378
|
+
...params,
|
|
379
|
+
},
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* 工作项添加评论
|
|
384
|
+
*/
|
|
385
|
+
async addIssueNotes(params) {
|
|
386
|
+
return this.request('/v2/issues/update-issue-notes', {
|
|
387
|
+
method: 'POST',
|
|
388
|
+
data: params,
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* 获取当前Token信息(用于调试)
|
|
393
|
+
*/
|
|
394
|
+
getTokenInfo() {
|
|
395
|
+
return this.cachedToken;
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* 手动刷新Token
|
|
399
|
+
*/
|
|
400
|
+
async refreshToken() {
|
|
401
|
+
this.cachedToken = null;
|
|
402
|
+
return this.getValidToken();
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
exports.ApiService = ApiService;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { AllWorkHourStats, HuaweiCloudConfig, IssueDetailResponseV2, IssueItem, IterationInfo, ProjectMember, WorkHourStats, WorkProgressStats } from '../types';
|
|
2
|
+
import { ApiService } from './api.service';
|
|
3
|
+
/**
|
|
4
|
+
* 业务服务类
|
|
5
|
+
* 提供面向业务场景的高级操作,封装ApiService的底层调用
|
|
6
|
+
*/
|
|
7
|
+
export declare class BusinessService {
|
|
8
|
+
private apiService;
|
|
9
|
+
constructor(config: HuaweiCloudConfig);
|
|
10
|
+
/**
|
|
11
|
+
* 获取底层ApiService实例
|
|
12
|
+
* 用于需要直接访问API服务的场景
|
|
13
|
+
*/
|
|
14
|
+
getApiService(): ApiService;
|
|
15
|
+
/**
|
|
16
|
+
* 通过角色ID获取项目成员
|
|
17
|
+
* @param projectId 项目ID
|
|
18
|
+
* @param roleId 角色ID
|
|
19
|
+
* @returns 指定角色的成员列表
|
|
20
|
+
*/
|
|
21
|
+
getMembersByRoleId(projectId: string, roleId: number): Promise<ProjectMember[]>;
|
|
22
|
+
/**
|
|
23
|
+
* 获取指定日期之后的迭代列表
|
|
24
|
+
* @param projectId 项目ID
|
|
25
|
+
* @param targetDate 目标日期,格式:YYYY-MM-DD
|
|
26
|
+
* @returns 正在进行中的和未来的迭代列表
|
|
27
|
+
*/
|
|
28
|
+
getActiveIterationsOnDate(projectId: string, targetDate: string): Promise<IterationInfo[]>;
|
|
29
|
+
/**
|
|
30
|
+
* 根据多个迭代ID和用户ID列表查询工作量列表(仅Task)
|
|
31
|
+
* @param projectId 项目ID
|
|
32
|
+
* @param iterationIds 迭代ID列表
|
|
33
|
+
* @param userIds 用户ID列表
|
|
34
|
+
* @returns Task类型的工作项列表
|
|
35
|
+
*/
|
|
36
|
+
getWorkloadByIterationsAndUsers(projectId: string, iterationIds: number[], userIds: string[]): Promise<IssueItem[]>;
|
|
37
|
+
/**
|
|
38
|
+
* 根据迭代ID和用户ID列表查询工作量列表(仅Task和Story)
|
|
39
|
+
* @param projectId 项目ID
|
|
40
|
+
* @param iterationId 迭代ID
|
|
41
|
+
* @param userIds 用户ID列表
|
|
42
|
+
* @returns Task和Story类型的工作项列表
|
|
43
|
+
*/
|
|
44
|
+
getWorkloadByIterationAndUsers(projectId: string, iterationId: number, userIds: string[]): Promise<IssueItem[]>;
|
|
45
|
+
addIssueNote(projectId: string, issueId: number, content: string): Promise<IssueDetailResponseV2>;
|
|
46
|
+
/**
|
|
47
|
+
* 统计工作项进度信息
|
|
48
|
+
* @param issues 工作项列表
|
|
49
|
+
* @returns 工作项进度统计结果,包括总体统计和按用户分组统计
|
|
50
|
+
*/
|
|
51
|
+
calculateWorkProgress(issues: IssueItem[]): WorkProgressStats;
|
|
52
|
+
/**
|
|
53
|
+
* 查询指定用户在指定日期的工时统计
|
|
54
|
+
* @param projectId 项目ID
|
|
55
|
+
* @param userIds 用户ID列表
|
|
56
|
+
* @param date 查询日期,格式:YYYY-MM-DD
|
|
57
|
+
* @returns 工时统计结果,包括总工时和按用户分组的工时详情
|
|
58
|
+
*/
|
|
59
|
+
getDailyWorkHourStats(projectId: string, userIds: string[], date: string): Promise<WorkHourStats>;
|
|
60
|
+
/**
|
|
61
|
+
* 查询指定用户在指定时间段内的所有工时统计(按人和领域分组)
|
|
62
|
+
* @param projectId 项目ID
|
|
63
|
+
* @param userIds 用户ID列表
|
|
64
|
+
* @param beginDate 开始日期,格式:YYYY-MM-DD
|
|
65
|
+
* @param endDate 结束日期,格式:YYYY-MM-DD
|
|
66
|
+
* @returns 工时统计结果,按用户和领域两个维度分组
|
|
67
|
+
*/
|
|
68
|
+
getAllWorkHourStats(projectId: string, userIds: string[], beginDate: string, endDate: string): Promise<AllWorkHourStats>;
|
|
69
|
+
}
|