资料下载提取码:8op3
篇幅有限,我另起了一个笔记进行复习
MybatisPlus快速学习
这里的数据库用的是MySQL8.0版本
如果有小伙伴跟着做了,最少也要5.7以上的版本
数据库和表
以下规则只针对本模块,更全面的文档参考《阿里巴巴Java开发手册》:
1、库名与应用名称尽量一致
2、表名、字段名必须使用小写字母或数字,禁止出现数字开头,
3、表名不使用复数名词
4、表的命名最好是加上“业务名称_表的作用”。如,edu_teacher
5、表必备三字段:id, gmt_create, gmt_modified
说明:
其中 id 必为主键,类型为 bigint unsigned、单表时自增、步长为 1。
(如果使用分库分表集群部署,则id类型为verchar,非自增,业务中使用分布式id生成器)
gmt_create, gmt_modified 的类型均为 datetime 类型,前者现在时表示主动创建,后者过去分词表示被 动更新。
6、单表行数超过 500 万行或者单表容量超过 2GB,才推荐进行分库分表。 说明:如果预计三年后的数据量根本达不到这个级别,请不要在创建表时就分库分表。
7、表达是与否概念的字段,必须使用 is_xxx 的方式命名,数据类型是 unsigned tinyint (1 表示是,0 表示否)。
说明:任何字段如果为非负数,必须是 unsigned。
注意:POJO 类中的任何布尔类型的变量,都不要加 is 前缀。数据库表示是与否的值,使用 tinyint 类型,坚持 is_xxx 的 命名方式是为了明确其取值含义与取值范围。
正例:表达逻辑删除的字段名 is_deleted,1 表示删除,0 表示未删除。
8、小数类型为 decimal,禁止使用 float 和 double。 说明:float 和 double 在存储的时候,存在精度损失的问题,很可能在值的比较时,得到不 正确的结果。如果存储的数据范围超过 decimal 的范围,建议将数据拆成整数和小数分开存储。
9、如果存储的字符串长度几乎相等,使用 char 定长字符串类型。
10、varchar 是可变长字符串,不预先分配存储空间,长度不要超过 5000,如果存储长度大于此值,定义字段类型为 text,独立出来一张表,用主键来对应,避免影响其它字段索 引效率。
11、唯一索引名为 uk_字段名;普通索引名则为 idx_字段名。
说明:uk_ 即 unique key;idx_ 即 index 的简称
12、不得使用外键与级联,一切外键概念必须在应用层解决。外键与级联更新适用于单机低并发,不适合分布式、高并发集群;级联更新是强阻塞,存在数据库更新风暴的风险;外键影响数据库的插入速度。
模块说明
ggkt_parent:硅谷课堂根目录(父工程),管理多个子模块:
common:公共模块父节点
common_util:工具类模块,所有模块都可以依赖于它
service_utils:service服务的base包,包含service服务的公共配置类,所有service模块依赖于它
rabbit_utils:rabbitmq封装工具类
model:实体类相关模块
server-gateway:服务网关
service:api接口服务父节点
service_acl:权限管理接口服务
service_activity:优惠券api接口服务
service_live:直播课程api接口服务
service_order:订单api接口服务
service_user:用户api接口服务
service_vod:点播课程 api接口服务
service_wechat:公众号api接口服务
service-client:feign服务调用父节点
service-activity-client:优惠券api接口
service-live-client:直播课程api接口
service-order-client:订单api接口
service-user-client:用户api接口
service-vod-client:点播课程api接口
介绍完毕,开始搭建
不用管官方的依赖,我们后面Pom文件自己加
SpringBoot版本统一为2.2.1RELEASE版本
而且作为父工程,是不需要src目录的,因为主要的内容都在他子模块下
所以留个Pom就行
依赖我就不粘贴了,太多
需要的小伙伴直接去Gitee找到父工程下的Pom就可以查看
先在parent下新建子模块model
以Maven模版生成就可以了
直接导入实体类子模块model,当然,MP自动生成也不是不行~
同样子模块也需要引入各种jar包和依赖
<dependencies>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
<scope>provided scope>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>easyexcelartifactId>
<scope>provided scope>
dependency>
<dependency>
<groupId>com.github.xiaoymingroupId>
<artifactId>knife4j-spring-boot-starterartifactId>
<scope>providedscope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-mongodbartifactId>
<scope>provided scope>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>fastjsonartifactId>
<scope>provided scope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-elasticsearchartifactId>
<scope>provided scope>
dependency>
dependencies>
<dependencies>
<dependency>
<groupId>com.ccgroupId>
<artifactId>modelartifactId>
<version>0.0.1-SNAPSHOTversion>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<optional>trueoptional>
dependency>
dependencies>
因为service包也是对服务进行管理,所以也不留src路径,下面继续还要创建多个服务包
再建service_vod(视频点播)模块
对接口信息进行管理,测试一条龙服务
非常快乐
因为Swagger组件是公共的,所以单独拿一个模块做Common-serviceUtils模块
依赖我就不多说了,具体路径可以看这个onlineClass\common\pom.xml
注意,common包下及其子模块 作为一个单独的子模块,是无法被service模块所看到的。
所以这里也要像之前引入实体类一样,去service模块把这个common模块引入
引入之前一定一定要补全common子模块service-utils的pom信息,缺少信息会找不到
这个是service-utils里的信息
这里是service包引入依赖
到这还不算完,要在在service_vod启动类上加入对于Swagger的扫描
定义在类上:@Api
定义在方法上:@ApiOperation
定义在参数上:@ApiParam
@Api(tags = "讲师管理接口")
@RestController
@RequestMapping(value="/admin/vod/teacher")
public class TeacherController {
@Autowired
private TeacherService teacherService;
//删除讲师
@ApiOperation("逻辑删除讲师")
@DeleteMapping("{id}")
public boolean removeById(@ApiParam(name = "id", value = "ID", required = true) @PathVariable String id){
return teacherService.removeById(id);
}
//查询所有讲师列表
@ApiOperation("所有讲师列表")
@GetMapping("findAll")
public List<Teacher> findAll(){
List<Teacher> list = teacherService.list();
return list;
}
}
Swagger后台路径:http://localhost:启动端口号/swagger-ui.html
后续包括文档的生成,参数注解生成等等都比较好办了,这里不是重点就不过多赘述了
左找右找,先后加了注解,也不知为什么找不到controller,全都是error
通过百度,发现这个问题一般来说都是Swagger配置类没写好。但是刚刚我还写了配置类,显然不太可能是配置类的锅
推测应该是没扫到配置类,之前说过要在启动类上加入配置类扫描
仔细一看,好家伙包名没对上!
改了包名就正常了,扫描到配置类自然就都合理起来了
Swagger后台路径:http://localhost:启动端口号/swagger-ui.html
添加课程时候,需要选择所属讲师,所以要对讲师进行管理,就是基于讲师的CRUD操作
引入代码生成器,自动生成相关内容
建议这些操作放到test测试包中,避免一起打包了,生成完毕也不需要再删除
(1)引入代码生成器依赖
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-generatorartifactId>
<version>3.3.1version>
dependency>
<dependency>
<groupId>org.apache.velocitygroupId>
<artifactId>velocity-engine-coreartifactId>
<version>2.0version>
dependency>
(2)复制生成代码工具类
修改代码中路径、数据库、包和表,复制到test目录下
(3)实体类统一替换为model模块的实体类
配置文件
# 服务端口
server.port=8301
# 服务名
spring.application.name=service-vod
# 环境设置:dev、test、prod
spring.profiles.active=dev
# mysql数据库连接
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/glkt_vod?characterEncoding=utf-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=root
#返回json的全局时间格式
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8
#mybatis日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
代码生成器
public class CodeGet {
public static void main(String[] args) {
// 1、创建代码生成器
AutoGenerator mpg = new AutoGenerator();
// 2、全局配置
// 全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
//gc.setOutputDir(projectPath + "/src/main/java");
//这里是输出生成代码的目录,这里要填电脑的绝对路径(必改)+拼接出来的路径
gc.setOutputDir("D:\\JavaProject\\onlineClass\\service\\service_vod\\"+"/src/main/java");
gc.setServiceName("%sService"); //去掉Service接口的首字母I
gc.setAuthor("cc");
gc.setOpen(false);
mpg.setGlobalConfig(gc);
// 3、数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3307/glkt_vod");
dsc.setDriverName("com.mysql.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("123456");
dsc.setDbType(DbType.MYSQL);
mpg.setDataSource(dsc);
// 4、包配置
PackageConfig pc = new PackageConfig();
pc.setModuleName("service_vod"); //模块名
pc.setParent("com");//包名,最后组合成 com.service_vod
pc.setController("controller");
pc.setEntity("entity");
pc.setService("service");
pc.setMapper("mapper");
mpg.setPackageInfo(pc);
// 5、策略配置
StrategyConfig strategy = new StrategyConfig();
//具体生成哪些表要具体指定
//strategy.setInclude("想要生成的表名");
strategy.setInclude("teacher");
strategy.setNaming(NamingStrategy.underline_to_camel);//数据库表映射到实体的命名策略
strategy.setColumnNaming(NamingStrategy.underline_to_camel);//数据库表字段映射到实体的命名策略
strategy.setEntityLombokModel(true); // lombok 模型 @Accessors(chain = true) setter链式操作
strategy.setRestControllerStyle(true); //restful api风格控制器
strategy.setControllerMappingHyphenStyle(true); //url中驼峰转连字符
mpg.setStrategy(strategy);
// 6、执行
mpg.execute();
}
}
启动类
@SpringBootApplication
public class ServiceVodApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceVodApplication.class, args);
}
}
修改一下引入的实体类,使用我们创建的实体类工程
加入MP配置类
到这里就算搭建成功了,可以开始着手下一步接口的撰写了。
Result结果集,都是老朋友了
项目中我们会将响应封装成json返回,一般我们会将所有接口的数据格式统一, 使前端(iOS Android, Web)对数据的操作更一致、轻松。
一般情况下,统一返回数据格式没有固定的格式,只要能描述清楚返回的数据状态以及要返回的具体数据就可以。但是一般会包含状态码、返回消息、数据这几部分内容
因为这个也算工具的一种,所以把这个结果集写到Common模块的子模块service_utils中
com.cc.result.Result,com.cc.result.ResultCodeEnum
有特定异常就先处理特定异常,匹配不到特定异常就走全局异常处理
先看看这种代码的try catch
这种try catch来捕获异常固然好,但是,代码量一大起来,超级多的try catch就会很乱
所以我们要加入全局异常处理
由于这是工具类的属性在Common包的service_utils下,和Result同级,这里只是示例,并不完整
如图是某个特定异常处理
有了这些异常处理,就可以捕获控制台输出的异常信息,从而对其进行处理
先测自定义异常,找一个接口人为制造异常
测试一下
如果我们把try catch去掉,只留下int i=10/0,这个异常算是算数异常,也就是ArithmeticException,因此也会被捕获,此时不出意外就会走特殊异常处理
最后,来试试全局异常,一个没有设置对应有任何处理的异常
当然,如果想好几种异常都走一种处理方式,也是完全可以的
只需要在异常类型注解上面加入对应类型就好
@ExceptionHandler({异常类型1.class,异常类型2.class})
以上就是全局异常处理类的相关内容
Delete请求方式+ResultFul请求风格
还是非常简单的,注意,这里是逻辑删除,没有做表的实际删除。
也就是把逻辑字段更新了一下
就是简单的按条件分页查询
位置:service模块下的service_vod子模块com.cc.service_vod.controller.TeacherController (findQueryPage)
复习一下RequestBody注解
到了前端测试的时候,发现分页不好使,怎么查都没有Limit
还以为mp出问题了,结果一看,MP的分页配置类没写
蚌埠住了,没写配置类
加上配置类就好了
位置:service模块下的service_vod子模块com.cc.service_vod.controller.TeacherController (saveTeacher)
注意,要是想修改的话,肯定是得先获取讲师的信息,才能在获取的基础上对教师信息进行改进
所以要先获取,根据id获取,这里不多赘述,欢迎去Gitee查看~
获取教师位置:service模块下的service_vod子模块com.cc.service_vod.controller.TeacherController (getTeacherById)
更新教师位置:service模块下的service_vod子模块com.cc.service_vod.controller.TeacherController (updateById)
根据id集合删讲师位置:service模块下的service_vod子模块com.cc.service_vod.controller.TeacherController (batchRemove)
前端内容比较多,这里单独再开一篇文章专门写这个
只有教师接口部分是手写的,剩下的内容都是复制粘贴
这部分是基础内容
硅谷课堂前端内容
进入会送一定额度的免费存储
(1)申请腾讯云账号:https://cloud.tencent.com/
(2)实名认证
(3)开通“对象存储COS”服务
(4)进入管理控制台
进入管理控制台,找到存储桶列表, 创建存储桶
简单填一下信息就可以了,注意不要选私有读写
后面的配置不用管,无脑跳过就行
创建完毕
点击 桶名称,进入详情页,可测试上传文件
参考文档:https://cloud.tencent.com/document/product/436/10199
Pom依赖
<dependency>
<dependency>
<groupId>com.qcloudgroupId>
<artifactId>cos_apiartifactId>
<version>5.6.54version>
dependency>
<dependency>
<groupId>joda-timegroupId>
<artifactId>joda-timeartifactId>
dependency>
dependency>
为后端服务引入依赖
配置application.properties
spring.servlet.multipart.max-file-size=1024MB
spring.servlet.multipart.max-request-size=1024MB
#不同的服务器,地址不同
tencent.cos.file.region=ap-beijing
tencent.cos.file.secretid=你的id
tencent.cos.file.secretkey=你的key
#bucket可以在控制台创建,也可以使用java代码创建
tencent.cos.file.bucketname=你的bucketName
在Service模块里新建utils工具包,把配置文件引入
/**
* 常量类,读取配置文件application.properties中的配置
*/
@Component
public class ConstantPropertiesUtil implements InitializingBean {
@Value("${tencent.cos.file.region}")
private String region;
@Value("${tencent.cos.file.secretid}")
private String secretId;
@Value("${tencent.cos.file.secretkey}")
private String secretKey;
@Value("${tencent.cos.file.bucketname}")
private String bucketName;
public static String END_POINT;
public static String ACCESS_KEY_ID;
public static String ACCESS_KEY_SECRET;
public static String BUCKET_NAME;
@Override
public void afterPropertiesSet() throws Exception {
END_POINT = region;
ACCESS_KEY_ID = secretId;
ACCESS_KEY_SECRET = secretKey;
BUCKET_NAME = bucketName;
}
}
创建Interface:FileService.java
public interface FileService {
//文件上传
String upload(MultipartFile file);
}
实现:FileServiceImpl.java
存储Service实现类
一些数据根据工具类就读取了
@Service
public class FileServiceImpl implements FileService {
@Override
public String upload(MultipartFile file) {
// Endpoint以杭州为例,其它Region请按实际情况填写。
String endpoint = ConstantPropertiesUtil.END_POINT;
String bucketName = ConstantPropertiesUtil.BUCKET_NAME;
// 1 初始化用户身份信息(secretId, secretKey)。
String secretId = ConstantPropertiesUtil.ACCESS_KEY_ID;
String secretKey = ConstantPropertiesUtil.ACCESS_KEY_SECRET;
COSCredentials cred = new BasicCOSCredentials(secretId, secretKey);
// 2 设置 bucket 的地域
// clientConfig 中包含了设置 region, https(默认 http),超时, 代理等 set 方法
Region region = new Region(ConstantPropertiesUtil.END_POINT);
ClientConfig clientConfig = new ClientConfig(region);
// 这里建议设置使用 https 协议
// 从 5.6.54 版本开始,默认使用了 https
clientConfig.setHttpProtocol(HttpProtocol.https);
// 3 生成 cos 客户端。
COSClient cosClient = new COSClient(cred, clientConfig);
try{
// 指定要上传的文件
InputStream inputStream = file.getInputStream();
// 指定文件将要存放的存储桶
// 指定文件上传到 COS 上的路径,即对象键。例如对象键为folder/picture.jpg,则表示将文件 picture.jpg 上传到 folder 路径下
String key = UUID.randomUUID().toString().replaceAll("-","")+
file.getOriginalFilename();
String dateUrl = new DateTime().toString("yyyy/MM/dd");
key = dateUrl+"/"+key;
ObjectMetadata objectMetadata = new ObjectMetadata();
PutObjectRequest putObjectRequest =
new PutObjectRequest(bucketName, key, inputStream,objectMetadata);
PutObjectResult putObjectResult = cosClient.putObject(putObjectRequest);
System.out.println(JSON.toJSONString(putObjectResult));
//https://ggkt-atguigu-1310644373.cos.ap-beijing.myqcloud.com/01.jpg
String url = "https://"+bucketName+"."+"cos"+"."+endpoint+".myqcloud.com"+"/"+key;
return url;
} catch (Exception clientException) {
clientException.printStackTrace();
return null;
}
}
}
编写controller,注入相关接口service就好
记得加跨域
直接打开Swagger进行测试
上传文件
回到腾讯云,文件已经上传上来了
(1)课程分类列表功能
这个也叫懒加载,不点就不加载
点了就展现二级内容
(2)课程分类导入功能
接口实现分析
课程分类采用树形展示,我们使用“树形数据与懒加载”的方式展现数据列表,因此需要提供的接口如下:根据上级id获取下级数据,参考element-ui文档:https://element.eleme.cn/#/zh-CN/component/table,页面搜索:树形数据与懒加载
因为没有生成相关的实体类,所以要用代码生成器生成一下,简单修改一下就可以生成
生成完毕后就可以写接口了
根据刚刚的分析,要对当前课程是否有parentId做出相关判断
controller位置:com.cc.service_vod.controller.SubjectController
serviceImpl位置:com.cc.service_vod.service.impl.FileServiceImpl (selectList、isChild)
这里有个小问题,就是Service层去调用MP的扩展方法怎么办
答案就是用BaseMapper去调用,因为已经扩展好了
引入JS 创建文件 src/api/vod/subject.js
复制粘贴样式 编写subject/list.vue src\views\vod\subject\list.vue
测试一下,包括懒加载都没有问题了
EasyExcel是阿里巴巴开源的一个excel处理框架,以使用简单、节省内存著称。EasyExcel能大大减少占用内存的主要原因是在解析Excel时没有将文件数据一次性全部加载到内存中,而是从磁盘上一行行读取数据,逐个解析。
(1)pom中引入xml相关依赖
<dependencies>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>easyexcelartifactId>
<version>2.1.1version>
dependency>
dependencies>
(2)创建实体类
Excel作为一种表,和数据库一样,也应该有实体类与之对应
设置表头和添加的数据字段
@Data
public class Stu {
//设置表头名称
@ExcelProperty("学生编号")
private int sno;
//设置表头名称
@ExcelProperty("学生姓名")
private String sname;
}
(3)实现写操作
创建方法循环设置要添加到Excel的数据
//循环设置要添加的数据,最终封装到list集合中
private static List<Stu> data() {
List<Stu> list = new ArrayList<Stu>();
for (int i = 0; i < 10; i++) {
Stu data = new Stu();
data.setSno(i);
data.setSname("张三"+i);
list.add(data);
}
return list;
}
实现最终的添加操作
public static void main(String[] args) throws Exception {
// 写法1
String fileName = "F:\\11.xlsx";
// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
// 如果这里想使用03 则 传入excelType参数即可
EasyExcel.write(fileName, 对应实体类.class).sheet("sheet表名").doWrite(data());
}
(1)创建实体类
@Data
public class Stu {
//设置表头名称
//设置列对应的属性 index代表表头下标,从0开始
@ExcelProperty(value = "学生编号",index = 0)
private int sno;
//设置表头名称
//设置列对应的属性
@ExcelProperty(value = "学生姓名",index = 1)
private String sname;
}
(2)创建读取操作的监听器
public class ExcelListener extends AnalysisEventListener<Stu> {
//创建list集合封装最终的数据
List<Stu> list = new ArrayList<Stu>();
//一行一行去读取excle内容
//默认从第二行开始读,第一行默认表头,表头在下面的invokeHeadMap中读
@Override
public void invoke(Stu user, AnalysisContext analysisContext) {
System.out.println("***"+user);
list.add(user);
}
//读取excel表头信息(第一行)
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
System.out.println("表头信息:"+headMap);
}
//读取完成后执行
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
}
}
(3)调用实现最终的读取
public static void main(String[] args) throws Exception {
//指定读取路径
String fileName = "D:\\11.xlsx";
// 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
EasyExcel.read(fileName, 实体类.class, new ExcelListener()).sheet().doRead();
}
在model模块下,创建课程表对应实体类,Excel版Subject专用
和普通的Subject分隔开
<div class="el-toolbar">
<div class="el-toolbar-body" style="justify-content: flex-start;">
<el-button type="text" @click="exportData"><i class="fa fa-plus"/> 导出</el-button>
</div>
</div>
controller接口
因为读操作要用监听器,所以创建一个
@Component
public class SubjectListener extends AnalysisEventListener<SubjectEeVo> {
@Autowired
private SubjectMapper subjectMapper;
//一行一行读取
@Override
public void invoke(SubjectEeVo subjectEeVo, AnalysisContext analysisContext) {
//调用方法添加数据库
Subject subject = new Subject();
BeanUtils.copyProperties(subjectEeVo,subject);
subjectMapper.insert(subject);
}
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
}
}
(1)在list.vue页面添加导入按钮
<el-button type="text" @click="importData"><i class="fa fa-plus"/> 导入</el-button>
(2)添加导入弹出层
<el-dialog title="导入" :visible.sync="dialogImportVisible" width="480px">
<el-form label-position="right" label-width="170px">
<el-form-item label="文件">
<el-upload
:multiple="false"
:on-success="onUploadSuccess"
:action="'http://localhost:8301/admin/vod/subject/importData'"
class="upload-demo">
<el-button size="small" type="primary">点击上传</el-button>
<div slot="tip" class="el-upload__tip">只能上传xls文件,且不超过500kb</div>
</el-upload>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogImportVisible = false">取消</el-button>
</div>
</el-dialog>
(3)添加导入弹出层属性
data() {
return {
dialogImportVisible: false,
list:[] //数据字典列表数组
}
},
(4)添加导入方法
importData() {
this.dialogImportVisible = true
},
onUploadSuccess(response, file) {
this.$message.info('上传成功')
this.dialogImportVisible = false
this.getSubList(0)
},
测试一下,导入成功
课程基本信息表course
课程描述表course_description
章节表chapter
小节和视频表video
课程分类表subject
在api目录创建course.js文件
在api目录teacher.js文件定义接口
编写list.vue页面,直接CV了
发布新课程
要操作两个表
无非就是把前端传来的CourseVo拆分成Course和CourseDescription这两个对象,分别传入对应的表
还是比较好做的
controller位置
service位置:
课程列表list.vue添加方法
course.js定义接口
添加课程按钮标签在这里添加
一看就是个更新信息,按ID查询回显+update操作
按ID查询的URL
注意,这个getById返回的是Vo,因为要带着课程描述一起更改,所以返回的是CourseFormVo
点击修改之后编辑信息进入下一步
发出的RestFul请求的id是null,这个明显是不对的
这里少了两个id,一个是课程表单ID,另一个是课程的描述id
一个是课程表单ID
看看前端的方法,明显是需要一个course的返回值的
在CourseController里的update方法
原来这里是没有返回值的
课程描述ID
这个BUG折磨了我一晚上,淦
希望能帮到诸位小伙伴
course.js定义方法
修改Info.vue页面,引入样式信息
创建Chapter-index.vue页面
CV了样式,就那样了~
重点就放在章节和小节的接口上
看看大纲的数据JSON结构:
数组结构的JSON,明显是返回List集合
Controller位置
业务比较复杂,在Service单独分离出来的业务
在这个路径下,欢迎去Gitee查看~
定义对应JS的API,都是CRUD的内容
画前端的页面,直接CV样式了
这个模块都是单表的CRUD,在Controller层直接用MybatisPlus就可以实现
所以只有Controller层
一共有两个接口,一个是回显的根据id查询课程信息,另一个是根据ID发布课程(更新一下是否发布的字段)
根据id查询课程信息
Controller位置:
service层
Mapper接口对应
这次是多表联查,不用MP了,用Mybatis去编写XML文件直接查多表
根据ID发布课程
发布课程,相当于把字段更新了
controller位置:和上面一样,Service也一样
Mybatis的绑定问题
这种问题无外乎两种
第一种:Mapper接口名称和XML文件的id对不上,没绑定上自然就报错了。这个要仔细检查
第二种:Maven没有加载这个文件
修改POM文件(就在当前模块下):
在POM最末尾(也就是 project> 之前)
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
<resources>
<resource>
<directory>src/main/javadirectory>
<includes>
<include>**/*.ymlinclude>
<include>**/*.propertiesinclude>
<include>**/*.xmlinclude>
includes>
<filtering>falsefiltering>
resource>
<resource>
<directory>src/main/resourcesdirectory>
<includes> <include>**/*.ymlinclude>
<include>**/*.propertiesinclude>
<include>**/*.xmlinclude>
includes>
<filtering>falsefiltering>
resource>
resources>
build>
修改properties文件
mybatis-plus.mapper-locations=classpath:com/atguigu/ggkt/vod/mapper/xml/*.xml
先是引入相关JS,在API目录下的course.js定义接口
再去复制样式 编写Publish.vue
(1)编写课程Controller
@ApiOperation(value = "删除课程")
@DeleteMapping("remove/{id}")
public Result remove(@PathVariable Long id) {
courseService.removeCourseById(id);
return Result.ok();
}
(2)编写课程Service
//删除课程
@Override
public void removeCourseById(Long id) {
//根据课程id删除小节
videoService.removeVideoByCourseId(id);
//根据课程id删除章节
chapterService.removeChapterByCourseId(id);
//根据课程id删除描述
descriptionService.removeById(id);
//根据课程id删除课程
baseMapper.deleteById(id);
}
(3)编写VideoService
@Service
public class VideoServiceImpl extends ServiceImpl<VideoMapper, Video> implements VideoService {
//根据课程id删除小节
@Override
public void removeVideoByCourseId(Long id) {
QueryWrapper<Video> wrapper = new QueryWrapper<>();
wrapper.eq("course_id",id);
baseMapper.delete(wrapper);
}
}
(4)编写ChapterService
//根据课程id删除章节
@Override
public void removeChapterByCourseId(Long id) {
QueryWrapper<Chapter> wrapper = new QueryWrapper<>();
wrapper.eq("course_id",id);
baseMapper.delete(wrapper);
}
API下course.js定义接口
course -> list.vue添加方法
都是老生常谈,欢迎到Gitee查看
课程统计需求
用折线图来显示观看人数数据
还可以通过时间区间对数据进行约束
所以涉及的表就是video_visitor
折线图对应的就是Echars
分析一下返回数据,一个要放x轴的日期,一个要放y轴的数量
所以,对应的就是两个集合,来存放不同的数据
因为是K-V的形式拿到最后的数据,所以这里用Key-集合Value的形式
Controller位置:
这里放出Mapper和Service的内容
最终查出来的效果,对播放数量进行了统计
Echars肯定不用想,必用
安装ECharts组件
ECharts是百度的一个项目,后来百度把Echart捐给apache,用于图表展示,提供了常规的折线图、柱状图、散点图、饼图、K线图,用于统计的盒形图,用于地理数据可视化的地图、热力图、线图,用于关系数据可视化的关系图、treemap、旭日图,多维数据可视化的平行坐标,还有用于 BI 的漏斗图,仪表盘,并且支持图与图之间的混搭。
官方网站:https://echarts.apache.org/zh/index.html
npm install --save [email protected]
编写页面
上传视频
在发布课程时候,需要添加课时并且上传课程视频,这个时候需要使用到腾讯云点播服务进行上传视频管理
这个功能要去腾讯云开一个点播服务的,大概几块钱的流量就可以了
腾讯云点播服务:https://console.cloud.tencent.com/vod/register
上传视频可将视频上传到云点播的存储中,以进行后续的处理和分发等。
前端集成有两种方式,使用“超级播放器预览”与“web播放器预览”,或者代码已经不更新,推荐使用前者,因此“web播放器预览”仅做了解。
1、查看“web播放器预览”;
说明:需要将视频进行转码,才能支持超级播放器播放,转码为:自适应码流
2、查看“任务流设置”
3、点击查看详情
当前任务流就是系统默认的“自适应码流”任务流
4、在【音视频管理】重新上传视频
5、查看详情,下面有HTML代码,复制粘贴到浏览器就可以使用
因为是视频点播接口,相关类需要创建Vod相关的播放接口
因为服务在腾讯云上,所以这里就不需要有Mapper相关接口了
引入点播相关POM依赖
< exclusion>是避免日志和SpringBoot的日志有冲突
<dependency>
<groupId>com.qcloudgroupId>
<artifactId>vod_apiartifactId>
<version>2.1.4version>
<exclusions>
<exclusion>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-log4j12artifactId>
exclusion>
exclusions>
dependency>
腾讯云点播Java文档:https://cloud.tencent.com/document/product/266/10276
Controller位置:Service_Vod下的com.cc.service_vod.controller.VodController
ServiceImpl 这里要引入腾讯云官方的SDK
ConstantPropertiesUtil是读取properties的工具类,通过这个工具类读取出相关配置内容,都是静态变量
//上传视频
@Override
public String uploadVideo(InputStream inputStream, String originalFilename) {
try {
VodUploadClient client =
new VodUploadClient(ConstantPropertiesUtil.ACCESS_KEY_ID,
ConstantPropertiesUtil.ACCESS_KEY_SECRET);
VodUploadRequest request = new VodUploadRequest();
//视频本地地址
request.setMediaFilePath("D:\\测试视频.mp4");
//指定任务流,就是之前你上传时设置的那个
request.setProcedure("任务流");
//调用上传方法,传入接入点地域及上传请求。
VodUploadResponse response = client.upload("对应的AP节点", request);
//返回文件id保存到业务表,用于控制视频播放
String fileId = response.getFileId();
System.out.println("Upload FileId = {}"+response.getFileId());
return fileId;
} catch (Exception e) {
System.out.println(e.toString());
}
return null;
}
Controller和上面一样,重点Service不太一样
还是腾讯云SDK的内容可在线生成相关删除代码:
地址:https://console.cloud.tencent.com/api/explorer?Product=vod&Version=2018-07-17&Action=DescribeMediaInfos&SignVersion=
生成出来的Service代码
//删除视频
@Override
public void removeVideo(String videoSourceId) {
try{
// 实例化一个认证对象,入参需要传入腾讯云账户secretId,secretKey,此处还需注意密钥对的保密
Credential cred =
new Credential(ConstantPropertiesUtil.ACCESS_KEY_ID,
ConstantPropertiesUtil.ACCESS_KEY_SECRET);
// 实例化要请求产品的client对象,clientProfile是可选的
VodClient client = new VodClient(cred, "");
// 实例化一个请求对象,每个接口都会对应一个request对象
DeleteMediaRequest req = new DeleteMediaRequest();
//设置删除视频ID
req.setFileId(videoSourceId);
// 返回的resp是一个DeleteMediaResponse的实例,与请求对象对应
DeleteMediaResponse resp = client.DeleteMedia(req);
// 输出json格式的字符串回包
System.out.println(DeleteMediaResponse.toJsonString(resp));
} catch (TencentCloudSDKException e) {
System.out.println(e.toString());
}
}
定义vod.js
CV页面样式 修改Video -> Form.vue页面
上传代码的部分有Bug,上传路径写固化了,而且没有官方解决办法
所以这里有了新的办法就是下面的客户端上传视频
更改是服务端Server上传
https://cloud.tencent.com/document/product/266/9219
操作步骤一(申请上传签名)
找到Java签名示例
复制一下,编写签名工具类
Signature类 在Util包下
在编写Controller来获取生成的签名,还是在那个包的Controller
因为上传也是一个页面嘛,所以也要对应的有页面进行处理
复制粘贴腾讯云给的示例代码
https://tencentyun.github.io/vod-js-sdk-v6/
请 单击此处 查看 script 方式引入的 Demo,请 单击此处 查看 Demo 源码。
创建之后把 Demo 源码CV进去
在页面上右键,跑起来,就可以对客户端上传进行测试
将课程中对应的视频全部删除
VideoController 删除小节时带着这个小节下的视频全删掉
@ApiOperation(value = "删除")
@DeleteMapping("remove/{id}")
public Result remove(@PathVariable Long id) {
videoService.removeVideoById(id);
return Result.ok();
}
提前注入,一会要调用服务删视频
一共两个接口,一个是根据课程id删除小节,另一个是根据小节id删除小节删除视频
从这里开始就算是硅谷课堂的上半部分了,因为开始了分布式的内容
硅谷课堂中间部分笔记
具体链接可以看看博客主页~