refactor: add manage-notify and manage-file dependencies to manage-app

This commit is contained in:
张翔
2026-03-14 10:38:00 +08:00
parent 2dd239a142
commit 419c046dc4
8 changed files with 296 additions and 4 deletions
+20
View File
@@ -22,6 +22,16 @@
<artifactId>manage-sys</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>cn.novalon.manage</groupId>
<artifactId>manage-notify</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>cn.novalon.manage</groupId>
<artifactId>manage-file</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>cn.novalon.manage</groupId>
<artifactId>manage-db</artifactId>
@@ -35,6 +45,16 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-spring-boot3</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-reactor</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
@@ -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) {
@@ -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);
}
}
@@ -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("系统统计相关操作")));
}
}
@@ -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");
}
}
@@ -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<ServerResponse> 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<ServerResponse> 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<ServerResponse> 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<ServerResponse> 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<ServerResponse> 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<ServerResponse> 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<ServerResponse> 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<ServerResponse> statsRoutes(StatsHandler statsHandler) {
return route()
.GET("/api/stats/overview", statsHandler::getOverview)
.build();
}
@Bean
public RouterFunction<ServerResponse> 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();
}
}
@@ -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);
}
}
@@ -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: