开发流程
需求分析
跟项目经理交流和分析需求文档。
课程信息相关的展示,查询,以及操作。
数据库表分析
数据库表和需求文档进行匹配,如分析哪些字段需要被使用。
需要使用数据库的课程信息表 - course。
实体类设计
根据数据库中的 Course 表对应创建 Course.java
代码语言:javascript复制@Data
public class Course implements Serializable {
// 使用 @JSONField(ordinal = int 类型的值) 指定排序的值,生成 JSON 时会按照指定顺序进行排序
// 使用 @JSONField(serialize = false) 排除不需要转换的字段
// 另外 fastjson 还会自动排除为空的字段
// 课程 ID
@JSONField(ordinal = )
private int id;
//课程名称
@JSONField(ordinal = )
private String course_name;
//课程介绍
@JSONField(ordinal = )
private String brief;
//讲师名称
@JSONField(ordinal = )
private String teacher_name;
//讲师介绍
@JSONField(ordinal = )
private String teacher_info;
//课程原价
@JSONField(ordinal = )
private double price;
//原价标签
@JSONField(ordinal = )
private String price_tag;
//课程优惠价
@JSONField(ordinal = )
private double discounts;
//课程概述
@JSONField(ordinal = )
private String preview_first_field;
//课程概述第二个字段
@JSONField(ordinal = )
private String preview_second_field;
//分享图片 url
@JSONField(ordinal = )
private String course_img_url;
//分享标题
@JSONField(ordinal = )
private String share_title;
//分享描述
@JSONField(ordinal = )
private String share_description;
//课程描述
@JSONField(ordinal = )
private String course_description;
//排序
@JSONField(ordinal = )
private int sort_num;
//课程状态,0 - 草稿,1 -上架
@JSONField(ordinal = )
private int status;
//创建时间
@JSONField(ordinal = )
private String create_time;
//修改时间
@JSONField(ordinal = )
private String update_time;
//是否删除
@JSONField(ordinal = )
private int isDel;
@JSONField(ordinal = )
private String share_image_title; //分享图 title
@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; //级别
}
Dao 接口及实现类编写
代码语言:javascript复制/**
* 课程模块 DAO 层接口
* */
public interface CourseDao {
...
}
/**
* 课程模块 DAO 层实现类
* */
public class CourseDaoImpl implements CourseDao {
...
}
Service 接口及实现类编写
代码语言:javascript复制/**
* 课程模块 Service 层 接口
* */
public interface CourseService {
...
}
/**
* 课程模块 Service 层 实现类
* */
public class CourseServiceImpl implements CourseService {
...
}
`CourseServlet` 编写
CourseServlet
继承通用的 BaseServlet
@WebServlet(name="courseServlet", value="/course")
public class CourseServlet extends BaseServlet {
...
}
功能一:查询课程列表信息
需求分析
分析需求文档的对应页面,查看需要展示课程列表的哪些数据。
编写代码
Dao 层
修改 CourseDao
,添加 findCourseList
方法。
/**
* 查询课程列表信息
*/
public List<Course> findCourseList();
实现类 CourseDaoImpl
。
/**
* 查询课程列表信息
*/
@Override
public List<Course> findCourseList() {
try {
// 创建 Query Runner
QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());
// 编写 SQL:判断是否删除,取出 is_del = 0 的数据,即未删除的数据
String sql = "SELECT `id`, `course_name`, `price`, `sort_num`, `status` FROM `course` WHERE is_del = ?";
// 执行查询
return qr.query(sql, new BeanListHandler<Course>(Course.class), );
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return null;
}
补充说明
逻辑删除:逻辑删除的本质是修改操作,所谓的逻辑删除其实并不是真正的删除,而是在表中将对应的是否删除标识做修改操作;比如 0 是未删除,1 是删除;在逻辑上数据是被删除的,但数据本身依然存在库中。
物理删除:物理删除就是真正的从数据库中做删除操作了。
Service 层
修改 CourseService
添加 findCourseList
方法。
/**
* 查询课程列表
*/
public List<Course> findCourseList();
实现类 CourseServiceImpl
/**
* 创建 CourseDao
*/
CourseDao courseDao = new CourseDaoImpl();
/**
* 查询课程列表
*/
@Override
public List<Course> findCourseList() {
return courseDao.findCourseList();
}
接口开发说明
接口文档:
前端的开发基于服务端编写的接口,如果前端人员等待服务端人员将接口开发完毕再去开发前端内容这样做效率是 非常低下的;所以当接口定义完成,可以使用工具生成接口文档,前端人员查看接口文档即可进行前端开发;这样 前端和服务人员并行开发,大大提高了生产效率。
开发规范:
- Get 请求时,采用 key / value 格式请求,Servlet中可以使用
getParameter()
获取。 - Post 请求时有三种数据格式:JSON 数据,JSON 类型的数据 Servlet 中使用
fastJson
进行解析;提交 form 表单数据;文件等多部件类型 multipart / form-data。 - 响应结果统一格式为 JSON。因为 JSON 数据格式比较简单,易于读写;JSON 格式能够直接为服务器端代码使用,大大简化了服务器端和客户端的代码开发量,但是完成的任务不变,易于维护。
编写 `CourseServlet`
在 CourseServlet
中添加 findCourseList
方法。
@WebServlet("/course")
public class CourseServlet extends BaseServlet {
/**
* 查询课程信息列表
*/
public void findCourseList(HttpServletRequest req, HttpServletResponse resp) {
try {
// 业务处理
CourseService courseService = new CourseServiceImpl();
List<Course> courseList = courseService.findCourseList();
// 响应结果
// FastJson 的 SimplePropertyPreFilter 指定要转换的 JSON 字段
SimplePropertyPreFilter filter = new SimplePropertyPreFilter(Course.class,
"id", "course_name", "price", "sort_num", "status");
String result = JSON.toJSONString(courseList, filter);
resp.getWriter().print(result);
} catch (IOException e) {
e.printStackTrace();
}
}
}
Postman
Postman 介绍
Postman 是一款功能强大的 http 接口测试工具,使用 postman 可以完成 http 各种请求的功能测试。
官方地址:https://www.getpostman.com/
Postman 使用
使用 postman 创建请求来测试接口;创建课程模块,将请求保存到对应模块中。
功能二:多条件查询课程信息
需求分析
根据课程名称和课程状态进行查询。
要查询的字段:id, course_name, price, sort_num, STATUS
查询条件:is_del, course_name, statuts
编写代码
Dao 层
因为是多条件查询,所以要注意多个参数情况下 SQL 的编写
CourseDao
public List<Course> findByCourseNameAndStatus(String courseName, String status);
CourseDaoImpl
/**
* 根据课程名和课程状态条件查询课程信息
*/
@Override
public List<Course> findByCourseNameAndStatus(String courseName, String status) {
try {
// 创建 QueryRunner
QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());
// 编写 SQL:当前的查询为多条件不定项查询
// 创建 StringBuffer 对象,将 SQL 字符串添加进缓冲区
StringBuffer stringBuffer = new StringBuffer("SELECT `id`, `course_name`, `price`, `sort_num`, `status` FROM `course` WHERE 1 = 1 AND `is_del` = ?");
// 创建 list 集合保存参数
List<Object> list = new ArrayList<>();
list.add();
// 判断传入的参数是否为空
if (courseName != null && !"".equals(courseName)) {
stringBuffer.append(" AND course_name LIKE ?");
// 将条件放进 list 集合
list.add("%" courseName "%");
}
if (status != null && !"".equals(status)) {
stringBuffer.append(" AND `status` = ?");
// 将 status 转换为字符串
int i = Integer.parseInt(status);
list.add(i);
}
// 执行查询
return qr.query(stringBuffer.toString(), new BeanListHandler<>(Course.class), list.toArray());
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return null;
}
Service 层
CourseService
List<Course> findByCourseNameAndStatus(String courseName, String status);
CourseServiceImpl
/**
* 根据课程名和课程状态条件查询课程信息
*/
@Override
public List<Course> findByCourseNameAndStatus(String courseName, String status) {
return courseDao.findByCourseNameAndStatus(courseName, status);
}
Servlet 编写
在 CourseServlet
中添加 findByCourseNameOrStatus
方法
/**
* 根据课程名和课程状态条件查询课程信息
*/
public void findByCourseNameAndStatus(HttpServletRequest req, HttpServletResponse resp) {
try {
// 接受参数
String course_name = req.getParameter("course_name");
String status = req.getParameter("status");
// 调用业务层进行业务处理
CourseService courseService = new CourseServiceImpl();
List<Course> courseList = courseService.findByCourseNameAndStatus(course_name, status);
// 返回结果
SimplePropertyPreFilter filter = new SimplePropertyPreFilter(Course.class,
"id", "course_name", "price", "sort_num", "status");
String result = JSON.toJSONString(courseList, filter);
resp.getWriter().print(result);
} catch (IOException e) {
e.printStackTrace();
}
}
接口测试
根据接口文档使用 postman 进行接口测试
功能三:新建课程营销信息
需求分析
选择新建课程,对课程营销信息进行录入。
编写 Dao 层
CourseDaoImpl
/**
* 保存课程营销信息
*
* @param course
*/
@Override
public int saveCourseSalesInfo(Course course) {
try {
// 创建 QueryRunner
QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());
// 编写 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[] 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.getStatus(), course.getCreate_time(), course.getUpdate_time()};
// 执行插入操作
return qr.update(sql, params);
} catch (SQLException throwable) {
throwable.printStackTrace();
}
return ;
}
Dao 层方法测试
TestCourseDao
/**
* 测试保存课程营销信息
*/
@Test
public void testSaveCourseSalesInfo() {
// 创建 Course 对象
Course course = new Course();
course.setCourse_name("数学入门");
course.setBrief("基础数学");
course.setTeacher_name("李老师");
course.setTeacher_info("数学老师");
course.setPreview_first_field("共10讲");
course.setPreview_second_field("每周日更新");
course.setDiscounts(88.88);
course.setPrice(188.0);
course.setPrice_tag("最新优惠价");
course.setShare_image_title("图片标题1");
course.setShare_title("标题1");
course.setShare_description("描述1");
course.setCourse_description("课程描述1");
course.setCourse_img_url("https://img.yuanmabao.com/zijie/pic/2020/09/08/1xr5g4e0fwb");
course.setStatus(); // 1 上架,0 下架
String format = DateUtils.getDateFormat();
course.setCreate_time(format);
course.setUpdate_time(format);
int i = courseDao.saveCourseSalesInfo(course);
System.out.println(i);
}
Service 层
编写枚举类,设置响应状态码
代码语言:javascript复制public enum StatusCode {
/**
* 执行成功
*/
SUCCESS(, "success"),
/**
* 执行失败
*/
FAIL(, "fail");
//定义属性
private int code;
private String message;
StatusCode() {
}
StatusCode(int code, String message) {
this.code = code;
this.message = message;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
/**
* 将枚举对象转换为 JSON
*/
@Override
public String toString() {
JSONObject object = new JSONObject();
object.put("status", code);
object.put("msg", message);
return object.toString();
}
}
CourseServiceImpl
/**
* 保存课程营销信息
*/
@Override
public String saveCourseSalesInfo(Course course) {
// 补全课程营销信息
DateUtils.getDateFormat();
String dateFormat = DateUtils.getDateFormat();
course.setCreate_time(dateFormat);
course.setUpdate_time(dateFormat);
course.setStatus();
// 执行插入操作
int row = courseDao.saveCourseSalesInfo(course);
return row > ? StatusCode.SUCCESS.toString() : StatusCode.FAIL.toString();
}
文件上传
图片上传分析
在添加课程营销信息的表单中,有一个图片上传项
文件上传介绍
文件上传的实质:文件的拷贝
文件上传:从本地将文件拷贝到服务器磁盘上
- 客户端:需要编写文件上传表单
- 服务端:需要编写代码接受上传的文件
客户端编码
默认情况下,表单的 enctype
的值是 application/x-www-form-urlencoded
,不能用于文件上传,只有使用 multipart/form-data
,才能完整的传递文件数据。
fileUpload.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>File Upload</title>
</head>
<body>
<%--
文件上传三要素:
1. 表单提交方式必须是 post;
2. 表单的 enctype 必须为 multipart/form-data
3. 表单中必须有文件上传项
--%>
<form method="post" enctype="multipart/form-data" action="#">
<input type="file" name="upload">
<br>
<input type="text" name="name">
<input type="text" name="password">
<input type="submit" value="文件上传">
</form>
</body>
</html>
服务端编码
服务端要接收文件上传的表单数据,获取上传文件所需步骤:
- 通过 request 获取请求体的内容。
- 解析请求体多部件上传的特点是,每个 input 都是一个表单项;根据分隔符将请求中所有的内容,切割成数组,数组中的每一个元素都是一个表单项。
- 遍历数组,分清楚哪个是普通的表单项,哪个是文件上传项;如何区分?判断是否有 filename?
- 获取到普通表单项中的内容,通过属性 name 获取。
- 获取文件上传项内容;文件名: filename = aaa.txt。
- 使用 IO 将文件内容保存到服务器中。
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>
FileUpload
核心类介绍:
DiskFileItemFactory
磁盘文件项工厂,读取文件时相关的配置,比如:缓存的大小 ,临时目录的位置ServletFileUplaod
文件上传的一个核心类FileItem
代表每一个表单项
ServletFileUpload
API:
isMultipartContent(request);
判断是否是一个文件上传的表单parseRequest(request);
解析 request 获得表单项的集合setHeaderEncoding("UTF-8");
设置上传的文件名的编码方式
FileItem
API:
isFormField()
判断是否是普通表单项getFieldName()
获得表单的 name 属性值item.getString()
获得表单的 value 值getName()
获得上传文件的名称getInputStream()
获得上传文件delete()
删除临时文件
FileUpload
使用步骤:
- 创建磁盘文件项工厂
- 创建文件上传的核心类
- 解析 request -- 获得文件项集合
- 遍历文件项集合
- 判断普通表单项 / 文件上传项
文件上传后台代码编写:
代码语言:javascript复制@WebServlet("/upload")
public class FileUploadServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
// 1 - 创建磁盘文件项工厂
DiskFileItemFactory factory = new DiskFileItemFactory();
// 2 - 创建文件上传核心类
ServletFileUpload upload = new ServletFileUpload(factory);
// 设置上传文件编码
upload.setHeaderEncoding("utf-8");
// 判断表单是否文件上传表单
boolean multipartContent = ServletFileUpload.isMultipartContent(req);
// 是文件上传表单
if (multipartContent) {
// 3 - 解析 request,获取文件项集合
List<FileItem> list = upload.parseRequest(req);
if (list != null) {
// 4 - 遍历获取表单项
for (FileItem item : list) {
// 5 - 判断是不是一个普通表单项
if (item.isFormField()) {
// 普通表单项
String fieldName = item.getFieldName();
String fieldValue = item.getString("utf-8");
System.out.println(fieldName " = " fieldValue);
} else {
// 文件上传项
String fileName = item.getName();
// 拼接新文件名,使用 UUID 保证不重复
String newFileName = UUIDUtils.getUuid() "_" fileName;
// 获取输入流
InputStream in = item.getInputStream();
// 创建输出流
FileOutputStream out = new FileOutputStream("E:/upload/" newFileName);
System.out.println();
// 使用 IOUtils 完成文件的复制
IOUtils.copy(in, out);
// 关闭流
out.close();
in.close();
}
}
}
}
} catch (FileUploadException e) {
e.printStackTrace();
}
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
}
将图片上传到 tomcat 服务器
1、将部署方式改变为 war 模式,把项目部署在 tomcat 的 webapps
目录下。
IDEA 中部署项目两种方式:
- war 模式 - 将项目以 war 包的形式上传真实到服务器的
webapps
目录中 - war exploded 模式 - 仅仅是目录的映射,就相当于 tomcat 在项目源文件夹中启动一样
2、在 webapps
中创建 upload 目录;upload 目录专门用来保存上传过来的图片。
3、修改代码,将图片上传到服务器。
修改图片的输出路径:
- 获取到项目的运行目录信息
- 截取到
webapps
的目录路径 - 拼接输出路径,将图片保存到 upload 目录
FileUploadServlet
...
// 创建输出流
// 获取项目的运行目录
String realPath = this.getServletContext().getRealPath("/");
// 截取 webapps 目录路径
String webAppPath = realPath.substring(, realPath.indexOf("lagou_edu_home"));
// 拼接输出路径,将图片保存到 upload
FileOutputStream out = new FileOutputStream(webAppPath "/upload/" newFileName);
...
4、页面加载图片
将 tomcat 作为图片服务器使用时,存储上传的图片后,如果想要图片可以被访问,需要在 idea 中进行配置:
- 编辑 tomcat --> Deployment --> 选择 Add --> External Source --> 找到
webapps
目录下的的 upload 文件夹 - 上传一张图片到服务器
- 在项目内部页面加载图片:
<img src="/upload/image.jpg">
- 也可以通过HTTP方式访问:
http://localhost:8080/upload/image.jpg
`BeanUtils` 工具类
BeanUtils
是 Apache commons 组件的成员之一,主要用于简化 JavaBean 封装数据的操作。可以将一个表单提交的所有数据封装到 JavaBean 中。
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.8.3</version>
</dependency>
BeanUtils
对象常用方法:
populate(Object bean, Map properties)
将 Map 数据封装到指定 JavaBean 中,一般用于将表单的所有数据封装到 JavaBeansetProperty(Object obj,String name,Object value)
设置属性值getProperty(Object obj,String name)
获得属性值
BeanUtils
使用测试:
public class TestBeanUtils {
@Test
public void test01() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
// 创建 Course 对象
Course course = new Course();
// 创建 Map
Map<String, Object> map = new HashMap<>();
/* *
* 向 Map 集合中添加数据,
* key 要与 course 的属性名保持一致,
* value 要与 course 的属性的类型保持一致
* */
map.put("id",);
map.put("course_name","大数据");
map.put("brief","课程包含所有大数据流行的技术");
map.put("teacher_name","张人大");
map.put("teacher_info","老师信息");
System.out.println(map);
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);
String price = BeanUtils.getProperty(course, "price");
System.out.println(price);
}
}
Servlet 编写
创建 CourseSalesInfoServlet
类,继承 HttpServlet
,完成保存课程营销信息操作。因为上传的信息包含文件信息,无法直接通过 request 直接获取参数,所以不能继承 BaseServlet
。
CourseSalesInfoServlet
@WebServlet("/courseSalesInfo")
public class CourseSalesInfoServlet extends HttpServlet {
/**
* 保存课程营销信息
* 收集表单数据,封装到 course 对象中,将图片上传到 tomcat 服务器中
*/
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
// 创建 Course 对象
Course course = new Course();
// 创建 Map 集合,用来收集数据
Map<String, Object> map = new HashMap<>();
// 创建磁盘工厂对象
DiskFileItemFactory factory = new DiskFileItemFactory();
// 文件上传核心对象
ServletFileUpload fileUpload = new ServletFileUpload(factory);
// 解析 request 对象,获取表单项集合
List<FileItem> list = fileUpload.parseRequest(req);
// 遍历集合 判断哪些是普通的表单项,那些是文件表单项
for (FileItem fileItem : list) {
if (fileItem.isFormField()) {
// 是普通表单项,获取表单项中的数据,保存到 map
String fieldName = fileItem.getFieldName();
String value = fileItem.getString("UTF-8");
System.out.println(fieldName " : " value);
// 使用 map 收集数据
map.put(fieldName, value);
} else {
// 文件上传项
// 获取文件名
String fileName = fileItem.getName();
String newFileName = UUIDUtils.getUuid() "_" fileName;
// 获取输入流
InputStream in = fileItem.getInputStream();
// 获取 webapps 的目录路径
String realPath = this.getServletContext().getRealPath("/");
String webappsPath = realPath.substring(, realPath.indexOf("lagou_edu_home"));
// 创建输出流
OutputStream out = new FileOutputStream(webappsPath "/upload/" newFileName);
IOUtils.copy(in, out);
out.close();
in.close();
// 将图片路径进行保存
map.put("course_img_url", Constants.LOCAL_URL "/upload/" newFileName);
}
}
// 使用 BeanUtils 将 map 中的数据封装到 course 对象
BeanUtils.populate(course, map);
// 补全信息
String dateFormat = DateUtils.getDateFormat();
course.setCreate_time(dateFormat);
course.setUpdate_time(dateFormat);
// 1 代表上架,0 代表下架
course.setStatus();
// 业务处理
CourseService cs = new CourseServiceImpl();
String result = cs.saveCourseSalesInfo(course);
// 响应结果
resp.getWriter().print(result);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
}
接口测试
Postman 测试上传文件
- 接口地址填写正确
- 将请求方式设置为 POST
- 需要上传文件,设置 Headers:
"key":"Content-Type"
,"value":"multipart/form-data"
- Body 选择 form-data
- key 右侧下拉选择 file,value 点击 Select Files 选择文件, 按照接口文档,补全测试参数
保存图片 URL 优化
创建常量类
代码语言:javascript复制public class Constants {
/**
* 本地的访问地址
*/
public static final String LOCAL_URL = "http://localhost:8080";
}
CourseSalesInfoServlet
...
// 将图片路径进行保存
map.put("course_img_url", Constants.LOCAL_URL "/upload/" newFileName);
...
功能四:修改课程营销信息
需求分析
营销信息其实就是课程相关的信息,操作的依然是 course 表,通过点击营销信息按钮进入到对应的课程营销信息页面,对原有信息进行修改。
Dao 层
通过分析,首先要编写根据课程 ID 查询课程信息进行回显;然后编写修改课程营销信息的方法,将修改写入数据库
CourseDaoImpl
/**
* 根据课程 ID 查询课程信息
*/
@Override
public Course findCourseById(int id) {
try {
QueryRunner queryRunner = new QueryRunner(DruidUtils.getDataSource());
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_image_title,n"
"share_title,n"
"share_description,n"
"course_description,n"
"`status`n"
"FROM course WHERE id = ?";
return queryRunner.query(sql, new BeanHandler<Course>(Course.class), id);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return null;
}
/**
* 修改课程营销信息
*/
@Override
public int updateCourseSalesInfo(Course course) {
try {
QueryRunner qr = 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()};
return qr.update(sql, params);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return ;
}
Dao 层测试
TestCourseDao
/**
* 测试修改课程信息操作
*/
@Test
public void testUpdateCourse() {
// 根据 id 查询课程信息,模拟回显操作
Course course = courseDao.findCourseById();
System.out.println(course);
// 修改课程营销信息
course.setCourse_name("新课程名");
course.setTeacher_name("张人大");
course.setDiscounts(88.8);
System.out.println(courseDao.updateCourseSalesInfo(course));
}
Service 层编写
CourseServiceImpl
/**
* 根据课程 ID 查询课程信息
*/
@Override
public Course findCourseById(int id) {
return courseDao.findCourseById(id);
}
/**
* 修改课程营销信息
*/
@Override
public String updateCourseSalesInfo(Course course) {
// 调用 dao
if (courseDao.updateCourseSalesInfo(course) > ) {
// 保存成功
return StatusCode.SUCCESS.toString();
} else {
// 保存失败
return StatusCode.FAIL.toString();
}
}
Servlet 编写
根据 ID 查询课程信息
在 CourseServlet
中添加根据 ID 查询课程信息的功能
...
public void findCourseById(HttpServletRequest req, HttpServletResponse resp) {
try {
// 接收参数
String id = req.getParameter("id");
// 业务处理
CourseService cs = new CourseServiceImpl();
Course course = cs.findCourseById(Integer.parseInt(id));
// 返回结果 响应 JSON 格式数据
// 使用 SimplePropertyPreFilter 指定要转换为 JSON 的字段
SimplePropertyPreFilter filter = new SimplePropertyPreFilter(Course.class,
"id","course_name","brief","teacher_name",
"teacher_info","preview_first_field","preview_second_field",
"discounts","price","price_tag","share_image_title","share_title",
"share_description","course_description");
String result = JSON.toJSONString(course, filter);
resp.getWriter().println(result);
} catch (IOException e) {
e.printStackTrace();
}
}
...
接口测试:根据接口文档使用 Postman 进行测试
修改 `CourseSalesInfoServlet`
保存营销信息和修改营销信息,访问的是同一个接口,所以在 CourseSalesInfoServlet
中需要进行一下判断:携带 id 就是修改操作;未携带 id 就是新增操作。
...
// 使用 BeanUtils 将 map 中的数据封装到 Course 对象
BeanUtils.populate(course, map);
String dateFormat = DateUtils.getDateFormat();
CourseService cs = new CourseServiceImpl();
String result = "";
// 判断 id 是否为空
if (map.get("id") != null) {
// 修改操作
// 补全信息
course.setUpdate_time(dateFormat);
// 业务处理
result = cs.updateCourseSalesInfo(course);
} else {
// 新建操作
// 补全信息
course.setCreate_time(dateFormat);
course.setUpdate_time(dateFormat);
// 1 代表上架,0 代表下架
course.setStatus();
// 业务处理
result = cs.saveCourseSalesInfo(course);
}
// 响应结果
resp.getWriter().print(result);
...
接口测试:根据接口文档使用 Postman 进行测试
功能五:修改课程状态
需求分析
数据库中课程状态码为 0 或者 1,课程状态:0 - 下架,1 - 上架
Dao 层编写
CourseDaoImpl
...
/**
* 修改课程状态
*/
@Override
public int updateCourseStatus(Course course) {
try {
QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());
String sql = "UPDATE course SET STATUS = ?, update_time = ? WHERE id = ?";
Object[] param = {course.getStatus(),course.getUpdate_time(),course.getId()};
return qr.update(sql, param);
} catch (SQLException e) {
e.printStackTrace();
}
return ;
}
...
Service 层编写
CourseServiceImpl
...
/**
* 修改课程状态
*/
@Override
public Map<String, Integer> updateCourseStatus(Course course) {
Map<String ,Integer> map = new HashMap<>();
// 调用 dao
if(courseDao.updateCourseStatus(course) > ){
if(course.getStatus() == ){
map.put("status", );
}else{
map.put("status", );
}
}
return map;
}
...
Servlet 编写
在 CourseServlet
中,添加 updateCourseStatus
方法
...
/**
* 修改课程状态
*/
public void updateCourseStatus(HttpServletRequest req, HttpServletResponse resp){
try {
// 获取参数
String id = req.getParameter("id");
// 业务处理
CourseService cs = new CourseServiceImpl();
// 根据课程 id 查询课程信息
Course course = cs.findCourseById(Integer.parseInt(id));
// 判断课程信息状态,进行取反设置
course.setStatus(course.getStatus() == ? : );
// 设置更新时间
course.setUpdate_time(DateUtils.getDateFormat());
// 获取修改后的课程状态
Map<String, Integer> map = cs.updateCourseStatus(course);
// 响应结果
resp.getWriter().print(JSON.toJSONString(map));
} catch (IOException e) {
e.printStackTrace();
}
}
...
接口测试:根据接口文档使用 Postman 进行测试