如何通过ScriptRunner实现Jira中Epic与Issue的Customer字段同步?
用ScriptRunner for Jira实现Customer字段的层级自动继承
核心思路
通过ScriptRunner的监听器或自动化规则,在工单创建、父项Customer字段变更时,递归向上查找层级最高的(直至Epic)有有效值的Customer字段,将该值同步到当前工单及所有子工单/关联工单。
准备工作
- 获取自定义字段
Customer的ID:进入Jira后台→自定义字段,找到该字段后查看URL,提取customfield_xxxx格式的ID(例如customfield_10010) - 确保ScriptRunner已安装启用,且操作账号具备Jira管理员权限
方案一:ScriptRunner监听器实现
1. 创建监听器
- 进入Jira后台→ScriptRunner→Listeners→Add Listener
- 选择触发事件:
Issue Created和Issue Updated(覆盖创建时继承、父项字段变更时同步的场景) - 选择
Script Listener类型,进入脚本编辑界面
2. 核心Groovy脚本
import com.atlassian.jira.component.ComponentAccessor import com.atlassian.jira.issue.Issue import com.atlassian.jira.issue.MutableIssue import com.atlassian.jira.issue.fields.CustomField import com.atlassian.jira.bc.issue.search.SearchService import com.atlassian.jira.web.bean.PagerFilter // 替换为你的Customer自定义字段ID def CUSTOM_FIELD_ID = "customfield_10010" // 替换为你项目中Epic类型的名称(注意大小写匹配) def EPIC_ISSUE_TYPE_NAME = "Epic" def customFieldManager = ComponentAccessor.getCustomFieldManager() def customerField = customFieldManager.getCustomFieldObject(CUSTOM_FIELD_ID) def issueManager = ComponentAccessor.getIssueManager() def user = ComponentAccessor.jiraAuthenticationContext.loggedInUser def searchService = ComponentAccessor.getComponent(SearchService) // 递归查找层级最高的有效Customer值 def findTopCustomerValue(Issue issue) { def currentIssue = issue def customerValue = currentIssue.getCustomFieldValue(customerField) // 向上遍历,直到找到有值的Epic或无父项为止 while (customerValue == null || !isEpic(currentIssue)) { def parentIssue = currentIssue.parentObject if (!parentIssue) break customerValue = parentIssue.getCustomFieldValue(customerField) currentIssue = parentIssue } return customerValue } // 判断工单是否为Epic def isEpic(Issue issue) { return issue.issueType.name == EPIC_ISSUE_TYPE_NAME } // 同步Customer值到当前工单及所有子项/关联工单 def syncCustomerValue(Issue issue, def targetValue) { // 更新当前工单 if (issue.getCustomFieldValue(customerField) != targetValue) { def mutableIssue = issue as MutableIssue mutableIssue.setCustomFieldValue(customerField, targetValue) issueManager.updateIssue(user, mutableIssue, com.atlassian.jira.event.type.EventDispatchOption.DO_NOT_DISPATCH, false) } // 递归更新所有子任务 issue.subTaskObjects.each { subTask -> syncCustomerValue(subTask, targetValue) } // 如果是Epic,更新所有关联的普通Issue(通过Epic Link字段) if (isEpic(issue)) { def epicLinkField = customFieldManager.getCustomFieldObjectByName("Epic Link") if (epicLinkField) { def jql = "\"Epic Link\" = ${issue.key}" def parseResult = searchService.parseQuery(user, jql) if (parseResult.isValid()) { def searchResults = searchService.search(user, parseResult.query, PagerFilter.getUnlimitedFilter()) searchResults.issues.each { linkedIssue -> syncCustomerValue(linkedIssue, targetValue) } } } } } // 主执行逻辑 def currentIssue = event.issue def topCustomerValue = findTopCustomerValue(currentIssue) if (topCustomerValue != null) { syncCustomerValue(currentIssue, topCustomerValue) }
3. 脚本说明
findTopCustomerValue:从当前工单向上遍历,优先取Epic的Customer值;若Epic无值,则取最近的有值的父项syncCustomerValue:先更新当前工单,再递归更新所有子任务;若当前工单是Epic,还会同步所有关联的普通Issue- 脚本中两处标记的变量需替换为你环境中的实际值
方案二:ScriptRunner自动化规则实现
如果偏好可视化配置,可使用ScriptRunner自动化规则:
- 进入目标项目→Project Settings→Automation→Create rule
- 触发条件选择:
Issue created+Issue updated(可添加条件过滤,仅当Customer字段变更时触发) - 添加
Script action,将上述核心脚本调整后放入(去掉event.issue相关,直接通过issue变量获取当前工单)
注意事项
- 首次部署前,务必在测试项目验证脚本逻辑,避免影响生产环境
- 若需要批量同步历史工单,可将脚本稍作修改后,在ScriptRunner的Script Console中执行
- 确保执行脚本的账号具备修改工单和编辑自定义字段的权限
内容的提问来源于stack exchange,提问作者ocroquette
相关产品推荐
相关产品推荐

