关于企微群聊天工具功能的开发---PHP+JS+CSS+layui (手把手教学)

news2024/12/25 1:24:33

文章目录

        • 前言
        • 准备工作
        • PHP代码示例
        • 前端代码示例 主要是js
        • 踩的小坑&笔记
        • 最终达成的效果
        • 总结

前言

公司要求开发企微群聊天工具。首先一个客户一个群,其余群成员都是公司销售、设计师、工长、售后等人员。要求开发一个群聊天工具,工长点击进来以后就可以看到群内客户的合同信息,可以上传每天的施工进度等等。
实现思路:获取当前群聊id,将群聊id和客户合同id进行关联。有了合同id,什么就都有了。
本人是后端人员,但是公司要让我前后端都搞。我也没办法,只能用几年前的layui和最基本的js。

准备工作
  1. 登录到企业微信管理后台官网
  2. 应用管理-应用-自建-创建应用在这里插入图片描述
  3. 复制保存好应用的AgentId和Secret。
  4. 配置到聊天工具栏在这里插入图片描述
  5. 配置应用最开始进入时候加载的页面在这里插入图片描述
  6. 配置企业可信IP、启用JS-SDK工具在这里插入图片描述
  7. 找到我的企业-企业ID在这里插入图片描述
  8. 客户与上下游-点击API-配置可调用接口的应用-选择自己刚才的创建应用。让它有调用客户信息的权限。在这里插入图片描述
    准备工作完成!接下来就是代码示例!
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 . "&timestamp=" . $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

<!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>

踩的小坑&笔记
  1. html文件必须在企微环境下运行,不然报错 “wx.invoke is not a function”。(这个很恶心,搞了大半天才知道。)
  2. 企微错误码查询工具
  3. 官方文档的提示:请确保在服务上线前一定全局缓存access_token和jsapi_ticket,两者有效期均为7200秒(以返回结果中的expires_in为准),否则一旦上线触发频率限制,服务将不再可用。
  4. 官方文档中的:常见错误以及解决方法
  5. 客户端调试工具 这个很重要!!!
  6. 当点击页面中某个按钮后,企微会自动登录然后跳转到首页。这时候需要修改企微登录的逻辑。(这是我当前项目中的逻辑,不适用于每一个人!
  7. 一定要先执行wx.agentConfig验签通过后才可以调用wx.invoke.
  8. 要注意,获取群列表的时候,只可以获取当前应用下的群列表!
  9. 在不同应用中获取的同一个群的id也是不同的!(不太确定,记得在文档中看到这句话了)。
最终达成的效果

每一个都是一个自建应用。在手机上显示的话,就会显示到聊天窗口的上面。

在这里插入图片描述

总结

其实这玩意就是企微内嵌了一个浏览器。
还是等于是页面开发。
主要就是验签的那一步,其实也没啥。
就是一开始不知道在企微还得下载一个安装包才可以进行调试。
能够进行调试以后,就一马平川了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1970492.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

selenium自动化代码报错“NoSuchElementException”——解决方案详解

假设自动化代码报错“NoSuchElementException”&#xff1a; 第一步&#xff1a;在报错的代码前&#xff0c;添加Thread.sleep(秒)&#xff0c;设置的时间长一点。 第二步&#xff1a; 执行自动化&#xff0c;在自动化打开的页面里&#xff0c;打开前端开发者工具&#xff0c…

二百五十三、OceanBase——Linux上安装OceanBase数据库(三):OBD页面上部署OceanBase数据库

一、目的 安装OceanBase后&#xff0c;启动obd web&#xff0c;需要在OBD页面上部署OceanBase数据库 二、参考文档 http://t.csdnimg.cn/Qeedq 三、实施步骤 1 在obadmin用户下&#xff0c;启动obd服务&#xff0c;登录页面访问 [obadminhurys23 oceanbase]$ obd web 2 登…

AIGC重塑设施农业:让农事操作更智能,生产效率更高

设施农业是现代农业的重要组成部分,随着人工智能等前沿技术的快速发展,这个领域迎来了新的变革机遇。尤其是大语言模型(Large Language Model,LLM)技术的崛起,其强大的语言理解和知识汇聚能力,为设施农业智能化发展带来了新的想象空间。本文将深入探讨大模型技术在设施农业生产…

寝室恶性负载识别模块原理和功能

石家庄光大远通电气有限公司寝室恶性负载识别智能模块导轨式安装&#xff0c;采用局域网或者4G集中控制&#xff0c;在宿舍多回路中可以单独设置控制参数达到精细化管理。 原理&#xff1a;‌ 电压电流检测法&#xff1a;‌通过检测电路中的电压和电流&#xff0c;‌计算电路中…

网段划分(为什么+分类划分,CIDR),ip地址组成,路由器介绍,广播地址,DHCP,NAT技术,ipv6,如何申请网络

目录 网段划分 引入 ip地址的组成 同一网段内 不同的网段 路由器 ip地址的分配 DHCP技术 分类划分法 分组 弊端 CIDR&#xff08;无类域间路由&#xff09; 变长子网掩码 数据包转发 子网地址范围计算 两个特殊的ip地址 网络号 广播地址 作用 其他解决方…

学习Java的日子 Day62 Filter过滤器

Day62 Filter过滤器 简介 Filter&#xff1a;过滤器&#xff0c;通过Filter可以拦截访问web资源的请求与响应操作。 Servlet API中提供了一个Filter接口&#xff0c;开发web应用时&#xff0c;如果编写的Java类实现了这个接口&#xff0c;则把这个java类称之为过滤器。他可以拦…

数据结构——二叉树_堆

目录 一、堆的概念 二、堆的结构 三、性质 &#xff08;1&#xff09;堆的性质 &#xff08;2&#xff09;二叉树的性质 四、堆的实现 &#xff08;1&#xff09;头文件——Heap.h (2)源文件——Heap.c 1.堆的初始化 2.堆的销毁 3.向上调整算法 4.堆的插入 4.判断堆…

​人工智能薪酬排行榜:2024年哪些岗位最吃香?

近日&#xff0c;智联招聘发布了2024年二季度《中国企业招聘薪酬报告》。报告显示&#xff0c;人工智能行业平均薪酬为13594元/月&#xff0c;位居行业榜首。 同时&#xff0c;在“2024年二季度企业招聘薪酬TOP20职业”中&#xff0c;人工智能工程师以平均月薪22003元排名第一。…

口碑最好的麦克风品牌有哪些,无线领夹麦克风十大品牌推荐

​在内容丰富的自媒体世界里&#xff0c;每一个细节都可能是吸引观众的关键&#xff0c;尤其是音质。面对这么多的无线领夹麦克风&#xff0c;到底哪款更值得入手呢&#xff1f;我试用并挑选了几款性价比高的无线领夹麦克风&#xff0c;它们能够为各类视频制作提供令人印象深刻…

7.5寸电子日历

7.5英寸无线智能纸质显示屏幕办公名牌电子墨水数字显示ESL标签仓库使用

ONVIF 摄像头视频流获取 - 步骤与Python例程

1.基本流程 加入组播udp接口&#xff0c;查询子网内在线的ONVIF摄像头的设备地址&#xff1a; 设备地址形如&#xff1a;http://192.168.0.6/onvif/device_service 这一步&#xff0c;参看上一篇发文&#xff1a;[ONVIF系列 - 01] 简介 - 设备发现 - 相关工具-CSDN博客查询med…

叉车车队管理系统怎么选,满足监管要求!

在现代物流与仓储管理中&#xff0c;选择一个高效且可靠的叉车车队管理系统至关重要。一个高性能的管理系统不仅提升作业效率&#xff0c;还能保障作业安全。企业管理者在采购叉车车队管理系统前需要考虑几个关键因素&#xff1a; 1、提升管理效率&#xff1a;‌确保系统能够实…

机器学习赋能的智能光子学器件系统研究与应用

在人工智能与光子学设计融合的背景下&#xff0c;科研的边界持续扩展&#xff0c;创新成果不断涌现。从理论模型的整合到光学现象的复杂模拟&#xff0c;从数据驱动的探索到光场的智能分析&#xff0c;机器学习正以前所未有的动力推动光子学领域的革新。据调查&#xff0c;目前…

【计算机毕业设计】707高校宿舍管理系统

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

电脑数据有没有办法恢复?适用于 Windows电脑的的 14 款Windows 恢复工具

如果您在笔记本电脑上安装了 Windows 10&#xff0c;却发现文件均未复制到新系统&#xff0c;该怎么办&#xff1f;您可以在线搜索 Windows 10 恢复工具下载&#xff0c;结果会有很多。 确实&#xff0c;优秀的 Windows 恢复工具能够有效地恢复已删除的文件。问题是市场上有太…

为什么有时候银行贷款审核会查大数据信用?

在申请银行贷款时&#xff0c;不少人会疑惑为何银行会深入审查申请人的大数据信用信息。这背后&#xff0c;其实是银行风险控制与精准决策的体现。 首先&#xff0c;大数据信用信用能全面反映申请人的信用状况 它不仅仅局限于传统的征信报告&#xff0c;还涵盖了消费行为、社交…

无线麦克风可以唱歌吗?领夹麦克风十大品牌,麦克风什么牌子好

追求卓越音质与无拘无束表现力的今天&#xff0c;无线领夹麦克风已成为众多创作者、主播、演讲者以及表演艺术家的得力助手。它们不仅提供了更大的自由度和灵活性&#xff0c;还确保了声音的清晰度和传输的稳定性&#xff0c;有专业的装备才能生成高质量的视频作品&#xff0c;…

WPF中调用UWP API

最近在github上看到一个音乐播放器项目&#xff0c;dopamine(项目地址&#xff1a;GitHub - digimezzo/dopamine-windows: Audio player which tries to make organizing and listening to music as simple and pretty as possible.) 在编译时&#xff0c;提示有一个库找不到 …

免费泛域名证书申请(永久免费,不限申请次数)

免费泛域名证书&#xff08;也称为通配符SSL证书&#xff09;的申请过程通常涉及以下几个步骤。以下是一个详细的指南&#xff0c;帮助您了解如何申请免费泛域名证书&#xff1a; 一、选择合适的证书提供商 目前&#xff0c;市场上提供免费泛域名证书的服务商主要有Lets Encr…