FundingOnline02——精进ssm 实战项目(参照尚硅谷 357SpringSecurity未做)

 

----------------------------------------------------------------------------------------------------------------- 

前置要求:Spring、SpringMVC、Mybatis、Maven

问题、报错、解决等都和做项目过程中遇到的bug等有关。

--------------------------------------------------------------------------------------

尚筹网项目-目录1

01-尚硅谷-尚筹网-简介

 

02-环境搭建

1 环境搭建总体目标

2 创建工程

3 创建数据库和数据库表

4 基于Maven的MyBatis逆向工程

5 父工程依赖管理

6 Spring整合MyBatis

7 日志系统

8 声明式事务

9 表述层配置 (视频中为10)

10 SpringMVC环境下的Ajax请求

11 异常映射

13 声明一个类管理常量

14 引入前端静态资源 

15 创建后台管理员登录页面(后台首页)  

16 使用layer弹层组件 

17 进化system-error.jsp 

 

04-后台管理系统-管理员登录

1 目标

2 思路

3 代码 

4 抽取后台主页面公共部分

5 登录状态的检查

 

05-后台管理系统-管理员维护

1 任务清单

2 分页

3 关键词查询

4 单条删除

5  新增

6 更新

 

06-后台管理系统-RBAC权限控制模型

 

-Ajax的同步和异步请求

1 异步的工作方式

2 同步的工作方式

3 本质

 

07-后台管理系统-角色维护

1 角色分页操作

2 角色维护

3 角色保存操作

4 角色更新操作

5 角色删除操

 

08-后台管理系统-菜单维护

1 树形结构基础知识介绍

2 菜单维护:页面显示树形结构

3 菜单维护:添加子节点

4 菜单维护:更新节点

5 菜单维护:删除节点

 

- @RestController = @Controller + @ResponseBody

 

09-后台管理系统-分配

1 权限控制

2 给Admin分配Role

3 给Role分配Auth

4 给Menu分配Auth

-----------------------------------------------------------------------------------------

对项目进行知识储备↓

 

SpringSecurity

1 SpringSecrity框架用法介绍

2 权限管理过程中的相关概念

3 权限管理的主流框架

4 使用配置类代替XML

5 HelloWorld 工程创建步骤

6 在HelloWorld基础上加入SpringSecurity

7  SpringSecurity操作实验

 

09-后台管理系统-分配

1 SpringSecurity回顾

2 项目中加入SpringSecurity

。。。。。。

-----------------------------------------------------------------------------------------

尚筹网项目-目录2 

 

2 尚筹网会员系统总目标

3 会员系统架构

4 parent工程

5 搭建环境约定

6 eureka工程

7 entity工程

(entity项目和Admin-entity项目一样 失效)

 之后只做对尚筹网官方文档的补充

redis

 

-----------------------------------------------------------------------------------------

报错

星图(众筹项目知识体系)

阿里支付宝开放平台

---------------------------------------------------------------------------------------------------------------------

 01-尚硅谷-尚筹网-简介

后台管理员使用单一架构开发,前台会员系统使用分布式架构开发。

管理员↓

左右对应

 

项目架构:

 

 

 

02-环境搭建

1 环境搭建总体目标

  

 

 

2 创建工程

2.1 项目架构图

 

parent——父工程(去聚合)

webui、component、entity——子工程(被模块)

 

2.2 工程创建计划

atcrowdfunding01-admin-parent

atcrowdfunding02-admin-webui

atcrowdfunding03-admin-component

atcrowdfunding04-admin-entity

atcrowdfunding05-common-util

atcrowdfunding06-common-reverse

 

 

 

2.3 Maven工程和Maven模块

 

父工程创建(maven project):

 

archetypeCatalog

internal

 

子工程创建(maven module):

 

 

 

最终结果展示:

 

 

 

 

2.4 建立工程之间的依赖关系

 

 

 

 (开始SSM整合,步骤:Spring+MyBatis→Spring+SpringMVC)

开始Spring+MyBatis整合:

3 创建数据库和数据库表

3.1 物理建模

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

3.2 创建数据库

CREATE DATABASE fundingonline_study CHARACTER SET utf8; 

 

3.3 创建管理员数据库表


use fundingonline_study;
DROP TABLE IF EXISTS `t_admin`;

CREATE TABLE
`t_admin` (
`id` int(11) NOT NULL AUTO_INCREMENT, # 主键
`login_acct` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, # 登录账号
`user_pswd` char(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, # 登录密码
`user_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, # 昵称
`email` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, # 邮箱地址
`create_time` char(19) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, # 创建时间
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `login_acct`(`login_acct`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 251 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

 

 

 

 

 4 基于Maven的MyBatis逆向工程

4.1 pom配置

 

 

 
<!--依赖MyBatis核心包-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>

<!--
控制 Maven 在构建过程中相关配置 -->
<build>
<finalName>atcrowdfunding006-common-reverse</finalName>
<!-- 构建过程中用到的插件 -->
<plugins>
<!-- 具体插件,逆向工程的操作是以构建过程中插件形式出现的 -->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.0</version>
<!-- 插件的依赖 -->
<dependencies>
<!-- 逆向工程的核心依赖 -->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.2</version>
</dependency>
<!-- 数据库连接池 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.2</version>
</dependency>
<!-- MySQL 驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.8</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>

 

4.2 generatorConfig.xml(逆向工程的文件,只需复制粘贴不必背)

<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<!-- mybatis-generator:generate -->
<context id="atguiguTables" targetRuntime="MyBatis3">
<commentGenerator>
<!-- 是否去除自动生成的注释 true:;false:-->
<property name="suppressAllComments" value="true" />
</commentGenerator>
<!--数据库连接的信息:驱动类、连接地址、用户名、密码 -->
<jdbcConnection
driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/project_crowd?useUnicode=true&amp;characterEncoding=utf8"
userId="root"
password="pq21ce">
</jdbcConnection>
<!-- 默认 false,把 JDBC DECIMAL NUMERIC 类型解析为 Integer,为 true 时把
JDBC DECIMAL
NUMERIC 类型解析为 java.math.BigDecimal -->
<javaTypeResolver>
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<!-- targetProject:生成 Entity 类的路径 -->
<javaModelGenerator targetProject=".\src\main\java"
targetPackage="com.atguigu.crowd.entity">
<!-- enableSubPackages:是否让 schema 作为包的后缀 -->
<property name="enableSubPackages" value="false" />
<!-- 从数据库返回的值被清理前后的空格 -->
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!-- targetProject:XxxMapper.xml 映射文件生成的路径 -->
<sqlMapGenerator targetProject=".\src\main\java"
targetPackage="com.atguigu.crowd.mapper">
<!-- enableSubPackages:是否让 schema 作为包的后缀 -->
<property name="enableSubPackages" value="false" />
</sqlMapGenerator>
<!-- targetPackageMapper 接口生成的位置 -->
<javaClientGenerator type="XMLMAPPER"
targetProject=".\src\main\java"
targetPackage="com.atguigu.crowd.mapper">
<!-- enableSubPackages:是否让 schema 作为包的后缀 -->
<property name="enableSubPackages" value="false" />
</javaClientGenerator>
<!-- 数据库表名字和我们的 com.atguigu.crowd.entity 类对应的映射指定 -->
<!-- <table tableName="t_auth" domainObjectName="Auth" />-->
<!-- <table tableName="t_admin" domainObjectName="Admin" />-->
<table tableName="t_role" domainObjectName="Role" />

</context>
</generatorConfiguration>

4.3 执行逆向生成操作的Maven命令

mybatis-generator:generate

注意选择要逆向的数据库表:

 

 

 

4.4 逆向工程生成的资源各归各位

归类好的情形:

出现问题:

①import com.stguigu.crowd.entity.Admin;语句爆红

②import org.apache.ibatis.annotations.Param;语句爆红

问题①解决方法:

 

问题②解决办法:

导包→mybatis

<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>

 

5 父工程依赖管理

5.1 版本声明

 

注:spring4.3.20 匹配 json2.9.8(需要自己配置json版本原因:SpringMVC用到了json却没有配置json,所以手动配置json版本时要注意版本匹配是否匹配问题)

 

5.2 依赖管理

<dependencyManagement>
<dependencies>
<!-- Spring 依赖 -->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-orm -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<!--<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>-->
<version>${atguigu.spring.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${atguigu.spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${atguigu.spring.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2</version>
</dependency>
<!-- 数据库依赖 -->
<!-- MySQL 驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.3</version>
</dependency>
<!-- 数据源 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.31</version>
</dependency>
<!-- MyBatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.2.8</version>
</dependency>
<!-- MyBatis 与 Spring 整合 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.2.2</version>
</dependency>
<!-- MyBatis 分页插件 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>4.0.0</version>
</dependency>
<!-- 日志 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.7</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<!-- 其他日志框架的中间转换包 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId> 爆红解决方法:下载该jar包,手动导入jar文件
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
<version>1.7.25</version>
</dependency>
<!-- Spring 进行 JSON 数据转换依赖 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.8</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.8</version>
</dependency>
<!-- JSTL 标签库 -->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- junit 测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- 引入 Servlet 容器中相关依赖 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<!-- JSP 页面使用的依赖 -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1.3-b06</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.5</version>
</dependency>

</dependencies>
</dependencyManagement>

 

 

5.3 依赖信息来源

Maven jar包网站:mvnrepository.com

 

 

 

6 Spring整合MyBatis

6.1目标

 

6.2 思路

核心目标:将adminMapper装配进IOC

 

 

 

 

 

6.3 操作清单

在子工程中加入搭建环境所需要的具体依赖

准备jdbc.properties

创建Spring配置文件专门配置Spring和Mybatis整合相关

在Spring配置文件中加载jdbc.properties属性文件

配置数据源

测试从数据源中获取数据库连接

配置SqlSessionFactoryBean

  装配数据源

  指定xxxMapper.xml配置文件的位置

  指定mybatis全局配置文件的位置(可选)

配置MapperScannerConfigurer

测试是否可以装配xxxMappper接口并通过这个家口操作数据库

 

6.4 操作步骤详解

6.4.1 在子工程中加入搭建环境所需要的具体依赖

子工程:选择component工程。原因:具体依赖和component工程相关。

<dependency>
<groupId>com.atguigu.crowd</groupId>
<artifactId>atcrowdfunding005-common-util</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<!-- Spring 依赖 -->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-orm -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
</dependency>
<!-- MySQL 驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- 数据源 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<!-- MyBatis 与 Spring 整合 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
</dependency>
<!-- MyBatis 分页插件 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
</dependency>
<!-- 日志 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<!-- 其他日志框架的中间转换包 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
</dependency>


<!-- Spring 进行 JSON 数据转换依赖 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>

<!-- JSTL 标签库 -->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>

 

 

 

6.4.2 jdbc.properties

jdbc.user=root
jdbc.password=pq21ce
jdbc.url=jdbc:mysql://localhost:3306/fundingonline_study?useUnicode=true&characterEncoding=UTF-8
jdbc.driver=com.mysql.jdbc.Driver

 

6.4.3 mybatis-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

</configuration>

 

6.4.4 spring-persist-mybatis.xml

Spring具体配置:

1、配置数据源、测试

<!--加载外部属性文件-->
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
<!--配置数据源-->
<!-- 配置数据源 com.alibaba.druid.pool.DruidDataSource-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<!-- 连接数据库的用户名 -->
<property name="username" value="${jdbc.user}"/>
<!-- 连接数据库的密码 -->
<property name="password" value="${jdbc.password}"/>
<!-- 目标数据库的 URL 地址 -->
<property name="url" value="${jdbc.url}"/>
<!-- 数据库驱动全类名 -->
<property name="driverClassName" value="${jdbc.driver}"/>
</bean>
创建Spirng的Junit测试类
package com.atguigu.crowd;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

/**
* @author 123
* date 2022-09-18
*/
// 在类上标记必要的注解,spring整合junit
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:spring-persist-mybatis.xml"})
public class CrowdTest {

@Autowired
private DataSource dataSource;

@Test
public void testConnection() throws SQLException {
Connection connection = dataSource.getConnection();
System.out.println(connection);
}
}

 

 

<!--junit和spring-test无法传递 那个module需要则在那个module导入-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>

 

2、配置SqlSessionFactoryBean

<!-- 配置SqlSessionFactoryBean整合Mybatis -->
<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--指定Mybatis全局配置文件的位置-->
<property name="configLocation" value="classpath:mybatis-config.xml"></property>
<!--指定Mapper.xml的位置-->
<property name="mapperLocations" value="classpath:mybatis/mapper/*Mapper.xml"></property>
<!--装配数据源-->
<property name="dataSource" ref="dataSource"></property>
<!-- 配置插件-->
<property name="plugins">
<array>
<bean class="com.github.pagehelper.PageHelper">
<property name="properties">
<props>
<!--配置数据库方言,告诉PageHelper当前使用的数据库-->
<prop key="dialect">mysql</prop>
<!--配置页码的合理化修正:
总页数只有20页,
若用户输入跳转至50页,则显示第20页;
若用户输入跳转至-5页,则显示第1页。
-->
<prop key="reasonable">true</prop>
</props>
</property>
</bean>
</array>
</property>
</bean>
<!-- 配置MapperScannerConfigurer来扫描Mapper接口所在的包-->
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.atguigu.crowd.mapper"></property>
</bean>
package com.atguigu.crowd;

import com.atguigu.crowd.entity.Admin;
import com.atguigu.crowd.mapper.AdminMapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

/**
* @author 123
* date 2022-09-18
*/
// 在类上标记必要的注解,spring整合junit
// 指定Spirng给Junit提供的运行器类
@RunWith(SpringJUnit4ClassRunner.class)
// 加载Spring配置文件的注解
@ContextConfiguration(locations = {"classpath:spring-persist-mybatis.xml"})
public class CrowdTest {

@Autowired
private AdminMapper adminMapper;

@Test
public void testInertAdmin() {
Admin admin = new Admin(null, "tom", "123123", "汤姆", "tom@qq.com", null);
int count = adminMapper.insert(admin);
System.out.println("受影响的行数:" + count);
}
}

测试报错:

解决:将tom改成其他值即可,因为值重复了、

 

 

总览:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
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-4.3.xsd">
<!--加载外部属性文件-->
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
<!--配置数据源-->
<!-- 配置数据源 com.alibaba.druid.pool.DruidDataSource-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<!-- 连接数据库的用户名 -->
<property name="username" value="${jdbc.user}"/>
<!-- 连接数据库的密码 -->
<property name="password" value="${jdbc.password}"/>
<!-- 目标数据库的 URL 地址 -->
<property name="url" value="${jdbc.url}"/>
<!-- 数据库驱动全类名 -->
<property name="driverClassName" value="${jdbc.driver}"/>
</bean>
<!-- 配置SqlSessionFactoryBean整合Mybatis -->
<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--指定Mybatis全局配置文件的位置-->
<property name="configLocation" value="classpath:mybatis-config.xml"></property>
<!--指定Mapper.xml的位置-->
<property name="mapperLocations" value="classpath:mybatis/mapper/*Mapper.xml"></property>
<!--装配数据源-->
<property name="dataSource" ref="dataSource"></property>
<!-- 配置插件-->
<property name="plugins">
<array>
<bean class="com.github.pagehelper.PageHelper">
<property name="properties">
<props>
<!--配置数据库方言,告诉PageHelper当前使用的数据库-->
<prop key="dialect">mysql</prop>
<!--配置页码的合理化修正:
总页数只有20页,
若用户输入跳转至50页,则显示第20页;
若用户输入跳转至-5页,则显示第1页。
-->
<prop key="reasonable">true</prop>
</props>
</property>
</bean>
</array>
</property>
</bean>
<!-- 配置MapperScannerConfigurer来扫描Mapper接口所在的包-->
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.atguigu.crowd.mapper"></property>

</bean>
</beans>

 

 

7 日志系统

7.1重要意义

 

 

7.2 技术选型

7.2.1 总体介绍

 

 

7.2.2 不同日志系统的整合

 

 

 

 

 

 

 

 

 

7.3 具体操作

7.3.1 初始状态

 

 

 

7.3.2 加入slf4j+logback

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.7</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>

代码不变,日志情况是:

 

 

 

 

 

8.3.3 我们主动打印的日志

// 1、获取Logger对象,这里传入的Class对象就是当前打印日志的类
Logger logger = LoggerFactory.getLogger(CrowdTest.class);

// 2、根据不同日志级别打印日志
logger.debug("debug level!!!");
logger.debug("debug level!!!");
logger.debug("debug level!!!");

logger.info("info lecel!!!");
logger.warn("warn level!!!");
logger.error("error levell!!");

 

 

效果:

 

 

7.3.4 更换框架日志系统

第一步:排除commons-logging

 

component:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>

webui:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>

此时,会报错(一开始使用的依赖就已经添加了排除commons-logging的代码且添加了jcl-over-slf4j包,所以不会报错):

 

 

当添加了jcl-over-slf4j依赖时,报错解除:

<!-- 其他日志框架的中间转换包 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.25</version>
</dependency>

 

7.3.5 logback配置文件

 

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true">
<!-- 指定日志输出的位置 -->
<appender name="STDOUT"
class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<!-- 日志输出的格式 -->
<!-- 按照顺序分别是:时间、日志级别、线程名称、打印日志的类、日志主体
内容、换行 -->
<pattern>[%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger]
[%msg]%n</pattern>
</encoder>
</appender>
<!-- 设置全局日志级别。日志级别按顺序分别是:DEBUG、INFO、WARN、ERROR -->
<!-- 指定任何一个日志级别都只打印当前级别和后面级别的日志。 -->
<root level="INFO">
<!-- 指定打印日志的 appender,这里通过“STDOUT”引用了前面配置的 appender -->
<appender-ref ref="STDOUT" />
</root>
<!-- 根据特殊需求指定局部日志级别 -->
<logger name="com.atguigu.crowd.mapper" level="DEBUG"/>
</configuration>

结果展示:

 

 

8 声明式事务

8.1 目标

8.2 思路

8.2.1 选择合适的事务管理器

 

 

8.2.2 配置文件结构

 

 

8.3 代码

8.3.1 创建Spring专门管理事务的配置文件

 

添加名称空间

 

8.3.2 配置自动扫描的包

 

<context:component-scan base-package="com.atguigu.crowd.service"/>

 

8.3.3 具体配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
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-4.3.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">

<!--配置自动扫描的包,主要是为了把Service扫描到IOC容器中-->
<context:component-scan base-package="com.atguigu.crowd.service"/>
<!--配置事务管理器-->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--装配数据源-->
<!--这个ref="dataSource"是spring-persist-mybatis.xml中数据源id名,写了ref="dataSource"后会提示:dataSource无法找到,但是只要运行项目时,spring-persist-mybatis.xml和spring-persist-tx.xml这俩文件都运行了,它就能找到-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--配置事务切面-->
<aop:config>
<!--考虑需要整合SpringSecurity,避免UserDetailsService加入事务控制,让切入点表达式定位到ServiceImpl而不是Service-->
<aop:pointcut id="txPointcut" expression="execution(* *..*ServiceImpl.*(..))"/>
<!--将切入点表达式和事务通知关联起来-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"></aop:advisor>
</aop:config>
<!--配置事务的通知-->
<tx:advice id="txAdvice" transaction-manager="txManager">
<!--配置事务的属性-->
<tx:attributes>
<!--查询方法:配置只读属性,让数据库知道这是一个查询操作,能够进行一定优化-->
<tx:method name="get*" read-only="true"/>
<tx:method name="find*" read-only="true"></tx:method>
<tx:method name="query*" read-only="true"></tx:method>
<tx:method name="count*" read-only="true"></tx:method>
<!--增删改方法:配置事务传播行为,回滚异常-->
<!--propagation:
REQUIRES:受其他事务回滚影响。表示当前方法必须工作在事务中,如果当前线程上没有已经开启的事务,则自己开新事务;如果已经有了,那么就使用这个已有的事务,但是用别人的事务,若别人回滚,他自己也会回滚。
REQUIRES_NEW:不受其他事务回滚的印影响。表示当前方法必须工作在事务中,如果当前线程上没有已经开启的事务,则自己开新事务;如果已经有了,也在自己开启的事务中运行。-->
<!--rollback-for:配置事务方法针对什么样的异常回滚 默认:运行时回滚 建议:编译时异常和运行时异常都回滚-->
<tx:method name="save*" propagation="REQUIRES_NEW" rollback-for="java.lang.Exception"></tx:method>
<tx:method name="update*" propagation="REQUIRES_NEW" rollback-for="java.lang.Exception"></tx:method>
<tx:method name="remove*" propagation="REQUIRES_NEW" rollback-for="java.lang.Exception"></tx:method>
<tx:method name="batch*" propagation="REQUIRES_NEW" rollback-for="java.lang.Exception"></tx:method>
</tx:attributes>

</tx:advice>
</beans>

 

 8.4 测试

package com.atguigu.crowd.service.impl;

import com.atguigu.crowd.entity.Admin;
import com.atguigu.crowd.mapper.AdminMapper;
import com.atguigu.crowd.service.api.AdminService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;


/**
* @author 123
* date 2022-01-05
*/
@Service
public class AdminServiceImpl implements AdminService {

@Autowired
private AdminMapper adminMapper;

@Override
public void saveAdmin(Admin admin) {
adminMapper.insert(admin);
}

}
crowdtest.java中:

@ContextConfiguration(locations = {"classpath:spring-persist-mybatis.xml", "classpath:spring-persist-tx.xml"})   // 来源spring-test依赖



@Autowired
private AdminService adminService;

@Test
public void testTx() {
Admin admin = new Admin(null, "m", "123123", "汤姆", "tom@qq.com", null);
adminService.saveAdmin(admin);
}

 

 

8.5 注意

 

 

 

9 表述层配置 

9.1目标

 

9.2 思路

  web.xml和Spring配置关系:

 

 

 

9.3 web.xml配置

 

 

 

9.3.1 配置ContextLoaderListener

<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-persist-*.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

 

9.3.2 配置CharacterEncodingFilter

<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>
<init-param>
<param-name>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

 

9.3.3 配置DispatcherServlet

<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-web-mvc.xml,classpath:spring-persist-*.xml</param-value>
</init-param>
<!--Servlet默认生命周期中,创建对象是第一次接受到请求时-->
<!--DispatcherServlet创建对象后有大量的框架初始化工作,不适合在第一次请求时来做-->
<!--这样设置就是为了让DispatcherServlet在web应用启动时创建对象、初始化-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<!--1.url-pattern 表示拦截所有请求-->
<!--<url-pattern>/</url-pattern>-->
<!--2.配置请求扩展名
优点:
1.css js png等等静态资源完全不经过Springmvc,不需要特殊处理
2.实现伪静态效果。表面上看起来访问一个html文件这样的静态资源,但实际上是经过java代码运算过的 作用:给黑客入侵增加难度 利于SEO优化(让搜索引擎更容易找到我们项目)
缺点:
不符合RESTFul风格。-->
<url-pattern>*.html</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>*.json</url-pattern>
</servlet-mapping>

 

 

常见错误:

 

 9.4 代码:SpringMVC配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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-4.3.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd">

<context:component-scan base-package="com.atguigu.crowd.mvc"/>

<mvc:annotation-driven/>

<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value=""/>
<property name="suffix" value=".jsp"/>
</bean>

</beans>
 

 

9.5 测试

9.5.1 在webui工程添加依赖

<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<scope>provided</scope>
</dependency>

 9.5.2 创建index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<!DOCTYPE html>
<html lang="zh-CN">
<body>

<a href="${pageContext.request.contextPath}/test/ssm.html">测试SSM整合环境</a>

</body>
</html>

 

 

 

 

9.6 base标签 

<base href="http://${pageContext.request.serverName}:${pageContext.request.serverPort}${pageContext.request.contextPath}/"/>  <%--${pageContext.request.contextPath}前面自带斜杠--%> <%--绝对路径:${pageContext.request.contextPath}--%>

 

10 SpringMVC环境下的Ajax请求

10.1 建立意识

 

10.2 常用注解

 

 

<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>

 

10.3 @RequestBody注解的使用

 

10.3.1 引入jQuery

 

<script type="text/javascript" src="jquery/jquery-2.1.1.min.js"></script>

10.3.2 发送数组到服务器端:方案一

jQuery代码

$(function () {
$("#btn1").click(function () {
$.ajax({
"url": "send/array/one.html", // 请求目标资源的地址
"type": "post", // 请求方式
"data": { // 要发送的请求参数
"array": [5,8,12]
},
"dataType": "text", // 如何对待服务器端返回的数据
"success": function (response) { // 服务器端处理请求成功后调用的回调函数,response是响应体数据
alert(response);
},
"error": function (response) { // // 服务器端处理请求失败后调用的回调函数,response是响应体数据
alert(response);
}
});
})
});

handler代码

@ResponseBody
@RequestMapping("send/array/one.html")
public String testReceiveArrayOne(@RequestParam("array[]") List<Integer> array) {
for (Integer number: array) {
System.out.println("number=" + number);
}
return "target";
}

 

10.3.3 发送数组到服务器端:方案二

jQuery代码

$("#btn2").click(function () {
$.ajax({
"url": "send/array/two.html", // 请求目标资源的地址
"type": "post", // 请求方式
"data": { // 要发送的请求参数
"array[0]": 5,
"array[1]": 8,
"array[2]": 12
},
"dataType": "text", // 如何对待服务器端返回的数据
"success": function (response) { // 服务器端处理请求成功后调用的回调函数,response是响应体数据
alert(response);
},
"error": function (response) { // // 服务器端处理请求失败后调用的回调函数,response是响应体数据
alert(response);
}
});
});

handler代码

@ResponseBody
@RequestMapping("/send/array/two.html")
public String testReceiveArrayTwo(ParamData paramData) {
List<Integer> array = paramData.getArray();
for (Integer number : array) {
System.out.println("number=" + number);
}
return "success";
}

ParamData代码

package com.atguigu.crowd.entity;

import java.util.List;

public class ParamData {

private List<Integer> array;

public ParamData() {
}

public ParamData(List<Integer> array) {
this.array = array;
}

public List<Integer> getArray() {
return array;
}

public void setArray(List<Integer> array) {
this.array = array;
}

@Override
public String toString() {
return "ParamData{" +
"array=" + array +
'}';
}
}

 

10.3.4 发送数组到服务器端:方案三

在浏览器开发者工具中看到的请求体:

 不同于法二,是request payload不是form data了

 

jQuery代码:

$("#btn3").click(function () {
// 准备好要发送到服务器端的数组
var array = [5,8,12];
console.log(array.length);

// 将JSON数组转换为JSON字符串
var requestBody = JSON.stringify(array);
// "['5','8','12']"
console.log(requestBody.length);

$.ajax({
"url": "send/array/three.html", // 请求目标资源的地址
"type": "post", // 请求方式
"data": requestBody, // 请求体
"contentType": "application/json;charset=UTF-8", // 设置请求体的内容类型,告诉服务器本次请求的请求体是JSON数据
"dataType": "text", // 如何对待服务器端返回的数据
"success": function (response) { // 服务器端处理请求成功后调用的回调函数,response是响应体数据
alert(response);
},
"error": function (response) { // // 服务器端处理请求失败后调用的回调函数,response是响应体数据
alert(response);
}
});
});

handler代码

@ResponseBody
@RequestMapping("/send/array/three.html")
public String testReceiveArrayThree(@RequestBody List<Integer> array) {
for (Integer number : array) {
logger.info("number=" + number);
}
return "success";
}

 

 

 

10.4 发送复杂的对象

10.4.1 代码:

package org.example.entity;

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

/**
* @author 123
* date 2023-01-07
*/
public class Student {

private Integer stuId;
private String stuName;
private Address address;
private List<Subject> subjectList;
private Map<String, String> map;

public Student() {
}

public Student(Integer stuId, String stuName, Address address, List<Subject> subjectList, Map<String, String> map) {
this.stuId = stuId;
this.stuName = stuName;
this.address = address;
this.subjectList = subjectList;
this.map = map;
}

public Integer getStuId() {
return stuId;
}

public void setStuId(Integer stuId) {
this.stuId = stuId;
}

public String getStuName() {
return stuName;
}

public void setStuName(String stuName) {
this.stuName = stuName;
}

public Address getAddress() {
return address;
}

public void setAddress(Address address) {
this.address = address;
}

public List<Subject> getSubjectList() {
return subjectList;
}

public void setSubjectList(List<Subject> subjectList) {
this.subjectList = subjectList;
}

public Map<String, String> getMap() {
return map;
}

public void setMap(Map<String, String> map) {
this.map = map;
}

@Override
public String toString() {
return "Student{" +
"stuId=" + stuId +
", stuName='" + stuName + '\'' +
", address=" + address +
", subjectList=" + subjectList +
", map=" + map +
'}';
}
}
package org.example.entity;

/**
* @author 123
* date 2023-01-07
*/
public class Address {

private String province;
private String city;
private String street;

public Address() {
}

public Address(String province, String city, String street) {
this.province = province;
this.city = city;
this.street = street;
}

public String getProvince() {
return province;
}

public void setProvince(String province) {
this.province = province;
}

public String getCity() {
return city;
}

public void setCity(String city) {
this.city = city;
}

public String getStreet() {
return street;
}

public void setStreet(String street) {
this.street = street;
}

@Override
public String toString() {
return "Address{" +
"province='" + province + '\'' +
", city='" + city + '\'' +
", street='" + street + '\'' +
'}';
}
}
package org.example.entity;

/**
* @author 123
* date 2023-01-07
*/
public class Subject {

private String subjectName;
private String subjectScore;

public Subject() {
}

public Subject(String subjectName, String subjectScore) {
this.subjectName = subjectName;
this.subjectScore = subjectScore;
}

public String getSubjectName() {
return subjectName;
}

public void setSubjectName(String subjectName) {
this.subjectName = subjectName;
}

public String getSubjectScore() {
return subjectScore;
}

public void setSubjectScore(String subjectScore) {
this.subjectScore = subjectScore;
}

@Override
public String toString() {
return "Subject{" +
"subjectName='" + subjectName + '\'' +
", subjectScore='" + subjectScore + '\'' +
'}';
}
}

TestHandler.java中

@ResponseBody
@RequestMapping("/send/compose/object.html")
public String testReceiveComposeObject(@RequestBody Student student) {
logger.info(student.toString());
return "success";
}

 index.jsp中

$("#btn4").click(function () {

// 准备要发送的数据
var student = {
"stuId": 5,
"stuName": "tom",
"address": {
"province": "广东",
"city": "深圳",
"street": "后端"
},
"subjectList": [
{
"subjectName": "JavaSE",
"subjectScore": 99
},{
"subjectName": "SSM",
"subjectScore": 90
}
],
"map": {
"k1": "v1",
"k2": "v2"
}
};

// 将JSON对象转换为JSON字符串
var requestBody = JSON.stringify(student); // 辨析payload时要加这步

// 发送ajax请求
$.ajax({
"url":"send/compose/object.html",
"type":"post",
"data":requestBody,
"contentType":"application/json;charset=UTF-8",
"dataType":function (response) {
alert(response);
},
"error":function (response) {
alert(response);
}
});

});




<button id="btn4">Send Compose Object</button>

 10.4.2 结果:

 

10.5 对Ajax请求返回的结果进行规范

 10.5.1 代码

package org.example.util;

/**
* @author 123
* date 2023-01-12
*
* 统一项目中AJAX请求返回的结果,也可以用于分布式架构中各个模块间调用时返回统一类型
*/
public class ResultEntity<T> {

public static final String SUCCESS ="SUCCESS";
public static final String FAILED = "FAILED";

// 用来封装当前请求处理的结果是成功还是失败
private String result;

// 请求处理失败时返回的错误消息
private String message;

// 要返回的数据
private T data;

//请求处理成功且不需要返回数据时使用的工具方法
public static <Type> ResultEntity<Type> successWithoutData() {
return new ResultEntity<Type>(SUCCESS, null, null);
}
//请求处理成功且需要数据时使用的工具方法 data:要返回的数据
public static <Type> ResultEntity<Type> successWithData(Type data) {
return new ResultEntity<Type>(SUCCESS, null, data);
}
//请求处理失败后使用的工具方法 message:失败的错误消息
public static <Type> ResultEntity<Type> failed(String message) {
return new ResultEntity<Type>(FAILED, message, null);
}

public ResultEntity() {
}

public ResultEntity(String result, String message, T data) {
this.result = result;
this.message = message;
this.data = data;
}

public String getResult() {
return result;
}

public void setResult(String result) {
this.result = result;
}

public String getMessage() {
return message;
}

public void setMessage(String message) {
this.message = message;
}

public T getData() {
return data;
}

public void setData(T data) {
this.data = data;
}

@Override
public String toString() {
return "ResultEntity{" +
"result='" + result + '\'' +
", message='" + message + '\'' +
", data=" + data +
'}';
}
}
TestHandler.java中:

@ResponseBody
@RequestMapping("/send/compose/object.json")
public ResultEntity<Student> testReceiveComposeObject(@RequestBody Student student) {
logger.info(student.toString());
return ResultEntity.successWithData(student); // 将查询到的Student对象封装到ResultEntity中返回
}

index.jsp → $("#btn4") 中:

 

"url":"send/compose/object.json",

"dataType":"json",

 

 

10.5.2 结果

 

报错:406

 

解决:将.html改为.json

 

 11 异常映射

11.1 目标

  

11.2 思路

 

 

  

 

 

 

note:

<!--
@RequestMapping("/xxx/xxx.html")
public String xxx(){
return "target";
}
如此,无方法体的controller可以直接配置xml代替该方法:
-->
<mvc:view-controller path="/xxx/xxx.html" view-name="target"/>

 

 

11.3 代码

11.3.1 基于XML的异常映射

  

 

<!--view-Controller-->
<!--<mvc:view-controller path="" view-name=""></mvc:view-controller>-->
<!--基于基于XML的异常映射-->
<bean id="simpleMappingExceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<!--配置异常类型和具体视图页面的对应关系-->
<property name="exceptionMappings">
<props>
<!--key属性指定异常全类名-->
<!--标签体中写对应的视图,这个值要拼前后缀得到具体路径-->
<prop key="java.lang.Exception">system-error</prop>
</props>
</property>
</bean>

 

11.3.2 判断请求类型的工具方法

  

 

  加依赖:

    

<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>

 

 

请求类型 判断的依据:

开发者模式下

 

创建工具类,编写工具方法:

/*
* 判断当前请求是否为ajax请求
* true:是 false:不是
* */
public static boolean judgeRequestType(HttpServletRequest request) {

// 1.获取请求消息头
String acceptHeader = request.getHeader("Accept");
String xRequestHeader = request.getHeader("X-Requested-With");

// 2.判断
return (acceptHeader != null && acceptHeader.contains("application/json"))

||

(xRequestHeader != null && xRequestHeader.equals("XMLHttpRequest"));
}

 

11.3.3 基于注解的异常映射(需先判断请求类型为普通还是ajax)

 代码:

package org.example.mvc.config;

import com.google.gson.Gson;
import com.sun.org.apache.xpath.internal.operations.Mod;
import org.example.util.CrowdUtil;
import org.example.util.ResultEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
* @author 123
* date 2023-01-13
*
* 表示当前是一个基于注解异常处理器类
*/
@ControllerAdvice
public class CrowdExceptionResolver {

// 数学异常
@ExceptionHandler(value = ArithmeticException.class)
public ModelAndView resolveMathException(
ArithmeticException exception,
HttpServletRequest request,
HttpServletResponse response) throws IOException {

String viewName = "system-error";

return commonResolve(viewName, exception, request, response);
}

// 一个异常类型对应一个方法
// @ExceptionHandler注解 将一个具体异常类型与一个方法关联起来
// 空指针异常
@ExceptionHandler(value = NullPointerException.class)
public ModelAndView resolveNullPointerException(
NullPointerException exception,
HttpServletRequest request,
HttpServletResponse response) throws IOException {

// 异常处理完成后要去的页面
String viewName = "system-error";

return commonResolve(viewName, exception, request, response);
}


// 通用方法,用于提取重复的代码
private ModelAndView commonResolve(

String viewName,

// 实际捕获到的异常类型
Exception exception,

// 当前请求对象
HttpServletRequest request,

// 当前响应对象
HttpServletResponse response
) {

//获取异常消息
String message = exception.getMessage();

//1.判断当前请求的类型
boolean judgeResult = CrowdUtil.judgRequestType(request);
//2.如果是一个ajax请求
if (judgeResult) {
//3.创建ResultEntity对象
ResultEntity<Object> failed = ResultEntity.failed(message);
//4.创建Gson对象
Gson gson = new Gson();
//5.将对象转换为Json字符串
String json = gson.toJson(failed);
//6.将Json字符串作为响应体返回给浏览器
try {
response.getWriter().write(json);
} catch (IOException e) {
e.printStackTrace();
}
//7.由于上面已经通过原生response对象返回了响应,所以不提供ModelAndView对象
return null;
}
//不是ajax请求
ModelAndView modelAndView = new ModelAndView();
//1.将exception对象存入模型
modelAndView.addObject("exception", exception);
//2.设置对应视图
modelAndView.setViewName(viewName);
//3.返回ModelAndView对象
return modelAndView;
}


}
TestHandler.java中:



@ResponseBody
@RequestMapping("/send/compose/object.json")
public ResultEntity<Student> testReceiveComposeObject(@RequestBody Student student, HttpServletRequest request) {

boolean judgeResult = CrowdUtil.judgRequestType(request);
logger.info("judgeResult="+judgeResult);

String a = null;
System.out.println(a.length());

logger.info(student.toString());
return ResultEntity.successWithData(student); // 将查询到的Student对象封装到ResultEntity中返回
}

 

 13 声明一个类管理常量

public class CrowdConstant {

public static final String MESSAGE_LOGIN_FAILED = "抱歉!账号密码错误!请重新输入!";

public static final String MESSAGE_LOGIN_ACCT_ALREADY_IN_USE = "抱歉!这个账号已经被使用了";

public static final String MESSAGE_ACCESS_FORBIDEN = "请登录以后再访问!";

public static final String ATTR_NAME_EXCEPTION = "exception";

}

 

14 引入前端静态资源

 

 

 

 15 创建后台管理员登录页面(后台首页)

15.1 创建admin-login.jsp

 

<%--
Created by IntelliJ IDEA.
User:
Date: 2023/1/15
Time: 9:59
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="keys" content="">
<meta name="author" content="">
<base href="http://${pageContext.request.serverName}:${pageContext.request.serverPort}${pageContext.request.contextPath}/">
<link rel="stylesheet" href="bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="css/font-awesome.min.css">
<link rel="stylesheet" href="css/login.css">
<script src="jquery/jquery-2.1.1.min.js"></script>
<script src="bootstrap/js/bootstrap.min.js"></script>
<style>

</style>
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="container">
<div class="navbar-header">
<div><a class="navbar-brand" href="index.html" style="font-size:32px;">众筹网</a></div>
</div>
</div>
</nav>

<div class="container">

<form action="admin/do/login.html" method="post" class="form-signin" role="form">
<%--<p>${SPRING_SECURITY_LAST_EXCEPTION }</p>
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>--%>

<h2 class="form-signin-heading">
<i class="glyphicon glyphicon-log-in"></i> 管理员登录
</h2>
<p>${requestScope.execption.message }</p>
<div class="form-group has-success has-feedback">
<input type="text" name="loginAcct" class="form-control" id="inputSuccess3" placeholder="请输入登录账号" autofocus>
<span class="glyphicon glyphicon-user form-control-feedback"></span>
</div>
<div class="form-group has-success has-feedback">
<input type="text" name="userPswd" class="form-control" id="inputSuccess4" placeholder="请输入登录密码" style="margin-top:10px;">
<span class="glyphicon glyphicon-lock form-control-feedback"></span>
</div>
<button type="submit" class="btn btn-lg btn-success btn-block">登录</button>
</form>
</div>

</body>
</html>

 

15.2 跳转到登录页面

<mvc:view-controller path="/admin/to/login/page.html" view-name="admin-login"/>

 

16 使用layer弹层组件

16.1 加入layer库文件和样式文件

 

16.2 在页面上引入layer环境

<script type="text/javascript" src="layer/layer.js"></script>

 16.3 使用layer弹层提示框

index.jsp中:

 

layer.msg("Layer的弹框");

 

17 进化system-error.jsp

<%--
Created by IntelliJ IDEA.
User: 郎贵雯
Date: 2023/1/15
Time: 9:59
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="keys" content="">
<meta name="author" content="">
<base href="http://${pageContext.request.serverName}:${pageContext.request.serverPort}${pageContext.request.contextPath}/">
<link rel="stylesheet" href="bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="css/font-awesome.min.css">
<link rel="stylesheet" href="css/login.css">
<script src="jquery/jquery-2.1.1.min.js"></script>
<script src="bootstrap/js/bootstrap.min.js"></script>
<script type="text/javascript">
$(function () {
$("button").click(function () {
//相当于后退
window.history.back();
});
});
</script>
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="container">
<div class="navbar-header">
<div><a class="navbar-brand" href="index.html" style="font-size:32px;">众筹网</a></div>
</div>
</div>
</nav>

<div class="container">

<h2 class="form-signin-heading" style="text-align: center;">
<i class="glyphicon glyphicon-log-in"></i> 众筹网系统消息
</h2>
<%--
requestScope对应的是存放request域数据的Map
requestScope.exception == request.getAttribute("exception")
requestScope.exception.message == exception.getMessage()
--%>
<h3 style="text-align: center;">${requestScope.exception.message }</h3>
<button style="width:150px;margin: 50px auto 0px auto" class="btn btn-lg btn-success btn-block">点我返回上一步</button>

</div>

</body>
</html>

 

 

04-后台管理系统-管理员登录

1 目标

 

2 思路

 

3 代码

3.1 创建工具方法执行MD5加密

/**
* 对明文字符串进行MD5加密
* @param source 传入的明文字符串
* @return 加密的结果
*/
public static String md5(String source) {

// 1.判断source是否有效
if(source == null || source.length() == 0) {

// 2.若非有效的字符串抛出异常
throw new RuntimeException(CrowdConstant.MESSAGE_STRING_INVALIDATE);
}

try {
// 3.获取MessageDigest对象
String algorithm = "md5";
MessageDigest messageDigest = MessageDigest.getInstance(algorithm);

// 4.获取明文字符串对应的字节数组
byte[] input = source.getBytes();

// 5.执行加密
byte[] output = messageDigest.digest(input);

// 6.创建BigInteger对象
int signum = 1;
BigInteger bigInteger = new BigInteger(signum, output);

// 7.按照16进制将bigInteger的值转换为字符串
int radix = 16;
String encoded = bigInteger.toString(radix).toUpperCase();

return encoded;

} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}

return null;
}

 

MD5测试:

package org.example.test;

import org.example.util.CrowdUtil;
import org.junit.Test;

/**
* @author 123
* date 2023-01-15
*/
public class StringTest {

@Test
public void testMd5() {
String source = "123123";
String encoded = CrowdUtil.md5(source);
System.out.println(encoded);
}
}

 

3.2  自定义异常——登陆失败

package org.example.exception;

/**
* @author 123
* date 2023-01-15
*
* 自定义异常:登录失败后抛出异常
*/
public class LoginFailedException extends RuntimeException {

private static final long serialVersionUID = 1L;

public LoginFailedException() {
}

public LoginFailedException(String message) {
super(message);
}

public LoginFailedException(String message, Throwable cause) {
super(message, cause);
}

public LoginFailedException(Throwable cause) {
super(cause);
}

public LoginFailedException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}

 

3.3 异常处理器类中添加登录失败的处理

@ExceptionHandler(value = LoginFailedException.class)
public ModelAndView resolveLoginFailedException(
LoginFailedException exception,
HttpServletRequest request,
HttpServletResponse response) throws IOException {

// 异常处理完成后要去的页面
String viewName = "admin-login";

return commonResolve(viewName, exception, request, response);
}

 

3.4 在登录页面显示异常消息

<h2 class="form-signin-heading">
<i class="glyphicon glyphicon-log-in"></i> 管理员登录
</h2>
<p>${requestScope.execption.message }</p>

 

3.5 handler方法

package org.example.mvc.handler;

import org.example.constant.CrowdConstant;
import org.example.entity.Admin;
import org.example.service.api.AdminService;
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 javax.servlet.http.HttpSession;

/**
* @author 123
* date 2023-01-15
*/
@Controller
public class AdminHandler {

@Autowired
private AdminService adminService;

@RequestMapping("/admin/do/login.html")
public String doLogin(
@RequestParam("loginAcct") String loginAcct,
@RequestParam("userPswd") String userPswd,
HttpSession session
) {

// 调用Service方法执行登录检查
// 这个方法若能够返回admin对象说明登录成功,若账号、密码不正确则会抛出异常
Admin admin = adminService.getAdminByLoginAcct(loginAcct, userPswd);

// 将登录成功返回的admin对象存入Session域
session.setAttribute(CrowdConstant.ATTR_NAME_LOGIN_ADMIN, admin);

return "admin-main";
}

}

 

3.6 service方法

AdminService.class中:

 

Admin getAdminByLoginAcct(String loginAcct, String userPswd);

AdminServiceImpl中:

 

@Override
public Admin getAdminByLoginAcct(String loginAcct, String userPswd) {

// 1.根据登录账号查询admin对象
// 创建AdminExample对象
AdminExample adminExample = new AdminExample();
// 创建Criteria对象
AdminExample.Criteria criteria = adminExample.createCriteria();
// 在Criteria对象中封装查询条件
criteria.andLoginAcctEqualTo(loginAcct);
// 调用AdminMapper的方法执行查询
List<Admin> admins = adminMapper.selectByExample(adminExample);
// 2.查询Admin对象是否为空
if (admins == null && admins.size() == 0) {
throw new LoginFailedException(CrowdConstant.MESSAGE_LOGIN_FAILED);
}
if (admins.size() > 1) {
throw new RuntimeException(CrowdConstant.MESSAGE_SYSTEM_ERROR_LOGIN_NOT_UNIQUE);
}
Admin admin = admins.get(0);
// 3.如果admin对象为空则抛出异常
if (admin == null) {
throw new LoginFailedException(CrowdConstant.MESSAGE_LOGIN_FAILED);
}
// 4.如果admin对象不为null则将数据库密码从Admin对象中取出
String userPswdDB = admin.getUserPswd();
// 5.将表单提交的明文密码进行加密
String userPswdForm = CrowdUtil.md5(userPswd);
// 6.对密码进行比较
if (!Objects.equals(userPswdDB, userPswdForm)) {
// 7.如果比较结果是不一致则抛出异常
throw new LoginFailedException(CrowdConstant.MESSAGE_LOGIN_FAILED);
}
// 8.如果一致则返回Admin对象
return admin;
}

 

登录测试↓

登录页面:

登录成功页面:

登录失败页面:

 

 

???登录失败提示消息未完成,理应为如下情况:

 

 

 

3.7 补充

3.7.1 前往后台主页面的方式调整

 

@RequestMapping("/admin/do/login.html")
public String doLogin(
@RequestParam("loginAcct") String loginAcct,
@RequestParam("userPswd") String userPswd,
HttpSession session
) {

// 调用Service方法执行登录检查
// 这个方法若能够返回admin对象说明登录成功,若账号、密码不正确则会抛出异常
Admin admin = adminService.getAdminByLoginAcct(loginAcct, userPswd);

// 将登录成功返回的admin对象存入Session域
session.setAttribute(CrowdConstant.ATTR_NAME_LOGIN_ADMIN, admin);

return "redirect:/admin/to/main/page.html";
}

需要给目标地址配置view-controller:

<mvc:view-controller path="/admin/to/main/page.html" view-name="admin-main"/>

 

3.7.2 退出登录

<li><a href="admin/do/logout.html"><i class="glyphicon glyphicon-off"></i> 退出系统</a></li>

 

@RequestMapping("/admin/do/logout.html")
public String doLogout(HttpSession session) {
//强制session失效
session.invalidate();
return "redirect:/admin/to/login/page.html";
}

 

4 抽取后台主页面公共部分

4.1 创建公共部分JSP

4.2 抽取后效果

 

 

 

<%--
Created by IntelliJ IDEA.
User:
Date: 2023/1/15
Time: 14:59
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<!DOCTYPE html>
<html lang="zh-CN">
<%@include file="/WEB-INF/include-head.jsp" %>

<body>
<%@include file="/WEB-INF/include-nav.jsp"%>
<div class="container-fluid">
<div class="row">
<%@include file="/WEB-INF/include-sidebar.jsp"%>
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
.........
</div>
</div>
</div>

<%--<h1>临时主页面</h1>
${sessionScope.loginAdmin.userName }--%>

</body>
</html>

 

4.3 创建JSP模板

模板内容是:

众筹网后台JSP模板:

<%--
Created by IntelliJ IDEA.
User: 郎贵雯
Date: 2023/1/15
Time: 14:59
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<!DOCTYPE html>
<html lang="zh-CN">
<%@include file="/WEB-INF/include-head.jsp" %>

<body>
<%@include file="/WEB-INF/include-nav.jsp"%>
<div class="container-fluid">
<div class="row">
<%@include file="/WEB-INF/include-sidebar.jsp"%>
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">

</div>
</div>
</div>

<%--<h1>临时主页面</h1>
${sessionScope.loginAdmin.userName }--%>

</body>
</html>

 

5 登录状态的检查

5.1 目标

  

 5.2 思路

  

5.3 代码

5.3.1 创建自定义异常

  

package org.example.exception;
/*
* 表示用户没有登录就访问受保护资源时抛出的异常
*
* */
public class AccessForbiddenException extends RuntimeException{
private static final long serialVersionUID = 8730782733834090098L;

public AccessForbiddenException() {
super();
}

public AccessForbiddenException(String s) {
super(s);
}

public AccessForbiddenException(String s, Throwable throwable) {
super(s, throwable);
}

public AccessForbiddenException(Throwable throwable) {
super(throwable);
}

protected AccessForbiddenException(String s, Throwable throwable, boolean b, boolean b1) {
super(s, throwable, b, b1);
}
}

 

5.3.2 创建拦截器类

  

package org.example.mvc.interceptor;

import org.example.constant.CrowdConstant;
import org.example.entity.Admin;
import org.example.exception.AccessForbiddenException;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

/**
* @author 123
* date 2023-01-21
*/
public class LoginInterceptor extends HandlerInterceptorAdapter {

@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {

// 1.通过request对象获取Session对象
HttpSession session = httpServletRequest.getSession();

// 2.尝试从Session域中获取Admin对象
Admin admin = (Admin)session.getAttribute(CrowdConstant.ATTR_NAME_LOGIN_ADMIN);

// 3.判断admin对象是否为空
if(admin == null) {

// 4.抛出异常
throw new AccessForbiddenException(CrowdConstant.MESSAGE_ACCESS_FORBIDEN);

}

// 5.若Admin对象不为null,则返回true就行
return true;
}

}

5.3.3 注册拦截器类

  

<!--注册拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<!--mvc:mapping配置要拦截的资源-->
<!--/*对应一层路径 /**对应多层路径-->
<mvc:mapping path="/**"/>

<!--mvc:exclude-mapping配置不拦截的资源-->
<mvc:exclude-mapping path="/admin/to/login/page.html"/>
<mvc:exclude-mapping path="/admin/do/login.html"/>
<mvc:exclude-mapping path="/admin/do/logout.html"/>

<!--配置拦截器类-->
<bean class="org.example.mvc.interceptor.LoginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>

 

拦截器-测试结果:

 

 

 

05-后台管理系统-管理员维护

1  任务清单

  

2 分页

2.1 目标

  

2.2 思路

 

2.3 代码

2.3.1 引入PageHelper

  确认是否加入了依赖:

<!-- MyBatis 分页插件 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
</dependency>

   在SQLSessionFactoryBean配置MyBatis插件:

    

<!-- 配置SqlSessionFactoryBean整合Mybatis -->
<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--指定Mybatis全局配置文件的位置-->
<property name="configLocation" value="classpath:mybatis-config.xml"></property>
<!--指定Mapper.xml的位置-->
<property name="mapperLocations" value="classpath:mybatis/mapper/*Mapper.xml"></property>
<!--装配数据源-->
<property name="dataSource" ref="dataSource"></property>
<!-- 配置插件-->
<property name="plugins">
<array>
<!-- 配置PageHelper插件 -->
<bean class="com.github.pagehelper.PageHelper">
<property name="properties">
<props>
<!--配置数据库方言,告诉PageHelper当前使用的数据库-->
<prop key="dialect">mysql</prop>
<!--配置页码的合理化修正-->
<prop key="reasonable">true</prop>
</props>
</property>
</bean>
</array>
</property>
</bean>

  

2.3.2 AdminMapper中编写SQL语句

  

 

 

<select id="selectAdminByKeyword" resultMap="BaseResultMap">
select id, login_acct, user_pswd, user_name, email, create_time
from t_admin
where
login_acct like concat("%",#{keyword},"%") or user_name like concat("%",#{keyword},"%") or email like concat("%",#{keyword},"%")
</select>

 

2.3.3 AdminMapper接口中声明方法

  

 

 

List<Admin> selectAdminByKeyword(String keyword);

2.3.4 AdminService方法

  

 

 

AdminService.class

PageInfo<Admin> getPageInfo(String keyword, Integer pageNum, Integer pageSize);

 AdminServiceImpl.class

@Override
public PageInfo<Admin> getPageInfo(String keyword, Integer pageNum, Integer pageSize) {
// 1.调用PageHelper的静态方法开启分页功能
PageHelper.startPage(pageNum, pageSize);
// 2.执行查询
List<Admin> list = adminMapper.selectAdminByKeyword(keyword);
// 3.封装到PageInfo对象中
return new PageInfo<>(list);
}

 

2.3.5 AdminHandler方法

  

@RequestMapping("/admin/get/page.html")
public String getPageInfo(
@RequestParam(value = "keyword", defaultValue = "") String keyword,
@RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum,
@RequestParam(value = "pageSize", defaultValue = "5") Integer pageSize,
ModelMap modelMap) {
// 调用service方法获取PageInfo对象
PageInfo<Admin> pageInfo = adminService.getPageInfo(keyword, pageNum, pageSize);
// 将PageInfo对象存入模型
modelMap.addAttribute(CrowdConstant.ATTR_NAME_PAGE_INFO, pageInfo);
return "admin-page";
}

 

2.3.6 显示主体页

  

 

 

<%--
Created by IntelliJ IDEA.
User:
Date: 2023/1/15
Time: 14:59
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html lang="zh-CN">
<%@include file="/WEB-INF/include-head.jsp" %>

<body>
<%@include file="/WEB-INF/include-nav.jsp" %>
<div class="container-fluid">
<div class="row">
<%@include file="/WEB-INF/include-sidebar.jsp" %>
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title"><i class="glyphicon glyphicon-th"></i> 数据列表</h3>
</div>
<div class="panel-body">
<form action="admin/get/page.html" class="form-inline" role="form" style="float:left;">
<div class="form-group has-feedback">
<div class="input-group">
<div class="input-group-addon">查询条件</div>
<input name="keyword" class="form-control has-success" type="text" placeholder="请输入查询条件">
</div>
</div>
<button type="submit" class="btn btn-warning"><i class="glyphicon glyphicon-search"></i> 查询
</button>
</form>
<button type="button" class="btn btn-danger" style="float:right;margin-left:10px;"><i
class=" glyphicon glyphicon-remove"></i> 删除
</button>
<a style="float: right;" href="admin/to/add/page.html" class="btn btn-primary"><i class="glyphicon glyphicon-plus"></i> 新增
</a>
<br>
<hr style="clear:both;">
<div class="table-responsive">
<table class="table table-bordered">
<thead>
<tr>
<th width="30">#</th>
<th width="30"><input type="checkbox"></th>
<th>账号</th>
<th>名称</th>
<th>邮箱地址</th>
<th width="100">操作</th>
</tr>
</thead>
<tbody>
<c:if test="${empty requestScope.pageInfo.list }">
<td colspan="6" align="center">抱歉!没有查询到您要的数据!</td>
</c:if>
<c:if test="${!empty requestScope.pageInfo.list }">
<c:forEach items="${requestScope.pageInfo.list }" var="admin" varStatus="myStatus">
<tr>
<td>${myStatus.count }</td>
<td><input type="checkbox"></td>
<td>${admin.loginAcct }</td>
<td>${admin.userName }</td>
<td>${admin.email }</td>
<td>
<button type="button" class="btn btn-success btn-xs">
<i class="glyphicon glyphicon-check"></i>
</button>
<button type="button" class="btn btn-primary btn-xs">
<i class="glyphicon glyphicon-pencil"></i>
</button>
<button type="button" class="btn btn-danger btn-xs">
<i class="glyphicon glyphicon-remove"></i>
</button>
</td>
</tr>
</c:forEach>
</c:if>
</tbody>
<tfoot>
<tr>
<td colspan="6" align="center">
<ul class="pagination">
<li class="disabled"><a href="#">上一页</a></li>
<li class="active"><a href="#">1 <span class="sr-only">(current)</span> </a> </li>
<li><a href="#">2</a> </li>
<li><a href="#">3</a> </li>
<li><a href="#">4</a> </li>
<li><a href="#">5</a> </li>
<li><a href="#">下一页</a> </li>
</ul>
</td>
</tr>
</tfoot>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>

 

2.3.7 在页面上使用Pagination实现页码导航条

  导入Pagination环境(3个):

  

 

  

  在有需要的页面上进行引入(注意先后顺序,Pagination要在jQuery的后面):

 

<%@include file="/WEB-INF/include-head.jsp" %>  <!-- head.jsp中包含jQuery环境-->

<link rel="stylesheet" href="css/pagination.css"/>
<script type="text/javascript" src="jquery/jquery.pagination.js"></script>

 

  HTML代码所需准备

  使用Pagination要求的div标签替换原有的页码部分:

旧的代码:

<tfoot>
<tr>
<td colspan="6" align="center">
      <ul class="pagination">
      <li class="disabled"><a href="#">上一页</a></li>
      <li class="active"><a href="#">1 <span class="sr-only">(current)</span> </a> </li>
      <li><a href="#">2</a> </li>
      <li><a href="#">3</a> </li>
       <li><a href="#">4</a> </li>
      <li><a href="#">5</a> </li>
      <li><a href="#">下一页</a> </li>
      </ul>
    </td>
</tr>
</tfoot>

 新的代码:

<tfoot>
<tr>
<td colspan="6" align="center">
<div id="Pagination" class="pagination"><!-- 这里显示分页 --></div>
</td>
</tr>
</tfoot>

 

  jQuery代码:

<script type="text/javascript">
$(function () {
//调用后面声明的函数,对页码导航条进行初始化操作
initPagination();
});

//生成页码导航条
function initPagination() {
// 获取总记录数
var totalRecoed = ${requestScope.pageInfo.total };
// 声明一个JSON对象存储Pagination要设置的属性
var properties = {
num_edge_entries: 3, //边缘页数
num_display_entries: 6, //主体页数
callback: pageSelectCallBack, // 指定用户点击“翻页”的按钮时跳转页面的回调函数
items_per_page:${requestScope.pageInfo.pageSize }, //每页显示1项 每页要显示的数据的数量
current_page: ${requestScope.pageInfo.pageNum - 1 }, //当前页数pageIndex从0开始 Pageination内部使用pageIndex来管理页码,配置index从0开始,pageNum从1开始,所以要减一
prex_text: "上一页", // 上一页按钮上显示的文本
next_text: "下一页" // 下一页按钮上显示的文本
}
// 生成页码导航条
$("#Pagination").pagination(totalRecoed, properties);
}

// 用户点击“123”这样的页码时调用这个函数实现页面跳转
function pageSelectCallBack(pageIndex,jQuery) {
//根据pageIndex计算得到pageNum
var pageNum = pageIndex + 1;
//跳转页面
window.location.href = "admin/get/page.html?pageNum="+pageNum";
//由于每一个页码按钮都是超链接,所以我们要在这里取消超链接的默认行为
return false;
}
</script>

 

  修改Pagination的源码

  jquery.pagination.js文件中的纰漏:

  

   修改这个文件:

jquery.pagination.js中:

 

    // 所有初始化完成,绘制链接
    drawLinks();
// 回调函数
//opts.callback(current_page, this);

 

3 关键词查询

3.1 页面上调整查询表单

  

<form action="admin/get/page.html" class="form-inline" role="form" style="float:left;">
<div class="form-group has-feedback">
<div class="input-group">
<div class="input-group-addon">查询条件</div>
<input name="keyword" class="form-control has-success" type="text" placeholder="请输入查询条件">
</div>
</div>
<button type="submit" class="btn btn-warning"><i class="glyphicon glyphicon-search"></i> 查询
</button>
</form>

 

3.2 在翻页时保持关键词查询条件

  

admin-page.jsp中:

window.location.href = "admin/get/page.html?pageNum="+pageNum+"&keyword=${param.keyword}";

 

4 单条删除

4.1 目标

4.2 思路

  

 

4.3 代码

4.3.1 调整删除的按钮

  

旧的:

<button type="button" class="btn btn-danger btn-xs">
<i class="glyphicon glyphicon-remove"></i>
</button>

 新的:

<a href="admin/remove/${admin.id }/${requestScope.pageInfo.pageNum}/${param.keyword }
.html" class="btn btn-danger btn-xs">
<i class=" glyphicon glyphicon-remove"></i>
</a>

 

4.3.2 AdminHandler.remove()

  

adminHandler:

@RequestMapping("/admin/remove/{adminId}/{pageNum}/{keyword}.html")
public String remove(@PathVariable("adminId") Integer adminId,
@PathVariable("pageNum") Integer pageNum,
@PathVariable("keyword") String keyword) {
// 执行删除
adminService.remove(adminId);
// 页面跳转
return "redirect:/admin/get/page.html?pageNum=" + pageNum + "&keyword=" + keyword;
}

 

 

4.3.3 AdminService.remove()

 adminService:

void remove(Integer adminId);

 adminServiceImpl:

@Override
public void remove(Integer adminId) {
adminMapper.deleteByPrimaryKey(adminId);
}

 

5  新增

5.1 目标

  

 

5.2 思路

  

 

 

 

5.3 在t_admin表中给账号添加唯一约束

  

 

5.4 修改“新增”按钮

  

新的:
<a style="float: right;" href="admin/to/add/page.html" class="btn btn-primary"><i class="glyphicon glyphicon-plus"></i> 新增

 

5.5 配置view-controller

<mvc:view-controller path="/admin/to/add/page.html" view-name="admin-add"/>

 

5.6 准备表单页面

  

<%--
Created by IntelliJ IDEA.
User:
Date: 2023/1/15
Time: 14:59
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<!DOCTYPE html>
<html lang="zh-CN">
<%@include file="/WEB-INF/include-head.jsp" %>

<body>
<%@include file="/WEB-INF/include-nav.jsp" %>
<div class="container-fluid">
<div class="row">
<%@include file="/WEB-INF/include-sidebar.jsp" %>
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
<ol class="breadcrumb">
<li><a href="/admin/to/login/page.html">首页</a></li>
<li><a href="/admin/get/page.html">数据列表</a></li>
<li class="active">新增</li>
</ol>
<div class="panel panel-default">
<div class="panel-heading">表单数据<div style="float:right;cursor:pointer;" data-toggle="modal" data-target="#myModal"><i class="glyphicon glyphicon-question-sign"></i></div></div>
<div class="panel-body">
<form action="admin/save.html" method="post" role="form">
<p>${requestScope.exception.message }</p>
<div class="form-group">
<label for="exampleInputPassword1">登陆账号</label>
<input name="loginAcct" type="text" class="form-control" id="exampleInputPassword1" placeholder="请输入登陆账号">
</div>
<div class="form-group">
<label for="exampleInputPassword1">登陆密码</label>
<input name="userPswd" type="text" class="form-control" id="exampleInputPassword2" placeholder="请输入登陆密码">
</div>
<div class="form-group">
<label for="exampleInputPassword1">用户名称</label>
<input name="userName" type="text" class="form-control" id="exampleInputPassword3" placeholder="请输入用户名称">
</div>
<div class="form-group">
<label for="exampleInputEmail1">邮箱地址</label>
<input name="email" type="email" class="form-control" id="exampleInputEmail1" placeholder="请输入邮箱地址">
<p class="help-block label label-warning">请输入合法的邮箱地址, 格式为: xxxx@xxxx.com</p>
</div>
<button type="submit" class="btn btn-success"><i class="glyphicon glyphicon-plus"></i> 新增</button>
<button type="reset" class="btn btn-danger"><i class="glyphicon glyphicon-refresh"></i> 重置</button>
</form>
</div>
</div>
</div>
</div>
</div>
</body>
</html>

 

 

5.7 handler方法

  

@RequestMapping("/admin/save.html")
public String save(Admin admin) {

adminService.saveAdmin(admin);

return "redirect:/admin/get/page.html?pageNum=" + Integer.MAX_VALUE;
}

 

5.8 Service方法

AdminService:

void saveAdmin(Admin admin);

AdminServiceImpl:

@Override
public void saveAdmin(Admin admin) {

// 1.密码加密
String userPswd = admin.getUserPswd();
userPswd = CrowdUtil.md5(userPswd);
admin.setUserPswd(userPswd);
// 2.生成创建时间
Date date = new Date();
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String creatTime = format.format(date);
admin.setCreateTime(creatTime);
// 3.执行保存
try{
adminMapper.insert(admin);
}catch (Exception e) {
e.printStackTrace();
logger.info("异常全类名=" + e.getClass().getName());

if (e instanceof DuplicateKeyException) {
throw new LoginAcctAlreadyInUseException(CrowdConstant.MESSAGE_LOGIN_ACCT_ALREADY_IN_USE);
}
}
}

 

创建 “账号重复” 异常类 LoginAcctAlreadyInUseException:

LoginAcctAlreadyInUseException:

 

package org.example.exception;
/*
* 保存或更新admin时如果检测到账号重复抛出这个异常
* */
public class LoginAcctAlreadyInUseException extends RuntimeException {

private static final long serialVersionUID = 7713129202396392027L;

public LoginAcctAlreadyInUseException() {
super();
}

public LoginAcctAlreadyInUseException(String s) {
super(s);
}

public LoginAcctAlreadyInUseException(String s, Throwable throwable) {
super(s, throwable);
}

public LoginAcctAlreadyInUseException(Throwable throwable) {
super(throwable);
}

protected LoginAcctAlreadyInUseException(String s, Throwable throwable, boolean b, boolean b1) {
super(s, throwable, b, b1);
}
}

 CrowdExceptionResolver:

 

@ExceptionHandler(value = LoginAcctAlreadyInUseException.class)
public ModelAndView resolveLoginAcctAlreadyInUseException(
LoginFailedException exception,
HttpServletRequest request,
HttpServletResponse response) throws IOException {

String viewName = "admin-add";

return commonResolve(viewName, exception, request, response);
}

 

6 更新

6.1 目标

  

 

6.2 思路

  

6.3 代码:回显表单

6.3.1 调整铅笔按钮

  

旧的:
<button type="button" class="btn btn-primary btn-xs">
<i class="glyphicon glyphicon-pencil"></i>
</button>

新的:
<a href="admin/to/edit/page.html?adminId=${admin.id }&pageNum=${requestScope.pageInfo.pageNum }&keyword=${param.keyword }" class="btn btn-primary btn-xs">
<i class=" glyphicon glyphicon-pencil"></i>
</a>

 

6.3.2 handler方法

  

 
@RequestMapping("admin/to/edit/page.html")
public String toEditPage(@RequestParam("adminId") Integer adminId,
ModelMap modelMap) {
// 1.根据adminId查询Admin对象
Admin admin = adminService.getAdminById(adminId);
// 2.将Admin对象存入模型
modelMap.addAttribute("admin", admin);
return "admin-edit";
}

 

6.3.3 Service方法

  

AdminService:

Admin getAdminById(Integer adminId);

 AdminServiceImpl:

@Override
public Admin getAdminById(Integer adminId) {
return adminMapper.selectByPrimaryKey(adminId);
}

 

 

6.3.4 整理表单页面

  

<%--
Created by IntelliJ IDEA.
User: 123
Date: 2023/1/15
Time: 14:59
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<!DOCTYPE html>
<html lang="zh-CN">
<%@include file="/WEB-INF/include-head.jsp" %>

<body>
<%@include file="/WEB-INF/include-nav.jsp" %>
<div class="container-fluid">
<div class="row">
<%@include file="/WEB-INF/include-sidebar.jsp" %>
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
<ol class="breadcrumb">
<li><a href="/admin/to/login/page.html">首页</a></li>
<li><a href="/admin/get/page.html">数据列表</a></li>
<li class="active">更新</li>
</ol>
<div class="panel panel-default">
<div class="panel-heading">表单数据<div style="float:right;cursor:pointer;" data-toggle="modal" data-target="#myModal"><i class="glyphicon glyphicon-question-sign"></i></div></div>
<div class="panel-body">
<form action="admin/update.html" method="post" role="form">
<input type="hidden" name="id" value="${requestScope.admin.id}">
<input type="hidden" name="pageNum" value="${param.pageNum }">
<input type="hidden" name="keyword" value="${param.keyword }">
<p>${requestScope.exception.message }</p>
<div class="form-group">
<label for="exampleInputPassword1">登陆账号</label>
<input value="${requestScope.admin.loginAcct }" name="loginAcct" type="text" class="form-control" id="exampleInputPassword1" placeholder="请输入登陆账号">
</div>
<div class="form-group">
<label for="exampleInputPassword1">用户名称</label>
<input value="${requestScope.admin.userName }" name="userName" type="text" class="form-control" id="exampleInputPassword3" placeholder="请输入用户名称">
</div>
<div class="form-group">
<label for="exampleInputEmail1">邮箱地址</label>
<input value="${requestScope.admin.email }" name="email" type="email" class="form-control" id="exampleInputEmail1" placeholder="请输入邮箱地址">
<p class="help-block label label-warning">请输入合法的邮箱地址, 格式为: xxxx@xxxx.com</p>
</div>
<button type="submit" class="btn btn-success"><i class="glyphicon glyphicon-edit"></i> 更新</button>
<button type="reset" class="btn btn-danger"><i class="glyphicon glyphicon-refresh"></i> 重置</button>
</form>
</div>
</div>
</div>
</div>
</div>
</body>
</html>

 

 

6.4 代码:执行更新

6.4.1 handler方法

  

@RequestMapping("admin/update.html")
public String update(Admin admin,
@RequestParam("pageNum") Integer pageNum,
@RequestParam("keyword") String keyword){
adminService.update(admin);
return "redirect:/admin/get/page.html?pageNum=" + pageNum + "&keyword=" + keyword;
}

 

6.4.2 Service方法

  

AdminService:

void update(Admin admin);

 AdminServiceImpl:

@Override
public void update(Admin admin) {
// "Selective" 表示有选择的更新,对于null值的字段不更新
try {
adminMapper.updateByPrimaryKeySelective(admin);
} catch (Exception e) {
e.printStackTrace();

if (e instanceof DuplicateKeyException) {
throw new LoginAcctAlreadyInUseForUpdateException(CrowdConstant.MESSAGE_LOGIN_ACCT_ALREADY_IN_USE);
}
}
}

 

6.4.3 声明自定义异常类

  

 

package org.example.exception;
/*
* 保存或更新admin时如果检测到账号重复抛出这个异常
* */
public class LoginAcctAlreadyInUseForUpdateException extends RuntimeException {

private static final long serialVersionUID = 7713129202396392027L;

public LoginAcctAlreadyInUseForUpdateException() {
super();
}

public LoginAcctAlreadyInUseForUpdateException(String s) {
super(s);
}

public LoginAcctAlreadyInUseForUpdateException(String s, Throwable throwable) {
super(s, throwable);
}

public LoginAcctAlreadyInUseForUpdateException(Throwable throwable) {
super(throwable);
}

protected LoginAcctAlreadyInUseForUpdateException(String s, Throwable throwable, boolean b, boolean b1) {
super(s, throwable, b, b1);
}
}

 

6.4.4 在异常处理器中声明对应的处理方法

  

@ExceptionHandler(value = LoginAcctAlreadyInUseForUpdateException.class)
public ModelAndView resolveLoginAcctAlreadyInUseForUpdateException(
LoginFailedException exception,
HttpServletRequest request,
HttpServletResponse response) throws IOException {

String viewName = "system-error";

return commonResolve(viewName, exception, request, response);
}

  

 

 

 

06-后台管理系统-RBAC权限控制模型

 

 

 

-Ajax的同步和异步请求

1 异步的工作方式

 1.1 图解

  

 

1.2 代码

  

<%--
Created by IntelliJ IDEA.
User: 123
Date: 2023/1/15
Time: 14:59
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<!DOCTYPE html>
<html lang="zh-CN">
<%@include file="/WEB-INF/include-head.jsp" %>
<script type="text/javascript">
$(function () {
$("#asyncBtn").click(function () {

console.log("ajax函数之前");
$.ajax({
"url": "test/ajax/async.html",
"type": "post",
"dataType": "text",
"success": function (response) {
// success是接收到服务器端响应后执行
console.log("ajax函数内部的success函数" + response);
}
});
// 在$.ajax()执行完成后执行,不等待success()函数
console.log("ajax函数之后");
});
})
</script>
<body>
<%@include file="/WEB-INF/include-nav.jsp" %>
<div class="container-fluid">
<div class="row">
<%@include file="/WEB-INF/include-sidebar.jsp" %>
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
<button id="asyncBtn">发送Ajax请求</button>
</div>
</div>
</div>
</body>
</html>

 

1.3 打印效果

 

2 同步的工作方式

2.1 图解


2.2 代码

  

<%--
Created by IntelliJ IDEA.
User: 123
Date: 2023/1/15
Time: 14:59
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<!DOCTYPE html>
<html lang="zh-CN">
<%@include file="/WEB-INF/include-head.jsp" %>
<script type="text/javascript">
$(function () {
$("#asyncBtn").click(function () {

console.log("ajax函数之前");
$.ajax({
"url": "test/ajax/async.html",
"type": "post",
"dataType": "text",
"async": false, // 关闭异步工作模型,使用同步工作方式,此时,所有操作在同一个线程内按顺序完成
"success": function (response) {
// success是接收到服务器端响应后执行
console.log("ajax函数内部的success函数" + response);
}
});



// 在$.ajax()执行完成后执行,不等待success()函数
console.log("ajax函数之后");

});
})
</script>
<body>
<%@include file="/WEB-INF/include-nav.jsp" %>
<div class="container-fluid">
<div class="row">
<%@include file="/WEB-INF/include-sidebar.jsp" %>
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
<button id="asyncBtn">发送Ajax请求</button>
</div>
</div>
</div>
</body>
</html>

 

2.3 打印效果

 

3 本质

  

 

 

 

 

 

 

07-后台管理系统-角色维护

1 角色分页操作

1.1 目标

  将角色数据进行分页显示。

 

1.2 思路

  

 

1.3 代码:后端

1.3.1 创建数据库表

  

 

1.3.2 逆向生成资源

  

<table tableName="t_role" domainObjectName="Role" />

 

  操作:

  

  生成资源后各归其位。

 

1.3.3 SQL语句

  

<select id="selectRoleByKeyword" resultMap="BaseResultMap">
select id, name from t_role
where name like concat("%",#{keyword},"%")
</select>

 

1.3.4 RoleMapper接口

  

List<Role> selectRoleByKeyword(String keyword);

 

1.3.5 RoleService 接口和实现

  

RoleService:

package org.example.service.api;

import com.github.pagehelper.PageInfo;
import org.example.entity.Role;

/**
* @author 123
* date 2023-01-28
*/
public interface RoleService {

PageInfo<Role> getPageInfo(Integer pageNum, Integer pageSize, String keyw);

}

RoleServiceImpl:

package org.example.service.impl;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.example.entity.Role;
import org.example.mapper.RoleMapper;
import org.example.service.api.RoleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
* @author 123
* date 2023-01-28
*/
@Service
public class RoleServiceImpl implements RoleService {

@Autowired
private RoleMapper roleMapper;


@Override
public PageInfo<Role> getPageInfo(Integer pageNum, Integer pageSize, String keyword) {

// 1.开启分页功能
PageHelper.startPage(pageNum, pageSize);

// 2.执行查询
List<Role> roleList = roleMapper.selectRoleByKeyword(keyword);

// 3.封装为PageInfo对象返回
return new PageInfo<>(roleList);
}
}

 

1.3.6 RoleHandler 

  

package org.example.mvc.handler;

import com.github.pagehelper.PageInfo;
import org.example.entity.Role;
import org.example.service.api.RoleService;
import org.example.util.ResultEntity;
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;

/**
* @author 123
* date 2023-01-28
*/
@Controller
public class RoleHandler {

@Autowired
private RoleService roleService;

@RequestMapping("/role/get/page/info.json")
public ResultEntity<PageInfo<Role>> getPageInfo(
@RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum,
@RequestParam(value = "pageSize", defaultValue = "5") Integer pageSize,
@RequestParam(value = "keyword", defaultValue = "") String keyword
) {

// 调用Service方法获取分页数据
PageInfo<Role> pageInfo = roleService.getPageInfo(pageNum, pageSize, keyword);

// 封装到ResultEntity对象中返回(如果上面的操作抛出异常,交给异常映射机制处理)
return ResultEntity.successWithData(pageInfo);
}

}

 

1.4 代码:过渡

1.4.1 配置 view-controller

  

<mvc:view-controller path="/role/to/page.html" view-name="admin-add"/>

 

1.4.2 待完善role-page.jsp

  

<%--
Created by IntelliJ IDEA.
User: 123
Date: 2023/1/15
Time: 14:59
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<!DOCTYPE html>
<html lang="zh-CN">
<%@include file="/WEB-INF/include-head.jsp" %>

<body>
<%@include file="/WEB-INF/include-nav.jsp" %>
<div class="container-fluid">
<div class="row">
<%@include file="/WEB-INF/include-sidebar.jsp" %>
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title"><i class="glyphicon glyphicon-th"></i> 数据列表</h3>
</div>
<div class="panel-body">
<form class="form-inline" role="form" style="float:left;">
<div class="form-group has-feedback">
<div class="input-group">
<div class="input-group-addon">查询条件</div>
<input id="keywordInput" class="form-control has-success" type="text"
placeholder="请输入查询条件">
</div>
</div>
<button id="searchBtn" type="button" class="btn btn-warning"><i
class="glyphicon glyphicon-search"></i> 查询
</button>
</form>
<button id="batchRemoveBtn" type="button" class="btn btn-danger"
style="float:right;margin-left:10px;"><i
class=" glyphicon glyphicon-remove"></i> 删除
</button>
<button id="showModelBtn" type="button" class="btn btn-primary" style="float:right;"><i
class="glyphicon glyphicon-plus"></i> 新增
</button>
<br>
<hr style="clear:both;">
<div class="table-responsive">
<table class="table table-bordered">
<thead>
<tr>
<th width="30">#</th>
<th width="30"><input

type="checkbox"></th>
<th>名称</th>
<th width="100">操作</th>
</tr>
</thead>
<tbody id="rolePageBody">
</tbody>
<tfoot>
<tr>
<td colspan="6" align="center">
<div id="Pagination" class="pagination"><!-- 这里显示分页 --></div>
</td>
</tr>
</tfoot>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>

 

1.4.3 修改 “角色维护” 超链接

  

<li style="height:30px;">
<a href="role/to/page.html"><i class="glyphicon glyphicon-king"></i> 角色维护</a>
</li>

 

1.5 代码:前端

 1.5.1 初始化数据

  


<script type="text/javascript">

$(function () {

// 1.为分页操作准备初始化数据
window.pageNum = 1;
window.pageSize = 5;
window.keyword = "";


})

</script>

 

 1.5.2 创建外部JavaScript文件

   

  在role-page.jsp引入外部JavaScript文件my-role.js:


<script type="text/javascript" src="crowd/my-role.js"></script>

  编写my-role.js文件(部分):

// 执行分页,生成页面效果  任何时候调用这个函数都会重新加载页面
function generatePage() {
// 1.获取分页数据
var pageInfoRemote = getPageInfoRemote();
// 2.填充表格
fillTableBody(pageInfoRemote);
}

// 远程访问服务器端程序获取pageInfo数据
function getPageInfoRemote() {
var ajaxRs = $.ajax({
"url": "role/get/page/info.json",
"type": "post",
"data": {
"pageNum": window.pageNum,
"pageSize": window.pageSize,
"keyword": window.keyword
},
"async": false,
"dataType": "json"
});
var statusCode = ajaxRs.status;
// 发生错误
if (statusCode != 200) {
layer.msg("服务器端程序调用失败!响应状态码是=" + statusCode + "说明信息=" + ajaxRs.statusText);
return null;
}
// 如果响应状态码是200表示请求成功
var resultEntity = ajaxRs.responseJSON;
// 从resultEntity中获取result属性
var result = resultEntity.result;
// 判断result是否成功
if (result == "FAILED") {
layer.msg(resultEntity.message);
return null;
}
// 确认result为成功后获取pageInfo
var pageInfo = resultEntity.data;
// 返回pageInfo
return pageInfo;
}

// 填充表格
function fillTableBody(pageInfo) {
// 清除tbody中的旧的数据
$("#rolePageBody").empty();
// 为了搜索没有结果时不显示页码
$("#Pagination").empty();
// 判断pageInfo是否有效
if (pageInfo == null || pageInfo == undefined || pageInfo.list.length == 0) {
$("#rolePageBody").append("<tr><td colspan='4'>抱歉!没有查询到您要的数据!</td></tr>");
return;
}
// 使用pageInfo的list属性填充tbody
for (var i = 0; i < pageInfo.list.length; i++) {
var role = pageInfo.list[i];
var roleId = role.id;
var roleName = role.name;
var numberTd = "<td>" + (i + 1) + "</td>";
var checkboxTd = "<td><input type='checkbox'></td>"
var roleNameTd = "<td>" + roleName + "</td>"

var checkBtn = "<button type='button' class='btn btn-success btn-xs'><i class=' glyphicon glyphicon-check'></i></button>";
var pencilBtn = "<button type='button' class='btn btn-primary btn-xs'><i class='glyphicon glyphicon-pencil'></i></button>";
var removeBtn = "<button type='button' class='btn btn-danger btn-xs'><i class='glyphicon glyphicon-remove'></i></button>";
var buttonTd = "<td>" + checkBtn + " " + pencilBtn + " " + removeBtn + "</td>";
var tr = "<tr>" + numberTd + checkboxTd + roleNameTd + buttonTd + "</tr>";
$("#rolePageBody").append(tr);
}
// 生成分页导航条
generateNavigator(pageInfo);
}

// 生成分页页码导航条
// 生成分页页码导航条
function generateNavigator(pageInfo) {
// 获取总记录数
var totalRecord = pageInfo.total;
// 声明相关属性
var properties = {
"num_edge_entries": 3,
"num_display_entries": 5,
"callback": paginationCallBack,
"items_per_page": pageInfo.pageSize,
"current_page": pageInfo.pageNum - 1,
"prev_text": "上一页",
"next_text": "下一页"
}
// 调用 pagination()函数
$("#Pagination").pagination(totalRecord, properties);
}

// 翻页时的回调函数
function paginationCallBack(pageIndex, jQuery) {
// 修改 window 对象的 pageNum 属性
window.pageNum = pageIndex + 1;
// 调用分页函数
generatePage();
// 取消页码超链接的默认行为
return false;

 

报错:

解决:

在role-page.jsp中添加环境:

<link rel="stylesheet" href="css/pagination.css"/>
<script type="text/javascript" src="jquery/jquery.pagination.js"></script>

 

2 角色查询操作

2.1 目标

  把页面上的“查询”表单和已经封装好的执行分页的函数连接起来即可。

2.2 思路

  

2.3 代码

2.3.1 HTML中标记id

<form class="form-inline" role="form" style="float:left;">
<div class="form-group has-feedback">
<div class="input-group">
<div class="input-group-addon">查询条件</div>
<input id="keywordInput" class="form-control has-success" type="text"
placeholder="请输入查询条件">
</div>
</div>
<button id="searchBtn" type="button" class="btn btn-warning"><i
class="glyphicon glyphicon-search"></i> 查询
</button>
</form>

2.3.2 jQuery代码

// 3.给查询按钮绑定单击响应函数
$("#searchBtn").click(function () {
// 1.获取关键词数据赋值给全局变量
window.keyword = $("#keywordInput").val();
// 2,调用分页函数刷新页面
generatePage();
});

 

 

3 角色保存操作

3.1 目标

  

3.2 思路

  

 

3.3 代码:页面引入模态框

3.3.1 创建JSP文件

  

  

3.3.2 加入模态框HTML代码 model-role-add.jsp

<%--
Created by IntelliJ IDEA.
User: 123
Date: 2023/1/31
Time: 13:27
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<div id="addModal" class="modal fade" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title">尚筹网系统弹窗</h4>
</div>
<div class="modal-body">
<form action="admin/do/login.html" method="post" class="form-signin" role="form">
<h2 class="form-signin-heading"><i class="glyphicon glyphicon-log-in"></i> 管理员登录</h2>
<div class="form-group has-success has-feedback">
<input type="text" name="roleName" class="form-control" placeholder="请输入角色名称" autofocus>
</div>
</form>
</div>
<div class="modal-footer">
<button id="saveRoleBtn" type="button" class="btn btn-primary">保存</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
</body>
</html>

 

 3.3.3 在role-page.jsp引入上面的文件 model-role-add.jsp

<%@include file="/WEB-INF/model-role-add.jsp" %>

  

 

3.4 代码:打开模态框

3.4.1 修改新增按钮

<button id="showModelBtn" type="button" class="btn btn-primary" style="float:right;"><i
class="glyphicon glyphicon-plus"></i> 新增
</button>

  

 

3.4.2 给新增按钮绑定单击响应函数

// 4.点击新增按钮打开模态框
$("#showModelBtn").click(function () {
$("#addModal").modal("show");
});

 

3.5 代码:执行保存

3.5.1 前端代码

  

// 5.给新增模态框中的保存按钮绑定单击响应函数
$("#saveRoleBtn").click(function () {
// 1.获取用户在文本框中输入的角色名称
// #addModal表示找到整个模态框
// 空格表示在后代元素中继续查找
// [name=roleName]表示匹配name属性等于roleName的元素
var roleName = $.trim($("#addModal [name=roleName]").val());

// 2.发送ajax请求
$.ajax({
"url": "role/save.json",
"type": "post",
"data": {
"name": roleName
},
"dataType": "json",
"success": function (response) {
var result = response.result;
if (result == "SUCCESS") {
layer.msg("操作成功!");

// 将页码定位到最后一页
window.pageNum = 99999999;
}
if (result == "FAILED") {
layer.msg("操作失败!" + response.message)
}
// 重新加载分页
generatePage();
},
"error": function (response) {
layer.msg(response.status + " " + response.statusText);
}
});
// 关闭模态框
$("#addModal").modal("hide");
// 清理模态框
$("#addModal [name=roleName]").val("");

//

});

3.5.2 后端代码

RoleHandler.class:

@ResponseBody
@RequestMapping("/role/save.json")
public ResultEntity<String> savaRole(Role role){
roleService.saveRole(role);
return ResultEntity.successWithoutData();
}

 RoleService:

void saveRole(Role role);

 RoleServiceImpl:

@Override
public void saveRole(Role role) {
roleMapper.insert(role);
}

 

4 角色更新操作

4.1 目标

  修改角色信息

4.2 思路

  

 

 

4.3 代码:页面引入模态框

4.3.1 创建JSP文件

  

4.3.2 加入模态框的HTML代码

<%--
Created by IntelliJ IDEA.
User: 123
Date: 2023/2/1
Time: 18:09
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<div id="editModal" class="modal fade" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title">尚筹网系统弹窗</h4>
</div>
<div class="modal-body">
<form action="admin/do/login.html" method="post" class="form-signin" role="form">
<h2 class="form-signin-heading"><i class="glyphicon glyphicon-log-in"></i> 管理员登录</h2>
<div class="form-group has-success has-feedback">
<input type="text" name="roleName" class="form-control" placeholder="请输入角色名称" autofocus>
</div>
</form>
</div>
<div class="modal-footer">
<button id="updateRoleBtn" type="button" class="btn btn-success">更新</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
</body>
</html>

4.3.3 在role-page.jsp引入模态框

  

<%@include file="/WEB-INF/model-role-edit.jsp" %>

 

4.4 代码:打开模态框(回显)

4.4.1 修改 “铅笔” 按钮

  

// 通过button标签的id属性把roleId值传递到button按钮的单击响应函数中
var checkBtn = "<button id='" + roleId + "' type='button' class='btn btn-success btn-xs checkBtn'><i class=' glyphicon glyphicon-check'></i></button>";

 

4.4.2 给 “铅笔” 按钮绑定单击响应函数

role-page.jsp:

// 6.给页面上的铅笔按钮绑定单击响应函数,目的是打开模态框
// 使用下方第一种绑定的方式(也就是传统的事件绑定方式),出现问题:只在第一个页面有效,翻页后就失效了
/*$(".pencilBtn").click(function () {
alert("aaaaa....");
});*/
// 使用jQuery对象的 on() 解决上面问题
// 1.首先找到所有“动态生成“的元素所附着的”静态“元素
// 2.on函数的第一个参数是事件类型,第二个参数是真正要绑定事件的选择器
// 3.第三个参数是响应函数
$("#rolePageBody").on("click", ".pencilBtn", function () {
// 打开模态框
$("#editModal").modal("show");
// 获取表格中当前行中的角色名称
var roleName = $(this).parent().prev().text();
// 获取当前角色的 id
// 依据是:var pencilBtn = "<button id='"+roleId+"' ……这段代码中我们把 roleId 设置到id 属性了
// 为了让执行更新的按钮能够获取到 roleId 的值,把它放在全局变量上
window.roleId = this.id;
// 使用 roleName 的值设置模态框中的文本框
$("#editModal [name=roleName]").val(roleName);
});

 

4.5 代码:执行更新

4.5.1 前端

role-page.jsp:

// 7.给更新模态框中的更新按钮绑定单击响应函数
$("#updateRoleBtn").click(function () {
// ①从文本框中获取新的角色名称
var roleName = $("#editModal [name=roleName]").val();
// ②发送 Ajax 请求执行更新
$.ajax({
"url": "role/update.json",
"type": "post",
"data": {
"id": window.roleId,
"name": roleName
},
"dataType": "json",
"success": function (response) {
var result = response.result;
if (result == "SUCCESS") {
layer.msg("操作成功!");
// 重新加载分页数据
generatePage();
}
if (result == "FAILED") {
layer.msg("操作失败!" + response.message);
}
},
"error": function (response) {
layer.msg(response.status + " " + response.statusText);
}
});
// ③关闭模态框
$("#editModal").modal("hide");
});

4.5.2 后端

RoleHandler:

@ResponseBody
@RequestMapping("/role/update.json")
public ResultEntity<String> updateRole(Role role) {
System.out.println(role.toString());
roleService.updateRole(role);
return ResultEntity.successWithoutData();
}

 RoleService:

void updateRole(Role role);

 RoleServiceImpl:

 

@Override
public void updateRole(Role role) {
roleMapper.updateByPrimaryKey(role);
}

5 角色删除操作

5.1 目标

  

5.2 思路

  

5.3 代码:后端

handler方法:

@RequestMapping("/role/remove/by/role/id/array.json")
public ResultEntity<String> removeByRoleIdArray(@RequestBody List<Integer> roleIdList){
System.out.println("-------------");
System.out.println(roleIdList.toString());
roleService.removeRole(roleIdList);
return ResultEntity.successWithoutData();
}

 RoleService:

void removeRole(List<Integer> roleIdList);

 RoleServiceImpl:

@Override
public void removeRole(List<Integer> roleIdList) {
RoleExample roleExample = new RoleExample();
RoleExample.Criteria criteria = roleExample.createCriteria();
// delete from t_role where id in (5,8,12)
criteria.andIdIn(roleIdList);
roleMapper.deleteByExample(roleExample);
}

5.4 代码:前端(公共部分)

5.4.1 声明函数用于打开模态框

  

  

// 声明专门的函数显示确认模态框
function showConfirmModel(roleArray) {
// 打开模态框
$("#confirmModal").modal("show");
// 清除旧数据
$("#roleNameDiv").empty();
// 在全局变量范围内创建数组来存放角色id
window.roleIdArray = [];
// 遍历roleArray数组
for (var i = 0; i < roleArray.length; i++) {
var role = roleArray[i];
var roleName = role.roleName;
$("#roleNameDiv").append(roleName + "<br />");
var roleId = role.roleId;
window.roleIdArray.push(roleId);
}
}

5.4.2 给确认模态框中的 “确认删除” 按钮绑定单击响应函数

role-page.jsp:

// 8.点击确认模态框中的确认删除按钮执行删除
$("#removeRoleBtn").click(function () {

var requestBody = JSON.stringify(window.roleIdArray);

$.ajax({
"url": "role/remove/by/role/id/array.json",
"type": "post",
"data": requestBody,
"contentType": "application/json;charset=UTF-8",
"dataType": "json",
"success": function (response) {
var result = response.result;
if (result == "SUCCESS") {
layer.msg("操作成功!");
// 重新加载分页数据
generatePage();
}
if (result == "FAILED") {
layer.msg("操作失败!" + response.message);
}
},
"error": function (response) {
layer.msg(response.status + " " + response.statusText);
}
});
// ③关闭模态框
$("#confirmModal").modal("hide");
});

 

报错(测试单个删除时):400错误

解决:

在 removeByRoleIdArray 方法中添加注解 @ResponseBody,↓

 

5.5 代码:前端(单条删除)

5.5.1 修改删除按钮

  

  在 fillTableBody(pageInfo) 函数中:

var removeBtn = "<button id='" + roleId + "' type='button' class='btn btn-danger btn-xs removeBtn'><i class='glyphicon glyphicon-remove'></i></button>";

 

5.5.2 绑定单击响应函数

role-page.jsp:

// 9.给单条删除按钮绑定单击响应函数
$("#rolePageBody").on("click", ".removeBtn", function () {
//从当前按钮出发获取角色名称
var roleName = $(this).parent().prev().text();

// 创建role对象
var roleArray = [{
roleId: this.id,
roleName: roleName,
}];
// 调用函数打开模态框
showConfirmModel(roleArray);
generatePage(); //
});

5.6 代码:前端(批量删除)

5.6.1 全选全不选

role-page.jsp:

// 10.给总的checkBox绑定单击响应
$("#summaryBox").click(function () {
// 1.获取当前多选框自身的状态
var currentStatus = this.checked;
// 2.用当前多选框的状态去设置其他多选框
$('.itemBox').prop("checked", currentStatus);
});
// 11.全选、全不选的反向操作
$("#rolePageBody").on("click", ".itemBox", function () {
// 获取当前已经选中的.itemBox的数量
var checkedBoxCount = $(".itemBox:checked").length;
// 获取全部.itemBox的数量
var totalBoxCount = $(".itemBox").length;
// 使用二者的比较结果设置总的checkBox
$("#summaryBox").prop("checked", checkedBoxCount == totalBoxCount);
});

5.6.2 给批量删除按钮绑定单击响应函数

role-page.jsp:

// 12.给批量删除的按钮绑定单击响应函数
$("#batchRemoveBtn").click(function () {
// 创建数组存放获取到的对象
var roleArray = [];
//遍历当前选中的多选框
$(".itemBox:checked").each(function () {
// 使用this引用当前遍历得到的多选框
var roleId = this.id;
// 通过DOM操作获取角色名称
var roleName = $(this).parent().next().text();
roleArray.push({
"roleId": roleId,
"roleName": roleName
});
});
// 检查roleArray的长度是否为0
if (roleArray.length == 0) {
layer.msg("请至少选择一个执行删除!");
return;
}
// 调用函数打开模态框
showConfirmModel(roleArray);
});
修改(添加id属性):

<button id="batchRemoveBtn" type="button" class="btn btn-danger"
style="float:right;margin-left:10px;"><i
class=" glyphicon glyphicon-remove"></i> 删除
</button>

 

 

08-后台管理系统-菜单维护

1 树形结构基础知识介绍

 1.1 节点类型

  

 

1.2 在数据库表中表示树形结构

1.2.1 创建菜单的数据库表

  

 

1.2.2 插入数据

INSERT INTO `fundingonline_study`.`t_menu`(`id`, `pid`, `name`, `url`, `icon`) VALUES (1, NULL, '系统权限菜单', NULL, 'glyphicon\r\nglyphicon-th-list');
INSERT INTO `fundingonline_study`.`t_menu`(`id`, `pid`, `name`, `url`, `icon`) VALUES (2, 1, '控 制 面 板', 'main.htm', 'glyphicon glyphicon glyphicon-tasks');
INSERT INTO `fundingonline_study`.`t_menu`(`id`, `pid`, `name`, `url`, `icon`) VALUES (3, 1, '权限管理', NULL, 'glyphicon glyphicon\r\nglyphicon-tasks');
INSERT INTO `fundingonline_study`.`t_menu`(`id`, `pid`, `name`, `url`, `icon`) VALUES (4, 3, ' 用 户 维 护 ', 'user/index.htm', 'glyphicon\r\nglyphicon-user');
INSERT INTO `fundingonline_study`.`t_menu`(`id`, `pid`, `name`, `url`, `icon`) VALUES (5, 3, ' 角 色 维 护 ', 'role/index.htm', 'glyphicon\r\nglyphicon-king');
INSERT INTO `fundingonline_study`.`t_menu`(`id`, `pid`, `name`, `url`, `icon`) VALUES (6, 3, ' 菜 单 维 护 ', 'permission/index.htm', 'glyphicon\r\nglyphicon-lock');
INSERT INTO `fundingonline_study`.`t_menu`(`id`, `pid`, `name`, `url`, `icon`) VALUES (7, 1, ' 业 务 审 核 ', NULL, 'glyphicon\r\nglyphicon-ok');
INSERT INTO `fundingonline_study`.`t_menu`(`id`, `pid`, `name`, `url`, `icon`) VALUES (8, 7, '实名认证审核', 'auth_cert/index.htm', 'glyphicon\r\nglyphicon-check');
INSERT INTO `fundingonline_study`.`t_menu`(`id`, `pid`, `name`, `url`, `icon`) VALUES (9, 7, ' 广 告 审 核 ', 'auth_adv/index.htm', 'glyphicon\r\nglyphicon-check');
INSERT INTO `fundingonline_study`.`t_menu`(`id`, `pid`, `name`, `url`, `icon`) VALUES (10, 7, ' 项 目 审 核 ', 'auth_project/index.htm', 'glyphicon\r\nglyphicon-check');
INSERT INTO `fundingonline_study`.`t_menu`(`id`, `pid`, `name`, `url`, `icon`) VALUES (11, 1, ' 业 务 管 理 ', NULL, 'glyphicon\r\nglyphicon-th-large');
INSERT INTO `fundingonline_study`.`t_menu`(`id`, `pid`, `name`, `url`, `icon`) VALUES (12, 11, ' 资 质 维 护 ', 'cert/index.htm', 'glyphicon\r\nglyphicon-picture');
INSERT INTO `fundingonline_study`.`t_menu`(`id`, `pid`, `name`, `url`, `icon`) VALUES (13, 11, ' 分 类 管 理 ', 'certtype/index.htm', 'glyphicon\r\nglyphicon-equalizer');
INSERT INTO `fundingonline_study`.`t_menu`(`id`, `pid`, `name`, `url`, `icon`) VALUES (14, 11, ' 流 程 管 理 ', 'process/index.htm', 'glyphicon\r\nglyphicon-random');
INSERT INTO `fundingonline_study`.`t_menu`(`id`, `pid`, `name`, `url`, `icon`) VALUES (15, 11, ' 广 告 管 理 ', 'advert/index.htm', 'glyphicon\r\nglyphicon-hdd');
INSERT INTO `fundingonline_study`.`t_menu`(`id`, `pid`, `name`, `url`, `icon`) VALUES (16, 11, ' 消 息 模 板 ', 'message/index.htm', 'glyphicon\r\nglyphicon-comment');
INSERT INTO `fundingonline_study`.`t_menu`(`id`, `pid`, `name`, `url`, `icon`) VALUES (17, 11, ' 项 目 分 类 ', 'projectType/index.htm', 'glyphicon\r\nglyphicon-list');
INSERT INTO `fundingonline_study`.`t_menu`(`id`, `pid`, `name`, `url`, `icon`) VALUES (18, 11, ' 项 目 标 签 ', 'tag/index.htm', 'glyphicon\r\nglyphicon-tags');
INSERT INTO `fundingonline_study`.`t_menu`(`id`, `pid`, `name`, `url`, `icon`) VALUES (19, 1, ' 参 数 管 理 ', 'param/index.htm', 'glyphicon\r\nglyphicon-list-alt');

 

1.2.3 关联方式

  

 

1.3 在Java类中表示树形结构

1.3.1 基本方式

  

 

1.3.2 为了配合zTree所需要添加的属性

   

 

1.4 按钮增删改查的规则 

  

 

 

 

 

2 菜单维护:页面显示树形结构

2.1 目标

  

2.2 思路

  

 

2.3 代码:逆向工程

  

<table tableName="t_menu" domainObjectName="Menu" />

  

  逆向生成的Menu实体类需要做一些调整:

package org.example.entity;

import java.util.ArrayList;
import java.util.List;

public class Menu {
// 主键
private Integer id;

// 父节点
private Integer pid;

// 节点名称
private String name;

// 节点附带的URL地址,是将来点击菜单项时要跳转的地址
private String url;

// 节点图标的样式
private String icon;

// 存储子节点的集合,初始化是为了避免空指针异常
private List<Menu> children = new ArrayList<>();

// 控制节点是否默认为打开状态,设置为true表示为默认打开
private Boolean open =
true;

@Override
public String toString() {

return "Menu{" +
"id=" + id +
", pid=" + pid +
", name='" + name + '\'' +
", url='" + url + '\'' +
", icon='" + icon + '\'' +
", children=" + children +
", open=" + open +
'}';
}


public Menu(Integer id, Integer pid, String name, String url, String icon, List<Menu> children, Boolean open) {
this.id = id;
this.pid = pid;
this.name = name;
this.url = url;
this.icon = icon;
this.children = children;
this.open = open;
}


public Menu() {
}

public List<Menu> getChildren() {
return children;
}

public void setChildren(List<Menu> children) {
this.children = children;
}

public Boolean getOpen() {
return open;
}

public void setOpen(Boolean open) {
this.open = open;
}

public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public Integer getPid() {
return pid;
}

public void setPid(Integer pid) {
this.pid = pid;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name == null ? null : name.trim();
}

public String getUrl() {
return url;
}

public void setUrl(String url) {
this.url = url == null ? null : url.trim();
}

public String getIcon() {
return icon;
}

public void setIcon(String icon) {
this.icon = icon == null ? null : icon.trim();
}
}

   最后各归各位。

 

2.4 代码:将数据在Java代码中组装成树形结构

2.4.1 service方法

  

package org.example.service.api;

import org.example.entity.Menu;

import java.util.List;

/**
* @author 123
* date 2023-02-02
*/
public interface MenuService {

List<Menu> getAll();

}
package org.example.service.impl;

import org.example.entity.Menu;
import org.example.entity.MenuExample;
import org.example.mapper.MenuMapper;
import org.example.service.api.MenuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
* @author 123
* date 2023-02-02
*/
@Service
public class MenuServiceImpl implements MenuService {

@Autowired
private MenuMapper menuMapper;

@Override
public List<Menu> getAll() {
return menuMapper.selectByExample(new MenuExample());
}
}

 

2.4.2 handler方法

  

package org.example.mvc.handler;

import org.example.entity.Menu;
import org.example.service.api.MenuService;
import org.example.util.ResultEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

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

/**
* @author 123
* date 2023-02-02
*/
@RestController
public class MenuHandler {

@Autowired
private MenuService menuService;

@RequestMapping("/menu/get/whole/tree.json")
public ResultEntity<Menu> getWholeTreeNew() {
// 1.查询全部的 Menu 对象
List<Menu> menuList = menuService.getAll();
// 2.声明一个变量用来存储找到的根节点
Menu root = null;
// 3.创建 Map 对象用来存储 id Menu 对象的对应关系便于查找父节点
Map<Integer, Menu> menuMap = new HashMap<>();
// 4.遍历 menuList 填充 menuMap
for (Menu menu : menuList) {
Integer id = menu.getId();
menuMap.put(id, menu);
}
// 5.再次遍历 menuList 查找根节点、组装父子节点
for (Menu menu : menuList) {
// 6.获取当前 menu 对象的 pid 属性值
Integer pid = menu.getPid();
// 7.如果 pid null,判定为根节点
if(pid == null) {
root = menu;
// 8.如果当前节点是根节点,那么肯定没有父节点,不必继续执行
continue ;
}
// 9.如果 pid 不为 null,说明当前节点有父节点,那么可以根据 pid menuMap 中查找对应的 Menu 对象
Menu father = menuMap.get(pid);
// 10.将当前节点存入父节点的 children 集合
father.getChildren().add(menu);
}
// 11.经过上面的运算,根节点包含了整个树形结构,返回根节点就是返回整个树
return ResultEntity.successWithData(root);
}

}

 

2.5 代码:跳转页面

  

  配置view-controller:


<
mvc:view-controller path="/menu/to/page.html" view-name="menu-page"/>

  新建menu-page.jsp:

<%--
Created by IntelliJ IDEA.
User: 123
Date: 2023/1/15
Time: 14:59
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<!DOCTYPE html>
<html lang="zh-CN">
<%@include file="/WEB-INF/include-head.jsp" %>

<body>
<%@include file="/WEB-INF/include-nav.jsp" %>
<div class="container-fluid">
<div class="row">
<%@include file="/WEB-INF/include-sidebar.jsp" %>
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
<div class="panel panel-default">
<div class="panel-heading">
<i class="glyphicon glyphicon-th-list"></i> 权限菜单列表
<div style="float:right;cursor:pointer;" data-toggle="modal" data-target="#myModal">
<i class="glyphicon glyphicon-question-sign"></i>
</div>
</div>
<div class="panel-body">
<ul id="treeDemo" class="ztree">
</ul>
</div>
</div>
</div>
</div>
</div>
</body>
</html>

 

2.6 代码:引入zTree环境

  

menu-page.jsp页面引入环境:


<link rel="stylesheet" href="ztree/zTreeStyle.css"/>
<script type="text/javascript" src="ztree/jquery.ztree.all-3.5.min.js"></script>

 

2.7 代码:页面上使用zTree初步显示树形结构(假数据)

<%--
Created by IntelliJ IDEA.
User: 123
Date: 2023/1/15
Time: 14:59
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<!DOCTYPE html>
<html lang="zh-CN">
<%@include file="/WEB-INF/include-head.jsp" %>
<link rel="stylesheet" href="ztree/zTreeStyle.css"/>
<script type="text/javascript" src="ztree/jquery.ztree.all-3.5.min.js"></script>
<script type="text/javascript">

$(function () {

// 1 创建JSON对象用于存储对zTree所做的设置
var setting = { };

// 2 准备生成树形结构的JSON数据
var zNodes =[
{ name:"父节点1 - 展开", open:true,
children: [
{ name:"父节点11 - 折叠",
children: [
{ name:"叶子节点111"},
{ name:"叶子节点112"},
{ name:"叶子节点113"},
{ name:"叶子节点114"}
]},
{ name:"父节点12 - 折叠",
children: [
{ name:"叶子节点121"},
{ name:"叶子节点122"},
{ name:"叶子节点123"},
{ name:"叶子节点124"}
]},
{ name:"父节点13 - 没有子节点", isParent:true}
]},
{ name:"父节点2 - 折叠",
children: [
{ name:"父节点21 - 展开", open:true,
children: [
{ name:"叶子节点211"},
{ name:"叶子节点212"},
{ name:"叶子节点213"},
{ name:"叶子节点214"}
]},
{ name:"父节点22 - 折叠",
children: [
{ name:"叶子节点221"},
{ name:"叶子节点222"},
{ name:"叶子节点223"},
{ name:"叶子节点224"}
]},
{ name:"父节点23 - 折叠",
children: [
{ name:"叶子节点231"},
{ name:"叶子节点232"},
{ name:"叶子节点233"},
{ name:"叶子节点234"}
]}
]},
{ name:"父节点3 - 没有子节点", isParent:true}

];

    // 3 初始化树形结构

$.fn.zTree.init($("#treeDemo"), setting, zNodes);

});

</script>

<body>
<%@include file="/WEB-INF/include-nav.jsp" %>
<div class="container-fluid">
<div class="row">
<%@include file="/WEB-INF/include-sidebar.jsp" %>
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
<div class="panel panel-default">
<div class="panel-heading">
<i class="glyphicon glyphicon-th-list"></i> 权限菜单列表
<div style="float:right;cursor:pointer;" data-toggle="modal" data-target="#myModal">
<i class="glyphicon glyphicon-question-sign"></i>
</div>
</div>
<div class="panel-body">
<%--这个ul标签是zTree动态生成的节点所依附的静态节点--%>
<ul id="treeDemo" class="ztree">
</ul>
</div>
</div>
</div>
</div>
</div>
</body>
</html>

 

2.8 代码:在页面上使用真实数据显示树形结构

menu-page.jsp:

$(function () {

// 1 准备生成树形结构的JSON数据,数据的来源是发送Ajax请求得到
$.ajax({
"url": "menu/get/whole/tree.json",
"type": "post",
"dataType": "json",
"success": function (response) {
var result = response.result;
if (result == "SUCCESS") {
// 2 创建JSON对象用于存储对zTree所做的设置
var setting = {};

// 3 从响应体中获取用来生成树形结构的JSON数据
var zNodes = response.data;

// 4 初始化树形结构
$.fn.zTree.init($("#treeDemo"), setting, zNodes);
}
if (result == "FAILED") {
layer.msg(response.message);
}
}
});

});

 

测试 “在页面上使用真实数据显示树形结构” 报错:404

解决:

MuneHandler.class 中的@Controller改为@RestController注解

 

2.9 代码:修改默认图标为真实图标

menu-page.jsp:

$(function () {

// 1 准备生成树形结构的JSON数据,数据的来源是发送Ajax请求得到
$.ajax({
"url": "menu/get/whole/tree.json",
"type": "post",
"dataType": "json",
"success": function (response) {
var result = response.result;
if (result == "SUCCESS") {
// 2 创建JSON对象用于存储对zTree所做的设置
var setting = {
"view": {
"addDiyDom": myAddDiyDom
}
};

// 3 从响应体中获取用来生成树形结构的JSON数据
var zNodes = response.data;

// 4 初始化树形结构
$.fn.zTree.init($("#treeDemo"), setting, zNodes);
}
if (result == "FAILED") {
layer.msg(response.message);
}
}
});

});

 my-menu.js:

function myAddDiyDom(treeId, treeNode) {

// treeId是整个树形结构附着的ul标签的id
// console.log("treeId=" + treeId);

// 当前树形节点的全部的数据,包括从后端查询得到的Menu对象的全部属性
// console.log(treeNode);

// zTree生成id的规则 treeDome_7_ico
// 解析:ul标签的id_当前节点的序号_功能
// 提示:“ul标签的id_当前节点的序号” 部分可以通过访问treeNode的tId属性得到
// 根据id的生成规则拼凑出来span标签的id
var spanId = treeNode.tId + "_ico";

// 根据控制图标的span标签的id找到这个span标签
// 删除旧的class
// 添加新的class
$("#" + spanId)
.removeClass()
.addClass(treeNode.icon);


}

 

2.10 代码:实现 “点了不跑”

  

menu-page.jsp:

$(function () {

// 1 准备生成树形结构的JSON数据,数据的来源是发送Ajax请求得到
$.ajax({
"url": "menu/get/whole/tree.json",
"type": "post",
"dataType": "json",
"success": function (response) {
var result = response.result;
if (result == "SUCCESS") {
// 2 创建JSON对象用于存储对zTree所做的设置
var setting = {
"view": {
"addDiyDom": myAddDiyDom
},
"data": {
"key": {
"url": "maomi"
}
}
};

// 3 从响应体中获取用来生成树形结构的JSON数据
var zNodes = response.data;

// 4 初始化树形结构
$.fn.zTree.init($("#treeDemo"), setting, zNodes);
}
if (result == "FAILED") {
layer.msg(response.message);
}
}
});

});

 

2.11 代码:显示按钮组

2.11.1 思路和步骤

  

 

 

2.11.2 myRemoveHoverDom(treeId, treeNode) 函数

  

// 在鼠标移开节点范围时删除按钮组
function myRemoveHoverDom(treeId, treeNode) {
// 拼接按钮组的id
var btnGroupId = treeNode.tId + "_btnGrp";
//移除对应的元素
$("#" + btnGroupId).remove();
}

 

2.11.3 myAddHoverDom(treeId, treeNode) 函数

  

// 在鼠标移入节点范围时添加按钮组
function myAddHoverDom(treeId, treeNode) {
// 按钮组的标签结构:<span><a><i></i></a><a><i></i></a></span>
// 按钮组出现的位置:节点中 treeDemo_n_a 超链接的后面
// 为了在需要移除按钮组的时候能够精确定位到按钮组所在 span,需要给 span 设置有规律的 id
var btnGroupId = treeNode.tId + "_btnGrp";
// 判断一下以前是否已经添加了按钮组
if($("#"+btnGroupId).length > 0) {
return ;
}
// 准备各个按钮的HTML标签
var addBtn = "<a id='" + treeNode.id + "' class='addBtn btn btn-info dropdown-toggle btn-xs' style='margin-left:10px;padding-top:0px;' title='添加子节点'>&nbsp;&nbsp;<i class='fa fa-fw fa-plus rbg '></i></a>";
var removeBtn = "<a id='" + treeNode.id + "' class='removeBtn btn btn-info dropdown-toggle btn-xs' style='margin-left:10px;padding-top:0px;' title=' 删 除 节 点 '>&nbsp;&nbsp;<i class='fa fa-fw fa-times rbg '></i></a>";
var editBtn = "<a id='" + treeNode.id + "' class='editBtn btn btn-info dropdown-toggle btn-xs' style='margin-left:10px;padding-top:0px;' title=' 修 改 节 点 '>&nbsp;&nbsp;<i class='fa fa-fw fa-edit rbg '></i></a>";


// 获取当前结点的级别
var level = treeNode.level;
// 声明变量存储拼装好的按钮代码
var btnHTML = "";
// 判断当前节点的级别
if(level == 0) {
// 级别为 0 时是根节点,只能添加子节点
btnHTML = addBtn;
}
if(level == 1) {
// 级别为 1 时是分支节点,可以添加子节点、修改
btnHTML = addBtn + " " + editBtn;
// 获取当前节点的子节点数量
var length = treeNode.children.length;
// 如果没有子节点,可以删除
if(length == 0) {
btnHTML = btnHTML + " " + removeBtn;
}
}
if(level == 2) {
// 级别为 2 时是叶子节点,可以修改、删除
btnHTML = editBtn + " " + removeBtn;
}
// 找到附着按钮组的超链接
var anchorId = treeNode.tId + "_a";
//执行在超链接后面附加span元素的操作
$("#" + anchorId).after("<span id='" + btnGroupId + "'>" + btnHTML + "</span>");

}

 

 2.12 代码:把生成树形结构的代码封装到函数

  

function  generateTree() {
// 1.准备生成树形结构的JSON数据,数据的来源是发送ajax请求得到的
$.ajax({
"url": "menu/get/whole/tree.json",
"type": "post",
"dataType": "json",
"success": function (response) {
var result = response.result;
if (result == "SUCCESS") {
// 2.创建JSON对象用于存储对zTree所做的设置
var setting = {
"view":{
"addDiyDom": myAddDiyDom,
"addHoverDom":myAddHoverDom,
"removeHoverDom":myRemoveHoverDom
},
"data":{
"key":{
"url":"maomi"
}
}
};
// 3.从响应体获取数据
var zNodes = response.data;
// 4.初始化树形结构
$.fn.zTree.init($("#treeDemo"), setting, zNodes);
}
if (result == "FAILED") {
layer.msg(response.message);
}
}
});
}

  menu-page.jsp页面上调用函数即可:

$(function () {

// 调用专门封装好的函数初始化树形结构
generatePage();

});

 

3 菜单维护:添加子节点

3.1 目标

  

 

 

3.2 思路

  

3.3 代码:前端

3.3.1 给 “+” 按钮添加class值

  

var addBtn = "<a id='" + treeNode.id + "' class='addBtn btn btn-info dropdown-toggle btn-xs' style='margin-left:10px;padding-top:0px;' title='添加子节点'>&nbsp;&nbsp;<i class='fa fa-fw fa-plus rbg '></i></a>";

  

  顺便把另外两个按钮也加上:

var removeBtn = "<a id='" + treeNode.id + "' class='removeBtn btn btn-info dropdown-toggle btn-xs' style='margin-left:10px;padding-top:0px;' title=' 删 除 节 点 '>&nbsp;&nbsp;<i class='fa fa-fw fa-times rbg '></i></a>";
var editBtn = "<a id='" + treeNode.id + "' class='editBtn btn btn-info dropdown-toggle btn-xs' style='margin-left:10px;padding-top:0px;' title=' 修 改 节 点 '>&nbsp;&nbsp;<i class='fa fa-fw fa-edit rbg '></i></a>";

 

 3.3.2 给 “+” 按钮绑定单击响应函数

menu-page.jsp:

// 给添加子结点按钮绑定单击响应函数
$("#treeDemo").on("click", ".addBtn", function () {
// 将当前结点的id,作为新节点的pid保存到全局变量
window.pid = this.id;
// 打开模态框,将当前结点的id,作为新节点的pid
$("#menuAddModal").modal("show");
return false;
});

 

 3.3.3 给添加子结点的模态框中的保存按钮绑定单击响应函数

menu-page.jsp:

// 给添加子结点的模态框中的保存按钮绑定单击响应函数
$("#menuSaveBtn").click(function () {
// 收集表单项中用户输入的数据
var name = $.trim($("#menuAddModal [name=name]").val());
var url = $.trim($("#menuAddModal [name=url]").val());
// 单选按钮要定位到“被选中”的那一个
var icon = $("#menuAddModal [name=icon]:checked").val();
// 发送ajax请求
$.ajax({
"url": "menu/save.json",
"type": "post",
"data": {
"pid": window.pid,
"name": name,
"url": url,
"icon": icon
},
"dataType": "json",
"success": function (response) {
var result = response.result;
if (result == "SUCCESS") {
layer.msg("操作成功!");
// 重新加载树形结构
generateTree();
}
if (result == "FAILED") {
layer.msg("操作失败!" + response.message);
}
},
"error": function (response) {
layer.msg(response.status + " " + response.statusText);
}
});
$("#menuAddModal").modal("hide");
// 清空表单
$("#menuResetBtn").click();
});

 

3.4 代码:后端

handler方法:

@RequestMapping("/menu/save.json")
public ResultEntity<String> saveMenu(Menu menu){
menuService.saveMenu(menu);
return ResultEntity.successWithoutData();
}

 menuService方法:

void saveMenu(Menu menu);

 menuServiceImpl方法:

@Override
public void saveMenu(Menu menu) {
menuMapper.insert(menu);
}

 

 

 

4 菜单维护:更新节点

4.1 目标

  

 

 

 

4.2 思路

 

 

 

4.3 代码:前端

4.3.1 给 “修改” 按钮绑定单击响应函数

menu-page.jsp:


// 给添加子结点按钮绑定单击响应函数
$("#treeDemo").on("click", ".editBtn", function () {
// 将当前节点的 id 保存到全局变量
window.id = this.id;
// 打开模态框
$("#menuEditModal").modal("show");
// 获取 zTreeObj 对象
var zTreeObj = $.fn.zTree.getZTreeObj("treeDemo");
// 根据id属性查询节点对象
// 用来搜索节点的属性名
var key = "id";
// 用来搜索节点的属性值
var value = window.id;
var currentNode = zTreeObj.getNodeByParam(key, value);
// 回显表单数据
$("#menuEditModal [name=name]").val(currentNode.name);
$("#menuEditModal [name=url]").val(currentNode.url);
// 回显 radio 可以这样理解:被选中的 radio 的 value 属性可以组成一个数组,
// 然后再用这个数组设置回 radio,就能够把对应的值选中
$("#menuEditModal [name=icon]").val([currentNode.icon]);
return false;
});

 

4.3.2 给更新模态框中的更新按钮绑定单击响应函数

menu-page.jsp:


$("#menuEditBtn").click(function () {
// 收集表单数据
var name = $("#menuEditModal [name=name]").val();
var url = $("#menuEditModal [name=url]").val();
// 回显 radio 可以这样理解:被选中的 radio 的 value 属性可以组成一个数组,
// 然后再用这个数组设置回 radio,就能够把对应的值选中
var icon = $("#menuEditModal [name=icon]:checked").val();
// 发送ajax请求
$.ajax({
"url": "menu/update.json",
"type": "post",
"data": {
"id": window.id,
"name": name,
"url": url,
"icon": icon
},
"dataType": "json",
"success": function (response) {
var result = response.result;
if (result == "SUCCESS") {
layer.msg("操作成功!");
// 重新加载树形结构
generateTree();
}
if (result == "FAILED") {
layer.msg("操作失败!" + response.message);
}
},
"error": function (response) {
layer.msg(response.status + " " + response.statusText);
}
});
$("#menuEditModal").modal("hide");
});

 

4.4 代码:后端

handler代码:

@RequestMapping("/menu/update.json")
public ResultEntity<String> updateMenu(Menu menu){
menuService.updateMenu(menu);

return ResultEntity.successWithoutData();
}

 menuService方法:

void updateMenu(Menu menu);

 menuServiceImpl方法:

@Override
public void updateMenu(Menu menu) {
// 由于pid没有传入所以要选择有选择的更新
menuMapper.updateByPrimaryKeySelective(menu);
}

 

 

5 菜单维护:删除节点

5.1 目标

  删除当前节点

5.2 思路

  

 

 

5.3 代码:前端

5.3.1 给 “×” 按钮绑定单击响应函数

menu-page.jsp:



$("#treeDemo").on("click",".removeBtn",function () {
// 将当前节点的 id 保存到全局变量
window.id = this.id;
// 打开模态框
$("#menuConfirmModal").modal("show");
// 获取 zTreeObj 对象
var zTreeObj = $.fn.zTree.getZTreeObj("treeDemo");
// 根据 id 属性查询节点对象
// 用来搜索节点的属性名
var key = "id";
// 用来搜索节点的属性值
var value = window.id;
var currentNode = zTreeObj.getNodeByParam(key, value);
$("#removeNodeSpan").html(" <i class='"+currentNode.icon+"'></i>"+currentNode.name+"");
return false;
});

 5.3.2 给确认模态框中的按钮绑定单击响应函数

menu-page.jsp:

$("#confirmBtn").click(function(){
$.ajax({
"url":"menu/remove.json",
"type":"post",
"data":{
"id":window.id
},
"dataType":"json",
"success":function(response){
var result = response.result;
if(result == "SUCCESS") {
layer.msg("操作成功!");
// 重新加载树形结构,注意:要在确认服务器端完成保存操作后再刷新
// 否则有可能刷新不到最新的数据,因为这里是异步的
generateTree();
}
if(result == "FAILED") {
layer.msg("操作失败!"+response.message);
}
},
"error":function(response){
layer.msg(response.status+" "+response.statusText);
}
});
// 关闭模态框
$("#menuConfirmModal").modal("hide");
});

 

 5.4 代码:后端

handler方法:

@RequestMapping("/menu/remove.json")
public ResultEntity<String> removeMenu(@RequestParam("id") Integer id) {
menuService.removeMenu(id);
return ResultEntity.successWithoutData();
}

 menuService方法:

void removeMenu(Integer id);

 menuServiceImpl方法:

@Override
public void removeMenu(Integer id) {
menuMapper.deleteByPrimaryKey(id);
}

 

 

- @RestController = @Controller + @ResponseBody

  MenuHandler.java、RoleHandler.java都进行 注解的修改。

 

 

09-后台管理系统-分配

1 权限控制

  权限控制机制的本质就是 “用钥匙开锁”。

 

 

 

2 给Admin分配Role

2.1 目标

  

2.2 思路

  

 

2.3 代码:前往分配页面

2.3.1 创建保存 Admin-Role 关联关系的数据库表

  

  

2.3.2 修改 “分配” 按钮

  

旧代码:

<button type="button" class="btn btn-success btn-xs">
<i class="glyphicon glyphicon-check"></i>
</button>

 新代码:

<a href="assign/to/assign/role/page.html?adminId=${admin.id }&pageNum=${requestScope.pageInfo.pageNum }&keyword=${param.keyword }" class="btn btn-success btn-xs">
<i class="glyphicon glyphicon-check"></i>
</a>

 

2.3.3 创建AssignHandler

  

package org.example.mvc.handler;

import org.example.entity.Role;
import org.example.service.api.AdminService;
import org.example.service.api.RoleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import java.util.List;

/**
* @author 123
* date 2023-02-05
*/
@Controller
public class AssignHandler {

@Autowired
private AdminService adminService;

@Autowired
private RoleService roleService;

@RequestMapping("/assign/to/assign/role/page.html")
public String toAssignRolePage(
@RequestParam("adminId") Integer adminId,
ModelMap modelMap
) {
// 1.查询已分配角色
List<Role> assignedRoleList = roleService.getAssignedRole(adminId);
// 2.查询未分配角色
List<Role> unAssignedRoleList = roleService.getUnAssignedRole(adminId);
// 3.存入模型(本质上其实是:request.setAttribute("attrName",attrValue);
modelMap.addAttribute("assignedRoleList", assignedRoleList);
modelMap.addAttribute("unAssignedRoleList", unAssignedRoleList);
return "assign-role";
}

}

 

2.3.4 RoleService中的方法

RoleService:

List<Role> getAssignedRole(Integer adminId);

List<Role> getUnAssignedRole(Integer adminId);

RoleServiceImpl:

@Override
public List<Role> getAssignedRole(Integer adminId) {
return roleMapper.selectAssignedRole(adminId);
}

@Override
public List<Role> getUnAssignedRole(Integer adminId) {
return roleMapper.selectUnAssignedRole(adminId);
}

 

2.3.5 SQL语句

  RoleMapper.java:

  

List<Role> selectAssignedRole(Integer adminId);

List<Role> selectUnAssignedRole(Integer adminId);

 

  RoleMapper.xml:

  

<select id="selectAssignedRole" resultMap="BaseResultMap">
select id,name from t_role where id in (select role_id from inner_admin_role where admin_id=#{adminId})
</select>
<select id="selectUnAssignedRole" resultMap="BaseResultMap">
select id,name from t_role where id not in (select role_id from inner_admin_role where admin_id=#{adminId})
</select>

 

2.3.6 在页面上显示角色数据

  

  页面具体代码:    

    

<%--
Created by IntelliJ IDEA.
User: 123
Date: 2023/1/15
Time: 14:59
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html lang="zh-CN">
<%@include file="/WEB-INF/include-head.jsp" %>

<body>
<%@include file="/WEB-INF/include-nav.jsp" %>
<div class="container-fluid">
<div class="row">
<%@include file="/WEB-INF/include-sidebar.jsp" %>
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
<ol class="breadcrumb">
<li><a href="#">首页</a></li>
<li><a href="#">数据列表</a></li>
<li class="active">分配角色</li>
</ol>
<div class="panel panel-default">
<div class="panel-body">
<form type="post" role="form" class="form-inline">

<div class="form-group">
<%--@declare id="exampleinputpassword1"--%><label for="exampleInputPassword1">未分配角色列表</label><br>
<select class="form-control" multiple="multiple" size="10" style="width:100px;overflow-y:auto;">
<c:forEach items="${requestScope.unAssignedRoleList }" var="role">
<option value="${role.id }">${role.name }</option>
</c:forEach>
</select>
</div>
<div class="form-group">
<ul>
<li class="btn btn-default glyphicon glyphicon-chevron-right"></li>
<br>
<li class="btn btn-default glyphicon glyphicon-chevron-left" style="margin-top:20px;"></li>
</ul>
</div>
<div class="form-group" style="margin-left:40px;">
<label for="exampleInputPassword1">已分配角色列表</label><br>
<select name="roleId" class="form-control" multiple="multiple" size="10" style="width:100px;overflow-y:auto;">
<c:forEach items="${requestScope.assignedRoleList }" var="role">
<option value="${role.id }">${role.name }</option>
</c:forEach>
</select>
</div>

</form>
</div>
</div>
</div>
</div>
</div>
</body>
</html>

2.3.7 调整表单让表单能够提交数据

  参考下面文件:

  

<%--
Created by IntelliJ IDEA.
User: 123
Date: 2023/1/15
Time: 14:59
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html lang="zh-CN">
<%@include file="/WEB-INF/include-head.jsp" %>

<body>
<%@include file="/WEB-INF/include-nav.jsp" %>
<div class="container-fluid">
<div class="row">
<%@include file="/WEB-INF/include-sidebar.jsp" %>
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
<ol class="breadcrumb">
<li><a href="#">首页</a></li>
<li><a href="#">数据列表</a></li>
<li class="active">分配角色</li>
</ol>
<div class="panel panel-default">
<div class="panel-body">
<form action="assign/do/role/assign.html" type="post" role="form" class="form-inline">
<input type="hidden" name="adminId" value="${param.adminId }">
<input type="hidden" name="pageNum" value="${param.pageNum }">
<input type="hidden" name="keyword" value="${param.keyword }">

<div class="form-group">
<%--@declare id="exampleinputpassword1"--%><label for="exampleInputPassword1">未分配角色列表</label><br>
<select class="form-control" multiple="multiple" size="10" style="width:100px;overflow-y:auto;">
<c:forEach items="${requestScope.unAssignedRoleList }" var="role">
<option value="${role.id }">${role.name }</option>
</c:forEach>
</select>
</div>
<div class="form-group">
<ul>
<li id="toRightBtn" class="btn btn-default glyphicon glyphicon-chevron-right"></li>
<br>
<li id="toLeftBtn" class="btn btn-default glyphicon glyphicon-chevron-left" style="margin-top:20px;"></li>
</ul>
</div>
<div class="form-group" style="margin-left:40px;">
<label for="exampleInputPassword1">已分配角色列表</label><br>
<select name="roleIdList" class="form-control" multiple="multiple" size="10" style="width:100px;overflow-y:auto;">
<c:forEach items="${requestScope.assignedRoleList }" var="role">
<option value="${role.id }">${role.name }</option>
</c:forEach>
</select>
</div>
<button type="submit" style="width: 150px;" class="btn btn-lg btn-success btn-block">保存</button>
</form>
</div>
</div>
</div>
</div>
</div>
</body>
</html>

 

 

2.3.8 jQuery代码

  

<%--
Created by IntelliJ IDEA.
User: 123
Date: 2023/1/15
Time: 14:59
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html lang="zh-CN">
<%@include file="/WEB-INF/include-head.jsp" %>
<script type="text/javascript">

$(function () {
$("#toRightBtn").click(function(){
// select 是标签选择器
// :eq(0)表示选择页面上的第一个
// :eq(1)表示选择页面上的第二个
// “>”表示选择子元素
// :selected 表示选择“被选中的”option
// appendTo()能够将 jQuery 对象追加到指定的位置
$("select:eq(0)>option:selected").appendTo("select:eq(1)");
})
$("#toLeftBtn").click(function(){
// select 是标签选择器
// :eq(0)表示选择页面上的第一个
// :eq(1)表示选择页面上的第二个
// “>”表示选择子元素
// :selected 表示选择“被选中的”option
// appendTo()能够将 jQuery 对象追加到指定的位置
$("select:eq(1)>option:selected").appendTo("select:eq(0)");
})
});


</script>
<body>
<%@include file="/WEB-INF/include-nav.jsp" %>
<div class="container-fluid">
<div class="row">
<%@include file="/WEB-INF/include-sidebar.jsp" %>
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
<ol class="breadcrumb">
<li><a href="#">首页</a></li>
<li><a href="#">数据列表</a></li>
<li class="active">分配角色</li>
</ol>
<div class="panel panel-default">
<div class="panel-body">
<form action="assign/do/role/assign.html" type="post" role="form" class="form-inline">
<input type="hidden" name="adminId" value="${param.adminId }">
<input type="hidden" name="pageNum" value="${param.pageNum }">
<input type="hidden" name="keyword" value="${param.keyword }">

<div class="form-group">
<%--@declare id="exampleinputpassword1"--%><label for="exampleInputPassword1">未分配角色列表</label><br>
<select class="form-control" multiple="multiple" size="10" style="width:100px;overflow-y:auto;">
<c:forEach items="${requestScope.unAssignedRoleList }" var="role">
<option value="${role.id }">${role.name }</option>
</c:forEach>
</select>
</div>
<div class="form-group">
<ul>
<li id="toRightBtn" class="btn btn-default glyphicon glyphicon-chevron-right"></li>
<br>
<li id="toLeftBtn" class="btn btn-default glyphicon glyphicon-chevron-left" style="margin-top:20px;"></li>
</ul>
</div>
<div class="form-group" style="margin-left:40px;">
<label for="exampleInputPassword1">已分配角色列表</label><br>
<select name="roleIdList" class="form-control" multiple="multiple" size="10" style="width:100px;overflow-y:auto;">
<c:forEach items="${requestScope.assignedRoleList }" var="role">
<option value="${role.id }">${role.name }</option>
</c:forEach>
</select>
</div>
<button type="submit" style="width: 150px;" class="btn btn-lg btn-success btn-block">保存</button>
</form>
</div>
</div>
</div>
</div>
</div>
</body>
</html>

 

 

2.4 代码:执行分配

2.4.1 handler方法

AssignHandler.java:

 

@RequestMapping("/assign/do/role/assign.html")
public String saveAdminRoleRelationship(
@RequestParam("adminId") Integer adminId,
@RequestParam("pageNum") Integer pageNum,
@RequestParam("keyword") String keyword,
// 我们允许用户在页面上取消所有已分配角色再提交表单,所以可以不提供roleIdList 请求参数
// 设置 required=false 表示这个请求参数不是必须的
@RequestParam(value = "roleIdList", required = false) List<Integer> roleIdList
) {
adminService.saveAdminRoleRelationship(adminId, roleIdList);
return "redirect:/admin/get/page.html?pageNum=" + pageNum + "&keyword=" + keyword;
}

 

2.4.2 service方法

AdminService:

void saveAdminRoleRelationship(Integer adminId, List<Integer> roleIdList);
AdminServiceImpl:


@Override
public void saveAdminRoleRelationship(Integer adminId, List<Integer> roleIdList) {
// 为了简化操作:先根据 adminId 删除旧的数据,再根据 roleIdList 保存全部新的数据
// 1.根据 adminId 删除旧的关联关系数据
adminMapper.deleteOLdRelationship(adminId);
// 2.根据 roleIdList 和 adminId 保存新的关联关系
if (roleIdList != null && roleIdList.size() > 0) {
adminMapper.insertNewRelationship(adminId, roleIdList);
}
}

 

2.4.3 SQL语句

  adminmapper.java:

  

void deleteOLdRelationship(Integer adminId);

void insertNewRelationship(
@Param("adminId") Integer adminId,@Param("roleIdList") List<Integer> roleIdList
);

 

  adminmapper.xml:

  

<delete id="deleteOLdRelationship">
delete from inner_admin_role where admin_id=#{adminId}
</delete>
<insert id="insertNewRelationship">
insert into inner_admin_role(admin_id,role_id) values
<foreach collection="roleIdList" item="roleId" separator=",">(#{adminId},#{roleId})</foreach>
</insert>

 

报错:

idea控制台异常:

 

org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.binding.BindingException: Parameter 'roleIdList' not found. Available parameters are [0, 1, param1, param2]
at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:75)
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:371)
at com.sun.proxy.$Proxy14.insert(Unknown Source)
at org.mybatis.spring.SqlSessionTemplate.insert(SqlSessionTemplate.java:240)
at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:51)
at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:52)
at com.sun.proxy.$Proxy15.insertNewRelationship(Unknown Source)
at org.example.service.impl.AdminServiceImpl.saveAdminRoleRelationship(AdminServiceImpl.java:145)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:333)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
at com.sun.proxy.$Proxy18.saveAdminRoleRelationship(Unknown Source)
at org.example.mvc.handler.AssignHandler.saveAdminRoleRelationship(AssignHandler.java:37)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:133)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:97)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:849)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:760)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:967)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:626)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:733)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:690)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:373)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1589)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)

解决:

出现该报错的大部分原因是mapper.xml文件中的参数没有对应匹配。

当传入参数只有一个时候,就不需要设定@Param。接口参数只有一个,不管接口参数名是什么,这个时候#{xxx}没有限制,可以是0,param1,也可以是aaa,bbb。可以不加注解。

当传入的的参数>1的时候,就会出现该报错:mybatis的参数集就是上边说的默认值[0, 1, param1, param2],如果你不用默认值,就需要加上@Param注解起别名。一旦加了注解,mybatis的参数集就和第一种情况一样了。

 

2.4.4 修正Bug

  

 

assign-role.jsp:

 

<%--
Created by IntelliJ IDEA.
User: 123
Date: 2023/1/15
Time: 14:59
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html lang="zh-CN">
<%@include file="/WEB-INF/include-head.jsp" %>
<script type="text/javascript">

$(function () {
$("#toRightBtn").click(function(){
// select 是标签选择器
// :eq(0)表示选择页面上的第一个
// :eq(1)表示选择页面上的第二个
// “>”表示选择子元素
// :selected 表示选择“被选中的”option
// appendTo()能够将 jQuery 对象追加到指定的位置
$("select:eq(0)>option:selected").appendTo("select:eq(1)");
})
$("#toLeftBtn").click(function(){
// select 是标签选择器
// :eq(0)表示选择页面上的第一个
// :eq(1)表示选择页面上的第二个
// “>”表示选择子元素
// :selected 表示选择“被选中的”option
// appendTo()能够将 jQuery 对象追加到指定的位置
$("select:eq(1)>option:selected").appendTo("select:eq(0)");
})
});
$("#submitBtn").click(function(){
// 在提交表单前把“已分配”部分的 option 全部选中
$("select:eq(1)>option").prop("selected","selected");
});

</script>
<body>
<%@include file="/WEB-INF/include-nav.jsp" %>
<div class="container-fluid">
<div class="row">
<%@include file="/WEB-INF/include-sidebar.jsp" %>
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
<ol class="breadcrumb">
<li><a href="#">首页</a></li>
<li><a href="#">数据列表</a></li>
<li class="active">分配角色</li>
</ol>
<div class="panel panel-default">
<div class="panel-body">
<form action="assign/do/role/assign.html" type="post" role="form" class="form-inline">
<input type="hidden" name="adminId" value="${param.adminId }">
<input type="hidden" name="pageNum" value="${param.pageNum }">
<input type="hidden" name="keyword" value="${param.keyword }">

<div class="form-group">
<%--@declare id="exampleinputpassword1"--%><label for="exampleInputPassword1">未分配角色列表</label><br>
<select class="form-control" multiple="multiple" size="10" style="width:100px;overflow-y:auto;">
<c:forEach items="${requestScope.unAssignedRoleList }" var="role">
<option value="${role.id }">${role.name }</option>
</c:forEach>
</select>
</div>
<div class="form-group">
<ul>
<li id="toRightBtn" class="btn btn-default glyphicon glyphicon-chevron-right"></li>
<br>
<li id="toLeftBtn" class="btn btn-default glyphicon glyphicon-chevron-left" style="margin-top:20px;"></li>
</ul>
</div>
<div class="form-group" style="margin-left:40px;">
<label for="exampleInputPassword1">已分配角色列表</label><br>
<select name="roleIdList" class="form-control" multiple="multiple" size="10" style="width:100px;overflow-y:auto;">
<c:forEach items="${requestScope.assignedRoleList }" var="role">
<option selected="selected" value="${role.id }">${role.name }</option>
</c:forEach>
</select>
</div>
<button id="submitBtn" type="submit" style="width: 150px;" class="btn btn-lg btn-success btn-block">保存</button>
</form>
</div>
</div>
</div>
</div>
</div>
</body>
</html>

 


3 给Role分配Auth

3.1 目标

  

 

3.2 思路

  

    

 

3.3 代码:数据库(Auth建表)

  

INSERT INTO `fundingonline_study`.`t_auth`(`id`, `name`, `title`, `category_id`) VALUES (1, '', '用户模块', NULL);
INSERT INTO `fundingonline_study`.`t_auth`(`id`, `name`, `title`, `category_id`) VALUES (2, 'user:delete', '删除', 1);
INSERT INTO `fundingonline_study`.`t_auth`(`id`, `name`, `title`, `category_id`) VALUES (3, 'user:get', '查询', 1);
INSERT INTO `fundingonline_study`.`t_auth`(`id`, `name`, `title`, `category_id`) VALUES (4, '', '角色模块', NULL);
INSERT INTO `fundingonline_study`.`t_auth`(`id`, `name`, `title`, `category_id`) VALUES (5, 'role:delete', '删除', 4);
INSERT INTO `fundingonline_study`.`t_auth`(`id`, `name`, `title`, `category_id`) VALUES (6, 'role:get', '查询', 4);
INSERT INTO `fundingonline_study`.`t_auth`(`id`, `name`, `title`, `category_id`) VALUES (7, 'role:add', '新增', 4);
INSERT INTO `fundingonline_study`.`t_auth`(`id`, `name`, `title`, `category_id`) VALUES (8, 'user:save', '保存', 1);
INSERT INTO `fundingonline_study`.`t_auth`(`id`, `name`, `title`, `category_id`) VALUES (9, 'test', 'test', NULL);
INSERT INTO `fundingonline_study`.`t_auth`(`id`, `name`, `title`, `category_id`) VALUES (10, 'test', 'test', NULL);
INSERT INTO `fundingonline_study`.`t_auth`(`id`, `name`, `title`, `category_id`) VALUES (11, 'test', 'test', NULL);
INSERT INTO `fundingonline_study`.`t_auth`(`id`, `name`, `title`, `category_id`) VALUES (12, 'testTx', 'testTx', NULL);
INSERT INTO `fundingonline_study`.`t_auth`(`id`, `name`, `title`, `category_id`) VALUES (13, 'testTx', 'testTx', NULL);
INSERT INTO `fundingonline_study`.`t_auth`(`id`, `name`, `title`, `category_id`) VALUES (14, 'testTx', 'testTx', NULL);
INSERT INTO `fundingonline_study`.`t_auth`(`id`, `name`, `title`, `category_id`) VALUES (15, 'testTx', 'testTx', NULL);
INSERT INTO `fundingonline_study`.`t_auth`(`id`, `name`, `title`, `category_id`) VALUES (16, 'test', 'test', NULL);
INSERT INTO `fundingonline_study`.`t_auth`(`id`, `name`, `title`, `category_id`) VALUES (17, 'testTx', 'testTx', NULL);
INSERT INTO `fundingonline_study`.`t_auth`(`id`, `name`, `title`, `category_id`) VALUES (18, 'test', 'test', NULL);

  

 

 

  

 

 

 

3.4  代码:打开模态框

3.4.1 准备模态框

  

  

<%@include file="/WEB-INF/modal-role-assign-auth.jsp" %>

 

3.4.2 给 “√” 按钮绑定单击响应函数

  

  在FillTableBody()函数中,修改checkBtn:

var checkBtn = "<button id='" + roleId + "' type='button' class='btn btn-success btn-xs checkBtn'><i class=' glyphicon glyphicon-check'></i></button>";

 

  

// 13.给分配权限按钮绑定单击响应函数
$("#rolePageBody").on("click", ".checkBtn", function () {
// 把当前角色id存入全局变量
window.roleId = this.id;
// 打开模态框
$("#assignModal").modal("show");
// 在模态框中装载树 Auth 的形结构数据
fillAuthTree();
});

  

3.4.3 加入zTree的环境

  

<link rel="stylesheet" href="ztree/zTreeStyle.css"/>
<script type="text/javascript" src="ztree/jquery.ztree.all-3.5.min.js"></script>

  注:引入时,zTree的JavaScript库文件要放在 my-role.js 文件之前。

 

3.4.4 显示不正常分析

  

 

3.4.5 函数 fillAuthTree()

  

// 声明专门的函数用来分配Auth的模态框中显示Auth的树形结构数据
function fillAuthTree() {
// 1.发送 Ajax 请求查询 Auth 数据
var ajaxReturn = $.ajax({
"url":"assgin/get/all/auth.json",
"type":"post",
"dataType":"json",
"async":false
});
if(ajaxReturn.status != 200) {
layer.msg("请求处理出错响应状态码 是 : "+ajaxReturn.status+" 说 明 是 :"+ajaxReturn.statusText);
return ;
}
// 2.从响应结果中获取 Auth 的 JSON 数据
// 从服务器端查询到的 list 不需要组装成树形结构,这里我们交给 zTree 去组装
var authList = ajaxReturn.responseJSON.data;
// 3.准备对 zTree 进行设置的 JSON 对象
var setting = {
"data": {
"simpleData": {
// 开启简单 JSON 功能
"enable": true,
// 使用 categoryId 属性关联父节点,不用默认的 pId 了
"pIdKey": "categoryId"
},
"key": {
// 使用 title 属性显示节点名称,不用默认的 name 作为属性名了
"name": "title"
}
},
"check": {
"enable": true
}
};
// 4.生成树形结构
// <ul id="authTreeDemo" class="ztree"></ul>
$.fn.zTree.init($("#authTreeDemo"), setting, authList);
// 获取 zTreeObj 对象
var zTreeObj = $.fn.zTree.getZTreeObj("authTreeDemo");
// 调用 zTreeObj 对象的方法,把节点展开
zTreeObj.expandAll(true);

// 5.查询已分配的 Auth 的 id 组成的数组
ajaxReturn = $.ajax({
"url":"assign/get/assigned/auth/id/by/role/id.json",
"type":"post",
"data":{
"roleId":window.roleId
},
"dataType":"json",
"async":false
});
if(ajaxReturn.status != 200) {
layer.msg("请求处理出错!响应状态码是:"+ajaxReturn.status+"说明是:"+ajaxReturn.statusText);
return ;
}
// 从响应结果中获取 authIdArray
var authIdArray = ajaxReturn.responseJSON.data;
// 6.根据 authIdArray 把树形结构中对应的节点勾选上
// ①遍历 authIdArray
for(var i = 0; i < authIdArray.length; i++) {
var authId = authIdArray[i];
// ②根据 id 查询树形结构中对应的节点
var treeNode = zTreeObj.getNodeByParam("id", authId);
// ③将 treeNode 设置为被勾选
// checked 设置为 true 表示节点勾选
var checked = true;
// checkTypeFlag 设置为 false,表示不“联动”,不联动是为了避免把不该勾选的勾选上
var checkTypeFlag = false;
// 执行
zTreeObj.checkNode(treeNode, checked, checkTypeFlag);
}
}

 

3.4.6 创建角色到权限之间关联关系的中间表

  

INSERT INTO `fundingonline_study`.`inner_role_auth`(`id`, `role_id`, `auth_id`) VALUES (1, 1, 2);
INSERT INTO `fundingonline_study`.`inner_role_auth`(`id`, `role_id`, `auth_id`) VALUES (8, 238, 1);
INSERT INTO `fundingonline_study`.`inner_role_auth`(`id`, `role_id`, `auth_id`) VALUES (9, 238, 8);
INSERT INTO `fundingonline_study`.`inner_role_auth`(`id`, `role_id`, `auth_id`) VALUES (12, 239, 4);
INSERT INTO `fundingonline_study`.`inner_role_auth`(`id`, `role_id`, `auth_id`) VALUES (13, 239, 5);

 

3.4.7 根据 role_id 查询 auth_id

// Assignhandler方法

 

@RequestMapping("/assign/get/assigned/auth/id/by/role/id.json")
public ResultEntity<List<Integer>> getAssignedAuthIdByRoleId(
@RequestParam("roleId") Integer roleId) {
List<Integer> authIdList = authService.getAssignedAuthIdByRoleId(roleId);
return ResultEntity.successWithData(authIdList);
}

 // AuthService.java

 

List<Integer> getAssignedAuthIdByRoleId(Integer roleId);

 // AuthServiceImpl.java

 

@Override
public List<Integer> getAssignedAuthIdByRoleId(Integer roleId) {
return authMapper.selectAssignedAuthIdByRoleId(roleId);
}

 // AuthMapper.java

 

List<Integer> selectAssignedAuthIdByRoleId(Integer roleId);

// AuthMapper.xml

 

<select id="selectAssignedAuthIdByRoleId" resultType="int">
select auth_id from inner_role_auth where role_id=#{roleId}
</select>

 

3.5 代码:执行分配

3.5.1 给 “分配” 按钮绑定单击响应函数

  

// 14.给分配权限模态框中的“分配”按钮绑定单击响应函数
$("#assignBtn").click(function () {
// ①收集树形结构的各个节点中被勾选的节点
// [1]声明一个专门的数组存放 id
var authIdArray = [];
// [2]获取 zTreeObj 对象
var zTreeObj = $.fn.zTree.getZTreeObj("authTreeDemo");
// [3]获取全部被勾选的节点
var checkedNodes = zTreeObj.getCheckedNodes();
// [4]遍历 checkedNodes
for (var i = 0; i < checkedNodes.length; i++) {
var checkedNode = checkedNodes[i];
var authId = checkedNode.id;
authIdArray.push(authId);
}
// ②发送请求执行分配
var requestBody = {
"authIdArray":authIdArray,
// 为了服务器端 handler 方法能够统一使用 List<Integer>方式接收数据,roleId 也存入数组
"roleId":[window.roleId]
}
requestBody = JSON.stringify(requestBody);
$.ajax({
"url": "assign/do/role/assign/auth.json",
"type": "post",
"data": requestBody,
"contentType": "application/json;charset=UTF-8",
"dataType": "json",
"success": function (response) {
var result = response.result;
if (result == "SUCCESS") {
layer.msg("操作成功!");
}
if (result == "FAILED") {
layer.msg("操作失败!" + response.message);
}
},
"error": function (response) {
layer.msg(response.status + " " + response.statusText);
}
});
$("#assignModal").modal("hide");
});

 

3.5.2 后端执行分配,保存关联关系

// AssignHanlder.java

 

@ResponseBody
@RequestMapping("/assign/do/role/assign/auth.json")
public ResultEntity<String> saveRoleAuthRelathinship(
@RequestBody Map<String, List<Integer>> map) {
authService.saveRoleAuthRelathinship(map);
return ResultEntity.successWithoutData();
}

// RoleService.java

 

void saveRoleAuthRelathinship(Map<String, List<Integer>> map);

 // RoleServiceImpl.java

 

@Override
public void saveRoleAuthRelathinship(Map<String, List<Integer>> map) {
// 1.获取 roleId 的值
List<Integer> roleIdList = map.get("roleId");
Integer roleId = roleIdList.get(0);
// 2.删除旧关联关系数据
authMapper.deleteOldRelationship(roleId);
// 3.获取 authIdList
List<Integer> authIdList = map.get("authIdArray");
// 4.判断 authIdList 是否有效
if (authIdList != null && authIdList.size() > 0) {
authMapper.insertNewRelationship(roleId, authIdList);
}
}

 // RoleMapper.java

 

void deleteOldRelationship(Integer roleId);

void insertNewRelationship(@Param("roleId") Integer roleId, @Param("authIdList") List<Integer> authIdList);

 // RoleMapper.xml

 

<delete id="deleteOldRelationship">
delete from inner_role_auth where role_id=#{roleId}
</delete>
<insert id="insertNewRelationship">
insert into inner_role_auth(auth_id,role_id) values
<foreach collection="authIdList" item="authId"
separator=",">(#{authId},#{roleId})
</foreach>
</insert>

 

 

4 给Menu分配Auth

 略

 

 

 

 

 

 

 

 

 

 

报错

0、将两个项目放一个界面展示

 

 

 

 

 

1、若将*Mapper.xml放在resources/mybatis/mapper下,sqlSessionFactoryBean里配置mapperLocation的value=“mybatis/mapper/*Mapper.xml”,就报错无法识别该路径

 

 

若如下,就能识别*Mapper.xml:

 

 

解决方法:https://blog.csdn.net/m0_72934541/article/details/126699156

 

 

重新建包,一级一级建,成果:

 

 

 

2、

 

解决方法:加载配置文件将tx的路径加上(

// 加载Spring配置文件的注解
@ContextConfiguration(locations = {"classpath:spring-persist-mybatis.xml", "classpath:spring-persist-tx.xml"})

 

3、

到这里卡主不动,数据未提交至数据库。

解决方法:可能是AdminService或AdminServiceImpl文件编写错误。

 

 4、spring-persist-tx.xml中的ref="dataSource"爆红

解决办法:

  • 产生的原因是spring的配置文件没有导入,需要手动导入
  • 点击File——》Project structure——》
  • 点击你需要添加的模块的配置文件(spring)——》点击+——》勾选上配置文件——》点击OK——》点击apply

 

5、测试SSM整合:运行index.jsp页面报404

 

解决方法:URL改为http://localhost:7080//atcrowdfunding002_admin_webui_war_exploded/

 

因为:

 

 

解决方法二:将上图修改为“/”

 

 

 

备份:

 

 

INSERT INTO `fundingonline_study`.`t_menu`(`id`, `pid`, `name`, `url`, `icon`) VALUES (1, NULL, '系统权限菜单', NULL, 'glyphicon\r\nglyphicon-th-list');INSERT INTO `fundingonline_study`.`t_menu`(`id`, `pid`, `name`, `url`, `icon`) VALUES (2, 1, '控 制 面 板', 'main.htm', 'glyphicon glyphicon glyphicon-tasks');INSERT INTO `fundingonline_study`.`t_menu`(`id`, `pid`, `name`, `url`, `icon`) VALUES (3, 1, '权限管理', NULL, 'glyphicon glyphicon\r\nglyphicon-tasks');INSERT INTO `fundingonline_study`.`t_menu`(`id`, `pid`, `name`, `url`, `icon`) VALUES (4, 3, ' 用 户 维 护 ', 'user/index.htm', 'glyphicon\r\nglyphicon-user');INSERT INTO `fundingonline_study`.`t_menu`(`id`, `pid`, `name`, `url`, `icon`) VALUES (5, 3, ' 角 色 维 护 ', 'role/index.htm', 'glyphicon\r\nglyphicon-king');INSERT INTO `fundingonline_study`.`t_menu`(`id`, `pid`, `name`, `url`, `icon`) VALUES (6, 3, ' 菜 单 维 护 ', 'permission/index.htm', 'glyphicon\r\nglyphicon-lock');INSERT INTO `fundingonline_study`.`t_menu`(`id`, `pid`, `name`, `url`, `icon`) VALUES (7, 1, ' 业 务 审 核 ', NULL, 'glyphicon\r\nglyphicon-ok');INSERT INTO `fundingonline_study`.`t_menu`(`id`, `pid`, `name`, `url`, `icon`) VALUES (8, 7, '实名认证审核', 'auth_cert/index.htm', 'glyphicon\r\nglyphicon-check');INSERT INTO `fundingonline_study`.`t_menu`(`id`, `pid`, `name`, `url`, `icon`) VALUES (9, 7, ' 广 告 审 核 ', 'auth_adv/index.htm', 'glyphicon\r\nglyphicon-check');INSERT INTO `fundingonline_study`.`t_menu`(`id`, `pid`, `name`, `url`, `icon`) VALUES (10, 7, ' 项 目 审 核 ', 'auth_project/index.htm', 'glyphicon\r\nglyphicon-check');INSERT INTO `fundingonline_study`.`t_menu`(`id`, `pid`, `name`, `url`, `icon`) VALUES (11, 1, ' 业 务 管 理 ', NULL, 'glyphicon\r\nglyphicon-th-large');INSERT INTO `fundingonline_study`.`t_menu`(`id`, `pid`, `name`, `url`, `icon`) VALUES (12, 11, ' 资 质 维 护 ', 'cert/index.htm', 'glyphicon\r\nglyphicon-picture');INSERT INTO `fundingonline_study`.`t_menu`(`id`, `pid`, `name`, `url`, `icon`) VALUES (13, 11, ' 分 类 管 理 ', 'certtype/index.htm', 'glyphicon\r\nglyphicon-equalizer');INSERT INTO `fundingonline_study`.`t_menu`(`id`, `pid`, `name`, `url`, `icon`) VALUES (14, 11, ' 流 程 管 理 ', 'process/index.htm', 'glyphicon\r\nglyphicon-random');INSERT INTO `fundingonline_study`.`t_menu`(`id`, `pid`, `name`, `url`, `icon`) VALUES (15, 11, ' 广 告 管 理 ', 'advert/index.htm', 'glyphicon\r\nglyphicon-hdd');INSERT INTO `fundingonline_study`.`t_menu`(`id`, `pid`, `name`, `url`, `icon`) VALUES (16, 11, ' 消 息 模 板 ', 'message/index.htm', 'glyphicon\r\nglyphicon-comment');INSERT INTO `fundingonline_study`.`t_menu`(`id`, `pid`, `name`, `url`, `icon`) VALUES (17, 11, ' 项 目 分 类 ', 'projectType/index.htm', 'glyphicon\r\nglyphicon-list');INSERT INTO `fundingonline_study`.`t_menu`(`id`, `pid`, `name`, `url`, `icon`) VALUES (18, 11, ' 项 目 标 签 ', 'tag/index.htm', 'glyphicon\r\nglyphicon-tags');INSERT INTO `fundingonline_study`.`t_menu`(`id`, `pid`, `name`, `url`, `icon`) VALUES (19, 1, ' 参 数 管 理 ', 'param/index.htm', 'glyphicon\r\nglyphicon-list-alt');
 

 

 

 

 

SpringSecurity

1 SpringSecrity框架用法介绍

  

  

 

2 权限管理过程中的相关概念

2.1 主体

   

 

 

 

2.2 认证

  

 

 

 

2.3 授权

  

 

 

  

 

 

3 权限管理的主流框架

3.1 SpringSecurity

  Spring技术栈的组成部分。

  

  

 

3.2 Shiro

  

 

 

 

 

4 使用配置类代替XML

 4.1 @Configuration 注解

  

  

  类标记了这个注解就可以使用这个代替spring的XML配置文件。

 

4.2 @Bean注解

  用来代替XML配置文件中的bean标签,下面两种形式效果一致:

  

  

 

 

5 HelloWorld 工程创建步骤

5.1 创建Maven的Web工程

  

5.2 加入SpringMVC环境需要的依赖

<!--这里并不是使用springMVC,仅仅是借助这个依赖把Spring的环境导入-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.20.RELEASE</version>
</dependency>
<!-- 引入 Servlet 容器中相关依赖 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<!-- JSP 页面使用的依赖 -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1.3-b06</version>
<scope>provided</scope>
</dependency>

 

5.3 创建SpringMVC 配置文件

  

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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-4.3.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd">

<context:component-scan
base-package="spring"></context:component-scan>

<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INS/views/"></property>
<property name="suffix" value=".jsp"></property>
</bean>

<mvc:annotation-driven></mvc:annotation-driven>
<mvc:default-servlet-handler/>

</beans>

 

5.4 在web.xml中配置DispatcherServlet

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

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

<servlet>
<servlet-name>springDispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>springDispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

</web-app>

 

5.5 创建包

  security.controller

 

5.6 从例子工程中复制Controller

  

 

 

 

  

package security.controller;

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

/**
* @author 123
* date 2023-02-07
*/
@Controller
public class AdminController {

@GetMapping("main.html")
public String main() {
return "main";
}

@RequestMapping("to/no/auth/page.html")
public String toNoAuthPage() {
return "no_auth";
}

}

 

5.7 加入webapp目录下文件

   

 

 

 

 

6 在HelloWorld基础上加入SpringSecurity

6.1 加入SpringSecurity依赖

<!-- 声明属性, 对 SpringSecurity 的版本进行统一管理 -->
<atguigu.spring.security.version>4.2.10.RELEASE</atguigu.spring.security.version>

 

<!-- SpringSecurity 标签库 -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>4.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>4.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>4.2.10.RELEASE</version>
</dependency>

 

6.2 加入SpringSecurity控制权限的Filter

  

<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<!--这个Filter要在所有Filter前面-->
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

 

  

 

 

 

6.3 加入配置类

  

 

 

 

6.4 效果

  

 

 

 

 

 

7  SpringSecurity操作实验

  

7.1 实验1:放行首页和静态资源

  

 重写后: 

  进一步修改:

    

 

 

  

 

 

 

 

 

.

.

.

实验9

 

 

 

 

09-后台管理系统-分配

1 SpringSecurity回顾

1.1 准备工作

  准备SpringMVC的环境,发送请求访问资源时完全没有权限。

1.2 加入SpringSecurity环境

1.2.1 加入SpringSecurity依赖

<!-- 声明属性, 对 SpringSecurity 的版本进行统一管理 -->
<atguigu.spring.security.version>4.2.10.RELEASE</atguigu.spring.security.version>


<!-- SpringSecurity 标签库 -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>4.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>4.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>4.2.10.RELEASE</version>
</dependency>

 

1.2.2 在web.xml中配置Filter

<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<!--这个Filter要在所有Filter前面-->
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

  

 

 

 

1.2.3 创建基于注解的配置类

package com.atguigu.crowd.mvc.config;

import com.ccctop.crowd.constant.CrowdConstant;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.access.AccessDeniedHandler;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebAppSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Bean
public BCryptPasswordEncoder getPasswordEncoder(){
return new BCryptPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder builder) throws Exception {
     // 与SpringSecurity环境下用户登录相关
        /*builder.inMemoryAuthentication().withUser("tom").password("123123").roles("ADMIN");*/
builder.userDetailsService(userDetailsService).passwordEncoder(getPasswordEncoder());
}

@Override
protected void configure(HttpSecurity security) throws Exception {
    // 与SpringSecurity环境下请求授权相关
        security.authorizeRequests()
.antMatchers("/admin/to/login/page.html", "/bootstrap/**", "/crowd/**", "/css/**", "/fonts/**", "/img/**", "/jquery/**", " /layer/**", "/script/**", "/ztree/**")
.permitAll()
.antMatchers("/admin/get/page.html")
.hasRole("经理")
.anyRequest()
.authenticated()
.and()
.exceptionHandling()
//.accessDeniedPage("/to/no/auth/page.html")
.accessDeniedHandler(new AccessDeniedHandler() {
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
httpServletRequest.setAttribute("message", CrowdConstant.MESSAGE_LOGIN_DENIED);
httpServletRequest.getRequestDispatcher("/WEB-INF/system-error.jsp").forward(httpServletRequest, httpServletResponse);
}
})
.and()
.csrf()
.disable()
.formLogin()
.loginPage("/admin/to/login/page.html")
.permitAll()
.loginProcessingUrl("/security/do/login.html")
.permitAll()
.usernameParameter("loginAcct")
.passwordParameter("userPswd")
.defaultSuccessUrl("/admin/to/main/page.html")
.and()
.logout()
.logoutUrl("/security/do/logout.html")
.logoutSuccessUrl("/admin/to/login/page.html")
     ;
}
}

 

1.3 放行首页和静态资源

  

 

1.4 未认证请求跳转到登录页面

  

  

 

 

  

 

 

 

1.5 实现完整的登录流程

 

 

1.5.1 设置表单

 

1.5.2 设置正确的账号、密码

 

1.6 退出登录

 

1.7 基于角色或权限实现访问控制

 

 

 

1.8 指定403页面

 

 

 

 

1.9 记住我-内存版

 

 

1.10 记住我-数据库版(不重要)

1.10.1 加入依赖  

 

1.10.2 创建数据库

 

1.10.3 配置数据源

 

 

1.10.4 在配置类中装配数据源

 

 

1.10.5 创建数据库表

 

 

1.11 查数据库完成认证

 

1.12 密码加密

1.12.1 认识SpringSecurity提供的加密接口

 

1.12.2 创建PasswordEncoder实现类

 

 

 

1.12.3 在SpringSecurity的配置类中装配MyPasswordEncoder

 

 

1.12.4 使用MyPasswordEncoder对象

 

 

1.12.5 潜在的问题

   

 

 

 

1.13 带盐值的加密

 

 

 

2 项目中加入SpringSecurity

2.1 加入SpringSecurity环境(-229)

2.1.1 依赖

  

parent  pom.xml:

 

<!-- 声明属性, 对 SpringSecurity 的版本进行统一管理 -->
<spring.security.version>4.2.10.RELEASE</spring.security.version>

 

<!-- SpringSecurity 标签库 -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>4.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.webjars.bower</groupId>
<artifactId>jquery</artifactId>
<version>3.2.1</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>4.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>4.2.10.RELEASE</version>
</dependency>

 component  pom.xml:

 

<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
</dependency>

 

2.1.2 在web.xml中配置DelegatingFilterProxy

webui  web.xml:

 

<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<!--这个Filter要在所有Filter前面-->
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

 

2.1.3 创建基于注解的配置类

// 表示当前类是一个配置类
@Configuration

// 启用Web环境下权限控制功能
@EnableWebSecurity
public class WebAppSecurityConfig extends WebSecurityConfigurerAdapter {

}

 

2.1.4 谁来把 WebAppSecurityConfig配置类 扫描到Ioc容器中

    

  

  

 

2.2 提出找不到bean的问题

  

 

2.3 分析上面那个问题

2.3.1 明确三大组件启动顺序

  

 

 

2.3.2 Filter查找IOC容器然后查找bean的工作机制

  

 

2.4 解决方案一:把两个IOC容器合二为一

  

 

2.5 解决方案二:改源码

  

  

  

  

  

 

2.6 意外收获

  

 

 

2.7 目标1:放行登录页和静态资源

2.7.1 思路

   

 

2.7.2 代码

  

  

package org.example.mvc.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

// 表示当前类是一个配置类
@Configuration

// 启用Web环境下权限控制功能
@EnableWebSecurity
public class WebAppSecurityConfig extends WebSecurityConfigurerAdapter {


@Override
protected void configure(HttpSecurity security) throws Exception {

security.authorizeRequests()
.antMatchers("/admin/to/login/page.html")
.permitAll()
.antMatchers("/bootstrap/**")
.permitAll()
.antMatchers("/crowd/**")
.permitAll()
.antMatchers("/css/**")
.permitAll()
.antMatchers("/fonts/**")
.permitAll()
.antMatchers("/img/**")
.permitAll()
.antMatchers("/jquery/**")
.permitAll()
.antMatchers("/layer/**")
.permitAll()
.antMatchers("/script/**")
.permitAll()
.antMatchers("/WEB-INF/**")
.permitAll()
.antMatchers("/ztree/**")
.permitAll()
.anyRequest()
.authenticated();
}
}

 

测试:进入登录“/admin/to/login/page.html” 报404 ?

此问题未解决,导致SpringSecurity版块未继续进行,未完成(229-258 未做)。

 

 

 

2.8 目标2:提交登录表单做内存认证

2.8.1 思路

  

 

 

2.8.2 操作1:设置表单

  

 

 

271-328未做

 

-----------------------------------------------------------------------------------------------------------------------------------

2 尚筹网会员系统总目标

环境-SpringBoot+springcloud

首页-众筹项目的展示

 

 

注册会员

 

 

会员登录

 

 

个人中心

 

 

 

 

发起众筹

 

 

参与别人的项目

 

 

 

 

 

 

后台进行业务审核

 

 

 

3 会员系统架构

3.1 架构图

 

 

3.2 需要创建的工程

atcrowdfunding07-member-parent
atcrowdfunding08-member-eureka
atcrowdfunding09-member-entity
atcrowdfunding10-member-mysql-provider
atcrowdfunding11-member-redis-provider
atcrowdfunding12-member-authentication-consumer
atcrowdfunding13-member-project-consumer
atcrowdfunding14-member-order-consumer
atcrowdfunding15-member-pay-consumer

atcrowdfunding16-member-zuul

atcrowdfunding17-member-api

 

 

 

 

 

4 parent工程配置pom.xml

<!--配置在父工程中要管理的依赖-->
<dependencyManagement>
<dependencies>
<!--导入SpringCloud需要用的依赖信息 ${spring-cloud.version}-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR2</version>
<type>pom</type>
<!--import依赖范围表示将spring-cloud-dependencies包中的依赖信息导入-->
<scope>import</scope>
</dependency>
<!--导入SpringBoot需要用的依赖信息-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.1.6.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.31</version>
</dependency>
</dependencies>
</dependencyManagement>

 

 

5 搭建环境约定

5.1 包名约定

  新创建的包都作为org.example的子包

5.2 主启动类类名

  CrowdMainClass

5.3 端口号

  

   

 

 

6 eureka工程

6.1 依赖

<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>

 

报错:

依赖爆红,提示没有版本,加入版本,提示没有该版本。

 

 

解决:

https://blog.csdn.net/qq_35760825/article/details/125153435

 

 

6.2 主启动类 CrowdMainClass

package org.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

/**
* @author 123
* date 2023-02-21
*/
@EnableEurekaServer
@SpringBootApplication
public class CrowdMainClass {

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

}

 

6.3 application.yml

server:
port: 1000
spring:
application:
name: atguigu-crowd-eureka
freemarker:
prefer-file-system-access: false
eureka:
instance:
hostname: localhost
client:
registerWithEureka: false # 自己就是注册中心,所以自己不注册自己
fetchRegistry: false # 自己就是注册中心,所以不需要“从注册中心取回信息”
serviceUrl: # 客户端访问 Eureka 时使用的地址
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

测试:

 

 

 

 

7 entity工程

7.1 实体类的进一步细分

  

  

  

  

  

 

7.2 创建包

  org.example.entity.po

  org.example.entity.vo

 

7.3 lombok

7.3.1 效果

 

 

 

7.3.2 lombok原理

 

 

7.3.3 Eclipse设置

 

7.3.3.1-2  替换为安装插件(因为是用的idea)

 

 

 

(逆向生成t_member数据库表,

给MemberPOMapper类添加@Service注解。)

generatorConfig.xml:

 

<javaModelGenerator targetProject=".\src\main\java"
targetPackage="org.example.entity.po">

 

<table tableName="t_member" domainObjectName="MemberPO" />
MemberPO类:

 

@AllArgsConstructor
@NoArgsConstructor
@Data
MemberPOMapper类:


@Service

 

 

测试报错:

解决:

idea中包的创建若连续创建(如:mapp.ex),则路径显示为“mapp.ex”,而非“mapp/ex”。

方法一:修改yml

 

 

 

方法二(我采用):

不修改yml文件,重新建mybatis/mapper,注意分两次建立,不可“bybatis.mapper”这样建立。

 

 

 

 

 

接下来 目录按照尚硅谷官方笔记进行补充(官方笔记保存在百度网盘中)

9.1.1(342-) 导入 spring-cloud-starter-openfeign 依赖失败

解决:

将阿里源切换回本地库,再加个与SpringBoot匹配的SpringCloud版本号

将这个方法的url切换成本地库的就为此问题解决方法:https://blog.csdn.net/qq_35760825/article/details/125153435

 

 

redis

345:学习redis

 https://www.cnblogs.com/ancestor/p/10816163.html

虚拟机密码:111

 

redis常用命令↓

Linux下载好redis后:

root下,启动redis服务:redis-server /myredis/redis.conf

(关闭服务器,再次打开服务器)

进入root路径: su root
密码:111
进入redis: redis-cli
测试命令行:ping
测试成功会得到:PONG

退出redies客户端:quit

 

对redis常规操作一套流程:

启动redis:redis-server /myredis/redis.conf

(关闭服务器,再次打开服务器)

查看redis是否还在运行:ps aux | grep redis

进入redis: redis-cli

测试命令行:ping
测试成功会得到:PONG

输入密码进行操作:auth 1

查看所有key-value:key *

设置key-value:(config)set  key值 value值

查看key对应的value:(config)get key值

退出redies客户端:quit

关闭redis:redis-cli shutdown

删除redis:

    rm -f /usr/local/redis/bin/redis*

    rm -rf redis

 

bind必须配成“192.168.201.100”,因为:(此处说法应该是错的,只要是外网ip就可,并非必须192.168.201.100)

打错了,应该是:192.168.201.100

 

 

 

 

配置静态ip-只为方便(不然每次启动虚拟机都会变) 非必须(似乎失败):

https://blog.csdn.net/dengxw00/article/details/128551924

TYPE="Ethernet"

PROXY_METHOD="none"

BROWSER_ONLY="no"

BOOTPROTO="static"

DEFROUTE="yes"

IPV4_FAILURE_FATAL="no"

IPV6INIT="yes"

IPV6_AUTOCONF="yes"

IPV6_DEFROUTE="yes"

IPV6_FAILURE_FATAL="no"

IPV6_ADDR_GEN_MODE="stable-privacy"

NAME="ens33"

UUID="92d6ac42-7e8e-455d-9d5e-db5046c27dc9"

DEVICE="ens33"

ONBOOT="yes"

IPADDR="192.168.8.101"

DNS1="192.168.8.2"

GATEWAY="192.168.8.2"

 

在windows系统中打开cmd  输入:telnet 192.168.56.1 6379 (telnet ip port)测试是否可以正常连接,如果能联通则表示可以正常连接。

 

 

 

SpringBoot整合redis:

第一步:redis跑起来

第二步:idea工程环境配置

第三步:测试

测试报错:

org.springframework.data.redis.RedisConnectionFailureException: Unable to connect to Redis; nested exception is io.lettuce.core.RedisConnectionException: Unable to connect to 192.168.201.100:6379

at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory$SharedConnection.getNativeConnection(LettuceConnectionFactory.java:1106)
at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory$SharedConnection.getConnection(LettuceConnectionFactory.java:1085)
at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory.getSharedConnection(LettuceConnectionFactory.java:866)
at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory.getConnection(LettuceConnectionFactory.java:341)
at org.springframework.data.redis.core.RedisConnectionUtils.doGetConnection(RedisConnectionUtils.java:132)
at org.springframework.data.redis.core.RedisConnectionUtils.getConnection(RedisConnectionUtils.java:95)
at org.springframework.data.redis.core.RedisConnectionUtils.getConnection(RedisConnectionUtils.java:82)
at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:211)
at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:184)
at org.springframework.data.redis.core.AbstractOperations.execute(AbstractOperations.java:95)
at org.springframework.data.redis.core.DefaultValueOperations.set(DefaultValueOperations.java:236)
at org.example.test.RedisTest.testSet(RedisTest.java:34)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:74)
at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:84)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:220)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:53)
Caused by: io.lettuce.core.RedisConnectionException: Unable to connect to 192.168.201.100:6379
at io.lettuce.core.RedisConnectionException.create(RedisConnectionException.java:78)
at io.lettuce.core.RedisConnectionException.create(RedisConnectionException.java:56)
at io.lettuce.core.AbstractRedisClient.getConnection(AbstractRedisClient.java:235)
at io.lettuce.core.RedisClient.connect(RedisClient.java:204)
at org.springframework.data.redis.connection.lettuce.StandaloneConnectionProvider.lambda$getConnection$1(StandaloneConnectionProvider.java:113)
at java.util.Optional.orElseGet(Optional.java:267)
at org.springframework.data.redis.connection.lettuce.StandaloneConnectionProvider.getConnection(StandaloneConnectionProvider.java:113)
at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory$SharedConnection.getNativeConnection(LettuceConnectionFactory.java:1104)
... 41 more

解决:

idea的yml配置文件中,

redis host赋值为:192.168.8.130

密码赋值为:1

 问题根源:静态ip未设置成功,连接是依旧用的虚拟机自动生成的ip,如此的弊端,有可能每次启动虚拟机ip都会变动。

 

问题:为什么运行那些CrowdMainClass.java文件,控制台会打印下图异常,但是依旧能运行前端代码。

 解决:

euraka工程的yml文件service-url下的defaultZone没有缩进:

 

 

 

 

 

 

13.3 (351):用localhost访问portal.html页面页面报错

 

理应如此:

 

 解决:

 可能是清理缓存

 

 

注册获取验证码

问题1

控制台报错: Illegal character in scheme name at index 4: http(s)://gyytz.market.alicloud......................

解决:yml中url路径成分写错了。改为https://gyytz....com

 

 问题2

阿里云登录https://account.aliyun.com/login/login.htm?spm=a2c6h.12873639.J_3207526240.5.2b53407fzoe77O&oauth_callback=

短信接口调用时报状态码404,导致点击获取验证码后,redis中没有下图中的key值:

 解决:

 

 

引发新问题1

但是,手机接收到的验证码是:【国阳云】验证码:**code**,**minute**分钟内有效,请勿泄漏于他人!

没有详细的验证码code、时间minute。

引发新问题2

redis没有存入相应的key-value:

 

理应如此:

 

 

 

 

问题3

收到的验证码:【国阳云】验证码:**code**,**minute**分钟内有效,请勿泄漏于他人!

解决:

 存入redis失败,导致原因:可能是未申请短信模板。

 

报异常(已解决):

突然连接不上端口6379了,

解决:

法四(+法三):

auth工程中的yml配置文件没有写redis的配置,写上:

 

法三(发现问题根源,但是没有解决SpringBoot整合不上redis问题——redis的配置文件redis.conf未修改,一直以来我修改的那个文件不是运行的,根据启动redis的路径显示,应该修改根路径下myredis中的redis.conf):

改三处↓

 

法一(依旧连不上端口):

使用阿里云服务器,在服务器中创建redis,使用服务器ip连接redis。

服务器密码有:

(root@47.96.231.143)1111aaaA、111aaA

 ----常用命令行----

进入bin目录:cd /usr/local/redis/redis-6.2.6/bin

启动redis:redis-server /usr/local/redis/redis-6.2.6/redis.conf

进入redis:redis-cli

 修改密码:config set requirepass 1

redis-cli中使用密码:auth 1

重启redis:

查看是否启动:ps aux | grep redis-server

 

法二(不用redis了):

先注释掉所有和redis相关的(无效):

注释:

 

 

 

注释:

 

 

 

 

 注释:

 

 

 注释:

 

 

 

 

注释:

 

 

 

 

注释:

 

 

 星图

 

 AccessKey_Info(保存在项目父目录下):

创建的新用户:

用户登录名称:atguigu1908oss@1534177224735915.onaliyun.com
AccessKey ID:LTAI5tQULTupewjG8gwApdMn
AccessKey Secret:ulJIdPqVINIUB9jqph2XpHOwT1fVKc

 

 

0

上次进行到:

 

 

 

 

阿里支付宝开放平台-沙箱环境支付功能搭建

1、 eclipse中测试demo

https://opendocs.alipay.com/common/02kkv

https://open.alipay.com/develop/sandbox/account

修改java文件

AlipayConfig.java

 

package com.alipay.config;

import java.io.FileWriter;
import java.io.IOException;

/* *
*类名:AlipayConfig
*功能:基础配置类
*详细:设置帐户有关信息及返回路径
*修改日期:2017-04-05
*说明:
*以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。
*该代码仅供学习和研究支付宝接口使用,只是提供一个参考。
*/

public class AlipayConfig {

//↓↓↓↓↓↓↓↓↓↓请在这里配置您的基本信息↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

// 应用ID,您的APPID,收款账号既是您的APPID对应支付宝账号
public static String app_id = "2021000122636028";

// 商户私钥,您的PKCS8格式RSA2私钥
public static String merchant_private_key = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCxwmNh1zeZDt7WsbFQw3/F1khqBRjUiYe/pE0aT6bJx3vqKj050KNis5R5WmIOcna9J7CL5ba4W4+GrefF8JY0oeg4pDa/hH3B6M2eM64zZkhZDuT35XVW9gzhf40Qb41urp9w0XUgixjABWr5gONSa3Q82QLmHQ/joi/wIExZdCYKwuEcY1yXMLIY3JxcZLkVcSIXksshZVJ2xkAKUhcTsJTqohdMsX2DJjNnfBueLuELSsf+g5/zAWSf/yYih/NR0A+0EMN+A5TCIFlBDdCex8gv6h66E/7LWP+IeZa7lUsfweECd3b9Yy9KTcUXJZx/SzxyM77GHOwvsnyzZSrRAgMBAAECggEAQ23VfRvCf1DgvtgQxu4h3c33EiB01sqEr/GHPd/RCOGe46i6KOA32gz82Turzock+4O+uJKeyTgSLahyLuegUr7H9BR2oBeGDLReqxA3mE/ISeAYguCLrj0PjcpwnDd0kvOyj30kzP92mAB2H8cAdBdGtH4tV1TW3iZ7wL6/t35wnTOo6lkP53TC5Hie8vkIhw3rBP1NORx1I5HQUVy6K06uyp054+eoBiVi7n7TGv8Qp4Hf81k3bEC/r3QQFKRYkl0J3Zl+MWiHyGP8OaU1WAyOccKhJ504jSDNzqB4jXjYh8jgiu+EZMboOZebCmrFkAF5Rf8OQBW89+M5rWo2QQKBgQDwjm9A8KP/S9JUpIHfGEAxq3Nh+FzaAFg9zJX8PFqUdkIBLZcjzb0NI5Pov9aJnJIRFjgc/b1yl9ysQ+DIRmVMrGucbe+2yYJBZOEYaCLxjMbEksb+syDsYCHEAxi+r9xRdbW1eqAXoOzW+qdHzEWlm7nzmqoy2T7vOXM+2G+MjQKBgQC9K+DjSn8f/1LjzkdYMD9ufiZCv6+tweyugvxdo7xS9P6ZaM7EMoe8zf02JKRxE4IVS8DT5Ivnr/6UBw/+8whw8lx0+g55isrMKr/tNqcU+di+wqad7IWV4A9J3HkdGj+bX1AsJoHTjpk5H+mACAuAztE9j6f74+3LZI28I6mAVQKBgHPBFmf5Y/EFW0C/qH7h/IHqLquGB6gfYlbai5HEuRGXlkta1M+1wLMB27q0FVhCr0bpmjbZIsBxsw5x20iEF1q93Up43FDoxnURRf+onKf/ReZxerA1IjKcT6MymkWn+ix4p+ecCk1UcqNAGh7CfQEj3mcKlbvCLjaPPpWPMJ5BAoGBAKaXJ5rCFiWlgeef1vBwvMufSC5sBFfueXJNY9VKxiQAIN4UkFHzddntRVoS9sWlul7qsUto71P/hx/xAEMy33In3Qge1fRvJ5yY4SATixxL0nZbkMNucU3FXXFcOURJw04ep8nZt2cJIyJNWiIn84lhmJ6wDe0jklwVl0Ph2/lpAoGAGCALFZPIJ1ldS9AI/5GVwCyYTZj4rYTHteIPxSRfVaYtcekAWgxrrPQbtdLkfUhB5KGYUKL8jMKoNEquhfMuSxod1XgzMfz8zTmZ6rqYN5eRPrw32HOoep9MBjHQpeZ3PPCWqcw+pmbsPuDa6XULb+adKUep2JKVBs2P5JdoTSY=";

// 支付宝公钥,查看地址:https://openhome.alipay.com/platform/keyManage.htm 对应APPID下的支付宝公钥。
public static String alipay_public_key = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAybCW/QK4tY9mTORuR7r9fN8/u3tGkloGD27NV2NnMc+6zEnowO5WtKpsUW8nctjS4FkE9Papr6U3Ue8gx4MIs+8lemU5W1WLiQU2f3wcnnJBbjiBNAELpLeGbSUMPv1RD8N0tzq0F0w70n+SDI5GZjF8u6E9vaqb4+keUl/kYzdKVunp+mfXV73bZTu+/TA66v9/UxMw0g8UEXL0YIpg7wXzUxBFhoCK0uxwA4A5jdF8PrtUjhIQACQzLiru5Nfi0vzYvGkOkM/4QKoJ/4WfNVZAaF0qb4DY/90PnpJH1yxL9jKHE3+L00YWdiQwq23JmTrPW8jsngrRZuyMPMbyHQIDAQAB";

// 服务器异步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
public static String notify_url = "http://kmwhgn.natappfree.cc/alipay.trade.page.pay-JAVA-UTF-8/notify_url.jsp";

// 页面跳转同步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
public static String return_url = "http://kmwhgn.natappfree.cc/alipay.trade.page.pay-JAVA-UTF-8/return_url.jsp";

// 签名方式
public static String sign_type = "RSA2";

// 字符编码格式
public static String charset = "utf-8";

// 支付宝网关(正式网关)
// public static String gatewayUrl = "https://openapi.alipay.com/gateway.do";

// 支付宝网关(沙箱网关)
public static String gatewayUrl = "https://openapi.alipaydev.com/gateway.do";

// 日志路径
public static String log_path = "E:\\";


//↑↑↑↑↑↑↑↑↑↑请在这里配置您的基本信息↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑

/**
* 写日志,方便测试(看网站需求,也可以改成把记录存入数据库)
* @param sWord 要写入日志里的文本内容
*/
public static void logResult(String sWord) {
FileWriter writer = null;
try {
writer = new FileWriter(log_path + "alipay_log_" + System.currentTimeMillis()+".txt");
writer.write(sWord);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (writer != null) {
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}

 

 

 

运行index.jsp页面→弹出http://localhost:7081/alipay.trade.page.pay-JAVA-UTF-8/index.jsp:

 

因为不是用的jdk1.7报错↓

 

2、idea中导入

application.xml

PayHandler.java中创建

① 调用支付宝接口专门封装的方法:

② returnUrlMethod方法:

 

③ notifyUrlMethod方法:

 

3、 下载沙箱版支付宝

 账密↓

 

 

 

启动该项目步骤:

0、启动redis服务

  虚拟存放位置:D:\VMware\Virtual Machines\CentOS 7 64 位用于毕设2

  开启redis服务

1、启动Tomcat

  访问http://localhost:7080/atcrowdfunding02_admin_webui_war_exploded/admin/to/login/page.html→后端管理员登录页面

2、启动8个微服务

  

 

   访问http://localhost:4000/→前端页面

  管理员账号密码: test/111

  用户:C/C

  沙箱版支付宝-支付密码:111111

 

辅助工具:

阿里云-云市场-第三方短信接口使用次数情况查看url:https://market.console.aliyun.com/?spm=5176.12818093.products-recent.dmarket.3be916d0mztg3Z#/?_k=7e6q6j

 

redis(可视化)

 

navicat(mysql可视化)

 

 

0

posted @ 2022-09-17 08:44  1900_SD  阅读(81)  评论(0编辑  收藏  举报