From 419c046dc419a29bd96dbf0a718bca6bd8686ba0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E7=BF=94?= Date: Sat, 14 Mar 2026 10:38:00 +0800 Subject: [PATCH] refactor: add manage-notify and manage-file dependencies to manage-app --- novalon-manage-api/manage-app/pom.xml | 20 +++ .../novalon/manage/app/ManageApplication.java | 4 +- .../manage/app/config/MultipartConfig.java | 19 +++ .../manage/app/config/OpenApiConfig.java | 45 +++++ .../manage/app/config/RateLimitConfig.java | 41 +++++ .../manage/app/config/SystemRouter.java | 155 ++++++++++++++++++ .../manage/app/config/WebFluxConfig.java | 14 ++ .../src/main/resources/application.yml | 2 +- 8 files changed, 296 insertions(+), 4 deletions(-) create mode 100644 novalon-manage-api/manage-app/src/main/java/cn/novalon/manage/app/config/MultipartConfig.java create mode 100644 novalon-manage-api/manage-app/src/main/java/cn/novalon/manage/app/config/OpenApiConfig.java create mode 100644 novalon-manage-api/manage-app/src/main/java/cn/novalon/manage/app/config/RateLimitConfig.java create mode 100644 novalon-manage-api/manage-app/src/main/java/cn/novalon/manage/app/config/SystemRouter.java create mode 100644 novalon-manage-api/manage-app/src/main/java/cn/novalon/manage/app/config/WebFluxConfig.java diff --git a/novalon-manage-api/manage-app/pom.xml b/novalon-manage-api/manage-app/pom.xml index 0be245b..d1cc872 100644 --- a/novalon-manage-api/manage-app/pom.xml +++ b/novalon-manage-api/manage-app/pom.xml @@ -22,6 +22,16 @@ manage-sys ${project.version} + + cn.novalon.manage + manage-notify + ${project.version} + + + cn.novalon.manage + manage-file + ${project.version} + cn.novalon.manage manage-db @@ -35,6 +45,16 @@ org.springframework.boot spring-boot-starter-actuator + + io.github.resilience4j + resilience4j-spring-boot3 + 2.2.0 + + + io.github.resilience4j + resilience4j-reactor + 2.2.0 + io.micrometer micrometer-registry-prometheus diff --git a/novalon-manage-api/manage-app/src/main/java/cn/novalon/manage/app/ManageApplication.java b/novalon-manage-api/manage-app/src/main/java/cn/novalon/manage/app/ManageApplication.java index e601cd7..ba2dea4 100644 --- a/novalon-manage-api/manage-app/src/main/java/cn/novalon/manage/app/ManageApplication.java +++ b/novalon-manage-api/manage-app/src/main/java/cn/novalon/manage/app/ManageApplication.java @@ -3,13 +3,11 @@ package cn.novalon.manage.app; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.context.properties.ConfigurationPropertiesScan; -import org.springframework.context.annotation.ComponentScan; import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories; @SpringBootApplication @ConfigurationPropertiesScan(basePackages = "cn.novalon.manage") -@ComponentScan(basePackages = "cn.novalon.manage") -@EnableR2dbcRepositories(basePackages = "cn.novalon.manage.db.dao") +@EnableR2dbcRepositories(basePackages = {"cn.novalon.manage.db.dao"}) public class ManageApplication { public static void main(String[] args) { diff --git a/novalon-manage-api/manage-app/src/main/java/cn/novalon/manage/app/config/MultipartConfig.java b/novalon-manage-api/manage-app/src/main/java/cn/novalon/manage/app/config/MultipartConfig.java new file mode 100644 index 0000000..8b7110f --- /dev/null +++ b/novalon-manage-api/manage-app/src/main/java/cn/novalon/manage/app/config/MultipartConfig.java @@ -0,0 +1,19 @@ +package cn.novalon.manage.sys.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.codec.multipart.DefaultPartHttpMessageReader; +import org.springframework.http.codec.multipart.MultipartHttpMessageReader; + +@Configuration +public class MultipartConfig { + + @Bean + public MultipartHttpMessageReader multipartHttpMessageReader() { + DefaultPartHttpMessageReader partReader = new DefaultPartHttpMessageReader(); + partReader.setMaxHeadersSize(8192); + partReader.setMaxDiskUsagePerPart(10 * 1024 * 1024); + partReader.setEnableLoggingRequestDetails(true); + return new MultipartHttpMessageReader(partReader); + } +} \ No newline at end of file diff --git a/novalon-manage-api/manage-app/src/main/java/cn/novalon/manage/app/config/OpenApiConfig.java b/novalon-manage-api/manage-app/src/main/java/cn/novalon/manage/app/config/OpenApiConfig.java new file mode 100644 index 0000000..e50384b --- /dev/null +++ b/novalon-manage-api/manage-app/src/main/java/cn/novalon/manage/app/config/OpenApiConfig.java @@ -0,0 +1,45 @@ +package cn.novalon.manage.sys.config; + +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 io.swagger.v3.oas.models.servers.Server; +import io.swagger.v3.oas.models.tags.Tag; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.Arrays; +import java.util.List; + +@Configuration +public class OpenApiConfig { + + @Bean + public OpenAPI customOpenAPI() { + return new OpenAPI() + .info(new Info() + .title("Novalon Manage System API") + .version("1.0.0") + .description("Novalon 管理系统 RESTful API 文档") + .contact(new Contact() + .name("Novalon Team") + .email("support@novalon.cn")) + .license(new License() + .name("Apache 2.0") + .url("https://www.apache.org/licenses/LICENSE-2.0"))) + .servers(List.of( + new Server().url("http://localhost:8080").description("开发环境"), + new Server().url("https://api.novalon.cn").description("生产环境"))) + .tags(Arrays.asList( + new Tag().name("用户管理").description("用户相关操作"), + new Tag().name("角色管理").description("角色相关操作"), + new Tag().name("配置管理").description("系统配置相关操作"), + new Tag().name("字典管理").description("字典数据相关操作"), + new Tag().name("通知管理").description("系统通知相关操作"), + new Tag().name("文件管理").description("文件上传下载相关操作"), + new Tag().name("日志管理").description("操作日志相关操作"), + new Tag().name("认证管理").description("登录认证相关操作"), + new Tag().name("统计信息").description("系统统计相关操作"))); + } +} diff --git a/novalon-manage-api/manage-app/src/main/java/cn/novalon/manage/app/config/RateLimitConfig.java b/novalon-manage-api/manage-app/src/main/java/cn/novalon/manage/app/config/RateLimitConfig.java new file mode 100644 index 0000000..b28d5f7 --- /dev/null +++ b/novalon-manage-api/manage-app/src/main/java/cn/novalon/manage/app/config/RateLimitConfig.java @@ -0,0 +1,41 @@ +package cn.novalon.manage.app.config; + +import io.github.resilience4j.ratelimiter.RateLimiter; +import io.github.resilience4j.ratelimiter.RateLimiterConfig; +import io.github.resilience4j.ratelimiter.RateLimiterRegistry; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.time.Duration; + +@Configuration +public class RateLimitConfig { + + @Value("${rate.limit.limit-for-period:100}") + private int limitForPeriod; + + @Value("${rate.limit.limit-refresh-period:1s}") + private Duration limitRefreshPeriod; + + @Value("${rate.limit.timeout-duration:0}") + private Duration timeoutDuration; + + @Bean + public RateLimiterRegistry rateLimiterRegistry() { + RateLimiterConfig config = RateLimiterConfig.custom() + .limitForPeriod(limitForPeriod) + .limitRefreshPeriod(limitRefreshPeriod) + .timeoutDuration(timeoutDuration) + .build(); + + return RateLimiterRegistry.of(config); + } + + @Bean + @Qualifier("apiRateLimiter") + public RateLimiter apiRateLimiter(RateLimiterRegistry registry) { + return registry.rateLimiter("apiRateLimiter"); + } +} \ No newline at end of file diff --git a/novalon-manage-api/manage-app/src/main/java/cn/novalon/manage/app/config/SystemRouter.java b/novalon-manage-api/manage-app/src/main/java/cn/novalon/manage/app/config/SystemRouter.java new file mode 100644 index 0000000..2b8ebfc --- /dev/null +++ b/novalon-manage-api/manage-app/src/main/java/cn/novalon/manage/app/config/SystemRouter.java @@ -0,0 +1,155 @@ +package cn.novalon.manage.sys.config; + +import cn.novalon.manage.sys.handler.auth.SysAuthHandler; +import cn.novalon.manage.sys.handler.config.SysConfigHandler; +import cn.novalon.manage.sys.handler.dictionary.DictionaryHandler; +import cn.novalon.manage.sys.handler.dict.SysDictHandler; +import cn.novalon.manage.sys.handler.log.SysLogHandler; +import cn.novalon.manage.sys.handler.menu.MenuHandler; +import cn.novalon.manage.sys.handler.role.SysRoleHandler; +import cn.novalon.manage.sys.handler.stats.StatsHandler; +import cn.novalon.manage.sys.handler.user.SysUserHandler; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.reactive.function.server.RouterFunction; +import org.springframework.web.reactive.function.server.ServerResponse; + +import static org.springframework.web.reactive.function.server.RouterFunctions.route; + +/** + * 系统路由配置类 + * + * 文件定义:配置WebFlux函数式路由,将HTTP请求映射到对应的Handler方法 + * 涉及业务:用户、角色、字典、菜单、公告、文件等所有RESTful API路由 + * 算法:使用RouterFunctions.route()构建函数式路由规则 + * + * @author 张翔 + * @date 2026-03-13 + */ +@Configuration +public class SystemRouter { + + @Bean + public RouterFunction dictionaryRoutes(DictionaryHandler dictionaryHandler) { + return route() + .GET("/api/dictionaries", dictionaryHandler::getAllDictionaries) + .GET("/api/dictionaries/{id}", dictionaryHandler::getDictionaryById) + .GET("/api/dictionaries/type/{type}", dictionaryHandler::getDictionariesByType) + .GET("/api/dictionaries/check/exists", dictionaryHandler::checkTypeAndCodeExists) + .POST("/api/dictionaries", dictionaryHandler::createDictionary) + .PUT("/api/dictionaries/{id}", dictionaryHandler::updateDictionary) + .DELETE("/api/dictionaries/{id}", dictionaryHandler::deleteDictionary) + .build(); + } + + @Bean + public RouterFunction userRoutes(SysUserHandler userHandler) { + return route() + .GET("/api/users", userHandler::getAllUsers) + .GET("/api/users/page", userHandler::getUsersByPage) + .GET("/api/users/count", userHandler::getUserCount) + .GET("/api/users/{id}", userHandler::getUserById) + .GET("/api/users/username/{username}", userHandler::getUserByUsername) + .POST("/api/users", userHandler::createUser) + .PUT("/api/users/{id}", userHandler::updateUser) + .DELETE("/api/users/{id}", userHandler::deleteUser) + .POST("/api/users/{id}/password", userHandler::changePassword) + .DELETE("/api/users/{id}/logical", userHandler::logicalDeleteUser) + .POST("/api/users/logical-delete", userHandler::logicalDeleteUsers) + .POST("/api/users/{id}/restore", userHandler::restoreUser) + .POST("/api/users/restore", userHandler::restoreUsers) + .GET("/api/users/check/username", userHandler::checkUsernameExists) + .GET("/api/users/check/email", userHandler::checkEmailExists) + .build(); + } + + @Bean + public RouterFunction menuRoutes(MenuHandler menuHandler) { + return route() + .GET("/api/menus", menuHandler::getAllMenus) + .GET("/api/menus/tree", menuHandler::getMenuTree) + .GET("/api/menus/{id}", menuHandler::getMenuById) + .POST("/api/menus", menuHandler::createMenu) + .PUT("/api/menus/{id}", menuHandler::updateMenu) + .DELETE("/api/menus/{id}", menuHandler::deleteMenu) + .build(); + } + + @Bean + public RouterFunction roleRoutes(SysRoleHandler roleHandler) { + return route() + .GET("/api/roles", roleHandler::getAllRoles) + .GET("/api/roles/page", roleHandler::getRolesByPage) + .GET("/api/roles/count", roleHandler::getRoleCount) + .GET("/api/roles/name/{roleName}", roleHandler::getRoleByName) + .GET("/api/roles/check-name", roleHandler::checkNameExists) + .GET("/api/roles/{id}", roleHandler::getRoleById) + .POST("/api/roles", roleHandler::createRole) + .PUT("/api/roles/{id}", roleHandler::updateRole) + .DELETE("/api/roles/{id}", roleHandler::deleteRole) + .POST("/api/roles/{id}/restore", roleHandler::restoreRole) + .build(); + } + + @Bean + public RouterFunction configRoutes(SysConfigHandler configHandler) { + return route() + .GET("/api/config", configHandler::getAllConfigs) + .GET("/api/config/{id}", configHandler::getConfigById) + .GET("/api/config/key/{configKey}", configHandler::getConfigByKey) + .POST("/api/config", configHandler::createConfig) + .PUT("/api/config/{id}", configHandler::updateConfig) + .DELETE("/api/config/{id}", configHandler::deleteConfig) + .build(); + } + + @Bean + public RouterFunction logRoutes(SysLogHandler logHandler) { + return route() + .GET("/api/logs/login", logHandler::getAllLoginLogs) + .GET("/api/logs/login/page", logHandler::getLoginLogsByPage) + .GET("/api/logs/login/count", logHandler::getLoginLogCount) + .GET("/api/logs/login/{id}", logHandler::getLoginLogById) + .POST("/api/logs/login", logHandler::createLoginLog) + .GET("/api/logs/exception", logHandler::getAllExceptionLogs) + .GET("/api/logs/exception/page", logHandler::getExceptionLogsByPage) + .GET("/api/logs/exception/count", logHandler::getExceptionLogCount) + .GET("/api/logs/exception/{id}", logHandler::getExceptionLogById) + .POST("/api/logs/exception", logHandler::createExceptionLog) + .build(); + } + + @Bean + public RouterFunction authRoutes(SysAuthHandler authHandler) { + return route() + .POST("/api/auth/login", authHandler::login) + .POST("/api/auth/register", authHandler::register) + .POST("/api/auth/logout", authHandler::logout) + .build(); + } + + @Bean + public RouterFunction statsRoutes(StatsHandler statsHandler) { + return route() + .GET("/api/stats/overview", statsHandler::getOverview) + .build(); + } + + @Bean + public RouterFunction dictRoutes(SysDictHandler dictHandler) { + return route() + .GET("/api/dict/types", dictHandler::getAllDictTypes) + .GET("/api/dict/types/{id}", dictHandler::getDictTypeById) + .GET("/api/dict/types/type/{dictType}", dictHandler::getDictTypeByType) + .POST("/api/dict/types", dictHandler::createDictType) + .PUT("/api/dict/types/{id}", dictHandler::updateDictType) + .DELETE("/api/dict/types/{id}", dictHandler::deleteDictType) + .GET("/api/dict/data", dictHandler::getAllDictData) + .GET("/api/dict/data/type/{dictType}", dictHandler::getDictDataByType) + .GET("/api/dict/data/{id}", dictHandler::getDictDataById) + .POST("/api/dict/data", dictHandler::createDictData) + .PUT("/api/dict/data/{id}", dictHandler::updateDictData) + .DELETE("/api/dict/data/{id}", dictHandler::deleteDictData) + .build(); + } +} \ No newline at end of file diff --git a/novalon-manage-api/manage-app/src/main/java/cn/novalon/manage/app/config/WebFluxConfig.java b/novalon-manage-api/manage-app/src/main/java/cn/novalon/manage/app/config/WebFluxConfig.java new file mode 100644 index 0000000..cafa2a4 --- /dev/null +++ b/novalon-manage-api/manage-app/src/main/java/cn/novalon/manage/app/config/WebFluxConfig.java @@ -0,0 +1,14 @@ +package cn.novalon.manage.sys.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.http.codec.ServerCodecConfigurer; +import org.springframework.web.reactive.config.WebFluxConfigurer; + +@Configuration +public class WebFluxConfig implements WebFluxConfigurer { + + @Override + public void configureHttpMessageCodecs(ServerCodecConfigurer configurer) { + configurer.defaultCodecs().maxInMemorySize(16 * 1024 * 1024); + } +} \ No newline at end of file diff --git a/novalon-manage-api/manage-app/src/main/resources/application.yml b/novalon-manage-api/manage-app/src/main/resources/application.yml index c9b70a7..bd5c468 100644 --- a/novalon-manage-api/manage-app/src/main/resources/application.yml +++ b/novalon-manage-api/manage-app/src/main/resources/application.yml @@ -5,7 +5,7 @@ spring: application: name: manage-app r2dbc: - url: r2dbc:postgresql://${DB_HOST:localhost}:${DB_PORT:5432}/${DB_NAME:novalon_manage} + url: r2dbc:postgresql://${DB_HOST:localhost}:${DB_PORT:55432}/${DB_NAME:manage_system} username: ${DB_USERNAME:postgres} password: ${DB_PASSWORD:postgres} pool: