项目实践-SpringBoot项目对接钉钉发送工作通知

前言

  1. 业务场景:目前很多企业在日常运行中的消息会议,工作通知,任务安排等信息一般都通过企业微信/邮件等通讯软件来进行交流和消息分发,避免了口头传达的不及时性和传递错误性。在笔者的项目研发中,业务中到审批流的消息分发,在支持企业微信/邮件等形式的基础上,开发支持钉钉发送工作通知消息的方式,毕竟支持通知的方式越多,说明系统的功能越完备嘛!
  2. 开发步骤:(这里以笔者自建的钉钉组织架构为例)
    – 创建属于自己的企业管理平台(钉钉开放平台
    – 应用开发-企业内部应用-钉钉用用-H5微应用
    – 参照开发者文档,设置微应用的权限/IP等信息(开发者文档-消息通知
    – 代码开发(下面会逐步介绍)

一、钉钉应用申请?

1.钉钉应用创建,若开发人员的钉钉账号已经是既有组织的平台管理账号,可以直接登录钉钉管理后台,若没有管理权限则可以通过管理员授权或者自己创建组织的形式来完成开发工作。

2.工作通知创建的应用类别为:企业内部应用–H5微应用,这里需要注意的是,应用创建完成后需要完成IP设置和权限设置

java 对接钉钉api java钉钉通知_Access


java 对接钉钉api java钉钉通知_json_02

二、SpringBoot实现钉钉工作通知

1.引入钉钉SDK工具包pom依赖

<!--钉钉工具包-->
        <dependency>
            <groupId>com.dingtalk.api</groupId>
            <artifactId>top-api-sdk</artifactId>
            <version>2.0.0-RELEASE</version>
        </dependency>

这里在引入SDK工具包的时候需要注意,多数环境的私服中不存在SDK的工具包,需要手动下载然后挂载到maven仓库中

  • 挂载的过程
    从官方或者笔者的资源中下载SDK工具包(可以私信我免费获取)
    在项目控制台中通过命令将jar包挂载到仓库中
mvn install:install-file -DgroupId=com.dingtalk.api -DartifactId=top-api-sdk-dev -Dversion=ding-open-mc-SNAPSHOT -Dfile=lib/taobao-sdk-java-auto_1479188381469-20210630.jar -Dpackaging=jar -DgeneratePom=true

2.Nacos配置

dingding:
  agentId: ********(按应用填写)
  appKey: dingsg*********ceyk(按应用填写)
  appSecret: pghvjQeW********vObLlkLlIh88ng0Elm42M***m8JhPa7CSciPntRjarjo(按应用填写)
  # 获取Access_token
  getTokenUrl: https://oapi.dingtalk.com/gettoken
  # 根据电话号码获取urid
  getByMobileUrl: https://oapi.dingtalk.com/user/get_by_mobile
  # 发送消息通知
  asyncMessageUrl: https://oapi.dingtalk.com/topapi/message/corpconversation/asyncsend_v2
  # 根据电话号码获取用户信息
  userByMobileUrl: https://oapi.dingtalk.com/topapi/v2/user/getbymobile
  # 更新通讯录用户
  userUpdateUrl: https://oapi.dingtalk.com/topapi/v2/user/update
  # 新增通讯录用户
  userAddUrl: https://oapi.dingtalk.com/topapi/v2/user/create

备注:
笔者此处将应用的参数和涉及的开放Api配置到nacos中,避免在服务运行过程中因应用调整或Api升级需要重新调整项目代码。

3.实现钉钉工作通知
整体思路

  • 同步系统平台人员到钉钉通讯录(根据项目需要进行同步,也可以选择不同步)
  • 获取钉钉Access_Token
  • 根据钉钉通知类别组装消息体
  • 钉钉通知发送
    A.获取Access_Token
/**
 - @author Miracle
 - @title: DingdingServiceImpl
 - @projectName proxy
 - @description: 【钉钉服务接口】Access_Token
 - @date 2021/7/614:52
 */
@Service
@Slf4j
public class DingdingServiceImpl implements DingdingService {
	@Value("${mare.loginUrl}")
    private String loginUrl;

    @Value("${dingding.agentId}")
    private Long AgentId;

    @Value("${dingding.appKey}")
    private String APP_KEY;

    @Value("${dingding.appSecret}")
    private String APP_SECRET;

    @Value("${dingding.getTokenUrl}")
    private String getTokenUrl;

    @Value("${dingding.getByMobileUrl}")
    private String getByMobileUrl;

    @Value("${dingding.asyncMessageUrl}")
    private String asyncMessageUrl;

    @Value("${dingding.userByMobileUrl}")
    private String userByMobileUrl;

    @Value("${dingding.userUpdateUrl}")
    private String userUpdateUrl;

    @Value("${dingding.userAddUrl}")
    private String userAddUrl;

	/**
     * 获取AccessToken
     * @return
     */
    public String getAccessToken() throws ApiException {
        DefaultDingTalkClient client =
                new DefaultDingTalkClient(getTokenUrl);
        OapiGettokenRequest request = new OapiGettokenRequest();
        //Appkey
        request.setAppkey(APP_KEY);
        //Appsecret
        request.setAppsecret(APP_SECRET);
        /*请求方式*/
        request.setHttpMethod("GET");
        OapiGettokenResponse response = client.execute(request);
        return response.getAccessToken();
    }
}

B.同步平台人员到钉钉通讯录

/**
     * 同步钉钉通讯录
     */
    @Override
    public void syncUsers() {
        try {
            // 获取钉钉AccessToken信息
            String accessToken = this.getAccessToken();
            log.info("获取钉钉AccessToken信息完成");
            // 获取平台全量人员信息
            Map<String, StaffObject> staffMap = getPlatformStaffs();
            log.info("获取平台全量人员信息完成");

            if(staffMap != null && staffMap.size() > 0){
                for (Map.Entry<String, StaffObject> entry : staffMap.entrySet()){
                    StaffObject staff = entry.getValue();
                    // 根据平台人员手机账号判断是否存在
                    DingTalkClient client = new DefaultDingTalkClient(userByMobileUrl);
                    OapiV2UserGetbymobileRequest req = new OapiV2UserGetbymobileRequest();
                    req.setMobile(staff.getPhoneNum());
                    OapiV2UserGetbymobileResponse user = client.execute(req, accessToken);
                    // 返回码
                    String errcode = user.getErrorCode();
                        // 通讯录中存在该用户
                    if("0".equals(errcode) || errcode == "0"){
                        OapiV2UserGetbymobileResponse.UserGetByMobileResponse json = user.getResult();
                        String userId = json.getUserid();
                        DingTalkClient updateClient = new DefaultDingTalkClient(userUpdateUrl);
                        OapiV2UserUpdateRequest updateReq = new OapiV2UserUpdateRequest();
                        updateReq.setUserid(userId);
                        updateReq.setName(staff.getName());
                        // 用户信息更新
                        OapiV2UserUpdateResponse updateRst = updateClient.execute(updateReq, accessToken);
                        log.info("【钉钉通讯录更新】电话号码:" + staff.getPhoneNum() + ",更新结果:" + updateRst.getErrcode() + "/" + updateRst.getErrmsg());
                    }else {
                        // 新增用户
                        DingTalkClient addClient = new DefaultDingTalkClient(userAddUrl);
                        OapiV2UserCreateRequest addReq = new OapiV2UserCreateRequest();
                        addReq.setName(staff.getName());
                        addReq.setMobile(staff.getPhoneNum());
                        addReq.setDeptIdList("1");
                        OapiV2UserCreateResponse addRst = addClient.execute(addReq, accessToken);
                        log.info("【钉钉通讯录新增】电话号码:" + staff.getPhoneNum() + ",更新结果:" + addRst.getResult());
                    }
                }
            }
        } catch (ApiException e) {
            e.printStackTrace();
        } 
    }

C.发送工作通知(以Text类型为例,其他类型可以参考开发者文档)

/**
     * 发送钉钉消息
     * @return
     * @throws ApiException
     */
    @Override
    public boolean sendDingMsg(DingEntity dingEntity) throws ApiException {
        Boolean sendResult = false;
        // 开始获取钉钉Access_Token,开启消息通知
        String accessToken = this.getAccessToken();
        // 通知接收人电话号码
		// String mobile = "15686457316,15712366325";
        if(StringUtils.isBlank(mobile)){
            return false;
        }
        String userIdList = "";
        // 循环处理拼接用户列表
        String[] split = mobile.split(",");
        for(String cell : split){
            DingTalkClient client2 = new DefaultDingTalkClient(getByMobileUrl);
            OapiUserGetByMobileRequest req = new OapiUserGetByMobileRequest();
            req.setMobile(cell);
            req.setHttpMethod("GET");
            OapiUserGetByMobileResponse rsp = client2.execute(req, accessToken);
            // 获取的urid即为发送的目标人员的ID
            String urid = rsp.getUserid();
            // 将接受人员的urid按照(user123,user456)的形式拼接
            userIdList = userIdList + urid + ",";
        }
        DingTalkClient client = new DefaultDingTalkClient(asyncMessageUrl);
        OapiMessageCorpconversationAsyncsendV2Request request = new OapiMessageCorpconversationAsyncsendV2Request();
        request.setUseridList(userIdList);
        request.setAgentId(AgentId);
        request.setToAllUser(false);
        OapiMessageCorpconversationAsyncsendV2Request.Msg msg = new OapiMessageCorpconversationAsyncsendV2Request.Msg();
        // 消息类别时文本消息
        msg.setMsgtype("text");
        msg.setText(new OapiMessageCorpconversationAsyncsendV2Request.Text());
		//  msg.getText().setContent("工作通知:你存在未处理的待办任务,请及时处理待办事项!");
        request.setMsg(msg);
        // 消息发送
        OapiMessageCorpconversationAsyncsendV2Response dingResult = client.execute(request, accessToken);
        JSONObject json = JSONObject.fromObject(dingResult);

        return json.get("errcode").equals("0");
    }

备注:

  • 因为钉钉暂时不支持获取全量的通讯录信息,因此笔者采用通过号码校验的方式来判断通讯录人员是否存在,此处会存在资源浪费和效率地下的情况,大家可以考虑采用更优的方案解决。
  • 钉钉消息通知的次数存在上限,具体信息参考官方开发者文档。