文章目录

概述

资料下载提取码:8op3

功能架构图

在这里插入图片描述

技术架构图

在这里插入图片描述

MybatisPlus复习

篇幅有限,我另起了一个笔记进行复习
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,禁止使用 floatdouble。 说明:floatdouble 在存储的时候,存在精度损失的问题,很可能在值的比较时,得到不 正确的结果。如果存储的数据范围超过 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就可以查看

实体类子模块model

先在parent下新建子模块model
以Maven模版生成就可以了
在这里插入图片描述

直接导入实体类子模块model,当然,MP自动生成也不是不行~

同样子模块也需要引入各种jar包和依赖

 <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <!--mybatis-plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <scope>provided </scope>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <scope>provided </scope>
        </dependency>
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-spring-boot-starter</artifactId>
            <!--在引用时请在maven中央仓库搜索2.X最新版本号-->
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-mongodb</artifactId>
            <scope>provided </scope>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <scope>provided </scope>
        </dependency>

        <!--创建索引库的-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
            <scope>provided </scope>
        </dependency>
    </dependencies>

服务子模块service

再建一个Service子模块
在这里插入图片描述
同样需要引入对应的依赖

    <dependencies>
        <!--数据载体-->
        <dependency>
            <groupId>com.cc</groupId>
            <artifactId>model</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
        <!--web 需要启动项目-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--mybatis-plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
        </dependency>

        <!--mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <!--开发者工具-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency>

        <!--下面这仨暂时用不到,如果提前启用了会报错-->
        <!-- 服务注册 -->
        <!--<dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>-->

        <!-- 服务调用feign -->
        <!--<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>-->

        <!-- 流量控制 -->
        <!--<dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>-->
    </dependencies>

因为service包也是对服务进行管理,所以也不留src路径,下面继续还要创建多个服务包
在这里插入图片描述
再建service_vod(视频点播)模块
在这里插入图片描述

总结

在这里插入图片描述

引入Swagger

对接口信息进行管理,测试一条龙服务
非常快乐
因为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.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>
    <version>3.3.1</version>
</dependency>

<dependency>
    <groupId>org.apache.velocity</groupId>
    <artifactId>velocity-engine-core</artifactId>
    <version>2.0</version>
</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中

  • 一个结果集 Result
  • 一个结果信息枚举集 ResultCodeEnum
    在这里插入图片描述
    具体位置:com.cc.result.Result,com.cc.result.ResultCodeEnum
    复习一下Json格式
    在这里插入图片描述
    使用的话,Result.success.(数据).message(“想填入的信息”)
    Result.success.(数据)

在这里插入图片描述

引入全局异常处理

有特定异常就先处理特定异常,匹配不到特定异常就走全局异常处理
在这里插入图片描述

示例

先看看这种代码的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注解
在这里插入图片描述

分页查询Bug

到了前端测试的时候,发现分页不好使,怎么查都没有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)
在这里插入图片描述

前台开发

前端内容比较多,这里单独再开一篇文章专门写这个
只有教师接口部分是手写的,剩下的内容都是复制粘贴
这部分是基础内容
硅谷课堂前端内容

腾讯云存储相关内容

快速起步

点击就送
在这里插入图片描述

开通“对象存储COS”服务

进入会送一定额度的免费存储

(1)申请腾讯云账号:https://cloud.tencent.com/

(2)实名认证

(3)开通“对象存储COS”服务

(4)进入管理控制台

在这里插入图片描述

创建存储桶以及测试

进入管理控制台,找到存储桶列表, 创建存储
简单填一下信息就可以了,注意不要选私有读写
后面的配置不用管,无脑跳过就行
在这里插入图片描述
创建完毕
在这里插入图片描述
点击 桶名称,进入详情页,可测试上传文件
在这里插入图片描述

创建API秘钥

在这里插入图片描述
在这里插入图片描述
基本操作已经完毕,剩下的交给后端接口

腾讯云存储后端接口开发

基础环境

参考文档:https://cloud.tencent.com/document/product/436/10199
Pom依赖

<dependency>
    <!-- 腾讯云COS依赖 -->
    <dependency>
        <groupId>com.qcloud</groupId>
        <artifactId>cos_api</artifactId>
        <version>5.6.54</version>
    </dependency>
    <!-- 日期工具栏依赖 -->
    <dependency>
        <groupId>joda-time</groupId>
        <artifactId>joda-time</artifactId>
    </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进行测试
上传文件
在这里插入图片描述
回到腾讯云,文件已经上传上来了

前端上传头像

复制粘贴样式和JS,测一下,好用
在这里插入图片描述
测试一下
在这里插入图片描述

后台开发-课程分类管理接口

需求分析

在这里插入图片描述

1、课程分类管理模块需求

(1)课程分类列表功能
这个也叫懒加载,不点就不加载
点了就展现二级内容
在这里插入图片描述
(2)课程分类导入功能

在这里插入图片描述
(3)课程分类导出功能
在这里插入图片描述

2、课程分类数据库设计

(1)创建课程分类表subject
在这里插入图片描述

3、功能实现-课程分类列表

接口实现分析

课程分类采用树形展示,我们使用“树形数据与懒加载”的方式展现数据列表,因此需要提供的接口如下:根据上级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

概述

EasyExcel是阿里巴巴开源的一个excel处理框架,以使用简单、节省内存著称。EasyExcel能大大减少占用内存的主要原因是在解析Excel时没有将文件数据一次性全部加载到内存中,而是从磁盘上一行行读取数据,逐个解析。

EasyExcel特点

  • Java领域解析、生成Excel比较有名的框架有Apache poi、jxl等。但他们都存在一个严重的问题就是非常的耗内存。如果你的系统并发量不大的话可能还行,但是一旦并发上来后一定会OOM或者JVM频繁的full gc。
  • EasyExcel采用一行一行的解析模式,并将一行的解析结果以观察者的模式通知处理(AnalysisEventListener)
  • EasyExcel是一个基于Java的简单、省内存的读写Excel的开源项目。在尽可能节约内存的情况下支持读写百M的Excel。
    比较一下二者的区别
    在这里插入图片描述

在这里插入图片描述

读写操作

EasyExcel写操作

在这里插入图片描述

(1)pom中引入xml相关依赖

<dependencies>
    <!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>easyexcel</artifactId>
        <version>2.1.1</version>
    </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());
}

在这里插入图片描述
这个时候,在D盘中已经生成了一个对应的Excel文件
在这里插入图片描述
在这里插入图片描述

EasyExcel读操作

在这里插入图片描述

(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>

JS逻辑
在这里插入图片描述

导入接口编写

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) {
    }
}

在这里插入图片描述

ServiceImpl层
在这里插入图片描述

整合导入前端

(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
在这里插入图片描述

根据相关表生成数据

在这里插入图片描述

在这里插入图片描述

后端接口编写

课程列表带条件的分页

在这里插入图片描述

controller代码位置在这里插入图片描述
service代码位置
在这里插入图片描述

整合课程列表前端

先引入路由
在这里插入图片描述
这样新的列表就出来了
在这里插入图片描述
创建对应的页面
在这里插入图片描述

在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

获取课程Form

在这里插入图片描述

Controller位置
在这里插入图片描述
ServiceImpl位置
在这里插入图片描述

更新课程

在这里插入图片描述

Controller位置
在这里插入图片描述
ServiceImpl位置
在这里插入图片描述

编辑大纲Bug

点击修改之后编辑信息进入下一步
在这里插入图片描述
在这里插入图片描述
发出的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也一样

小Bug

Mybatis的绑定问题
这种问题无外乎两种
在这里插入图片描述
第一种:Mapper接口名称和XML文件的id对不上,没绑定上自然就报错了。这个要仔细检查
第二种:Maven没有加载这个文件
在这里插入图片描述
修改POM文件(就在当前模块下):
在POM最末尾(也就是 </ project> 之前)

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
    <resources>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.yml</include>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>false</filtering>
        </resource>
        <resource>
            <directory>src/main/resources</directory>
            <includes> <include>**/*.yml</include>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>false</filtering>
        </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 echarts@4.1.0

编写页面

创建chart.vue页面直接CV了~
在这里插入图片描述

整合腾讯云点播

上传视频
在发布课程时候,需要添加课时并且上传课程视频,这个时候需要使用到腾讯云点播服务进行上传视频管理
在这里插入图片描述

需求分析
在这里插入图片描述

云点播服务

这个功能要去腾讯云开一个点播服务的,大概几块钱的流量就可以了
腾讯云点播服务:https://console.cloud.tencent.com/vod/register

在这里插入图片描述
开通好了进来就自带体验流量
在这里插入图片描述

上传视频

上传视频可将视频上传到云点播的存储中,以进行后续的处理和分发等。

  • 单击左侧菜单栏【媒资管理 > 视频管理】,默认展示【已上传】标签页;
  • 点击【上传视频】按钮;
  • 单击【选择视频】,选择本地视频文件;
  • 单击【开始上传】;
  • 页面将自动跳转至【正在上传】标签页, 本地文件所在行【状态】栏为“上传成功”时,单击【已上传】标签页,可见完成上传的视频;
    在这里插入图片描述
    到这里就上传完毕了,单击【管理】,可以查看视频详情;
    在这里插入图片描述
前端集成播放页面

前端集成有两种方式,使用“超级播放器预览”与“web播放器预览”,或者代码已经不更新,推荐使用前者,因此“web播放器预览”仅做了解。

1、查看“web播放器预览”;
在这里插入图片描述
说明:需要将视频进行转码,才能支持超级播放器播放,转码为:自适应码流

2、查看“任务流设置”

在这里插入图片描述
3、点击查看详情
在这里插入图片描述
当前任务流就是系统默认的“自适应码流”任务流

4、在【音视频管理】重新上传视频

5、查看详情,下面有HTML代码,复制粘贴到浏览器就可以使用
在这里插入图片描述

云点播服务端接口

因为是视频点播接口,相关类需要创建Vod相关的播放接口
因为服务在腾讯云上,所以这里就不需要有Mapper相关接口了
在这里插入图片描述

引入点播相关POM依赖
< exclusion>是避免日志和SpringBoot的日志有冲突

<dependency>
    <groupId>com.qcloud</groupId>
    <artifactId>vod_api</artifactId>
    <version>2.1.4</version>
    <exclusions>
        <exclusion>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
        </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删除小节删除视频
在这里插入图片描述

从这里开始就算是硅谷课堂的上半部分了,因为开始了分布式的内容
硅谷课堂中间部分笔记
具体链接可以看看博客主页~

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐