保存发布信息、页面静态化
点击某课程数据后的 发布
链接(审核状态为通过),即可对该课程进行发布
需要使用的表:
YAPI文档:
代码展示:
web层
//保存发布信息
@PostMapping("/publish/{courseBaseId}")
public ResponseResult saveCoursePubMessages(@PathVariable("courseBaseId") Long courseBaseId){
return coursePubService.saveCoursePubMessages(courseBaseId);
}
serviceImpl层
@Autowired
private BasicClient basicClient;
@Autowired
private TeachplanService teachplanService;
@Autowired
private CourseBaseMapper courseBaseMapper;
@Autowired
private CourseTeacherMapper courseTeacherMapper;
@Autowired
private Configuration configuration;
@Autowired
private MinioService minioService;
@Override
@Transactional
public ResponseResult saveCoursePubMessages(Long courseBaseId) {
//是否以前存在课程发布信息
LambdaQueryWrapper<CoursePub> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(CoursePub::getCourseId,courseBaseId);
CoursePub one1 = this.getOne(queryWrapper);
if (one1 !=null){
throw new BusinessException(ErrorCode.ADDEERROR);
}
//查询课程基本信息
CourseBase one = courseBaseMapper.selectById(courseBaseId);
if (one == null) {
throw new BusinessException(ErrorCode.NOTFOUND);
}
one.setAuditStatus(CourseConstants.AUDIT_PUBLISHED_STATUS);
courseBaseMapper.updateById(one);
//查询课程计划信息
LambdaQueryWrapper<Teachplan> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Teachplan::getCourseId, courseBaseId);
List<Teachplan> teachplanList = teachplanService.list(wrapper);
if (teachplanList.size() == 0) {
throw new BusinessException(ErrorCode.NOTFOUND);
}
String teachplanJSON = JSON.toJSONString(teachplanList);
//课程讲师信息
LambdaQueryWrapper<CourseTeacher> qw = new LambdaQueryWrapper<>();
qw.eq(CourseTeacher::getCourseId, courseBaseId);
List<CourseTeacher> courseTeachers = courseTeacherMapper.selectList(qw);
String courseTeachersJSON = "";
if (courseTeachers.size() != 0) {
courseTeachersJSON = JSON.toJSONString(courseTeachers);
}
CoursePub coursePub = BeanHelper.copyProperties(one, CoursePub.class);
//补全信息
coursePub.setCourseId(courseBaseId);
coursePub.setTeachplan(teachplanJSON);
if (StrUtil.isNotBlank(courseTeachersJSON)) {
coursePub.setTeachers(courseTeachersJSON);
}
String mt = one.getMt();
String st = one.getSt();
//根据id,调用feign接口查询mt、st名称
String mtName = getMtNameOrStName(mt);
String stName = getMtNameOrStName(st);
coursePub.setMtName(mtName);
coursePub.setStName(stName);
coursePub.setCreateDate(new Date());
boolean save = this.save(coursePub);
if (!save) {
throw new BusinessException(ErrorCode.COURSEPUBSHRRROR);
}
ResponseResult<TeachplanVo> rr = teachplanService.treeNodes(courseBaseId);
TeachplanVo teachplanVo = rr.getData();
//生成html页面
try {
HashMap<String, Object> map = new HashMap<>();
map.put("coursePub",coursePub);
map.put("teachplanNode",teachplanVo);
Template template = configuration.getTemplate("learing_article.ftl");
String htmlStr = FreeMarkerTemplateUtils.processTemplateIntoString(template, map);
ByteArrayInputStream inputStream = new ByteArrayInputStream(htmlStr.getBytes());
String filename = courseBaseId + ".html";
minioService.uploadFreemarkerHtmlByMinio(inputStream, filename);
} catch (IOException | TemplateException e) {
e.printStackTrace();
System.out.println(e.getMessage());
}
return ResponseResult.okResult();
}
效果展示:
在页面搜索:http://www.xuecheng.com/pages/47.html,查看生成的静态页面
时间问题
#安装ntpdate工具
yum -y install ntp ntpdate
#自动同步时间
ntpdate ntp.ubuntu.com
代码优化
以前是真没在意,我的代码又长又烂,= =现在优化以下
存在的问题:
同步:就是在发出一个调用时,在没有得到结果之前, 该调用就不返回
异步:调用在发出之后,这个调用就直接返回了,没有返回结果
常见的异步处理有两类:
- 在同一应用(微服务)内部:使用多线程
- 设计到夸应用调用(微服务调用):基于消息中间件
@Async是Spring提供的异步处理注解,被此注解标注的方法会在新的线程中执行,其实就相当于我们自己new Thread。此注解使用起来也比较简单。
-
引导类中使用@EnableAsync注解开启异步调用
-
在方法上加上@Async注解(标明要异步调用)
将需要多线程处理的业务逻辑,抽取到一个新的java类中,配置方法。在方法上添加@Async注解
package com.xuecheng.content.service.impl;
import com.xuecheng.basic.api.BasicClient;
import com.xuecheng.content.mappers.CourseBaseMapper;
import com.xuecheng.content.mappers.CourseTeacherMapper;
import com.xuecheng.content.service.MinioService;
import com.xuecheng.content.service.PublishHtmlService;
import com.xuecheng.content.service.TeachplanService;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
@Service
public class PublishHtmlServiceImpl implements PublishHtmlService {
@Autowired
private Configuration configuration;
@Autowired
private MinioService minioService;
@Override
@Async
public void publish(Map map, Long courseBaseId) {
try {
Template template = configuration.getTemplate("learing_article.ftl");
String htmlStr = FreeMarkerTemplateUtils.processTemplateIntoString(template, map);
ByteArrayInputStream inputStream = new ByteArrayInputStream(htmlStr.getBytes());
String filename = courseBaseId + ".html";
minioService.uploadFreemarkerHtmlByMinio(inputStream, filename);
} catch (IOException | TemplateException e) {
e.printStackTrace();
System.out.println(e.getMessage());
}
}
}
修改CoursePubServiceImpl.class
效果演示:
需要删除之前的信息
发送MQ消息
课程发布对于该项目来说是一个核心业务,除了页面生成之外,一般都需要扩展其他操作,这些逻辑若都在课程发布中接入,那课程发布的实现将耦合所有下游业务。且下游模块出现问题,将会影响该核心业务。
- 搜索服务:在门户网站使用ElasticSearch进行搜索,同步es索引
- 短信服务:当订阅了某个教育机构,发布新课程时,会受到课程通知。
存在的问题:
- 耦合度高:每次加入新的需求,都要修改原来的代码
- 性能下降:调用者需要等待服务提供者响应,如果调用链过长则响应时间等于每次调用的时间之和。
- 资源浪费:调用链中的每个服务在等待响应过程中,不能释放请求占用的资源,高并发场景下会极度浪费系统资源
- 级联失败:如果服务提供者出现问题,所有调用方都会跟着出问题,如同多米诺骨牌一样,迅速导致整个微服务群故障
为了解决上诉问题,我们使用RabbitMQ消息中间件来完成。
访问mq:
管理后台地址:http://192.168.136.150:15672/
用户名/密码:guest/guest
和使用Mysql类似,Java代码操作时一般不会使用超级管理员账号
- 一个项目分配一个账号
- 一个项目分配一个虚拟主机
- 分配此账号对此虚拟主机的操作权限
- 本次课程:我们设置的用户名-密码-虚拟机均为xuecheng
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
rabbitmq:
host: 192.168.136.150
virtual-host: xuecheng
username: xuecheng
password: xuecheng
发送消息
CoursePubServiceImpl:
@Autowired
private RabbitTemplate rabbitTemplate;
rabbitTemplate.convertAndSend("content-exchange","course.pub",coursePub.getId());
测试消息
编写监听器:
package com.xuecheng.search.listener;
import cn.hutool.log.Log;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class MessageListener {
@RabbitListener(bindings = @QueueBinding(
value = @Queue(value = "course.index.queue"),
exchange = @Exchange(
value = "content-exchange",
type = ExchangeTypes.TOPIC
),
key = "course.*"
))
public void updateIndex(Long coursePubId){
log.info("获取的课程发布id为:"+coursePubId);
//更新es,下次做
}
}
效果展示:
删除以前的数据,重新发布
今天的任务完成,突然觉得自己的代码写的跟shi山一样…
需要资料的私聊我