阿里云视频点播微服务
文章目录
3. 视频点播微服务
3.1 创建vod服务(需更新)
-
创建服务:zx-service-vod31
-
编写pom配置
<?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>zx-parent20</artifactId> <groupId>com.czxy.zx</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>zx-service-vod</artifactId> <dependencies> <!--web起步依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- nacos 客户端 --> <dependency> <groupId>com.alibaba.nacos</groupId> <artifactId>nacos-client</artifactId> </dependency> <!-- nacos 服务发现 --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <!-- mybatis plus--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>${mybatis.plus.version}</version> </dependency> <!-- mysql驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!--自定义项目--> <dependency> <groupId>com.czxy.zx</groupId> <artifactId>zx-common31</artifactId> </dependency> <dependency> <groupId>com.czxy.zx</groupId> <artifactId>zx-domain31</artifactId> </dependency> <dependency> <groupId>com.aliyun</groupId> <artifactId>aliyun-java-sdk-core</artifactId> <version>4.5.1</version> </dependency> <dependency> <groupId>com.aliyun.oss</groupId> <artifactId>aliyun-sdk-oss</artifactId> <version>3.10.2</version> </dependency> <dependency> <groupId>com.aliyun</groupId> <artifactId>aliyun-java-sdk-vod</artifactId> <version>2.15.11</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.28</version> </dependency> <dependency> <groupId>org.json</groupId> <artifactId>json</artifactId> <version>20170516</version> </dependency> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.8.2</version> </dependency> <dependency> <groupId>com.aliyun</groupId> <artifactId>aliyun-sdk-vod-upload</artifactId> <version>1.4.14</version> </dependency> </dependencies> </project> -
编写yml配置
# 服务端口号 server: port: 9030 # 服务名 spring: application: name: vod-service datasource: driverClassName: com.mysql.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/zx_edu_vod?useUnicode=true&characterEncoding=utf8 username: root password: 1234 devtools: restart: enabled: true #设置开启热部署 additional-paths: src/main/java #重启目录 exclude: WEB-INF/** cloud: nacos: discovery: server-addr: 127.0.0.1:8848 #nacos服务地址 aliyun: video: vod: keyId: LTAI4GD66m4xsD5e1Qnns9mR keySecret: FWPFzljoLDGMiLxCE58xhLykDb3LW4 regionId: cn-shanghai categoryPageSize: 10
-
编写启动类
![]()
package com.czxy.zx; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; /** * @author 桐叔 * @email liangtong@itcast.cn */ @SpringBootApplication @EnableEurekaClient public class ZxVodServiceApplication { public static void main(String[] args) { SpringApplication.run(ZxVodServiceApplication.class,args); } } -
拷贝工具类
![]()
3.2 vod分类管理
3.2.1 前置技术:定时器 schedule
1)入门

-
pom文件,schedule为spring 内置技术,只要保证spring boot基本运行即可
<dependencies> <!--web起步依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies>
-
启动类添加注解
@EnableSchedulingpackage com.czxy.zx; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableScheduling; /** * @author 桐叔 * @email liangtong@itcast.cn */ @SpringBootApplication @EnableScheduling public class ScheduleApplication { public static void main(String[] args) { SpringApplication.run(ScheduleApplication.class, args); } } -
编写定时任务
package com.czxy.zx.schedule; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; /** * @author 桐叔 * @email liangtong@itcast.cn */ @Component public class TestSchedule { // 从0秒开始,每3秒执行一次 @Scheduled(cron = "0/3 * * * * ? ") public void testDemo() { System.out.println(System.currentTimeMillis()); } }
2)cron表达式
-
cron语法:由7部分组成,第7部分为年,一般不写
秒 分 时 日 月 周 (年)
-
第4位和第6位,至少有一个?
-
-
cron 组成取值
-
, 或,例如:1,3,5 ,第1、第3、第5秒
-
- 至,例如:3-5,第3、第4、第5秒
-
* 任意
-
/ 每,
起始/间隔,例如:0/3 从0开始,每3秒执行 -
? 不指定,因为日期和周冲突
-
L 最后一个值
-
W 只能在日期字段设置,表示最近的工作日。15W,例如:15是周六,周五触发。15是周日,下周一触发。15是周二,当天触发。
-

-
常见cron表达式
"30 * * * * ?" 第30秒触发任务 "30 10 * * * ?" 每小时的10分30秒触发任务 "30 10 1 * * ?" 每天1点10分30秒触发任务 "30 10 1 20 * ?" 每月20号1点10分30秒触发任务 "30 10 1 20 10 ? *" 每年10月20号1点10分30秒触发任务 "30 10 1 20 10 ? 2011" 2011年10月20号1点10分30秒触发任务 "30 10 1 ? 10 * 2011" 2011年10月每天1点10分30秒触发任务 "30 10 1 ? 10 SUN 2011" 2011年10月每周日1点10分30秒触发任务 "15,30,45 * * * * ?" 每15秒,30秒,45秒时触发任务 "15-45 * * * * ?" 15到45秒内,每秒都触发任务 "15/5 * * * * ?" 每分钟的每15秒开始触发,每隔5秒触发一次 "15-30/5 * * * * ?" 每分钟的15秒到30秒之间开始触发,每隔5秒触发一次 "0 0/3 * * * ?" 每小时的第0分0秒开始,每三分钟触发一次 "0 15 10 ? * MON-FRI" 星期一到星期五的10点15分0秒触发任务 "0 15 10 L * ?" 每个月最后一天的10点15分0秒触发任务 "0 15 10 LW * ?" 每个月最后一个工作日的10点15分0秒触发任务 "0 15 10 ? * 5L" 每个月最后一个星期四的10点15分0秒触发任务 "0 15 10 ? * 5#3" 每个月第三周的星期四的10点15分0秒触发任务
3)扩展:Quartz
-
spring boot 自带的定时器简单容易
-
QuartZ定时器管理灵活,可以动态配置
<!--spring boot集成quartz--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId> </dependency> -
学习参考
https://blog.csdn.net/upxiaofeng/article/details/79415108
3.2.2 后端基本结构

-
初始化数据库
CREATE DATABASE zx_edu_vod; USE zx_edu_vod; CREATE TABLE vod_category( cate_id BIGINT PRIMARY KEY, cate_name VARCHAR(64), `level` BIGINT, parent_id BIGINT, `type` VARCHAR(10) COMMENT '取值:default、material' );
-
完善JavaBean
package com.czxy.zx.vod.utils;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
/** vod 分类封装对象(无限极)
* @author 桐叔
* @email liangtong@itcast.cn
*/
@Data
@TableName("vod_category")
public class VodCategory {
@TableId
private Long cateId;
private String cateName;
private Long level;
private Long parentId;
private String type;
@TableField(exist = false)
private List<VodCategory> children;
}
-
mapper
package com.czxy.zx.vod.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.czxy.zx.vod.utils.VodCategory; import org.apache.ibatis.annotations.Mapper; /** * @author 桐叔 * @email liangtong@itcast.cn */ @Mapper public interface VodCategoryMapper extends BaseMapper<VodCategory> { } -
service
package com.czxy.zx.vod.service; import com.baomidou.mybatisplus.extension.service.IService; import com.czxy.zx.vod.utils.VodCategory; /** * @author 桐叔 * @email liangtong@itcast.cn */ public interface VodCategoryService extends IService<VodCategory> { } package com.czxy.zx.vod.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.czxy.zx.vod.mapper.VodCategoryMapper; import com.czxy.zx.vod.service.VodCategoryService; import com.czxy.zx.vod.utils.VodCategory; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; /** * @author 桐叔 * @email liangtong@itcast.cn */ @Service @Transactional public class VodCategoryServiceImpl extends ServiceImpl<VodCategoryMapper, VodCategory> implements VodCategoryService { } -
拷贝配置类
![]()
3.2.3 定时添加
-
每个月1号零点更新数据
-
修改启动类
package com.czxy.zx; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.scheduling.annotation.EnableScheduling; /** * @author 桐叔 * @email liangtong@itcast.cn */ @SpringBootApplication @EnableDiscoveryClient @EnableScheduling public class ZxVodServiceApplication { public static void main(String[] args) { SpringApplication.run(ZxVodServiceApplication.class,args); } } -
编写定时器处理类
![]()
package com.czxy.zx.vod.schedule; import com.czxy.zx.vod.service.VodCategoryService; import com.czxy.zx.vod.utils.VideoUtils; import com.czxy.zx.vod.utils.VodCategory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import javax.annotation.Resource; import java.util.List; /** * @author 桐叔 * @email liangtong@itcast.cn */ @Component public class CategorySchedule { @Resource private VideoUtils videoUtils; @Resource private VodCategoryService vodCategoryService; private Logger LOGGER = LoggerFactory.getLogger(CategorySchedule.class); /** * 每月1号零点更新 */ @Scheduled(cron = "0 0 0 1 * ?") public void handlerCategory() { // 1 获得所有分类 List<VodCategory> categoryList = videoUtils.getCategoryList(); // 2 校验通过后,没有添加 batchAll(categoryList); } /** * 批量处理所有分类,递归处理子分类 * @param categoryList */ private void batchAll(List<VodCategory> categoryList) { System.out.println("处理:" + categoryList.size()); if(LOGGER.isInfoEnabled()) { LOGGER.info("处理:" + categoryList.size()); } for (VodCategory vodCategory : categoryList) { VodCategory findVodCategory = vodCategoryService.getById(vodCategory.getCateId()); if(findVodCategory == null) { vodCategoryService.save(vodCategory); if(LOGGER.isInfoEnabled()) { LOGGER.info("添加:" + vodCategory.getCateId()); } } else { // TODO 是否更新? } // 处理孩子 batchAll(vodCategory.getChildren()); } } }
3.2.4 查询所有分类


package com.czxy.zx.vod.controller;
import com.aliyun.vod.upload.resp.BaseResponse;
import com.czxy.zx.vo.BaseResult;
import com.czxy.zx.vod.service.VodCategoryService;
import com.czxy.zx.vod.utils.VodCategory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author 桐叔
* @email liangtong@itcast.cn
*/
@RestController
@RequestMapping("/vodcategory")
public class VodCategoryController {
@Resource
private VodCategoryService vodCategoryService;
@GetMapping
public BaseResult findAll() {
//1 查询所有
List<VodCategory> list = vodCategoryService.list();
//2 处理
List<VodCategory> categoryList = new ArrayList<>();
Map<Long,VodCategory> cache = new HashMap<>();
list.forEach(vodCategory -> {
if(vodCategory.getParentId() == -1) {
categoryList.add(vodCategory);
} else {
VodCategory cacheCategory = cache.get(vodCategory.getParentId());
// 初始化计划
List<VodCategory> childrenList = cacheCategory.getChildren();
if(childrenList == null) {
cacheCategory.setChildren(new ArrayList<>());
}
// 追加数据到集合
cacheCategory.getChildren().add(vodCategory);
}
cache.put(vodCategory.getCateId(), vodCategory);
});
//3 返回
return BaseResult.ok("查询成功", categoryList);
}
}
3.3 vod转码模板管理
3.3.1 后端基本结构
-
表结构
#转码模板表 CREATE TABLE vod_template( template_id VARCHAR(32) PRIMARY KEY, `name` VARCHAR(128) COMMENT '', is_default VARCHAR(20) COMMENT '取值:Default、NotDefault', transcode_mode VARCHAR(20) COMMENT '取值:NoTranscode、FastTranscode' );
-
JavaBean,修改VodTemplate
package com.czxy.zx.vod.utils; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; /** * @author 桐叔 * @email liangtong@itcast.cn */ @Data @TableName("vod_template") public class VodTemplate { @TableId(type = IdType.ASSIGN_UUID) private String templateId; //id private String name; //名称 private String isDefault; //是否默认,取值:Default、NotDefault private String transcodeMode; //模式,取值:NoTranscode、FastTranscode } -
编写Mapper
![]()
package com.czxy.zx.vod.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.czxy.zx.vod.utils.VodTemplate; import org.apache.ibatis.annotations.Mapper; /** * @author 桐叔 * @email liangtong@itcast.cn */ @Mapper public interface VodTemplateMapper extends BaseMapper<VodTemplate> { } -
编写service
![]()
package com.czxy.zx.vod.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.czxy.zx.vod.mapper.VodTemplateMapper; import com.czxy.zx.vod.service.VodTemplateService; import com.czxy.zx.vod.utils.VodTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; /** * @author 桐叔 * @email liangtong@itcast.cn */ @Service @Transactional public class VodTemplateServiceImpl extends ServiceImpl<VodTemplateMapper, VodTemplate> implements VodTemplateService { }
3.3.2 定时添加

package com.czxy.zx.vod.schedule;
import com.czxy.zx.vod.service.VodTemplateService;
import com.czxy.zx.vod.utils.VideoUtils;
import com.czxy.zx.vod.utils.VodTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.List;
/**
* @author 桐叔
* @email liangtong@itcast.cn
*/
@Component
public class TemplateSchedule {
@Resource
private VideoUtils videoUtils;
@Resource
private VodTemplateService vodTemplateService;
private Logger LOGGER = LoggerFactory.getLogger(TemplateSchedule.class);
/**
* 每月1号零点更新
*/
@Scheduled(cron = "0 0 0 1 * ?")
public void handlerTemplate() {
// 从阿里云获得所有的转码模板
List<VodTemplate> transcodeList = videoUtils.getTranscodeList();
if(LOGGER.isInfoEnabled()) {
LOGGER.info("处理:" + transcodeList.size());
}
// 保存数据
for (VodTemplate vodTemplate : transcodeList) {
// 查询
VodTemplate findTemplate = vodTemplateService.getById(vodTemplate.getTemplateId());
if(findTemplate == null) {
vodTemplateService.save(vodTemplate);
if(LOGGER.isInfoEnabled()) {
LOGGER.info("添加:" + vodTemplate.getTemplateId());
}
} else {
// TODO 更新
}
}
}
}
3.3.3 查询所有转码模板


package com.czxy.zx.vod.controller;
import com.czxy.zx.vo.BaseResult;
import com.czxy.zx.vod.service.VodCategoryService;
import com.czxy.zx.vod.service.VodTemplateService;
import com.czxy.zx.vod.utils.VodCategory;
import com.czxy.zx.vod.utils.VodTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author 桐叔
* @email liangtong@itcast.cn
*/
@RestController
@RequestMapping("/vodtemplate")
public class VodTemplateController {
@Resource
private VodTemplateService vodTemplateService;
@GetMapping
public BaseResult findAll() {
//1 查询所有
List<VodTemplate> list = vodTemplateService.list();
//3 返回
return BaseResult.ok("查询成功", list);
}
}
3.4 整合分类和转码模板
3.4.1 需求
-
级联菜单支持选择任意项的功能

-
转码模板,查询结果为“NoTranscode”,显示处理成“无加密”
3.4.2 ajax操作
-
编写ajax

import axios from '@/utils/request'
// 查询所有vod分类
export function findAllVodCate() {
return axios.get('/vod-service/vodcategory');
}
// 保存课程
export function findAllvodTemp(courseVo) {
return axios.get('/vod-service/vodtemplate');
}
-
导入ajax
![]()
import { findAllVodCate, findAllvodTemp } from '@/api/edu/vod' -
调用ajax:编写函数
-
声明变量
data() { return { vodCategoryList: [], vodTemplateList: [], } } -
编写函数
async findAllVodCategory() { let { data } = await findAllVodCate() this.vodCategoryList = data }, async findAllvodTemplate() { let { data } = await findAllvodTemp() this.vodTemplateList = data }
-
-
调用ajax:调用函数
![]()
async nextChapter() { // 保存 let { data,message } = await saveCourse(this.course) debugger this.$message.success(message) // 保存id this.course.id = data this.active = 2 // 查询所有章节 this.findAllChapter() // 查询vod分类 this.findAllVodCategory() // 查询vod转码模板 this.findAllvodTemplate() },
3.4.3 数据展示
-
声明变量
data() {
return {
vodCategoryProps: { // vod分类与级联选择对应关系
value: 'cateId',
label: 'cateName',
checkStrictly: true
},
vodVideo: { // vod数据封装对象
}
}
}
-
展示数据
<el-form-item label="上传视频" label-width="80px">
<!-- 分类级联 -->
<el-row>
<el-col :span="4" style="text-align:right">
分类:
</el-col>
<el-col :span="20">
<el-cascader v-model="vodVideo.categoryId" :options="vodCategoryList" :props="vodCategoryProps" style="width:300px"></el-cascader>
</el-col>
</el-row>
<!-- 转码模板单选按钮组 -->
<el-row>
<el-col :span="4" style="text-align:right">
转码模板:
</el-col>
<el-col :span="20">
<el-radio-group >
<el-radio v-for="(temp,index) in vodTemplateList" :key="index" v-model="vodVideo.templateId" :label="temp.templateId">
{{temp.name == 'NoTranscode' ? '无加密' : temp.name}} {{temp.isDefault == 'Default' ? '(默认)' : ''}}
</el-radio>
</el-radio-group>
</el-col>
</el-row>
<!-- 上传视频 -->
<el-row>
<el-col :span="4" style="text-align:right">
选择视频:
</el-col>
<el-col :span="20">
<!-- 上传组件 -->
</el-col>
</el-row>
</el-form-item>
3.5 视频上传
3.5.1 需求

3.5.2 后端实现
-
上传视频,并返回视频信息
-
问题:视频上传成功后,阿里云需要一个处理视频的视频,随意不能实时观看。
-
采取的方案:先上传,再查询视频信息
-
package com.czxy.zx.vod.controller;
import com.czxy.zx.vo.BaseResult;
import com.czxy.zx.vod.utils.VideoUtils;
import com.czxy.zx.vod.utils.VodVideoInfo;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
/**
* @author 桐叔
* @email liangtong@itcast.cn
*/
@RestController
@RequestMapping("/vodvideo")
public class VodVideoController {
@Resource
private VideoUtils videoUtils;
/**
* 上传视频
* @param categoryId
* @param templateId
* @param file
* @return
*/
@PostMapping("/upload/{categoryId}/{templateId}")
public BaseResult upload(
@PathVariable("categoryId") Long categoryId,
@PathVariable("templateId") String templateId,
MultipartFile file) {
// 1 上传
String videoId = videoUtils.uploadVideo(categoryId, templateId, file);
return BaseResult.ok("上传成功", videoId);
}
/**
* 查询视频信息
* @param videoId
* @return
*/
@GetMapping("/{videoId}")
public BaseResult getInfo(@PathVariable("videoId") String videoId) {
// 2 获得商品信息
VodVideoInfo playInfo = videoUtils.getPlayInfo(videoId);
//
return BaseResult.ok("查询成功", playInfo);
}
}
3.5.3 前端实现
-
编写ajax
![]()
// 查询 export function findVideoInfo(videoId) { return axios.get(`/vod-service/vodvideo/${videoId}`); } -
导入ajax
![]()
-
编写上传组件
<el-col :span="20"> <!-- 上传组件 --> <el-upload class="upload-demo" :action="uploadUrl" :before-upload="beforeVideoUpload" :on-success="videoUploadSuccess" :file-list="videoFileList" :limit="1"> <el-button size="small" type="primary">点击上传</el-button> <div slot="tip" class="el-upload__tip">只能上传视频文件</div> </el-upload> <el-button type="primary" @click="findVodVideoInfo">同步数据</el-button> <el-avatar shape="square" :size="50" :src="video.coverURL"></el-avatar> <br/> {{video}} </el-col> -
声明变量
data() { return { videoFileList: [], // 上传文件列表 videoId: '0fc9c1413b334b21b1ea48413c4ab35b', // 视频id } } -
声明函数
methods() { beforeVideoUpload() { if(!this.vodVideo.categoryId) { this.$message.warning('必须选择分类') return false } if(!this.vodVideo.templateId) { this.$message.warning('必须选择模板') return false } }, videoUploadSuccess(response, file, fileList) { if(response.code == 0) { this.$message.error(response.message) this.videoFileList = [] return } // 成功 this.$message.success(response.message) // 查询详情 this.videoId = response.data setTimeout(async ()=> { this.findVodVideoInfo() } , 1000) }, async findVodVideoInfo() { let { data } = await findVideoInfo(this.videoId) // 设置视频信息 this.video.coverURL = data.coverURL this.video.videoSourceId = data.playURL this.video.videoOriginalName = data.title console.info(this.video) } } -
动态设置上传路径
computed: { uploadUrl() { // 上传路径 // 如果是数组 if(Array.isArray(this.vodVideo.categoryId)) { // 处理id 获取最后一个分类 let categoryId = this.vodVideo.categoryId[this.vodVideo.categoryId.length-1] // 拼凑上传地址 return `${process.env.VUE_APP_BASE_API}/vod-service/vodvideo/upload/${categoryId}/${this.vodVideo.templateId}` } return "" } } -
视频上传成功后,保存video信息时异常,
video_source_id长度太短ALTER TABLE edu_video MODIFY COLUMN video_source_id VARCHAR(500)










浙公网安备 33010602011771号