likes
comments
collection
share

钉钉二次开发完美完爆消息通知

作者站长头像
站长
· 阅读数 14

钉钉

  • 钉钉的接入和微信公众号基本时一致的,唯一不同的是微信很难拿到手机号切公众号接口的权限和账号本身特性有关,而钉钉拿到手机号还是很容易的,并且钉钉接口的权限只需要申请就可以了。

钉钉二次开发完美完爆消息通知

  • 首先我们注册一个后台账号并登陆进去创建一个企业内部应用。我这里就是创建的test应用。

钉钉二次开发完美完爆消息通知

  • 到这里我们就拥有了CorpId,AgentId,AppKey,AppSecrect四个参数了。这四个参数在不同的地方分别被使用。都说了和微信是一个套路,那么剩下的自然是需要我们配置权限管理。

钉钉二次开发完美完爆消息通知

  • 下面是我针对目前功能所开通的权限。如果你嫌麻烦那就所有权限全部开通。
接口权限点code全部状态筛选操作
个人手机号信息获取用户个人信息Contact.User.mobile已开通
通讯录个人信息读权限获取用户通讯录个人信息Contact.User.Read已开通
调用SNS API时需要具备的基本权限查询个人授权记录snsapi_base已开通
企业员工手机号信息fieldMobile已开通
通讯录部门信息读权限获取部门详情;获取指定用户的所有父部门列表;获取部门列表;获取指定部门的所有父部门列表;查看更多qyapi_get_department_list已开通
成员信息读权限获取用户高管模式设置;查询用户详情;获取部门用户userid列表;获取管理员列表;查看更多qyapi_get_member已开通
根据手机号姓名获取成员信息的接口访问权限根据手机号获取useridqyapi_get_member_by_mobile已开通
通讯录部门成员读权限查询部门用户完整信息;获取部门用户基础信息;获取角色详情;获取指定角色的员工列表qyapi_get_department_member已开通
调用企业API基础权限生成jsapi ticket;生成微应用管理后台accessToken;查询连接器主数据详情;分页拉取连接器主数据;查看更多qyapi_base已开通
待办应用中待办写权限更新待办执行者状态;新增钉钉待办任务;更新钉钉待办任务;删除钉钉待办任务Todo.Todo.Write已开通
待办应用中待办读权限查询企业下用户待办列表;根据sourceId获取钉钉待办任务详情;获取钉钉待办任务详情Todo.Todo.Read已开通

测试用例

获取部门列表

  • 钉钉和公众号不同的是,钉钉细分很多,树形结构一直递归下去。和微信公众号一样我们也需要获取企业下(公众号)列表。
 @Test
 public void getDeptListTest() {
   final List<OapiV2DepartmentListsubResponse.DeptBaseResponse> deptBaseResponses = deptService.selectDeptList(null);
   for (OapiV2DepartmentListsubResponse.DeptBaseResponse deptBaseRespons : deptBaseResponses) {
     System.out.println(JSON.toJSONString(deptBaseRespons));
   }
 }
  • 如果行获取跟部门下的用户列表我们deptId=1或者不传。

钉钉二次开发完美完爆消息通知

 {"autoAddUser":true,"createDeptGroup":true,"deptId":581377085,"name":"运营部","parentId":1}
 {"autoAddUser":true,"createDeptGroup":true,"deptId":581377086,"name":"设计部","parentId":1}
 {"autoAddUser":true,"createDeptGroup":true,"deptId":581377087,"name":"产品部","parentId":1}
 {"autoAddUser":true,"createDeptGroup":true,"deptId":581377088,"name":"人事部","parentId":1}
 {"autoAddUser":true,"createDeptGroup":true,"deptId":581377089,"name":"行政部","parentId":1}

获取部门用户列表

 @Test
 public void getUserInfo() {
   Long deptId = 1L;
   final List<AbstrctUser> userList = userInfoService.selectUserListBaseOnDeptId(deptId);
   for (AbstrctUser abstrctUser : userList) {
     System.out.println(JSON.toJSONString(abstrctUser));
   }
 }

钉钉二次开发完美完爆消息通知

 {"userId":"XXX","userName":"丁1"}
 {"userId":"XXXX","userName":"张1"}

通过手机号获取用户信息

 @Test
     public void getUsrDetailOnPhoneTest() {
         String phone = "phone";
         final List<AbstrctUser> userList = userInfoService.selectUserBaseOnPhone(phone);
         for (AbstrctUser abstrctUser : userList) {
             System.out.println(JSON.toJSONString(abstrctUser));
         }
     }

发送信息

 ​
 @Test
     public void sendMsg() {
         for (int i = 0; i < 1; i++) {
             List<String> userIds = Arrays.asList(new String[]{"tet,ttttt"});
             List<Long> deptIds = Arrays.asList(new Long[]{1l});
             messageService.sendToDeptInUser(userIds,deptIds,false,new TextMessage("你们好,元旦放假了!!!,我正在测试消息发送多人"+UUID.randomUUID()));
         }
     }

免登录

前端

  • 同样的钉钉中我们也需要获取当前登陆用户信息,所以还需要我们钉钉登陆授权。
  • 点我了解登陆认证钉钉
  • npm install dingtalk-jsapi --save进行安装。
 dd.ready(function() {
     // dd.ready参数为回调函数,在环境准备就绪时触发,jsapi的调用需要保证在该回调函数触发后调用,否则无效。
     dd.runtime.permission.requestAuthCode({
         corpId: "corpid",
         onSuccess: function(result) {
         /*{
             code: 'hYLK98jkf0m' //string authCode
         }*/
         },
         onFail : function(err) {}
   
     });
 });
 ​
  • 安装完成后就可以通过上面代码获取授权码code,其中需要我们的参数corpId也是我们在企业配置账号中的信息之一。

后端

  • 前端获取到code之后,后端就可以根据code获取到当前登陆用户了。
  • https://oapi.dingtalk.com/user/getuserinfo?access_token=%s&code=%s

文件上传

  • 同样我们钉钉发送消息也不仅仅局限于文本,https://oapi.dingtalk.com/media/upload

钉钉二次开发完美完爆消息通知

  • 经过和微信的对比我们知道和微信支持的类型有很大的交集。所以在微信章节中我提到消息进行抽象化 , 现在我们可以共用一套实体类,出现两者不共同的我们在单独的创建特有的实体满足不同需求。
 public MeterialResponse uploadPic(Meterial meterial, String fileName, InputStream inputStream) {
         DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/media/upload");
         OapiMediaUploadRequest req = new OapiMediaUploadRequest();
         req.setType(meterial.getType());
         // 要上传的媒体文件
         FileItem item = new FileItem(fileName,inputStream);
         req.setMedia(item);
         OapiMediaUploadResponse rsp=null;
         try {
             rsp = client.execute(req, tokenService.accessAndGetDingDingToken());
         } catch (ApiException e) {
             e.printStackTrace();
         }
         MeterialResponse response = new MeterialResponse();
         final Field[] declaredFields = rsp.getClass().getDeclaredFields();
         JSONObject jsonObject = new JSONObject();
         for (Field declaredField : declaredFields) {
             final ApiField annotation = declaredField.getAnnotation(ApiField.class);
             if (null == annotation) {
                 continue;
             }
             final String value = annotation.value();
             declaredField.setAccessible(true);
             try {
                 jsonObject.put(value, declaredField.get(rsp));
             } catch (IllegalAccessException e) {
                 e.printStackTrace();
             }
         }
         BeanUtils.transBeanWithTranAnnotation(jsonObject, response);
         return response;
     }

发送消息

  • 在上面我们已经实现了上传文件了。返回的media_id就是我们交互的载体。我们发送信息只需要将media_id发送过去就行了。
 @Test
     public void sendMultiTypeMessage() {
         String fileName = "/macpower.png";
         InputStream resourceAsStream = this.getClass().getResourceAsStream(fileName);
         final MeterialResponse response = uploadService.uploadPic(new ImageMeterial(), fileName, resourceAsStream);
         System.out.println(response);
         Message message = new ImageMessage(response.getMediaId());
         List<String> userIds = Arrays.asList(new String[]{"manager2239"});
         List<Long> deptIds = Arrays.asList(new Long[]{1l});
         messageService.sendToDeptInUser(userIds,deptIds,false,message);
     }
  • 这里的发送实际上知识Message的不同而已。

钉钉二次开发完美完爆消息通知

代码说明

钉钉二次开发完美完爆消息通知

  • 首先我将包管理放在根目录管理。
  • message-api用于管理微信,钉钉通用部分,比如定义通用方法,微信,钉钉都需要实现发送信息功能,那么关于接口的定义就是放在message-api中的。还有就是关于消息的抽象的定义也是在message-api中完成的。包括一些工具类放在message-api完成。
  • message-demo是针对钉钉,微信的测试用例。在demo中搭建好测试环境。编写测试用例。
  • 剩下的就是dingding-message-rootwechat-message-root两个模块了。顾名思义就是钉钉微信的处理业务。两个模块结构是一样的,内部会支持message-api定义的骨架的基础上进行定制开发。比如wechat-message-root中会开发微信特有的上传图文的功能。如果我们想使用该功能的话就不能仅仅用公共的MessageService 。 而是需要使用更加明确的WechatMessageService
  • 再者,我们使用spring框架基本上是覆盖很广。所以每个message-root还需要在细分。即wechat-message-root下细分为spring-wechat-starterwechat-core 。 前者是对spring的开箱即用的支持,后者才是真正对接微信公众号的服务功能。

钉钉二次开发完美完爆消息通知

  • 可以看到service.impl中主要都是实现message-api模块中定义好的接口。在实现主接口的基础上需要扩展封住的功能会在wechat-core中进行扩展开发。
  • 因为钉钉,微信都实现message-api模块的接口,且内部都有spring的开箱即用功能,而spring容器中beanName是唯一的。而且为了我们自己能确定使用具体的模块的be an ,我们需要在注册的时候制定我们beanName,这样我们在使用的时候在通过名称查找。
  • 比如我这样定义了一个bean
 @Bean("wechatMessageService")
 public MessageService wechatMessageService(TokenService tokenService) {
   WechatMessageServiceImpl wechatMessageService = new WechatMessageServiceImpl();
   wechatMessageService.setTokenService(tokenService);
   return wechatMessageService;
 }
  • 那么我在使用的时候需要这样配置
 @Autowired
 @Qualifier("wechatMessageService")
 MessageService messageService;
  • 这样我们在使用的时候在两者公共的功能时,只需要通过对应的service完成就可以了。比如群发功能。这个时候如果我们微信钉钉都需要实现,那么我们可以通过这样引入service
 @Autowired
 List<MessageService> messageServiceList ;
  • 执行的时候我们只需要遍历执行就可以了

钉钉二次开发完美完爆消息通知

总结

  • 系统对接其实并没啥技术含量,只要我们根据官网文档一步一步操作就可以了。我们就是调用api 。 真正的高度时在平台的实现。比如我们的接口调用的权限基于o a u t h2完成的登陆鉴权。这些在maltcloud中我们都有去实现。后面有机会会结合实战去解读下oauth2和如何设计完成接口权限调度
  • 除了微信的对接外,常见的还有支付宝的支付功能。这也是在掉接口核心的永远都是平台的功能。如果有需要我们后面在看看支付宝的接入