From 8fa8efc051f39fe37300e34a16abd24e74a833ab Mon Sep 17 00:00:00 2001 From: xsx <825657193@qq.com> Date: Tue, 16 Jul 2024 21:18:11 +0800 Subject: [PATCH 01/48] =?UTF-8?q?feat:=20=E5=B0=86redis=E5=B0=81=E8=A3=85?= =?UTF-8?q?=E6=88=90MQ?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/db/db.sql => db/im-platfrom.sql | 5 +- im-client/pom.xml | 5 - .../main/java/com/bx/imclient/IMClient.java | 2 + .../com/bx/imclient/config/RedisConfig.java | 31 ----- .../java/com/bx/imclient/sender/IMSender.java | 28 ++--- .../task/AbstractMessageResultTask.java | 15 +-- .../task/GroupMessageResultResultTask.java | 15 ++- .../task/PrivateMessageResultResultTask.java | 15 ++- im-commom/pom.xml | 5 + .../com/bx/imcommon/mq/RedisMQConfig.java | 33 ++++++ .../com/bx/imcommon/mq/RedisMQConsumer.java | 13 +++ .../com/bx/imcommon/mq/RedisMQListener.java | 29 +++++ .../com/bx/imcommon/mq/RedisMQPullTask.java | 109 ++++++++++++++++++ .../com/bx/imcommon/mq/RedisMQTemplate.java | 42 +++++++ .../util/ThreadPoolExecutorFactory.java | 29 +---- .../java/com/bx/implatform/IMPlatformApp.java | 13 +-- .../com/bx/implatform/contant/Constant.java | 13 ++- .../com/bx/implatform/contant/RedisKey.java | 31 +++-- .../com/bx/implatform/dto/GroupBanDTO.java | 21 ++++ .../com/bx/implatform/dto/GroupUnbanDTO.java | 19 +++ .../com/bx/implatform/dto/UserBanDTO.java | 22 ++++ .../com/bx/implatform/enums/MessageType.java | 59 ++-------- .../task/UserBannedConsumerTask.java | 50 ++++++++ .../task/AbstractPullMessageTask.java | 12 +- 24 files changed, 430 insertions(+), 186 deletions(-) rename im-platform/src/main/resources/db/db.sql => db/im-platfrom.sql (94%) delete mode 100644 im-client/src/main/java/com/bx/imclient/config/RedisConfig.java create mode 100644 im-commom/src/main/java/com/bx/imcommon/mq/RedisMQConfig.java create mode 100644 im-commom/src/main/java/com/bx/imcommon/mq/RedisMQConsumer.java create mode 100644 im-commom/src/main/java/com/bx/imcommon/mq/RedisMQListener.java create mode 100644 im-commom/src/main/java/com/bx/imcommon/mq/RedisMQPullTask.java create mode 100644 im-commom/src/main/java/com/bx/imcommon/mq/RedisMQTemplate.java create mode 100644 im-platform/src/main/java/com/bx/implatform/dto/GroupBanDTO.java create mode 100644 im-platform/src/main/java/com/bx/implatform/dto/GroupUnbanDTO.java create mode 100644 im-platform/src/main/java/com/bx/implatform/dto/UserBanDTO.java create mode 100644 im-platform/src/main/java/com/bx/implatform/task/UserBannedConsumerTask.java diff --git a/im-platform/src/main/resources/db/db.sql b/db/im-platfrom.sql similarity index 94% rename from im-platform/src/main/resources/db/db.sql rename to db/im-platfrom.sql index 1ea4b86..a5b44a9 100644 --- a/im-platform/src/main/resources/db/db.sql +++ b/db/im-platfrom.sql @@ -7,6 +7,8 @@ create table `im_user`( `head_image_thumb` varchar(255) default '' 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) comment '被封禁原因', `type` smallint default 1 comment '用户类型 1:普通用户 2:审核账户', `signature` varchar(1024) default '' comment '个性签名', `last_login_time` datetime DEFAULT null comment '最后登录时间', @@ -45,7 +47,8 @@ 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) comment '被封禁原因', `deleted` tinyint(1) default 0 comment '是否已删除', `created_time` datetime default CURRENT_TIMESTAMP comment '创建时间' )ENGINE=InnoDB CHARSET=utf8mb3 comment '群'; diff --git a/im-client/pom.xml b/im-client/pom.xml index 320a3cb..774bafb 100644 --- a/im-client/pom.xml +++ b/im-client/pom.xml @@ -17,10 +17,5 @@ im-commom 2.0.0 - - - org.springframework.boot - spring-boot-starter-data-redis - \ No newline at end of file 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..7a476b1 100644 --- a/im-client/src/main/java/com/bx/imclient/IMClient.java +++ b/im-client/src/main/java/com/bx/imclient/IMClient.java @@ -65,4 +65,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..2a4fb88 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,20 +8,20 @@ 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; @@ -34,7 +34,7 @@ public class IMSender { for (Integer terminal : message.getRecvTerminals()) { // 获取对方连接的channelId String key = String.join(":", IMRedisKey.IM_USER_SERVER_ID, message.getRecvId().toString(), terminal.toString()); - Integer serverId = (Integer)redisTemplate.opsForValue().get(key); + Integer serverId = (Integer)redisMQTemplate.opsForValue().get(key); // 如果对方在线,将数据存储至redis,等待拉取推送 if (serverId != null) { String sendKey = String.join(":", IMRedisKey.IM_MESSAGE_PRIVATE_QUEUE, serverId.toString()); @@ -45,7 +45,7 @@ public class IMSender { recvInfo.setSender(message.getSender()); recvInfo.setReceivers(Collections.singletonList(new IMUserInfo(message.getRecvId(), terminal))); recvInfo.setData(message.getData()); - redisTemplate.opsForList().rightPush(sendKey, recvInfo); + redisMQTemplate.opsForList().rightPush(sendKey, recvInfo); } else { IMSendResult result = new IMSendResult(); result.setSender(message.getSender()); @@ -65,7 +65,7 @@ public class IMSender { } // 获取终端连接的channelId String key = String.join(":", IMRedisKey.IM_USER_SERVER_ID, message.getSender().getId().toString(), terminal.toString()); - Integer serverId = (Integer)redisTemplate.opsForValue().get(key); + Integer serverId = (Integer)redisMQTemplate.opsForValue().get(key); // 如果终端在线,将数据存储至redis,等待拉取推送 if (serverId != null) { String sendKey = String.join(":", IMRedisKey.IM_MESSAGE_PRIVATE_QUEUE, serverId.toString()); @@ -76,7 +76,7 @@ public class IMSender { recvInfo.setSender(message.getSender()); recvInfo.setReceivers(Collections.singletonList(new IMUserInfo(message.getSender().getId(), terminal))); recvInfo.setData(message.getData()); - redisTemplate.opsForList().rightPush(sendKey, recvInfo); + redisMQTemplate.opsForList().rightPush(sendKey, recvInfo); } } } @@ -97,7 +97,7 @@ public class IMSender { }); } // 批量拉取 - List serverIds = redisTemplate.opsForValue().multiGet(sendMap.keySet()); + List serverIds = redisMQTemplate.opsForValue().multiGet(sendMap.keySet()); // 格式:map<服务器id,list<接收方>> Map> serverMap = new HashMap<>(); List offLineUsers = new LinkedList<>(); @@ -123,7 +123,7 @@ public class IMSender { recvInfo.setData(message.getData()); // 推送至队列 String key = String.join(":", IMRedisKey.IM_MESSAGE_GROUP_QUEUE, entry.getKey().toString()); - redisTemplate.opsForList().rightPush(key, recvInfo); + redisMQTemplate.opsForList().rightPush(key, recvInfo); } // 推送给自己的其他终端 @@ -134,7 +134,7 @@ public class IMSender { } // 获取终端连接的channelId String key = String.join(":", IMRedisKey.IM_USER_SERVER_ID, message.getSender().getId().toString(), terminal.toString()); - Integer serverId = (Integer)redisTemplate.opsForValue().get(key); + Integer serverId = (Integer)redisMQTemplate.opsForValue().get(key); // 如果终端在线,将数据存储至redis,等待拉取推送 if (serverId != null) { IMRecvInfo recvInfo = new IMRecvInfo(); @@ -145,7 +145,7 @@ public class IMSender { recvInfo.setSendResult(false); recvInfo.setData(message.getData()); String sendKey = String.join(":", IMRedisKey.IM_MESSAGE_GROUP_QUEUE, serverId.toString()); - redisTemplate.opsForList().rightPush(sendKey, recvInfo); + redisMQTemplate.opsForList().rightPush(sendKey, recvInfo); } } } @@ -178,7 +178,7 @@ public class IMSender { } } // 批量拉取 - List serverIds = redisTemplate.opsForValue().multiGet(userMap.keySet()); + List serverIds = redisMQTemplate.opsForValue().multiGet(userMap.keySet()); int idx = 0; Map> onlineMap = new HashMap<>(); for (Map.Entry entry : userMap.entrySet()) { @@ -195,7 +195,7 @@ public class IMSender { public Boolean isOnline(Long userId) { String key = String.join(":", IMRedisKey.IM_USER_SERVER_ID, userId.toString(), "*"); - return !Objects.requireNonNull(redisTemplate.keys(key)).isEmpty(); + return !Objects.requireNonNull(redisMQTemplate.keys(key)).isEmpty(); } public List getOnlineUser(List userIds){ diff --git a/im-client/src/main/java/com/bx/imclient/task/AbstractMessageResultTask.java b/im-client/src/main/java/com/bx/imclient/task/AbstractMessageResultTask.java index 154f2f3..001ffd2 100644 --- a/im-client/src/main/java/com/bx/imclient/task/AbstractMessageResultTask.java +++ b/im-client/src/main/java/com/bx/imclient/task/AbstractMessageResultTask.java @@ -5,13 +5,13 @@ import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.CommandLineRunner; -import javax.annotation.PreDestroy; -import java.util.concurrent.ExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; @Slf4j public abstract class AbstractMessageResultTask implements CommandLineRunner { - private static final ExecutorService EXECUTOR_SERVICE = ThreadPoolExecutorFactory.getThreadPoolExecutor(); + private static final ScheduledThreadPoolExecutor EXECUTOR_SERVICE = ThreadPoolExecutorFactory.getThreadPoolExecutor(); @Override public void run(String... args) { @@ -26,19 +26,12 @@ public abstract class AbstractMessageResultTask implements CommandLineRunner { log.error("任务调度异常", e); } if (!EXECUTOR_SERVICE.isShutdown()) { - Thread.sleep(100); - EXECUTOR_SERVICE.execute(this); + EXECUTOR_SERVICE.schedule(this,100, TimeUnit.MICROSECONDS); } } }); } - @PreDestroy - public void destroy() { - log.info("{}线程任务关闭", this.getClass().getSimpleName()); - EXECUTOR_SERVICE.shutdown(); - } - public abstract void pullMessage() throws InterruptedException; } diff --git a/im-client/src/main/java/com/bx/imclient/task/GroupMessageResultResultTask.java b/im-client/src/main/java/com/bx/imclient/task/GroupMessageResultResultTask.java index 400220d..d9fa639 100644 --- a/im-client/src/main/java/com/bx/imclient/task/GroupMessageResultResultTask.java +++ b/im-client/src/main/java/com/bx/imclient/task/GroupMessageResultResultTask.java @@ -6,12 +6,11 @@ import com.bx.imclient.listener.MessageListenerMulticaster; import com.bx.imcommon.contant.IMRedisKey; import com.bx.imcommon.enums.IMListenerType; import com.bx.imcommon.model.IMSendResult; +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.Component; - -import javax.annotation.Resource; import java.util.LinkedList; import java.util.List; import java.util.Objects; @@ -21,8 +20,8 @@ import java.util.Objects; @RequiredArgsConstructor public class GroupMessageResultResultTask extends AbstractMessageResultTask { - @Resource(name = "IMRedisTemplate") - private RedisTemplate redisTemplate; + @Autowired + private RedisMQTemplate redisMQTemplate; @Value("${spring.application.name}") private String appName; @@ -46,12 +45,12 @@ public class GroupMessageResultResultTask extends AbstractMessageResultTask { List loadBatch() { String key = StrUtil.join(":", IMRedisKey.IM_RESULT_GROUP_QUEUE, appName); //这个接口redis6.2以上才支持 - //List list = redisTemplate.opsForList().leftPop(key, batchSize); + //List list = redisMQTemplate.opsForList().leftPop(key, batchSize); List results = new LinkedList<>(); - JSONObject jsonObject = (JSONObject) redisTemplate.opsForList().leftPop(key); + JSONObject jsonObject = (JSONObject) redisMQTemplate.opsForList().leftPop(key); while (!Objects.isNull(jsonObject) && results.size() < batchSize) { results.add(jsonObject.toJavaObject(IMSendResult.class)); - jsonObject = (JSONObject) redisTemplate.opsForList().leftPop(key); + jsonObject = (JSONObject) redisMQTemplate.opsForList().leftPop(key); } return results; } diff --git a/im-client/src/main/java/com/bx/imclient/task/PrivateMessageResultResultTask.java b/im-client/src/main/java/com/bx/imclient/task/PrivateMessageResultResultTask.java index 2451db1..ac7355c 100644 --- a/im-client/src/main/java/com/bx/imclient/task/PrivateMessageResultResultTask.java +++ b/im-client/src/main/java/com/bx/imclient/task/PrivateMessageResultResultTask.java @@ -6,13 +6,12 @@ import com.bx.imclient.listener.MessageListenerMulticaster; import com.bx.imcommon.contant.IMRedisKey; import com.bx.imcommon.enums.IMListenerType; import com.bx.imcommon.model.IMSendResult; +import com.bx.imcommon.mq.RedisMQTemplate; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +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.Component; - -import javax.annotation.Resource; import java.util.LinkedList; import java.util.List; import java.util.Objects; @@ -22,8 +21,8 @@ import java.util.Objects; @RequiredArgsConstructor public class PrivateMessageResultResultTask extends AbstractMessageResultTask { - @Resource(name = "IMRedisTemplate") - private RedisTemplate redisTemplate; + @Autowired + private RedisMQTemplate redisMQTemplate; @Value("${spring.application.name}") private String appName; @@ -47,12 +46,12 @@ public class PrivateMessageResultResultTask extends AbstractMessageResultTask { List loadBatch() { String key = StrUtil.join(":", IMRedisKey.IM_RESULT_PRIVATE_QUEUE, appName); //这个接口redis6.2以上才支持 - //List list = redisTemplate.opsForList().leftPop(key, batchSize); + //List list = redisMQTemplate.opsForList().leftPop(key, batchSize); List results = new LinkedList<>(); - JSONObject jsonObject = (JSONObject) redisTemplate.opsForList().leftPop(key); + JSONObject jsonObject = (JSONObject) redisMQTemplate.opsForList().leftPop(key); while (!Objects.isNull(jsonObject) && results.size() < batchSize) { results.add(jsonObject.toJavaObject(IMSendResult.class)); - jsonObject = (JSONObject) redisTemplate.opsForList().leftPop(key); + jsonObject = (JSONObject) redisMQTemplate.opsForList().leftPop(key); } return results; } diff --git a/im-commom/pom.xml b/im-commom/pom.xml index 43fb929..61b263c 100644 --- a/im-commom/pom.xml +++ b/im-commom/pom.xml @@ -65,5 +65,10 @@ slf4j-api 1.7.36 + + + org.springframework.boot + spring-boot-starter-data-redis + \ No newline at end of file diff --git a/im-commom/src/main/java/com/bx/imcommon/mq/RedisMQConfig.java b/im-commom/src/main/java/com/bx/imcommon/mq/RedisMQConfig.java new file mode 100644 index 0000000..24ba4ba --- /dev/null +++ b/im-commom/src/main/java/com/bx/imcommon/mq/RedisMQConfig.java @@ -0,0 +1,33 @@ +package com.bx.imcommon.mq; + +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 +public class RedisMQConfig { + + @Bean + public RedisMQTemplate redisMQTemplate(RedisConnectionFactory redisConnectionFactory) { + RedisMQTemplate redisMQTemplate = new RedisMQTemplate(); + redisMQTemplate.setConnectionFactory(redisConnectionFactory); + // 设置值(value)的序列化采用FastJsonRedisSerializer + redisMQTemplate.setValueSerializer(fastJsonRedisSerializer()); + redisMQTemplate.setHashValueSerializer(fastJsonRedisSerializer()); + // 设置键(key)的序列化采用StringRedisSerializer。 + redisMQTemplate.setKeySerializer(new StringRedisSerializer()); + redisMQTemplate.setHashKeySerializer(new StringRedisSerializer()); + redisMQTemplate.afterPropertiesSet(); + return redisMQTemplate; + } + + @Bean + public FastJsonRedisSerializer fastJsonRedisSerializer(){ + return new FastJsonRedisSerializer<>(Object.class); + } + +} diff --git a/im-commom/src/main/java/com/bx/imcommon/mq/RedisMQConsumer.java b/im-commom/src/main/java/com/bx/imcommon/mq/RedisMQConsumer.java new file mode 100644 index 0000000..b9dd6bc --- /dev/null +++ b/im-commom/src/main/java/com/bx/imcommon/mq/RedisMQConsumer.java @@ -0,0 +1,13 @@ +package com.bx.imcommon.mq; + +import java.util.List; + +/** + * redis 队列消费者抽象类 + */ +public abstract class RedisMQConsumer { + + public void onMessage(T data){} + + public void onMessage(List datas){} +} diff --git a/im-commom/src/main/java/com/bx/imcommon/mq/RedisMQListener.java b/im-commom/src/main/java/com/bx/imcommon/mq/RedisMQListener.java new file mode 100644 index 0000000..eb5fd1e --- /dev/null +++ b/im-commom/src/main/java/com/bx/imcommon/mq/RedisMQListener.java @@ -0,0 +1,29 @@ +package com.bx.imcommon.mq; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * redis 队列消费监听注解 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface RedisMQListener { + + /** + * 队列,也是redis的key + */ + String queue(); + + /** + * 一次性拉取的数据数量 + */ + int batchSize() default 1; + + /** + * 拉取间隔周期,单位:ms + */ + int period() default 100; +} diff --git a/im-commom/src/main/java/com/bx/imcommon/mq/RedisMQPullTask.java b/im-commom/src/main/java/com/bx/imcommon/mq/RedisMQPullTask.java new file mode 100644 index 0000000..e0a3700 --- /dev/null +++ b/im-commom/src/main/java/com/bx/imcommon/mq/RedisMQPullTask.java @@ -0,0 +1,109 @@ +package com.bx.imcommon.mq; + +import com.alibaba.fastjson.JSONObject; + +import com.bx.imcommon.util.ThreadPoolExecutorFactory; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.data.redis.connection.RedisConnection; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; + +import javax.annotation.PreDestroy; +import javax.annotation.Resource; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.*; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +/** + * reids 队列拉取定时任务 + * + * @author: Blue + * @date: 2024-07-15 + * @version: 1.0 + */ +@Slf4j +@Component +public class RedisMQPullTask implements CommandLineRunner { + + private static final ScheduledThreadPoolExecutor EXECUTOR_SERVICE = + ThreadPoolExecutorFactory.getThreadPoolExecutor(); + + @Autowired(required = false) + private List consumers = Collections.emptyList(); + + @Autowired + private RedisMQTemplate redisMQTemplate; + + @Override + public void run(String... args) { + consumers.forEach((consumer -> { + // 注解参数 + RedisMQListener annotation = consumer.getClass().getAnnotation(RedisMQListener.class); + String key = annotation.queue(); + int batchSize = annotation.batchSize(); + int period = annotation.period(); + // 获取泛型类型 + Type superClass = consumer.getClass().getGenericSuperclass(); + Type type = ((ParameterizedType)superClass).getActualTypeArguments()[0]; + EXECUTOR_SERVICE.execute(new Runnable() { + @Override + public void run() { + List datas = new LinkedList<>(); + try { + // 拉取一个批次的数据 + List objects = pullBatch(key, batchSize); + for (Object obj : objects) { + if (obj instanceof JSONObject) { + JSONObject jsonObject = (JSONObject)obj; + Object data = jsonObject.toJavaObject(type); + consumer.onMessage(data); + datas.add(data); + } + } + if(!datas.isEmpty()){ + consumer.onMessage(datas); + } + } catch (Exception e) { + log.error("数据消费异常,队列:{}", key, e); + } + // 继续消费数据 + if (!EXECUTOR_SERVICE.isShutdown()) { + if (datas.size() < batchSize) { + // 数据已经消费完,等待下一个周期继续拉取 + EXECUTOR_SERVICE.schedule(this, period, TimeUnit.MICROSECONDS); + } else { + // 数据没有消费完,直接开启下一个消费周期 + EXECUTOR_SERVICE.execute(this); + } + } + } + }); + })); + } + + private List pullBatch(String key, Integer batchSize) { + List objects = new LinkedList<>(); + if (redisMQTemplate.isSupportBatchPull()) { + // 版本大于6.2,支持批量拉取 + objects = redisMQTemplate.opsForList().leftPop(key, 100); + } else { + // 版本小于6.2,只能逐条拉取 + Object obj = redisMQTemplate.opsForList().leftPop(key); + while (!Objects.isNull(obj) && objects.size() < batchSize) { + objects.add(obj); + obj = redisMQTemplate.opsForList().leftPop(key); + } + } + return objects; + } + + @PreDestroy + public void destory(){ + ThreadPoolExecutorFactory.shutDown(); + } +} diff --git a/im-commom/src/main/java/com/bx/imcommon/mq/RedisMQTemplate.java b/im-commom/src/main/java/com/bx/imcommon/mq/RedisMQTemplate.java new file mode 100644 index 0000000..f966579 --- /dev/null +++ b/im-commom/src/main/java/com/bx/imcommon/mq/RedisMQTemplate.java @@ -0,0 +1,42 @@ +package com.bx.imcommon.mq; + +import cn.hutool.core.util.StrUtil; +import org.apache.logging.log4j.util.Strings; +import org.springframework.data.redis.connection.RedisConnection; +import org.springframework.data.redis.core.RedisTemplate; + +import java.util.Properties; + +/** + * @author: 谢绍许 + * @date: 2024-07-16 + * @version: 1.0 + */ +public class RedisMQTemplate extends RedisTemplate { + + private String version = Strings.EMPTY; + + public String getVersion() { + if (version.isEmpty()) { + RedisConnection redisConnection = this.getConnectionFactory().getConnection(); + Properties properties = redisConnection.info(); + version = properties.getProperty("redis_version"); + } + return version; + } + + /** + * 是否支持批量拉取,redis版本大于6.2支持批量拉取 + * @return + */ + Boolean isSupportBatchPull() { + String version = getVersion(); + String[] arr = version.split("\\."); + if (arr.length < 2) { + return false; + } + Integer firVersion = Integer.valueOf(arr[0]); + Integer secVersion = Integer.valueOf(arr[1]); + return firVersion > 6 || (firVersion == 6 && secVersion >= 2); + } +} diff --git a/im-commom/src/main/java/com/bx/imcommon/util/ThreadPoolExecutorFactory.java b/im-commom/src/main/java/com/bx/imcommon/util/ThreadPoolExecutorFactory.java index d07e0f4..e2cdd6d 100644 --- a/im-commom/src/main/java/com/bx/imcommon/util/ThreadPoolExecutorFactory.java +++ b/im-commom/src/main/java/com/bx/imcommon/util/ThreadPoolExecutorFactory.java @@ -2,9 +2,7 @@ package com.bx.imcommon.util; import lombok.extern.slf4j.Slf4j; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.*; /** * 创建单例线程池 @@ -36,7 +34,7 @@ public final class ThreadPoolExecutorFactory { /** * 线程池对象 */ - private static volatile ThreadPoolExecutor threadPoolExecutor = null; + private static volatile ScheduledThreadPoolExecutor threadPoolExecutor = null; /** * 构造方法私有化 @@ -47,32 +45,17 @@ public final class ThreadPoolExecutorFactory { } } - /** - * 重写readResolve方法 - */ - private Object readResolve() { - //重写readResolve方法,防止序列化破坏单例 - return ThreadPoolExecutorFactory.getThreadPoolExecutor(); - } /** * 双检锁创建线程安全的单例 */ - public static ThreadPoolExecutor getThreadPoolExecutor() { + public static ScheduledThreadPoolExecutor getThreadPoolExecutor() { if (null == threadPoolExecutor) { synchronized (ThreadPoolExecutorFactory.class) { if (null == threadPoolExecutor) { - threadPoolExecutor = new ThreadPoolExecutor( + threadPoolExecutor = new ScheduledThreadPoolExecutor( //核心线程数 CORE_POOL_SIZE, - //最大线程数,包含临时线程 - MAX_IMUM_POOL_SIZE, - //临时线程的存活时间 - KEEP_ALIVE_TIME, - //时间单位(毫秒) - TimeUnit.MILLISECONDS, - //等待队列 - new LinkedBlockingQueue<>(QUEUE_SIZE), //拒绝策略 new ThreadPoolExecutor.CallerRunsPolicy() ); @@ -85,13 +68,13 @@ public final class ThreadPoolExecutorFactory { /** * 关闭线程池 */ - public void shutDown() { + public static void shutDown() { if (threadPoolExecutor != null) { threadPoolExecutor.shutdown(); } } - public void execute(Runnable runnable) { + public static void execute(Runnable runnable) { if (runnable == null) { return; } diff --git a/im-platform/src/main/java/com/bx/implatform/IMPlatformApp.java b/im-platform/src/main/java/com/bx/implatform/IMPlatformApp.java index e2aabee..ecb30a4 100644 --- a/im-platform/src/main/java/com/bx/implatform/IMPlatformApp.java +++ b/im-platform/src/main/java/com/bx/implatform/IMPlatformApp.java @@ -1,25 +1,18 @@ package com.bx.implatform; -import cn.hutool.core.util.StrUtil; -import com.bx.implatform.contant.RedisKey; import lombok.extern.slf4j.Slf4j; import org.mybatis.spring.annotation.MapperScan; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.ApplicationArguments; -import org.springframework.boot.ApplicationRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; +import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.EnableAspectJAutoProxy; -import org.springframework.data.redis.core.RedisTemplate; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; -import java.util.Set; + @Slf4j @EnableAspectJAutoProxy(exposeProxy = true) +@ComponentScan(basePackages = {"com.bx"}) @MapperScan(basePackages = {"com.bx.implatform.mapper"}) @SpringBootApplication(exclude = {SecurityAutoConfiguration.class})// 禁用secrity public class IMPlatformApp { diff --git a/im-platform/src/main/java/com/bx/implatform/contant/Constant.java b/im-platform/src/main/java/com/bx/implatform/contant/Constant.java index 6d23dfc..08e4125 100644 --- a/im-platform/src/main/java/com/bx/implatform/contant/Constant.java +++ b/im-platform/src/main/java/com/bx/implatform/contant/Constant.java @@ -2,20 +2,21 @@ package com.bx.implatform.contant; public final class Constant { - private Constant() { - } - + /** + * 系统用户id + */ + public static final Long SYS_USER_ID = 0L; /** * 最大图片上传大小 */ - public static final long MAX_IMAGE_SIZE = 20 * 1024 * 1024; + public static final Long MAX_IMAGE_SIZE = 20 * 1024 * 1024L; /** * 最大上传文件大小 */ - public static final long MAX_FILE_SIZE = 20 * 1024 * 1024; + public static final Long MAX_FILE_SIZE = 20 * 1024 * 1024L; /** * 群聊最大人数 */ - public static final long MAX_GROUP_MEMBER = 500; + public static final Long MAX_GROUP_MEMBER = 500L; } diff --git a/im-platform/src/main/java/com/bx/implatform/contant/RedisKey.java b/im-platform/src/main/java/com/bx/implatform/contant/RedisKey.java index 026e3c9..f6fbdd3 100644 --- a/im-platform/src/main/java/com/bx/implatform/contant/RedisKey.java +++ b/im-platform/src/main/java/com/bx/implatform/contant/RedisKey.java @@ -18,31 +18,38 @@ public final class RedisKey { * webrtc 群通话 */ public static final String IM_WEBRTC_GROUP_SESSION = "im:webrtc:group:session"; + + /** + * 用户被封禁消息队列 + */ + public static final String IM_QUEUE_USER_BANNED = "im:queue:user:banned"; + + /** + * 群聊被封禁消息队列 + */ + public static final String IM_QUEUE_GROUP_BANNED = "im:queue:group:banned"; + /** - * 缓存前缀 + * 群聊解封消息队列 */ - public static final String IM_CACHE = "im:cache:"; + public static final String IM_QUEUE_GROUP_UNBAN = "im:queue:user:unban"; + + /** * 缓存是否好友:bool */ - public static final String IM_CACHE_FRIEND = IM_CACHE + "friend"; + public static final String IM_CACHE_FRIEND = "im:cache:friend"; /** * 缓存群聊信息 */ - public static final String IM_CACHE_GROUP = IM_CACHE + "group"; + public static final String IM_CACHE_GROUP = "im:cache:group"; /** * 缓存群聊成员id */ - public static final String IM_CACHE_GROUP_MEMBER_ID = IM_CACHE + "group_member_ids"; - - /** - * 分布式锁前缀 - */ - public static final String IM_LOCK = "im:lock:"; - + public static final String IM_CACHE_GROUP_MEMBER_ID = "im:cache:group_member_ids"; /** * 分布式锁前缀 */ - public static final String IM_LOCK_RTC_GROUP = IM_LOCK + "rtc:group"; + public static final String IM_LOCK_RTC_GROUP = "im:lock:rtc:group"; } diff --git a/im-platform/src/main/java/com/bx/implatform/dto/GroupBanDTO.java b/im-platform/src/main/java/com/bx/implatform/dto/GroupBanDTO.java new file mode 100644 index 0000000..ec36509 --- /dev/null +++ b/im-platform/src/main/java/com/bx/implatform/dto/GroupBanDTO.java @@ -0,0 +1,21 @@ +package com.bx.implatform.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * @author: Blue + * @date: 2024-07-14 + * @version: 1.0 + */ +@Data +@ApiModel(description = "群组封禁") +public class GroupBanDTO { + + @ApiModelProperty(value = "群组id") + private Long id; + + @ApiModelProperty(value = "封禁原因") + private String reason; +} diff --git a/im-platform/src/main/java/com/bx/implatform/dto/GroupUnbanDTO.java b/im-platform/src/main/java/com/bx/implatform/dto/GroupUnbanDTO.java new file mode 100644 index 0000000..7a9a7fd --- /dev/null +++ b/im-platform/src/main/java/com/bx/implatform/dto/GroupUnbanDTO.java @@ -0,0 +1,19 @@ +package com.bx.implatform.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * @author: Blue + * @date: 2024-07-14 + * @version: 1.0 + */ +@Data +@ApiModel(description = "群组解锁") +public class GroupUnbanDTO { + + @ApiModelProperty(value = "群组id") + private Long id; + +} diff --git a/im-platform/src/main/java/com/bx/implatform/dto/UserBanDTO.java b/im-platform/src/main/java/com/bx/implatform/dto/UserBanDTO.java new file mode 100644 index 0000000..38571f9 --- /dev/null +++ b/im-platform/src/main/java/com/bx/implatform/dto/UserBanDTO.java @@ -0,0 +1,22 @@ +package com.bx.implatform.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * @author: Blue + * @date: 2024-07-14 + * @version: 1.0 + */ +@Data +@ApiModel("用户锁定DTO") +public class UserBanDTO { + + @ApiModelProperty(value = "用户id") + private Long id; + + @ApiModelProperty(value = "锁定原因") + private String reason; + +} diff --git a/im-platform/src/main/java/com/bx/implatform/enums/MessageType.java b/im-platform/src/main/java/com/bx/implatform/enums/MessageType.java index 21537d4..7a69c2c 100644 --- a/im-platform/src/main/java/com/bx/implatform/enums/MessageType.java +++ b/im-platform/src/main/java/com/bx/implatform/enums/MessageType.java @@ -9,6 +9,7 @@ import lombok.AllArgsConstructor; * 20-29: 提示类消息: 在会话中间显示的提示 * 30-39: UI交互类消息: 显示加载状态等 * 40-49: 操作交互类消息: 语音通话、视频通话消息等 + * 50-60: 后台操作类消息: 用户封禁、群组封禁等 * 100-199: 单人语音通话rtc信令 * 200-299: 多人语音通话rtc信令 * @@ -16,62 +17,22 @@ import lombok.AllArgsConstructor; @AllArgsConstructor public enum MessageType { - /** - * 文字 - */ - TEXT(0, "文字"), - /** - * 图片 - */ - IMAGE(1, "图片"), - /** - * 文件 - */ - FILE(2, "文件"), - /** - * 音频 - */ - AUDIO(3, "音频"), - /** - * 视频 - */ - VIDEO(4, "视频"), - - /** - * 撤回 - */ + TEXT(0, "文字消息"), + IMAGE(1, "图片消息"), + FILE(2, "文件消息"), + AUDIO(3, "语音消息"), + VIDEO(4, "视频消息"), RECALL(10, "撤回"), - /** - * 已读 - */ READED(11, "已读"), - - /** - * 消息已读回执 - */ RECEIPT(12, "消息已读回执"), - /** - * 时间提示 - */ TIP_TIME(20,"时间提示"), - /** - * 文字提示 - */ TIP_TEXT(21,"文字提示"), - /** - * 消息加载标记 - */ - LOADING(30,"加载中"), - - /** - * 语音通话提示 - */ + LOADING(30,"加载中标记"), ACT_RT_VOICE(40,"语音通话"), - /** - * 视频通话提示 - */ ACT_RT_VIDEO(41,"视频通话"), - + USER_BANNED(50,"用户封禁"), + GROUP_BANNED(51,"群聊封禁"), + GROUP_UNBAN(52,"群聊解封"), RTC_CALL_VOICE(100, "语音呼叫"), RTC_CALL_VIDEO(101, "视频呼叫"), RTC_ACCEPT(102, "接受"), diff --git a/im-platform/src/main/java/com/bx/implatform/task/UserBannedConsumerTask.java b/im-platform/src/main/java/com/bx/implatform/task/UserBannedConsumerTask.java new file mode 100644 index 0000000..31f61b8 --- /dev/null +++ b/im-platform/src/main/java/com/bx/implatform/task/UserBannedConsumerTask.java @@ -0,0 +1,50 @@ +package com.bx.implatform.task; + +import com.bx.imclient.IMClient; +import com.bx.imcommon.enums.IMTerminalType; +import com.bx.imcommon.model.IMPrivateMessage; +import com.bx.imcommon.model.IMUserInfo; +import com.bx.imcommon.mq.RedisMQConsumer; +import com.bx.imcommon.mq.RedisMQListener; +import com.bx.implatform.contant.Constant; +import com.bx.implatform.contant.RedisKey; +import com.bx.implatform.dto.UserBanDTO; +import com.bx.implatform.service.IUserService; +import com.bx.implatform.util.BeanUtils; +import com.bx.implatform.vo.PrivateMessageVO; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.core.parameters.P; +import org.springframework.stereotype.Component; + +/** + * @author: 谢绍许 + * @date: 2024-07-15 + * @version: 1.0 + */ +@Slf4j +@Component +@RequiredArgsConstructor +@RedisMQListener(queue = RedisKey.IM_QUEUE_USER_BANNED) +public class UserBannedConsumerTask extends RedisMQConsumer { + + private final IMClient imClient; + @Override + public void onMessage(UserBanDTO dto) { + + log.info("用户被封禁处理,userId:{},原因:{}",dto.getId(),dto.getReason()); + // 推送消息 + + PrivateMessageVO msgInfo = new PrivateMessageVO(); + msgInfo.setRecvId(dto.getId()); + msgInfo.setSendId(Constant.SYS_USER_ID); + msgInfo.setContent(dto.getReason()); + IMPrivateMessage sendMessage = new IMPrivateMessage<>(); + sendMessage.setSender(new IMUserInfo(Constant.SYS_USER_ID, IMTerminalType.WEB.code())); + sendMessage.setRecvId(dto.getId()); + sendMessage.setSendToSelf(false); + sendMessage.setData(msgInfo); + sendMessage.setSendResult(true); + imClient.sendPrivateMessage(sendMessage); + } +} diff --git a/im-server/src/main/java/com/bx/imserver/task/AbstractPullMessageTask.java b/im-server/src/main/java/com/bx/imserver/task/AbstractPullMessageTask.java index 6752869..905c5fc 100644 --- a/im-server/src/main/java/com/bx/imserver/task/AbstractPullMessageTask.java +++ b/im-server/src/main/java/com/bx/imserver/task/AbstractPullMessageTask.java @@ -9,11 +9,13 @@ import org.springframework.boot.CommandLineRunner; import javax.annotation.PreDestroy; import java.util.concurrent.ExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; @Slf4j public abstract class AbstractPullMessageTask implements CommandLineRunner { - private static final ExecutorService EXECUTOR_SERVICE = ThreadPoolExecutorFactory.getThreadPoolExecutor(); + private static final ScheduledThreadPoolExecutor EXECUTOR_SERVICE = ThreadPoolExecutorFactory.getThreadPoolExecutor(); @Autowired private IMServerGroup serverGroup; @@ -32,18 +34,12 @@ public abstract class AbstractPullMessageTask implements CommandLineRunner { log.error("任务调度异常", e); } if (!EXECUTOR_SERVICE.isShutdown()) { - Thread.sleep(100); - EXECUTOR_SERVICE.execute(this); + EXECUTOR_SERVICE.schedule(this,100, TimeUnit.MICROSECONDS); } } }); } - @PreDestroy - public void destroy() { - log.info("{}线程任务关闭", this.getClass().getSimpleName()); - EXECUTOR_SERVICE.shutdown(); - } public abstract void pullMessage() throws InterruptedException; } From 9f28d8c891391473caf59d11553597ad80582dac Mon Sep 17 00:00:00 2001 From: xsx <825657193@qq.com> Date: Tue, 16 Jul 2024 22:58:49 +0800 Subject: [PATCH 02/48] =?UTF-8?q?feat:=20im-client=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E5=8F=91=E9=80=81=E7=B3=BB=E7=BB=9F=E6=B6=88=E6=81=AFapi?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/bx/imclient/IMClient.java | 11 ++++ .../java/com/bx/imclient/sender/IMSender.java | 59 ++++++++++++++++- .../task/SystemMessageResultResultTask.java | 43 ++++++++++++ .../com/bx/imcommon/contant/IMRedisKey.java | 14 +++- .../java/com/bx/imcommon/enums/IMCmdType.java | 6 +- .../com/bx/imcommon/enums/IMListenerType.java | 8 ++- .../bx/imcommon/model/IMPrivateMessage.java | 2 +- .../com/bx/imcommon/model/IMSendResult.java | 2 +- .../bx/imcommon/model/IMSystemMessage.java | 34 ++++++++++ .../com/bx/imcommon/mq/RedisMQConsumer.java | 6 ++ .../com/bx/imcommon/mq/RedisMQPullTask.java | 8 +-- .../listener/SystemMessageListener.java | 36 ++++++++++ .../task/UserBannedConsumerTask.java | 29 +++------ .../com/bx/implatform/vo/SystemMessageVO.java | 27 ++++++++ .../processor/GroupMessageProcessor.java | 4 +- .../processor/PrivateMessageProcessor.java | 6 +- .../netty/processor/ProcessorFactory.java | 3 + .../processor/SystemMessageProcessor.java | 65 +++++++++++++++++++ .../imserver/task/PullGroupMessageTask.java | 2 +- .../imserver/task/PullPrivateMessageTask.java | 2 +- .../imserver/task/PullSystemMessageTask.java | 34 ++++++++++ 21 files changed, 359 insertions(+), 42 deletions(-) create mode 100644 im-client/src/main/java/com/bx/imclient/task/SystemMessageResultResultTask.java create mode 100644 im-commom/src/main/java/com/bx/imcommon/model/IMSystemMessage.java create mode 100644 im-platform/src/main/java/com/bx/implatform/listener/SystemMessageListener.java create mode 100644 im-platform/src/main/java/com/bx/implatform/vo/SystemMessageVO.java create mode 100644 im-server/src/main/java/com/bx/imserver/netty/processor/SystemMessageProcessor.java create mode 100644 im-server/src/main/java/com/bx/imserver/task/PullSystemMessageTask.java 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 7a476b1..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接收) * 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 2a4fb88..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 @@ -28,6 +28,59 @@ public class IMSender { 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 serverIds = redisMQTemplate.opsForValue().multiGet(sendMap.keySet()); + // 格式:map<服务器id,list<接收方>> + Map> serverMap = new HashMap<>(); + List offLineUsers = new LinkedList<>(); + int idx = 0; + for (Map.Entry entry : sendMap.entrySet()) { + Integer serverId = (Integer)serverIds.get(idx++); + if (!Objects.isNull(serverId)) { + List list = serverMap.computeIfAbsent(serverId, o -> new LinkedList<>()); + list.add(entry.getValue()); + } else { + // 加入离线列表 + offLineUsers.add(entry.getValue()); + } + } + // 逐个server发送 + for (Map.Entry> entry : serverMap.entrySet()) { + IMRecvInfo recvInfo = new IMRecvInfo(); + recvInfo.setCmd(IMCmdType.SYSTEM_MESSAGE.code()); + recvInfo.setReceivers(new LinkedList<>(entry.getValue())); + recvInfo.setServiceName(appName); + recvInfo.setSendResult(message.getSendResult()); + recvInfo.setData(message.getData()); + // 推送至队列 + String key = String.join(":", IMRedisKey.IM_MESSAGE_SYSTEM_QUEUE, entry.getKey().toString()); + redisMQTemplate.opsForList().rightPush(key, recvInfo); + } + // 对离线用户回复消息状态 + if(message.getSendResult() && !offLineUsers.isEmpty()){ + List results = new LinkedList<>(); + for (IMUserInfo offLineUser : offLineUsers) { + IMSendResult result = new IMSendResult(); + result.setReceiver(offLineUser); + result.setCode(IMSendCode.NOT_ONLINE.code()); + result.setData(message.getData()); + results.add(result); + } + listenerMulticaster.multicast(IMListenerType.SYSTEM_MESSAGE, results); + } + } + + public void sendPrivateMessage(IMPrivateMessage message) { List results = new LinkedList<>(); if(!Objects.isNull(message.getRecvId())){ @@ -84,10 +137,10 @@ public class IMSender { if(message.getSendResult() && !results.isEmpty()){ listenerMulticaster.multicast(IMListenerType.PRIVATE_MESSAGE, results); } + } public void sendGroupMessage(IMGroupMessage message) { - // 根据群聊每个成员所连的IM-server,进行分组 Map sendMap = new HashMap<>(); for (Integer terminal : message.getRecvTerminals()) { @@ -104,7 +157,7 @@ public class IMSender { int idx = 0; for (Map.Entry entry : sendMap.entrySet()) { Integer serverId = (Integer)serverIds.get(idx++); - if (serverId != null) { + if (!Objects.isNull(serverId)) { List list = serverMap.computeIfAbsent(serverId, o -> new LinkedList<>()); list.add(entry.getValue()); } else { @@ -136,7 +189,7 @@ public class IMSender { String key = String.join(":", IMRedisKey.IM_USER_SERVER_ID, message.getSender().getId().toString(), terminal.toString()); Integer serverId = (Integer)redisMQTemplate.opsForValue().get(key); // 如果终端在线,将数据存储至redis,等待拉取推送 - if (serverId != null) { + if (!Objects.isNull(serverId)) { IMRecvInfo recvInfo = new IMRecvInfo(); recvInfo.setCmd(IMCmdType.GROUP_MESSAGE.code()); recvInfo.setSender(message.getSender()); diff --git a/im-client/src/main/java/com/bx/imclient/task/SystemMessageResultResultTask.java b/im-client/src/main/java/com/bx/imclient/task/SystemMessageResultResultTask.java new file mode 100644 index 0000000..7bd3491 --- /dev/null +++ b/im-client/src/main/java/com/bx/imclient/task/SystemMessageResultResultTask.java @@ -0,0 +1,43 @@ +package com.bx.imclient.task; + +import cn.hutool.core.util.StrUtil; +import com.alibaba.fastjson.JSONObject; +import com.bx.imclient.listener.MessageListenerMulticaster; +import com.bx.imcommon.contant.IMRedisKey; +import com.bx.imcommon.enums.IMListenerType; +import com.bx.imcommon.model.IMSendResult; +import com.bx.imcommon.mq.RedisMQConsumer; +import com.bx.imcommon.mq.RedisMQListener; +import com.bx.imcommon.mq.RedisMQTemplate; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.util.LinkedList; +import java.util.List; +import java.util.Objects; + +@Slf4j +@Component +@RequiredArgsConstructor +@RedisMQListener(queue = IMRedisKey.IM_RESULT_SYSTEM_QUEUE, batchSize = 100, period = 100) +public class SystemMessageResultResultTask extends RedisMQConsumer { + + @Value("${spring.application.name}") + private String appName; + + private final MessageListenerMulticaster listenerMulticaster; + + @Override + public void onMessage(List results) { + listenerMulticaster.multicast(IMListenerType.SYSTEM_MESSAGE, results); + } + + @Override + public String generateKey() { + return StrUtil.join(":", IMRedisKey.IM_RESULT_SYSTEM_QUEUE, appName); + } + +} diff --git a/im-commom/src/main/java/com/bx/imcommon/contant/IMRedisKey.java b/im-commom/src/main/java/com/bx/imcommon/contant/IMRedisKey.java index eccbac8..a7a8bf3 100644 --- a/im-commom/src/main/java/com/bx/imcommon/contant/IMRedisKey.java +++ b/im-commom/src/main/java/com/bx/imcommon/contant/IMRedisKey.java @@ -2,7 +2,6 @@ package com.bx.imcommon.contant; public final class IMRedisKey { - private IMRedisKey() {} /** * im-server最大id,从0开始递增 @@ -13,13 +12,22 @@ public final class IMRedisKey { */ public static final String IM_USER_SERVER_ID = "im:user:server_id"; /** - * 未读私聊消息队列 + * 系统消息队列 + */ + public static final String IM_MESSAGE_SYSTEM_QUEUE = "im:message:system"; + /** + * 私聊消息队列 */ public static final String IM_MESSAGE_PRIVATE_QUEUE = "im:message:private"; /** - * 未读群聊消息队列 + * 群聊消息队列 */ public static final String IM_MESSAGE_GROUP_QUEUE = "im:message:group"; + + /** + * 系统消息发送结果队列 + */ + public static final String IM_RESULT_SYSTEM_QUEUE = "im:result:system"; /** * 私聊消息发送结果队列 */ diff --git a/im-commom/src/main/java/com/bx/imcommon/enums/IMCmdType.java b/im-commom/src/main/java/com/bx/imcommon/enums/IMCmdType.java index c302aa1..a6731ec 100644 --- a/im-commom/src/main/java/com/bx/imcommon/enums/IMCmdType.java +++ b/im-commom/src/main/java/com/bx/imcommon/enums/IMCmdType.java @@ -24,7 +24,11 @@ public enum IMCmdType { /** * 群发消息 */ - GROUP_MESSAGE(4, "群发消息"); + GROUP_MESSAGE(4, "群发消息"), + /** + * 系统消息 + */ + SYSTEM_MESSAGE(5,"系统消息"); private final Integer code; diff --git a/im-commom/src/main/java/com/bx/imcommon/enums/IMListenerType.java b/im-commom/src/main/java/com/bx/imcommon/enums/IMListenerType.java index 0448d2d..5dbb1b2 100644 --- a/im-commom/src/main/java/com/bx/imcommon/enums/IMListenerType.java +++ b/im-commom/src/main/java/com/bx/imcommon/enums/IMListenerType.java @@ -15,7 +15,13 @@ public enum IMListenerType { /** * 群聊消息 */ - GROUP_MESSAGE(2, "群聊消息"); + GROUP_MESSAGE(2, "群聊消息"), + /** + * 系统消息 + */ + SYSTEM_MESSAGE(3, "群聊消息"); + + private final Integer code; diff --git a/im-commom/src/main/java/com/bx/imcommon/model/IMPrivateMessage.java b/im-commom/src/main/java/com/bx/imcommon/model/IMPrivateMessage.java index 206c882..e33b540 100644 --- a/im-commom/src/main/java/com/bx/imcommon/model/IMPrivateMessage.java +++ b/im-commom/src/main/java/com/bx/imcommon/model/IMPrivateMessage.java @@ -26,7 +26,7 @@ public class IMPrivateMessage { private List recvTerminals = IMTerminalType.codes(); /** - * 是否发送给自己的其他终端,默认true + * 是否同步消息给自己的其他终端,默认true */ private Boolean sendToSelf = true; diff --git a/im-commom/src/main/java/com/bx/imcommon/model/IMSendResult.java b/im-commom/src/main/java/com/bx/imcommon/model/IMSendResult.java index c0354b9..9f4688c 100644 --- a/im-commom/src/main/java/com/bx/imcommon/model/IMSendResult.java +++ b/im-commom/src/main/java/com/bx/imcommon/model/IMSendResult.java @@ -16,7 +16,7 @@ public class IMSendResult { private IMUserInfo receiver; /** - * 发送状态 IMCmdType + * 发送状态编码 IMSendCode */ private Integer code; diff --git a/im-commom/src/main/java/com/bx/imcommon/model/IMSystemMessage.java b/im-commom/src/main/java/com/bx/imcommon/model/IMSystemMessage.java new file mode 100644 index 0000000..2241cb5 --- /dev/null +++ b/im-commom/src/main/java/com/bx/imcommon/model/IMSystemMessage.java @@ -0,0 +1,34 @@ +package com.bx.imcommon.model; + +import com.bx.imcommon.enums.IMTerminalType; +import lombok.Data; + +import java.util.LinkedList; +import java.util.List; + +@Data +public class IMSystemMessage { + + + /** + * 接收者id列表,为空表示向所有在线用户广播 + */ + private List recvIds = new LinkedList<>(); + + /** + * 接收者终端类型,默认全部 + */ + private List recvTerminals = IMTerminalType.codes(); + + /** + * 是否需要回推发送结果,默认true + */ + private Boolean sendResult = true; + + /** + * 消息内容 + */ + private T data; + + +} diff --git a/im-commom/src/main/java/com/bx/imcommon/mq/RedisMQConsumer.java b/im-commom/src/main/java/com/bx/imcommon/mq/RedisMQConsumer.java index b9dd6bc..e3ec88d 100644 --- a/im-commom/src/main/java/com/bx/imcommon/mq/RedisMQConsumer.java +++ b/im-commom/src/main/java/com/bx/imcommon/mq/RedisMQConsumer.java @@ -10,4 +10,10 @@ public abstract class RedisMQConsumer { public void onMessage(T data){} public void onMessage(List datas){} + + public String generateKey(){ + // 默认队列名就是redis的key + RedisMQListener annotation = this.getClass().getAnnotation(RedisMQListener.class); + return annotation.queue(); + } } diff --git a/im-commom/src/main/java/com/bx/imcommon/mq/RedisMQPullTask.java b/im-commom/src/main/java/com/bx/imcommon/mq/RedisMQPullTask.java index e0a3700..e53ae27 100644 --- a/im-commom/src/main/java/com/bx/imcommon/mq/RedisMQPullTask.java +++ b/im-commom/src/main/java/com/bx/imcommon/mq/RedisMQPullTask.java @@ -1,18 +1,12 @@ package com.bx.imcommon.mq; import com.alibaba.fastjson.JSONObject; - import com.bx.imcommon.util.ThreadPoolExecutorFactory; -import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; -import org.springframework.data.redis.connection.RedisConnection; -import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; - import javax.annotation.PreDestroy; -import javax.annotation.Resource; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.*; @@ -44,7 +38,7 @@ public class RedisMQPullTask implements CommandLineRunner { consumers.forEach((consumer -> { // 注解参数 RedisMQListener annotation = consumer.getClass().getAnnotation(RedisMQListener.class); - String key = annotation.queue(); + String key = consumer.generateKey(); int batchSize = annotation.batchSize(); int period = annotation.period(); // 获取泛型类型 diff --git a/im-platform/src/main/java/com/bx/implatform/listener/SystemMessageListener.java b/im-platform/src/main/java/com/bx/implatform/listener/SystemMessageListener.java new file mode 100644 index 0000000..2f22099 --- /dev/null +++ b/im-platform/src/main/java/com/bx/implatform/listener/SystemMessageListener.java @@ -0,0 +1,36 @@ +package com.bx.implatform.listener; + +import cn.hutool.core.collection.CollUtil; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.bx.imclient.annotation.IMListener; +import com.bx.imclient.listener.MessageListener; +import com.bx.imcommon.enums.IMListenerType; +import com.bx.imcommon.enums.IMSendCode; +import com.bx.imcommon.model.IMSendResult; +import com.bx.implatform.entity.PrivateMessage; +import com.bx.implatform.enums.MessageStatus; +import com.bx.implatform.service.IPrivateMessageService; +import com.bx.implatform.vo.PrivateMessageVO; +import com.bx.implatform.vo.SystemMessageVO; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +@Slf4j +@IMListener(type = IMListenerType.SYSTEM_MESSAGE) +public class SystemMessageListener implements MessageListener { + + @Override + public void process(List> results) { + for(IMSendResult result : results){ + SystemMessageVO messageInfo = result.getData(); + if (result.getCode().equals(IMSendCode.SUCCESS.code())) { + log.info("消息送达,消息id:{},接收者:{},终端:{}", messageInfo.getId(), result.getReceiver().getId(), result.getReceiver().getTerminal()); + } + } + } +} diff --git a/im-platform/src/main/java/com/bx/implatform/task/UserBannedConsumerTask.java b/im-platform/src/main/java/com/bx/implatform/task/UserBannedConsumerTask.java index 31f61b8..e112f9d 100644 --- a/im-platform/src/main/java/com/bx/implatform/task/UserBannedConsumerTask.java +++ b/im-platform/src/main/java/com/bx/implatform/task/UserBannedConsumerTask.java @@ -1,21 +1,17 @@ package com.bx.implatform.task; import com.bx.imclient.IMClient; -import com.bx.imcommon.enums.IMTerminalType; -import com.bx.imcommon.model.IMPrivateMessage; -import com.bx.imcommon.model.IMUserInfo; +import com.bx.imcommon.model.IMSystemMessage; import com.bx.imcommon.mq.RedisMQConsumer; import com.bx.imcommon.mq.RedisMQListener; -import com.bx.implatform.contant.Constant; import com.bx.implatform.contant.RedisKey; import com.bx.implatform.dto.UserBanDTO; -import com.bx.implatform.service.IUserService; -import com.bx.implatform.util.BeanUtils; -import com.bx.implatform.vo.PrivateMessageVO; +import com.bx.implatform.enums.MessageType; +import com.bx.implatform.vo.SystemMessageVO; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.security.core.parameters.P; import org.springframework.stereotype.Component; +import java.util.Collections; /** * @author: 谢绍许 @@ -31,20 +27,15 @@ public class UserBannedConsumerTask extends RedisMQConsumer { private final IMClient imClient; @Override public void onMessage(UserBanDTO dto) { - log.info("用户被封禁处理,userId:{},原因:{}",dto.getId(),dto.getReason()); - // 推送消息 - - PrivateMessageVO msgInfo = new PrivateMessageVO(); - msgInfo.setRecvId(dto.getId()); - msgInfo.setSendId(Constant.SYS_USER_ID); + // 推送消息将用户赶下线 + SystemMessageVO msgInfo = new SystemMessageVO(); + msgInfo.setType(MessageType.USER_BANNED.code()); msgInfo.setContent(dto.getReason()); - IMPrivateMessage sendMessage = new IMPrivateMessage<>(); - sendMessage.setSender(new IMUserInfo(Constant.SYS_USER_ID, IMTerminalType.WEB.code())); - sendMessage.setRecvId(dto.getId()); - sendMessage.setSendToSelf(false); + IMSystemMessage sendMessage = new IMSystemMessage<>(); + sendMessage.setRecvIds(Collections.singletonList(dto.getId())); sendMessage.setData(msgInfo); sendMessage.setSendResult(true); - imClient.sendPrivateMessage(sendMessage); + imClient.sendSystemMessage(sendMessage); } } diff --git a/im-platform/src/main/java/com/bx/implatform/vo/SystemMessageVO.java b/im-platform/src/main/java/com/bx/implatform/vo/SystemMessageVO.java new file mode 100644 index 0000000..692b6e1 --- /dev/null +++ b/im-platform/src/main/java/com/bx/implatform/vo/SystemMessageVO.java @@ -0,0 +1,27 @@ +package com.bx.implatform.vo; + +import com.bx.imcommon.serializer.DateToLongSerializer; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.Date; + +@Data +@ApiModel("系统消息VO") +public class SystemMessageVO { + + @ApiModelProperty(value = " 消息id") + private Long id; + + @ApiModelProperty(value = " 发送内容") + private String content; + + @ApiModelProperty(value = "消息内容类型 MessageType") + private Integer type; + + @ApiModelProperty(value = " 发送时间") + @JsonSerialize(using = DateToLongSerializer.class) + private Date sendTime; +} diff --git a/im-server/src/main/java/com/bx/imserver/netty/processor/GroupMessageProcessor.java b/im-server/src/main/java/com/bx/imserver/netty/processor/GroupMessageProcessor.java index eb75f17..6f62d91 100644 --- a/im-server/src/main/java/com/bx/imserver/netty/processor/GroupMessageProcessor.java +++ b/im-server/src/main/java/com/bx/imserver/netty/processor/GroupMessageProcessor.java @@ -13,10 +13,10 @@ import io.netty.channel.ChannelHandlerContext; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; import java.util.List; +import java.util.Objects; @Slf4j @Component @@ -33,7 +33,7 @@ public class GroupMessageProcessor extends AbstractMessageProcessor for (IMUserInfo receiver : receivers) { try { ChannelHandlerContext channelCtx = UserChannelCtxMap.getChannelCtx(receiver.getId(), receiver.getTerminal()); - if (channelCtx != null) { + if (!Objects.isNull(channelCtx)) { // 推送消息到用户 IMSendInfo sendInfo = new IMSendInfo(); sendInfo.setCmd(IMCmdType.GROUP_MESSAGE.code()); diff --git a/im-server/src/main/java/com/bx/imserver/netty/processor/PrivateMessageProcessor.java b/im-server/src/main/java/com/bx/imserver/netty/processor/PrivateMessageProcessor.java index 26803c5..3cae629 100644 --- a/im-server/src/main/java/com/bx/imserver/netty/processor/PrivateMessageProcessor.java +++ b/im-server/src/main/java/com/bx/imserver/netty/processor/PrivateMessageProcessor.java @@ -15,6 +15,8 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; +import java.util.Objects; + @Slf4j @Component @RequiredArgsConstructor @@ -26,10 +28,10 @@ public class PrivateMessageProcessor extends AbstractMessageProcessor sendInfo = new IMSendInfo<>(); sendInfo.setCmd(IMCmdType.PRIVATE_MESSAGE.code()); diff --git a/im-server/src/main/java/com/bx/imserver/netty/processor/ProcessorFactory.java b/im-server/src/main/java/com/bx/imserver/netty/processor/ProcessorFactory.java index 72aeb98..117334c 100644 --- a/im-server/src/main/java/com/bx/imserver/netty/processor/ProcessorFactory.java +++ b/im-server/src/main/java/com/bx/imserver/netty/processor/ProcessorFactory.java @@ -20,6 +20,9 @@ public class ProcessorFactory { case GROUP_MESSAGE: processor = SpringContextHolder.getApplicationContext().getBean(GroupMessageProcessor.class); break; + case SYSTEM_MESSAGE: + processor = SpringContextHolder.getApplicationContext().getBean(SystemMessageProcessor.class); + break; default: break; } diff --git a/im-server/src/main/java/com/bx/imserver/netty/processor/SystemMessageProcessor.java b/im-server/src/main/java/com/bx/imserver/netty/processor/SystemMessageProcessor.java new file mode 100644 index 0000000..2020252 --- /dev/null +++ b/im-server/src/main/java/com/bx/imserver/netty/processor/SystemMessageProcessor.java @@ -0,0 +1,65 @@ +package com.bx.imserver.netty.processor; + +import cn.hutool.core.util.StrUtil; +import com.bx.imcommon.contant.IMRedisKey; +import com.bx.imcommon.enums.IMCmdType; +import com.bx.imcommon.enums.IMSendCode; +import com.bx.imcommon.model.IMRecvInfo; +import com.bx.imcommon.model.IMSendInfo; +import com.bx.imcommon.model.IMSendResult; +import com.bx.imcommon.model.IMUserInfo; +import com.bx.imserver.netty.UserChannelCtxMap; +import io.netty.channel.ChannelHandlerContext; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; + +import java.util.Objects; + +@Slf4j +@Component +@RequiredArgsConstructor +public class SystemMessageProcessor extends AbstractMessageProcessor { + + private final RedisTemplate redisTemplate; + + @Override + public void process(IMRecvInfo recvInfo) { + IMUserInfo receiver = recvInfo.getReceivers().get(0); + log.info("接收到系统消息,接收者:{},内容:{}", receiver.getId(), recvInfo.getData()); + try { + ChannelHandlerContext channelCtx = UserChannelCtxMap.getChannelCtx(receiver.getId(), receiver.getTerminal()); + if (!Objects.isNull(channelCtx)) { + // 推送消息到用户 + IMSendInfo sendInfo = new IMSendInfo<>(); + sendInfo.setCmd(IMCmdType.SYSTEM_MESSAGE.code()); + sendInfo.setData(recvInfo.getData()); + channelCtx.channel().writeAndFlush(sendInfo); + // 消息发送成功确认 + sendResult(recvInfo, IMSendCode.SUCCESS); + } else { + // 消息推送失败确认 + sendResult(recvInfo, IMSendCode.NOT_FIND_CHANNEL); + log.error("未找到channel,接收者:{},内容:{}",receiver.getId(), recvInfo.getData()); + } + } catch (Exception e) { + // 消息推送失败确认 + sendResult(recvInfo, IMSendCode.UNKONW_ERROR); + log.error("发送异常,,接收者:{},内容:{}", receiver.getId(), recvInfo.getData(), e); + } + + } + + private void sendResult(IMRecvInfo recvInfo, IMSendCode sendCode) { + if (recvInfo.getSendResult()) { + IMSendResult result = new IMSendResult<>(); + result.setReceiver(recvInfo.getReceivers().get(0)); + result.setCode(sendCode.code()); + result.setData(recvInfo.getData()); + // 推送到结果队列 + String key = StrUtil.join(":",IMRedisKey.IM_RESULT_SYSTEM_QUEUE,recvInfo.getServiceName()); + redisTemplate.opsForList().rightPush(key, result); + } + } +} diff --git a/im-server/src/main/java/com/bx/imserver/task/PullGroupMessageTask.java b/im-server/src/main/java/com/bx/imserver/task/PullGroupMessageTask.java index 6d8e83f..48d37b5 100644 --- a/im-server/src/main/java/com/bx/imserver/task/PullGroupMessageTask.java +++ b/im-server/src/main/java/com/bx/imserver/task/PullGroupMessageTask.java @@ -23,7 +23,7 @@ public class PullGroupMessageTask extends AbstractPullMessageTask { @Override public void pullMessage() { - // 从redis拉取未读消息 + // 从redis拉取消息 String key = String.join(":", IMRedisKey.IM_MESSAGE_GROUP_QUEUE, IMServerGroup.serverId + ""); JSONObject jsonObject = (JSONObject) redisTemplate.opsForList().leftPop(key); while (!Objects.isNull(jsonObject)) { diff --git a/im-server/src/main/java/com/bx/imserver/task/PullPrivateMessageTask.java b/im-server/src/main/java/com/bx/imserver/task/PullPrivateMessageTask.java index 5edd0d6..007414e 100644 --- a/im-server/src/main/java/com/bx/imserver/task/PullPrivateMessageTask.java +++ b/im-server/src/main/java/com/bx/imserver/task/PullPrivateMessageTask.java @@ -23,7 +23,7 @@ public class PullPrivateMessageTask extends AbstractPullMessageTask { @Override public void pullMessage() { - // 从redis拉取未读消息 + // 从redis拉取消息 String key = String.join(":", IMRedisKey.IM_MESSAGE_PRIVATE_QUEUE, IMServerGroup.serverId + ""); JSONObject jsonObject = (JSONObject) redisTemplate.opsForList().leftPop(key); while (!Objects.isNull(jsonObject)) { diff --git a/im-server/src/main/java/com/bx/imserver/task/PullSystemMessageTask.java b/im-server/src/main/java/com/bx/imserver/task/PullSystemMessageTask.java new file mode 100644 index 0000000..c4a3f6d --- /dev/null +++ b/im-server/src/main/java/com/bx/imserver/task/PullSystemMessageTask.java @@ -0,0 +1,34 @@ +package com.bx.imserver.task; + +import com.bx.imcommon.contant.IMRedisKey; +import com.bx.imcommon.enums.IMCmdType; +import com.bx.imcommon.model.IMRecvInfo; +import com.bx.imcommon.mq.RedisMQConsumer; +import com.bx.imcommon.mq.RedisMQListener; +import com.bx.imserver.netty.IMServerGroup; +import com.bx.imserver.netty.processor.AbstractMessageProcessor; +import com.bx.imserver.netty.processor.ProcessorFactory; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * @author: 谢绍许 + * @date: 2024-07-16 + * @version: 1.0 + */ +@Slf4j +@Component +@RedisMQListener(queue = IMRedisKey.IM_MESSAGE_SYSTEM_QUEUE,batchSize = 10) +public class PullSystemMessageTask extends RedisMQConsumer { + + @Override + public void onMessage(IMRecvInfo recvInfo) { + AbstractMessageProcessor processor = ProcessorFactory.createProcessor(IMCmdType.SYSTEM_MESSAGE); + processor.process(recvInfo); + } + + public String generateKey(){ + // 队列名:im:message:system:{服务id} + return String.join(":", IMRedisKey.IM_MESSAGE_SYSTEM_QUEUE, IMServerGroup.serverId + ""); + } +} From 126cf03121d78403e8683553864394c5e83ba614 Mon Sep 17 00:00:00 2001 From: xsx <825657193@qq.com> Date: Tue, 16 Jul 2024 23:32:11 +0800 Subject: [PATCH 03/48] =?UTF-8?q?feat:=20=E6=95=B0=E6=8D=AE=E6=B6=88?= =?UTF-8?q?=E8=B4=B9=E6=94=B9=E7=94=A8=E6=B3=A8=E8=A7=A3=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../task/AbstractMessageResultTask.java | 34 ++++---------- .../task/GroupMessageResultResultTask.java | 43 +++-------------- .../task/PrivateMessageResultResultTask.java | 47 +++---------------- .../task/SystemMessageResultResultTask.java | 22 +-------- .../com/bx/imcommon/mq/RedisMQConsumer.java | 16 +++++++ .../com/bx/imcommon/mq/RedisMQPullTask.java | 24 +++++----- .../task/AbstractPullMessageTask.java | 38 ++++----------- .../imserver/task/PullGroupMessageTask.java | 25 +++------- .../imserver/task/PullPrivateMessageTask.java | 26 +++------- .../imserver/task/PullSystemMessageTask.java | 8 +--- 10 files changed, 75 insertions(+), 208 deletions(-) diff --git a/im-client/src/main/java/com/bx/imclient/task/AbstractMessageResultTask.java b/im-client/src/main/java/com/bx/imclient/task/AbstractMessageResultTask.java index 001ffd2..346cfab 100644 --- a/im-client/src/main/java/com/bx/imclient/task/AbstractMessageResultTask.java +++ b/im-client/src/main/java/com/bx/imclient/task/AbstractMessageResultTask.java @@ -1,37 +1,21 @@ package com.bx.imclient.task; -import com.bx.imcommon.util.ThreadPoolExecutorFactory; -import lombok.SneakyThrows; +import cn.hutool.core.util.StrUtil; +import com.bx.imcommon.mq.RedisMQConsumer; import lombok.extern.slf4j.Slf4j; -import org.springframework.boot.CommandLineRunner; - -import java.util.concurrent.ScheduledThreadPoolExecutor; -import java.util.concurrent.TimeUnit; +import org.springframework.beans.factory.annotation.Value; @Slf4j -public abstract class AbstractMessageResultTask implements CommandLineRunner { +public abstract class AbstractMessageResultTask extends RedisMQConsumer { - private static final ScheduledThreadPoolExecutor EXECUTOR_SERVICE = ThreadPoolExecutorFactory.getThreadPoolExecutor(); + @Value("${spring.application.name}") + private String appName; @Override - public void run(String... args) { - // 初始化定时器 - EXECUTOR_SERVICE.execute(new Runnable() { - @SneakyThrows - @Override - public void run() { - try { - pullMessage(); - } catch (Exception e) { - log.error("任务调度异常", e); - } - if (!EXECUTOR_SERVICE.isShutdown()) { - EXECUTOR_SERVICE.schedule(this,100, TimeUnit.MICROSECONDS); - } - } - }); + public String generateKey() { + return StrUtil.join(":", super.generateKey(), appName); } - public abstract void pullMessage() throws InterruptedException; + } diff --git a/im-client/src/main/java/com/bx/imclient/task/GroupMessageResultResultTask.java b/im-client/src/main/java/com/bx/imclient/task/GroupMessageResultResultTask.java index d9fa639..c8be7e2 100644 --- a/im-client/src/main/java/com/bx/imclient/task/GroupMessageResultResultTask.java +++ b/im-client/src/main/java/com/bx/imclient/task/GroupMessageResultResultTask.java @@ -1,58 +1,27 @@ package com.bx.imclient.task; -import cn.hutool.core.util.StrUtil; -import com.alibaba.fastjson.JSONObject; import com.bx.imclient.listener.MessageListenerMulticaster; import com.bx.imcommon.contant.IMRedisKey; import com.bx.imcommon.enums.IMListenerType; import com.bx.imcommon.model.IMSendResult; -import com.bx.imcommon.mq.RedisMQTemplate; +import com.bx.imcommon.mq.RedisMQListener; import lombok.RequiredArgsConstructor; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; -import java.util.LinkedList; + import java.util.List; -import java.util.Objects; @Component @RequiredArgsConstructor -public class GroupMessageResultResultTask extends AbstractMessageResultTask { - - @Autowired - private RedisMQTemplate redisMQTemplate; - - @Value("${spring.application.name}") - private String appName; - - @Value("${im.result.batch:100}") - private int batchSize; +@RedisMQListener(queue = IMRedisKey.IM_RESULT_GROUP_QUEUE, batchSize = 100) +public class GroupMessageResultResultTask extends AbstractMessageResultTask { private final MessageListenerMulticaster listenerMulticaster; @Override - public void pullMessage() { - List results; - do { - results = loadBatch(); - if(!results.isEmpty()){ - listenerMulticaster.multicast(IMListenerType.GROUP_MESSAGE, results); - } - } while (results.size() >= batchSize); + public void onMessage(List results) { + listenerMulticaster.multicast(IMListenerType.GROUP_MESSAGE, results); } - List loadBatch() { - String key = StrUtil.join(":", IMRedisKey.IM_RESULT_GROUP_QUEUE, appName); - //这个接口redis6.2以上才支持 - //List list = redisMQTemplate.opsForList().leftPop(key, batchSize); - List results = new LinkedList<>(); - JSONObject jsonObject = (JSONObject) redisMQTemplate.opsForList().leftPop(key); - while (!Objects.isNull(jsonObject) && results.size() < batchSize) { - results.add(jsonObject.toJavaObject(IMSendResult.class)); - jsonObject = (JSONObject) redisMQTemplate.opsForList().leftPop(key); - } - return results; - } } diff --git a/im-client/src/main/java/com/bx/imclient/task/PrivateMessageResultResultTask.java b/im-client/src/main/java/com/bx/imclient/task/PrivateMessageResultResultTask.java index ac7355c..38e5ac1 100644 --- a/im-client/src/main/java/com/bx/imclient/task/PrivateMessageResultResultTask.java +++ b/im-client/src/main/java/com/bx/imclient/task/PrivateMessageResultResultTask.java @@ -1,59 +1,26 @@ package com.bx.imclient.task; -import cn.hutool.core.util.StrUtil; -import com.alibaba.fastjson.JSONObject; import com.bx.imclient.listener.MessageListenerMulticaster; import com.bx.imcommon.contant.IMRedisKey; import com.bx.imcommon.enums.IMListenerType; import com.bx.imcommon.model.IMSendResult; -import com.bx.imcommon.mq.RedisMQTemplate; +import com.bx.imcommon.mq.RedisMQListener; import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; -import java.util.LinkedList; + import java.util.List; -import java.util.Objects; -@Slf4j + @Component @RequiredArgsConstructor -public class PrivateMessageResultResultTask extends AbstractMessageResultTask { - - @Autowired - private RedisMQTemplate redisMQTemplate; - - @Value("${spring.application.name}") - private String appName; - - @Value("${im.result.batch:100}") - private int batchSize; +@RedisMQListener(queue = IMRedisKey.IM_RESULT_PRIVATE_QUEUE, batchSize = 100) +public class PrivateMessageResultResultTask extends AbstractMessageResultTask { private final MessageListenerMulticaster listenerMulticaster; @Override - public void pullMessage() { - List results; - do { - results = loadBatch(); - if(!results.isEmpty()){ - listenerMulticaster.multicast(IMListenerType.PRIVATE_MESSAGE, results); - } - } while (results.size() >= batchSize); - } - - List loadBatch() { - String key = StrUtil.join(":", IMRedisKey.IM_RESULT_PRIVATE_QUEUE, appName); - //这个接口redis6.2以上才支持 - //List list = redisMQTemplate.opsForList().leftPop(key, batchSize); - List results = new LinkedList<>(); - JSONObject jsonObject = (JSONObject) redisMQTemplate.opsForList().leftPop(key); - while (!Objects.isNull(jsonObject) && results.size() < batchSize) { - results.add(jsonObject.toJavaObject(IMSendResult.class)); - jsonObject = (JSONObject) redisMQTemplate.opsForList().leftPop(key); - } - return results; + public void onMessage(List results) { + listenerMulticaster.multicast(IMListenerType.PRIVATE_MESSAGE, results); } } diff --git a/im-client/src/main/java/com/bx/imclient/task/SystemMessageResultResultTask.java b/im-client/src/main/java/com/bx/imclient/task/SystemMessageResultResultTask.java index 7bd3491..a42ca94 100644 --- a/im-client/src/main/java/com/bx/imclient/task/SystemMessageResultResultTask.java +++ b/im-client/src/main/java/com/bx/imclient/task/SystemMessageResultResultTask.java @@ -1,32 +1,19 @@ package com.bx.imclient.task; -import cn.hutool.core.util.StrUtil; -import com.alibaba.fastjson.JSONObject; import com.bx.imclient.listener.MessageListenerMulticaster; import com.bx.imcommon.contant.IMRedisKey; import com.bx.imcommon.enums.IMListenerType; import com.bx.imcommon.model.IMSendResult; -import com.bx.imcommon.mq.RedisMQConsumer; import com.bx.imcommon.mq.RedisMQListener; -import com.bx.imcommon.mq.RedisMQTemplate; import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; -import java.util.LinkedList; import java.util.List; -import java.util.Objects; -@Slf4j @Component @RequiredArgsConstructor -@RedisMQListener(queue = IMRedisKey.IM_RESULT_SYSTEM_QUEUE, batchSize = 100, period = 100) -public class SystemMessageResultResultTask extends RedisMQConsumer { - - @Value("${spring.application.name}") - private String appName; +@RedisMQListener(queue = IMRedisKey.IM_RESULT_SYSTEM_QUEUE, batchSize = 100) +public class SystemMessageResultResultTask extends AbstractMessageResultTask { private final MessageListenerMulticaster listenerMulticaster; @@ -35,9 +22,4 @@ public class SystemMessageResultResultTask extends RedisMQConsumer listenerMulticaster.multicast(IMListenerType.SYSTEM_MESSAGE, results); } - @Override - public String generateKey() { - return StrUtil.join(":", IMRedisKey.IM_RESULT_SYSTEM_QUEUE, appName); - } - } diff --git a/im-commom/src/main/java/com/bx/imcommon/mq/RedisMQConsumer.java b/im-commom/src/main/java/com/bx/imcommon/mq/RedisMQConsumer.java index e3ec88d..cd997fd 100644 --- a/im-commom/src/main/java/com/bx/imcommon/mq/RedisMQConsumer.java +++ b/im-commom/src/main/java/com/bx/imcommon/mq/RedisMQConsumer.java @@ -7,13 +7,29 @@ import java.util.List; */ public abstract class RedisMQConsumer { + /** + * 消费消息回调(单条) + */ public void onMessage(T data){} + /** + * 消费消息回调(批量) + */ public void onMessage(List datas){} + /** + * 生成redis队列完整key + */ public String generateKey(){ // 默认队列名就是redis的key RedisMQListener annotation = this.getClass().getAnnotation(RedisMQListener.class); return annotation.queue(); } + + /** + * 队列是否就绪,返回true才会开始消费 + */ + public Boolean isReady(){ + return true; + } } diff --git a/im-commom/src/main/java/com/bx/imcommon/mq/RedisMQPullTask.java b/im-commom/src/main/java/com/bx/imcommon/mq/RedisMQPullTask.java index e53ae27..7b0d4c6 100644 --- a/im-commom/src/main/java/com/bx/imcommon/mq/RedisMQPullTask.java +++ b/im-commom/src/main/java/com/bx/imcommon/mq/RedisMQPullTask.java @@ -49,18 +49,20 @@ public class RedisMQPullTask implements CommandLineRunner { public void run() { List datas = new LinkedList<>(); try { - // 拉取一个批次的数据 - List objects = pullBatch(key, batchSize); - for (Object obj : objects) { - if (obj instanceof JSONObject) { - JSONObject jsonObject = (JSONObject)obj; - Object data = jsonObject.toJavaObject(type); - consumer.onMessage(data); - datas.add(data); + if(consumer.isReady()){ + // 拉取一个批次的数据 + List objects = pullBatch(key, batchSize); + for (Object obj : objects) { + if (obj instanceof JSONObject) { + JSONObject jsonObject = (JSONObject)obj; + Object data = jsonObject.toJavaObject(type); + consumer.onMessage(data); + datas.add(data); + } + } + if(!datas.isEmpty()){ + consumer.onMessage(datas); } - } - if(!datas.isEmpty()){ - consumer.onMessage(datas); } } catch (Exception e) { log.error("数据消费异常,队列:{}", key, e); diff --git a/im-server/src/main/java/com/bx/imserver/task/AbstractPullMessageTask.java b/im-server/src/main/java/com/bx/imserver/task/AbstractPullMessageTask.java index 905c5fc..c6b2290 100644 --- a/im-server/src/main/java/com/bx/imserver/task/AbstractPullMessageTask.java +++ b/im-server/src/main/java/com/bx/imserver/task/AbstractPullMessageTask.java @@ -1,45 +1,23 @@ package com.bx.imserver.task; -import com.bx.imcommon.util.ThreadPoolExecutorFactory; +import com.bx.imcommon.mq.RedisMQConsumer; import com.bx.imserver.netty.IMServerGroup; -import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.CommandLineRunner; - -import javax.annotation.PreDestroy; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.ScheduledThreadPoolExecutor; -import java.util.concurrent.TimeUnit; @Slf4j -public abstract class AbstractPullMessageTask implements CommandLineRunner { - - private static final ScheduledThreadPoolExecutor EXECUTOR_SERVICE = ThreadPoolExecutorFactory.getThreadPoolExecutor(); +public abstract class AbstractPullMessageTask extends RedisMQConsumer { @Autowired private IMServerGroup serverGroup; @Override - public void run(String... args) { - EXECUTOR_SERVICE.execute(new Runnable() { - @SneakyThrows - @Override - public void run() { - try { - if (serverGroup.isReady()) { - pullMessage(); - } - } catch (Exception e) { - log.error("任务调度异常", e); - } - if (!EXECUTOR_SERVICE.isShutdown()) { - EXECUTOR_SERVICE.schedule(this,100, TimeUnit.MICROSECONDS); - } - } - }); + public String generateKey() { + return String.join(":", super.generateKey(), IMServerGroup.serverId + ""); } - - public abstract void pullMessage() throws InterruptedException; + @Override + public Boolean isReady() { + return serverGroup.isReady(); + } } diff --git a/im-server/src/main/java/com/bx/imserver/task/PullGroupMessageTask.java b/im-server/src/main/java/com/bx/imserver/task/PullGroupMessageTask.java index 48d37b5..2d0db6b 100644 --- a/im-server/src/main/java/com/bx/imserver/task/PullGroupMessageTask.java +++ b/im-server/src/main/java/com/bx/imserver/task/PullGroupMessageTask.java @@ -1,38 +1,25 @@ package com.bx.imserver.task; -import com.alibaba.fastjson.JSONObject; import com.bx.imcommon.contant.IMRedisKey; import com.bx.imcommon.enums.IMCmdType; import com.bx.imcommon.model.IMRecvInfo; -import com.bx.imserver.netty.IMServerGroup; +import com.bx.imcommon.mq.RedisMQListener; import com.bx.imserver.netty.processor.AbstractMessageProcessor; import com.bx.imserver.netty.processor.ProcessorFactory; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; -import java.util.Objects; - @Slf4j @Component @RequiredArgsConstructor -public class PullGroupMessageTask extends AbstractPullMessageTask { - - private final RedisTemplate redisTemplate; +@RedisMQListener(queue = IMRedisKey.IM_MESSAGE_GROUP_QUEUE, batchSize = 10) +public class PullGroupMessageTask extends AbstractPullMessageTask { @Override - public void pullMessage() { - // 从redis拉取消息 - String key = String.join(":", IMRedisKey.IM_MESSAGE_GROUP_QUEUE, IMServerGroup.serverId + ""); - JSONObject jsonObject = (JSONObject) redisTemplate.opsForList().leftPop(key); - while (!Objects.isNull(jsonObject)) { - IMRecvInfo recvInfo = jsonObject.toJavaObject(IMRecvInfo.class); - AbstractMessageProcessor processor = ProcessorFactory.createProcessor(IMCmdType.GROUP_MESSAGE); - processor.process(recvInfo); - // 下一条消息 - jsonObject = (JSONObject) redisTemplate.opsForList().leftPop(key); - } + public void onMessage(IMRecvInfo recvInfo) { + AbstractMessageProcessor processor = ProcessorFactory.createProcessor(IMCmdType.GROUP_MESSAGE); + processor.process(recvInfo); } } diff --git a/im-server/src/main/java/com/bx/imserver/task/PullPrivateMessageTask.java b/im-server/src/main/java/com/bx/imserver/task/PullPrivateMessageTask.java index 007414e..b9b3545 100644 --- a/im-server/src/main/java/com/bx/imserver/task/PullPrivateMessageTask.java +++ b/im-server/src/main/java/com/bx/imserver/task/PullPrivateMessageTask.java @@ -1,37 +1,25 @@ package com.bx.imserver.task; -import com.alibaba.fastjson.JSONObject; import com.bx.imcommon.contant.IMRedisKey; import com.bx.imcommon.enums.IMCmdType; import com.bx.imcommon.model.IMRecvInfo; -import com.bx.imserver.netty.IMServerGroup; +import com.bx.imcommon.mq.RedisMQListener; import com.bx.imserver.netty.processor.AbstractMessageProcessor; import com.bx.imserver.netty.processor.ProcessorFactory; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; -import java.util.Objects; - @Slf4j @Component @RequiredArgsConstructor -public class PullPrivateMessageTask extends AbstractPullMessageTask { - - private final RedisTemplate redisTemplate; +@RedisMQListener(queue = IMRedisKey.IM_MESSAGE_PRIVATE_QUEUE, batchSize = 10) +public class PullPrivateMessageTask extends AbstractPullMessageTask { @Override - public void pullMessage() { - // 从redis拉取消息 - String key = String.join(":", IMRedisKey.IM_MESSAGE_PRIVATE_QUEUE, IMServerGroup.serverId + ""); - JSONObject jsonObject = (JSONObject) redisTemplate.opsForList().leftPop(key); - while (!Objects.isNull(jsonObject)) { - IMRecvInfo recvInfo = jsonObject.toJavaObject(IMRecvInfo.class); - AbstractMessageProcessor processor = ProcessorFactory.createProcessor(IMCmdType.PRIVATE_MESSAGE); - processor.process(recvInfo); - // 下一条消息 - jsonObject = (JSONObject) redisTemplate.opsForList().leftPop(key); - } + public void onMessage(IMRecvInfo recvInfo) { + AbstractMessageProcessor processor = ProcessorFactory.createProcessor(IMCmdType.PRIVATE_MESSAGE); + processor.process(recvInfo); } + } diff --git a/im-server/src/main/java/com/bx/imserver/task/PullSystemMessageTask.java b/im-server/src/main/java/com/bx/imserver/task/PullSystemMessageTask.java index c4a3f6d..5e12f1b 100644 --- a/im-server/src/main/java/com/bx/imserver/task/PullSystemMessageTask.java +++ b/im-server/src/main/java/com/bx/imserver/task/PullSystemMessageTask.java @@ -3,9 +3,7 @@ package com.bx.imserver.task; import com.bx.imcommon.contant.IMRedisKey; import com.bx.imcommon.enums.IMCmdType; import com.bx.imcommon.model.IMRecvInfo; -import com.bx.imcommon.mq.RedisMQConsumer; import com.bx.imcommon.mq.RedisMQListener; -import com.bx.imserver.netty.IMServerGroup; import com.bx.imserver.netty.processor.AbstractMessageProcessor; import com.bx.imserver.netty.processor.ProcessorFactory; import lombok.extern.slf4j.Slf4j; @@ -19,7 +17,7 @@ import org.springframework.stereotype.Component; @Slf4j @Component @RedisMQListener(queue = IMRedisKey.IM_MESSAGE_SYSTEM_QUEUE,batchSize = 10) -public class PullSystemMessageTask extends RedisMQConsumer { +public class PullSystemMessageTask extends AbstractPullMessageTask { @Override public void onMessage(IMRecvInfo recvInfo) { @@ -27,8 +25,4 @@ public class PullSystemMessageTask extends RedisMQConsumer { processor.process(recvInfo); } - public String generateKey(){ - // 队列名:im:message:system:{服务id} - return String.join(":", IMRedisKey.IM_MESSAGE_SYSTEM_QUEUE, IMServerGroup.serverId + ""); - } } From 00228b826e920c2c3626bcd430d1afdab5db09c6 Mon Sep 17 00:00:00 2001 From: xsx <825657193@qq.com> Date: Wed, 17 Jul 2024 00:53:15 +0800 Subject: [PATCH 04/48] =?UTF-8?q?feat:=20=E8=B4=A6=E6=88=B7=E5=B0=81?= =?UTF-8?q?=E7=A6=81=E5=8A=9F=E8=83=BD=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/bx/implatform/entity/User.java | 14 +++++++++- .../service/impl/UserServiceImpl.java | 28 +++++++++++++++---- im-ui/src/api/enums.js | 1 + im-ui/src/view/Home.vue | 17 +++++++++++ im-uniapp/App.vue | 23 +++++++++++---- im-uniapp/common/enums.js | 1 + 6 files changed, 72 insertions(+), 12 deletions(-) diff --git a/im-platform/src/main/java/com/bx/implatform/entity/User.java b/im-platform/src/main/java/com/bx/implatform/entity/User.java index 639b43b..878310c 100644 --- a/im-platform/src/main/java/com/bx/implatform/entity/User.java +++ b/im-platform/src/main/java/com/bx/implatform/entity/User.java @@ -74,11 +74,23 @@ public class User extends Model { @TableField("signature") private String signature; /** - * 密码(明文) + * 密码 */ @TableField("password") private String password; + /** + * 是否被封禁 + */ + @TableField("is_banned") + private Boolean isBanned; + + /** + * 被封禁原因 + */ + @TableField("reason") + private String reason; + /** * 最后登录时间 */ diff --git a/im-platform/src/main/java/com/bx/implatform/service/impl/UserServiceImpl.java b/im-platform/src/main/java/com/bx/implatform/service/impl/UserServiceImpl.java index b10bad4..7f81724 100644 --- a/im-platform/src/main/java/com/bx/implatform/service/impl/UserServiceImpl.java +++ b/im-platform/src/main/java/com/bx/implatform/service/impl/UserServiceImpl.java @@ -50,8 +50,12 @@ public class UserServiceImpl extends ServiceImpl implements IU @Override public LoginVO login(LoginDTO dto) { User user = this.findUserByUserName(dto.getUserName()); - if (null == user) { - throw new GlobalException(ResultCode.PROGRAM_ERROR, "用户不存在"); + if (Objects.isNull(user)) { + throw new GlobalException("用户不存在"); + } + if (user.getIsBanned()) { + String tip = String.format("您的账号因'%s'已被管理员封禁,请联系客服!",user.getReason()); + throw new GlobalException(tip); } if (!passwordEncoder.matches(dto.getPassword(), user.getPassword())) { throw new GlobalException(ResultCode.PASSWOR_ERROR); @@ -61,8 +65,10 @@ public class UserServiceImpl extends ServiceImpl implements IU session.setUserId(user.getId()); session.setTerminal(dto.getTerminal()); String strJson = JSON.toJSONString(session); - String accessToken = JwtUtil.sign(user.getId(), strJson, jwtProperties.getAccessTokenExpireIn(), jwtProperties.getAccessTokenSecret()); - String refreshToken = JwtUtil.sign(user.getId(), strJson, jwtProperties.getRefreshTokenExpireIn(), jwtProperties.getRefreshTokenSecret()); + String accessToken = JwtUtil.sign(user.getId(), strJson, jwtProperties.getAccessTokenExpireIn(), + jwtProperties.getAccessTokenSecret()); + String refreshToken = JwtUtil.sign(user.getId(), strJson, jwtProperties.getRefreshTokenExpireIn(), + jwtProperties.getRefreshTokenSecret()); LoginVO vo = new LoginVO(); vo.setAccessToken(accessToken); vo.setAccessTokenExpiresIn(jwtProperties.getAccessTokenExpireIn()); @@ -79,8 +85,18 @@ public class UserServiceImpl extends ServiceImpl implements IU } String strJson = JwtUtil.getInfo(refreshToken); Long userId = JwtUtil.getUserId(refreshToken); - String accessToken = JwtUtil.sign(userId, strJson, jwtProperties.getAccessTokenExpireIn(), jwtProperties.getAccessTokenSecret()); - String newRefreshToken = JwtUtil.sign(userId, strJson, jwtProperties.getRefreshTokenExpireIn(), jwtProperties.getRefreshTokenSecret()); + User user = this.getById(userId); + if (Objects.isNull(user)) { + throw new GlobalException("用户不存在"); + } + if (user.getIsBanned()) { + String tip = String.format("您的账号因'%s'被管理员封禁,请联系客服!",user.getReason()); + throw new GlobalException(tip); + } + String accessToken = + JwtUtil.sign(userId, strJson, jwtProperties.getAccessTokenExpireIn(), jwtProperties.getAccessTokenSecret()); + String newRefreshToken = JwtUtil.sign(userId, strJson, jwtProperties.getRefreshTokenExpireIn(), + jwtProperties.getRefreshTokenSecret()); LoginVO vo = new LoginVO(); vo.setAccessToken(accessToken); vo.setAccessTokenExpiresIn(jwtProperties.getAccessTokenExpireIn()); diff --git a/im-ui/src/api/enums.js b/im-ui/src/api/enums.js index 0fcb7eb..efdaa01 100644 --- a/im-ui/src/api/enums.js +++ b/im-ui/src/api/enums.js @@ -12,6 +12,7 @@ const MESSAGE_TYPE = { LOADING: 30, ACT_RT_VOICE: 40, ACT_RT_VIDEO: 41, + USER_BANNED: 50, RTC_CALL_VOICE: 100, RTC_CALL_VIDEO: 101, RTC_ACCEPT: 102, diff --git a/im-ui/src/view/Home.vue b/im-ui/src/view/Home.vue index 7feb810..bc6549f 100644 --- a/im-ui/src/view/Home.vue +++ b/im-ui/src/view/Home.vue @@ -107,6 +107,9 @@ } else if (cmd == 4) { // 插入群聊消息 this.handleGroupMessage(msgInfo); + } else if (cmd == 5){ + // 处理系统消息 + this.handleSystemMessage(msgInfo); } }); this.$wsApi.onClose((e) => { @@ -246,6 +249,20 @@ this.playAudioTip(); } }, + handleSystemMessage(msg){ + // 用户被封禁 + + if (msg.type == this.$enums.MESSAGE_TYPE.USER_BANNED) { + this.$wsApi.close(3000); + this.$alert("您的账号已被管理员封禁,原因:"+ msg.content, "账号被封禁", { + confirmButtonText: '确定', + callback: action => { + this.onExit(); + } + }); + return; + } + }, onExit() { this.$wsApi.close(3000); sessionStorage.removeItem("accessToken"); diff --git a/im-uniapp/App.vue b/im-uniapp/App.vue index 549bf4e..432ec85 100644 --- a/im-uniapp/App.vue +++ b/im-uniapp/App.vue @@ -30,7 +30,7 @@ wsApi.connect(UNI_APP.WS_URL, loginInfo.accessToken); wsApi.onConnect(() => { // 重连成功提示 - if(this.reconnecting){ + if (this.reconnecting) { this.reconnecting = false; uni.showToast({ title: "已重新连接", @@ -55,6 +55,9 @@ } else if (cmd == 4) { // 群聊消息 this.handleGroupMessage(msgInfo); + } else if (cmd == 5) { + // 系统消息 + this.handleSystemMessage(msgInfo); } }); wsApi.onClose((res) => { @@ -189,7 +192,17 @@ // 插入群聊消息 this.insertGroupMessage(group, msg); }) - + }, + handleSystemMessage(msg) { + if (msg.type == enums.MESSAGE_TYPE.USER_BANNED) { + // 用户被封禁 + wsApi.close(3099); + uni.showModal({ + content: '您的账号已被管理员封禁,原因:' + msg.content, + showCancel: false, + }) + this.exit(); + } }, insertGroupMessage(group, msg) { // 群视频信令 @@ -268,7 +281,7 @@ }, exit() { console.log("exit"); - wsApi.close(1000); + wsApi.close(3099); uni.removeStorageSync("loginInfo"); uni.reLaunch({ url: "/pages/login/login" @@ -302,9 +315,9 @@ wsApi.reconnect(UNI_APP.WS_URL, loginInfo.accessToken); }).catch(() => { // 5s后重试 - setTimeout(()=>{ + setTimeout(() => { this.reconnectWs(); - },5000) + }, 5000) }) }, reloadUserInfo() { diff --git a/im-uniapp/common/enums.js b/im-uniapp/common/enums.js index 6dddc3b..d6f0c53 100644 --- a/im-uniapp/common/enums.js +++ b/im-uniapp/common/enums.js @@ -13,6 +13,7 @@ const MESSAGE_TYPE = { LOADING:30, ACT_RT_VOICE:40, ACT_RT_VIDEO:41, + USER_BANNED:50, RTC_CALL_VOICE: 100, RTC_CALL_VIDEO: 101, RTC_ACCEPT: 102, From ebb2f116c5af7045824b13fe01de3ec063706fec Mon Sep 17 00:00:00 2001 From: xsx <825657193@qq.com> Date: Thu, 18 Jul 2024 01:24:59 +0800 Subject: [PATCH 05/48] =?UTF-8?q?=E6=A1=86=E6=9E=B6=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E5=8D=87=E7=BA=A7=E8=87=B3:=20springboot3.3=E3=80=81jdk17?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- im-commom/pom.xml | 7 - .../com/bx/imcommon/mq/RedisMQConfig.java | 1 - .../com/bx/imcommon/mq/RedisMQPullTask.java | 8 +- .../com/bx/imcommon/mq/RedisMQTemplate.java | 1 - .../util/ThreadPoolExecutorFactory.java | 3 +- im-platform/pom.xml | 67 +++--- .../java/com/bx/implatform/IMPlatformApp.java | 3 +- .../bx/implatform/annotation/OnlineCheck.java | 1 - .../implatform/aspect/OnlineCheckAspect.java | 7 - .../bx/implatform/aspect/RedisLockAspect.java | 2 - .../com/bx/implatform/config/RedisConfig.java | 54 ++--- .../bx/implatform/config/RedissonConfig.java | 9 +- .../bx/implatform/config/SwaggerConfig.java | 51 +++-- .../implatform/controller/FileController.java | 15 +- .../controller/FriendController.java | 26 +-- .../controller/GroupController.java | 41 ++-- .../controller/GroupMessageController.java | 37 ++-- .../controller/LoginController.java | 19 +- .../controller/PrivateMessageController.java | 38 ++-- .../controller/SystemController.java | 13 +- .../implatform/controller/UserController.java | 24 +-- .../controller/WebrtcGroupController.java | 54 +++-- .../controller/WebrtcPrivateController.java | 44 ++-- .../com/bx/implatform/dto/GroupBanDTO.java | 10 +- .../bx/implatform/dto/GroupMessageDTO.java | 21 +- .../com/bx/implatform/dto/GroupUnbanDTO.java | 7 +- .../java/com/bx/implatform/dto/LoginDTO.java | 20 +- .../com/bx/implatform/dto/ModifyPwdDTO.java | 12 +- .../bx/implatform/dto/PrivateMessageDTO.java | 16 +- .../com/bx/implatform/dto/RegisterDTO.java | 14 +- .../com/bx/implatform/dto/UserBanDTO.java | 9 +- .../implatform/dto/WebrtcGroupAnswerDTO.java | 16 +- .../dto/WebrtcGroupCandidateDTO.java | 17 +- .../implatform/dto/WebrtcGroupDeviceDTO.java | 15 +- .../implatform/dto/WebrtcGroupFailedDTO.java | 12 +- .../implatform/dto/WebrtcGroupInviteDTO.java | 13 +- .../bx/implatform/dto/WebrtcGroupJoinDTO.java | 11 +- .../implatform/dto/WebrtcGroupOfferDTO.java | 16 +- .../implatform/dto/WebrtcGroupSetupDTO.java | 13 +- .../java/com/bx/implatform/entity/Friend.java | 22 +- .../java/com/bx/implatform/entity/Group.java | 41 ++-- .../com/bx/implatform/entity/GroupMember.java | 35 +--- .../bx/implatform/entity/GroupMessage.java | 30 +-- .../bx/implatform/entity/PrivateMessage.java | 25 +-- .../java/com/bx/implatform/entity/User.java | 64 ++---- .../com/bx/implatform/filter/CacheFilter.java | 6 +- .../CacheHttpServletRequestWrapper.java | 8 +- .../implatform/generator/CodeGenerator.java | 192 +++++++++--------- .../interceptor/AuthInterceptor.java | 5 +- .../interceptor/XssInterceptor.java | 4 +- .../listener/GroupMessageListener.java | 2 - .../listener/SystemMessageListener.java | 10 - .../service/IWebrtcGroupService.java | 1 - .../service/IWebrtcPrivateService.java | 4 - .../service/impl/GroupServiceImpl.java | 7 +- .../service/thirdparty/FileService.java | 2 +- .../bx/implatform/session/SessionContext.java | 3 +- .../bx/implatform/session/WebrtcUserInfo.java | 15 +- .../task/UserBannedConsumerTask.java | 1 + .../implatform/util/SensitiveFilterUtil.java | 2 +- .../java/com/bx/implatform/vo/FriendVO.java | 14 +- .../com/bx/implatform/vo/GroupInviteVO.java | 13 +- .../com/bx/implatform/vo/GroupMemberVO.java | 17 +- .../com/bx/implatform/vo/GroupMessageVO.java | 27 ++- .../java/com/bx/implatform/vo/GroupVO.java | 29 ++- .../java/com/bx/implatform/vo/LoginVO.java | 13 +- .../bx/implatform/vo/OnlineTerminalVO.java | 6 +- .../bx/implatform/vo/PrivateMessageVO.java | 19 +- .../com/bx/implatform/vo/SystemConfigVO.java | 7 +- .../com/bx/implatform/vo/SystemMessageVO.java | 13 +- .../com/bx/implatform/vo/UploadImageVO.java | 9 +- .../java/com/bx/implatform/vo/UserVO.java | 28 ++- .../bx/implatform/vo/WebrtcGroupFailedVO.java | 9 +- .../bx/implatform/vo/WebrtcGroupInfoVO.java | 11 +- .../src/main/resources/application.yml | 11 +- im-server/pom.xml | 6 +- .../com/bx/imserver/config/RedisConfig.java | 33 --- .../com/bx/imserver/netty/IMServerGroup.java | 6 +- pom.xml | 53 ++--- 79 files changed, 628 insertions(+), 922 deletions(-) delete mode 100644 im-server/src/main/java/com/bx/imserver/config/RedisConfig.java diff --git a/im-commom/pom.xml b/im-commom/pom.xml index 61b263c..a019f36 100644 --- a/im-commom/pom.xml +++ b/im-commom/pom.xml @@ -39,15 +39,9 @@ org.springframework spring-beans - - org.apache.velocity - velocity - ${velocity.version} - com.fasterxml.jackson.datatype jackson-datatype-joda - 2.9.10 org.springframework @@ -63,7 +57,6 @@ org.slf4j slf4j-api - 1.7.36 diff --git a/im-commom/src/main/java/com/bx/imcommon/mq/RedisMQConfig.java b/im-commom/src/main/java/com/bx/imcommon/mq/RedisMQConfig.java index 24ba4ba..99c5074 100644 --- a/im-commom/src/main/java/com/bx/imcommon/mq/RedisMQConfig.java +++ b/im-commom/src/main/java/com/bx/imcommon/mq/RedisMQConfig.java @@ -4,7 +4,6 @@ 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; diff --git a/im-commom/src/main/java/com/bx/imcommon/mq/RedisMQPullTask.java b/im-commom/src/main/java/com/bx/imcommon/mq/RedisMQPullTask.java index 7b0d4c6..cf5bf99 100644 --- a/im-commom/src/main/java/com/bx/imcommon/mq/RedisMQPullTask.java +++ b/im-commom/src/main/java/com/bx/imcommon/mq/RedisMQPullTask.java @@ -2,14 +2,18 @@ package com.bx.imcommon.mq; import com.alibaba.fastjson.JSONObject; import com.bx.imcommon.util.ThreadPoolExecutorFactory; +import jakarta.annotation.PreDestroy; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component; -import javax.annotation.PreDestroy; + import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; -import java.util.*; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Objects; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; diff --git a/im-commom/src/main/java/com/bx/imcommon/mq/RedisMQTemplate.java b/im-commom/src/main/java/com/bx/imcommon/mq/RedisMQTemplate.java index f966579..bf8b076 100644 --- a/im-commom/src/main/java/com/bx/imcommon/mq/RedisMQTemplate.java +++ b/im-commom/src/main/java/com/bx/imcommon/mq/RedisMQTemplate.java @@ -1,6 +1,5 @@ package com.bx.imcommon.mq; -import cn.hutool.core.util.StrUtil; import org.apache.logging.log4j.util.Strings; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.core.RedisTemplate; diff --git a/im-commom/src/main/java/com/bx/imcommon/util/ThreadPoolExecutorFactory.java b/im-commom/src/main/java/com/bx/imcommon/util/ThreadPoolExecutorFactory.java index e2cdd6d..3cf0112 100644 --- a/im-commom/src/main/java/com/bx/imcommon/util/ThreadPoolExecutorFactory.java +++ b/im-commom/src/main/java/com/bx/imcommon/util/ThreadPoolExecutorFactory.java @@ -2,7 +2,8 @@ package com.bx.imcommon.util; import lombok.extern.slf4j.Slf4j; -import java.util.concurrent.*; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.ThreadPoolExecutor; /** * 创建单例线程池 diff --git a/im-platform/pom.xml b/im-platform/pom.xml index 16e987c..05b2590 100644 --- a/im-platform/pom.xml +++ b/im-platform/pom.xml @@ -18,10 +18,6 @@ im-client 2.0.0 - - org.springframework.boot - spring-boot - org.springframework.boot spring-boot-starter-web @@ -29,29 +25,30 @@ com.baomidou mybatis-plus-boot-starter + + + org.mybatis + mybatis-spring + + + + + org.mybatis + mybatis-spring + ${mybatis.spring.version} com.alibaba druid - mysql - mysql-connector-java + com.mysql + mysql-connector-j org.springframework.boot spring-boot-starter-jdbc - - io.springfox - springfox-swagger2 - ${swagger.version} - - - io.springfox - springfox-swagger-ui - ${swagger.version} - org.aspectj aspectjweaver @@ -62,8 +59,9 @@ spring-boot-starter-data-redis - org.springframework.boot - spring-boot-starter-security + org.springframework.security + spring-security-crypto + 6.3.1 org.springframework.session @@ -77,27 +75,7 @@ io.minio minio - 8.4.3 - - - com.squareup.okhttp3 - okhttp - - - org.jetbrains.kotlin - kotlin-stdlib - - - - - com.squareup.okhttp3 - okhttp - 4.9.0 - - - org.jetbrains.kotlin - kotlin-stdlib - 1.3.70 + ${minio.version} @@ -112,8 +90,13 @@ org.redisson - redisson - 3.17.3 + redisson-spring-boot-starter + ${redisson.version} + + + com.github.xiaoymin + knife4j-openapi3-jakarta-spring-boot-starter + ${knife4j.version} @@ -123,7 +106,7 @@ org.springframework.boot spring-boot-maven-plugin - 2.0.3.RELEASE + 3.3.1 diff --git a/im-platform/src/main/java/com/bx/implatform/IMPlatformApp.java b/im-platform/src/main/java/com/bx/implatform/IMPlatformApp.java index ecb30a4..73dab5d 100644 --- a/im-platform/src/main/java/com/bx/implatform/IMPlatformApp.java +++ b/im-platform/src/main/java/com/bx/implatform/IMPlatformApp.java @@ -4,7 +4,6 @@ import lombok.extern.slf4j.Slf4j; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.EnableAspectJAutoProxy; @@ -14,7 +13,7 @@ import org.springframework.context.annotation.EnableAspectJAutoProxy; @EnableAspectJAutoProxy(exposeProxy = true) @ComponentScan(basePackages = {"com.bx"}) @MapperScan(basePackages = {"com.bx.implatform.mapper"}) -@SpringBootApplication(exclude = {SecurityAutoConfiguration.class})// 禁用secrity +@SpringBootApplication public class IMPlatformApp { public static void main(String[] args) { diff --git a/im-platform/src/main/java/com/bx/implatform/annotation/OnlineCheck.java b/im-platform/src/main/java/com/bx/implatform/annotation/OnlineCheck.java index f616002..4baec54 100644 --- a/im-platform/src/main/java/com/bx/implatform/annotation/OnlineCheck.java +++ b/im-platform/src/main/java/com/bx/implatform/annotation/OnlineCheck.java @@ -4,7 +4,6 @@ import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import java.util.concurrent.TimeUnit; /** * 在线校验,标注此注解的接口用户必须保持长连接,否则将抛异常 diff --git a/im-platform/src/main/java/com/bx/implatform/aspect/OnlineCheckAspect.java b/im-platform/src/main/java/com/bx/implatform/aspect/OnlineCheckAspect.java index b81124c..a9be97e 100644 --- a/im-platform/src/main/java/com/bx/implatform/aspect/OnlineCheckAspect.java +++ b/im-platform/src/main/java/com/bx/implatform/aspect/OnlineCheckAspect.java @@ -1,8 +1,6 @@ package com.bx.implatform.aspect; -import cn.hutool.core.util.StrUtil; import com.bx.imclient.IMClient; -import com.bx.implatform.annotation.RedisLock; import com.bx.implatform.exception.GlobalException; import com.bx.implatform.session.SessionContext; import com.bx.implatform.session.UserSession; @@ -11,13 +9,8 @@ import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; -import org.aspectj.lang.reflect.MethodSignature; -import org.redisson.api.RLock; -import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; -import java.lang.reflect.Method; - /** * @author: blue * @date: 2024-06-16 diff --git a/im-platform/src/main/java/com/bx/implatform/aspect/RedisLockAspect.java b/im-platform/src/main/java/com/bx/implatform/aspect/RedisLockAspect.java index 0cd5586..dc5c6b5 100644 --- a/im-platform/src/main/java/com/bx/implatform/aspect/RedisLockAspect.java +++ b/im-platform/src/main/java/com/bx/implatform/aspect/RedisLockAspect.java @@ -8,8 +8,6 @@ import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.reflect.MethodSignature; -import org.redisson.Redisson; -import org.redisson.RedissonLock; import org.redisson.api.RLock; import org.redisson.api.RedissonClient; import org.springframework.core.DefaultParameterNameDiscoverer; diff --git a/im-platform/src/main/java/com/bx/implatform/config/RedisConfig.java b/im-platform/src/main/java/com/bx/implatform/config/RedisConfig.java index 14922b0..d74e656 100644 --- a/im-platform/src/main/java/com/bx/implatform/config/RedisConfig.java +++ b/im-platform/src/main/java/com/bx/implatform/config/RedisConfig.java @@ -1,18 +1,17 @@ package com.bx.implatform.config; import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import jakarta.annotation.Resource; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.CachingConfigurerSupport; import org.springframework.cache.annotation.EnableCaching; -import org.springframework.cache.interceptor.CacheErrorHandler; -import org.springframework.cache.interceptor.CacheResolver; -import org.springframework.cache.interceptor.SimpleCacheErrorHandler; -import org.springframework.cache.interceptor.SimpleCacheResolver; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; @@ -24,9 +23,7 @@ import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializationContext; import org.springframework.data.redis.serializer.StringRedisSerializer; -import javax.annotation.Resource; import java.time.Duration; -import java.util.Objects; @EnableCaching @Configuration @@ -40,7 +37,6 @@ public class RedisConfig extends CachingConfigurerSupport { public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisTemplate redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(redisConnectionFactory); - // 设置值(value)的序列化采用jackson2JsonRedisSerializer redisTemplate.setValueSerializer(jackson2JsonRedisSerializer()); redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer()); @@ -51,42 +47,28 @@ public class RedisConfig extends CachingConfigurerSupport { return redisTemplate; } + + + @Bean + public CacheManager cacheManager() { + // 设置redis缓存管理器 + RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(600)) + .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer())); + return RedisCacheManager.builder(factory).cacheDefaults(cacheConfiguration).build(); + } + + @Bean public Jackson2JsonRedisSerializer jackson2JsonRedisSerializer() { - Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class); ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); // 解决jackson2无法反序列化LocalDateTime的问题 om.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); om.registerModule(new JavaTimeModule()); + om.setSerializationInclusion(JsonInclude.Include.NON_NULL); // 忽略空值 om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); - jackson2JsonRedisSerializer.setObjectMapper(om); - return jackson2JsonRedisSerializer; - } - - - @Bean - @Override - public CacheResolver cacheResolver() { - return new SimpleCacheResolver(Objects.requireNonNull(cacheManager())); - } - - @Bean - @Override - public CacheErrorHandler errorHandler() { - // 用于捕获从Cache中进行CRUD时的异常的回调处理器。 - return new SimpleCacheErrorHandler(); - } - - @Bean - @Override - public CacheManager cacheManager() { - // 设置redis缓存管理器 - RedisCacheConfiguration cacheConfiguration = - RedisCacheConfiguration.defaultCacheConfig() - .disableCachingNullValues() - .entryTtl(Duration.ofMinutes(10)) - .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer())); - return RedisCacheManager.builder(factory).cacheDefaults(cacheConfiguration).build(); + //忽略无效字段 + om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + return new Jackson2JsonRedisSerializer<>(om, Object.class); } } diff --git a/im-platform/src/main/java/com/bx/implatform/config/RedissonConfig.java b/im-platform/src/main/java/com/bx/implatform/config/RedissonConfig.java index 7dc1c92..f3203a0 100644 --- a/im-platform/src/main/java/com/bx/implatform/config/RedissonConfig.java +++ b/im-platform/src/main/java/com/bx/implatform/config/RedissonConfig.java @@ -6,11 +6,8 @@ import org.redisson.api.RedissonClient; import org.redisson.client.codec.StringCodec; import org.redisson.config.Config; import org.redisson.config.SingleServerConfig; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.data.redis.RedisProperties; -import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; /** * @author: Blue @@ -18,9 +15,9 @@ import org.springframework.context.annotation.Configuration; * @version: 1.0 */ -@Configuration -@ConditionalOnClass(Config.class) -@EnableConfigurationProperties(RedisProperties.class) +//@Configuration +//@ConditionalOnClass(Config.class) +//@EnableConfigurationProperties(RedisProperties.class) public class RedissonConfig { @Bean diff --git a/im-platform/src/main/java/com/bx/implatform/config/SwaggerConfig.java b/im-platform/src/main/java/com/bx/implatform/config/SwaggerConfig.java index 3410b08..5c5841f 100644 --- a/im-platform/src/main/java/com/bx/implatform/config/SwaggerConfig.java +++ b/im-platform/src/main/java/com/bx/implatform/config/SwaggerConfig.java @@ -1,41 +1,38 @@ package com.bx.implatform.config; -import io.swagger.annotations.ApiOperation; + +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Contact; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.info.License; +import org.springdoc.core.models.GroupedOpenApi; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import springfox.documentation.builders.ApiInfoBuilder; -import springfox.documentation.builders.PathSelectors; -import springfox.documentation.builders.RequestHandlerSelectors; -import springfox.documentation.service.ApiInfo; -import springfox.documentation.spi.DocumentationType; -import springfox.documentation.spring.web.plugins.Docket; -import springfox.documentation.swagger2.annotations.EnableSwagger2; @Configuration -@EnableSwagger2 public class SwaggerConfig { @Bean - public Docket createRestApi() { - - return new Docket(DocumentationType.SWAGGER_2) - .apiInfo(apiInfo()) - .select() - //这里采用包含注解的方式来确定要显示的接口 - .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)) - //这里采用包扫描的方式来确定要显示的接口 - .paths(PathSelectors.any()) - .build(); - + public GroupedOpenApi userApi() { + String[] paths = {"/**"}; + String[] packagedToMatch = {"com.bx"}; + return GroupedOpenApi.builder().group("BoxIM") + .pathsToMatch(paths) + .packagesToScan(packagedToMatch).build(); } - private ApiInfo apiInfo() { - return new ApiInfoBuilder() - .title("IM Platform doc") - .description("盒子IM API文档") - .termsOfServiceUrl("http://8.134.92.70/") - .version("1.0") - .build(); + @Bean + public OpenAPI customOpenAPI() { + Contact contact = new Contact(); + contact.setName("Blue"); + return new OpenAPI().info(new Info() + .title("Box-IM") + .description("盒子IM") + .contact(contact) + .version("3.0") + .termsOfService("https://www.boxim.online") + .license(new License().name("MIT") + .url("https://www.boxim.online"))); } } diff --git a/im-platform/src/main/java/com/bx/implatform/controller/FileController.java b/im-platform/src/main/java/com/bx/implatform/controller/FileController.java index 204f639..ddf9236 100644 --- a/im-platform/src/main/java/com/bx/implatform/controller/FileController.java +++ b/im-platform/src/main/java/com/bx/implatform/controller/FileController.java @@ -4,33 +4,34 @@ import com.bx.implatform.result.Result; import com.bx.implatform.result.ResultUtils; import com.bx.implatform.service.thirdparty.FileService; import com.bx.implatform.vo.UploadImageVO; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; @Slf4j @RestController -@Api(tags = "文件上传") +@Tag(name = "文件上传") @RequiredArgsConstructor public class FileController { private final FileService fileService; - @ApiOperation(value = "上传图片", notes = "上传图片,上传后返回原图和缩略图的url") + @Operation(summary = "上传图片", description = "上传图片,上传后返回原图和缩略图的url") @PostMapping("/image/upload") - public Result uploadImage(MultipartFile file) { + public Result uploadImage(@RequestParam("file") MultipartFile file) { return ResultUtils.success(fileService.uploadImage(file)); } @CrossOrigin - @ApiOperation(value = "上传文件", notes = "上传文件,上传后返回文件url") + @Operation(summary = "上传文件", description = "上传文件,上传后返回文件url") @PostMapping("/file/upload") - public Result uploadFile(MultipartFile file) { + public Result uploadFile(@RequestParam("file") MultipartFile file) { return ResultUtils.success(fileService.uploadFile(file), ""); } diff --git a/im-platform/src/main/java/com/bx/implatform/controller/FriendController.java b/im-platform/src/main/java/com/bx/implatform/controller/FriendController.java index 3ded8bd..cbc281c 100644 --- a/im-platform/src/main/java/com/bx/implatform/controller/FriendController.java +++ b/im-platform/src/main/java/com/bx/implatform/controller/FriendController.java @@ -6,17 +6,17 @@ import com.bx.implatform.result.ResultUtils; import com.bx.implatform.service.IFriendService; import com.bx.implatform.session.SessionContext; import com.bx.implatform.vo.FriendVO; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; -import javax.validation.Valid; -import javax.validation.constraints.NotEmpty; import java.util.List; import java.util.stream.Collectors; -@Api(tags = "好友") +@Tag(name = "好友") @RestController @RequestMapping("/friend") @RequiredArgsConstructor @@ -25,7 +25,7 @@ public class FriendController { private final IFriendService friendService; @GetMapping("/list") - @ApiOperation(value = "好友列表", notes = "获取好友列表") + @Operation(summary = "好友列表", description = "获取好友列表") public Result> findFriends() { List friends = friendService.findFriendByUserId(SessionContext.getSession().getUserId()); List vos = friends.stream().map(f -> { @@ -40,28 +40,28 @@ public class FriendController { @PostMapping("/add") - @ApiOperation(value = "添加好友", notes = "双方建立好友关系") - public Result addFriend(@NotEmpty(message = "好友id不可为空") @RequestParam("friendId") Long friendId) { + @Operation(summary = "添加好友", description = "双方建立好友关系") + public Result addFriend(@NotNull(message = "好友id不可为空") @RequestParam("friendId") Long friendId) { friendService.addFriend(friendId); return ResultUtils.success(); } @GetMapping("/find/{friendId}") - @ApiOperation(value = "查找好友信息", notes = "查找好友信息") - public Result findFriend(@NotEmpty(message = "好友id不可为空") @PathVariable("friendId") Long friendId) { + @Operation(summary = "查找好友信息", description = "查找好友信息") + public Result findFriend(@NotNull(message = "好友id不可为空") @PathVariable("friendId") Long friendId) { return ResultUtils.success(friendService.findFriend(friendId)); } @DeleteMapping("/delete/{friendId}") - @ApiOperation(value = "删除好友", notes = "解除好友关系") - public Result delFriend(@NotEmpty(message = "好友id不可为空") @PathVariable("friendId") Long friendId) { + @Operation(summary = "删除好友", description = "解除好友关系") + public Result delFriend(@NotNull(message = "好友id不可为空") @PathVariable("friendId") Long friendId) { friendService.delFriend(friendId); return ResultUtils.success(); } @PutMapping("/update") - @ApiOperation(value = "更新好友信息", notes = "更新好友头像或昵称") + @Operation(summary = "更新好友信息", description = "更新好友头像或昵称") public Result modifyFriend(@Valid @RequestBody FriendVO vo) { friendService.update(vo); return ResultUtils.success(); diff --git a/im-platform/src/main/java/com/bx/implatform/controller/GroupController.java b/im-platform/src/main/java/com/bx/implatform/controller/GroupController.java index a76b451..9aa83c3 100644 --- a/im-platform/src/main/java/com/bx/implatform/controller/GroupController.java +++ b/im-platform/src/main/java/com/bx/implatform/controller/GroupController.java @@ -6,16 +6,16 @@ import com.bx.implatform.service.IGroupService; import com.bx.implatform.vo.GroupInviteVO; import com.bx.implatform.vo.GroupMemberVO; import com.bx.implatform.vo.GroupVO; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; -import javax.validation.Valid; -import javax.validation.constraints.NotNull; import java.util.List; -@Api(tags = "群聊") +@Tag(name = "群聊") @RestController @RequestMapping("/group") @RequiredArgsConstructor @@ -23,61 +23,62 @@ public class GroupController { private final IGroupService groupService; - @ApiOperation(value = "创建群聊", notes = "创建群聊") + @Operation(summary = "创建群聊", description = "创建群聊") @PostMapping("/create") public Result createGroup(@Valid @RequestBody GroupVO vo) { return ResultUtils.success(groupService.createGroup(vo)); } - @ApiOperation(value = "修改群聊信息", notes = "修改群聊信息") + @Operation(summary = "修改群聊信息", description = "修改群聊信息") @PutMapping("/modify") public Result modifyGroup(@Valid @RequestBody GroupVO vo) { return ResultUtils.success(groupService.modifyGroup(vo)); } - @ApiOperation(value = "解散群聊", notes = "解散群聊") + @Operation(summary = "解散群聊", description = "解散群聊") @DeleteMapping("/delete/{groupId}") - public Result deleteGroup(@NotNull(message = "群聊id不能为空") @PathVariable Long groupId) { + public Result deleteGroup(@NotNull(message = "群聊id不能为空") @PathVariable("groupId") Long groupId) { groupService.deleteGroup(groupId); return ResultUtils.success(); } - @ApiOperation(value = "查询群聊", notes = "查询单个群聊信息") + @Operation(summary = "查询群聊", description = "查询单个群聊信息") @GetMapping("/find/{groupId}") - public Result findGroup(@NotNull(message = "群聊id不能为空") @PathVariable Long groupId) { + public Result findGroup(@NotNull(message = "群聊id不能为空") @PathVariable("groupId") Long groupId) { return ResultUtils.success(groupService.findById(groupId)); } - @ApiOperation(value = "查询群聊列表", notes = "查询群聊列表") + @Operation(summary = "查询群聊列表", description = "查询群聊列表") @GetMapping("/list") public Result> findGroups() { return ResultUtils.success(groupService.findGroups()); } - @ApiOperation(value = "邀请进群", notes = "邀请好友进群") + @Operation(summary = "邀请进群", description = "邀请好友进群") @PostMapping("/invite") public Result invite(@Valid @RequestBody GroupInviteVO vo) { groupService.invite(vo); return ResultUtils.success(); } - @ApiOperation(value = "查询群聊成员", notes = "查询群聊成员") + @Operation(summary = "查询群聊成员", description = "查询群聊成员") @GetMapping("/members/{groupId}") - public Result> findGroupMembers(@NotNull(message = "群聊id不能为空") @PathVariable Long groupId) { + public Result> findGroupMembers( + @NotNull(message = "群聊id不能为空") @PathVariable("groupId") Long groupId) { return ResultUtils.success(groupService.findGroupMembers(groupId)); } - @ApiOperation(value = "退出群聊", notes = "退出群聊") + @Operation(summary = "退出群聊", description = "退出群聊") @DeleteMapping("/quit/{groupId}") - public Result quitGroup(@NotNull(message = "群聊id不能为空") @PathVariable Long groupId) { + public Result quitGroup(@NotNull(message = "群聊id不能为空") @PathVariable("groupId") Long groupId) { groupService.quitGroup(groupId); return ResultUtils.success(); } - @ApiOperation(value = "踢出群聊", notes = "将用户踢出群聊") + @Operation(summary = "踢出群聊", description = "将用户踢出群聊") @DeleteMapping("/kick/{groupId}") - public Result kickGroup(@NotNull(message = "群聊id不能为空") @PathVariable Long groupId, - @NotNull(message = "用户id不能为空") @RequestParam Long userId) { + public Result kickGroup(@NotNull(message = "群聊id不能为空") @PathVariable("groupId") Long groupId, + @NotNull(message = "用户id不能为空") @RequestParam Long userId) { groupService.kickGroup(groupId, userId); return ResultUtils.success(); } diff --git a/im-platform/src/main/java/com/bx/implatform/controller/GroupMessageController.java b/im-platform/src/main/java/com/bx/implatform/controller/GroupMessageController.java index c9df96c..2a54241 100644 --- a/im-platform/src/main/java/com/bx/implatform/controller/GroupMessageController.java +++ b/im-platform/src/main/java/com/bx/implatform/controller/GroupMessageController.java @@ -5,16 +5,16 @@ import com.bx.implatform.result.Result; import com.bx.implatform.result.ResultUtils; import com.bx.implatform.service.IGroupMessageService; import com.bx.implatform.vo.GroupMessageVO; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; -import javax.validation.Valid; -import javax.validation.constraints.NotNull; import java.util.List; -@Api(tags = "群聊消息") +@Tag(name = "群聊消息") @RestController @RequestMapping("/message/group") @RequiredArgsConstructor @@ -23,43 +23,44 @@ public class GroupMessageController { private final IGroupMessageService groupMessageService; @PostMapping("/send") - @ApiOperation(value = "发送群聊消息", notes = "发送群聊消息") + @Operation(summary = "发送群聊消息", description = "发送群聊消息") public Result sendMessage(@Valid @RequestBody GroupMessageDTO vo) { return ResultUtils.success(groupMessageService.sendMessage(vo)); } @DeleteMapping("/recall/{id}") - @ApiOperation(value = "撤回消息", notes = "撤回群聊消息") - public Result recallMessage(@NotNull(message = "消息id不能为空") @PathVariable Long id) { + @Operation(summary = "撤回消息", description = "撤回群聊消息") + public Result recallMessage(@NotNull(message = "消息id不能为空") @PathVariable("id") Long id) { groupMessageService.recallMessage(id); return ResultUtils.success(); } @GetMapping("/pullOfflineMessage") - @ApiOperation(value = "拉取离线消息", notes = "拉取离线消息,消息将通过webscoket异步推送") - public Result pullOfflineMessage(@RequestParam Long minId) { + @Operation(summary = "拉取离线消息", description = "拉取离线消息,消息将通过webscoket异步推送") + public Result pullOfflineMessage(@RequestParam("minId") Long minId) { groupMessageService.pullOfflineMessage(minId); return ResultUtils.success(); } @PutMapping("/readed") - @ApiOperation(value = "消息已读", notes = "将群聊中的消息状态置为已读") - public Result readedMessage(@RequestParam Long groupId) { + @Operation(summary = "消息已读", description = "将群聊中的消息状态置为已读") + public Result readedMessage(@RequestParam("groupId") Long groupId) { groupMessageService.readedMessage(groupId); return ResultUtils.success(); } @GetMapping("/findReadedUsers") - @ApiOperation(value = "获取已读用户id", notes = "获取消息已读用户列表") - public Result> findReadedUsers(@RequestParam Long groupId,@RequestParam Long messageId) { - return ResultUtils.success(groupMessageService.findReadedUsers(groupId,messageId)); + @Operation(summary = "获取已读用户id", description = "获取消息已读用户列表") + public Result> findReadedUsers(@RequestParam("groupId") Long groupId, + @RequestParam("messageId") Long messageId) { + return ResultUtils.success(groupMessageService.findReadedUsers(groupId, messageId)); } @GetMapping("/history") - @ApiOperation(value = "查询聊天记录", notes = "查询聊天记录") + @Operation(summary = "查询聊天记录", description = "查询聊天记录") public Result> recallMessage(@NotNull(message = "群聊id不能为空") @RequestParam Long groupId, - @NotNull(message = "页码不能为空") @RequestParam Long page, - @NotNull(message = "size不能为空") @RequestParam Long size) { + @NotNull(message = "页码不能为空") @RequestParam Long page, + @NotNull(message = "size不能为空") @RequestParam Long size) { return ResultUtils.success(groupMessageService.findHistoryMessage(groupId, page, size)); } } diff --git a/im-platform/src/main/java/com/bx/implatform/controller/LoginController.java b/im-platform/src/main/java/com/bx/implatform/controller/LoginController.java index 4a960a1..49c0e34 100644 --- a/im-platform/src/main/java/com/bx/implatform/controller/LoginController.java +++ b/im-platform/src/main/java/com/bx/implatform/controller/LoginController.java @@ -7,14 +7,13 @@ import com.bx.implatform.result.Result; import com.bx.implatform.result.ResultUtils; import com.bx.implatform.service.IUserService; import com.bx.implatform.vo.LoginVO; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; -import javax.validation.Valid; - -@Api(tags = "用户登录和注册") +@Tag(name = "用户登录和注册") @RestController @RequiredArgsConstructor public class LoginController { @@ -22,30 +21,28 @@ public class LoginController { private final IUserService userService; @PostMapping("/login") - @ApiOperation(value = "用户登陆", notes = "用户登陆") + @Operation(summary = "用户登陆", description = "用户登陆") public Result login(@Valid @RequestBody LoginDTO dto) { LoginVO vo = userService.login(dto); return ResultUtils.success(vo); } - @PutMapping("/refreshToken") - @ApiOperation(value = "刷新token", notes = "用refreshtoken换取新的token") + @Operation(summary = "刷新token", description = "用refreshtoken换取新的token") public Result refreshToken(@RequestHeader("refreshToken") String refreshToken) { LoginVO vo = userService.refreshToken(refreshToken); return ResultUtils.success(vo); } - @PostMapping("/register") - @ApiOperation(value = "用户注册", notes = "用户注册") + @Operation(summary = "用户注册", description = "用户注册") public Result register(@Valid @RequestBody RegisterDTO dto) { userService.register(dto); return ResultUtils.success(); } @PutMapping("/modifyPwd") - @ApiOperation(value = "修改密码", notes = "修改用户密码") + @Operation(summary = "修改密码", description = "修改用户密码") public Result modifyPassword(@Valid @RequestBody ModifyPwdDTO dto) { userService.modifyPassword(dto); return ResultUtils.success(); diff --git a/im-platform/src/main/java/com/bx/implatform/controller/PrivateMessageController.java b/im-platform/src/main/java/com/bx/implatform/controller/PrivateMessageController.java index 9f99531..7cc3da1 100644 --- a/im-platform/src/main/java/com/bx/implatform/controller/PrivateMessageController.java +++ b/im-platform/src/main/java/com/bx/implatform/controller/PrivateMessageController.java @@ -5,16 +5,16 @@ import com.bx.implatform.result.Result; import com.bx.implatform.result.ResultUtils; import com.bx.implatform.service.IPrivateMessageService; import com.bx.implatform.vo.PrivateMessageVO; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; -import javax.validation.Valid; -import javax.validation.constraints.NotNull; import java.util.List; -@Api(tags = "私聊消息") +@Tag(name = "私聊消息") @RestController @RequestMapping("/message/private") @RequiredArgsConstructor @@ -23,44 +23,44 @@ public class PrivateMessageController { private final IPrivateMessageService privateMessageService; @PostMapping("/send") - @ApiOperation(value = "发送消息", notes = "发送私聊消息") + @Operation(summary = "发送消息", description = "发送私聊消息") public Result sendMessage(@Valid @RequestBody PrivateMessageDTO vo) { return ResultUtils.success(privateMessageService.sendMessage(vo)); } - @DeleteMapping("/recall/{id}") - @ApiOperation(value = "撤回消息", notes = "撤回私聊消息") - public Result recallMessage(@NotNull(message = "消息id不能为空") @PathVariable Long id) { + @Operation(summary = "撤回消息", description = "撤回私聊消息") + public Result recallMessage(@NotNull(message = "消息id不能为空") @PathVariable("id") Long id) { privateMessageService.recallMessage(id); return ResultUtils.success(); } @GetMapping("/pullOfflineMessage") - @ApiOperation(value = "拉取离线消息", notes = "拉取离线消息,消息将通过webscoket异步推送") - public Result pullOfflineMessage(@RequestParam Long minId) { + @Operation(summary = "拉取离线消息", description = "拉取离线消息,消息将通过webscoket异步推送") + public Result pullOfflineMessage(@RequestParam("minId") Long minId) { privateMessageService.pullOfflineMessage(minId); return ResultUtils.success(); } @PutMapping("/readed") - @ApiOperation(value = "消息已读", notes = "将会话中接收的消息状态置为已读") - public Result readedMessage(@RequestParam Long friendId) { + @Operation(summary = "消息已读", description = "将会话中接收的消息状态置为已读") + public Result readedMessage(@RequestParam("friendId") Long friendId) { privateMessageService.readedMessage(friendId); return ResultUtils.success(); } @GetMapping("/maxReadedId") - @ApiOperation(value = "获取最大已读消息的id",notes="获取某个会话中已读消息的最大id") - public Result getMaxReadedId(@RequestParam Long friendId){ + @Operation(summary = "获取最大已读消息的id", description = "获取某个会话中已读消息的最大id") + public Result getMaxReadedId(@RequestParam("friendId") Long friendId) { return ResultUtils.success(privateMessageService.getMaxReadedId(friendId)); } @GetMapping("/history") - @ApiOperation(value = "查询聊天记录", notes = "查询聊天记录") - public Result> recallMessage(@NotNull(message = "好友id不能为空") @RequestParam Long friendId, - @NotNull(message = "页码不能为空") @RequestParam Long page, - @NotNull(message = "size不能为空") @RequestParam Long size) { + @Operation(summary = "查询聊天记录", description = "查询聊天记录") + public Result> recallMessage( + @NotNull(message = "好友id不能为空") @RequestParam("friendId") Long friendId, + @NotNull(message = "页码不能为空") @RequestParam("page") Long page, + @NotNull(message = "size不能为空") @RequestParam("size") Long size) { return ResultUtils.success(privateMessageService.findHistoryMessage(friendId, page, size)); } diff --git a/im-platform/src/main/java/com/bx/implatform/controller/SystemController.java b/im-platform/src/main/java/com/bx/implatform/controller/SystemController.java index d3cd30b..fd6de4d 100644 --- a/im-platform/src/main/java/com/bx/implatform/controller/SystemController.java +++ b/im-platform/src/main/java/com/bx/implatform/controller/SystemController.java @@ -1,14 +1,15 @@ package com.bx.implatform.controller; import com.bx.implatform.config.WebrtcConfig; -import com.bx.implatform.dto.PrivateMessageDTO; import com.bx.implatform.result.Result; import com.bx.implatform.result.ResultUtils; import com.bx.implatform.vo.SystemConfigVO; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; @@ -17,7 +18,7 @@ import org.springframework.web.bind.annotation.*; * @date: 2024-06-10 * @version: 1.0 */ -@Api(tags = "系统相关") +@Tag(name = "系统相关") @RestController @RequestMapping("/system") @RequiredArgsConstructor @@ -26,7 +27,7 @@ public class SystemController { private final WebrtcConfig webrtcConfig; @GetMapping("/config") - @ApiOperation(value = "加载系统配置", notes = "加载系统配置") + @Operation(summary = "加载系统配置", description = "加载系统配置") public Result loadConfig() { return ResultUtils.success(new SystemConfigVO(webrtcConfig)); } diff --git a/im-platform/src/main/java/com/bx/implatform/controller/UserController.java b/im-platform/src/main/java/com/bx/implatform/controller/UserController.java index 1291db2..dd6c133 100644 --- a/im-platform/src/main/java/com/bx/implatform/controller/UserController.java +++ b/im-platform/src/main/java/com/bx/implatform/controller/UserController.java @@ -9,16 +9,16 @@ import com.bx.implatform.session.UserSession; import com.bx.implatform.util.BeanUtils; import com.bx.implatform.vo.OnlineTerminalVO; import com.bx.implatform.vo.UserVO; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; -import javax.validation.Valid; -import javax.validation.constraints.NotEmpty; import java.util.List; -@Api(tags = "用户") +@Tag(name = "用户") @RestController @RequestMapping("/user") @RequiredArgsConstructor @@ -27,14 +27,14 @@ public class UserController { private final IUserService userService; @GetMapping("/terminal/online") - @ApiOperation(value = "判断用户哪个终端在线", notes = "返回在线的用户id的终端集合") - public Result> getOnlineTerminal(@NotEmpty @RequestParam("userIds") String userIds) { + @Operation(summary = "判断用户哪个终端在线", description = "返回在线的用户id的终端集合") + public Result> getOnlineTerminal(@NotNull @RequestParam("userIds") String userIds) { return ResultUtils.success(userService.getOnlineTerminals(userIds)); } @GetMapping("/self") - @ApiOperation(value = "获取当前用户信息", notes = "获取当前用户信息") + @Operation(summary = "获取当前用户信息", description = "获取当前用户信息") public Result findSelfInfo() { UserSession session = SessionContext.getSession(); User user = userService.getById(session.getUserId()); @@ -44,20 +44,20 @@ public class UserController { @GetMapping("/find/{id}") - @ApiOperation(value = "查找用户", notes = "根据id查找用户") - public Result findById(@NotEmpty @PathVariable("id") Long id) { + @Operation(summary = "查找用户", description = "根据id查找用户") + public Result findById(@NotNull @PathVariable("id") Long id) { return ResultUtils.success(userService.findUserById(id)); } @PutMapping("/update") - @ApiOperation(value = "修改用户信息", notes = "修改用户信息,仅允许修改登录用户信息") + @Operation(summary = "修改用户信息", description = "修改用户信息,仅允许修改登录用户信息") public Result update(@Valid @RequestBody UserVO vo) { userService.update(vo); return ResultUtils.success(); } @GetMapping("/findByName") - @ApiOperation(value = "查找用户", notes = "根据用户名或昵称查找用户") + @Operation(summary = "查找用户", description = "根据用户名或昵称查找用户") public Result> findByName(@RequestParam("name") String name) { return ResultUtils.success(userService.findUserByName(name)); } diff --git a/im-platform/src/main/java/com/bx/implatform/controller/WebrtcGroupController.java b/im-platform/src/main/java/com/bx/implatform/controller/WebrtcGroupController.java index c9fb6b7..fa3bf62 100644 --- a/im-platform/src/main/java/com/bx/implatform/controller/WebrtcGroupController.java +++ b/im-platform/src/main/java/com/bx/implatform/controller/WebrtcGroupController.java @@ -1,24 +1,22 @@ package com.bx.implatform.controller; -import com.bx.implatform.config.WebrtcConfig; import com.bx.implatform.dto.*; import com.bx.implatform.result.Result; import com.bx.implatform.result.ResultUtils; import com.bx.implatform.service.IWebrtcGroupService; import com.bx.implatform.vo.WebrtcGroupInfoVO; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; -import javax.validation.Valid; - /** * @author: Blue * @date: 2024-06-01 * @version: 1.0 */ -@Api(tags = "webrtc视频多人通话") +@Tag(name = "webrtc视频多人通话") @RestController @RequestMapping("/webrtc/group") @RequiredArgsConstructor @@ -26,101 +24,101 @@ public class WebrtcGroupController { private final IWebrtcGroupService webrtcGroupService; - @ApiOperation(httpMethod = "POST", value = "发起群视频通话") + @Operation(summary = "发起群视频通话") @PostMapping("/setup") public Result setup(@Valid @RequestBody WebrtcGroupSetupDTO dto) { webrtcGroupService.setup(dto); return ResultUtils.success(); } - @ApiOperation(httpMethod = "POST", value = "接受通话") + @Operation(summary = "接受通话") @PostMapping("/accept") - public Result accept(@RequestParam Long groupId) { + public Result accept(@RequestParam("groupId") Long groupId) { webrtcGroupService.accept(groupId); return ResultUtils.success(); } - @ApiOperation(httpMethod = "POST", value = "拒绝通话") + @Operation(summary = "拒绝通话") @PostMapping("/reject") - public Result reject(@RequestParam Long groupId) { + public Result reject(@RequestParam("groupId") Long groupId) { webrtcGroupService.reject(groupId); return ResultUtils.success(); } - @ApiOperation(httpMethod = "POST", value = "通话失败") + @Operation(summary = "通话失败") @PostMapping("/failed") public Result failed(@Valid @RequestBody WebrtcGroupFailedDTO dto) { webrtcGroupService.failed(dto); return ResultUtils.success(); } - @ApiOperation(httpMethod = "POST", value = "进入视频通话") + @Operation(summary = "进入视频通话") @PostMapping("/join") - public Result join(@RequestParam Long groupId) { + public Result join(@RequestParam("groupId") Long groupId) { webrtcGroupService.join(groupId); return ResultUtils.success(); } - @ApiOperation(httpMethod = "POST", value = "取消通话") + @Operation(summary = "取消通话") @PostMapping("/cancel") - public Result cancel(@RequestParam Long groupId) { + public Result cancel(@RequestParam("groupId") Long groupId) { webrtcGroupService.cancel(groupId); return ResultUtils.success(); } - @ApiOperation(httpMethod = "POST", value = "离开视频通话") + @Operation(summary = "离开视频通话") @PostMapping("/quit") - public Result quit(@RequestParam Long groupId) { + public Result quit(@RequestParam("groupId") Long groupId) { webrtcGroupService.quit(groupId); return ResultUtils.success(); } - @ApiOperation(httpMethod = "POST", value = "推送offer信息") + @Operation(summary = "推送offer信息") @PostMapping("/offer") public Result offer(@Valid @RequestBody WebrtcGroupOfferDTO dto) { webrtcGroupService.offer(dto); return ResultUtils.success(); } - @ApiOperation(httpMethod = "POST", value = "推送answer信息") + @Operation(summary = "推送answer信息") @PostMapping("/answer") public Result answer(@Valid @RequestBody WebrtcGroupAnswerDTO dto) { webrtcGroupService.answer(dto); return ResultUtils.success(); } - @ApiOperation(httpMethod = "POST", value = "邀请用户进入视频通话") + @Operation(summary = "邀请用户进入视频通话") @PostMapping("/invite") public Result invite(@Valid @RequestBody WebrtcGroupInviteDTO dto) { webrtcGroupService.invite(dto); return ResultUtils.success(); } - @ApiOperation(httpMethod = "POST", value = "同步candidate") + @Operation(summary = "同步candidate") @PostMapping("/candidate") public Result candidate(@Valid @RequestBody WebrtcGroupCandidateDTO dto) { webrtcGroupService.candidate(dto); return ResultUtils.success(); } - @ApiOperation(httpMethod = "POST", value = "设备操作") + @Operation(summary = "设备操作") @PostMapping("/device") public Result device(@Valid @RequestBody WebrtcGroupDeviceDTO dto) { webrtcGroupService.device(dto); return ResultUtils.success(); } - @ApiOperation(httpMethod = "GET", value = "获取通话信息") + @Operation(summary = "获取通话信息") @GetMapping("/info") - public Result info(@RequestParam Long groupId) { + public Result info(@RequestParam("groupId") Long groupId) { return ResultUtils.success(webrtcGroupService.info(groupId)); } - @ApiOperation(httpMethod = "POST", value = "获取通话信息") + @Operation(summary = "获取通话信息") @PostMapping("/heartbeat") - public Result heartbeat(@RequestParam Long groupId) { + public Result heartbeat(@RequestParam("groupId") Long groupId) { webrtcGroupService.heartbeat(groupId); return ResultUtils.success(); } - } +} diff --git a/im-platform/src/main/java/com/bx/implatform/controller/WebrtcPrivateController.java b/im-platform/src/main/java/com/bx/implatform/controller/WebrtcPrivateController.java index 7d583c6..c0d4728 100644 --- a/im-platform/src/main/java/com/bx/implatform/controller/WebrtcPrivateController.java +++ b/im-platform/src/main/java/com/bx/implatform/controller/WebrtcPrivateController.java @@ -1,18 +1,15 @@ package com.bx.implatform.controller; import com.bx.implatform.annotation.OnlineCheck; -import com.bx.implatform.config.ICEServer; import com.bx.implatform.result.Result; import com.bx.implatform.result.ResultUtils; import com.bx.implatform.service.IWebrtcPrivateService; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; -import java.util.List; - -@Api(tags = "webrtc视频单人通话") +@Tag(name = "webrtc视频单人通话") @RestController @RequestMapping("/webrtc/private") @RequiredArgsConstructor @@ -21,60 +18,59 @@ public class WebrtcPrivateController { private final IWebrtcPrivateService webrtcPrivateService; @OnlineCheck - @ApiOperation(httpMethod = "POST", value = "呼叫视频通话") + @Operation(summary = "呼叫视频通话") @PostMapping("/call") - public Result call(@RequestParam Long uid, @RequestParam(defaultValue = "video") String mode, @RequestBody String offer) { + public Result call(@RequestParam("uid") Long uid, @RequestParam(name = "mode", defaultValue = "video") String mode, + @RequestBody String offer) { webrtcPrivateService.call(uid, mode, offer); return ResultUtils.success(); } - @ApiOperation(httpMethod = "POST", value = "接受视频通话") + @Operation(summary = "接受视频通话") @PostMapping("/accept") - public Result accept(@RequestParam Long uid, @RequestBody String answer) { + public Result accept(@RequestParam("uid") Long uid, @RequestBody String answer) { webrtcPrivateService.accept(uid, answer); return ResultUtils.success(); } - - @ApiOperation(httpMethod = "POST", value = "拒绝视频通话") + @Operation(summary = "拒绝视频通话") @PostMapping("/reject") - public Result reject(@RequestParam Long uid) { + public Result reject(@RequestParam("uid") Long uid) { webrtcPrivateService.reject(uid); return ResultUtils.success(); } - @ApiOperation(httpMethod = "POST", value = "取消呼叫") + @Operation(summary = "取消呼叫") @PostMapping("/cancel") - public Result cancel(@RequestParam Long uid) { + public Result cancel(@RequestParam("uid") Long uid) { webrtcPrivateService.cancel(uid); return ResultUtils.success(); } - @ApiOperation(httpMethod = "POST", value = "呼叫失败") + @Operation(summary = "呼叫失败") @PostMapping("/failed") - public Result failed(@RequestParam Long uid, @RequestParam String reason) { + public Result failed(@RequestParam("uid") Long uid, @RequestParam String reason) { webrtcPrivateService.failed(uid, reason); return ResultUtils.success(); } - @ApiOperation(httpMethod = "POST", value = "挂断") + @Operation(summary = "挂断") @PostMapping("/handup") - public Result handup(@RequestParam Long uid) { + public Result handup(@RequestParam("uid") Long uid) { webrtcPrivateService.handup(uid); return ResultUtils.success(); } - @PostMapping("/candidate") - @ApiOperation(httpMethod = "POST", value = "同步candidate") - public Result candidate(@RequestParam Long uid, @RequestBody String candidate) { + @Operation(summary = "同步candidate") + public Result candidate(@RequestParam("uid") Long uid, @RequestBody String candidate) { webrtcPrivateService.candidate(uid, candidate); return ResultUtils.success(); } - @ApiOperation(httpMethod = "POST", value = "获取通话信息") + @Operation(summary = "获取通话信息") @PostMapping("/heartbeat") - public Result heartbeat(@RequestParam Long uid) { + public Result heartbeat(@RequestParam("uid") Long uid) { webrtcPrivateService.heartbeat(uid); return ResultUtils.success(); } diff --git a/im-platform/src/main/java/com/bx/implatform/dto/GroupBanDTO.java b/im-platform/src/main/java/com/bx/implatform/dto/GroupBanDTO.java index ec36509..cbe673d 100644 --- a/im-platform/src/main/java/com/bx/implatform/dto/GroupBanDTO.java +++ b/im-platform/src/main/java/com/bx/implatform/dto/GroupBanDTO.java @@ -1,7 +1,7 @@ package com.bx.implatform.dto; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; + +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; /** @@ -10,12 +10,12 @@ import lombok.Data; * @version: 1.0 */ @Data -@ApiModel(description = "群组封禁") +@Schema(description = "群组封禁") public class GroupBanDTO { - @ApiModelProperty(value = "群组id") + @Schema(description = "群组id") private Long id; - @ApiModelProperty(value = "封禁原因") + @Schema(description = "封禁原因") private String reason; } diff --git a/im-platform/src/main/java/com/bx/implatform/dto/GroupMessageDTO.java b/im-platform/src/main/java/com/bx/implatform/dto/GroupMessageDTO.java index 6af8f38..4fbf715 100644 --- a/im-platform/src/main/java/com/bx/implatform/dto/GroupMessageDTO.java +++ b/im-platform/src/main/java/com/bx/implatform/dto/GroupMessageDTO.java @@ -1,36 +1,35 @@ package com.bx.implatform.dto; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; import lombok.Data; import org.hibernate.validator.constraints.Length; -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Size; import java.util.List; @Data -@ApiModel("群聊消息DTO") +@Schema(description = "群聊消息DTO") public class GroupMessageDTO { @NotNull(message = "群聊id不可为空") - @ApiModelProperty(value = "群聊id") + @Schema(description = "群聊id") private Long groupId; @Length(max = 1024, message = "发送内容长度不得大于1024") @NotEmpty(message = "发送内容不可为空") - @ApiModelProperty(value = "发送内容") + @Schema(description = "发送内容") private String content; @NotNull(message = "消息类型不可为空") - @ApiModelProperty(value = "消息类型") + @Schema(description = "消息类型") private Integer type; - @ApiModelProperty(value = "是否回执消息") + @Schema(description = "是否回执消息") private Boolean receipt = false; @Size(max = 20, message = "一次最多只能@20个小伙伴哦") - @ApiModelProperty(value = "被@用户列表") + @Schema(description = "被@用户列表") private List atUserIds; } diff --git a/im-platform/src/main/java/com/bx/implatform/dto/GroupUnbanDTO.java b/im-platform/src/main/java/com/bx/implatform/dto/GroupUnbanDTO.java index 7a9a7fd..2ccffa6 100644 --- a/im-platform/src/main/java/com/bx/implatform/dto/GroupUnbanDTO.java +++ b/im-platform/src/main/java/com/bx/implatform/dto/GroupUnbanDTO.java @@ -1,7 +1,6 @@ package com.bx.implatform.dto; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; /** @@ -10,10 +9,10 @@ import lombok.Data; * @version: 1.0 */ @Data -@ApiModel(description = "群组解锁") +@Schema(description = "群组解锁") public class GroupUnbanDTO { - @ApiModelProperty(value = "群组id") + @Schema(description = "群组id") private Long id; } diff --git a/im-platform/src/main/java/com/bx/implatform/dto/LoginDTO.java b/im-platform/src/main/java/com/bx/implatform/dto/LoginDTO.java index 0cdede2..30b240e 100644 --- a/im-platform/src/main/java/com/bx/implatform/dto/LoginDTO.java +++ b/im-platform/src/main/java/com/bx/implatform/dto/LoginDTO.java @@ -1,30 +1,28 @@ package com.bx.implatform.dto; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; import lombok.Data; -import javax.validation.constraints.Max; -import javax.validation.constraints.Min; -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; - @Data -@ApiModel("用户登录DTO") +@Schema(description = "用户登录DTO") public class LoginDTO { @Max(value = 2, message = "登录终端类型取值范围:0,2") @Min(value = 0, message = "登录终端类型取值范围:0,2") @NotNull(message = "登录终端类型不可为空") - @ApiModelProperty(value = "登录终端 0:web 1:app 2:pc") + @Schema(description = "登录终端 0:web 1:app 2:pc") private Integer terminal; @NotEmpty(message = "用户名不可为空") - @ApiModelProperty(value = "用户名") + @Schema(description = "用户名") private String userName; @NotEmpty(message = "用户密码不可为空") - @ApiModelProperty(value = "用户密码") + @Schema(description = "用户密码") private String password; } diff --git a/im-platform/src/main/java/com/bx/implatform/dto/ModifyPwdDTO.java b/im-platform/src/main/java/com/bx/implatform/dto/ModifyPwdDTO.java index 3ba3078..c733493 100644 --- a/im-platform/src/main/java/com/bx/implatform/dto/ModifyPwdDTO.java +++ b/im-platform/src/main/java/com/bx/implatform/dto/ModifyPwdDTO.java @@ -1,21 +1,19 @@ package com.bx.implatform.dto; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; import lombok.Data; -import javax.validation.constraints.NotEmpty; - @Data -@ApiModel("修改密码DTO") +@Schema(description = "修改密码DTO") public class ModifyPwdDTO { @NotEmpty(message = "旧用户密码不可为空") - @ApiModelProperty(value = "旧用户密码") + @Schema(description = "旧用户密码") private String oldPassword; @NotEmpty(message = "新用户密码不可为空") - @ApiModelProperty(value = "新用户密码") + @Schema(description = "新用户密码") private String newPassword; } diff --git a/im-platform/src/main/java/com/bx/implatform/dto/PrivateMessageDTO.java b/im-platform/src/main/java/com/bx/implatform/dto/PrivateMessageDTO.java index 99cee20..4fb49e0 100644 --- a/im-platform/src/main/java/com/bx/implatform/dto/PrivateMessageDTO.java +++ b/im-platform/src/main/java/com/bx/implatform/dto/PrivateMessageDTO.java @@ -1,29 +1,27 @@ package com.bx.implatform.dto; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; import lombok.Data; import org.hibernate.validator.constraints.Length; -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; - @Data -@ApiModel("私聊消息DTO") +@Schema(description = "私聊消息DTO") public class PrivateMessageDTO { @NotNull(message = "接收用户id不可为空") - @ApiModelProperty(value = "接收用户id") + @Schema(description = "接收用户id") private Long recvId; @Length(max = 1024, message = "内容长度不得大于1024") @NotEmpty(message = "发送内容不可为空") - @ApiModelProperty(value = "发送内容") + @Schema(description = "发送内容") private String content; @NotNull(message = "消息类型不可为空") - @ApiModelProperty(value = "消息类型") + @Schema(description = "消息类型") private Integer type; } diff --git a/im-platform/src/main/java/com/bx/implatform/dto/RegisterDTO.java b/im-platform/src/main/java/com/bx/implatform/dto/RegisterDTO.java index fd33135..f298808 100644 --- a/im-platform/src/main/java/com/bx/implatform/dto/RegisterDTO.java +++ b/im-platform/src/main/java/com/bx/implatform/dto/RegisterDTO.java @@ -1,29 +1,27 @@ package com.bx.implatform.dto; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; import lombok.Data; import org.hibernate.validator.constraints.Length; -import javax.validation.constraints.NotEmpty; - @Data -@ApiModel("用户注册DTO") +@Schema(description = "用户注册DTO") public class RegisterDTO { @Length(max = 64, message = "用户名不能大于64字符") @NotEmpty(message = "用户名不可为空") - @ApiModelProperty(value = "用户名") + @Schema(description = "用户名") private String userName; @Length(min = 5, max = 20, message = "密码长度必须在5-20个字符之间") @NotEmpty(message = "用户密码不可为空") - @ApiModelProperty(value = "用户密码") + @Schema(description = "用户密码") private String password; @Length(max = 64, message = "昵称不能大于64字符") @NotEmpty(message = "用户昵称不可为空") - @ApiModelProperty(value = "用户昵称") + @Schema(description = "用户昵称") private String nickName; diff --git a/im-platform/src/main/java/com/bx/implatform/dto/UserBanDTO.java b/im-platform/src/main/java/com/bx/implatform/dto/UserBanDTO.java index 38571f9..f8953c6 100644 --- a/im-platform/src/main/java/com/bx/implatform/dto/UserBanDTO.java +++ b/im-platform/src/main/java/com/bx/implatform/dto/UserBanDTO.java @@ -1,7 +1,6 @@ package com.bx.implatform.dto; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; /** @@ -10,13 +9,13 @@ import lombok.Data; * @version: 1.0 */ @Data -@ApiModel("用户锁定DTO") +@Schema(description = "用户锁定DTO") public class UserBanDTO { - @ApiModelProperty(value = "用户id") + @Schema(description = "用户id") private Long id; - @ApiModelProperty(value = "锁定原因") + @Schema(description = "锁定原因") private String reason; } diff --git a/im-platform/src/main/java/com/bx/implatform/dto/WebrtcGroupAnswerDTO.java b/im-platform/src/main/java/com/bx/implatform/dto/WebrtcGroupAnswerDTO.java index b66e68e..6e96bc0 100644 --- a/im-platform/src/main/java/com/bx/implatform/dto/WebrtcGroupAnswerDTO.java +++ b/im-platform/src/main/java/com/bx/implatform/dto/WebrtcGroupAnswerDTO.java @@ -1,31 +1,29 @@ package com.bx.implatform.dto; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; import lombok.Data; -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; - /** * @author: Blue * @date: 2024-06-01 * @version: 1.0 */ @Data -@ApiModel("回复用户连接请求DTO") +@Schema(description = "回复用户连接请求DTO") public class WebrtcGroupAnswerDTO { @NotNull(message = "群聊id不可为空") - @ApiModelProperty(value = "群聊id") + @Schema(description = "群聊id") private Long groupId; @NotNull(message = "用户id不可为空") - @ApiModelProperty(value = "用户id,代表回复谁的连接请求") + @Schema(description = "用户id,代表回复谁的连接请求") private Long userId; @NotEmpty(message = "anwer不可为空") - @ApiModelProperty(value = "用户本地anwer信息") + @Schema(description = "用户本地anwer信息") private String answer; } diff --git a/im-platform/src/main/java/com/bx/implatform/dto/WebrtcGroupCandidateDTO.java b/im-platform/src/main/java/com/bx/implatform/dto/WebrtcGroupCandidateDTO.java index fd04e94..69d054e 100644 --- a/im-platform/src/main/java/com/bx/implatform/dto/WebrtcGroupCandidateDTO.java +++ b/im-platform/src/main/java/com/bx/implatform/dto/WebrtcGroupCandidateDTO.java @@ -1,32 +1,29 @@ package com.bx.implatform.dto; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; import lombok.Data; -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; -import java.util.List; - /** * @author: Blue * @date: 2024-06-01 * @version: 1.0 */ @Data -@ApiModel("发起群视频通话DTO") +@Schema(description = "发起群视频通话DTO") public class WebrtcGroupCandidateDTO { @NotNull(message = "群聊id不可为空") - @ApiModelProperty(value = "群聊id") + @Schema(description = "群聊id") private Long groupId; @NotNull(message = "用户id不可为空") - @ApiModelProperty(value = "用户id") + @Schema(description = "用户id") private Long userId; @NotEmpty(message = "candidate信息不可为空") - @ApiModelProperty(value = "candidate信息") + @Schema(description = "candidate信息") private String candidate; } diff --git a/im-platform/src/main/java/com/bx/implatform/dto/WebrtcGroupDeviceDTO.java b/im-platform/src/main/java/com/bx/implatform/dto/WebrtcGroupDeviceDTO.java index 5d87b4d..b7e2073 100644 --- a/im-platform/src/main/java/com/bx/implatform/dto/WebrtcGroupDeviceDTO.java +++ b/im-platform/src/main/java/com/bx/implatform/dto/WebrtcGroupDeviceDTO.java @@ -1,29 +1,26 @@ package com.bx.implatform.dto; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; import lombok.Data; -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; - /** * @author: Blue * @date: 2024-06-01 * @version: 1.0 */ @Data -@ApiModel("用户设备操作DTO") +@Schema(description = "用户设备操作DTO") public class WebrtcGroupDeviceDTO { @NotNull(message = "群聊id不可为空") - @ApiModelProperty(value = "群聊id") + @Schema(description = "群聊id") private Long groupId; - @ApiModelProperty(value = "是否开启摄像头") + @Schema(description = "是否开启摄像头") private Boolean isCamera; - @ApiModelProperty(value = "是否开启麦克风") + @Schema(description = "是否开启麦克风") private Boolean isMicroPhone; } diff --git a/im-platform/src/main/java/com/bx/implatform/dto/WebrtcGroupFailedDTO.java b/im-platform/src/main/java/com/bx/implatform/dto/WebrtcGroupFailedDTO.java index 3df2cb9..fa2d5ba 100644 --- a/im-platform/src/main/java/com/bx/implatform/dto/WebrtcGroupFailedDTO.java +++ b/im-platform/src/main/java/com/bx/implatform/dto/WebrtcGroupFailedDTO.java @@ -1,25 +1,23 @@ package com.bx.implatform.dto; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; import lombok.Data; -import javax.validation.constraints.NotNull; - /** * @author: Blue * @date: 2024-06-01 * @version: 1.0 */ @Data -@ApiModel("用户通话失败DTO") +@Schema(description = "用户通话失败DTO") public class WebrtcGroupFailedDTO { @NotNull(message = "群聊id不可为空") - @ApiModelProperty(value = "群聊id") + @Schema(description = "群聊id") private Long groupId; - @ApiModelProperty(value = "失败原因") + @Schema(description = "失败原因") private String reason; } diff --git a/im-platform/src/main/java/com/bx/implatform/dto/WebrtcGroupInviteDTO.java b/im-platform/src/main/java/com/bx/implatform/dto/WebrtcGroupInviteDTO.java index 7719a39..1c0f894 100644 --- a/im-platform/src/main/java/com/bx/implatform/dto/WebrtcGroupInviteDTO.java +++ b/im-platform/src/main/java/com/bx/implatform/dto/WebrtcGroupInviteDTO.java @@ -1,12 +1,11 @@ package com.bx.implatform.dto; import com.bx.implatform.session.WebrtcUserInfo; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; import lombok.Data; -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; import java.util.List; /** @@ -15,15 +14,15 @@ import java.util.List; * @version: 1.0 */ @Data -@ApiModel("邀请用户进入群视频通话DTO") +@Schema(description = "邀请用户进入群视频通话DTO") public class WebrtcGroupInviteDTO { @NotNull(message = "群聊id不可为空") - @ApiModelProperty(value = "群聊id") + @Schema(description = "群聊id") private Long groupId; @NotEmpty(message = "参与用户信息不可为空") - @ApiModelProperty(value = "参与用户信息") + @Schema(description = "参与用户信息") private List userInfos; } diff --git a/im-platform/src/main/java/com/bx/implatform/dto/WebrtcGroupJoinDTO.java b/im-platform/src/main/java/com/bx/implatform/dto/WebrtcGroupJoinDTO.java index adbc066..b59adb0 100644 --- a/im-platform/src/main/java/com/bx/implatform/dto/WebrtcGroupJoinDTO.java +++ b/im-platform/src/main/java/com/bx/implatform/dto/WebrtcGroupJoinDTO.java @@ -1,23 +1,20 @@ package com.bx.implatform.dto; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; import lombok.Data; -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; - /** * @author: Blue * @date: 2024-06-01 * @version: 1.0 */ @Data -@ApiModel("进入群视频通话DTO") +@Schema(description = "进入群视频通话DTO") public class WebrtcGroupJoinDTO { @NotNull(message = "群聊id不可为空") - @ApiModelProperty(value = "群聊id") + @Schema(description = "群聊id") private Long groupId; } diff --git a/im-platform/src/main/java/com/bx/implatform/dto/WebrtcGroupOfferDTO.java b/im-platform/src/main/java/com/bx/implatform/dto/WebrtcGroupOfferDTO.java index 057fe20..dbe0048 100644 --- a/im-platform/src/main/java/com/bx/implatform/dto/WebrtcGroupOfferDTO.java +++ b/im-platform/src/main/java/com/bx/implatform/dto/WebrtcGroupOfferDTO.java @@ -1,31 +1,29 @@ package com.bx.implatform.dto; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; import lombok.Data; -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; - /** * @author: Blue * @date: 2024-06-01 * @version: 1.0 */ @Data -@ApiModel("回复用户连接请求DTO") +@Schema(description = "回复用户连接请求DTO") public class WebrtcGroupOfferDTO { @NotNull(message = "群聊id不可为空") - @ApiModelProperty(value = "群聊id") + @Schema(description = "群聊id") private Long groupId; @NotNull(message = "用户id不可为空") - @ApiModelProperty(value = "用户id,代表回复谁的连接请求") + @Schema(description = "用户id,代表回复谁的连接请求") private Long userId; @NotEmpty(message = "offer不可为空") - @ApiModelProperty(value = "用户offer信息") + @Schema(description = "用户offer信息") private String offer; } diff --git a/im-platform/src/main/java/com/bx/implatform/dto/WebrtcGroupSetupDTO.java b/im-platform/src/main/java/com/bx/implatform/dto/WebrtcGroupSetupDTO.java index f59989f..724ceec 100644 --- a/im-platform/src/main/java/com/bx/implatform/dto/WebrtcGroupSetupDTO.java +++ b/im-platform/src/main/java/com/bx/implatform/dto/WebrtcGroupSetupDTO.java @@ -1,12 +1,11 @@ package com.bx.implatform.dto; import com.bx.implatform.session.WebrtcUserInfo; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; import lombok.Data; -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; import java.util.List; /** @@ -15,15 +14,15 @@ import java.util.List; * @version: 1.0 */ @Data -@ApiModel("发起群视频通话DTO") +@Schema(description = "发起群视频通话DTO") public class WebrtcGroupSetupDTO { @NotNull(message = "群聊id不可为空") - @ApiModelProperty(value = "群聊id") + @Schema(description = "群聊id") private Long groupId; @NotEmpty(message = "参与用户信息不可为空") - @ApiModelProperty(value = "参与用户信息") + @Schema(description = "参与用户信息") private List userInfos; } diff --git a/im-platform/src/main/java/com/bx/implatform/entity/Friend.java b/im-platform/src/main/java/com/bx/implatform/entity/Friend.java index 364ffe1..f869b81 100644 --- a/im-platform/src/main/java/com/bx/implatform/entity/Friend.java +++ b/im-platform/src/main/java/com/bx/implatform/entity/Friend.java @@ -1,14 +1,9 @@ package com.bx.implatform.entity; -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; -import com.baomidou.mybatisplus.extension.activerecord.Model; import lombok.Data; -import lombok.EqualsAndHashCode; -import java.io.Serializable; import java.util.Date; /** @@ -20,52 +15,39 @@ import java.util.Date; * @since 2022-10-22 */ @Data -@EqualsAndHashCode(callSuper = false) @TableName("im_friend") -public class Friend extends Model { +public class Friend{ - private static final long serialVersionUID = 1L; /** * id */ - @TableId(value = "id", type = IdType.AUTO) + @TableId private Long id; /** * 用户id */ - @TableField("user_id") private Long userId; /** * 好友id */ - @TableField("friend_id") private Long friendId; /** * 用户昵称 */ - @TableField("friend_nick_name") private String friendNickName; /** * 用户头像 */ - @TableField("friend_head_image") private String friendHeadImage; /** * 创建时间 */ - @TableField("created_time") private Date createdTime; - - @Override - protected Serializable pkVal() { - return this.id; - } - } diff --git a/im-platform/src/main/java/com/bx/implatform/entity/Group.java b/im-platform/src/main/java/com/bx/implatform/entity/Group.java index 1f4ba0b..86cdd55 100644 --- a/im-platform/src/main/java/com/bx/implatform/entity/Group.java +++ b/im-platform/src/main/java/com/bx/implatform/entity/Group.java @@ -1,14 +1,9 @@ package com.bx.implatform.entity; -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; -import com.baomidou.mybatisplus.extension.activerecord.Model; import lombok.Data; -import lombok.EqualsAndHashCode; -import java.io.Serializable; import java.util.Date; /** @@ -18,64 +13,60 @@ import java.util.Date; * @since 2022-10-31 */ @Data -@EqualsAndHashCode(callSuper = false) @TableName("im_group") -public class Group extends Model { - - private static final long serialVersionUID = 1L; +public class Group { /** * id */ - @TableId(value = "id", type = IdType.AUTO) + @TableId private Long id; /** * 群名字 */ - @TableField("name") private String name; /** * 群主id */ - @TableField("owner_id") private Long ownerId; /** - * 头像 + * 群头像 */ - @TableField("head_image") private String headImage; /** - * 头像缩略图 + * 群头像缩略图 */ - @TableField("head_image_thumb") private String headImageThumb; /** * 群公告 */ - @TableField("notice") private String notice; /** - * 是否已删除 + * 是否被封禁 */ - @TableField("deleted") - private Boolean deleted; + private Boolean isBanned; + + /** + * 被封禁原因 + */ + private String reason; /** * 创建时间 */ - @TableField("created_time") private Date createdTime; + /** + * 是否已删除 + */ + private Boolean deleted; + - @Override - protected Serializable pkVal() { - return this.id; - } } diff --git a/im-platform/src/main/java/com/bx/implatform/entity/GroupMember.java b/im-platform/src/main/java/com/bx/implatform/entity/GroupMember.java index a7d362d..ef6d7a3 100644 --- a/im-platform/src/main/java/com/bx/implatform/entity/GroupMember.java +++ b/im-platform/src/main/java/com/bx/implatform/entity/GroupMember.java @@ -1,14 +1,11 @@ package com.bx.implatform.entity; -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.extension.activerecord.Model; import lombok.Data; import lombok.EqualsAndHashCode; -import java.io.Serializable; import java.util.Date; /** @@ -24,67 +21,51 @@ import java.util.Date; @TableName("im_group_member") public class GroupMember extends Model { - private static final long serialVersionUID = 1L; - /** * id */ - @TableId(value = "id", type = IdType.AUTO) + @TableId private Long id; /** * 群id */ - @TableField("group_id") private Long groupId; /** * 用户id */ - @TableField("user_id") private Long userId; /** - * 群内显示名称 + * 组内显示名称 */ - @TableField("alias_name") private String aliasName; /** - * 头像 + * 用户头像 */ - @TableField("head_image") private String headImage; - /** * 备注 */ - @TableField("remark") private String remark; /** - * 是否已离开群聊 + * 是否已退出 */ - @TableField("quit") private Boolean quit; - /** - * 退群时间 - */ - @TableField("quit_time") - private Date quitTime; - /** * 创建时间 */ - @TableField("created_time") private Date createdTime; + /** + * 退出时间 + */ + private Date quitTime; - @Override - protected Serializable pkVal() { - return this.id; - } } diff --git a/im-platform/src/main/java/com/bx/implatform/entity/GroupMessage.java b/im-platform/src/main/java/com/bx/implatform/entity/GroupMessage.java index ebe02d5..a0ec72e 100644 --- a/im-platform/src/main/java/com/bx/implatform/entity/GroupMessage.java +++ b/im-platform/src/main/java/com/bx/implatform/entity/GroupMessage.java @@ -1,14 +1,9 @@ package com.bx.implatform.entity; -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; -import com.baomidou.mybatisplus.extension.activerecord.Model; import lombok.Data; -import lombok.EqualsAndHashCode; -import java.io.Serializable; import java.util.Date; /** @@ -20,87 +15,68 @@ import java.util.Date; * @since 2022-10-31 */ @Data -@EqualsAndHashCode(callSuper = false) -@TableName("im_group_message") -public class GroupMessage extends Model { - private static final long serialVersionUID = 1L; +@TableName("im_group_message") +public class GroupMessage { /** * id */ - @TableId(value = "id", type = IdType.AUTO) + @TableId private Long id; /** * 群id */ - @TableField("group_id") private Long groupId; /** * 发送用户id */ - @TableField("send_id") private Long sendId; /** * 发送用户昵称 */ - @TableField("send_nick_name") private String sendNickName; /** * 接受用户id,为空表示全体发送 */ - @TableField("recv_ids") private String recvIds; /** * @用户列表 */ - @TableField("at_user_ids") private String atUserIds; /** * 发送内容 */ - @TableField("content") private String content; /** * 消息类型 MessageType */ - @TableField("type") private Integer type; /** * 是否回执消息 */ - @TableField("receipt") private Boolean receipt; /** * 回执消息是否完成 */ - @TableField("receipt_ok") private Boolean receiptOk; /** * 状态 MessageStatus */ - @TableField("status") private Integer status; /** * 发送时间 */ - @TableField("send_time") private Date sendTime; - - @Override - protected Serializable pkVal() { - return this.id; - } - } diff --git a/im-platform/src/main/java/com/bx/implatform/entity/PrivateMessage.java b/im-platform/src/main/java/com/bx/implatform/entity/PrivateMessage.java index 1914d2e..cc605f1 100644 --- a/im-platform/src/main/java/com/bx/implatform/entity/PrivateMessage.java +++ b/im-platform/src/main/java/com/bx/implatform/entity/PrivateMessage.java @@ -1,14 +1,8 @@ package com.bx.implatform.entity; -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; -import com.baomidou.mybatisplus.extension.activerecord.Model; import lombok.Data; -import lombok.EqualsAndHashCode; -import java.io.Serializable; import java.util.Date; /** @@ -20,59 +14,44 @@ import java.util.Date; * @since 2022-10-01 */ @Data -@EqualsAndHashCode(callSuper = false) @TableName("im_private_message") -public class PrivateMessage extends Model { - - private static final long serialVersionUID = 1L; +public class PrivateMessage { /** * id */ - @TableId(value = "id", type = IdType.AUTO) private Long id; /** * 发送用户id */ - @TableField("send_id") private Long sendId; /** * 接收用户id */ - @TableField("recv_id") private Long recvId; /** * 发送内容 */ - @TableField("content") private String content; /** - * 消息类型 0:文字 1:图片 2:文件 3:语音 10:撤回消息 + * 消息类型 MessageType */ - @TableField("type") private Integer type; /** * 状态 */ - @TableField("status") private Integer status; /** * 发送时间 */ - @TableField("send_time") private Date sendTime; - @Override - protected Serializable pkVal() { - return this.id; - } - } diff --git a/im-platform/src/main/java/com/bx/implatform/entity/User.java b/im-platform/src/main/java/com/bx/implatform/entity/User.java index 878310c..6767630 100644 --- a/im-platform/src/main/java/com/bx/implatform/entity/User.java +++ b/im-platform/src/main/java/com/bx/implatform/entity/User.java @@ -1,14 +1,9 @@ package com.bx.implatform.entity; -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; -import com.baomidou.mybatisplus.extension.activerecord.Model; import lombok.Data; -import lombok.EqualsAndHashCode; -import java.io.Serializable; import java.util.Date; /** @@ -20,93 +15,78 @@ import java.util.Date; * @since 2022-10-01 */ @Data -@EqualsAndHashCode(callSuper = false) @TableName("im_user") -public class User extends Model { - - private static final long serialVersionUID = 1L; +public class User { /** * id */ - @TableId(value = "id", type = IdType.AUTO) + @TableId private Long id; /** * 用户名 */ - @TableField("user_name") private String userName; /** - * 用户名 + * 用户昵称 */ - @TableField("nick_name") private String nickName; /** - * 性别 + * 用户头像 */ - @TableField("sex") - private Integer sex; + private String headImage; /** - * 头像 + * 用户头像缩略图 */ - @TableField("head_image") - private String headImage; + private String headImageThumb; /** - * 头像缩略图 + * 密码(明文) */ - @TableField("head_image_thumb") - private String headImageThumb; + private String password; /** - * 用户类型 1:普通用户 2:审核专用账户 + * 性别 0:男 1::女 */ - @TableField("type") - private Integer type; + private Integer sex; /** * 个性签名 */ - @TableField("signature") private String signature; - /** - * 密码 - */ - @TableField("password") - private String password; /** - * 是否被封禁 + * 账号是否被封禁 */ - @TableField("is_banned") private Boolean isBanned; /** - * 被封禁原因 + * 账号被封禁原因 */ - @TableField("reason") private String reason; /** * 最后登录时间 */ - @TableField("last_login_time") private Date lastLoginTime; /** - * 创建时间 + * 创建时间(注册时间) */ - @TableField("created_time") private Date createdTime; + /** + * 账号类型 1:普通用户 2:wx小程序审核账户 + */ + private Integer type; - @Override - protected Serializable pkVal() { - return this.id; - } + /** + * 客户端id,用于uni-push推送 + */ + private String cid; } diff --git a/im-platform/src/main/java/com/bx/implatform/filter/CacheFilter.java b/im-platform/src/main/java/com/bx/implatform/filter/CacheFilter.java index e33a6ff..6872796 100644 --- a/im-platform/src/main/java/com/bx/implatform/filter/CacheFilter.java +++ b/im-platform/src/main/java/com/bx/implatform/filter/CacheFilter.java @@ -1,11 +1,11 @@ package com.bx.implatform.filter; +import jakarta.servlet.*; +import jakarta.servlet.annotation.WebFilter; +import jakarta.servlet.http.HttpServletRequest; import org.springframework.boot.web.servlet.ServletComponentScan; import org.springframework.stereotype.Component; -import javax.servlet.*; -import javax.servlet.annotation.WebFilter; -import javax.servlet.http.HttpServletRequest; import java.io.IOException; @Component diff --git a/im-platform/src/main/java/com/bx/implatform/filter/CacheHttpServletRequestWrapper.java b/im-platform/src/main/java/com/bx/implatform/filter/CacheHttpServletRequestWrapper.java index 9101015..1e301a6 100644 --- a/im-platform/src/main/java/com/bx/implatform/filter/CacheHttpServletRequestWrapper.java +++ b/im-platform/src/main/java/com/bx/implatform/filter/CacheHttpServletRequestWrapper.java @@ -1,11 +1,11 @@ package com.bx.implatform.filter; +import jakarta.servlet.ReadListener; +import jakarta.servlet.ServletInputStream; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequestWrapper; import org.apache.commons.compress.utils.IOUtils; -import javax.servlet.ReadListener; -import javax.servlet.ServletInputStream; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletRequestWrapper; import java.io.*; public class CacheHttpServletRequestWrapper extends HttpServletRequestWrapper { diff --git a/im-platform/src/main/java/com/bx/implatform/generator/CodeGenerator.java b/im-platform/src/main/java/com/bx/implatform/generator/CodeGenerator.java index d0f423f..ea0099c 100644 --- a/im-platform/src/main/java/com/bx/implatform/generator/CodeGenerator.java +++ b/im-platform/src/main/java/com/bx/implatform/generator/CodeGenerator.java @@ -1,102 +1,102 @@ package com.bx.implatform.generator; -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.core.toolkit.StringPool; -import com.baomidou.mybatisplus.generator.AutoGenerator; -import com.baomidou.mybatisplus.generator.InjectionConfig; -import com.baomidou.mybatisplus.generator.config.*; -import com.baomidou.mybatisplus.generator.config.po.TableInfo; -import com.baomidou.mybatisplus.generator.config.rules.DateType; -import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy; -import com.baomidou.mybatisplus.generator.engine.VelocityTemplateEngine; - -import java.util.ArrayList; -import java.util.List; +//import com.baomidou.mybatisplus.annotation.IdType; +//import com.baomidou.mybatisplus.core.toolkit.StringPool; +//import com.baomidou.mybatisplus.generator.AutoGenerator; +//import com.baomidou.mybatisplus.generator.InjectionConfig; +//import com.baomidou.mybatisplus.generator.config.*; +//import com.baomidou.mybatisplus.generator.config.po.TableInfo; +//import com.baomidou.mybatisplus.generator.config.rules.DateType; +//import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy; +//import com.baomidou.mybatisplus.generator.engine.VelocityTemplateEngine; +// +//import java.util.ArrayList; +//import java.util.List; public class CodeGenerator { - public static void main(String[] args) { - // 代码生成器 - AutoGenerator mpg = new AutoGenerator(); - - // 全局配置 - GlobalConfig gc = new GlobalConfig(); - //生成的代码输出路径,自己根据需要修改 - String projectPath = "d:\\work\\project\\code"; - gc.setOutputDir(projectPath + "/src/main/java"); - gc.setAuthor("blue"); - gc.setOpen(false); - gc.setFileOverride(true); - gc.setActiveRecord(true); - gc.setBaseColumnList(true); - gc.setBaseResultMap(true); - gc.setIdType(IdType.AUTO); - gc.setDateType(DateType.ONLY_DATE); - mpg.setGlobalConfig(gc); - - // 数据源配置 - DataSourceConfig dsc = new DataSourceConfig(); - dsc.setUrl("jdbc:mysql://localhost:3306/box-im?useUnicode=true&characterEncoding=utf-8"); - dsc.setDriverName("com.mysql.jdbc.Driver"); - dsc.setUsername("root"); - dsc.setPassword("root"); - mpg.setDataSource(dsc); - - // 包配置 - PackageConfig pc = new PackageConfig(); - pc.setModuleName(""); - pc.setParent("com.bx"); - mpg.setPackageInfo(pc); - - // 如果模板引擎是 velocity - String templatePath = "/templates/mapper.xml.vm"; - - // 自定义输出配置 - List focList = new ArrayList<>(); - // 自定义配置会被优先输出 - focList.add(new FileOutConfig(templatePath) { - @Override - public String outputFile(TableInfo tableInfo) { - // 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!! - return projectPath + "/src/main/resources/mapper/" - + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML; - } - }); - - // 自定义配置 - InjectionConfig cfg = new InjectionConfig() { - @Override - public void initMap() { - // to do nothing - } - }; - cfg.setFileOutConfigList(focList); - mpg.setCfg(cfg); - - // 配置模板 - TemplateConfig templateConfig = new TemplateConfig(); - templateConfig.setXml(null); - mpg.setTemplate(templateConfig); - - // 策略配置 - StrategyConfig strategy = new StrategyConfig(); - // 下划线转驼峰 - strategy.setNaming(NamingStrategy.underline_to_camel); - strategy.setColumnNaming(NamingStrategy.underline_to_camel); - strategy.setEntityTableFieldAnnotationEnable(true); - strategy.setVersionFieldName("version"); - //逻辑删除的字段 - strategy.setLogicDeleteFieldName("deleted"); - strategy.setEntityLombokModel(true); - strategy.setRestControllerStyle(true); - - - //多张表的时候直接在代码中写表名 - strategy.setInclude("friends"); - strategy.setTablePrefix(""); - mpg.setStrategy(strategy); - - mpg.setTemplateEngine(new VelocityTemplateEngine()); - mpg.execute(); - } +// public static void main(String[] args) { +// // 代码生成器 +// AutoGenerator mpg = new AutoGenerator(); +// +// // 全局配置 +// GlobalConfig gc = new GlobalConfig(); +// //生成的代码输出路径,自己根据需要修改 +// String projectPath = "d:\\work\\project\\code"; +// gc.setOutputDir(projectPath + "/src/main/java"); +// gc.setAuthor("blue"); +// gc.setOpen(false); +// gc.setFileOverride(true); +// gc.setActiveRecord(true); +// gc.setBaseColumnList(true); +// gc.setBaseResultMap(true); +// gc.setIdType(IdType.AUTO); +// gc.setDateType(DateType.ONLY_DATE); +// mpg.setGlobalConfig(gc); +// +// // 数据源配置 +// DataSourceConfig dsc = new DataSourceConfig(); +// dsc.setUrl("jdbc:mysql://localhost:3306/box-im?useUnicode=true&characterEncoding=utf-8"); +// dsc.setDriverName("com.mysql.jdbc.Driver"); +// dsc.setUsername("root"); +// dsc.setPassword("root"); +// mpg.setDataSource(dsc); +// +// // 包配置 +// PackageConfig pc = new PackageConfig(); +// pc.setModuleName(""); +// pc.setParent("com.bx"); +// mpg.setPackageInfo(pc); +// +// // 如果模板引擎是 velocity +// String templatePath = "/templates/mapper.xml.vm"; +// +// // 自定义输出配置 +// List focList = new ArrayList<>(); +// // 自定义配置会被优先输出 +// focList.add(new FileOutConfig(templatePath) { +// @Override +// public String outputFile(TableInfo tableInfo) { +// // 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!! +// return projectPath + "/src/main/resources/mapper/" +// + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML; +// } +// }); +// +// // 自定义配置 +// InjectionConfig cfg = new InjectionConfig() { +// @Override +// public void initMap() { +// // to do nothing +// } +// }; +// cfg.setFileOutConfigList(focList); +// mpg.setCfg(cfg); +// +// // 配置模板 +// TemplateConfig templateConfig = new TemplateConfig(); +// templateConfig.setXml(null); +// mpg.setTemplate(templateConfig); +// +// // 策略配置 +// StrategyConfig strategy = new StrategyConfig(); +// // 下划线转驼峰 +// strategy.setNaming(NamingStrategy.underline_to_camel); +// strategy.setColumnNaming(NamingStrategy.underline_to_camel); +// strategy.setEntityTableFieldAnnotationEnable(true); +// strategy.setVersionFieldName("version"); +// //逻辑删除的字段 +// strategy.setLogicDeleteFieldName("deleted"); +// strategy.setEntityLombokModel(true); +// strategy.setRestControllerStyle(true); +// +// +// //多张表的时候直接在代码中写表名 +// strategy.setInclude("friends"); +// strategy.setTablePrefix(""); +// mpg.setStrategy(strategy); +// +// mpg.setTemplateEngine(new VelocityTemplateEngine()); +// mpg.execute(); +// } } diff --git a/im-platform/src/main/java/com/bx/implatform/interceptor/AuthInterceptor.java b/im-platform/src/main/java/com/bx/implatform/interceptor/AuthInterceptor.java index d964cd5..b3bd248 100644 --- a/im-platform/src/main/java/com/bx/implatform/interceptor/AuthInterceptor.java +++ b/im-platform/src/main/java/com/bx/implatform/interceptor/AuthInterceptor.java @@ -7,6 +7,8 @@ import com.bx.implatform.config.JwtProperties; import com.bx.implatform.enums.ResultCode; import com.bx.implatform.exception.GlobalException; import com.bx.implatform.session.UserSession; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.NotNull; @@ -14,9 +16,6 @@ import org.springframework.stereotype.Component; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerInterceptor; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - @Slf4j @Component @AllArgsConstructor diff --git a/im-platform/src/main/java/com/bx/implatform/interceptor/XssInterceptor.java b/im-platform/src/main/java/com/bx/implatform/interceptor/XssInterceptor.java index d747176..073b2c4 100644 --- a/im-platform/src/main/java/com/bx/implatform/interceptor/XssInterceptor.java +++ b/im-platform/src/main/java/com/bx/implatform/interceptor/XssInterceptor.java @@ -3,14 +3,14 @@ package com.bx.implatform.interceptor; import com.bx.implatform.enums.ResultCode; import com.bx.implatform.exception.GlobalException; import com.bx.implatform.util.XssUtil; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.NotNull; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import java.io.BufferedReader; import java.util.Map; diff --git a/im-platform/src/main/java/com/bx/implatform/listener/GroupMessageListener.java b/im-platform/src/main/java/com/bx/implatform/listener/GroupMessageListener.java index d927b99..25a31f6 100644 --- a/im-platform/src/main/java/com/bx/implatform/listener/GroupMessageListener.java +++ b/im-platform/src/main/java/com/bx/implatform/listener/GroupMessageListener.java @@ -5,9 +5,7 @@ import com.bx.imclient.listener.MessageListener; import com.bx.imcommon.enums.IMListenerType; import com.bx.imcommon.enums.IMSendCode; import com.bx.imcommon.model.IMSendResult; -import com.bx.implatform.contant.RedisKey; import com.bx.implatform.vo.GroupMessageVO; -import com.bx.implatform.vo.PrivateMessageVO; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.data.redis.core.RedisTemplate; diff --git a/im-platform/src/main/java/com/bx/implatform/listener/SystemMessageListener.java b/im-platform/src/main/java/com/bx/implatform/listener/SystemMessageListener.java index 2f22099..e97474d 100644 --- a/im-platform/src/main/java/com/bx/implatform/listener/SystemMessageListener.java +++ b/im-platform/src/main/java/com/bx/implatform/listener/SystemMessageListener.java @@ -1,24 +1,14 @@ package com.bx.implatform.listener; -import cn.hutool.core.collection.CollUtil; -import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import com.bx.imclient.annotation.IMListener; import com.bx.imclient.listener.MessageListener; import com.bx.imcommon.enums.IMListenerType; import com.bx.imcommon.enums.IMSendCode; import com.bx.imcommon.model.IMSendResult; -import com.bx.implatform.entity.PrivateMessage; -import com.bx.implatform.enums.MessageStatus; -import com.bx.implatform.service.IPrivateMessageService; -import com.bx.implatform.vo.PrivateMessageVO; import com.bx.implatform.vo.SystemMessageVO; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Lazy; -import java.util.HashSet; import java.util.List; -import java.util.Set; @Slf4j @IMListener(type = IMListenerType.SYSTEM_MESSAGE) diff --git a/im-platform/src/main/java/com/bx/implatform/service/IWebrtcGroupService.java b/im-platform/src/main/java/com/bx/implatform/service/IWebrtcGroupService.java index edef9e6..144a118 100644 --- a/im-platform/src/main/java/com/bx/implatform/service/IWebrtcGroupService.java +++ b/im-platform/src/main/java/com/bx/implatform/service/IWebrtcGroupService.java @@ -1,6 +1,5 @@ package com.bx.implatform.service; -import com.bx.implatform.config.WebrtcConfig; import com.bx.implatform.dto.*; import com.bx.implatform.vo.WebrtcGroupInfoVO; diff --git a/im-platform/src/main/java/com/bx/implatform/service/IWebrtcPrivateService.java b/im-platform/src/main/java/com/bx/implatform/service/IWebrtcPrivateService.java index 04dd4ad..dbf4944 100644 --- a/im-platform/src/main/java/com/bx/implatform/service/IWebrtcPrivateService.java +++ b/im-platform/src/main/java/com/bx/implatform/service/IWebrtcPrivateService.java @@ -1,9 +1,5 @@ package com.bx.implatform.service; -import com.bx.implatform.config.ICEServer; - -import java.util.List; - /** * webrtc 通信服务 * diff --git a/im-platform/src/main/java/com/bx/implatform/service/impl/GroupServiceImpl.java b/im-platform/src/main/java/com/bx/implatform/service/impl/GroupServiceImpl.java index c32de84..3055e25 100644 --- a/im-platform/src/main/java/com/bx/implatform/service/impl/GroupServiceImpl.java +++ b/im-platform/src/main/java/com/bx/implatform/service/impl/GroupServiceImpl.java @@ -18,7 +18,10 @@ import com.bx.implatform.enums.ResultCode; import com.bx.implatform.exception.GlobalException; import com.bx.implatform.mapper.GroupMapper; import com.bx.implatform.mapper.GroupMessageMapper; -import com.bx.implatform.service.*; +import com.bx.implatform.service.IFriendService; +import com.bx.implatform.service.IGroupMemberService; +import com.bx.implatform.service.IGroupService; +import com.bx.implatform.service.IUserService; import com.bx.implatform.session.SessionContext; import com.bx.implatform.session.UserSession; import com.bx.implatform.util.BeanUtils; @@ -40,7 +43,7 @@ import java.util.*; import java.util.stream.Collectors; @Slf4j -@CacheConfig(cacheNames = RedisKey.IM_CACHE_GROUP) +@CacheConfig(cacheManager = "cacheManager",cacheNames = RedisKey.IM_CACHE_GROUP) @Service @RequiredArgsConstructor public class GroupServiceImpl extends ServiceImpl implements IGroupService { diff --git a/im-platform/src/main/java/com/bx/implatform/service/thirdparty/FileService.java b/im-platform/src/main/java/com/bx/implatform/service/thirdparty/FileService.java index b727425..8baaaa6 100644 --- a/im-platform/src/main/java/com/bx/implatform/service/thirdparty/FileService.java +++ b/im-platform/src/main/java/com/bx/implatform/service/thirdparty/FileService.java @@ -9,6 +9,7 @@ import com.bx.implatform.util.FileUtil; import com.bx.implatform.util.ImageUtil; import com.bx.implatform.util.MinioUtil; import com.bx.implatform.vo.UploadImageVO; +import jakarta.annotation.PostConstruct; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; @@ -16,7 +17,6 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; -import javax.annotation.PostConstruct; import java.io.IOException; import java.util.Objects; diff --git a/im-platform/src/main/java/com/bx/implatform/session/SessionContext.java b/im-platform/src/main/java/com/bx/implatform/session/SessionContext.java index 9577d36..ce61dbb 100644 --- a/im-platform/src/main/java/com/bx/implatform/session/SessionContext.java +++ b/im-platform/src/main/java/com/bx/implatform/session/SessionContext.java @@ -1,10 +1,9 @@ package com.bx.implatform.session; +import jakarta.servlet.http.HttpServletRequest; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; -import javax.servlet.http.HttpServletRequest; - /* * @Description * @Author Blue diff --git a/im-platform/src/main/java/com/bx/implatform/session/WebrtcUserInfo.java b/im-platform/src/main/java/com/bx/implatform/session/WebrtcUserInfo.java index 5bae898..03c6b2d 100644 --- a/im-platform/src/main/java/com/bx/implatform/session/WebrtcUserInfo.java +++ b/im-platform/src/main/java/com/bx/implatform/session/WebrtcUserInfo.java @@ -1,7 +1,6 @@ package com.bx.implatform.session; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; /** @@ -10,20 +9,20 @@ import lombok.Data; * @version: 1.0 */ @Data -@ApiModel("用户信息") +@Schema(description = "用户信息") public class WebrtcUserInfo { - @ApiModelProperty(value = "用户id") + @Schema(description = "用户id") private Long id; - @ApiModelProperty(value = "用户昵称") + @Schema(description = "用户昵称") private String nickName; - @ApiModelProperty(value = "用户头像") + @Schema(description = "用户头像") private String headImage; - @ApiModelProperty(value = "是否开启摄像头") + @Schema(description = "是否开启摄像头") private Boolean isCamera; - @ApiModelProperty(value = "是否开启麦克风") + @Schema(description = "是否开启麦克风") private Boolean isMicroPhone; } diff --git a/im-platform/src/main/java/com/bx/implatform/task/UserBannedConsumerTask.java b/im-platform/src/main/java/com/bx/implatform/task/UserBannedConsumerTask.java index e112f9d..364e8e9 100644 --- a/im-platform/src/main/java/com/bx/implatform/task/UserBannedConsumerTask.java +++ b/im-platform/src/main/java/com/bx/implatform/task/UserBannedConsumerTask.java @@ -11,6 +11,7 @@ import com.bx.implatform.vo.SystemMessageVO; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; + import java.util.Collections; /** diff --git a/im-platform/src/main/java/com/bx/implatform/util/SensitiveFilterUtil.java b/im-platform/src/main/java/com/bx/implatform/util/SensitiveFilterUtil.java index bad68c1..2368d72 100644 --- a/im-platform/src/main/java/com/bx/implatform/util/SensitiveFilterUtil.java +++ b/im-platform/src/main/java/com/bx/implatform/util/SensitiveFilterUtil.java @@ -1,12 +1,12 @@ package com.bx.implatform.util; +import jakarta.annotation.PostConstruct; import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.CharUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; -import javax.annotation.PostConstruct; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; diff --git a/im-platform/src/main/java/com/bx/implatform/vo/FriendVO.java b/im-platform/src/main/java/com/bx/implatform/vo/FriendVO.java index 7a580c1..7b40fb4 100644 --- a/im-platform/src/main/java/com/bx/implatform/vo/FriendVO.java +++ b/im-platform/src/main/java/com/bx/implatform/vo/FriendVO.java @@ -1,24 +1,22 @@ package com.bx.implatform.vo; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; import lombok.Data; -import javax.validation.constraints.NotNull; - @Data -@ApiModel("好友信息VO") +@Schema(description = "好友信息VO") public class FriendVO { @NotNull(message = "好友id不可为空") - @ApiModelProperty(value = "好友id") + @Schema(description = "好友id") private Long id; @NotNull(message = "好友昵称不可为空") - @ApiModelProperty(value = "好友昵称") + @Schema(description = "好友昵称") private String nickName; - @ApiModelProperty(value = "好友头像") + @Schema(description = "好友头像") private String headImage; } diff --git a/im-platform/src/main/java/com/bx/implatform/vo/GroupInviteVO.java b/im-platform/src/main/java/com/bx/implatform/vo/GroupInviteVO.java index 1d1f38d..7c86962 100644 --- a/im-platform/src/main/java/com/bx/implatform/vo/GroupInviteVO.java +++ b/im-platform/src/main/java/com/bx/implatform/vo/GroupInviteVO.java @@ -1,22 +1,21 @@ package com.bx.implatform.vo; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; import lombok.Data; -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; import java.util.List; @Data -@ApiModel("邀请好友进群请求VO") +@Schema(description = "邀请好友进群请求VO") public class GroupInviteVO { @NotNull(message = "群id不可为空") - @ApiModelProperty(value = "群id") + @Schema(description = "群id") private Long groupId; @NotEmpty(message = "群id不可为空") - @ApiModelProperty(value = "好友id列表不可为空") + @Schema(description = "好友id列表不可为空") private List friendIds; } diff --git a/im-platform/src/main/java/com/bx/implatform/vo/GroupMemberVO.java b/im-platform/src/main/java/com/bx/implatform/vo/GroupMemberVO.java index f4db9ae..e5f4192 100644 --- a/im-platform/src/main/java/com/bx/implatform/vo/GroupMemberVO.java +++ b/im-platform/src/main/java/com/bx/implatform/vo/GroupMemberVO.java @@ -1,29 +1,28 @@ package com.bx.implatform.vo; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; @Data -@ApiModel("群成员信息VO") +@Schema(description = "群成员信息VO") public class GroupMemberVO { - @ApiModelProperty("用户id") + @Schema(description = "用户id") private Long userId; - @ApiModelProperty("群内显示名称") + @Schema(description = "群内显示名称") private String aliasName; - @ApiModelProperty("头像") + @Schema(description = "头像") private String headImage; - @ApiModelProperty("是否已退出") + @Schema(description = "是否已退出") private Boolean quit; - @ApiModelProperty(value = "是否在线") + @Schema(description = "是否在线") private Boolean online; - @ApiModelProperty("备注") + @Schema(description = "备注") private String remark; } diff --git a/im-platform/src/main/java/com/bx/implatform/vo/GroupMessageVO.java b/im-platform/src/main/java/com/bx/implatform/vo/GroupMessageVO.java index 377fd8a..ea41bde 100644 --- a/im-platform/src/main/java/com/bx/implatform/vo/GroupMessageVO.java +++ b/im-platform/src/main/java/com/bx/implatform/vo/GroupMessageVO.java @@ -2,8 +2,7 @@ package com.bx.implatform.vo; import com.bx.imcommon.serializer.DateToLongSerializer; import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.swagger.annotations.ApiModelProperty; -import io.swagger.models.auth.In; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import java.util.Date; @@ -12,40 +11,40 @@ import java.util.List; @Data public class GroupMessageVO { - @ApiModelProperty(value = "消息id") + @Schema(description = "消息id") private Long id; - @ApiModelProperty(value = "群聊id") + @Schema(description = "群聊id") private Long groupId; - @ApiModelProperty(value = " 发送者id") + @Schema(description = " 发送者id") private Long sendId; - @ApiModelProperty(value = " 发送者昵称") + @Schema(description = " 发送者昵称") private String sendNickName; - @ApiModelProperty(value = "消息内容") + @Schema(description = "消息内容") private String content; - @ApiModelProperty(value = "消息内容类型 具体枚举值由应用层定义") + @Schema(description = "消息内容类型 具体枚举值由应用层定义") private Integer type; - @ApiModelProperty(value = "是否回执消息") + @Schema(description = "是否回执消息") private Boolean receipt; - @ApiModelProperty(value = "回执消息是否完成") + @Schema(description = "回执消息是否完成") private Boolean receiptOk; - @ApiModelProperty(value = "已读消息数量") + @Schema(description = "已读消息数量") private Integer readedCount = 0; - @ApiModelProperty(value = "@用户列表") + @Schema(description = "@用户列表") private List atUserIds; - @ApiModelProperty(value = " 状态") + @Schema(description = " 状态") private Integer status; - @ApiModelProperty(value = "发送时间") + @Schema(description = "发送时间") @JsonSerialize(using = DateToLongSerializer.class) private Date sendTime; } diff --git a/im-platform/src/main/java/com/bx/implatform/vo/GroupVO.java b/im-platform/src/main/java/com/bx/implatform/vo/GroupVO.java index cbbbf14..4743b1a 100644 --- a/im-platform/src/main/java/com/bx/implatform/vo/GroupVO.java +++ b/im-platform/src/main/java/com/bx/implatform/vo/GroupVO.java @@ -1,50 +1,47 @@ package com.bx.implatform.vo; -import com.baomidou.mybatisplus.annotation.TableField; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; import lombok.Data; import org.hibernate.validator.constraints.Length; -import javax.validation.constraints.NotEmpty; - @Data -@ApiModel("群信息VO") +@Schema(description = "群信息VO") public class GroupVO { - @ApiModelProperty(value = "群id") + @Schema(description = "群id") private Long id; @Length(max = 20, message = "群名称长度不能大于20") @NotEmpty(message = "群名称不可为空") - @ApiModelProperty(value = "群名称") + @Schema(description = "群名称") private String name; - @ApiModelProperty(value = "群主id") + @Schema(description = "群主id") private Long ownerId; - @ApiModelProperty(value = "头像") + @Schema(description = "头像") private String headImage; - @ApiModelProperty(value = "头像缩略图") + @Schema(description = "头像缩略图") private String headImageThumb; @Length(max = 1024, message = "群聊显示长度不能大于1024") - @ApiModelProperty(value = "群公告") + @Schema(description = "群公告") private String notice; @Length(max = 20, message = "群聊显示长度不能大于20") - @ApiModelProperty(value = "用户在群显示昵称") + @Schema(description = "用户在群显示昵称") private String aliasName; @Length(max = 20, message = "群聊显示长度不能大于20") - @ApiModelProperty(value = "群聊显示备注") + @Schema(description = "群聊显示备注") private String remark; - @ApiModelProperty(value = "是否已删除") + @Schema(description = "是否已删除") private Boolean deleted; - @ApiModelProperty(value = "是否已退出") + @Schema(description = "是否已退出") private Boolean quit; diff --git a/im-platform/src/main/java/com/bx/implatform/vo/LoginVO.java b/im-platform/src/main/java/com/bx/implatform/vo/LoginVO.java index 1b765bc..ba9f9a2 100644 --- a/im-platform/src/main/java/com/bx/implatform/vo/LoginVO.java +++ b/im-platform/src/main/java/com/bx/implatform/vo/LoginVO.java @@ -1,23 +1,22 @@ package com.bx.implatform.vo; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; @Data -@ApiModel("用户登录VO") +@Schema(description = "用户登录VO") public class LoginVO { - @ApiModelProperty(value = "每次请求都必须在header中携带accessToken") + @Schema(description = "每次请求都必须在header中携带accessToken") private String accessToken; - @ApiModelProperty(value = "accessToken过期时间(秒)") + @Schema(description = "accessToken过期时间(秒)") private Integer accessTokenExpiresIn; - @ApiModelProperty(value = "accessToken过期后,通过refreshToken换取新的token") + @Schema(description = "accessToken过期后,通过refreshToken换取新的token") private String refreshToken; - @ApiModelProperty(value = "refreshToken过期时间(秒)") + @Schema(description = "refreshToken过期时间(秒)") private Integer refreshTokenExpiresIn; } diff --git a/im-platform/src/main/java/com/bx/implatform/vo/OnlineTerminalVO.java b/im-platform/src/main/java/com/bx/implatform/vo/OnlineTerminalVO.java index 67e5413..da40ec6 100644 --- a/im-platform/src/main/java/com/bx/implatform/vo/OnlineTerminalVO.java +++ b/im-platform/src/main/java/com/bx/implatform/vo/OnlineTerminalVO.java @@ -1,6 +1,6 @@ package com.bx.implatform.vo; -import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.AllArgsConstructor; import lombok.Data; @@ -15,10 +15,10 @@ import java.util.List; @AllArgsConstructor public class OnlineTerminalVO { - @ApiModelProperty(value = "用户id") + @Schema(description = "用户id") private Long userId; - @ApiModelProperty(value = "在线终端类型") + @Schema(description = "在线终端类型") private List terminals; } diff --git a/im-platform/src/main/java/com/bx/implatform/vo/PrivateMessageVO.java b/im-platform/src/main/java/com/bx/implatform/vo/PrivateMessageVO.java index c8ea8a5..608e854 100644 --- a/im-platform/src/main/java/com/bx/implatform/vo/PrivateMessageVO.java +++ b/im-platform/src/main/java/com/bx/implatform/vo/PrivateMessageVO.java @@ -2,35 +2,34 @@ package com.bx.implatform.vo; import com.bx.imcommon.serializer.DateToLongSerializer; import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import java.util.Date; @Data -@ApiModel("私聊消息VO") +@Schema(description = "私聊消息VO") public class PrivateMessageVO { - @ApiModelProperty(value = " 消息id") + @Schema(description = " 消息id") private Long id; - @ApiModelProperty(value = " 发送者id") + @Schema(description = " 发送者id") private Long sendId; - @ApiModelProperty(value = " 接收者id") + @Schema(description = " 接收者id") private Long recvId; - @ApiModelProperty(value = " 发送内容") + @Schema(description = " 发送内容") private String content; - @ApiModelProperty(value = "消息内容类型 IMCmdType") + @Schema(description = "消息内容类型 IMCmdType") private Integer type; - @ApiModelProperty(value = " 状态") + @Schema(description = " 状态") private Integer status; - @ApiModelProperty(value = " 发送时间") + @Schema(description = " 发送时间") @JsonSerialize(using = DateToLongSerializer.class) private Date sendTime; } diff --git a/im-platform/src/main/java/com/bx/implatform/vo/SystemConfigVO.java b/im-platform/src/main/java/com/bx/implatform/vo/SystemConfigVO.java index f8e936e..aa92709 100644 --- a/im-platform/src/main/java/com/bx/implatform/vo/SystemConfigVO.java +++ b/im-platform/src/main/java/com/bx/implatform/vo/SystemConfigVO.java @@ -1,8 +1,7 @@ package com.bx.implatform.vo; import com.bx.implatform.config.WebrtcConfig; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.AllArgsConstructor; import lombok.Data; @@ -12,11 +11,11 @@ import lombok.Data; * @version: 1.0 */ @Data -@ApiModel("系统配置VO") +@Schema(description = "系统配置VO") @AllArgsConstructor public class SystemConfigVO { - @ApiModelProperty(value = "webrtc配置") + @Schema(description = "webrtc配置") private WebrtcConfig webrtc; } diff --git a/im-platform/src/main/java/com/bx/implatform/vo/SystemMessageVO.java b/im-platform/src/main/java/com/bx/implatform/vo/SystemMessageVO.java index 692b6e1..d1ee164 100644 --- a/im-platform/src/main/java/com/bx/implatform/vo/SystemMessageVO.java +++ b/im-platform/src/main/java/com/bx/implatform/vo/SystemMessageVO.java @@ -2,26 +2,25 @@ package com.bx.implatform.vo; import com.bx.imcommon.serializer.DateToLongSerializer; import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import java.util.Date; @Data -@ApiModel("系统消息VO") +@Schema(description = "系统消息VO") public class SystemMessageVO { - @ApiModelProperty(value = " 消息id") + @Schema(description = " 消息id") private Long id; - @ApiModelProperty(value = " 发送内容") + @Schema(description = " 发送内容") private String content; - @ApiModelProperty(value = "消息内容类型 MessageType") + @Schema(description = "消息内容类型 MessageType") private Integer type; - @ApiModelProperty(value = " 发送时间") + @Schema(description = " 发送时间") @JsonSerialize(using = DateToLongSerializer.class) private Date sendTime; } diff --git a/im-platform/src/main/java/com/bx/implatform/vo/UploadImageVO.java b/im-platform/src/main/java/com/bx/implatform/vo/UploadImageVO.java index 3c63709..f58114f 100644 --- a/im-platform/src/main/java/com/bx/implatform/vo/UploadImageVO.java +++ b/im-platform/src/main/java/com/bx/implatform/vo/UploadImageVO.java @@ -1,16 +1,15 @@ package com.bx.implatform.vo; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; @Data -@ApiModel("图片上传VO") +@Schema(description = "图片上传VO") public class UploadImageVO { - @ApiModelProperty(value = "原图") + @Schema(description = "原图") private String originUrl; - @ApiModelProperty(value = "缩略图") + @Schema(description = "缩略图") private String thumbUrl; } diff --git a/im-platform/src/main/java/com/bx/implatform/vo/UserVO.java b/im-platform/src/main/java/com/bx/implatform/vo/UserVO.java index f44d5aa..ae8345b 100644 --- a/im-platform/src/main/java/com/bx/implatform/vo/UserVO.java +++ b/im-platform/src/main/java/com/bx/implatform/vo/UserVO.java @@ -1,48 +1,46 @@ package com.bx.implatform.vo; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; import lombok.Data; import org.hibernate.validator.constraints.Length; -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; - @Data -@ApiModel("用户信息VO") +@Schema(description = "用户信息VO") public class UserVO { @NotNull(message = "用户id不能为空") - @ApiModelProperty(value = "id") + @Schema(description = "id") private Long id; @NotEmpty(message = "用户名不能为空") @Length(max = 64, message = "用户名不能大于64字符") - @ApiModelProperty(value = "用户名") + @Schema(description = "用户名") private String userName; @NotEmpty(message = "用户昵称不能为空") @Length(max = 64, message = "昵称不能大于64字符") - @ApiModelProperty(value = "用户昵称") + @Schema(description = "用户昵称") private String nickName; - @ApiModelProperty(value = "性别") + @Schema(description = "性别") private Integer sex; - @ApiModelProperty(value = "用户类型 1:普通用户 2:审核账户") + @Schema(description = "用户类型 1:普通用户 2:审核账户") private Integer type; @Length(max = 1024, message = "个性签名不能大于1024个字符") - @ApiModelProperty(value = "个性签名") + @Schema(description = "个性签名") private String signature; - @ApiModelProperty(value = "头像") + @Schema(description = "头像") private String headImage; - @ApiModelProperty(value = "头像缩略图") + @Schema(description = "头像缩略图") private String headImageThumb; - @ApiModelProperty(value = "是否在线") + @Schema(description = "是否在线") private Boolean online; } diff --git a/im-platform/src/main/java/com/bx/implatform/vo/WebrtcGroupFailedVO.java b/im-platform/src/main/java/com/bx/implatform/vo/WebrtcGroupFailedVO.java index 5c7b988..7cd4482 100644 --- a/im-platform/src/main/java/com/bx/implatform/vo/WebrtcGroupFailedVO.java +++ b/im-platform/src/main/java/com/bx/implatform/vo/WebrtcGroupFailedVO.java @@ -1,7 +1,6 @@ package com.bx.implatform.vo; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import java.util.List; @@ -12,12 +11,12 @@ import java.util.List; * @version: 1.0 */ @Data -@ApiModel("用户加入群通话失败VO") +@Schema(description = "用户加入群通话失败VO") public class WebrtcGroupFailedVO { - @ApiModelProperty(value = "失败用户列表") + @Schema(description = "失败用户列表") private List userIds; - @ApiModelProperty(value = "失败原因") + @Schema(description = "失败原因") private String reason; } diff --git a/im-platform/src/main/java/com/bx/implatform/vo/WebrtcGroupInfoVO.java b/im-platform/src/main/java/com/bx/implatform/vo/WebrtcGroupInfoVO.java index bf27527..7bed11f 100644 --- a/im-platform/src/main/java/com/bx/implatform/vo/WebrtcGroupInfoVO.java +++ b/im-platform/src/main/java/com/bx/implatform/vo/WebrtcGroupInfoVO.java @@ -1,8 +1,7 @@ package com.bx.implatform.vo; import com.bx.implatform.session.WebrtcUserInfo; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import java.util.List; @@ -13,16 +12,16 @@ import java.util.List; * @version: 1.0 */ @Data -@ApiModel("群通话信息VO") +@Schema(description = "群通话信息VO") public class WebrtcGroupInfoVO { - @ApiModelProperty(value = "是否在通话中") + @Schema(description = "是否在通话中") private Boolean isChating; - @ApiModelProperty(value = "通话发起人") + @Schema(description = "通话发起人") WebrtcUserInfo host; - @ApiModelProperty(value = "通话用户列表") + @Schema(description = "通话用户列表") private List userInfos; } diff --git a/im-platform/src/main/resources/application.yml b/im-platform/src/main/resources/application.yml index 8e0cbe8..0e70e38 100644 --- a/im-platform/src/main/resources/application.yml +++ b/im-platform/src/main/resources/application.yml @@ -7,7 +7,7 @@ spring: pathmatch: matching-strategy: ant_path_matcher datasource: - driver-class-name: com.mysql.jdbc.Driver + driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/box-im?useSSL=false&useUnicode=true&characterEncoding=utf-8&allowPublicKeyRetrieval=true username: root password: root @@ -22,12 +22,13 @@ spring: max-request-size: 50MB mybatis-plus: + global-config: + db-config: + id-type: AUTO # ID自增 configuration: - # 是否开启自动驼峰命名规则 - map-underscore-to-camel-case: false + map-underscore-to-camel-case: true #开启自动驼峰命名规则 log-impl: org.apache.ibatis.logging.stdout.StdOutImpl - mapper-locations: - - classpath*:mapper/*.xml + minio: endpoint: http://127.0.0.1:9001 #内网地址 public: http://127.0.0.1:9001 #外网访问地址 diff --git a/im-server/pom.xml b/im-server/pom.xml index e5d0e78..3b6fe90 100644 --- a/im-server/pom.xml +++ b/im-server/pom.xml @@ -18,10 +18,6 @@ im-commom 2.0.0 - - org.springframework.boot - spring-boot - io.netty netty-all @@ -40,7 +36,7 @@ org.springframework.boot spring-boot-maven-plugin - 2.0.3.RELEASE + 3.3.1 diff --git a/im-server/src/main/java/com/bx/imserver/config/RedisConfig.java b/im-server/src/main/java/com/bx/imserver/config/RedisConfig.java deleted file mode 100644 index 844cdf5..0000000 --- a/im-server/src/main/java/com/bx/imserver/config/RedisConfig.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.bx.imserver.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 -public class RedisConfig { - - @Bean - 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-server/src/main/java/com/bx/imserver/netty/IMServerGroup.java b/im-server/src/main/java/com/bx/imserver/netty/IMServerGroup.java index c498fd1..1498d18 100644 --- a/im-server/src/main/java/com/bx/imserver/netty/IMServerGroup.java +++ b/im-server/src/main/java/com/bx/imserver/netty/IMServerGroup.java @@ -1,13 +1,13 @@ package com.bx.imserver.netty; import com.bx.imcommon.contant.IMRedisKey; +import jakarta.annotation.PreDestroy; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.CommandLineRunner; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; -import javax.annotation.PreDestroy; import java.util.List; @Slf4j @@ -17,7 +17,7 @@ public class IMServerGroup implements CommandLineRunner { public static volatile long serverId = 0; - RedisTemplate redisTemplate; + private final RedisTemplate redisTemplate; private final List imServers; @@ -36,7 +36,7 @@ public class IMServerGroup implements CommandLineRunner { } @Override - public void run(String... args) throws Exception { + public void run(String... args) { // 初始化SERVER_ID String key = IMRedisKey.IM_MAX_SERVER_ID; serverId = redisTemplate.opsForValue().increment(key, 1); diff --git a/pom.xml b/pom.xml index c3202b5..f5511d8 100644 --- a/pom.xml +++ b/pom.xml @@ -8,6 +8,11 @@ com.bx pom 2.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.3.1 + im-platform @@ -17,34 +22,24 @@ - 1.8 - 1.8 - 1.8 UTF-8 UTF-8 - 1.8 - 3.4.0 - 1.2.40 - 1.7 - 2.7.0 - 5.8.16 + 17 + 3.5.7 + 1.2.83 + 4.5.0 + 5.8.28 1.1.22 + 8.5.1 + 3.21.3 3.8.1 - 1.18.16 - 5.1.46 + 1.18.32 + 3.0.3 - - - org.springframework.boot - spring-boot-dependencies - 2.7.17 - pom - import - org.projectlombok lombok @@ -70,31 +65,11 @@ druid ${druid.version} - - org.apache.velocity - velocity - ${velocity.version} - - - io.springfox - springfox-swagger2 - ${swagger.version} - - - io.springfox - springfox-swagger-ui - ${swagger.version} - cn.hutool hutool-all ${hutool.version} - - mysql - mysql-connector-java - ${mysql.version} - org.apache.poi poi-ooxml From 1c98ccf3f98f76543494dca82ea24ab09b23d189 Mon Sep 17 00:00:00 2001 From: xsx <825657193@qq.com> Date: Thu, 18 Jul 2024 10:41:12 +0800 Subject: [PATCH 06/48] =?UTF-8?q?=E6=95=B4=E7=90=86swagger=E9=85=8D?= =?UTF-8?q?=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- im-client/pom.xml | 4 +- im-commom/pom.xml | 9 +---- im-platform/pom.xml | 13 +------ .../com/bx/implatform/config/MvcConfig.java | 11 ++---- .../bx/implatform/config/SwaggerConfig.java | 6 +-- .../controller/LoginController.java | 2 +- .../implatform/controller/UserController.java | 2 +- .../controller/WebrtcGroupController.java | 2 +- .../controller/WebrtcPrivateController.java | 2 +- .../interceptor/AuthInterceptor.java | 2 +- .../src/main/resources/static/favicon.ico | Bin 0 -> 63432 bytes im-server/pom.xml | 11 +----- pom.xml | 35 ++---------------- 13 files changed, 23 insertions(+), 76 deletions(-) create mode 100644 im-platform/src/main/resources/static/favicon.ico diff --git a/im-client/pom.xml b/im-client/pom.xml index 774bafb..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,7 +15,7 @@ com.bx im-commom - 2.0.0 + 3.0.0 \ No newline at end of file diff --git a/im-commom/pom.xml b/im-commom/pom.xml index a019f36..92599d1 100644 --- a/im-commom/pom.xml +++ b/im-commom/pom.xml @@ -5,16 +5,11 @@ box-im com.bx - 2.0.0 + 3.0.0 4.0.0 im-commom - - - 8 - 8 - - + jar org.projectlombok diff --git a/im-platform/pom.xml b/im-platform/pom.xml index 05b2590..65ef8ab 100644 --- a/im-platform/pom.xml +++ b/im-platform/pom.xml @@ -5,18 +5,16 @@ box-im com.bx - 2.0.0 + 3.0.0 4.0.0 - im-platform - com.bx im-client - 2.0.0 + 3.0.0 org.springframework.boot @@ -53,11 +51,6 @@ org.aspectj aspectjweaver - - - org.springframework.boot - spring-boot-starter-data-redis - org.springframework.security spring-security-crypto @@ -99,14 +92,12 @@ ${knife4j.version} - ${project.artifactId} org.springframework.boot spring-boot-maven-plugin - 3.3.1 diff --git a/im-platform/src/main/java/com/bx/implatform/config/MvcConfig.java b/im-platform/src/main/java/com/bx/implatform/config/MvcConfig.java index 4ee1d19..2dee3c9 100644 --- a/im-platform/src/main/java/com/bx/implatform/config/MvcConfig.java +++ b/im-platform/src/main/java/com/bx/implatform/config/MvcConfig.java @@ -19,13 +19,10 @@ public class MvcConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { - registry.addInterceptor(xssInterceptor) - .addPathPatterns("/**") - .excludePathPatterns("/error"); - registry.addInterceptor(authInterceptor) - .addPathPatterns("/**") - .excludePathPatterns("/login", "/logout", "/register", "/refreshToken", - "/swagger-resources/**", "/webjars/**", "/v2/**", "/swagger-ui.html/**"); + registry.addInterceptor(xssInterceptor).addPathPatterns("/**").excludePathPatterns("/error"); + registry.addInterceptor(authInterceptor).addPathPatterns("/**") + .excludePathPatterns("/login", "/logout", "/register", "/refreshToken", "/swagger/**", "/v3/api-docs/**", + "/swagger-resources/**", "/swagger-ui.html", "/swagger-ui/**", "/doc.html"); } @Bean diff --git a/im-platform/src/main/java/com/bx/implatform/config/SwaggerConfig.java b/im-platform/src/main/java/com/bx/implatform/config/SwaggerConfig.java index 5c5841f..9ab109a 100644 --- a/im-platform/src/main/java/com/bx/implatform/config/SwaggerConfig.java +++ b/im-platform/src/main/java/com/bx/implatform/config/SwaggerConfig.java @@ -16,7 +16,7 @@ public class SwaggerConfig { public GroupedOpenApi userApi() { String[] paths = {"/**"}; String[] packagedToMatch = {"com.bx"}; - return GroupedOpenApi.builder().group("BoxIM") + return GroupedOpenApi.builder().group("IM-Platform") .pathsToMatch(paths) .packagesToScan(packagedToMatch).build(); } @@ -26,8 +26,8 @@ public class SwaggerConfig { Contact contact = new Contact(); contact.setName("Blue"); return new OpenAPI().info(new Info() - .title("Box-IM") - .description("盒子IM") + .title("盒子IM") + .description("盒子IM业务平台服务") .contact(contact) .version("3.0") .termsOfService("https://www.boxim.online") diff --git a/im-platform/src/main/java/com/bx/implatform/controller/LoginController.java b/im-platform/src/main/java/com/bx/implatform/controller/LoginController.java index 49c0e34..840838f 100644 --- a/im-platform/src/main/java/com/bx/implatform/controller/LoginController.java +++ b/im-platform/src/main/java/com/bx/implatform/controller/LoginController.java @@ -13,7 +13,7 @@ import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; -@Tag(name = "用户登录和注册") +@Tag(name = "注册登陆") @RestController @RequiredArgsConstructor public class LoginController { diff --git a/im-platform/src/main/java/com/bx/implatform/controller/UserController.java b/im-platform/src/main/java/com/bx/implatform/controller/UserController.java index dd6c133..422298f 100644 --- a/im-platform/src/main/java/com/bx/implatform/controller/UserController.java +++ b/im-platform/src/main/java/com/bx/implatform/controller/UserController.java @@ -18,7 +18,7 @@ import org.springframework.web.bind.annotation.*; import java.util.List; -@Tag(name = "用户") +@Tag(name = "用户相关") @RestController @RequestMapping("/user") @RequiredArgsConstructor diff --git a/im-platform/src/main/java/com/bx/implatform/controller/WebrtcGroupController.java b/im-platform/src/main/java/com/bx/implatform/controller/WebrtcGroupController.java index fa3bf62..2639da2 100644 --- a/im-platform/src/main/java/com/bx/implatform/controller/WebrtcGroupController.java +++ b/im-platform/src/main/java/com/bx/implatform/controller/WebrtcGroupController.java @@ -16,7 +16,7 @@ import org.springframework.web.bind.annotation.*; * @date: 2024-06-01 * @version: 1.0 */ -@Tag(name = "webrtc视频多人通话") +@Tag(name = "多人通话") @RestController @RequestMapping("/webrtc/group") @RequiredArgsConstructor diff --git a/im-platform/src/main/java/com/bx/implatform/controller/WebrtcPrivateController.java b/im-platform/src/main/java/com/bx/implatform/controller/WebrtcPrivateController.java index c0d4728..9ae38e8 100644 --- a/im-platform/src/main/java/com/bx/implatform/controller/WebrtcPrivateController.java +++ b/im-platform/src/main/java/com/bx/implatform/controller/WebrtcPrivateController.java @@ -9,7 +9,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; -@Tag(name = "webrtc视频单人通话") +@Tag(name = "单人通话") @RestController @RequestMapping("/webrtc/private") @RequiredArgsConstructor diff --git a/im-platform/src/main/java/com/bx/implatform/interceptor/AuthInterceptor.java b/im-platform/src/main/java/com/bx/implatform/interceptor/AuthInterceptor.java index b3bd248..3cd0f43 100644 --- a/im-platform/src/main/java/com/bx/implatform/interceptor/AuthInterceptor.java +++ b/im-platform/src/main/java/com/bx/implatform/interceptor/AuthInterceptor.java @@ -37,7 +37,7 @@ public class AuthInterceptor implements HandlerInterceptor { } String strJson = JwtUtil.getInfo(token); UserSession userSession = JSON.parseObject(strJson, UserSession.class); - //验证 token + // 验证 token if (!JwtUtil.checkSign(token, jwtProperties.getAccessTokenSecret())) { log.error("token已失效,用户:{}", userSession.getUserName()); log.error("token:{}", token); diff --git a/im-platform/src/main/resources/static/favicon.ico b/im-platform/src/main/resources/static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..8cd39d48604f2b9c7b6ab493cd040440d2bdd9b6 GIT binary patch literal 63432 zcmX6^WmsEXvxVT+B1MWzk>XBqX>oUV_hJExI|O&v7AQ_|FNEM)+}&Cn3IxA-zxyM} zNq%HMbN1{rYt~v5qoyK@gGq{sfPjD_FZV?Q0Rhnp{?b54h5s_}sWb`xf#{(jD}_)y zN&W}^1=&_oSrP#OoQU;gj)H)2=Pv(6Qp?BW*;DJ{*ckqpxMK}%R6_y3oB2E=0xyoXJ+ zgzYXrW`44`YsVsWm2`ZAh+SQhB+`!sGFt-l3m7Y=uAdU;u5-!+gX-j@YjG+N+@)(3 z+=7=ZkE`EaXo}8>XdimyAT%Pil_8SQQ|lpv=Dsn*@-!L&g>LhG@bzbl8Y|fW4)BeeQ2mw4lkdWt3cH z${Y-TmqH(e_;mLg1qU0T?EQ@{yg}ZSv9J%bC-riwQ7?$HKV9UTu#`f`LvVDU$3N(* z^+Shz19b_as+uJnULzl)P+Cf4NhsSfjuNDyB>w=B$Wa8pkwQIcu4zA1WL ze8-DieluZ5AJ~$W@%u-y7S>pFFE_?Q#zYbyziI9{slQ$kzGrh&**!4^q!D@7UQg>S zp-E_CRIiZ|K%Lu;sZ}O|OLxy91(RBL4-~yyKuuRbPsfkmu<=dIg&x;ia_lZgr$V+7 z<1DKwkr(&Tx2t(j!f(`DIeXgDiGSdHVE7&`6;|5ffGIHPDeWp;jC$h5dw(V zVnAr^9bc4Wi8Lp~Sds3DlyFK%jxJqiZ#7nC9HXC|_eTxM{L6Bgb;yM@QTV}(!@=OI z24aJ}S&L_-E56AqHZFB8g8(BZLE6AaWB77h|64AX70Jzr=-5H+1Maawls%DO5%Gcp zX9q^lDJPclG$!(K36)NtY&wcai*|}0O+#6G!#e0#>RK`i*{uH+1(h0YB~VCq_>yLSy~G=#Hs$ZL}6bJ8h;k`k)EruqV&F zL{A^*)jDNOQ)JMe)@rFjBxpRc#Y;EtjwMI6$nsZpIu;vYZAttDlj!uBI11gSs+=hl zo1R{jgWxZwNnEkw%ijsNxkK@tg%mDu-7Th#BR&)C8I}A6->qZOyU-@q{yiwVYD-5BtgV! zf|FE%k5P`G->ZL7Sc%1i_305M@1Eg>7Eusutgz^rb&8R7O#H|3%k}gPP2dU16Om-Q ziT^l$Hcf0$`iE4I5z&8lc!Pk31dI70St)XRYJqyoIa2*3Oy@5nZr$(dt~N%4k?7-* zaEw6!l>Tj^P@!$7r@Qbw+|IwDmk~=x2n`yZ;jcALOh4c|{4Pf1jLpjf?y+<7`LfgJfxG2ZXij9@{FhQ(K^ul~q0BTwT zY3-#%pE4FO;L<5kS4)pjUyufrX&cqq50U~58OQw_uLxrOAk5K)OP?3UiLt1$aL3xQ zAp3s-m#c^ke(wl5feDJW*y@4O>q7!~gkRr82k8e3P7T@Xg^*CG>g5DwA&3?^7I<*h z;K3j~5(Q4}gYo7q%r0>tBL_D_H)-Z8&Dhg|Pm-R;)VSXh##>N^Z+cm4>4t9&ZNP~V zt13E1id#5%;m=UseA&?YHpXyIOfp~si-exmq%oLldZ%x1wqvWtVZcR`UA%)?;)I;= z({)wl;SbhGq{*Bsj!g7vL4aFm;=nJFTKg9of{p8rGm4$qq5Eg_aRdCUQN ?YgHi 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

Ql>GY{n7Hom- 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

f2G1 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 literal 0 HcmV?d00001 diff --git a/im-server/pom.xml b/im-server/pom.xml index 3b6fe90..2e6edbf 100644 --- a/im-server/pom.xml +++ b/im-server/pom.xml @@ -5,7 +5,7 @@ box-im com.bx - 2.0.0 + 3.0.0 4.0.0 @@ -16,27 +16,20 @@ com.bx im-commom - 2.0.0 + 3.0.0 io.netty netty-all 4.1.42.Final - - - org.springframework.boot - spring-boot-starter-data-redis - - ${project.artifactId} org.springframework.boot spring-boot-maven-plugin - 3.3.1 diff --git a/pom.xml b/pom.xml index f5511d8..a00400d 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ box-im com.bx pom - 2.0.0 + 3.0.0 org.springframework.boot spring-boot-starter-parent @@ -70,37 +70,6 @@ hutool-all ${hutool.version} - - org.apache.poi - poi-ooxml - 4.1.2 - - - commons-io - commons-io - 2.6 - - - dom4j - dom4j - 1.6.1 - - - org.codehaus.jackson - jackson-mapper-asl - 1.9.13 - - - javax.interceptor - javax.interceptor-api - 1.2 - - - org.springframework.security - spring-security-jwt - 1.0.10.RELEASE - - @@ -116,4 +85,6 @@ + + \ No newline at end of file From 7b5e07f5fd2eed3858fd9efdf64f378be82d3a39 Mon Sep 17 00:00:00 2001 From: xsx <825657193@qq.com> Date: Thu, 18 Jul 2024 10:47:07 +0800 Subject: [PATCH 07/48] =?UTF-8?q?im-client=E8=87=AA=E5=8A=A8=E8=A3=85?= =?UTF-8?q?=E9=85=8D=E9=80=82=E9=85=8Dspringboot3.x=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/bx/imclient/IMAutoConfiguration.java | 2 +- im-client/src/main/resources/META-INF/spring.factories | 2 -- ...springframework.boot.autoconfigure.AutoConfiguration.imports | 1 + im-platform/src/main/java/com/bx/implatform/IMPlatformApp.java | 1 - 4 files changed, 2 insertions(+), 4 deletions(-) delete mode 100644 im-client/src/main/resources/META-INF/spring.factories create mode 100644 im-client/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 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/resources/META-INF/spring.factories b/im-client/src/main/resources/META-INF/spring.factories deleted file mode 100644 index 801458e..0000000 --- a/im-client/src/main/resources/META-INF/spring.factories +++ /dev/null @@ -1,2 +0,0 @@ -org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ -com.bx.imclient.IMAutoConfiguration \ No newline at end of file diff --git a/im-client/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/im-client/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..fe31de9 --- /dev/null +++ b/im-client/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +com.bx.imclient.IMAutoConfiguration \ No newline at end of file diff --git a/im-platform/src/main/java/com/bx/implatform/IMPlatformApp.java b/im-platform/src/main/java/com/bx/implatform/IMPlatformApp.java index 73dab5d..2b8a91c 100644 --- a/im-platform/src/main/java/com/bx/implatform/IMPlatformApp.java +++ b/im-platform/src/main/java/com/bx/implatform/IMPlatformApp.java @@ -11,7 +11,6 @@ import org.springframework.context.annotation.EnableAspectJAutoProxy; @Slf4j @EnableAspectJAutoProxy(exposeProxy = true) -@ComponentScan(basePackages = {"com.bx"}) @MapperScan(basePackages = {"com.bx.implatform.mapper"}) @SpringBootApplication public class IMPlatformApp { From 238f581dffc975920134c4ba2d111ffede9809a4 Mon Sep 17 00:00:00 2001 From: xsx <825657193@qq.com> Date: Fri, 19 Jul 2024 00:15:48 +0800 Subject: [PATCH 08/48] =?UTF-8?q?feat:=20=E7=BE=A4=E7=BB=84=E5=B0=81?= =?UTF-8?q?=E7=A6=81-=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- db/im-platfrom.sql | 4 +- .../bx/implatform/config/SwaggerConfig.java | 2 +- .../com/bx/implatform/contant/RedisKey.java | 2 +- .../controller/FriendController.java | 6 +- .../controller/GroupController.java | 10 +-- .../controller/GroupMessageController.java | 10 +-- .../controller/PrivateMessageController.java | 14 ++-- .../implatform/controller/UserController.java | 2 +- .../controller/WebrtcPrivateController.java | 16 ++-- .../bx/implatform/service/IGroupService.java | 2 +- .../service/impl/FriendServiceImpl.java | 13 ++-- .../service/impl/GroupMessageServiceImpl.java | 27 +++---- .../service/impl/GroupServiceImpl.java | 41 +++++----- .../impl/PrivateMessageServiceImpl.java | 10 +-- .../service/impl/UserServiceImpl.java | 4 +- .../service/impl/WebrtcGroupServiceImpl.java | 56 +++++++------ .../task/GroupBannedConsumerTask.java | 78 +++++++++++++++++++ .../task/GroupUnbanConsumerTask.java | 70 +++++++++++++++++ .../src/main/resources/application.yml | 8 +- im-server/src/main/resources/application.yml | 8 +- im-ui/src/components/setting/Setting.vue | 4 +- 21 files changed, 265 insertions(+), 122 deletions(-) create mode 100644 im-platform/src/main/java/com/bx/implatform/task/GroupBannedConsumerTask.java create mode 100644 im-platform/src/main/java/com/bx/implatform/task/GroupUnbanConsumerTask.java diff --git a/db/im-platfrom.sql b/db/im-platfrom.sql index a5b44a9..6959176 100644 --- a/db/im-platfrom.sql +++ b/db/im-platfrom.sql @@ -8,7 +8,7 @@ create table `im_user`( `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) comment '被封禁原因', + `reason` varchar(255) default '' comment '被封禁原因', `type` smallint default 1 comment '用户类型 1:普通用户 2:审核账户', `signature` varchar(1024) default '' comment '个性签名', `last_login_time` datetime DEFAULT null comment '最后登录时间', @@ -48,7 +48,7 @@ create table `im_group`( `head_image_thumb` varchar(255) default '' comment '群头像缩略图', `notice` varchar(1024) default '' comment '群公告', `is_banned` tinyint(1) default 0 comment '是否被封禁 0:否 1:是', - `reason` varchar(255) comment '被封禁原因', + `reason` varchar(255) default '' comment '被封禁原因', `deleted` tinyint(1) default 0 comment '是否已删除', `created_time` datetime default CURRENT_TIMESTAMP comment '创建时间' )ENGINE=InnoDB CHARSET=utf8mb3 comment '群'; diff --git a/im-platform/src/main/java/com/bx/implatform/config/SwaggerConfig.java b/im-platform/src/main/java/com/bx/implatform/config/SwaggerConfig.java index 9ab109a..3b2ae2a 100644 --- a/im-platform/src/main/java/com/bx/implatform/config/SwaggerConfig.java +++ b/im-platform/src/main/java/com/bx/implatform/config/SwaggerConfig.java @@ -26,7 +26,7 @@ public class SwaggerConfig { Contact contact = new Contact(); contact.setName("Blue"); return new OpenAPI().info(new Info() - .title("盒子IM") + .title("盒子IM接口文档") .description("盒子IM业务平台服务") .contact(contact) .version("3.0") diff --git a/im-platform/src/main/java/com/bx/implatform/contant/RedisKey.java b/im-platform/src/main/java/com/bx/implatform/contant/RedisKey.java index f6fbdd3..17ddd9f 100644 --- a/im-platform/src/main/java/com/bx/implatform/contant/RedisKey.java +++ b/im-platform/src/main/java/com/bx/implatform/contant/RedisKey.java @@ -32,7 +32,7 @@ public final class RedisKey { /** * 群聊解封消息队列 */ - public static final String IM_QUEUE_GROUP_UNBAN = "im:queue:user:unban"; + public static final String IM_QUEUE_GROUP_UNBAN = "im:queue:group:unban"; /** diff --git a/im-platform/src/main/java/com/bx/implatform/controller/FriendController.java b/im-platform/src/main/java/com/bx/implatform/controller/FriendController.java index cbc281c..8076fc4 100644 --- a/im-platform/src/main/java/com/bx/implatform/controller/FriendController.java +++ b/im-platform/src/main/java/com/bx/implatform/controller/FriendController.java @@ -41,21 +41,21 @@ public class FriendController { @PostMapping("/add") @Operation(summary = "添加好友", description = "双方建立好友关系") - public Result addFriend(@NotNull(message = "好友id不可为空") @RequestParam("friendId") Long friendId) { + public Result addFriend(@NotNull(message = "好友id不可为空") @RequestParam Long friendId) { friendService.addFriend(friendId); return ResultUtils.success(); } @GetMapping("/find/{friendId}") @Operation(summary = "查找好友信息", description = "查找好友信息") - public Result findFriend(@NotNull(message = "好友id不可为空") @PathVariable("friendId") Long friendId) { + public Result findFriend(@NotNull(message = "好友id不可为空") @PathVariable Long friendId) { return ResultUtils.success(friendService.findFriend(friendId)); } @DeleteMapping("/delete/{friendId}") @Operation(summary = "删除好友", description = "解除好友关系") - public Result delFriend(@NotNull(message = "好友id不可为空") @PathVariable("friendId") Long friendId) { + public Result delFriend(@NotNull(message = "好友id不可为空") @PathVariable Long friendId) { friendService.delFriend(friendId); return ResultUtils.success(); } diff --git a/im-platform/src/main/java/com/bx/implatform/controller/GroupController.java b/im-platform/src/main/java/com/bx/implatform/controller/GroupController.java index 9aa83c3..6398030 100644 --- a/im-platform/src/main/java/com/bx/implatform/controller/GroupController.java +++ b/im-platform/src/main/java/com/bx/implatform/controller/GroupController.java @@ -37,14 +37,14 @@ public class GroupController { @Operation(summary = "解散群聊", description = "解散群聊") @DeleteMapping("/delete/{groupId}") - public Result deleteGroup(@NotNull(message = "群聊id不能为空") @PathVariable("groupId") Long groupId) { + public Result deleteGroup(@NotNull(message = "群聊id不能为空") @PathVariable Long groupId) { groupService.deleteGroup(groupId); return ResultUtils.success(); } @Operation(summary = "查询群聊", description = "查询单个群聊信息") @GetMapping("/find/{groupId}") - public Result findGroup(@NotNull(message = "群聊id不能为空") @PathVariable("groupId") Long groupId) { + public Result findGroup(@NotNull(message = "群聊id不能为空") @PathVariable Long groupId) { return ResultUtils.success(groupService.findById(groupId)); } @@ -64,20 +64,20 @@ public class GroupController { @Operation(summary = "查询群聊成员", description = "查询群聊成员") @GetMapping("/members/{groupId}") public Result> findGroupMembers( - @NotNull(message = "群聊id不能为空") @PathVariable("groupId") Long groupId) { + @NotNull(message = "群聊id不能为空") @PathVariable Long groupId) { return ResultUtils.success(groupService.findGroupMembers(groupId)); } @Operation(summary = "退出群聊", description = "退出群聊") @DeleteMapping("/quit/{groupId}") - public Result quitGroup(@NotNull(message = "群聊id不能为空") @PathVariable("groupId") Long groupId) { + public Result quitGroup(@NotNull(message = "群聊id不能为空") @PathVariable Long groupId) { groupService.quitGroup(groupId); return ResultUtils.success(); } @Operation(summary = "踢出群聊", description = "将用户踢出群聊") @DeleteMapping("/kick/{groupId}") - public Result kickGroup(@NotNull(message = "群聊id不能为空") @PathVariable("groupId") Long groupId, + public Result kickGroup(@NotNull(message = "群聊id不能为空") @PathVariable Long groupId, @NotNull(message = "用户id不能为空") @RequestParam Long userId) { groupService.kickGroup(groupId, userId); return ResultUtils.success(); diff --git a/im-platform/src/main/java/com/bx/implatform/controller/GroupMessageController.java b/im-platform/src/main/java/com/bx/implatform/controller/GroupMessageController.java index 2a54241..9117c0e 100644 --- a/im-platform/src/main/java/com/bx/implatform/controller/GroupMessageController.java +++ b/im-platform/src/main/java/com/bx/implatform/controller/GroupMessageController.java @@ -30,29 +30,29 @@ public class GroupMessageController { @DeleteMapping("/recall/{id}") @Operation(summary = "撤回消息", description = "撤回群聊消息") - public Result recallMessage(@NotNull(message = "消息id不能为空") @PathVariable("id") Long id) { + public Result recallMessage(@NotNull(message = "消息id不能为空") @PathVariable Long id) { groupMessageService.recallMessage(id); return ResultUtils.success(); } @GetMapping("/pullOfflineMessage") @Operation(summary = "拉取离线消息", description = "拉取离线消息,消息将通过webscoket异步推送") - public Result pullOfflineMessage(@RequestParam("minId") Long minId) { + public Result pullOfflineMessage(@RequestParam Long minId) { groupMessageService.pullOfflineMessage(minId); return ResultUtils.success(); } @PutMapping("/readed") @Operation(summary = "消息已读", description = "将群聊中的消息状态置为已读") - public Result readedMessage(@RequestParam("groupId") Long groupId) { + public Result readedMessage(@RequestParam Long groupId) { groupMessageService.readedMessage(groupId); return ResultUtils.success(); } @GetMapping("/findReadedUsers") @Operation(summary = "获取已读用户id", description = "获取消息已读用户列表") - public Result> findReadedUsers(@RequestParam("groupId") Long groupId, - @RequestParam("messageId") Long messageId) { + public Result> findReadedUsers(@RequestParam Long groupId, + @RequestParam Long messageId) { return ResultUtils.success(groupMessageService.findReadedUsers(groupId, messageId)); } diff --git a/im-platform/src/main/java/com/bx/implatform/controller/PrivateMessageController.java b/im-platform/src/main/java/com/bx/implatform/controller/PrivateMessageController.java index 7cc3da1..2ae4b57 100644 --- a/im-platform/src/main/java/com/bx/implatform/controller/PrivateMessageController.java +++ b/im-platform/src/main/java/com/bx/implatform/controller/PrivateMessageController.java @@ -30,37 +30,37 @@ public class PrivateMessageController { @DeleteMapping("/recall/{id}") @Operation(summary = "撤回消息", description = "撤回私聊消息") - public Result recallMessage(@NotNull(message = "消息id不能为空") @PathVariable("id") Long id) { + public Result recallMessage(@NotNull(message = "消息id不能为空") @PathVariable Long id) { privateMessageService.recallMessage(id); return ResultUtils.success(); } @GetMapping("/pullOfflineMessage") @Operation(summary = "拉取离线消息", description = "拉取离线消息,消息将通过webscoket异步推送") - public Result pullOfflineMessage(@RequestParam("minId") Long minId) { + public Result pullOfflineMessage(@RequestParam Long minId) { privateMessageService.pullOfflineMessage(minId); return ResultUtils.success(); } @PutMapping("/readed") @Operation(summary = "消息已读", description = "将会话中接收的消息状态置为已读") - public Result readedMessage(@RequestParam("friendId") Long friendId) { + public Result readedMessage(@RequestParam Long friendId) { privateMessageService.readedMessage(friendId); return ResultUtils.success(); } @GetMapping("/maxReadedId") @Operation(summary = "获取最大已读消息的id", description = "获取某个会话中已读消息的最大id") - public Result getMaxReadedId(@RequestParam("friendId") Long friendId) { + public Result getMaxReadedId(@RequestParam Long friendId) { return ResultUtils.success(privateMessageService.getMaxReadedId(friendId)); } @GetMapping("/history") @Operation(summary = "查询聊天记录", description = "查询聊天记录") public Result> recallMessage( - @NotNull(message = "好友id不能为空") @RequestParam("friendId") Long friendId, - @NotNull(message = "页码不能为空") @RequestParam("page") Long page, - @NotNull(message = "size不能为空") @RequestParam("size") Long size) { + @NotNull(message = "好友id不能为空") @RequestParam Long friendId, + @NotNull(message = "页码不能为空") @RequestParam Long page, + @NotNull(message = "size不能为空") @RequestParam Long size) { return ResultUtils.success(privateMessageService.findHistoryMessage(friendId, page, size)); } diff --git a/im-platform/src/main/java/com/bx/implatform/controller/UserController.java b/im-platform/src/main/java/com/bx/implatform/controller/UserController.java index 422298f..bd2959f 100644 --- a/im-platform/src/main/java/com/bx/implatform/controller/UserController.java +++ b/im-platform/src/main/java/com/bx/implatform/controller/UserController.java @@ -58,7 +58,7 @@ public class UserController { @GetMapping("/findByName") @Operation(summary = "查找用户", description = "根据用户名或昵称查找用户") - public Result> findByName(@RequestParam("name") String name) { + public Result> findByName(@RequestParam String name) { return ResultUtils.success(userService.findUserByName(name)); } } diff --git a/im-platform/src/main/java/com/bx/implatform/controller/WebrtcPrivateController.java b/im-platform/src/main/java/com/bx/implatform/controller/WebrtcPrivateController.java index 9ae38e8..2b0f241 100644 --- a/im-platform/src/main/java/com/bx/implatform/controller/WebrtcPrivateController.java +++ b/im-platform/src/main/java/com/bx/implatform/controller/WebrtcPrivateController.java @@ -20,7 +20,7 @@ public class WebrtcPrivateController { @OnlineCheck @Operation(summary = "呼叫视频通话") @PostMapping("/call") - public Result call(@RequestParam("uid") Long uid, @RequestParam(name = "mode", defaultValue = "video") String mode, + public Result call(@RequestParam Long uid, @RequestParam(defaultValue = "video") String mode, @RequestBody String offer) { webrtcPrivateService.call(uid, mode, offer); return ResultUtils.success(); @@ -28,49 +28,49 @@ public class WebrtcPrivateController { @Operation(summary = "接受视频通话") @PostMapping("/accept") - public Result accept(@RequestParam("uid") Long uid, @RequestBody String answer) { + public Result accept(@RequestParam Long uid, @RequestBody String answer) { webrtcPrivateService.accept(uid, answer); return ResultUtils.success(); } @Operation(summary = "拒绝视频通话") @PostMapping("/reject") - public Result reject(@RequestParam("uid") Long uid) { + public Result reject(@RequestParam Long uid) { webrtcPrivateService.reject(uid); return ResultUtils.success(); } @Operation(summary = "取消呼叫") @PostMapping("/cancel") - public Result cancel(@RequestParam("uid") Long uid) { + public Result cancel(@RequestParam Long uid) { webrtcPrivateService.cancel(uid); return ResultUtils.success(); } @Operation(summary = "呼叫失败") @PostMapping("/failed") - public Result failed(@RequestParam("uid") Long uid, @RequestParam String reason) { + public Result failed(@RequestParam Long uid, @RequestParam String reason) { webrtcPrivateService.failed(uid, reason); return ResultUtils.success(); } @Operation(summary = "挂断") @PostMapping("/handup") - public Result handup(@RequestParam("uid") Long uid) { + public Result handup(@RequestParam Long uid) { webrtcPrivateService.handup(uid); return ResultUtils.success(); } @PostMapping("/candidate") @Operation(summary = "同步candidate") - public Result candidate(@RequestParam("uid") Long uid, @RequestBody String candidate) { + public Result candidate(@RequestParam Long uid, @RequestBody String candidate) { webrtcPrivateService.candidate(uid, candidate); return ResultUtils.success(); } @Operation(summary = "获取通话信息") @PostMapping("/heartbeat") - public Result heartbeat(@RequestParam("uid") Long uid) { + public Result heartbeat(@RequestParam Long uid) { webrtcPrivateService.heartbeat(uid); return ResultUtils.success(); } diff --git a/im-platform/src/main/java/com/bx/implatform/service/IGroupService.java b/im-platform/src/main/java/com/bx/implatform/service/IGroupService.java index 6f58619..57c1e58 100644 --- a/im-platform/src/main/java/com/bx/implatform/service/IGroupService.java +++ b/im-platform/src/main/java/com/bx/implatform/service/IGroupService.java @@ -68,7 +68,7 @@ public interface IGroupService extends IService { * @param groupId 群聊id * @return 群聊实体 */ - Group getById(Long groupId); + Group getAndCheckById(Long groupId); /** * 根据id查找群聊 diff --git a/im-platform/src/main/java/com/bx/implatform/service/impl/FriendServiceImpl.java b/im-platform/src/main/java/com/bx/implatform/service/impl/FriendServiceImpl.java index 20ef491..0aa688d 100644 --- a/im-platform/src/main/java/com/bx/implatform/service/impl/FriendServiceImpl.java +++ b/im-platform/src/main/java/com/bx/implatform/service/impl/FriendServiceImpl.java @@ -25,6 +25,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; +import java.util.Objects; @Slf4j @Service @@ -47,7 +48,7 @@ public class FriendServiceImpl extends ServiceImpl impleme public void addFriend(Long friendId) { long userId = SessionContext.getSession().getUserId(); if (friendId.equals(userId)) { - throw new GlobalException(ResultCode.PROGRAM_ERROR, "不允许添加自己为好友"); + throw new GlobalException("不允许添加自己为好友"); } // 互相绑定好友关系 FriendServiceImpl proxy = (FriendServiceImpl) AopContext.currentProxy(); @@ -87,12 +88,10 @@ public class FriendServiceImpl extends ServiceImpl impleme queryWrapper.lambda() .eq(Friend::getUserId, userId) .eq(Friend::getFriendId, vo.getId()); - Friend f = this.getOne(queryWrapper); - if (f == null) { - throw new GlobalException(ResultCode.PROGRAM_ERROR, "对方不是您的好友"); + if (Objects.isNull(f)) { + throw new GlobalException("对方不是您的好友"); } - f.setFriendHeadImage(vo.getHeadImage()); f.setFriendNickName(vo.getNickName()); this.updateById(f); @@ -148,8 +147,8 @@ public class FriendServiceImpl extends ServiceImpl impleme .eq(Friend::getUserId, session.getUserId()) .eq(Friend::getFriendId, friendId); Friend friend = this.getOne(wrapper); - if (friend == null) { - throw new GlobalException(ResultCode.PROGRAM_ERROR, "对方不是您的好友"); + if (Objects.isNull(friend)) { + throw new GlobalException("对方不是您的好友"); } FriendVO vo = new FriendVO(); vo.setId(friend.getFriendId()); diff --git a/im-platform/src/main/java/com/bx/implatform/service/impl/GroupMessageServiceImpl.java b/im-platform/src/main/java/com/bx/implatform/service/impl/GroupMessageServiceImpl.java index 5a8c45c..512241a 100644 --- a/im-platform/src/main/java/com/bx/implatform/service/impl/GroupMessageServiceImpl.java +++ b/im-platform/src/main/java/com/bx/implatform/service/impl/GroupMessageServiceImpl.java @@ -21,7 +21,6 @@ import com.bx.implatform.entity.GroupMember; import com.bx.implatform.entity.GroupMessage; import com.bx.implatform.enums.MessageStatus; import com.bx.implatform.enums.MessageType; -import com.bx.implatform.enums.ResultCode; import com.bx.implatform.exception.GlobalException; import com.bx.implatform.mapper.GroupMessageMapper; import com.bx.implatform.service.IGroupMemberService; @@ -57,17 +56,11 @@ public class GroupMessageServiceImpl extends ServiceImpl userIds = groupMemberService.findUserIdsByGroupId(group.getId()); @@ -103,18 +96,18 @@ public class GroupMessageServiceImpl extends ServiceImpl IMConstant.ALLOW_RECALL_SECOND * 1000) { - throw new GlobalException(ResultCode.PROGRAM_ERROR, "消息已发送超过5分钟,无法撤回"); + throw new GlobalException("消息已发送超过5分钟,无法撤回"); } // 判断是否在群里 GroupMember member = groupMemberService.findByGroupAndUserId(msg.getGroupId(), session.getUserId()); if (Objects.isNull(member) || Boolean.TRUE.equals(member.getQuit())) { - throw new GlobalException(ResultCode.PROGRAM_ERROR, "您已不在群聊里面,无法撤回消息"); + throw new GlobalException("您已不在群聊里面,无法撤回消息"); } // 修改数据库 msg.setStatus(MessageStatus.RECALL.code()); @@ -151,7 +144,7 @@ public class GroupMessageServiceImpl extends ServiceImpl members = groupMemberService.findByUserId(session.getUserId()); @@ -315,12 +308,12 @@ public class GroupMessageServiceImpl extends ServiceImpl wrapper = new QueryWrapper<>(); diff --git a/im-platform/src/main/java/com/bx/implatform/service/impl/GroupServiceImpl.java b/im-platform/src/main/java/com/bx/implatform/service/impl/GroupServiceImpl.java index 3055e25..6b9979a 100644 --- a/im-platform/src/main/java/com/bx/implatform/service/impl/GroupServiceImpl.java +++ b/im-platform/src/main/java/com/bx/implatform/service/impl/GroupServiceImpl.java @@ -14,7 +14,6 @@ import com.bx.implatform.contant.RedisKey; import com.bx.implatform.entity.*; import com.bx.implatform.enums.MessageStatus; import com.bx.implatform.enums.MessageType; -import com.bx.implatform.enums.ResultCode; import com.bx.implatform.exception.GlobalException; import com.bx.implatform.mapper.GroupMapper; import com.bx.implatform.mapper.GroupMessageMapper; @@ -43,9 +42,9 @@ import java.util.*; import java.util.stream.Collectors; @Slf4j -@CacheConfig(cacheManager = "cacheManager",cacheNames = RedisKey.IM_CACHE_GROUP) @Service @RequiredArgsConstructor +@CacheConfig(cacheNames = RedisKey.IM_CACHE_GROUP) public class GroupServiceImpl extends ServiceImpl implements IGroupService { private final IUserService userService; private final IGroupMemberService groupMemberService; @@ -84,7 +83,7 @@ public class GroupServiceImpl extends ServiceImpl implements public GroupVO modifyGroup(GroupVO vo) { UserSession session = SessionContext.getSession(); // 校验是不是群主,只有群主能改信息 - Group group = this.getById(vo.getId()); + Group group = this.getAndCheckById(vo.getId()); // 群主有权修改群基本信息 if (group.getOwnerId().equals(session.getUserId())) { group = BeanUtils.copyProperties(vo, Group.class); @@ -93,7 +92,7 @@ public class GroupServiceImpl extends ServiceImpl implements // 更新成员信息 GroupMember member = groupMemberService.findByGroupAndUserId(vo.getId(), session.getUserId()); if (Objects.isNull(member) || member.getQuit()) { - throw new GlobalException(ResultCode.PROGRAM_ERROR, "您不是群聊的成员"); + throw new GlobalException( "您不是群聊的成员"); } member.setAliasName(StringUtils.isEmpty(vo.getAliasName()) ? session.getNickName() : vo.getAliasName()); member.setRemark(StringUtils.isEmpty(vo.getRemark()) ? Objects.requireNonNull(group).getName() : vo.getRemark()); @@ -109,7 +108,7 @@ public class GroupServiceImpl extends ServiceImpl implements UserSession session = SessionContext.getSession(); Group group = this.getById(groupId); if (!group.getOwnerId().equals(session.getUserId())) { - throw new GlobalException(ResultCode.PROGRAM_ERROR, "只有群主才有权限解除群聊"); + throw new GlobalException("只有群主才有权限解除群聊"); } // 群聊用户id List userIds = groupMemberService.findUserIdsByGroupId(groupId); @@ -131,7 +130,7 @@ public class GroupServiceImpl extends ServiceImpl implements Long userId = SessionContext.getSession().getUserId(); Group group = this.getById(groupId); if (group.getOwnerId().equals(userId)) { - throw new GlobalException(ResultCode.PROGRAM_ERROR, "您是群主,不可退出群聊"); + throw new GlobalException( "您是群主,不可退出群聊"); } // 删除群聊成员 groupMemberService.removeByGroupAndUserId(groupId, userId); @@ -146,12 +145,12 @@ public class GroupServiceImpl extends ServiceImpl implements @Override public void kickGroup(Long groupId, Long userId) { UserSession session = SessionContext.getSession(); - Group group = this.getById(groupId); + Group group = this.getAndCheckById(groupId); if (!group.getOwnerId().equals(session.getUserId())) { - throw new GlobalException(ResultCode.PROGRAM_ERROR, "您不是群主,没有权限踢人"); + throw new GlobalException( "您不是群主,没有权限踢人"); } if (userId.equals(session.getUserId())) { - throw new GlobalException(ResultCode.PROGRAM_ERROR, "亲,不能移除自己哟"); + throw new GlobalException( "亲,不能移除自己哟"); } // 删除群聊成员 groupMemberService.removeByGroupAndUserId(groupId, userId); @@ -168,11 +167,11 @@ public class GroupServiceImpl extends ServiceImpl implements UserSession session = SessionContext.getSession(); Group group = super.getById(groupId); if (Objects.isNull(group)) { - throw new GlobalException(ResultCode.PROGRAM_ERROR, "群组不存在"); + throw new GlobalException( "群组不存在"); } GroupMember member = groupMemberService.findByGroupAndUserId(groupId, session.getUserId()); if (Objects.isNull(member)) { - throw new GlobalException(ResultCode.PROGRAM_ERROR, "您未加入群聊"); + throw new GlobalException( "您未加入群聊"); } GroupVO vo = BeanUtils.copyProperties(group, GroupVO.class); vo.setAliasName(member.getAliasName()); @@ -183,13 +182,16 @@ public class GroupServiceImpl extends ServiceImpl implements @Cacheable(key = "#groupId") @Override - public Group getById(Long groupId) { + public Group getAndCheckById(Long groupId) { Group group = super.getById(groupId); if (Objects.isNull(group)) { - throw new GlobalException(ResultCode.PROGRAM_ERROR, "群组不存在"); + throw new GlobalException( "群组不存在"); } if (group.getDeleted()) { - throw new GlobalException(ResultCode.PROGRAM_ERROR, "群组'" + group.getName() + "'已解散"); + throw new GlobalException( "群组'" + group.getName() + "'已解散"); + } + if (group.getIsBanned()) { + throw new GlobalException( "群组'" + group.getName() + "'已被封禁,原因:"+group.getReason()); } return group; } @@ -223,26 +225,23 @@ public class GroupServiceImpl extends ServiceImpl implements @Override public void invite(GroupInviteVO vo) { UserSession session = SessionContext.getSession(); - Group group = this.getById(vo.getGroupId()); - if (Objects.isNull(group)) { - throw new GlobalException(ResultCode.PROGRAM_ERROR, "群聊不存在"); - } + Group group = this.getAndCheckById(vo.getGroupId()); GroupMember member = groupMemberService.findByGroupAndUserId(vo.getGroupId(), session.getUserId()); if (Objects.isNull(group) || member.getQuit()) { - throw new GlobalException(ResultCode.PROGRAM_ERROR, "您不在群聊中,邀请失败"); + throw new GlobalException("您不在群聊中,邀请失败"); } // 群聊人数校验 List members = groupMemberService.findByGroupId(vo.getGroupId()); long size = members.stream().filter(m -> !m.getQuit()).count(); if (vo.getFriendIds().size() + size > Constant.MAX_GROUP_MEMBER) { - throw new GlobalException(ResultCode.PROGRAM_ERROR, "群聊人数不能大于" + Constant.MAX_GROUP_MEMBER + "人"); + throw new GlobalException("群聊人数不能大于" + Constant.MAX_GROUP_MEMBER + "人"); } // 找出好友信息 List friends = friendsService.findFriendByUserId(session.getUserId()); List friendsList = vo.getFriendIds().stream().map(id -> friends.stream().filter(f -> f.getFriendId().equals(id)).findFirst().get()) .collect(Collectors.toList()); if (friendsList.size() != vo.getFriendIds().size()) { - throw new GlobalException(ResultCode.PROGRAM_ERROR, "部分用户不是您的好友,邀请失败"); + throw new GlobalException( "部分用户不是您的好友,邀请失败"); } // 批量保存成员数据 List groupMembers = friendsList.stream().map(f -> { diff --git a/im-platform/src/main/java/com/bx/implatform/service/impl/PrivateMessageServiceImpl.java b/im-platform/src/main/java/com/bx/implatform/service/impl/PrivateMessageServiceImpl.java index 0e10922..91d5bdd 100644 --- a/im-platform/src/main/java/com/bx/implatform/service/impl/PrivateMessageServiceImpl.java +++ b/im-platform/src/main/java/com/bx/implatform/service/impl/PrivateMessageServiceImpl.java @@ -49,7 +49,7 @@ public class PrivateMessageServiceImpl extends ServiceImpl IMConstant.ALLOW_RECALL_SECOND * 1000) { - throw new GlobalException(ResultCode.PROGRAM_ERROR, "消息已发送超过5分钟,无法撤回"); + throw new GlobalException("消息已发送超过5分钟,无法撤回"); } // 修改消息状态 msg.setStatus(MessageStatus.RECALL.code()); @@ -138,7 +138,7 @@ public class PrivateMessageServiceImpl extends ServiceImpl friends = friendService.findFriendByUserId(session.getUserId()); diff --git a/im-platform/src/main/java/com/bx/implatform/service/impl/UserServiceImpl.java b/im-platform/src/main/java/com/bx/implatform/service/impl/UserServiceImpl.java index 7f81724..2478731 100644 --- a/im-platform/src/main/java/com/bx/implatform/service/impl/UserServiceImpl.java +++ b/im-platform/src/main/java/com/bx/implatform/service/impl/UserServiceImpl.java @@ -141,11 +141,11 @@ public class UserServiceImpl extends ServiceImpl implements IU public void update(UserVO vo) { UserSession session = SessionContext.getSession(); if (!session.getUserId().equals(vo.getId())) { - throw new GlobalException(ResultCode.PROGRAM_ERROR, "不允许修改其他用户的信息!"); + throw new GlobalException("不允许修改其他用户的信息!"); } User user = this.getById(vo.getId()); if (Objects.isNull(user)) { - throw new GlobalException(ResultCode.PROGRAM_ERROR, "用户不存在"); + throw new GlobalException("用户不存在"); } // 更新好友昵称和头像 if (!user.getNickName().equals(vo.getNickName()) || !user.getHeadImageThumb().equals(vo.getHeadImageThumb())) { diff --git a/im-platform/src/main/java/com/bx/implatform/service/impl/WebrtcGroupServiceImpl.java b/im-platform/src/main/java/com/bx/implatform/service/impl/WebrtcGroupServiceImpl.java index 8851b32..6f91f4a 100644 --- a/im-platform/src/main/java/com/bx/implatform/service/impl/WebrtcGroupServiceImpl.java +++ b/im-platform/src/main/java/com/bx/implatform/service/impl/WebrtcGroupServiceImpl.java @@ -17,6 +17,7 @@ import com.bx.implatform.enums.MessageType; import com.bx.implatform.exception.GlobalException; import com.bx.implatform.service.IGroupMemberService; import com.bx.implatform.service.IGroupMessageService; +import com.bx.implatform.service.IGroupService; import com.bx.implatform.service.IWebrtcGroupService; import com.bx.implatform.session.SessionContext; import com.bx.implatform.session.UserSession; @@ -39,7 +40,7 @@ import java.util.stream.Collectors; /** * 群语音通话服务类,所有涉及修改webtcSession的方法都要挂分布式锁 * - * @author: blue + * @author: blue * @date: 2024-06-01 * @version: 1.0 */ @@ -47,7 +48,7 @@ import java.util.stream.Collectors; @Service @RequiredArgsConstructor public class WebrtcGroupServiceImpl implements IWebrtcGroupService { - + private final IGroupService groupService; private final IGroupMemberService groupMemberService; private final IGroupMessageService groupMessageService; private final RedisTemplate redisTemplate; @@ -55,15 +56,12 @@ public class WebrtcGroupServiceImpl implements IWebrtcGroupService { private final UserStateUtils userStateUtils; private final WebrtcConfig webrtcConfig; - @OnlineCheck @RedisLock(prefixKey = RedisKey.IM_LOCK_RTC_GROUP, key = "#dto.groupId") @Override public void setup(WebrtcGroupSetupDTO dto) { UserSession userSession = SessionContext.getSession(); - if(!imClient.isOnline(userSession.getUserId())){ - throw new GlobalException("您已断开连接,请重新登陆"); - } + groupService.getAndCheckById(dto.getGroupId()); if (dto.getUserInfos().size() > webrtcConfig.getMaxChannel()) { throw new GlobalException("最多支持" + webrtcConfig.getMaxChannel() + "人进行通话"); } @@ -72,7 +70,7 @@ public class WebrtcGroupServiceImpl implements IWebrtcGroupService { throw new GlobalException("部分用户不在群聊中"); } String key = buildWebrtcSessionKey(dto.getGroupId()); - if (redisTemplate.hasKey(key)) { + if (Boolean.TRUE.equals(redisTemplate.hasKey(key))) { throw new GlobalException("该群聊已存在一个通话"); } // 有效用户 @@ -116,11 +114,11 @@ public class WebrtcGroupServiceImpl implements IWebrtcGroupService { } // 向被邀请的用户广播消息,发起呼叫 List recvIds = getRecvIds(userInfos); - sendRtcMessage1(MessageType.RTC_GROUP_SETUP, dto.getGroupId(), recvIds, JSON.toJSONString(userInfos),false); + sendRtcMessage1(MessageType.RTC_GROUP_SETUP, dto.getGroupId(), recvIds, JSON.toJSONString(userInfos), false); // 发送文字提示信息 - WebrtcUserInfo mineInfo = findUserInfo(webrtcSession,userSession.getUserId()); + WebrtcUserInfo mineInfo = findUserInfo(webrtcSession, userSession.getUserId()); String content = mineInfo.getNickName() + " 发起了语音通话"; - sendTipMessage(dto.getGroupId(),content); + sendTipMessage(dto.getGroupId(), content); log.info("发起群通话,userId:{},groupId:{}", userSession.getUserId(), dto.getGroupId()); } @@ -142,7 +140,7 @@ public class WebrtcGroupServiceImpl implements IWebrtcGroupService { saveWebrtcSession(groupId, webrtcSession); // 广播信令 List recvIds = getRecvIds(webrtcSession.getUserInfos()); - sendRtcMessage1(MessageType.RTC_GROUP_ACCEPT, groupId, recvIds, "",true); + sendRtcMessage1(MessageType.RTC_GROUP_ACCEPT, groupId, recvIds, "", true); log.info("加入群通话,userId:{},groupId:{}", userSession.getUserId(), groupId); } @@ -169,7 +167,7 @@ public class WebrtcGroupServiceImpl implements IWebrtcGroupService { userStateUtils.setFree(userSession.getUserId()); // 广播消息给的所有用户 List recvIds = getRecvIds(userInfos); - sendRtcMessage1(MessageType.RTC_GROUP_REJECT, groupId, recvIds, "",true); + sendRtcMessage1(MessageType.RTC_GROUP_REJECT, groupId, recvIds, "", true); log.info("拒绝群通话,userId:{},groupId:{}", userSession.getUserId(), groupId); } @@ -198,8 +196,8 @@ public class WebrtcGroupServiceImpl implements IWebrtcGroupService { vo.setUserIds(Arrays.asList(userSession.getUserId())); vo.setReason(dto.getReason()); List recvIds = getRecvIds(userInfos); - sendRtcMessage1(MessageType.RTC_GROUP_FAILED, dto.getGroupId(), recvIds, JSON.toJSONString(vo),false); - log.info("群通话失败,userId:{},groupId:{},原因:{}", userSession.getUserId(), dto.getReason()); + sendRtcMessage1(MessageType.RTC_GROUP_FAILED, dto.getGroupId(), recvIds, JSON.toJSONString(vo), false); + log.info("群通话失败,userId:{},groupId:{},原因:{}", userSession.getUserId(),dto.getGroupId(), dto.getReason()); } @OnlineCheck @@ -216,7 +214,7 @@ public class WebrtcGroupServiceImpl implements IWebrtcGroupService { throw new GlobalException("您不在群里中"); } IMUserInfo mine = findInChatUser(webrtcSession, userSession.getUserId()); - if(!Objects.isNull(mine) && mine.getTerminal() != userSession.getTerminal()){ + if (!Objects.isNull(mine) && mine.getTerminal().equals(userSession.getTerminal())) { throw new GlobalException("已在其他设备加入通话"); } WebrtcUserInfo userInfo = new WebrtcUserInfo(); @@ -238,7 +236,7 @@ public class WebrtcGroupServiceImpl implements IWebrtcGroupService { userStateUtils.setBusy(userSession.getUserId()); // 广播信令 List recvIds = getRecvIds(webrtcSession.getUserInfos()); - sendRtcMessage1(MessageType.RTC_GROUP_JOIN, groupId, recvIds, JSON.toJSONString(userInfo),false); + sendRtcMessage1(MessageType.RTC_GROUP_JOIN, groupId, recvIds, JSON.toJSONString(userInfo), false); log.info("加入群通话,userId:{},groupId:{}", userSession.getUserId(), groupId); } @@ -271,8 +269,6 @@ public class WebrtcGroupServiceImpl implements IWebrtcGroupService { } if (!imClient.isOnline(userInfo.getId())) { offlineUserIds.add(userInfo.getId()); -// userStateUtils.setBusy(userInfo.getId()); -// newUserInfos.add(userInfo); } else if (userStateUtils.isBusy(userInfo.getId())) { busyUserIds.add(userInfo.getId()); } else { @@ -301,9 +297,10 @@ public class WebrtcGroupServiceImpl implements IWebrtcGroupService { } // 向被邀请的发起呼叫 List newUserIds = getRecvIds(newUserInfos); - sendRtcMessage1(MessageType.RTC_GROUP_SETUP, dto.getGroupId(), newUserIds, JSON.toJSONString(userInfos),false); + sendRtcMessage1(MessageType.RTC_GROUP_SETUP, dto.getGroupId(), newUserIds, JSON.toJSONString(userInfos), false); // 向已在通话中的用户同步新邀请的用户信息 - sendRtcMessage1(MessageType.RTC_GROUP_INVITE, dto.getGroupId(), userIds, JSON.toJSONString(newUserInfos),false); + sendRtcMessage1(MessageType.RTC_GROUP_INVITE, dto.getGroupId(), userIds, JSON.toJSONString(newUserInfos), + false); log.info("邀请加入群通话,userId:{},groupId:{},邀请用户:{}", userSession.getUserId(), dto.getGroupId(), newUserIds); } @@ -323,9 +320,9 @@ public class WebrtcGroupServiceImpl implements IWebrtcGroupService { webrtcSession.getUserInfos().forEach(user -> userStateUtils.setFree(user.getId())); // 广播消息给的所有用户 List recvIds = getRecvIds(webrtcSession.getUserInfos()); - sendRtcMessage1(MessageType.RTC_GROUP_CANCEL, groupId, recvIds, "",false); + sendRtcMessage1(MessageType.RTC_GROUP_CANCEL, groupId, recvIds, "", false); // 发送文字提示信息 - sendTipMessage(groupId,"通话结束"); + sendTipMessage(groupId, "通话结束"); log.info("发起人取消群通话,userId:{},groupId:{}", userSession.getUserId(), groupId); } @@ -351,9 +348,9 @@ public class WebrtcGroupServiceImpl implements IWebrtcGroupService { webrtcSession.getUserInfos().forEach(user -> userStateUtils.setFree(user.getId())); // 广播给还在呼叫中的用户,取消通话 List recvIds = getRecvIds(webrtcSession.getUserInfos()); - sendRtcMessage1(MessageType.RTC_GROUP_CANCEL, groupId, recvIds, "",false); + sendRtcMessage1(MessageType.RTC_GROUP_CANCEL, groupId, recvIds, "", false); // 发送文字提示信息 - sendTipMessage(groupId,"通话结束"); + sendTipMessage(groupId, "通话结束"); log.info("群通话结束,groupId:{}", groupId); } else { // 更新会话信息 @@ -364,7 +361,7 @@ public class WebrtcGroupServiceImpl implements IWebrtcGroupService { userStateUtils.setFree(userSession.getUserId()); // 广播信令 List recvIds = getRecvIds(userInfos); - sendRtcMessage1(MessageType.RTC_GROUP_QUIT, groupId, recvIds, "",false); + sendRtcMessage1(MessageType.RTC_GROUP_QUIT, groupId, recvIds, "", false); log.info("用户退出群通话,userId:{},groupId:{}", userSession.getUserId(), groupId); } } @@ -434,7 +431,7 @@ public class WebrtcGroupServiceImpl implements IWebrtcGroupService { saveWebrtcSession(dto.getGroupId(), webrtcSession); // 广播信令 List recvIds = getRecvIds(webrtcSession.getUserInfos()); - sendRtcMessage1(MessageType.RTC_GROUP_DEVICE, dto.getGroupId(), recvIds, JSON.toJSONString(dto),false); + sendRtcMessage1(MessageType.RTC_GROUP_DEVICE, dto.getGroupId(), recvIds, JSON.toJSONString(dto), false); log.info("设备操作,userId:{},groupId:{},摄像头:{}", userSession.getUserId(), dto.getGroupId(), dto.getIsCamera()); } @@ -526,7 +523,8 @@ public class WebrtcGroupServiceImpl implements IWebrtcGroupService { return webrtcSession.getUserInfos().stream().anyMatch(user -> user.getId().equals(userId)); } - private void sendRtcMessage1(MessageType messageType, Long groupId, List recvIds, String content,Boolean sendSelf) { + private void sendRtcMessage1(MessageType messageType, Long groupId, List recvIds, String content, + Boolean sendSelf) { UserSession userSession = SessionContext.getSession(); GroupMessageVO messageInfo = new GroupMessageVO(); messageInfo.setType(messageType.code()); @@ -559,7 +557,7 @@ public class WebrtcGroupServiceImpl implements IWebrtcGroupService { imClient.sendGroupMessage(sendMessage); } - private void sendTipMessage(Long groupId,String content){ + private void sendTipMessage(Long groupId, String content) { UserSession userSession = SessionContext.getSession(); // 群聊成员列表 List userIds = groupMemberService.findUserIdsByGroupId(groupId); @@ -581,5 +579,5 @@ public class WebrtcGroupServiceImpl implements IWebrtcGroupService { sendMessage.setSendResult(false); sendMessage.setData(msgInfo); imClient.sendGroupMessage(sendMessage); - }; + } } diff --git a/im-platform/src/main/java/com/bx/implatform/task/GroupBannedConsumerTask.java b/im-platform/src/main/java/com/bx/implatform/task/GroupBannedConsumerTask.java new file mode 100644 index 0000000..3569cd6 --- /dev/null +++ b/im-platform/src/main/java/com/bx/implatform/task/GroupBannedConsumerTask.java @@ -0,0 +1,78 @@ +package com.bx.implatform.task; + +import cn.hutool.core.util.StrUtil; +import com.bx.imclient.IMClient; +import com.bx.imcommon.enums.IMTerminalType; +import com.bx.imcommon.model.IMGroupMessage; +import com.bx.imcommon.model.IMSystemMessage; +import com.bx.imcommon.model.IMUserInfo; +import com.bx.imcommon.mq.RedisMQConsumer; +import com.bx.imcommon.mq.RedisMQListener; +import com.bx.implatform.contant.Constant; +import com.bx.implatform.contant.RedisKey; +import com.bx.implatform.dto.GroupBanDTO; +import com.bx.implatform.dto.UserBanDTO; +import com.bx.implatform.entity.Group; +import com.bx.implatform.entity.GroupMessage; +import com.bx.implatform.enums.MessageStatus; +import com.bx.implatform.enums.MessageType; +import com.bx.implatform.service.IGroupMemberService; +import com.bx.implatform.service.IGroupMessageService; +import com.bx.implatform.service.IGroupService; +import com.bx.implatform.util.BeanUtils; +import com.bx.implatform.vo.GroupMessageVO; +import com.bx.implatform.vo.SystemMessageVO; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.logging.log4j.util.Strings; +import org.springframework.boot.context.properties.source.ConfigurationPropertyState; +import org.springframework.stereotype.Component; + +import java.util.Collections; +import java.util.Date; +import java.util.List; + +/** + * @author: 谢绍许 + * @date: 2024-07-15 + * @version: 1.0 + */ +@Slf4j +@Component +@RequiredArgsConstructor +@RedisMQListener(queue = RedisKey.IM_QUEUE_GROUP_BANNED) +public class GroupBannedConsumerTask extends RedisMQConsumer { + + private final IMClient imClient; + + private final IGroupMessageService groupMessageService; + + private final IGroupMemberService groupMemberService; + + @Override + public void onMessage(GroupBanDTO dto) { + log.info("群聊被封禁处理,群id:{},原因:{}", dto.getId(), dto.getReason()); + // 群聊成员列表 + List userIds = groupMemberService.findUserIdsByGroupId(dto.getId()); + // 保存消息 + GroupMessage msg = new GroupMessage(); + msg.setGroupId(dto.getId()); + String tip = "本群聊已被管理员封禁,原因:" + dto.getReason(); + msg.setContent(tip); + msg.setSendId(Constant.SYS_USER_ID); + msg.setSendTime(new Date()); + msg.setStatus(MessageStatus.UNSEND.code()); + msg.setSendNickName("系统管理员"); + msg.setType(MessageType.TIP_TEXT.code()); + groupMessageService.save(msg); + // 推送提示语到群聊中 + GroupMessageVO msgInfo = BeanUtils.copyProperties(msg, GroupMessageVO.class); + IMGroupMessage sendMessage = new IMGroupMessage<>(); + sendMessage.setSender(new IMUserInfo(Constant.SYS_USER_ID, IMTerminalType.PC.code())); + sendMessage.setRecvIds(userIds); + sendMessage.setSendResult(true); + sendMessage.setSendToSelf(false); + sendMessage.setData(msgInfo); + imClient.sendGroupMessage(sendMessage); + } +} diff --git a/im-platform/src/main/java/com/bx/implatform/task/GroupUnbanConsumerTask.java b/im-platform/src/main/java/com/bx/implatform/task/GroupUnbanConsumerTask.java new file mode 100644 index 0000000..feb9672 --- /dev/null +++ b/im-platform/src/main/java/com/bx/implatform/task/GroupUnbanConsumerTask.java @@ -0,0 +1,70 @@ +package com.bx.implatform.task; + +import cn.hutool.core.util.StrUtil; +import com.bx.imclient.IMClient; +import com.bx.imcommon.enums.IMTerminalType; +import com.bx.imcommon.model.IMGroupMessage; +import com.bx.imcommon.model.IMUserInfo; +import com.bx.imcommon.mq.RedisMQConsumer; +import com.bx.imcommon.mq.RedisMQListener; +import com.bx.implatform.contant.Constant; +import com.bx.implatform.contant.RedisKey; +import com.bx.implatform.dto.GroupBanDTO; +import com.bx.implatform.dto.GroupUnbanDTO; +import com.bx.implatform.entity.GroupMessage; +import com.bx.implatform.enums.MessageStatus; +import com.bx.implatform.enums.MessageType; +import com.bx.implatform.service.IGroupMemberService; +import com.bx.implatform.service.IGroupMessageService; +import com.bx.implatform.util.BeanUtils; +import com.bx.implatform.vo.GroupMessageVO; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.util.Date; +import java.util.List; + +/** + * @author: 谢绍许 + * @date: 2024-07-15 + * @version: 1.0 + */ +@Slf4j +@Component +@RequiredArgsConstructor +@RedisMQListener(queue = RedisKey.IM_QUEUE_GROUP_UNBAN) +public class GroupUnbanConsumerTask extends RedisMQConsumer { + + private final IMClient imClient; + + private final IGroupMessageService groupMessageService; + + private final IGroupMemberService groupMemberService; + + @Override + public void onMessage(GroupUnbanDTO dto) { + log.info("群聊解除封禁处理,群id:{}",dto.getId()); + // 群聊成员列表 + List userIds = groupMemberService.findUserIdsByGroupId(dto.getId()); + // 保存消息 + GroupMessage msg = new GroupMessage(); + msg.setGroupId(dto.getId()); + msg.setContent("已解除封禁"); + msg.setSendId(Constant.SYS_USER_ID); + msg.setSendTime(new Date()); + msg.setStatus(MessageStatus.UNSEND.code()); + msg.setSendNickName("系统管理员"); + msg.setType(MessageType.TIP_TEXT.code()); + groupMessageService.save(msg); + // 推送提示语到群聊中 + GroupMessageVO msgInfo = BeanUtils.copyProperties(msg, GroupMessageVO.class); + IMGroupMessage sendMessage = new IMGroupMessage<>(); + sendMessage.setSender(new IMUserInfo(Constant.SYS_USER_ID, IMTerminalType.PC.code())); + sendMessage.setRecvIds(userIds); + sendMessage.setSendResult(true); + sendMessage.setSendToSelf(false); + sendMessage.setData(msgInfo); + imClient.sendGroupMessage(sendMessage); + } +} diff --git a/im-platform/src/main/resources/application.yml b/im-platform/src/main/resources/application.yml index 0e70e38..f83affc 100644 --- a/im-platform/src/main/resources/application.yml +++ b/im-platform/src/main/resources/application.yml @@ -12,9 +12,11 @@ spring: username: root password: root - redis: - host: 127.0.0.1 - port: 6379 + data: + redis: + host: localhost + port: 6379 + database: 2 servlet: multipart: diff --git a/im-server/src/main/resources/application.yml b/im-server/src/main/resources/application.yml index 827343d..affb1dc 100644 --- a/im-server/src/main/resources/application.yml +++ b/im-server/src/main/resources/application.yml @@ -1,7 +1,9 @@ spring: - redis: - host: 127.0.0.1 - port: 6379 + data: + redis: + host: localhost + port: 6379 + database: 2 websocket: enable: true diff --git a/im-ui/src/components/setting/Setting.vue b/im-ui/src/components/setting/Setting.vue index 410b05b..326874f 100644 --- a/im-ui/src/components/setting/Setting.vue +++ b/im-ui/src/components/setting/Setting.vue @@ -108,7 +108,9 @@ \ No newline at end of file diff --git a/im-uniapp/pages.json b/im-uniapp/pages.json index d3d955c..b1e2366 100644 --- a/im-uniapp/pages.json +++ b/im-uniapp/pages.json @@ -17,8 +17,6 @@ "path": "pages/group/group" }, { "path": "pages/mine/mine" - }, { - "path": "pages/friend/friend-search" }, { "path": "pages/common/user-info" }, { diff --git a/im-uniapp/pages/chat/chat-box.vue b/im-uniapp/pages/chat/chat-box.vue index 543a906..db7a513 100644 --- a/im-uniapp/pages/chat/chat-box.vue +++ b/im-uniapp/pages/chat/chat-box.vue @@ -782,7 +782,7 @@ border: #dddddd solid 1px; overflow: hidden; position: relative; - background-color: #f7f8fd; + background-color: white; .scroll-box { height: 100%; diff --git a/im-uniapp/pages/chat/chat.vue b/im-uniapp/pages/chat/chat.vue index a00e079..ef3284b 100644 --- a/im-uniapp/pages/chat/chat.vue +++ b/im-uniapp/pages/chat/chat.vue @@ -6,12 +6,17 @@ 消息接收中... + + + + + 温馨提示:您现在还没有任何聊天消息,快跟您的好友发起聊天吧~ - @@ -26,6 +31,7 @@ export default { data() { return { + searchText: "", menu: { show: false, style: "", @@ -33,7 +39,8 @@ items: [{ key: 'DELETE', name: '删除该聊天', - icon: 'trash' + icon: 'trash', + color: '#e64e4e' }, { key: 'TOP', @@ -94,6 +101,12 @@ moveToTop(chatIdx) { this.$store.commit("moveTop", chatIdx); }, + isShowChat(chat){ + if(chat.delete){ + return false; + } + return !this.searchText || chat.showName.includes(this.searchText) + }, refreshUnreadBadge() { if (this.unreadCount > 0) { uni.setTabBarBadge({ @@ -158,7 +171,23 @@ border: #dddddd solid 1px; display: flex; flex-direction: column; + + .nav-bar { + padding: 2rpx 20rpx; + display: flex; + align-items: center; + background-color: white; + border-bottom: 1px solid #ddd; + height: 110rpx; + .nav-search { + flex: 1; + height: 110rpx; + } + + + } + .chat-tip { position: absolute; top: 400rpx; diff --git a/im-uniapp/pages/common/user-info.vue b/im-uniapp/pages/common/user-info.vue index 2dbeea8..fd95031 100644 --- a/im-uniapp/pages/common/user-info.vue +++ b/im-uniapp/pages/common/user-info.vue @@ -161,10 +161,12 @@ flex-direction: column; padding-left: 40rpx; flex: 1; - + + .info-primary { display: flex; - + align-items: center; + .info-username { font-size: 40rpx; font-weight: 600; diff --git a/im-uniapp/pages/friend/friend-search.vue b/im-uniapp/pages/friend/friend-search.vue deleted file mode 100644 index 7325411..0000000 --- a/im-uniapp/pages/friend/friend-search.vue +++ /dev/null @@ -1,52 +0,0 @@ - - - - - \ No newline at end of file diff --git a/im-uniapp/pages/friend/friend.vue b/im-uniapp/pages/friend/friend.vue index 5f17664..f0cb9be 100644 --- a/im-uniapp/pages/friend/friend.vue +++ b/im-uniapp/pages/friend/friend.vue @@ -2,7 +2,7 @@ - + @@ -34,15 +34,15 @@ export default { data() { return { + searchText: '', friendIdx: [], friendGroup: [] } }, methods: { - onFocusSearch() { - uni.navigateTo({ - url: "/pages/friend/friend-search" - }) + onInput(searchText){ + this.searchText = searchText; + this.refreshFriendGroup() }, onAddNewFriends() { uni.navigateTo({ @@ -68,6 +68,9 @@ if (f.delete) { return; } + if(this.searchText && !f.nickName.includes(this.searchText)){ + return; + } let letter = this.firstLetter(f.nickName).toUpperCase(); // 非英文一律为#组 if (!this.isEnglish(letter)) { @@ -135,11 +138,9 @@ .nav-search { flex: 1; - } .nav-add { - line-height: 56px; cursor: pointer; } } diff --git a/im-uniapp/pages/group/group.vue b/im-uniapp/pages/group/group.vue index 9a2c377..0d17c9b 100644 --- a/im-uniapp/pages/group/group.vue +++ b/im-uniapp/pages/group/group.vue @@ -2,10 +2,10 @@ - + - + @@ -14,7 +14,7 @@ - + @@ -50,7 +50,7 @@ flex-direction: column; .nav-bar { - margin: 5rpx; + padding: 2rpx 10rpx; display: flex; align-items: center; background-color: white; @@ -60,7 +60,6 @@ } .nav-add { - line-height: 56px; cursor: pointer; } } From 75c9a0744f51fb06db54beab235d0c7e90f021dd Mon Sep 17 00:00:00 2001 From: xsx <825657193@qq.com> Date: Sat, 27 Jul 2024 11:19:03 +0800 Subject: [PATCH 18/48] =?UTF-8?q?=E5=8E=BB=E6=8E=89h5=E6=A0=87=E9=A2=98?= =?UTF-8?q?=E3=80=81=E8=A1=A5=E5=85=85=E6=B3=A8=E5=86=8C=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/LoginController.java | 2 +- .../service/impl/UserServiceImpl.java | 2 +- im-uniapp/App.vue | 15 +- im-uniapp/pages.json | 2 + im-uniapp/pages/group/group-member.vue | 2 +- im-uniapp/pages/login/login.vue | 46 ++++-- im-uniapp/pages/register/register.vue | 142 ++++++++++++++++++ 7 files changed, 189 insertions(+), 22 deletions(-) create mode 100644 im-uniapp/pages/register/register.vue diff --git a/im-platform/src/main/java/com/bx/implatform/controller/LoginController.java b/im-platform/src/main/java/com/bx/implatform/controller/LoginController.java index 8ab4cdb..e889e40 100644 --- a/im-platform/src/main/java/com/bx/implatform/controller/LoginController.java +++ b/im-platform/src/main/java/com/bx/implatform/controller/LoginController.java @@ -22,7 +22,7 @@ public class LoginController { @PostMapping("/login") @Operation(summary = "用户登陆", description = "用户登陆") - public Result login(@Valid @RequestBody LoginDTO dto) { + public Result login(@Valid @RequestBody LoginDTO dto) { LoginVO vo = userService.login(dto); return ResultUtils.success(vo); } diff --git a/im-platform/src/main/java/com/bx/implatform/service/impl/UserServiceImpl.java b/im-platform/src/main/java/com/bx/implatform/service/impl/UserServiceImpl.java index 574a088..91f553f 100644 --- a/im-platform/src/main/java/com/bx/implatform/service/impl/UserServiceImpl.java +++ b/im-platform/src/main/java/com/bx/implatform/service/impl/UserServiceImpl.java @@ -108,7 +108,7 @@ public class UserServiceImpl extends ServiceImpl implements Us @Override public void register(RegisterDTO dto) { User user = this.findUserByUserName(dto.getUserName()); - if (null != user) { + if (Objects.isNull(user)) { throw new GlobalException(ResultCode.USERNAME_ALREADY_REGISTER); } user = BeanUtils.copyProperties(dto, User.class); diff --git a/im-uniapp/App.vue b/im-uniapp/App.vue index 2891dfd..b0a903d 100644 --- a/im-uniapp/App.vue +++ b/im-uniapp/App.vue @@ -353,10 +353,16 @@ \ No newline at end of file diff --git a/im-uniapp/pages.json b/im-uniapp/pages.json index b1e2366..924ff42 100644 --- a/im-uniapp/pages.json +++ b/im-uniapp/pages.json @@ -10,6 +10,8 @@ "pages": [{ "path": "pages/login/login" }, { + "path" : "pages/register/register" + },{ "path": "pages/chat/chat" }, { "path": "pages/friend/friend" diff --git a/im-uniapp/pages/group/group-member.vue b/im-uniapp/pages/group/group-member.vue index e6849d9..dae293f 100644 --- a/im-uniapp/pages/group/group-member.vue +++ b/im-uniapp/pages/group/group-member.vue @@ -15,7 +15,7 @@ {{ member.aliasName}} - diff --git a/im-uniapp/pages/login/login.vue b/im-uniapp/pages/login/login.vue index e3146b0..d686bef 100644 --- a/im-uniapp/pages/login/login.vue +++ b/im-uniapp/pages/login/login.vue @@ -1,15 +1,19 @@ @@ -49,7 +53,7 @@ console.log("登录成功,自动跳转到聊天页面...") uni.setStorageSync("userName", this.loginForm.userName); uni.setStorageSync("password", this.loginForm.password); - loginInfo.expireTime = new Date().getTime() + loginInfo.refreshTokenExpiresIn*1000; + loginInfo.expireTime = new Date().getTime() + loginInfo.refreshTokenExpiresIn * 1000; uni.setStorageSync("loginInfo", loginInfo); // 调用App.vue的初始化方法 getApp().init() @@ -60,7 +64,7 @@ }) } }, - + onLoad() { this.loginForm.userName = uni.getStorageSync("userName"); this.loginForm.password = uni.getStorageSync("password"); @@ -69,16 +73,34 @@ \ No newline at end of file diff --git a/im-uniapp/pages/register/register.vue b/im-uniapp/pages/register/register.vue new file mode 100644 index 0000000..1d302df --- /dev/null +++ b/im-uniapp/pages/register/register.vue @@ -0,0 +1,142 @@ + + + + + \ No newline at end of file From b481b626b1f8ea5cc02e66bc87318d3dc6e84a0e Mon Sep 17 00:00:00 2001 From: xsx <825657193@qq.com> Date: Sun, 28 Jul 2024 01:28:42 +0800 Subject: [PATCH 19/48] =?UTF-8?q?=E7=BE=A4=E8=81=8A=E6=98=B5=E7=A7=B0?= =?UTF-8?q?=E5=A4=87=E6=B3=A8=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- db/im-platfrom.sql | 5 +- .../com/bx/implatform/entity/GroupMember.java | 17 ++- .../service/impl/GroupMessageServiceImpl.java | 4 +- .../service/impl/GroupServiceImpl.java | 109 ++++++++++-------- .../service/impl/UserServiceImpl.java | 32 +++-- .../service/impl/WebrtcGroupServiceImpl.java | 8 +- .../com/bx/implatform/vo/GroupMemberVO.java | 12 +- .../java/com/bx/implatform/vo/GroupVO.java | 16 ++- im-ui/src/components/chat/ChatAtBox.vue | 4 +- im-ui/src/components/chat/ChatBox.vue | 6 +- im-ui/src/components/chat/ChatGroupMember.vue | 4 +- im-ui/src/components/chat/ChatGroupSide.vue | 10 +- im-ui/src/components/chat/ChatHistory.vue | 2 +- im-ui/src/components/group/AddGroupMember.vue | 2 +- im-ui/src/components/group/GroupItem.vue | 4 +- im-ui/src/components/group/GroupMember.vue | 4 +- .../src/components/group/GroupMemberItem.vue | 4 +- .../components/group/GroupMemberSelector.vue | 2 +- im-ui/src/store/chatStore.js | 2 +- im-ui/src/store/groupStore.js | 2 +- im-ui/src/view/Chat.vue | 2 +- im-ui/src/view/Friend.vue | 2 +- im-ui/src/view/Group.vue | 37 +++--- im-ui/src/view/Home.vue | 2 +- im-uniapp/App.vue | 2 +- .../components/chat-at-box/chat-at-box.vue | 12 +- .../components/group-item/group-item.vue | 4 +- .../group-member-selector.vue | 8 +- im-uniapp/pages/chat/chat-box.vue | 10 +- im-uniapp/pages/group/group-edit.vue | 11 +- im-uniapp/pages/group/group-info.vue | 14 +-- im-uniapp/pages/group/group-invite.vue | 4 +- im-uniapp/pages/group/group-member.vue | 12 +- im-uniapp/pages/group/group.vue | 16 +-- im-uniapp/store/chatStore.js | 2 +- im-uniapp/vite.config.js | 2 +- 36 files changed, 208 insertions(+), 181 deletions(-) diff --git a/db/im-platfrom.sql b/db/im-platfrom.sql index a146c98..f30c96f 100644 --- a/db/im-platfrom.sql +++ b/db/im-platfrom.sql @@ -57,9 +57,10 @@ 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 '创建时间', diff --git a/im-platform/src/main/java/com/bx/implatform/entity/GroupMember.java b/im-platform/src/main/java/com/bx/implatform/entity/GroupMember.java index ef6d7a3..38a8e07 100644 --- a/im-platform/src/main/java/com/bx/implatform/entity/GroupMember.java +++ b/im-platform/src/main/java/com/bx/implatform/entity/GroupMember.java @@ -1,5 +1,6 @@ package com.bx.implatform.entity; +import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.extension.activerecord.Model; @@ -38,9 +39,14 @@ public class GroupMember extends Model { private Long userId; /** - * 组内显示名称 + * 用户昵称 */ - private String aliasName; + private String userNickName; + + /** + * 显示昵称备注 + */ + private String remarkNickName; /** * 用户头像 @@ -48,9 +54,9 @@ public class GroupMember extends Model { private String headImage; /** - * 备注 + * 显示群名备注 */ - private String remark; + private String remarkGroupName; /** * 是否已退出 @@ -67,5 +73,8 @@ public class GroupMember extends Model { */ private Date quitTime; + public String getShowNickName() { + return StrUtil.isEmpty(remarkNickName) ? userNickName : remarkNickName; + } } diff --git a/im-platform/src/main/java/com/bx/implatform/service/impl/GroupMessageServiceImpl.java b/im-platform/src/main/java/com/bx/implatform/service/impl/GroupMessageServiceImpl.java index b29ec6b..5338a26 100644 --- a/im-platform/src/main/java/com/bx/implatform/service/impl/GroupMessageServiceImpl.java +++ b/im-platform/src/main/java/com/bx/implatform/service/impl/GroupMessageServiceImpl.java @@ -71,7 +71,7 @@ public class GroupMessageServiceImpl extends ServiceImpl !session.getUserId().equals(uid)).collect(Collectors.toList()); GroupMessageVO msgInfo = BeanUtils.copyProperties(msg, GroupMessageVO.class); msgInfo.setType(MessageType.RECALL.code()); - String content = String.format("'%s'撤回了一条消息", member.getAliasName()); + String content = String.format("'%s'撤回了一条消息", member.getShowNickName()); msgInfo.setContent(content); msgInfo.setSendTime(new Date()); diff --git a/im-platform/src/main/java/com/bx/implatform/service/impl/GroupServiceImpl.java b/im-platform/src/main/java/com/bx/implatform/service/impl/GroupServiceImpl.java index 46d50e2..955ab54 100644 --- a/im-platform/src/main/java/com/bx/implatform/service/impl/GroupServiceImpl.java +++ b/im-platform/src/main/java/com/bx/implatform/service/impl/GroupServiceImpl.java @@ -30,7 +30,6 @@ import com.bx.implatform.vo.GroupMessageVO; import com.bx.implatform.vo.GroupVO; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; import org.springframework.cache.annotation.CacheConfig; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; @@ -62,17 +61,17 @@ public class GroupServiceImpl extends ServiceImpl implements group.setOwnerId(user.getId()); this.save(group); // 把群主加入群 - GroupMember groupMember = new GroupMember(); - groupMember.setGroupId(group.getId()); - groupMember.setUserId(user.getId()); - groupMember.setHeadImage(user.getHeadImageThumb()); - groupMember.setAliasName(StringUtils.isEmpty(vo.getAliasName()) ? session.getNickName() : vo.getAliasName()); - groupMember.setRemark(StringUtils.isEmpty(vo.getRemark()) ? group.getName() : vo.getRemark()); - groupMemberService.save(groupMember); - + GroupMember member = new GroupMember(); + member.setGroupId(group.getId()); + member.setUserId(user.getId()); + member.setHeadImage(user.getHeadImageThumb()); + member.setRemarkNickName(vo.getRemarkNickName()); + member.setRemarkGroupName(vo.getRemarkGroupName()); + groupMemberService.save(member); + // 返回 vo.setId(group.getId()); - vo.setAliasName(groupMember.getAliasName()); - vo.setRemark(groupMember.getRemark()); + vo.setShowNickName(StrUtil.isEmpty(vo.getRemarkNickName()) ? member.getUserNickName() : vo.getRemarkNickName()); + vo.setShowGroupName(StrUtil.isEmpty(vo.getRemarkGroupName()) ? group.getName() : vo.getRemarkGroupName()); log.info("创建群聊,群聊id:{},群聊名称:{}", group.getId(), group.getName()); return vo; } @@ -84,19 +83,21 @@ public class GroupServiceImpl extends ServiceImpl implements UserSession session = SessionContext.getSession(); // 校验是不是群主,只有群主能改信息 Group group = this.getAndCheckById(vo.getId()); - // 群主有权修改群基本信息 - if (group.getOwnerId().equals(session.getUserId())) { - group = BeanUtils.copyProperties(vo, Group.class); - this.updateById(group); - } // 更新成员信息 GroupMember member = groupMemberService.findByGroupAndUserId(vo.getId(), session.getUserId()); if (Objects.isNull(member) || member.getQuit()) { - throw new GlobalException( "您不是群聊的成员"); + throw new GlobalException("您不是群聊的成员"); } - member.setAliasName(StringUtils.isEmpty(vo.getAliasName()) ? session.getNickName() : vo.getAliasName()); - member.setRemark(StringUtils.isEmpty(vo.getRemark()) ? Objects.requireNonNull(group).getName() : vo.getRemark()); + member.setRemarkNickName(vo.getRemarkNickName()); + member.setRemarkGroupName(vo.getRemarkGroupName()); groupMemberService.updateById(member); + // 群主有权修改群基本信息 + if (group.getOwnerId().equals(session.getUserId())) { + group = BeanUtils.copyProperties(vo, Group.class); + this.updateById(group); + } + vo.setShowNickName(StrUtil.isEmpty(vo.getRemarkNickName()) ? member.getUserNickName() : vo.getRemarkNickName()); + vo.setShowGroupName(StrUtil.isEmpty(vo.getRemarkGroupName()) ? group.getName() : vo.getRemarkGroupName()); log.info("修改群聊,群聊id:{},群聊名称:{}", group.getId(), group.getName()); return vo; } @@ -121,7 +122,7 @@ public class GroupServiceImpl extends ServiceImpl implements String key = StrUtil.join(":", RedisKey.IM_GROUP_READED_POSITION, groupId); redisTemplate.delete(key); // 推送解散群聊提示 - this.sendTipMessage(groupId,userIds,String.format("'%s'解散了群聊",session.getNickName())); + this.sendTipMessage(groupId, userIds, String.format("'%s'解散了群聊", session.getNickName())); log.info("删除群聊,群聊id:{},群聊名称:{}", group.getId(), group.getName()); } @@ -130,15 +131,15 @@ public class GroupServiceImpl extends ServiceImpl implements Long userId = SessionContext.getSession().getUserId(); Group group = this.getById(groupId); if (group.getOwnerId().equals(userId)) { - throw new GlobalException( "您是群主,不可退出群聊"); + throw new GlobalException("您是群主,不可退出群聊"); } // 删除群聊成员 groupMemberService.removeByGroupAndUserId(groupId, userId); // 清理已读缓存 String key = StrUtil.join(":", RedisKey.IM_GROUP_READED_POSITION, groupId); - redisTemplate.opsForHash().delete(key,userId.toString()); + redisTemplate.opsForHash().delete(key, userId.toString()); // 推送退出群聊提示 - this.sendTipMessage(groupId,Arrays.asList(userId),"您已退出群聊"); + this.sendTipMessage(groupId, Arrays.asList(userId), "您已退出群聊"); log.info("退出群聊,群聊id:{},群聊名称:{},用户id:{}", group.getId(), group.getName(), userId); } @@ -147,18 +148,18 @@ public class GroupServiceImpl extends ServiceImpl implements UserSession session = SessionContext.getSession(); Group group = this.getAndCheckById(groupId); if (!group.getOwnerId().equals(session.getUserId())) { - throw new GlobalException( "您不是群主,没有权限踢人"); + throw new GlobalException("您不是群主,没有权限踢人"); } if (userId.equals(session.getUserId())) { - throw new GlobalException( "亲,不能移除自己哟"); + throw new GlobalException("亲,不能移除自己哟"); } // 删除群聊成员 groupMemberService.removeByGroupAndUserId(groupId, userId); // 清理已读缓存 String key = StrUtil.join(":", RedisKey.IM_GROUP_READED_POSITION, groupId); - redisTemplate.opsForHash().delete(key,userId.toString()); + redisTemplate.opsForHash().delete(key, userId.toString()); // 推送踢出群聊提示 - this.sendTipMessage(groupId,Arrays.asList(userId),"您已被移出群聊"); + this.sendTipMessage(groupId, Arrays.asList(userId), "您已被移出群聊"); log.info("踢出群聊,群聊id:{},群聊名称:{},用户id:{}", group.getId(), group.getName(), userId); } @@ -167,15 +168,17 @@ public class GroupServiceImpl extends ServiceImpl implements UserSession session = SessionContext.getSession(); Group group = super.getById(groupId); if (Objects.isNull(group)) { - throw new GlobalException( "群组不存在"); + throw new GlobalException("群组不存在"); } GroupMember member = groupMemberService.findByGroupAndUserId(groupId, session.getUserId()); if (Objects.isNull(member)) { - throw new GlobalException( "您未加入群聊"); + throw new GlobalException("您未加入群聊"); } GroupVO vo = BeanUtils.copyProperties(group, GroupVO.class); - vo.setAliasName(member.getAliasName()); - vo.setRemark(member.getRemark()); + vo.setRemarkGroupName(member.getRemarkGroupName()); + vo.setRemarkNickName(member.getRemarkNickName()); + vo.setShowNickName(member.getShowNickName()); + vo.setShowGroupName(StrUtil.isEmpty(vo.getRemarkGroupName()) ? group.getName() : vo.getRemarkGroupName()); vo.setQuit(member.getQuit()); return vo; } @@ -185,13 +188,13 @@ public class GroupServiceImpl extends ServiceImpl implements public Group getAndCheckById(Long groupId) { Group group = super.getById(groupId); if (Objects.isNull(group)) { - throw new GlobalException( "群组不存在"); + throw new GlobalException("群组不存在"); } if (group.getDeleted()) { - throw new GlobalException( "群组'" + group.getName() + "'已解散"); + throw new GlobalException("群组'" + group.getName() + "'已解散"); } if (group.getIsBanned()) { - throw new GlobalException( "群组'" + group.getName() + "'已被封禁,原因:"+group.getReason()); + throw new GlobalException("群组'" + group.getName() + "'已被封禁,原因:" + group.getReason()); } return group; } @@ -215,8 +218,9 @@ public class GroupServiceImpl extends ServiceImpl implements return groups.stream().map(g -> { GroupVO vo = BeanUtils.copyProperties(g, GroupVO.class); GroupMember member = groupMembers.stream().filter(m -> g.getId().equals(m.getGroupId())).findFirst().get(); - vo.setAliasName(member.getAliasName()); - vo.setRemark(member.getRemark()); + vo.setShowNickName( + StrUtil.isEmpty(vo.getRemarkNickName()) ? session.getNickName() : vo.getRemarkNickName()); + vo.setShowGroupName(StrUtil.isEmpty(vo.getRemarkGroupName()) ? g.getName() : vo.getRemarkGroupName()); vo.setQuit(member.getQuit()); return vo; }).collect(Collectors.toList()); @@ -238,19 +242,20 @@ public class GroupServiceImpl extends ServiceImpl implements } // 找出好友信息 List friends = friendsService.findFriendByUserId(session.getUserId()); - List friendsList = vo.getFriendIds().stream().map(id -> friends.stream().filter(f -> f.getFriendId().equals(id)).findFirst().get()) - .collect(Collectors.toList()); + List friendsList = vo.getFriendIds().stream() + .map(id -> friends.stream().filter(f -> f.getFriendId().equals(id)).findFirst().get()) + .collect(Collectors.toList()); if (friendsList.size() != vo.getFriendIds().size()) { - throw new GlobalException( "部分用户不是您的好友,邀请失败"); + throw new GlobalException("部分用户不是您的好友,邀请失败"); } // 批量保存成员数据 List groupMembers = friendsList.stream().map(f -> { - Optional optional = members.stream().filter(m -> m.getUserId().equals(f.getFriendId())).findFirst(); + Optional optional = + members.stream().filter(m -> m.getUserId().equals(f.getFriendId())).findFirst(); GroupMember groupMember = optional.orElseGet(GroupMember::new); groupMember.setGroupId(vo.getGroupId()); groupMember.setUserId(f.getFriendId()); - groupMember.setAliasName(f.getFriendNickName()); - groupMember.setRemark(group.getName()); + groupMember.setUserNickName(f.getFriendNickName()); groupMember.setHeadImage(f.getFriendHeadImage()); groupMember.setCreatedTime(new Date()); groupMember.setQuit(false); @@ -261,25 +266,29 @@ public class GroupServiceImpl extends ServiceImpl implements } // 推送进入群聊消息 List userIds = groupMemberService.findUserIdsByGroupId(vo.getGroupId()); - String memberNames = groupMembers.stream().map(GroupMember::getAliasName).collect(Collectors.joining(",")); - String content = String.format("'%s'邀请'%s'加入了群聊",session.getNickName(), memberNames); - this.sendTipMessage(vo.getGroupId(),userIds,content); - log.info("邀请进入群聊,群聊id:{},群聊名称:{},被邀请用户id:{}", group.getId(), group.getName(), vo.getFriendIds()); + String memberNames = groupMembers.stream().map(GroupMember::getShowNickName).collect(Collectors.joining(",")); + String content = String.format("'%s'邀请'%s'加入了群聊", session.getNickName(), memberNames); + this.sendTipMessage(vo.getGroupId(), userIds, content); + log.info("邀请进入群聊,群聊id:{},群聊名称:{},被邀请用户id:{}", group.getId(), group.getName(), + vo.getFriendIds()); } @Override public List findGroupMembers(Long groupId) { + Group group = getAndCheckById(groupId); List members = groupMemberService.findByGroupId(groupId); List userIds = members.stream().map(GroupMember::getUserId).collect(Collectors.toList()); List onlineUserIds = imClient.getOnlineUser(userIds); return members.stream().map(m -> { GroupMemberVO vo = BeanUtils.copyProperties(m, GroupMemberVO.class); + vo.setShowNickName(m.getShowNickName()); + vo.setShowGroupName(StrUtil.isEmpty(m.getRemarkGroupName()) ? group.getName() : m.getRemarkGroupName()); vo.setOnline(onlineUserIds.contains(m.getUserId())); return vo; }).sorted((m1, m2) -> m2.getOnline().compareTo(m1.getOnline())).collect(Collectors.toList()); } - private void sendTipMessage(Long groupId,List recvIds,String content){ + private void sendTipMessage(Long groupId, List recvIds, String content) { UserSession session = SessionContext.getSession(); // 消息入库 GroupMessage message = new GroupMessage(); @@ -293,14 +302,14 @@ public class GroupServiceImpl extends ServiceImpl implements message.setRecvIds(CommaTextUtils.asText(recvIds)); groupMessageMapper.insert(message); // 推送 - GroupMessageVO msgInfo = BeanUtils.copyProperties(message,GroupMessageVO.class); + GroupMessageVO msgInfo = BeanUtils.copyProperties(message, GroupMessageVO.class); IMGroupMessage sendMessage = new IMGroupMessage<>(); sendMessage.setSender(new IMUserInfo(session.getUserId(), session.getTerminal())); - if(CollUtil.isEmpty(recvIds)){ + if (CollUtil.isEmpty(recvIds)) { // 为空表示向全体发送 List userIds = groupMemberService.findUserIdsByGroupId(groupId); sendMessage.setRecvIds(userIds); - }else{ + } else { sendMessage.setRecvIds(recvIds); } sendMessage.setData(msgInfo); diff --git a/im-platform/src/main/java/com/bx/implatform/service/impl/UserServiceImpl.java b/im-platform/src/main/java/com/bx/implatform/service/impl/UserServiceImpl.java index 91f553f..3b4dd3c 100644 --- a/im-platform/src/main/java/com/bx/implatform/service/impl/UserServiceImpl.java +++ b/im-platform/src/main/java/com/bx/implatform/service/impl/UserServiceImpl.java @@ -2,7 +2,7 @@ package com.bx.implatform.service.impl; import com.alibaba.fastjson.JSON; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.bx.imclient.IMClient; @@ -147,24 +147,20 @@ public class UserServiceImpl extends ServiceImpl implements Us if (Objects.isNull(user)) { throw new GlobalException("用户不存在"); } - // 更新好友昵称和头像 + if (!user.getNickName().equals(vo.getNickName()) || !user.getHeadImageThumb().equals(vo.getHeadImageThumb())) { - QueryWrapper queryWrapper = new QueryWrapper<>(); - queryWrapper.lambda().eq(Friend::getFriendId, session.getUserId()); - List friends = friendService.list(queryWrapper); - for (Friend friend : friends) { - friend.setFriendNickName(vo.getNickName()); - friend.setFriendHeadImage(vo.getHeadImageThumb()); - } - friendService.updateBatchById(friends); - } - // 更新群聊中的头像 - if (!user.getHeadImageThumb().equals(vo.getHeadImageThumb())) { - List members = groupMemberService.findByUserId(session.getUserId()); - for (GroupMember member : members) { - member.setHeadImage(vo.getHeadImageThumb()); - } - groupMemberService.updateBatchById(members); + // 更新好友昵称和头像 + LambdaUpdateWrapper wrapper1 = Wrappers.lambdaUpdate(); + wrapper1.eq(Friend::getFriendId, session.getUserId()); + wrapper1.set(Friend::getFriendNickName,vo.getNickName()); + wrapper1.set(Friend::getFriendHeadImage,vo.getHeadImageThumb()); + friendService.update(wrapper1); + // 更新群聊中的昵称和头像 + LambdaUpdateWrapper wrapper2 = Wrappers.lambdaUpdate(); + wrapper2.eq(GroupMember::getUserId, session.getUserId()); + wrapper2.set(GroupMember::getHeadImage,vo.getHeadImageThumb()); + wrapper2.set(GroupMember::getUserNickName,vo.getNickName()); + groupMemberService.update(wrapper2); } // 更新用户信息 user.setNickName(vo.getNickName()); diff --git a/im-platform/src/main/java/com/bx/implatform/service/impl/WebrtcGroupServiceImpl.java b/im-platform/src/main/java/com/bx/implatform/service/impl/WebrtcGroupServiceImpl.java index b27190f..551e162 100644 --- a/im-platform/src/main/java/com/bx/implatform/service/impl/WebrtcGroupServiceImpl.java +++ b/im-platform/src/main/java/com/bx/implatform/service/impl/WebrtcGroupServiceImpl.java @@ -40,7 +40,7 @@ import java.util.stream.Collectors; /** * 群语音通话服务类,所有涉及修改webtcSession的方法都要挂分布式锁 * - * @author: blue + * @author: blue * @date: 2024-06-01 * @version: 1.0 */ @@ -197,7 +197,7 @@ public class WebrtcGroupServiceImpl implements WebrtcGroupService { vo.setReason(dto.getReason()); List recvIds = getRecvIds(userInfos); sendRtcMessage1(MessageType.RTC_GROUP_FAILED, dto.getGroupId(), recvIds, JSON.toJSONString(vo), false); - log.info("群通话失败,userId:{},groupId:{},原因:{}", userSession.getUserId(),dto.getGroupId(), dto.getReason()); + log.info("群通话失败,userId:{},groupId:{},原因:{}", userSession.getUserId(), dto.getGroupId(), dto.getReason()); } @OnlineCheck @@ -219,7 +219,7 @@ public class WebrtcGroupServiceImpl implements WebrtcGroupService { } WebrtcUserInfo userInfo = new WebrtcUserInfo(); userInfo.setId(userSession.getUserId()); - userInfo.setNickName(member.getAliasName()); + userInfo.setNickName(member.getShowNickName()); userInfo.setHeadImage(member.getHeadImage()); // 默认是开启麦克风,关闭摄像头 userInfo.setIsCamera(false); @@ -455,7 +455,7 @@ public class WebrtcGroupServiceImpl implements WebrtcGroupService { GroupMember member = groupMemberService.findByGroupAndUserId(groupId, hostId); host = new WebrtcUserInfo(); host.setId(hostId); - host.setNickName(member.getAliasName()); + host.setNickName(member.getShowNickName()); host.setHeadImage(member.getHeadImage()); } vo.setHost(host); diff --git a/im-platform/src/main/java/com/bx/implatform/vo/GroupMemberVO.java b/im-platform/src/main/java/com/bx/implatform/vo/GroupMemberVO.java index e5f4192..fd2a876 100644 --- a/im-platform/src/main/java/com/bx/implatform/vo/GroupMemberVO.java +++ b/im-platform/src/main/java/com/bx/implatform/vo/GroupMemberVO.java @@ -11,7 +11,10 @@ public class GroupMemberVO { private Long userId; @Schema(description = "群内显示名称") - private String aliasName; + private String showNickName; + + @Schema(description = "群内昵称备注") + private String remarkNickName; @Schema(description = "头像") private String headImage; @@ -22,7 +25,10 @@ public class GroupMemberVO { @Schema(description = "是否在线") private Boolean online; - @Schema(description = "备注") - private String remark; + @Schema(description = "群名显示名称") + private String showGroupName; + + @Schema(description = "群名备注") + private String remarkGroupName; } diff --git a/im-platform/src/main/java/com/bx/implatform/vo/GroupVO.java b/im-platform/src/main/java/com/bx/implatform/vo/GroupVO.java index 4743b1a..002c4c4 100644 --- a/im-platform/src/main/java/com/bx/implatform/vo/GroupVO.java +++ b/im-platform/src/main/java/com/bx/implatform/vo/GroupVO.java @@ -30,13 +30,19 @@ public class GroupVO { @Schema(description = "群公告") private String notice; - @Length(max = 20, message = "群聊显示长度不能大于20") + @Length(max = 20, message = "显示昵称长度不能大于20") @Schema(description = "用户在群显示昵称") - private String aliasName; + private String remarkNickName; - @Length(max = 20, message = "群聊显示长度不能大于20") - @Schema(description = "群聊显示备注") - private String remark; + @Schema(description = "群内显示名称") + private String showNickName; + + @Schema(description = "群名显示名称") + private String showGroupName; + + @Length(max = 20, message = "群备注长度不能大于20") + @Schema(description = "群名备注") + private String remarkGroupName; @Schema(description = "是否已删除") private Boolean deleted; diff --git a/im-ui/src/components/chat/ChatAtBox.vue b/im-ui/src/components/chat/ChatAtBox.vue index 3b07c30..7c371c9 100644 --- a/im-ui/src/components/chat/ChatAtBox.vue +++ b/im-ui/src/components/chat/ChatAtBox.vue @@ -46,11 +46,11 @@ if (this.ownerId == userId && name.startsWith(this.searchText)) { this.showMembers.push({ userId: -1, - aliasName: name + showNickName: name }) } this.members.forEach((m) => { - if (m.userId != userId && !m.quit && m.aliasName.startsWith(this.searchText)) { + if (m.userId != userId && !m.quit && m.showNickName.startsWith(this.searchText)) { this.showMembers.push(m); } }) diff --git a/im-ui/src/components/chat/ChatBox.vue b/im-ui/src/components/chat/ChatBox.vue index 1910f50..97b3678 100644 --- a/im-ui/src/components/chat/ChatBox.vue +++ b/im-ui/src/components/chat/ChatBox.vue @@ -238,7 +238,7 @@ element.className = "at" element.dataset.id = member.userId; element.contentEditable = 'false' - element.innerText = `@${member.aliasName}` + element.innerText = `@${member.showNickName}` range.insertNode(element) // 光标移动到末尾 range.collapse() @@ -495,7 +495,7 @@ members.forEach(m => { userInfos.push({ id: m.userId, - nickName: m.aliasName, + nickName: m.showNickName, headImage: m.headImage, isCamera: false, isMicroPhone: true @@ -701,7 +701,7 @@ showName(msgInfo) { if (this.chat.type == 'GROUP') { let member = this.groupMembers.find((m) => m.userId == msgInfo.sendId); - return member ? member.aliasName : ""; + return member ? member.showNickName : ""; } else { return msgInfo.sendId == this.mine.id ? this.mine.nickName : this.chat.showName } diff --git a/im-ui/src/components/chat/ChatGroupMember.vue b/im-ui/src/components/chat/ChatGroupMember.vue index 589d346..f1ae8d7 100644 --- a/im-ui/src/components/chat/ChatGroupMember.vue +++ b/im-ui/src/components/chat/ChatGroupMember.vue @@ -1,10 +1,10 @@ diff --git a/im-ui/src/components/chat/ChatGroupSide.vue b/im-ui/src/components/chat/ChatGroupSide.vue index 44e1635..a245b7b 100644 --- a/im-ui/src/components/chat/ChatGroupSide.vue +++ b/im-ui/src/components/chat/ChatGroupSide.vue @@ -16,7 +16,7 @@ @close="showAddGroupMember=false">

-
@@ -32,12 +32,12 @@ - + - + -
提交 编辑 @@ -119,7 +119,7 @@ computed: { ownerName() { let member = this.groupMembers.find((m) => m.userId == this.group.ownerId); - return member && member.aliasName; + return member && member.showNickName; }, isOwner() { return this.group.ownerId == this.$store.state.userStore.userInfo.id; diff --git a/im-ui/src/components/chat/ChatHistory.vue b/im-ui/src/components/chat/ChatHistory.vue index 3dde34c..4d89e8c 100644 --- a/im-ui/src/components/chat/ChatHistory.vue +++ b/im-ui/src/components/chat/ChatHistory.vue @@ -98,7 +98,7 @@ showName(msgInfo) { if (this.chat.type == 'GROUP') { let member = this.groupMembers.find((m) => m.userId == msgInfo.sendId); - return member ? member.aliasName : ""; + return member ? member.showNickName : ""; } else { return msgInfo.sendId == this.mine.id ? this.mine.nickName : this.chat.showName } diff --git a/im-ui/src/components/group/AddGroupMember.vue b/im-ui/src/components/group/AddGroupMember.vue index d41f815..672d187 100644 --- a/im-ui/src/components/group/AddGroupMember.vue +++ b/im-ui/src/components/group/AddGroupMember.vue @@ -8,7 +8,7 @@
-
- +
-
{{group.remark}}
+
{{group.showGroupName}}
diff --git a/im-ui/src/components/group/GroupMember.vue b/im-ui/src/components/group/GroupMember.vue index 2a5f848..21dfac8 100644 --- a/im-ui/src/components/group/GroupMember.vue +++ b/im-ui/src/components/group/GroupMember.vue @@ -1,11 +1,11 @@ diff --git a/im-ui/src/components/group/GroupMemberItem.vue b/im-ui/src/components/group/GroupMemberItem.vue index 1af7943..20e13bd 100644 --- a/im-ui/src/components/group/GroupMemberItem.vue +++ b/im-ui/src/components/group/GroupMemberItem.vue @@ -1,11 +1,11 @@ - + @@ -82,7 +82,7 @@ let chat = { type: 'GROUP', targetId: this.group.id, - showName: this.group.remark, + showName: this.group.showGroupName, headImage: this.group.headImage, }; this.$store.commit("openChat", chat); @@ -177,7 +177,7 @@ computed: { ownerName() { let member = this.groupMembers.find((m) => m.userId == this.group.ownerId); - return member && member.aliasName; + return member && member.showNickName; }, isOwner() { return this.group.ownerId == this.$store.state.userStore.userInfo.id; diff --git a/im-uniapp/pages/group/group-invite.vue b/im-uniapp/pages/group/group-invite.vue index f1a6980..700e572 100644 --- a/im-uniapp/pages/group/group-invite.vue +++ b/im-uniapp/pages/group/group-invite.vue @@ -1,11 +1,11 @@