學成在線(第15天)

學習頁面查詢課程計劃

到目前爲止,咱們已能夠編輯課程計劃信息並上傳課程視頻,下一步咱們要實如今線學習頁面動態讀取章節對應的
視頻並進行播放。在線學習頁面所須要的信息有兩類:一類是課程計劃信息、一類是課程學習信息(視頻地址、學
習進度等),以下圖:前端

在線學習集成媒資管理的需求以下:
一、在線學習頁面顯示課程計劃
二、點擊課程計劃播放該課程計劃對應的視頻java

Api 接口

課程計劃信息從哪裏獲取?
目前課程計劃信息在課程管理數據庫和ES索引庫中存在,考慮性能要求,課程發佈後對課程的查詢統一從ES索引庫
中查詢。
前端經過請求搜索服務獲取課程信息,須要單獨在搜索服務中定義課程信息查詢接口。
本接口接收課程id,查詢課程全部信息返回給前端。mysql

@ApiOperation("根據id查詢課程信息")
public Map<String,CoursePub> getall(String id);

返回的課程信息爲json結構:key爲課程id,value爲課程內容。spring

 服務端開發

在搜索服務中開發查詢課程信息接口。sql

Service

在搜索服務中增長查詢課程信息接口的service數據庫

public Map<String, CoursePub> getall(String id) {
        //設置索引庫
        SearchRequest searchRequest = new SearchRequest(es_index);
        //設置類型
        searchRequest.types(es_type);
        SearchSourceBuilder searchSourceBuilder new SearchSourceBuilder();
        //查詢條件,根據課程id查詢
        searchSourceBuilder.query(QueryBuilders.termsQuery("id", id));
        //取消source源字段過慮,查詢全部字段
//        searchSourceBuilder.fetchSource(new String[]{"name", "grade", "charge","pic"}, new
String[]{});
        searchRequest.source(searchSourceBuilder);
        SearchResponse searchResponse null;
        try {
            //執行搜索
            searchResponse = restHighLevelClient.search(searchRequest);
        } catch (IOException e) {
            e.printStackTrace();
        }
        //獲取搜索結果
        SearchHits hits = searchResponse.getHits();
        SearchHit[] searchHits = hits.getHits();
        Map<String,CoursePub> map = new HashMap<>();
        for (SearchHit hit : searchHits) {
            String courseId = hit.getId();
            Map<String, Object> sourceAsMap = hit.getSourceAsMap();
            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 coursePub new CoursePub();
            coursePub.setId(courseId);
            coursePub.setName(name);
            coursePub.setPic(pic);
 coursePub.setGrade(grade);
            coursePub.setTeachplan(teachplan);
            coursePub.setDescription(description);
            map.put(courseId,coursePub);
        }
        return map;
    }
View Code

Controller

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

  學習頁面獲取視頻播放地址

 需求分析

用戶進入在線學習頁面,點擊課程計劃將播放該課程計劃對應的教學視頻。
業務流程以下:json

業務流程說明:
一、用戶進入在線學習頁面,頁面請求搜索服務獲取課程信息(包括課程計劃信息)而且在頁面展現。
二、在線學習請求學習服務獲取視頻播放地址。
三、學習服務校驗當前用戶是否有權限學習,若是沒有權限學習則提示用戶。
四、學習服務校驗經過,請求搜索服務獲取課程媒資信息。
五、搜索服務請求ElasticSearch獲取課程媒資信息。api

課程發佈存儲媒資信息

需求分析

課程媒資信息是在課程發佈的時候存入ElasticSearch索引庫,由於課程發佈後課程信息將基本再也不修改,具體的
業務流程以下。app

業務流程以下:
一、課程發佈,向課程媒資信息表寫入數據。
1)根據課程id刪除teachplanMediaPub中的數據
2)根據課程id查詢teachplanMedia數據
3)將查詢到的teachplanMedia數據插入到teachplanMediaPub中
二、Logstash定時掃描課程媒資信息表,並將課程媒資信息寫入索引庫。elasticsearch

數據模型

在xc_course數據庫建立課程計劃媒資發佈表:

CREATE TABLE `teachplan_media_pub` (
  `teachplan_id` varchar(32NOT NULL COMMENT '課程計劃id',
  `media_id` varchar(32NOT NULL COMMENT '媒資文件id',
  `media_fileoriginalname` varchar(128NOT NULL COMMENT '媒資文件的原始名稱',
  `media_url` varchar(256NOT NULL COMMENT '媒資文件訪問地址',
  `courseid` varchar(32NOT NULL COMMENT '課程Id',
  `timestamptimestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT
'logstash使用',
  PRIMARY KEY (`teachplan_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

數據模型類以下:

@Data
@ToString
@Entity
@Table(name="teachplan_media_pub")
@GenericGenerator(name = "jpa‐assigned", strategy = "assigned")
public class TeachplanMediaPub implements Serializable {
    private static final long serialVersionUID = ‐916357110051689485L;
    @Id
    @GeneratedValue(generator = "jpa‐assigned")
    @Column(name="teachplan_id")
    private String teachplanId;
    @Column(name="media_id")
    private String mediaId;
    @Column(name="media_fileoriginalname")
    private String mediaFileOriginalName;
    @Column(name="media_url")
    private String mediaUrl;
    @Column(name="courseid")
    private String courseId;
    @Column(name="timestamp")
    private Date timestamp;//時間戳
}
View Code

Dao

建立TeachplanMediaPub表的Dao,向TeachplanMediaPub存儲信息採用先刪除該課程的媒資信息,再添加該課
程的媒資信息,因此這裏定義根據課程id刪除課程計劃媒資方法:

public interface TeachplanMediaPubRepository extends JpaRepository<TeachplanMediaPub, String> {
    //根據課程id刪除課程計劃媒資信息
    long deleteByCourseId(String courseId);
}
//從TeachplanMedia查詢課程計劃媒資信息
public interface TeachplanMediaRepository extends JpaRepository<TeachplanMedia, String> {
    List<TeachplanMedia> findByCourseId(String courseId);
}

Service

編寫保存課程計劃媒資信息方法,並在課程發佈時調用此方法。
一、保存課程計劃媒資信息方法
本方法採用先刪除該課程的媒資信息,再添加該課程的媒資信息。

//保存課程計劃媒資信息
private void saveTeachplanMediaPub(String courseId){
    //查詢課程媒資信息
    List<TeachplanMedia> teachplanMediaList = teachplanMediaRepository.findByCourseId(courseId);
    //將課程計劃媒資信息存儲待索引表
    teachplanMediaPubRepository.deleteByCourseId(courseId);
    List<TeachplanMediaPub> teachplanMediaPubList = new ArrayList<>();
    for(TeachplanMedia teachplanMedia:teachplanMediaList){
        TeachplanMediaPub teachplanMediaPub =new TeachplanMediaPub();
        BeanUtils.copyProperties(teachplanMedia,teachplanMediaPub);
        teachplanMediaPubList.add(teachplanMediaPub);
    }
    teachplanMediaPubRepository.saveAll(teachplanMediaPubList);
}

二、課程發佈時調用此方法
修改課程發佈的service方法:

....
//保存課程計劃媒資信息到待索引表
saveTeachplanMediaPub(courseId);
//頁面url
String pageUrl = cmsPostPageResult.getPageUrl();
return new CoursePublishResult(CommonCode.SUCCESS,pageUrl);
.....

Logstash 掃描課程計劃媒資

建立索引

一、建立xc_course_media索引
二、並向此索引建立以下映射

Post http://localhost:9200/xc_course_media/doc/_mapping

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

建立Logstash模板文件

在logstach的config目錄建立xc_course_media_template.json,內容以下:

{
   "mappings" : {
      "doc" : {
         "properties" : {
           "courseid" : {
               "type" : "keyword"
            },
            "teachplan_id" : {
               "type" : "keyword"
            },
            "media_id" : {
               "type" : "keyword"
            },
            "media_url" : {
               "index" : false,
               "type" : "text"
            },
            "media_fileoriginalname" : {
               "index" : false,
               "type" : "text"
            }
      }
   },
   "template" : "xc_course_media"
}

配置mysql.conf

在logstash的config目錄下配置mysql_course_media.conf文件供logstash使用,logstash會根據
mysql_course_media.conf文件的配置的地址從MySQL中讀取數據向ES中寫入索引。

配置輸入數據源和輸出數據源。

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 => mysql
  # the path to our downloaded jdbc driver 
  jdbc_driver_library => "F:/develop/maven/repository3/mysql/mysql‐connector‐java/5.1.41/mysql‐
connector‐java‐5.1.41.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:/ElasticSearch/logstash‐6.2.1/config/xc_course_media_metadata"
  }
}
output {
  elasticsearch {
  #ES的ip地址和端口
  hosts => "localhost:9200"
  #hosts => ["localhost:9200","localhost:9202","localhost:9203"]
  #ES索引庫名稱
  index => "xc_course_media"
  document_id => "%{id}"
  document_type => "doc"
  template =>"D:/ElasticSearch/logstash‐6.2.1/config/xc_course_media_template.json"
  template_name =>"xc_course_media"
  template_overwrite =>"true"
  }
  stdout {
 #日誌輸出
  codec => json_lines
  }
}
View Code

啓動logstash.bat

啓動logstash.bat採集teachplan_media_pub中的數據,向ES寫入索引。

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

 在線學習接口

需求分析

根據下邊的業務流程,本章節完成前端學習頁面請求學習服務獲取課程視頻地址,並自動播放視頻。

 搭建開發環境

 建立數據庫

建立xc_learning數據庫,學習數據庫將記錄學生的選課信息、學習信息。
導入:資料/xc_learning.sql

Api 接口

此api接口是課程學習頁面請求學習服務獲取課程學習地址。

定義返回值類型:

@Data
@ToString
@NoArgsConstructor
public class GetMediaResult extends ResponseResult {
    public GetMediaResult(ResultCode resultCode, String fileUrl) {
        super(resultCode);
        this.fileUrl = fileUrl;
    }
    //媒資文件播放地址
    private String fileUrl;
}

定義接口,學習服務根據傳入課程ID、章節Id(課程計劃ID)來取學習地址。

@Api(value = "錄播課程學習管理",description = "錄播課程學習管理")
public interface CourseLearningControllerApi {
    @ApiOperation("獲取課程學習地址")
    public GetMediaResult getmedia(String courseId,String teachplanId);
}

服務端開發

搜索服務註冊Eureka

學習服務要調用搜索服務查詢課程媒資信息,因此須要將搜索服務註冊到eureka中。
一、查看服務名稱是否爲xc-service-search

注意修改application.xml中的服務名稱:
spring:
  application:
    name: xc‐service‐search

二、配置搜索服務的配置文件application.yml,加入Eureka配置 以下:

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: #最大重試次數,當Eureka中能夠找到服務,可是服務連不上時將會重試,若是eureka中找不
到服務則直接走斷路器
  MaxAutoRetriesNextServer: #切換實例的重試次數
  OkToRetryOnAllOperations: false  #對全部操做請求都進行重試,若是是get則能夠,若是是post,put等操做
沒有實現冪等的狀況下是很危險的,因此設置爲false
  ConnectTimeout: 5000  #請求鏈接的超時時間
  ReadTimeout: 6000 #請求處理的超時時間

3 、添加eureka依賴:

<!‐‐  導入Eureka客戶端的依賴 ‐‐>
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring‐cloud‐starter‐netflix‐eureka‐client</artifactId>
</dependency>

四、修改啓動類,在class上添加以下註解:

@EnableDiscoveryClient

搜索服務客戶端

在學習服務建立搜索服務的客戶端接口,此接口會生成代理對象,調用搜索服務:

package com.xuecheng.learning.client; 
import com.xuecheng.api.search.EsCourseControllerApi;
import com.xuecheng.framework.client.XcServiceList;
import org.springframework.cloud.netflix.feign.FeignClient;
@FeignClient(value = "xc‐service‐search")
public interface CourseSearchClient {
  @GetMapping(value="/getmedia/{teachplanId}")
  public TeachplanMediaPub getmedia(@PathVariable("teachplanId") String teachplanId);
}

Service

在學習服務中定義service方法,此方法遠程請求課程管理服務、媒資管理服務獲取課程學習地址。

@Service 
public class LearningService {
    @Autowired
    CourseSearchClient courseSearchClient;
    //獲取課程
    public GetMediaResult getMedia(String courseId, String teachplanId) {
        //校驗學生的學習權限。。是否資費等
        //調用搜索服務查詢
        TeachplanMediaPub teachplanMediaPub = courseSearchClient.getmedia(teachplanId);
        if(teachplanMediaPub == null || StringUtils.isEmpty(teachplanMediaPub.getMediaUrl())){
            //獲取視頻播放地址出錯
            ExceptionCast.cast(LearningCode.LEARNING_GETMEDIA_ERROR);
        }
        return new GetMediaResult(CommonCode.SUCCESS,teachplanMediaPub.getMediaUrl());
    }
}

Controller

調用service根據課程計劃id查詢視頻播放地址:

@RestController 
@RequestMapping("/learning/course")
public class CourseLearningController implements CourseLearningControllerApi {
    @Autowired
    LearningService learningService;
    @Override
    @GetMapping("/getmedia/{courseId}/{teachplanId}")
    public GetMediaResult getmedia(@PathVariable String courseId, @PathVariable String
teachplanId) {
        //獲取課程學習地址
        return learningService.getMedia(courseId, teachplanId);
    }
}

前端開發

須要在學習中心前端頁面須要完成以下功能:
一、進入課程學習頁面須要帶上課程Id參數及課程計劃Id的參數,其中課程Id參數必帶,課程計劃Id能夠爲空。
二、進入頁面根據課程Id取出該課程的課程計劃顯示在右側。
三、進入頁面後判斷若是請求參數中有課程計劃Id則播放該章節的視頻。
四、進入頁面後判斷若是課程計劃id爲0則須要取出本課程第一個課程計劃的Id,並播放第一個課程計劃的視頻。

api方法

let sysConfig = require('@/../config/sysConfig')
let apiUrl = sysConfig.xcApiUrlPre;
/*獲取播放地址*/
export const get_media = (courseId,chapter) => {
  return http.requestGet(apiUrl+'/api/learning/course/getmedia/'+courseId+'/'+chapter);
}

配置代理

在Nginx中的ucenter.xuecheng.com虛擬主機中配置/api/learning/的路徑轉發,此url請轉發到學習服務。

#學習服務
upstream learning_server_pool{
server 127.0.0.1:40600 weight=10;    
}  
#學習服務
location ^~ /api/learning/ { 
proxy_pass http://learning_server_pool/learning/;      
}

視頻播放頁面

一、若是傳入的課程計劃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 ;
          }
    
          let courseInfo = view_course[this.courseId]
          console.log(courseInfo)
          this.coursename = courseInfo.name
          if(courseInfo.teachplan){
            //將從服務端獲取的課程計劃json轉成對象
            let teachplan = 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)
            }
          }
      })
    },
View Code

取出第一個章節id:

//取出第一個章節
      getFirstTeachplan(){
        for(var i=0;i<this.teachplanList.length;i++){
            let firstTeachplan this.teachplanList[i];
            if(firstTeachplan.children && firstTeachplan.children.length>0){
              let secondTeachplan = firstTeachplan.children[0];
              return secondTeachplan.id;
            }
          }
        return ;
      },

開始學習:

 //開始學習
      study(chapter){
        // 獲取播放地址
        courseApi.get_media(this.courseId,chapter).then((res)=>{
          if(res.success){
            let fileUrl = sysConfig.videoUrl + res.fileUrl
            //播放視頻
            this.playvideo(fileUrl)
          }else if(res.message){
            this.$message.error(res.message)
          }else{
            this.$message.error("播放視頻失敗,請刷新頁面重試")
          }
        }).catch(res=>{
          this.$message.error("播放視頻失敗,請刷新頁面重試")
        });
      },

二、點擊右側課程章節切換播放
在原有代碼基礎上添加click事件,點擊調用開始學習方法(study)。

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

測試

訪問在線學習頁面:http://ucenter.xuecheng.com/#/learning/課程 id/課程計劃id
經過url傳入兩個參數:課程id和課程計劃id
若是沒有課程計劃則傳入0

測試項目以下:
一、傳入正確的課程id、課程計劃id,自動播放本章節的視頻
二、傳入正確的課程id、課程計劃id傳入0,自動播放第一個視頻
三、傳入錯誤的課程id或課程計劃id,提示錯誤信息。

4 、經過右側章節目錄切換章節及播放視頻。

 

 

相關文章
相關標籤/搜索