文章目录
1、需求分析
1.1、课程管理
- 新建课程
- 条件查询 根据 课程名称 状态
- 课程信息列表展示(ID、课程名称、价格、排序、状态)
- 课程状态的切换 上架下架
1.2、营销信息 (营销信息其实就是课程的具体信息)
- 回显对应课程的详细信息
- 修改课程信息,包含了图片上传
1.3、配置课时(配置课时指的就是对课程内容的配置,课程内容就包括了 章节信息和课时信息)
- 添加章节
- 以树形结构的下拉列表方式,展示课程对应的章节与课时信息
- 修改章节
- 章节状态的设置
2、课程管理模块表设计
2.1、表关系介绍
3、项目搭建
3.1、创建项目-项目结构
3.2、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>
<packaging>war</packaging>
<groupId>com.lzy</groupId>
<artifactId>lagou_edu_home</artifactId>
<version>1.0-SNAPSHOT</version>
<!-- properties 是全局设置,可以设置整个maven项目的编译器 JDK版本 -->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- 重点 -->
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<!--Servlet-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</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>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
<!--Beanutils-->
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.8.3</version>
</dependency>
<!--DBUtils-->
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.6</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.1</version>
</dependency>
<!--数据库相关-->
<!-- https://mvnrepository.com/artifact/com.mysql/mysql-connector-j -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.0.31</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.9</version>
</dependency>
<!--fastjson工具包-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>com.colobu</groupId>
<artifactId>fastjson-jaxrs-json-provider</artifactId>
<version>0.3.1</version>
</dependency>
<!--文件上传-->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.2.1</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
<!-- 在build中 我们需要指定一下项目的JDK编译版本,maven默认使用1.5版本进行编译
注意 build 与 dependencies是平级关系,标签不要写错位置 -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<release>11</release>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.3.1</version>
</plugin>
</plugins>
</build>
</project>
3.3、工具类
DataUtils
public class DateUtils {
/**
* 获取日期对象 格式化后的字符串
* @return
*/
public static String getDateFormart(){
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String format = sdf.format(date);
return format;
}
}
DruidUtils
public class DruidUtils {
//1.定义成员变量
public static DataSource dataSource;
//2.静态代码块
static{
try {
//3.创建属性集对象
Properties p = new Properties();
//4.加载配置文件 Druid 连接池不能够主动加载配置文件 ,需要指定文件
InputStream inputStream = DruidUtils.class.getClassLoader().getResourceAsStream("druid.properties");
//5. 使用Properties对象的 load方法 从字节流中读取配置信息
p.load(inputStream);
//6. 通过工厂类获取连接池对象
dataSource = DruidDataSourceFactory.createDataSource(p);
} catch (Exception e) {
e.printStackTrace();
}
}
//获取连接的方法
public static Connection getConnection(){
try {
return dataSource.getConnection();
} catch (SQLException e) {
e.printStackTrace();
return null;
}
}
//获取Druid连接池对象的方法
public static DataSource getDataSource(){
return dataSource;
}
//释放资源
public static void close(Connection con, Statement statement){
if(con != null && statement != null){
try {
statement.close();
//归还连接
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void close(Connection con, Statement statement, ResultSet resultSet){
if(con != null && statement != null && resultSet != null){
try {
resultSet.close();
statement.close();
//归还连接
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
UUIDUtils
import java.util.UUID;
/**
* UUID是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯一的。
*
* UUID由以下几部分的组合:
* 1.当前日期和时间,UUID的第一个部分与时间有关,如果你在生成一个UUID之后,过几秒又生成一个UUID,则第一个部分不同,其余相同。
* 2.时钟序列。
* 3.全局唯一的IEEE机器识别号,如果有网卡,从网卡MAC地址获得,没有网卡以其他方式获得。
*
*/
public class UUIDUtils {
//获取唯一ID的 方法
public static String getUUID() {
return UUID.randomUUID().toString().replace("-", "");
}
}
3.4、配置类
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/lagou_edu?characterEncoding=UTF-8
username=root
password=lige0612
initialSize=5
maxActive=10
maxWait=3000
3.5、实体类
Course
@Data
public class Course implements Serializable {
//使用 JSONField 设置ordinal的值,来对转换成的JSON数据进行排序
//课程ID
@JSONField(ordinal = 1)
private int id;
//课程名称
@JSONField(ordinal = 2)
private String course_name;
//课程介绍
@JSONField(ordinal = 3)
private String brief;
//讲师名称
@JSONField(ordinal = 4)
private String teacher_name;
//讲师介绍
@JSONField(ordinal = 5)
private String teacher_info;
//课程原价
@JSONField(ordinal = 6)
private double price;
//原价标签
@JSONField(ordinal = 7)
private String price_tag;
//课程优惠价
@JSONField(ordinal = 8)
private double discounts;
//课程概述
@JSONField(ordinal = 9)
private String preview_first_field;
//课程概述第二个字段
@JSONField(ordinal = 10)
private String preview_second_field;
//分享图片url
@JSONField(ordinal = 11)
private String course_img_url;
//分享标题
@JSONField(ordinal = 12)
private String share_title;
//分享描述
@JSONField(ordinal = 13)
private String share_description;
//课程描述
@JSONField(ordinal = 14)
private String course_description;
//排序
@JSONField(ordinal = 15)
private int sort_num;
//课程状态,0-草稿,1-上架
@JSONField(ordinal = 16)
private int status;
//创建时间
@JSONField(ordinal = 17)
private String create_time;
//修改时间
@JSONField(ordinal = 18)
private String update_time;
//是否删除
@JSONField(ordinal = 19)
private int isDel;
@JSONField(ordinal = 20)
private String share_image_title; //分享图title
//使用JSONField(serialize = false)排除不需要转换的字段
@JSONField(serialize = false)
private int total_course_time; //课时数
@JSONField(serialize = false)
private int sales; //显示销量
@JSONField(serialize = false)
private int actual_sales; //真实销量
@JSONField(serialize = false)
private int is_new; //是否新品
@JSONField(serialize = false)
private String is_new_des; //广告语
@JSONField(serialize = false)
private int last_operator_id; //最后操作者
@JSONField(serialize = false)
private int total_duration; //总时长
@JSONField(serialize = false)
private long course_type; //课程类型
@JSONField(serialize = false)
private String last_notice_time; //最后课程最近通知时间
@JSONField(serialize = false)
private long is_gray; //是否是灰度课程
@JSONField(serialize = false)
private long grade; //级别
}
Course_Lesson
@Data
public class Course_Lesson implements Serializable {
//课时id
@JSONField(ordinal = 1)
private int id;
//课程id
@JSONField(ordinal = 2)
private int course_id;
//章节id
@JSONField(ordinal = 3)
private int section_id;
//课时主题
@JSONField(ordinal = 4)
private String theme;
//课程时长
@JSONField(ordinal = 5)
private int duration;
//是否免费
@JSONField(ordinal = 6)
private int is_free;
//课时排序
@JSONField(ordinal = 7)
private int orderNum;
//课时状态,0-隐藏,1-未发布,2-已发布
@JSONField(ordinal = 8)
private int status;
//创建时间
@JSONField(ordinal = 9)
private String create_time;
//修改时间
@JSONField(ordinal = 10)
private String update_time;
//是否删除
@JSONField(ordinal = 11)
private int isDel;
@Override
public String toString() {
return "Course_Lesson{" +
"id=" + id +
", course_id=" + course_id +
", section_id=" + section_id +
", theme='" + theme + '\'' +
", duration=" + duration +
", is_free=" + is_free +
", orderNum=" + orderNum +
", status=" + status +
", create_time='" + create_time + '\'' +
", update_time='" + update_time + '\'' +
", isDel=" + isDel +
'}';
}
@JSONField(serialize = false)
private String start_img_url; //课时背景图
@JSONField(serialize = false)
private String text_content; //课时内容
@JSONField(serialize = false)
private String markdown_text_content; //课时内容(markdown文本)
@JSONField(serialize = false)
private String transcode; //转码集合
@JSONField(serialize = false)
private String resource_url; //课程资源路径
@JSONField(serialize = false)
private int last_operator_id; //最后操作者id
@JSONField(serialize = false)
private String ali_file_url; //阿里云视频文件URL
@JSONField(serialize = false)
private String ali_file_dk; //阿里云视频文件的DK
@JSONField(serialize = false)
private String ali_file_edk; //阿里云视频文件的EDK
@JSONField(serialize = false)
private String ali_file_vid; //阿里云视频资源文件ID
@JSONField(serialize = false)
private int is_timing_publish; //是否定时发布
@JSONField(serialize = false)
private String publish_time; //定时发布时间
}
Course_Media
package com.lzy.pojo;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
/**
* 课程媒体表
* */
@Data
public class Course_Media implements Serializable {
//课程媒体主键ID
@JSONField(ordinal = 1)
private int id;
//课程id
@JSONField(ordinal = 1)
private int course_id;
//章节id
@JSONField(ordinal = 2)
private int section_id;
//课时id
@JSONField(ordinal = 3)
private int lesson_id;
//封面图Url
@JSONField(ordinal = 4)
private String cover_image_url;
//媒体类型,0-音频,1-视频
@JSONField(ordinal = 5)
private int media_type;
//状态
@JSONField(ordinal = 6)
private int status;
//创建时间
@JSONField(ordinal = 7)
private String create_time;
//修改时间
@JSONField(ordinal = 8)
private String update_time;
//是否删除
@JSONField(ordinal = 9)
private int isDel;
@Override
public String toString() {
return "Course_Media{" +
"id=" + id +
", course_id=" + course_id +
", section_id=" + section_id +
", lesson_id=" + lesson_id +
", cover_image_url='" + cover_image_url + '\'' +
", media_type=" + media_type +
", status=" + status +
", create_time='" + create_time + '\'' +
", update_time='" + update_time + '\'' +
", isDel=" + isDel +
'}';
}
@JSONField(serialize = false)
private long channel; //媒体渠道
@JSONField(serialize = false)
private String duration; //时长
@JSONField(serialize = false)
private String file_id; //媒体资源文件ID
@JSONField(serialize = false)
private String file_url; //媒体文件URL
@JSONField(serialize = false)
private String file_edk; //媒体资源文件对应的EDK
@JSONField(serialize = false)
private double file_size; //文件大小MB
@JSONField(serialize = false)
private String file_name; //文件名称
@JSONField(serialize = false)
private String file_dk; //媒体资源文件对应的DK
@JSONField(serialize = false)
private long last_operator_id; //最后操作者ID
@JSONField(serialize = false)
private long duration_num; //时长,秒数
}
Course_Section
package com.lzy.pojo;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
/**
* 课程章节
* */
@Data
public class Course_Section implements Serializable {
//课程章节id
@JSONField(ordinal = 1)
private int id;
//课程id
@JSONField(ordinal = 2)
private int course_id;
//章节名
@JSONField(ordinal = 3)
private String section_name;
//章节描述
@JSONField(ordinal = 4)
private String description;
//排序
@JSONField(ordinal = 5)
private long order_num;
//章节状态,0:隐藏;1:待更新;2:已发布
@JSONField(ordinal = 6)
private long status;
//创建时间
@JSONField(ordinal = 7)
private String create_time;
//修改时间
@JSONField(ordinal = 8)
private String update_time;
//是否删除 0-未删除,1-已删除
@JSONField(ordinal = 9)
private int isDel;
@Override
public String toString() {
return "Course_Section{" +
"id=" + id +
", course_id=" + course_id +
", section_name='" + section_name + '\'' +
", description='" + description + '\'' +
", order_num=" + order_num +
", status=" + status +
", create_time='" + create_time + '\'' +
", update_time='" + update_time + '\'' +
", isDel=" + isDel +
'}';
}
@JSONField(serialize = false)
private int last_operator_id; //最后操作者ID
@JSONField(serialize = false)
private int is_visible; //是否可见
@JSONField(serialize = false)
private String last_operator; //最后操作者
}
3.6、通用Servlet
BaseServlet
public class BaseServlet extends HttpServlet {
/*
*
* dogGET方法作为一个调用器,根据请求功能的不同,调用对应的方法
* 规定必须传递一个参数 methodName=功能名
* */
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1、获取参数 要访问的方法名
String methodName = req.getParameter("methodName");
// 2、判断执行对应的方法 通过反射来进行优化
if(methodName != null){
// 通过反射优化代码,提升代码的可维护性
// 1、获取字节码文件对象 this = TestServlet
try {
Class c = this.getClass();
// 2、根据传入的方法名,获取对应的方法对象
Method method = c.getMethod(methodName, HttpServletRequest.class, HttpServletResponse.class);
// 3、调用method对象的invoke方法,执行对应的功能
method.invoke(this, req, resp);
} catch (Exception e) {
e.printStackTrace();
System.out.println("请求的功能不存在");
}
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
我们新建的业务相关的类继承这个Base,然后在传参数的时候,传入对应的方法名即可调用
3.7、三层结构搭建
4、功能一:查询课程列表信息
- 名称: findCourseList
- 描述: 查询课程列表信息
- URL: http://localhost:8080/lagou_edu_home/course
- 请求方式: GET
- 请求参数
字段 | 说明 | 类型 | 是否必须 | 备注 |
---|---|---|---|---|
methodName | 要访问的功能名 | String | 是 | 该字段必须填写,用来确定要访问是 哪一个的方法 |
- 请求参数示例:
methodName: "findCourseList"
- 响应结果
字段 | 说明 | 类型 | 是否必须 | 备注 |
---|---|---|---|---|
id | 课程id | int | 是 | |
course_name | 课程名称 | String | 是 | |
price | 课程价格 | double | 是 | 课程的原价格 |
sort_num | 课程排序 | int | 是 | 数字越大,越排在后面 |
status | 课程状态 | int | 是 | 0-草稿,1-上架 |
- 响应结果示例
[{
"id": 1,
"course_name": "32个Java面试必考点",
"price": 8000,
"sort_num": 1,
"status": 1
}]
CourseDaoImpl
public class CourseDaoImpl implements CourseDao {
@Override
public List<Course> findCourseList() {
// 1、创建QueryRunner
QueryRunner queryRunner = new QueryRunner(DruidUtils.getDataSource());
// 2、编写SQL 判断是否删除 取出is_del = 0的数据,未删除的数据
String sql = "select id,course_name,price,sort_num,status,is_del from course where is_del = ?";
// 3、执行查询
try {
List<Course> query = queryRunner.query(sql, new BeanListHandler<Course>(Course.class), 0);
return query;
} catch (
SQLException e) {
e.printStackTrace();
return null;
}
}
}
CourseServiceImpl
public class CourseServiceImpl implements CourseService {
// 创建CourseDao
private CourseDao courseDao = new CourseDaoImpl();
@Override
public List<Course> findCourseList() {
return courseDao.findCourseList();
}
}
CourseServlet
@WebServlet("/course")
public class CourseServlet extends BaseServlet {
// 创建CourseService
private CourseService courseService = new CourseServiceImpl();
public void findCourseList(HttpServletRequest request, HttpServletResponse response){
try {
// 1、接收参数 在BaseServlet中实现了
// 2、业务处理
List<Course> courseList = courseService.findCourseList();
//3、响应结果
// 属性过滤 指定要获取的属性
SimplePropertyPreFilter filter = new SimplePropertyPreFilter(Course.class,
"id", "course_name", "price", "sort_num", "status");
String res = JSON.toJSONString(courseList, filter);
System.out.println(res);
response.getWriter().println(res);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
处理乱码问题使用一个过滤器来处理
@WebFilter("/*")
public class EncodingFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request= (HttpServletRequest) servletRequest;
HttpServletResponse response= (HttpServletResponse)servletResponse;
//解决请求乱码
request.setCharacterEncoding("UTF-8");
//解决响应乱码
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
//放行
filterChain.doFilter(request,response);
}
@Override
public void destroy() {
}
}
新知识点
:
5、功能二:多条件查询课程信息
- 名称: findByCourseNameAndStatus
- 描述: 根据条件查询课程信息
- URL: http://localhost:8080/lagou_edu_home/course
- 请求方式: GET
- 请求参数
字段 | 说明 | 类型 | 是否必需 | 备注 |
---|---|---|---|---|
methodName | 要访问的功能名 | String | 是 | 该字段必须填写,用来确定要访问是 哪一个的方法 |
course_name | 课程名称 | String | 否 | 选中要查询的课程名,查询课程信息 |
status | 课程状态 | int | 否 | 选择课程状态 ,0-草稿,1-上架 |
- 请求参数示例:
methodName:"findByCourseNameAndStatus",
course_name:"32个Java面试必考点",
status:"1"
- 响应结果
字段 | 说明 | 类型 | 是否必须 | 备注 |
---|---|---|---|---|
id | 课程id | int | 是 | |
course_name | 课程名称 | String | 是 | |
price | 课程价格 | double | 是 | 课程的原价格 |
sortNum | 课程排序 | int | 是 | 数字越大,越排在后面 |
status | 课程状态 | int | 是 | 0-草稿,1-上架 |
- 响应结果示例
[{
"id": 1,
"course_name": "32个Java面试必考点",
"price": 8000,
"sort_num": 1,
"status": 1
}]
CouseDaoImpl
// 根据多条件查询课程信息
@Override
public List<Course> findByCourseNameAndStatus(String courseName, String status) {
try {
// 1、创建QueryRunner
QueryRunner queryRunner = new QueryRunner(DruidUtils.getDataSource());
// 2、编写SQL 当前的查询为多条件不定项查询
// 2.1、创建StringButffer对象,将SQL字符串添加进缓冲区
StringBuffer stringBuffer = new StringBuffer("select id, course_name, price, sort_num,status from course where 1=1 and is_del = ?");
// 2.2、创建list集合 保存参数
ArrayList<Object> lists = new ArrayList<>();
lists.add(0);
// 2.3、判断传入的参数是否为空
if(courseName != null && courseName != null){
stringBuffer.append(" and course_name like ?");
// like查询 需要拼接 %
courseName = "%" + courseName + "%";
// 然后条件放入list集合
lists.add(courseName);
}
if(status != null && status != ""){
stringBuffer.append("and status = ?");
// 将status转换为int
int i = Integer.parseInt(status);
lists.add(i);
}
// 执行查询
List<Course> query = queryRunner.query(stringBuffer.toString(), new BeanListHandler<Course>(Course.class), lists.toArray());
return query;
} catch (SQLException e) {
e.printStackTrace();
return null;
}
}
CourseServiceImpl
@Override
public List<Course> findByCourseNameAndStatus(String courseName, String status) {
return courseDao.findByCourseNameAndStatus(courseName, status);
}
CourseServlet
// 根据条件查询课程信息、
public void findByCourseNameAndStatus(HttpServletRequest request, HttpServletResponse response){
try {
// 1、接受参数
String courseName = request.getParameter("course_name");
String status = request.getParameter("status");
// 2、业务处理
List<Course> courseList = courseService.findByCourseNameAndStatus(courseName, status);
// 3、响应结果
SimplePropertyPreFilter filter = new SimplePropertyPreFilter(Course.class,
"id", "course_name", "price", "sort_num", "status");
String res = JSON.toJSONString(courseList, filter);
response.getWriter().println(res);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
新知识点
- 我感觉那个拼接sql的那个就是mybatis中那个动态sql中的if标签应该是一个意思
- 还要哪个where 1=1也是一个小技巧
- 使用StringBuffer做字符串拼接
- 甚至还有程序的健壮性
都是值得学习的
6、功能三:新建课程
- 名称: courseSalesInfo
- 描述: 保存课程相关的营销信息
- URL: http://localhost:8080/lagou_edu_home/courseSalesInfo
- 请求方式: POST
- 请求参数
字段 | 说明 | 类型 | 是否必需 | 备注 |
---|---|---|---|---|
id | 课程id | int | 否 | 添加操作不用携带, 修改操作必须携带ID |
course_name | 课程名称 | String | 是 | |
brief | 课程简介 | String | 是 | 一句话介绍课程 |
teacher_name | 讲师名称 | String | 是 | |
teacher_info | 讲师介绍 | String | 是 | |
preview_first_field | 课程概述1 | String | 是 | 第一段描述 例如: 课程共15讲 |
preview_second_field | 课程概述2 | String | 是 | 第二段描述 例如: 每周五更新 |
discounts | 售卖价格 | double | 是 | 课程的售卖价格 |
price | 商品原价 | double | 是 | 课程的原销售价 |
price_tag | 促销文案 | String | 是 | 例如: 立即抢购 |
share_image_title | 分享图title | String | 是 | |
share_title | 分享标题 | String | 是 | |
share_description | 分享描述 | String | 是 | |
course_description | 课程描述 | String | 是 | |
file | 文件 | 是 |
- 请求参数示例 key:value 格式
file:文件
course_name: 微服务架构
brief: 大厂架构师带你一起学
teacher_name: PDD
teacher_info: 技术精湛安全驾驶30年
preview_first_field: 共5讲
preview_second_field: 每周二更新
discounts: 88.8
price: 800.0
price_tag: 先到先得
share_image_title: hello word
share_title: IT修炼之路永无止境
share_description: 金牌讲师带你了解最新最牛的技术让你的实力再次进阶!
course_description: 十年编程两茫茫,工期短,需求长。千行代码,Bug何处藏。纵使上线又如何,新版本,继续忙。黑白颠倒没商量,睡地铺,吃食堂。夜半梦醒,无人在身旁。最怕灯火阑珊时,手机响,心里慌.
- 响应结果
字段 | 说明 | 类型 | 是否必须 | 备注 |
---|---|---|---|---|
status | 表示执行成功或失败 | int | 是 | 0 表示成功, 1 表示失败 |
msg | 响应消息 | String | 是 |
- 响应结果示例
成功
{"msg":"success","status":0}
失败
{"msg":"fail","status":1}
6.1、新知识点:文件上传
6.1.1、文件上传介绍
文件上传的实质:文件的拷贝
- 文件上传:从本地将文件拷贝到服务器磁盘上
- 客户端:需要编写文件上传表单
- 服务端:需要编写代码接受上传的文件
6.1.2、客户端编码
文件上传三要素
- 表单提交方式:post(get方式提交有大小限制,post没有)
- 表单的enctype属性:必须设置为 multipart/form-data
- enctype就是encodetype就是编码类型的意思
- multipart/form-data是多部件文件上传,指表单数据有多部分构成,既有文本数据,又有文件等二进制数据的意思。
- 表单必须有文件上传项: file ,必须要有name属性和值
注意: 默认情况下,表单的enctype的值是application/x-www-form-urlencoded,不能用于文件上传,只有使用了multipart/form-data,才能完整的传递文件数据
代码实例
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%--
表单提交必须是POST ,
表单的enctype属性:必须设置为 multipart/form-data.
input的type类型必须指定为: file, 一定要有name属性
--%>
<form action="${pageContext.request.contextPath}/upload" method="post"
enctype="multipart/form-data">
<input type="file" name="upload">
<br>
<input type="text" name="name">
<input type="text" name="password">
<input type="submit" value="文件上传">
</form>
</body>
</html>
6.1.3、服务端编码
服务端要接受文件上传的表单数据
-
文件上传的接收原理
-
请求体
-
服务端获取上传的文件
- 通过request获取请求体的内容
- 解析请求体 多部件上传的特点是,每个input都是一个表单项,根据分隔符将请求中所有的内容,切割成数组,数组中的每一个元素 都是一个表单项
- 遍历数组,分清楚那个是普通的表单项, 哪个是 文件上传项,如何区分? 判断是否有 filename
- 获取到普通表单项中的内容,通过属性name获取
- 获取文件上传项内容
文件名: filname = aaa.txt 文件内容: - 使用IO将文件内容,保存到服务器中
6.1.4、FileUpload工具类
FileUpload包可以很容易地将文件上传到你的Web应用程序.
IOUtils封装了Java中io的常见操作,使用十分方便 ,需要下载 commons-io-1.4.jar 包
- 导包
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.2.1</version>
</dependency>
类名 | 介绍 |
---|---|
DiskFileItemFactory | 磁盘文件项工厂, 读取文件时相关的配置,比如: 缓存的大小 , 临时目录的位置 |
ServletFileUplaod | 文件上传的一个核心类 |
FileItem | 代表每一个表单项 |
- 文件上传的API的详情
ServletFileUpload
方法 | 说明 |
---|---|
isMultipartContent(request); | 判断是否是一个文件上传的表单 |
parseRequest(request); | 解析request获得表单项的集合 |
setHeaderEncoding(“UTF-8”); | 设置上传的文件名的编码方式 |
FileItem
方法 | 说明 |
---|---|
isFormField() | 判断是否是普通表单项 |
getFieldName() | 获得表单的name属性值 |
item.getString() | 获得表单的value值 |
getName() | 获得上传文件的名称 |
getInputStream() | 获得上传文件 |
delete() | 删除临时文件 |
6.1.5、将图片上传到tomcat服务器
- 将部署方式修改为war模式,把项目部署在tomcat的webapps下
-
idea中部署项目两种方式
- war模式:将项目以war包的形式上传真实到服务器的webapps目录中;
- war exploded模式:仅仅是目录的映射,就相当于tomcat在项目源文件夹中启动一样;
-
- 在webapps下创建upload文件夹专门用来保存图片
- 修改图片的输出路径
- 获取到项目的运行目录信息
- 截取到webapps的 目录路径
- 拼接输出路径,将图片保存到upload
@WebServlet("/upload")
public class FileUploadServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
// 1、创建磁盘文件工厂对象
DiskFileItemFactory factory = new DiskFileItemFactory();
// 2、创建文件上传核心类
ServletFileUpload upload = new ServletFileUpload(factory);
// 2.1、设置上传文件的编码
upload.setHeaderEncoding("utf-8");
// 2.2、判断表单是否是 文件上传表单
boolean multipartContent = ServletFileUpload.isMultipartContent(req);
// 2.3、是文件上传表单
if(multipartContent){
// 3、解析request -- 获取表单项的集合
List<FileItem> fileItems = upload.parseRequest(req);
// 4、遍历表单项集合
for (FileItem fileItem : fileItems) {
// 5、判断是普通的表单项 还是文件上传项
boolean formField = fileItem.isFormField();
// 普通表单项
if(formField){
String fileFieldName = fileItem.getFieldName();
String fileFieldContent = fileItem.getString("utf-8");
System.out.println(fileFieldName + " = " + fileFieldContent);
}else {
// 文件上传项
// 获取文件名
String fileName = fileItem.getName();
// 保证上传文件没有重复,拼接新的文件名,使用UUID保证不重复
String newFileName = UUIDUtils.getUUID() + "_" + fileName;
// 获取输入流
InputStream inputStream = fileItem.getInputStream();
// 创建输出流
// 1、获取项目的运行目录
String realPath = this.getServletContext().getRealPath("/");
// 2、截取到webapps目录路径
String webappsPath = realPath.substring(0, realPath.indexOf("lagou_edu_home"));
// 3、拼接输出
FileOutputStream fileOutputStream = new FileOutputStream(webappsPath + "/upload/" + newFileName);
// 使用IOUtils 完成文件的copy
IOUtils.copy(inputStream, fileOutputStream);
// 关闭流
inputStream.close();
fileOutputStream.close();
}
}
}
} catch (FileUploadException e) {
e.printStackTrace();
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
- 还要将upload这个文件作为资源放到tomcat服务器中才可以
- 然后就可以正常的访问图片了(两种访问方式)
<img src="/upload/abbd99891af442a8a9cb65848744452e_qiyu.jpg">
http://localhost:8080/upload/abbd99891af442a8a9cb65848744452e_qiyu.jpg
6.2、新知识点BeanUtis工具类
- 介绍
BeanUtils 是 Apache commons组件的成员之一,主要用于简化JavaBean封装数据的操作。可以将
一个表单提交的所有数据封装到JavaBean中。
就是把一个map集合转换成JavaBean
- 导入依赖
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.8.3</version>
</dependency>
- BeanUtils 对象常用方法
方法 | 描述 |
---|---|
populate(Object bean, Map properties) | 将Map数据封装到指定Javabean中,一般用于将表单的所有数据封装到javabean |
setProperty(Object obj,String name,Objectvalue) | 设置属性值 |
getProperty(Object obj,String name) | 获得属性值 |
测试代码
public class TestBeanUtils {
@Test
public void test01() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
// 1、创建course对象
Course course = new Course();
// 2、创建Map
Map<String, Object> map = new HashMap<>();
// 3、向map集合中添加数据,可以要与course的属性名保持一致,value的数据类型与course的属性的类型保持一致
map.put("id", 1);
map.put("course_name", "大数据");
map.put("brief", "课程包含所有大数据流行的技术");
map.put("teacher_name", "周星星");
map.put("info", "非著名演员");
// 4、将map中的数据封装到course中
BeanUtils.populate(course, map);
System.out.println(course.getId() + " " + course.getCourse_name() + " " +
course.getBrief() + " " + course.getTeacher_name() + " " +
course.getTeacher_info());
// 设置属性, 获取属性
BeanUtils.setProperty(course,"price", 100.0);
BeanUtils.getProperty(course, "price");
System.out.println("price = " + course.getPrice());
}
}
CourseDaoImpl (负责将数据持久化)
// 保存课程营销信息
@Override
public int saveCourseSalesInfo(Course course) {
try {
// 1、创建QueryRunner
QueryRunner queryRunner = new QueryRunner(DruidUtils.getDataSource());
// 2、编写SQL
String sql = "insert into course(\n" +
"course_name,\n" +
"brief,\n" +
"teacher_name,\n" +
"teacher_info,\n" +
"preview_first_field,\n" +
"preview_second_field,\n" +
"discounts,\n" +
"price,\n" +
"price_tag,\n" +
"share_image_title,\n" +
"share_title,\n" +
"share_description,\n" +
"course_description,\n" +
"course_img_url,\n" +
"status,\n" +
"create_time,\n" +
"update_time\n" +
")values(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);";
Object[] param = {
course.getCourse_name(),
course.getBrief(),
course.getTeacher_name(),
course.getTeacher_info(),
course.getPreview_first_field(),
course.getPreview_second_field(),
course.getDiscounts(),
course.getPrice(),
course.getPrice_tag(),
course.getShare_image_title(),
course.getShare_title(),
course.getShare_description(),
course.getCourse_description(),
course.getCourse_img_url(),
course.getStatus(),
course.getCreate_time(),
course.getUpdate_time()
};
// 4、执行插入操作
int update = queryRunner.update(sql, param);
return update;
} catch (SQLException e) {
e.printStackTrace();
return 0;
}
}
CourseServiceImpl(负责业务上的补全)
@Override
public String saveCourseSalesInfo(Course course) {
// 1、补全课程营销信息
String dateFormart = DateUtils.getDateFormart();
course.setCreate_time(dateFormart);
course.setUpdate_time(dateFormart);
course.setStatus(1);
// 2、执行插入操作
int row = courseDao.saveCourseSalesInfo(course);
if(row > 0){
// 插入成功
String res = StatusCode.SUCCESS.toString();
return res;
}else{
String res = StatusCode.FAIL.toString();
return res;
}
}
CourseSaleInfoServlet(处理数据和文件,调用server层,返回响应结果)
@WebServlet("/courseSalesInfo")
public class CourseSalesInfoServlet extends HttpServlet {
private CourseService courseService = new CourseServiceImpl();
/*
* 保存课程营销信息
* 收集表单数据,封装到course对象中,将图片你删除更换到tomcat服务器中
* */
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
// 1、创建Course对象
Course course = new Course();
// 2、创建Map集合,用来收集数据
Map<String, Object> map = new HashMap<>();
// 3、创建磁盘工厂对象
DiskFileItemFactory factory = new DiskFileItemFactory();
// 4、创建文件上传核心对象
ServletFileUpload upload = new ServletFileUpload(factory);
// 5、解析request对象,获取表单项集合
upload.setHeaderEncoding("utf-8");
List<FileItem> fileItems = upload.parseRequest(req);
// 6、遍历集合
for (FileItem fileItem : fileItems) {
boolean formField = fileItem.isFormField();
if (formField){
// 是普通表单项,获取表单项中的数据保存到map中
String key = fileItem.getFieldName();
String value = fileItem.getString("utf-8");
System.out.println("key = " + key + " value = " + value);
// 使用map收集数据
map.put(key, value);
}else{
// 文件上传项
// 获取文件名
String fileName = fileItem.getName();
// 防止名称重复
String newFileName = UUIDUtils.getUUID().toString() + "_" + fileName;
// 获取输入流
InputStream inputStream = fileItem.getInputStream();
// 获取当前项目的路径
String realPath = this.getServletContext().getRealPath("/");
// 进行路径截取
String webappsPath = realPath.substring(0, realPath.indexOf("lagou_edu_home"));
// 获取输出流
OutputStream outputStream = new FileOutputStream(webappsPath + "/upload/" + newFileName);
// 然后将输入流中的东西放入输出流中
IOUtils.copy(inputStream, outputStream);
// 关流
inputStream.close();
outputStream.close();
// 将图片路径进行保存
map.put("course_img_url", Constants.LOCAL_URL + "/upload/" + newFileName);
}
}
// 利用BeanUtils将map对象转换成course
BeanUtils.populate(course, map);
// 补全信息
String dateFormart = DateUtils.getDateFormart();
course.setCreate_time(dateFormart); // 创建时间
course.setUpdate_time(dateFormart); // 更新时间
course.setStatus(1); // 上架
// 插入到数据库中
String res = courseService.saveCourseSalesInfo(course);
// 响应结果
resp.getWriter().println(res);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
新知识点
:
-
了解了点文件上传的原理(之前学javaweb的时候也碰到过这样的问题)
-
了解FileUpload和IOUtils组合使用完成复杂表单项的处理(那几个常用的API)
-
还学到了利用工具类BeanUtils将Map转换为指定的JavaBean(常用API)
==org.apache.catalina.core.ContainerBase.addChildInternal ContainerBase.addChild: start:==这个报错就是你写请求路径的时候没有加"/"
7、功能四:修改课程营销信息
- 名称: findCourseById
- 描述: 根据ID查询课程信息
- URL: http://localhost:8080/lagou_edu_home/course
- 请求方式: GET
- 请求参数
字段 | 说明 | 类型 | 是否必需 | 备注 |
---|---|---|---|---|
methodName | 要访问的功能名 | String | 是 | 该字段必须填写,用来确定要访问是 哪一个的方法 |
id | 课程ID | int | 是 | 根据ID查询 |
- 请求参数示例
methodName: "findCourseById",
id: 10
- 响应参数
字段 | 说明 | 类型 | 是否必需 | 备注 |
---|---|---|---|---|
id | 课程ID | 是 | ||
course_name | 课程名称 | String | 是 | |
brief | 课程简介 | String | 是 | 一句话介绍课程 |
teacher_name | 讲师名称 | String | 是 | |
teacher_info | 讲师介绍 | String | 是 | |
preview_first_field | 课程概述1 | String | 是 | 第一段描述 例如: 课程共15讲 |
preview_second_field | 课程概述2 | String | 是 | 第二段描述 例如: 每周五更新 |
discounts | 售卖价格 | double | 是 | 课程的售卖价格 |
price | 商品原价 | double | 是 | 课程的原销售价 |
price_tag | 促销文案 | String | 是 | 例如: 立即抢购 |
course_img_url | 分享图url | String | 是 | |
share_title | 分享标题 | String | 是 | |
share_image_title | 分享图title | String | 是 | |
share_description | 分享描述 | String | 是 | |
course_description | 课程描述 | String | 是 | |
status | 课程状态 | int | 是 |
- 响应数据示例
{
"id": 19,
"course_name": "微服务架构",
"brief": "大厂架构师带你一起学",
"teacher_name": "PDD",
"teacher_info": "技术精湛,安全驾驶30年",
"price": 800,
"price_tag": "先到先得",
"discounts": 88.8,
"preview_first_field": "共5讲",
"preview_second_field": "每周二更新",
"course_img_url": "H:/upload/cacf91081ca14b4eb4b4700f10a382f6giao.jpg",
"share_title": "IT修炼之路,永无止境",
"share_description": "金牌讲师带你了解最新最牛的技术,让你的实力再次进阶!",
"course_description": "十年编程两茫茫,工期短,需求长。千行代码,Bug何处藏。纵使上线又如何,新版本,继续忙。黑白颠倒没商量,睡地铺,吃食堂。夜半梦醒,无人在身旁。最怕灯火阑珊时,手机响,心里慌.",
"status":0
}
思考
:我第一眼看到这个流程分析的时候就有点不太懂,因为我之前做个一个前后端不分离的一个书城项目,比如说它要修改的时候,点击这个营销信息的时候,会请求一个Servelt然后通过这个Servlet跳转到指定的页面中并且把要回显的数据也传了过去,这个跳转的过程是在servlet中执行的,但是我不知道这个前后端分离的版本是不是在后端处理的这个,我猜应该是返回一个成功与否的消息,然后将回显的信息传过去,在前端通过消息来判断是否要跳转到详情页面并将数据带过去进行回显
后来看的处理过程是这样子的,搞两个接口根据id查询信息,一个就是那个新增课程的那个修改了一下,因为本质都差不多,在新增课程里面对id是否为null做了一个判断就可以利用代码了,然后根据id查信息,后直接返回一个消息,并且直接把信息转化成json用response打到了页面上,但是我还是不知道前端怎么拿到数据,难道要写前端的时候再去修改吗
CourseDaoImpl两个dao一个查询一个修改
// 根据ID查询课程信息
@Override
public Course findCourseById(int id) {
try {
// 1、创建QueryRunner
QueryRunner queryRunner = new QueryRunner(DruidUtils.getDataSource());
// 2、创建sql语句
String sql = "select \n" +
"id,\n" +
"course_name,\n" +
"brief,\n" +
"teacher_name,\n" +
"teacher_info,\n" +
"preview_first_field,\n" +
"preview_second_field,\n" +
"discounts,\n" +
"price,\n" +
"price_tag,\n" +
"course_img_url,\n" +
"share_title,\n" +
"share_image_title,\n" +
"share_description,\n" +
"course_description,\n" +
"status\n" +
"\n" +
"from course where id = ?";
// 3、查询数据
Course query = queryRunner.query(sql, new BeanHandler<Course>(Course.class), id);
return query;
} catch (SQLException e) {
e.printStackTrace();
return null;
}
}
// 修改课程信息
@Override
public int updateCourseSalesInfo(Course course) {
try {
QueryRunner queryRunner = new QueryRunner(DruidUtils.getDataSource());
String sql = "update course set \n" +
"course_name = ?,\n" +
"brief = ?,\n" +
"teacher_name = ?,\n" +
"teacher_info = ?,\n" +
"preview_first_field = ?,\n" +
"preview_second_field = ?,\n" +
"discounts = ?,\n" +
"price = ?,\n" +
"price_tag = ?,\n" +
"share_image_title = ?,\n" +
"share_title = ?,\n" +
"share_description = ?,\n" +
"course_description = ?,\n" +
"course_img_url = ?,\n" +
"update_time = ?\n" +
"where id = ?";
Object[] params = {
course.getCourse_name(),
course.getBrief(),
course.getTeacher_name(),
course.getTeacher_info(),
course.getPreview_first_field(),
course.getPreview_second_field(),
course.getDiscounts(),
course.getPrice(),
course.getPrice_tag(),
course.getShare_image_title(),
course.getShare_title(),
course.getShare_description(),
course.getCourse_description(),
course.getCourse_img_url(),
course.getUpdate_time(),
course.getId()};
int update = queryRunner.update(sql, params);
return update;
} catch (SQLException e) {
e.printStackTrace();
return 0;
}
}
CourseServiceImpl两个接口
// 根据id查课程信息
@Override
public Course findCourseById(int id) {
return courseDao.findCourseById(id);
}
// 修改课程信息
@Override
public String updateCourseSalesInfo(Course course) {
int row = courseDao.updateCourseSalesInfo(course);
// 判断是否保存成功
if (row > 0){
// 保存成功
String res = StatusCode.SUCCESS.toString();
return res;
}else{
// 保存失败
String res = StatusCode.FAIL.toString();
return res;
}
}
CourseServlet
- 修改了新增的方法
/*
* 保存课程营销信息
* 收集表单数据,封装到course对象中,将图片你删除更换到tomcat服务器中
* */
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
// 1、创建Course对象
Course course = new Course();
// 2、创建Map集合,用来收集数据
Map<String, Object> map = new HashMap<>();
// 3、创建磁盘工厂对象
DiskFileItemFactory factory = new DiskFileItemFactory();
// 4、创建文件上传核心对象
ServletFileUpload upload = new ServletFileUpload(factory);
// 5、解析request对象,获取表单项集合
upload.setHeaderEncoding("utf-8");
List<FileItem> fileItems = upload.parseRequest(req);
// 6、遍历集合
for (FileItem fileItem : fileItems) {
boolean formField = fileItem.isFormField();
if (formField){
// 是普通表单项,获取表单项中的数据保存到map中
String key = fileItem.getFieldName();
String value = fileItem.getString("utf-8");
System.out.println("key = " + key + " value = " + value);
// 使用map收集数据
map.put(key, value);
}else{
// 文件上传项
// 获取文件名
String fileName = fileItem.getName();
// 防止名称重复
String newFileName = UUIDUtils.getUUID().toString() + "_" + fileName;
// 获取输入流
InputStream inputStream = fileItem.getInputStream();
// 获取当前项目的路径
String realPath = this.getServletContext().getRealPath("/");
// 进行路径截取
String webappsPath = realPath.substring(0, realPath.indexOf("lagou_edu_home"));
// 获取输出流
OutputStream outputStream = new FileOutputStream(webappsPath + "/upload/" + newFileName);
// 然后将输入流中的东西放入输出流中
IOUtils.copy(inputStream, outputStream);
// 关流
inputStream.close();
outputStream.close();
// 将图片路径进行保存
map.put("course_img_url", Constants.LOCAL_URL + "/upload/" + newFileName);
}
}
// 利用BeanUtils将map对象转换成course
BeanUtils.populate(course, map);
String dateFormart = DateUtils.getDateFormart();
String res = "";
if(map.get("id") != null){
// 修改操作
// 补全信息
course.setUpdate_time(dateFormart);
res = courseService.updateCourseSalesInfo(course);
}else{
// 新建操作
// 补全信息
course.setCreate_time(dateFormart); // 创建时间
course.setUpdate_time(dateFormart); // 更新时间
course.setStatus(1); // 上架
// 插入到数据库中
res = courseService.saveCourseSalesInfo(course);
}
// 响应结果
resp.getWriter().println(res);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
// 根据课程id 查询课程信息
public void findCourseById(HttpServletRequest request, HttpServletResponse response){
try {
// 1、接受参数
String id = request.getParameter("id");
// 2、业务处理
CourseDaoImpl courseDao = new CourseDaoImpl();
Course course = courseDao.findCourseById(Integer.parseInt(id));
// 3、返回结果
SimplePropertyPreFilter filter = new SimplePropertyPreFilter(Course.class,
"id", "course_name", "brief", "teacher_name", "teacher_info",
"price", "price_tag", "discounts", "preview_first_field", "preview_second_field",
"course_img_url", "share_title", "share_description", "course_description",
"share_image_title");
String res = JSON.toJSONString(course, filter);
response.getWriter().print(res);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
新知识点
- 这里好像没有新的东西,还是一些模块的东西,但是那个前端怎么接收数据并且同时还能切换到详情页面不知道怎么搞的,等到了写前端的时候再看
8、功能五:修改课程状态
- 名称: updateCourseStatus
- 描述: 修改课程状态
- URL: http://localhost:8080/lagou_edu_home/course
- 请求方式: GET
- 请求参数
字段 | 说明 | 类型 | 是否必需 | 备注 |
---|---|---|---|---|
methodName | 要访问的功能名 | String | 是 | 该字段必须填写,用来确定要访问是 哪一个的方法 |
id | 课程ID | int | 是 |
- 请求参数示例
methodName:"updateCourseStatus",
id:18
- 响应结果
字段 | 说明 | 类型 | 是否必须 | 备注 |
---|---|---|---|---|
status | 修改后的状态 | int | 是 | 课程状态,0-草稿,1-上架 |
- 响应结果示例
草稿
{"status":0}
上架
{"status":1}
CourseDaoImpl
// 修改课程状态
@Override
public int updateCourseStatus(Course course) {
try {
QueryRunner queryRunner = new QueryRunner(DruidUtils.getDataSource());
String sql = "update course set status = ?, update_time = ? where id = ?";
Object[] params = {course.getStatus(), course.getUpdate_time(), course.getId()};
int update = queryRunner.update(sql, params);
return update;
} catch (SQLException e) {
e.printStackTrace();
return 0;
}
}
CourseServiceImpl
// 修改课程状态信息
@Override
public Map<String, Integer> updateCourseStatus(Course course) {
// 调用dao
int row = courseDao.updateCourseStatus(course);
Map<String, Integer> map = new HashMap<>();
if (row > 0){
if (course.getStatus() == 0){
map.put("status", 0);
}else{
map.put("status", 1);
}
}
return map;
}
CourseServlet
// 修改课程状态
public void updateCourseStatus(HttpServletRequest request, HttpServletResponse response){
try {
// 1、接收参数
String id = request.getParameter("id");
Course course = courseService.findCourseById(Integer.parseInt(id));
// 2、处理status
if(course.getStatus() == 1){
course.setStatus(0);
}else{
course.setStatus(1);
}
// 3、修改更新时间
String dateFormart = DateUtils.getDateFormart();
course.setUpdate_time(dateFormart);
// 4、调用修改状态的方法
Map<String, Integer> map = courseService.updateCourseStatus(course);
// 5、响应结果
String res = JSON.toJSONString(map);
response.getWriter().print(res);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
9、功能六:展示课程内容
- 名称: findSectionAndLessonByCourseId
- 描述: 根据课程ID查询章节与课时信息
- URL: http://localhost:8080/lagou_edu_home/courseContent
- 请求方式: GET
- 请求参数
字段 | 说明 | 类型 | 是否必需 | 备注 |
---|---|---|---|---|
methodName | 要访问的功能名 | String | 是 | 该字段必须填写,用来确定要访问是 哪一个的方法 |
course_id | 课程ID | int | 是 | 根据课程ID查询课程相关的, 章节信息和课时信信 |
- 请求参数示例
methodName:"findSectionAndLessonByCourseId",
course_id:"10"
- 响应结果示例
[{
"lessonList": [{
"id": 32,
"course_id": 10,
"section_id": 5,
"theme": "第一讲:如何给自己洗脑",
"duration": 10,
"is_free": 1,
"order_num": 1,
"status": 2,
"create_time": "2019-01-23 20:37:02.0",
"update_time": "2020-02-24 18:37:34.0",
"isDel": 0
},
{
"id": 33,
"course_id": 10,
"section_id": 5,
"theme": "第二讲:如何给别人洗脑",
"duration": 10,
"is_free": 1,
"order_num": 1,
"status": 2,
"create_time": "2019-01-23 20:37:02.0",
"update_time": "2020-02-24 18:37:34.0",
"isDel": 0
}],
"id": 5,
"course_id": 10,
"section_name": "麻式太极",
"description": "麻式太极拳,你动我试试",
"orderNum": 0,
"status": 2,
"create_time": "2019-07-11 10:55:10.0",
"update_time": "2019-10-09 12:43:01.0",
"isDel": 0
}]
CourseContentDaoImpl因为既要查询课程信息又要查询课时信息,需要两个查询语句(是为了避免使用复杂的查询语句)这里我不苟同他直接把查询课时的方法直接在Dao层就写到了查询课程的方法里面
// 根据课程ID查询课程相关信息
@Override
public List<Course_Section> findSectionAndLessionByCourseId(int courseId) {
try {
// 1、创建QueryRunner
QueryRunner queryRunner = new QueryRunner(DruidUtils.getDataSource());
// 2、编写SQL
String sql = "select\n" +
"id,\n" +
"course_id,\n" +
"section_name,\n" +
"description,\n" +
"order_num,\n" +
"status\n" +
"from course_section where course_id = ?";
// 3、执行查询
List<Course_Section> sectionList = queryRunner.query(sql, new BeanListHandler<Course_Section>(Course_Section.class), courseId);
// 4、根据章节ID查询课时信息
for (Course_Section courseSection : sectionList) {
// 调用方法
List<Course_Lesson> lessionBySectionId = findLessionBySectionId(courseSection.getId());
// 将课时数据封装到 章节对象中
courseSection.setCourseLessonList(lessionBySectionId);
}
return sectionList;
} catch (SQLException e) {
e.printStackTrace();
return null;
}
}
// 根据章节ID 查询章节内容
@Override
public List<Course_Lesson> findLessionBySectionId(int sectionId) {
try {
QueryRunner queryRunner = new QueryRunner(DruidUtils.getDataSource());
String sql = "select \n" +
"id,\n" +
"course_id,\n" +
"section_id,\n" +
"theme,\n" +
"duration,\n" +
"is_free,\n" +
"order_num,\n" +
"`status`\n" +
"from course_lesson where section_id = ?";
List<Course_Lesson> courseLessonList = queryRunner.query(sql, new BeanListHandler<Course_Lesson>(Course_Lesson.class), sectionId);
return courseLessonList;
} catch (SQLException e) {
e.printStackTrace();
return null;
}
}
CourseContentServiceImpl
/*
* 课程内容管理Service的实现类
* */
public class CourseContentServiceImpl implements CourseContentService {
private CourseContentDao courseContentDao = new CourseContentDaoImpl();
@Override
public List<Course_Section> findSectionAndLessionByCourseId(int courseId) {
return courseContentDao.findSectionAndLessionByCourseId(courseId);
}
}
CourseContentServlet
private CourseContentService courseContentService = new CourseContentServiceImpl();
// 展示课程的章节和课时信息
public void findSectionAndLessonByCourseId(HttpServletRequest request, HttpServletResponse response){
try {
// 1、接受参数
String courseId = request.getParameter("course_id");
// 2、调用业务方法
List<Course_Section> list = courseContentService.findSectionAndLessionByCourseId(Integer.parseInt(courseId));
// 3、响应结果
String res = JSON.toJSONString(list);
response.getWriter().print(res);
} catch (IOException e) {
e.printStackTrace();
}
}
10、功能七:新建章节
使用两个接口来完成这个
- 名称: findCourseById
- 描述: 回显章节对应的课程信息
- URL: http://localhost:8080/lagou_edu_home/courseContent
- 请求方式: GET
- 请求参数
字段 | 说明 | 类型 | 是否必需 | 备注 |
---|---|---|---|---|
methodName | 要访问的功能名 | String | 是 | 该字段必须填写,用来确定要访问是 哪一个的方法 |
course_id | 课程ID | int | 是 | 根据课程ID查询课程相关的, 章节信息和课时信信 |
- 请求参数示例
methodName: "findCourseById",
course_id: id
- 响应结果
字段 | 说明 | 类型 | 是否必需 | 备注 |
---|---|---|---|---|
id | 课程id | id | 是 | |
course_name | 课程名称 | String | 是 |
- 响应结果示例
{
"id":10,
"course_name":"麻式太极"
}
- 名称: saveOrUpdateSection
- 描述: 保存和修改章节信息
- URL: http://localhost:8080/lagou_edu_home/courseContent
- 请求方式: POST
- 请求参数
字段 | 说明 | 类型 | 是否必需 | 备注 |
---|---|---|---|---|
methodName | 要访问的功能名 | String | 是 | 该字段必须填写,用来确定要访问是 哪一个的方法 |
id | 章节ID | int | 否 | 添加操作不携带id, 修改操作必须携带ID |
course_id | 课程ID | int | 是 | |
section_name | 章节名称 | String | 是 | |
description | 章节描述 | String | 是 | |
order_num | 章节排序 | int | 是 |
- 请求参数示例
JSON 格式数据
{
"methodName":"saveOrUpdateSection",
"course_id":19,
"section_name":"微服务架构",
"description":"跟着药水一起学习如何使用微服务",
"order_num ":0
}
- 响应结果
字段 | 说明 | 类型 | 是否必须 | 备注 |
---|---|---|---|---|
status | 表示执行成功或失败 | String | 是 | 0 表示成功, 1 表示失败 |
msg | 响应消息 | String | 是 |
- 响应结果示例
成功
{"msg":"success","status":0}
失败
{"msg":"fail","status":1}
格式 | 说明 |
---|---|
Content-Type : application/x-www-formurlencoded | 请求体中的数据会以普通表单形式键值对)发送到后端。 |
Content-Type : application/json ; charset=utf-8 | 请求体中的数据会以json字符串的形式发送到后端。 |
Content-Type : multipart/form-data | 多部件上传既可以上传键值对 也可以上传文件。 |
这里我们会发现第二个接口的请求参数是一个JSON格式的数据,所以我们这里要修改一下BaseServlet通用类
思路
- 获取Post请求的Content-Type类型
- 然后判断Content-Type,如果为application/json;charset=utf-8就是JSON格式的
通过获取request中的输入流对象,将json格式中的数据读取出来,转换成String类型 - 然后通过FastJSON中的pareseObject将json格式转换为map类型的
- 取出其中的methodName供反射使用,并将map保存到request中供后续使用
public class BaseServlet extends HttpServlet {
/*
*
* dogGET方法作为一个调用器,根据请求功能的不同,调用对应的方法
* 规定必须传递一个参数 methodName=功能名
* */
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1、获取参数 要访问的方法名
//String methodName = req.getParameter("methodName");
String methodName = null;
// 1.1、获取POST请求的Content-Type类型
String contentType = req.getHeader("Content-Type");
// 1.2、判断传递的数据是不是Json格式
if("application/json;charset=utf-8".equals(contentType)){
// 是JSON格式,调用getPostJSON方法
String postJSON = getPostJSON(req);
// 将JSON格式的字符串转换为map
Map<String, Object> map = JSON.parseObject(postJSON, Map.class);
// 从map集合中获取到methodName
methodName = (String)map.get("methodName");
// 将获取到的数据,保存到request中
req.setAttribute("map", map);
}else{
methodName = req.getParameter("methodName");
}
// 2、判断执行对应的方法 通过反射来进行优化
if(methodName != null){
// 通过反射优化代码,提升代码的可维护性
// 1、获取字节码文件对象 this = TestServlet
try {
Class c = this.getClass();
// 2、根据传入的方法名,获取对应的方法对象
Method method = c.getMethod(methodName, HttpServletRequest.class, HttpServletResponse.class);
// 3、调用method对象的invoke方法,执行对应的功能
method.invoke(this, req, resp);
} catch (Exception e) {
e.printStackTrace();
System.out.println("请求的功能不存在");
}
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
/*
* post请求格式为: application/json;charset=utf-8
* 使用该方法进行读取
* */
public String getPostJSON(HttpServletRequest request){
try {
// 1、从request中 获取缓存输入流对象
BufferedReader reader = request.getReader();
// 2、创建StringBuffer 保存读取出的数据
StringBuffer stringBuffer = new StringBuffer();
// 3、循环读取
String line = null;
while((line = reader.readLine()) != null){
// 将每次读取的数据追加到stringBufffer中
stringBuffer.append(line);
}
// 4、返回结果
return stringBuffer.toString();
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}
CourseContentDaoImpl
// 根据课程Id查询课程信息
@Override
public Course findCourseByCourseId(int courseId) {
try {
QueryRunner queryRunner = new QueryRunner(DruidUtils.getDataSource());
String sql = "select id, course_name from course where id = ?";
Course course = queryRunner.query(sql, new BeanHandler<Course>(Course.class), courseId);
return course;
} catch (SQLException e) {
e.printStackTrace();
return null;
}
}
// 保存章节信息
@Override
public int saveSection(Course_Section section) {
try {
QueryRunner queryRunner = new QueryRunner(DruidUtils.getDataSource());
String sql = "insert into course_section(\n" +
"course_id,\n" +
"section_name,\n" +
"description,\n" +
"order_num,\n" +
"`status`,\n" +
"create_time,\n" +
"update_time\n" +
") values(?, ?, ?, ?, ?, ?, ?)";
Object[] params = {
section.getCourse_id(),
section.getSection_name(),
section.getDescription(),
section.getOrder_num(),
section.getStatus(),
section.getCreate_time(),
section.getUpdate_time()
};
int row = queryRunner.update(sql, params);
return row;
} catch (SQLException e) {
e.printStackTrace();
return 0;
}
}
CourseContentServiceImpl
@Override
public Course findCourseByCourseId(int courseId) {
return courseContentDao.findCourseByCourseId(courseId);
}
@Override
public String saveSection(Course_Section course_section) {
// 补全信息
String dateFormart = DateUtils.getDateFormart();
course_section.setCreate_time(dateFormart);
course_section.setUpdate_time(dateFormart);
course_section.setStatus(2);
// 调用业务
String res = "";
int row = courseContentDao.saveSection(course_section);
if(row > 0){
res = StatusCode.SUCCESS.toString();
}else{
res = StatusCode.FAIL.toString();
}
return res;
}
CourseContentServlet
// 新建章节
// 查询信息
public void findCourseById(HttpServletRequest request, HttpServletResponse response){
try {
// 1、接收参数
String courseId = request.getParameter("course_id");
// 2、调用业务
Course course = courseContentService.findCourseByCourseId(Integer.parseInt(courseId));
// 3、响应结果
// JSONObject jsonObject = new JSONObject();
// jsonObject.put("id", course.getId());
// jsonObject.put("course_name", course.getCourse_name());
SimplePropertyPreFilter filter = new SimplePropertyPreFilter(Course.class, "id", "course_name");
String res = JSON.toJSONString(course, filter);
response.getWriter().print(res);
} catch (IOException e) {
e.printStackTrace();
}
}
// 保存或者修改章程信息
public void saveOrUpdateSection(HttpServletRequest request, HttpServletResponse response){
try {
// 1、获取参数
Map<String, Object> map = (Map)request.getAttribute("map");
// 2、创建一个Course_Section
Course_Section courseSection = new Course_Section();
// 3、使用BeanUtils工具类,将map转化成Course_Section
BeanUtils.populate(courseSection, map);
// 4、调用业务
String res = courseContentService.saveSection(courseSection);
// 5、响应结果
response.getWriter().print(res);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
新知识
- 了解了post请求有不同的类型
- 又熟悉了一下fastjson将json转化为map,然后再通过BeanUtils将map转换为java对象,这应该就是常用的手段,要留个心眼
- 见到了可以获取request的流数据,用StringBuffer可以读取出来数据
- 最主要的是服务端如何去处理前端请求参数为json格式的
11、功能八:修改章节状态
- 名称: updateSectionStatus
- 描述: 修改章节状态
- URL: http://localhost:8080/lagou_edu_home/courseContent
- 请求方式: GET
- 请求参数
字段 | 说明 | 类型 | 是否必需 | 备注 |
---|---|---|---|---|
methodName | 要访问的功能名 | String | 是 | 该字段必须填写,用来确定要访问是 哪一个的方法 |
id | 章节ID | int | 是 | |
status | 章节状态 | int | 是 | 状态,0:隐藏;1:待更新;2:已发布 |
- 请求参数示例
"methodName":"updateSectionStatus",
"id":2,
"status":1
- 响应结果
字段 | 说明 | 类型 | 是否必须 | 备注 |
---|---|---|---|---|
status | 表示执行成功或失败 | String | 是 | 0 表示成功, 1 表示失败 |
msg | 响应消息 | String | 是 |
CourseContentDaoImpl
@Override
public int updateSectionStatus(int id, int status) {
try {
QueryRunner queryRunner = new QueryRunner(DruidUtils.getDataSource());
String sql = "update course_section set\n" +
"status = ?, \n" +
"update_time = ? where id = ?";
Object[] params = {
status,
DateUtils.getDateFormart(),
id
};
int row = queryRunner.update(sql, params);
return row;
} catch (SQLException e) {
e.printStackTrace();
return 0;
}
}
CourseContentServiceImpl
@Override
public String updateSectionStatus(int id, int status) {
int row = courseContentDao.updateSectionStatus(id, status);
String res = "";
if(row > 0){
res = StatusCode.SUCCESS.toString();
}else{
res = StatusCode.FAIL.toString();
}
return res;
}
CourseContentServlet
// 修改章节状态
public void updateSectionStatus(HttpServletRequest request, HttpServletResponse response){
try {
// 1、获取参数
String id = request.getParameter("id");
String status = request.getParameter("status");
// 2、业务
String res = courseContentService.updateSectionStatus(Integer.parseInt(id), Integer.parseInt(status));
response.getWriter().print(res);
} catch (IOException e) {
e.printStackTrace();
}
}