DB-日志管理模块设计
日志管理设计说明
记录用户操作的日志功能, 可以对用户的操作习惯进行分析
主要是实现对用户行为日志(例如谁在什么时间点执行了什么操作,访问了哪些方法,传递的什么参数,执行时长等)进行记录、查询、删除等操作。
数据库表设计:
表明根据需求, 这里使用sys_logs作为表明
字段
类型
注释
id
bigint(20)
主键自增
username
varchar(50)
记录用户名
operation
varchar(50)
记录用户操作
method
varchar(200)
请求方法
params
varchar(5000)
请求参数
time
bigint(20)
执行时长
ip
varcher(64)
用户ip
createdTime
datetime
创建时间
API设计说明
说明:分层目的主要将复杂问题简单化,实现各司其职,各尽所能。
日志管理列表页面呈现
业务时序分析
服务端实现
Controller实现
业务描述与设计实现
基于日志管理的请求业务,在PageController中添加doLogUI方法,doPageUI方法分别用于返回日志列表页面,日志分页页面。
关键代码设计与实现
第一步:在PageController中定义返回日志列表的方法。代码如下:
@RequestMapping("log/log_list")
public String doLogUI() {
return "sys/log_list";
}
第二步:在PageController中定义用于返回分页页面的方法。代码如下:@RequestMapping("doPageUI")
public String doPageUI() {
return "common/page";
}
客户端实现
日志菜单事件处理
业务描述与设计
首先准备日志列表页面(/templates/pages/sys/log_list.html),然后在starter.html页面中点击日志管理菜单时异步加载日志列表页面。
关键代码设计与实现
找到项目中的starter.html 页面,页面加载完成以后,注册日志管理菜单项的点击事件,当点击日志管理时,执行事件处理函数。关键代码如下:
//此函数是在页面加载完成以后执行
(function(){
doLoadUI("load-log-id","log/log_list");
})()
//基于不同对象的点击事件执行资源加载操作
function doLoadUI(id,url){
$("#"+id).click(function(){//click事件处理函数
//jquery中的load函数为一个异步加载的ajax函数。
//此函数用于在指定位置异步加载资源(并将返回的资源填充到这个指定位置)
$("#mainContentId").load(url);
})
}
日志列表页面事件处理
业务描述与设计实现
当日志列表页面加载完成以后异步加载分页页面(page.html)。
关键代码设计与实现:
在log_list.html页面中异步加载page页面,这样可以实现分页页面重用,哪里需要分页页面,哪里就进行页面加载即可。
$(function(){
$("#pageId").load("doPageUI");
});
说明:数据加载通常是一个相对比较耗时操作,为了改善用户体验,可以先为用户呈现一个页面,数据加载时,显示数据正在加载中,数据加载完成以后再呈现数据。这样也可满足现阶段不同类型客户端需求.
日志管理列表数据呈现
数据架构分析
日志查询服务端数据基本架构
服务端API架构及业务时序图分析
服务端日志分页查询代码基本架构
服务端日志列表数据查询时序图
服务端关键业务及代码实现
POJO类实现
此对象主要用于封装数据库提取的数据, 或者向输入框写入的数据, 此对象中的属性建议和表中字段有对应的映射关系(名字, 类型)
建议: 所有用于封装数据的对象都建议实现序列化接口 (Serializable), 将对象通过网络进行传输
序列化: 将对象转为字节的过程称之为对象序列化
反序列化: 将字节转为对象的过程称之为反序列化
应用场景: 对对象进行缓存, 将对象进行钝化(写入文件)
package com.cy.pj.sys.pojo;
@Data
public class SysLog implements Serializable{
//对象在序列化和反序列化时会基于此id进行数据处理。
//将对象序列化时会将这个id作为版本写入到字节中。
//将字节反序列化会从字节中提取这个版本id然后和类中的版本id进行比对,一致则进行反序列化。
private static final long serialVersionUID = -1592163223057343412L;
private Integer id;
//用户名
private String username;
//用户操作
private String operation;
//请求方法
private String method;
//请求参数
private String params;
//执行时长(毫秒)
private Long time;
//IP地址
private String ip;
//创建时间
private Date createdTime;
}
说明:通过此对象除了可以封装从数据库查询的数据,还可以封装客户端请求数据,实现层与层之间数据的传递。
Dao接口实现
业务描述及设计实现
通过数据层对象,基于业务层参数数据查询日志记录总数以及当前页要呈现的用户行为日志信息。
关键代码分析及实现:
1 定义数据层接口对象,通过将此对象保证给业务层以提供日志数据访问
2 在SysLogDao接口中添加getRowCount方法用于按条件统计记录总数
3 在SysLogDao接口中添加findPageObjects方法,基于此方法实现当前页记录的数据查询操作。
package com.cy.pj.sys.dao;
@Mapper
public interface SysLogDao {
/**
* 基于条件查询用户行为日志记录总数
* @param username 查询条件
* @return 查询到的记录总数
*/
int getRowCount(@Param("username")String username);
/**
* 基于条件查询当前页记录
* @param username 查询条件
* @param startIndex 当前页数据的起始位置(用于limit 子句)
* @param pageSize 当前页面大小(每页最多显示多少条记录)
* @return 查询到的记录
*/
List findPageObjects(
@Param("username")String username,
@Param("startIndex")Integer startIndex,
@Param("pageSize")Integer pageSize);
}
说明:
当DAO中方法参数多余一个时尽量使用@Param注解进行修饰并指定名字,然后在Mapper文件中便可以通过类似#{username}方式进行获取,否则只能通过#{arg0},#{arg1}或者#{param1},#{param2}等方式进行获取。
当DAO方法中的参数应用在动态SQL中时无论多少个参数,尽量使用@Param注解进行修饰并定义。
Mapper文件实现
业务描述及设计实现
基于Dao接口创建映射文件,在此文件中通过相关元素(例如select)描述要执行的数据操作
关键代码设计及实现
在映射文件的设计目录(mapper/sys)中添加SysLogMapper.xml映射文件
/p>
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
from sys_logs
username like concat("%",#{username},"%")
resultType="com.cy.pj.sys.pojo.SysLog">
select *
order by createdTime
limit #{startIndex},#{pageSize}
select count(*)
注意:
提取sql语句的共性内容使用sql标签进行提取,并且赋予id属性然后再所需的sql标签内部通过 标签直接引入,减少代码的编写.
bug分析:
测试数据层数据:
package com.cy.pj.sys.dao;//测试包下
@SpringBootTest
public class SysLogDaoTests {
@Autowired
private SysLogDao sysLogDao;
@Test
public void testGetRowCount() {
System.out.println(sysLogDao.getRowCount("admin"));
}
@Test
public void testFindPageObjects() {
List list = sysLogDao.findPageObjects("admin", 0, 3);
//for (SysLog sysLog : list) {
//System.out.println(sysLog);
//}
list.forEach(System.out::println);//遍历方式
}
}
Service接口及实现类
业务描述与设计实现
业务层主要是实现模块中业务逻辑的处理。在日志分页查询中,业务层对象首先要通过业务方法中的参数接收控制层数据(例如username,pageCurrent)并校验。然后基于用户名进行总记录数的查询并校验,再基于起始位置及页面大小进行当前页记录的查询,最后对查询结果进行封装并返回。
关键代码设计及实现
业务值对象定义,基于此对象封装数据层返回的数据以及计算的分页信息
基于此对象封装业务执行结果
在Java语言,可以简单将内存中的对象分为两大类:
1)存储数据的对象(设计的关键在属性上)-典型的POJO对象(VO,BO,DO)
2)执行业务的对象(设计的关键在方法上)-典型的controller,service,dao
package com.cy.pj.common.pojo;
@Data
@NoArgsConstructor
//@AllArgsConstructor
public class PageObject implements Serializable {//pojo中的bo对象
private static final long serialVersionUID = -3130527491950235344L;
//总记录数 查出来的
private Integer rowCount;
//总页数 计算出来的 基于rowCount 和 pageSize
private Integer pageCount;
//页面大小(每页最多可