首页 > 其他分享 >若依集成CIM(即时推送系统)实现将服务端修改为SpringBoot+Vue前后端分离版(文末见代码下载)

若依集成CIM(即时推送系统)实现将服务端修改为SpringBoot+Vue前后端分离版(文末见代码下载)

时间:2024-03-07 16:23:30浏览次数:26  
标签:COMMENT netty Vue SpringBoot utf8 cim NULL 文末 服务端

 场景

若依前后端分离版本地搭建开发环境并运行项目的教程:

https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/108465662 

CIM

Gitee地址:

https://gitee.com/farsunset/cim

CIM项目是基于mina或者netty框架下的推送系统,我们平常使用第三方的推送SDK,如极光推送,百度推送,小米推送,以及腾讯信鸽等来支撑自己的移动端的业务,或许有一些用户自己实现即时通讯系统的需求,那么CIM为您提供了一个解决方案或者思路,目前CIM支持 websocket,android,ios,桌面应用,系统应用等多端接入支持,目前CIM服务端使用springboot搭建仅仅拥有消息推送的功能,关于数据缓存与持久化都需要使用者自己开发,但是配备了比较完整的使用文档。最后希望CIM能为您带来一些价值。

将上面的cim代码拉取到本地,代码结构如下

 

目录说明
1.cim-use-examples是各个客户端使用示例

2.cim-client-sdk 是各个客户端的SDK源码

3.cim-server-sdk 是服务端SDK源码,分为 mina和netty 两个版本,二者任选其一

4.cim-boot-server是springboot服务端工程源码,使用Idea工具开发

其中所有的sdk均为IntelliJ IDEA工程,Maven打包成jar导出引入到对应的客户端或服务端工程

然后我们使用IDEA打开并运行其服务端代码

 

 

打开浏览器访问

http://localhost:8088/console/session/list

 

 

默认是8080端口,这里将其修改过

 

为了测试其推送效果,使用AndroidStudio打开其Android的示例代码

 

 

找到包路径下的app下的Constant中的服务器IP地址设置,将地址修改为上面启动的服务端的ip地址。其他端口号保持不变

 

 

然后运行APP

 

 

连接成功之后会跳转到登录页面,输入登录账号之后点击登录

 

 

登录成功,然后回到后台服务端页面中

 

 

刷新页面,会发现在线用户列表中已经显示,点击发送消息

 

 

输入消息内容,点击发送

 

 

则会在app中收到消息推送。

以上是CIM自带服务端的推送效果,但是器服务端不是前后端分离版的,现在讲怎样

在SpringBoot+Vue前后端分离版的架构中集成CIM的服务端

首先按照上面一开始的博客将前后端分离的架构搭建好

然后找到若依的common模块的pom.xml中引入项目所需的依赖,首先添加properties

    <properties>
        <java.version>1.8</java.version>
        <netty.version>4.1.35.Final</netty.version>
        <mina.version>2.1.3</mina.version>
        <protobuf.version>3.11.1</protobuf.version>
    </properties>

然后按照cim服务端的依赖添加坐标

        <!--- ##################使用mina版本SDK时的配置  start ##################-->
        <dependency>
            <groupId>org.apache.mina</groupId>
            <artifactId>mina-core</artifactId>
            <version>${mina.version}</version>
        </dependency>

        <!--- ##################使用mina版本SDK时的配置  end ##################-->

        <!--- ##################使用netty本SDK时的配置  start ##################-->
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-handler</artifactId>
            <version>${netty.version}</version>
        </dependency>
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-buffer</artifactId>
            <version>${netty.version}</version>
        </dependency>
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-codec</artifactId>
            <version>${netty.version}</version>
        </dependency>
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-codec-http</artifactId>
            <version>${netty.version}</version>
        </dependency>
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-common</artifactId>
            <version>${netty.version}</version>
        </dependency>
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-transport</artifactId>
            <version>${netty.version}</version>
        </dependency>
        <!--- ##################使用netty本SDK时的配置  end ##################-->



        <dependency>
            <groupId>com.google.protobuf</groupId>
            <artifactId>protobuf-java</artifactId>
            <version>${protobuf.version}</version>
        </dependency>

        <dependency>
            <groupId>cn.teaey.apns4j</groupId>
            <artifactId>apns4j</artifactId>
            <version>1.1.4</version>
        </dependency>

        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.6</version>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.8.1</version>
        </dependency>

注意这里不用引入sdk的依赖,下面直接将sdk的源码集成进来

 

 

 

在ruoyi-common的包下直接新建cim.sdk.server目录,然后将CIM下的

cim-server-sdk下的cim-server-sdk-netty下的src下的包路径下的代码复制过来

 

 

复制到对应的上面新建的包路径下

 

 

将其复制过来之后,原有代码的引入路径会报错。将sdk.server下的每个代码中的引入路径全部删掉重新导入。

 

 

然后找到resources下的i18n下的message.properties国家化文件。将CIM自带的的国际化的字段信息复制到若依下面

 

 

接下来就是转移配置文件中的一些配置

打开若依的application.yml

首先是添加host配置

 

若依默认是不带host配置的

然后再添加cim所需要的配置

 

 

这里就需要把cim在properties中的配置改为yml中的配置了

下面就是将cim的服务端的代码集成到若依这套架构中

在若依的ruoyi-admin下的com.ruoyi下新建cim包以及config包,config包下主要是放置用来配置项目启动后

启动websocket服务的配置类,然后在cim包下放置cim服务端的代码

 

 

然后将其复制过来之后,每个目录下引用的路径要修改。依次将每个打开,将错误的import删掉,重新导入。

然后修改sessionController,这个是显示在线用户的列表的接口,将其修改为前端请求数据的接口

@RestController
@RequestMapping("/system/onlineUser")
public class SessionController {

 @Resource
 private CIMSessionService cimSessionService;


 /**
  * 查询在线用户列表
  */
 @GetMapping("/list")
 public AjaxResult list()
 {
  List<CIMSession> list = cimSessionService.list();
  return AjaxResult.success(list);
 }
}

然后将api.controller下的MessageController中的接口修改

@RestController
@RequestMapping("/api/message")
public class MessageController  {

 @Resource
 private DefaultMessagePusher defaultMessagePusher;


 @PostMapping(value = "/send")
 public ResponseEntity<Long> send(@RequestBody Message message)  {

  message.setId(System.currentTimeMillis());

  defaultMessagePusher.push(message);

  return ResponseEntity.ok(message.getId());
 }

}

然后就是生成前端代码。这里使用若依自带的代码生成工具

首先在数据库中设计一个表,表结构和CIM的sdk的CIMSession一样

 

表结构的sql如下

DROP TABLE IF EXISTS `cim_online_user`;
CREATE TABLE `cim_online_user`  (
  `id` int(50) NOT NULL COMMENT '数据库主键Id',
  `account` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'session绑定的用户账号',
  `nid` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'session在本台服务器上的ID',
  `deviceId` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '客户端ID (设备号码+应用包名),ios为deviceToken',
  `host` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'session绑定的服务器IP',
  `channel` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '终端设备类型',
  `deviceModel` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '终端设备型号',
  `clientVersion` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '终端应用版本',
  `systemVersion` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '终端系统版本',
  `bindTime` datetime(0) NULL DEFAULT NULL COMMENT '登录时间',
  `longitude` double(100, 0) NULL DEFAULT NULL COMMENT '经度',
  `latitude` double(100, 0) NULL DEFAULT NULL COMMENT '纬度',
  `location` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '位置',
  `apns` int(10) NULL DEFAULT NULL COMMENT 'APNs推送状态',
  `state` int(10) NULL DEFAULT NULL COMMENT '状态',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '在线用户' ROW_FORMAT = Compact;

SET FOREIGN_KEY_CHECKS = 1;

然后使用若依的代码生成工具,生成代码,这主要取前端代码和sql文件

然后替换为前端代码后,将其页面代码修改为请求后台接口获取

<template>
  <div class="app-container">
    <el-table
      v-loading="loading"
      :data="onlineUserList"
      @selection-change="handleSelectionChange"
    >
      <el-table-column label="账号" align="center" prop="account" />
      <el-table-column label="设备ID" align="center" prop="nid" />
      <el-table-column
        label="客户端ID"
        align="center"
        prop="deviceId"
        width="400"
      />
      <el-table-column label="服务器IP" align="center" prop="host" />
      <el-table-column
        label="终端设备类型"
        align="center"
        prop="channel"
        width="100"
      />
      <el-table-column
        label="终端设备型号"
        align="center"
        prop="deviceModel"
        width="100"
      />
      <el-table-column
        label="终端应用版本"
        align="center"
        prop="clientVersion"
        width="100"
      />
      <el-table-column
        label="终端系统版本"
        align="center"
        prop="systemVersion"
        width="100"
      />
      <el-table-column
        label="登录时间"
        align="center"
        prop="bindTime"
        width="180"
        :formatter="dateFormat"
      />
      <el-table-column label="位置" align="center" prop="location" />
      <el-table-column label="状态" align="center" prop="state" />
      <el-table-column
        label="操作"
        align="center"
        class-name="small-padding fixed-width"
      >
        <template slot-scope="scope">
          <el-button
            size="mini"
            type="text"
            icon="el-icon-edit"
            @click="sendMessage(scope.row)"
            >发送消息</el-button
          >
        </template>
      </el-table-column>
    </el-table>

    <pagination
      v-show="total > 0"
      :total="total"
      :page.sync="queryParams.pageNum"
      :limit.sync="queryParams.pageSize"
      @pagination="getList"
    />

    <!-- 发送消息对话框 -->
    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
        <el-form-item label="接收账号" prop="account">
          <el-input v-model="form.account" readonly="true" />
        </el-form-item>
        <el-form-item label="消息内容" prop="nid">
          <el-input
            v-model="form.content"
            type="textarea"
            placeholder="请输入内容"
          />
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button type="primary" @click="submitForm">确 定</el-button>
        <el-button @click="cancel">取 消</el-button>
      </div>
    </el-dialog>
  </div>
</template>

<script>
import { listOnlineUser, doSendMessage } from "@/api/system/onlineUser";
import moment from "moment";
export default {
  name: "OnlineUser",
  components: {},
  data() {
    return {
      // 遮罩层
      loading: true,
      // 选中数组
      ids: [],
      // 非单个禁用
      single: true,
      // 非多个禁用
      multiple: true,
      // 显示搜索条件
      showSearch: true,
      // 总条数
      total: 0,
      // 在线用户表格数据
      onlineUserList: [],
      // 弹出层标题
      title: "",
      // 是否显示弹出层
      open: false,
      // 查询参数
      queryParams: {
        pageNum: 1,
        pageSize: 10,
        account: null,
        host: null,
      },
      // 表单参数
      form: {},
      // 表单校验
      rules: {},
    };
  },
  created() {
    this.getList();
  },
  methods: {
    //时间格式化
    dateFormat: function (row, column) {
      debugger;
      var date = row[column.property];
      if (date == undefined) {
        return "";
      }
      return moment(date).format("YYYY-MM-DD HH:mm:ss");
    },
    /** 查询在线用户列表 */
    getList() {
      this.loading = true;
      listOnlineUser().then((response) => {
        debugger;
        this.onlineUserList = response.data;
        this.total = response.total;
        this.loading = false;
      });
    },
    // 取消按钮
    cancel() {
      this.open = false;
      this.reset();
    },
    // 表单重置
    reset() {
      this.form = {
        id: null,
        account: null,
        host: null,
      };
      this.resetForm("form");
    },
    /** 搜索按钮操作 */
    handleQuery() {
      this.queryParams.pageNum = 1;
      this.getList();
    },
    /** 重置按钮操作 */
    resetQuery() {
      this.resetForm("queryForm");
      this.handleQuery();
    },

    /** 发送消息按钮操作 */
    sendMessage(row) {
      this.reset();
      this.open = true;
      this.title = "发送消息";
      this.form.account = row.account;
    }, 
    /** 提交按钮 */
    submitForm() {
      this.$refs["form"].validate((valid) => {
        if (valid) {
          debugger;
          doSendMessage(this.form).then((response) => {
            this.msgSuccess("发送成功");
            this.open = false;
          });
        }
      });
    },
  },
};
</script>

然后将请求的js的代码修改为

import request from '@/utils/request'

// 查询在线用户列表
export function listOnlineUser() {
    return request({
        url: '/system/onlineUser/list',
        method: 'get',
    })
}



// 发送消息
export function doSendMessage(data) {
    var message = {};
    message.content = data.content;
    message.action = 2;
    message.sender = 'system';
    message.receiver = data.account;
    message.format = '0';
    debugger
    return request({
        url: '/api/message/send',
        method: 'post',
        data: message
    })
}

然后运行前端和后端项目,并且使用其app的demo,连接后台后,在新版的在线用户中,就可以看到app端连接的用户了

 

点击后面的发送消息按钮

 

 

点击发送

 

 

就会在app端收到推送,然后使用其示例代码中的cim-client-web中的index.html登录之后,就会在在线用户列表中看到

 

 

然后给web端发送消息

 

 

示例代码下载

https://download.csdn.net/download/BADAO_LIUMANG_QIZHI/15727997


标签:COMMENT,netty,Vue,SpringBoot,utf8,cim,NULL,文末,服务端
From: https://www.cnblogs.com/badaoliumangqizhi/p/18059150

相关文章

  • Vue学习笔记38--单文件组件
    单文件组件命名规则如下所示:------单个单词命名规则:------方式一:temp.vue方式二:Temp.vue建议使用(可和vue开发者工具呼应)------多个单词命名规则------方式一:my-temp.vue方式二:MyTemp.vue建议使用(可和vue开发者工具呼应)组件交互相关的代码暴露方式:1.分别暴露:exportconst......
  • Vue+Axios的方法异步回调顺序问题
    一、问题阐述有的时候我们需要控制异步函数的执行顺序,比如a方法中如果要用到异步函数b方法的请求结果,就需要进行顺序控制,否则a函数先执行就会导致找不到数据直接报错。二、方法1.异步控制1.1.async,await等做异步控制1.2修改函数放置位置达到异步控制效果(我遇到的情况无效,但......
  • vue_居中左对齐
    div中文本居中对齐后,然后再左对齐如下效果:===============================22345645=================2345678987654============12========================================= <!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8......
  • Vue调试神器vue-devtools配置 / 解决提示 Download the Vue Devtools extension for a
    访问Vue页面,控制台提示:    ......
  • vue2项目中不能直接在store中声明响应式变量,vue3项目中能在store中直接声明响应式变量
    vue2项目中不能直接在store中声明响应式变量,vue3项目中能在store中直接声明响应式变量,页面元素也会响应式生效在Vue2项目中,store中的状态默认情况下是不具备响应式的特性的。这是因为Vue2.x使用的是基于对象定义的Vue.observable()来创建响应式对象,而store中的状态是通......
  • Vue学习笔记37--内置关系
    示例一:<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width,initial-scale=1.0"><title>一个重要的内置关系</title>......
  • SpringBoot使用外部Web容器的解决方案
    SpringBoot默认内嵌了Web容器(如Tomcat、Jetty或Undertow),这使得应用可以作为独立的可执行JAR或WAR文件运行,无需外部Web容器。然而,在某些情况下,你可能想要将SpringBoot应用部署到外部的Web容器中,比如ApacheTomcat或Jetty。嵌入式的Web容器:应用可以打包成可执行的Jar。优点:简单......
  • vue使用超图openlayers调用mapv实现蜂窝图
    在用超图openlayer开发的时候遇到问题,在此作为记录。文字描述不对的地方请多担待, 下载依赖,npmimapv 按需引入,因为官网例子是普通的html引入,{mapv}引入方式调用的是超图@supermap中的方法。DataSet是mapv的方法 import{Mapv} from'@supermap/iclient-ol/overla......
  • 通过debug搞清楚.vue文件怎么变成.js文件
    前言我们每天写的vue代码都是写在vue文件中,但是浏览器却只认识html、css、js等文件类型。所以这个时候就需要一个工具将vue文件转换为浏览器能够认识的js文件,想必你第一时间就想到了webpack或者vite。但是webpack和vite本身是没有能力处理vue文件的,其实实际背后生效的是vue-loade......
  • 玩转SpringBoot:SpringBoot的几种定时任务实现方式
    引言在现代软件开发中,定时任务是一种常见的需求,用于执行周期性的任务或在特定的时间点执行任务。这些任务可能涉及数据同步、数据备份、报表生成、缓存刷新等方面,对系统的稳定性和可靠性有着重要的影响。SpringBoot提供了强大且简单的定时任务功能,使开发人员能够轻松地管理和执......