-
vue+quasar+electron+springboot+mysql撸一个TODO LIST 看板
先看效果
写本项目的目的有几点:
- 学习下vue+electron桌面开发
- 学习下java和spring开发(本人一直使用PHP)
- 一直缺少一款能适合自己的TODO LIST软件,能有桌面端的
可直接打包成dmg、exe 等二进制文件使用。
这是我打包后的效果。
技术栈
- vue
- quasar
- electron
- springboot
- mysql
部分后端知识
自定义注解
|
|
|
|
|
public 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 |
|
*/ |
|
|
|
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 |
|
*/ |
|
|
|
public Response handHibernateException(ConstraintViolationException ex) { |
|
return new Response(ResponseRet.dbExecuteFail, ex.getSQLException().toString()); |
|
} |
|
|
|
|
|
public Response handGenericJDBCException(GenericJDBCException ex) { |
|
return new Response(ResponseRet.dbExecuteFail, ex.getSQLException().toString()); |
|
} |
你可以全局处理entity定义的参数约束,或其他异常。
p6spy记录sql和耗时
|
|
|
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)); |
|
} |
|
|
|
|
|
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()); |
|
} |
|
} |
源码地址
- 前端 https://github.com/visonforcoding/carambola-todo
- 后端 https://github.com/visonforcoding/carambola-todo-service
出 处:https://www.cnblogs.com/maitian2013/p/14636632.html
最新更新
带有参数的装饰器
类装饰器
django中的auth模块与admin后台管理
python的日期处理
字符串常用方法
基本数据类型概述
python-map()函数基本用法
python带你实现任意下载AcFun视频数据~
bbs项目之注册功能
变量的定义和使用
三大常用数据库事务详解之三:事务运行
三大常用关系型数据库事务详解之二:基
三大关系型数据库事务详解之一:基本概
MongoDB常用命令(2)
MongoDB基本介绍与安装(1)
SQLServer触发器调用JavaWeb接口
SQL Server索引的原理深入解析
SqlServer2016模糊匹配的三种方式及效率问题
SQL中Truncate的用法
sqlserver 多表关联时在where语句中慎用tri
VB.NET中如何快速访问注册表
ASP.NET中图象处理过程详解
Vue(1)Vue安装与使用
JavaScript 语言入门
js将一段字符串的首字母转成大写
纯原生html编写的h5视频播放器
H5仿原生app短信验证码vue2.0组件附源码地
TypeScript(4)接口
TypeScript(3)基础类型
TypeScript(2)WebStorm自动编译TypeScript配置