今日重点

  1. 分布式文件系统 :
  2. 文件系统概述:
  3. 新增课程时需要有课程的封面,而该封面会上传到服务中 可以通过互联网访问,数据库中保存为该图片的路径地址!
  4. (图片信息量大,查询并发也会很大)
  5. 文件系统就是管理存储系统中的数据,并可以通过操作硬盘存储数据进行对数据的持久化
  6. 分布式文件系统:
  7. 分布式文件系统解决海量数据存储的问题,搭建集群通过网络互相连接,作用都是用来存储文件数据 ( 集群每一个机器下都是不一样的数据存储!)
  8. 每个机器下又可以生成 主从关系: 实现高可用 主服务器和从服务器的文件数据是一模一样的数据!
  9. 分布式文件系统搭建集群的目的:
  10. 1. 减轻单个服务节点压力 存储数据的压力
  11. 2. 并发量 高扩展(如果数据太多,多来几个服务器 )
  12. 3. 高可用(单个服务节点构建主从关系)
  13. 分布式文件系统种类:
  14. GFS: 是一个大型的分布式文件系统 为Google云计算提供海量存储数据,处于所有核心技术的底层!
  15. 分布式文件系统提供商:
  16. 阿里(OSS):
  17. 阿里云提供的海量,安全 低成本 高可靠的云存储服务,其数据设计持久性不低于99.99999999999(12个9),服务可用性不低于99.995%!
  18. 七牛云海量存储系统(KODO):
  19. 自主研发的非结构化数据存储管理平台,支持中心和边缘存储!
  20. 成本低 存储加速-cdn 易扩展
  21. 选择七牛云的原因:
  22. 1.成本低
  23. 七牛云在商业图片服务器中,其成本低,无需前期投入 。七牛云对象存储按需使用、按需付费的便捷性,能够有效避免存储及带宽资源的闲置浪费。
  24. 2.存储加速-cdn
  25. 边缘存储可充分利用可用链路带宽,数据在边缘节点上传和下载可平均提速 60% 以上, 此特点为 CDN 的特点。
  26. cdn 免费一个月域名
  27. 3. 易扩展
  28. 利用七牛云对象存储,您的存储空间无上限的同时也无需担心扩容问题。您能够实现存储需求的弹性伸缩,从而提高业务灵活性
  29. 七牛云使用:
  30. 注册认证 充钱就完了 java SDK 文档中查找入门代码 重要数据(SK AK cdn 域名)
  31. 七牛云服务器java后端上传服务:
  32. 导入官方文档依赖
  33. 使用官方文档jdk代码 设置数据为(ZOOR 华东)
  34. 密钥管理中找到: AK SK 密钥与公钥
  35. 直接使用就行
  36. 注意:上传文件时 根据文件key判断是否存在:
  37. 存在查看hash值是否一样 一样就覆盖 不一样就会报错!
  38. 前后端分离结构执行流程:
  39. 1.向业务服务器申请上传授权(类似于Token)
  40. 2.返回上传的凭证
  41. 3.将凭证和上传文件给七牛云存储服务
  42. 4.返回上传的结果
  43. 内容管理调用文件系统:
  44. 单一职责,大家都要用
  45. RestTemplate 发送调用请求 !
  46. getForObject 只要响应体的数据
  47. getForEntity 获得对象包括请求头和响应数据
  48. postForObject 只能获得响应结果
  49. postForEntity 获取对象包括请求头和响应数据
  50. 由于功能比较单一 直接带Contorller层写代码即可,不需要在service层开发!
  51. 课程审核:
  52. 教育结构提交:
  53. 课程计划录入 ---> 教师信息录入---> 保存信息:未提交 点击提交: 成功显示已发布 失败显示审核未通过
  54. 运营平台审核
  55. 可以查看所有的教育机构的数据来进行审核
  56. 图片上传的流程:

学习目标


1理解互联网项目的图片服务器的特点
2理解分布式文件系统的特点
3能够根据文档开通和使用七牛云对象存储服务
4熟悉文件上传图的流程
5熟悉课程图片上传的业务流程和功能实现时序图
6能够根据文档搭建文件功能微服务
7能够使用RestTemplate发送Http请求
8能够根据文档定义课程图片上传接口
9能够完成课程图片上传功能实现
10熟悉课程的审核状态
11能够根据文档定义课程审核接口
12能够完成课程审核功能实现

1.文件服务技术预言

  1. 在机构创建课程信息的时候,需要将课程图片和教师图片相应的信息上传到学成在线管理平台上,学成在线会对其图片在图片服务器中进行管理。课程图片和教师图片示例图如下图:<br />课程图片

Day05-第二章-内容管理-课程图片&课程审核 - 图1

教师图片

Day05-第二章-内容管理-课程图片&课程审核 - 图2

  1. 学成在线的内容管理服务中,设计到课程图片和教师图片上传和显示,而本项目处于互联网的环境下,其海量的数据和庞大的传输访问,需要在项目的设计和技术选型时,应考虑其中。互联网项目中的图片存储,也是现如今很多厂商都去使用相应的技术方案来去解决。<br />课程图片示意图

Day05-第二章-内容管理-课程图片&课程审核 - 图3

  1. 一般对于海量的图片存储,通常的做法就是使用分布式文件系统来管理互联网项目的图片。下面我就分布式文件系统来展开对图片服务器的学习。<br />特点:<br /> 1.海量数据<br /> 2.高并发

1.1 文件系统


去了解分布式文件系统前,我们先来介绍下文件系统。
文件系统是一种存储和组织计算机数据的方法,它使得对其访问和查找变得容易,文件系统使用文件和树形目录的抽象逻辑概念代替了硬盘和光盘等物理设备使用数据块的概念,用户使用文件系统来保存数据不必关心数据实际保存在硬盘(或者光盘)的地址为多少的数据块上,只需要记住这个文件的所属目录和文件名。
通过上文可以总结为:文件系统是负责管理和存储文件的系统软件,操作系统通过文件系统提供的接口去存取文件,用户通过操作系统访问磁盘上的文件。如下图:
计算机文件系统结构图

Day05-第二章-内容管理-课程图片&课程审核 - 图4

硬盘格式:
硬盘(英语:Hard Disk Drive,缩写:HDD,有时为了与固态硬盘相区分称“机械硬盘”或“传统硬盘”)
固态硬盘固态驱动器(英语:Solid-state drive或Solid-state disk,简称SSD)是一种以集成电路制作的电脑存储设备。
只读光盘(Compact Disc Read-Only Memory,缩写CD-ROM),是一种在电脑上使用的光盘
苹果Mac硬盘采用独特的磁盘文件格式,即HFS+,也有称呼为HFS Plus的(具体英语为Hierarchical File System Plus,即分层文件系统)

1.2 分布式文件系统


互联网时代对海量数据增长,单靠靠简增加硬盘的个数已经满足不了项目的数据存储,因为硬盘传输速度有限但是数据在急剧增长,另外我们还要要做好数据备份、数据安全等。
分布式文件系统的出现就可以解决海量数据存储的问题。分布式网络存储系统采用可扩展的系统结构,利用多台存储服务器分担存储负荷,使用位置服务器定位存储信息,它不但提高了系统的可靠性、可用性和存,还易于扩展。 如下图:
分布式文件系统示例图

Day05-第二章-内容管理-课程图片&课程审核 - 图5

优点:
1.减轻单个服务节点的压力
存储数据的压力
并发的压力
2.高扩展
3.高可用

1.3 分布式文件系统种类


常见的分布式文件系统有,GFS、HDFS、FastDFS、GridFS 等。各自适用于不同的领域。它们都不是系统级的分布式文件系统,而是应用级的分布式文件存储服务。
1.GFS(Google File System)
Google文件系统(Google File System,GFS)是一个大型的分布式文件系统。它为Google云计算提供海量存储,处于所有核心技术的底层。对业界影响非常大,后来很多分布式文件系统都是参照它的设计。 由于GFS并不是一个开源的系统,我们仅仅能从Google公布的技术文档来获得一点了解,而无法进行深入的研究。
2.HDFS
HDFS,是Hadoop Distributed File System的简称,是Hadoop抽象文件系统的一种实现。HDFS是一个高度容错性的系统,适合部署在廉价的机器上。HDFS能提供高吞吐量的数据访问,非常适合大规模数据集上的应用。 HDFS的文件分布在集群机器上,同时提供副本进行容错及可靠性保证。例如客户端写入读取文件的直接操作都是分布在集群各个机器上的,没有单点性能压力。
3.FastDFS
FastDFS是用c语言编写的一款开源的分布式文件系统,它是由淘宝资深架构师余庆编写并开源。FastDFS专为互联网量身定制,充分考虑了冗余备份、负载均衡、线性扩容等机制,并注重高可用、高性能等指标,使用FastDFS很容易搭建一套高性能的文件服务器集群提供文件上传、下载等服务。
4.GridFS
GridFS是MongoDB的一个内置功能分布式文件系统,它提供一组文件操作的API以利用MongoDB存储文件,GridFS的基本原理是将文件保存在两个Collection中,一个保存文件索引,一个保存文件内容,文件内容按一定大小分成若干块,每一块存在一个Document中,这种方法不仅提供了文件存储,还提供了对文件相关的一些附加属性(比如MD5值,文件名等等)的存储。文件在GridFS中会按4MB为单位进行分块存储。

1.4 分布式文件系统提供商


不管是哪种分布式文件系统,都是需要占用一定的场地、物理硬件基础、系统环境中来构建整个分布式文件系统,对于中小型企业来说,是无法承担的起的。
现巨头公司已经将分布式文件系统构建完毕,并以服务的方式对外售卖,中小企业只需要购买便可享用分布式文件系统。现主要的分布式文件系统提供商主要有下面的厂商:
1.阿里的OSS
阿里云对象存储服务(Object Storage Service,简称 OSS),是阿里云提供的海量、安全、低成本、高可靠的云存储服务。其数据设计持久性不低于 99.9999999999%(12 个 9),服务设计可用性(或业务连续性)不低于 99.995%。
官方网站:https://www.aliyun.com/product/oss
2.七牛云海量存储系统(KODO)
七牛云海量存储系统(KODO)是自主研发的非结构化数据存储管理平台,支持中心和边缘存储。平台经过多年大规模用户验证已跻身先进技术行列,并广泛应用于海量数据管理的各类场景。 利用七牛云对象存储,您的存储空间无上限的同时也无需担心扩容问题。您能够实现存储需求的弹性伸缩,从而提高业务灵活性。
官方网站:https://www.qiniu.com/products/kodo
3.百度云存BOS
百度对象存储BOS提供稳定、安全、高效、高可扩展的云存储服务。您可以将任意数量和形式的非结构化数据存入BOS,并对数据进行管理和处理。BOS支持标准、低频、冷和归档存储等多种存储类型,满足多场景的存储需求。
官方网站:https://cloud.baidu.com/product/bos.html
本项目中将采用七牛云服务来处理项目中的图片,选择七牛云的原因如下:(面试)
1.成本低
七牛云在商业图片服务器中,其成本低,无需前期投入。七牛云对象存储按需使用、按需付费的便捷性,能够有效避免存储及带宽资源的闲置浪费。
2.存储加速-cdn
边缘存储可充分利用可用链路带宽,数据在边缘节点上传和下载可平均提速 60% 以上, 此特点为 CDN 的特点。
3. 易扩展
利用七牛云对象存储,您的存储空间无上限的同时也无需担心扩容问题。您能够实现存储需求的弹性伸缩,从而提高业务灵活性。

1.5 七牛云文件入门使用


在项目使用七牛云之前,我们需要进行注册和认证。在准备工作完毕后,将在七牛云中上图片并显示上传后的图片。

1.5.1 七牛云文件注册和认证


七牛云的对象存储系统 KODO 使用前,我们需要在官方网站上注册并认证后才可以使用,下面是注册和认证的主要流程:
1.进入七牛对象存储网站
七牛对象存储网站主页面

Day05-第二章-内容管理-课程图片&课程审核 - 图6

填写注销信息

Day05-第二章-内容管理-课程图片&课程审核 - 图7

2.登录后进入对象存储主页面
注册后在产品主页选择对象存储

Day05-第二章-内容管理-课程图片&课程审核 - 图8

Day05-第二章-内容管理-课程图片&课程审核 - 图9

3.开始认证个人身份
点击认证连接示意图

Day05-第二章-内容管理-课程图片&课程审核 - 图10

开始个人认证示意图

Day05-第二章-内容管理-课程图片&课程审核 - 图11

填写认证信息示意图(推荐使用支付宝)

Day05-第二章-内容管理-课程图片&课程审核 - 图12

支付宝登录并关联认证后信息示意图

Day05-第二章-内容管理-课程图片&课程审核 - 图13

1.5.2 七牛云文件页面控制台


在使用七牛云文件上传前,我们需要按照 官方示例 来进行操作,具体步骤如下:
1.创建空间
我们需要在首先,您需要创建一个 空间(Bucket)
根据官方的示意图来进行 创建存储空间
空间名称在七牛云上是唯一的,不能有重复。课上老师创建的存储空间名称,一旦创建后,就不能再有相同的名称。
2.上传文件
已经创建了空间,您可以向空间里上传资源。
根据官方的示意图来进行 上传文件
3.删除文件
如果您不再需要把上传的文件保存在七牛空间中,您可以删除它。
根据官方的示意图来进行 上传文件
PS:删除空间文件不可恢复,慎重操作。
对于管理平台的其他操作,可以参照 官方文档 来进行学习。

1.5.3 七牛云文件服务代码编程


上面我们通过七牛云的管理平台上传图片,实际开发中一般是通过代码来上传图片的。对此七牛云也为 Java 提供了相应的 SDK 来上传图片。上传图片的官方 Java 示例代码:示例代码
为安全考虑,七牛对上传文件定义了安全机制,需要在上传文件时,除了上传的图片,还需要上传的凭证。凭证类似于火车票或船票。上传凭证需要使用方通过 AccessKey + SecretKey + 存储空间名称 来生成。
上传示例图

Day05-第二章-内容管理-课程图片&课程审核 - 图14

  1. 七牛云为 Java 提供了相应的 SDK 来上传图片,对此我们需要引入七牛的 SDK 的依赖包到工程中,一下是 Maven 的依赖坐标:<br />创建测试工程 test-qiniu ,并将下面的依赖引入到 pom 文件中<br />●七牛云依赖坐标
  1. <dependencies>
  2. <!-- 测试junit -->
  3. <dependency>
  4. <groupId>junit</groupId>
  5. <artifactId>junit</artifactId>
  6. <version>4.10</version>
  7. <scope>test</scope>
  8. </dependency>
  9. <!-- 七牛云依赖 -->
  10. <dependency>
  11. <groupId>com.qiniu</groupId>
  12. <artifactId>qiniu-java-sdk</artifactId>
  13. <version>7.2.11</version>
  14. </dependency>
  15. </dependencies>


上传图片的官方代码

  1. package com.xuecheng.imgs.test;
  2. import com.google.gson.Gson;
  3. import com.qiniu.common.QiniuException;
  4. import com.qiniu.common.Zone;
  5. import com.qiniu.http.Response;
  6. import com.qiniu.storage.Configuration;
  7. import com.qiniu.storage.UploadManager;
  8. import com.qiniu.storage.model.DefaultPutRet;
  9. import com.qiniu.util.Auth;
  10. import org.junit.Test;
  11. /**
  12. * <p></p>
  13. *
  14. * @Description:
  15. */
  16. public class QiniuTest {
  17. @Test
  18. public void uploadFile() {
  19. //构造一个带指定 Region 对象的配置类
  20. Configuration cfg = new Configuration(Zone.huabei());
  21. //...其他参数参考类注释
  22. UploadManager uploadManager = new UploadManager(cfg);
  23. //...生成上传凭证,然后准备上传
  24. String accessKey = "your access key";
  25. String secretKey = "your secret key";
  26. String bucket = "your bucket name";
  27. //如果是Windows情况下,格式是 D:\\qiniu\\test.png
  28. String localFilePath = "/home/qiniu/test.png";
  29. //默认不指定key的情况下,以文件内容的hash值作为文件名
  30. String key = "文件的名称,可以自己定义";
  31. Auth auth = Auth.create(accessKey, secretKey);
  32. String upToken = auth.uploadToken(bucket);
  33. try {
  34. Response response = uploadManager.put(localFilePath, key, upToken);
  35. //解析上传成功的结果
  36. DefaultPutRet putRet =
  37. new Gson().fromJson(response.bodyString(), DefaultPutRet.class);
  38. System.out.println(putRet.key);
  39. System.out.println(putRet.hash);
  40. } catch (QiniuException ex) {
  41. Response r = ex.response;
  42. System.err.println(r.toString());
  43. try {
  44. System.err.println(r.bodyString());
  45. } catch (QiniuException ex2) {
  46. //ignore
  47. }
  48. }
  49. }
  50. }

PS:七牛云对图片的保存为生成一个Key,当前Key可以进行制定。
文件Key不指定:默认采用文件的 hash 值来作为文件的 key 值。
文件Key指定:七牛云会安装制定的名称来作为文件的 key 值,但要注意的是,key 值在一个存储空间中必须唯一。

1.5.4 七牛云文件服务编程模型


之前我们在七牛云管理平台上对图片进行管理,但实际开发中,需要在客户端使用代码管理云平台的图片。七牛云对象存储对开发人员提供了编程模型和 Java SDK 开发工具包。
1.代码编程模型
官方以保护数据安全和考虑架构合理性为出发点,对基于七牛云存储服务如何进行开发提供了一些设计和编码建议。希望开发者在使用七牛云存储服务之前详细阅读这些建议,并尽可能的符合这些原则,以免造成不必要的时间浪费和带来数据安全风险。
七牛云存储服务构建的应用

Day05-第二章-内容管理-课程图片&课程审核 - 图15

在上图中,可以看到三个关键组件:
●七牛云存储服务 七牛云存储服务是以键值对方式提供非结构化资源存储服务。向业务服务器提供资源管理服务,向客户端提供资源上传和下载服务。
●业务服务器业务服务器需要开发者自行管理和维护,并且至少提供如下几个基本功能:
○生成各种安全凭证(参考安全机制),安全凭证的创建不能在客户端进行,否则会产生极大的安全风险。
○使用关系型数据库(例如MySQL)管理用户帐号信息。最终用户信息的管理并非云存储服务的功能范畴。云存储服务只管理企业账号。
○使用数据库管理资源元数据和资源之间的关联关系。
○响应客户端的业务请求,执行业务流程并返回执行结果。
●客户端 客户端通常同时是资源的生产方和消费方。客户端在展示内容时,通常需要先从业务服务器获取资源的元信息,并得到必要的下载凭证,然后使用下载凭证从七牛云存储服务获取待展示的资源内容,从而实现一个完整的内容展示过程。
2.上传业务流程
客户端在上传资源到七牛云存储之前要先从业务服务器获取一个有效的上传凭证,因此需要先后和两个服务端打交道。
上传业务流程方式:

Day05-第二章-内容管理-课程图片&课程审核 - 图16

客户端上传步骤:
1.客户端(手机 或 PC 等)向学成在线服务端申请上传凭证。
2.学成在线服务端将上传凭证给予客户端。
3.客户端带着凭证上传文件。
4.七牛云验证凭证,通过后完成文件上传。
5.七牛云平台将上传后的结果返回给客户端。

2.课程图片上传


学车在线在内容管理中需要上传图片,需要按照一定的流程来对图片进行上传并在页面中显示上传后的结果。下面是课程上传图片流程图:
课程上传时序图

Day05-第二章-内容管理-课程图片&课程审核 - 图17

2.1 课程上传图片接口业务需求


根据课程上传流程图中,需要在内容管理微服务中定义一下接口:
●获得文件上传凭证接口—内容管理服务 调用文件系统生成上传凭证。
●生成凭证接口—文件系统服务 根据相应的参数生成凭证。

2.2 文件系统微服务器搭建


学成在线对于图片上传的流程中,需要单独搭建文件服务器。本次的文件服务器我们使用传智播客团队开发的开源项目文件服务工程:farming-master。
github地址:https://github.com/fightingape/farming

2.2.1 farming 文件服务的介绍


farming 文件服务工程有 SpringBoot 构建的微服务工程,对七牛云服务的 SDK 进行二次封装,并对外提供 Http 接口地址来操作七牛云服务内容。其目录结构如下图:
文件服务工程结构

Day05-第二章-内容管理-课程图片&课程审核 - 图18

farming 文件服务工程所有对外的接口都在 controller 包下定义,对外的接口主要有三大类。
1.TokenController 用于对外提供获得上传文件凭证接口。
2.UploadController 用于对外提供文件上传的接口。
3.ManagementController 用于对外提供文件的管理的接口。
本次业务的开发中,我们只使用 TokenController 提供的接口。
控制层接口代码示意图

Day05-第二章-内容管理-课程图片&课程审核 - 图19

farming 文件服务工程在文件上传后,会将上传后的文件信息保存到文件服务的后端数据库中,对其数据进行记录操作,这样可以保证数据的完整性。
在学成在线中,此业务服务工程单独成立,并在项目构建作为基础工程被创建。只需要将其引入便可以使用。下面我们开始构建图片业务服务工程 farming 。

2.2.2 初始化数据库


farming需要相应的数据库来支撑,首先我们需要将相应的数据库构建出来。下面是构架数据的相关 SQL 语句。
●建库语句

  1. SET NAMES utf8mb4;
  2. SET FOREIGN_KEY_CHECKS = 0;
  3. CREATE DATABASE `farmingdb` CHARACTER SET 'utf8' COLLATE 'utf8_general_ci';
  4. Use farmingdb;
  5. -- ----------------------------
  6. -- Table structure for fileobject
  7. -- ----------------------------
  8. DROP TABLE IF EXISTS `fileobject`;
  9. CREATE TABLE `fileobject` (
  10. `ID` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
  11. `fileName` varchar(50) DEFAULT NULL COMMENT '原文件名',
  12. `origin` varchar(20) NOT NULL COMMENT '存储源',
  13. `resourceKey` varchar(100) NOT NULL COMMENT '文件key',
  14. `flag` varchar(10) NOT NULL COMMENT '正反面',
  15. `downloadUrl` varchar(200) DEFAULT NULL COMMENT '文件下载地址',
  16. `isProtect` tinyint(1) NOT NULL COMMENT '公有还是私有',
  17. `uploaddate` datetime NOT NULL,
  18. PRIMARY KEY (`ID`)
  19. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  20. SET FOREIGN_KEY_CHECKS = 1;

2.2.3 部署文件服务

2.2.3.1 导入工程
将 farming-master 工程放到 和 xc-parent 同级目录下。

2.2.3.3 注册七牛云对象存储服务
本项目默认使用了七牛云的对象存储服务,需要注册后开通,
1.新建存储空间
创建存储空间

Day05-第二章-内容管理-课程图片&课程审核 - 图20

根据自己的环境,创建自己的存储空间的名称,上面的存储名称只是演示。
2.创建成功,获取融合 CDN 测试域名
获得CDN测试域名

Day05-第二章-内容管理-课程图片&课程审核 - 图21

上图解释:
注释①:学员在测试开发的时候使用七牛云提供的免费 CDN 域名。
注释②:老师在课上演示时使用购买的 CDN 域名。
两者使用起来是一样的,注释一是免费的,注释二是收费的区别。
3.获取七牛云服务秘钥
获得秘钥信息

Day05-第二章-内容管理-课程图片&课程审核 - 图22

2.3.4 配置文件服务参数


配置文件:farming/src/main/resources/application.yml
1.配置七牛云获取到AccessKey, SecretKey和测试域名

  1. my:
  2. file:
  3. qiniu:
  4. secretKey: 自己的 secretKey
  5. accessKey: 自己的 accessKey
  6. domainOfBucket: 创建存储空间给的默认的 cdn 域名

2.配置数据库连接

  1. datasource:
  2. url: jdbc:mysql://自己数据库的ip地址:3306/farmingdb?characterEncoding=utf8&useUnicode=true&serverTimezone=Asia/Shanghai
  3. driver-class-name: com.mysql.cj.jdbc.Driver
  4. username: root
  5. password: root

2.3.5 获得上传凭证服务接口


farming 文件服务工程中也对 swagger 进行了集成。我们可以通过 swagger 生成的文档来查看相关的接口。但接口中对参数的说明少,故此,定义出接口文档信息。
我们需要将 farming -master 服务启动后,就可以对服务中提供的下面接口进行测试了。
PS:下面的接口已经提供好了,不需要进行开发。
1.获得文件上传凭证接口
请求地址

Day05-第二章-内容管理-课程图片&课程审核 - 图23

接口传入传出列表

Day05-第二章-内容管理-课程图片&课程审核 - 图24

2.测试数据
POST http://localhost:56082/farming/generatetoken
●QueryString 参数

  1. origin=qiniu

●RequestBody 参数

  1. {
  2. "tokenType":"1",
  3. "scope":"自己的存储空间的名称",
  4. "deadline":3600,
  5. "key":"111xxxeeee"
  6. }

Day05-第二章-内容管理-课程图片&课程审核 - 图25

2.3 课程上传图片功能业务实现


学成在线的图片上传,需要两个微服务的开发,文件系统服务和内容管理服务。根据业务时序图,需要在每个服务中开发一个接口来完成整个的图片上传操作,下面进行分别开发。

2.3.1 文件系统微服务开发


由于我们使用的是传智播客团队开发的文件系统服务工程:farming-master。其中已经对七牛云的 SDK 进行二次封装并对外提供相应的接口,只需要按照接口的传入传出参数开发即可。

2.3.2 内容管理微服务开发


在图片上传的业务时序图,内容管理需要远程调用文件系统微服务的接口。调用接口时需要传入生成凭证的参数,并将结果响应给前端。
服务调用示意图

Day05-第二章-内容管理-课程图片&课程审核 - 图26

2.3.2.1 服务间的远程调用
由于内容服务需要远程调用文件服务,文件服务提供的是 Http 接口地址,这里我们需要通过代码的方式在内容管理服务中完成远程调用,而代码实现远程调用的 Api 使用的是 Spring 提供的 RestTemplate
RestTemplate 类是在 Spring Framework 3.0 开始引入的,是 Spring 封装的处理 HTTP 请求的类 。该类的 Api 提供了 RESTful 请求操作的接口,如 GET,POST,PUT,DELETE 等 。下面是项目中使用的 Api 方法:
RestTemplate 常用Api 方法。

| 方法名

| 描述

| | —- | —- | | getForObject

| 通过 GET 请求获得响应结果

| | getForEntity

| 通过 GET 请求获取 ResponseEntity
对象,包容有状态码,响应头和响应数据

| | postForObject

| 通过 POST 请求创建资源,获得响应结果

| | postForEntity

| 通过 POST 请求获取 ResponseEntity
对象,包容有状态码,响应头和响应数据

|

在项目中使用 RestTemplate 需要对其进行配置并将实例化对象配置到 Spring 容器当中。
1.RestTemplate 的配置
●配置类

  1. package com.xuecheng.content.config;
  2. import org.springframework.context.annotation.Bean;
  3. import org.springframework.context.annotation.Configuration;
  4. import org.springframework.http.client.ClientHttpRequestFactory;
  5. import org.springframework.http.client.SimpleClientHttpRequestFactory;
  6. import org.springframework.web.client.RestTemplate;
  7. /**
  8. * RestTemplate 配置类
  9. */
  10. @Configuration
  11. public class RestTemplateConfig {
  12. /**
  13. * 创建 RestTemplate 示例类并注入到 Spring 容器中
  14. * @param factory {@link SimpleClientHttpRequestFactory} 接工厂
  15. * @return
  16. */
  17. @Bean
  18. public RestTemplate restTemplate(ClientHttpRequestFactory factory){
  19. return new RestTemplate(factory);
  20. }
  21. /**
  22. * 创建 Http 连接工厂,配置连接时长和读取时长
  23. * @return
  24. */
  25. @Bean
  26. public ClientHttpRequestFactory simpleClientHttpRequestFactory() {
  27. SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
  28. factory.setConnectTimeout(15000);
  29. factory.setReadTimeout(5000);
  30. return factory;
  31. }
  32. }

2.RestTemplate 的 方法使用
●测试类

  1. package com.xuecheng.content.rest;
  2. import org.junit.Test;
  3. import org.junit.runner.RunWith;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.boot.test.context.SpringBootTest;
  6. import org.springframework.http.ResponseEntity;
  7. import org.springframework.test.context.junit4.SpringRunner;
  8. import org.springframework.web.client.RestTemplate;
  9. import java.util.HashMap;
  10. import java.util.Map;
  11. /**
  12. * <p></p>
  13. *
  14. * @Description:
  15. */
  16. @SpringBootTest
  17. @RunWith(SpringRunner.class)
  18. public class RestTemplateTest {
  19. @Autowired
  20. private RestTemplate restTemplate;
  21. @Test
  22. public void testPostMethod() {
  23. // 1.url路径地址
  24. String url = "http://localhost:56082/farming/generatetoken?origin=qiniu";
  25. // 2.构建请求体的json数据
  26. Map<String, Object> dataMap = new HashMap<>();
  27. dataMap.put("tokenType", "1");
  28. dataMap.put("scope", "自己的七牛云cdn域名");
  29. dataMap.put("deadline", 3600);
  30. dataMap.put("key", "上传文件的名称");
  31. /**
  32. * 方法 postForEntity
  33. * 作用:发送post请求,并已制定的格式返回响应数据
  34. * 参数:
  35. * 1.url路径地址
  36. * 2.post请求器中的参数,再次会使用map类型来封装
  37. * 3.响应使用指定类型返回
  38. * 4.url路径的参数
  39. */
  40. // 3.发送请求
  41. ResponseEntity<Map> response = restTemplate.postForEntity(url, dataMap, Map.class);
  42. // 4.获得响应结果
  43. Map body = response.getBody();
  44. System.out.println(body);
  45. }
  46. }

2.3.2.2 图片获得上传凭证接口定义
下面将定义内容管理微服务中的获得上传凭证接口,此接口为前端调用。
1.接口参数列表
根据前后端传入参数列表来定义接口
Http接口地址

Day05-第二章-内容管理-课程图片&课程审核 - 图27

接口传入传出列表

Day05-第二章-内容管理-课程图片&课程审核 - 图28

2.定义响应数据
在xc-api工程下定义实体类:

  1. package com.xuecheng.api.content.model.file;
  2. import io.swagger.annotations.ApiModel;
  3. import lombok.Data;
  4. /**
  5. * <p></p>
  6. *
  7. * @Description:
  8. */
  9. @Data
  10. @ApiModel("文件上传凭证封装类")
  11. public class UploadTokenResult {
  12. private String tokenType;
  13. private String scope;
  14. private String key;
  15. private String qnToken;
  16. private String up_region;
  17. private String domain;
  18. private int deadline;
  19. }

3.接口定义
在 xc-api 接口工程中的 FileAndSmsAPI 接口定义方法,接口中没有任何的参数传入,传出的参数有些从文件服务中返回。
返回参数的个数和名称不太确定,所有使用弱类型的 Map 来进行封装,这样文件服务返回的数据发生变化,内容管理的代码不涉及到修改操作。
●内容管理图片上传接口定义

  1. package com.xuecheng.api.content;
  2. import io.swagger.annotations.Api;
  3. import io.swagger.annotations.ApiOperation;
  4. import java.util.Map;
  5. /**
  6. * <P>
  7. * 课程管理服务获得外部接口 Api
  8. * </p>
  9. */
  10. @Api(value = "文件、图片上传及 短信服务 服务 Api ",tags = "获得外部接口 Api")
  11. public interface FileManagerApi {
  12. @ApiOperation(value = "获取七牛云上传凭证, 拿到凭证客户端上传")
  13. UploadTokenResult qiniuUploadToken();
  14. }

2.3.2.3 图片获得上传凭证配置信息
内容管理微服务中,需要对配置七牛相关的内容,其中内容主要有两种信息的配置。
●对文件系统微服务接口所需参数的配置
●对前端所需参数的配置
1.对文件系统微服务接口所需参数的配置
内容管理微服务调用文件系统微服务,需要将文件系统微服的请求地址和文件存储空间名称配置到内容管理微服中。
●配置信息

  1. #文件系统微服的请求地址
  2. file.service.url=http://127.0.0.1:56082/farming/generatetoken?origin=qiniu
  3. #文件存储空间名称
  4. file.service.bucket= 自己的七牛云存储空间的名称

2.对前端所需参数的配置
内容管理除了将获得的上传凭证传给前端,还需要上传文件相关的参数配置给予前端,方便前端获得凭证后进行文件上传。
●配置信息

  1. #文件存储区域的地址
  2. file.service.upload.region = http://upload.qiniu.com
  3. #文件访问的cdn加速域名
  4. cdn.domain = 自己七牛云CND域名(例如:qqqfxpqgk.hd-bkt.clouddn.com)
  5. file.token.type = 1
  6. file.token.deadline = 3600

将上面的两种类型的配置信息配置到配置中心 nacos 中的 content-service 应用中。如下示例图:
配置中心的配置信息示意图

Day05-第二章-内容管理-课程图片&课程审核 - 图29

2.3.2.3 图片获得上传凭证接口开发
内容管理在此业务中,前端无任何的参数传入,内容管理微服务只是构建文件系统微服务中的接口参数,和调用改接口。所有这里没有 service 和 dao 层的开发。
在 xc-content-service 内容管理微服务中,创建并编写功能实现代码。
1.controller 开发

  1. package com.xuecheng.content.controller;
  2. import com.xuecheng.api.content.FileManagementApi;
  3. import com.xuecheng.api.content.model.file.UploadTokenResult;
  4. import com.xuecheng.common.domain.code.CommonErrorCode;
  5. import com.xuecheng.common.exception.ExceptionCast;
  6. import com.xuecheng.content.common.constant.ContentErrorCode;
  7. import lombok.extern.slf4j.Slf4j;
  8. import org.apache.commons.lang.RandomStringUtils;
  9. import org.springframework.beans.factory.annotation.Autowired;
  10. import org.springframework.beans.factory.annotation.Value;
  11. import org.springframework.http.HttpStatus;
  12. import org.springframework.http.ResponseEntity;
  13. import org.springframework.web.bind.annotation.GetMapping;
  14. import org.springframework.web.bind.annotation.RestController;
  15. import org.springframework.web.client.RestTemplate;
  16. import java.util.HashMap;
  17. import java.util.Map;
  18. import java.util.UUID;
  19. /**
  20. * <p></p>
  21. *
  22. * @Description:
  23. */
  24. @Slf4j
  25. @RestController
  26. public class FileManagerController implements FileManagerApi {
  27. @Autowired
  28. private RestTemplate restTemplate;
  29. //#文件系统微服的请求地址
  30. // file.service.url=http://127.0.0.1:56082/farming/generatetoken?origin=qiniu
  31. // #文件存储空间名称
  32. // file.service.bucket= xc140-static-imgs
  33. //
  34. //
  35. // #文件存储区域的地址
  36. // file.service.upload.region = http://upload.qiniu.com
  37. // #文件访问的cdn加速域名
  38. // cdn.domain = r9hurnt4x.hd-bkt.clouddn.com
  39. // file.token.type = 1
  40. // file.token.deadline = 3600
  41. @Value("${file.service.url}")
  42. private String url;
  43. @Value("${file.service.bucket}")
  44. private String bucket;
  45. @Value("${file.service.upload.region}")
  46. private String region;
  47. @Value("${cdn.domain}")
  48. private String domain;
  49. @Value("${file.token.type}")
  50. private String tokenType;
  51. @Value("${file.token.deadline}")
  52. private int deadline;
  53. @GetMapping("common/qnUploadToken")
  54. public UploadTokenResult generateUploadToken() {
  55. // 1.发送请求获得上传令牌
  56. HashMap<String, Object> requestBody = new HashMap<>();
  57. //{
  58. // "tokenType":"1",
  59. // "scope":"xc140-static-imgs",
  60. // "deadline":3600,
  61. // "key":"111xxxeeee"
  62. // }
  63. requestBody.put("tokenType",tokenType);
  64. requestBody.put("scope",bucket);
  65. requestBody.put("deadline",deadline);
  66. // 由于qiniu的文件key是确保文件的唯一,需要生成一个唯一标识
  67. String fileKey = UUID.randomUUID().toString() + RandomStringUtils.randomAlphanumeric(16);
  68. requestBody.put("key",fileKey);
  69. /*
  70. * 参数:
  71. * 1.访问路径地址
  72. * 2.请求体数据(json数据)
  73. * 3.返回responsebody要封装的类型
  74. * 4.querystring参数(选填)
  75. * */
  76. ResponseEntity<Map> response = restTemplate.postForEntity(url, requestBody, Map.class);
  77. // 2.解析响应结果,封装数据并返回给前端
  78. // 判断获得的结果数据:
  79. // 1.响应状态(200) 2.响应result(code=0--成功)
  80. HttpStatus statusCode = response.getStatusCode();
  81. if (!(HttpStatus.OK == statusCode)) {
  82. ExceptionCast.cast(ContentErrorCode.E_120025);
  83. }
  84. Map body = response.getBody();
  85. Object o = body.get("code");
  86. int code = CommonErrorCode.SUCCESS.getCode();
  87. if (!(code == (int)o)) {
  88. ExceptionCast.cast(ContentErrorCode.E_120026);
  89. }
  90. UploadTokenResult tokenResult = new UploadTokenResult();
  91. tokenResult.setQnToken(body.get("result").toString());
  92. tokenResult.setDeadline(deadline);
  93. tokenResult.setDomain(domain);
  94. tokenResult.setTokenType(tokenType);
  95. tokenResult.setKey(fileKey);
  96. tokenResult.setScope(bucket);
  97. tokenResult.setUp_region(region);
  98. return tokenResult;
  99. }
  100. }

2.3.3 信息接口测试


测试环境需要启动的微服务有:
1.服务网关 xc-gateway-service (端口:63010)
2.内容管理 xc-content-service (端口:63040)
3.文件管理 farming-master (端口:56082)
1. 使用 postman 在请求信息中添加请求参数
GET http://localhost:63010/content/common/qnUploadToken
无需访问令牌。

Day05-第二章-内容管理-课程图片&课程审核 - 图30

3.课程审核


教育机构通过教学管理中心调用内容管理微服务完成课程数据的录入,其主要涉及到的内容为:
1.课程基本信息—CourseBase
前端页面中添加课程基本信息中,主要是对课程基本信息的数据填写。
课程基本信息示例图

Day05-第二章-内容管理-课程图片&课程审核 - 图31

2.课程营销信息—CourseMarket
课程营销示例图

Day05-第二章-内容管理-课程图片&课程审核 - 图32

3.课程计划信息—Teachplan
课程计划示例图

Day05-第二章-内容管理-课程图片&课程审核 - 图33

4.课程教师信息—CourseTeacher
课程教师示例图

Day05-第二章-内容管理-课程图片&课程审核 - 图34

3.1 课程内容业务流程


教育机构就完成了课程信息录入,并且提交课程信息,由运营平台进行审核课程内容。内容审核通过后,才可以进行课程的发布,学员进行对课程的学习。内容未审核,需要由教学结构继续修改课程信息,并保存提交课程信息,运营平台再来审核。
内容管理的课程业务流程

Day05-第二章-内容管理-课程图片&课程审核 - 图35

3.2 课程内容审核业务需求


通过课程内容业务流程,我们需要在内容管理完成下面需求。
1.教育机构提交课程信息功能实现。
教学机构对课程进行提交操作,修改课程的状态为 “已提交” ,并等待运营平台进行审核。提交审核只能对课程状态为:’未提交‘ 和 ’审核未通过‘。
操作示意图

Day05-第二章-内容管理-课程图片&课程审核 - 图36

2.运营平台可以查询所有机构下的课程信息。
学成在线运营平台需要查看所有机构下的课程信息,对其 “已提交” 状态的课程进行审核。
操作示意图

Day05-第二章-内容管理-课程图片&课程审核 - 图37

3.运行平台可以课程信息进行审核。
学成在线运营平台工作人员对课程审核要给出结果:“审核通过”,“审核未通过”。“审核通过” 后,教育机构可以对课程信息继续进行业务操作。“审核未通 过” ,需要教学机构进行修改并在此提交。
操作示意图

Day05-第二章-内容管理-课程图片&课程审核 - 图38

Day05-第二章-内容管理-课程图片&课程审核 - 图39

3.3 课程提交功能实现


教学机构对课程进行提交操作,修改课程的状态为 “已提交” ,并等待运营平台进行审核。提交审核只能对课程状态为:’未提交‘ 和 ’审核未通过‘。
操作示意图

Day05-第二章-内容管理-课程图片&课程审核 - 图40

3.3.1 课程提交功能接口定义


1.接口参数列表
根据前后端传入参数列表来定义接口
Http接口地址

Day05-第二章-内容管理-课程图片&课程审核 - 图41

接口传入传出列表

Day05-第二章-内容管理-课程图片&课程审核 - 图42

2. 接口编写
在 xc-api 工程中的 CourseBaseApi中添加接口方法。
●课程提交功能接口

  1. @Api(value = "课程基本信息管理Api",tags = "内容-课程基本信息管理",
  2. description = "课程基本信息业务管理")
  3. public interface CourseBaseApi {
  4. //其他代码省略
  5. @ApiOperation(value = "提交审核")
  6. @ApiImplicitParam(name = "courseBaseId", value = "课程ID", required = true, dataType = "Long", paramType = "query", example = "1")
  7. void commitCourseBase(Long courseBaseId);
  8. }

3.3.2 课程提交功能接口开发


1.DAO 层
Mybatis Plus 已经简化了单表操作,它提供的 Api 就可以完成添加数据操作,所有不需要进行编写。
2.service编写
●接口

  1. /**
  2. * <p>
  3. * 课程基本信息 服务类
  4. * </p>
  5. */
  6. public interface CourseBaseService extends IService<CourseBase> {
  7. //其他代码省略
  8. /**
  9. * 根据课程Id提交课程信息
  10. * @param courseBaseId
  11. * @param companyId
  12. */
  13. void commitCourseBase(Long courseBaseId, Long companyId);
  14. }

●实现类

  1. @Service
  2. public class CourseBaseServiceImpl extends ServiceImpl<CourseBaseMapper, CourseBase> implements CourseBaseService {
  3. /*
  4. * 业务分析:
  5. * 1.判断关键数据
  6. * 2.判断业务数据
  7. * 课程基础信息
  8. * 判断是否存在
  9. * 判断是否是同一家教学机构
  10. * 判断是否删除
  11. * 判断审核状态
  12. * 3.修改课程审核状态
  13. * 已提交
  14. * */
  15. @Transactional
  16. public void commitCourseBase(Long courseId, Long companyId) {
  17. //1.判断关键数据
  18. if (ObjectUtils.isEmpty(courseId)||
  19. ObjectUtils.isEmpty(companyId)
  20. ) {
  21. ExceptionCast.cast(CommonErrorCode.E_100101);
  22. }
  23. // 2.判断业务数据
  24. // 课程基础信息
  25. // 判断是否存在
  26. // 判断是否是同一家教学机构
  27. // 判断是否删除
  28. // 判断审核状态
  29. getCourseByLogic(companyId, courseId);
  30. // 3.修改课程审核状态
  31. // 已提交
  32. // createDate changeDate 使用MP自动填充来完成
  33. // UpdateWrapper 会将MP的自动填充功能失效
  34. LambdaUpdateWrapper<CourseBase> updateWrapper = new LambdaUpdateWrapper<>();
  35. updateWrapper.set(CourseBase::getAuditStatus, CourseAuditEnum.AUDIT_COMMIT_STATUS.getCode());
  36. updateWrapper.set(CourseBase::getChangeDate, LocalDateTime.now());
  37. updateWrapper.eq(CourseBase::getId, courseId);
  38. boolean result = this.update(updateWrapper);
  39. if (!result) {
  40. ExceptionCast.cast(ContentErrorCode.E_120017);
  41. }
  42. }
  43. private void getCourseByLogic(Long companyId, Long courseBaseId) {
  44. CourseBase courseBase = getCourseBaseByBaseId(courseBaseId, companyId);
  45. String auditStatus = courseBase.getAuditStatus();
  46. if (CourseAuditEnum.AUDIT_PASTED_STATUS.getCode().equals(auditStatus)||
  47. CourseAuditEnum.AUDIT_COMMIT_STATUS.getCode().equals(auditStatus)||
  48. CourseAuditEnum.AUDIT_PUBLISHED_STATUS.getCode().equals(auditStatus)
  49. ) {
  50. ExceptionCast.cast(ContentErrorCode.E_120018)
  51. }
  52. }
  53. }

3.Controller 编写
在 ContentBaseController 中新增方法,如下:

  1. @RestController
  2. @RequestMapping("coursebase")
  3. public class CourseBaseController implements CourseBaseApi {
  4. @Autowired
  5. private CourseBaseService courseBaseService;
  6. //其他代码省略
  7. @GetMapping("course/commit/{courseBaseId}")
  8. public void commitCourseBase(@PathVariable Long courseBaseId) {
  9. Long companyId = SecurityUtil.getCompanyId();
  10. courseBaseService.commitCourseBase(courseBaseId, companyId);
  11. }
  12. }

3.3.3 信息接口测试


测试环境需要启动的微服务有:
1.注册中心 xc-discover-service (端口:63000)
2.服务网关 xc-gateway-service (端口:63010)
3.内容管理 xc-content-service (端口:63040)
1. 使用 postman 在请求信息中添加请求参数
GET http://127.0.0.1:63010/content/course/commit/47
●请求头(访问令牌)

  1. 请求头的 key 值: authorization
  2. 请求头的 value 值:
  3. Bearer ewogICAgImF1ZCI6IFsKICAgICAgICAieHVlY2hlbmctcmVzb3VyY2UiCiAgICBdLAogICAgInBheWxvYWQiOiB7CiAgICAgICAgIjExNzcxNDQyMDk0NjMxMjgxMjUiOiB7CiAgICAgICAgICAgICJyZXNvdXJjZXMiOiBbCiAgICAgICAgICAgIF0sCiAgICAgICAgICAgICJ1c2VyX2F1dGhvcml0aWVzIjogewogICAgICAgICAgICAgICAgInJfMDAxIjogWwogICAgICAgICAgICAgICAgICAgICJ4Y19jb21wYW55X21vZGlmeSIsCgkJCQkJInhjX2NvbXBhbnlfdmlldyIsCgkJCQkJInhjX2NvdXJzZV9iYXNlX2RlbCIsCgkJCQkJInhjX2NvdXJzZV9iYXNlX2VkaXQiLAoJCQkJCSJ4Y19jb3Vyc2VfYmFzZV9saXN0IiwKCQkJCQkieGNfY291cnNlX2Jhc2Vfc2F2ZSIsCgkJCQkJInhjX2NvdXJzZV9iYXNlX3ZpZXciLAoJCQkJCSJ4Y19jb3Vyc2VfcHVibGlzaCIsCgkJCQkJInhjX21hcmtldF9zYXZlX21vZGlmeSIsCgkJCQkJInhjX21hcmtldF92aWV3IiwKCQkJCQkieGNfbWVkaWFfZGVsIiwKCQkJCQkieGNfbWVkaWFfbGlzdCIsCgkJCQkJInhjX21lZGlhX3ByZXZpZXciLAoJCQkJCSJ4Y19tZWRpYV9zYXZlIiwKCQkJCQkieGNfdGVhY2hlcl9saXN0IiwKCQkJCQkieGNfdGVhY2hlcl9tb2RpZnkiLAoJCQkJCSJ4Y190ZWFjaGVyX3NhdmUiLAoJCQkJCSJ4Y193b3JrcmVjb3JkX2NvcnJlY3Rpb24iLAoJCQkJCSJ4Y193b3JrcmVjb3JkX2xpc3QiLAoJCQkJCSJ4Y190ZWFjaHBsYW53b3JrX2RlbCIsCgkJCQkJInhjX3RlYWNocGxhbndvcmtfbGlzdCIsCgkJCQkJInhjX3RlYWNocGxhbndvcmtfc2F2ZV9tb2RpZnkiLAoJCQkJCSJ4Y190ZWFjaHBsYW5fZGVsIiwKCQkJCQkieGNfdGVhY2hwbGFuX3NhdmVfbW9kaWZ5IiwKCQkJCQkieGNfdGVhY2hwbGFuX3ZpZXciCiAgICAgICAgICAgICAgICBdLAogICAgICAgICAgICAgICAgInJfMDAyIjogWwogICAgICAgICAgICAgICAgICAgICJ4Y19jb3Vyc2VfYWRtaW5fbGlzdCIsCgkJCQkJInhjX2NvdXJzZV9iYXNlX2NvbW1pdCIsCgkJCQkJInhjX3N5c3RlbV9jYXRlZ29yeSIsCgkJCQkJInhjX21fbWVkaWFfbGlzdCIsCgkJCQkJInhjX21lZGlhX2F1ZGl0IgogICAgICAgICAgICAgICAgXQogICAgICAgICAgICB9CiAgICAgICAgfQogICAgfSwKICAgICJ1c2VyX25hbWUiOiAieGMtdXNlci1maXJzdCIsCiAgICAic2NvcGUiOiBbCiAgICAgICAgInJlYWQiCiAgICBdLAogICAgIm1vYmlsZSI6ICIxNTAxMjM0NTY3OCIsCiAgICAiZXhwIjogMTYwNjUyNTEyMiwKICAgICJjbGllbnRfYXV0aG9yaXRpZXMiOiBbCiAgICAgICAgIlJPTEVfVVNFUiIKICAgIF0sCiAgICAianRpIjogIjFlYjdlOTg3LWQ3YzItNDBmNS1iMGQ2LWNkNjEzOWNiMThlMCIsCiAgICAiY2xpZW50X2lkIjogInhjLWNvbS1wbGF0Zm9ybSIsCiAgICAiY29tcGFueUlkIjogMTIzMjE0MTQyNQp9

Day05-第二章-内容管理-课程图片&课程审核 - 图43

3.4 课程信息查询实现-运营(已实现)


学成在线运营平台需要查看所有机构下的课程信息,对其 “已提交” 状态的课程进行审核。
操作示意图

Day05-第二章-内容管理-课程图片&课程审核 - 图44

3.4.1 课程信息查询-运营 接口定义


1.接口参数列表
根据前后端传入参数列表来定义接口
Http接口地址

Day05-第二章-内容管理-课程图片&课程审核 - 图45

接口传入传出列表

Day05-第二章-内容管理-课程图片&课程审核 - 图46

2. 接口编写
在 xc-api 工程中的创建 CourseAuditApi 中添加接口方法。
●课程提交功能接口

  1. @Api(value = "课程基本信息管理-运营 Api",tags = "课程基本信息管理-运营",
  2. description = "课程基本信息业务管理-运营")
  3. public interface CourseAuditApi {
  4. @ApiOperation("课程基础信息条件分页查询-运营")
  5. @ApiImplicitParams({
  6. @ApiImplicitParam(value = "model",dataType = "QueryCourseModel",paramType = "body")
  7. })
  8. PageVO queryCourseList(PageRequestParams params, QueryCourseModel model);
  9. }

3.4.2 课程信息查询-运营 接口开发


1.DAO 层
Mybatis Plus 已经简化了单表操作,它提供的 Api 就可以完成添加数据操作,所有不需要进行编写。
2.service编写
service的代码实现可以借助于之前的课程基本信息的查询操作,只需要将其修改对公司 Id 进行判断即可。
●实现类

  1. @Service
  2. public class CourseBaseServiceImpl extends ServiceImpl<CourseBaseMapper, CourseBase> implements CourseBaseService {
  3. //其他代码省略
  4. public PageVO<CourseBaseDTO> queryCourseList(PageRequestParams params, QueryCourseModel model, Long companyId) {
  5. //其他代码省略
  6. //3.3 固定的查询条件
  7. if (!(ObjectUtils.nullSafeEquals(companyId,
  8. CourseAuditController.OPERATION_FLAG))) {
  9. queryWrapper.eq(CourseBase::getCompanyId, companyId);
  10. }
  11. }
  12. }

3.Controller 编写
●创建 CourseAuditController中新增方法,如下:

  1. @RestController
  2. public class CourseAuditController implements CourseAuditApi {
  3. // 运营平台标识--可以查询所哟教学机构的数据
  4. public static final Long OPERATION_FLAG = -99887799L;
  5. @Autowired
  6. private CourseBaseService courseBaseService;
  7. @PostMapping("m/course/list")
  8. public PageVO<CourseBaseDTO> queryCourseList(PageRequestParams params,@RequestBody QueryCourseBaseModel model) {
  9. PageVO pageVO = courseBaseService.queryCourseBaseList(params, model, OPERATION_FLAG);
  10. return pageVO;
  11. }
  12. }

3.4.3 信息接口测试


1. 使用 postman 在请求信息中添加请求参数
POST http://127.0.0.1:63010/content/m/course/list
测试环境需要启动的微服务有:
1.注册中心 xc-discover-service (端口:63000)
2.服务网关 xc-gateway-service (端口:63010)
3.内容管理 xc-content-service (端口:63040)
课程基本信息分页条件查询参数:
1.分页数据
通过QueryString(问号传参)方式来传递参数
2.查询条件
通过RequestBody(请求体中json格式数据)方式来传递参数
3.请求的令牌
通过请求头来传递令牌
●请求头(访问令牌)

  1. 请求头的 key 值: authorization
  2. 请求头的 value 值:
  3. Bearer ewogICAgImF1ZCI6IFsKICAgICAgICAieHVlY2hlbmctcmVzb3VyY2UiCiAgICBdLAogICAgInBheWxvYWQiOiB7CiAgICAgICAgIjExNzcxNDQyMDk0NjMxMjgxMjUiOiB7CiAgICAgICAgICAgICJyZXNvdXJjZXMiOiBbCiAgICAgICAgICAgIF0sCiAgICAgICAgICAgICJ1c2VyX2F1dGhvcml0aWVzIjogewogICAgICAgICAgICAgICAgInJfMDAxIjogWwogICAgICAgICAgICAgICAgICAgICJ4Y19jb21wYW55X21vZGlmeSIsCgkJCQkJInhjX2NvbXBhbnlfdmlldyIsCgkJCQkJInhjX2NvdXJzZV9iYXNlX2RlbCIsCgkJCQkJInhjX2NvdXJzZV9iYXNlX2VkaXQiLAoJCQkJCSJ4Y19jb3Vyc2VfYmFzZV9saXN0IiwKCQkJCQkieGNfY291cnNlX2Jhc2Vfc2F2ZSIsCgkJCQkJInhjX2NvdXJzZV9iYXNlX3ZpZXciLAoJCQkJCSJ4Y19jb3Vyc2VfcHVibGlzaCIsCgkJCQkJInhjX21hcmtldF9zYXZlX21vZGlmeSIsCgkJCQkJInhjX21hcmtldF92aWV3IiwKCQkJCQkieGNfbWVkaWFfZGVsIiwKCQkJCQkieGNfbWVkaWFfbGlzdCIsCgkJCQkJInhjX21lZGlhX3ByZXZpZXciLAoJCQkJCSJ4Y19tZWRpYV9zYXZlIiwKCQkJCQkieGNfdGVhY2hlcl9saXN0IiwKCQkJCQkieGNfdGVhY2hlcl9tb2RpZnkiLAoJCQkJCSJ4Y190ZWFjaGVyX3NhdmUiLAoJCQkJCSJ4Y193b3JrcmVjb3JkX2NvcnJlY3Rpb24iLAoJCQkJCSJ4Y193b3JrcmVjb3JkX2xpc3QiLAoJCQkJCSJ4Y190ZWFjaHBsYW53b3JrX2RlbCIsCgkJCQkJInhjX3RlYWNocGxhbndvcmtfbGlzdCIsCgkJCQkJInhjX3RlYWNocGxhbndvcmtfc2F2ZV9tb2RpZnkiLAoJCQkJCSJ4Y190ZWFjaHBsYW5fZGVsIiwKCQkJCQkieGNfdGVhY2hwbGFuX3NhdmVfbW9kaWZ5IiwKCQkJCQkieGNfdGVhY2hwbGFuX3ZpZXciCiAgICAgICAgICAgICAgICBdLAogICAgICAgICAgICAgICAgInJfMDAyIjogWwogICAgICAgICAgICAgICAgICAgICJ4Y19jb3Vyc2VfYWRtaW5fbGlzdCIsCgkJCQkJInhjX2NvdXJzZV9iYXNlX2NvbW1pdCIsCgkJCQkJInhjX3N5c3RlbV9jYXRlZ29yeSIsCgkJCQkJInhjX21fbWVkaWFfbGlzdCIsCgkJCQkJInhjX21lZGlhX2F1ZGl0IgogICAgICAgICAgICAgICAgXQogICAgICAgICAgICB9CiAgICAgICAgfQogICAgfSwKICAgICJ1c2VyX25hbWUiOiAieGMtdXNlci1maXJzdCIsCiAgICAic2NvcGUiOiBbCiAgICAgICAgInJlYWQiCiAgICBdLAogICAgIm1vYmlsZSI6ICIxNTAxMjM0NTY3OCIsCiAgICAiZXhwIjogMTYwNjUyNTEyMiwKICAgICJjbGllbnRfYXV0aG9yaXRpZXMiOiBbCiAgICAgICAgIlJPTEVfVVNFUiIKICAgIF0sCiAgICAianRpIjogIjFlYjdlOTg3LWQ3YzItNDBmNS1iMGQ2LWNkNjEzOWNiMThlMCIsCiAgICAiY2xpZW50X2lkIjogInhjLWNvbS1wbGF0Zm9ybSIsCiAgICAiY29tcGFueUlkIjogMTIzMjE0MTQyNQp9


QueryString 信息(分页参数)

  1. ......?pageNo=1&pageSize=5

●请求体信息(查询条件参数)

  1. {
  2. "auditStatus":"202004",
  3. "courseName":"springboot"
  4. }

Day05-第二章-内容管理-课程图片&课程审核 - 图47

3.5 课程信息审核-运营


学成在线运营平台工作人员对课程审核要给出结果:“审核通过”,“审核未通过”。“审核通过” 后,教育机构可以对课程信息继续进行业务操作。“审核未通过” ,需要教学机构进行修改并在此提交。
操作示意图

Day05-第二章-内容管理-课程图片&课程审核 - 图48

Day05-第二章-内容管理-课程图片&课程审核 - 图49

审核要求:
1.对于被审核的课程
被审核是课程状态必须为 ‘已提交’ 状态,如果课程处于 ‘审核通过’、’审核未通过’、’课程发布’、’未提交’ 是不能进行课程审核的。
2.机构审核课程状态
机构对课程状态必须为 ‘已提交’ 数据进行 ‘审核通过’ 或 ‘审核未通过’ 两种状态的赋值。

3.5.1 课程信息审核接口定义-运营


学成在线运营平台可以对所有机构下的课程状态为’’已提交’’的数据进行审核,
1.接口参数列表
根据前后端传入参数列表来定义接口
Http接口地址

Day05-第二章-内容管理-课程图片&课程审核 - 图50

接口传入传出列表

Day05-第二章-内容管理-课程图片&课程审核 - 图51

2.接口参数封装
对于接口,我们需要通过 VO 类来进行定义。
●课程审核封装 VO 类

  1. package com.xuecheng.api.content.model.vo;
  2. import io.swagger.annotations.ApiModel;
  3. import io.swagger.annotations.ApiModelProperty;
  4. import lombok.Data;
  5. /**
  6. * <p>
  7. * 课程审核时的VO对象
  8. * </p>
  9. */
  10. @Data
  11. @ApiModel(value="CourseBaseAuditVO", description="课程审核时的VO对象")
  12. public class CourseAuditVO {
  13. @ApiModelProperty(value = "课程Id")
  14. private Long courseBaseId;
  15. @ApiModelProperty(value = "审核状态:参照数据字典 code 为 202")
  16. private String auditStatus;
  17. @ApiModelProperty(value = "审核意见")
  18. private String auditMind;
  19. }

3.接口定义

  1. package com.xuecheng.api.content;
  2. import com.xuecheng.api.content.model.vo.CourseBaseAuditVO;
  3. import io.swagger.annotations.Api;
  4. import io.swagger.annotations.ApiImplicitParam;
  5. import io.swagger.annotations.ApiImplicitParams;
  6. import io.swagger.annotations.ApiOperation;
  7. @Api(value = "课程基本信息管理-运营 Api",tags = "课程基本信息管理-运营",
  8. description = "课程基本信息业务管理-运营")
  9. public interface CourseAuditApi {
  10. //其他代码省略
  11. @ApiOperation("课程审核(提交的课程才可审核)")
  12. @ApiImplicitParam(name = "auditVO",
  13. value = "课程信息VO",
  14. required = true, dataType = "CourseAuditVO", paramType = "body")
  15. void approveCourse(CourseAuditVO auditVO);
  16. }

3.5.2 课程信息审核接口开发 -运营


1.DAO 层
Mybatis Plus 已经简化了单表操作,它提供的 Api 就可以完成添加数据操作,所有不需要进行编写。
2.service编写
service的代码实现可以借助于之前的课程基本信息的查询操作,只需要将其修改对公司 Id 进行判断即可。
●接口

  1. /**
  2. * <p>
  3. * 课程基本信息 服务类
  4. * </p>
  5. *
  6. * @author itcast
  7. * @since 2019-08-27
  8. */
  9. public interface CourseBaseService extends IService<CourseBase> {
  10. /**
  11. * 课程审核
  12. * @param courseBaseDTO
  13. * @return
  14. */
  15. void approve(CourseBaseDTO courseBaseDTO);
  16. }

●实现类

  1. @Service
  2. public class CourseBaseServiceImpl extends ServiceImpl<CourseBaseMapper, CourseBase> implements CourseBaseService {
  3. //其他代码省略
  4. /*
  5. * 业务分析:
  6. * 1.判断关键数据
  7. * auditMind auditStatus courseid
  8. *
  9. * 2.判断业务数据
  10. * 课程基础信息
  11. * 判断是否存在
  12. * 判断是否删除
  13. * 判断审核状态
  14. * 必须为:已提交
  15. * 审核状态
  16. * 运营平台只能给课程审核状态赋值:审核通过、审核未通过
  17. * 3.修改课程审核信息
  18. * auditMind auditStatus auditNum(每次+1)
  19. * */
  20. @Transactional
  21. public void approveCourse(CourseBaseDTO dto) {
  22. // 1.判断关键数据
  23. // auditMind auditStatus courseid
  24. if (StringUtil.isBlank(dto.getAuditMind())||
  25. StringUtil.isBlank(dto.getAuditStatus())||
  26. ObjectUtils.isEmpty(dto.getCourseBaseId())
  27. ) {
  28. ExceptionCast.cast(CommonErrorCode.E_100101);
  29. }
  30. // 2.判断业务数据
  31. // 课程基础信息
  32. // 判断是否存在
  33. // 判断是否删除
  34. // 判断审核状态
  35. // 必须为:已提交
  36. LambdaQueryWrapper<CourseBase> queryWrapper = new LambdaQueryWrapper<>();
  37. queryWrapper.eq(CourseBase::getId, dto.getCourseBaseId());
  38. queryWrapper.eq(CourseBase::getStatus, CommonEnum.USING_FLAG.getCodeInt());
  39. queryWrapper.eq(CourseBase::getAuditStatus, CourseAuditEnum.AUDIT_COMMIT_STATUS.getCode());
  40. int count = this.count(queryWrapper);
  41. if (count < 1) {
  42. ExceptionCast.cast(ContentErrorCode.E_120023);
  43. }
  44. // 审核状态
  45. // 运营平台只能给课程审核状态赋值:审核通过、审核未通过
  46. String auditStatus = dto.getAuditStatus();
  47. if (CourseAuditEnum.AUDIT_PUBLISHED_STATUS.getCode().equals(auditStatus)||
  48. CourseAuditEnum.AUDIT_COMMIT_STATUS.getCode().equals(auditStatus)||
  49. CourseAuditEnum.AUDIT_UNPAST_STATUS.getCode().equals(auditStatus)
  50. ) {
  51. ExceptionCast.cast(ContentErrorCode.E_120016);
  52. }
  53. // 3.修改课程审核信息
  54. // auditMind auditStatus auditNum(每次+1)
  55. LambdaUpdateWrapper<CourseBase> updateWrapper = new LambdaUpdateWrapper<>();
  56. updateWrapper.set(CourseBase::getAuditStatus, dto.getAuditStatus());
  57. updateWrapper.set(CourseBase::getAuditMind, dto.getAuditMind());
  58. // 使用redis来实现
  59. // updateWrapper.set(CourseBase::getAuditNums, dto.getAuditMind());
  60. updateWrapper.set(CourseBase::getChangeDate, LocalDateTime.now());
  61. updateWrapper.eq(CourseBase::getId, dto.getCourseBaseId());
  62. boolean result = this.update(updateWrapper);
  63. if (!result) {
  64. ExceptionCast.cast(ContentErrorCode.E_120017);
  65. }
  66. }
  67. }

3.controller编写

  1. @RestController
  2. public class CourseAuditController implements CourseAuditApi {
  3. @Autowired
  4. private CourseBaseService courseBaseService;
  5. @PostMapping("m/courseReview/approve")
  6. public void approveCourse(@RequestBody CourseAuditVO auditVO) {
  7. //将 vo 转为 dto 数据
  8. CourseBaseDTO dto = CourseBaseConvert.INSTANCE.vo2dto(auditVO);
  9. //调用service方法进行课程审核
  10. courseBaseService.approve(dto);
  11. }
  12. }

3.5.3 课程信息审核接口测试 -运营


测试环境需要启动的微服务有:
1.注册中心 xc-discover-service (端口:63000)
2.服务网关 xc-gateway-service (端口:63010)
3.内容管理 xc-content-service (端口:63040)
1. 使用 postman 在请求信息中添加请求参数
POST http://127.0.0.1:63010/content/m/courseReview/approve
●requestBody 的请求数据

  1. {
  2. "auditStatus" : "202004",
  3. "courseBaseId" : "28",
  4. "auditMind": "课程腻害~"
  5. }


Day05-第二章-内容管理-课程图片&课程审核 - 图52