一、前提
Jira Software、钉钉群、RESTful服务、LDAP服务
二、流程图
三、对接步骤
1、创建项目群,把相关人员拉入群
2、钉钉群的智能群助手里添加自定义机器人
3、设置机器人,安全设置里建议勾选IP地址段,将RESTful服务所在的机器IP填入
4、注意创建完成后的Webhook地址,RESTful服务用这个地址发送通知消息,后面步骤要用上
5、RESTful服务新增一个POST接口,这里可以自定义通知内容,部署到服务器
1 @ApiOperation(value = "JIRA网络钩子") 2 @PostMapping("/jiraWebHook") 3 @ResponseBody 4 public Result<Boolean> jiraWebHook(HttpServletRequest request) { 5 BufferedReader br = null; 6 String str, wholeStr = ""; 7 try { 8 br = request.getReader(); 9 10 while ((str = br.readLine()) != null) { 11 wholeStr += str; 12 } 13 log.info("jiraWebHook body: {}", wholeStr); 14 } catch (Exception e) { 15 log.error("jiraWebHook error", e); 16 } finally { 17 if (br != null) { 18 try { 19 br.close(); 20 } catch (Exception e) { 21 } 22 } 23 } 24 return Result.success(JiraWebHookUtil.hook(JSONObject.parseObject(wholeStr, JiraPostData.class))); 25 }
1 @Slf4j 2 public class JiraWebHookUtil { 3 4 private static final String DING_TALK_ROBOT_URL = "https://oapi.dingtalk.com/robot/send" + 5 "?access_token=xxxxxx"; 6 private static final String JIRA_BASE_URL = "http://xxx.xxx.xxx.xxx:38080/browse/"; 7 private static RestTemplate restTemplate = new RestTemplate(); 8 9 public static boolean hook(JiraPostData postData){ 10 try { 11 if(postData != null){ 12 if(StringUtils.equals(postData.getWebhookEvent(), "jira:issue_updated")){ 13 //更新事件 14 List<Item> items = postData.getChangelog().getItems(); 15 String toUserId = Strings.EMPTY; 16 String fromUserId = Strings.EMPTY; 17 for(Item item : items){ 18 if(StringUtils.equals(item.getField(), "assignee")){ 19 toUserId = item.getTo(); 20 fromUserId = item.getFrom(); 21 break; 22 } 23 } 24 if(StringUtils.isNotBlank(toUserId) && StringUtils.isNotBlank(fromUserId)){ 25 String toMobile = getUserMobile(toUserId); 26 String fromMobile = getUserMobile(fromUserId); 27 if(StringUtils.isNotBlank(toMobile) && StringUtils.isNotBlank(fromMobile)){ 28 String issueKey = postData.getIssue().getKey(); 29 String issueSummary = postData.getIssue().getFields().getSummary(); 30 DingTalkMarkdownMessage msg = new DingTalkMarkdownMessage(); 31 msg.setMsgtype("markdown"); 32 Markdown markdown = new Markdown(); 33 markdown.setTitle(postData.getIssue().getKey()); 34 markdown.setText("### @" + fromMobile + " 【转移任务给】@" + toMobile 35 + " >任务编号:[" + issueKey + "](" + JIRA_BASE_URL + issueKey + ")" 36 + " 任务标题:" + issueSummary); 37 msg.setMarkdown(markdown); 38 At at = new At(); 39 at.setAtMobiles(Arrays.asList(toMobile, fromMobile)); 40 msg.setAt(at); 41 return sendMsgToDingTalk(JSONObject.toJSONString(msg)); 42 } 43 } 44 }else if(StringUtils.equals(postData.getWebhookEvent(), "jira:issue_created")){ 45 //创建事件 46 String fromUserId = postData.getUser().getKey(); 47 String toUserId = postData.getIssue().getFields().getAssignee().getKey(); 48 if(StringUtils.isNotBlank(toUserId) && StringUtils.isNotBlank(fromUserId)){ 49 String toMobile = getUserMobile(toUserId); 50 String fromMobile = getUserMobile(fromUserId); 51 if(StringUtils.isNotBlank(toMobile) && StringUtils.isNotBlank(fromMobile)){ 52 String issueKey = postData.getIssue().getKey(); 53 String issueSummary = postData.getIssue().getFields().getSummary(); 54 DingTalkMarkdownMessage msg = new DingTalkMarkdownMessage(); 55 msg.setMsgtype("markdown"); 56 Markdown markdown = new Markdown(); 57 markdown.setTitle(postData.getIssue().getKey()); 58 markdown.setText("### @" + fromMobile + " 【指派任务给】@" + toMobile 59 + " >任务编号:[" + issueKey + "](" + JIRA_BASE_URL + issueKey + ")" 60 + " 任务标题:" + issueSummary); 61 msg.setMarkdown(markdown); 62 At at = new At(); 63 at.setAtMobiles(Arrays.asList(toMobile, fromMobile)); 64 msg.setAt(at); 65 return sendMsgToDingTalk(JSONObject.toJSONString(msg)); 66 } 67 } 68 } 69 } 70 71 }catch (Exception e) { 72 log.error("触发JIRA网络钩子失败", e); 73 } 74 return false; 75 } 76 77 private static boolean sendMsgToDingTalk(String msg){ 78 JSONObject postData = JSONObject.parseObject(msg); 79 JSONObject result = restTemplate.postForObject(DING_TALK_ROBOT_URL, postData, JSONObject.class); 80 if(result != null 81 && result.getIntValue("errcode") == 0 82 && StringUtils.equals(result.getString("errmsg"), "ok")){ 83 return true; 84 } 85 return false; 86 } 87 88 private static String getUserMobile(String userId){ 89 String mobile = Strings.EMPTY; 90 91 Hashtable<String, String> env = new Hashtable<String, String>(); 92 env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory"); 93 env.put(Context.PROVIDER_URL, "ldap://xxxxxx:389"); 94 env.put(Context.SECURITY_AUTHENTICATION, "simple"); 95 env.put(Context.SECURITY_PRINCIPAL, "cn=xxx,dc=xxx,dc=xxx,dc=com"); 96 env.put(Context.SECURITY_CREDENTIALS, "xxxxxx"); 97 DirContext ctx = null; 98 try{ 99 ctx = new InitialDirContext(env); 100 }catch(Exception e){ 101 log.error("连接LDAP失败", e); 102 } 103 104 // 设置过滤条件 105 String filter = "(&(objectClass=top)(objectClass=inetOrgPerson)(cn=" + userId + "))"; 106 // 限制要查询的字段内容 107 String[] attrPersonArray = {"cn", "mobile"}; 108 SearchControls searchControls = new SearchControls(); 109 searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE); 110 // 设置将被返回的Attribute 111 searchControls.setReturningAttributes(attrPersonArray); 112 // 三个参数分别为: 113 // 上下文; 114 // 要搜索的属性,如果为空或 null,则返回目标上下文中的所有对象; 115 // 控制搜索的搜索控件,如果为 null,则使用默认的搜索控件 116 try { 117 NamingEnumeration<SearchResult> answer = ctx.search( 118 "ou=People,dc=xxx,dc=xxx,dc=xxx", filter, searchControls); 119 // 输出查到的数据 120 while (answer.hasMore()) { 121 SearchResult result = answer.next(); 122 Attribute attr = result.getAttributes().get("mobile"); 123 mobile = attr.get().toString(); 124 break; 125 } 126 }catch (Exception e){ 127 log.error("查询LDAP用户信息失败", e); 128 } 129 return mobile; 130 } 131 132 }
6、Jira Software配置网络钩子,使用第5步的接口地址
四、验证效果