diff --git a/.idea/compiler.xml b/.idea/compiler.xml index 667a8df..91dae2f 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -7,8 +7,32 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml index 7ecaa0f..de949f4 100644 --- a/.idea/encodings.xml +++ b/.idea/encodings.xml @@ -4,5 +4,21 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index b7686d0..583a849 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -5,6 +5,7 @@ diff --git a/netty/.gitignore b/netty/.gitignore new file mode 100644 index 0000000..359dca5 --- /dev/null +++ b/netty/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ +logs/ +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/netty/common/common-generator/pom.xml b/netty/common/common-generator/pom.xml new file mode 100644 index 0000000..c99ab7e --- /dev/null +++ b/netty/common/common-generator/pom.xml @@ -0,0 +1,70 @@ + + 4.0.0 + + cn.bunny + common + 0.0.1-SNAPSHOT + + + common-generator + jar + + common-utils + https://maven.apache.org + + + UTF-8 + + + + + org.projectlombok + lombok + + + javax.xml.bind + jaxb-api + 2.1 + + + + mysql + mysql-connector-java + + + + com.zaxxer + HikariCP + 5.1.0 + + + + com.baomidou + mybatis-plus-spring-boot3-starter + + + + com.baomidou + mybatis-plus-generator + 3.5.6 + + + org.apache.velocity + velocity-engine-core + 2.3 + + + + + + + + + + + + + + + diff --git a/netty/common/common-generator/src/main/java/cn/bunny/common/generator/NewCodeGet.java b/netty/common/common-generator/src/main/java/cn/bunny/common/generator/NewCodeGet.java new file mode 100644 index 0000000..27d0fe9 --- /dev/null +++ b/netty/common/common-generator/src/main/java/cn/bunny/common/generator/NewCodeGet.java @@ -0,0 +1,83 @@ +package cn.bunny.common.generator; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.generator.FastAutoGenerator; +import com.baomidou.mybatisplus.generator.config.OutputFile; +import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Collections; + +public class NewCodeGet { + // 数据连接 + public static final String sqlHost = "jdbc:mysql://106.15.251.123:3305/bunny_docs?serverTimezone=GMT%2B8&useSSL=false&characterEncoding=utf-8&allowPublicKeyRetrieval=true"; + // 作者名称 + public static final String author = "Bunny"; + // 公共路径 + public static final String outputDir = "F:\\web项目\\PC\\BunnyNote\\BunnyBBS-server\\service\\service-web"; + // 实体类名称 + public static final String entity = "Bunny"; + + public static void main(String[] args) { + Generation("article"); + } + + /** + * 根据表名生成相应结构代码 + * + * @param tableName 表名 + */ + public static void Generation(String... tableName) { + // TODO 修改数据库路径、账户、密码 + FastAutoGenerator.create(sqlHost, "root", "02120212") + .globalConfig(builder -> { + // 添加作者名称 + builder.author(author) + // 启用swagger + .enableSwagger() + // 指定输出目录 + .outputDir(outputDir + "/src/main/java"); + }) + .packageConfig(builder -> { + builder.entity(entity)// 实体类包名 + // TODO 父包名。如果为空,将下面子包名必须写全部, 否则就只需写子包名 + .parent("cn.bunny.service.web") + .controller("controller")// 控制层包名 + .mapper("mapper")// mapper层包名 + .service("service")// service层包名 + .serviceImpl("service.impl")// service实现类包名 + // 自定义mapper.xml文件输出目录 + .pathInfo(Collections.singletonMap(OutputFile.xml, outputDir + "/src/main/resources/mapper")); + }) + .strategyConfig(builder -> { + // 设置要生成的表名 + builder.addInclude(tableName) + //.addTablePrefix("sys_")// TODO 设置表前缀过滤 + .entityBuilder() + .enableLombok() + .enableChainModel() + .naming(NamingStrategy.underline_to_camel)// 数据表映射实体命名策略:默认下划线转驼峰underline_to_camel + .columnNaming(NamingStrategy.underline_to_camel)// 表字段映射实体属性命名规则:默认null,不指定按照naming执行 + .idType(IdType.AUTO)// TODO 添加全局主键类型 + .formatFileName("%s")// 格式化实体名称,%s取消首字母I, + .mapperBuilder() + .mapperAnnotation(Mapper.class)// 开启mapper注解 + .enableBaseResultMap()// 启用xml文件中的BaseResultMap 生成 + .enableBaseColumnList()// 启用xml文件中的BaseColumnList + .formatMapperFileName("%sMapper")// 格式化Dao类名称 + .formatXmlFileName("%sMapper")// 格式化xml文件名称 + .serviceBuilder() + .formatServiceFileName("%sService")// 格式化 service 接口文件名称 + .formatServiceImplFileName("%sServiceImpl")// 格式化 service 接口文件名称 + .controllerBuilder() + .enableRestStyle(); + }) + // .injectionConfig(consumer -> { + // Map customFile = new HashMap<>(); + // // 配置DTO(需要的话)但是需要有能配置Dto的模板引擎,比如freemarker,但是这里我们用的VelocityEngine,因此不多作介绍 + // customFile.put(outputDir, "/src/main/resources/templates/entityDTO.java.ftl"); + // consumer.customFile(customFile); + // }) + .execute(); + } +} diff --git a/netty/common/common-generator/src/main/java/cn/bunny/common/generator/OldCodeGet.java b/netty/common/common-generator/src/main/java/cn/bunny/common/generator/OldCodeGet.java new file mode 100644 index 0000000..d43819c --- /dev/null +++ b/netty/common/common-generator/src/main/java/cn/bunny/common/generator/OldCodeGet.java @@ -0,0 +1,57 @@ +package cn.bunny.common.generator; + +public class OldCodeGet { + public static void main(String[] args) { + // // 1、创建代码生成器 + // AutoGenerator mpg = new AutoGenerator(); + // + // // 2、全局配置 + // // 全局配置 + // GlobalConfig gc = new GlobalConfig(); + // // TODO 需要修改路径名称 + // gc.setOutputDir("F:\\web项目\\Bunny-Cli\\Java\\java-template\\service" + "/src/main/java"); + // gc.setServiceName("%sService"); // 去掉Service接口的首字母I + // gc.setAuthor("bunny"); + // gc.setOpen(false); + // mpg.setGlobalConfig(gc); + // + // // 3、数据源配置 + // DataSourceConfig dsc = new DataSourceConfig(); + // // TODO 需要修改数据库 + // dsc.setUrl("jdbc:mysql://106.15.251.123:3305/guigu-oa?serverTimezone=GMT%2B8&useSSL=false&characterEncoding=utf-8&allowPublicKeyRetrieval=true"); + // dsc.setDriverName("com.mysql.cj.jdbc.Driver"); + // dsc.setUsername("root"); + // dsc.setPassword("02120212"); + // dsc.setDbType(DbType.MYSQL); + // mpg.setDataSource(dsc); + // + // // 4、包配置 + // PackageConfig pc = new PackageConfig(); + // pc.setParent("cn.bunny"); + // // TODO 需要修改模块名 + // pc.setModuleName("service"); + // pc.setController("controller"); + // pc.setService("service"); + // pc.setMapper("mapper"); + // mpg.setPackageInfo(pc); + // + // // 5、策略配置 + // StrategyConfig strategy = getStrategyConfig(); + // mpg.setStrategy(strategy); + // + // // 6、执行 + // mpg.execute(); + // } + // + // private static StrategyConfig getStrategyConfig() { + // StrategyConfig strategy = new StrategyConfig(); + // // TODO 要生成的表 + // strategy.setInclude("sys_menu", "sys_role_menu"); + // strategy.setNaming(NamingStrategy.underline_to_camel);// 数据库表映射到实体的命名策略 + // strategy.setColumnNaming(NamingStrategy.underline_to_camel);// 数据库表字段映射到实体的命名策略 + // strategy.setEntityLombokModel(true); // lombok 模型 @Accessors(chain = true) setter链式操作 + // strategy.setRestControllerStyle(true); // restful api风格控制器 + // strategy.setControllerMappingHyphenStyle(true); // url中驼峰转连字符 + // return strategy; + } +} diff --git a/netty/common/common-utils/pom.xml b/netty/common/common-utils/pom.xml new file mode 100644 index 0000000..f832c59 --- /dev/null +++ b/netty/common/common-utils/pom.xml @@ -0,0 +1,27 @@ + + 4.0.0 + + cn.bunny + common + 0.0.1-SNAPSHOT + + + common-utils + jar + + common-utils + https://maven.apache.org + + + UTF-8 + + + + + cn.bunny + model + 0.0.1-SNAPSHOT + + + diff --git a/netty/common/common-utils/src/main/java/cn/bunny/common/utils/CommentUtil.java b/netty/common/common-utils/src/main/java/cn/bunny/common/utils/CommentUtil.java new file mode 100644 index 0000000..b733f66 --- /dev/null +++ b/netty/common/common-utils/src/main/java/cn/bunny/common/utils/CommentUtil.java @@ -0,0 +1,51 @@ +package cn.bunny.common.utils; + +import cn.bunny.vo.system.comment.CommentVo; + +import java.util.ArrayList; +import java.util.List; + +public class CommentUtil { + /** + * 构建树型结构 + * + * @param commentList 评论列表 + * @return 结构列表 + */ + public static List buildTree(List commentList) { + // 构建树形结构 + List tree = new ArrayList<>(); + // 遍历评论列表 + for (CommentVo comment : commentList) { + // 找到顶级评论(没有父评论) + if (comment.getPCommentId() == 0) { + // 递归构建子评论 + comment.setChildren(getChildren(comment.getId(), commentList)); + tree.add(comment); + } + } + return tree; + } + + /** + * 递归获取子评论 + * + * @param commentId 当前评论ID + * @param commentList 评论列表 + * @return 子评论列表 + */ + private static List getChildren(Long commentId, List commentList) { + List children = new ArrayList<>(); + + // 遍历评论列表 + for (CommentVo comment : commentList) { + // 找到当前评论的子评论 + if (Long.valueOf(comment.getPCommentId()).equals(commentId)) { + // 递归构建子评论的子评论 + comment.setChildren(getChildren(comment.getId(), commentList)); + children.add(comment); + } + } + return children; + } +} diff --git a/netty/common/common-utils/src/main/java/cn/bunny/common/utils/FileUtil.java b/netty/common/common-utils/src/main/java/cn/bunny/common/utils/FileUtil.java new file mode 100644 index 0000000..739253c --- /dev/null +++ b/netty/common/common-utils/src/main/java/cn/bunny/common/utils/FileUtil.java @@ -0,0 +1,25 @@ +package cn.bunny.common.utils; + +public class FileUtil { + public static String getSize(Long fileSize) { + double fileSizeInKB = fileSize / 1024.00; + double fileSizeInMB = fileSizeInKB / 1024; + double fileSizeInGB = fileSizeInMB / 1024; + + String size; + if (fileSizeInGB >= 1) { + fileSizeInGB = Double.parseDouble(String.format("%.2f", fileSizeInGB)); + size = fileSizeInGB + "GB"; + } else if (fileSizeInMB >= 1) { + fileSizeInMB = Double.parseDouble(String.format("%.2f", fileSizeInMB)); + size = fileSizeInMB + "MB"; + } else if (fileSizeInKB >= 1) { + fileSizeInKB = Double.parseDouble(String.format("%.2f", fileSizeInKB)); + size = fileSizeInKB + "KB"; + } else { + size = fileSize + "B"; + } + + return size; + } +} diff --git a/netty/common/pom.xml b/netty/common/pom.xml new file mode 100644 index 0000000..c04ed68 --- /dev/null +++ b/netty/common/pom.xml @@ -0,0 +1,30 @@ + + 4.0.0 + + cn.bunny + bunny-template + 0.0.1-SNAPSHOT + + + common + pom + common Maven Webapp + https://maven.apache.org + + service-utils + common-generator + common-utils + + + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + + io.jsonwebtoken + jjwt + + + diff --git a/netty/common/service-utils/pom.xml b/netty/common/service-utils/pom.xml new file mode 100644 index 0000000..3a326b2 --- /dev/null +++ b/netty/common/service-utils/pom.xml @@ -0,0 +1,57 @@ + + 4.0.0 + + cn.bunny + common + 0.0.1-SNAPSHOT + + + service-utils + jar + service-utils + https://maven.apache.org + + + UTF-8 + + + + + cn.bunny + model + 0.0.1-SNAPSHOT + + + + org.apache.httpcomponents + httpclient + 4.5.14 + + + + com.github.xiaoymin + knife4j-openapi3-jakarta-spring-boot-starter + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-data-redis + + + + org.redisson + redisson + 3.26.1 + + + + com.github.pagehelper + pagehelper + + + diff --git a/netty/common/service-utils/src/main/java/cn/bunny/common/service/config/Knife4jConfig.java b/netty/common/service-utils/src/main/java/cn/bunny/common/service/config/Knife4jConfig.java new file mode 100644 index 0000000..5abefce --- /dev/null +++ b/netty/common/service-utils/src/main/java/cn/bunny/common/service/config/Knife4jConfig.java @@ -0,0 +1,46 @@ +package cn.bunny.common.service.config; + +import io.swagger.v3.oas.models.ExternalDocumentation; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Contact; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.info.License; +import lombok.extern.slf4j.Slf4j; +import org.springdoc.core.models.GroupedOpenApi; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +@Slf4j +public class Knife4jConfig { + @Bean + public OpenAPI openAPI() { + log.info("Knife4jConfig===>配置knife4j"); + // 作者等信息 + Contact contact = new Contact().name("Bunny").email("1319900154@qq.com").url("http://z-bunny.com"); + // 使用协议 + License license = new License().name("MIT").url("http://MUT.com"); + // 相关信息 + Info info = new Info().title("Bunny-Java-Template").description("Bunny的Java模板").version("v1.0.0").contact(contact).license(license); + + return new OpenAPI().info(info).externalDocs(new ExternalDocumentation()); + } + + // 前台相关分类接口 + @Bean + public GroupedOpenApi groupedOpenApi() { + return GroupedOpenApi.builder().group("前台接口管理").pathsToMatch("/api/**").build(); + } + + // 管理员相关分类接口 + @Bean + public GroupedOpenApi groupedOpenAdminApi() { + return GroupedOpenApi.builder().group("管理员接口请求").pathsToMatch("/admin/**").build(); + } + + // 测试相关分类接口 + @Bean + public GroupedOpenApi testGroupedOpenApi() { + return GroupedOpenApi.builder().group("测试相关分类接口").pathsToMatch("/test/**").build(); + } +} diff --git a/netty/common/service-utils/src/main/java/cn/bunny/common/service/config/MyBatisPlusFieldConfig.java b/netty/common/service-utils/src/main/java/cn/bunny/common/service/config/MyBatisPlusFieldConfig.java new file mode 100644 index 0000000..d32b125 --- /dev/null +++ b/netty/common/service-utils/src/main/java/cn/bunny/common/service/config/MyBatisPlusFieldConfig.java @@ -0,0 +1,39 @@ +package cn.bunny.common.service.config; + +import cn.bunny.common.service.context.BaseContext; +import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; +import org.apache.ibatis.reflection.MetaObject; +import org.springframework.stereotype.Component; + +import java.util.Date; + +/** + * 配置MP在修改和新增时的操作 + */ +@Component +public class MyBatisPlusFieldConfig implements MetaObjectHandler { + + /** + * 使用mp做添加操作时候,这个方法执行 + */ + @Override + public void insertFill(MetaObject metaObject) { + // 设置属性值 + this.setFieldValByName("createTime", new Date(), metaObject); + this.setFieldValByName("updateTime", new Date(), metaObject); + this.setFieldValByName("deleteStatus", 1, metaObject); + if (BaseContext.getUsername() != null) { + this.setFieldValByName("createBy", BaseContext.getUsername(), metaObject); + this.setFieldValByName("updateBy", BaseContext.getUsername(), metaObject); + } + } + + /** + * 使用mp做修改操作时候,这个方法执行 + */ + @Override + public void updateFill(MetaObject metaObject) { + this.setFieldValByName("updateTime", new Date(), metaObject); + this.setFieldValByName("updateBy", BaseContext.getUsername(), metaObject); + } +} diff --git a/netty/common/service-utils/src/main/java/cn/bunny/common/service/config/MybatisPlusConfig.java b/netty/common/service-utils/src/main/java/cn/bunny/common/service/config/MybatisPlusConfig.java new file mode 100644 index 0000000..013c778 --- /dev/null +++ b/netty/common/service-utils/src/main/java/cn/bunny/common/service/config/MybatisPlusConfig.java @@ -0,0 +1,42 @@ +package cn.bunny.common.service.config; + +import com.baomidou.mybatisplus.annotation.DbType; +import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; +import com.github.pagehelper.PageInterceptor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +/** + * Mybatis-Plus配置类 + */ +@EnableTransactionManagement +@Configuration +@Slf4j +public class MybatisPlusConfig { + @Bean + public MybatisPlusInterceptor mybatisPlusInterceptor() { + log.info("MybatisPlusInterceptor===>注入Mybatis-Plus配置..."); + + MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); + // 分页插件 + PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL); + paginationInnerInterceptor.setMaxLimit(100L);// ? 设置最大分页为100 + interceptor.addInnerInterceptor(paginationInnerInterceptor); + // 乐观锁 + interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); + // 防止全表删除 + interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor()); + + return interceptor; + } + + @Bean// pagehelper分页插件需要配的拦截器 + public PageInterceptor pageInterceptor() { + return new PageInterceptor(); + } +} diff --git a/netty/common/service-utils/src/main/java/cn/bunny/common/service/config/RedisConfiguration.java b/netty/common/service-utils/src/main/java/cn/bunny/common/service/config/RedisConfiguration.java new file mode 100644 index 0000000..1b5c8d7 --- /dev/null +++ b/netty/common/service-utils/src/main/java/cn/bunny/common/service/config/RedisConfiguration.java @@ -0,0 +1,98 @@ +package cn.bunny.common.service.config; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; +import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.CacheManager; +import org.springframework.context.annotation.Bean; +import org.springframework.data.redis.cache.RedisCacheConfiguration; +import org.springframework.data.redis.cache.RedisCacheManager; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.RedisSerializationContext; +import org.springframework.data.redis.serializer.StringRedisSerializer; +import org.springframework.stereotype.Component; + +import java.time.Duration; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +/** + * 设置Redis序列化 + */ +@Component +@Slf4j +public class RedisConfiguration { + /** + * 使用StringRedisSerializer序列化为字符串 + */ + @Bean + public RedisTemplate redisTemplate(LettuceConnectionFactory connectionFactory) { + log.info("RedisConfiguration===>使用StringRedisSerializer序列化为字符串"); + + RedisTemplate redisTemplate = new RedisTemplate<>(); + redisTemplate.setConnectionFactory(connectionFactory); + // 设置key序列化为string + redisTemplate.setKeySerializer(new StringRedisSerializer()); + // 设置value序列化为JSON,使用GenericJackson2JsonRedisSerializer替换默认序列化 + redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); + redisTemplate.setHashKeySerializer(new StringRedisSerializer()); + redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer()); + + return redisTemplate; + } + + /** + * 解决cache(@Cacheable)把数据缓存到redis中的value是乱码问题 + */ + @Bean + @SuppressWarnings("all") + public CacheManager cacheManager(RedisConnectionFactory factory) { + log.info("RedisConfiguration===>解决cache(@Cacheable)把数据缓存到redis中的value是乱码问题"); + + // 配置序列化 + RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() + .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())) + .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jsonRedisSerializer())) + .entryTtl(Duration.ofDays(30)); + + return RedisCacheManager.builder(factory).cacheDefaults(config).build(); + } + + /** + * 指定的日期模式 + */ + public Jackson2JsonRedisSerializer jsonRedisSerializer() { + log.info("RedisConfiguration===>指定的日期模式"); + + ObjectMapper mapper = new ObjectMapper(); + // 设置ObjectMapper访问权限 + mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); + // 记录序列化之后的数据类型,方便反序列化 + mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL); + // LocalDatetime序列化,默认不兼容jdk8日期序列化 + JavaTimeModule timeModule = new JavaTimeModule(); + timeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); + timeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); + + timeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); + timeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); + // 关闭默认的日期格式化方式,默认UTC日期格式 yyyy-MM-dd’T’HH:mm:ss.SSS + mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + mapper.registerModule(timeModule); + + return new Jackson2JsonRedisSerializer<>(mapper, Object.class); + } +} diff --git a/netty/common/service-utils/src/main/java/cn/bunny/common/service/context/BaseContext.java b/netty/common/service-utils/src/main/java/cn/bunny/common/service/context/BaseContext.java new file mode 100644 index 0000000..1a01c70 --- /dev/null +++ b/netty/common/service-utils/src/main/java/cn/bunny/common/service/context/BaseContext.java @@ -0,0 +1,52 @@ +package cn.bunny.common.service.context; + +public class BaseContext { + private static final ThreadLocal userId = new ThreadLocal<>(); + private static final ThreadLocal username = new ThreadLocal(); + private static final ThreadLocal adminId = new ThreadLocal<>(); + private static final ThreadLocal adminName = new ThreadLocal<>(); + + // 用户id相关 + public static Long getUserId() { + return userId.get(); + } + + public static void setUserId(Long _userId) { + userId.set(_userId); + } + + public static String getUsername() { + return username.get(); + } + + public static void setUsername(String _username) { + username.set(_username); + } + + public static void removeUser() { + username.remove(); + userId.remove(); + } + + // adminId 相关 + public static Long getAdminId() { + return adminId.get(); + } + + public static void setAdminId(Long _adminId) { + adminId.set(_adminId); + } + + public static String getAdminName() { + return adminName.get(); + } + + public static void setAdminName(String _adminName) { + adminName.set(_adminName); + } + + public static void removeAdmin() { + adminName.remove(); + adminId.remove(); + } +} \ No newline at end of file diff --git a/netty/common/service-utils/src/main/java/cn/bunny/common/service/exception/BunnyException.java b/netty/common/service-utils/src/main/java/cn/bunny/common/service/exception/BunnyException.java new file mode 100644 index 0000000..1012971 --- /dev/null +++ b/netty/common/service-utils/src/main/java/cn/bunny/common/service/exception/BunnyException.java @@ -0,0 +1,33 @@ +package cn.bunny.common.service.exception; + +import cn.bunny.result.ResultCodeEnum; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; +import lombok.extern.slf4j.Slf4j; + +@NoArgsConstructor +@Getter +@ToString +@Slf4j +public class BunnyException extends RuntimeException { + Integer code;// 状态码 + String message;// 描述信息 + + public BunnyException(Integer code, String message) { + super(message); + this.code = code; + this.message = message; + } + + public BunnyException(String message) { + super(message); + this.message = message; + } + + public BunnyException(ResultCodeEnum codeEnum) { + super(codeEnum.getMessage()); + this.code = codeEnum.getCode(); + this.message = codeEnum.getMessage(); + } +} diff --git a/netty/common/service-utils/src/main/java/cn/bunny/common/service/exception/GlobalExceptionHandler.java b/netty/common/service-utils/src/main/java/cn/bunny/common/service/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..345d29b --- /dev/null +++ b/netty/common/service-utils/src/main/java/cn/bunny/common/service/exception/GlobalExceptionHandler.java @@ -0,0 +1,82 @@ +package cn.bunny.common.service.exception; + + +import cn.bunny.result.Result; +import cn.bunny.result.ResultCodeEnum; +import cn.bunny.result.constant.ExceptionConstant; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import java.io.FileNotFoundException; +import java.nio.file.AccessDeniedException; +import java.sql.SQLIntegrityConstraintViolationException; + +@RestControllerAdvice +@Slf4j +public class GlobalExceptionHandler { + // 自定义异常信息 + @ExceptionHandler(BunnyException.class) + @ResponseBody + public Result exceptionHandler(BunnyException exception) { + log.error("GlobalExceptionHandler===>自定义异常信息:{}", exception.getMessage()); + + Integer code = exception.getCode() != null ? exception.getCode() : 500; + return Result.error(null, code, exception.getMessage()); + } + + // 运行时异常信息 + @ExceptionHandler(RuntimeException.class) + @ResponseBody + public Result exceptionHandler(RuntimeException exception) throws FileNotFoundException { + log.error("GlobalExceptionHandler===>运行时异常信息:{}", exception.getMessage()); + exception.printStackTrace(); + return Result.error(null, 500, "出错了啦"); + } + + // 捕获系统异常 + @ExceptionHandler(Exception.class) + @ResponseBody + public Result error(Exception exception) { + log.error("GlobalExceptionHandler===>系统异常信息:{}", exception.getMessage()); + + return Result.error(null, 500, "系统异常"); + } + + // 特定异常处理 + @ExceptionHandler(ArithmeticException.class) + @ResponseBody + public Result error(ArithmeticException exception) { + log.error("GlobalExceptionHandler===>特定异常信息:{}", exception.getMessage()); + + return Result.error(null, 500, exception.getMessage()); + } + + // spring security异常 + @ExceptionHandler(AccessDeniedException.class) + @ResponseBody + public Result error(AccessDeniedException exception) throws AccessDeniedException { + log.error("GlobalExceptionHandler===>spring security异常:{}", exception.getMessage()); + + return Result.error(ResultCodeEnum.SERVICE_ERROR); + } + + // 处理SQL异常 + @ExceptionHandler(SQLIntegrityConstraintViolationException.class) + @ResponseBody + public Result exceptionHandler(SQLIntegrityConstraintViolationException exception) { + log.error("GlobalExceptionHandler===>处理SQL异常:{}", exception.getMessage()); + + String message = exception.getMessage(); + if (message.contains("Duplicate entry")) { + // 截取用户名 + String username = message.split(" ")[2]; + // 错误信息 + String errorMessage = username + ExceptionConstant.ALREADY_USER_Exception; + return Result.error(errorMessage); + } else { + return Result.error(ExceptionConstant.UNKNOWN_Exception); + } + } +} diff --git a/netty/common/service-utils/src/main/java/cn/bunny/common/service/properties/SnowflakeProperties.java b/netty/common/service-utils/src/main/java/cn/bunny/common/service/properties/SnowflakeProperties.java new file mode 100644 index 0000000..19b0057 --- /dev/null +++ b/netty/common/service-utils/src/main/java/cn/bunny/common/service/properties/SnowflakeProperties.java @@ -0,0 +1,25 @@ +package cn.bunny.common.service.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +@Configuration +@ConfigurationProperties(prefix = "bunny.snowflake") +@Data +public class SnowflakeProperties { + // 数据中心id + private Long datacenterId; + // 数据中心id位数 + private Long datacenterBits; + // 机器id + private Long workerId; + // 机器id位数 + private Long workerBits; + // 序列id所占位数 + private Long sequenceBits; + // 时间戳起始点(毫秒) + private Long twepoch; + // 单次批量生成id的最大数量 + private Integer maxBatchCount; +} \ No newline at end of file diff --git a/netty/common/service-utils/src/main/java/cn/bunny/common/service/utils/EmptyUtil.java b/netty/common/service-utils/src/main/java/cn/bunny/common/service/utils/EmptyUtil.java new file mode 100644 index 0000000..9cbfe0c --- /dev/null +++ b/netty/common/service-utils/src/main/java/cn/bunny/common/service/utils/EmptyUtil.java @@ -0,0 +1,21 @@ +package cn.bunny.common.service.utils; + +import cn.bunny.common.service.exception.BunnyException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.util.StringUtils; + +@Slf4j +public class EmptyUtil { + /** + * 是否为空 + * + * @param value 判断值 + * @param message 错误消息 + */ + public static void isEmpty(Object value, String message) { + if (value == null || !StringUtils.hasText(value.toString())) { + log.error("为空对象错误:{},{}", value, message); + throw new BunnyException(message); + } + } +} diff --git a/netty/common/service-utils/src/main/java/cn/bunny/common/service/utils/HttpUtil.java b/netty/common/service-utils/src/main/java/cn/bunny/common/service/utils/HttpUtil.java new file mode 100644 index 0000000..bfb9b51 --- /dev/null +++ b/netty/common/service-utils/src/main/java/cn/bunny/common/service/utils/HttpUtil.java @@ -0,0 +1,206 @@ +package cn.bunny.common.service.utils; + +import org.apache.commons.lang3.StringUtils; +import org.apache.http.HttpResponse; +import org.apache.http.NameValuePair; +import org.apache.http.client.HttpClient; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.HttpDelete; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpPut; +import org.apache.http.conn.ClientConnectionManager; +import org.apache.http.conn.scheme.Scheme; +import org.apache.http.conn.scheme.SchemeRegistry; +import org.apache.http.conn.ssl.SSLSocketFactory; +import org.apache.http.entity.ByteArrayEntity; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.message.BasicNameValuePair; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class HttpUtil { + public static HttpResponse doGet(String host, String path, String method, Map headers, Map querys) throws Exception { + HttpClient httpClient = wrapClient(host); + + HttpGet request = new HttpGet(buildUrl(host, path, querys)); + for (Map.Entry e : headers.entrySet()) { + request.addHeader(e.getKey(), e.getValue()); + } + + return httpClient.execute(request); + } + + public static HttpResponse doPost(String host, String path, String method, Map headers, Map querys, Map bodys) throws Exception { + HttpClient httpClient = wrapClient(host); + + HttpPost request = new HttpPost(buildUrl(host, path, querys)); + for (Map.Entry e : headers.entrySet()) { + request.addHeader(e.getKey(), e.getValue()); + } + + if (bodys != null) { + List nameValuePairList = new ArrayList(); + + for (String key : bodys.keySet()) { + nameValuePairList.add(new BasicNameValuePair(key, bodys.get(key))); + } + UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(nameValuePairList, "utf-8"); + formEntity.setContentType("application/x-www-form-urlencoded; charset=UTF-8"); + request.setEntity(formEntity); + } + + return httpClient.execute(request); + } + + + public static HttpResponse doPost(String host, String path, String method, Map headers, Map querys, String body) throws Exception { + HttpClient httpClient = wrapClient(host); + + HttpPost request = new HttpPost(buildUrl(host, path, querys)); + for (Map.Entry e : headers.entrySet()) { + request.addHeader(e.getKey(), e.getValue()); + } + + if (StringUtils.isNotBlank(body)) { + request.setEntity(new StringEntity(body, "utf-8")); + } + + return httpClient.execute(request); + } + + + public static HttpResponse doPost(String host, String path, String method, Map headers, Map querys, byte[] body) throws Exception { + HttpClient httpClient = wrapClient(host); + + HttpPost request = new HttpPost(buildUrl(host, path, querys)); + for (Map.Entry e : headers.entrySet()) { + request.addHeader(e.getKey(), e.getValue()); + } + + if (body != null) { + request.setEntity(new ByteArrayEntity(body)); + } + + return httpClient.execute(request); + } + + + public static HttpResponse doPut(String host, String path, String method, Map headers, Map querys, String body) throws Exception { + HttpClient httpClient = wrapClient(host); + + HttpPut request = new HttpPut(buildUrl(host, path, querys)); + for (Map.Entry e : headers.entrySet()) { + request.addHeader(e.getKey(), e.getValue()); + } + + if (StringUtils.isNotBlank(body)) { + request.setEntity(new StringEntity(body, "utf-8")); + } + + return httpClient.execute(request); + } + + + public static HttpResponse doPut(String host, String path, String method, Map headers, Map querys, byte[] body) throws Exception { + HttpClient httpClient = wrapClient(host); + + HttpPut request = new HttpPut(buildUrl(host, path, querys)); + for (Map.Entry e : headers.entrySet()) { + request.addHeader(e.getKey(), e.getValue()); + } + + if (body != null) { + request.setEntity(new ByteArrayEntity(body)); + } + + return httpClient.execute(request); + } + + + public static HttpResponse doDelete(String host, String path, String method, Map headers, Map querys) throws Exception { + HttpClient httpClient = wrapClient(host); + + HttpDelete request = new HttpDelete(buildUrl(host, path, querys)); + for (Map.Entry e : headers.entrySet()) { + request.addHeader(e.getKey(), e.getValue()); + } + + return httpClient.execute(request); + } + + private static String buildUrl(String host, String path, Map querys) throws UnsupportedEncodingException { + StringBuilder sbUrl = new StringBuilder(); + sbUrl.append(host); + if (!StringUtils.isBlank(path)) { + sbUrl.append(path); + } + if (null != querys) { + StringBuilder sbQuery = new StringBuilder(); + for (Map.Entry query : querys.entrySet()) { + if (!sbQuery.isEmpty()) { + sbQuery.append("&"); + } + if (StringUtils.isBlank(query.getKey()) && !StringUtils.isBlank(query.getValue())) { + sbQuery.append(query.getValue()); + } + if (!StringUtils.isBlank(query.getKey())) { + sbQuery.append(query.getKey()); + if (!StringUtils.isBlank(query.getValue())) { + sbQuery.append("="); + sbQuery.append(URLEncoder.encode(query.getValue(), StandardCharsets.UTF_8)); + } + } + } + if (!sbQuery.isEmpty()) { + sbUrl.append("?").append(sbQuery); + } + } + + return sbUrl.toString(); + } + + private static HttpClient wrapClient(String host) { + HttpClient httpClient = new DefaultHttpClient(); + if (host.startsWith("https://")) { + sslClient(httpClient); + } + + return httpClient; + } + + private static void sslClient(HttpClient httpClient) { + try { + SSLContext ctx = SSLContext.getInstance("TLS"); + X509TrustManager tm = new X509TrustManager() { + public X509Certificate[] getAcceptedIssuers() { + return null; + } + + public void checkClientTrusted(X509Certificate[] xcs, String str) { + } + + public void checkServerTrusted(X509Certificate[] xcs, String str) { + } + }; + ctx.init(null, new TrustManager[]{tm}, null); + SSLSocketFactory ssf = new SSLSocketFactory(ctx); + ssf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); + ClientConnectionManager ccm = httpClient.getConnectionManager(); + SchemeRegistry registry = ccm.getSchemeRegistry(); + registry.register(new Scheme("https", 443, ssf)); + } catch (Exception ex) { + throw new RuntimeException(); + } + } +} \ No newline at end of file diff --git a/netty/common/service-utils/src/main/java/cn/bunny/common/service/utils/JwtHelper.java b/netty/common/service-utils/src/main/java/cn/bunny/common/service/utils/JwtHelper.java new file mode 100644 index 0000000..cbce3e2 --- /dev/null +++ b/netty/common/service-utils/src/main/java/cn/bunny/common/service/utils/JwtHelper.java @@ -0,0 +1,50 @@ +package cn.bunny.common.service.utils; + +import io.jsonwebtoken.*; +import org.springframework.util.StringUtils; + +import java.util.Date; + +public class JwtHelper { + private static final long tokenExpiration = 24 * 60 * 60 * 1000; + private static final String tokenSignKey = "Bunny-Java-Template"; + + public static String createToken(Long userId, String userName, Integer day) { + return Jwts.builder() + .setSubject("Bunny-USER") + .setExpiration(new Date(System.currentTimeMillis() + tokenExpiration * day)) + .claim("userId", userId) + .claim("userName", userName) + .signWith(SignatureAlgorithm.HS256, tokenSignKey) + .compressWith(CompressionCodecs.GZIP) + .compact(); + } + + public static Long getUserId(String token) { + if (!StringUtils.hasText(token)) return null; + + Jws claimsJws = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token); + Claims claims = claimsJws.getBody(); + + return Long.valueOf(String.valueOf(claims.get("userId"))); + } + + public static String getUsername(String token) { + if (!StringUtils.hasText(token)) return ""; + + Jws claimsJws = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token); + Claims claims = claimsJws.getBody(); + return (String) claims.get("userName"); + } + + public static void removeToken(String token) { + // jwttoken无需删除,客户端扔掉即可。 + } + + public static void main(String[] args) { + String token = JwtHelper.createToken(7L, "admin", 7); + // token = "eyJhbGciOiJIUzI1NiIsInppcCI6IkdaSVAifQ.H4sIAAAAAAAA_6tWKi5NUrJScirNy6vUDQ12DVLSUUqtKFCyMjQ3MTc0NrYwNddRKi1OLfJMUbKyNDIwNLQwMDAzg4j5JeamAjUbGhtaWhoYGJqaOBQW6iXn5yrVAgCrO9jLWAAAAA.DS1wYprXGoIMrjtUWfDSN9AG5gWoRZ17oAgcvC0kwag"; + System.out.println(JwtHelper.getUserId(token)); + System.out.println(JwtHelper.getUsername(token)); + } +} \ No newline at end of file diff --git a/netty/common/service-utils/src/main/java/cn/bunny/common/service/utils/ResponseUtil.java b/netty/common/service-utils/src/main/java/cn/bunny/common/service/utils/ResponseUtil.java new file mode 100644 index 0000000..2f3f1f2 --- /dev/null +++ b/netty/common/service-utils/src/main/java/cn/bunny/common/service/utils/ResponseUtil.java @@ -0,0 +1,26 @@ +package cn.bunny.common.service.utils; + +import cn.bunny.result.Result; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.http.HttpStatus; + +import java.io.IOException; + +public class ResponseUtil { + + public static void out(HttpServletResponse response, Result result) { + ObjectMapper mapper = new ObjectMapper(); + + // 注册JavaTimeModule模块 + mapper.registerModule(new JavaTimeModule()); + response.setContentType("application/json;charset=UTF-8"); + response.setStatus(HttpStatus.OK.value()); + try { + mapper.writeValue(response.getWriter(), result); + } catch (IOException e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/netty/common/service-utils/src/main/java/cn/bunny/common/service/utils/SnowflakeIdGenerator.java b/netty/common/service-utils/src/main/java/cn/bunny/common/service/utils/SnowflakeIdGenerator.java new file mode 100644 index 0000000..8504b27 --- /dev/null +++ b/netty/common/service-utils/src/main/java/cn/bunny/common/service/utils/SnowflakeIdGenerator.java @@ -0,0 +1,149 @@ +package cn.bunny.common.service.utils; + + +import cn.bunny.common.service.properties.SnowflakeProperties; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; + +@Component +public class SnowflakeIdGenerator { + // 数据中心id + private final long datacenterId; + // 数据中心id位数 + private final long datacenterBits; + // 机器id + private final long workerId; + // 机器id位数 + private final long workerBits; + // 序列id所占位数 + private final long sequenceBits; + // 时间戳起始点(毫秒) + private final long twepoch; + + // 数据中心最大id + private final long maxDatacenterId; + // 机器最大id + private final long maxWorkerId; + // 最大序列号 + private final long maxSequence; + + // 机器id左移位数 + private final long workerIdShift; + // 数据中心id左移位数 + private final long datacenterIdShift; + // 毫秒数左移位数 + private final long timestampLeftShift; + + // 单次批量生成id的最大数量 + private final int maxBatchCount; + + // 序列号 + private long sequence = 0L; + // 上一次时间戳 + private long lastTimestamp = -1L; + + public SnowflakeIdGenerator(SnowflakeProperties properties) { + // 数据中心id + this.datacenterId = properties.getDatacenterId(); + // 数据中心id位数 + this.datacenterBits = properties.getDatacenterBits(); + // 机器id + this.workerId = properties.getWorkerId(); + // 机器id位数 + this.workerBits = properties.getWorkerBits(); + // 序列id所占位数 + this.sequenceBits = properties.getSequenceBits(); + // 时间戳起始点(毫秒) + this.twepoch = properties.getTwepoch(); + // 数据中心最大id + this.maxDatacenterId = -1L ^ (-1L << properties.getDatacenterBits()); + // 机器最大id + this.maxWorkerId = -1L ^ (-1L << properties.getWorkerBits()); + // 最大序列号 + this.maxSequence = -1L ^ (-1L << properties.getSequenceBits()); + + this.workerIdShift = properties.getSequenceBits(); + // 数据中心id左移位数 + this.datacenterIdShift = properties.getSequenceBits() + properties.getWorkerBits(); + // 毫秒数左移位数 + this.timestampLeftShift = properties.getSequenceBits() + properties.getWorkerBits() + properties.getSequenceBits(); + // 单次批量生成id的最大数量 + this.maxBatchCount = properties.getMaxBatchCount(); + + // 校验datacenterId和workerId是否超出最大值 + if (datacenterId > maxDatacenterId || datacenterId < 0) { + throw new IllegalArgumentException(String.format("数据中心Id不能大于%d或小于0", maxDatacenterId)); + } + if (workerId > maxWorkerId || workerId < 0) { + throw new IllegalArgumentException(String.format("机器Id不能大于%d或小于0", maxWorkerId)); + } + } + + /** + * id生成方法(单个) + */ + public synchronized long nextId() { + // 获取当前时间的毫秒数 + long timestamp = currentTime(); + + // 判断时钟是否回拨 + if (timestamp < lastTimestamp) { + throw new RuntimeException(String.format("时钟回拨,回拨毫秒数:%d", lastTimestamp - timestamp)); + } + + // 设置序列号 + if (lastTimestamp == timestamp) { + // 设置序列号递增,如果当前毫秒内序列号已经达到最大值,则直到下一毫秒在重新从0开始计算序列号 + sequence = (sequence + 1) & maxSequence; + if (sequence == 0) { + timestamp = tilNextMillis(lastTimestamp); + } + } else { + sequence = 0L; + } + lastTimestamp = timestamp; + + // 计算id + return ((timestamp - twepoch) << timestampLeftShift) | + (datacenterId << datacenterIdShift) | + (workerId << workerIdShift) | + sequence; + } + + + /** + * id生成方法(批量) + */ + public synchronized List nextIds(int count) { + if (count > maxBatchCount || count < 0) { + throw new IllegalArgumentException(String.format("批量生成id的数量不能大于%d或小于0", maxBatchCount)); + } + List ids = new ArrayList<>(count); + for (int i = 0; i < count; i++) { + ids.add(nextId()); + } + return ids; + } + + /** + * 循环等待直至获取到新的毫秒时间戳 + * 确保生成的时间戳总是向前移动的,即使在相同的毫秒内请求多个ID时也能保持唯一性。 + */ + private long tilNextMillis(long lastTimestamp) { + long timestamp = currentTime(); + // 循环等待直至获取到新的毫秒时间戳 + while (timestamp <= lastTimestamp) { + timestamp = currentTime(); + } + return timestamp; + } + + /** + * 获取当前时间的毫秒数 + */ + private long currentTime() { + return System.currentTimeMillis(); + } +} \ No newline at end of file diff --git a/netty/model/pom.xml b/netty/model/pom.xml new file mode 100644 index 0000000..33eccf6 --- /dev/null +++ b/netty/model/pom.xml @@ -0,0 +1,80 @@ + + 4.0.0 + + cn.bunny + bunny-template + 0.0.1-SNAPSHOT + + + model + jar + + model + https://maven.apache.org + + + UTF-8 + + + + + + org.projectlombok + lombok + + + + + cn.hutool + hutool-all + + + + com.alibaba.fastjson2 + fastjson2 + + + + mysql + mysql-connector-java + + + + com.zaxxer + HikariCP + 5.1.0 + + + + com.baomidou + mybatis-plus-spring-boot3-starter + + + + com.github.xiaoymin + knife4j-openapi3-jakarta-spring-boot-starter + + + + org.springframework.boot + spring-boot-starter-security + + + + org.springframework.security + spring-security-test + + + + org.springframework.boot + spring-boot-starter-web + + + + io.swagger + swagger-annotations + 1.6.14 + + + diff --git a/netty/model/src/main/java/cn/bunny/dto/article/LoadArticleListDto.java b/netty/model/src/main/java/cn/bunny/dto/article/LoadArticleListDto.java new file mode 100644 index 0000000..c3b5e3b --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/dto/article/LoadArticleListDto.java @@ -0,0 +1,22 @@ +package cn.bunny.dto.article; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Objects; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class LoadArticleListDto { + private Integer pBoardId; + private Integer boardId; + private Integer orderType = 1; + + public void getOrderType(Integer orderType) { + this.orderType = Objects.requireNonNullElse(orderType, 1); + } +} diff --git a/netty/model/src/main/java/cn/bunny/dto/article/LoadUserArticleDto.java b/netty/model/src/main/java/cn/bunny/dto/article/LoadUserArticleDto.java new file mode 100644 index 0000000..a644f4f --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/dto/article/LoadUserArticleDto.java @@ -0,0 +1,15 @@ +package cn.bunny.dto.article; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class LoadUserArticleDto { + private Long userId; + private Integer type; +} diff --git a/netty/model/src/main/java/cn/bunny/dto/common/CommentPostDto.java b/netty/model/src/main/java/cn/bunny/dto/common/CommentPostDto.java new file mode 100644 index 0000000..18a0ec4 --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/dto/common/CommentPostDto.java @@ -0,0 +1,19 @@ +package cn.bunny.dto.common; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.web.multipart.MultipartFile; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class CommentPostDto { + private String articleId; + private Integer pCommentId; + private String content; + private MultipartFile image; + private Long replyUserId; +} diff --git a/netty/model/src/main/java/cn/bunny/dto/common/CommentQueryDto.java b/netty/model/src/main/java/cn/bunny/dto/common/CommentQueryDto.java new file mode 100644 index 0000000..2ad59bc --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/dto/common/CommentQueryDto.java @@ -0,0 +1,16 @@ +package cn.bunny.dto.common; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class CommentQueryDto { + private String articleId; + // time 时间查询、goods 点赞数量 + private String orderType; +} diff --git a/netty/model/src/main/java/cn/bunny/dto/common/QueryTopTypeDto.java b/netty/model/src/main/java/cn/bunny/dto/common/QueryTopTypeDto.java new file mode 100644 index 0000000..843a9de --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/dto/common/QueryTopTypeDto.java @@ -0,0 +1,15 @@ +package cn.bunny.dto.common; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class QueryTopTypeDto { + private Long commentId; + private Integer topType; +} diff --git a/netty/model/src/main/java/cn/bunny/dto/email/EmailTemplateDto.java b/netty/model/src/main/java/cn/bunny/dto/email/EmailTemplateDto.java new file mode 100644 index 0000000..734a1c0 --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/dto/email/EmailTemplateDto.java @@ -0,0 +1,21 @@ +package cn.bunny.dto.email; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class EmailTemplateDto { + // 模板名称 + private String templateName; + // 主题 + private String subject; + // 邮件内容 + private String body; + // 邮件类型 + private String type; +} diff --git a/netty/model/src/main/java/cn/bunny/dto/email/EmailUsersDto.java b/netty/model/src/main/java/cn/bunny/dto/email/EmailUsersDto.java new file mode 100644 index 0000000..be3b1b4 --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/dto/email/EmailUsersDto.java @@ -0,0 +1,30 @@ +package cn.bunny.dto.email; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 添加邮箱用户 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class EmailUsersDto { + // 修改时需要传 + private Long id; + // 邮箱 + private String email; + // 密码 + private String password; + // SMTP服务器 + private String host; + // 端口号 + private Integer port; + // 邮箱协议 + private Integer smtpAgreement; + // 是否为默认邮件 + private Boolean isDefault; +} diff --git a/netty/model/src/main/java/cn/bunny/dto/user/LoginDto.java b/netty/model/src/main/java/cn/bunny/dto/user/LoginDto.java new file mode 100644 index 0000000..1f4213b --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/dto/user/LoginDto.java @@ -0,0 +1,19 @@ +package cn.bunny.dto.user; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class LoginDto { + // 用户名 + private String username; + // 密码 + private String password; + // 邮箱验证码 + private String emailCode; +} diff --git a/netty/model/src/main/java/cn/bunny/dto/user/RegisterDto.java b/netty/model/src/main/java/cn/bunny/dto/user/RegisterDto.java new file mode 100644 index 0000000..5a15b4d --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/dto/user/RegisterDto.java @@ -0,0 +1,17 @@ +package cn.bunny.dto.user; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class RegisterDto { + private String email; + private String nickName; + private String password; + private String code; +} diff --git a/netty/model/src/main/java/cn/bunny/dto/user/ResetPwdDto.java b/netty/model/src/main/java/cn/bunny/dto/user/ResetPwdDto.java new file mode 100644 index 0000000..490dbbc --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/dto/user/ResetPwdDto.java @@ -0,0 +1,16 @@ +package cn.bunny.dto.user; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class ResetPwdDto { + private String email; + private String password; + private String emailCode; +} diff --git a/netty/model/src/main/java/cn/bunny/dto/user/UserInfoDto.java b/netty/model/src/main/java/cn/bunny/dto/user/UserInfoDto.java new file mode 100644 index 0000000..4bbbc79 --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/dto/user/UserInfoDto.java @@ -0,0 +1,22 @@ +package cn.bunny.dto.user; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.web.multipart.MultipartFile; + +/** + * 修改用户信息 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class UserInfoDto { + private String nickName; + private String password; + private MultipartFile avatar; + private Integer sex; + private String personDescription; +} diff --git a/netty/model/src/main/java/cn/bunny/dto/user/UserIntegralRecordDto.java b/netty/model/src/main/java/cn/bunny/dto/user/UserIntegralRecordDto.java new file mode 100644 index 0000000..8320e0a --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/dto/user/UserIntegralRecordDto.java @@ -0,0 +1,15 @@ +package cn.bunny.dto.user; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class UserIntegralRecordDto { + private String startDate; + private String endDate; +} diff --git a/netty/model/src/main/java/cn/bunny/dto/user/UserMessageDto.java b/netty/model/src/main/java/cn/bunny/dto/user/UserMessageDto.java new file mode 100644 index 0000000..575bd73 --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/dto/user/UserMessageDto.java @@ -0,0 +1,14 @@ +package cn.bunny.dto.user; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class UserMessageDto { + private String type; +} diff --git a/netty/model/src/main/java/cn/bunny/entity/base/BaseEntity.java b/netty/model/src/main/java/cn/bunny/entity/base/BaseEntity.java new file mode 100644 index 0000000..451c19b --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/entity/base/BaseEntity.java @@ -0,0 +1,41 @@ +package cn.bunny.entity.base; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +@Data +public class BaseEntity implements Serializable { + + @TableId(value = "id", type = IdType.ASSIGN_ID) + @ApiModelProperty("唯一标识") + private Long id; + + @TableField("create_time") + @ApiModelProperty("创建时间") + private Date createTime; + + @TableField("update_time") + @ApiModelProperty("更新时间") + private Date updateTime; + + @TableField("update_user") + @ApiModelProperty("操作用户ID") + private Long updateUser; + + @TableLogic + @TableField("is_deleted") + @ApiModelProperty("是否被删除") + private Boolean isDeleted; + + @TableField(exist = false) + private Map param = new HashMap<>(); +} diff --git a/netty/model/src/main/java/cn/bunny/entity/system/admin/AdminPower.java b/netty/model/src/main/java/cn/bunny/entity/system/admin/AdminPower.java new file mode 100644 index 0000000..2544ab8 --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/entity/system/admin/AdminPower.java @@ -0,0 +1,58 @@ +package cn.bunny.entity.system.admin; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + *

+ * + *

+ * + * @author Bunny + * @since 2024-05-18 + */ +@Getter +@Setter +@Accessors(chain = true) +@TableName("admin_power") +@ApiModel(value = "AdminPower对象", description = "") +public class AdminPower implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @ApiModelProperty("权限ID") + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + @ApiModelProperty("权限名称") + private String powerName; + + @ApiModelProperty("权限编码") + private String powerCode; + + @ApiModelProperty("描述") + private String description; + + @ApiModelProperty("创建时间") + private LocalDateTime createTime; + + @ApiModelProperty("更新时间") + private LocalDateTime updateTime; + + @ApiModelProperty("更新用户") + private String updateUser; + + @ApiModelProperty("是否删除,0-未删除,1-已删除") + private Byte isDelete; +} diff --git a/netty/model/src/main/java/cn/bunny/entity/system/admin/AdminRole.java b/netty/model/src/main/java/cn/bunny/entity/system/admin/AdminRole.java new file mode 100644 index 0000000..59c36f4 --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/entity/system/admin/AdminRole.java @@ -0,0 +1,55 @@ +package cn.bunny.entity.system.admin; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + *

+ * + *

+ * + * @author Bunny + * @since 2024-05-18 + */ +@Getter +@Setter +@Accessors(chain = true) +@TableName("admin_role") +@ApiModel(value = "AdminRole对象", description = "") +public class AdminRole implements Serializable { + + private static final long serialVersionUID = 1L; + + @TableId(value = "id", type = IdType.AUTO) + private String id; + + @ApiModelProperty("角色名称") + private String roleName; + + @ApiModelProperty("描述") + private String description; + + @ApiModelProperty("角色代码") + private String roleCode; + + @ApiModelProperty("创建时间") + private LocalDateTime createTime; + + @ApiModelProperty("更新时间") + private LocalDateTime updateTime; + + @ApiModelProperty("操作用户") + private String updateUser; + + @ApiModelProperty("是否删除") + private Byte isDeleted; +} diff --git a/netty/model/src/main/java/cn/bunny/entity/system/admin/AdminRolePower.java b/netty/model/src/main/java/cn/bunny/entity/system/admin/AdminRolePower.java new file mode 100644 index 0000000..26370de --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/entity/system/admin/AdminRolePower.java @@ -0,0 +1,56 @@ +package cn.bunny.entity.system.admin; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + *

+ * + *

+ * + * @author Bunny + * @since 2024-05-18 + */ +@Getter +@Setter +@Accessors(chain = true) +@TableName("admin_role_power") +@ApiModel(value = "AdminRolePower对象", description = "") +public class AdminRolePower implements Serializable { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty("ID") + @TableId(value = "id", type = IdType.AUTO) + private String id; + + @ApiModelProperty("角色id") + private String roleId; + + @ApiModelProperty("权限id") + private String powerId; + + @ApiModelProperty("描述") + private String description; + + @ApiModelProperty("创建时间") + private LocalDateTime createTime; + + @ApiModelProperty("更新时间") + private LocalDateTime updateTime; + + @ApiModelProperty("更新用户") + private String updateUser; + + @ApiModelProperty("是否删除,0-未删除,1-已删除") + private Byte isDelete; +} diff --git a/netty/model/src/main/java/cn/bunny/entity/system/admin/AdminUserRole.java b/netty/model/src/main/java/cn/bunny/entity/system/admin/AdminUserRole.java new file mode 100644 index 0000000..78f954f --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/entity/system/admin/AdminUserRole.java @@ -0,0 +1,56 @@ +package cn.bunny.entity.system.admin; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + *

+ * + *

+ * + * @author Bunny + * @since 2024-05-18 + */ +@Getter +@Setter +@Accessors(chain = true) +@TableName("admin_user_role") +@ApiModel(value = "AdminUserRole对象", description = "") +public class AdminUserRole implements Serializable { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty("ID") + @TableId(value = "id", type = IdType.AUTO) + private String id; + + @ApiModelProperty("用户id") + private String userId; + + @ApiModelProperty("角色id") + private String roleId; + + @ApiModelProperty("描述") + private String description; + + @ApiModelProperty("创建时间") + private LocalDateTime createTime; + + @ApiModelProperty("更新时间") + private LocalDateTime updateTime; + + @ApiModelProperty("更新用户") + private String updateUser; + + @ApiModelProperty("是否删除,0-未删除,1-已删除") + private Byte isDelete; +} diff --git a/netty/model/src/main/java/cn/bunny/entity/system/admin/auth/AuthUserRole.java b/netty/model/src/main/java/cn/bunny/entity/system/admin/auth/AuthUserRole.java new file mode 100644 index 0000000..f1b3794 --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/entity/system/admin/auth/AuthUserRole.java @@ -0,0 +1,21 @@ +package cn.bunny.entity.system.admin.auth; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class AuthUserRole { + // 用户id + private Long userId; + // 角色id + private Long roleId; + // 角色代码 + private String roleCode; + // 描述 + private String roleDescription; +} diff --git a/netty/model/src/main/java/cn/bunny/entity/system/article/Article.java b/netty/model/src/main/java/cn/bunny/entity/system/article/Article.java new file mode 100644 index 0000000..fe9f84d --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/entity/system/article/Article.java @@ -0,0 +1,83 @@ +package cn.bunny.entity.system.article; + +import cn.bunny.entity.base.BaseEntity; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; + +/** + *

+ * 文章信息 + *

+ * + * @author Bunny + * @since 2024-05-17 + */ +@Getter +@Setter +@Accessors(chain = true) +@ApiModel(value = "Article对象", description = "文章信息") +public class Article extends BaseEntity implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @ApiModelProperty("板块ID") + private Integer boardId; + + @ApiModelProperty("板块名称") + private String boardName; + + @ApiModelProperty("父级板块ID") + private Integer pBoardId; + + @ApiModelProperty("父板块名称") + private String pBoardName; + + @ApiModelProperty("用户ID") + private Long userId; + + @ApiModelProperty("昵称") + private String nickName; + + @ApiModelProperty("最后登录ip地址") + private String userIpAddress; + + @ApiModelProperty("标题") + private String title; + + @ApiModelProperty("封面") + private String cover; + + @ApiModelProperty("内容") + private String content; + + @ApiModelProperty("0:富文本编辑器 1:markdown编辑器") + private Byte editorType; + + @ApiModelProperty("摘要") + private String summary; + + @ApiModelProperty("阅读数量") + private Integer readCount; + + @ApiModelProperty("点赞数") + private Integer goodCount; + + @ApiModelProperty("评论数") + private Integer commentCount; + + @ApiModelProperty("0未置顶 1:已置顶") + private Byte topType; + + @ApiModelProperty("0:没有附件 1:有附件") + private Byte attachmentType; + + @ApiModelProperty("-1已删除 0:待审核 1:已审核 ") + private Byte status; +} diff --git a/netty/model/src/main/java/cn/bunny/entity/system/article/ForumComment.java b/netty/model/src/main/java/cn/bunny/entity/system/article/ForumComment.java new file mode 100644 index 0000000..bb3b0ff --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/entity/system/article/ForumComment.java @@ -0,0 +1,67 @@ +package cn.bunny.entity.system.article; + +import cn.bunny.entity.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; + +/** + *

+ * 评论 + *

+ * + * @author Bunny + * @since 2024-05-17 + */ +@Getter +@Setter +@Accessors(chain = true) +@TableName("forum_comment") +@ApiModel(value = "ForumComment对象", description = "评论") +public class ForumComment extends BaseEntity implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @ApiModelProperty("父级评论ID") + private Long pCommentId; + + @ApiModelProperty("文章ID") + private String articleId; + + @ApiModelProperty("回复内容") + private String content; + + @ApiModelProperty("图片") + private String imgPath; + + @ApiModelProperty("用户ID") + private Long userId; + + @ApiModelProperty("昵称") + private String nickName; + + @ApiModelProperty("用户ip地址") + private String userIpAddress; + + @ApiModelProperty("回复人ID") + private Long replyUserId; + + @ApiModelProperty("回复人昵称") + private String replyNickName; + + @ApiModelProperty("0:未置顶 1:置顶") + private Integer topType; + + @ApiModelProperty("good数量") + private Integer goodCount; + + @ApiModelProperty("0:待审核 1:已审核") + private Integer status; +} diff --git a/netty/model/src/main/java/cn/bunny/entity/system/article/IntegralRecord.java b/netty/model/src/main/java/cn/bunny/entity/system/article/IntegralRecord.java new file mode 100644 index 0000000..b88f1e8 --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/entity/system/article/IntegralRecord.java @@ -0,0 +1,40 @@ +package cn.bunny.entity.system.article; + +import cn.bunny.entity.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; + +/** + *

+ * 用户积分记录表 + *

+ * + * @author Bunny + * @since 2024-05-17 + */ +@Getter +@Setter +@Accessors(chain = true) +@TableName("integral_record") +@ApiModel(value = "IntegralRecord对象", description = "用户积分记录表") +public class IntegralRecord extends BaseEntity implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @ApiModelProperty("用户ID") + private Long userId; + + @ApiModelProperty("操作类型") + private Byte operType; + + @ApiModelProperty("积分") + private Integer integral; +} diff --git a/netty/model/src/main/java/cn/bunny/entity/system/article/LikeRecord.java b/netty/model/src/main/java/cn/bunny/entity/system/article/LikeRecord.java new file mode 100644 index 0000000..8d27cb9 --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/entity/system/article/LikeRecord.java @@ -0,0 +1,43 @@ +package cn.bunny.entity.system.article; + +import cn.bunny.entity.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; + +/** + *

+ * 点赞记录 + *

+ * + * @author Bunny + * @since 2024-05-17 + */ +@Getter +@Setter +@Accessors(chain = true) +@TableName("like_record") +@ApiModel(value = "LikeRecord对象", description = "点赞记录") +public class LikeRecord extends BaseEntity implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @ApiModelProperty("操作类型0:文章点赞 1:评论点赞") + private Byte opType; + + @ApiModelProperty("主体ID") + private String objectId; + + @ApiModelProperty("用户ID") + private Long userId; + + @ApiModelProperty("主体作者ID") + private Long authorUserId; +} diff --git a/netty/model/src/main/java/cn/bunny/entity/system/board/ForumBoard.java b/netty/model/src/main/java/cn/bunny/entity/system/board/ForumBoard.java new file mode 100644 index 0000000..0fad48c --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/entity/system/board/ForumBoard.java @@ -0,0 +1,49 @@ +package cn.bunny.entity.system.board; + +import cn.bunny.entity.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; + +/** + *

+ * 文章板块信息 + *

+ * + * @author Bunny + * @since 2024-05-19 + */ +@Getter +@Setter +@Accessors(chain = true) +@TableName("forum_board") +@ApiModel(value = "ForumBoard对象", description = "文章板块信息") +public class ForumBoard extends BaseEntity implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @ApiModelProperty("父级板块ID") + private Long parentId; + + @ApiModelProperty("板块名") + private String boardName; + + @ApiModelProperty("封面") + private String cover; + + @ApiModelProperty("描述") + private String boardDesc; + + @ApiModelProperty("排序") + private Integer sort; + + @ApiModelProperty("0:只允许管理员发帖 1:任何人可以发帖") + private Boolean postType; +} diff --git a/netty/model/src/main/java/cn/bunny/entity/system/email/EmailSend.java b/netty/model/src/main/java/cn/bunny/entity/system/email/EmailSend.java new file mode 100644 index 0000000..1d2e779 --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/entity/system/email/EmailSend.java @@ -0,0 +1,29 @@ +package cn.bunny.entity.system.email; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.web.multipart.MultipartFile; + +/** + * 邮件发送对象 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class EmailSend { + // 给谁发送 + private String sendTo; + // 发送主题 + private String subject; + // 是否为富文本 + private Boolean isRichText; + // 发送内容 + private String message; + // 抄送人 + private String ccParam; + // 发送的文件 + private MultipartFile[] file; +} diff --git a/netty/model/src/main/java/cn/bunny/entity/system/email/EmailSendInit.java b/netty/model/src/main/java/cn/bunny/entity/system/email/EmailSendInit.java new file mode 100644 index 0000000..d2b3aa1 --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/entity/system/email/EmailSendInit.java @@ -0,0 +1,20 @@ +package cn.bunny.entity.system.email; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 邮箱发送初始化参数 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class EmailSendInit { + private Integer port; + private String host; + private String username; + private String password; +} diff --git a/netty/model/src/main/java/cn/bunny/entity/system/email/EmailTemplate.java b/netty/model/src/main/java/cn/bunny/entity/system/email/EmailTemplate.java new file mode 100644 index 0000000..18cb9c2 --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/entity/system/email/EmailTemplate.java @@ -0,0 +1,42 @@ +package cn.bunny.entity.system.email; + +import cn.bunny.entity.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; + +/** + *

+ * + *

+ * + * @author Bunny + * @since 2024-05-19 + */ +@Getter +@Setter +@Accessors(chain = true) +@TableName("email_template") +@ApiModel(value = "EmailTemplate对象", description = "邮件模板") +public class EmailTemplate extends BaseEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @ApiModelProperty("模板名称") + private String templateName; + + @ApiModelProperty("主题") + private String subject; + + @ApiModelProperty("邮件内容") + private String body; + + @ApiModelProperty("邮件类型") + private String type; +} diff --git a/netty/model/src/main/java/cn/bunny/entity/system/email/EmailUsers.java b/netty/model/src/main/java/cn/bunny/entity/system/email/EmailUsers.java new file mode 100644 index 0000000..def87bb --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/entity/system/email/EmailUsers.java @@ -0,0 +1,49 @@ +package cn.bunny.entity.system.email; + +import cn.bunny.entity.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; + +/** + *

+ * 邮箱发送表 + *

+ * + * @author Bunny + * @since 2024-05-17 + */ +@Getter +@Setter +@Accessors(chain = true) +@TableName("email_users") +@ApiModel(value = "EmailUsers对象", description = "邮箱发送表") +public class EmailUsers extends BaseEntity implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @ApiModelProperty("邮箱") + private String email; + + @ApiModelProperty("密码") + private String password; + + @ApiModelProperty("Host地址") + private String host; + + @ApiModelProperty("端口号") + private Integer port; + + @ApiModelProperty("邮箱协议") + private String smtpAgreement; + + @ApiModelProperty("是否为默认邮件") + private Integer isDefault; +} diff --git a/netty/model/src/main/java/cn/bunny/entity/system/file/Files.java b/netty/model/src/main/java/cn/bunny/entity/system/file/Files.java new file mode 100644 index 0000000..2597bb2 --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/entity/system/file/Files.java @@ -0,0 +1,50 @@ +package cn.bunny.entity.system.file; + +import cn.bunny.entity.base.BaseEntity; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; + +/** + *

+ * 用于存储文件信息的数据表 + *

+ * + * @author Bunny + * @since 2024-05-17 + */ +@Getter +@Setter +@Accessors(chain = true) +@ApiModel(value = "Files对象", description = "用于存储文件信息的数据表") +public class Files extends BaseEntity implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @ApiModelProperty("文章ID") + private Long articleId; + + @ApiModelProperty("文件的名称") + private String filename; + + @ApiModelProperty("文件在服务器上的存储路径") + private String filepath; + + @ApiModelProperty("文件的大小,以字节为单位") + private Long fileSize; + + @ApiModelProperty("文件的MIME类型") + private String fileType; + + @ApiModelProperty("创建用户") + private Long createUser; + + @ApiModelProperty("下载次数") + private Integer downloadCount; +} diff --git a/netty/model/src/main/java/cn/bunny/entity/system/user/User.java b/netty/model/src/main/java/cn/bunny/entity/system/user/User.java new file mode 100644 index 0000000..33c086d --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/entity/system/user/User.java @@ -0,0 +1,61 @@ +package cn.bunny.entity.system.user; + +import cn.bunny.entity.base.BaseEntity; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + *

+ * 用户信息 + *

+ * + * @author Bunny + * @since 2024-05-17 + */ +@EqualsAndHashCode(callSuper = true) +@Data +@Accessors(chain = true) +@ApiModel(value = "User对象", description = "用户信息") +public class User extends BaseEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + @ApiModelProperty("昵称") + private String nickName; + @ApiModelProperty("邮箱") + private String email; + @ApiModelProperty("密码") + private String password; + @ApiModelProperty("头像") + private String avatar; + @ApiModelProperty("0:女 1:男") + private Byte sex; + @ApiModelProperty("个人描述") + private String personDescription; + @ApiModelProperty("加入时间") + private LocalDateTime joinTime; + + @ApiModelProperty("最后登录时间") + private LocalDateTime lastLoginTime; + + @ApiModelProperty("最后登录IP") + private String lastLoginIp; + + @ApiModelProperty("最后登录ip地址") + private String lastLoginIpAddress; + + @ApiModelProperty("积分") + private Integer totalIntegral; + + @ApiModelProperty("当前积分") + private Integer currentIntegral; + + @ApiModelProperty("0:禁用 1:正常") + private Byte status; +} diff --git a/netty/model/src/main/java/cn/bunny/entity/system/user/UserMessage.java b/netty/model/src/main/java/cn/bunny/entity/system/user/UserMessage.java new file mode 100644 index 0000000..faa6cdf --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/entity/system/user/UserMessage.java @@ -0,0 +1,58 @@ +package cn.bunny.entity.system.user; + +import cn.bunny.entity.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; + +/** + *

+ * 用户消息 + *

+ * + * @author Bunny + * @since 2024-05-17 + */ +@Getter +@Setter +@Accessors(chain = true) +@TableName("user_message") +@ApiModel(value = "UserMessage对象", description = "用户消息") +public class UserMessage extends BaseEntity implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @ApiModelProperty("接收人用户ID") + private Long receivedUserId; + + @ApiModelProperty("文章ID") + private String articleId; + + @ApiModelProperty("文章标题") + private String articleTitle; + + @ApiModelProperty("评论ID") + private Integer commentId; + + @ApiModelProperty("发送人用户ID") + private Long sendUserId; + + @ApiModelProperty("发送人昵称") + private String sendNickName; + + @ApiModelProperty("sys:系统消息 reply:评论 likePost:文章点赞 likeComment:评论点赞 attachment:附件下载") + private String messageType; + + @ApiModelProperty("消息内容") + private String messageContent; + + @ApiModelProperty("1:未读 2:已读") + private Byte status; +} diff --git a/netty/model/src/main/java/cn/bunny/enums/OperationType.java b/netty/model/src/main/java/cn/bunny/enums/OperationType.java new file mode 100644 index 0000000..7e0999e --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/enums/OperationType.java @@ -0,0 +1,8 @@ +package cn.bunny.enums; + +/** + * 数据库操作类型 + */ +public enum OperationType { + UPDATE, INSERT +} diff --git a/netty/model/src/main/java/cn/bunny/result/Result.java b/netty/model/src/main/java/cn/bunny/result/Result.java new file mode 100644 index 0000000..6484684 --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/result/Result.java @@ -0,0 +1,173 @@ +package cn.bunny.result; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class Result { + // 状态码 + private Integer code; + // 返回消息 + private String message; + // 返回数据 + private T data; + + /** + * * 自定义返回体 + * + * @param data 返回体 + * @return Result + */ + protected static Result build(T data) { + Result result = new Result<>(); + result.setData(data); + return result; + } + + /** + * * 自定义返回体,使用ResultCodeEnum构建 + * + * @param body 返回体 + * @param codeEnum 返回状态码 + * @return Result + */ + public static Result build(T body, ResultCodeEnum codeEnum) { + Result result = build(body); + result.setCode(codeEnum.getCode()); + result.setMessage(codeEnum.getMessage()); + return result; + } + + /** + * * 自定义返回体 + * + * @param body 返回体 + * @param code 返回状态码 + * @param message 返回消息 + * @return Result + */ + public static Result build(T body, Integer code, String message) { + Result result = build(body); + result.setCode(code); + result.setMessage(message); + result.setData(null); + return result; + } + + /** + * * 操作成功 + * + * @return Result + */ + public static Result success() { + return success(null, ResultCodeEnum.SUCCESS); + } + + /** + * * 操作成功 + * + * @param data baseCategory1List + */ + public static Result success(T data) { + return build(data, ResultCodeEnum.SUCCESS); + } + + /** + * * 操作成功-状态码 + * + * @param codeEnum 状态码 + */ + public static Result success(ResultCodeEnum codeEnum) { + return success(null, codeEnum); + } + + /** + * * 操作成功-自定义返回数据和状态码 + * + * @param data 返回体 + * @param codeEnum 状态码 + */ + public static Result success(T data, ResultCodeEnum codeEnum) { + return build(data, codeEnum); + } + + /** + * * 操作失败-自定义返回数据和状态码 + * + * @param data 返回体 + * @param message 错误信息 + */ + public static Result success(T data, String message) { + return build(data, 200, message); + } + + /** + * * 操作失败-自定义返回数据和状态码 + * + * @param data 返回体 + * @param code 状态码 + * @param message 错误信息 + */ + public static Result success(T data, Integer code, String message) { + return build(data, code, message); + } + + /** + * * 操作失败 + */ + public static Result error() { + return Result.build(null); + } + + /** + * * 操作失败-自定义返回数据 + * + * @param data 返回体 + */ + public static Result error(T data) { + return build(data, ResultCodeEnum.FAIL); + } + + /** + * * 操作失败-状态码 + * + * @param codeEnum 状态码 + */ + public static Result error(ResultCodeEnum codeEnum) { + return build(null, codeEnum); + } + + /** + * * 操作失败-自定义返回数据和状态码 + * + * @param data 返回体 + * @param codeEnum 状态码 + */ + public static Result error(T data, ResultCodeEnum codeEnum) { + return build(data, codeEnum); + } + + /** + * * 操作失败-自定义返回数据和状态码 + * + * @param data 返回体 + * @param code 状态码 + * @param message 错误信息 + */ + public static Result error(T data, Integer code, String message) { + return build(data, code, message); + } + + /** + * * 操作失败-自定义返回数据和状态码 + * + * @param data 返回体 + * @param message 错误信息 + */ + public static Result error(T data, String message) { + return build(null, 500, message); + } +} diff --git a/netty/model/src/main/java/cn/bunny/result/ResultCodeEnum.java b/netty/model/src/main/java/cn/bunny/result/ResultCodeEnum.java new file mode 100644 index 0000000..d391b55 --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/result/ResultCodeEnum.java @@ -0,0 +1,54 @@ +package cn.bunny.result; + +import lombok.Getter; + +/** + * 统一返回结果状态信息类 + */ +@Getter +public enum ResultCodeEnum { + // 成功操作 200 + SUCCESS(200, "操作成功"), + SUCCESS_LOGOUT(200, "退出成功"), + EMAIL_CODE_REFRESH(200, "邮箱验证码已刷新"), + // 验证错误 201 + USERNAME_NOT_EMPTY(201, "用户名不能为空"), + PASSWORD_NOT_EMPTY(201, "密码不能为空"), + EMAIL_CODE_NOT_EMPTY(201, "邮箱验证码不能为空"), + SEND_EMAIL_CODE_NOT_EMPTY(201, "请先发送邮箱验证码"), + EMAIL_CODE_NOT_MATCHING(201, "邮箱验证码不匹配"), + LOGIN_ERROR(201, "账号或密码错误"), + LOGIN_ERROR_USERNAME_PASSWORD_NOT_EMPTY(201, "登录信息不能为空"), + // 数据相关 206 + ILLEGAL_REQUEST(206, "非法请求"), + REPEAT_SUBMIT(206, "重复提交"), + DATA_ERROR(206, "数据异常"), + // 身份过期 208 + LOGIN_AUTH(208, "请先登陆"), + AUTHENTICATION_EXPIRED(208, "身份验证过期"), + SESSION_EXPIRATION(208, "会话过期"), + // 封禁 209 + FAIL_NO_ACCESS_DENIED_USER_LOCKED(209, "该账户被封禁"), + THE_SAME_USER_HAS_LOGGED_IN(209, "相同用户已登录"), + // 提示错误 + URL_ENCODE_ERROR(216, "URL编码失败"), + ILLEGAL_CALLBACK_REQUEST_ERROR(217, "非法回调请求"), + FETCH_USERINFO_ERROR(219, "获取用户信息失败"), + // 无权访问 403 + FAIL_REQUEST_NOT_AUTH(403, "用户未认证"), + FAIL_NO_ACCESS_DENIED(403, "无权访问"), + FAIL_NO_ACCESS_DENIED_USER_OFFLINE(403, "用户强制下线"), + LOGGED_IN_FROM_ANOTHER_DEVICE(403, "没有权限访问"), + // 系统错误 500 + SERVICE_ERROR(500, "服务异常"), + FAIL(500, "失败"), + ; + + private final Integer code; + private final String message; + + ResultCodeEnum(Integer code, String message) { + this.code = code; + this.message = message; + } +} \ No newline at end of file diff --git a/netty/model/src/main/java/cn/bunny/result/constant/ExceptionConstant.java b/netty/model/src/main/java/cn/bunny/result/constant/ExceptionConstant.java new file mode 100644 index 0000000..7662b0e --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/result/constant/ExceptionConstant.java @@ -0,0 +1,52 @@ +package cn.bunny.result.constant; + +import lombok.Data; + + +@Data +public class ExceptionConstant { + public static final String UNKNOWN_Exception = "未知错误"; + public static final String TOKEN_IS_EMPTY = "token为空"; + public static final String DATA_IS_EMPTY = "数据为空"; + public static final String REQUEST_DATA_NOT_EMPTY_Exception = "请求参数为空"; + public static final String UPDATE_DTO_IS_NULL_Exception = "修改参数为空"; + public static final String ADD_DATA_IS_EMPTY_Exception = "添加数据为空"; + public static final String DELETE_ID_IS_NOT_EMPTY_Exception = "删除id不能为空"; + // 文章操作相关 + public static final String DO_LIKE_COMMENT_NOT_EXIST = "点赞内容不存在"; + public static final String REPLY_USER_EMPTY_EXCEPTION = "回复的用户不存在"; + public static final String REPLY_USER_ID_EMPTY_EXCEPTION = "回复的用户不能为空"; + public static final String MENU_IS_NOT_EXIST_Exception = "菜单不存在"; + public static final String POST_COMMENT_EMPTY_Exception = "评论内容不能为空"; + public static final String ARTICLE_ID_NOT_EMPTY_Exception = "文章id不能为空"; + public static final String UPDATE_ID_IS_NOT_EMPTY_Exception = "修改id不能为空"; + public static final String CANNOT_TOP_OTHER_USER = "不能操作此内容"; + public static final String ARTICLE_NOT_FOUND_EXCEPTION = "文章未找到"; + // 登录相关 + public static final String USER_TOKEN_OUT_OF_DATE_Exception = "用户登录过期"; + public static final String LOGIN_DTO_IS_EMPTY_Exception = "登录参数不能为空"; + public static final String LOGIN_FAILED_Exception = "登录失败"; + // 账号相关 + public static final String ACCOUNT_NOT_FOUND_Exception = "账号不存在"; + public static final String ACCOUNT_LOCKED_Exception = "账号被锁定"; + // 用户相关 + public static final String USER_NOT_LOGIN_Exception = "用户未登录"; + public static final String USERNAME_IS_EMPTY_Exception = "用户名不能为空"; + public static final String ALREADY_USER_Exception = "用户已存在"; + public static final String USER_NOT_FOUND_Exception = "用户不存在"; + // 密码相关 + public static final String PASSWORD_Exception = "密码错误"; + public static final String PASSWORD_NOT_EMPTY_Exception = "密码不能为空"; + public static final String OLD_PASSWORD_Exception = "旧密码不匹配"; + public static final String PASSWORD_EDIT_Exception = "密码修改失败"; + public static final String OLD_PASSWORD_SAME_NEW_PASSWORD_Exception = "旧密码与新密码相同"; + // 验证码错误 + public static final String PLEASE_SEND_EMAIL_CODE_Exception = "请先发送验证码"; + public static final String MESSAGE_CODE_NOT_PASS_Exception = "短信验证码未过期"; + public static final String MESSAGE_CODE_UNAUTHORIZED_Exception = "短信验证码未授权,请联系管理员"; + public static final String VERIFICATION_CODE_ERROR_Exception = "验证码错误"; + public static final String CAPTCHA_IS_EMPTY_Exception = "验证码不能为空"; + public static final String KEY_IS_EMPTY_Exception = "验证码key不能为空"; + public static final String VERIFICATION_CODE_DOES_NOT_MATCH_Exception = "验证码不匹配"; + public static final String VERIFICATION_CODE_IS_EMPTY_Exception = "验证码失效或不存在"; +} diff --git a/netty/model/src/main/java/cn/bunny/result/constant/FileMessageConstant.java b/netty/model/src/main/java/cn/bunny/result/constant/FileMessageConstant.java new file mode 100644 index 0000000..db799c7 --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/result/constant/FileMessageConstant.java @@ -0,0 +1,21 @@ +package cn.bunny.result.constant; + +import lombok.Data; + +@Data +public class FileMessageConstant { + public static final String DOWNLOAD_BUCKET_EXCEPTION = "下载文件失败"; + public static final String FILE_UPLOAD_EXCEPTION = "文件上传失败"; + public static final String BUCKET_EXISTS_EXCEPTION = "查询文化部对象失败"; + public static final String DELETE_BUCKET_EXCEPTION = "删除文件对象失败"; + public static final String FILE_IS_EMPTY = "文件信息为空"; + public static final String FILE_IS_NOT_EXITS = "文件信息为空"; + public static final String GET_BUCKET_EXCEPTION = "获取文件信息失败"; + public static final String QUERY_BUCKET_EXCEPTION = "查询文件信息失败"; + public static final String CREATE_BUCKET_EXCEPTION = "创建文件对象失败"; + public static final String UPDATE_BUCKET_EXCEPTION = "更新文件对象失败"; + public static final String COMPOSE_OBJECT_EXCEPTION = "对象错误"; + public static final String COPY_BUCKET_EXCEPTION = "复制文件内容失败"; + public static final String DISABLE_BUCKET_EXCEPTION = "禁用文件失败"; + public static final String ENABLE_BUCKET_EXCEPTION = "启用文件失败"; +} diff --git a/netty/model/src/main/java/cn/bunny/result/constant/LocalDateTimeConstant.java b/netty/model/src/main/java/cn/bunny/result/constant/LocalDateTimeConstant.java new file mode 100644 index 0000000..b0c4d40 --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/result/constant/LocalDateTimeConstant.java @@ -0,0 +1,11 @@ +package cn.bunny.result.constant; + +import lombok.Data; + +@Data +public class LocalDateTimeConstant { + public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd"; + public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm"; + public static final String DEFAULT_DATE_TIME_SECOND_FORMAT = "yyyy-MM-dd HH:mm:ss"; + public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss"; +} diff --git a/netty/model/src/main/java/cn/bunny/result/constant/MailMessageConstant.java b/netty/model/src/main/java/cn/bunny/result/constant/MailMessageConstant.java new file mode 100644 index 0000000..c2410ac --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/result/constant/MailMessageConstant.java @@ -0,0 +1,15 @@ +package cn.bunny.result.constant; + +import lombok.Data; + +/** + * 邮箱消息 + */ +@Data +public class MailMessageConstant { + public static final String EMPTY_SEND_OBJECT = "空发送对象"; + public static final String ADDRESS_NOT_NULL = "收件人不能为空"; + public static final String TITLE_NOT_NULL = "标题不能为空"; + public static final String SEND_MESSAGE_NOT_NULL = "发送消息不能为空"; + public static final String EMAIL_CONFIG_NOT_FOUND = "邮箱配置为空"; +} diff --git a/netty/model/src/main/java/cn/bunny/result/constant/RedisUserConstant.java b/netty/model/src/main/java/cn/bunny/result/constant/RedisUserConstant.java new file mode 100644 index 0000000..9772191 --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/result/constant/RedisUserConstant.java @@ -0,0 +1,67 @@ +package cn.bunny.result.constant; + +import lombok.Data; + +/** + * Redis用户前缀设置 + */ +@Data +public class RedisUserConstant { + // 管理员用户 + public static final String ADMIN_LOGIN_INFO_PREFIX = "ADMIN::LOGIN_INFO::"; + public static final String ADMIN_EMAIL_CODE_PREFIX = "ADMIN::EMAIL_CODE::"; + // 普通用户 + public static final String USER_LOGIN_INFO_PREFIX = "USER::LOGIN_INFO::"; + public static final String USER_EMAIL_CODE_PREFIX = "USER::EMAIL_CODE::"; + public static final String USER_DO_LIKE_PREFIX = "USER::doLike::"; + + /** + * * 管理员用户登录信息 + * + * @param adminUser 管理员用户 + * @return 登录信息key + */ + public static String getAdminLoginInfoPrefix(String adminUser) { + return ADMIN_LOGIN_INFO_PREFIX + adminUser; + } + + /** + * * 管理员用户邮箱验证码 + * + * @param adminUser 管理员用户 + * @return 管理员用户邮箱验证码key + */ + public static String getAdminUserEmailCodePrefix(String adminUser) { + return ADMIN_EMAIL_CODE_PREFIX + adminUser; + } + + /** + * * 用户登录信息 + * + * @param user 用户名 + * @return 登录信息key + */ + public static String getUserLoginInfoPrefix(String user) { + return USER_LOGIN_INFO_PREFIX + user; + } + + /** + * * 用户邮箱验证码 + * + * @param user 用户名 + * @return 用户邮箱验证码key + */ + public static String getUserEmailCodePrefix(String user) { + return USER_EMAIL_CODE_PREFIX + user; + } + + /** + * * 用户点赞操作 + * + * @param user 用户名 + * @return 用户点赞key + */ + public static String getUserDoLikePrefix(String user) { + return USER_DO_LIKE_PREFIX + user; + } +} diff --git a/netty/model/src/main/java/cn/bunny/result/constant/SQLAutoFillConstant.java b/netty/model/src/main/java/cn/bunny/result/constant/SQLAutoFillConstant.java new file mode 100644 index 0000000..6b09482 --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/result/constant/SQLAutoFillConstant.java @@ -0,0 +1,14 @@ +package cn.bunny.result.constant; + +import lombok.Data; + +/** + * 数据库中自动填充字段 + */ +@Data +public class SQLAutoFillConstant { + public static final String SET_CREATE_TIME = "setCreateTime"; + public static final String SET_UPDATE_TIME = "setUpdateTime"; + public static final String SET_CREATE_USER = "setCreateUser"; + public static final String SET_UPDATE_USER = "setUpdateUser"; +} diff --git a/netty/model/src/main/java/cn/bunny/result/constant/SecurityConstant.java b/netty/model/src/main/java/cn/bunny/result/constant/SecurityConstant.java new file mode 100644 index 0000000..a26c0b4 --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/result/constant/SecurityConstant.java @@ -0,0 +1,13 @@ +package cn.bunny.result.constant; + +import lombok.Data; + +import java.util.Arrays; +import java.util.List; + +@Data +public class SecurityConstant { + public static String[] annotations = {"/", "/test/**", "/diagram-viewer/**", "/editor-app/**", "/*.html", + "/*/*/noAuth/**", "/*/noAuth/**", "/favicon.ico", "/swagger-resources/**", "/webjars/**", "/v3/**", "/swagger-ui.html/**", "/doc.html"}; + public static List annotationsList = Arrays.asList(annotations); +} diff --git a/netty/model/src/main/java/cn/bunny/result/constant/StatusConstant.java b/netty/model/src/main/java/cn/bunny/result/constant/StatusConstant.java new file mode 100644 index 0000000..c530625 --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/result/constant/StatusConstant.java @@ -0,0 +1,14 @@ +package cn.bunny.result.constant; + +import lombok.Data; + +/** + * 状态常量,启用或者禁用 + */ +@Data +public class StatusConstant { + // 启用为1 + public static final Integer ENABLE = 1; + // 禁用为0 + public static final Integer DISABLE = 0; +} diff --git a/netty/model/src/main/java/cn/bunny/result/constant/UserConstant.java b/netty/model/src/main/java/cn/bunny/result/constant/UserConstant.java new file mode 100644 index 0000000..d9f4832 --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/result/constant/UserConstant.java @@ -0,0 +1,8 @@ +package cn.bunny.result.constant; + +import lombok.Data; + +@Data +public class UserConstant { + public static final String USER_AVATAR = "https://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132"; +} diff --git a/netty/model/src/main/java/cn/bunny/tree/AbstractTreeNode.java b/netty/model/src/main/java/cn/bunny/tree/AbstractTreeNode.java new file mode 100644 index 0000000..d5e7770 --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/tree/AbstractTreeNode.java @@ -0,0 +1,11 @@ +package cn.bunny.tree; + +import java.util.List; + +public interface AbstractTreeNode { + Long getId(); + + Long getParentId(); + + void setChildren(List children); +} diff --git a/netty/model/src/main/java/cn/bunny/tree/TreeBuilder.java b/netty/model/src/main/java/cn/bunny/tree/TreeBuilder.java new file mode 100644 index 0000000..f85d419 --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/tree/TreeBuilder.java @@ -0,0 +1,29 @@ +package cn.bunny.tree; + +import java.util.ArrayList; +import java.util.List; + +public class TreeBuilder { + + public List buildTree(List nodeList) { + List tree = new ArrayList<>(); + for (T node : nodeList) { + if (node.getParentId() == 0) { + node.setChildren(getChildren(node.getId(), nodeList)); + tree.add(node); + } + } + return tree; + } + + private List getChildren(Long nodeId, List nodeList) { + List children = new ArrayList<>(); + for (T node : nodeList) { + if (node.getParentId().equals(nodeId)) { + node.setChildren(getChildren(node.getId(), nodeList)); + children.add(node); + } + } + return children; + } +} diff --git a/netty/model/src/main/java/cn/bunny/vo/email/EmailTemplateVo.java b/netty/model/src/main/java/cn/bunny/vo/email/EmailTemplateVo.java new file mode 100644 index 0000000..8cc34d0 --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/vo/email/EmailTemplateVo.java @@ -0,0 +1,19 @@ +package cn.bunny.vo.email; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class EmailTemplateVo { + // 模板名称 + private String templateName; + // 主题 + private String subject; + // 邮件内容 + private String body; +} \ No newline at end of file diff --git a/netty/model/src/main/java/cn/bunny/vo/file/FileInfoVo.java b/netty/model/src/main/java/cn/bunny/vo/file/FileInfoVo.java new file mode 100644 index 0000000..349ccb1 --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/vo/file/FileInfoVo.java @@ -0,0 +1,32 @@ +package cn.bunny.vo.file; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 返回文件信息 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class FileInfoVo { + private Long id; + // 文章Id + private Long articleId; + // 文件的名称 + private String filename; + // 文件在服务器上的存储路径 + private String filepath; + // 文件的大小,以字节为单位 + private Long fileSize; + // 文件大小 + private String size; + // 文件的MIME类型 + private String fileType; + // 上传人昵称 + private String uploadNickname; + private Integer downloadCount; +} diff --git a/netty/model/src/main/java/cn/bunny/vo/page/PageResult.java b/netty/model/src/main/java/cn/bunny/vo/page/PageResult.java new file mode 100644 index 0000000..feb66b2 --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/vo/page/PageResult.java @@ -0,0 +1,27 @@ +package cn.bunny.vo.page; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.List; + +/** + * 封装分页查询结果 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class PageResult implements Serializable { + // 当前页 + private Integer pageNo; + // 每页记录数 + private Integer pageSize; + // 总记录数 + private long total; + // 当前页数据集合 + private List list; +} \ No newline at end of file diff --git a/netty/model/src/main/java/cn/bunny/vo/system/article/ForumArticleVo.java b/netty/model/src/main/java/cn/bunny/vo/system/article/ForumArticleVo.java new file mode 100644 index 0000000..6caaf01 --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/vo/system/article/ForumArticleVo.java @@ -0,0 +1,70 @@ +package cn.bunny.vo.system.article; + +import cn.bunny.vo.file.FileInfoVo; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class ForumArticleVo { + // 文章ID + private Long id; + // 板块ID + private Integer boardId; + // 父级板块ID + private Integer pBoardId; + // 板块名称 + private String boardName; + // 父板块名称 + private String pBoardName; + // 用户ID + private String userId; + // 昵称 + private String nickName; + // 最后登录ip地址 + private String userIpAddress; + // 标题 + private String title; + // 封面 + private String cover; + // 内容 + private String content; + // 0:富文本编辑器 1:markdown编辑器 + private Byte editorType; + // 摘要 + private String summary; + // 发布时间 + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @JsonSerialize(using = LocalDateTimeSerializer.class) + @JsonDeserialize(using = LocalDateTimeDeserializer.class) + private String createTime; + // 最后更新时间 + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @JsonSerialize(using = LocalDateTimeSerializer.class) + @JsonDeserialize(using = LocalDateTimeDeserializer.class) + private String updateTime; + // 阅读数量 + private Integer readCount; + // 点赞数 + private Integer goodCount; + // 评论数 + private Integer commentCount; + // 0未置顶 1:已置顶 + private Byte topType; + // 0:没有附件 1:有附件 + private Byte attachmentType; + // -1已删除 0:待审核 1:已审核 + private Byte status; + private List fileInfo; +} diff --git a/netty/model/src/main/java/cn/bunny/vo/system/board/ForumBoardVo.java b/netty/model/src/main/java/cn/bunny/vo/system/board/ForumBoardVo.java new file mode 100644 index 0000000..66c79e0 --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/vo/system/board/ForumBoardVo.java @@ -0,0 +1,29 @@ +package cn.bunny.vo.system.board; + +import cn.bunny.tree.AbstractTreeNode; +import lombok.Data; + +import java.util.List; + +@Data +public class ForumBoardVo implements AbstractTreeNode { + private Long id; + // 父级板块ID + private Long parentId; + // 板块名 + private String boardName; + // 封面 + private String cover; + // 描述 + private String boardDesc; + // 排序 + private Integer sort; + // 0:只允许管理员发帖 1:任何人可以发帖 + private Boolean postType; + private List children; + + @Override + public void setChildren(List children) { + this.children = (List) children; + } +} diff --git a/netty/model/src/main/java/cn/bunny/vo/system/comment/CommentVo.java b/netty/model/src/main/java/cn/bunny/vo/system/comment/CommentVo.java new file mode 100644 index 0000000..46585af --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/vo/system/comment/CommentVo.java @@ -0,0 +1,53 @@ +package cn.bunny.vo.system.comment; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; +import java.util.List; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class CommentVo { + // 评论ID + private Long id; + // 父级评论ID + private Integer pCommentId; + // 文章ID + private String articleId; + // 回复内容 + private String content; + // 图片 + private String imgPath; + // 用户ID + private Long userId; + // 昵称 + private String nickName; + // 用户ip地址 + private String userIpAddress; + // 回复人ID + private Long replyUserId; + // 回复人昵称 + private String replyNickName; + // 0:未置顶 1:置顶 + private Byte topType; + // 发布时间 + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @JsonSerialize(using = LocalDateTimeSerializer.class) + @JsonDeserialize(using = LocalDateTimeDeserializer.class) + private LocalDateTime postTime; + // good数量 + private Integer goodCount; + // 0:待审核 1:已审核 + private Byte status; + private List children; +} diff --git a/netty/model/src/main/java/cn/bunny/vo/system/login/LoginVo.java b/netty/model/src/main/java/cn/bunny/vo/system/login/LoginVo.java new file mode 100644 index 0000000..9bda0ae --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/vo/system/login/LoginVo.java @@ -0,0 +1,48 @@ +package cn.bunny.vo.system.login; + +import cn.bunny.result.constant.LocalDateTimeConstant; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * 用户登录返回内容 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class LoginVo { + private Long id; + private String nickName; + private String email; + private String password; + private String avatar; + private Byte sex; + private String personDescription; + @JsonFormat(pattern = LocalDateTimeConstant.DEFAULT_DATE_TIME_SECOND_FORMAT) + @JsonSerialize(using = LocalDateTimeSerializer.class) + @JsonDeserialize(using = LocalDateTimeDeserializer.class) + private LocalDateTime joinTime; + @JsonFormat(pattern = LocalDateTimeConstant.DEFAULT_DATE_TIME_SECOND_FORMAT) + @JsonSerialize(using = LocalDateTimeSerializer.class) + @JsonDeserialize(using = LocalDateTimeDeserializer.class) + private LocalDateTime lastLoginTime; + private String lastLoginIp; + private String lastLoginIpAddress; + private Integer totalIntegral; + private Integer currentIntegral; + private Byte status; + private String token; + private List roleList; + private List powerList; +} diff --git a/netty/model/src/main/java/cn/bunny/vo/system/login/ValidateCodeVo.java b/netty/model/src/main/java/cn/bunny/vo/system/login/ValidateCodeVo.java new file mode 100644 index 0000000..21c7ae4 --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/vo/system/login/ValidateCodeVo.java @@ -0,0 +1,20 @@ +package cn.bunny.vo.system.login; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Schema(description = "验证码响应结果实体类") +public class ValidateCodeVo { + @Schema(description = "验证码key") + private String codeKey; // 验证码的key + + @Schema(description = "验证码value") + private String codeValue; // 图片验证码对应的字符串数据 +} \ No newline at end of file diff --git a/netty/model/src/main/java/cn/bunny/vo/system/user/UserInfoVo.java b/netty/model/src/main/java/cn/bunny/vo/system/user/UserInfoVo.java new file mode 100644 index 0000000..fa60a87 --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/vo/system/user/UserInfoVo.java @@ -0,0 +1,43 @@ +package cn.bunny.vo.system.user; + +import cn.bunny.result.constant.LocalDateTimeConstant; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +/** + * 获取用户信息返回参数 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class UserInfoVo { + private Long userId; + private String nickName; + private String email; + private String avatar; + private Byte sex; + private String personDescription; + @JsonFormat(pattern = LocalDateTimeConstant.DEFAULT_DATE_TIME_SECOND_FORMAT) + @JsonSerialize(using = LocalDateTimeSerializer.class) + @JsonDeserialize(using = LocalDateTimeDeserializer.class) + private LocalDateTime joinTime; + @JsonFormat(pattern = LocalDateTimeConstant.DEFAULT_DATE_TIME_SECOND_FORMAT) + @JsonSerialize(using = LocalDateTimeSerializer.class) + @JsonDeserialize(using = LocalDateTimeDeserializer.class) + private LocalDateTime lastLoginTime; + private String lastLoginIp; + private String lastLoginIpAddress; + private Integer totalIntegral; + private Integer currentIntegral; + private Byte status; +} diff --git a/netty/model/src/main/java/cn/bunny/vo/system/user/UserIntegralRecordVo.java b/netty/model/src/main/java/cn/bunny/vo/system/user/UserIntegralRecordVo.java new file mode 100644 index 0000000..9c33f60 --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/vo/system/user/UserIntegralRecordVo.java @@ -0,0 +1,33 @@ +package cn.bunny.vo.system.user; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class UserIntegralRecordVo { + // 记录ID + private Integer recordId; + // 用户ID + private String userId; + // 操作类型 + private Byte operType; + // 积分 + private Integer integral; + // 创建时间 + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @JsonSerialize(using = LocalDateTimeSerializer.class) + @JsonDeserialize(using = LocalDateTimeDeserializer.class) + private LocalDateTime createTime; +} diff --git a/netty/model/src/main/java/cn/bunny/vo/system/user/UserMessageCountVo.java b/netty/model/src/main/java/cn/bunny/vo/system/user/UserMessageCountVo.java new file mode 100644 index 0000000..f0baa0c --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/vo/system/user/UserMessageCountVo.java @@ -0,0 +1,19 @@ +package cn.bunny.vo.system.user; + + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class UserMessageCountVo { + private Integer total; + private Integer sys; + private Integer reply; + private Integer likePost; + private Integer likeComment; +} diff --git a/netty/model/src/main/java/cn/bunny/vo/system/user/UserMessageVo.java b/netty/model/src/main/java/cn/bunny/vo/system/user/UserMessageVo.java new file mode 100644 index 0000000..44c5da9 --- /dev/null +++ b/netty/model/src/main/java/cn/bunny/vo/system/user/UserMessageVo.java @@ -0,0 +1,43 @@ +package cn.bunny.vo.system.user; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class UserMessageVo { + // 自增ID + private Integer messageId; + // 接收人用户ID + private String receivedUserId; + // 文章ID + private String articleId; + // 文章标题 + private String articleTitle; + // 评论ID + private Integer commentId; + // 发送人用户ID + private String sendUserId; + // 发送人昵称 + private String sendNickName; + // sys:系统消息 reply:评论 likePost:文章点赞 likeComment:评论点赞 attachment:附件下载 + private String messageType; + // 消息内容 + private String messageContent; + // 创建时间 + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @JsonSerialize(using = LocalDateTimeSerializer.class) + @JsonDeserialize(using = LocalDateTimeDeserializer.class) + private LocalDateTime createTime; +} diff --git a/netty/module/module-mail/pom.xml b/netty/module/module-mail/pom.xml new file mode 100644 index 0000000..90242e5 --- /dev/null +++ b/netty/module/module-mail/pom.xml @@ -0,0 +1,26 @@ + + 4.0.0 + + cn.bunny + module + 0.0.1-SNAPSHOT + + + module-mail + jar + + module-mail + https://maven.apache.org + + + UTF-8 + + + + + org.springframework.boot + spring-boot-starter-mail + + + diff --git a/netty/module/module-mail/src/main/java/cn/bunny/module/mail/template-propties b/netty/module/module-mail/src/main/java/cn/bunny/module/mail/template-propties new file mode 100644 index 0000000..cc51c94 --- /dev/null +++ b/netty/module/module-mail/src/main/java/cn/bunny/module/mail/template-propties @@ -0,0 +1,23 @@ +Configuration example + +mail: + host: smtp.qq.com # 邮箱地址 + port: 465 # 邮箱端口号 + username: xxx@qq.com # 设置发送邮箱 + password: xx # 如果是纯数字要加引号 + default-encoding: UTF-8 # 设置编码格式 + protocol: smtps + properties: + mail: + debug: true # 是否开启debug模式发送邮件 + smtp: + auth: true + connectionTimeout: 5000 # 设置连接延迟 + timeout: 5000 # 延迟时间 + writeTimeout: 5000 # 写入邮箱延迟 + allow8BitMime: true + sendPartial: true + ssl: + enabled: true # 是否开启SSL连接 + socketFactory: + class: javax.net.ssl.SSLSocketFactory # 必要设置!!! \ No newline at end of file diff --git a/netty/module/module-mail/src/main/java/cn/bunny/module/mail/utils/MailSendCheckUtil.java b/netty/module/module-mail/src/main/java/cn/bunny/module/mail/utils/MailSendCheckUtil.java new file mode 100644 index 0000000..6829253 --- /dev/null +++ b/netty/module/module-mail/src/main/java/cn/bunny/module/mail/utils/MailSendCheckUtil.java @@ -0,0 +1,23 @@ +package cn.bunny.module.mail.utils; + +import cn.bunny.common.service.utils.EmptyUtil; +import cn.bunny.entity.system.email.EmailSend; +import cn.bunny.result.constant.MailMessageConstant; + +public class MailSendCheckUtil { + /** + * 检测发送对象是否为空的对象 + * + * @param emailSend 邮件发送对象 + */ + public static void check(EmailSend emailSend) { + // 空发送对象 + EmptyUtil.isEmpty(emailSend, MailMessageConstant.EMPTY_SEND_OBJECT); + // 收件人不能为空 + EmptyUtil.isEmpty(emailSend.getSendTo(), MailMessageConstant.ADDRESS_NOT_NULL); + // 标题不能为空 + EmptyUtil.isEmpty(emailSend.getSubject(), MailMessageConstant.TITLE_NOT_NULL); + // 发送消息不能为空 + EmptyUtil.isEmpty(emailSend.getMessage(), MailMessageConstant.SEND_MESSAGE_NOT_NULL); + } +} diff --git a/netty/module/module-mail/src/main/java/cn/bunny/module/mail/utils/MailSenderUtil.java b/netty/module/module-mail/src/main/java/cn/bunny/module/mail/utils/MailSenderUtil.java new file mode 100644 index 0000000..0975c97 --- /dev/null +++ b/netty/module/module-mail/src/main/java/cn/bunny/module/mail/utils/MailSenderUtil.java @@ -0,0 +1,167 @@ +package cn.bunny.module.mail.utils; + +import cn.bunny.entity.system.email.EmailSend; +import cn.bunny.entity.system.email.EmailSendInit; +import jakarta.mail.MessagingException; +import jakarta.mail.internet.MimeMessage; +import org.springframework.mail.SimpleMailMessage; +import org.springframework.mail.javamail.JavaMailSenderImpl; +import org.springframework.mail.javamail.MimeMessageHelper; +import org.springframework.web.multipart.MultipartFile; + +import java.util.Objects; + +public class MailSenderUtil { + private final String username; + private final JavaMailSenderImpl javaMailSender; + + /** + * 初始化构造函数进行当前类赋值 + */ + public MailSenderUtil(EmailSendInit emailSendInit) { + JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl(); + javaMailSender.setHost(emailSendInit.getHost()); + javaMailSender.setPort(emailSendInit.getPort()); + javaMailSender.setUsername(emailSendInit.getUsername()); + javaMailSender.setPassword(emailSendInit.getPassword()); + javaMailSender.setProtocol("smtps"); + javaMailSender.setDefaultEncoding("UTF-8"); + + this.username = emailSendInit.getUsername(); + this.javaMailSender = javaMailSender; + } + + /** + * 综合邮箱发送 + * + * @param emailSend 邮件消息 + */ + public void sendEmail(EmailSend emailSend) throws MessagingException { + MailSendCheckUtil.check(emailSend); + + // 创建 MimeMessage 对象用于发送邮件富文本或者附件 + MimeMessage message = javaMailSender.createMimeMessage(); + // 创建 MimeMessageHelper + MimeMessageHelper helper = new MimeMessageHelper(message, true); + + // 设置发送人 + helper.setFrom(username); + // 设置邮件接受者 + helper.setTo(emailSend.getSendTo()); + // 设置邮件主题 + helper.setSubject(emailSend.getSubject()); + // 设置发送消息 为富文本 + helper.setText(emailSend.getMessage(), emailSend.getIsRichText()); + // 设置抄送人 + helper.setCc(emailSend.getCcParam().split(",")); + // 邮件添加附件 + MultipartFile[] files = emailSend.getFile(); + for (MultipartFile file : files) { + helper.addAttachment(Objects.requireNonNull(file.getOriginalFilename()), file); + } + + // 发送邮件 + javaMailSender.send(message); + } + + /** + * 发送邮件-简单 + * + * @param emailSend 邮件消息 + */ + public void sendSimpleEmail(EmailSend emailSend) { + MailSendCheckUtil.check(emailSend); + // 创建邮件消息体 SimpleMailMessage 发送简单邮件 + SimpleMailMessage mailMessage = new SimpleMailMessage(); + + // 设置邮件发送人 + mailMessage.setFrom(username); + // 设置邮件接受者 + mailMessage.setTo(emailSend.getSendTo()); + // 设置邮件主题 + mailMessage.setSubject(emailSend.getSubject()); + // 设置邮件消息 + mailMessage.setText(emailSend.getMessage()); + + javaMailSender.send(mailMessage); + } + + /** + * 发送带附件邮件 + * + * @param emailSend 邮件消息 + */ + public void sendAttachmentEmail(EmailSend emailSend, MultipartFile file, boolean isRich) throws MessagingException { + MailSendCheckUtil.check(emailSend); + // 创建 MimeMessage 对象用户发送附件或者是富文本内容 + MimeMessage mailMessage = javaMailSender.createMimeMessage(); + // 创建 MimeMessageHelper + MimeMessageHelper helper = new MimeMessageHelper(mailMessage, true); + + // 奢姿邮件发送人 + helper.setFrom(username); + // 设置邮件接受者 + helper.setTo(emailSend.getSendTo()); + // 设置邮件消息 + helper.setText(emailSend.getMessage(), isRich); + // 设置邮件主题 + helper.setSubject(emailSend.getSubject()); + // 邮件添加附件 + helper.addAttachment(Objects.requireNonNull(file.getOriginalFilename()), file); + + // 发送邮件 + javaMailSender.send(mailMessage); + } + + /** + * 发送富文本邮件 + * + * @param emailSend 邮件消息 + */ + public void sendRichText(EmailSend emailSend, boolean isRich) throws MessagingException { + MailSendCheckUtil.check(emailSend); + // 创建 MimeMessage 对象用户发送附件或者是富文本内容 + MimeMessage mailMessage = javaMailSender.createMimeMessage(); + // 创建 MimeMessageHelper + MimeMessageHelper helper = new MimeMessageHelper(mailMessage, true); + + // 设置邮件发送者 + helper.setFrom(username); + // 设置邮件接受者 + helper.setTo(emailSend.getSendTo()); + // 设置邮件主题 + helper.setSubject(emailSend.getSubject()); + // 设置邮件富文本,后面跟true 表示HTML格式发送 + helper.setText(emailSend.getMessage(), isRich); + + // 发送邮件 + javaMailSender.send(mailMessage); + } + + /** + * 发送带抄送的邮件 + * + * @param emailSend 邮件消息 + */ + public void sendCC(EmailSend emailSend, boolean isRich) throws MessagingException { + MailSendCheckUtil.check(emailSend); + // 创建 MimeMessage 对象用于发送邮件富文本或者附件 + MimeMessage message = javaMailSender.createMimeMessage(); + // 创建 MimeMessageHelper + MimeMessageHelper helper = new MimeMessageHelper(message, true); + + // 设置发送人 + helper.setFrom(username); + // 设置邮件接受者 + helper.setTo(emailSend.getSendTo()); + // 设置邮件主题 + helper.setSubject(emailSend.getSubject()); + // 设置发送消息 为富文本 + helper.setText(emailSend.getMessage(), isRich); + // 设置抄送人 + helper.setCc(emailSend.getCcParam().split(",")); + + // 发送邮件 + javaMailSender.send(message); + } +} diff --git a/netty/module/module-minio/pom.xml b/netty/module/module-minio/pom.xml new file mode 100644 index 0000000..cdc48fe --- /dev/null +++ b/netty/module/module-minio/pom.xml @@ -0,0 +1,27 @@ + + 4.0.0 + + cn.bunny + module + 0.0.1-SNAPSHOT + + + module-minio + jar + + module-minio + https://maven.apache.org + + + UTF-8 + + + + + + io.minio + minio + + + diff --git a/netty/module/module-minio/src/main/java/cn/bunny/module/minio/properties/MinioProperties.java b/netty/module/module-minio/src/main/java/cn/bunny/module/minio/properties/MinioProperties.java new file mode 100644 index 0000000..8ee36f7 --- /dev/null +++ b/netty/module/module-minio/src/main/java/cn/bunny/module/minio/properties/MinioProperties.java @@ -0,0 +1,27 @@ +package cn.bunny.module.minio.properties; + +import io.minio.MinioClient; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +@ConfigurationProperties(prefix = "bunny.minio") +@ConditionalOnProperty(name = "bunny.minio.bucket-name")// 当属性有值时这个配置才生效 +@Data +@Slf4j +public class MinioProperties { + private String endpointUrl; + private String accessKey; + private String secretKey; + private String bucketName; + + @Bean + public MinioClient minioClient() { + log.info("注册MinioClient..."); + return MinioClient.builder().endpoint(endpointUrl).credentials(accessKey, secretKey).build(); + } +} diff --git a/netty/module/module-minio/src/main/java/cn/bunny/module/minio/utils/MinioUtil.java b/netty/module/module-minio/src/main/java/cn/bunny/module/minio/utils/MinioUtil.java new file mode 100644 index 0000000..8fc332d --- /dev/null +++ b/netty/module/module-minio/src/main/java/cn/bunny/module/minio/utils/MinioUtil.java @@ -0,0 +1,982 @@ +package cn.bunny.module.minio.utils; + +import cn.bunny.result.constant.FileMessageConstant; +import cn.bunny.common.service.exception.BunnyException; +import io.minio.*; +import io.minio.messages.*; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.io.InputStream; +import java.util.List; +import java.util.Map; + +/** + * Minio操作工具类 简化操作步骤 + * By:Bunny0212 + */ +@Component +@Slf4j +public class MinioUtil { + @Autowired + private MinioClient minioClient; + + /** + * 判断桶是否存在 + * + * @param bucketName 桶名称 + * @return 布尔值,是否存在 + */ + public boolean bucketExists(String bucketName) { + boolean found = false; + try { + found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build()); + return found; + } catch (Exception exception) { + log.error("判断桶是否存在 ------ 失败消息:{}", exception.getLocalizedMessage()); + exception.getStackTrace(); + } + throw new BunnyException(FileMessageConstant.BUCKET_EXISTS_EXCEPTION); + } + + /** + * 删除桶的加密配置 + * + * @param bucketName 桶名称 + */ + public void deleteBucketEncryption(String bucketName) { + try { + minioClient.deleteBucketEncryption(DeleteBucketEncryptionArgs.builder().build()); + log.info("删除桶的加密配置 ------ 成功"); + } catch (Exception exception) { + log.error("删除桶的加密配置 ------ 失败消息:{}", exception.getLocalizedMessage()); + exception.getStackTrace(); + } + throw new BunnyException(FileMessageConstant.DELETE_BUCKET_EXCEPTION); + } + + /** + * 删除桶的生命周期配置 + * + * @param bucketName 桶名称 + */ + public void deleteBucketLifecycle(String bucketName) { + try { + minioClient.deleteBucketLifecycle(DeleteBucketLifecycleArgs.builder().build()); + log.info("删除桶的生命周期配置 ------ 成功"); + } catch (Exception exception) { + log.error("删除桶的生命周期配置 ------ 失败消息:{}", exception.getLocalizedMessage()); + exception.getStackTrace(); + throw new BunnyException(FileMessageConstant.DELETE_BUCKET_EXCEPTION); + } + } + + /** + * 删除桶的通知配置 + * + * @param bucketName 桶的名称 + */ + public void deleteBucketNotification(String bucketName) { + try { + minioClient.deleteBucketNotification(DeleteBucketNotificationArgs.builder().bucket(bucketName).build()); + log.info("删除桶的通知配置 ------ 成功"); + } catch (Exception exception) { + log.error("删除桶的加密配置 ------ 失败消息:{}", exception.getLocalizedMessage()); + exception.getStackTrace(); + throw new BunnyException(FileMessageConstant.DELETE_BUCKET_EXCEPTION); + } + } + + /** + * 删除桶策略配置 + * + * @param bucketName 桶的名称 + */ + public void deleteBucketPolicy(String bucketName) { + try { + minioClient.deleteBucketPolicy(DeleteBucketPolicyArgs.builder().bucket(bucketName).build()); + log.info("删除桶中的对象锁配置 ------ 成功"); + } catch (Exception exception) { + log.error("删除桶的加密配置 ------ 失败消息:{}", exception.getLocalizedMessage()); + exception.getStackTrace(); + throw new BunnyException(FileMessageConstant.DELETE_BUCKET_EXCEPTION); + } + } + + /** + * 删除桶的桶复制配置 + * + * @param bucketName 桶的名称 + */ + public void deleteBucketReplication(String bucketName) { + try { + minioClient.deleteBucketReplication(DeleteBucketReplicationArgs.builder().bucket(bucketName).build()); + log.info("删除桶的桶复制配置 ------ 成功"); + } catch (Exception exception) { + log.error("删除桶的桶复制配置 ------ 失败消息:{}", exception.getLocalizedMessage()); + exception.getStackTrace(); + throw new BunnyException(FileMessageConstant.DELETE_BUCKET_EXCEPTION); + } + } + + /** + * 删除桶的标签 + * + * @param bucketName 桶的名称 + */ + public void deleteBucketTags(String bucketName) { + try { + minioClient.deleteBucketTags(DeleteBucketTagsArgs.builder().bucket(bucketName).build()); + log.info("删除桶的标签 ------ 成功"); + } catch (Exception exception) { + log.error("删除桶的桶复制配置 ------ 失败消息:{}", exception.getLocalizedMessage()); + exception.getStackTrace(); + throw new BunnyException(FileMessageConstant.DELETE_BUCKET_EXCEPTION); + } + } + + /** + * 删除桶中的对象锁配置 + * + * @param bucketName 桶的名称 + */ + public void deleteObjectLockConfiguration(String bucketName) { + try { + minioClient.deleteObjectLockConfiguration(DeleteObjectLockConfigurationArgs.builder().bucket(bucketName).build()); + log.info("删除桶中的对象锁配置 ------ 成功"); + } catch (Exception exception) { + log.error("删除桶中的对象锁配置 ------ 失败消息:{}", exception.getLocalizedMessage()); + exception.getStackTrace(); + throw new BunnyException(FileMessageConstant.DELETE_BUCKET_EXCEPTION); + } + } + + /** + * 获取存储桶的加密配置 + * + * @param bucketName 桶的名称 + * @return SseConfiguration 获取成功不为null + */ + public SseConfiguration getBucketEncryption(String bucketName) { + SseConfiguration conf = null; + + try { + conf = minioClient.getBucketEncryption(GetBucketEncryptionArgs.builder().bucket(bucketName).build()); + log.info("获取存储桶的加密配置 ------ 成功"); + } catch (Exception exception) { + log.error("获取存储桶的加密配置 ------ 失败消息:{}", exception.getLocalizedMessage()); + exception.getStackTrace(); + throw new BunnyException(FileMessageConstant.GET_BUCKET_EXCEPTION); + } + + return conf; + } + + /** + * 获取存储桶的生命周期配置 + * + * @param bucketName 桶的名称 + * @return LifecycleConfiguration 获取成功不为null + */ + public LifecycleConfiguration getBucketLifecycle(String bucketName) { + LifecycleConfiguration lifecycle = null; + try { + lifecycle = minioClient.getBucketLifecycle(GetBucketLifecycleArgs.builder().bucket(bucketName).build()); + log.info("获取存储桶的生命周期配置 ------ 成功"); + } catch (Exception exception) { + log.error("获取存储桶的生命周期配置 ------ 失败消息:{}", exception.getLocalizedMessage()); + exception.getStackTrace(); + throw new BunnyException(FileMessageConstant.GET_BUCKET_EXCEPTION); + } + + return lifecycle; + } + + /** + * 获取存储桶的通知配置 + * + * @param bucketName 桶的名称 + * @return NotificationConfiguration 获取成功不为null + */ + public NotificationConfiguration getBucketNotification(String bucketName) { + NotificationConfiguration configuration = null; + + try { + configuration = minioClient.getBucketNotification(GetBucketNotificationArgs.builder().bucket(bucketName).build()); + log.info("获取存储桶的通知配置 ------ 成功"); + } catch (Exception exception) { + log.error("获取存储桶的通知配置 ------ 失败消息:{}", exception.getLocalizedMessage()); + exception.getStackTrace(); + throw new BunnyException(FileMessageConstant.GET_BUCKET_EXCEPTION); + } + + return configuration; + } + + /** + * 获取桶的桶策略配置 + * + * @param bucketName 桶的名称 + * @return String 获取成功不为null + */ + public String getBucketPolicy(String bucketName) { + String config = null; + + try { + config = minioClient.getBucketPolicy(GetBucketPolicyArgs.builder().bucket(bucketName).build()); + log.info("获取桶的桶策略配置 ------ 成功"); + } catch (Exception exception) { + log.error("获取桶的桶策略配置 ------ 失败消息:{}", exception.getLocalizedMessage()); + exception.getStackTrace(); + throw new BunnyException(FileMessageConstant.GET_BUCKET_EXCEPTION); + } + + return config; + } + + /** + * 获取桶复制参数 + * + * @param bucketName 桶的名称 + * @return ReplicationConfiguration 获取成功不为null + */ + public ReplicationConfiguration getBucketReplication(String bucketName) { + ReplicationConfiguration configuration = null; + + try { + configuration = minioClient.getBucketReplication(GetBucketReplicationArgs.builder().bucket(bucketName).build()); + log.info("获取桶复制参数 ------ 成功"); + } catch (Exception exception) { + log.error("获取桶复制参数 ------ 失败消息:{}", exception.getLocalizedMessage()); + exception.getStackTrace(); + throw new BunnyException(FileMessageConstant.GET_BUCKET_EXCEPTION); + } + + return configuration; + } + + /** + * 获取存储桶的标签 + * + * @param bucketName 桶的名称 + * @return Tags 获取成功不为null + */ + public Tags getBucketTags(String bucketName) { + Tags tags = null; + + try { + tags = minioClient.getBucketTags(GetBucketTagsArgs.builder().bucket(bucketName).build()); + log.info("获取存储桶的标签 ------ 成功"); + } catch (Exception exception) { + log.error("获取存储桶的标签 ------ 失败消息:{}", exception.getLocalizedMessage()); + exception.getStackTrace(); + throw new BunnyException(FileMessageConstant.GET_BUCKET_EXCEPTION); + } + return tags; + } + + /** + * 获取存储桶的版本控制配置 + * + * @param bucketName 桶的名称 + * @return Tags 获取成功不为null + */ + public VersioningConfiguration getBucketVersioning(String bucketName) { + VersioningConfiguration configuration = null; + + try { + configuration = minioClient.getBucketVersioning(GetBucketVersioningArgs.builder().bucket(bucketName).build()); + log.info("获取存储桶的版本控制配置 ------ 成功"); + } catch (Exception exception) { + log.error("获取存储桶的版本控制配置 ------ 失败消息:{}", exception.getLocalizedMessage()); + exception.getStackTrace(); + throw new BunnyException(FileMessageConstant.GET_BUCKET_EXCEPTION); + } + + return configuration; + } + + /** + * 获取存储桶中的对象锁配置 + * + * @param bucketName 桶的名称 + * @return Tags 获取成功不为null + */ + public ObjectLockConfiguration getObjectLockConfiguration(String bucketName) { + ObjectLockConfiguration configuration = null; + try { + configuration = minioClient.getObjectLockConfiguration(GetObjectLockConfigurationArgs.builder().bucket(bucketName).build()); + log.info("获取存储桶中的对象锁配置 ------ 成功"); + } catch (Exception exception) { + log.error("获取存储桶中的对象锁配置 ------ 失败消息:{}", exception.getLocalizedMessage()); + exception.getStackTrace(); + throw new BunnyException(FileMessageConstant.GET_BUCKET_EXCEPTION); + } + + return configuration; + } + + /** + * 列出所有桶的桶信息 + * + * @return Tags 获取成功不为null + */ + public List listBuckets() { + List buckets = null; + + try { + buckets = minioClient.listBuckets(); + log.info("列出所有桶的桶信息 ------ 成功"); + } catch (Exception exception) { + log.error("列出所有桶的桶信息 ------ 失败消息:{}", exception.getLocalizedMessage()); + exception.getStackTrace(); + throw new BunnyException(FileMessageConstant.QUERY_BUCKET_EXCEPTION); + } + + return buckets; + } + + /** + * 创建桶 + * + * @param bucketName 桶的名称 + */ + public void makeBucket(String bucketName) { + try { + minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); + log.info("创建桶 ------ 成功"); + } catch (Exception exception) { + log.error("创建桶 ------ 失败消息:{}", exception.getLocalizedMessage()); + exception.getStackTrace(); + throw new BunnyException(FileMessageConstant.CREATE_BUCKET_EXCEPTION); + } + } + + /** + * 创建桶 + * + * @param bucketName 桶的名称 + */ + public void makeBucket(String bucketName, String region) { + try { + minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).region(region).build()); + log.info("创建桶 ------ 成功"); + } catch (Exception exception) { + log.error("创建桶 ------ 失败消息:{}", exception.getLocalizedMessage()); + exception.getStackTrace(); + throw new BunnyException(FileMessageConstant.CREATE_BUCKET_EXCEPTION); + } + } + + /** + * 创建桶 + * + * @param bucketName 桶的名称 + */ + public void makeBucket(String bucketName, String region, boolean objectLock) { + try { + minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).region(region).objectLock(objectLock).build()); + log.info("创建桶 ------ 成功"); + } catch (Exception exception) { + log.error("创建桶 ------ 失败消息:{}", exception.getLocalizedMessage()); + exception.getStackTrace(); + throw new BunnyException(FileMessageConstant.CREATE_BUCKET_EXCEPTION); + } + } + + /** + * 删除桶 + * + * @param bucketName 桶的名称 + */ + public void removeBucket(String bucketName) { + try { + minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); + log.info("删除桶 ------ 成功"); + } catch (Exception exception) { + log.error("删除桶 ------ 失败消息:{}", exception.getLocalizedMessage()); + exception.getStackTrace(); + throw new BunnyException(FileMessageConstant.DELETE_BUCKET_EXCEPTION); + } + } + + /** + * 设置桶的加密配置 + * + * @param bucketName 同名称 + * @param configuration 配置信息 + */ + public void setBucketEncryption(String bucketName, SseConfiguration configuration) { + try { + minioClient.setBucketEncryption(SetBucketEncryptionArgs.builder().bucket(bucketName).config(configuration).build()); + log.info("设置桶的加密配置 ------ 成功"); + } catch (Exception exception) { + log.error("设置桶的加密配置 ------ 失败消息:{}", exception.getLocalizedMessage()); + exception.getStackTrace(); + throw new BunnyException(FileMessageConstant.UPDATE_BUCKET_EXCEPTION); + } + } + + /** + * 设置桶策略 + * + * @param bucketName 同名称 + * @param policyJson 配置信息 + */ + public void setBucketPolicy(String bucketName, String policyJson) { + try { + minioClient.setBucketPolicy(SetBucketPolicyArgs.builder().bucket(bucketName).config(policyJson).build()); + log.info("查看桶策略 ------ 成功"); + } catch (Exception exception) { + log.error("查看桶策略 ------ 失败消息:{}", exception.getLocalizedMessage()); + exception.getStackTrace(); + throw new BunnyException(FileMessageConstant.UPDATE_BUCKET_EXCEPTION); + } + } + + /** + * 设置桶复制参数 + * + * @param bucketName 同名称 + * @param config 配置信息 + * @return 布尔值是否完成 + */ + public void setBucketReplication(String bucketName, ReplicationConfiguration config) { + try { + minioClient.setBucketReplication(SetBucketReplicationArgs.builder().bucket(bucketName).config(config).build()); + log.info("设置桶复制参数 ------ 成功"); + } catch (Exception exception) { + log.error("设置桶复制参数 ------ 失败消息:{}", exception.getLocalizedMessage()); + exception.getStackTrace(); + throw new BunnyException(FileMessageConstant.UPDATE_BUCKET_EXCEPTION); + } + } + + /** + * 设置/修改桶标签 + * + * @param bucketName 桶名称 + * @param map 标签集合 + */ + public void setBucketTags(String bucketName, Map map) { + try { + minioClient.setBucketTags(SetBucketTagsArgs.builder().bucket(bucketName).tags(map).build()); + log.info("设置/修改桶标签 ------ 成功"); + } catch (Exception exception) { + log.error("设置/修改桶标签 ------ 失败消息:{}", exception.getLocalizedMessage()); + exception.getStackTrace(); + throw new BunnyException(FileMessageConstant.UPDATE_BUCKET_EXCEPTION); + } + } + + /** + * 设置桶的版本配置 + * + * @param bucketName 同名称 + * @param configuration 配置信息 + */ + public void setBucketVersioning(String bucketName, VersioningConfiguration configuration) { + try { + minioClient.setBucketVersioning( + SetBucketVersioningArgs.builder().bucket(bucketName).config(configuration).build()); + log.info("设置桶的版本配置 ------ 成功"); + } catch (Exception exception) { + log.error("设置桶的版本配置 ------ 失败消息:{}", exception.getLocalizedMessage()); + exception.getStackTrace(); + throw new BunnyException(FileMessageConstant.UPDATE_BUCKET_EXCEPTION); + } + } + + /** + * 对象对象锁定配置 + * + * @param bucketName 同名称 + * @param configuration 配置信息 + */ + public void setObjectLockConfiguration(String bucketName, ObjectLockConfiguration configuration) { + try { + minioClient.setObjectLockConfiguration(SetObjectLockConfigurationArgs.builder().bucket(bucketName).config(configuration).build()); + log.info("对象对象锁定配置 ------ 成功"); + } catch (Exception exception) { + log.error("对象对象锁定配置 ------ 失败消息:{}", exception.getLocalizedMessage()); + exception.getStackTrace(); + throw new BunnyException(FileMessageConstant.UPDATE_BUCKET_EXCEPTION); + } + } + + /** + * 组合对象 + * + * @param bucketName 同名称 + * @param objectName 对象名称 + * @param sources 对象列表 + */ + public void composeObject(String bucketName, String objectName, List sources) { + try { + minioClient.composeObject(ComposeObjectArgs.builder().bucket(bucketName).object(objectName).sources(sources).build()); + log.info("组合对象 ------ 成功"); + } catch (Exception exception) { + log.error("组合对象 ------ 失败消息:{}", exception.getLocalizedMessage()); + exception.getStackTrace(); + throw new BunnyException(FileMessageConstant.COMPOSE_OBJECT_EXCEPTION); + } + } + + /** + * 组合对象 + * + * @param bucketName 同名称 + * @param objectName 对象名称 + * @param sources 对象列表 + * @param userMetadata 用户元数据 + */ + public void composeObject(String bucketName, String objectName, List sources, Map userMetadata) { + try { + minioClient.composeObject(ComposeObjectArgs.builder() + .bucket(bucketName) + .object(objectName) + .sources(sources) + .userMetadata(userMetadata).build()); + log.info("组合对象 ------ 成功"); + } catch (Exception exception) { + log.error("组合对象 ------ 失败消息:{}", exception.getLocalizedMessage()); + exception.getStackTrace(); + throw new BunnyException(FileMessageConstant.COMPOSE_OBJECT_EXCEPTION); + } + } + + /** + * 组合对象 + * + * @param bucketName 同名称 + * @param objectName 对象名称 + * @param sources 对象列表 + * @param userMetadata 用户元数据 + * @param sideEncryption 通过组合源对象列表 + */ + public void composeObject(String bucketName, String objectName, List sources, Map userMetadata, ServerSideEncryption sideEncryption) { + try { + minioClient.composeObject(ComposeObjectArgs.builder() + .bucket(bucketName) + .object(objectName) + .sources(sources) + .userMetadata(userMetadata) + .sse(sideEncryption).build()); + log.info("组合对象 ------ 成功"); + } catch (Exception exception) { + log.error("组合对象 ------ 失败消息:{}", exception.getLocalizedMessage()); + exception.getStackTrace(); + throw new BunnyException(FileMessageConstant.COMPOSE_OBJECT_EXCEPTION); + } + } + + /** + * 复制另一个桶中文件 + * + * @param bucketName 桶中名字 + * @param objectName 复制名称 + * @param sourceBucketName 复制到桶中名称 + * @param sourceObjectName 复制完成后名称 + */ + public void copyObject(String bucketName, String objectName, String sourceBucketName, String sourceObjectName) { + try { + minioClient.copyObject(CopyObjectArgs.builder() + .bucket(bucketName) + .object(objectName) + .source(CopySource.builder().bucket(sourceBucketName).object(sourceObjectName).build()) + .build()); + log.info("复制另一个桶中文件 ------ 成功"); + } catch (Exception exception) { + log.error("复制另一个桶中文件 ------ 失败消息:{}", exception.getLocalizedMessage()); + exception.getStackTrace(); + throw new BunnyException(FileMessageConstant.COPY_BUCKET_EXCEPTION); + } + } + + /** + * 复制另一个桶中文件 + * + * @param bucketName 桶中名字 + * @param objectName 复制名称 + * @param sourceBucketName 复制到桶中名称 + * @param sourceObjectName 复制完成后名称 + * @param sideEncryption 加密key + */ + public void copyObject(String bucketName, String objectName, String sourceBucketName, String sourceObjectName, ServerSideEncryption sideEncryption) { + try { + minioClient.copyObject(CopyObjectArgs.builder() + .bucket(bucketName) + .object(objectName) + .source(CopySource.builder().bucket(sourceBucketName).object(sourceObjectName).build()) + .sse(sideEncryption) + .build()); + log.info("复制另一个桶中文件 ------ 成功"); + } catch (Exception exception) { + log.error("复制另一个桶中文件 ------ 失败消息:{}", exception.getLocalizedMessage()); + exception.getStackTrace(); + throw new BunnyException(FileMessageConstant.COPY_BUCKET_EXCEPTION); + } + } + + /** + * 复制另一个桶中文件 + * + * @param bucketName 桶中名字 + * @param objectName 复制名称 + * @param sourceBucketName 复制到桶中名称 + * @param sourceObjectName 复制完成后名称 + * @param etag 标签 + * @param headers 头部信息 + */ + public void copyObject(String bucketName, String objectName, String sourceBucketName, String sourceObjectName, String etag, Map headers) { + try { + minioClient.copyObject(CopyObjectArgs.builder() + .bucket(bucketName) + .object(objectName) + .source(CopySource.builder().bucket(sourceBucketName).object(sourceObjectName).matchETag(etag).build()) + .headers(headers) + .build()); + log.info("复制另一个桶中文件 ------ 成功"); + } catch (Exception exception) { + log.error("复制另一个桶中文件 ------ 失败消息:{}", exception.getLocalizedMessage()); + exception.getStackTrace(); + throw new BunnyException(FileMessageConstant.COPY_BUCKET_EXCEPTION); + } + } + + /** + * 删除对象的标记 + * + * @param bucketName 桶中名字 + * @param objectName 复制名称 + */ + public void deleteObjectTags(String bucketName, String objectName) { + try { + minioClient.deleteObjectTags(DeleteObjectTagsArgs.builder().bucket(bucketName).object(objectName).build()); + log.info("删除对象的标记 ------ 成功"); + } catch (Exception exception) { + log.error("删除对象的标记 ------ 失败消息:{}", exception.getLocalizedMessage()); + exception.getStackTrace(); + throw new BunnyException(FileMessageConstant.DELETE_BUCKET_EXCEPTION); + } + } + + /** + * 禁用对象 + * + * @param bucketName 桶中名字 + * @param objectName 复制名称 + */ + public void disableObjectLegalHold(String bucketName, String objectName) { + try { + minioClient.disableObjectLegalHold(DisableObjectLegalHoldArgs.builder().bucket(bucketName).object(objectName).build()); + log.info("禁用对象 ------ 成功"); + } catch (Exception exception) { + log.error("禁用对象 ------ 失败消息:{}", exception.getLocalizedMessage()); + exception.getStackTrace(); + throw new BunnyException(FileMessageConstant.DISABLE_BUCKET_EXCEPTION); + } + } + + /** + * 启用文件对象 + * + * @param bucketName 桶中名字 + * @param objectName 复制名称 + */ + public void enableObjectLegalHold(String bucketName, String objectName) { + try { + minioClient.enableObjectLegalHold(EnableObjectLegalHoldArgs.builder().bucket(bucketName).object(objectName).build()); + log.info("禁用对象 ------ 成功"); + } catch (Exception exception) { + log.error("禁用对象 ------ 失败消息:{}", exception.getLocalizedMessage()); + exception.getStackTrace(); + throw new BunnyException(FileMessageConstant.ENABLE_BUCKET_EXCEPTION); + } + } + + /** + * 获取文件 + * + * @param bucketName 桶名称 + * @param objectName 对象名称 + */ + public InputStream getObject(String bucketName, String objectName) { + try { + GetObjectResponse object = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).build()); + log.info("获取文件 ------ 成功"); + return object; + } catch (Exception exception) { + log.error("获取文件 ------ 失败消息:{}", exception.getLocalizedMessage()); + exception.getStackTrace(); + throw new BunnyException(FileMessageConstant.GET_BUCKET_EXCEPTION); + } + } + + /** + * 下载文件 + * + * @param bucketName 桶名称 + * @param objectName 对象名称 + * @param filename 文件名 + */ + public void downloadObject(String bucketName, String objectName, String filename) { + try { + minioClient.downloadObject(DownloadObjectArgs.builder().bucket(bucketName).object(objectName).filename(filename).build()); + log.info("下载文件 ------ 成功"); + } catch (Exception exception) { + log.error("下载文件 ------ 失败消息:{}", exception.getLocalizedMessage()); + exception.getStackTrace(); + throw new BunnyException(FileMessageConstant.DOWNLOAD_BUCKET_EXCEPTION); + } + } + + /** + * 下载文件 + * + * @param bucketName 桶名称 + * @param objectName 对象名称 + * @param filename 文件名 + * @param sideEncryption 秘钥 + */ + public void downloadObject(String bucketName, String objectName, String filename, ServerSideEncryptionCustomerKey sideEncryption) { + try { + minioClient.downloadObject(DownloadObjectArgs.builder().bucket(bucketName).object(objectName).filename(filename).ssec(sideEncryption).build()); + log.info("下载文件 ------ 成功"); + } catch (Exception exception) { + log.error("下载文件 ------ 失败消息:{}", exception.getLocalizedMessage()); + exception.getStackTrace(); + throw new BunnyException(FileMessageConstant.DOWNLOAD_BUCKET_EXCEPTION); + } + } + + /** + * 获取对象保留 + * + * @param bucketName 桶名称 + * @param objectName 对象名称 + * @param versionId 版本ID + * @return 对象保留 + */ + public Retention getObjectRetention(String bucketName, String objectName, String versionId) { + try { + Retention retention = minioClient.getObjectRetention(GetObjectRetentionArgs.builder().bucket(bucketName).object(objectName).versionId(versionId).build()); + log.info("获取对象保留 ------ 成功"); + return retention; + } catch (Exception exception) { + log.error("获取对象保留 ------ 失败消息:{}", exception.getLocalizedMessage()); + exception.getStackTrace(); + throw new BunnyException(FileMessageConstant.DOWNLOAD_BUCKET_EXCEPTION); + } + } + + /** + * 获取对象标签 + * + * @param bucketName 桶名称 + * @param objectName 对象名称 + * @return 对象保留 + */ + public Tags getObjectTags(String bucketName, String objectName) { + try { + Tags tags = minioClient.getObjectTags(GetObjectTagsArgs.builder().bucket(bucketName).object(objectName).build()); + log.info("获取对象标签 ------ 成功"); + return tags; + } catch (Exception exception) { + log.error("获取对象标签 ------ 失败消息:{}", exception.getLocalizedMessage()); + exception.getStackTrace(); + throw new BunnyException(FileMessageConstant.GET_BUCKET_EXCEPTION); + } + } + + /** + * 上传文件 + * + * @param bucketName 桶名称 + * @param filename 文件名 + * @param inputStream 输入流 + * @param size 大小 + */ + public void putObject(String bucketName, String filename, InputStream inputStream, Long size) { + try { + minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(filename).stream(inputStream, size, -1).build()); + log.info("上传文件 ------ 成功"); + } catch (Exception exception) { + log.error("上传文件 ------ 失败消息:{}", exception.getLocalizedMessage()); + exception.getStackTrace(); + throw new BunnyException(FileMessageConstant.FILE_UPLOAD_EXCEPTION); + } + } + + /** + * 上传文件 + * + * @param bucketName 桶名称 + * @param filename 文件名 + * @param contentType 文件类型 + * @param inputStream 输入流 + * @param size 大小 + */ + public void putObject(String bucketName, String filename, String contentType, InputStream inputStream, Long size) { + try { + minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(filename).stream(inputStream, size, -1).contentType(contentType).build()); + log.info("上传文件 ------ 成功"); + } catch (Exception exception) { + log.error("上传文件 ------ 失败消息:{}", exception.getLocalizedMessage()); + exception.getStackTrace(); + throw new BunnyException(FileMessageConstant.FILE_UPLOAD_EXCEPTION); + } + } + + /** + * 上传文件 + * + * @param bucketName 桶名称 + * @param object 对象信息 + * @param filename 文件名 + * @param contentType 文件类型 + */ + public void putObject(String bucketName, String object, String filename, String contentType) { + try { + if (contentType != null) { + minioClient.uploadObject(UploadObjectArgs.builder().bucket(bucketName).object(filename).contentType(contentType).build()); + } else { + minioClient.uploadObject(UploadObjectArgs.builder().bucket(bucketName).object(filename).build()); + } + log.info("上传文件 ------ 成功"); + } catch (Exception exception) { + log.error("上传文件 ------ 失败消息:{}", exception.getLocalizedMessage()); + exception.getStackTrace(); + throw new BunnyException(FileMessageConstant.FILE_UPLOAD_EXCEPTION); + } + } + + /** + * 删除文件 + * + * @param bucketName 桶名称 + * @param objectName 对象名称 + */ + public void removeObject(String bucketName, String objectName) { + try { + minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build()); + log.info("删除文件 ------ 成功"); + } catch (Exception exception) { + log.error("删除文件 ------ 失败消息:{}", exception.getLocalizedMessage()); + exception.getStackTrace(); + throw new BunnyException(FileMessageConstant.DELETE_BUCKET_EXCEPTION); + } + } + + /** + * 删除文件 + * + * @param bucketName 桶名称 + * @param objectName 对象名称 + * @param versionId 版本号 + */ + public void removeObject(String bucketName, String objectName, String versionId) { + try { + minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).versionId(versionId).build()); + log.info("删除文件 ------ 成功"); + } catch (Exception exception) { + log.error("删除文件 ------ 失败消息:{}", exception.getLocalizedMessage()); + exception.getStackTrace(); + throw new BunnyException(FileMessageConstant.DELETE_BUCKET_EXCEPTION); + } + } + + /** + * 删除文件 + * + * @param bucketName 桶名称 + * @param objectName 对象名称 + * @param bypassRetentionMode 保持模式 + */ + public void removeObject(String bucketName, String objectName, String versionId, boolean bypassRetentionMode) { + try { + minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).versionId(versionId).bypassGovernanceMode(bypassRetentionMode).build()); + log.info("删除文件 ------ 成功"); + } catch (Exception exception) { + log.error("删除文件 ------ 失败消息:{}", exception.getLocalizedMessage()); + exception.getStackTrace(); + throw new BunnyException(FileMessageConstant.DELETE_BUCKET_EXCEPTION); + } + } + + /** + * 上传多文件,通过创建中间TAR文件(可选压缩)来完成的 + * + * @param bucketName 桶名称 + * @param objects 对象名称 + */ + public void uploadSnowballObjects(String bucketName, List objects) { + try { + minioClient.uploadSnowballObjects(UploadSnowballObjectsArgs.builder().bucket(bucketName).objects(objects).build()); + log.info("上传多文件 ------ 成功"); + } catch (Exception exception) { + log.error("上传多文件 ------ 失败消息:{}", exception.getLocalizedMessage()); + exception.getStackTrace(); + throw new BunnyException(FileMessageConstant.FILE_UPLOAD_EXCEPTION); + } + } + + /** + * 设置对象标签 + * + * @param bucketName 桶名称 + * @param objectName 对象名称 + * @param retention 配置信息 + * @param bypassGovernanceMode 保持模式 + */ + public void setObjectLockRetention(String bucketName, String objectName, Retention retention, boolean bypassGovernanceMode) { + try { + minioClient.setObjectRetention(SetObjectRetentionArgs.builder().bucket(bucketName).object(objectName).config(retention).bypassGovernanceMode(bypassGovernanceMode).build()); + log.info("删除文件 ------ 成功"); + } catch (Exception exception) { + log.error("删除文件 ------ 失败消息:{}", exception.getLocalizedMessage()); + exception.getStackTrace(); + throw new BunnyException(FileMessageConstant.UPDATE_BUCKET_EXCEPTION); + } + } + + /** + * 设置对象标签 + * + * @param bucketName 桶名称 + * @param objectName 对象名称 + * @param map 对象集合 + */ + public void setObjectTags(String bucketName, String objectName, Map map) { + try { + minioClient.setObjectTags(SetObjectTagsArgs.builder().bucket(bucketName).object(objectName).tags(map).build()); + log.info("删除文件 ------ 成功"); + } catch (Exception exception) { + log.error("删除文件 ------ 失败消息:{}", exception.getLocalizedMessage()); + exception.getStackTrace(); + throw new BunnyException(FileMessageConstant.UPDATE_BUCKET_EXCEPTION); + } + } + + /** + * 获取对象状态 + * + * @param bucketName 桶名称 + * @param objectName 对象名称 + * @param versionId 版本ID + * @param customerKey 秘钥 + * @return 对象信息 + */ + public StatObjectResponse statObject(String bucketName, String objectName, String versionId, ServerSideEncryptionCustomerKey customerKey) { + try { + StatObjectResponse statObjectResponse = minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).versionId(versionId).ssec(customerKey).build()); + log.info("获取对象状态 ------ 成功"); + return statObjectResponse; + } catch (Exception exception) { + log.error("获取对象状态 ------ 失败消息:{}", exception.getLocalizedMessage()); + exception.getStackTrace(); + throw new BunnyException(FileMessageConstant.DELETE_BUCKET_EXCEPTION); + } + } +} \ No newline at end of file diff --git a/netty/module/module-rabbitMQ/pom.xml b/netty/module/module-rabbitMQ/pom.xml new file mode 100644 index 0000000..29884a1 --- /dev/null +++ b/netty/module/module-rabbitMQ/pom.xml @@ -0,0 +1,35 @@ + + 4.0.0 + + cn.bunny + module + 0.0.1-SNAPSHOT + + + module-rabbitMQ + jar + + module-rabbitMQ + https://maven.apache.org + + + UTF-8 + + + + + org.springframework.boot + spring-boot-starter-amqp + + + org.springframework.amqp + spring-rabbit-test + + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + 2.16.0-rc1 + + + diff --git a/netty/module/module-rabbitMQ/src/main/java/cn/bunny/module/rabbitMQ/config/RabbitMqConfig.java b/netty/module/module-rabbitMQ/src/main/java/cn/bunny/module/rabbitMQ/config/RabbitMqConfig.java new file mode 100644 index 0000000..99abbc7 --- /dev/null +++ b/netty/module/module-rabbitMQ/src/main/java/cn/bunny/module/rabbitMQ/config/RabbitMqConfig.java @@ -0,0 +1,38 @@ +package cn.bunny.module.rabbitMQ.config; + +import jakarta.annotation.PostConstruct; +import lombok.extern.slf4j.Slf4j; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; +import org.springframework.amqp.support.converter.MessageConverter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +public class RabbitMqConfig { + @Autowired + private RabbitTemplate rabbitTemplate; + + @PostConstruct + public void init() { + rabbitTemplate.setReturnsCallback(returned -> { + log.error("触发return callback,"); + log.debug("exchange: {}", returned.getExchange()); + log.debug("routingKey: {}", returned.getRoutingKey()); + log.debug("message: {}", returned.getMessage()); + log.debug("replyCode: {}", returned.getReplyCode()); + log.debug("replyText: {}", returned.getReplyText()); + }); + } + + @Bean + public MessageConverter messageConverter() { + // 1.定义消息转换器 + Jackson2JsonMessageConverter converter = new Jackson2JsonMessageConverter(); + // 2.配置自动创建消息id,用于识别不同消息,也可以在业务中基于ID判断是否是重复消息 + converter.setCreateMessageIds(true); + return converter; + } +} diff --git a/netty/module/module-rabbitMQ/src/main/java/cn/bunny/module/rabbitMQ/consumer/DelayConsumer.java b/netty/module/module-rabbitMQ/src/main/java/cn/bunny/module/rabbitMQ/consumer/DelayConsumer.java new file mode 100644 index 0000000..ac1874d --- /dev/null +++ b/netty/module/module-rabbitMQ/src/main/java/cn/bunny/module/rabbitMQ/consumer/DelayConsumer.java @@ -0,0 +1,9 @@ +package cn.bunny.module.rabbitMQ.consumer; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +public class DelayConsumer { +} diff --git a/netty/module/module-rabbitMQ/src/main/java/cn/bunny/module/rabbitMQ/consumer/DirectConsumer.java b/netty/module/module-rabbitMQ/src/main/java/cn/bunny/module/rabbitMQ/consumer/DirectConsumer.java new file mode 100644 index 0000000..6b3bc85 --- /dev/null +++ b/netty/module/module-rabbitMQ/src/main/java/cn/bunny/module/rabbitMQ/consumer/DirectConsumer.java @@ -0,0 +1,24 @@ +package cn.bunny.module.rabbitMQ.consumer; + +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Test; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +public class DirectConsumer { + @Autowired + RabbitTemplate rabbitTemplate; + + /** + * 发送红色消息 + */ + @Test + void testSendDirectRed() throws Exception { + for (int i = 0; i < 1000; i++) { + rabbitTemplate.convertAndSend("bunny.direct", "red", "发送消息:" + i); + } + } +} diff --git a/netty/module/module-rabbitMQ/src/main/java/cn/bunny/module/rabbitMQ/consumer/ErrorConsumer.java b/netty/module/module-rabbitMQ/src/main/java/cn/bunny/module/rabbitMQ/consumer/ErrorConsumer.java new file mode 100644 index 0000000..583823e --- /dev/null +++ b/netty/module/module-rabbitMQ/src/main/java/cn/bunny/module/rabbitMQ/consumer/ErrorConsumer.java @@ -0,0 +1,9 @@ +package cn.bunny.module.rabbitMQ.consumer; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +public class ErrorConsumer { +} diff --git a/netty/module/module-rabbitMQ/src/main/java/cn/bunny/module/rabbitMQ/consumer/FanoutConsumer.java b/netty/module/module-rabbitMQ/src/main/java/cn/bunny/module/rabbitMQ/consumer/FanoutConsumer.java new file mode 100644 index 0000000..0f65252 --- /dev/null +++ b/netty/module/module-rabbitMQ/src/main/java/cn/bunny/module/rabbitMQ/consumer/FanoutConsumer.java @@ -0,0 +1,9 @@ +package cn.bunny.module.rabbitMQ.consumer; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +public class FanoutConsumer { +} diff --git a/netty/module/module-rabbitMQ/src/main/java/cn/bunny/module/rabbitMQ/consumer/LazyConsumer.java b/netty/module/module-rabbitMQ/src/main/java/cn/bunny/module/rabbitMQ/consumer/LazyConsumer.java new file mode 100644 index 0000000..30b451f --- /dev/null +++ b/netty/module/module-rabbitMQ/src/main/java/cn/bunny/module/rabbitMQ/consumer/LazyConsumer.java @@ -0,0 +1,9 @@ +package cn.bunny.module.rabbitMQ.consumer; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +public class LazyConsumer { +} diff --git a/netty/module/module-rabbitMQ/src/main/java/cn/bunny/module/rabbitMQ/consumer/TopicConsumer.java b/netty/module/module-rabbitMQ/src/main/java/cn/bunny/module/rabbitMQ/consumer/TopicConsumer.java new file mode 100644 index 0000000..d6a8de0 --- /dev/null +++ b/netty/module/module-rabbitMQ/src/main/java/cn/bunny/module/rabbitMQ/consumer/TopicConsumer.java @@ -0,0 +1,9 @@ +package cn.bunny.module.rabbitMQ.consumer; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +public class TopicConsumer { +} diff --git a/netty/module/module-rabbitMQ/src/main/java/cn/bunny/module/rabbitMQ/listener/DelayListener.java b/netty/module/module-rabbitMQ/src/main/java/cn/bunny/module/rabbitMQ/listener/DelayListener.java new file mode 100644 index 0000000..318cd63 --- /dev/null +++ b/netty/module/module-rabbitMQ/src/main/java/cn/bunny/module/rabbitMQ/listener/DelayListener.java @@ -0,0 +1,9 @@ +package cn.bunny.module.rabbitMQ.listener; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +public class DelayListener { +} diff --git a/netty/module/module-rabbitMQ/src/main/java/cn/bunny/module/rabbitMQ/listener/DirectListener.java b/netty/module/module-rabbitMQ/src/main/java/cn/bunny/module/rabbitMQ/listener/DirectListener.java new file mode 100644 index 0000000..aa83490 --- /dev/null +++ b/netty/module/module-rabbitMQ/src/main/java/cn/bunny/module/rabbitMQ/listener/DirectListener.java @@ -0,0 +1,30 @@ +package cn.bunny.module.rabbitMQ.listener; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.amqp.core.ExchangeTypes; +import org.springframework.amqp.rabbit.annotation.Exchange; +import org.springframework.amqp.rabbit.annotation.Queue; +import org.springframework.amqp.rabbit.annotation.QueueBinding; +import org.springframework.amqp.rabbit.annotation.RabbitListener; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +public class DirectListener { + /** + * * 监听者2 + * 创建队列 持久化的、不自动删除 + * 创建交换机 持久化的、不自动删除 + * key包含 red 和 yellow + * + * @param message 接受消息 + */ + @RabbitListener(bindings = @QueueBinding( + value = @Queue(name = "direct.queue2", durable = "true", autoDelete = "false"), + exchange = @Exchange(name = "bunny.direct", type = ExchangeTypes.DIRECT, durable = "true", autoDelete = "false"), + key = {"red", "yellow"} + )) + public void listenDirectQueue2(String message) { + System.out.println("消费者2接收到 Direct key 为 {\"red\", \"yellow\"} 消息:【" + message + "】"); + } +} \ No newline at end of file diff --git a/netty/module/module-rabbitMQ/src/main/java/cn/bunny/module/rabbitMQ/listener/ErrorListener.java b/netty/module/module-rabbitMQ/src/main/java/cn/bunny/module/rabbitMQ/listener/ErrorListener.java new file mode 100644 index 0000000..22d2529 --- /dev/null +++ b/netty/module/module-rabbitMQ/src/main/java/cn/bunny/module/rabbitMQ/listener/ErrorListener.java @@ -0,0 +1,9 @@ +package cn.bunny.module.rabbitMQ.listener; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +public class ErrorListener { +} diff --git a/netty/module/module-rabbitMQ/src/main/java/cn/bunny/module/rabbitMQ/listener/FanoutListener.java b/netty/module/module-rabbitMQ/src/main/java/cn/bunny/module/rabbitMQ/listener/FanoutListener.java new file mode 100644 index 0000000..2d4d0ff --- /dev/null +++ b/netty/module/module-rabbitMQ/src/main/java/cn/bunny/module/rabbitMQ/listener/FanoutListener.java @@ -0,0 +1,9 @@ +package cn.bunny.module.rabbitMQ.listener; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +public class FanoutListener { +} diff --git a/netty/module/module-rabbitMQ/src/main/java/cn/bunny/module/rabbitMQ/listener/LazyListener.java b/netty/module/module-rabbitMQ/src/main/java/cn/bunny/module/rabbitMQ/listener/LazyListener.java new file mode 100644 index 0000000..c682154 --- /dev/null +++ b/netty/module/module-rabbitMQ/src/main/java/cn/bunny/module/rabbitMQ/listener/LazyListener.java @@ -0,0 +1,9 @@ +package cn.bunny.module.rabbitMQ.listener; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +public class LazyListener { +} diff --git a/netty/module/module-rabbitMQ/src/main/java/cn/bunny/module/rabbitMQ/listener/TopicListener.java b/netty/module/module-rabbitMQ/src/main/java/cn/bunny/module/rabbitMQ/listener/TopicListener.java new file mode 100644 index 0000000..9363906 --- /dev/null +++ b/netty/module/module-rabbitMQ/src/main/java/cn/bunny/module/rabbitMQ/listener/TopicListener.java @@ -0,0 +1,9 @@ +package cn.bunny.module.rabbitMQ.listener; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +public class TopicListener { +} diff --git a/netty/module/module-task/pom.xml b/netty/module/module-task/pom.xml new file mode 100644 index 0000000..3ab04f3 --- /dev/null +++ b/netty/module/module-task/pom.xml @@ -0,0 +1,23 @@ + + 4.0.0 + + cn.bunny + module + 0.0.1-SNAPSHOT + + + module-task + jar + + module-task + https://maven.apache.org + + + UTF-8 + + + + + + diff --git a/netty/module/module-task/src/main/java/cn/bunny/module/task/TemplateTask.java b/netty/module/module-task/src/main/java/cn/bunny/module/task/TemplateTask.java new file mode 100644 index 0000000..2dbf0b7 --- /dev/null +++ b/netty/module/module-task/src/main/java/cn/bunny/module/task/TemplateTask.java @@ -0,0 +1,14 @@ +package cn.bunny.module.task; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +public class TemplateTask { + @Scheduled(cron = "0/1 5 * * * ?") + public void templateTask() { + log.info("定时任务执行..."); + } +} diff --git a/netty/module/module-websocket/pom.xml b/netty/module/module-websocket/pom.xml new file mode 100644 index 0000000..e3b9b40 --- /dev/null +++ b/netty/module/module-websocket/pom.xml @@ -0,0 +1,27 @@ + + 4.0.0 + + cn.bunny + module + 0.0.1-SNAPSHOT + + + module-websocket + jar + + module-websocket + https://maven.apache.org + + + UTF-8 + + + + + + org.springframework.boot + spring-boot-starter-websocket + + + diff --git a/netty/module/module-websocket/src/main/java/cn/bunny/module/websocket/WebSocketServer.java b/netty/module/module-websocket/src/main/java/cn/bunny/module/websocket/WebSocketServer.java new file mode 100644 index 0000000..2d84cc6 --- /dev/null +++ b/netty/module/module-websocket/src/main/java/cn/bunny/module/websocket/WebSocketServer.java @@ -0,0 +1,71 @@ +package cn.bunny.module.websocket; + +import jakarta.websocket.OnClose; +import jakarta.websocket.OnMessage; +import jakarta.websocket.OnOpen; +import jakarta.websocket.Session; +import jakarta.websocket.server.PathParam; +import jakarta.websocket.server.ServerEndpoint; +import org.springframework.stereotype.Component; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +/** + * WebSocket服务 + */ +@Component +@ServerEndpoint("/ws/{sid}") +public class WebSocketServer { + + // 存放会话对象 + private static final Map sessionMap = new HashMap(); + + /** + * 连接建立成功调用的方法 + */ + @OnOpen + public void onOpen(Session session, @PathParam("sid") String sid) { + System.out.println("客户端:" + sid + "建立连接"); + sessionMap.put(sid, session); + } + + /** + * 收到客户端消息后调用的方法 + * + * @param message 客户端发送过来的消息 + */ + @OnMessage + public void onMessage(String message, @PathParam("sid") String sid) { + System.out.println("收到来自客户端:" + sid + "的信息:" + message); + } + + /** + * 连接关闭调用的方法 + * + * @param sid 请求id + */ + @OnClose + public void onClose(@PathParam("sid") String sid) { + System.out.println("连接断开:" + sid); + sessionMap.remove(sid); + } + + /** + * 群发 + * + * @param message 消息 + */ + public void sendToAllClient(String message) { + Collection sessions = sessionMap.values(); + for (Session session : sessions) { + try { + // 服务器向客户端发送消息 + session.getBasicRemote().sendText(message); + } catch (Exception e) { + e.printStackTrace(); + } + } + } +} diff --git a/netty/module/module-websocket/src/main/java/cn/bunny/module/websocket/config/WebSocketConfiguration.java b/netty/module/module-websocket/src/main/java/cn/bunny/module/websocket/config/WebSocketConfiguration.java new file mode 100644 index 0000000..8df905f --- /dev/null +++ b/netty/module/module-websocket/src/main/java/cn/bunny/module/websocket/config/WebSocketConfiguration.java @@ -0,0 +1,13 @@ +package cn.bunny.module.websocket.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.socket.server.standard.ServerEndpointExporter; + +@Configuration +public class WebSocketConfiguration { + @Bean + public ServerEndpointExporter endpointExporter() { + return new ServerEndpointExporter(); + } +} diff --git a/netty/module/pom.xml b/netty/module/pom.xml new file mode 100644 index 0000000..3a479ab --- /dev/null +++ b/netty/module/pom.xml @@ -0,0 +1,34 @@ + + 4.0.0 + + cn.bunny + bunny-template + 0.0.1-SNAPSHOT + + + module + pom + + module + https://maven.apache.org + + module-minio + module-mail + module-rabbitMQ + module-websocket + module-task + + + + UTF-8 + + + + + cn.bunny + service-utils + 0.0.1-SNAPSHOT + + + diff --git a/netty/module/spring-security/pom.xml b/netty/module/spring-security/pom.xml new file mode 100644 index 0000000..070d1de --- /dev/null +++ b/netty/module/spring-security/pom.xml @@ -0,0 +1,32 @@ + + 4.0.0 + + cn.bunny + module + 0.0.1-SNAPSHOT + + + spring-security + jar + + spring-security + https://maven.apache.org + + + UTF-8 + + + + + + org.springframework.boot + spring-boot-starter-security + + + + org.springframework.security + spring-security-test + + + diff --git a/netty/module/spring-security/src/main/java/cn/bunny/security/config/WebSecurityConfig.java b/netty/module/spring-security/src/main/java/cn/bunny/security/config/WebSecurityConfig.java new file mode 100644 index 0000000..5899405 --- /dev/null +++ b/netty/module/spring-security/src/main/java/cn/bunny/security/config/WebSecurityConfig.java @@ -0,0 +1,95 @@ +package cn.bunny.security.config; + +import cn.bunny.security.custom.CustomPasswordEncoder; +import cn.bunny.security.filter.TokenAuthenticationFilter; +import cn.bunny.security.filter.TokenLoginFilterService; +import cn.bunny.security.handelr.SecurityAccessDeniedHandler; +import cn.bunny.security.handelr.SecurityAuthenticationEntryPoint; +import cn.bunny.security.service.CustomAuthorizationManagerService; +import cn.bunny.security.service.CustomUserDetailsService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; +import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.core.session.SessionRegistry; +import org.springframework.security.core.session.SessionRegistryImpl; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.security.web.util.matcher.RegexRequestMatcher; + +@Configuration +@EnableWebSecurity +@EnableMethodSecurity +public class WebSecurityConfig { + @Autowired + private RedisTemplate redisTemplate; + // 自定义用户接口 + @Autowired + private CustomUserDetailsService customUserDetailsService; + // 自定义密码加密器 + @Autowired + private CustomPasswordEncoder customPasswordEncoder; + // 自定义验证码 + @Autowired + private CustomAuthorizationManagerService customAuthorizationManager; + @Autowired + private AuthenticationConfiguration authenticationConfiguration; + + @Bean + public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception { + httpSecurity + // 前端段分离不需要---禁用明文验证 + .httpBasic(AbstractHttpConfigurer::disable) + // 前端段分离不需要---禁用默认登录页 + .formLogin(AbstractHttpConfigurer::disable) + // 前端段分离不需要---禁用退出页 + .logout(AbstractHttpConfigurer::disable) + // 前端段分离不需要---csrf攻击 + .csrf(AbstractHttpConfigurer::disable) + // 跨域访问权限,如果需要可以关闭后自己配置跨域访问 + .cors(AbstractHttpConfigurer::disable) + // 前后端分离不需要---因为是无状态的 + .sessionManagement(AbstractHttpConfigurer::disable) + // 前后端分离不需要---记住我,e -> e.rememberMeParameter("rememberBunny").rememberMeCookieName("rememberBunny").key("BunnyKey") + .rememberMe(AbstractHttpConfigurer::disable) + .authorizeHttpRequests(authorize -> { + // 有样式文件,不需要访问权限 + authorize.requestMatchers(RegexRequestMatcher.regexMatcher("^\\S*[css|js]$")).permitAll(); + // 上面都不是需要鉴权访问 + authorize.anyRequest().access(customAuthorizationManager); + }) + .exceptionHandling(exception -> { + // 请求未授权接口 + exception.authenticationEntryPoint(new SecurityAuthenticationEntryPoint()); + // 没有权限访问 + exception.accessDeniedHandler(new SecurityAccessDeniedHandler()); + }) + // 登录验证过滤器 + .addFilterBefore(new TokenLoginFilterService(authenticationConfiguration, redisTemplate, customUserDetailsService), UsernamePasswordAuthenticationFilter.class) + // 其它权限鉴权过滤器 + .addFilterAt(new TokenAuthenticationFilter(redisTemplate), UsernamePasswordAuthenticationFilter.class) + // 自定义密码加密器和用户登录 + .passwordManagement(customPasswordEncoder).userDetailsService(customUserDetailsService); + + return httpSecurity.build(); + } + + @Bean + public SessionRegistry sessionRegistry() { + return new SessionRegistryImpl(); + } + + // 排出鉴定路径 + @Bean + public WebSecurityCustomizer webSecurityCustomizer() { + String[] annotations = {"/", "/test/**", "/diagram-viewer/**", "/editor-app/**", "/*.html", + "/*/*/noAuth/**", "/*/noAuth/**", "/favicon.ico", "/swagger-resources/**", "/webjars/**", "/v3/**", "/swagger-ui.html/**", "/doc.html"}; + return web -> web.ignoring().requestMatchers(annotations); + } +} diff --git a/netty/module/spring-security/src/main/java/cn/bunny/security/custom/CustomPasswordEncoder.java b/netty/module/spring-security/src/main/java/cn/bunny/security/custom/CustomPasswordEncoder.java new file mode 100644 index 0000000..57864ed --- /dev/null +++ b/netty/module/spring-security/src/main/java/cn/bunny/security/custom/CustomPasswordEncoder.java @@ -0,0 +1,28 @@ +package cn.bunny.security.custom; + +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.Customizer; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configurers.PasswordManagementConfigurer; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.util.DigestUtils; + +/** + * 自定义密码加密比对 + */ +@Configuration +public class CustomPasswordEncoder implements PasswordEncoder, Customizer> { + @Override + public String encode(CharSequence rawPassword) { + return DigestUtils.md5DigestAsHex(rawPassword.toString().getBytes()); + } + + @Override + public boolean matches(CharSequence rawPassword, String encodedPassword) { + return encodedPassword.matches(DigestUtils.md5DigestAsHex(rawPassword.toString().getBytes())); + } + + @Override + public void customize(PasswordManagementConfigurer httpSecurityPasswordManagementConfigurer) { + } +} diff --git a/netty/module/spring-security/src/main/java/cn/bunny/security/custom/CustomUser.java b/netty/module/spring-security/src/main/java/cn/bunny/security/custom/CustomUser.java new file mode 100644 index 0000000..296d5a1 --- /dev/null +++ b/netty/module/spring-security/src/main/java/cn/bunny/security/custom/CustomUser.java @@ -0,0 +1,22 @@ +package cn.bunny.security.custom; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.User; + +import java.util.Collection; + +/** + * 重写自带的User + */ +@Getter +@Setter +public class CustomUser extends User { + private cn.bunny.entity.system.user.User user; + + public CustomUser(cn.bunny.entity.system.user.User user, Collection authorities) { + super(user.getEmail(), user.getPassword(), authorities); + this.user = user; + } +} \ No newline at end of file diff --git a/netty/module/spring-security/src/main/java/cn/bunny/security/filter/TokenAuthenticationFilter.java b/netty/module/spring-security/src/main/java/cn/bunny/security/filter/TokenAuthenticationFilter.java new file mode 100644 index 0000000..bea057b --- /dev/null +++ b/netty/module/spring-security/src/main/java/cn/bunny/security/filter/TokenAuthenticationFilter.java @@ -0,0 +1,91 @@ +package cn.bunny.security.filter; + +import cn.bunny.common.service.context.BaseContext; +import cn.bunny.common.service.exception.BunnyException; +import cn.bunny.common.service.utils.JwtHelper; +import cn.bunny.common.service.utils.ResponseUtil; +import cn.bunny.result.Result; +import cn.bunny.result.ResultCodeEnum; +import cn.bunny.result.constant.RedisUserConstant; +import cn.bunny.vo.system.login.LoginVo; +import com.alibaba.fastjson2.JSON; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.util.StringUtils; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +public class TokenAuthenticationFilter extends OncePerRequestFilter { + private final RedisTemplate redisTemplate; + + public TokenAuthenticationFilter(RedisTemplate redisTemplate) { + this.redisTemplate = redisTemplate; + } + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException, BunnyException { + String token = request.getHeader("token"); + + // login请求就没token,直接放行,因为后边有其他的过滤器 + if (token == null) { + doFilter(request, response, chain); + return; + } + + // 如果想让这个用户下线,清空Redis这个用户值,返回未登录,判断Redis是否有这个用户 + // 如果想让这个用户锁定,清空Redis值并在数据库中设置status值为1 + String userName = JwtHelper.getUsername(token); + Object usernameObject = redisTemplate.opsForValue().get(RedisUserConstant.getUserLoginInfoPrefix(userName)); + if (usernameObject == null) { + Result error = Result.error(ResultCodeEnum.LOGIN_AUTH); + ResponseUtil.out(response, error); + return; + } + + // 获取Redis中登录信息 + LoginVo loginVo = JSON.parseObject(JSON.toJSONString(usernameObject), LoginVo.class); + // 如果是登录接口,直接放行 + UsernamePasswordAuthenticationToken authentication = getAuthentication(request); + if (authentication != null) { + // 设置用户详细信息 + authentication.setDetails(loginVo.getPersonDescription()); + SecurityContextHolder.getContext().setAuthentication(authentication); + } + + chain.doFilter(request, response); + } + + private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) { + // 请求头是否有token + String token = request.getHeader("token"); + String username = RedisUserConstant.getAdminLoginInfoPrefix(JwtHelper.getUsername(token)); + List authList = new ArrayList<>(); + if (!StringUtils.hasText(username)) return null; + + // 当前用户信息放到ThreadLocal里面 + BaseContext.setAdminId(JwtHelper.getUserId(token)); + BaseContext.setAdminName(username); + + // 通过username从redis获取权限数据 + Object UserObject = redisTemplate.opsForValue().get(username); + // 把redis获取字符串权限数据转换要求集合类型 List + if (UserObject != null) { + LoginVo loginVo = JSON.parseObject(JSON.toJSONString(UserObject), LoginVo.class); + List roleList = loginVo.getRoleList(); + roleList.forEach(role -> authList.add(new SimpleGrantedAuthority(role))); + + return new UsernamePasswordAuthenticationToken(username, null, authList); + } else { + return new UsernamePasswordAuthenticationToken(username, null, new ArrayList<>()); + } + } +} diff --git a/netty/module/spring-security/src/main/java/cn/bunny/security/filter/TokenLoginFilterService.java b/netty/module/spring-security/src/main/java/cn/bunny/security/filter/TokenLoginFilterService.java new file mode 100644 index 0000000..9eadac3 --- /dev/null +++ b/netty/module/spring-security/src/main/java/cn/bunny/security/filter/TokenLoginFilterService.java @@ -0,0 +1,139 @@ +package cn.bunny.security.filter; + + +import cn.bunny.common.service.utils.ResponseUtil; +import cn.bunny.dto.user.LoginDto; +import cn.bunny.result.Result; +import cn.bunny.result.ResultCodeEnum; +import cn.bunny.result.constant.RedisUserConstant; +import cn.bunny.security.handelr.SecurityAuthenticationFailureHandler; +import cn.bunny.security.handelr.SecurityAuthenticationSuccessHandler; +import cn.bunny.security.service.CustomUserDetailsService; +import cn.bunny.vo.system.login.LoginVo; +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.servlet.FilterChain; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.http.HttpMethod; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; +import org.springframework.util.StringUtils; + +import java.io.IOException; +import java.util.Objects; +import java.util.concurrent.TimeUnit; + +/** + * * UsernamePasswordAuthenticationFilter + * * 也可以在这里添加验证码、短信等的验证 + * 由于SpringSecurity的登录只能是表单形式 并且用户名密码需要时username、password,可以通过继承 UsernamePasswordAuthenticationFilter 获取登录请求的参数 + * 再去设置到 UsernamePasswordAuthenticationToken 中 来改变请求传参方式、参数名等 或者也可以在登录的时候加入其他参数等等 + */ +public class TokenLoginFilterService extends UsernamePasswordAuthenticationFilter { + private final RedisTemplate redisTemplate; + private final CustomUserDetailsService customUserDetailsService; + private LoginDto loginDto; + + // 依赖注入 + public TokenLoginFilterService(AuthenticationConfiguration authenticationConfiguration, RedisTemplate redisTemplate, CustomUserDetailsService customUserDetailsService) throws Exception { + this.setAuthenticationSuccessHandler(new SecurityAuthenticationSuccessHandler()); + this.setAuthenticationFailureHandler(new SecurityAuthenticationFailureHandler()); + this.setPostOnly(false); + // ? 指定登录接口及提交方式,可以指定任意路径 + this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/*/login", HttpMethod.POST.name())); + this.setAuthenticationManager(authenticationConfiguration.getAuthenticationManager()); + // 依赖注入 + this.redisTemplate = redisTemplate; + this.customUserDetailsService = customUserDetailsService; + } + + /** + * * 登录认证,获取输入的用户名和密码,调用方法认证 + * 接受前端login登录参数 + * 在这里可以设置短信验证登录 + */ + @Override + public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { + try { + // 获取用户信息 + loginDto = new ObjectMapper().readValue(request.getInputStream(), LoginDto.class); + + // 登录验证码判断 + String username = loginDto.getUsername(); + String emailCode = loginDto.getEmailCode().toLowerCase(); + String redisEmailCode = (String) redisTemplate.opsForValue().get(RedisUserConstant.getAdminUserEmailCodePrefix(username)); + + // 如果不存在验证码 + if (!StringUtils.hasText(emailCode)) { + ResponseUtil.out(response, Result.error(ResultCodeEnum.EMAIL_CODE_NOT_EMPTY)); + return null; + } + if (!StringUtils.hasText(redisEmailCode)) { + ResponseUtil.out(response, Result.error(ResultCodeEnum.SEND_EMAIL_CODE_NOT_EMPTY)); + return null; + } + // 验证码不匹配 + if (!Objects.equals(redisEmailCode.toLowerCase(), emailCode)) { + ResponseUtil.out(response, Result.error(ResultCodeEnum.EMAIL_CODE_NOT_MATCHING)); + return null; + } + + // 封装对象,将用户名密码传入 + Authentication authenticationToken = new UsernamePasswordAuthenticationToken(loginDto.getUsername(), loginDto.getPassword()); + return this.getAuthenticationManager().authenticate(authenticationToken); + } catch (IOException e) { + throw new RuntimeException(e.getLocalizedMessage()); + } + } + + /** + * * 认证成功调用方法 + * 返回登录成功后的信息 + */ + @Override + protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication auth) { + // 封装返回对象 + LoginVo loginVo = customUserDetailsService.login(loginDto); + + // 判断用户是否被锁定 + if (loginVo.getStatus() == 1) { + ResponseUtil.out(response, Result.error(ResultCodeEnum.FAIL_NO_ACCESS_DENIED_USER_LOCKED)); + return; + } + + // 将值存入Redis中 + redisTemplate.opsForValue().set(RedisUserConstant.getAdminLoginInfoPrefix(loginVo.getEmail()), loginVo, 15, TimeUnit.DAYS); + // 将Redis中验证码删除 + redisTemplate.delete(RedisUserConstant.getAdminUserEmailCodePrefix(loginVo.getEmail())); + // 返回登录信息 + ResponseUtil.out(response, Result.success(loginVo)); + } + + /** + * * 认证失败调用方法,失败判断 + * 1. 是否包含用户名 + * 2. 是否包含密码 + */ + @Override + protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) { + // 账号和密码不能为空 + if (loginDto == null) { + ResponseUtil.out(response, Result.error(ResultCodeEnum.LOGIN_ERROR_USERNAME_PASSWORD_NOT_EMPTY)); + } + // 用户名为空 + if (!StringUtils.hasText(loginDto.getUsername())) { + ResponseUtil.out(response, Result.error(ResultCodeEnum.USERNAME_NOT_EMPTY)); + } + // 密码为空 + if (!StringUtils.hasText(loginDto.getPassword())) { + ResponseUtil.out(response, Result.error(ResultCodeEnum.PASSWORD_NOT_EMPTY)); + } + // 抛出异常,账号或密码错误 + ResponseUtil.out(response, Result.error(null, ResultCodeEnum.LOGIN_ERROR)); + } +} diff --git a/netty/module/spring-security/src/main/java/cn/bunny/security/handelr/SecurityAccessDeniedHandler.java b/netty/module/spring-security/src/main/java/cn/bunny/security/handelr/SecurityAccessDeniedHandler.java new file mode 100644 index 0000000..b14a8d7 --- /dev/null +++ b/netty/module/spring-security/src/main/java/cn/bunny/security/handelr/SecurityAccessDeniedHandler.java @@ -0,0 +1,26 @@ +package cn.bunny.security.handelr; + +import cn.bunny.result.Result; +import cn.bunny.result.ResultCodeEnum; +import com.alibaba.fastjson2.JSON; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.SneakyThrows; +import org.springframework.security.access.AccessDeniedException; + +/** + * 没有权限访问 + */ +public class SecurityAccessDeniedHandler implements org.springframework.security.web.access.AccessDeniedHandler { + @SneakyThrows + @Override + public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) { + Result result = Result.error(ResultCodeEnum.FAIL_NO_ACCESS_DENIED); + + Object json = JSON.toJSON(result); + + // 返回响应 + response.setContentType("application/json;charset=UTF-8"); + response.getWriter().println(json); + } +} diff --git a/netty/module/spring-security/src/main/java/cn/bunny/security/handelr/SecurityAuthenticationEntryPoint.java b/netty/module/spring-security/src/main/java/cn/bunny/security/handelr/SecurityAuthenticationEntryPoint.java new file mode 100644 index 0000000..cd82558 --- /dev/null +++ b/netty/module/spring-security/src/main/java/cn/bunny/security/handelr/SecurityAuthenticationEntryPoint.java @@ -0,0 +1,37 @@ +package cn.bunny.security.handelr; + +import cn.bunny.common.service.utils.ResponseUtil; +import cn.bunny.result.Result; +import cn.bunny.result.ResultCodeEnum; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.AuthenticationEntryPoint; + +import java.io.IOException; + +/** + * 请求未认证接口 + */ +@Slf4j +public class SecurityAuthenticationEntryPoint implements AuthenticationEntryPoint { + @Override + public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException { + String token = response.getHeader("token"); + String message = authException.getMessage(); + // 创建结果对象 + Result result; + + if (token == null) { + result = Result.error(ResultCodeEnum.LOGIN_AUTH); + log.info("请求未登录接口:{},用户id:{}", message, null); + } else { + result = Result.error(ResultCodeEnum.LOGGED_IN_FROM_ANOTHER_DEVICE); + log.info("请求未授权接口:{},用户id:{}", message, token); + } + + // 返回响应 + ResponseUtil.out(response, result); + } +} diff --git a/netty/module/spring-security/src/main/java/cn/bunny/security/handelr/SecurityAuthenticationFailureHandler.java b/netty/module/spring-security/src/main/java/cn/bunny/security/handelr/SecurityAuthenticationFailureHandler.java new file mode 100644 index 0000000..e48ad49 --- /dev/null +++ b/netty/module/spring-security/src/main/java/cn/bunny/security/handelr/SecurityAuthenticationFailureHandler.java @@ -0,0 +1,26 @@ +package cn.bunny.security.handelr; + +import cn.bunny.result.Result; +import com.alibaba.fastjson2.JSON; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.security.core.AuthenticationException; + +import java.io.IOException; + +public class SecurityAuthenticationFailureHandler implements org.springframework.security.web.authentication.AuthenticationFailureHandler { + @Override + public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { + // 错误消息 + String localizedMessage = exception.getLocalizedMessage(); + Result result = Result.error(localizedMessage); + + // 转成JSON + Object json = JSON.toJSON(result); + + // 返回响应 + response.setContentType("application/json;charset=UTF-8"); + response.getWriter().println(json); + } +} diff --git a/netty/module/spring-security/src/main/java/cn/bunny/security/handelr/SecurityAuthenticationSuccessHandler.java b/netty/module/spring-security/src/main/java/cn/bunny/security/handelr/SecurityAuthenticationSuccessHandler.java new file mode 100644 index 0000000..e5bd0fd --- /dev/null +++ b/netty/module/spring-security/src/main/java/cn/bunny/security/handelr/SecurityAuthenticationSuccessHandler.java @@ -0,0 +1,26 @@ +package cn.bunny.security.handelr; + +import cn.bunny.result.Result; +import com.alibaba.fastjson2.JSON; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.authentication.AuthenticationSuccessHandler; + +import java.io.IOException; + +/** + * 登录成功 + */ +public class SecurityAuthenticationSuccessHandler implements AuthenticationSuccessHandler { + @Override + public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException { + // 获取用户身份信息 + Object principal = authentication.getPrincipal(); + Result result = Result.success(principal); + + // 返回 + response.setContentType("application/json;charset=UTF-8"); + response.getWriter().println(JSON.toJSON(result)); + } +} diff --git a/netty/module/spring-security/src/main/java/cn/bunny/security/service/CustomAuthorizationManagerService.java b/netty/module/spring-security/src/main/java/cn/bunny/security/service/CustomAuthorizationManagerService.java new file mode 100644 index 0000000..3c1f939 --- /dev/null +++ b/netty/module/spring-security/src/main/java/cn/bunny/security/service/CustomAuthorizationManagerService.java @@ -0,0 +1,7 @@ +package cn.bunny.security.service; + +import org.springframework.security.authorization.AuthorizationManager; +import org.springframework.security.web.access.intercept.RequestAuthorizationContext; + +public interface CustomAuthorizationManagerService extends AuthorizationManager { +} diff --git a/netty/module/spring-security/src/main/java/cn/bunny/security/service/CustomUserDetailsService.java b/netty/module/spring-security/src/main/java/cn/bunny/security/service/CustomUserDetailsService.java new file mode 100644 index 0000000..7cf5b4e --- /dev/null +++ b/netty/module/spring-security/src/main/java/cn/bunny/security/service/CustomUserDetailsService.java @@ -0,0 +1,23 @@ +package cn.bunny.security.service; + +import cn.bunny.dto.user.LoginDto; +import cn.bunny.vo.system.login.LoginVo; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; + +public interface CustomUserDetailsService extends UserDetailsService { + /** + * 根据用户名获取用户对象(获取不到直接抛异常) + */ + @Override + UserDetails loadUserByUsername(String username) throws UsernameNotFoundException; + + /** + * 前台用户登录接口 + * + * @param loginDto 登录参数 + * @return 登录后结果返回 + */ + LoginVo login(LoginDto loginDto); +} diff --git a/netty/pom.xml b/netty/pom.xml new file mode 100644 index 0000000..4325dff --- /dev/null +++ b/netty/pom.xml @@ -0,0 +1,147 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.2.3 + + + cn.bunny + bunny-template + 0.0.1-SNAPSHOT + pom + bunny-template + bunny-template + + + common + model + service + module + + + + 22 + 22 + 21 + 3.8.1 + 3.5.6 + 8.0.30 + 4.5.0 + 2.0.47 + 8.5.9 + 1.18.32 + 0.9.1 + 3.3.3 + 2.10.1 + 1.9.21 + 6.1.0 + 2.2 + 3.1 + + + + + + junit + junit + ${junit.version} + + + + org.apache.velocity + velocity-engine-core + ${velocity.version} + + + org.apache.velocity.tools + velocity-tools-generic + ${velocity-tools.version} + + + + com.baomidou + mybatis-plus-spring-boot3-starter + ${mybatis-plus.version} + + + + mysql + mysql-connector-java + ${mysql.version} + + + + com.github.xiaoymin + knife4j-openapi3-jakarta-spring-boot-starter + ${knife4j.version} + + + + com.alibaba.fastjson2 + fastjson2 + ${fastjson2.version} + + + + io.minio + minio + ${minio.version} + + + + org.projectlombok + lombok + ${lombok.version} + + + + cn.hutool + hutool-all + 5.8.27 + + + + io.jsonwebtoken + jjwt + ${jwt.version} + + + + com.alibaba + easyexcel + ${easyexcel.version} + + + + org.aspectj + aspectjrt + ${aspectj} + + + + org.aspectj + aspectjweaver + ${aspectj} + + + joda-time + joda-time + ${jodatime.version} + + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + 2.12.3 + + + com.github.pagehelper + pagehelper + ${pagehelper.version} + + + + diff --git a/netty/service/Dockerfile b/netty/service/Dockerfile new file mode 100644 index 0000000..447a2bc --- /dev/null +++ b/netty/service/Dockerfile @@ -0,0 +1,21 @@ +FROM openjdk:21 +MAINTAINER bunny + +#系统编码 +ENV LANG=C.UTF-8 LC_ALL=C.UTF-8 + +# 设置时区,构建镜像时执行的命令 +RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime +RUN echo "Asia/Shanghai" > /etc/timezone + +# 设定工作目录 +WORKDIR /home/bunny + +# 复制jar包 +COPY target/*.jar /home/bunny/app.jar + +#启动容器时的进程 +ENTRYPOINT ["java","-jar","/home/bunny/app.jar"] + +#暴露 8800 端口 +EXPOSE 8800 \ No newline at end of file diff --git a/netty/service/pom.xml b/netty/service/pom.xml new file mode 100644 index 0000000..f738218 --- /dev/null +++ b/netty/service/pom.xml @@ -0,0 +1,111 @@ + + 4.0.0 + + cn.bunny + bunny-template + 0.0.1-SNAPSHOT + + + service + jar + service + https://maven.apache.org + + + UTF-8 + 192.168.3.98:1100 + 192.168.3.98:2375 + bunny-service + + + + + cn.bunny + spring-security + 0.0.1-SNAPSHOT + + + cn.bunny + module-mail + 0.0.1-SNAPSHOT + + + cn.bunny + module-minio + 0.0.1-SNAPSHOT + + + + junit + junit + 3.8.1 + + + + org.springframework.boot + spring-boot-starter-test + + + + org.springframework.boot + spring-boot-starter-websocket + + + + org.aspectj + aspectjrt + + + org.aspectj + aspectjweaver + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + + true + + + + com.spotify + docker-maven-plugin + 1.2.2 + + + + build-image + + package + + build + push + + + + + harbor + http://${docker.repostory} + + http://${docker.host} + + + ${docker.repostory}/${docker.registry.name}/${project.artifactId}:${project.version} + + + ${project.basedir} + + false + + + + + diff --git a/netty/service/src/main/java/cn/bunny/service/ServiceApplication.java b/netty/service/src/main/java/cn/bunny/service/ServiceApplication.java new file mode 100644 index 0000000..559b8c6 --- /dev/null +++ b/netty/service/src/main/java/cn/bunny/service/ServiceApplication.java @@ -0,0 +1,24 @@ +package cn.bunny.service; + +import lombok.extern.slf4j.Slf4j; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +@ComponentScan(basePackages = {"cn.bunny"}) +@MapperScan("cn.bunny.service.mapper") +@EnableScheduling// 定时任务 +@EnableCaching// 开启缓存注解 +@EnableTransactionManagement// 开启事务注解 +@SpringBootApplication +@Slf4j +public class ServiceApplication { + public static void main(String[] args) { + log.info("ServiceApplication启动..."); + SpringApplication.run(ServiceApplication.class, args); + } +} diff --git a/netty/service/src/main/java/cn/bunny/service/aop/annotation/AutoFill.java b/netty/service/src/main/java/cn/bunny/service/aop/annotation/AutoFill.java new file mode 100644 index 0000000..8c26d32 --- /dev/null +++ b/netty/service/src/main/java/cn/bunny/service/aop/annotation/AutoFill.java @@ -0,0 +1,15 @@ +package cn.bunny.service.aop.annotation; + +import cn.bunny.enums.OperationType; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface AutoFill { + // 数据库操作类型 + OperationType value(); +} diff --git a/netty/service/src/main/java/cn/bunny/service/aop/aspect/AutoFillAspect.java b/netty/service/src/main/java/cn/bunny/service/aop/aspect/AutoFillAspect.java new file mode 100644 index 0000000..7d2fc45 --- /dev/null +++ b/netty/service/src/main/java/cn/bunny/service/aop/aspect/AutoFillAspect.java @@ -0,0 +1,27 @@ +package cn.bunny.service.aop.aspect; + +import lombok.extern.slf4j.Slf4j; +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 +@Slf4j +public class AutoFillAspect { + @Pointcut("execution(* cn.bunny.service.*.*(..))") + public void autoFillPointcut() { + } + + /** + * 之前操作 + * + * @param joinPoint 参数 + */ + @Before("autoFillPointcut()") + public void autoFill(JoinPoint joinPoint) { + log.info("开始进行自动填充"); + } +} diff --git a/netty/service/src/main/java/cn/bunny/service/controller/IndexController.java b/netty/service/src/main/java/cn/bunny/service/controller/IndexController.java new file mode 100644 index 0000000..ac07651 --- /dev/null +++ b/netty/service/src/main/java/cn/bunny/service/controller/IndexController.java @@ -0,0 +1,18 @@ +package cn.bunny.service.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@Tag(name = "访问首页内容") +@RestController +@RequestMapping("/") +public class IndexController { + @Operation(summary = "访问首页", description = "访问首页") + @GetMapping("") + public String index() { + return "欢迎访问 Bunny Java Template"; + } +} diff --git a/netty/service/src/main/java/cn/bunny/service/controller/LoginController.java b/netty/service/src/main/java/cn/bunny/service/controller/LoginController.java new file mode 100644 index 0000000..eeb495f --- /dev/null +++ b/netty/service/src/main/java/cn/bunny/service/controller/LoginController.java @@ -0,0 +1,36 @@ +package cn.bunny.service.controller; + + +import cn.bunny.dto.user.LoginDto; +import cn.bunny.result.Result; +import cn.bunny.service.service.UserService; +import cn.bunny.vo.system.login.LoginVo; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@Tag(name = "登录相关接口") +@RestController +@RequestMapping("/admin") +public class LoginController { + @Autowired + private UserService userService; + + @Operation(summary = "登录接口", description = "后台用户登录接口") + @PostMapping("login") + public Result login(@RequestBody LoginDto loginDto) { + LoginVo vo = userService.login(loginDto); + return Result.success(vo); + } + + @Operation(summary = "发送邮箱验证码", description = "发送邮箱验证码") + @PostMapping("noAuth/sendEmail") + public Result sendEmail(String email) { + userService.sendEmail(email); + return Result.success(); + } +} diff --git a/netty/service/src/main/java/cn/bunny/service/mapper/AdminPowerMapper.java b/netty/service/src/main/java/cn/bunny/service/mapper/AdminPowerMapper.java new file mode 100644 index 0000000..0fc1686 --- /dev/null +++ b/netty/service/src/main/java/cn/bunny/service/mapper/AdminPowerMapper.java @@ -0,0 +1,34 @@ +package cn.bunny.service.mapper; + +import cn.bunny.entity.system.admin.AdminPower; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + *

+ * Mapper 接口 + *

+ * + * @author Bunny + * @since 2024-05-18 + */ +@Mapper +public interface AdminPowerMapper extends BaseMapper { + /** + * 查询用户权限信息 + * + * @param roleIdList 角色id 列表 + * @return 用户对应的权限 + */ + AdminPower[] selectByPowerWithRoleIdList(List roleIdList); + + /** + * 查询用户权限 + * + * @param userId 用户id + * @return 用户权限列表 + */ + List queryByUserIdWithPower(Long userId); +} diff --git a/netty/service/src/main/java/cn/bunny/service/mapper/AdminRoleMapper.java b/netty/service/src/main/java/cn/bunny/service/mapper/AdminRoleMapper.java new file mode 100644 index 0000000..6616caf --- /dev/null +++ b/netty/service/src/main/java/cn/bunny/service/mapper/AdminRoleMapper.java @@ -0,0 +1,26 @@ +package cn.bunny.service.mapper; + +import cn.bunny.entity.system.admin.AdminRole; +import cn.bunny.entity.system.admin.auth.AuthUserRole; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + *

+ * Mapper 接口 + *

+ * + * @author Bunny + * @since 2024-05-18 + */ +@Mapper +public interface AdminRoleMapper extends BaseMapper { + + /** + * 查询用户所有的角色信息 + * + * @param userId 用户id + * @return 用户对应的角色 + */ + AuthUserRole[] selectByRoleWithUserId(Long userId); +} diff --git a/netty/service/src/main/java/cn/bunny/service/mapper/EmailUsersMapper.java b/netty/service/src/main/java/cn/bunny/service/mapper/EmailUsersMapper.java new file mode 100644 index 0000000..ab8b865 --- /dev/null +++ b/netty/service/src/main/java/cn/bunny/service/mapper/EmailUsersMapper.java @@ -0,0 +1,24 @@ +package cn.bunny.service.mapper; + +import cn.bunny.entity.system.email.EmailUsers; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + *

+ * Mapper 接口 + *

+ * + * @author Bunny + * @since 2024-05-14 + */ +@Mapper +public interface EmailUsersMapper extends BaseMapper { + + /** + * 彻底删除邮箱用户 + * + * @param id 用户ID + */ + void thoroughDeleteById(Long id); +} diff --git a/netty/service/src/main/java/cn/bunny/service/mapper/UserMapper.java b/netty/service/src/main/java/cn/bunny/service/mapper/UserMapper.java new file mode 100644 index 0000000..83ac911 --- /dev/null +++ b/netty/service/src/main/java/cn/bunny/service/mapper/UserMapper.java @@ -0,0 +1,25 @@ +package cn.bunny.service.mapper; + +import cn.bunny.entity.system.user.User; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + *

+ * 用户信息 Mapper 接口 + *

+ * + * @author Bunny + * @since 2024-05-18 + */ +@Mapper +public interface UserMapper extends BaseMapper { + /** + * 前台用户登录接口 + * + * @param username 邮箱/昵称 + * @param password 吗,Image + * @return 登录参数 + */ + User login(String username, String password); +} diff --git a/netty/service/src/main/java/cn/bunny/service/security/CustomAuthorizationManagerServiceImpl.java b/netty/service/src/main/java/cn/bunny/service/security/CustomAuthorizationManagerServiceImpl.java new file mode 100644 index 0000000..6c3fccb --- /dev/null +++ b/netty/service/src/main/java/cn/bunny/service/security/CustomAuthorizationManagerServiceImpl.java @@ -0,0 +1,36 @@ +package cn.bunny.service.security; + +import cn.bunny.security.service.CustomAuthorizationManagerService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.authorization.AuthorizationDecision; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.access.intercept.RequestAuthorizationContext; +import org.springframework.stereotype.Service; + +import java.util.function.Supplier; + + +/** + * 自定义权限判断 + * 判断用户有哪些权限 + */ +@Service +@Slf4j +public class CustomAuthorizationManagerServiceImpl implements CustomAuthorizationManagerService { + @Override + public void verify(Supplier authentication, RequestAuthorizationContext requestAuthorizationContext) { + CustomAuthorizationManagerService.super.verify(authentication, requestAuthorizationContext); + } + + @Override + public AuthorizationDecision check(Supplier authentication, RequestAuthorizationContext object) { + String token = object.getRequest().getHeader("token"); + + if (token == null) { + throw new AccessDeniedException(""); + } + + return new AuthorizationDecision(true); + } +} diff --git a/netty/service/src/main/java/cn/bunny/service/security/CustomUserDetailsService.java b/netty/service/src/main/java/cn/bunny/service/security/CustomUserDetailsService.java new file mode 100644 index 0000000..b3d7237 --- /dev/null +++ b/netty/service/src/main/java/cn/bunny/service/security/CustomUserDetailsService.java @@ -0,0 +1,54 @@ +package cn.bunny.service.security; + +import cn.bunny.dto.user.LoginDto; +import cn.bunny.entity.system.admin.AdminRole; +import cn.bunny.entity.system.user.User; +import cn.bunny.security.custom.CustomUser; +import cn.bunny.service.mapper.AdminRoleMapper; +import cn.bunny.service.mapper.UserMapper; +import cn.bunny.service.service.UserService; +import cn.bunny.vo.system.login.LoginVo; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.core.authority.AuthorityUtils; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UsernameNotFoundException; + +import java.util.List; + +@Configuration +public class CustomUserDetailsService implements cn.bunny.security.service.CustomUserDetailsService { + @Autowired + private UserMapper userMapper; + @Autowired + private UserService userService; + @Autowired + private AdminRoleMapper adminRoleMapper; + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + // 根据邮箱查询用户名 + User user = userMapper.selectOne(Wrappers.lambdaQuery().eq(User::getEmail, username)); + List sysRoleList = adminRoleMapper.selectList(null); + // 都为空抛出异常,用户不存在 + if (user == null) { + throw new UsernameNotFoundException(""); + } + + // 查询所有的角色 + List roleAuthoritieList = sysRoleList.stream().map(AdminRole::getRoleCode).toList(); + return new CustomUser(user, AuthorityUtils.createAuthorityList(roleAuthoritieList)); + } + + /** + * 前台用户登录接口 + * + * @param loginDto 登录参数 + * @return 登录后结果返回 + */ + @Override + public LoginVo login(LoginDto loginDto) { + return userService.login(loginDto); + } +} \ No newline at end of file diff --git a/netty/service/src/main/java/cn/bunny/service/service/UserService.java b/netty/service/src/main/java/cn/bunny/service/service/UserService.java new file mode 100644 index 0000000..aad4b8e --- /dev/null +++ b/netty/service/src/main/java/cn/bunny/service/service/UserService.java @@ -0,0 +1,31 @@ +package cn.bunny.service.service; + +import cn.bunny.dto.user.LoginDto; +import cn.bunny.entity.system.user.User; +import cn.bunny.vo.system.login.LoginVo; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + *

+ * 用户信息 服务类 + *

+ * + * @author Bunny + * @since 2024-05-18 + */ +public interface UserService extends IService { + /** + * 前台用户登录接口 + * + * @param loginDto 登录参数 + * @return 登录后结果返回 + */ + LoginVo login(LoginDto loginDto); + + /** + * 发送邮箱验证码 + * + * @param email 邮箱 + */ + void sendEmail(String email); +} diff --git a/netty/service/src/main/java/cn/bunny/service/service/impl/UserServiceImpl.java b/netty/service/src/main/java/cn/bunny/service/service/impl/UserServiceImpl.java new file mode 100644 index 0000000..e0cf57c --- /dev/null +++ b/netty/service/src/main/java/cn/bunny/service/service/impl/UserServiceImpl.java @@ -0,0 +1,122 @@ +package cn.bunny.service.service.impl; + +import cn.bunny.common.service.utils.EmptyUtil; +import cn.bunny.common.service.utils.JwtHelper; +import cn.bunny.dto.user.LoginDto; +import cn.bunny.entity.system.admin.AdminPower; +import cn.bunny.entity.system.admin.auth.AuthUserRole; +import cn.bunny.entity.system.email.EmailSend; +import cn.bunny.entity.system.email.EmailSendInit; +import cn.bunny.entity.system.email.EmailUsers; +import cn.bunny.entity.system.user.User; +import cn.bunny.module.mail.utils.MailSenderUtil; +import cn.bunny.result.constant.ExceptionConstant; +import cn.bunny.result.constant.MailMessageConstant; +import cn.bunny.result.constant.RedisUserConstant; +import cn.bunny.service.mapper.AdminPowerMapper; +import cn.bunny.service.mapper.AdminRoleMapper; +import cn.bunny.service.mapper.EmailUsersMapper; +import cn.bunny.service.mapper.UserMapper; +import cn.bunny.service.service.UserService; +import cn.bunny.vo.system.login.LoginVo; +import cn.hutool.captcha.CaptchaUtil; +import cn.hutool.captcha.CircleCaptcha; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Service; +import org.springframework.util.DigestUtils; + +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.TimeUnit; + +/** + *

+ * 用户信息 服务实现类 + *

+ * + * @author Bunny + * @since 2024-05-18 + */ +@Service +public class UserServiceImpl extends ServiceImpl implements UserService { + @Autowired + private AdminRoleMapper roleMapper; + @Autowired + private AdminPowerMapper powerMapper; + @Autowired + private EmailUsersMapper emailUsersMapper; + @Autowired + private RedisTemplate redisTemplate; + + /** + * 前台用户登录接口 + * + * @param loginDto 登录参数 + * @return 登录后结果返回 + */ + @Override + public LoginVo login(LoginDto loginDto) { + // 判断用户和密码是否为空 + String username = loginDto.getUsername(); + EmptyUtil.isEmpty(username, ExceptionConstant.USERNAME_IS_EMPTY_Exception); + // 密码不能为空 + EmptyUtil.isEmpty(loginDto.getPassword(), ExceptionConstant.PASSWORD_NOT_EMPTY_Exception); + String password = DigestUtils.md5DigestAsHex(loginDto.getPassword().getBytes()); + // 查询数据库,用户对应的角色、权限 + User user = baseMapper.login(username, password); + Long userId = user.getId(); + + // 查询用户所有的角色信息 + AuthUserRole[] roleList = roleMapper.selectByRoleWithUserId(userId); + List roleCodeList = Arrays.stream(roleList).map(AuthUserRole::getRoleCode).toList(); + List roleIdList = Arrays.stream(roleList).map(AuthUserRole::getRoleId).toList(); + // 查询用户权限信息 + AdminPower[] adminPowerList = powerMapper.selectByPowerWithRoleIdList(roleIdList); + List powerCodeList = Arrays.stream(adminPowerList).map(AdminPower::getPowerCode).toList(); + + // 设置返回类型 + LoginVo loginVo = new LoginVo(); + BeanUtils.copyProperties(user, loginVo); + String token = JwtHelper.createToken(loginVo.getId(), loginVo.getEmail(), 7); + loginVo.setToken(token); + loginVo.setRoleList(roleCodeList); + loginVo.setPowerList(powerCodeList); + + return loginVo; + } + + /** + * 发送邮箱验证码 + * + * @param email 邮箱 + */ + @Override + public void sendEmail(String email) { + // 从数据库中获取发送邮箱参数 + EmailUsers emailUsers = emailUsersMapper.selectOne(Wrappers.lambdaQuery().eq(EmailUsers::getIsDefault, 1)); + EmailSendInit emailSendInit = new EmailSendInit(); + // 判断发送邮箱邮件是否为空 + EmptyUtil.isEmpty(emailUsers, MailMessageConstant.EMAIL_CONFIG_NOT_FOUND); + BeanUtils.copyProperties(emailUsers, emailSendInit); + emailSendInit.setUsername(emailUsers.getEmail()); + // 生成验证码 + MailSenderUtil mailSenderUtil = new MailSenderUtil(emailSendInit); + CircleCaptcha captcha = CaptchaUtil.createCircleCaptcha(150, 48, 4, 2); + // 生成验证码和可以 + String code = captcha.getCode(); + // 发送验证码 + EmailSend emailSend = new EmailSend(); + emailSend.setSubject("邮箱验证码"); + emailSend.setMessage(code); + emailSend.setSendTo(email); + emailSend.setIsRichText(false); + mailSenderUtil.sendSimpleEmail(emailSend); + + // 将验证码保存到Redis中,并设置15分钟过期 + redisTemplate.opsForValue().set(RedisUserConstant.ADMIN_EMAIL_CODE_PREFIX + email, code, 15, TimeUnit.MINUTES); + } +} diff --git a/netty/service/src/main/resources/application-dev.yml b/netty/service/src/main/resources/application-dev.yml new file mode 100644 index 0000000..511b357 --- /dev/null +++ b/netty/service/src/main/resources/application-dev.yml @@ -0,0 +1,19 @@ +bunny: + datasource: + host: 106.15.251.123 + port: 3305 + sqlData: guigu-oa + username: root + password: "02120212" + + redis: + host: 47.120.65.66 + port: 6379 + database: 3 + password: "02120212" + + minio: + endpointUrl: "http://129.211.31.58:9000" + bucket-name: ssyx + accessKey: bunny + secretKey: "02120212" \ No newline at end of file diff --git a/netty/service/src/main/resources/application.yml b/netty/service/src/main/resources/application.yml new file mode 100644 index 0000000..fb30ebc --- /dev/null +++ b/netty/service/src/main/resources/application.yml @@ -0,0 +1,66 @@ +server: + port: 8800 + +spring: + profiles: + active: dev + application: + name: bunny-service + + datasource: + type: com.zaxxer.hikari.HikariDataSource + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://${bunny.datasource.host}:${bunny.datasource.port}/${bunny.datasource.sqlData}?serverTimezone=GMT%2B8&useSSL=false&characterEncoding=utf-8&allowPublicKeyRetrieval=true + username: ${bunny.datasource.username} + password: ${bunny.datasource.password} + + data: + redis: + host: ${bunny.redis.host} + port: ${bunny.redis.port} + database: ${bunny.redis.database} + password: ${bunny.redis.password} + lettuce: + pool: + max-active: 20 #最大连接数 + max-wait: -1 #最大阻塞等待时间(负数表示没限制) + max-idle: 5 #最大空闲 + min-idle: 0 #最小空闲 + + jackson: + date-format: yyyy-MM-dd HH:mm:ss + time-zone: GMT+8 + +mybatis-plus: + mapper-locations: classpath:mapper/*.xml + global-config: + db-config: + logic-delete-field: isDelete + configuration: + log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 查看日志 + +logging: + level: + cn.bunny.service.mapper: debug + cn.bunny.service.controller: info + cn.bunny.service.service: info + pattern: + dateformat: HH:mm:ss:SSS + file: + path: "logs/${spring.application.name}" + +bunny: + minio: + endpointUrl: ${bunny.minio.endpointUrl} + accessKey: ${bunny.minio.accessKey} + secretKey: ${bunny.minio.secretKey} + bucket-name: ${bunny.minio.bucket-name} + + snowflake: + datacenterBits: 5 # 数据中心id位数 + workerBits: 5 # 机器id位数 + sequenceBits: 12 # 序列id所占位数 + datacenterId: 1 # 数据中心id,范围0-2^5-1 + workerId: 1 # 机器id,范围0-2^5-1 + twepoch: 1704038400000 # 时间戳起始点(2024-01-01 00::00:00 的毫秒数) + maxBatchCount: 100000 #单次批量生成id的最大数量 默认10万 \ No newline at end of file diff --git a/netty/service/src/main/resources/banner.txt b/netty/service/src/main/resources/banner.txt new file mode 100644 index 0000000..cc77fc2 --- /dev/null +++ b/netty/service/src/main/resources/banner.txt @@ -0,0 +1,16 @@ +-----------------▄██-█▄--------- +-----------------███▄██▄-------- +-----------------███████-------- +-----------------▀███████------- +-------------------██████▄▄----- +-------------------█████████▄--- +-------------------██████▄████-- +-------▄███████████████████████- +-----▄███████████████████████▀-- +---▄██████████████████████------ +---███████████████████████------ +---███████████████████████------ +-▄▄██████████████████████▀------ +-█████████████████▀█████-------- +-▀██████████████▀▀-▀█████▄------ +-------▀▀▀▀▀▀▀▀▀------▀▀▀▀------ \ No newline at end of file diff --git a/netty/service/src/main/resources/favicon.ico b/netty/service/src/main/resources/favicon.ico new file mode 100644 index 0000000..385f8a6 Binary files /dev/null and b/netty/service/src/main/resources/favicon.ico differ diff --git a/netty/service/src/main/resources/logback.xml b/netty/service/src/main/resources/logback.xml new file mode 100644 index 0000000..04ecbe3 --- /dev/null +++ b/netty/service/src/main/resources/logback.xml @@ -0,0 +1,61 @@ + + + + + + + %cyan([%thread]) %yellow(%-5level) %green(%logger{100}).%boldRed(%method)-%boldMagenta(%line) - %blue(%msg%n) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/netty/service/src/main/resources/mapper/AdminPowerMapper.xml b/netty/service/src/main/resources/mapper/AdminPowerMapper.xml new file mode 100644 index 0000000..842029f --- /dev/null +++ b/netty/service/src/main/resources/mapper/AdminPowerMapper.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + id, power_name, power_code, description, create_time, update_time, update_user, is_delete + + + + + + + + diff --git a/netty/service/src/main/resources/mapper/AdminRoleMapper.xml b/netty/service/src/main/resources/mapper/AdminRoleMapper.xml new file mode 100644 index 0000000..8e11472 --- /dev/null +++ b/netty/service/src/main/resources/mapper/AdminRoleMapper.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + id, role_name, description, role_code, create_time, update_time, update_user, is_deleted + + + + + diff --git a/netty/service/src/main/resources/mapper/EmailUsersMapper.xml b/netty/service/src/main/resources/mapper/EmailUsersMapper.xml new file mode 100644 index 0000000..29b04db --- /dev/null +++ b/netty/service/src/main/resources/mapper/EmailUsersMapper.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + id, email, password, host, port, create_time, update_time, update_user, is_delete + + + + + delete + from email_users + where id = #{id} + + + diff --git a/netty/service/src/main/resources/mapper/UserMapper.xml b/netty/service/src/main/resources/mapper/UserMapper.xml new file mode 100644 index 0000000..6ee63e7 --- /dev/null +++ b/netty/service/src/main/resources/mapper/UserMapper.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + id, nick_name, email, password, avatar, sex, person_description, join_time, last_login_time, last_login_ip, last_login_ip_address, total_integral, current_integral, status, create_time, update_time, update_user, is_deleted + + + + + diff --git a/netty/service/src/test/java/cn/bunny/CustomPasswordEncoderTest.java b/netty/service/src/test/java/cn/bunny/CustomPasswordEncoderTest.java new file mode 100644 index 0000000..f952f74 --- /dev/null +++ b/netty/service/src/test/java/cn/bunny/CustomPasswordEncoderTest.java @@ -0,0 +1,27 @@ +package cn.bunny; + +import cn.bunny.security.custom.CustomPasswordEncoder; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.security.crypto.password.PasswordEncoder; + +@SpringBootTest(classes = CustomPasswordEncoder.class) +class CustomPasswordEncoderTest { + @Autowired + private CustomPasswordEncoder customPasswordEncoder; + @Autowired + private PasswordEncoder passwordEncoder; + + @Test + void testCustomPasswordEncoder() { + String encode = customPasswordEncoder.encode("111111"); + System.out.println(encode); + } + + @Test + void testPasswordEncoder() { + String encode = passwordEncoder.encode("111111"); + System.out.println(encode); + } +} \ No newline at end of file