XC-15媒资管理系统集成

news2024/11/16 17:34:17

学习页面查询课程计划

需求分析

到目前位置,我们已经可以编辑课程计划信息并且上传课程视频,下一步我们要是心啊在线学习页面动态获取章节对应的视频并且进行播放,在线学习页面所需要的信息有两类,一类是课程计划信息,一类是课程学习信息(视频地址,学习进度等),如下图
在这里插入图片描述
在线学习继承每集管理的需求如下:
1.在学习页面显示课程计划
2.点击课程计划播放该课程计划对应的视频
本章节实现学习页面动态显示课程计划,进入不同课程的学习也买你右侧显示当前课程的课程计划.

API接口

课程计划信息从哪里获取?
目前课程计划信息在课程管理数据库和ES索引库中存在,考虑性能需求,课程发布后对课程查询同意从ES索引库中查询.
前端通过请求搜索服务获取课程信息,需要单独在搜索服务中定义客户菜呢个信息查询接口.
本接口接收课程id,查询课程所有信息,返回给前端.
在search中定义接口api

    @ApiOperation("根据课程id查询课程信息")
    public Map<String,CoursePub> getall (String id);

服务端开发

启动es,
打开es文件,启动es和eshead
启动es
打开eslasticsearch.bat
在这里插入图片描述

启动eshead

npm run start

在这里插入图片描述

Service

//    使用ES客户端向ES请求查询索引信息

    public Map<String, CoursePub> getAll(String id) {

//        定义一个搜索的请求对象 xc_andrew
        SearchRequest searchRequest = new SearchRequest(index);
//        指定type   doc
        searchRequest.types(type);

//        定义一个searchSourceBuilder
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();


//        使用termQuery
        searchSourceBuilder.query(QueryBuilders.termQuery("id", id));
//        过滤原字段,去除所有字段,不用设置过滤原字段


        searchRequest.source(searchSourceBuilder);


        Map<String, CoursePub> map = new HashMap<>();
        try {
            SearchResponse search = restHighLevelClient.search(searchRequest);
            SearchHits hits = search.getHits();
            SearchHit[] hits1 = hits.getHits();
            for (SearchHit hit : hits1) {
//        最终要返回的课程信息
                CoursePub coursePub = new CoursePub();
                Map<String, Object> sourceAsMap = hit.getSourceAsMap();
//                课程id
                String courseId = (String) sourceAsMap.get("id");
                String name = (String) sourceAsMap.get("name");
                String grade = (String) sourceAsMap.get("grade");
                String charge = (String) sourceAsMap.get("charge");
                String pic = (String) sourceAsMap.get("pic");
                String description = (String) sourceAsMap.get("description");
                String teachplan = (String) sourceAsMap.get("teachplan");

                coursePub.setId(courseId);
                coursePub.setGrade(grade);
                coursePub.setCharge(charge);
                coursePub.setPic(pic);
                coursePub.setDescription(description);
                coursePub.setTeachplan(teachplan);

                map.put(courseId, coursePub);
            }


        } catch (IOException e) {
            e.printStackTrace();
        }


        return map;

    }

Controller

    @Override
    @GetMapping("/getall/{id}")
    public Map<String, CoursePub> getall(String id) {
        return esCourseService.getAll(id);
    }

测试

在这里插入图片描述

在这里插入图片描述

前端开发

配置虚拟主机

学习中心的二级域名为ucenter.xuecheng.com,我们在nginx中配置ucenter虚拟主机.
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
对应的方法,返回一个map<String,CoursePub>然后返回给前端调用

这里配置完nginx后需要重新启动
判断nginx是否启动,如果没有,那么直接启动,如果已经启动,那么
进行重启

nginx -s reload

在这里插入图片描述

API方法

API调试

测试

学习页面获取视频播放地址

需求分析

用户进入在线学习页面,点击课程计划,将播放该课程计划对应的教学视频.
业务流程如下:
在这里插入图片描述
业务流程说明:
1.用户进入在线学习页面,页面请求搜索获取课程信息(包括课程计划信息)并且在页面展示.
2.在线学习请求学习服务获取视频播放地址.
3.学习服务校验当前用户是否有全新啊学习,如果没有全新啊学习,则提示用户.
4.学习服务校验通过,请求搜索服务获取课程媒资信息.
2.搜索服务请求ElasticSearch获取课程媒资信息.
为甚要请求ElasticSearch查询课程媒资信息?
处于性能的考虑,公开查询课程信息从搜索服务查询.
什么时候将课程媒资信息储存到ElasticSearch中?
课程媒资信息是在课程发布的时候存入ElasticSearch,因为课程发布后课程信息将基本不再修改…

课程发布存储媒资信息

需求分析

课程媒资信息实在课程发布的时候存入ElasticSearch索引库,因为课程发布后课程信息将基本不在修改,具体的业务流程如下.
业务流程如下:
1.课程发布,像课程媒资i西南西表写入数据.
2.根据课程id删除teachplanMediaPub中的数据
3. 根据课程id查询teachplanMedia数据
4.将查询到的teachplanMedia数据插入到teachplanMediaPub中
5.Logstash定时扫描课程妹子信息表,将课程媒资信息写入索引库.

数据模型

Dao

package com.xuecheng.manage_course.dao;

import com.xuecheng.framework.domain.course.TeachplanMedia;
import com.xuecheng.framework.domain.course.TeachplanMediaPub;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;

/**
 * Created by Administrator.
 */
public interface TeachplanMediaPubRepository extends JpaRepository<TeachplanMediaPub,String> {
//    根据课程id删除记录
    long deleteByCourseId(String courseId);

}

package com.xuecheng.manage_course.dao;

import com.xuecheng.framework.domain.course.CourseBase;
import com.xuecheng.framework.domain.course.TeachplanMedia;
import org.apache.ibatis.annotations.Param;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;

/**
 * Created by Administrator.
 */
public interface TeachplanMediaRepository extends JpaRepository<TeachplanMedia,String> {
//    根据课程id来查询列表
    List<TeachplanMedia> findByCourseId( String courseId);

}

Service



//得到页面的url
        String pageUrl = cmsPostPageResult.getPageUrl();


//        向teachplanMediaPub中保存课程的信息
        saveTeachplanMediaPub(id);

        return new CoursePublishResult(CommonCode.SUCCESS, pageUrl);


    }

//        向teachplanMediaPub中保存课程的信息
private void saveTeachplanMediaPub(String courseId){
//        先删除teachplanMediaPub中的数据
    teachplanMediaPubRepository.deleteByCourseId(courseId);
//    从teachplanMedia中查询
    List<TeachplanMedia> teachplanMediaList = teachplanMediaRepository.findByCourseId(courseId);
//    将teachpanMediaList插入到teachplanMediaPub表中
    List<TeachplanMediaPub> teachplanMediaPubs = new ArrayList<>();

//    将teachplanMediaList中的数据放到TeachplanMediaPubs中
    for (TeachplanMediaPub teachplanMediaPub : teachplanMediaPubs) {
//        类型不对,看看如何将一个不同类型的对象融入到另一个对象中
        TeachplanMediaPub teachplanMediaPub1 = new TeachplanMediaPub();
//        将0中的数据拷贝到1中
        BeanUtils.copyProperties(teachplanMediaPub,teachplanMediaPub1);
//        最后在pub中添加一个时间戳
        teachplanMediaPub1.setTimestamp(new Date());
        teachplanMediaPubs. add(teachplanMediaPub1);


    }



//    将课程媒资信息插入到Pub中
    teachplanMediaPubRepository.saveAll(teachplanMediaPubs);

}

测试

测试课程发布后是否成功将课程媒资信息存储到teachplan_media_pub中,测试流程如下:
1.指点一个课程
2.为课程计划添加课程媒资
3.执行课程发布
4.观察课程计划媒资信息是否存储值teachplan_meida_pub中
注意:由于此测试仅用于测试发布课程计划媒资信息的功能,可暂时将cms页面发布功能暂时屏蔽,提高测试效率.

Logstash扫描课程计划媒资

Logstash定时扫描课程媒资信息表,并将课程媒资信息写入索引库.

创建索引

1.创建xc_course_media索引
在这里插入图片描述
点击此处的新建,创建media索引库

2.并向此索引创建如下映射
Post http://localhost:9200/xc_course_media/doc/_mapping

//启动es,打开es文件点击bin目录下的elasticsearch.bat文件启动
//打开head文件的cmd,使用npm run start 启动

在这里插入图片描述

打开postman 发送
http://localhost:9200/xc_andrew_media/doc/_mapping
在这里插入图片描述

{
    "properties":{
        "courseid":{
"type": "keyword"
        },
        "media_id":{
            "type":"keyword"
        },
        "media_url":{
            "index":false,
            "type":"text"
        },
        "media_fileoriginalname":{
            "index":false,
            "type":"text"
        }
    }
}

创建成功:

{
    "acknowledged": true
}

创建Logstash模板文件

启动logstash
在logstach的config目录创建xc_course_media_template.json,内容格式如下:
本教程的xc_course_media_template.json目录是:

配置mysql.conf

input {
  stdin {
  }
  jdbc {
  jdbc_connection_string => "jdbc:mysql://localhost:3306/xc_course?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC"
  # the user we wish to excute our statement as
  jdbc_user => "root"
  jdbc_password => root
  # the path to our downloaded jdbc driver  
  jdbc_driver_library => "D:/BaiduNetdiskDownload/maven_repository/mysql/mysql-connector-java/5.1.47/mysql-connector-java-5.1.47.jar"
  # the name of the driver class for mysql
  jdbc_driver_class => "com.mysql.jdbc.Driver"
  jdbc_paging_enabled => "true"
  jdbc_page_size => "50000"
  #要执行的sql文件
  #statement_filepath => "/conf/course.sql"
  statement => "select * from teachplan_media_pub where timestamp > date_add(:sql_last_value,INTERVAL 8 HOUR)"
  #定时配置
  schedule => "* * * * *"
  record_last_run => true
  last_run_metadata_path => "D:/BaiduNetdiskDownload/es/logstash-6.2.1/config/logstash_metadata"
  }
}


output {
  elasticsearch {  
  #ES的ip地址和端口
  hosts => "localhost:9200"
  #hosts => ["localhost:9200","localhost:9202","localhost:9203"]
  #ES索引库名称
  index => "xc_andrew_media"
  document_id => "%{teachplan_id}"
  document_type => "doc"
  template =>"D:/BaiduNetdiskDownload/es/logstash-6.2.1/config/xc_course_media_template.json"
  template_name =>"xc_course_media"
  template_overwrite =>"true"
  }
  stdout {
 #日志输出
  codec => json_lines
  }
}

启动mysql.conf

启动logstash.bat

配置完成后,进入bin目录

logstash.bat -f   ../config/mysql_course_media.conf

启动
在这里插入图片描述
自动导入conf中配置的具体mysql库中的值

搜索服务查询课程媒资接口

在这里插入图片描述

需求分析

搜索服务提供查询课程媒资接口,此接口供学习服务调用.

API

在课程搜索包下定义Api

    //
    @ApiOperation("根据课程计划id查询课程媒资信息")
    public TeachplanMediaPub getmedia (String id);


Service


//通过多个课程计划查询课程媒资信息
    public QueryResponseResult<TeachplanMediaPub> getmeida(String[] teachplanIds) {
//定义一个搜索请求对象
        SearchRequest searchRequest = new SearchRequest(media_index);
//        指定Type
        searchRequest.types(media_type);

//        定义SearchSourceBuilder
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

//        过滤字段
        String[] mediaSourceField = media_source_field.split(",");
//        前一个参数表示包括那些字段,后一个表示排除 那些字段
        searchSourceBuilder.fetchSource(mediaSourceField,new String[]{});
//     设置使用TermQuery,根据多个id来查询
        searchSourceBuilder.query(QueryBuilders.termsQuery("teachplan_id",teachplanIds));


        searchRequest.source(searchSourceBuilder);
SearchResponse searchResponse = null;
//       使用es的客户端搜索请求es
        long total = 0;
        List<TeachplanMediaPub> teachplanMediaPubList = new ArrayList<>();
        try {
           searchResponse = restHighLevelClient.search(searchRequest);

//          得到我们要的匹配到的结果
            SearchHits hits = searchResponse.getHits();
            total = hits.totalHits;
//            从当前对象中得到匹配的记录
            SearchHit[] searchHits = hits.getHits();
            for (SearchHit searchHit : searchHits) {
//                得到记录的原文档
                TeachplanMediaPub teachplanMediaPub = new TeachplanMediaPub()   ;
                Map<String, Object> sourceAsMap = searchHit.getSourceAsMap();
//                取出课程计划的媒资信息
                String codurseid = (String) sourceAsMap.get("courseid");
                String media_id = (String) sourceAsMap.get("media_id");
                String media_url = (String) sourceAsMap.get("media_url");
                String teachplan_id = (String) sourceAsMap.get("teachplan_id");
                String media_fileoriginalname = (String) sourceAsMap.get("media_fileoriginalname");

                teachplanMediaPub.setCourseId(codurseid);
                teachplanMediaPub.setMediaId(media_id);
                teachplanMediaPub.setMediaUrl(media_url);
                teachplanMediaPub.setTeachplanId(teachplan_id);
                teachplanMediaPub.setMediaFileOriginalName(media_fileoriginalname);

                teachplanMediaPubList.add(teachplanMediaPub);

            }

        } catch (IOException e) {
            e.printStackTrace();
        }
//        在定义之前,有一个数据集和
         QueryResult<TeachplanMediaPub> queryResult = new QueryResult<>();
        queryResult.setList(teachplanMediaPubList);
        queryResult.setTotal(total);
        QueryResponseResult<TeachplanMediaPub> queryResponseResult = new QueryResponseResult<>(CommonCode.SUCCESS_SEARCH_MEDIA_PUBLIST,queryResult);
        return queryResponseResult;

    }

Controller

    @Override
    @GetMapping("/getmedia/{id}")
    public TeachplanMediaPub getmedia(@PathVariable("id") String teachplanid) {
        String[] teachplanIds = new String[]{teachplanid};
        QueryResponseResult<TeachplanMediaPub> queryResponseResult = esCourseService.getmeida(teachplanIds);
        QueryResult<TeachplanMediaPub> queryResult = queryResponseResult.getQueryResult();
        if (queryResult!=null){
            List<TeachplanMediaPub> list = queryResult.getList();
            int size = list.size();
            if (list != null&& size>0){
//                根据一个id只是取出一条记录
                return list.get(0);
            }
        }
        return new TeachplanMediaPub();
    }

测试

在这里插入图片描述

在线学习接口

需求分析

根据下边的业务流程,本章节完成前端需恶习页面请求学习服务获取视频课程的地址,并且自动播放视频.
在这里插入图片描述
*加粗样式

搭建开发环境

创建xc_learning数据库,学习数据库将记录学生的选课信息,学习信息.
导入:资料/xc_learning.sql

创建数据库

/*
SQLyog v10.2 
MySQL - 5.7.21-log : Database - xc_learning
*********************************************************************
*/


/*!40101 SET NAMES utf8 */;

/*!40101 SET SQL_MODE=''*/;

/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
CREATE DATABASE /*!32312 IF NOT EXISTS*/`xc_learning` /*!40100 DEFAULT CHARACTER SET utf8 */;

USE `xc_learning`;

/*Table structure for table `xc_learning_course` */

DROP TABLE IF EXISTS `xc_learning_course`;

CREATE TABLE `xc_learning_course` (
  `id` varchar(32) NOT NULL,
  `course_id` varchar(32) NOT NULL COMMENT '课程id',
  `user_id` varchar(32) NOT NULL COMMENT '用户id',
  `valid` varchar(32) DEFAULT NULL COMMENT '有效性',
  `start_time` datetime DEFAULT NULL,
  `end_time` datetime DEFAULT NULL,
  `status` varchar(32) DEFAULT NULL COMMENT '选课状态',
  PRIMARY KEY (`id`),
  UNIQUE KEY `xc_learning_list_unique` (`course_id`,`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

/*Data for the table `xc_learning_course` */

insert  into `xc_learning_course`(`id`,`course_id`,`user_id`,`valid`,`start_time`,`end_time`,`status`) values ('402885816243d2dd016243f24c030002','402885816243d2dd016243f24c030002','49',NULL,NULL,NULL,'501001'),('8a7e82b564b5e53f0164b5ee61e50002','4028e581617f945f01617f9dabc40000','49',NULL,NULL,NULL,'501001'),('8a7e82b564b5e53f0164b5ee6b780003','4028e581617f945f01617f9dabc40001','49',NULL,NULL,NULL,'501001');

/*Table structure for table `xc_task_his` */

DROP TABLE IF EXISTS `xc_task_his`;

CREATE TABLE `xc_task_his` (
  `id` varchar(32) NOT NULL COMMENT '任务id',
  `create_time` datetime DEFAULT NULL,
  `update_time` datetime DEFAULT NULL,
  `delete_time` datetime DEFAULT NULL,
  `task_type` varchar(32) DEFAULT NULL COMMENT '任务类型',
  `mq_exchange` varchar(64) DEFAULT NULL COMMENT '交换机名称',
  `mq_routingkey` varchar(64) DEFAULT NULL COMMENT 'routingkey',
  `request_body` varchar(512) DEFAULT NULL COMMENT '任务请求的内容',
  `version` int(10) DEFAULT '0' COMMENT '乐观锁版本号',
  `status` varchar(32) DEFAULT NULL COMMENT '任务状态',
  `errormsg` varchar(512) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

/*Data for the table `xc_task_his` */

insert  into `xc_task_his`(`id`,`create_time`,`update_time`,`delete_time`,`task_type`,`mq_exchange`,`mq_routingkey`,`request_body`,`version`,`status`,`errormsg`) values ('10','2018-04-04 22:58:20','2018-07-13 22:58:54','2018-07-16 12:24:36',NULL,'ex_learning_addchoosecourse','addchoosecourse','{\"userId\":\"49\",\"courseId\":\"4028e581617f945f01617f9dabc40000\"}',NULL,'10201',NULL),('11','2018-07-16 12:28:03','2018-07-15 12:28:04','2018-07-16 12:29:11',NULL,'ex_learning_addchoosecourse','addchoosecourse','{\"userId\":\"49\",\"courseId\":\"4028e581617f945f01617f9dabc40001\"}',NULL,NULL,NULL);

/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;

在这里插入图片描述

创建学习服务工程

application

server:
  port: ${PORT:40600}
spring:
  application:
    name: xc-service-learning
  datasource:
    druid:
      url: ${MYSQL_URL:jdbc:mysql://localhost:3306/xc_learning?characterEncoding=utf-8}
      username: root
      password: root
      driverClassName: com.mysql.jdbc.Driver
      initialSize: 5  #初始建立连接数量
      minIdle: 5  #最小连接数量
      maxActive: 20 #最大连接数量
      maxWait: 10000  #获取连接最大等待时间,毫秒
      testOnBorrow: true #申请连接时检测连接是否有效
      testOnReturn: false #归还连接时检测连接是否有效
      timeBetweenEvictionRunsMillis: 60000 #配置间隔检测连接是否有效的时间(单位是毫秒)
      minEvictableIdleTimeMillis: 300000  #连接在连接池的最小生存时间(毫秒)
#rabbitmq配置
  rabbitmq:
    host: 127.0.0.1
    port: 5672
    username: guest
    password: guest
    publisher-confirms: true
    virtual-host: /
eureka:
  client:
    registerWithEureka: true #服务注册开关
    fetchRegistry: true #服务发现开关
    serviceUrl: #Eureka客户端与Eureka服务端进行交互的地址,多个中间用逗号分隔
      defaultZone: ${EUREKA_SERVER:http://localhost:50101/eureka/,http://localhost:50102/eureka/}
  instance:
    prefer-ip-address:  true  #将自己的ip地址注册到Eureka服务中
    ip-address: ${IP_ADDRESS:127.0.0.1}
    instance-id: ${spring.application.name}:${server.port} #指定实例id
ribbon:
  MaxAutoRetries: 2 #最大重试次数,当Eureka中可以找到服务,但是服务连不上时将会重试,如果eureka中找不到服务则直接走断路器
  MaxAutoRetriesNextServer: 3 #切换实例的重试次数
  OkToRetryOnAllOperations: false  #对所有操作请求都进行重试,如果是get则可以,如果是post,put等操作没有实现幂等的情况下是很危险的,所以设置为false
  ConnectTimeout: 5000  #请求连接的超时时间
  ReadTimeout: 6000 #请求处理的超时时间
<?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-learning</artifactId>

    <dependencies>
        <!-- 导入Eureka客户端的依赖 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>com.xuecheng</groupId>
            <artifactId>xc-service-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>com.xuecheng</groupId>
            <artifactId>xc-framework-model</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>com.xuecheng</groupId>
            <artifactId>xc-framework-utils</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>com.xuecheng</groupId>
            <artifactId>xc-framework-common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>${springframework.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
    </dependencies>
</project>

在这里插入图片描述

API接口

package com.xuecheng.api.learning;

import com.xuecheng.framework.domain.learning.respones.GetMediaResult;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;

/**
 * @author Andrewer
 * @version 1.0
 * @project xcEduService01
 * @description
 * @date 2023/1/12 14:59:06
 */
@Api(value = "录播课程学习管理",description = "录播课程学习管理")
public interface CourseLearningControllerApi {
    @ApiOperation("获取课程学习地址")
//    第一个参数校验学习的资格,第二个参数从es获取媒资地址信息
    public GetMediaResult getMedia(String courseId,String teachPlanId);

}
package com.xuecheng.framework.domain.learning.respones;

import com.xuecheng.framework.model.response.ResponseResult;
import com.xuecheng.framework.model.response.ResultCode;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

/**
 * @author Andrewer
 * @version 1.0
 * @project xcEduService01
 * @description
 * @date 2023/1/12 15:02:07
 */
@Data
@ToString
//远程调用的时候需要加一个无参的构造方法
@NoArgsConstructor
public class GetMediaResult extends ResponseResult {
//    视频播放地址

    String fileUrl;
    public GetMediaResult(ResultCode resultCode,String fileUrl){
        super(resultCode);
        this.fileUrl = fileUrl  ;
    }
}

服务端开发

需求分析

学习服务根据传入课程ID,章节id(课程计划ID)请求搜索服务,获取学习地址.

搜索服务注册Eureka

学习服务要嗲用搜索服务串课程媒资信息,所以需要将搜索服务注册到eureka中
1.查看服务名称是否为xc-service-search
2.配置搜索服务的配置文件applicaiton.yml,加入Eureka,配置如下:

server:
  port: ${port:40100}
spring:
  application:
    name: xc-service-search
xuecheng:
  elasticsearch:
    hostlist: ${eshostlist:127.0.0.1:9200} # 多个节点中间用','分割
  course:
      index: xc_andrew
      type: doc
      source_field: id,name,grade,mt,st,charge,valid,pic,qq,price,price_old,status,studymodel,teachmode,expires,pub_time,start_time,end_time
  media:
      index: xc_andrew_media
      type: doc
      source_field: courseid,media_id,media_url,teachplan_id,media_fileoriginalname
eureka:
  client:
    registerWithEureka: true #服务注册开关
    fetchRegistry: true #服务发现开关
    serviceUrl: #Eureka客户端 与Eureka服务端进行交互的地址,多个中间用逗号分割
      defaultZone: ${EUREKA_SERVER:http://localhost:50101/eureka/,http://localhost:50102/eureka/}
  instance:
    prefer-ip-address: true #将自己的ip注册到Eureka里面
    ip-address: ${IP_ADDRESS:127.0.0.1}
    instance-id: ${spring.application.name}:${server.port} #指定实例ip
ribbon:
  MaxAutoRetries: 2 #最大重试的此时,当Eureka中可以找到服务,但是服务连接不上的时候会重试,如果eureka中找不到服务,则直接走断路器
  MaxAutoRetriesNextserver: 3 # 切换实例的重试此时
  OkToRetryOnAllOperations: false #对所有操作请求都进行重试,如歌是get则可以,如果是post,put等操作没有实现幂等的情况下是很危险的,所以设置false
  ConnectTimeOut: 5000 #请求连接的超时时间
  ReadTimeOut: 6000 # 请求处理的超时时间
package com.xuecheng.search;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.annotation.ComponentScan;

/**
 * @author Administrator
 * @version 1.0
 **/
//使Eureka发现服务的地址
@EnableDiscoveryClient
//如果使用搜索服务,需要加FeignClient,如果搜索服务不需要对外远程调用其他的服务,则不需要加
//@EnableFeignClients
@SpringBootApplication
@EntityScan("com.xuecheng.framework.domain.search")//扫描实体类
@ComponentScan(basePackages={"com.xuecheng.api"})//扫描接口
@ComponentScan(basePackages={"com.xuecheng.search"})//扫描本项目下的所有类
@ComponentScan(basePackages={"com.xuecheng.framework"})//扫描common下的所有类
public class SearchApplication {

    public static void main(String[] args) throws Exception {
        SpringApplication.run(SearchApplication.class, args);
    }

}

搜索服务客服端

Service

package com.xuecheng.learning.service;

import com.xuecheng.framework.domain.course.TeachplanMediaPub;
import com.xuecheng.framework.domain.learning.respones.GetMediaResult;
import com.xuecheng.framework.domain.learning.respones.LearningCode;
import com.xuecheng.framework.exception.ExceptionCast;
import com.xuecheng.framework.model.response.CommonCode;
import com.xuecheng.learning.client.CourseSearchClient;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * @author Andrewer
 * @version 1.0
 * @project xcEduService01
 * @description
 * @date 2023/1/12 16:05:07
 */
@Service
public class LearningService {


    @Autowired
    CourseSearchClient searchClient;
//获取课程的学习地址(视频的播放地址)

    public GetMediaResult getMedia(String courseId, String teachPlanId) {

//        校验学生学习的权限




//        远程调用搜索服务查询课程计划所对应的媒资信息
//      就像调用本地方法一样进行远程调用
        TeachplanMediaPub teachplanMediaPub = searchClient.getMedia(teachPlanId);
//        这里需要进行判断一下
        if (teachplanMediaPub==null|| StringUtils.isEmpty(teachplanMediaPub.getMediaUrl())){
//            获取学习地址错误
            ExceptionCast.cast(LearningCode.LEARNING_GETMEDIA_ERROR);

        }
        return new GetMediaResult(CommonCode.SUCCESS,teachplanMediaPub.getMediaUrl());

    }
}

Controller

package com.xuecheng.learning.controller;

import com.netflix.discovery.converters.Auto;
import com.xuecheng.api.learning.CourseLearningControllerApi;
import com.xuecheng.framework.domain.learning.respones.GetMediaResult;
import com.xuecheng.learning.service.LearningService;
import org.springframework.beans.factory.annotation.Autowired;
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;

/**
 * @author Andrewer
 * @version 1.0
 * @project xcEduService01
 * @description
 * @date 2023/1/12 16:00:16
 */
@RestController
@RequestMapping("/learning/course")
public class CourseLearningController implements CourseLearningControllerApi {
@Autowired
    LearningService learningService;
    @Override
    @GetMapping("/getmeida/{courseId}/{teachplanId}")
    public GetMediaResult getMedia(@PathVariable("courseId") String courseId,
                                   @PathVariable("teachplanId") String teachPlanId) {
        return learningService.getMedia(courseId,teachPlanId);
    }

}

测试

在这里插入图片描述

前端开发

需求分析

需要在学习中心前端页面需要完成如下功能:
1.进入课程学习页面,需要带上课程Id参数集课程计划Id的参数,其中课程Id参数笔袋,课程计划Id可以为空.
2.进入页面更具课程Id取出该课程的课程计划显示在右侧.
4.进入页面后判断如果请求参数中有课程计划Id的则播放该章节的视频
4.进入页面后判断你如果是课程计划id为0,则需要取出本课程的第一个课程计划的Id,并播放第一个课程计划的视频.

Api方法

配置代理

#学习服务
upstreamlearning_server_pool{server127.0.0.1:40600weight=10;
}
#学习服务
location^~/api/learning/{
proxy_passhttp://learning_server_pool/learning/;}

视频播放页面

1、如果传入的课程计划id为0则取出第一个课程计划id在created钩子方法中完成

created(){
//当前请求的url
this.url=window.location//课程id
this.courseId=this.$route.params.courseId//课程计划id
this.chapter=this.$route.params.chapter//查询课程信息
systemApi.course_view(this.courseId).then((view_course)=>{
if(!view_course||!view_course[this.courseId]){this.$message.error("获取课程信息失败,请重新进入此页面!")return;
}
letcourseInfo=view_course[this.courseId]console.log(courseInfo)
this.coursename=courseInfo.nameif(courseInfo.teachplan){
//将从服务端获取的课程计划json转成对象letteachplan=JSON.parse(courseInfo.teachplan);
//将课程计划赋值给数据模型this.teachplanList=teachplan.children;console.log(this.teachplanList)
if(!this.chapter||this.chapter=='0'){//取出第一个教学计划
this.chapter=this.getFirstTeachplan()console.log(this.chapter)
//开始学习this.study(this.chapter)
}}
})},

取出第一个章节id:

//取出第一个章节getFirstTeachplan(){
for(vari=0;i<this.teachplanList.length;i++){letfirstTeachplan=this.teachplanList[i];
if(firstTeachplan.children&&firstTeachplan.children.length>0){letsecondTeachplan=firstTeachplan.children[0];
returnsecondTeachplan.id;}
}return;
},

开始学习:

//开始学习study(chapter){
//获取播放地址
courseApi.get_media(this.courseId,chapter).then((res)=>{if(res.success){
letfileUrl=sysConfig.videoUrl+res.fileUrl
//播放视频this.playvideo(fileUrl)
}elseif(res.message){this.$message.error(res.message)
}else{this.$message.error("播放视频失败,请刷新页面重试")
}
}).catch(res=>{this.$message.error("播放视频失败,请刷新页面重试")
});},

2、点击右侧课程章节切换播放
在原有代码基础上添加click事件,点击调用开始学习方法(study)。

<liv‐if="teachplan_first.children!=null"v‐for="(teachplan_second,index)inteachplan_first.children"><iclass="glyphiconglyphicon‐check"></i>
<a:href="url"@click="study(teachplan_second.id)">{{teachplan_second.pname}}
</a></li>

测试

访问在线学习页面:http://ucenter.xuecheng.com/#/learning/课程id/课程计划id
通过url传入两个参数:课程id和课程计划id如果没有课程计划则传入0

测试项目如下:1、传入正确的课程id、课程计划id,自动播放本章节的视频
2、传入正确的课程id、课程计划id传入0,自动播放第一个视频
3、传入错误的课程id或课程计划id,提示错误信息。
4、通过右侧章节目录切换章节及播放视频。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/160684.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

我与CSDN相识的一年

一、保持初心&#xff0c;笃定前行&#xff0c;回首2022 1、1 保持初心 回想起与CSDN的相识也是有一年多了。最初的相识也是我刚进入大学的时候。那个时候我还是懵懂无知的一个计算机类的大学生。在老师留下的一次任务中&#xff0c;我发现自己有点解决不了&#xff0c;想上网…

如何处理mybatis处理数据库关系中的多对一关系呢?

测试环境的搭建&#xff1a; 导入lombok&#xff1a; 不懂得小伙伴可移步这篇文章 新建实体类&#xff1a; 拿我们日常生活中最常见的举例&#xff1a;多个学生对应一个老师 对于学生这边而言&#xff0c;关联… 多个学生关联一个老师[多对一] 对于老师而言&#xff0c;集…

BGP(边界网关路由协议)小实验

目录实验要求ospf协议启动关于BGP基本知识点BGP关系建立的配置BGP的宣告实验要求 如下实验拓扑&#xff0c;各个自治系统区域和网段已经标注 基本的ip配置&#xff0c;环回配置就不再展示。 要求&#xff1a;除了R5的环回外&#xff0c;其他环回均可以互相访问 ospf协议启动…

C语言进阶——自定义类型——位段、枚举、联合

结构体 目录 一. 位段 1.概念 2.位段的内存分配 3.位段的跨平台问题 4.位段的应用 二. 枚举 1.枚举类型的定义 2.枚举的优点 3.枚举的使用 三. 联合&#xff08;共用体&#xff09; 1.联合类型的定义 ​编辑 2.联合的特点 3. 联合大小的计算 一. 位段 1.…

Git 合并多条commit

文章目录修改前开始修改第一种方式: 命令行第二种方式: Android Studio遇到冲突的解决办法第一种&#xff1a;修改到底,干就完事了第二种&#xff1a;回滚吧&#xff0c;有点慌修改前 开始修改 第一种方式: 命令行 git rebase -i 01fc32484fb2d2229aa20 // 这里对应的是init的…

osg fbo(四),将颜色缓冲区图片中的牛通过shader变绿

osg fbo&#xff08;三&#xff09;中&#xff0c;把整个屏幕变绿了&#xff0c;因为是把shader添加到了颜色缓冲区图片上了。如果只想把牛变绿&#xff0c;就需要把shader添加到原始场景根中。 即 osg::ref_ptr<osg::StateSet> statset_SceneRoot sceneRoot->getOr…

一、Java框架之Spring配置文件开发

文章目录1. 基础概念1.1 Spring Framework1.2 核心概念产生背景IoC、Bean、DI2. 入门案例2.1 普通Maven项目2.2 IoC入门案例2.3 DI入门案例3. bean配置3.1 bean基础配置bean的基础配置bean的别名配置bean的作用范围3.2 bean实例化实例化方法1&#xff1a;构造方法实例化方法2&a…

Chrome浏览器http访问跨越问题与解决方法

一、Chromium 内核&#xff08;<93版本&#xff09;跨越问题解决方法 设置Chrome浏览器的 disable-web-security, 实现跨域访问后端的接口。这个参数可以降低chrome浏览器的安全性&#xff0c;禁用同源策略&#xff0c;利于开发人员本地调试。 解决办法&#xff1a; 新建一…

【区间合并】AcWing 803. 区间合并

803. 区间合并 文章目录题目描述输入格式&#xff1a;输出格式&#xff1a;数据范围输入样例输出样例方法&#xff1a;区间合并解题思路代码复杂度分析&#xff1a;题目描述 给定 nnn 个区间 [li,ri][l_i,r_i][li​,ri​]&#xff0c;要求合并所有有交集的区间。 注意如果在端…

按键精灵免字库本地识别OCR

按键精灵免字库识别—基于百度飞桨PaddleOCR的RapidOCR前言为什么为什么有大漠了还要使用其它OCR为什么要使用RapidOCR开发PaddleOCR介绍PaddleOCR使用衍生项目版——小白方案按键精灵post调用图片转base64方法转json方法post调用JVM版改为mavenOcrEngine路径idea Run配置网页版…

Hudi系列7:使用SparkSQL操作Hudi

文章目录一. SparkSQL连接Hudi1.1 Hive配置1.2 SparkSQL连接Hudi二. 创建表2.1 常规的建表2.2 CTAS三. 插入数据四. 查询数据五. 更新数据5.1 普通5.2 MergeInto六. 删除数据七. Insert Overwrite参考:一. SparkSQL连接Hudi 1.1 Hive配置 我们需要将Hive 的 metastore服务独立…

【Ubuntu18.04系统开启防火墙】

【Ubuntu18.04系统开启防火墙】1 查看防火墙状态2 开启防火墙3 关闭防火墙4 允许开启防火墙时&#xff0c;ssh连接和22端口许可4.1 允许tcp 22端口通过防火墙4.2 允许SSH服务4.3 防火墙规则重启4.4 验证端口号是否开启1 查看防火墙状态 sudo ufw status2 开启防火墙 sudo ufw…

如何重装windows10系统(超详细图文版)

目录1.&#xff08;制作装机盘&#xff09;准备好装机U盘2. (下载驱动软件&#xff09;(※这步很重要&#xff09;3.&#xff08;下载镜像&#xff09;准备好要安装的新操作系统镜像4.&#xff08;查询bios快捷键&#xff09;查询你的主板品牌&#xff0c;找到你主板品牌进入bi…

医用球囊和导管制造中的精确压力控制

摘要&#xff1a;在医用导管和球囊成型过程中对压力控制有非常严格要求&#xff0c;如高精度和宽量程的控制能力&#xff0c;需具备可编程、自动手动切换和外接压力传感器功能&#xff0c;还需具备可用于球囊泄漏、爆破和疲劳性能测试的多功能性。本文介绍了可满足这些要求的压…

连Pycharm都不知道怎么用,学什么Python?(doge))

python初始设置日常使用一、设置Python 解释器1.1 远程配置2、调整字体及其大小2.1 调整编辑器字体及其大小2.2 调整控制台的字体及其大小3、设置编码4、修改文件背景颜色5、设置Git 和Github5.1 配置git5.2 配置github5.3 下载仓库内容6 、新建.py文件时默认添加信息7、恢复代…

[ 环境搭建篇 ] 安装 java 环境并配置环境变量(附 JDK1.8 安装包)

&#x1f36c; 博主介绍 &#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 _PowerShell &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【数据通信】 【通讯安全】 【web安全】【面试分析】 &#x1f389;点赞➕评论➕收藏 养成习…

Demo演示:ARM+FPGA主流嵌入式架构板卡-HDMI显示摄像画面

各位工程师小伙伴们&#xff0c;大家好&#xff0c;ARMFPGA 作为一种主流的嵌入式系统的处理架构。相对于单纯的的ARM开发或单纯的FPGA开发&#xff0c;ARM加FPGA能够带来功耗、性能、成本等组合优势。 米尔新推出的MYD-JX8MMXA7开发板基于ARMFPGA架构&#xff0c;集成i.MX 8M…

多线程理解之3

1.怎么解决多线程并发访问临界资源的产生的问题&#xff1f; 用锁 pthread_mutex_t mtx解决。 2.mtx锁的作用具体是什么&#xff1f; 先把临界资源锁起来&#xff0c;再把它打开&#xff0c;这样一来&#xff0c;多个执行流想要同时执行临界资源就不可以了&#xff0c;只能一个…

[C++]string的使用及模拟实现

&#x1f941;作者&#xff1a; 华丞臧 &#x1f4d5;​​​​专栏&#xff1a;【C】 各位读者老爷如果觉得博主写的不错&#xff0c;请诸位多多支持(点赞收藏关注)。如果有错误的地方&#xff0c;欢迎在评论区指出。 推荐一款刷题网站 &#x1f449;LeetCode 文章目录一、stri…

Android Compose——Paging3

Paging3效果视频简述HiltRetrofit访问接口网络实例PagingSourceViewModelView效果视频 简述 本Demo采用HiltRetrofitPaging3完成&#xff0c;主要为了演示paging3分页功能的使用&#xff0c;下列为Demo所需要的相关依赖 //retrofitimplementation com.squareup.retrofit2:retr…