ai-engineering-init 1.10.0 → 1.11.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.
Files changed (27) hide show
  1. package/.claude/agents/code-scanner.md +145 -0
  2. package/.claude/hooks/skill-forced-eval.js +2 -0
  3. package/.claude/skills/analyze-requirements/SKILL.md +112 -0
  4. package/.claude/skills/fix-bug/SKILL.md +166 -0
  5. package/.claude/skills/yunxiao-task-management/SKILL.md +170 -258
  6. package/.claude/skills/yunxiao-task-management/templates//346/217/220/346/265/213/345/215/225/346/250/241/346/235/277.html +17 -0
  7. package/.codex/skills/analyze-requirements/SKILL.md +112 -0
  8. package/.codex/skills/dev/SKILL.md +131 -476
  9. package/.codex/skills/fix-bug/SKILL.md +166 -0
  10. package/.codex/skills/next/SKILL.md +42 -186
  11. package/.codex/skills/progress/SKILL.md +76 -147
  12. package/.codex/skills/yunxiao-task-management/SKILL.md +170 -258
  13. package/.codex/skills/yunxiao-task-management/templates//346/217/220/346/265/213/345/215/225/346/250/241/346/235/277.html +17 -0
  14. package/.cursor/agents/code-scanner.md +145 -0
  15. package/.cursor/hooks/cursor-skill-eval.js +10 -0
  16. package/.cursor/rules/skill-activation.mdc +7 -0
  17. package/.cursor/skills/analyze-requirements/SKILL.md +112 -0
  18. package/.cursor/skills/fix-bug/SKILL.md +166 -0
  19. package/.cursor/skills/yunxiao-task-management/SKILL.md +170 -258
  20. package/.cursor/skills/yunxiao-task-management/templates//346/217/220/346/265/213/345/215/225/346/250/241/346/235/277.html +17 -0
  21. package/AGENTS.md +44 -21
  22. package/README.md +1 -1
  23. package/package.json +1 -1
  24. package/src/skills/analyze-requirements/SKILL.md +112 -0
  25. package/src/skills/fix-bug/SKILL.md +166 -0
  26. package/src/skills/yunxiao-task-management/SKILL.md +170 -258
  27. package/src/skills/yunxiao-task-management/templates//346/217/220/346/265/213/345/215/225/346/250/241/346/235/277.html +17 -0
@@ -3,8 +3,16 @@ name: yunxiao-task-management
3
3
  description: |
4
4
  阿里云云效(Yunxiao)项目协作任务管理技能。通过云效 Open API 直接操作工作项:查询任务、修改状态、编辑描述、添加评论、记录工时、创建子任务、搜索项目等。
5
5
  支持完整开发工作流:读取任务 → 查找父需求 → 读取需求详情 → 创建提测单 → 编辑提测单 → 转给测试。
6
- 当用户提到"云效"、"任务管理"、"工作项"、"修改状态"、"添加评论"、"记录工时"、"创建子任务"、"查询任务"、"项目任务"、"提测"、"提测单"、"父需求"、"需求详情"、"SARW"、"EZML"、"IXXP"或其他云效项目编号时使用此技能。
7
- 即使用户只是简单说"把XXX改为开发完成"、"帮我查一下我的待处理任务"、"读取EZML-1878"、"创建提测单",也应该触发此技能。
6
+ 支持智能提测单完善:自动从 git 获取分支/提交信息,从 pom.xml 读取版本号,查找用户任务并填充提测单模板。
7
+
8
+ 触发场景:
9
+ - 查询/修改云效任务状态
10
+ - 完善提测单内容(自动收集 git 信息)
11
+ - 创建提测单子任务
12
+ - 查看个人待处理任务
13
+ - 读取需求详情
14
+
15
+ 触发词:云效、任务管理、工作项、修改状态、提测、提测单、完善提测单、SARW、EZML、IXXP、云效任务
8
16
  ---
9
17
 
10
18
  # 云效任务管理 Skill
@@ -21,7 +29,16 @@ YUNXIAO_ACCESS_TOKEN: 个人访问令牌
21
29
  **已知用户信息**:
22
30
  - 组织 ID: `61dbcd725356b19beeb1dc03`
23
31
  - 用户: 徐嘉骏(ID: `66286d4b06679a65daed4d28`)
24
- - 常用项目: SARW(`4574cb1c653fe873335b6c4716`)、EZML(`6f99a4e627fa88d6f8cb541e6c`)
32
+
33
+ **已知项目缓存**(避免全量扫描):
34
+
35
+ | customCode | 项目名称 | 项目 ID |
36
+ |------------|---------|---------|
37
+ | SARW | - | `4574cb1c653fe873335b6c4716` |
38
+ | EZML | - | `6f99a4e627fa88d6f8cb541e6c` |
39
+ | IXXP | 广州小鹏汽车 | `d93e6ddf18c83254d0b8f27e7d` |
40
+
41
+ > 新发现的项目应追加到此表。搜索项目时**优先查缓存**,命中则跳过 API 搜索。
25
42
 
26
43
  ## API 基础信息
27
44
 
@@ -64,12 +81,14 @@ Body: {} 或 { keyword: "关键词" }
64
81
 
65
82
  返回数组,每个项目包含 `id`、`name`、`customCode`(如 SARW、IXXP)。
66
83
 
67
- **按成员过滤项目**(仅获取用户参与的项目,大幅减少搜索范围):
68
- ```json
69
- {
70
- "conditions": "{\"conditionGroups\":[[{\"className\":\"user\",\"fieldIdentifier\":\"members\",\"format\":\"list\",\"operator\":\"CONTAINS\",\"toValue\":null,\"value\":[\"用户ID\"]}]]}"
71
- }
72
- ```
84
+ **重要**:`keyword` 搜索按项目名称模糊匹配,**不支持按 customCode 搜索**。要找 customCode,需全量分页扫描匹配。
85
+
86
+ **优先使用缓存**:先查"已知项目缓存"表,命中则直接用 ID,无需调 API。
87
+
88
+ **按项目名称查找**(用户说"小鹏汽车"时):
89
+ 1. 先查缓存表的项目名称列
90
+ 2. 未命中则用 `keyword` 搜索:`{ keyword: "小鹏汽车" }`
91
+ 3. 仍未找到则全量分页扫描(perPage=100,逐页匹配 name)
73
92
 
74
93
  ### 2. 搜索工作项
75
94
 
@@ -92,10 +111,11 @@ Body: {
92
111
  - `subject`: 标题
93
112
  - `status`: `{ name, id }` — 状态名和状态 ID
94
113
  - `assignedTo`: `{ name, id }` — 负责人
114
+ - `parentId`: 父工作项 ID
95
115
 
96
- #### conditions 高级过滤(重要!)
116
+ #### conditions 高级过滤
97
117
 
98
- 搜索工作项支持 `conditions` 参数进行服务端过滤,避免全量拉取再本地筛选。`conditions` 是 **JSON 字符串**,格式:
118
+ `conditions` 是 **JSON 字符串**,格式:
99
119
 
100
120
  ```json
101
121
  {
@@ -110,34 +130,12 @@ Body: {
110
130
  { "className": "user", "fieldIdentifier": "assignedTo", "format": "list", "operator": "CONTAINS", "toValue": null, "value": ["用户ID"] }
111
131
  ```
112
132
 
113
- **排除已完成/已取消状态**(只查未完成的任务,最实用):
133
+ **排除已完成/已取消状态**:
114
134
  ```json
115
135
  { "className": "status", "fieldIdentifier": "status", "format": "list", "operator": "NOT_CONTAINS", "toValue": null, "value": ["100014", "141230"] }
116
136
  ```
117
137
 
118
- > `statusStage` 字段在 API 返回中为 undefined,不可用于过滤。用 `status` + `NOT_CONTAINS` 排除特定状态 ID 代替。
119
-
120
- **按创建人过滤**:
121
- ```json
122
- { "className": "user", "fieldIdentifier": "creator", "format": "list", "operator": "CONTAINS", "toValue": null, "value": ["用户ID"] }
123
- ```
124
-
125
- **按优先级过滤**:
126
- ```json
127
- { "className": "option", "fieldIdentifier": "priority", "format": "list", "operator": "CONTAINS", "toValue": null, "value": ["优先级值"] }
128
- ```
129
-
130
- **组合多个条件**(放在同一个数组内为 AND 关系):
131
- ```json
132
- {
133
- "conditionGroups": [[
134
- { "className": "user", "fieldIdentifier": "assignedTo", "format": "list", "operator": "CONTAINS", "toValue": null, "value": ["用户ID"] },
135
- { "className": "statusStage", "fieldIdentifier": "statusStage", "format": "list", "operator": "CONTAINS", "toValue": null, "value": ["TODO", "DOING"] }
136
- ]]
137
- }
138
- ```
139
-
140
- > **注意**:`conditions` 传给 API 时必须是 **JSON 字符串**(`JSON.stringify(obj)`),不是对象。
138
+ > `conditions` 传给 API 时必须是 **JSON 字符串**(`JSON.stringify(obj)`),不是对象。
141
139
 
142
140
  ### 3. 获取工作项详情
143
141
 
@@ -151,12 +149,9 @@ GET /oapi/v1/projex/organizations/{orgId}/workitems/{workitemId}
151
149
  PUT /oapi/v1/projex/organizations/{orgId}/workitems/{workitemId}
152
150
  ```
153
151
 
154
- **修改状态**:
155
- ```json
156
- { "status": "100011" }
157
- ```
152
+ 可组合多个字段一次更新:`{ "description": "...", "status": "100011", "subject": "..." }`。成功返回 **HTTP 204**。
158
153
 
159
- 常见状态 ID(从项目中的工作项动态获取,以下为参考值):
154
+ 常见状态 ID(参考值,不同项目可能不同):
160
155
 
161
156
  | 状态名 | 状态 ID |
162
157
  |--------|---------|
@@ -166,25 +161,6 @@ PUT /oapi/v1/projex/organizations/{orgId}/workitems/{workitemId}
166
161
  | 已完成 | 100014 |
167
162
  | 已取消 | 141230 |
168
163
 
169
- > 不同项目的状态 ID 可能不同!先从该项目的工作项中收集已有状态,再使用对应 ID。
170
-
171
- **修改描述**:
172
- ```json
173
- { "description": "新的描述内容" }
174
- ```
175
-
176
- **修改标题**:
177
- ```json
178
- { "subject": "新标题" }
179
- ```
180
-
181
- **修改负责人**:
182
- ```json
183
- { "assignedTo": "用户ID" }
184
- ```
185
-
186
- 可组合多个字段一次更新。成功返回 **HTTP 204**(无 body)。
187
-
188
164
  ### 5. 添加评论
189
165
 
190
166
  ```
@@ -192,13 +168,6 @@ POST /oapi/v1/projex/organizations/{orgId}/workitems/{workitemId}/comments
192
168
  Body: { "content": "评论内容" }
193
169
  ```
194
170
 
195
- 返回 `{ id: "评论ID" }`。
196
-
197
- **获取评论列表**:
198
- ```
199
- GET /oapi/v1/projex/organizations/{orgId}/workitems/{workitemId}/comments?page=1&perPage=20
200
- ```
201
-
202
171
  ### 6. 创建子任务
203
172
 
204
173
  ```
@@ -208,13 +177,11 @@ Body: {
208
177
  "subject": "子任务标题",
209
178
  "workitemTypeId": "任务类型ID",
210
179
  "assignedTo": "用户ID", // 必需!
211
- "parentId": "父工作项ID", // 关键:建立父子关系
180
+ "parentId": "父工作项ID",
212
181
  "description": "描述(可选)"
213
182
  }
214
183
  ```
215
184
 
216
- > `assignedTo` 虽然看似可选,但 API 强制要求,缺少会报错。
217
-
218
185
  **获取任务类型 ID**:
219
186
  ```
220
187
  GET /oapi/v1/projex/organizations/{orgId}/projects/{projectId}/workitemTypes?category=Task
@@ -222,264 +189,209 @@ GET /oapi/v1/projex/organizations/{orgId}/projects/{projectId}/workitemTypes?cat
222
189
 
223
190
  ### 7. 工时管理
224
191
 
225
- #### 预估工时
192
+ **预估工时**:`POST .../workitems/{id}/estimatedEfforts` Body: `{ "spentTime": 480, "owner": "用户ID" }`
193
+ **实际工时**:`POST .../workitems/{id}/effortRecords` Body: `{ "actualTime": 240, "gmtStart": ts, "gmtEnd": ts }`
226
194
 
227
- **查看**:
228
- ```
229
- GET /oapi/v1/projex/organizations/{orgId}/workitems/{workitemId}/estimatedEfforts
230
- ```
195
+ ## 不支持的操作
231
196
 
232
- **创建**(单位:分钟):
233
- ```
234
- POST /oapi/v1/projex/organizations/{orgId}/workitems/{workitemId}/estimatedEfforts
235
- Body: {
236
- "spentTime": 480, // 480分钟 = 8小时
237
- "description": "预估工时描述",
238
- "owner": "用户ID"
239
- }
240
- ```
197
+ | 操作 | 原因 |
198
+ |------|------|
199
+ | 添加附件 | API 不支持(504 超时/415 错误) |
200
+ | 获取用户信息 | 需要「用户信息(读取)」权限,Token 不支持 |
241
201
 
242
- #### 实际工时
202
+ ---
243
203
 
244
- **查看**:
245
- ```
246
- GET /oapi/v1/projex/organizations/{orgId}/workitems/{workitemId}/effortRecords
247
- ```
204
+ ## 提测单完善流程(核心场景)
205
+
206
+ 当用户说"完善提测单"、"填写提测单"、"帮我提测"时触发。用户可能**不提供任务 ID**,只说项目名或需求描述。
207
+
208
+ ### 流程概览
248
209
 
249
- **创建**:
250
210
  ```
251
- POST /oapi/v1/projex/organizations/{orgId}/workitems/{workitemId}/effortRecords
252
- Body: {
253
- "actualTime": 240, // 240分钟 = 4小时
254
- "description": "实际工时描述",
255
- "gmtStart": 1772800000000, // 开始时间(毫秒时间戳)
256
- "gmtEnd": 1772803000000 // 结束时间(毫秒时间戳)
257
- }
211
+ 1. 定位任务 → 2. 收集 git 信息 → 3. 读取版本号 → 4. 加载模板 → 5. 填充并更新描述
258
212
  ```
259
213
 
260
- ### 8. 跨项目搜索用户任务(优化策略)
214
+ ### Step 1: 定位任务
261
215
 
262
- 云效 API 的 `workitems:search` 必须指定 `spaceId`,不支持全局搜索。推荐优化流程:
216
+ **场景 A:用户提供了任务编号**(如 IXXP-122)
217
+ - 从编号前缀提取 customCode(IXXP)
218
+ - 查缓存表 → 命中则直接用项目 ID
219
+ - 未命中则全量分页扫描匹配 customCode
220
+ - 搜索项目 Task,找到 serialNumber 匹配的工作项
263
221
 
264
- **第一步:缩小项目范围** — 用 `members` conditions 只获取用户参与的项目:
265
- ```javascript
266
- const memberConditions = JSON.stringify({
267
- conditionGroups: [[{
268
- className: "user", fieldIdentifier: "members",
269
- format: "list", operator: "CONTAINS", toValue: null, value: [USER_ID]
270
- }]]
271
- });
272
- // POST projects:search with body: { conditions: memberConditions }
273
- ```
222
+ **场景 B:用户只说项目名称**(如"小鹏汽车")
223
+ - 用项目名称匹配缓存或 keyword 搜索
224
+ - 找到项目后,用 `assignedTo` conditions 过滤当前用户的任务
225
+ - 展示任务列表,让用户选择或自动匹配标题含"提测"的任务
226
+
227
+ **场景 C:用户只说"我的提测单"**
228
+ - 用已知项目缓存逐个搜索当前用户的任务
229
+ - 筛选标题含"提测"的任务
274
230
 
275
- **第二步:服务端过滤任务** — 用 `assignedTo` + `status NOT_CONTAINS` 只返回自己未完成的任务:
276
231
  ```javascript
277
- const taskConditions = JSON.stringify({
232
+ // 查找用户在指定项目中的任务
233
+ const conditions = JSON.stringify({
278
234
  conditionGroups: [[
279
235
  { className: "user", fieldIdentifier: "assignedTo", format: "list", operator: "CONTAINS", toValue: null, value: [USER_ID] },
280
236
  { className: "status", fieldIdentifier: "status", format: "list", operator: "NOT_CONTAINS", toValue: null, value: ["100014", "141230"] }
281
237
  ]]
282
238
  });
283
- // POST workitems:search with body: { spaceId, category: "Task", conditions: taskConditions, ... }
239
+ // POST workitems:search with { spaceId, category: "Task", conditions, page: 1, perPage: 200 }
284
240
  ```
285
241
 
286
- **第三步:并发批处理**(15 个项目一批):
287
- ```javascript
288
- const batchSize = 15;
289
- for (let i = 0; i < projects.length; i += batchSize) {
290
- const batch = projects.slice(i, i + batchSize);
291
- const results = await Promise.all(batch.map(p => searchWorkitems(p.id, taskConditions)));
292
- myTasks.push(...results.flat());
293
- }
294
- ```
242
+ ### Step 2: 收集 git 信息
295
243
 
296
- > 通过 assignedTo + status NOT_CONTAINS 服务端过滤,每个项目只返回该用户的未完成任务,大幅减少数据量。
297
- > 注意:`members` 项目过滤在实测中未生效(仍返回全量项目),建议直接扫描所有项目但用 batchSize=20 并发处理。
244
+ 从当前工作目录的 git 仓库自动收集:
298
245
 
299
- ## 不支持的操作
246
+ ```bash
247
+ # 当前分支名(后端分支)
248
+ git branch --show-current
300
249
 
301
- | 操作 | 原因 |
302
- |------|------|
303
- | 添加附件 | API 不支持(504 超时/415 不支持的类型),MCP 源码中无附件相关实现 |
304
- | 获取用户信息 | `/oapi/v1/platform/user` 需要「用户信息(读取)」权限,项目 Token 不支持 |
250
+ # 最近的相关提交(用户可能指定 commit hash)
251
+ git log {commitHash} -1 --stat
305
252
 
306
- ## 开发工作流(核心流程)
253
+ # 提交所在分支
254
+ git branch --contains {commitHash}
255
+ ```
307
256
 
308
- 用户日常开发工作流,会频繁使用。当用户说"读取任务"、"查看需求"、"创建提测单"、"提测"时触发。
257
+ **信息映射**:
258
+ - 后端分支 = `git branch --show-current` 或 `git branch --contains {hash}`
259
+ - 需求描述 = 用户提供 或 commit message
260
+ - 前端分支 / h5分支 = 用户提供,默认留空或 "无"
309
261
 
310
- ### 流程概览
262
+ ### Step 3: 读取版本号
311
263
 
312
- ```
313
- 读取任务(Task) → 查找父需求(Req) → 读取需求详情 → 开发 → 创建提测单 → 编辑提测单 → 转给测试
264
+ 从定制仓库的 pom.xml 读取 `parent.version`:
265
+
266
+ ```bash
267
+ # 读取 pom.xml 的 parent version
268
+ grep -A1 '<artifactId>core-dependencies</artifactId>' pom.xml | grep '<version>'
314
269
  ```
315
270
 
316
- ### Step 1: 读取任务并找到父需求
271
+ ### Step 4: 加载提测单模板
317
272
 
318
- 通过 `serialNumber`(如 EZML-1878)找到任务,任务的 `parentId` 字段指向父需求。
273
+ 模板文件位置:`{skill目录}/templates/提测单模板.html`
319
274
 
320
- ```javascript
321
- // 1. 搜索项目中的任务,找到目标任务
322
- // 项目编号 = serialNumber 的前缀(如 EZML-1878 → EZML)
323
- // 先搜索项目列表找到 customCode 匹配的项目
275
+ 读取模板文件,替换占位符:
276
+ - `{项目名称}` → 项目中文名(如"小鹏汽车总部")
277
+ - `{版本号}` pom.xml parent version
278
+ - `{后端分支}` git 分支名
279
+ - `{前端分支}` → 用户提供或 "无"
280
+ - `{h5分支}` → 用户提供或 "无"
281
+ - `{需求描述}` → 需求内容
282
+ - `{开发者ID}` → 当前用户 ID
283
+ - `{开发者名}` → 当前用户名
284
+ - `{自测说明}` → "已自测通过" 或用户提供
285
+ - `{测试用例}` → 根据需求自动生成或用户提供
286
+ - `{提测人ID}` → 当前用户 ID
287
+ - `{提测人}` → 当前用户名
288
+ - `{验收人}` → 用户指定(先在项目成员中搜索 ID,未找到则纯文本)
324
289
 
325
- // 2. 从任务的 parentId 获取父需求
326
- const task = items.find(i => i.serialNumber === "EZML-1878");
327
- const parentDetail = await yunxiaoReq(`/oapi/v1/projex/organizations/${ORG}/workitems/${task.parentId}`);
328
- // parentDetail.categoryId === "Req"
329
- // parentDetail.serialNumber === "EZML-1877"
330
- ```
290
+ **@提及语法**:`<span data-type="mention" data-id="{用户ID}">@{用户名}</span>`
331
291
 
332
- **关键字段**:
333
- - `parentId`: 父工作项 ID(Task → Req 的关联)
334
- - `categoryId`: 工作项类别(`Task` / `Req` / `Bug` / `Risk`)
335
- - `workitemType`: `{ name, id }` — 如 `{ name: "产品类需求", id: "9uy29901re573f561d69jn40" }`
336
- - `space`: `{ name, id }` — 所属项目
292
+ **搜索用户 ID**:从项目工作项的 `assignedTo` 和 `creator` 字段收集所有成员,匹配姓名。
337
293
 
338
- ### Step 2: 读取父需求详情
294
+ ### Step 5: 更新工作项描述
339
295
 
340
296
  ```javascript
341
- const parentDetail = await yunxiaoReq(`/oapi/v1/projex/organizations/${ORG}/workitems/${parentId}`);
342
- // description 是 JSON 字符串: { "htmlValue": "<article>...</article>" }
343
- let desc = parentDetail.description;
344
- try { desc = JSON.parse(desc).htmlValue; } catch {}
345
- // 去除 HTML 标签提取纯文本
346
- const text = desc.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
297
+ await yunxiaoReq(`/oapi/v1/projex/organizations/${ORG}/workitems/${workitemId}`, {
298
+ method: "PUT",
299
+ body: { description: filledTemplate }
300
+ });
301
+ // 成功返回 204
347
302
  ```
348
303
 
349
- 需求描述是 HTML 格式,包含:需求背景、原型链接、具体需求列表等。
304
+ ### 完整示例
350
305
 
351
- ### Step 3: 查看同级子任务
306
+ ```
307
+ 用户:完善 IXXP-122 提测单,提交是 45bdb2a 装修优化,前端分支 master,验收人卢佳南
308
+ ```
352
309
 
353
- 查看父需求下的所有子任务(了解分工):
310
+ 执行步骤:
311
+ 1. IXXP → 查缓存命中 → 项目 ID `d93e6ddf18c83254d0b8f27e7d`
312
+ 2. 搜索 Task 找到 IXXP-122
313
+ 3. `git log 45bdb2a -1 --stat` → 获取提交详情
314
+ 4. `git branch --contains 45bdb2a` → 后端分支
315
+ 5. 读取 pom.xml → 版本号
316
+ 6. 加载模板,替换占位符
317
+ 7. 在项目成员中搜索"卢佳南"(未找到则纯文本写入)
318
+ 8. PUT 更新描述 → 204 成功
354
319
 
355
- ```javascript
356
- // 搜索该项目所有 Task,筛选 parentId 匹配的
357
- const allTasks = await searchWorkitems(projectId, "Task");
358
- const siblings = allTasks.filter(t => t.parentId === parentReqId);
359
- // 展示: serialNumber, subject, status.name, assignedTo.name
360
320
  ```
321
+ 用户:帮我完善小鹏汽车项目的提测单
322
+ ```
323
+
324
+ 执行步骤:
325
+ 1. "小鹏汽车" → 查缓存匹配 IXXP
326
+ 2. 用 assignedTo 过滤当前用户的未完成任务
327
+ 3. 筛选标题含"提测"的任务 → 展示列表让用户确认
328
+ 4. 从 git 自动收集分支、提交信息
329
+ 5. 读取版本号,加载模板,填充更新
330
+
331
+ ---
332
+
333
+ ## 开发工作流(创建提测单)
361
334
 
362
- ### Step 4: 创建提测单
335
+ 当需要**新建**提测单(而非完善已有的),用户说"创建提测单"时触发。
363
336
 
364
- 提测单是父需求(Req)下的一个 **Task 子任务**,指定给测试人员。
337
+ ### 流程
338
+
339
+ ```
340
+ 读取任务(Task) → 查找父需求(Req) → 创建提测单子任务 → 填充描述
341
+ ```
342
+
343
+ ### Step 1: 读取任务并找到父需求
365
344
 
366
345
  ```javascript
367
- // 获取 Task 类型 ID
368
- const types = await yunxiaoReq(`/oapi/v1/projex/organizations/${ORG}/projects/${projectId}/workitemTypes?category=Task`);
369
- const taskTypeId = types[0].id; // 通常只有一个 Task 类型
346
+ const task = items.find(i => i.serialNumber === "EZML-1878");
347
+ const parentDetail = await yunxiaoReq(`/oapi/v1/projex/organizations/${ORG}/workitems/${task.parentId}`);
348
+ ```
349
+
350
+ ### Step 2: 创建提测单子任务
370
351
 
371
- // 创建提测单(本质是创建 Task,parentId 指向父需求)
352
+ ```javascript
353
+ const types = await yunxiaoReq(`/oapi/v1/projex/organizations/${ORG}/projects/${projectId}/workitemTypes?category=Task`);
372
354
  await yunxiaoReq(`/oapi/v1/projex/organizations/${ORG}/workitems`, {
373
355
  method: "POST",
374
356
  body: {
375
357
  spaceId: projectId,
376
358
  subject: "提测-需求标题摘要",
377
- workitemTypeId: taskTypeId,
378
- assignedTo: "测试人员ID", // 必需!
379
- parentId: parentReqId, // 关键:挂在父需求下
380
- description: "提测单 HTML 内容" // 见下方模板
359
+ workitemTypeId: types[0].id,
360
+ assignedTo: "测试人员ID",
361
+ parentId: parentReqId,
362
+ description: filledTemplate // 同提测单完善流程 Step 4-5
381
363
  }
382
364
  });
383
365
  ```
384
366
 
385
- ### Step 5: 提测单描述模板
386
-
387
- 根据团队实际使用的提测单格式(参考 SARW-117、SARW-28 等),提测单描述包含以下结构:
388
-
389
- ```html
390
- <article class="4ever-article">
391
- <p>项目名称:{项目名称}</p>
392
- <p>项目版本:{版本号}</p>
393
- <p>项目分支:后端分支:{后端分支}&nbsp;&nbsp;前端分支:{前端分支}&nbsp;h5分支:{h5分支}</p>
394
- <p>项目需求:</p>
395
- <ol>
396
- <li><div>{需求1描述} <span data-type="mention" data-id="{开发者ID}">@{开发者名}</span></div></li>
397
- <li><div>{需求2描述} <span data-type="mention" data-id="{开发者ID}">@{开发者名}</span></div></li>
398
- </ol>
399
- <p>开发人员:</p>
400
- <ul style="list-style-type:none">
401
- <li><div><input type="checkbox" readonly="">&nbsp;<span data-type="mention" data-id="{ID}">@{名字}</span></div></li>
402
- </ul>
403
- <p>自测:</p>
404
- <p>{自测截图或说明}</p>
405
- <p>AI扫描:</p>
406
- <p>{AI扫描结果}</p>
407
- <p>提测人&amp;验收人</p>
408
- <p><span data-type="mention" data-id="{提测人ID}">@{提测人}</span>&nbsp;<span data-type="mention" data-id="{验收人ID}">@{验收人}</span></p>
409
- </article>
410
- ```
411
-
412
- **@提及语法**:`<span data-type="mention" data-id="{用户ID}" data-login="{用户ID}">@{用户名}</span>`
413
-
414
- ### Step 6: 转给测试
415
-
416
- 创建提测单后,将对应的开发任务状态改为"已提测"(如果项目有此状态)或"开发完成":
367
+ ### Step 3: 修改开发任务状态
417
368
 
418
369
  ```javascript
419
- // 修改任务状态为开发完成
420
370
  await yunxiaoReq(`/oapi/v1/projex/organizations/${ORG}/workitems/${taskId}`, {
421
371
  method: "PUT",
422
372
  body: { status: "100011" } // 开发完成
423
373
  });
424
374
  ```
425
375
 
426
- ### 常用工作项类型 ID(按项目动态获取)
427
-
428
- | 类别 | 类型名 | 用途 |
429
- |------|--------|------|
430
- | Task | 任务 | 开发任务、提测单 |
431
- | Req | 产品类需求 | 产品需求(父级) |
432
- | Req | 业务类需求 | 业务需求 |
433
- | Req | 技术类需求 | 技术需求 |
434
- | Bug | 缺陷 | Bug 单 |
435
-
436
- > 每个项目的类型 ID 不同,通过 `GET /workitemTypes?category={类别}` 获取。
437
-
438
376
  ---
439
377
 
440
- ## 操作流程示例
441
-
442
- ### 批量修改状态
443
-
444
- ```
445
- 用户:把 SARW-87、SARW-74 改为开发完成
446
- ```
378
+ ## 跨项目搜索用户任务
447
379
 
448
- 1. 搜索 SARW 项目中的工作项,找到 serialNumber 匹配的 ID
449
- 2. 从项目已有工作项中收集状态 → 找到"开发完成"的 ID
450
- 3. 对每个工作项调用 PUT 更新状态
451
- 4. 逐个 GET 验证更新结果
380
+ 云效 API 不支持全局搜索,需逐项目扫描。
452
381
 
453
- ### 查询个人任务
454
-
455
- ```
456
- 用户:查看我所有待处理的任务
457
- ```
458
-
459
- 1. `assignedTo` + `status NOT_CONTAINS` conditions 并发搜索每个项目(服务端过滤)
460
- 2. 并发批处理(batchSize=20)扫描所有项目
461
- 3. 汇总展示
462
-
463
- ### 读取任务需求并提测
464
-
465
- ```
466
- 用户:读取 EZML-1878,看看要做什么
467
- ```
468
-
469
- 1. 从 serialNumber 前缀找到项目(EZML → 搜索 customCode 匹配)
470
- 2. 搜索项目 Task,找到 EZML-1878
471
- 3. 通过 `parentId` 获取父需求(EZML-1877)详情
472
- 4. 解析父需求 HTML 描述,提取需求内容
473
- 5. 展示给用户
474
-
475
- ```
476
- 用户:创建提测单
382
+ ```javascript
383
+ // 全量扫描所有项目(perPage=100,逐页)
384
+ // 对每个项目用 assignedTo + status NOT_CONTAINS 过滤
385
+ // 并发批处理(batchSize=20)
386
+ const batchSize = 20;
387
+ for (let i = 0; i < projects.length; i += batchSize) {
388
+ const batch = projects.slice(i, i + batchSize);
389
+ const results = await Promise.all(batch.map(p => searchWorkitems(p.id)));
390
+ myTasks.push(...results.flat());
391
+ }
477
392
  ```
478
393
 
479
- 1. 获取项目 Task 类型 ID
480
- 2. 生成提测单描述(使用模板)
481
- 3. 创建子任务(parentId 指向父需求,assignedTo 指向测试人员)
482
- 4. 修改开发任务状态为"开发完成"
394
+ > `members` 项目过滤在实测中未生效,建议直接扫描所有项目。
483
395
 
484
396
  ## Token 权限要求
485
397
 
@@ -0,0 +1,17 @@
1
+ <article class="4ever-article">
2
+ <p>项目名称:{项目名称}</p>
3
+ <p>项目版本:{版本号}</p>
4
+ <p>项目分支:后端分支:{后端分支}&nbsp;&nbsp;前端分支:{前端分支}&nbsp;&nbsp;h5分支:{h5分支}</p>
5
+ <p>项目需求:</p>
6
+ <ol>
7
+ <li><div>{需求描述} <span data-type="mention" data-id="{开发者ID}">@{开发者名}</span></div></li>
8
+ </ol>
9
+ <p>自测:</p>
10
+ <p>{自测说明}</p>
11
+ <p></p>
12
+ <p>测试用例:</p>
13
+ <p>{测试用例}</p>
14
+ <p></p>
15
+ <p>提测人&amp;验收人</p>
16
+ <p><span data-type="mention" data-id="{提测人ID}">@{提测人}</span>&amp;{验收人}</p>
17
+ </article>