Browse Source

过滤html标签

master
Andrews 2 years ago
parent
commit
a442ac2f80
  1. 2
      im-client/src/main/java/com/bx/imclient/IMClient.java
  2. 3
      im-client/src/main/java/com/bx/imclient/config/RedisConfig.java
  3. 12
      im-client/src/main/java/com/bx/imclient/sender/IMSender.java
  4. 41
      im-client/src/main/java/com/bx/imclient/task/AbstractMessageResultTask.java
  5. 2
      im-client/src/main/java/com/bx/imclient/task/GroupMessageResultResultTask.java
  6. 4
      im-client/src/main/java/com/bx/imclient/task/PrivateMessageResultResultTask.java
  7. 12
      im-commom/src/main/java/com/bx/imcommon/contant/IMConstant.java
  8. 40
      im-commom/src/main/java/com/bx/imcommon/contant/IMRedisKey.java
  9. 32
      im-commom/src/main/java/com/bx/imcommon/enums/IMCmdType.java
  10. 21
      im-commom/src/main/java/com/bx/imcommon/enums/IMListenerType.java
  11. 22
      im-commom/src/main/java/com/bx/imcommon/enums/IMSendCode.java
  12. 18
      im-commom/src/main/java/com/bx/imcommon/enums/IMTerminalType.java
  13. 4
      im-commom/src/main/java/com/bx/imcommon/model/IMGroupMessage.java
  14. 2
      im-commom/src/main/java/com/bx/imcommon/model/IMPrivateMessage.java
  15. 2
      im-commom/src/main/java/com/bx/imcommon/model/IMRecvInfo.java
  16. 16
      im-commom/src/main/java/com/bx/imcommon/model/IMSendInfo.java
  17. 2
      im-commom/src/main/java/com/bx/imcommon/model/IMSendResult.java
  18. 43
      im-commom/src/main/java/com/bx/imcommon/util/JwtUtil.java
  19. 100
      im-commom/src/main/java/com/bx/imcommon/util/ThreadPoolExecutorFactory.java
  20. 5
      im-platform/src/main/java/com/bx/implatform/IMPlatformApp.java
  21. 2
      im-platform/src/main/java/com/bx/implatform/config/ICEServer.java
  22. 2
      im-platform/src/main/java/com/bx/implatform/config/ICEServerConfig.java
  23. 7
      im-platform/src/main/java/com/bx/implatform/config/JwtProperties.java
  24. 3
      im-platform/src/main/java/com/bx/implatform/config/MinIoClientConfig.java
  25. 5
      im-platform/src/main/java/com/bx/implatform/config/MvcConfig.java
  26. 6
      im-platform/src/main/java/com/bx/implatform/config/RedisConfig.java
  27. 1
      im-platform/src/main/java/com/bx/implatform/config/SwaggerConfig.java
  28. 23
      im-platform/src/main/java/com/bx/implatform/contant/Constant.java
  29. 41
      im-platform/src/main/java/com/bx/implatform/contant/RedisKey.java
  30. 13
      im-platform/src/main/java/com/bx/implatform/controller/FileController.java
  31. 33
      im-platform/src/main/java/com/bx/implatform/controller/FriendController.java
  32. 45
      im-platform/src/main/java/com/bx/implatform/controller/GroupController.java
  33. 37
      im-platform/src/main/java/com/bx/implatform/controller/GroupMessageController.java
  34. 24
      im-platform/src/main/java/com/bx/implatform/controller/LoginController.java
  35. 37
      im-platform/src/main/java/com/bx/implatform/controller/PrivateMessageController.java
  36. 33
      im-platform/src/main/java/com/bx/implatform/controller/UserController.java
  37. 22
      im-platform/src/main/java/com/bx/implatform/controller/WebrtcController.java
  38. 10
      im-platform/src/main/java/com/bx/implatform/dto/GroupMessageDTO.java
  39. 10
      im-platform/src/main/java/com/bx/implatform/dto/LoginDTO.java
  40. 4
      im-platform/src/main/java/com/bx/implatform/dto/ModifyPwdDTO.java
  41. 10
      im-platform/src/main/java/com/bx/implatform/dto/PrivateMessageDTO.java
  42. 12
      im-platform/src/main/java/com/bx/implatform/dto/RegisterDTO.java
  43. 1
      im-platform/src/main/java/com/bx/implatform/entity/Group.java
  44. 5
      im-platform/src/main/java/com/bx/implatform/entity/GroupMember.java
  45. 4
      im-platform/src/main/java/com/bx/implatform/entity/PrivateMessage.java
  46. 4
      im-platform/src/main/java/com/bx/implatform/entity/User.java
  47. 24
      im-platform/src/main/java/com/bx/implatform/enums/FileType.java
  48. 22
      im-platform/src/main/java/com/bx/implatform/enums/MessageStatus.java
  49. 68
      im-platform/src/main/java/com/bx/implatform/enums/MessageType.java
  50. 41
      im-platform/src/main/java/com/bx/implatform/enums/ResultCode.java
  51. 17
      im-platform/src/main/java/com/bx/implatform/exception/GlobalException.java
  52. 43
      im-platform/src/main/java/com/bx/implatform/exception/GlobalExceptionHandler.java
  53. 8
      im-platform/src/main/java/com/bx/implatform/filter/CacheFilter.java
  54. 15
      im-platform/src/main/java/com/bx/implatform/interceptor/AuthInterceptor.java
  55. 15
      im-platform/src/main/java/com/bx/implatform/interceptor/XssInterceptor.java
  56. 12
      im-platform/src/main/java/com/bx/implatform/listener/GroupMessageListener.java
  57. 9
      im-platform/src/main/java/com/bx/implatform/listener/PrivateMessageListener.java
  58. 1
      im-platform/src/main/java/com/bx/implatform/mapper/FriendMapper.java
  59. 1
      im-platform/src/main/java/com/bx/implatform/mapper/GroupMapper.java
  60. 1
      im-platform/src/main/java/com/bx/implatform/mapper/GroupMemberMapper.java
  61. 1
      im-platform/src/main/java/com/bx/implatform/mapper/GroupMessageMapper.java
  62. 1
      im-platform/src/main/java/com/bx/implatform/mapper/PrivateMessageMapper.java
  63. 1
      im-platform/src/main/java/com/bx/implatform/mapper/UserMapper.java
  64. 2
      im-platform/src/main/java/com/bx/implatform/result/Result.java
  65. 35
      im-platform/src/main/java/com/bx/implatform/result/ResultUtils.java
  66. 5
      im-platform/src/main/java/com/bx/implatform/service/IFriendService.java
  67. 20
      im-platform/src/main/java/com/bx/implatform/service/IGroupMemberService.java
  68. 6
      im-platform/src/main/java/com/bx/implatform/service/IGroupMessageService.java
  69. 13
      im-platform/src/main/java/com/bx/implatform/service/IGroupService.java
  70. 3
      im-platform/src/main/java/com/bx/implatform/service/IPrivateMessageService.java
  71. 3
      im-platform/src/main/java/com/bx/implatform/service/IUserService.java
  72. 19
      im-platform/src/main/java/com/bx/implatform/service/IWebrtcService.java
  73. 84
      im-platform/src/main/java/com/bx/implatform/service/impl/FriendServiceImpl.java
  74. 27
      im-platform/src/main/java/com/bx/implatform/service/impl/GroupMemberServiceImpl.java
  75. 35
      im-platform/src/main/java/com/bx/implatform/service/impl/GroupMessageServiceImpl.java
  76. 13
      im-platform/src/main/java/com/bx/implatform/service/impl/GroupServiceImpl.java
  77. 8
      im-platform/src/main/java/com/bx/implatform/service/impl/PrivateMessageServiceImpl.java
  78. 6
      im-platform/src/main/java/com/bx/implatform/service/impl/UserServiceImpl.java
  79. 20
      im-platform/src/main/java/com/bx/implatform/service/impl/WebrtcServiceImpl.java
  80. 69
      im-platform/src/main/java/com/bx/implatform/service/thirdparty/FileService.java
  81. 9
      im-platform/src/main/java/com/bx/implatform/session/SessionContext.java
  82. 8
      im-platform/src/main/java/com/bx/implatform/session/UserSession.java
  83. 75
      im-platform/src/main/java/com/bx/implatform/util/BeanUtils.java
  84. 7
      im-platform/src/main/java/com/bx/implatform/util/DateTimeUtils.java
  85. 15
      im-platform/src/main/java/com/bx/implatform/util/FileUtil.java
  86. 8
      im-platform/src/main/java/com/bx/implatform/util/ImageUtil.java
  87. 71
      im-platform/src/main/java/com/bx/implatform/util/MinioUtil.java
  88. 7
      im-platform/src/main/java/com/bx/implatform/util/XssUtil.java
  89. 1
      im-platform/src/main/java/com/bx/implatform/vo/FriendVO.java
  90. 1
      im-platform/src/main/java/com/bx/implatform/vo/GroupMemberVO.java
  91. 9
      im-platform/src/main/java/com/bx/implatform/vo/GroupVO.java
  92. 7
      im-platform/src/main/java/com/bx/implatform/vo/UserVO.java
  93. 7
      im-server/src/main/java/com/bx/imserver/IMServerApp.java
  94. 2
      im-server/src/main/java/com/bx/imserver/config/RedisConfig.java
  95. 17
      im-server/src/main/java/com/bx/imserver/constant/ChannelAttrKey.java
  96. 29
      im-server/src/main/java/com/bx/imserver/netty/IMChannelHandler.java
  97. 22
      im-server/src/main/java/com/bx/imserver/netty/IMServerGroup.java
  98. 33
      im-server/src/main/java/com/bx/imserver/netty/UserChannelCtxMap.java
  99. 13
      im-server/src/main/java/com/bx/imserver/netty/processor/AbstractMessageProcessor.java
  100. 34
      im-server/src/main/java/com/bx/imserver/netty/processor/GroupMessageProcessor.java

2
im-client/src/main/java/com/bx/imclient/IMClient.java

@ -14,7 +14,7 @@ import java.util.Map;
@AllArgsConstructor
public class IMClient {
private IMSender imSender;
private final IMSender imSender;
/**
* 判断用户是否在线

3
im-client/src/main/java/com/bx/imclient/config/RedisConfig.java

@ -25,8 +25,7 @@ public class RedisConfig {
}
public FastJsonRedisSerializer fastJsonRedisSerializer(){
FastJsonRedisSerializer <Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
return fastJsonRedisSerializer;
return new FastJsonRedisSerializer<>(Object.class);
}
}

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

@ -1,6 +1,6 @@
package com.bx.imclient.sender;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.CollUtil;
import com.bx.imclient.listener.MessageListenerMulticaster;
import com.bx.imcommon.contant.IMRedisKey;
import com.bx.imcommon.enums.IMCmdType;
@ -79,7 +79,7 @@ public class IMSender {
// 根据群聊每个成员所连的IM-server,进行分组
Map<String, IMUserInfo> sendMap = new HashMap<>();
for (Integer terminal : message.getRecvTerminals()) {
message.getRecvIds().stream().forEach(id -> {
message.getRecvIds().forEach(id -> {
String key = String.join(":", IMRedisKey.IM_USER_SERVER_ID, id.toString(), terminal.toString());
sendMap.put(key,new IMUserInfo(id, terminal));
});
@ -99,7 +99,7 @@ public class IMSender {
// 加入离线列表
offLineUsers.add(entry.getValue());
}
};
}
// 逐个server发送
for (Map.Entry<Integer, List<IMUserInfo>> entry : serverMap.entrySet()) {
IMRecvInfo recvInfo = new IMRecvInfo();
@ -149,8 +149,8 @@ public class IMSender {
}
public Map<Long,List<IMTerminalType>> getOnlineTerminal(List<Long> userIds){
if(CollectionUtil.isEmpty(userIds)){
return Collections.EMPTY_MAP;
if(CollUtil.isEmpty(userIds)){
return Collections.emptyMap();
}
// 把所有用户的key都存起来
Map<String,IMUserInfo> userMap = new HashMap<>();
@ -178,7 +178,7 @@ public class IMSender {
public Boolean isOnline(Long userId) {
String key = String.join(":", IMRedisKey.IM_USER_SERVER_ID, userId.toString(), "*");
return !redisTemplate.keys(key).isEmpty();
return !Objects.requireNonNull(redisTemplate.keys(key)).isEmpty();
}
public List<Long> getOnlineUser(List<Long> userIds){

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

@ -1,48 +1,43 @@
package com.bx.imclient.task;
import com.bx.imcommon.util.ThreadPoolExecutorFactory;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import javax.annotation.PreDestroy;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@Slf4j
public abstract class AbstractMessageResultTask implements CommandLineRunner {
private int threadNum = 8;
private ExecutorService executorService = Executors.newFixedThreadPool(threadNum);
private static final ExecutorService EXECUTOR_SERVICE = ThreadPoolExecutorFactory.getThreadPoolExecutor();
@Override
public void run(String... args) throws Exception {
public void run(String... args) {
// 初始化定时器
for(int i=0;i<threadNum;i++){
executorService.execute(new Runnable() {
@SneakyThrows
@Override
public void run() {
try{
pullMessage();
}catch (Exception e){
log.error("任务调度异常",e);
Thread.sleep(200);
}
if(!executorService.isShutdown()){
executorService.execute(this);
}
EXECUTOR_SERVICE.execute(new Runnable() {
@SneakyThrows
@Override
public void run() {
try{
pullMessage();
}catch (Exception e){
log.error("任务调度异常",e);
Thread.sleep(200);
}
if(!EXECUTOR_SERVICE.isShutdown()){
EXECUTOR_SERVICE.execute(this);
}
});
}
}
});
}
@PreDestroy
public void destroy(){
log.info("{}线程任务关闭",this.getClass().getSimpleName());
executorService.shutdown();
EXECUTOR_SERVICE.shutdown();
}
public abstract void pullMessage();

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

@ -19,7 +19,7 @@ public class GroupMessageResultResultTask extends AbstractMessageResultTask {
@Resource(name = "IMRedisTemplate")
private RedisTemplate<String,Object> redisTemplate;
private MessageListenerMulticaster listenerMulticaster;
private final MessageListenerMulticaster listenerMulticaster;
@Override
public void pullMessage() {

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

@ -13,8 +13,6 @@ import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
;
@Slf4j
@Component
@AllArgsConstructor
@ -24,7 +22,7 @@ public class PrivateMessageResultResultTask extends AbstractMessageResultTask {
@Resource(name = "IMRedisTemplate")
private RedisTemplate<String,Object> redisTemplate;
private MessageListenerMulticaster listenerMulticaster;
private final MessageListenerMulticaster listenerMulticaster;
@Override
public void pullMessage() {

12
im-commom/src/main/java/com/bx/imcommon/contant/IMConstant.java

@ -1,11 +1,17 @@
package com.bx.imcommon.contant;
public final class IMConstant {
public class IMConstant {
private IMConstant() {
}
// 在线状态过期时间 600s
/**
* 在线状态过期时间 600s
*/
public static final long ONLINE_TIMEOUT_SECOND = 600;
// 消息允许撤回时间 300s
/**
* 消息允许撤回时间 300s
*/
public static final long ALLOW_RECALL_SECOND = 300;

40
im-commom/src/main/java/com/bx/imcommon/contant/IMRedisKey.java

@ -1,18 +1,32 @@
package com.bx.imcommon.contant;
public class IMRedisKey {
public final class IMRedisKey {
// 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_MESSAGE_PRIVATE_QUEUE = "im:message:private";
// 未读群聊消息队列
public final static String IM_MESSAGE_GROUP_QUEUE = "im:message:group";
// 私聊消息发送结果队列
public final static String IM_RESULT_PRIVATE_QUEUE = "im:result:private";
// 群聊消息发送结果队列
public final static String IM_RESULT_GROUP_QUEUE = "im:result:group";
private IMRedisKey() {}
/**
* im-server最大id,从0开始递增
*/
public static final String IM_MAX_SERVER_ID = "im:max_server_id";
/**
* 用户ID所连接的IM-server的ID
*/
public static final String IM_USER_SERVER_ID = "im:user:server_id";
/**
* 未读私聊消息队列
*/
public static final String IM_MESSAGE_PRIVATE_QUEUE = "im:message:private";
/**
* 未读群聊消息队列
*/
public static final String IM_MESSAGE_GROUP_QUEUE = "im:message:group";
/**
* 私聊消息发送结果队列
*/
public static final String IM_RESULT_PRIVATE_QUEUE = "im:result:private";
/**
* 群聊消息发送结果队列
*/
public static final String IM_RESULT_GROUP_QUEUE = "im:result:group";
}

32
im-commom/src/main/java/com/bx/imcommon/enums/IMCmdType.java

@ -1,16 +1,30 @@
package com.bx.imcommon.enums;
import lombok.AllArgsConstructor;
@AllArgsConstructor
public enum IMCmdType {
LOGIN(0,"登陆"),
HEART_BEAT(1,"心跳"),
FORCE_LOGUT(2,"强制下线"),
PRIVATE_MESSAGE(3,"私聊消息"),
GROUP_MESSAGE(4,"群发消息");
/**
* 登陆
*/
LOGIN(0, "登陆"),
/**
* 心跳
*/
HEART_BEAT(1, "心跳"),
/**
* 强制下线
*/
FORCE_LOGUT(2, "强制下线"),
/**
* 私聊消息
*/
PRIVATE_MESSAGE(3, "私聊消息"),
/**
* 群发消息
*/
GROUP_MESSAGE(4, "群发消息");
private final Integer code;
@ -18,8 +32,8 @@ public enum IMCmdType {
private final String desc;
public static IMCmdType fromCode(Integer code){
for (IMCmdType typeEnum:values()) {
public static IMCmdType fromCode(Integer code) {
for (IMCmdType typeEnum : values()) {
if (typeEnum.code.equals(code)) {
return typeEnum;
}
@ -28,7 +42,7 @@ public enum IMCmdType {
}
public Integer code(){
public Integer code() {
return this.code;
}

21
im-commom/src/main/java/com/bx/imcommon/enums/IMListenerType.java

@ -3,18 +3,25 @@ package com.bx.imcommon.enums;
import lombok.AllArgsConstructor;
@AllArgsConstructor
public enum IMListenerType{
ALL(0,"全部消息"),
PRIVATE_MESSAGE(1,"私聊消息"),
GROUP_MESSAGE(2,"群聊消息");
public enum IMListenerType {
/**
* 全部消息
*/
ALL(0, "全部消息"),
/**
* 私聊消息
*/
PRIVATE_MESSAGE(1, "私聊消息"),
/**
* 群聊消息
*/
GROUP_MESSAGE(2, "群聊消息");
private final Integer code;
private final String desc;
public Integer code(){
public Integer code() {
return this.code;
}

22
im-commom/src/main/java/com/bx/imcommon/enums/IMSendCode.java

@ -5,15 +5,27 @@ import lombok.AllArgsConstructor;
@AllArgsConstructor
public enum IMSendCode {
SUCCESS(0,"发送成功"),
NOT_ONLINE(1,"对方当前不在线"),
NOT_FIND_CHANNEL(2,"未找到对方的channel"),
UNKONW_ERROR(9999,"未知异常");
/**
* 发送成功
*/
SUCCESS(0, "发送成功"),
/**
* 对方当前不在线
*/
NOT_ONLINE(1, "对方当前不在线"),
/**
* 未找到对方的channel
*/
NOT_FIND_CHANNEL(2, "未找到对方的channel"),
/**
* 未知异常
*/
UNKONW_ERROR(9999, "未知异常");
private final Integer code;
private final String desc;
public Integer code(){
public Integer code() {
return this.code;
}

18
im-commom/src/main/java/com/bx/imcommon/enums/IMTerminalType.java

@ -9,16 +9,22 @@ import java.util.stream.Collectors;
@AllArgsConstructor
public enum IMTerminalType {
WEB(0,"web"),
APP(1,"app");
/**
* web
*/
WEB(0, "web"),
/**
* app
*/
APP(1, "app");
private final Integer code;
private final String desc;
public static IMTerminalType fromCode(Integer code){
for (IMTerminalType typeEnum:values()) {
public static IMTerminalType fromCode(Integer code) {
for (IMTerminalType typeEnum : values()) {
if (typeEnum.code.equals(code)) {
return typeEnum;
}
@ -26,11 +32,11 @@ public enum IMTerminalType {
return null;
}
public static List<Integer> codes(){
public static List<Integer> codes() {
return Arrays.stream(values()).map(IMTerminalType::code).collect(Collectors.toList());
}
public Integer code(){
public Integer code() {
return this.code;
}

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

@ -17,7 +17,7 @@ public class IMGroupMessage<T> {
/**
* 接收者id列表(群成员列表,为空则不会推送)
*/
private List<Long> recvIds = new LinkedList<>();
private List<Long> recvIds = new LinkedList<>();
/**
@ -36,7 +36,7 @@ public class IMGroupMessage<T> {
private Boolean sendResult = true;
/**
* 消息内容
* 消息内容
*/
private T data;

2
im-commom/src/main/java/com/bx/imcommon/model/IMPrivateMessage.java

@ -36,7 +36,7 @@ public class IMPrivateMessage<T> {
private Boolean sendResult = true;
/**
* 消息内容
* 消息内容
*/
private T data;

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

@ -23,7 +23,7 @@ public class IMRecvInfo {
List<IMUserInfo> receivers;
/**
* 是否需要回调发送结果
* 是否需要回调发送结果
*/
private Boolean sendResult;

16
im-commom/src/main/java/com/bx/imcommon/model/IMSendInfo.java

@ -5,14 +5,14 @@ import lombok.Data;
@Data
public class IMSendInfo<T> {
/**
* 命令
*/
private Integer cmd;
/**
* 命令
*/
private Integer cmd;
/**
* 推送消息体
*/
private T data;
/**
* 推送消息体
*/
private T data;
}

2
im-commom/src/main/java/com/bx/imcommon/model/IMSendResult.java

@ -21,7 +21,7 @@ public class IMSendResult<T> {
private Integer code;
/**
* 消息内容
* 消息内容
*/
private T data;

43
im-commom/src/main/java/com/bx/imcommon/util/JwtUtil.java

@ -8,19 +8,23 @@ import com.auth0.jwt.exceptions.JWTVerificationException;
import java.util.Date;
public class JwtUtil {
public final class JwtUtil {
private JwtUtil() {
}
/**
* 生成jwt字符串 JWT(json web token)
* @param userId 用户id
* @param info 用户细腻系
*
* @param userId 用户id
* @param info 用户细腻系
* @param expireIn 过期时间
* @param secret 秘钥
* @param secret 秘钥
* @return token
* */
public static String sign(Long userId, String info,long expireIn,String secret) {
*/
public static String sign(Long userId, String info, long expireIn, String secret) {
try {
Date date = new Date(System.currentTimeMillis() + expireIn*1000);
Date date = new Date(System.currentTimeMillis() + expireIn * 1000);
Algorithm algorithm = Algorithm.HMAC256(secret);
return JWT.create()
//将userId保存到token里面
@ -38,44 +42,47 @@ public class JwtUtil {
/**
* 根据token获取userId
* @param token 登录token
*
* @param token 登录token
* @return 用户id
* */
*/
public static Long getUserId(String token) {
try {
String userId = JWT.decode(token).getAudience().get(0);
return Long.parseLong(userId);
}catch (JWTDecodeException e) {
} catch (JWTDecodeException e) {
return null;
}
}
/**
* 根据token获取用户数据
*
* @param token 用户登录token
* @return 用户数据
* */
*/
public static String getInfo(String token) {
try {
return JWT.decode(token).getClaim("info").asString();
}catch (JWTDecodeException e) {
} catch (JWTDecodeException e) {
return null;
}
}
/**
* 校验token
* @param token 用户登录token
*
* @param token 用户登录token
* @param secret 秘钥
* @return true/false
* */
public static Boolean checkSign(String token,String secret) {
try{
Algorithm algorithm = Algorithm.HMAC256(secret);
*/
public static Boolean checkSign(String token, String secret) {
try {
Algorithm algorithm = Algorithm.HMAC256(secret);
JWTVerifier verifier = JWT.require(algorithm).build();
verifier.verify(token);
return true;
}catch (JWTVerificationException e) {
} catch (JWTVerificationException e) {
return false;
}
}

100
im-commom/src/main/java/com/bx/imcommon/util/ThreadPoolExecutorFactory.java

@ -0,0 +1,100 @@
package com.bx.imcommon.util;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* 创建单例线程池
* @author Andrews
* @date 2023/11/30 11:12
*/
public final class ThreadPoolExecutorFactory {
/**
* 机器的CPU核数:Runtime.getRuntime().availableProcessors()
* corePoolSize 池中所保存的线程数包括空闲线程
* CPU 密集型核心线程数 = CPU核数 + 1
* IO 密集型核心线程数 = CPU核数 * 2
*/
private static final int CORE_POOL_SIZE = Runtime.getRuntime().availableProcessors() * 2;
/**
* maximumPoolSize - 池中允许的最大线程数(采用LinkedBlockingQueue时没有作用)
*/
private static final int MAX_IMUM_POOL_SIZE = 100;
/**
* keepAliveTime -当线程数大于核心时此为终止前多余的空闲线程等待新任务的最长时间线程池维护线程所允许的空闲时间
*/
private static final int KEEP_ALIVE_TIME = 1000;
/**
* 等待队列的大小默认是无界的性能损耗的关键
*/
private static final int QUEUE_SIZE = 200;
/**
* 线程池对象
*/
private static volatile ThreadPoolExecutor threadPoolExecutor = null;
/**
* 构造方法私有化
*/
private ThreadPoolExecutorFactory() {
if (null == threadPoolExecutor) {
threadPoolExecutor = ThreadPoolExecutorFactory.getThreadPoolExecutor();
}
}
/**
* 重写readResolve方法
*/
private Object readResolve() {
//重写readResolve方法,防止序列化破坏单例
return ThreadPoolExecutorFactory.getThreadPoolExecutor();
}
/**
* 双检锁创建线程安全的单例
*/
public static ThreadPoolExecutor getThreadPoolExecutor() {
if (null == threadPoolExecutor) {
synchronized (ThreadPoolExecutorFactory.class) {
if (null == threadPoolExecutor) {
threadPoolExecutor = new ThreadPoolExecutor(
//核心线程数
CORE_POOL_SIZE,
//最大线程数,包含临时线程
MAX_IMUM_POOL_SIZE,
//临时线程的存活时间
KEEP_ALIVE_TIME,
//时间单位(毫秒)
TimeUnit.MILLISECONDS,
//等待队列
new LinkedBlockingQueue<>(QUEUE_SIZE),
//拒绝策略
new ThreadPoolExecutor.CallerRunsPolicy()
);
}
}
}
return threadPoolExecutor;
}
/**
* 关闭线程池
*/
public void shutDown() {
if (threadPoolExecutor != null) {
threadPoolExecutor.shutdown();
}
}
public void execute(Runnable runnable) {
if (runnable == null) {
return;
}
threadPoolExecutor.execute(runnable);
}
}

5
im-platform/src/main/java/com/bx/implatform/IMPlatformApp.java

@ -7,14 +7,13 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Slf4j
@EnableAspectJAutoProxy(exposeProxy = true)
@MapperScan(basePackages = {"com.bx.implatform.mapper"})
@SpringBootApplication(exclude= {SecurityAutoConfiguration.class })// 禁用secrity
@SpringBootApplication(exclude = {SecurityAutoConfiguration.class})// 禁用secrity
public class IMPlatformApp {
public static void main(String[] args) {
SpringApplication.run(IMPlatformApp.class,args);
SpringApplication.run(IMPlatformApp.class, args);
}
}

2
im-platform/src/main/java/com/bx/implatform/config/ICEServer.java

@ -1,9 +1,7 @@
package com.bx.implatform.config;
import lombok.Data;
@Data
public class ICEServer {
private String urls;

2
im-platform/src/main/java/com/bx/implatform/config/ICEServerConfig.java

@ -9,7 +9,7 @@ import java.util.List;
@Data
@Component
@ConfigurationProperties(prefix="webrtc")
@ConfigurationProperties(prefix = "webrtc")
public class ICEServerConfig {
private List<ICEServer> iceServers = new ArrayList<>();

7
im-platform/src/main/java/com/bx/implatform/config/JwtProperties.java

@ -1,6 +1,5 @@
package com.bx.implatform.config;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@ -10,13 +9,13 @@ import org.springframework.stereotype.Component;
public class JwtProperties {
@Value("${jwt.accessToken.expireIn}")
private Integer accessTokenExpireIn;
private Integer accessTokenExpireIn;
@Value("${jwt.accessToken.secret}")
private String accessTokenSecret;
private String accessTokenSecret;
@Value("${jwt.refreshToken.expireIn}")
private Integer refreshTokenExpireIn ;
private Integer refreshTokenExpireIn;
@Value("${jwt.refreshToken.secret}")
private String refreshTokenSecret;

3
im-platform/src/main/java/com/bx/implatform/config/MinIoClientConfig.java

@ -5,7 +5,6 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MinIoClientConfig {
@ -18,7 +17,7 @@ public class MinIoClientConfig {
@Bean
public MinioClient minioClient(){
public MinioClient minioClient() {
// 注入minio 客户端
return MinioClient.builder()
.endpoint(endpoint)

5
im-platform/src/main/java/com/bx/implatform/config/MvcConfig.java

@ -10,7 +10,6 @@ import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
@AllArgsConstructor
public class MvcConfig implements WebMvcConfigurer {
@ -24,12 +23,12 @@ public class MvcConfig implements WebMvcConfigurer {
.addPathPatterns("/**");
registry.addInterceptor(authInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/login","/logout","/register","/refreshToken",
.excludePathPatterns("/login", "/logout", "/register", "/refreshToken",
"/swagger-resources/**", "/webjars/**", "/v2/**", "/swagger-ui.html/**");
}
@Bean
public PasswordEncoder passwordEncoder(){
public PasswordEncoder passwordEncoder() {
// 使用BCrypt加密密码
return new BCryptPasswordEncoder();
}

6
im-platform/src/main/java/com/bx/implatform/config/RedisConfig.java

@ -26,7 +26,7 @@ import org.springframework.data.redis.serializer.StringRedisSerializer;
import javax.annotation.Resource;
import java.time.Duration;
import java.util.Objects;
@EnableCaching
@Configuration
@ -52,7 +52,7 @@ public class RedisConfig extends CachingConfigurerSupport {
}
@Bean
public Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer(){
public Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer() {
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
@ -68,7 +68,7 @@ public class RedisConfig extends CachingConfigurerSupport {
@Bean
@Override
public CacheResolver cacheResolver() {
return new SimpleCacheResolver(cacheManager());
return new SimpleCacheResolver(Objects.requireNonNull(cacheManager()));
}
@Bean

1
im-platform/src/main/java/com/bx/implatform/config/SwaggerConfig.java

@ -11,7 +11,6 @@ import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2
public class SwaggerConfig {

23
im-platform/src/main/java/com/bx/implatform/contant/Constant.java

@ -1,12 +1,21 @@
package com.bx.implatform.contant;
public final class Constant {
public class Constant {
// 最大图片上传大小
public static final long MAX_IMAGE_SIZE = 5*1024*1024;
// 最大上传文件大小
public static final long MAX_FILE_SIZE = 10*1024*1024;
// 群聊最大人数
private 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;
}

41
im-platform/src/main/java/com/bx/implatform/contant/RedisKey.java

@ -1,18 +1,33 @@
package com.bx.implatform.contant;
public class RedisKey {
public final class RedisKey {
// 已读群聊消息位置(已读最大id)
public final static String IM_GROUP_READED_POSITION = "im:readed:group:position";
// webrtc 会话信息
public final static String IM_WEBRTC_SESSION = "im:webrtc:session";
// 缓存前缀
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";
private RedisKey() {
}
/**
* 已读群聊消息位置(已读最大id)
*/
public static final String IM_GROUP_READED_POSITION = "im:readed:group:position";
/**
* webrtc 会话信息
*/
public static final String IM_WEBRTC_SESSION = "im:webrtc:session";
/**
* 缓存前缀
*/
public static final String IM_CACHE = "im:cache:";
/**
* 缓存是否好友bool
*/
public static final String IM_CACHE_FRIEND = IM_CACHE + "friend";
/**
* 缓存群聊信息
*/
public static final String IM_CACHE_GROUP = IM_CACHE + "group";
/**
* 缓存群聊成员id
*/
public static final String IM_CACHE_GROUP_MEMBER_ID = IM_CACHE + "group_member_ids";
}

13
im-platform/src/main/java/com/bx/implatform/controller/FileController.java

@ -6,31 +6,30 @@ import com.bx.implatform.service.thirdparty.FileService;
import com.bx.implatform.vo.UploadImageVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
@Slf4j
@RestController
@Api(tags = "文件上传")
@AllArgsConstructor
@RequiredArgsConstructor
public class FileController {
private FileService fileService;
private final FileService fileService;
@ApiOperation(value = "上传图片",notes="上传图片,上传后返回原图和缩略图的url")
@ApiOperation(value = "上传图片", notes = "上传图片,上传后返回原图和缩略图的url")
@PostMapping("/image/upload")
public Result<UploadImageVO> uploadImage(MultipartFile file) {
return ResultUtils.success(fileService.uploadImage(file));
}
@ApiOperation(value = "上传文件",notes="上传文件,上传后返回文件url")
@ApiOperation(value = "上传文件", notes = "上传文件,上传后返回文件url")
@PostMapping("/file/upload")
public Result<String> uploadFile(MultipartFile file) {
return ResultUtils.success(fileService.uploadFile(file),"");
return ResultUtils.success(fileService.uploadFile(file), "");
}
}

33
im-platform/src/main/java/com/bx/implatform/controller/FriendController.java

@ -8,7 +8,7 @@ import com.bx.implatform.session.SessionContext;
import com.bx.implatform.vo.FriendVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
@ -19,16 +19,16 @@ import java.util.stream.Collectors;
@Api(tags = "好友")
@RestController
@RequestMapping("/friend")
@AllArgsConstructor
@RequiredArgsConstructor
public class FriendController {
private IFriendService friendService;
private final IFriendService friendService;
@GetMapping("/list")
@ApiOperation(value = "好友列表",notes="获取好友列表")
public Result< List<FriendVO>> findFriends(){
@ApiOperation(value = "好友列表", notes = "获取好友列表")
public Result<List<FriendVO>> findFriends() {
List<Friend> friends = friendService.findFriendByUserId(SessionContext.getSession().getUserId());
List<FriendVO> vos = friends.stream().map(f->{
List<FriendVO> vos = friends.stream().map(f -> {
FriendVO vo = new FriendVO();
vo.setId(f.getFriendId());
vo.setHeadImage(f.getFriendHeadImage());
@ -39,31 +39,30 @@ public class FriendController {
}
@PostMapping("/add")
@ApiOperation(value = "添加好友",notes="双方建立好友关系")
public Result addFriend(@NotEmpty(message = "好友id不可为空") @RequestParam("friendId") Long friendId){
friendService.addFriend(friendId);
return ResultUtils.success();
@ApiOperation(value = "添加好友", notes = "双方建立好友关系")
public Result addFriend(@NotEmpty(message = "好友id不可为空") @RequestParam("friendId") Long friendId) {
friendService.addFriend(friendId);
return ResultUtils.success();
}
@GetMapping("/find/{friendId}")
@ApiOperation(value = "查找好友信息",notes="查找好友信息")
public Result<FriendVO> findFriend(@NotEmpty(message = "好友id不可为空") @PathVariable("friendId") Long friendId){
@ApiOperation(value = "查找好友信息", notes = "查找好友信息")
public Result<FriendVO> findFriend(@NotEmpty(message = "好友id不可为空") @PathVariable("friendId") Long friendId) {
return ResultUtils.success(friendService.findFriend(friendId));
}
@DeleteMapping("/delete/{friendId}")
@ApiOperation(value = "删除好友",notes="解除好友关系")
public Result delFriend(@NotEmpty(message = "好友id不可为空") @PathVariable("friendId") Long friendId){
@ApiOperation(value = "删除好友", notes = "解除好友关系")
public Result delFriend(@NotEmpty(message = "好友id不可为空") @PathVariable("friendId") Long friendId) {
friendService.delFriend(friendId);
return ResultUtils.success();
}
@PutMapping("/update")
@ApiOperation(value = "更新好友信息",notes="更新好友头像或昵称")
public Result modifyFriend(@Valid @RequestBody FriendVO vo){
@ApiOperation(value = "更新好友信息", notes = "更新好友头像或昵称")
public Result modifyFriend(@Valid @RequestBody FriendVO vo) {
friendService.update(vo);
return ResultUtils.success();
}

45
im-platform/src/main/java/com/bx/implatform/controller/GroupController.java

@ -8,78 +8,77 @@ import com.bx.implatform.vo.GroupMemberVO;
import com.bx.implatform.vo.GroupVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.util.List;
@Api(tags = "群聊")
@RestController
@RequestMapping("/group")
@AllArgsConstructor
@RequiredArgsConstructor
public class GroupController {
private IGroupService groupService;
private final IGroupService groupService;
@ApiOperation(value = "创建群聊",notes="创建群聊")
@ApiOperation(value = "创建群聊", notes = "创建群聊")
@PostMapping("/create")
public Result<GroupVO> createGroup(@Valid @RequestBody GroupVO vo){
public Result<GroupVO> createGroup(@Valid @RequestBody GroupVO vo) {
return ResultUtils.success(groupService.createGroup(vo));
}
@ApiOperation(value = "修改群聊信息",notes="修改群聊信息")
@ApiOperation(value = "修改群聊信息", notes = "修改群聊信息")
@PutMapping("/modify")
public Result<GroupVO> modifyGroup(@Valid @RequestBody GroupVO vo){
public Result<GroupVO> modifyGroup(@Valid @RequestBody GroupVO vo) {
return ResultUtils.success(groupService.modifyGroup(vo));
}
@ApiOperation(value = "解散群聊",notes="解散群聊")
@ApiOperation(value = "解散群聊", notes = "解散群聊")
@DeleteMapping("/delete/{groupId}")
public Result deleteGroup(@NotNull(message = "群聊id不能为空") @PathVariable Long groupId){
public Result deleteGroup(@NotNull(message = "群聊id不能为空") @PathVariable Long groupId) {
groupService.deleteGroup(groupId);
return ResultUtils.success();
}
@ApiOperation(value = "查询群聊",notes="查询单个群聊信息")
@ApiOperation(value = "查询群聊", notes = "查询单个群聊信息")
@GetMapping("/find/{groupId}")
public Result<GroupVO> findGroup(@NotNull(message = "群聊id不能为空") @PathVariable Long groupId){
public Result<GroupVO> findGroup(@NotNull(message = "群聊id不能为空") @PathVariable Long groupId) {
return ResultUtils.success(groupService.findById(groupId));
}
@ApiOperation(value = "查询群聊列表",notes="查询群聊列表")
@ApiOperation(value = "查询群聊列表", notes = "查询群聊列表")
@GetMapping("/list")
public Result<List<GroupVO>> findGroups(){
public Result<List<GroupVO>> findGroups() {
return ResultUtils.success(groupService.findGroups());
}
@ApiOperation(value = "邀请进群",notes="邀请好友进群")
@ApiOperation(value = "邀请进群", notes = "邀请好友进群")
@PostMapping("/invite")
public Result invite(@Valid @RequestBody GroupInviteVO vo){
public Result invite(@Valid @RequestBody GroupInviteVO vo) {
groupService.invite(vo);
return ResultUtils.success();
}
@ApiOperation(value = "查询群聊成员",notes="查询群聊成员")
@ApiOperation(value = "查询群聊成员", notes = "查询群聊成员")
@GetMapping("/members/{groupId}")
public Result<List<GroupMemberVO>> findGroupMembers(@NotNull(message = "群聊id不能为空") @PathVariable Long groupId){
public Result<List<GroupMemberVO>> findGroupMembers(@NotNull(message = "群聊id不能为空") @PathVariable Long groupId) {
return ResultUtils.success(groupService.findGroupMembers(groupId));
}
@ApiOperation(value = "退出群聊",notes="退出群聊")
@ApiOperation(value = "退出群聊", notes = "退出群聊")
@DeleteMapping("/quit/{groupId}")
public Result quitGroup(@NotNull(message = "群聊id不能为空") @PathVariable Long groupId){
public Result quitGroup(@NotNull(message = "群聊id不能为空") @PathVariable Long groupId) {
groupService.quitGroup(groupId);
return ResultUtils.success();
}
@ApiOperation(value = "踢出群聊",notes="将用户踢出群聊")
@ApiOperation(value = "踢出群聊", notes = "将用户踢出群聊")
@DeleteMapping("/kick/{groupId}")
public Result kickGroup(@NotNull(message = "群聊id不能为空") @PathVariable Long groupId,
@NotNull(message = "用户id不能为空") @RequestParam Long userId){
groupService.kickGroup(groupId,userId);
@NotNull(message = "用户id不能为空") @RequestParam Long userId) {
groupService.kickGroup(groupId, userId);
return ResultUtils.success();
}

37
im-platform/src/main/java/com/bx/implatform/controller/GroupMessageController.java

@ -7,66 +7,63 @@ import com.bx.implatform.service.IGroupMessageService;
import com.bx.implatform.vo.GroupMessageVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.util.List;
@Api(tags = "群聊消息")
@RestController
@RequestMapping("/message/group")
@AllArgsConstructor
@RequiredArgsConstructor
public class GroupMessageController {
private IGroupMessageService groupMessageService;
private final IGroupMessageService groupMessageService;
@PostMapping("/send")
@ApiOperation(value = "发送群聊消息",notes="发送群聊消息")
public Result<Long> sendMessage(@Valid @RequestBody GroupMessageDTO vo){
@ApiOperation(value = "发送群聊消息", notes = "发送群聊消息")
public Result<Long> sendMessage(@Valid @RequestBody GroupMessageDTO vo) {
return ResultUtils.success(groupMessageService.sendMessage(vo));
}
@DeleteMapping("/recall/{id}")
@ApiOperation(value = "撤回消息",notes="撤回群聊消息")
public Result<Long> recallMessage(@NotNull(message = "消息id不能为空") @PathVariable Long id){
@ApiOperation(value = "撤回消息", notes = "撤回群聊消息")
public Result<Long> recallMessage(@NotNull(message = "消息id不能为空") @PathVariable Long id) {
groupMessageService.recallMessage(id);
return ResultUtils.success();
}
// todo 删除
@PostMapping("/pullUnreadMessage")
@ApiOperation(value = "拉取未读消息",notes="拉取未读消息")
public Result pullUnreadMessage(){
@ApiOperation(value = "拉取未读消息", notes = "拉取未读消息")
public Result pullUnreadMessage() {
groupMessageService.pullUnreadMessage();
return ResultUtils.success();
}
@GetMapping("/loadMessage")
@ApiOperation(value = "拉取消息",notes="拉取消息,一次最多拉取100条")
public Result<List<GroupMessageVO>> loadMessage(@RequestParam Long minId){
@ApiOperation(value = "拉取消息", notes = "拉取消息,一次最多拉取100条")
public Result<List<GroupMessageVO>> loadMessage(@RequestParam Long minId) {
return ResultUtils.success(groupMessageService.loadMessage(minId));
}
@PutMapping("/readed")
@ApiOperation(value = "消息已读",notes="将群聊中的消息状态置为已读")
public Result readedMessage(@RequestParam Long groupId){
@ApiOperation(value = "消息已读", notes = "将群聊中的消息状态置为已读")
public Result readedMessage(@RequestParam Long groupId) {
groupMessageService.readedMessage(groupId);
return ResultUtils.success();
}
@GetMapping("/history")
@ApiOperation(value = "查询聊天记录",notes="查询聊天记录")
@ApiOperation(value = "查询聊天记录", notes = "查询聊天记录")
public Result<List<GroupMessageVO>> 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));
@NotNull(message = "页码不能为空") @RequestParam Long page,
@NotNull(message = "size不能为空") @RequestParam Long size) {
return ResultUtils.success(groupMessageService.findHistoryMessage(groupId, page, size));
}
}

24
im-platform/src/main/java/com/bx/implatform/controller/LoginController.java

@ -9,46 +9,44 @@ import com.bx.implatform.service.IUserService;
import com.bx.implatform.vo.LoginVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
@Api(tags = "用户登录和注册")
@RestController
@AllArgsConstructor
@RequiredArgsConstructor
public class LoginController {
private IUserService userService;
private final IUserService userService;
@PostMapping("/login")
@ApiOperation(value = "用户注册",notes="用户注册")
public Result register(@Valid @RequestBody LoginDTO dto){
@ApiOperation(value = "用户注册", notes = "用户注册")
public Result register(@Valid @RequestBody LoginDTO dto) {
LoginVO vo = userService.login(dto);
return ResultUtils.success(vo);
}
@PutMapping("/refreshToken")
@ApiOperation(value = "刷新token",notes="用refreshtoken换取新的token")
public Result refreshToken(@RequestHeader("refreshToken")String refreshToken){
@ApiOperation(value = "刷新token", notes = "用refreshtoken换取新的token")
public Result refreshToken(@RequestHeader("refreshToken") String refreshToken) {
LoginVO vo = userService.refreshToken(refreshToken);
return ResultUtils.success(vo);
}
@PostMapping("/register")
@ApiOperation(value = "用户注册",notes="用户注册")
public Result register(@Valid @RequestBody RegisterDTO dto){
@ApiOperation(value = "用户注册", notes = "用户注册")
public Result register(@Valid @RequestBody RegisterDTO dto) {
userService.register(dto);
return ResultUtils.success();
}
@PutMapping("/modifyPwd")
@ApiOperation(value = "修改密码",notes="修改用户密码")
public Result update(@Valid @RequestBody ModifyPwdDTO dto){
@ApiOperation(value = "修改密码", notes = "修改用户密码")
public Result update(@Valid @RequestBody ModifyPwdDTO dto) {
userService.modifyPassword(dto);
return ResultUtils.success();
}

37
im-platform/src/main/java/com/bx/implatform/controller/PrivateMessageController.java

@ -7,7 +7,7 @@ import com.bx.implatform.service.IPrivateMessageService;
import com.bx.implatform.vo.PrivateMessageVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
@ -17,53 +17,52 @@ import java.util.List;
@Api(tags = "私聊消息")
@RestController
@RequestMapping("/message/private")
@AllArgsConstructor
@RequiredArgsConstructor
public class PrivateMessageController {
private IPrivateMessageService privateMessageService;
private final IPrivateMessageService privateMessageService;
@PostMapping("/send")
@ApiOperation(value = "发送消息",notes="发送私聊消息")
public Result<Long> sendMessage(@Valid @RequestBody PrivateMessageDTO vo){
@ApiOperation(value = "发送消息", notes = "发送私聊消息")
public Result<Long> sendMessage(@Valid @RequestBody PrivateMessageDTO vo) {
return ResultUtils.success(privateMessageService.sendMessage(vo));
}
@DeleteMapping("/recall/{id}")
@ApiOperation(value = "撤回消息",notes="撤回私聊消息")
public Result<Long> recallMessage(@NotNull(message = "消息id不能为空") @PathVariable Long id){
@ApiOperation(value = "撤回消息", notes = "撤回私聊消息")
public Result<Long> recallMessage(@NotNull(message = "消息id不能为空") @PathVariable Long id) {
privateMessageService.recallMessage(id);
return ResultUtils.success();
}
// todo 删除
@PostMapping("/pullUnreadMessage")
@ApiOperation(value = "拉取未读消息",notes="拉取未读消息")
public Result pullUnreadMessage(){
@ApiOperation(value = "拉取未读消息", notes = "拉取未读消息")
public Result pullUnreadMessage() {
privateMessageService.pullUnreadMessage();
return ResultUtils.success();
}
@GetMapping("/loadMessage")
@ApiOperation(value = "拉取消息",notes="拉取消息,一次最多拉取100条")
public Result<List<PrivateMessageVO>> loadMessage(@RequestParam Long minId){
@ApiOperation(value = "拉取消息", notes = "拉取消息,一次最多拉取100条")
public Result<List<PrivateMessageVO>> loadMessage(@RequestParam Long minId) {
return ResultUtils.success(privateMessageService.loadMessage(minId));
}
@PutMapping("/readed")
@ApiOperation(value = "消息已读",notes="将会话中接收的消息状态置为已读")
public Result readedMessage(@RequestParam Long friendId){
@ApiOperation(value = "消息已读", notes = "将会话中接收的消息状态置为已读")
public Result readedMessage(@RequestParam Long friendId) {
privateMessageService.readedMessage(friendId);
return ResultUtils.success();
}
@GetMapping("/history")
@ApiOperation(value = "查询聊天记录",notes="查询聊天记录")
@ApiOperation(value = "查询聊天记录", notes = "查询聊天记录")
public Result<List<PrivateMessageVO>> 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));
@NotNull(message = "页码不能为空") @RequestParam Long page,
@NotNull(message = "size不能为空") @RequestParam Long size) {
return ResultUtils.success(privateMessageService.findHistoryMessage(friendId, page, size));
}
}

33
im-platform/src/main/java/com/bx/implatform/controller/UserController.java

@ -11,58 +11,55 @@ import com.bx.implatform.vo.OnlineTerminalVO;
import com.bx.implatform.vo.UserVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import java.util.List;
@Api(tags = "用户")
@RestController
@RequestMapping("/user")
@AllArgsConstructor
@RequiredArgsConstructor
public class UserController {
private IUserService userService;
private final IUserService userService;
@GetMapping("/terminal/online")
@ApiOperation(value = "判断用户哪个终端在线",notes="返回在线的用户id的终端集合")
public Result<List<OnlineTerminalVO>> getOnlineTerminal(@NotEmpty @RequestParam("userIds") String userIds){
@ApiOperation(value = "判断用户哪个终端在线", notes = "返回在线的用户id的终端集合")
public Result<List<OnlineTerminalVO>> getOnlineTerminal(@NotEmpty @RequestParam("userIds") String userIds) {
return ResultUtils.success(userService.getOnlineTerminals(userIds));
}
@GetMapping("/self")
@ApiOperation(value = "获取当前用户信息",notes="获取当前用户信息")
public Result<UserVO> findSelfInfo(){
@ApiOperation(value = "获取当前用户信息", notes = "获取当前用户信息")
public Result<UserVO> findSelfInfo() {
UserSession session = SessionContext.getSession();
User user = userService.getById(session.getUserId());
UserVO userVO = BeanUtils.copyProperties(user,UserVO.class);
UserVO userVO = BeanUtils.copyProperties(user, UserVO.class);
return ResultUtils.success(userVO);
}
@GetMapping("/find/{id}")
@ApiOperation(value = "查找用户",notes="根据id查找用户")
public Result<UserVO> findById(@NotEmpty @PathVariable("id") Long id){
@ApiOperation(value = "查找用户", notes = "根据id查找用户")
public Result<UserVO> findById(@NotEmpty @PathVariable("id") Long id) {
return ResultUtils.success(userService.findUserById(id));
}
@PutMapping("/update")
@ApiOperation(value = "修改用户信息",notes="修改用户信息,仅允许修改登录用户信息")
public Result update(@Valid @RequestBody UserVO vo){
@ApiOperation(value = "修改用户信息", notes = "修改用户信息,仅允许修改登录用户信息")
public Result update(@Valid @RequestBody UserVO vo) {
userService.update(vo);
return ResultUtils.success();
}
@GetMapping("/findByName")
@ApiOperation(value = "查找用户",notes="根据用户名或昵称查找用户")
public Result<List<UserVO>> findByName(@RequestParam("name") String name){
return ResultUtils.success( userService.findUserByName(name));
@ApiOperation(value = "查找用户", notes = "根据用户名或昵称查找用户")
public Result<List<UserVO>> findByName(@RequestParam("name") String name) {
return ResultUtils.success(userService.findUserByName(name));
}
}

22
im-platform/src/main/java/com/bx/implatform/controller/WebrtcController.java

@ -6,7 +6,7 @@ import com.bx.implatform.result.ResultUtils;
import com.bx.implatform.service.IWebrtcService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@ -14,22 +14,22 @@ import java.util.List;
@Api(tags = "webrtc视频单人通话")
@RestController
@RequestMapping("/webrtc/private")
@AllArgsConstructor
@RequiredArgsConstructor
public class WebrtcController {
private IWebrtcService webrtcService;
private final IWebrtcService webrtcService;
@ApiOperation(httpMethod = "POST", value = "呼叫视频通话")
@PostMapping("/call")
public Result call(@RequestParam Long uid, @RequestBody String offer) {
webrtcService.call(uid,offer);
webrtcService.call(uid, offer);
return ResultUtils.success();
}
@ApiOperation(httpMethod = "POST", value = "接受视频通话")
@PostMapping("/accept")
public Result accept(@RequestParam Long uid,@RequestBody String answer) {
webrtcService.accept(uid,answer);
public Result accept(@RequestParam Long uid, @RequestBody String answer) {
webrtcService.accept(uid, answer);
return ResultUtils.success();
}
@ -50,8 +50,8 @@ public class WebrtcController {
@ApiOperation(httpMethod = "POST", value = "呼叫失败")
@PostMapping("/failed")
public Result failed(@RequestParam Long uid,@RequestParam String reason) {
webrtcService.failed(uid,reason);
public Result failed(@RequestParam Long uid, @RequestParam String reason) {
webrtcService.failed(uid, reason);
return ResultUtils.success();
}
@ -65,15 +65,15 @@ public class WebrtcController {
@PostMapping("/candidate")
@ApiOperation(httpMethod = "POST", value = "同步candidate")
public Result forwardCandidate(@RequestParam Long uid,@RequestBody String candidate ) {
webrtcService.candidate(uid,candidate);
public Result forwardCandidate(@RequestParam Long uid, @RequestBody String candidate) {
webrtcService.candidate(uid, candidate);
return ResultUtils.success();
}
@GetMapping("/iceservers")
@ApiOperation(httpMethod = "GET", value = "获取iceservers")
public Result<List<ICEServer>> iceservers() {
public Result<List<ICEServer>> iceservers() {
return ResultUtils.success(webrtcService.getIceServers());
}
}

10
im-platform/src/main/java/com/bx/implatform/dto/GroupMessageDTO.java

@ -14,20 +14,20 @@ import java.util.List;
@ApiModel("群聊消息DTO")
public class GroupMessageDTO {
@NotNull(message="群聊id不可为空")
@NotNull(message = "群聊id不可为空")
@ApiModelProperty(value = "群聊id")
private Long groupId;
@Length(max=1024,message = "发送内容长度不得大于1024")
@NotEmpty(message="发送内容不可为空")
@Length(max = 1024, message = "发送内容长度不得大于1024")
@NotEmpty(message = "发送内容不可为空")
@ApiModelProperty(value = "发送内容")
private String content;
@NotNull(message="消息类型不可为空")
@NotNull(message = "消息类型不可为空")
@ApiModelProperty(value = "消息类型")
private Integer type;
@Size(max = 20,message = "一次最多只能@20个小伙伴哦")
@Size(max = 20, message = "一次最多只能@20个小伙伴哦")
@ApiModelProperty(value = "被@用户列表")
private List<Long> atUserIds;
}

10
im-platform/src/main/java/com/bx/implatform/dto/LoginDTO.java

@ -13,17 +13,17 @@ import javax.validation.constraints.NotNull;
@ApiModel("用户登录DTO")
public class LoginDTO {
@Max(value = 1,message = "登录终端类型取值范围:0,1")
@Min(value = 0,message = "登录终端类型取值范围:0,1")
@NotNull(message="登录终端类型不可为空")
@Max(value = 1, message = "登录终端类型取值范围:0,1")
@Min(value = 0, message = "登录终端类型取值范围:0,1")
@NotNull(message = "登录终端类型不可为空")
@ApiModelProperty(value = "登录终端 0:web 1:app")
private Integer terminal;
@NotEmpty(message="用户名不可为空")
@NotEmpty(message = "用户名不可为空")
@ApiModelProperty(value = "用户名")
private String userName;
@NotEmpty(message="用户密码不可为空")
@NotEmpty(message = "用户密码不可为空")
@ApiModelProperty(value = "用户密码")
private String password;

4
im-platform/src/main/java/com/bx/implatform/dto/ModifyPwdDTO.java

@ -10,11 +10,11 @@ import javax.validation.constraints.NotEmpty;
@ApiModel("修改密码DTO")
public class ModifyPwdDTO {
@NotEmpty(message="旧用户密码不可为空")
@NotEmpty(message = "旧用户密码不可为空")
@ApiModelProperty(value = "旧用户密码")
private String oldPassword;
@NotEmpty(message="新用户密码不可为空")
@NotEmpty(message = "新用户密码不可为空")
@ApiModelProperty(value = "新用户密码")
private String newPassword;

10
im-platform/src/main/java/com/bx/implatform/dto/PrivateMessageDTO.java

@ -1,6 +1,5 @@
package com.bx.implatform.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@ -13,18 +12,17 @@ import javax.validation.constraints.NotNull;
@ApiModel("私聊消息DTO")
public class PrivateMessageDTO {
@NotNull(message="接收用户id不可为空")
@NotNull(message = "接收用户id不可为空")
@ApiModelProperty(value = "接收用户id")
private Long recvId;
@Length(max=1024,message = "内容长度不得大于1024")
@NotEmpty(message="发送内容不可为空")
@Length(max = 1024, message = "内容长度不得大于1024")
@NotEmpty(message = "发送内容不可为空")
@ApiModelProperty(value = "发送内容")
private String content;
@NotNull(message="消息类型不可为空")
@NotNull(message = "消息类型不可为空")
@ApiModelProperty(value = "消息类型")
private Integer type;

12
im-platform/src/main/java/com/bx/implatform/dto/RegisterDTO.java

@ -11,18 +11,18 @@ import javax.validation.constraints.NotEmpty;
@ApiModel("用户注册DTO")
public class RegisterDTO {
@Length(max = 64,message = "用户名不能大于64字符")
@NotEmpty(message="用户名不可为空")
@Length(max = 64, message = "用户名不能大于64字符")
@NotEmpty(message = "用户名不可为空")
@ApiModelProperty(value = "用户名")
private String userName;
@Length(min=5,max = 20,message = "密码长度必须在5-20个字符之间")
@NotEmpty(message="用户密码不可为空")
@Length(min = 5, max = 20, message = "密码长度必须在5-20个字符之间")
@NotEmpty(message = "用户密码不可为空")
@ApiModelProperty(value = "用户密码")
private String password;
@Length(max = 64,message = "昵称不能大于64字符")
@NotEmpty(message="用户昵称不可为空")
@Length(max = 64, message = "昵称不能大于64字符")
@NotEmpty(message = "用户昵称不可为空")
@ApiModelProperty(value = "用户昵称")
private String nickName;

1
im-platform/src/main/java/com/bx/implatform/entity/Group.java

@ -13,6 +13,7 @@ import java.util.Date;
/**
*
*
* @author blue
* @since 2022-10-31
*/

5
im-platform/src/main/java/com/bx/implatform/entity/GroupMember.java

@ -45,19 +45,18 @@ public class GroupMember extends Model<GroupMember> {
private Long userId;
/**
* 群内显示名称
* 群内显示名称
*/
@TableField("alias_name")
private String aliasName;
/**
* 头像
* 头像
*/
@TableField("head_image")
private String headImage;
/**
* 备注
*/

4
im-platform/src/main/java/com/bx/implatform/entity/PrivateMessage.java

@ -13,7 +13,7 @@ import java.util.Date;
/**
* <p>
*
*
* </p>
*
* @author blue
@ -24,7 +24,7 @@ import java.util.Date;
@TableName("im_private_message")
public class PrivateMessage extends Model<PrivateMessage> {
private static final long serialVersionUID=1L;
private static final long serialVersionUID = 1L;
/**
* id

4
im-platform/src/main/java/com/bx/implatform/entity/User.java

@ -13,7 +13,7 @@ import java.util.Date;
/**
* <p>
* 用户
* 用户
* </p>
*
* @author blue
@ -24,7 +24,7 @@ import java.util.Date;
@TableName("im_user")
public class User extends Model<User> {
private static final long serialVersionUID=1L;
private static final long serialVersionUID = 1L;
/**
* id

24
im-platform/src/main/java/com/bx/implatform/enums/FileType.java

@ -5,19 +5,29 @@ import lombok.AllArgsConstructor;
@AllArgsConstructor
public enum FileType {
FILE(0,"文件"),
IMAGE(1,"图片"),
VIDEO(2,"视频"),
AUDIO(3,"声音");
/**
* 文件
*/
FILE(0, "文件"),
/**
* 图片
*/
IMAGE(1, "图片"),
/**
* 视频
*/
VIDEO(2, "视频"),
/**
* 声音
*/
AUDIO(3, "声音");
private final Integer code;
private final String desc;
public Integer code(){
public Integer code() {
return this.code;
}

22
im-platform/src/main/java/com/bx/implatform/enums/MessageStatus.java

@ -5,17 +5,29 @@ import lombok.AllArgsConstructor;
@AllArgsConstructor
public enum MessageStatus {
UNSEND(0,"未送达"),
SENDED(1,"送达"),
RECALL(2,"撤回"),
READED(3,"已读");
/**
* 文件
*/
UNSEND(0, "未送达"),
/**
* 文件
*/
SENDED(1, "送达"),
/**
* 撤回
*/
RECALL(2, "撤回"),
/**
* 已读
*/
READED(3, "已读");
private final Integer code;
private final String desc;
public Integer code(){
public Integer code() {
return this.code;
}
}

68
im-platform/src/main/java/com/bx/implatform/enums/MessageType.java

@ -5,28 +5,70 @@ import lombok.AllArgsConstructor;
@AllArgsConstructor
public enum MessageType {
TEXT(0,"文字"),
IMAGE(1,"图片"),
FILE(2,"文件"),
AUDIO(3,"音频"),
VIDEO(4,"视频"),
RECALL(10,"撤回"),
/**
* 文字
*/
TEXT(0, "文字"),
/**
* 图片
*/
IMAGE(1, "图片"),
/**
* 文件
*/
FILE(2, "文件"),
/**
* 音频
*/
AUDIO(3, "音频"),
/**
* 视频
*/
VIDEO(4, "视频"),
/**
* 撤回
*/
RECALL(10, "撤回"),
/**
* 已读
*/
READED(11, "已读"),
RTC_CALL(101,"呼叫"),
RTC_ACCEPT(102,"接受"),
/**
* 呼叫
*/
RTC_CALL(101, "呼叫"),
/**
* 接受
*/
RTC_ACCEPT(102, "接受"),
/**
* 拒绝
*/
RTC_REJECT(103, "拒绝"),
RTC_CANCEL(104,"取消呼叫"),
RTC_FAILED(105,"呼叫失败"),
RTC_HANDUP(106,"挂断"),
RTC_CANDIDATE(107,"同步candidate");
/**
* 取消呼叫
*/
RTC_CANCEL(104, "取消呼叫"),
/**
* 呼叫失败
*/
RTC_FAILED(105, "呼叫失败"),
/**
* 挂断
*/
RTC_HANDUP(106, "挂断"),
/**
* 同步candidate
*/
RTC_CANDIDATE(107, "同步candidate");
private final Integer code;
private final String desc;
public Integer code(){
public Integer code() {
return this.code;
}
}

41
im-platform/src/main/java/com/bx/implatform/enums/ResultCode.java

@ -8,23 +8,42 @@ import lombok.Getter;
*
* @author Blue
* @date 2020/10/19
*
**/
@Getter
@AllArgsConstructor
public enum ResultCode {
SUCCESS(200,"成功"),
NO_LOGIN(400,"未登录"),
INVALID_TOKEN(401,"token无效或已过期"),
PROGRAM_ERROR(500,"系统繁忙,请稍后再试"),
PASSWOR_ERROR(10001,"密码不正确"),
USERNAME_ALREADY_REGISTER(10003,"该用户名已注册"),
XSS_PARAM_ERROR(10004,"请不要输入非法内容"),
;
/**
* 成功
*/
SUCCESS(200, "成功"),
/**
* 未登录
*/
NO_LOGIN(400, "未登录"),
/**
* token无效或已过期
*/
INVALID_TOKEN(401, "token无效或已过期"),
/**
* 系统繁忙请稍后再试
*/
PROGRAM_ERROR(500, "系统繁忙,请稍后再试"),
/**
* 密码不正确
*/
PASSWOR_ERROR(10001, "密码不正确"),
/**
* 该用户名已注册
*/
USERNAME_ALREADY_REGISTER(10003, "该用户名已注册"),
/**
* 请不要输入非法内容
*/
XSS_PARAM_ERROR(10004, "请不要输入非法内容");
private int code;
private String msg;
private final int code;
private final String msg;
}

17
im-platform/src/main/java/com/bx/implatform/exception/GlobalException.java

@ -5,21 +5,20 @@ import lombok.Data;
import java.io.Serializable;
@Data
public class GlobalException extends RuntimeException implements Serializable {
private static final long serialVersionUID = 8134030011662574394L;
private Integer code;
private String message;
public GlobalException(Integer code, String message){
this.code=code;
this.message=message;
public GlobalException(Integer code, String message) {
this.code = code;
this.message = message;
}
public GlobalException(ResultCode resultCode, String message){
public GlobalException(ResultCode resultCode, String message) {
this.code = resultCode.getCode();
this.message=message;
this.message = message;
}
public GlobalException(ResultCode resultCode) {
@ -27,9 +26,9 @@ public class GlobalException extends RuntimeException implements Serializable {
this.message = resultCode.getMsg();
}
public GlobalException(String message){
this.code= ResultCode.PROGRAM_ERROR.getCode();
this.message=message;
public GlobalException(String message) {
this.code = ResultCode.PROGRAM_ERROR.getCode();
this.message = message;
}
}

43
im-platform/src/main/java/com/bx/implatform/exception/GlobalExceptionHandler.java

@ -20,53 +20,52 @@ import java.lang.reflect.UndeclaredThrowableException;
import java.time.format.DateTimeParseException;
import java.util.List;
@ControllerAdvice
@ResponseBody
@Slf4j
public class GlobalExceptionHandler {
@ExceptionHandler(value = Exception.class)
public Result handleException(Exception e){
if(e instanceof GlobalException) {
GlobalException ex = (GlobalException)e;
log.error("全局异常捕获:msg:{},log:{},{}" , ex.getMessage(), e);
public Result handleException(Exception e) {
if (e instanceof GlobalException) {
GlobalException ex = (GlobalException) e;
log.error("全局异常捕获:msg:{},log:{},{}", ex.getMessage(), e);
return ResultUtils.error(ex.getCode(), ex.getMessage());
}
else if(e instanceof UndeclaredThrowableException) {
} else if (e instanceof UndeclaredThrowableException) {
GlobalException ex = (GlobalException) e.getCause();
log.error("全局异常捕获:msg:{},log:{},{}" , ex.getMessage(), e);
log.error("全局异常捕获:msg:{},log:{},{}", ex.getMessage(), e);
return ResultUtils.error(ex.getCode(), ex.getMessage());
}else{
log.error("全局异常捕获:msg:{},{}" , e.getMessage(), e);
} else {
log.error("全局异常捕获:msg:{},{}", e.getMessage(), e);
return ResultUtils.error(ResultCode.PROGRAM_ERROR);
}
}
/**
* 数据解析错误
* 数据解析错误
**/
@ExceptionHandler(value = HttpMessageNotReadableException.class)
public Result handleMessageNotReadableException(HttpMessageNotReadableException e){
log.error("全局异常捕获:msg:{}" , e.getMessage());
public Result handleMessageNotReadableException(HttpMessageNotReadableException e) {
log.error("全局异常捕获:msg:{}", e.getMessage());
Throwable t = e.getCause();
if(t instanceof JSONException){
if (t instanceof JSONException) {
t = t.getCause();
if(t instanceof DateTimeParseException){
if (t instanceof DateTimeParseException) {
return ResultUtils.error(ResultCode.PROGRAM_ERROR, "日期格式不正确");
}
return ResultUtils.error(ResultCode.PROGRAM_ERROR, "数据格式不正确");
return ResultUtils.error(ResultCode.PROGRAM_ERROR, "数据格式不正确");
}
return ResultUtils.error(ResultCode.PROGRAM_ERROR);
}
/**
* 处理请求参数格式错误 @RequestBody上validate失败后抛出的异常
*
* @param exception
* @return
*/
@ExceptionHandler(value = { MethodArgumentNotValidException.class})
@ExceptionHandler(value = {MethodArgumentNotValidException.class})
@ResponseStatus(HttpStatus.OK)
public Result handleValidationExceptionHandler(MethodArgumentNotValidException exception) {
BindingResult bindResult = exception.getBindingResult();
@ -76,23 +75,23 @@ public class GlobalExceptionHandler {
if (msg.contains("NumberFormatException")) {
msg = "参数类型错误!";
}
}else {
} else {
msg = "系统繁忙,请稍后重试...";
}
return ResultUtils.error(ResultCode.PROGRAM_ERROR,msg);
return ResultUtils.error(ResultCode.PROGRAM_ERROR, msg);
}
@ExceptionHandler(BindException.class)
@ResponseStatus(HttpStatus.OK)
public Result handleBindException(BindException e){
public Result handleBindException(BindException e) {
//抛出异常可能不止一个 输出为一个List集合
List<ObjectError> errors = e.getAllErrors();
//取第一个异常
ObjectError error = errors.get(0);
//获取异常信息
String errorMsg = error.getDefaultMessage();
return ResultUtils.error(ResultCode.PROGRAM_ERROR,errorMsg);
return ResultUtils.error(ResultCode.PROGRAM_ERROR, errorMsg);
}
}

8
im-platform/src/main/java/com/bx/implatform/filter/CacheFilter.java

@ -10,14 +10,16 @@ import java.io.IOException;
@Component
@ServletComponentScan
@WebFilter(urlPatterns = "/*",filterName = "xssFilter")
@WebFilter(urlPatterns = "/*", filterName = "xssFilter")
public class CacheFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) {}
public void init(FilterConfig filterConfig) {
}
@Override
public void destroy() {}
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

15
im-platform/src/main/java/com/bx/implatform/interceptor/AuthInterceptor.java

@ -9,6 +9,7 @@ import com.bx.implatform.exception.GlobalException;
import com.bx.implatform.session.UserSession;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
@ -24,7 +25,7 @@ public class AuthInterceptor implements HandlerInterceptor {
private final JwtProperties jwtProperties;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
public boolean preHandle(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler) throws Exception {
//如果不是映射到方法直接通过
if (!(handler instanceof HandlerMethod)) {
return true;
@ -32,18 +33,18 @@ public class AuthInterceptor implements HandlerInterceptor {
//从 http 请求头中取出 token
String token = request.getHeader("accessToken");
if (StrUtil.isEmpty(token)) {
log.error("未登陆,url:{}",request.getRequestURI());
log.error("未登陆,url:{}", request.getRequestURI());
throw new GlobalException(ResultCode.NO_LOGIN);
}
//验证 token
if(!JwtUtil.checkSign(token, jwtProperties.getAccessTokenSecret())){
log.error("token已失效,url:{}",request.getRequestURI());
if (!JwtUtil.checkSign(token, jwtProperties.getAccessTokenSecret())) {
log.error("token已失效,url:{}", request.getRequestURI());
throw new GlobalException(ResultCode.INVALID_TOKEN);
}
// 存放session
String strJson = JwtUtil.getInfo(token);
UserSession userSession = JSON.parseObject(strJson,UserSession.class);
request.setAttribute("session",userSession);
String strJson = JwtUtil.getInfo(token);
UserSession userSession = JSON.parseObject(strJson, UserSession.class);
request.setAttribute("session", userSession);
return true;
}
}

15
im-platform/src/main/java/com/bx/implatform/interceptor/XssInterceptor.java

@ -5,6 +5,7 @@ import com.bx.implatform.exception.GlobalException;
import com.bx.implatform.util.XssUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
@ -18,26 +19,26 @@ import java.util.Map;
public class XssInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
public boolean preHandle(HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler) {
// 检查参数
Map<String, String[]> paramMap = request.getParameterMap();
for(String[] values:paramMap.values()){
for(String value:values){
if(XssUtil.checkXss(value)){
Map<String, String[]> paramMap = request.getParameterMap();
for (String[] values : paramMap.values()) {
for (String value : values) {
if (XssUtil.checkXss(value)) {
throw new GlobalException(ResultCode.XSS_PARAM_ERROR);
}
}
}
// 检查body
String body = getBody(request);
if(XssUtil.checkXss(body)){
if (XssUtil.checkXss(body)) {
throw new GlobalException(ResultCode.XSS_PARAM_ERROR);
}
return true;
}
@SneakyThrows
private String getBody(HttpServletRequest request){
private String getBody(HttpServletRequest request) {
BufferedReader reader = request.getReader();
StringBuilder stringBuilder = new StringBuilder();
String line;

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

@ -11,21 +11,19 @@ import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
@Slf4j
@IMListener(type = IMListenerType.GROUP_MESSAGE)
@AllArgsConstructor
public class GroupMessageListener implements MessageListener<GroupMessageVO> {
private RedisTemplate<String,Object> redisTemplate;
private final RedisTemplate<String, Object> redisTemplate;
@Override
public void process(IMSendResult<GroupMessageVO> result){
public void process(IMSendResult<GroupMessageVO> result) {
GroupMessageVO messageInfo = result.getData();
// todo 删除
// 保存该用户已拉取的最大消息id
if(result.getCode().equals(IMSendCode.SUCCESS.code())) {
String key = String.join(":",RedisKey.IM_GROUP_READED_POSITION,messageInfo.getGroupId().toString(),result.getReceiver().getId().toString());
if (result.getCode().equals(IMSendCode.SUCCESS.code())) {
String key = String.join(":", RedisKey.IM_GROUP_READED_POSITION, messageInfo.getGroupId().toString(), result.getReceiver().getId().toString());
redisTemplate.opsForValue().set(key, messageInfo.getId());
}
}

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

@ -14,7 +14,6 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
@Slf4j
@IMListener(type = IMListenerType.PRIVATE_MESSAGE)
public class PrivateMessageListener implements MessageListener<PrivateMessageVO> {
@ -24,16 +23,16 @@ public class PrivateMessageListener implements MessageListener<PrivateMessageVO>
private IPrivateMessageService privateMessageService;
@Override
public void process(IMSendResult<PrivateMessageVO> result){
public void process(IMSendResult<PrivateMessageVO> result) {
PrivateMessageVO messageInfo = result.getData();
// 更新消息状态,这里只处理成功消息,失败的消息继续保持未读状态
if(result.getCode().equals(IMSendCode.SUCCESS.code())){
if (result.getCode().equals(IMSendCode.SUCCESS.code())) {
UpdateWrapper<PrivateMessage> updateWrapper = new UpdateWrapper<>();
updateWrapper.lambda().eq(PrivateMessage::getId,messageInfo.getId())
updateWrapper.lambda().eq(PrivateMessage::getId, messageInfo.getId())
.eq(PrivateMessage::getStatus, MessageStatus.UNSEND.code())
.set(PrivateMessage::getStatus, MessageStatus.SENDED.code());
privateMessageService.update(updateWrapper);
log.info("消息已读,消息id:{},发送者:{},接收者:{},终端:{}",messageInfo.getId(),result.getSender().getId(),result.getReceiver().getId(),result.getReceiver().getTerminal());
log.info("消息已读,消息id:{},发送者:{},接收者:{},终端:{}", messageInfo.getId(), result.getSender().getId(), result.getReceiver().getId(), result.getReceiver().getTerminal());
}
}

1
im-platform/src/main/java/com/bx/implatform/mapper/FriendMapper.java

@ -3,7 +3,6 @@ package com.bx.implatform.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.bx.implatform.entity.Friend;
public interface FriendMapper extends BaseMapper<Friend> {
}

1
im-platform/src/main/java/com/bx/implatform/mapper/GroupMapper.java

@ -3,7 +3,6 @@ package com.bx.implatform.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.bx.implatform.entity.Group;
public interface GroupMapper extends BaseMapper<Group> {
}

1
im-platform/src/main/java/com/bx/implatform/mapper/GroupMemberMapper.java

@ -3,7 +3,6 @@ package com.bx.implatform.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.bx.implatform.entity.GroupMember;
public interface GroupMemberMapper extends BaseMapper<GroupMember> {
}

1
im-platform/src/main/java/com/bx/implatform/mapper/GroupMessageMapper.java

@ -3,7 +3,6 @@ package com.bx.implatform.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.bx.implatform.entity.GroupMessage;
public interface GroupMessageMapper extends BaseMapper<GroupMessage> {
}

1
im-platform/src/main/java/com/bx/implatform/mapper/PrivateMessageMapper.java

@ -3,7 +3,6 @@ package com.bx.implatform.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.bx.implatform.entity.PrivateMessage;
public interface PrivateMessageMapper extends BaseMapper<PrivateMessage> {
}

1
im-platform/src/main/java/com/bx/implatform/mapper/UserMapper.java

@ -3,7 +3,6 @@ package com.bx.implatform.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.bx.implatform.entity.User;
public interface UserMapper extends BaseMapper<User> {
}

2
im-platform/src/main/java/com/bx/implatform/result/Result.java

@ -2,11 +2,9 @@ package com.bx.implatform.result;
import lombok.Data;
@Data
public class Result<T> {
private int code;
private String message;

35
im-platform/src/main/java/com/bx/implatform/result/ResultUtils.java

@ -1,62 +1,63 @@
package com.bx.implatform.result;
import com.bx.implatform.enums.ResultCode;
public class ResultUtils {
public final class ResultUtils {
private ResultUtils() {
}
public static <T> Result<T> success(){
Result<T> result=new Result<>();
public static <T> Result<T> success() {
Result<T> result = new Result<>();
result.setCode(ResultCode.SUCCESS.getCode());
result.setMessage(ResultCode.SUCCESS.getMsg());
return result;
}
public static <T> Result<T> success(T data){
Result<T> result=new Result<>();
public static <T> Result<T> success(T data) {
Result<T> result = new Result<>();
result.setCode(ResultCode.SUCCESS.getCode());
result.setMessage(ResultCode.SUCCESS.getMsg());
result.setData(data);
return result;
}
public static <T> Result<T> success(T data, String messsage){
Result<T> result=new Result<>();
public static <T> Result<T> success(T data, String messsage) {
Result<T> result = new Result<>();
result.setCode(ResultCode.SUCCESS.getCode());
result.setMessage(messsage);
result.setData(data);
return result;
}
public static <T> Result<T> success(String messsage){
Result<T> result=new Result<>();
public static <T> Result<T> success(String messsage) {
Result<T> result = new Result<>();
result.setCode(ResultCode.SUCCESS.getCode());
result.setMessage(messsage);
return result;
}
public static <T> Result<T> error(Integer code, String messsage){
Result<T> result=new Result<>();
public static <T> Result<T> error(Integer code, String messsage) {
Result<T> result = new Result<>();
result.setCode(code);
result.setMessage(messsage);
return result;
}
public static <T> Result<T> error(ResultCode resultCode, String messsage){
Result<T> result=new Result<>();
public static <T> Result<T> error(ResultCode resultCode, String messsage) {
Result<T> result = new Result<>();
result.setCode(resultCode.getCode());
result.setMessage(messsage);
return result;
}
public static <T> Result<T> error(ResultCode resultCode){
Result<T> result=new Result<>();
public static <T> Result<T> error(ResultCode resultCode) {
Result<T> result = new Result<>();
result.setCode(resultCode.getCode());
result.setMessage(resultCode.getMsg());
return result;
}
}

5
im-platform/src/main/java/com/bx/implatform/service/IFriendService.java

@ -6,7 +6,6 @@ import com.bx.implatform.vo.FriendVO;
import java.util.List;
public interface IFriendService extends IService<Friend> {
/**
@ -22,7 +21,7 @@ public interface IFriendService extends IService<Friend> {
/**
* 查询用户的所有好友
*
* @param userId 用户id
* @param userId 用户id
* @return 好友列表
*/
List<Friend> findFriendByUserId(Long userId);
@ -44,7 +43,7 @@ public interface IFriendService extends IService<Friend> {
/**
* 更新好友信息主要是头像和昵称
*
* @param vo 好友vo
* @param vo 好友vo
*/
void update(FriendVO vo);

20
im-platform/src/main/java/com/bx/implatform/service/IGroupMemberService.java

@ -5,18 +5,16 @@ import com.bx.implatform.entity.GroupMember;
import java.util.List;
public interface IGroupMemberService extends IService<GroupMember> {
/**
* 根据群聊id和用户id查询群聊成员
*
* @param groupId 群聊id
* @param userId 用户id
* @param userId 用户id
* @return 群聊成员信息
*/
GroupMember findByGroupAndUserId(Long groupId,Long userId);
GroupMember findByGroupAndUserId(Long groupId, Long userId);
/**
* 根据用户id查询群聊成员
@ -24,7 +22,7 @@ public interface IGroupMemberService extends IService<GroupMember> {
* @param userId 用户id
* @return 成员列表
*/
List<GroupMember> findByUserId(Long userId);
List<GroupMember> findByUserId(Long userId);
/**
* 根据群聊id查询群聊成员包括已退出
@ -32,7 +30,7 @@ public interface IGroupMemberService extends IService<GroupMember> {
* @param groupId 群聊id
* @return 群聊成员列表
*/
List<GroupMember> findByGroupId(Long groupId);
List<GroupMember> findByGroupId(Long groupId);
/**
* 根据群聊id查询没有退出的群聊成员id
@ -49,20 +47,20 @@ public interface IGroupMemberService extends IService<GroupMember> {
* @param members 成员列表
* @return 成功或失败
*/
boolean saveOrUpdateBatch(Long groupId,List<GroupMember> members);
boolean saveOrUpdateBatch(Long groupId, List<GroupMember> members);
/**
* 根据群聊id删除移除成员
*
* @param groupId 群聊id
* @param groupId 群聊id
*/
void removeByGroupId(Long groupId);
/**
*根据群聊id和用户id移除成员
* 根据群聊id和用户id移除成员
*
* @param groupId 群聊id
* @param groupId 群聊id
* @param userId 用户id
*/
void removeByGroupAndUserId(Long groupId,Long userId);
void removeByGroupAndUserId(Long groupId, Long userId);
}

6
im-platform/src/main/java/com/bx/implatform/service/IGroupMessageService.java

@ -7,7 +7,6 @@ import com.bx.implatform.vo.GroupMessageVO;
import java.util.List;
public interface IGroupMessageService extends IService<GroupMessage> {
/**
@ -26,7 +25,6 @@ public interface IGroupMessageService extends IService<GroupMessage> {
void recallMessage(Long id);
/**
*
* 异步拉取群聊消息通过websocket异步推送
*/
void pullUnreadMessage();
@ -34,7 +32,7 @@ public interface IGroupMessageService extends IService<GroupMessage> {
/**
* 拉取消息只能拉取最近1个月的消息一次拉取100条
*
* @param minId 消息起始id
* @param minId 消息起始id
* @return 聊天消息列表
*/
List<GroupMessageVO> loadMessage(Long minId);
@ -42,7 +40,7 @@ public interface IGroupMessageService extends IService<GroupMessage> {
/**
* 消息已读,同步其他终端清空未读数量
*
* @param groupId 群聊
* @param groupId 群聊
*/
void readedMessage(Long groupId);

13
im-platform/src/main/java/com/bx/implatform/service/IGroupService.java

@ -8,13 +8,12 @@ import com.bx.implatform.vo.GroupVO;
import java.util.List;
public interface IGroupService extends IService<Group> {
/**
* 创建新群聊
*
* @param vo 群聊信息
* @param vo 群聊信息
* @return 群聊信息
**/
GroupVO createGroup(GroupVO vo);
@ -22,7 +21,7 @@ public interface IGroupService extends IService<Group> {
/**
* 修改群聊信息
*
* @param vo 群聊信息
* @param vo 群聊信息
* @return 群聊信息
**/
GroupVO modifyGroup(GroupVO vo);
@ -45,21 +44,21 @@ public interface IGroupService extends IService<Group> {
* 将用户踢出群聊
*
* @param groupId 群聊id
* @param userId 用户id
* @param userId 用户id
*/
void kickGroup(Long groupId,Long userId);
void kickGroup(Long groupId, Long userId);
/**
* 查询当前用户的所有群聊
*
* @return 群聊信息列表
**/
List<GroupVO> findGroups();
List<GroupVO> findGroups();
/**
* 邀请好友进群
*
* @param vo 群id好友id列表
* @param vo 群id好友id列表
**/
void invite(GroupInviteVO vo);

3
im-platform/src/main/java/com/bx/implatform/service/IPrivateMessageService.java

@ -7,7 +7,6 @@ import com.bx.implatform.vo.PrivateMessageVO;
import java.util.List;
public interface IPrivateMessageService extends IService<PrivateMessage> {
/**
@ -34,7 +33,7 @@ public interface IPrivateMessageService extends IService<PrivateMessage> {
* @param size 页码大小
* @return 聊天记录列表
*/
List<PrivateMessageVO> findHistoryMessage(Long friendId, Long page,Long size);
List<PrivateMessageVO> findHistoryMessage(Long friendId, Long page, Long size);
/**
* 异步拉取私聊消息通过websocket异步推送

3
im-platform/src/main/java/com/bx/implatform/service/IUserService.java

@ -11,7 +11,6 @@ import com.bx.implatform.vo.UserVO;
import java.util.List;
public interface IUserService extends IService<User> {
/**
@ -32,7 +31,7 @@ public interface IUserService extends IService<User> {
/**
* 用refreshToken换取新 token
*
* @param refreshToken 刷新token
* @param refreshToken 刷新token
* @return 登录token
*/
LoginVO refreshToken(String refreshToken);

19
im-platform/src/main/java/com/bx/implatform/service/IWebrtcService.java

@ -5,29 +5,28 @@ import org.springframework.web.bind.annotation.RequestBody;
import java.util.List;
/**
* webrtc 通信服务
*
* @author
*/
public interface IWebrtcService {
void call(Long uid, String offer);
void accept( Long uid,@RequestBody String answer);
void call(Long uid, String offer);
void reject( Long uid);
void accept(Long uid, @RequestBody String answer);
void cancel( Long uid);
void reject(Long uid);
void failed( Long uid, String reason);
void cancel(Long uid);
void leave( Long uid) ;
void failed(Long uid, String reason);
void candidate( Long uid, String candidate);
void leave(Long uid);
List<ICEServer> getIceServers();
void candidate(Long uid, String candidate);
List<ICEServer> getIceServers();
}

84
im-platform/src/main/java/com/bx/implatform/service/impl/FriendServiceImpl.java

@ -15,7 +15,7 @@ import com.bx.implatform.service.IFriendService;
import com.bx.implatform.session.SessionContext;
import com.bx.implatform.session.UserSession;
import com.bx.implatform.vo.FriendVO;
import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.framework.AopContext;
import org.springframework.cache.annotation.CacheConfig;
@ -26,76 +26,71 @@ import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Slf4j
@Service
@AllArgsConstructor
@CacheConfig(cacheNames= RedisKey.IM_CACHE_FRIEND)
@RequiredArgsConstructor
@CacheConfig(cacheNames = RedisKey.IM_CACHE_FRIEND)
public class FriendServiceImpl extends ServiceImpl<FriendMapper, Friend> implements IFriendService {
private final UserMapper userMapper;
private final UserMapper userMapper;
@Override
public List<Friend> findFriendByUserId(Long userId) {
LambdaQueryWrapper<Friend> queryWrapper = Wrappers.lambdaQuery();
queryWrapper.eq(Friend::getUserId,userId);
queryWrapper.eq(Friend::getUserId, userId);
return this.list(queryWrapper);
}
@Transactional(rollbackFor = Exception.class)
@Override
public void addFriend(Long friendId) {
long userId = SessionContext.getSession().getUserId();
if(userId == friendId){
throw new GlobalException(ResultCode.PROGRAM_ERROR,"不允许添加自己为好友");
if (userId == friendId) {
throw new GlobalException(ResultCode.PROGRAM_ERROR, "不允许添加自己为好友");
}
// 互相绑定好友关系
FriendServiceImpl proxy = (FriendServiceImpl)AopContext.currentProxy();
proxy.bindFriend(userId,friendId);
proxy.bindFriend(friendId,userId);
log.info("添加好友,用户id:{},好友id:{}",userId,friendId);
FriendServiceImpl proxy = (FriendServiceImpl) AopContext.currentProxy();
proxy.bindFriend(userId, friendId);
proxy.bindFriend(friendId, userId);
log.info("添加好友,用户id:{},好友id:{}", userId, friendId);
}
@Transactional(rollbackFor = Exception.class)
@Override
public void delFriend(Long friendId) {
long userId = SessionContext.getSession().getUserId();
// 互相解除好友关系,走代理清理缓存
FriendServiceImpl proxy = (FriendServiceImpl)AopContext.currentProxy();
proxy.unbindFriend(userId,friendId);
proxy.unbindFriend(friendId,userId);
log.info("删除好友,用户id:{},好友id:{}",userId,friendId);
FriendServiceImpl proxy = (FriendServiceImpl) AopContext.currentProxy();
proxy.unbindFriend(userId, friendId);
proxy.unbindFriend(friendId, userId);
log.info("删除好友,用户id:{},好友id:{}", userId, friendId);
}
@Cacheable(key="#userId1+':'+#userId2")
@Cacheable(key = "#userId1+':'+#userId2")
@Override
public Boolean isFriend(Long userId1, Long userId2) {
QueryWrapper<Friend> queryWrapper = new QueryWrapper<>();
queryWrapper.lambda()
.eq(Friend::getUserId,userId1)
.eq(Friend::getFriendId,userId2);
return this.count(queryWrapper) > 0;
.eq(Friend::getUserId, userId1)
.eq(Friend::getFriendId, userId2);
return this.count(queryWrapper) > 0;
}
@Override
public void update(FriendVO vo) {
long userId = SessionContext.getSession().getUserId();
QueryWrapper<Friend> queryWrapper = new QueryWrapper<>();
queryWrapper.lambda()
.eq(Friend::getUserId,userId)
.eq(Friend::getFriendId,vo.getId());
.eq(Friend::getUserId, userId)
.eq(Friend::getFriendId, vo.getId());
Friend f = this.getOne(queryWrapper);
if(f == null){
throw new GlobalException(ResultCode.PROGRAM_ERROR,"对方不是您的好友");
if (f == null) {
throw new GlobalException(ResultCode.PROGRAM_ERROR, "对方不是您的好友");
}
f.setFriendHeadImage(vo.getHeadImage());
@ -107,16 +102,16 @@ public class FriendServiceImpl extends ServiceImpl<FriendMapper, Friend> impleme
/**
* 单向绑定好友关系
*
* @param userId 用户id
* @param friendId 好友的用户id
* @param userId 用户id
* @param friendId 好友的用户id
*/
@CacheEvict(key="#userId+':'+#friendId")
@CacheEvict(key = "#userId+':'+#friendId")
public void bindFriend(Long userId, Long friendId) {
QueryWrapper<Friend> queryWrapper = new QueryWrapper<>();
queryWrapper.lambda()
.eq(Friend::getUserId,userId)
.eq(Friend::getFriendId,friendId);
if(this.count(queryWrapper)==0){
.eq(Friend::getUserId, userId)
.eq(Friend::getFriendId, friendId);
if (this.count(queryWrapper) == 0) {
Friend friend = new Friend();
friend.setUserId(userId);
friend.setFriendId(friendId);
@ -131,36 +126,35 @@ public class FriendServiceImpl extends ServiceImpl<FriendMapper, Friend> impleme
/**
* 单向解除好友关系
*
* @param userId 用户id
* @param friendId 好友的用户id
* @param userId 用户id
* @param friendId 好友的用户id
*/
@CacheEvict(key="#userId+':'+#friendId")
@CacheEvict(key = "#userId+':'+#friendId")
public void unbindFriend(Long userId, Long friendId) {
QueryWrapper<Friend> queryWrapper = new QueryWrapper<>();
queryWrapper.lambda()
.eq(Friend::getUserId,userId)
.eq(Friend::getFriendId,friendId);
.eq(Friend::getUserId, userId)
.eq(Friend::getFriendId, friendId);
List<Friend> friends = this.list(queryWrapper);
friends.forEach(friend -> this.removeById(friend.getId()));
}
@Override
public FriendVO findFriend(Long friendId) {
UserSession session = SessionContext.getSession();
QueryWrapper<Friend> wrapper = new QueryWrapper<>();
wrapper.lambda()
.eq(Friend::getUserId,session.getUserId())
.eq(Friend::getFriendId,friendId);
.eq(Friend::getUserId, session.getUserId())
.eq(Friend::getFriendId, friendId);
Friend friend = this.getOne(wrapper);
if(friend == null){
throw new GlobalException(ResultCode.PROGRAM_ERROR,"对方不是您的好友");
if (friend == null) {
throw new GlobalException(ResultCode.PROGRAM_ERROR, "对方不是您的好友");
}
FriendVO vo = new FriendVO();
vo.setId(friend.getFriendId());
vo.setHeadImage(friend.getFriendHeadImage());
vo.setNickName(friend.getFriendNickName());
return vo;
return vo;
}
}

27
im-platform/src/main/java/com/bx/implatform/service/impl/GroupMemberServiceImpl.java

@ -17,19 +17,18 @@ import org.springframework.stereotype.Service;
import java.util.List;
import java.util.stream.Collectors;
@Service
@CacheConfig(cacheNames = RedisKey.IM_CACHE_GROUP_MEMBER_ID)
public class GroupMemberServiceImpl extends ServiceImpl<GroupMemberMapper, GroupMember> implements IGroupMemberService {
@CacheEvict(key="#member.getGroupId()")
@CacheEvict(key = "#member.getGroupId()")
@Override
public boolean save(GroupMember member) {
return super.save(member);
}
@CacheEvict(key="#groupId")
@CacheEvict(key = "#groupId")
@Override
public boolean saveOrUpdateBatch(Long groupId,List<GroupMember> members) {
public boolean saveOrUpdateBatch(Long groupId, List<GroupMember> members) {
return super.saveOrUpdateBatch(members);
}
@ -37,8 +36,8 @@ public class GroupMemberServiceImpl extends ServiceImpl<GroupMemberMapper, Group
@Override
public GroupMember findByGroupAndUserId(Long groupId, Long userId) {
QueryWrapper<GroupMember> wrapper = new QueryWrapper<>();
wrapper.lambda().eq(GroupMember::getGroupId,groupId)
.eq(GroupMember::getUserId,userId);
wrapper.lambda().eq(GroupMember::getGroupId, groupId)
.eq(GroupMember::getUserId, userId);
return this.getOne(wrapper);
}
@ -46,7 +45,7 @@ public class GroupMemberServiceImpl extends ServiceImpl<GroupMemberMapper, Group
public List<GroupMember> findByUserId(Long userId) {
LambdaQueryWrapper<GroupMember> memberWrapper = Wrappers.lambdaQuery();
memberWrapper.eq(GroupMember::getUserId, userId)
.eq(GroupMember::getQuit,false);
.eq(GroupMember::getQuit, false);
return this.list(memberWrapper);
}
@ -57,12 +56,12 @@ public class GroupMemberServiceImpl extends ServiceImpl<GroupMemberMapper, Group
return this.list(memberWrapper);
}
@Cacheable(key="#groupId")
@Cacheable(key = "#groupId")
@Override
public List<Long> findUserIdsByGroupId(Long groupId) {
LambdaQueryWrapper<GroupMember> memberWrapper = Wrappers.lambdaQuery();
memberWrapper.eq(GroupMember::getGroupId, groupId)
.eq(GroupMember::getQuit,false);
.eq(GroupMember::getQuit, false);
List<GroupMember> members = this.list(memberWrapper);
return members.stream().map(GroupMember::getUserId).collect(Collectors.toList());
}
@ -71,8 +70,8 @@ public class GroupMemberServiceImpl extends ServiceImpl<GroupMemberMapper, Group
@Override
public void removeByGroupId(Long groupId) {
LambdaUpdateWrapper<GroupMember> wrapper = Wrappers.lambdaUpdate();
wrapper.eq(GroupMember::getGroupId,groupId)
.set(GroupMember::getQuit,true);
wrapper.eq(GroupMember::getGroupId, groupId)
.set(GroupMember::getQuit, true);
this.update(wrapper);
}
@ -80,9 +79,9 @@ public class GroupMemberServiceImpl extends ServiceImpl<GroupMemberMapper, Group
@Override
public void removeByGroupAndUserId(Long groupId, Long userId) {
LambdaUpdateWrapper<GroupMember> wrapper = Wrappers.lambdaUpdate();
wrapper.eq(GroupMember::getGroupId,groupId)
.eq(GroupMember::getUserId,userId)
.set(GroupMember::getQuit,true);
wrapper.eq(GroupMember::getGroupId, groupId)
.eq(GroupMember::getUserId, userId)
.set(GroupMember::getQuit, true);
this.update(wrapper);
}
}

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

@ -1,7 +1,8 @@
package com.bx.implatform.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.text.CharSequenceUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
@ -28,8 +29,10 @@ import com.bx.implatform.session.UserSession;
import com.bx.implatform.util.BeanUtils;
import com.bx.implatform.util.DateTimeUtils;
import com.bx.implatform.vo.GroupMessageVO;
import lombok.AllArgsConstructor;
import com.google.common.base.Splitter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
@ -38,7 +41,7 @@ import java.util.stream.Collectors;
@Slf4j
@Service
@AllArgsConstructor
@RequiredArgsConstructor
public class GroupMessageServiceImpl extends ServiceImpl<GroupMessageMapper, GroupMessage> implements IGroupMessageService {
private final IGroupService groupService;
private final IGroupMemberService groupMemberService;
@ -69,8 +72,8 @@ public class GroupMessageServiceImpl extends ServiceImpl<GroupMessageMapper, Gro
msg.setSendId(session.getUserId());
msg.setSendTime(new Date());
msg.setSendNickName(member.getAliasName());
if (CollectionUtil.isNotEmpty(dto.getAtUserIds())) {
msg.setAtUserIds(StrUtil.join(",", dto.getAtUserIds()));
if (CollUtil.isNotEmpty(dto.getAtUserIds())) {
msg.setAtUserIds(CharSequenceUtil.join(",", dto.getAtUserIds()));
}
this.save(msg);
// 群发
@ -140,10 +143,10 @@ public class GroupMessageServiceImpl extends ServiceImpl<GroupMessageMapper, Gro
for (GroupMember member : members) {
// 获取群聊已读的最大消息id,只推送未读消息
String key = String.join(":", RedisKey.IM_GROUP_READED_POSITION, member.getGroupId().toString(), session.getUserId().toString());
Integer maxReadedId = (Integer)redisTemplate.opsForValue().get(key);
Integer maxReadedId = (Integer) redisTemplate.opsForValue().get(key);
LambdaQueryWrapper<GroupMessage> wrapper = Wrappers.lambdaQuery();
wrapper.eq(GroupMessage::getGroupId, member.getGroupId()).gt(GroupMessage::getSendTime, member.getCreatedTime())
.ne(GroupMessage::getSendId, session.getUserId()).ne(GroupMessage::getStatus, MessageStatus.RECALL.code());
.ne(GroupMessage::getSendId, session.getUserId()).ne(GroupMessage::getStatus, MessageStatus.RECALL.code());
if (maxReadedId != null) {
wrapper.gt(GroupMessage::getId, maxReadedId);
}
@ -181,25 +184,27 @@ public class GroupMessageServiceImpl extends ServiceImpl<GroupMessageMapper, Gro
Date minDate = DateTimeUtils.addMonths(new Date(), -1);
LambdaQueryWrapper<GroupMessage> wrapper = Wrappers.lambdaQuery();
wrapper.gt(GroupMessage::getId, minId).gt(GroupMessage::getSendTime, minDate).in(GroupMessage::getGroupId, ids)
.ne(GroupMessage::getStatus, MessageStatus.RECALL.code()).orderByAsc(GroupMessage::getId).last("limit 100");
.ne(GroupMessage::getStatus, MessageStatus.RECALL.code()).orderByAsc(GroupMessage::getId).last("limit 100");
List<GroupMessage> messages = this.list(wrapper);
// 转成vo
List<GroupMessageVO> vos = messages.stream().map(m -> {
GroupMessageVO vo = BeanUtils.copyProperties(m, GroupMessageVO.class);
// 被@用户列表
List<String> atIds = Arrays.asList(StrUtil.split(m.getAtUserIds(), ","));
vo.setAtUserIds(atIds.stream().map(Long::parseLong).collect(Collectors.toList()));
if (StringUtils.isNotBlank(m.getAtUserIds())) {
List<String> atIds = Splitter.on(",").trimResults().splitToList(m.getAtUserIds());
vo.setAtUserIds(atIds.stream().map(Long::parseLong).collect(Collectors.toList()));
}
return vo;
}).collect(Collectors.toList());
// 消息状态,数据库没有存群聊的消息状态,需要从redis取
List<String> keys = ids.stream().map(id -> String.join(":", RedisKey.IM_GROUP_READED_POSITION, id.toString(), session.getUserId().toString()))
.collect(Collectors.toList());
.collect(Collectors.toList());
List<Object> sendPos = redisTemplate.opsForValue().multiGet(keys);
int idx = 0;
for (Long id : ids) {
Object o = sendPos.get(idx);
Integer sendMaxId = Objects.isNull(o) ? -1 : (Integer)o;
Integer sendMaxId = Objects.isNull(o) ? -1 : (Integer) o;
vos.stream().filter(vo -> vo.getGroupId().equals(id)).forEach(vo -> {
if (vo.getId() <= sendMaxId) {
// 已读
@ -237,7 +242,7 @@ public class GroupMessageServiceImpl extends ServiceImpl<GroupMessageMapper, Gro
sendMessage.setSendResult(false);
imClient.sendGroupMessage(sendMessage);
// 记录已读消息位置
String key = StrUtil.join(":", RedisKey.IM_GROUP_READED_POSITION, groupId, session.getUserId());
String key = CharSequenceUtil.join(":", RedisKey.IM_GROUP_READED_POSITION, groupId, session.getUserId());
redisTemplate.opsForValue().set(key, message.getId());
}
@ -256,11 +261,11 @@ public class GroupMessageServiceImpl extends ServiceImpl<GroupMessageMapper, Gro
// 查询聊天记录,只查询加入群聊时间之后的消息
QueryWrapper<GroupMessage> 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);
.ne(GroupMessage::getStatus, MessageStatus.RECALL.code()).orderByDesc(GroupMessage::getId).last("limit " + stIdx + "," + size);
List<GroupMessage> messages = this.list(wrapper);
List<GroupMessageVO> messageInfos =
messages.stream().map(m -> BeanUtils.copyProperties(m, GroupMessageVO.class)).collect(Collectors.toList());
messages.stream().map(m -> BeanUtils.copyProperties(m, GroupMessageVO.class)).collect(Collectors.toList());
log.info("拉取群聊记录,用户id:{},群聊id:{},数量:{}", userId, groupId, messageInfos.size());
return messageInfos;
}

13
im-platform/src/main/java/com/bx/implatform/service/impl/GroupServiceImpl.java

@ -23,7 +23,7 @@ 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.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cache.annotation.CacheConfig;
@ -32,16 +32,13 @@ import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.*;
import java.util.stream.Collectors;
@Slf4j
@CacheConfig(cacheNames = RedisKey.IM_CACHE_GROUP)
@Service
@AllArgsConstructor
@RequiredArgsConstructor
public class GroupServiceImpl extends ServiceImpl<GroupMapper, Group> implements IGroupService {
private final IUserService userService;
private final IGroupMemberService groupMemberService;
@ -90,7 +87,7 @@ public class GroupServiceImpl extends ServiceImpl<GroupMapper, Group> implements
throw new GlobalException(ResultCode.PROGRAM_ERROR, "您不是群聊的成员");
}
member.setAliasName(StringUtils.isEmpty(vo.getAliasName()) ? session.getNickName() : vo.getAliasName());
member.setRemark(StringUtils.isEmpty(vo.getRemark()) ? group.getName() : vo.getRemark());
member.setRemark(StringUtils.isEmpty(vo.getRemark()) ? Objects.requireNonNull(group).getName() : vo.getRemark());
groupMemberService.updateById(member);
log.info("修改群聊,群聊id:{},群聊名称:{}", group.getId(), group.getName());
return vo;
@ -206,7 +203,7 @@ public class GroupServiceImpl extends ServiceImpl<GroupMapper, Group> implements
// 找出好友信息
List<Friend> friends = friendsService.findFriendByUserId(session.getUserId());
List<Friend> friendsList = vo.getFriendIds().stream().map(id -> friends.stream().filter(f -> f.getFriendId().equals(id)).findFirst().get())
.collect(Collectors.toList());
.collect(Collectors.toList());
if (friendsList.size() != vo.getFriendIds().size()) {
throw new GlobalException(ResultCode.PROGRAM_ERROR, "部分用户不是您的好友,邀请失败");
}

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

@ -25,6 +25,7 @@ import com.bx.implatform.util.BeanUtils;
import com.bx.implatform.util.DateTimeUtils;
import com.bx.implatform.vo.PrivateMessageVO;
import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@ -37,7 +38,7 @@ import java.util.stream.Collectors;
@Slf4j
@Service
@AllArgsConstructor
@RequiredArgsConstructor
public class PrivateMessageServiceImpl extends ServiceImpl<PrivateMessageMapper, PrivateMessage> implements IPrivateMessageService {
private final IFriendService friendService;
@ -47,7 +48,7 @@ public class PrivateMessageServiceImpl extends ServiceImpl<PrivateMessageMapper,
public Long sendMessage(PrivateMessageDTO dto) {
UserSession session = SessionContext.getSession();
Boolean isFriends = friendService.isFriend(session.getUserId(), dto.getRecvId());
if (!isFriends) {
if (Boolean.FALSE.equals(isFriends)) {
throw new GlobalException(ResultCode.PROGRAM_ERROR, "您已不是对方好友,无法发送消息");
}
// 保存消息
@ -107,7 +108,6 @@ public class PrivateMessageServiceImpl extends ServiceImpl<PrivateMessageMapper,
}
@Override
public List<PrivateMessageVO> findHistoryMessage(Long friendId, Long page, Long size) {
page = page > 0 ? page : 1;
@ -167,7 +167,6 @@ public class PrivateMessageServiceImpl extends ServiceImpl<PrivateMessageMapper,
}
@Override
public List<PrivateMessageVO> loadMessage(Long minId) {
UserSession session = SessionContext.getSession();
@ -208,7 +207,6 @@ public class PrivateMessageServiceImpl extends ServiceImpl<PrivateMessageMapper,
}
@Transactional(rollbackFor = Exception.class)
@Override
public void readedMessage(Long friendId) {

6
im-platform/src/main/java/com/bx/implatform/service/impl/UserServiceImpl.java

@ -27,9 +27,8 @@ import com.bx.implatform.util.BeanUtils;
import com.bx.implatform.vo.LoginVO;
import com.bx.implatform.vo.OnlineTerminalVO;
import com.bx.implatform.vo.UserVO;
import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@ -39,10 +38,9 @@ import java.util.stream.Collectors;
@Slf4j
@Service
@AllArgsConstructor
@RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
private final RedisTemplate<String, Object> redisTemplate;
private final PasswordEncoder passwordEncoder;
private final IGroupMemberService groupMemberService;
private final IFriendService friendService;

20
im-platform/src/main/java/com/bx/implatform/service/impl/WebrtcServiceImpl.java

@ -13,7 +13,7 @@ import com.bx.implatform.session.SessionContext;
import com.bx.implatform.session.UserSession;
import com.bx.implatform.session.WebrtcSession;
import com.bx.implatform.vo.PrivateMessageVO;
import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
@ -25,7 +25,7 @@ import java.util.concurrent.TimeUnit;
@Slf4j
@Service
@AllArgsConstructor
@RequiredArgsConstructor
public class WebrtcServiceImpl implements IWebrtcService {
private final IMClient imClient;
@ -52,7 +52,7 @@ public class WebrtcServiceImpl implements IWebrtcService {
messageInfo.setContent(offer);
IMPrivateMessage<PrivateMessageVO> sendMessage = new IMPrivateMessage<>();
sendMessage.setSender(new IMUserInfo(session.getUserId(),session.getTerminal()));
sendMessage.setSender(new IMUserInfo(session.getUserId(), session.getTerminal()));
sendMessage.setRecvId(uid);
sendMessage.setSendToSelf(false);
sendMessage.setSendResult(false);
@ -79,7 +79,7 @@ public class WebrtcServiceImpl implements IWebrtcService {
messageInfo.setContent(answer);
IMPrivateMessage<PrivateMessageVO> sendMessage = new IMPrivateMessage<>();
sendMessage.setSender(new IMUserInfo(session.getUserId(),session.getTerminal()));
sendMessage.setSender(new IMUserInfo(session.getUserId(), session.getTerminal()));
sendMessage.setRecvId(uid);
// 告知其他终端已经接受会话,中止呼叫
sendMessage.setSendToSelf(true);
@ -103,7 +103,7 @@ public class WebrtcServiceImpl implements IWebrtcService {
messageInfo.setSendId(session.getUserId());
IMPrivateMessage<PrivateMessageVO> sendMessage = new IMPrivateMessage<>();
sendMessage.setSender(new IMUserInfo(session.getUserId(),session.getTerminal()));
sendMessage.setSender(new IMUserInfo(session.getUserId(), session.getTerminal()));
sendMessage.setRecvId(uid);
// 告知其他终端已经拒绝会话,中止呼叫
sendMessage.setSendToSelf(true);
@ -125,7 +125,7 @@ public class WebrtcServiceImpl implements IWebrtcService {
messageInfo.setSendId(session.getUserId());
IMPrivateMessage<PrivateMessageVO> sendMessage = new IMPrivateMessage<>();
sendMessage.setSender(new IMUserInfo(session.getUserId(),session.getTerminal()));
sendMessage.setSender(new IMUserInfo(session.getUserId(), session.getTerminal()));
sendMessage.setRecvId(uid);
sendMessage.setSendToSelf(false);
sendMessage.setSendResult(false);
@ -148,7 +148,7 @@ public class WebrtcServiceImpl implements IWebrtcService {
messageInfo.setSendId(session.getUserId());
IMPrivateMessage<PrivateMessageVO> sendMessage = new IMPrivateMessage<>();
sendMessage.setSender(new IMUserInfo(session.getUserId(),session.getTerminal()));
sendMessage.setSender(new IMUserInfo(session.getUserId(), session.getTerminal()));
sendMessage.setRecvId(uid);
// 告知其他终端已经会话失败,中止呼叫
sendMessage.setSendToSelf(true);
@ -174,7 +174,7 @@ public class WebrtcServiceImpl implements IWebrtcService {
messageInfo.setSendId(session.getUserId());
IMPrivateMessage<PrivateMessageVO> sendMessage = new IMPrivateMessage<>();
sendMessage.setSender(new IMUserInfo(session.getUserId(),session.getTerminal()));
sendMessage.setSender(new IMUserInfo(session.getUserId(), session.getTerminal()));
sendMessage.setRecvId(uid);
sendMessage.setSendToSelf(false);
sendMessage.setSendResult(false);
@ -198,7 +198,7 @@ public class WebrtcServiceImpl implements IWebrtcService {
messageInfo.setContent(candidate);
IMPrivateMessage<PrivateMessageVO> sendMessage = new IMPrivateMessage<>();
sendMessage.setSender(new IMUserInfo(session.getUserId(),session.getTerminal()));
sendMessage.setSender(new IMUserInfo(session.getUserId(), session.getTerminal()));
sendMessage.setRecvId(uid);
sendMessage.setSendToSelf(false);
sendMessage.setSendResult(false);
@ -215,7 +215,7 @@ public class WebrtcServiceImpl implements IWebrtcService {
private WebrtcSession getWebrtcSession(Long userId, Long uid) {
String key = getSessionKey(userId, uid);
WebrtcSession webrtcSession = (WebrtcSession)redisTemplate.opsForValue().get(key);
WebrtcSession webrtcSession = (WebrtcSession) redisTemplate.opsForValue().get(key);
if (webrtcSession == null) {
throw new GlobalException("视频通话已结束");
}

69
im-platform/src/main/java/com/bx/implatform/service/thirdparty/FileService.java

@ -18,19 +18,20 @@ import org.springframework.web.multipart.MultipartFile;
import javax.annotation.PostConstruct;
import java.io.IOException;
import java.util.Objects;
/**
* todo 通过校验文件MD5实现重复文件秒传
* 通过校验文件MD5实现重复文件秒传
* 文件上传服务
*
* @author Blue
* @date 2022/10/28
*
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class FileService {
private final MinioUtil minioUtil;
private final MinioUtil minioUtil;
@Value("${minio.public}")
private String minIoServer;
@Value("${minio.bucketName}")
@ -42,8 +43,8 @@ public class FileService {
@PostConstruct
public void init(){
if(!minioUtil.bucketExists(bucketName)){
public void init() {
if (!minioUtil.bucketExists(bucketName)) {
// 创建bucket
minioUtil.makeBucket(bucketName);
// 公开bucket
@ -52,61 +53,61 @@ public class FileService {
}
public String uploadFile(MultipartFile file){
public String uploadFile(MultipartFile file) {
Long userId = SessionContext.getSession().getUserId();
// 大小校验
if(file.getSize() > Constant.MAX_FILE_SIZE){
throw new GlobalException(ResultCode.PROGRAM_ERROR,"文件大小不能超过10M");
if (file.getSize() > Constant.MAX_FILE_SIZE) {
throw new GlobalException(ResultCode.PROGRAM_ERROR, "文件大小不能超过10M");
}
// 上传
String fileName = minioUtil.upload(bucketName,filePath,file);
if(StringUtils.isEmpty(fileName)){
throw new GlobalException(ResultCode.PROGRAM_ERROR,"文件上传失败");
String fileName = minioUtil.upload(bucketName, filePath, file);
if (StringUtils.isEmpty(fileName)) {
throw new GlobalException(ResultCode.PROGRAM_ERROR, "文件上传失败");
}
String url = generUrl(FileType.FILE,fileName);
log.info("文件文件成功,用户id:{},url:{}",userId,url);
String url = generUrl(FileType.FILE, fileName);
log.info("文件文件成功,用户id:{},url:{}", userId, url);
return url;
}
public UploadImageVO uploadImage(MultipartFile file){
public UploadImageVO uploadImage(MultipartFile file) {
try {
Long userId = SessionContext.getSession().getUserId();
// 大小校验
if(file.getSize() > Constant.MAX_IMAGE_SIZE){
throw new GlobalException(ResultCode.PROGRAM_ERROR,"图片大小不能超过5M");
if (file.getSize() > Constant.MAX_IMAGE_SIZE) {
throw new GlobalException(ResultCode.PROGRAM_ERROR, "图片大小不能超过5M");
}
// 图片格式校验
if(!FileUtil.isImage(file.getOriginalFilename())){
throw new GlobalException(ResultCode.PROGRAM_ERROR,"图片格式不合法");
if (!FileUtil.isImage(file.getOriginalFilename())) {
throw new GlobalException(ResultCode.PROGRAM_ERROR, "图片格式不合法");
}
// 上传原图
UploadImageVO vo = new UploadImageVO();
String fileName = minioUtil.upload(bucketName,imagePath,file);
if(StringUtils.isEmpty(fileName)){
throw new GlobalException(ResultCode.PROGRAM_ERROR,"图片上传失败");
String fileName = minioUtil.upload(bucketName, imagePath, file);
if (StringUtils.isEmpty(fileName)) {
throw new GlobalException(ResultCode.PROGRAM_ERROR, "图片上传失败");
}
vo.setOriginUrl(generUrl(FileType.IMAGE,fileName));
vo.setOriginUrl(generUrl(FileType.IMAGE, fileName));
// 大于30K的文件需上传缩略图
if(file.getSize() > 30 * 1024){
byte[] imageByte = ImageUtil.compressForScale(file.getBytes(),30);
fileName = minioUtil.upload(bucketName,imagePath,file.getOriginalFilename(),imageByte,file.getContentType());
if(StringUtils.isEmpty(fileName)){
throw new GlobalException(ResultCode.PROGRAM_ERROR,"图片上传失败");
if (file.getSize() > 30 * 1024) {
byte[] imageByte = ImageUtil.compressForScale(file.getBytes(), 30);
fileName = minioUtil.upload(bucketName, imagePath, Objects.requireNonNull(file.getOriginalFilename()), imageByte, file.getContentType());
if (StringUtils.isEmpty(fileName)) {
throw new GlobalException(ResultCode.PROGRAM_ERROR, "图片上传失败");
}
}
vo.setThumbUrl(generUrl(FileType.IMAGE,fileName));
log.info("文件图片成功,用户id:{},url:{}",userId,vo.getOriginUrl());
vo.setThumbUrl(generUrl(FileType.IMAGE, fileName));
log.info("文件图片成功,用户id:{},url:{}", userId, vo.getOriginUrl());
return vo;
} catch (IOException e) {
log.error("上传图片失败,{}",e.getMessage(),e);
throw new GlobalException(ResultCode.PROGRAM_ERROR,"图片上传失败");
log.error("上传图片失败,{}", e.getMessage(), e);
throw new GlobalException(ResultCode.PROGRAM_ERROR, "图片上传失败");
}
}
public String generUrl(FileType fileTypeEnum, String fileName){
String url = minIoServer +"/"+bucketName;
switch (fileTypeEnum){
public String generUrl(FileType fileTypeEnum, String fileName) {
String url = minIoServer + "/" + bucketName;
switch (fileTypeEnum) {
case FILE:
url += "/file/";
break;

9
im-platform/src/main/java/com/bx/implatform/session/SessionContext.java

@ -12,14 +12,11 @@ import javax.servlet.http.HttpServletRequest;
*/
public class SessionContext {
public static UserSession getSession(){
public static UserSession getSession() {
// 从请求上下文里获取Request对象
ServletRequestAttributes requestAttributes = ServletRequestAttributes.class.
cast(RequestContextHolder.getRequestAttributes());
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = requestAttributes.getRequest();
UserSession userSession = (UserSession) request.getAttribute("session");
return userSession;
return (UserSession) request.getAttribute("session");
}
}

8
im-platform/src/main/java/com/bx/implatform/session/UserSession.java

@ -2,18 +2,18 @@ package com.bx.implatform.session;
import com.bx.imcommon.model.IMSessionInfo;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
public class UserSession extends IMSessionInfo {
/*
/**
* 用户名称
*/
private String userName;
/*
/**
* 用户昵称
*/
private String nickName;

75
im-platform/src/main/java/com/bx/implatform/util/BeanUtils.java

@ -1,42 +1,45 @@
package com.bx.implatform.util;
import org.springframework.util.ReflectionUtils;
public final class BeanUtils {
private BeanUtils() {
}
private static void handleReflectionException(Exception e) {
ReflectionUtils.handleReflectionException(e);
}
/**
* 属性拷贝
*
* @param orig 源对象
* @param destClass 目标
* @return T
*/
public static <T> T copyProperties(Object orig, Class<T> destClass) {
try {
T target = destClass.newInstance();
if (orig == null) {
return null;
}
copyProperties(orig, target);
return target;
} catch (Exception e) {
handleReflectionException(e);
return null;
}
}
public class BeanUtils {
private static void handleReflectionException(Exception e) {
ReflectionUtils.handleReflectionException(e);
}
/**
* 属性拷贝
* @param orig 源对象
* @param destClass 目标
* @return T
*/
public static <T> T copyProperties(Object orig, Class<T> destClass) {
try {
Object target = destClass.newInstance();
if(orig == null) {
return null;
}
copyProperties(orig, target);
return (T) target;
}catch(Exception e) {
handleReflectionException(e);
return null;
}
}
public static void copyProperties(Object orig, Object dest) {
try {
org.springframework.beans.BeanUtils.copyProperties(orig, dest);
} catch (Exception e) {
handleReflectionException(e);
}
}
public static void copyProperties(Object orig, Object dest) {
try {
org.springframework.beans.BeanUtils.copyProperties(orig, dest);
} catch (Exception e) {
handleReflectionException(e);
}
}
}

7
im-platform/src/main/java/com/bx/implatform/util/DateTimeUtils.java

@ -11,7 +11,10 @@ import java.util.Date;
*
* @version 1.0
*/
public class DateTimeUtils extends DateUtils {
public final class DateTimeUtils extends DateUtils {
private DateTimeUtils() {
}
public static final String FULL_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
public static final String PARTDATEFORMAT = "yyyyMMdd";
@ -31,5 +34,5 @@ public class DateTimeUtils extends DateUtils {
return sdf.format(date);
}
}

15
im-platform/src/main/java/com/bx/implatform/util/FileUtil.java

@ -1,11 +1,14 @@
package com.bx.implatform.util;
public class FileUtil {
public final class FileUtil {
private FileUtil() {
}
/**
* 获取文件后缀
*
* @param fileName 文件名
* @param fileName 文件名
* @return boolean
*/
public static String getFileExtension(String fileName) {
@ -15,13 +18,13 @@ public class FileUtil {
/**
* 判断文件是否图片类型
*
* @param fileName 文件名
* @return boolean
* @param fileName 文件名
* @return boolean
*/
public static boolean isImage(String fileName) {
String extension = getFileExtension(fileName);
String[] imageExtension = new String[]{"jpeg", "jpg", "bmp", "png","webp","gif"};
for (String e : imageExtension){
String[] imageExtension = new String[]{"jpeg", "jpg", "bmp", "png", "webp", "gif"};
for (String e : imageExtension) {
if (extension.toLowerCase().equals(e)) {
return true;
}

8
im-platform/src/main/java/com/bx/implatform/util/ImageUtil.java

@ -1,6 +1,5 @@
package com.bx.implatform.util;
import lombok.extern.slf4j.Slf4j;
import net.coobird.thumbnailator.Thumbnails;
@ -8,7 +7,10 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@Slf4j
public class ImageUtil {
public final class ImageUtil {
private ImageUtil() {
}
//以下是常量,按照阿里代码开发规范,不允许代码中出现魔法值
private static final Integer ZERO = 0;
@ -53,8 +55,6 @@ public class ImageUtil {
}
/**
* 自动调节精度(经验数值)
*

71
im-platform/src/main/java/com/bx/implatform/util/MinioUtil.java

@ -1,8 +1,7 @@
package com.bx.implatform.util;
import io.minio.*;
import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
@ -14,20 +13,21 @@ import java.util.Date;
@Slf4j
@Component
@AllArgsConstructor
@RequiredArgsConstructor
public class MinioUtil {
private final MinioClient minioClient;
private final MinioClient minioClient;
/**
* 查看存储bucket是否存在
*
* @return boolean
*/
public Boolean bucketExists(String bucketName) {
try {
return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
} catch (Exception e) {
log.error("查询bucket失败",e);
log.error("查询bucket失败", e);
return false;
}
}
@ -41,7 +41,7 @@ public class MinioUtil {
.bucket(bucketName)
.build());
} catch (Exception e) {
log.error("创建bucket失败,",e);
log.error("创建bucket失败,", e);
}
}
@ -64,34 +64,35 @@ public class MinioUtil {
.config(sb)
.build());
} catch (Exception e) {
log.error("创建bucket失败,",e);
log.error("创建bucket失败,", e);
}
}
/**
* 文件上传
*
* @param bucketName bucket名称
* @param path 路径
* @param file 文件
* @param path 路径
* @param file 文件
* @return Boolean
*/
public String upload(String bucketName,String path,MultipartFile file) {
public String upload(String bucketName, String path, MultipartFile file) {
String originalFilename = file.getOriginalFilename();
if (StringUtils.isBlank(originalFilename)){
if (StringUtils.isBlank(originalFilename)) {
throw new RuntimeException();
}
String fileName = System.currentTimeMillis()+"";
if(originalFilename.lastIndexOf(".") >= 0){
fileName +=originalFilename.substring(originalFilename.lastIndexOf("."));
String fileName = System.currentTimeMillis() + "";
if (originalFilename.lastIndexOf(".") >= 0) {
fileName += originalFilename.substring(originalFilename.lastIndexOf("."));
}
String objectName = DateTimeUtils.getFormatDate(new Date(),DateTimeUtils.PARTDATEFORMAT)+ "/" + fileName;
String objectName = DateTimeUtils.getFormatDate(new Date(), DateTimeUtils.PARTDATEFORMAT) + "/" + fileName;
try {
PutObjectArgs objectArgs = PutObjectArgs.builder().bucket(bucketName).object(path+"/" +objectName)
PutObjectArgs objectArgs = PutObjectArgs.builder().bucket(bucketName).object(path + "/" + objectName)
.stream(file.getInputStream(), file.getSize(), -1).contentType(file.getContentType()).build();
//文件名称相同会覆盖
minioClient.putObject(objectArgs);
} catch (Exception e) {
log.error("上传图片失败,",e);
log.error("上传图片失败,", e);
return null;
}
return objectName;
@ -99,25 +100,26 @@ public class MinioUtil {
/**
* 文件上传
* @param bucketName bucket名称
* @param path 路径
* @param name 文件名
* @param fileByte 文件内容
* @param contentType contentType
* @return objectName
*
* @param bucketName bucket名称
* @param path 路径
* @param name 文件名
* @param fileByte 文件内容
* @param contentType contentType
* @return objectName
*/
public String upload(String bucketName,String path,String name,byte[] fileByte,String contentType) {
public String upload(String bucketName, String path, String name, byte[] fileByte, String contentType) {
String fileName = System.currentTimeMillis() + name.substring(name.lastIndexOf("."));
String objectName = DateTimeUtils.getFormatDate(new Date(),DateTimeUtils.PARTDATEFORMAT)+ "/" + fileName;
String objectName = DateTimeUtils.getFormatDate(new Date(), DateTimeUtils.PARTDATEFORMAT) + "/" + fileName;
try {
InputStream stream = new ByteArrayInputStream(fileByte);
PutObjectArgs objectArgs = PutObjectArgs.builder().bucket(bucketName).object(path+"/" +objectName)
PutObjectArgs objectArgs = PutObjectArgs.builder().bucket(bucketName).object(path + "/" + objectName)
.stream(stream, fileByte.length, -1).contentType(contentType).build();
//文件名称相同会覆盖
minioClient.putObject(objectArgs);
} catch (Exception e) {
log.error("上传文件失败,",e);
log.error("上传文件失败,", e);
return null;
}
return objectName;
@ -126,16 +128,17 @@ public class MinioUtil {
/**
* 删除
*
* @param bucketName bucket名称
* @param path 路径
* @param fileName 文件名
* @param path 路径
* @param fileName 文件名
* @return true/false
*/
public boolean remove(String bucketName,String path,String fileName){
public boolean remove(String bucketName, String path, String fileName) {
try {
minioClient.removeObject( RemoveObjectArgs.builder().bucket(bucketName).object(path+fileName).build());
}catch (Exception e){
log.error("删除文件失败,",e);
minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(path + fileName).build());
} catch (Exception e) {
log.error("删除文件失败,", e);
return false;
}
return true;

7
im-platform/src/main/java/com/bx/implatform/util/XssUtil.java

@ -3,13 +3,16 @@ package com.bx.implatform.util;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class XssUtil {
public final class XssUtil {
private XssUtil() {
}
private static final String XSS_PATTERN = "((?i)<script.*?>)|((?i)alert\\s*\\()|((?i)prompt\\s*\\()|((?i)document\\.cookie)|((?i)location\\.href)|((?i)window\\.location)|((?i)onerror\\s*\\()|((?i)eval\\s*\\()|((?i)window\\.open\\s*\\()|((?i)innerHTML)|((?i)onclick\\s*\\()|((?i)onmouseover\\s*\\()|((?i)onsubmit\\s*\\()|((?i)onload\\s*\\()|((?i)onfocus\\s*\\()|((?i)onblur\\s*\\()|((?i)onkeyup\\s*\\()|((?i)onkeydown\\s*\\()|((?i)onkeypress\\s*\\()|((?i)onmouseout\\s*\\()|((?i)src=)|((?i)href=)|((?i)style=)|((?i)background=)|((?i)expression\\s*\\()|((?i)XMLHttpRequest\\s*\\()|((?i)ActiveXObject\\s*\\()|((?i)iframe)|((?i)document\\.write\\s*\\()|((?i)document\\.writeln\\s*\\()|((?i)setTimeout\\s*\\()|((?i)setInterval\\s*\\()|((?i)onreadystatechange\\s*\\()|((?i)appendChild\\s*\\()|((?i)createTextNode\\s*\\()|((?i)createElement\\s*\\()|((?i)getElementsByTagName\\s*\\()|((?i)getElementsByClassName\\s*\\()|((?i)querySelector\\s*\\()|((?i)querySelectorAll\\s*\\()|((?i)document\\.location)|((?i)document\\.body\\.innerHTML)|((?i)document\\.forms)|((?i)document\\.images)|((?i)document\\.links)|((?i)document\\.URL)|((?i)document\\.domain)|((?i)document\\.referrer)|((?i)history\\.back\\s*\\()";
private static final Pattern PATTERN = Pattern.compile(XSS_PATTERN);
public static boolean checkXss(String inputString) {
if (inputString!=null) {
if (inputString != null) {
Matcher matcher = PATTERN.matcher(inputString);
return matcher.find();
}

1
im-platform/src/main/java/com/bx/implatform/vo/FriendVO.java

@ -1,6 +1,5 @@
package com.bx.implatform.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

1
im-platform/src/main/java/com/bx/implatform/vo/GroupMemberVO.java

@ -1,6 +1,5 @@
package com.bx.implatform.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

9
im-platform/src/main/java/com/bx/implatform/vo/GroupVO.java

@ -11,11 +11,10 @@ import javax.validation.constraints.NotEmpty;
@ApiModel("群信息VO")
public class GroupVO {
@ApiModelProperty(value = "群id")
private Long id;
@Length(max=20,message = "群名称长度不能大于20")
@Length(max = 20, message = "群名称长度不能大于20")
@NotEmpty(message = "群名称不可为空")
@ApiModelProperty(value = "群名称")
private String name;
@ -29,15 +28,15 @@ public class GroupVO {
@ApiModelProperty(value = "头像缩略图")
private String headImageThumb;
@Length(max=1024,message = "群聊显示长度不能大于1024")
@Length(max = 1024, message = "群聊显示长度不能大于1024")
@ApiModelProperty(value = "群公告")
private String notice;
@Length(max=20,message = "群聊显示长度不能大于20")
@Length(max = 20, message = "群聊显示长度不能大于20")
@ApiModelProperty(value = "用户在群显示昵称")
private String aliasName;
@Length(max=20,message = "群聊显示长度不能大于20")
@Length(max = 20, message = "群聊显示长度不能大于20")
@ApiModelProperty(value = "群聊显示备注")
private String remark;

7
im-platform/src/main/java/com/bx/implatform/vo/UserVO.java

@ -1,6 +1,5 @@
package com.bx.implatform.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@ -18,12 +17,12 @@ public class UserVO {
private Long id;
@NotEmpty(message = "用户名不能为空")
@Length(max = 64,message = "用户名不能大于64字符")
@Length(max = 64, message = "用户名不能大于64字符")
@ApiModelProperty(value = "用户名")
private String userName;
@NotEmpty(message = "用户昵称不能为空")
@Length(max = 64,message = "昵称不能大于64字符")
@Length(max = 64, message = "昵称不能大于64字符")
@ApiModelProperty(value = "用户昵称")
private String nickName;
@ -33,7 +32,7 @@ public class UserVO {
@ApiModelProperty(value = "用户类型 1:普通用户 2:审核账户")
private Integer type;
@Length(max = 1024,message = "个性签名不能大于1024个字符")
@Length(max = 1024, message = "个性签名不能大于1024个字符")
@ApiModelProperty(value = "个性签名")
private String signature;

7
im-server/src/main/java/com/bx/imserver/IMServerApp.java

@ -1,22 +1,19 @@
package com.bx.imserver;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
@EnableAsync
@EnableScheduling
@ComponentScan(basePackages={"com.bx"})
@ComponentScan(basePackages = {"com.bx"})
@SpringBootApplication
public class IMServerApp {
public static void main(String[] args) {
SpringApplication.run(IMServerApp.class,args);
SpringApplication.run(IMServerApp.class, args);
}
}

2
im-server/src/main/java/com/bx/imserver/config/RedisConfig.java

@ -25,7 +25,7 @@ public class RedisConfig {
}
public FastJsonRedisSerializer fastJsonRedisSerializer(){
public FastJsonRedisSerializer fastJsonRedisSerializer() {
return new FastJsonRedisSerializer<>(Object.class);
}

17
im-server/src/main/java/com/bx/imserver/constant/ChannelAttrKey.java

@ -1,12 +1,21 @@
package com.bx.imserver.constant;
public class ChannelAttrKey {
public final class ChannelAttrKey {
// 用户ID
private ChannelAttrKey() {
}
/**
* 用户ID
*/
public static final String USER_ID = "USER_ID";
// 终端类型
/**
* 终端类型
*/
public static final String TERMINAL_TYPE = "TERMINAL_TYPE";
// 心跳次数
/**
* 心跳次数
*/
public static final String HEARTBEAT_TIMES = "HEARTBEAt_TIMES";
}

29
im-server/src/main/java/com/bx/imserver/netty/IMChannelHandler.java

@ -15,7 +15,6 @@ import io.netty.util.AttributeKey;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
/**
* WebSocket 长连接下 文本帧的处理器
* 实现浏览器发送文本回写
@ -27,24 +26,24 @@ public class IMChannelHandler extends SimpleChannelInboundHandler<IMSendInfo> {
/**
* 读取到消息后进行处理
*
* @param ctx channel上下文
* @param ctx channel上下文
* @param sendInfo 发送消息
*/
@Override
protected void channelRead0(ChannelHandlerContext ctx, IMSendInfo sendInfo) {
protected void channelRead0(ChannelHandlerContext ctx, IMSendInfo sendInfo) {
// 创建处理器进行处理
AbstractMessageProcessor processor = ProcessorFactory.createProcessor(IMCmdType.fromCode(sendInfo.getCmd()));
processor.process(ctx,processor.transForm(sendInfo.getData()));
processor.process(ctx, processor.transForm(sendInfo.getData()));
}
/**
* 出现异常的处理 打印报错日志
*
* @param ctx channel上下文
* @param ctx channel上下文
* @param cause 异常信息
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
log.error(cause.getMessage());
//关闭上下文
//ctx.close();
@ -53,10 +52,10 @@ public class IMChannelHandler extends SimpleChannelInboundHandler<IMSendInfo> {
/**
* 监控浏览器上线
*
* @param ctx channel上下文
* @param ctx channel上下文
*/
@Override
public void handlerAdded(ChannelHandlerContext ctx) {
public void handlerAdded(ChannelHandlerContext ctx) {
log.info(ctx.channel().id().asLongText() + "连接");
}
@ -66,16 +65,16 @@ public class IMChannelHandler extends SimpleChannelInboundHandler<IMSendInfo> {
Long userId = ctx.channel().attr(userIdAttr).get();
AttributeKey<Integer> terminalAttr = AttributeKey.valueOf(ChannelAttrKey.TERMINAL_TYPE);
Integer terminal = ctx.channel().attr(terminalAttr).get();
ChannelHandlerContext context = UserChannelCtxMap.getChannelCtx(userId,terminal);
ChannelHandlerContext context = UserChannelCtxMap.getChannelCtx(userId, terminal);
// 判断一下,避免异地登录导致的误删
if(context != null && ctx.channel().id().equals(context.channel().id())){
if (context != null && ctx.channel().id().equals(context.channel().id())) {
// 移除channel
UserChannelCtxMap.removeChannelCtx(userId,terminal);
UserChannelCtxMap.removeChannelCtx(userId, terminal);
// 用户下线
RedisTemplate<String,Object> redisTemplate = SpringContextHolder.getBean("redisTemplate");
String key = String.join(":", IMRedisKey.IM_USER_SERVER_ID,userId.toString(), terminal.toString());
RedisTemplate<String, Object> redisTemplate = SpringContextHolder.getBean("redisTemplate");
String key = String.join(":", IMRedisKey.IM_USER_SERVER_ID, userId.toString(), terminal.toString());
redisTemplate.delete(key);
log.info("断开连接,userId:{},终端类型:{}",userId,terminal);
log.info("断开连接,userId:{},终端类型:{}", userId, terminal);
}
}
@ -89,7 +88,7 @@ public class IMChannelHandler extends SimpleChannelInboundHandler<IMSendInfo> {
Long userId = ctx.channel().attr(attr).get();
AttributeKey<Integer> terminalAttr = AttributeKey.valueOf(ChannelAttrKey.TERMINAL_TYPE);
Integer terminal = ctx.channel().attr(terminalAttr).get();
log.info("心跳超时,即将断开连接,用户id:{},终端类型:{} ",userId,terminal);
log.info("心跳超时,即将断开连接,用户id:{},终端类型:{} ", userId, terminal);
ctx.channel().close();
}
} else {

22
im-server/src/main/java/com/bx/imserver/netty/IMServerGroup.java

@ -13,23 +13,23 @@ import java.util.List;
@Slf4j
@Component
@AllArgsConstructor
public class IMServerGroup implements CommandLineRunner {
public class IMServerGroup implements CommandLineRunner {
public static volatile long serverId = 0;
RedisTemplate<String,Object> redisTemplate;
RedisTemplate<String, Object> redisTemplate;
private List<IMServer> imServers;
private final List<IMServer> imServers;
/***
* 判断服务器是否就绪
*
* @return
**/
public boolean isReady(){
for(IMServer imServer:imServers){
if(!imServer.isReady()){
return false;
public boolean isReady() {
for (IMServer imServer : imServers) {
if (!imServer.isReady()) {
return false;
}
}
return true;
@ -39,17 +39,17 @@ public class IMServerGroup implements CommandLineRunner {
public void run(String... args) throws Exception {
// 初始化SERVER_ID
String key = IMRedisKey.IM_MAX_SERVER_ID;
serverId = redisTemplate.opsForValue().increment(key,1);
serverId = redisTemplate.opsForValue().increment(key, 1);
// 启动服务
for(IMServer imServer:imServers){
for (IMServer imServer : imServers) {
imServer.start();
}
}
@PreDestroy
public void destroy(){
public void destroy() {
// 停止服务
for(IMServer imServer:imServers){
for (IMServer imServer : imServers) {
imServer.stop();
}
}

33
im-server/src/main/java/com/bx/imserver/netty/UserChannelCtxMap.java

@ -5,40 +5,37 @@ import io.netty.channel.ChannelHandlerContext;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class UserChannelCtxMap {
/*
/**
* 维护userId和ctx的关联关系格式:Map<userId,map<terminalctx>>
*/
private static Map<Long, Map<Integer,ChannelHandlerContext>> channelMap = new ConcurrentHashMap();
private static Map<Long, Map<Integer, ChannelHandlerContext>> channelMap = new ConcurrentHashMap();
public static void addChannelCtx(Long userId,Integer channel,ChannelHandlerContext ctx){
channelMap.computeIfAbsent(userId,key -> new ConcurrentHashMap()).put(channel,ctx);
public static void addChannelCtx(Long userId, Integer channel, ChannelHandlerContext ctx) {
channelMap.computeIfAbsent(userId, key -> new ConcurrentHashMap()).put(channel, ctx);
}
public static void removeChannelCtx(Long userId,Integer terminal){
if(userId != null && terminal != null && channelMap.containsKey(userId)){
Map<Integer,ChannelHandlerContext> userChannelMap = channelMap.get(userId);
if(userChannelMap.containsKey(terminal)){
userChannelMap.remove(terminal);
}
public static void removeChannelCtx(Long userId, Integer terminal) {
if (userId != null && terminal != null && channelMap.containsKey(userId)) {
Map<Integer, ChannelHandlerContext> userChannelMap = channelMap.get(userId);
userChannelMap.remove(terminal);
}
}
public static ChannelHandlerContext getChannelCtx(Long userId,Integer terminal){
if(userId != null && terminal != null && channelMap.containsKey(userId)){
Map<Integer,ChannelHandlerContext> userChannelMap = channelMap.get(userId);
if(userChannelMap.containsKey(terminal)){
public static ChannelHandlerContext getChannelCtx(Long userId, Integer terminal) {
if (userId != null && terminal != null && channelMap.containsKey(userId)) {
Map<Integer, ChannelHandlerContext> userChannelMap = channelMap.get(userId);
if (userChannelMap.containsKey(terminal)) {
return userChannelMap.get(terminal);
}
}
return null;
}
public static Map<Integer,ChannelHandlerContext> getChannelCtx(Long userId){
if(userId == null){
return null;
public static Map<Integer, ChannelHandlerContext> getChannelCtx(Long userId) {
if (userId == null) {
return null;
}
return channelMap.get(userId);
}

13
im-server/src/main/java/com/bx/imserver/netty/processor/AbstractMessageProcessor.java

@ -1,17 +1,18 @@
package com.bx.imserver.netty.processor;
import io.netty.channel.ChannelHandlerContext;
public abstract class AbstractMessageProcessor<T> {
public void process(ChannelHandlerContext ctx,T data){}
public void process(ChannelHandlerContext ctx, T data) {
}
public void process(T data){}
public void process(T data) {
}
public T transForm(Object o){
return (T)o;
}
public T transForm(Object o) {
return (T) o;
}
}

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

@ -9,7 +9,7 @@ import com.bx.imcommon.model.IMSendResult;
import com.bx.imcommon.model.IMUserInfo;
import com.bx.imserver.netty.UserChannelCtxMap;
import io.netty.channel.ChannelHandlerContext;
import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
@ -19,45 +19,45 @@ import java.util.List;
@Slf4j
@Component
@AllArgsConstructor
@RequiredArgsConstructor
public class GroupMessageProcessor extends AbstractMessageProcessor<IMRecvInfo> {
private final RedisTemplate<String,Object> redisTemplate;
private final RedisTemplate<String, Object> redisTemplate;
@Async
@Override
public void process(IMRecvInfo recvInfo) {
IMUserInfo sender = recvInfo.getSender();
List<IMUserInfo> receivers = recvInfo.getReceivers();
log.info("接收到群消息,发送者:{},接收用户数量:{},内容:{}",sender.getId(),receivers.size(),recvInfo.getData());
for(IMUserInfo receiver:receivers){
log.info("接收到群消息,发送者:{},接收用户数量:{},内容:{}", sender.getId(), receivers.size(), recvInfo.getData());
for (IMUserInfo receiver : receivers) {
try {
ChannelHandlerContext channelCtx = UserChannelCtxMap.getChannelCtx(receiver.getId(),receiver.getTerminal());
if(channelCtx != null){
ChannelHandlerContext channelCtx = UserChannelCtxMap.getChannelCtx(receiver.getId(), receiver.getTerminal());
if (channelCtx != null) {
// 推送消息到用户
IMSendInfo sendInfo = new IMSendInfo();
sendInfo.setCmd(IMCmdType.GROUP_MESSAGE.code());
sendInfo.setData(recvInfo.getData());
channelCtx.channel().writeAndFlush(sendInfo);
// 消息发送成功确认
sendResult(recvInfo,receiver,IMSendCode.SUCCESS);
sendResult(recvInfo, receiver, IMSendCode.SUCCESS);
}else {
} else {
// 消息发送成功确认
sendResult(recvInfo,receiver,IMSendCode.NOT_FIND_CHANNEL);
log.error("未找到channel,发送者:{},接收id:{},内容:{}",sender.getId(),receiver.getId(),recvInfo.getData());
sendResult(recvInfo, receiver, IMSendCode.NOT_FIND_CHANNEL);
log.error("未找到channel,发送者:{},接收id:{},内容:{}", sender.getId(), receiver.getId(), recvInfo.getData());
}
}catch (Exception e){
} catch (Exception e) {
// 消息发送失败确认
sendResult(recvInfo,receiver,IMSendCode.UNKONW_ERROR);
log.error("发送消息异常,发送者:{},接收id:{},内容:{}",sender.getId(),receiver.getId(),recvInfo.getData());
sendResult(recvInfo, receiver, IMSendCode.UNKONW_ERROR);
log.error("发送消息异常,发送者:{},接收id:{},内容:{}", sender.getId(), receiver.getId(), recvInfo.getData());
}
}
}
private void sendResult(IMRecvInfo recvInfo,IMUserInfo receiver,IMSendCode sendCode){
if(recvInfo.getSendResult()) {
private void sendResult(IMRecvInfo recvInfo, IMUserInfo receiver, IMSendCode sendCode) {
if (recvInfo.getSendResult()) {
IMSendResult result = new IMSendResult();
result.setSender(recvInfo.getSender());
result.setReceiver(receiver);

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save