day02、内容管理-课程分页查询
1.内容管理
1.1 内容管理是什么 ?
内容管理系统cms(content management system),是协助组织和个人,借助信息技术,实现内容的创建、储存、分享、应用、检索,并在企业个人、组织、业务、战略等诸方面产生价值的过程 。能够支撑内容管理的一种工具或一套工具的软件系统。 不同的项目对内容的定位不同,比如:新闻媒体对新闻信息的管理,公司管理对公司内部数据内容管理、物流对订单内容管理等。
简单来说,就是运用计算机技术,管理行业中的数据。
1.2 内容管理系统定位
本项目作为一个大型的在线教育平台,其内容管理主要对课程相关内容进行管理,从课程数据的录入、课程审批、课程内容发布等内容性的业务需求数据进行管理。
1.3 项目业务介绍(背)
(管理哪些数据)
教育机构通过内容管理操作的业务:
需求列表及业务流程如下:
1、课程内容管理:包括课程的基本信息和课程营销。
- 教育机构用户在门户管理界面中的课程管理链接进入课程管理界面。
- 在管理界面中可以对课程进行列表查询和管理
- 添加课程时选在课程的类型
- 选择课程类型后,添加课程基本信息和课程营销数据
2、课程计划管理:包括课程授课的主体大纲和关联的大纲的资料。
- 对课程基本信息保存后,填写课程计划,如果课程有课程计划需要将其查询出来
- 对新课程没有课程计划,需要填写课程计划大章节
- 在课程计划大章节下填写课程小章节
3、课程教师管理:包括课程授课的教师信息。
- 保存课程计划,对课程的教师进行管理,如果课程有教师信息需要查询出来,并对课程的教师进行管理
1.3 内容管理数据模型
2. ★★项目的开发步骤(背)
后面的开发都按这个步骤来做,很重要。
①需求分析
梳理用户的需求,分析业务流程。
②接口定义
根据需求分析定义服务端微服务接口,提供前端调用。
③服务端和前端并行开发
服务端依据接口进行服务端接口开发(Java后端开发人员)。
- 对业务接口进行开发
- 开发完接口要对其进行测试
前端开发用户操作界面,并调用服务端接口完成业务处理(全栈或前端开发人员)。
- 使用前端技术完成前端界面的构建
- 调用服务端来获取数据
④前后端集成测试
在前后端完成各自的开发后,对其整个业务进行前后端集成测试。
在开发步骤中,后端开发人员只需要关注:
- 功能的业务流程和分析
- 后端接口定义
- 后端微服务的开发
- 后端接口的测试
- 前后端集成测试
3. 本项目的开发步骤
3.1课程查询
3.1.1业务需求
1、分页查询 课程基本信息(CourseBase)集合数据
2、根据课程名称和审核状态条件进行数据查询
3、教学机构只能查询到属于自己机构下的课程基本信息
4、接口基于Http 请求,响应Json数据
3.1.2接口定义
《项目开发规范文档》中指出:常用的HTTP动词有下面五个:
GET(SELECT) | 从服务器取出资源(一项或多项) |
---|---|
POST(CREATE) | 在服务器新建一个资源 |
PUT(UPDATE) | 在服务器更新资源(客户端提供改变后的完整资源) |
DELETE(DELETE) | 从服务器删除资源 |
而在分页查询接口中只能使用POST请求,不能用GET请求:
因为分页查询要传递两种数据:①分页数据,采用QueryString方式传参
②查询条件,采用RequestBody方式传参,而GET请求没有请求体
3.1.3接口开发
-
将xc-content-service放入项目的xc-parent下,pom文件中导入。
-
将自动生成器生成的xc-mp-generator\src\main\java\com\xuecheng\content目录下的四个文件夹导入到xc-content-service\src\main\java\com\xuecheng\content目录下。
-
在xc-api目录com\xuecheng\api\content下编写CourseBaseApi接口,创建qo包,在包下编写类QueryCourseBaseModel封装分页查询的对象。
@Api(value = "CourseBaseApi",tags = "课程基础信息管理Api") public interface CourseBaseApi { @ApiOperation("课程基础信息分页查询") PageVO queryCourseList(PageRequestParams params, QueryCourseBaseModel model); } @ApiModel("课程基础信息查询封装对象") public class QueryCourseBaseModel { private String auditStatus; private String courseName; }
-
修改xc-content-sercvice下的CourseBaseController,
@Slf4j @RestController @RequestMapping("courseBase") public class CourseBaseController implements CourseBaseApi{ @Autowired private CourseBaseService courseBaseService; /* * SpringMVC对于参数方式接受: * 1.默认:QueryString--@RequestParam(该注解不需要添加) * 传入的参数key和对象中的属性名一致时,也可以不加 * 2.path(Restful风格)--@PathValiable * 3.body请求体传参--@RequestBody * */ @PostMapping("course/list") public PageVO queryCourseList(PageRequestParams params, @RequestBody QueryCourseBaseModel model) { return null; }
-
服务之间的调用(nacos)简单回顾:
namespace: 区分开发环境
group: 区分一个环境下的不同项目组dataid:区分一个下面下的不同工程
profile: 区分一个环境下一个项目的不同种类环境的配置内容 -
在xc-content-service的pom文件中添加nacos配置,(同时删除apollo配置(老师挖的坑))
<!-- spring cloud nacos --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency> <!-- druid 配置 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> </dependency>
-
配置application.yml(nacos作为配置中心,可以将文件名改为bootstrap.yml,这里nacos既做注册中心,又做配置中心)
ps:删除其中的apollo配置
#微服务配置 spring: application: name: content-service jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8 cloud: nacos: server-addr: 192.168.94.129:8848 discovery: namespace: cb0fda63-816a-4eba-95a5-1c3e8e8155a9 group: xc-group config: namespace: cb0fda63-816a-4eba-95a5-1c3e8e8155a9 group: xc-group file-extension: properties profiles: active: dev # 日志文件配置路径 logging: config: classpath:log4j2-dev.xml # swagger 文档配置 swagger: title: "学成在线内容管理系统" description: "内容系统管理系统对课程相关信息进行业务管理数据" base-package: com.xuecheng enabled: true version: 1.0.0
-
由于各数据库连接池的配置信息大致相同,可提取其中公共部分,构建公共配置
主要有以下公共配置:(..config..)
-
mp-config.properties mp公共配置信息
-
spring-http-config.properties http公共配置信息
-
spring-druid-config.properties drurid数据库连接池公共配置信息
默认不会加载公共配置,在配置文件中添加以下信息:... shared-configs: - dataId: mp-config.properties group: xc-group - dataId: spring-http-config.properties group: xc-group - dataId: spring-druid-config.properties group: xc-group
公共配置的一些配置不适用,需要在content-service-dev.properties中对相关配置进行覆盖
#覆盖公共配置信息 server.servlet.context-path = /content server.port=63040 #数据库链接的覆盖 spring.datasource.url = jdbc:mysql://192.168.94.129:3306/xc_content?userUnicode=true&useSSL=false&characterEncoding=utf8
-
-
重新编译项目,启动ContentApplication
代码书写
步骤:
- 判断关键数据,主要是分页数据
- 构建分页对象,设置当前页和每页条数
- 构建查询条件:
- 课程名称:like查询
- 课程审核状态:eq查询
- 获得数据
- 封装查询结果
- 通过swagger进行代码测试
3.1.4接口数据传输
接口数据传输存在的3个问题:
-
PO(持久化对象)实体类和后端数据库一一对应,直接传输给前端人员,容易泄露数据库;
-
前端只需要得到所需数据;
-
后端需将数据转为json,数据量较大时,耗时较长。
①DTO(数据传输对象)
DTO(Data Transfer Object,数据传送对象) 是一个普通的Java类,它封装了要传送的数据。当前端需要读取服务器端的数据的时候,服务器端将数据封装在DTO中,这样前端就可以获得它需要的所有数据。
由于 DTO 中的属性都是前端所需要的。相比 PO 来说,属性会精简,这样在转换 Json 时,效率会好。
DTO 中的属性名称可以定义和 PO 属性名称不一致,这样前端工程获得 DTO 对象中的数据,其属性名称不一致,从而提高了应用的安全性。
tip1:自动生成器中修改
//TODO 默认生成entity,需要生成DTO修改此变量
// 一般情况下要先生成 DTO类 然后修改此参数再生成 PO 类。
private static final Boolean IS_DTO = true;
- 从false改为true之后,运行代码生成器,在entity目录下生成DTO文件
tip2:前端不需要的数据可以在DTO文件中进行删除,属性名也可以进行自定义。
▲该项目中前端已经完成,这里不可以自定义属性名称,否则前端接收不到数据,项目无法正常运行。▲
这里将主键属性名改为courseBaseId,其余项不做修改。
-
在xc-api目录com.xuecheng.api.content下新建dto包,将生成的CourseBaseDTO放入
(与前端交互的数据一般放在api目录下,公共的数据放入common目录)
②DTO用法
//DTO的使用
//先声明一个空数据
List<CourseBaseDTO> dtos = Collections.EMPTY_LIST;
//集合不为空,
if (!(CollectionUtils.isEmpty(records))){
for (CourseBase record : records ) {
CourseBaseDTO courseBaseDTO = new CourseBaseDTO();
courseBaseDTO.setCourseBaseId(record.getId());
courseBaseDTO.setName(record.getName());
courseBaseDTO.setMt(record.getMt());
/* ...
* 属性较多时,赋值过程较为繁琐
* 引入MapStruct(对象属性映射)
*/
}
}
③MapStruct(对象属性映射)的用法
-
导入依赖
-
<dependencies> <!-- MapStruct 依赖包 --> <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-jdk8</artifactId> <version>1.3.0.Final</version> </dependency> <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-processor</artifactId> <version>1.3.0.Final</version> </dependency> </dependencies> <!-- maven 编译插件 --> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.7.0</version> <configuration> <source>1.8</source> <target>1.8</target> <encoding>utf-8</encoding> </configuration> </plugin> </plugins> </build>
-
官网实例
项目中:
-
在test.java包中,创建类com.xuecheng.content.test.MapStructTest.java
import com.xuecheng.api.content.model.dto.CourseBaseDTO; import com.xuecheng.content.convert.CourseBaseConvert; import com.xuecheng.content.entity.CourseBase; import org.junit.jupiter.api.Test; public class MapStructTest { @Test public void entity2dto(){ //po CourseBase courseBase = new CourseBase(); courseBase.setId(11L); courseBase.setName("测试课程"); //dto CourseBaseDTO dto = CourseBaseConvert.INSTANCE.entity2dto(courseBase);; System.out.println(dto); } }
-
在com.xuecheng.content.convert目录下新建接口CourseBaseConvert
import com.xuecheng.api.content.model.dto.CourseBaseDTO;
import com.xuecheng.content.entity.CourseBase;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@Mapper
public interface CourseBaseConvert {
CourseBaseConvert INSTANCE = Mappers.getMapper(CourseBaseConvert.class);
CourseBaseDTO entity2dto(CourseBase courseBase);
}
-
运行MapStructTest,发现id未赋值给courseBaseId
(不做任何操作时,找属性名一致的进行赋值,属性名不同的则不赋值)
-
将id赋值给courseBaseId
//添加 //1.单个属性 @Mapping(source = "id",target = "courseBaseId") //2.多个属性 @Mappings({ @Mapping(source = "id1",target = "courseBaseId1"), @Mapping(source = "id2",target = "courseBaseId2") ... }) CourseBaseDTO entity2dto(CourseBase courseBase);
-
还可以直接对集合对象进行转换
//传入集合对象 /* * 集合对象的转换: * 条件:需要依赖单个对象属性转换的方法(↑) * entitys2dtos --> entitys2dto * */ List<CourseBaseDTO> entitys2dtos(List<CourseBase> courseBases);
可以在target\classes\com\xuecheng\content\convert\CourseBaseConvertImpl.class中查看自动生成的代码,可以看出集合对象的转换中用到了单个属性的转换方法。
public List<CourseBaseDTO> entitys2dtos(List<CourseBase> courseBases) { if (courseBases == null) { return null; } else { List<CourseBaseDTO> list = new ArrayList(courseBases.size()); Iterator var3 = courseBases.iterator(); while(var3.hasNext()) { CourseBase courseBase = (CourseBase)var3.next(); list.add(this.entity2dto(courseBase)); } return list; } }
3.1.5机构查询数据隔离
-
教育机构需要通过第三方登录系统完成登录操作。
-
登录成功后会向教学机构的客户端保存一份token(令牌,相当于电影票)。
-
教学机构每次在使用前端系统访问后端微服务时,都会将token以请求头的方式发送给后端。
请求头的key: authorization 前端访问后端示意图
tip:网关需要向注册中心索取ip地址和端口号,所以网关要注册到nacos
-
注册网关到nacos
- 将xc-geteway-server复制到xc-parent目录下,修改application.yml,运行GatewayServer,将网关注册到nacos.
-
测试网关
将ContentApplication运行起来,使用Postman测试
-
选择POST请求,http://localhost:63010/content/course/list?pageNo=1&pageSize=2
-
Headers中添加测试令牌,key:Authorization value:
Bearer ewogICAgImF1ZCI6IFsKICAgICAgICAieHVlY2hlbmctcmVzb3VyY2UiCiAgICBdLAogICAgInBheWxvYWQiOiB7CiAgICAgICAgIjExNzcxNDQyMDk0NjMxMjgxMjUiOiB7CiAgICAgICAgICAgICJyZXNvdXJjZXMiOiBbCiAgICAgICAgICAgIF0sCiAgICAgICAgICAgICJ1c2VyX2F1dGhvcml0aWVzIjogewogICAgICAgICAgICAgICAgInJfMDAxIjogWwogICAgICAgICAgICAgICAgICAgICJ4Y19jb21wYW55X21vZGlmeSIsCgkJCQkJInhjX2NvbXBhbnlfdmlldyIsCgkJCQkJInhjX2NvdXJzZV9iYXNlX2RlbCIsCgkJCQkJInhjX2NvdXJzZV9iYXNlX2VkaXQiLAoJCQkJCSJ4Y19jb3Vyc2VfYmFzZV9saXN0IiwKCQkJCQkieGNfY291cnNlX2Jhc2Vfc2F2ZSIsCgkJCQkJInhjX2NvdXJzZV9iYXNlX3ZpZXciLAoJCQkJCSJ4Y19jb3Vyc2VfcHVibGlzaCIsCgkJCQkJInhjX21hcmtldF9zYXZlX21vZGlmeSIsCgkJCQkJInhjX21hcmtldF92aWV3IiwKCQkJCQkieGNfbWVkaWFfZGVsIiwKCQkJCQkieGNfbWVkaWFfbGlzdCIsCgkJCQkJInhjX21lZGlhX3ByZXZpZXciLAoJCQkJCSJ4Y19tZWRpYV9zYXZlIiwKCQkJCQkieGNfdGVhY2hlcl9saXN0IiwKCQkJCQkieGNfdGVhY2hlcl9tb2RpZnkiLAoJCQkJCSJ4Y190ZWFjaGVyX3NhdmUiLAoJCQkJCSJ4Y193b3JrcmVjb3JkX2NvcnJlY3Rpb24iLAoJCQkJCSJ4Y193b3JrcmVjb3JkX2xpc3QiLAoJCQkJCSJ4Y190ZWFjaHBsYW53b3JrX2RlbCIsCgkJCQkJInhjX3RlYWNocGxhbndvcmtfbGlzdCIsCgkJCQkJInhjX3RlYWNocGxhbndvcmtfc2F2ZV9tb2RpZnkiLAoJCQkJCSJ4Y190ZWFjaHBsYW5fZGVsIiwKCQkJCQkieGNfdGVhY2hwbGFuX3NhdmVfbW9kaWZ5IiwKCQkJCQkieGNfdGVhY2hwbGFuX3ZpZXciCiAgICAgICAgICAgICAgICBdLAogICAgICAgICAgICAgICAgInJfMDAyIjogWwogICAgICAgICAgICAgICAgICAgICJ4Y19jb3Vyc2VfYWRtaW5fbGlzdCIsCgkJCQkJInhjX2NvdXJzZV9iYXNlX2NvbW1pdCIsCgkJCQkJInhjX3N5c3RlbV9jYXRlZ29yeSIsCgkJCQkJInhjX21fbWVkaWFfbGlzdCIsCgkJCQkJInhjX21lZGlhX2F1ZGl0IgogICAgICAgICAgICAgICAgXQogICAgICAgICAgICB9CiAgICAgICAgfQogICAgfSwKICAgICJ1c2VyX25hbWUiOiAieGMtdXNlci1maXJzdCIsCiAgICAic2NvcGUiOiBbCiAgICAgICAgInJlYWQiCiAgICBdLAogICAgIm1vYmlsZSI6ICIxNTAxMjM0NTY3OCIsCiAgICAiZXhwIjogMTYwNjUyNTEyMiwKICAgICJjbGllbnRfYXV0aG9yaXRpZXMiOiBbCiAgICAgICAgIlJPTEVfVVNFUiIKICAgIF0sCiAgICAianRpIjogIjFlYjdlOTg3LWQ3YzItNDBmNS1iMGQ2LWNkNjEzOWNiMThlMCIsCiAgICAiY2xpZW50X2lkIjogInhjLWNvbS1wbGF0Zm9ybSIsCiAgICAiY29tcGFueUlkIjogMTIzMjE0MTQyNQp9
-
Body,选择raw→Json
-