微信企业号在线客服 需求 1.新建项目wechatWork 2.python 安装wechatpy  3.企业号后台设置消息http接口 4.设计model.py  应用管理表 客服用户管理表 5.admin.py 代码 6.Channels的安装和使用(web socket 及时通讯) 7.liunx安装redis 8.接受微信API发送的消息 8.企业微信PC扫码登录 7.运行效果图 3.PC超级管理员(使用企业微信扫码登录系统)

  因企微信企业号后台暂时只提供,历史消息模块,无法适用客服模式,很难及时反馈用户提问的消息,基于企业微信二次开发客服系统,可实现消息实时触达,多客服,知识库等功能

1.新建项目wechatWork

微信企业号在线客服
需求
1.新建项目wechatWork
2.python 安装wechatpy 
3.企业号后台设置消息http接口
4.设计model.py  应用管理表 客服用户管理表
5.admin.py 代码
6.Channels的安装和使用(web socket 及时通讯)
7.liunx安装redis
8.接受微信API发送的消息
8.企业微信PC扫码登录
7.运行效果图
3.PC超级管理员(使用企业微信扫码登录系统)

2.python 安装wechatpy 

官方文档:https://wechatpy.readthedocs.io/zh_CN/master/

pip install wechatpy
# with cryptography (推荐)
pip install wechatpy[cryptography]
# with pycryptodome
pip install wechatpy[pycrypto]

3.企业号后台设置消息http接口

微信企业号在线客服
需求
1.新建项目wechatWork
2.python 安装wechatpy 
3.企业号后台设置消息http接口
4.设计model.py  应用管理表 客服用户管理表
5.admin.py 代码
6.Channels的安装和使用(web socket 及时通讯)
7.liunx安装redis
8.接受微信API发送的消息
8.企业微信PC扫码登录
7.运行效果图
3.PC超级管理员(使用企业微信扫码登录系统)

微信企业号在线客服
需求
1.新建项目wechatWork
2.python 安装wechatpy 
3.企业号后台设置消息http接口
4.设计model.py  应用管理表 客服用户管理表
5.admin.py 代码
6.Channels的安装和使用(web socket 及时通讯)
7.liunx安装redis
8.接受微信API发送的消息
8.企业微信PC扫码登录
7.运行效果图
3.PC超级管理员(使用企业微信扫码登录系统)

4.设计model.py  应用管理表 客服用户管理表

from django.contrib.auth.models import User
from django.db import models
from django.utils.crypto import random
# Create your models here.
from django.utils.html import format_html


def rename(newname):
    def decorator(fn):
        fn.__name__ = newname
        return fn

    return decorator


# 客服人员

class KF(models.Model):
    agent = models.ForeignKey('agent', null=True, to_field="id", on_delete=models.CASCADE, verbose_name="应用名称")
    username = models.CharField(max_length=225, verbose_name="姓名", blank=True, default="")
    userid = models.CharField(max_length=225, verbose_name="UM", blank=True, default="")
    status = models.BooleanField(verbose_name="是否在线")
    avatar = models.URLField(max_length=225, verbose_name="头像", blank=True, default="")
    createTime = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
    lastTime = models.DateTimeField(auto_now=True, verbose_name="修改时间")
    author = models.CharField(max_length=64, verbose_name="创建者", blank=True, default="")
    editor = models.ForeignKey(User, null=True, on_delete=models.CASCADE, verbose_name="修改者")

    class Meta:
        verbose_name_plural = "在线客服"
        ordering = ['id']

    @rename("头像")
    def showAvatar(self):
        return format_html("<img src='{}' class='showAvatar'", self.avatar)

    def __str__(self):
        return self.username


# 行内员工

def randomSign():
    switch = {
        0: "只要还有明天,今天就永远是起跑线。",
        1: "只要还有明天,今天就永远是起跑线。",
        2: "只要还有明天,今天就永远是起跑线。"
    }
    return switch[random.randint(0, len(switch) - 1)]


class userList(models.Model):
    username = models.CharField(max_length=225, verbose_name="姓名", blank=True, default="")
    userid = models.CharField(max_length=225, verbose_name="UM", blank=True, default="")
    avatar = models.URLField(max_length=225, verbose_name="头像", blank=True, default="")
    sign = models.CharField(max_length=225, verbose_name="个性签名", blank=True, default=randomSign)
    agent = models.ForeignKey('agent', null=True, to_field="id", on_delete=models.CASCADE, verbose_name="应用名称")
    createTime = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
    lastTime = models.DateTimeField(auto_now=True, verbose_name="修改时间")
    author = models.CharField(max_length=64, verbose_name="创建者", default="weChat")
    editor = models.ForeignKey(User, null=True, on_delete=models.CASCADE, verbose_name="修改者")

    @rename("头像")
    def showAvatar(self):
        return format_html("<img src='{}' class='showAvatar'", self.avatar)

    class Meta:
        verbose_name_plural = "用户列表"
        ordering = ['id']

    def __str__(self):
        return self.username


# 接受的消息

class Message(models.Model):
    ToUserName = models.CharField(max_length=225, verbose_name="企业号ID", blank=True, default="")
    FromUserName = models.CharField(max_length=225, verbose_name="发送者", blank=True, default="")
    CreateTime = models.DateTimeField(verbose_name="发送时间", blank=True, default=None)
    MsgId = models.CharField(max_length=225, verbose_name="消息ID", blank=True, default="")
    AgentID = models.CharField(max_length=225, verbose_name="应用ID", blank=True, default="")
    MsgType = models.CharField(max_length=225, verbose_name="消息类型", blank=True, default="")
    content = models.TextField(max_length=2000, verbose_name="消息内容", blank=True, default="")
    userList = models.ForeignKey('userList', null=True, to_field="id", on_delete=models.CASCADE)

    class Meta:
        verbose_name_plural = "所有消息"
        ordering = ['id']

    def __str__(self):
        return self.FromUserName

# 企业号应用管理,支持多个应用接入客服系统
class agent(models.Model): name = models.CharField(max_length=225, verbose_name="应用名称", blank=True, default="") agentid = models.CharField(max_length=225, verbose_name="应用ID", blank=True, default="") secret = models.CharField(max_length=225, verbose_name="应用密钥", blank=True, default="") avatar = models.URLField(max_length=225, verbose_name="部门Logo", blank=True, default="") welcomeText = models.TextField(max_length=2000, verbose_name="欢迎语", blank=True, default="") firstText = models.TextField(max_length=2000, verbose_name="会话提示语", blank=True, default="您好,很高兴为您服务!") conversationTime = models.IntegerField(verbose_name="会话时长(分钟)", default=20) notuserText = models.TextField(max_length=2000, verbose_name="客服不在线提示语", blank=True, default="非常抱歉,客服处于离线状态,您的消息我们已发送IT服务台,马上会有IT同事跟进处理!") webhook_url = models.URLField(verbose_name="群机器人地址", default="") createTime = models.DateTimeField(auto_now_add=True, verbose_name="创建时间") lastTime = models.DateTimeField(auto_now=True, verbose_name="修改时间") author = models.CharField(max_length=64, verbose_name="创建者") editor = models.ForeignKey(User, null=True, on_delete=models.CASCADE, verbose_name="修改者") @rename("部门Logo") def showAvatar(self): return format_html("<img src='{}' class='showAvatar'", self.avatar) @rename("详情") def checkMessage(self): return format_html("<a href='/work/index/{}.html' target='blank'>回复</a>", self.id) class Meta: verbose_name_plural = "应用管理" ordering = ['id'] def __str__(self): return self.name # 客服知识库 class knowledgeBase(models.Model): questionType = models.CharField(max_length=225, verbose_name="问题类型", blank=True, default="") key = models.CharField(max_length=225, verbose_name="关键字", blank=True, default="") rule = models.IntegerField(choices=((0, '包含'), (1, '完全匹配')), default=0, verbose_name='规则') answerType = models.IntegerField(choices=((0, '文字'), (1, '图文'), (2, '图片'), (3, '语音'), (4, '视频')), default=0, verbose_name='发送类型') content = models.TextField(max_length=2000, verbose_name="消息内容", blank=True, default="") createTime = models.DateTimeField(auto_now_add=True, verbose_name="创建时间") lastTime = models.DateTimeField(auto_now=True, verbose_name="修改时间") author = models.CharField(max_length=64, verbose_name="创建者") editor = models.ForeignKey(User, null=True, on_delete=models.CASCADE, verbose_name="修改者") class Meta: verbose_name_plural = "知识库" ordering = ['id'] def __str__(self): return self.name

5.admin.py 代码

from django.contrib import admin

# Register your models here.

from import_export import resources
from import_export.admin import ImportExportModelAdmin
from wechatpy.enterprise import WeChatClient
from wechatpy.enterprise.crypto import WeChatCrypto
from wechatpy.exceptions import InvalidSignatureException
import weChatWork as config

from work.models import *

admin.site.site_title = "企业号后台管理"
admin.site.site_header = "企业号后台管理"

# 企业号ID
corpid = config.settings.weChatWork["corpid"]


class agentResource(resources.ModelResource):
    def get_export_headers(self):
        # 是你想要的导出头部标题headers
        return ['应用名称', '欢迎语', '会话提示语', '部门Logo', '创建时间', '修改时间', '创建者', '修改者']

    class Meta:
        field = ('name', 'welcomeText', 'firstText', 'avatar', 'createTime', 'lastTime', 'author', 'editor')
        model = agent
        fields = field
        export_order = field


@admin.register(agent)
class agentAdmin(ImportExportModelAdmin):
    fields = ('agentid', 'secret', 'welcomeText', 'firstText', 'notuserText', 'webhook_url', 'conversationTime')
    # 需要显示的字段信息
    list_display = ('showAvatar', 'name', 'welcomeText', 'firstText', 'notuserText', 'webhook_url', 'conversationTime',
                    'createTime',
                    'lastTime', 'author',
                    'editor',
                    'checkMessage')
    exclude = ('avatar', 'author', 'editor')
    # 设置哪些字段可以点击进入编辑界面,默认是第一个字段
    list_display_links = ('showAvatar', 'name')
    model_icon = "fa fa-tag"
    list_per_page = 10
    resource_class = agentResource

    def save_model(self, request, obj, form, change):
        if form.is_valid():
            if not change:
                obj.author = request.user.username
            obj.editor = request.user
            client = WeChatClient(corpid, obj.secret)
            agent = client.agent.get(obj.agentid)
            obj.name = agent["name"]
            obj.avatar = agent["square_logo_url"]
            print(agent)
            obj.save()
        super().save_model(request, obj, form, change)


class KFResource(resources.ModelResource):
    def get_export_headers(self):
        # 是你想要的导出头部标题headers
        return ['姓名', 'UM', '头像']

    class Meta:
        field = ('username', 'userid', 'sign', 'avatar',)
        model = KF
        fields = field
        export_order = field


@admin.register(KF)
class KFAdmin(ImportExportModelAdmin):
    fields = ("agent", 'userid',)
    # 需要显示的字段信息
    list_display = (
        'id', "agent", "showAvatar", 'username', 'userid', 'status', 'createTime', 'lastTime', 'author', 'editor')
    exclude = ('username', 'status', 'avatar', 'author', 'editor')
    # 设置哪些字段可以点击进入编辑界面,默认是第一个字段
    list_display_links = ('id', 'username')
    model_icon = "fa fa-tag"
    list_per_page = 10
    resource_class = KFResource

    def save_model(self, request, obj, form, change):
        if form.is_valid():
            if not change:
                obj.author = request.user.username
            obj.userid = obj.userid.upper()
            obj.editor = request.user
            obj.status = False
            agentInfo = agent.objects.filter(agentid=obj.agent.agentid).first()
            client = WeChatClient(corpid, agentInfo.secret)
            user = client.user.get(obj.userid)
            obj.username = user["name"]
            obj.avatar = user["avatar"]
            obj.save()
        super().save_model(request, obj, form, change)


class userListResource(resources.ModelResource):
    def get_export_headers(self):
        # 是你想要的导出头部标题headers
        return ['姓名', 'UM', '头像']

    class Meta:
        field = ('username', 'userid', 'avatar', 'createTime', 'lastTime', 'author', 'editor')
        model = userList
        fields = field
        export_order = field


@admin.register(userList)
class userListAdmin(ImportExportModelAdmin):
    fields = ()
    # 需要显示的字段信息
    list_display = ('id', 'agent', 'showAvatar', 'username', 'userid', 'createTime', 'lastTime', 'author', 'editor')
    # 设置哪些字段可以点击进入编辑界面,默认是第一个字段
    list_display_links = ('id', 'username')
    search_fields = ('username', 'userid')
    model_icon = "fa fa-tag"
    list_per_page = 50
    resource_class = userListResource

    def save_model(self, request, obj, form, change):
        if form.is_valid():
            obj.editor = request.user
            obj.save()
        super().save_model(request, obj, form, change)


class MessageResource(resources.ModelResource):
    def get_export_headers(self):
        # 是你想要的导出头部标题headers
        return ['企业号ID', '发送者', '发送时间', '消息ID', '应用ID', '消息类型', '消息内容']

    class Meta:
        field = ('FromUserName', 'CreateTime', 'MsgId', 'AgentID', 'MsgType', 'content')
        model = Message
        fields = field
        export_order = field


# Register your models here.
@admin.register(Message)
class MessageAdmin(ImportExportModelAdmin):
    fields = ('FromUserName', 'CreateTime', 'MsgId', 'AgentID', 'MsgType', 'content')
    # 需要显示的字段信息
    list_display = ('id', 'FromUserName', 'CreateTime', 'MsgId', 'AgentID', 'MsgType', 'content')
    # 设置哪些字段可以点击进入编辑界面,默认是第一个字段
    list_display_links = ('id', 'FromUserName')
    model_icon = "fa fa-tag"
    list_per_page = 10
    resource_class = MessageResource


class knowledgeBaseResource(resources.ModelResource):
    def get_export_headers(self):
        # 是你想要的导出头部标题headers
        return ['问题类型', '关键字', '规则', '发送类型', '消息内容', '创建时间', '修改时间', '创建者', '修改者']

    class Meta:
        field = ('questionType', 'key', 'rule', 'answerType', 'content', 'createTime', 'lastTime', 'author', 'editor')
        model = knowledgeBase
        fields = field
        export_order = field


# Register your models here.
@admin.register(knowledgeBase)
class knowledgeBaseAdmin(ImportExportModelAdmin):
    fields = ('questionType', 'key', 'rule', 'answerType', 'content')
    # 需要显示的字段信息
    list_display = ('id', 'questionType', 'key', 'rule', 'answerType', 'content')
    # 设置哪些字段可以点击进入编辑界面,默认是第一个字段
    list_display_links = ('id', 'questionType')
    model_icon = "fa fa-tag"
    list_per_page = 10
    resource_class = knowledgeBaseResource

6.Channels的安装和使用(web socket 及时通讯)

 Channels是针对 Django 项目的一个增强框架,它可以是的同步的 Django 项目转变为异步的项目。它可以使得 Django 项目不仅支持 HTTP 请求,还可以支持 Websocket, chat协议,IOT协议 ,甚至是你自定义的协议,同时也整合了 Django 的 auth 以及 session 系統等等

Channels 提供从 PYPI 直接pip下载安装
pip install -U channels==2.0.2 Django==2.0.4 channels_redis==2.1.1

参考文档:https://www.vimiix.com/post/63/?from=timeline&isappinstalled=0

7.liunx安装redis

     windows 下安装redis: https://www.runoob.com/redis/redis-install.html

  liunx 安装redis: https://blog.csdn.net/qq_39135287/article/details/83474865

8.接受微信API发送的消息

import wechatpy
from django.contrib import auth
from wechatpy.enterprise.client.api import WeChatOAuth
from wechatpy.enterprise.exceptions import InvalidCorpIdException
from wechatpy import enterprise, parse_message
from django.shortcuts import render, redirect
from django.http import JsonResponse, HttpResponse, HttpResponseRedirect
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.decorators import login_required
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
import uuid, datetime, json, time
import weChatWork as config
from django.utils.safestring import mark_safe
from wechatpy.enterprise.crypto import WeChatCrypto
from wechatpy.exceptions import InvalidSignatureException
import os
import urllib
import work
from work.models import *
from django.core.cache import cache
from wechatpy.enterprise import WeChatClient
from wechatpy.session.redisstorage import RedisStorage
from redis import Redis
from urllib.parse import quote
from comm import libary
from django.utils.safestring import mark_safe
import json
import urllib3

corpid = config.settings.weChatWork["corpid"]
sourceFile = config.settings.weChatWork["sourceFile"]
soucketGroupName = config.settings.weChatWork["soucketGroupName"]


# 群消息提问内容
def template_string(**kwargs):
    return """<font color="warning">新消息</font>
> 姓名:{username}
> UM:{um}
> 发送内容:{content}
> 点击查看:{url}
""".format(**kwargs)


# 查看请求信息

@login_required
def requestInfo(request):
    result = request.environ.items()
    return render(request, 'requestInfo.html', {'rinfo': result})


# 登录功能

def login(request):
    code = request.GET.get('code', None)
    next = request.GET.get('next', '/')
    agentid = request.GET.get('state', "1000030")
    redirect_uri = quote(request.get_raw_uri(), 'utf-8')
    agentInfo = agent.objects.filter(agentid=agentid).first()
    agentid = agentInfo.agentid
    secret = agentInfo.secret
    client = WeChatClient(corpid, secret)
    if libary.checkMobile(request):
        # 微信登录
        url = client.oauth.authorize_url(redirect_uri, state=agentid)
        if code:
            try:
                user_info = client.oauth.get_user_info(code)
                if user_info["errcode"] != "0" and user_info["errmsg"] != "ok":
                    return redirect("/static/error/500.html")
                registered(request, user_info["UserId"])
                return redirect(next)
            except Exception as e:
                print(e.args)
        else:
            return redirect(url)
    else:
        # PC网站登录
        url = "https://open.work.weixin.qq.com/wwopen/sso/qrConnect?appid={0}&agentid={1}&redirect_uri={2}&state={3}".format(
            corpid, agentid, redirect_uri, agentid)
        if code:
            try:
                print(client.access_token)
                user_info = client.user.get_info(agentid, code)
                if user_info["errcode"] != "0" and user_info["errmsg"] != "ok":
                    return redirect("/static/error/500.html")
                UserId = user_info["UserId"].upper()
                registered(request, UserId)
                kfUser = KF.objects.filter(userid=UserId)
                kfUser.update(status=True)
                return redirect(next)
            except Exception as e:
                print(e.errmsg, e.errcode)
                # 这里需要处理请求里包含的 code 无效的情况
                pass
        else:
            return redirect(url)


# 注销
def logout(request):
    kfUser = KF.objects.filter(userid=request.user.username).first()
    kfUser.status = False
    kfUser.save()
    auth.logout(request)
    return redirect('/admin/login/')


# 注册
def registered(request, userid):
    user = User.objects.filter(username=userid).first()
    if not user:
        user = User.objects.create_user(username=userid, email=str(userid) + "@pingan.com.cn", password=uuid.uuid1(),
                                        is_staff=True, is_active=True)
    auth.login(request, user)
    return user


# 403 未授权页面
def rorbidden(request):
    return redirect("/static/error/403.html")


# Create your tests here.
@login_required
def index(request):
    method = request.method.upper()
    if method == "GET":
        return render(request, "work/index.html")
    elif method == "POST":
        UserId = request.user.username
        kfUser = KF.objects.filter(userid=UserId).first()
        kfUser.status = True
        kfUser.save()
        print(request.POST)
        Content = request.POST.get('mine[content]')
        userid = request.POST.get('mine[id]')
        FromUserName = request.POST.get('to[userid]')
        agentInfo = kfUser.agent
        agentid = agentInfo.agentid
        secret = agentInfo.secret
        client = WeChatClient(corpid, secret)
        result = client.message.send_text(agentid, FromUserName, Content)
        print(result)
        return JsonResponse(result)


@login_required
def chatMobile(request):
    return render(request, "work/mobile.html")


@login_required
def GetUserList(request):
    loginUser = request.user.username
    user = KF.objects.filter(userid=loginUser).first()
    mine = {"username": user.username, "id": user.userid, "status": "online", "sign": "嘻哈哈", "avatar": user.avatar}
    ulist = list(
        work.models.userList.objects.filter(agent__agentid=user.agent.agentid).values("id", "username", "userid",
                                                                                      "avatar", "sign"))
    friend = [{"groupname": "今天", "id": 1, "online": len(ulist), "list": ulist},
              {"groupname": "前天", "id": 2, "online": 0, "list": []},
              {"groupname": "三天前", "id": 4, "online": 0, "list": []},
              {"groupname": "已回复", "id": 5, "online": 0, "list": []},
              {"groupname": "未回复", "id": 6, "online": 0, "list": []}]
    return JsonResponse({'code': 0, 'msg': "", "data": {"mine": mine, "friend": friend, "group": []}})


# 微信企业号 接收消息服务器配置
from django.views.decorators.csrf import csrf_exempt


@csrf_exempt
def weChatIndex(request):
    method = request.method.upper()
    TOKEN = "###"
    EncodingAESKey = "####"
    crypto = WeChatCrypto(TOKEN, EncodingAESKey, corpid)
    echostr = request.GET.get("echostr")
    signature = request.GET.get("msg_signature")
    timestamp = request.GET.get("timestamp")
    nonce = request.GET.get("nonce")
    if method == "GET":
        try:
            echo_str = crypto.check_signature(signature, timestamp, nonce, echostr)
            return HttpResponse(echo_str)
        except InvalidSignatureException:
            pass
    elif method == "POST":
        try:
            decrypted_xml = crypto.decrypt_message(request.body, signature, timestamp, nonce)
        except (InvalidSignatureException, InvalidCorpIdException):
            raise  # 处理异常情况
        else:
            msg = parse_message(decrypted_xml)
            MsgType = msg._data["MsgType"]
            ToUserName = msg._data["ToUserName"]
            FromUserName = msg._data["FromUserName"]
            CreateTime = msg._data["CreateTime"]
            tl = time.localtime(int(CreateTime))
            cTime = time.strftime("%Y-%m-%d %H:%M:%S", tl)
            AgentID = msg._data["AgentID"]
            Message.objects.create(ToUserName=ToUserName, FromUserName=FromUserName,
                                   userList=userList.objects.filter(userid=FromUserName.upper()).first(),
                                   CreateTime=cTime, AgentID=AgentID, MsgType=MsgType, content=json.dumps(msg._data))
            swm = switch_wechat_messages()
            swm.case_to_function(MsgType)(msg._data)
            return JsonResponse({"msg": "OK"})


def setWeChatRedis(data, Content):
    ToUserName = data["ToUserName"]
    FromUserName = data["FromUserName"]
    CreateTime = data["CreateTime"]
    MsgType = data["MsgType"]
    MsgId = data["MsgId"]
    AgentID = data["AgentID"]
    currentUser = createUser(FromUserName, AgentID)
    chatlog = []
    print("currentUser=", currentUser)
    print("FromUserName=", FromUserName)
    chatInfo = {"AgentID": AgentID, "username": currentUser.username, "userid": FromUserName,
                "avatar": currentUser.avatar, "id": currentUser.id,
                "type": "friend", "content": Content, "timestamp": CreateTime, "mine": True, "MsgType": MsgType}
    # 查询当前应用信息
    agentInfo = agent.objects.filter(agentid=AgentID).first()
    # 获得一个在线客服
    currentKF = KF.objects.filter(agent__agentid=AgentID, status=True).order_by("lastTime").first()

    # 判断当前用户是否在线

    if currentKF:
        # 判断会话是否过期
        userStateKey = AgentID + "&" + FromUserName
        conversationTime = agentInfo.conversationTime * 60
        # 首次会话自动推送欢迎语
        kfuser = cache.get(userStateKey)
        print("kfuser=", kfuser)
        if not kfuser:
            cache.set(userStateKey, currentKF.userid.upper(), timeout=conversationTime)
            if agentInfo.firstText:
                client = WeChatClient(corpid, agentInfo.secret)
                client.message.send_text(AgentID, FromUserName, agentInfo.firstText)
        else:
            cache.set(userStateKey, kfuser, timeout=conversationTime)
        # 选择在线客服推送消息 完成
        # 用户发送的消息 制定到客服 完成
        # 客服离线 消息转发到其他在线客服 完成 (历史消息未完成)
        from work.consumers import sendContent
        message = sendContent(chatInfo)
        sendUser = currentKF.userid.upper()

        isLogin = KF.objects.filter(agent__agentid=AgentID, status=True, userid=kfuser).first()
        if isLogin:
            sendUser = kfuser
        else:
            currentKF.save()

        from channels.layers import get_channel_layer
        channel_layer = get_channel_layer()
        from asgiref.sync import async_to_sync
        async_to_sync(channel_layer.group_send)(soucketGroupName + sendUser,
                                                {"type": "chat_message", 'message': message})

    else:
        redisKey = AgentID + "$" + FromUserName
        chatlog.append(chatInfo)
        if not cache.get(redisKey):
            cache.set(redisKey, chatlog, timeout=None)
        else:
            redisUser = cache.get(redisKey)
            redisUser.append(chatInfo)
            cache.set(redisKey, redisUser, timeout=None)

        if agentInfo.notuserText:
            client = WeChatClient(corpid, agentInfo.secret)
            client.message.send_text(AgentID, FromUserName, agentInfo.notuserText)

        content = template_string(
            username=currentUser.username,
            um=FromUserName,
            content=Content,
            url="[发起对话](http://w.pinganbanksz.com:8000/work/chatMobile/)"
        )
        if agentInfo.webhook_url:
            encoded_data = json.dumps({"msgtype": "markdown", "markdown": {'content': content}}).encode('utf-8')
            http = urllib3.PoolManager()
            rr = http.request(method='POST', url=agentInfo.webhook_url, body=encoded_data,
                              headers={'Content-Type': 'application/json'})
            print(f'webhook response:{rr.data}')
            assert json.loads(rr.data).get('errcode') == 0


def downloadFile(data, fileType):
    AgentID = data["AgentID"]
    agentInfo = agent.objects.filter(agentid=AgentID).first()
    agentid = agentInfo.agentid
    secret = agentInfo.secret
    client = WeChatClient(corpid, secret)
    result = client.media.get_url(data["MediaId"])
    file = str(uuid.uuid1()) + "." + fileType
    BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    sourceUrl = os.path.join(BASE_DIR, sourceFile)
    LocalPath = os.path.join(sourceUrl, file)
    # os.path.join将多个路径组合后返回
    urllib.request.urlretrieve(result, LocalPath)
    return "/{0}/{1}".format(sourceFile, file)


class switch_wechat_messages(object):

    def case_to_function(self, case):
        fun_name = str(case) + "Message"
        method = getattr(self, fun_name, self.unknownMessage)
        return method

    def textMessage(self, data):
        Content = data["Content"]
        setWeChatRedis(data, Content)

    def imageMessage(self, data):
        PicUrl = data["PicUrl"]
        setWeChatRedis(data, PicUrl)

        print(data)

    def shortVideoMessage(self, data):
        print(data)

    def videoMessage(self, data):
        fileType = "avi"
        print(data)
        setWeChatRedis(data, downloadFile(data, fileType))

    def voiceMessage(self, data):
        fileType = data["Format"]
        setWeChatRedis(data, downloadFile(data, fileType))
        print(data)

    def locationMessage(self, data):
        print(data)

    def linkMessage(self, data):
        print(data)

    def eventMessage(self, data):
        AgentID = data["AgentID"]
        FromUserName = data["FromUserName"]
        createUser(FromUserName, AgentID)

    def unknownMessage(self, data):
        print(data)


def createUser(userid, AgentID):
    userid = userid.upper()
    agentInfo = agent.objects.filter(agentid=AgentID).first()
    client = WeChatClient(corpid, agentInfo.secret)
    user = client.user.get(userid)
    obj = userList.objects.filter(userid=userid, agent__agentid=AgentID).first()
    if not obj:
        obj = userList.objects.create(username=user["name"], userid=userid, avatar=user["avatar"],
                                      agent=agentInfo)
    else:
        obj.username = user["name"]
        obj.avatar = user["avatar"]

    # 用户访问的时候一天推送一次提示语
    if obj.lastTime.date() < datetime.datetime.now().date():
        if obj:
            obj.save()
        result = client.message.send_text(AgentID, userid, agentInfo.welcomeText)

    return obj

8.企业微信PC扫码登录

微信企业号在线客服
需求
1.新建项目wechatWork
2.python 安装wechatpy 
3.企业号后台设置消息http接口
4.设计model.py  应用管理表 客服用户管理表
5.admin.py 代码
6.Channels的安装和使用(web socket 及时通讯)
7.liunx安装redis
8.接受微信API发送的消息
8.企业微信PC扫码登录
7.运行效果图
3.PC超级管理员(使用企业微信扫码登录系统)

微信企业号在线客服
需求
1.新建项目wechatWork
2.python 安装wechatpy 
3.企业号后台设置消息http接口
4.设计model.py  应用管理表 客服用户管理表
5.admin.py 代码
6.Channels的安装和使用(web socket 及时通讯)
7.liunx安装redis
8.接受微信API发送的消息
8.企业微信PC扫码登录
7.运行效果图
3.PC超级管理员(使用企业微信扫码登录系统)

在浏览器输入一下地址,XXXXX需要修改为企业号参数(appid, agentid, redirect_uri)

参数说明:appid   企业ID  企业号后台-->我的企业-->最底部  企业ID

                agentid 应用ID 企业号后台-->应用管理-->点击XX应用-->即可查看 

      redirect_url 登录成功后跳转系统地址  例如在用户扫码登录后,跳转到百度 http://www.baidu.com  跳转地址需要url 编码

https://open.work.weixin.qq.com/wwopen/sso/qrConnect?appid=XXXXXXX&agentid=XXXXX&redirect_uri=XXXXX&state=XXXXX

  

微信企业号在线客服
需求
1.新建项目wechatWork
2.python 安装wechatpy 
3.企业号后台设置消息http接口
4.设计model.py  应用管理表 客服用户管理表
5.admin.py 代码
6.Channels的安装和使用(web socket 及时通讯)
7.liunx安装redis
8.接受微信API发送的消息
8.企业微信PC扫码登录
7.运行效果图
3.PC超级管理员(使用企业微信扫码登录系统)

微信企业号在线客服
需求
1.新建项目wechatWork
2.python 安装wechatpy 
3.企业号后台设置消息http接口
4.设计model.py  应用管理表 客服用户管理表
5.admin.py 代码
6.Channels的安装和使用(web socket 及时通讯)
7.liunx安装redis
8.接受微信API发送的消息
8.企业微信PC扫码登录
7.运行效果图
3.PC超级管理员(使用企业微信扫码登录系统)

7.运行效果图

1.客户端 (微信企业号即可发起聊天)

微信企业号在线客服
需求
1.新建项目wechatWork
2.python 安装wechatpy 
3.企业号后台设置消息http接口
4.设计model.py  应用管理表 客服用户管理表
5.admin.py 代码
6.Channels的安装和使用(web socket 及时通讯)
7.liunx安装redis
8.接受微信API发送的消息
8.企业微信PC扫码登录
7.运行效果图
3.PC超级管理员(使用企业微信扫码登录系统)

 2.客服端 (使用企业微信扫码登录系统)

微信企业号在线客服
需求
1.新建项目wechatWork
2.python 安装wechatpy 
3.企业号后台设置消息http接口
4.设计model.py  应用管理表 客服用户管理表
5.admin.py 代码
6.Channels的安装和使用(web socket 及时通讯)
7.liunx安装redis
8.接受微信API发送的消息
8.企业微信PC扫码登录
7.运行效果图
3.PC超级管理员(使用企业微信扫码登录系统)

2.发送和接受内容

微信企业号在线客服
需求
1.新建项目wechatWork
2.python 安装wechatpy 
3.企业号后台设置消息http接口
4.设计model.py  应用管理表 客服用户管理表
5.admin.py 代码
6.Channels的安装和使用(web socket 及时通讯)
7.liunx安装redis
8.接受微信API发送的消息
8.企业微信PC扫码登录
7.运行效果图
3.PC超级管理员(使用企业微信扫码登录系统)

3.PC超级管理员(使用企业微信扫码登录系统)

微信企业号在线客服
需求
1.新建项目wechatWork
2.python 安装wechatpy 
3.企业号后台设置消息http接口
4.设计model.py  应用管理表 客服用户管理表
5.admin.py 代码
6.Channels的安装和使用(web socket 及时通讯)
7.liunx安装redis
8.接受微信API发送的消息
8.企业微信PC扫码登录
7.运行效果图
3.PC超级管理员(使用企业微信扫码登录系统)

 微信企业号在线客服
需求
1.新建项目wechatWork
2.python 安装wechatpy 
3.企业号后台设置消息http接口
4.设计model.py  应用管理表 客服用户管理表
5.admin.py 代码
6.Channels的安装和使用(web socket 及时通讯)
7.liunx安装redis
8.接受微信API发送的消息
8.企业微信PC扫码登录
7.运行效果图
3.PC超级管理员(使用企业微信扫码登录系统)