物流项目(一)

1. 项目目的

本次物流项目目的

  1. 巩固复习SSM框架的使用
  2. 学习Shiro安全框架的使用
  3. 掌握后台权限管理(绝大部分企业后台项目的权限都使用的RBAC权限管理)
  4. 掌握物流项目的开发的业务以及功能
  5. 让大家拿到一份需求文档以后,从零开始,如何开发一个完整的企业项目流程

(1) 技术选型

(2) 根据需求文档设计数据库设计

(3) 根据需求文档进行需求分析,完成功能

 

2. 项目安排

  1. 完成系统基本的用户权限用户角色功能
  2. 使用shiro框架完成安全管理
  3. 完成部分物流业务功能

 

 

3. 权限系统

3.1. 什么是RBAC

名词解释  基于角色的权限访问控制(Role-Based Access Control

 

 

 

任何一个系统的后台都应该有权限管理,公司不同的员工(后台用户)对应的不同的角色,不同的角色就登录系统就拥有不同的权限(访问公司系统对应的界面)

 

由上面的解释可以得知一个系统访问有三个关键名称

用户,角色,权限

 

  1. 用户: 系统后台登录的管理员(张三,李四,王五等等),这个人访问后台系统的账户
  2. 角色: 一个公司有不同的工作人员,每个人在公司都对应有角色,这些角色下面可以有多个用户(业务员操作员财务仓管员总经理等角色
  3. 权限:  控制系统那些页面(资源)访问需要权限。

 

3.1.1. 权限,用户,角色之间的关系

一般来讲,在一个系统中,用户并不直接拥有权限,用户拥有对应的角色,再给角色分配对应的权限。 那么,最终通过数据库查询,可以得到,这个用户拥有什么权限

 

这种设计叫做 RBAC 基于角色的权限访问控制(Role-Based Access Control

 

一个角色可以拥有多个用户  ,一个用户也可以有多个权限

一个角色可以多个权限,一个权限也可以有多个权限。

 

用户角色 多对多关系  N-N

角色权限 多堆多关系  N-N

 

 

 

 

在实际开发中,一个用户一个角色情况比较多,也就是单角色用户,所以在我们的项目中,我们用户如下设计

 

一个用户对应一个角色 1-1

一个角色可以用于多个集合 1-n

 

 

 

3.2. RBAC权限管理数据库表设计

使用EclipseERMaster插件来设计

 

用户表

角色表

权限表

3.2.1. ER

 

 

 

3.2.2. Sql

SET SESSION FOREIGN_KEY_CHECKS=0;

 

/* Drop Tables */

 

DROP TABLE IF EXISTS permission;

DROP TABLE IF EXISTS user;

DROP TABLE IF EXISTS role;

 

 

 

 

/* Create Tables */

 

-- 权限表

CREATE TABLE permission

(

permission_id bigint NOT NULL AUTO_INCREMENT COMMENT '权限id',

name varchar(50) COMMENT '权限名称',

type varchar(20) COMMENT '权限类型(menu 菜单权限,permission:普通权限)',

url varchar(100) COMMENT '权限跳转url地址',

expression varchar(50) COMMENT '权限表达式(shiro权限判断使用)',

parent_id bigint COMMENT '父权限',

sort int COMMENT '菜单权限显示时候排序',

PRIMARY KEY (permission_id)

) COMMENT = '权限表';

 

 

-- 角色表

CREATE TABLE role

(

role_id bigint NOT NULL AUTO_INCREMENT COMMENT '角色id',

rolename varchar(50) COMMENT '角色名称',

remark varchar(100) COMMENT '角色备注',

-- 角色id,多个角色使用逗号隔开 1,2,3,4

permission_ids varchar(200) COMMENT '角色id,多个角色使用逗号隔开 1,2,3,4',

PRIMARY KEY (role_id)

) COMMENT = '角色表';

 

 

-- 用户表

CREATE TABLE user

(

user_id bigint NOT NULL AUTO_INCREMENT COMMENT '用户id',

username varchar(50) COMMENT '用户名',

realname varchar(50) COMMENT '真实名称',

password varchar(100) COMMENT '密码',

salt varchar(50) COMMENT '盐(密码加密用)',

status int DEFAULT 1 COMMENT '用户状态 1 可用,2,锁定,3离职',

create_date date COMMENT '入职日期',

role_id bigint NOT NULL COMMENT '角色id',

PRIMARY KEY (user_id)

) COMMENT = '用户表';

 

 

 

/* Create Foreign Keys */

 

ALTER TABLE user

ADD FOREIGN KEY (role_id)

REFERENCES role (role_id)

ON UPDATE RESTRICT

ON DELETE RESTRICT

;

 

 

 

4. 项目环境准备

4.1. 技术选型

整个项目使用的SSM框架 SpringMVCSpringMyBatisMySql数据库,Shiro安全框架

 

4.2. 使用Maven创建Web项目

 

4.3. 引入maven项目pom文件依赖的和插件配置

Mavenpom文件集成了整个项目所需要的绝大部分框架依赖,后期其他依赖在实际开发中按需引入集合

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

  <modelVersion>4.0.0</modelVersion>

  <groupId>cn.zj</groupId>

  <artifactId>logistics_system</artifactId>

  <version>0.0.1-SNAPSHOT</version>

  <packaging>war</packaging>

<properties>

<spring.version>4.3.2.RELEASE</spring.version>

<slf4j.version>1.6.6</slf4j.version>

<log4j.version>1.2.17</log4j.version>

<mybatis.version>3.4.4</mybatis.version>

<mybatis-spring.version>1.3.2</mybatis-spring.version>

<druid.version>1.1.10</druid.version>

<shiro.version>1.2.3</shiro.version>

<mybatis-pagehelper.version>5.1.8</mybatis-pagehelper.version>

<jdbcmysql.version>5.1.26</jdbcmysql.version>

<junit.version>4.12</junit.version>

<jstl.version>1.2</jstl.version>

<jackson.version>2.9.5</jackson.version>

<aspect.version>1.7.4</aspect.version>

<servlet.version>3.1.0</servlet.version>

<jsp.version>2.2.1</jsp.version>

</properties>

<dependencies>

<!-- spring -->

<dependency>

<groupId>org.aspectj</groupId>

<artifactId>aspectjweaver</artifactId>

<version>${aspect.version}</version>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-webmvc</artifactId>

<version>${spring.version}</version>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-jdbc</artifactId>

<version>${spring.version}</version>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-tx</artifactId>

<version>${spring.version}</version>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-test</artifactId>

<version>${spring.version}</version>

</dependency>

 

<!-- log4j日志 -->

<dependency>

<groupId>log4j</groupId>

<artifactId>log4j</artifactId>

<version>${log4j.version}</version>

</dependency>

<dependency>

<groupId>org.slf4j</groupId>

<artifactId>slf4j-api</artifactId>

<version>${slf4j.version}</version>

</dependency>

<dependency>

<groupId>org.slf4j</groupId>

<artifactId>slf4j-log4j12</artifactId>

<version>${slf4j.version}</version>

</dependency>

 

<!-- 连接池 -->

<dependency>

<groupId>com.alibaba</groupId>

<artifactId>druid</artifactId>

<version>${druid.version}</version>

</dependency>

<!-- mybatis -->

<dependency>

<groupId>org.mybatis</groupId>

<artifactId>mybatis</artifactId>

<version>${mybatis.version}</version>

</dependency>

 

<!-- mybatis的分页插件 -->

<dependency>

<groupId>com.github.pagehelper</groupId>

<artifactId>pagehelper</artifactId>

<version>${mybatis-pagehelper.version}</version>

</dependency>

 

 

<!-- mybatis和spring集成包 -->

<dependency>

<groupId>org.mybatis</groupId>

<artifactId>mybatis-spring</artifactId>

<version>${mybatis-spring.version}</version>

</dependency>

 

 

 

<!-- 加入servletjsp的依赖 -->

<dependency>

<groupId>javax.servlet</groupId>

<artifactId>javax.servlet-api</artifactId>

<version>${servlet.version}</version>

<scope>provided</scope>

</dependency>

 

<dependency>

<groupId>javax.servlet.jsp</groupId>

<artifactId>javax.servlet.jsp-api</artifactId>

<version>${jsp.version}</version>

<scope>provided</scope>

</dependency>

 

 

<!-- 引入shiro框架的依赖 -->

<dependency>

<groupId>org.apache.shiro</groupId>

<artifactId>shiro-core</artifactId>

<version>${shiro.version}</version>

</dependency>

<dependency>

<groupId>org.apache.shiro</groupId>

<artifactId>shiro-web</artifactId>

<version>${shiro.version}</version>

</dependency>

<dependency>

<groupId>org.apache.shiro</groupId>

<artifactId>shiro-spring</artifactId>

<version>${shiro.version}</version>

</dependency>

<dependency>

<groupId>org.apache.shiro</groupId>

<artifactId>shiro-quartz</artifactId>

<version>${shiro.version}</version>

</dependency>

<!-- shiro和spring集成包 -->

<dependency>

<groupId>org.apache.shiro</groupId>

<artifactId>shiro-spring</artifactId>

<version>${shiro.version}</version>

</dependency>

<!-- MySQL数据库驱动依赖 -->

<dependency>

<groupId>mysql</groupId>

<artifactId>mysql-connector-java</artifactId>

<version>${jdbcmysql.version}</version>

</dependency>

 

<!-- 缓存依赖 -->

<dependency>

<groupId>org.apache.shiro</groupId>

<artifactId>shiro-ehcache</artifactId>

<version>1.2.3</version>

</dependency>

<dependency>

<groupId>net.sf.ehcache</groupId>

<artifactId>ehcache-core</artifactId>

<version>2.6.0</version>

</dependency>

 

<!-- jstl标签库 -->

 

<dependency>

<groupId>javax.servlet</groupId>

<artifactId>jstl</artifactId>

<version>${jstl.version}</version>

</dependency>

 

<!-- jackson json转换工具 -->

<dependency>

<groupId>com.fasterxml.jackson.core</groupId>

<artifactId>jackson-databind</artifactId>

<version>${jackson.version}</version>

</dependency>

<dependency>

<groupId>com.fasterxml.jackson.core</groupId>

<artifactId>jackson-core</artifactId>

<version>${jackson.version}</version>

</dependency>

<dependency>

<groupId>com.fasterxml.jackson.core</groupId>

<artifactId>jackson-annotations</artifactId>

<version>${jackson.version}</version>

</dependency>

 

<dependency>

<groupId>org.mybatis.generator</groupId>

<artifactId>mybatis-generator-core</artifactId>

<version>1.3.7</version>

</dependency>

<dependency>

<groupId>org.mybatis.generator</groupId>

<artifactId>mybatis-generator-maven-plugin</artifactId>

<version>1.3.7</version>

</dependency>

 

 

 

<dependency>

<groupId>org.apache.commons</groupId>

<artifactId>commons-lang3</artifactId>

<version>3.1</version>

</dependency>

<!-- 单元测试jar包 -->

<dependency>

<groupId>junit</groupId>

<artifactId>junit</artifactId>

<version>${junit.version}</version>

</dependency>

</dependencies>

<build>

<plugins>

<plugin>

<groupId>org.apache.maven.plugins</groupId>

<artifactId>maven-compiler-plugin</artifactId>

<version>3.1</version>

<configuration>

<source>1.8</source>

<target>1.8</target>

</configuration>

</plugin>

<plugin>

<groupId>org.apache.tomcat.maven</groupId>

<artifactId>tomcat7-maven-plugin</artifactId>

<version>2.2</version>

<configuration>

<path>/logistics</path>

<port>8080</port>

</configuration>

</plugin>

<plugin>

<groupId>org.apache.maven.plugins</groupId>

<artifactId>maven-surefire-plugin</artifactId>

<version>2.20.1</version>

<configuration>

<skipTests>true</skipTests>

</configuration>

</plugin>

</plugins>

</build>

</project>

 

5. 框架集成配置

整个框架的 SpringSpringMVC采用 xml方式配置(也可以自定义使用注解配置),

所以需要创建准备对应的xml配置文件

spring.xml

Spring框架的主配置文件,主要

springmvc.xml

SpringMVC配置文件

db.properties

数据库连接配置文件

log4j.properties

Log4j日志配置文件

其他配置文件后面使用到按需配置

使用Web项目,需要将框架位置到web.xml中才能生效

 

5.1. spring.xml文件配置

Spring.xml配置文件,主要配置spring注解配置包扫描组件位置,MyBatis代理对象创建

事务管理的配置

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:context="http://www.springframework.org/schema/context"

xmlns:aop="http://www.springframework.org/schema/aop"

xmlns:tx="http://www.springframework.org/schema/tx"

xmlns:mvc="http://www.springframework.org/schema/mvc"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="

http://www.springframework.org/schema/beans

        http://www.springframework.org/schema/beans/spring-beans.xsd

        http://www.springframework.org/schema/context

        http://www.springframework.org/schema/context/spring-context.xsd

        http://www.springframework.org/schema/aop

        http://www.springframework.org/schema/aop/spring-aop.xsd

        http://www.springframework.org/schema/tx

        http://www.springframework.org/schema/tx/spring-tx.xsd

        http://www.springframework.org/schema/mvc

        http://www.springframework.org/schema/mvc/spring-mvc.xsd

        ">

 

<!-- 配置包扫描位置 -->

<context:component-scan base-package="cn.zj.logistics" />

 

<!-- 读取 db.properties数据库配置文件 -->

<context:property-placeholder

location="classpath:db.properties" />

 

<!-- 配置 druid 连接池 -->

<bean id="dataSource"

class="com.alibaba.druid.pool.DruidDataSource" init-method="init"

destroy-method="close">

<property name="driverClassName"

value="${jdbc.driverClassName}" />

<property name="url" value="${jdbc.url}" />

<property name="username" value="${jdbc.username}" />

<property name="password" value="${jdbc.password}" />

<property name="maxActive" value="${jdbc.maxActive}" />

</bean>

 

 

<!-- 配置SqlSessionFactory对象的创建 -->

<bean id="sqlSessionFactory"

class="org.mybatis.spring.SqlSessionFactoryBean">

 

<!-- 注入插件 -->

<property name="plugins">

<array>

<bean class="com.github.pagehelper.PageInterceptor">

<property name="properties">

<value>

<!-- 方言 -->

helperDialect=mysql

</value>

</property>

</bean>

</array>

</property>

<property name="dataSource" ref="dataSource" />

 

<!-- 配置映射文件 -->

<property name="mapperLocations">

<array>

<value>classpath:cn/sxt/edu/mapper/*Mapper.xml</value>

</array>

</property>

 

<!-- 配置别名,使用包扫描配置 -->

<property name="typeAliasesPackage" value="cn.zj.logistics.pojo" />

 

<!-- 读取mybatis-config.xml文件(使用个性化配置,日志,缓存等等) -->

<property name="configLocation"

value="classpath:mybatis-config.xml"></property>

</bean>

 

 

<!-- 使用包扫描的方式创包下面所有接口对应的代理对象 -->

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">

<!-- 配置包扫描创建代理对象的位置 -->

<property name="basePackage" value="cn.zj.logistics.mapper" />

 

<!-- 注意: 需要注入SqlSessionFactory工厂对象的名称 !!! -->

<property name="sqlSessionFactoryBeanName"

value="sqlSessionFactory" />

</bean>

 

 

<!-- MyBatis的事务配置 -->

 

<!-- 配置事务管理器 : what? -->

 

<bean id="transactionManager"

class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

<!-- 注入数据源 -->

<property name="dataSource" ref="dataSource" />

</bean>

 

<!-- Spring事务配置 : when? -->

<tx:advice id="txAdvice"

transaction-manager="transactionManager">

<tx:attributes>

<!-- dql操作,一般都是只读事务 -->

<tx:method name="get*" read-only="true"

propagation="REQUIRED" />

<tx:method name="find*" read-only="true"

propagation="REQUIRED" />

<tx:method name="select*" read-only="true"

propagation="REQUIRED" />

<tx:method name="query*" read-only="true"

propagation="REQUIRED" />

<!-- dml操作,非只读事务 -->

<tx:method name="*" read-only="false" />

</tx:attributes>

</tx:advice>

<!-- 配置AOP切面,将事务切到Service层 -->

<aop:config>

<!-- 切入点 :where? -->

<aop:pointcut

expression="execution(* cn.zj.logistics.service.impl.*.*(..))" id="pt" />

<!-- 切面:切入点+通知 -->

<aop:advisor advice-ref="txAdvice" pointcut-ref="pt" />

</aop:config>

</beans>

 

5.2. springmvc.xml配置

Springmvc.xml主要配置一些springmvc框架相关配置,开启注解驱动,视图解析器,静态资源处理等等

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:context="http://www.springframework.org/schema/context"

xmlns:mvc="http://www.springframework.org/schema/mvc"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="

http://www.springframework.org/schema/beans

        http://www.springframework.org/schema/beans/spring-beans.xsd

        http://www.springframework.org/schema/context

        http://www.springframework.org/schema/context/spring-context.xsd

        http://www.springframework.org/schema/mvc

        http://www.springframework.org/schema/mvc/spring-mvc.xsd

        ">

 

<!-- 开启SpringMVC的注解驱动 -->

<mvc:annotation-driven />

 

 

<!-- 配置视图解析器 -->

<bean

class="org.springframework.web.servlet.view.InternalResourceViewResolver">

 

<property name="prefix" value="/WEB-INF/view/" />

 

<property name="suffix" value=".jsp"></property>

 

</bean>

 

<!-- 支持静态资源处理 -->

<mvc:default-servlet-handler/>

 

 

</beans>

 

5.3. db.properties配置文件

数据库连接配置文件

jdbc.driverClassName=com.mysql.jdbc.Driver

jdbc.url=jdbc:mysql://localhost:3306/logistics?characterEncoding=utf-8

jdbc.username=root

jdbc.password=root

jdbc.maxActive=10

5.4. log4j.properties日志配置文件

# Global logging configuration

log4j.rootLogger=ERROR, stdout

# MyBatis logging configuration...

log4j.logger.cn.zj.logistics=TRACE

# Console output...

log4j.appender.stdout=org.apache.log4j.ConsoleAppender

log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

 

5.5. web.xml

Web.xml作为动态web项目的入口文件,springspringmvc框架集成相关配置需要放到在此文件下配置

<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">

 

 <!-- 配置spring的授权过滤器,让spring来管理shiro框架 -->

 <filter>

  <filter-name>shiroFilter</filter-name>

  <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>

  <!-- 配置shirofilter的生命周期交给servlet管理 -->

  <init-param>

  <param-name>targetFilterLifecycle</param-name>

  <param-value>true</param-value>

  </init-param>

 </filter>

 <filter-mapping>

  <filter-name>shiroFilter</filter-name>

  <url-pattern>/*</url-pattern>

 </filter-mapping>

  

  <!--  字符编码过滤器 -->

  <filter>

<filter-name>CharacterEncodingFilter</filter-name>

<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>

<init-param>

<param-name>encoding</param-name>

<param-value>utf-8</param-value>

</init-param>

</filter>

<filter-mapping>

<filter-name>CharacterEncodingFilter</filter-name>

<url-pattern>/*</url-pattern>

</filter-mapping>

 

 

<!-- 1.配置SpringMVC的前端控制器(总控) -->

<servlet>

<servlet-name>MVC</servlet-name>

<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

<init-param>

<param-name>contextConfigLocation</param-name>

<param-value>classpath:spring*.xml</param-value>

</init-param>

<load-on-startup>1</load-on-startup>

</servlet>

 

<servlet-mapping>

<servlet-name>MVC</servlet-name>

<url-pattern>*.do</url-pattern>

</servlet-mapping>

 

 

<!-- 设置项目的欢迎页面 -->

   <welcome-file-list>

   <welcome-file>/login.jsp</welcome-file>

   </welcome-file-list>

</web-app>

 

5.6. Springmvc框架集成的测试

 

5.6.1. 新建一个AdminController

package cn.zj.logistic.controller;

 

import org.springframework.stereotype.Controller;

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

 

@Controller

@RequestMapping("/admin")

public class AdminController {

 

//后台用户列表显示页面

@RequestMapping("/adminPage.do")

public String adminPage() {

 

return "admin_list";

}

}

 

5.6.2. adminPage.jsp管理员操作jsp文件

因为springmvc框架配置了视图解析器,所有SpringMVC控制器请求默认都请求转发

WEB-INF/view 目录下面

WEB-INF目录下面新建一个view目录,在下面再创建一个 adminPage.jsp文件

 

 

 

5.6.3. 使用maventomcat插件启动web项目

 

 

 

 

 

 

5.6.4. 访问页面地址

在页面输入管理员页面地址,出现内容,说明SpringMVC框架集成成功

 

 

 

5.7. MyBatis框架集成测试

MyBatis的框架的集成配置在spring.xml文件中已经集成,在此处直接测试即可

 

5.7.1. 使用MyBatis的逆向工程生成实体类和Mapper映射

实际开发中,绝大部分项目的开发,数据库表的javadomain映射管理,数据库的Mapper文件的生成,都使用的是逆向工程生成的(使用逆向工程开发者不在需要手动去写数据库单表的增删改查操作)

 

除了逆向工程自动生成,在开发者自己还不太熟悉MyBatis框架的使用情况下,为了学习和巩固MyBatis框架的每个细节使用。还是可以自己完全手动编写代码

 

5.7.2. 逆向工程配置文件

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>

  <context id="context1">

   <!-- 注释构建 -->

    <commentGenerator>

       <!-- 去掉所有的注释 -->

     <property name="suppressAllComments" value="true"/>

     <property name="suppressDate" value="true"/>

    </commentGenerator>

    

    <!-- 数据库四要素 -->

    <jdbcConnection connectionURL="jdbc:mysql://localhost:3306/logistics_system" driverClass="org.gjt.mm.mysql.Driver" password="root" userId="root" />

    <!-- 实体类  -->

    <javaModelGenerator targetPackage="cn.zj.logistics.pojo" targetProject="mybatis-generator/src" />

    <!-- 映射文件 -->

    <sqlMapGenerator targetPackage="cn.zj.logistics.mapper" targetProject="mybatis-generator/src" />

    

    <!-- ANNOTATEDMAPPER

     XMLMAPPER

     -->

    

    <!-- 操作接口 -->

    <javaClientGenerator  targetPackage="cn.zj.logistics.mapper" targetProject="mybatis-generator/src" type="XMLMAPPER" />

    <table  tableName="user"  domainObjectName="User"  enableCountByExample="true" enableDeleteByExample="false" enableSelectByExample="true" enableUpdateByExample="false"></table>

    <table  tableName="role" domainObjectName="Role" enableCountByExample="false" enableDeleteByExample="false" enableSelectByExample="true" enableUpdateByExample="false"></table>

    <table  tableName="permission"  domainObjectName="Permission" enableCountByExample="false" enableDeleteByExample="false" enableSelectByExample="true" enableUpdateByExample="false"></table>

  </context>

</generatorConfiguration>

 

5.7.3. 逆向工程操作

 

 

生成的接口映射文件和实体类

 

 

5.7.4. 将这两个包里面的文件拷贝到我们项目即可

 

 

 

5.7.5. 创建项目service

因为spring事务管理是切入到service层的,所以我们在测试的时候,先把service层代码写出来,在测试的时候引入service层来测试即可

 

 

 

UserService层代码

//Spring的 IOC

@Service

public class UserServiceImpl implements UserService {

//Spring的 DI

@Autowired

private  UserMapper userMapper;

 

/**

 * 插入操作

 */

@Override

public int insert(User record) {

return userMapper.insert(record);

}

/**

 * 条件查询

 */

@Override

public List<User> selectByExample(UserExample example) {

return userMapper.selectByExample(example);

}

}

 

5.7.6. 编写UserTest单元测试类,测试代码

编写一个测试后台用户的单元测试类型,使用Spring的测试+单元测试来测试一下MyBatis是否集成成功

 

5.7.6.1. 插入操作

 

 

 

5.7.6.2. 查询操作

 

 

 

看到上面结果说明 SSM框架集成已经成功了,接下来就可以正式编写我们的项目代码

 

 

6. 项目前端模板页面的准备

 

6.1. jsp页面的准备

 

 

 

7. 后台首页访问

 

7.1. 创建IndexController

后台首页的jsp页面在WEB-INF/view下面,不能直接访问,需要是用户访问一个控制器并请求转发跳转到 index.jsp中的

 

package cn.zj.logistics.controller;

 

import org.springframework.stereotype.Controller;

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

 

@Controller

public class IndexController {

//后台首页

@RequestMapping("/index.do")

public String index() {

 

//返回的index是逻辑视图页面,自动根据SpringMVC的视图解析器寻找WEB-INF/view下面的 index.jsp

return "index";

}

//后台首页欢迎页面

@RequestMapping("/welcome.do")

public String welcode() {

return "welcome";

}

}

 

 

7.2. 后台首页效果

 

 

到此,后台界面准备完毕,接下来,就是我们具体的RBAC的细节开发了

 

后台管理员管理

角色管理

权限管理

 

 

8. 后台管理员管理

后台管理员管理必须先调整到管理员页面,管理员页面是对应管理员操作的的入口页面,所有操作入门口都在这个页面

  1. 管理员列表

(1) 分页功能

(2) 条件搜索功能

  1. 添加管理员
  2. 删除管理员
  3. 删除管理员

8.1. 跳转到管理员页面

由于管理员页面 adminpage.jsp WEB-INF/view 下面,所以只能访问控制器以后跳转到此页面下面

Index.jsp 菜单页跳转的页面地址

<li><a data-href="${ctx}/admin/adminPage.do" data-title="管理员列表" href="javascript:void(0)">管理员列表</a></li>

 

 

 

AdminController页面代码

package cn.zj.logistics.controller;

 

import org.springframework.stereotype.Controller;

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

 

@Controller

@RequestMapping("/admin")

public class AdminController {

 

//后台用户列表显示页面

@RequestMapping("/adminPage.do")

public String adminPage() {

 

return "adminPage";

}

}

 

8.2. 用户管理界面效果

 

 

 

8.3. 管理员列表-功能

8.3.1. 后台查询数据

后台管理员列表的显示,先完成后台的查询操作,再完成前台页面数据的显示,后台查询要考虑到条件搜索,分页功能

 

在单元测试测试Service层的条件查询方法

 

 

 

 

虽然逆向工程代码可以进行条件查询,但是MyBatis的逆向工程查询功能代码默认是不能进行分页查询的

 

8.3.2. MyBatis的分页插件 MyBatisHelper

 

问题 :如何对MyBatis的逆向工程代码进行分页?

解决方案:使用MyBatisHelper分页插件

 

MyBatisHelper插件是一个业界牛逼人物(刘增辉 )编写的免费的开源的分页插件,是得到MyBatis框架官方推荐的插件,使用起来非常非常非常(重要事情说三遍)简单,一句对代码即可对MyBatis逆向工程代码查询数据进行分页(

 

MyBatisHelper官方地址

https://pagehelper.github.io/

 

8.3.3. 项目pom文件引入分页插件依赖

最新版本5.18

<dependency>

    <groupId>com.github.pagehelper</groupId>

    <artifactId>pagehelper</artifactId>

    <version>5.18</version>

</dependency>

插件必须要整合到MyBatis框架的SqlSessionFactory工厂中

spring和mybatis集成配置文件配置插件信息

<!-- 配置SqlSessionFactory对象的创建 -->

<bean id="sqlSessionFactory"

class="org.mybatis.spring.SqlSessionFactoryBean">

 

<!-- 注入插件 -->

<property name="plugins">

<array>

<bean class="com.github.pagehelper.PageInterceptor">

<property name="properties">

<value>

<!-- 方言 -->

helperDialect=mysql

</value>

</property>

</bean>

</array>

</property>

<property name="dataSource" ref="dataSource" />

 

<!-- 配置映射文件 -->

<property name="mapperLocations">

<array>

<value>classpath:cn/sxt/edu/mapper/*Mapper.xml</value>

</array>

</property>

 

<!-- 配置别名,使用包扫描配置 -->

<property name="typeAliasesPackage" value="cn.sxt.edu.pojo" />

 

<!-- 读取mybatis-config.xml文件(使用个性化配置,日志,缓存等等) -->

<property name="configLocation"

value="classpath:mybatis-config.xml"></property>

</bean>

 

 

 

8.3.4.  MyBatisHelper的使用

配置和使用在线地址

https://github.com/pagehelper/Mybatis-PageHelper/blob/master/wikis/zh/HowToUse.md

 

// 测试查询管理员数据

@Test

public void testQuery() throws Exception {

//模拟用户前台提交的关键字搜索

String keyword = "乔峰";

 

/**

 * 使用PageHelper一个静态方法即可完成分页

 * PageHelper.startPage(pageNum, pageSize)

 * pageNum : 当前页 默认 从1 开始

 * pageSize :每页条数

 */

//模拟用户提交过来的 页面,和每页的条数

Integer pageNum = 2;

Integer pageSize = 10;

 

//开启分页(注意一定要方在查询代码之前执行)

PageHelper.startPage(pageNum,pageSize);

 

UserExample example = new UserExample();

//条件

Criteria criteria = example.createCriteria();

criteria.andRealnameLike("%"+keyword+"%");

 

//查询结果(没有分页钱数据)

List<User> users = userService.selectByExample(example);

/**创建分页对象

 *把没有分页查询的数据创建一个分页对象

 *此对象封装了分页的所有数据

 *1.数据集合

 *2.总条数

 *3.下一页

 *4.每页条数

 *5.当前页

 *等等等

*/

PageInfo<User> pageInfo = new PageInfo<>(users);

 

System.out.println("每页结果集 :"+pageInfo.getList());

System.out.println("总记录数 :"+pageInfo.getTotal());

System.out.println("当前页 :"+pageInfo.getPageNum());

System.out.println("每页条数 :"+pageInfo.getPageSize());

System.out.println("下一页 :"+pageInfo.getNextPage());

}

8.3.4.1. 测试结果

注意:分页查询一般会发送两条sql语句,一条是查询对应的总记录数,另外一条是查询对应的结果集(当前页的数据)

DEBUG [main] - ==>  Preparing: SELECT count(0) FROM user WHERE (realname LIKE ?)

DEBUG [main] - ==> Parameters: %乔峰%(String)

TRACE [main] - <==    Columns: count(0)

TRACE [main] - <==        Row: 200

DEBUG [main] - <==      Total: 1

DEBUG [main] - ==>  Preparing: select user_id, username, realname, password, salt, status, role_id from user WHERE ( realname like ? ) LIMIT ?, ?

DEBUG [main] - ==> Parameters: %乔峰%(String), 10(Integer), 10(Integer)

TRACE [main] - <==    Columns: user_id, username, realname, password, salt, status, role_id

TRACE [main] - <==        Row: 16, qiaofen10, 乔峰 :10, abc, null, 1, null

TRACE [main] - <==        Row: 17, qiaofen11, 乔峰 :11, abc, null, 1, null

TRACE [main] - <==        Row: 18, qiaofen12, 乔峰 :12, abc, null, 1, null

TRACE [main] - <==        Row: 19, qiaofen13, 乔峰 :13, abc, null, 1, null

TRACE [main] - <==        Row: 20, qiaofen14, 乔峰 :14, abc, null, 1, null

TRACE [main] - <==        Row: 21, qiaofen15, 乔峰 :15, abc, null, 1, null

TRACE [main] - <==        Row: 22, qiaofen16, 乔峰 :16, abc, null, 1, null

TRACE [main] - <==        Row: 23, qiaofen17, 乔峰 :17, abc, null, 1, null

TRACE [main] - <==        Row: 24, qiaofen18, 乔峰 :18, abc, null, 1, null

TRACE [main] - <==        Row: 25, qiaofen19, 乔峰 :19, abc, null, 1, null

DEBUG [main] - <==      Total: 10

分页对象 :PageInfo{pageNum=2, pageSize=10, size=10, startRow=11, endRow=20, total=200, pages=20, list=Page{count=true, pageNum=2, pageSize=10, startRow=10, endRow=20, total=200, pages=20, reasonable=false, pageSizeZero=false}[cn.zj.logistics.pojo.User@31d0e481, cn.zj.logistics.pojo.User@3243b914, cn.zj.logistics.pojo.User@241e8ea6, cn.zj.logistics.pojo.User@542e560f, cn.zj.logistics.pojo.User@626c44e7, cn.zj.logistics.pojo.User@4dc8caa7, cn.zj.logistics.pojo.User@1d730606, cn.zj.logistics.pojo.User@3bcbb589, cn.zj.logistics.pojo.User@3b00856b, cn.zj.logistics.pojo.User@3016fd5e], prePage=1, nextPage=3, isFirstPage=false, isLastPage=false, hasPreviousPage=true, hasNextPage=true, navigatePages=8, navigateFirstPage=1, navigateLastPage=8, navigatepageNums=[1, 2, 3, 4, 5, 6, 7, 8]}

每页结果集 :Page{count=true, pageNum=2, pageSize=10, startRow=10, endRow=20, total=200, pages=20, reasonable=false, pageSizeZero=false}[cn.zj.logistics.pojo.User@31d0e481, cn.zj.logistics.pojo.User@3243b914, cn.zj.logistics.pojo.User@241e8ea6, cn.zj.logistics.pojo.User@542e560f, cn.zj.logistics.pojo.User@626c44e7, cn.zj.logistics.pojo.User@4dc8caa7, cn.zj.logistics.pojo.User@1d730606, cn.zj.logistics.pojo.User@3bcbb589, cn.zj.logistics.pojo.User@3b00856b, cn.zj.logistics.pojo.User@3016fd5e]

总记录数 :200

当前页 :2

每页条数 :10

下一页 :3

 

 

8.3.4.2. PageInfo源代码观察

PageInfo封装了所有分页信息数据,

 

package com.github.pagehelper;

 

import java.util.Collection;

import java.util.List;

 

/**

 * 对Page<E>结果进行包装

 * <p/>

 * 新增分页的多项属性,主要参考:http://bbs.csdn.net/topics/360010907

 *

 * @author liuzh/abel533/isea533

 * @version 3.3.0

 * @since 3.2.2

 * 项目地址 : http://git.oschina.net/free/Mybatis_PageHelper

 */

@SuppressWarnings({"rawtypes", "unchecked"})

public class PageInfo<T> extends PageSerializable<T> {

    //当前页

    private int pageNum;

    //每页的数量

    private int pageSize;

    //当前页的数量

    private int size;

 

    //由于startRow和endRow不常用,这里说个具体的用法

    //可以在页面中"显示startRow到endRow 共size条数据"

 

    //当前页面第一个元素在数据库中的行号

    private int startRow;

    //当前页面最后一个元素在数据库中的行号

    private int endRow;

    //总页数

    private int pages;

 

    //前一页

    private int prePage;

    //下一页

    private int nextPage;

 

    //是否为第一页

    private boolean isFirstPage = false;

    //是否为最后一页

    private boolean isLastPage = false;

    //是否有前一页

    private boolean hasPreviousPage = false;

    //是否有下一页

    private boolean hasNextPage = false;

    //导航页码数

    private int navigatePages;

    //所有导航页号

    private int[] navigatepageNums;

    //导航条上的第一页

    private int navigateFirstPage;

    //导航条上的最后一页

    private int navigateLastPage;

 

    public PageInfo() {

    }

 

    /**

     * 包装Page对象

     *

     * @param list

     */

    public PageInfo(List<T> list) {

        this(list, 8);

    }

}

 

8.3.4.2.1. PageSerializable源代码

PageSerializablePageInfo的父类,里面封装了结果集和总条数

 

package com.github.pagehelper;

 

import java.io.Serializable;

import java.util.List;

 

/**

 * @author liuzh

 */

public class PageSerializable<T> implements Serializable {

    private static final long serialVersionUID = 1L;

    //总记录数

    protected long    total;

    //结果集

    protected List<T> list;

 

    public PageSerializable() {

    }

 

    public PageSerializable(List<T> list) {

        this.list = list;

        if(list instanceof Page){

            this.total = ((Page)list).getTotal();

        } else {

            this.total = list.size();

        }

    }

 

    public static <T> PageSerializable<T> of(List<T> list){

        return new PageSerializable<T>(list);

    }

 

    public long getTotal() {

        return total;

    }

 

    public void setTotal(long total) {

        this.total = total;

    }

 

    public List<T> getList() {

        return list;

    }

 

    public void setList(List<T> list) {

        this.list = list;

    }

 

    @Override

    public String toString() {

        return "PageSerializable{" +

                "total=" + total +

                ", list=" + list +

                '}';

    }

}

 

 

8.3.5. 前端页面显示数据

后台页面已经把数据查询出来了,并且也有对应的条件搜索功能,接下来,就是需要把后台数据显示到前断页面 adminPage.jsp上了

 

前端页面使用ajax发送请求加载用户json数据,返回给前台,使用 BootstrapTables 表格插件显示数据

 

8.3.6. BootstrapTable插件学习使用

8.3.6.1. 使用前的准备工作

BootstrapTable 是在Bootstrap前端框架基础之上编写的一个数据显示工具,此工具显示基于html <table>标签,而且自带分页功能和条件搜索功能。我们开发者使用它后台只需要返回  BootstrapTable 插件所需要的json数据即可,总条数和结果集 (PageHelper 分页插件的分页对象中就包含了 total总条数list 结果集两个数据)。返回后台数据只需要返回把PageInfo对象当做json字符串返回即可

 

8.3.6.1.1. AdminController 的返回用户数据方法

package cn.zj.logistics.controller;

 

import java.util.List;

 

import org.apache.commons.lang3.StringUtils;

import org.apache.shiro.authz.annotation.RequiresPermissions;

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.RequestParam;

import org.springframework.web.bind.annotation.ResponseBody;

 

import com.github.pagehelper.PageHelper;

import com.github.pagehelper.PageInfo;

 

import cn.zj.logistics.pojo.User;

import cn.zj.logistics.pojo.UserExample;

import cn.zj.logistics.pojo.UserExample.Criteria;

import cn.zj.logistics.service.UserService;

 

 

@Controller

@RequestMapping("/admin")

public class AdminController {

 

@Autowired

private UserService userService;

 

/**

 * 用户数据列表,前台使用bootstrapTable插件发送ajax请求,返回的是json字符串(前台页面使用的 bootstrapTable插件进行数据显示和分页操作的)

 * @param keyword 关键字搜索(账号和真实姓名的模糊查询)

 * @param pageNum  页码(默认第一页)

 * @param pageSize 每页条数(默认10条数据)

 * @return

 */

@RequestMapping("/list.do")

@ResponseBody

public PageInfo<User> list(

String keyword,

@RequestParam(defaultValue = "1") Integer pageNum,

@RequestParam(defaultValue = "10") Integer pageSize) {

// 开始使用分页插件分页

 

PageHelper.startPage(pageNum, pageSize);

 

UserExample example = new UserExample();

 

if (StringUtils.isNotBlank(keyword)) {

 

//username账号模糊查询条件

Criteria criteria = example.createCriteria();

criteria.andUsernameLike("%" + keyword + "%");

//真实姓名模糊查询条件

Criteria criteria1 = example.createCriteria();

criteria1.andRealnameLike("%" + keyword + "%");

//或者关系

example.or(criteria1);

}

 

List<User> users = userService.selectByExample(example);

 

// 创建分页对象

PageInfo<User> pageInfo = new PageInfo<>(users);

return pageInfo;

}

 

 

//后台用户列表显示页面

@RequestMapping("/adminPage.do")

public String adminPage() {

 

return "adminPage";

}

}

 

 

 

 

8.3.6.1.2. 访问页面返回的json字符串

 

 

 

8.3.6.1.3. 格式化日期

 

 

8.3.6.1.4. 格式化后效果

 

 

 

8.3.7. adminPage.jsp页面开始使用插件

8.3.7.1. 插件下载学习地址

https://bootstrap-table.com/ 插件首页

https://bootstrap-table.com/docs/getting-started/introduction/ 文档学习地址

 

项目adminPage.jsp引入css样式文件和js文件,前提是项目必须先引入bootstrap前端框架的相关js和css文件,并且必须有jquery文件

 

 

 

8.3.7.2. Css样式

<link rel="stylesheet" type="text/css" href="lib/bootstrap-3.3.7-dist/css/bootstrap.css" />

<link rel="stylesheet" type="text/css" href="lib/bootstrap-table/bootstrap-table.css" />

8.3.7.3. Js文件

<script type="text/javascript" src="lib/jquery/1.11.3/jquery.min.js"></script>

<script type="text/javascript" src="lib/bootstrap-3.3.7-dist/js/bootstrap.js"></script>

<script type="text/javascript" src="lib/bootstrap-table/bootstrap-table.js"></script>

<script type="text/javascript" src="lib/bootstrap-table/locale/bootstrap-table-zh-CN.js"></script>

 

8.3.8. 页面表格table标签

<table id="userTable" class="table table-border table-bordered table-bg">

</table>

 

bootstrapTable的具体代码使用

$(function() {

$('#userTable').bootstrapTable({

url: '${ctx}/admin/list.do',//ajax请求的url地址

/*

ajax请求以后回调函数的处理

后台使用返回的PageInfo对象中的 结果 级的key是list,总条数是total

而前台bootstrapTable插件需要的数据的key叫做rows ,总条数也是叫做total

那么出现一个问题 : 总条数的key能对上,结果集对不上,就需要在ajax请求完成回调

responseHandler 这个函数方法处理一下

并且在自定义一个 json,rows做为key,返回json的 list作为值

total:还是total

这样才能满足 bootstrapTable插件数据的需要

*/

responseHandler: function(res) {

/*

res: 后台分页对象PageInfo返回对应的json对象

res.list : 结果集

res.total : 总记录数

*/

var data =  {rows: res.list,total: res.total};

return data;

},

pagination: true,

toolbar: "#toolbar",//顶部显示的工具条(添加和批量删除的)

contentType: 'application/x-www-form-urlencoded',//条件搜索的时候ajax请求给后台数据的数据类型(条件搜索post提交必须设置)

search: true,//是否显示搜索框

pageNumber: 1,//默认的页面 第一页

pageSize: 10,//默认的每页条数

//pageList:[10,25,50,100],//每页能显示的条数

sidePagination: "server",//是否是服务器分页,每次请求都是对应的10条数据,下一页发送ajax请求

paginationHAlign: 'right', //底部分页条

showToggle: true, //是否显示详细视图和列表视图的切换按钮

cardView: false, //是否显示详细视图

showColumns: true, //是否显示所有的列

showRefresh: true, //是否显示刷新按钮

columns: [ //表格显示数据对应的表头设置,

{ checkbox: true},//是否显示前台的复选框(多选)

/*

每列数据的表头的设置

filed:返回json数据对应数据的key

title:表头要显示的名

*/

{field: 'userId',title: '编号'},

{field: 'username',title: '账号'},

{field: 'realname',title: '真实名称'},

{field: 'createDate',title: '入职日期'},

//操作列的设置(删除,修改)

/*

formatter: 格式化这一行,回调一个函数

*/

{

field:'userId',

title:'操作',

align:'center',

formatter:operationFormatter

 

}],

/*发送请求的参数,

params: bootstrapTable的插件内部参数对象包含如下参数

limit, offset, search, sort

limit:每页条数

offset:每页的结束位置

search:搜索框对应的值

sort:排序

*/

queryParams: function(params) {

//此方法在用户分页或者搜索的时候回自动发送ajax请求调用,并把对应的参数传递给后台

return {

pageNum: params.offset / params.limit + 1, //页码

pageSize: params.limit, //页面大小

keyword: params.search

};

},

})

 

});

 

 

/*

操作行格式化对应的函数

value: 当前列的值

row:当前行的值

index:索引位置

*/

function operationFormatter(value,row,index){

console.log(row);

var html ='<a title="编辑" href="javascript:;" οnclick="admin_edit('+row.id+')" style="text-decoration:none"><i class="Hui-iconfont"></i></a>';

html += '<a title="删除" href="javascript:;" οnclick="admin_del(this,'+row.id+')" class="ml-5" style="text-decoration:none"><i class="Hui-iconfont"></i></a>';

return html;

}

8.3.9. 效果

 

 

8.3.10. Ajax请求观察

 

 

 

 

好了,通过上面的代码和各种工具,我们终于完成了后台管理员列表的查询分页以及搜索功能了

8.3.11. 本节总结

 

总结:在开发中要学会使用工具,插件不要重复造轮子,如果自己写这个操作,能写出来,但是时间太长,以为成本就高,企业注重的是商业效率,不是学术研究

 

本节掌握新知识点: MyBatis分页插件的使用和bootstaraptable表格分页组件的使用

8.4. 管理员删除

上述管理员列表做完以后,接下来相对简单一些的功能,就是删除功能

 

点击删除页面的每一行数据的删除按钮,弹出一个提示框,如果用户点击确定删除(删除以后再刷新页面),如果取消,不删除。

 

 

 

问题:如何弹出这么漂亮又美丽的提示框呢?

:使用弹窗插件 layer.js

 

8.4.1. Layer弹窗插件

插件官方地址

http://layer.layui.com/

 

下载弹窗插件

 

 

 

 

8.4.1.1. 页面引入弹窗插件

<script type="text/javascript" src="${ctx}/lib/layer/2.4/layer.js"></script>

 

8.4.1.2. 删除按钮点击触发事件,弹窗

 

/*管理员-删除*/

function admin_del(userId){

//layer的弹框使用

layer.confirm('确认要删除吗?',function(index){

//如果是点击的确认就会执行函数ajax请求

$.post("${ctx}/admin/delete.do",{"userId":userId},function(data){

//弹出一个提示消息

layer.msg(data.msg, {time: 1000, icon:6});

 

//如果是1说明删除成功,刷新表格 ,调用 bootstrap插件的刷新一下表格

if(data.code == 1){

refreshTable();

}

 

});

});

}

/**

 * 刷新表格方法,在删除,修改,添加成功以后调用

 */

function refreshTable(){

$("#userTable").bootstrapTable("refresh");

}

 

 

8.4.2. 后台消息对象创建

使用ajax进行dml操作以后,都会给前台返回一个操作状态,成功或者失败,并且返回操作成功或者失败消息,在这里专门封装一个消息对象,用于给前台ajax请求返回的状态判断

 

package cn.zj.logistics.mo;

 

/**

 * 消息对象,此对象主要用于ajax操作以后返回状态

 * 如插入数据,修改数据,删除数据以后,

 * 返回的json对象

 */

public class MessageObject {

private Integer code; //0 失败 1成功

private String msg;//消息

public Integer getCode() {

return code;

}

public void setCode(Integer code) {

this.code = code;

}

public String getMsg() {

return msg;

}

public void setMsg(String msg) {

this.msg = msg;

}

public MessageObject(Integer code, String msg) {

super();

this.code = code;

this.msg = msg;

}

@Override

public String toString() {

return "MessageObject [code=" + code + ", msg=" + msg + "]";

}

 

}

 

8.4.3. 后台AdminController代码

@Controller

@RequestMapping("/admin")

public class AdminController {

 

@Autowired

private UserService userService;

 

//根据用户id删除数据:返回 消息对象,对应的json给前端的ajax请求

@RequestMapping("/delete.do")

@ResponseBody

public MessageObject delete(Long userId) {

//调用业务层删除方法

int row = userService.deleteByPrimaryKey(userId);

 

MessageObject mo = null;

if(row == 1) {

mo = new MessageObject(1, "删除管理员成功");

}else {

mo = new MessageObject(0, "删除管理员失败");

}

return mo;

}

 

8.4.4. 删除效果

删除前

 

 

删除中

 

 

删除后瞬间

 

 

 

 

8.5. 管理员添加

完成删除删除管理员以后,接下载做添加操作

添加思路

 

  1. 点击添加按钮弹出一个模态框,模态框中显示添加管理对应的表单
  2. 在提交数据之前要校验表单合法性,账号在数据库中是否存在账号(账号是唯一的)
  3. 使用ajax提交数据
  4. 提交数据成功以后关闭模态框,并且父页面的表格bootstraptable 刷新

 

8.5.1. 添加用户管理员页面准备

点击页面的添加按钮弹出跳转到adminEdit.jsp页面,因为adminEdit.jsp页面在WEB-INF/view下面所以,用户发送请求先去控制器,再请求转发跳转到 adminEdit.jsp

 

使用 layer 弹窗插件模态方式弹出页面

 

8.5.1.1. 弹框代码

//弹出添加管理员页面方法

function adminAdd(){

//使用layer的弹框模态显示

 layer.open({

      type: 2,

      title: '添加管理员',

      shadeClose: true,

      shade: false,

      maxmin: true, //开启最大化最小化按钮

      area: ['800px', '400px'],

      content: '${ctx}/admin/edit.do'

    });

}

 

8.5.1.2. 后台AdminController代码

@Controller

@RequestMapping("/admin")

public class AdminController {

 

@Autowired

private UserService userService;

//引入角色Service,在添加和修改的时候页面显示所有的角色信息

@Autowired

private RoleService roleService;

 

// 根据用户id删除数据:返回 消息对象,对应的json给前端的ajax请求

@RequestMapping("/edit.do")

public String edit(Model m) {

 

//查询出所有的角色,并共享到用户编辑页面

RoleExample example = new RoleExample();

List<Role> roles = roleService.selectByExample(example);

//共享角色信息

m.addAttribute("roles", roles);

 

return "adminEdit";

}

}

 

 

8.5.1.3. 显示效果

 

 

 

8.5.2. 添加管理员表单提交与验证

添加页面准备好了以后,接下来就是用户提交表单和提交前的表单校验了

 

8.5.2.1. 表单验证-jquery.validate插件

在提交表单之前肯定要做一定的校验的,如用户信息不能为空,账号最少几位,数据库中是否存在等等。

 

我们使用一个专业的强大表单校验插件,基于Jquery编写的

 

Jquery.validate 表单校验插件

https://jqueryvalidation.org/ 官网

 

https://www.runoob.com/jquery/jquery-plugin-validate.html 在线学习地址

 

8.5.2.2. 引入js代码

<script type="text/javascript" src="${ctx}/lib/jquery.validation/1.14.0/jquery.validate.js"></script> 

<script type="text/javascript" src="${ctx}/lib/jquery.validation/1.14.0/validate-methods.js"></script> 

<script type="text/javascript" src="${ctx}/lib/jquery.validation/1.14.0/messages_zh.js"></script> 

 

 

8.5.2.3. 校验操作

正常一个表单点击提交submit按钮会提交,如果把这个表单交给jquery.validate以后此表单就不会提交了,而是校验成功以后开发者需要自己重新编写提交表单数据操作

基本实例规则如下

 

/*

$("#表单id").validate({

rules(规则):{

表单元素名称1(name的值):{

规则1:值,

规则2:值

 

},

表单元素名称2(name的值):{

规则1:值,

规则2:值

 

}

},

messages(错误消息):{

表单元素名称1(name的值):{

规则1:错误提示信息,

规则2:错误提示信息

 

},

表单元素名称2(name的值):{

规则1:错误提示信息,

规则2:错误提示信息

 

}

},

//表单校验成功以后执行方法

submitHandler:function(form){

//表单校验成功以后可以发送ajax请求提交数据到后台了

}

 

})

*/

 

前台js校验代码

//开启校验

$("#userForm").validate({

rules:{//校验规则

username:{//账号

required:true,//不能为空

minlength:4,//最小4位数

maxlength:12,//最大12位数

remote: {//使用ajax接受返回的是boolean类型true可用,false不可用

    url: "${ctx}/admin/checkUsername.do",     //后台处理程序

    type: "post",               //数据发送方式

    dataType: "json",           //接受数据格式   

    data: {                     //要传递的数据

        username: function() {

            return $("#username").val();

        }

    }

}

},

realname:{//真实名称

required:true//不能为空

},

password:{//密码

required:true,//不能为空

},

password2:{//确认密码

required:true,//不能为空

equalTo: "#password"//确认必须和密码相等

},

roleId:{//角色

min:1,//最小必须为1

},

},

messages:{//校验失败的提示小实习

username:{//账号

required:"账号不能为空",//不能为空

minlength:"账号最少4位数",//最小4位数

maxlength:"账号最大12位数",//最大12位数

remote:"账号已经存在请换一个账号"

},

realname:{//真实名称

required:"真实姓名不能为空"//不能为空

},

password:{//密码

required:"密码不能为空",//不能为空

},

password2:{//确认密码

required:"确认密码不能为空",//不能为空

equalTo: "确认密码必须和密码相同"//确认必须和密码相等

},

roleId:{//角色

min:"请选择一个角色"

},

},

//校验成功以后知悉的回调函数  form参数 表单

submitHandler:function(form){

console.log(form);

}

});

 

 

后台服务器检查用户是否存在代码

 

@Controller

@RequestMapping("/admin")

public class AdminController {

 

@Autowired

private UserService userService;

//引入角色Service,在添加和修改的时候页面显示所有的角色信息

@Autowired

private RoleService roleService;

 

//jquery.validate 插件的ajax请求检查用户名是否存在

@RequestMapping("/checkUsername.do")

@ResponseBody

public boolean checkUsername(String username) {

 

UserExample example = new UserExample();

Criteria criteria = example.createCriteria();

criteria.andUsernameEqualTo(username);

List<User> users = userService.selectByExample(example );

if(users.size() == 1) {

//返回false说明账号存在不能使用

return false;

}else {

//账号不存在可以使用

return true;

}

}

}

 

 

校验效果

 

 

 

 

8.5.3. 使用ajax提交表单数据

jquery.validate submitHandler 回调函数中提交开发者自己提交表单数据到后台进行插入操作

$(function(){

//开启校验

$("#userForm").validate({

rules:{//校验规则

username:{//账号

required:true,//不能为空

minlength:4,//最小4位数

maxlength:12,//最大12位数

remote: {//使用ajax接受返回的是boolean类型

    url: "${ctx}/admin/checkUsername.do",     //后台处理程序

    type: "post",               //数据发送方式

    dataType: "json",           //接受数据格式   

    data: {                     //要传递的数据

        username: function() {

            return $("#username").val();

        }

    }

}

},

realname:{//真实名称

required:true//不能为空

},

password:{//密码

required:true,//不能为空

},

password2:{//确认密码

required:true,//不能为空

equalTo: "#password"//确认必须和密码相等

},

roleId:{//角色

min:1,//最小必须为1

},

},

messages:{//校验失败的提示小实习

username:{//账号

required:"账号不能为空",//不能为空

minlength:"账号最少4位数",//最小4位数

maxlength:"账号最大12位数",//最大12位数

remote:"账号已经存在请换一个账号"

},

realname:{//真实名称

required:"真实姓名不能为空"//不能为空

},

password:{//密码

required:"密码不能为空",//不能为空

},

password2:{//确认密码

required:"确认密码不能为空",//不能为空

equalTo: "确认密码必须和密码相同"//确认必须和密码相等

},

roleId:{//角色

min:"请选择一个角色"

},

},

//校验成功以后知悉的回调函数  form参数 表单

submitHandler:function(form){

//设置表单的action提交地址

form.action = "${ctx}/admin/insert.do";

 

//调用表单的ajax提交方式

$(form).ajaxSubmit(function(data){

//如果是1说明添加成功,刷新表格 ,调用 bootstrap插件的刷新一下表格

if(data.code == 1){

layer.msg(data.msg,{icon:1,time:2000},function(){

//获取当前弹出层索引

var index = parent.layer.getFrameIndex(window.name);

//让父层页面重新刷新一下(重新加载一下)

window.parent.refreshTable();

//关闭当前弹出层

parent.layer.close(index);

});

 

}

 

});

}

});

});

 

8.5.4. 后台插入代码

@Controller

@RequestMapping("/admin")

public class AdminController {

 

@Autowired

private UserService userService;

// 引入角色Service,在添加和修改的时候页面显示所有的角色信息

@Autowired

private RoleService roleService;

 

// 新增后台管理员方法

@RequestMapping("/insert.do")

@ResponseBody

public MessageObject insert(User user) {

// 调用业务层删除方法

int row = userService.insert(user);

 

MessageObject mo = null;

if (row == 1) {

mo = new MessageObject(1, "添加管理员成功");

} else {

mo = new MessageObject(0, "添加管理员失败");

}

return mo;

}

}

8.6 管理员修改

修改和添加页面差不多,逻辑也是一样,界面使用同一个界面,只是最终提交表单的时候是走的修改逻辑,页面需要数据回显

 

修改思路

  1. 点击添加按钮弹出一个模态框,模态框中显示添加管理对应的表单
  2. 在提交数据之前要校验表单合法性,账号不能修改,只能修改其他信息
  3. 使用ajax提交数据
  4. 提交数据成功以后关闭模态框,并且父页面的表格bootstraptable 刷新

 

8.6.1. 点击修改弹出模态并回显数据

8.6.1.1. 前台js代码

/**

 * 修改用户

   userId:用户id,查询对应的用户数据用于回显

 */

function adminEdit(userId){

//使用layer的弹框模态显示

 layer.open({

     type: 2,

     title: '添加管理员',

     shadeClose: true,

     shade: false,

     maxmin: true, //开启最大化最小化按钮

     area: ['800px', '400px'],

     content: '${ctx}/admin/edit.do?userId='+userId

   });

}

 

8.6.1.2. 后台代码

添加和修改走的是一个页面跳转逻辑,但是修改的时候需要接受userId查询出来并回显

 

//编辑

@RequestMapping("/edit.do")

public String edit(Long userId,Model m) {

System.out.println("userId:"+userId);

//如果userId 不等于空,说明是编辑操作,需要查询出对应用户id的信息并且回显过去

if(userId !=null) {

//根据用户id查询用户信息

User user = userService.selectByPrimaryKey(userId);

//共享用户信息

m.addAttribute("user", user);

}

 

// 查询出所有的角色,并共享到用户编辑页面

RoleExample example = new RoleExample();

List<Role> roles = roleService.selectByExample(example);

// 共享角色信息

m.addAttribute("roles", roles);

 

return "adminEdit";

}

 

8.6.2. 校验并提交数据

注意:因为新增和修改走的是同一个校验逻辑,那么表单校验代码是一样的,但是如何区分什么时候是添加什么时候是修改呢,因为修改的时候不需要校验账号的,此时 jsp的标签库就起到作用了,因为jspjstl标签在页面的任何地方都可以使用,包括在js中一样使用,我们在修改的时候,后台会把对应的 user对象共享过来,我们只需要用 jstl+el表达式判断有没有user对象,有user对象走修改操作,没有就走新增操作

//开启校验

$("#userForm").validate({

rules:{//校验规则

<c:if test="${empty user}">

username:{//账号

required:true,//不能为空

minlength:4,//最小4位数

maxlength:12,//最大12位数

remote: {//使用ajax接受返回的是boolean类型

    url: "${ctx}/admin/checkUsername.do",     //后台处理程序

    type: "post",               //数据发送方式

    dataType: "json",           //接受数据格式   

    data: {                     //要传递的数据

        username: function() {

            return $("#username").val();

        }

    }

}

},

</c:if>

realname:{//真实名称

required:true//不能为空

},

password:{//密码

required:true,//不能为空

},

password2:{//确认密码

required:true,//不能为空

equalTo: "#password"//确认必须和密码相等

},

roleId:{//角色

min:1,//最小必须为1

},

},

messages:{//校验失败的提示消息

<c:if test='${empty user}'>

username:{//账号

required:"账号不能为空",//不能为空

minlength:"账号最少4位数",//最小4位数

maxlength:"账号最大12位数",//最大12位数

remote:"账号已经存在请换一个账号"

},

</c:if>

realname:{//真实名称

required:"真实姓名不能为空"//不能为空

},

password:{//密码

required:"密码不能为空",//不能为空

},

password2:{//确认密码

required:"确认密码不能为空",//不能为空

equalTo: "确认密码必须和密码相同"//确认必须和密码相等

},

roleId:{//角色

min:"请选择一个角色"

},

},

//校验成功以后知悉的回调函数  form参数 表单

submitHandler:function(form){

//设置表单的action提交地址:如果添加走  insert

<c:if test='${user == null}'>

form.action = "${ctx}/admin/insert.do";

</c:if>

 

//设置表单的action提交地址:如果修改走  update

<c:if test='${user != null}'>

form.action = "${ctx}/admin/update.do";

</c:if>

 

//调用表单的ajax提交方式

$(form).ajaxSubmit(function(data){

//如果是1说明添加成功,刷新表格 ,调用 bootstrap插件的刷新一下表格

if(data.code == 1){

layer.msg(data.msg,{icon:1,time:2000},function(){

//获取当前弹出层索引

var index = parent.layer.getFrameIndex(window.name);

//让父层页面重新刷新一下(重新加载一下)

window.parent.refreshTable();

//关闭当前弹出层

parent.layer.close(index);

});

 

}

 

});

}

});

 

8.6.3. 后台修改代码

@Controller

@RequestMapping("/admin")

public class AdminController {

 

@Autowired

private UserService userService;

// 引入角色Service,在添加和修改的时候页面显示所有的角色信息

@Autowired

private RoleService roleService;

 

// 修改后台管理员方法

@RequestMapping("/update.do")

@ResponseBody

public MessageObject update(User user) {

// 调用业务层删除方法

int row = userService.updateByPrimaryKeySelective(user);

 

MessageObject mo = null;

if (row == 1) {

mo = new MessageObject(1, "修改管理员成功");

} else {

mo = new MessageObject(0, "修改管理员失败");

}

return mo;

}

}

8.6.4. 效果

 

 

 

8.7. 总结

在用户管理这个功能里面,需要掌握一些前端相关的插件

MyBatisHelper 后台MyBatis分页插件

bootstraptable 表格插件,

Jquery.validate 表单校验插件,

layer 弹框插件

 

有了这些插件,可以快速的完成页面的数据操作,当然所有的这些操作开发者可以完全自己开发,但是我们不需要重复造轮子

转载于:https://www.cnblogs.com/qq2267711589/p/11196938.html

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值