学成在线项目笔记第三期

删除课程计划

需求分析

  • 课程计划添加成功,如果课程还没有提交时可以删除课程计划。

  • 删除第一级别的章时要求章下边没有小节方可删除。

  • 删除第二级别的小节的同时需要将其它关联的视频信息也删除。

  • 删除课程计划需要传输课程计划的id。

接口定义

删除课程计划的接口示例如下:

1
2
3
4
5
6
7
Request URL: /content/teachplan/246
Request Method: DELETE

如果失败:
{"errCode":"120409","errMessage":"课程计划信息还有子级信息,无法操作"}

如果成功:状态码200,不返回信息

接口开发

model

model层直接用之前开发的DTO即可,无需添加新类

api-接口定义

在课程计划teachplan的controller中添加接口

1
2
3
4
@DeleteMapping("/teachplan/{id}")
public List<TeachplanDto> deleteTeachplan(@PathVariable Long id){
return teachplanService.deleteTeachplan(id);
}

service-接口开发

创建service接口

在teachplan的service中创建接口:

1
2
3
4
5
6
7
/**
* @description 删除课程计划信息
* @param id teachplanId
* @author xiaoming
* @date 2023/1/30 13:01
*/
public List<TeachplanDto> deleteTeachplan(Long id);

创建service实现类impl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Transactional
@Override
public List<TeachplanDto> deleteTeachplan(Long id) {
Teachplan teachplan = teachplanMapper.selectById(id);
Long courseId = teachplan.getCourseId();
if (teachplan == null) {
throw new XueChengException("无法找到该章节");
}

LambdaQueryWrapper<Teachplan> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Teachplan::getParentid, id);
Integer count = teachplanMapper.selectCount(queryWrapper);
if (count > 0) {
throw new XueChengException("存在子章节,无法删除该章节");
}

Long teachplanId = teachplan.getId();
LambdaQueryWrapper<TeachplanMedia> query = new LambdaQueryWrapper<>();
query.eq(TeachplanMedia::getTeachplanId, teachplanId);
Integer mediaCount = teachplanMediaMapper.selectCount(query);
if (mediaCount > 0) {
teachplanMediaMapper.delete(query);
}
teachplanMapper.deleteById(teachplanId);
return teachplanMapper.selectTreeNodes(courseId);
}

接口测试

首先使用httpclient测试

1
2
### 删除课程计划
DELETE {{content_host}}/content/teachplan/43

分以下情况测试:

  1. 删除有子课程计划的计划。

  2. 删除没有子课程计划的计划。

课程计划排序

需求分析

课程计划新增后默认排在同级别最后,课程计划排序功能是可以灵活调整课程计划的显示顺序

接口定义

向下移动

1
2
3
Request URL: http://localhost:8601/api/content/teachplan/movedown/43
Request Method: POST
43为课程计划id

向上移动

1
2
3
Request URL: http://localhost:8601/api/content/teachplan/moveup/43
Request Method: POST
43为课程计划id
  • 上移表示将课程计划向上移动。

  • 下移表示将课程计划向下移动。

  • 向上移动表示和上边的课程计划交换位置,将两个课程计划的排序字段值交换。

    • 到达最上面应该没有反映,因为已经在最上面了
  • 向下移动表示和下边的课程计划交换位置,将两个课程计划的排序字段值交换。

    • 到达最下面应该没有反映,因为已经到最下面了

接口开发

model

没有新的配置,略

api-接口定义

在课程计划teachplan的controller中添加接口

1
2
3
4
5
6
7
8
9
@PostMapping("/teachplan/moveup/{id}")
public void moveup(@PathVariable Long id){
teachplanService.moveup(id);
}

@PostMapping("/teachplan/movedown/{id}")
public void movedown(@PathVariable Long id){
teachplanService.movedown(id);
}

service-接口开发

创建service接口

在teachplan的service中创建接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* @description 上移
* @param id teachplanId
* @author xiaoming
* @date 2023/1/30 13:02
*/
public void moveup(Long id);

/**
* @description 下移
* @param id teachplId
* @author xiaoming
* @date 2023/1/30 13:03
*/
public void movedown(Long id);

创建service实现类impl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
@Override
public void moveup(Long id) {
Teachplan teachplan = teachplanMapper.selectById(id);
Long parentid = teachplan.getParentid();
Integer targetOrderby = teachplan.getOrderby();
if (targetOrderby == 1) {
throw new XueChengException("已经是第一个了,无法继续上移");
}
LambdaQueryWrapper<Teachplan> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Teachplan::getParentid, parentid);
queryWrapper.lt(Teachplan::getOrderby, targetOrderby);
Integer count = teachplanMapper.selectCount(queryWrapper);
if (count >= 1) {
queryWrapper.eq(Teachplan::getOrderby, targetOrderby - 1);
Teachplan preTeachplan = teachplanMapper.selectOne(queryWrapper);
preTeachplan.setOrderby(preTeachplan.getOrderby() + 1);
teachplanMapper.updateById(preTeachplan);
teachplan.setOrderby(targetOrderby - 1);
}
teachplanMapper.updateById(teachplan);
}

@Override
public void movedown(Long id) {
Teachplan teachplan = teachplanMapper.selectById(id);
Long parentid = teachplan.getParentid();
Integer targetOrderby = teachplan.getOrderby();
LambdaQueryWrapper<Teachplan> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Teachplan::getParentid, parentid);
queryWrapper.gt(Teachplan::getOrderby, targetOrderby);
Integer count = teachplanMapper.selectCount(queryWrapper);
if (count == 0) {
throw new XueChengException("已经是最后一个了,无法继续下移");
} else {
queryWrapper.eq(Teachplan::getOrderby, targetOrderby + 1);
Teachplan preTeachplan = teachplanMapper.selectOne(queryWrapper);
preTeachplan.setOrderby(preTeachplan.getOrderby() - 1);
teachplanMapper.updateById(preTeachplan);
teachplan.setOrderby(targetOrderby + 1);
}
teachplanMapper.updateById(teachplan);
}

接口测试

这个功能直接前后端联调,这个可能立即看到 效果。

向上移动测试

  1. 先找一个上边有课程计划的进行测试,向上移动后两个交换顺序。

  2. 再找最上边的课程计划向上移动,应该没有反映才对,因为已经在最上边了。

向下移动测试

  1. 先找一个下边有课程计划的进行测试,向下移动后两个交换顺序。

  2. 再找最下边的课程计划向下移动,应该没有反映才对,因为已经在最下边了。

师资管理

完成以实战内容需要将课程资料下的course-add-step3-teacher.vue文件 覆盖 project-xczx2-portal-
vue-ts\src\module-organization\pages\course-manage\course-add-step3-teacher.vue

需求分析

  • 课程计划界面点击下一步进入教师管理界面:

  • 点击添加教师打开添加界面,如下图,不用实现上传照片。

  • 添加成功查询教师信息,

  • 点击编辑,修改教师信息

  • 点击删除,删除指定的教师信息。

注意:只允许向机构自己的课程中添加老师、删除老师。

机构id统一使用:1232141425L

接口定义

查询教师接口请求示例

1
2
3
4
5
6
get /courseTeacher/list/75
75为课程id,请求参数为课程id

响应结果
[{"id":23,"courseId":75,"teacherName":"张老师","position":"讲师","introduction":"张老师教师简介张老师教师简介张老师教师简介张老师教师简
介","photograph":null,"createDate":null}]

添加教师请求示例

1
2
3
4
5
6
7
8
9
10
post /courseTeacher
请求参数:
{
"courseId": 75,
"teacherName": "王老师",
"position": "教师职位",
"introduction": "教师简介"
}
响应结果:
{"id":24,"courseId":75,"teacherName":"王老师","position":"教师职位","introduction":"教师简介","photograph":null,"createDate":null}

修改教师

1
2
3
4
5
6
7
8
9
10
11
12
13
post /courseTeacher
请求参数:
{
"id": 24,
"courseId": 75,
"teacherName": "王老师",
"position": "教师职位",
"introduction": "教师简介",
"photograph": null,
"createDate": null
}
响应:
{"id":24,"courseId":75,"teacherName":"王老师","position":"教师职位","introduction":"教师简介","photograph":null,"createDate":null}

删除教师

1
2
3
4
5
6
delete /ourseTeacher/course/75/26
75:课程id
26:教师id,即course_teacher表的主键

请求参数:课程id、教师id
响应:状态码200,不返回信息

接口开发

师资管理功能开发全部都在 content 工程中进行

model

直接使用mybatis生成的po类即可,略

api-接口定义

根据接口定义,在 content-api中创建新的controller,创建接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
package com.xuecheng.contentApi.controller;

import com.xuecheng.contentModel.po.CourseTeacher;
import com.xuecheng.contentService.service.CourseTeacherService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.util.List;

/**
* @author xioaming
* @version 1.0
* @description 课程教师接口类
* @date 2023/1/30 15:32
*/
@RestController
public class CourseTeacherController {

@Autowired
CourseTeacherService courseTeacherService;


/**
* @description 查询课程老师
* @param courseId 课程id
* @return com.xuecheng.contentModel.po.CourseTeacher
* @author xiaoming
* @date 2023/1/30 15:50
*/
@GetMapping("/courseTeacher/list/{courseId}")
public List<CourseTeacher> selectCourseTeacher(@PathVariable Long courseId){
return courseTeacherService.getCourseTeacherInfo(courseId);
}

/**
* @description 添加/修改课程老师
* @param teacher 教师信息
* @return com.xuecheng.contentModel.po.CourseTeacher
* @author xiaoming
* @date 2023/1/30 15:51
*/
@PostMapping("/courseTeacher")
public CourseTeacher saveCourseTeacher(@RequestBody @Validated CourseTeacher teacher){
//机构id,由于认证系统没有上线暂时硬编码
Long companyId = 1232141425L;
return courseTeacherService.saveCourseTeacher(teacher);
}


/**
* @description 删除课程老师
* @param courseId 课程id
* @param id 教师id
* @author xiaoming
* @date 2023/1/30 16:02
*/
@DeleteMapping("/courseTeacher/course/{courseId}/{id}")
public void deleteCourseTeacher(@PathVariable Long courseId, @PathVariable Long id){
courseTeacherService.deleteCourseTeacher(courseId, id);
}
}

service-接口开发

创建service接口

在courseteacher的service中创建接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package com.xuecheng.contentService.service;

import com.xuecheng.contentModel.po.CourseTeacher;

import java.util.List;

/**
* @author xioaming
* @version 1.0
* @description 课程教师信息业务类
* @date 2023/1/30 15:34
*/
public interface CourseTeacherService {

/**
* @description 查询课程老师信息
* @param courseId 课程id
* @return com.xuecheng.contentModel.po.CourseTeacher
* @author xiaoming
* @date 2023/1/30 21:10
*/
public List<CourseTeacher> getCourseTeacherInfo(Long courseId);


/**
* @description 新增、修改教师信息
* @param teacher 教师信息传输类
* @return com.xuecheng.contentModel.po.CourseTeacher
* @author xiaoming
* @date 2023/1/30 21:20
*/
public CourseTeacher saveCourseTeacher(CourseTeacher teacher);

/**
* @description 删除教师信息
* @param courseId 课程id
* @param id 教师信息表id
* @author xiaoming
* @date 2023/1/30 21:57
*/
public void deleteCourseTeacher(Long courseId, Long id);
}

创建service实现类impl

实现 courseteacherservice 的业务类impl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
package com.xuecheng.contentService.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.xuecheng.contentModel.po.CourseTeacher;
import com.xuecheng.contentService.mapper.CourseTeacherMapper;
import com.xuecheng.contentService.service.CourseTeacherService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;

/**
* @author xioaming
* @version 1.0
* @description 课程教师业务实现类
* @date 2023/1/30 15:34
*/
@Service
public class CourseTeacherServiceImpl implements CourseTeacherService {

@Autowired
CourseTeacherMapper courseTeacherMapper;

@Override
public List<CourseTeacher> getCourseTeacherInfo(Long courseId) {
LambdaQueryWrapper<CourseTeacher> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(CourseTeacher::getCourseId, courseId);
CourseTeacher teacher = courseTeacherMapper.selectOne(queryWrapper);
List<CourseTeacher> list = new ArrayList<>();
if (teacher == null){
list.add(new CourseTeacher());
}else {
list.add(teacher);
}
return list;
}

@Transactional
@Override
public CourseTeacher saveCourseTeacher(CourseTeacher teacher) {
Long teacherCourseId = teacher.getCourseId();
LambdaQueryWrapper<CourseTeacher> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(CourseTeacher::getCourseId, teacherCourseId);
Integer count = courseTeacherMapper.selectCount(queryWrapper);
if (count < 1){
CourseTeacher courseTeacher = new CourseTeacher();
BeanUtils.copyProperties(teacher, courseTeacher);
courseTeacherMapper.insert(courseTeacher);
return getCourseTeacher(courseTeacher);
}
CourseTeacher courseTeacher = courseTeacherMapper.selectOne(queryWrapper);
Long id = courseTeacher.getId();
BeanUtils.copyProperties(teacher, courseTeacher);
courseTeacher.setId(id);
courseTeacherMapper.updateById(courseTeacher);
Long courseId = courseTeacher.getCourseId();
LambdaQueryWrapper<CourseTeacher> query = new LambdaQueryWrapper<>();
query.eq(CourseTeacher::getCourseId, courseId);
return courseTeacherMapper.selectOne(query);
}

private CourseTeacher getCourseTeacher(CourseTeacher courseTeacher) {
Long courseId = courseTeacher.getCourseId();
LambdaQueryWrapper<CourseTeacher> query = new LambdaQueryWrapper<>();
query.eq(CourseTeacher::getCourseId, courseId);
return courseTeacherMapper.selectOne(query);
}

@Transactional
@Override
public void deleteCourseTeacher(Long courseId, Long id) {
courseTeacherMapper.deleteById(id);
}
}

说明:

  1. 查询教师接口,前端必须要获取到值才能展示页面,对于自己新增的课程,第一次进入肯定是没有老师信息的,所以我是先创建一个空的教师信息,使前端能获取到然后展示到页面,再将这个空的教师信息在调用新增教师信息接口时用新的值覆盖掉,如果你有更好的方法可以自己改哦
  2. 添加教师接口 和 修改教师接口 前端调用的是同一个url,所以要写成一个,在后端进行判断是新增还是修改,但这里存在一个问题就是:这么写一个课程就只能存在一个老师,虽然也满足要求,但是后期可能会改动一下

接口测试

前后端联调,HTTPClient,swapper都行

删除课程

完成以实战内容需要将课程资料下的course-list.vue文件 覆盖 project-xczx2-portal-vue-
ts\src\module-organization\pages\course-manage\course-list.vue

需求分析

  • 课程的审核状态为未提交时方可删除。

  • 删除课程需要删除课程相关的基本信息、营销信息、课程计划、课程教师信息。

接口定义

1
2
3
4
5
delete /course/
87 为课程id

请求参数:课程id
响应:状态码 200 ,不返回信息

接口开发

删除课程,是在content工程的 courseBase中进行接口开发

model

api-接口定义

在 courseBaseInfoController 中 添加接口:

1
2
3
4
@DeleteMapping("/course/{courseId}")
public void deleteCourseBase(@PathVariable @Validated Long courseId){
courseBaseInfoService.deleteCourseBase(courseId);
}

service-接口开发

创建service接口

在courseBase的service中创建接口:

1
2
3
4
5
6
7
/**
* @description 删除课程信息
* @param courseId 课程信息
* @author xiaoming
* @date 2023/1/31 13:11
*/
public void deleteCourseBase(Long courseId);

创建service实现类impl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Transactional
@Override
public void deleteCourseBase(Long courseId) {
LambdaQueryWrapper<CourseBase> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(CourseBase::getId, courseId);
CourseBase courseBase = courseBaseMapper.selectOne(queryWrapper);
String status = courseBase.getStatus();
if (!status.equals("203001")) {
throw new XueChengException("课程未处于未提交状态,无法删除");
}
//courseteacher 找courseid再删
LambdaQueryWrapper<CourseTeacher> query3 = new LambdaQueryWrapper<>();
query3.eq(CourseTeacher::getCourseId, courseId);
courseTeacherMapper.delete(query3);

//teachplanmedia 找courseid再删
LambdaQueryWrapper<TeachplanMedia> query2 = new LambdaQueryWrapper<>();
query2.eq(TeachplanMedia::getCourseId, courseId);
teachplanMediaMapper.delete(query2);

//teachplan 找courseid再删
LambdaQueryWrapper<Teachplan> query1 = new LambdaQueryWrapper<>();
query1.eq(Teachplan::getCourseId, courseId);
teachplanMapper.delete(query1);

//coursebase 删id
//coursemarket 删id
Long id = courseBase.getId();
courseMarketMapper.deleteById(id);
courseBaseMapper.deleteById(id);
}

这里有点憨,从后往前删了。。。不过问题不大

接口测试

直接前后端联调,删自己创建的课程,检查数据库多张表(基本信息、营销信息、课程计划、课程教师信息)的删除情况