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请求:

image-20220718160504101

因为分页查询要传递两种数据:①分页数据,采用QueryString方式传参

②查询条件,采用RequestBody方式传参,而GET请求没有请求体

3.1.3接口开发

  1. 将xc-content-service放入项目的xc-parent下,pom文件中导入。

  2. 将自动生成器生成的xc-mp-generator\src\main\java\com\xuecheng\content目录下的四个文件夹导入到xc-content-service\src\main\java\com\xuecheng\content目录下。

  3. 在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;
    }
    
  4. 修改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;
          }
    
  5. 服务之间的调用(nacos)简单回顾:

    namespace: 区分开发环境
    group: 区分一个环境下的不同项目组dataid:区分一个下面下的不同工程
    profile: 区分一个环境下一个项目的不同种类环境的配置内容

  6. 在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>
    
  7. 配置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
    

    image-20220718191851736

  8. 由于各数据库连接池的配置信息大致相同,可提取其中公共部分,构建公共配置

    主要有以下公共配置:(..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
      
  9. 重新编译项目,启动ContentApplication

代码书写

步骤:

  1. 判断关键数据,主要是分页数据
  2. 构建分页对象,设置当前页和每页条数
  3. 构建查询条件:
    • 课程名称:like查询
    • 课程审核状态:eq查询
  4. 获得数据
  5. 封装查询结果
  6. 通过swagger进行代码测试

3.1.4接口数据传输

接口数据传输存在的3个问题:

  • PO(持久化对象)实体类和后端数据库一一对应,直接传输给前端人员,容易泄露数据库;

  • 前端只需要得到所需数据;

  • 后端需将数据转为json,数据量较大时,耗时较长。

①DTO(数据传输对象)

​ DTO(Data Transfer Object,数据传送对象) 是一个普通的Java类,它封装了要传送的数据。当前端需要读取服务器端的数据的时候,服务器端将数据封装在DTO中,这样前端就可以获得它需要的所有数据。
​ 由于 DTO 中的属性都是前端所需要的。相比 PO 来说,属性会精简,这样在转换 Json 时,效率会好。
​ DTO 中的属性名称可以定义和 PO 属性名称不一致,这样前端工程获得 DTO 对象中的数据,其属性名称不一致,从而提高了应用的安全性。

image-20220719010818126

tip1:自动生成器中修改

//TODO 默认生成entity,需要生成DTO修改此变量	
// 一般情况下要先生成 DTO类 然后修改此参数再生成 PO 类。
private static final Boolean IS_DTO = true;
  1. 从false改为true之后,运行代码生成器,在entity目录下生成DTO文件

image-20220719011442093

tip2:前端不需要的数据可以在DTO文件中进行删除,属性名也可以进行自定义。

▲该项目中前端已经完成,这里不可以自定义属性名称,否则前端接收不到数据,项目无法正常运行。▲

这里将主键属性名改为courseBaseId,其余项不做修改。

  1. 在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(对象属性映射)的用法
  1. 导入依赖

  2. <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>
    
  3. 官网实例

image-20220719014744089

项目中:

  1. 在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);
        }
    }
    
  2. 在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);
    
}
  1. 运行MapStructTest,发现id未赋值给courseBaseId

    (不做任何操作时,找属性名一致的进行赋值,属性名不同的则不赋值)

  2. 将id赋值给courseBaseId

     	//添加
    	//1.单个属性
    	@Mapping(source = "id",target = "courseBaseId")
    	//2.多个属性
    	@Mappings({
            @Mapping(source = "id1",target = "courseBaseId1"),
            @Mapping(source = "id2",target = "courseBaseId2")
            ...
        })
        CourseBaseDTO entity2dto(CourseBase courseBase);
    
  3. 还可以直接对集合对象进行转换

    //传入集合对象
        /*
        * 集合对象的转换:
        * 条件:需要依赖单个对象属性转换的方法(↑)
        * 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机构查询数据隔离

image-20220719030423388

  1. 教育机构需要通过第三方登录系统完成登录操作。

  2. 登录成功后会向教学机构的客户端保存一份token(令牌,相当于电影票)。

  3. 教学机构每次在使用前端系统访问后端微服务时,都会将token以请求头的方式发送给后端。
    请求头的key: authorization

    image-20220719030740989

    ​ 前端访问后端示意图

    tip:网关需要向注册中心索取ip地址和端口号,所以网关要注册到nacos

  4. 注册网关到nacos

    • 将xc-geteway-server复制到xc-parent目录下,修改application.yml,运行GatewayServer,将网关注册到nacos.
  5. 测试网关

    将ContentApplication运行起来,使用Postman测试

    • 选择POST请求,http://localhost:63010/content/course/list?pageNo=1&pageSize=2

    • Headers中添加测试令牌,key:Authorization value:

      Bearer ewogICAgImF1ZCI6IFsKICAgICAgICAieHVlY2hlbmctcmVzb3VyY2UiCiAgICBdLAogICAgInBheWxvYWQiOiB7CiAgICAgICAgIjExNzcxNDQyMDk0NjMxMjgxMjUiOiB7CiAgICAgICAgICAgICJyZXNvdXJjZXMiOiBbCiAgICAgICAgICAgIF0sCiAgICAgICAgICAgICJ1c2VyX2F1dGhvcml0aWVzIjogewogICAgICAgICAgICAgICAgInJfMDAxIjogWwogICAgICAgICAgICAgICAgICAgICJ4Y19jb21wYW55X21vZGlmeSIsCgkJCQkJInhjX2NvbXBhbnlfdmlldyIsCgkJCQkJInhjX2NvdXJzZV9iYXNlX2RlbCIsCgkJCQkJInhjX2NvdXJzZV9iYXNlX2VkaXQiLAoJCQkJCSJ4Y19jb3Vyc2VfYmFzZV9saXN0IiwKCQkJCQkieGNfY291cnNlX2Jhc2Vfc2F2ZSIsCgkJCQkJInhjX2NvdXJzZV9iYXNlX3ZpZXciLAoJCQkJCSJ4Y19jb3Vyc2VfcHVibGlzaCIsCgkJCQkJInhjX21hcmtldF9zYXZlX21vZGlmeSIsCgkJCQkJInhjX21hcmtldF92aWV3IiwKCQkJCQkieGNfbWVkaWFfZGVsIiwKCQkJCQkieGNfbWVkaWFfbGlzdCIsCgkJCQkJInhjX21lZGlhX3ByZXZpZXciLAoJCQkJCSJ4Y19tZWRpYV9zYXZlIiwKCQkJCQkieGNfdGVhY2hlcl9saXN0IiwKCQkJCQkieGNfdGVhY2hlcl9tb2RpZnkiLAoJCQkJCSJ4Y190ZWFjaGVyX3NhdmUiLAoJCQkJCSJ4Y193b3JrcmVjb3JkX2NvcnJlY3Rpb24iLAoJCQkJCSJ4Y193b3JrcmVjb3JkX2xpc3QiLAoJCQkJCSJ4Y190ZWFjaHBsYW53b3JrX2RlbCIsCgkJCQkJInhjX3RlYWNocGxhbndvcmtfbGlzdCIsCgkJCQkJInhjX3RlYWNocGxhbndvcmtfc2F2ZV9tb2RpZnkiLAoJCQkJCSJ4Y190ZWFjaHBsYW5fZGVsIiwKCQkJCQkieGNfdGVhY2hwbGFuX3NhdmVfbW9kaWZ5IiwKCQkJCQkieGNfdGVhY2hwbGFuX3ZpZXciCiAgICAgICAgICAgICAgICBdLAogICAgICAgICAgICAgICAgInJfMDAyIjogWwogICAgICAgICAgICAgICAgICAgICJ4Y19jb3Vyc2VfYWRtaW5fbGlzdCIsCgkJCQkJInhjX2NvdXJzZV9iYXNlX2NvbW1pdCIsCgkJCQkJInhjX3N5c3RlbV9jYXRlZ29yeSIsCgkJCQkJInhjX21fbWVkaWFfbGlzdCIsCgkJCQkJInhjX21lZGlhX2F1ZGl0IgogICAgICAgICAgICAgICAgXQogICAgICAgICAgICB9CiAgICAgICAgfQogICAgfSwKICAgICJ1c2VyX25hbWUiOiAieGMtdXNlci1maXJzdCIsCiAgICAic2NvcGUiOiBbCiAgICAgICAgInJlYWQiCiAgICBdLAogICAgIm1vYmlsZSI6ICIxNTAxMjM0NTY3OCIsCiAgICAiZXhwIjogMTYwNjUyNTEyMiwKICAgICJjbGllbnRfYXV0aG9yaXRpZXMiOiBbCiAgICAgICAgIlJPTEVfVVNFUiIKICAgIF0sCiAgICAianRpIjogIjFlYjdlOTg3LWQ3YzItNDBmNS1iMGQ2LWNkNjEzOWNiMThlMCIsCiAgICAiY2xpZW50X2lkIjogInhjLWNvbS1wbGF0Zm9ybSIsCiAgICAiY29tcGFueUlkIjogMTIzMjE0MTQyNQp9
      
    • Body,选择raw→Json

posted @   忘了鱼尾纱的猫  阅读(224)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?

阅读目录(Content)

此页目录为空

  1. 1 刘哈哈与大先生 刘心&大鹏
  2. 2 我们打着光脚在风车下跑,手上的狗尾巴草摇啊摇 等一下就回家 / -艾兜
  3. 3 哎呦 毛不易
  4. 4 夜、萤火虫和你 AniFace
刘哈哈与大先生 - 刘心&大鹏
00:00 / 00:00
An audio error has occurred, player will skip forward in 2 seconds.

作词 : 刘心/大鹏

作曲 : 刘心

刘:我越来越没自信了

破头发还是不停的脱

那些快乐 快乐 不属于我

还剩下什么 一把吉他陪我唱歌

鹏:这不是我想的生活

鹏:这不是我想的生活

多希望有美眉爱看我

不是笑我 笑我 调侃着我

搞得像个失败者

其实我妈妈说我优点也很多

刘:这个世界开始变得越来越快了

刘:这个世界开始变得越来越快了

可是怎么却都不快乐

鹏:脱下你沉重的假面听我唱首歌

鹏:脱下你沉重的假面听我唱首歌

简单有时比复杂值得

刘:我想要点春风 我想要点感动

刘:我想要点春风 我想要点感动

鹏:我想要点香槟 我想要碟花生

沙滩 海风

刘:美女如云穿梭

这是我的美梦(合)白日梦

刘:这不是我想的生活

刘:这不是我想的生活

多希望有美眉爱看我

不是笑我 笑我 调侃着我

搞得像个失败者

其实我妈妈说我优点也很多

鹏:这个世界开始变得越来越快了

鹏:这个世界开始变得越来越快了

可是怎么却都不快乐

刘:脱下你沉重的假面听我唱首歌

刘:脱下你沉重的假面听我唱首歌

简单有时比复杂值得

鹏:这个世界开始变得越来越快了

鹏:这个世界开始变得越来越快了

可是怎么却都不快乐

刘:脱下你沉重的假面听我唱首歌

刘:脱下你沉重的假面听我唱首歌

简单有些时候都要比复杂值得

鹏:我想要点春风 我想要点感动

鹏:我想要点春风 我想要点感动

刘:我想要点香槟 我想要碟花生

沙滩 海风

鹏:美女如云穿梭

这是我的美梦(合)白日梦

点击右上角即可分享
微信分享提示