再说一次,企业微信开发文档很坑,特别坑,坑成鬼了。

有时候浪费很长时间改bug,查来查去,都没有发现到底哪里错了,查资料,搜资料,询问大神,都没有发现bug。改到最后都抑郁了,才发现用浏览器访问根本就不会报出具体的错误,pc端的企业微信客户端也不行,只有用手机上的企业微信客户端访问才会有具体的错误信息,在这方面浪费了很长时间,感觉自己有点傻,但是内心不承认。

总之,要想一帆风顺开发,听起来像是开玩笑,总会遇到让自己怀疑人生的bug。

文档对于很多细节描述不具体,而且也有很多东西没有demo可以参观,有demo的还是PHP,怎么说呢,有些东西想在网上查查,都查不到,所有,在开始之前,请让我先好好吐槽一下,以解我这几天的心头之火。

好吧,废话不多说,总不能转行吧!所以还是好好学习吧!!

企业微信自建应用审批接口开发:

企业微信自带有审批接口,最主要是,使用企业微信审批能力,在非审批应用内设置流程、发起审批。还能订阅通知消息,接收审批状态变化情况。

现在很多企业都使用企业微信审批接口,使用企业微信审批接口,审批发生变化,会自动发送消息给审批人以及抄送人,比较方便。微信自带的审批接口适用于小企业,简单的业务。一般大公司都会开发适用于自己企业的OA系统。

企业微信自建应用审批接口开发:

1.首先在企业微信后端,创建应用,进入审批接口,添加模板

2.自建应用发起审批

通过JS-SDK,可在自建应用中发起审批,需要设置可信域名,花生壳免费域名,我试了,不行!

3.设置工作台应用主页,在主页内发起审批

注意:

1.测试时一定要用手机企业微信客户端,会显示详细错误信息,用浏览器或者pc端, 比较坑爹。

2.善于利用签名工具,验证自己各数据是否正确生成


1.首先在企业微信后端,创建应用,进入审批接口,添加模板

2.自建应用发起审批

点击接入流程说明,进入开发API

开发逻辑为上图所示

2.1

通过JS-SDK,可在自建应用中发起审批。查看JS-SDK调用详细说明
                具体步骤:
                   1.通过config接口注入权限验证配置
                   2.通过agentConfig注入应用的权限
                   3.调用审批流程引擎JS-API

1.通过config接口注入权限验证配置

wx.config({
        beta: true,// 必须这么写,否则wx.invoke调用形式的jsapi会有问题
        debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
        appId: '', // 必填,企业微信的corpID
        timestamp:'' , // 必填,生成签名的时间戳
        nonceStr: '', // 必填,生成签名的随机串
        signature: '',// 必填,签名,见 附录-JS-SDK使用权限签名算法
        jsApiList: ['agentConfig','thirdPartyOpenPage','selectExternalContact'] // 必填,需要使用的JS接口列表,凡是要调用的接口都需要传进来
    });
    wx.ready(function(){
    
        2.通过agentConfig注入应用的权限。
        wx.agentConfig({
            corpid: '', // 必填,企业微信的corpid,必须与当前登录的企业一致
            agentid: '', // 必填,企业微信的应用id
            timestamp: , // 必填,生成签名的时间戳
            nonceStr: '', // 必填,生成签名的随机串
            signature: '',// 必填,签名,见附录1
            jsApiList: ['thirdPartyOpenPage','selectExternalContact'], //必填
            success: function(res) {
            
                3.调用审批流程引擎JS-API
                wx.invoke('thirdPartyOpenPage', {
                    "oaType": "10001",// String
                    "templateId": "46af67a118a6ebf000002",// String
                    "thirdNo": "thirdNo",// String
                    "extData": {
                        'fieldList': [{
                            'title': '采购类型',
                            'type': 'text',
                            'value': '市场活动',
                        },
                        {
                            'title': '订单链接',
                            'type': 'link',
                            'value': 'https://work.weixin.qq.com',
                        }],
                    }
                },
                function(res) {
                    // 输出接口的回调信息
                    console.log(res);
                });
                
            },
            fail: function(res) {
                if(res.errMsg.indexOf('function not exist') > -1){
                    alert('版本过低请升级')
                }
            }
        });
    });

各个接口都调用成功的话,那么离成功就不远了;

2.1.jsp页面主要代码


<script type="text/javascript">function approval() {//动态获取当前页面urlvar link = window.location.href;$.ajax({type:"GET",data:{"url":link},url:"<%=request.getContextPath()%>/approval/send_approval.do",dataType:"json",success:function (data) {console.log(data);wx.config({beta: true,debug: true,appId: data.appId,timestamp: data.config_timestamp,nonceStr: data.config_nonceStr,signature: data.config_signature,jsApiList: ['agentConfig','openUserProfile','thirdPartyOpenPage','selectExternalContact']});wx.ready(function () {alert("config");wx.agentConfig({corpid: data.appId,agentid: data.agentid,timestamp: data.agent_timestamp,nonceStr: data.agent_nonceStr,signature: data.agent_signature,jsApiList: ['thirdPartyOpenPage','selectExternalContact'],success: function(res) {//审批流程js调用alert("agentConfig");wx.invoke('thirdPartyOpenPage', {"oaType": data.oaType,"templateId": data.templateId,"thirdNo": data.thirdNo,"extData": {'fieldList': [{'title': '采购类型','type': 'text','value': '市场活动',},{'title': '订单链接','type': 'link','value': 'https://work.weixin.qq.com',}],}},function(res) {// 输出接口的回调信息alert("thirdPartyOpenPage");alert(res);});},fail: function(res) {alert("approval提交不通过");alert("agentConfig:"+res.errMsg);if(res.errMsg.indexOf('function not exist') > -1){alert('版本过低请升级')}}});});wx.error(function(res){});}})}</script>

2.2后端主要代码

  @RequestMapping(value = "send_approval.do")public void sendApproval(HttpServletRequest request, HttpServletResponse response,String url)throws Exception{request.setCharacterEncoding("UTF-8");response.setCharacterEncoding("UTF-8");response.setCharacterEncoding("UTF-8");JSONObject jsonObject = iApprovalService.approvalCreate(url).getData();//返回数据到jsp页面response.getWriter().print(jsonObject);}
 public JSONObject approvalCreate(String url){JSONObject object = new JSONObject();//agentConfig接口下主要数据String approval_ticket = JS_Util.getApprovalTicket();JSONObject appJSONObject = JS_Util.getSignature(url,approval_ticket);object.put("agent_nonceStr",appJSONObject.get("nonceStr").toString());object.put("agent_timestamp",appJSONObject.get("timestamp").toString());object.put("agent_signature",appJSONObject.get("signature").toString());System.out.println("===============================");System.out.println(appJSONObject.toString());//config接口下主要数据String ticket = JS_Util.getJsApiTicket();JSONObject jsonObject = JS_Util.getSignature(url,ticket);object.put("config_nonceStr",jsonObject.get("nonceStr").toString());object.put("config_timestamp",jsonObject.get("timestamp").toString());object.put("config_signature",jsonObject.get("signature").toString());System.out.println("===========================");System.out.println(jsonObject.toString());//invoke接口下主要数据object.put("thirdNo",String.valueOf(generateOrderNo()));object.put("appId",PropertiesUtil.getProperty("corpid"));object.put("agentid",PropertiesUtil.getProperty("mycreateapp_agentid"));object.put("templateId",PropertiesUtil.getProperty("approval_id"));object.put("oaType","10001");return object;}

signatrue的生成

 

可以使用签名工具,查看生成的signatrue是否正确,可以快速的排除自己代码的错误

signature生成

签名算法

签名生成规则如下:
参与签名的参数有四个: noncestr(随机字符串), jsapi_ticket, timestamp(时间戳), url(当前网页的URL, 不包含#及其后面部分)

将这些参数使用URL键值对的格式 (即 key1=value1&key2=value2…)拼接成字符串string1。
有两个注意点:1. 字段值采用原始值,不要进行URL转义;2. 必须严格按照如下格式拼接,不可变动字段顺序。

   jsapi_ticket=JSAPITICKET&noncestr=NONCESTR&timestamp=TIMESTAMP&url=URL

然后对string1作sha1加密即可。

注意事项

  1. 签名用的noncestr和timestamp必须与wx.config中的nonceStr和timestamp相同。
  2. 签名用的url必须是调用JS接口页面的完整URL。
  3. 出于安全考虑,开发者必须在服务器端实现签名的逻辑。
 
public class JS_Util {private static Logger logger = LoggerFactory.getLogger(JS_Util.class);//获取企业的jsapi_ticketprivate final static String GET_WX_TICKET = "https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket?access_token=ACCESS_TOKEN";//应用jsapi_tictetprivate final static String GET_APP_TICKET = "https://qyapi.weixin.qq.com/cgi-bin/ticket/get?access_token=ACCESS_TOKEN&type=agent_config";//获取企业的ticketpublic static String getJsApiTicket(){String access_token = WeiXinUtil.getAccessToken(PropertiesUtil.getProperty("corpid"),PropertiesUtil.getProperty("mycreateapp_secret")).getAccess_token();String url = GET_WX_TICKET.replace("ACCESS_TOKEN",access_token);JSONObject jsonObject = WeiXinUtil.httpRequest(url,"GET",null);String ticket = jsonObject.get("ticket").toString();return ticket;}//获取审批流程ticketpublic static String getApprovalTicket(){String access_token = WeiXinUtil.getAccessToken(PropertiesUtil.getProperty("corpid"),PropertiesUtil.getProperty("mycreateapp_secret")).getAccess_token();String url = GET_APP_TICKET.replace("ACCESS_TOKEN",access_token);JSONObject jsonObject = WeiXinUtil.httpRequest(url,"GET",null);String ticket = jsonObject.get("ticket").toString();return ticket;}/*** 参与签名的参数有四个: noncestr(随机字符串), jsapi_ticket, timestamp(时间戳), url(当前网页的URL, 不包含#及其后面部分)* 将这些参数使用URL键值对的格式 (即 key1=value1&key2=value2…)拼接成字符串string1。* 然后对string1作sha1加密即可。* @return*/public static JSONObject getSignature(String url,String ticket){JSONObject jsonObject = new JSONObject();String noncestr = getRandomString(16);String timestamp = Long.toString(System.currentTimeMillis()).substring(0,10);// jsapi_ticket=JSAPITICKET&noncestr=NONCESTR&timestamp=TIMESTAMP&url=URL  顺序不能更改String str = "jsapi_ticket="+ticket+"&noncestr="+noncestr+"&timestamp="+timestamp+"&url="+url;String signature = SHA1(str);jsonObject.put("nonceStr",noncestr);jsonObject.put("timestamp",timestamp);jsonObject.put("signature",signature);jsonObject.put("ticket",ticket);return jsonObject;}private static String SHA1(String decript){try{MessageDigest md = MessageDigest.getInstance("SHA-1");md.update(decript.getBytes());byte[] digest = md.digest();StringBuffer hexstr = new StringBuffer();for (int i = 0; i < digest.length; i++) {String shaHex = Integer.toHexString(digest[i] & 0xFF);if (shaHex.length() < 2) {hexstr.append(0);}hexstr.append(shaHex);}return hexstr.toString();}catch (Exception e){logger.error("sha1加密错误",e);}return "";}private static String getRandomString(int length){String keyString = "ergrfewfwdgggcvv;uihefujsncjdvngrjegeuirgverggvbergbvuigverug";int len = keyString.length();StringBuffer str = new StringBuffer();for(int i=0;i<length;i++){str.append(keyString.charAt((int) Math.round(Math.random() * (len - 1))));}return str.toString();}}

获取企业的ticket和应用的ticket,请求的url不同,但是使用的accessToken是相同的

​public class WeiXinUtil {private static Logger log = LoggerFactory.getLogger(WeiXinUtil.class);//微信的请求url//获取access_token的接口地址(GET)public final static String access_token_url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid={corpId}&corpsecret={corpsecret}";/*** 1.发起https请求并获取结果*/public static JSONObject httpRequest(String requestUrl, String requestMethod, String outputStr) {JSONObject jsonObject = null;StringBuffer buffer = new StringBuffer();try {// 创建SSLContext对象,并使用我们指定的信任管理器初始化TrustManager[] tm = { new MyX509TrustManager() };SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");sslContext.init(null, tm, new java.security.SecureRandom());// 从上述SSLContext对象中得到SSLSocketFactory对象SSLSocketFactory ssf = sslContext.getSocketFactory();URL url = new URL(requestUrl);HttpsURLConnection httpUrlConn = (HttpsURLConnection) url.openConnection();httpUrlConn.setSSLSocketFactory(ssf);httpUrlConn.setDoOutput(true);httpUrlConn.setDoInput(true);httpUrlConn.setUseCaches(false);// 设置请求方式(GET/POST)httpUrlConn.setRequestMethod(requestMethod);if ("GET".equalsIgnoreCase(requestMethod))httpUrlConn.connect();// 当有数据需要提交时if (null != outputStr) {OutputStream outputStream = httpUrlConn.getOutputStream();// 注意编码格式,防止中文乱码outputStream.write(outputStr.getBytes("UTF-8"));outputStream.close();}// 将返回的输入流转换成字符串InputStream inputStream = httpUrlConn.getInputStream();InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");BufferedReader bufferedReader = new BufferedReader(inputStreamReader);String str = null;while ((str = bufferedReader.readLine()) != null) {buffer.append(str);}bufferedReader.close();inputStreamReader.close();// 释放资源inputStream.close();inputStream = null;httpUrlConn.disconnect();jsonObject = JSONObject.fromObject(buffer.toString());} catch (ConnectException ce) {log.error("Weixin server connection timed out.");} catch (Exception e) {log.error("https request error:{}", e);}return jsonObject;}/*** 获取应用的access_token*/public static AccessToken getAccessToken(String appid, String appsecret) {AccessToken accessToken = null;String requestUrl = access_token_url.replace("{corpId}", appid).replace("{corpsecret}", appsecret);JSONObject jsonObject = httpRequest(requestUrl, "GET", null);// 如果请求成功if (null != jsonObject) {try {accessToken = new AccessToken();accessToken.setAccess_token(jsonObject.getString("access_token"));accessToken.setExpires_in(jsonObject.getInt("expires_in"));} catch (JSONException e) {accessToken = null;// 获取token失败log.error("获取token失败 errcode:{} errmsg:{}", jsonObject.getInt("errcode"), jsonObject.getString("errmsg"));}}return accessToken;}
}

主要的参数都被我统一放在了properties文件里,写了个工具类PropertiesUtil进行取出,方便管理。

3.设置工作台应用主页,在主页内发起审批,域名为设置的可信域名,刚开始用的花生壳,没有测试通过,域名所有权验证不通过

测试时一定要用手机企业微信客户端,会显示详细错误信息,用浏览器或者pc端, 比较坑爹。

审批状态回调及审批查询请移步:https://blog.csdn.net/yzhbyssgdbd123/article/details/98171964

企业微信自建应用审批接口开发相关推荐

  1. 企业微信自建应用审批流程踩坑全过程(uni-app)

    1. 引入企业微信sdk: 1-1. 因为uni-app遵循Vue的单文件组件 (SFC) 规范,不能直接在页面上引入 在index.html中引入: <script src="htt ...

  2. python 审批流_Odoo 基于企业微信实现的通用审批流功能的自助配置及使用说明 - Oejia 技术栈,企业方案分享、Odoo顾问...

    ### 概述 以下为基于企业微信自建应用审批流程引擎实现的Odoo通用审批功能,基于企业微信官方审批应用实现的Odoo通用审批功能请移步到 http://oejia.net/blog/2020/05/ ...

  3. 企业微信自建应用开发初探

    企业微信是腾讯推出的一个新的办公协作平台,通过与微信一致的沟通体验,为企业员工提供最基础和最实用的办公服务.同时企业微信作为一个开发平台,企业可以根据需要开发定制自己的企业应用集成到企业微信上.ABC ...

  4. 企业微信自建应用获取用户信息

    记录一下企业微信自建应用获取用户信息开发过程: 1.第一步,获取授权code,这个在原来首页开发不太合适,在进入首页前新建页面进行授权,点击页面按钮进行授权.需要拼接的授权url.其中corpid是企 ...

  5. 企业微信自建应用通过PHP进行收发消息

    我们在企业微信的使用中肯定会用到自建应用,自建应用使用最多的功能就是消息的推送,使用的场景也有很多,例如:在公司内网的程序,可以监控员工在什么时候进行了敏感操作,比如某管理员删除了帐号或者其他内容,可 ...

  6. 企业微信-自建应用一:介绍与创建

    什么是自建应用? 一句话概括就是企业内部自用的应用 创建自建应用 登录企业微信桌面版,点击"开发者中心" 进入到开发者中心,点击"立即创建" 登录企业微信管理后 ...

  7. 通过企业微信内建应用,python搭建重置域账户密码

    企业微信重置域账户密码 1.打开企业微信应用,发送用户名,等待几秒,后台重置完密码,自动返回新密码 2.工作原理: 1.企业微信自建应用程序 2.设置应用api信息 3.防火墙映射服务端IP地址,后端 ...

  8. 实现【企业微信自建应用使用uni-app H5 开发】,并解决【uni-app集成微信JSSDK(wx、jWeixin)变量冲突问题】

    目录 企业微信的开发与调试 企业微信应用主体逻辑 登录(构造网页授权链接) 企业微信SDK配置 demo案例 重要bug修复方法 总结 企业微信的开发与调试 在本地开发基础代码和UI 发布到测试环境进 ...

  9. 企业微信自建引用接口调用报错[ErrorCode:301002]not allow operate another agent with this accesstoken

    1. 首先,在构建构造网页授权链接时,其中的参数agentid需要和你的自建应用id一致. 使用管理员账号登录网页版企业微信,查看AgentId 2. 使用accessToken对比agentid是否 ...

最新文章

  1. (原创)按照一定的格式生成一定数量的随机数的例子
  2. JavaScript的格式--从格式做起,做最严谨的工程师
  3. C语言操作MySQL-----又一个小技巧
  4. 2020\Simulation_2\1.12.5MB
  5. Android开发之线程池管理ThreadPoolExecutor和Executors.newSingleThreadExecutor()
  6. abap 导入队列末尾_在C#中将对象添加到队列的末尾-排队操作
  7. SUBSTR函数的使用
  8. 【HDU1754】I HATE IT,线段树练习
  9. 定期删除网站日志php_tomcat实现定时删除日志
  10. vba listbox 内容输出到文本_利用FSO对象读取文本文件的信息
  11. 《Adobe Illustrator CS6中文版经典教程(彩色版)》—第0课0.14节使用画笔工具
  12. Kudu Partition Tablet Bucket
  13. 【数学与统计基础】常用统计检验方法的Python实现
  14. WebStorm SVN提交performing vcs refresh
  15. 怎样用苹果手机看html文件在哪里,怎么在电脑上打开苹果手机上的文件?
  16. 计算机管理无法打开权限不足,win10 打不开软件管理员权限不够
  17. mr编程实现手机流量统计和读取MySQL数据
  18. EasyCVR实现智慧楼宇道闸控制流程及参考代码分享
  19. Future.get()抛出ExecutionException或InterruptedException?
  20. 关于制作赛车游戏的一些入门知识

热门文章

  1. iPhone照片格式怎么改?
  2. SuperMap iObjects C++在Linux上面的使用
  3. pythonic的典故_旷视开源深度学习框架「天元」,提供人人可用的AI“生产力工具”【星特写】...
  4. 提交代码时用prettier自动格式化
  5. GPS基础知识(五)、GPS导航电文
  6. js脚本根据身份证号获取性别、年龄、家庭地址、生日
  7. 深信服技术支持工程师(安全、云计算方向)面试题目
  8. 收款码在线生成系统源码 无限制
  9. 请试试看每天吃一碗;白头发不见了,给爸妈留
  10. 01-ECAM、发展史、JS值、编程语言、变量