devops-mcp-server-extension 1.0.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/LICENSE +218 -0
- package/README.md +527 -0
- package/README.zh-cn.md +503 -0
- package/dist/common/errors.js +77 -0
- package/dist/common/modularTemplates.js +483 -0
- package/dist/common/pipelineTemplates.js +19 -0
- package/dist/common/toolsetManager.js +123 -0
- package/dist/common/toolsets.js +23 -0
- package/dist/common/types.js +60 -0
- package/dist/common/utils.js +381 -0
- package/dist/common/version.js +1 -0
- package/dist/index.js +225 -0
- package/dist/operations/appstack/appOrchestrations.js +260 -0
- package/dist/operations/appstack/appTags.js +168 -0
- package/dist/operations/appstack/appTemplates.js +72 -0
- package/dist/operations/appstack/applications.js +171 -0
- package/dist/operations/appstack/changeOrders.js +320 -0
- package/dist/operations/appstack/changeRequests.js +288 -0
- package/dist/operations/appstack/deploymentResources.js +286 -0
- package/dist/operations/appstack/globalVars.js +221 -0
- package/dist/operations/appstack/releaseWorkflows.js +695 -0
- package/dist/operations/appstack/variableGroups.js +245 -0
- package/dist/operations/codeup/branches.js +157 -0
- package/dist/operations/codeup/changeRequestComments.js +140 -0
- package/dist/operations/codeup/changeRequests.js +230 -0
- package/dist/operations/codeup/commits.js +121 -0
- package/dist/operations/codeup/compare.js +30 -0
- package/dist/operations/codeup/files.js +249 -0
- package/dist/operations/codeup/repositories.js +71 -0
- package/dist/operations/codeup/types.js +414 -0
- package/dist/operations/flow/hostGroup.js +52 -0
- package/dist/operations/flow/pipeline.js +609 -0
- package/dist/operations/flow/pipelineJob.js +126 -0
- package/dist/operations/flow/resourceMember.js +137 -0
- package/dist/operations/flow/serviceConnection.js +27 -0
- package/dist/operations/flow/tag.js +191 -0
- package/dist/operations/flow/types.js +523 -0
- package/dist/operations/flow/vmDeployOrder.js +171 -0
- package/dist/operations/organization/members.js +106 -0
- package/dist/operations/organization/organization.js +110 -0
- package/dist/operations/organization/types.js +111 -0
- package/dist/operations/packages/artifacts.js +71 -0
- package/dist/operations/packages/repositories.js +39 -0
- package/dist/operations/packages/types.js +56 -0
- package/dist/operations/projex/effort.js +122 -0
- package/dist/operations/projex/project.js +243 -0
- package/dist/operations/projex/sprint.js +103 -0
- package/dist/operations/projex/types.js +618 -0
- package/dist/operations/projex/workitem.js +826 -0
- package/dist/operations/testhub/testcases.js +240 -0
- package/dist/operations/testhub/testplans.js +128 -0
- package/dist/tool-handlers/appstack-app-release-workflows.js +103 -0
- package/dist/tool-handlers/appstack-change-orders.js +55 -0
- package/dist/tool-handlers/appstack-change-requests.js +49 -0
- package/dist/tool-handlers/appstack-deployment-resources.js +31 -0
- package/dist/tool-handlers/appstack-global-vars.js +37 -0
- package/dist/tool-handlers/appstack-orchestrations.js +49 -0
- package/dist/tool-handlers/appstack-release-workflows.js +37 -0
- package/dist/tool-handlers/appstack-tags.js +37 -0
- package/dist/tool-handlers/appstack-templates.js +19 -0
- package/dist/tool-handlers/appstack-variable-groups.js +55 -0
- package/dist/tool-handlers/appstack.js +37 -0
- package/dist/tool-handlers/base.js +25 -0
- package/dist/tool-handlers/code-management.js +150 -0
- package/dist/tool-handlers/commit.js +31 -0
- package/dist/tool-handlers/effort.js +103 -0
- package/dist/tool-handlers/index.js +119 -0
- package/dist/tool-handlers/organization.js +72 -0
- package/dist/tool-handlers/packages.js +32 -0
- package/dist/tool-handlers/pipeline.js +289 -0
- package/dist/tool-handlers/project-management.js +201 -0
- package/dist/tool-handlers/resourceMember.js +43 -0
- package/dist/tool-handlers/service-connections.js +16 -0
- package/dist/tool-handlers/tag.js +64 -0
- package/dist/tool-handlers/test-management.js +74 -0
- package/dist/tool-handlers/vmDeployOrder.js +50 -0
- package/dist/tool-registry/appstack-app-release-workflows.js +80 -0
- package/dist/tool-registry/appstack-change-orders.js +40 -0
- package/dist/tool-registry/appstack-change-requests.js +35 -0
- package/dist/tool-registry/appstack-deployment-resources.js +20 -0
- package/dist/tool-registry/appstack-global-vars.js +25 -0
- package/dist/tool-registry/appstack-orchestrations.js +35 -0
- package/dist/tool-registry/appstack-release-workflows.js +25 -0
- package/dist/tool-registry/appstack-tags.js +25 -0
- package/dist/tool-registry/appstack-templates.js +10 -0
- package/dist/tool-registry/appstack-variable-groups.js +40 -0
- package/dist/tool-registry/appstack.js +25 -0
- package/dist/tool-registry/base.js +19 -0
- package/dist/tool-registry/code-management.js +109 -0
- package/dist/tool-registry/commit.js +20 -0
- package/dist/tool-registry/effort.js +39 -0
- package/dist/tool-registry/index.js +7 -0
- package/dist/tool-registry/organization.js +65 -0
- package/dist/tool-registry/packages.js +21 -0
- package/dist/tool-registry/pipeline.js +190 -0
- package/dist/tool-registry/project-management.js +143 -0
- package/dist/tool-registry/resourceMember.js +29 -0
- package/dist/tool-registry/service-connections.js +10 -0
- package/dist/tool-registry/tag.js +44 -0
- package/dist/tool-registry/test-management.js +59 -0
- package/dist/tool-registry/vmDeployOrder.js +34 -0
- package/package.json +52 -0
|
@@ -0,0 +1,826 @@
|
|
|
1
|
+
import { buildUrl, yunxiaoRequest, getYunxiaoApiBaseUrl, getCurrentSessionToken, isRegionEdition } from "../../common/utils.js";
|
|
2
|
+
import { createYunxiaoError } from "../../common/errors.js";
|
|
3
|
+
import { getUserAgent } from "universal-user-agent";
|
|
4
|
+
import { VERSION } from "../../common/version.js";
|
|
5
|
+
import { WorkItemSchema, WorkItemActivitySchema, WorkItemAttachmentSchema, WorkItemFileSchema, WorkItemRelationRecordSchema } from "./types.js";
|
|
6
|
+
import { getCurrentUserFunc, resolveOrganizationId } from "../organization/organization.js";
|
|
7
|
+
export async function getWorkItemFunc(organizationId, workItemId) {
|
|
8
|
+
const finalOrgId = await resolveOrganizationId(organizationId);
|
|
9
|
+
const url = isRegionEdition()
|
|
10
|
+
? `/oapi/v1/projex/workitems/${workItemId}`
|
|
11
|
+
: `/oapi/v1/projex/organizations/${finalOrgId}/workitems/${workItemId}`;
|
|
12
|
+
const response = await yunxiaoRequest(url, {
|
|
13
|
+
method: "GET",
|
|
14
|
+
});
|
|
15
|
+
return WorkItemSchema.parse(response);
|
|
16
|
+
}
|
|
17
|
+
export async function searchWorkitemsFunc(organizationId, category, spaceId, subject, status, createdAfter, createdBefore, updatedAfter, updatedBefore, creator, assignedTo, sprint, workitemType, statusStage, tag, priority, subjectDescription, finishTimeAfter, finishTimeBefore, updateStatusAtAfter, updateStatusAtBefore, advancedConditions, orderBy = "gmtCreate", sort = "desc", page, perPage, includeDetails = false // 新增参数:是否自动补充缺失的description等详细信息
|
|
18
|
+
) {
|
|
19
|
+
// 处理assignedTo为"self"的情况,自动获取当前用户ID
|
|
20
|
+
let finalAssignedTo = assignedTo;
|
|
21
|
+
let finalCreator = creator;
|
|
22
|
+
if (assignedTo === "self" || creator === "self") {
|
|
23
|
+
try {
|
|
24
|
+
const currentUser = await getCurrentUserFunc();
|
|
25
|
+
if (currentUser.id) {
|
|
26
|
+
if (assignedTo === "self") {
|
|
27
|
+
finalAssignedTo = currentUser.id;
|
|
28
|
+
}
|
|
29
|
+
if (creator === "self") {
|
|
30
|
+
finalCreator = currentUser.id;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
finalAssignedTo = assignedTo;
|
|
35
|
+
finalCreator = creator;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
finalAssignedTo = assignedTo;
|
|
40
|
+
finalCreator = creator;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
const finalOrgId = await resolveOrganizationId(organizationId);
|
|
44
|
+
const url = isRegionEdition()
|
|
45
|
+
? `/oapi/v1/projex/workitems:search`
|
|
46
|
+
: `/oapi/v1/projex/organizations/${finalOrgId}/workitems:search`;
|
|
47
|
+
const payload = {
|
|
48
|
+
category: category,
|
|
49
|
+
spaceId: spaceId,
|
|
50
|
+
};
|
|
51
|
+
const conditions = buildWorkitemConditions({
|
|
52
|
+
subject,
|
|
53
|
+
status,
|
|
54
|
+
createdAfter,
|
|
55
|
+
createdBefore,
|
|
56
|
+
updatedAfter,
|
|
57
|
+
updatedBefore,
|
|
58
|
+
creator: finalCreator,
|
|
59
|
+
assignedTo: finalAssignedTo,
|
|
60
|
+
sprint,
|
|
61
|
+
workitemType,
|
|
62
|
+
statusStage,
|
|
63
|
+
tag,
|
|
64
|
+
priority,
|
|
65
|
+
subjectDescription,
|
|
66
|
+
finishTimeAfter,
|
|
67
|
+
finishTimeBefore,
|
|
68
|
+
updateStatusAtAfter,
|
|
69
|
+
updateStatusAtBefore,
|
|
70
|
+
advancedConditions
|
|
71
|
+
});
|
|
72
|
+
if (conditions) {
|
|
73
|
+
payload.conditions = conditions;
|
|
74
|
+
}
|
|
75
|
+
payload.orderBy = orderBy;
|
|
76
|
+
// 添加分页和排序参数
|
|
77
|
+
if (sort) {
|
|
78
|
+
payload.sort = sort;
|
|
79
|
+
}
|
|
80
|
+
if (page !== undefined) {
|
|
81
|
+
payload.page = page;
|
|
82
|
+
}
|
|
83
|
+
if (perPage !== undefined) {
|
|
84
|
+
payload.perPage = perPage;
|
|
85
|
+
}
|
|
86
|
+
// 使用 fetch 直接获取响应,以便读取响应头中的分页信息
|
|
87
|
+
const isAbsolute = url.startsWith("http://") || url.startsWith("https://");
|
|
88
|
+
const fullUrl = isAbsolute ? url : `${getYunxiaoApiBaseUrl()}${url.startsWith("/") ? url : `/${url}`}`;
|
|
89
|
+
const requestHeaders = {
|
|
90
|
+
"Accept": "application/json",
|
|
91
|
+
"Content-Type": "application/json",
|
|
92
|
+
"User-Agent": `modelcontextprotocol/servers/alibabacloud-devops-mcp-server/v${VERSION} ${getUserAgent()}`,
|
|
93
|
+
};
|
|
94
|
+
const token = getCurrentSessionToken();
|
|
95
|
+
if (token) {
|
|
96
|
+
requestHeaders["x-yunxiao-token"] = token;
|
|
97
|
+
}
|
|
98
|
+
const response = await fetch(fullUrl, {
|
|
99
|
+
method: "POST",
|
|
100
|
+
headers: requestHeaders,
|
|
101
|
+
body: JSON.stringify(payload),
|
|
102
|
+
});
|
|
103
|
+
if (!response.ok) {
|
|
104
|
+
const responseBody = await response.json().catch(() => ({}));
|
|
105
|
+
throw createYunxiaoError(response.status, responseBody, fullUrl, "POST", requestHeaders, payload);
|
|
106
|
+
}
|
|
107
|
+
const responseBody = await response.json();
|
|
108
|
+
// 从响应头中提取分页信息
|
|
109
|
+
const pagination = (() => {
|
|
110
|
+
const xPage = response.headers.get("x-page");
|
|
111
|
+
const xPerPage = response.headers.get("x-per-page");
|
|
112
|
+
const xTotalPages = response.headers.get("x-total-pages");
|
|
113
|
+
const xTotal = response.headers.get("x-total");
|
|
114
|
+
const xNextPage = response.headers.get("x-next-page");
|
|
115
|
+
const xPrevPage = response.headers.get("x-prev-page");
|
|
116
|
+
if (xPage && xPerPage && xTotalPages && xTotal) {
|
|
117
|
+
return {
|
|
118
|
+
page: parseInt(xPage, 10),
|
|
119
|
+
perPage: parseInt(xPerPage, 10),
|
|
120
|
+
totalPages: parseInt(xTotalPages, 10),
|
|
121
|
+
total: parseInt(xTotal, 10),
|
|
122
|
+
nextPage: xNextPage ? parseInt(xNextPage, 10) : null,
|
|
123
|
+
prevPage: xPrevPage ? parseInt(xPrevPage, 10) : null,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
return undefined;
|
|
127
|
+
})();
|
|
128
|
+
if (!Array.isArray(responseBody)) {
|
|
129
|
+
return {
|
|
130
|
+
items: [],
|
|
131
|
+
pagination,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
const workItems = responseBody.map(workitem => WorkItemSchema.parse(workitem));
|
|
135
|
+
// 如果需要补充详细信息,使用分批并发方式获取
|
|
136
|
+
if (includeDetails) {
|
|
137
|
+
const itemsNeedingDetails = workItems.filter(item => item.id.length > 0 &&
|
|
138
|
+
(item.description === null || item.description === undefined || item.description === ""));
|
|
139
|
+
if (itemsNeedingDetails.length > 0) {
|
|
140
|
+
// 分批并发获取详情
|
|
141
|
+
const descriptionMap = await batchGetWorkItemDetails(finalOrgId, itemsNeedingDetails);
|
|
142
|
+
// 更新workItems中的description
|
|
143
|
+
const updatedItems = workItems.map(item => {
|
|
144
|
+
if (descriptionMap.has(item.id)) {
|
|
145
|
+
return {
|
|
146
|
+
...item,
|
|
147
|
+
description: descriptionMap.get(item.id) || item.description
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
return item;
|
|
151
|
+
});
|
|
152
|
+
return {
|
|
153
|
+
items: updatedItems,
|
|
154
|
+
pagination,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return {
|
|
159
|
+
items: workItems,
|
|
160
|
+
pagination,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
// 分批并发获取工作项详情
|
|
164
|
+
async function batchGetWorkItemDetails(organizationId, workItems, batchSize = 10, // 每批处理10个
|
|
165
|
+
maxItems = 100 // 最多处理100个
|
|
166
|
+
) {
|
|
167
|
+
const descriptionMap = new Map();
|
|
168
|
+
// 限制处理数量
|
|
169
|
+
const limitedItems = workItems.slice(0, maxItems);
|
|
170
|
+
// 分批处理
|
|
171
|
+
for (let i = 0; i < limitedItems.length; i += batchSize) {
|
|
172
|
+
const batch = limitedItems.slice(i, i + batchSize);
|
|
173
|
+
// 批次内并发执行
|
|
174
|
+
const batchResults = await Promise.allSettled(batch.map(async (item) => {
|
|
175
|
+
// 再次检查item.id是否为有效字符串
|
|
176
|
+
if (typeof item.id !== 'string' || item.id.length === 0) {
|
|
177
|
+
return {
|
|
178
|
+
id: item.id || 'unknown',
|
|
179
|
+
description: null,
|
|
180
|
+
success: false
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
const itemId = item.id;
|
|
184
|
+
try {
|
|
185
|
+
const detailedItem = await getWorkItemFunc(organizationId, itemId);
|
|
186
|
+
return {
|
|
187
|
+
id: itemId,
|
|
188
|
+
description: detailedItem.description,
|
|
189
|
+
success: true
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
catch (error) {
|
|
193
|
+
return {
|
|
194
|
+
id: itemId,
|
|
195
|
+
description: null,
|
|
196
|
+
success: false
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
}));
|
|
200
|
+
// 处理批次结果
|
|
201
|
+
batchResults.forEach((result) => {
|
|
202
|
+
if (result.status === 'fulfilled') {
|
|
203
|
+
// 确保description类型正确,将undefined转换为null
|
|
204
|
+
const description = result.value.description === undefined ? null : result.value.description;
|
|
205
|
+
descriptionMap.set(result.value.id, description);
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
return descriptionMap;
|
|
210
|
+
}
|
|
211
|
+
function buildWorkitemConditions(args) {
|
|
212
|
+
if (args.advancedConditions) {
|
|
213
|
+
return args.advancedConditions;
|
|
214
|
+
}
|
|
215
|
+
const filterConditions = [];
|
|
216
|
+
if (args.subject) {
|
|
217
|
+
filterConditions.push({
|
|
218
|
+
className: "string",
|
|
219
|
+
fieldIdentifier: "subject",
|
|
220
|
+
format: "input",
|
|
221
|
+
operator: "CONTAINS",
|
|
222
|
+
toValue: null,
|
|
223
|
+
value: [args.subject],
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
if (args.status) {
|
|
227
|
+
const statusValues = args.status.split(",");
|
|
228
|
+
const values = statusValues.map(v => v.trim());
|
|
229
|
+
filterConditions.push({
|
|
230
|
+
className: "status",
|
|
231
|
+
fieldIdentifier: "status",
|
|
232
|
+
format: "list",
|
|
233
|
+
operator: "CONTAINS",
|
|
234
|
+
toValue: null,
|
|
235
|
+
value: values,
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
if (args.createdAfter) {
|
|
239
|
+
const createdBefore = args.createdBefore ? `${args.createdBefore} 23:59:59` : null;
|
|
240
|
+
filterConditions.push({
|
|
241
|
+
className: "dateTime",
|
|
242
|
+
fieldIdentifier: "gmtCreate",
|
|
243
|
+
format: "input",
|
|
244
|
+
operator: "BETWEEN",
|
|
245
|
+
toValue: createdBefore,
|
|
246
|
+
value: [`${args.createdAfter} 00:00:00`],
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
if (args.updatedAfter) {
|
|
250
|
+
const updatedBefore = args.updatedBefore ? `${args.updatedBefore} 23:59:59` : null;
|
|
251
|
+
filterConditions.push({
|
|
252
|
+
className: "dateTime",
|
|
253
|
+
fieldIdentifier: "gmtModified",
|
|
254
|
+
format: "input",
|
|
255
|
+
operator: "BETWEEN",
|
|
256
|
+
toValue: updatedBefore,
|
|
257
|
+
value: [`${args.updatedAfter} 00:00:00`],
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
if (args.creator) {
|
|
261
|
+
const creatorValues = args.creator.split(",");
|
|
262
|
+
const values = creatorValues.map(v => v.trim());
|
|
263
|
+
filterConditions.push({
|
|
264
|
+
className: "user",
|
|
265
|
+
fieldIdentifier: "creator",
|
|
266
|
+
format: "list",
|
|
267
|
+
operator: "CONTAINS",
|
|
268
|
+
toValue: null,
|
|
269
|
+
value: values,
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
if (args.assignedTo) {
|
|
273
|
+
const assignedToValues = args.assignedTo.split(",");
|
|
274
|
+
const values = assignedToValues.map(v => v.trim());
|
|
275
|
+
filterConditions.push({
|
|
276
|
+
className: "user",
|
|
277
|
+
fieldIdentifier: "assignedTo",
|
|
278
|
+
format: "list",
|
|
279
|
+
operator: "CONTAINS",
|
|
280
|
+
toValue: null,
|
|
281
|
+
value: values,
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
if (args.sprint) {
|
|
285
|
+
const sprintValues = args.sprint.split(",");
|
|
286
|
+
const values = sprintValues.map(v => v.trim());
|
|
287
|
+
filterConditions.push({
|
|
288
|
+
className: "sprint",
|
|
289
|
+
fieldIdentifier: "sprint",
|
|
290
|
+
format: "list",
|
|
291
|
+
operator: "CONTAINS",
|
|
292
|
+
toValue: null,
|
|
293
|
+
value: values,
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
if (args.workitemType) {
|
|
297
|
+
const workitemTypeValues = args.workitemType.split(",");
|
|
298
|
+
const values = workitemTypeValues.map(v => v.trim());
|
|
299
|
+
filterConditions.push({
|
|
300
|
+
className: "workitemType",
|
|
301
|
+
fieldIdentifier: "workitemType",
|
|
302
|
+
format: "list",
|
|
303
|
+
operator: "CONTAINS",
|
|
304
|
+
toValue: null,
|
|
305
|
+
value: values,
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
if (args.statusStage) {
|
|
309
|
+
const statusStageValues = args.statusStage.split(",");
|
|
310
|
+
const values = statusStageValues.map(v => v.trim());
|
|
311
|
+
filterConditions.push({
|
|
312
|
+
className: "statusStage",
|
|
313
|
+
fieldIdentifier: "statusStage",
|
|
314
|
+
format: "list",
|
|
315
|
+
operator: "CONTAINS",
|
|
316
|
+
toValue: null,
|
|
317
|
+
value: values,
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
if (args.tag) {
|
|
321
|
+
const tagValues = args.tag.split(",");
|
|
322
|
+
const values = tagValues.map(v => v.trim());
|
|
323
|
+
filterConditions.push({
|
|
324
|
+
className: "tag",
|
|
325
|
+
fieldIdentifier: "tag",
|
|
326
|
+
format: "multiList",
|
|
327
|
+
operator: "CONTAINS",
|
|
328
|
+
toValue: null,
|
|
329
|
+
value: values,
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
if (args.priority) {
|
|
333
|
+
const priorityValues = args.priority.split(",");
|
|
334
|
+
const values = priorityValues.map(v => v.trim());
|
|
335
|
+
filterConditions.push({
|
|
336
|
+
className: "option",
|
|
337
|
+
fieldIdentifier: "priority",
|
|
338
|
+
format: "list",
|
|
339
|
+
operator: "CONTAINS",
|
|
340
|
+
toValue: null,
|
|
341
|
+
value: values,
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
if (args.subjectDescription) {
|
|
345
|
+
filterConditions.push({
|
|
346
|
+
className: "string",
|
|
347
|
+
fieldIdentifier: "subject-description",
|
|
348
|
+
format: "input",
|
|
349
|
+
operator: "CONTAINS",
|
|
350
|
+
toValue: null,
|
|
351
|
+
value: [args.subjectDescription],
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
if (args.finishTimeAfter) {
|
|
355
|
+
const finishTimeBefore = args.finishTimeBefore ? `${args.finishTimeBefore} 23:59:59` : null;
|
|
356
|
+
filterConditions.push({
|
|
357
|
+
className: "date",
|
|
358
|
+
fieldIdentifier: "finishTime",
|
|
359
|
+
format: "input",
|
|
360
|
+
operator: "BETWEEN",
|
|
361
|
+
toValue: finishTimeBefore,
|
|
362
|
+
value: [`${args.finishTimeAfter} 00:00:00`],
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
if (args.updateStatusAtAfter) {
|
|
366
|
+
const updateStatusAtBefore = args.updateStatusAtBefore ? `${args.updateStatusAtBefore} 23:59:59` : null;
|
|
367
|
+
filterConditions.push({
|
|
368
|
+
className: "date",
|
|
369
|
+
fieldIdentifier: "updateStatusAt",
|
|
370
|
+
format: "input",
|
|
371
|
+
operator: "BETWEEN",
|
|
372
|
+
toValue: updateStatusAtBefore,
|
|
373
|
+
value: [`${args.updateStatusAtAfter} 00:00:00`],
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
if (filterConditions.length === 0) {
|
|
377
|
+
return undefined;
|
|
378
|
+
}
|
|
379
|
+
const conditions = {
|
|
380
|
+
conditionGroups: [filterConditions],
|
|
381
|
+
};
|
|
382
|
+
return JSON.stringify(conditions);
|
|
383
|
+
}
|
|
384
|
+
export async function createWorkItemFunc(organizationId, assignedTo, spaceId, subject, workitemTypeId, customFieldValues, description, labels, parentId, participants, sprint, trackers, verifier, versions) {
|
|
385
|
+
const finalOrgId = await resolveOrganizationId(organizationId);
|
|
386
|
+
const url = isRegionEdition()
|
|
387
|
+
? `/oapi/v1/projex/workitems`
|
|
388
|
+
: `/oapi/v1/projex/organizations/${finalOrgId}/workitems`;
|
|
389
|
+
const payload = {
|
|
390
|
+
assignedTo,
|
|
391
|
+
spaceId,
|
|
392
|
+
subject,
|
|
393
|
+
workitemTypeId
|
|
394
|
+
};
|
|
395
|
+
if (customFieldValues) {
|
|
396
|
+
payload.customFieldValues = customFieldValues;
|
|
397
|
+
}
|
|
398
|
+
if (description !== undefined) {
|
|
399
|
+
payload.description = description;
|
|
400
|
+
}
|
|
401
|
+
if (labels && labels.length > 0) {
|
|
402
|
+
payload.labels = labels;
|
|
403
|
+
}
|
|
404
|
+
if (parentId !== undefined) {
|
|
405
|
+
payload.parentId = parentId;
|
|
406
|
+
}
|
|
407
|
+
if (participants && participants.length > 0) {
|
|
408
|
+
payload.participants = participants;
|
|
409
|
+
}
|
|
410
|
+
if (sprint !== undefined) {
|
|
411
|
+
payload.sprint = sprint;
|
|
412
|
+
}
|
|
413
|
+
if (trackers && trackers.length > 0) {
|
|
414
|
+
payload.trackers = trackers;
|
|
415
|
+
}
|
|
416
|
+
if (verifier !== undefined) {
|
|
417
|
+
payload.verifier = verifier;
|
|
418
|
+
}
|
|
419
|
+
if (versions && versions.length > 0) {
|
|
420
|
+
payload.versions = versions;
|
|
421
|
+
}
|
|
422
|
+
const response = await yunxiaoRequest(url, {
|
|
423
|
+
method: "POST",
|
|
424
|
+
body: payload,
|
|
425
|
+
});
|
|
426
|
+
return WorkItemSchema.parse(response);
|
|
427
|
+
}
|
|
428
|
+
export async function updateWorkItemFunc(organizationId, workItemId, updateWorkItemFields) {
|
|
429
|
+
const finalOrgId = await resolveOrganizationId(organizationId);
|
|
430
|
+
const url = isRegionEdition()
|
|
431
|
+
? `/oapi/v1/projex/workitems/${workItemId}`
|
|
432
|
+
: `/oapi/v1/projex/organizations/${finalOrgId}/workitems/${workItemId}`;
|
|
433
|
+
// 构建请求体,将自定义字段合并到主对象中
|
|
434
|
+
const requestBody = {};
|
|
435
|
+
// 复制所有标准字段
|
|
436
|
+
if (updateWorkItemFields.subject !== undefined) {
|
|
437
|
+
requestBody.subject = updateWorkItemFields.subject;
|
|
438
|
+
}
|
|
439
|
+
if (updateWorkItemFields.description !== undefined) {
|
|
440
|
+
requestBody.description = updateWorkItemFields.description;
|
|
441
|
+
}
|
|
442
|
+
if (updateWorkItemFields.status !== undefined) {
|
|
443
|
+
requestBody.status = updateWorkItemFields.status;
|
|
444
|
+
}
|
|
445
|
+
if (updateWorkItemFields.assignedTo !== undefined) {
|
|
446
|
+
requestBody.assignedTo = updateWorkItemFields.assignedTo;
|
|
447
|
+
}
|
|
448
|
+
if (updateWorkItemFields.priority !== undefined) {
|
|
449
|
+
requestBody.priority = updateWorkItemFields.priority;
|
|
450
|
+
}
|
|
451
|
+
if (updateWorkItemFields.labels !== undefined) {
|
|
452
|
+
requestBody.labels = updateWorkItemFields.labels;
|
|
453
|
+
}
|
|
454
|
+
if (updateWorkItemFields.sprint !== undefined) {
|
|
455
|
+
requestBody.sprint = updateWorkItemFields.sprint;
|
|
456
|
+
}
|
|
457
|
+
if (updateWorkItemFields.trackers !== undefined) {
|
|
458
|
+
requestBody.trackers = updateWorkItemFields.trackers;
|
|
459
|
+
}
|
|
460
|
+
if (updateWorkItemFields.verifier !== undefined) {
|
|
461
|
+
requestBody.verifier = updateWorkItemFields.verifier;
|
|
462
|
+
}
|
|
463
|
+
if (updateWorkItemFields.participants !== undefined) {
|
|
464
|
+
requestBody.participants = updateWorkItemFields.participants;
|
|
465
|
+
}
|
|
466
|
+
if (updateWorkItemFields.versions !== undefined) {
|
|
467
|
+
requestBody.versions = updateWorkItemFields.versions;
|
|
468
|
+
}
|
|
469
|
+
// 处理自定义字段
|
|
470
|
+
if (updateWorkItemFields.customFieldValues !== undefined) {
|
|
471
|
+
// 将自定义字段合并到请求体中
|
|
472
|
+
Object.entries(updateWorkItemFields.customFieldValues).forEach(([fieldId, value]) => {
|
|
473
|
+
requestBody[fieldId] = value;
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
const response = await yunxiaoRequest(url, {
|
|
477
|
+
method: "PUT",
|
|
478
|
+
body: requestBody,
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
export async function getWorkItemTypesFunc(organizationId, id, // 项目唯一标识
|
|
482
|
+
category // 工作项类型,可选值为 Req,Bug,Task 等。
|
|
483
|
+
) {
|
|
484
|
+
const finalOrgId = await resolveOrganizationId(organizationId);
|
|
485
|
+
const url = isRegionEdition()
|
|
486
|
+
? `/oapi/v1/projex/projects/${id}/workitemTypes?category=${encodeURIComponent(category)}`
|
|
487
|
+
: `/oapi/v1/projex/organizations/${finalOrgId}/projects/${id}/workitemTypes?category=${encodeURIComponent(category)}`;
|
|
488
|
+
const response = await yunxiaoRequest(url, {
|
|
489
|
+
method: "GET",
|
|
490
|
+
});
|
|
491
|
+
return response;
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* 列出所有工作项类型
|
|
495
|
+
* @param organizationId 企业ID
|
|
496
|
+
* @returns 工作项类型列表
|
|
497
|
+
*/
|
|
498
|
+
export async function listAllWorkItemTypesFunc(organizationId) {
|
|
499
|
+
const finalOrgId = await resolveOrganizationId(organizationId);
|
|
500
|
+
const url = isRegionEdition()
|
|
501
|
+
? `/oapi/v1/projex/workitemTypes`
|
|
502
|
+
: `/oapi/v1/projex/organizations/${finalOrgId}/workitemTypes`;
|
|
503
|
+
const response = await yunxiaoRequest(url, {
|
|
504
|
+
method: "GET",
|
|
505
|
+
});
|
|
506
|
+
// 确保返回的是数组格式
|
|
507
|
+
if (Array.isArray(response)) {
|
|
508
|
+
return response;
|
|
509
|
+
}
|
|
510
|
+
// 如果响应中包含result字段,则返回result中的数据
|
|
511
|
+
if (response && typeof response === 'object' && 'result' in response && Array.isArray(response.result)) {
|
|
512
|
+
return response.result;
|
|
513
|
+
}
|
|
514
|
+
// 其他情况返回空数组
|
|
515
|
+
return [];
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* 列出工作项类型
|
|
519
|
+
* @param organizationId 企业ID
|
|
520
|
+
* @param spaceIdentifier 项目唯一标识
|
|
521
|
+
* @param category 工作项类型分类(可选)
|
|
522
|
+
* @returns 工作项类型列表
|
|
523
|
+
*/
|
|
524
|
+
export async function listWorkItemTypesFunc(organizationId, spaceIdentifier, category) {
|
|
525
|
+
const finalOrgId = await resolveOrganizationId(organizationId);
|
|
526
|
+
let url = isRegionEdition()
|
|
527
|
+
? `/oapi/v1/projex/projects/${spaceIdentifier}/workitemTypes`
|
|
528
|
+
: `/oapi/v1/projex/organizations/${finalOrgId}/projects/${spaceIdentifier}/workitemTypes`;
|
|
529
|
+
// 如果提供了category参数,则添加到URL中
|
|
530
|
+
if (category) {
|
|
531
|
+
url += `?category=${encodeURIComponent(category)}`;
|
|
532
|
+
}
|
|
533
|
+
const response = await yunxiaoRequest(url, {
|
|
534
|
+
method: "GET",
|
|
535
|
+
});
|
|
536
|
+
// 确保返回的是数组格式
|
|
537
|
+
if (Array.isArray(response)) {
|
|
538
|
+
return response;
|
|
539
|
+
}
|
|
540
|
+
// 如果响应中包含result字段,则返回result中的数据
|
|
541
|
+
if (response && typeof response === 'object' && 'result' in response && Array.isArray(response.result)) {
|
|
542
|
+
return response.result;
|
|
543
|
+
}
|
|
544
|
+
// 其他情况返回空数组
|
|
545
|
+
return [];
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* 获取工作项类型详情
|
|
549
|
+
* @param organizationId 企业ID
|
|
550
|
+
* @param spaceIdentifier 项目唯一标识
|
|
551
|
+
* @param id 工作项类型ID
|
|
552
|
+
* @returns 工作项类型详情
|
|
553
|
+
*/
|
|
554
|
+
export async function getWorkItemTypeFunc(organizationId, id) {
|
|
555
|
+
const finalOrgId = await resolveOrganizationId(organizationId);
|
|
556
|
+
const url = isRegionEdition()
|
|
557
|
+
? `/oapi/v1/projex/workitemTypes/${id}`
|
|
558
|
+
: `/oapi/v1/projex/organizations/${finalOrgId}/workitemTypes/${id}`;
|
|
559
|
+
const response = await yunxiaoRequest(url, {
|
|
560
|
+
method: "GET",
|
|
561
|
+
});
|
|
562
|
+
// 如果响应中包含result字段,则返回result中的数据
|
|
563
|
+
if (response && typeof response === 'object' && 'result' in response) {
|
|
564
|
+
return response.result;
|
|
565
|
+
}
|
|
566
|
+
// 否则直接返回响应
|
|
567
|
+
return response;
|
|
568
|
+
}
|
|
569
|
+
/**
|
|
570
|
+
* 列出工作项关联的工作项类型
|
|
571
|
+
* @param organizationId 企业ID
|
|
572
|
+
* @param spaceIdentifier 项目唯一标识
|
|
573
|
+
* @param workItemTypeId 工作项ID
|
|
574
|
+
* @param relationType 关联类型 (BLOCK, RELATE, DUPLICATE, CHILD)
|
|
575
|
+
* @returns 关联的工作项类型列表
|
|
576
|
+
*/
|
|
577
|
+
export async function listWorkItemRelationWorkItemTypesFunc(organizationId, workItemTypeId, relationType) {
|
|
578
|
+
const finalOrgId = await resolveOrganizationId(organizationId);
|
|
579
|
+
const url = isRegionEdition()
|
|
580
|
+
? `/oapi/v1/projex/workitemTypes/${workItemTypeId}/relationWorkitemTypes`
|
|
581
|
+
: `/oapi/v1/projex/organizations/${finalOrgId}/workitemTypes/${workItemTypeId}/relationWorkitemTypes`;
|
|
582
|
+
const queryParams = {};
|
|
583
|
+
if (relationType != null) {
|
|
584
|
+
queryParams.relationType = relationType;
|
|
585
|
+
}
|
|
586
|
+
let finalUrl = buildUrl(url, queryParams);
|
|
587
|
+
const response = await yunxiaoRequest(finalUrl, {
|
|
588
|
+
method: "GET",
|
|
589
|
+
});
|
|
590
|
+
// 确保返回的是数组格式
|
|
591
|
+
if (Array.isArray(response)) {
|
|
592
|
+
return response;
|
|
593
|
+
}
|
|
594
|
+
// 如果响应中包含result字段,则返回result中的数据
|
|
595
|
+
if (response && typeof response === 'object' && 'result' in response && Array.isArray(response.result)) {
|
|
596
|
+
return response.result;
|
|
597
|
+
}
|
|
598
|
+
// 其他情况返回空数组
|
|
599
|
+
return [];
|
|
600
|
+
}
|
|
601
|
+
/**
|
|
602
|
+
* 获取工作项类型字段配置
|
|
603
|
+
* @param organizationId 企业ID
|
|
604
|
+
* @param projectId 项目唯一标识
|
|
605
|
+
* @param workItemTypeId 工作项类型ID
|
|
606
|
+
* @returns 工作项类型字段配置
|
|
607
|
+
*/
|
|
608
|
+
export async function getWorkItemTypeFieldConfigFunc(organizationId, projectId, workItemTypeId) {
|
|
609
|
+
const finalOrgId = await resolveOrganizationId(organizationId);
|
|
610
|
+
const url = isRegionEdition()
|
|
611
|
+
? `/oapi/v1/projex/projects/${projectId}/workitemTypes/${workItemTypeId}/fields`
|
|
612
|
+
: `/oapi/v1/projex/organizations/${finalOrgId}/projects/${projectId}/workitemTypes/${workItemTypeId}/fields`;
|
|
613
|
+
const response = await yunxiaoRequest(url, {
|
|
614
|
+
method: "GET",
|
|
615
|
+
});
|
|
616
|
+
// 如果响应中包含result字段,则返回result中的数据
|
|
617
|
+
if (response && typeof response === 'object' && 'result' in response) {
|
|
618
|
+
return response.result;
|
|
619
|
+
}
|
|
620
|
+
// 否则直接返回响应
|
|
621
|
+
return response;
|
|
622
|
+
}
|
|
623
|
+
/**
|
|
624
|
+
* 获取工作项工作流
|
|
625
|
+
* @param organizationId 企业ID
|
|
626
|
+
* @param projectId 项目唯一标识
|
|
627
|
+
* @param workItemTypeId 工作项类型ID
|
|
628
|
+
* @returns 工作项工作流信息
|
|
629
|
+
*/
|
|
630
|
+
export async function getWorkItemWorkflowFunc(organizationId, projectId, workItemTypeId) {
|
|
631
|
+
const finalOrgId = await resolveOrganizationId(organizationId);
|
|
632
|
+
const url = isRegionEdition()
|
|
633
|
+
? `/oapi/v1/projex/projects/${projectId}/workitemTypes/${workItemTypeId}/workflows`
|
|
634
|
+
: `/oapi/v1/projex/organizations/${finalOrgId}/projects/${projectId}/workitemTypes/${workItemTypeId}/workflows`;
|
|
635
|
+
const response = await yunxiaoRequest(url, {
|
|
636
|
+
method: "GET",
|
|
637
|
+
});
|
|
638
|
+
// 如果响应中包含result字段,则返回result中的数据
|
|
639
|
+
if (response && typeof response === 'object' && 'result' in response) {
|
|
640
|
+
return response.result;
|
|
641
|
+
}
|
|
642
|
+
// 否则直接返回响应
|
|
643
|
+
return response;
|
|
644
|
+
}
|
|
645
|
+
/**
|
|
646
|
+
* 列出工作项评论
|
|
647
|
+
* @param organizationId 企业ID
|
|
648
|
+
* @param workItemId 工作项ID
|
|
649
|
+
* @param page 页码
|
|
650
|
+
* @param perPage 每页条数
|
|
651
|
+
* @returns 工作项评论列表
|
|
652
|
+
*/
|
|
653
|
+
export async function listWorkItemCommentsFunc(organizationId, workItemId, page = 1, perPage = 20) {
|
|
654
|
+
const finalOrgId = await resolveOrganizationId(organizationId);
|
|
655
|
+
const url = isRegionEdition()
|
|
656
|
+
? `/oapi/v1/projex/workitems/${workItemId}/comments?page=${page}&perPage=${perPage}`
|
|
657
|
+
: `/oapi/v1/projex/organizations/${finalOrgId}/workitems/${workItemId}/comments?page=${page}&perPage=${perPage}`;
|
|
658
|
+
const response = await yunxiaoRequest(url, {
|
|
659
|
+
method: "GET",
|
|
660
|
+
});
|
|
661
|
+
// 确保返回的是数组格式
|
|
662
|
+
if (Array.isArray(response)) {
|
|
663
|
+
return response;
|
|
664
|
+
}
|
|
665
|
+
// 如果响应中包含result字段,则返回result中的数据
|
|
666
|
+
if (response && typeof response === 'object' && 'result' in response && Array.isArray(response.result)) {
|
|
667
|
+
return response.result;
|
|
668
|
+
}
|
|
669
|
+
// 其他情况返回空数组
|
|
670
|
+
return [];
|
|
671
|
+
}
|
|
672
|
+
/**
|
|
673
|
+
* 创建工作项评论
|
|
674
|
+
* @param organizationId 企业ID
|
|
675
|
+
* @param workItemId 工作项ID
|
|
676
|
+
* @param content 评论内容
|
|
677
|
+
* @returns 创建的评论信息
|
|
678
|
+
*/
|
|
679
|
+
export async function createWorkItemCommentFunc(organizationId, workItemId, content) {
|
|
680
|
+
const finalOrgId = await resolveOrganizationId(organizationId);
|
|
681
|
+
const url = isRegionEdition()
|
|
682
|
+
? `/oapi/v1/projex/workitems/${workItemId}/comments`
|
|
683
|
+
: `/oapi/v1/projex/organizations/${finalOrgId}/workitems/${workItemId}/comments`;
|
|
684
|
+
const payload = {
|
|
685
|
+
content: content
|
|
686
|
+
};
|
|
687
|
+
const response = await yunxiaoRequest(url, {
|
|
688
|
+
method: "POST",
|
|
689
|
+
body: payload,
|
|
690
|
+
});
|
|
691
|
+
// 如果响应中包含result字段,则返回result中的数据
|
|
692
|
+
if (response && typeof response === 'object' && 'result' in response) {
|
|
693
|
+
return response.result;
|
|
694
|
+
}
|
|
695
|
+
// 否则直接返回响应
|
|
696
|
+
return response;
|
|
697
|
+
}
|
|
698
|
+
/**
|
|
699
|
+
* 获取工作项动态列表
|
|
700
|
+
* @param organizationId 企业ID
|
|
701
|
+
* @param workItemId 工作项唯一标识
|
|
702
|
+
* @returns 工作项动态列表
|
|
703
|
+
*/
|
|
704
|
+
export async function listWorkitemActivitiesFunc(organizationId, workItemId) {
|
|
705
|
+
const finalOrgId = await resolveOrganizationId(organizationId);
|
|
706
|
+
const url = isRegionEdition()
|
|
707
|
+
? `/oapi/v1/projex/workitems/${workItemId}/activities`
|
|
708
|
+
: `/oapi/v1/projex/organizations/${finalOrgId}/workitems/${workItemId}/activities`;
|
|
709
|
+
const response = await yunxiaoRequest(url, {
|
|
710
|
+
method: "GET",
|
|
711
|
+
});
|
|
712
|
+
// 确保返回的是数组格式
|
|
713
|
+
if (Array.isArray(response)) {
|
|
714
|
+
return response.map(activity => WorkItemActivitySchema.parse(activity));
|
|
715
|
+
}
|
|
716
|
+
// 其他情况返回空数组
|
|
717
|
+
return [];
|
|
718
|
+
}
|
|
719
|
+
/**
|
|
720
|
+
* 获取工作项附件列表
|
|
721
|
+
* @param organizationId 企业ID
|
|
722
|
+
* @param workItemId 工作项唯一标识
|
|
723
|
+
* @returns 工作项附件列表
|
|
724
|
+
*/
|
|
725
|
+
export async function listWorkitemAttachmentsFunc(organizationId, workItemId) {
|
|
726
|
+
const finalOrgId = await resolveOrganizationId(organizationId);
|
|
727
|
+
const url = isRegionEdition()
|
|
728
|
+
? `/oapi/v1/projex/workitems/${workItemId}/attachments`
|
|
729
|
+
: `/oapi/v1/projex/organizations/${finalOrgId}/workitems/${workItemId}/attachments`;
|
|
730
|
+
const response = await yunxiaoRequest(url, {
|
|
731
|
+
method: "GET",
|
|
732
|
+
});
|
|
733
|
+
// 确保返回的是数组格式
|
|
734
|
+
if (Array.isArray(response)) {
|
|
735
|
+
return response.map(attachment => WorkItemAttachmentSchema.parse(attachment));
|
|
736
|
+
}
|
|
737
|
+
// 其他情况返回空数组
|
|
738
|
+
return [];
|
|
739
|
+
}
|
|
740
|
+
/**
|
|
741
|
+
* 获取工作项文件信息
|
|
742
|
+
* @param organizationId 企业ID
|
|
743
|
+
* @param workItemId 工作项唯一标识
|
|
744
|
+
* @param fileId 文件唯一标识
|
|
745
|
+
* @returns 工作项文件信息
|
|
746
|
+
*/
|
|
747
|
+
export async function getWorkitemFileFunc(organizationId, workItemId, fileId) {
|
|
748
|
+
const finalOrgId = await resolveOrganizationId(organizationId);
|
|
749
|
+
const url = isRegionEdition()
|
|
750
|
+
? `/oapi/v1/projex/workitems/${workItemId}/files/${fileId}`
|
|
751
|
+
: `/oapi/v1/projex/organizations/${finalOrgId}/workitems/${workItemId}/files/${fileId}`;
|
|
752
|
+
const response = await yunxiaoRequest(url, {
|
|
753
|
+
method: "GET",
|
|
754
|
+
});
|
|
755
|
+
return WorkItemFileSchema.parse(response);
|
|
756
|
+
}
|
|
757
|
+
/**
|
|
758
|
+
* 获取工作项关联项列表
|
|
759
|
+
* @param organizationId 企业ID
|
|
760
|
+
* @param workItemId 工作项唯一标识
|
|
761
|
+
* @param relationType 关联类型
|
|
762
|
+
* @returns 工作项关联项列表
|
|
763
|
+
*/
|
|
764
|
+
export async function listWorkitemRelationRecordsFunc(organizationId, workItemId, relationType) {
|
|
765
|
+
const finalOrgId = await resolveOrganizationId(organizationId);
|
|
766
|
+
const url = isRegionEdition()
|
|
767
|
+
? `/oapi/v1/projex/workitems/${workItemId}/relationRecords?relationType=${encodeURIComponent(relationType)}`
|
|
768
|
+
: `/oapi/v1/projex/organizations/${finalOrgId}/workitems/${workItemId}/relationRecords?relationType=${encodeURIComponent(relationType)}`;
|
|
769
|
+
const response = await yunxiaoRequest(url, {
|
|
770
|
+
method: "GET",
|
|
771
|
+
});
|
|
772
|
+
// 确保返回的是数组格式
|
|
773
|
+
if (Array.isArray(response)) {
|
|
774
|
+
return response.map(relation => WorkItemRelationRecordSchema.parse(relation));
|
|
775
|
+
}
|
|
776
|
+
// 其他情况返回空数组
|
|
777
|
+
return [];
|
|
778
|
+
}
|
|
779
|
+
/**
|
|
780
|
+
* 创建工作项关联项
|
|
781
|
+
* @param organizationId 企业ID
|
|
782
|
+
* @param workItemId 工作项唯一标识
|
|
783
|
+
* @param relationType 关联类型
|
|
784
|
+
* @param relatedWorkitemId 关联的工作项ID
|
|
785
|
+
* @returns 创建的关联记录ID
|
|
786
|
+
*/
|
|
787
|
+
export async function createWorkitemRelationRecordFunc(organizationId, workItemId, relationType, relatedWorkitemId) {
|
|
788
|
+
const finalOrgId = await resolveOrganizationId(organizationId);
|
|
789
|
+
const url = isRegionEdition()
|
|
790
|
+
? `/oapi/v1/projex/workitems/${workItemId}/relationRecords`
|
|
791
|
+
: `/oapi/v1/projex/organizations/${finalOrgId}/workitems/${workItemId}/relationRecords`;
|
|
792
|
+
const payload = {
|
|
793
|
+
relationType: relationType,
|
|
794
|
+
workItemId: relatedWorkitemId
|
|
795
|
+
};
|
|
796
|
+
const response = await yunxiaoRequest(url, {
|
|
797
|
+
method: "POST",
|
|
798
|
+
body: payload,
|
|
799
|
+
});
|
|
800
|
+
// 返回创建的关联记录ID
|
|
801
|
+
if (response && typeof response === 'object' && 'id' in response) {
|
|
802
|
+
return { id: String(response.id) };
|
|
803
|
+
}
|
|
804
|
+
return { id: "" };
|
|
805
|
+
}
|
|
806
|
+
/**
|
|
807
|
+
* 删除工作项关联项
|
|
808
|
+
* @param organizationId 企业ID
|
|
809
|
+
* @param workItemId 工作项唯一标识
|
|
810
|
+
* @param relationType 关联类型
|
|
811
|
+
* @param relatedWorkitemId 要删除关联的工作项ID
|
|
812
|
+
*/
|
|
813
|
+
export async function deleteWorkitemRelationRecordFunc(organizationId, workItemId, relationType, relatedWorkitemId) {
|
|
814
|
+
const finalOrgId = await resolveOrganizationId(organizationId);
|
|
815
|
+
const url = isRegionEdition()
|
|
816
|
+
? `/oapi/v1/projex/workitems/${workItemId}/relationRecords`
|
|
817
|
+
: `/oapi/v1/projex/organizations/${finalOrgId}/workitems/${workItemId}/relationRecords`;
|
|
818
|
+
const payload = {
|
|
819
|
+
relationType: relationType,
|
|
820
|
+
workItemId: relatedWorkitemId
|
|
821
|
+
};
|
|
822
|
+
await yunxiaoRequest(url, {
|
|
823
|
+
method: "DELETE",
|
|
824
|
+
body: payload,
|
|
825
|
+
});
|
|
826
|
+
}
|