文章目录
前言
公司要求开发企微群聊天工具。首先一个客户一个群,其余群成员都是公司销售、设计师、工长、售后等人员。要求开发一个群聊天工具,工长点击进来以后就可以看到群内客户的合同信息,可以上传每天的施工进度等等。
实现思路:获取当前群聊id,将群聊id和客户合同id进行关联。有了合同id,什么就都有了。
本人是后端人员,但是公司要让我前后端都搞。我也没办法,只能用几年前的layui和最基本的js。
准备工作
- 登录到企业微信管理后台官网
- 应用管理-应用-自建-创建应用
- 复制保存好应用的AgentId和Secret。
- 配置到聊天工具栏
- 配置应用最开始进入时候加载的页面
- 配置企业可信IP、启用JS-SDK工具
- 找到我的企业-企业ID
- 客户与上下游-点击API-配置可调用接口的应用-选择自己刚才的创建应用。让它有调用客户信息的权限。
准备工作完成!接下来就是代码示例!
PHP代码示例
php
function getToken(){
//获取access_token
$url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=" . $company_id . "&corpsecret=" . $secret;
$result = sendCurlRequest($url);
if ($result['errcode'] == 0) {
return ajax_return(200, '获取成功', $result);
}
return ajax_return(500, 'token获取失败');
}
//这个方法很重要 验签!
function getJsApiTicket(){
$access_token = $_REQUEST['access_token'];
//获取企业的jsapi_ticket--这个没用到
// $url = "https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket?access_token=".$access_token;
//获取应用内的jsapi_ticket
$url = "https://qyapi.weixin.qq.com/cgi-bin/ticket/get?access_token=" . $access_token . "&type=agent_config";
$result = sendCurlRequest($url);
if ($result['errcode'] == 0) {
$result['company_id'] = $company_id;
$result['agentid'] = $agentid;
$result['timestamp'] = $time;
$result['nonceStr'] = $nonceStr;
$str = "jsapi_ticket=" . $result['ticket'] . "&noncestr=" . $nonceStr . "×tamp=" . $time . "&url=" . $sign_url;
$result['signature'] = sha1($str);
$result['url'] = $sign_url;
return ajax_return(200, '获取成功', $result);
}
return ajax_return(500, 'jsapi_ticket获取失败', $result);
}
//返回json
function ajax_return($code, $msg, $data = [])
{
exit(json_encode(
[
'code' => $code,
'msg' => $msg,
'data' => $data
]
));
}
//发送curl请求
function sendCurlRequest($url, $data = array(), $method = 'GET')
{
$ch = curl_init();//1.初始化
curl_setopt($ch, CURLOPT_URL, $url);//2.请求地址
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);//3.请求方式
//4.参数如下
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
if ($method == "POST") {//5.post方式的时候添加数据
$data = json_encode($data);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
}
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($ch);
curl_close($ch);
return json_decode($output, true);
}
//生成随机16位的字符串
function generateRandomString($length = 16)
{
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$charactersLength = strlen($characters);
$randomString = '';
for ($i = 0; $i < $length; $i++) {
$randomString .= $characters[rand(0, $charactersLength - 1)];
}
return $randomString;
}
前端代码示例 主要是js
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Document</title>
<link rel="stylesheet" href="https://cdn.staticfile.org/layui/2.5.7/css/layui.css" media="all">
</head>
<body>
<div class="layui-container" style="padding: 10px">
<div class="layui-row">
<div class="layui-col-xs12">
<div id="search" style="display: none">
<div class="layui-input-inline" style="display: flex;justify-content: space-between;">
<input type="text" id="searchInput" name="title" required lay-verify="required"
placeholder="请输入合同号" autocomplete="off" class="layui-input">
<button class="layui-btn" id="searchBtn" style="margin-left: 10px;">搜索</button>
</div>
</div>
<div id="content" style="display: none">
<div class="layui-input-inline" style="display: flex;justify-content: space-between;margin-top: 10px">
<button class="layui-btn" id="contractDetails">合同详情</button>
<button class="layui-btn" id="broadcast">施工播报</button>
<button class="layui-btn" id="acceptLog">验收日志</button>
</div>
<div class="layui-input-inline" style="display: flex;justify-content: space-between;margin-top: 10px">
<button class="layui-btn" id="complaint">客诉整改</button>
<button class="layui-btn" id="satisfaction">满意度评价</button>
<button class="layui-btn" id="change">变更管理</button>
</div>
</div>
<input type="hidden" id="access_token" name="access_token" class="layui-input">
<input type="hidden" id="group_id" name="group_id" value="" class="layui-input">
</div>
</div>
<div id="myTable">
<table class="layui-table" id="simple" lay-filter="simple"></table>
</div>
<script type="text/html" id="barDemo">
<div class="layui-btn-container">
<button class="layui-btn layui-btn-sm" lay-event="bind">绑定</button>
</div>
</script>
</div>
</body>
<script src="https://cdn.staticfile.org/jquery/3.6.0/jquery.min.js"></script>
<script src="https://cdn.staticfile.org/layui/2.5.7/layui.js"></script>
<script src="https://res.wx.qq.com/open/js/jweixin-1.2.0.js"></script>
<script src="https://open.work.weixin.qq.com/wwopen/js/jwxwork-1.0.0.js" referrerpolicy="origin"></script>
<script>
//定义参数
let access_token = "";
$(document).ready(function () {
let group_id = window.sessionStorage.getItem('group_id')
if(!group_id){
//获取token
getAccessToken();
}else{
checkBandContract(group_id)
}
})
// 获取AccessToken,这通常需要企业微信的CorpID和Secret
function getAccessToken() {
$.ajax({
url: "http://",
type: "GET",
dataType: 'json',
contentType: 'application/json',
success: function (res) {
$('#access_token').val(res.data.access_token)
access_token = res.data.access_token
console.log('access_token:',access_token)
//获取企业的jsapi_ticket
getJsApiTicket(access_token);
},
error: function (e) {
console.log(e)
}
})
}
//获取ticket以及群id
function getJsApiTicket(access_token){
$.ajax({
url: "http://getJsApiTicket", //对应php代码中的getJsApiTicket方法
type: "GET",
dataType: 'json',
contentType: 'application/json',
data: {
access_token:access_token,
},
success: function (res) {
wx.agentConfig({
beta:true,
debug:true,
corpid: res.data.company_id, // 必填,企业微信的corpid,必须与当前登录的企业一致
agentid: res.data.agentid, // 必填,企业微信的应用id (e.g. 1000247)
timestamp: res.data.timestamp, // 必填,生成签名的时间戳
nonceStr: res.data.nonceStr, // 必填,生成签名的随机串
signature: res.data.signature, // 必填,签名,见附录-JS-SDK使用权限签名算法
jsApiList: ['getCurExternalChat'], //必填,传入需要使用的接口名称
success: function (res) {
wx.invoke('getCurExternalChat', { }, function (res) {
if (res.err_msg === 'getCurExternalChat:ok') {
let group_id = res.chatId //返回当前外部群的群聊ID
//判断当前群聊是否绑定了合同
checkBandContract(group_id)
//存入到session
window.sessionStorage.setItem('group_id',res.chatId)
} else {
//错误处理
console.log(888888)
}
})
// 回调
},
fail: function (res) {
if (res.err_msg.indexOf('function not exist') > -1) {
alert('版本过低请升级')
}
alert('获取企微群id失败')
},
})
},
error: function (e) {
console.log(e)
}
})
}
//判断当前群聊是否绑定了合同
function checkBandContract(group_id){
$.ajax({
url: "http://",
type: "GET",
dataType: 'json',
data:{
group_id:group_id
},
contentType: 'application/json',
success: function (res) {
if(res.data == 1){
//未绑定
$('#search').css('display','block')
}else if(res.data == 2){
//已绑定
$('#content').css('display','block')
}else{
layer.msg('系统出错!请联系管理员')
}
},
error: function (e) {
console.log(e)
}
})
}
layui.use(['form', 'layer','table'], function () {
let layer = layui.layer;
let table = layui.table;
//搜索点击事件
$('#searchBtn').on('click', function () {
let searchText = $('#searchInput').val();
$.ajax({
url: "http",
type: "GET",
dataType: 'json',
data:{
sn:searchText
},
contentType: 'application/json',
success: function (res) {
if(res.code === 200){
// getTableData(res.data)
table.render({
elem: '#simple',
cellMinWidth: 80, // 全局定义常规单元格的最小宽度,layui2.2.1新增
cols: [[
// 表头,对应数据格式,此示例只设置3格
// 若不填写width列宽将自动分配
{field: 'cid', title: 'ID', width: 80, fixed: 'left'},
{field: 'sn', title: 'SN', width: 120},
{field: 'customer_name', title: '姓名', width: 80},
{field: 'address', title: '地址', width: 220},
{title: '操作', toolbar: "#barDemo"}
]],
// 默认不开启分页(即false),若要开启,设置:
page: true,
// 若不想通过url获取数据也可用data设置赋值已知数据
data: res.data,
toolbar:'#barDemo'
});
console.log('tableData:',res)
}else{
layer.msg(res.msg);
}
},
error: function (e) {
console.log(e)
}
})
});
//工具列点击事件
table.on('tool(simple)', function (obj) {
let event = obj.event;
if (event === 'bind') {
let id = obj.data.cid;
let group_id = window.sessionStorage.getItem('group_id')
console.log('group_id:',group_id)
if (id === '' || id === 'undefined') {
layer.msg('id不能为空');
return;
}
if (group_id === '' || group_id === 'undefined') {
layer.msg('group_id不能为空');
return;
}
layer.confirm('绑定后不可更改,确定绑定吗?', {
btn: ['确定', '取消'] //按钮
}, function () {
$.post('http', {
id: id,
group_id: group_id
}, function (data) {
if (data.code == 200) {
layer.msg(data.msg);
setTimeout(function () {
$('#myTable').css('display','none')
$('#search').css('display','none')
$('#content').css('display','block')
}, '1000');
} else {
layer.msg(data.msg);
}
}, 'JSON');
});
}
});
//合同详情
$('#contractDetails').on('click', function () {
let group_id = window.sessionStorage.getItem('group_id')
$.ajax({
url: "http:",
type: "GET",
dataType: 'json',
data:{
group_id:group_id
},
contentType: 'application/json',
success: function (res) {
if(res.code === 200){
console.log('res===',res)
window.location.href = "";
}else{
console.log('res=====',res)
layer.msg(res.msg);
}
},
error: function (e) {
console.log(e)
}
})
});
//施工播报
//验收日志
//客诉整改
//变更管理
});
</script>
</html>
踩的小坑&笔记
- html文件必须在企微环境下运行,不然报错 "wx.invoke is not a function"。(这个很恶心,搞了大半天才知道。)
- 企微错误码查询工具
- 官方文档的提示:请确保在服务上线前一定全局缓存access_token和jsapi_ticket ,两者有效期均为7200秒(以返回结果中的expires_in为准),否则一旦上线触发频率限制,服务将不再可用。
- 官方文档中的:常见错误以及解决方法
- 客户端调试工具 这个很重要!!!
- 当点击页面中某个按钮后,企微会自动登录然后跳转到首页。这时候需要修改企微登录的逻辑。(这是我当前项目中的逻辑,不适用于每一个人!)
- 一定要先执行
wx.agentConfig
验签通过后才可以调用wx.invoke
. - 要注意,获取群列表的时候,只可以获取当前应用下的群列表!
- 在不同应用中获取的同一个群的id也是不同的!(不太确定,记得在文档中看到这句话了)。
最终达成的效果
每一个都是一个自建应用。在手机上显示的话,就会显示到聊天窗口的上面。
总结
其实这玩意就是企微内嵌了一个浏览器。
还是等于是页面开发。
主要就是验签的那一步,其实也没啥。
就是一开始不知道在企微还得下载一个安装包才可以进行调试。
能够进行调试以后,就一马平川了。