今日重点
课程绑定媒资信息:关联课程计划之前 先添加视频 运营平台进行审核,审核通过 才可以进行媒资绑定!主体业务操作在内容管理中实现!fengin 调用:接口路径规范: 内部微服务调用地址: /项目跟路径/l/ 。。。接口类型规范: 响应数据格式JSON 返回数据必备三个数据 : 操作代码 提示信息 数据内容成功 代码0 信息success 远程调用数据失败 错误代码 具体错误信息 null1.绑定课程计划与媒资信息要求:1)前端提交 媒资ID与课程计划ID至内容管理服务来请求绑定2) 内容管理服务通过媒资ID向媒资管理服务查询要绑定的媒资信息3) 内容管理服务通过课程计划ID查询要绑定的课程计划信息4) 内容管理服务综合媒资信息和课程计划信息在数据库记录绑定关系,最后返回绑定结果给前端2.绑定课程计划与媒资信息业务要求:1)三级课程计划(小章节)才可以添加视频的关联。2)三级课程计划(小章节)与媒资进行绑定时进行判断,要根据关联的 Teachplan 的 CourseId 和 TeachplanId 来查询 TeachplanMedia 数据。2.1) 查询出如果存在,将做 TeachplanMedia 更新操作2.2) 查询出如果不存在,将做 TeachplanMedia 新增操作
1. 课程计划绑定媒资
1.1 需求分析
此模块主要针对教育机构用户将课程计划绑定媒资信息,最终形成视频课程及课程资料的业务过程,主要功能包括
●关键字查找机构内已审核的媒资信息
●内容管理服务远程调用获得媒资信息
●选择媒资信息并绑定至课程计划
○将关联数据保存到内容管理服务(TeachplanMedia)
1.1.1 业务流程描述
课程计划绑定媒资流程如下:

课程所关联的课程计划信息,其中包括客户才能计划关联媒资信息管理,课程计划为课程内容的大纲,主要方便学员学习和视频播放列表的展示。<br />**1.教育机构用户进入课程管理页面并编辑某一个课程,在"课程大纲"标签页的某一小节后可点击”添加视频“。**<br />课程计划绑定媒资信息

2.弹出添加视频对话框,可通过视频关键字搜索已审核通过的视频媒资。
填写需要关联审核通过的视频

3.选择视频媒资,点击提交按钮,完成课程计划绑定媒资流程。
关联视频

上图解释:
注释①:填写审核通过的视频关键字
注释②:选择关联出的视频名称
注释③:选择好后,点击提交完成视频关联
关联后的课程计划

4.学习中心对课程计划查询
通过内容管理对课程发布后,学员就可以通过学习中心对发布后的课程进行学习,课程学习主要是以课程章节来学习课程。而课程章节就为课程下的课程计划信息。在课程学习页面中,会将课程计划以课程章节的形式进行展示。
学习中心的课程计划

1.1.2 系统交互流程
课程计划绑定媒资交互流程如下:

以上功能中,设计三个主要工程:前端工程、内容管理微服务、媒资管理微服务。
步骤描述,分为以下几个部分,每个部分代表用户一次操作触发:
1.查询机构内已审核的媒资信息
1)前端组装查询参数并发送查询请求给 媒资管理服务
2) 媒资管理服务获取当前登录用户所在教学机构ID,将此教学机构及审核状态(已审核) 媒资信息数据返回给前端。
2.绑定课程计划与媒资信息
1)前端提交 媒资ID与课程计划ID至内容管理服务来请求绑定
2) 内容管理服务通过媒资ID向媒资管理服务查询要绑定的媒资信息
3) 内容管理服务通过课程计划ID查询要绑定的课程计划信息
4) 内容管理服务综合媒资信息和课程计划信息在数据库记录绑定关系,最后返回绑定结果给前端
1.1.3 业务所需开发功能
此模块主要针对教育机构用户 为内容管理的课程计划添加关联媒资信息的操作,主要功能包括
●查询媒资管理中的 已审核通过 的媒资信息。
●根据媒资 id 查询媒资管理中的媒资文件信息
●在内容管理中关联内容管理和媒资信息
对于功能,下面我们需要分别来进行实现。
1.2 查询媒资信息功能实现
根据业务流程图,此功能需要满足下面的要求:
1.在媒资管理中实现
2.查询媒资信息必须为审核通过
3.查询的媒资信息要属于一个机构下的数据
1.查询媒资信息功能调整
此功能我们之前已经在媒资管理微服务中实现,现在需求对增加查询条件:媒资信息已审核
2.添加查询条件数据
在查询媒资信息接口的条件封装类 QueryMediaModel 中,添加审核状态属性:
●添加审核状态属性
@Data@ApiModel("媒资查询封装类")public class QueryMediaModel {//其他内容省略@ApiModelProperty("审核状态")private String auditStatus;}
在业务类 MediaServiceImpl 实现类中的 queryMedias 方法中添加对审核状态的判断:
●添加审核状态代码
/*** 媒资信息 服务实现类*/@Servicepublic class MediaServiceImpl extends ServiceImpl<MediaMapper, Media> implements MediaService {//其他代码省略public PageVO<MediaDTO> queryMediaList(PageRequestParams params, QueryMediaModel queryMediaModel, Long companyId) {//其他代码省略//3.构建查询条件//其他代码省略//添加对媒资审核状态的条件queryWrapper.eq(StringUtils.isNotBlank(queryMediaModel.getAuditStatus()),Media::getAuditStatus, queryMediaModel.getAuditStatus());queryWrapper.eq(!(ObjectUtils.isEmpty(companyId)),Media::getCompanyId, companyId);//其他代码省略}}
3.业务测试
测试环境需要启动的微服务有:
1.服务网关 xc-gateway-service (端口:63010)
2.媒资管理 xc-media-service (端口:63050)
1. 使用 postman 在请求信息中添加请求参数
POST http://127.0.0.1:63010/media/media/list
●QueryString 信息(分页参数)
......?pageNo=1&pageSize=5
●请求体信息(查询条件参数)
{"filename":"""type":"","auditStatus":"202003"}
●请求头(访问令牌)
请求头的 key 值: authorization请求头的 value 值:Bearer ewogICAgImF1ZCI6IFsKICAgICAgICAieHVlY2hlbmctcmVzb3VyY2UiCiAgICBdLAogICAgInBheWxvYWQiOiB7CiAgICAgICAgIjExNzcxNDQyMDk0NjMxMjgxMjUiOiB7CiAgICAgICAgICAgICJyZXNvdXJjZXMiOiBbCiAgICAgICAgICAgIF0sCiAgICAgICAgICAgICJ1c2VyX2F1dGhvcml0aWVzIjogewogICAgICAgICAgICAgICAgInJfMDAxIjogWwogICAgICAgICAgICAgICAgICAgICJ4Y19jb21wYW55X21vZGlmeSIsCgkJCQkJInhjX2NvbXBhbnlfdmlldyIsCgkJCQkJInhjX2NvdXJzZV9iYXNlX2RlbCIsCgkJCQkJInhjX2NvdXJzZV9iYXNlX2VkaXQiLAoJCQkJCSJ4Y19jb3Vyc2VfYmFzZV9saXN0IiwKCQkJCQkieGNfY291cnNlX2Jhc2Vfc2F2ZSIsCgkJCQkJInhjX2NvdXJzZV9iYXNlX3ZpZXciLAoJCQkJCSJ4Y19jb3Vyc2VfcHVibGlzaCIsCgkJCQkJInhjX21hcmtldF9zYXZlX21vZGlmeSIsCgkJCQkJInhjX21hcmtldF92aWV3IiwKCQkJCQkieGNfbWVkaWFfZGVsIiwKCQkJCQkieGNfbWVkaWFfbGlzdCIsCgkJCQkJInhjX21lZGlhX3ByZXZpZXciLAoJCQkJCSJ4Y19tZWRpYV9zYXZlIiwKCQkJCQkieGNfdGVhY2hlcl9saXN0IiwKCQkJCQkieGNfdGVhY2hlcl9tb2RpZnkiLAoJCQkJCSJ4Y190ZWFjaGVyX3NhdmUiLAoJCQkJCSJ4Y193b3JrcmVjb3JkX2NvcnJlY3Rpb24iLAoJCQkJCSJ4Y193b3JrcmVjb3JkX2xpc3QiLAoJCQkJCSJ4Y190ZWFjaHBsYW53b3JrX2RlbCIsCgkJCQkJInhjX3RlYWNocGxhbndvcmtfbGlzdCIsCgkJCQkJInhjX3RlYWNocGxhbndvcmtfc2F2ZV9tb2RpZnkiLAoJCQkJCSJ4Y190ZWFjaHBsYW5fZGVsIiwKCQkJCQkieGNfdGVhY2hwbGFuX3NhdmVfbW9kaWZ5IiwKCQkJCQkieGNfdGVhY2hwbGFuX3ZpZXciCiAgICAgICAgICAgICAgICBdLAogICAgICAgICAgICAgICAgInJfMDAyIjogWwogICAgICAgICAgICAgICAgICAgICJ4Y19jb3Vyc2VfYWRtaW5fbGlzdCIsCgkJCQkJInhjX2NvdXJzZV9iYXNlX2NvbW1pdCIsCgkJCQkJInhjX3N5c3RlbV9jYXRlZ29yeSIsCgkJCQkJInhjX21fbWVkaWFfbGlzdCIsCgkJCQkJInhjX21lZGlhX2F1ZGl0IgogICAgICAgICAgICAgICAgXQogICAgICAgICAgICB9CiAgICAgICAgfQogICAgfSwKICAgICJ1c2VyX25hbWUiOiAieGMtdXNlci1maXJzdCIsCiAgICAic2NvcGUiOiBbCiAgICAgICAgInJlYWQiCiAgICBdLAogICAgIm1vYmlsZSI6ICIxNTAxMjM0NTY3OCIsCiAgICAiZXhwIjogMTYwNjUyNTEyMiwKICAgICJjbGllbnRfYXV0aG9yaXRpZXMiOiBbCiAgICAgICAgIlJPTEVfVVNFUiIKICAgIF0sCiAgICAianRpIjogIjFlYjdlOTg3LWQ3YzItNDBmNS1iMGQ2LWNkNjEzOWNiMThlMCIsCiAgICAiY2xpZW50X2lkIjogInhjLWNvbS1wbGF0Zm9ybSIsCiAgICAiY29tcGFueUlkIjogMTIzMjE0MTQyNQp9
响应内容如下:

1.3 根据id查询媒资信息功能实现
1.3.1 查询媒资信息需求分析
根据业务流程图,此功能需要满足下面的要求:
1.在媒资管理中实现
2.根据 Id 来进行查询
根据项目开发规范文档中:接口开发规范—微服务远程调用接口规范 ,对学成在线微服务间远程调用定义了一些内容。
微服务之间调用接口规范示意图

●HTTP 请求方式规范 同 ‘6.4.2.1 前端调用接口规范’ 中的 HTTP 请求方式规范,对此不在阐述
●HTTP 请求地址URI
○URI 中尽量避免使用动词,单词之间不要以中划线”-“分割
○URI 地址前缀:/服务名称/l/xxx
●HTTP响应数据无论是进行 CRUD 还是 异常信息返回,其中必须包含:操作代码、提示信息、数据内容。
○响应数据格式为:Json
■如果是获得正常信息
●操作代码固定为:0
●提示信息固定为:success
●数据内容:远程调用获得数据
■如果获得错误信息
●操作代码:错误代码(不同业务错误代码不同)
●提示信息:错误信息(要看具体的错误信息)
●数据内容:null
针对学成在线微服务间远程相应内容,项目中使用自定义类 RestResponse (xc-common工程中) ,如下:
●RestResponse 响应类
package com.xuecheng.common.domain.response;import com.xuecheng.common.domain.code.CommonErrorCode;import com.xuecheng.common.domain.code.ErrorCode;/*** 响应通用参数包装*/public class RestResponse<T> {/*** 响应错误编码,0为正常*/private int code;/*** 响应错误信息*/private String msg;/*** 响应内容*/private T result;/*** 错误信息的封装* @param msg* @param <T>* @return*/public static <T> RestResponse<T> validfail(String msg) {RestResponse<T> response = new RestResponse<T>();response.setCode(-2);response.setMsg(msg);return response;}/*** 对ErrorCode信息的封装* @param errorCode {@link ErrorCode} 业务错误信息* @return RestResponse Rest服务封装相应数据*/public static <T> RestResponse<T> validfail(ErrorCode errorCode) {RestResponse<T> response = newRestResponse<T>(errorCode.getCode(),errorCode.getDesc());return response;}/*** 添加正常响应数据(包含响应内容)* @return RestResponse Rest服务封装相应数据*/public static <T> RestResponse<T> success(T result) {RestResponse<T> response = new RestResponse<T>();response.setResult(result);return response;}/*** 添加正常响应数据(不包含响应内容)* @return RestResponse Rest服务封装相应数据*/public static <T> RestResponse<T> success() {return new RestResponse<T>();}public RestResponse() {this(0, "success");}public RestResponse(int code, String msg) {this.code = code;this.msg = msg;}public int getCode() {return code;}public void setCode(int code) {this.code = code;}public String getMsg() {return msg;}public void setMsg(String msg) {this.msg = msg;}public T getResult() {return result;}public void setResult(T result) {this.result = result;}public Boolean isSuccessful() {return this.code == CommonErrorCode.SUCCESS.getCode();}@Overridepublic String toString() {return "RestResponse [code=" + code + ", msg=" + msg + ", result="+ result + "]";}}
1.3.2 查询媒资信息接口定义
1.接口参数列表
根据前后端传入参数列表来定义接口
Http接口地址

接口传入传出列表

2. 接口编写
在 xc-api 工程的 com.xuecheng.api.media 包下创建接口类接口定义如下:
●根据Id查询媒资
@Api(value = "媒资管理", tags = "媒资管理API",description = "对媒资信息进行管理")public interface MediaApi {//其他代码省略@ApiOperation("根据Id查询媒资信息")@ApiImplicitParam(name = "mediaId",value = "媒资ID", required = true,dataType = "long", paramType = "path", example = "1")RestResponse getMediaById(Long mediaId);}
1.3.2 查询媒资信息接口开发
1.DAO编写
Mybatis Plus 已经简化了单表操作,它提供的 Api 就可以完成添加数据操作,所有不需要进行编写。
2. service编写
●接口
public interface MediaService extends IService<Media> {//其他代码省略/*** 根据id查询媒资信息-远端服务调用* @param mediaId* @return*/RestResponse<MediaDTO> getById4Service(Long mediaId);}
●实现类
/*** 媒资信息 服务实现类*/@Servicepublic class MediaServiceImpl extends ServiceImpl<MediaMapper, Media> implements MediaService {/** 业务分析:* PS:由于是学成内部微服务间的调用,此功能只需要提供基础数据即可,不需要加上业务逻辑判断* 如果为了数据的完整性,可以对传入和传出的参数进行判断** 1.判断mediaid是否为空* 2.查询数据* 3.判断数据是否存在* 4.如果存在,使用RestResponse来封装返回* */public RestResponse<MediaDTO> getById4Service(Long mediaId) {// 1.判断关键数据if (ObjectUtils.isEmpty(mediaId)) {return RestResponse.validfail(CommonErrorCode.E_100101);}// 2.判断业务数据Media media = this.getById(mediaId);if (ObjectUtils.isEmpty(media)) {return RestResponse.validfail(MediaErrorCode.E_140005);} else {MediaDTO mediaDTO = MediaConvert.INSTANCE.entity2dto(media);return RestResponse.success(mediaDTO);}}}
3. controller编写
@RestControllerpublic class MediaController implements MediaApi {@Autowiredprivate MediaService mediaService;//其他代码省略@GetMapping("l/media/{mediaId}")public RestResponse<MediaDTO> getById4Service(@PathVariable Long mediaId) {RestResponse<MediaDTO> response = mediaService.getById4Service(mediaId);return response;}}
1.3.4 信息接口测试
测试环境需要启动的微服务有:
1.注册中心 xc-discover-service (端口:63000)
2.服务网关 xc-gateway-service (端口:63010)
3.媒资管理 xc-media-service (端口:63050)
1. 使用 postman 在请求信息中添加请求参数
GET http://localhost:63010/media/l/media/25
响应内容如下:

1.4 课程计划绑定媒资功能实现
本功能是在内容管理微服务开发,来完成课程计划(课程大纲)与媒资信息的关联操作。
1.4.1 数据模型(表结构)
‘ 课程计划与媒资管理的信息保存在内容管理数据库中的 teachplan_media 表当中,在完成数据绑定时,要将课程计划于媒资的关联关系数据保存到此表中。
1.媒资表说明
媒资表结构

在上图中的表结构中,主要字段为:
1.自身信息描述:id,create_date。
2.数据关联描述:teachplan_id,course_id, media_id ,media_fileName
之前在做 Teachplan 的删除操作时,我们已经讲 TeachplanMedia 的相关代码生成出来,无需再次生成。
1.4.2 课程计划绑定媒资需求分析
1.绑定课程计划与媒资信息要求:
1)前端提交 媒资ID与课程计划ID至内 容管理服务来请求绑定
2) 内容管理服务通过媒资ID向媒资管理服务查询要绑定的媒资信息
3) 内容管理服务通过课程计划ID查询要绑定的课程计划信息
4) 内容管理服务综合媒资信息和课程计划信息在数据库记录绑定关系,最后返回绑定结果给前端
2.绑定课程计划与媒资信息业务要求:
1)三级课程计划(小章节)才可以添加视频的关联。
2)三级课程计划(小章节)与媒资进行绑定时进行判断,要根据关联的 Teachplan 的 CourseId 和 TeachplanId 来查询 TeachplanMedia 数据。
2.1) 查询出如果存在,将做 TeachplanMedia 更新操作
2.2) 查询出如果不存在,将做 TeachplanMedia 新增操作
1.4.3 系统微服间远程调用实现
由于 内容管理微服务和 媒资管理微服务 都属于学成在线的也服务,在项目运行环境中,可以使用 Spring cloud Eureka 注册到 微服务注册中心中,并通过 Spring cloud Feign 发起远程调用。
微服务调用结构图

在上面的结构图中,需求我们将微服务作出以下操作:
1.将业务微服务注册到 Nacos 中。
2.在内容管理中添加 Feign、Ribbon 的依赖和相关配置信息。
3.编写 Feign 远程调用的接口。
1.4.3.1 微服间远程调用配置
1.在内容管理中引入相关依赖
在内容管理中,需要 Nacos、Feign、Ribbon 的依赖配置到 pom 文件中。但当前项目已经将其进行引入。
2.配置 spring cloud 相关内容
在Nacos中添加下面的公共配置信息,如下
feign-config.properties 的配置:
# 开启 feign 的远程调用使用熔断feign.sentinel.enabled = true# 配置请求GZIP压缩feign.compression.request.enabled = true# 配置压缩数据大小的下限feign.compression.request.min-request-size = 2048# 配置响应GZIP压缩feign.compression.response.enabled = true# 配置压缩支持的MIME TYPEfeign.compression.request.mime-types[0] = text/xmlfeign.compression.request.mime-types[1] = application/xmlfeign.compression.request.mime-types[2] = application/json
ribbon-config.properties 的配置:
#对当前实例的重试次数 default 0ribbon.MaxAutoRetries = 1#设置连接超时时间 default 2000ribbon.ConnectTimeout = 3000#对所有操作请求都进行重试 default falseribbon.OkToRetryOnAllOperations = false#设置读取超时时间 default 5000ribbon.ReadTimeout = 20000#切换实例的重试次数 default 1ribbon.MaxAutoRetriesNextServer = 1
我们只需在媒资服务中引入公共配置到应用中:
#微服务配置spring:application:name: content-servicejackson:date-format: yyyy-MM-dd HH:mm:sstime-zone: GMT+8cloud:nacos:discovery: #配置注册中心server-addr: 192.168.94.129:8848namespace: 5c0b093c-4084-46b5-bf33-899321cb7ef5group: ${group.name}config: #配置中心server-addr: 192.168.94.129:8848namespace: 5c0b093c-4084-46b5-bf33-899321cb7ef5group: ${group.name}file-extension: propertiesshared-configs:- dataId: mp-config.properitesgroup: ${group.name}- dataId: aliyun-vod.propertiesgroup: ${group.name}- dataId: feign-config.propertiesgroup: ${group.name}- dataId: ribbon-config.propertiesgroup: ${group.name}profiles: # 激活配置环境active: dev# 组名称group:name: xc-group
1.4.3.2 编写 Feign 远程代理
1.Feign代理接口
由于是内容管理远程调用媒资管理,所有要在 xc-api 创建对 Media 的 Feign 远程调用接口类,内容如下:
package com.xuecheng.feign.media;import com.xuecheng.api.media.model.dto.MediaDTO;import com.xuecheng.common.constant.XcFeignServiceNameList;import com.xuecheng.common.domain.response.RestResponse;import com.xuecheng.feign.media.sentinel.MediaApiAgentFallback;import org.springframework.cloud.openfeign.FeignClient;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.PathVariable;/*** <p></p>** @Description:*/@FeignClient(value = XcFeignServiceNameList.XC_MEDIA_SERVICE)public interface MediaApiAgent {String PREFIX_TAG = "/media/";@GetMapping(PREFIX_TAG+"l/media/{mediaId}")RestResponse<MediaDTO> getMediaById4s(@PathVariable Long mediaId);}
2.开启 Feign 注解配置
xc-api 工程下添加 Spring Cloud Feign 发起对媒资微服务的远程调用,所有要在内容管理启动类上添加开启 Feign 的注解配置,如下:
package com.xuecheng.feign.config;import org.springframework.cloud.openfeign.EnableFeignClients;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;/*** <p></p>** @Description:*/@Configuration@ComponentScan(basePackages = "com.xuecheng.feign")@EnableFeignClients(basePackages = {"com.xuecheng.feign"})public class FeignConfig {}
1.4.3.3 编写 Feign 熔断器
为了保护微服务间远程调用,不会出现链路失效(雪崩),我们需要对Feign接口编写Sentinel的熔断降级方法,内容如下:
xc-api 添加Sentinel依赖
<!--sentinel--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency>
xc-content-service 添加Sentinel配置
#微服务配置spring:application:name: content-servicecloud:sentinel:transport:dashboard: 192.168.94.129:8858 #sentinel控制台地址
xc-content-service 添加Sentinel降级方法
package com.xuecheng.feign.media.sentinel;import com.xuecheng.api.media.model.dto.MediaDTO;import com.xuecheng.common.domain.code.CommonErrorCode;import com.xuecheng.common.domain.response.RestResponse;import com.xuecheng.feign.media.MediaApiAgent;import feign.hystrix.FallbackFactory;import lombok.extern.slf4j.Slf4j;import org.springframework.stereotype.Component;/*** <p></p>** @Description:*/@Slf4j@Componentpublic class MediaApiAgentFallback implements FallbackFactory<MediaApiAgent> {@Overridepublic MediaApiAgent create(Throwable throwable) {return new MediaApiAgent() {@Overridepublic RestResponse<MediaDTO> getMediaById4s(Long mediaId) {log.error(CommonErrorCode.E_999982.getDesc()+"error msg : {}",throwable.getMessage());return RestResponse.validfail(CommonErrorCode.E_999982);}};}}
编写好后,需要在 MediaApiAgent 的 FeignClient 注解属性中 fallback 添加值,如下:
package com.xuecheng.feign.media;import com.xuecheng.api.media.model.dto.MediaDTO;import com.xuecheng.common.constant.XcFeignServiceNameList;import com.xuecheng.common.domain.response.RestResponse;import com.xuecheng.feign.media.sentinel.MediaApiAgentFallback;import org.springframework.cloud.openfeign.FeignClient;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.PathVariable;/*** <p></p>** @Description:*/@FeignClient(value = XcFeignServiceNameList.XC_MEDIA_SERVICE,fallbackFactory = MediaApiAgentFallback.class)public interface MediaApiAgent {String PREFIX_TAG = "/media/";@GetMapping(PREFIX_TAG+"l/media/{mediaId}")RestResponse<MediaDTO> getMediaById4s(@PathVariable Long mediaId);}
1.4.3.4 开启自动扫包
在xc-api工程下的 resource目录下创建 META-INF/spring.factories 文件,文件内容如下
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.xuecheng.feign.config.FeignConfig
1.4.4 课程计划绑定媒资接口定义
1.接口参数列表
根据前后端传入参数列表来定义接口
Http接口地址

接口传入传出列表

2. 传入传出的参数封装类
根据前后端传入参数封装 VO 类
●VO 封装类
package com.xuecheng.api.media.model.vo;import io.swagger.annotations.ApiModel;import io.swagger.annotations.ApiModelProperty;import lombok.Data;/*** <p>* 教学计划-媒资绑定提交数据* </p>** @Description:*/@Data@ApiModel(value="BindTeachplanMediaVO", description="教学计划-媒资绑定提交数据")public class BindTeachplanMediaVO {@ApiModelProperty(value = "媒资信息标识", required = true)private Long mediaId;@ApiModelProperty(value = "课程计划标识", required = true)private Long teachplanId;}
根据前后端传出参数封装 DTO 类
●DTO 封装类 (代码生成器生成)
package com.xuecheng.api.content.model;import io.swagger.annotations.ApiModel;import io.swagger.annotations.ApiModelProperty;import lombok.Data;import java.io.Serializable;@Data@ApiModel(value="TeachplanMediaDTO", description="")public class TeachplanMediaDTO implements Serializable {@ApiModelProperty(value = "主键")private Long teachplanMediaId;@ApiModelProperty(value = "媒资信息标识")private Long mediaId;@ApiModelProperty(value = "课程计划标识")private Long teachplanId;@ApiModelProperty(value = "课程标识")private Long courseId;@ApiModelProperty(value = "课程发布标识")private Long coursePubId;@ApiModelProperty(value = "媒资文件原始名称")private String mediaFilename;}
3. 接口编写
在 xc-api 工程的 com.xuecheng.api.content 包下创建接口类接口定义如下:
●根据Id查询媒资
/*** <p>* 课程计划服务API* </p>*/@Api(value = "课程计划信息管理",tags = "内容-课程计划信息管理Api接口",description = "课程计划信息管理Api接口")public interface TeachplanApi {//其他代码省略@ApiOperation("课程计划绑定媒资信息")TeachplanMediaDTO associateMedia(BindTeachplanMediaVO vo);}
1.4.5 课程计划绑定媒资接口开发
1.DAO编写
Mybatis Plus 已经简化了单表操作,它提供的 Api 就可以完成添加数据操作,所有不需要进行编写。
2. service编写
对于service层,除了进行业务层逻辑判断和操作外,还需要将 PO 数据转为 DTO 数据,所以需要编写课程计划的对象属性映射。
●对象属性转换类
package com.xuecheng.content.convert;import com.xuecheng.api.content.model.TeachplanMediaDTO;import com.xuecheng.api.content.model.vo.BindTeachplanMediaVO;import com.xuecheng.content.entity.TeachplanMedia;import org.mapstruct.Mapper;import org.mapstruct.Mapping;import org.mapstruct.factory.Mappers;import java.util.List;/*** 课程计划媒资的PO和DTO转换器*/@Mapperpublic interface TeachplanMediaConvert {TeachplanMediaConvert INSTANCE = Mappers.getMapper(TeachplanMediaConvert.class);TeachplanMedia dto2entity(TeachplanMediaDTO teachplanDTO);TeachplanMediaDTO entity2dto(TeachplanMedia teachplanMedia);}
●接口
package com.xuecheng.content.service;import com.xuecheng.api.content.model.TeachplanMediaDTO;import com.xuecheng.content.entity.TeachplanMedia;import com.baomidou.mybatisplus.extension.service.IService;/*** 课程计划和媒资服务类*/public interface TeachplanService extends IService<Teachplan> {//其他代码省略/*** 课程计划绑定媒资信息* @param dto* @param companyId* @return*/TeachplanMediaDTO associateMedia(TeachplanMediaDTO dto,Long companyId);}
●实现类
/*** <p>* 课程计划 服务实现类* </p>*/@Slf4j@Servicepublic class TeachplanServiceImpl extends ServiceImpl<TeachplanMapper, Teachplan> implements TeachplanService {@Autowiredprivate MediaApiAgent mediaApiAgent;@Autowiredprivate TeachplanMediaService teachplanMediaService;/** 业务分析:* 1.判断关键数据* teachplanid mediaid* 2.判断业务数据* 课程的计划* 判断是否是第三级* 判断课程类型是否录播(课程基本信息中获得并判断)* 判断课程计划是否存在:teachplanId、courseId** 课程基本信息* 判断是否存在或是否是同一家机构* 判断是否删除* 判断审核状态:未提交、审核未通过** 媒资信息* 判断媒资信息是否存在* 判断是否是同一家机构* 判断媒资审核状态* 3.保存课程计划和媒资信息的数据* 判断绑定的信息是否存在* 如果存在* 修改绑定媒资的信息即可:mediaid mediafilename* 如果不存在* 添加课程计划媒资信息** 4.修改课程计划的类型* mediatype值*** 5.判断保存的结果并返回数据库最新数据内容* */@Transactionalpublic TeachplanMediaDTO associateMedia(TeachplanMediaDTO dto, Long companyId) {//1.判断关键数据// teachplanid mediaidif (ObjectUtils.isEmpty(dto.getTeachplanId())||ObjectUtils.isEmpty(dto.getMediaId())||ObjectUtils.isEmpty(companyId)) {ExceptionCast.cast(CommonErrorCode.E_100101);}// 2.判断业务数据// 课程计划// 判断是否存在Teachplan teachplan = this.getById(dto.getTeachplanId());if (ObjectUtils.isEmpty(teachplan)) {ExceptionCast.cast(ContentErrorCode.E_120402);}// 判断课程计划等级// 只有三级课程计划才可以绑定媒资if (!(TeachPlanEnum.THIRD_LEVEL.equals(teachplan.getGrade()))) {ExceptionCast.cast(ContentErrorCode.E_120410);}// 课程基础信息// 判断是否是同一家机构// 判断该课程的审核状态(课程基础信息)// 只有:未提交、审核未通过CourseBaseDTO courseBase = getCourseAndVerify(companyId, teachplan.getCourseId());// 判断课程的类型:只有是点播课程才可以绑定媒资信息String teachmode = courseBase.getTeachmode();if (!(CourseModeEnum.COURSE_MODE_RECORD_STATUS.getCode().equals(teachmode))) {ExceptionCast.cast(ContentErrorCode.E_120418);}// 媒资信息// 判断是否存在// 判断是否是同一家教学机构// 判断媒资审核状态RestResponse<MediaDTO> mediaResponse = mediaApiAgent.getMediaById4s(dto.getMediaId());if (!(mediaResponse.isSuccessful())) {ExceptionCast.castWithCodeAndDesc(mediaResponse.getCode(),mediaResponse.getMsg());}MediaDTO mediaDTO = mediaResponse.getResult();if (!(ObjectUtils.nullSafeEquals(companyId,mediaDTO.getCompanyId()))) {ExceptionCast.cast(CommonErrorCode.E_100108);}if (!(AuditEnum.AUDIT_PASTED_STATUS.getCode().equals(mediaDTO.getAuditStatus()))) {ExceptionCast.cast(ContentErrorCode.E_120416);}// 3.保存课程计划和媒资信息的数据// 判断绑定的信息是否存在// select * from teachplan_media where teachplan_id = ? and courseid = ?LambdaQueryWrapper<TeachplanMedia> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(TeachplanMedia::getTeachplanId, dto.getTeachplanId());queryWrapper.eq(TeachplanMedia::getCourseId, dto.getCourseId());TeachplanMedia teachplanMedia = teachplanMediaService.getOne(queryWrapper);boolean teachplanMeidaResult = false;if (ObjectUtils.isEmpty(teachplanMedia)) {// 如果不存在// 添加课程计划媒资信息teachplanMedia = TeachplanMedia.builder().mediaId(dto.getMediaId()).mediaFilename(mediaDTO.getFilename()).teachplanId(dto.getTeachplanId()).courseId(teachplan.getCourseId()).build();teachplanMeidaResult = teachplanMediaService.save(teachplanMedia);} else {// 如果存在// 修改绑定媒资的信息即可:mediaid mediafilenameLambdaUpdateWrapper<TeachplanMedia> updateWrapper = new LambdaUpdateWrapper<>();updateWrapper.set(TeachplanMedia::getMediaId, mediaDTO.getId());updateWrapper.set(TeachplanMedia::getMediaFilename, mediaDTO.getFilename());updateWrapper.eq(TeachplanMedia::getId, teachplanMedia.getId());teachplanMeidaResult = teachplanMediaService.update(updateWrapper);}if (!teachplanMeidaResult) {ExceptionCast.cast(ContentErrorCode.E_120421);}// 4.修改课程计划的类型// mediatype值changeTeachplanMediaType(teachplan.getId(), mediaDTO.getType());// 5.判断保存的结果并返回数据库最新数据内容TeachplanMedia po = teachplanMediaService.getById(teachplanMedia.getId());TeachplanMediaDTO resultDTO = TeachplanMediaConvert.INSTANCE.entity2dto(po);return resultDTO;}private void changeTeachplanMediaType(Long teachplanId, String mediaType) {LambdaUpdateWrapper<Teachplan> teachplanUpdateWrapper = new LambdaUpdateWrapper<>();teachplanUpdateWrapper.set(Teachplan::getMediaType, mediaType);teachplanUpdateWrapper.set(Teachplan::getChangeDate, LocalDateTime.now());teachplanUpdateWrapper.eq(Teachplan::getId, teachplanId);boolean teachplanResult = this.update(teachplanUpdateWrapper);if (!teachplanResult) {ExceptionCast.cast(ContentErrorCode.E_120421);}}}
3. controller编写
/*** <p>* 课程计划 前端控制器* </p>** @author itcast*/@Slf4j@RestControllerpublic class CourseBaseController implements CourseBaseApi {@Autowiredprivate TeachplanService teachplanService;//其他代码省略@PostMapping("teachplan/media/association")public TeachplanMediaDTO associateMedia(@RequestBody BindTeachplanMediaVO vo) {Long companyId = SecurityUtil.getCompanyId();TeachplanMediaDTO teachplanMediaDTO = new TeachplanMediaDTO();teachplanMediaDTO.setTeachplanId(vo.getTeachplanId());teachplanMediaDTO.setMediaId(vo.getMediaId());TeachplanMediaDTO resultDTO = teachplanService.associateMedia(teachplanMediaDTO, companyId);return resultDTO;}}
1.4.6 信息接口测试
测试环境需要启动的微服务有:
1.注册中心 xc-discover-service (端口:63000)
2.服务网关 xc-gateway-service (端口:63010)
3.媒资管理 xc-media-service (端口:63050)
4.内容管理 xc-content-service (端口:63040)
1. 使用 postman 在请求信息中添加请求参数
POST http://127.0.0.1:63010/content/teachplan/media/association
●请求体信息(查询条件参数)
{"mediaId":25,"teachplanId":48}
●请求头(访问令牌)
请求头的 key 值: authorization请求头的 value 值:Bearer ewogICAgImF1ZCI6IFsKICAgICAgICAieHVlY2hlbmctcmVzb3VyY2UiCiAgICBdLAogICAgInBheWxvYWQiOiB7CiAgICAgICAgIjExNzcxNDQyMDk0NjMxMjgxMjUiOiB7CiAgICAgICAgICAgICJyZXNvdXJjZXMiOiBbCiAgICAgICAgICAgIF0sCiAgICAgICAgICAgICJ1c2VyX2F1dGhvcml0aWVzIjogewogICAgICAgICAgICAgICAgInJfMDAxIjogWwogICAgICAgICAgICAgICAgICAgICJ4Y19jb21wYW55X21vZGlmeSIsCgkJCQkJInhjX2NvbXBhbnlfdmlldyIsCgkJCQkJInhjX2NvdXJzZV9iYXNlX2RlbCIsCgkJCQkJInhjX2NvdXJzZV9iYXNlX2VkaXQiLAoJCQkJCSJ4Y19jb3Vyc2VfYmFzZV9saXN0IiwKCQkJCQkieGNfY291cnNlX2Jhc2Vfc2F2ZSIsCgkJCQkJInhjX2NvdXJzZV9iYXNlX3ZpZXciLAoJCQkJCSJ4Y19jb3Vyc2VfcHVibGlzaCIsCgkJCQkJInhjX21hcmtldF9zYXZlX21vZGlmeSIsCgkJCQkJInhjX21hcmtldF92aWV3IiwKCQkJCQkieGNfbWVkaWFfZGVsIiwKCQkJCQkieGNfbWVkaWFfbGlzdCIsCgkJCQkJInhjX21lZGlhX3ByZXZpZXciLAoJCQkJCSJ4Y19tZWRpYV9zYXZlIiwKCQkJCQkieGNfdGVhY2hlcl9saXN0IiwKCQkJCQkieGNfdGVhY2hlcl9tb2RpZnkiLAoJCQkJCSJ4Y190ZWFjaGVyX3NhdmUiLAoJCQkJCSJ4Y193b3JrcmVjb3JkX2NvcnJlY3Rpb24iLAoJCQkJCSJ4Y193b3JrcmVjb3JkX2xpc3QiLAoJCQkJCSJ4Y190ZWFjaHBsYW53b3JrX2RlbCIsCgkJCQkJInhjX3RlYWNocGxhbndvcmtfbGlzdCIsCgkJCQkJInhjX3RlYWNocGxhbndvcmtfc2F2ZV9tb2RpZnkiLAoJCQkJCSJ4Y190ZWFjaHBsYW5fZGVsIiwKCQkJCQkieGNfdGVhY2hwbGFuX3NhdmVfbW9kaWZ5IiwKCQkJCQkieGNfdGVhY2hwbGFuX3ZpZXciCiAgICAgICAgICAgICAgICBdLAogICAgICAgICAgICAgICAgInJfMDAyIjogWwogICAgICAgICAgICAgICAgICAgICJ4Y19jb3Vyc2VfYWRtaW5fbGlzdCIsCgkJCQkJInhjX2NvdXJzZV9iYXNlX2NvbW1pdCIsCgkJCQkJInhjX3N5c3RlbV9jYXRlZ29yeSIsCgkJCQkJInhjX21fbWVkaWFfbGlzdCIsCgkJCQkJInhjX21lZGlhX2F1ZGl0IgogICAgICAgICAgICAgICAgXQogICAgICAgICAgICB9CiAgICAgICAgfQogICAgfSwKICAgICJ1c2VyX25hbWUiOiAieGMtdXNlci1maXJzdCIsCiAgICAic2NvcGUiOiBbCiAgICAgICAgInJlYWQiCiAgICBdLAogICAgIm1vYmlsZSI6ICIxNTAxMjM0NTY3OCIsCiAgICAiZXhwIjogMTYwNjUyNTEyMiwKICAgICJjbGllbnRfYXV0aG9yaXRpZXMiOiBbCiAgICAgICAgIlJPTEVfVVNFUiIKICAgIF0sCiAgICAianRpIjogIjFlYjdlOTg3LWQ3YzItNDBmNS1iMGQ2LWNkNjEzOWNiMThlMCIsCiAgICAiY2xpZW50X2lkIjogInhjLWNvbS1wbGF0Zm9ybSIsCiAgICAiY29tcGFueUlkIjogMTIzMjE0MTQyNQp9
响应内容如下(例子):

