企业微信JS-SDK是企业微信面向网页开发者提供的基于企业微信内的网页开发工具包。
网页开发者可借助企业微信高效地使用拍照、选图、语音、位置等手机系统的能力,同时可以直接使用企业微信分享、扫一扫等企业微信特有的能力
引入js文件
<script src="//res.wx.qq.com/open/js/jweixin-1.2.0.js"></script>
配置config
所有需要使用JS-SDK的页面必须先注入配置信息,否则将无法调用
wx.config({
beta: true,// 必须这么写,否则wx.invoke调用形式的jsapi会有问题
debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: '', // 必填,企业微信的corpID
timestamp: , // 必填,生成签名的时间戳
nonceStr: '', // 必填,生成签名的随机串
signature: '',// 必填,签名,见 附录-JS-SDK使用权限签名算法
jsApiList: [] // 必填,需要使用的JS接口列表,凡是要调用的接口都需要传进来
});
签名生成规则如下:
参与签名的参数有四个: noncestr(随机字符串), jsapi_ticket, timestamp(时间戳), url(当前网页的URL, 不包含#及其后面部分),必须严格符合下面的格式:
jsapi_ticket=JSAPITICKET&noncestr=NONCESTR×tamp=TIMESTAMP&url=URL
- 次序不能更改
- 字段值采用原始值,url不进行转义
最后使用sha1进行加密
jsapi_ticket的有效期为7200秒,通过access_token来获取
请求方式:GET(HTTPS)
请求URL:https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket?access_token=ACCESS_TOKEN
返回结果:
{
"errcode":0,
"errmsg":"ok",
"ticket":"bxLdikRXVbTPdHSM05e5u5sUoXNKd8-41ZO3MhKoyN5OfkWITDGgnr2fwJ0m9E8NYzWKVZvdVtaUgWvsdshFKA",
"expires_in":7200
}
实现代码
url = "https://qyapi.weixin.qq.com/cgi-bin/ticket/get"
url_list = [
url,
"?access_token=",
access_token,
]
apiurl = "".join(url_list)
response = requests.get(apiurl)
print(response.json())
应用的jsapi_ticket用于计算agentConfig,和企业的获取方法类似
- 必须用wx.agentConfig中的agentid对应的应用去获取access_token。
- 签名用的noncestr和timestamp必须与wx.agentConfig中的nonceStr和timestamp相同。
请求方式:GET(HTTPS)
请求URL:https://qyapi.weixin.qq.com/cgi-bin/ticket/get?access_token=ACCESS_TOKEN&type=agent_config
实现代码
url = "https://qyapi.weixin.qq.com/cgi-bin/ticket/get"
url_list = [
url,
"?access_token=",
access_token,
"&type=agent_config" # 添加这一行
]
apiurl = "".join(url_list)
response = requests.get(apiurl)
print(response.json())
注意:获取jsapi_ticket的api调用次数非常有限(一小时内,一个企业最多可获取400次,且单个应用不能超过100次),频繁刷新jsapi_ticket会导致api调用受限,影响自身业务,开发者必须在自己的服务全局缓存应用的jsapi_ticket。
可以使用Redis进行存储,这里我用的本地缓存,通过判断创建时间,每一次服务启动过程中变量只保存一份
# 全局存储
jsapi_ticket = {"jsapi_ticket": "", "expires_in": 0, "datetime": None}
def getJsapiTicket(self, access_token):
"""获取jsapi_ticket"""
# TODO: jsapi-ticket缓存
now = datetime.datetime.now() # 获取创建时间
if QWeiXinUtils.jsapi_ticket.get("datetime") is None: # 判断是否生成
QWeiXinUtils.jsapi_ticket['datetime'] = now # 如果没有先记录创建时间,否则按原有的创建时间完成后续计算
timeval = (now - QWeiXinUtils.jsapi_ticket['datetime']).total_seconds() # 计算jsapi-ticket的创建时长
print("timeval:", timeval)
# 创建一个新的jsapi_ticket
if QWeiXinUtils.jsapi_ticket.get("jsapi_ticket") == "" or QWeiXinUtils.jsapi_ticket["expires_in"] <= timeval:
print("jsapi_ticket is null")
url = "https://qyapi.weixin.qq.com/cgi-bin/ticket/get"
url_list = [
url,
"?access_token=",
access_token,
"&type=agent_config" # agentConfig中使用的
]
apiurl = "".join(url_list)
response = requests.get(apiurl)
QWeiXinUtils.jsapi_ticket["jsapi_ticket"] = response.json().get(
'jsapi_ticket')
QWeiXinUtils.jsapi_ticket["expires_in"] = response.json().get( # 记录过期时间,其实可以固定7200
'expires_in')
# 记录access_token创建的时间
QWeiXinUtils.jsapi_ticket['datetime'] = now
print("---------------", QWeiXinUtils.jsapi_ticket)
return QWeiXinUtils.jsapi_ticket["jsapi_ticket"] # 返回jsapi_ticket
当同时需要配置企业jsapi_ticket和应用jsapi_ticket的时候,二者不能共同使用(api获取都不一样),需要分别缓存
在获取jsapi_ticket后,对其他三个字段共同加密:
jsapi_ticket=JSAPITICKET&noncestr=NONCESTR×tamp=TIMESTAMP&url=URL
SHA-1, 一种密码散列函数,具有哈希函数的特性:
不可以从消息摘要中复原信息;两个不同的消息不会产生同样的消息摘要。
python实现:
def getSHA1(self, timestamp, nonceStr, jsapi_ticket, url):
data = "jsapi_ticket=%s&noncestr=%s×tamp=%s&url=%s" % (
jsapi_ticket, nonceStr, timestamp, url)
sha = hashlib.sha1(data.encode("utf8"))
return sha.hexdigest()
以下内容直接参照企业微信文档接口即可
wx.invoke('openUserProfile', {
"type": 1, //1表示该userid是企业成员,2表示该userid是外部联系人
"userid": "xxx" //可以是企业成员,也可以是外部联系人
}, function (res) {
if (res.err_msg != "openUserProfile:ok") {
//错误处理
}
console.log("=>", res)
});
wx.invoke("selectEnterpriseContact", {
"fromDepartmentId": 0,// 必填,表示打开的通讯录从指定的部门开始展示,-1表示自己所在部门开始, 0表示从最上层开始
"mode": "multi",// 必填,选择模式,single表示单选,multi表示多选
"type": ["department", "user"],// 必填,选择限制类型,指定department、user中的一个或者多个
}, (res) => {
if (res.err_msg == "selectEnterpriseContact:ok") {
if (typeof res.result == 'string') {
res.result = JSON.parse(res.result) //由于目前各个终端尚未完全兼容,需要开发者额外判断result类型以保证在各个终端的兼容性
}
var selectedDepartmentList = res.result.departmentList;// 已选的部门列表
for (var i = 0; i < selectedDepartmentList.length; i++) {
var department = selectedDepartmentList[i];
var departmentId = department.id;// 已选的单个部门ID
var departemntName = department.name;// 已选的单个部门名称
}
let userList = res.result.userList; // 已选的成员列表
this.selectedUserList = []
for (var i = 0; i < userList.length; i++) {
var user = userList[i];
var userId = user.id; // 已选的单个成员ID
var userName = user.name;// 已选的单个成员名称
var userAvatar = user.avatar;// 已选的单个成员头像
console.log(userId, userName, userAvatar)
var obj = new Object()
obj.userid = userId
obj.username = userName
obj.userAvatar = userAvatar
obj.text = ''
obj.money = ''
this.selectedUserList.push(obj)
}
this.show = true;
}
}
);