微服务[学成在线] day15:媒资管理系统集成

避开PSRR仿真三大坑:用Cadence psspxf分析分频器时,这些设置错了白忙活 本文深入探讨了使用Cadence psspxf工具进行分频器PSRR仿真时常见的三大技术陷阱,包括扰动信号参数设置、收敛性优化和后处理信号提取。通过详细的参数推荐和实战技巧,帮助工程师避免仿真误差,提升PSRR分析效率与准确性,特别适用于16分频器等数字-模拟混合电路设计。 阅读详情

😎 知识点概览

为了方便后续回顾该项目时能够清晰的知道本章节讲了哪些内容,并且能够从该章节的笔记中得到一些帮助,所以在完成本章节的学习后在此对本章节所涉及到的知识点进行总结概述。

本章节为【学成在线】项目的 day15 的内容

  • 根据 课程ID 搜索该课程已发布的课程信息,并返回该课程的所有课程计划信息。
  • 将指定课程 发布时 所的课程计划的媒资信息保存到 teachplan_media_publish 表中,
  • 根据 课程计划id 搜索该课程计划所对应的媒资信息,需要用到的是该课程计划对应的 m3u8 地址,用于在线播放视频,该接口在课程管理服务中开发,供学习服务进行远程调用。
  • 在学习服务中远程调用 课程计划媒资信息查询接口,获取该课程计划的视频播放的 m3u8 url地址,并返回给前端,前端使用该 url 进行视频的在线播放。
  • 在线学习完整的测试流程:媒资信息的上传、选择、发布到前端门户、搜索门户测试,在线学习的播放视频。

目录

内容会比较多,小伙伴门可以根据目录进行按需查阅。

一、学习页面:查询课程计划

0x01 需求分析

到目前为止,我们已可以编辑课程计划信息并上传课程视频,下一步我们要实现在线学习页面动态读取章节对应的视频并进行播放。在线学习页面所需要的信息有两类:

  • 课程计划信息
  • 课程学习信息(视频地址、学习进度等)

如下图:

在线学习集成媒资管理的需求如下:

1、在线学习页面显示课程计划

2、点击课程计划播放该课程计划对应的视频

本章节实现学习页面动态显示课程计划,进入不同课程的学习页面右侧动态显示当前课程的课程计划。

0x02 Api接口

课程计划信息从哪里获取?

在课程发布完成后会自动发布到一个 course_pub 的表中,logstash 会自动将课程发布后的信息自动采集到 ES 索引库中,这些信息也包含课程计划信息。

所以考虑性能要求,课程发布后对课程的查询统一从 ES 索引库中查询。

前端通过请求 搜索服务 获取课程信息,需要单独在 搜索服务 中定义课程信息查询接口。
本接口接收课程id,查询课程所有信息返回给前端。

我们在搜素服务 API 下添加以下方法

@ApiOperation("根据id搜索课程发布信息")
public Map<String,CoursePub> getdetail(String id);

返回的课程信息为 json 结构:key 为课程id,value 为课程内容。

0x03 服务端开发

在搜索服务中开发查询课程信息接口。

Controller

在搜素服务下添加以下方法

/**
     * 根据id搜索课程发布信息
     * @param id 课程id
     * @return JSON数据
     */
@Override
@GetMapping("/getdetail/{id}")
public Map<String, CoursePub> getdetail(@PathVariable("id")String id) {
    return esCourseService.getdetail(id);
}

Service

/**
     * 根据id搜索课程发布信息
     * @param id 课程id
     * @return JSON数据
     */
public Map<String, CoursePub> getdetail(String id) {
    //设置索引
    SearchRequest searchRequest = new SearchRequest(es_index);
    //设置类型
    searchRequest.types(es_type);
    //创建搜索源对象
    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

    //设置查询条件,根据id进行查询
    searchSourceBuilder.query(QueryBuilders.termQuery("id",id));
    //这里不使用source的原字段过滤,查询所有字段
    // searchSourceBuilder.fetchSource(new String[]{"name", "grade", "charge","pic"}, newString[]{});

    //设置搜索源对象
    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) {
        //从搜索结果中取值并添加到coursePub对象
        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对象
        map.put(courseId,coursePub);
    }
    return map;
}

测试

使用 swagger-ui 或 postman 测试查询课程信息接口。

0x04 前端开发

配置NGINX虚拟主机

学习中心的二级域名为 ucenter.xuecheng.com ,我们在 nginx 中配置 ucenter 虚拟主机。

#学成网用户中心
server {
    listen 80;
    server_name ucenter.xuecheng.com;
    #个人中心
    location / {
        proxy_pass http://ucenter_server_pool;
    }
} 
#前端ucenter
upstream ucenter_server_pool{
    #server 127.0.0.1:7081 weight=10;
    server 127.0.0.1:13000 weight=10;
}

在学习中心要调用搜索的 API,使用 Nginx 解决代理,如下图:

在 ucenter 虚拟主机下配置搜索 Api 代理路径

#后台搜索(公开api)
upstream search_server_pool{
    server 127.0.0.1:40100 weight=10;
} 
#学成网用户中心
server {
    listen 80;
    server_name ucenter.xuecheng.com;
    #个人中心
        location / {
        proxy_pass http://ucenter_server_pool;
    }
    #后端搜索服务
        location /openapi/search/ {
        proxy_pass http://search_server_pool/search/;
    }
} 

前端 API 方法

在学习中心 xc-ui-pc-leanring 对课程信息的查询属于基础常用功能,所以我们将课程查询的 api 方法定义在base 模块下,如下图:

在system.js 中定义课程查询方法:

import http from './public'
export const course_view = id => {
	return http.requestGet('/openapi/search/course/getdetail/'+id);
}

前端 API 方法调用

在 learning_video.vue 页面中调用课程信息查询接口得到课程计划,将课程计划json 串转成对象。

xc-ui-pc-leanring/src/module/course/page/learning_video.vue

1、定义视图

课程计划

<!--课程计划部分代码-->
<div class="navCont">
    <div class="course-weeklist">
        <div class="nav nav-stacked" v-for="(teachplan_first, index) in teachplanList">
            <div class="tit nav-justified text-center"><i class="pull-left glyphicon glyphicon-th-list"></i>{{teachplan_first.pname}}<i class="pull-right"></i></div>
            <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>

            <!-- <div class="tit nav-justified text-center"><i class="pull-left glyphicon glyphicon-th-list"></i>第一章<i class="pull-right"></i></div>
<li  ><i class="glyphicon glyphicon-check"></i>
<a :href="url" >
第一节
</a>
</li>-->
            <!--<li><i class="glyphicon glyphicon-unchecked"></i>为什么分为A、B、C部分</li>-->
        </div>
    </div>
</div>

课程名称

<div class="top text-center">
{{coursename}}
</div>

定义数据对象

data() {
    return {
        url:'',//当前url
        courseId:'',//课程id
        chapter:'',//章节Id
        coursename:'',//课程名称
        coursepic:'',//课程图片
        teachplanList:[],//课程计划
        playerOptions: {//播放参数
            autoplay: false,
            controls: true,
            sources: [{
                type: "application/x-mpegURL",
                src: ''
            }]
        },
    }
}

在 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){
            let teachplan = JSON.parse(courseInfo.teachplan);
            this.teachplanList = teachplan.children;
        }
    })
},

测试

在浏览器请求:http://ucenter.xuecheng.com/#/learning/4028e581617f945f01617f9dabc40000/0

  • 4028e581617f945f01617f9dabc40000:第一个参数为课程 id,测试时从 ES索引库找一个课程 id
  • 0:第二个参数为课程计划 id,此参数用于点击课程计划播放视频。

如果出现跨域问题,但是确定已经配置了跨域,请尝试结束所以 nginx.exe 的进程 和 清空浏览器缓存。

如果还没有解决?重启电脑试试。

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

0x01 需求分析

用户进入在线学习页面,点击课程计划将播放该课程计划对应的教学视频。

业务流程如下:

业务流程说明:

1、用户进入在线学习页面,页面请求搜索服务获取课程信息(包括课程计划信息)并且在页面展示。

2、在线学习请求学习服务获取视频播放地址。

3、学习服务校验当前用户是否有权限学习,如果没有权限学习则提示用户。

4、学习服务校验通过,请求搜索服务获取课程媒资信息。

5、搜索服务请求ElasticSearch获取课程媒资信息。

为什么要请求 ElasticSearch 查询课程媒资信息?

出于性能的考虑,公开查询课程信息从搜索服务查询,分摊 mysql 数据库的访问压力。

什么时候将课程媒资信息存储到 ElasticSearch 中?

课程媒资信息是在课程发布的时候存入 ElasticSearch,因为课程发布后课程信息将基本不再修改。

0x02 课程发布:储存媒资信息

需求分析

课程媒资信息是在课程发布的时候存入 ElasticSearch 索引库,因为课程发布后课程信息将基本不再修改,具体的业务流程如下。

1、课程发布,向课程媒资信息表写入数据。

1)根据课程 id 删除 teachplanMediaPub 中的数据

2)根据课程 id 查询 teachplanMedia 数据

3)将查询到的 teachplanMedia 数据插入到 teachplanMediaPub 中

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

数据模型

在 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

数据模型类如下:

package com.xuecheng.framework.domain.course;

import lombok.Data;
import lombok.ToString;
import org.hibernate.annotations.GenericGenerator;

import javax.persistence.*;
import java.io.Serializable;
import java.util.Date;

@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;//时间戳
}

Dao

创建 TeachplanMediaPub 表的 Dao,向 TeachplanMediaPub 存储信息采用先删除该课程的媒资信息,再添加该课程的媒资信息,所以这里定义根据课程 id 删除课程计划媒资方法:

public interface TeachplanMediaPubRepository extends JpaRepository<TeachplanMediaPub, String> {
    //根据课程id删除课程计划媒资信息
    long deleteByCourseId(String courseId);
} 

从TeachplanMedia查询课程计划媒资信息

//从TeachplanMedia查询课程计划媒资信息
public interface TeachplanMediaRepository extends JpaRepository<TeachplanMedia, String> {
    List<TeachplanMedia> findByCourseId(String courseId);
}

Service

编写保存课程计划媒资信息方法,并在课程发布时调用此方法。

1、保存课程计划媒资信息方法

本方法采用先删除该课程的媒资信息,再添加该课程的媒资信息,在 CourseService 下定义该方法

//保存课程计划媒资信息
private void saveTeachplanMediaPub(String courseId){
    //查询课程媒资信息
    List<TeachplanMedia> byCourseId = teachplanMediaRepository.findByCourseId(courseId);
    if(byCourseId == null) return;  //没有查询到媒资数据则直接结束该方法

    //将课程计划媒资信息储存到待索引表

    //删除原有的索引信息
    teachplanMediaPubRepository.deleteByCourseId(courseId);
    //一个课程可能会有多个媒资信息,遍历并使用list进行储存
    List<TeachplanMediaPub> teachplanMediaPubList = new ArrayList<>();
    for (TeachplanMedia teachplanMedia: byCourseId) {
        TeachplanMediaPub teachplanMediaPub = new TeachplanMediaPub();
        BeanUtils.copyProperties(teachplanMedia, teachplanMediaPub);
        teachplanMediaPubList.add(teachplanMediaPub);
    }
    //保存所有信息
    teachplanMediaPubRepository.saveAll(teachplanMediaPubList);
}

2、课程发布时调用此方法

修改课程发布的 coursePublish 方法:

....
//保存课程计划媒资信息到待索引表
saveTeachplanMediaPub(courseId);
//页面url
String pageUrl = cmsPostPageResult.getPageUrl();
return new CoursePublishResult(CommonCode.SUCCESS,pageUrl);
.....

测试

测试课程发布后是否成功将课程媒资信息存储到 teachplan_media_pub 中,测试流程如下:

1、指定一个课程

2、为课程计划添加课程媒资

3、执行课程发布

4、观察课程计划媒资信息是否存储至 teachplan_media_pub 中

注意:由于此测试仅用于测试发布课程计划媒资信息的功能,可暂时将 cms页面发布的功能暂时屏蔽,提高测试效率。

测试结果如下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Vrzs5589-1595567273126)(https://qnoss.codeyee.com/20200704_15/image7)]

0x03 Logstash:扫描课程计划媒资

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

创建索引

1、创建 xc_course_media 索引

2、并向此索引创建如下映射

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

文件路径为 %ES_ROOT_DIR%/logstash6.8.8/config/xc_course_media_template.json

%ES_ROOT_DIR% 为 ElasticSearch 和 logstash 的安装目录

内容如下:

{
    "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 中写入索引。

参考https://www.elastic.co/guide/en/logstash/current/plugins-inputs-jdbc.html

配置输入数据源和输出数据源。

input {
    stdin {} 
	jdbc {
		jdbc_connection_string => "jdbc:mysql://localhost:3306/xc_course?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC"
        # 数据库信息
        jdbc_user => "root"
        jdbc_password => "123123"
        # MYSQL 驱动地址,修改为maven仓库对应的位置
        jdbc_driver_library => "D:/soft/apache-maven-3.5.4/repository/mysql/mysql-connector-java/5.1.40/mysql-connector-java-5.1.40.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:/soft/elasticsearch/logstash-6.8.8/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 => "%{teachplan_id}"
		document_type => "doc"
		template => "D:/soft/elasticsearch/logstash-6.8.8/config/xc_course_media_template.json"
		template_name =>"xc_course_media"
		template_overwrite =>"true"
	} 
	stdout {
		#日志输出
		codec => json_lines
	}
}

启动 logstash.bat

启动 logstash.bat 采集 teachplan_media_pub 中的数据,向 ES 写入索引。

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

课程发布成功后,Logstash 会自动参加 teachplan_media_pub 表中新增的数据,效果如下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ILPBxfXi-1595567273134)(https://qnoss.codeyee.com/20200704_15/image10)]

Logstash多实例运行

由于之前我们还启动了一个 Logstash 对课程的发布信息进行采集,所以如果想两个 logstash 实例同时运行,因为每个实例都有一个.lock文件,所以不能使用同一个目录来存放数据,所以我们需要使用 --path.data= 为每个实例指定单独的数据目录,具体的代码如下:

该配置是在windows下进行的

课程发布实例

logstash_start_course_pub.bat

@title logstash in course_pub
logstash.bat -f ..\config\mysql.conf --path.data=../data/course_pub

课程计划媒体发布实例

logstash_start_teachplan_media.bat

@title logstash i n teachplan_media_pub
logstash.bat -f ../config/mysql_course_media.conf --path.data=../data/teachplan_media/

同时运行效果如下

0x04 搜素服务:查询课程媒资接口

需求分析

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

Api接口定义

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

Service

1、配置课程计划媒资索引库等信息

在 application.yml 中配置

xuecheng:
  elasticsearch:
    hostlist: ${eshostlist:127.0.0.1:9200} #多个结点中间用逗号分隔
    course:
      index: xc_course
      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_course_media
      type: doc
      source_field: courseid,media_id,media_url,teachplan_id,media_fileoriginalname

2、service 方法开发

在 课程搜索服务 中定义课程媒资查询接口,为了适应后续需求,service 参数定义为数组,可一次查询多个课程计划的媒资信息。

 /**
     * 根据一个或者多个课程计划id查询媒资信息
     * @param teachplanIds 课程id
     * @return QueryResponseResult
     */
    public QueryResponseResult<TeachplanMediaPub> getmedia(String [] teachplanIds){
        //设置索引
        SearchRequest searchRequest = new SearchRequest(media_index);

        //设置类型
        searchRequest.types(media_type);

        //创建搜索源对象
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        
        //源字段过滤
        String[] media_index_arr = media_field.split(",");
        searchSourceBuilder.fetchSource(media_index_arr, new String[]{});

        //查询条件,根据课程计划id查询(可以传入多个课程计划id)
        searchSourceBuilder.query(QueryBuilders.termsQuery("teachplan_id", teachplanIds));

        searchRequest.source(searchSourceBuilder);
        SearchResponse searchResponse = null;
        try {
            searchResponse = restHighLevelClient.search(searchRequest);
        } catch (IOException e) {
            e.printStackTrace();
        }
        //获取结果
        SearchHits hits = searchResponse.getHits();
        long totalHits = hits.getTotalHits();
        SearchHit[] searchHits = hits.getHits();

        //数据列表
        List<TeachplanMediaPub> teachplanMediaPubList = new ArrayList<>();

        for(SearchHit hit:searchHits){
            TeachplanMediaPub teachplanMediaPub =new TeachplanMediaPub();
            Map<String, Object> sourceAsMap = hit.getSourceAsMap();
            //取出课程计划媒资信息
            String courseid = (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(courseid);
            teachplanMediaPub.setMediaUrl(media_url);
            teachplanMediaPub.setMediaFileOriginalName(media_fileoriginalname);
            teachplanMediaPub.setMediaId(media_id);
            teachplanMediaPub.setTeachplanId(teachplan_id);
            //将对象加入到列表中
            teachplanMediaPubList.add(teachplanMediaPub);
        }

        //构建返回课程媒资信息对象
        QueryResult<TeachplanMediaPub> queryResult = new QueryResult<>();
        queryResult.setList(teachplanMediaPubList);
        queryResult.setTotal(totalHits);
        return new QueryResponseResult<TeachplanMediaPub>(CommonCode.SUCCESS,queryResult);
    }

Controller

/**
     * 根据课程计划id搜索发布后的媒资信息
     * @param teachplanId
     * @return
     */
@GetMapping(value="/getmedia/{teachplanId}")
@Override
public TeachplanMediaPub getmedia(@PathVariable("teachplanId") String teachplanId) {
    //为了service的拓展性,所以我们service接收的是数组作为参数,以便后续开发查询多个ID的接口
    String[] teachplanIds = new String[]{teachplanId};
    //通过service查询ES获取课程媒资信息
    QueryResponseResult<TeachplanMediaPub> mediaPubQueryResponseResult = esCourseService.getmedia(teachplanIds);
    QueryResult<TeachplanMediaPub> queryResult = mediaPubQueryResponseResult.getQueryResult();
    if(queryResult!=null&& queryResult.getList()!=null
       && queryResult.getList().size()>0){
        //返回课程计划对应课程媒资
        return queryResult.getList().get(0);
    } return new TeachplanMediaPub();
}

测试

使用 swagger-ui 和 postman 测试课程媒资查询接口。

三、在线学习:接口开发

0x01 需求分析

根据下边的业务流程,本章节完成前端学习页面请求学习服务获取课程视频地址,并自动播放视频。

0x02 搭建开发环境

1、创建数据库

创建 xc_learning 数据库,学习数据库将记录学生的选课信息、学习信息。

导入:资料/xc_learning.sql

2、创建学习服务工程

参考课程管理服务工程结构,创建学习服务工程:

导入:资料/xc-service-learning.zip

项目工程结构如下

0x03 Api接口

此 api 接口是课程学习页面请求学习服务获取课程学习地址。

定义返回值类型:

package com.xuecheng.framework.domain.learning.response;

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

@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 getMediaPlayUrl(String courseId,String teachplanId);
}

0x04 服务端开发

需求分析

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

搜索服务注册Eureka

学习服务要调用搜索服务查询课程媒资信息,所以需要将搜索服务注册到 eureka 中。

1、查看服务名称是否为 xc-service-search

# 注意修改application.xml中的服务名称:
spring:
  application:
	name: xc‐service‐search

2、配置搜索服务的配置文件 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 依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring‐cloud‐starter‐netflix‐eureka‐client</artifactId>
</dependency>

4、修改启动类,在class上添加如下注解:

@EnableDiscoveryClient

搜索服务客户端

在 学习服务 创建搜索服务的客户端接口,此接口会生成代理对象,调用搜索服务:

package com.xuecheng.learning.client;
import com.xuecheng.framework.domain.course.TeachplanMediaPub;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

@FeignClient(value = "xc‐service‐search")
public interface CourseSearchClient {
    @GetMapping(value="/getmedia/{teachplanId}")
    public TeachplanMediaPub getmedia(@PathVariable("teachplanId") String teachplanId);
}

自定义错误代码

我们在 com.xuecheng.framework.domain.learning.response 包下自定义一个错误消息模型

package com.xuecheng.framework.domain.learning.response;

import com.xuecheng.framework.model.response.ResultCode;
import lombok.ToString;

@ToString
public enum LearningCode implements ResultCode {
    LEARNING_GET_MEDIA_ERROR(false,23001,"学习中心获取媒资信息错误!");

    //操作代码
    boolean success;
    //操作代码
    int code;
    //提示信息
    String message;
    private LearningCode(boolean success, int code, String message){
        this.success = success;
        this.code = code;
        this.message = message;
    }

    @Override
    public boolean success() {
        return success;
    }

    @Override
    public int code() {
        return code;
    }

    @Override
    public String message() {
        return message;
    }
}

该消息模型基于 ResultCode 来实现,代码如下

package com.xuecheng.framework.model.response;

/**
 * Created by mrt on 2018/3/5.
 * 10000-- 通用错误代码
 * 22000-- 媒资错误代码
 * 23000-- 用户中心错误代码
 * 24000-- cms错误代码
 * 25000-- 文件系统
 */
public interface ResultCode {
    //操作是否成功,true为成功,false操作失败
    boolean success();
    //操作代码
    int code();
    //提示信息
    String message();

从 ResultCode 中我们可以看出,我们约定了用户中心的错误代码使用 23000,所以我们定义的一些错误信息的代码就从 23000 开始计数。

Service

在学习服务中定义 service 方法,此方法远程请求课程管理服务、媒资管理服务获取课程学习地址。

package com.xuecheng.learning.service.impl;

import com.netflix.discovery.converters.Auto;
import com.xuecheng.framework.domain.course.TeachplanMediaPub;
import com.xuecheng.framework.domain.learning.response.GetMediaResult;
import com.xuecheng.framework.exception.ExceptionCast;
import com.xuecheng.framework.model.response.CommonCode;
import com.xuecheng.learning.client.CourseSearchClient;
import com.xuecheng.learning.service.LearningService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;


@Service
public class LearningServiceImpl implements LearningService {

    @Autowired
    CourseSearchClient courseSearchClient;

    /**
     * 远程调用搜索服务获取已发布媒体信息中的url
     * @param courseId 课程id
     * @param teachplanId  媒体信息id
     * @return
     */
    @Override
    public GetMediaResult getMediaPlayUrl(String courseId, String teachplanId) {
        //校验学生权限,是否已付费等


        //远程调用搜索服务进行查询媒体信息
        TeachplanMediaPub mediaPub = courseSearchClient.getmedia(teachplanId);
        if(mediaPub == null) ExceptionCast.cast(CommonCode.FAIL);

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

Controller

调用 service 根据课程计划 id 查询视频播放地址:

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

测试

使用 swagger-ui 或postman 测试学习服务查询课程视频地址接口。

0x05 前端开发

需求分析

需要在学习中心前端页面需要完成如下功能:

1、进入课程学习页面需要带上 课程 Id参数及课程计划Id的参数,其中 课程 Id 参数必带,课程计划 Id 可以为空。

2、进入页面根据 课程 Id 取出该课程的课程计划显示在右侧。

3、进入页面后判断如果请求参数中有课程计划 Id 则播放该章节的视频。

4、进入页面后判断如果 课程计划id 为0则需要取出本课程第一个 课程计划的Id,并播放第一个课程计划的视频。

进入到模块 xc-ui-pc-leanring/src/module/course

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;
}

#学成网用户中心
server {
    listen 80;
    server_name ucenter.xuecheng.com;
    #个人中心
    location / {
        proxy_pass http://ucenter_server_pool;
    }
    #后端搜索服务
    location /openapi/search/ {
        proxy_pass http://search_server_pool/search/; 
    }
    #学习服务
    location ^~ /api/learning/ {
        proxy_pass http://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 ;
        }
        let courseInfo = view_course[this.courseId]
        console.log(courseInfo)
        this.coursename = courseInfo.name
        if(courseInfo.teachplan){
            console.log("准备开始播放视频")
            let teachplan = JSON.parse(courseInfo.teachplan);
            this.teachplanList = teachplan.children;
            //开始学习
            if(this.chapter == "0" || !this.chapter){
                //取出第一个教学计划
                this.chapter = this.getFirstTeachplan();
                console.log("第一个教学计划id为 ",this.chapter);
                this.study(this.chapter);
            }else{
                this.study(this.chapter);
            }

        }
    })
},

取出第一个章节 id,用户未输入课程计划 id 或者输入为 0 时,播放第一个。

//取出第一个章节
getFirstTeachplan(){
    for(var i=0;i<this.teachplanList.length;i++){
        let firstTeachplan = this.teachplanList[i];
		//如果当前children存在,则取出第一个返回
        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("播放视频失败,请刷新页面重试")
    });
},

2、点击右侧课程章节切换播放

在原有代码基础上添加 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>

3、地址栏路由url变更

这里需要注意一个问题,在用户点击课程章节切换播放时,地址栏的 url 也应该同步改变为当前所选择的课程计划 id


4、在线学习按钮

将 learnstatus 默认更改为 1,这样就能显示出马上学习的按钮,方便我们后续的集成测试。

文件路径为 xc-ui-pc-static-portal/include/course_detail_dynamic.html 部分代码块如下

<script>

   var body= new Vue({   //创建一个Vue的实例
        el: "#body", //挂载点是id="app"的地方

        data: {
            editLoading: false,
            title:'测试',
            courseId:'',
            charge:'',//203001免费,203002收费
            learnstatus: 1 ,//课程状态,1:马上学习,2:立即报名、3:立即购买
            course:{},
            companyId:'template',
            company_stat:[],
            course_stat:{"s601001":"","s601002":"","s601003":""}


        },

简单的测试

访问在线学习页面:http://ucenter.xuecheng.com/#/learning/课程id/课程计划id

通过 url 传入两个参数:课程id 和 课程计划id

如果没有课程计划则传入0

测试项目如下:

1、传入正确的课程id、课程计划id,自动播放本章节的视频

2、传入正确的课程id、课程计划id传入0,自动播放第一个视频

3、传入错误的课程id 或 课程计划id,提示错误信息。

4、通过右侧章节目录切换章节及播放视频。

访问: http://ucenter.xuecheng.com/#/learning/4028e58161bcf7f40161bcf8b77c0000/4028e58161bd18ea0161bd1f73190008

传入正确的课程id、课程计划id,自动播放本章节的视频

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ef0xxym7-1595567273153)(https://qnoss.codeyee.com/20200704_15/image17)]

传入正确的课程id、课程计划id传入0,自动播放第一个视频

访问 http://ucenter.xuecheng.com/#/learning/4028e58161bcf7f40161bcf8b77c0000/0

识别出第一个课程计划的 id

需要注意的是这里的 chapter 参数是我自己在 study 函数里加上去的,可以忽略。

传入错误的课程id或课程计划id,提示错误信息。

通过右侧章节目录切换章节及播放视频。

点击章节即可播放,但是点击制定章节后 url 没有发生改变,这个问题暂时还没有解决,关注笔记后面的内容。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TOGdxwb4-1595567273158)(https://qnoss.codeyee.com/20200704_15/image20)]

完整的测试

准备工作

  • 启动 RabbitMQ,启动 Logstash、ElasticSearch

  • 建议把所有后端服务都开起来

  • 启动 前端静态门户、启动 nginx 、启动课程管理前端

我们整理一下测试的流程

  1. 上传两个媒资视频文件,用于测试
  2. 进入到课程管理,为课程计划选择媒资信息
  3. 发布课程,等待 logstash 将数据采集到 ElasticSearch 的索引库中
  4. 进入学成网主页,点击课程,进入到搜索门户页面
  5. 搜索课程,进入到课程详情页面
  6. 点击开始学习,进入到课程学习页面,选择课程计划中的一个章节进行学习。
1、上传文件

首先我们使用之前开发的媒资管理模块,上传两个视频文件用于测试。

第一个文件上传成功

一些问题

在上传第二个文件时,发生了错误,我们来检查一下问题出在了哪里

在媒体服务的控制台中可以看到,在 mergeChunks 方法在校验文件 md5 时候抛出了异常

我们在 MD5 校验这里打个断点,重新上传文件,分析一下问题所在。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OpEMZGI8-1595567273166)(https://qnoss.codeyee.com/20200704_15/image23)]

单步调试后发现,合并文件后的MD5值与用户上传的源文件值不相等

方案1:删除本地分块文件重新尝试上传

考虑到可能是在用户上传完 视频的分块文件时发生了一些问题,导致合并文件后与源文件的大小不等,导致MD5也不相同,这里我们把这个视频上传到本地的文件全部删除,在媒资上传页面重新上传文件。

对比所有分块文件的字节大小和本地源文件的大小,完全是相等的

删除所有文件后重新上传,md5值还是不等,考虑从调试一下文件合并的代码。

方案2:检查前端提交的MD5值是否正确

在查阅是否有其他的MD5值获取方案时,发现了一个使用 windows 本地命令获取文件MD5值的方法

certutil -hashfile .\19-在线学习接口-集成测试.avi md5

惊奇的发现,TM的原来是前端那边转换的MD5值不正确,后端这边是没有问题的。

从前面的图可以看出,本地和后端转换的都是以一个 f6f0 开头的MD5值

那么问题就出现在前端了,还需要花一些时间去分析一下,这里暂时就先告一段落,因为上传了几个文件测试中只有这一个文件出现了问题。

2、为课程计划选择媒资信息

进入到一个课程的管理页面

http://localhost:12000/#/course/manage/baseinfo/4028e58161bcf7f40161bcf8b77c0000

将刚才我们上传的媒资文件的信息和课程计划绑定

选择效果如下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-epKaqzCD-1595567273178)(https://qnoss.codeyee.com/20200704_15/image29)]

2、发布课程,等待 logstash 从 course_pub 以及 teachplan_media_pub 表中采集数据到 ElasticSearch 当中

发布成功后,我们可以从 teachplan_media_pub 表中看到刚才我们发布的媒资信息

再观察 Logstash 的控制台,发现两个 Logstash 的实例都对更新的课程发布信息进行了采集

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hTUve2ik-1595567273183)(https://qnoss.codeyee.com/20200704_15/image32)]

3、前端门户测试

打开我们的门户主站 http://www.xuecheng.com/

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4wZe9R84-1595567273185)(https://qnoss.codeyee.com/20200704_15/image33)]

点击导航栏的课程,进入到我们的搜索门户页面

如果无法进入到搜索门户,请检查你的 xc-ui-pc-portal 前端工程是否已经启动

进入到搜索门户后,可以看到一些初始化时搜索的课程数据,默认是搜索第一页的数据,每页2个课程。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BJ1AKoJb-1595567273187)(https://qnoss.codeyee.com/20200704_15/image34)]

我们可以测试搜索一下前面我们选择媒资信息时所用的课程

点击课程,进入到课程详情页面,然后再点击开始学习。

点击马上学习后,会进入到该课程的在线学习页面,默认自动播放我们第一个课程计划中的视频。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tcuLWnf2-1595567273193)(https://qnoss.codeyee.com/20200704_15/image37)]

我们可以在右侧的目录中选择第二个课程计划,会自动播放所选的课程计划所对应的媒资视频播放地址,该 播放地址正是我们刚才通过 Logstash 自动采集到 ElasticSearch 的索引信息,效果图如下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Cvi9Dr0Y-1595567273195)(https://qnoss.codeyee.com/20200704_15/image38)]

四、待完善的一些功能

  • 课程发布前,校验课程计划里面是否包含二级课程计划
  • 课程发布前,校验课程计划信息里面是否全部包含媒资信息
  • 删除媒资信息,并且同步删除ES中的索引
  • 在获取该课程的播放地址时校验用户的合法、
  • 在线学习页面,点击右侧目录中的课程计划同时改变url中的课程计划地址
  • 视频文件 19-在线学习接口-集成测试.avi 前端上传时提交的MD5值不正确

😁 认识作者

作者:👦 LCyee ,全干型代码🐕

自建博客:https://www.codeyee.com

记录学习以及项目开发过程中的笔记与心得,记录认知迭代的过程,分享想法与观点。

优快云 博客:https://blog.youkuaiyun.com/codeyee

记录和分享一些开发过程中遇到的问题以及解决的思路。

欢迎加入微服务练习生的队伍,一起交流项目学习过程中的一些问题、分享学习心得等,不定期组织一起刷题、刷项目,共同见证成长。

基于深度学习的ECG心电信号异常分类:从数据集到Transformer实战 心电图(Electrocardiogram, ECG)是诊断心血管疾病最常用、最无创的工具。随着可穿戴设备的普及,海量的心电数据需要自动、快速且准确地分析。人工智能,尤其是深度学习,在心电信号分类任务中表现出了超越传统规则算法的能力。本文将深入探讨ECG异常分类的技术细节,从心电数据的生理基础,到数据预处理,再到使用1D-CNN、LSTM、以及前沿的Transformer模型进行实战演练。文章末尾提供了完整的代码以及多个国际公开数据集的获取方式。 阅读详情

相关推荐

《无人驾驶车辆模型预测控制》(第二版)第四章详细学习——算法部分

根据代码进行的“MPC跟踪直线和圆算法”的详细推导学习记录。

Fengdf666的博客 5018

学成在线--day15 媒资管理系统集成

学成在线-第15天-讲义-媒资管理系统集成 1 学习页面查询课程计

zgz102928的博客 294

告别谷歌翻译失效!手把手教你用Immersive Translate插件实现双语网页阅读(附Chrome/Edge/Firefox保姆级配置)

本文详细介绍了如何使用Immersive Translate插件实现双语网页阅读,解决谷歌翻译失效的问题。通过Chrome、Edge和Firefox的保姆级配置指南,帮助用户轻松安装和使用这款支持双语对照、智能识别网页内容的翻译插件,提升外文资料阅读效率。

weixin_28683689的博客 486

黑马学成在线项目

记录黑马学成在线项目的学习

Electric Sheep 889

Allegro模块复用:从原理图到PCB的完整流程与关键细节

本文详细介绍了Allegro模块复用从原理图到PCB的完整流程与关键细节,包括DSN和MDD文件的创建、模块调用的实战技巧以及双向同步与版本控制方法。通过标准化处理和高级复用技巧,能显著提升PCB设计效率,减少重复工作时间。适用于智能硬件和工业控制等领域的工程师。

weixin_30252709的博客 332

2022-2028年中国美容仪行业发展战略规划及投资机会预测报告

报告类型:产业研究 报告格式:电子版、纸介版 出品单位:智研咨询-产业信息网 美容仪是一种根据人体生理机能进行调节改善身体和面部的机器,它按照功能来说有美白、嫩肤、祛斑、祛皱、脱毛、减肥等多种,按照名称来说有超声波导入、光子嫩肤、高周波电疗、RF 电波拉皮、电子祛斑点痣、E 光永久脱毛嫩肤、贾法尼营养导入导出等。 智研咨询发布的《2022-2028年中国美容仪行业发展战略规划及投资机会预测报告》共十七章。首先介绍了中国美容仪行业市场发展环境、美容仪整体运行态势等,接着分析了中...

kt346的博客 468

最新传智学成在线JAVAEE微服务项目实战视频教程

目录 ├─day01 搭建环境 CMS服务端开发 ├─day02 CMS前端开发 ├─day03 CMS页面管理开发 ├─day04 页面静态化 ├─day05 消息中间件RabbitMQ ├─day06 页面发布 课程管理 ├─day07 课程管理实战 ├─day08 课程图片管理 分布式文件系统 ├─day09 课程预览 Eureka Feign ├─day10 课程发布 ElasticSea...

u011576602的博客 992

MiniMax H3 + ComfyUI 工作流搭建指南:从整合包到视频生成

AI视频生成正成为内容创作的新基础设施,而ComfyUI作为可视化工作流引擎,将复杂的模型推理过程拆解为节点与连线,让本地视频生成变得可复用、可批量。理解这一原理后,用户无需编写代码,只需掌握模型加载、提示词控制、采样与解码的基本链路,即可搭建出稳定的视频生产管线。MiniMax H3作为前沿视频生成模型,在ComfyUI中通过权重文件和自定义节点落地,配合整合包可大幅降低环境配置门槛。从硬件评估、目录结构到节点连接与参数调优,本文系统性梳理了从零跑通一条视频生成工作流的完整路径,帮助用户在批量素材生成、风

weixin_29057065的博客 414

2017黑加传智java_传智黑马 Java在职加薪课 学成在线

传智黑马 Java在职加薪课 学成在线|____【传智播客】学成在线|____day20 Devops 项目总结|____day19 分布式事务|____day18 用户授权|____day17 用户认证 Zuul|____day16 Spring Security Oauth2|____day15 媒资管理系统集成|____day14 媒资管理|____day13 在线学习 HLS|____da...

weixin_30927801的博客 195

Python 爬虫高级实战:HTTPS 证书忽略与代理抓包配置

本文深入解析了HTTPS加密与SSL证书校验机制在爬虫开发中的核心问题及解决方案。文章首先剖析了HTTPS协议架构和证书校验流程,指出爬虫开发中常见的证书异常场景及其负面影响。随后详细介绍了Python主流请求库(requests、httpx、aiohttp)的证书忽略配置方法,包括同步/异步模式下的实现方案。针对代理环境,文章系统比较了HTTP、HTTPS、SOCKS5等代理类型的特性差异,并提供了完整的代理配置示例。特别对中间人抓包场景,阐述了Mitmproxy的工作原理和配置要点。最后提出了工程级爬虫

2503_91057718的博客 85

044、镜头脏污与异物检测:产线 AOI 检测方案与 ISP 脏污补偿

文章摘要(148字) 本文针对镜头生产中的脏污检测问题,提出产线AOI检测与ISP补偿结合的解决方案。产线端采用明场、暗场、偏振三通道联合检测技术,将油污漏检率从8.3%降至0.2%。ISP端通过RAW域局部梯度分析和频域补偿算法,动态修复产线漏检的微小脏污。作者结合实战案例,详细分析了传统灰度阈值法的局限性,并给出多模态检测的代码实现要点,强调必须建立"产线拦截+终端补偿"的双重防护机制。

sundong_81的博客 228

安排,全栈分布式微服务媒资管理系统(视频、代码)

来源:来自网络,如侵权请告知博主删除????。仅学习使用,请勿用于其他~最近有小伙伴管我要分布式这类的项目,还有问我还有没有前后端分离的项目,还有些问我有没有微服务的,今天找的资源一...

qq_14958051的博客 605

白话机器学习算法理论+实战番外篇之Xgboost

1. 写在前面 如果想从事数据挖掘或者机器学习的工作,掌握常用的机器学习算法是非常有必要的,在这简单的先捋一捋, 常见的机器学习算法: 监督学习算法:逻辑回归,线性回归,决策树,朴素贝叶斯,K近邻,支持向量机,集成算法Adaboost等 无监督算法:聚类,降维,关联规则, PageRank等 我前面已经尝试用最白话的语言完成了一个白话机器学习算法理论+实战系列,这个系列已经基本包含了上面这些算...

Miracle8070 1万+

Java的新项目学成在线笔记-day14(五)

1.3 发送视频处理消息 当视频上传成功后向MQ 发送视频 处理消息。 修改媒资管理服务的文件上传代码,当文件上传成功向MQ发送视频处理消息。 1.3.1 RabbitMQ配置 1、将media-processor工程下的RabbitmqConfig配置类拷贝到media工程下。 2、在media工程下配置mq队列等信息 修改application.y...

czbkzmj的博客 208

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

最新《java技术提升班教程》

微服务持续集成与容器管理讲义持续集成与容器管理.pdf资源CentOS7-2.zipapache-maven-3.5.4-bin.tar.gzjenkins-2.83-1.1.noarch.rpmjdk-8u171-linux-x64.rpm华为 云服务治理day01 微服务引擎CSEday02 云容器引擎CCEday03 学成在线项目部署day04 微服务治理day05 Mesher学成在线项目...

a13141213的博客 472

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

黑马mysql百度云盘_黑马-百度云搜索引擎

资源内容:黑马|____阶段6 微服务社交平台【十次方】|____17 项目完结|____16 dockfile|____15 SpringCloud之一统天下|____14 SpringCloud之初入江湖|____13 密码加密与微服务鉴权JWT|____12 rabbitm...

weixin_39966765的博客 1124

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

微服务[学成在线] day20:项目总结

???? 写在前面 我为什么要花时间去写这个笔记? 由于在学习该项目的过程中发现了原讲义存在的一些问题,所以该笔记基于「学成在线」微服务项目 PDF 讲义进行编写,并且投入了时间去优化了笔记的格式、代码的高亮、重点的标记等。 以及一些原讲义中所描述的一些知识点使我无法理解的内容,我会对这些内容的表达方式进行修改或者提出一些问题,并且用我自己所理解的一些想法去重新的解释这个问题。 总结一下原 PDF 讲义中已知的一些问题: 从 PDF 中复制出来的代码,部分特殊符号的编码有问题,并且不易被发现,例如横杠

通往体面生活的路上 3261

微服务[学成在线] day18:基于oauth2实现RBAC认证授权、微服务间认证实现

???? 知识点概览 为了方便后续回顾该项目时能够清晰的知道本章节讲了哪些内容,并且能够从该章节的笔记中得到一些帮助,所以在完成本章节的学习后在此对本章节所涉及到的知识点进行总结概述。 本章节为【学成在线】项目的 day18 的内容 基于方法的权限校验 基于 RBAC 进行用户权限配置以及动态查询。 根据教师所属的公司来实现课程信息查询的细粒度授权。也就是 A 公司的老师只能查询到 A 公司下的课程。 使用 Feign 拦截器实现获取前端请求中的 header 信息,并将 header 中带有

通往体面生活的路上 2004

微服务[学成在线] day13:使用FFmpeg进行格式转换以及m3u8文件生成、文件分块上传接口实现

???? 知识点概览 为了方便后续回顾该项目时能够清晰的知道本章节讲了哪些内容,并且能够从该章节的笔记中得到一些帮助,所以在完成本章节的学习后在此对本章节所涉及到的知识点进行总结概述。 本章节为【学成在线】项目的 day13 的内容 FFmpeg 的基本使用 使用 m3u8 和 video.js技术实现视频的在线播放 搭建媒资服务工程实现文件的分块储存 目录 内容会比较多,小伙伴们可以根据目录进行按需查阅。 文章目录???? 知识点概览目录一、在线学习需求分析0x01 需求描述0x02 视频点

通往体面生活的路上 1724

微服务[学成在线] day10:课程发布、ElasticSearch

???? 知识点概览 为了方便后续回顾该项目时能够清晰的知道本章节讲了哪些内容,并且能够从该章节的笔记中得到一些帮助,所以在完成本章节的学习后在此对本章节所涉及到的知识点进行总结概述。 本章节为【学成在线】项目的 day10 的内容 课程发布功能开发 ElasticsSearch 安装部署 ElasticsSearch 快速入门、IK 分词器、映射、索引。 目录 内容会比较多,小伙伴们可以根据目录进行按需查阅。 文章目录???? 知识点概览目录一、课程发布0x01 需求分析0x02 CMS一键

通往体面生活的路上 1205

微服务[学成在线] day05:消息中间件 RabbitMQ

???? 知识点概览 为了方便后续回顾该项目时能够清晰的知道本章节讲了哪些内容,并且能够从该章节的笔记中得到一些帮助,所以在完成本章节的学习后在此对本章节所涉及到的知识点进行总结概述。 本章节为【学成在线】项目的 day05 的内容 RabbitMQ 的基本应用场景 使用原生RabbitMQ 库构建生产者与消费者模型 整合 Springboot 实现 RabbitMQ 生产者与消费者模型 目录 内容会比较多,小伙伴们可以根据目录进行按需查阅。 文章目录???? 知识点概览目录一、需求分析二、初

通往体面生活的路上 846

微服务[学成在线] day20:项目部署与持续集成(DevOps)

???? 知识点概览 为了方便后续回顾该项目时能够清晰的知道本章节讲了哪些内容,并且能够从该章节的笔记中得到一些帮助,所以在完成本章节的学习后在此对本章节所涉及到的知识点进行总结概述。 本章节为【学成在线】项目的 day20 的内容。 原讲义中对该章节的 gitlab 与 jenkins 安装步骤以及部署容器的内容和步骤描述得不够详细,在本文中做出了补充,详细的描述每个步骤。 本章节建议还是尝试看笔记来完成一次操作,然后再去看视频资料,这样能理解得更深刻。 目录 内容会比较多,可以根据目录进行按

通往体面生活的路上 812

微服务[学成在线] day16:基于Spring Security Oauth2开发认证服务

???? 知识点概览 为了方便后续回顾该项目时能够清晰的知道本章节讲了哪些内容,并且能够从该章节的笔记中得到一些帮助,所以在完成本章节的学习后在此对本章节所涉及到的知识点进行总结概述。 本章节为【学成在线】项目的 day16 的内容 学习 Spring Security + Oauth2 基本概念以及实现过程。 学习 Oauth2 的基本应用场景,这里主要是通过 Oauth2 的密码模式来实战。 初识 JWT 令牌。 本章节的最后通过 Spring Security Oauth2 完成了认证

通往体面生活的路上 801

微服务[学成在线] day11:基于 ElasticSearch 构建搜索服务

???? 知识点概览 为了方便后续回顾该项目时能够清晰的知道本章节讲了哪些内容,并且能够从该章节的笔记中得到一些帮助,所以在完成本章节的学习后在此对本章节所涉及到的知识点进行总结概述。 本章节为【学成在线】项目的 day11 的内容 基于 Java 客户端实现 DSL 搜索 搭建 ElasticSearch 集群环境 使用 Logstash 自动创建 ElasticSearch 的索引、数据文档 基于 ElasticSearch 开发搜索服务接口 目录 内容会比较多,小伙伴们可以根据目录进行

通往体面生活的路上 778

微服务[学成在线] day07:课程管理开发

???? 知识点概览 为了方便后续回顾该项目时能够清晰的知道本章节讲了哪些内容,并且能够从该章节的笔记中得到一些帮助,所以在完成本章节的学习后在此对本章节所涉及到的知识点进行总结概述。 本章节为【学成在线】项目的 day07 的内容 对课程管理的CRUD 级联菜单 使用 mongoDB 储存数据字典 目录 内容会比较多,小伙伴们可以根据目录进行按需查阅。 文章目录???? 知识点概览目录一、课程查询0x01 需求分析0x02 定义API接口0x03 课程管理服务PageHelperDaoSer

通往体面生活的路上 710

微服务[学成在线] day12:基于 Nuxt.js 构建搜索前端工程

???? 知识点概览 为了方便后续回顾该项目时能够清晰的知道本章节讲了哪些内容,并且能够从该章节的笔记中得到一些帮助,所以在完成本章节的学习后在此对本章节所涉及到的知识点进行总结概述。 本章节为【学成在线】项目的 day12 的内容 Nuxt.js 的基本使用 基于 Nuxt.js 开发搜索门户前端 目录 知识点结合实战应用会更有意义,所以这里我就不再对单个知识点进行拆分成单个笔记,内容会比较多,这里我们可以根据目录进行按需阅读。 文章目录???? 知识点概览目录一、搜索前端技术需求0x01 什

通往体面生活的路上 698
上一篇: 微服务[学成在线] day14:媒资管理
下一篇: 微服务[学成在线] day16:基于Spring Security Oauth2开发认证服务
cory_lin
博客等级 码龄7年 22粉丝 24原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值