From 5269fb91492c3eea48256b0747214e053a217852 Mon Sep 17 00:00:00 2001 From: aoshiguchen <1052045476@qq.com> Date: Thu, 9 Mar 2023 23:37:20 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BB=A3=E7=90=86=E6=9C=8D=E5=8A=A1=E7=AB=AFso?= =?UTF-8?q?lon=E5=8C=96=E6=94=B9=E9=80=A0,rest=E6=8E=A5=E5=8F=A3=E3=80=81?= =?UTF-8?q?=E9=9D=99=E6=80=81=E8=B5=84=E6=BA=90=E6=9C=8D=E5=8A=A1=E8=B0=83?= =?UTF-8?q?=E9=80=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../neutrino/proxy/server/ProxyServer.java | 6 +- .../rest/config/WebConfigurerAdapter.java | 56 --------- .../rest/interceptor/BaseAuthInterceptor.java | 118 ++++++++---------- .../rest/interceptor/CorsInterceptor.java | 49 -------- .../rest/interceptor/GlobalAdviceHandler.java | 48 ------- .../interceptor/GlobalExceptionFilter.java | 37 ++++++ .../interceptor/GlobalExceptionHandler.java | 53 -------- .../ClientConnectRecordController.java | 19 ++- .../server/controller/IndexController.java | 19 +-- .../server/controller/JobInfoController.java | 37 +++--- .../server/controller/JobLogController.java | 21 ++-- .../server/controller/LicenseController.java | 37 +++--- .../controller/PortMappingController.java | 29 +++-- .../server/controller/PortPoolController.java | 28 +++-- .../server/controller/ReportController.java | 25 ++-- .../server/controller/UserController.java | 42 ++++--- .../controller/UserLoginRecordController.java | 19 ++- .../src/main/resources/app.yml | 14 +-- .../src/main/resources/static/favicon.ico | Bin 67646 -> 11870 bytes 19 files changed, 258 insertions(+), 399 deletions(-) delete mode 100644 neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/base/rest/config/WebConfigurerAdapter.java delete mode 100644 neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/base/rest/interceptor/CorsInterceptor.java delete mode 100644 neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/base/rest/interceptor/GlobalAdviceHandler.java create mode 100644 neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/base/rest/interceptor/GlobalExceptionFilter.java delete mode 100644 neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/base/rest/interceptor/GlobalExceptionHandler.java diff --git a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/ProxyServer.java b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/ProxyServer.java index 93ebd620..9bc2731c 100644 --- a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/ProxyServer.java +++ b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/ProxyServer.java @@ -2,6 +2,7 @@ package fun.asgc.neutrino.proxy.server; import org.noear.solon.Solon; import org.noear.solon.annotation.Controller; +import org.noear.solon.web.cors.CrossFilter; /** * @@ -12,6 +13,9 @@ import org.noear.solon.annotation.Controller; public class ProxyServer { public static void main(String[] args) { - Solon.start(ProxyServer.class, args); + Solon.start(ProxyServer.class, args, app -> { + // 跨域支持。加-1 优先级更高 + app.filter(-1, new CrossFilter().allowedOrigins("*")); + }); } } diff --git a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/base/rest/config/WebConfigurerAdapter.java b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/base/rest/config/WebConfigurerAdapter.java deleted file mode 100644 index 6b35c18c..00000000 --- a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/base/rest/config/WebConfigurerAdapter.java +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright (c) 2022 aoshiguchen - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package fun.asgc.neutrino.proxy.server.base.rest.config; - -import fun.asgc.neutrino.core.annotation.Configuration; -import fun.asgc.neutrino.core.web.config.WebMvcConfigurer; -import fun.asgc.neutrino.core.web.interceptor.ExceptionHandlerRegistry; -import fun.asgc.neutrino.core.web.interceptor.InterceptorRegistry; -import fun.asgc.neutrino.core.web.interceptor.RestControllerAdviceHandler; -import fun.asgc.neutrino.proxy.server.base.rest.interceptor.*; - -/** - * web配置 - * @author: aoshiguchen - * @date: 2022/7/30 - */ -@Configuration -public class WebConfigurerAdapter implements WebMvcConfigurer { - - @Override - public void addInterceptors(InterceptorRegistry registry) { - registry.addInterceptor(new BaseAuthInterceptor()) - .addPathPatterns("/**").excludePathPatterns("/**/*.html", "/**/*.js", "/**/*.ico"); - registry.addInterceptor(new CorsInterceptor()); - registry.addInterceptor(new VisitLogInterceptor()); - } - - @Override - public RestControllerAdviceHandler adviceHandler() { - return new GlobalAdviceHandler(); - } - - @Override - public void addExceptionHandler(ExceptionHandlerRegistry registry) { - registry.addExceptionHandler(new GlobalExceptionHandler()); - } -} diff --git a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/base/rest/interceptor/BaseAuthInterceptor.java b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/base/rest/interceptor/BaseAuthInterceptor.java index 1a09a6b3..299d4db8 100644 --- a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/base/rest/interceptor/BaseAuthInterceptor.java +++ b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/base/rest/interceptor/BaseAuthInterceptor.java @@ -1,87 +1,75 @@ -/** - * Copyright (c) 2022 aoshiguchen - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ package fun.asgc.neutrino.proxy.server.base.rest.interceptor; -import fun.asgc.neutrino.core.util.HttpUtil; import fun.asgc.neutrino.core.util.StringUtil; -import fun.asgc.neutrino.core.web.context.HttpContextHolder; -import fun.asgc.neutrino.core.web.context.HttpRequestWrapper; -import fun.asgc.neutrino.core.web.context.HttpResponseWrapper; -import fun.asgc.neutrino.core.web.interceptor.HandlerInterceptor; -import fun.asgc.neutrino.proxy.server.base.rest.Authorization; -import fun.asgc.neutrino.proxy.server.base.rest.ServiceException; -import fun.asgc.neutrino.proxy.server.base.rest.SystemContext; -import fun.asgc.neutrino.proxy.server.base.rest.SystemContextHolder; +import fun.asgc.neutrino.proxy.server.base.rest.*; import fun.asgc.neutrino.proxy.server.base.rest.annotation.OnlyAdmin; import fun.asgc.neutrino.proxy.server.constant.EnableStatusEnum; import fun.asgc.neutrino.proxy.server.constant.ExceptionConstant; import fun.asgc.neutrino.proxy.server.dal.entity.UserDO; import fun.asgc.neutrino.proxy.server.service.UserService; import org.noear.solon.Solon; +import org.noear.solon.annotation.Component; +import org.noear.solon.core.handle.Action; +import org.noear.solon.core.handle.Context; +import org.noear.solon.core.handle.Handler; +import org.noear.solon.core.route.RouterInterceptor; +import org.noear.solon.core.route.RouterInterceptorChain; import java.lang.reflect.Method; /** - * 鉴权拦截器 * @author: aoshiguchen - * @date: 2022/7/30 + * @date: 2023/3/9 */ -public class BaseAuthInterceptor implements HandlerInterceptor { +@Component +public class BaseAuthInterceptor implements RouterInterceptor { - @Override - public boolean preHandle(HttpRequestWrapper requestParser, HttpResponseWrapper responseWrapper, String route, Method targetMethod) throws Exception { - SystemContext systemContext = new SystemContext(); - SystemContextHolder.set(systemContext); - systemContext.setIp(HttpUtil.getIP(HttpContextHolder.getChannelHandlerContext(), requestParser)); + @Override + public void doIntercept(Context ctx, Handler mainHandler, RouterInterceptorChain chain) throws Throwable { + if (!(mainHandler instanceof Action)) { + return; + } + Action action = (Action) mainHandler; + Method targetMethod = action.method().getMethod(); - Authorization authorization = targetMethod.getAnnotation(Authorization.class); - if (null == authorization || authorization.login()) { - String authorize = requestParser.getHeaderValue("Authorize"); - if (StringUtil.isEmpty(authorize)) { - throw ServiceException.create(ExceptionConstant.USER_NOT_LOGIN); - } - UserDO userDO = Solon.context().getBean(UserService.class).findByToken(authorize); - if (null == userDO) { - throw ServiceException.create(ExceptionConstant.USER_NOT_LOGIN); - } - if (EnableStatusEnum.DISABLE.getStatus().equals(userDO.getEnable())) { - throw ServiceException.create(ExceptionConstant.USER_DISABLE); - } - if (targetMethod.isAnnotationPresent(OnlyAdmin.class) && !userDO.getLoginName().equals("admin")) { - throw ServiceException.create(ExceptionConstant.NO_PERMISSION_VISIT); - } + SystemContext systemContext = new SystemContext(); + SystemContextHolder.set(systemContext); + systemContext.setIp(ctx.ip()); - systemContext.setToken(authorize); - systemContext.setUser(userDO); + Authorization authorization = targetMethod.getAnnotation(Authorization.class); + if (null == authorization || authorization.login()) { + String authorize = ctx.header("Authorize"); + if (StringUtil.isEmpty(authorize)) { + throw ServiceException.create(ExceptionConstant.USER_NOT_LOGIN); + } + UserDO userDO = Solon.context().getBean(UserService.class).findByToken(authorize); + if (null == userDO) { + throw ServiceException.create(ExceptionConstant.USER_NOT_LOGIN); + } + if (EnableStatusEnum.DISABLE.getStatus().equals(userDO.getEnable())) { + throw ServiceException.create(ExceptionConstant.USER_DISABLE); + } + if (targetMethod.isAnnotationPresent(OnlyAdmin.class) && !userDO.getLoginName().equals("admin")) { + throw ServiceException.create(ExceptionConstant.NO_PERMISSION_VISIT); + } - // token续期 - Solon.context().getBean(UserService.class).updateTokenExpirationTime(authorize); - } + systemContext.setToken(authorize); + systemContext.setUser(userDO); - return true; - } + // token续期 + Solon.context().getBean(UserService.class).updateTokenExpirationTime(authorize); + } + chain.doIntercept(ctx, mainHandler); + } - @Override - public void afterCompletion(HttpRequestWrapper requestParser, HttpResponseWrapper responseWrapper, String route, Method targetMethod) { - SystemContextHolder.remove(); - } + @Override + public Object postResult(Context ctx, Object result) throws Throwable { + SystemContextHolder.remove(); + if (result instanceof ResponseBody) { + return result; + } + return new ResponseBody<>() + .setCode(0) + .setData(result); + } } diff --git a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/base/rest/interceptor/CorsInterceptor.java b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/base/rest/interceptor/CorsInterceptor.java deleted file mode 100644 index 50334dd3..00000000 --- a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/base/rest/interceptor/CorsInterceptor.java +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Copyright (c) 2022 aoshiguchen - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package fun.asgc.neutrino.proxy.server.base.rest.interceptor; - -import fun.asgc.neutrino.core.web.context.HttpRequestWrapper; -import fun.asgc.neutrino.core.web.context.HttpResponseWrapper; -import fun.asgc.neutrino.core.web.interceptor.HandlerInterceptor; - -import java.lang.reflect.Method; - -/** - * 处理跨域问题 - * @author: aoshiguchen - * @date: 2022/7/30 - */ -public class CorsInterceptor implements HandlerInterceptor { - - @Override - public void postHandle(HttpRequestWrapper requestParser, HttpResponseWrapper responseWrapper, String route, Method targetMethod, Object result) throws Exception { - responseWrapper.headers().add("Access-Control-Allow-Origin", "*"); - responseWrapper.headers().add("Access-Control-Allow-Methods", "POST, GET, OPTIONS"); - responseWrapper.headers().add("Access-Control-Max-Age", "86400"); - responseWrapper.headers().add("Access-Control-Allow-Headers", "*"); - responseWrapper.headers().add("Access-Control-Allow-Credentials", "true"); - responseWrapper.headers().add("XDomainRequestAllowed", "1"); - responseWrapper.headers().add("Access-Control-Allow-Headers","Authorize, Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type"); - responseWrapper.headers().add("Access-Control-Allow-Credentials", "true"); - } - -} diff --git a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/base/rest/interceptor/GlobalAdviceHandler.java b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/base/rest/interceptor/GlobalAdviceHandler.java deleted file mode 100644 index 06ccd1e7..00000000 --- a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/base/rest/interceptor/GlobalAdviceHandler.java +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Copyright (c) 2022 aoshiguchen - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package fun.asgc.neutrino.proxy.server.base.rest.interceptor; - -import fun.asgc.neutrino.core.web.context.HttpRequestWrapper; -import fun.asgc.neutrino.core.web.context.HttpResponseWrapper; -import fun.asgc.neutrino.core.web.interceptor.RestControllerAdviceHandler; -import fun.asgc.neutrino.proxy.server.base.rest.ResponseBody; - -import java.lang.reflect.Method; - -/** - * 全局返回处理 - * @author: aoshiguchen - * @date: 2022/7/31 - */ -public class GlobalAdviceHandler implements RestControllerAdviceHandler { - - @Override - public Object advice(HttpRequestWrapper requestParser, HttpResponseWrapper responseWrapper, String route, Method targetMethod, Object res) { - if (res instanceof ResponseBody) { - return res; - } - return new ResponseBody<>() - .setCode(0) - .setData(res); - } - -} diff --git a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/base/rest/interceptor/GlobalExceptionFilter.java b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/base/rest/interceptor/GlobalExceptionFilter.java new file mode 100644 index 00000000..1563e250 --- /dev/null +++ b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/base/rest/interceptor/GlobalExceptionFilter.java @@ -0,0 +1,37 @@ +package fun.asgc.neutrino.proxy.server.base.rest.interceptor; + +import fun.asgc.neutrino.proxy.server.base.rest.ResponseBody; +import fun.asgc.neutrino.proxy.server.base.rest.ServiceException; +import fun.asgc.neutrino.proxy.server.constant.ExceptionConstant; +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.noear.solon.annotation.Component; +import org.noear.solon.core.handle.Context; +import org.noear.solon.core.handle.Filter; +import org.noear.solon.core.handle.FilterChain; + +/** + * @author: aoshiguchen + * @date: 2023/3/9 + */ +@Component +public class GlobalExceptionFilter implements Filter { + + @Override + public void doFilter(Context ctx, FilterChain chain) throws Throwable { + try { + chain.doFilter(ctx); + } catch (Throwable e) { + if (e instanceof ServiceException) { + ServiceException serviceException = (ServiceException)e; + ctx.render(new ResponseBody<>() + .setCode(serviceException.getCode()) + .setMsg(serviceException.getMsg())); + return; + } + ctx.render(new ResponseBody<>() + .setCode(ExceptionConstant.SYSTEM_ERROR.getCode()) + .setMsg(ExceptionConstant.SYSTEM_ERROR.getMsg()) + .setStack(ExceptionUtils.getStackTrace(e))); + } + } +} diff --git a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/base/rest/interceptor/GlobalExceptionHandler.java b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/base/rest/interceptor/GlobalExceptionHandler.java deleted file mode 100644 index cecd7725..00000000 --- a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/base/rest/interceptor/GlobalExceptionHandler.java +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Copyright (c) 2022 aoshiguchen - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package fun.asgc.neutrino.proxy.server.base.rest.interceptor; - -import fun.asgc.neutrino.core.web.context.HttpRequestWrapper; -import fun.asgc.neutrino.core.web.context.HttpResponseWrapper; -import fun.asgc.neutrino.core.web.interceptor.RestControllerExceptionHandler; -import fun.asgc.neutrino.proxy.server.constant.ExceptionConstant; -import fun.asgc.neutrino.proxy.server.base.rest.ResponseBody; -import fun.asgc.neutrino.proxy.server.base.rest.ServiceException; -import org.apache.commons.lang3.exception.ExceptionUtils; - -/** - * 全局异常处理 - * @author: aoshiguchen - * @date: 2022/7/31 - */ -public class GlobalExceptionHandler implements RestControllerExceptionHandler { - - @Override - public Object handle(HttpRequestWrapper requestParser, HttpResponseWrapper responseWrapper, Throwable e) { - if (e instanceof ServiceException) { - ServiceException serviceException = (ServiceException)e; - return new ResponseBody<>() - .setCode(serviceException.getCode()) - .setMsg(serviceException.getMsg()); - } - return new ResponseBody<>() - .setCode(ExceptionConstant.SYSTEM_ERROR.getCode()) - .setMsg(ExceptionConstant.SYSTEM_ERROR.getMsg()) - .setStack(ExceptionUtils.getStackTrace(e)); - } - -} diff --git a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/controller/ClientConnectRecordController.java b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/controller/ClientConnectRecordController.java index 8ade1dde..8bcd8c5e 100644 --- a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/controller/ClientConnectRecordController.java +++ b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/controller/ClientConnectRecordController.java @@ -21,32 +21,31 @@ */ package fun.asgc.neutrino.proxy.server.controller; -import fun.asgc.neutrino.core.annotation.Autowired; -import fun.asgc.neutrino.core.annotation.NonIntercept; import fun.asgc.neutrino.core.db.page.Page; import fun.asgc.neutrino.core.db.page.PageQuery; -import fun.asgc.neutrino.core.web.annotation.GetMapping; -import fun.asgc.neutrino.core.web.annotation.RequestMapping; -import fun.asgc.neutrino.core.web.annotation.RestController; import fun.asgc.neutrino.proxy.server.controller.req.ClientConnectRecordListReq; import fun.asgc.neutrino.proxy.server.controller.res.ClientConnectRecordListRes; import fun.asgc.neutrino.proxy.server.service.ClientConnectRecordService; import fun.asgc.neutrino.proxy.server.util.ParamCheckUtil; import lombok.extern.slf4j.Slf4j; +import org.noear.solon.annotation.Controller; +import org.noear.solon.annotation.Get; +import org.noear.solon.annotation.Inject; +import org.noear.solon.annotation.Mapping; /** * @author: aoshiguchen * @date: 2022/11/26 */ @Slf4j -@NonIntercept -@RequestMapping("client-connect-record") -@RestController +@Mapping("/client-connect-record") +@Controller public class ClientConnectRecordController { - @Autowired + @Inject private ClientConnectRecordService clientConnectRecordService; - @GetMapping("page") + @Get + @Mapping("/page") public Page page(PageQuery pageQuery, ClientConnectRecordListReq req) { ParamCheckUtil.checkNotNull(pageQuery, "pageQuery"); return clientConnectRecordService.page(pageQuery, req); diff --git a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/controller/IndexController.java b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/controller/IndexController.java index 912b5c03..0e705b31 100644 --- a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/controller/IndexController.java +++ b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/controller/IndexController.java @@ -21,32 +21,35 @@ */ package fun.asgc.neutrino.proxy.server.controller; -import fun.asgc.neutrino.core.annotation.NonIntercept; import fun.asgc.neutrino.core.web.annotation.RequestBody; import fun.asgc.neutrino.proxy.server.base.rest.Authorization; import fun.asgc.neutrino.proxy.server.controller.req.LoginReq; import fun.asgc.neutrino.proxy.server.controller.res.LoginRes; import fun.asgc.neutrino.proxy.server.service.UserService; import fun.asgc.neutrino.proxy.server.util.ParamCheckUtil; -import org.noear.solon.annotation.Controller; -import org.noear.solon.annotation.Inject; -import org.noear.solon.annotation.Mapping; -import org.noear.solon.annotation.Post; +import org.noear.solon.annotation.*; +import org.noear.solon.core.handle.Context; /** * * @author: aoshiguchen * @date: 2022/7/31 */ -@NonIntercept @Controller public class IndexController { @Inject private UserService userService; + @Authorization(login = false) + @Get + @Mapping("/") + public void home(Context ctx) { + ctx.forward("/index.html"); + } + @Authorization(login = false) @Post - @Mapping("login") + @Mapping("/login") public LoginRes login(@RequestBody LoginReq req) { ParamCheckUtil.checkNotEmpty(req.getLoginName(), "loginName"); ParamCheckUtil.checkNotEmpty(req.getLoginPassword(), "loginPassword"); @@ -55,7 +58,7 @@ public class IndexController { } @Post - @Mapping("logout") + @Mapping("/logout") public void logout() { userService.logout(); } diff --git a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/controller/JobInfoController.java b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/controller/JobInfoController.java index 1ed25b21..b225d265 100644 --- a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/controller/JobInfoController.java +++ b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/controller/JobInfoController.java @@ -21,18 +21,23 @@ */ package fun.asgc.neutrino.proxy.server.controller; -import fun.asgc.neutrino.core.annotation.Autowired; -import fun.asgc.neutrino.core.annotation.NonIntercept; import fun.asgc.neutrino.core.db.page.Page; import fun.asgc.neutrino.core.db.page.PageQuery; -import fun.asgc.neutrino.core.web.annotation.*; +import fun.asgc.neutrino.core.web.annotation.RequestBody; import fun.asgc.neutrino.proxy.server.base.rest.annotation.OnlyAdmin; -import fun.asgc.neutrino.proxy.server.controller.req.*; -import fun.asgc.neutrino.proxy.server.controller.res.*; +import fun.asgc.neutrino.proxy.server.controller.req.JobInfoExecuteReq; +import fun.asgc.neutrino.proxy.server.controller.req.JobInfoListReq; +import fun.asgc.neutrino.proxy.server.controller.req.JobInfoUpdateEnableStatusReq; +import fun.asgc.neutrino.proxy.server.controller.req.JobInfoUpdateReq; +import fun.asgc.neutrino.proxy.server.controller.res.JobInfoExecuteRes; +import fun.asgc.neutrino.proxy.server.controller.res.JobInfoListRes; +import fun.asgc.neutrino.proxy.server.controller.res.JobInfoUpdateEnableStatusRes; +import fun.asgc.neutrino.proxy.server.controller.res.JobInfoUpdateRes; import fun.asgc.neutrino.proxy.server.dal.entity.JobInfoDO; import fun.asgc.neutrino.proxy.server.service.JobInfoService; import fun.asgc.neutrino.proxy.server.util.ParamCheckUtil; import lombok.extern.slf4j.Slf4j; +import org.noear.solon.annotation.*; import java.util.List; @@ -42,26 +47,28 @@ import java.util.List; * @date: 2022/9/5 */ @Slf4j -@NonIntercept -@RequestMapping("job-info") -@RestController +@Mapping("/job-info") +@Controller public class JobInfoController { - @Autowired + @Inject private JobInfoService jobInfoService; - @GetMapping("page") + @Get + @Mapping("/page") public Page page(PageQuery pageQuery, JobInfoListReq req) { ParamCheckUtil.checkNotNull(pageQuery, "pageQuery"); return jobInfoService.page(pageQuery, req); } - @GetMapping("findList") + @Get + @Mapping("/findList") public List findList() { return jobInfoService.findList(); } @OnlyAdmin - @PostMapping("update/enable-status") + @Post + @Mapping("/update/enable-status") public JobInfoUpdateEnableStatusRes updateEnableStatus(@RequestBody JobInfoUpdateEnableStatusReq req) { ParamCheckUtil.checkNotNull(req, "req"); ParamCheckUtil.checkNotNull(req.getId(), "id"); @@ -70,7 +77,8 @@ public class JobInfoController { } @OnlyAdmin - @PostMapping("execute") + @Post + @Mapping("execute") public JobInfoExecuteRes execute(@RequestBody JobInfoExecuteReq req) { ParamCheckUtil.checkNotNull(req, "req"); ParamCheckUtil.checkNotNull(req.getId(), "id"); @@ -78,7 +86,8 @@ public class JobInfoController { return jobInfoService.execute(req); } - @PostMapping("update") + @Post + @Mapping("update") public JobInfoUpdateRes update(@RequestBody JobInfoUpdateReq req) { ParamCheckUtil.checkNotNull(req, "req"); diff --git a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/controller/JobLogController.java b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/controller/JobLogController.java index 16f80d38..cb0db7f2 100644 --- a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/controller/JobLogController.java +++ b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/controller/JobLogController.java @@ -21,16 +21,17 @@ */ package fun.asgc.neutrino.proxy.server.controller; -import fun.asgc.neutrino.core.annotation.Autowired; -import fun.asgc.neutrino.core.annotation.NonIntercept; import fun.asgc.neutrino.core.db.page.Page; import fun.asgc.neutrino.core.db.page.PageQuery; -import fun.asgc.neutrino.core.web.annotation.*; -import fun.asgc.neutrino.proxy.server.controller.req.*; -import fun.asgc.neutrino.proxy.server.controller.res.*; +import fun.asgc.neutrino.proxy.server.controller.req.JobLogListReq; +import fun.asgc.neutrino.proxy.server.controller.res.JobLogListRes; import fun.asgc.neutrino.proxy.server.service.JobLogService; import fun.asgc.neutrino.proxy.server.util.ParamCheckUtil; import lombok.extern.slf4j.Slf4j; +import org.noear.solon.annotation.Controller; +import org.noear.solon.annotation.Get; +import org.noear.solon.annotation.Inject; +import org.noear.solon.annotation.Mapping; /** * @@ -38,14 +39,14 @@ import lombok.extern.slf4j.Slf4j; * @date: 2022/9/25 */ @Slf4j -@NonIntercept -@RequestMapping("job-log") -@RestController +@Mapping("/job-log") +@Controller public class JobLogController { - @Autowired + @Inject private JobLogService jobLogService; - @GetMapping("page") + @Get + @Mapping("/page") public Page page(PageQuery pageQuery, JobLogListReq req) { ParamCheckUtil.checkNotNull(pageQuery, "pageQuery"); return jobLogService.page(pageQuery, req); diff --git a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/controller/LicenseController.java b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/controller/LicenseController.java index 3d0230ff..4ab23792 100644 --- a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/controller/LicenseController.java +++ b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/controller/LicenseController.java @@ -21,11 +21,10 @@ */ package fun.asgc.neutrino.proxy.server.controller; -import fun.asgc.neutrino.core.annotation.Autowired; -import fun.asgc.neutrino.core.annotation.NonIntercept; import fun.asgc.neutrino.core.db.page.Page; import fun.asgc.neutrino.core.db.page.PageQuery; -import fun.asgc.neutrino.core.web.annotation.*; +import fun.asgc.neutrino.core.web.annotation.RequestBody; +import fun.asgc.neutrino.core.web.annotation.RequestParam; import fun.asgc.neutrino.proxy.server.base.rest.annotation.OnlyAdmin; import fun.asgc.neutrino.proxy.server.controller.req.LicenseCreateReq; import fun.asgc.neutrino.proxy.server.controller.req.LicenseListReq; @@ -34,6 +33,7 @@ import fun.asgc.neutrino.proxy.server.controller.req.LicenseUpdateReq; import fun.asgc.neutrino.proxy.server.controller.res.*; import fun.asgc.neutrino.proxy.server.service.LicenseService; import fun.asgc.neutrino.proxy.server.util.ParamCheckUtil; +import org.noear.solon.annotation.*; import java.util.List; @@ -42,28 +42,28 @@ import java.util.List; * @author: aoshiguchen * @date: 2022/8/6 */ -@NonIntercept -@RequestMapping("license") -@RestController +@Mapping("/license") +@Controller public class LicenseController { - - @Autowired + @Inject private LicenseService licenseService; - @GetMapping("page") + @Get + @Mapping("/page") public Page page(PageQuery pageQuery, LicenseListReq req) { ParamCheckUtil.checkNotNull(pageQuery, "pageQuery"); return licenseService.page(pageQuery, req); } - @GetMapping("list") + @Mapping("/list") public List list(LicenseListReq req) { return licenseService.list(req); } @OnlyAdmin - @PostMapping("create") + @Post + @Mapping("/create") public LicenseCreateRes create(@RequestBody LicenseCreateReq req) { ParamCheckUtil.checkNotNull(req, "req"); ParamCheckUtil.checkNotEmpty(req.getName(), "name"); @@ -73,7 +73,8 @@ public class LicenseController { } @OnlyAdmin - @PostMapping("update") + @Post + @Mapping("/update") public LicenseUpdateRes update(@RequestBody LicenseUpdateReq req) { ParamCheckUtil.checkNotNull(req, "req"); ParamCheckUtil.checkNotNull(req.getId(), "id"); @@ -82,7 +83,8 @@ public class LicenseController { return licenseService.update(req); } - @GetMapping("detail") + @Post + @Mapping("/detail") public LicenseDetailRes detail(@RequestParam("id") Integer id) { ParamCheckUtil.checkNotNull(id, "id"); @@ -90,7 +92,8 @@ public class LicenseController { } @OnlyAdmin - @PostMapping("update/enable-status") + @Post + @Mapping("/update/enable-status") public LicenseUpdateEnableStatusRes updateEnableStatus(@RequestBody LicenseUpdateEnableStatusReq req) { ParamCheckUtil.checkNotNull(req, "req"); ParamCheckUtil.checkNotNull(req.getId(), "id"); @@ -100,7 +103,8 @@ public class LicenseController { } @OnlyAdmin - @PostMapping("delete") + @Post + @Mapping("/delete") public void delete(@RequestParam("id") Integer id) { ParamCheckUtil.checkNotNull(id, "id"); @@ -108,7 +112,8 @@ public class LicenseController { } @OnlyAdmin - @PostMapping("reset") + @Post + @Mapping("/reset") public void reset(@RequestParam("id") Integer id) { ParamCheckUtil.checkNotNull(id, "id"); diff --git a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/controller/PortMappingController.java b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/controller/PortMappingController.java index 6af60752..426d471b 100644 --- a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/controller/PortMappingController.java +++ b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/controller/PortMappingController.java @@ -21,11 +21,11 @@ */ package fun.asgc.neutrino.proxy.server.controller; -import fun.asgc.neutrino.core.annotation.Autowired; import fun.asgc.neutrino.core.annotation.NonIntercept; import fun.asgc.neutrino.core.db.page.Page; import fun.asgc.neutrino.core.db.page.PageQuery; -import fun.asgc.neutrino.core.web.annotation.*; +import fun.asgc.neutrino.core.web.annotation.RequestBody; +import fun.asgc.neutrino.core.web.annotation.RequestParam; import fun.asgc.neutrino.proxy.server.controller.req.PortMappingCreateReq; import fun.asgc.neutrino.proxy.server.controller.req.PortMappingListReq; import fun.asgc.neutrino.proxy.server.controller.req.PortMappingUpdateEnableStatusReq; @@ -33,6 +33,7 @@ import fun.asgc.neutrino.proxy.server.controller.req.PortMappingUpdateReq; import fun.asgc.neutrino.proxy.server.controller.res.*; import fun.asgc.neutrino.proxy.server.service.PortMappingService; import fun.asgc.neutrino.proxy.server.util.ParamCheckUtil; +import org.noear.solon.annotation.*; /** * 端口映射 @@ -40,20 +41,22 @@ import fun.asgc.neutrino.proxy.server.util.ParamCheckUtil; * @date: 2022/8/8 */ @NonIntercept -@RequestMapping("port-mapping") -@RestController +@Mapping("/port-mapping") +@Controller public class PortMappingController { - @Autowired + @Inject private PortMappingService portMappingService; - @GetMapping("page") + @Get + @Mapping("/page") public Page page(PageQuery pageQuery, PortMappingListReq req) { ParamCheckUtil.checkNotNull(pageQuery, "pageQuery"); return portMappingService.page(pageQuery, req); } - @PostMapping("create") + @Post + @Mapping("/create") public PortMappingCreateRes create(@RequestBody PortMappingCreateReq req) { ParamCheckUtil.checkNotNull(req, "req"); ParamCheckUtil.checkNotNull(req.getLicenseId(), "licenseId"); @@ -64,21 +67,24 @@ public class PortMappingController { return portMappingService.create(req); } - @PostMapping("update") + @Post + @Mapping("/update") public PortMappingUpdateRes update(@RequestBody PortMappingUpdateReq req) { ParamCheckUtil.checkNotNull(req, "req"); return portMappingService.update(req); } - @GetMapping("detail") + @Get + @Mapping("/detail") public PortMappingDetailRes detail(@RequestParam("id") Integer id) { ParamCheckUtil.checkNotNull(id, "id"); return portMappingService.detail(id); } - @PostMapping("update/enable-status") + @Post + @Mapping("/update/enable-status") public PortMappingUpdateEnableStatusRes updateEnableStatus(@RequestBody PortMappingUpdateEnableStatusReq req) { ParamCheckUtil.checkNotNull(req, "req"); ParamCheckUtil.checkNotNull(req.getId(), "id"); @@ -87,7 +93,8 @@ public class PortMappingController { return portMappingService.updateEnableStatus(req); } - @PostMapping("delete") + @Post + @Mapping("/delete") public void delete(@RequestParam("id") Integer id) { ParamCheckUtil.checkNotNull(id, "id"); diff --git a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/controller/PortPoolController.java b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/controller/PortPoolController.java index 25910fd9..fd5141b9 100644 --- a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/controller/PortPoolController.java +++ b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/controller/PortPoolController.java @@ -21,11 +21,10 @@ */ package fun.asgc.neutrino.proxy.server.controller; -import fun.asgc.neutrino.core.annotation.Autowired; -import fun.asgc.neutrino.core.annotation.NonIntercept; import fun.asgc.neutrino.core.db.page.Page; import fun.asgc.neutrino.core.db.page.PageQuery; -import fun.asgc.neutrino.core.web.annotation.*; +import fun.asgc.neutrino.core.web.annotation.RequestBody; +import fun.asgc.neutrino.core.web.annotation.RequestParam; import fun.asgc.neutrino.proxy.server.base.rest.annotation.OnlyAdmin; import fun.asgc.neutrino.proxy.server.controller.req.PortPoolCreateReq; import fun.asgc.neutrino.proxy.server.controller.req.PortPoolListReq; @@ -35,6 +34,7 @@ import fun.asgc.neutrino.proxy.server.controller.res.PortPoolListRes; import fun.asgc.neutrino.proxy.server.controller.res.PortPoolUpdateEnableStatusRes; import fun.asgc.neutrino.proxy.server.service.PortPoolService; import fun.asgc.neutrino.proxy.server.util.ParamCheckUtil; +import org.noear.solon.annotation.*; import java.util.List; @@ -43,27 +43,29 @@ import java.util.List; * @author: aoshiguchen * @date: 2022/8/7 */ -@NonIntercept -@RequestMapping("port-pool") -@RestController +@Mapping("/port-pool") +@Controller public class PortPoolController { - @Autowired + @Inject private PortPoolService portPoolService; - @GetMapping("page") + @Get + @Mapping("/page") public Page page(PageQuery pageQuery, PortPoolListReq req) { ParamCheckUtil.checkNotNull(pageQuery, "pageQuery"); return portPoolService.page(pageQuery, req); } - @GetMapping("list") + @Get + @Mapping("/list") public List list(PortPoolListReq req) { return portPoolService.list(req); } @OnlyAdmin - @PostMapping("create") + @Post + @Mapping("/create") public PortPoolCreateRes create(@RequestBody PortPoolCreateReq req) { ParamCheckUtil.checkNotNull(req, "req"); ParamCheckUtil.checkNotNull(req.getPort(), "port"); @@ -72,7 +74,8 @@ public class PortPoolController { } @OnlyAdmin - @PostMapping("update/enable-status") + @Post + @Mapping("/update/enable-status") public PortPoolUpdateEnableStatusRes updateEnableStatus(@RequestBody PortPoolUpdateEnableStatusReq req) { ParamCheckUtil.checkNotNull(req, "req"); ParamCheckUtil.checkNotNull(req.getId(), "id"); @@ -82,7 +85,8 @@ public class PortPoolController { } @OnlyAdmin - @PostMapping("delete") + @Post + @Mapping("/delete") public void delete(@RequestParam("id") Integer id) { ParamCheckUtil.checkNotNull(id, "id"); diff --git a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/controller/ReportController.java b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/controller/ReportController.java index 053d5744..f5727b59 100644 --- a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/controller/ReportController.java +++ b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/controller/ReportController.java @@ -21,13 +21,8 @@ */ package fun.asgc.neutrino.proxy.server.controller; -import fun.asgc.neutrino.core.annotation.Autowired; -import fun.asgc.neutrino.core.annotation.NonIntercept; import fun.asgc.neutrino.core.db.page.Page; import fun.asgc.neutrino.core.db.page.PageQuery; -import fun.asgc.neutrino.core.web.annotation.GetMapping; -import fun.asgc.neutrino.core.web.annotation.RequestMapping; -import fun.asgc.neutrino.core.web.annotation.RestController; import fun.asgc.neutrino.proxy.server.controller.req.LicenseFlowReportReq; import fun.asgc.neutrino.proxy.server.controller.req.UserFlowReportReq; import fun.asgc.neutrino.proxy.server.controller.res.LicenseFlowReportRes; @@ -35,18 +30,21 @@ import fun.asgc.neutrino.proxy.server.controller.res.ReportDataViewRes; import fun.asgc.neutrino.proxy.server.controller.res.UserFlowReportRes; import fun.asgc.neutrino.proxy.server.service.ReportService; import fun.asgc.neutrino.proxy.server.util.ParamCheckUtil; +import org.noear.solon.annotation.Controller; +import org.noear.solon.annotation.Get; +import org.noear.solon.annotation.Inject; +import org.noear.solon.annotation.Mapping; /** * 报表管理 * @author: aoshiguchen * @date: 2022/9/12 */ -@NonIntercept -@RequestMapping("report") -@RestController +@Mapping("/report") +@Controller public class ReportController { - @Autowired + @Inject private ReportService reportService; /** @@ -62,7 +60,8 @@ public class ReportController { * 4、点击历史流量:下方展示历史流量折线图。筛选项:日/月 日期选择:日(展示最近30天,最多跨30日),月(展示最近12个月,最多跨12个月) * @return */ - @GetMapping("data-view") + @Get + @Mapping("/data-view") public ReportDataViewRes dataView() { return new ReportDataViewRes() .setUserOnlineNumber(2).setEnableUserNumber(3).setUserNumber(5) @@ -78,7 +77,8 @@ public class ReportController { * @param req * @return */ - @GetMapping("user/flow-report/page") + @Get + @Mapping("/user/flow-report/page") public Page userFlowReportPage(PageQuery pageQuery, UserFlowReportReq req) { ParamCheckUtil.checkNotNull(pageQuery, "pageQuery"); @@ -91,7 +91,8 @@ public class ReportController { * @param req * @return */ - @GetMapping("license/flow-report/page") + @Get + @Mapping("/license/flow-report/page") public Page licenseFlowReportPage(PageQuery pageQuery, LicenseFlowReportReq req) { ParamCheckUtil.checkNotNull(pageQuery, "pageQuery"); diff --git a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/controller/UserController.java b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/controller/UserController.java index c61affdd..777d4ab0 100644 --- a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/controller/UserController.java +++ b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/controller/UserController.java @@ -21,11 +21,10 @@ */ package fun.asgc.neutrino.proxy.server.controller; -import fun.asgc.neutrino.core.annotation.Autowired; -import fun.asgc.neutrino.core.annotation.NonIntercept; import fun.asgc.neutrino.core.db.page.Page; import fun.asgc.neutrino.core.db.page.PageQuery; -import fun.asgc.neutrino.core.web.annotation.*; +import fun.asgc.neutrino.core.web.annotation.RequestBody; +import fun.asgc.neutrino.core.web.annotation.RequestParam; import fun.asgc.neutrino.proxy.server.base.rest.SystemContextHolder; import fun.asgc.neutrino.proxy.server.base.rest.annotation.OnlyAdmin; import fun.asgc.neutrino.proxy.server.constant.ExceptionConstant; @@ -36,6 +35,7 @@ import fun.asgc.neutrino.proxy.server.dal.entity.UserDO; import fun.asgc.neutrino.proxy.server.service.UserService; import fun.asgc.neutrino.proxy.server.util.Md5Util; import fun.asgc.neutrino.proxy.server.util.ParamCheckUtil; +import org.noear.solon.annotation.*; import java.util.List; @@ -44,35 +44,38 @@ import java.util.List; * @author: aoshiguchen * @date: 2022/7/31 */ -@NonIntercept -@RequestMapping("user") -@RestController +@Mapping("/user") +@Controller public class UserController { - @Autowired + @Inject private UserService userService; - @Autowired + @Inject private UserMapper userMapper; - @GetMapping("page") + @Get + @Mapping("/page") public Page page(PageQuery pageQuery, UserListReq req) { ParamCheckUtil.checkNotNull(pageQuery, "pageQuery"); return userService.page(pageQuery, req); } - @GetMapping("list") + @Get + @Mapping("/list") public List list(UserListReq req) { return userService.list(req); } - @GetMapping("info") + @Get + @Mapping("/info") public UserInfoRes info(UserInfoReq req) { return userService.info(req); } @OnlyAdmin - @PostMapping("update/enable-status") + @Post + @Mapping("/update/enable-status") public UserUpdateEnableStatusRes updateEnableStatus(@RequestBody UserUpdateEnableStatusReq req) { ParamCheckUtil.checkNotNull(req, "req"); ParamCheckUtil.checkNotNull(req.getId(), "id"); @@ -82,7 +85,8 @@ public class UserController { } @OnlyAdmin - @PostMapping("create") + @Post + @Mapping("/create") public UserCreateRes create(@RequestBody UserCreateReq req) { ParamCheckUtil.checkNotNull(req, "req"); ParamCheckUtil.checkNotEmpty(req.getName(), "name"); @@ -92,7 +96,8 @@ public class UserController { } @OnlyAdmin - @PostMapping("update") + @Post + @Mapping("/update") public UserUpdateRes update(@RequestBody UserUpdateReq req) { ParamCheckUtil.checkNotNull(req, "req"); ParamCheckUtil.checkNotNull(req.getId(), "id"); @@ -103,7 +108,8 @@ public class UserController { } @OnlyAdmin - @PostMapping("update/password") + @Post + @Mapping("/update/password") public UserUpdatePasswordRes updatePassword(@RequestBody UserUpdatePasswordReq req) { ParamCheckUtil.checkNotNull(req, "req"); ParamCheckUtil.checkNotNull(req.getId(), "id"); @@ -113,7 +119,8 @@ public class UserController { return userService.updatePassword(req); } - @PostMapping("current-user/update/password") + @Post + @Mapping("/current-user/update/password") public UserUpdatePasswordRes currentUserUpdatePassword(@RequestBody UserUpdatePasswordReq req) { ParamCheckUtil.checkNotNull(req, "req"); ParamCheckUtil.checkNotEmpty(req.getOldLoginPassword(), "oldLoginPassword"); @@ -129,7 +136,8 @@ public class UserController { } @OnlyAdmin - @PostMapping("delete") + @Post + @Mapping("/delete") public void delete(@RequestParam("id") Integer id) { ParamCheckUtil.checkNotNull(id, "id"); diff --git a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/controller/UserLoginRecordController.java b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/controller/UserLoginRecordController.java index 4b7dcb5d..c317f107 100644 --- a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/controller/UserLoginRecordController.java +++ b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/controller/UserLoginRecordController.java @@ -21,30 +21,29 @@ */ package fun.asgc.neutrino.proxy.server.controller; -import fun.asgc.neutrino.core.annotation.Autowired; -import fun.asgc.neutrino.core.annotation.NonIntercept; import fun.asgc.neutrino.core.db.page.Page; import fun.asgc.neutrino.core.db.page.PageQuery; -import fun.asgc.neutrino.core.web.annotation.GetMapping; -import fun.asgc.neutrino.core.web.annotation.RequestMapping; -import fun.asgc.neutrino.core.web.annotation.RestController; import fun.asgc.neutrino.proxy.server.controller.req.UserLoginRecordListReq; import fun.asgc.neutrino.proxy.server.controller.res.UserLoginRecordListRes; import fun.asgc.neutrino.proxy.server.service.UserLoginRecordService; import fun.asgc.neutrino.proxy.server.util.ParamCheckUtil; +import org.noear.solon.annotation.Controller; +import org.noear.solon.annotation.Get; +import org.noear.solon.annotation.Inject; +import org.noear.solon.annotation.Mapping; /** * @author: aoshiguchen * @date: 2022/10/20 */ -@NonIntercept -@RequestMapping("user-login-record") -@RestController +@Mapping("/user-login-record") +@Controller public class UserLoginRecordController { - @Autowired + @Inject private UserLoginRecordService userLoginRecordService; - @GetMapping("page") + @Get + @Mapping("/page") public Page page(PageQuery pageQuery, UserLoginRecordListReq req) { ParamCheckUtil.checkNotNull(pageQuery, "pageQuery"); diff --git a/neutrino-proxy-server/src/main/resources/app.yml b/neutrino-proxy-server/src/main/resources/app.yml index 2f651215..4b0d7852 100644 --- a/neutrino-proxy-server/src/main/resources/app.yml +++ b/neutrino-proxy-server/src/main/resources/app.yml @@ -18,13 +18,13 @@ neutrino: key-store-password: 123456 key-manager-password: 123456 jks-path: classpath:/test.jks - data: - db: - type: sqlite - url: jdbc:sqlite:data.db - driver-class: org.sqlite.JDBC - username: - password: +# data: +# db: +# type: sqlite +# url: jdbc:sqlite:data.db +# driver-class: org.sqlite.JDBC +# username: +# password: #添加MIME印射(如果有需要?) #是否启用静态文件服务。(可不配,默认为启用) diff --git a/neutrino-proxy-server/src/main/resources/static/favicon.ico b/neutrino-proxy-server/src/main/resources/static/favicon.ico index 474afb8c073f4683386b03ca8782e6a3d909c54a..4b526ba9be57865ba11d9e8ab59a3c006d924d06 100644 GIT binary patch literal 11870 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
& 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

7M}6UZmiWESP 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