map = ServletUtil.getParamMap(request);
+ param = JSONUtil.toJsonStr(map);
+ }
+ builder.append(param);
+ builder.append("_");
+ // 获取path参数
+ String path = submitRepeat.path();
+ if (StringUtils.isEmpty(path)) {
+ path = request.getServletPath();
+ }
+ builder.append(path);
+ return builder.toString();
+ }
+
+}
diff --git a/src/main/java/com/platform/common/aspectj/SubmitRepeat.java b/src/main/java/com/platform/common/aspectj/SubmitRepeat.java
new file mode 100644
index 0000000..502b407
--- /dev/null
+++ b/src/main/java/com/platform/common/aspectj/SubmitRepeat.java
@@ -0,0 +1,39 @@
+package com.platform.common.aspectj;
+
+import com.platform.common.enums.YesOrNoEnum;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 自定义一个注解,给需要防止重复提交的方法加上该注解
+ *
+ * 使用:@SubmitRepeat
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface SubmitRepeat {
+
+ /**
+ * 过期时间
+ */
+ long value() default 2L;
+
+ /**
+ * 拦截uri
+ */
+ String path() default "";
+
+ /**
+ * 拦截msg
+ */
+ String msg() default "请勿重复请求";
+
+ /**
+ * 是否抛异常
+ */
+ YesOrNoEnum exception() default YesOrNoEnum.YES;
+
+}
diff --git a/src/main/java/com/platform/common/config/ApplicationConfig.java b/src/main/java/com/platform/common/config/ApplicationConfig.java
new file mode 100644
index 0000000..6f2d06c
--- /dev/null
+++ b/src/main/java/com/platform/common/config/ApplicationConfig.java
@@ -0,0 +1,110 @@
+package com.platform.common.config;
+
+import cn.hutool.core.date.DatePattern;
+import cn.hutool.core.date.DateUtil;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+import com.platform.common.enums.YesOrNoEnum;
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.EnableAspectJAutoProxy;
+import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+import org.springframework.web.filter.CorsFilter;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.TimeZone;
+
+/**
+ * 程序注解配置
+ */
+@Configuration
+// 表示通过aop框架暴露该代理对象,AopContext能够访问
+@EnableAspectJAutoProxy(exposeProxy = true)
+// 指定要扫描的Mapper类的包的路径
+@MapperScan({"com.platform.modules.**.dao"})
+// 扫描spring工具类
+@ComponentScan(basePackages = {"cn.hutool.extra.spring"})
+public class ApplicationConfig {
+
+ /**
+ * 时区配置
+ */
+ @Bean
+ public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization() {
+ return builder -> builder.timeZone(TimeZone.getDefault());
+ }
+
+ /**
+ * 序列化枚举值为数据库存储值
+ */
+ @Bean
+ public Jackson2ObjectMapperBuilderCustomizer customizer() {
+ return builder -> builder.featuresToEnable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
+ }
+
+ @Bean
+ public static MappingJackson2HttpMessageConverter objectMapper() {
+ final ObjectMapper objectMapper = new ObjectMapper();
+ // 忽略未知的枚举字段
+ objectMapper.enable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL);
+ // 忽略多余的字段不参与序列化
+ objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
+ // null属性字段转""
+ objectMapper.getSerializerProvider().setNullValueSerializer(new JsonSerializer() {
+ @Override
+ public void serialize(Object arg0, JsonGenerator arg1, SerializerProvider arg2) throws IOException {
+ arg1.writeString("");
+ }
+ });
+ SimpleModule simpleModule = new SimpleModule();
+ // 格式化Long
+ simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
+ // 格式化时间
+ simpleModule.addSerializer(Date.class, new JsonSerializer() {
+ @Override
+ public void serialize(Date date, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
+ jsonGenerator.writeString(DateUtil.format(date, DatePattern.NORM_DATETIME_FORMAT));
+ }
+ });
+ // 格式化金额
+ simpleModule.addSerializer(BigDecimal.class, new JsonSerializer() {
+ @Override
+ public void serialize(BigDecimal decimal, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
+ jsonGenerator.writeString(decimal.setScale(2, BigDecimal.ROUND_HALF_DOWN).toString());
+ }
+ });
+ // 注册 module
+ objectMapper.registerModule(simpleModule);
+ MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
+ converter.setObjectMapper(objectMapper);
+ return converter;
+ }
+
+ @Value("${platform.cors}")
+ private String cors;
+
+ @Bean
+ public CorsFilter corsFilter() {
+ UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+ if (YesOrNoEnum.YES.getCode().equals(cors)) {
+ CorsConfiguration corsConfiguration = new CorsConfiguration();
+ corsConfiguration.addAllowedOrigin("*");
+ corsConfiguration.addAllowedHeader("*");
+ corsConfiguration.addAllowedMethod("*");
+ corsConfiguration.setAllowCredentials(true);
+ source.registerCorsConfiguration("/**", corsConfiguration);
+ }
+ return new CorsFilter(source);
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/platform/common/config/MybatisPlusConfig.java b/src/main/java/com/platform/common/config/MybatisPlusConfig.java
new file mode 100644
index 0000000..1229942
--- /dev/null
+++ b/src/main/java/com/platform/common/config/MybatisPlusConfig.java
@@ -0,0 +1,45 @@
+package com.platform.common.config;
+
+import com.baomidou.mybatisplus.core.injector.AbstractMethod;
+import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
+import com.baomidou.mybatisplus.extension.injector.methods.InsertBatchSomeColumn;
+import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
+
+import java.util.List;
+
+@EnableTransactionManagement(proxyTargetClass = true)
+@Configuration
+public class MybatisPlusConfig {
+
+ @Bean
+ public MybatisPlusInterceptor mybatisPlusInterceptor() {
+ MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
+ // 乐观锁插件
+ interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
+ // 防全表更新与删除插件
+ interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
+ return interceptor;
+ }
+
+ /**
+ * 批量操作增强
+ */
+ @Bean
+ public DefaultSqlInjector mybatisSqlInjector() {
+ return new DefaultSqlInjector() {
+ @Override
+ public List getMethodList(Class> mapperClass) {
+ //防止父类方法不可用
+ List methods = super.getMethodList(mapperClass);
+ methods.add(new InsertBatchSomeColumn());
+ return methods;
+ }
+ };
+ }
+
+}
diff --git a/src/main/java/com/platform/common/config/PlatformConfig.java b/src/main/java/com/platform/common/config/PlatformConfig.java
new file mode 100644
index 0000000..0a8a8cb
--- /dev/null
+++ b/src/main/java/com/platform/common/config/PlatformConfig.java
@@ -0,0 +1,58 @@
+package com.platform.common.config;
+
+import com.platform.common.core.EnumUtils;
+import com.platform.common.enums.YesOrNoEnum;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.stereotype.Component;
+
+/**
+ * 读取项目相关配置
+ */
+@Component
+@Configuration
+@ConfigurationProperties(prefix = "platform")
+public class PlatformConfig {
+
+ /**
+ * 上传路径
+ */
+ public static String ROOT_PATH;
+
+ /**
+ * 文件预览
+ */
+ public static String PREVIEW = "/preview/**";
+
+ /**
+ * 图标
+ */
+ public static String FAVICON = "/favicon.ico";
+
+ /**
+ * token超时时间(分钟)
+ */
+ public static Integer TIMEOUT;
+
+ /**
+ * 是否开启短信
+ */
+ public static YesOrNoEnum SMS;
+
+ @Value("${platform.timeout}")
+ public void setTokenTimeout(Integer timeout) {
+ PlatformConfig.TIMEOUT = timeout;
+ }
+
+ @Value("${platform.sms:N}")
+ public void setSms(String sms) {
+ PlatformConfig.SMS = EnumUtils.toEnum(YesOrNoEnum.class, sms, YesOrNoEnum.NO);
+ }
+
+ @Value("${platform.rootPath}")
+ public void setRootPath(String rootPath) {
+ PlatformConfig.ROOT_PATH = rootPath;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/platform/common/config/WebMvcConfig.java b/src/main/java/com/platform/common/config/WebMvcConfig.java
new file mode 100644
index 0000000..ae56f50
--- /dev/null
+++ b/src/main/java/com/platform/common/config/WebMvcConfig.java
@@ -0,0 +1,64 @@
+package com.platform.common.config;
+
+import cn.hutool.core.io.file.FileNameUtil;
+import com.platform.common.version.DeviceInterceptor;
+import com.platform.common.version.VersionHandlerMapping;
+import com.platform.common.version.VersionInterceptor;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
+import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
+
+import javax.annotation.Resource;
+import java.util.List;
+
+/**
+ * 通用配置
+ */
+@Configuration
+public class WebMvcConfig extends WebMvcConfigurationSupport {
+
+ @Resource
+ private VersionInterceptor versionInterceptor;
+
+ @Resource
+ private DeviceInterceptor deviceInterceptor;
+
+ @Override
+ public RequestMappingHandlerMapping createRequestMappingHandlerMapping() {
+ return new VersionHandlerMapping();
+ }
+
+ @Override
+ public void configureMessageConverters(List> converters) {
+ converters.add(ApplicationConfig.objectMapper());
+ }
+
+ @Value("${platform.rootPath}")
+ private String rootPath;
+
+ @Override
+ public void addResourceHandlers(ResourceHandlerRegistry registry) {
+ // favicon.ico
+ registry.addResourceHandler(PlatformConfig.FAVICON).addResourceLocations("classpath:/static/");
+ // file
+ registry.addResourceHandler(PlatformConfig.PREVIEW).addResourceLocations("file:" + rootPath + FileNameUtil.UNIX_SEPARATOR);
+ }
+
+ /**
+ * 自定义拦截规则
+ */
+ @Override
+ public void addInterceptors(InterceptorRegistry registry) {
+ registry.addInterceptor(versionInterceptor)
+ .addPathPatterns("/**")
+ .excludePathPatterns(PlatformConfig.FAVICON, PlatformConfig.PREVIEW);
+ registry.addInterceptor(deviceInterceptor)
+ .addPathPatterns("/**")
+ .excludePathPatterns(PlatformConfig.FAVICON, PlatformConfig.PREVIEW);
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/platform/common/constant/AppConstants.java b/src/main/java/com/platform/common/constant/AppConstants.java
new file mode 100644
index 0000000..8a2e7cf
--- /dev/null
+++ b/src/main/java/com/platform/common/constant/AppConstants.java
@@ -0,0 +1,183 @@
+package com.platform.common.constant;
+
+/**
+ * 通用常量信息
+ */
+public class AppConstants {
+
+ /**
+ * user_id
+ */
+ public static final String USER_ID = "user_id";
+
+ /**
+ * 默认头像
+ */
+ public static final String DEFAULT_PORTRAIT = "http://q3z3-im.oss-cn-beijing.aliyuncs.com/61bed1c563de173eb00e8d8c.png";
+
+ /**
+ * 注销昵称
+ */
+ public static final String DELETED_NICK_NAME = "已注销";
+
+ /**
+ * 用户二维码
+ */
+ public static final String QR_CODE_USER = "user:";
+
+ /**
+ * 群组二维码
+ */
+ public static final String QR_CODE_GROUP = "group:";
+
+ /**
+ * 视频参数
+ */
+ public static final String VIDEO_PARAM = "?x-oss-process=video/snapshot,t_1000,f_png,w_600,m_fast";
+
+ /**
+ * 新建群名称:好友创建通知
+ */
+ public static final String GROUP_CREATE_NAME = "{}的群聊-{}";
+
+ /**
+ * 不是你的好友
+ */
+ public static final String FRIEND_NOT_EXIST = "对方不是你的好友";
+
+ /**
+ * 通知:好友创建通知
+ */
+ public static final String NOTICE_FRIEND_CREATE = "你们已经是好友啦,现在开始聊天吧";
+
+ /**
+ * 通知:群组创建通知
+ */
+ public static final String NOTICE_GROUP_CREATE_MASTER = "你邀请{}加入了群聊";
+
+ /**
+ * 通知:群组创建通知
+ */
+ public static final String NOTICE_GROUP_CREATE_MEMBER = "{}邀请你加入了群聊";
+
+ /**
+ * 通知:群组退出通知
+ */
+ public static final String NOTICE_GROUP_LOGOUT = "{}退出了群聊";
+
+ /**
+ * 通知:群组转让通知
+ */
+ public static final String NOTICE_GROUP_TRANSFER = "你已成为新群主";
+
+ /**
+ * 通知:群组加入通知
+ */
+ public static final String NOTICE_GROUP_JOIN = "{}加入了群聊";
+
+ /**
+ * 通知:群组踢出通知
+ */
+ public static final String NOTICE_GROUP_KICKED_MASTER = "{}被移出群聊";
+
+ /**
+ * 通知:群组踢出通知
+ */
+ public static final String NOTICE_GROUP_KICKED_MEMBER = "你被移出群聊";
+
+ /**
+ * 通知:群组解散通知
+ */
+ public static final String NOTICE_GROUP_DISSOLVE = "群主解散了群聊";
+
+ /**
+ * 通知:群组改名
+ */
+ public static final String NOTICE_GROUP_EDIT = "{}修改群名为“{}”";
+
+ /**
+ * 通知:群组公告
+ */
+ public static final String NOTICE_GROUP_NOTICE = "{}修改群公告为“{}”";
+
+ /**
+ * 帖子_通知
+ */
+ public static final String REDIS_TOPIC_NOTICE = "topic:notice:";
+
+ /**
+ * 帖子_回复
+ */
+ public static final String REDIS_TOPIC_REPLY = "topic:reply:";
+
+ /**
+ * 好友_通知
+ */
+ public static final String REDIS_FRIEND_NOTICE = "friend:notice:";
+
+ /**
+ * redis_附近的人
+ */
+ public static final String REDIS_NEAR = "chat:near";
+
+ /**
+ * redis_摇一摇
+ */
+ public static final String REDIS_SHAKE = "chat:shake";
+
+ /**
+ * redis_摇一摇
+ */
+ public static final String REDIS_GEO = "chat:geo";
+
+ /**
+ * redis_消息
+ */
+ public static final String REDIS_MSG = "chat:msg:{}:{}";
+
+ /**
+ * redis_消息(天数)
+ */
+ public static final Integer REDIS_MSG_TIME = 7;
+
+ /**
+ * redis_天气
+ */
+ public static final String REDIS_MP_WEATHER = "mp:weather:{}:{}";
+
+ /**
+ * redis_天气(分钟)
+ */
+ public static final Integer REDIS_MP_WEATHER_TIME = 30;
+
+ /**
+ * redis_好友
+ */
+ public static final String REDIS_FRIEND = "chat:friend:{}:{}";
+
+ /**
+ * redis_好友(天)
+ */
+ public static final Integer REDIS_FRIEND_TIME = 60;
+
+ /**
+ * redis_群组
+ */
+ public static final String REDIS_GROUP_INFO = "chat:group:{}:{}";
+
+ /**
+ * redis_群组(天)
+ */
+ public static final Integer REDIS_GROUP_TIME = 60;
+
+ /**
+ * redis_实时音视频_签名
+ */
+ public static final String REDIS_TRTC_SIGN = "chat:trtc:sign:";
+
+ /**
+ * redis_实时音视频_用户前缀
+ */
+ public static final String REDIS_TRTC_USER = "u";
+
+}
diff --git a/src/main/java/com/platform/common/constant/HeadConstant.java b/src/main/java/com/platform/common/constant/HeadConstant.java
new file mode 100644
index 0000000..9d3510f
--- /dev/null
+++ b/src/main/java/com/platform/common/constant/HeadConstant.java
@@ -0,0 +1,43 @@
+package com.platform.common.constant;
+
+/**
+ * 头部常量
+ */
+public class HeadConstant {
+
+ /**
+ * 登录用户 redis key
+ */
+ public static final String TOKEN_REDIS_APP = "token:app:";
+
+ /**
+ * 令牌
+ */
+ public static final String TOKEN_HEADER_ADMIN = "Authorization";
+
+ /**
+ * 版本号
+ */
+ public static final String VERSION = "version";
+
+ /**
+ * 签名
+ */
+ public static final String SIGN = "sign";
+
+ /**
+ * appId
+ */
+ public static final String APP_ID = "appId";
+
+ /**
+ * 签名
+ */
+ public static final String TIMESTAMP = "timestamp";
+
+ /**
+ * 设备
+ */
+ public static final String DEVICE = "device";
+
+}
diff --git a/src/main/java/com/platform/common/core/CharsetKit.java b/src/main/java/com/platform/common/core/CharsetKit.java
new file mode 100644
index 0000000..d2ccced
--- /dev/null
+++ b/src/main/java/com/platform/common/core/CharsetKit.java
@@ -0,0 +1,89 @@
+package com.platform.common.core;
+
+import org.springframework.util.StringUtils;
+
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * 字符集工具类
+ */
+public class CharsetKit {
+ /**
+ * ISO-8859-1
+ */
+ public static final String ISO_8859_1 = "ISO-8859-1";
+ /**
+ * UTF-8
+ */
+ public static final String UTF_8 = "UTF-8";
+ /**
+ * GBK
+ */
+ public static final String GBK = "GBK";
+
+ /**
+ * ISO-8859-1
+ */
+ public static final Charset CHARSET_ISO_8859_1 = Charset.forName(ISO_8859_1);
+ /**
+ * UTF-8
+ */
+ public static final Charset CHARSET_UTF_8 = Charset.forName(UTF_8);
+ /**
+ * GBK
+ */
+ public static final Charset CHARSET_GBK = Charset.forName(GBK);
+
+ /**
+ * 转换为Charset对象
+ *
+ * charset 字符集,为空则返回默认字符集
+ */
+ public static Charset charset(String charset) {
+ return StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset);
+ }
+
+ /**
+ * 转换字符串的字符集编码
+ *
+ * source 字符串
+ * srcCharset 源字符集,默认ISO-8859-1
+ * destCharset 目标字符集,默认UTF-8
+ * 转换后的字符集
+ */
+ public static String convert(String source, String srcCharset, String destCharset) {
+ return convert(source, Charset.forName(srcCharset), Charset.forName(destCharset));
+ }
+
+ /**
+ * 转换字符串的字符集编码
+ *
+ * source 字符串
+ * srcCharset 源字符集,默认ISO-8859-1
+ * destCharset 目标字符集,默认UTF-8
+ * 转换后的字符集
+ */
+ public static String convert(String source, Charset srcCharset, Charset destCharset) {
+ if (null == srcCharset) {
+ srcCharset = StandardCharsets.ISO_8859_1;
+ }
+
+ if (null == destCharset) {
+ srcCharset = StandardCharsets.UTF_8;
+ }
+
+ if (StringUtils.isEmpty(source) || srcCharset.equals(destCharset)) {
+ return source;
+ }
+ return new String(source.getBytes(srcCharset), destCharset);
+ }
+
+ /**
+ * 系统字符集编码
+ */
+ public static String sysCharset() {
+ return Charset.defaultCharset().name();
+ }
+
+}
diff --git a/src/main/java/com/platform/common/core/EnumUtils.java b/src/main/java/com/platform/common/core/EnumUtils.java
new file mode 100644
index 0000000..c772252
--- /dev/null
+++ b/src/main/java/com/platform/common/core/EnumUtils.java
@@ -0,0 +1,30 @@
+package com.platform.common.core;
+
+import cn.hutool.core.util.EnumUtil;
+import org.springframework.util.StringUtils;
+
+import java.util.Map;
+
+/**
+ * 类型转换器
+ */
+public class EnumUtils {
+
+ public static > E toEnum(Class clazz, String code) {
+ return toEnum(clazz, code, null);
+ }
+
+ public static > E toEnum(Class clazz, String code, E defaultValue) {
+ if (StringUtils.isEmpty(code)) {
+ return defaultValue;
+ }
+ Map enumMap = EnumUtil.getNameFieldMap(clazz, "code");
+ for (String key : enumMap.keySet()) {
+ if (code.equals(enumMap.get(key).toString())) {
+ return EnumUtil.fromString(clazz, key);
+ }
+ }
+ return defaultValue;
+ }
+
+}
diff --git a/src/main/java/com/platform/common/enums/DeviceEnum.java b/src/main/java/com/platform/common/enums/DeviceEnum.java
new file mode 100644
index 0000000..3ea8f68
--- /dev/null
+++ b/src/main/java/com/platform/common/enums/DeviceEnum.java
@@ -0,0 +1,53 @@
+package com.platform.common.enums;
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+import com.fasterxml.jackson.annotation.JsonValue;
+import lombok.Getter;
+
+/**
+ * 设备类型枚举
+ */
+@Getter
+public enum DeviceEnum {
+
+ /**
+ * 安卓
+ */
+ ANDROID("android", "安卓"),
+ /**
+ * 苹果
+ */
+ IOS("ios", "苹果"),
+ /**
+ * 微软
+ */
+ WINDOWS("windows", "微软"),
+ /**
+ * MAC
+ */
+ MAC("mac", "MAC"),
+ /**
+ * H5
+ */
+ H5("h5", "h5"),
+ /**
+ * PC
+ */
+ PC("pc", "PC"),
+ /**
+ * 小程序
+ */
+ MINI("mini", "小程序"),
+ ;
+
+ @EnumValue
+ @JsonValue
+ private final String code;
+ private final String info;
+
+ DeviceEnum(String code, String info) {
+ this.code = code;
+ this.info = info;
+ }
+
+}
diff --git a/src/main/java/com/platform/common/enums/GenderEnum.java b/src/main/java/com/platform/common/enums/GenderEnum.java
new file mode 100644
index 0000000..655c680
--- /dev/null
+++ b/src/main/java/com/platform/common/enums/GenderEnum.java
@@ -0,0 +1,36 @@
+package com.platform.common.enums;
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+import com.fasterxml.jackson.annotation.JsonValue;
+import lombok.Getter;
+
+/**
+ * 性别类型枚举
+ */
+@Getter
+public enum GenderEnum {
+
+ /**
+ * 未知
+ */
+ UNKNOWN("0", "未知"),
+ /**
+ * 男
+ */
+ MALE("1", "男"),
+ /**
+ * 女
+ */
+ FEMALE("2", "女");
+
+ @EnumValue
+ @JsonValue
+ private final String code;
+ private final String info;
+
+ GenderEnum(String code, String info) {
+ this.code = code;
+ this.info = info;
+ }
+
+}
diff --git a/src/main/java/com/platform/common/enums/ResultCodeEnum.java b/src/main/java/com/platform/common/enums/ResultCodeEnum.java
new file mode 100644
index 0000000..7a3517c
--- /dev/null
+++ b/src/main/java/com/platform/common/enums/ResultCodeEnum.java
@@ -0,0 +1,62 @@
+package com.platform.common.enums;
+
+import lombok.Getter;
+
+/**
+ * 返回码枚举
+ */
+@Getter
+public enum ResultCodeEnum {
+
+ /**
+ * 操作成功
+ */
+ SUCCESS(200, "操作成功"),
+ /**
+ * 未授权
+ */
+ UNAUTHORIZED(401, "登录已过期,请重新登录"),
+ /**
+ * 无访问权限
+ */
+ AUTH(403, "无访问权限"),
+ /**
+ * 客户证书已过期或无效
+ */
+ CERTIFICATE(40317, "客户证书已过期或无效"),
+ /**
+ * 资源/服务未找到
+ */
+ NOT_FOUND(404, "路径不存在,请检查路径是否正确"),
+ /**
+ * 操作失败
+ */
+ FAIL(500, "系统异常,请联系管理员"),
+ /**
+ * 版本号
+ */
+ VERSION(601, "版本过低,请升级"),
+ /**
+ * 安全验证
+ */
+ SECURITY(602, "安全验证"),
+ ;
+
+ private final Integer code;
+ private final String info;
+
+ ResultCodeEnum(Integer code, String info) {
+ this.code = code;
+ this.info = info;
+ }
+
+ public static ResultCodeEnum init(Integer code) {
+ for (ResultCodeEnum resultCode : ResultCodeEnum.values()) {
+ if (resultCode.getCode().equals(code)) {
+ return resultCode;
+ }
+ }
+ return ResultCodeEnum.FAIL;
+ }
+
+}
diff --git a/src/main/java/com/platform/common/enums/YesOrNoEnum.java b/src/main/java/com/platform/common/enums/YesOrNoEnum.java
new file mode 100644
index 0000000..fe7fc22
--- /dev/null
+++ b/src/main/java/com/platform/common/enums/YesOrNoEnum.java
@@ -0,0 +1,45 @@
+package com.platform.common.enums;
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+import com.fasterxml.jackson.annotation.JsonValue;
+import lombok.Getter;
+
+/**
+ * 是否类型枚举
+ */
+@Getter
+public enum YesOrNoEnum {
+
+ /**
+ * 是
+ */
+ YES("Y", "是"),
+ /**
+ * 否
+ */
+ NO("N", "否"),
+ /**
+ * 退
+ */
+ REFUND("R", "退"),
+ ;
+
+ @EnumValue
+ @JsonValue
+ private final String code;
+ private final String info;
+
+ YesOrNoEnum(String code, String info) {
+ this.code = code;
+ this.info = info;
+ }
+
+ public static boolean transform(YesOrNoEnum result) {
+ return YesOrNoEnum.YES.equals(result);
+ }
+
+ public static YesOrNoEnum transform(boolean result) {
+ return result ? YesOrNoEnum.YES : YesOrNoEnum.NO;
+ }
+
+}
diff --git a/src/main/java/com/platform/common/exception/BaseException.java b/src/main/java/com/platform/common/exception/BaseException.java
new file mode 100644
index 0000000..63473cb
--- /dev/null
+++ b/src/main/java/com/platform/common/exception/BaseException.java
@@ -0,0 +1,48 @@
+package com.platform.common.exception;
+
+import com.platform.common.enums.ResultCodeEnum;
+import com.platform.common.utils.MessageUtils;
+import lombok.Getter;
+
+/**
+ * 自定义异常
+ */
+public class BaseException extends RuntimeException {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 错误码
+ */
+ @Getter
+ private ResultCodeEnum resultCode;
+
+ /**
+ * 错误消息
+ */
+ private String message;
+
+ public BaseException(String message) {
+ this.message = message;
+ }
+
+ public BaseException(ResultCodeEnum resultCode) {
+ this.resultCode = resultCode;
+ this.message = resultCode.getInfo();
+ }
+
+ public BaseException(ResultCodeEnum resultCode, String message) {
+ this.resultCode = resultCode;
+ this.message = message;
+ }
+
+ @Override
+ public String getMessage() {
+ String message = this.message;
+ if (1 + 1 != 2) {
+ message = MessageUtils.message("", "");
+ }
+ return message;
+ }
+
+}
diff --git a/src/main/java/com/platform/common/exception/LoginException.java b/src/main/java/com/platform/common/exception/LoginException.java
new file mode 100644
index 0000000..8d1f847
--- /dev/null
+++ b/src/main/java/com/platform/common/exception/LoginException.java
@@ -0,0 +1,20 @@
+package com.platform.common.exception;
+
+import com.platform.common.enums.ResultCodeEnum;
+import lombok.Getter;
+import org.apache.shiro.authc.AuthenticationException;
+
+/**
+ * 登录异常
+ */
+public class LoginException extends AuthenticationException {
+
+ @Getter
+ private ResultCodeEnum code;
+
+ public LoginException(ResultCodeEnum resultCode) {
+ super(resultCode.getInfo());
+ this.code = resultCode;
+ }
+
+}
diff --git a/src/main/java/com/platform/common/redis/GeoHashUtils.java b/src/main/java/com/platform/common/redis/GeoHashUtils.java
new file mode 100644
index 0000000..532d3ae
--- /dev/null
+++ b/src/main/java/com/platform/common/redis/GeoHashUtils.java
@@ -0,0 +1,148 @@
+package com.platform.common.redis;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.geo.*;
+import org.springframework.data.redis.connection.RedisGeoCommands;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * GeoHash工具类
+ */
+@Component
+public class GeoHashUtils {
+
+ @Autowired
+ private RedisTemplate redisTemplate;
+
+ /***
+ * 将指定的地理空间位置(纬度、经度、名称)添加到指定的key中。
+ * @param key redis的key
+ * @param longitude 经度
+ * @param latitude 纬度
+ * @param name 名称
+ * @return
+ */
+ public Long add(String key, double longitude, double latitude, String name) {
+// Long addedNum = redisTemplate.opsForGeo().add("city", new Point(121.47, 31.23), "上海");
+// Long addedNum = redisTemplate.opsForGeo().add("city", new Point(113.27, 23.13), "广州");
+ return redisTemplate.opsForGeo().add(key, new Point(longitude, latitude), name);
+ }
+
+ /***
+ * 将指定的地理空间位置(纬度、经度、名称)添加到指定的key中。
+ * @param key redis的key
+ * @param map 名称 - 经度 - 纬度
+ * @return
+ */
+ public Long add(String key, Map map) {
+ return redisTemplate.opsForGeo().add(key, map);
+ }
+
+ /***
+ * 将指定的地理空间移除。
+ * @param key redis的key
+ * @param name 名称
+ * @return
+ */
+ public Long remove(String key, String... name) {
+ return redisTemplate.opsForGeo().remove(key, name);
+ }
+
+ /***
+ * 将指定的地理空间移除。
+ * @param key redis的key
+ * @param nameList 名称集合
+ * @return
+ */
+ public Long remove(String key, List nameList) {
+ return redisTemplate.opsForGeo().remove(key, nameList.toArray());
+ }
+
+ /***
+ * 从key里返回所有给定位置元素的位置(经度和纬度)。
+ * @param key redis的key
+ * @param name 名称
+ */
+ public List get(String key, String... name) {
+ List points = redisTemplate.opsForGeo().position(key, name);//params: key, 地方名称...
+ return points;
+ }
+
+ /***
+ * 从key里返回所有给定位置元素的位置(经度和纬度)。
+ * @param key redis的key
+ * @param nameList 名称的集合
+ */
+ public List get(String key, List nameList) {
+ List points = redisTemplate.opsForGeo().position(key, nameList.toArray());
+ return points;
+ }
+
+ /***
+ * 返回两个给定位置之间的距离。
+ * @param key redis的key
+ * @param name1 地方名称1
+ * @param name2 地方名称2
+ * @return
+ */
+ public Distance dist(String key, String name1, String name2) {
+ Distance distance = redisTemplate.opsForGeo()
+ .distance(key, name1, name2, RedisGeoCommands.DistanceUnit.KILOMETERS);
+ return distance;
+ }
+
+ /***
+ * 以给定的城市为中心, 返回键包含的位置元素当中, 与中心的距离不超过给定最大距离的所有位置元素,并给出所有位置元素与中心的平均距离。
+ * @param key redis的key
+ * @param name 名称
+ * @param distance 距离
+ * @param count 人数
+ * @return
+ */
+ public List> radius(String key, String name, Integer distance, Integer count) {
+ Distance distances = new Distance(distance, Metrics.KILOMETERS);
+ RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().includeCoordinates().sortAscending().limit(count);
+ return redisTemplate.opsForGeo().radius(key, name, distances, args).getContent();
+ }
+
+ /***
+ * 以给定的经纬度为中心, 返回键包含的位置元素当中, 与中心的距离不超过给定最大距离的所有位置元素,并给出所有位置元素与中心的平均距离。
+ * @param key redis的key
+ * @param longitude 经度
+ * @param latitude 纬度
+ * @param distance 距离
+ * @param count 人数
+ * @return
+ */
+ public List> radius(String key, double longitude, double latitude, Integer distance, Integer count) {
+ Circle circle = new Circle(new Point(longitude, latitude), new Distance(distance, Metrics.KILOMETERS));
+ RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().includeCoordinates().sortAscending().limit(count);
+ return redisTemplate.opsForGeo().radius(key, circle, args).getContent();
+ }
+
+ /***
+ * 返回一个或多个位置元素的 Geohash 表示
+ * @param key redis的key
+ * @param name 名称的集合
+ */
+ public List hash(String key, String... name) {
+ List results = redisTemplate.opsForGeo().hash(key, name);
+ return results;
+ }
+
+ /***
+ * 返回一个或多个位置元素的 Geohash 表示
+ * @param key redis的key
+ * @param nameList 名称的集合
+ */
+ public List hash(String key, List nameList) {
+ List results = redisTemplate.opsForGeo().hash(key, nameList.toArray());
+ return results;
+ }
+
+}
+
diff --git a/src/main/java/com/platform/common/redis/GeoVo.java b/src/main/java/com/platform/common/redis/GeoVo.java
new file mode 100644
index 0000000..233311c
--- /dev/null
+++ b/src/main/java/com/platform/common/redis/GeoVo.java
@@ -0,0 +1,10 @@
+package com.platform.common.redis;
+
+import lombok.Data;
+
+@Data
+public class GeoVo {
+
+ private String name;
+
+}
diff --git a/src/main/java/com/platform/common/redis/RedisConfig.java b/src/main/java/com/platform/common/redis/RedisConfig.java
new file mode 100644
index 0000000..f75bdb6
--- /dev/null
+++ b/src/main/java/com/platform/common/redis/RedisConfig.java
@@ -0,0 +1,31 @@
+package com.platform.common.redis;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cache.annotation.EnableCaching;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.serializer.RedisSerializer;
+import org.springframework.data.redis.serializer.StringRedisSerializer;
+
+/**
+ * redis配置
+ */
+@Configuration
+@EnableCaching
+public class RedisConfig {
+
+ @Autowired
+ private RedisTemplate redisTemplate;
+
+ @Bean
+ public RedisTemplate stringSerializerRedisTemplate() {
+ RedisSerializer stringSerializer = new StringRedisSerializer();
+ redisTemplate.setKeySerializer(stringSerializer);
+ redisTemplate.setValueSerializer(stringSerializer);
+ redisTemplate.setHashKeySerializer(stringSerializer);
+ redisTemplate.setHashValueSerializer(stringSerializer);
+ return redisTemplate;
+ }
+
+}
diff --git a/src/main/java/com/platform/common/redis/RedisListenerConfig.java b/src/main/java/com/platform/common/redis/RedisListenerConfig.java
new file mode 100644
index 0000000..27b0856
--- /dev/null
+++ b/src/main/java/com/platform/common/redis/RedisListenerConfig.java
@@ -0,0 +1,19 @@
+package com.platform.common.redis;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.listener.RedisMessageListenerContainer;
+
+@Configuration
+public class RedisListenerConfig {
+
+ @Bean
+ RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {
+ RedisMessageListenerContainer container = new RedisMessageListenerContainer();
+ container.setConnectionFactory(connectionFactory);
+ //container.addMessageListener(new RedisExpiredListener(), new PatternTopic("__keyevent@0__:expired"));
+ return container;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/platform/common/redis/RedisUtils.java b/src/main/java/com/platform/common/redis/RedisUtils.java
new file mode 100644
index 0000000..b367158
--- /dev/null
+++ b/src/main/java/com/platform/common/redis/RedisUtils.java
@@ -0,0 +1,1372 @@
+package com.platform.common.redis;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.connection.DataType;
+import org.springframework.data.redis.core.Cursor;
+import org.springframework.data.redis.core.ScanOptions;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.data.redis.core.ZSetOperations.TypedTuple;
+import org.springframework.stereotype.Component;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.StringUtils;
+
+import java.util.*;
+import java.util.Map.Entry;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Redis工具类
+ *
+ * @author WangFan
+ * @version 1.1 (GitHub文档: https://github.com/whvcse/RedisUtil )
+ * @date 2018-02-24 下午03:09:50
+ */
+@Component
+public class RedisUtils {
+
+ @Autowired
+ private StringRedisTemplate redisTemplate;
+
+ /** -------------------key相关操作--------------------- */
+
+ /**
+ * 删除key
+ *
+ * @param key
+ */
+ public void delete(String key) {
+ if (StringUtils.isEmpty(key)) {
+ return;
+ }
+ redisTemplate.delete(key);
+ }
+
+ /**
+ * 批量删除key
+ *
+ * @param keys
+ */
+ public void delete(Collection keys) {
+ if (CollectionUtils.isEmpty(keys)) {
+ return;
+ }
+ redisTemplate.delete(keys);
+ }
+
+ /**
+ * 序列化key
+ *
+ * @param key
+ * @return
+ */
+ public byte[] dump(String key) {
+ return redisTemplate.dump(key);
+ }
+
+ /**
+ * 是否存在key
+ *
+ * @param key
+ * @return
+ */
+ public Boolean hasKey(String key) {
+ return redisTemplate.hasKey(key);
+ }
+
+ /**
+ * 设置过期时间
+ *
+ * @param key
+ * @param timeout
+ * @param unit
+ * @return
+ */
+ public Boolean expire(String key, long timeout, TimeUnit unit) {
+ return redisTemplate.expire(key, timeout, unit);
+ }
+
+ /**
+ * 设置过期时间
+ *
+ * @param key
+ * @param date
+ * @return
+ */
+ public Boolean expire(String key, Date date) {
+ return redisTemplate.expireAt(key, date);
+ }
+
+ /**
+ * 查找匹配的key
+ *
+ * @param pattern
+ * @return
+ */
+ public Set keys(String pattern) {
+ return redisTemplate.keys(pattern);
+ }
+
+ /**
+ * 将当前数据库的 key 移动到给定的数据库 db 当中
+ *
+ * @param key
+ * @param dbIndex
+ * @return
+ */
+ public Boolean move(String key, int dbIndex) {
+ return redisTemplate.move(key, dbIndex);
+ }
+
+ /**
+ * 移除 key 的过期时间,key 将持久保持
+ *
+ * @param key
+ * @return
+ */
+ public Boolean persist(String key) {
+ return redisTemplate.persist(key);
+ }
+
+ /**
+ * 返回 key 的剩余的过期时间
+ *
+ * @param key
+ * @param unit
+ * @return
+ */
+ public Long getExpire(String key, TimeUnit unit) {
+ return redisTemplate.getExpire(key, unit);
+ }
+
+ /**
+ * 返回 key 的剩余的过期时间
+ *
+ * @param key
+ * @return
+ */
+ public Long getExpire(String key) {
+ return redisTemplate.getExpire(key);
+ }
+
+ /**
+ * 从当前数据库中随机返回一个 key
+ *
+ * @return
+ */
+ public String randomKey() {
+ return redisTemplate.randomKey();
+ }
+
+ /**
+ * 修改 key 的名称
+ *
+ * @param oldKey
+ * @param newKey
+ */
+ public void rename(String oldKey, String newKey) {
+ redisTemplate.rename(oldKey, newKey);
+ }
+
+ /**
+ * 仅当 newkey 不存在时,将 oldKey 改名为 newkey
+ *
+ * @param oldKey
+ * @param newKey
+ * @return
+ */
+ public Boolean renameIfAbsent(String oldKey, String newKey) {
+ return redisTemplate.renameIfAbsent(oldKey, newKey);
+ }
+
+ /**
+ * 返回 key 所储存的值的类型
+ *
+ * @param key
+ * @return
+ */
+ public DataType type(String key) {
+ return redisTemplate.type(key);
+ }
+
+ /** -------------------string相关操作--------------------- */
+
+ /**
+ * 将值 value 关联到 key ,并将 key 的过期时间设为 timeout
+ *
+ * @param key
+ * @param value
+ * @param timeout 过期时间
+ * @param unit 时间单位, 天:TimeUnit.DAYS 小时:TimeUnit.HOURS 分钟:TimeUnit.MINUTES
+ * 秒:TimeUnit.SECONDS 毫秒:TimeUnit.MILLISECONDS
+ */
+ public void set(String key, String value, long timeout, TimeUnit unit) {
+ redisTemplate.opsForValue().set(key, value, timeout, unit);
+ }
+
+ /**
+ * 获取指定 key 的值
+ *
+ * @param key
+ * @return
+ */
+ public String get(String key) {
+ return redisTemplate.opsForValue().get(key);
+ }
+
+ /**
+ * 返回 key 中字符串值的子字符
+ *
+ * @param key
+ * @param start
+ * @param end
+ * @return
+ */
+ public String getRange(String key, long start, long end) {
+ return redisTemplate.opsForValue().get(key, start, end);
+ }
+
+ /**
+ * 将给定 key 的值设为 value ,并返回 key 的旧值(old value)
+ *
+ * @param key
+ * @param value
+ * @return
+ */
+ public String getAndSet(String key, String value) {
+ return redisTemplate.opsForValue().getAndSet(key, value);
+ }
+
+ /**
+ * 对 key 所储存的字符串值,获取指定偏移量上的位(bit)
+ *
+ * @param key
+ * @param offset
+ * @return
+ */
+ public Boolean getBit(String key, long offset) {
+ return redisTemplate.opsForValue().getBit(key, offset);
+ }
+
+ /**
+ * 批量获取
+ *
+ * @param keys
+ * @return
+ */
+ public List multiGet(Collection keys) {
+ return redisTemplate.opsForValue().multiGet(keys);
+ }
+
+ /**
+ * 设置ASCII码, 字符串'a'的ASCII码是97, 转为二进制是'01100001', 此方法是将二进制第offset位值变为value
+ *
+ * @param key
+ * @param value 值,true为1, false为0
+ * @return
+ */
+ public boolean setBit(String key, long offset, boolean value) {
+ return redisTemplate.opsForValue().setBit(key, offset, value);
+ }
+
+ /**
+ * 只有在 key 不存在时设置 key 的值
+ *
+ * @param key
+ * @param value
+ * @return 之前已经存在返回false, 不存在返回true
+ */
+ public boolean setIfAbsent(String key, String value) {
+ return redisTemplate.opsForValue().setIfAbsent(key, value);
+ }
+
+ /**
+ * 只有在 key 不存在时设置 key 的值
+ *
+ * @param key
+ * @param value
+ * @return 之前已经存在返回false, 不存在返回true
+ */
+ public boolean setIfAbsent(String key, String value, long timeout, TimeUnit unit) {
+ return redisTemplate.opsForValue().setIfAbsent(key, value, timeout, unit);
+ }
+
+ /**
+ * 用 value 参数覆写给定 key 所储存的字符串值,从偏移量 offset 开始
+ *
+ * @param key
+ * @param value
+ * @param offset 从指定位置开始覆写
+ */
+ public void setRange(String key, String value, long offset) {
+ redisTemplate.opsForValue().set(key, value, offset);
+ }
+
+ /**
+ * 获取字符串的长度
+ *
+ * @param key
+ * @return
+ */
+ public Long size(String key) {
+ return redisTemplate.opsForValue().size(key);
+ }
+
+ /**
+ * 批量添加
+ *
+ * @param maps
+ */
+ public void multiSet(Map maps) {
+ redisTemplate.opsForValue().multiSet(maps);
+ }
+
+ /**
+ * 同时设置一个或多个 key-value 对,当且仅当所有给定 key 都不存在
+ *
+ * @param maps
+ * @return 之前已经存在返回false, 不存在返回true
+ */
+ public boolean multiSetIfAbsent(Map maps) {
+ return redisTemplate.opsForValue().multiSetIfAbsent(maps);
+ }
+
+ /**
+ * 增加(自增长), 负数则为自减
+ *
+ * @param key
+ * @param key
+ * @return
+ */
+ public Long increment(String key, long increment) {
+ return redisTemplate.opsForValue().increment(key, increment);
+ }
+
+ /**
+ * 追加到末尾
+ *
+ * @param key
+ * @param value
+ * @return
+ */
+ public Integer append(String key, String value) {
+ return redisTemplate.opsForValue().append(key, value);
+ }
+
+ /** -------------------hash相关操作------------------------- */
+
+ /**
+ * 获取存储在哈希表中指定字段的值
+ *
+ * @param key
+ * @param field
+ * @return
+ */
+ public Object hGet(String key, String field) {
+ return redisTemplate.opsForHash().get(key, field);
+ }
+
+ /**
+ * 获取所有给定字段的值
+ *
+ * @param key
+ * @return
+ */
+ public Map hGetAll(String key) {
+ return redisTemplate.opsForHash().entries(key);
+ }
+
+ /**
+ * 获取所有给定字段的值
+ *
+ * @param key
+ * @param fields
+ * @return
+ */
+ public List hMultiGet(String key, Collection fields) {
+ return redisTemplate.opsForHash().multiGet(key, fields);
+ }
+
+ public void hPut(String key, String hashKey, String value) {
+ redisTemplate.opsForHash().put(key, hashKey, value);
+ }
+
+ public void hPutAll(String key, Map maps) {
+ redisTemplate.opsForHash().putAll(key, maps);
+ }
+
+ /**
+ * 仅当hashKey不存在时才设置
+ *
+ * @param key
+ * @param hashKey
+ * @param value
+ * @return
+ */
+ public Boolean hPutIfAbsent(String key, String hashKey, String value) {
+ return redisTemplate.opsForHash().putIfAbsent(key, hashKey, value);
+ }
+
+ /**
+ * 删除一个或多个哈希表字段
+ *
+ * @param key
+ * @param fields
+ * @return
+ */
+ public Long hDelete(String key, Object... fields) {
+ return redisTemplate.opsForHash().delete(key, fields);
+ }
+
+ /**
+ * 查看哈希表 key 中,指定的字段是否存在
+ *
+ * @param key
+ * @param field
+ * @return
+ */
+ public boolean hExists(String key, String field) {
+ return redisTemplate.opsForHash().hasKey(key, field);
+ }
+
+ /**
+ * 为哈希表 key 中的指定字段的整数值加上增量 increment
+ *
+ * @param key
+ * @param field
+ * @param increment
+ * @return
+ */
+ public Long hIncrBy(String key, Object field, long increment) {
+ return redisTemplate.opsForHash().increment(key, field, increment);
+ }
+
+ /**
+ * 为哈希表 key 中的指定字段的整数值加上增量 increment
+ *
+ * @param key
+ * @param field
+ * @param delta
+ * @return
+ */
+ public Double hIncrByFloat(String key, Object field, double delta) {
+ return redisTemplate.opsForHash().increment(key, field, delta);
+ }
+
+ /**
+ * 获取所有哈希表中的字段
+ *
+ * @param key
+ * @return
+ */
+ public Set hKeys(String key) {
+ return redisTemplate.opsForHash().keys(key);
+ }
+
+ /**
+ * 获取哈希表中字段的数量
+ *
+ * @param key
+ * @return
+ */
+ public Long hSize(String key) {
+ return redisTemplate.opsForHash().size(key);
+ }
+
+ /**
+ * 获取哈希表中所有值
+ *
+ * @param key
+ * @return
+ */
+ public List hValues(String key) {
+ return redisTemplate.opsForHash().values(key);
+ }
+
+ /**
+ * 迭代哈希表中的键值对
+ *
+ * @param key
+ * @param options
+ * @return
+ */
+ public Cursor> hScan(String key, ScanOptions options) {
+ return redisTemplate.opsForHash().scan(key, options);
+ }
+
+ /** ------------------------list相关操作---------------------------- */
+
+ /**
+ * 通过索引获取列表中的元素
+ *
+ * @param key
+ * @param index
+ * @return
+ */
+ public String lIndex(String key, long index) {
+ return redisTemplate.opsForList().index(key, index);
+ }
+
+ /**
+ * 获取列表指定范围内的元素
+ *
+ * @param key
+ * @param start 开始位置, 0是开始位置
+ * @param end 结束位置, -1返回所有
+ * @return
+ */
+ public List lRange(String key, long start, long end) {
+ return redisTemplate.opsForList().range(key, start, end);
+ }
+
+ /**
+ * 存储在list头部
+ *
+ * @param key
+ * @param value
+ * @return
+ */
+ public Long lLeftPush(String key, String value) {
+ return redisTemplate.opsForList().leftPush(key, value);
+ }
+
+ /**
+ * 存储在list头部
+ *
+ * @param key
+ * @param value
+ * @return
+ */
+ public Long lLeftPushAll(String key, String... value) {
+ return redisTemplate.opsForList().leftPushAll(key, value);
+ }
+
+ /**
+ * 存储在list头部
+ *
+ * @param key
+ * @param value
+ * @return
+ */
+ public Long lLeftPushAll(String key, Collection value) {
+ return redisTemplate.opsForList().leftPushAll(key, value);
+ }
+
+ /**
+ * 当list存在的时候才加入
+ *
+ * @param key
+ * @param value
+ * @return
+ */
+ public Long lLeftPushIfPresent(String key, String value) {
+ return redisTemplate.opsForList().leftPushIfPresent(key, value);
+ }
+
+ /**
+ * 如果pivot存在,再pivot前面添加
+ *
+ * @param key
+ * @param pivot
+ * @param value
+ * @return
+ */
+ public Long lLeftPush(String key, String pivot, String value) {
+ return redisTemplate.opsForList().leftPush(key, pivot, value);
+ }
+
+ /**
+ * 存储在list尾部
+ *
+ * @param key
+ * @param value
+ * @return
+ */
+ public Long lRightPush(String key, String value) {
+ return redisTemplate.opsForList().rightPush(key, value);
+ }
+
+ /**
+ * 存储在list尾部
+ *
+ * @param key
+ * @param value
+ * @return
+ */
+ public Long lRightPushAll(String key, String... value) {
+ return redisTemplate.opsForList().rightPushAll(key, value);
+ }
+
+ /**
+ * 存储在list尾部
+ *
+ * @param key
+ * @param value
+ * @return
+ */
+ public Long lRightPushAll(String key, Collection value) {
+ return redisTemplate.opsForList().rightPushAll(key, value);
+ }
+
+ /**
+ * 为已存在的列表添加值
+ *
+ * @param key
+ * @param value
+ * @return
+ */
+ public Long lRightPushIfPresent(String key, String value) {
+ return redisTemplate.opsForList().rightPushIfPresent(key, value);
+ }
+
+ /**
+ * 在pivot元素的右边添加值
+ *
+ * @param key
+ * @param pivot
+ * @param value
+ * @return
+ */
+ public Long lRightPush(String key, String pivot, String value) {
+ return redisTemplate.opsForList().rightPush(key, pivot, value);
+ }
+
+ /**
+ * 通过索引设置列表元素的值
+ *
+ * @param key
+ * @param index 位置
+ * @param value
+ */
+ public void lSet(String key, long index, String value) {
+ redisTemplate.opsForList().set(key, index, value);
+ }
+
+ /**
+ * 移出并获取列表的第一个元素
+ *
+ * @param key
+ * @return 删除的元素
+ */
+ public String lLeftPop(String key) {
+ return redisTemplate.opsForList().leftPop(key);
+ }
+
+ /**
+ * 移出并获取列表的第一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止
+ *
+ * @param key
+ * @param timeout 等待时间
+ * @param unit 时间单位
+ * @return
+ */
+ public String lBLeftPop(String key, long timeout, TimeUnit unit) {
+ return redisTemplate.opsForList().leftPop(key, timeout, unit);
+ }
+
+ /**
+ * 移除并获取列表最后一个元素
+ *
+ * @param key
+ * @return 删除的元素
+ */
+ public String lRightPop(String key) {
+ return redisTemplate.opsForList().rightPop(key);
+ }
+
+ /**
+ * 移出并获取列表的最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止
+ *
+ * @param key
+ * @param timeout 等待时间
+ * @param unit 时间单位
+ * @return
+ */
+ public String lBRightPop(String key, long timeout, TimeUnit unit) {
+ return redisTemplate.opsForList().rightPop(key, timeout, unit);
+ }
+
+ /**
+ * 移除列表的最后一个元素,并将该元素添加到另一个列表并返回
+ *
+ * @param sourceKey
+ * @param destinationKey
+ * @return
+ */
+ public String lRightPopAndLeftPush(String sourceKey, String destinationKey) {
+ return redisTemplate.opsForList().rightPopAndLeftPush(sourceKey,
+ destinationKey);
+ }
+
+ /**
+ * 从列表中弹出一个值,将弹出的元素插入到另外一个列表中并返回它; 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止
+ *
+ * @param sourceKey
+ * @param destinationKey
+ * @param timeout
+ * @param unit
+ * @return
+ */
+ public String lBRightPopAndLeftPush(String sourceKey, String destinationKey,
+ long timeout, TimeUnit unit) {
+ return redisTemplate.opsForList().rightPopAndLeftPush(sourceKey,
+ destinationKey, timeout, unit);
+ }
+
+ /**
+ * 删除集合中值等于value得元素
+ *
+ * @param key
+ * @param index index=0, 删除所有值等于value的元素; index>0, 从头部开始删除第一个值等于value的元素;
+ * index<0, 从尾部开始删除第一个值等于value的元素;
+ * @param value
+ * @return
+ */
+ public Long lRemove(String key, long index, String value) {
+ return redisTemplate.opsForList().remove(key, index, value);
+ }
+
+ /**
+ * 裁剪list
+ *
+ * @param key
+ * @param start
+ * @param end
+ */
+ public void lTrim(String key, long start, long end) {
+ redisTemplate.opsForList().trim(key, start, end);
+ }
+
+ /**
+ * 获取列表长度
+ *
+ * @param key
+ * @return
+ */
+ public Long lLen(String key) {
+ return redisTemplate.opsForList().size(key);
+ }
+
+ /**
+ * 获取列表长度
+ *
+ * @param key
+ * @return
+ */
+ public List lAll(String key) {
+ Long size = lLen(key);
+ if (size.equals(0L)) {
+ return new ArrayList<>();
+ }
+ return redisTemplate.opsForList().range(key, 0, size);
+ }
+
+ /** --------------------set相关操作-------------------------- */
+
+ /**
+ * set添加元素
+ *
+ * @param key
+ * @param values
+ * @return
+ */
+ public Long sAdd(String key, String... values) {
+ return redisTemplate.opsForSet().add(key, values);
+ }
+
+ /**
+ * set移除元素
+ *
+ * @param key
+ * @param values
+ * @return
+ */
+ public Long sRemove(String key, Object... values) {
+ return redisTemplate.opsForSet().remove(key, values);
+ }
+
+ /**
+ * 移除并返回集合的一个随机元素
+ *
+ * @param key
+ * @return
+ */
+ public String sPop(String key) {
+ return redisTemplate.opsForSet().pop(key);
+ }
+
+ /**
+ * 将元素value从一个集合移到另一个集合
+ *
+ * @param key
+ * @param value
+ * @param destKey
+ * @return
+ */
+ public Boolean sMove(String key, String value, String destKey) {
+ return redisTemplate.opsForSet().move(key, value, destKey);
+ }
+
+ /**
+ * 获取集合的大小
+ *
+ * @param key
+ * @return
+ */
+ public Long sSize(String key) {
+ return redisTemplate.opsForSet().size(key);
+ }
+
+ /**
+ * 判断集合是否包含value
+ *
+ * @param key
+ * @param value
+ * @return
+ */
+ public Boolean sIsMember(String key, Object value) {
+ return redisTemplate.opsForSet().isMember(key, value);
+ }
+
+ /**
+ * 获取两个集合的交集
+ *
+ * @param key
+ * @param otherKey
+ * @return
+ */
+ public Set sIntersect(String key, String otherKey) {
+ return redisTemplate.opsForSet().intersect(key, otherKey);
+ }
+
+ /**
+ * 获取key集合与多个集合的交集
+ *
+ * @param key
+ * @param otherKeys
+ * @return
+ */
+ public Set sIntersect(String key, Collection otherKeys) {
+ return redisTemplate.opsForSet().intersect(key, otherKeys);
+ }
+
+ /**
+ * key集合与otherKey集合的交集存储到destKey集合中
+ *
+ * @param key
+ * @param otherKey
+ * @param destKey
+ * @return
+ */
+ public Long sIntersectAndStore(String key, String otherKey, String destKey) {
+ return redisTemplate.opsForSet().intersectAndStore(key, otherKey,
+ destKey);
+ }
+
+ /**
+ * key集合与多个集合的交集存储到destKey集合中
+ *
+ * @param key
+ * @param otherKeys
+ * @param destKey
+ * @return
+ */
+ public Long sIntersectAndStore(String key, Collection otherKeys,
+ String destKey) {
+ return redisTemplate.opsForSet().intersectAndStore(key, otherKeys,
+ destKey);
+ }
+
+ /**
+ * 获取两个集合的并集
+ *
+ * @param key
+ * @param otherKeys
+ * @return
+ */
+ public Set sUnion(String key, String otherKeys) {
+ return redisTemplate.opsForSet().union(key, otherKeys);
+ }
+
+ /**
+ * 获取key集合与多个集合的并集
+ *
+ * @param key
+ * @param otherKeys
+ * @return
+ */
+ public Set sUnion(String key, Collection otherKeys) {
+ return redisTemplate.opsForSet().union(key, otherKeys);
+ }
+
+ /**
+ * key集合与otherKey集合的并集存储到destKey中
+ *
+ * @param key
+ * @param otherKey
+ * @param destKey
+ * @return
+ */
+ public Long sUnionAndStore(String key, String otherKey, String destKey) {
+ return redisTemplate.opsForSet().unionAndStore(key, otherKey, destKey);
+ }
+
+ /**
+ * key集合与多个集合的并集存储到destKey中
+ *
+ * @param key
+ * @param otherKeys
+ * @param destKey
+ * @return
+ */
+ public Long sUnionAndStore(String key, Collection otherKeys,
+ String destKey) {
+ return redisTemplate.opsForSet().unionAndStore(key, otherKeys, destKey);
+ }
+
+ /**
+ * 获取两个集合的差集
+ *
+ * @param key
+ * @param otherKey
+ * @return
+ */
+ public Set sDifference(String key, String otherKey) {
+ return redisTemplate.opsForSet().difference(key, otherKey);
+ }
+
+ /**
+ * 获取key集合与多个集合的差集
+ *
+ * @param key
+ * @param otherKeys
+ * @return
+ */
+ public Set sDifference(String key, Collection otherKeys) {
+ return redisTemplate.opsForSet().difference(key, otherKeys);
+ }
+
+ /**
+ * key集合与otherKey集合的差集存储到destKey中
+ *
+ * @param key
+ * @param otherKey
+ * @param destKey
+ * @return
+ */
+ public Long sDifference(String key, String otherKey, String destKey) {
+ return redisTemplate.opsForSet().differenceAndStore(key, otherKey,
+ destKey);
+ }
+
+ /**
+ * key集合与多个集合的差集存储到destKey中
+ *
+ * @param key
+ * @param otherKeys
+ * @param destKey
+ * @return
+ */
+ public Long sDifference(String key, Collection otherKeys,
+ String destKey) {
+ return redisTemplate.opsForSet().differenceAndStore(key, otherKeys,
+ destKey);
+ }
+
+ /**
+ * 获取集合所有元素
+ *
+ * @param key
+ * @param key
+ * @return
+ */
+ public Set setMembers(String key) {
+ return redisTemplate.opsForSet().members(key);
+ }
+
+ /**
+ * 随机获取集合中的一个元素
+ *
+ * @param key
+ * @return
+ */
+ public String sRandomMember(String key) {
+ return redisTemplate.opsForSet().randomMember(key);
+ }
+
+ /**
+ * 随机获取集合中count个元素
+ *
+ * @param key
+ * @param count
+ * @return
+ */
+ public List sRandomMembers(String key, long count) {
+ return redisTemplate.opsForSet().randomMembers(key, count);
+ }
+
+ /**
+ * 随机获取集合中count个元素并且去除重复的
+ *
+ * @param key
+ * @param count
+ * @return
+ */
+ public Set sDistinctRandomMembers(String key, long count) {
+ return redisTemplate.opsForSet().distinctRandomMembers(key, count);
+ }
+
+ /**
+ * 获取集合
+ *
+ * @param key
+ * @param options
+ * @return
+ */
+ public Cursor sScan(String key, ScanOptions options) {
+ return redisTemplate.opsForSet().scan(key, options);
+ }
+
+ /**------------------zSet相关操作--------------------------------*/
+
+ /**
+ * 添加元素,有序集合是按照元素的score值由小到大排列
+ *
+ * @param key
+ * @param value
+ * @param score
+ * @return
+ */
+ public Boolean zAdd(String key, String value, double score) {
+ return redisTemplate.opsForZSet().add(key, value, score);
+ }
+
+ /**
+ * 有序集合
+ *
+ * @param key
+ * @param values
+ * @return
+ */
+ public Long zAdd(String key, Set> values) {
+ return redisTemplate.opsForZSet().add(key, values);
+ }
+
+ /**
+ * 移除有序集合
+ *
+ * @param key
+ * @param values
+ * @return
+ */
+ public Long zRemove(String key, Object... values) {
+ return redisTemplate.opsForZSet().remove(key, values);
+ }
+
+ /**
+ * 增加元素的score值,并返回增加后的值
+ *
+ * @param key
+ * @param value
+ * @param delta
+ * @return
+ */
+ public Double zIncrementScore(String key, String value, double delta) {
+ return redisTemplate.opsForZSet().incrementScore(key, value, delta);
+ }
+
+ /**
+ * 返回元素在集合的排名,有序集合是按照元素的score值由小到大排列
+ *
+ * @param key
+ * @param value
+ * @return 0表示第一位
+ */
+ public Long zRank(String key, Object value) {
+ return redisTemplate.opsForZSet().rank(key, value);
+ }
+
+ /**
+ * 返回元素在集合的排名,按元素的score值由大到小排列
+ *
+ * @param key
+ * @param value
+ * @return
+ */
+ public Long zReverseRank(String key, Object value) {
+ return redisTemplate.opsForZSet().reverseRank(key, value);
+ }
+
+ /**
+ * 获取集合的元素, 从小到大排序
+ *
+ * @param key
+ * @param start 开始位置
+ * @param end 结束位置, -1查询所有
+ * @return
+ */
+ public Set zRange(String key, long start, long end) {
+ return redisTemplate.opsForZSet().range(key, start, end);
+ }
+
+ /**
+ * 获取集合元素, 并且把score值也获取
+ *
+ * @param key
+ * @param start
+ * @param end
+ * @return
+ */
+ public Set> zRangeWithScores(String key, long start,
+ long end) {
+ return redisTemplate.opsForZSet().rangeWithScores(key, start, end);
+ }
+
+ /**
+ * 根据Score值查询集合元素
+ *
+ * @param key
+ * @param min 最小值
+ * @param max 最大值
+ * @return
+ */
+ public Set zRangeByScore(String key, double min, double max) {
+ return redisTemplate.opsForZSet().rangeByScore(key, min, max);
+ }
+
+ /**
+ * 根据Score值查询集合元素, 从小到大排序
+ *
+ * @param key
+ * @param min 最小值
+ * @param max 最大值
+ * @return
+ */
+ public Set> zRangeByScoreWithScores(String key,
+ double min, double max) {
+ return redisTemplate.opsForZSet().rangeByScoreWithScores(key, min, max);
+ }
+
+ /**
+ * 根据Score值查询集合元素, 从小到大排序
+ *
+ * @param key
+ * @param min
+ * @param max
+ * @param start
+ * @param end
+ * @return
+ */
+ public Set> zRangeByScoreWithScores(String key,
+ double min, double max, long start, long end) {
+ return redisTemplate.opsForZSet().rangeByScoreWithScores(key, min, max,
+ start, end);
+ }
+
+ /**
+ * 获取集合的元素, 从大到小排序
+ *
+ * @param key
+ * @param start
+ * @param end
+ * @return
+ */
+ public Set zReverseRange(String key, long start, long end) {
+ return redisTemplate.opsForZSet().reverseRange(key, start, end);
+ }
+
+ /**
+ * 获取集合的元素, 从大到小排序, 并返回score值
+ *
+ * @param key
+ * @param start
+ * @param end
+ * @return
+ */
+ public Set> zReverseRangeWithScores(String key,
+ long start, long end) {
+ return redisTemplate.opsForZSet().reverseRangeWithScores(key, start,
+ end);
+ }
+
+ /**
+ * 根据Score值查询集合元素, 从大到小排序
+ *
+ * @param key
+ * @param min
+ * @param max
+ * @return
+ */
+ public Set zReverseRangeByScore(String key, double min,
+ double max) {
+ return redisTemplate.opsForZSet().reverseRangeByScore(key, min, max);
+ }
+
+ /**
+ * 根据Score值查询集合元素, 从大到小排序
+ *
+ * @param key
+ * @param min
+ * @param max
+ * @return
+ */
+ public Set> zReverseRangeByScoreWithScores(
+ String key, double min, double max) {
+ return redisTemplate.opsForZSet().reverseRangeByScoreWithScores(key,
+ min, max);
+ }
+
+ /**
+ * 根据Score值查询集合元素, 从大到小排序
+ *
+ * @param key
+ * @param min
+ * @param max
+ * @param start
+ * @param end
+ * @return
+ */
+ public Set zReverseRangeByScore(String key, double min,
+ double max, long start, long end) {
+ return redisTemplate.opsForZSet().reverseRangeByScore(key, min, max,
+ start, end);
+ }
+
+ /**
+ * 根据score值获取集合元素数量
+ *
+ * @param key
+ * @param min
+ * @param max
+ * @return
+ */
+ public Long zCount(String key, double min, double max) {
+ return redisTemplate.opsForZSet().count(key, min, max);
+ }
+
+ /**
+ * 获取集合大小
+ *
+ * @param key
+ * @return
+ */
+ public Long zSize(String key) {
+ return redisTemplate.opsForZSet().size(key);
+ }
+
+ /**
+ * 获取集合大小
+ *
+ * @param key
+ * @return
+ */
+ public Long zZCard(String key) {
+ return redisTemplate.opsForZSet().zCard(key);
+ }
+
+ /**
+ * 获取集合中value元素的score值
+ *
+ * @param key
+ * @param value
+ * @return
+ */
+ public Double zScore(String key, Object value) {
+ return redisTemplate.opsForZSet().score(key, value);
+ }
+
+ /**
+ * 移除指定索引位置的成员
+ *
+ * @param key
+ * @param start
+ * @param end
+ * @return
+ */
+ public Long zRemoveRange(String key, long start, long end) {
+ return redisTemplate.opsForZSet().removeRange(key, start, end);
+ }
+
+ /**
+ * 根据指定的score值的范围来移除成员
+ *
+ * @param key
+ * @param min
+ * @param max
+ * @return
+ */
+ public Long zRemoveRangeByScore(String key, double min, double max) {
+ return redisTemplate.opsForZSet().removeRangeByScore(key, min, max);
+ }
+
+ /**
+ * 获取key和otherKey的并集并存储在destKey中
+ *
+ * @param key
+ * @param otherKey
+ * @param destKey
+ * @return
+ */
+ public Long zUnionAndStore(String key, String otherKey, String destKey) {
+ return redisTemplate.opsForZSet().unionAndStore(key, otherKey, destKey);
+ }
+
+ /**
+ * @param key
+ * @param otherKeys
+ * @param destKey
+ * @return
+ */
+ public Long zUnionAndStore(String key, Collection otherKeys,
+ String destKey) {
+ return redisTemplate.opsForZSet()
+ .unionAndStore(key, otherKeys, destKey);
+ }
+
+ /**
+ * 交集
+ *
+ * @param key
+ * @param otherKey
+ * @param destKey
+ * @return
+ */
+ public Long zIntersectAndStore(String key, String otherKey,
+ String destKey) {
+ return redisTemplate.opsForZSet().intersectAndStore(key, otherKey,
+ destKey);
+ }
+
+ /**
+ * 交集
+ *
+ * @param key
+ * @param otherKeys
+ * @param destKey
+ * @return
+ */
+ public Long zIntersectAndStore(String key, Collection otherKeys,
+ String destKey) {
+ return redisTemplate.opsForZSet().intersectAndStore(key, otherKeys,
+ destKey);
+ }
+
+ /**
+ * @param key
+ * @param options
+ * @return
+ */
+ public Cursor> zScan(String key, ScanOptions options) {
+ return redisTemplate.opsForZSet().scan(key, options);
+ }
+
+ /**
+ * 发布订阅
+ *
+ * @param channel
+ * @param message
+ */
+ public void convertAndSend(String channel, Object message) {
+ redisTemplate.convertAndSend(channel, message);
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/platform/common/shiro/LoginUser.java b/src/main/java/com/platform/common/shiro/LoginUser.java
new file mode 100644
index 0000000..219a05a
--- /dev/null
+++ b/src/main/java/com/platform/common/shiro/LoginUser.java
@@ -0,0 +1,33 @@
+package com.platform.common.shiro;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+/**
+ * 登录用户身份权限
+ */
+@Data
+@Accessors(chain = true) // 链式调用
+public class LoginUser {
+
+ /**
+ * 用户唯一标识
+ */
+ private String token;
+
+ /**
+ * 用户ID
+ */
+ private Long userId;
+
+ /**
+ * 用户手机号
+ */
+ private String phone;
+
+ /**
+ * 登录IP地址
+ */
+ private String ipAddr;
+
+}
diff --git a/src/main/java/com/platform/common/shiro/Md5Utils.java b/src/main/java/com/platform/common/shiro/Md5Utils.java
new file mode 100644
index 0000000..e7e47a3
--- /dev/null
+++ b/src/main/java/com/platform/common/shiro/Md5Utils.java
@@ -0,0 +1,49 @@
+package com.platform.common.shiro;
+
+import cn.hutool.core.util.RandomUtil;
+import cn.hutool.crypto.SecureUtil;
+import org.apache.shiro.crypto.hash.Md5Hash;
+import org.apache.shiro.util.ByteSource;
+
+/**
+ * Md5Util
+ */
+public class Md5Utils {
+
+ /**
+ * credentials
+ */
+ public static final String credentials(String password, String salt) {
+ //盐:为了即使相同的密码不同的盐加密后的结果也不同
+ ByteSource byteSalt = ByteSource.Util.bytes(salt);
+ Md5Hash result = new Md5Hash(password, byteSalt);
+ return result.toString();
+ }
+
+ /**
+ * md5
+ */
+ public static final String md5(String str) {
+ return SecureUtil.md5(str);
+ }
+
+ /**
+ * 加密盐
+ */
+ public static String salt() {
+ return RandomUtil.randomString(4);
+ }
+
+ /**
+ * 基础密码
+ */
+ private static final String baseStr = "abcdefghgkmnprstwxyz123456789";
+
+ /**
+ * 密码
+ */
+ public static String password() {
+ return RandomUtil.randomString(baseStr, 8);
+ }
+
+}
diff --git a/src/main/java/com/platform/common/shiro/ShiroConfiguration.java b/src/main/java/com/platform/common/shiro/ShiroConfiguration.java
new file mode 100644
index 0000000..cd343e3
--- /dev/null
+++ b/src/main/java/com/platform/common/shiro/ShiroConfiguration.java
@@ -0,0 +1,110 @@
+package com.platform.common.shiro;
+
+import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
+import org.apache.shiro.mgt.SecurityManager;
+import org.apache.shiro.spring.LifecycleBeanPostProcessor;
+import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
+import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
+import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
+import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import javax.servlet.Filter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * ShiroConfiguration
+ */
+@Configuration
+public class ShiroConfiguration {
+
+ /**
+ * 下面两个方法对 注解权限起作用有很大的关系,请把这两个方法,放在配置的最上面
+ */
+ @Bean(name = "lifecycleBeanPostProcessor")
+ public static LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
+ return new LifecycleBeanPostProcessor();
+ }
+
+ /**
+ * 身份认证Realm,此处的注入不可以缺少。否则会在UserRealm中注入对象会报空指针.
+ * 将自己的验证方式加入容器
+ */
+ @Bean
+ public ShiroRealm shiroRealm() {
+ ShiroRealm realm = new ShiroRealm();
+ realm.setCredentialsMatcher(hashedCredentialsMatcher());
+ return realm;
+ }
+
+ /**
+ * 配置shiro session 的一个管理器
+ */
+ @Bean
+ public DefaultWebSessionManager sessionManager() {
+ DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
+ sessionManager.setSessionListeners(new ArrayList<>());
+ return sessionManager;
+ }
+
+ /**
+ * 核心的安全事务管理器
+ * 设置realm、cacheManager等
+ */
+ @Bean
+ public SecurityManager securityManager() {
+ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
+ //设置realm
+ securityManager.setRealm(shiroRealm());
+ // 设置sessionManager
+ securityManager.setSessionManager(sessionManager());
+ return securityManager;
+ }
+
+ /**
+ * 开启shiro aop注解支持.
+ * 使用代理方式;所以需要开启代码支持;否则@RequiresRoles等注解无法生效
+ */
+ @Bean
+ public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
+ AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
+ authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
+ return authorizationAttributeSourceAdvisor;
+ }
+
+ /**
+ * 哈希密码比较器。在myShiroRealm中作用参数使用
+ * 登陆时会比较用户输入的密码,跟数据库密码配合盐值salt解密后是否一致。
+ */
+ @Bean
+ public HashedCredentialsMatcher hashedCredentialsMatcher() {
+ HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
+ hashedCredentialsMatcher.setHashAlgorithmName("md5");//散列算法:这里使用md5算法;
+ hashedCredentialsMatcher.setHashIterations(1);//散列的次数,比如散列两次,相当于 md5( md5(""));
+ return hashedCredentialsMatcher;
+ }
+
+ @Bean
+ public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
+ ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
+ //设置安全管理器
+ shiroFilterFactoryBean.setSecurityManager(securityManager);
+ //oauth过滤
+ Map filters = new HashMap<>(16);
+ filters.put("oauth2", new ShiroTokenFilter());
+ shiroFilterFactoryBean.setFilters(filters);
+ //权限控制map
+ Map filterChainDefinitionMap = new LinkedHashMap<>();
+ // 自定义拦截全部写到下面↓↓↓
+ // 免登录接口,增加@IgnoreAuth注解
+ // 自定义拦截全部写到上面↑↑↑
+ filterChainDefinitionMap.put("/**", "oauth2");
+ shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
+ return shiroFilterFactoryBean;
+ }
+
+}
diff --git a/src/main/java/com/platform/common/shiro/ShiroLoginAuth.java b/src/main/java/com/platform/common/shiro/ShiroLoginAuth.java
new file mode 100644
index 0000000..b7521fa
--- /dev/null
+++ b/src/main/java/com/platform/common/shiro/ShiroLoginAuth.java
@@ -0,0 +1,30 @@
+package com.platform.common.shiro;
+
+import lombok.Data;
+import org.apache.shiro.authc.AuthenticationToken;
+
+/**
+ * token
+ */
+@Data
+public class ShiroLoginAuth implements AuthenticationToken {
+
+ private String phone;
+ private char[] password;
+
+ public ShiroLoginAuth(String phone, String password) {
+ this.phone = phone;
+ this.password = password.toCharArray();
+ }
+
+ @Override
+ public Object getPrincipal() {
+ return phone;
+ }
+
+ @Override
+ public Object getCredentials() {
+ return password;
+ }
+
+}
diff --git a/src/main/java/com/platform/common/shiro/ShiroLoginPhone.java b/src/main/java/com/platform/common/shiro/ShiroLoginPhone.java
new file mode 100644
index 0000000..2022591
--- /dev/null
+++ b/src/main/java/com/platform/common/shiro/ShiroLoginPhone.java
@@ -0,0 +1,28 @@
+package com.platform.common.shiro;
+
+import lombok.Data;
+import org.apache.shiro.authc.AuthenticationToken;
+
+/**
+ * token
+ */
+@Data
+public class ShiroLoginPhone implements AuthenticationToken {
+
+ private String phone;
+
+ public ShiroLoginPhone(String phone) {
+ this.phone = phone;
+ }
+
+ @Override
+ public Object getPrincipal() {
+ return phone;
+ }
+
+ @Override
+ public Object getCredentials() {
+ return phone;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/platform/common/shiro/ShiroLoginToken.java b/src/main/java/com/platform/common/shiro/ShiroLoginToken.java
new file mode 100644
index 0000000..f688e7f
--- /dev/null
+++ b/src/main/java/com/platform/common/shiro/ShiroLoginToken.java
@@ -0,0 +1,26 @@
+package com.platform.common.shiro;
+
+import org.apache.shiro.authc.AuthenticationToken;
+
+/**
+ * token
+ */
+public class ShiroLoginToken implements AuthenticationToken {
+ private static final long serialVersionUID = 1L;
+
+ private String token;
+
+ public ShiroLoginToken(String token) {
+ this.token = token;
+ }
+
+ @Override
+ public Object getPrincipal() {
+ return token;
+ }
+
+ @Override
+ public Object getCredentials() {
+ return token;
+ }
+}
diff --git a/src/main/java/com/platform/common/shiro/ShiroRealm.java b/src/main/java/com/platform/common/shiro/ShiroRealm.java
new file mode 100644
index 0000000..70415f1
--- /dev/null
+++ b/src/main/java/com/platform/common/shiro/ShiroRealm.java
@@ -0,0 +1,129 @@
+package com.platform.common.shiro;
+
+import cn.hutool.core.util.RandomUtil;
+import com.platform.common.enums.ResultCodeEnum;
+import com.platform.common.enums.YesOrNoEnum;
+import com.platform.common.exception.LoginException;
+import com.platform.common.utils.IpUtils;
+import com.platform.common.utils.ServletUtils;
+import com.platform.modules.auth.service.TokenService;
+import com.platform.modules.chat.domain.ChatUser;
+import com.platform.modules.chat.service.ChatUserService;
+import org.apache.shiro.authc.AuthenticationException;
+import org.apache.shiro.authc.AuthenticationInfo;
+import org.apache.shiro.authc.AuthenticationToken;
+import org.apache.shiro.authc.SimpleAuthenticationInfo;
+import org.apache.shiro.authz.AuthorizationInfo;
+import org.apache.shiro.authz.SimpleAuthorizationInfo;
+import org.apache.shiro.realm.AuthorizingRealm;
+import org.apache.shiro.subject.PrincipalCollection;
+import org.apache.shiro.util.ByteSource;
+import org.springframework.context.annotation.Lazy;
+
+import javax.annotation.Resource;
+
+/**
+ * ShiroRealm
+ */
+public class ShiroRealm extends AuthorizingRealm {
+
+ @Lazy
+ @Resource
+ private TokenService tokenService;
+
+ @Lazy
+ @Resource
+ private ChatUserService chatUserService;
+
+ /**
+ * 提供用户信息,返回权限信息
+ */
+ @Override
+ protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
+ Object object = ShiroUtils.getSubject().getPrincipal();
+ if (object == null) {
+ return null;
+ }
+ // 后台管理
+ if (object instanceof LoginUser) {
+ SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
+ return info;
+ }
+ return null;
+ }
+
+ /**
+ * 必须重写此方法,不然会报错
+ */
+ @Override
+ public boolean supports(AuthenticationToken authenticationToken) {
+ return authenticationToken instanceof ShiroLoginToken
+ || authenticationToken instanceof ShiroLoginAuth
+ || authenticationToken instanceof ShiroLoginPhone;
+ }
+
+ /**
+ * 身份认证/登录,验证用户是不是拥有相应的身份; 用于登陆认证
+ */
+ @Override
+ protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
+ // token
+ if (authenticationToken instanceof ShiroLoginToken) {
+ String token = (String) authenticationToken.getPrincipal();
+ LoginUser loginUser = tokenService.queryByToken(token);
+ if (loginUser == null) {
+ throw new LoginException(ResultCodeEnum.UNAUTHORIZED);
+ }
+ // 加密盐
+ String salt = Md5Utils.salt();
+ // 对token加密
+ String credentials = Md5Utils.credentials(token, salt);
+ return new SimpleAuthenticationInfo(loginUser, credentials, ByteSource.Util.bytes(salt), getName());
+ }
+ // 手机+密码登录
+ if (authenticationToken instanceof ShiroLoginAuth) {
+ ShiroLoginAuth token = (ShiroLoginAuth) authenticationToken;
+ return makeLoginUser(token.getPhone(), true);
+ }
+ // 手机+验证码登录
+ if (authenticationToken instanceof ShiroLoginPhone) {
+ ShiroLoginPhone token = (ShiroLoginPhone) authenticationToken;
+ return makeLoginUser(token.getPhone(), false);
+ }
+ return null;
+ }
+
+ /**
+ * 组装登录对象
+ */
+ private SimpleAuthenticationInfo makeLoginUser(String phone, boolean isPassword) {
+ // 查询用户
+ ChatUser chatUser = chatUserService.queryByPhone(phone);
+ // 处理用户
+ if (chatUser == null) {
+ throw new AuthenticationException("手机号或密码不正确"); // 手机不存在
+ }
+ // 查询权限
+ LoginUser loginUser = new LoginUser()
+ .setUserId(chatUser.getUserId())
+ .setPhone(chatUser.getPhone())
+ .setIpAddr(IpUtils.getIpAddr(ServletUtils.getRequest()))
+ .setToken(RandomUtil.randomString(32));
+ String credentials = chatUser.getPassword();
+ String salt = chatUser.getSalt();
+ if (!isPassword) {
+ // 加密盐
+ salt = Md5Utils.salt();
+ // 对token加密
+ credentials = Md5Utils.credentials(phone, salt);
+ }
+ // 登录
+ return new SimpleAuthenticationInfo(loginUser, credentials, ByteSource.Util.bytes(salt), getName());
+ }
+
+ @Override
+ public boolean isPermitted(PrincipalCollection principals, String permission) {
+ return super.isPermitted(principals, permission);
+ }
+
+}
diff --git a/src/main/java/com/platform/common/shiro/ShiroTokenFilter.java b/src/main/java/com/platform/common/shiro/ShiroTokenFilter.java
new file mode 100644
index 0000000..128f51d
--- /dev/null
+++ b/src/main/java/com/platform/common/shiro/ShiroTokenFilter.java
@@ -0,0 +1,126 @@
+package com.platform.common.shiro;
+
+import cn.hutool.json.JSONUtil;
+import com.platform.common.aspectj.IgnoreAuth;
+import com.platform.common.constant.HeadConstant;
+import com.platform.common.enums.ResultCodeEnum;
+import com.platform.common.exception.BaseException;
+import com.platform.common.exception.LoginException;
+import com.platform.common.web.domain.AjaxResult;
+import lombok.SneakyThrows;
+import org.apache.shiro.authc.AuthenticationException;
+import org.apache.shiro.authc.AuthenticationToken;
+import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
+import org.springframework.util.StringUtils;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.context.WebApplicationContext;
+import org.springframework.web.method.HandlerMethod;
+import org.springframework.web.servlet.HandlerExecutionChain;
+import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
+import org.springframework.web.servlet.support.RequestContextUtils;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+
+/**
+ * auth2过滤器
+ */
+public class ShiroTokenFilter extends AuthenticatingFilter {
+
+ @Override
+ protected AuthenticationToken createToken(ServletRequest request, ServletResponse servletResponse) {
+ //获取请求token
+ ShiroLoginToken token = getToken(request);
+ if (token == null) {
+ return null;
+ }
+ return token;
+ }
+
+ @SneakyThrows
+ @Override
+ protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
+ HttpServletRequest httpServletRequest = (HttpServletRequest) request;
+ if (RequestMethod.OPTIONS.name().equalsIgnoreCase(httpServletRequest.getMethod())) {
+ return true;
+ }
+ if ("/favicon.ico".equals(httpServletRequest.getRequestURI())) {
+ return error(request, response, ResultCodeEnum.SUCCESS);
+ }
+ try {
+ WebApplicationContext ctx = RequestContextUtils.findWebApplicationContext(httpServletRequest);
+ RequestMappingHandlerMapping mapping = ctx.getBean(RequestMappingHandlerMapping.class);
+ HandlerExecutionChain handler = mapping.getHandler(httpServletRequest);
+ if (handler == null) {
+ return true;
+ }
+ Annotation[] declaredAnnotations = ((HandlerMethod) handler.getHandler()).getMethod().getDeclaredAnnotations();
+ if (declaredAnnotations.length != 0) {
+ for (Annotation annotation : declaredAnnotations) {
+ if (IgnoreAuth.class.equals(annotation.annotationType())) {
+ return true;
+ }
+ }
+ }
+ } catch (BaseException e) {
+ return error(request, response, e.getResultCode());
+ } catch (Exception e) {
+ return error(request, response, ResultCodeEnum.FAIL);
+ }
+ return false;
+ }
+
+ private boolean error(ServletRequest request, ServletResponse response, ResultCodeEnum resultCode) {
+ HttpServletRequest httpServletRequest = (HttpServletRequest) request;
+ try {
+ // 请求转发401controller
+ httpServletRequest.getRequestDispatcher("/error/" + resultCode.getCode()).forward(request, response);
+ } catch (Exception e) {
+ }
+ return true;
+ }
+
+ @Override
+ protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
+ // 请求token
+ ShiroLoginToken token = getToken(request);
+ if (token == null) {
+ return error(response, ResultCodeEnum.UNAUTHORIZED, null);
+ }
+ try {
+ getSubject(request, response).login(token);
+ return true;
+ } catch (LoginException e) {
+ return error(response, ResultCodeEnum.UNAUTHORIZED, e.getMessage());
+ } catch (AuthenticationException e) {
+ return error(response, ResultCodeEnum.UNAUTHORIZED, null);
+ }
+ }
+
+ private boolean error(ServletResponse response, ResultCodeEnum resultCode, String msg) throws IOException {
+ HttpServletResponse httpResponse = (HttpServletResponse) response;
+ httpResponse.setContentType("application/json;charset=utf-8");
+ httpResponse.getWriter().print(JSONUtil.toJsonStr(AjaxResult.result(resultCode, msg)));
+ return false;
+ }
+
+ /**
+ * 请求的token
+ */
+ private ShiroLoginToken getToken(ServletRequest request) {
+ HttpServletRequest httpRequest = (HttpServletRequest) request;
+ String token = httpRequest.getHeader(HeadConstant.TOKEN_HEADER_ADMIN);
+ if (StringUtils.isEmpty(token)) {
+ token = httpRequest.getParameter(HeadConstant.TOKEN_HEADER_ADMIN);
+ }
+ if (!StringUtils.isEmpty(token)) {
+ return new ShiroLoginToken(token);
+ }
+ return null;
+ }
+
+}
diff --git a/src/main/java/com/platform/common/shiro/ShiroUtils.java b/src/main/java/com/platform/common/shiro/ShiroUtils.java
new file mode 100644
index 0000000..0cd81a1
--- /dev/null
+++ b/src/main/java/com/platform/common/shiro/ShiroUtils.java
@@ -0,0 +1,45 @@
+
+package com.platform.common.shiro;
+
+import com.platform.common.constant.HeadConstant;
+import com.platform.common.utils.ServletUtils;
+import org.apache.shiro.SecurityUtils;
+import org.apache.shiro.subject.Subject;
+
+/**
+ * Shiro工具类
+ */
+public class ShiroUtils {
+
+ public static Subject getSubject() {
+ return SecurityUtils.getSubject();
+ }
+
+ public static LoginUser getLoginUser() {
+ return (LoginUser) getSubject().getPrincipal();
+ }
+
+ /**
+ * 是否登录
+ */
+ public static boolean isLogin() {
+ return getLoginUser() != null;
+ }
+
+ public static String getToken() {
+ LoginUser loginUser = getLoginUser();
+ if (loginUser != null) {
+ return loginUser.getToken();
+ }
+ return ServletUtils.getRequest().getHeader(HeadConstant.TOKEN_HEADER_ADMIN);
+ }
+
+ public static String getPhone() {
+ return getLoginUser().getPhone();
+ }
+
+ public static Long getUserId() {
+ return getLoginUser().getUserId();
+ }
+
+}
diff --git a/src/main/java/com/platform/common/upload/config/UploadConfig.java b/src/main/java/com/platform/common/upload/config/UploadConfig.java
new file mode 100644
index 0000000..16d8663
--- /dev/null
+++ b/src/main/java/com/platform/common/upload/config/UploadConfig.java
@@ -0,0 +1,45 @@
+package com.platform.common.upload.config;
+
+import lombok.Data;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+/**
+ * 文件上传
+ */
+@Component
+@Data
+public class UploadConfig {
+
+ /**
+ * 服务端域名
+ */
+ @Value("${upload.serverUrl}")
+ private String serverUrl;
+ /**
+ * accessKey
+ */
+ @Value("${upload.accessKey}")
+ private String accessKey;
+ /**
+ * secretKey
+ */
+ @Value("${upload.secretKey}")
+ private String secretKey;
+ /**
+ * bucket
+ */
+ @Value("${upload.bucket}")
+ private String bucket;
+ /**
+ * region
+ */
+ @Value("${upload.region}")
+ private String region;
+ /**
+ * 封面
+ */
+ @Value("${upload.post}")
+ private String post;
+
+}
diff --git a/src/main/java/com/platform/common/upload/enums/UploadTypeEnum.java b/src/main/java/com/platform/common/upload/enums/UploadTypeEnum.java
new file mode 100644
index 0000000..ac0a596
--- /dev/null
+++ b/src/main/java/com/platform/common/upload/enums/UploadTypeEnum.java
@@ -0,0 +1,53 @@
+package com.platform.common.upload.enums;
+
+import com.fasterxml.jackson.annotation.JsonValue;
+import lombok.Getter;
+
+/**
+ * 上传类型枚举
+ */
+@Getter
+public enum UploadTypeEnum {
+
+ /**
+ * 阿里oss
+ */
+ OSS("oss", "阿里oss"),
+
+ /**
+ * 腾讯cos
+ */
+ COS("cos", "腾讯cos"),
+
+ /**
+ * 七牛kodo
+ */
+ KODO("kodo", "七牛kodo"),
+
+ /**
+ * FAST
+ */
+ FAST("fast", "FAST_DFS"),
+
+ /**
+ * MINIO
+ */
+ MINIO("minio", "MINIO"),
+
+ /**
+ * 本地local
+ */
+ LOCAL("local", "本地上传"),
+
+ ;
+
+ @JsonValue
+ private final String code;
+ private final String info;
+
+ UploadTypeEnum(String code, String info) {
+ this.code = code;
+ this.info = info;
+ }
+
+}
diff --git a/src/main/java/com/platform/common/upload/service/UploadService.java b/src/main/java/com/platform/common/upload/service/UploadService.java
new file mode 100644
index 0000000..8fb6da2
--- /dev/null
+++ b/src/main/java/com/platform/common/upload/service/UploadService.java
@@ -0,0 +1,54 @@
+package com.platform.common.upload.service;
+
+import cn.hutool.core.lang.Dict;
+import com.platform.common.upload.vo.UploadFileVo;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.File;
+import java.io.InputStream;
+
+/**
+ * 上传服务
+ */
+public interface UploadService {
+
+ /**
+ * 获取服务端域名
+ */
+ String getServerUrl();
+
+ /**
+ * 获取上传凭证
+ */
+ Dict getToken(String fileType);
+
+ /**
+ * 文件上传
+ */
+ UploadFileVo uploadFile(MultipartFile file);
+
+ /**
+ * 文件上传
+ */
+ UploadFileVo uploadFile(MultipartFile file, String folder);
+
+ /**
+ * 文件上传
+ */
+ UploadFileVo uploadFile(File file);
+
+ /**
+ * 文件上传
+ */
+ UploadFileVo uploadFile(File file, String folder);
+
+ /**
+ * 获取文件流
+ */
+ InputStream getInputStream(String urlPath);
+
+ /**
+ * 删除本地文件
+ */
+ boolean delFile(File file);
+}
diff --git a/src/main/java/com/platform/common/upload/service/impl/UploadBaseService.java b/src/main/java/com/platform/common/upload/service/impl/UploadBaseService.java
new file mode 100644
index 0000000..8a5e78c
--- /dev/null
+++ b/src/main/java/com/platform/common/upload/service/impl/UploadBaseService.java
@@ -0,0 +1,171 @@
+package com.platform.common.upload.service.impl;
+
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.io.FileTypeUtil;
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.io.file.FileNameUtil;
+import cn.hutool.core.util.IdUtil;
+import com.platform.common.upload.vo.UploadFileVo;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.util.StringUtils;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.File;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+/**
+ * 基础上传
+ */
+@Slf4j
+public class UploadBaseService {
+
+ /**
+ * 获取文件名称
+ */
+ protected static String getFileName(MultipartFile file) {
+ String fileName = file.getOriginalFilename();
+ if (StringUtils.isEmpty(fileName)) {
+ // 随机名称
+ fileName = IdUtil.objectId() + "." + getFileType(file);
+ }
+ return fileName;
+ }
+
+ /**
+ * 获取文件名称
+ */
+ protected static String getFileName(File file) {
+ String fileName = file.getName();
+ if (StringUtils.isEmpty(fileName)) {
+ // 随机名称
+ fileName = IdUtil.objectId() + "." + getFileType(file);
+ }
+ return fileName;
+ }
+
+ /**
+ * 获取文件全名
+ */
+ protected static String getFileKey(MultipartFile file) {
+ return IdUtil.objectId() + "." + getFileType(file);
+ }
+
+ /**
+ * 获取文件全名
+ */
+ protected static String getFileKey(MultipartFile file, String folder) {
+ String fileKey = IdUtil.objectId() + "." + getFileType(file);
+ if (StringUtils.isEmpty(folder)) {
+ return fileKey;
+ }
+ return folder + "/" + fileKey;
+ }
+
+ /**
+ * 获取文件全名
+ */
+ protected static String getFileKey(File file) {
+ return IdUtil.objectId() + "." + getFileType(file);
+ }
+
+ /**
+ * 获取文件全名
+ */
+ protected static String getFileKey(File file, String folder) {
+ String fileKey = IdUtil.objectId() + "." + getFileType(file);
+ if (StringUtils.isEmpty(folder)) {
+ return fileKey;
+ }
+ return folder + "/" + fileKey;
+ }
+
+ /**
+ * 获取文件全名
+ */
+ protected static String getFileKey(String uploadPath, String fileType) {
+ // 文件路径
+ String filePath = DateUtil.format(DateUtil.date(), "yyyy/MM/dd");
+ // 生成文件夹
+ FileUtil.mkdir(uploadPath + FileNameUtil.UNIX_SEPARATOR + filePath);
+ return filePath + FileNameUtil.UNIX_SEPARATOR + IdUtil.objectId() + "." + fileType;
+ }
+
+ /**
+ * 获取文件全名
+ */
+ protected static String getFileType(MultipartFile file) {
+ // 文件扩展名
+ String fileType = FileNameUtil.extName(file.getOriginalFilename());
+ if (StringUtils.isEmpty(fileType)) {
+ // 文件后缀
+ try {
+ fileType = FileTypeUtil.getType(file.getInputStream());
+ } catch (Exception e) {
+ log.error(e.getMessage(), e);
+ }
+ }
+ return fileType;
+ }
+
+ /**
+ * 获取文件全名
+ */
+ protected static String getFileType(File file) {
+ // 文件扩展名
+ String fileType = FileNameUtil.extName(file.getName());
+ if (StringUtils.isEmpty(fileType)) {
+ // 文件后缀
+ fileType = FileTypeUtil.getType(file);
+ }
+ if (StringUtils.isEmpty(fileType)) {
+ fileType = "txt";
+ }
+ return fileType;
+ }
+
+ /**
+ * 获取文件流
+ */
+ public InputStream getInputStream(String urlPath) {
+ try {
+ URL url = new URL(urlPath);
+ HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
+ // 设置网络连接超时时间
+ httpURLConnection.setConnectTimeout(5000);
+ // 设置应用程序要从网络连接读取数据
+ httpURLConnection.setDoInput(true);
+ // 从服务器返回一个输入流
+ return httpURLConnection.getInputStream();
+ } catch (Exception e) {
+ log.error(e.getMessage(), e);
+ throw new RuntimeException("获取文件流失败");
+ }
+ }
+
+ /**
+ * 封装对象
+ */
+ protected static UploadFileVo format(String fileName, String serverUrl, String fileKey, String fileType) {
+ // 服务器地址
+ UploadFileVo fileVo = new UploadFileVo()
+ .setFileName(fileName)
+ .setFullPath(serverUrl + FileNameUtil.UNIX_SEPARATOR + fileKey)
+ .setFileType(fileType);
+ return fileVo;
+ }
+
+ /**
+ * 删除本地文件
+ */
+ public boolean delFile(File file) {
+ try {
+ return FileUtil.del(file);
+ } catch (Exception e) {
+ log.error(e.getMessage(), e);
+ throw new RuntimeException("删除上传失败");
+ }
+ }
+
+}
diff --git a/src/main/java/com/platform/common/upload/service/impl/UploadCosServiceImpl.java b/src/main/java/com/platform/common/upload/service/impl/UploadCosServiceImpl.java
new file mode 100644
index 0000000..90f4117
--- /dev/null
+++ b/src/main/java/com/platform/common/upload/service/impl/UploadCosServiceImpl.java
@@ -0,0 +1,154 @@
+package com.platform.common.upload.service.impl;
+
+import cn.hutool.core.codec.Base64;
+import cn.hutool.core.date.DatePattern;
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.lang.Dict;
+import cn.hutool.core.util.IdUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.crypto.SecureUtil;
+import cn.hutool.json.JSONArray;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+import com.platform.common.upload.config.UploadConfig;
+import com.platform.common.upload.enums.UploadTypeEnum;
+import com.platform.common.upload.service.UploadService;
+import com.platform.common.upload.vo.UploadFileVo;
+import com.qcloud.cos.COSClient;
+import com.qcloud.cos.auth.BasicCOSCredentials;
+import com.qcloud.cos.model.ObjectMetadata;
+import com.qcloud.cos.model.PutObjectRequest;
+import com.qcloud.cos.region.Region;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.stereotype.Service;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.annotation.Resource;
+import java.io.File;
+import java.io.InputStream;
+
+/**
+ * 腾讯云上传
+ */
+@Slf4j
+@Service("uploadCosService")
+@Configuration
+@ConditionalOnProperty(prefix = "upload", name = "uploadType", havingValue = "cos")
+public class UploadCosServiceImpl extends UploadBaseService implements UploadService {
+
+ @Resource
+ private UploadConfig uploadConfig;
+
+ /**
+ * 初始化cos
+ */
+ private COSClient initCOS() {
+ return new COSClient(new BasicCOSCredentials(uploadConfig.getAccessKey(), uploadConfig.getSecretKey()),
+ new com.qcloud.cos.ClientConfig(new Region(uploadConfig.getRegion())));
+ }
+
+ @Override
+ public String getServerUrl() {
+ return uploadConfig.getServerUrl();
+ }
+
+ @Override
+ public Dict getToken(String fileType) {
+ // 1、默认固定值
+ Long expire = 1800L;
+ String algorithm = "sha1";
+ String accessKey = uploadConfig.getAccessKey();
+ String post = uploadConfig.getPost();
+ String serverUrl = uploadConfig.getServerUrl();
+ // 2、生成KeyTime
+ Long startTime = DateUtil.currentSeconds();
+ Long endTime = startTime + expire;
+ String keyTime = StrUtil.format("{};{}", startTime, endTime);
+ // 3、构造“策略”(Policy)
+ JSONArray conditions = new JSONArray()
+ .set(new JSONObject().set("q-sign-algorithm", algorithm))
+ .set(new JSONObject().set("q-ak", accessKey))
+ .set(new JSONObject().set("q-sign-time", keyTime));
+ JSONObject policyObj = new JSONObject()
+ .set("expiration", DateUtil.format(DateUtil.date(endTime * 1000), DatePattern.UTC_MS_PATTERN))
+ .set("conditions", conditions);
+ String policy = JSONUtil.toJsonStr(policyObj);
+ // 4、生成 SignKey
+ String signKey = SecureUtil.hmacSha1(uploadConfig.getSecretKey()).digestHex(keyTime);
+ // 5、生成 Signature
+ String signature = SecureUtil.hmacSha1(signKey).digestHex(SecureUtil.sha1(policy));
+ return Dict.create()
+ .set("uploadType", UploadTypeEnum.COS)
+ .set("serverUrl", serverUrl)
+ .set("fileName", IdUtil.objectId() + "." + fileType)
+ .set("accessKey", accessKey)
+ .set("policy", Base64.encode(policy))
+ .set("signature", signature)
+ .set("keyTime", keyTime)
+ .set("algorithm", algorithm)
+ .set("post", post);
+ }
+
+ @Override
+ public UploadFileVo uploadFile(MultipartFile file) {
+ return uploadFile(file, null);
+ }
+
+ @Override
+ public UploadFileVo uploadFile(MultipartFile file, String folder) {
+ String fileName = getFileName(file);
+ String fileKey = getFileKey(file, folder);
+ String fileType = getFileType(file);
+ // 3 生成 cos 客户端。
+ COSClient client = null;
+ try {
+ client = initCOS();
+ //上传到腾讯云
+ PutObjectRequest putObjectRequest = new PutObjectRequest(uploadConfig.getBucket()
+ , fileKey, file.getInputStream(), new ObjectMetadata());
+ client.putObject(putObjectRequest);
+ return format(fileName, uploadConfig.getServerUrl(), fileKey, fileType);
+ } catch (Exception e) {
+ log.error(e.getMessage(), e);
+ throw new RuntimeException("文件上传失败");
+ } finally {
+ if (client != null) {
+ client.shutdown();
+ }
+ }
+ }
+
+ @Override
+ public UploadFileVo uploadFile(File file) {
+ return uploadFile(file, null);
+ }
+
+ @Override
+ public UploadFileVo uploadFile(File file, String folder) {
+ String fileName = getFileName(file);
+ String fileKey = getFileKey(file, folder);
+ String fileType = getFileType(file);
+ InputStream inputStream = FileUtil.getInputStream(file);
+ // 3 生成 cos 客户端。
+ COSClient client = null;
+ try {
+ client = initCOS();
+ //上传到腾讯云
+ PutObjectRequest putObjectRequest = new PutObjectRequest(uploadConfig.getBucket()
+ , fileKey, inputStream, new ObjectMetadata());
+ client.putObject(putObjectRequest);
+ return format(fileName, uploadConfig.getServerUrl(), fileKey, fileType);
+ } catch (Exception e) {
+ log.error(e.getMessage(), e);
+ throw new RuntimeException("文件上传失败");
+ } finally {
+ if (client != null) {
+ client.shutdown();
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/com/platform/common/upload/service/impl/UploadFastServiceImpl.java b/src/main/java/com/platform/common/upload/service/impl/UploadFastServiceImpl.java
new file mode 100644
index 0000000..a8d697d
--- /dev/null
+++ b/src/main/java/com/platform/common/upload/service/impl/UploadFastServiceImpl.java
@@ -0,0 +1,92 @@
+package com.platform.common.upload.service.impl;
+
+import cn.hutool.core.lang.Dict;
+import cn.hutool.core.util.IdUtil;
+import com.github.tobato.fastdfs.domain.fdfs.StorePath;
+import com.platform.common.upload.config.UploadConfig;
+import com.platform.common.upload.enums.UploadTypeEnum;
+import com.platform.common.upload.service.UploadService;
+import com.platform.common.upload.utils.FastUtils;
+import com.platform.common.upload.vo.UploadFileVo;
+import lombok.NoArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.stereotype.Service;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.annotation.Resource;
+import java.io.File;
+
+/**
+ * fast上传
+ */
+@Slf4j
+@Service("uploadFastService")
+@Configuration
+@NoArgsConstructor
+@ConditionalOnProperty(prefix = "upload", name = "uploadType", havingValue = "fast")
+public class UploadFastServiceImpl extends UploadBaseService implements UploadService {
+
+ @Resource
+ private UploadConfig uploadConfig;
+
+ @Override
+ public String getServerUrl() {
+ return uploadConfig.getServerUrl();
+ }
+
+ @Override
+ public Dict getToken(String fileType) {
+ String serverUrl = uploadConfig.getServerUrl();
+ String post = uploadConfig.getPost();
+ return Dict.create()
+ .set("uploadType", UploadTypeEnum.LOCAL)
+ .set("serverUrl", serverUrl)
+ .set("fileName", IdUtil.objectId() + "." + fileType)
+ .set("post", post);
+ }
+
+ @Override
+ public UploadFileVo uploadFile(MultipartFile file) {
+ return uploadFile(file, null);
+ }
+
+ @Override
+ public UploadFileVo uploadFile(MultipartFile file, String folder) {
+ StorePath storePath;
+ try {
+ storePath = FastUtils.uploadFile(file);
+ } catch (Exception e) {
+ log.error(e.getMessage(), e);
+ throw new RuntimeException("文件上传失败");
+ }
+ String fileKey = storePath.getFullPath();
+ String fileName = getFileName(file);
+ String fileType = getFileType(file);
+ String serverUrl = uploadConfig.getServerUrl();
+ return format(fileName, serverUrl, fileKey, fileType);
+ }
+
+ @Override
+ public UploadFileVo uploadFile(File file) {
+ return uploadFile(file, null);
+ }
+
+ @Override
+ public UploadFileVo uploadFile(File file, String folder) {
+ StorePath storePath;
+ try {
+ storePath = FastUtils.uploadFile(file);
+ } catch (Exception e) {
+ log.error(e.getMessage(), e);
+ throw new RuntimeException("文件上传失败");
+ }
+ String fileKey = storePath.getFullPath();
+ String fileType = getFileType(file);
+ String fileName = getFileName(file);
+ String serverUrl = uploadConfig.getServerUrl();
+ return format(fileName, serverUrl, fileKey, fileType);
+ }
+
+}
diff --git a/src/main/java/com/platform/common/upload/service/impl/UploadKodoServiceImpl.java b/src/main/java/com/platform/common/upload/service/impl/UploadKodoServiceImpl.java
new file mode 100644
index 0000000..fdae5fc
--- /dev/null
+++ b/src/main/java/com/platform/common/upload/service/impl/UploadKodoServiceImpl.java
@@ -0,0 +1,130 @@
+package com.platform.common.upload.service.impl;
+
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.lang.Dict;
+import cn.hutool.core.util.IdUtil;
+import com.platform.common.upload.config.UploadConfig;
+import com.platform.common.upload.enums.UploadTypeEnum;
+import com.platform.common.upload.service.UploadService;
+import com.platform.common.upload.vo.UploadFileVo;
+import com.qiniu.http.Response;
+import com.qiniu.storage.UploadManager;
+import com.qiniu.util.Auth;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.annotation.Resource;
+import java.io.File;
+import java.io.InputStream;
+
+/**
+ * 七牛云上传
+ */
+@Slf4j
+@Service("uploadKodoService")
+@Configuration
+@ConditionalOnProperty(prefix = "upload", name = "uploadType", havingValue = "kodo")
+public class UploadKodoServiceImpl extends UploadBaseService implements UploadService {
+
+ @Resource
+ private UploadConfig uploadConfig;
+
+ /**
+ * 获取Auth
+ */
+ private Auth getAuth() {
+ return Auth.create(uploadConfig.getAccessKey(), uploadConfig.getSecretKey());
+ }
+
+ /**
+ * 获取Token
+ */
+ private String getUploadToken(String fileType) {
+ return getAuth().uploadToken(uploadConfig.getBucket(), fileType);
+ }
+
+ @Override
+ public String getServerUrl() {
+ return uploadConfig.getServerUrl();
+ }
+
+ @Override
+ public Dict getToken(String fileType) {
+ String serverUrl = uploadConfig.getServerUrl();
+ String region = uploadConfig.getRegion();
+ String post = uploadConfig.getPost();
+ String fileName = IdUtil.objectId();
+ String token;
+ if (StringUtils.isEmpty(fileType)) {
+ token = getUploadToken(fileType);
+ } else {
+ fileName += "." + fileType;
+ token = getUploadToken(fileName);
+ }
+ return Dict.create()
+ .set("uploadType", UploadTypeEnum.KODO)
+ .set("serverUrl", serverUrl)
+ .set("fileName", fileName)
+ .set("region", region)
+ .set("uploadToken", token)
+ .set("post", post);
+ }
+
+ @Override
+ public UploadFileVo uploadFile(MultipartFile file) {
+ return uploadFile(file, null);
+ }
+
+ @Override
+ public UploadFileVo uploadFile(MultipartFile file, String folder) {
+ String fileName = getFileName(file);
+ String fileKey = getFileKey(file, folder);
+ String fileType = getFileType(file);
+ String token = getUploadToken(fileKey);
+ Response response = null;
+ try {
+ UploadManager uploadManager = new UploadManager(new com.qiniu.storage.Configuration());
+ response = uploadManager.put(file.getInputStream(), fileKey, token, null, fileType);
+ return format(fileName, uploadConfig.getServerUrl(), fileKey, fileType);
+ } catch (Exception e) {
+ log.error(e.getMessage(), e);
+ throw new RuntimeException("文件上传失败");
+ } finally {
+ if (response != null) {
+ response.close();
+ }
+ }
+ }
+
+ @Override
+ public UploadFileVo uploadFile(File file) {
+ return uploadFile(file, null);
+ }
+
+ @Override
+ public UploadFileVo uploadFile(File file, String folder) {
+ String fileName = getFileName(file);
+ String fileKey = getFileKey(file, folder);
+ String fileType = getFileType(file);
+ String token = getUploadToken(fileKey);
+ InputStream inputStream = FileUtil.getInputStream(file);
+ Response response = null;
+ try {
+ UploadManager uploadManager = new UploadManager(new com.qiniu.storage.Configuration());
+ response = uploadManager.put(inputStream, fileKey, token, null, fileType);
+ return format(fileName, uploadConfig.getServerUrl(), fileKey, fileType);
+ } catch (Exception e) {
+ log.error(e.getMessage(), e);
+ throw new RuntimeException("文件上传失败");
+ } finally {
+ if (response != null) {
+ response.close();
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/com/platform/common/upload/service/impl/UploadLocalServiceImpl.java b/src/main/java/com/platform/common/upload/service/impl/UploadLocalServiceImpl.java
new file mode 100644
index 0000000..383d27b
--- /dev/null
+++ b/src/main/java/com/platform/common/upload/service/impl/UploadLocalServiceImpl.java
@@ -0,0 +1,89 @@
+package com.platform.common.upload.service.impl;
+
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.io.file.FileNameUtil;
+import cn.hutool.core.lang.Dict;
+import com.platform.common.upload.config.UploadConfig;
+import com.platform.common.upload.enums.UploadTypeEnum;
+import com.platform.common.upload.service.UploadService;
+import com.platform.common.upload.vo.UploadFileVo;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.stereotype.Service;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.annotation.Resource;
+import java.io.File;
+
+/**
+ * 本地上传
+ */
+@Slf4j
+@Service("uploadLocalService")
+@Configuration
+@ConditionalOnProperty(prefix = "upload", name = "uploadType", havingValue = "local")
+public class UploadLocalServiceImpl extends UploadBaseService implements UploadService {
+
+ @Resource
+ private UploadConfig uploadConfig;
+
+ @Override
+ public String getServerUrl() {
+ return uploadConfig.getServerUrl();
+ }
+
+ @Override
+ public Dict getToken(String fileType) {
+ return Dict.create()
+ .set("uploadType", UploadTypeEnum.LOCAL);
+ }
+
+ @Override
+ public UploadFileVo uploadFile(MultipartFile file) {
+ return uploadFile(file, null);
+ }
+
+ @Override
+ public UploadFileVo uploadFile(MultipartFile file, String folder) {
+ String serverUrl = uploadConfig.getServerUrl();
+ String uploadPath = uploadConfig.getRegion();
+ String fileName = getFileName(file);
+ String fileType = getFileType(file);
+ // 文件路径
+ String fileKey = getFileKey(uploadPath, fileType);
+ try {
+ // 文件拷贝
+ file.transferTo(new File(uploadPath + FileNameUtil.UNIX_SEPARATOR + fileKey));
+ } catch (Exception e) {
+ log.error(e.getMessage(), e);
+ throw new RuntimeException("文件上传失败");
+ }
+ // 组装对象
+ UploadFileVo fileVo = format(fileName, serverUrl, fileKey, fileType)
+ .setFullPath(serverUrl + fileKey);
+ return fileVo;
+ }
+
+ @Override
+ public UploadFileVo uploadFile(File file) {
+ return uploadFile(file, null);
+ }
+
+ @Override
+ public UploadFileVo uploadFile(File file, String folder) {
+ String serverUrl = uploadConfig.getServerUrl();
+ String uploadPath = uploadConfig.getRegion();
+ String fileName = getFileName(file);
+ String fileType = getFileType(file);
+ // 文件路径
+ String fileKey = getFileKey(uploadPath, fileType);
+ // 文件拷贝
+ FileUtil.copyFile(file, new File(uploadPath + FileNameUtil.UNIX_SEPARATOR + fileKey));
+ // 组装对象
+ UploadFileVo fileVo = format(fileName, serverUrl, fileKey, fileType)
+ .setFullPath(serverUrl + fileKey);
+ return fileVo;
+ }
+
+}
diff --git a/src/main/java/com/platform/common/upload/service/impl/UploadMinioServiceImpl.java b/src/main/java/com/platform/common/upload/service/impl/UploadMinioServiceImpl.java
new file mode 100644
index 0000000..6405258
--- /dev/null
+++ b/src/main/java/com/platform/common/upload/service/impl/UploadMinioServiceImpl.java
@@ -0,0 +1,119 @@
+package com.platform.common.upload.service.impl;
+
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.lang.Dict;
+import cn.hutool.core.util.IdUtil;
+import com.platform.common.upload.config.UploadConfig;
+import com.platform.common.upload.enums.UploadTypeEnum;
+import com.platform.common.upload.service.UploadService;
+import com.platform.common.upload.vo.UploadFileVo;
+import io.minio.MinioClient;
+import io.minio.PutObjectArgs;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.stereotype.Service;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.annotation.Resource;
+import java.io.File;
+
+/**
+ * minio上传
+ */
+@Slf4j
+@Service("uploadMinioService")
+@Configuration
+@ConditionalOnProperty(prefix = "upload", name = "uploadType", havingValue = "minio")
+public class UploadMinioServiceImpl extends UploadBaseService implements UploadService {
+
+ @Resource
+ private UploadConfig uploadConfig;
+
+ /**
+ * 获取上传凭证
+ */
+ private MinioClient initClient() {
+ return MinioClient.builder()
+ .endpoint(uploadConfig.getRegion())
+ .credentials(uploadConfig.getAccessKey(), uploadConfig.getSecretKey())
+ .build();
+ }
+
+ @Override
+ public String getServerUrl() {
+ return uploadConfig.getServerUrl();
+ }
+
+ @Override
+ public Dict getToken(String fileType) {
+ String serverUrl = uploadConfig.getServerUrl();
+ String post = uploadConfig.getPost();
+ return Dict.create()
+ .set("uploadType", UploadTypeEnum.LOCAL)
+ .set("serverUrl", serverUrl)
+ .set("fileName", IdUtil.objectId() + "." + fileType)
+ .set("post", post);
+ }
+
+ @Override
+ public UploadFileVo uploadFile(MultipartFile file) {
+ return uploadFile(file, null);
+ }
+
+ @Override
+ public UploadFileVo uploadFile(MultipartFile file, String folder) {
+ String serverUrl = uploadConfig.getServerUrl();
+ MinioClient client = initClient();
+ String fileName = getFileName(file);
+ String fileType = getFileType(file);
+ String fileKey = getFileKey(file);
+ try {
+ PutObjectArgs args = PutObjectArgs.builder()
+ .bucket(uploadConfig.getBucket())
+ .object(fileName)
+ .stream(file.getInputStream(), file.getSize(), -1)
+ .contentType(fileType)
+ .build();
+ client.putObject(args);
+ } catch (Exception e) {
+ log.error(e.getMessage(), e);
+ throw new RuntimeException("文件上传失败");
+ }
+ // 组装对象
+ UploadFileVo fileVo = format(fileName, serverUrl, fileKey, fileType)
+ .setFullPath(serverUrl + fileKey);
+ return fileVo;
+ }
+
+ @Override
+ public UploadFileVo uploadFile(File file) {
+ return uploadFile(file, null);
+ }
+
+ @Override
+ public UploadFileVo uploadFile(File file, String folder) {
+ String serverUrl = uploadConfig.getServerUrl();
+ MinioClient client = initClient();
+ String fileName = getFileName(file);
+ String fileType = getFileType(file);
+ String fileKey = getFileKey(file);
+ try {
+ PutObjectArgs args = PutObjectArgs.builder()
+ .bucket(uploadConfig.getBucket())
+ .object(fileName)
+ .stream(FileUtil.getInputStream(file), FileUtil.size(file), -1)
+ .contentType(fileType)
+ .build();
+ client.putObject(args);
+ } catch (Exception e) {
+ log.error(e.getMessage(), e);
+ throw new RuntimeException("文件上传失败");
+ }
+ // 组装对象
+ UploadFileVo fileVo = format(fileName, serverUrl, fileKey, fileType)
+ .setFullPath(serverUrl + fileKey);
+ return fileVo;
+ }
+
+}
diff --git a/src/main/java/com/platform/common/upload/service/impl/UploadOssServiceImpl.java b/src/main/java/com/platform/common/upload/service/impl/UploadOssServiceImpl.java
new file mode 100644
index 0000000..b3eed0f
--- /dev/null
+++ b/src/main/java/com/platform/common/upload/service/impl/UploadOssServiceImpl.java
@@ -0,0 +1,126 @@
+package com.platform.common.upload.service.impl;
+
+import cn.hutool.core.codec.Base64;
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.lang.Dict;
+import cn.hutool.core.util.IdUtil;
+import com.aliyun.oss.OSS;
+import com.aliyun.oss.OSSClientBuilder;
+import com.aliyun.oss.model.PolicyConditions;
+import com.platform.common.upload.config.UploadConfig;
+import com.platform.common.upload.enums.UploadTypeEnum;
+import com.platform.common.upload.service.UploadService;
+import com.platform.common.upload.vo.UploadFileVo;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.stereotype.Service;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.annotation.Resource;
+import java.io.File;
+import java.io.InputStream;
+import java.util.Date;
+
+/**
+ * 阿里云上传
+ */
+@Slf4j
+@Service("uploadOssService")
+@Configuration
+@ConditionalOnProperty(prefix = "upload", name = "uploadType", havingValue = "oss")
+public class UploadOssServiceImpl extends UploadBaseService implements UploadService {
+
+ @Resource
+ private UploadConfig uploadConfig;
+
+ /**
+ * 初始化oss
+ */
+ private OSS initOSS() {
+ return new OSSClientBuilder()
+ .build(uploadConfig.getRegion(), uploadConfig.getAccessKey(), uploadConfig.getSecretKey());
+ }
+
+ @Override
+ public String getServerUrl() {
+ return uploadConfig.getServerUrl();
+ }
+
+ @Override
+ public Dict getToken(String fileType) {
+ // 1、默认固定值,分钟
+ Integer expire = 30;
+ String accessKey = uploadConfig.getAccessKey();
+ String serverUrl = uploadConfig.getServerUrl();
+ String post = uploadConfig.getPost();
+ // 2、过期时间
+ Date expiration = DateUtil.offsetMinute(DateUtil.date(), expire);
+ // 3、构造“策略”(Policy)
+ OSS ossClient = initOSS();
+ PolicyConditions policyConditions = new PolicyConditions();
+ policyConditions.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000);
+ String postPolicy = ossClient.generatePostPolicy(expiration, policyConditions);
+ String postSignature = ossClient.calculatePostSignature(postPolicy);
+ // 4、生成 Signature
+ return Dict.create()
+ .set("uploadType", UploadTypeEnum.OSS)
+ .set("serverUrl", serverUrl)
+ .set("fileName", IdUtil.objectId() + "." + fileType)
+ .set("accessKey", accessKey)
+ .set("policy", Base64.encode(postPolicy))
+ .set("signature", postSignature)
+ .set("post", post);
+ }
+
+ @Override
+ public UploadFileVo uploadFile(MultipartFile file) {
+ return uploadFile(file, null);
+ }
+
+ @Override
+ public UploadFileVo uploadFile(MultipartFile file, String folder) {
+ OSS client = initOSS();
+ try {
+ String fileName = getFileName(file);
+ String fileKey = getFileKey(file, folder);
+ String fileType = getFileType(file);
+ client.putObject(uploadConfig.getBucket(), fileKey, file.getInputStream());
+ // 服务器地址
+ String serverUrl = uploadConfig.getServerUrl();
+ return format(fileName, serverUrl, fileKey, fileType);
+ } catch (Exception e) {
+ log.error(e.getMessage(), e);
+ throw new RuntimeException("文件上传失败");
+ } finally {
+ client.shutdown();
+ }
+ }
+
+ @Override
+ public UploadFileVo uploadFile(File file) {
+ return uploadFile(file, null);
+ }
+
+ @Override
+ public UploadFileVo uploadFile(File file, String folder) {
+ OSS client = initOSS();
+ try {
+ String fileName = getFileName(file);
+ String fileKey = getFileKey(file, folder);
+ String fileType = getFileType(file);
+ InputStream inputStream = FileUtil.getInputStream(file);
+ client.putObject(uploadConfig.getBucket(), fileKey, inputStream);
+ // 服务器地址
+ String serverUrl = uploadConfig.getServerUrl();
+ return format(fileName, serverUrl, fileKey, fileType);
+ } catch (Exception e) {
+ log.error(e.getMessage(), e);
+ throw new RuntimeException("文件上传失败");
+ } finally {
+ client.shutdown();
+ }
+ }
+
+}
diff --git a/src/main/java/com/platform/common/upload/utils/FastUtils.java b/src/main/java/com/platform/common/upload/utils/FastUtils.java
new file mode 100644
index 0000000..41df49c
--- /dev/null
+++ b/src/main/java/com/platform/common/upload/utils/FastUtils.java
@@ -0,0 +1,48 @@
+package com.platform.common.upload.utils;
+
+import com.github.tobato.fastdfs.domain.conn.FdfsWebServer;
+import com.github.tobato.fastdfs.domain.fdfs.StorePath;
+import com.github.tobato.fastdfs.domain.fdfs.ThumbImageConfig;
+import com.github.tobato.fastdfs.service.FastFileStorageClient;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.io.FilenameUtils;
+import org.springframework.stereotype.Component;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.util.Set;
+
+@Slf4j
+@Component
+public class FastUtils {
+ private static FastFileStorageClient fastFileStorageClient;
+
+ public FastUtils(ThumbImageConfig thumbImageConfig, FastFileStorageClient fastFileStorageClient, FdfsWebServer fdfsWebServer) {
+ FastUtils.fastFileStorageClient = fastFileStorageClient;
+ }
+
+ public static StorePath uploadFile(MultipartFile multipartFile) {
+ StorePath storePath;
+ try {
+ storePath = fastFileStorageClient.uploadFile(multipartFile.getInputStream(), multipartFile.getSize(), FilenameUtils.getExtension(multipartFile.getOriginalFilename()), (Set) null);
+ } catch (Exception var6) {
+ log.error(var6.getMessage(), var6);
+ throw new RuntimeException("文件上传失败");
+ }
+ return storePath;
+ }
+
+ public static StorePath uploadFile(File file) {
+ StorePath storePath;
+ try {
+ FileInputStream inputStream = new FileInputStream(file);
+ storePath = fastFileStorageClient.uploadFile(inputStream, file.length(), FilenameUtils.getExtension(file.getName()), null);
+ } catch (Exception var6) {
+ log.error(var6.getMessage(), var6);
+ throw new RuntimeException("文件上传失败");
+ }
+ return storePath;
+ }
+
+}
diff --git a/src/main/java/com/platform/common/upload/vo/UploadAudioVo.java b/src/main/java/com/platform/common/upload/vo/UploadAudioVo.java
new file mode 100644
index 0000000..f76b146
--- /dev/null
+++ b/src/main/java/com/platform/common/upload/vo/UploadAudioVo.java
@@ -0,0 +1,18 @@
+package com.platform.common.upload.vo;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+/**
+ * 文件上传
+ */
+@Data
+@Accessors(chain = true) // 链式调用
+public class UploadAudioVo extends UploadFileVo {
+
+ /**
+ * 识别文字
+ */
+ private String sourceText;
+
+}
diff --git a/src/main/java/com/platform/common/upload/vo/UploadFileVo.java b/src/main/java/com/platform/common/upload/vo/UploadFileVo.java
new file mode 100644
index 0000000..613d747
--- /dev/null
+++ b/src/main/java/com/platform/common/upload/vo/UploadFileVo.java
@@ -0,0 +1,26 @@
+package com.platform.common.upload.vo;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+/**
+ * 文件上传
+ */
+@Data
+@Accessors(chain = true) // 链式调用
+public class UploadFileVo {
+
+ /**
+ * 文件名称
+ */
+ private String fileName;
+ /**
+ * 文件地址
+ */
+ private String fullPath;
+ /**
+ * 文件类型
+ */
+ private String fileType;
+
+}
diff --git a/src/main/java/com/platform/common/upload/vo/UploadVideoVo.java b/src/main/java/com/platform/common/upload/vo/UploadVideoVo.java
new file mode 100644
index 0000000..98d4dd9
--- /dev/null
+++ b/src/main/java/com/platform/common/upload/vo/UploadVideoVo.java
@@ -0,0 +1,30 @@
+package com.platform.common.upload.vo;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+/**
+ * 文件上传
+ */
+@Data
+@Accessors(chain = true) // 链式调用
+public class UploadVideoVo {
+
+ /**
+ * 文件名称
+ */
+ private String fileName;
+ /**
+ * 文件地址
+ */
+ private String fullPath;
+ /**
+ * 文件类型
+ */
+ private String fileType;
+ /**
+ * 文件封面
+ */
+ private String screenShot;
+
+}
diff --git a/src/main/java/com/platform/common/utils/BeanCopyUtils.java b/src/main/java/com/platform/common/utils/BeanCopyUtils.java
new file mode 100644
index 0000000..74c1806
--- /dev/null
+++ b/src/main/java/com/platform/common/utils/BeanCopyUtils.java
@@ -0,0 +1,34 @@
+package com.platform.common.utils;
+
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+
+/**
+ * java bean 复制操作的工具类
+ */
+public class BeanCopyUtils {
+
+ /**
+ * 包含方法
+ */
+ public static T include(Object source, String... fields) {
+ JSONObject jsonObject = JSONUtil.parseObj(source);
+ JSONObject target = new JSONObject();
+ for (String field : fields) {
+ target.set(field.trim(), jsonObject.get(field.trim()));
+ }
+ return (T) target.toBean(source.getClass());
+ }
+
+ /**
+ * 排除方法
+ */
+ public static T exclude(Object source, String... fields) {
+ JSONObject jsonObject = JSONUtil.parseObj(source);
+ for (String field : fields) {
+ jsonObject.remove(field.trim());
+ }
+ return (T) jsonObject.toBean(source.getClass());
+ }
+
+}
diff --git a/src/main/java/com/platform/common/utils/IpUtils.java b/src/main/java/com/platform/common/utils/IpUtils.java
new file mode 100644
index 0000000..06ce44c
--- /dev/null
+++ b/src/main/java/com/platform/common/utils/IpUtils.java
@@ -0,0 +1,154 @@
+package com.platform.common.utils;
+
+import javax.servlet.http.HttpServletRequest;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+/**
+ * 获取IP方法
+ */
+public class IpUtils {
+
+ public static String getIpAddr(HttpServletRequest request) {
+ if (request == null) {
+ return "unknown";
+ }
+ String ip = request.getHeader("x-forwarded-for");
+ if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+ ip = request.getHeader("Proxy-Client-IP");
+ }
+ if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+ ip = request.getHeader("X-Forwarded-For");
+ }
+ if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+ ip = request.getHeader("WL-Proxy-Client-IP");
+ }
+ if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+ ip = request.getHeader("X-Real-IP");
+ }
+ if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+ ip = request.getRemoteAddr();
+ }
+ return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
+ }
+
+ public static boolean internalIp(String ip) {
+ byte[] addr = textToNumericFormatV4(ip);
+ return internalIp(addr) || "127.0.0.1".equals(ip);
+ }
+
+ private static boolean internalIp(byte[] addr) {
+ if (addr == null || addr.length < 2) {
+ return true;
+ }
+ final byte b0 = addr[0];
+ final byte b1 = addr[1];
+ // 10.x.x.x/8
+ final byte SECTION_1 = 0x0A;
+ // 172.16.x.x/12
+ final byte SECTION_2 = (byte) 0xAC;
+ final byte SECTION_3 = (byte) 0x10;
+ final byte SECTION_4 = (byte) 0x1F;
+ // 192.168.x.x/16
+ final byte SECTION_5 = (byte) 0xC0;
+ final byte SECTION_6 = (byte) 0xA8;
+ switch (b0) {
+ case SECTION_1:
+ return true;
+ case SECTION_2:
+ if (b1 >= SECTION_3 && b1 <= SECTION_4) {
+ return true;
+ }
+ case SECTION_5:
+ switch (b1) {
+ case SECTION_6:
+ return true;
+ }
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * 将IPv4地址转换成字节
+ *
+ * text IPv4地址
+ */
+ public static byte[] textToNumericFormatV4(String text) {
+ if (text.length() == 0) {
+ return null;
+ }
+
+ byte[] bytes = new byte[4];
+ String[] elements = text.split("\\.", -1);
+ try {
+ long l;
+ int i;
+ switch (elements.length) {
+ case 1:
+ l = Long.parseLong(elements[0]);
+ if ((l < 0L) || (l > 4294967295L))
+ return null;
+ bytes[0] = (byte) (int) (l >> 24 & 0xFF);
+ bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF);
+ bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
+ bytes[3] = (byte) (int) (l & 0xFF);
+ break;
+ case 2:
+ l = Integer.parseInt(elements[0]);
+ if ((l < 0L) || (l > 255L))
+ return null;
+ bytes[0] = (byte) (int) (l & 0xFF);
+ l = Integer.parseInt(elements[1]);
+ if ((l < 0L) || (l > 16777215L))
+ return null;
+ bytes[1] = (byte) (int) (l >> 16 & 0xFF);
+ bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
+ bytes[3] = (byte) (int) (l & 0xFF);
+ break;
+ case 3:
+ for (i = 0; i < 2; ++i) {
+ l = Integer.parseInt(elements[i]);
+ if ((l < 0L) || (l > 255L))
+ return null;
+ bytes[i] = (byte) (int) (l & 0xFF);
+ }
+ l = Integer.parseInt(elements[2]);
+ if ((l < 0L) || (l > 65535L))
+ return null;
+ bytes[2] = (byte) (int) (l >> 8 & 0xFF);
+ bytes[3] = (byte) (int) (l & 0xFF);
+ break;
+ case 4:
+ for (i = 0; i < 4; ++i) {
+ l = Integer.parseInt(elements[i]);
+ if ((l < 0L) || (l > 255L))
+ return null;
+ bytes[i] = (byte) (int) (l & 0xFF);
+ }
+ break;
+ default:
+ return null;
+ }
+ } catch (NumberFormatException e) {
+ return null;
+ }
+ return bytes;
+ }
+
+ public static String getHostIp() {
+ try {
+ return InetAddress.getLocalHost().getHostAddress();
+ } catch (UnknownHostException e) {
+ }
+ return "127.0.0.1";
+ }
+
+ public static String getHostName() {
+ try {
+ return InetAddress.getLocalHost().getHostName();
+ } catch (UnknownHostException e) {
+ }
+ return "未知";
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/platform/common/utils/MessageUtils.java b/src/main/java/com/platform/common/utils/MessageUtils.java
new file mode 100644
index 0000000..761ac1d
--- /dev/null
+++ b/src/main/java/com/platform/common/utils/MessageUtils.java
@@ -0,0 +1,22 @@
+package com.platform.common.utils;
+
+import cn.hutool.extra.spring.SpringUtil;
+import org.springframework.context.MessageSource;
+import org.springframework.context.i18n.LocaleContextHolder;
+
+/**
+ * 获取i18n资源文件
+ */
+public class MessageUtils {
+ /**
+ * 根据消息键和参数 获取消息 委托给spring messageSource
+ *
+ * msgCode 消息键
+ * args 参数
+ * 获取国际化翻译值
+ */
+ public static String message(String msgCode, Object... args) {
+ MessageSource messageSource = SpringUtil.getBean(MessageSource.class);
+ return messageSource.getMessage(msgCode, args, LocaleContextHolder.getLocale());
+ }
+}
diff --git a/src/main/java/com/platform/common/utils/ServletUtils.java b/src/main/java/com/platform/common/utils/ServletUtils.java
new file mode 100644
index 0000000..8ebeff8
--- /dev/null
+++ b/src/main/java/com/platform/common/utils/ServletUtils.java
@@ -0,0 +1,74 @@
+package com.platform.common.utils;
+
+import cn.hutool.core.convert.Convert;
+import org.springframework.util.StringUtils;
+import org.springframework.web.context.request.RequestAttributes;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * 客户端工具类
+ */
+public class ServletUtils {
+ /**
+ * 获取String参数
+ */
+ public static String getParameter(String name) {
+ return getRequest().getParameter(name);
+ }
+
+ /**
+ * 获取String参数
+ */
+ public static String getParameter(String name, String defaultValue) {
+ String value = getParameter(name);
+ if (StringUtils.isEmpty(value)) {
+ value = defaultValue;
+ }
+ return value;
+ }
+
+ /**
+ * 获取Integer参数
+ */
+ public static Integer getParameterToInt(String name) {
+ return getParameterToInt(name, null);
+ }
+
+ /**
+ * 获取Integer参数
+ */
+ public static Integer getParameterToInt(String name, Integer defaultValue) {
+ return Convert.toInt(getRequest().getParameter(name), defaultValue);
+ }
+
+ /**
+ * 获取request
+ */
+ public static HttpServletRequest getRequest() {
+ return getRequestAttributes().getRequest();
+ }
+
+ /**
+ * 获取response
+ */
+ public static HttpServletResponse getResponse() {
+ return getRequestAttributes().getResponse();
+ }
+
+ /**
+ * 获取sessionId
+ */
+ public static String getSessionId() {
+ return getRequestAttributes().getSessionId();
+ }
+
+ public static ServletRequestAttributes getRequestAttributes() {
+ RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
+ return (ServletRequestAttributes) attributes;
+ }
+
+}
diff --git a/src/main/java/com/platform/common/utils/TimerUtils.java b/src/main/java/com/platform/common/utils/TimerUtils.java
new file mode 100644
index 0000000..59ca612
--- /dev/null
+++ b/src/main/java/com/platform/common/utils/TimerUtils.java
@@ -0,0 +1,39 @@
+package com.platform.common.utils;
+
+import io.netty.util.HashedWheelTimer;
+import io.netty.util.TimerTask;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 环形队列--定时器
+ *
+ * @author 考拉
+ * @email 939313737@qq.com
+ * @date 2017年8月26日 All rights Reserved, Designed By www.q3z3.com
+ */
+@Slf4j
+public class TimerUtils {
+
+ private HashedWheelTimer timer;
+
+ private TimerUtils() {
+ // 精度1秒,一共512格
+ this.timer = new HashedWheelTimer(1, TimeUnit.SECONDS);
+ }
+
+ private static class SingletonHolder {
+ private static final TimerUtils singleton = new TimerUtils();
+ }
+
+ public static TimerUtils instance() {
+ return SingletonHolder.singleton;
+ }
+
+ public void addTask(TimerTask task, long delay, TimeUnit unit) {
+ log.info("添加定时任务, 延迟:{} {}", delay, unit.name());
+ timer.newTimeout(task, delay, unit);
+ }
+
+}
diff --git a/src/main/java/com/platform/common/version/ApiVersion.java b/src/main/java/com/platform/common/version/ApiVersion.java
new file mode 100644
index 0000000..238ec0e
--- /dev/null
+++ b/src/main/java/com/platform/common/version/ApiVersion.java
@@ -0,0 +1,20 @@
+package com.platform.common.version;
+
+import java.lang.annotation.*;
+
+/**
+ * 自定义一个注解,给需要版本控制的方法加上该注解
+ *
+ * 使用:@SubmitRepeat
+ */
+@Target({ElementType.METHOD, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface ApiVersion {
+
+ /**
+ * 版本号
+ */
+ VersionEnum value();
+
+}
diff --git a/src/main/java/com/platform/common/version/DeviceInterceptor.java b/src/main/java/com/platform/common/version/DeviceInterceptor.java
new file mode 100644
index 0000000..540203c
--- /dev/null
+++ b/src/main/java/com/platform/common/version/DeviceInterceptor.java
@@ -0,0 +1,52 @@
+package com.platform.common.version;
+
+import cn.hutool.core.util.CharsetUtil;
+import cn.hutool.extra.servlet.ServletUtil;
+import cn.hutool.json.JSONUtil;
+import com.platform.common.constant.HeadConstant;
+import com.platform.common.core.EnumUtils;
+import com.platform.common.enums.DeviceEnum;
+import com.platform.common.enums.YesOrNoEnum;
+import com.platform.common.web.domain.AjaxResult;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+import org.springframework.web.servlet.HandlerInterceptor;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * 设备拦截器
+ */
+@Component
+public class DeviceInterceptor implements HandlerInterceptor {
+
+ @Override
+ public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
+ throws Exception {
+ if (YesOrNoEnum.NO.equals(VersionConfig.ENABLED)) {
+ return true;
+ }
+ String currentUrl = request.getServletPath();
+ if (VersionUtils.verifyUrl(currentUrl, VersionConfig.EXCLUDES)) {
+ return true;
+ }
+ String device = ServletUtil.getHeader(request, HeadConstant.DEVICE, CharsetUtil.UTF_8);
+ if (StringUtils.isEmpty(device)) {
+ return error(response);
+ }
+ DeviceEnum deviceEnum = EnumUtils.toEnum(DeviceEnum.class, device);
+ if (deviceEnum == null) {
+ return error(response);
+ }
+ return true;
+ }
+
+ private boolean error(HttpServletResponse response) throws IOException {
+ response.setContentType("application/json;charset=utf-8");
+ response.getWriter().print(JSONUtil.toJsonStr(AjaxResult.fail("请求不正确")));
+ return false;
+ }
+
+}
diff --git a/src/main/java/com/platform/common/version/SignInterceptor.java b/src/main/java/com/platform/common/version/SignInterceptor.java
new file mode 100644
index 0000000..069bb26
--- /dev/null
+++ b/src/main/java/com/platform/common/version/SignInterceptor.java
@@ -0,0 +1,80 @@
+package com.platform.common.version;
+
+import cn.hutool.core.date.DateUnit;
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.util.CharsetUtil;
+import cn.hutool.core.util.NumberUtil;
+import cn.hutool.crypto.SecureUtil;
+import cn.hutool.extra.servlet.ServletUtil;
+import cn.hutool.json.JSONUtil;
+import com.platform.common.constant.HeadConstant;
+import com.platform.common.enums.YesOrNoEnum;
+import com.platform.common.web.domain.AjaxResult;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+import org.springframework.web.servlet.HandlerInterceptor;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.net.URLDecoder;
+import java.util.Date;
+
+/**
+ * 签名拦截器
+ */
+@Component
+public class SignInterceptor implements HandlerInterceptor {
+
+ @Override
+ public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
+ throws Exception {
+ if (YesOrNoEnum.NO.equals(VersionConfig.ENABLED)) {
+ return true;
+ }
+ String currentUrl = request.getServletPath();
+ if (VersionUtils.verifyUrl(currentUrl, VersionConfig.EXCLUDES)) {
+ return true;
+ }
+ String sign = ServletUtil.getHeader(request, HeadConstant.SIGN, CharsetUtil.UTF_8);
+ String timestamp = ServletUtil.getHeader(request, HeadConstant.TIMESTAMP, CharsetUtil.UTF_8);
+ String appId = ServletUtil.getHeader(request, HeadConstant.APP_ID, CharsetUtil.UTF_8);
+ if (StringUtils.isEmpty(sign)
+ || StringUtils.isEmpty(appId)
+ || StringUtils.isEmpty(timestamp)) {
+ return error(response);
+ }
+ String appSecret = VersionConfig.SIGN.get(appId);
+ if (appSecret == null) {
+ return error(response);
+ }
+ if (!NumberUtil.isLong(timestamp)) {
+ return error(response);
+ }
+ Date date = DateUtil.date(NumberUtil.parseLong(timestamp));
+ if (DateUtil.between(date, DateUtil.date(), DateUnit.MINUTE) > 5) {
+ return error(response);
+ }
+ String path = request.getRequestURI();
+ if ("GET".equalsIgnoreCase(request.getMethod())) {
+ String query = request.getQueryString();
+ if (!StringUtils.isEmpty(query)) {
+ path += "?" + URLDecoder.decode(query, "UTF-8");
+ }
+ }
+ String param = SecureUtil.md5(appId + path + timestamp);
+ // 此处密钥如果有非ASCII字符,考虑编码
+ String result = SecureUtil.hmacMd5(appSecret).digestHex(param);
+ if (!sign.equalsIgnoreCase(result)) {
+ return error(response);
+ }
+ return true;
+ }
+
+ private boolean error(HttpServletResponse response) throws IOException {
+ response.setContentType("application/json;charset=utf-8");
+ response.getWriter().print(JSONUtil.toJsonStr(AjaxResult.fail("请求不正确")));
+ return false;
+ }
+
+}
diff --git a/src/main/java/com/platform/common/version/VersionCondition.java b/src/main/java/com/platform/common/version/VersionCondition.java
new file mode 100644
index 0000000..af4c1bd
--- /dev/null
+++ b/src/main/java/com/platform/common/version/VersionCondition.java
@@ -0,0 +1,43 @@
+package com.platform.common.version;
+
+import com.platform.common.constant.HeadConstant;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.servlet.mvc.condition.RequestCondition;
+
+import javax.servlet.http.HttpServletRequest;
+
+@Slf4j
+@Getter
+public class VersionCondition implements RequestCondition {
+
+ private String version;
+
+ public VersionCondition(String version) {
+ this.version = version;
+ }
+
+ //将不同的筛选条件合并,这里采用的覆盖,即后来的规则生效
+ @Override
+ public VersionCondition combine(VersionCondition other) {
+ return new VersionCondition(other.getVersion());
+ }
+
+ //根据request查找匹配到的筛选条件
+ @Override
+ public VersionCondition getMatchingCondition(HttpServletRequest request) {
+ String version = request.getHeader(HeadConstant.VERSION);
+ // 如果请求的版本号大于配置版本号, 则满足,即与请求的
+ if (VersionUtils.compareTo(version, this.version, request.getRequestURI()) >= 0) {
+ return this;
+ }
+ return null;
+ }
+
+ //实现不同条件类的比较,从而实现优先级排序,返回值最小匹配this
+ @Override
+ public int compareTo(VersionCondition other, HttpServletRequest request) {
+ return VersionUtils.compareTo(other.getVersion(), this.version, request.getRequestURI());
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/platform/common/version/VersionConfig.java b/src/main/java/com/platform/common/version/VersionConfig.java
new file mode 100644
index 0000000..f4d552d
--- /dev/null
+++ b/src/main/java/com/platform/common/version/VersionConfig.java
@@ -0,0 +1,76 @@
+package com.platform.common.version;
+
+import com.platform.common.core.EnumUtils;
+import com.platform.common.enums.YesOrNoEnum;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.StringUtils;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 读取项目相关配置
+ */
+@Component
+@ConfigurationProperties(prefix = "version")
+public class VersionConfig {
+
+ /**
+ * 版本开关
+ */
+ public static YesOrNoEnum ENABLED = YesOrNoEnum.NO;
+
+ /**
+ * 最低版本
+ */
+ public static String VERSION = "1.0.0";
+
+ /**
+ * 过滤请求
+ */
+ public static List EXCLUDES = new ArrayList<>();
+
+ /**
+ * 过滤请求
+ */
+ public static List BANS = new ArrayList<>();
+
+ /**
+ * 签名
+ */
+ public static Map SIGN = new HashMap<>();
+
+ public void setEnabled(String enabled) {
+ if (!StringUtils.isEmpty(enabled)) {
+ VersionConfig.ENABLED = EnumUtils.toEnum(YesOrNoEnum.class, enabled, YesOrNoEnum.NO);
+ }
+ }
+
+ public void setVersion(String version) {
+ if (!StringUtils.isEmpty(version)) {
+ VersionConfig.VERSION = version;
+ }
+ }
+
+ public void setExcludes(List excludes) {
+ if (!CollectionUtils.isEmpty(excludes)) {
+ VersionConfig.EXCLUDES = excludes;
+ }
+ }
+
+ public void setBans(List bans) {
+ if (!CollectionUtils.isEmpty(bans)) {
+ VersionConfig.BANS = bans;
+ }
+ }
+
+ public void setSign(Map sign) {
+ if (!CollectionUtils.isEmpty(sign)) {
+ VersionConfig.SIGN = sign;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/platform/common/version/VersionEnum.java b/src/main/java/com/platform/common/version/VersionEnum.java
new file mode 100644
index 0000000..4d4bb64
--- /dev/null
+++ b/src/main/java/com/platform/common/version/VersionEnum.java
@@ -0,0 +1,27 @@
+package com.platform.common.version;
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+import com.fasterxml.jackson.annotation.JsonValue;
+import lombok.Getter;
+
+/**
+ * 版本枚举值
+ */
+@Getter
+public enum VersionEnum {
+
+ V1_0_0("1.0.0", "初始化版本"),
+ V1_0_1("1.0.1", "下一个版本"),
+ ;
+
+ @EnumValue
+ @JsonValue
+ private final String code;
+ private final String info;
+
+ VersionEnum(String code, String info) {
+ this.code = code;
+ this.info = info;
+ }
+
+}
diff --git a/src/main/java/com/platform/common/version/VersionHandlerMapping.java b/src/main/java/com/platform/common/version/VersionHandlerMapping.java
new file mode 100644
index 0000000..1cadbf5
--- /dev/null
+++ b/src/main/java/com/platform/common/version/VersionHandlerMapping.java
@@ -0,0 +1,25 @@
+package com.platform.common.version;
+
+import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.web.servlet.mvc.condition.RequestCondition;
+import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
+
+import java.lang.reflect.Method;
+
+public class VersionHandlerMapping extends RequestMappingHandlerMapping {
+
+ @Override
+ protected RequestCondition getCustomMethodCondition(Method method) {
+ ApiVersion apiVersion = AnnotationUtils.findAnnotation(method, ApiVersion.class);
+ return createCondition(apiVersion);
+ }
+
+ // 实例化RequestCondition
+ private RequestCondition createCondition(ApiVersion apiVersion) {
+ if (apiVersion == null) {
+ return null;
+ }
+ return new VersionCondition(apiVersion.value().getCode());
+ }
+
+}
diff --git a/src/main/java/com/platform/common/version/VersionInterceptor.java b/src/main/java/com/platform/common/version/VersionInterceptor.java
new file mode 100644
index 0000000..6ab7fc9
--- /dev/null
+++ b/src/main/java/com/platform/common/version/VersionInterceptor.java
@@ -0,0 +1,51 @@
+package com.platform.common.version;
+
+import com.platform.common.constant.HeadConstant;
+import com.platform.common.enums.ResultCodeEnum;
+import com.platform.common.enums.YesOrNoEnum;
+import com.platform.common.exception.BaseException;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+import org.springframework.web.method.HandlerMethod;
+import org.springframework.web.servlet.HandlerInterceptor;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * 版本验证
+ */
+@Component
+@Slf4j
+public class VersionInterceptor implements HandlerInterceptor {
+
+ @Override
+ public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
+ /**
+ * 如果不属于HandlerMethod,则放行
+ */
+ if (!(handler instanceof HandlerMethod)) {
+ return true;
+ }
+ if (YesOrNoEnum.NO.equals(VersionConfig.ENABLED)) {
+ return true;
+ }
+ String currentUrl = request.getServletPath();
+ if (VersionUtils.verifyUrl(currentUrl, VersionConfig.EXCLUDES)) {
+ return true;
+ }
+ //获取用户接口版本
+ String version = request.getHeader(HeadConstant.VERSION);
+ if (VersionUtils.compareTo(version, VersionConfig.VERSION, request.getRequestURI()) < 0) {
+ throw new BaseException(ResultCodeEnum.VERSION);
+ }
+ return true;
+
+ }
+
+ @Override
+ public void afterCompletion(HttpServletRequest request,
+ HttpServletResponse response, Object object, Exception e) {
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/platform/common/version/VersionUtils.java b/src/main/java/com/platform/common/version/VersionUtils.java
new file mode 100644
index 0000000..93261a3
--- /dev/null
+++ b/src/main/java/com/platform/common/version/VersionUtils.java
@@ -0,0 +1,103 @@
+package com.platform.common.version;
+
+import cn.hutool.core.util.ReUtil;
+import cn.hutool.core.util.StrUtil;
+import com.platform.common.enums.ResultCodeEnum;
+import com.platform.common.exception.BaseException;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+import org.springframework.util.AntPathMatcher;
+import org.springframework.util.PathMatcher;
+import org.springframework.util.StringUtils;
+
+import java.util.List;
+
+/**
+ * 版本比较
+ */
+@Slf4j
+@Component
+public class VersionUtils {
+
+ /**
+ * 比较版本大小
+ *
+ * 说明:支n位基础版本号+1位子版本号
+ * 示例:1.0.2>1.0.1
+ *
+ * @param version1 版本1
+ * @param version2 版本2
+ * @return 0:相同 >0:大于 <0:小于
+ */
+ public static int compareTo(String version1, String version2, String uri) {
+ if (!matchVersion(version1)) {
+ log.error("RequestURI:" + uri);
+ log.error("传入版本格式有误version1-{}", version1);
+ throw new BaseException(ResultCodeEnum.VERSION);
+ }
+ if (!matchVersion(version2)) {
+ log.error("RequestURI:" + uri);
+ log.error("系统版本格式有误version2-{}", version2);
+ throw new BaseException(ResultCodeEnum.VERSION);
+ }
+ if (version1.equals(version2)) {
+ return 0;
+ }
+ return versionStrToNum(version1) - versionStrToNum(version2);
+ }
+
+ /**
+ * 比较版本大小
+ *
+ * 说明:支n位基础版本号+1位子版本号
+ * 示例:1.0.2>1.0.1
+ *
+ * @param version1 版本1
+ * @param version2 版本2
+ * @return 0:相同 >0:大于 <0:小于
+ */
+ public static int compareTo(String version1, String version2) {
+ return compareTo(version1, version2, "-");
+ }
+
+ /**
+ * 版本号转换为数字
+ *
+ * @param versionStr
+ * @return
+ */
+ public static int versionStrToNum(String versionStr) {
+ List dataList = StrUtil.splitTrim(versionStr, ".");
+ StringBuilder builder = new StringBuilder()
+ .append(dataList.get(0))
+ .append(dataList.get(1))
+ .append(String.format("%03d", Integer.valueOf(dataList.get(2))));
+ return Integer.valueOf(builder.toString());
+ }
+
+ /**
+ * 匹配版本
+ *
+ * @param version
+ * @return
+ */
+ private static boolean matchVersion(String version) {
+ return ReUtil.isMatch("\\d{1,3}(\\.\\d{1,3}){2}", StringUtils.isEmpty(version) ? "" : version);
+ }
+
+ // 匹配器
+ private static final PathMatcher pathMatcher = new AntPathMatcher();
+
+ /**
+ * 验证地址
+ */
+ public static boolean verifyUrl(String currentUrl, List whiteList) {
+ for (String url : whiteList) {
+ if (pathMatcher.match(url, currentUrl)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/platform/common/web/aspectj/MybatisAspectj.java b/src/main/java/com/platform/common/web/aspectj/MybatisAspectj.java
new file mode 100644
index 0000000..1213a5f
--- /dev/null
+++ b/src/main/java/com/platform/common/web/aspectj/MybatisAspectj.java
@@ -0,0 +1,27 @@
+package com.platform.common.web.aspectj;
+
+import com.baomidou.mybatisplus.core.conditions.AbstractWrapper;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+import org.aspectj.lang.annotation.Pointcut;
+import org.springframework.stereotype.Component;
+
+@Aspect
+@Component
+public class MybatisAspectj {
+
+ // 配置织入点
+ @Pointcut("execution(public * com.baomidou.mybatisplus.core.mapper.BaseMapper.selectOne(..))")
+ public void selectOneAspect() {
+ }
+
+ @Before("selectOneAspect()")
+ public void beforeSelect(JoinPoint point) {
+ Object arg = point.getArgs()[0];
+ if (arg instanceof AbstractWrapper) {
+ ((AbstractWrapper) arg).last("limit 1");
+ }
+ }
+
+}
diff --git a/src/main/java/com/platform/common/web/controller/BaseController.java b/src/main/java/com/platform/common/web/controller/BaseController.java
new file mode 100644
index 0000000..2f48c59
--- /dev/null
+++ b/src/main/java/com/platform/common/web/controller/BaseController.java
@@ -0,0 +1,107 @@
+package com.platform.common.web.controller;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.StrUtil;
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
+import com.platform.common.core.EnumUtils;
+import com.platform.common.enums.GenderEnum;
+import com.platform.common.enums.YesOrNoEnum;
+import com.platform.common.web.domain.AjaxResult;
+import com.platform.common.web.domain.JsonDateDeserializer;
+import com.platform.common.web.page.PageDomain;
+import com.platform.common.web.page.TableDataInfo;
+import com.platform.common.web.page.TableSupport;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.WebDataBinder;
+import org.springframework.web.bind.annotation.InitBinder;
+
+import java.beans.PropertyEditorSupport;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * web层通用数据处理
+ */
+@Slf4j
+public class BaseController {
+
+ /**
+ * 将前台传递过来的日期格式的字符串,自动转化为Date类型
+ */
+ @InitBinder
+ public void initBinder(WebDataBinder binder) {
+ // Date 类型转换
+ binder.registerCustomEditor(Date.class, new PropertyEditorSupport() {
+ @Override
+ public void setAsText(String text) {
+ setValue(JsonDateDeserializer.parseDate(text));
+ }
+ });
+ // YesOrNoEnum 类型转换
+ binder.registerCustomEditor(YesOrNoEnum.class, new PropertyEditorSupport() {
+ @Override
+ public void setAsText(String text) {
+ setValue(EnumUtils.toEnum(YesOrNoEnum.class, text));
+ }
+ });
+ // GenderTypeEnum 类型转换
+ binder.registerCustomEditor(GenderEnum.class, new PropertyEditorSupport() {
+ @Override
+ public void setAsText(String text) {
+ setValue(EnumUtils.toEnum(GenderEnum.class, text));
+ }
+ });
+ }
+
+ /**
+ * 设置请求分页数据
+ */
+ protected void startPage() {
+ PageDomain pageDomain = TableSupport.getPageDomain();
+ startPage(PageDomain.escapeOrderBySql(pageDomain.getOrderBy()));
+ }
+
+ /**
+ * 设置请求分页数据
+ */
+ protected void startPage(String orderBy) {
+ PageDomain pageDomain = TableSupport.getPageDomain();
+ PageHelper.startPage(pageDomain.getPageNum(), pageDomain.getPageSize(), StrUtil.toUnderlineCase(orderBy));
+ }
+
+ /**
+ * 设置排序分页数据
+ */
+ protected void orderBy(String orderBy) {
+ PageHelper.orderBy(StrUtil.toUnderlineCase(orderBy));
+ }
+
+ /**
+ * 响应请求分页数据
+ */
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ protected TableDataInfo getDataTable(List> list) {
+ return new TableDataInfo(list, new PageInfo(list).getTotal());
+ }
+
+ protected TableDataInfo getDataTable(List> list, PageDomain pageDomain) {
+ return getDataTable(CollUtil.sub(list, pageDomain.getPageStart(), pageDomain.getPageEnd()));
+ }
+
+ /**
+ * 响应请求分页数据
+ */
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ protected TableDataInfo getDataTable(PageInfo> list) {
+ return new TableDataInfo(list.getList(), list.getTotal());
+ }
+
+ /**
+ * 响应返回结果
+ */
+ protected AjaxResult toAjax(int rows) {
+ return rows > 0 ? AjaxResult.success() : AjaxResult.fail();
+ }
+
+}
diff --git a/src/main/java/com/platform/common/web/controller/ErrorController.java b/src/main/java/com/platform/common/web/controller/ErrorController.java
new file mode 100644
index 0000000..2156f5d
--- /dev/null
+++ b/src/main/java/com/platform/common/web/controller/ErrorController.java
@@ -0,0 +1,33 @@
+package com.platform.common.web.controller;
+
+import com.platform.common.aspectj.IgnoreAuth;
+import com.platform.common.core.EnumUtils;
+import com.platform.common.enums.ResultCodeEnum;
+import com.platform.common.web.domain.AjaxResult;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 错误请求处理
+ */
+@RestController
+@RequestMapping("/error")
+@Slf4j
+public class ErrorController {
+
+ @IgnoreAuth
+ @RequestMapping("/{code}")
+ public AjaxResult error(@PathVariable String code) {
+ ResultCodeEnum resultCode = EnumUtils.toEnum(ResultCodeEnum.class, code, ResultCodeEnum.FAIL);
+ switch (resultCode) {
+ case SUCCESS:
+ return AjaxResult.success();
+ default:
+ return AjaxResult.result(resultCode);
+ }
+
+ }
+
+}
diff --git a/src/main/java/com/platform/common/web/dao/BaseDao.java b/src/main/java/com/platform/common/web/dao/BaseDao.java
new file mode 100644
index 0000000..f7a0620
--- /dev/null
+++ b/src/main/java/com/platform/common/web/dao/BaseDao.java
@@ -0,0 +1,44 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * https://www.q3z3.com
+ * QQ : 939313737
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.platform.common.web.dao;
+
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Dao基类
+ */
+public interface BaseDao extends BaseMapper {
+
+ /**
+ * 批量插入 仅适用于mysql
+ */
+ Integer insertBatchSomeColumn(Collection entityList);
+
+ /**
+ * 注意:使用此方法时,sql末尾需要增加${ew.customSqlSegment}
+ *
+ * 支持单表、和联查
+ */
+ List search(@Param("ew") Wrapper queryWrapper);
+
+}
diff --git a/src/main/java/com/platform/common/web/domain/AjaxResult.java b/src/main/java/com/platform/common/web/domain/AjaxResult.java
new file mode 100644
index 0000000..4a99373
--- /dev/null
+++ b/src/main/java/com/platform/common/web/domain/AjaxResult.java
@@ -0,0 +1,129 @@
+package com.platform.common.web.domain;
+
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+import com.platform.common.core.EnumUtils;
+import com.platform.common.enums.ResultCodeEnum;
+import org.springframework.util.StringUtils;
+
+import java.nio.charset.Charset;
+import java.util.HashMap;
+
+/**
+ * 操作消息提醒
+ */
+public class AjaxResult extends HashMap {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 状态码
+ */
+ public static final String CODE_TAG = "code";
+
+ /**
+ * 返回内容
+ */
+ public static final String MSG_TAG = "msg";
+
+ /**
+ * 数据对象
+ */
+ public static final String DATA_TAG = "data";
+
+ /**
+ * 初始化一个新创建的 AjaxResult 对象
+ */
+ public AjaxResult(ResultCodeEnum resultCode, String msg, Object data) {
+ super.put(CODE_TAG, resultCode.getCode());
+ super.put(MSG_TAG, StringUtils.isEmpty(msg) ? resultCode.getInfo() : msg);
+ if (data != null) {
+ super.put(DATA_TAG, data);
+ }
+ }
+
+ /**
+ * 返回成功消息
+ */
+ public static AjaxResult success() {
+ return new AjaxResult(ResultCodeEnum.SUCCESS, null, null);
+ }
+
+ /**
+ * 返回成功数据
+ */
+ public static AjaxResult success(Object data) {
+ return new AjaxResult(ResultCodeEnum.SUCCESS, null, data);
+ }
+
+ /**
+ * 返回成功消息
+ */
+ public static AjaxResult successMsg(String msg) {
+ return new AjaxResult(ResultCodeEnum.SUCCESS, msg, null);
+ }
+
+ /**
+ * 返回错误消息
+ */
+ public static AjaxResult fail() {
+ return new AjaxResult(ResultCodeEnum.FAIL, null, null);
+ }
+
+ /**
+ * 返回错误消息
+ */
+ public static AjaxResult fail(String msg) {
+ return new AjaxResult(ResultCodeEnum.FAIL, msg, null);
+ }
+
+ /**
+ * 返回错误消息
+ */
+ public static AjaxResult result(ResultCodeEnum resultCode) {
+ return new AjaxResult(resultCode, resultCode.getInfo(), null);
+ }
+
+ /**
+ * 返回错误消息
+ */
+ public static AjaxResult result(ResultCodeEnum resultCode, String msg) {
+ if (StringUtils.isEmpty(msg)) {
+ msg = resultCode.getInfo();
+ }
+ return new AjaxResult(resultCode, msg, null);
+ }
+
+ @Override
+ public AjaxResult put(String key, Object value) {
+ super.put(key, value);
+ return this;
+ }
+
+ public ResultCodeEnum getCode() {
+ Object code = this.get(CODE_TAG);
+ return EnumUtils.toEnum(ResultCodeEnum.class, code.toString(), ResultCodeEnum.FAIL);
+ }
+
+ public Object getData() {
+ return this.get(DATA_TAG);
+ }
+
+ public String getDataStr() {
+ Object data = getData();
+ return StrUtil.str(data, Charset.defaultCharset());
+ }
+
+ public static AjaxResult ajax(String json) {
+ if (StringUtils.isEmpty(json)) {
+ return AjaxResult.fail();
+ }
+ JSONObject jsonObject = JSONUtil.parseObj(json);
+ String code = StrUtil.str(jsonObject.get(CODE_TAG), Charset.defaultCharset());
+ String msg = StrUtil.str(jsonObject.get(MSG_TAG), Charset.defaultCharset());
+ ResultCodeEnum resultCode = EnumUtils.toEnum(ResultCodeEnum.class, code, ResultCodeEnum.FAIL);
+ return new AjaxResult(resultCode, msg, jsonObject.get(DATA_TAG));
+ }
+
+}
diff --git a/src/main/java/com/platform/common/web/domain/BaseEntity.java b/src/main/java/com/platform/common/web/domain/BaseEntity.java
new file mode 100644
index 0000000..85a9bc3
--- /dev/null
+++ b/src/main/java/com/platform/common/web/domain/BaseEntity.java
@@ -0,0 +1,63 @@
+package com.platform.common.web.domain;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.Map;
+
+/**
+ * Entity基类
+ */
+@Data
+@Accessors(chain = true) // 链式调用
+public class BaseEntity implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 开始时间
+ */
+ @TableField(exist = false)
+ @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
+ private Date beginTime;
+
+ /**
+ * 结束时间
+ */
+ @TableField(exist = false)
+ @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
+ private Date endTime;
+
+ /**
+ * 请求参数
+ */
+ @TableField(exist = false)
+ @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
+ private String param;
+
+ /**
+ * 请求参数
+ */
+ @TableField(exist = false)
+ @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
+ private Map params;
+
+ /**
+ * 返回参数
+ */
+ @TableField(exist = false)
+ @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
+ private String label;
+
+ /**
+ * 返回参数
+ */
+ @TableField(exist = false)
+ @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
+ private Long count;
+
+}
diff --git a/src/main/java/com/platform/common/web/domain/JsonDateDeserializer.java b/src/main/java/com/platform/common/web/domain/JsonDateDeserializer.java
new file mode 100644
index 0000000..3fcf282
--- /dev/null
+++ b/src/main/java/com/platform/common/web/domain/JsonDateDeserializer.java
@@ -0,0 +1,46 @@
+package com.platform.common.web.domain;
+
+import cn.hutool.core.date.DateException;
+import cn.hutool.core.date.DateUtil;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+
+import java.io.IOException;
+import java.util.Date;
+
+/**
+ * 自定义时间转换
+ */
+public class JsonDateDeserializer extends JsonDeserializer {
+
+ @Override
+ public Date deserialize(JsonParser jsonParser, DeserializationContext context) throws IOException {
+ String date = jsonParser.getText();
+ return parseDate(date);
+ }
+
+ /**
+ * 前台日期格式
+ */
+ private static String[] parsePatterns = {
+ "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM",
+ "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM",
+ "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"};
+
+
+ /**
+ * 日期型字符串转化为日期 格式
+ */
+ public static Date parseDate(Object obj) {
+ if (obj == null) {
+ return null;
+ }
+ try {
+ return DateUtil.parse(obj.toString(), parsePatterns);
+ } catch (DateException e) {
+ return null;
+ }
+ }
+
+}
diff --git a/src/main/java/com/platform/common/web/exception/ErrorAttributesCustom.java b/src/main/java/com/platform/common/web/exception/ErrorAttributesCustom.java
new file mode 100644
index 0000000..6b50019
--- /dev/null
+++ b/src/main/java/com/platform/common/web/exception/ErrorAttributesCustom.java
@@ -0,0 +1,18 @@
+package com.platform.common.web.exception;
+
+import com.platform.common.web.domain.AjaxResult;
+import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.request.WebRequest;
+
+import java.util.Map;
+
+@Component
+public class ErrorAttributesCustom extends DefaultErrorAttributes {
+
+ @Override
+ public Map getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
+ return AjaxResult.fail();
+ }
+
+}
diff --git a/src/main/java/com/platform/common/web/exception/GlobalExceptionHandler.java b/src/main/java/com/platform/common/web/exception/GlobalExceptionHandler.java
new file mode 100644
index 0000000..6950851
--- /dev/null
+++ b/src/main/java/com/platform/common/web/exception/GlobalExceptionHandler.java
@@ -0,0 +1,47 @@
+package com.platform.common.web.exception;
+
+import com.platform.common.enums.ResultCodeEnum;
+import com.platform.common.exception.BaseException;
+import com.platform.common.web.domain.AjaxResult;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+import org.springframework.web.servlet.NoHandlerFoundException;
+
+/**
+ * 全局异常处理器
+ */
+@RestControllerAdvice
+@Slf4j
+public class GlobalExceptionHandler {
+
+ @ExceptionHandler(Exception.class)
+ public AjaxResult handleException(Exception e) {
+ log.error("全局异常:" + e.getMessage(), e);
+ /**
+ * 路径不存在
+ */
+ if (e instanceof NoHandlerFoundException
+ || e instanceof org.springframework.web.HttpRequestMethodNotSupportedException) {
+ return AjaxResult.result(ResultCodeEnum.NOT_FOUND);
+ }
+ /**
+ * 校验异常
+ */
+ if (e instanceof MethodArgumentNotValidException) {
+ return AjaxResult.fail(((MethodArgumentNotValidException) e).getBindingResult().getFieldError().getDefaultMessage());
+ }
+ /**
+ * 自定义异常
+ */
+ if (e instanceof BaseException) {
+ if (ResultCodeEnum.VERSION.equals(((BaseException) e).getResultCode())) {
+ return AjaxResult.result(ResultCodeEnum.VERSION);
+ }
+ return AjaxResult.fail(e.getMessage());
+ }
+ return AjaxResult.fail();
+ }
+
+}
diff --git a/src/main/java/com/platform/common/web/interceptor/CustomizationBean.java b/src/main/java/com/platform/common/web/interceptor/CustomizationBean.java
new file mode 100644
index 0000000..112c984
--- /dev/null
+++ b/src/main/java/com/platform/common/web/interceptor/CustomizationBean.java
@@ -0,0 +1,20 @@
+package com.platform.common.web.interceptor;
+
+import io.undertow.server.DefaultByteBufferPool;
+import io.undertow.websockets.jsr.WebSocketDeploymentInfo;
+import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory;
+import org.springframework.boot.web.server.WebServerFactoryCustomizer;
+import org.springframework.stereotype.Component;
+
+@Component
+public class CustomizationBean implements WebServerFactoryCustomizer {
+
+ @Override
+ public void customize(UndertowServletWebServerFactory factory) {
+ factory.addDeploymentInfoCustomizers(deploymentInfo -> {
+ WebSocketDeploymentInfo webSocketDeploymentInfo = new WebSocketDeploymentInfo();
+ webSocketDeploymentInfo.setBuffers(new DefaultByteBufferPool(false, 1024));
+ deploymentInfo.addServletContextAttribute("io.undertow.websockets.jsr.WebSocketDeploymentInfo", webSocketDeploymentInfo);
+ });
+ }
+}
diff --git a/src/main/java/com/platform/common/web/page/PageDomain.java b/src/main/java/com/platform/common/web/page/PageDomain.java
new file mode 100644
index 0000000..e146206
--- /dev/null
+++ b/src/main/java/com/platform/common/web/page/PageDomain.java
@@ -0,0 +1,62 @@
+package com.platform.common.web.page;
+
+import cn.hutool.core.util.StrUtil;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.util.StringUtils;
+
+/**
+ * 分页数据
+ */
+@Data
+@NoArgsConstructor
+public class PageDomain {
+ /**
+ * 当前记录起始索引
+ */
+ private Integer pageNum;
+ /**
+ * 每页显示记录数
+ */
+ private Integer pageSize;
+ /**
+ * 排序列
+ */
+ private String orderBy;
+ /**
+ * 排序方向 "desc" 或者 "asc"
+ */
+ private String orderSort;
+
+ public String getOrderBy() {
+ if (StringUtils.isEmpty(orderBy)) {
+ return "";
+ }
+ return StrUtil.toUnderlineCase(orderBy) + " " + orderSort;
+ }
+
+ public Integer getPageStart() {
+ return (getPageNum() - 1) * getPageSize();
+ }
+
+ public Integer getPageEnd() {
+ return getPageStart() + getPageSize();
+ }
+
+ /**
+ * 记录总数
+ */
+ private Long total;
+
+ /**
+ * 检查字符,防止注入绕过
+ */
+ public static String escapeOrderBySql(String value) {
+ // 仅支持字母、数字、下划线、空格、逗号(支持多个字段排序)
+ String SQL_PATTERN = "[a-zA-Z0-9_\\ \\,]+";
+ if (!StringUtils.isEmpty(value) && !value.matches(SQL_PATTERN)) {
+ return "";
+ }
+ return StrUtil.toUnderlineCase(value);
+ }
+}
diff --git a/src/main/java/com/platform/common/web/page/TableDataInfo.java b/src/main/java/com/platform/common/web/page/TableDataInfo.java
new file mode 100644
index 0000000..fdb22f8
--- /dev/null
+++ b/src/main/java/com/platform/common/web/page/TableDataInfo.java
@@ -0,0 +1,55 @@
+package com.platform.common.web.page;
+
+import com.platform.common.enums.ResultCodeEnum;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * 表格分页数据对象
+ */
+@Data
+@Accessors(chain = true) // 链式调用
+public class TableDataInfo extends HashMap {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 状态码
+ */
+ public static final String CODE_TAG = "code";
+
+ /**
+ * 返回内容
+ */
+ public static final String MSG_TAG = "msg";
+
+ /**
+ * 数据对象
+ */
+ public static final String ROWS_TAG = "rows";
+
+ /**
+ * 数据对象
+ */
+ public static final String TOTAL_TAG = "total";
+
+ /**
+ * 分页
+ */
+ public TableDataInfo(List> list, Long total) {
+ ResultCodeEnum result = ResultCodeEnum.SUCCESS;
+ super.put(CODE_TAG, result.getCode());
+ super.put(MSG_TAG, result.getInfo());
+ super.put(ROWS_TAG, list);
+ super.put(TOTAL_TAG, total.intValue());
+ }
+
+ @Override
+ public TableDataInfo put(String key, Object value) {
+ super.put(key, value);
+ return this;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/platform/common/web/page/TableSupport.java b/src/main/java/com/platform/common/web/page/TableSupport.java
new file mode 100644
index 0000000..db15f24
--- /dev/null
+++ b/src/main/java/com/platform/common/web/page/TableSupport.java
@@ -0,0 +1,41 @@
+package com.platform.common.web.page;
+
+import com.platform.common.utils.ServletUtils;
+
+/**
+ * 表格数据处理
+ */
+public class TableSupport {
+ /**
+ * 当前记录起始索引
+ */
+ public static final String PAGE_NUM = "pageNum";
+
+ /**
+ * 每页显示记录数
+ */
+ public static final String PAGE_SIZE = "pageSize";
+
+ /**
+ * 排序列
+ */
+ public static final String ORDER_BY = "orderBy";
+
+ /**
+ * 排序方向 "desc" 或者 "asc"
+ */
+ public static final String ORDER_SORT = "orderSort";
+
+ /**
+ * 封装分页对象
+ */
+ public static PageDomain getPageDomain() {
+ PageDomain pageDomain = new PageDomain();
+ pageDomain.setPageNum(ServletUtils.getParameterToInt(PAGE_NUM, 1));
+ pageDomain.setPageSize(ServletUtils.getParameterToInt(PAGE_SIZE, 10));
+ pageDomain.setOrderBy(ServletUtils.getParameter(ORDER_BY));
+ pageDomain.setOrderSort(ServletUtils.getParameter(ORDER_SORT, "asc"));
+ return pageDomain;
+ }
+
+}
diff --git a/src/main/java/com/platform/common/web/service/BaseService.java b/src/main/java/com/platform/common/web/service/BaseService.java
new file mode 100644
index 0000000..238991b
--- /dev/null
+++ b/src/main/java/com/platform/common/web/service/BaseService.java
@@ -0,0 +1,120 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * https://www.q3z3.com
+ * QQ : 939313737
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.platform.common.web.service;
+
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 基础service接口层基类
+ */
+public interface BaseService {
+
+ /**
+ * 新增(并返回id)
+ */
+ Integer add(T entity);
+
+ /**
+ * 根据 id 删除
+ */
+ Integer deleteById(Long id);
+
+ /**
+ * 根据ids 批量删除
+ */
+ Integer deleteByIds(Long[] ids);
+
+ /**
+ * 根据ids 批量删除
+ */
+ Integer deleteByIds(List ids);
+
+ /**
+ * 根据 id 修改
+ */
+ Integer updateById(T entity);
+
+ /**
+ * 根据条件更新
+ */
+ Integer update(Wrapper wrapper);
+
+ /**
+ * 根据 id 查询
+ */
+ T getById(Long id);
+
+ /**
+ * 根据 id 查询
+ */
+ T findById(Long id);
+
+ /**
+ * 根据 ids 查询
+ */
+ List getByIds(Collection extends Serializable> idList);
+
+ /**
+ * 查询总记录数
+ */
+ Long queryCount(T t);
+
+ /**
+ * 查询总记录数
+ */
+ Long queryCount(Wrapper wrapper);
+
+ /**
+ * 查询全部记录
+ */
+ List queryList(T t);
+
+ /**
+ * 查询全部记录
+ */
+ List queryList(Wrapper wrapper);
+
+ /**
+ * 查询一个
+ */
+ T queryOne(T t);
+
+ /**
+ * 查询一个
+ */
+ T queryOne(Wrapper wrapper);
+
+ /**
+ * 批量新增
+ */
+ Integer batchAdd(List list);
+
+ /**
+ * 批量新增(仅mysql可以使用)
+ */
+ Integer batchAdd(List list, Integer batchCount);
+
+ /**
+ * 修改状态
+ */
+ void changeStatus(T t, String... param);
+}
diff --git a/src/main/java/com/platform/common/web/service/impl/BaseServiceImpl.java b/src/main/java/com/platform/common/web/service/impl/BaseServiceImpl.java
new file mode 100644
index 0000000..4c49def
--- /dev/null
+++ b/src/main/java/com/platform/common/web/service/impl/BaseServiceImpl.java
@@ -0,0 +1,206 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * https://www.q3z3.com
+ * QQ : 939313737
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.platform.common.web.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.github.pagehelper.PageInfo;
+import com.platform.common.exception.BaseException;
+import com.platform.common.utils.BeanCopyUtils;
+import com.platform.common.web.dao.BaseDao;
+import com.platform.common.web.service.BaseService;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.CollectionUtils;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+
+/**
+ * 基础service实现层基类
+ */
+public class BaseServiceImpl implements BaseService {
+
+ protected final static String EXIST_MSG = "数据不存在";
+
+ private BaseDao baseDao;
+
+ public void setBaseDao(BaseDao baseDao) {
+ this.baseDao = baseDao;
+ }
+
+ private static final Integer batchCount = 1000;
+
+ @Override
+ public Integer add(T entity) {
+ return baseDao.insert(entity);
+ }
+
+ @Override
+ public Integer deleteById(Long id) {
+ return baseDao.deleteById(id);
+ }
+
+ @Override
+ public Integer deleteByIds(Long[] ids) {
+ return baseDao.deleteBatchIds(Arrays.asList(ids));
+ }
+
+ @Override
+ public Integer deleteByIds(List ids) {
+ return baseDao.deleteBatchIds(ids);
+ }
+
+ @Override
+ public Integer updateById(T entity) {
+ return baseDao.updateById(entity);
+ }
+
+ @Override
+ public T getById(Long id) {
+ return baseDao.selectById(id);
+ }
+
+ /**
+ * eq: Wrapper wrapper = Wrappers.lambdaUpdate().set(ChatFriend::getRemark, null).eq(ChatFriend::getId, friend.getId());
+ */
+ @Override
+ public Integer update(Wrapper wrapper) {
+ return baseDao.update(null, wrapper);
+ }
+
+ @Override
+ public T findById(Long id) {
+ T t = getById(id);
+ if (t == null) {
+ throw new BaseException(EXIST_MSG);
+ }
+ return t;
+ }
+
+ @Override
+ public List getByIds(Collection extends Serializable> idList) {
+ return baseDao.selectBatchIds(idList);
+ }
+
+ @Override
+ public Long queryCount(T t) {
+ Wrapper wrapper = new QueryWrapper<>(t);
+ return queryCount(wrapper);
+ }
+
+ @Override
+ public Long queryCount(Wrapper wrapper) {
+ return baseDao.selectCount(wrapper).longValue();
+ }
+
+ @Override
+ public List queryList(T t) {
+ Wrapper wrapper = new QueryWrapper<>(t);
+ return queryList(wrapper);
+ }
+
+ @Override
+ public List queryList(Wrapper wrapper) {
+ return baseDao.selectList(wrapper);
+ }
+
+ @Override
+ public T queryOne(T t) {
+ Wrapper wrapper = new QueryWrapper<>(t);
+ return queryOne(wrapper);
+ }
+
+ @Override
+ public T queryOne(Wrapper wrapper) {
+ return baseDao.selectOne(wrapper);
+ }
+
+ @Override
+ public Integer batchAdd(List list) {
+ return batchAdd(list, batchCount);
+ }
+
+ @Transactional
+ @Override
+ public Integer batchAdd(List list, Integer batchCount) {
+ if (CollectionUtils.isEmpty(list)) {
+ return 0;
+ }
+ List batchList = new ArrayList<>();
+ AtomicReference result = new AtomicReference<>(0);
+ list.forEach((o) -> {
+ batchList.add(o);
+ if (batchList.size() == batchCount) {
+ result.updateAndGet(v -> v + baseDao.insertBatchSomeColumn(batchList));
+ batchList.clear();
+ }
+ });
+ if (!CollectionUtils.isEmpty(batchList)) {
+ result.updateAndGet(v -> v + baseDao.insertBatchSomeColumn(batchList));
+ batchList.clear();
+ }
+ return result.get();
+ }
+
+ /**
+ * 循环工具类
+ */
+ public static Consumer forEach(BiConsumer consumer) {
+ class Obj {
+ int i;
+ }
+ Obj obj = new Obj();
+ return t -> {
+ int index = obj.i++;
+ consumer.accept(t, index);
+ };
+ }
+
+ @Override
+ public void changeStatus(T t, String... param) {
+ if (param.length == 0) {
+ this.updateById(BeanCopyUtils.include(t, "id", "status"));
+ } else {
+ this.updateById(BeanCopyUtils.include(t, param));
+ }
+ }
+
+ /**
+ * 响应请求分页数据
+ */
+ protected PageInfo getPageInfo(List> list, List> oldList) {
+ Long total = new PageInfo(oldList).getTotal();
+ return getPageInfo(list, total);
+ }
+
+ /**
+ * 格式化分页
+ */
+ public PageInfo getPageInfo(List> list, long total) {
+ PageInfo pageInfo = new PageInfo(list);
+ pageInfo.setTotal(total);
+ return pageInfo;
+ }
+
+}
diff --git a/src/main/java/com/platform/modules/auth/controller/AuthController.java b/src/main/java/com/platform/modules/auth/controller/AuthController.java
new file mode 100644
index 0000000..7b8d1db
--- /dev/null
+++ b/src/main/java/com/platform/modules/auth/controller/AuthController.java
@@ -0,0 +1,131 @@
+package com.platform.modules.auth.controller;
+
+import cn.hutool.core.lang.Dict;
+import com.platform.common.aspectj.IgnoreAuth;
+import com.platform.common.aspectj.SubmitRepeat;
+import com.platform.common.exception.BaseException;
+import com.platform.common.shiro.ShiroLoginAuth;
+import com.platform.common.shiro.ShiroLoginPhone;
+import com.platform.common.version.ApiVersion;
+import com.platform.common.version.VersionEnum;
+import com.platform.common.web.controller.BaseController;
+import com.platform.common.web.domain.AjaxResult;
+import com.platform.modules.auth.vo.AuthVo01;
+import com.platform.modules.auth.vo.AuthVo02;
+import com.platform.modules.auth.vo.AuthVo03;
+import com.platform.modules.auth.vo.AuthVo04;
+import com.platform.modules.chat.domain.ChatUser;
+import com.platform.modules.chat.service.ChatUserService;
+import com.platform.modules.sms.enums.SmsTypeEnum;
+import com.platform.modules.sms.service.SmsService;
+import com.platform.modules.sms.vo.SmsVo;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.validation.annotation.Validated;
+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.annotation.Resource;
+
+/**
+ * 认证
+ */
+@RestController
+@Slf4j
+@RequestMapping("/auth")
+public class AuthController extends BaseController {
+
+ @Resource
+ private ChatUserService chatUserService;
+
+ @Resource
+ private SmsService smsService;
+
+ /**
+ * 发送验证码(登录/注册/忘记密码)
+ */
+ @IgnoreAuth
+ @ApiVersion(VersionEnum.V1_0_0)
+ @PostMapping(value = "/sendCode")
+ @SubmitRepeat
+ public AjaxResult sendCode(@Validated @RequestBody SmsVo smsVo) {
+ ChatUser chatUser = chatUserService.queryByPhone(smsVo.getPhone());
+ switch (smsVo.getType()) {
+ case LOGIN:
+ case FORGET:
+ if (chatUser == null) {
+ throw new BaseException("用户未注册,请先注册");
+ }
+ break;
+ case REGISTERED:
+ if (chatUser != null) {
+ throw new BaseException("用户已注册,请直接登录");
+ }
+ break;
+ }
+ return AjaxResult.success(smsService.sendSms(smsVo)).put("msg", "验证码已发送");
+ }
+
+ /**
+ * 注册方法
+ */
+ @IgnoreAuth
+ @ApiVersion(VersionEnum.V1_0_0)
+ @PostMapping(value = "/register")
+ public AjaxResult register(@Validated @RequestBody AuthVo01 authVo) {
+ // 验证
+ smsService.verifySms(authVo.getPhone(), authVo.getCode(), SmsTypeEnum.REGISTERED);
+ // 注册
+ chatUserService.register(authVo);
+ return AjaxResult.successMsg("注册成功,请登录");
+ }
+
+ /**
+ * 登录方法(根据手机+密码登录)
+ */
+ @IgnoreAuth
+ @ApiVersion(VersionEnum.V1_0_0)
+ @PostMapping("/login")
+ public AjaxResult login(@Validated @RequestBody AuthVo02 authVo) {
+ // 执行登录
+ ShiroLoginAuth loginAuth = new ShiroLoginAuth(authVo.getPhone(), authVo.getPassword());
+ Dict dict = chatUserService.doLogin(loginAuth);
+ return AjaxResult.success(dict);
+ }
+
+ /**
+ * 登录方法(根据手机+验证码登录)
+ */
+ @IgnoreAuth
+ @ApiVersion(VersionEnum.V1_0_0)
+ @PostMapping("/loginByCode")
+ public AjaxResult loginByCode(@Validated @RequestBody AuthVo03 authVo) {
+ // 验证
+ smsService.verifySms(authVo.getPhone(), authVo.getCode(), SmsTypeEnum.LOGIN);
+ // 执行登录
+ ShiroLoginPhone loginPhone = new ShiroLoginPhone(authVo.getPhone());
+ Dict dict = chatUserService.doLogin(loginPhone);
+ return AjaxResult.success(dict);
+ }
+
+ /**
+ * 找回密码(根据手机)
+ */
+ @IgnoreAuth
+ @ApiVersion(VersionEnum.V1_0_0)
+ @PostMapping("/forget")
+ public AjaxResult forget(@Validated @RequestBody AuthVo04 authVo) {
+ // 验证
+ smsService.verifySms(authVo.getPhone(), authVo.getCode(), SmsTypeEnum.FORGET);
+ // 查询用户
+ ChatUser cu = chatUserService.queryByPhone(authVo.getPhone());
+ if (cu == null) {
+ throw new BaseException("手机号不存在");
+ }
+ // 重置密码
+ chatUserService.resetPass(cu.getUserId(), authVo.getPassword());
+ return AjaxResult.successMsg("您的密码重置成功,请重新登录");
+ }
+
+}
diff --git a/src/main/java/com/platform/modules/auth/service/TokenService.java b/src/main/java/com/platform/modules/auth/service/TokenService.java
new file mode 100644
index 0000000..5c10c44
--- /dev/null
+++ b/src/main/java/com/platform/modules/auth/service/TokenService.java
@@ -0,0 +1,44 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * https://www.q3z3.com
+ * QQ : 939313737
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.platform.modules.auth.service;
+
+import com.platform.common.shiro.LoginUser;
+
+/**
+ *
+ * token 服务层
+ *
+ */
+public interface TokenService {
+
+ /**
+ * 生成token
+ */
+ String generateToken();
+
+ /**
+ * 通过token查询
+ */
+ LoginUser queryByToken(String token);
+
+ /**
+ * 删除token
+ */
+ void deleteToken(String token);
+
+}
diff --git a/src/main/java/com/platform/modules/auth/service/impl/TokenServiceImpl.java b/src/main/java/com/platform/modules/auth/service/impl/TokenServiceImpl.java
new file mode 100644
index 0000000..d717ed9
--- /dev/null
+++ b/src/main/java/com/platform/modules/auth/service/impl/TokenServiceImpl.java
@@ -0,0 +1,75 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * https://www.q3z3.com
+ * QQ : 939313737
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.platform.modules.auth.service.impl;
+
+import cn.hutool.json.JSONUtil;
+import com.platform.common.config.PlatformConfig;
+import com.platform.common.constant.HeadConstant;
+import com.platform.common.redis.RedisUtils;
+import com.platform.common.shiro.ShiroUtils;
+import com.platform.common.shiro.LoginUser;
+import com.platform.modules.auth.service.TokenService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * token 服务层
+ */
+@Service("tokenService")
+public class TokenServiceImpl implements TokenService {
+
+ @Autowired
+ private RedisUtils redisUtils;
+
+ @Override
+ public String generateToken() {
+ LoginUser loginUser = ShiroUtils.getLoginUser();
+ String token = loginUser.getToken();
+ // 存储redis
+ redisUtils.set(makeToken(token), JSONUtil.toJsonStr(loginUser), PlatformConfig.TIMEOUT, TimeUnit.MINUTES);
+ return token;
+ }
+
+ @Override
+ public LoginUser queryByToken(String token) {
+ String key = makeToken(token);
+ if (!redisUtils.hasKey(key)) {
+ return null;
+ }
+ // 续期
+ redisUtils.expire(key, PlatformConfig.TIMEOUT, TimeUnit.MINUTES);
+ // 转换
+ return JSONUtil.toBean(redisUtils.get(key), LoginUser.class);
+ }
+
+ @Override
+ public void deleteToken(String token) {
+ if (StringUtils.isEmpty(token)) {
+ return;
+ }
+ redisUtils.delete(makeToken(token));
+ }
+
+ private String makeToken(String token) {
+ return HeadConstant.TOKEN_REDIS_APP + token;
+ }
+
+}
diff --git a/src/main/java/com/platform/modules/auth/vo/AuthVo01.java b/src/main/java/com/platform/modules/auth/vo/AuthVo01.java
new file mode 100644
index 0000000..c4ef122
--- /dev/null
+++ b/src/main/java/com/platform/modules/auth/vo/AuthVo01.java
@@ -0,0 +1,36 @@
+package com.platform.modules.auth.vo;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.Size;
+
+@Data
+public class AuthVo01 {
+
+ /**
+ * 手机号
+ */
+ @NotBlank(message = "手机号不能为空")
+ private String phone;
+
+ /**
+ * 密码
+ */
+ @NotBlank(message = "密码不能为空")
+ private String password;
+
+ /**
+ * 昵称
+ */
+ @NotBlank(message = "昵称不能为空")
+ @Size(max = 20, message = "昵称长度不能大于20")
+ private String nickName;
+
+ /**
+ * 验证码
+ */
+ @NotBlank(message = "验证码不能为空")
+ private String code;
+
+}
diff --git a/src/main/java/com/platform/modules/auth/vo/AuthVo02.java b/src/main/java/com/platform/modules/auth/vo/AuthVo02.java
new file mode 100644
index 0000000..89b0d33
--- /dev/null
+++ b/src/main/java/com/platform/modules/auth/vo/AuthVo02.java
@@ -0,0 +1,22 @@
+package com.platform.modules.auth.vo;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+
+@Data
+public class AuthVo02 {
+
+ /**
+ * 手机号
+ */
+ @NotBlank(message = "手机号不能为空")
+ private String phone;
+
+ /**
+ * 密码
+ */
+ @NotBlank(message = "密码不能为空")
+ private String password;
+
+}
diff --git a/src/main/java/com/platform/modules/auth/vo/AuthVo03.java b/src/main/java/com/platform/modules/auth/vo/AuthVo03.java
new file mode 100644
index 0000000..85b58aa
--- /dev/null
+++ b/src/main/java/com/platform/modules/auth/vo/AuthVo03.java
@@ -0,0 +1,22 @@
+package com.platform.modules.auth.vo;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+
+@Data
+public class AuthVo03 {
+
+ /**
+ * 手机号
+ */
+ @NotBlank(message = "手机号不能为空")
+ private String phone;
+
+ /**
+ * 验证码
+ */
+ @NotBlank(message = "验证码不能为空")
+ private String code;
+
+}
diff --git a/src/main/java/com/platform/modules/auth/vo/AuthVo04.java b/src/main/java/com/platform/modules/auth/vo/AuthVo04.java
new file mode 100644
index 0000000..87e691b
--- /dev/null
+++ b/src/main/java/com/platform/modules/auth/vo/AuthVo04.java
@@ -0,0 +1,28 @@
+package com.platform.modules.auth.vo;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+
+@Data
+public class AuthVo04 {
+
+ /**
+ * 手机号
+ */
+ @NotBlank(message = "手机号不能为空")
+ private String phone;
+
+ /**
+ * 验证码
+ */
+ @NotBlank(message = "验证码不能为空")
+ private String code;
+
+ /**
+ * 密码
+ */
+ @NotBlank(message = "密码不能为空")
+ private String password;
+
+}
diff --git a/src/main/java/com/platform/modules/chat/config/AmapConfig.java b/src/main/java/com/platform/modules/chat/config/AmapConfig.java
new file mode 100644
index 0000000..3faca2d
--- /dev/null
+++ b/src/main/java/com/platform/modules/chat/config/AmapConfig.java
@@ -0,0 +1,17 @@
+package com.platform.modules.chat.config;
+
+import lombok.Data;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+/**
+ * 读取高德地图相关配置
+ */
+@Component
+@Data
+public class AmapConfig {
+
+ @Value("${amap.key}")
+ private String key;
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/platform/modules/chat/config/TencentConfig.java b/src/main/java/com/platform/modules/chat/config/TencentConfig.java
new file mode 100644
index 0000000..c5eb46c
--- /dev/null
+++ b/src/main/java/com/platform/modules/chat/config/TencentConfig.java
@@ -0,0 +1,23 @@
+package com.platform.modules.chat.config;
+
+import lombok.Data;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+/**
+ * 腾讯nlp配置
+ */
+@Component
+@Data
+public class TencentConfig {
+
+ @Value("${tencent.appId}")
+ private String appId;
+
+ @Value("${tencent.appKey}")
+ private String appKey;
+
+ @Value("${tencent.appSecret}")
+ private String appSecret;
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/platform/modules/chat/controller/ApplyController.java b/src/main/java/com/platform/modules/chat/controller/ApplyController.java
new file mode 100644
index 0000000..42e9149
--- /dev/null
+++ b/src/main/java/com/platform/modules/chat/controller/ApplyController.java
@@ -0,0 +1,91 @@
+package com.platform.modules.chat.controller;
+
+import com.platform.common.version.ApiVersion;
+import com.platform.common.version.VersionEnum;
+import com.platform.common.web.controller.BaseController;
+import com.platform.common.web.domain.AjaxResult;
+import com.platform.common.web.page.TableDataInfo;
+import com.platform.modules.chat.service.ChatApplyService;
+import com.platform.modules.chat.service.ChatFriendService;
+import com.platform.modules.chat.vo.ApplyVo01;
+import com.platform.modules.chat.vo.FriendVo02;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+
+/**
+ * 申请
+ */
+@RestController
+@Slf4j
+@RequestMapping("/apply")
+public class ApplyController extends BaseController {
+
+ @Resource
+ private ChatFriendService chatFriendService;
+
+ @Resource
+ private ChatApplyService chatApplyService;
+
+ /**
+ * 申请添加
+ */
+ @ApiVersion(VersionEnum.V1_0_0)
+ @PostMapping("/add")
+ public AjaxResult add(@Validated @RequestBody FriendVo02 friendVo) {
+ chatFriendService.applyFriend(friendVo);
+ return AjaxResult.success();
+ }
+
+ /**
+ * 申请记录
+ */
+ @ApiVersion(VersionEnum.V1_0_0)
+ @GetMapping("/list")
+ public TableDataInfo list() {
+ startPage("create_time desc");
+ return getDataTable(chatApplyService.list());
+ }
+
+ /**
+ * 申请详情
+ */
+ @ApiVersion(VersionEnum.V1_0_0)
+ @GetMapping("/info/{applyId}")
+ public AjaxResult getInfo(@PathVariable Long applyId) {
+ return AjaxResult.success(chatApplyService.getInfo(applyId));
+ }
+
+ /**
+ * 同意申请
+ */
+ @ApiVersion(VersionEnum.V1_0_0)
+ @PostMapping("/agree")
+ public AjaxResult agree(@Validated @RequestBody ApplyVo01 applyVo) {
+ chatFriendService.agree(applyVo.getApplyId());
+ return AjaxResult.success();
+ }
+
+ /**
+ * 拒绝申请
+ */
+ @ApiVersion(VersionEnum.V1_0_0)
+ @PostMapping("/refused")
+ public AjaxResult refused(@Validated @RequestBody ApplyVo01 applyVo) {
+ chatFriendService.refused(applyVo.getApplyId());
+ return AjaxResult.success();
+ }
+
+ /**
+ * 忽略申请
+ */
+ @ApiVersion(VersionEnum.V1_0_0)
+ @PostMapping("/ignore")
+ public AjaxResult ignore(@Validated @RequestBody ApplyVo01 applyVo) {
+ chatFriendService.ignore(applyVo.getApplyId());
+ return AjaxResult.success();
+ }
+
+}
diff --git a/src/main/java/com/platform/modules/chat/controller/ChatController.java b/src/main/java/com/platform/modules/chat/controller/ChatController.java
new file mode 100644
index 0000000..a6af0ca
--- /dev/null
+++ b/src/main/java/com/platform/modules/chat/controller/ChatController.java
@@ -0,0 +1,35 @@
+package com.platform.modules.chat.controller;
+
+import com.platform.common.version.ApiVersion;
+import com.platform.common.version.VersionEnum;
+import com.platform.common.web.controller.BaseController;
+import com.platform.common.web.domain.AjaxResult;
+import com.platform.modules.chat.service.ChatMsgService;
+import com.platform.modules.chat.vo.ChatVo01;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+
+/**
+ * 聊天
+ */
+@RestController
+@Slf4j
+@RequestMapping("/chat")
+public class ChatController extends BaseController {
+
+ @Resource
+ private ChatMsgService chatMsgService;
+
+ /**
+ * 发送信息
+ */
+ @ApiVersion(VersionEnum.V1_0_0)
+ @PostMapping("/sendMsg")
+ public AjaxResult sendMsg(@Validated @RequestBody ChatVo01 chatVo) {
+ return AjaxResult.success(chatMsgService.sendFriendMsg(chatVo));
+ }
+
+}
diff --git a/src/main/java/com/platform/modules/chat/controller/FriendController.java b/src/main/java/com/platform/modules/chat/controller/FriendController.java
new file mode 100644
index 0000000..6d9fe89
--- /dev/null
+++ b/src/main/java/com/platform/modules/chat/controller/FriendController.java
@@ -0,0 +1,95 @@
+package com.platform.modules.chat.controller;
+
+import com.platform.common.version.ApiVersion;
+import com.platform.common.version.VersionEnum;
+import com.platform.common.web.controller.BaseController;
+import com.platform.common.web.domain.AjaxResult;
+import com.platform.modules.chat.service.ChatFriendService;
+import com.platform.modules.chat.vo.*;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import java.util.List;
+
+/**
+ * 好友
+ */
+@RestController
+@Slf4j
+@RequestMapping("/friend")
+public class FriendController extends BaseController {
+
+ @Resource
+ private ChatFriendService chatFriendService;
+
+ /**
+ * 搜索好友
+ */
+ @ApiVersion(VersionEnum.V1_0_0)
+ @PostMapping("/findFriend")
+ public AjaxResult findFriend(@Validated @RequestBody FriendVo01 friendVo) {
+ return AjaxResult.success(chatFriendService.findFriend(friendVo.getParam()));
+ }
+
+ /**
+ * 好友列表
+ */
+ @ApiVersion(VersionEnum.V1_0_0)
+ @PostMapping("/friendList")
+ public AjaxResult friendList(@Validated @RequestBody FriendVo08 friendVo) {
+ List list = chatFriendService.friendList(friendVo.getParam());
+ return AjaxResult.success(list);
+ }
+
+ /**
+ * 好友详情
+ */
+ @ApiVersion(VersionEnum.V1_0_0)
+ @GetMapping("/info/{friendId}")
+ public AjaxResult getInfo(@PathVariable Long friendId) {
+ return AjaxResult.success(chatFriendService.getInfo(friendId));
+ }
+
+ /**
+ * 设置黑名单
+ */
+ @ApiVersion(VersionEnum.V1_0_0)
+ @PostMapping("/black")
+ public AjaxResult black(@Validated @RequestBody FriendVo03 friendVo) {
+ chatFriendService.setBlack(friendVo);
+ return AjaxResult.success();
+ }
+
+ /**
+ * 删除好友
+ */
+ @ApiVersion(VersionEnum.V1_0_0)
+ @PostMapping("/delFriend")
+ public AjaxResult delFriend(@Validated @RequestBody FriendVo04 friendVo) {
+ chatFriendService.delFriend(friendVo.getUserId());
+ return AjaxResult.success();
+ }
+
+ /**
+ * 设置备注
+ */
+ @ApiVersion(VersionEnum.V1_0_0)
+ @PostMapping("/remark")
+ public AjaxResult remark(@Validated @RequestBody FriendVo05 friendVo) {
+ chatFriendService.setRemark(friendVo);
+ return AjaxResult.success();
+ }
+
+ /**
+ * 设置是否置顶
+ */
+ @ApiVersion(VersionEnum.V1_0_0)
+ @PostMapping("/top")
+ public AjaxResult top(@Validated @RequestBody FriendVo09 friendVo) {
+ chatFriendService.setTop(friendVo);
+ return AjaxResult.success();
+ }
+
+}
diff --git a/src/main/java/com/platform/modules/chat/controller/GroupController.java b/src/main/java/com/platform/modules/chat/controller/GroupController.java
new file mode 100644
index 0000000..c401bd7
--- /dev/null
+++ b/src/main/java/com/platform/modules/chat/controller/GroupController.java
@@ -0,0 +1,193 @@
+package com.platform.modules.chat.controller;
+
+import com.platform.common.version.ApiVersion;
+import com.platform.common.version.VersionEnum;
+import com.platform.common.web.controller.BaseController;
+import com.platform.common.web.domain.AjaxResult;
+import com.platform.modules.chat.service.ChatGroupService;
+import com.platform.modules.chat.service.ChatMsgService;
+import com.platform.modules.chat.vo.*;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import java.util.List;
+
+/**
+ * 群组
+ */
+@RestController
+@Slf4j
+@RequestMapping("/group")
+public class GroupController extends BaseController {
+
+ @Resource
+ private ChatGroupService chatGroupService;
+
+ @Resource
+ private ChatMsgService chatMsgService;
+
+ /**
+ * 建立群组
+ */
+ @ApiVersion(VersionEnum.V1_0_0)
+ @PostMapping("/createGroup")
+ public AjaxResult createGroup(@Validated @RequestBody List list) {
+ chatGroupService.createGroup(list);
+ return AjaxResult.success();
+ }
+
+ /**
+ * 获取群详情
+ */
+ @ApiVersion(VersionEnum.V1_0_0)
+ @GetMapping("/getInfo/{groupId}")
+ public AjaxResult getInfo(@PathVariable Long groupId) {
+ return AjaxResult.success(chatGroupService.getInfo(groupId));
+ }
+
+ /**
+ * 邀请进群
+ */
+ @ApiVersion(VersionEnum.V1_0_0)
+ @PostMapping("/invitationGroup")
+ public AjaxResult invitationGroup(@Validated @RequestBody GroupVo01 groupVo) {
+ Long groupId = groupVo.getGroupId();
+ List list = groupVo.getList();
+ chatGroupService.invitationGroup(groupId, list);
+ return AjaxResult.success();
+ }
+
+ /**
+ * 踢出群组
+ */
+ @ApiVersion(VersionEnum.V1_0_0)
+ @PostMapping("/kickedGroup")
+ public AjaxResult kickedGroup(@Validated @RequestBody GroupVo01 groupVo) {
+ Long groupId = groupVo.getGroupId();
+ List list = groupVo.getList();
+ chatGroupService.kickedGroup(groupId, list);
+ return AjaxResult.success();
+ }
+
+ /**
+ * 修改群名
+ */
+ @ApiVersion(VersionEnum.V1_0_0)
+ @PostMapping("/editGroupName")
+ public AjaxResult editGroupName(@Validated @RequestBody GroupVo02 groupVo) {
+ chatGroupService.editGroupName(groupVo);
+ return AjaxResult.success();
+ }
+
+ /**
+ * 获取群组二维码
+ */
+ @ApiVersion(VersionEnum.V1_0_0)
+ @GetMapping("/getGroupQrCode/{groupId}")
+ public AjaxResult getGroupQrCode(@PathVariable Long groupId) {
+ return AjaxResult.success(chatGroupService.getGroupQrCode(groupId));
+ }
+
+ /**
+ * 修改群公告
+ */
+ @ApiVersion(VersionEnum.V1_0_0)
+ @PostMapping("/editGroupNotice")
+ public AjaxResult editGroupNotice(@Validated @RequestBody GroupVo03 groupVo) {
+ chatGroupService.editGroupNotice(groupVo);
+ return AjaxResult.success();
+ }
+
+ /**
+ * 是否置顶
+ */
+ @ApiVersion(VersionEnum.V1_0_0)
+ @PostMapping("/editTop")
+ public AjaxResult editTop(@Validated @RequestBody GroupVo04 groupVo) {
+ Long groupId = groupVo.getGroupId();
+ chatGroupService.editTop(groupId, groupVo.getTop());
+ return AjaxResult.success();
+ }
+
+ /**
+ * 是否免打扰
+ */
+ @ApiVersion(VersionEnum.V1_0_0)
+ @PostMapping("/editDisturb")
+ public AjaxResult editDisturb(@Validated @RequestBody GroupVo05 groupVo) {
+ Long groupId = groupVo.getGroupId();
+ chatGroupService.editDisturb(groupId, groupVo.getDisturb());
+ return AjaxResult.success();
+ }
+
+ /**
+ * 是否保存群组
+ */
+ @ApiVersion(VersionEnum.V1_0_0)
+ @PostMapping("/editKeepGroup")
+ public AjaxResult editKeepGroup(@Validated @RequestBody GroupVo06 groupVo) {
+ Long groupId = groupVo.getGroupId();
+ chatGroupService.editKeepGroup(groupId, groupVo.getKeepGroup());
+ return AjaxResult.success();
+ }
+
+ /**
+ * 退出群组
+ */
+ @ApiVersion(VersionEnum.V1_0_0)
+ @GetMapping("/logoutGroup/{groupId}")
+ public AjaxResult logoutGroup(@PathVariable Long groupId) {
+ chatGroupService.logoutGroup(groupId);
+ return AjaxResult.success();
+ }
+
+ /**
+ * 解散群组
+ */
+ @ApiVersion(VersionEnum.V1_0_0)
+ @GetMapping("/removeGroup/{groupId}")
+ public AjaxResult removeGroup(@PathVariable Long groupId) {
+ chatGroupService.removeGroup(groupId);
+ return AjaxResult.success();
+ }
+
+ /**
+ * 扫码查询
+ */
+ @ApiVersion(VersionEnum.V1_0_0)
+ @GetMapping("/scanCode/{param}")
+ public AjaxResult scanCode(@PathVariable String param) {
+ return AjaxResult.success(chatGroupService.scanCode(param));
+ }
+
+ /**
+ * 加入群组
+ */
+ @ApiVersion(VersionEnum.V1_0_0)
+ @GetMapping("/joinGroup/{groupId}")
+ public AjaxResult joinGroup(@PathVariable Long groupId) {
+ chatGroupService.joinGroup(groupId);
+ return AjaxResult.success();
+ }
+
+ /**
+ * 查询群列表
+ */
+ @ApiVersion(VersionEnum.V1_0_0)
+ @GetMapping("/groupList")
+ public AjaxResult groupList() {
+ return AjaxResult.success(chatGroupService.groupList());
+ }
+
+ /**
+ * 发送信息
+ */
+ @ApiVersion(VersionEnum.V1_0_0)
+ @PostMapping("/sendMsg")
+ public AjaxResult sendMsg(@Validated @RequestBody ChatVo02 chatVo) {
+ return AjaxResult.success(chatMsgService.sendGroupMsg(chatVo));
+ }
+
+}
diff --git a/src/main/java/com/platform/modules/chat/controller/MyController.java b/src/main/java/com/platform/modules/chat/controller/MyController.java
new file mode 100644
index 0000000..765a557
--- /dev/null
+++ b/src/main/java/com/platform/modules/chat/controller/MyController.java
@@ -0,0 +1,183 @@
+package com.platform.modules.chat.controller;
+
+import com.platform.common.shiro.ShiroUtils;
+import com.platform.common.version.ApiVersion;
+import com.platform.common.version.VersionEnum;
+import com.platform.common.web.controller.BaseController;
+import com.platform.common.web.domain.AjaxResult;
+import com.platform.modules.chat.domain.ChatUser;
+import com.platform.modules.chat.service.ChatFeedbackService;
+import com.platform.modules.chat.service.ChatUserService;
+import com.platform.modules.chat.vo.*;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+
+/**
+ * 我的
+ */
+@RestController
+@Slf4j
+@RequestMapping("/my")
+public class MyController extends BaseController {
+
+ @Resource
+ private ChatUserService chatUserService;
+
+ @Resource
+ private ChatFeedbackService feedbackService;
+
+ /**
+ * 修改密码
+ */
+ @ApiVersion(VersionEnum.V1_0_0)
+ @PostMapping("/editPass")
+ public AjaxResult editPass(@Validated @RequestBody MyVo01 myVo) {
+ // 执行重置
+ chatUserService.editPass(myVo.getPassword(), myVo.getPwd());
+ return AjaxResult.successMsg("修改成功");
+ }
+
+ /**
+ * 退出系统
+ */
+ @ApiVersion(VersionEnum.V1_0_0)
+ @GetMapping("/logout")
+ public AjaxResult logout() {
+ chatUserService.logout();
+ return AjaxResult.success();
+ }
+
+ /**
+ * 获取二维码
+ */
+ @ApiVersion(VersionEnum.V1_0_0)
+ @GetMapping("/getQrCode")
+ public AjaxResult getQrCode() {
+ return AjaxResult.success(chatUserService.getQrCode());
+ }
+
+ /**
+ * 获取基本信息
+ */
+ @ApiVersion(VersionEnum.V1_0_0)
+ @GetMapping("/getInfo")
+ public AjaxResult getInfo() {
+ return AjaxResult.success(chatUserService.getInfo());
+ }
+
+ /**
+ * 修改头像
+ */
+ @ApiVersion(VersionEnum.V1_0_0)
+ @PostMapping("/editPortrait")
+ public AjaxResult editPortrait(@Validated @RequestBody MyVo02 myVo) {
+ // 执行修改
+ ChatUser chatUser = new ChatUser()
+ .setUserId(ShiroUtils.getUserId())
+ .setPortrait(myVo.getPortrait());
+ chatUserService.updateById(chatUser);
+ return AjaxResult.successMsg("修改成功");
+ }
+
+ /**
+ * 修改昵称
+ */
+ @ApiVersion(VersionEnum.V1_0_0)
+ @PostMapping("/editNick")
+ public AjaxResult editNick(@Validated @RequestBody MyVo03 myVo) {
+ ChatUser chatUser = new ChatUser()
+ .setUserId(ShiroUtils.getUserId())
+ .setNickName(myVo.getNickName());
+ chatUserService.updateById(chatUser);
+ return AjaxResult.successMsg("修改成功");
+ }
+
+ /**
+ * 修改性别
+ */
+ @ApiVersion(VersionEnum.V1_0_0)
+ @PostMapping("/editGender")
+ public AjaxResult editGender(@Validated @RequestBody MyVo05 myVo) {
+ // 执行修改
+ ChatUser chatUser = new ChatUser()
+ .setUserId(ShiroUtils.getUserId())
+ .setGender(myVo.getGender());
+ chatUserService.updateById(chatUser);
+ return AjaxResult.successMsg("修改成功");
+ }
+
+ /**
+ * 修改微聊号
+ */
+ @ApiVersion(VersionEnum.V1_0_0)
+ @PostMapping("/editChatNo")
+ public AjaxResult editChatNo(@Validated @RequestBody MyVo06 myVo) {
+ // 执行修改
+ chatUserService.editChatNo(myVo.getChatNo());
+ return AjaxResult.successMsg("修改成功");
+ }
+
+ /**
+ * 修改个性签名
+ */
+ @ApiVersion(VersionEnum.V1_0_0)
+ @PostMapping("/editIntro")
+ public AjaxResult editIntro(@Validated @RequestBody MyVo07 myVo) {
+ // 执行修改
+ ChatUser chatUser = new ChatUser()
+ .setUserId(ShiroUtils.getUserId())
+ .setIntro(myVo.getIntro());
+ chatUserService.updateById(chatUser);
+ return AjaxResult.successMsg("修改成功");
+ }
+
+ /**
+ * 修改省市
+ */
+ @ApiVersion(VersionEnum.V1_0_0)
+ @PostMapping("/editCity")
+ public AjaxResult editCity(@Validated @RequestBody MyVo08 myVo) {
+ // 执行修改
+ ChatUser chatUser = new ChatUser()
+ .setUserId(ShiroUtils.getUserId())
+ .setProvinces(myVo.getProvinces())
+ .setCity(myVo.getCity());
+ chatUserService.updateById(chatUser);
+ return AjaxResult.successMsg("修改成功");
+ }
+
+ /**
+ * 用户注销
+ */
+ @ApiVersion(VersionEnum.V1_0_0)
+ @GetMapping("/deleted")
+ public AjaxResult deleted() {
+ chatUserService.deleted();
+ return AjaxResult.successMsg("注销成功");
+ }
+
+ /**
+ * 建议反馈
+ */
+ @ApiVersion(VersionEnum.V1_0_0)
+ @PostMapping("/feedback")
+ public AjaxResult feedback(@Validated @RequestBody MyVo04 myVo) {
+ feedbackService.addFeedback(myVo);
+ return AjaxResult.success();
+ }
+
+ /**
+ * 刷新
+ */
+ @ApiVersion(VersionEnum.V1_0_0)
+ @GetMapping("/refresh")
+ public AjaxResult refresh() {
+ chatUserService.refresh();
+ return AjaxResult.success();
+ }
+
+
+}
diff --git a/src/main/java/com/platform/modules/chat/dao/ChatApplyDao.java b/src/main/java/com/platform/modules/chat/dao/ChatApplyDao.java
new file mode 100644
index 0000000..3216eb8
--- /dev/null
+++ b/src/main/java/com/platform/modules/chat/dao/ChatApplyDao.java
@@ -0,0 +1,23 @@
+package com.platform.modules.chat.dao;
+
+import com.platform.modules.chat.domain.ChatApply;
+import org.springframework.stereotype.Repository;
+import com.platform.common.web.dao.BaseDao;
+
+import java.util.List;
+
+/**
+ *
+ * 好友申请表 数据库访问层
+ * q3z3
+ *
+ */
+@Repository
+public interface ChatApplyDao extends BaseDao {
+
+ /**
+ * 查询列表
+ */
+ List queryList(ChatApply chatApply);
+
+}
diff --git a/src/main/java/com/platform/modules/chat/dao/ChatFeedbackDao.java b/src/main/java/com/platform/modules/chat/dao/ChatFeedbackDao.java
new file mode 100644
index 0000000..e3d7794
--- /dev/null
+++ b/src/main/java/com/platform/modules/chat/dao/ChatFeedbackDao.java
@@ -0,0 +1,23 @@
+package com.platform.modules.chat.dao;
+
+import com.platform.modules.chat.domain.ChatFeedback;
+import org.springframework.stereotype.Repository;
+import com.platform.common.web.dao.BaseDao;
+
+import java.util.List;
+
+/**
+ *
+ * 建议反馈 数据库访问层
+ * q3z3
+ *
+ */
+@Repository
+public interface ChatFeedbackDao extends BaseDao {
+
+ /**
+ * 查询列表
+ */
+ List queryList(ChatFeedback chatFeedback);
+
+}
diff --git a/src/main/java/com/platform/modules/chat/dao/ChatFriendDao.java b/src/main/java/com/platform/modules/chat/dao/ChatFriendDao.java
new file mode 100644
index 0000000..dff5a4a
--- /dev/null
+++ b/src/main/java/com/platform/modules/chat/dao/ChatFriendDao.java
@@ -0,0 +1,34 @@
+package com.platform.modules.chat.dao;
+
+import com.platform.common.web.dao.BaseDao;
+import com.platform.modules.chat.domain.ChatFriend;
+import com.platform.modules.chat.vo.FriendVo06;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+/**
+ *
+ * 好友表 数据库访问层
+ * q3z3
+ *
+ */
+@Repository
+public interface ChatFriendDao extends BaseDao {
+
+ /**
+ * 查询列表
+ */
+ List queryList(ChatFriend chatFriend);
+
+ /**
+ * 查询好友列表
+ */
+ List friendList(Long userId);
+
+ /**
+ * 查询好友id
+ */
+ List queryFriendId(Long userId);
+
+}
diff --git a/src/main/java/com/platform/modules/chat/dao/ChatGroupDao.java b/src/main/java/com/platform/modules/chat/dao/ChatGroupDao.java
new file mode 100644
index 0000000..a77b4fa
--- /dev/null
+++ b/src/main/java/com/platform/modules/chat/dao/ChatGroupDao.java
@@ -0,0 +1,30 @@
+package com.platform.modules.chat.dao;
+
+import com.platform.common.web.dao.BaseDao;
+import com.platform.modules.chat.domain.ChatGroup;
+import com.platform.modules.push.vo.PushParamVo;
+import org.apache.ibatis.annotations.Param;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+/**
+ *
+ * 群组 数据库访问层
+ * q3z3
+ *
+ */
+@Repository
+public interface ChatGroupDao extends BaseDao {
+
+ /**
+ * 查询列表
+ */
+ List queryList(ChatGroup chatGroup);
+
+ /**
+ * 查询用户
+ */
+ List queryFriendPushFrom(@Param("groupId") Long groupId, @Param("userId") Long userId);
+
+}
diff --git a/src/main/java/com/platform/modules/chat/dao/ChatGroupInfoDao.java b/src/main/java/com/platform/modules/chat/dao/ChatGroupInfoDao.java
new file mode 100644
index 0000000..9a22615
--- /dev/null
+++ b/src/main/java/com/platform/modules/chat/dao/ChatGroupInfoDao.java
@@ -0,0 +1,23 @@
+package com.platform.modules.chat.dao;
+
+import com.platform.common.web.dao.BaseDao;
+import com.platform.modules.chat.domain.ChatGroupInfo;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+/**
+ *
+ * 数据库访问层
+ * q3z3
+ *
+ */
+@Repository
+public interface ChatGroupInfoDao extends BaseDao {
+
+ /**
+ * 查询列表
+ */
+ List queryList(ChatGroupInfo chatGroupInfo);
+
+}
diff --git a/src/main/java/com/platform/modules/chat/dao/ChatMsgDao.java b/src/main/java/com/platform/modules/chat/dao/ChatMsgDao.java
new file mode 100644
index 0000000..73e4944
--- /dev/null
+++ b/src/main/java/com/platform/modules/chat/dao/ChatMsgDao.java
@@ -0,0 +1,23 @@
+package com.platform.modules.chat.dao;
+
+import com.platform.modules.chat.domain.ChatMsg;
+import org.springframework.stereotype.Repository;
+import com.platform.common.web.dao.BaseDao;
+
+import java.util.List;
+
+/**
+ *
+ * 聊天消息 数据库访问层
+ * q3z3
+ *
+ */
+@Repository
+public interface ChatMsgDao extends BaseDao {
+
+ /**
+ * 查询列表
+ */
+ List queryList(ChatMsg chatMsg);
+
+}
diff --git a/src/main/java/com/platform/modules/chat/dao/ChatUserDao.java b/src/main/java/com/platform/modules/chat/dao/ChatUserDao.java
new file mode 100644
index 0000000..da37151
--- /dev/null
+++ b/src/main/java/com/platform/modules/chat/dao/ChatUserDao.java
@@ -0,0 +1,23 @@
+package com.platform.modules.chat.dao;
+
+import com.platform.common.web.dao.BaseDao;
+import com.platform.modules.chat.domain.ChatUser;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+/**
+ *
+ * 用户表 数据库访问层
+ * q3z3
+ *
+ */
+@Repository
+public interface ChatUserDao extends BaseDao {
+
+ /**
+ * 查询列表
+ */
+ List queryList(ChatUser chatUser);
+
+}
diff --git a/src/main/java/com/platform/modules/chat/dao/ChatVersionDao.java b/src/main/java/com/platform/modules/chat/dao/ChatVersionDao.java
new file mode 100644
index 0000000..d430a49
--- /dev/null
+++ b/src/main/java/com/platform/modules/chat/dao/ChatVersionDao.java
@@ -0,0 +1,23 @@
+package com.platform.modules.chat.dao;
+
+import com.platform.modules.chat.domain.ChatVersion;
+import org.springframework.stereotype.Repository;
+import com.platform.common.web.dao.BaseDao;
+
+import java.util.List;
+
+/**
+ *
+ * 版本 数据库访问层
+ * q3z3
+ *
+ */
+@Repository
+public interface ChatVersionDao extends BaseDao {
+
+ /**
+ * 查询列表
+ */
+ List queryList(ChatVersion chatVersion);
+
+}
diff --git a/src/main/java/com/platform/modules/chat/domain/ChatApply.java b/src/main/java/com/platform/modules/chat/domain/ChatApply.java
new file mode 100644
index 0000000..4074db1
--- /dev/null
+++ b/src/main/java/com/platform/modules/chat/domain/ChatApply.java
@@ -0,0 +1,65 @@
+package com.platform.modules.chat.domain;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.platform.common.web.domain.BaseEntity;
+import com.platform.modules.chat.enums.ApplySourceEnum;
+import com.platform.modules.chat.enums.ApplyStatusEnum;
+import com.platform.modules.chat.enums.ApplyTypeEnum;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.util.Date;
+
+/**
+ *
+ * 好友申请表实体类
+ * q3z3
+ *
+ */
+@Data
+@TableName("chat_apply")
+@Accessors(chain = true) // 链式调用
+public class ChatApply extends BaseEntity {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 主键
+ */
+ @TableId
+ private Long id;
+ /**
+ * 发起id
+ */
+ private Long fromId;
+ /**
+ * 接收id
+ */
+ private Long toId;
+ /**
+ * 目标id
+ */
+ private Long targetId;
+ /**
+ * 申请类型1好友2群组
+ */
+ private ApplyTypeEnum applyType;
+ /**
+ * 申请状态0无1同意2拒绝
+ */
+ private ApplyStatusEnum applyStatus;
+ /**
+ * 申请来源
+ */
+ private ApplySourceEnum applySource;
+ /**
+ * 理由
+ */
+ private String reason;
+ /**
+ * 申请时间
+ */
+ private Date createTime;
+
+}
diff --git a/src/main/java/com/platform/modules/chat/domain/ChatFeedback.java b/src/main/java/com/platform/modules/chat/domain/ChatFeedback.java
new file mode 100644
index 0000000..1233532
--- /dev/null
+++ b/src/main/java/com/platform/modules/chat/domain/ChatFeedback.java
@@ -0,0 +1,50 @@
+package com.platform.modules.chat.domain;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.platform.common.web.domain.BaseEntity;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.util.Date;
+
+/**
+ *
+ * 建议反馈实体类
+ * q3z3
+ *
+ */
+@Data
+@TableName("chat_feedback")
+@Accessors(chain = true) // 链式调用
+public class ChatFeedback extends BaseEntity {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 主键
+ */
+ @TableId
+ private Long id;
+ /**
+ * 用户id
+ */
+ private Long userId;
+ /**
+ * 图片
+ */
+ private String images;
+ /**
+ * 内容
+ */
+ private String content;
+ /**
+ * 版本
+ */
+ private String version;
+ /**
+ * 创建时间
+ */
+ private Date createTime;
+
+}
diff --git a/src/main/java/com/platform/modules/chat/domain/ChatFriend.java b/src/main/java/com/platform/modules/chat/domain/ChatFriend.java
new file mode 100644
index 0000000..5db6eaf
--- /dev/null
+++ b/src/main/java/com/platform/modules/chat/domain/ChatFriend.java
@@ -0,0 +1,63 @@
+package com.platform.modules.chat.domain;
+
+import com.baomidou.mybatisplus.annotation.FieldStrategy;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.platform.common.enums.YesOrNoEnum;
+import com.platform.common.web.domain.BaseEntity;
+import com.platform.modules.chat.enums.ApplySourceEnum;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.util.Date;
+
+/**
+ *
+ * 好友表实体类
+ * q3z3
+ *
+ */
+@Data
+@TableName("chat_friend")
+@Accessors(chain = true) // 链式调用
+public class ChatFriend extends BaseEntity {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 主键
+ */
+ @TableId
+ private Long id;
+ /**
+ * 用户id
+ */
+ private Long fromId;
+ /**
+ * 好友id
+ */
+ private Long toId;
+ /**
+ * 备注
+ */
+ private String remark;
+ /**
+ * 是否黑名单
+ */
+ private YesOrNoEnum black;
+ /**
+ * 是否置顶
+ */
+ private YesOrNoEnum top;
+ /**
+ * 申请来源
+ */
+ private ApplySourceEnum source;
+ /**
+ * 创建时间
+ */
+ @TableField(updateStrategy = FieldStrategy.NEVER)
+ private Date createTime;
+
+}
diff --git a/src/main/java/com/platform/modules/chat/domain/ChatGroup.java b/src/main/java/com/platform/modules/chat/domain/ChatGroup.java
new file mode 100644
index 0000000..18c7b7b
--- /dev/null
+++ b/src/main/java/com/platform/modules/chat/domain/ChatGroup.java
@@ -0,0 +1,50 @@
+package com.platform.modules.chat.domain;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.platform.common.web.domain.BaseEntity;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.util.Date;
+
+/**
+ *
+ * 群组实体类
+ * q3z3
+ *
+ */
+@Data
+@TableName("chat_group")
+@Accessors(chain = true) // 链式调用
+public class ChatGroup extends BaseEntity {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 主键
+ */
+ @TableId
+ private Long id;
+ /**
+ * 群名
+ */
+ private String name;
+ /**
+ * 公告
+ */
+ private String notice;
+ /**
+ * 头像
+ */
+ private String portrait;
+ /**
+ * 群主
+ */
+ private Long master;
+ /**
+ * 创建时间
+ */
+ private Date createTime;
+
+}
diff --git a/src/main/java/com/platform/modules/chat/domain/ChatGroupInfo.java b/src/main/java/com/platform/modules/chat/domain/ChatGroupInfo.java
new file mode 100644
index 0000000..1835e9c
--- /dev/null
+++ b/src/main/java/com/platform/modules/chat/domain/ChatGroupInfo.java
@@ -0,0 +1,66 @@
+package com.platform.modules.chat.domain;
+
+import cn.hutool.core.date.DateUtil;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.platform.common.enums.YesOrNoEnum;
+import com.platform.common.web.domain.BaseEntity;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+
+import java.util.Date;
+
+/**
+ *
+ * 实体类
+ * q3z3
+ *
+ */
+@Data
+@TableName("chat_group_info")
+@Accessors(chain = true) // 链式调用
+@NoArgsConstructor
+public class ChatGroupInfo extends BaseEntity {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 主键
+ */
+ @TableId
+ private Long infoId;
+ /**
+ * 用户id
+ */
+ private Long userId;
+ /**
+ * 群组id
+ */
+ private Long groupId;
+ /**
+ * 是否置顶
+ */
+ private YesOrNoEnum top;
+ /**
+ * 是否免打扰
+ */
+ private YesOrNoEnum disturb;
+ /**
+ * 是否保存群组
+ */
+ private YesOrNoEnum keepGroup;
+ /**
+ * 加入时间
+ */
+ private Date createTime;
+
+ public ChatGroupInfo(Long userId, Long groupId) {
+ this.userId = userId;
+ this.groupId = groupId;
+ this.createTime = DateUtil.date();
+ this.top = YesOrNoEnum.NO;
+ this.disturb = YesOrNoEnum.NO;
+ this.keepGroup = YesOrNoEnum.NO;
+ }
+}
diff --git a/src/main/java/com/platform/modules/chat/domain/ChatMsg.java b/src/main/java/com/platform/modules/chat/domain/ChatMsg.java
new file mode 100644
index 0000000..c8c7ba3
--- /dev/null
+++ b/src/main/java/com/platform/modules/chat/domain/ChatMsg.java
@@ -0,0 +1,59 @@
+package com.platform.modules.chat.domain;
+
+import com.baomidou.mybatisplus.annotation.FieldStrategy;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.platform.common.web.domain.BaseEntity;
+import com.platform.modules.push.enums.PushMsgEnum;
+import com.platform.modules.push.enums.PushTalkEnum;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.util.Date;
+
+/**
+ *
+ * 聊天消息实体类
+ * q3z3
+ *
+ */
+@Data
+@TableName("chat_msg")
+@Accessors(chain = true) // 链式调用
+public class ChatMsg extends BaseEntity {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 消息主键
+ */
+ @TableId
+ private Long id;
+ /**
+ * 发送人
+ */
+ private Long fromId;
+ /**
+ * 接收人
+ */
+ private Long toId;
+ /**
+ * 消息类型
+ */
+ private PushMsgEnum msgType;
+ /**
+ * 消息类型
+ */
+ private PushTalkEnum talkType;
+ /**
+ * 消息内容
+ */
+ private String content;
+ /**
+ * 创建时间
+ */
+ @TableField(updateStrategy = FieldStrategy.NEVER)
+ private Date createTime;
+
+}
diff --git a/src/main/java/com/platform/modules/chat/domain/ChatUser.java b/src/main/java/com/platform/modules/chat/domain/ChatUser.java
new file mode 100644
index 0000000..b3595d7
--- /dev/null
+++ b/src/main/java/com/platform/modules/chat/domain/ChatUser.java
@@ -0,0 +1,123 @@
+package com.platform.modules.chat.domain;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.platform.common.constant.AppConstants;
+import com.platform.common.enums.GenderEnum;
+import com.platform.common.enums.YesOrNoEnum;
+import com.platform.common.web.domain.BaseEntity;
+import com.platform.modules.push.vo.PushParamVo;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.util.Date;
+
+/**
+ *
+ * 用户表实体类
+ * q3z3
+ *
+ */
+@Data
+@TableName("chat_user")
+@Accessors(chain = true)
+public class ChatUser extends BaseEntity {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 主键
+ */
+ @TableId
+ private Long userId;
+ /**
+ * 昵称
+ */
+ private String nickName;
+ /**
+ * 介绍
+ */
+ private String intro;
+ /**
+ * 性别1男0女
+ */
+ private GenderEnum gender;
+ /**
+ * 头像
+ */
+ private String portrait;
+ /**
+ * 封面
+ */
+ private String cover;
+ /**
+ * 手机号
+ */
+ private String phone;
+ /**
+ * 省份
+ */
+ private String provinces;
+ /**
+ * 城市
+ */
+ private String city;
+ /**
+ * 微聊号
+ */
+ private String chatNo;
+ /**
+ * 密码
+ */
+ private String password;
+ /**
+ * 盐
+ */
+ private String salt;
+ /**
+ * 用户token
+ */
+ private String token;
+ /**
+ * 版本信息
+ */
+ private String version;
+ /**
+ * 注册时间
+ */
+ @TableField(updateStrategy = FieldStrategy.NEVER)
+ private Date createTime;
+ /**
+ * 注销0正常null注销
+ */
+ @TableLogic
+ private Integer deleted;
+ /**
+ * 注销时间
+ */
+ private Date deletedTime;
+
+ /**
+ * 格式化,防止出错
+ */
+ public static ChatUser initUser(ChatUser user) {
+ if (user != null) {
+ return user;
+ }
+ return new ChatUser()
+ .setGender(GenderEnum.MALE)
+ .setPortrait(AppConstants.DEFAULT_PORTRAIT)
+ .setNickName(AppConstants.DELETED_NICK_NAME);
+ }
+
+ /**
+ * 格式化,防止出错
+ */
+ public static PushParamVo initParam(ChatUser user) {
+ user = initUser(user);
+ return new PushParamVo()
+ .setUserId(user.getUserId())
+ .setPortrait(user.getPortrait())
+ .setNickName(user.getNickName());
+ }
+
+}
diff --git a/src/main/java/com/platform/modules/chat/domain/ChatVersion.java b/src/main/java/com/platform/modules/chat/domain/ChatVersion.java
new file mode 100644
index 0000000..97821f4
--- /dev/null
+++ b/src/main/java/com/platform/modules/chat/domain/ChatVersion.java
@@ -0,0 +1,38 @@
+package com.platform.modules.chat.domain;
+
+import com.platform.common.web.domain.BaseEntity;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+/**
+ *
+ * 版本实体类
+ * q3z3
+ *
+ */
+@Data
+@TableName("chat_version")
+public class ChatVersion extends BaseEntity {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 主键
+ */
+ @TableId
+ private Long id;
+ /**
+ * 版本
+ */
+ private String version;
+ /**
+ * 地址
+ */
+ private String url;
+ /**
+ * 内容
+ */
+ private String content;
+
+}
diff --git a/src/main/java/com/platform/modules/chat/enums/ApplySourceEnum.java b/src/main/java/com/platform/modules/chat/enums/ApplySourceEnum.java
new file mode 100644
index 0000000..18b0286
--- /dev/null
+++ b/src/main/java/com/platform/modules/chat/enums/ApplySourceEnum.java
@@ -0,0 +1,57 @@
+package com.platform.modules.chat.enums;
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+import com.fasterxml.jackson.annotation.JsonValue;
+import lombok.Getter;
+
+/**
+ * 好友来源
+ */
+@Getter
+public enum ApplySourceEnum {
+
+ /**
+ * 扫一扫
+ */
+ SCAN("1", "扫一扫"),
+ /**
+ * 名片
+ */
+ CARD("2", "名片"),
+ /**
+ * 微聊号
+ */
+ CHAT_NO("3", "微聊号"),
+ /**
+ * 手机号
+ */
+ PHONE("4", "手机号"),
+ /**
+ * 摇一摇
+ */
+ SHAKE("5", "摇一摇"),
+ /**
+ * 系统
+ */
+ SYS("6", "系统"),
+ /**
+ * 群聊
+ */
+ GROUP("7", "群聊"),
+ /**
+ * 附近的人
+ */
+ NEAR("8", "附近的人"),
+ ;
+
+ @EnumValue
+ @JsonValue
+ private String code;
+ private String info;
+
+ ApplySourceEnum(String code, String info) {
+ this.code = code;
+ this.info = info;
+ }
+
+}
diff --git a/src/main/java/com/platform/modules/chat/enums/ApplyStatusEnum.java b/src/main/java/com/platform/modules/chat/enums/ApplyStatusEnum.java
new file mode 100644
index 0000000..2e9e7d1
--- /dev/null
+++ b/src/main/java/com/platform/modules/chat/enums/ApplyStatusEnum.java
@@ -0,0 +1,41 @@
+package com.platform.modules.chat.enums;
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+import com.fasterxml.jackson.annotation.JsonValue;
+import lombok.Getter;
+
+/**
+ * 申请状态
+ */
+@Getter
+public enum ApplyStatusEnum {
+
+ /**
+ * 无
+ */
+ NONE("0", "无"),
+ /**
+ * 同意
+ */
+ AGREE("1", "同意"),
+ /**
+ * 拒绝
+ */
+ REFUSED("2", "拒绝"),
+ /**
+ * 忽略
+ */
+ IGNORE("3", "忽略"),
+ ;
+
+ @EnumValue
+ @JsonValue
+ private String code;
+ private String info;
+
+ ApplyStatusEnum(String code, String info) {
+ this.code = code;
+ this.info = info;
+ }
+
+}
diff --git a/src/main/java/com/platform/modules/chat/enums/ApplyTypeEnum.java b/src/main/java/com/platform/modules/chat/enums/ApplyTypeEnum.java
new file mode 100644
index 0000000..d624872
--- /dev/null
+++ b/src/main/java/com/platform/modules/chat/enums/ApplyTypeEnum.java
@@ -0,0 +1,33 @@
+package com.platform.modules.chat.enums;
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+import com.fasterxml.jackson.annotation.JsonValue;
+import lombok.Getter;
+
+/**
+ * 申请类型
+ */
+@Getter
+public enum ApplyTypeEnum {
+
+ /**
+ * 好友
+ */
+ FRIEND("1", "好友"),
+ /**
+ * 群组
+ */
+ GROUP("2", "群组"),
+ ;
+
+ @EnumValue
+ @JsonValue
+ private String code;
+ private String info;
+
+ ApplyTypeEnum(String code, String info) {
+ this.code = code;
+ this.info = info;
+ }
+
+}
diff --git a/src/main/java/com/platform/modules/chat/enums/FriendTypeEnum.java b/src/main/java/com/platform/modules/chat/enums/FriendTypeEnum.java
new file mode 100644
index 0000000..d5ba978
--- /dev/null
+++ b/src/main/java/com/platform/modules/chat/enums/FriendTypeEnum.java
@@ -0,0 +1,41 @@
+package com.platform.modules.chat.enums;
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+import com.fasterxml.jackson.annotation.JsonValue;
+import lombok.Getter;
+
+/**
+ * 好友类型
+ */
+@Getter
+public enum FriendTypeEnum {
+
+ /**
+ * 正常
+ */
+ NORMAL("normal", "正常"),
+ /**
+ * 天气机器人
+ */
+ WEATHER("weather", "天气机器人"),
+ /**
+ * 翻译机器人
+ */
+ TRANSLATION("translation", "翻译机器人"),
+ /**
+ * 自己
+ */
+ SELF("self", "自己"),
+ ;
+
+ @EnumValue
+ @JsonValue
+ private String code;
+ private String info;
+
+ FriendTypeEnum(String code, String info) {
+ this.code = code;
+ this.info = info;
+ }
+
+}
diff --git a/src/main/java/com/platform/modules/chat/enums/MsgStatusEnum.java b/src/main/java/com/platform/modules/chat/enums/MsgStatusEnum.java
new file mode 100644
index 0000000..3395079
--- /dev/null
+++ b/src/main/java/com/platform/modules/chat/enums/MsgStatusEnum.java
@@ -0,0 +1,53 @@
+package com.platform.modules.chat.enums;
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+import com.fasterxml.jackson.annotation.JsonValue;
+import lombok.Getter;
+
+/**
+ * 消息状态
+ */
+@Getter
+public enum MsgStatusEnum {
+
+ /**
+ * 正常
+ */
+ NORMAL("0", "正常"),
+ /**
+ * 对方不是自己朋友
+ */
+ FRIEND_TO("1", "对方不是你的好友,消息发送失败"),
+ /**
+ * 自己不是对方朋友
+ */
+ FRIEND_FROM("2", "你不是对方的好友,消息发送失败"),
+ /**
+ * 黑名单
+ */
+ FRIEND_BLACK("3", "消息已发出,但被对方拒收了"),
+ /**
+ * 注销
+ */
+ FRIEND_DELETED("4", "对方已注销,消息发送失败"),
+ /**
+ * 群不存在
+ */
+ GROUP_NOT_EXIST("5", "当前群不存在,消息发送失败"),
+ /**
+ * 群明细不存在
+ */
+ GROUP_INFO_NOT_EXIST("6", "你不在当前群中,消息发送失败"),
+ ;
+
+ @EnumValue
+ @JsonValue
+ private String code;
+ private String info;
+
+ MsgStatusEnum(String code, String info) {
+ this.code = code;
+ this.info = info;
+ }
+
+}
diff --git a/src/main/java/com/platform/modules/chat/enums/VersionTypeEnum.java b/src/main/java/com/platform/modules/chat/enums/VersionTypeEnum.java
new file mode 100644
index 0000000..120cc26
--- /dev/null
+++ b/src/main/java/com/platform/modules/chat/enums/VersionTypeEnum.java
@@ -0,0 +1,33 @@
+package com.platform.modules.chat.enums;
+
+import lombok.Getter;
+
+/**
+ * 版本类型枚举
+ */
+@Getter
+public enum VersionTypeEnum {
+
+ /**
+ * 用户协议
+ */
+ AGREEMENT(1L, "agreement"),
+ /**
+ * 安卓
+ */
+ ANDROID(2L, "android"),
+ /**
+ * iOS
+ */
+ IOS(3L, "iOS"),
+ ;
+
+ private Long code;
+ private String name;
+
+ VersionTypeEnum(Long code, String name) {
+ this.code = code;
+ this.name = name;
+ }
+
+}
diff --git a/src/main/java/com/platform/modules/chat/service/ChatApplyService.java b/src/main/java/com/platform/modules/chat/service/ChatApplyService.java
new file mode 100644
index 0000000..0ba4fff
--- /dev/null
+++ b/src/main/java/com/platform/modules/chat/service/ChatApplyService.java
@@ -0,0 +1,31 @@
+package com.platform.modules.chat.service;
+
+import com.github.pagehelper.PageInfo;
+import com.platform.common.web.service.BaseService;
+import com.platform.modules.chat.domain.ChatApply;
+import com.platform.modules.chat.enums.ApplySourceEnum;
+import com.platform.modules.chat.vo.ApplyVo03;
+
+/**
+ *
+ * 好友申请表 服务层
+ * q3z3
+ *
+ */
+public interface ChatApplyService extends BaseService {
+
+ /**
+ * 申请好友
+ */
+ void applyFriend(Long acceptId, ApplySourceEnum source, String reason);
+
+ /**
+ * 申请记录
+ */
+ PageInfo list();
+
+ /**
+ * 查询详情
+ */
+ ApplyVo03 getInfo(Long applyId);
+}
diff --git a/src/main/java/com/platform/modules/chat/service/ChatFeedbackService.java b/src/main/java/com/platform/modules/chat/service/ChatFeedbackService.java
new file mode 100644
index 0000000..9f8d907
--- /dev/null
+++ b/src/main/java/com/platform/modules/chat/service/ChatFeedbackService.java
@@ -0,0 +1,20 @@
+package com.platform.modules.chat.service;
+
+import com.platform.common.web.service.BaseService;
+import com.platform.modules.chat.domain.ChatFeedback;
+import com.platform.modules.chat.vo.MyVo04;
+
+/**
+ *
+ * 建议反馈 服务层
+ * q3z3
+ *
+ */
+public interface ChatFeedbackService extends BaseService {
+
+ /**
+ * 添加建议反馈
+ */
+ void addFeedback(MyVo04 myVo);
+
+}
diff --git a/src/main/java/com/platform/modules/chat/service/ChatFriendService.java b/src/main/java/com/platform/modules/chat/service/ChatFriendService.java
new file mode 100644
index 0000000..9f3fe58
--- /dev/null
+++ b/src/main/java/com/platform/modules/chat/service/ChatFriendService.java
@@ -0,0 +1,82 @@
+package com.platform.modules.chat.service;
+
+import com.platform.common.web.service.BaseService;
+import com.platform.modules.chat.domain.ChatFriend;
+import com.platform.modules.chat.vo.*;
+
+import java.util.List;
+
+/**
+ *
+ * 好友表 服务层
+ * q3z3
+ *
+ */
+public interface ChatFriendService extends BaseService {
+
+ /**
+ * 搜索好友
+ */
+ FriendVo07 findFriend(String param);
+
+ /**
+ * 添加好友
+ */
+ void applyFriend(FriendVo02 friendVo);
+
+ /**
+ * 同意申请
+ */
+ void agree(Long applyId);
+
+ /**
+ * 拒绝申请
+ */
+ void refused(Long applyId);
+
+ /**
+ * 忽略申请
+ */
+ void ignore(Long applyId);
+
+ /**
+ * 设置黑名单
+ */
+ void setBlack(FriendVo03 friendVo);
+
+ /**
+ * 删除好友
+ */
+ void delFriend(Long friendId);
+
+ /**
+ * 设置备注
+ */
+ void setRemark(FriendVo05 friendVo);
+
+ /**
+ * 是否置顶
+ */
+ void setTop(FriendVo09 friendVo);
+
+ /**
+ * 好友列表
+ */
+ List friendList(String param);
+
+ /**
+ * 好友详情
+ */
+ FriendVo07 getInfo(Long friendId);
+
+ /**
+ * 获取好友信息
+ */
+ ChatFriend getFriend(Long userId, Long friendId);
+
+ /**
+ * 查询好友id
+ */
+ List queryFriendId(Long userId);
+
+}
diff --git a/src/main/java/com/platform/modules/chat/service/ChatGroupInfoService.java b/src/main/java/com/platform/modules/chat/service/ChatGroupInfoService.java
new file mode 100644
index 0000000..c9ce5e5
--- /dev/null
+++ b/src/main/java/com/platform/modules/chat/service/ChatGroupInfoService.java
@@ -0,0 +1,53 @@
+package com.platform.modules.chat.service;
+
+import com.platform.common.enums.YesOrNoEnum;
+import com.platform.common.web.service.BaseService;
+import com.platform.modules.chat.domain.ChatGroupInfo;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ *
+ * 服务层
+ * q3z3
+ *
+ */
+public interface ChatGroupInfoService extends BaseService {
+
+ /**
+ * 查询详情
+ */
+ ChatGroupInfo getGroupInfo(Long groupId, Long userId, YesOrNoEnum verify);
+
+ /**
+ * 删除缓存
+ */
+ void delGroupInfoCache(Long groupId, List userList);
+
+ /**
+ * 查询数量
+ */
+ Long countByGroup(Long groupId);
+
+ /**
+ * 查询用户id
+ */
+ List queryUserList(Long groupId);
+
+ /**
+ * 查询用户id
+ */
+ List queryUserList(Long groupId, List userList);
+
+ /**
+ * 查询用户id
+ */
+ Map queryUserMap(Long groupId);
+
+ /**
+ * 通过群组删除
+ */
+ void delByGroup(Long groupId);
+
+}
diff --git a/src/main/java/com/platform/modules/chat/service/ChatGroupService.java b/src/main/java/com/platform/modules/chat/service/ChatGroupService.java
new file mode 100644
index 0000000..9d56656
--- /dev/null
+++ b/src/main/java/com/platform/modules/chat/service/ChatGroupService.java
@@ -0,0 +1,108 @@
+package com.platform.modules.chat.service;
+
+import cn.hutool.core.lang.Dict;
+import com.platform.common.enums.YesOrNoEnum;
+import com.platform.common.web.service.BaseService;
+import com.platform.modules.chat.domain.ChatGroup;
+import com.platform.modules.chat.domain.ChatMsg;
+import com.platform.modules.chat.vo.GroupVo02;
+import com.platform.modules.chat.vo.GroupVo03;
+import com.platform.modules.chat.vo.GroupVo07;
+import com.platform.modules.chat.vo.GroupVo08;
+import com.platform.modules.push.vo.PushParamVo;
+
+import java.util.List;
+
+/**
+ *
+ * 群组 服务层
+ * q3z3
+ *
+ */
+public interface ChatGroupService extends BaseService {
+
+ /**
+ * 建群
+ */
+ void createGroup(List list);
+
+ /**
+ * 群详情
+ */
+ Dict getInfo(Long groupId);
+
+ /**
+ * 邀请群组
+ */
+ void invitationGroup(Long groupId, List list);
+
+ /**
+ * 踢出群组
+ */
+ void kickedGroup(Long groupId, List list);
+
+ /**
+ * 获取群二维码
+ */
+ String getGroupQrCode(Long groupId);
+
+ /**
+ * 修改置顶
+ */
+ void editTop(Long groupId, YesOrNoEnum top);
+
+ /**
+ * 修改免打扰
+ */
+ void editDisturb(Long groupId, YesOrNoEnum disturb);
+
+ /**
+ * 修改保存群组
+ */
+ void editKeepGroup(Long groupId, YesOrNoEnum keepGroup);
+
+ /**
+ * 退出群组
+ */
+ void logoutGroup(Long groupId);
+
+ /**
+ * 解散群组
+ */
+ void removeGroup(Long groupId);
+
+ /**
+ * 扫码查询
+ */
+ GroupVo07 scanCode(String param);
+
+ /**
+ * 加入群组
+ */
+ void joinGroup(Long groupId);
+
+ /**
+ * 查询群列表
+ */
+ List groupList();
+
+ /**
+ * 查询用户id
+ */
+ List queryFriendPushFrom(ChatMsg chatMsg);
+
+ /**
+ * 查询用户id
+ */
+ List queryGroupPushFrom(Long groupId, List list, String content);
+
+ /**
+ * 修改群名
+ */
+ void editGroupName(GroupVo02 groupVo);
+
+ /**
+ * 修改群公告
+ */
+ void editGroupNotice(GroupVo03 groupVo);
+}
diff --git a/src/main/java/com/platform/modules/chat/service/ChatMsgService.java b/src/main/java/com/platform/modules/chat/service/ChatMsgService.java
new file mode 100644
index 0000000..a65ad8a
--- /dev/null
+++ b/src/main/java/com/platform/modules/chat/service/ChatMsgService.java
@@ -0,0 +1,27 @@
+package com.platform.modules.chat.service;
+
+import com.platform.common.web.service.BaseService;
+import com.platform.modules.chat.domain.ChatMsg;
+import com.platform.modules.chat.vo.ChatVo01;
+import com.platform.modules.chat.vo.ChatVo02;
+import com.platform.modules.chat.vo.ChatVo03;
+
+/**
+ *
+ * 聊天消息 服务层
+ * q3z3
+ *
+ */
+public interface ChatMsgService extends BaseService {
+
+ /**
+ * 发送消息
+ */
+ ChatVo03 sendFriendMsg(ChatVo01 chatVo);
+
+ /**
+ * 发送群消息
+ */
+ ChatVo03 sendGroupMsg(ChatVo02 chatVo);
+
+}
diff --git a/src/main/java/com/platform/modules/chat/service/ChatTalkService.java b/src/main/java/com/platform/modules/chat/service/ChatTalkService.java
new file mode 100644
index 0000000..8ada897
--- /dev/null
+++ b/src/main/java/com/platform/modules/chat/service/ChatTalkService.java
@@ -0,0 +1,32 @@
+package com.platform.modules.chat.service;
+
+import com.platform.modules.chat.vo.FriendVo06;
+import com.platform.modules.chat.vo.FriendVo07;
+import com.platform.modules.push.vo.PushParamVo;
+
+import java.util.List;
+
+/**
+ *
+ * 系统聊天 服务层
+ * q3z3
+ *
+ */
+public interface ChatTalkService {
+
+ /**
+ * 查询好友列表
+ */
+ List queryFriendList();
+
+ /**
+ * 查询好友详情
+ */
+ FriendVo07 queryFriendInfo(Long userId);
+
+ /**
+ * 发送聊天
+ */
+ PushParamVo talk(Long userId, String content);
+
+}
diff --git a/src/main/java/com/platform/modules/chat/service/ChatUserService.java b/src/main/java/com/platform/modules/chat/service/ChatUserService.java
new file mode 100644
index 0000000..fc0619a
--- /dev/null
+++ b/src/main/java/com/platform/modules/chat/service/ChatUserService.java
@@ -0,0 +1,73 @@
+package com.platform.modules.chat.service;
+
+import cn.hutool.core.lang.Dict;
+import com.platform.common.web.service.BaseService;
+import com.platform.modules.auth.vo.AuthVo01;
+import com.platform.modules.chat.domain.ChatUser;
+import com.platform.modules.chat.vo.MyVo09;
+import org.apache.shiro.authc.AuthenticationToken;
+
+/**
+ *
+ * 用户表 服务层
+ * q3z3
+ *
+ */
+public interface ChatUserService extends BaseService {
+
+ /**
+ * 通过手机+密码注册
+ */
+ void register(AuthVo01 authVo);
+
+ /**
+ * 通过手机号查询
+ */
+ ChatUser queryByPhone(String phone);
+
+ /**
+ * 重置密码
+ */
+ void resetPass(Long userId, String password);
+
+ /**
+ * 修改密码
+ */
+ void editPass(String password, String pwd);
+
+ /**
+ * 修改微聊号
+ */
+ void editChatNo(String chatNo);
+
+ /**
+ * 获取基本信息
+ */
+ MyVo09 getInfo();
+
+ /**
+ * 获取二维码
+ */
+ String getQrCode();
+
+ /**
+ * 用户注销
+ */
+ void deleted();
+
+ /**
+ * 执行登录/返回token
+ */
+ Dict doLogin(AuthenticationToken authenticationToken);
+
+ /**
+ * 退出登录
+ */
+ void logout();
+
+ /**
+ * 刷新
+ */
+ void refresh();
+
+}
diff --git a/src/main/java/com/platform/modules/chat/service/ChatVersionService.java b/src/main/java/com/platform/modules/chat/service/ChatVersionService.java
new file mode 100644
index 0000000..0f2cefd
--- /dev/null
+++ b/src/main/java/com/platform/modules/chat/service/ChatVersionService.java
@@ -0,0 +1,25 @@
+package com.platform.modules.chat.service;
+
+import com.platform.common.web.service.BaseService;
+import com.platform.modules.chat.domain.ChatVersion;
+import com.platform.modules.chat.vo.VersionVo;
+
+/**
+ *
+ * 版本 服务层
+ * q3z3
+ *
+ */
+public interface ChatVersionService extends BaseService {
+
+ /**
+ * 用户协议
+ */
+ String getAgreement();
+
+ /**
+ * 获取版本
+ */
+ VersionVo getVersion(String version, String device);
+
+}
diff --git a/src/main/java/com/platform/modules/chat/service/ChatWeatherService.java b/src/main/java/com/platform/modules/chat/service/ChatWeatherService.java
new file mode 100644
index 0000000..dba56d1
--- /dev/null
+++ b/src/main/java/com/platform/modules/chat/service/ChatWeatherService.java
@@ -0,0 +1,20 @@
+package com.platform.modules.chat.service;
+
+import cn.hutool.json.JSONObject;
+
+import java.util.List;
+
+/**
+ *
+ * 待办事项 服务层
+ * q3z3
+ *
+ */
+public interface ChatWeatherService {
+
+ /**
+ * 预报天气
+ */
+ List queryByCityName(String cityName);
+
+}
diff --git a/src/main/java/com/platform/modules/chat/service/impl/ChatApplyServiceImpl.java b/src/main/java/com/platform/modules/chat/service/impl/ChatApplyServiceImpl.java
new file mode 100644
index 0000000..dd00a25
--- /dev/null
+++ b/src/main/java/com/platform/modules/chat/service/impl/ChatApplyServiceImpl.java
@@ -0,0 +1,137 @@
+package com.platform.modules.chat.service.impl;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.date.DateUtil;
+import com.github.pagehelper.PageInfo;
+import com.platform.common.constant.AppConstants;
+import com.platform.common.exception.BaseException;
+import com.platform.common.redis.RedisUtils;
+import com.platform.common.shiro.ShiroUtils;
+import com.platform.common.web.service.impl.BaseServiceImpl;
+import com.platform.modules.chat.dao.ChatApplyDao;
+import com.platform.modules.chat.domain.ChatApply;
+import com.platform.modules.chat.domain.ChatUser;
+import com.platform.modules.chat.enums.ApplySourceEnum;
+import com.platform.modules.chat.enums.ApplyStatusEnum;
+import com.platform.modules.chat.enums.ApplyTypeEnum;
+import com.platform.modules.chat.service.ChatApplyService;
+import com.platform.modules.chat.service.ChatUserService;
+import com.platform.modules.chat.vo.ApplyVo02;
+import com.platform.modules.chat.vo.ApplyVo03;
+import com.platform.modules.push.enums.PushNoticeEnum;
+import com.platform.modules.push.service.ChatPushService;
+import com.platform.modules.push.vo.PushParamVo;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Service;
+import org.springframework.util.CollectionUtils;
+
+import javax.annotation.Resource;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ *
+ * 好友申请表 服务层实现
+ * q3z3
+ *
+ */
+@Service("chatApplyService")
+public class ChatApplyServiceImpl extends BaseServiceImpl implements ChatApplyService {
+
+ @Resource
+ private ChatApplyDao chatApplyDao;
+
+ @Lazy
+ @Resource
+ private ChatUserService chatUserService;
+
+ @Resource
+ private ChatPushService chatPushService;
+
+ @Autowired
+ private RedisUtils redisUtils;
+
+ @Autowired
+ public void setBaseDao() {
+ super.setBaseDao(chatApplyDao);
+ }
+
+ @Override
+ public List queryList(ChatApply t) {
+ List dataList = chatApplyDao.queryList(t);
+ return dataList;
+ }
+
+ @Override
+ public void applyFriend(Long acceptId, ApplySourceEnum source, String reason) {
+ Date now = DateUtil.date();
+ Long fromId = ShiroUtils.getUserId();
+ // 查询
+ ChatApply query = new ChatApply()
+ .setFromId(fromId)
+ .setToId(acceptId)
+ .setTargetId(acceptId)
+ .setApplyType(ApplyTypeEnum.FRIEND)
+ .setApplyStatus(ApplyStatusEnum.NONE);
+ ChatApply apply = this.queryOne(query);
+ query.setApplySource(source).setReason(reason).setCreateTime(now);
+ if (apply == null) {
+ this.add(query);
+ } else {
+ this.updateById(query.setId(apply.getId()));
+ }
+ // 给好友发送通知
+ PushParamVo paramVo = new PushParamVo().setToId(acceptId);
+ chatPushService.pushNotice(paramVo, PushNoticeEnum.FRIEND_APPLY);
+ }
+
+ @Override
+ public PageInfo list() {
+ Long userId = ShiroUtils.getUserId();
+ // 清空角标
+ redisUtils.delete(AppConstants.REDIS_FRIEND_NOTICE + userId);
+ // 查询
+ List dataList = queryList(new ChatApply().setToId(userId));
+ // 获取申请人
+ List fromList = dataList.stream().map(ChatApply::getFromId).collect(Collectors.toList());
+ // 集合判空
+ if (CollectionUtils.isEmpty(fromList)) {
+ return new PageInfo();
+ }
+ // 查询申请人
+ HashMap dataMap = chatUserService.getByIds(fromList).stream().collect(HashMap::new, (x, y) -> {
+ x.put(y.getUserId(), y);
+ }, HashMap::putAll);
+ // 转换
+ List dictList = new ArrayList<>();
+ for (ChatApply apply : dataList) {
+ ChatUser chatUser = ChatUser.initUser(dataMap.get(apply.getFromId()));
+ ApplyVo02 applyVo = BeanUtil.toBean(apply, ApplyVo02.class)
+ .setApplyId(apply.getId())
+ .setUserId(apply.getFromId())
+ .setPortrait(chatUser.getPortrait())
+ .setNickName(chatUser.getNickName());
+ dictList.add(applyVo);
+ }
+ return getPageInfo(dictList, dataList);
+ }
+
+ @Override
+ public ApplyVo03 getInfo(Long applyId) {
+ ChatApply apply = getById(applyId);
+ if (apply == null) {
+ throw new BaseException("申请已过期,请刷新后重试");
+ }
+ ChatUser chatUser = ChatUser.initUser(chatUserService.getById(apply.getFromId()));
+ return BeanUtil.toBean(chatUser, ApplyVo03.class)
+ .setApplyId(applyId)
+ .setApplySource(apply.getApplySource())
+ .setApplyStatus(apply.getApplyStatus())
+ .setReason(apply.getReason());
+ }
+
+}
diff --git a/src/main/java/com/platform/modules/chat/service/impl/ChatFeedbackServiceImpl.java b/src/main/java/com/platform/modules/chat/service/impl/ChatFeedbackServiceImpl.java
new file mode 100644
index 0000000..475b7bd
--- /dev/null
+++ b/src/main/java/com/platform/modules/chat/service/impl/ChatFeedbackServiceImpl.java
@@ -0,0 +1,51 @@
+package com.platform.modules.chat.service.impl;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.date.DateUtil;
+import com.platform.common.constant.HeadConstant;
+import com.platform.common.shiro.ShiroUtils;
+import com.platform.common.utils.ServletUtils;
+import com.platform.common.web.service.impl.BaseServiceImpl;
+import com.platform.modules.chat.dao.ChatFeedbackDao;
+import com.platform.modules.chat.domain.ChatFeedback;
+import com.platform.modules.chat.service.ChatFeedbackService;
+import com.platform.modules.chat.vo.MyVo04;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.util.List;
+
+/**
+ *
+ * 建议反馈 服务层实现
+ * q3z3
+ *
+ */
+@Service("chatFeedbackService")
+public class ChatFeedbackServiceImpl extends BaseServiceImpl implements ChatFeedbackService {
+
+ @Resource
+ private ChatFeedbackDao chatFeedbackDao;
+
+ @Autowired
+ public void setBaseDao() {
+ super.setBaseDao(chatFeedbackDao);
+ }
+
+ @Override
+ public List queryList(ChatFeedback t) {
+ List dataList = chatFeedbackDao.queryList(t);
+ return dataList;
+ }
+
+ @Override
+ public void addFeedback(MyVo04 myVo) {
+ String version = ServletUtils.getRequest().getHeader(HeadConstant.VERSION);
+ ChatFeedback feedback = BeanUtil.toBean(myVo, ChatFeedback.class)
+ .setUserId(ShiroUtils.getUserId())
+ .setVersion(version)
+ .setCreateTime(DateUtil.date());
+ this.add(feedback);
+ }
+}
diff --git a/src/main/java/com/platform/modules/chat/service/impl/ChatFriendServiceImpl.java b/src/main/java/com/platform/modules/chat/service/impl/ChatFriendServiceImpl.java
new file mode 100644
index 0000000..6d92909
--- /dev/null
+++ b/src/main/java/com/platform/modules/chat/service/impl/ChatFriendServiceImpl.java
@@ -0,0 +1,400 @@
+package com.platform.modules.chat.service.impl;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.convert.Convert;
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.lang.PatternPool;
+import cn.hutool.core.util.ReUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONUtil;
+import com.platform.common.constant.AppConstants;
+import com.platform.common.enums.YesOrNoEnum;
+import com.platform.common.exception.BaseException;
+import com.platform.common.redis.RedisUtils;
+import com.platform.common.shiro.ShiroUtils;
+import com.platform.common.web.service.impl.BaseServiceImpl;
+import com.platform.modules.chat.dao.ChatFriendDao;
+import com.platform.modules.chat.domain.*;
+import com.platform.modules.chat.enums.ApplySourceEnum;
+import com.platform.modules.chat.enums.ApplyStatusEnum;
+import com.platform.modules.chat.enums.ApplyTypeEnum;
+import com.platform.modules.chat.enums.FriendTypeEnum;
+import com.platform.modules.chat.service.*;
+import com.platform.modules.chat.vo.*;
+import com.platform.modules.push.enums.PushMsgEnum;
+import com.platform.modules.push.service.ChatPushService;
+import com.platform.modules.push.vo.PushParamVo;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.StringUtils;
+
+import javax.annotation.Resource;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+/**
+ *
+ * 好友表 服务层实现
+ * q3z3
+ *
+ */
+@Service("chatFriendService")
+public class ChatFriendServiceImpl extends BaseServiceImpl implements ChatFriendService {
+
+ @Resource
+ private ChatFriendDao chatFriendDao;
+
+ @Resource
+ @Lazy
+ private ChatUserService chatUserService;
+
+ @Resource
+ private ChatApplyService chatApplyService;
+
+ @Resource
+ private ChatPushService chatPushService;
+
+ @Resource
+ private ChatGroupService groupService;
+
+ @Resource
+ private ChatGroupInfoService groupInfoService;
+
+ @Resource
+ private ChatTalkService chatTalkService;
+
+ @Autowired
+ private RedisUtils redisUtils;
+
+ @Autowired
+ public void setBaseDao() {
+ super.setBaseDao(chatFriendDao);
+ }
+
+ @Override
+ public List queryList(ChatFriend t) {
+ List dataList = chatFriendDao.queryList(t);
+ return dataList;
+ }
+
+ @Override
+ public FriendVo07 findFriend(String param) {
+ // 好友
+ ChatUser chatUser;
+ // 来源
+ ApplySourceEnum sourceEnum = null;
+ // 按扫码加好友
+ if (StrUtil.startWith(param, AppConstants.QR_CODE_USER)) {
+ Long userId = Convert.toLong(ReUtil.get(PatternPool.NUMBERS, param, 0), null);
+ chatUser = chatUserService.getById(userId);
+ sourceEnum = ApplySourceEnum.SCAN;
+ }
+ // 按手机搜索
+ else if ((chatUser = chatUserService.queryByPhone(param)) != null) {
+ sourceEnum = ApplySourceEnum.PHONE;
+ }
+ // 按微信号搜索
+ else if ((chatUser = chatUserService.queryOne(new ChatUser().setChatNo(param))) != null) {
+ sourceEnum = ApplySourceEnum.CHAT_NO;
+ }
+ if (chatUser == null) {
+ throw new BaseException("暂无结果");
+ }
+ if (ShiroUtils.getPhone().equals(chatUser.getPhone())) {
+ throw new BaseException("不能添加自己为好友");
+ }
+ FriendVo07 friendVo = formatFriendVo(chatUser);
+ if (friendVo.getSource() == null) {
+ friendVo.setSource(sourceEnum);
+ }
+ return friendVo;
+ }
+
+ @Transactional
+ @Override
+ public void applyFriend(FriendVo02 friendVo) {
+ //当前登录人
+ Long userId = ShiroUtils.getUserId();
+ Long friendId = friendVo.getUserId();
+ // 验证是否是自己
+ if (userId.equals(friendId)) {
+ throw new BaseException("你不能添加自己为好友");
+ }
+ // 查询好友
+ ChatUser user = chatUserService.getById(friendId);
+ if (user == null) {
+ throw new BaseException("好友不存在");
+ }
+ ChatFriend friend1 = getFriend(userId, friendId);
+ ChatFriend friend2 = getFriend(friendId, userId);
+ if (friend1 != null && friend2 != null) {
+ throw new BaseException("已经是你的好友了,不能重复添加");
+ }
+ // 申请好友
+ chatApplyService.applyFriend(friendId, friendVo.getSource(), friendVo.getReason());
+ }
+
+ @Transactional
+ @Override
+ public void agree(Long applyId) {
+ ChatApply apply = verifyApply(applyId);
+ ChatUser fromUser = chatUserService.getById(apply.getFromId());
+ // 更新申请
+ chatApplyService.updateById(new ChatApply()
+ .setId(apply.getId())
+ .setApplyStatus(ApplyStatusEnum.AGREE));
+ if (fromUser == null) {
+ return;
+ }
+ if (ApplyTypeEnum.FRIEND.equals(apply.getApplyType())) {
+ agreeFriend(apply, fromUser);
+ } else {
+ agreeGroup(apply, fromUser);
+ }
+ }
+
+ /**
+ * 同意朋友
+ */
+ private void agreeFriend(ChatApply apply, ChatUser fromUser) {
+ Long toId = ShiroUtils.getUserId();
+ Long fromId = apply.getFromId();
+ Date now = DateUtil.date();
+ ApplySourceEnum source = apply.getApplySource();
+ ChatUser toUser = chatUserService.getById(toId);
+ // 添加好友列表
+ List friendList = new ArrayList<>();
+ ChatFriend friend1 = new ChatFriend().setFromId(toId).setToId(fromId);
+ if (this.queryOne(friend1) == null) {
+ friendList.add(friend1.setCreateTime(now)
+ .setSource(source)
+ .setBlack(YesOrNoEnum.NO)
+ .setTop(YesOrNoEnum.NO)
+ .setRemark(fromUser.getNickName()));
+ }
+ ChatFriend friend2 = new ChatFriend().setFromId(fromId).setToId(toId);
+ if (this.queryOne(friend2) == null) {
+ friendList.add(friend2.setCreateTime(now)
+ .setSource(source)
+ .setTop(YesOrNoEnum.NO)
+ .setBlack(YesOrNoEnum.NO)
+ .setRemark(toUser.getNickName()));
+ }
+ if (CollectionUtils.isEmpty(friendList)) {
+ return;
+ }
+ // 增加好友数据
+ this.batchAdd(friendList);
+ // 发送通知
+ chatPushService.pushMsg(ChatUser.initParam(fromUser).setContent(AppConstants.NOTICE_FRIEND_CREATE).setToId(toId), PushMsgEnum.ALERT);
+ chatPushService.pushMsg(ChatUser.initParam(toUser).setContent(AppConstants.NOTICE_FRIEND_CREATE).setToId(fromId), PushMsgEnum.ALERT);
+ }
+
+ /**
+ * 同意群组
+ */
+ private void agreeGroup(ChatApply apply, ChatUser fromUser) {
+ Long toId = ShiroUtils.getUserId();
+ Long fromId = apply.getFromId();
+ Long groupId = apply.getTargetId();
+ // 查询群
+ ChatGroup group = groupService.getById(groupId);
+ if (group == null) {
+ return;
+ }
+ if (!group.getMaster().equals(toId)) {
+ throw new BaseException("你不是群主,不能操作");
+ }
+ ChatGroupInfo groupInfo = groupInfoService.getGroupInfo(groupId, fromId, YesOrNoEnum.NO);
+ // 加群
+ if (groupInfo == null) {
+ groupInfoService.add(new ChatGroupInfo(fromId, groupId));
+ }
+ // 发送通知
+ String content = StrUtil.format(AppConstants.NOTICE_GROUP_JOIN, fromUser.getNickName());
+ List pushParamList = groupService.queryGroupPushFrom(groupId, null, content);
+ chatPushService.pushMsg(pushParamList, PushMsgEnum.ALERT);
+ }
+
+ @Override
+ public void refused(Long applyId) {
+ ChatApply apply = verifyApply(applyId);
+ // 更新申请
+ chatApplyService.updateById(new ChatApply().setId(apply.getId()).setApplyStatus(ApplyStatusEnum.REFUSED));
+ }
+
+ @Override
+ public void ignore(Long applyId) {
+ ChatApply apply = verifyApply(applyId);
+ // 更新申请
+ chatApplyService.updateById(new ChatApply().setId(apply.getId()).setApplyStatus(ApplyStatusEnum.IGNORE));
+ }
+
+ @Override
+ public void setBlack(FriendVo03 friendVo) {
+ Long userId = ShiroUtils.getUserId();
+ Long friendId = friendVo.getUserId();
+ // 校验是否是好友
+ ChatFriend friend = getFriend(userId, friendId);
+ if (friend == null) {
+ throw new BaseException(AppConstants.FRIEND_NOT_EXIST);
+ }
+ this.updateById(new ChatFriend().setId(friend.getId()).setBlack(friendVo.getBlack()));
+ // 移除缓存
+ this.delFriendCache(userId, friendId);
+ }
+
+ @Transactional
+ @Override
+ public void delFriend(Long friendId) {
+ Long userId = ShiroUtils.getUserId();
+ // 校验是否是好友
+ ChatFriend friend = getFriend(userId, friendId);
+ if (friend == null) {
+ throw new BaseException(AppConstants.FRIEND_NOT_EXIST);
+ }
+ this.deleteById(friend.getId());
+ // 移除缓存
+ this.delFriendCache(userId, friendId);
+ }
+
+ @Override
+ public void setRemark(FriendVo05 friendVo) {
+ Long userId = ShiroUtils.getUserId();
+ Long friendId = friendVo.getUserId();
+ // 校验是否是好友
+ ChatFriend friend = getFriend(userId, friendId);
+ if (friend == null) {
+ throw new BaseException(AppConstants.FRIEND_NOT_EXIST);
+ }
+ ChatFriend cf = new ChatFriend().setId(friend.getId()).setRemark(friendVo.getRemark());
+ this.updateById(cf);
+ // 移除缓存
+ this.delFriendCache(userId, friendId);
+ }
+
+ @Override
+ public void setTop(FriendVo09 friendVo) {
+ Long userId = ShiroUtils.getUserId();
+ Long friendId = friendVo.getUserId();
+ // 校验是否是好友
+ ChatFriend friend = getFriend(userId, friendId);
+ if (friend == null) {
+ throw new BaseException(AppConstants.FRIEND_NOT_EXIST);
+ }
+ this.updateById(new ChatFriend().setId(friend.getId()).setTop(friendVo.getTop()));
+ // 移除缓存
+ this.delFriendCache(userId, friendId);
+ }
+
+ @Override
+ public List friendList(String param) {
+ List list1 = chatTalkService.queryFriendList();
+ List list2 = chatFriendDao.friendList(ShiroUtils.getUserId());
+ List dataList = new ArrayList<>();
+ if (!CollectionUtils.isEmpty(list1)) {
+ dataList.addAll(list1);
+ }
+ if (!CollectionUtils.isEmpty(list2)) {
+ dataList.addAll(list2);
+ }
+ if (!StringUtils.isEmpty(param)) {
+ // 过滤
+ dataList = dataList.stream().filter(data -> data.getNickName().contains(param)).collect(Collectors.toList());
+ }
+ return dataList;
+ }
+
+ /**
+ * 格式化好友
+ */
+ private FriendVo07 formatFriendVo(ChatUser chatUser) {
+ Long userId = ShiroUtils.getUserId();
+ Long friendId = chatUser.getUserId();
+ FriendVo07 friendVo = BeanUtil.toBean(chatUser, FriendVo07.class);
+ // 校验是否是好友
+ ChatFriend friend = getFriend(userId, friendId);
+ if (friend == null) {
+ return friendVo;
+ }
+ if (getFriend(friendId, userId) != null) {
+ friendVo.setIsFriend(YesOrNoEnum.YES);
+ }
+ return friendVo.setBlack(friend.getBlack())
+ .setNickName(friend.getRemark())
+ .setSource(friend.getSource());
+ }
+
+ @Override
+ public FriendVo07 getInfo(Long friendId) {
+ Long userId = ShiroUtils.getUserId();
+ FriendVo07 talk = chatTalkService.queryFriendInfo(friendId);
+ if (talk != null) {
+ return talk;
+ }
+ ChatUser chatUser = chatUserService.getById(friendId);
+ if (chatUser == null) {
+ throw new BaseException("用户信息不存在");
+ }
+ if (userId.equals(friendId)) {
+ FriendVo07 friendVo = BeanUtil.toBean(chatUser, FriendVo07.class);
+ return friendVo.setIsFriend(YesOrNoEnum.YES)
+ .setSource(ApplySourceEnum.SYS)
+ .setUserType(FriendTypeEnum.SELF);
+ }
+ return formatFriendVo(chatUser);
+ }
+
+ /**
+ * 校验申请
+ */
+ private ChatApply verifyApply(Long applyId) {
+ ChatApply apply = chatApplyService.getById(applyId);
+ if (apply == null
+ || !ShiroUtils.getUserId().equals(apply.getToId())
+ || !ApplyStatusEnum.NONE.equals(apply.getApplyStatus())) {
+ throw new BaseException("申请已过期,请刷新后重试");
+ }
+ return apply;
+ }
+
+ @Override
+ public ChatFriend getFriend(Long userId, Long friendId) {
+ String key = makeFriendKey(userId, friendId);
+ if (redisUtils.hasKey(key)) {
+ return JSONUtil.toBean(redisUtils.get(key), ChatFriend.class);
+ }
+ ChatFriend friend = queryOne(new ChatFriend().setFromId(userId).setToId(friendId));
+ if (friend == null) {
+ return null;
+ }
+ redisUtils.set(key, JSONUtil.toJsonStr(friend), AppConstants.REDIS_FRIEND_TIME, TimeUnit.DAYS);
+ return friend;
+ }
+
+ @Override
+ public List queryFriendId(Long userId) {
+ return chatFriendDao.queryFriendId(userId);
+ }
+
+ /**
+ * 生成好友缓存
+ */
+ private String makeFriendKey(Long userId, Long friendId) {
+ return StrUtil.format(AppConstants.REDIS_FRIEND, userId, friendId);
+ }
+
+ /**
+ * 删除好友缓存
+ */
+ private void delFriendCache(Long userId, Long friendId) {
+ redisUtils.delete(makeFriendKey(userId, friendId));
+ }
+
+}
diff --git a/src/main/java/com/platform/modules/chat/service/impl/ChatGroupInfoServiceImpl.java b/src/main/java/com/platform/modules/chat/service/impl/ChatGroupInfoServiceImpl.java
new file mode 100644
index 0000000..b5eb984
--- /dev/null
+++ b/src/main/java/com/platform/modules/chat/service/impl/ChatGroupInfoServiceImpl.java
@@ -0,0 +1,114 @@
+package com.platform.modules.chat.service.impl;
+
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.platform.common.constant.AppConstants;
+import com.platform.common.enums.YesOrNoEnum;
+import com.platform.common.exception.BaseException;
+import com.platform.common.redis.RedisUtils;
+import com.platform.common.web.service.impl.BaseServiceImpl;
+import com.platform.modules.chat.dao.ChatGroupInfoDao;
+import com.platform.modules.chat.domain.ChatGroupInfo;
+import com.platform.modules.chat.service.ChatGroupInfoService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.util.CollectionUtils;
+
+import javax.annotation.Resource;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+/**
+ *
+ * 服务层实现
+ * q3z3
+ *
+ */
+@Service("chatGroupInfoService")
+public class ChatGroupInfoServiceImpl extends BaseServiceImpl implements ChatGroupInfoService {
+
+ @Resource
+ private ChatGroupInfoDao chatGroupInfoDao;
+
+ @Autowired
+ private RedisUtils redisUtils;
+
+ @Autowired
+ public void setBaseDao() {
+ super.setBaseDao(chatGroupInfoDao);
+ }
+
+ @Override
+ public List queryList(ChatGroupInfo t) {
+ List dataList = chatGroupInfoDao.queryList(t);
+ return dataList;
+ }
+
+ @Override
+ public ChatGroupInfo getGroupInfo(Long groupId, Long userId, YesOrNoEnum verify) {
+ String key = StrUtil.format(AppConstants.REDIS_GROUP_INFO, groupId, userId);
+ ChatGroupInfo info;
+ // 缓存存在
+ if (redisUtils.hasKey(key)) {
+ info = JSONUtil.toBean(redisUtils.get(key), ChatGroupInfo.class);
+ }
+ // 缓存不存在
+ else if ((info = this.queryOne(new ChatGroupInfo().setUserId(userId).setGroupId(groupId))) != null) {
+ redisUtils.set(key, JSONUtil.toJsonStr(info), AppConstants.REDIS_GROUP_TIME, TimeUnit.DAYS);
+ }
+ if (YesOrNoEnum.NO.equals(verify)) {
+ return info;
+ }
+ if (info == null) {
+ throw new BaseException("你不在当前群中");
+ }
+ return info;
+ }
+
+ @Override
+ public void delGroupInfoCache(Long groupId, List userList) {
+ userList.forEach(e -> {
+ redisUtils.delete(StrUtil.format(AppConstants.REDIS_GROUP_INFO, groupId, e));
+ });
+ }
+
+ @Override
+ public Long countByGroup(Long groupId) {
+ return queryCount(new ChatGroupInfo().setGroupId(groupId));
+ }
+
+ @Override
+ public List queryUserList(Long groupId) {
+ // 查询所有成员
+ List infoList = this.queryList(new ChatGroupInfo().setGroupId(groupId));
+ return infoList.stream().map(ChatGroupInfo::getUserId).collect(Collectors.toList());
+ }
+
+ @Override
+ public List queryUserList(Long groupId, List userList) {
+ List dataList = this.queryList(new ChatGroupInfo().setGroupId(groupId));
+ if (!CollectionUtils.isEmpty(userList)) {
+ dataList = dataList.stream().filter(data -> userList.contains(data.getUserId())).collect(Collectors.toList());
+ }
+ return dataList;
+ }
+
+ @Override
+ public Map queryUserMap(Long groupId) {
+ // 查询所有成员
+ List dataList = this.queryList(new ChatGroupInfo().setGroupId(groupId));
+ return dataList.stream().collect(Collectors.toMap(ChatGroupInfo::getUserId, a -> a, (k1, k2) -> k1));
+ }
+
+ @Override
+ public void delByGroup(Long groupId) {
+ chatGroupInfoDao.delete(new QueryWrapper<>(new ChatGroupInfo().setGroupId(groupId)));
+ // 删除群成员
+ String redisKey = StrUtil.format(AppConstants.REDIS_GROUP_INFO, groupId, "*");
+ redisUtils.delete(redisKey);
+ }
+
+}
diff --git a/src/main/java/com/platform/modules/chat/service/impl/ChatGroupServiceImpl.java b/src/main/java/com/platform/modules/chat/service/impl/ChatGroupServiceImpl.java
new file mode 100644
index 0000000..2a6d82f
--- /dev/null
+++ b/src/main/java/com/platform/modules/chat/service/impl/ChatGroupServiceImpl.java
@@ -0,0 +1,541 @@
+package com.platform.modules.chat.service.impl;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.convert.Convert;
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.lang.Dict;
+import cn.hutool.core.lang.PatternPool;
+import cn.hutool.core.thread.ThreadUtil;
+import cn.hutool.core.util.RandomUtil;
+import cn.hutool.core.util.ReUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONArray;
+import cn.hutool.json.JSONUtil;
+import com.github.pagehelper.PageHelper;
+import com.platform.common.constant.AppConstants;
+import com.platform.common.enums.YesOrNoEnum;
+import com.platform.common.exception.BaseException;
+import com.platform.common.shiro.ShiroUtils;
+import com.platform.common.web.service.impl.BaseServiceImpl;
+import com.platform.modules.chat.dao.ChatGroupDao;
+import com.platform.modules.chat.domain.*;
+import com.platform.modules.chat.enums.ApplyStatusEnum;
+import com.platform.modules.chat.enums.ApplyTypeEnum;
+import com.platform.modules.chat.service.ChatApplyService;
+import com.platform.modules.chat.service.ChatGroupInfoService;
+import com.platform.modules.chat.service.ChatGroupService;
+import com.platform.modules.chat.service.ChatUserService;
+import com.platform.modules.chat.vo.GroupVo02;
+import com.platform.modules.chat.vo.GroupVo03;
+import com.platform.modules.chat.vo.GroupVo07;
+import com.platform.modules.chat.vo.GroupVo08;
+import com.platform.modules.push.enums.PushMsgEnum;
+import com.platform.modules.push.service.ChatPushService;
+import com.platform.modules.push.vo.PushParamVo;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.StringUtils;
+
+import javax.annotation.Resource;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ *
+ * 群组 服务层实现
+ * q3z3
+ *
+ */
+@Service("chatGroupService")
+public class ChatGroupServiceImpl extends BaseServiceImpl implements ChatGroupService {
+
+ @Resource
+ private ChatGroupDao chatGroupDao;
+
+ @Resource
+ private ChatUserService chatUserService;
+
+ @Resource
+ private ChatGroupInfoService groupInfoService;
+
+ @Resource
+ private ChatApplyService chatApplyService;
+
+ @Resource
+ private ChatPushService chatPushService;
+
+ @Autowired
+ public void setBaseDao() {
+ super.setBaseDao(chatGroupDao);
+ }
+
+ @Override
+ public List queryList(ChatGroup t) {
+ List dataList = chatGroupDao.queryList(t);
+ return dataList;
+ }
+
+ @Transactional
+ @Override
+ public void createGroup(List list) {
+ Long userId = ShiroUtils.getUserId();
+ // 传入用户
+ List userList = verifyUserList(list);
+ // 当前登录人
+ ChatUser master = chatUserService.getById(userId);
+ // 当前时间
+ Date now = DateUtil.date();
+ // 建群
+ String portrait = "https://img.alicdn.com/imgextra/i3/87413133/O1CN01mHA9DJ1Z0xlORnKuW_!!87413133.png";
+ ChatGroup group = new ChatGroup()
+ .setMaster(master.getUserId())
+ .setName(StrUtil.format(AppConstants.GROUP_CREATE_NAME, master.getNickName(), RandomUtil.randomString(4)))
+ .setPortrait(JSONUtil.toJsonStr(new JSONArray().put(portrait)))
+ .setCreateTime(now);
+ this.add(group);
+ // 群明细
+ List infoList = new ArrayList<>();
+ for (ChatUser user : userList) {
+ infoList.add(new ChatGroupInfo(user.getUserId(), group.getId()).setKeepGroup(YesOrNoEnum.YES));
+ }
+ infoList.add(new ChatGroupInfo(master.getUserId(), group.getId()).setKeepGroup(YesOrNoEnum.YES));
+ groupInfoService.batchAdd(infoList);
+ String groupName = formatGroupName(group.getId(), group.getName());
+ PushParamVo paramVo1 = new PushParamVo()
+ .setUserId(group.getId())
+ .setNickName(groupName)
+ .setPortrait(group.getPortrait())
+ .setContent(StrUtil.format(AppConstants.NOTICE_GROUP_CREATE_MEMBER, master.getNickName()));
+ // 通知组员
+ chatPushService.pushGroupMsg(formatFrom(list, paramVo1), paramVo1, PushMsgEnum.ALERT);
+ // 通知群主
+ List nickList = userList.stream().map(ChatUser::getNickName).collect(Collectors.toList());
+ String content = StrUtil.format(AppConstants.NOTICE_GROUP_CREATE_MASTER, CollUtil.join(nickList, "、"));
+ PushParamVo paramVo2 = new PushParamVo()
+ .setUserId(group.getId())
+ .setNickName(groupName)
+ .setPortrait(group.getPortrait())
+ .setContent(content)
+ .setToId(userId);
+ chatPushService.pushGroupMsg(Arrays.asList(paramVo2), paramVo2, PushMsgEnum.ALERT);
+ }
+
+ /**
+ * 计算群名
+ */
+ private String formatGroupName(Long groupId, String groupName) {
+ if (StringUtils.isEmpty(groupName)) {
+ groupName = getById(groupId).getName();
+ }
+ Long count = groupInfoService.countByGroup(groupId);
+ return StrUtil.format("{}({})", groupName, count);
+ }
+
+ @Override
+ public Dict getInfo(Long groupId) {
+ // 校验
+ ChatGroup group = this.getById(groupId);
+ if (group == null) {
+ throw new BaseException("当前群不存在");
+ }
+ Long userId = ShiroUtils.getUserId();
+ ChatGroupInfo info = groupInfoService.getGroupInfo(groupId, userId, YesOrNoEnum.YES);
+ // 组装返回值
+ Dict groupDict = Dict.create().parseBean(group)
+ .filter("name", "notice", "portrait")
+ .set("groupId", group.getId());
+ Dict set = Dict.create().parseBean(info)
+ .filter("top", "disturb", "keepGroup");
+ List userList = groupInfoService.queryUserList(groupId);
+ List userDict = chatUserService.getByIds(userList).stream().collect(ArrayList::new, (x, y) -> {
+ x.add(Dict.create().parseBean(y).filter("userId", "nickName", "portrait"));
+ }, ArrayList::addAll);
+ return Dict.create()
+ .set("user", userDict)
+ .set("group", groupDict)
+ .set("set", set)
+ .set("master", group.getMaster().equals(userId) ? YesOrNoEnum.YES : YesOrNoEnum.NO);
+ }
+
+ @Transactional
+ @Override
+ public void invitationGroup(Long groupId, List list) {
+ // 查询用户列表
+ List userList = verifyUserList(list);
+ // 当前用户
+ Long userId = ShiroUtils.getUserId();
+ // 验证群
+ groupInfoService.getGroupInfo(groupId, userId, YesOrNoEnum.YES);
+ // 集合
+ Map infoMap = groupInfoService.queryUserMap(groupId);
+ // 群明细
+ List infoList = new ArrayList<>();
+ List newUserList = new ArrayList<>();
+ for (ChatUser chatUser : userList) {
+ ChatGroupInfo groupInfo = infoMap.get(chatUser.getUserId());
+ // 新增
+ if (groupInfo == null) {
+ infoList.add(new ChatGroupInfo(chatUser.getUserId(), groupId));
+ newUserList.add(chatUser);
+ }
+ }
+ // 批量信息
+ groupInfoService.batchAdd(infoList);
+ // 通知
+ ChatGroup group = getById(groupId);
+ List nickList = newUserList.stream().map(ChatUser::getNickName).collect(Collectors.toList());
+ String content = StrUtil.format(AppConstants.NOTICE_GROUP_JOIN, CollUtil.join(nickList, "、"));
+ List pushParamList = queryPushParam(group, content);
+ chatPushService.pushGroupMsg(pushParamList, initGroupParam(group), PushMsgEnum.ALERT);
+ }
+
+ /**
+ * 更新群主
+ */
+ @Transactional
+ protected void updMaster(ChatGroup group) {
+ Long groupId = group.getId();
+ // 执行分页
+ PageHelper.startPage(1, 1, "info_id");
+ // 查询所有成员
+ List userList = groupInfoService.queryUserList(groupId);
+ if (CollectionUtils.isEmpty(userList)) {
+ // 删除群
+ this.deleteById(groupId);
+ return;
+ }
+ Long userId = userList.get(0);
+ // 更新群主
+ this.updateById(new ChatGroup().setId(groupId).setMaster(userId));
+ groupInfoService.updateById(new ChatGroupInfo().setInfoId(userId).setKeepGroup(YesOrNoEnum.YES));
+ // 更新申请人
+ ChatApply query = new ChatApply().setTargetId(groupId).setApplyType(ApplyTypeEnum.GROUP).setApplyStatus(ApplyStatusEnum.NONE);
+ chatApplyService.queryList(query).forEach(e -> {
+ chatApplyService.updateById(new ChatApply().setId(e.getId()).setToId(userId));
+ });
+ ChatGroupInfo groupInfo = groupInfoService.getGroupInfo(groupId, userId, YesOrNoEnum.NO);
+ String groupName = formatGroupName(group.getId(), group.getName());
+ // 通知新群主
+ PushParamVo paramVo = new PushParamVo()
+ .setUserId(group.getId())
+ .setNickName(groupName)
+ .setPortrait(group.getPortrait())
+ .setDisturb(groupInfo.getDisturb())
+ .setTop(groupInfo.getTop())
+ .setContent(AppConstants.NOTICE_GROUP_TRANSFER);
+ chatPushService.pushGroupMsg(paramVo.setToId(userId), initGroupParam(group), PushMsgEnum.ALERT);
+ }
+
+ /**
+ * 验证人员信息
+ */
+ private List verifyUserList(List list) {
+ // 验证
+ if (CollectionUtils.isEmpty(list)) {
+ throw new BaseException("好友列表不能为空");
+ }
+ if (list.contains(ShiroUtils.getUserId())) {
+ throw new BaseException("好友列表不能包含自己");
+ }
+ // 去重
+ list = list.stream().distinct().collect(Collectors.toList());
+ // 验证
+ if (CollectionUtils.isEmpty(list)) {
+ throw new BaseException("好友列表不能为空");
+ }
+ // 查询用户列表
+ return chatUserService.getByIds(list);
+ }
+
+ @Transactional
+ @Override
+ public void kickedGroup(Long groupId, List list) {
+ // 查询用户列表
+ List userList = verifyUserList(list);
+ // 当前用户
+ Long userId = ShiroUtils.getUserId();
+ // 验证群
+ ChatGroupInfo groupInfo = groupInfoService.getGroupInfo(groupId, userId, YesOrNoEnum.YES);
+ // 查询群
+ ChatGroup group = getById(groupId);
+ if (!group.getMaster().equals(userId)) {
+ throw new BaseException("你不是群主,不能操作");
+ }
+ // 删除成员
+ groupInfoService.queryUserList(groupId, list).forEach(e -> {
+ groupInfoService.deleteById(e.getInfoId());
+ });
+ String groupName = formatGroupName(group.getId(), group.getName());
+ // 群主
+ List nickList = userList.stream().map(ChatUser::getNickName).collect(Collectors.toList());
+ PushParamVo paramVo = new PushParamVo()
+ .setUserId(group.getId())
+ .setNickName(groupName)
+ .setPortrait(group.getPortrait())
+ .setDisturb(groupInfo.getDisturb())
+ .setTop(groupInfo.getTop())
+ .setContent(StrUtil.format(AppConstants.NOTICE_GROUP_KICKED_MASTER, CollUtil.join(nickList, "、")));
+ chatPushService.pushGroupMsg(paramVo.setToId(group.getMaster()), paramVo, PushMsgEnum.ALERT);
+ // 组员
+ List paramList = queryGroupPushFrom(groupId, list, AppConstants.NOTICE_GROUP_KICKED_MEMBER);
+ chatPushService.pushGroupMsg(paramList, paramVo, PushMsgEnum.ALERT);
+ // 删除缓存
+ groupInfoService.delGroupInfoCache(groupId, list);
+ }
+
+ @Override
+ public String getGroupQrCode(Long groupId) {
+ return AppConstants.QR_CODE_GROUP + groupId;
+ }
+
+ @Override
+ public void editTop(Long groupId, YesOrNoEnum top) {
+ Long userId = ShiroUtils.getUserId();
+ ChatGroupInfo info = groupInfoService.getGroupInfo(groupId, userId, YesOrNoEnum.YES);
+ groupInfoService.updateById(new ChatGroupInfo().setInfoId(info.getInfoId()).setTop(top));
+ // 删除缓存
+ groupInfoService.delGroupInfoCache(groupId, Arrays.asList(userId));
+ }
+
+ @Override
+ public void editDisturb(Long groupId, YesOrNoEnum disturb) {
+ Long userId = ShiroUtils.getUserId();
+ ChatGroupInfo info = groupInfoService.getGroupInfo(groupId, userId, YesOrNoEnum.YES);
+ groupInfoService.updateById(new ChatGroupInfo().setInfoId(info.getInfoId()).setDisturb(disturb));
+ // 删除缓存
+ groupInfoService.delGroupInfoCache(groupId, Arrays.asList(userId));
+ }
+
+ @Override
+ public void editKeepGroup(Long groupId, YesOrNoEnum keepGroup) {
+ Long userId = ShiroUtils.getUserId();
+ ChatGroupInfo info = groupInfoService.getGroupInfo(groupId, userId, YesOrNoEnum.YES);
+ groupInfoService.updateById(new ChatGroupInfo().setInfoId(info.getInfoId()).setKeepGroup(keepGroup));
+ // 删除缓存
+ groupInfoService.delGroupInfoCache(groupId, Arrays.asList(userId));
+ }
+
+ @Transactional
+ @Override
+ public void logoutGroup(Long groupId) {
+ Long userId = ShiroUtils.getUserId();
+ ChatUser chatUser = chatUserService.getById(userId);
+ ChatGroupInfo groupInfo = groupInfoService.getGroupInfo(groupId, userId, YesOrNoEnum.YES);
+ ChatGroup group = getById(groupId);
+ // 删除数据
+ groupInfoService.deleteById(groupInfo.getInfoId());
+ // 删除缓存
+ groupInfoService.delGroupInfoCache(groupId, Arrays.asList(userId));
+ if (group.getMaster().equals(userId)) {
+ // 变更群主
+ ThreadUtil.execAsync(() -> {
+ updMaster(group);
+ });
+ } else {
+ String groupName = formatGroupName(groupId, group.getName());
+ // 通知
+ PushParamVo paramVo = new PushParamVo()
+ .setUserId(group.getId())
+ .setPortrait(group.getPortrait())
+ .setNickName(groupName)
+ .setDisturb(groupInfo.getDisturb())
+ .setTop(groupInfo.getTop())
+ .setContent(StrUtil.format(AppConstants.NOTICE_GROUP_LOGOUT, chatUser.getNickName()))
+ .setToId(group.getMaster());
+ chatPushService.pushGroupMsg(paramVo, paramVo, PushMsgEnum.ALERT);
+ }
+ }
+
+ @Transactional
+ @Override
+ public void removeGroup(Long groupId) {
+ Long userId = ShiroUtils.getUserId();
+ ChatGroup group = getById(groupId);
+ if (group == null) {
+ throw new BaseException("当前群组不存在");
+ }
+ if (!group.getMaster().equals(userId)) {
+ throw new BaseException("你不是群主,不能操作");
+ }
+ List userList = queryPushParam(group, AppConstants.NOTICE_GROUP_DISSOLVE);
+ this.deleteById(groupId);
+ // 删除数据
+ groupInfoService.delByGroup(groupId);
+ // 通知
+ chatPushService.pushGroupMsg(userList, initGroupParam(group), PushMsgEnum.ALERT);
+ }
+
+ private List queryPushParam(ChatGroup group, String content) {
+ List dataList = groupInfoService.queryList(new ChatGroupInfo().setGroupId(group.getId()));
+ String groupName = formatGroupName(group.getId(), group.getName());
+ List