本模块主要是实现对用户行为日志(例如谁在什么时间点执行了什么操作,访问了哪些方法,传递的什么参数,执行时长等)进行记录、查询、删除等操作。其表设计语句如下:
基于用户需求,实现静态页面(html/css/js),通过静态页面为用户呈现基本需求实现,如图-1所示。
说明:假如客户对此原型进行了确认,后续则可以基于此原型进行研发。
说明:分层目的主要将复杂问题简单化,实现各司其职,各尽所能。
当点击首页左侧的"日志管理"菜单时,其总体时序分析如图-3所示:
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 页面,页面加载完成以后,注册日志管理菜单项的点击事件,当点击菜单管理时,执行事件处理函数。关键代码如下:
(starter.html)
$(function(){
doLoadUI("load-log-id","log/log_list")
})
function doLoadUI(id,url){
$("#"+id).click(function(){
$("#mainContentId").load(url);
});
}
load函数
为jquery中的ajax异步请求函数。 jquery中的load函数为一个异步加载的ajax函数。 此函数用于在指定位置异步加载资源(并将返回的资源填充到这个指定位置)是引用
.click元素绑定点击事件
<script> $(function(){ //当点击btn按钮时,触发点击事件执行其中的函数 $("#btn").click( function(){ alert("input按钮被点击了..."); }); </script> <body><input id="btn" type="button" value="点我~!" /> </body> ```
日志列表页面事件处理
业务描述与设计实现
当日志列表页面加载完成以后异步加载分页页面(page.html)。
关键代码设计与实现:
在log_list.html*页面中异步加载page页面,这样可以实现分页页面重用,哪里需要分页页面,哪里就进行页面加载即可。关键代码如下:
(log_list.html)
$(function(){
$("#pageId").load("doPageUI");
});
说明:数据加载通常是一个相对比较耗时操作,为了改善用户体验,可以先为用户呈现一个页面,数据加载时,显示数据正在加载中,数据加载完成以后再呈现数据。这样也可满足现阶段不同类型客户端需求(例如手机端,电脑端,电视端,手表端。)
基于此对象封装业务执行结果 * 在Java语言,可以简单将内存中的对象分为两大类: *
1)存储数据的对象(设计的关键在属性上)-典型的POJO对象(VO,BO,DO) *
2)执行业务的对象(设计的关键在方法上)-典型的controller,service,dao
构建实体对象(POJO)封装从数据库查询到的记录,一行记录映射为内存中一个的这样的对象。对象属性定义时尽量与表中字段有一定的映射关系,并添加对应的set/get/toString等方法,便于对数据进行更好的操作。
建议: 所有用于封装数据的对象都建议实现序列化接口 (Serializable), 将对象通过网络进行传输
import lombok.Data;----------自动生成get() set()方法
@Data
public class SysLog implements Serializable{
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;
}
业务值对象定义,基于此对象封装数据层返回的数据以及计算的分页信息,具体代码参考如下:
package com.cy.pj.common.pojo;
import java.io.Serializable;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class PageObject<T> implements Serializable{//pojo中的bo对象,new PageObject
private static final long serialVersionUID = -3130527491950235344L;
/**总记录数(从数据库查询出来的)*/
private Integer rowCount;
/**总页数(基于rowCount和页面大小计算出来的)*/
private Integer pageCount;
/**页面大小(每页最多可以呈现的记录总数)*/
private Integer pageSize;
/**当前页码值(客户端传递)*/
private Integer pageCurrent;
/**当前页记录,list集合中的T由PageObject类上定义的泛型决定*/
private List<T> records;
public PageObject(Integer rowCount, Integer pageSize, Integer pageCurrent, List<T> records) {
super();
this.rowCount = rowCount;
this.pageSize = pageSize;
this.pageCurrent = pageCurrent;
this.records = records;
//计算总页数的方法一:
//this.pageCount=this.rowCount/this.pageSize;
//if(this.rowCount%this.pageSize!=0)pageCount++;
//计算总页数的方法二:
this.pageCount=(rowCount-1)/pageSize+1;
}
}
定义控制层值对象(VO),目的是基于此对象封装控制层响应结果(在此对象中主要是为业务层执行结果添加状态信息)。Spring MVC框架在响应时可以调用相关API(例如jackson)将其对象转换为JSON格式字符串。
基于此对象封装服务端要响应到客户端的数据,这个数据包含:
package com.cy.pj.common.pojo;
import java.io.Serializable;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
public class JsonResult implements Serializable{
private static final long serialVersionUID = 5110901796917551720L;
/**状态吗*/
private Integer state=1;
/**状态信息*/
private String message="ok";
/**正常的响应数据*/
private Object data;
public JsonResult(String message){
this.message=message;
}
public JsonResult(Object data){
this.data=data;
}
public JsonResult(Throwable e){//Throwable为所有异常类的父类
this.state=0;
this.message=e.getMessage();
}
//.....
}
业务描述及设计实现
通过数据层对象,基于业务层参数数据查询日志记录总数以及当前页要呈现的用户行为日志信息。
基于多个记录id执行数据删除操作
@param ids 记录id(可变参数)
@return
基于条件查询用户行为日志记录总数
@param username 查询条件
@return 查询到的记录总数
基于条件查询当前页记录
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import com.cy.pj.sys.pojo.SysLog;
@Mapper
public interface SysLogDao {
int deleteObjects(Integer ...ids);// int deleteObjects(Integer[] ids)
/**
* 基于条件查询用户行为日志记录总数
* @param username 查询条件
* @return 查询到的记录总数
*/
int getRowCount(String username);
/**
* 基于条件查询当前页记录
* @param username 查询条件
* @param startIndex 当前页数据的起始位置(用于limit 子句)
* @param pageSize 当前页面大小(每页最多显示多少条记录)
* @return 查询到的记录
*/
List<SysLog> findPageObjects(String username,
Integer startIndex,Integer pageSize);
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cy.pj.sys.dao.SysLogDao">
<!-- 基于id删除日志信息 -->
<delete id="deleteObjects">
delete from sys_logs
<where>
<choose><!-- 选择分支 -->
<when test="ids!=null and ids.length>0">
id in
<foreach collection="ids"
open="(" close=")" separator="," item="id">
#{id}
</foreach>
</when>
<otherwise><!-- when表达式返回值为false会执行otherwise -->
1=2
</otherwise>
</choose>
</where>
</delete>
<!-- 删除方式1
<delete id="deleteObjects">
delete from sys_logs
<where>
<if test="ids!=null and ids.length>0">
id in
<foreach collection="ids"
open="(" close=")" separator="," item="id">
#{id}
</foreach>
</if>
or 1=2
</where>
</delete>
-->
<!-- 通过sql标签提取sql中共性 -->
<sql id="queryWhereId">
from sys_logs
<if test="username!=null and username!=''">
<where>
username like concat("%",#{username},"%")
</where>
</if>
</sql>
<!-- 查询当前页记录总数 -->
<select id="findPageObjects"
resultType="com.cy.pj.sys.pojo.SysLog">
select *
<!-- 包含id为 queryWhereId的sql元素-->
<include refid="queryWhereId"/>
order by createdTime
limit #{startIndex},#{pageSize}
</select>
<!-- 按条件查询总记录数 -->
<select id="getRowCount" resultType="int">
select count(*)
<include refid="queryWhereId"/>
</select>
</mapper>
package com.cy.pj.sys.dao;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import com.cy.pj.sys.pojo.SysLog;
@SpringBootTest
public class SysLogDaoTests {
@Autowired
private SysLogDao sysLogDao;
@Test
void testDeleteObjects() {
int rows=sysLogDao.deleteObjects(15,16);
System.out.println("delete.rows="+rows);
}
@Test
void testGetRowCount() {
int rowCount=sysLogDao.getRowCount("admin");
System.out.println("rowCount="+rowCount);
}
@Test
void testFindPageObjects() {
List<SysLog> list=
sysLogDao.findPageObjects("admin", 0, 3);
list.forEach(item->System.out.println(item));
}
}
jdk8中 遍历集合
/
*基于记录id删除日志信息
* @param ids
* @return 删除的行数
*/
/ 基于条件进行分页查询
* @param userame 查询条件
* @param pageCurrent 当前页面
* @return 查询到记录信息以及分析信息
*/
SysLogService接口
import com.cy.pj.common.pojo.PageObject;
import com.cy.pj.sys.pojo.SysLog;
public interface SysLogService {
int deleteObjects(Integer... ids);
PageObject<SysLog> findPageObjects(String username,Integer pageCurrent);
}
SysLogServiceImpl实现类
package com.cy.pj.sys.service.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.cy.pj.common.exception.ServiceException;
import com.cy.pj.common.pojo.PageObject;
import com.cy.pj.sys.dao.SysLogDao;
import com.cy.pj.sys.pojo.SysLog;
import com.cy.pj.sys.service.SysLogService;
@Service
public class SysLogServiceImpl implements SysLogService {
@Autowired
private SysLogDao sysLogDao;
@Override
public int deleteObjects(Integer... ids) {
//1.参数校验
if(ids==null||ids.length==0)
throw new IllegalArgumentException("参数值无效");
//2.执行删除
int rows=sysLogDao.deleteObjects(ids);
//3.验证结果
if(rows==0)
throw new ServiceException("记录可能已经不存在");
return rows;
}
@Override
public PageObject<SysLog> findPageObjects(String username, Integer pageCurrent) {
//1.参数校验。
if(pageCurrent==null||pageCurrent<1)
throw new IllegalArgumentException("页码值无效");
//2.基于用户名查询总记录数并校验。
int rowCount=sysLogDao.getRowCount(username);
if(rowCount==0)
//为了对业务中的信息进行更好的反馈和定位,通常会在项目中自定义异常
//throw new RuntimeException("记录不存在");//尽量避免抛出RuntimeException
throw new ServiceException("记录不存在");
//3.基于pageCurrent查询当前页记录。
int pageSize=3;//每页最多显示多少条记录
int startIndex=(pageCurrent-1)*pageSize;
List<SysLog> records=
sysLogDao.findPageObjects(username, startIndex, pageSize);
//4.对查询结果进行封装并返回。
//封装方式1
// PageObject pageObject=new PageObject<>();
// pageObject.setRowCount(rowCount);
// pageObject.setPageSize(pageSize);
// pageObject.setPageCurrent(pageCurrent);
// int pageCount=rowCount/pageSize;
// if(rowCount%pageSize!=0)pageCount++;
// pageObject.setPageCount(pageCount);
// pageObject.setRecords(records);
// return pageObject;
//封装方式2
//return new PageObject<>(rowCount, pageCount, pageSize, pageCurrent, records);
//封装方式3
return new PageObject<>(rowCount, pageSize, pageCurrent, records);
}
}
SysLogServiceTests
package com.cy.pj.sys.service;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import com.cy.pj.common.pojo.PageObject;
import com.cy.pj.sys.pojo.SysLog;
@SpringBootTest
public class SysLogServiceTests {
@Autowired
private SysLogService sysLogService;
@Test
void testFindPageObjects() {
PageObject<SysLog> pageObject=
sysLogService.findPageObjects("admin",1);
System.out.println(pageObject);
}
}
将此类对象使用Spring框架中的@Controller注解进行标识,表示此类对象要交给Spring管理。然后基于@RequestMapping注解为此类定义根路径映射。代码参考如下:
SysLogController
package com.cy.pj.sys.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.cy.pj.common.pojo.JsonResult;
import com.cy.pj.common.pojo.PageObject;
import com.cy.pj.sys.pojo.SysLog;
import com.cy.pj.sys.service.SysLogService;
@Controller
@RequestMapping("/log/")
public class SysLogController {
@Autowired
private SysLogService sysLogService;
@RequestMapping("doFindPageObjects")
@ResponseBody
public JsonResult doFindPageObjects(String username,Integer pageCurrent) {
PageObject<SysLog> pageObject=sysLogService.findPageObjects(username, pageCurrent);
return new JsonResult(pageObject);//此位置封装为业务的正常数据
}
}
方法1
JsonResult jr=new JsonResult();
jr.setData(pageObject);
方法2
return new JsonResult(pageObject);
1)此值会返回给DispatcherServlet对象
2)DispatcherServlet对象会将JsonResult转换为json格式字符串然后响应到客户端
说明:转换过程是DispatcherServlet对象调用了jackson api来实现的
为了对业务中的信息进行更好的反馈和定位,通常会在项目中自定义异常,
尽量避免抛出RuntimeException
ServiceException
public class ServiceException extends RuntimeException {
private static final long serialVersionUID = -9085326160255400760L;
public ServiceException(String message, Throwable cause) {
super(message, cause);
// TODO Auto-generated constructor stub
}
public ServiceException(String message) {
super(message);
// TODO Auto-generated constructor stub
public ServiceException(Throwable cause) {
super(cause);
// TODO Auto-generated constructor stub
}
}
对控制层可能出现的异常,进行统一异常处理,代码如下
GlobalExceptionHandler
package com.cy.pj.common.web;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import com.cy.pj.common.pojo.JsonResult;
/**
* @ControllerAdvice 注解描述的类,spring mvc会认为它是一个控制层全局异常处理对象。
*/
//@ControllerAdvice
//@ResponseBody
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* @ExceptionHandler 注解描述的方法为异常处理方法(不是我们定的),
* 此注解中定义的异常类型,为这个方法可以处理的异常类型,它可以处理
* 此异常以及这个异常类型的子类类型的异常。
* @param e 此参数用于接收具体异常对象,其类型一般与@ExceptionHandler
* 注解中定义异常类型相同或者为其父类类型。
* @return 封装了异常信息的对象
*/
@ExceptionHandler(RuntimeException.class)
//告诉spring mvc最终将响应结果转换为json格式进行输出。
public JsonResult doHandleRuntimeException(RuntimeException e) {
//方法内部实现异常处理
//例如:输出异常,封装异常
//输出或打印异常
e.printStackTrace();
//封装异常信息
return new JsonResult(e);
}
//。。。。。。。。。。。
}