websocket已经讲过了有兴趣的可以戳这里

今天我们要做的就是基于websocket实时人工客服,先来了解一下机制

WebSocket 机制

WebSocket 是 HTML5 一种新的协议。它实现了浏览器与服务器全双工通信,能更好的节省服务器资源和带宽并达到实时通讯,它建立在 TCP 之上,同 HTTP 一样通过 TCP 来传输数据,但是它和 HTTP 最大不同是:

  • WebSocket 是一种双向通信协议,在建立连接后,WebSocket 服务器和 Browser/Client Agent 都能主动的向对方发送或接收数据,就像 Socket 一样;
  • WebSocket 需要类似 TCP 的客户端和服务器端通过握手连接,连接成功后才能相互通信。
  • 图 2.WebSocket 请求响应客户端服务器交互图

那我们知道可为什么要使用它了吧

django后端接口

表的设计

在这里插入图片描述

# 聊天表
class Chat(models.Model):
    user_id_a = models.IntegerField()
    user_id_b = models.IntegerField()

    class Meta:
        index_together = ['user_id_a', 'user_id_b']
        unique_together = ['user_id_a', 'user_id_b']
        # 联合索引要这样写入!!!!!!!!!
        db_table = '聊天表'


# 消息表
class Message(models.Model):
    name = models.CharField(max_length=32, null=True)
    message = models.CharField(max_length=132)
    chat = models.ForeignKey(Chat, on_delete=models.CASCADE)

    class Meta:
        db_table = '消息表'

views.py

from dwebsocket.decorators import accept_websocket
from .serializers import *
from .models import *


# 接收前端信息
@accept_websocket
def reception_socket(request):
    if request.is_websocket():
        for message in request.websocket:
            input_text = eval(str(message, encoding='utf-8'))
            username = input_text.get("username")
            uid = input_text.get("uid")
            input_text = input_text.get("inputText")
            try:
                chat = Chat.objects.create(user_id_a=uid, user_id_b=998)
                Message.objects.create(message=input_text, chat=chat, name=username)
            except:
                chat_id = Chat.objects.get(user_id_a=uid, user_id_b=998)
                Message.objects.create(message=input_text, chat=chat_id, name=username)

            request.websocket.send(message)


# 主动推送消息
@accept_websocket
def send_websocket(request):
    uid = request.GET.get("uid")

    if request.is_websocket():
        while 1:
            time.sleep(1)  ## 向前端发送时间
            
            try:
                data = Chat.objects.get(user_id_a=uid, user_id_b=998)
                ser = MessageModelSerializer(Message.objects.filter(chat_id=data.id), many=True)
                request.websocket.send(json.dumps(ser.data))
            except:
                request.websocket.send(json.dumps([{"message": ""}]))

urls.py

from django.urls import path, include
from .views import *


urlpatterns = [
    path('reception_socket/', reception_socket),    # 客服系统接收前端信息
    path('send_websocket/', send_websocket),  #客服发送消息
]

VUE前端

客户页面 staff_service.vue

我这里封装为组件了 你也可以给个路由成为一个单独的页面

在这里插入图片描述

<template>
  <div>
    <div>
      <a-button type="primary" @click="showDrawer">
        <a-icon type="plus"/>
        人工客服
      </a-button>

      <a-drawer
        title="翔翔客服带给您最好的体验"
        :width="720"
        :visible="visible"
        :body-style="{ paddingBottom: '80px' }"
        @close="onClose(false)"
      >
        <a-row :gutter="16">
          <table>
            <tr v-for="i in message">
              <th>{{i.name}}:</th>
              <td>{{ i.message }}</td>
            </tr>
          </table>
        </a-row>
        <a-form :form="form" layout="vertical" hide-required-mark>
          <a-row :gutter="16">
            <a-col :span="24">
              <a-form-item label="Description">
                <a-textarea
                  v-model="inputText"
                  :rows="4"
                  placeholder="请输入问题描述"
                />
              </a-form-item>
            </a-col>
          </a-row>
        </a-form>
        <div
          :style="{
          position: 'absolute',
          right: 0,
          bottom: 0,
          width: '100%',
          borderTop: '1px solid #e9e9e9',
          padding: '10px 16px',
          background: '#fff',
          textAlign: 'right',
          zIndex: 1,
        }"
        >
          <a-button :style="{ marginRight: '8px' }" @click="onClose(false)">
            Cancel
          </a-button>
          <a-button type="primary" @click="onClose(true)">
            Submit
          </a-button>
        </div>
      </a-drawer>
    </div>
  </div>
</template>

<script>
import md5 from 'js-md5';
import echarts from "echarts";

export default {
  name: "staff_service",
  data() {
    return {
      form: this.$form.createForm(this),
      visible: false,
      inputText: '',
      username: localStorage.getItem("username"),
      uid: localStorage.getItem("id"),
      message: [],
    };
  },
  methods: {
    showDrawer() {
      this.visible = true;
    },
    onClose(ok) {
      this.visible = ok;
      if (ok === true) {
        var ed2020 = "2020";

        var sign = md5('price=500&goodid=3,1' + ed2020);

        console.log(sign);

        var _this = this;

        //判断浏览器是否支持websocket
        if ("WebSocket" in window) {
          console.log("支持");
          //生成websocket链接
          var ws = new WebSocket("ws://localhost:8000/user/reception_socket/");
          //发送链接请求
          ws.onopen = function () {
            var data = JSON.stringify({username: _this.username, inputText: _this.inputText, uid: _this.uid})
            ws.send(data);
          }
          //发送消息
          ws.onmessage = function (evt) {
            //将获取信息打印
            var received_msg = evt.data;
            // alert(received_msg);
          }
          //捕获断开链接
          ws.onclose = function () {
            console.log("链接已经关闭");
          }

        }
      }
    },
  },
  mounted() {
    // this.main()
    var ed2020 = "2020";

    var sign = md5('price=500&goodid=3,1' + ed2020);

    console.log(sign);


    var _this = this;


    //判断浏览器是否支持websocket
    if ("WebSocket" in window) {

      console.log("支持");
      //生成websocket链接
      var ws = new WebSocket("ws://localhost:8000/user/send_websocket/?uid=" + _this.uid);
      //发送链接请求
      ws.onopen = function () {
        ws.send("text");
      }
      //发送消息
      ws.onmessage = function (evt) {
        //将获取信息打印
        var received_msg = evt.data;
        var data
        var data2
        data = JSON.parse(received_msg)
        _this.message = data
        // data = JSON.parse(data)
        // console.log(data[0])

      }
      //捕获断开链接
      ws.onclose = function () {
        console.log("链接已经关闭");
      }
    }
  }
}
</script>

<style scoped>

</style>

客服页面service.vue

需要设置路由 客服是一个单独的页面

<template>
  <div>
    <div>
      <a-button type="primary" @click="showDrawer">
        <a-icon type="plus"/>
        人工客服
      </a-button>

      <a-drawer
        title="翔翔客服带给您最好的体验"
        :width="720"
        :visible="visible"
        :body-style="{ paddingBottom: '80px' }"
        @close="onClose(false)"
      >
        <a-row :gutter="16">
          <table>
            <tr v-for="i in message">
              <th>{{i.name}}:</th>
              <td>{{ i.message }}</td>
            </tr>
          </table>
        </a-row>
        <a-form :form="form" layout="vertical" hide-required-mark>
          <a-row :gutter="16">
            <a-col :span="24">
              <a-form-item label="Description">
                <a-textarea
                  v-model="inputText"
                  :rows="4"
                  placeholder="请输入问题描述"
                />
              </a-form-item>
            </a-col>
          </a-row>
        </a-form>
        <div
          :style="{
          position: 'absolute',
          right: 0,
          bottom: 0,
          width: '100%',
          borderTop: '1px solid #e9e9e9',
          padding: '10px 16px',
          background: '#fff',
          textAlign: 'right',
          zIndex: 1,
        }"
        >
          <a-button :style="{ marginRight: '8px' }" @click="onClose(false)">
            Cancel
          </a-button>
          <a-button type="primary" @click="onClose(true)">
            Submit
          </a-button>
        </div>
      </a-drawer>
    </div>
  </div>
</template>

<script>
import md5 from 'js-md5';
import echarts from "echarts";

export default {
  name: "staff_service",
  data() {
    return {
      form: this.$form.createForm(this),
      visible: false,
      inputText: '',
      username: localStorage.getItem("username"),
      uid: localStorage.getItem("id"),
      message: [],
    };
  },
  methods: {
    showDrawer() {
      this.visible = true;
    },
    onClose(ok) {
      this.visible = ok;
      if (ok === true) {
        var ed2020 = "2020";

        var sign = md5('price=500&goodid=3,1' + ed2020);

        console.log(sign);

        var _this = this;

        //判断浏览器是否支持websocket
        if ("WebSocket" in window) {
          console.log("支持");
          //生成websocket链接
          var ws = new WebSocket("ws://localhost:8000/user/reception_socket/");
          //发送链接请求
          ws.onopen = function () {
            var data = JSON.stringify({username: "客服", inputText: _this.inputText, uid: _this.uid})
            ws.send(data);
          }
          //发送消息
          ws.onmessage = function (evt) {
            //将获取信息打印
            var received_msg = evt.data;
            // alert(received_msg);
          }
          //捕获断开链接
          ws.onclose = function () {
            console.log("链接已经关闭");
          }

        }
      }
    },
  },
  mounted() {
    // this.main()
    var ed2020 = "2020";

    var sign = md5('price=500&goodid=3,1' + ed2020);

    console.log(sign);


    var _this = this;


    //判断浏览器是否支持websocket
    if ("WebSocket" in window) {

      console.log("支持");
      //生成websocket链接
      var ws = new WebSocket("ws://localhost:8000/user/send_websocket/?uid=" + _this.uid);
      //发送链接请求
      ws.onopen = function () {
        ws.send("text");
      }
      //发送消息
      ws.onmessage = function (evt) {
        //将获取信息打印
        var received_msg = evt.data;
        var data
        var data2
        data = JSON.parse(received_msg)
        _this.message = data
        // data = JSON.parse(data)
        // console.log(data[0])

      }
      //捕获断开链接
      ws.onclose = function () {
        console.log("链接已经关闭");
      }
    }
  }
}
</script>

<style scoped>

</style>

最终效果

在这里插入图片描述

ok 今天的学习就到这里了