目标:
基于H5开发项目,嵌入到企业微信中,并接入js-sdk 并运用企业微信接口。
开发环境:
腾讯企业微信最新版本。后端环境不做要求,能跑通即可(这里我选用的是eclipse的maven项目,之前也出了一篇搭建后台项目的博客,可以了解一下)
需要工具:
企业微信注册。花生壳开通内网穿透(花费6元)
一、创建项目 能运行即可
随便一个能运行的项目即可。
二、花生壳配置
参考另一篇文章:https://blog.csdn.net/zh5220909/article/details/81188185
三、申请企业微信
(1)安卓手机上下载 企业微信APP
(2)直接使用微信登录即可,需要新建一个公司,直接根据提示申请一个测试的即可
(3)完成新建公司,则可以去登录管理端了。管理端网址:https://work.weixin.qq.com/wework_admin/loginpage_wx?from=myhome
(4)使用企业微信扫一扫登录即可
(5)创建一个应用
设置网页授权JS-SDK
设置成功后
四、前端集成js-sdk
直接上页面代码,页面上的注释注意看看
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="viewport"
content="width=device-width, initial-scale=1, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta charset="utf-8" />
<meta content="telephone=no" name="format-detection" />
<meta name="viewport"
content="width=device-width, initial-scale=1, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<title>企业微信</title>
<script type="text/javascript" src="http://code.jquery.com/jquery-latest.js"></script>
<!-- 企业微信的JS-SDK -->
<script type="text/javascript" src="http://res.wx.qq.com/open/js/jweixin-1.2.0.js"></script>
<style type="text/css">
.daiban {
width: 60%;
height: 40px;
text-align: center;
line-height: 2.5;
border: 0px solid #ddd;
background-color: yellow;
margin: 20px 20% 0px 20%;
border-radius: 5px;
}
.genzong {
width: 60%;
height: 40px;
text-align: center;
line-height: 2.5;
border: 0px solid #ddd;
background-color: pink;
margin: 20px 20% 0px 20%;
border-radius: 5px;
}
</style>
</head>
<body>
<div id="a1" class="daiban">待办</div>
<div id="a2" class="genzong">交换跟踪</div>
<script type="text/javascript">
//初始化方法
function init() {
getToken();
bindClick();
}
//从后台获取wx.config中所需要的参数
function getToken() {
$.ajax({
url : "/zboxService/zwwx/getSignature",
type : "get",
dataType : "json",
success : function(data) {
console.log(data)
var json = data;
wx.config({
beta : true,// 必须这么写,否则wx.invoke调用形式的jsapi会有问题
debug : false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId : 'ww01c11527d43106b5', // 必填,企业微信的corpID
timestamp : json.timestamp, // 必填,生成签名的时间戳
nonceStr : json.noncestr, // 必填,生成签名的随机串
signature : json.signature,// 必填,签名,见附录1
jsApiList : [ 'checkJsApi', 'chooseImage',
'openEnterpriseChat' ]
// 必填,需要使用的JS接口列表,所有JS接口列表见附录2
});
wx.ready(function() {
wx.checkJsApi({
jsApiList : [ 'chooseImage' ], // 需要检测的JS接口列表,所有JS接口列表见附录2,
success : function(ress) {
alert(2)
// 以键值对的形式返回,可用的api值true,不可用为false
// 如:{"checkResult":{"chooseImage":true},"errMsg":"checkJsApi:ok"}
}
});
});
}
})
}
//绑定两个按钮的点击事件
function bindClick() {
//这个接口是打开userIds对话框,运行则微信端直接跳到聊天框
$("#a1").click(function() {
wx.openEnterpriseChat({
// 注意:userIds和externalUserIds至少选填一个,且userIds+openIds总数不能超过2000。
userIds : 'DingSaiSai02;ZhuHao', //参与会话的企业成员列表,格式为userid1;userid2;...,用分号隔开。
externalUserIds : '', // 参与会话的外部联系人列表,格式为userId1;userId2;…,用分号隔开。
groupName : '讨论组', // 必填,会话名称。单聊时该参数传入空字符串""即可。
success : function(res) {
// 回调
},
fail : function(res) {
if (res.errMsg.indexOf('function not exist') > -1) {
alert('版本过低请升级')
}
}
});
});
//这是选择本地图片的接口
$("#a2").click(function() {
wx.chooseImage({
count : 1, // 默认9
sizeType : [ 'original', 'compressed' ], // 可以指定是原图还是压缩图,默认二者都有
sourceType : [ 'album', 'camera' ], // 可以指定来源是相册还是相机,默认二者都有
defaultCameraMode : "batch", //表示进入拍照界面的默认模式,目前有normal与batch两种选择,normal表示普通单拍模式,batch表示连拍模式,不传该参数则为normal模式。(注:用户进入拍照界面仍然可自由切换两种模式)
success : function(res) {
var localIds = res.localIds; // 返回选定照片的本地ID列表,
// andriod中localId可以作为img标签的src属性显示图片;
// 而在IOS中需通过上面的接口getLocalImgData获取图片base64数据,从而用于img标签的显示
}
});
});
}
init();
</script>
</body>
</html>
后台代码,本人直接封装成了工具类
constens文件,里面写了几个对接企业微信的远程接口URL及参数格式
package com.hao.qywx.web;
public class ZwwxPostTencentConstants {
/**
* 获取企业微信token地址及对应参数
*/
public static final String QYWX_GET_TOKEN_URL = "https://qyapi.weixin.qq.com/cgi-bin/gettoken";
public static final String QYWX_GET_TOKEN_URL_PARAM_CORPID = "corpid";
public static final String QYWX_GET_TOKEN_URL_PARAM_CORPSECRET = "corpsecret";
/**
* 获取企业微信token返回成功对应errcode值
*/
public static final String QYWX_GET_TOKEN_RETURN_SUCCESS_CODE = "0";
public static final String QYWX_GET_TOKEN_RETURN_ERRCODE = "errcode";
public static final String QYWX_GET_TOKEN_RETURN_ERRMSG = "errmsg";
public static final String QYWX_GET_TOKEN_RETURN_TOKEN = "access_token";
/**
* 获取企业微信引入JS-SDK的ticket地址及对应参数
*/
public static final String QYWX_GET_JSAPITICKET_URL = "https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket";
public static final String QYWX_GET_JSAPITICKET_URL_PARAM_TOKEN = "access_token";
public static final String QYWX_GET_JSAPITICKET_URL_PARAM_TICKET = "jsapi_ticket";
public static final String QYWX_GET_JSAPITICKET_URL_PARAM_NONCESTR = "noncestr";
public static final String QYWX_GET_JSAPITICKET_URL_PARAM_TIMESTAMP = "timestamp";
public static final String QYWX_GET_JSAPITICKET_URL_PARAM_URL = "url";
public static final String QYWX_GET_JSAPITICKET_RETURN_SIGNATURE = "signature";
/**
* 获取用户ticket参数
*/
public static final String QYWX_GET_JSAPITICKET_RETURN_SUCCESS_CODE = "0";
public static final String QYWX_GET_JSAPITICKET_RETURN_ERRCODE = "errcode";
public static final String QYWX_GET_JSAPITICKET_RETURN_ERRMSG = "errmsg";
public static final String QYWX_GET_JSAPITICKET_RETURN_TICKET = "ticket";
/**
* 通过code获取用户信息url及其对应参数
*/
public static final String QYWX_GET_USERINFO_URL = "https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo";
public static final String QYWX_GET_USERINFO_URL_PARAM_TOKEN = "access_token";
public static final String QYWX_GET_USERINFO_URL_PARAM_CODE = "code";
/**
* 通过code获取用户信息返回参数
*/
public static final String QYWX_GET_USERINFO_RETURN_SUCCESS_CODE = "0";
public static final String QYWX_GET_USERINFO_RETURN_ERRCODE = "errcode";
public static final String QYWX_GET_USERINFO_RETURN_ERRMSG = "errmsg";
public static final String QYWX_GET_USERINFO_RETURN_USERID = "UserId";
/**
* 企业微信的CorpID,在企业微信管理端查看
*/
public static final String QYWX_CORPID = "";
/**
* 授权方的网页应用ID,在具体的网页应用中查看
*/
public static final String QYWX_AGENTID = "";
/**
* 应用的凭证密钥,在具体的网页应用中查看
*/
public static final String QYWX_CORPSECRET = "";
/**
* 参数连接符
*/
public static final String QYWX_AND = "&";
public static final String QYWX_EQUAL = "=";
public static final String QYWX_QUERY = "?";
}
工具类,
package com.hao.qywx.web;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import com.alibaba.fastjson.JSONObject;
public class ZwwxPostTencentUtil {
/**
* @name 中文名称
* @description 根据页面URL和页面ticket生成接入JS-SDK接入码
* @time 创建时间:2018年7月23日19:58:05
* @param url:接入js-sdk的页面地址
* ticket:通过token生成的接入js-sdk的ticket
* @return 请求返回接入js-sdk所需json对象
* @author 朱浩
* @history 修订历史(历次修订内容、修订人、修订时间等)
*/
public static JSONObject getSignature(String url, String ticket) {
JSONObject rul = new JSONObject();
String noncestr = getRandomString(16);
String timestamp = (int)(System.currentTimeMillis()/1000)+"";
String sign = "";
sign += ZwwxPostTencentConstants.QYWX_GET_JSAPITICKET_URL_PARAM_TICKET
+ ZwwxPostTencentConstants.QYWX_EQUAL
+ ticket
+ ZwwxPostTencentConstants.QYWX_AND
+ ZwwxPostTencentConstants.QYWX_GET_JSAPITICKET_URL_PARAM_NONCESTR
+ ZwwxPostTencentConstants.QYWX_EQUAL
+ noncestr
+ ZwwxPostTencentConstants.QYWX_AND
+ ZwwxPostTencentConstants.QYWX_GET_JSAPITICKET_URL_PARAM_TIMESTAMP
+ ZwwxPostTencentConstants.QYWX_EQUAL + timestamp
+ ZwwxPostTencentConstants.QYWX_AND
+ ZwwxPostTencentConstants.QYWX_GET_JSAPITICKET_URL_PARAM_URL
+ ZwwxPostTencentConstants.QYWX_EQUAL + url;
String signature = "";
try {
// 指定sha1算法
MessageDigest digest = MessageDigest.getInstance("SHA-1");
digest.update(sign.getBytes());
// 获取字节数组
byte messageDigest[] = digest.digest();
// Create Hex String
StringBuffer hexString = new StringBuffer();
// 字节数组转换为 十六进制 数
for (int i = 0; i < messageDigest.length; i++) {
String shaHex = Integer.toHexString(messageDigest[i] & 0xFF);
if (shaHex.length() < 2) {
hexString.append(0);
}
hexString.append(shaHex);
}
signature = hexString.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
rul.put(ZwwxPostTencentConstants.QYWX_GET_JSAPITICKET_RETURN_SIGNATURE,
signature);
rul.put(ZwwxPostTencentConstants.QYWX_GET_JSAPITICKET_URL_PARAM_NONCESTR,
noncestr);
rul.put(ZwwxPostTencentConstants.QYWX_GET_JSAPITICKET_URL_PARAM_TIMESTAMP,
timestamp);
return rul;
}
/**
* @name 中文名称
* @description 根据扫码登录返回的code值和应用token值获取扫码登录人用户id(注:id为企业微信扫码人id)
* @time 创建时间:2018年7月23日14:07:28
* @param code
* :前台扫码登录后微信js插件会自动跳转链接,且附带该code。 accessToken: 应用调用接口凭证,
* 该类下getTencentToken()方法可直接获取。
* @return 请求返回userid
* @author 朱浩
* @history 修订历史(历次修订内容、修订人、修订时间等)
*/
public static String getTencentUserInfo(String code, String accessToken) {
String url = "";
url += ZwwxPostTencentConstants.QYWX_GET_USERINFO_URL
+ ZwwxPostTencentConstants.QYWX_QUERY;
url += ZwwxPostTencentConstants.QYWX_GET_USERINFO_URL_PARAM_TOKEN
+ ZwwxPostTencentConstants.QYWX_EQUAL + accessToken;
url += ZwwxPostTencentConstants.QYWX_AND;
url += ZwwxPostTencentConstants.QYWX_GET_USERINFO_URL_PARAM_CODE
+ ZwwxPostTencentConstants.QYWX_EQUAL + code;
JSONObject userInfoJson = sendPostRequest(url);
String errcode = userInfoJson
.getString(ZwwxPostTencentConstants.QYWX_GET_USERINFO_RETURN_ERRCODE);
if (ZwwxPostTencentConstants.QYWX_GET_USERINFO_RETURN_SUCCESS_CODE
.equals(errcode)) {
return userInfoJson
.getString(ZwwxPostTencentConstants.QYWX_GET_USERINFO_RETURN_USERID);
} else {
}
return null;
}
/**
* @name 中文名称
* @description 获取企业微信Token(应用调用接口凭证) 该值具有以下特性: 1、通用性:该企业微信应用下通用, 2、
* 有效性:该值有效时长为7200秒, 3、微信端不可长期无限制请求,推荐使用缓存保存改值
* @time 创建时间:2018年7月23日14:07:28
* @param 请求URL地址
* ,参数需自行拼接
* @return 请求返回token值
* @author 朱浩
* @history 修订历史(历次修订内容、修订人、修订时间等)
*/
public static String getTencentToken() {
String url = "";
url += ZwwxPostTencentConstants.QYWX_GET_TOKEN_URL
+ ZwwxPostTencentConstants.QYWX_QUERY;
url += ZwwxPostTencentConstants.QYWX_GET_TOKEN_URL_PARAM_CORPID
+ ZwwxPostTencentConstants.QYWX_EQUAL
+ ZwwxPostTencentConstants.QYWX_CORPID;
url += ZwwxPostTencentConstants.QYWX_AND
+ ZwwxPostTencentConstants.QYWX_GET_TOKEN_URL_PARAM_CORPSECRET
+ ZwwxPostTencentConstants.QYWX_EQUAL;
url += ZwwxPostTencentConstants.QYWX_CORPSECRET;
JSONObject tokenJson = sendPostRequest(url);
String errcode = tokenJson
.getString(ZwwxPostTencentConstants.QYWX_GET_TOKEN_RETURN_ERRCODE);
if (ZwwxPostTencentConstants.QYWX_GET_TOKEN_RETURN_SUCCESS_CODE
.equals(errcode)) {
return tokenJson
.getString(ZwwxPostTencentConstants.QYWX_GET_TOKEN_RETURN_TOKEN);
} else {
}
return null;
}
/**
* @name 中文名称
* @description 根据token获取接入js-sdk的ticket
* @time 创建时间:2018年7月23日19:33:47
* @param accessToken
* : 应用调用接口凭证, 该类下getTencentToken()方法可直接获取。
* @return 请求返回ticket值。
* @author 朱浩
* @history 修订历史(历次修订内容、修订人、修订时间等)
*/
public static String getTencentJSSDKTicket(String token) {
String url = "";
url += ZwwxPostTencentConstants.QYWX_GET_JSAPITICKET_URL
+ ZwwxPostTencentConstants.QYWX_QUERY;
url += ZwwxPostTencentConstants.QYWX_GET_JSAPITICKET_URL_PARAM_TOKEN
+ ZwwxPostTencentConstants.QYWX_EQUAL + token;
JSONObject ticketJson = sendPostRequest(url);
String errcode = ticketJson
.getString(ZwwxPostTencentConstants.QYWX_GET_JSAPITICKET_RETURN_ERRCODE);
if (ZwwxPostTencentConstants.QYWX_GET_JSAPITICKET_RETURN_SUCCESS_CODE
.equals(errcode)) {
return ticketJson
.getString(ZwwxPostTencentConstants.QYWX_GET_JSAPITICKET_RETURN_TICKET);
} else {
}
return null;
}
/**
* @name 中文名称
* @description 相关说明
* @time 创建时间:2018年7月23日11:48:33
* @param 请求URL地址
* ,参数需自行拼接
* @return 请求返回json对象
* @author 朱浩
* @history 修订历史(历次修订内容、修订人、修订时间等)
*/
private static JSONObject sendPostRequest(String url) {
StringBuffer stringBuffer = new StringBuffer("");
try {
URL postUrl = new URL(url);
HttpURLConnection connection = (HttpURLConnection) postUrl
.openConnection();
connection.setDoOutput(true);
connection.setDoInput(true);
connection.setRequestMethod("POST");
connection.setUseCaches(false);
connection.setInstanceFollowRedirects(true);
connection.setRequestProperty("Content-Type", "application/json");
BufferedReader reader = new BufferedReader(new InputStreamReader(
connection.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
stringBuffer.append(line);
}
reader.close();
} catch (Exception e) {
e.printStackTrace();
}
return JSONObject.parseObject(stringBuffer.toString());
}
/**
* @name 中文名称
* @description 获取指定位数的随机字符串(包含小写字母、大写字母、数字,0<length)
* @time 创建时间:2018年7月23日14:17:21
* @param 获取字符串长度
* @return 对应长度的随机字符串
* @author 朱浩
* @history 修订历史(历次修订内容、修订人、修订时间等)
*/
private static String getRandomString(int length) {
// 随机字符串的随机字符库
String KeyString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
StringBuffer sb = new StringBuffer();
int len = KeyString.length();
for (int i = 0; i < length; i++) {
sb.append(KeyString.charAt((int) Math.round(Math.random()
* (len - 1))));
}
return sb.toString();
}
}
实现接口
JSONObject rul = new JSONObject();
String token = ZwwxPostTencentUtil.getTencentToken();
String ticket = ZwwxPostTencentUtil.getTencentJSSDKTicket(token);
rul = ZwwxPostTencentUtil.getSignature(url, ticket);
五、踩坑记录
(1)必须有可以外网访问的项目,推荐花生壳内网穿透直接搞定。
(2)在企业微信后台配置js-sdk可信域名的时候需要根据提示下载一个文件放在项目根目录下
(3)网页端是无法调试JS-SDK接口的,包括微信开发工具其实也不是很好用,最好是用手机测试
(4)我在用手机测试的时候一直没有反应,但是在微信开发工具中测试又是有反应的(这里的反应指的是alert),最终在大神的指导下,写在APP从新安装(好吧,这个确实是个坑,当你在微信开发工具中测试有反应,但手机上没反应的话可以试试)
(5)注意一下我获取Signature这段代码,其中的timestamp 单位是秒 秒 秒 !
(6)还有各种坑,可能想不起来了,欢迎各位留言,有可能我就遇到过!
六、各种文献链接
企业微信后台登录链接:https://work.weixin.qq.com/
企业微信开发文档API:https://work.weixin.qq.com/api/doc
花生壳官网:https://hsk.oray.com/
花生壳管理端:https://login.oray.com/login/?tplname=qrcode&s_url=https%3A%2F%2Fhsk.oray.com%2Fconsole%2Fmanage%2F
项目文件 github:https://github.com/zhuhao18/Zbox-qywx