diff --git a/.gitignore b/.gitignore
index 6e07688..083aa4a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,10 +9,10 @@
/im-server/src/main/resources/logback-prod.xml
/im-commom/im-commom.iml
/im-uniapp/node_modules/
-/im-ui/package-lock.json
+/im-web/package-lock.json
/im-uniapp/unpackage/
/im-uniapp/hybrid/
/im-uniapp/package-lock.json
-/im-ui/src/components/rtc/LocalVideo.vue
-/im-ui/src/components/rtc/RemoteVideo.vue
-/im-ui/src/components/rtc/RtcGroupAcceptor.vue
+/im-web/src/components/rtc/LocalVideo.vue
+/im-web/src/components/rtc/RemoteVideo.vue
+/im-web/src/components/rtc/RtcGroupAcceptor.vue
diff --git a/README.md b/README.md
index 032bf40..4ac5a63 100644
--- a/README.md
+++ b/README.md
@@ -6,8 +6,9 @@
1. 盒子IM是一个仿微信实现的网页版聊天软件,不依赖任何第三方收费组件。
-1. 支持私聊、群聊、离线消息、发送语音、图片、文件、emoji表情等功能
-1. 支持音视频通话(基于webrtc实现,需要ssl证书)
+1. 支持私聊、群聊、离线消息、发送语音、图片、文件、已读未读、群@等功能
+1. 支持单人、多人音视频通话(基于原生webrtc实现,需要ssl证书)
+1. uniapp端兼容app、h5、微信小程序,可与web端同时在线,并保持消息同步
1. 后端采用springboot+netty实现,网页端使用vue,移动端使用uniapp
1. 服务器支持集群化部署,每个im-server仅处理自身连接用户的消息
@@ -16,13 +17,11 @@
#### 近期更新
-发布2.0版本,本次更新加入了uniapp移动端:
+发布3.0版本:
-- 支持移动端和web端同时在线,多端消息同步
-- 目前已兼容h5、微信小程序,安卓和IOS
-- 聊天窗口加入已读未读显示
-- 群聊加入@功能
-- 界面风格升级,表情包更新、生成文字头像等
+- 后台管理端上线,后台管理代码仓库地址:https://gitee.com/bluexsx/box-im-admin
+- 框架和组件版本全面升级: jdk17、springboot3.3、node18等
+- 部分界面,功能、性能优化
#### 在线体验
@@ -37,27 +36,18 @@

-微信小程序:
-
-
-
-
-#### 相关项目
-
-一位网友的开源项目,基于盒子IM接口开发的仿QQ客户端,有兴趣的小伙伴可以也关注一下:
-
-https://gitee.com/zyzyteam/crim
+由于微信小程序每次发布审核过于严苛和繁琐,暂时不再提供体验环境,但uniapp端依然会继续兼容小程序
#### 项目结构
-| 模块 | 功能 |
-|-------------|------------|
-| im-platform | 与页面进行交互,处理业务请求 |
-| im-server | 推送聊天消息|
-| im-client | 消息推送sdk|
-| im-common | 公共包 |
-| im-ui | web页面 |
-| im-uniapp | app页面 |
+| 模块 | 功能 |
+|-------------|----------------------------------|
+| im-platform | 业务平台服务,负责处理来自用户的业务请求(http) |
+| im-server | 消息推送服务,不依赖业务,负责将消息推送给用户(ws) |
+| im-client | 消息推送sdk, 任何服务均可集成此sdk与im-server通信 |
+| im-common | 公共包 |
+| im-ui | web页面 |
+| im-uniapp | uniapp页面 |
#### 消息推送方案

@@ -69,12 +59,12 @@ https://gitee.com/zyzyteam/crim
#### 本地快速部署
1.安装运行环境
-- 安装node:v14.16.0
-- 安装jdk:1.8
-- 安装maven:3.6.3
-- 安装mysql:5.7,密码分别为root/root,运行sql脚本(脚本在im-platfrom的resources/db目录)
-- 安装redis:5.0
-- 安装minio,命令端口使用9001,并创建一个名为"box-im"的bucket,并设置访问权限为公开
+- 安装node:v18.19.0
+- 安装jdk:17
+- 安装maven:3.9.6
+- 安装mysql:8.0,账号密码分别为root/root,创建名为im_platform的数据库,运行db/im_platfrom.sql脚本
+- 安装redis:6.2
+- 安装minio:RELEASE.2024-xx,使用默认账号、密码、端口
2.启动后端服务
```
@@ -85,161 +75,37 @@ java -jar ./im-server/target/im-server.jar
3.启动前端web
```
-cd im-ui
+cd im-web
npm install
npm run serve
```
访问 http://localhost:8080
-
4.启动uniapp-h5
将im-uniapp目录导入HBuilderX,点击菜单"运行"->"开发环境-h5"
访问 http://localhost:5173
-#### 快速接入
-消息推送的请求代码已经封装在im-client包中,对于需要接入im-server的小伙伴,可以按照下面的教程快速的将IM功能集成到自己的项目中。
-
-注意服务器端和前端都需要接入,服务器端发送消息,前端接收消息。
-
-4.1 服务器端接入
-
-引入pom文件
-```
-
- com.bx
- im-client
- 2.0.0
-
-```
-内容使用了redis进行通信,所以要配置redis地址:
-
-```
-spring:
- redis:
- host: 127.0.0.1
- port: 6379
-```
-
-直接把IMClient通过@Autowire导进来就可以发送消息了,IMClient 只有2个接口:
-```
-public class IMClient {
-
- /**
- * 发送私聊消息
- *
- * @param message 私有消息
- */
- public void sendPrivateMessage(IMPrivateMessage message);
-
- /**
- * 发送群聊消息(发送结果通过MessageListener接收)
- *
- * @param message 群聊消息
- */
- public void sendGroupMessage(IMGroupMessage message);
-}
-```
-
-发送私聊消息(群聊也是类似的方式):
-```
- @Autowired
- private IMClient imClient;
-
- public void sendMessage(){
- IMPrivateMessage sendMessage = new IMPrivateMessage<>();
- // 发送方的id和终端类型
- sendMessage.setSender(new IMUserInfo(1L, IMTerminalType.APP.code()));
- // 对方的id
- sendMessage.setRecvId(2L);
- // 推送给对方所有终端
- sendMessage.setRecvTerminals(IMTerminalType.codes());
- // 同时推送给自己的其他类型终端
- sendMessage.setSendToSelf(true);
- // 需要回推发送结果,将在IMListener接收发送结果
- sendMessage.setSendResult(true);
- // 推送的内容
- sendMessage.setData(msgInfo);
- // 推送消息
- imClient.sendPrivateMessage(sendMessage);
-}
-
-```
-监听发送结果:
-1.编写消息监听类,实现MessageListener,并加上@IMListener
-2.发送消息时指定sendResult为true
-```
-@Slf4j
-@IMListener(type = IMListenerType.ALL)
-public class PrivateMessageListener implements MessageListener {
-
- @Override
- public void process(IMSendResult result){
- PrivateMessageVO messageInfo = result.getData();
- if(result.getCode().equals(IMSendCode.SUCCESS.code())){
- log.info("消息发送成功,消息id:{},发送者:{},接收者:{},终端:{}",messageInfo.getId(),result.getSender().getId(),result.getReceiver().getId(),result.getReceiver().getTerminal());
- }
- }
-}
-```
-
-4.2 前端接入
-首先将im-ui/src/api/wssocket.js拷贝到自己的项目。
-
-接入代码如下:
-```
-import * as wsApi from './api/wssocket';
-
-let wsUrl = 'ws://localhost:8878/im'
-let token = "您的token";
-wsApi.connect(wsUrl,token);
-wsApi.onConnect(() => {
- // 连接打开
- console.log("连接成功");
-});
-wsApi.onMessage((cmd,msgInfo) => {
- if (cmd == 2) {
- // 异地登录,强制下线
- console.log("您已在其他地方登陆,将被强制下线");
- } else if (cmd == 3) {
- // 私聊消息
- console.log(msgInfo);
- } else if (cmd == 4) {
- // 群聊消息
- console.log(msgInfo);
- }
-})
-wsApi.onClose((e) => {
- if (e.code != 3000) {
- console.log("意外断开,进行重连");
- wsApi.reconnect(wsUrl,token);
- }else{
- console.log("主动断开");
- }
-});
-```
-
-
#### 界面截图
私聊:
-
+
群聊:
-
-
-
+
+群通话:
+
好友列表:
-
+
-群聊列表:
-
+群列表:
+
-微信小程序:
-
+移动端APP:
+
-
+
#### 加入交流群
1群目前已满员,扫码进入2群:
diff --git a/im-platform/src/main/resources/db/db.sql b/db/im-platform.sql
similarity index 71%
rename from im-platform/src/main/resources/db/db.sql
rename to db/im-platform.sql
index 1ea4b86..d90976f 100644
--- a/im-platform/src/main/resources/db/db.sql
+++ b/db/im-platform.sql
@@ -1,19 +1,20 @@
-use `box-im`;
create table `im_user`(
`id` bigint not null auto_increment primary key comment 'id',
`user_name` varchar(255) not null comment '用户名',
`nick_name` varchar(255) not null comment '用户昵称',
`head_image` varchar(255) default '' comment '用户头像',
`head_image_thumb` varchar(255) default '' comment '用户头像缩略图',
- `password` varchar(255) not null comment '密码(明文)',
+ `password` varchar(255) not null comment '密码',
`sex` tinyint(1) default 0 comment '性别 0:男 1:女',
+ `is_banned` tinyint(1) default 0 comment '是否被封禁 0:否 1:是',
+ `reason` varchar(255) default '' comment '被封禁原因',
`type` smallint default 1 comment '用户类型 1:普通用户 2:审核账户',
`signature` varchar(1024) default '' comment '个性签名',
`last_login_time` datetime DEFAULT null comment '最后登录时间',
`created_time` datetime DEFAULT CURRENT_TIMESTAMP comment '创建时间',
unique key `idx_user_name`(user_name),
key `idx_nick_name`(nick_name)
-) ENGINE=InnoDB CHARSET=utf8mb3 comment '用户';
+) ENGINE=InnoDB CHARSET=utf8mb4 comment '用户';
create table `im_friend`(
`id` bigint not null auto_increment primary key comment 'id',
@@ -24,18 +25,18 @@ create table `im_friend`(
`created_time` datetime DEFAULT CURRENT_TIMESTAMP comment '创建时间',
key `idx_user_id` (`user_id`),
key `idx_friend_id` (`friend_id`)
-) ENGINE=InnoDB CHARSET=utf8mb3 comment '好友';
+) ENGINE=InnoDB CHARSET=utf8mb4 comment '好友';
create table `im_private_message`(
`id` bigint not null auto_increment primary key comment 'id',
`send_id` bigint not null comment '发送用户id',
`recv_id` bigint not null comment '接收用户id',
`content` text comment '发送内容',
- `type` tinyint(1) NOT NULL comment '消息类型 0:文字 1:图片 2:文件 3:语音 10:系统提示',
- `status` tinyint(1) NOT NULL comment '状态 0:未读 1:已读 2:撤回',
+ `type` tinyint(1) NOT NULL comment '消息类型 0:文字 1:图片 2:文件 3:语音 4:视频 21:提示',
+ `status` tinyint(1) NOT NULL comment '状态 0:未读 1:已读 2:撤回 3:已读',
`send_time` datetime DEFAULT CURRENT_TIMESTAMP comment '发送时间',
key `idx_send_recv_id` (`send_id`,`recv_id`)
-)ENGINE=InnoDB CHARSET=utf8mb3 comment '私聊消息';
+)ENGINE=InnoDB CHARSET=utf8mb4 comment '私聊消息';
create table `im_group`(
@@ -45,24 +46,26 @@ create table `im_group`(
`head_image` varchar(255) default '' comment '群头像',
`head_image_thumb` varchar(255) default '' comment '群头像缩略图',
`notice` varchar(1024) default '' comment '群公告',
- `remark` varchar(255) default '' comment '群备注',
+ `is_banned` tinyint(1) default 0 comment '是否被封禁 0:否 1:是',
+ `reason` varchar(255) default '' comment '被封禁原因',
`deleted` tinyint(1) default 0 comment '是否已删除',
`created_time` datetime default CURRENT_TIMESTAMP comment '创建时间'
-)ENGINE=InnoDB CHARSET=utf8mb3 comment '群';
+)ENGINE=InnoDB CHARSET=utf8mb4 comment '群';
create table `im_group_member`(
`id` bigint not null auto_increment primary key comment 'id',
`group_id` bigint not null comment '群id',
`user_id` bigint not null comment '用户id',
- `alias_name` varchar(255) DEFAULT '' comment '组内显示名称',
+ `user_nick_name` varchar(255) DEFAULT '' comment '用户昵称',
+ `remark_nick_name` varchar(255) DEFAULT '' comment '显示昵称备注',
`head_image` varchar(255) DEFAULT '' comment '用户头像',
- `remark` varchar(255) DEFAULT '' comment '备注',
+ `remark_group_name` varchar(255) DEFAULT '' comment '显示群名备注',
`quit` tinyint(1) DEFAULT 0 comment '是否已退出',
`quit_time` datetime DEFAULT NULL comment '退出时间',
`created_time` datetime DEFAULT CURRENT_TIMESTAMP comment '创建时间',
key `idx_group_id`(`group_id`),
key `idx_user_id`(`user_id`)
-)ENGINE=InnoDB CHARSET=utf8mb3 comment '群成员';
+)ENGINE=InnoDB CHARSET=utf8mb4 comment '群成员';
create table `im_group_message`(
`id` bigint not null auto_increment primary key comment 'id',
@@ -74,8 +77,16 @@ create table `im_group_message`(
`at_user_ids` varchar(1024) comment '被@的用户id列表,逗号分隔',
`receipt` tinyint DEFAULT 0 comment '是否回执消息',
`receipt_ok` tinyint DEFAULT 0 comment '回执消息是否完成',
- `type` tinyint(1) NOT NULL comment '消息类型 0:文字 1:图片 2:文件 3:语音 4:视频 10:系统提示' ,
- `status` tinyint(1) DEFAULT 0 comment '状态 0:未发出 1:已送达 2:撤回 3:已读',
+ `type` tinyint(1) NOT NULL comment '消息类型 0:文字 1:图片 2:文件 3:语音 4:视频 21:提示' ,
+ `status` tinyint(1) DEFAULT 0 comment '状态 0:未发出 2:撤回 ',
`send_time` datetime DEFAULT CURRENT_TIMESTAMP comment '发送时间',
key `idx_group_id` (group_id)
-)ENGINE=InnoDB CHARSET=utf8mb3 comment '群消息';
+)ENGINE=InnoDB CHARSET=utf8mb4 comment '群消息';
+
+create table `im_sensitive_word`(
+ `id` bigint not null auto_increment primary key comment 'id',
+ `content` varchar(64) not null comment '敏感词内容',
+ `enabled` tinyint DEFAULT 0 COMMENT '是否启用 0:未启用 1:启用',
+ `creator` bigint DEFAULT NULL COMMENT '创建者',
+ `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间'
+)ENGINE=InnoDB CHARSET=utf8mb4 comment '敏感词';
\ No newline at end of file
diff --git a/im-client/pom.xml b/im-client/pom.xml
index 320a3cb..4634ec7 100644
--- a/im-client/pom.xml
+++ b/im-client/pom.xml
@@ -5,7 +5,7 @@
box-im
com.bx
- 2.0.0
+ 3.0.0
4.0.0
@@ -15,12 +15,7 @@
com.bx
im-commom
- 2.0.0
-
-
-
- org.springframework.boot
- spring-boot-starter-data-redis
+ 3.0.0
\ No newline at end of file
diff --git a/im-client/src/main/java/com/bx/imclient/IMAutoConfiguration.java b/im-client/src/main/java/com/bx/imclient/IMAutoConfiguration.java
index 9f8fa84..445cb45 100644
--- a/im-client/src/main/java/com/bx/imclient/IMAutoConfiguration.java
+++ b/im-client/src/main/java/com/bx/imclient/IMAutoConfiguration.java
@@ -7,7 +7,7 @@ import org.springframework.context.annotation.Configuration;
@Slf4j
@Configuration
-@ComponentScan("com.bx.imclient")
+@ComponentScan(basePackages = "com.bx.imclient,com.bx.imcommon")
public class IMAutoConfiguration {
}
diff --git a/im-client/src/main/java/com/bx/imclient/IMClient.java b/im-client/src/main/java/com/bx/imclient/IMClient.java
index e94f0b8..e13518e 100644
--- a/im-client/src/main/java/com/bx/imclient/IMClient.java
+++ b/im-client/src/main/java/com/bx/imclient/IMClient.java
@@ -4,6 +4,7 @@ import com.bx.imclient.sender.IMSender;
import com.bx.imcommon.enums.IMTerminalType;
import com.bx.imcommon.model.IMGroupMessage;
import com.bx.imcommon.model.IMPrivateMessage;
+import com.bx.imcommon.model.IMSystemMessage;
import lombok.AllArgsConstructor;
import org.springframework.context.annotation.Configuration;
@@ -46,6 +47,16 @@ public class IMClient {
return imSender.getOnlineTerminal(userIds);
}
+ /**
+ * 发送系统消息(发送结果通过MessageListener接收)
+ *
+ * @param message 私有消息
+ */
+ public void sendSystemMessage(IMSystemMessage message){
+ imSender.sendSystemMessage(message);
+ }
+
+
/**
* 发送私聊消息(发送结果通过MessageListener接收)
*
@@ -65,4 +76,6 @@ public class IMClient {
}
+
+
}
diff --git a/im-client/src/main/java/com/bx/imclient/config/RedisConfig.java b/im-client/src/main/java/com/bx/imclient/config/RedisConfig.java
deleted file mode 100644
index 5ef996c..0000000
--- a/im-client/src/main/java/com/bx/imclient/config/RedisConfig.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package com.bx.imclient.config;
-
-import com.alibaba.fastjson.support.spring.FastJsonRedisSerializer;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.data.redis.connection.RedisConnectionFactory;
-import org.springframework.data.redis.core.RedisTemplate;
-import org.springframework.data.redis.serializer.StringRedisSerializer;
-
-@Configuration("IMRedisConfig")
-public class RedisConfig {
-
- @Bean("IMRedisTemplate")
- public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
- RedisTemplate redisTemplate = new RedisTemplate();
- redisTemplate.setConnectionFactory(redisConnectionFactory);
- // 设置值(value)的序列化采用FastJsonRedisSerializer
- redisTemplate.setValueSerializer(fastJsonRedisSerializer());
- redisTemplate.setHashValueSerializer(fastJsonRedisSerializer());
- // 设置键(key)的序列化采用StringRedisSerializer。
- redisTemplate.setKeySerializer(new StringRedisSerializer());
- redisTemplate.setHashKeySerializer(new StringRedisSerializer());
- redisTemplate.afterPropertiesSet();
- return redisTemplate;
- }
-
- public FastJsonRedisSerializer fastJsonRedisSerializer(){
- return new FastJsonRedisSerializer<>(Object.class);
- }
-
-}
diff --git a/im-client/src/main/java/com/bx/imclient/sender/IMSender.java b/im-client/src/main/java/com/bx/imclient/sender/IMSender.java
index 1e0a7f6..61d0a4d 100644
--- a/im-client/src/main/java/com/bx/imclient/sender/IMSender.java
+++ b/im-client/src/main/java/com/bx/imclient/sender/IMSender.java
@@ -8,33 +8,86 @@ import com.bx.imcommon.enums.IMListenerType;
import com.bx.imcommon.enums.IMSendCode;
import com.bx.imcommon.enums.IMTerminalType;
import com.bx.imcommon.model.*;
+import com.bx.imcommon.mq.RedisMQTemplate;
import lombok.RequiredArgsConstructor;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
-import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
-import javax.annotation.Resource;
import java.util.*;
@Service
@RequiredArgsConstructor
public class IMSender {
- @Resource(name="IMRedisTemplate")
- private RedisTemplate redisTemplate;
+ @Autowired
+ private RedisMQTemplate redisMQTemplate;
@Value("${spring.application.name}")
private String appName;
private final MessageListenerMulticaster listenerMulticaster;
+
+ public void sendSystemMessage(IMSystemMessage message){
+ // 根据群聊每个成员所连的IM-server,进行分组
+ Map sendMap = new HashMap<>();
+ for (Integer terminal : message.getRecvTerminals()) {
+ message.getRecvIds().forEach(id -> {
+ String key = String.join(":", IMRedisKey.IM_USER_SERVER_ID, id.toString(), terminal.toString());
+ sendMap.put(key,new IMUserInfo(id, terminal));
+ });
+ }
+ // 批量拉取
+ List