一、前提
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 + "\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 }
6、Jira Software配置网络钩子,使用第5步的接口地址
四、验证效果