到目前爲止,咱們已能夠編輯課程計劃信息並上傳課程視頻,下一步咱們要實如今線學習頁面動態讀取章節對應的
視頻並進行播放。在線學習頁面所須要的信息有兩類:一類是課程計劃信息、一類是課程學習信息(視頻地址、學
習進度等),以下圖:前端
在線學習集成媒資管理的需求以下:
一、在線學習頁面顯示課程計劃
二、點擊課程計劃播放該課程計劃對應的視頻java
課程計劃信息從哪裏獲取?
目前課程計劃信息在課程管理數據庫和ES索引庫中存在,考慮性能要求,課程發佈後對課程的查詢統一從ES索引庫
中查詢。
前端經過請求搜索服務獲取課程信息,須要單獨在搜索服務中定義課程信息查詢接口。
本接口接收課程id,查詢課程全部信息返回給前端。mysql
@ApiOperation("根據id查詢課程信息") public Map<String,CoursePub> getall(String id);
返回的課程信息爲json結構:key爲課程id,value爲課程內容。spring
在搜索服務中開發查詢課程信息接口。sql
在搜索服務中增長查詢課程信息接口的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; }
@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(32) NOT NULL COMMENT '課程計劃id', `media_id` varchar(32) NOT NULL COMMENT '媒資文件id', `media_fileoriginalname` varchar(128) NOT NULL COMMENT '媒資文件的原始名稱', `media_url` varchar(256) NOT NULL COMMENT '媒資文件訪問地址', `courseid` varchar(32) NOT NULL COMMENT '課程Id', `timestamp` timestamp 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;//時間戳 }
建立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); }
編寫保存課程計劃媒資信息方法,並在課程發佈時調用此方法。
一、保存課程計劃媒資信息方法
本方法採用先刪除該課程的媒資信息,再添加該課程的媒資信息。
//保存課程計劃媒資信息 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); .....
一、建立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" } } }
在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" }
在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 } }
啓動logstash.bat採集teachplan_media_pub中的數據,向ES寫入索引。
logstash.bat ‐f ../config/mysql_course_media.conf
根據下邊的業務流程,本章節完成前端學習頁面請求學習服務獲取課程視頻地址,並自動播放視頻。
建立xc_learning數據庫,學習數據庫將記錄學生的選課信息、學習信息。
導入:資料/xc_learning.sql
此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中。
一、查看服務名稱是否爲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: 2 #最大重試次數,當Eureka中能夠找到服務,可是服務連不上時將會重試,若是eureka中找不 到服務則直接走斷路器 MaxAutoRetriesNextServer: 3 #切換實例的重試次數 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 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()); } }
調用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,並播放第一個課程計劃的視頻。
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) } } }) },
取出第一個章節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"><i class="glyphicon glyphicon‐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
測試項目以下:
一、傳入正確的課程id、課程計劃id,自動播放本章節的視頻
二、傳入正確的課程id、課程計劃id傳入0,自動播放第一個視頻
三、傳入錯誤的課程id或課程計劃id,提示錯誤信息。
4 、經過右側章節目錄切換章節及播放視頻。