动吧旅游 生态系统项目 part 1

产品功能的实现:
1.首先实现软件的功能;
2.学会控制;
3.运维(项目运行时日志的分析,项目的布署,项目的拓展)

1. 项目简介

1.1概述
动吧旅游生态系统,应市场高端用户需求,公司决定开发这样的一套旅游系统,此系统包含旅游电商系统(广告子系统,推荐子系统,评价子系统,商品子系统,订单子系统,…),旅游分销系统(分销商的管理),旅游业务系统(产品研发,计调服务,系统管理,…
1.2原型分析
基于用户需求,进行原型设计(基于html+css+js进行静态页面实现)。例如系统登录页面:(bootstrap)
在这里插入图片描述
系统登录成功页面(例如starter.html)
在这里插入图片描述
菜单展示页面
在这里插入图片描述
说明:原型设计好以后,会与客户进行确认,确认好以后进行签字,然后就是设计和实现.

2.技术架构

2.1项目分层架构
本项目应用层基于MVC设计思想,进行分层架构设计,其核心目的是将复杂问题简单化,实现各司其职,各尽所能.然后基于“高内聚,低耦合”的设计思想,再实现各对象之间协同,从而提高系统的可维护性,可扩展性。
在这里插入图片描述
其中:
1.开放接口层:可直接封装 Service 方法暴露成 RPC (远程过程调用)接口;也可通过 Web 封装成 http 接口;同时也可进行网关安全控制、流量控制等。
2.终端显示层:负责各个端的模板渲染并显示。当前主要是 thymeleaf 渲染,JS 渲染,移动端展示等。
3.Web请求处理层:主要是对访问控制进行转发,请求参数校验,响应结果处理等
4.Service 层:相对具体的业务逻辑服务层(核心业务,扩展业务)。
5.Manager 层:通用业务处理层,它有如下特征:
1) 对第三方平台封装的层,预处理返回结果及转化异常信息;
2) 对 Service 层通用能力的下沉,如缓存方案、中间件通用处理;
3) 与 DAO 层交互,对多个 DAO 的组合复用。
6.DAO 层:数据访问层,与底层 MySQL、Oracle、Hbase 等进行数据交互。
7.外部接口或第三方平台:包括其它部门RPC开放接口,基础平台,其它公司的 HTTP 接口
说明:对如上分层中涉及到知识的点,逐步加强。
总之:分层的目的就是将复杂问题进行拆解,然后分而治,进而提高系统的可扩展性以及可维护性。
2.2API应用架构
整体API应用架构:
在这里插入图片描述
## 3.技术整合
3.1 环境整备

3.1.1数据库初始化
启动MySQL客户端并登陆,然后执行
1)set names utf8;
2)source d:/jtsys.sql
说明:假如在mysql客户端查询表中数据,可以先执行set names gbk,否则可能会出现乱码。
3.1.2 配置初始环境
1.统一工作区编码(UTF-8)
2.统一JDK版本(JDK1.8)
3.统一MAVEN配置(3.6.3)
创建项目
1.项目名称:CGB-DB-SYS-V1.01
2.组ID: com.cy
3.打包方式:jar
在这里插入图片描述
3.1.3添加依赖

3.1.4修改配置文件(application.yml文件)
a)

#server
server:
  port: 80
  servlet:
    context-path: /
  tomcat:
     threads:
          max: 512
#spring
spring:
  datasource:
    url: jdbc:mysql://local host:3307/d'bsys?serverTimezone=GMT
    username: root
    password: root
  thymeleaf:
    prefix: classpath:/templates/pages/
    suffix: .html
#mybatis
mybatis:
  configuration:
    default-statement-timeout: 30
    map-underscore-to-camel-case: true
  mapper-locations:
  - classpath:/mapper/*/*.xml
#lOG
logging:
  level:
     com.cy: DEBUG

b)application.properties

server.port=8080
server.tomcat.threads.max=512
spring.datasource.url=jdbc:mysql://localhost:3307/dbactivity?serverTimezone=GMT%2B8&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=root
mybatis.mapper-locations=classpath:/mapper/*/*.xml
spring.thymeleaf.prefix=classpath:/templates/pages/
spring.thymeleaf.suffix=.html
spring.thymeleaf.cache=false
logging.level.com.cy=debug

3.2首页初始化
3.2.1定义页面初始资源
1.将js、css、images相关资源拷贝到项目static目录
2.将pages页面拷贝到项目的templates目录

3.3创建包结构及相关文件
在这里插入图片描述
测试:

package com.cy.pj.sys.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

/** 
* 项目中所有页面的处理都放在这个controller中 
*/
@Controller
@RequestMapping("/")
public class PageController {
	 
	@RequestMapping("doIndexUI")
	public String doIndexUI(){
		 return "starter";
               }

}

启动tomcat,在地址栏输入http://localhost/doIndexUI(地址中的端口号要参考自己tomcat启动端口)地址进行访问,假如没有问题会呈现如下页面:
在这里插入图片描述
在这里插入图片描述
AdminLTE基于bootstramp实现(官网:AdminLTE.io)
layui,easyui

用户行为日志管理

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

CREATE TABLE `sys_logs` (

  `id` bigint(20) NOT NULL AUTO_INCREMENT,

  `username` varchar(50) DEFAULT NULL COMMENT '用户名',

  `operation` varchar(50) DEFAULT NULL COMMENT '用户操作',

  `method` varchar(200) DEFAULT NULL COMMENT '请求方法',

  `params` varchar(5000) DEFAULT NULL COMMENT '请求参数',

  `time` bigint(20) NOT NULL COMMENT '执行时长(毫秒)',

  `ip` varchar(64) DEFAULT NULL COMMENT 'IP地址',

  `createdTime` datetime DEFAULT NULL COMMENT '创建时间',

  PRIMARY KEY (`id`)

) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='系统日志';

2.原型设计说明
基于用户需求,实现静态页面(html/css/js),通过静态页面为用户呈现基本需求实现,如图
在这里插入图片描述
3.API设计说明
在这里插入图片描述
业务时序分析
在这里插入图片描述
4.服务端实现
在这里插入图片描述
加载顺序
在这里插入图片描述
PageController中

//在PageController中定义返回日志列表
	@RequestMapping("log/log_list")
	public String doLogUI() {
		return "sys/log_list";
	}
	//在PageController中定义用于返回分页页面
	@RequestMapping("doPageUI")
	public String doPageUI() {
		return "common/page";
	}

starter.html页面:

<script type="text/javascript">
    $(function(){
    	doLoadUI("load-log-id","log/log_list")
    })
    function doLoadUI(id,url){
    	$("#"+id).click(function(){//click事件处理函数
    		//jquery中load函数为一个异步加载的ajax函数
    		//此函数用于在指定位置异步加载资源(并将返回的资源填充到id为mainContentId的div中)
    		$("#mainContentId").load(url);
    		/* $.get(url,function(result){
    			$("#mainContentId").html(result);
    		},"html") */
    	})
    }
</script>

或者改为
在这里插入图片描述
log_list.html中:

<script type="text/javascript">
   $(function(){
	   //当日志列表加载完成后,加载分页页面
	   $("#pageId").load("doPageUI");
   })
</script>

加载成功后页面
在这里插入图片描述
pojo中创建SysLog类

package com.cy.pj.sys.pojo;

import java.io.Serializable;
import java.util.Date;

import lombok.Data;

/** 
*此对象主要用于封装数据库提取的数据或者向数据库写入的数据
*此对象中的属性建议和表中字段有对应的映射关系(名字,类型)
建议:所有用于封装数据的对象都建议实现序列化接口Serializable
1)序列化:将对象转换为字节的过程称之为对象序列化
2)反序列化:将字节转换为对象的过程称之为反序列化
应用场景:对对象进行缓存,将对象进行钝化(写入文件),将对象通过网络进行传输
*/
@Data
public class SysLog implements Serializable{
	
	private static final long serialVersionUID = -1592163223057343412L;
	//对象在序列化和反序列化的时候会基于此id进行数据处理
	//将对象序列化时会将这个id作为版本号写入到字节中
	//反序列化时会从字节中提取版本id然后和类中id进行对比,一致进行反序化
	//private static final long serialVersionUID = 1L;
	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;

}

创建SysLogDao文件



@Mapper
public interface SysLogDao {
	/**
	 * 基于条件查询用户行为日志记录总数
	 * @param username 查询条件
	 * @return 查询到的记录总数
	 * 
	 * */
	int getRowCount(@Param("username")String userName);
	/**
	 * 基于条件查询当前页记录
	 * @param username 查询条件
	 * @param startIndex 查询时的起始位置(用于limit子句)
	 * @param pageSize 当前页面大小(每页最多显示多少条记录)
	 * @return 查询到的记录
	 * 
	 * */
	public List<SysLog> findPageObjects( @Param("username")String  username,

            @Param("startIndex")Integer startIndex,

            @Param("pageSize")Integer pageSize );

}


创建mapper映射文件(SysLogMapper.xml)

<?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">
   <select id="findPageObjects" resultType="com.cy.pj.sys.pojo.SysLog">
  	select * from sys_logs
  	<where>
  		<if test="username!=null and username!=''">
  			username like concat("%",#{username},"%")
  		</if>
  	</where>
  		order by createdTime
  		limit #{startIndex},#{pageSize}
  </select>
  
  <select id="getRowCount" resultType="int">
  	select count(*) from sys_logs
  	<where>
  		<if test="username!=null and username!=''">
  			 username like concat("%",#{username},"%")
  		</if>
  		</where>
  </select>
  </mapper>

提取共性代码

<mapper namespace="com.cy.pj.sys.dao.SysLogDao">
  	<sql id="queryWhereId">
  		from sys_logs
  	<where>
  		<if test="username!=null and username!=''">
  			 username like concat("%",#{username},"%")
  		</if>
  		</where>
  	</sql>
  	
   <select id="findPageObjects" resultType="com.cy.pj.sys.pojo.SysLog">
  	select * 
		<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.entity.SysLog;
@SpringBootTest
public class SysLogDaoTests {
 @Autowired
  private SysLogDao sysLogDao;
    @Test
  public void testGetRowCount() {
		int rows=sysLogDao.getRowCount("admin");
			 System.out.println("rows="+rows);
	 }
 @Test
	public void testFindPageObjects() {
 List<SysLog> list= sysLogDao.findPageObjects("admin", 0, 3);
		for(SysLog log:list) {
					System.out.println(log);
		 }
	 }
}


学习网站
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
pojo:
1)do(data object):数据层封装数据,要求与数据库表结构有一一对应关系
2)bo(business object):在业务层封装业务执行结果,由Service层输出的封装业务逻辑对象
3)vo(view object):显示层对象,通常是web像模板渲染引擎层传输的数据.例如:model
对应关系:
do bo vo
dao service controller
创建PagaObject类

package com.cy.pj.common.pojo;

import java.io.Serializable;
import java.util.List;

import lombok.Data;

/**
 * 基于此对象封装业务执行结果
 * 
 * 在Java语言,可以简单将内存中的对象分为两大类:
 * 1)存储数据的对象(设计的关键在属性上)——典型的pojo对象(VO,BO,DO)
 * 2)执行业务的对象(设计的关键在方法上)——典型的controller,service,dao
 */
@Data
public class PageObject<T> implements Serializable {//pojo中的bo对象
	private static final long serialVersionUID = -1696088982496489016L;
	/** 当前页的页码值 (用户点击以后,客户端传到服务端)*/
	private Integer pageCurrent = 1;

	/** 页面大小 (每页最多可以呈现的记录总数)*/
	private Integer pageSize = 3;

	/** 总行数(通过数据库查询获得) */
	private Integer rowCount = 0;

	/** 总页数(通过计算获得 基于rowCount和页面大小计算出来) */
	private Integer pageCount = 0;

	/** 当前页记录  List<T>中T由PageObject类上定义的泛型T决定*/
	private List<T> records;

	public Integer getPageCurrent() {
		return pageCurrent;
	}

	public PageObject(Integer pageCurrent, Integer pageSize, Integer rowCount,  List<T> records) {
		super();
		this.pageCurrent = pageCurrent;
		this.pageSize = pageSize;
		this.rowCount = rowCount;
		/*
		 * //计算总页数方法一: this.pageCount=rowCount/pageSize; if(rowCount%pageSize!=0) {
		 * pageCount++; }
		 */
		//计算总页数方法二:
		this.pageCount=(rowCount-1)/pageSize+1;
		
		this.records = records;
	}


创建接口SysLogService

package com.cy.pj.sys.service;

import com.cy.pj.common.pojo.PageObject;
import com.cy.pj.sys.pojo.SysLog;

public interface SysLogService {
	 /**
     * @param name 基于条件查询时的参数名
     * @param pageCurrent 当前的页码值
     * @return 当前页记录+分页信息
     */
	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 PageObject<SysLog> findPageObjects(String userName, Integer pageCurrent) {
		// TODO Auto-generated method stub
		/*
		 * 验证参数合法性 1.验证pageCurrent的合法性 不合法抛出IllegalArgumentException异常
		 */
		if (pageCurrent == null || pageCurrent < 1) {
			throw new IllegalArgumentException("当前页码不正确");

		}
		/*
		 * 2. 基于条件查询总记录数并校验 2.1)执行查询
		 */
		int rowCount = sysLogDao.getRowCount(userName);
		if (rowCount == 0) {
			throw new ServiceException("系统没有查到对应记录");
			// 为了对业务中的信息进行更好的反馈和定位,通常会在项目中自定义异常
			// throw new RuntimeException("记录不存在");//尽量避免抛出RuntimeException
		}

		/*
		 * 3.基于pageCurrent条件查询当前页记录(pageSize定义为2) 3.1)定义pageSize
		 */
		int pageSize = 2;// 每页最多显示多少条记录
		// 3.2)计算startIndex
		int startIndex = (pageCurrent - 1) * pageSize;
		// 3.3)执行当前数据的查询操作
		List<SysLog> records = sysLogDao.findPageObjects(userName, startIndex, pageSize);

		/*
		 * 4.对分页信息以及当前页记录进行封装并返回 4.1)构建PageObject对象
		 * 
		 * 
		 * PageObject<SysLog> pageObject=new PageObject<>(); //4.2)封装数据
		 * pageObject.setPageCurrent(pageCurrent);
		 * 
		 * pageObject.setPageSize(pageSize);
		 * 
		 * pageObject.setRowCount(rowCount);
		 * 
		 * pageObject.setRecords(records); if(rowCount%pageSize!=0) pageCount++;
		 * pageObject.setPageCount((rowCount-1)/pageSize+1);
		 * 
		 * //5.返回封装结果 return pageObject;
		 */
		
		return new PageObject<>(pageCurrent, pageSize, rowCount, records);
	}

}

自定义secvice异常,ServiceException类,继承RuntimeException,生成序列化id
在这里插入图片描述
创建com.cy.pj.common.exception包,创建ServiceException

package com.cy.pj.common.exception;
/** 
* 自定义非检查异常
* 目的:对业务中的信息进行更好的反馈和定位
* 说明:此类中的构造方法是基于父类RuntimeException
*/
public class ServiceException extends RuntimeException{
	 private static final long serialVersionUID = 7793296502722655579L;

	public ServiceException() {
		super();
		// TODO Auto-generated constructor stub
	}

	public ServiceException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
		super(message, cause, enableSuppression, writableStackTrace);
		// TODO Auto-generated constructor stub
	}

	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
	}

}

说明:几乎在所有的框架中都提供了自定义异常,例如MyBatis中的BindingException等。
定义Service对象的单元测试类

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

     public void testFindPageObjects() {

        PageObject<SysLog> pageObject=

        sysLogService.findPageObjects("admin", 1);

        System.out.println(pageObject);

     }

}

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

package com.cy.pj.common.pojo;

import java.io.Serializable;

import lombok.Data;

/** 
* 基于此对象封装服务端要相应到客户端的数据,这个数据包含:
* 1)状态码(表示这个响应结果是正确的还是错误的)
* 2)状态信息(状态码对象对应的状态消息)
* 3)正常的响应数据(例如一个查询结果)
* 
* POJO:(vo---view Object:封装了视图层(表现层)要呈现的数据)
*/
@Data
public class JsonResult  implements Serializable{
	private static final long serialVersionUID = 5110901796917551720L;
	//状态码,默认状态为ok
	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();
	}
	public JsonResult() {}
	

}

定义Controller类,并将此类对象使用Spring框架中的@Controller注解进行标识,表示此类对象要交给Spring管理。然后基于@RequestMapping注解为此类定义根路径映射。

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);
	 		//System.out.println(pageObject);
	 //控制层对业务层数据再次进行封装
	 JsonResult jr=new JsonResult();
	 jr.setData(pageObject);
	 
	 
	 return new JsonResult(pageObject);//此位置的业务封装为正常数据

	}

}

定义全局异常处理类,对控制层可能出现的异常,进行统一异常处理,代码如下:

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 com.cy.pj.common.pojo.JsonResult;



/** 
* @ControllerAdvice注解描述的类,springmvc会认为它是控制层
* 全局异常处理对象
*/
@ControllerAdvice
public class GlobalExceptionHanderler {
	/**
	 * @ExceptionHandler 描述的方法为异常处理的方法
	 * 此注解中定义的异常类型,为这个方法可以处理的异常类型,它可以处理
	 * 此异常以及这个异常类型的子类类型的异常
	 * @param e
	 * @return 
	 * */
	@ExceptionHandler(RuntimeException.class)
	@ResponseBody
	public JsonResult doHandleRuntimeException(RuntimeException e) {
		e.printStackTrace();
		return new JsonResult(e);
	}

}

客户端页面事件分析:
当用户点击首页日志管理时,其页面流转分析如图:
在这里插入图片描述
日志列表信息呈现:
log_list.html中:

$(function() {
		/* //当日志列表加载完成后,加载分页页面
		 $("#pageId").load("doPageUI", function() {//页面或者资源加载完成后执行
			//异步加载用户行为日志信息
			doGetObjects();
				});   */
		 $("#pageId").load("doPageUI",function(){doGetObjects();});
		//上面形式简写形式
		//$("#pageId").load("doPageUI",doGetObjects);//不要写成doGetObjects();
			})
//开启ajax异步任务,按条件加载用户行为日志信息
	function doGetObjects() {

		//1.定义url和参数
		var url = "log/doFindPageObjects"		
			//1.1定义参数的方式:
			//var params="pageCurrent=1";
			//1.2定义参数方式
			//var params={"pageCurrent" : 1};
		var params = {
			"pageCurrent" : 1
			};//pageCurrent=2

		//2.发起异步请求

		//请问如下ajax请求的回调函数参数名可以是任意吗?//可以,必须符合标识符的规范

		$.getJSON(url, params, function(result) {

			//请问result是一个字符串还是json格式的js对象?对象
			//debugger;//断点调试
			doHandleQueryResponseResult(result);

		});//特殊的ajax函数

	}
	//如上方法简写形式
	//$.getJSON(url, params, doHandleQueryResponseResult);
	function doHandleQueryResponseResult(result) { //JsonResult
		
		console.log(result);
		console.log("result.state",result.state);
		console.log("result.data",result.data);
		console.log("result.data.records",result.data.records);
				if (result.state == 1) {//ok,正常的响应数据

			//更新table中tbody内部的数据
			 
			doSetTableBodyRows(result.data.records);//将数据呈现在页面上

			//更新页面page.html分页数据

			//doSetPagination(result.data); //此方法写到page.html中

		} else {

			alert(result.message);

		}

	}

	function doSetTableBodyRows(records) {
		//1.获取tbody对象,并清空对象
      
		var tBody = $("#tbodyId");

		tBody.empty();

		//2.迭代records记录,并将其内容追加到tbody

		for ( var i in records) {

			//2.1 构建tr对象

			var tr = $("<tr></tr>");

			//2.2 构建tds对象

			var tds = doCreateTds(records[i]);

			//2.3 将tds追加到tr中

			tr.append(tds);

			//2.4 将tr追加到tbody中

			tBody.append(tr);

		}

	}
	function doCreateTds(data) {

		var tds = "<td><input type='checkbox' class='cBox' name='cItem' value='"+data.id+"'></td>"
				+

				"<td>" + data.username + "</td>" +

				"<td>" + data.operation + "</td>" +

				"<td>" + data.method + "</td>" +

				"<td>" + data.params + "</td>" +

				"<td>" + data.ip + "</td>" +

				"<td>" + data.time + "</td>";

		return tds;

	}

参数取出代码理解:
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
显示如图:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
分页数据的呈现
日志信息列表初始化完成以后初始化分页数据(调用setPagination函数),然后再点击上一页,下一页等操作时,更新页码值,执行基于当前页码值的查询。
在page.html中:
第一步:在page.html页面中定义doSetPagination方法(实现分页数据初始化,在log_list.html中调用),代码如下:

function doSetPagination(page) {//pageObject {rowCount:10,pageCount:3,...}
		//1.初始化总记录数;
		$(".rowCount").html("总记录数(" + page.rowCount + ")");
		//2.初始化总页数
		$(".pageCount").html("总页数(" + page.pageCount + ")");
		//3.初始化当前页码值
		$(".pageCurrent").html("当前页(" + page.pageCurrent + ")");
		//4.存储page信息到指定对象上。
		//data函数为jquery中的一个数据绑定函数,其语法为data(key[,value]);,假如只有key表示取值,两个都有表示值的绑定
		$("#pageId").data("pageCurrent", page.pageCurrent);
		$("#pageId").data("pageCount", page.pageCount);
	}

第二步:第二步:分页页面page.html中注册点击事件。代码如下:

$(function(){
            //事件注册
$("#pageId").on("click",".first,.pre,.next,.last",doJumpToPage);
})

第三步:定义doJumpToPage方法(通过此方法实现当前数据查询)

 function doJumpToPage(){

        //1.获取点击对象的class值

        var cls=$(this).prop("class");//Property

        //2.基于点击的对象执行pageCurrent值的修改

        //2.1获取pageCurrent,pageCount的当前值

        var pageCurrent=$("#pageId").data("pageCurrent");

        var pageCount=$("#pageId").data("pageCount");

        //2.2修改pageCurrent的值

        if(cls=="first"){//首页

                pageCurrent=1;

        }else if(cls=="pre"&&pageCurrent>1){//上一页

                pageCurrent--;

        }else if(cls=="next"&&pageCurrent<pageCount){//下一页

                pageCurrent++;

        }else if(cls=="last"){//最后一页

                pageCurrent=pageCount;

        }else{

         return;

}

        //3.对pageCurrent值进行重新绑定

        $("#pageId").data("pageCurrent",pageCurrent);

        //4.基于新的pageCurrent的值进行当前页数据查询

        doGetObjects();

    }

第四步:修改page.html分页查询方法:(看黄色底色部分)
在这里插入图片描述
列表页面信息查询实现:
当用户点击日志列表的查询按钮时,基于用户输入的用户名进行有条件的分页查询,并将查询结果呈现在页面。
第一步:日志列表页面log_list.html加载完成,在查询按钮上进行事件注册。代码如下:

$(".input-group-btn").on("click",".btn-search",doQueryObjects)

第二步:定义查询按钮对应的点击事件处理函数。代码如下:

function doQueryObjects(){

           //为什么要在此位置初始化pageCurrent的值为1?

           //数据查询时页码的初始位置也应该是第一页

           $("#pageId").data("pageCurrent",1);

           //为什么要调用doGetObjects函数?

           //重用js代码,简化jS代码编写。

           doGetObjects();

   }

第三步:在分页查询函数中追加name参数定义(看黄色底色部分),代码如下:
在这里插入图片描述
第四步:将page.html中响应结果方式改为:
在这里插入图片描述
格式分析:
在这里插入图片描述
在这里插入图片描述
注册点击事件:

//注册事件
   //on函数用于在指定html元素上注册事件,当点击html元素内部的子元素时可以触发事件
	$(function() {
		//$(".pagination").on("click", ".first,.pre,.next,.last", doJumpToPage)
		$("#pageId").on("click", ".first,.pre,.next,.last", doJumpToPage)
	}) 

doJumpToPage()中方法的编写:
prop函数(property缩写):

//1.获得被点击对象的class属性值,基于此值判定被点击的对象
		//prop(property) 函数为jquery中用于获取class属性值的一个函数,其语法为prop(key[,value]);prop函数中假如只有key表示只取值,有key和value表示为属性赋值
		//1.1获取被点击对象 var clickObj=$(this);
		//1.2 获取点击对象的class属性的值 var cls=clickObj.prop("class")
		var cls = $(this).prop("class");

问题分析:
1.SAXParseException
在这里插入图片描述
在这里插入图片描述
2.No tests found with runner ‘JUnit 5’
在这里插入图片描述
3.ParameterResolutionException
在这里插入图片描述
4.无法找到对应的Bean对象(NoSuchBeanDefinitionException),如图:
在这里插入图片描述
问题分析:
1)检测key的名字写的是否正确。
2)检测spring对此Bean对象的扫描,对于dao而言。
3)使用有@Mapper注解描述或者在@MapperScan扫描范围之内。
4)以上都正确,要检测是否编译了。
5.绑定异常(BindingException),如图:
在这里插入图片描述
问题分析:
1)接口的类全名与对应的映射文件命名空间不同。
2)接口的方法名与对应的映射文件元素名不存在。
3)检测映射文件的路径与application.properties或者application.yml中的配置是否一致。
4)以上都没有问题时,检测你的类和映射文件是否正常编译。
6.反射异常(ReflectionException),如图:
在这里插入图片描述
问题分析:
1)映射文件中动态sql中使用的参数在接口方法中没有使用@Param注解修饰
2)假如使用了注解修饰还要检测名字是否一致。
说明:当动态sql的参数在接口中没有使用@Param注解修饰,还可以借助_parameter这个变量获取参数的值(mybatis中的一种规范)。
7.结果映射异常,如图
在这里插入图片描述
图-16
问题分析:getRowCount元素可能没有写resultType或resultMap。
8.绑定异常
在这里插入图片描述
问题分析:绑定异常,检测findPageObjects方法参数与映射文件参数名字是否匹配.检查版本号是否为最新版本,不是最新版本,参数传入要加@params(" ")
9.Bean创建异常,如图
在这里插入图片描述
问题分析:应该是查询时的结果映射对的类全名写错了
10.请求方式不匹配,如图
在这里插入图片描述
问题分析:请求方式与控制层处理方式不匹配。
11.响应结果异常,如图
在这里插入图片描述
问题分析:服务端响应数据不正确,例如服务端没有注册将对象转换为JSON串的Bean
12.请求参数异常
在这里插入图片描述
问题分析:客户端请求参数中不包含服务端控制层方法参数或格式不匹配
13.JS编写错误,如图
在这里插入图片描述
问题分析:点击右侧VM176:64位置进行查看
在这里插入图片描述
问题分析:找到对应位置,检测data的值以及数据来源
在这里插入图片描述
问题分析:找到对应位置,假如无法确定位置,可排除法或打桩,debug分析。
在这里插入图片描述
问题分析:调用length方法的对象有问题,可先检测下对象的值
在这里插入图片描述
问题分析:检测record定义或赋值的地方。
资源没找到
在这里插入图片描述
问题分析:服务端资源没找到,检查url和controller映射,不要点击图中的jquery。
14.视图解析异常,如图
在这里插入图片描述
问题分析:检查服务端要访问的方法上是否有@ResponseBody注解.

重难点分析:

1.日志管理整体业务分析与实现。
1)分层架构(应用层MVC:基于spring的mvc 模块)。
2)API架构(SysLogDao,SysLogService,SysLogController)。
3)业务架构(查询,删除,添加用户行为日志)。
4)数据架构(SysLog,PageObject,JsonResult,…)。
2.日志管理持久层映射文件中SQL元素的定义及编写。
1)定义在映射文件”mapper/sys/SysLogMapper.xml”(必须在加载范围内)。
2)每个SQL元素必须提供一个唯一ID,对于select必须指定结果映射(resultType)。
3)系统底层运行时会将每个SQL元素的对象封装一个值对象
(MappedStatement)。
3.日志管理模块数据查询操作中的数据封装。
1)数据层(数据逻辑)的SysLog对象应用(一行记录一个log对象)。
2)业务层(业务逻辑)PageObject对象应用(封装每页记录以及对应的分页信息)。
3)控制层(控制逻辑)的JsonResult对象应用(对业务数据添加状态信息)。
4.日志管理控制层请求数据映射,响应数据的封装及转换(转换为json 串)。
1)请求路径映射,请求方式映射(GET,POST),请求参数映射(直接量,POJO)。
2)响应数据两种(页面,JSON串)。
5.日志管理模块异常处理如何实现的。
1)请求处理层(控制层)定义统一(全局)异常处理类。
2)使用注解@ControllerAdvice描述类,使用@ExceptionHandler描述方法.
3)异常处理规则:能处理则处理,不能处理则抛出。

FAQ分析

用户行为日志表中都有哪些字段?(面试)
用户行为日志是如何实现分页查询的?(limit)
用户行为数据的封装过程?(数据层,业务层,控制层)
项目中的异常是如何处理的?
页面中数据乱码,如何解决?(数据来源,请求数据,响应数据)
说说的日志删除业务是如何实现?
Spring MVC 响应数据处理?(view,json)
项目你常用的JS函数说几个?(data,prop,ajax,each,…)
MyBatis中的@Params注解的作用?(为参数变量其别名)
Jquery中data函数用于做什么?可以借助data函数将数据绑定到指定对象,语法为data(key[,value]),key和value为自己业务中的任意数据,假如只有key表示取值。
Jquery中的prop函数用于获取html标签对象中”标准属性”的值或为属性赋值,其语法为prop(propertyName[,propertyValue]),假如只有属性名则为获取属性值。
Jquery中attr函数为用户获取html标签中任意属性值或为属性赋值的一个方法,其语法为attr(propertyName[,propertyValue]),假如只有属性名则为获取属性值。

日志写操作事务的传播特性如何配置?(每次开启新事务)?
日志写操作为什么应该是异步的?
Spring 中的异步操作如何实现?
Spring 中的@Async如何应用?
项目中的BUG分析及解决套路?

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值