今天咱们做课程的预览功能
背景:
我们经常浏览电商网站,电商网站浏览量最大的页面是什么?商品检索列表、商品详情。对于在线教育网站,课程就是商品。因此,课程详情页的访问流量是巨大的。因此,除了上面的功能性需求,本章我们还需要满足一个非功能性需求:高性能课程详情页访问。
需要的表
为什么要形成课程发布信息呢?
课程发布信息代表着一次课程输出的最终确定。后续的业务,如课程购买、学习将围绕着该课程发布信息,从业务角度该信息不可轻易修改,只读(若修改,需要重新走审核流程)。
课程业务表与发布内容相分离,课程业务信息是经常发生变动的,而课程发布信息不会轻易修改,因此形成业务级别的读写分离,两者互不影响。
Freemarker
背景:
这里我想让大家想一件事情,做课程预览这功能大家最初的想法是什么?最简单的想法:查询数据库然后将数据显示到页面,每次访问都会先查询数据库,但是这种方法在一个页面有大量的数据时(微服务大部分压力在数据库)查询多个表,数据显示会很慢,影响用户的体验。
然后呢?我们怎么优化,使用redis?可以,但是还是不够快,对于一个几乎所有数据都不怎么修改的页面,其实我们可以使用freemark模板语言,生成静态html页面,然后将页面存储到阿里云或者minio中,用户点击预览实际上是访问的静态页面,而无需查询庞大的数据库,访问速度才是最快。
freeMark官网
FreeMarker 是一款 模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本(HTML网页,电子邮件,配置文件,源代码等)的通用工具。 它不是面向最终用户的,而是一个Java类库,是一款程序员可以嵌入他们所开发产品的组件。
模板编写为FreeMarker Template Language (FTL)。它是简单的,专用的语言, 不像PHP那样成熟的编程语言。 那就意味着要准备数据在真实编程语言中来显示,比如数据库查询和业务运算, 之后模板显示已经准备好的数据。在模板中,你可以专注于如何展现数据, 而在模板之外可以专注于要展示什么数据。
- freemarker并不关心数据的来源,只是根据模板的内容,将数据模型在模板中显示并输出文件(通常为html,也可以生成其它格式的文本文件)
流程:
freemarker作为springmvc一种视图格式,默认情况下SpringMVC支持freemarker视图格式。
需要创建Spring Boot+Freemarker工程用于测试模板。
在线预览:freemarker作为springmvc一种页面。在浏览器展示
浏览器发起请求---->SpringMVC---->执行Controller----->定义返回值(模板地址)---->模板中绑定数据---->浏览器展示
freeMark快速入门
这里我就简单介绍一下,要想详细了解大家可以进我发官网去看看
以下内容直接粘贴看看效果就行了,不做强制要求
导入依赖
<!-- 添加springboot 父类依赖 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.2.RELEASE</version>
</parent>
<dependencies>
<!--添加 spring boot web 依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<!-- lombok使用 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
<scope>provided</scope>
</dependency>
<!-- apache 对 java io 的封装工具库 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
</dependency>
</dependencies>
配置application.yml配置
spring:
freemarker:
#开启 freemarker 功能
enabled: true
#关闭模板缓存,方便测试
cache: false
settings:
template_update_delay: 0
#页面模板后缀名
suffix: .ftl
charset: UTF-8
#页面模板位置(默认为 classpath:/templates/)
template-loader-path: classpath:/templates/
resources:
#关闭项目中的静态资源映射(static、resources文件夹下的资源)
add-mappings: false
创建模型类;
package com.xuecheng.freemarker.entity;
import lombok.Data;
import lombok.ToString;
import java.util.Date;
import java.util.List;
@Data
@ToString
public class Student {
private String name;//姓名
private int age;//年龄
private Date birthday;//生日
private Float money;//钱包
}
模拟controller层:
package com.xuecheng.test.freemarker.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.client.RestTemplate;
import java.util.Map;
@Controller
public class HelloController {
@GetMapping("basic")
public String test(Model model) {
//1.纯文本形式的参数
model.addAttribute("name", "freemarker");
//2.实体类相关的参数
Student student = new Student();
student.setName("小明");
student.setAge(18);
model.addAttribute("stu", student);
return "01-basic";
}
}
创建模板
在resources下创建templates,此目录为freemarker的默认模板存放目录。
在templates下创建模板文件 01-basic.ftl ,模板中的插值表达式最终会被freemarker替换成具体的数据。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Hello World!</title>
</head>
<body>
<b>普通文本 String 展示: Hello ${name}</b><br><br>
<br>
<hr>
<b>对象Student中的数据展示:${stu}</b><br/>
姓名:${stu.name}<br/>
年龄:${stu.age}
<hr>
</body>
</html>
支持EL表达式
freemarker基础
页面上的指令,语法规则。
基础语法种类:
1.注释,即<#-- -->,介于其之间的内容会被freemarker忽略
<#--我是一个freemarker注释-->
2、插值(Interpolation):即 ${..} 部分,freemarker会用真实的值代替${..}
Hello ${name}
3、FTL指令:和HTML标记类似,名字前加#予以区分,Freemarker会解析标签中的表达式或逻辑。
<#>FTL指令</#>
@GetMapping("/list")
public String list(Model model) {
//响应数据
//2.1 小强对象模型数据
Student stu1 = new Student();
stu1.setName("小强");
stu1.setAge(18);
stu1.setMoney(1000.86f);
//2.2 小红对象模型数据
Student stu2 = new Student();
stu2.setName("小红");
stu2.setMoney(200.1f);
stu2.setAge(19);
//2.3 将两个对象模型数据存放到List集合中
List<Student> stus = new ArrayList<>();
stus.add(stu1);
stus.add(stu2);
model.addAttribute("stus",stus);
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Hello World!</title>
</head>
<body>
<b>展示list中的stu数据:</b>
<br>
<br>
<#--list 数据的展示-->
<b>展示list中的stu数据:</b>
<#list stus as s>
name:${s.name},
age:${s.age},
money:${s.money},
</#list>
<hr>
</body>
</html>
4、文本,仅文本信息,这些不是freemarker的注释、插值、FTL指令的内容会被freemarker忽略解析,直接输出内容.
<#--freemarker中的普通文本-->
我是一个普通的文本
集合指令(List和Map)
@GetMapping("/list")
public String list(Model model) {
//响应数据
//2.1 小强对象模型数据
Student stu1 = new Student();
stu1.setName("小强");
stu1.setAge(18);
stu1.setMoney(1000.86f);
//2.2 小红对象模型数据
Student stu2 = new Student();
stu2.setName("小红");
stu2.setMoney(200.1f);
stu2.setAge(19);
//2.3 将两个对象模型数据存放到List集合中
List<Student> stus = new ArrayList<>();
stus.add(stu1);
stus.add(stu2);
model.addAttribute("stus",stus);
//3. 创建Map数据
Map<String,Student> stuMap = new HashMap<>();
stuMap.put("stu1",stu1);
stuMap.put("stu2",stu2);
model.addAttribute("stuMap", stuMap);
//返回模板地址
return "02-collection";
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Hello World!</title>
</head>
<body>
<b>展示list中的stu数据:</b>
<br>
<br>
<#--list 数据的展示-->
<b>展示list中的stu数据:</b>
<#list stus as s>
name:${s.name},
age:${s.age},
money:${s.money},
</#list>
<hr>
<b>展示map中的数据:</b>
<#--获取map中某个key下的数据:map.key.属性-->
stu1的name:${stuMap.stu1.name}<br>
stu2的name:${stuMap.stu2.name}<br>
<#--遍历map集合,获取map中的所有数据
语法:[map]?keys as key -- 循环map集合获取获取每个key
-->
<hr>
遍历map集合 <br>
<#--<#list stuMap?keys as key>-->
<#-- ${stuMap[key].name} -- ${stuMap[key].age} <br>-->
<#--</#list>-->
<#list stuMap? keys as key>
${stuMap[key].name} ---- ${stuMap[key].age}-----${stuMap[key].money}<br>
</#list>
</body>
</html>
${k_index}:
index:得到循环的下标,使用方法是在stu后边加"_index",它的值是从0开始
if指令
if 指令即判断指令,是常用的FTL指令,freemarker在解析时遇到if会进行判断,条件为真则输出if中间的内容,否则跳过内容不再输出。
<#--格式-->
<#if condition>
...
<#elseif condition2>
...
<#elseif condition3>
...
...
<#else>
...
</#if>
package com.example;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.client.RestTemplate;
import java.util.*;
@Controller
public class HelloController {
@GetMapping("basic")
public String test(Model model) {
//1.纯文本形式的参数
model.addAttribute("name", "freemarker");
//2.实体类相关的参数
Student student = new Student();
student.setName("小明");
student.setAge(18);
model.addAttribute("stu", student);
return "01-basic";
}
@GetMapping("/list")
public String list(Model model) {
//响应数据
//2.1 小强对象模型数据
Student stu1 = new Student();
stu1.setName("小强");
stu1.setAge(18);
stu1.setMoney(1000.86f);
//2.2 小红对象模型数据
Student stu2 = new Student();
stu2.setName("小红");
stu2.setMoney(200.1f);
stu2.setAge(19);
//2.3 将两个对象模型数据存放到List集合中
List<Student> stus = new ArrayList<>();
stus.add(stu1);
stus.add(stu2);
model.addAttribute("stus",stus);
//3. 创建Map数据
Map<String,Student> stuMap = new HashMap<>();
stuMap.put("stu1",stu1);
stuMap.put("stu2",stu2);
model.addAttribute("stuMap", stuMap);
//返回模板地址
return "03-if";
}
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Hello World!</title>
</head>
<body>
<#list stus as s>
<#if s.name='小红'>
<b style="color: red">
${s.name}<br>
</b>
<#elseif s.name='小绿'>
<b style="color: green">
${s.name}<br>
</b>
<#--类似于swich中的default-->
<#else>
${s.name}<br>
</#if>
</#list>
</body>
</html>
空值处理:
Freemarker 在显示数据时,如果遇到了null或空,Freemarker就会在显示页面中报出错误信息:null or missing,
Freemarker中需要对空值间判断,判断后的数据就不会显示错误信息。
判断某变量是否存在使用 “??”
如:为防止stus为空报错可以加上判断如下:
<#if stus??>
<#list stus as stu>
......
</#list>
</#if>
缺失变量默认值使用 “!”**
使用!要以指定一个默认值,当变量为空时显示默认值
如:${name!''}表示如果name为空显示空字符串。
实际例子:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Hello World!</title>
</head>
<body>
<#list stus as s>
<#if s??>
<#if s.name='小红'>
<b style="color: red">
${s.name}<br>
${s.birthday!'2022-8-11'}<br>
</b>
<#elseif s.name='小绿'>
<b style="color: green">
${s.name}<br>
${s.birthday!'2022-8-11'}<br>
</b>
<#else>
${s.name}<br>
${s.birthday!'2022-8-11'}<br>
</#if>
</#if>
</#list>
</body>
</html>
内建函数
内建函数语法格式: 变量+?+函数名称
- 某个集合的大小
<hr>
集合大小:${stus?size}<br>
-
日期格式化
显示年月日:
${today?date}
显示时分秒:${today?time}
显示日期+时间:${today?datetime}
自定义格式化:${today?string("yyyy年MM月")}
Date date = new Date();
model.addAttribute("today",date);
<hr>
日期格式
<br>
显示年月日:${today?date}<br>
显示时分秒:${today?time}<br>
显示日期+时间:${today?datetime}<br>
自定义格式化:${today?string("yyyy/MM月dd日")}
- 内函数
c
model.addAttribute("point", 102920122);
point是数字型,使用${point}会显示这个数字的值,每三位使用逗号分隔。
如果不想显示为每三位分隔的数字,可以使用c函数将数字型转成字符串输出
model.addAttribute("point", 102920122);
<hr>
内函数:<br>
有c:${point?c}<br>
无c:${point}
静态化测试(练习)
之前的测试都是SpringMVC将Freemarker作为视图解析器(ViewReporter)来集成到项目中,工作中,有的时候需要使用Freemarker原生Api来生成静态内容并使用文件来存储静态化后的内容,下面一起来学习下原生Api生成文本文件。
使用freemarker原生Api将页面生成html文件,本节测试html文件生成的方法:
1、根据模板文件生成html文件的字符串
模板文件 + 数据模型 = 静态文件
2、根据模板字符串生成静态文件
模板字符串 + 数据模型 = 静态文件
GeneratorHtml.class
package com.example;
import freemarker.template.Configuration;
import freemarker.template.Template;
import org.apache.commons.io.IOUtils;
import org.junit.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
@SpringBootTest
public class GeneratorHtml {
@Test
public void testHtml() throws Exception {
//1、创建配置类,指定版本信息
Configuration configuration = new Configuration(Configuration.getVersion());
//2、指定模板,字符编码
String path = this.getClass().getResource("/templates").getPath();
configuration.setDirectoryForTemplateLoading(new File(path)); //指定模板文件的文件夹路径
configuration.setDefaultEncoding("utf-8");
//3、获取模板文件
Template template = configuration.getTemplate("01-basic.ftl");
//4、构造数据,必须是一个Map集合
Map data = getData();
//5、根据数据 + 模板文件进行数据填充,得到字符串
String content = FreeMarkerTemplateUtils.processTemplateIntoString(template, data);
//6、将静态化字符串写入到指定文件下
InputStream inputStream = IOUtils.toInputStream(content); //字符串转化为输入流
FileOutputStream outputStream = new FileOutputStream(new File("e:/index.html")); //创建文件
IOUtils.copy(inputStream, outputStream); //输入流写入文件
}
private Map getData() {
Map map = new HashMap();
map.put("name","zjf");
Student student = new Student();
student.setName("王者荣耀");
student.setAge(12);
map.put("student",student);
return map;
}
}
01-basic.ftl
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Hello World!</title>
</head>
<body>
<#--我是一个freemarker注释-->
<b>普通文本 String 展示:</b><br><br>
Hello ${name1!""}<br>
<hr>
<b>对象Student中的数据展示:</b><br/>
姓名:${student.name}<br/>
年龄:${student.age}
<hr>
</body>
</html>
课程预览
课程管理中,教学机构和运营平台都是需要对课程生成的详情页进行预览操作,项目中的课程预览会使用 Freemarker 模板技术来做业务实现,之前已经将 Freemarker 的相关技术学习完后,下面将进行课程预览的相关需求开发。
需求分析
此模块主要针对教育机构及平台运营对课程信息的预览,主要功能包括:
- 教育机构提交审核前预览,教育机构在
课程提交审核
前,往往需要看一下课程最终会被发布成什么样子,是否符合所预期,因此需要提供教学机构课程预览
功能来满足该需求,若符合预期,则进行课程提交审核
操作。 - 平台运营审核过程中预览,平台运营在课程的审核过程中,也需要预览功能来更直观的看到课程内容,因此需要提供
平台运营课程预览
功能来满足该需求。
教育机构用户在课程管理
中可对该机构内所管理的课程进行检索。
课程预览交互流程如下:
步骤描述:
1.前端对某审核通过的课程执行课程发布操作
2.查询课程基本信息、课程营销、课程计划、教师信息并保存到 课程发布中。
3.远程调用系统管理查询课程分类信息
4.保存课程发布信息
5.获得页面的数据模型(课程发布、课程营销、课程计划、教师信息、课程分类等)
6.使用Spring MVC 结果视图器 Freemarker 选择页面模板,生成静态页面
7.将生成的静态页面返回给前端
内容管理开发中,需要调用系统管理服务来完成数据的获取。所以本次开发中,需 要在两个微服务中,分别进行开发,功能如下:
- 系统管理服务:开发根据可曾分类 ID 获得课程分类信息的接口。
- 内容管理服务:开发课程预览的接口。
项目环境搭建
学成在线的课程预览会在 xc-content-service 微服务工程中,进行开发。在开发前,我们需要将其运行环境先构建出来。
- 数据库环境
- Freemarker 环境集成
导入pom.xml依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
yml配置文件
spring:
freemarker:
#开启 freemarker 功能
enabled: true
#关闭模板缓存,方便测试
cache: false
settings:
template_update_delay: 0
#页面模板后缀名
suffix: .ftl
charset: UTF-8
#页面模板位置(默认为 classpath:/templates/)
template-loader-path: classpath:/templates/
resources:
#关闭项目中的静态资源映射(static、resources文件夹下的资源)
add-mappings: false
导入ftl模板
代码太多了,这里我就不具体显示了,需要的私信我
课程分类信息业务实现
在课程预览中,内容管理微服务 远程调用 系统管理服务接口来获得课程分类名称,如下:
下面将在系统管理微服务xc-basic-service中开发此接口。
注意这里我们只需要查询大分类就行,为了去填补mt
YAPI文档接口
代码展示
//根据id 查询分类信息
@GetMapping("/{id}")
public ResponseResult findCategoryById(@PathVariable("id") String id){
Category category = categoryService.getById(id);
CategoryVo categoryVo = BeanHelper.copyProperties(category, CategoryVo.class);
return ResponseResult.okResult(categoryVo);
}
效果展示
使用idea自带的Http client测试以下,点击以下那个小地球(IDEA21版才有,以前的版本,在Tools里面)
抽取feign接口:
由于课程预览功能要调用此功能,不同微服务之间可以使用feign进行练习,所以我们需要抽取feign接口。
引入依赖:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>com.xuecheng</groupId>
<artifactId>xc-framework-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
package com.xuecheng.basic.api;
import com.xuecheng.commons.model.vo.CategoryVo;
import com.xuecheng.commons.model.vo.ResponseResult;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient("xc-basic-service")
public interface BasicClient {
//泛型必须写,不写会报错
@GetMapping("/category/{id}")
ResponseResult<CategoryVo> findCategoryById(@PathVariable("id") String id);
}
在ContentApplication启动类加@EnableFeignClients包扫描
课程预览
YAPI文档:
在xc-content-service引入分接口
<!-- 引入分类feign接口-->
<dependency>
<groupId>com.xuecheng</groupId>
<artifactId>xc-basic-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
需要的表:
代码修复:
需要将 coursePub实体类中的price字段修改为BigDecimal
代码展示:
web层
package com.xuecheng.content.controller;
import com.xuecheng.commons.model.vo.ResponseResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.xuecheng.content.service.CoursePubService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.ModelAndView;
/**
* <p>
* 课程发布前端控制器
* </p>
*
* @author ZJF
*/
@Slf4j
@RestController
@RequestMapping("/coursePub")
public class CoursePubController {
@Autowired
private CoursePubService coursePubService;
//课程发布预览
@GetMapping("/preview/{courseBaseId}")
public ModelAndView previewCourser(@PathVariable("courseBaseId") Long courseBaseId){
return coursePubService.previewCourser(courseBaseId);
}
}
serviceImpl层
package com.xuecheng.content.service.impl;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.xuecheng.basic.api.BasicClient;
import com.xuecheng.commons.enums.ErrorCode;
import com.xuecheng.commons.model.vo.CategoryVo;
import com.xuecheng.commons.model.vo.ResponseResult;
import com.xuecheng.commons.model.vo.TeachplanVo;
import com.xuecheng.commons.utils.BeanHelper;
import com.xuecheng.content.domain.CourseBase;
import com.xuecheng.content.domain.CoursePub;
import com.xuecheng.content.domain.CourseTeacher;
import com.xuecheng.content.mappers.CourseBaseMapper;
import com.xuecheng.content.mappers.CoursePubMapper;
import com.xuecheng.content.mappers.CourseTeacherMapper;
import com.xuecheng.content.service.CoursePubService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xuecheng.content.service.TeachplanService;
import com.xuecheng.web.exception.BusinessException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.ModelAndView;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
/**
* <p>
* 课程发布 服务实现类
* </p>
*
* @author zjf
* @since 2022-07-27
*/
@Service
public class CoursePubServiceImpl extends ServiceImpl<CoursePubMapper, CoursePub> implements CoursePubService {
@Autowired
private BasicClient basicClient;
@Autowired
private TeachplanService teachplanService;
@Autowired
private CourseBaseMapper courseBaseMapper;
@Autowired
private CourseTeacherMapper courseTeacherMapper;
/**
* 获取预览的课程发布数据
* 1、课程信息
* 2、课程老师
* 3、课程计划信息
* 将3部分数据,转化成页面所需要的map集合
* coursePub:课程发布对象数据
* teachplanNode:课程计划数据
*/
@Override
public ModelAndView previewCourser(Long courseBaseId) {
//根据id查询课程基本信息
LambdaQueryWrapper<CourseBase> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(CourseBase::getId, courseBaseId);
CourseBase courseBase = courseBaseMapper.selectOne(wrapper);
if (courseBase == null) {
throw new BusinessException(ErrorCode.NOTFOUND);
}
CoursePub coursePub = BeanHelper.copyProperties(courseBase, CoursePub.class);
//补全信息
coursePub.setCreateDate(new Date());
coursePub.setCourseId(courseBaseId);
String mt = courseBase.getMt();
String mtName = getMtNameOrStName(mt);
String st = courseBase.getSt();
String stName = getMtNameOrStName(st);
//根据id查询教师信息
LambdaQueryWrapper<CourseTeacher> qw = new LambdaQueryWrapper<>();
qw.eq(CourseTeacher::getCourseId,courseBaseId);
List<CourseTeacher> teacherList = courseTeacherMapper.selectList(qw);
//补全信息
coursePub.setTeachers(JSON.toJSONString(teacherList));
coursePub.setMtName(mtName);
coursePub.setStName(stName);
//查询课程结构
ResponseResult<TeachplanVo> rr = teachplanService.treeNodes(courseBaseId);
TeachplanVo teachplanVo = rr.getData();
coursePub.setTeachplan(JSON.toJSONString(teachplanVo));
//使用modelAndView直接转到页面
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("coursePub",coursePub);
modelAndView.addObject("teachplanNode",teachplanVo);
modelAndView.setViewName("learing_article");
return modelAndView;
}
//根据id,调用feign接口查询分类名称
public String getMtNameOrStName(String m){
ResponseResult<CategoryVo> result = basicClient.findCategoryById(m);
CategoryVo categoryVo = result.getData();
if (categoryVo == null) {
throw new BusinessException(ErrorCode.NOTFOUND);
}
String name = categoryVo.getName();
return name;
}
}
效果展示:
点击预览
今天的任务完成,明天做freemarker静态页面的保存