Browse Source

1.0.0版本

master
xie.bx 3 years ago
commit
4b5a62406d
  1. 3
      .gitignore
  2. 61
      commom/pom.xml
  3. 10
      commom/src/main/java/com/lx/common/contant/RedisKey.java
  4. 36
      commom/src/main/java/com/lx/common/enums/MessageStatusEnum.java
  5. 37
      commom/src/main/java/com/lx/common/enums/MessageTypeEnum.java
  6. 49
      commom/src/main/java/com/lx/common/enums/ResultCode.java
  7. 39
      commom/src/main/java/com/lx/common/enums/WSCmdEnum.java
  8. 102
      commom/src/main/java/com/lx/common/generator/CodeGenerator.java
  9. 9
      commom/src/main/java/com/lx/common/model/im/HeartbeatInfo.java
  10. 11
      commom/src/main/java/com/lx/common/model/im/SendInfo.java
  11. 21
      commom/src/main/java/com/lx/common/model/im/SingleMessageInfo.java
  12. 16
      commom/src/main/java/com/lx/common/result/Result.java
  13. 83
      commom/src/main/java/com/lx/common/result/ResultUtils.java
  14. 166
      commom/src/main/java/com/lx/common/util/BeanUtils.java
  15. 1128
      commom/src/main/java/com/lx/common/util/DateTimeUtils.java
  16. 40
      commom/src/main/java/com/lx/common/util/SpringContextHolder.java
  17. 83
      im-platform/pom.xml
  18. 19
      im-platform/src/main/java/com/lx/implatform/ImplatformApp.java
  19. 106
      im-platform/src/main/java/com/lx/implatform/config/RedisConfig.java
  20. 42
      im-platform/src/main/java/com/lx/implatform/config/SwaggerConfig.java
  21. 180
      im-platform/src/main/java/com/lx/implatform/config/WebSecurityConfg.java
  22. 58
      im-platform/src/main/java/com/lx/implatform/controller/FriendsController.java
  23. 32
      im-platform/src/main/java/com/lx/implatform/controller/RegisterController.java
  24. 40
      im-platform/src/main/java/com/lx/implatform/controller/SingleMessageController.java
  25. 59
      im-platform/src/main/java/com/lx/implatform/controller/UserController.java
  26. 71
      im-platform/src/main/java/com/lx/implatform/entity/Friends.java
  27. 78
      im-platform/src/main/java/com/lx/implatform/entity/SingleMessage.java
  28. 77
      im-platform/src/main/java/com/lx/implatform/entity/User.java
  29. 35
      im-platform/src/main/java/com/lx/implatform/exception/GlobalException.java
  30. 98
      im-platform/src/main/java/com/lx/implatform/exception/GlobalExceptionHandler.java
  31. 16
      im-platform/src/main/java/com/lx/implatform/mapper/FriendsMapper.java
  32. 16
      im-platform/src/main/java/com/lx/implatform/mapper/SingleMessageMapper.java
  33. 16
      im-platform/src/main/java/com/lx/implatform/mapper/UserMapper.java
  34. 26
      im-platform/src/main/java/com/lx/implatform/service/IFriendsService.java
  35. 21
      im-platform/src/main/java/com/lx/implatform/service/ISingleMessageService.java
  36. 28
      im-platform/src/main/java/com/lx/implatform/service/IUserService.java
  37. 104
      im-platform/src/main/java/com/lx/implatform/service/impl/FriendsServiceImpl.java
  38. 43
      im-platform/src/main/java/com/lx/implatform/service/impl/SecurityUserDetailsServiceImpl.java
  39. 95
      im-platform/src/main/java/com/lx/implatform/service/impl/SingleMessageServiceImpl.java
  40. 95
      im-platform/src/main/java/com/lx/implatform/service/impl/UserServiceImpl.java
  41. 23
      im-platform/src/main/java/com/lx/implatform/session/SessionContext.java
  42. 11
      im-platform/src/main/java/com/lx/implatform/session/UserSession.java
  43. 60
      im-platform/src/main/java/com/lx/implatform/task/PullAlreadyReadMessageTask.java
  44. 22
      im-platform/src/main/java/com/lx/implatform/vo/FriendsVO.java
  45. 26
      im-platform/src/main/java/com/lx/implatform/vo/RegisterVO.java
  46. 29
      im-platform/src/main/java/com/lx/implatform/vo/SingleMessageVO.java
  47. 27
      im-platform/src/main/java/com/lx/implatform/vo/UnfriendsUserVO.java
  48. 27
      im-platform/src/main/java/com/lx/implatform/vo/UserVO.java
  49. 28
      im-platform/src/main/resources/application.yml
  50. 35
      im-platform/src/main/resources/db/db.sql
  51. 53
      im-server/pom.xml
  52. 32
      im-server/src/main/java/com/lx/implatform/imserver/IMServerApp.java
  53. 53
      im-server/src/main/java/com/lx/implatform/imserver/config/RedisConfig.java
  54. 8
      im-server/src/main/java/com/lx/implatform/imserver/constant/Constant.java
  55. 45
      im-server/src/main/java/com/lx/implatform/imserver/task/PullUnreadMessageTask.java
  56. 86
      im-server/src/main/java/com/lx/implatform/imserver/websocket/WebSocketHandler.java
  57. 31
      im-server/src/main/java/com/lx/implatform/imserver/websocket/WebsocketChannelCtxHloder.java
  58. 71
      im-server/src/main/java/com/lx/implatform/imserver/websocket/WebsocketServer.java
  59. 19
      im-server/src/main/java/com/lx/implatform/imserver/websocket/endecode/MessageProtocolDecoder.java
  60. 20
      im-server/src/main/java/com/lx/implatform/imserver/websocket/endecode/MessageProtocolEncoder.java
  61. 51
      im-server/src/main/java/com/lx/implatform/imserver/websocket/processor/HeartbeatProcessor.java
  62. 10
      im-server/src/main/java/com/lx/implatform/imserver/websocket/processor/MessageProcessor.java
  63. 23
      im-server/src/main/java/com/lx/implatform/imserver/websocket/processor/ProcessorFactory.java
  64. 45
      im-server/src/main/java/com/lx/implatform/imserver/websocket/processor/SingleMessageProcessor.java
  65. 11
      im-server/src/main/resources/application.yml
  66. 159
      pom.xml

3
.gitignore

@ -0,0 +1,3 @@
/.idea/

61
commom/pom.xml

@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>lx-im</artifactId>
<groupId>com.lx</groupId>
<version>${im.version}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>commom</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.3.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</dependency>
<!-- 引入redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity</artifactId>
<version>${velocity.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
</project>

10
commom/src/main/java/com/lx/common/contant/RedisKey.java

@ -0,0 +1,10 @@
package com.lx.common.contant;
public class RedisKey {
public final static String IM_USER_SERVER_ID = "im:user_server_id:";
public final static String IM_UNREAD_MESSAGE = "im:unread_msg:";
public final static String IM_ALREADY_READED_MESSAGE = "im:already_read_msg";
}

36
commom/src/main/java/com/lx/common/enums/MessageStatusEnum.java

@ -0,0 +1,36 @@
package com.lx.common.enums;
public enum MessageStatusEnum {
UNREAD(0,"未读"),
ALREADY_READ(1,"已读");
private Integer code;
private String desc;
MessageStatusEnum(Integer index, String desc) {
this.code =index;
this.desc=desc;
}
public static MessageStatusEnum fromCode(Integer code){
for (MessageStatusEnum typeEnum:values()) {
if (typeEnum.code.equals(code)) {
return typeEnum;
}
}
return null;
}
public String getDesc() {
return desc;
}
public Integer getCode(){
return this.code;
}
}

37
commom/src/main/java/com/lx/common/enums/MessageTypeEnum.java

@ -0,0 +1,37 @@
package com.lx.common.enums;
public enum MessageTypeEnum {
TEXT(0,"文字"),
FILE(1,"文件"),
IMAGE(2,"图片"),
VIDEO(3,"视频");
private Integer code;
private String desc;
MessageTypeEnum(Integer index, String desc) {
this.code =index;
this.desc=desc;
}
public static MessageTypeEnum fromCode(Integer code){
for (MessageTypeEnum typeEnum:values()) {
if (typeEnum.code.equals(code)) {
return typeEnum;
}
}
return null;
}
public String getDesc() {
return desc;
}
public Integer getCode(){
return this.code;
}
}

49
commom/src/main/java/com/lx/common/enums/ResultCode.java

@ -0,0 +1,49 @@
package com.lx.common.enums;
/**
* 响应码枚举
*
* @author Blue
* @date 2020/10/19
*
**/
public enum ResultCode {
SUCCESS(200,"成功"),
LOGIN_ERROR(400,"登录异常"),
NO_LOGIN(401,"未登录"),
FORBIDDEN(403,"禁止访问"),
NOT_FIND(404,"无法找到文件"),
PROGRAM_ERROR(500,"系统繁忙,请稍后再试"),
PASSWOR_ERROR(10001,"密码不正确"),
VERITY_CODE_NOT_EXIST(10002,"验证码不存在"),
VERITY_CODE_ERROR(10003,"验证码不正确"),
USERNAME_ALREADY_REGISTER(10004,"该用户名已注册"),
MOBILE_ALREADY_REGISTER(10005,"该手机号码已注册"),
;
private int code;
private String msg;
// 构造方法
ResultCode(int code, String msg) {
this.code = code;
this.msg = msg;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}

39
commom/src/main/java/com/lx/common/enums/WSCmdEnum.java

@ -0,0 +1,39 @@
package com.lx.common.enums;
public enum WSCmdEnum {
HEARTBEAT(0,"心跳"),
SINGLE_MESSAGE(1,"单发消息"),
GROUP_MESSAGE(2,"群发消息");
private Integer code;
private String desc;
WSCmdEnum(Integer index, String desc) {
this.code =index;
this.desc=desc;
}
public static WSCmdEnum fromCode(Integer code){
for (WSCmdEnum typeEnum:values()) {
if (typeEnum.code.equals(code)) {
return typeEnum;
}
}
return null;
}
public String getDesc() {
return desc;
}
public Integer getCode(){
return this.code;
}
}

102
commom/src/main/java/com/lx/common/generator/CodeGenerator.java

@ -0,0 +1,102 @@
package com.lx.common.generator;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.VelocityTemplateEngine;
import java.util.ArrayList;
import java.util.List;
public class CodeGenerator {
public static void main(String[] args) {
// 代码生成器
AutoGenerator mpg = new AutoGenerator();
// 全局配置
GlobalConfig gc = new GlobalConfig();
//生成的代码输出路径,自己根据需要修改
String projectPath = "d:\\work\\project\\code";
gc.setOutputDir(projectPath + "/src/main/java");
gc.setAuthor("blue");
gc.setOpen(false);
gc.setFileOverride(true);
gc.setActiveRecord(true);
gc.setBaseColumnList(true);
gc.setBaseResultMap(true);
gc.setIdType(IdType.AUTO);
gc.setDateType(DateType.ONLY_DATE);
mpg.setGlobalConfig(gc);
// 数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/simple-im?useUnicode=true&characterEncoding=utf-8");
dsc.setDriverName("com.mysql.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("root");
mpg.setDataSource(dsc);
// 包配置
PackageConfig pc = new PackageConfig();
pc.setModuleName("");
pc.setParent("com.lx");
mpg.setPackageInfo(pc);
// 如果模板引擎是 velocity
String templatePath = "/templates/mapper.xml.vm";
// 自定义输出配置
List<FileOutConfig> focList = new ArrayList<>();
// 自定义配置会被优先输出
focList.add(new FileOutConfig(templatePath) {
@Override
public String outputFile(TableInfo tableInfo) {
// 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!!
return projectPath + "/src/main/resources/mapper/"
+ tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
}
});
// 自定义配置
InjectionConfig cfg = new InjectionConfig() {
@Override
public void initMap() {
// to do nothing
}
};
cfg.setFileOutConfigList(focList);
mpg.setCfg(cfg);
// 配置模板
TemplateConfig templateConfig = new TemplateConfig();
templateConfig.setXml(null);
mpg.setTemplate(templateConfig);
// 策略配置
StrategyConfig strategy = new StrategyConfig();
// 下划线转驼峰
strategy.setNaming(NamingStrategy.underline_to_camel);
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
strategy.setEntityTableFieldAnnotationEnable(true);
strategy.setVersionFieldName("version");
//逻辑删除的字段
strategy.setLogicDeleteFieldName("deleted");
strategy.setEntityLombokModel(true);
strategy.setRestControllerStyle(true);
//多张表的时候直接在代码中写表名
strategy.setInclude("friends");
strategy.setTablePrefix("");
mpg.setStrategy(strategy);
mpg.setTemplateEngine(new VelocityTemplateEngine());
mpg.execute();
}
}

9
commom/src/main/java/com/lx/common/model/im/HeartbeatInfo.java

@ -0,0 +1,9 @@
package com.lx.common.model.im;
import lombok.Data;
@Data
public class HeartbeatInfo {
private long userId;
}

11
commom/src/main/java/com/lx/common/model/im/SendInfo.java

@ -0,0 +1,11 @@
package com.lx.common.model.im;
import lombok.Data;
@Data
public class SendInfo<T> {
private Integer cmd;
private T data;
}

21
commom/src/main/java/com/lx/common/model/im/SingleMessageInfo.java

@ -0,0 +1,21 @@
package com.lx.common.model.im;
import lombok.Data;
import java.util.Date;
@Data
public class SingleMessageInfo {
private long id;
private Long sendUserId;
private Long recvUserId;
private String content;
private Integer type;
private Date sendTime;
}

16
commom/src/main/java/com/lx/common/result/Result.java

@ -0,0 +1,16 @@
package com.lx.common.result;
import lombok.Data;
@Data
public class Result<T> {
private int code;
private String message;
private T data;
}

83
commom/src/main/java/com/lx/common/result/ResultUtils.java

@ -0,0 +1,83 @@
package com.lx.common.result;
import com.lx.common.enums.ResultCode;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ResultUtils {
public static final <T> Result<T> success(){
Result result=new Result();
result.setCode(ResultCode.SUCCESS.getCode());
result.setMessage(ResultCode.SUCCESS.getMsg());
return result;
}
public static final <T> Result<T> success(T data){
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
cachedThreadPool.submit(new Runnable() {
@Override
public void run() {
System.out.println("当前线程"+Thread.currentThread().getName());
}
});
Result result=new Result();
result.setCode(ResultCode.SUCCESS.getCode());
result.setMessage(ResultCode.SUCCESS.getMsg());
result.setData(data);
return result;
}
public static final <T> Result<T> success(T data, String messsage){
Result result=new Result();
result.setCode(ResultCode.SUCCESS.getCode());
result.setMessage(messsage);
result.setData(data);
return result;
}
public static final <T> Result<T> success(String messsage){
Result result=new Result();
result.setCode(ResultCode.SUCCESS.getCode());
result.setMessage(messsage);
return result;
}
public static final <T> Result<T> error(Integer code, String messsage){
Result result=new Result();
result.setCode(code);
result.setMessage(messsage);
return result;
}
public static final <T> Result<T> error(ResultCode resultCode, String messsage){
Result result=new Result();
result.setCode(resultCode.getCode());
result.setMessage(messsage);
return result;
}
public static final <T> Result<T> error(ResultCode resultCode, String messsage,T data){
Result result=new Result();
result.setCode(resultCode.getCode());
result.setMessage(messsage);
result.setData(data);
return result;
}
public static final <T> Result<T> error(ResultCode resultCode){
Result result=new Result();
result.setCode(resultCode.getCode());
result.setMessage(resultCode.getMsg());
return result;
}
}

166
commom/src/main/java/com/lx/common/util/BeanUtils.java

@ -0,0 +1,166 @@
package com.lx.common.util;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.util.ReflectionUtils;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class BeanUtils {
private static void handleReflectionException(Exception e) {
ReflectionUtils.handleReflectionException(e);
}
public static <T, U> List<U> copyProperties(List<T> sourceList, Class<U> clazz) {
if(sourceList == null || sourceList.size() <= 0) {
return new ArrayList<>();
}
List<U> result = new ArrayList<>();
for (T source : sourceList) {
result.add(copyProperties(source, clazz));
}
return result;
}
/**
* 如果source , 为空返回空对象
* @param sourceList
* @param clazz
* @return
*/
public static <T, U> List<U> copyPropertiesList(List<T> sourceList, Class<U> clazz) {
if(sourceList == null || sourceList.size() <= 0) {
return new ArrayList<U>();
}
List<U> result = new ArrayList<>();
U target = null;
for (T source : sourceList) {
try {
target = clazz.newInstance();
copyProperties(source, target);
}catch(Exception e) {
handleReflectionException(e);
return new ArrayList<>();
}
result.add(target);
}
return result;
}
/**
* source空为null
* @param orig
* @param destClass
* @param <T>
* @return
*/
public static <T> T copyProperties(Object orig, Class<T> destClass) {
try {
Object target = destClass.newInstance();
if(orig == null) {
return null;
}
copyProperties(orig, (Object)target);
return (T) target;
}catch(Exception e) {
handleReflectionException(e);
return null;
}
}
/**
* source 为null 返回 空对象
* @param orig
* @param destClass
* @param <T>
* @return
*/
public static <T> T copyProperty(Object orig, Class<T> destClass) {
try {
Object target = destClass.newInstance();
if(orig == null) {
return (T)target;
}
copyProperties(orig, (Object)target);
return (T) target;
}catch(Exception e) {
handleReflectionException(e);
Object o1 = new Object();
try {
o1 = destClass.newInstance();
}catch(Exception ex) {
handleReflectionException(e);
}
return (T)o1;
}
}
public static <T, U> List<U> copyProperties(List<T> sourceList, Class<U> clazz, String... ignoreProperties) {
if(sourceList == null || sourceList.size() <= 0) {
return new ArrayList<U>();
}
List<U> result = new ArrayList<>();
for (T source : sourceList) {
result.add(copyProperties(source, clazz, ignoreProperties));
}
return result;
}
public static <T> T copyProperties(Object orig, Class<T> destClass, String... ignoreProperties) {
if(orig == null) {
return null;
}
try {
Object target = destClass.newInstance();
org.springframework.beans.BeanUtils.copyProperties(orig, (Object)target, ignoreProperties);
return (T)target;
}catch(Exception e) {
return null;
}
}
public static String[] getNullPropertyNames(Object source) {
final BeanWrapper beanWrapper = new BeanWrapperImpl(source);
java.beans.PropertyDescriptor[] propDesc = beanWrapper.getPropertyDescriptors();
Set<String> emptynames = new HashSet<String>();
for(java.beans.PropertyDescriptor pd : propDesc) {
Object srcValue = beanWrapper.getPropertyValue(pd.getName());
if (srcValue == null) {emptynames.add(pd.getName());}
}
String[] result = new String[emptynames.size()];
return emptynames.toArray(result);
}
public static void copyProperties(Object orig, Object dest) {
try {
org.springframework.beans.BeanUtils.copyProperties(orig, dest);
} catch (Exception e) {
handleReflectionException(e);
}
}
}

1128
commom/src/main/java/com/lx/common/util/DateTimeUtils.java

File diff suppressed because it is too large

40
commom/src/main/java/com/lx/common/util/SpringContextHolder.java

@ -0,0 +1,40 @@
package com.lx.common.util;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class SpringContextHolder implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringContextHolder.applicationContext = applicationContext;
}
public static ApplicationContext getApplicationContext() {
assertApplicationContext();
return applicationContext;
}
public static <T> T getBean(String beanName) {
assertApplicationContext();
return (T) applicationContext.getBean(beanName);
}
public static <T> T getBean(Class<T> requiredType) {
assertApplicationContext();
return applicationContext.getBean(requiredType);
}
private static void assertApplicationContext() {
if (SpringContextHolder.applicationContext == null) {
throw new RuntimeException("applicaitonContext属性为null,请检查是否注入了SpringContextHolder!");
}
}
}

83
im-platform/pom.xml

@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>lx-im</artifactId>
<groupId>com.lx</groupId>
<version>${im.version}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>im-platform</artifactId>
<dependencies>
<dependency>
<groupId>com.lx</groupId>
<artifactId>commom</artifactId>
<version>${im.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger.version}</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${swagger.version}</version>
</dependency>
<!-- 引入redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

19
im-platform/src/main/java/com/lx/implatform/ImplatformApp.java

@ -0,0 +1,19 @@
package com.lx.implatform;
import lombok.extern.slf4j.Slf4j;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
@Slf4j
@MapperScan(basePackages = {"com.lx.implatform.mapper"})
@ComponentScan(basePackages={"com.lx"})
@SpringBootApplication
public class ImplatformApp {
public static void main(String[] args) {
SpringApplication.run(ImplatformApp.class);
}
}

106
im-platform/src/main/java/com/lx/implatform/config/RedisConfig.java

@ -0,0 +1,106 @@
package com.lx.implatform.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.CacheErrorHandler;
import org.springframework.cache.interceptor.CacheResolver;
import org.springframework.cache.interceptor.SimpleCacheErrorHandler;
import org.springframework.cache.interceptor.SimpleCacheResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import javax.annotation.Resource;
import java.time.Duration;
/**
* redis配置
* @author zsq
*/
@EnableCaching
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
@Resource
private RedisConnectionFactory factory;
/**
* 重写Redis序列化方式使用Json方式:
* 当我们的数据存储到Redis的时候我们的键key和值value都是通过Spring提供的Serializer序列化到数据库的RedisTemplate默认使用的是JdkSerializationRedisSerializerStringRedisTemplate默认使用的是StringRedisSerializer
* Spring Data JPA为我们提供了下面的Serializer
* GenericToStringSerializerJackson2JsonRedisSerializerJacksonJsonRedisSerializerJdkSerializationRedisSerializerOxmSerializerStringRedisSerializer
* 在此我们将自己配置RedisTemplate并定义Serializer
*
* @param redisConnectionFactory
* @return
*/
@Primary
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate();
redisTemplate.setConnectionFactory(redisConnectionFactory);
// 设置值(value)的序列化采用jackson2JsonRedisSerializer
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer());
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer());
// 设置键(key)的序列化采用StringRedisSerializer。
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
@Bean
public Jackson2JsonRedisSerializer jackson2JsonRedisSerializer(){
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// 解决jackson2无法反序列化LocalDateTime的问题
om.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
om.registerModule(new JavaTimeModule());
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
jackson2JsonRedisSerializer.setObjectMapper(om);
return jackson2JsonRedisSerializer;
}
@Bean
@Override
public CacheResolver cacheResolver() {
return new SimpleCacheResolver(cacheManager());
}
@Bean
@Override
public CacheErrorHandler errorHandler() {
// 用于捕获从Cache中进行CRUD时的异常的回调处理器。
return new SimpleCacheErrorHandler();
}
@Bean
@Override
public CacheManager cacheManager() {
RedisCacheConfiguration cacheConfiguration =
RedisCacheConfiguration.defaultCacheConfig()
.disableCachingNullValues()
.entryTtl(Duration.ofMinutes(15))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer()));
return RedisCacheManager.builder(factory).cacheDefaults(cacheConfiguration).build();
}
}

42
im-platform/src/main/java/com/lx/implatform/config/SwaggerConfig.java

@ -0,0 +1,42 @@
package com.lx.implatform.config;
import io.swagger.annotations.ApiOperation;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
//这里采用包含注解的方式来确定要显示的接口
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
//这里采用包扫描的方式来确定要显示的接口
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("uaa service Doc")
.description("用户授权服务Api文档")
.termsOfServiceUrl("http://www.baidu.com/")
.version("1.0")
.build();
}
}

180
im-platform/src/main/java/com/lx/implatform/config/WebSecurityConfg.java

@ -0,0 +1,180 @@
package com.lx.implatform.config;
import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.lx.common.enums.ResultCode;
import com.lx.common.result.Result;
import com.lx.common.result.ResultUtils;
import com.lx.implatform.service.IUserService;
import com.lx.implatform.session.UserSession;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.*;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import java.io.PrintWriter;
/*
* SpringSecurity安全框架配置
* @Author Blue
* @Date 2022/10/21
*/
@Slf4j
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class WebSecurityConfg extends WebSecurityConfigurerAdapter {
@Value("${web-ui.login-page}")
private String loginPage;
@Qualifier("securityUserDetailsServiceImpl")
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private IUserService userService;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/login","/logout","/register","/swagger-resources/**", "/webjars/**", "/v2/**", "/swagger-ui.html/**")
.permitAll()
.anyRequest() //任何其它请求
.authenticated() //都需要身份认证
.and()
//2、登录配置表单认证方式
.formLogin()
.loginPage(loginPage)//自定义登录页面的url
.usernameParameter("username")//设置登录账号参数,与表单参数一致
.passwordParameter("password")//设置登录密码参数,与表单参数一致
.loginProcessingUrl("/login")//配置默认登录入口
.successHandler(successHandler())
.failureHandler(failureHandler())
.and()
//3、注销
.logout()
.logoutUrl("/logout")
.logoutSuccessHandler(logoutHandler())
.permitAll()
.and()
//4、session管理
.sessionManagement()
.invalidSessionUrl(loginPage) //失效后跳转到登陆页面
.and()
//5、禁用跨站csrf攻击防御
.csrf()
.disable()
.exceptionHandling()
.authenticationEntryPoint(entryPoint());
}
@Bean
AuthenticationFailureHandler failureHandler(){
return (request, response, exception) -> {
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
Result result = ResultUtils.error(ResultCode.LOGIN_ERROR,exception.getMessage());
if (exception instanceof LockedException) {
result =ResultUtils.error(ResultCode.LOGIN_ERROR,"账户被锁定,请联系管理员!");
} else if (exception instanceof CredentialsExpiredException) {
result = ResultUtils.error(ResultCode.LOGIN_ERROR,"密码过期,请联系管理员!");
} else if (exception instanceof AccountExpiredException) {
result =ResultUtils.error(ResultCode.LOGIN_ERROR,"账户过期,请联系管理员!");
} else if (exception instanceof DisabledException) {
result = ResultUtils.error(ResultCode.LOGIN_ERROR,"账户被禁用,请联系管理员!");
} else if (exception instanceof BadCredentialsException) {
result =ResultUtils.error(ResultCode.LOGIN_ERROR,"用户名或者密码输入错误,请重新输入!");
}
out.write(new ObjectMapper().writeValueAsString(result));
out.flush();
out.close();
};
}
@Bean
AuthenticationSuccessHandler successHandler(){
return (request, response, authentication) -> {
// 响应
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
Result result = ResultUtils.success();
out.write(new ObjectMapper().writeValueAsString(result));
out.flush();
out.close();
};
}
@Bean
LogoutSuccessHandler logoutHandler(){
return (request, response, authentication) -> {
User useDetail = (User)authentication.getPrincipal();
String strJson = useDetail.getUsername();
UserSession userSession = JSON.parseObject(strJson,UserSession.class);
log.info("{}退出", userSession.getUserName());
// 响应
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
Result result = ResultUtils.success();
out.write(new ObjectMapper().writeValueAsString(result));
out.flush();
out.close();
};
}
@Bean
AuthenticationEntryPoint entryPoint(){
return (request, response, exception) -> {
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
Result result = ResultUtils.error(ResultCode.NO_LOGIN);
out.write(new ObjectMapper().writeValueAsString(result));
out.flush();
out.close();
};
}
@Bean
public PasswordEncoder passwordEncoder(){
// 使用BCrypt加密密码
return new BCryptPasswordEncoder();
}
/**
* 密码加密
* @param auth
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
}

58
im-platform/src/main/java/com/lx/implatform/controller/FriendsController.java

@ -0,0 +1,58 @@
package com.lx.implatform.controller;
import com.lx.common.result.Result;
import com.lx.common.result.ResultUtils;
import com.lx.common.util.BeanUtils;
import com.lx.implatform.entity.Friends;
import com.lx.implatform.service.IFriendsService;
import com.lx.implatform.session.SessionContext;
import com.lx.implatform.vo.FriendsVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.validation.constraints.NotEmpty;
import java.util.List;
import java.util.stream.Collectors;
@Api(tags = "好友相关API")
@RestController
@RequestMapping("/friends")
public class FriendsController {
@Autowired
private IFriendsService friendsService;
@GetMapping("/list")
@ApiOperation(value = "好友列表",notes="获取好友列表")
public Result findFriends(){
List<Friends> friendsList = friendsService.findFriendsByUserId(SessionContext.getSession().getId());
List<FriendsVO> vos = friendsList.stream().map(f->{
FriendsVO vo = BeanUtils.copyProperties(f,FriendsVO.class);
return vo;
}).collect(Collectors.toList());
return ResultUtils.success(vos);
}
@PostMapping("/add")
@ApiOperation(value = "添加好友",notes="双方建立好友关系")
public Result addFriends(@NotEmpty(message = "好友id不可为空") @RequestParam("friendId") Long friendId){
friendsService.addFriends(friendId);
return ResultUtils.success();
}
@DeleteMapping("/delete")
@ApiOperation(value = "删除好友",notes="解除好友关系")
public Result delFriends(@NotEmpty(message = "好友id不可为空") @RequestParam("friendId") Long friendId){
friendsService.delFriends(friendId);
return ResultUtils.success();
}
}

32
im-platform/src/main/java/com/lx/implatform/controller/RegisterController.java

@ -0,0 +1,32 @@
package com.lx.implatform.controller;
import com.lx.common.result.Result;
import com.lx.common.result.ResultUtils;
import com.lx.implatform.service.IUserService;
import com.lx.implatform.vo.RegisterVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
@Api(tags = "用户注册")
@RestController
public class RegisterController {
@Autowired
private IUserService userService;
@PostMapping("/register")
@ApiOperation(value = "用户注册",notes="用户注册")
public Result register(@Valid @RequestBody RegisterVO dto){
userService.register(dto);
return ResultUtils.success();
}
}

40
im-platform/src/main/java/com/lx/implatform/controller/SingleMessageController.java

@ -0,0 +1,40 @@
package com.lx.implatform.controller;
import com.lx.common.result.Result;
import com.lx.common.result.ResultUtils;
import com.lx.implatform.service.ISingleMessageService;
import com.lx.implatform.vo.SingleMessageVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
@Api(tags = "单发消息相关api")
@RestController
@RequestMapping("/message/single")
public class SingleMessageController {
@Autowired
private ISingleMessageService singleMessageService;
@PostMapping("/send")
@ApiOperation(value = "发送消息",notes="发送单人消息")
public Result register(@Valid @RequestBody SingleMessageVO vo){
singleMessageService.sendMessage(vo);
return ResultUtils.success();
}
@PostMapping("/pullUnreadMessage")
@ApiOperation(value = "拉取未读消息",notes="拉取未读消息")
public Result pullUnreadMessage(){
singleMessageService.pullUnreadMessage();
return ResultUtils.success();
}
}

59
im-platform/src/main/java/com/lx/implatform/controller/UserController.java

@ -0,0 +1,59 @@
package com.lx.implatform.controller;
import com.lx.common.result.Result;
import com.lx.common.result.ResultUtils;
import com.lx.common.util.BeanUtils;
import com.lx.implatform.entity.User;
import com.lx.implatform.service.IUserService;
import com.lx.implatform.session.SessionContext;
import com.lx.implatform.session.UserSession;
import com.lx.implatform.vo.UserVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.validation.constraints.NotEmpty;
import java.util.List;
@Api(tags = "用户相关API")
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private IUserService userService;
@GetMapping("/online")
public Result checkOnline(@NotEmpty @RequestParam("userIds") String userIds){
List<Long> onlineIds = userService.checkOnline(userIds);
return ResultUtils.success(onlineIds);
}
@GetMapping("/self")
public Result findSelfInfo(){
UserSession session = SessionContext.getSession();
User user = userService.getById(session.getId());
UserVO userVO = BeanUtils.copyProperties(user,UserVO.class);
return ResultUtils.success(userVO);
}
@GetMapping("/find/{id}")
public Result findByIde(@NotEmpty @PathVariable("id") long id){
User user = userService.getById(id);
UserVO userVO = BeanUtils.copyProperties(user,UserVO.class);
return ResultUtils.success(userVO);
}
@GetMapping("/findByNickName")
@ApiOperation(value = "查找非好友用户",notes="查找非好友用户")
public Result findUnfriendsUser(@NotEmpty(message = "用户昵称不可为空") @RequestParam("nickName") String nickName){
return ResultUtils.success( userService.findUserByNickName(nickName));
}
}

71
im-platform/src/main/java/com/lx/implatform/entity/Friends.java

@ -0,0 +1,71 @@
package com.lx.implatform.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.util.Date;
/**
* <p>
*
* </p>
*
* @author blue
* @since 2022-10-22
*/
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("friends")
public class Friends extends Model<Friends> {
private static final long serialVersionUID=1L;
/**
* id
*/
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/**
* 用户id
*/
@TableField("user_id")
private Long userId;
/**
* 好友id
*/
@TableField("friend_id")
private Long friendId;
/**
* 用户昵称
*/
@TableField("friend_nick_name")
private String friendNickName;
/**
* 用户头像
*/
@TableField("friend_head_image")
private String friendHeadImage;
/**
* 创建时间
*/
@TableField("created_time")
private Date createdTime;
@Override
protected Serializable pkVal() {
return this.id;
}
}

78
im-platform/src/main/java/com/lx/implatform/entity/SingleMessage.java

@ -0,0 +1,78 @@
package com.lx.implatform.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.util.Date;
/**
* <p>
*
* </p>
*
* @author blue
* @since 2022-10-01
*/
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("single_message")
public class SingleMessage extends Model<SingleMessage> {
private static final long serialVersionUID=1L;
/**
* id
*/
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/**
* 发送用户id
*/
@TableField("send_user_id")
private Long sendUserId;
/**
* 接收用户id
*/
@TableField("recv_user_id")
private Long recvUserId;
/**
* 发送内容
*/
@TableField("content")
private String content;
/**
* 消息类型
*/
@TableField("type")
private Integer type;
/**
* 状态
*/
@TableField("status")
private Integer status;
/**
* 发送时间
*/
@TableField("send_time")
private Date sendTime;
@Override
protected Serializable pkVal() {
return this.id;
}
}

77
im-platform/src/main/java/com/lx/implatform/entity/User.java

@ -0,0 +1,77 @@
package com.lx.implatform.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.util.Date;
/**
* <p>
*
* </p>
*
* @author blue
* @since 2022-10-01
*/
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("user")
public class User extends Model<User> {
private static final long serialVersionUID=1L;
/**
* id
*/
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/**
* 用户名
*/
@TableField("user_name")
private String userName;
/**
* 用户名
*/
@TableField("nick_name")
private String nickName;
/**
* 用户名
*/
@TableField("head_image")
private String headImage;
/**
* 密码(明文)
*/
@TableField("password")
private String password;
/**
* 最后登录时间
*/
@TableField("last_login_time")
private Date lastLoginTime;
/**
* 创建时间
*/
@TableField("created_time")
private Date createdTime;
@Override
protected Serializable pkVal() {
return this.id;
}
}

35
im-platform/src/main/java/com/lx/implatform/exception/GlobalException.java

@ -0,0 +1,35 @@
package com.lx.implatform.exception;
import com.lx.common.enums.ResultCode;
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(ResultCode resultCode, String message){
this.code = resultCode.getCode();
this.message=message;
}
public GlobalException(ResultCode resultCode) {
this.code = resultCode.getCode();
this.message = resultCode.getMsg();
}
public GlobalException(String message){
this.code= ResultCode.PROGRAM_ERROR.getCode();
this.message=message;
}
}

98
im-platform/src/main/java/com/lx/implatform/exception/GlobalExceptionHandler.java

@ -0,0 +1,98 @@
package com.lx.implatform.exception;
import cn.hutool.json.JSONException;
import com.lx.common.enums.ResultCode;
import com.lx.common.result.Result;
import com.lx.common.result.ResultUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
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);
return ResultUtils.error(ex.getCode(), ex.getMessage());
}
else if(e instanceof UndeclaredThrowableException) {
GlobalException ex = (GlobalException) e.getCause();
log.error("全局异常捕获:msg:{},log:{},{}" , ex.getMessage(), e);
return ResultUtils.error(ex.getCode(), ex.getMessage());
}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());
Throwable t = e.getCause();
if(t instanceof JSONException){
t = t.getCause();
if(t instanceof DateTimeParseException){
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})
@ResponseStatus(HttpStatus.OK)
public Result handleValidationExceptionHandler(MethodArgumentNotValidException exception) {
BindingResult bindResult = exception.getBindingResult();
String msg;
if (bindResult != null && bindResult.hasErrors()) {
msg = bindResult.getAllErrors().get(0).getDefaultMessage();
if (msg.contains("NumberFormatException")) {
msg = "参数类型错误!";
}
}else {
msg = "系统繁忙,请稍后重试...";
}
return ResultUtils.error(ResultCode.PROGRAM_ERROR,msg);
}
@ExceptionHandler(BindException.class)
@ResponseStatus(HttpStatus.OK)
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);
}
}

16
im-platform/src/main/java/com/lx/implatform/mapper/FriendsMapper.java

@ -0,0 +1,16 @@
package com.lx.implatform.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.lx.implatform.entity.Friends;
/**
* <p>
* Mapper 接口
* </p>
*
* @author blue
* @since 2022-10-22
*/
public interface FriendsMapper extends BaseMapper<Friends> {
}

16
im-platform/src/main/java/com/lx/implatform/mapper/SingleMessageMapper.java

@ -0,0 +1,16 @@
package com.lx.implatform.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.lx.implatform.entity.SingleMessage;
/**
* <p>
* Mapper 接口
* </p>
*
* @author blue
* @since 2022-10-01
*/
public interface SingleMessageMapper extends BaseMapper<SingleMessage> {
}

16
im-platform/src/main/java/com/lx/implatform/mapper/UserMapper.java

@ -0,0 +1,16 @@
package com.lx.implatform.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.lx.implatform.entity.User;
/**
* <p>
* Mapper 接口
* </p>
*
* @author blue
* @since 2022-10-01
*/
public interface UserMapper extends BaseMapper<User> {
}

26
im-platform/src/main/java/com/lx/implatform/service/IFriendsService.java

@ -0,0 +1,26 @@
package com.lx.implatform.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.lx.implatform.entity.Friends;
import java.util.List;
/**
* <p>
* 服务类
* </p>
*
* @author blue
* @since 2022-10-22
*/
public interface IFriendsService extends IService<Friends> {
Boolean isFriends(Long userId1,long userId2);
List<Friends> findFriendsByUserId(long UserId);
void addFriends(long friendId);
void delFriends(long friendId);
}

21
im-platform/src/main/java/com/lx/implatform/service/ISingleMessageService.java

@ -0,0 +1,21 @@
package com.lx.implatform.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.lx.implatform.entity.SingleMessage;
import com.lx.implatform.vo.SingleMessageVO;
/**
* <p>
* 服务类
* </p>
*
* @author blue
* @since 2022-10-01
*/
public interface ISingleMessageService extends IService<SingleMessage> {
void sendMessage(SingleMessageVO vo);
void pullUnreadMessage();
}

28
im-platform/src/main/java/com/lx/implatform/service/IUserService.java

@ -0,0 +1,28 @@
package com.lx.implatform.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.lx.implatform.entity.User;
import com.lx.implatform.vo.RegisterVO;
import com.lx.implatform.vo.UserVO;
import java.util.List;
/**
* <p>
* 用户服务类
* </p>
*
* @author blue
* @since 2022-10-01
*/
public interface IUserService extends IService<User> {
void register(RegisterVO registerDTO);
User findUserByName(String username);
List<UserVO> findUserByNickName(String nickname);
List<Long> checkOnline(String userIds);
}

104
im-platform/src/main/java/com/lx/implatform/service/impl/FriendsServiceImpl.java

@ -0,0 +1,104 @@
package com.lx.implatform.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lx.common.enums.ResultCode;
import com.lx.implatform.entity.Friends;
import com.lx.implatform.entity.User;
import com.lx.implatform.exception.GlobalException;
import com.lx.implatform.mapper.FriendsMapper;
import com.lx.implatform.service.IFriendsService;
import com.lx.implatform.service.IUserService;
import com.lx.implatform.session.SessionContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* <p>
* 好友服务实现类
* </p>
*
* @author blue
* @since 2022-10-22
*/
@Service
public class FriendsServiceImpl extends ServiceImpl<FriendsMapper, Friends> implements IFriendsService {
@Autowired
private IUserService userService;
@Override
public List<Friends> findFriendsByUserId(long UserId) {
QueryWrapper<Friends> queryWrapper = new QueryWrapper<>();
queryWrapper.lambda().eq(Friends::getUserId,UserId);
List<Friends> friendsList = this.list(queryWrapper);
return friendsList;
}
@Transactional
@Override
public void addFriends(long friendId) {
long userId = SessionContext.getSession().getId();
if(userId == friendId){
throw new GlobalException(ResultCode.PROGRAM_ERROR,"不允许添加自己为好友");
}
// 互相绑定好友关系
bindFriends(userId,friendId);
bindFriends(friendId,userId);
}
@Transactional
@Override
public void delFriends(long friendId) {
long userId = SessionContext.getSession().getId();
// 互相解除好友关系
unbindFriends(userId,friendId);
unbindFriends(friendId,userId);
}
@Override
public Boolean isFriends(Long userId1, long userId2) {
QueryWrapper<Friends> queryWrapper = new QueryWrapper<>();
queryWrapper.lambda()
.eq(Friends::getUserId,userId1)
.eq(Friends::getFriendId,userId2);
return this.count(queryWrapper) > 0;
}
private void bindFriends(long userId, long friendsId) {
QueryWrapper<Friends> queryWrapper = new QueryWrapper<>();
queryWrapper.lambda()
.eq(Friends::getUserId,userId)
.eq(Friends::getFriendId,friendsId);
if(this.count(queryWrapper)==0){
Friends friends = new Friends();
friends.setUserId(userId);
friends.setFriendId(friendsId);
User friendsInfo = userService.getById(friendsId);
friends.setFriendHeadImage(friendsInfo.getHeadImage());
friends.setFriendNickName(friendsInfo.getNickName());
this.save(friends);
}
}
private void unbindFriends(long userId, long friendsId) {
QueryWrapper<Friends> queryWrapper = new QueryWrapper<>();
queryWrapper.lambda()
.eq(Friends::getUserId,userId)
.eq(Friends::getFriendId,friendsId);
List<Friends> friendsList = this.list(queryWrapper);
friendsList.stream().forEach(friends -> {
this.removeById(friends.getId());
});
}
}

43
im-platform/src/main/java/com/lx/implatform/service/impl/SecurityUserDetailsServiceImpl.java

@ -0,0 +1,43 @@
package com.lx.implatform.service.impl;
import com.alibaba.fastjson.JSON;
import com.lx.common.util.BeanUtils;
import com.lx.implatform.entity.User;
import com.lx.implatform.service.IUserService;
import com.lx.implatform.session.UserSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class SecurityUserDetailsServiceImpl implements UserDetailsService {
@Autowired
private IUserService userService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userService.findUserByName(username);
if(user == null) {
throw new UsernameNotFoundException("用户不存在");
}
//定义权限列表.
List<GrantedAuthority> authorities = new ArrayList<>();
// 用户可以访问的资源名称(或者说用户所拥有的权限) 注意:必须"ROLE_"开头
authorities.add(new SimpleGrantedAuthority("ROLE_XX"));
UserSession session = BeanUtils.copyProperties(user,UserSession.class);
String strJson = JSON.toJSONString(session);
UserDetails userDetails = new org.springframework.security.core.userdetails.User(strJson,user.getPassword(),authorities);
return userDetails;
}
}

95
im-platform/src/main/java/com/lx/implatform/service/impl/SingleMessageServiceImpl.java

@ -0,0 +1,95 @@
package com.lx.implatform.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lx.common.contant.RedisKey;
import com.lx.common.enums.MessageStatusEnum;
import com.lx.common.enums.ResultCode;
import com.lx.common.model.im.SingleMessageInfo;
import com.lx.common.util.BeanUtils;
import com.lx.implatform.entity.SingleMessage;
import com.lx.implatform.exception.GlobalException;
import com.lx.implatform.mapper.SingleMessageMapper;
import com.lx.implatform.service.IFriendsService;
import com.lx.implatform.service.ISingleMessageService;
import com.lx.implatform.session.SessionContext;
import com.lx.implatform.vo.SingleMessageVO;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
/**
* <p>
* 单人消息服务实现类
* </p>
*
* @author blue
* @since 2022-10-01
*/
@Service
public class SingleMessageServiceImpl extends ServiceImpl<SingleMessageMapper, SingleMessage> implements ISingleMessageService {
@Autowired
private IFriendsService friendsService;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Override
public void sendMessage(SingleMessageVO vo) {
Long userId = SessionContext.getSession().getId();
Boolean isFriends = friendsService.isFriends(userId,vo.getRecvUserId());
if(!isFriends){
throw new GlobalException(ResultCode.PROGRAM_ERROR,"您已不是对方好友,无法发送消息");
}
// 保存消息
SingleMessage msg = BeanUtils.copyProperties(vo,SingleMessage.class);
msg.setSendUserId(userId);
msg.setStatus(MessageStatusEnum.UNREAD.getCode());
msg.setSendTime(new Date());
this.save(msg);
// 获取对方连接的channelId
String key = RedisKey.IM_USER_SERVER_ID+msg.getRecvUserId();
String serverId = (String)redisTemplate.opsForValue().get(key);
// 如果对方在线,将数据存储至redis,等待拉取推送
if(!StringUtils.isEmpty(serverId)){
String sendKey = RedisKey.IM_UNREAD_MESSAGE + serverId;
SingleMessageInfo msgInfo = BeanUtils.copyProperties(msg,SingleMessageInfo.class);
redisTemplate.opsForList().rightPush(sendKey,msgInfo);
}
}
@Override
public void pullUnreadMessage() {
// 获取当前连接的channelId
Long userId = SessionContext.getSession().getId();
String key = RedisKey.IM_USER_SERVER_ID+userId;
String serverId = (String)redisTemplate.opsForValue().get(key);
if(StringUtils.isEmpty(serverId)){
throw new GlobalException(ResultCode.PROGRAM_ERROR,"用户未建立连接");
}
// 获取当前用户所有未读消息
QueryWrapper<SingleMessage> queryWrapper = new QueryWrapper<>();
queryWrapper.lambda().eq(SingleMessage::getRecvUserId,userId)
.eq(SingleMessage::getStatus,MessageStatusEnum.UNREAD);
List<SingleMessage> messages = this.list(queryWrapper);
// 上传至redis,等待推送
if(!messages.isEmpty()){
List<SingleMessageInfo> infos = messages.stream().map(m->{
SingleMessageInfo msgInfo = BeanUtils.copyProperties(m,SingleMessageInfo.class);
return msgInfo;
}).collect(Collectors.toList());
String sendKey = RedisKey.IM_UNREAD_MESSAGE + serverId;
redisTemplate.opsForList().rightPushAll(sendKey,infos.toArray());
}
}
}

95
im-platform/src/main/java/com/lx/implatform/service/impl/UserServiceImpl.java

@ -0,0 +1,95 @@
package com.lx.implatform.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lx.common.contant.RedisKey;
import com.lx.common.enums.ResultCode;
import com.lx.common.util.BeanUtils;
import com.lx.implatform.entity.User;
import com.lx.implatform.exception.GlobalException;
import com.lx.implatform.mapper.UserMapper;
import com.lx.implatform.service.IUserService;
import com.lx.implatform.vo.RegisterVO;
import com.lx.implatform.vo.UserVO;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;
/**
* <p>
* 用户服务实现类
* </p>
*
* @author blue
* @since 2022-10-01
*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
@Autowired
RedisTemplate<String,Object> redisTemplate;
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public void register(RegisterVO vo) {
User user = findUserByName(vo.getUserName());
if(null != user){
throw new GlobalException(ResultCode.USERNAME_ALREADY_REGISTER);
}
user = BeanUtils.copyProperties(vo,User.class);
user.setPassword(passwordEncoder.encode(user.getPassword()));
this.save(user);
}
@Override
public User findUserByName(String username) {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.lambda().eq(User::getUserName,username);
return this.getOne(queryWrapper);
}
@Override
public List<UserVO> findUserByNickName(String nickname) {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.lambda()
.like(User::getNickName,nickname)
.last("limit 10");
List<User> users = this.list(queryWrapper);
List<UserVO> vos = users.stream().map(u-> {
UserVO vo = BeanUtils.copyProperties(u,UserVO.class);
vo.setOnline(isOnline(u.getId()));
return vo;
}).collect(Collectors.toList());
return vos;
}
@Override
public List<Long> checkOnline(String userIds) {
String[] idArr = userIds.split(",");
List<Long> onlineIds = new LinkedList<>();
for(String userId:idArr){
if(isOnline(Long.parseLong(userId))){
onlineIds.add(Long.parseLong(userId));
}
}
return onlineIds;
}
private boolean isOnline(Long userId){
String key = RedisKey.IM_USER_SERVER_ID + userId;
String serverId = (String) redisTemplate.opsForValue().get(key);
return StringUtils.isNotEmpty(serverId);
}
}

23
im-platform/src/main/java/com/lx/implatform/session/SessionContext.java

@ -0,0 +1,23 @@
package com.lx.implatform.session;
import com.alibaba.fastjson.JSON;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
/*
* @Description
* @Author Blue
* @Date 2022/10/21
*/
public class SessionContext {
public static UserSession getSession(){
User useDetail = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
String strJson = useDetail.getUsername();
UserSession userSession = JSON.parseObject(strJson,UserSession.class);
return userSession;
}
}

11
im-platform/src/main/java/com/lx/implatform/session/UserSession.java

@ -0,0 +1,11 @@
package com.lx.implatform.session;
import lombok.Data;
@Data
public class UserSession {
private Long id;
private String userName;
private String nickName;
}

60
im-platform/src/main/java/com/lx/implatform/task/PullAlreadyReadMessageTask.java

@ -0,0 +1,60 @@
package com.lx.implatform.task;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.lx.common.contant.RedisKey;
import com.lx.common.enums.MessageStatusEnum;
import com.lx.implatform.entity.SingleMessage;
import com.lx.implatform.service.ISingleMessageService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
@Slf4j
@Component
public class PullAlreadyReadMessageTask {
private int threadNum = 8;
private ExecutorService executorService = Executors.newFixedThreadPool(threadNum);
@Autowired
private RedisTemplate<String,Object> redisTemplate;
@Autowired
private ISingleMessageService singleMessageService;
@PostConstruct
public void init(){
for(int i=0;i<threadNum;i++){
executorService.submit(new Task());
}
}
protected class Task implements Runnable{
@Override
public void run() {
try {
String key = RedisKey.IM_ALREADY_READED_MESSAGE;
Integer msgId = (Integer)redisTemplate.opsForList().leftPop(key,1, TimeUnit.SECONDS);
if(msgId!=null){
UpdateWrapper<SingleMessage> updateWrapper = new UpdateWrapper<>();
updateWrapper.lambda().eq(SingleMessage::getId,msgId)
.set(SingleMessage::getStatus, MessageStatusEnum.ALREADY_READ.getCode());
singleMessageService.update(updateWrapper);
log.info("消息已读,id:{}",msgId);
}
}catch (Exception e){
log.error(e.getMessage());
}finally {
// 下一次循环
executorService.submit(this);
}
}
}
}

22
im-platform/src/main/java/com/lx/implatform/vo/FriendsVO.java

@ -0,0 +1,22 @@
package com.lx.implatform.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel("好友信息VO")
public class FriendsVO {
@ApiModelProperty(value = "好友id")
private Long friendId;
@ApiModelProperty(value = "用户昵称")
private String friendNickName;
@ApiModelProperty(value = "用户头像")
private String friendHeadImage;
}

26
im-platform/src/main/java/com/lx/implatform/vo/RegisterVO.java

@ -0,0 +1,26 @@
package com.lx.implatform.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
@Data
@ApiModel("用户注册VO")
public class RegisterVO {
@NotEmpty(message="用户名不可为空")
@ApiModelProperty(value = "用户名")
private String userName;
@NotEmpty(message="用户密码不可为空")
@ApiModelProperty(value = "用户密码")
private String password;
@NotEmpty(message="用户昵称不可为空")
@ApiModelProperty(value = "用户昵称")
private String nickName;
}

29
im-platform/src/main/java/com/lx/implatform/vo/SingleMessageVO.java

@ -0,0 +1,29 @@
package com.lx.implatform.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
@Data
@ApiModel("单发消息VO")
public class SingleMessageVO {
@NotNull(message="接收用户id不可为空")
@ApiModelProperty(value = "接收用户id")
private Long recvUserId;
@NotEmpty(message="发送内容不可为空")
@ApiModelProperty(value = "发送内容")
private String content;
@NotNull(message="发送内容不可为空")
@ApiModelProperty(value = "消息类型")
private Integer type;
}

27
im-platform/src/main/java/com/lx/implatform/vo/UnfriendsUserVO.java

@ -0,0 +1,27 @@
package com.lx.implatform.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel("非好友用户信息VO")
public class UnfriendsUserVO {
@ApiModelProperty(value = "id")
private Long id;
@ApiModelProperty(value = "用户名")
private String userName;
@ApiModelProperty(value = "用户昵称")
private String nickName;
@ApiModelProperty(value = "头像")
private String headImage;
@ApiModelProperty(value = "是否已是好友")
private Boolean isFriend;
}

27
im-platform/src/main/java/com/lx/implatform/vo/UserVO.java

@ -0,0 +1,27 @@
package com.lx.implatform.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel("用户信息VO")
public class UserVO {
@ApiModelProperty(value = "id")
private Long id;
@ApiModelProperty(value = "用户名")
private String userName;
@ApiModelProperty(value = "用户昵称")
private String nickName;
@ApiModelProperty(value = "头像")
private String headImage;
@ApiModelProperty(value = "是否在线")
private Boolean online;
}

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

@ -0,0 +1,28 @@
#这是配置服务的端口
server:
port: 8888
#配置项目的数据源
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/lx-im?useUnicode=true&characterEncoding=utf-8
username: root
password: root
redis:
host: 127.0.0.1
port: 6379
database: 1
mybatis-plus:
configuration:
# 是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN(下划线命名) 到经典 Java 属性名 aColumn(驼峰命名) 的类似映射
map-underscore-to-camel-case: false
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# mapper
mapper-locations:
# *.xml的具体路径
- classpath*:mapper/*.xml
web-ui:
login-page: http://localhost:8080

35
im-platform/src/main/resources/db/db.sql

@ -0,0 +1,35 @@
use `lx-im`;
create table `user`(
`id` bigint not null auto_increment primary key comment 'id',
`user_name` varchar(255) not null comment '用户名',
`nick_name` varchar(255) not null comment '用户昵称',
`head_image` varchar(255) default '' comment '用户头像',
`password` varchar(255) not null comment '密码(明文)',
`last_login_time` datetime DEFAULT null comment '最后登录时间',
`created_time` datetime DEFAULT CURRENT_TIMESTAMP comment '创建时间',
unique key `idx_user_name`(user_name),
key `idx_nick_name`(nick_name)
);
create table `friends`(
`id` bigint not null auto_increment primary key comment 'id',
`user_id` bigint not null comment '用户id',
`friend_id` bigint not null comment '好友id',
`friend_nick_name` varchar(255) not null comment '用户昵称',
`friend_head_image` varchar(255) default '' comment '用户头像',
`created_time` datetime DEFAULT CURRENT_TIMESTAMP comment '创建时间',
key `idx_user_id` (`user_id`),
key `idx_friend_id` (`friend_id`)
);
create table `single_message`(
`id` bigint not null auto_increment primary key comment 'id',
`send_user_id` bigint not null comment '发送用户id',
`recv_user_id` bigint not null comment '接收用户id',
`content` text comment '发送内容',
`type` tinyint(1) NOT NULL comment '消息类型 0:文字 1:图片 2:文件',
`status` tinyint(1) NOT NULL comment '状态 0:未读 1:已读 ',
`send_time` datetime DEFAULT CURRENT_TIMESTAMP comment '发送时间',
key `idx_send_recv_user_id` (`send_user_id`,`recv_user_id`)
);

53
im-server/pom.xml

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>lx-im</artifactId>
<groupId>com.lx</groupId>
<version>${im.version}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>im-server</artifactId>
<dependencies>
<dependency>
<groupId>com.lx</groupId>
<artifactId>commom</artifactId>
<version>${im.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.42.Final</version>
</dependency>
<!-- 引入redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

32
im-server/src/main/java/com/lx/implatform/imserver/IMServerApp.java

@ -0,0 +1,32 @@
package com.lx.implatform.imserver;
import com.lx.implatform.imserver.websocket.WebsocketServer;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
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.lx"})
@SpringBootApplication
public class IMServerApp implements CommandLineRunner {
@Value("${websocket.port}")
private int port;
public static void main(String[] args) {
SpringApplication.run(IMServerApp.class);
}
public void run(String... args) throws Exception {
new WebsocketServer().start(port);
}
}

53
im-server/src/main/java/com/lx/implatform/imserver/config/RedisConfig.java

@ -0,0 +1,53 @@
package com.lx.implatform.imserver.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import javax.annotation.Resource;
@Configuration
public class RedisConfig {
@Resource
private RedisConnectionFactory factory;
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate();
redisTemplate.setConnectionFactory(redisConnectionFactory);
// 设置值(value)的序列化采用jackson2JsonRedisSerializer
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer());
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer());
// 设置键(key)的序列化采用StringRedisSerializer。
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
@Bean
public Jackson2JsonRedisSerializer jackson2JsonRedisSerializer(){
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// 解决jackson2无法反序列化LocalDateTime的问题
om.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
om.registerModule(new JavaTimeModule());
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
jackson2JsonRedisSerializer.setObjectMapper(om);
return jackson2JsonRedisSerializer;
}
}

8
im-server/src/main/java/com/lx/implatform/imserver/constant/Constant.java

@ -0,0 +1,8 @@
package com.lx.implatform.imserver.constant;
public class Constant {
// public static String LOCAL_SERVER_ID = UUID.randomUUID().toString();
}

45
im-server/src/main/java/com/lx/implatform/imserver/task/PullUnreadMessageTask.java

@ -0,0 +1,45 @@
package com.lx.implatform.imserver.task;
import com.lx.common.contant.RedisKey;
import com.lx.common.enums.WSCmdEnum;
import com.lx.common.model.im.SingleMessageInfo;
import com.lx.implatform.imserver.websocket.WebsocketChannelCtxHloder;
import com.lx.implatform.imserver.websocket.WebsocketServer;
import com.lx.implatform.imserver.websocket.processor.MessageProcessor;
import com.lx.implatform.imserver.websocket.processor.ProcessorFactory;
import io.netty.channel.ChannelHandlerContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.util.List;
@Slf4j
@Component
public class PullUnreadMessageTask {
@Autowired
private RedisTemplate<String,Object> redisTemplate;
@Scheduled(fixedRate=100)
public void pullUnreadMessage() {
// 从redis拉取未读消息
String key = RedisKey.IM_UNREAD_MESSAGE + WebsocketServer.LOCAL_SERVER_ID;
List messageInfos = redisTemplate.opsForList().range(key,0,-1);
for(Object o: messageInfos){
redisTemplate.opsForList().leftPop(key);
SingleMessageInfo messageInfo = (SingleMessageInfo)o;
ChannelHandlerContext ctx = WebsocketChannelCtxHloder.getChannelCtx(messageInfo.getRecvUserId());
if(ctx != null){
MessageProcessor processor = ProcessorFactory.createProcessor(WSCmdEnum.SINGLE_MESSAGE);
processor.process(ctx,messageInfo);
}
}
}
}

86
im-server/src/main/java/com/lx/implatform/imserver/websocket/WebSocketHandler.java

@ -0,0 +1,86 @@
package com.lx.implatform.imserver.websocket;
import com.lx.common.contant.RedisKey;
import com.lx.common.enums.WSCmdEnum;
import com.lx.common.model.im.SendInfo;
import com.lx.common.util.SpringContextHolder;
import com.lx.implatform.imserver.websocket.processor.MessageProcessor;
import com.lx.implatform.imserver.websocket.processor.ProcessorFactory;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.AttributeKey;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
/**
* WebSocket 长连接下 文本帧的处理器
* 实现浏览器发送文本回写
* 浏览器连接状态监控
*/
@Slf4j
public class WebSocketHandler extends SimpleChannelInboundHandler<SendInfo> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, SendInfo sendInfo) throws Exception {
// 创建处理器进行处理
MessageProcessor processor = ProcessorFactory.createProcessor(WSCmdEnum.fromCode(sendInfo.getCmd()));
processor.process(ctx,processor.transform(sendInfo.getData()));
}
/**
* 出现异常的处理 打印报错日志
*
* @param ctx the ctx
* @param cause the cause
* @throws Exception the Exception
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
log.error(cause.getMessage());
//关闭上下文
//ctx.close();
}
/**
* 监控浏览器上线
*
* @param ctx the ctx
* @throws Exception the Exception
*/
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
log.info(ctx.channel().id().asLongText() + "连接");
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
AttributeKey<Long> attr = AttributeKey.valueOf("USER_ID");
Long userId = ctx.channel().attr(attr).get();
// 移除channel
WebsocketChannelCtxHloder.removeChannelCtx(userId);
// 用户下线
RedisTemplate redisTemplate = SpringContextHolder.getBean("redisTemplate");
String key = RedisKey.IM_USER_SERVER_ID + userId;
redisTemplate.delete(key);
log.info(ctx.channel().id().asLongText() + "断开连接");
}
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof IdleStateEvent) {
IdleState state = ((IdleStateEvent) evt).state();
if (state == IdleState.READER_IDLE) {
// 在规定时间内没有收到客户端的上行数据, 主动断开连接
ctx.channel().close();
}
} else {
super.userEventTriggered(ctx, evt);
}
}
}

31
im-server/src/main/java/com/lx/implatform/imserver/websocket/WebsocketChannelCtxHloder.java

@ -0,0 +1,31 @@
package com.lx.implatform.imserver.websocket;
import io.netty.channel.ChannelHandlerContext;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
public class WebsocketChannelCtxHloder {
private static Map<Long, ChannelHandlerContext> channelMap = new ConcurrentHashMap();
public static void addChannelCtx(Long userId,ChannelHandlerContext ctx){
channelMap.put(userId,ctx);
}
public static void removeChannelCtx(Long userId){
channelMap.remove(userId);
}
public static ChannelHandlerContext getChannelCtx(Long userId){
return channelMap.get(userId);
}
public static Set<Long> getAllChannelIds(){
return channelMap.keySet();
}
}

71
im-server/src/main/java/com/lx/implatform/imserver/websocket/WebsocketServer.java

@ -0,0 +1,71 @@
package com.lx.implatform.imserver.websocket;
import com.lx.implatform.imserver.websocket.endecode.MessageProtocolDecoder;
import com.lx.implatform.imserver.websocket.endecode.MessageProtocolEncoder;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.IdleStateHandler;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
public class WebsocketServer {
public static String LOCAL_SERVER_ID = UUID.randomUUID().toString();
public void start(int port) {
// 服务端启动辅助类,用于设置TCP相关参数
ServerBootstrap bootstrap = new ServerBootstrap();
// 获取Reactor线程池
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workGroup = new NioEventLoopGroup();
// 设置为主从线程模型
bootstrap.group(bossGroup, workGroup)
// 设置服务端NIO通信类型
.channel(NioServerSocketChannel.class)
// 设置ChannelPipeline,也就是业务职责链,由处理的Handler串联而成,由从线程池处理
.childHandler(new ChannelInitializer<Channel>() {
// 添加处理的Handler,通常包括消息编解码、业务处理,也可以是日志、权限、过滤等
@Override
protected void initChannel(Channel ch) throws Exception {
// 获取职责链
ChannelPipeline pipeline = ch.pipeline();
//
pipeline.addLast(new IdleStateHandler(15, 0, 0, TimeUnit.SECONDS));
pipeline.addLast("http-codec", new HttpServerCodec());
pipeline.addLast("aggregator", new HttpObjectAggregator(65535));
pipeline.addLast("http-chunked", new ChunkedWriteHandler());
pipeline.addLast(new WebSocketServerProtocolHandler("/im"));
pipeline.addLast("encode",new MessageProtocolEncoder());
pipeline.addLast("decode",new MessageProtocolDecoder());
pipeline.addLast("handler", new WebSocketHandler());
}
})
// bootstrap 还可以设置TCP参数,根据需要可以分别设置主线程池和从线程池参数,来优化服务端性能。
// 其中主线程池使用option方法来设置,从线程池使用childOption方法设置。
// backlog表示主线程池中在套接口排队的最大数量,队列由未连接队列(三次握手未完成的)和已连接队列
.option(ChannelOption.SO_BACKLOG, 5)
// 表示连接保活,相当于心跳机制,默认为7200s
.childOption(ChannelOption.SO_KEEPALIVE, true);
try {
// 绑定端口,启动select线程,轮询监听channel事件,监听到事件之后就会交给从线程池处理
Channel channel = bootstrap.bind(port).sync().channel();
// 等待服务端口关闭
channel.closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 优雅退出,释放线程池资源
bossGroup.shutdownGracefully();
workGroup.shutdownGracefully();
}
}
}

19
im-server/src/main/java/com/lx/implatform/imserver/websocket/endecode/MessageProtocolDecoder.java

@ -0,0 +1,19 @@
package com.lx.implatform.imserver.websocket.endecode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.lx.common.model.im.SendInfo;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageDecoder;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import java.util.List;
public class MessageProtocolDecoder extends MessageToMessageDecoder<TextWebSocketFrame> {
@Override
protected void decode(ChannelHandlerContext channelHandlerContext, TextWebSocketFrame textWebSocketFrame, List<Object> list) throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
SendInfo sendInfo = objectMapper.readValue(textWebSocketFrame.text(), SendInfo.class);
list.add(sendInfo);
}
}

20
im-server/src/main/java/com/lx/implatform/imserver/websocket/endecode/MessageProtocolEncoder.java

@ -0,0 +1,20 @@
package com.lx.implatform.imserver.websocket.endecode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.lx.common.model.im.SendInfo;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageEncoder;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import java.util.List;
public class MessageProtocolEncoder extends MessageToMessageEncoder<SendInfo> {
@Override
protected void encode(ChannelHandlerContext channelHandlerContext, SendInfo sendInfo, List<Object> list) throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
String text = objectMapper.writeValueAsString(sendInfo);
TextWebSocketFrame frame = new TextWebSocketFrame(text);
list.add(frame);
}
}

51
im-server/src/main/java/com/lx/implatform/imserver/websocket/processor/HeartbeatProcessor.java

@ -0,0 +1,51 @@
package com.lx.implatform.imserver.websocket.processor;
import cn.hutool.core.bean.BeanUtil;
import com.lx.common.contant.RedisKey;
import com.lx.common.enums.WSCmdEnum;
import com.lx.common.model.im.HeartbeatInfo;
import com.lx.common.model.im.SendInfo;
import com.lx.implatform.imserver.websocket.WebsocketChannelCtxHloder;
import com.lx.implatform.imserver.websocket.WebsocketServer;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.AttributeKey;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;
@Slf4j
@Component
public class HeartbeatProcessor implements MessageProcessor<HeartbeatInfo> {
@Autowired
RedisTemplate<String,Object> redisTemplate;
@Override
public void process(ChannelHandlerContext ctx, HeartbeatInfo beatInfo) {
log.info("接收到心跳,channelId:{},userId:{}",ctx.channel().id().asLongText(),beatInfo.getUserId());
// 绑定用户和channel
WebsocketChannelCtxHloder.addChannelCtx(beatInfo.getUserId(),ctx);
// 设置属性
AttributeKey<Long> attr = AttributeKey.valueOf("USER_ID");
ctx.channel().attr(attr).set(beatInfo.getUserId());
// 在redis上记录每个user的channelId,15秒没有心跳,则自动过期
String key = RedisKey.IM_USER_SERVER_ID+beatInfo.getUserId();
redisTemplate.opsForValue().set(key, WebsocketServer.LOCAL_SERVER_ID,15, TimeUnit.SECONDS);
// 响应ws
SendInfo sendInfo = new SendInfo();
sendInfo.setCmd(WSCmdEnum.HEARTBEAT.getCode());
ctx.channel().writeAndFlush(sendInfo);
}
public HeartbeatInfo transform(Object o){
HashMap map = (HashMap)o;
HeartbeatInfo beatInfo =BeanUtil.fillBeanWithMap(map, new HeartbeatInfo(), false);
return beatInfo;
}
}

10
im-server/src/main/java/com/lx/implatform/imserver/websocket/processor/MessageProcessor.java

@ -0,0 +1,10 @@
package com.lx.implatform.imserver.websocket.processor;
import io.netty.channel.ChannelHandlerContext;
public interface MessageProcessor<T> {
void process(ChannelHandlerContext ctx,T data);
T transform(Object o);
}

23
im-server/src/main/java/com/lx/implatform/imserver/websocket/processor/ProcessorFactory.java

@ -0,0 +1,23 @@
package com.lx.implatform.imserver.websocket.processor;
import com.lx.common.enums.WSCmdEnum;
import com.lx.common.util.SpringContextHolder;
public class ProcessorFactory {
public static MessageProcessor createProcessor(WSCmdEnum cmd){
MessageProcessor processor = null;
switch (cmd){
case HEARTBEAT:
processor = (MessageProcessor) SpringContextHolder.getApplicationContext().getBean("heartbeatProcessor");
break;
case SINGLE_MESSAGE:
processor = (MessageProcessor)SpringContextHolder.getApplicationContext().getBean("singleMessageProcessor");
break;
default:
break;
}
return processor;
}
}

45
im-server/src/main/java/com/lx/implatform/imserver/websocket/processor/SingleMessageProcessor.java

@ -0,0 +1,45 @@
package com.lx.implatform.imserver.websocket.processor;
import cn.hutool.core.bean.BeanUtil;
import com.lx.common.contant.RedisKey;
import com.lx.common.enums.WSCmdEnum;
import com.lx.common.model.im.SendInfo;
import com.lx.common.model.im.SingleMessageInfo;
import io.netty.channel.ChannelHandlerContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import java.util.HashMap;
@Slf4j
@Component
public class SingleMessageProcessor implements MessageProcessor<SingleMessageInfo> {
@Autowired
private RedisTemplate<String,Object> redisTemplate;
@Async
@Override
public void process(ChannelHandlerContext ctx, SingleMessageInfo data) {
log.info("接收到消息,发送者:{},接收者:{},内容:{}",data.getSendUserId(),data.getRecvUserId(),data.getContent());
// 推送消息到用户
SendInfo sendInfo = new SendInfo();
sendInfo.setCmd(WSCmdEnum.SINGLE_MESSAGE.getCode());
sendInfo.setData(data);
ctx.channel().writeAndFlush(sendInfo);
// 已读消息推送至redis,等待更新数据库
String key = RedisKey.IM_ALREADY_READED_MESSAGE;
redisTemplate.opsForList().rightPush(key,data.getId());
}
@Override
public SingleMessageInfo transform(Object o) {
HashMap map = (HashMap)o;
SingleMessageInfo info = BeanUtil.fillBeanWithMap(map, new SingleMessageInfo(), false);
return info;
}
}

11
im-server/src/main/resources/application.yml

@ -0,0 +1,11 @@
server:
port: 8877
websocket:
port: 8878
spring:
redis:
host: 127.0.0.1
port: 6379
database: 1

159
pom.xml

@ -0,0 +1,159 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>lx-im</artifactId>
<groupId>com.lx</groupId>
<packaging>pom</packaging>
<version>${im.version}</version>
<modules>
<module>im-platform</module>
<module>im-server</module>
<module>commom</module>
</modules>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
<!--FastJson-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
</dependencies>
<properties>
<im.version>1.0.0</im.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<mybatisplus-spring-boot-starter.version>3.3.2</mybatisplus-spring-boot-starter.version>
<fastjson.version>1.2.40</fastjson.version>
<velocity.version>1.7</velocity.version>
<ehcache.version>3.3.1</ehcache.version>
<swagger.version>2.7.0</swagger.version>
<hutool.version>5.3.9</hutool.version>
<druid.version>1.1.22</druid.version>
<alipay.version>3.7.110.ALL</alipay.version>
<zxing.version>3.3.3</zxing.version>
<commons-lang3.version>3.8.1</commons-lang3.version>
<lombok.version>1.18.16</lombok.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<!-- Import dependency management from Spring Boot -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.0.3.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatisplus-spring-boot-starter.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons-lang3.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity</artifactId>
<version>${velocity.version}</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger.version}</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${swagger.version}</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool.version}</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
<dependency>
<groupId>javax.interceptor</groupId>
<artifactId>javax.interceptor-api</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt</artifactId>
<version>1.0.10.RELEASE</version>
</dependency>
</dependencies>
</dependencyManagement>
<repositories>
<repository>
<id>aliyun-maven</id>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>
Loading…
Cancel
Save