本文共 15502 字,大约阅读时间需要 51 分钟。
开发流程
需求分析
配置课时(课程内容管理)模块,主要是对课程内容进行管理。
数据库表分析
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 类型,这样就可以表示一个章节中包含多个课时。
// Course 类:... List sectionList = new ArrayList<>();...// Course_Section 类:... List lessonList = new ArrayList<>(); private Course course;...// Course_Lesson 类:... private Course_Section course_section;...
Dao 接口及实现类编写
/** * 课程内容管理 DAO 层接口 * */public interface CourseContentDao {}/** * 课程内容管理 DAO 层实现类 * */public class CourseContentDaoImpl implements CourseContentDao {}
Service 接口及实现类编写
/** * 课程内容管理 Service 层接口 * */public interface CourseContentService {}/** * 课程内容管理 Service 层实现类 * */public class CourseContentServiceImpl implements CourseContentService {}
CourseContentServlet 编写
CourseContentServlet 继承 BaseServlet
@WebServlet("/courseContent")public class CourseContentServlet extends BaseServlet {}
功能一:展示课程内容
需求分析
分析:要展示的内容是对应课程下的章节与课时信息
-- 查询 ID 为 1 的课程的章节与课时信息SELECT cs.`id`, cs.`section_name`, cl.`theme` '课时名称'FROM course_section cs INNER JOIN course_lesson clON cs.`id` = cl.`section_id` WHERE cs.`course_id` = 59;-- 在程序中尽量避免使用连接查询。可以将上面的 SQL 进行拆分,每一条 SQL 对应一个功能-- 根据课程 ID 查询章节相关的内容SELECT id, course_id, section_name, description, order_num, `status`FROM course_section WHERE course_id = 59;-- 根据章节 ID 查询课时相关的内容SELECT id, course_id, section_id, theme, is_free, order_num, `status`FROM course_lesson WHERE section_id = 32;
DAO 层编写
编写两个方法
CourseContentDaoImpl
/** * 根据课程 ID 查询课程相关信息 * * @param courseId */@Overridepublic List findSectionAndLessonByCourseId(int courseId) { try { // 创建 QueryRunner QueryRunner qr = new QueryRunner(DruidUtils.getDataSource()); // 编写 SQL String sql = "SELECT " + "id," + "course_id," + "section_name," + "description," + "order_num," + "STATUS" + "FROM course_section WHERE course_id = ?"; // 执行查询 List sectionList = qr.query(sql, new BeanListHandler(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 */@Overridepublic List findLessonBySectionId(int sectionId) { try { // 创建 QueryRunner QueryRunner qr = new QueryRunner(DruidUtils.getDataSource()); // 编写 SQL String sql = "SELECT " + "id," + "course_id," + "section_id," + "theme," + "duration," + "is_free," + "order_num," + "STATUS" + "FROM course_lesson WHERE section_id = ?"; // 执行查询 return qr.query(sql, new BeanListHandler(Course_Lesson.class), sectionId); } catch (SQLException throwables) { throwables.printStackTrace(); } return null;}
DAO 层测试
public class TestCourseContentDao { CourseContentDao contentDao = new CourseContentDaoImpl(); /** * 测试 查询对应课程下的章节与课时 */ @Test public void testFindSectionAndLessonByCourseId(){ List list = contentDao.findSectionAndLessonByCourseId(59); // 遍历输出章节信息 for (Course_Section courseSection : list) { System.out.println(courseSection.getId() + " = " + courseSection.getSection_name()); List lessonList = courseSection.getLessonList(); // 遍历输出课时信息 for (Course_Lesson courseLesson : lessonList) { System.out.println(courseLesson.getId() + " = " + courseLesson.getTheme() + " = " + courseLesson.getSection_id()); } System.out.println(); } }}
Service 层编写
CourseContentServiceImpl
CourseContentDao contentDao = new CourseContentDaoImpl();/** * 根据课程 id 查询课程内容 */@Overridepublic List findSectionAndLessonByCourseId(int courseId) { return contentDao.findSectionAndLessonByCourseId(courseId);}
Servlet 编写
CourseContentServlet 中添加 findSectionAndLessonByCourseId 方法
/** * 展示对应课程的章节与课时信息 */public void findSectionAndLessonByCourseId(HttpServletRequest request, HttpServletResponse response) { try { // 获取参数 String course_id = request.getParameter("course_id"); // 业务处理 CourseContentService contentService = new CourseContentServiceImpl(); List sectionList = contentService.findSectionAndLessonByCourseId(Integer.parseInt(course_id)); // 返回结果 response.getWriter().println(JSON.toJSONString(sectionList)); } catch (IOException e) { e.printStackTrace(); }}
接口测试:使用 Postman 根据接口文档进行测试
功能二:新建章节信息
需求分析
首先根据课程 ID 查询课程名称进行回显,然后获取输入的新的章节信息,执行保存章节数据。
Dao 层编写
CourseContentDaoImpl
.../** * 添加章节时进行数据回显 */@Overridepublic 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.class), courseId); } catch (SQLException throwables) { throwables.printStackTrace(); } return null;}/** * 保存章节信息 */@Overridepublic int saveSection(Course_Section section) { try { // 创建 QueryRunner QueryRunner qr = new QueryRunner(DruidUtils.getDataSource()); // 编写 SQL String sql = "INSERT INTO course_section(" + "course_id, " + "section_name, " + "description, " + "order_num, " + "`status`, " + "create_time, " + "update_time)" + "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 0;}...
Dao 层测试 TestCourseContentDao
/** * 测试 根据课程 id 回显课程名称 */@Testpublic void testFindCourseByCourseId() { Course course = contentDao.findCourseByCourseId(59); System.out.println(course.getId() + " " + course.getCourse_name());}/** * 测试 保存章节信息 */@Testpublic void testSaveSection() { Course_Section section = new Course_Section(); section.setCourse_id(59); section.setSection_name("章节名字"); section.setDescription("章节描述"); section.setOrder_num(8); // 更新时间 String dateFormat = DateUtils.getDateFormat(); section.setCreate_time(dateFormat); section.setUpdate_time(dateFormat); // 状态:0 隐藏,1 待更新,2 已发布 section.setStatus(2); System.out.println(contentDao.saveSection(section));}
Service 层编写
CourseContentServiceImpl
.../** * 添加章节时进行数据回显 */@Overridepublic Course findCourseById(int courseId) { return contentDao.findCourseByCourseId(courseId);}/** * 保存章节信息 */@Overridepublic String saveSection(Course_Section section) { // 补全章节信息 // 状态:0 隐藏,1 待更新,2 已发布 section.setStatus(2); String date = DateUtils.getDateFormat(); section.setCreate_time(date); section.setUpdate_time(date); // 调用 Dao 进行插入 if (contentDao.saveSection(section) > 0) { // 保存成功 return StatusCode.SUCCESS.toString(); } else { // 保存失败 return StatusCode.FAIL.toString(); }}...
Servlet 编写
课程信息回显接口
/** * 回显章节对应的课程信息 */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 格式的数据:
/** * 如果 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 方法:
@Overrideprotected 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 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:
/** * 保存 & 修改 章节信息 */public void saveOrUpdateSection(HttpServletRequest request, HttpServletResponse response){ try { // 从 request 域对象中获取获取参数 Map 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
/** * 修改章节信息 */@Overridepublic int updateSection(Course_Section section) { try { // 创建 QueryRunner QueryRunner qr = new QueryRunner(DruidUtils.getDataSource()); // 编写 SQL String sql = "UPDATE course_section SET " + "section_name = ?," + "description = ?," + "order_num = ?," + "update_time = ?" + "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 0;}
Dao 层测试 TestCourseContentDao
/** * 测试 修改章节信息 */@Testpublic void testUpdateSection() { Course_Section section = new Course_Section(); section.setId(41); section.setSection_name("微服务架构-Renda"); section.setDescription("微服务架构详解-Renda"); section.setOrder_num(3); section.setUpdate_time(DateUtils.getDateFormat()); System.out.println(contentDao.updateSection(section));}
Service 层编写
CourseContentServiceImpl
/** * 修改章节信息 * * @param section */@Overridepublic String updateSection(Course_Section section) { // 补全章节信息 String date = DateUtils.getDateFormat(); section.setUpdate_time(date); // 调用 Dao 进行插入,根据修改是否成功,封装对应信息 if (contentDao.updateSection(section) > 0) { return StatusCode.SUCCESS.toString(); } else { return StatusCode.FAIL.toString(); }}
Servlet 编写
保存章节信息和修改章节信息,访问的是同一个接口,所以在 CourseContentServlet 的 saveOrUpdateSection 方法中进行一下判断:携带 id 就是修改章节操作;未携带 id 就是新增章节操作。
...if (section.getId() != 0) { // 修改操作 response.getWriter().print(contentService.updateSection(section));} else { // 保存操作 response.getWriter().print(contentService.saveSection(section));}...
接口测试:根据接口文档,使用 Postman 进行测试。
功能四:章节状态管理
需求分析
根据选择的状态信息,发送对应的状态编号,进行修改 status 状态;0 隐藏,1 待更新,2 已发布。
DAO 层编写
CourseContentDaoImpl
/** * 修改章节的状态 */@Overridepublic 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 0;}
Dao 层测试 TestCourseContentDao
/** * 测试 修改章节状态 */@Testpublic void testUpdateSectionStatus() { System.out.println(contentDao.updateSectionStatus(1, 0));}
Service 层编写
CourseContentServiceImpl
/** * 修改章节的状态 */@Overridepublic String updateSectionStatus(int id, int status) { // 调用 Dao 修改状态,根据修改是否成功,封装对应信息 if (contentDao.updateSectionStatus(id, status) > 0){ return StatusCode.SUCCESS.toString(); } else { return StatusCode.FAIL.toString(); }}
Servlet 编写
CourseContentServlet
/** * 修改章节状态 */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 进行测试。
想了解更多,欢迎关注我的微信公众号:Renda_Zhang
转载地址:https://blog.csdn.net/weixin_33073525/article/details/113491427 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!