• 利用websocket仿web端在线客服实时聊天系统


    需求场景模拟

    1.移动端给客服发送消息,客户在bs端后台收到消息并回复(本文以一个客服为例)
    2.左侧聊天栏显示最新的消息和消息时间
    3.需要查看对方是否已读自己的消息

    开发需求

    一、技术选型

    使用websocket进行消息推送
    优点:个人感觉开发简单,不需要部署第三方服务
    缺点:无状态,页面每刷新一次就会产生一个新的session,某些情况下不稳定
    还是那句话,技术没有什么好与坏,只有合适不合适,同时最终采用技术栈意味着你能忍受它的缺点

    二、需求分析

    1.发送消息意味着需要发送人和接收人两个角色,同时需要对用户进行持久化
    2.对接收人(发送人)来说,显示最新的消息和时间,就意味着显示双方消息记录的最后提条消息的内容和发送时间
    3.消息已读意味着打开聊天对话框就要告诉对方,自己已读对方的消息。这里会产生两种情况:
    己方在线对方未在线,需要在对方上线时(即打开对话框)告诉对方自己已读对方的消息
    解决方案:存储消息数据,在自己打开对框的时候,获取聊天记录,并将聊天记录中对方给自己发的消息状态全部更新为已读。

    双方同时在线(聊天对话界面),这里稍微有点操作,那就是如何让双方及时的知道对方已读自己的消息。
    场景及解决方案:
    场景:当自己给对方发送消息时,自己看到自己发给对方的消息已读(即便对方不给自己发消息)
    解决
    1.自己对对方发消息时,更新对方发给自己的全部消息为已读,对方读取消息
    2.对方读取消息时,让对方告诉自己对方已读自己的消息,自己进行已读展示,通过一个共用接 口即可,当对方调用指定接口时,让对方告诉自己对方已读即可。
    4.利用mongodb进行用户以及聊天记录的存储

    效果演示

    消息聊天演示:
    请添加图片描述
    消息时间演示:
    在这里插入图片描述
    消息未读演示:
    在这里插入图片描述

    软件需求实现

    1.技术架构

    PC端:vue+springboot
    移动端:html+springboot

    2.实现流程图:(仅供参考)

    在这里插入图片描述

    一、数据库设计

    1.sys_user

    字段说明
    userId用户id
    nickName昵称
    heardUrl头像地址
    sex性别

    2.chat_msg

    字段说明
    createTime创建时间
    msgKey通信key(sendId_receiveId)
    chatMsg通信消息
    sendId发送id
    receiveId接收id
    readState查看状态 0未看 1已看

    二、代码实现

    说明:代码有所删减修改,但核心代码存在!!!请选择性复用代码

    1.web端

    引入依赖

            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-data-mongodbartifactId>
                <version>2.1.6.RELEASEversion>
            dependency>
            <dependency>
                <groupId>org.apache.tomcat.embedgroupId>
                <artifactId>tomcat-embed-websocketartifactId>
                <version>9.0.39version>
            dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    配置yml

    #开发时服务端口号,部署无效
    server:
      port: 9019
      servlet: 
        context-path: fmis
    spring:
      data:
        mongodb:
          host: 192.168.1.125
          port: 27017
          database: cangzhou
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    1.前端代码

    index.vue

    <template>
      <div :class="classObj" class="app-wrapper">
        <Logo>Logo>
        <div class="clearfix">div>
        <sidebar class="sidebar-container" />
        <div class="communicate" >
          <el-button type="text" @click="talkRoomVisible = true" :class="{ breathing: unReadMessageNum && talkRoomVisible ==false}">
            //对话图标
            <img src="../../static/images/login/tips.png" alt />
          el-button>
        div>
        <CommunicationInterface
          style="
            position: absolute;
            z-index: 1000;
            top: 0;
            right: 0;
            left: 0;
            margin: auto;
          "
          v-if="talkRoomVisible"
          :userList="userList"
        />
      div>
    template>
    
    <script>
    import message from "./mixin/message";
    import CommunicationInterface from "@/dialog/chat.vue";
    export default {
      name: "Layout",
      components: {
        Navbar,
        Sidebar,
        AppMain,
        Logo,
        CommunicationInterface,
      },
      data() {
        return {};
      },
      mixins: [ResizeMixin, message],
      computed: {
        sidebar() {
          return this.$store.state.app.sidebar;
        },
        device() {
          return this.$store.state.app.device;
        },
        fixedHeader() {
          return this.$store.state.settings.fixedHeader;
        },
        classObj() {
          return {
            hideSidebar: !this.sidebar.opened,
            openSidebar: this.sidebar.opened,
            withoutAnimation: this.sidebar.withoutAnimation,
            mobile: this.device === "mobile",
          };
        },
        unreadList() {
          return this.userList.filter((e) => {
            return e.unReadMsgNum > 0;
          });
        },
      },
      methods: {
        handleClickOutside() {
          this.$store.dispatch("app/closeSideBar", { withoutAnimation: false });
        },
      },
    };
    script>
    
    <style lang="scss" scoped>
    @import "~@/styles/mixin.scss";
    @import "~@/styles/variables.scss";
    .breathing {
      animation: breathe 1s infinite;
    }
    @keyframes breathe {
      0% {
        opacity: 0.1;
      }
      50% {
        opacity: 1;
      }
      100% {
        opacity: 0.1;
      }
    }
    .hoverVisibleUnreadList{
      position: absolute;
      bottom: 35px;
      right: -50px;
      width: 180px;
      line-height: 35px;
      display: none;
      border-radius: 3px;
     box-shadow: 0 0 2px #888888;
    }
    .communicate:hover .hoverVisibleUnreadList{
      display: block
    }
    .app-wrapper {
      @include clearfix;
      position: relative;
      height: 100%;
      width: 100%;
      &.mobile.openSidebar {
        position: fixed;
        top: 0;
      }
    }
    .drawer-bg {
      background: #000;
      opacity: 0.3;
      width: 100%;
      top: 0;
      height: 100%;
      position: absolute;
      z-index: 999;
    }
    
    .fixed-header {
      position: fixed;
      top: 0;
      right: 0;
      z-index: 9;
      width: calc(100% - 0); // calc(100% - #{$sideBarWidth})
      transition: width 0.28s;
    }
    
    .hideSidebar .fixed-header {
      width: calc(100% - 0px);
    }
    
    .mobile .fixed-header {
      width: 100%;
    }
    .communicate {
      position: absolute;
      z-index: 2000;
      height: 50px;
      width: 50px;
      bottom: 50px;
      right: 50px;
      border-radius: 50%;
      img {
        width: 50px;
      }
    }
    style>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    chat.vue
    
    • 1
    <template>
      <div>
        <div class="box">
          <div class="min">
            <ul class="contactList">
              <h3 style="border-bottom: 0.5px solid #337bf1">联系人列表h3>
              <li
                v-for="item in userListSortByLastTime"
                :key="item.nickName"
                @click="CreateConnection(item)"
                :class="{ red: item.userId === userId }"
              >
                <div class="leftLi">
                  
                  <img :src="item.heardUrl" />
                  <div class="ListItem">
                    <div>
                      <div class="nickName">{{ item.nickName }}div>
                      <span style="vertical-align: top">{{
                        item.lastMsgTime | dateFormat
                      }}span>
                    div>
                    <div class="ellipsis">
                      {{ item.lastMsg }}
                      <span v-if="item.unReadMsgNum > 0">{{
                        [item.unReadMsgNum]
                      }}span>
                    div>
                  div>
                  <div class="delete" @click="delUserByUserId(item)">删除div>
                div>
              li>
            ul>
            <div class="chat">
              <h3>{{ contactsName || "聊天区域" }}h3>
              <div class="content" ref="reference">
                <ul style="color: black">
                  <li
                    v-for="item in content"
                    :key="item.id"
                    :style="item.flag ? 'text-align: right;' : 'text-align: left;'"
                  >
                    <span>
                      
                      <img
                        v-show="!item.flag"
                        :src="heardUrl"
                      />
                      <span class="times" v-show="item.flag">{{
                        item.createTime
                          ? item.createTime.substr(11, item.createTime.length)
                          : ""
                      }}span>
                      <span :class="item.flag ? 'i' : 'he'">{{ item.name }}span>
                      <span class="time" v-show="!item.flag">{{
                        item.createTime
                          ? item.createTime.substr(11, item.createTime.length)
                          : ""
                      }}span>
                      <img
                        v-show="item.flag"
                        :src="require('../../../static/images/login/titleboy.png')"
                      />
                      
                      <div class="readOrUnread" v-if="item.flag">
                        {{ item.readState == "1" ? "已读" : "未读" }}
                      div>
                    span>
                  li>
                ul>
              div>
              <div class="input">
                <textarea
                  cols="50"
                  rows="10"
                  v-model="text"
                  @keydown.enter="submit"
                >textarea>
              div>
            div>
          div>
        div>
      div>
    template>
    
    <script>
    // import axios from 'axios'
    import * as API from "@/api/systemApplication";
    import { dateFormat } from "@/utils/index";
    
    export default {
      props: {
        dialogVisible: {
          type: Boolean,
          default: false,
        },
        userList: {
          type: Array,
          required: true,
        },
      },
      data() {
        return {
          // dialogVisible: true
          c: null,
          text: null, //输入的消息
          content: [], //内容列表
          scoket: "",
          sendId: 1,
          userId: "",
          contactsName: "",
          URL: window.ROOT,
          heardUrl: "", //右侧聊天框头像
        };
      },
      computed: {
        userListSortByLastTime() {
          // 做排序,最新接收到的消息放在列表的最上面,需要后端提供年月日时分秒
          return this.userList.sort((a, b) => {
            let at = a.lastMsgTime ? `${a.lastMsgTime}` : "1970-01-01 00:00:00";
            // console.log(at,"at")
            let bt = b.lastMsgTime ? `${b.lastMsgTime}` : "1970-01-01 00:00:00";
            // console.log(bt,"bt")
            return new Date(bt).getTime() - new Date(at).getTime();
          });
        },
      },
      mounted() {
        // 因为上面用了v-if 所以每次这个组件出来 都会走mounted
        this.userId = this.userList[0].userId;
        this.heardUrl = this.userList[0].heardUrl;
        // 获取当前聊天人的消息
        this.getSelectedUserMessageList();
        // 接收ws消息
        this.$root.$on("pushNewMessage", this.pushNewMessage);
    
        this.$root.$emit("changeUnReadMsgNum", {
          userId: this.userId,
        });
      },
      destroyed() {
        // 取消接收ws消息
        this.$root.$off("pushNewMessage", this.pushNewMessage);
      },
      filters: {
        dateFormat(v) {
          if (!v) {
            return;
          }
          // 如果是昨天发的消息,左侧列表中展示的时间只显示月、日
          if (v.substr(0, 10) !== dateFormat(new Date()).substr(0, 10)) {
            return dateFormat(v, "MM-DD");
          }
          // 如果是今天发的消息,左侧列表中展示的时间显示时、分
          return dateFormat(v, "HH:mm");
        },
      },
      methods: {
        delUserByUserId(item) {
          this.$root.$emit("loadUserList", {
            userId:item.userId,
          });
        },
        // 获取历史记录
        getSelectedUserMessageList() {
          API["storeHouseChatMsgHistory"]({
            sendId: `${this.sendId}`,
            receiveId: this.userId,
            pageSize: "100",
            currentPage: "1",
          }).then((res) => {
            const { data } = res;
            this.content = data.list.reverse();
            this.scrollAuto();
          });
        },
        pushNewMessage(news) {
          if (news.code == 0) {
            console.log(11112, news);
            for (var i = 0; i < this.content.length; i++) {
              this.content[i].readState = 1;
            }
            return;
          }
          // 接收到的新消息
          if (news.sendId != this.userId) return; // 这里判断id对不对再推数据
          this.content.push({
            flag: 0,
            name: news.chatMsg,
            readState: "0",
          });
          this.scrollAuto();
          // 这里是为了做消除用户列表中的未读,也就是未读变为已读,告诉后端消息已读
          API["msgRead"]({
            sendId: `${this.sendId}`,
            receiveId: `${this.userId}`,
          });
          // 更改前端页面上的数据,不再提示几条未读
          this.$root.$emit("changeUnReadMsgNum", {
            userId: news.sendId,
          });
    
          // if(news.code ==0){
          // 		for(var i=0;i
          // 			this.content[i].readState=1
          // 		}
          // }
        },
        // 按enter键(也就是要发送消息)
        submit() {
          if (!this.text) {
            return;
          }
          this.content.push({
            flag: 1,
            name: this.text,
            readState: "0",
          });
          // 修改左侧列表上展示的最后一条消息和时间
          this.$root.$emit("changeLast", {
            lastMsg: this.text,
            lastMsgTime: dateFormat(Date.now()),
            userId: this.userId,
          });
          this.$root.$talkRoomWs.send(
            JSON.stringify({
              linkType: "msg",
              sendId: `${this.sendId}`,
              userId: this.sendId,
              receiveId: this.userId,
              msgKey: `${this.sendId}_${this.userId}`,
              chatMsg: this.text,
              readState: "0",
            })
          );
          this.text = null;
          this.scrollAuto();
        },
        // 这里是点击左侧成员列表时执行的函数
        CreateConnection(item) {
          if (this.userId === item.userId) return;
          this.contactsName = item.nickName;
          this.text = "";
          this.content = [];
          this.userId = item.userId;
          this.getSelectedUserMessageList();
          this.$root.$emit("changeUnReadMsgNum", item);
          API["msgRead"]({
            sendId: `${this.sendId}`,
            receiveId: `${this.userId}`,
          });
          // 点击左侧列表时,聊天框中的头像也要切换
          for (var i = 0; i < this.userList.length; i++) {
            if (this.userList[i].userId == item.userId) {
              this.heardUrl = this.userList[i].heardUrl;
            }
          }
        },
        scrollAuto() {
          this.$nextTick(() => {
            this.$refs["reference"].scrollTop =
              this.$refs["reference"].scrollHeight;
          });
     
        },
      },
    };
    script>
    
    <style lang="scss" scoped>
    h3 {
      text-align: center;
      height: 40px;
      line-height: 40px;
      border-bottom: 0.5px solid #ccc;
      font-size: 16px;
      font-weight: 300;
    }
    .box {
      margin-top: 150px;
      margin-left: 28%;
      width: 900px;
      border-radius: 10px;
      overflow: hidden;
      box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12), 0 0 6px rgba(0, 0, 0, 0.04);
      .min {
        display: flex;
        .contactList {
          list-style: none;
          overflow: auto;
          height: 600px;
          width: 260px;
          background: #4387f6;
          color: azure;
          padding: 0px 5px;
          padding-left: 5px;
    
          .red {
            background: #5d9aff;
            border-radius: 10px;
          }
    
          li {
            cursor: pointer;
            text-align: left;
            box-sizing: border-box;
            padding: 14px 0px;
            padding-left: 5px;
            height: 65px;
          }
    
          .leftLi {
            @extend .min;
            img {
              height: 40px;
              display: block;
            }
            .ListItem {
              @extend .min;
              padding-left: 5px;
              flex-direction: column;
            }
    
            div :first-child {
              height: 30px;
              line-height: 20px;
              span {
                color: #bed7ff;
                font-size: 12px;
                margin-left: 15px;
              }
            }
    
            div :last-child {
              line-height: 0px;
              color: #bed7ff;
              font-size: 12px;
              span {
                text-align: center;
                border-radius: 3px;
              }
            }
          }
        }
        .chat {
          background: #f3f3f3;
          width: 700px;
          height: 600px;
          @extend .min;
          flex-direction: column;
          .content {
            overflow: auto;
            height: 500px;
            span {
              display: inline-block;
              padding: 0px 5px;
              line-height: 28px;
              @mixin name($px) {
                transition: 0.5s;
                opacity: 0;
                transform: translate($px);
              }
              .time {
                @include name(-20px);
              }
              .times {
                @include name(20px);
              }
            }
    
            span:hover .time {
              opacity: 1;
              transform: translate(0px);
            }
            span:hover .times {
              opacity: 1;
              transform: translate(0px);
            }
    
            @mixin Bubble($color) {
              background: $color;
              border-radius: 5px;
              border-bottom-left-radius: 20px;
            }
    
            .i {
              color: #f3f3f3;
              @include Bubble(#5d9aff);
              max-width: 65%;
              text-align: left;
              word-wrap: break-word;
              word-break: break-all;
              overflow: hidden;
              padding: 8px 5px;
            }
            .he {
              @include Bubble(#e9e9e9);
              max-width: 65%;
              word-wrap: break-word;
              word-break: break-all;
              overflow: hidden;
              padding: 8px 5px;
            }
          }
          .input {
            height: 180px;
            border-top: 0.5px solid #f3f3f3;
            textarea {
              width: 100%;
              border: 0px;
              resize: none;
            }
          }
        }
      }
    }
    
    ul > li {
      width: 100px;
      text-align: center;
      width: 100%;
      margin-top: 3px;
    }
    
    img {
      height: 45px;
      vertical-align: top;
      border-radius: 50%;
    }
    .content li > span {
      position: relative;
    }
    .content li {
      margin-top: 15px;
    }
    .readOrUnread {
      font-size: 12px;
      margin-right: 47px;
      line-height: 12px;
    }
    .ellipsis {
      width: 100px;
      height: 12px;
      line-height: 12px !important;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
    .nickName {
      display: inline-block;
      width: 90px;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
    .leftLi {
      position: relative;
    }
    .delete {
      position: absolute;
      bottom: 3px;
      right: 45px;
      color: #fff;
    }
    style>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
    • 331
    • 332
    • 333
    • 334
    • 335
    • 336
    • 337
    • 338
    • 339
    • 340
    • 341
    • 342
    • 343
    • 344
    • 345
    • 346
    • 347
    • 348
    • 349
    • 350
    • 351
    • 352
    • 353
    • 354
    • 355
    • 356
    • 357
    • 358
    • 359
    • 360
    • 361
    • 362
    • 363
    • 364
    • 365
    • 366
    • 367
    • 368
    • 369
    • 370
    • 371
    • 372
    • 373
    • 374
    • 375
    • 376
    • 377
    • 378
    • 379
    • 380
    • 381
    • 382
    • 383
    • 384
    • 385
    • 386
    • 387
    • 388
    • 389
    • 390
    • 391
    • 392
    • 393
    • 394
    • 395
    • 396
    • 397
    • 398
    • 399
    • 400
    • 401
    • 402
    • 403
    • 404
    • 405
    • 406
    • 407
    • 408
    • 409
    • 410
    • 411
    • 412
    • 413
    • 414
    • 415
    • 416
    • 417
    • 418
    • 419
    • 420
    • 421
    • 422
    • 423
    • 424
    • 425
    • 426
    • 427
    • 428
    • 429
    • 430
    • 431
    • 432
    • 433
    • 434
    • 435
    • 436
    • 437
    • 438
    • 439
    • 440
    • 441
    • 442
    • 443
    • 444
    • 445
    • 446
    • 447
    • 448
    • 449
    • 450
    • 451
    • 452
    • 453
    • 454
    • 455
    • 456
    • 457
    • 458
    • 459
    • 460
    • 461
    • 462
    • 463
    • 464
    • 465
    • 466
    • 467
    • 468
    • 469
    • 470
    • 471

    message.js

    import * as API from "@/api/systemApplication";
    export default {
        data() {
            return {
                sendId: 1, // todo 客服id,默认为1
                scoket: null,
                userList: [], //联系人列表
                talkRoomVisible: false,
            }
        },
        computed: {
            // 未读消息的数量
            unReadMessageNum() {
                let unReadNum = 0;
                for (let i = 0; i < this.userList.length; i++) {
                    const e = this.userList[i];
                    // 注意这里的unReadMsgNum可能不是数字
                    if (e.unReadMsgNum && typeof e.unReadMsgNum == 'number') {
                        unReadNum += e.unReadMsgNum
                    }
                }
                return unReadNum
            }
        },
        mounted() {
            // 初始化ws
            this.initWs();
            // 获取左侧用户列表
            this.getList();
            // 如果聊天窗口是开启状态,按esc键,则关闭聊天窗口
            this.initCloseTalkRoom()
            // 修改前端页面上左侧列表展示的未读条数
            this.$root.$on("changeUnReadMsgNum", this.changeUnReadMsgNum);
            // 修改左侧列表上展示的最后一条消息和时间
            this.$root.$on("changeLast", this.changeLast);
            // 删除聊天室左侧列表中的项,再重新加载用户列表
            this.$root.$on("loadUserList", this.loadUserList);
        },
        methods: {
            initWs() {
                // let url = this.URL;
                // this.$root.$talkRoomWs = new WebSocket("ws://192.168.1.169:9019/fmis-api/ws/asset");
                let url = window.ROOT;
                this.$root.$talkRoomWs =  new WebSocket(`${url.replace("http", "ws")}/ws/asset`);
                let ws = this.$root.$talkRoomWs;
                ws.onopen = (e) => {
                    console.log(`WebSocket 连接状态: ${ws.readyState}`);
                };
                ws.onmessage = (data) => {
                    try {
                        let news = JSON.parse(data.data)
                        // 用新信息覆盖旧消息
                        if (news.unReadNumLst) {
                            for (let i = 0; i < news.unReadNumLst.length; i++) {
                                const e = news.unReadNumLst[i];
                                let index = this.userList.findIndex(item => item.userId == e.userId)
                                if (index > -1) {
                                    // this.userList[index].lastMsg = e.lastMsg
                                    // this.userList[index].lastMsgTime = e.lastMsgTime
                                    // this.userList[index].unReadMsgNum = e.unReadMsgNum
                                    Object.assign(this.userList[index], e)
                                } else {
                                    // todo 新增的逻辑
                                    this.userList.push(e)
                                }
                            }
                        }
    
                        this.$root.$emit('pushNewMessage', news)
    
                    } catch (err) {
                        // console.log(err);
                    }
                };
    
                ws.onclose = (data) => {
                    console.log("WebSocket连接已关闭");
                };
                setTimeout(() => {
                    ws.send(
                        JSON.stringify({
                            linkType: "bind",
                            sendId: this.sendId,
                        })
                    );
                }, 500);
            },
            // 获取左侧的用户列表
            getList() {
                API["storeHouseWsUserList"]({
                    sendId: this.sendId,
                    userId: this.sendId,
                }).then((res) => {
                    const { data } = res;
                    this.userList = data;
                });
            },
            // 如果聊天窗口是开启状态,按esc键,则关闭聊天窗口
            initCloseTalkRoom() {
                window["onkeydown"] = (e) => {
                    if (e.keyCode === 27 && this.talkRoomVisible) {
                        this.talkRoomVisible = false
                    }
                };
            },
            // 修改前端页面上左侧列表展示的未读条数
            changeUnReadMsgNum(item) {
                let index = this.userList.findIndex((e) => e.userId == item.userId);
                console.log(index);
                if (index > -1) {
                    this.userList[index].unReadMsgNum = 0;
                }
                console.log(this.userList[index].unReadMsgNum);
            },
            // 修改左侧列表上展示的最后一条消息和时间
            changeLast(obj) {
                this.userList.find((item) => {
                    if (item.userId === obj.userId) {
                        item.lastMsg = obj.lastMsg;
                        item.lastMsgTime = obj.lastMsgTime;
                    }
                });
            },
            loadUserList(item) {
                console.log(item,"item")
                let postData= {
                    userId:item.userId
                }
                API["delUserByUserId"](postData).then((res) => {
                    console.log(res);
                    if (res.code) {
                        this.$message({
                            message: res.message,
                            type: "success",
                        });
                        this.getList()
                    }
                });
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142

    systemApplication.js

    
    // 获取列表
    export function storeHouseWsUserList(data) {
      return request({
        url: '/chat/userList',
        method: 'post',
        data
      })
    }
    // 获取历史连天记录
    export function storeHouseChatMsgHistory(data) {
      return request({
        url: '/chat/msgHistory',
        method: 'post',
        data
      })
    }
    // 已读未读
    export function msgRead(data) {
      return request({
        url: '/chat/msgRead',
        method: 'post',
        data
      })
    }
    
    // 删除左侧列表中的项
    
    export function delUserByUserId(data) {
      return request({
        url: '/chat/delUserByUserId',
        method: 'post',
        data
      })
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    2.后端代码

    WebSocketServer

    import com.alibaba.fastjson.JSONObject;
    import com.jinmdz.fmis.api.api.model.storehouse.chat.ChatMsg;
    import com.jinmdz.fmis.api.global.CacheManager;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    import javax.annotation.PostConstruct;
    import javax.websocket.*;
    import javax.websocket.server.ServerEndpoint;
    import java.io.IOException;
    import java.util.concurrent.CopyOnWriteArraySet;
    import java.util.concurrent.atomic.AtomicInteger;
    
    /**
     * @author lvyq
     * @version 1.0
     * @description: TODO
     * @date 2021/8/17 16:30
     */
    @ServerEndpoint(value = "/ws/asset")
    @Component
    public class WebSocketServer {
    
        private static ChatService  chatService;
        @Autowired
        public  void setChatService(ChatService chatService) {
            WebSocketServer.chatService = chatService;
        }
    
        @PostConstruct
        public void init() {
            System.out.println("websocket 加载");
        }
        private static Logger log = LoggerFactory.getLogger(WebSocketServer.class);
        private static final AtomicInteger OnlineCount = new AtomicInteger(0);
        // concurrent包的线程安全Set,用来存放每个客户端对应的Session对象。
        private static CopyOnWriteArraySet<Session> SessionSet = new CopyOnWriteArraySet<Session>();
        /**
         * 连接建立成功调用的方法
         */
        @OnOpen
        public void onOpen(Session session) {
            SessionSet.add(session);
            int cnt = OnlineCount.incrementAndGet();
            log.info("有连接加入,当前连接数为:{}", cnt);
            SendMessage(session, "连接成功");
        }
    
        /**
         * 连接关闭调用的方法
         */
        @OnClose
        public void onClose(Session session) {
            SessionSet.remove(session);
            int cnt = OnlineCount.decrementAndGet();
            log.info("有连接关闭,当前连接数为:{}", cnt);
        }
    
        /**
         * 收到客户端消息后调用的方法
         * @param message 客户端发送过来的消息
         */
        @OnMessage
        public void onMessage(String message, Session session) {
            log.info("来自客户端的消息:{}",message);
            try{
                JSONObject jsonObject = JSONObject.parseObject(message);
                String linkType = jsonObject.getString("linkType");
                String sendId = jsonObject.getString("sendId");
                if (linkType.equals("bind")){
                    CacheManager.set("bind_"+sendId,session);
                    SendMessage(session, "连接成功");
                }else if (linkType.equals("msg")){
                    //聊天
                    ChatMsg msg = new ChatMsg();
                    //发消息
                    String chatMsg = jsonObject.getString("chatMsg");
                    String receiveId = jsonObject.getString("receiveId");
                    msg.setChatMsg(chatMsg);
                    msg.setSendId(sendId);
                    msg.setMsgKey(sendId+"_"+receiveId);
                    msg.setReceiveId(receiveId);
                    msg.setReadState("0");
                    chatService.sendOne(msg);}
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    
        /**
         * 出现错误
         * @param session
         * @param error
         */
        @OnError
        public void onError(Session session, Throwable error) {
            log.error("发生错误:{},Session ID: {}",error.getMessage(),session.getId());
            error.printStackTrace();
        }
    
        /**
         * 发送消息,实践表明,每次浏览器刷新,session会发生变化。
         * @param session
         * @param message
         */
        public static void SendMessage(Session session, String message) {
            try {
                //session.getBasicRemote().sendText(String.format("%s (From Server,Session ID=%s)",message,session.getId()));
                log.info("sessionID="+session.getId());
                session.getBasicRemote().sendText(message);
            } catch (IOException e) {
                log.error("发送消息出错:{}", e.getMessage());
                e.printStackTrace();
            }
        }
    
    
        /**
         * 指定Session发送消息
         * @param sessionId
         * @param message
         * @throws IOException
         */
        public static void SendMessageById(String message,String sessionId) throws IOException {
            Session session = null;
            for (Session s : SessionSet) {
                if(s.getId().equals(sessionId)){
                    session = s;
                    break;
                }
            }
            if(session!=null){
                SendMessage(session, message);
            }
            else{
                log.warn("没有找到你指定ID的会话:{}",sessionId);
            }
        }
        /**
         * @description: 指定发送
         * @author: lvyq
         * @date: 2021/9/24 11:30
         * @version 1.0
         */
        public static void SendMessageByRecId(String message,String receiveId) throws IOException {
            Session session=  CacheManager.get("bind_"+receiveId);
            String sessionId = "";
            if (session!=null){
                sessionId=session.getId();
            }
            for (Session s : SessionSet) {
                if(s.getId().equals(sessionId)){
                    session = s;
                    break;
                }
            }
            if(session!=null){
                SendMessage(session, message);
            }
            else{
                log.warn("没有找到你指定ID的会话:{}",sessionId);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165

    ChatController

    import com.jinmdz.fmis.api.api.model.storehouse.chat.ChatMsg;
    import com.jinmdz.fmis.api.api.model.storehouse.chat.SysUser;
    import com.jinmdz.fmis.api.api.service.ChatService;
    import com.jinmdz.fmis.api.api.service.WebSocketServer;
    import com.jinmdz.fmis.api.base.BaseController;
    import com.jinmdz.fmis.core.base.BaseResult;
    import org.springframework.web.bind.annotation.*;
    import javax.annotation.Resource;
    import java.io.IOException;
    
    /**
     * @author lvyq
     * @version 1.0
     * @description: TODO-消息通信
     * @date 2021/9/26 14:50
     */
    @RestController
    @RequestMapping("/chat")
    public class ChatController extends BaseController {
    
        @Resource
        private ChatService chatService;
    
        @Resource
        private WebSocketServer webSocketServer;
    
        /**
         * @description: 消息记录
         * @author: lvyq
         * @date: 2021/9/26 15:19
         * @version 1.0
         */
        @PostMapping("/msgHistory")
       public BaseResult msgHistory(@RequestBody ChatMsg data) throws IOException {
            return  chatService.msgHistory(data);
       }
    
       /**
        * @description: 消息已读
        * @author: lvyq
        * @date: 2021/9/26 16:27
        * @version 1.0
        */
       @PostMapping("/msgRead")
       private BaseResult msgRead(@RequestBody ChatMsg data) throws IOException {
           return  chatService.msgRead(data);
       }
    
       /**
        * @description: 全体发送
        * @author: lvyq
        * @date: 2021/11/11 13:38
        * @version 1.0
        */
       @GetMapping("/sendAll/{msg}")
        public void sendAll(@PathVariable("msg") String msg) throws IOException {
           WebSocketServer.BroadCastInfo(msg);
       }
    
        /**
         * @description: 发消息给某人
         * @author: lvyq
         * @date: 2021/9/24 11:15
         * @version 1.0
         */
        @PostMapping("/sendOne")
        public void sendOne(@RequestBody ChatMsg data) throws IOException {
            chatService.sendOne(data);
        }
    
        /**
         * @description: 获取用户列表
         * @author: lvyq
         * @date: 2021/9/24 13:43
         * @version 1.0
         */
    
        @PostMapping("/userList")
        private BaseResult userList(@RequestBody SysUser data){
            return chatService.userListDev(data);
        }
    
        /**
         * @description: 根据userId删除用户
         * @author: lvyq
         * @date: 2021/11/19 13:29
         * @version 1.0
         */
        @PostMapping("/delUserByUserId")
        public BaseResult delUserByUserId(@RequestBody SysUser data){
            return chatService.delUserByUserId(data.getUserId());
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95

    ChatService

    import com.alibaba.fastjson.JSONObject;
    import com.jinmdz.fmis.api.api.model.storehouse.chat.ChatMsg;
    import com.jinmdz.fmis.api.api.model.storehouse.chat.SysUser;
    import com.jinmdz.fmis.api.api.service.repository.SysUserRepository;
    import com.jinmdz.fmis.api.base.BaseService;
    import com.jinmdz.fmis.api.model.system.UserItem;
    import com.jinmdz.fmis.core.base.BaseResult;
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.data.domain.PageRequest;
    import org.springframework.data.domain.Pageable;
    import org.springframework.data.domain.Sort;
    import org.springframework.data.mongodb.core.MongoTemplate;
    import org.springframework.data.mongodb.core.query.Criteria;
    import org.springframework.data.mongodb.core.query.Query;
    import org.springframework.data.mongodb.core.query.Update;
    import org.springframework.stereotype.Service;
    
    import javax.annotation.Resource;
    import java.io.IOException;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    /**
     * @author lvyq
     * @version 1.0
     * @description: TODO
     * @date 2021/9/24 10:06
     */
    @Service
    public class ChatService extends BaseService {
    
        @Resource
        private MongoTemplate mongoTemplate;
    
        /**
         * @description: 获取用户列表
         * @author: lvyq
         * @date: 2021/9/24 10:12
         * @version 1.0
         */
        
        public BaseResult userList(UserItem userItem, SysUser data) {
            String receiveId = userItem.getId().toString();
            List<SysUser> sysUserList = userLst(receiveId);
            return successData(sysUserList);
        }
    
        /**
         * @description: 发消息给某人
         * @author: lvyq
         * @date: 2021/9/24 11:16
         * @version 1.0
         */
        public void sendOne(ChatMsg data) throws IOException {
            mongoTemplate.save(data);
            //TODO 更新消息  接收者_发送者
            String msg_key = data.getReceiveId()+"_"+data.getSendId();
            String receiveId = data.getReceiveId();
            HashMap<String,Object> map = new HashMap<>();
            JSONObject mav = new JSONObject();
            Query queryMsg = new Query().addCriteria(Criteria.where("readState").is("0").and("msgKey").is(msg_key));
            Update update = new Update().set("readState","1");
            //批量修改
            mongoTemplate.updateMulti(queryMsg,update,ChatMsg.class,"chat_msg");
            mav.put("sendId",data.getSendId());
            mav.put("chatMsg",data.getChatMsg());
            mav.put("unReadNumLst",userLst(receiveId));
            WebSocketServer.SendMessageByRecId(mav.toJSONString(),receiveId);
        }
    
    
        /**
         * @description: 消息列表信息-unLogin
         * @author: lvyq
         * @date: 2021/9/26 17:50
         * @version 1.0
         */
        public BaseResult userListDev(SysUser data) {
            String receiveId = data.getUserId();
            List<SysUser> sysUserList = userLst(receiveId);
            return successData(sysUserList);
        }
    
        /**
         * @description: 消息记录
         * @author: lvyq
         * @date: 2021/9/26 15:20
         * @version 1.0
         */
        
        public BaseResult msgHistory(ChatMsg data) throws IOException {
            int page = data.getCurrentPage();
            if (page<0){
                page=1;
            }
            page=page-1;
            int size = data.getPageSize();
            if (size<0){
                size=20;
            }
            if (StringUtils.isNotEmpty(data.getSendId()) && StringUtils.isNotEmpty(data.getReceiveId())){
                String msgKeyOne= data.getSendId()+"_"+data.getReceiveId();
                String msgKeyTwo = data.getReceiveId()+"_"+data.getSendId();
                Criteria orCri1 = new Criteria();
                Criteria orCri2 = new Criteria();
                orCri1.andOperator(Criteria.where("msgKey").is(msgKeyOne));
                orCri2.andOperator(Criteria.where("msgKey").is(msgKeyTwo));
                Query query = new Query().addCriteria(new Criteria().orOperator(orCri1,orCri2));
                long total = mongoTemplate.count(query,ChatMsg.class);
                Pageable pageable = PageRequest.of(page,size, Sort.by(Sort.Direction.DESC,"createTime"));
                query.with(pageable);
                List<ChatMsg> list = mongoTemplate.find(query,ChatMsg.class);
                HashMap<String, Object> map = new HashMap<>();
                if (list != null) {
                    for (ChatMsg chatMsg:list){
                        chatMsg.setName(chatMsg.getChatMsg());
                        if (chatMsg.getSendId().equals(data.getSendId())){
                                chatMsg.setFlag(1);
                        }else {
                            chatMsg.setFlag(0);
                        }
                    }
                    Map<String, Object> pageMap = new HashMap<>();
                    map.put("list", list);
                    pageMap.put("total", total);
                    pageMap.put("pageSize", data.getPageSize());
                    pageMap.put("currentPage", data.getCurrentPage());
                    map.put("pager", pageMap);
                }
                Query queryMsg = new Query().addCriteria(Criteria.where("readState").is("0").and("sendId").is(data.getReceiveId()).and("receiveId").is(data.getSendId()));
                Update update = new Update().set("readState","1");
                mongoTemplate.updateMulti(queryMsg,update,ChatMsg.class,"chat_msg");
               //消息已读通知
                /*JSONObject mav = new JSONObject();
                mav.put("state",true);
                mav.put("msg","消息已读");
                WebSocketServer.SendMessageByRecId(mav.toJSONString(),data.getReceiveId());*/
                return successData(map);
            }else {
                return failure("非法参数");
            }
    
        }
    
        /**
         * @description: 将消息修改为已读
         * @author: lvyq
         * @date: 2021/9/26 16:27
         * @version 1.0
         */
        
        public BaseResult msgRead(ChatMsg data) throws IOException {
            Query query = new Query().addCriteria(Criteria.where("readState").is("0").and("sendId").is(data.getReceiveId()).and("receiveId").is(data.getSendId()));
            Update update = new Update().set("readState","1");
            mongoTemplate.updateMulti(query,update,ChatMsg.class,"chat_msg");
            //消息已读通知
            JSONObject mav = new JSONObject();
            mav.put("state",true);
            mav.put("msg","消息已读");
            mav.put("code",0);
            WebSocketServer.SendMessageByRecId(mav.toJSONString(),data.getReceiveId());
            return success("操作成功");
    
        }
    
        /**
         * @description: 获取集合
         * @author: lvyq
         * @date: 2021/9/26 17:46
         * @version 1.0
         */
        private  List<SysUser> userLst(String receiveId){
            List<SysUser> sysUserList = mongoTemplate.findAll(SysUser.class);
            for (SysUser sysUser:sysUserList){
                String sendId = sysUser.getUserId();
                String msgKey = sendId+"_"+receiveId;
                if (sendId.equals(receiveId)){
                    continue;
                }
                //最新一条记录
                String msgKeyOne= sendId+"_"+receiveId;
                String msgKeyTwo = receiveId+"_"+sendId;
                Criteria orCri1 = new Criteria();
                Criteria orCri2 = new Criteria();
                orCri1.andOperator(Criteria.where("msgKey").is(msgKeyOne));
                orCri2.andOperator(Criteria.where("msgKey").is(msgKeyTwo));
                Query msgQuery = new Query().addCriteria(new Criteria().orOperator(orCri1,orCri2));
                Pageable pageable = PageRequest.of(0,1, Sort.by(Sort.Direction.DESC,"createTime"));
                msgQuery.with(pageable);
                List<ChatMsg> list = mongoTemplate.find(msgQuery,ChatMsg.class);
                SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                if (list.size()>0){
                      sysUser.setLastMsg(list.get(0).getChatMsg());
                      sysUser.setLastMsgTime(simpleDateFormat.format(list.get(0).getCreateTime()));
                }else {
                    sysUser.setLastMsg("");
                    sysUser.setLastMsgTime("");
                }
                Query query = new Query().addCriteria(Criteria.where("readState").is("0").and("msgKey").is(msgKey));
                long unReadNum = mongoTemplate.count(query, ChatMsg.class);
                sysUser.setUnReadMsgNum(unReadNum);
            }
            return sysUserList;
        }
    
        /**
         * @description:时间转换
         * @author: lvyq
         * @date: 2021/9/27 16:48
         * @version 1.0
         */
        private String changeTime(Date createTime) {
            Date date = new Date();
            SimpleDateFormat formatDay = new SimpleDateFormat("MM-dd");
            if (formatDay.format(date).equals(formatDay.format(createTime))){
                SimpleDateFormat formatMin = new SimpleDateFormat("HH:mm");
                return formatMin.format(createTime);
            }else {
                return formatDay.format(createTime);
            }
    
        }
    
        /**
         * @description: 根据userId删除用户
         * @author: lvyq
         * @date: 2021/11/19 13:29
         * @version 1.0
         */
        
        public BaseResult delUserByUserId(String userId) {
            Query query = new Query().addCriteria(Criteria.where("userId").is(userId));
            mongoTemplate.findAndRemove(query,SysUser.class);
            return success("操作成功");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240

    ChatMsg

    import com.fasterxml.jackson.annotation.JsonFormat;
    import com.jinmdz.fmis.core.base.BasePageData;
    import lombok.Data;
    import org.springframework.data.annotation.Id;
    import org.springframework.data.annotation.Transient;
    import org.springframework.data.mongodb.core.mapping.Document;
    
    import java.util.Date;
    
    /**
     * @author lvyq
     * @version 1.0
     * @description: 消息通信
     * @date 2021/9/24 9:00
     */
    @Data
    @Document("chat_msg")
    public class ChatMsg extends BasePageData {
        @Id
        private String id;
    
        /**
         * 创建时间
         */
        @JsonFormat(pattern = yyyy_MM_dd_HH_mm_ss)
        private Date createTime=new Date();
    
        /**
         * 通信key(sendId_receiveId)
         */
        private String msgKey;
    
        /**
         * 通信消息
         */
        private String chatMsg;
    
        /**
         * 发送id
         */
        private String sendId;
    
        /**
         * 接收id
         */
        private String receiveId;
    
        /**
         * 查看状态 0未看 1已看
         */
        private String readState;
    
        /**
         *1为我    0 为他
         */
        @Transient
        private Integer flag;
    
        @Transient
        private String  name;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62

    SysUser

    import com.jinmdz.fmis.core.base.BasePageData;
    import lombok.Data;
    import org.springframework.data.annotation.Id;
    import org.springframework.data.annotation.Transient;
    import org.springframework.data.mongodb.core.mapping.Document;
    
    /**
     * @author lvyq
     * @version 1.0
     * @description: TODO
     * @date 2021/9/24 9:14
     */
    @Data
    @Document("sys_user")
    public class SysUser extends BasePageData {
        @Id
        private String id;
        /**
         * 用户id
         */
        private String userId;
    
        /**
         * 昵称
         */
        private String nickName;
    
    
        /**
         * 头像地址
         */
    
        private String heardUrl;
    
        /**
             * 性别
         */
        private Integer sex;
    
        /**
         * 登录状态 0未登录 1已登录
         */
        private Integer loginState;
    
        /**
         * 用户发给自己且未读的消息数
         */
        @Transient
        private long unReadMsgNum;
    
        /**
         * 最新一条消息
         */
        @Transient
        private String lastMsg;
    
        /**
         * 最新一条消息时间
         */
        @Transient
        private String lastMsgTime;
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64

    2.移动端

    1.前端代码:

    说明:userid在登录系统时存储在了cookie中

    DOCTYPE html>
    <html>
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=0">
        <title>问题解答title>
        <script src="../../common/js/GBCookie.js">script>
    
        <style type="text/css">
            body {
                overflow: hidden;
            }
    
            h3 {
                text-align: center;
                height: 40px;
                line-height: 40px;
                border-bottom: 0.5px solid #ccc;
                font-size: 16px;
                font-weight: 300;
            }
    
            .chat {
                background: #f3f3f3;
                /*width: 700px;*/
                /*height: 600px;*/
                width: 100vw;
                height: 100vh;
            }
    
            .chat > h3 {
                height: 5vh;
                line-height: 5vh;
            }
    
            .content {
                overflow: auto;
                /*height: 500px;*/
                height: 75vh;
            }
    
            ul {
                list-style: none;
            }
    
            ul > li {
                width: 100px;
                text-align: center;
                line-height: 50px;
                width: 100%;
                margin-top: 3px;
            }
    
            img {
                height: 45px;
                vertical-align: bottom;
                border-radius: 50%;
            }
    
            .input {
                height: 180px;
                border-top: 0.5px solid #f3f3f3;
            }
    
            textarea {
                width: 100%;
                border: 0px;
                resize: none;
                height: 20vh;
            }
    
            span {
                display: inline-block;
                padding: 0px 5px;
                height: 37px;
                /*line-height: 37px;*/
                line-height: 30px;
    
            }
    
            span:hover .times {
                opacity: 1;
                transform: translate(0px);
            }
    
            span:hover .time {
                opacity: 1;
                transform: translate(0px);
            }
    
            .times {
                -webkit-transition: 0.5s;
                transition: 0.5s;
                opacity: 0;
                -webkit-transform: translate(20px);
                transform: translate(20px);
            }
    
            .time {
                -webkit-transition: 0.5s;
                transition: 0.5s;
                opacity: 0;
                -webkit-transform: translate(-20px);
                transform: translate(-20px);
            }
    
            .he {
                background: #e9e9e9;
                border-radius: 5px;
                border-bottom-right-radius: 15px;
    
            }
    
            .i {
                color: #f3f3f3;
                background: #5d9aff;
                border-radius: 5px;
                border-bottom-left-radius: 14px;
            }
    
            .state {
                font-size: 12px;
            }
    
            .readOrUnread {
                /*position: absolute;*/
                /*right: 55px;*/
                /*bottom: -39px;*/
                /*font-size: 12px;*/
                margin-right: 55px;
            }
    
            .content li > span {
                position: relative;
            }
    
            .content li {
                margin-top: 15px;
            }
    
            li .i,
            li .he {
                max-width: 50vw;
                word-wrap: break-word;
                word-break: break-all;
                text-align: left;
                height: auto;
                padding: 7px 5px;
            }
        style>
        <script src="../../common/vue/vue.min.js">script>
        <script src="../../common/axios/axios.min.js">script>
        <link rel="stylesheet" href="../../common/css/base.css">
    head>
    <body>
    
    <div id="app">
        <div class="chat"><h3>客服<span class="state">span>h3>
            <div class="content" ref="reference">
                <ul style="color: black">
                    <li v-for="item in content"
                        :key="item.id"
                        :style="item.flag ? 'text-align: right;' : 'text-align: left;'">
                        <span>
                          <img v-show="!item.flag" src="../img/icon/01.png" />
                          <span class="times" v-show="item.flag">{{
                                  item.createTime
                                          ? item.createTime.substr(11, item.createTime.length)
                                          : ""
                              }}span>
                        <span :class="item.flag ? 'i' : 'he'">{{ item.name }}span>
                        <span class="time" v-show="!item.flag">{{
                                item.createTime
                                        ? item.createTime.substr(11, item.createTime.length)
                                        : ""}}
                        span>
                      <img
                              v-show="item.flag"
                              :src="heardUrl"
                      />
                      <div class="readOrUnread" v-if="item.flag">
                        {{ item.readState == "1" ? "已读" : "未读" }}
                      div>
                    span>
                    li>
                ul>
            div>
            <div class="input">
                <textarea
                        cols="50"
                        rows="10"
                        v-model="text"
                        @keydown.enter="submit"
                >textarea>
            div>
        div>
    div>
    
    <script type="text/javascript">
        var app = new Vue({
            el: "#app",
            data() {
                return {
                    text: "",
                    content: [],
                    socket: "",
                    userId: "",
                    openId: "",
                    heardUrl: "",
                    webUrl: ""
                }
            },
            created() {
                this.openId = getCookie('openId');
            },
            mounted() {
                this.getWebUrl()
                this.getSysUserInfoByUserId()
            },
            methods: {
                submit() {
                    if (!this.text) {
                        return;
                    }
                    this.content.push({
                        flag: 1,
                        name: this.text,
                    });
                    this.socket.send(
                        JSON.stringify({
                            linkType: "msg",
                            sendId: this.openId,
                            userId: this.openId,
                            receiveId: 1,
                            msgKey: this.openId + '_1',
                            chatMsg: this.text,
                            readState: 0,
                        })
                    );
                    this.text = null;
                    this.scrollAuto();
                },
                scrollAuto() {
                    this.$nextTick(() => {
                        this.$refs["reference"].scrollTop = this.$refs["reference"].scrollHeight
                    });
                },
                history() {
                    axios({
                        method: "post",
                        url: "../../../chat/msgHistory",
                        data: {
                            receiveId: 1,
                            sendId: this.openId,
                            pageSize: "20",
                            currentPage: "1",
                        }
                    }).then((res) => {
                        if (res) {
                            const {data} = res;
                            this.content = data.data.list.reverse();
                            this.scrollAuto();
                        }
                    })
                    this.msgRead()
                },
                msgRead() {
                    let url = `http://${this.webUrl}/chat/msgRead`
                    console.log(url, "url")
                    axios({
                        method: "post",
                        url: url,
                        data: {
                            sendId: this.openId,
                            receiveId: `1`,
                        }
                    })
                },
                getWebUrl() {
                    axios({
                        method: "POST",
                        url: "../../../chat/getWebUrl",
                    }).then((res) => {
                        if (res) {
                            const {data} = res
                            this.webUrl = data.webUrl
                            this.history()
                            this.initWs()
                        }
    
                    })
                },
                initWs() {
                    if (typeof (WebSocket) == "undefined") {
                        console.log("遗憾:您的浏览器不支持WebSocket");
                    } else {
                        console.log("恭喜:您的浏览器支持WebSocket");
                        this.socket = new WebSocket(`ws://${this.webUrl}/ws/asset`);
                        //连接打开事件
                        this.socket.onopen = function () {
                            console.log("Socket 已打开");
                            //session与用户id绑定
                            this.socket.send("{\"linkType\":\"bind\",\"sendId\":\""${this.sendId}"\"}  ");
                        };
                        //收到消息事件
                        this.socket.onmessage = (data) => {
                            console.log(data)
                            if (data.data !== '连接成功') {
                                let news = JSON.parse(data.data);
                                this.content.push({
                                    flag: 0,
                                    name: news.chatMsg,
                                });
                                //对方接收到消息之后,把未读改为已读
                                if (news.code == 0) {
                                    this.content.pop()
                                    for (var i = 0; i < this.content.length; i++) {
                                        this.content[i].readState = 1
                                    }
                                }
                                this.msgRead()
                                this.scrollAuto();
                            }
    
                        };
                        // socket.
                        //连接关闭事件
                        this.socket.onclose = function () {
                            console.log("Socket已关闭");
                        };
                        //发生了错误事件
                        this.socket.onerror = function () {
                            alert("Socket发生了错误");
                        }
    
                        //窗口关闭时,关闭连接
                        window.unload = function () {
                            this.socket.close();
                        };
                    }
                    setTimeout(() => {
                        this.socket.send(
                            JSON.stringify({
                                linkType: "bind",
                                sendId: this.openId,
                            })
                        );
                    }, 500);
                },
                //通过openId获取用户头像信息
                getSysUserInfoByUserId() {
                    axios({
                        method: "GET",
                        url: "../../../chat/getSysUserInfoByUserId",
                        params: {
                            "openId": this.openId
                        }
                    }).then((res) => {
                        if (res.data.success) {
                            if (res.data.data) {
                                this.heardUrl = res.data.data.heardUrl
                                console.log(this.heardUrl)
                            }
    
                        }
                    })
                }
            }
        })
        
    script>
    body>
    html>
     
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
    • 331
    • 332
    • 333
    • 334
    • 335
    • 336
    • 337
    • 338
    • 339
    • 340
    • 341
    • 342
    • 343
    • 344
    • 345
    • 346
    • 347
    • 348
    • 349
    • 350
    • 351
    • 352
    • 353
    • 354
    • 355
    • 356
    • 357
    • 358
    • 359
    • 360
    • 361
    • 362
    • 363
    • 364
    • 365
    • 366
    • 367
    • 368
    • 369
    • 370
    • 371
    • 372
    • 373
    • 374

    GBCookie.js

    function setCookie(c_name, value, expiredays) {
        var exdate = new Date()
        exdate.setDate(exdate.getDate() + expiredays)
        document.cookie = c_name + "=" + escape(value) +
            ((expiredays == null) ? "" : ";expires=" + exdate.toGMTString())
    }
    
    //取回cookie
    function getCookie(c_name) {
        if (document.cookie.length > 0) {
            c_start = document.cookie.indexOf(c_name + "=")`在这里插入代码片`
            if (c_start != -1) {
                c_start = c_start + c_name.length + 1
                c_end = document.cookie.indexOf(";", c_start)
                if (c_end == -1) c_end = document.cookie.length
                return unescape(document.cookie.substring(c_start, c_end))
            }
        }
        return ""
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    2.后端代码

    引入mongodb依赖

            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-data-mongodbartifactId>
                <version>2.1.6.RELEASEversion>
            dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    yml配置

    #mongodb 配置
    spring:
      data:
        mongodb:
          host: 192.168.1.125
          port: 27017
          database: cangzhou
          # 客服消息地址
    websocket:
      url: 192.168.1.209:9019/fmis
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    WxChatController

    import com.alibaba.fastjson.JSONObject;
    import com.jinmdz.common.response.CommonCode;
    import com.jinmdz.entity.wechat.SysUser;
    import com.jinmdz.entity.wechat.WeChatResult;
    import com.jinmdz.model.chat.ChatMsg;
    import com.jinmdz.service.ChatService;
    import org.springframework.data.mongodb.core.MongoTemplate;
    import org.springframework.data.mongodb.core.query.Criteria;
    import org.springframework.data.mongodb.core.query.Query;
    import org.springframework.data.mongodb.core.query.Update;
    import org.springframework.web.bind.annotation.*;
    
    import javax.annotation.Resource;
    
    /**
     * @author lvyq
     * @version 1.0
     * @description: TODO
     * @date 2021/11/12 13:18
     */
    @RestController
    @RequestMapping("/chat")
    public class WxChatController {
        @Resource
        public MongoTemplate mongoTemplate;
    
        @Resource
        private ChatService chatService;
    
        /**
         * @description: 添加用户-测试
         * @author: lvyq
         * @date: 2021/11/18 16:08
         * @version 1.0
         */
    
        @PostMapping("/saveUser")
        public WeChatResult saveUser(@RequestBody SysUser user){
            Query query = new Query().addCriteria(Criteria.where("userId").is(user.getUserId()));
            Update update = new Update().set("nickName",user.getNickName()).set("heardUrl",user.getHeardUrl());
            SysUser sysUser=  mongoTemplate.findOne(query,SysUser.class);
            if (sysUser!=null){
                mongoTemplate.updateFirst(query,update,SysUser.class);
            }else {
                mongoTemplate.insert(user);
    
            }
            return new WeChatResult(CommonCode.SUCCESS);
        }
        /**
         * @description:  消息记录
         * @author: lvyq
         * @date: 2021/11/12 13:18
         * @version 1.0
         */
        @PostMapping("/msgHistory")
        public JSONObject msgHistory(@RequestBody ChatMsg data){
            return  chatService.msgHistory(data);
        }
    
        
    
        /**
         * @description: 获取ws的地址
         * @author: lvyq
         * @date: 2021/11/16 14:45
         * @version 1.0
         */
        
        @PostMapping("/getWebUrl")
        public JSONObject getWebUrl(){
            return chatService.getWebUrl();
        }
           
         /**
         * @description: 根据用户openId获取用户基本信息
         * @author: lvyq
         * @date: 2021/11/18 16:27
         * @version 1.0
         */
        
        @GetMapping("/getSysUserInfoByUserId")
        public WeChatResult getSysUserInfoByUserId(String openId){
            if (StringUtils.isEmpty(openId)){
                return new WeChatResult(CommonCode.INVALID_PARAM);
            }
            Query query = new Query().addCriteria(Criteria.where("userId").is(openId));
            SysUser sysUser = mongoTemplate.findOne(query,SysUser.class);
            return new WeChatResult(sysUser);
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92

    ChatService

    import com.alibaba.fastjson.JSONObject;
    import com.jinmdz.model.chat.ChatMsg;
    
    /**
     * @author lvyq
     * @version 1.0
     * @description: TODO
     * @date 2021/11/12 13:18
     */
    public interface ChatService {
        JSONObject msgHistory(ChatMsg data);
    
        JSONObject getUnReadNum(ChatMsg data);
    
        JSONObject getWebUrl();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    ChatServiceImpl

    import com.alibaba.fastjson.JSONObject;
    import com.jinmdz.model.chat.ChatMsg;
    import com.jinmdz.service.ChatService;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.data.domain.PageRequest;
    import org.springframework.data.domain.Pageable;
    import org.springframework.data.domain.Sort;
    import org.springframework.data.mongodb.core.MongoTemplate;
    import org.springframework.data.mongodb.core.query.Criteria;
    import org.springframework.data.mongodb.core.query.Query;
    import org.springframework.data.mongodb.core.query.Update;
    import org.springframework.stereotype.Service;
    import org.springframework.util.StringUtils;
    
    import javax.annotation.Resource;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    /**
     * @author lvyq
     * @version 1.0
     * @description: TODO
     * @date 2021/11/12 13:19
     */
    @Service
        public class ChatServiceImpl implements ChatService {
    
        @Resource
        private MongoTemplate mongoTemplate;
    
        @Value("${websocket.url}")
        public  String WebUrl;
    
        /**
         * @description: 消息记录
         * @author: lvyq
         * @date: 2021/9/26 15:20
         * @version 1.0
         */
    
        @Override
        public JSONObject msgHistory(ChatMsg data) {
            JSONObject mav = new JSONObject();
            int page = data.getCurrentPage();
            if (page<0){
                page=1;
            }
            page=page-1;
            int size = data.getPageSize();
            if (size<0){
                size=20;
            }
            if (!StringUtils.isEmpty(data.getSendId()) && !StringUtils.isEmpty(data.getReceiveId())){
                String msgKeyOne= data.getSendId()+"_"+data.getReceiveId();
                String msgKeyTwo = data.getReceiveId()+"_"+data.getSendId();
                Criteria orCri1 = new Criteria();
                Criteria orCri2 = new Criteria();
                orCri1.andOperator(Criteria.where("msgKey").is(msgKeyOne));
                orCri2.andOperator(Criteria.where("msgKey").is(msgKeyTwo));
                Query query = new Query().addCriteria(new Criteria().orOperator(orCri1,orCri2));
                long total = mongoTemplate.count(query,ChatMsg.class);
                Pageable pageable = PageRequest.of(page,size, Sort.by(Sort.Direction.DESC,"createTime"));
                query.with(pageable);
                List<ChatMsg> list = mongoTemplate.find(query,ChatMsg.class);
                HashMap<String, Object> map = new HashMap<>();
                if (list != null) {
                    for (ChatMsg chatMsg:list){
                        chatMsg.setName(chatMsg.getChatMsg());
                        if (chatMsg.getSendId().equals(data.getSendId())){
                            chatMsg.setFlag(1);
                        }else {
                            chatMsg.setFlag(0);
                        }
                    }
                    Map<String, Object> pageMap = new HashMap<>();
                    map.put("list", list);
                    pageMap.put("total", total);
                    pageMap.put("pageSize", data.getPageSize());
                    pageMap.put("currentPage", data.getCurrentPage());
                    map.put("pager", pageMap);
                }
                Query queryMsg = new Query().addCriteria(Criteria.where("readState").is("0").and("sendId").is(data.getReceiveId()).and("receiveId").is(data.getSendId()));
                Update update = new Update().set("readState","1");
                mongoTemplate.updateFirst(queryMsg,update,ChatMsg.class);
                mav.put("state",true);
                mav.put("data",map);
                return mav;
            }else {
                mav.put("state",false);
                mav.put("msg","非法参数");
                return mav;
            }
    
        }
    
        /**
         * @description: 未读消息条数
         * @author: lvyq
         * @date: 2021/11/16 14:20
         * @version 1.0
         */
        
        @Override
        public JSONObject getUnReadNum(ChatMsg data) {
            JSONObject mav = new JSONObject();
            Query query = new Query();
            query.addCriteria(Criteria.where("readState").is("0").and("sendId").is(data.getSendId()).and("receiveId").is(data.getReceiveId()));
            long count = mongoTemplate.count(query,ChatMsg.class);
            mav.put("state",true);
            mav.put("unReadNum",count);
            return mav;
        }
    
    
        
    
        /**
         * @description: 获取websocket地址
         * @author: lvyq
         * @date: 2021/11/16 14:47
         * @version 1.0
         */
        @Override
        public JSONObject getWebUrl() {
            JSONObject mav = new JSONObject();
            mav.put("state",true);
            mav.put("webUrl",WebUrl);
            return mav;
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
  • 相关阅读:
    MYSQL08_子查询的概述、单行、多行、相关子查询
    Hello Playwright:(1)从开发到部署
    【JavaScript面试】isArray() 、Array.of()、Array.from() 方法
    论文浅尝 | 基于预训练语言模型的简单问题知识图谱问答
    C++库函数——set与map的模拟实现
    【虹科干货】TWAMP:什么是双向主动测量协议?
    JS 循环JSON将数据遍历到Table里面
    UUID和雪花(Snowflake)算法该如何选择?
    【手撕AHB-APB Bridge】~ AHB地址总线的低两位为什么不用来表示地址呢?
    MyBioSource 猴转甲状腺素蛋白 (TTR) ELISA试剂盒方案
  • 原文地址:https://blog.csdn.net/qq_27185879/article/details/126320183