组态王如何做网站链接,百度免费官网入口,牛推网,小红书seo排名规则内容管理模块-课程分类、新增课程、修改课程 文章目录 内容管理模块-课程分类、新增课程、修改课程一、课程分类1.1 课程分类表1.2 查询树形结构1.2.1 表自连接1.2.2 SQL递归 1.3 Mapper1.4 Service1.5 Controller1.6 效果图 二、添加课程2.1 需求分析2.2 数据表2.2.1 课程基础…内容管理模块-课程分类、新增课程、修改课程 文章目录 内容管理模块-课程分类、新增课程、修改课程一、课程分类1.1 课程分类表1.2 查询树形结构1.2.1 表自连接1.2.2 SQL递归 1.3 Mapper1.4 Service1.5 Controller1.6 效果图 二、添加课程2.1 需求分析2.2 数据表2.2.1 课程基础信息表2.2.2 课程营销信息表 2.3 交互类2.3.1 AddCourseDto2.3.2 CourseBaseInfoDto 2.3 Mapper2.4 Service2.5 Controller2.6 效果图 三、修改课程3.1 需求分析3.2 数据回显3.2.1 Service类3.2.2 Controller类 3.3 修改课程3.3.1 修改课程Dto3.3.2 Service3.3.3 Controller 一、课程分类
点击“添加课程”之后随便选一个“课程形式” 然后有一个“课程分类”我们下面就要实现课程分类这个地方缺少一个课程分类的下拉框 1.1 课程分类表
典型的树形分类结构 Data
TableName(course_category)
public class CourseCategory implements Serializable {private static final long serialVersionUID 1L;/*** 主键*/private String id;/*** 分类名称*/private String name;/*** 分类标签默认和名称一样*/private String label;/*** 父结点id第一级的父节点是0自关联字段id*/private String parentid;/*** 是否显示*/private Integer isShow;/*** 排序字段*/private Integer orderby;/*** 是否叶子*/private Integer isLeaf;}创建一个Dto方便之后向前端响应课程分类表数据
Data
public class CourseCategoryTreeDto extends CourseCategory implements Serializable {//Serializable:在网络传输需要序列化的时候需要实现Serializable接口private static final long serialVersionUID 2950235607890841126L;//下级节点ListCourseCategoryTreeDto childrenTreeNodes;}展示出来是下面这种格式
{
id : 1-2,
isLeaf : null,
isShow : null,
label : 移动开发,
name : 移动开发,
orderby : 2,
parentid : 1,
childrenTreeNodes : [{childrenTreeNodes : null,id : 1-2-1,isLeaf : null,isShow : null,label : 微信开发,name : 微信开发,orderby : 1,parentid : 1-2}}
1.2 查询树形结构
1.2.1 表自连接
假如说数据层级比较固定而且数据层级比较少可以使用表自连接的方式
select one.id one_id, one.label one_label, two.id two_id,two.label two_label,three.id three_id,three.label three_label
from course_category one -- one是表的别名表示一级分类inner join course_category two -- two是表的别名表示二级分类on two.parentid one.id -- 子节点的parentid是父节点的id
inner join course_category threeon three.parentid two.id查询结果 1.2.2 SQL递归
灵活的方式实现树形表的查询比如使用MYSQL递归实现使用with语法 递归时MySQL8之后才有的 递归语法
WITH [RECURSIVE]cte_name [(col_name [, col_name] ...)] AS (subquery)[, cte_name [(col_name [, col_name] ...)] AS (subquery)]
cte_name :公共表达式的名称,可以理解为表名,用来表示as后面跟着的子查询
col_name :公共表达式包含的列名,可以写也可以不写 有一个关键字RECURSIVE就是递归的含义 cte_name相当于表的一个别名 (col_name [, col_name] …)]是表中的哪些字段 示例代码
-- t1J就是一个虚拟表
with RECURSIVE t1 AS
(
-- 这个t1表的初始数据就是1SELECT 1 as n-- 将下面查询出的数据结果集放入t1虚拟表中UNION ALL-- 下面是递归查询的内容SELECT n 1 FROM t1 WHERE n 5
)-- 查询最终结果
SELECT * FROM t1;下面查询树形结果的SQL,向下递归 向下递归先拿一级节点拿到一级节点后找二级节点拿到二级节点后找三级节点… with RECURSIVE t1 AS(
-- 初始数据就认为是根节点select * from course_category as p where id 1-- 每递归一次就把数据放入t1
union all-- 由树根找叶子
select t2.*
from course_category as t2
INNER JOIN t1ON t2.parentid t1.id -- 当我们拿到id为1的结点递归后就可以拿到1-1等子结点的结果集
-- 当我们拿到id为1-1等结点后递归后就可以拿到1-1-1等子结点结果集
-- .......
)select * from t1向上递归 向上递归拿到最下一级的节点后找次下及节点… 由子节点找父节点 with RECURSIVE t1 AS(
-- 初始数据就认为是根节点select * from course_category as p where id 1-1-1-- 每递归一次就把数据放入t1
union all-- 由树根找叶子
select t2.*
from course_category as t2
INNER JOIN t1ON t2.id t1.parentid -- 当我们拿到id为1的结点递归后就可以拿到1-1等子结点的结果集
-- 当我们拿到id为1-1等结点后递归后就可以拿到1-1-1等子结点结果集
-- .......
)select * from t1mysql为了避免无限递归默认递归次数为1000可以通过设置cte_max_recursion_depth参数增加递归深度还可以通过max_execution_time限制执行时间超过此时间也会终止递归操作。 mysql递归相当于在存储过程中执行若干次sql语句java程序仅与数据库建立一次链接执行递归操作所以只要控制好递归深度控制好数据量性能就没有问题。 1.3 Mapper
//使用递归查询分类
public ListCourseCategoryTreeDto selectTreeNodes(Param(id) String id);!--查询课程分类--select idselectTreeNodes resultTypecom.xuecheng.content.model.dto.CourseCategoryTreeDtowith RECURSIVE t1 AS (select *from course_category as pwhere id #{id}union allselect t2.*from course_category as t2INNER JOIN t1ON t2.parentid t1.id)select *from t1order by t1.id, t1.orderby/select我们现在从数据库中查到的数据是下列这个模样 代码中查询出来的数据如下所示 1.4 Service
要将Mapper层返回的数据进行进一步的处理。
将根节点id1舍弃不要因为在业务上没什么需要了
我们要将子节点放入到父节点的childrenTreeNodes集合里面。比如将1-1-x的节点放入到1-1节点的childrenTreeNodes集合里面
Slf4j
Service
public class CourseCategoryServiceImpl implements CourseCategoryService {Autowiredprivate CourseCategoryMapper courseCategoryMapper;Overridepublic ListCourseCategoryTreeDto queryTreeNodes(String id) {//TODO 数据库递归查询出课程分类信息ListCourseCategoryTreeDto courseCategoryTreeDtos courseCategoryMapper.selectTreeNodes(id);//TODO 找到每个节点的子节点最终封装成ListCourseCategoryTreeDto//将list转map,以备使用,排除根节点MapString, CourseCategoryTreeDto mapTemp courseCategoryTreeDtos.stream()//!id.equals(item.getId()) 含义就是排除根节点.filter(item - !id.equals(item.getId())).collect(//转Map是需要一个key一个value的//第一个key是代表元素的意思key - key.getId()是拿到key元素的id然后充当Map的key//value表示对象的本身所以不需要任何的处理//(key1, key2) - key2 表示当key重复的时候键相同以后来的key为主Collectors.toMap(key - key.getId(), value - value, (key1, key2) - key2));//最终返回的listListCourseCategoryTreeDto categoryTreeDtos new ArrayList();//依次遍历每个元素,排除根节点//courseCategoryTreeDtos是从数据库查询出来的全部的数据courseCategoryTreeDtos.stream().filter(item - !id.equals(item.getId())).forEach(item - {if (item.getParentid().equals(id)) {//紧挨根节点下的节点categoryTreeDtos.add(item);}//找到当前节点的父节点CourseCategoryTreeDto courseCategoryTreeDto mapTemp.get(item.getParentid());if (courseCategoryTreeDto ! null) {if (courseCategoryTreeDto.getChildrenTreeNodes() null) {courseCategoryTreeDto.setChildrenTreeNodes(new ArrayListCourseCategoryTreeDto());}//下边开始往ChildrenTreeNodes属性中放子节点courseCategoryTreeDto.getChildrenTreeNodes().add(item);}});return categoryTreeDtos;}}1.5 Controller
/*** 课程分类相关接口*/
Slf4j
RestController
public class CourseCategoryController {Autowiredprivate CourseCategoryService courseCategoryService;GetMapping(/course-category/tree-nodes)public ListCourseCategoryTreeDto queryTreeNodes() {return courseCategoryService.queryTreeNodes(1);}}1.6 效果图 二、添加课程
2.1 需求分析 将来这些信息都会存储到“course_base”表中 点击“添加课程”选项 点击添加课程选择课程形式为录播 选择完毕点击下一步进入课程基本信息添加界面
本界面分两部分信息一部分是课程基本信息上一部分是课程营销信息。
课程基本信息 课程营销信息 下面的信息会存储在course_market表中 也就是说一个表单中的数据要存储到两张表中
在这个界面中填写课程的基本信息、课程营销信息上。
填写完毕保存并进行下一步
2.2 数据表
course_base、course_market两张表存储
两张表是一对一的关系一个课程只有一个营销信息
并且两张表的主键id是相同的
2.2.1 课程基础信息表
course_base /*** 课程基本信息*/
Data
TableName(course_base)
public class CourseBase implements Serializable {private static final long serialVersionUID 1L;/*** 主键*/TableId(value id, type IdType.AUTO)private Long id;/*** 机构ID*/private Long companyId;/*** 机构名称*/private String companyName;/*** 课程名称*/private String name;/*** 适用人群*/private String users;/*** 课程标签*/private String tags;/*** 大分类*/private String mt;/*** 小分类*/private String st;/*** 课程等级*/private String grade;/*** 教育模式(common普通record 录播live直播等*/private String teachmode;/*** 课程介绍*/private String description;/*** 课程图片*/private String pic;/*** 创建时间*/TableField(fill FieldFill.INSERT)private LocalDateTime createDate;/*** 修改时间*/TableField(fill FieldFill.INSERT_UPDATE)private LocalDateTime changeDate;/*** 创建人*/private String createPeople;/*** 更新人*/private String changePeople;/*** 审核状态*/private String auditStatus;/*** 课程发布状态 未发布 已发布 下线*/private String status;
}
2.2.2 课程营销信息表
course_market /*** 课程营销信息*/
Data
TableName(course_market)
public class CourseMarket implements Serializable {private static final long serialVersionUID 1L;/*** 主键课程id*/private Long id;/*** 收费规则对应数据字典*/private String charge;/*** 现价*/private Float price;/*** 原价*/private Float originalPrice;/*** 咨询qq*/private String qq;/*** 微信*/private String wechat;/*** 电话*/private String phone;/*** 有效期天数*/private Integer validDays;}
2.3 交互类
2.3.1 AddCourseDto
价格信息存Float是没有问题的但是计算的时候是有问题的我们要使用BigDecimal
/*** description 添加课程dto*/
Data
ApiModel(value AddCourseDto, description 新增课程基本信息)
public class AddCourseDto {NotEmpty(message 课程名称不能为空)ApiModelProperty(value 课程名称, required true)private String name;NotEmpty(message 适用人群不能为空)Size(message 适用人群内容过少, min 10)ApiModelProperty(value 适用人群, required true)private String users;ApiModelProperty(value 课程标签)private String tags;NotEmpty(message 课程分类不能为空)ApiModelProperty(value 大分类, required true)private String mt;NotEmpty(message 课程分类不能为空)ApiModelProperty(value 小分类, required true)private String st;NotEmpty(message 课程等级不能为空)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;NotEmpty(message 收费规则不能为空)ApiModelProperty(value 收费规则对应数据字典, required true)private String charge;ApiModelProperty(value 价格)private BigDecimal price;ApiModelProperty(value 原价)private BigDecimal originalPrice;ApiModelProperty(value qq)private String qq;ApiModelProperty(value 微信)private String wechat;ApiModelProperty(value 电话)private String phone;ApiModelProperty(value 有效期)private Integer validDays;
}2.3.2 CourseBaseInfoDto
添加成功之后我们查询出来的课程的详细信息
/*** 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;}2.3 Mapper
/*** 课程基本信息 Mapper 接口*/
public interface CourseBaseMapper extends BaseMapperCourseBase {}/*** 课程营销信息 Mapper 接口*/
public interface CourseMarketMapper extends BaseMapperCourseMarket {}2.4 Service
2.5 Controller ApiOperation(新增课程)PostMapping(/course)public CourseBaseInfoDto createCourseBase(Validated RequestBody AddCourseDto addCourseDto) {
// 将来会集成SpringSecurity框架用户登录之后就可以获取到用户所属机构的ID
// 先把机构ID写死return courseBaseInfoService.createCourseBase(10086L,addCourseDto);}2.6 效果图 之后点击保存这个时候会报错一个404因为后面课程大纲的内容还没有编写
再查看数据库 再通过页面查看一下还是挺带劲的 三、修改课程
涉及的表也是course_base课程基本信息表、course_market课程营销表
其实就是比添加课程多了一个数据回显而已
3.1 需求分析
点击编辑按钮就可以修改课程信息 要修改的表单内容其实是和添加课程时的表单是一个样子的
然后这个地方点击编辑的时候要做一个数据回显 修改课程的请求数据只是比添加课程的请求数据多了一个课程id而已
但是没有营销信息 3.2 数据回显 3.2.1 Service类
这个方法之前其实写过 Overridepublic CourseBaseInfoDto getCourseBaseInfo(Long courseId){//TODO 查询课程基本信息CourseBase courseBase courseBaseMapper.selectById(courseId);if(courseBase null){return null;}//TODO 查询课程营销信息CourseMarket courseMarket courseMarketMapper.selectById(courseId);CourseBaseInfoDto courseBaseInfoDto new CourseBaseInfoDto();BeanUtils.copyProperties(courseBase,courseBaseInfoDto);if(courseMarket ! null){BeanUtils.copyProperties(courseMarket,courseBaseInfoDto);}//TODO 查询分类名称是哪一级的CourseCategory courseCategoryBySt courseCategoryMapper.selectById(courseBase.getSt());//小分类courseBaseInfoDto.setStName(courseCategoryBySt.getName());//小分类名称CourseCategory courseCategoryByMt courseCategoryMapper.selectById(courseBase.getMt());//大分类courseBaseInfoDto.setMtName(courseCategoryByMt.getName());//大分类名称return courseBaseInfoDto;}3.2.2 Controller类 ApiOperation(根据课程id查询接口)GetMapping(/course/{courseId})public CourseBaseInfoDto getCourseBaseById(PathVariable(courseId) Long courseId) {return courseBaseInfoService.getCourseBaseInfo(courseId);}3.3 修改课程
3.3.1 修改课程Dto
修改课程的请求数据只比增加课程的请求数据多一个课程id
/*** 修改课程Dto*/
Data
ApiModel(value EditCourseDto,description 修改课程基本信息)
public class EditCourseDto extends AddCourseDto{ApiModelProperty(value 课程id,required true)private Long id;
}
3.3.2 Service /*** 修改课程** param companyId 机构id后面做认证收取那使用* param editCourseDto 要修改的课程信息* return 修改之后的课程详细信息*/Overridepublic CourseBaseInfoDto updateCourseBase(Long companyId, EditCourseDto editCourseDto) {//TODO 课程基本信息//获取到课程idLong courseId editCourseDto.getId();//查询课程CourseBase courseBase courseBaseMapper.selectById(courseId);if (courseBase null) {XueChengPlusException.cast(课程不存在);}//数据合法性校验//根据具体的业务逻辑进行校验 - 本机构只能修改本机构的课程if (!companyId.equals(courseBase.getCompanyId())) {XueChengPlusException.cast(本机构只能修改本机构的课程);}//封装数据BeanUtils.copyProperties(editCourseDto, courseBase);//修改时间courseBase.setChangeDate(LocalDateTime.now());//更新数据库int i courseBaseMapper.updateById(courseBase);if (i0){XueChengPlusException.cast(修改课程失败);}//TODO 更新课程营销信息CourseMarket courseMarketNew new CourseMarket();BeanUtils.copyProperties(editCourseDto, courseMarketNew);int count courseMarketMapper.updateById(courseMarketNew);if (count 0) {throw new RuntimeException(更新课程营销信息失败);}//查询课程信息CourseBaseInfoDto courseBaseInfo getCourseBaseInfo(courseId);return courseBaseInfo;}3.3.3 Controller ApiOperation(修改课程接口)PutMapping(/course)public CourseBaseInfoDto getCourseBaseById(RequestBody EditCourseDto editCourseDto) {//机构id先写死后面授权认证的时候后会改过来Long companyId 1232141425L;return courseBaseInfoService.updateCourseBase(companyId,editCourseDto);}