#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 teachplanwhere parentid=#{parentId} and course_id=#{courseId}
</select>
(2)TeachplanServiceImpl
将原来的新增部分改为如下所示:
/*** 新增/修改/保存课程计划* @param saveTeachplanDto 模型类*/@Overridepublic void saveTeachplan(SaveTeachplanDto saveTeachplanDto) {// 通过课程计划id判断是新增和修改Long teachplanId = saveTeachplanDto.getId();if (teachplanId == null) {// 新增Teachplan teachplan = new Teachplan();BeanUtils.copyProperties(saveTeachplanDto, teachplan);// 确定排序字段 - 找到同级节点个数,排序字段就是当前节点下的子节点数量加一 问题:当节点进行删除操作后,存在bugLong 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);// 将参数复制到teachplanBeanUtils.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@Overridepublic void removeTeachplan(Long id) {// 1.根据id查出teachplanTeachplan 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值交换,实现上下移动。
具体思路:
- 上移还是下移:首先在controller中调用service的时候,我们多传一个flag用于标记是向上移动还是向下移动。
- 边界值判断:上移:查询与当前teachplan同级的节点的orderBy的最大值,如果当前的teachplan的orderBy是最小值,那么它就是第一个,则无操作直接返回;下移同理(orderBy为最大值)。
- 找到与当前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@Overridepublic 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 teachplanwhere parentid=#{parentId} and course_id=#{courseId}</select><!-- 查询同一级节点中orderBy的最小值 --><select id="selectMinOrderBy" resultType="java.lang.Integer">select min(orderby)from teachplanwhere parentid=#{parentId} and course_id=#{courseId}</select>
五、师资管理
1.查询教师
1.1接口定义
根据响应结果的结构可知,返回值应为List<CourseTeacher>
@Api(value = "课程计划教师编辑接口", tags = "课程计划教师编辑接口")
@RestController
public class CourseTeacherController {@Autowiredprivate 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>*/@Overridepublic 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 当前登录机构IDLong companyId = 1232141425L;return courseTeacherService.saveCourseTeacher(companyId, courseTeacher);}
2.2service开发
/*** 新增/修改教师接口* @param courseTeacher 模型类* @return CourseTeacher*/@Overridepublic 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 当前登录机构IDLong companyId = 1232141425L;courseTeacherService.removeCourseTeacher(courseId, id, companyId);}
3.2service开发
/*** 删除教师信息* @param courseId 课程id* @param id 教师id*/@Overridepublic 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 获取用户所属机构idLong companyId = 1232141425L;courseBaseInfoService.deleteCourseBase(companyId, id);}
2.service开发
/*** 删除课程:基本信息、营销信息、课程计划、课程教师信息* @param companyId 机构id* @param courseId 课程id*/@Transactional@Overridepublic 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);}
如果有需要改进的地方,请评论区留言哦