学成在线(第6天)

页面发布课程管理

技术方案

本项目使用MQ实现页面发布的技术方案如下:

技术方案说明:
1、平台包括多个站点,页面归属不同的站点。
2、发布一个页面应将该页面发布到所属站点的服务器上。
3、每个站点服务部署cms client程序,并与交换机绑定,绑定时指定站点Id为routingKey。
指定站点id为routingKey就可以实现cms client只能接收到所属站点的页面发布消息。
4、页面发布程序向MQ发布消息时指定页面所属站点Id为routingKey,将该页面发布到它所在服务器上的cms
client。

页面发布流程图如下:

1、前端请求cms执行页面发布。
2、cms执行静态化程序生成html文件。
3、cms将html文件存储到GridFS中。
4、cms向MQ发送页面发布消息
5、MQ将页面发布消息通知给Cms Client
6、Cms Client从GridFS中下载html文件
7、Cms Client将html保存到所在服务器指定目录

页面发布消费方

需求分析

功能分析:
创建Cms Client工程作为页面发布消费方,将Cms Client部署在多个服务器上,它负责接收到页面发布 的消息后从
GridFS中下载文件在本地保存。
需求如下:
1、将cms Client部署在服务器,配置队列名称和站点ID。
2、cms Client连接RabbitMQ并监听各自的“页面发布队列”
3、cms Client接收页面发布队列的消息
4、根据消息中的页面id从mongodb数据库下载页面到本地

调用 dao查询页面信息,获取到页面的物理路径,调用dao查询站点信息,得到站点的物理路径
页面物理路径=站点物理路径+页面物理路径+页面名称。
从GridFS查询静态文件内容,将静态文件内容保存到页面物理路径下。

创建Cms Client工程

1、创建maven工程

pom.xml依赖

<?xml version="1.0" encoding="UTF‐8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema‐instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven‐4.0.0.xsd">
    <parent>
        <artifactId>xc‐framework‐parent</artifactId>
        <groupId>com.xuecheng</groupId>
        <version>1.0‐SNAPSHOT</version>
        <relativePath>../xc‐framework‐parent/pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>xc‐service‐manage‐cms‐client</artifactId>
    <dependencies>
        <dependency>
            <groupId>com.xuecheng</groupId>
            <artifactId>xc‐framework‐model</artifactId>
            <version>1.0‐SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring‐boot‐starter‐test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring‐boot‐starter‐amqp</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring‐boot‐starter‐data‐mongodb</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons‐io</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
        </dependency>
    </dependencies>
</project>

2、配置文件

在resources下配置application.yml和logback-spring.xml。
application.yml的内容如下:

server:
  port: 31000
spring:
  application:
    name: xc‐service‐manage‐cms‐client
  data:
    mongodb:
      uri:  mongodb://root:[email protected]:27017
      database: xc_cms
  rabbitmq:
    host: 127.0.0.1
    port: 5672
    username: guest
    password: guest
    virtualHost: /
xuecheng:
  mq:
  #cms客户端监控的队列名称(不同的客户端监控的队列不能重复)
    queue: queue_cms_postpage_01
    routingKey: 5a751fab6abb5044e0d19ea1 #此routingKey为门户站点ID

说明:在配置文件中配置队列的名称,每个 cms client在部署时注意队列名称不要重复

3、启动类

@SpringBootApplication
@EntityScan("com.xuecheng.framework.domain.cms")//扫描实体类
@ComponentScan(basePackages={"com.xuecheng.framework"})//扫描common下的所有类
@ComponentScan(basePackages={"com.xuecheng.manage_cms_client"})
public class ManageCmsClientApplication {
    public static void main(String[] args) {
        SpringApplication.run(ManageCmsClientApplication.class, args);
    }
}

RabbitmqConfig 配置类

消息队列设置如下:
1、创建“ex_cms_postpage”交换机
2、每个Cms Client创建一个队列与交换机绑定
3、每个Cms Client程序配置队列名称和routingKey,将站点ID作为routingKey。

package com.xuecheng.manage_cms_client.config;
import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
 * @author Administrator
 * @version 1.0
 **/
@Configuration
public class RabbitmqConfig {
    //队列bean的名称
    public static final String QUEUE_CMS_POSTPAGE = "queue_cms_postpage";
    //交换机的名称
    public static final String EX_ROUTING_CMS_POSTPAGE="ex_routing_cms_postpage";
    //队列的名称
    @Value("${xuecheng.mq.queue}")
    public  String queue_cms_postpage_name;
    //routingKey 即站点Id
    @Value("${xuecheng.mq.routingKey}")
    public  String routingKey;
    /**
     * 交换机配置使用direct类型
     * @return the exchange
     */
    @Bean(EX_ROUTING_CMS_POSTPAGE)
    public Exchange EXCHANGE_TOPICS_INFORM() {
        return ExchangeBuilder.directExchange(EX_ROUTING_CMS_POSTPAGE).durable(true).build();
    }
    //声明队列
    @Bean(QUEUE_CMS_POSTPAGE)
    public Queue QUEUE_CMS_POSTPAGE() {
        Queue queue = new Queue(queue_cms_postpage_name);
        return queue;
    }
    /**
     * 绑定队列到交换机
     *
     * @param queue    the queue
     * @param exchange the exchange

     * @return the binding
     */
    @Bean
    public Binding BINDING_QUEUE_INFORM_SMS(@Qualifier(QUEUE_CMS_POSTPAGE) Queue queue,
@Qualifier(EX_ROUTING_CMS_POSTPAGE) Exchange exchange) {
        return BindingBuilder.bind(queue).to(exchange).with(routingKey).noargs();
    }
}

定义消息格式

消息内容采用json格式存储数据,如下:
页面id:发布页面的id

{
    "pageId":""
}

PageDao

1、使用CmsPageRepository 查询页面信息

public interface CmsPageRepository extends MongoRepository<CmsPage,String> {
}

2、使用CmsSiteRepository查询站点信息,主要获取站点物理路径

public interface CmsSiteRepository extends MongoRepository<CmsSite,String> {
}

PageService

在Service中定义保存页面静态文件到服务器物理路径方法:

package com.xuecheng.manage_cms_client.service;
import com.mongodb.client.gridfs.GridFSBucket;
import com.mongodb.client.gridfs.GridFSDownloadStream;
import com.mongodb.client.gridfs.model.GridFSFile;
import com.xuecheng.framework.domain.cms.CmsPage;
import com.xuecheng.framework.domain.cms.CmsSite;
import com.xuecheng.framework.domain.cms.response.CmsCode;
import com.xuecheng.framework.exception.ExceptionCast;
import com.xuecheng.manage_cms_client.dao.CmsPageRepository;
import com.xuecheng.manage_cms_client.dao.CmsSiteRepository;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.gridfs.GridFsResource;
import org.springframework.data.mongodb.gridfs.GridFsTemplate;
import org.springframework.stereotype.Service;
import java.io.*;
import java.util.Optional;
/**
 * @author Administrator
 * @version 1.0
 **/
@Service
public class PageService {
    @Autowired
    CmsPageRepository cmsPageRepository;
    @Autowired
    CmsSiteRepository cmsSiteRepository;
    @Autowired
    GridFsTemplate gridFsTemplate;
    @Autowired
    GridFSBucket gridFSBucket;
    //将页面html保存到页面物理路径
    public void savePageToServerPath(String pageId){
        Optional<CmsPage> optional = cmsPageRepository.findById(pageId);
        if(!optional.isPresent()){
            ExceptionCast.cast(CmsCode.CMS_PAGE_NOTEXISTS);
        }
        //取出页面物理路径
        CmsPage cmsPage = optional.get();
        //页面所属站点
        CmsSite cmsSite = this.getCmsSiteById(cmsPage.getSiteId());
        //页面物理路径
        String pagePath = cmsSite.getSitePhysicalPath() + cmsPage.getPagePhysicalPath() +
cmsPage.getPageName();
        //查询页面静态文件
        String htmlFileId = cmsPage.getHtmlFileId();
        InputStream inputStream = this.getFileById(htmlFileId);
        if(inputStream == null){
            ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_HTMLISNULL);
        }
        FileOutputStream fileOutputStream = null;
        try {
            fileOutputStream = new FileOutputStream(new File(pagePath));
            //将文件内容保存到服务物理路径
            IOUtils.copy(inputStream,fileOutputStream);
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                fileOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    //根据文件id获取文件内容
    public InputStream getFileById(String fileId){
        try {
            GridFSFile gridFSFile =
gridFsTemplate.findOne(Query.query(Criteria.where("_id").is(fileId)));
            GridFSDownloadStream gridFSDownloadStream =
gridFSBucket.openDownloadStream(gridFSFile.getObjectId());
            GridFsResource gridFsResource = new GridFsResource(gridFSFile,gridFSDownloadStream);
            return gridFsResource.getInputStream();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
    //根据站点id得到站点
    public CmsSite getCmsSiteById(String siteId){
        Optional<CmsSite> optional = cmsSiteRepository.findById(siteId);
        if(optional.isPresent()){
            CmsSite cmsSite = optional.get();
            return cmsSite;
        }
        return null;
    }
}

ConsumerPostPage

在cms client工程的mq包下创建ConsumerPostPage类,ConsumerPostPage作为发布页面的消费客户端,监听
页面发布队列的消息,收到消息后从mongodb下载文件,保存在本地。

package com.xuecheng.manage_cms_client.mq;
import com.alibaba.fastjson.JSON;
import com.xuecheng.framework.domain.cms.CmsPage;
import com.xuecheng.manage_cms_client.dao.CmsPageRepository;
import com.xuecheng.manage_cms_client.service.PageService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.Optional;
/**
 * @author Administrator
 * @version 1.0
 **/
@Component
public class ConsumerPostPage {
    private static final Logger LOGGER = LoggerFactory.getLogger(ConsumerPostPage.class);
    @Autowired
    CmsPageRepository cmsPageRepository;
    @Autowired
    PageService pageService;
    @RabbitListener(queues={"${xuecheng.mq.queue}"})
    public void postPage(String msg){
        //解析消息
        Map map = JSON.parseObject(msg, Map.class);
        LOGGER.info("receive cms post page:{}",msg.toString());
        //取出页面id
        String pageId = (String) map.get("pageId");
        //查询页面信息
        Optional<CmsPage> optional = cmsPageRepository.findById(pageId);
        if(!optional.isPresent()){
            LOGGER.error("receive cms post page,cmsPage is null:{}",msg.toString());
            return ;
        }
        //将页面保存到服务器物理路径
        pageService.savePageToServerPath(pageId);
    }
}

页面发布生产方

需求分析

管理员通过 cms系统发布“页面发布”的消费,cms系统作为页面发布的生产方。
需求如下:
1、管理员进入管理界面点击“页面发布”,前端请求cms页面发布接口。
2、cms页面发布接口执行页面静态化,并将静态化页面存储至GridFS中。
3、静态化成功后,向消息队列发送页面发布的消息。
1) 获取页面的信息及页面所属站点ID。
2) 设置消息内容为页面ID。(采用json格式,方便日后扩展)
3) 发送消息给ex_cms_postpage交换机,并将站点ID作为routingKey。

页面发布前端

用户操作流程:
1、用户进入cms页面列表。
2、点击“发布”请求服务端接口,发布页面。
3、提示“发布成功”,或发布失败。

API方法

在 cms前端添加 api方法。

/*发布页面*/
export const page_postPage= id => {
  return http.requestPost(apiUrl+‘/cms/page/postPage/‘+id)
}

页面

修改page_list.vue,添加发布按钮

<el‐table‐column label="发布" width="80">
  <template slot‐scope="scope">
    <el‐button
      size="small" type="primary" plain @click="postPage(scope.row.pageId)">发布
    </el‐button>
  </template>
</el‐table‐column>

添加页面发布事件:

postPage (id) {
  this.$confirm(‘确认发布该页面吗?‘, ‘提示‘, {
  }).then(() => {
    cmsApi.page_postPage(id).then((res) => {
      if(res.success){
        console.log(‘发布页面id=‘+id);
        this.$message.success(‘发布成功,请稍后查看结果‘);
      }else{
        this.$message.error(‘发布失败‘);
      }
    });
  }).catch(() => {
  });
},

测试

课程管理

需求分析

课程管理包括如下功能需求:
1、分类管理
2、新增课程
3、修改课程
4、预览课程
5、发布课程

用户的操作流程如下:
1、进入我的课程

2、点击“添加课程”,进入添加课程界面

3、输入课程基本信息,点击提交
4、课程基本信息提交成功,自动进入“管理课程”界面,点击“管理课程”也可以进入“管理课程”界面

环境搭建

搭建数据库环境

1) 创建数据库
课程管理使用MySQL数据库,创建课程管理数据库:xc_course。
导入xc_course.sql脚本

2) 数据表介绍
课程信息内容繁多,将课程信息分类保存在如下表中:

导入课程管理服务工程

持久层技术介绍:
课程管理服务使用MySQL数据库存储课程信息,持久层技术如下:
1、spring data jpa:用于表的基本CRUD。
2、mybatis:用于复杂的查询操作。
3、druid:使用阿里巴巴提供的spring boot 整合druid包druid-spring-boot-starter管理连接池。

课程计划查询

需求分析

左侧显示的就是课程计划,课程计划是一个树型结构,方便扩展课程计划的级别。
在上边页面中,点击“添加课程计划”即可对课程计划进行添加操作。
点击修改可对某个章节内容进行修改。
点击删除可删除某个章节。

页面原型

在course_plan.vue文件中添加tree组件的代码,进行测试:
1、组件标签

<el‐tree
      :data="data"
      show‐checkbox
      node‐key="id"
      default‐expand‐all
      :expand‐on‐click‐node="false"
      :render‐content="renderContent">
    </el‐tree>

2、数据对象

let id = 1000;
  export default {
    data() {
      return {
        data : [{
          id: 1,
          label: ‘一级 1‘,
          children: [{
            id: 4,
            label: ‘二级 1‐1‘,
            children: [{
              id: 9,
              label: ‘三级 1‐1‐1‘
            }, {
              id: 10,
              label: ‘三级 1‐1‐2‘
            }]
          }]
        }]
      }
     }
   }

课程管理服务

课程计划是树型结构,采用表的自连接方式进行查询,sql语句如下:

SELECT
a.id one_id,
a.pname one_pname,
b.id two_id,
b.pname two_pname,
c.id three_id,
c.pname three_pname
FROM
teachplan a
LEFT JOIN teachplan b
ON a.id = b.parentid
LEFT JOIN teachplan c
ON b.id = c.parentid
WHERE a.parentid=‘0‘
AND a.courseid=‘4028e581617f945f01617f9dabc40000‘
ORDER BY a.orderby,
b.orderby,
c.orderby

Dao

1) mapper接口

@Mapper
public interface TeachplanMapper {
    public TeachplanNode selectList(String courseId);
}

2)mapper映射文件

<resultMap type="com.xuecheng.framework.domain.course.ext.TeachplanNode" id="teachplanMap" >
     <id property="id" column="one_id"/>
     <result property="pname" column="one_name"/>
     <collection property="children"
ofType="com.xuecheng.framework.domain.course.ext.TeachplanNode">
         <id property="id" column="two_id"/>
         <result property="pname" column="two_name"/>
         <collection property="children"
ofType="com.xuecheng.framework.domain.course.ext.TeachplanNode">
             <id property="id" column="three_id"/>
             <result property="pname" column="three_name"/>
         </collection>
     </collection>
 </resultMap>
 <select id="selectList" resultMap="teachplanMap" parameterType="java.lang.String" >
     SELECT
       a.id one_id,
       a.pname one_name,
       b.id two_id,
       b.pname two_name,
       c.id three_id,
       c.pname three_name
     FROM
       teachplan a LEFT JOIN teachplan b
         ON a.id = b.parentid
       LEFT JOIN teachplan c
         ON b.id = c.parentid
     WHERE  a.parentid = ‘0‘
     <if test="_parameter!=null and _parameter!=‘‘">
         and a.courseid=#{courseId}
     </if>
     ORDER BY a.orderby,
       b.orderby,
       c.orderby
</select>

Service

创建CourseService类,定义查询课程计划方法

@Service
public class CourseService {
    @Autowired
    TeachplanMapper teachplanMapper;
    //查询课程计划
    public TeachplanNode findTeachplanList(String courseId){
        TeachplanNode teachplanNode = teachplanMapper.selectList(courseId);
        return teachplanNode;
    }
}

Controller

@RestController
@RequestMapping("/course")
public class CourseController implements CourseControllerApi {
    @Autowired
    CourseService courseService;
    //查询课程计划
    @Override
    @GetMapping("/teachplan/list/{courseId}")
    public TeachplanNode findTeachplanList(String courseId) {
        return courseService.findTeachplanList(courseId);
    }
}

测试

原文地址:https://www.cnblogs.com/anan-java/p/12231382.html

时间: 2024-10-11 05:39:59

学成在线(第6天)的相关文章

学成在线(第1天)

第一次用博客,笔者大学毕业即将走向社会,从事Java开发,有个博客是很有必要的.博客用于记录学习工作中遇到的问题,可以很好的总结经验.同时我在遇到bug的时候也是通过他人博客解决的,我也想通过博客帮助到遇到同样问题的人. 年前面试过几家,现在企业都要求分布式,微服务springcloud的项目经验,为了能找到更好的工作,我选择了自学黑马<学成在线>这个微服务项目,这个博客就用于记录每天看视频做这个项目遇到的问题和总结吧! 1.项目的功能架构 1.1  项目背景 学成在线借鉴了MOOC(大型开放

学成在线(第21天)项目总结

1  学成在线是个什么样的项目? 1.1  项目背景 学成在线借鉴了MOOC(大型开放式网络课程,即MOOC(massive open online courses))的设计思想,是一个提供IT职业课程在线学习的平台,它为即将和已经加入IT领域的技术人才提供在线学习服务,用户通过在线学习.在线练习.在线考试等学习内容,最终掌握所学的IT技能,并能在工作中熟练应用.当前市场的在线教育模式多种多样,包括:B2C.C2C.B2B2C等业务模式,学成在线采用B2B2C业务模式,即向企业或个人提供在线教育

学成在线(第15天)

学习页面查询课程计划 到目前为止,我们已可以编辑课程计划信息并上传课程视频,下一步我们要实现在线学习页面动态读取章节对应的视频并进行播放.在线学习页面所需要的信息有两类:一类是课程计划信息.一类是课程学习信息(视频地址.学习进度等),如下图: 在线学习集成媒资管理的需求如下:1.在线学习页面显示课程计划2.点击课程计划播放该课程计划对应的视频 Api 接口 课程计划信息从哪里获取?目前课程计划信息在课程管理数据库和ES索引库中存在,考虑性能要求,课程发布后对课程的查询统一从ES索引库中查询.前端

学成在线(第3天)

新增页面 新增页面接口定义 1.定义响应模型 @Data public class CmsPageResult extends ResponseResult {     CmsPage cmsPage;     public CmsPageResult(ResultCode resultCode,CmsPage cmsPage) {         super(resultCode);         this.cmsPage = cmsPage;     } } 2.定义添加Api @ApiO

学成在线(第4天)

页面静态化需求 1.为什么要进行页面管理?本项目cms系统的功能就是根据运营需要,对门户等子系统的部分页面进行管理,从而实现快速根据用户需求修改页面内容并上线的需求. 2.如何修改页面的内容?在开发中修改页面内容是需要人工编写html及JS文件,CMS系统是通过程序自动化的对页面内容进行修改,通过页面静态化技术生成html页面. 3.如何对页面进行静态化? 采用页面模板+数据 = 输出html页面的技术实现静态化. 4.静态化的html页面存放在哪里? 生成对的静态化的页面,由cms程序自动发布

学成在线(第8天)

FastDFS 研究 什么是分布式文件系统 什么是文件系统 文件系统是负责管理和存储文件的系统软件,它是操作系统和硬件驱动之间的桥梁,操作系统通过文件系统提供的接口去存取文件,用户通过操作系统访问磁盘上的文件.如下图: 什么是分布式文件系统 为什么会有分布文件系统呢?分布式文件系统是面对互联网的需求而产生,互联网时代对海量数据如何存储?靠简单的增加硬盘的个数已经满足不了我们的要求,因为硬盘传输速度有限但是数据在急剧增长,另外我们还要要做好数据备份.数据安全等.采用分布式文件系统可以将多个地点的文

学成在线(第11天)

课程搜索需求分析 需求分析 1.根据分类搜索课程信息.2.根据关键字搜索课程信息,搜索方式为全文检索,关键字需要匹配课程的名称. 课程内容.3.根据难度等级搜索课程.4.搜索结点分页显示. 搜索流程 1.课程管理服务将数据写到MySQL数据库2.使用Logstash将MySQL数据库中的数据写到ES的索引库.3.用户在前端搜索课程信息,请求到搜索服务.4.搜索服务请求ES搜索课程信息. 课程索引 如何维护课程索引信息?1.当课程向MySQL添加后同时将课程信息添加到索引库.采用Logstach实

学成在线(第14天)

视频处理 需求分析 原始视频通常需要经过编码处理,生成m3u8和ts文件方可基于HLS协议播放视频.通常用户上传原始视频,系统自动处理成标准格式,系统对用户上传的视频自动编码.转换,最终生成m3u8文件和ts文件,处理流程如下:1.用户上传视频成功2.系统对上传成功的视频自动开始编码处理3.用户查看视频处理结果,没有处理成功的视频用户可在管理界面再次触发处理4.视频处理完成将视频地址及处理结果保存到数据库 视频处理流程如下: 视频处理进程的任务是接收视频处理消息进行视频处理,业务流程如下:1.监

学成在线_nginx遇到的问题

下载nginx:http://nginx.org/en/download.html 下载了最新的稳定版本 安装目录内运行nginx.exe 一直运行不起来.进程里面也没有这个进程.我把本机的IIS的80端口关闭了也起作用. 后来查询,发现可以在logs文件夹下面看错误日志. An attempt was made to access a socket in a way forbidden by its access permissions https://www.cnblogs.com/hope