JIRA對接釘釘群機械人-實現任務的指派通知

一、前提

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 }

View Code

  1 @Slf4j
  2 public class JiraWebHookUtil {
  3 
  4     private static final String DING_TALK_ROBOT_URL = "//oapi.dingtalk.com/robot/send" +
  5             "?access_token=xxxxxx";
  6     private static final String JIRA_BASE_URL = "//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                                     + "\n>任務編號:[" + issueKey + "](" + JIRA_BASE_URL + issueKey + ")"
 36                                     + "  \n任務標題:" + 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                                     + "\n>任務編號:[" + issueKey + "](" + JIRA_BASE_URL + issueKey + ")"
 60                                     + "  \n任務標題:" + 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 }

View Code

6、Jira Software配置網絡鉤子,使用第5步的接口地址

 

四、驗證效果