接入企业微信审批开发记录

背景
客户提了关于对接企业微信【审批】功能的需求,具体需求包括:
1、当企业微信审批流程到达某个节点后,能将审批信息推送到我们系统,或者我们系统能够拉取某个流程的信息;
2、能在审批流程中嵌套一个评价节点,跳转我们系统的评价页面。

1 调研企业微信是否支持

根据这篇博文,可知企业微信支持 审批回调 ,这个在官方文档中也可以找到。所以1中 当企业微信审批流程到达某个节点后,能将审批信息推送到我们系统是可以做到的。
对于2中的需求,经调研,发现企业微信支持定制审批流程的 模版 ,可以在 模版 中加上一个 说明文字 的控件,在控件中插入链接,然后靠用户的自觉性,在某个节点点击链接去评价,这个实现客户也是认可的。

2 流程梳理

审批回调 自然需要配置回调接口,企业微信的逻辑是先在企业微信中 自建 一个应用,然后给这个自建应用进行 接收消息服务器配置 ,这个 接收消息服务器配置 就是回调的url,然后在 审批 中配置 开启回调通知的模版可调用接口的应用 ,这样开启了企业微信审批申请状态变化回调。

2.1 自建应用

在这里先讲一下如何创建一个企业用于自己开发测试,移动端比较简单,左侧点击图标新建即可。
首先在PC端的企业微信上进入工作台

接着随便点击一个应用,比如审批,点击右上角的图标前往管理后台

然后就会打开浏览器,点击顶部的 应用管理 菜单,可以看大下面有 创建应用 的选项

点击创建,填入必填项,创建成功

接着就是配置应用中的一些信息,上面的这个 AgentId 是应用的ID,那个 Secret 是应用的凭证密钥,这个在获取企业微信access_token的时候需要

接下来往下拖,点击那个 API接收消息,我这里配置过了,显示的是已启用

可以看到这里有3个参数,下面2个是点击右边的 随机获取 按钮随机获取的,第1个参数URL,可以参考边上的获取帮助链接,这个是企业微信的验证URL,这里的实现放在2.2

还需要配置 企业可信IP企业可信IP 是配置哪些IP可以通过API获取企业数据,比如审批流程信息

2.2 回调接口实现

这一节说明的是企业微信应用验证URL和回调接口的实现。
首先,需要下载企业微信的加解密库,选择XML版本,下面的代码依赖于这些。

点击查看代码
package com.springboot.demo.scheduled;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.springboot.demo.aes.WXBizMsgCrypt;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StringReader;

/**
 * 企业微信审批状态变化回调
 *
 * @author Lip
 * @since 2024-12-31
 */
@Slf4j
@RestController
@RequestMapping("qy")
public class QywxCallbackController {

    @GetMapping(value = "/Callback")
    public void connect(HttpServletRequest request, HttpServletResponse response) {
        // 企业号将发送GET请求到填写的URL上,GET请求携带四个参数,企业在获取时需要做url_decode处理,否则会验证不成功

        // 微信加密签名
        String msgSignature = request.getParameter("msg_signature");
        // 时间戳
        String timestamp = request.getParameter("timestamp");
        // 随机数
        String nonce = request.getParameter("nonce");
        // 随机字符串
        String echoStr = request.getParameter("echostr");

        // 自建应用中生成的
        String contactsToken = "5AsIzFkiXMaa";
        String contactsEncodingAesKey = "GwJH5XOLPSmlTGJ2qKEcIy1k3wGg4rsFO51Df7woNsi";
        // 企业ID
        String corpId = "ww1f0eebf7e9d7c54d";

        // 回调key值
        String sEchoStr;
        try {
            PrintWriter out = response.getWriter();
            WXBizMsgCrypt wxCrypt = new WXBizMsgCrypt(contactsToken, contactsEncodingAesKey, corpId);
            sEchoStr = wxCrypt.VerifyURL(msgSignature, timestamp, nonce, echoStr);

            if (StringUtils.isBlank(sEchoStr)) {
                log.error("URL验证失败");
            }
            out.write(sEchoStr);
            out.flush();
        } catch (Exception e) {
            log.error("企业微信回调url验证错误", e);
        }
    }

    @PostMapping(value = "/Callback")
    public void acceptMessage(HttpServletRequest request) {
        System.out.println("————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————");
        log.info("企业微信信息交互");

        // 微信加密签名
        String sMsgSignature = request.getParameter("msg_signature");
        // 时间戳
        String sTimestamp = request.getParameter("timestamp");
        // 随机数
        String sNonce = request.getParameter("nonce");
        System.out.println("acceptMessage方法sMsgSignature: " + sMsgSignature);
        System.out.println("acceptMessage方法sTimestamp: " + sTimestamp);
        System.out.println("acceptMessage方法sNonce: " + sNonce);

        try {
            // 获取请求的输入流
            ServletInputStream inputStream = request.getInputStream();

            // 创建一个 StringBuilder 对象来存储请求内容
            StringBuilder xmlContent = new StringBuilder();

            // 使用 BufferedReader 读取输入流内容
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
            String line;
            while ((line = reader.readLine()) != null) {
                xmlContent.append(line);
            }

            // 关闭输入流和读取器
            inputStream.close();
            reader.close();

            // 输出请求内容
            System.out.println("请求 XML 内容:" + xmlContent);
            String sReqData = xmlContent.toString();
//          String sReqData = "<xml><ToUserName><![CDATA[wx5823bf96d3bd56c7]]></ToUserName><Encrypt><![CDATA[RypEvHKD8QQKFhvQ6QleEB4J58tiPdvo+rtK1I9qca6aM/wvqnLSV5zEPeusUiX5L5X/0lWfrf0QADHHhGd3QczcdCUpj911L3vg3W/sYYvuJTs3TUUkSUXxaccAS0qhxchrRYt66wiSpGLYL42aM6A8dTT+6k4aSknmPj48kzJs8qLjvd4Xgpue06DOdnLxAUHzM6+kDZ+HMZfJYuR+LtwGc2hgf5gsijff0ekUNXZiqATP7PF5mZxZ3Izoun1s4zG4LUMnvw2r+KqCKIw+3IQH03v+BCA9nMELNqbSf6tiWSrXJB3LAVGUcallcrw8V2t9EL4EhzJWrQUax5wLVMNS0+rUPA3k22Ncx4XXZS9o0MBH27Bo6BpNelZpS+/uh9KsNlY6bHCmJU9p8g7m3fVKn28H3KDYA5Pl/T8Z1ptDAVe0lXdQ2YoyyH2uyPIGHBZZIs2pDBS8R07+qN+E7Q==]]></Encrypt><AgentID><![CDATA[218]]></AgentID></xml>";

            String contactsToken = "5AsIzFkiXMaa";
            String contactsEncodingAesKey = "GwJH5XOLPSmlTGJ2qKEcIy1k3wGg4rsFO51Df7woNsi";
            String corpId = "ww1f0eebf7e9d7c54d";

            WXBizMsgCrypt wxcpt = new WXBizMsgCrypt(contactsToken, contactsEncodingAesKey, corpId);
            String sMsg = wxcpt.DecryptMsg(sMsgSignature, sTimestamp, sNonce, sReqData);
            System.out.println("after decrypt msg: " + sMsg);

            // TODO: 解析出明文xml标签的内容进行处理
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            DocumentBuilder db = dbf.newDocumentBuilder();
            StringReader sr = new StringReader(sMsg);
            InputSource is = new InputSource(sr);
            Document document = db.parse(is);

            Element root = document.getDocumentElement();
            NodeList nodelist1 = root.getElementsByTagName("SpNo");
            // 获取审批编号
            String SpNo = nodelist1.item(0).getTextContent();
            System.out.println("审批编号为:" + SpNo);
            NodeList SpStatus_Node = root.getElementsByTagName("SpStatus");
            // 申请单状态:1-审批中;2-已通过;3-已驳回;4-已撤销;6-通过后撤销;7-已删除;10-已支付
            String SpStatus = SpStatus_Node.item(0).getTextContent();
            NodeList SpRecord_Node = root.getElementsByTagName("SpRecord");
            for (int i = 0; i < SpRecord_Node.getLength(); i++) {
                System.out.println("审批记录第" + (i + 1) + "个节点审批状态为:" + SpRecord_Node.item(i).getFirstChild().getTextContent());
            }
        } catch (Exception e) {
            log.error("企业微信消息交互错误", e);
        }
    }
}

这两个接口的路径是一样的,GET方法是验证接口,POST方法是回调接口。

2.3 审批流程模板添加链接




看图,3步搞定

posted @ 2025-01-03 16:55  大唐冠军侯  阅读(979)  评论(0)    收藏  举报