添加课程
界面原型
第一步: 用户进入课程查询列表,点击添加课程
按钮,选择课程类型是直播还是录播,课程类型不同那么授课方式也不同
- 添加的课程和教学机构是一对一的关系
第二步: 用户选完课程形式后,点击下一步
填写课程的基本信息和营销信息(两张表)
- 用户只要填完课程信息就会把数据保存到数据库中,可以
在课程管理中查看对应的课程发布信息
,当审核状态为通过时发布按钮
点亮
第三步: 填写课程计划信息
即课程的大纲目录包括章节和小节
,每个小节需要上传课程视频,用户点击小节标题即可开始播放视频
,如果是直播课程则会进入直播间
第四步: 填写课程师资信息
数据模型
添加课程涉及到的数据表课程基本信息表
课程查询功能涉及到的数据表课程计划信息表
请求/响应模型(model工程)
第一步: 根据请求参数定义对应的请求模型类
,添加课程的初始审核状态为未提交
,初始发布状态为未发布
// 新增课程
POST {{content_host}}/content/course
Content-Type: application/json
{
// 机构名称和Id根据当前登陆的用户获取,以下信息对应课程基本信息表
"mt": "",大分类
"st": "",小分类
"name": "",课程名称
"pic": "",课程图片
"teachmode": "200002",教育模式
"users": "初级人员",适用人群
"tags": "",课程标签
"grade": "204001",课程等级
"description": "",课程介绍
//以下信息对应课程营销表
"charge": "201000",收费规则
"price": 0,现价
"originalPrice":0,原价
"qq": "",咨询QQ
"wechat": "",微信
"phone": "",电话
"validDays": 365,有效期天数
}
package com.xuecheng.content.model.dto;
/**
* @version 1.0
* @description 添加课程dto
*/
@Data
@ApiModel(value = "AddCourseDto", description = "新增课程基本信息")
public class AddCourseDto {
@ApiModelProperty(value = "课程名称", required = true)
private String name;
@ApiModelProperty(value = "适用人群", required = true)
private String users;
@ApiModelProperty(value = "课程标签")
private String tags;
@ApiModelProperty(value = "大分类", required = true)
private String mt;
@ApiModelProperty(value = "小分类", required = true)
private String st;
@ApiModelProperty(value = "课程等级", required = true)
private String grade;
@ApiModelProperty(value = "教学模式(普通,录播,直播等)", required = true)
private String teachmode;
@ApiModelProperty(value = "课程介绍")
private String description;
@ApiModelProperty(value = "课程图片", required = true)
private String pic;
@ApiModelProperty(value = "收费规则,对应数据字典", required = true)
private String charge;
@ApiModelProperty(value = "价格")
private Float price;
@ApiModelProperty(value = "原价")
private Float originalPrice;
@ApiModelProperty(value = "qq")
private String qq;
@ApiModelProperty(value = "微信")
private String wechat;
@ApiModelProperty(value = "电话")
private String phone;
@ApiModelProperty(value = "有效期")
private Integer validDays;
}
第二步: 根据响应结果定义对应的响应模型类
,由于其大部分信息来自课程基本信息表
,所以我们可以将定义的响应结果模型类继承CourseBase
{
"id": 109,
"companyId": 1,
"companyName": null,
"name": "测试课程103",
"users": "初级人员",
"tags": "",
"mt": "1-1",
"mtName": null,大分类名称,
"st": "1-1-1",
"stName": null,小分类名称
"grade": "204001",
"teachmode": "200002",
"description": "",
"pic": "",
"createDate": "2022-09-08 07:35:16",
"changeDate": null,
"createPeople": null,
"changePeople": null,
"auditStatus": "202002",
"status": 1,
"coursePubId": null,
"coursePubDate": null,
"charge": "201000",
"price": null,
"originalPrice":0,
"qq": "",
"wechat": "",
"phone": "",
"validDays": 365
}
package com.xuecheng.content.model.dto;
/**
* @version 1.0
* @description 课程基本信息dto
*/
@Data
public class CourseBaseInfoDto extends CourseBase {
/**
* 收费规则,对应数据字典
*/
private String charge;
/**
* 价格
*/
private Float price;
/**
* 原价
*/
private Float originalPrice;
/**
* 咨询qq
*/
private String qq;
/**
* 微信
*/
private String wechat;
/**
* 电话
*/
private String phone;
/**
* 有效期天数
*/
private Integer validDays;
/**
* 大分类名称
*/
private String mtName;
/**
* 小分类名称
*/
private String stName;
}
接口定义(api工程)
@ApiOperation("新增课程基础信息接口")
@PostMapping("/course")
public CourseBaseInfoDto createCourseBase(@RequestBody AddCourseDto addCourseDto) {
// 获取机构id(添加的课程和教学机构是一对一的关系),暂时以硬编码的方式指定机构Id后期通过当前登陆的教学机构获取
Long companyId = 22L;
return courseBaseInfoService.createCourseBase(companyId, addCourseDto);
}
业务开发(service工程)
第一步: 编写Mapper接口及其SQL映射文件
public interface CourseBaseMapper extends BaseMapper<CourseBase> {
}
public interface CourseCategoryMapper extends BaseMapper<CourseCategory> {
//使用递归查询分类
public List<CourseCategoryTreeDto> selectTreeNodes(String id);
}
public interface CourseMarketMapper extends BaseMapper<CourseMarket> {
}
第二步: 编写Service接口及其实现类, 在业务逻辑代码中实现添加课程的效果,由于操作两张表所以需要开启事务
- 首先
对请求参数做合法性校验
(前端后端都要校验,Service和Controller层都要校验), 即判断一下用户是否输入了表单必填项以及设置一些参数的默认值 - 然后对请求参数进行封装并调用数据表对应的Mapper接口进行数据持久化,
课程基本信息和课程营销信息的Id相同
- 最后组装要返回给前端的结果信息
public interface CourseBaseInfoService {
/**
* 新增课程基本信息
* @param companyId 教学机构id
* @param addCourseDto 课程基本信息
* @return
*/
CourseBaseInfoDto createCourseBase(Long companyId, AddCourseDto addCourseDto);
}
@Slf4j
@Service
public class CourseBaseInfoServiceImpl implements CourseBaseInfoService {
@Resource
CourseBaseMapper courseBaseMapper;
@Resource
CourseMarketMapper courseMarketMapper;
@Resource
CourseCategoryMapper courseCategoryMapper;
@Override
// 操作两张表需要开启事务
@Transactional
public CourseBaseInfoDto createCourseBase(Long companyId, AddCourseDto addCourseDto) {
// 1. 合法性校验
if (StringUtils.isBlank(addCourseDto.getName())) {
throw new RuntimeException("课程名称为空");
}
if (StringUtils.isBlank(addCourseDto.getMt())) {
throw new RuntimeException("课程分类为空");
}
if (StringUtils.isBlank(addCourseDto.getSt())) {
throw new RuntimeException("课程分类为空");
}
if (StringUtils.isBlank(addCourseDto.getGrade())) {
throw new RuntimeException("课程等级为空");
}
if (StringUtils.isBlank(addCourseDto.getTeachmode())) {
throw new RuntimeException("教育模式为空");
}
if (StringUtils.isBlank(addCourseDto.getUsers())) {
throw new RuntimeException("适应人群为空");
}
if (StringUtils.isBlank(addCourseDto.getCharge())) {
throw new RuntimeException("收费规则为空");
}
// 2. 将请求参数中包含的课程基本信息封装到对应的实体类对象当中
CourseBase courseBase = new CourseBase();
BeanUtils.copyProperties(addCourseDto, courseBase);
// 2.1 设置默认审核状态(去数据字典表中查询状态码)
courseBase.setAuditStatus("202002");
// 2.2 设置默认发布状态
courseBase.setStatus("203001");
// 2.3 设置机构id
courseBase.setCompanyId(companyId);
// 2.4 设置添加时间
courseBase.setCreateDate(LocalDateTime.now());
// 2.5 向课程基本信息表中插入一条记录
int baseInsert = courseBaseMapper.insert(courseBase);
// 3.获取刚添加记录的课程Id
Long courseId = courseBase.getId();
// 4. 将请求参数中包含的课程营销信息封装到对应的实体类对象当中
CourseMarket courseMarket = new CourseMarket();
BeanUtils.copyProperties(addCourseDto, courseMarket);
// 设置课程营销信息的Id即我们刚添加的课程基本信息
courseMarket.setId(courseId);
// 4.1 判断收费规则,若课程收费,则价格必须大于0
String charge = courseMarket.getCharge();
if ("201001".equals(charge)) {
// 价格可以使用Float类型存储,计算的时候使用BigDecimal计算
Float price = addCourseDto.getPrice();
if (price == null || price.floatValue() <= 0) {
throw new RuntimeException("课程设置了收费,价格不能为空,且必须大于0");
}
}
// 4.2 向课程营销信息表中插入一条记录
int marketInsert = courseMarketMapper.insert(courseMarket);
// 判断课程基本信息和营销信息是否插入成功
if (baseInsert <= 0 || marketInsert <= 0) {
throw new RuntimeException("新增课程基本信息失败");
}
// 5.根据插入课程基本信息/课程营销信息的Id,去课程基本信息表和课程营销信息表中查询刚添加的记录,组装成CourseBaseInfoDto对象返回给前端
return getCourseBaseInfo(courseId);
}
}
第三步: 根据刚插入课程基本信息/课程营销信息的Id
,去课程基本信息表和课程营销信息表中查询刚添加的记录,组装成CourseBaseInfoDto对象
返回给前端
private CourseBaseInfoDto getCourseBaseInfo(Long courseId) {
// 创建返回的Dto对象
CourseBaseInfoDto courseBaseInfoDto = new CourseBaseInfoDto();
// 1. 根据课程id查询课程基本信息
CourseBase courseBase = courseBaseMapper.selectById(courseId);
if (courseBase == null)
return null;
// 1.1 拷贝属性
BeanUtils.copyProperties(courseBase, courseBaseInfoDto);
// 2. 根据课程id查询课程营销信息
CourseMarket courseMarket = courseMarketMapper.selectById(courseId);
// 2.1 拷贝属性
if (courseMarket != null)
BeanUtils.copyProperties(courseMarket, courseBaseInfoDto);
// 3. 查询课程分类名称并封装到到CourseBaseInfoDto对象中
// 3.1 根据小分类id(1-1-1)查询对应的课程分类对象
CourseCategory courseCategoryBySt = courseCategoryMapper.selectById(courseBase.getSt());
// 3.2 设置课程的小分类名称
courseBaseInfoDto.setStName(courseCategoryBySt.getName());
// 3.3 根据大分类id(1-1)查询对应的课程分类对象
CourseCategory courseCategoryByMt = courseCategoryMapper.selectById(courseBase.getMt());
// 3.4 设置课程大分类名称
courseBaseInfoDto.setMtName(courseCategoryByMt.getName());
return courseBaseInfoDto;
}
接口测试
使用HTTP Client
测试新增课程接口
POST {{content_host}}/content/course
Content-Type: application/json
{
"mt": "1-1",
"st": "1-1-1",
"name": "测试课程tmp",
"pic": "",
"teachmode": "200002",
"users": "初级人员",
"tags": "",
"grade": "204001",
"description": "这是一门测试课程",
"charge": "201000",
"price": 99,
"originalPrice": 999,
"qq": "123564",
"wechat": "123654",
"phone": "156213",
"validDays": 365
}