第三次课:课程分类和课程发布基本信息功能
分类: springboot 专栏: 在线教育项目实战 标签: 递归算法 导入功能
2023-05-03 17:27:15 1253浏览
递归算法
重难点:数据的封装和递归算法
Excel导入课程分类
采用easyexcel,官网:https://easyexcel.opensource.alibaba.com/
依赖
<dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>3.2.1</version> </dependency>
最简单的读的excel示例
最简单的读的对象
@Getter @Setter @EqualsAndHashCode public class DemoData { private String string; private Date date; private Double doubleData; }
假设每个属性类型不同的话,一般会用ExcelProperty注解
@Getter @Setter @EqualsAndHashCode public class IndexOrNameData { /** * 强制读取第三个 这里不建议 index 和 name 同时用,要么一个对象只用index,要么一个对象只用name去匹配 */ @ExcelProperty(index = 2) private Double doubleData; /** * 用名字去匹配,这里需要注意,如果名字重复,会导致只有一个字段读取到数据 */ @ExcelProperty("字符串标题") private String string; @ExcelProperty("日期标题") private Date date; }
最简单的读的监听器
// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去 @Slf4j public class DemoDataListener implements ReadListener<DemoData> { /** * 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收 */ private static final int BATCH_COUNT = 100; /** * 缓存的数据 */ private List<DemoData> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT); /** * 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。 */ private DemoDAO demoDAO; public DemoDataListener() { // 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数 demoDAO = new DemoDAO(); } /** * 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来 * * @param demoDAO */ public DemoDataListener(DemoDAO demoDAO) { this.demoDAO = demoDAO; } /** * 这个每一条数据解析都会来调用 * * @param data one row value. Is is same as {@link AnalysisContext#readRowHolder()} * @param context */ @Override public void invoke(DemoData data, AnalysisContext context) { log.info("解析到一条数据:{}", JSON.toJSONString(data)); //去掉重复的 if (!cachedDataList.contains(data)) { cachedDataList.add(data); } // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM if (cachedDataList.size() >= BATCH_COUNT) { saveData(); // 存储完成清理 list cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT); } } /** * 所有数据解析完成了 都会来调用 * * @param context */ @Override public void doAfterAllAnalysed(AnalysisContext context) { // 这里也要保存数据,确保最后遗留的数据也存储到数据库 saveData(); log.info("所有数据解析完成!"); } /** * 加上存储数据库 */ private void saveData() { log.info("{}条数据,开始存储数据库!", cachedDataList.size()); demoDAO.save(cachedDataList); log.info("存储数据库成功!"); } }
持久层
/** * 假设这个是你的DAO存储。当然还要这个类让spring管理,当然你不用需要存储,也不需要这个类。 **/ public class DemoDAO { public void save(List<DemoData> list) { // 如果是mybatis,尽量别直接调用多次insert,自己写一个mapper里面新增一个方法batchInsert,所有数据一次性插入 } }
业务层
//1 获取文件输入流 InputStream inputStream = file.getInputStream(); // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭 EasyExcel.read(inputStream, SubjectData.class, new SubjectExcelListener(subjectService)).sheet().doRead();
保存一级分类和二级分类
@Override public void saveFromExcel(List<SubjectData> cachedDataList) {//优化后的尽量少查数据库 //开始把从excel中读取到的数据保存到数据库 if (ObjectUtils.isEmpty(cachedDataList)) { throw new JfException(20001, "文件数据为空"); } //把cachedDataList封装成map Map<String, List<String>> map = new HashMap<>(); for (SubjectData subjectData : cachedDataList) { List<String> li = map.get(subjectData.getOneSubjectName()); if (li == null) li = new ArrayList<>(); li.add(subjectData.getTwoSubjectName()); map.put(subjectData.getOneSubjectName(), li); } //为了减少查询语句 List<EduSubject> subjects = this.list(); Map<String ,EduSubject> existMap = new HashMap<>(); for (EduSubject o : subjects) { existMap.put(o.getTitle(),o); } map.forEach((one,twoLi)-> { EduSubject existOneSubject= existMap.get(one); if(existOneSubject==null){ //保存一级目录 existOneSubject=new EduSubject(); existOneSubject.setTitle( one); existOneSubject.setParentId("0"); this.save(existOneSubject); } // 备注:如果一级目录已经存在的话只保存二级目录即可 List<EduSubject> twoSubjects= new ArrayList<>(); for (String title : twoLi) { EduSubject existTwoSubject = existMap.get(title); if(existTwoSubject == null){ //判断二级目录去重 existTwoSubject = new EduSubject(); existTwoSubject.setTitle(title); existTwoSubject.setParentId(existOneSubject.getId()); twoSubjects.add(existTwoSubject); } } //批量保存二级目录 this.saveBatch(twoSubjects); }); }
递归算法查询课程分类列表(树形)
递归查询效率比较高。
可以模仿以下方法
/** * <p> * 根据权限数据构建菜单数据 * </p> */ public class PermissionHelper { /** * 使用递归方法建菜单 * @param treeNodes * @return */ public static List<Permission> bulid(List<Permission> treeNodes) { List<Permission> trees = new ArrayList<>(); for (Permission treeNode : treeNodes) { if ("0".equals(treeNode.getPid())) { treeNode.setLevel(1); trees.add(findChildren(treeNode,treeNodes)); } } return trees; } /** * 递归查找子节点 * @param treeNodes * @return */ public static Permission findChildren(Permission treeNode,List<Permission> treeNodes) { treeNode.setChildren(new ArrayList<Permission>()); for (Permission it : treeNodes) { if(treeNode.getId().equals(it.getPid())) { int level = treeNode.getLevel() + 1; it.setLevel(level); if (treeNode.getChildren() == null) { treeNode.setChildren(new ArrayList<>()); } treeNode.getChildren().add(findChildren(it,treeNodes)); } } return treeNode; } }
王老师写法:
/** * //其实有很多查询的方式 * 1.先查询出一级分类然后循环查二级分类set进去。这种是笨办法,sql语句多 * 2.自关联查询 * 3.递归查询(性能最好) * @return */ @Override public List<SubjectVo> getAllSubject() { List<SubjectVo> vos= new ArrayList<>(); List<EduSubject> list = this.list();//查询所有的分类 for (EduSubject eduSubject : list) { SubjectVo subjectVo = new SubjectVo(); BeanUtils.copyProperties(eduSubject,subjectVo); vos.add(subjectVo); } //vos 数据库有几条分类数据就有几个 ,二级和一级全都混合在一起 //递归的方式查 一直自查 直到查不到数据为止 List<SubjectVo> subjectVos = new ArrayList<>(); for (SubjectVo treeNode : vos) { if ("0".equals(treeNode.getParentId())) {//说明是一级分类 subjectVos.add(findChildren(treeNode,vos));//就要装填二级分类 } } return subjectVos; } /** * 递归查找子节点 自己调用自己 * @param treeNodes * @return */ public static SubjectVo findChildren( SubjectVo subject,List<SubjectVo> treeNodes) { subject.setChildren(new ArrayList<>()); for (SubjectVo it : treeNodes) { if(subject.getId().equals(it.getParentId())) { // subject.getChildren().add(findChildren(it,treeNodes));//把二级分类挂在一级上面(也可以是三级挂在二级上面以此类推) subject.getChildren().add(it);//把二级分类挂在一级上面 findChildren(it,treeNodes);//根据二级找三级,根据三级找四级,以此类推 } } System.out.println(subject.getChildren()); return subject; }
添加课程基本信息
后端接口
注意添加课程基本信息的同时,要把课程简介表也填充好,课程简介表的id为课程基本信息ID
注意要改课程简介实体类里id的生成策略
@TableId(type = IdType.INPUT) private String id;//拿到课程的主键id后手动set进来
//1 向课程表添加课程基本信息 //CourseInfoVo对象转换eduCourse对象 EduCourse eduCourse = new EduCourse(); BeanUtils.copyProperties(courseInfoVo,eduCourse); int insert = baseMapper.insert(eduCourse); if(insert == 0) { //添加失败 throw new JfException(20001,"添加课程信息失败"); } //获取添加之后课程id String cid = eduCourse.getId(); //2 向课程简介表添加课程简介 //edu_course_description EduCourseDescription courseDescription = new EduCourseDescription(); courseDescription.setDescription(courseInfoVo.getDescription()); //设置描述id就是课程id courseDescription.setId(cid); courseDescriptionService.save(courseDescription); return cid;
富文本编辑器
参考文章https://www.kancloud.cn/liuwave/quill/1434140
npm install vue-quill-editor --save
全局使用
import Vue from 'vue' import VueQuillEditor from 'vue-quill-editor' // require styles import 'quill/dist/quill.core.css' import 'quill/dist/quill.snow.css' import 'quill/dist/quill.bubble.css' Vue.use(VueQuillEditor, /* { default global options } */)
<el-form-item label="课程简介" > <quill-editor v-model="courseInfo.description" /> </el-form-item> <style> .ql-editor{ height:400px; } </style>
好博客就要一起分享哦!分享海报
此处可发布评论
评论(3)展开评论
您可能感兴趣的博客
他的专栏
他感兴趣的技术