Browse Source

!42 合并

Merge pull request !42 from blue/v_2.0.0
master
blue 2 years ago
committed by Gitee
parent
commit
9868a710c8
No known key found for this signature in database GPG Key ID: 173E9B9CA92EEF8F
  1. 1
      im-client/src/main/java/com/bx/imclient/IMAutoConfiguration.java
  2. 4
      im-client/src/main/java/com/bx/imclient/listener/MessageListener.java
  3. 10
      im-client/src/main/java/com/bx/imclient/listener/MessageListenerMulticaster.java
  4. 44
      im-client/src/main/java/com/bx/imclient/sender/IMSender.java
  5. 16
      im-client/src/main/java/com/bx/imclient/task/AbstractMessageResultTask.java
  6. 41
      im-client/src/main/java/com/bx/imclient/task/GroupMessageResultResultTask.java
  7. 43
      im-client/src/main/java/com/bx/imclient/task/PrivateMessageResultResultTask.java
  8. 4
      im-commom/src/main/java/com/bx/imcommon/model/IMRecvInfo.java
  9. 11
      im-platform/src/main/java/com/bx/implatform/listener/GroupMessageListener.java
  10. 18
      im-platform/src/main/java/com/bx/implatform/listener/PrivateMessageListener.java
  11. 18
      im-platform/src/main/java/com/bx/implatform/service/impl/GroupMessageServiceImpl.java
  12. 1
      im-platform/src/main/java/com/bx/implatform/service/impl/PrivateMessageServiceImpl.java
  13. 2
      im-platform/src/main/resources/application.yml
  14. 4
      im-server/src/main/java/com/bx/imserver/netty/processor/GroupMessageProcessor.java
  15. 3
      im-server/src/main/java/com/bx/imserver/netty/processor/PrivateMessageProcessor.java
  16. 4
      im-server/src/main/java/com/bx/imserver/task/AbstractPullMessageTask.java
  17. 8
      im-server/src/main/java/com/bx/imserver/task/PullGroupMessageTask.java
  18. 13
      im-server/src/main/java/com/bx/imserver/task/PullPrivateMessageTask.java
  19. 2
      im-ui/src/api/date.js
  20. 1
      im-ui/src/components/chat/ChatBox.vue
  21. 2
      im-uniapp/common/date.js
  22. 2
      im-uniapp/components/chat-item/chat-item.vue

1
im-client/src/main/java/com/bx/imclient/IMAutoConfiguration.java

@ -9,4 +9,5 @@ import org.springframework.context.annotation.Configuration;
@Configuration @Configuration
@ComponentScan("com.bx.imclient") @ComponentScan("com.bx.imclient")
public class IMAutoConfiguration { public class IMAutoConfiguration {
} }

4
im-client/src/main/java/com/bx/imclient/listener/MessageListener.java

@ -3,8 +3,10 @@ package com.bx.imclient.listener;
import com.bx.imcommon.model.IMSendResult; import com.bx.imcommon.model.IMSendResult;
import java.util.List;
public interface MessageListener<T> { public interface MessageListener<T> {
void process(IMSendResult<T> result); void process(List<IMSendResult<T>> result);
} }

10
im-client/src/main/java/com/bx/imclient/listener/MessageListenerMulticaster.java

@ -1,6 +1,7 @@
package com.bx.imclient.listener; package com.bx.imclient.listener;
import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.bx.imclient.annotation.IMListener; import com.bx.imclient.annotation.IMListener;
import com.bx.imcommon.enums.IMListenerType; import com.bx.imcommon.enums.IMListenerType;
@ -19,10 +20,14 @@ public class MessageListenerMulticaster {
@Autowired(required = false) @Autowired(required = false)
private List<MessageListener> messageListeners = Collections.emptyList(); private List<MessageListener> messageListeners = Collections.emptyList();
public void multicast(IMListenerType listenerType, IMSendResult result){ public void multicast(IMListenerType listenerType, List<IMSendResult> results){
if(CollUtil.isEmpty(results)){
return;
}
for(MessageListener listener:messageListeners){ for(MessageListener listener:messageListeners){
IMListener annotation = listener.getClass().getAnnotation(IMListener.class); IMListener annotation = listener.getClass().getAnnotation(IMListener.class);
if(annotation!=null && (annotation.type().equals(IMListenerType.ALL) || annotation.type().equals(listenerType))){ if(annotation!=null && (annotation.type().equals(IMListenerType.ALL) || annotation.type().equals(listenerType))){
results.forEach(result->{
// 将data转回对象类型 // 将data转回对象类型
if(result.getData() instanceof JSONObject){ if(result.getData() instanceof JSONObject){
Type superClass = listener.getClass().getGenericInterfaces()[0]; Type superClass = listener.getClass().getGenericInterfaces()[0];
@ -30,8 +35,9 @@ public class MessageListenerMulticaster {
JSONObject data = (JSONObject)result.getData(); JSONObject data = (JSONObject)result.getData();
result.setData(data.toJavaObject(type)); result.setData(data.toJavaObject(type));
} }
});
// 回调到调用方处理 // 回调到调用方处理
listener.process(result); listener.process(results);
} }
} }
} }

44
im-client/src/main/java/com/bx/imclient/sender/IMSender.java

@ -9,6 +9,7 @@ import com.bx.imcommon.enums.IMSendCode;
import com.bx.imcommon.enums.IMTerminalType; import com.bx.imcommon.enums.IMTerminalType;
import com.bx.imcommon.model.*; import com.bx.imcommon.model.*;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -22,9 +23,13 @@ public class IMSender {
@Resource(name="IMRedisTemplate") @Resource(name="IMRedisTemplate")
private RedisTemplate<String, Object> redisTemplate; private RedisTemplate<String, Object> redisTemplate;
@Value("${spring.application.name}")
private String appName;
private final MessageListenerMulticaster listenerMulticaster; private final MessageListenerMulticaster listenerMulticaster;
public<T> void sendPrivateMessage(IMPrivateMessage<T> message) { public<T> void sendPrivateMessage(IMPrivateMessage<T> message) {
List<IMSendResult> results = new LinkedList<>();
for (Integer terminal : message.getRecvTerminals()) { for (Integer terminal : message.getRecvTerminals()) {
// 获取对方连接的channelId // 获取对方连接的channelId
String key = String.join(":", IMRedisKey.IM_USER_SERVER_ID, message.getRecvId().toString(), terminal.toString()); String key = String.join(":", IMRedisKey.IM_USER_SERVER_ID, message.getRecvId().toString(), terminal.toString());
@ -35,18 +40,18 @@ public class IMSender {
IMRecvInfo recvInfo = new IMRecvInfo(); IMRecvInfo recvInfo = new IMRecvInfo();
recvInfo.setCmd(IMCmdType.PRIVATE_MESSAGE.code()); recvInfo.setCmd(IMCmdType.PRIVATE_MESSAGE.code());
recvInfo.setSendResult(message.getSendResult()); recvInfo.setSendResult(message.getSendResult());
recvInfo.setServiceName(appName);
recvInfo.setSender(message.getSender()); recvInfo.setSender(message.getSender());
recvInfo.setReceivers(Collections.singletonList(new IMUserInfo(message.getRecvId(), terminal))); recvInfo.setReceivers(Collections.singletonList(new IMUserInfo(message.getRecvId(), terminal)));
recvInfo.setData(message.getData()); recvInfo.setData(message.getData());
redisTemplate.opsForList().rightPush(sendKey, recvInfo); redisTemplate.opsForList().rightPush(sendKey, recvInfo);
} else if (message.getSendResult()) { } else {
// 回复消息状态
IMSendResult result = new IMSendResult(); IMSendResult result = new IMSendResult();
result.setSender(message.getSender()); result.setSender(message.getSender());
result.setReceiver(new IMUserInfo(message.getRecvId(), terminal)); result.setReceiver(new IMUserInfo(message.getRecvId(), terminal));
result.setCode(IMSendCode.NOT_ONLINE.code()); result.setCode(IMSendCode.NOT_ONLINE.code());
result.setData(message.getData()); result.setData(message.getData());
listenerMulticaster.multicast(IMListenerType.PRIVATE_MESSAGE, result); results.add(result);
} }
} }
// 推送给自己的其他终端 // 推送给自己的其他终端
@ -72,10 +77,14 @@ public class IMSender {
} }
} }
} }
// 对离线用户回复消息状态
if(message.getSendResult() && !results.isEmpty()){
listenerMulticaster.multicast(IMListenerType.PRIVATE_MESSAGE, results);
}
} }
public<T> void sendGroupMessage(IMGroupMessage<T> message) { public<T> void sendGroupMessage(IMGroupMessage<T> message) {
// 根据群聊每个成员所连的IM-server,进行分组 // 根据群聊每个成员所连的IM-server,进行分组
Map<String, IMUserInfo> sendMap = new HashMap<>(); Map<String, IMUserInfo> sendMap = new HashMap<>();
for (Integer terminal : message.getRecvTerminals()) { for (Integer terminal : message.getRecvTerminals()) {
@ -106,23 +115,14 @@ public class IMSender {
recvInfo.setCmd(IMCmdType.GROUP_MESSAGE.code()); recvInfo.setCmd(IMCmdType.GROUP_MESSAGE.code());
recvInfo.setReceivers(new LinkedList<>(entry.getValue())); recvInfo.setReceivers(new LinkedList<>(entry.getValue()));
recvInfo.setSender(message.getSender()); recvInfo.setSender(message.getSender());
recvInfo.setServiceName(appName);
recvInfo.setSendResult(message.getSendResult()); recvInfo.setSendResult(message.getSendResult());
recvInfo.setData(message.getData()); recvInfo.setData(message.getData());
// 推送至队列 // 推送至队列
String key = String.join(":", IMRedisKey.IM_MESSAGE_GROUP_QUEUE, entry.getKey().toString()); String key = String.join(":", IMRedisKey.IM_MESSAGE_GROUP_QUEUE, entry.getKey().toString());
redisTemplate.opsForList().rightPush(key, recvInfo); redisTemplate.opsForList().rightPush(key, recvInfo);
} }
// 对离线用户回复消息状态
if (message.getSendResult()) {
for (IMUserInfo offLineUser : offLineUsers) {
IMSendResult result = new IMSendResult();
result.setSender(message.getSender());
result.setReceiver(offLineUser);
result.setCode(IMSendCode.NOT_ONLINE.code());
result.setData(message.getData());
listenerMulticaster.multicast(IMListenerType.GROUP_MESSAGE, result);
}
}
// 推送给自己的其他终端 // 推送给自己的其他终端
if (message.getSendToSelf()) { if (message.getSendToSelf()) {
for (Integer terminal : IMTerminalType.codes()) { for (Integer terminal : IMTerminalType.codes()) {
@ -146,6 +146,20 @@ public class IMSender {
} }
} }
} }
// 对离线用户回复消息状态
if(message.getSendResult() && !offLineUsers.isEmpty()){
List<IMSendResult> results = new LinkedList<>();
for (IMUserInfo offLineUser : offLineUsers) {
IMSendResult result = new IMSendResult();
result.setSender(message.getSender());
result.setReceiver(offLineUser);
result.setCode(IMSendCode.NOT_ONLINE.code());
result.setData(message.getData());
results.add(result);
}
listenerMulticaster.multicast(IMListenerType.GROUP_MESSAGE, results);
}
} }
public Map<Long,List<IMTerminalType>> getOnlineTerminal(List<Long> userIds){ public Map<Long,List<IMTerminalType>> getOnlineTerminal(List<Long> userIds){

16
im-client/src/main/java/com/bx/imclient/task/AbstractMessageResultTask.java

@ -20,13 +20,13 @@ public abstract class AbstractMessageResultTask implements CommandLineRunner {
@SneakyThrows @SneakyThrows
@Override @Override
public void run() { public void run() {
try{ try {
pullMessage(); pullMessage();
}catch (Exception e){ } catch (Exception e) {
log.error("任务调度异常",e); log.error("任务调度异常", e);
Thread.sleep(200);
} }
if(!EXECUTOR_SERVICE.isShutdown()){ if (!EXECUTOR_SERVICE.isShutdown()) {
Thread.sleep(100);
EXECUTOR_SERVICE.execute(this); EXECUTOR_SERVICE.execute(this);
} }
} }
@ -35,10 +35,10 @@ public abstract class AbstractMessageResultTask implements CommandLineRunner {
@PreDestroy @PreDestroy
public void destroy(){ public void destroy() {
log.info("{}线程任务关闭",this.getClass().getSimpleName()); log.info("{}线程任务关闭", this.getClass().getSimpleName());
EXECUTOR_SERVICE.shutdown(); EXECUTOR_SERVICE.shutdown();
} }
public abstract void pullMessage(); public abstract void pullMessage() throws InterruptedException;
} }

41
im-client/src/main/java/com/bx/imclient/task/GroupMessageResultResultTask.java

@ -1,34 +1,59 @@
package com.bx.imclient.task; package com.bx.imclient.task;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.bx.imclient.listener.MessageListenerMulticaster; import com.bx.imclient.listener.MessageListenerMulticaster;
import com.bx.imcommon.contant.IMRedisKey; import com.bx.imcommon.contant.IMRedisKey;
import com.bx.imcommon.enums.IMListenerType; import com.bx.imcommon.enums.IMListenerType;
import com.bx.imcommon.model.IMSendResult; import com.bx.imcommon.model.IMSendResult;
import lombok.AllArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.concurrent.TimeUnit; import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
@Component @Component
@AllArgsConstructor @RequiredArgsConstructor
public class GroupMessageResultResultTask extends AbstractMessageResultTask { public class GroupMessageResultResultTask extends AbstractMessageResultTask {
@Resource(name = "IMRedisTemplate") @Resource(name = "IMRedisTemplate")
private RedisTemplate<String,Object> redisTemplate; private RedisTemplate<String,Object> redisTemplate;
@Value("${spring.application.name}")
private String appName;
@Value("${im.result.batch:100}")
private int batchSize;
private final MessageListenerMulticaster listenerMulticaster; private final MessageListenerMulticaster listenerMulticaster;
@Override @Override
public void pullMessage() { public void pullMessage() {
String key = IMRedisKey.IM_RESULT_GROUP_QUEUE; List<IMSendResult> results;
JSONObject jsonObject = (JSONObject)redisTemplate.opsForList().leftPop(key,10, TimeUnit.SECONDS); do {
if(jsonObject != null) { results = loadBatch();
IMSendResult result = jsonObject.toJavaObject(IMSendResult.class); if(!results.isEmpty()){
listenerMulticaster.multicast(IMListenerType.GROUP_MESSAGE,result); listenerMulticaster.multicast(IMListenerType.GROUP_MESSAGE, results);
}
} while (results.size() >= batchSize);
}
List<IMSendResult> loadBatch() {
String key = StrUtil.join(":", IMRedisKey.IM_RESULT_GROUP_QUEUE, appName);
//这个接口redis6.2以上才支持
//List<Object> list = redisTemplate.opsForList().leftPop(key, batchSize);
List<IMSendResult> results = new LinkedList<>();
JSONObject jsonObject = (JSONObject) redisTemplate.opsForList().leftPop(key);
while (!Objects.isNull(jsonObject) && results.size() < batchSize) {
results.add(jsonObject.toJavaObject(IMSendResult.class));
jsonObject = (JSONObject) redisTemplate.opsForList().leftPop(key);
} }
return results;
} }
} }

43
im-client/src/main/java/com/bx/imclient/task/PrivateMessageResultResultTask.java

@ -1,37 +1,60 @@
package com.bx.imclient.task; package com.bx.imclient.task;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.bx.imclient.listener.MessageListenerMulticaster; import com.bx.imclient.listener.MessageListenerMulticaster;
import com.bx.imcommon.contant.IMRedisKey; import com.bx.imcommon.contant.IMRedisKey;
import com.bx.imcommon.enums.IMListenerType; import com.bx.imcommon.enums.IMListenerType;
import com.bx.imcommon.model.IMSendResult; import com.bx.imcommon.model.IMSendResult;
import lombok.AllArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.concurrent.TimeUnit; import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
@Slf4j @Slf4j
@Component @Component
@AllArgsConstructor @RequiredArgsConstructor
public class PrivateMessageResultResultTask extends AbstractMessageResultTask { public class PrivateMessageResultResultTask extends AbstractMessageResultTask {
@Resource(name = "IMRedisTemplate") @Resource(name = "IMRedisTemplate")
private RedisTemplate<String,Object> redisTemplate; private RedisTemplate<String, Object> redisTemplate;
@Value("${spring.application.name}")
private String appName;
@Value("${im.result.batch:100}")
private int batchSize;
private final MessageListenerMulticaster listenerMulticaster; private final MessageListenerMulticaster listenerMulticaster;
@Override @Override
public void pullMessage() { public void pullMessage() {
String key = IMRedisKey.IM_RESULT_PRIVATE_QUEUE; List<IMSendResult> results;
JSONObject jsonObject = (JSONObject)redisTemplate.opsForList().leftPop(key,10, TimeUnit.SECONDS); do {
if(jsonObject != null) { results = loadBatch();
IMSendResult result = jsonObject.toJavaObject(IMSendResult.class); if(!results.isEmpty()){
listenerMulticaster.multicast(IMListenerType.PRIVATE_MESSAGE, result); listenerMulticaster.multicast(IMListenerType.PRIVATE_MESSAGE, results);
}
} while (results.size() >= batchSize);
}
List<IMSendResult> loadBatch() {
String key = StrUtil.join(":", IMRedisKey.IM_RESULT_PRIVATE_QUEUE, appName);
//这个接口redis6.2以上才支持
//List<Object> list = redisTemplate.opsForList().leftPop(key, batchSize);
List<IMSendResult> results = new LinkedList<>();
JSONObject jsonObject = (JSONObject) redisTemplate.opsForList().leftPop(key);
while (!Objects.isNull(jsonObject) && results.size() < batchSize) {
results.add(jsonObject.toJavaObject(IMSendResult.class));
jsonObject = (JSONObject) redisTemplate.opsForList().leftPop(key);
} }
return results;
} }
} }

4
im-commom/src/main/java/com/bx/imcommon/model/IMRecvInfo.java

@ -27,6 +27,10 @@ public class IMRecvInfo {
*/ */
private Boolean sendResult; private Boolean sendResult;
/**
* 当前服务名回调发送结果使用
*/
private String serviceName;
/** /**
* 推送消息体 * 推送消息体
*/ */

11
im-platform/src/main/java/com/bx/implatform/listener/GroupMessageListener.java

@ -7,10 +7,13 @@ import com.bx.imcommon.enums.IMSendCode;
import com.bx.imcommon.model.IMSendResult; import com.bx.imcommon.model.IMSendResult;
import com.bx.implatform.contant.RedisKey; import com.bx.implatform.contant.RedisKey;
import com.bx.implatform.vo.GroupMessageVO; import com.bx.implatform.vo.GroupMessageVO;
import com.bx.implatform.vo.PrivateMessageVO;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.RedisTemplate;
import java.util.List;
@Slf4j @Slf4j
@IMListener(type = IMListenerType.GROUP_MESSAGE) @IMListener(type = IMListenerType.GROUP_MESSAGE)
@AllArgsConstructor @AllArgsConstructor
@ -19,9 +22,13 @@ public class GroupMessageListener implements MessageListener<GroupMessageVO> {
private final RedisTemplate<String, Object> redisTemplate; private final RedisTemplate<String, Object> redisTemplate;
@Override @Override
public void process(IMSendResult<GroupMessageVO> result) { public void process(List<IMSendResult<GroupMessageVO>> results) {
for(IMSendResult<GroupMessageVO> result:results){
GroupMessageVO messageInfo = result.getData(); GroupMessageVO messageInfo = result.getData();
// 空空如也 if (result.getCode().equals(IMSendCode.SUCCESS.code())) {
log.info("消息送达,消息id:{},发送者:{},接收者:{},终端:{}", messageInfo.getId(), result.getSender().getId(), result.getReceiver().getId(), result.getReceiver().getTerminal());
}
}
} }
} }

18
im-platform/src/main/java/com/bx/implatform/listener/PrivateMessageListener.java

@ -1,5 +1,6 @@
package com.bx.implatform.listener; package com.bx.implatform.listener;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.bx.imclient.annotation.IMListener; import com.bx.imclient.annotation.IMListener;
import com.bx.imclient.listener.MessageListener; import com.bx.imclient.listener.MessageListener;
@ -14,6 +15,10 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@Slf4j @Slf4j
@IMListener(type = IMListenerType.PRIVATE_MESSAGE) @IMListener(type = IMListenerType.PRIVATE_MESSAGE)
public class PrivateMessageListener implements MessageListener<PrivateMessageVO> { public class PrivateMessageListener implements MessageListener<PrivateMessageVO> {
@ -23,16 +28,23 @@ public class PrivateMessageListener implements MessageListener<PrivateMessageVO>
private IPrivateMessageService privateMessageService; private IPrivateMessageService privateMessageService;
@Override @Override
public void process(IMSendResult<PrivateMessageVO> result) { public void process(List<IMSendResult<PrivateMessageVO>> results) {
Set<Long> messageIds = new HashSet<>();
for(IMSendResult<PrivateMessageVO> result : results){
PrivateMessageVO messageInfo = result.getData(); PrivateMessageVO messageInfo = result.getData();
// 更新消息状态,这里只处理成功消息,失败的消息继续保持未读状态 // 更新消息状态,这里只处理成功消息,失败的消息继续保持未读状态
if (result.getCode().equals(IMSendCode.SUCCESS.code())) { if (result.getCode().equals(IMSendCode.SUCCESS.code())) {
messageIds.add(messageInfo.getId());
log.info("消息送达,消息id:{},发送者:{},接收者:{},终端:{}", messageInfo.getId(), result.getSender().getId(), result.getReceiver().getId(), result.getReceiver().getTerminal());
}
}
// 批量修改状态
if(CollUtil.isNotEmpty(messageIds)){
UpdateWrapper<PrivateMessage> updateWrapper = new UpdateWrapper<>(); UpdateWrapper<PrivateMessage> updateWrapper = new UpdateWrapper<>();
updateWrapper.lambda().eq(PrivateMessage::getId, messageInfo.getId()) updateWrapper.lambda().in(PrivateMessage::getId, messageIds)
.eq(PrivateMessage::getStatus, MessageStatus.UNSEND.code()) .eq(PrivateMessage::getStatus, MessageStatus.UNSEND.code())
.set(PrivateMessage::getStatus, MessageStatus.SENDED.code()); .set(PrivateMessage::getStatus, MessageStatus.SENDED.code());
privateMessageService.update(updateWrapper); privateMessageService.update(updateWrapper);
log.info("消息已读,消息id:{},发送者:{},接收者:{},终端:{}", messageInfo.getId(), result.getSender().getId(), result.getReceiver().getId(), result.getReceiver().getTerminal());
} }
} }

18
im-platform/src/main/java/com/bx/implatform/service/impl/GroupMessageServiceImpl.java

@ -1,6 +1,8 @@
package com.bx.implatform.service.impl; package com.bx.implatform.service.impl;
import cn.hutool.core.collection.CollStreamUtil;
import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
@ -86,6 +88,7 @@ public class GroupMessageServiceImpl extends ServiceImpl<GroupMessageMapper, Gro
IMGroupMessage<GroupMessageVO> sendMessage = new IMGroupMessage<>(); IMGroupMessage<GroupMessageVO> sendMessage = new IMGroupMessage<>();
sendMessage.setSender(new IMUserInfo(session.getUserId(), session.getTerminal())); sendMessage.setSender(new IMUserInfo(session.getUserId(), session.getTerminal()));
sendMessage.setRecvIds(userIds); sendMessage.setRecvIds(userIds);
sendMessage.setSendResult(false);
sendMessage.setData(msgInfo); sendMessage.setData(msgInfo);
imClient.sendGroupMessage(sendMessage); imClient.sendGroupMessage(sendMessage);
log.info("发送群聊消息,发送id:{},群聊id:{},内容:{}", session.getUserId(), dto.getGroupId(), dto.getContent()); log.info("发送群聊消息,发送id:{},群聊id:{},内容:{}", session.getUserId(), dto.getGroupId(), dto.getContent());
@ -145,10 +148,11 @@ public class GroupMessageServiceImpl extends ServiceImpl<GroupMessageMapper, Gro
public List<GroupMessageVO> loadMessage(Long minId) { public List<GroupMessageVO> loadMessage(Long minId) {
UserSession session = SessionContext.getSession(); UserSession session = SessionContext.getSession();
List<GroupMember> members = groupMemberService.findByUserId(session.getUserId()); List<GroupMember> members = groupMemberService.findByUserId(session.getUserId());
List<Long> ids = members.stream().map(GroupMember::getGroupId).collect(Collectors.toList()); if (CollectionUtil.isEmpty(members)) {
if (CollectionUtil.isEmpty(ids)) {
return new ArrayList<>(); return new ArrayList<>();
} }
Map<Long, GroupMember> groupMemberMap = CollStreamUtil.toIdentityMap(members, GroupMember::getGroupId);
Set<Long> ids = groupMemberMap.keySet();
// 只能拉取最近1个月的 // 只能拉取最近1个月的
Date minDate = DateUtils.addMonths(new Date(), -1); Date minDate = DateUtils.addMonths(new Date(), -1);
LambdaQueryWrapper<GroupMessage> wrapper = Wrappers.lambdaQuery(); LambdaQueryWrapper<GroupMessage> wrapper = Wrappers.lambdaQuery();
@ -157,10 +161,16 @@ public class GroupMessageServiceImpl extends ServiceImpl<GroupMessageMapper, Gro
List<GroupMessage> messages = this.list(wrapper); List<GroupMessage> messages = this.list(wrapper);
// 转成vo // 转成vo
List<GroupMessageVO> vos = messages.stream().map(m -> { List<GroupMessageVO> vos = messages.stream()
.filter(m -> {
//排除加群之前的消息
GroupMember member = groupMemberMap.get(m.getGroupId());
return Objects.nonNull(member) && DateUtil.compare(member.getCreatedTime(), m.getSendTime()) <= 0;
})
.map(m -> {
GroupMessageVO vo = BeanUtils.copyProperties(m, GroupMessageVO.class); GroupMessageVO vo = BeanUtils.copyProperties(m, GroupMessageVO.class);
// 被@用户列表 // 被@用户列表
if (StringUtils.isNotBlank(m.getAtUserIds())) { if (StringUtils.isNotBlank(m.getAtUserIds()) && Objects.nonNull(vo)) {
List<String> atIds = Splitter.on(",").trimResults().splitToList(m.getAtUserIds()); List<String> atIds = Splitter.on(",").trimResults().splitToList(m.getAtUserIds());
vo.setAtUserIds(atIds.stream().map(Long::parseLong).collect(Collectors.toList())); vo.setAtUserIds(atIds.stream().map(Long::parseLong).collect(Collectors.toList()));
} }

1
im-platform/src/main/java/com/bx/implatform/service/impl/PrivateMessageServiceImpl.java

@ -65,6 +65,7 @@ public class PrivateMessageServiceImpl extends ServiceImpl<PrivateMessageMapper,
sendMessage.setRecvId(msgInfo.getRecvId()); sendMessage.setRecvId(msgInfo.getRecvId());
sendMessage.setSendToSelf(true); sendMessage.setSendToSelf(true);
sendMessage.setData(msgInfo); sendMessage.setData(msgInfo);
sendMessage.setSendResult(true);
imClient.sendPrivateMessage(sendMessage); imClient.sendPrivateMessage(sendMessage);
log.info("发送私聊消息,发送id:{},接收id:{},内容:{}", session.getUserId(), dto.getRecvId(), dto.getContent()); log.info("发送私聊消息,发送id:{},接收id:{},内容:{}", session.getUserId(), dto.getRecvId(), dto.getContent());
return msg.getId(); return msg.getId();

2
im-platform/src/main/resources/application.yml

@ -3,6 +3,8 @@ server:
port: 8888 port: 8888
#配置项目的数据源 #配置项目的数据源
spring: spring:
application:
name: im-platform
mvc: mvc:
pathmatch: pathmatch:
matching-strategy: ant_path_matcher matching-strategy: ant_path_matcher

4
im-server/src/main/java/com/bx/imserver/netty/processor/GroupMessageProcessor.java

@ -1,5 +1,6 @@
package com.bx.imserver.netty.processor; package com.bx.imserver.netty.processor;
import cn.hutool.core.util.StrUtil;
import com.bx.imcommon.contant.IMRedisKey; import com.bx.imcommon.contant.IMRedisKey;
import com.bx.imcommon.enums.IMCmdType; import com.bx.imcommon.enums.IMCmdType;
import com.bx.imcommon.enums.IMSendCode; import com.bx.imcommon.enums.IMSendCode;
@ -24,7 +25,6 @@ public class GroupMessageProcessor extends AbstractMessageProcessor<IMRecvInfo>
private final RedisTemplate<String, Object> redisTemplate; private final RedisTemplate<String, Object> redisTemplate;
@Async
@Override @Override
public void process(IMRecvInfo recvInfo) { public void process(IMRecvInfo recvInfo) {
IMUserInfo sender = recvInfo.getSender(); IMUserInfo sender = recvInfo.getSender();
@ -64,7 +64,7 @@ public class GroupMessageProcessor extends AbstractMessageProcessor<IMRecvInfo>
result.setCode(sendCode.code()); result.setCode(sendCode.code());
result.setData(recvInfo.getData()); result.setData(recvInfo.getData());
// 推送到结果队列 // 推送到结果队列
String key = IMRedisKey.IM_RESULT_GROUP_QUEUE; String key = StrUtil.join(":",IMRedisKey.IM_RESULT_GROUP_QUEUE,recvInfo.getServiceName());
redisTemplate.opsForList().rightPush(key, result); redisTemplate.opsForList().rightPush(key, result);
} }
} }

3
im-server/src/main/java/com/bx/imserver/netty/processor/PrivateMessageProcessor.java

@ -1,5 +1,6 @@
package com.bx.imserver.netty.processor; package com.bx.imserver.netty.processor;
import cn.hutool.core.util.StrUtil;
import com.bx.imcommon.contant.IMRedisKey; import com.bx.imcommon.contant.IMRedisKey;
import com.bx.imcommon.enums.IMCmdType; import com.bx.imcommon.enums.IMCmdType;
import com.bx.imcommon.enums.IMSendCode; import com.bx.imcommon.enums.IMSendCode;
@ -57,7 +58,7 @@ public class PrivateMessageProcessor extends AbstractMessageProcessor<IMRecvInfo
result.setCode(sendCode.code()); result.setCode(sendCode.code());
result.setData(recvInfo.getData()); result.setData(recvInfo.getData());
// 推送到结果队列 // 推送到结果队列
String key = IMRedisKey.IM_RESULT_PRIVATE_QUEUE; String key = StrUtil.join(":",IMRedisKey.IM_RESULT_PRIVATE_QUEUE,recvInfo.getServiceName());
redisTemplate.opsForList().rightPush(key, result); redisTemplate.opsForList().rightPush(key, result);
} }
} }

4
im-server/src/main/java/com/bx/imserver/task/AbstractPullMessageTask.java

@ -30,9 +30,9 @@ public abstract class AbstractPullMessageTask implements CommandLineRunner {
} }
} catch (Exception e) { } catch (Exception e) {
log.error("任务调度异常", e); log.error("任务调度异常", e);
Thread.sleep(200);
} }
if (!EXECUTOR_SERVICE.isShutdown()) { if (!EXECUTOR_SERVICE.isShutdown()) {
Thread.sleep(100);
EXECUTOR_SERVICE.execute(this); EXECUTOR_SERVICE.execute(this);
} }
} }
@ -45,5 +45,5 @@ public abstract class AbstractPullMessageTask implements CommandLineRunner {
EXECUTOR_SERVICE.shutdown(); EXECUTOR_SERVICE.shutdown();
} }
public abstract void pullMessage(); public abstract void pullMessage() throws InterruptedException;
} }

8
im-server/src/main/java/com/bx/imserver/task/PullGroupMessageTask.java

@ -12,7 +12,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit; import java.util.Objects;
@Slf4j @Slf4j
@Component @Component
@ -25,11 +25,13 @@ public class PullGroupMessageTask extends AbstractPullMessageTask {
public void pullMessage() { public void pullMessage() {
// 从redis拉取未读消息 // 从redis拉取未读消息
String key = String.join(":", IMRedisKey.IM_MESSAGE_GROUP_QUEUE, IMServerGroup.serverId + ""); String key = String.join(":", IMRedisKey.IM_MESSAGE_GROUP_QUEUE, IMServerGroup.serverId + "");
JSONObject jsonObject = (JSONObject) redisTemplate.opsForList().leftPop(key, 10, TimeUnit.SECONDS); JSONObject jsonObject = (JSONObject) redisTemplate.opsForList().leftPop(key);
if (jsonObject != null) { while (!Objects.isNull(jsonObject)) {
IMRecvInfo recvInfo = jsonObject.toJavaObject(IMRecvInfo.class); IMRecvInfo recvInfo = jsonObject.toJavaObject(IMRecvInfo.class);
AbstractMessageProcessor processor = ProcessorFactory.createProcessor(IMCmdType.GROUP_MESSAGE); AbstractMessageProcessor processor = ProcessorFactory.createProcessor(IMCmdType.GROUP_MESSAGE);
processor.process(recvInfo); processor.process(recvInfo);
// 下一条消息
jsonObject = (JSONObject) redisTemplate.opsForList().leftPop(key);
} }
} }

13
im-server/src/main/java/com/bx/imserver/task/PullPrivateMessageTask.java

@ -12,25 +12,26 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit; import java.util.Objects;
@Slf4j @Slf4j
@Component @Component
@RequiredArgsConstructor @RequiredArgsConstructor
public class PullPrivateMessageTask extends AbstractPullMessageTask { public class PullPrivateMessageTask extends AbstractPullMessageTask {
private final RedisTemplate<String,Object> redisTemplate; private final RedisTemplate<String, Object> redisTemplate;
@Override @Override
public void pullMessage() { public void pullMessage() {
// 从redis拉取未读消息 // 从redis拉取未读消息
String key = String.join(":", IMRedisKey.IM_MESSAGE_PRIVATE_QUEUE,IMServerGroup.serverId+""); String key = String.join(":", IMRedisKey.IM_MESSAGE_PRIVATE_QUEUE, IMServerGroup.serverId + "");
JSONObject jsonObject = (JSONObject)redisTemplate.opsForList().leftPop(key,10, TimeUnit.SECONDS); JSONObject jsonObject = (JSONObject) redisTemplate.opsForList().leftPop(key);
if(jsonObject!=null){ while (!Objects.isNull(jsonObject)) {
IMRecvInfo recvInfo = jsonObject.toJavaObject(IMRecvInfo.class); IMRecvInfo recvInfo = jsonObject.toJavaObject(IMRecvInfo.class);
AbstractMessageProcessor processor = ProcessorFactory.createProcessor(IMCmdType.PRIVATE_MESSAGE); AbstractMessageProcessor processor = ProcessorFactory.createProcessor(IMCmdType.PRIVATE_MESSAGE);
processor.process(recvInfo); processor.process(recvInfo);
// 下一条消息
jsonObject = (JSONObject) redisTemplate.opsForList().leftPop(key);
} }
} }
} }

2
im-ui/src/api/date.js

@ -21,7 +21,7 @@ let toTimeText = (timeStamp, simple) => {
//不属于今年 //不属于今年
timeText = formatDateTime(dateTime); timeText = formatDateTime(dateTime);
if(simple){ if(simple){
timeText = timeText.substring(2,5); timeText = timeText.substr(2,8);
} }
} }
return timeText; return timeText;

1
im-ui/src/components/chat/ChatBox.vue

@ -256,7 +256,6 @@
let textNode = document.createTextNode(txt); let textNode = document.createTextNode(txt);
range.insertNode(textNode) range.insertNode(textNode)
range.collapse(); range.collapse();
} }
let items = (event.clipboardData || window.clipboardData).items let items = (event.clipboardData || window.clipboardData).items
if (items.length) { if (items.length) {

2
im-uniapp/common/date.js

@ -21,7 +21,7 @@ let toTimeText = (timeStamp, simple) => {
//不属于今年 //不属于今年
timeText = formatDateTime(dateTime); timeText = formatDateTime(dateTime);
if(simple){ if(simple){
timeText = timeText.substring(2,5); timeText = timeText.substr(2,8);
} }
} }
return timeText; return timeText;

2
im-uniapp/components/chat-item/chat-item.vue

@ -7,7 +7,7 @@
<view class="chat-right"> <view class="chat-right">
<view class="chat-name"> <view class="chat-name">
<view class="chat-name-text">{{chat.showName}}</view> <view class="chat-name-text">{{chat.showName}}</view>
<view class="chat-time">{{$date.toTimeText(chat.lastSendTime)}}</view> <view class="chat-time">{{$date.toTimeText(chat.lastSendTime,true)}}</view>
</view> </view>
<view class="chat-content"> <view class="chat-content">
<view class="chat-at-text">{{atText}}</view> <view class="chat-at-text">{{atText}}</view>

Loading…
Cancel
Save