vue+quasar+electron+springboot+mysql擼一個TODO LIST 看板

GO麥田麥穗發表於2021-04-13

先看效果

寫本專案的目的有幾點:

  1. 學習下vue+electron桌面開發
  2. 學習下java和spring開發(本人一直使用PHP)
  3. 一直缺少一款能適合自己的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());
        }
    }

原始碼地址

相關文章