您的位置:首页 > 游戏 > 手游 > 佛山品牌网站建设_站长之家html_信息流广告投放工作内容_关键词挖掘排名

佛山品牌网站建设_站长之家html_信息流广告投放工作内容_关键词挖掘排名

2025/1/11 1:12:41 来源:https://blog.csdn.net/cangshanjiang/article/details/145040335  浏览:    关键词:佛山品牌网站建设_站长之家html_信息流广告投放工作内容_关键词挖掘排名
佛山品牌网站建设_站长之家html_信息流广告投放工作内容_关键词挖掘排名

course

模块架构

核心功能分析

1 课程分类

• 支持三级分类管理,限制分类层级不超过三级。

• 提供工具类(CategoryDataWrapper)处理分类的树形结构。

Category 表(或对应 PO 类)设计如下:

public class Category {private Long id;          // 分类IDprivate String name;      // 分类名称private Long parentId;    // 父分类IDprivate Integer level;    // 分类层级private Integer priority; // 排序优先级private Boolean status;   // 状态(启用/禁用)
}

层级计算

• 如果无父分类(parentId = null),则为一级分类,level = 1。

• 有父分类时,子分类的层级为 父分类.level + 1。

@Override
public void addCategory(CategoryAddDTO categoryAddDTO) {if (categoryAddDTO.getParentId() != null) {Category parentCategory = categoryMapper.selectById(categoryAddDTO.getParentId());if (parentCategory == null) {throw new BusinessException("父分类不存在");}if (parentCategory.getLevel() >= 3) {throw new BusinessException("三级分类下不能再创建子分类");}}
​Category category = new Category();category.setName(categoryAddDTO.getName());category.setParentId(categoryAddDTO.getParentId());category.setLevel(parentCategory == null ? 1 : parentCategory.getLevel() + 1);category.setStatus(true); // 默认启用categoryMapper.insert(category);
}

查询分类树

查询分类时,返回完整的分类树结构:

@Override
public List<CategoryVO> getCategoryTree() {List<Category> categories = categoryMapper.selectAll();
​// 使用工具类将分类列表转换为树形结构List<CategoryVO> categoryTree = TreeDataUtils.buildTree(categories, new CategoryDataWrapper());
​return categoryTree;
}

2 目录管理

• 目录包括章和节,以及可能的练习题目(类型定义在CataType)。

草稿模式: 支持目录草稿管理,使用course_catalogue_draft表临时存储未发布的目录结构。

目录表:course_catalogue

public class CourseCatalogue {private Long id;               // 目录IDprivate String name;           // 名称private Long courseId;         // 所属课程IDprivate Integer type;          // 类型(章、节、练习)private Long parentCatalogueId;// 父目录IDprivate Long mediaId;          // 媒资ID(视频或其他媒体)private Long videoId;          // 视频IDprivate String videoName;      // 视频名称private LocalDateTime livingStartTime; // 直播开始时间private LocalDateTime livingEndTime;   // 直播结束时间private Boolean playBack;      // 是否支持回放private Integer cIndex;        // 排序索引private LocalDateTime createTime; // 创建时间private LocalDateTime updateTime; // 更新时间
}

CataType 定义了目录节点的类型:

public class CataType {public static final int CHAPTER = 1; // 章public static final int SECTION = 2; // 节public static final int PRACTICE = 3; // 练习或测试
}

草稿目录编辑完成后,将其发布到正式表中:

@Override
public void publishDraft(Long courseId) {// 清空正式表中该课程的目录courseCatalogueMapper.deleteByCourseId(courseId);
​// 将草稿表中的目录迁移到正式表courseCatalogueDraftMapper.insertFromCourseCatalogue(courseId);
​// 删除草稿表中的数据courseCatalogueDraftMapper.deleteByCourseId(courseId);
}

3 课程上下架

• 状态流转:

• 待上架 (1) → 已上架 (2) → 下架 (3) → 完结 (4)

• 定时任务通过CourseJobHandler实现课程的自动上下架。

课程状态存储在课程表中,通过 status 字段表示:

public class Course {private Long id;                  // 课程IDprivate String name;              // 课程名称private Integer status;           // 状态(待上架、已上架、下架、完结)private LocalDateTime publishTime;// 发布时间private LocalDateTime startTime;  // 开始时间private LocalDateTime endTime;    // 结束时间private Boolean deleted;          // 是否删除private LocalDateTime createTime; // 创建时间private LocalDateTime updateTime; // 更新时间
}

在 CourseMapper 中定义查询方法:

@Select("SELECT * FROM course WHERE status = #{status} AND start_time <= #{time}")
List<Course> getCoursesByStatusAndStartTime(@Param("status") Integer status, @Param("time") LocalDateTime time);
​
@Select("SELECT * FROM course WHERE status = #{status} AND end_time <= #{time}")
List<Course> getCoursesByStatusAndEndTime(@Param("status") Integer status, @Param("time") LocalDateTime time);

• 查询条件:

待上架课程:当前时间大于等于 start_time。

已上架课程:当前时间大于等于 end_time。

4 关联题目

• 提供对题目的关联管理,支持批量插入和删除。

• 工具类SubjectUtils用于处理题目选项。

课程-题目关联表

public class CourseCataSubject {private Long id;        // 主键IDprivate Long courseId;  // 课程IDprivate Long cataId;    // 目录IDprivate Long subjectId; // 题目ID
}

使用 MyBatis 的动态 SQL 构造批量插入语句:

@Insert("<script>INSERT INTO course_cata_subject (course_id, cata_id, subject_id) VALUES " +"<foreach collection='courseCataSubjects' item='ccs' separator=','>" +"(#{ccs.courseId}, #{ccs.cataId}, #{ccs.subjectId})" +"</foreach></script>")
int batchInsert(@Param("courseCataSubjects") List<CourseCataSubject> courseCataSubjects);

工具类:SubjectUtils

• 动态处理题目选项(如单选、多选)。

封装选项:将选项集合写入题目对象。

解析选项:从题目对象中读取选项集合。

public class SubjectUtils {
​/*** 将选项列表设置到题目中* @param subject 题目* @param options 选项列表*/public static void setOptions(Subject subject, List<String> options) {if (CollUtils.isNotEmpty(options)) {for (int count = 0; count < options.size(); count++) {ReflectUtils.setFieldValue(subject, "option" + (count + 1), options.get(count));}}}
​/*** 从题目中获取选项列表* @param subject 题目* @return 选项列表*/public static List<String> getOptions(Subject subject) {List<String> options = new ArrayList<>();for (int count = 1; count <= 10; count++) {Object option = ReflectUtils.getFieldValue(subject, "option" + count);if (ObjectUtils.isEmpty(option) || StringUtils.isEmpty((String) option)) {return options;}options.add((String) option);}return options;}
}

工具类实现细节

1. 设置选项

• 动态将 options 列表中的选项写入到 Subject 对象的 option1, option2 等字段中。

• 使用反射工具 ReflectUtils 动态设置字段值。

题目的选项数量可能是动态的,例如:

• 单选题可能有 4 个选项

• 多选题可能有 6 个选项

• 不同题目类型对选项数量的要求可能不同。

而 Subject 类可能预定义了固定数量的选项字段(如 option1, option2, …, option10),但具体使用多少字段取决于题目类型或数据。因此:

通过反射动态遍历字段,可以灵活处理任意数量的选项。

• 避免写死代码(如逐一手动访问 option1, option2 等)。

示例

• 输入选项 ["A", "B", "C"]。

• 结果:subject.option1 = "A", subject.option2 = "B", subject.option3 = "C"。

2. 获取选项

• 通过反射获取 Subject 对象中的 option1, option2 等字段值。

• 将非空字段值依次加入选项列表。

示例

• subject.option1 = "A", subject.option2 = "B", subject.option3 = null。

• 输出选项:["A", "B"]。

5 缓存机制

• 使用Redis存储分类和课程数量缓存,减少数据库查询压力。

• 定义了统一的缓存键规则(如REDIS_KEY_CATEGORY_THIRD_NUMBER)。

缓存用途

分类数据缓存:存储各分类及其子分类的课程数量。

课程数量缓存:记录课程的数量统计结果,用于快速查询。

在 RedisConstants 类中定义了统一的缓存键规则:

public class RedisConstants {
​// 一级二级分类拥有的三级分类的数量public static final String REDIS_KEY_CATEGORY_THIRD_NUMBER = "CATEGORY:THIRD_NUMBER";
​// 内部嵌套类用于格式化动态键public static class Formatter {// 分类课程数量统计public static final String STATISTICS_COURSE_NUM_CATE = "COURSE:COURSE_NUM_CATEGORY";// 有课程的分类 ID 列表public static final String CATEGORY_ID_LIST_HAVE_COURSE = "COURSE:CATEGORY_ID_WITH_COURSE";}
}

键命名规范

静态键

• 如 CATEGORY:THIRD_NUMBER,用于存储固定层级的分类统计。

动态键

• 如 COURSE:CATEGORY_ID_WITH_COURSE:{categoryId},通过参数动态生成完整的 Redis 键名,适用于按分类 ID 或其他标识存储的数据。

在 CourseCategoryServiceImpl 中实现存储分类课程数量的缓存逻辑:

@Override
public void cacheCategoryCourseCount(Long categoryId, int courseCount) {String redisKey = RedisConstants.REDIS_KEY_CATEGORY_THIRD_NUMBER + ":" + categoryId;redisTemplate.opsForValue().set(redisKey, courseCount, Duration.ofHours(1));log.info("Cached course count [{}] for category [{}]", courseCount, categoryId);
}

Redis Key 构造

• 静态键 + 动态参数组合生成完整的 Redis Key。

• 示例:CATEGORY:THIRD_NUMBER:101 表示分类 ID 为 101 的三级分类课程数量。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com