企业微信开发实战:自建审批流引擎

1.概述

企业微信开发实战:自建审批流引擎_第1张图片
企业微信上是这样介绍的。不过经本人的研究测试,该工作流引擎的功能是比较有限的。
首先只有移动端才能发起,流程的定义是必须在企业微信控制台中定义,而且不支持条件分支,适用于比较简单的应用场景,请假之类的。而且审批界面数据展示自定义程度很低。

2.企业微信开发基础

文档链接:
https://work.weixin.qq.com/api/doc#90000/90135/90665
corpid
每个企业都拥有唯一的corpid,获取此信息可在管理后台“我的企业”-“企业信息”下查看“企业ID”(需要有管理员权限)
agentid
每个应用都有唯一的agentid。在管理后台->“应用与小程序”->“应用”,点进某个应用,即可看到agentid。
secret
secret是企业应用里面用于保障数据安全的“钥匙”,每一个应用都有一个独立的访问密钥,为了保证数据的安全,secret务必不能泄漏。
access_token
access_token是企业后台去企业微信的后台获取信息时的重要票据,由corpid和secret产生。所有接口在通信时都需要携带此信息用于验证接口的访问权限

3.审批流引擎开发

文档链接
https://work.weixin.qq.com/api/doc#90000/90135/90269

1.创建自建应用审批模板

企业微信开发实战:自建审批流引擎_第2张图片

2.前端调用页面

1.通过config接口注入权限验证配置。查看
2.通过agentConfig注入应用的权限。查看
3.调用审批流程引擎JS-API(如下文请求示例)。




    
    菜鸟教程(runoob.com)
    

    

    






注意:调试前端页面必须用企业微信客户端,pc版均不支持。

3.获取签名的后台接口

@Api(value="企业微信相关信息获取",description = "企业微信相关信息获取")
@RequestMapping("/weichat-config")
@Controller
public class WeiChatConfigController {
    @Autowired
    ApprovalPrecessService approvalPrecessService;

    @ApiOperation(value = "获取前端登录时需要的签名信息",notes = "获取前端登录时需要的签名信息",httpMethod = "GET")
    @RequestMapping(value = "/getSignature",method = RequestMethod.GET)
    @ResponseBody
    public RestResult getTWeixinConfig(String url){
        WeixinConfigDTO tWeixinConfig = approvalPrecessService.getTweixinConfig(url);
        return new RestResult(tWeixinConfig);
    }
}
public class ApprovalProcessServiceImpl implements ApprovalPrecessService {
//    private static log log = log.getlog(ApprovalProcessServiceImpl.class);
//    private static PropertiesFileUtil propertiesFileUtil = new  PropertiesFileUtil();
//	@Autowired
//	TWeixinConfigMapper tWeixinConfigMapper;

	@Autowired
	private WeichatService weichatService;

	@Override
	public String getSignature(String jsTickt,String noncestr,String timestamp,String url) throws Exception {
				String str = "jsapi_ticket="+jsTickt+"&"+"noncestr="+noncestr+"&"+"timestamp="+timestamp+"&"+"url="+url;
		        return DigestUtils.sha1Hex(str);
	}

	@Override
	public WeixinConfigDTO getTweixinConfig(String url){
		WeixinConfigDTO weixinConfigDTO = new WeixinConfigDTO();
		try {
			String agentId = WeiChatConfigConstants.QIYEWEICHAT_AGENTID;
			String corpId = WeiChatConfigConstants.QIYEWEICHAT_CORPID;
			String secret = WeiChatConfigConstants.QIYEWEICHAT_CORPSECRET;
			String accessToken = weichatService.getWeiChatAccessTokern();
			//企业的jsTickt
			String jsTickt  = CallWxUtil.getJsTicket(accessToken);
			//应用的jsTickt
			String agentJsTickt = CallWxUtil.getAgentJsTicket(accessToken);
			String timestamp=String.valueOf(System.currentTimeMillis());
			String noncestr =getRandomString(16);
			String userUrl=url;
			String signature  = getSignature(jsTickt, noncestr, timestamp, userUrl);
			String agentSignature = getSignature(agentJsTickt, noncestr, timestamp, userUrl);
			weixinConfigDTO.setCorpId(corpId);
			weixinConfigDTO.setAgentId(agentId);
			weixinConfigDTO.setCorpSecret(secret);
			weixinConfigDTO.setSignature(signature);
			weixinConfigDTO.setAgentSignature(agentSignature);
			weixinConfigDTO.setNoncestr(noncestr);
			weixinConfigDTO.setJsapiTicket(jsTickt);
			weixinConfigDTO.setTimestamp(Long.valueOf(timestamp));
			weixinConfigDTO.setAccessToken(accessToken);
		}catch (Exception e){
			log.info(e.getMessage());
			log.info("获取签名异常!!");
		}
		return  weixinConfigDTO;
	}
	
	private static String getRandomString(int length){
		String keyString = "ergrfewfwdgggcvv;uihefujsncjdvngrjegeuirgverggvbergbvuigverug";
		int len = keyString.length();
		StringBuffer str = new StringBuffer();
		for(int i=0;i

获取jsticket 参考文档
https://work.weixin.qq.com/api/doc#10029/获取应用的jsapi_ticket

其中jsticket是签名的重要参数,和access_token一样具有时效性,建议缓存处理。

4.接受审批回调信息的接口

文档参考:https://work.weixin.qq.com/api/doc#90000/90135/90930
首先在控制台配置url为介绍审批回调信息的接口,然后随机生成token,encodingAESkey,建议生成后固定。
企业微信开发实战:自建审批流引擎_第3张图片

企业微信首先会对回调接口进行校验,成功之后便会推送回调信息。
这里GET 为检验接口,
1.对收到的请求,解析上述的各个参数值(参数值需要做Urldecode处理)
2.根据已有的token,结合第1步获取的参数timestamp, nonce, echostr重新计算签名,然后与参数msg_signature检查是否一致,确认调用者的合法性。计算方法参考:消息体签名检验
3.解密echostr参数得到消息内容(即msg字段)
4.在1秒内响应GET请求,响应内容为上一步得到的明文消息内容(不能加引号,不能带bom头,不能带换行符)
POST为推送接口
1.对msg_signature进行校验
2.解密Encrypt,得到明文的消息结构体(消息结构体后面章节会详说)
3.如果需要被动回复消息,构造被动响应包
4.正确响应本次请求
· 企业微信服务器在五秒内收不到响应会断掉连接,并且重新发起请求,总共重试三次
· 当接收成功后,http头部返回200表示接收ok,其他错误码企业微信后台会一律当做失败并发起重试

@Controller
@RequestMapping("/weichat-callBack")
public class WeiChatCallbackController {

    @Autowired
    private WeiChatCallbackService weiChatCallbackService;


    //第一次get请求验证
    @GetMapping(value = "/callBack")
    @ResponseBody
    public String verifyURL(String msg_signature, String timestamp, String nonce, String echostr) {
        return weiChatCallbackService.verifyURL(msg_signature,timestamp,nonce,echostr);
    }

    //接受post请求消息推送
    @PostMapping("/callBack")
    @ResponseBody
    public void callBack(HttpServletRequest request, String msg_signature, String timestamp, String nonce)  {
        weiChatCallbackService.doCallBack(request, msg_signature, timestamp, nonce);
    }
    
}
public class WeiChatCallbackServiceImpl implements WeiChatCallbackService {

    private WXBizMsgCrypt getWXBizMsgCrypt() throws AesException {
        String sToken = WeiChatConfigConstants.QIYEWEICHAT_MSGTOKEN;
        String sCorpID = WeiChatConfigConstants.QIYEWEICHAT_CORPID;
        String sEncodingAESKey = WeiChatConfigConstants.QIYEWEICHAT_ENCODINGAESKEY;
        return new WXBizMsgCrypt(sToken, sEncodingAESKey, sCorpID);
    }


    @Override
    public String verifyURL(String msg_signature, String timestamp, String nonce, String echostr) {
        try {
            WXBizMsgCrypt wxcpt = getWXBizMsgCrypt();
            String sEchoStr; //需要返回的明文
            sEchoStr = wxcpt.VerifyURL(msg_signature, timestamp,
                    nonce, echostr);
            return sEchoStr;
        } catch (Exception e) {
            //验证URL失败,错误原因请查看异常
            e.printStackTrace();
            log.error(e.getMessage());
        }
        return "无";
    }

    @Override
    public void doCallBack(HttpServletRequest request, String msg_signature, String timestamp, String nonce) {

        // post请求的密文数据
        try {
            WXBizMsgCrypt wxcpt = getWXBizMsgCrypt();

            InputStream inputStream = request.getInputStream();
            String sPostData = IOUtils.toString(inputStream,"UTF-8");
            String sMsg = wxcpt.DecryptMsg(msg_signature, timestamp, nonce, sPostData);
            System.out.println("after decrypt msg: " + sMsg);
            // TODO: 解析出明文xml标签的内容进行处理
            // For example:
            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();

            //获取消息类型
            String msgType = getContentByTagName(root,"MsgType");
            System.out.println("MsgType:" + msgType);
            //处理文本信息
            if (WeiChatMsgTypeEnum.TEXT.getValueInFact().equals(msgType)){
                dealWeiChatTextMsg(root);
                //处理事件信息
            }else if (WeiChatMsgTypeEnum.EVENT.getValueInFact().equals(msgType)){
                dealWeiChatEventMsg(root);
            }
        } catch (Exception e) {
            // TODO
            // 解密失败,失败原因请查看异常
            e.printStackTrace();
            log.error(e.getMessage());
        }
    }

    private void dealWeiChatTextMsg(Element root) {
            String content = getContentByTagName(root,"Content");
            System.out.println("Content:" + content);

            content = getContentByTagName(root,"FromUserName");
            System.out.println("FromUserName:" + content);
    }

    private void dealWeiChatEventMsg(Element root) {
        String content = getContentByTagName(root,"OpenSpName");
        System.out.println("审批模板名称:" + content);

        content = getContentByTagName(root,"ApplyUserName");
        System.out.println("提交人姓名:" + content);

        content = getContentByTagName(root,"NodeStatus");
        System.out.println("节点审批状态:" + content);

    }

    private String getContentByTagName(Element root, String tagName){
        NodeList nodelist = root.getElementsByTagName(tagName);
        String Content = nodelist.item(0).getTextContent();
        return Content;
    }

}

解密,签名的工具类可参考官方的demo
https://work.weixin.qq.com/api/doc#90000/90138/90307
下载java 版即可

4.效果测试

这里使用移动端app测试,必须让项目处于公网可信域名下。笔者是通过内网穿透到自己买的域名上,具体可以自己学习研究。
同时在工作台配置
企业微信开发实战:自建审批流引擎_第4张图片
在企业微信上打开前端页面
企业微信开发实战:自建审批流引擎_第5张图片
企业微信开发实战:自建审批流引擎_第6张图片
点击提交

后台回调到审批信息回传信息
企业微信开发实战:自建审批流引擎_第7张图片

你可能感兴趣的:(企业微信开发,企业微信开发)