vue+quasar+electron+springboot+mysql撸一个TODO LIST 看板
先看效果
写本项目的目的有几点:
- 学习下vue+electron桌面开发
- 学习下java和spring开发(本人一直使用PHP)
- 一直缺少一款能适合自己的TODO LIST软件,能有桌面端的
可直接打包成dmg、exe 等二进制文件使用。
这是我打包后的效果。
技术栈
- vue
- quasar
- electron
- springboot
- mysql
部分后端知识
自定义注解
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface LoginRequired { }
自定义一个loginRequired
注解,标注是否需要登录
然后在拦截器里进行全局检测
HandlerMethod handlerMethod = (HandlerMethod) handler; Method method = handlerMethod.getMethod(); LoginRequired classRequired = method.getDeclaringClass().getAnnotation(LoginRequired.class); // 判断接口是否需要登录 LoginRequired methodRequired = method.getAnnotation(LoginRequired.class); if (classRequired == null && methodRequired == null) { return true; } appService.initSession(); //token 方式验证 if (request.getSession().getAttribute(App.SESSION_USER) != null) { return true; }
@RestControllerAdvice
利用@RestControllerAdvice注解进行全局控制器异常拦截
/** * ConstraintViolationException */ @ExceptionHandler(MethodArgumentNotValidException.class) public Response handleConstraintViolationException(MethodArgumentNotValidException ex) { Map<String, String> errors = new HashMap<>(); ex.getBindingResult().getAllErrors().forEach((error) -> { String fieldName = ((FieldError) error).getField(); String errorMessage = error.getDefaultMessage(); errors.put(fieldName, errorMessage); }); return new Response(ResponseRet.parametrErrror, "参数错误", errors); } /** * 违反约束异常 字段不为空等 * * @param ex * @return */ @ExceptionHandler(ConstraintViolationException.class) public Response handHibernateException(ConstraintViolationException ex) { return new Response(ResponseRet.dbExecuteFail, ex.getSQLException().toString()); } @ExceptionHandler(GenericJDBCException.class) public Response handGenericJDBCException(GenericJDBCException ex) { return new Response(ResponseRet.dbExecuteFail, ex.getSQLException().toString()); }
你可以全局处理entity定义的参数约束,或其他异常。
p6spy记录sql和耗时
@Override public void onAfterExecuteQuery(PreparedStatementInformation statementInformation, long timeElapsedNanos, SQLException e) { App.sqlCount.set(App.sqlCount.get() + 1); Long duration = timeElapsedNanos / 1000000; App.sqlDuration.set(App.sqlDuration.get() + duration); Log.info(String.format("执行sql || %s 耗时 %s ms", statementInformation.getSqlWithValues(), duration)); } @Override public void onAfterExecuteUpdate(PreparedStatementInformation statementInformation, long timeElapsedNanos, int rowCount, SQLException e) { App.sqlCount.set(App.sqlCount.get() + 1); Log.info(App.sqlCount.get().toString()); Long duration = timeElapsedNanos / 1000000; App.sqlDuration.set(App.sqlDuration.get() + duration); String singleLineSql = statementInformation.getSqlWithValues().replaceAll("\n", "\\\\n"); Log.info(String.format("执行sql || %s 耗时 %s ms", singleLineSql, duration)); }
记录带所有参数的sql和执行耗时
继承DispatcherServlet记录请求参数
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper(request); ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response); //创建一个 json 对象,用来存放 http 日志信息 ObjectNode rootNode = mapper.createObjectNode(); rootNode.put("uri", requestWrapper.getRequestURI()); rootNode.put("clientIp", requestWrapper.getRemoteAddr()); // rootNode.set("requestHeaders", mapper.valueToTree(getRequestHeaders(requestWrapper))); String method = requestWrapper.getMethod(); String contentType = requestWrapper.getContentType(); rootNode.put("method", method); try { super.doDispatch(requestWrapper, responseWrapper); } finally { if (method.equals("GET") || method.equals("DELETE")) { rootNode.set("request", mapper.valueToTree(requestWrapper.getParameterMap())); } else if (contentType.equals("application/x-www-form-urlencoded")) { rootNode.set("request", mapper.valueToTree(requestWrapper.getParameterMap())); } else { JsonNode newNode = mapper.readTree(requestWrapper.getContentAsByteArray()); rootNode.set("request", newNode); } rootNode.put("status", responseWrapper.getStatus()); JsonNode newNode = mapper.readTree(responseWrapper.getContentAsByteArray()); rootNode.set("response", newNode); responseWrapper.copyBodyToResponse(); // rootNode.set("responseHeaders", mapper.valueToTree(getResponsetHeaders(responseWrapper))); Log.info(rootNode.toString()); } }
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步