feat: 子流程支持循环执行以及HTTP回调 --story=130003551 --story=132632590 #711
feat: 子流程支持循环执行以及HTTP回调 --story=130003551 --story=132632590 #711Mianhuatang8 wants to merge 37 commits intoTencentBlueKing:masterfrom
Conversation
# Reviewed, transaction id: 74500
# Reviewed, transaction id: 74762
# Reviewed, transaction id: 74765
* feat: 循环变量协议修改 --story=130003551 # Reviewed, transaction id: 74970 * fix: 代码优化 --story=130003551 # Reviewed, transaction id: 74976 * fix: 代码优化 --story=130003551 # Reviewed, transaction id: 74987
# Reviewed, transaction id: 75024
# Reviewed, transaction id: 75036
* fix: 验收问题处理 --story=130003551 # Reviewed, transaction id: 75549 * fix: 验收问题处理 --story=130003551 # Reviewed, transaction id: 75555 * fix: 验收问题处理 --story=130003551 # Reviewed, transaction id: 75559
# Reviewed, transaction id: 76097
* fix: 验收问题处理 --story=130003551 # Reviewed, transaction id: 76446 * fix: 将循环次数选择抽出为公共组件 --story=130003551 # Reviewed, transaction id: 76615
# Reviewed, transaction id: 76758
# Reviewed, transaction id: 75033
# Reviewed, transaction id: 77424
# Reviewed, transaction id: 77438
# Reviewed, transaction id: 77443
# Reviewed, transaction id: 79251
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #711 +/- ##
=========================================
Coverage ? 81.38%
=========================================
Files ? 288
Lines ? 16689
Branches ? 0
=========================================
Hits ? 13582
Misses ? 3107
Partials ? 0 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Code Review 总结
本 PR 为子流程添加了循环执行和 HTTP 回调功能,涉及前端模板编辑、任务执行、变量选择等多个模块的大量改动。整体功能方向清晰,变量列表搜索/分组的 UI 重构和循环执行配置面板的实现较为完整。以下是主要发现:
⚠️ 逻辑问题
- VariableList 直接修改 prop 数据 —
onSearch和toggleGroup直接修改了来自 prop 的group.isCollapse,违反 Vue 单向数据流原则,可能导致父组件状态被意外篡改。 forEach(async ...)不被 await —filterHistoryData中的breadcrumbData.forEach(async ...)的异步回调不会被外层等待,loading 状态会提前结束,异步异常无法被 catch 捕获。activitiesArray.find(...).id可能 NPE — find 可能返回 undefined,直接访问.id会抛异常。
⚡ 性能建议
loadSubprocessOutput每次加载子流程都会请求 — 如果输出参数不常变,建议做缓存。
✨ 代码整洁
document.querySelector全局选择器 — formMixins 中handleListShow使用全局选择器可能匹配到错误元素。TemplateData未使用 — 已导入但未在模板中引用。- 缩进不一致 — NodeConfig.vue 中部分代码缩进跌落,视觉上容易产生误解。
- 魔法数字 — loopConfig 默认值中的
loop_times: 3等建议抽常量。
整体改动质量不错,UI 交互体验有明显提升。上述 1-3 项建议优先处理。
| if (!this.isListOpen) { | ||
| return; | ||
| } | ||
| const listPanel = document.querySelector('.rf-select-list'); |
There was a problem hiding this comment.
document.querySelector('.rf-select-list') 只能匹配到页面上第一个 .rf-select-list 元素。当页面同时渲染多个 Tag 组件时,点击关闭逻辑可能作用在错误的下拉面板上。建议改用 this.$el.querySelector 或通过 $refs 定位当前组件实例内的面板。
| onSearch() { | ||
| // 搜索时自动展开所有分组 | ||
| this.filteredGroupList.forEach((group) => { | ||
| group.isCollapse = false; |
There was a problem hiding this comment.
onSearch 和 toggleGroup 直接修改了 prop varList 引用的 group 对象的 isCollapse,违反 Vue 单向数据流原则。建议将折叠状态维护在组件自身 data 中(如用 collapsedGroups Set 跟踪)。
| version: has.call(output, 'version') ? output.version : 'legacy', | ||
| }; | ||
| }); | ||
| // 输出变量 |
There was a problem hiding this comment.
✨ Lines 777-799 缩进从 10 空格跌落到 8 空格,视觉上看起来像跳出了 try 块。建议统一缩进以避免维护时产生误解。
| // 从接口获取子流程标准输出参数 | ||
| const subprocessPlugin = this.atomList.find(item => item.code === 'subprocess_plugin'); | ||
| const subprocessPluginVersion = subprocessPlugin?.list?.[0]?.version || ''; | ||
| const res = await this.loadSubprocessOutput({ space_id: this.spaceId, version: subprocessPluginVersion }); |
There was a problem hiding this comment.
⚡ loadSubprocessOutput 在每次 loadSubflowData 时都会发起网络请求。如果 subprocess_plugin 输出参数不频繁变化,建议缓存结果避免重复请求。
| import { getOrderNodeToNodeTree } from '@/utils/orderCanvasNodeToNodeTree.js'; | ||
| import JumpLinkBKFlowOrExternal from '@/components/common/JumpLinkBKFlowOrExternal.vue'; | ||
| import SubStageCanvas from '../../../components/canvas/StageCanvas/SubStageCanvas.vue'; | ||
| import TemplateData from './TemplateData.vue'; |
There was a problem hiding this comment.
✨ TemplateData 已导入并注册但未在 <template> 中使用,请移除未使用的 import 和组件注册。
| } | ||
| } else { | ||
| const { template_node_id: templateNodeId } = this.subCanvsActivityCollection[this.nodeDetailConfig.node_id]; | ||
| curNewNodeId = activitiesArray.find(item => item.template_node_id === templateNodeId).id; |
There was a problem hiding this comment.
activitiesArray.find(item => item.template_node_id === templateNodeId) 可能返回 undefined,直接访问 .id 会抛异常。建议加空值判断。
| component_code: this.nodeDetailConfig.component_code, | ||
| }; | ||
| const res = await this.getNodeActDetail(query); | ||
| this.breadcrumbData.forEach(async (item) => { |
There was a problem hiding this comment.
breadcrumbData.forEach(async ...) 中的 async 回调不会被外层 await,导致 isBreadCrumbLoading = false(line 788)在异步操作完成前执行,且异常无法被 catch 捕获。建议改用 for...of + await 或 Promise.all。
| || (Array.isArray(loopParams) && loopParams.length === 0) | ||
| || (!Array.isArray(loopParams) && typeof loopParams === 'object' && Object.keys(loopParams).length === 0)) { | ||
| this.formData.loopConfig.loop_params = [{ name: '', value: '', is_quote: false }]; | ||
| } else if (!Array.isArray(loopParams)) { |
There was a problem hiding this comment.
✨ loop_times: 3 等默认值属于魔法数字,按项目规范建议抽取为命名常量(如 DEFAULT_LOOP_TIMES)。
# Reviewed, transaction id: 78435
# Reviewed, transaction id: 78600
# Reviewed, transaction id: 78614
# Reviewed, transaction id: 79261
There was a problem hiding this comment.
Code Review 总结(增量审查)
本次增量审查针对子流程循环执行功能的最新提交。大部分代码改动围绕面包屑选择器重构、循环/重试/执行次数选择、以及循环输出参数处理,整体实现思路清晰。
上一轮问题状态
⚠️ activitiesArray.find(...).idNPE — 已通过前置判断部分缓解,但在filterHistoryDataelse 分支中find仍可能返回 undefined(line 757),风险仍存⚠️ VariableList直接修改 prop — 未修复,onSearch和toggleGroup仍直接修改 prop 对象的isCollapse⚠️ forEach(async ...)— 未修复,filterHistoryData(line 767) 和loadBreadCrumbData(line 1524) 中仍存在- ✨
TemplateData未使用 — 未修复,仍在 import 和注册 - ✨
document.querySelector('.rf-select-list')— 未修复,formMixins.js line 367 仍使用全局选择器 - ✨
loop_times: 3魔法数字 — 未修复 - ✨ NodeConfig.vue 缩进问题 — 未修复
新发现问题
- 🚨
filterHistoryData中activitiesArray.find(...)未做空值保护(line 757),直接.id可能 NPE ⚠️ filterHistoryData中taskInfo = outputs.find(...) || {}因|| {}始终为 truthy,if (taskInfo)永远为 true,else 分支成为死代码(line 738)⚠️ selectBreadcrumExecuteCount中prevItem在targetIndex <= 0时为 null,直接访问.allExecutedInfo会抛 NPE(line 816)
| } | ||
| } else { | ||
| const { template_node_id: templateNodeId } = this.subCanvsActivityCollection[this.nodeDetailConfig.node_id]; | ||
| curNewNodeId = activitiesArray.find(item => item.template_node_id === templateNodeId).id; |
There was a problem hiding this comment.
activitiesArray.find(item => item.template_node_id === templateNodeId) 可能返回 undefined,直接访问 .id 会抛异常。建议使用 optional chaining:.find(...)?.id。
| if (outputs && outputs.task_id) { | ||
| taskId = outputs.task_id; | ||
| } else if (Array.isArray(outputs)) { | ||
| const taskInfo = outputs.find(item => item.key === 'task_id') || {}; |
There was a problem hiding this comment.
outputs.find(...) || {} 结果始终为 truthy(空对象),if (taskInfo) 永远为 true,后续 else 分支成为死代码。应去掉 || {},改为 const taskInfo = outputs.find(item => item.key === 'task_id') 后再判断。
| component_code: this.nodeDetailConfig.component_code, | ||
| }; | ||
| const res = await this.getNodeActDetail(query); | ||
| this.breadcrumbData.forEach(async (item) => { |
There was a problem hiding this comment.
breadcrumbData.forEach(async ...) 中的 async 回调不会被外层 await,导致后续 isBreadCrumbLoading = false 在异步操作完成前执行。建议改用 for...of + await 或 Promise.all。
# Reviewed, transaction id: 78412
# Reviewed, transaction id: 78415
# Reviewed, transaction id: 78783
# Reviewed, transaction id: 78864
# Reviewed, transaction id: 79016
# Reviewed, transaction id: 79018
# Reviewed, transaction id: 79087
# Reviewed, transaction id: 79276
There was a problem hiding this comment.
Code Review 总结(增量审查 - HTTP回调功能)
本次增量审查主要关注新增的 HTTP 回调(Webhook)功能相关提交,包括 HttpCallbackConfig.vue、WebhookCallback.vue、TabTemplateConfig.vue 改动,以及 TaskOperation.vue / index.vue 中的回调记录获取逻辑。整体功能完整,调试能力和表单校验做得较好。
上一轮问题状态
⚠️ activitiesArray.find(...).idNPE(line 757)— 未修复,仍存在⚠️ VariableListprop 直接修改 — 未修复⚠️ forEach(async ...)不被 await — 未修复(line 767, 1524)⚠️ outputs.find(...) || {}死代码 — 未修复(line 738, 807)- ✨
TemplateData未使用 — 未修复 - ✨ 其余 Minor 项 — 未修复
新发现问题
详见行内评论(共 5 条)。
| // 请求获取回调记录 | ||
| if (['FINISHED', 'FAILED'].includes(this.state)) { | ||
| const instanceData = await this.getTaskInstanceData(this.taskId); | ||
| this.webhookHistory = instanceData.webhook_delivery_history; |
There was a problem hiding this comment.
instanceData.webhook_delivery_history 未做防御性处理,如果接口未返回该字段会导致 webhookHistory 为 undefined,进而传给 WebhookCallback 组件引发渲染异常。建议改为:
this.webhookHistory = instanceData.webhook_delivery_history || [];| this.triggerMethod = triggerMethod; | ||
| this.parentTaskInfo = parentTaskInfo || {}; | ||
| // 查询关联流程权限,用于判断是否展示查看流程按钮 | ||
| const templateData = await this.loadTemplateData({ templateId, common: templateSource === 'common' }); |
There was a problem hiding this comment.
loadTemplateData 失败(如用户无模板查看权限)会导致整个 getTaskData 进入 catch,后续 setPipelineTree 不会执行,整个任务页面无法渲染。建议将这段逻辑移到 try/catch 外层或独立 try/catch 中,避免影响核心任务数据加载。
| import NoData from '@/components/common/base/NoData.vue'; | ||
| import Vue from 'vue'; | ||
| import { bkTooltips } from 'bk-magic-vue'; | ||
| Vue.use(bkTooltips); |
There was a problem hiding this comment.
✨ Vue.use(bkTooltips) 放在模块顶层会在 import 时产生全局副作用。如果其他组件已经注册了该指令,这里的重复注册虽不会报错,但不符合单一职责原则。建议改为在组件内通过 directives: { bkTooltips } 局部注册。
No description provided.