DB旅游生态系统-02-日志管理设计说明

业务设计说明

本模块主要是实现对用户行为日志(例如谁在什么时间点执行了什么操作,访问了哪些方法,传递的什么参数,执行时长等)进行记录、查询、删除等操作。其表设计语句如下:

数据库表设计:

表明根据需求, 这里使用sys_logs作为表明
DB旅游生态系统-02-日志管理设计说明_第1张图片

原型设计说明

基于用户需求,实现静态页面(html/css/js),通过静态页面为用户呈现基本需求实现,如图-1所示。
DB旅游生态系统-02-日志管理设计说明_第2张图片
说明:假如客户对此原型进行了确认,后续则可以基于此原型进行研发。

API设计说明

DB旅游生态系统-02-日志管理设计说明_第3张图片
说明:分层目的主要将复杂问题简单化,实现各司其职,各尽所能。

日志管理列表页面呈现

业务时序分析

当点击首页左侧的"日志管理"菜单时,其总体时序分析如图-3所示:
DB旅游生态系统-02-日志管理设计说明_第4张图片

服务端实现

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");
});

说明:数据加载通常是一个相对比较耗时操作,为了改善用户体验,可以先为用户呈现一个页面,数据加载时,显示数据正在加载中,数据加载完成以后再呈现数据。这样也可满足现阶段不同类型客户端需求(例如手机端,电脑端,电视端,手表端。)

DB旅游生态系统-02-日志管理设计说明_第5张图片

日志管理列表数据呈现

数据架构分析

DB旅游生态系统-02-日志管理设计说明_第6张图片

服务端API架构及业务时序图分析

DB旅游生态系统-02-日志管理设计说明_第7张图片

服务端日志列表数据查询时序图

DB旅游生态系统-02-日志管理设计说明_第8张图片

服务端关键业务及代码实现

基于此对象封装业务执行结果 * 在Java语言,可以简单将内存中的对象分为两大类: *
1)存储数据的对象(设计的关键在属性上)-典型的POJO对象(VO,BO,DO) *
2)执行业务的对象(设计的关键在方法上)-典型的controller,service,dao

创建do封装类

构建实体对象(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;
}

创建bo封装类

业务值对象定义,基于此对象封装数据层返回的数据以及计算的分页信息,具体代码参考如下:

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封装类

定义控制层值对象(VO),目的是基于此对象封装控制层响应结果(在此对象中主要是为业务层执行结果添加状态信息)。Spring MVC框架在响应时可以调用相关API(例如jackson)将其对象转换为JSON格式字符串。

基于此对象封装服务端要响应到客户端的数据,这个数据包含:

  • 1)状态码 (表示这个响应结果是正确的还是错误)
  • 2)状态信息(状态码对象的状态消息)
  • 3)正常的响应数据(例如一个查询结果)
    — POJO:(VO-View Object 封装了表现层要呈现的数据)
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();
	 }
	 //.....
}

数据层代码实现

Dao接口实现

业务描述及设计实现
通过数据层对象,基于业务层参数数据查询日志记录总数以及当前页要呈现的用户行为日志信息。


基于多个记录id执行数据删除操作
@param ids 记录id(可变参数)
@return


基于条件查询用户行为日志记录总数
@param username 查询条件
@return 查询到的记录总数


基于条件查询当前页记录

  • @param username 查询条件
  • @param startIndex 当前页数据的起始位置(用于limit 子句)
  • @param pageSize 当前页面大小(每页最多显示多少条记录)
  • @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);
}

Mapper文件实现

<?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>

单元测试类SysLogDaoTests,数据层

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中 遍历集合


  • 第一种 常规foreach
    for(SysLog log:list) {
    System.out.println(log);
    }

  • 第二种 lambda 表达式
    list.forEach(item->System.out.println(item));

  • 第三种
    list.forEach(System.out::println);

业务层代码实现

Service接口

/
 *基于记录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);
	
}

Service实现类

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);
	}

}

定义Service对象的单元测试类

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);
	}	
}

控制层代码实现

定义Controller类

将此类对象使用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来实现的
    DB旅游生态系统-02-日志管理设计说明_第9张图片

自定义非检查异常

为了对业务中的信息进行更好的反馈和定位,通常会在项目中自定义异常,
尽量避免抛出RuntimeException

  • 目的:对业务中的信息进行更好的反馈和定位
  • 说明:此类中的构造方法参考父类构造方法进行实现

创建ServiceException 类

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);
	  }
	  //。。。。。。。。。。。	  
}
  • 控制类controller会把产生的异常封装在JsonResult对象中,然后抛给SpringMVC,框架下的前端控制器DispatcherServlet
  • SpringMVC会根据注解@ControllerAdvice,找到异常处理类, GlobalExceptionHandler,然后调用其中的异常处理方法处理异常.

控制层响应数据处理分析图

DB旅游生态系统-02-日志管理设计说明_第10张图片

你可能感兴趣的:(项目-demo---技术点,java)