日志管理设计说明

  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='系统日志';

  1. 原型设计说明

基于用户需求,实现静态页面(html/css/js),通过静态页面为用户呈现基本需求实现,如图-1所示。

日志管理设计说明_第1张图片

图-1

说明:假如客户对此原型进行了确认,后续则可以基于此原型进行研发。

  1. API设计说明

日志业务后台API分层架构及调用关系如图-2所示:

日志管理设计说明_第2张图片

说明:分层目的主要将复杂问题简单化,实现各司其职,各尽所能。

 

  1. 日志管理列表页面呈现

  1. 业务时序分析

当点击首页左侧的"日志管理"菜单时,其总体时序分析如图-3所示:

日志管理设计说明_第3张图片

  1. 服务端实现

  1. 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";

}

  1. 客户端实现

  1. 日志菜单事件处理

  • 业务描述与设计

首先准备日志列表页面(/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(){

                    $("#mainContentId").load(url);

    });

}

其中,load函数为jquery中的ajax异步请求函数。

  1. 日志列表页面事件处理

  • 业务描述与设计实现

当日志列表页面加载完成以后异步加载分页页面(page.html)。

  • 关键代码设计与实现:

在log_list.html页面中异步加载page页面,这样可以实现分页页面重用,哪里需要分页页面,哪里就进行页面加载即可。关键代码如下:

$(function(){

        $("#pageId").load("doPageUI");

});

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

 

  1. 日志管理列表数据呈现

  1. 数据架构分析

日志查询服务端数据基本架构,如图-4所示。

日志管理设计说明_第4张图片

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

服务端日志分页查询代码基本架构,如图-5所示:

日志管理设计说明_第5张图片

服务端日志列表数据查询时序图,如图-6所示:

日志管理设计说明_第6张图片

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

  1. Entity类实现

  • 业务描述及设计实现

构建实体对象(POJO)封装从数据库查询到的记录,一行记录映射为内存中一个的这样的对象。对象属性定义时尽量与表中字段有一定的映射关系,并添加对应的set/get/toString等方法,便于对数据进行更好的操作。

 

基于Dao接口创建映射文件,在此文件中通过相关元素(例如select)描述要执行的数据操作。

 

第一步:在映射文件的设计目录(mapper/sys)中添加SysLogMapper.xml映射文件,代码如下:

 

  • 关键代码分析及实现
  • package com.cy.pj.sys.entity;

    import java.io.Serializable;

    import java.util.Date;

     

    public class SysLog implements Serializable {

            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;

     

            /**设置:*/

            public void setId(Integer id) {

                    this.id = id;

            }

            /**获取:*/

            public Integer getId() {

                    return id;

            }

            /**设置:用户名*/

            public void setUsername(String username) {

                    this.username = username;

            }

            /** 获取:用户名*/

            public String getUsername() {

                    return username;

            }

            /**设置:用户操作*/

            public void setOperation(String operation) {

                    this.operation = operation;

            }

            /**获取:用户操作*/

            public String getOperation() {

                    return operation;

            }

            /**设置:请求方法*/

            public void setMethod(String method) {

                    this.method = method;

            }

            /**获取:请求方法*/

            public String getMethod() {

                    return method;

            }

            /** 设置:请求参数*/

            public void setParams(String params) {

                    this.params = params;

            }

            /** 获取:请求参数 */

            public String getParams() {

                    return params;

            }

            /**设置:IP地址 */

            public void setIp(String ip) {

                    this.ip = ip;

            }

            /** 获取:IP地址*/

            public String getIp() {

                    return ip;

            }

            /** 设置:创建时间*/

            public void setCreateDate(Date createdTime) {

                    this.createdTime = createdTime;

            }

            /** 获取:创建时间*/

            public Date getCreatedTime() {

                    return createdTime;

            }

     

            public Long getTime() {

                    return time;

            }

     

            public void setTime(Long time) {

                    this.time = time;

            }

    }

  • 说明:通过此对象除了可以封装从数据库查询的数据,还可以封装客户端请求数据,实现层与层之间数据的传递。

    思考:这个对象的set方法,get方法可能会在什么场景用到?

  • Dao接口实现

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

  • 关键代码分析及实现:
  • 第一步:定义数据层接口对象,通过将此对象保证给业务层以提供日志数据访问。代码如下:

  • @Mapper

    public interface SysLogDao {

    }

  • 第二步:在SysLogDao接口中添加getRowCount方法用于按条件统计记录总数。代码如下:

  •    /**

             * @param username 查询条件(例如查询哪个用户的日志信息)

             * @return 总记录数(基于这个结果可以计算总页数)

             */

            int getRowCount(@Param("username") String username);

            

    }

  • 第三步:在SysLogDao接口中添加findPageObjects方法,基于此方法实现当前页记录的数据查询操作。代码如下:

  •   * @param username  查询条件(例如查询哪个用户的日志信息)

             * @param startIndex 当前页的起始位置

             * @param pageSize 当前页的页面大小

             * @return 当前页的日志记录信息

             * 数据库中每条日志信息封装到一个SysLog对象中

             */

            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文件实现

  • 业务描述及设计实现
  • 关键代码设计及实现

  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

 

第二步:在映射文件中添加sql元素实现,SQL中的共性操作,代码如下:

 

         from sys_Logs

          

            

               username like concat("%",#{username},"%")

            

          

    

第三步:在映射文件中添加id为getRowCount元素,按条件统计记录总数,

代码如下:

            resultType="int">

          select count(*)

          

    

第四步:在映射文件中添加id为findPageObjects元素,实现分页查询。代码如下:

            resultType="com.cy.pj.sys.entity.SysLog">

         select *

         

order by createdTime desc

         limit #{startIndex},#{pageSize}    

思考:

  1. 动态sql:基于用户需求动态拼接SQL
  2. Sql标签元素的作用是什么?对sql语句中的共性进行提取,以遍实现更好的复用.
  3. Include标签的作用是什么?引入使用sql标签定义的元素

 

    第五步:单元测试类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 list=

                   sysLogDao.findPageObjects("admin", 0, 3);

                   for(SysLog log:list) {

                           System.out.println(log);

                   }

           }

}

  1. Service接口及实现类

  • 业务描述与设计实现

业务层主要是实现模块中业务逻辑的处理。在日志分页查询中,业务层对象首先要通过业务方法中的参数接收控制层数据(例如username,pageCurrent)并校验。然后基于用户名进行总记录数的查询并校验,再基于起始位置及页面大小进行当前页记录的查询,最后对查询结果进行封装并返回。

  • 关键代码设计及实现

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

package com.cy.pj.common.vo;

public class PageObject implements Serializable {

        private static final long serialVersionUID = 6780580291247550747L;//类泛型

    /**当前页的页码值*/

         private Integer pageCurrent=1;

    /**页面大小*/

    private Integer pageSize=3;

    /**总行数(通过查询获得)*/

    private Integer rowCount=0;

    /**总页数(通过计算获得)*/

    private Integer pageCount=0;

    /**当前页记录*/

    private List records;

public PageObject(){}

        public PageObject(Integer pageCurrent, Integer pageSize, Integer rowCount, List records) {

                super();

                this.pageCurrent = pageCurrent;

                this.pageSize = pageSize;

                this.rowCount = rowCount;

                this.records = records;

//                this.pageCount=rowCount/pageSize;

//                if(rowCount%pageSize!=0) {

//                        pageCount++;

//                }

                this.pageCount=(rowCount-1)/pageSize+1;

        }

        public Integer getPageCurrent() {

                return pageCurrent;

        }

        public void setPageCurrent(Integer pageCurrent) {

                this.pageCurrent = pageCurrent;

        }

        public Integer getPageSize() {

                return pageSize;

        }

        public void setPageSize(Integer pageSize) {

                this.pageSize = pageSize;

        }

        public Integer getRowCount() {

                return rowCount;

        }

        public void setRowCount(Integer rowCount) {

                this.rowCount = rowCount;

        }

        

        public Integer getPageCount() {

                return pageCount;

        }

        public void setPageCount(Integer pageCount) {

           this.pageCount = pageCount;

        }

        public List getRecords() {

                return records;

        }

        public void setRecords(List records) {

                this.records = records;

        }

}

定义日志业务接口及方法,暴露外界对日志业务数据的访问,其代码参考如下:

package com.cy.pj.sys.service;

public interface SysLogService {

             /**

      * @param name 基于条件查询时的参数名

      * @param pageCurrent 当前的页码值

      * @return 当前页记录+分页信息

      */

         PageObject findPageObjects(

                         String username,

                         Integer pageCurrent);

}

 日志业务接口及实现类,用于具体执行日志业务数据的分页查询操作,其代码如下:

package com.cy.pj.sys.service.impl;

@Service

public class SysLogServiceImpl implements SysLogService{

          @Autowired

     private SysLogDao sysLogDao;

          @Override

          public PageObject findPageObjects(

                          String name, Integer pageCurrent) {

                  //1.验证参数合法性

                  //1.1验证pageCurrent的合法性,

                  //不合法抛出IllegalArgumentException异常

                  if(pageCurrent==null||pageCurrent<1)

                  throw new IllegalArgumentException("当前页码不正确");

                  //2.基于条件查询总记录数

                  //2.1) 执行查询

                  int rowCount=sysLogDao.getRowCount(name);

                  //2.2) 验证查询结果,假如结果为0不再执行如下操作

                  if(rowCount==0)

          throw new ServiceException("系统没有查到对应记录");

                  //3.基于条件查询当前页记录(pageSize定义为2)

                  //3.1)定义pageSize

                  int pageSize=2;

                  //3.2)计算startIndex

                  int startIndex=(pageCurrent-1)*pageSize;

                  //3.3)执行当前数据的查询操作

                  List records=

                  sysLogDao.findPageObjects(name, startIndex, pageSize);

                  //4.对分页信息以及当前页记录进行封装

                  //4.1)构建PageObject对象

                  PageObject pageObject=new PageObject<>();

                  //4.2)封装数据

                  pageObject.setPageCurrent(pageCurrent);

                  pageObject.setPageSize(pageSize);

                  pageObject.setRowCount(rowCount);

                  pageObject.setRecords(records);

           pageObject.setPageCount((rowCount-1)/pageSize+1);

                  //5.返回封装结果。

                  return pageObject;

          }

}

在当前方法中需要的ServiceException是一个自己定义的异常, 通过自定义异常可更好的实现对业务问题的描述,同时可以更好的提高用户体验。参考代码如下:

package com.cy.pj.common.exception;

public class ServiceException extends RuntimeException {

        private static final long serialVersionUID = 7793296502722655579L;

        public ServiceException() {

                super();

        }

        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.vo.PageObject;

import com.cy.pj.sys.entity.SysLog;

 

@SpringBootTest

public class SysLogServiceTests {

 

        @Autowired

        private SysLogService sysLogService;

        @Test

        public void testFindPageObjects() {

           PageObject pageObject=

           sysLogService.findPageObjects("admin", 1);

           System.out.println(pageObject);

           

        }

}

  1. Controller类实现

  • 业务描述与设计实现

控制层对象主要负责请求和响应数据的处理,例如,本模块首先要通过控制层对象处理请求参数,然后通过业务层对象执行业务逻辑,再通过VO对象封装响应结果(主要对业务层数据添加状态信息),最后将响应结果转换为JSON格式的字符串响应到客户端。

  • 关键代码设计与实现

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

package com.cy.pj.common.vo;

public class JsonResult implements Serializable {

        private static final long serialVersionUID = -856924038217431339L;//SysResult/Result/R

        /**状态码*/

        private int state=1;//1表示SUCCESS,0表示ERROR

        /**状态信息*/

        private String message="ok";

        /**正确数据*/

        private Object data;

        public JsonResult() {}

        public JsonResult(String message){

                this.message=message;

        }

        /**一般查询时调用,封装查询结果*/

        public JsonResult(Object data) {

                this.data=data;

        }

        /**出现异常时时调用*/

        public JsonResult(Throwable t){

                this.state=0;

                this.message=t.getMessage();

        }

        public int getState() {

                return state;

        }

        public void setState(int state) {

                this.state = state;

        }

        public String getMessage() {

                return message;

        }

        public void setMessage(String message) {

                this.message = message;

        }

        public Object getData() {

                return data;

        }

        public void setData(Object data) {

                this.data = data;

        }

}

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

package com.cy.pj.sys.controller;

@Controller

@RequestMapping("/log/")

public class SysLogController {

        @Autowired

        private SysLogService sysLogService;

}

@RequestMapping("doFindPageObjects")

@ResponseBody

public JsonResult doFindPageObjects(String username,Integer pageCurrent){

 PageObject pageObject=

        sysLogService.findPageObjects(username,pageCurrent);

return new JsonResult(pageObject);

}

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

package com.cy.pj.common.web;

import java.util.logging.Logger;

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.vo.JsonResult;

@ControllerAdvice

public class GlobalExceptionHandler {

        //JDK中的自带的日志API

        @ExceptionHandler(RuntimeException.class)

   @ResponseBody

        public JsonResult doHandleRuntimeException(

                        RuntimeException e){

            e.printStackTrace();//也可以写日志

                异常信息

        }return new JsonResult(e);//封装

}

控制层响应数据处理分析,如图-7所示:

日志管理设计说明_第7张图片

  1. 客户端关键业务及代码实现

  1. 客户端页面事件分析

当用户点击首页日志管理时,其页面流转分析如图-8所示:

日志管理设计说明_第8张图片

  1. 日志列表信息呈现

  • 业务描述与设计实现

日志分页页面加载完成以后,向服务端发起异步请求加载日志信息,当日志信息加载完成需要将日志信息、分页信息呈现到列表页面上。

  • 关键代码设计与实现

第一步:分页页面加载完成,向服务端发起异步请求,代码参考如下:

 $(function(){

           //为什么要将doGetObjects函数写到load函数对应的回调内部。

           $("#pageId").load("doPageUI",function(){

                   doGetObjects();

           });

}

第二步:定义异步请求处理函数,代码参考如下:

 function doGetObjects(){

           //debugger;//断点调试

           //1.定义url和参数

           var url="log/doFindPageObjects"

           var params={"pageCurrent":1};//pageCurrent=2

           //2.发起异步请求

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

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

                   //请问result是一个字符串还是json格式的js对象?对象

                 doHandleQueryResponseResult(result);

                 }

           );//特殊的ajax函数

   }

result 结果对象分析,如图-9所示:

日志管理设计说明_第9张图片

第三步:定义回调函数,处理服务端的响应结果。代码如下:

function doHandleQueryResponseResult (result){ //JsonResult

           if(result.state==1){//ok

                //更新table中tbody内部的数据

                doSetTableBodyRows(result.data.records);//将数据呈现在页面上

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

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

            }else{

                alert(result.message);

            }  

 }

第四步:将异步响应结果呈现在table的tbody位置。代码参考如下:

function doSetTableBodyRows(records){

           //1.获取tbody对象,并清空对象

           var tBody=$("#tbodyId");

           tBody.empty();

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

           for(var i in records){

                   //2.1 构建tr对象

                   var tr=$("");

                   //2.2 构建tds对象

                   var tds=doCreateTds(records[i]);

                   //2.3 将tds追加到tr中

                   tr.append(tds);

                   //2.4 将tr追加到tbody中

                   tBody.append(tr);

           }

   }

第五步:创建每行中的td元素,并填充具体业务数据。代码参考如下:

          var tds=""+

                     ""+data.username+""+

                     ""+data.operation+""+

                     ""+data.method+""+

                     ""+data.params+""+

                     ""+data.ip+""+

                     ""+data.time+"";          

return tds;

   }

  1. 分页数据信息呈现

  • 业务描述与设计实现

日志信息列表初始化完成以后初始化分页数据(调用setPagination函数),然后再点击上一页,下一页等操作时,更新页码值,执行基于当前页码值的查询。

  • 关键代码设计与实现:

第一步:在page.html页面中定义doSetPagination方法(实现分页数据初始化),代码如下:

function doSetPagination(page){

            //1.始化数据

            $(".rowCount").html("总记录数("+page.rowCount+")");

            $(".pageCount").html("总页数("+page.pageCount+")");

            $(".pageCurrent").html("当前页("+page.pageCurrent+")");

            //2.绑定数据(为后续对此数据的使用提供服务)

            $("#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

                pageCurrent++;

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

                pageCurrent=pageCount;

        }else{

         return;

}

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

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

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

        doGetObjects();

    }

修改分页查询方法:(看黄色底色部分)

function doGetObjects(){

           //debugger;//断点调试

           //1.定义url和参数

           var url="log/doFindPageObjects"

           //? 请问data函数的含义是什么?(从指定元素上获取绑定的数据)

           //此数据会在何时进行绑定?(setPagination,doQueryObjects)

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

           //为什么要执行如下语句的判定,然后初始化pageCurrent的值为1

           //pageCurrent参数在没有赋值的情况下,默认初始值应该为1.

           if(!pageCurrent) pageCurrent=1;

           var params={"pageCurrent":pageCurrent};//pageCurrent=2

           //2.发起异步请求

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

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

                   //请问result是一个字符串还是json格式的js对象?对象

                    doH

andleResponseResult(result);

                 }

           );//特殊的ajax函数 }

  1. 列表页面信息查询实现

  • 业务描述及设计

当用户点击日志列表的查询按钮时,基于用户输入的用户名进行有条件的分页查询,并将查询结果呈现在页面。

  • 关键代码设计与实现:

第一步:日志列表页面加载完成,在查询按钮上进行事件注册。代码如下:

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

function doQueryObjects(){

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

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

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

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

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

           doGetObjects();

   }

第三步:在分页查询函数中追加name参数定义(看黄色底色部分),代码如下:

function doGetObjects(){

           //debugger;//断点调试

           //1.定义url和参数

           var url="log/doFindPageObjects"

           //? 请问data函数的含义是什么?(从指定元素上获取绑定的数据)

           //此数据会在何时进行绑定?(setPagination,doQueryObjects)

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

           //为什么要执行如下语句的判定,然后初始化pageCurrent的值为1

           //pageCurrent参数在没有赋值的情况下,默认初始值应该为1.

           if(!pageCurrent) pageCurrent=1;

           var params={"pageCurrent":pageCurrent};

           //为什么此位置要获取查询参数的值?

           //一种冗余的应用方法,目的时让此函数在查询时可以重用。

           var username=$("#searchNameId").val();

           //如下语句的含义是什么?动态在json格式的js对象中添加key/value,

           if(username) params.username=username;//查询时需要

           //2.发起异步请求

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

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

                   //请问result是一个字符串还是json格式的js对象?对象

                    doHandleResponseResult(result);

                 }

           );

   }

  1. 日志管理删除操作实现

  1. 数据架构分析

当用户执行日志删除操作时,客户端与务端交互时的基本数据架构,如图-10所示。

日志管理设计说明_第10张图片

  1. 删除业务时序分析

客户端提交删除请求,服务端对象的工作时序分析,如图-11所示。

日志管理设计说明_第11张图片

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

  1. Dao接口实现

  • 业务描述及设计实现

数据层基于业务层提交的日志记录id,进行日志删除操作。

  • 关键代码设计及实现:

在SysLogDao中添加基于id执行日志删除的方法。代码参考如下:

int deleteObjects(@Param("ids")Integer… ids);

  1. Mapper文件实现

  • 业务描述及设计实现

在SysLogDao接口对应的映射文件中添加用于执行删除业务的delete元素,此元素内部定义具体的SQL实现。

  • 关键代码设计与实现

在SysLogMapper.xml文件添加delete元素,关键代码如下:

       delete from sys_Logs

       where id in

       

                open="("

                close=")"

                separator=","

                item="id">

               #{id}

        

    

FAQ分析:如上SQL实现可能会存在什么问题?(可靠性问题,性能问题)

从可靠性的角度分析,假如ids的值为null或长度为0时,SQL构建可能会出现语法问题,可参考如下代码进行改进(先对ids的值进行判定):

        delete from sys_logs

        0">

          where id in  

            

                 open="("

                 close=")"

                 separator=","

                 item="id">

                 #{id}

            

        

        

           where 1==2

        

从SQL执行性能角度分析,一般在SQL语句中不建议使用in表达式,可以参考如下代码进行实现(重点是forearch中or运算符的应用):

       delete from sys_logs

       

         0">

           

              

                       item="id"

                       separator="or">

                  id=#{id}

              

           

         

         

          where 1==2

         

       

    

说明:这里的choose元素也为一种选择结构,when元素相当于if,otherwise相当于else的语法。

  1. Service接口及实现类

  • 业务描述与设计实现

在日志业务层定义用于执行删除业务的方法,首先通过方法参数接收控制层传递的多个记录的id,并对参数id进行校验。然后基于日志记录id执行删除业务实现。最后返回业务执行结果。

  • 关键代码设计与实现

第一步:在SysLogService接口中,添加基于多个id进行日志删除的方法。关键代码如下:

int deleteObjects(Integer… ids) {}

第二步:在SysLogServiceImpl实现类中添加删除业务的具体实现。关键代码如下:

@Override

public int deleteObjects(Integer… ids) {

                //1.判定参数合法性

                if(ids==null||ids.length==0)

            throw new IllegalArgumentException("请选择一个");

                //2.执行删除操作

                int rows;

                try{

                rows=sysLogDao.deleteObjects(ids);

                }catch(Throwable e){

                e.printStackTrace();

                //发出报警信息(例如给运维人员发短信)

                throw new ServiceException("系统故障,正在恢复中...");

                }

                //4.对结果进行验证

                if(rows==0)

                throw new ServiceException("记录可能已经不存在");

                //5.返回结果

                return rows;

}

  1. Controller类实现

  • 业务描述与设计实现

在日志控制层对象中,添加用于处理日志删除请求的方法。首先在此方法中通过形参接收客户端提交的数据,然后调用业务层对象执行删除操作,最后封装执行结果,并在运行时将响应对象转换为JSON格式的字符串,响应到客户端。

  • 关键代码设计与实现

第一步:在SysLogController中添加用于执行删除业务的方法。代码如下:

   @RequestMapping("doDeleteObjects")

          @ResponseBody

          public JsonResult doDeleteObjects(Integer… ids){

                  sysLogService.deleteObjects(ids);

                  return new JsonResult("delete ok");

          }

第二步:启动tomcat进行访问测试,打开浏览器输入如下网址:

http://localhost/log/doDeleteObjects?ids=1,2,3

  1. 客户端关键业务及代码实现

  1. 日志列表页面事件处理

  • 业务描述及设计实现

用户在页面上首先选择要删除的元素,然后点击删除按钮,将用户选择的记录id异步提交到服务端,最后在服务端执行日志的删除动作。

  • 关键代码设计与实现

第一步:页面加载完成以后,在删除按钮上进行点击事件注册。关键代码如下:

...

$(".input-group-btn")

           .on("click",".btn-delete",doDeleteObjects)

...

第二步:定义删除操作对应的事件处理函数。关键代码如下:

  function doDeleteObjects(){

           //1.获取选中的id值

           var ids=doGetCheckedIds();

           if(ids.length==0){

                  alert("至少选择一个");

                  return;

           }

           //2.发异步请求执行删除操作

           var url="log/doDeleteObjects";

           var params={"ids":ids.toString()};

           console.log(params);

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

                   if(result.state==1){

                         alert(result.message);

                         doGetObjects();

                   }else{

                         alert(result.message);

                   }

           });

   }

第三步:定义获取用户选中的记录id的函数。关键代码如下:

 function doGetCheckedIds(){

           //定义一个数组,用于存储选中的checkbox的id值

           var array=[];//new Array();

           //获取tbody中所有类型为checkbox的input元素

           $("#tbodyId input[type=checkbox]").

           //迭代这些元素,每发现一个元素都会执行如下回调函数

           each(function(){

                   //假如此元素的checked属性的值为true

                   if($(this).prop("checked")){

                           //调用数组对象的push方法将选中对象的值存储到数组

                           array.push($(this).val());

                   }

           });

           return array;

 }

第四步:Thead中全选元素的状态影响tbody中checkbox对象状态。代码如下:

 function doChangeTBodyCheckBoxState(){

           //1.获取当前点击对象的checked属性的值

           var flag=$(this).prop("checked");//true or false

           //2.将tbody中所有checkbox元素的值都修改为flag对应的值。

           //第一种方案

           /* $("#tbodyId input[name='cItem']")

           .each(function(){

                   $(this).prop("checked",flag);

           }); */

           //第二种方案

           $("#tbodyId input[type='checkbox']")

           .prop("checked",flag);

   }

第五步:Tbody中checkbox的状态影响thead中全选元素的状态。代码如下:

function doChangeTHeadCheckBoxState(){

          //1.设定默认状态值

          var flag=true;

          //2.迭代所有tbody中的checkbox值并进行与操作

          $("#tbodyId input[type='checkbox']")

          .each(function(){

                  flag=flag&$(this).prop("checked")

          });

          //3.修改全选元素checkbox的值为flag

          $("#checkAll").prop("checked",flag);

   }

第六步:完善业务刷新方法,当在最后一页执行删除操作时,基于全选按钮状态及当前页码值,刷新页面。关键代码如下:

function doRefreshAfterDeleteOK(){

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

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

             var checked=$("#checkAll").prop("checked");

             if(pageCurrent==pageCount&&checked&&pageCurrent>1){

                     pageCurrent--;

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

             }

         doGetObjects();

   }

说明:最后将如上方法添加在删除操作成功以后的代码块中。

  1. 日志管理数据添加实现

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

  1. Dao接口实现

  • 业务描述与设计实现

数据层基于业务层的持久化请求,将业务层提交的用户行为日志信息写入到数据库。

  • 关键代码设计与实现

在SysLogDao接口中添加,用于实现日志信息持久化的方法。关键代码如下:

int insertObject(SysLog entity);

  1. Mapper映射文件

  • 业务描述与设计实现

基于SysLogDao中方法的定义,编写用于数据持久化的SQL元素。

  • 关键代码设计与实现

在SysLogMapper.xml中添加insertObject元素,用于向日志表写入用户行为日志。关键代码如下:

       insert into sys_logs

       (username,operation,method,params,time,ip,createdTime)

       values

(#{username},#{operation},#{method},#{params},#{time},#{ip},#{createdTime})

  1. Service接口及实现类

  • 业务描述与设计实现

将日志切面中抓取到的用户行为日志信息,通过业务层对象方法持久化到数据库。

  • 关键代码实现

第一步:在SysLogService接口中,添加保存日志信息的方法。关键代码如下:

void saveObject(SysLog entity)

第二步:在SysLogServiceImpl类中添加,保存日志的方法实现。关键代码如下:

 @Override

        public void saveObject(SysLog entity) {

          sysLogDao.insertObject(entity);

}

  1. 日志切面Aspect实现

 

(MappedStatement)。

 

 

 

说明:当动态sql的参数在接口中没有使用@Param注解修饰,还可以借助_parameter这个变量获取参数的值(mybatis中的一种规范)。

 

  • 业务描述与设计实现
  • 在日志切面中,抓取用户行为信息,并将其封装到日志对象然后传递到业务,通过业务层对象对日志日志信息做进一步处理。此部分内容后续结合AOP进行实现(暂时先了解,不做具体实现)。
  • 关键代码设计与实现
  • 定义日志切面类对象,通过环绕通知处理日志记录操作。关键代码如下:

  • @Aspect

    @Component

    public class SysLogAspect {

            private Logger log=Logger.getLogger(SysLogAspect.class);

        @Autowired

            private SysLogService sysLogService;

            @Pointcut("@annotation(com.cy.pj.common.annotation.RequiredLog)")

            public void logPointCut(){}

        @Around("logPointCut()")

        public Object around(ProceedingJoinPoint //连接点

                        jointPoint) throws Throwable{

                long startTime=System.currentTimeMillis();

                //执行目标方法(result为目标方法的执行结果)

                Object result=jointPoint.proceed();

                long endTime=System.currentTimeMillis();

                long totalTime=endTime-startTime;

                log.info("方法执行的总时长为:"+totalTime);

                saveSysLog(jointPoint,totalTime);

                return result;

        }

        private void saveSysLog(ProceedingJoinPoint point,

                          long totleTime) throws NoSuchMethodException, SecurityException, JsonProcessingException{

                //1.获取日志信息

                MethodSignature ms= (MethodSignature)point.getSignature();

                Class targetClass=point.getTarget().getClass();

                String className=targetClass.getName();

                //获取接口声明的方法

                String methodName=ms.getMethod().getName();

                Class[] parameterTypes=ms.getMethod().getParameterTypes();

                //获取目标对象方法(AOP版本不同,可能获取方法对象方式也不同)

                Method targetMethod=targetClass.getDeclaredMethod(

                                    methodName,parameterTypes);

           //获取用户名,学完shiro再进行自定义实现,没有就先给固定值

                String username=ShiroUtils.getPrincipal().getUsername();

                //获取方法参数

                Object[] paramsObj=point.getArgs();

                System.out.println("paramsObj="+paramsObj);

                //将参数转换为字符串

                String params=new ObjectMapper()

                .writeValueAsString(paramsObj);

                //2.封装日志信息

                SysLog log=new SysLog();

                log.setUsername(username);//登陆的用户

                //假如目标方法对象上有注解,我们获取注解定义的操作值

                RequiredLog requestLog=

                targetMethod.getDeclaredAnnotation(RequiredLog.class);

                if(requestLog!=null){

                log.setOperation(requestLog.value());

                }

    log.setMethod(className+"."+methodName);//className.methodName()

                log.setParams(params);//method params

                log.setIp(IPUtils.getIpAddr());//ip 地址

                log.setTime(totleTime);//

                log.setCreateDate(new Date());

                //3.保存日志信息

                sysLogService.insertObject(log);

        }

    }

  • 方法中用到的ip地址获取需要提供一个如下的工具类:(不用自己实现,直接用)

  • public class IPUtils {

            private static Logger logger = LoggerFactory.getLogger(IPUtils.class);

    public static String getIpAddr() {

            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();

                    String ip = null;

                    try {

                            ip = request.getHeader("x-forwarded-for");

                            if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {

                                    ip = request.getHeader("Proxy-Client-IP");

                            }

                    if (StringUtils.isEmpty(ip) || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {

                                    ip = request.getHeader("WL-Proxy-Client-IP");

                            }

                            if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {

                                    ip = request.getHeader("HTTP_CLIENT_IP");

                            }

                            if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {

                                    ip = request.getHeader("HTTP_X_FORWARDED_FOR");

                            }

                            if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {

                                    ip = request.getRemoteAddr();

                            }

                    } catch (Exception e) {

                            logger.error("IPUtils ERROR ", e);

                    }

                    return ip;

            }

    }

  • 原理分析,如图-12所示:

  • 日志管理设计说明_第12张图片

  • 总结

  • 重难点分析

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

  • 用户行为日志表中都有哪些字段?
  • 用户行为日志是如何实现分页查询的?
  • 用户行为数据的封装过程?
  • 项目中的异常是如何处理的?
  • 页面中数据乱码,如何解决?(数据来源,请求数据,响应数据)
  • 说说的日志删除业务是如何实现?
  • 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分析及解决套路?
  • BUG分析

  • 无法找到对应的Bean对象(NoSuchBeanDefinitionException),如图-13所示:
  • 日志管理设计说明_第13张图片
  • 问题分析:

  • 检测key的名字写的是否正确。
  • 检测spring对此Bean对象的扫描,对于dao而言。
  • 使用有@Mapper注解描述或者在@MapperScan扫描范围之内。
  • 以上都正确,要检测是否编译了。
  • 绑定异常(BindingException),如图-14所示。
  • 日志管理设计说明_第14张图片
  • 图-14

    问题分析:

  • 接口的类全名与对应的映射文件命名空间不同。
  • 接口的方法名与对应的映射文件元素名不存在。
  • 检测映射文件的路径与application.properties或者application.yml中的配置是否一致。
  • 以上都没有问题时,检测你的类和映射文件是否正常编译。
  • 反射异常(ReflectionException),如图-15所示
  • 日志管理设计说明_第15张图片
  • 图-15

    问题分析:

  • 映射文件中动态sql中使用的参数在接口方法中没有使用@Param注解修饰
  • 假如使用了注解修饰还要检测名字是否一致。
  • 结果映射异常,如图-16所示:
  • 日志管理设计说明_第16张图片
  • 问题分析:getRowCount元素可能没有写resultType或resultMap。

  • 绑定异常,如图-17所示:
  • 日志管理设计说明_第17张图片
  • 问题分析:绑定异常,检测findPageObjects方法参数与映射文件参数名字是否匹配。

  • Bean创建异常,如图-18所示
  • 日志管理设计说明_第18张图片
  • 问题分析:应该是查询时的结果映射对的类全名写错了。

  • 请求方式不匹配,如图-19示
  • 日志管理设计说明_第19张图片
  • 问题分析:请求方式与控制层处理方式不匹配。

  • 响应结果异常,如图-20所示:
  • 日志管理设计说明_第20张图片
  • 问题分析:服务端响应数据不正确,例如服务端没有注册将对象转换为JSON串的Bean

  • 请求参数异常,如图-21示:
  • 日志管理设计说明_第21张图片
  • 图-21

    问题分析:客户端请求参数中不包含服务端控制层方法参数或格式不匹配。

  • JS编写错误,如图-22所示:
  • 日志管理设计说明_第22张图片
  • 图-22

    问题分析:点击右侧VM176:64位置进行查看。

  • JS编写错误,如图-23所示:
  • 日志管理设计说明_第23张图片
  • 图-23

    问题分析:找到对应位置,检测data的值以及数据来源。

  • JS编写错误,如图-24所示:
  • 日志管理设计说明_第24张图片
  • 图-24

    问题分析:找到对应位置,假如无法确定位置,可排除法或打桩,debug分析。

  • JS写错误,如图-25所示:
  • 日志管理设计说明_第25张图片
  • 问题分析:调用length方法的对象有问题,可先检测下对象的值。

  • JS编写错误,如图-26所示:
  • 日志管理设计说明_第26张图片
  • 问题分析:检测record定义或赋值的地方。

  • 资源没找到,如图-27所示:
  • 日志管理设计说明_第27张图片
  • 问题分析:服务端资源没找到,检查url和controller映射,不要点击图中的jquery。

  • 视图解析异常,如图-28所示:
  • 日志管理设计说明_第28张图片
  • 问题分析:检查服务端要访问的方法上是否有@ResponseBody注解.

 

package com.cy.pj.common.exception;

public class ServiceException extends RuntimeException {

    /**
     * 
     */
    private static final long serialVersionUID = -5598865415547474216L;

    public ServiceException() {
        super();
        // 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
    }

}
 

package com.cy.pj.common.utils;

import com.cy.pj.common.exception.ServiceException;

public class AssertUtils {

    public static void isArgValid(boolean statement,String msg) {
        if(statement)
            throw new IllegalArgumentException(msg);
    }
    
    public static void isServiceValid(boolean statement,String msg) {
        if(statement)
            throw new ServiceException(msg);
    }
    
}
 

 

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;

/**
 * 此注解描述的类为一个控制层全局异常处理类,在这个类可以定义异常处理方法,
 * 基于这些异常处理方法对异常进行处理
 * @author huawei
 *
 */
@ControllerAdvice
public class GlobalExceptionHandler {

    /**
     * @ExceptionHandler 此注解描述的方法为一个异常处理方法,在注解内部定义的异常类型
     * 为此方法可以处理的异常类型(包括异常的子类类型)
     * @param e 用于接收出现的异常
     * @return
     */
    @ExceptionHandler(RuntimeException.class)
    @ResponseBody
    public JsonResult doHandleRuntimeException(RuntimeException e) {
        e.printStackTrace();//在后台控制台打印异常
        return new JsonResult(e);//封装异常信息
    }
}
 

 

package com.cy.pj.common.pojo;

import java.io.Serializable;

import lombok.Data;

@Data
public class CheckBox implements Serializable{

    /**
     * 
     */
    private static final long serialVersionUID = -3930756932197466333L;
    private Integer id;
    private String name;
}
 

package com.cy.pj.common.pojo;

import java.io.Serializable;

import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * 借助此对象封装控制层响应到客户端的数据,在这个对象中会为数据添加一个状态
 * @author huawei
 *
 */
@Data
@NoArgsConstructor
public class JsonResult implements Serializable{
    /**
     * 
     */
    private static final long serialVersionUID = 5110901796917551720L;
    /**
     * 状态码:信息标识
     */
    private Integer state=1;//1:success,0:error
    /*状态码对应的信息*/
    private String message="success";
    /*业务层响应给控制层的数据*/
    private Object data;
    
    public JsonResult(String message) {
        this.message=message;
    }
    
    public JsonResult(Object data) {
        this.data=data;
    }
    
    public JsonResult(Throwable e) {
        this.state=0;
        this.message=e.getMessage();//获取异常对象中的异常信息
    }
    
}
 

 

package com.cy.pj.common.pojo;

import java.io.Serializable;

import lombok.Data;

@Data
public class Node implements Serializable{

    /**
     * 基于此对象存储树节点信息
     */
    private static final long serialVersionUID = -7022202313802285223L;
    private Integer id;
    private String name;
    private Integer parentId;
}
 

 

package com.cy.pj.common.pojo;

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

import lombok.Data;
import lombok.NoArgsConstructor;
/**
 * 基于此对象封装分页信息
 * @author huawei
 *
 */
@Data
@NoArgsConstructor
public class PageObject implements Serializable{
    
    /**
     * 
     */
    private static final long serialVersionUID = -2123021917437043740L;
    /*总记录数*/
    private Long rowCount;//池化思想
    /*当前页记录*/
    private List records;
    /*总页数*/
    private Long pageCount;
    /*页面大小(每页最多显示多少条记录)*/
    private Integer pageSize;
    /*当前页码值*/
    private Long pageCurrent;

    public PageObject(Long rowCount, List records, Long pageCount, Integer pageSize, Long pageCurrent) {
        super();
        this.rowCount = rowCount;
        this.records = records;
        this.pageCount = pageCount;
        this.pageSize = pageSize;
        this.pageCurrent = pageCurrent;
    }

    public PageObject(Long rowCount, List records,Integer pageSize, Long pageCurrent) {
        super();
        this.rowCount = rowCount;
        this.records = records;
        this.pageSize = pageSize;
        this.pageCurrent = pageCurrent;
        //计算pageCount的值,方法1
//        this.pageCount=rowCount/pageSize;
//        if(rowCount%pageSize!=0)this.pageCount++;
        //计算pageCount的值,方法1
        this.pageCount=(this.rowCount-1)/pageSize+1;
    }
    
}
 

 

package com.cy.pj.sys.pojo;

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

import lombok.Data;
/**
 * 部门PO对象
 */
@Data
public class SysDept implements Serializable{
    private static final long serialVersionUID = 8876920804134951849L;
    private Integer id;
    private String name;
    private Integer parentId;
    private Integer sort;
    private String note;
    private Date createdTime;
    private Date modifiedTime;
    private String createdUser;
    private String modifiedUser;
}
 

 

package com.cy.pj.sys.pojo;

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

import lombok.Data;

@Data
public class SysLog implements Serializable{//ObjectOutputStream/ObjectInputStream

    /**
     * 基于此对象封装从数据库查询到的日志信息
     * 类似对象的特征:
     * 1.添加set/get/toString/constructor
     * 2.实现Serializable接口(建议所有用于封装数据的对象都实现此接口)
     * 2.1序列化:将对象转换为字节便于传输和存储
     * 2.2反序列化:将网络中的或存储介质中的字节转换为对象
     */
    private static final long serialVersionUID = -8562927111682153360L;
    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.sys.pojo;

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

import lombok.Data;

@Data
public class SysMenu implements Serializable{

    /**
     * 
     */
    private static final long serialVersionUID = -7691821439056974454L;
    private Integer id;
    /**菜单名称*/
    private String name;
    /**菜单url: log/doFindPageObjects*/
    private String url;
    /**菜单类型(两种:按钮,普通菜单)*/
    private Integer type=1;
    /**排序(序号)*/
    private Integer sort;
    /**备注*/
    private String note;
    /**上级菜单id*/
    private Integer parentId;
    /**菜单对应的权限标识(sys:log:delete)*/
    private String permission;
    /**创建用户*/
    private String createdUser;
    /**修改用户*/
    private String modifiedUser;
    private Date createdTime;
    private Date modifiedTime;

}
 

 

package com.cy.pj.sys.pojo;

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

import lombok.Data;

/**
 * 基于此对象封装角色信息
 * @author huawei
 *
 */
@Data
public class SysRole implements Serializable{

    /**
     * 
     */
    private static final long serialVersionUID = -8557710039441592130L;
    private Integer id;
    private String name;
    private String note;
    private Date createdTime;
    private Date modifiedTime;
    private String createdUser;
    private String modifiedUser;
}
 

 

package com.cy.pj.sys.pojo;

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

import lombok.Data;

/**
 * 当基于角色id执行角色以及对应的菜单信息查询时,将查询到结果存储到此对象,
 * 并将此对象的数据最终更新到编辑页面上。
 * 查询方案:(现在数据来自两张表)
 * 1.业务层进行多次单表查询(先查角色表再查菜单表)--最简单的一种
 * 2.数据层做嵌套查询(数据层多次查询,基于角色表的查询结果再次查询角色菜单关系表)
 * 3.在数据层执行多表关联查询(join)
 *
 */
@Data
public class SysRoleMenu implements Serializable{

    private static final long serialVersionUID = -2671028987524519218L;
    /*角色id*/
    private Integer id;
    /*角色名称*/
    private String name;
    /*角色备注*/
    private String note;
    /*角色对应的菜单id(这个数据存在角色和菜单关系表中)*/
    private List menuIds;
}
 

package com.cy.pj.sys.pojo;

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

import lombok.Data;

@Data
public class SysUser implements Serializable{

    /**
     * 
     */
    private static final long serialVersionUID = -3859097288799746946L;
    private Integer id;
    private String username;
    private String password;
    private String salt;//盐值
    private String email;
    private String mobile;
    private Integer valid=1;
      private Integer deptId;
    private Date createdTime;
    private Date modifiedTime;
    private String createdUser;
    private String modifiedUser;

}
 

 

package com.cy.pj.sys.pojo;

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

import lombok.Data;

@Data
public class SysUserDept implements Serializable{

    /**
     * 
     */
    private static final long serialVersionUID = -6219081603383521954L;
    private Integer id;
    private String username;
    private String password;//md5
    private String salt;
    private String email;
    private String mobile;
    private Integer valid=1;
    /**
     * 基于此对象存储部门信息
     */
    private SysDept sysDept; //private Integer deptId;
    private Date createdTime;
    private Date modifiedTime;
    private String createdUser;
    private String modifiedUser;
}
 

package com.cy.pj.sys.dao;
import java.util.List;
import java.util.Map;

import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

import com.cy.pj.common.pojo.Node;
import com.cy.pj.sys.pojo.SysDept;

@Mapper
public interface SysDeptDao {
      /**
               * 查询所有部门以及部门的上级菜单信息
       * @return
       */
      @Select("select c.*,p.name parentName from sys_depts c left join sys_depts p on c.parentId=p.id")
      List> findObjects();
      
      @Select("select id,name,parentId from sys_depts")
      List findZTreeNodes();
      
      int updateObject(SysDept entity);
      int insertObject(SysDept entity);
      
      @Select("select count(*) from sys_depts where parentId=#{id}")
      int getChildCount(Integer id);
      
      @Delete("delete from sys_depts where id=#{id}")
      int deleteObject(Integer id);
}

 

package com.cy.pj.sys.dao;

import java.util.List;

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

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

@Mapper
public interface SysLogDao {

    /**
     * 基于id删除多条日志信息
     * @param ids
     * @return
     */
    int deleteObjects(@Param("ids") Integer... ids);
    /**
     * 基于条件查询用户行为日志记录总数
     * @param name 查询条件(用户名-基于此用户名进行模糊查询)
     * @return 查询到的记录总数
     */
    long getRowCount(@Param("username")String username);
    
    /**
     * 基于条件查询当前页数据
     * @param name 查询条件
     * @param startIndex 起始位置
     * @param pageSize 页面大小(每页最多显示多少条记录)
     * @return 查询到的记录
     */
    List findPageObjects(@Param("username") 
    String username,@Param("startIndex")Long startIndex,@Param("pageSize")Integer pageSize);
}
 

package com.cy.pj.sys.dao;

import java.util.List;
import java.util.Map;

import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

import com.cy.pj.common.pojo.Node;
import com.cy.pj.sys.pojo.SysMenu;

@Mapper
public interface SysMenuDao {
    
    int updateObject(SysMenu entity);
    
    int insertObject(SysMenu entity);
    
    @Select("select id,name,parentId from sys_menus")
    List findZtreeMenuNodes();
    
    /**
     * 基于id删除当前菜单对象
     * @param id
     * @return
     */
    @Delete("delete from sys_menus where id=#{id}")
    int deleteObject(Integer id);

    /**
     * 基于id获取当前菜单对应的子菜单个数
     * @param id 菜单id
     * @return 子菜单的个数
     */
    @Select("select count(*) from sys_menus where parentId=#{id}")
    int getChildCount(Integer id);
    /**
     * 查询所有菜单信息以及这个菜单对应的上级菜单信息
     * @return
     */
    List> findObjects();
}
 

package com.cy.pj.sys.dao;

import java.util.List;

import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Options;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

import com.cy.pj.common.pojo.CheckBox;
import com.cy.pj.sys.pojo.SysRole;
import com.cy.pj.sys.pojo.SysRoleMenu;

@Mapper
public interface SysRoleDao {//sys_roles
    
    /**
     * 查询所有角色id和name
     * @return
     */
    @Select("select id,name from sys_roles")
    List findRoles();
    
    int updateObject(SysRole entity);

    /**
     * 基于角色id查找自身信息
     * @param id
     * @return
     */
    //@Select("select id,name,note from sys_roles where id=#{id}")
    SysRoleMenu findObjectById(Integer id);
    
    /**
     * 保存角色自身信息
     * @param entity
     * @return
     */
    @Insert("insert into sys_roles (name,note,createdUser,modifiedUser,createdTime,modifiedTime) values (#{name},#{note},#{createdUser},#{modifiedUser},now(),now())")
    @Options(useGeneratedKeys = true,keyProperty = "id")
    int insertObject(SysRole entity);

    /**
     * 基于角色名统计查询角色信息
     * @param name 角色名
     * @return 统计的个数
     */
    long getRowCount(@Param("name")String name);
    
    /**
     * 按条件从指定位置查询当前页角色信息
     * @param name 角色名
     * @param startIndex 起始位置
     * @param pageSize 页面大小
     * @return 当前页的角色信息
     */
    List findPageObjects(String name,Long startIndex,Integer pageSize);
}
 

package com.cy.pj.sys.dao;

import java.util.List;

import org.apache.ibatis.annotations.Delete;

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;


/**
 * 基于此对象操作角色和菜单关系表(sys_role_menus)数据
 * @author huawei
 *
 */
@Mapper
public interface SysRoleMenuDao {
    
    /**
     * 基于角色id删除角色和菜单关系数据
     * @param roleId
     * @return
     */
    @Delete("delete from sys_role_menus where role_id=#{roleId}")
    int deleteObjectsByRoleId(Integer roleId);
    /**
     * 基于角色id获取菜单id
     * @param id
     * @return
     */
    @Select("select menu_id from sys_role_menus where role_id=#{id}")
    List findMenuIdsByRoleId(Integer id);
    
    int insertObjects(Integer roleId,Integer[] menuIds);
    /**
     * 基于菜单id删除角色和菜单关系数据
     * @param menuId
     * @return
     */
    @Delete("delete from sys_role_menus where menu_id=#{menuId}")
    int deleteObjectsByMenuId(Integer menuId);
}
 

package com.cy.pj.sys.dao;

import java.util.List;
import java.util.Map;

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Update;

import com.cy.pj.sys.pojo.SysUser;
import com.cy.pj.sys.pojo.SysUserDept;

@Mapper
public interface SysUserDao {
    
    /**
     * 更新用户自身信息
     * @param entity
     * @return
     */
    int updateObject(SysUser entity);
    
    /**
     * 基于用户id查询用户和部门信息
     * @param id
     * @return
     */
    SysUserDept findObjectById(Integer id);
    /**
     * 将用户自身信息写到数据库
     * @param entity
     * @return
     */
    int insertObject(SysUser entity);
    /**
     * 修改用户状态
     * @param id
     * @param stste
     * @param modifiedUser
     * @return
     */
    @Update("update sys_users set valid=#{valid},modifiedUser=#{modifiedUser},modifiedTime=now() where id=#{id}")
    int validById(Integer id,Integer valid,String modifiedUser);

    /**
     * 基于用户名进行用户信息的模糊查询,获取总记录数
     * @param username
     * @return
     */
    long getRowCount(String username);
    
    /**
     * 基于用户信息的模糊查询,获取当前页记录,并将数据封装到pojo对象
     * SysUserDept
     * @param username 查询条件
     * @param startIndex 起始位置
     * @param pageSize 页面大小
     * @return 当前页查询到的记录
     */
    ListfindPageObjects(String username,Long startIndex,Integer pageSize);
}
 

package com.cy.pj.sys.dao;

import java.util.List;

import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

/**
 * 基于此dao操作用户角色关系表数据
 * @author huawei
 *
 */
@Mapper
public interface SysUserRoleDao {
    
    @Delete("delete from sys_user_roles where user_id=#{userId}")
    int deleteObjectsByUserId(Integer userId);

    @Select("select role_id from sys_user_roles where user_id=#{userId}")
    List findRoleIdsByUserId(Integer userId);
    
    int insertObjects(Integer userId,Integer[]roleIds);
}
 

package com.cy.pj.sys.service;

import java.util.List;
import java.util.Map;

import com.cy.pj.common.pojo.Node;
import com.cy.pj.sys.pojo.SysDept;

public interface SysDeptService {
     List> findObjects();
     List findZTreeNodes();
     int saveObject(SysDept entity);
     int updateObject(SysDept entity);
     int deleteObject(Integer id);
}
 

package com.cy.pj.sys.service;

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


/**
 * 现在的项目都会分层设计,每次对象都会对应的接口,
 * 层与层对象进行耦合时,建议耦合与接口
 * @author huawei
 *
 */
public interface SysLogService {
    
    int deleteObjects(Integer... ids);

    /**
     * 分页查询日志信息
     * @param username 用户名
     * @param pageCurrent 当前页页码
     * @return 封装了查询和计算结果的一个分页对象
     */
    PageObject findPageObjects(String username,Long pageCurrent);
}
 

 

package com.cy.pj.sys.service;

import java.util.List;
import java.util.Map;

import com.cy.pj.common.pojo.Node;
import com.cy.pj.sys.pojo.SysMenu;

public interface SysMenuService {
    
    /**
     * 更新菜单信息
     * @param entity
     * @return
     */
    int updateObject(SysMenu entity);
    /**
     * 保存菜单信息
     * @param entity
     * @return
     */
    int saveObject(SysMenu entity);
    /**
     * 
     * @return
     */
    List findZtreeMenuNodes();
    
    /**
     * 基于菜单id删除菜单以及菜单对应的关系数据
     * @param id
     * @return 删除的行数
     */
    int deleteObject(Integer id);

    List> findObjects();
}
 

 

package com.cy.pj.sys.service;

import java.util.List;

import com.cy.pj.common.pojo.CheckBox;
import com.cy.pj.common.pojo.PageObject;
import com.cy.pj.sys.pojo.SysRole;
import com.cy.pj.sys.pojo.SysRoleMenu;

public interface SysRoleService {
    
    List findRoles();
    /**
     * 基于角色id获取角色以及角色对应的菜单id
     * @param id
     * @return
     */
    SysRoleMenu findObjectById(Integer id);
    
    int updateObject(SysRole entity,Integer[] menuIds);
    int saveObject(SysRole entity,Integer[] menuIds);

    /**
     * 查询当前页角色信息
     * @param name
     * @param pageCurrent
     * @return
     */
    PageObject findPageObjects(String name,Long pageCurrent);
}
 

 

package com.cy.pj.sys.service;

import java.util.Map;

import com.cy.pj.common.pojo.PageObject;
import com.cy.pj.sys.pojo.SysUser;
import com.cy.pj.sys.pojo.SysUserDept;

public interface SysUserService {
    
    int updateObject(SysUser entity,Integer[]roleIds);
    
    Map findObjectById(Integer id);
    
    int saveObject(SysUser entity,Integer[]roleIds);
    
    int validById(Integer id,Integer valid);

    PageObject findPageObjects(String username,Long pageCurrent);
}
 

 

package com.cy.pj.sys.service.impl;

import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import com.cy.pj.common.exception.ServiceException;
import com.cy.pj.common.pojo.Node;
import com.cy.pj.sys.dao.SysDeptDao;
import com.cy.pj.sys.pojo.SysDept;
import com.cy.pj.sys.service.SysDeptService;

@Service
public class SysDeptServiceImpl implements SysDeptService {
    @Autowired
    private SysDeptDao sysDeptDao;
    @Override
    public List> findObjects() {
        List> list=
        sysDeptDao.findObjects();
        if(list==null||list.size()==0)
        throw new ServiceException("没有部门信息");
        return list;
    }
    @Override
    public List findZTreeNodes() {
        List list=
        sysDeptDao.findZTreeNodes();
        if(list==null||list.size()==0)
        throw new ServiceException("没有部门信息");
        return list;
    }
    @Override
    public int updateObject(SysDept entity) {
        //1.合法验证
        if(entity==null)
        throw new ServiceException("保存对象不能为空");
        if(StringUtils.isEmpty(entity.getName()))
        throw new ServiceException("部门不能为空");
        int rows;
        //2.更新数据
        try{
        rows=sysDeptDao.updateObject(entity);
        }catch(Exception e){
        e.printStackTrace();
        throw new ServiceException("更新失败");
        }
        //3.返回数据
        return rows;
    }
    
    @Override
    public int saveObject(SysDept entity) {
        //1.合法验证
        if(entity==null)
        throw new ServiceException("保存对象不能为空");
        if(StringUtils.isEmpty(entity.getName()))
        throw new ServiceException("部门不能为空");
        //2.保存数据
        int rows=sysDeptDao.insertObject(entity);
        //if(rows==1)
        //throw new ServiceException("save error");
        //3.返回数据
        return rows;
    }
    @Override
    public int deleteObject(Integer id) {
        //1.合法性验证
        if(id==null||id<=0)
        throw new ServiceException("数据不合法,id="+id);
        //2.执行删除操作
        //2.1判定此id对应的菜单是否有子元素
        int childCount=sysDeptDao.getChildCount(id);
        if(childCount>0)
        throw new ServiceException("此元素有子元素,不允许删除");
        //2.2判定此部门是否有用户
        //int userCount=sysUserDao.getUserCountByDeptId(id);
        //if(userCount>0)
        //throw new ServiceException("此部门有员工,不允许对部门进行删除");
        //2.2判定此部门是否已经被用户使用,假如有则拒绝删除
        //2.3执行删除操作
        int rows=sysDeptDao.deleteObject(id);
        if(rows==0)
        throw new ServiceException("此信息可能已经不存在");
        return rows;
    }


}
 

 

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) {
        if(ids==null||ids.length==0)
            throw new IllegalArgumentException("请输入id的值");
        int rows = sysLogDao.deleteObjects(ids);
        if(rows==0)
            throw new ServiceException("记录可能已经不存在");
        return rows;
    }

    @Override
    public PageObject findPageObjects(String username, Long pageCurrent) {
        //验证参数的有效性
        if(pageCurrent==null||pageCurrent<1)
            throw new IllegalArgumentException("当前页码值无效");
        
        //统计总记录数
        long rowCount = sysLogDao.getRowCount(username);
        if(rowCount==0)
            throw new ServiceException("没有找到对应的结果");
        //查询当前数据
        int pageSize=5;//这个值也可以从页面传递到服务端
        long startIndex=(pageCurrent-1)*pageSize;
        List records=sysLogDao.findPageObjects(username,startIndex,pageSize);
                
        //封装查询结果以及计算结果
//        PageObject po = new PageObject<>();
//        po.setRowCount(rowCount);
//        po.setRecords(records);
//        po.setPageSize(pageSize);
//        po.setPageCurrent(pageCurrent);
        //long pageCount=rowCount/pageSize;
       // if(rowCount%pageSize!=0)pageCount++;
        //po.setPageCount(pageCount);
        //return po;
        //return new PageObject<>(rowCount, records, pageCount, pageSize, pageCurrent);
        return new PageObject<>(rowCount, records, pageSize, pageCurrent);
    }

}
 

 

package com.cy.pj.sys.service.impl;

import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import com.cy.pj.common.exception.ServiceException;
import com.cy.pj.common.pojo.Node;
import com.cy.pj.sys.dao.SysMenuDao;
import com.cy.pj.sys.dao.SysRoleMenuDao;
import com.cy.pj.sys.pojo.SysMenu;
import com.cy.pj.sys.service.SysMenuService;

@Service
public class SysMenuServiceImpl implements SysMenuService{

    @Autowired
    private SysMenuDao sysMenuDao;
    
    @Autowired
    private SysRoleMenuDao sysRoleMenuDao;
    
    @Override
    public int updateObject(SysMenu entity) {
        //1.参数校验
        if(entity==null)
            throw new IllegalArgumentException("保存对象不能为空");
        if(StringUtils.isEmpty(entity.getName()))//org.springframework.util.StringUtils
            throw new IllegalArgumentException("菜单名不允许为空");
        //....
        //2.保存菜单信息
        int rows = sysMenuDao.updateObject(entity);
        if(rows==0)
            throw new ServiceException("记录可能已经不存在了");
        return rows;
    }
    
    @Override
    public int saveObject(SysMenu entity) {
        //1.参数校验
        if(entity==null)
            throw new IllegalArgumentException("保存对象不能为空");
        if(StringUtils.isEmpty(entity.getName()))//org.springframework.util.StringUtils
            throw new IllegalArgumentException("菜单名不允许为空");
        //....
        //2.保存菜单信息
        int rows = sysMenuDao.insertObject(entity);
        return rows;
    }
    
    @Override
    public List findZtreeMenuNodes() {
        // ...
        return sysMenuDao.findZtreeMenuNodes();
    }
    
    @Override
    public int deleteObject(Integer id) {
        // 1.参数校验
        if(id==null||id<1)
            throw new IllegalArgumentException("id值无效");
        //2.查询子菜单个数
        int childCount = sysMenuDao.getChildCount(id);
        if(childCount>0)
            throw new ServiceException("请先删除子菜单");
        //3.删除菜单信息
        //3.1删除关系数据
        sysRoleMenuDao.deleteObjectsByMenuId(id);
        //3.2删除自身信息
        int rows = sysMenuDao.deleteObject(id);
        if(rows==0) 
            throw new ServiceException("记录可能已经不存在了");
        return rows;
    }

    public List> findObjects() {
        // ....
        return sysMenuDao.findObjects();
    }
    
    
}
 

 

package com.cy.pj.sys.service.impl;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import com.cy.pj.common.exception.ServiceException;
import com.cy.pj.common.pojo.CheckBox;
import com.cy.pj.common.pojo.PageObject;
import com.cy.pj.sys.dao.SysRoleDao;
import com.cy.pj.sys.dao.SysRoleMenuDao;
import com.cy.pj.sys.pojo.SysRole;
import com.cy.pj.sys.pojo.SysRoleMenu;
import com.cy.pj.sys.service.SysRoleService;

@Service
public class SysRoleServiceImpl implements SysRoleService {

    @Autowired
    private SysRoleDao sysRoleDao;
    
    @Autowired
    private SysRoleMenuDao sysRoleMenuDao;
    
    @Override
    public List findRoles() {
        // ...
        return sysRoleDao.findRoles();
    }
    
    @Override
    public SysRoleMenu findObjectById(Integer id) {
        // 1.参数校验
        if(id==null||id<1)
            throw new IllegalArgumentException("id值无效");
        //2.基于角色id查询角色自身信息
        SysRoleMenu rm = sysRoleDao.findObjectById(id);//sys_roles
        //3.基于角色id查询角色对应的菜单id并进行封装
        //List menuIds = sysRoleMenuDao.findMenuIdsByRoleId(id);//sys_role_menus
        //rm.setMenuIds(menuIds);
        //4.返回查询结果
        return rm;
    }
    
    @Override
    public int updateObject(SysRole entity, Integer[] menuIds) {
        // 1.参数校验
        if(entity==null)
            throw new IllegalArgumentException("保存对象不能为空");
        if(StringUtils.isEmpty(entity.getName()))//StringUtils为spring中的一个工具类
            throw new IllegalArgumentException("角色名不能为空");
        if(menuIds==null||menuIds.length==0)
            throw new IllegalArgumentException("必须为角色分配权限");
        //2.更新角色自身信息
        int rows = sysRoleDao.updateObject(entity);
        //3.更新角色菜单关系数据
        //3.1删除原有关系数据
        sysRoleMenuDao.deleteObjectsByRoleId(entity.getId());
        //3.2添加新的关系数据
        sysRoleMenuDao.insertObjects(entity.getId(), menuIds);
        return rows;
    }
    
    
    @Override
    public int saveObject(SysRole entity, Integer[] menuIds) {
        // 1.参数校验
        if(entity==null)
            throw new IllegalArgumentException("保存对象不能为空");
        if(StringUtils.isEmpty(entity.getName()))//StringUtils为spring中的一个工具类
            throw new IllegalArgumentException("角色名不能为空");
        if(menuIds==null||menuIds.length==0)
            throw new IllegalArgumentException("必须为角色分配权限");
        //2.保存角色自身信息
        int rows = sysRoleDao.insertObject(entity);
        //3.保存角色菜单关系数据
        sysRoleMenuDao.insertObjects(entity.getId(), menuIds);
        return rows;
    }
    
    @Override
    public PageObject findPageObjects(String name, Long pageCurrent) {
        // 1.参数有效性校验
        if(pageCurrent==null||pageCurrent<1)
            throw new IllegalArgumentException("页码值无效");
        //2.查询总记录数并校验
        long rowCount = sysRoleDao.getRowCount(name);
        if(rowCount==0)
            throw new ServiceException("没有找到对应记录");
        //3.查询当前页
        int pageSize=2;
        long startIndex=(pageCurrent-1)*pageSize;
        List records = 
                sysRoleDao.findPageObjects(name, startIndex, pageSize);
        //4.封装查询结果并返回
        return new PageObject<>(rowCount, records, pageSize, pageCurrent);
    }

}
 

 

package com.cy.pj.sys.service.impl;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import org.apache.shiro.crypto.hash.SimpleHash;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import com.cy.pj.common.exception.ServiceException;
import com.cy.pj.common.pojo.PageObject;
import com.cy.pj.common.utils.AssertUtils;
import com.cy.pj.sys.dao.SysUserDao;
import com.cy.pj.sys.dao.SysUserRoleDao;
import com.cy.pj.sys.pojo.SysRole;
import com.cy.pj.sys.pojo.SysUser;
import com.cy.pj.sys.pojo.SysUserDept;
import com.cy.pj.sys.service.SysUserService;

@Service
public class SysUserServiceImpl implements SysUserService {

    @Autowired
    private SysUserDao sysUserDao;
    
    @Autowired
    private SysUserRoleDao sysUserRoleDao;
    
    @Override
    public int updateObject(SysUser entity, Integer[] roleIds) {
        // 1.参数校验
        AssertUtils.isArgValid(entity==null, "保存对象不能为空");
        AssertUtils.isArgValid(entity.getUsername()==null||"".equals(entity.getUsername()),"用户名不能为空");
        AssertUtils.isArgValid(roleIds==null||roleIds.length==0, "必须为用户分配角色");
        //2.保存用户自身信息
        int rows = sysUserDao.updateObject(entity);
        if(rows==0)
            AssertUtils.isServiceValid(rows==0, "记录可能不存在");
        //3.保存用户与角色关系数据
        sysUserRoleDao.deleteObjectsByUserId(entity.getId());
        sysUserRoleDao.insertObjects(entity.getId(), roleIds);
        return rows;
    }
    
    @Override
    public Map findObjectById(Integer id) {
        // 1.参数校验
        AssertUtils.isArgValid(id==null||id<1, "id值无效");
        //2.查询用户以及用户对应的部门信息
        SysUserDept user = sysUserDao.findObjectById(id);
        AssertUtils.isServiceValid(user==null, "记录可能已经不存在");
        //3.查询用户对应的角色信息
        List roleIds = sysUserRoleDao.findRoleIdsByUserId(id);
        //4.封装查询结果
        Map map = new HashMap<>();
        map.put("user", user);
        map.put("roleIds", roleIds);
        return map;
    }
    
    @Override
    public int saveObject(SysUser entity, Integer[] roleIds) {
        // 1.参数校验
        AssertUtils.isArgValid(entity==null, "保存对象不能为空");
        AssertUtils.isArgValid(entity.getUsername()==null||"".equals(entity.getUsername()),"用户名不能为空");
        AssertUtils.isArgValid(entity.getPassword()==null||"".equals(entity.getPassword()),"密码不能为空");
        AssertUtils.isArgValid(roleIds==null||roleIds.length==0, "必须为用户分配角色");
        //2.保存用户自身信息
        //借助spring框架中自带API对密码进行加密
        //2.1构建salt值
        String salt = UUID.randomUUID().toString();//产生一个固定长度随机字符串
        //2.2基于相关API对密码进行加密
        //String hashedPassword = 
                //DigestUtils.md5DigestAsHex((salt+entity.getPassword()).getBytes());
        //借助shiro框架中的API对密码进行加密
        SimpleHash sh = new SimpleHash("MD5",//算法名称
                entity.getPassword(), //未加密的密码
                salt, //加密盐
                1);//这里1表示加密次数
        String hashedPassword = sh.toHex();//将加密结果转换为16进制的字符串
        entity.setSalt(salt);//为什么盐值也要保存到数据库?(登陆时还要使用此salt对登陆密码进行加密)
        entity.setPassword(hashedPassword);
        //2.3将用户信息写入到数据库
        int rows = sysUserDao.insertObject(entity);
        //3.保存用户与角色关系数据
        sysUserRoleDao.insertObjects(entity.getId(), roleIds);
        
        return rows;
    }
    
    @Override
    public int validById(Integer id, Integer valid) {
        //1.参数校验
//        if(id==null||id<1)
//            throw new IllegalArgumentException("参数值无效");
//        if(valid!=0&&valid!=1)
//            throw new IllegalArgumentException("状态值无效");
        AssertUtils.isArgValid(id==null||id<1, "参数值无效");
        AssertUtils.isArgValid(valid!=0&&valid!=1, "状态值无效");
        //2.修改用户状态
        int rows = sysUserDao.validById(id, valid, "admin");//这里先假设为登陆用户
//        if(rows==0)
//            throw new ServiceException("记录可能已经不存在");
        AssertUtils.isServiceValid(rows==0, "记录可能已经不存在");
        return rows;
    }
    
    
    @Override
    public PageObject findPageObjects(String username, Long pageCurrent) {
        // 1.参数有效性校验
                if(pageCurrent==null||pageCurrent<1)
                    throw new IllegalArgumentException("页码值无效");
                //2.查询总记录数并校验
                long rowCount = sysUserDao.getRowCount(username);
                if(rowCount==0)
                    throw new ServiceException("没有找到对应记录");
                //3.查询当前页
                int pageSize=2;
                long startIndex=(pageCurrent-1)*pageSize;
                List records = 
                        sysUserDao.findPageObjects(username, startIndex, pageSize);
                //4.封装查询结果并返回
                return new PageObject<>(rowCount, records, pageSize, pageCurrent);
    }

}
 

 

package com.cy.pj.sys.controller;

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


/**
 * 基于Controller处理所有页面请求
 * @author huawei
 *
 */
@Controller
@RequestMapping("/")
public class PageController {
    //http://localhost/log/log_list
    //http://localhost/menu/menu_list
    //rest风格的url,{}代表是变量表达式
    @RequestMapping("{module}/{moduleUI}")
    public String doModuleUI(@PathVariable String moduleUI) {
        return "sys/"+moduleUI;
    }
    
//    @RequestMapping("menu/menu_list")
//    public String doMenuUI() {
//        return "sys/menu_list";
//    }
//     
//
//    @RequestMapping("log/log_list")
//    public String doLogUI() {
//        return "sys/log_list";
//    }
    
    @RequestMapping("doPageUI")
    public String doPageUI() {
        return "common/page";
    }
    
    @RequestMapping("doIndexUI")
    public String doIndexUI() {
        return "starter";
    }
}
 

 

package com.cy.pj.sys.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import com.cy.pj.common.pojo.JsonResult;
import com.cy.pj.sys.pojo.SysDept;
import com.cy.pj.sys.service.SysDeptService;

@RestController
@RequestMapping("/dept/")
public class SysDeptController {
    @Autowired
    private SysDeptService sysDeptService;
    @RequestMapping("doFindObjects")
    public JsonResult doFindObjects() {
        return new JsonResult(sysDeptService.findObjects());
    }
    @RequestMapping("doFindZTreeNodes")
    public JsonResult doFindZTreeNodes() {
        return new JsonResult(sysDeptService.findZTreeNodes());
    }
    
    @RequestMapping("doUpdateObject")
    public JsonResult doUpdateObject(SysDept entity){
        sysDeptService.updateObject(entity);
        return new JsonResult("update ok");
    }
    
    @RequestMapping("doSaveObject")
    public JsonResult doSaveObject(SysDept entity){
        sysDeptService.saveObject(entity);
        return new JsonResult("save ok");
    }
    @RequestMapping("doDeleteObject")
    @ResponseBody
    public JsonResult doDeleteObject(Integer id){
        sysDeptService.deleteObject(id);
        return new JsonResult("delete ok");
    }
}
 

 

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("doDeleteObjects")
    @ResponseBody
    public JsonResult doDeleteObjects(Integer... ids) {
        sysLogService.deleteObjects(ids);
        return new JsonResult("delete ok");
    }
    
    @RequestMapping("doFindPageObjects")
    @ResponseBody
    public JsonResult doFindPageObjects(String username,Long pageCurrent) {
        //try {
            PageObject pageObject = 
                    sysLogService.findPageObjects(username, pageCurrent);
            return new JsonResult(pageObject);
        //} catch (Exception e) {
        //    return new JsonResult(e);
        //}
    }
    /**
     * 思考:假如控制层有多个方法,每个方法都要进行try操作,其实也是一个重复的操作
     */
    
    
    
    
}
 

 

package com.cy.pj.sys.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.cy.pj.common.pojo.JsonResult;
import com.cy.pj.sys.pojo.SysMenu;
import com.cy.pj.sys.service.SysMenuService;

//@Controller
@RequestMapping("/menu/")
//@ResponseBody
@RestController//@Controller+@ResponseBody
public class SysMenuController {

    @Autowired
    private SysMenuService sysMenuService;
    
    @RequestMapping("doUpdateObject")
    public JsonResult doUpdateObject(SysMenu entity) {
        sysMenuService.updateObject(entity);
        return new JsonResult("update ok");
        
    }
    
    @RequestMapping("doSaveObject")
    public JsonResult doSaveObject(SysMenu entity) {
        sysMenuService.saveObject(entity);
        return new JsonResult("save ok");
        
    }
    
    @RequestMapping("doFindZtreeMenuNodes")
    public JsonResult doFindZtreeMenuNodes() {
        
        return new JsonResult(sysMenuService.findZtreeMenuNodes());
        
    }
    
    @RequestMapping("doDeleteObject")
    public JsonResult doDeleteObject(Integer id) {
        sysMenuService.deleteObject(id);
        return new JsonResult("delete ok");
        
    }
    
    @RequestMapping("doFindObjects")
    //@ResponseBody
    public JsonResult doFindObjects() {
        return new JsonResult(sysMenuService.findObjects());
        
    }
}
 

 

package com.cy.pj.sys.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.cy.pj.common.pojo.JsonResult;
import com.cy.pj.sys.pojo.SysRole;
import com.cy.pj.sys.service.SysRoleService;

@RestController
@RequestMapping("/role/")
public class SysRoleController {

    @Autowired
    private SysRoleService sysRoleService;
    
    @RequestMapping("doFindRoles")
    public JsonResult doFindRoles() {
        return new JsonResult(sysRoleService.findRoles());
    }
    
    @RequestMapping("doFindObjectById")
    public JsonResult doFindObjectById(Integer id) {
        return new JsonResult(sysRoleService.findObjectById(id));
    }
    
    @RequestMapping("doUpdateObject")
    public JsonResult doUpdateObject(SysRole entity,Integer[]menuIds) {
        sysRoleService.updateObject(entity, menuIds);
        return new JsonResult("save ok");
        
    }
    
    @RequestMapping("doSaveObject")
    public JsonResult doSaveObject(SysRole entity,Integer[]menuIds) {
        sysRoleService.saveObject(entity, menuIds);
        return new JsonResult("save ok");
        
    }
    
    @RequestMapping("doFindPageObjects")
    public JsonResult doFindPageObjects(String name,Long pageCurrent) {
        return new JsonResult(sysRoleService.findPageObjects(name, pageCurrent));
    }
    
}
 

 

package com.cy.pj.sys.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.cy.pj.common.pojo.JsonResult;
import com.cy.pj.sys.pojo.SysUser;
import com.cy.pj.sys.service.SysUserService;

@RestController
@RequestMapping("/user/")
public class SysUserController {

    @Autowired
    private SysUserService sysUserService;
    
    @RequestMapping("doUpdateObject")
    public JsonResult doUpdateObject(SysUser entity,Integer[]roleIds) {
        sysUserService.updateObject(entity, roleIds);
        return new JsonResult("update ok");
    }
    
    @RequestMapping("doFindObjectById")
    public JsonResult doFindObjectById(Integer id) {
        return new JsonResult(sysUserService.findObjectById(id));
    }
    
    @RequestMapping("doSaveObject")
    public JsonResult doSaveObject(SysUser entity,Integer[]roleIds) {
        sysUserService.saveObject(entity, roleIds);
        return new JsonResult("save ok");
    }
    
    @RequestMapping("doValidById")
    public JsonResult doValidById(Integer id,Integer valid) {
        sysUserService.validById(id, valid);
        return new JsonResult("update ok");
        
    }
    
    @RequestMapping("doFindPageObjects")
    public JsonResult doFindPageObjects(String username,Long pageCurrent) {
        return new JsonResult(sysUserService.findPageObjects(username, pageCurrent));
    }
}
 

package com.cy;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}
 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(日志管理设计说明)