diff --git a/pom.xml b/pom.xml index 51ac360..f37c33c 100644 --- a/pom.xml +++ b/pom.xml @@ -56,14 +56,8 @@ org.springdoc springdoc-openapi-starter-webmvc-ui - 2.0.2 + 2.1.0 - - org.springdoc - springdoc-openapi-security - 1.7.0 - - diff --git a/src/main/java/club/joylink/xiannccda/XianNccDaApplication.java b/src/main/java/club/joylink/xiannccda/XianNccDaApplication.java index 961ae38..fa293b8 100644 --- a/src/main/java/club/joylink/xiannccda/XianNccDaApplication.java +++ b/src/main/java/club/joylink/xiannccda/XianNccDaApplication.java @@ -1,5 +1,10 @@ package club.joylink.xiannccda; +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.enums.SecuritySchemeIn; +import io.swagger.v3.oas.annotations.enums.SecuritySchemeType; +import io.swagger.v3.oas.annotations.info.Info; +import io.swagger.v3.oas.annotations.security.SecurityScheme; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @@ -8,7 +13,8 @@ import org.springframework.web.servlet.config.annotation.EnableWebMvc; @SpringBootApplication @EnableCaching -@MapperScan("club.joylink.xiannccda.mapper") +@OpenAPIDefinition(info = @Info(title = "西安NCC调度辅助决策系统API", version = "0.1")) +@SecurityScheme(name = "jwt", scheme = "bearer", type = SecuritySchemeType.HTTP, in = SecuritySchemeIn.HEADER) public class XianNccDaApplication { public static void main(String[] args) { diff --git a/src/main/java/club/joylink/xiannccda/alert/Alert.java b/src/main/java/club/joylink/xiannccda/alert/core/Alert.java similarity index 97% rename from src/main/java/club/joylink/xiannccda/alert/Alert.java rename to src/main/java/club/joylink/xiannccda/alert/core/Alert.java index a638011..f111d04 100644 --- a/src/main/java/club/joylink/xiannccda/alert/Alert.java +++ b/src/main/java/club/joylink/xiannccda/alert/core/Alert.java @@ -1,4 +1,4 @@ -package club.joylink.xiannccda.alert; +package club.joylink.xiannccda.alert.core; import java.util.ArrayList; import java.util.List; @@ -24,7 +24,7 @@ public class Alert { /** * 任务执行线程池 */ - static ScheduledExecutorService Executor = Executors.newScheduledThreadPool(2); + static ScheduledExecutorService Executor = Executors.newScheduledThreadPool(1); static boolean started = false; static final int interval = 100; // 执行器默认间隔,单位ms diff --git a/src/main/java/club/joylink/xiannccda/alert/AlertEvent.java b/src/main/java/club/joylink/xiannccda/alert/core/AlertEvent.java similarity index 92% rename from src/main/java/club/joylink/xiannccda/alert/AlertEvent.java rename to src/main/java/club/joylink/xiannccda/alert/core/AlertEvent.java index 09557ec..29e514e 100644 --- a/src/main/java/club/joylink/xiannccda/alert/AlertEvent.java +++ b/src/main/java/club/joylink/xiannccda/alert/core/AlertEvent.java @@ -1,4 +1,4 @@ -package club.joylink.xiannccda.alert; +package club.joylink.xiannccda.alert.core; import java.time.LocalDateTime; import java.util.EventObject; @@ -6,6 +6,7 @@ import java.util.EventObject; public abstract class AlertEvent extends EventObject { private final LocalDateTime dateTime; + /** * Constructs a prototypical Event. * @@ -16,6 +17,7 @@ public abstract class AlertEvent extends EventObject { super(source); this.dateTime = LocalDateTime.now(); } + public AlertEvent(Object source, LocalDateTime time) { super(source); this.dateTime = time; diff --git a/src/main/java/club/joylink/xiannccda/alert/AlertInfo.java b/src/main/java/club/joylink/xiannccda/alert/core/AlertInfo.java similarity index 87% rename from src/main/java/club/joylink/xiannccda/alert/AlertInfo.java rename to src/main/java/club/joylink/xiannccda/alert/core/AlertInfo.java index 3d4644a..7bbe2aa 100644 --- a/src/main/java/club/joylink/xiannccda/alert/AlertInfo.java +++ b/src/main/java/club/joylink/xiannccda/alert/core/AlertInfo.java @@ -1,4 +1,4 @@ -package club.joylink.xiannccda.alert; +package club.joylink.xiannccda.alert.core; import java.time.LocalDateTime; diff --git a/src/main/java/club/joylink/xiannccda/alert/AlertListener.java b/src/main/java/club/joylink/xiannccda/alert/core/AlertListener.java similarity index 65% rename from src/main/java/club/joylink/xiannccda/alert/AlertListener.java rename to src/main/java/club/joylink/xiannccda/alert/core/AlertListener.java index 557578d..1d5c0b0 100644 --- a/src/main/java/club/joylink/xiannccda/alert/AlertListener.java +++ b/src/main/java/club/joylink/xiannccda/alert/core/AlertListener.java @@ -1,5 +1,6 @@ -package club.joylink.xiannccda.alert; +package club.joylink.xiannccda.alert.core; public interface AlertListener { + void accept(AE event); } diff --git a/src/main/java/club/joylink/xiannccda/alert/AlertMonitoringTask.java b/src/main/java/club/joylink/xiannccda/alert/core/AlertMonitoringTask.java similarity index 74% rename from src/main/java/club/joylink/xiannccda/alert/AlertMonitoringTask.java rename to src/main/java/club/joylink/xiannccda/alert/core/AlertMonitoringTask.java index 232e2e1..027d4c5 100644 --- a/src/main/java/club/joylink/xiannccda/alert/AlertMonitoringTask.java +++ b/src/main/java/club/joylink/xiannccda/alert/core/AlertMonitoringTask.java @@ -1,4 +1,4 @@ -package club.joylink.xiannccda.alert; +package club.joylink.xiannccda.alert.core; /** * 报警监测任务 diff --git a/src/main/java/club/joylink/xiannccda/config/SwaggerConfig.java b/src/main/java/club/joylink/xiannccda/config/SwaggerConfig.java deleted file mode 100644 index 12b9aba..0000000 --- a/src/main/java/club/joylink/xiannccda/config/SwaggerConfig.java +++ /dev/null @@ -1,29 +0,0 @@ -package club.joylink.xiannccda.config; - -import io.swagger.v3.oas.models.Components; -import io.swagger.v3.oas.models.OpenAPI; -import io.swagger.v3.oas.models.info.Info; -import io.swagger.v3.oas.models.security.SecurityScheme; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -public class SwaggerConfig { - - @Bean - public OpenAPI customOpenAPI() { - return new OpenAPI() - .components(new Components() - // 设置 spring security jwt accessToken 认证的请求头 Authorization: Bearer xxx.xxx.xxx - .addSecuritySchemes("authScheme", new SecurityScheme() - .type(SecurityScheme.Type.HTTP) - .bearerFormat("JWT") - .scheme("bearer"))) - .info(new Info() - .title("My API") - .description("My API description") - .version("1.0.0") - ); - } - -} diff --git a/src/main/java/club/joylink/xiannccda/configuration/MybatisPlusConfig.java b/src/main/java/club/joylink/xiannccda/configuration/MybatisPlusConfig.java new file mode 100644 index 0000000..2dae02f --- /dev/null +++ b/src/main/java/club/joylink/xiannccda/configuration/MybatisPlusConfig.java @@ -0,0 +1,23 @@ +package club.joylink.xiannccda.configuration; + +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.PaginationInnerInterceptor; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +@MapperScan("club.joylink.xiannccda.mapper") +public class MybatisPlusConfig { + + @Bean + public MybatisPlusInterceptor mybatisPlusInterceptor() { + MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); + interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); + interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor()); + return interceptor; + } + +} diff --git a/src/main/java/club/joylink/xiannccda/configuration/ResponseExceptionHandler.java b/src/main/java/club/joylink/xiannccda/configuration/ResponseExceptionHandler.java new file mode 100644 index 0000000..f447336 --- /dev/null +++ b/src/main/java/club/joylink/xiannccda/configuration/ResponseExceptionHandler.java @@ -0,0 +1,33 @@ +package club.joylink.xiannccda.configuration; + +import club.joylink.xiannccda.exception.BusinessException; +import org.springframework.http.HttpStatus; +import org.springframework.http.ProblemDetail; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@RestControllerAdvice +public class ResponseExceptionHandler { + + @ExceptionHandler(BusinessException.class) + public ProblemDetail businessExceptionHandler(BusinessException e) { + ProblemDetail problemDetail = ProblemDetail.forStatus(HttpStatus.INTERNAL_SERVER_ERROR); + problemDetail.setProperty("code", e.getCode()); + problemDetail.setTitle(e.getMessage()); + return problemDetail; + } + + /** + * 系统未处理异常捕获 + * + * @param e + * @return + */ + @ExceptionHandler(Exception.class) + public ProblemDetail UncaughtExceptionHandler(Exception e) { + ProblemDetail problemDetail = ProblemDetail.forStatus(HttpStatus.INTERNAL_SERVER_ERROR); + problemDetail.setTitle("未处理异常"); + problemDetail.setDetail(e.getMessage()); + return problemDetail; + } +} diff --git a/src/main/java/club/joylink/xiannccda/config/ServerCorsConfiguration.java b/src/main/java/club/joylink/xiannccda/configuration/ServerCorsConfiguration.java similarity index 85% rename from src/main/java/club/joylink/xiannccda/config/ServerCorsConfiguration.java rename to src/main/java/club/joylink/xiannccda/configuration/ServerCorsConfiguration.java index 4753b4a..0192f32 100644 --- a/src/main/java/club/joylink/xiannccda/config/ServerCorsConfiguration.java +++ b/src/main/java/club/joylink/xiannccda/configuration/ServerCorsConfiguration.java @@ -1,4 +1,4 @@ -package club.joylink.xiannccda.config; +package club.joylink.xiannccda.configuration; import java.util.Arrays; import org.springframework.context.annotation.Bean; @@ -6,8 +6,6 @@ import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfigurationSource; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; -import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; /** * @author Walker-sheng diff --git a/src/main/java/club/joylink/xiannccda/config/SpringSecurityConfiguration.java b/src/main/java/club/joylink/xiannccda/configuration/SpringSecurityConfiguration.java similarity index 70% rename from src/main/java/club/joylink/xiannccda/config/SpringSecurityConfiguration.java rename to src/main/java/club/joylink/xiannccda/configuration/SpringSecurityConfiguration.java index 13114ed..53da929 100644 --- a/src/main/java/club/joylink/xiannccda/config/SpringSecurityConfiguration.java +++ b/src/main/java/club/joylink/xiannccda/configuration/SpringSecurityConfiguration.java @@ -1,4 +1,4 @@ -package club.joylink.xiannccda.config; +package club.joylink.xiannccda.configuration; import com.nimbusds.jose.jwk.JWK; import com.nimbusds.jose.jwk.JWKSet; @@ -6,17 +6,18 @@ import com.nimbusds.jose.jwk.RSAKey; import com.nimbusds.jose.jwk.source.ImmutableJWKSet; import com.nimbusds.jose.jwk.source.JWKSource; import com.nimbusds.jose.proc.SecurityContext; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; import java.util.ArrayList; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; -import org.springframework.security.config.Customizer; 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.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; @@ -43,19 +44,22 @@ import org.springframework.web.cors.CorsConfigurationSource; @EnableWebSecurity public class SpringSecurityConfiguration { + @Value("${key.public}") + RSAPublicKey publicKey; + @Value("${key.private}") + RSAPrivateKey privateKey; + @Autowired CorsConfigurationSource corsConfigurationSource; static List AuthWhiteList = new ArrayList<>(); static { - AuthWhiteList.add(AntPathRequestMatcher.antMatcher(HttpMethod.POST, "/api/userInfo/register")); - AuthWhiteList.add(AntPathRequestMatcher.antMatcher("/error")); + AuthWhiteList.add(AntPathRequestMatcher.antMatcher(HttpMethod.POST, "/api/user/register")); + AuthWhiteList.add(AntPathRequestMatcher.antMatcher(HttpMethod.POST, "/api/user/login")); AuthWhiteList.add(AntPathRequestMatcher.antMatcher("/swagger-ui/**")); AuthWhiteList.add(AntPathRequestMatcher.antMatcher("/api-docs/**")); AuthWhiteList.add(AntPathRequestMatcher.antMatcher("/swagger-ui.html")); - AuthWhiteList.add(AntPathRequestMatcher.antMatcher("/test")); - AuthWhiteList.add(AntPathRequestMatcher.antMatcher("/test2")); } @Bean @@ -63,7 +67,10 @@ public class SpringSecurityConfiguration { // @formatter:off http .authorizeHttpRequests((authorize) -> { - authorize.requestMatchers("/favicon.ico","/js2/**","/swagger-resources/configuration/security","/swagger-resources/configuration/ui","/swagger-resources/**","/upload/**","/v2/**","/swagger-ui.html","/webjars/**","/img/**","/css/**","/js/**","/image/**").permitAll(); +// authorize.requestMatchers("/favicon.ico", "/js2/**", +// "/swagger-resources/configuration/security", "/swagger-resources/configuration/ui", +// "/swagger-resources/**", "/upload/**", "/v2/**", "/swagger-ui.html", "/webjars/**", +// "/img/**", "/css/**", "/js/**", "/image/**").permitAll(); AuthWhiteList.forEach(matcher -> authorize.requestMatchers(matcher).permitAll()); authorize.requestMatchers(HttpMethod.OPTIONS).permitAll(); authorize.anyRequest().authenticated(); @@ -71,8 +78,8 @@ public class SpringSecurityConfiguration { ) .cors(corsConfig -> corsConfig.configurationSource(corsConfigurationSource)) .csrf((csrf) -> csrf.disable()) - .httpBasic(Customizer.withDefaults()) - .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt) +// .httpBasic(Customizer.withDefaults()) + .oauth2ResourceServer(oauth2 -> oauth2.jwt(jwt -> jwt.decoder(this.jwtDecoder()))) .sessionManagement( (session) -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .exceptionHandling((exceptions) -> exceptions @@ -108,38 +115,28 @@ public class SpringSecurityConfiguration { return new BCryptPasswordEncoder(13); } -// /** -// * JWT解码器 -// * -// * @return -// */ -// @Bean -// public JwtDecoder jwtDecoder() { -// return NimbusJwtDecoder.withPublicKey(this.rsaKeys.publicKey).build(); -// } -// -// /** -// * JWT编码器 -// * -// * @return -// */ -// @Bean -// public JwtEncoder jwtEncoder() { -// JWK jwk = new RSAKey.Builder(this.rsaKeys.publicKey).privateKey(this.rsaKeys.privateKey) -// .build(); -// JWKSource jwks = new ImmutableJWKSet<>(new JWKSet(jwk)); -// return new NimbusJwtEncoder(jwks); -// } + /** + * JWT解码器 + * + * @return + */ + @Bean + public JwtDecoder jwtDecoder() { + return NimbusJwtDecoder.withPublicKey(this.publicKey).build(); + } - // /** - // * 自定义方法安全授权处理器 - // * PS:此方法必须是static的,以确保Spring在初始化Spring Security的方法Security@Configuration类之前发布它 - // */ - // @Bean - // static MethodSecurityExpressionHandler methodSecurityExpressionHandler() { - // DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler(); - // handler.setDefaultRolePrefix(""); // 原本SpringSecurity默认角色前缀为'ROLE_' - // return handler; - // } + /** + * JWT编码器 + * + * @return + */ + @Bean + public JwtEncoder jwtEncoder() { + JWK jwk = new RSAKey.Builder(this.publicKey) + .privateKey(this.privateKey) + .build(); + JWKSource jwks = new ImmutableJWKSet<>(new JWKSet(jwk)); + return new NimbusJwtEncoder(jwks); + } } diff --git a/src/main/java/club/joylink/xiannccda/controller/TestController.java b/src/main/java/club/joylink/xiannccda/controller/TestController.java deleted file mode 100644 index baa2678..0000000 --- a/src/main/java/club/joylink/xiannccda/controller/TestController.java +++ /dev/null @@ -1,49 +0,0 @@ -package club.joylink.xiannccda.controller; - -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.media.Content; -import io.swagger.v3.oas.annotations.media.Schema; -import io.swagger.v3.oas.annotations.responses.ApiResponse; -import io.swagger.v3.oas.annotations.tags.Tag; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; -import org.springframework.http.MediaType; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; - -@RestController -@RequestMapping() -@Tag(name = "这是一个测试api名称", description = "接口描述") -public class TestController { - - @GetMapping("test") - @Operation(summary = "测试", description = "测试接口") - @ApiResponse(description = "返回字符串") - public String d() { - return "aaaaaaaaaaaaaaaa"; - } - - - @GetMapping("test2") - @Operation(summary = "测试接口2", description = "测试接口描述") - @ApiResponse(description = "返回test对象") - public TestData test2( - @Parameter(name = "name", description = "这是一个那么参数") @RequestParam(name = "name") String name) { - return new TestData("name", "psss"); - } - - @Data - @NoArgsConstructor - @AllArgsConstructor - @Schema(name = "返回对象数据", description = "返回对象数据描述") - public static class TestData { - - @Schema(name = "name", description = "属性描述") - private String name; - private String passwd; - } -} diff --git a/src/main/java/club/joylink/xiannccda/controller/UserController.java b/src/main/java/club/joylink/xiannccda/controller/UserController.java new file mode 100644 index 0000000..dda5414 --- /dev/null +++ b/src/main/java/club/joylink/xiannccda/controller/UserController.java @@ -0,0 +1,62 @@ +package club.joylink.xiannccda.controller; + +import club.joylink.xiannccda.dto.LoginInfoDto; +import club.joylink.xiannccda.entity.User; +import club.joylink.xiannccda.entity.User.Register; +import club.joylink.xiannccda.service.UserService; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; +import java.util.List; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +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; + +/** + * 用户管理接口 + * + * @author walker-sheng + * @since 2023-06-01 + */ +@RestController +@RequestMapping("/api/user") +@Tag(name = "用户管理接口", description = "用户管理接口描述") +public class UserController { + + private UserService userService; + + public UserController(UserService userService) { + this.userService = userService; + } + + @PostMapping("/register") + @Operation(summary = "用户注册") + @ApiResponse(description = "用户注册结果") + public String register(@RequestBody @Validated(Register.class) User user) { + return this.userService.register(user); + } + + @PostMapping("/login") + @Operation(summary = "用户登录") + @ApiResponse(description = "授权的jwt") + public String login(@RequestBody LoginInfoDto loginInfo) { + return this.userService.login(loginInfo); + } + + @GetMapping("/paging") + @SecurityRequirement(name = "jwt") + @Operation(summary = "分页查询用户") + @ApiResponse(description = "用户列表") + public Page pageQuery() { + return this.userService.pageQuery(); + } + +} diff --git a/src/main/java/club/joylink/xiannccda/controller/advice/CommonResponseBody.java b/src/main/java/club/joylink/xiannccda/controller/advice/CommonResponseBody.java deleted file mode 100644 index d61cbdc..0000000 --- a/src/main/java/club/joylink/xiannccda/controller/advice/CommonResponseBody.java +++ /dev/null @@ -1,103 +0,0 @@ -package club.joylink.xiannccda.controller.advice; - - -import club.joylink.xiannccda.exception.BusinessException; -import club.joylink.xiannccda.vo.CommonJsonResponse; -import club.joylink.xiannccda.vo.ResponseConsts; -import com.google.protobuf.AbstractMessageLite; -import java.util.List; -import lombok.extern.slf4j.Slf4j; -import org.springframework.core.MethodParameter; -import org.springframework.http.MediaType; -import org.springframework.http.server.ServerHttpRequest; -import org.springframework.http.server.ServerHttpResponse; -import org.springframework.validation.ObjectError; -import org.springframework.web.bind.MethodArgumentNotValidException; -import org.springframework.web.bind.MissingServletRequestParameterException; -import org.springframework.web.bind.annotation.ControllerAdvice; -import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.bind.annotation.ResponseBody; -import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; - -@ControllerAdvice -@Slf4j -public class CommonResponseBody implements ResponseBodyAdvice { - - @Override - public boolean supports(MethodParameter returnType, Class converterType) { - return returnType.getMethod() != null && !returnType.getMethod().getReturnType().getSimpleName() - .equals("CommonJsonResponse"); - } - - @Override - public Object beforeBodyWrite(Object body, MethodParameter returnType, - MediaType selectedContentType, - Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { - //byte[] - if (body instanceof byte[]) { - response.getHeaders().setContentType(MediaType.APPLICATION_OCTET_STREAM); - return body; - } - //byte[] - if (body instanceof AbstractMessageLite) { - response.getHeaders().setContentType(MediaType.APPLICATION_OCTET_STREAM); - final AbstractMessageLite proto = (AbstractMessageLite) body; - return proto.toByteArray(); - } - if (body instanceof CommonJsonResponse) { - return body; - } - if (request.getURI().getPath().startsWith("/swagger")) { - return body; - } - if (request.getURI().getPath().startsWith("/api-docs")) { - return body; - } - if (request.getURI().getPath().equals("/api/userinfo/ifRegisted")) { - return body; - } - if (request.getURI().getPath().equals("/api/userinfo/identity")) { - return body; - } - CommonJsonResponse commonJsonResponse = CommonJsonResponse.newSuccessResponse(body); - if (returnType.getMethod().getReturnType().equals(String.class) || body instanceof String) { - return commonJsonResponse.toJSONString(); - } - return commonJsonResponse; - } - - - @ExceptionHandler({Exception.class}) - @ResponseBody - public CommonJsonResponse handleException(Exception e) { - if (e instanceof MethodArgumentNotValidException) { - // 参数验证异常处理 - MethodArgumentNotValidException validException = (MethodArgumentNotValidException) e; - List errorList = validException.getBindingResult().getAllErrors(); - StringBuffer sb = new StringBuffer(); - errorList.forEach(error -> - sb.append(error.getDefaultMessage()).append(";")); - log.error("【参数校验异常】{}", e); - return CommonJsonResponse.newErrorResponse(ResponseConsts.VALIDATE_ERROR.getCode(), - sb.toString()); - } - if (e instanceof BusinessException) { - BusinessException be = (BusinessException) e; - log.error("【业务异常】{}", e); - return CommonJsonResponse.newErrorResponse(be.getCode(), be.getVoMessage()); - } else if (e instanceof MissingServletRequestParameterException) { - log.error("【接口参数异常】", e); - return CommonJsonResponse.newErrorResponse(ResponseConsts.VALIDATE_ERROR.getCode(), - "接口参数异常"); - }/* else if (e instanceof SimulationException) { - club.joylink.rtss.simulation.cbtc.exception.SimulationException simulationException = (club.joylink.rtss.simulation.cbtc.exception.SimulationException) e; - log.error("【仿真系统异常】{}", e); - return CommonJsonResponse.newErrorResponse(simulationException.getCode(), - simulationException.getMessage()); - }*/ - - log.error("【系统异常】{}", e); - return CommonJsonResponse.newErrorResponse(); - } - -} diff --git a/src/main/java/club/joylink/xiannccda/dto/LoginInfoDto.java b/src/main/java/club/joylink/xiannccda/dto/LoginInfoDto.java new file mode 100644 index 0000000..ad2da8e --- /dev/null +++ b/src/main/java/club/joylink/xiannccda/dto/LoginInfoDto.java @@ -0,0 +1,19 @@ +package club.joylink.xiannccda.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +@Schema(name = "登录信息") +public class LoginInfoDto { + + @Schema(description = "账号", defaultValue = "19999999999") + String account; + @Schema(description = "密码", defaultValue = "123456") + String password; +} diff --git a/src/main/java/club/joylink/xiannccda/entity/User.java b/src/main/java/club/joylink/xiannccda/entity/User.java new file mode 100644 index 0000000..bc20af2 --- /dev/null +++ b/src/main/java/club/joylink/xiannccda/entity/User.java @@ -0,0 +1,59 @@ +package club.joylink.xiannccda.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import io.swagger.v3.oas.annotations.media.Schema.RequiredMode; +import jakarta.validation.constraints.NotBlank; +import java.time.LocalDateTime; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; + +/** + *

+ * 用户 + *

+ * + * @author walker-sheng + * @since 2023-06-01 + */ +@Getter +@Setter +@Accessors(chain = true) +@Schema(name = "User", description = "用户") +public class User { + + @TableId(value = "id", type = IdType.AUTO) + private Integer id; + + @Schema(description = "名字") + @NotBlank(message = "名字不能为空", groups = {Register.class}) + private String name; + + @Schema(description = "手机号") + @NotBlank(message = "手机号不能为空", groups = {Register.class}) + private String mobile; + + @Schema(description = "密码") + @NotBlank(message = "密码不能为空", groups = {Register.class}) + private String password; + + @Schema(description = "注册时间") + private LocalDateTime registerTime; + + public static final String ID = "id"; + + public static final String NAME = "name"; + + public static final String MOBILE = "mobile"; + + public static final String PASSWORD = "password"; + + public static final String REGISTER_TIME = "register_time"; + + public interface Register { + + } + +} diff --git a/src/main/java/club/joylink/xiannccda/exception/BusinessExceptionAssertEnum.java b/src/main/java/club/joylink/xiannccda/exception/BusinessExceptionAssertEnum.java index 52dd788..6bf881c 100644 --- a/src/main/java/club/joylink/xiannccda/exception/BusinessExceptionAssertEnum.java +++ b/src/main/java/club/joylink/xiannccda/exception/BusinessExceptionAssertEnum.java @@ -4,68 +4,9 @@ import lombok.Getter; @Getter public enum BusinessExceptionAssertEnum implements BusinessExceptionAssert { - INVALID_LICENSE(90000, "invalid license"), - LICENSE_EXPIRED(90001, "license expired"), - LICENSE_LOCAL_SAVE_FAILED(90002, "license local save failed"), - LICENSE_LOCAL_LOAD_FAILED(90003, "license local load failed"), - LICENSE_DECRYPT_FAILED(90004, "license decrypt failed"), - LICENSE_NOT_EXIST(90005, "license not exist"), - LICENSE_FILE_NOT_EXIST(90006, "license file not exist"), - - - SYSTEM_EXCEPTION(10000, "system exception"), - TRAINING_ROOM_SIMULATION_NOT_EXIST(10001, "training room simulation not exist"), - SIMULATION_PERMISSION_ALREADY_GET(10002, "permission already get"), - INSUFFICIENT_PERMISSIONS(10003, "insufficient permissions"), - OPERATION_NOT_SUPPORTED(10004, "operation not supported"), - INVALID_OPERATION(10005, "invalid operation"), - TRAINING_ROOM_SIMULATION_LOAD_DEVICE_ERROR(10006, "training room simulation load device error"), - DATA_NOT_EXIST(10007, "data not exist"), - ARGUMENT_ILLEGAL(10008, "argument illegal"), - SIMULATION_PERMISSION_USE_UP(10009, "simulation permission use up"), - NAME_REPEAT(10010, "name repeat"), - QUESTION_RULE_NOT_EXIST(10011, "question rule not exist"), - DATA_ALREADY_EXIST(10012, "data already exist"), - SIMULATION_PERMISSION_NOT_EXIST(10013, "simulation permission not exist"), - SIMULATION_PERMISSION_NOT_AVAILABLE(10014, "simulation permission not available"), - UNSUPPORTED_FILE_FORMAT(10015, "unsupported file format"), - OPERATION_REPEAT(10016, "operation repeat"), - SIMULATION_EXCEPTION_FOR_SHOW(10017, ""), //错误信息用于展示给仿真用户 - OPERATION_FAIL(10018, "操作失败"), - STATION_DETAIL_NOT_FOUND_RD_PLAN_ITEM(10019, "站细卡控未找到对应的接发车计划"), - - DATA_ERROR(11000, "data error"), - REPEAT_RUN_PLAN_FROM_CTC_UPDATE_DATA(11200, "repeat run plan"), - CI_GENERATE_ERROR(11001, "ci data generate error"), - MAP_PASSENGER_FLOW_DATA_ERROR(11002, "map passenger flow data error"), - DATA_UNIQUE_PROPERTY_REPEAT(11013, "data unique property repeat"), - DATA_INVALID(11004, "data invalid"), - DATA_BEEN_USED(11005, "data has been used"), - DATA_STATE_INCORRECT(11007, "data state incorrect"), - - // 运行图工具 - BEYOND_RECEPTION(101, "beyond station reception"), - - // 仿真 - SIMULATION_NOT_EXIST(30001, "simulation not exist"), - SIMULATION_OPERATION_FAILED(30002, "simulation operation failed"), - - // - LOGIN_INFO_ERROR(40003, "login info error"), - LOGIN_EXPIRED(40004, "login expired"), - NOT_LOGIN(40005, "not login"), - WECHAT_CODE_EXPIRED(40029, "wechat code expired"), - INVALID_CLIENT(40031, "invalid client"), - INCORRECT_VERIFICATION_CODE(40051, "incorrect verification code"), - THIRD_SERVICE_CALL_EXCEPTION(40071, "the third service call exception"), - VOICE_COMMAND_PARSE_ERROR(40061, "voice command parse error"), - VOICE_COMMAND_CONFIG_NULL(40062, "voice command config is null"), - VOICE_COMMAND_DEVICE_UNDEFINED(40063, "device undefined"), - VOICE_COMMAND_WORK_UNDEFINED(40064, "device work undefined"), - - //支付异常 - PAY_ERROR(50000, "pay error"), - WECHAT_NOTIFY_ERROR(401, "wechat notify error"); + ARGUMENT_ILLEGAL(1001, "argument illegal"), + DATA_NOT_EXIST(1002, "data not exist"), + UNIQUE_FIELD_REPEAT(1003, "unique field repeat"); int code; diff --git a/src/main/java/club/joylink/xiannccda/mapper/UserMapper.java b/src/main/java/club/joylink/xiannccda/mapper/UserMapper.java new file mode 100644 index 0000000..1c67f76 --- /dev/null +++ b/src/main/java/club/joylink/xiannccda/mapper/UserMapper.java @@ -0,0 +1,18 @@ +package club.joylink.xiannccda.mapper; + +import club.joylink.xiannccda.entity.User; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + *

+ * 用户 Mapper 接口 + *

+ * + * @author walker-sheng + * @since 2023-06-01 + */ +@Mapper +public interface UserMapper extends BaseMapper { + +} diff --git a/src/main/java/club/joylink/xiannccda/mapper/xml/UserMapper.xml b/src/main/java/club/joylink/xiannccda/mapper/xml/UserMapper.xml new file mode 100644 index 0000000..e9d708e --- /dev/null +++ b/src/main/java/club/joylink/xiannccda/mapper/xml/UserMapper.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + id, name, mobile, password, register_time + + + diff --git a/src/main/java/club/joylink/xiannccda/repository/IUserRepository.java b/src/main/java/club/joylink/xiannccda/repository/IUserRepository.java new file mode 100644 index 0000000..c6c79b0 --- /dev/null +++ b/src/main/java/club/joylink/xiannccda/repository/IUserRepository.java @@ -0,0 +1,16 @@ +package club.joylink.xiannccda.repository; + +import club.joylink.xiannccda.entity.User; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + *

+ * 用户 服务类 + *

+ * + * @author walker-sheng + * @since 2023-06-01 + */ +public interface IUserRepository extends IService { + +} diff --git a/src/main/java/club/joylink/xiannccda/repository/impl/UserRepository.java b/src/main/java/club/joylink/xiannccda/repository/impl/UserRepository.java new file mode 100644 index 0000000..75aaa7c --- /dev/null +++ b/src/main/java/club/joylink/xiannccda/repository/impl/UserRepository.java @@ -0,0 +1,20 @@ +package club.joylink.xiannccda.repository.impl; + +import club.joylink.xiannccda.entity.User; +import club.joylink.xiannccda.mapper.UserMapper; +import club.joylink.xiannccda.repository.IUserRepository; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.stereotype.Service; + +/** + *

+ * 用户 服务实现类 + *

+ * + * @author walker-sheng + * @since 2023-06-01 + */ +@Service +public class UserRepository extends ServiceImpl implements IUserRepository { + +} diff --git a/src/main/java/club/joylink/xiannccda/service/UserService.java b/src/main/java/club/joylink/xiannccda/service/UserService.java new file mode 100644 index 0000000..88c996b --- /dev/null +++ b/src/main/java/club/joylink/xiannccda/service/UserService.java @@ -0,0 +1,58 @@ +package club.joylink.xiannccda.service; + +import club.joylink.xiannccda.dto.LoginInfoDto; +import club.joylink.xiannccda.entity.User; +import club.joylink.xiannccda.exception.BusinessExceptionAssertEnum; +import club.joylink.xiannccda.repository.IUserRepository; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO; +import java.time.Instant; +import java.time.LocalDateTime; +import org.springframework.security.oauth2.jwt.JwtClaimsSet; +import org.springframework.security.oauth2.jwt.JwtEncoder; +import org.springframework.security.oauth2.jwt.JwtEncoderParameters; +import org.springframework.stereotype.Service; + +@Service +public class UserService { + + final IUserRepository userRepository; + final JwtEncoder jwtEncoder; + + public UserService(IUserRepository userRepository, JwtEncoder jwtEncoder) { + this.userRepository = userRepository; + this.jwtEncoder = jwtEncoder; + } + + public String register(User user) { + user.setRegisterTime(LocalDateTime.now()); + this.userRepository.save(user); + return user.getId() + ""; + } + + public String login(LoginInfoDto loginInfo) { + User user = this.userRepository.getOne( + Wrappers.lambdaQuery().eq(User::getMobile, loginInfo.getAccount()) + .eq(User::getPassword, loginInfo.getPassword())); + BusinessExceptionAssertEnum.ARGUMENT_ILLEGAL.assertNotNull(user, "账号或密码错误"); + long expiry = 3 * 24 * 60 * 60; + Instant now = Instant.now(); + JwtClaimsSet claims = JwtClaimsSet.builder() + .issuer("self") + .issuedAt(now) + .expiresAt(now.plusSeconds(expiry)) + .subject(user.getId() + "")//此处实际为用户id(数据库记录id) +// .claim("scope", scope) + .build(); + // 使用默认的RS256生成jwt + final String token = this.jwtEncoder.encode(JwtEncoderParameters.from(claims)).getTokenValue(); + return token; + } + + public Page pageQuery() { + Page page = PageDTO.of(1, 10); + Page userPage = this.userRepository.page(page, Wrappers.lambdaQuery()); + return userPage; + } +} diff --git a/src/main/resources/app.key b/src/main/resources/app.key new file mode 100644 index 0000000..8df92d5 --- /dev/null +++ b/src/main/resources/app.key @@ -0,0 +1,3 @@ +-----BEGIN PRIVATE KEY----- +MIIEugIBADANBgkqhkiG9w0BAQEFAASCBKQwggSgAgEAAoIBAQCk16Ov5gy35qzQzVUkv44FQSJp+xD73eMh0NeETTboZ4SJyQ3AIfvNQAiTSvNA6lKY4gpPxSveddmRPIbp1gVM6CKzZF7EWs6ZR9UxM7RECLrloGiVRUv3q5vbtIxMNTyMLsLtc5+PE6ZsvoeOEAUzXtoPZXbg0enWfs/qfxVaEr3c7riSXF5S0Df0gUsquRF7+CNAdr2GEDMJjtEKl7pDRk8IsiMVpuupmfhOhSveiK316Uc1D8jLIugKHZ62jAPGXbkLML30gIILWKFFEZLabvw04fZUCJq01ijRbRwLJG9Z/AnIWTk1gMfH8oIZc5R0sI389IEAYyiL9r04joCFAgMBAAECgf8/fKzUifMJdakyLV7TL6ksZSeVwowOMyD46UjNp5SbDr9Tzbx55DmvY9Tx7efKXtgu6I1adg29vQj5wbT6g8nyK4YpmZM4uiOmSGtsatQn9G6bpv6xE4/gIhLIJDkN94N/cc1nnk3iEzeegOxhxmat+w/HKJMYLLharoTY8OeGQIb9XwDppt2y1+gFXJvEG9jGb/lIZ4xLMqQfOi0T9PHooLRgjqUQ07JZHPELlLLKohDwsRXMHkDmXiCYQ7P58+hjgBLzxNr3WmpuRISLewjjCa0XWbfxrFhY8HOYIQZNBjeNmVwANsAlMlqnjjv4sfCBlHg7LA6qMfEyONy2zlECgYEAziDvMEd3EHeUJMf+Macicdgfh29zWlyPYI7u5lzCjUqy6po7Q1uFYP7SPetO8mnX0j9pbZC3Zr05TCdXc4jdccKze58j1u+zPAYxX5I2LY0T5BsQOCT4/Oa11QBE0cJSA/wy+XLMpJ8+UIs7dirMFUMjWAMwvu4De7wfsu0IIf0CgYEAzLmKNS6EjeY1GhnZs8uuvtjr5XPQ3G22WAj5g/SjaDNZ5JC1+Ag5G5Iwd262Pa6LCPXRvVGnG3FvVqIk60aHoatqY5zJA+6cCdD1OHhGJTzrPfVmcWkiNpstHqD0w8l4QGhwd8O/0IDJm4NN7bW3WTttHIBz/ElqiWoG4YEG+ykCgYBSTrJTy+WOLMF54mXs+7j0ToFgei9MgLM7sjdQwu9ordA9f3J0lgHvVjErSl+OypbEPE/j0Sp6mspbT0ZLOvZ5q24xybzs4W/nYu2qJN7/V1r+9ZOHZ7QIgDNRJzdTrs1DDBxqoN14SqH+VWpb6ADv6IwfY76+LpozeU4LCzz5HQKBgHuuR/HzJX+4qTIYle5KSkrgMDuR6YeR/IxY960hmar1AwTT7CtphF7ExeURjKXdEgAayliOwN8Se8oh8R32oTApp/+AE+z9NWW0yMER4IbUs+XdoMM5WcMVon+Ti+vQhoaa0f940iQ7+hCqleTbWGZfQX7rl6a+D8/urzODSN1pAoGAdui4nehVY7HyH9JH/Td3ZhW4SAB9U4cfNUFt+MaEDdrtxgJ5tP5NEDjIa1q0f3tdfL/WssBT0ti9thjGn2cHROE/ZlaTQR0riwgbFgLpvtQh0t4nth8RPF17+XsMDL1VGSyartYc+g+dUVubyH2PMIxPfoZCWvQSYX13YfF2Zbk= +-----END PRIVATE KEY----- \ No newline at end of file diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 60b3721..0c158e6 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -4,3 +4,8 @@ spring: url: jdbc:mysql://192.168.3.233:3306/xian-ncc-da?useSSL=false&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true username: root password: joylink0503 + +logging: + level: + root: "info" + diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml new file mode 100644 index 0000000..910c753 --- /dev/null +++ b/src/main/resources/application-prod.yml @@ -0,0 +1,17 @@ +spring: + datasource: + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://192.168.3.233:3306/xian-ncc-da?useSSL=false&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true + username: root + password: joylink0503 +spring-doc: + packages-to-scan: club.joylink.xiannccda.controller + swagger-ui: + enabled: false + api-docs: + enabled: false +logging: + file: + path: /logs/xiannccda + level: + root: "info" \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 1d7a392..47d3bcd 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,8 +1,10 @@ server: - port: 9071 + port: 9081 spring: profiles: active: dev + banner: + location: classpath:banner.txt datasource: hikari: minimum-idle: 5 # 连接池维护的最小空闲连接数 @@ -13,21 +15,16 @@ spring: max-lifetime: 1800000 # 池中连接关闭后的最长生命周期,单位ms connection-timeout: 30000 # 等待连接的超时时间,单位ms connection-test-query: select 1 - security: - oauth2: - resource-server: - jwt: - public-key-location: classpath:app.pub -springdoc: +key: + public: classpath:app.pub + private: classpath:app.key +spring-doc: packages-to-scan: club.joylink.xiannccda.controller swagger-ui: enabled: true api-docs: enabled: true path: /api-docs -logging: - level: - root: info mybatis-plus: # 映射文件的位置 mapper-locations: classpath:mybatis/mapper/*.xml diff --git a/src/main/resources/banner.txt b/src/main/resources/banner.txt new file mode 100644 index 0000000..ac0413e --- /dev/null +++ b/src/main/resources/banner.txt @@ -0,0 +1 @@ +XIAN-NCC-DA \ No newline at end of file diff --git a/src/test/java/club/joylink/xiannccda/MybatisPlusGenerator.java b/src/test/java/club/joylink/xiannccda/MybatisPlusGenerator.java index 21d7516..dad29ae 100644 --- a/src/test/java/club/joylink/xiannccda/MybatisPlusGenerator.java +++ b/src/test/java/club/joylink/xiannccda/MybatisPlusGenerator.java @@ -26,36 +26,40 @@ public class MybatisPlusGenerator { private static final List ExcludeTableList = new ArrayList<>(); static { - ExcludeTableList.add("user_role"); +// ExcludeTableList.add("user_role"); } public static void main(String[] args) { String baseDir = System.getProperty("user.dir") + File.separator + "src" + File.separator + "main"; String javaBaseDir = baseDir + File.separator + "java"; - String mapperDir = - baseDir + File.separator + "resources" + File.separator + "mybatis" + File.separator - + "mapper"; +// String mapperDir = +// baseDir + File.separator + "resources" + File.separator + "mybatis" + File.separator +// + "mapper"; System.out.println(javaBaseDir); - System.out.println(mapperDir); +// System.out.println(mapperDir); FastAutoGenerator.create(DataSourceConfigBuilder) .globalConfig(builder -> - builder.enableSpringdoc() + builder + .author("walker-sheng") + .enableSpringdoc() .disableOpenDir() .dateType(DateType.TIME_PACK) .outputDir(javaBaseDir)) .packageConfig(builder -> - builder.parent("club.joylink.xiannccda") - .service("repository") - .serviceImpl("repository.impl").pathInfo( - Collections.singletonMap(OutputFile.xml, mapperDir))) + builder.parent("club.joylink.xiannccda") + .service("repository") + .serviceImpl("repository.impl") +// .pathInfo( +// Collections.singletonMap(OutputFile.xml, mapperDir)) + ) // .templateConfig(builder -> builder.entity()) .strategyConfig(builder -> { builder.addExclude(ExcludeTableList); // entity builder.entityBuilder().enableLombok() .disableSerialVersionUID() - .enableFileOverride() // 覆盖旧文件 +// .enableFileOverride() // 覆盖旧文件 .enableColumnConstant() // 开启生成字段常量 .enableChainModel(); // 开启链式模型 // mapper diff --git a/src/test/java/club/joylink/xiannccda/XianNccDaApplicationTests.java b/src/test/java/club/joylink/xiannccda/XianNccDaApplicationTests.java index 16a7837..3c79e58 100644 --- a/src/test/java/club/joylink/xiannccda/XianNccDaApplicationTests.java +++ b/src/test/java/club/joylink/xiannccda/XianNccDaApplicationTests.java @@ -6,8 +6,8 @@ import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest class XianNccDaApplicationTests { - @Test - void contextLoads() { - } + @Test + void contextLoads() { + } } diff --git a/src/test/java/club/joylink/xiannccda/alert/AlertTest.java b/src/test/java/club/joylink/xiannccda/alert/AlertTest.java index b71132e..6649e13 100644 --- a/src/test/java/club/joylink/xiannccda/alert/AlertTest.java +++ b/src/test/java/club/joylink/xiannccda/alert/AlertTest.java @@ -1,5 +1,9 @@ package club.joylink.xiannccda.alert; +import club.joylink.xiannccda.alert.core.Alert; +import club.joylink.xiannccda.alert.core.AlertEvent; +import club.joylink.xiannccda.alert.core.AlertListener; +import club.joylink.xiannccda.alert.core.AlertMonitoringTask; import org.junit.jupiter.api.Test; public class AlertTest {