一、课程最终发布信息展示 – 后端
1、实体类
@ApiModel(value = "课程最终发布")
@Data
public class CoursePublishVo {
private String id;
private String title;
private String cover;
private String lessonNum;
private String subjectLevelOne;
private String subjectLevelTwo;
private String teacherName;
private String price;
}
2、编写Controller类
@ApiOperation(value = "根据课程id查询课程确认信息")
@GetMapping("getPublishCourseInfo/{id}")
public R getPublishCourseInfo(@PathVariable String id) {
CoursePublishVo coursePublishVo = courseService.publishCourseInfo(id);
return R.ok().data("publishCourse",coursePublishVo);
}
3、编写Service类
@Override
public CoursePublishVo publishCourseInfo(String id) {
CoursePublishVo publicCourseInfo = baseMapper.getPublicCourseInfo(id);
return publicCourseInfo;
}
由于我们最终发布页面显示的数据是来源于四张表(课程表、课程描述表、讲师表、分类表),所以我们需要手动写SQL语句实现
。
4、编写mapper接口
public CoursePublishVo getPublicCourseInfo(String courseId);
5、编写mapper类xml配置文件
<select id="getPublicCourseInfo" resultType="com.kuang.eduservice.entity.vo.CoursePublishVo">
SELECT ec.id,ec.title,ec.price,ec.lesson_num AS lessonNum,ec.cover,
et.name AS teacherName,
es1.title AS subjectLevelOne,
es2.title AS subjectLevelTwo
FROM edu_course ec LEFT JOIN edu_course_description ecd ON ec.id=ecd.id
LEFT JOIN edu_teacher et ON ec.teacher_id=et.id
LEFT JOIN edu_subject es1 ON ec.subject_parent_id=es1.id
LEFT JOIN edu_subject es2 ON ec.subject_id=es2.id
WHERE ec.id=#{courseId}
</select>
6、项目运行出现错误并解决
项目创建mapper接口,编写xml文件sql语句,执行出现错误
这个错误是有maven默认加载机制造成问题。maven加载时候,把java文件夹里面 .java 类型文件进行编译,如果其他类型文件,不会加载。
解决方式:
1、复制xml到target目录中
2、把xml文件放在resources目录中
3、推荐使用:通过配置文件实现
(1)pom.xml
(2)项目application.properties
在Service模块的pom中
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
在service模块的application.properties中
# 配置mapper xml文件的路径
mybatis-plus.mapper-locations=classpath:com/kuang/eduservice/mapper/xml/*.xml
重新启动服务运行即可。
二、课程最终发布信息展示 – 前端
展示图:
1、定义api接口
getPublishCourseInfo(id) {
return request({
url: `/eduservice/course/getPublishCourseInfo/${id}`,
method: 'get'
})
}
2、引入接口
import course from '@/api/edu/course'
3、编写前端js
data() {
return {
courseId: '',
coursePublish: {}
}
},
created() {
if(this.$route.params && this.$route.params.id) {
this.courseId = this.$route.params.id
this.getCoursePublishId()
}
},
methods: {
getCoursePublishId() {
course.getPublishCourseInfo(this.courseId)
.then(response => {
this.coursePublish = response.data.publishCourse
})
},
}
4、前端样式css
<style scoped>
.ccInfo {
background: #f5f5f5;
padding: 20px;
overflow: hidden;
border: 1px dashed #DDD;
margin-bottom: 40px;
position: relative;
}
.ccInfo img {
background: #d6d6d6;
width: 500px;
height: 278px;
display: block;
float: left;
border: none;
}
.ccInfo .main {
margin-left: 520px;
}
.ccInfo .main h2 {
font-size: 28px;
margin-bottom: 30px;
line-height: 1;
font-weight: normal;
}
.ccInfo .main p {
margin-bottom: 10px;
word-wrap: break-word;
line-height: 24px;
max-height: 48px;
overflow: hidden;
}
.ccInfo .main p {
margin-bottom: 10px;
word-wrap: break-word;
line-height: 24px;
max-height: 48px;
overflow: hidden;
}
.ccInfo .main h3 {
left: 540px;
bottom: 20px;
line-height: 1;
font-size: 28px;
color: #d32f24;
font-weight: normal;
position: absolute;
}
</style>
三、课程最终发布 – 前后端
1、编写Controller类
@ApiOperation(value = "课程最终发布修改课程状态")
@PostMapping("publishCourse/{id}")
public R publishCourse(@PathVariable String id) {
EduCourse eduCourse = new EduCourse();
eduCourse.setId(id);
eduCourse.setStatus("Normal");
courseService.updateById(eduCourse);
return R.ok();
}
2、定义api接口
publishCourse(id) {
return request({
url: `/eduservice/course/publishCourse/${id}`,
method: 'post'
})
}
3、编写前端js
在views/edu/publish.vue中
publish() {
this.$confirm('确认最终发布课程, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
course.publishCourse(this.courseId)
.then(response => {
this.$message({
type: 'success',
message: '课程发布成功!'
});
this.$router.push({ path: '/course/list' })
})
})
}
}
四、课程列表显示
效果图:
1、后端Controller类
@ApiOperation(value = "课程列表")
@GetMapping("getCourseList")
public R getCourseList(){
List<EduCourse> list = courseService.list(null);
return R.ok().data("list",list);
}
@ApiOperation(value = "分页查询课程列表")
@PostMapping("pageListCourse/{current}/{limit}")
public R pageListCourse(
@ApiParam(name = "current", value = "当前页码", required = true)
@PathVariable Long current,
@ApiParam(name = "limit", value = "每页记录数", required = true)
@PathVariable Long limit){
Page<EduCourse> pageCourse =new Page<>(current,limit);
courseService.page(pageCourse,null);
long total = pageCourse.getTotal();
List<EduCourse> courseList = pageCourse.getRecords();
return R.ok().data("total",total).data("rows",courseList);
}
@ApiOperation(value = "条件查询带分页课程列表")
@PostMapping("pageCourseCondition/{current}/{limit}")
public R pageCourseCondition(
@ApiParam(name = "current", value = "当前页码", required = true)
@PathVariable Long current,
@ApiParam(name = "limit", value = "每页记录数", required = true)
@PathVariable Long limit,
@RequestBody(required = false) CourseQuery courseQuery){
Page<EduCourse> pageCourse =new Page<>(current,limit);
QueryWrapper<EduCourse> wrapper = new QueryWrapper<>();
String title = courseQuery.getTitle();
String status = courseQuery.getStatus();
if (!StringUtils.isEmpty(title)) {
wrapper.like("title",title);
}
if (!StringUtils.isEmpty(status)) {
wrapper.eq("status",status);
}
courseService.page(pageCourse,wrapper);
long total = pageCourse.getTotal();
List<EduCourse> courseList = pageCourse.getRecords();
return R.ok().data("total",total).data("rows",courseList);
}
2、定义api接口
pageCourseCondition(current,limit,courseQuery){
return request({
url: `/eduservice/course/pageCourseCondition/${current}/${limit}`,
method: 'post',
data: courseQuery
})
}
3、页面展示
<template>
<div class="app-container">
课程列表
<!--查询表单-->
<el-form :inline="true" class="demo-form-inline">
<el-form-item>
<el-input v-model="courseQuery.title" placeholder="课程名称"/>
</el-form-item>
<el-form-item>
<el-select v-model="courseQuery.status" clearable placeholder="课程状态">
<el-option value="Normal" label="已发布"/>
<el-option value="Draft" label="未发布"/>
</el-select>
</el-form-item>
<el-button type="primary" icon="el-icon-search" @click="getList()">查询</el-button>
<el-button type="default" @click="resetData()">清空</el-button>
</el-form>
<!-- 表格 -->
<el-table
:data="list"
border
fit
highlight-current-row>
<el-table-column
label="序号"
width="70"
align="center">
<template slot-scope="scope">
{{ (page - 1) * limit + scope.$index + 1 }}
</template>
</el-table-column>
<el-table-column prop="title" label="课程名称" />
<el-table-column label="课程状态" width="80">
<template slot-scope="scope">
{{ scope.row.status==='Normal'?'已发布':'未发布' }}
</template>
</el-table-column>
<el-table-column prop="lessonNum" label="课时数" width="100"/>
<el-table-column prop="gmtCreate" label="添加时间" width="180"/>
<el-table-column prop="viewCount" label="浏览数量" width="70" />
<el-table-column label="操作" width="200" align="center">
<template slot-scope="scope">
<router-link :to="'/teacher/edit/'+scope.row.id">
<el-button type="primary" size="mini" icon="el-icon-edit">编辑课程基本信息</el-button>
</router-link>
<router-link :to="'/teacher/edit/'+scope.row.id">
<el-button type="primary" size="mini" icon="el-icon-edit">编辑课程大纲息</el-button>
</router-link>
<el-button type="danger" size="mini" icon="el-icon-delete" @click="removeDataById(scope.row.id)">删除课程信息</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<el-pagination
:current-page="page"
:page-size="limit"
:total="total"
style="padding: 30px 0; text-align: center;"
layout="total, prev, pager, next, jumper"
@current-change="getList"
/>
</div>
</template>
4、编写前端js
<script>
import course from '@/api/edu/course'
export default {
data() {
return {
list:null,
page:1,
limit:10,
total:0,
courseQuery:{}
}
},
created() {
this.getList()
},
methods:{
getList(page = 1){
this.page = page
course.pageCourseCondition(this.page,this.limit,this.courseQuery)
.then(response =>{
this.list = response.data.rows
this.total = response.data.total
})
.catch(error => {
console.log(error)
})
},
resetData() {
this.courseQuery = {}
this.getList()
}
}
}
</script>
五、课程列表删除
删除课程,要把视频,小节,章节,描述,课程本身都删除。
1、后端Controller类
@ApiOperation(value = "删除课程")
@PostMapping("deleteCourse/{courseId}")
public R deleteCourse(@PathVariable String courseId){
courseService.removeCourse(courseId);
return R.ok();
}
2、后端service类
@Override
public void removeCourse(String courseId) {
videoService.removeVideoByCourseId(courseId);
chapterService.removeChapterByCourseId(courseId);
courseDescriptionService.removeById(courseId);
int result = baseMapper.deleteById(courseId);
if (result == 0){
throw new GuliException(20001,"删除失败");
}
}
3、定义api接口
deleteCourse(courseId) {
return request({
url: `/eduservice/course/deleteCourse/${courseId}`,
method: 'post'
})
},
4、编写前端js
removeDataById(id) {
this.$confirm('此操作将永久删除课程, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
course.deleteCourse(id)
.then(response => {
this.$message({
type: 'success',
message: '删除课程成功!'
});
this.getList()
})
})
},
六、阿里云视频点播服务
视频点播(ApsaraVideo for VoD)是集音视频采集、编辑、上传、自动化转码处理、媒体资源管理、分发加速于一体的一站式音视频点播解决方案。
1、开通视频点播
进入阿里云官网:https://www.aliyun.com/,找到视频点播
开通视频点播服务(选择按流量计费)
2、资费说明
https://www.aliyun.com/price/product?spm=a2c4g.11186623.2.12.7fbd59b9vmXVN6#/vod/detail
3、整体流程
使用视频点播实现音视频上传、存储、处理和播放的整体流程如下:
4、视频点播服务的基本使用
完整的参考文档:https://help.aliyun.com/product/29932.html?spm=a2c4g.11186623.6.540.3c356a58OEmVZJ
对于控制台说明:
对于文档&SDK说明:
服务端:后端接口
客户端:浏览器、安卓、ios
API:阿里云提供固定的地址,只需要调用这个固定的地址,向地址传递参数,实现功能。
SDK:sdk对api方式进行封装,更方便使用。之前使用EayExcel调用阿里云提供类或者接口里面的方法实现视频功能。
5、使用Java代码具体使用SDK方式
注意:因为上传视频可以进行加密,加密之后,使用加密之后地址不能进行视频播放,在数据库存储不存地址,而是存储视频id。
1、获取视频播放地址
(1)在service下创建子模块service_vod
模块
引入依赖
<dependencies>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
</dependency>
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-vod</artifactId>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-sdk-vod-upload</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
</dependency>
</dependencies>
(2)初始化操作,创建 DefaultAcsClient 对象
public class InitObject {
public static DefaultAcsClient initVodClient(String accessKeyId, String accessKeySecret) throws ClientException {
String regionId = "cn-shanghai";
DefaultProfile profile = DefaultProfile.getProfile(regionId, accessKeyId, accessKeySecret);
DefaultAcsClient client = new DefaultAcsClient(profile);
return client;
}
}
(3)实现根据视频id获取视频播放地址
public class TestVod {
public static void main(String[] args) throws Exception {
DefaultAcsClient client = InitObject.initVodClient("<您的AccessKeyId>", "<您的AccessKeySecret>");
GetPlayInfoRequest request = new GetPlayInfoRequest();
GetPlayInfoResponse response = new GetPlayInfoResponse();
request.setVideoId("视频ID");
response = client.getAcsResponse(request);
List<GetPlayInfoResponse.PlayInfo> playInfoList = response.getPlayInfoList();
for (GetPlayInfoResponse.PlayInfo playInfo : playInfoList) {
System.out.print("PlayInfo.PlayURL = " + playInfo.getPlayURL() + "\n");
}
System.out.print("VideoBase.Title = " + response.getVideoBase().getTitle() + "\n");
}
}
2、获取视频播放凭证
public static void main(String[] args) throws Exception {
DefaultAcsClient client = InitObject.initVodClient("LTAI4GAS6zW5Gh6amLaHJBLT", "A0ZKeYdnQw0eWE1hR7BLmq0SOR5OMA");
GetVideoPlayAuthRequest request = new GetVideoPlayAuthRequest();
GetVideoPlayAuthResponse response = new GetVideoPlayAuthResponse();
request.setVideoId("c55d4cfcc36c47c5b7fd9f34e5367555");
response = client.getAcsResponse(request);
System.out.println("playauth:"+response.getPlayAuth());
}
3、上传视频到阿里云视频点播服务
public static void main(String[] args) throws Exception {
String accessKeyId = "LTAI4GAS6zW5Gh6amLaHJBLT";
String accessKeySecret = "A0ZKeYdnQw0eWE1hR7BLmq0SOR5OMA";
String title = "6 - What If I Want to Move Faster";
String fileName = "E:/6 - What If I Want to Move Faster.mp4";
UploadVideoRequest request = new UploadVideoRequest(accessKeyId, accessKeySecret, title, fileName);
request.setPartSize(2 * 1024 * 1024L);
request.setTaskNum(1);
UploadVideoImpl uploader = new UploadVideoImpl();
UploadVideoResponse response = uploader.uploadVideo(request);
if (response.isSuccess()) {
System.out.print("VideoId=" + response.getVideoId() + "\n");
} else {
System.out.print("VideoId=" + response.getVideoId() + "\n");
System.out.print("ErrorCode=" + response.getCode() + "\n");
System.out.print("ErrorMessage=" + response.getMessage() + "\n");
}
}
4、视频上传
文档位置:
安装非开源的jar包
以 1.4.12 版本为例,步骤如下:
1.下载Java示例代码VODUploadDemo-java-1.4.13.zip开发包(包含示例代码和所需jar包), 见服务端上传SDK ;
2.将解压后lib目录下的所有jar文件拷贝至您的项目中;
3.SDK依赖的jar包版本说明
注意:以下列举出部分依赖jar包的版本,您可直接在您的项目中添加maven依赖,也可以将VODUploadDemo-java-1.4.13.zip包中的所有jar包引入您的项目中使用。其中,aliyun-java-vod-upload-1.4.13.jar 还未正式开源
,请您直接引入jar包至您的项目中使用。
步骤如下:
- 引入上传视频,但是这个依赖不能在maven中央仓库中下载到,手动把依赖安装到本地仓库里面。
- 使用maven命令安装
在本地Maven仓库中安装jar包:下载视频上传SDK,解压,命令行进入lib目录,执行以下代码
mvn install:install-file -DgroupId=com.aliyun -DartifactId=aliyun-sdk-vod-upload -Dversion=1.4.11 -Dpackaging=jar -Dfile=aliyun-java-vod-upload-1.4.11.jar
引入依赖:
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-sdk-vod-upload</artifactId>
</dependency>
七、添加小节实现视频上传 – 后端
1、引入依赖
2、创建application配置文件
#服务端口
server.port=8003
#服务名
spring.application.name=service-vod
#环境设置:dev、test、prod
spring.profiles.active=dev
#阿里云 vod
#不同的服务器,地址不同
aliyun.oss.file.keyid=LTAI4GAS6zW5Gh6amLaHJBLT
aliyun.oss.file.keysecret=A0ZKeYdnQw0eWE1hR7BLmq0SOR5OMA
3、主启动类
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@ComponentScan(basePackages = {"com.kuang"})
public class VodApplication {
public static void main(String[] args) {
SpringApplication.run(VodApplication.class,args);
}
}
4、编写Controller类
@Api(description = "阿里云视频上传")
@RestController
@RequestMapping("/eduvod/video")
@CrossOrigin
public class VodController {
@Autowired
private VodService vodService;
@ApiOperation(value = "上传视频到阿里云")
@PostMapping("uploadAlyiVideo")
public R uploadAlyiVideo(MultipartFile file) {
String videoId = vodService.uploadAlyiVideo(file);
return R.ok().data("videoId",videoId);
}
}
5、编写Service类
@Service
public class VodServiceImpl implements VodService {
@Override
public String uploadAlyiVideo(MultipartFile file) {
try {
String fileName = file.getOriginalFilename();
String title = fileName.substring(0,fileName.lastIndexOf("."));
InputStream inputStream = file.getInputStream();
UploadStreamRequest request = new UploadStreamRequest(ConstantVodUtils.ACCESS_KEY_ID, ConstantVodUtils.ACCESS_KEY_SECRET, title, fileName, inputStream);
UploadVideoImpl uploader = new UploadVideoImpl();
UploadStreamResponse response = uploader.uploadStream(request);
System.out.print("RequestId=" + response.getRequestId() + "\n");
String videoId = null;
if (response.isSuccess()) {
videoId = response.getVideoId();
} else {
videoId = response.getVideoId();
}
return videoId;
}catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
6、编写工具类
@Component
public class ConstantVodUtils implements InitializingBean {
@Value("${aliyun.vod.file.keyid}")
private String keyId;
@Value("${aliyun.vod.file.keysecret}")
private String keySecret;
public static String ACCESS_KEY_ID;
public static String ACCESS_KEY_SECRET;
@Override
public void afterPropertiesSet() throws Exception {
ACCESS_KEY_ID = keyId;
ACCESS_KEY_SECRET = keySecret;
}
}
上面代码运行出现错误,上传大小问题
在application进行文件大小设置
# 最大上传单个文件大小:默认1M
spring.servlet.multipart.max-file-size=1024MB
# 最大置总上传的数据大小 :默认10M
spring.servlet.multipart.max-request-size=1024MB
八、添加小节实现视频上传 – 前端
1、整合上传组件
<el-form-item label="上传视频">
<el-upload
:on-success="handleVodUploadSuccess"
:on-remove="handleVodRemove"
:before-remove="beforeVodRemove"
:on-exceed="handleUploadExceed"
:file-list="fileList"
:action="BASE_API+'/eduvod/video/uploadAlyiVideo'"
:limit="1"
class="upload-demo">
<el-button size="small" type="primary">上传视频</el-button>
<el-tooltip placement="right-end">
<div slot="content">最大支持1G,<br>
支持3GP、ASF、AVI、DAT、DV、FLV、F4V、<br>
GIF、M2T、M4V、MJ2、MJPEG、MKV、MOV、MP4、<br>
MPE、MPG、MPEG、MTS、OGG、QT、RM、RMVB、<br>
SWF、TS、VOB、WMV、WEBM 等视频格式上传</div>
<i class="el-icon-question"/>
</el-tooltip>
</el-upload>
</el-form-item>
2、数据定义
fileList: [],
BASE_API: process.env.BASE_API
3、方法定义
handleVodUploadSuccess(response, file, fileList) {
this.video.videoSourceId = response.data.videoId
this.video.videoOriginalName = file.name
},
handleUploadExceed() {
this.$message.warning('想要重新上传视频,请先删除已上传的视频')
},
4、出现问题及解决方法
(1)需要在nginx 配置8003 端口规则
location ~ /eduvod/ {
proxy_pass http://localhost:8003;
}
(2)nginx支持上传文件大小限制
解决方法:在nginx配置文件中添加大小设置
5、删除视频后端接口
@ApiOperation(value = "根据视频id删除阿里云视频")
@DeleteMapping("removeAliyVideo/{id}")
public R removeAliyVideo(@PathVariable String id) {
try{
DefaultAcsClient client = InitVodClient.initVodClient(ConstantVodUtils.ACCESS_KEY_ID, ConstantVodUtils.ACCESS_KEY_SECRET);
DeleteVideoRequest request = new DeleteVideoRequest();
request.setVideoIds(id);
client.getAcsResponse(request);
return R.ok();
}catch (Exception e){
e.printStackTrace();
throw new GuliException(20001,"删除视频失败");
}
}
6、点击 X 删除上传文件
在api中定义接口
removeAliyVideo(id) {
return request({
url: `/eduvod/video/removeAliyVideo/${id}`,
method: 'delete'
})
}
页面调用
handleVodRemove() {
video.deleteAliyunvod(this.video.videoSourceId)
.then(response => {
this.$message({
type: 'success',
message: '删除视频成功!'
});
this.fileList = []
this.video.videoSourceId = ''
this.video.videoOriginalName = ''
})
},
beforeVodRemove(file,fileList) {
return this.$confirm(`确定移除 ${ file.name }?`);
},
如果有收获!!! 希望老铁们来个三连,点赞、收藏、转发。
创作不易,别忘点个赞,可以让更多的人看到这篇文章,顺便鼓励我写出更好的博客
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)