当前位置: 首页 > article >正文

学成在线实战

#1024程序员节|征文#

一、Bug修改

在实战之前,老师留了一个bug,这个bug出现的原因是因为在查询课程计划时,使用的是Inner join查询,所以当章节下面没有小节的时候,是查不出来数据的,只需要将其sql语句改为left join即可解决

二、实战前准备

1.排序设置不合理原因分析

在先前老师带我们做的添加课程计划中的设置排序这部分是有问题的,老师也说过这部分并不合理,需要我们自己调整。
原因在于:原先我们添加课程计划,设置排序时采用的是节点数量+1方式,这样有什么问题呢,举个例子:当我们添加的小节有四个,他们的orderby值为1、2、3、4,如果我们将2和3删除之后,在添加小节,那么新添加的小节orderby=2+1,那么它会排在原先的4之前而不是最后,会出现乱序问题,会导致乱序问题,这便是原因。

2.改进策略

我们通过查询同级之中orderby的max值并将其+1作为新增的排序规则即可。

(1)TeachplanMapper

/**
 * 查询同一级节点中orderBy的最大值
 * @param parentId 父节点id
 * @param courseId 课程id
 * @return
*/
Integer selectMaxOrderBy(@Param("parentId") Long parentId, @Param("courseId") Long courseId);


// TeacherMapper.xml
<!-- 查询同一级节点中orderBy的最大值 -->
<select id="selectMaxOrderBy" resultType="java.lang.Integer">
     select max(orderby)
     from teachplan
     where parentid=#{parentId} and course_id=#{courseId}
</select>

(2)TeachplanServiceImpl

将原来的新增部分改为如下所示:

    /**
     * 新增/修改/保存课程计划
     * @param saveTeachplanDto 模型类
     */
    @Override
    public void saveTeachplan(SaveTeachplanDto saveTeachplanDto) {
        // 通过课程计划id判断是新增和修改
        Long teachplanId = saveTeachplanDto.getId();
        if (teachplanId == null) {
            // 新增
            Teachplan teachplan = new Teachplan();
            BeanUtils.copyProperties(saveTeachplanDto, teachplan);
            // 确定排序字段 - 找到同级节点个数,排序字段就是当前节点下的子节点数量加一 问题:当节点进行删除操作后,存在bug
            Long courseId = saveTeachplanDto.getCourseId();
            Long parentId = saveTeachplanDto.getParentid();
//            teachplan.setOrderby(getTeachplanCount(courseId, parentid) + 1);
            int count = getTeachplanCount(courseId, parentId);
            int maxOrderBy = 0; // 给个默认值,否则当章节之下无小节点时,会报空指针异常
            if (count != 0) { // 章节之下有小节点
                maxOrderBy = teachplanMapper.selectMaxOrderBy(parentId, courseId); // 根据当下的最大orderBy+1排序
            }
            teachplan.setOrderby(maxOrderBy + 1);

            teachplanMapper.insert(teachplan);
        } else {
            // 修改
            Teachplan teachplan = teachplanMapper.selectById(teachplanId);
            // 将参数复制到teachplan
            BeanUtils.copyProperties(saveTeachplanDto, teachplan);
            teachplanMapper.updateById(teachplan);
        }
    }

三、删除课程计划

1.接口定义

    /**
     * 删除课程计划
     * @param id 课程计划id
     */
    @ApiOperation("删除课程计划")
    @DeleteMapping("/teachplan/{id}")
    public void removeTeachplan(@PathVariable Long id) {
        teachplanService.removeTeachplan(id);
    }

2.service开发

需求:
        删除第一级别的大章节时要求大章节下边没有小章节时方可删除。
        删除第二级别的小章节的同时需要将teachplan_media表关联的信息也删除。

    /**
     * 根据课程计划id,查询此节点下子节点的数量
     * @param parentId
     * @return
     */
    private int getSonCount(Long parentId) {
        LambdaQueryWrapper<Teachplan> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper = queryWrapper.eq(Teachplan::getParentid, parentId);
        return teachplanMapper.selectCount(queryWrapper);
    }
    /**
     * 删除课程计划
     * @param id 课程id
     */
    @Transactional
    @Override
    public void removeTeachplan(Long id) {
        // 1.根据id查出teachplan
        Teachplan teachplan = teachplanMapper.selectById(id);
        if (teachplan == null) { // 判断数据是否存在,避免空指针
            XueChengPlusException.cast(CommonError.OBJECT_NULL);
        }

        // 2.是大章节 - 判断有没有小章节,没有直接删;有则抛出自定义异常
        if (teachplan.getParentid() == 0) { // 是大章节
            // 查询有没有小章节
            int count = getSonCount(id);
            if (count > 0) { // 有小章节 不可删除
                XueChengPlusException.cast("课程计划信息还有子级信息,无法操作", "120409");
            }
            teachplanMapper.deleteById(id);
            return;
        }

        // 3.是小章节 - 删除小节与其对应在teachplan_media表中的数据
        teachplanMapper.deleteById(id);
        LambdaQueryWrapper<TeachplanMedia> mediaQueryWrapper = new LambdaQueryWrapper<>();
        mediaQueryWrapper = mediaQueryWrapper.eq(TeachplanMedia::getTeachplanId, id);
        teachplanMediaMapper.delete(mediaQueryWrapper);
    }

四、向上/向下移动课程计划

1.思路分析:

因为向上/向下两个方法的思路基本一致,因此在service中共用一个方法实现。

整体思路:先进行边界值判断,上移时如果为第一个或者下移时是最后一个,则无操作直接结束;如果不是边界值,那么找到当前teachplan相邻的上一个或者下一个teachplan进行orderBy值交换,实现上下移动。

具体思路:

  1. 上移还是下移:首先在controller中调用service的时候,我们多传一个flag用于标记是向上移动还是向下移动。
  2. 边界值判断:上移:查询与当前teachplan同级的节点的orderBy的最大值,如果当前的teachplan的orderBy是最小值,那么它就是第一个,则无操作直接返回;下移同理(orderBy为最大值)。
  3. 找到与当前teachplan的相邻teachplan:根据前端所传的teachplanId,查出parientId与courseId,从而找出与当前teachplan同级的所有节点数据,按升序排列放入list集合,遍历集合找出当前teachplan的元素下标,即可获取顺序排列后的相邻的上一个或者下一个teachplan数据。

2.接口定义

    /**
     * 向上移动课程计划
      * @param id 课程计划id
     */
    @ApiOperation("向上移动课程计划")
    @PostMapping("/teachplan/moveup/{id}")
    public void moveUpTeachplan(@PathVariable Long id) {
        teachplanService.moveDownOrUpTeachplan(id, true);
    }

    /**
     * 向下移动课程计划
     * @param id 课程计划id
     */
    @ApiOperation("向下移动课程计划")
    @PostMapping("/teachplan/movedown/{id}")
    public void moveDownTeachplan(@PathVariable Long id) {
        teachplanService.moveDownOrUpTeachplan(id, false);
    }

3.service开发

    @Transactional
    @Override
    public void moveDownOrUpTeachplan(Long id, boolean flag) {
        // 1.数据校验
        Teachplan teachplan = teachplanMapper.selectById(id);
        if (teachplan == null) { // 判断数据是否存在,避免空指针
            XueChengPlusException.cast(CommonError.OBJECT_NULL);
        }

        // 2.拿到当前节点同级数据的集合与它所对应的下标 - 用于获取顺序相邻的节点数据用于orderBy交换
        Map<String, Object> map = getListAndIndex(teachplan);
        List<Teachplan> teachplanList = (List<Teachplan>) map.get("list");
        int index = (int) map.get("index");

        // 3.上移或者下移
        if (flag) { // 上移
            int minOrderBy = teachplanMapper.selectMinOrderBy(teachplan.getParentid(), teachplan.getCourseId());
            if (teachplan.getOrderby() == minOrderBy) { // 是第一个,不做操作直接返回
                return;
            }
            Teachplan preTeachplan = teachplanList.get(index - 1); // 上一个节点
            exchangeOrderBy(teachplan, preTeachplan); // 交换orderBy
        } else {
            int maxOrderBy = teachplanMapper.selectMaxOrderBy(teachplan.getParentid(), teachplan.getCourseId());
            if (teachplan.getOrderby() == maxOrderBy) { // 是最后一个,不做操作直接返回
                return;
            }
            Teachplan nextTeachplan = teachplanList.get(index + 1); // 下一个节点
            exchangeOrderBy(teachplan, nextTeachplan); // 交换orderBy
        }
    }

    /**
     * 交换两个teachplan的orderBy
     * @param teachplan 当前节点
     * @param otherTeachplan 上一个或者下一个节点
     */
    public void exchangeOrderBy(Teachplan teachplan, Teachplan otherTeachplan) {
        Integer orderBy = teachplan.getOrderby();
        teachplan.setOrderby(otherTeachplan.getOrderby());
        otherTeachplan.setOrderby(orderBy);
        teachplanMapper.updateById(teachplan);
        teachplanMapper.updateById(otherTeachplan);
    }
    /**
     * 获取当前节点同级节点的list集合以及其元素下标 - 用于获取上一个或者下一个节点数据
     * @param teachplan 课程计划
     * @return Map<String, Object>
     */
    private Map<String, Object> getListAndIndex(Teachplan teachplan) {
        // 1.获取当前同级节点的list集合
        // 根据parengId与courseId通过orderBy字段排序获取当前级的所有List<Teachplan> 升序排序
        LambdaQueryWrapper<Teachplan> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper = queryWrapper
                .eq(Teachplan::getCourseId, teachplan.getCourseId())
                .eq(Teachplan::getParentid, teachplan.getParentid())
                .orderByAsc(Teachplan::getOrderby);
        List<Teachplan> teachplanList = teachplanMapper.selectList(queryWrapper);

        // 2.获取当前操作节点的元素下标 - 用于获取与其相邻的上一个或下一个元素
        // 根据list下标索引获取当前节点的下一个节点,再进行orderBy交换即可
        int index; // 记录当前
        for (index = 0; index < teachplanList.size(); index++) {
            if (teachplanList.get(index).getId().longValue() == teachplan.getId().longValue()) {
                break;
            }
        }

        // 3.封装数据返回
        Map<String, Object> map = new HashMap<>();
        map.put("list", teachplanList);
        map.put("index", index);
        return map;
    }

4.mapper开发

    /**
     * 查询同一级节点中orderBy的最大值
     * @param parentId 父节点id
     * @param courseId 课程id
     * @return
     */
    Integer selectMaxOrderBy(@Param("parentId") Long parentId, @Param("courseId") Long courseId);

    /**
     * 查询同一级节点中orderBy的最小值
     * @param parentId 父节点id
     * @param courseId 课程id
     * @return
     */
    Integer selectMinOrderBy(@Param("parentId") Long parentId, @Param("courseId") Long courseId);
    <!-- 查询同一级节点中orderBy的最大值 -->
    <select id="selectMaxOrderBy" resultType="java.lang.Integer">
        select max(orderby)
        from teachplan
        where parentid=#{parentId} and course_id=#{courseId}
    </select>

    <!-- 查询同一级节点中orderBy的最小值 -->
    <select id="selectMinOrderBy" resultType="java.lang.Integer">
        select min(orderby)
        from teachplan
        where parentid=#{parentId} and course_id=#{courseId}
    </select>

五、师资管理

1.查询教师

1.1接口定义

根据响应结果的结构可知,返回值应为List<CourseTeacher> 

@Api(value = "课程计划教师编辑接口", tags = "课程计划教师编辑接口")
@RestController
public class CourseTeacherController {

    @Autowired
    private CourseTeacherService courseTeacherService;

    /**
     * 查询教师信息接口
     * @param id 课程id
     * @return List<CourseTeacher>
     */
    @ApiOperation("查询教师信息")
    @GetMapping("/courseTeacher/list/{id}")
    public List<CourseTeacher> getTeacherInfo(@PathVariable Long id) {
        List<CourseTeacher> list = courseTeacherService.getTeacherInfo(id);
        return list;
    }
}

2.2service开发

    /**
     * 查询课程教师
     * @param courseId 课程id
     * @return List<CourseTeacher>
     */
    @Override
    public List<CourseTeacher> getTeacherInfo(Long courseId) {
        LambdaQueryWrapper<CourseTeacher> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper = queryWrapper.eq(CourseTeacher::getCourseId, courseId);
        List<CourseTeacher> courseTeachers = courseTeacherMapper.selectList(queryWrapper);
        return courseTeachers;
    }

2.新增/修改教师

老师的文档上写的新增和修改的请求方式不一样,应该是两个接口,但是实际前端还是用的一个接口,因此新增和修改共用一个接口。

2.1接口定义

    /**
     * 新增/修改教师接口
     * @param courseTeacher 模型类
     * @return CourseTeacher
     */
    @ApiOperation("新增/修改教师")
    @PostMapping("/courseTeacher")
    public CourseTeacher saveCourseTeacher(@RequestBody CourseTeacher courseTeacher) {
        // TODO 当前登录机构ID
        Long companyId = 1232141425L;
        return courseTeacherService.saveCourseTeacher(companyId, courseTeacher);
    }

2.2service开发

    /**
     * 新增/修改教师接口
     * @param courseTeacher 模型类
     * @return CourseTeacher
     */
    @Override
    public CourseTeacher saveCourseTeacher(Long companyId, CourseTeacher courseTeacher) {
        // 1.判断当前登录机构是否为本课程机构
        CourseBase courseBase = courseBaseMapper.selectById(courseTeacher.getCourseId());
        if (ObjectUtils.isEmpty(courseBase)) { // 判断对象是否为空
            XueChengPlusException.cast(CommonError.OBJECT_NULL);
        }
        if (companyId.longValue() != courseBase.getCompanyId().longValue()) { // 机构不一致
            XueChengPlusException.cast("不能操作其它机构课程");
        }

        // 2.新增/修改教师信息
        Long courseTeacherId = courseTeacher.getId();
        if (courseTeacherId == null) { // 新增
            courseTeacher.setCreateDate(LocalDateTime.now());
            int insert = courseTeacherMapper.insert(courseTeacher);
            if (insert <= 0) {
                XueChengPlusException.cast(CommonError.UNKOWN_ERROR);
            }
        } else { // 修改
            int update = courseTeacherMapper.updateById(courseTeacher);
            if (update <= 0) {
                XueChengPlusException.cast(CommonError.UNKOWN_ERROR);
            }
        }

        return courseTeacher;
    }

3.删除教师信息

3.1接口定义

    /**
     * 删除教师信息
     * @param courseId 课程id
     * @param id 教师id
     */
    @ApiOperation("删除教师")
    @DeleteMapping("/courseTeacher/course/{courseId}/{id}")
    public void removeCourseTeacher(@PathVariable Long courseId, @PathVariable Long id) {
        // TODO 当前登录机构ID
        Long companyId = 1232141425L;
        courseTeacherService.removeCourseTeacher(courseId, id, companyId);
    }

3.2service开发

    /**
     * 删除教师信息
     * @param courseId 课程id
     * @param id       教师id
     */
    @Override
    public void removeCourseTeacher(Long courseId, Long id, Long companyId) {
        // 1.判断是否为当前登录机构
        CourseBase courseBase = courseBaseMapper.selectById(courseId);
        if (ObjectUtils.isEmpty(courseBase)) { // 判断对象是否为空
            XueChengPlusException.cast(CommonError.OBJECT_NULL);
        }
        if (companyId.longValue() != courseBase.getCompanyId().longValue()) { // 机构不一致
            XueChengPlusException.cast("不能操作其它机构课程");
        }

        // 2.删除本课程中此教师信息
        courseTeacherMapper.deleteById(id);
    }

六、删除课程

注意:这部分是写在CourseBaseInfoController的不要写错辣!!!!!

1.接口开发

    /**
     * 删除课程:基本信息、营销信息、课程计划、课程教师信息
     * @param id
     */
    @ApiOperation("删除课程")
    @DeleteMapping("/course/{id}")
    public void removeCourseBase(@PathVariable Long id) {
        // TODO 获取用户所属机构id
        Long companyId = 1232141425L;
        courseBaseInfoService.deleteCourseBase(companyId, id);
    }

2.service开发

    /**
     * 删除课程:基本信息、营销信息、课程计划、课程教师信息
     * @param companyId 机构id
     * @param courseId 课程id
     */
    @Transactional
    @Override
    public void deleteCourseBase(Long companyId, Long courseId) {
        // 1.机构校验
        CourseBase courseBase = courseBaseMapper.selectById(courseId);
        if (courseBase == null) {
            XueChengPlusException.cast("课程不存在");
        }
        if (!companyId.equals(courseBase.getCompanyId())) {
            XueChengPlusException.cast("本机构只能操作本机构的课程");
        }

        // 2.删除基本信息
        courseBaseMapper.deleteById(courseId);

        // 3.删除对应的营销信息
        courseMarketMapper.deleteById(courseId);

        // 4.删除对应的所有课程计划
        LambdaQueryWrapper<Teachplan> teachplanWrapper = new LambdaQueryWrapper<>();
        teachplanWrapper = teachplanWrapper.eq(Teachplan::getCourseId, courseId);
        teachplanMapper.delete(teachplanWrapper);

        // 5.删除对应的课程教师信息
        LambdaQueryWrapper<CourseTeacher> courseTeacherWrapper = new LambdaQueryWrapper<>();
        courseTeacherWrapper = courseTeacherWrapper.eq(CourseTeacher::getCourseId, courseId);
        courseTeacherMapper.delete(courseTeacherWrapper);
    }

如果有需要改进的地方,请评论区留言哦


http://www.kler.cn/news/367732.html

相关文章:

  • OCR应用之集装箱箱号自动识别技术,原理与应用
  • centos安装指定版本的jenkins
  • 102. UE5 GAS RPG 实现范围技能奥术伤害
  • 三种材料的金相图及金相图解析材料
  • logback日志导入使用
  • 基于信号分解和多种深度学习结合的上证指数预测模型
  • 机器学习 - 树结构1 - 随机森林
  • Spring Cloud --- Sentinel 规则持久化
  • YOLO11改进 | 卷积模块 | 卷积模块替换为选择性内核SKConv【附完整代码一键运行】
  • 设计模式概览
  • 手机拍证件照,换正装有领衣服及底色的方法
  • jenkins配置邮件通知
  • Flutter控制台提示setState() or markNeedsBuild() called during build错误
  • Linux中DNS搭建
  • GenAI 生态系统现状:不止大语言模型和向量数据库
  • Day 8 UE5c++
  • React实现购物车功能
  • 川渝地区软件工程考研择校分析
  • Pulsar mq 设置延迟消息模式 pulsar mq 发送延迟消息 pulsar如何发送消费延时消息
  • Django+MySQL接口开发完全指南
  • 深入解析 MySQL 数据库:数据库备份机制
  • list补充
  • ESP32C3的 USB 串行/JTAG 控制器
  • AListFlutter(手机alist)——一键安装,可在手机/电视上运行并挂载各个网盘
  • springboot-springboot官方文档架构
  • Android 判断手机放置的方向