教育平台项目后台管理系统:课程内容模块

2020-09-08 16:13:38 浏览数 (1)

开发流程

需求分析

配置课时(课程内容管理)模块,主要是对课程内容进行管理。

数据库表分析

course - 课程表

course_section - 课程章节表

course_lesson - 课时信息表

一个课程表对多个课程章节表,一个课程章节表对多个课时表。

实体类设计

Course 类与 Course_Section 类是一对多关系;Course_Section 类与 Course_Lesson 类是一对多关系。

在 Course 类中定义一个 List 集合,并指定 List 的泛型是 Course_Section 类型,表示 一个课程中可以包含多个章节。

在 Course_Section 类中,定义一个 Course 类型的属性,用来保存章节所对应的具体的课程信息。

在 Course_Section 类中定义一个 List 集合,并指定 List 的泛型是 Course_Lesson 类型,这样就可以表示一个章节中包含多个课时。

代码语言:javascript复制
// Course 类:
...
  List<Course_Section> sectionList = new ArrayList<>();
...

// Course_Section 类:
...
  List<Course_Lesson> lessonList = new ArrayList<>();
  private Course course;
...

// Course_Lesson 类:
...
  private Course_Section course_section;
...
Dao 接口及实现类编写
代码语言:javascript复制
/**
 * 课程内容管理 DAO 层接口
 * */
public interface CourseContentDao {
}

/**
 * 课程内容管理 DAO 层实现类
 * */
public class CourseContentDaoImpl implements CourseContentDao {
}
Service 接口及实现类编写
代码语言:javascript复制
/**
 * 课程内容管理 Service 层接口
 * */
public interface CourseContentService {
}

/**
 * 课程内容管理 Service 层实现类
 * */
public class CourseContentServiceImpl implements CourseContentService {
}
`CourseContentServlet` 编写

CourseContentServlet 继承 BaseServlet

代码语言:javascript复制
@WebServlet("/courseContent")
public class CourseContentServlet extends BaseServlet {
}

功能一:展示课程内容

需求分析

分析:要展示的内容是对应课程下的章节与课时信息

代码语言:javascript复制
-- 查询 ID 为 1 的课程的章节与课时信息
SELECT 
    cs.`id`,
    cs.`section_name`,
    cl.`theme` '课时名称'
FROM course_section cs INNER JOIN course_lesson cl
ON cs.`id` = cl.`section_id` 
WHERE cs.`course_id` = ;

-- 在程序中尽量避免使用连接查询。可以将上面的 SQL 进行拆分,每一条 SQL 对应一个功能

-- 根据课程 ID 查询章节相关的内容
SELECT 
    id,
    course_id,
    section_name,
    description,
    order_num,
    `status`
FROM course_section WHERE course_id = ;

-- 根据章节 ID 查询课时相关的内容
SELECT
    id,
    course_id,
    section_id,
    theme,
    is_free,
    order_num,
    `status`
FROM course_lesson WHERE section_id = ;
DAO 层编写

编写两个方法

CourseContentDaoImpl

代码语言:javascript复制
/**
 * 根据课程 ID 查询课程相关信息
 *
 * @param courseId
 */
@Override
public List<Course_Section> findSectionAndLessonByCourseId(int courseId) {
    try {
        // 创建 QueryRunner
        QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());

        // 编写 SQL
        String sql = "SELECT n"  
                "id,n"  
                "course_id,n"  
                "section_name,n"  
                "description,n"  
                "order_num,n"  
                "STATUSn"  
                "FROM course_section WHERE course_id = ?";

        // 执行查询
        List<Course_Section> sectionList = qr.query(sql,
                new BeanListHandler<Course_Section>(Course_Section.class), courseId);

        // 根据章节 ID 查询课时信息
        for (Course_Section section : sectionList) {
            // 调用获取章节对应的课时方法,将课时数据封装到章节对象中
            section.setLessonList(findLessonBySectionId(section.getId()));
        }

        // 返回结果
        return sectionList;
    } catch (SQLException throwables) {
        throwables.printStackTrace();
    }
    return null;
}

/**
 * 根据章节 ID 查询章节相关的课时信息
 *
 * @param sectionId
 */
@Override
public List<Course_Lesson> findLessonBySectionId(int sectionId) {
    try {
        // 创建 QueryRunner
        QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());

        // 编写 SQL
        String sql = "SELECT n"  
                "id,n"  
                "course_id,n"  
                "section_id,n"  
                "theme,n"  
                "duration,n"  
                "is_free,n"  
                "order_num,n"  
                "STATUSn"  
                "FROM course_lesson WHERE section_id = ?";

        // 执行查询
        return qr.query(sql, new BeanListHandler<Course_Lesson>(Course_Lesson.class), sectionId);
    } catch (SQLException throwables) {
        throwables.printStackTrace();
    }

    return null;
}

DAO 层测试

代码语言:javascript复制
public class TestCourseContentDao {

    CourseContentDao contentDao = new CourseContentDaoImpl();

    /**
     * 测试 查询对应课程下的章节与课时
     */
    @Test
    public void testFindSectionAndLessonByCourseId(){
        List<Course_Section> list = contentDao.findSectionAndLessonByCourseId();
        // 遍历输出章节信息
        for (Course_Section courseSection : list) {
            System.out.println(courseSection.getId()   " = "   courseSection.getSection_name());

            List<Course_Lesson> lessonList = courseSection.getLessonList();
            // 遍历输出课时信息
            for (Course_Lesson courseLesson : lessonList) {
                System.out.println(courseLesson.getId()   " = "   courseLesson.getTheme()   " = "
                          courseLesson.getSection_id());
            }
            System.out.println();
        }
    }
}
Service 层编写

CourseContentServiceImpl

代码语言:javascript复制
CourseContentDao contentDao = new CourseContentDaoImpl();

/**
 * 根据课程 id 查询课程内容
 */
@Override
public List<Course_Section> findSectionAndLessonByCourseId(int courseId) {
    return contentDao.findSectionAndLessonByCourseId(courseId);
}
Servlet 编写

CourseContentServlet 中添加 findSectionAndLessonByCourseId 方法

代码语言:javascript复制
/**
 * 展示对应课程的章节与课时信息
 */
public void findSectionAndLessonByCourseId(HttpServletRequest request, HttpServletResponse response) {
    try {
        // 获取参数
        String course_id = request.getParameter("course_id");

        // 业务处理
        CourseContentService contentService = new CourseContentServiceImpl();
        List<Course_Section> sectionList = contentService.findSectionAndLessonByCourseId(Integer.parseInt(course_id));

        // 返回结果
        response.getWriter().println(JSON.toJSONString(sectionList));
    } catch (IOException e) {
        e.printStackTrace();
    }
}

接口测试:使用 Postman 根据接口文档进行测试

功能二:新建章节信息

需求分析

首先根据课程 ID 查询课程名称进行回显,然后获取输入的新的章节信息,执行保存章节数据。

Dao 层编写

CourseContentDaoImpl

代码语言:javascript复制
...

/**
 * 添加章节时进行数据回显
 */
@Override
public Course findCourseByCourseId(int courseId) {
    try {
        // 创建 QueryRunner
        QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());

        // 编写 SQL
        String sql = "SELECT id, course_name FROM course WHERE id = ?";

        // 执行查询并返回结果
        return qr.query(sql, new BeanHandler<Course>(Course.class), courseId);
    } catch (SQLException throwables) {
        throwables.printStackTrace();
    }

    return null;
}

/**
 * 保存章节信息
 */
@Override
public int saveSection(Course_Section section) {
    try {
        // 创建 QueryRunner
        QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());

        // 编写 SQL
        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()};

        // 返回受影响的行数
        return qr.update(sql, params);
    } catch (SQLException throwables) {
        throwables.printStackTrace();
    }

    return ;
}

...

Dao 层测试 TestCourseContentDao

代码语言:javascript复制
/**
 * 测试 根据课程 id 回显课程名称
 */
@Test
public void testFindCourseByCourseId() {
    Course course = contentDao.findCourseByCourseId();
    System.out.println(course.getId()   " "   course.getCourse_name());
}

/**
 * 测试 保存章节信息
 */
@Test
public void testSaveSection() {
    Course_Section section = new Course_Section();
    section.setCourse_id();
    section.setSection_name("章节名字");
    section.setDescription("章节描述");
    section.setOrder_num();
    // 更新时间
    String dateFormat = DateUtils.getDateFormat();
    section.setCreate_time(dateFormat);
    section.setUpdate_time(dateFormat);
    // 状态:0 隐藏,1 待更新,2 已发布
    section.setStatus();
    System.out.println(contentDao.saveSection(section));
}
Service 层编写

CourseContentServiceImpl

代码语言:javascript复制
...

/**
 * 添加章节时进行数据回显
 */
@Override
public Course findCourseById(int courseId) {
    return contentDao.findCourseByCourseId(courseId);
}

/**
 * 保存章节信息
 */
@Override
public String saveSection(Course_Section section) {
    // 补全章节信息
    // 状态:0 隐藏,1 待更新,2 已发布
    section.setStatus();
    String date = DateUtils.getDateFormat();
    section.setCreate_time(date);
    section.setUpdate_time(date);

    // 调用 Dao 进行插入
    if (contentDao.saveSection(section) > ) {
        // 保存成功
        return StatusCode.SUCCESS.toString();
    } else {
        // 保存失败
        return StatusCode.FAIL.toString();
    }
}

...
Servlet 编写
课程信息回显接口
代码语言:javascript复制
/**
 * 回显章节对应的课程信息
 */
public void findCourseById(HttpServletRequest request , HttpServletResponse response){
    try {
        // 获取参数
        String courseId = request.getParameter("course_id");

        // 业务处理
        CourseContentService contentService = new CourseContentServiceImpl();
        Course course = contentService.findCourseById(Integer.parseInt(courseId));

        // 返回数据,将对象转换为 JSON,只转换需要的字段
        SimplePropertyPreFilter filter = new SimplePropertyPreFilter(Course.class,
                "id", "course_name");
        response.getWriter().println(JSON.toJSONString(course,filter));
    } catch (IOException e) {
        e.printStackTrace();
    }
}
保存章节信息接口

POST 请求方法常用的三种数据提交格式:

  • Content-Type : application/x-www-form-urlencoded 请求体中的数据会以普通表单形式(键值对)发送到后端。
  • Content-Type : application/json ; charset=utf-8 请求体中的数据会以 JSON 字符串的形式发送到后端。
  • Content-Type : multipart/form-data 多部件上传既可以上传键值对,也可以上传文件。

第二种 JSON 格式与第三种多部件上传都无法使用 getParameter() 方法获取数据。

根据接口文档描述:前台传输的是 JSON 格式的数据,使用 getParameter() 方法无法获取参数。

如果请求参数是 JSON 格式的数,可以通过 request.getReader() 这个方法获取一个流对象来进行 读取。

首先,在 BaseServlet 中创建一个方法用来获取 JSON 格式的数据:

代码语言:javascript复制
/**
 * 如果 POST 请求格式为 application/json;charset=utf-8
 * 则在这个方法中使用流的方式获取到 POST 请求的数据
 */
public String getPostJSON(HttpServletRequest request){
    try {
        // 从 request 中获取字符缓冲输入流对象
        BufferedReader reader = request.getReader();

        // 创建 StringBuffer 用来保存读取出的数据
        StringBuffer sb = new StringBuffer();

        // 循环读取
        String line = null;
        while((line = reader.readLine()) != null){
            // 追加到 StringBuffer 中
            sb.append(line);
        }

        // 将读取到的内容转换为字符串并返回
        return sb.toString();
    } catch (IOException e) {
        e.printStackTrace();
    }

    return null;
}

然后,修改 BaseServlet 中的 doPost 方法:

代码语言:javascript复制
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    // 要访问的方法名
    String methodName = null;

    // 获取 POST 请求的 Content-Type 类型
    String contentType = req.getHeader("Content-Type");

    // 判断传递的数据是不是 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");
    }

    // 判断并执行对应的方法
    if (methodName != null) {
        // 使用反射方式提升代码的可维护性
        try {
            // 获取字节码对象
            Class aClass = this.getClass();
            // 根据传入的方法名获取对应的方法对象
            Method method = aClass.getMethod(methodName, HttpServletRequest.class, HttpServletResponse.class);
            method.invoke(this, req, resp);
        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
            System.out.println("请求的功能不存在");
        }
    }
}

最后,编写接口代码,CourseContentServlet

代码语言:javascript复制
/**
 * 保存 & 修改 章节信息
 */
public void saveOrUpdateSection(HttpServletRequest request, HttpServletResponse response){
    try {
        // 从 request 域对象中获取获取参数
        Map<String,Object> map = (Map) request.getAttribute("map");

        // 创建 Course_Section
        Course_Section section = new Course_Section();

        // 使用 BeanUtils 工具类,将 map 中的数据封装到 section
        BeanUtils.populate(section, map);

        // 业务处理并响应结果
        CourseContentService contentService = new CourseContentServiceImpl();
        response.getWriter().print(contentService.saveSection(section));
    } catch (IllegalAccessException | InvocationTargetException | IOException e) {
        e.printStackTrace();
    }
}

使用 Postman 测试接口:

  • 选择 POST 请求方式,设置 Content-Type = application/json
  • 选择 raw 发送 JSON 格式数据

功能三:章节信息修改

需求分析

选择章节,点击编辑,传递章节 id;根据章节 id 修改章节信息。

根据接口文档,没有要求编写回显接口,不需要后台根据章节 id 查询对应章节信息进行回显,所以回显操作由前端代码完成。

DAO 层编写

CourseContentDaoImpl

代码语言:javascript复制
/**
 * 修改章节信息
 */
@Override
public int updateSection(Course_Section section) {
    try {
        // 创建 QueryRunner
        QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());

        // 编写 SQL
        String sql = "UPDATE course_section SET n"  
                "section_name = ?,n"  
                "description = ?,n"  
                "order_num = ?,n"  
                "update_time = ?n"  
                "WHERE id = ?";

        // 准备参数
        Object[] param = {section.getSection_name(), section.getDescription(),
                section.getOrder_num(), section.getUpdate_time(), section.getId()};

        // 执行修改操作
        return qr.update(sql, param);
    } catch (SQLException throwables) {
        throwables.printStackTrace();
    }

    return ;
}

Dao 层测试 TestCourseContentDao

代码语言:javascript复制
/**
 * 测试 修改章节信息
 */
@Test
public void testUpdateSection() {
    Course_Section section = new Course_Section();
    section.setId();
    section.setSection_name("微服务架构-Renda");
    section.setDescription("微服务架构详解-Renda");
    section.setOrder_num();
    section.setUpdate_time(DateUtils.getDateFormat());

    System.out.println(contentDao.updateSection(section));
}
Service 层编写

CourseContentServiceImpl

代码语言:javascript复制
/**
 * 修改章节信息
 *
 * @param section
 */
@Override
public String updateSection(Course_Section section) {
    // 补全章节信息
    String date = DateUtils.getDateFormat();
    section.setUpdate_time(date);

    // 调用 Dao 进行插入,根据修改是否成功,封装对应信息
    if (contentDao.updateSection(section) > ) {
        return StatusCode.SUCCESS.toString();
    } else {
        return StatusCode.FAIL.toString();
    }

}
Servlet 编写

保存章节信息和修改章节信息,访问的是同一个接口,所以在 CourseContentServletsaveOrUpdateSection 方法中进行一下判断:携带 id 就是修改章节操作;未携带 id 就是新增章节操作。

代码语言:javascript复制
...

if (section.getId() != ) {
    // 修改操作
    response.getWriter().print(contentService.updateSection(section));
} else {
    // 保存操作
    response.getWriter().print(contentService.saveSection(section));
}

...

接口测试:根据接口文档,使用 Postman 进行测试。

功能四:章节状态管理

需求分析

根据选择的状态信息,发送对应的状态编号,进行修改 status 状态;0 隐藏,1 待更新,2 已发布。

DAO 层编写

CourseContentDaoImpl

代码语言:javascript复制
/**
 * 修改章节的状态
 */
@Override
public int updateSectionStatus(int id, int status) {
    try {
        // 创建 QueryRunner
        QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());

        // 编写 SQL
        String sql = "UPDATE course_section SET `status` = ?, update_time = ? WHERE id = ?";

        // 准备参数
        Object[] param = {status, DateUtils.getDateFormat(), id};

        // 执行修改操作
        return qr.update(sql, param);
    } catch (SQLException throwables) {
        throwables.printStackTrace();
    }

    return ;
}

Dao 层测试 TestCourseContentDao

代码语言:javascript复制
/**
 * 测试 修改章节状态
 */
@Test
public void testUpdateSectionStatus() {
    System.out.println(contentDao.updateSectionStatus(, ));
}
Service 层编写

CourseContentServiceImpl

代码语言:javascript复制
/**
 * 修改章节的状态
 */
@Override
public String updateSectionStatus(int id, int status) {
    // 调用 Dao 修改状态,根据修改是否成功,封装对应信息
    if (contentDao.updateSectionStatus(id, status) > ){
        return StatusCode.SUCCESS.toString();
    } else {
        return StatusCode.FAIL.toString();
    }
}
Servlet 编写

CourseContentServlet

代码语言:javascript复制
/**
 * 修改章节状态
 */
public void updateSectionStatus(HttpServletRequest request , HttpServletResponse response) {
    try {
        // 获取参数
        int id = Integer.parseInt(request.getParameter("id"));
        int status = Integer.parseInt(request.getParameter("status"));

        // 业务处理并返回结果数据
        CourseContentService contentService = new CourseContentServiceImpl();
        response.getWriter().println(contentService.updateSectionStatus(id, status));
    } catch (IOException e) {
        e.printStackTrace();
    }
}

接口测试:根据接口文档,使用 Postman 进行测试。

0 人点赞