项目架构
项目介绍
教育后台管理系统是提供给相关业务人员使用的一个后台管理系统,业务人员可以在这个后台管理系统中,对课程信息、广告信息、用户信息、 权限信息等数据进行维护。
在 Java web 阶段已经完成了教育后台管理系统中的课程模块,接下来将对教育后台管理系统进行升级改造,基于 SSM 框架来完成课程信息模块、广告信息模块、用户信息模块、权限信息模块。
技术选型
前端技术选型
Vue.js
:是一套用于构建用户界面的渐进式 JavaScript 框架Element UI 库
:是饿了么前端出品的基于 Vue.js 的后台组件库,方便程序员进行页面快速布局和构建Node.js
:是运行在服务端的 JavaScript 运行环境Axios
:实现了对 Ajax 的封装,而 Ajax 技术实现了局部数据的刷新
后端技术选型
- Web 层借助 SpringMVC 接收请求,进行视图跳转
- Service 层借助 Spring 进行 IOC、AOP、及事务管理
- Dao 层借助 MyBatis 进行数据库交互
项目开发环境
开发工具:后端 - IDEA 2019,前端 - VS code,数据库客户端工具 - SQLYog
开发环境:JDK 11,Maven 3.6.3,MySQL 5.7
Maven 进阶使用(Maven 聚合工程)
Maven 基础知识
Maven 介绍
Maven 是一个项目管理工具,主要作用是在项目开发阶段对 Java 项目进行依赖管理和项目构建。
依赖管理:是对 jar 包的管理。通过导入 maven 坐标,就相当于将仓库中的 jar 包导入了当前项目中。
项目构建:通过 maven 的一个命令就可以完成项目从清理、编译、测试、报告、打包,部署整个过程。
Maven 的仓库类型
本地仓库
远程仓库
- Maven 中央仓库(http://repo2.maven.org/maven2/)
- Maven 私服(公司局域网内的仓库,需要自己搭建)
- 其他公共远程仓库(例如 apache 提供的远程仓库,http://repo.maven.apache.org/maven2/)
本地仓库 ---> maven 私服 --> maven 中央仓库
Maven 常用命令
clean 清理
compile 编译
test 测试
package 打包
install 安装
Maven 坐标书写规范
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
Maven 的依赖传递
什么是依赖传递
在 maven 中,依赖是可以传递的,假设存在三个项目,分别是项目 A,项目 B 以及项目C。假设 C 依赖 B,B 依赖 A,那么可以根据 Maven 项目依赖的特征推出项目 C 也依赖 A。
...
<!-- springMVC 相关坐标-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
...
如上所示,当 web 项目直接依赖了 spring-webmvc
,而 spring-webmvc
依赖了 sping-aop
、spring-beans
等。最终的结果就是在 web 项目中间接依赖了 spring-aop
、spring-beans
等。
依赖冲突
...
<!-- springMVC-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<!-- springAOP-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
...
如上所示,由于依赖传递现象的存在,spring-webmvc
依赖 spring-beans-5.1.5
,而spring-aop
依赖 spring-beans-5.1.6
,但是发现 spirng-beans-5.1.5
已经加入到了工程中,这时候如果希望 spring-beans-5.1.6
加入工程,就造成了依赖冲突。
如何解决依赖冲突
- 使用 maven 提供的依赖调解原则:第一声明者优先原则、路径近者优先原则
- 排除依赖
- 锁定版本
依赖调节原则
第一声明者优先原则
在 POM 文件中定义依赖,以先声明的依赖为准。其实就是根据坐标导入的顺序来确定最终使用哪个传递过来的依赖。
...
<!-- springAOP 第一声明者优先使用 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<!-- springMVC-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
...
结论:spring-aop
和 spring-webmvc
都传递过来了 spring-beans
,但是因为 spring-aop
在前面,所以最终使用的 spring-beans
是由 spring-aop
传递过来的,而 spring-webmvc
传递过来的 spring-beans
则被忽略了。
路径近者优先原则
...
<!-- springAOP-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<!-- springMVC-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<!-- 直接依赖 spring-beans,路径更近,优先使用 5.1.7 版本 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.1.7.RELEASE</version>
</dependency>
...
总结:直接依赖大于依赖传递。
排除依赖
可以使用 exclusions
标签将传递过来的依赖排除出去。
...
<!-- springMVC-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.5.RELEASE</version>
<!-- 排除依赖 5.1.5 版本的 spring-beans -->
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- springAOP -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
...
版本锁定
采用直接锁定版本的方法确定依赖 jar 包的版本,版本锁定后则不考虑依赖的声明顺序或依赖的路径,以锁定的版本为准添加到工程中,此方法在企业开发中经常使用。
版本锁定的使用方式:
第一步 - 在 dependencyManagement
标签中锁定依赖的版本
第二步 - 在 dependencies
标签中声明需要导入的 maven 坐标
- 在
dependencyManagement
标签中锁定依赖的版本
...
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
</dependencies>
</dependencyManagement>
...
- 在
dependencies
标签中声明需要导入的 maven 坐标
...
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<!-- 对应的 jar 包就无需配置版本号 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
</dependencies>
...
properties
标签的使用
...
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
<java.version>1.11</java.version>
<maven.compiler.source>1.11</maven.compiler.source>
<maven.compiler.target>1.11</maven.compiler.target>
<spring.version>5.1.5.RELEASE</spring.version>
<springmvc.version>5.1.5.RELEASE</springmvc.version>
<mybatis.version>3.5.1</mybatis.version>
</properties>
...
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
...
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${springmvc.version}</version>
</dependency>
,,,
</dependencies>
,,,
Maven 聚合工程(分模块)
概念:在现实生活中,汽车厂家进行汽车生产时,由于整个生产过程非常复杂和繁琐,工作量非常大,所以厂家都会将整个汽车的部件分开生产,最终再将生产好的部件进行组装,形成一台完整的汽车。
分模块构建 maven 工程分析
在企业项目开发中,由于项目规模大,业务复杂,参与的人员比较多,一般会通过合理的模块拆分将一个大型的项目拆分为 N 多个小模块,分别进行开发。而且拆分出的模块可以非常容易的被其他模块复用。
常见的拆分方式有两种:
- 按照业务模块进行拆分,每个模块拆分成一个 maven 工程,例如将一个项目分为用户模块,订单模块,购物车模块等,每个模块对应就是一个 maven 工程
- 按照层进行拆分,例如持久层、业务层、表现层等,每个层对应就是一个 maven 工程
不管上面哪种拆分方式,通常都会提供一个父工程,将一些公共的代码和配置提取到父工程中进行统一管理和配置。
父工程:资源的统一管理 - 依赖管理、版本锁定
按层拆分
- maven_web 依赖 mavent_service 依赖 mavent_dao 依赖 maven_pojo
- maven_web、mavent_service、mavent_dao、maven_pojo 四个工程都继承 maven_parent
Maven 工程的继承
在 Java 语言中,类之间是可以继承的,通过继承,子类就可以引用父类中非 private 的属性和方法。同样,在 maven 工程之间也可以继承,子工程继承父工程后,就可以使用在父工程中引入的依赖。继承的目的是为了消除重复代码。
被继承的 Maven 工程通常称为父工程,父工程的打包方式必须为 POM,所以区分某个 Maven 工程是否为父工程就看这个工程的打包方式是否为 POM:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.renda</groupId>
<artifactId>edu_home_parent</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>ssm-utils</module>
<module>ssm-domain</module>
<module>ssm-dao</module>
<module>ssm-service</module>
<module>ssm-web</module>
</modules>
<properties>
<spring.version>5.1.5.RELEASE</spring.version>
<springmvc.version>5.1.5.RELEASE</springmvc.version>
<mybatis.version>3.5.1</mybatis.version>
</properties>
<!-- 统一版本锁定 -->
<dependencyManagement>
<dependencies>
...
</dependencies>
</dependencyManagement>
<!-- 统一依赖管理 -->
<dependencies>
...
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>
继承其他 Maven 父工程的工程通常称为子工程,在 pom.xml 文件中通过 parent 标签进行父工程的继承:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.renda</groupId>
<artifactId>edu_home_parent</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>ssm-web</artifactId>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>com.lagou</groupId>
<artifactId>ssm-service</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
Maven 工程的聚合
在 maven 工程的 pom.xml 文件中可以使用标签将其他 maven 工程聚合到一起,聚合的目的是为了进行统一操作。
例如拆分后的 maven 工程有多个,如果要进行打包,就需要针对每个工程分别执行打包命令,操作起来非常繁琐。这时就可以使用标签将这些工程统一聚合到 maven 父工程中,需要打包的时候,只需要在此工程中执行一次打包命令,其下被聚合的工程就都会被打包了。
使用 Maven 聚合工程搭建教育后台管理系统
工程整体结构如下:
1)lagou_edu_home_parent
为父工程,其余工程为子工程,都继承父工程 lagou_edu_home_parent
2)lagou_edu_home_parent
工程将其子工程都进行了聚合
3)子工程之间存在依赖关系:
- ssm_utils:编写工具类
- ssm_domain:编写实体类
- ssm_dao:编写 dao 层代码及配置文件
- ssm_service:编写 service 层代码及配置文件
- ssm_web:编写 web 层代码及配置文件
- ssm_domain 依赖 ssm_utils
- ssm_dao 依赖 ssm_domain
- ssm_service 依赖 ssm_dao
- ssm_web 依赖 ssm_service
- ssm_utils、ssm_domain、ssm_dao、ssm_service、ssm_web 都继承 lagou_edu_home_parent
依赖关系建立原则:当前项目要用到哪个项目的资源,那么当前项目就依赖要用到的资源的项目(通过直接依赖和间接依赖的方式)。
父工程 lagou_edu_home_parent
构建
修改 pom.xml
添加依赖:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.renda</groupId>
<artifactId>lagou_edu_home_parent</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<!-- 指定编码及版本 -->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
<java.version>1.11</java.version>
<maven.compiler.source>1.11</maven.compiler.source>
<maven.compiler.target>1.11</maven.compiler.target>
<spring.version>5.1.5.RELEASE</spring.version>
<springmvc.version>5.1.5.RELEASE</springmvc.version>
<mybatis.version>3.5.1</mybatis.version>
</properties>
<!-- 锁定 jar 版本 -->
<dependencyManagement>
<dependencies>
<!-- Mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency>
<!-- springMVC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${springmvc.version}</version>
</dependency>
<!-- spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- mybatis 相关坐标-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.15</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</dependency>
<!-- java Junit 测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!-- spring 相关坐标 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
</dependency>
<!-- MyBatis 整合 Spring 坐标-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency>
<!-- springMVC 相关坐标 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- jackson 解析 JSON -->
<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-core</artifactId>
<version>2.9.8</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.0</version>
</dependency>
<!-- 分页助手 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>4.1.6</version>
</dependency>
<!-- 文件上传和 BeanUtils -->
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.8.3</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<!-- 解决跨域问题所需依赖 -->
<dependency>
<groupId>com.thetransactioncompany</groupId>
<artifactId>cors-filter</artifactId>
<version>2.5</version>
</dependency>
<!-- log4j -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.7</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
<!-- <build>-->
<!-- <plugins>-->
<!-- <plugin>-->
<!-- <groupId>org.apache.maven.plugins</groupId>-->
<!-- <artifactId>maven-compiler-plugin</artifactId>-->
<!-- <version>3.1</version>-->
<!-- <configuration>-->
<!-- <source>${maven.compiler.source}</source>-->
<!-- <target>${maven.compiler.target}</target>-->
<!-- <encoding>${maven.compiler.encoding}</encoding>-->
<!-- </configuration>-->
<!-- </plugin>-->
<!-- </plugins>-->
<!-- </build>-->
</project>
子工程 ssm-utils
构建
默认生成的 pom.xml 文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>lagou_edu_home_parent</artifactId>
<groupId>com.renda</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>ssm-utils</artifactId>
</project>
子工程 ssm-domain
构建
配置 ssm-domain
工程的 pom.xml
文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>lagou_edu_home_parent</artifactId>
<groupId>com.renda</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>ssm-domain</artifactId>
<!-- 依赖 ssm_utils -->
<dependencies>
<dependency>
<groupId>com.renda</groupId>
<artifactId>ssm-utils</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
创建实体类 Test.java
package com.renda.domain;
public class Test {
private Integer id;
private String name;
public Test() {
}
public Test(Integer id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "Test{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
子工程 ssm-dao
构建
配置 ssm-dao
工程的 pom.xml
文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>lagou_edu_home_parent</artifactId>
<groupId>com.renda</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>ssm-dao</artifactId>
<dependencies>
<dependency>
<!-- 依赖 ssm_domain -->
<groupId>com.renda</groupId>
<artifactId>ssm-domain</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
创建 DAO 接口和 Mapper 映射文件
public interface TestMapper {
List<Test> findAllTest();
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.renda.dao.TestMapper">
<select id="findAllTest" resultType="test">
select * from test
</select>
</mapper>
在 resources 目录下创建 Spring 配置文件 applicationContext-dao.xml
<?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.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- spring 整合 mybatis -->
<!-- 引入 jdbc.properties -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 1.数据源配置 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!-- 2.sqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="typeAliasesPackage" value="com.renda.domain"/>
<!-- 引入加载 mybatis 核心配置文件 -->
<property name="configLocation" value="classpath:sqlMapConfig.xml"></property>
</bean>
<!-- 3.mapper 映射扫描,没有别的地方用到这个 bean,所以可以省略 id -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.renda.dao"/>
</bean>
</beans>
sqlMapConfig.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>
<settings>
<!--
是否开启自动驼峰命名规则(camel case)映射。
即从数据库列名 A_COLUMN 到属性名 aColumn 的类似映射。
如从 a_name 映射为 aName
-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
</configuration>
子工程 ssm-service
构建
配置 ssm-service
工程的 pom.xml
文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>lagou_edu_home_parent</artifactId>
<groupId>com.renda</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>ssm-service</artifactId>
<dependencies>
<!-- 依赖 ssm_dao -->
<dependency>
<groupId>com.renda</groupId>
<artifactId>ssm-dao</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
创建 TestService
接口和实现类
public interface TestService {
/**
* 对 test 表进行查询所有
*/
List<Test> findAllTest();
}
@Service
public class TestServiceImpl implements TestService {
@Autowired
private TestMapper testMapper;
@Override
public List<Test> findAllTest() {
return testMapper.findAllTest();
}
}
创建 spring 配置文件 applicationContext-service.xml
<?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.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 进行 service 的相关配置:IOC -->
<!-- 开启 IOC 注解扫描 -->
<context:component-scan base-package="com.renda.service"/>
<!-- 引入 applicationContext-dao.xml -->
<import resource="classpath:applicationContext-dao.xml"/>
</beans>
子工程 ssm-web
构建
配置 ssm-web
工程的 pom.xml
文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>lagou_edu_home_parent</artifactId>
<groupId>com.renda</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>ssm-web</artifactId>
<!-- 打包方式为 war -->
<packaging>war</packaging>
<dependencies>
<!-- 依赖 ssm_service -->
<dependency>
<groupId>com.renda</groupId>
<artifactId>ssm-service</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
创建 Controller
@RestController // 等同于 @Controller + @ResponseBody
public class TestController {
@Autowired
private TestService testService;
@RequestMapping("/findAllTest")
public List<Test> findAllTest() {
return testService.findAllTest();
}
}
创建 SpringMVC 配置文件 springmvc.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 1.组件扫描 -->
<context:component-scan base-package="com.renda.controller"/>
<!-- 2.mvc 注解增强 -->
<mvc:annotation-driven/>
<!-- 3.视图解析器:暂时不用配置,因为没有用到视图跳转 -->
<!-- 4.静态资源放行 -->
<mvc:default-servlet-handler/>
</beans>
编写 applicationContext.xml
<?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.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 引入 applcationContext-service.xml -->
<import resource="classpath:applicationContext-service.xml"/>
</beans>
配置 web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!-- 前端控制器 -->
<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:springmvc.xml</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- spring 的监听器 contextLoaderListener -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!-- 中文乱码过滤器 -->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 配置跨域过滤器 -->
<filter>
<filter-name>corsFilter</filter-name>
<filter-class>com.thetransactioncompany.cors.CORSFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>corsFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
课程模块
课程模块功能分析
课程模块包含了多条件查询、 图片上传、 新建&修改课程、课程状态管理、课程内容展示、回显章节对应的课程信息、新建&修改章节信息、修改章节状态、 新建&修改课时信息等接口的编写。
课程管理
实现以下功能:
- 多条件查询
- 图片上传
- 新建课程信息
- 回显课程信息
- 修改课程信息
- 课程状态管理
- 课程内容展示
- 回显章节对应的课程信息
- 新建&修改章节信息
- 修改章节状态
- 新建课时信息
课程模块表设计
创建数据库及表
数据库
ssm_lagou_edu
表
course
课程表course_lesson
课时表course_media
课程媒体表course_section
章节表menu
promotion_ad
promotion_space
resource
resource_category
role_menu_relation
role_resource_relation
roles
teacher
user
user_phone_verification
user_role_relation
user_weixin
表关系介绍
ER 图
一个课程表对多个章节表
一个章节表对多个课时表
一个课时表对一个课程媒体表
数据实体描述
课程表
CREATE TABLE `course` (
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'id',
`course_name` VARCHAR(255) DEFAULT NULL COMMENT '课程名',
`brief` VARCHAR(255) DEFAULT '' COMMENT '课程一句话简介',
`price` DOUBLE(10,2) DEFAULT NULL COMMENT '原价',
`price_tag` VARCHAR(255) DEFAULT '' COMMENT '原价标签',
`discounts` DOUBLE(10,2) DEFAULT NULL COMMENT '优惠价',
`discounts_tag` VARCHAR(255) DEFAULT NULL COMMENT '优惠标签',
`course_description_mark_down` LONGTEXT COMMENT '描述markdown',
`course_description` LONGTEXT COMMENT '课程描述',
`course_img_url` VARCHAR(255) DEFAULT NULL COMMENT '课程分享图片url',
`is_new` TINYINT(1) DEFAULT NULL COMMENT '是否新品',
`is_new_des` VARCHAR(255) DEFAULT NULL COMMENT '广告语',
`last_operator_id` INT(11) DEFAULT NULL COMMENT '最后操作者',
`auto_online_time` DATETIME DEFAULT NULL COMMENT '自动上架时间',
`create_time` DATETIME NOT NULL COMMENT '记录创建时间',
`update_time` DATETIME NOT NULL COMMENT '更新时间',
`is_del` TINYINT(1) DEFAULT '0' COMMENT '是否删除',
`total_duration` INT(11) DEFAULT NULL COMMENT '总时长(分钟)',
`course_list_img` VARCHAR(255) DEFAULT NULL COMMENT '课程列表展示图片',
`status` INT(2) DEFAULT '0' COMMENT '课程状态,0-草稿,1-上架',
`sort_num` INT(11) DEFAULT NULL COMMENT '课程排序,用于后台保存草稿时用到',
`preview_first_field` VARCHAR(255) DEFAULT NULL COMMENT '课程预览第一个字段',
`preview_second_field` VARCHAR(255) DEFAULT NULL COMMENT '课程预览第二个字段',
`sales` INT(11) DEFAULT '0' COMMENT '销量',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=INNODB AUTO_INCREMENT=31 DEFAULT CHARSET=utf8;
章节表
CREATE TABLE `course_section` (
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'id',
`course_id` INT(11) DEFAULT NULL COMMENT '课程id',
`section_name` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '章节名',
`description` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '章节描述',
`create_time` DATETIME NOT NULL COMMENT '记录创建时间',
`update_time` DATETIME NOT NULL COMMENT '更新时间',
`is_de` TINYINT(1) DEFAULT '0' COMMENT '是否删除',
`order_num` INT(11) DEFAULT NULL COMMENT '排序字段',
`status` INT(1) NOT NULL DEFAULT '0' COMMENT '状态,0:隐藏;1:待更新;2:已发布',
PRIMARY KEY (`id`) USING BTREE,
KEY `course_id_index` (`course_id`) USING BTREE,
KEY `idx_course_id` (`course_id`) USING BTREE
) ENGINE=INNODB AUTO_INCREMENT=22 DEFAULT CHARSET=utf8;
课时表
CREATE TABLE `course_lesson` (
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'id',
`course_id` INT(11) NOT NULL COMMENT '课程id',
`section_id` INT(11) NOT NULL DEFAULT '0' COMMENT '章节id',
`theme` VARCHAR(255) NOT NULL COMMENT '课时主题',
`duration` INT(11) NOT NULL DEFAULT '0' COMMENT '课时时长(分钟)',
`is_free` TINYINT(1) NOT NULL DEFAULT '0' COMMENT '是否免费',
`create_time` DATETIME NOT NULL COMMENT '记录创建时间',
`update_time` DATETIME NOT NULL COMMENT '更新时间',
`is_del` TINYINT(1) DEFAULT '0' COMMENT '是否删除',
`order_num` INT(11) DEFAULT NULL COMMENT '排序字段',
`status` INT(2) DEFAULT '0' COMMENT '课时状态,0-隐藏,1-未发布,2-已发布',
PRIMARY KEY (`id`) USING BTREE,
KEY `course_id_index` (`course_id`,`section_id`) USING BTREE,
KEY `idx_sectionId_orderNum` (`section_id`,`order_num`) USING BTREE
) ENGINE=INNODB AUTO_INCREMENT=26 DEFAULT CHARSET=utf8 COMMENT='课程节内容';
课程媒体
CREATE TABLE `course_media` (
`id` INT(11) NOT NULL AUTO_INCREMENT COMMENT '课程媒体主键ID',
`course_id` INT(11) DEFAULT NULL COMMENT '课程Id',
`section_id` INT(11) DEFAULT NULL COMMENT '章ID',
`lesson_id` INT(11) DEFAULT NULL COMMENT '课时ID',
`cover_image_url` VARCHAR(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '封面图URL',
`duration` VARCHAR(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '时长(06:02)',
`file_edk` VARCHAR(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '媒体资源文件对应的EDK',
`file_size` DOUBLE(10,2) DEFAULT NULL COMMENT '文件大小MB',
`file_name` VARCHAR(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '文件名称',
`file_dk` VARCHAR(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '媒体资源文件对应的DK',
`create_time` DATETIME NOT NULL COMMENT '创建时间',
`update_time` DATETIME NOT NULL COMMENT '更新时间',
`is_del` TINYINT(1) DEFAULT '0' COMMENT '是否删除,0未删除,1删除',
`duration_num` INT(11) DEFAULT NULL COMMENT '时长,秒数(主要用于音频在H5控件中使用)',
`file_id` VARCHAR(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '媒体资源文件ID',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE KEY `uniq_lessonid_channel_mediatype_idx` (`lesson_id`) USING BTREE
) ENGINE=INNODB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
课程模块接口实现
多条件课程列表查询
需求分析
根据课程名称及课程状态进行多条件查询
查看接口文档,进行编码
实体类 Course
public class Course {
// 主键
private int id;
// 课程名称
private String courseName;
// 课程一句话简介
private String brief;
// 原价
private double price;
// 原价标签
private String priceTag;
// 优惠价
private double discounts;
// 优惠价标签
private String discountsTag;
// 课程内容 markdown
private String courseDescriptionMarkDown;
// 课程描述
private String courseDescription;
// 课程分享图片 url
private String courseImgUrl;
// 是否新品
private int isNew;
// 广告语
private String isNewDes;
// 最后操作者
private int lastOperatorId;
// 自动上架时间
private Date autoOnlineTime;
// 创建时间
private Date createTime;
// 更新时间
private Date updateTime;
// 是否删除
private int isDel;
// 总时长
private int totalDuration;
// 课程列表展示图片
private String courseListImg;
// 课程状态,0 - 草稿,1 - 上架
private int status;
// 课程排序
private int sortNum;
// 课程预览第一个字段
private String previewFirstField;
// 课程预览第二个字段
private String previewSecondField;
// 销量
private int sales;
// getter, setter, toString ...
}
ResponseResult
用于表现层返回响应信息
public class ResponseResult {
private Boolean success;
private Integer state;
private String message;
private Object content;
// getter, setter, toString ...
}
实体类 CourseVo
View Object 表现层对象,主要用于表现层来接收参数的
public class CourseVO {
// 主键
private Integer id;
// 课程名称
private String courseName;
// 课程一句话简介
private String brief;
// 原价
private double price;
// 原价标签
private String priceTag;
// 优惠价
private double discounts;
// 优惠价标签
private String discountsTag;
// 课程内容 markdown
private String courseDescriptionMarkDown;
// 课程描述
private String courseDescription;
// 课程分享图片 url
private String courseImgUrl;
// 是否新品
private int isNew;
// 广告语
private String isNewDes;
// 最后操作者
private int lastOperatorId;
// 是否删除
private int isDel;
// 总时长
private int totalDuration;
// 课程列表展示图片
private String courseListImg;
// 课程状态,0 - 草稿,1 - 上架
private int status;
// 课程排序
private int sortNum;
// 课程预览第一个字段
private String previewFirstField;
// 课程预览第二个字段
private String previewSecondField;
// 销量
private int sales;
// 讲师姓名
private String teacherName;
// 讲师职位
private String position;
// 讲师描述
private String description;
// getter, setter, toString ...
}
Dao层 CourseMapper
public interface CourseMapper {
/**
* 多条件课程列表查询
*/
List<Course> findCourseByCondition(CourseVO courseVO);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.renda.dao.CourseMapper">
<!--
注意:status 在 mysql 是一个关键字,所以要加上反单引号来防止出现误判情况
-->
<select id="findCourseByCondition" parameterType="courseVo" resultType="course">
select * from course
<where>
<if test="courseName != null and courseName != ''">
and course_name like concat('%', #{courseName}, '%')
</if>
<if test="`status` != null and `status` != ''">
and `status` = #{status}
</if>
<if test="true">
and is_del != 1
</if>
</where>
</select>
</mapper>
Service 层 CourseService
public interface CourseService {
List<Course> findCourseByCondition(CourseVO courseVO);
}
@Service
public class CourseServiceImpl implements CourseService {
@Autowired
private CourseMapper courseMapper;
@Override
public List<Course> findCourseByCondition(CourseVO courseVO) {
return courseMapper.findCourseByCondition(courseVO);
}
}
Web层 CourseController
@RestController
@RequestMapping("/course")
public class CourseController {
@Autowired
private CourseService courseService;
/**
* 多条件课程列表查询
*/
@RequestMapping("/findCourseByCondition")
public ResponseResult findCourseByCondition(@RequestBody CourseVO courseVO) {
// 调用 service
List<Course> courseList = courseService.findCourseByCondition(courseVO);
// 返回响应参数的 JSON 字符串
return new ResponseResult(true, 200, "响应成功", courseList);
}
}
Postman 测试接口
课程图片上传
需求分析
在新增课程页面需要进行图片上传,并回显图片信息
查看接口文档,进行编码
springmvc.xml
<!-- 配置文件解析器 -->
<!-- 此处 id 为固定写法,不能随便取名 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="1048576"/>
</bean>
Web 层 CourseController
@RequestMapping("/courseUpload")
public ResponseResult fileUpload(@RequestParam("file") MultipartFile file, HttpServletRequest request) throws IOException {
// 1.判断接收到的上传文件是否为空
if (file.isEmpty()) {
throw new RuntimeException();
}
// 2.获取项目部署路径
// D:\apache-tomcat-8.5.55\webapps\ssm-web\
String realPath = request.getServletContext().getRealPath("/");
// D:\apache-tomcat-8.5.56\webapps\
String substring = realPath.substring(0, realPath.indexOf("ssm_web"));
// 3.生成新文件名
String originalFilename = file.getOriginalFilename();
String newFileName = "test";
if (originalFilename != null) {
newFileName = System.currentTimeMillis() + originalFilename.substring(originalFilename.lastIndexOf("."));
}
// 4.文件上传
String uploadPath = substring + "upload\\";
File filePath = new File(uploadPath, newFileName);
// 如果目录不存在就创建目录
if (!filePath.getParentFile().exists()) {
if (filePath.getParentFile().mkdirs()) {
System.out.println("目录已经被创建:" + filePath);
} else {
System.out.println("创建目录失败");
}
}
// 图片进行上传
file.transferTo(filePath);
// 5. 将文件名和文件路径返回,进行响应
HashMap<Object, Object> map = new HashMap<>();
map.put("fileName", newFileName);
map.put("filePath", "http://localhost:8080/upload/" + newFileName);
// 返回响应参数
return new ResponseResult(true, 200, "图片上传成功", map);
}
Postman 测试接口
首先确保配置的 Tomcat 服务器的 Artifact 为 ssm-web:war
,设置好图片 Tomcat 服务器保存图片的路径为 %MAVEN_HOME%\webapps\upload
。
然后 Postman 测试时,选择 Body 的 form-data
,并选择 key 为 file 属性。
新建课程信息
需求分析
填写好新增课程信息以及关联的讲师信息后,点击保存,将表单信息保存到数据库中
查看接口文档,进行编码
Dao 层 CourseMapper
void saveCourse(Course course);
void saveTeacher(Teacher teacher);
CourseMapper.xml
<!--
新增课程信息
使用 selectKey 获取添加成功记录返回的 ID 值赋值给 Course 实体中 ID 属性
-->
<insert id="saveCourse" parameterType="course">
<selectKey resultType="int" order="AFTER" keyProperty="id">
select LAST_INSERT_ID()
</selectKey>
INSERT INTO course(
course_name,
brief,
preview_first_field,
preview_second_field,
course_img_url,
course_list_img,
sort_num,
price,
discounts,
sales,
discounts_tag,
course_description_mark_down,
create_time,
update_time
) VALUES (
#{courseName},#{brief},#{previewFirstField},#{previewSecondField},
#{courseImgUrl},#{courseListImg},#{sortNum},#{price},#{discounts},
#{sales},#{discountsTag},#{courseDescriptionMarkDown},#{createTime},
#{updateTime}
)
</insert>
<!-- 新增讲师信息 -->
<insert id="saveTeacher" parameterType="teacher">
INSERT INTO teacher(
course_id,
teacher_name,
`position`,
description,
create_time,
update_time
) VALUES (#{courseId},#{teacherName},#{position},#{description},#{createTime},#{updateTime});
</insert>
Service 层 CourseService
void saveCourseOrTeacher(CourseVO courseVO) throws InvocationTargetException, IllegalAccessException;
@Override
public void saveCourseOrTeacher(CourseVO courseVO) throws InvocationTargetException, IllegalAccessException {
// 1.封装课程信息
Course course = new Course();
BeanUtils.copyProperties(course, courseVO);
// 补全课程信息
Date date = new Date();
course.setCreateTime(date);
course.setUpdateTime(date);
// 保存课程
courseMapper.saveCourse(course);
// 获取新插入数据的 id 值
int courseId = course.getId();
// 2.封装讲师信息
Teacher teacher = new Teacher();
BeanUtils.copyProperties(teacher, courseVO);
// 补全讲师信息
teacher.setCreateTime(date);
teacher.setUpdateTime(date);
teacher.setIsDel(0);
teacher.setCourseId(courseId);
// 保存讲师信息
courseMapper.saveTeacher(teacher);
}
Web 层 CourseController
@RequestMapping("/saveOrUpdateCourse")
public ResponseResult saveOrUpdateCourse(@RequestBody CourseVO courseVO) throws InvocationTargetException, IllegalAccessException {
courseService.saveCourseOrTeacher(courseVO);
return new ResponseResult(true, 200, "响应成功", null);
}
Postman 测试接口
回显课程信息
需求分析
点击编辑按钮,回显课程信息以及其关联的讲师信息
查看接口文档,进行编码
Dao 层 CourseMapper
CourseVO findCourseById(Integer id);
CourseMapper.xml
<select id="findCourseById" parameterType="int" resultType="courseVO">
SELECT
c.*,
t.`teacher_name` `teacher_name`,
t.`position` `position`,
t.`description` `description`
FROM
course c
LEFT JOIN teacher t
ON c.`id` = t.`course_id`
WHERE c.`id` = #{id}
</select>
Service 层:CourseService
CourseVO findCourseById(Integer id);
@Override
public CourseVO findCourseById(Integer id) {
return courseMapper.findCourseById(id);
}
Web 层 CourseController
@RequestMapping("/findCourseById")
public ResponseResult findCourseById(Integer id){
CourseVO courseVO = courseService.findCourseById(id);
return new ResponseResult(true, 200, "根据ID查询课程信息成功", courseVO);
}
Postman 测试接口
修改课程信息
需求分析
点击保存按钮,将修改后的课程信息保存到数据库中
查看接口文档,进行编码
Dao 层 CourseMapper
void updateTeacher(Teacher teacher);
CourseMapper.xml
<!-- 更新课程信息 -->
<update id="updateCourse" parameterType="course">
UPDATE course
<trim prefix="SET" suffixOverrides=",">
<if test="courseName != null and courseName != ''">
course_name = #{courseName},
</if>
<if test="brief != null and brief != ''">
brief = #{brief},
</if>
<if test="previewFirstField != null and previewFirstField != ''">
preview_first_field=#{previewFirstField},
</if>
<if test="previewSecondField != null and previewSecondField != ''">
preview_second_field=#{previewSecondField},
</if>
<if test="courseImgUrl != null and courseImgUrl != ''">
course_img_url=#{courseImgUrl},
</if>
<if test="courseListImg != null and courseListImg != ''">
course_list_img=#{courseListImg},
</if>
<if test="sortNum != null and sortNum != ''">
sort_num=#{sortNum},
</if>
<if test="price != null and price != ''">
price=#{price},
</if>
<if test="discounts != null and discounts != ''">
discounts=#{discounts},
</if>
<if test="sales != null and sales != '' or sales==0">
sales=#{sales},
</if>
<if test="discountsTag != null and discountsTag != ''">
discounts_tag=#{discountsTag},
</if>
<if test="courseDescriptionMarkDown != null and courseDescriptionMarkDown != ''">
course_description_mark_down=#{courseDescriptionMarkDown},
</if>
<if test="updateTime != null">
update_time=#{updateTime},
</if>
</trim>
</update>
<!-- 更新讲师信息 -->
<update id="updateTeacher" parameterType="teacher">
UPDATE teacher
<trim prefix="SET" suffixOverrides=",">
<if test="teacherName != null and teacherName != ''">
teacher_name = #{teacherName},
</if>
<if test="position != null and position != ''">
position = #{position},
</if>
<if test="description != null and description != ''">
description = #{description},
</if>
<if test="updateTime != null">
update_time=#{updateTime}
</if>
</trim>
<where>
<if test="courseId != null and courseId != ''">course_id = #{courseId}</if>
</where>
</update>
Service 层 CourseService
void updateCourseOrTeacher(CourseVO courseVO) throws InvocationTargetException, IllegalAccessException;
@Override
public void updateCourseOrTeacher(CourseVO courseVO) throws InvocationTargetException, IllegalAccessException {
// 封装课程信息
Course course = new Course();
BeanUtils.copyProperties(course, courseVO);
// 补全信息
Date date = new Date();
course.setUpdateTime(date);
// 更新课程信息
courseMapper.updateCourse(course);
// 封装讲师信息
Teacher teacher = new Teacher();
BeanUtils.copyProperties(teacher, courseVO);
// 补全信息
teacher.setCourseId(course.getId());
teacher.setUpdateTime(date);
// 更新讲师信息
courseMapper.updateTeacher(teacher);
}
Web 层 CourseController
@RequestMapping("/saveOrUpdateCourse")
public ResponseResult saveOrUpdateCourse(@RequestBody CourseVO courseVO) throws InvocationTargetException, IllegalAccessException {
if (courseVO.getId() == null) {
// 新增操作
courseService.saveCourseOrTeacher(courseVO);
return new ResponseResult(true, 200, "新增成功", null);
} else {
// 修改操作
courseService.updateCourseOrTeacher(courseVO);
return new ResponseResult(true, 200, "修改成功", null);
}
}
Postman 测试接口
课程状态管理
需求分析
在课程列表展示页面中,可以通过点击上架 / 下架按钮,修改课程状态
查看接口文档,进行编码
Dao 层 CourseMapper
void updateCourseStatus(Course course);
CourseMapper.xml
<update id="updateCourseStatus" parameterType="course">
update `course` set `status` = #{status}, `update_time` = #{updateTime} where `id` = #{id}
</update>
Service 层 CourseService
void updateCourseStatus(int courseId, int status);
@Override
public void updateCourseStatus(int courseId, int status) {
// 封装数据
Course course = new Course();
course.setId(courseId);
course.setStatus(status);
course.setUpdateTime(new Date());
// 调用 mapper
courseMapper.updateCourseStatus(course);
}
Web 层 CourseController
@RequestMapping("/updateCourseStatus")
public ResponseResult updateCourseStatus(Integer id, Integer status) {
// 调用 service,传递参数,完成课程状态的变更
courseService.updateCourseStatus(id, status);
// 响应数据
HashMap<Object, Object> map = new HashMap<>();
map.put("status", status);
return new ResponseResult(true, 200, "课程状态变更成功", map);
}
Postman 测试接口
课程内容展示
需求分析
需求:点击内容管理,展示课程对应的课程内容(课程内容包含了章节和课时)
查看接口文档,进行编码
CourseSection
章节
public class CourseSection {
// id
private Integer id;
// 课程 id
private int courseId;
// 章节名
private String sectionName;
// 章节描述
private String description;
// 创建时间
private Date createTime;
// 更新时间
private Date updateTime;
// 是否删除
private int isDe;
// 排序
private int orderNum;
// 状态
private int status;
// 课时集合
private List<CourseLesson> lessonList;
// getter, setter ...
}
CourseLesson
课时
public class CourseLesson {
// 主键
private Integer id;
// 课程 id
private int courseId;
// 章节 id
private int sectionId;
// 课时主题
private String theme;
// 课时时长
private int duration;
// 是否免费
private int isFree;
// 创建时间
private Date createTime;
// 修改时间
private Date updateTime;
// 是否删除
private int isDel;
// 排序
private int orderNum;
// 状态
private int status;
// getter, setter ...
}
Dao 层 CourseContentMapper
public interface CourseContentMapper {
List<CourseSection> findSectionAndLessonByCourseId(Integer courseId);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.renda.dao.CourseContentMapper">
<resultMap id="SectionAndLessonResultMap" type="courseSection">
<id property="id" column="id"/>
<result property="courseId" column="course_id"/>
<result property="sectionName" column="section_name"/>
<result property="description" column="description"/>
<result property="createTime" column="create_time"/>
<result property="updateTime" column="update_time"/>
<result property="isDe" column="is_de"/>
<result property="orderNum" column="order_num"/>
<collection property="lessonList" ofType="courseLesson">
<id property="id" column="lesson_id"/>
<result property="courseId" column="course_id"/>
<result property="sectionId" column="section_id"/>
<result property="theme" column="theme"/>
<result property="duration" column="duration"/>
<result property="isFree" column="is_free"/>
<result property="orderNum" column="order_num"/>
<result property="status" column="status"/>
</collection>
</resultMap>
<!-- 根据课程 ID 查询课程内容(章节 - 课时) -->
<select id="findSectionAndLessonByCourseId" parameterType="int" resultMap="SectionAndLessonResultMap">
SELECT
cs.*,
cl.id lesson_id,
cl.course_id,
cl.section_id,
cl.theme,
cl.duration,
cl.create_time,
cl.update_time,
cl.is_del,
cl.order_num,
cl.`status`
FROM
course_section cs
LEFT JOIN course_lesson cl
ON cl.section_id = cs.id
WHERE cs.course_id = #{id}
ORDER BY cs.order_num;
</select>
</mapper>
Service 层 CourseContentService
public interface CourseContentService {
List<CourseSection> findSectionAndLessonByCourseId(Integer courseId);
}
@Service
public class CourseContentServiceImpl implements CourseContentService {
@Autowired
private CourseContentMapper courseContentMapper;
@Override
public List<CourseSection> findSectionAndLessonByCourseId(Integer courseId) {
return courseContentMapper.findSectionAndLessonByCourseId(courseId);
}
}
Web 层 CourseContentController
@RestController
@RequestMapping("/courseContent")
public class CourseContentController {
@Autowired
private CourseContentService courseContentService;
@RequestMapping("/findSectionAndLesson")
public ResponseResult findSectionAndLessonByCourseId(Integer courseId) {
List<CourseSection> list = courseContentService.findSectionAndLessonByCourseId(courseId);
return new ResponseResult(true, 200, "章节及课时内容查询成功", list);
}
}
Postman 测试接口
回显章节对应的课程信息
需求分析
在课程内容界面回显课程信息
查看接口文档,进行编码
Dao 层 CourseContentMapper
Course findCourseByCourseId(int courseId);
<select id="findCourseByCourseId" parameterType="int" resultType="course">
SELECT id, course_name FROM course WHERE id = #{courseId}
</select>
Service 层 CourseContentService
Course findCourseByCourseId(int courseId);
@Override
public Course findCourseByCourseId(int courseId) {
return courseContentMapper.findCourseByCourseId(courseId);
}
Web 层 CourseContentController
@RequestMapping("/findCourseByCourseId")
public ResponseResult findCourseByCourseId(Integer courseId){
Course course = courseContentService.findCourseByCourseId(courseId);
return new ResponseResult(true, 200, "查询课程信息成功", course);
}
Postman 测试接口
新建章节信息
需求分析
在课程内容展示页面中,可以通过点击添加阶段按钮,添加章节信息
查看接口文档,进行编码
Dao 层 CourseContentMapper
void saveSection(CourseSection courseSection);
<insert id="saveSection" parameterType="CourseSection">
INSERT INTO course_section (
course_id,
section_name,
description,
order_num,
`status`,
create_time,
update_time
)
VALUES
(#{courseId},#{sectionName},#{description},#{orderNum},
#{status},#{createTime},#{updateTime});
</insert>
Service 层 CourseContentService
void saveSection(CourseSection courseSection);
@Override
public void saveSection(CourseSection courseSection) {
// 补全信息
Date date = new Date();
courseSection.setCreateTime(date);
courseSection.setUpdateTime(date);
// 调用 dao 方法
courseContentMapper.saveSection(courseSection);
}
Web 层 CourseContentController
@RequestMapping("/saveOrUpdateSection")
public ResponseResult saveOrUpdateSection(@RequestBody CourseSection courseSection) {
courseContentService.saveSection(courseSection);
return new ResponseResult(true, 200, "新增章节成功", null);
}
Postman 测试接口
修改章节信息
需求分析
点击确定按钮,将修改后的章节信息保存到数据库中
查看接口文档,进行编码
Dao 层 CourseContentMapper
void updateSection(CourseSection courseSection);
<update id="updateSection" parameterType="courseSection">
UPDATE course_section
<trim prefix="SET" suffixOverrides=",">
<if test="sectionName != null and sectionName != ''">
section_name = #{sectionName},
</if>
<if test="description != null and description != ''">
description = #{description},
</if>
<if test="orderNum != null and orderNum != '' or orderNum == 0">
order_num = #{orderNum},
</if>
<if test="updateTime != null">
update_time = #{updateTime},
</if>
</trim>
<where>
<if test="id != null and id != ''">
id = #{id}
</if>
</where>
</update>
Service 层 CourseContentService
void updateSection(CourseSection courseSection);
@Override
public void updateSection(CourseSection courseSection) {
// 补全信息
courseSection.setUpdateTime(new Date());
// 调用 dao 方法
courseContentMapper.updateSection(courseSection);
}
Web 层 CourseContentController
@RequestMapping("/saveOrUpdateSection")
public ResponseResult saveOrUpdateSection(@RequestBody CourseSection courseSection) {
// 判断是否携带了章节 ID
if (courseSection.getId() == null) {
// 新增
courseContentService.saveSection(courseSection);
return new ResponseResult(true, 200, "新增章节成功", null);
} else {
// 更新
courseContentService.updateSection(courseSection);
return new ResponseResult(true, 200, "更新章节成功", null);
}
}
Postman 测试接口
修改章节状态
需求分析
点击修改章节状态按钮,将当前章节信息的状态进行修改
查看接口文档,进行编码
Dao 层 CourseContentMapper
void updateSectionStatus(CourseSection courseSection);
CourseContentMapper.xml
<update id="updateSectionStatus" parameterType="courseSection">
UPDATE course_section SET `status` = #{status}, update_time = #{updateTime} WHERE id = #{id}
</update>
Service 层 CourseContentService
void updateSectionStatus(int id, int status);
@Override
public void updateSectionStatus(int id, int status) {
// 封装数据
CourseSection courseSection = new CourseSection();
courseSection.setStatus(status);
courseSection.setUpdateTime(new Date());
courseSection.setId(id);
// 调用 mapper
courseContentMapper.updateSectionStatus(courseSection);
}
Web 层 CourseContentController
@RequestMapping("/updateSectionStatus")
public ResponseResult updateSectionStatus(int id, int status) {
courseContentService.updateSectionStatus(id, status);
// 数据响应
HashMap<Object, Object> map = new HashMap<>();
map.put("status", status);
return new ResponseResult(true, 200, "修改章节状态成功", map);
}
Postman 测试接口
新建课时信息
需求分析
点击添加阶段按钮,将弹出页面填写的章节信息保存到数据库中
查看接口文档,进行编码
Dao 层 CourseContentMapper
void saveLesson(CourseLesson courseLesson);
CourseContentMapper.xml
<insert id="saveLesson" parameterType="courseLesson">
INSERT INTO course_lesson (
course_id,
section_id,
theme,
duration,
is_free,
order_num,
`status`,
create_time,
update_time
)
VALUES
(#{courseId},#{sectionId},#{theme},#{duration},#{isFree},#{orderNum},
#{status},#{createTime},#{updateTime});
</insert>
Service 层 CourseContentService
void saveLesson(CourseLesson courseLesson);
@Override
public void saveLesson(CourseLesson courseLesson) {
// 补全信息
Date date = new Date();
courseLesson.setCreateTime(date);
courseLesson.setUpdateTime(date);
// 调用 dao 方法
courseContentMapper.saveLesson(courseLesson);
}
Web 层 CourseContentController
@RequestMapping("/saveOrUpdateLesson")
public ResponseResult saveOrUpdateLesson(@RequestBody CourseLesson courseLesson) {
courseContentService.saveLesson(courseLesson);
return new ResponseResult(true, 200, "新增课时成功", null);
}
Postman 测试接口
修改课时信息
需求分析
点击确定按钮,将修改后的课时信息保存到数据库中
查看接口文档,进行编码
Dao 层 CourseContentMapper
void updateLesson(CourseLesson courseLesson);
CourseContentMapper.xml
<update id="updateLesson" parameterType="courseLesson">
UPDATE course_lesson
<trim prefix="SET" suffixOverrides=",">
<if test="theme != null and theme != ''">
theme = #{theme},
</if>
<if test="duration != null and duration != ''">
duration = #{duration},
</if>
<if test="`isFree` != null and `isFree` != ''">
is_free = #{isFree},
</if>
<if test="orderNum != null and orderNum != '' or orderNum == 0">
order_num = #{orderNum},
</if>
<if test="updateTime != null">
update_time = #{updateTime},
</if>
</trim>
<where>
<if test="id != null and id != ''">
id = #{id}
</if>
</where>
</update>
Service 层 CourseContentService
void updateLesson(CourseLesson courseLesson);
@Override
public void updateLesson(CourseLesson courseLesson) {
// 补全信息
courseLesson.setUpdateTime(new Date());
// 调用 dao 方法
courseContentMapper.updateLesson(courseLesson);
}
Web 层 CourseContentController
@RequestMapping("/saveOrUpdateLesson")
public ResponseResult saveOrUpdateLesson(@RequestBody CourseLesson courseLesson) {
// 判断是否携带了课时 ID
if (courseLesson.getId() == null) {
// 新增
courseContentService.saveLesson(courseLesson);
return new ResponseResult(true, 200, "新增课时成功", null);
} else {
// 更新
courseContentService.updateLesson(courseLesson);
return new ResponseResult(true, 200, "修改课时成功", null);
}
}
Postman 测试接口
想了解更多,欢迎关注我的微信公众号:Renda_Zhang