
开发流程
跟项目经理交流和分析需求文档。
课程信息相关的展示,查询,以及操作。
数据库表和需求文档进行匹配,如分析哪些字段需要被使用。
需要使用数据库的课程信息表 - course。
根据数据库中的 Course 表对应创建 Course.java
@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 层接口
 * */
public interface CourseDao {
    ...
}
/**
 * 课程模块 DAO 层实现类
 * */
public class CourseDaoImpl implements CourseDao {
    ...
}
/**
 *  课程模块 Service 层 接口
 * */
public interface CourseService {
    ...
}
/**
 * 课程模块 Service 层 实现类
 * */
public class CourseServiceImpl implements CourseService {
    ...
}
CourseServlet 继承通用的 BaseServlet
@WebServlet(name="courseServlet", value="/course")
public class CourseServlet extends BaseServlet {
    ...
}
分析需求文档的对应页面,查看需要展示课程列表的哪些数据。
修改 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 是删除;在逻辑上数据是被删除的,但数据本身依然存在库中。
物理删除:物理删除就是真正的从数据库中做删除操作了。
修改 CourseService 添加 findCourseList 方法。
/**
 * 查询课程列表
 */
public List<Course> findCourseList();
实现类 CourseServiceImpl
/**
 * 创建 CourseDao
 */
CourseDao courseDao = new CourseDaoImpl();
/**
 * 查询课程列表
 */
@Override
public List<Course> findCourseList() {
    return courseDao.findCourseList();
}
接口文档:
前端的开发基于服务端编写的接口,如果前端人员等待服务端人员将接口开发完毕再去开发前端内容这样做效率是 非常低下的;所以当接口定义完成,可以使用工具生成接口文档,前端人员查看接口文档即可进行前端开发;这样 前端和服务人员并行开发,大大提高了生产效率。
开发规范:
getParameter() 获取。fastJson 进行解析;提交 form 表单数据;文件等多部件类型 multipart / form-data。在 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 是一款功能强大的 http 接口测试工具,使用 postman 可以完成 http 各种请求的功能测试。
官方地址:https://www.getpostman.com/
使用 postman 创建请求来测试接口;创建课程模块,将请求保存到对应模块中。
根据课程名称和课程状态进行查询。
要查询的字段:id, course_name, price, sort_num, STATUS
查询条件:is_del, course_name, statuts
因为是多条件查询,所以要注意多个参数情况下 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;
}
CourseService
List<Course> findByCourseNameAndStatus(String courseName, String status);
CourseServiceImpl
/**
 * 根据课程名和课程状态条件查询课程信息
 */
@Override
public List<Course> findByCourseNameAndStatus(String courseName, String status) {
    return courseDao.findByCourseNameAndStatus(courseName, status);
}
在 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 进行接口测试
选择新建课程,对课程营销信息进行录入。
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 ;
}
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://www.xx.com/xxx.jpg");
    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);
}
编写枚举类,设置响应状态码
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>
服务端要接收文件上传的表单数据,获取上传文件所需步骤:
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 使用步骤:
文件上传后台代码编写:
@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);
    }
}
1、将部署方式改变为 war 模式,把项目部署在 tomcat 的 webapps 目录下。
IDEA 中部署项目两种方式:
webapps 目录中2、在 webapps 中创建 upload 目录;upload 目录专门用来保存上传过来的图片。
3、修改代码,将图片上传到服务器。
修改图片的输出路径:
webapps 的目录路径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 中进行配置:
webapps 目录下的的 upload 文件夹<img src="/upload/image.jpg">http://localhost:8080/upload/image.jpgBeanUtils 是 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);
    }
}
创建 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);
    }
}
"key":"Content-Type","value":"multipart/form-data"创建常量类
public class Constants {
    /**
     * 本地的访问地址
     */
    public static final String LOCAL_URL = "http://localhost:8080";
}
CourseSalesInfoServlet
...
// 将图片路径进行保存
map.put("course_img_url", Constants.LOCAL_URL + "/upload/" + newFileName);
...
营销信息其实就是课程相关的信息,操作的依然是 course 表,通过点击营销信息按钮进入到对应的课程营销信息页面,对原有信息进行修改。
通过分析,首先要编写根据课程 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 ;
}
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));
}
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();
    }
}
在 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 中需要进行一下判断:携带 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 - 上架
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 ;
}
...
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;
}
...
在 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 进行测试