diff --git a/.gitignore b/.gitignore index 1aa5cb5..79e1582 100644 --- a/.gitignore +++ b/.gitignore @@ -2,8 +2,8 @@ /box-im.iml /im-server/im-server.iml /im-platform/im-platform.iml -/commom/commom.iml /im-platform/src/main/resources/application-prod.yml /im-platform/src/main/resources/logback-prod.xml /im-server/src/main/resources/application-prod.yml /im-server/src/main/resources/logback-prod.xml +/im-commom/im-commom.iml diff --git a/commom/src/main/java/com/bx/common/contant/RedisKey.java b/commom/src/main/java/com/bx/common/contant/RedisKey.java deleted file mode 100644 index ff0c04d..0000000 --- a/commom/src/main/java/com/bx/common/contant/RedisKey.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.bx.common.contant; - -public class RedisKey { - - // im-server最大id,从0开始递增 - public final static String IM_MAX_SERVER_ID = "im:max_server_id"; - // 用户ID所连接的IM-server的ID - public final static String IM_USER_SERVER_ID = "im:user:server_id:"; - // 未读私聊消息队列 - public final static String IM_UNREAD_PRIVATE_MESSAGE = "im:unread:private:"; - // 未读群聊消息队列 - public final static String IM_UNREAD_GROUP_MESSAGE = "im:unread:group:"; - // 已读私聊消息id队列 - public final static String IM_READED_PRIVATE_MESSAGE_ID = "im:readed:private:id"; - // 已读群聊消息位置(已读最大id) - public final static String IM_GROUP_READED_POSITION = "im:readed:group:position:"; - // 缓存前缀 - public final static String IM_CACHE = "im:cache:"; - // 缓存是否好友:bool - public final static String IM_CACHE_FRIEND = IM_CACHE+"friend"; - // 缓存群聊信息 - public final static String IM_CACHE_GROUP = IM_CACHE+"group"; - // 缓存群聊成员id - public final static String IM_CACHE_GROUP_MEMBER_ID = IM_CACHE+"group_member_ids"; - -} diff --git a/commom/src/main/java/com/bx/common/enums/MessageTypeEnum.java b/commom/src/main/java/com/bx/common/enums/MessageTypeEnum.java deleted file mode 100644 index de53313..0000000 --- a/commom/src/main/java/com/bx/common/enums/MessageTypeEnum.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.bx.common.enums; - - -public enum MessageTypeEnum { - - TEXT(0,"文字"), - FILE(1,"文件"), - IMAGE(2,"图片"), - VIDEO(3,"视频"), - TIP(10,"系统提示"); - - private Integer code; - - private String desc; - - MessageTypeEnum(Integer index, String desc) { - this.code =index; - this.desc=desc; - } - - public static MessageTypeEnum fromCode(Integer code){ - for (MessageTypeEnum typeEnum:values()) { - if (typeEnum.code.equals(code)) { - return typeEnum; - } - } - return null; - } - - - public String getDesc() { - return desc; - } - - public Integer getCode(){ - return this.code; - } -} diff --git a/commom/src/main/java/com/bx/common/model/im/GroupMessageInfo.java b/commom/src/main/java/com/bx/common/model/im/GroupMessageInfo.java deleted file mode 100644 index caaab76..0000000 --- a/commom/src/main/java/com/bx/common/model/im/GroupMessageInfo.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.bx.common.model.im; - -import lombok.Data; - -import java.util.Date; -import java.util.List; - -@Data -public class GroupMessageInfo { - - private Long id; - - private Long groupId; - - private Long sendId; - - private List recvIds; - - private String content; - - private Integer type; - - private Date sendTime; -} diff --git a/commom/src/main/java/com/bx/common/model/im/PrivateMessageInfo.java b/commom/src/main/java/com/bx/common/model/im/PrivateMessageInfo.java deleted file mode 100644 index fbc4b49..0000000 --- a/commom/src/main/java/com/bx/common/model/im/PrivateMessageInfo.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.bx.common.model.im; - -import lombok.Data; - -import java.util.Date; - -@Data -public class PrivateMessageInfo { - - private long id; - - private Long sendId; - - private Long recvId; - - private String content; - - private Integer type; - - private Date sendTime; -} diff --git a/commom/src/main/java/com/bx/common/model/im/SendInfo.java b/commom/src/main/java/com/bx/common/model/im/SendInfo.java deleted file mode 100644 index 794c154..0000000 --- a/commom/src/main/java/com/bx/common/model/im/SendInfo.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.bx.common.model.im; - -import lombok.Data; - -@Data -public class SendInfo { - - private Integer cmd; - private T data; - -} diff --git a/im-client/pom.xml b/im-client/pom.xml new file mode 100644 index 0000000..9ef288f --- /dev/null +++ b/im-client/pom.xml @@ -0,0 +1,26 @@ + + + + box-im + com.bx + 1.1.0 + + 4.0.0 + + im-client + + + + com.bx + im-commom + 1.1.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/IMAutoConfiguration.java b/im-client/src/main/java/com/bx/imclient/IMAutoConfiguration.java new file mode 100644 index 0000000..b3a93fc --- /dev/null +++ b/im-client/src/main/java/com/bx/imclient/IMAutoConfiguration.java @@ -0,0 +1,12 @@ +package com.bx.imclient; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + + +@Slf4j +@Configuration +@ComponentScan("com.bx.imclient") +public class IMAutoConfiguration { +} diff --git a/im-client/src/main/java/com/bx/imclient/IMClient.java b/im-client/src/main/java/com/bx/imclient/IMClient.java new file mode 100644 index 0000000..68b6b74 --- /dev/null +++ b/im-client/src/main/java/com/bx/imclient/IMClient.java @@ -0,0 +1,41 @@ +package com.bx.imclient; + +import com.bx.imclient.listener.MessageListenerMulticaster; +import com.bx.imclient.sender.IMSender; +import com.bx.imcommon.model.GroupMessageInfo; +import com.bx.imcommon.model.PrivateMessageInfo; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; + +import java.util.List; + +@Configuration +public class IMClient { + + @Autowired + private MessageListenerMulticaster listenerMulticaster; + @Autowired + private IMSender imSender; + + /** + * 发送私聊消息 + * + * @param recvId 接收用户id + * @param messageInfo 消息体,将转成json发送到客户端 + */ + public void sendPrivateMessage(Long recvId, PrivateMessageInfo... messageInfo){ + imSender.sendPrivateMessage(recvId,messageInfo); + } + + /** + * 发送群聊消息 + * + * @param recvIds 群聊用户id列表 + * @param messageInfo 消息体,将转成json发送到客户端 + */ + public void sendGroupMessage(List recvIds, GroupMessageInfo... messageInfo){ + imSender.sendGroupMessage(recvIds,messageInfo); + } + + +} diff --git a/im-client/src/main/java/com/bx/imclient/annotation/IMListener.java b/im-client/src/main/java/com/bx/imclient/annotation/IMListener.java new file mode 100644 index 0000000..4f54bf9 --- /dev/null +++ b/im-client/src/main/java/com/bx/imclient/annotation/IMListener.java @@ -0,0 +1,18 @@ +package com.bx.imclient.annotation; + +import com.bx.imcommon.enums.IMListenerType; +import org.springframework.stereotype.Component; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.TYPE,ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +@Component +public @interface IMListener { + + IMListenerType type(); + +} 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 new file mode 100644 index 0000000..23fac7c --- /dev/null +++ b/im-client/src/main/java/com/bx/imclient/config/RedisConfig.java @@ -0,0 +1,49 @@ +package com.bx.imclient.config; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +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.Jackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +import javax.annotation.Resource; + +@Configuration("IMRedisConfig") +public class RedisConfig { + + @Resource + private RedisConnectionFactory factory; + + @Bean("IMRedisTemplate") + public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) { + RedisTemplate redisTemplate = new RedisTemplate(); + redisTemplate.setConnectionFactory(redisConnectionFactory); + // 设置值(value)的序列化采用jackson2JsonRedisSerializer + redisTemplate.setValueSerializer(jackson2JsonRedisSerializer()); + redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer()); + // 设置键(key)的序列化采用StringRedisSerializer。 + redisTemplate.setKeySerializer(new StringRedisSerializer()); + redisTemplate.setHashKeySerializer(new StringRedisSerializer()); + redisTemplate.afterPropertiesSet(); + return redisTemplate; + } + + @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.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); + jackson2JsonRedisSerializer.setObjectMapper(om); + return jackson2JsonRedisSerializer; + } + +} diff --git a/im-client/src/main/java/com/bx/imclient/listener/MessageListener.java b/im-client/src/main/java/com/bx/imclient/listener/MessageListener.java new file mode 100644 index 0000000..95ae871 --- /dev/null +++ b/im-client/src/main/java/com/bx/imclient/listener/MessageListener.java @@ -0,0 +1,10 @@ +package com.bx.imclient.listener; + + +import com.bx.imcommon.model.SendResult; + +public interface MessageListener { + + void process(SendResult result); + +} diff --git a/im-client/src/main/java/com/bx/imclient/listener/MessageListenerMulticaster.java b/im-client/src/main/java/com/bx/imclient/listener/MessageListenerMulticaster.java new file mode 100644 index 0000000..8d91a3c --- /dev/null +++ b/im-client/src/main/java/com/bx/imclient/listener/MessageListenerMulticaster.java @@ -0,0 +1,27 @@ +package com.bx.imclient.listener; + + +import com.bx.imclient.annotation.IMListener; +import com.bx.imcommon.enums.IMListenerType; +import com.bx.imcommon.model.SendResult; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.Collections; +import java.util.List; + +@Component +public class MessageListenerMulticaster { + + @Autowired(required = false) + private List messageListeners = Collections.emptyList(); + + public void multicast(IMListenerType type, SendResult result){ + for(MessageListener listener:messageListeners){ + IMListener annotation = listener.getClass().getAnnotation(IMListener.class); + if(annotation!=null && (annotation.type().equals(IMListenerType.ALL) || annotation.type().equals(type))){ + listener.process(result); + } + } + } +} 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 new file mode 100644 index 0000000..b6b0de5 --- /dev/null +++ b/im-client/src/main/java/com/bx/imclient/sender/IMSender.java @@ -0,0 +1,112 @@ +package com.bx.imclient.sender; + +import com.bx.imclient.listener.MessageListenerMulticaster; +import com.bx.imcommon.contant.RedisKey; +import com.bx.imcommon.enums.IMCmdType; +import com.bx.imcommon.enums.IMListenerType; +import com.bx.imcommon.enums.IMSendStatus; +import com.bx.imcommon.model.GroupMessageInfo; +import com.bx.imcommon.model.IMRecvInfo; +import com.bx.imcommon.model.PrivateMessageInfo; +import com.bx.imcommon.model.SendResult; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Service; + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +@Service +public class IMSender { + + @Autowired + @Qualifier("IMRedisTemplate") + private RedisTemplate redisTemplate; + + @Autowired + private MessageListenerMulticaster listenerMulticaster; + + public void sendPrivateMessage(Long recvId, PrivateMessageInfo... messageInfos){ + // 获取对方连接的channelId + String key = RedisKey.IM_USER_SERVER_ID + recvId; + Integer serverId = (Integer) redisTemplate.opsForValue().get(key); + // 如果对方在线,将数据存储至redis,等待拉取推送 + if (serverId != null) { + String sendKey = RedisKey.IM_UNREAD_PRIVATE_QUEUE + serverId; + IMRecvInfo[] recvInfos = new IMRecvInfo[messageInfos.length]; + for (int i=0;i recvInfo = new IMRecvInfo<>(); + recvInfo.setCmd(IMCmdType.PRIVATE_MESSAGE.code()); + List recvIds = new LinkedList(); + recvIds.add(recvId); + recvInfo.setRecvIds(recvIds); + recvInfo.setData(messageInfos[i]); + recvInfos[i] = recvInfo; + } + redisTemplate.opsForList().rightPushAll(sendKey, recvInfos); + }else{ + // 回复消息状态 + for(PrivateMessageInfo messageInfo : messageInfos ) { + SendResult result = new SendResult(); + result.setMessageInfo(messageInfo); + result.setRecvId(recvId); + result.setStatus(IMSendStatus.FAIL); + result.setFailReason("用户不在线"); + listenerMulticaster.multicast(IMListenerType.PRIVATE_MESSAGE, result); + } + } + } + + public void sendGroupMessage(List recvIds, GroupMessageInfo... messageInfos){ + // 根据群聊每个成员所连的IM-server,进行分组 + List offLineIds = Collections.synchronizedList(new LinkedList()); + Map> serverMap = new ConcurrentHashMap<>(); + recvIds.parallelStream().forEach(id->{ + String key = RedisKey.IM_USER_SERVER_ID + id; + Integer serverId = (Integer)redisTemplate.opsForValue().get(key); + if(serverId != null){ + if(serverMap.containsKey(serverId)){ + serverMap.get(serverId).add(id); + }else { + // 此处需要加锁,否则list可以会被覆盖 + synchronized(serverMap){ + List list = Collections.synchronizedList(new LinkedList()); + list.add(id); + serverMap.put(serverId,list); + } + } + }else{ + offLineIds.add(id); + } + }); + // 逐个server发送 + for (Map.Entry> entry : serverMap.entrySet()) { + IMRecvInfo[] recvInfos = new IMRecvInfo[messageInfos.length]; + for (int i=0;i recvInfo = new IMRecvInfo<>(); + recvInfo.setCmd(IMCmdType.GROUP_MESSAGE.code()); + recvInfo.setRecvIds(new LinkedList<>(entry.getValue())); + recvInfo.setData(messageInfos[i]); + recvInfos[i] = recvInfo; + } + String key = RedisKey.IM_UNREAD_GROUP_QUEUE +entry.getKey(); + redisTemplate.opsForList().rightPushAll(key,recvInfos); + } + // 不在线的用户,回复消息状态 + for(GroupMessageInfo messageInfo:messageInfos ){ + for(Long id : offLineIds){ + // 回复消息状态 + SendResult result = new SendResult(); + result.setMessageInfo(messageInfo); + result.setRecvId(id); + result.setStatus(IMSendStatus.FAIL); + result.setFailReason("用户不在线"); + listenerMulticaster.multicast(IMListenerType.GROUP_MESSAGE,result); + } + } + } +} diff --git a/im-client/src/main/java/com/bx/imclient/task/AbstractPullMessageTask.java b/im-client/src/main/java/com/bx/imclient/task/AbstractPullMessageTask.java new file mode 100644 index 0000000..53b368f --- /dev/null +++ b/im-client/src/main/java/com/bx/imclient/task/AbstractPullMessageTask.java @@ -0,0 +1,47 @@ +package com.bx.imclient.task; + +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +@Slf4j +public abstract class AbstractPullMessageTask { + + private int threadNum = 8; + + private ExecutorService executorService = Executors.newFixedThreadPool(threadNum); + + @PostConstruct + public void init(){ + // 初始化定时器 + for(int i=0;i redisTemplate; + + @Autowired + private MessageListenerMulticaster listenerMulticaster; + + @Override + public void pullMessage() { + String key = RedisKey.IM_RESULT_GROUP_QUEUE; + SendResult result = (SendResult)redisTemplate.opsForList().leftPop(key,10, TimeUnit.SECONDS); + if(result != null) { + listenerMulticaster.multicast(IMListenerType.GROUP_MESSAGE,result); + } + } + +} diff --git a/im-client/src/main/java/com/bx/imclient/task/PullSendResultPrivateMessageTask.java b/im-client/src/main/java/com/bx/imclient/task/PullSendResultPrivateMessageTask.java new file mode 100644 index 0000000..e634493 --- /dev/null +++ b/im-client/src/main/java/com/bx/imclient/task/PullSendResultPrivateMessageTask.java @@ -0,0 +1,38 @@ +package com.bx.imclient.task; + +import com.bx.imclient.listener.MessageListenerMulticaster; +import com.bx.imcommon.contant.RedisKey; +import com.bx.imcommon.enums.IMListenerType; +import com.bx.imcommon.model.SendResult; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; + +import java.util.concurrent.TimeUnit; + +; + +@Slf4j +@Component +public class PullSendResultPrivateMessageTask extends AbstractPullMessageTask{ + + + @Qualifier("IMRedisTemplate") + @Autowired + private RedisTemplate redisTemplate; + + @Autowired + private MessageListenerMulticaster listenerMulticaster; + + @Override + public void pullMessage() { + String key = RedisKey.IM_RESULT_PRIVATE_QUEUE; + SendResult result = (SendResult)redisTemplate.opsForList().leftPop(key,10, TimeUnit.SECONDS); + if(result != null) { + listenerMulticaster.multicast(IMListenerType.PRIVATE_MESSAGE, result); + } + } + +} diff --git a/im-client/src/main/resources/META-INF/spring.factories b/im-client/src/main/resources/META-INF/spring.factories new file mode 100644 index 0000000..801458e --- /dev/null +++ b/im-client/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +com.bx.imclient.IMAutoConfiguration \ No newline at end of file diff --git a/commom/pom.xml b/im-commom/pom.xml similarity index 74% rename from commom/pom.xml rename to im-commom/pom.xml index c269593..f998bcf 100644 --- a/commom/pom.xml +++ b/im-commom/pom.xml @@ -5,10 +5,10 @@ box-im com.bx - 1.0.0 + 1.1.0 4.0.0 - commom + im-commom 8 @@ -34,29 +34,24 @@ org.apache.commons commons-lang3 - - com.baomidou - mybatis-plus-generator - 3.3.2 - - - mysql - mysql-connector-java - compile - + org.springframework spring-beans - - - org.springframework.boot - spring-boot-starter-data-redis - org.apache.velocity velocity ${velocity.version} + + com.fasterxml.jackson.datatype + jackson-datatype-joda + 2.9.10 + + + org.springframework + spring-context + \ No newline at end of file diff --git a/im-commom/src/main/java/com/bx/imcommon/contant/Constant.java b/im-commom/src/main/java/com/bx/imcommon/contant/Constant.java new file mode 100644 index 0000000..11c1e5d --- /dev/null +++ b/im-commom/src/main/java/com/bx/imcommon/contant/Constant.java @@ -0,0 +1,10 @@ +package com.bx.imcommon.contant; + + +public class Constant { + + // 在线状态过期时间 600s + public static final long ONLINE_TIMEOUT_SECOND = 600; + // 消息允许撤回时间 300s + public static final long ALLOW_RECALL_SECOND = 300; +} diff --git a/im-commom/src/main/java/com/bx/imcommon/contant/RedisKey.java b/im-commom/src/main/java/com/bx/imcommon/contant/RedisKey.java new file mode 100644 index 0000000..e8c2d7e --- /dev/null +++ b/im-commom/src/main/java/com/bx/imcommon/contant/RedisKey.java @@ -0,0 +1,19 @@ +package com.bx.imcommon.contant; + +public class RedisKey { + + // im-server最大id,从0开始递增 + public final static String IM_MAX_SERVER_ID = "im:max_server_id"; + // 用户ID所连接的IM-server的ID + public final static String IM_USER_SERVER_ID = "im:user:server_id:"; + // 未读私聊消息队列 + public final static String IM_UNREAD_PRIVATE_QUEUE = "im:unread:private:"; + // 未读群聊消息队列 + public final static String IM_UNREAD_GROUP_QUEUE = "im:unread:group:"; + // 私聊消息发送结果队列 + public final static String IM_RESULT_PRIVATE_QUEUE = "im:result:private"; + // 群聊消息发送结果队列 + public final static String IM_RESULT_GROUP_QUEUE = "im:result:group"; + + +} diff --git a/commom/src/main/java/com/bx/common/enums/WSCmdEnum.java b/im-commom/src/main/java/com/bx/imcommon/enums/IMCmdType.java similarity index 64% rename from commom/src/main/java/com/bx/common/enums/WSCmdEnum.java rename to im-commom/src/main/java/com/bx/imcommon/enums/IMCmdType.java index 587ae04..aedfde0 100644 --- a/commom/src/main/java/com/bx/common/enums/WSCmdEnum.java +++ b/im-commom/src/main/java/com/bx/imcommon/enums/IMCmdType.java @@ -1,6 +1,8 @@ -package com.bx.common.enums; +package com.bx.imcommon.enums; -public enum WSCmdEnum { + + +public enum IMCmdType { LOGIN(0,"登陆"), HEART_BEAT(1,"心跳"), @@ -13,13 +15,13 @@ public enum WSCmdEnum { private String desc; - WSCmdEnum(Integer index, String desc) { + IMCmdType(Integer index, String desc) { this.code =index; this.desc=desc; } - public static WSCmdEnum fromCode(Integer code){ - for (WSCmdEnum typeEnum:values()) { + public static IMCmdType fromCode(Integer code){ + for (IMCmdType typeEnum:values()) { if (typeEnum.code.equals(code)) { return typeEnum; } @@ -28,11 +30,11 @@ public enum WSCmdEnum { } - public String getDesc() { + public String description() { return desc; } - public Integer getCode(){ + public Integer code(){ return this.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 new file mode 100644 index 0000000..6ec1828 --- /dev/null +++ b/im-commom/src/main/java/com/bx/imcommon/enums/IMListenerType.java @@ -0,0 +1,27 @@ +package com.bx.imcommon.enums; + +public enum IMListenerType{ + ALL(0,"全部消息"), + PRIVATE_MESSAGE(1,"私聊消息"), + GROUP_MESSAGE(2,"群聊消息"); + + private Integer code; + + private String desc; + + IMListenerType(Integer index, String desc) { + this.code =index; + this.desc=desc; + } + + + public String description() { + return desc; + } + + + public Integer code(){ + return this.code; + } + +} diff --git a/im-commom/src/main/java/com/bx/imcommon/enums/IMSendStatus.java b/im-commom/src/main/java/com/bx/imcommon/enums/IMSendStatus.java new file mode 100644 index 0000000..cf82384 --- /dev/null +++ b/im-commom/src/main/java/com/bx/imcommon/enums/IMSendStatus.java @@ -0,0 +1,27 @@ +package com.bx.imcommon.enums; + + +public enum IMSendStatus { + + SUCCESS(0,"发送成功"), + FAIL(1,"发送失败"); + + private int code; + private String desc; + + // 构造方法 + IMSendStatus(int code, String desc) { + this.code = code; + this.desc = desc; + } + + public String description() { + return desc; + } + + + public Integer code(){ + return this.code; + } + +} diff --git a/im-commom/src/main/java/com/bx/imcommon/model/GroupMessageInfo.java b/im-commom/src/main/java/com/bx/imcommon/model/GroupMessageInfo.java new file mode 100644 index 0000000..7706868 --- /dev/null +++ b/im-commom/src/main/java/com/bx/imcommon/model/GroupMessageInfo.java @@ -0,0 +1,42 @@ +package com.bx.imcommon.model; + +import com.bx.imcommon.serializer.DateToLongSerializer; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import lombok.Data; + +import java.util.Date; + +@Data +public class GroupMessageInfo { + + /* + * 消息id + */ + private Long id; + + /* + * 群聊id + */ + private Long groupId; + + /* + * 发送者id + */ + private Long sendId; + + /* + * 消息内容 + */ + private String content; + + /* + * 消息内容类型 具体枚举值由应用层定义 + */ + private Integer type; + + /** + * 发送时间 + */ + @JsonSerialize(using = DateToLongSerializer.class) + private Date sendTime; +} diff --git a/commom/src/main/java/com/bx/common/model/im/HeartbeatInfo.java b/im-commom/src/main/java/com/bx/imcommon/model/HeartbeatInfo.java similarity index 72% rename from commom/src/main/java/com/bx/common/model/im/HeartbeatInfo.java rename to im-commom/src/main/java/com/bx/imcommon/model/HeartbeatInfo.java index 55b6c09..e47bfc5 100644 --- a/commom/src/main/java/com/bx/common/model/im/HeartbeatInfo.java +++ b/im-commom/src/main/java/com/bx/imcommon/model/HeartbeatInfo.java @@ -1,4 +1,4 @@ -package com.bx.common.model.im; +package com.bx.imcommon.model; import lombok.Data; diff --git a/im-commom/src/main/java/com/bx/imcommon/model/IMRecvInfo.java b/im-commom/src/main/java/com/bx/imcommon/model/IMRecvInfo.java new file mode 100644 index 0000000..289e2fc --- /dev/null +++ b/im-commom/src/main/java/com/bx/imcommon/model/IMRecvInfo.java @@ -0,0 +1,26 @@ +package com.bx.imcommon.model; + +import lombok.Data; + +import java.util.List; + +@Data +public class IMRecvInfo { + + /* + * 命令类型 + */ + private Integer cmd; + + /* + * 接收者id列表 + */ + private List recvIds; + + /* + * 推送消息体 + */ + private T data; +} + + diff --git a/im-commom/src/main/java/com/bx/imcommon/model/IMSendInfo.java b/im-commom/src/main/java/com/bx/imcommon/model/IMSendInfo.java new file mode 100644 index 0000000..769851b --- /dev/null +++ b/im-commom/src/main/java/com/bx/imcommon/model/IMSendInfo.java @@ -0,0 +1,18 @@ +package com.bx.imcommon.model; + +import lombok.Data; + +@Data +public class IMSendInfo { + + /* + * 命令 + */ + private Integer cmd; + + /* + * 推送消息体 + */ + private T data; + +} diff --git a/commom/src/main/java/com/bx/common/model/im/LoginInfo.java b/im-commom/src/main/java/com/bx/imcommon/model/LoginInfo.java similarity index 71% rename from commom/src/main/java/com/bx/common/model/im/LoginInfo.java rename to im-commom/src/main/java/com/bx/imcommon/model/LoginInfo.java index 3636697..b4eb451 100644 --- a/commom/src/main/java/com/bx/common/model/im/LoginInfo.java +++ b/im-commom/src/main/java/com/bx/imcommon/model/LoginInfo.java @@ -1,4 +1,4 @@ -package com.bx.common.model.im; +package com.bx.imcommon.model; import lombok.Data; diff --git a/im-commom/src/main/java/com/bx/imcommon/model/PrivateMessageInfo.java b/im-commom/src/main/java/com/bx/imcommon/model/PrivateMessageInfo.java new file mode 100644 index 0000000..be5c753 --- /dev/null +++ b/im-commom/src/main/java/com/bx/imcommon/model/PrivateMessageInfo.java @@ -0,0 +1,42 @@ +package com.bx.imcommon.model; + +import com.bx.imcommon.serializer.DateToLongSerializer; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import lombok.Data; + +import java.util.Date; + +@Data +public class PrivateMessageInfo { + + /* + * 消息id + */ + private long id; + + /* + * 发送者id + */ + private Long sendId; + + /* + * 接收者id + */ + private Long recvId; + + /* + * 发送内容 + */ + private String content; + + /* + * 消息内容类型 具体枚举值由应用层定义 + */ + private Integer type; + + /** + * 发送时间 + */ + @JsonSerialize(using = DateToLongSerializer.class) + private Date sendTime; +} diff --git a/im-commom/src/main/java/com/bx/imcommon/model/SendResult.java b/im-commom/src/main/java/com/bx/imcommon/model/SendResult.java new file mode 100644 index 0000000..66ea316 --- /dev/null +++ b/im-commom/src/main/java/com/bx/imcommon/model/SendResult.java @@ -0,0 +1,29 @@ +package com.bx.imcommon.model; + +import com.bx.imcommon.enums.IMSendStatus; +import lombok.Data; + +@Data +public class SendResult { + + /* + * 接收者id + */ + private Long recvId; + + /* + * 发送状态 + */ + private IMSendStatus status; + + /* + * 失败原因 + */ + private String failReason=""; + + /* + * 消息体(透传) + */ + private T messageInfo; + +} diff --git a/im-commom/src/main/java/com/bx/imcommon/serializer/DateToLongSerializer.java b/im-commom/src/main/java/com/bx/imcommon/serializer/DateToLongSerializer.java new file mode 100644 index 0000000..6aa3421 --- /dev/null +++ b/im-commom/src/main/java/com/bx/imcommon/serializer/DateToLongSerializer.java @@ -0,0 +1,28 @@ +package com.bx.imcommon.serializer; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.core.type.WritableTypeId; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.jsontype.TypeSerializer; + +import java.io.IOException; +import java.util.Date; + +public class DateToLongSerializer extends JsonSerializer { + + @Override + public void serialize(Date date, JsonGenerator jsonGenerator, + SerializerProvider serializerProvider) throws IOException { + jsonGenerator.writeNumber(date.getTime()); + } + + @Override + public void serializeWithType(Date value, JsonGenerator gen, SerializerProvider serializers, TypeSerializer typeSer) throws IOException { + WritableTypeId typeIdDef = typeSer.writeTypePrefix(gen, + typeSer.typeId(value, JsonToken.VALUE_STRING)); + serialize(value, gen, serializers); + typeSer.writeTypeSuffix(gen, typeIdDef); + } +} diff --git a/im-platform/pom.xml b/im-platform/pom.xml index c27f121..de6c24e 100644 --- a/im-platform/pom.xml +++ b/im-platform/pom.xml @@ -5,7 +5,7 @@ box-im com.bx - 1.0.0 + 1.1.0 4.0.0 @@ -15,8 +15,8 @@ com.bx - commom - 1.0.0 + im-client + 1.1.0 org.springframework.boot @@ -101,6 +101,11 @@ thumbnailator 0.4.8 + + com.baomidou + mybatis-plus-generator + 3.3.2 + 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 3743c93..15480c2 100644 --- a/im-platform/src/main/java/com/bx/implatform/ImplatformApp.java +++ b/im-platform/src/main/java/com/bx/implatform/ImplatformApp.java @@ -4,14 +4,12 @@ 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.context.annotation.ComponentScan; import org.springframework.context.annotation.EnableAspectJAutoProxy; @Slf4j @EnableAspectJAutoProxy(exposeProxy = true) @MapperScan(basePackages = {"com.bx.implatform.mapper"}) -@ComponentScan(basePackages={"com.bx"}) @SpringBootApplication public class ImplatformApp { diff --git a/im-platform/src/main/java/com/bx/implatform/config/WebSecurityConfg.java b/im-platform/src/main/java/com/bx/implatform/config/WebSecurityConfg.java index 9a3a218..7ef0d62 100644 --- a/im-platform/src/main/java/com/bx/implatform/config/WebSecurityConfg.java +++ b/im-platform/src/main/java/com/bx/implatform/config/WebSecurityConfg.java @@ -1,9 +1,9 @@ package com.bx.implatform.config; import com.alibaba.fastjson.JSON; -import com.bx.common.enums.ResultCode; -import com.bx.common.result.Result; -import com.bx.common.result.ResultUtils; +import com.bx.implatform.enums.ResultCode; +import com.bx.implatform.result.Result; +import com.bx.implatform.result.ResultUtils; import com.bx.implatform.service.IUserService; import com.bx.implatform.session.UserSession; import com.fasterxml.jackson.databind.ObjectMapper; diff --git a/commom/src/main/java/com/bx/common/contant/Constant.java b/im-platform/src/main/java/com/bx/implatform/contant/Constant.java similarity index 56% rename from commom/src/main/java/com/bx/common/contant/Constant.java rename to im-platform/src/main/java/com/bx/implatform/contant/Constant.java index 6c5f130..77fab01 100644 --- a/commom/src/main/java/com/bx/common/contant/Constant.java +++ b/im-platform/src/main/java/com/bx/implatform/contant/Constant.java @@ -1,17 +1,12 @@ -package com.bx.common.contant; - +package com.bx.implatform.contant; public class Constant { - // 最大图片上传大小 public static final long MAX_IMAGE_SIZE = 5*1024*1024; // 最大上传文件大小 public static final long MAX_FILE_SIZE = 10*1024*1024; // 群聊最大人数 public static final long MAX_GROUP_MEMBER = 500; - // 在线状态过期时间 600s - public static final long ONLINE_TIMEOUT_SECOND = 600; - // 消息允许撤回时间 300s - public static final long ALLOW_RECALL_SECOND = 300; + } 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 new file mode 100644 index 0000000..9d25026 --- /dev/null +++ b/im-platform/src/main/java/com/bx/implatform/contant/RedisKey.java @@ -0,0 +1,16 @@ +package com.bx.implatform.contant; + +public class RedisKey { + + // 已读群聊消息位置(已读最大id) + public final static String IM_GROUP_READED_POSITION = "im:readed:group:position:"; + // 缓存前缀 + public final static String IM_CACHE = "im:cache:"; + // 缓存是否好友:bool + public final static String IM_CACHE_FRIEND = IM_CACHE+"friend"; + // 缓存群聊信息 + public final static String IM_CACHE_GROUP = IM_CACHE+"group"; + // 缓存群聊成员id + public final static String IM_CACHE_GROUP_MEMBER_ID = IM_CACHE+"group_member_ids"; + +} 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 901083f..0d50738 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 @@ -1,7 +1,7 @@ package com.bx.implatform.controller; -import com.bx.common.result.Result; -import com.bx.common.result.ResultUtils; +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; 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 e1d718e..9d97450 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 @@ -1,9 +1,8 @@ package com.bx.implatform.controller; - -import com.bx.common.result.Result; -import com.bx.common.result.ResultUtils; import com.bx.implatform.entity.Friend; +import com.bx.implatform.result.Result; +import com.bx.implatform.result.ResultUtils; import com.bx.implatform.service.IFriendService; import com.bx.implatform.session.SessionContext; import com.bx.implatform.vo.FriendVO; 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 aac8764..250ebbb 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 @@ -1,8 +1,8 @@ package com.bx.implatform.controller; -import com.bx.common.result.Result; -import com.bx.common.result.ResultUtils; +import com.bx.implatform.result.Result; +import com.bx.implatform.result.ResultUtils; import com.bx.implatform.service.IGroupService; import com.bx.implatform.vo.GroupInviteVO; import com.bx.implatform.vo.GroupMemberVO; 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 99bac42..48d0665 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 @@ -1,8 +1,9 @@ package com.bx.implatform.controller; -import com.bx.common.result.Result; -import com.bx.common.result.ResultUtils; +import com.bx.imcommon.model.GroupMessageInfo; +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; @@ -12,6 +13,7 @@ import org.springframework.web.bind.annotation.*; import javax.validation.Valid; import javax.validation.constraints.NotNull; +import java.util.List; @Api(tags = "群聊消息") @@ -43,5 +45,12 @@ public class GroupMessageController { return ResultUtils.success(); } + @GetMapping("/history") + @ApiOperation(value = "查询聊天记录",notes="查询聊天记录") + public Result> recallMessage(@NotNull(message = "群聊id不能为空") @RequestParam Long groupId, + @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/PrivateMessageController.java b/im-platform/src/main/java/com/bx/implatform/controller/PrivateMessageController.java index 5fd0286..1b9a338 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 @@ -1,8 +1,9 @@ package com.bx.implatform.controller; -import com.bx.common.result.Result; -import com.bx.common.result.ResultUtils; +import com.bx.imcommon.model.PrivateMessageInfo; +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; @@ -12,6 +13,7 @@ import org.springframework.web.bind.annotation.*; import javax.validation.Valid; import javax.validation.constraints.NotNull; +import java.util.List; @Api(tags = "私聊消息") @RestController @@ -42,5 +44,15 @@ public class PrivateMessageController { privateMessageService.pullUnreadMessage(); return ResultUtils.success(); } + + + @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){ + return ResultUtils.success( privateMessageService.findHistoryMessage(friendId,page,size)); + } + } diff --git a/im-platform/src/main/java/com/bx/implatform/controller/RegisterController.java b/im-platform/src/main/java/com/bx/implatform/controller/RegisterController.java index 9a3e044..a1250b8 100644 --- a/im-platform/src/main/java/com/bx/implatform/controller/RegisterController.java +++ b/im-platform/src/main/java/com/bx/implatform/controller/RegisterController.java @@ -1,8 +1,8 @@ package com.bx.implatform.controller; -import com.bx.common.result.Result; -import com.bx.common.result.ResultUtils; +import com.bx.implatform.result.Result; +import com.bx.implatform.result.ResultUtils; import com.bx.implatform.service.IUserService; import com.bx.implatform.vo.RegisterVO; import io.swagger.annotations.Api; 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 603c8ef..3cc5cdf 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 @@ -1,13 +1,12 @@ package com.bx.implatform.controller; - -import com.bx.common.result.Result; -import com.bx.common.result.ResultUtils; -import com.bx.common.util.BeanUtils; import com.bx.implatform.entity.User; +import com.bx.implatform.result.Result; +import com.bx.implatform.result.ResultUtils; import com.bx.implatform.service.IUserService; import com.bx.implatform.session.SessionContext; import com.bx.implatform.session.UserSession; +import com.bx.implatform.util.BeanUtils; import com.bx.implatform.vo.UserVO; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; diff --git a/commom/src/main/java/com/bx/common/enums/FileTypeEnum.java b/im-platform/src/main/java/com/bx/implatform/enums/FileType.java similarity index 60% rename from commom/src/main/java/com/bx/common/enums/FileTypeEnum.java rename to im-platform/src/main/java/com/bx/implatform/enums/FileType.java index a41ac1f..2e628e5 100644 --- a/commom/src/main/java/com/bx/common/enums/FileTypeEnum.java +++ b/im-platform/src/main/java/com/bx/implatform/enums/FileType.java @@ -1,6 +1,6 @@ -package com.bx.common.enums; +package com.bx.implatform.enums; -public enum FileTypeEnum { +public enum FileType { FILE(0,"文件"), IMAGE(1,"图片"), @@ -13,13 +13,13 @@ public enum FileTypeEnum { private String desc; - FileTypeEnum(Integer index, String desc) { + FileType(Integer index, String desc) { this.code =index; this.desc=desc; } - public static FileTypeEnum fromCode(Integer code){ - for (FileTypeEnum typeEnum:values()) { + public static FileType fromCode(Integer code){ + for (FileType typeEnum:values()) { if (typeEnum.code.equals(code)) { return typeEnum; } @@ -28,11 +28,11 @@ public enum FileTypeEnum { } - public String getDesc() { + public String description() { return desc; } - public Integer getCode(){ + public Integer code(){ return this.code; } diff --git a/commom/src/main/java/com/bx/common/enums/MessageStatusEnum.java b/im-platform/src/main/java/com/bx/implatform/enums/MessageStatus.java similarity index 57% rename from commom/src/main/java/com/bx/common/enums/MessageStatusEnum.java rename to im-platform/src/main/java/com/bx/implatform/enums/MessageStatus.java index be6997b..212c3eb 100644 --- a/commom/src/main/java/com/bx/common/enums/MessageStatusEnum.java +++ b/im-platform/src/main/java/com/bx/implatform/enums/MessageStatus.java @@ -1,7 +1,7 @@ -package com.bx.common.enums; +package com.bx.implatform.enums; -public enum MessageStatusEnum { +public enum MessageStatus { UNREAD(0,"未读"), ALREADY_READ(1,"已读"), @@ -11,13 +11,13 @@ public enum MessageStatusEnum { private String desc; - MessageStatusEnum(Integer index, String desc) { + MessageStatus(Integer index, String desc) { this.code =index; this.desc=desc; } - public static MessageStatusEnum fromCode(Integer code){ - for (MessageStatusEnum typeEnum:values()) { + public static MessageStatus fromCode(Integer code){ + for (MessageStatus typeEnum:values()) { if (typeEnum.code.equals(code)) { return typeEnum; } @@ -26,11 +26,11 @@ public enum MessageStatusEnum { } - public String getDesc() { + public String description() { return desc; } - public Integer getCode(){ + public Integer code(){ return this.code; } } 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 new file mode 100644 index 0000000..f6a0622 --- /dev/null +++ b/im-platform/src/main/java/com/bx/implatform/enums/MessageType.java @@ -0,0 +1,29 @@ +package com.bx.implatform.enums; + + +public enum MessageType { + + TEXT(0,"文字"), + FILE(1,"文件"), + IMAGE(2,"图片"), + VIDEO(3,"视频"), + TIP(10,"系统提示"); + + private Integer code; + + private String desc; + + MessageType(Integer index, String desc) { + this.code =index; + this.desc=desc; + } + + + public String description() { + return desc; + } + + public Integer code(){ + return this.code; + } +} diff --git a/commom/src/main/java/com/bx/common/enums/ResultCode.java b/im-platform/src/main/java/com/bx/implatform/enums/ResultCode.java similarity index 96% rename from commom/src/main/java/com/bx/common/enums/ResultCode.java rename to im-platform/src/main/java/com/bx/implatform/enums/ResultCode.java index ec41f5c..bc6f96c 100644 --- a/commom/src/main/java/com/bx/common/enums/ResultCode.java +++ b/im-platform/src/main/java/com/bx/implatform/enums/ResultCode.java @@ -1,4 +1,4 @@ -package com.bx.common.enums; +package com.bx.implatform.enums; /** * 响应码枚举 diff --git a/im-platform/src/main/java/com/bx/implatform/exception/GlobalException.java b/im-platform/src/main/java/com/bx/implatform/exception/GlobalException.java index b3deac0..fdd189d 100644 --- a/im-platform/src/main/java/com/bx/implatform/exception/GlobalException.java +++ b/im-platform/src/main/java/com/bx/implatform/exception/GlobalException.java @@ -1,6 +1,6 @@ package com.bx.implatform.exception; -import com.bx.common.enums.ResultCode; +import com.bx.implatform.enums.ResultCode; import lombok.Data; import java.io.Serializable; diff --git a/im-platform/src/main/java/com/bx/implatform/exception/GlobalExceptionHandler.java b/im-platform/src/main/java/com/bx/implatform/exception/GlobalExceptionHandler.java index 693ff39..0c44af8 100644 --- a/im-platform/src/main/java/com/bx/implatform/exception/GlobalExceptionHandler.java +++ b/im-platform/src/main/java/com/bx/implatform/exception/GlobalExceptionHandler.java @@ -1,9 +1,9 @@ package com.bx.implatform.exception; import cn.hutool.json.JSONException; -import com.bx.common.enums.ResultCode; -import com.bx.common.result.Result; -import com.bx.common.result.ResultUtils; +import com.bx.implatform.enums.ResultCode; +import com.bx.implatform.result.Result; +import com.bx.implatform.result.ResultUtils; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.http.converter.HttpMessageNotReadableException; diff --git a/commom/src/main/java/com/bx/common/generator/CodeGenerator.java b/im-platform/src/main/java/com/bx/implatform/generator/CodeGenerator.java similarity index 99% rename from commom/src/main/java/com/bx/common/generator/CodeGenerator.java rename to im-platform/src/main/java/com/bx/implatform/generator/CodeGenerator.java index 7e94971..d0f423f 100644 --- a/commom/src/main/java/com/bx/common/generator/CodeGenerator.java +++ b/im-platform/src/main/java/com/bx/implatform/generator/CodeGenerator.java @@ -1,4 +1,4 @@ -package com.bx.common.generator; +package com.bx.implatform.generator; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.core.toolkit.StringPool; 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 new file mode 100644 index 0000000..3a8a745 --- /dev/null +++ b/im-platform/src/main/java/com/bx/implatform/listener/GroupMessageListener.java @@ -0,0 +1,38 @@ +package com.bx.implatform.listener; + +import com.bx.imclient.annotation.IMListener; +import com.bx.imclient.listener.MessageListener; +import com.bx.imcommon.enums.IMListenerType; +import com.bx.imcommon.enums.IMSendStatus; +import com.bx.imcommon.model.GroupMessageInfo; +import com.bx.imcommon.model.SendResult; +import com.bx.implatform.contant.RedisKey; +import com.bx.implatform.enums.MessageType; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; + + +@Slf4j +@IMListener(type = IMListenerType.GROUP_MESSAGE) +public class GroupMessageListener implements MessageListener { + + @Autowired + private RedisTemplate redisTemplate; + + @Override + public void process(SendResult result){ + GroupMessageInfo messageInfo = (GroupMessageInfo) result.getMessageInfo(); + if(messageInfo.getType().equals(MessageType.TIP)){ + // 提示类数据不记录 + return; + } + + // 保存该用户已拉取的最大消息id + if(result.getStatus().equals(IMSendStatus.SUCCESS)) { + String key = RedisKey.IM_GROUP_READED_POSITION + messageInfo.getGroupId() + ":" + result.getRecvId(); + redisTemplate.opsForValue().set(key, messageInfo.getId()); + } + } + +} diff --git a/im-platform/src/main/java/com/bx/implatform/listener/PrivateMessageListener.java b/im-platform/src/main/java/com/bx/implatform/listener/PrivateMessageListener.java new file mode 100644 index 0000000..636ee4e --- /dev/null +++ b/im-platform/src/main/java/com/bx/implatform/listener/PrivateMessageListener.java @@ -0,0 +1,43 @@ +package com.bx.implatform.listener; + +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.IMSendStatus; +import com.bx.imcommon.model.PrivateMessageInfo; +import com.bx.imcommon.model.SendResult; +import com.bx.implatform.entity.PrivateMessage; +import com.bx.implatform.enums.MessageStatus; +import com.bx.implatform.enums.MessageType; +import com.bx.implatform.service.IPrivateMessageService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; + + +@Slf4j +@IMListener(type = IMListenerType.PRIVATE_MESSAGE) +public class PrivateMessageListener implements MessageListener { + + @Autowired + private IPrivateMessageService privateMessageService; + + @Override + public void process(SendResult result){ + PrivateMessageInfo messageInfo = (PrivateMessageInfo) result.getMessageInfo(); + if(messageInfo.getType().equals(MessageType.TIP)){ + // 提示类数据不记录 + return; + } + // 更新消息状态 + if(result.getStatus().equals(IMSendStatus.SUCCESS)){ + UpdateWrapper updateWrapper = new UpdateWrapper<>(); + updateWrapper.lambda().eq(PrivateMessage::getId,messageInfo.getId()) + .eq(PrivateMessage::getStatus, MessageStatus.UNREAD.code()) + .set(PrivateMessage::getStatus, MessageStatus.ALREADY_READ.code()); + privateMessageService.update(updateWrapper); + log.info("消息已读,消息id:{},发送者:{},接收者:{}",messageInfo.getId(),messageInfo.getSendId(),messageInfo.getRecvId()); + } + } + +} diff --git a/commom/src/main/java/com/bx/common/result/Result.java b/im-platform/src/main/java/com/bx/implatform/result/Result.java similarity index 79% rename from commom/src/main/java/com/bx/common/result/Result.java rename to im-platform/src/main/java/com/bx/implatform/result/Result.java index 08006a4..16e630b 100644 --- a/commom/src/main/java/com/bx/common/result/Result.java +++ b/im-platform/src/main/java/com/bx/implatform/result/Result.java @@ -1,4 +1,4 @@ -package com.bx.common.result; +package com.bx.implatform.result; import lombok.Data; diff --git a/commom/src/main/java/com/bx/common/result/ResultUtils.java b/im-platform/src/main/java/com/bx/implatform/result/ResultUtils.java similarity index 94% rename from commom/src/main/java/com/bx/common/result/ResultUtils.java rename to im-platform/src/main/java/com/bx/implatform/result/ResultUtils.java index 0accdca..50326fd 100644 --- a/commom/src/main/java/com/bx/common/result/ResultUtils.java +++ b/im-platform/src/main/java/com/bx/implatform/result/ResultUtils.java @@ -1,7 +1,7 @@ -package com.bx.common.result; +package com.bx.implatform.result; -import com.bx.common.enums.ResultCode; +import com.bx.implatform.enums.ResultCode; public class ResultUtils { @@ -50,7 +50,7 @@ public class ResultUtils { return result; } - public static final Result error(ResultCode resultCode, String messsage,T data){ + public static final Result error(ResultCode resultCode, String messsage, T data){ Result result=new Result(); result.setCode(resultCode.getCode()); result.setMessage(messsage); diff --git a/im-platform/src/main/java/com/bx/implatform/service/IGroupMessageService.java b/im-platform/src/main/java/com/bx/implatform/service/IGroupMessageService.java index 0bd85fb..78a07ac 100644 --- a/im-platform/src/main/java/com/bx/implatform/service/IGroupMessageService.java +++ b/im-platform/src/main/java/com/bx/implatform/service/IGroupMessageService.java @@ -1,9 +1,12 @@ package com.bx.implatform.service; import com.baomidou.mybatisplus.extension.service.IService; +import com.bx.imcommon.model.GroupMessageInfo; import com.bx.implatform.entity.GroupMessage; import com.bx.implatform.vo.GroupMessageVO; +import java.util.List; + public interface IGroupMessageService extends IService { @@ -13,4 +16,6 @@ public interface IGroupMessageService extends IService { void recallMessage(Long id); void pullUnreadMessage(); + + List findHistoryMessage(Long groupId, Long page, Long size); } diff --git a/im-platform/src/main/java/com/bx/implatform/service/IPrivateMessageService.java b/im-platform/src/main/java/com/bx/implatform/service/IPrivateMessageService.java index e6c8069..be629d2 100644 --- a/im-platform/src/main/java/com/bx/implatform/service/IPrivateMessageService.java +++ b/im-platform/src/main/java/com/bx/implatform/service/IPrivateMessageService.java @@ -1,9 +1,12 @@ package com.bx.implatform.service; import com.baomidou.mybatisplus.extension.service.IService; +import com.bx.imcommon.model.PrivateMessageInfo; import com.bx.implatform.entity.PrivateMessage; import com.bx.implatform.vo.PrivateMessageVO; +import java.util.List; + public interface IPrivateMessageService extends IService { @@ -11,6 +14,8 @@ public interface IPrivateMessageService extends IService { void recallMessage(Long id); + List findHistoryMessage(Long friendId, Long page,Long size); + void pullUnreadMessage(); } 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 89faeca..f074d76 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 @@ -2,10 +2,10 @@ package com.bx.implatform.service.impl; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.bx.common.contant.RedisKey; -import com.bx.common.enums.ResultCode; +import com.bx.implatform.contant.RedisKey; import com.bx.implatform.entity.Friend; import com.bx.implatform.entity.User; +import com.bx.implatform.enums.ResultCode; import com.bx.implatform.exception.GlobalException; import com.bx.implatform.mapper.FriendMapper; import com.bx.implatform.service.IFriendService; @@ -13,6 +13,7 @@ import com.bx.implatform.service.IUserService; import com.bx.implatform.session.SessionContext; import com.bx.implatform.session.UserSession; import com.bx.implatform.vo.FriendVO; +import lombok.extern.slf4j.Slf4j; import org.springframework.aop.framework.AopContext; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.CacheConfig; @@ -24,6 +25,7 @@ import org.springframework.transaction.annotation.Transactional; import java.util.List; +@Slf4j @CacheConfig(cacheNames= RedisKey.IM_CACHE_FRIEND) @Service public class FriendServiceImpl extends ServiceImpl implements IFriendService { @@ -63,6 +65,7 @@ public class FriendServiceImpl extends ServiceImpl impleme FriendServiceImpl proxy = (FriendServiceImpl)AopContext.currentProxy(); proxy.bindFriend(userId,friendId); proxy.bindFriend(friendId,userId); + log.info("添加好友,用户id:{},好友id:{}",userId,friendId); } @@ -80,6 +83,7 @@ public class FriendServiceImpl extends ServiceImpl impleme FriendServiceImpl proxy = (FriendServiceImpl)AopContext.currentProxy(); proxy.unbindFriend(userId,friendId); proxy.unbindFriend(friendId,userId); + log.info("删除好友,用户id:{},好友id:{}",userId,friendId); } diff --git a/im-platform/src/main/java/com/bx/implatform/service/impl/GroupMemberServiceImpl.java b/im-platform/src/main/java/com/bx/implatform/service/impl/GroupMemberServiceImpl.java index 69616b1..e62de44 100644 --- a/im-platform/src/main/java/com/bx/implatform/service/impl/GroupMemberServiceImpl.java +++ b/im-platform/src/main/java/com/bx/implatform/service/impl/GroupMemberServiceImpl.java @@ -3,7 +3,7 @@ package com.bx.implatform.service.impl; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.bx.common.contant.RedisKey; +import com.bx.implatform.contant.RedisKey; import com.bx.implatform.entity.GroupMember; import com.bx.implatform.mapper.GroupMemberMapper; import com.bx.implatform.service.IGroupMemberService; 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 3036dab..2b5769c 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 @@ -2,30 +2,33 @@ package com.bx.implatform.service.impl; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.bx.common.contant.Constant; -import com.bx.common.contant.RedisKey; -import com.bx.common.enums.MessageStatusEnum; -import com.bx.common.enums.MessageTypeEnum; -import com.bx.common.enums.ResultCode; -import com.bx.common.model.im.GroupMessageInfo; -import com.bx.common.util.BeanUtils; +import com.bx.imclient.IMClient; +import com.bx.imcommon.contant.Constant; +import com.bx.imcommon.model.GroupMessageInfo; +import com.bx.implatform.contant.RedisKey; import com.bx.implatform.entity.Group; 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; import com.bx.implatform.service.IGroupMessageService; import com.bx.implatform.service.IGroupService; import com.bx.implatform.session.SessionContext; +import com.bx.implatform.util.BeanUtils; import com.bx.implatform.vo.GroupMessageVO; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; +import java.util.Collections; +import java.util.Date; +import java.util.LinkedList; +import java.util.List; import java.util.stream.Collectors; @Slf4j @@ -42,11 +45,14 @@ public class GroupMessageServiceImpl extends ServiceImpl redisTemplate; + @Autowired + private IMClient imClient; + /** * 发送群聊消息(与mysql所有交换都要进行缓存) * * @param vo - * @return + * @return 群聊id */ @Override public Long sendMessage(GroupMessageVO vo) { @@ -70,7 +76,7 @@ public class GroupMessageServiceImpl extends ServiceImpl userIds = groupMemberService.findUserIdsByGroupId(msg.getGroupId()); GroupMessageInfo msgInfo = BeanUtils.copyProperties(msg, GroupMessageInfo.class); - msgInfo.setType(MessageTypeEnum.TIP.getCode()); + msgInfo.setType(MessageType.TIP.code()); String content = String.format("'%s'撤回了一条消息",member.getAliasName()); msgInfo.setContent(content); msgInfo.setSendTime(new Date()); - this.sendMessage(userIds,msgInfo); + imClient.sendGroupMessage(userIds,msgInfo); log.info("撤回群聊消息,发送id:{},群聊id:{},内容:{}",userId,msg.getGroupId(),msg.getContent()); } @@ -124,22 +130,17 @@ public class GroupMessageServiceImpl extends ServiceImpl recvIds = new LinkedList(); recvIds.add(userId); List members = groupMemberService.findByUserId(userId); for(GroupMember member:members){ // 获取群聊已读的最大消息id,只推送未读消息 - key = RedisKey.IM_GROUP_READED_POSITION + member.getGroupId()+":"+userId; + String key = RedisKey.IM_GROUP_READED_POSITION + member.getGroupId()+":"+userId; Integer maxReadedId = (Integer)redisTemplate.opsForValue().get(key); QueryWrapper wrapper = new QueryWrapper(); wrapper.lambda().eq(GroupMessage::getGroupId,member.getGroupId()) .gt(GroupMessage::getSendTime,member.getCreatedTime()) - .ne(GroupMessage::getStatus,MessageStatusEnum.RECALL.getCode()); + .ne(GroupMessage::getStatus, MessageStatus.RECALL.code()); if(maxReadedId!=null){ wrapper.lambda().gt(GroupMessage::getId,maxReadedId); } @@ -151,40 +152,50 @@ public class GroupMessageServiceImpl extends ServiceImpl messageInfos = messages.stream().map(m->{ GroupMessageInfo msgInfo = BeanUtils.copyProperties(m, GroupMessageInfo.class); - msgInfo.setRecvIds(recvIds); return msgInfo; }).collect(Collectors.toList()); - key = RedisKey.IM_UNREAD_GROUP_MESSAGE + serverId; - redisTemplate.opsForList().rightPushAll(key,messageInfos.toArray()); + // 发送消息 + GroupMessageInfo[] infoArr = messageInfos.toArray(new GroupMessageInfo[messageInfos.size()]); + imClient.sendGroupMessage(Collections.singletonList(userId), infoArr); log.info("拉取未读群聊消息,用户id:{},群聊id:{},数量:{}",userId,member.getGroupId(),messageInfos.size()); } } - private void sendMessage(List userIds, GroupMessageInfo msgInfo){ - // 根据群聊每个成员所连的IM-server,进行分组 - Map> serverMap = new ConcurrentHashMap<>(); - userIds.parallelStream().forEach(id->{ - String key = RedisKey.IM_USER_SERVER_ID + id; - Integer serverId = (Integer)redisTemplate.opsForValue().get(key); - if(serverId != null){ - if(serverMap.containsKey(serverId)){ - serverMap.get(serverId).add(id); - }else { - // 此处需要加锁,否则list可以会被覆盖 - synchronized(serverMap){ - List list = Collections.synchronizedList(new LinkedList()); - list.add(id); - serverMap.put(serverId,list); - } - } - } - }); - // 逐个server发送 - for (Map.Entry> entry : serverMap.entrySet()) { - msgInfo.setRecvIds(new LinkedList<>(entry.getValue())); - String key = RedisKey.IM_UNREAD_GROUP_MESSAGE +entry.getKey(); - redisTemplate.opsForList().rightPush(key,msgInfo); + /** + * 拉取历史聊天记录 + * + * @param groupId 群聊id + * @param page 页码 + * @param size 页码大小 + * @return 聊天记录列表 + */ + @Override + public List findHistoryMessage(Long groupId, Long page, Long size) { + page = page > 0 ? page:1; + size = size > 0 ? size:10; + Long userId = SessionContext.getSession().getId(); + Long stIdx = (page-1)* size; + // 群聊成员信息 + GroupMember member = groupMemberService.findByGroupAndUserId(groupId,userId); + if(member == null || member.getQuit()){ + throw new GlobalException(ResultCode.PROGRAM_ERROR,"您已不在群聊中"); } + // 查询聊天记录,只查询加入群聊时间之后的消息 + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.lambda().eq(GroupMessage::getGroupId,groupId) + .gt(GroupMessage::getSendTime,member.getCreatedTime()) + .ne(GroupMessage::getStatus, MessageStatus.RECALL.code()) + .orderByDesc(GroupMessage::getId) + .last("limit "+stIdx + ","+size); + + List messages = this.list(wrapper); + List messageInfos = messages.stream().map(m->{ + GroupMessageInfo info = BeanUtils.copyProperties(m, GroupMessageInfo.class); + return info; + }).collect(Collectors.toList()); + log.info("拉取群聊记录,用户id:{},群聊id:{},数量:{}",userId,groupId,messageInfos.size()); + return messageInfos; } + } 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 65da723..ab72d03 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 @@ -2,14 +2,13 @@ package com.bx.implatform.service.impl; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.bx.common.contant.Constant; -import com.bx.common.contant.RedisKey; -import com.bx.common.enums.ResultCode; -import com.bx.common.util.BeanUtils; +import com.bx.implatform.contant.Constant; +import com.bx.implatform.contant.RedisKey; import com.bx.implatform.entity.Friend; import com.bx.implatform.entity.Group; import com.bx.implatform.entity.GroupMember; import com.bx.implatform.entity.User; +import com.bx.implatform.enums.ResultCode; import com.bx.implatform.exception.GlobalException; import com.bx.implatform.mapper.GroupMapper; import com.bx.implatform.service.IFriendService; @@ -18,9 +17,11 @@ 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; import com.bx.implatform.vo.GroupInviteVO; import com.bx.implatform.vo.GroupMemberVO; import com.bx.implatform.vo.GroupVO; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.CacheConfig; @@ -36,6 +37,7 @@ import java.util.Optional; import java.util.stream.Collectors; +@Slf4j @CacheConfig(cacheNames = RedisKey.IM_CACHE_GROUP) @Service public class GroupServiceImpl extends ServiceImpl implements IGroupService { @@ -79,6 +81,7 @@ public class GroupServiceImpl extends ServiceImpl implements GroupVO vo = BeanUtils.copyProperties(group, GroupVO.class); vo.setAliasName(user.getNickName()); vo.setRemark(groupName); + log.info("创建群聊,群聊id:{},群聊名称:{}",group.getId(),group.getName()); return vo; } @@ -109,6 +112,7 @@ public class GroupServiceImpl extends ServiceImpl implements member.setAliasName(StringUtils.isEmpty(vo.getAliasName())?session.getNickName():vo.getAliasName()); member.setRemark(StringUtils.isEmpty(vo.getRemark())?group.getName():vo.getRemark()); groupMemberService.updateById(member); + log.info("修改群聊,群聊id:{},群聊名称:{}",group.getId(),group.getName()); return vo; } @@ -131,6 +135,7 @@ public class GroupServiceImpl extends ServiceImpl implements // 逻辑删除群数据 group.setDeleted(true); this.updateById(group); + log.info("删除群聊,群聊id:{},群聊名称:{}",group.getId(),group.getName()); } @@ -142,13 +147,14 @@ public class GroupServiceImpl extends ServiceImpl implements */ @Override public void quitGroup(Long groupId) { - UserSession session = SessionContext.getSession(); + Long userId = SessionContext.getSession().getId(); Group group = this.getById(groupId); - if(group.getOwnerId() == session.getId()){ + if(group.getOwnerId() == userId){ throw new GlobalException(ResultCode.PROGRAM_ERROR,"您是群主,不可退出群聊"); } // 删除群聊成员 - groupMemberService.removeByGroupAndUserId(groupId,session.getId()); + groupMemberService.removeByGroupAndUserId(groupId,userId); + log.info("退出群聊,群聊id:{},群聊名称:{},用户id:{}",group.getId(),group.getName(),userId); } @@ -171,6 +177,7 @@ public class GroupServiceImpl extends ServiceImpl implements } // 删除群聊成员 groupMemberService.removeByGroupAndUserId(groupId,userId); + log.info("踢出群聊,群聊id:{},群聊名称:{},用户id:{}",group.getId(),group.getName(),userId); } @Override @@ -281,6 +288,7 @@ public class GroupServiceImpl extends ServiceImpl implements if(!groupMembers.isEmpty()) { groupMemberService.saveOrUpdateBatch(group.getId(),groupMembers); } + log.info("邀请进入群聊,群聊id:{},群聊名称:{},被邀请用户id:{}",group.getId(),group.getName(),vo.getFriendIds()); } /** 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 95265a3..3e5ba22 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 @@ -2,19 +2,20 @@ package com.bx.implatform.service.impl; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.bx.common.contant.Constant; -import com.bx.common.contant.RedisKey; -import com.bx.common.enums.MessageStatusEnum; -import com.bx.common.enums.MessageTypeEnum; -import com.bx.common.enums.ResultCode; -import com.bx.common.model.im.PrivateMessageInfo; -import com.bx.common.util.BeanUtils; +import com.bx.imclient.IMClient; +import com.bx.imcommon.contant.Constant; +import com.bx.imcommon.contant.RedisKey; +import com.bx.imcommon.model.PrivateMessageInfo; import com.bx.implatform.entity.PrivateMessage; +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.PrivateMessageMapper; import com.bx.implatform.service.IFriendService; import com.bx.implatform.service.IPrivateMessageService; import com.bx.implatform.session.SessionContext; +import com.bx.implatform.util.BeanUtils; import com.bx.implatform.vo.PrivateMessageVO; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -32,8 +33,9 @@ public class PrivateMessageServiceImpl extends ServiceImpl redisTemplate; - + private RedisTemplate redisTemplate; + @Autowired + private IMClient imClient; /** * 发送私聊消息 * @@ -43,26 +45,20 @@ public class PrivateMessageServiceImpl extends ServiceImpl Constant.ALLOW_RECALL_SECOND * 1000){ - throw new GlobalException(ResultCode.PROGRAM_ERROR,"消息已发送超过5分钟,无法撤回"); + if (System.currentTimeMillis() - msg.getSendTime().getTime() > Constant.ALLOW_RECALL_SECOND * 1000) { + throw new GlobalException(ResultCode.PROGRAM_ERROR, "消息已发送超过5分钟,无法撤回"); } // 修改消息状态 - msg.setStatus(MessageStatusEnum.RECALL.getCode()); + msg.setStatus(MessageStatus.RECALL.code()); this.updateById(msg); - // 获取对方连接的channelId - String key = RedisKey.IM_USER_SERVER_ID+msg.getRecvId(); - Integer serverId = (Integer)redisTemplate.opsForValue().get(key); - // 如果对方在线,将数据存储至redis,等待拉取推送 - if(serverId != null){ - String sendKey = RedisKey.IM_UNREAD_PRIVATE_MESSAGE + serverId; - PrivateMessageInfo msgInfo = BeanUtils.copyProperties(msg, PrivateMessageInfo.class); - msgInfo.setType(MessageTypeEnum.TIP.getCode()); - msgInfo.setSendTime(new Date()); - msgInfo.setContent("对方撤回了一条消息"); - redisTemplate.opsForList().rightPush(sendKey,msgInfo); - } - log.info("撤回私聊消息,发送id:{},接收id:{},内容:{}",msg.getSendId(),msg.getRecvId(),msg.getContent()); + // 推送消息 + PrivateMessageInfo msgInfo = BeanUtils.copyProperties(msg, PrivateMessageInfo.class); + msgInfo.setType(MessageType.TIP.code()); + msgInfo.setSendTime(new Date()); + msgInfo.setContent("对方撤回了一条消息"); + imClient.sendPrivateMessage(msgInfo.getRecvId(),msgInfo); + log.info("撤回私聊消息,发送id:{},接收id:{},内容:{}", msg.getSendId(), msg.getRecvId(), msg.getContent()); + } + + + /** + * 拉取历史聊天记录 + * + * @param friendId 好友id + * @param page 页码 + * @param size 页码大小 + * @return 聊天记录列表 + */ + @Override + public List findHistoryMessage(Long friendId, Long page, Long size) { + page = page > 0 ? page : 1; + size = size > 0 ? size : 10; + Long userId = SessionContext.getSession().getId(); + Long stIdx = (page - 1) * size; + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.lambda().and(wrap -> wrap.and( + wp -> wp.eq(PrivateMessage::getSendId, userId) + .eq(PrivateMessage::getRecvId, friendId)) + .or(wp -> wp.eq(PrivateMessage::getRecvId, userId) + .eq(PrivateMessage::getSendId, friendId))) + .ne(PrivateMessage::getStatus, MessageStatus.RECALL.code()) + .orderByDesc(PrivateMessage::getId) + .last("limit " + stIdx + "," + size); + + List messages = this.list(wrapper); + List messageInfos = messages.stream().map(m -> { + PrivateMessageInfo info = BeanUtils.copyProperties(m, PrivateMessageInfo.class); + return info; + }).collect(Collectors.toList()); + + log.info("拉取聊天记录,用户id:{},好友id:{},数量:{}", userId, friendId, messageInfos.size()); + return messageInfos; } /** @@ -111,25 +136,26 @@ public class PrivateMessageServiceImpl extends ServiceImpl queryWrapper = new QueryWrapper<>(); - queryWrapper.lambda().eq(PrivateMessage::getRecvId,userId) - .eq(PrivateMessage::getStatus,MessageStatusEnum.UNREAD); + queryWrapper.lambda().eq(PrivateMessage::getRecvId, userId) + .eq(PrivateMessage::getStatus, MessageStatus.UNREAD); List messages = this.list(queryWrapper); // 上传至redis,等待推送 - if(!messages.isEmpty()){ - List infos = messages.stream().map(m->{ + if (!messages.isEmpty()) { + List messageInfos = messages.stream().map(m -> { PrivateMessageInfo msgInfo = BeanUtils.copyProperties(m, PrivateMessageInfo.class); - return msgInfo; + return msgInfo; }).collect(Collectors.toList()); - String sendKey = RedisKey.IM_UNREAD_PRIVATE_MESSAGE + serverId; - redisTemplate.opsForList().rightPushAll(sendKey,infos.toArray()); - log.info("拉取未读私聊消息,用户id:{},数量:{}",userId,infos.size()); + // 推送消息 + PrivateMessageInfo[] infoArr = messageInfos.toArray(new PrivateMessageInfo[messageInfos.size()]); + imClient.sendPrivateMessage(userId,infoArr); + log.info("拉取未读私聊消息,用户id:{},数量:{}", userId, infoArr.length); } } } diff --git a/im-platform/src/main/java/com/bx/implatform/service/impl/SecurityUserDetailsServiceImpl.java b/im-platform/src/main/java/com/bx/implatform/service/impl/SecurityUserDetailsServiceImpl.java index b1043b8..053e254 100644 --- a/im-platform/src/main/java/com/bx/implatform/service/impl/SecurityUserDetailsServiceImpl.java +++ b/im-platform/src/main/java/com/bx/implatform/service/impl/SecurityUserDetailsServiceImpl.java @@ -2,10 +2,10 @@ package com.bx.implatform.service.impl; import com.alibaba.fastjson.JSON; -import com.bx.common.util.BeanUtils; import com.bx.implatform.entity.User; import com.bx.implatform.service.IUserService; import com.bx.implatform.session.UserSession; +import com.bx.implatform.util.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; 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 8f1945e..1fafcd2 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,12 +2,11 @@ package com.bx.implatform.service.impl; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.bx.common.contant.RedisKey; -import com.bx.common.enums.ResultCode; -import com.bx.common.util.BeanUtils; +import com.bx.imcommon.contant.RedisKey; import com.bx.implatform.entity.Friend; import com.bx.implatform.entity.GroupMember; import com.bx.implatform.entity.User; +import com.bx.implatform.enums.ResultCode; import com.bx.implatform.exception.GlobalException; import com.bx.implatform.mapper.UserMapper; import com.bx.implatform.service.IFriendService; @@ -15,8 +14,10 @@ import com.bx.implatform.service.IGroupMemberService; import com.bx.implatform.service.IUserService; import com.bx.implatform.session.SessionContext; import com.bx.implatform.session.UserSession; +import com.bx.implatform.util.BeanUtils; import com.bx.implatform.vo.RegisterVO; import com.bx.implatform.vo.UserVO; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.security.crypto.password.PasswordEncoder; @@ -28,6 +29,7 @@ import java.util.List; import java.util.stream.Collectors; +@Slf4j @Service public class UserServiceImpl extends ServiceImpl implements IUserService { @@ -58,6 +60,7 @@ public class UserServiceImpl extends ServiceImpl implements IU user = BeanUtils.copyProperties(vo,User.class); user.setPassword(passwordEncoder.encode(user.getPassword())); this.save(user); + log.info("注册用户,用户id:{},用户名:{},昵称:{}",user.getId(),vo.getUserName(),vo.getNickName()); } /** @@ -116,6 +119,7 @@ public class UserServiceImpl extends ServiceImpl implements IU user.setHeadImage(vo.getHeadImage()); user.setHeadImageThumb(vo.getHeadImageThumb()); this.updateById(user); + log.info("用户信息更新,用户:{}}",user.toString()); } 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 ee503ed..0a54257 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 @@ -1,9 +1,10 @@ package com.bx.implatform.service.thirdparty; -import com.bx.common.contant.Constant; -import com.bx.common.enums.FileTypeEnum; -import com.bx.common.enums.ResultCode; +import com.bx.implatform.contant.Constant; +import com.bx.implatform.enums.FileType; +import com.bx.implatform.enums.ResultCode; import com.bx.implatform.exception.GlobalException; +import com.bx.implatform.session.SessionContext; import com.bx.implatform.util.FileUtil; import com.bx.implatform.util.ImageUtil; import com.bx.implatform.util.MinioUtil; @@ -50,6 +51,7 @@ public class FileService { public String uploadFile(MultipartFile file){ + Long userId = SessionContext.getSession().getId(); // 大小校验 if(file.getSize() > Constant.MAX_FILE_SIZE){ throw new GlobalException(ResultCode.PROGRAM_ERROR,"文件大小不能超过10M"); @@ -59,11 +61,14 @@ public class FileService { if(StringUtils.isEmpty(fileName)){ throw new GlobalException(ResultCode.PROGRAM_ERROR,"文件上传失败"); } - return generUrl(FileTypeEnum.FILE,fileName); + String url = generUrl(FileType.FILE,fileName); + log.info("文件文件成功,用户id:{},url:{}",userId,url); + return url; } public UploadImageVO uploadImage(MultipartFile file){ try { + Long userId = SessionContext.getSession().getId(); // 大小校验 if(file.getSize() > Constant.MAX_IMAGE_SIZE){ throw new GlobalException(ResultCode.PROGRAM_ERROR,"图片大小不能超过5M"); @@ -78,14 +83,15 @@ public class FileService { if(StringUtils.isEmpty(fileName)){ throw new GlobalException(ResultCode.PROGRAM_ERROR,"图片上传失败"); } - vo.setOriginUrl(generUrl(FileTypeEnum.IMAGE,fileName)); + vo.setOriginUrl(generUrl(FileType.IMAGE,fileName)); // 上传缩略图 byte[] imageByte = ImageUtil.compressForScale(file.getBytes(),100); fileName = minioUtil.upload(bucketName,imagePath,file.getOriginalFilename(),imageByte,file.getContentType()); if(StringUtils.isEmpty(fileName)){ throw new GlobalException(ResultCode.PROGRAM_ERROR,"图片上传失败"); } - vo.setThumbUrl(generUrl(FileTypeEnum.IMAGE,fileName)); + vo.setThumbUrl(generUrl(FileType.IMAGE,fileName)); + log.info("文件图片成功,用户id:{},url:{}",userId,vo.getOriginUrl()); return vo; } catch (IOException e) { log.error("上传图片失败,{}",e.getMessage(),e); @@ -94,7 +100,7 @@ public class FileService { } - public String generUrl(FileTypeEnum fileTypeEnum, String fileName){ + public String generUrl(FileType fileTypeEnum, String fileName){ String url = minIOServer+"/"+bucketName; switch (fileTypeEnum){ case FILE: diff --git a/im-platform/src/main/java/com/bx/implatform/task/PullAlreadyReadMessageTask.java b/im-platform/src/main/java/com/bx/implatform/task/PullAlreadyReadMessageTask.java deleted file mode 100644 index 0f5d027..0000000 --- a/im-platform/src/main/java/com/bx/implatform/task/PullAlreadyReadMessageTask.java +++ /dev/null @@ -1,74 +0,0 @@ -package com.bx.implatform.task; - -import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; -import com.bx.common.contant.RedisKey; -import com.bx.common.enums.MessageStatusEnum; -import com.bx.implatform.entity.PrivateMessage; -import com.bx.implatform.service.IPrivateMessageService; -import lombok.SneakyThrows; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.stereotype.Component; - -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; - -@Slf4j -@Component -public class PullAlreadyReadMessageTask { - - private int threadNum = 8; - - private ExecutorService executorService = Executors.newFixedThreadPool(threadNum); - - @Autowired - private RedisTemplate redisTemplate; - - @Autowired - private IPrivateMessageService privateMessageService; - - @PostConstruct - public void init(){ - for(int i=0;i updateWrapper = new UpdateWrapper<>(); - updateWrapper.lambda().eq(PrivateMessage::getId,msgId) - .eq(PrivateMessage::getStatus,MessageStatusEnum.UNREAD.getCode()) - .set(PrivateMessage::getStatus, MessageStatusEnum.ALREADY_READ.getCode()); - privateMessageService.update(updateWrapper); - log.info("消息已读,id:{}",msgId); - } - }catch (Exception e){ - log.error(e.getMessage()); - Thread.sleep(200); - }finally { - // 下一次循环 - if(!executorService.isShutdown()){ - executorService.submit(this); - } - } - } - } -} diff --git a/commom/src/main/java/com/bx/common/util/BeanUtils.java b/im-platform/src/main/java/com/bx/implatform/util/BeanUtils.java similarity index 99% rename from commom/src/main/java/com/bx/common/util/BeanUtils.java rename to im-platform/src/main/java/com/bx/implatform/util/BeanUtils.java index 5914212..9bc74be 100644 --- a/commom/src/main/java/com/bx/common/util/BeanUtils.java +++ b/im-platform/src/main/java/com/bx/implatform/util/BeanUtils.java @@ -1,4 +1,4 @@ -package com.bx.common.util; +package com.bx.implatform.util; import org.springframework.beans.BeanWrapper; import org.springframework.beans.BeanWrapperImpl; @@ -12,7 +12,6 @@ import java.util.Set; public class BeanUtils { - private static void handleReflectionException(Exception e) { ReflectionUtils.handleReflectionException(e); } diff --git a/commom/src/main/java/com/bx/common/util/DateTimeUtils.java b/im-platform/src/main/java/com/bx/implatform/util/DateTimeUtils.java similarity index 99% rename from commom/src/main/java/com/bx/common/util/DateTimeUtils.java rename to im-platform/src/main/java/com/bx/implatform/util/DateTimeUtils.java index 5d79b0f..4cad011 100644 --- a/commom/src/main/java/com/bx/common/util/DateTimeUtils.java +++ b/im-platform/src/main/java/com/bx/implatform/util/DateTimeUtils.java @@ -1,4 +1,4 @@ -package com.bx.common.util; +package com.bx.implatform.util; import cn.hutool.core.date.DateTime; import cn.hutool.core.date.DateUtil; diff --git a/im-platform/src/main/java/com/bx/implatform/util/FileUtil.java b/im-platform/src/main/java/com/bx/implatform/util/FileUtil.java index b83d45d..8e7347a 100644 --- a/im-platform/src/main/java/com/bx/implatform/util/FileUtil.java +++ b/im-platform/src/main/java/com/bx/implatform/util/FileUtil.java @@ -21,7 +21,7 @@ public class FileUtil { */ public static boolean isImage(String fileName) { String extension = getFileExtension(fileName); - String[] imageExtension = new String[]{"jpeg", "jpg", "bmp", "png","webp"}; + String[] imageExtension = new String[]{"jpeg", "jpg", "bmp", "png","webp","gif"}; for (String e : imageExtension){ if (extension.toLowerCase().equals(e)) { return true; diff --git a/im-platform/src/main/java/com/bx/implatform/util/MinioUtil.java b/im-platform/src/main/java/com/bx/implatform/util/MinioUtil.java index bf4f1cd..ff62244 100644 --- a/im-platform/src/main/java/com/bx/implatform/util/MinioUtil.java +++ b/im-platform/src/main/java/com/bx/implatform/util/MinioUtil.java @@ -1,7 +1,6 @@ package com.bx.implatform.util; -import com.bx.common.util.DateTimeUtils; import io.minio.*; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; diff --git a/im-platform/src/main/resources/application.yml b/im-platform/src/main/resources/application.yml index bb22378..a75e938 100644 --- a/im-platform/src/main/resources/application.yml +++ b/im-platform/src/main/resources/application.yml @@ -23,7 +23,7 @@ mybatis-plus: configuration: # 是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN(下划线命名) 到经典 Java 属性名 aColumn(驼峰命名) 的类似映射 map-underscore-to-camel-case: false - # log-impl: org.apache.ibatis.logging.stdout.StdOutImpl + #log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # mapper mapper-locations: # *.xml的具体路径 diff --git a/im-server/pom.xml b/im-server/pom.xml index 3c9b13c..397264a 100644 --- a/im-server/pom.xml +++ b/im-server/pom.xml @@ -5,7 +5,7 @@ box-im com.bx - 1.0.0 + 1.1.0 4.0.0 @@ -15,8 +15,8 @@ com.bx - commom - 1.0.0 + im-commom + 1.1.0 org.springframework.boot diff --git a/im-server/src/main/java/com/bx/imserver/IMServerApp.java b/im-server/src/main/java/com/bx/imserver/IMServerApp.java index 3cd2947..236d5a8 100644 --- a/im-server/src/main/java/com/bx/imserver/IMServerApp.java +++ b/im-server/src/main/java/com/bx/imserver/IMServerApp.java @@ -1,7 +1,7 @@ package com.bx.imserver; -import com.bx.imserver.websocket.WebsocketServer; +import com.bx.imserver.ws.WebsocketServer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.CommandLineRunner; diff --git a/im-server/src/main/java/com/bx/imserver/processor/GroupMessageProcessor.java b/im-server/src/main/java/com/bx/imserver/processor/GroupMessageProcessor.java new file mode 100644 index 0000000..5bf361f --- /dev/null +++ b/im-server/src/main/java/com/bx/imserver/processor/GroupMessageProcessor.java @@ -0,0 +1,77 @@ +package com.bx.imserver.processor; + +import com.bx.imcommon.contant.RedisKey; +import com.bx.imcommon.enums.IMCmdType; +import com.bx.imcommon.enums.IMSendStatus; +import com.bx.imcommon.model.GroupMessageInfo; +import com.bx.imcommon.model.IMRecvInfo; +import com.bx.imcommon.model.IMSendInfo; +import com.bx.imcommon.model.SendResult; +import com.bx.imserver.util.UserChannelCtxHolder; +import io.netty.channel.ChannelHandlerContext; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Slf4j +@Component +public class GroupMessageProcessor extends MessageProcessor> { + + @Autowired + private RedisTemplate redisTemplate; + + @Async + @Override + public void process(IMRecvInfo recvInfo) { + GroupMessageInfo messageInfo = recvInfo.getData(); + List recvIds = recvInfo.getRecvIds(); + log.info("接收到群消息,发送者:{},群id:{},接收id:{},内容:{}",messageInfo.getSendId(),messageInfo.getGroupId(),recvIds,messageInfo.getContent()); + for(Long recvId:recvIds){ + try { + ChannelHandlerContext channelCtx = UserChannelCtxHolder.getChannelCtx(recvId); + if(channelCtx != null){ + // 自己发的消息不用推送 + if(recvId != messageInfo.getSendId()){ + // 推送消息到用户 + IMSendInfo sendInfo = new IMSendInfo(); + sendInfo.setCmd(IMCmdType.GROUP_MESSAGE.code()); + sendInfo.setData(messageInfo); + channelCtx.channel().writeAndFlush(sendInfo); + // 消息发送成功确认 + String key = RedisKey.IM_RESULT_GROUP_QUEUE; + SendResult sendResult = new SendResult(); + sendResult.setRecvId(recvId); + sendResult.setStatus(IMSendStatus.SUCCESS); + sendResult.setMessageInfo(messageInfo); + redisTemplate.opsForList().rightPush(key,sendResult); + } + }else { + // 消息发送失败确认 + String key = RedisKey.IM_RESULT_GROUP_QUEUE; + SendResult sendResult = new SendResult(); + sendResult.setRecvId(recvId); + sendResult.setStatus(IMSendStatus.FAIL); + sendResult.setFailReason("未找到WS连接"); + sendResult.setMessageInfo(messageInfo); + redisTemplate.opsForList().rightPush(key,sendResult); + log.error("未找到WS连接,发送者:{},群id:{},接收id:{},内容:{}",messageInfo.getSendId(),messageInfo.getGroupId(),recvIds,messageInfo.getContent()); + } + }catch (Exception e){ + // 消息发送失败确认 + String key = RedisKey.IM_RESULT_GROUP_QUEUE; + SendResult sendResult = new SendResult(); + sendResult.setRecvId(recvId); + sendResult.setStatus(IMSendStatus.FAIL); + sendResult.setFailReason("未知异常"); + sendResult.setMessageInfo(messageInfo); + redisTemplate.opsForList().rightPush(key,sendResult); + log.error("发送消息异常,发送者:{},群id:{},接收id:{},内容:{}",messageInfo.getSendId(),messageInfo.getGroupId(),recvIds,messageInfo.getContent()); + } + } + } + +} diff --git a/im-server/src/main/java/com/bx/imserver/websocket/processor/HeartbeatProcessor.java b/im-server/src/main/java/com/bx/imserver/processor/HeartbeatProcessor.java similarity index 79% rename from im-server/src/main/java/com/bx/imserver/websocket/processor/HeartbeatProcessor.java rename to im-server/src/main/java/com/bx/imserver/processor/HeartbeatProcessor.java index 1701299..9c4a976 100644 --- a/im-server/src/main/java/com/bx/imserver/websocket/processor/HeartbeatProcessor.java +++ b/im-server/src/main/java/com/bx/imserver/processor/HeartbeatProcessor.java @@ -1,12 +1,12 @@ -package com.bx.imserver.websocket.processor; +package com.bx.imserver.processor; import cn.hutool.core.bean.BeanUtil; -import com.bx.common.contant.Constant; -import com.bx.common.contant.RedisKey; -import com.bx.common.enums.WSCmdEnum; -import com.bx.common.model.im.HeartbeatInfo; -import com.bx.common.model.im.SendInfo; -import com.bx.imserver.websocket.WebsocketServer; +import com.bx.imcommon.contant.Constant; +import com.bx.imcommon.contant.RedisKey; +import com.bx.imcommon.enums.IMCmdType; +import com.bx.imcommon.model.HeartbeatInfo; +import com.bx.imcommon.model.IMSendInfo; +import com.bx.imserver.ws.WebsocketServer; import io.netty.channel.ChannelHandlerContext; import io.netty.util.AttributeKey; import lombok.extern.slf4j.Slf4j; @@ -31,8 +31,8 @@ public class HeartbeatProcessor extends MessageProcessor { @Override public void process(ChannelHandlerContext ctx, HeartbeatInfo beatInfo) { // 响应ws - SendInfo sendInfo = new SendInfo(); - sendInfo.setCmd(WSCmdEnum.HEART_BEAT.getCode()); + IMSendInfo sendInfo = new IMSendInfo(); + sendInfo.setCmd(IMCmdType.HEART_BEAT.code()); ctx.channel().writeAndFlush(sendInfo); // 设置属性 diff --git a/im-server/src/main/java/com/bx/imserver/websocket/processor/LoginProcessor.java b/im-server/src/main/java/com/bx/imserver/processor/LoginProcessor.java similarity index 70% rename from im-server/src/main/java/com/bx/imserver/websocket/processor/LoginProcessor.java rename to im-server/src/main/java/com/bx/imserver/processor/LoginProcessor.java index 64bd23a..c89f1f0 100644 --- a/im-server/src/main/java/com/bx/imserver/websocket/processor/LoginProcessor.java +++ b/im-server/src/main/java/com/bx/imserver/processor/LoginProcessor.java @@ -1,13 +1,13 @@ -package com.bx.imserver.websocket.processor; +package com.bx.imserver.processor; import cn.hutool.core.bean.BeanUtil; -import com.bx.common.contant.Constant; -import com.bx.common.contant.RedisKey; -import com.bx.common.enums.WSCmdEnum; -import com.bx.common.model.im.LoginInfo; -import com.bx.common.model.im.SendInfo; -import com.bx.imserver.websocket.WebsocketChannelCtxHolder; -import com.bx.imserver.websocket.WebsocketServer; +import com.bx.imcommon.contant.Constant; +import com.bx.imcommon.contant.RedisKey; +import com.bx.imcommon.enums.IMCmdType; +import com.bx.imcommon.model.IMSendInfo; +import com.bx.imcommon.model.LoginInfo; +import com.bx.imserver.util.UserChannelCtxHolder; +import com.bx.imserver.ws.WebsocketServer; import io.netty.channel.ChannelHandlerContext; import io.netty.util.AttributeKey; import lombok.extern.slf4j.Slf4j; @@ -32,15 +32,15 @@ public class LoginProcessor extends MessageProcessor { @Override synchronized public void process(ChannelHandlerContext ctx, LoginInfo loginInfo) { log.info("用户登录,userId:{}",loginInfo.getUserId()); - ChannelHandlerContext context = WebsocketChannelCtxHolder.getChannelCtx(loginInfo.getUserId()); + ChannelHandlerContext context = UserChannelCtxHolder.getChannelCtx(loginInfo.getUserId()); if(context != null){ // 不允许多地登录,强制下线 - SendInfo sendInfo = new SendInfo(); - sendInfo.setCmd(WSCmdEnum.FORCE_LOGUT.getCode()); + IMSendInfo sendInfo = new IMSendInfo(); + sendInfo.setCmd(IMCmdType.FORCE_LOGUT.code()); context.channel().writeAndFlush(sendInfo); } // 绑定用户和channel - WebsocketChannelCtxHolder.addChannelCtx(loginInfo.getUserId(),ctx); + UserChannelCtxHolder.addChannelCtx(loginInfo.getUserId(),ctx); // 设置用户id属性 AttributeKey attr = AttributeKey.valueOf("USER_ID"); ctx.channel().attr(attr).set(loginInfo.getUserId()); @@ -51,8 +51,8 @@ public class LoginProcessor extends MessageProcessor { String key = RedisKey.IM_USER_SERVER_ID+loginInfo.getUserId(); redisTemplate.opsForValue().set(key, WSServer.getServerId(), Constant.ONLINE_TIMEOUT_SECOND, TimeUnit.SECONDS); // 响应ws - SendInfo sendInfo = new SendInfo(); - sendInfo.setCmd(WSCmdEnum.LOGIN.getCode()); + IMSendInfo sendInfo = new IMSendInfo(); + sendInfo.setCmd(IMCmdType.LOGIN.code()); ctx.channel().writeAndFlush(sendInfo); } diff --git a/im-server/src/main/java/com/bx/imserver/websocket/processor/MessageProcessor.java b/im-server/src/main/java/com/bx/imserver/processor/MessageProcessor.java similarity index 85% rename from im-server/src/main/java/com/bx/imserver/websocket/processor/MessageProcessor.java rename to im-server/src/main/java/com/bx/imserver/processor/MessageProcessor.java index 9b156a9..a5ebc5e 100644 --- a/im-server/src/main/java/com/bx/imserver/websocket/processor/MessageProcessor.java +++ b/im-server/src/main/java/com/bx/imserver/processor/MessageProcessor.java @@ -1,4 +1,4 @@ -package com.bx.imserver.websocket.processor; +package com.bx.imserver.processor; import io.netty.channel.ChannelHandlerContext; diff --git a/im-server/src/main/java/com/bx/imserver/processor/PrivateMessageProcessor.java b/im-server/src/main/java/com/bx/imserver/processor/PrivateMessageProcessor.java new file mode 100644 index 0000000..ce5f910 --- /dev/null +++ b/im-server/src/main/java/com/bx/imserver/processor/PrivateMessageProcessor.java @@ -0,0 +1,69 @@ +package com.bx.imserver.processor; + +import com.bx.imcommon.contant.RedisKey; +import com.bx.imcommon.enums.IMCmdType; +import com.bx.imcommon.enums.IMSendStatus; +import com.bx.imcommon.model.IMRecvInfo; +import com.bx.imcommon.model.IMSendInfo; +import com.bx.imcommon.model.PrivateMessageInfo; +import com.bx.imcommon.model.SendResult; +import com.bx.imserver.util.UserChannelCtxHolder; +import io.netty.channel.ChannelHandlerContext; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +public class PrivateMessageProcessor extends MessageProcessor> { + + @Autowired + private RedisTemplate redisTemplate; + + @Override + public void process(IMRecvInfo recvInfo) { + PrivateMessageInfo messageInfo = recvInfo.getData(); + Long recvId = recvInfo.getRecvIds().get(0); + log.info("接收到消息,发送者:{},接收者:{},内容:{}",messageInfo.getSendId(),recvId,messageInfo.getContent()); + try{ + ChannelHandlerContext channelCtx = UserChannelCtxHolder.getChannelCtx(recvId); + if(channelCtx != null ){ + // 推送消息到用户 + IMSendInfo sendInfo = new IMSendInfo(); + sendInfo.setCmd(IMCmdType.PRIVATE_MESSAGE.code()); + sendInfo.setData(messageInfo); + channelCtx.channel().writeAndFlush(sendInfo); + // 消息发送成功确认 + String key = RedisKey.IM_RESULT_PRIVATE_QUEUE; + SendResult sendResult = new SendResult(); + sendResult.setRecvId(recvId); + sendResult.setStatus(IMSendStatus.SUCCESS); + sendResult.setMessageInfo(messageInfo); + redisTemplate.opsForList().rightPush(key,sendResult); + }else{ + // 消息推送失败确认 + String key = RedisKey.IM_RESULT_PRIVATE_QUEUE; + SendResult sendResult = new SendResult(); + sendResult.setRecvId(recvId); + sendResult.setStatus(IMSendStatus.FAIL); + sendResult.setFailReason("未找到WS连接"); + sendResult.setMessageInfo(messageInfo); + redisTemplate.opsForList().rightPush(key,sendResult); + log.error("未找到WS连接,发送者:{},接收者:{},内容:{}",messageInfo.getSendId(),recvId,messageInfo.getContent()); + } + }catch (Exception e){ + // 消息推送失败确认 + String key = RedisKey.IM_RESULT_PRIVATE_QUEUE; + SendResult sendResult = new SendResult(); + sendResult.setRecvId(recvId); + sendResult.setStatus(IMSendStatus.FAIL); + sendResult.setFailReason("未知异常"); + sendResult.setMessageInfo(messageInfo); + redisTemplate.opsForList().rightPush(key,sendResult); + log.error("发送异常,发送者:{},接收者:{},内容:{}",messageInfo.getSendId(),recvId,messageInfo.getContent(),e); + } + + } + +} diff --git a/im-server/src/main/java/com/bx/imserver/websocket/processor/ProcessorFactory.java b/im-server/src/main/java/com/bx/imserver/processor/ProcessorFactory.java similarity index 82% rename from im-server/src/main/java/com/bx/imserver/websocket/processor/ProcessorFactory.java rename to im-server/src/main/java/com/bx/imserver/processor/ProcessorFactory.java index 7f86cae..86a4705 100644 --- a/im-server/src/main/java/com/bx/imserver/websocket/processor/ProcessorFactory.java +++ b/im-server/src/main/java/com/bx/imserver/processor/ProcessorFactory.java @@ -1,11 +1,11 @@ -package com.bx.imserver.websocket.processor; +package com.bx.imserver.processor; -import com.bx.common.enums.WSCmdEnum; -import com.bx.common.util.SpringContextHolder; +import com.bx.imcommon.enums.IMCmdType; +import com.bx.imserver.util.SpringContextHolder; public class ProcessorFactory { - public static MessageProcessor createProcessor(WSCmdEnum cmd){ + public static MessageProcessor createProcessor(IMCmdType cmd){ MessageProcessor processor = null; switch (cmd){ case LOGIN: 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 f529a70..c5841e6 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,6 +1,6 @@ package com.bx.imserver.task; -import com.bx.imserver.websocket.WebsocketServer; +import com.bx.imserver.ws.WebsocketServer; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; diff --git a/im-server/src/main/java/com/bx/imserver/task/PullUnreadGroupMessageTask.java b/im-server/src/main/java/com/bx/imserver/task/PullUnreadGroupMessageTask.java index 241cbb2..03f508b 100644 --- a/im-server/src/main/java/com/bx/imserver/task/PullUnreadGroupMessageTask.java +++ b/im-server/src/main/java/com/bx/imserver/task/PullUnreadGroupMessageTask.java @@ -1,11 +1,12 @@ package com.bx.imserver.task; -import com.bx.common.contant.RedisKey; -import com.bx.common.enums.WSCmdEnum; -import com.bx.common.model.im.GroupMessageInfo; -import com.bx.imserver.websocket.WebsocketServer; -import com.bx.imserver.websocket.processor.MessageProcessor; -import com.bx.imserver.websocket.processor.ProcessorFactory; +import com.bx.imcommon.contant.RedisKey; +import com.bx.imcommon.enums.IMCmdType; +import com.bx.imcommon.model.GroupMessageInfo; +import com.bx.imcommon.model.IMRecvInfo; +import com.bx.imserver.processor.MessageProcessor; +import com.bx.imserver.processor.ProcessorFactory; +import com.bx.imserver.ws.WebsocketServer; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; @@ -28,13 +29,13 @@ public class PullUnreadGroupMessageTask extends AbstractPullMessageTask { @Override public void pullMessage() { // 从redis拉取未读消息 - String key = RedisKey.IM_UNREAD_GROUP_MESSAGE + WSServer.getServerId(); + String key = RedisKey.IM_UNREAD_GROUP_QUEUE + WSServer.getServerId(); List messageInfos = redisTemplate.opsForList().range(key,0,-1); for(Object o: messageInfos){ redisTemplate.opsForList().leftPop(key); - GroupMessageInfo messageInfo = (GroupMessageInfo)o; - MessageProcessor processor = ProcessorFactory.createProcessor(WSCmdEnum.GROUP_MESSAGE); - processor.process(messageInfo); + IMRecvInfo recvInfo = (IMRecvInfo)o; + MessageProcessor processor = ProcessorFactory.createProcessor(IMCmdType.GROUP_MESSAGE); + processor.process(recvInfo); } } diff --git a/im-server/src/main/java/com/bx/imserver/task/PullUnreadPrivateMessageTask.java b/im-server/src/main/java/com/bx/imserver/task/PullUnreadPrivateMessageTask.java index bf515ef..4b0c7cb 100644 --- a/im-server/src/main/java/com/bx/imserver/task/PullUnreadPrivateMessageTask.java +++ b/im-server/src/main/java/com/bx/imserver/task/PullUnreadPrivateMessageTask.java @@ -1,12 +1,13 @@ package com.bx.imserver.task; -import com.bx.common.contant.RedisKey; -import com.bx.common.enums.WSCmdEnum; -import com.bx.common.model.im.PrivateMessageInfo; -import com.bx.imserver.websocket.WebsocketServer; -import com.bx.imserver.websocket.processor.MessageProcessor; -import com.bx.imserver.websocket.processor.ProcessorFactory; +import com.bx.imcommon.contant.RedisKey; +import com.bx.imcommon.enums.IMCmdType; +import com.bx.imcommon.model.IMRecvInfo; +import com.bx.imcommon.model.PrivateMessageInfo; +import com.bx.imserver.processor.MessageProcessor; +import com.bx.imserver.processor.ProcessorFactory; +import com.bx.imserver.ws.WebsocketServer; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; @@ -28,13 +29,13 @@ public class PullUnreadPrivateMessageTask extends AbstractPullMessageTask { @Override public void pullMessage() { // 从redis拉取未读消息 - String key = RedisKey.IM_UNREAD_PRIVATE_MESSAGE + WSServer.getServerId(); + String key = RedisKey.IM_UNREAD_PRIVATE_QUEUE + WSServer.getServerId(); List messageInfos = redisTemplate.opsForList().range(key,0,-1); for(Object o: messageInfos){ redisTemplate.opsForList().leftPop(key); - PrivateMessageInfo messageInfo = (PrivateMessageInfo)o; - MessageProcessor processor = ProcessorFactory.createProcessor(WSCmdEnum.PRIVATE_MESSAGE); - processor.process(messageInfo); + IMRecvInfo recvInfo = (IMRecvInfo)o; + MessageProcessor processor = ProcessorFactory.createProcessor(IMCmdType.PRIVATE_MESSAGE); + processor.process(recvInfo); } } diff --git a/commom/src/main/java/com/bx/common/util/SpringContextHolder.java b/im-server/src/main/java/com/bx/imserver/util/SpringContextHolder.java similarity index 97% rename from commom/src/main/java/com/bx/common/util/SpringContextHolder.java rename to im-server/src/main/java/com/bx/imserver/util/SpringContextHolder.java index 0a980f4..25caf30 100644 --- a/commom/src/main/java/com/bx/common/util/SpringContextHolder.java +++ b/im-server/src/main/java/com/bx/imserver/util/SpringContextHolder.java @@ -1,4 +1,4 @@ -package com.bx.common.util; +package com.bx.imserver.util; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; diff --git a/im-server/src/main/java/com/bx/imserver/websocket/WebsocketChannelCtxHolder.java b/im-server/src/main/java/com/bx/imserver/util/UserChannelCtxHolder.java similarity index 90% rename from im-server/src/main/java/com/bx/imserver/websocket/WebsocketChannelCtxHolder.java rename to im-server/src/main/java/com/bx/imserver/util/UserChannelCtxHolder.java index 5c76aa3..e457e41 100644 --- a/im-server/src/main/java/com/bx/imserver/websocket/WebsocketChannelCtxHolder.java +++ b/im-server/src/main/java/com/bx/imserver/util/UserChannelCtxHolder.java @@ -1,4 +1,4 @@ -package com.bx.imserver.websocket; +package com.bx.imserver.util; import io.netty.channel.ChannelHandlerContext; @@ -6,7 +6,7 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -public class WebsocketChannelCtxHolder { +public class UserChannelCtxHolder { /* * 维护userId和ctx的关联关系,格式:Map diff --git a/im-server/src/main/java/com/bx/imserver/websocket/processor/GroupMessageProcessor.java b/im-server/src/main/java/com/bx/imserver/websocket/processor/GroupMessageProcessor.java deleted file mode 100644 index 67ace27..0000000 --- a/im-server/src/main/java/com/bx/imserver/websocket/processor/GroupMessageProcessor.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.bx.imserver.websocket.processor; - -import com.bx.common.contant.RedisKey; -import com.bx.common.enums.MessageTypeEnum; -import com.bx.common.enums.WSCmdEnum; -import com.bx.common.model.im.GroupMessageInfo; -import com.bx.common.model.im.SendInfo; -import com.bx.imserver.websocket.WebsocketChannelCtxHolder; -import io.netty.channel.ChannelHandlerContext; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.scheduling.annotation.Async; -import org.springframework.stereotype.Component; - -import java.util.List; - -@Slf4j -@Component -public class GroupMessageProcessor extends MessageProcessor { - - @Autowired - private RedisTemplate redisTemplate; - - @Async - @Override - public void process(GroupMessageInfo data) { - log.info("接收到群消息,发送者:{},群id:{},接收id:{},内容:{}",data.getSendId(),data.getGroupId(),data.getRecvIds(),data.getContent()); - List recvIds = data.getRecvIds(); - // 接收者id列表不需要传输,节省带宽 - data.setRecvIds(null); - for(Long recvId:recvIds){ - ChannelHandlerContext channelCtx = WebsocketChannelCtxHolder.getChannelCtx(recvId); - if(channelCtx != null){ - // 自己发的消息不用推送 - if(recvId != data.getSendId()){ - // 推送消息到用户 - SendInfo sendInfo = new SendInfo(); - sendInfo.setCmd(WSCmdEnum.GROUP_MESSAGE.getCode()); - sendInfo.setData(data); - channelCtx.channel().writeAndFlush(sendInfo); - } - if(data.getType() != MessageTypeEnum.TIP.getCode()){ - // 设置已读最大id - String key = RedisKey.IM_GROUP_READED_POSITION + data.getGroupId()+":"+recvId; - redisTemplate.opsForValue().set(key,data.getId()); - } - }else { - log.error("未找到WS连接,发送者:{},群id:{},接收id:{},内容:{}",data.getSendId(),data.getGroupId(),data.getRecvIds()); - } - - } - } - -} diff --git a/im-server/src/main/java/com/bx/imserver/websocket/processor/PrivateMessageProcessor.java b/im-server/src/main/java/com/bx/imserver/websocket/processor/PrivateMessageProcessor.java deleted file mode 100644 index c8513ba..0000000 --- a/im-server/src/main/java/com/bx/imserver/websocket/processor/PrivateMessageProcessor.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.bx.imserver.websocket.processor; - -import com.bx.common.contant.RedisKey; -import com.bx.common.enums.MessageTypeEnum; -import com.bx.common.enums.WSCmdEnum; -import com.bx.common.model.im.PrivateMessageInfo; -import com.bx.common.model.im.SendInfo; -import com.bx.imserver.websocket.WebsocketChannelCtxHolder; -import io.netty.channel.ChannelHandlerContext; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.stereotype.Component; - -@Slf4j -@Component -public class PrivateMessageProcessor extends MessageProcessor { - - @Autowired - private RedisTemplate redisTemplate; - - @Override - public void process(PrivateMessageInfo data) { - log.info("接收到消息,发送者:{},接收者:{},内容:{}",data.getSendId(),data.getRecvId(),data.getContent()); - // 一个用户可以同时登陆,所以有多个channel - ChannelHandlerContext channelCtx = WebsocketChannelCtxHolder.getChannelCtx(data.getRecvId()); - if(channelCtx != null ){ - // 推送消息到用户 - SendInfo sendInfo = new SendInfo(); - sendInfo.setCmd(WSCmdEnum.PRIVATE_MESSAGE.getCode()); - sendInfo.setData(data); - channelCtx.channel().writeAndFlush(sendInfo); - - if(data.getType() != MessageTypeEnum.TIP.getCode()) { - // 已读消息推送至redis,等待更新数据库 - String key = RedisKey.IM_READED_PRIVATE_MESSAGE_ID; - redisTemplate.opsForList().rightPush(key, data.getId()); - } - }else{ - log.error("未找到WS连接,发送者:{},接收者:{},内容:{}",data.getSendId(),data.getRecvId(),data.getContent()); - } - - } - -} diff --git a/im-server/src/main/java/com/bx/imserver/websocket/WebSocketHandler.java b/im-server/src/main/java/com/bx/imserver/ws/WebSocketHandler.java similarity index 81% rename from im-server/src/main/java/com/bx/imserver/websocket/WebSocketHandler.java rename to im-server/src/main/java/com/bx/imserver/ws/WebSocketHandler.java index 8e8e9f6..9d76477 100644 --- a/im-server/src/main/java/com/bx/imserver/websocket/WebSocketHandler.java +++ b/im-server/src/main/java/com/bx/imserver/ws/WebSocketHandler.java @@ -1,11 +1,12 @@ -package com.bx.imserver.websocket; +package com.bx.imserver.ws; -import com.bx.common.contant.RedisKey; -import com.bx.common.enums.WSCmdEnum; -import com.bx.common.model.im.SendInfo; -import com.bx.common.util.SpringContextHolder; -import com.bx.imserver.websocket.processor.MessageProcessor; -import com.bx.imserver.websocket.processor.ProcessorFactory; +import com.bx.imcommon.contant.RedisKey; +import com.bx.imcommon.enums.IMCmdType; +import com.bx.imcommon.model.IMSendInfo; +import com.bx.imserver.processor.MessageProcessor; +import com.bx.imserver.processor.ProcessorFactory; +import com.bx.imserver.util.SpringContextHolder; +import com.bx.imserver.util.UserChannelCtxHolder; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.handler.timeout.IdleState; @@ -21,7 +22,7 @@ import org.springframework.data.redis.core.RedisTemplate; * 浏览器连接状态监控 */ @Slf4j -public class WebSocketHandler extends SimpleChannelInboundHandler { +public class WebSocketHandler extends SimpleChannelInboundHandler { /** * 读取到消息后进行处理 @@ -31,9 +32,9 @@ public class WebSocketHandler extends SimpleChannelInboundHandler { * @throws Exception */ @Override - protected void channelRead0(ChannelHandlerContext ctx, SendInfo sendInfo) throws Exception { + protected void channelRead0(ChannelHandlerContext ctx, IMSendInfo sendInfo) throws Exception { // 创建处理器进行处理 - MessageProcessor processor = ProcessorFactory.createProcessor(WSCmdEnum.fromCode(sendInfo.getCmd())); + MessageProcessor processor = ProcessorFactory.createProcessor(IMCmdType.fromCode(sendInfo.getCmd())); processor.process(ctx,processor.transForm(sendInfo.getData())); } @@ -66,11 +67,11 @@ public class WebSocketHandler extends SimpleChannelInboundHandler { public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { AttributeKey attr = AttributeKey.valueOf("USER_ID"); Long userId = ctx.channel().attr(attr).get(); - ChannelHandlerContext context = WebsocketChannelCtxHolder.getChannelCtx(userId); + ChannelHandlerContext context = UserChannelCtxHolder.getChannelCtx(userId); // 判断一下,避免异地登录导致的误删 if(context != null && ctx.channel().id().equals(context.channel().id())){ // 移除channel - WebsocketChannelCtxHolder.removeChannelCtx(userId); + UserChannelCtxHolder.removeChannelCtx(userId); // 用户下线 RedisTemplate redisTemplate = SpringContextHolder.getBean("redisTemplate"); String key = RedisKey.IM_USER_SERVER_ID + userId; diff --git a/im-server/src/main/java/com/bx/imserver/websocket/WebsocketServer.java b/im-server/src/main/java/com/bx/imserver/ws/WebsocketServer.java similarity index 95% rename from im-server/src/main/java/com/bx/imserver/websocket/WebsocketServer.java rename to im-server/src/main/java/com/bx/imserver/ws/WebsocketServer.java index 831d1eb..b51d33d 100644 --- a/im-server/src/main/java/com/bx/imserver/websocket/WebsocketServer.java +++ b/im-server/src/main/java/com/bx/imserver/ws/WebsocketServer.java @@ -1,8 +1,8 @@ -package com.bx.imserver.websocket; +package com.bx.imserver.ws; -import com.bx.common.contant.RedisKey; -import com.bx.imserver.websocket.endecode.MessageProtocolDecoder; -import com.bx.imserver.websocket.endecode.MessageProtocolEncoder; +import com.bx.imcommon.contant.RedisKey; +import com.bx.imserver.ws.endecode.MessageProtocolDecoder; +import com.bx.imserver.ws.endecode.MessageProtocolEncoder; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; diff --git a/im-server/src/main/java/com/bx/imserver/websocket/endecode/MessageProtocolDecoder.java b/im-server/src/main/java/com/bx/imserver/ws/endecode/MessageProtocolDecoder.java similarity index 76% rename from im-server/src/main/java/com/bx/imserver/websocket/endecode/MessageProtocolDecoder.java rename to im-server/src/main/java/com/bx/imserver/ws/endecode/MessageProtocolDecoder.java index 657c3f9..0ed8342 100644 --- a/im-server/src/main/java/com/bx/imserver/websocket/endecode/MessageProtocolDecoder.java +++ b/im-server/src/main/java/com/bx/imserver/ws/endecode/MessageProtocolDecoder.java @@ -1,6 +1,6 @@ -package com.bx.imserver.websocket.endecode; +package com.bx.imserver.ws.endecode; -import com.bx.common.model.im.SendInfo; +import com.bx.imcommon.model.IMSendInfo; import com.fasterxml.jackson.databind.ObjectMapper; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToMessageDecoder; @@ -13,7 +13,7 @@ public class MessageProtocolDecoder extends MessageToMessageDecoder list) throws Exception { ObjectMapper objectMapper = new ObjectMapper(); - SendInfo sendInfo = objectMapper.readValue(textWebSocketFrame.text(), SendInfo.class); + IMSendInfo sendInfo = objectMapper.readValue(textWebSocketFrame.text(), IMSendInfo.class); list.add(sendInfo); } } diff --git a/im-server/src/main/java/com/bx/imserver/websocket/endecode/MessageProtocolEncoder.java b/im-server/src/main/java/com/bx/imserver/ws/endecode/MessageProtocolEncoder.java similarity index 78% rename from im-server/src/main/java/com/bx/imserver/websocket/endecode/MessageProtocolEncoder.java rename to im-server/src/main/java/com/bx/imserver/ws/endecode/MessageProtocolEncoder.java index 328f02a..a99946b 100644 --- a/im-server/src/main/java/com/bx/imserver/websocket/endecode/MessageProtocolEncoder.java +++ b/im-server/src/main/java/com/bx/imserver/ws/endecode/MessageProtocolEncoder.java @@ -1,6 +1,6 @@ -package com.bx.imserver.websocket.endecode; +package com.bx.imserver.ws.endecode; -import com.bx.common.model.im.SendInfo; +import com.bx.imcommon.model.IMSendInfo; import com.fasterxml.jackson.databind.ObjectMapper; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToMessageEncoder; @@ -8,12 +8,13 @@ import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; import java.util.List; -public class MessageProtocolEncoder extends MessageToMessageEncoder { +public class MessageProtocolEncoder extends MessageToMessageEncoder { @Override - protected void encode(ChannelHandlerContext channelHandlerContext, SendInfo sendInfo, List list) throws Exception { + protected void encode(ChannelHandlerContext channelHandlerContext, IMSendInfo sendInfo, List list) throws Exception { ObjectMapper objectMapper = new ObjectMapper(); String text = objectMapper.writeValueAsString(sendInfo); + TextWebSocketFrame frame = new TextWebSocketFrame(text); list.add(frame); } diff --git a/im-ui/src/api/wssocket.js b/im-ui/src/api/wssocket.js index b365ef6..0bf7300 100644 --- a/im-ui/src/api/wssocket.js +++ b/im-ui/src/api/wssocket.js @@ -2,14 +2,14 @@ var websock = null; let rec; //断线重连后,延迟5秒重新创建WebSocket连接 rec用来存储延迟请求的代码 let isConnect = false; //连接标识 避免重复连接 let wsurl = ""; -let $store = null; +let userId = null; let messageCallBack = null; let openCallBack = null; let hasLogin = false; -let createWebSocket = (url, store) => { - $store = store; +let createWebSocket = (url, id) => { wsurl = url; + userId = id; initWebSocket(); }; @@ -45,7 +45,7 @@ let initWebSocket = () => { // 发送登录命令 let loginInfo = { cmd: 0, - data: {userId: $store.state.userStore.userInfo.id} + data: {userId: userId} }; websock.send(JSON.stringify(loginInfo)); @@ -88,7 +88,7 @@ var heartCheck = { let heartBeat = { cmd: 1, data: { - userId: $store.state.userStore.userInfo.id + userId: userId } }; websock.send(JSON.stringify(heartBeat)) @@ -139,8 +139,6 @@ function onopen(callback) { } - - // 将方法暴露出去 export { createWebSocket, diff --git a/im-ui/src/components/chat/ChatBox.vue b/im-ui/src/components/chat/ChatBox.vue index e515f46..8228bce 100644 --- a/im-ui/src/components/chat/ChatBox.vue +++ b/im-ui/src/components/chat/ChatBox.vue @@ -40,7 +40,7 @@
-
+
@@ -57,6 +57,9 @@ + @@ -66,7 +69,9 @@ import FileUpload from "../common/FileUpload.vue"; import Emotion from "../common/Emotion.vue"; import ChatVoice from "./ChatVoice.vue"; - + import ChatHistory from "./ChatHistory.vue"; + + export default { name: "chatPrivate", components: { @@ -74,7 +79,8 @@ FileUpload, ChatGroupSide, Emotion, - ChatVoice + ChatVoice, + ChatHistory }, props: { chat: { @@ -93,7 +99,8 @@ emoBoxPos: { // emoji表情弹出位置 x: 0, y: 0 - } + }, + showHistory: false // 是否显示历史聊天记录 } }, methods: { @@ -211,6 +218,12 @@ closeVoiceBox() { this.showVoice = false; }, + showHistoryBox(){ + this.showHistory = true; + }, + closeHistoryBox(){ + this.showHistory = false; + }, handleSendVoice(data) { let msgInfo = { content: JSON.stringify(data), diff --git a/im-ui/src/components/chat/ChatHistory.vue b/im-ui/src/components/chat/ChatHistory.vue new file mode 100644 index 0000000..1c1c30e --- /dev/null +++ b/im-ui/src/components/chat/ChatHistory.vue @@ -0,0 +1,170 @@ + + + + + diff --git a/im-ui/src/components/chat/MessageItem.vue b/im-ui/src/components/chat/MessageItem.vue index 88d0317..400bf24 100644 --- a/im-ui/src/components/chat/MessageItem.vue +++ b/im-ui/src/components/chat/MessageItem.vue @@ -37,7 +37,7 @@ - @@ -70,6 +70,10 @@ msgInfo: { type: Object, required: true + }, + menu:{ + type: Boolean, + default: true } }, data() { diff --git a/im-ui/src/view/Home.vue b/im-ui/src/view/Home.vue index f50ab7f..4bc2a62 100644 --- a/im-ui/src/view/Home.vue +++ b/im-ui/src/view/Home.vue @@ -63,7 +63,7 @@ init(userInfo) { this.$store.commit("setUserInfo", userInfo); this.$store.commit("initStore"); - this.$wsApi.createWebSocket(process.env.VUE_APP_WS_URL, this.$store); + this.$wsApi.createWebSocket(process.env.VUE_APP_WS_URL, userInfo.id); this.$wsApi.onopen(() => { this.pullUnreadMessage(); }); diff --git a/pom.xml b/pom.xml index fc15ee7..945d351 100644 --- a/pom.xml +++ b/pom.xml @@ -7,12 +7,13 @@ box-im com.bx pom - 1.0.0 + 1.1.0 im-platform im-server - commom + im-commom + im-client