第一章 环境搭建

学习目标:

  • 能够描述黑马头条项目有哪些业务
  • 能够了解黑马头条项目使用到什么技术
  • 能够了解黑马头条数据库设计过程
  • 能够掌握初始化工程的搭建
  • 能够掌握接口开发的流程
  • 能够完成频道管理的功能开发
  • 能够掌握使用接口swagger、postman、knife4j

1 项目介绍

1.1项目背景

  1. 随着智能手机的普及,人们更加习惯于通过手机来看新闻。由于生活节奏的加快,很多人只能利用碎片时间来获取信息,因此,对于移动资讯客户端的需求也越来越高。黑马头条项目正是在这样背景下开发出来。黑马头条项目采用当下火热的微服务+大数据技术架构实现。本项目主要着手于获取最新最热新闻资讯,通过大数据分析用户喜好精确推送咨询新闻<br />![1-1.png](https://cdn.nlark.com/yuque/0/2021/png/12492094/1615355782655-d19589d4-f039-4d1f-a2ee-5a0068783986.png#align=left&display=inline&height=239&margin=%5Bobject%20Object%5D&name=1-1.png&originHeight=239&originWidth=579&size=69501&status=done&style=none&width=579)

1.2 项目概述

概述:黑马头条项目是对在线教育平台业务进行大数据统计分析的系统。碎片化、切换频繁、社交化和个性化现如今成为人们阅读行为的标签。黑马头条对海量信息进行搜集,通过系统计算分类,分析用户的兴趣进行推送从而满足用户的需求。项目为新闻资讯类性的app项目,分为自媒体pc端(发布文章咨询、管理文章等),平台后台管理端(审核管理平台内容等),用户app端(用户搜索、浏览、评论、点赞等)

用到的技术:spring、springboot、springcloud、es、maven、docker、redis、mongoDB、MySQL等
1-2-1.png

1.3 项目术语定义

  • 项目:泛指黑马头条整个项目或某一项目模块
  • 工程:泛指黑马头条某一项目的源码工程
  • 用户:泛指黑马头条APP用户端用户
  • 自媒体人:泛指通过黑马自媒体系统发送文章的用户
  • 管理员:泛指黑马头条管理系统的使用用户
  • App:泛指黑马头条APP
  • WeMedia:泛指黑马头条自媒体系统
  • Admin:泛指黑马头条管理系统

2 需求说明

2.1 功能架构图

2-1.png

2.2 APP主要功能大纲

  • 频道栏:用户可以通过此功能添加自己感兴趣的频道,在添加标签时,系统可依据用户喜好进行推荐
  • 文章列表:需要显示文章标题、文章图片、评论数等信息,且需要监控文章是否在APP端展现的行为
  • 搜索文章:联想用户想搜索的内容,并记录用户的历史搜索信息
  • 个人中心:用户可以在其个人中心查看收藏、关注的人、以及系统设置等功能
  • 查看文章:用户点击文章进入查看文章页面,在此页面上可进行点赞、评论、不喜欢、分享等操作;除此之外还需要收集用户查看文章的时间,是否看我等行为信息
  • 实名认证:用户可以进行身份证认证和实名认证,实名认证之后即可成为自媒体人,在平台上发布文章
  • 注册登录:登录时,验证内容为手机号登录/注册,通过手机号验证码进行登录/注册,首次登录用户自动注册账号。

2.3 自媒体端功能大纲

  • 内容管理:自媒体用户管理文章页面,可以根据条件进行筛选,文章包含草稿、已发布、未通过、已撤回状态。用户可以对文章进行修改,上/下架操作、查看文章状态等操作
  • 评论管理:管理文章评论页面,显示用户已发布的全部文章,可以查看文章总评论数和粉丝评论数,可以对文章进行关闭评论等操作
  • 素材管理:管理自媒体文章发布的图片,便于用户发布带有多张图片的文章
  • 图文数据:自媒体人发布文章的数据:阅读数、评论数、收藏了、转发量,用户可以查看对应文章的阅读数据
  • 粉丝画像:内容包括:粉丝性别分布、粉丝年龄分布、粉丝终端分布、粉丝喜欢分类分布

2.4 平台管理端功能大纲

  • 用户管理:系统后台用来维护用户信息,可以对用户进行增删改查操作,对于违规用户可以进行冻结操
  • 用户审核:管理员审核用户信息页面,用户审核分为身份审核和实名审核,身份审核是对用户的身份信息进行审核,包括但不限于工作信息、资质信息、经历信息等;实名认证是对用户实名身份进行认证
  • 内容管理:管理员查询现有文章,并对文章进行新增、删除、修改、置顶等操作
  • 内容审核:管理员审核自媒体人发布的内容,包括但不限于文章文字、图片、敏感信息等
  • 频道管理:管理频道分类界面,可以新增频道,查看频道,新增或修改频道关联的标签
  • 网站统计:统计内容包括:日活用户、访问量、新增用户、访问量趋势、热门搜索、用户地区分布等数据
  • 内容统计:统计内容包括:文章采集量、发布量、阅读量、阅读时间、评论量、转发量、图片量等数据
  • 权限管理:超级管理员对后台管理员账号进行新增或删除角色操作

2.5 其它需求

2-1-7.png

2.6 交互需求2-1-8.png

3 技术结构图

包括前端(Weex、Vue、Echarts、WS)、网关(GateWay)、DevOps(单元测试、代码规范)

服务层中包括中间件(Kafka)、索引、微服务、大数据存储等重难点技术
1560934156755.png

  • Weex+Vue+WebSocket :使用Weex跨平台开发工具,整合集成VUE框架,完成黑马头条移动端功能开发,并集成WebSocket实现即时消息(文章推荐、私信)的推送
  • Vue+Echarts : 自媒体系统使用Vue开发关键,集成Echarts图表框架,完成相关粉丝画像、数据分析等功能
  • Vue+Echarts+WebSocket : 管理系统也是使用Vue开发,集成Echarts,完成网站统计、内容统计等功能,集成WebSocket,实现系统看板实时数据自动化更新
  • Spring-Cloud-Gateway : 微服务之前架设的网关服务,实现服务注册中的API请求路由,以及控制流速控制和熔断处理都是常用的架构手段,而这些功能Gateway天然支持
  • PMD&P3C : 静态代码扫描工具,在项目中扫描项目代码,检查异常点、优化点、代码规范等,为开发团队提供规范统一,提升项目代码质量
  • Junit : 在持续集成思想中,单元测试偏向自动化过程,项目通过Junit+Maven的集成实现这种过程
  • 运用Spring Boot快速开发框架,构建项目工程;并结合Spring Cloud全家桶技术,实现后端个人中心、自媒体、管理中心等微服务。
  • 运用WebMagic爬虫技术,完善系统内容自动化采集
  • 运用Kafka完成内部系统消息通知;与客户端系统消息通知;以及实时数据计算
  • 运用MyCat数据库中间件计算,对系统数据进行分开分表,提升系统数据层性能
  • 运用Redis缓存技术,实现热数据的计算,NoSession等功能,提升系统性能指标
  • 运用Zoookeeper技术,完成大数据节点之后的协调与管理,提升系统存储层高可用
  • 使用Mysql存储用户数据,以保证上层数据查询的高性能
  • 使用Mongo存储用户热数据,以保证用户热数据高扩展和高性能指标
  • 使用FastDFS作为静态资源存储器,在其上实现热静态资源缓存、淘汰等功能
  • 运用Habse技术,存储系统中的冷数据,保证系统数据的可靠性
  • 运用ES搜索技术,对冷数据、文章数据建立索引,以保证冷数据、文章查询性能
  • 运用Sqoop、Kettle等工具,实现大数据的离线入仓;或者数据备份到Hadoop
  • 运用Spark+Hive进行离线数据分析,实现系统中各类统计报表
  • 运用Spark Streaming + Hive+Kafka实现实时数据分析与应用;比如文章推荐
  • 运用Neo4j知识图谱技术,分析数据关系,产出知识结果,并应用到上层业务中,以帮助用户、自媒体、运营效果/能力提升。比如粉丝等级计算
  • 运用AI技术,来完成系统自动化功能,以提升效率及节省成本。比如实名认证自动化

4 数据库设计

4.1 ER图设计

4-1.png

er图设计划分出了9个库,各个库主要解决的是某一个特定的业务。

数据库设计规范,详见资料文件夹下《黑马头条-数据库规范设计说明书.md》文件。

PowerDesinger工具使用,详见资料文件夹下’powerdesinger的基本使用’文件夹里的《powerdesinger的基本使用》文件。

4.2 分库设计

  1. 黑马头条项目采用的分库分表设计,因为业务比较复杂,后期的访问量巨大,为了分摊数据库的压力,整个项目用的不只是一个数据库。其中核心库有**5**个,每一个数据库解决的是一个业务点,非常接近与实际项目设计。<br />![4-2-1.png](https://cdn.nlark.com/yuque/0/2021/png/12492094/1615356388873-abd5eef8-72d9-4ccd-b4df-1069a42f1518.png#align=left&display=inline&height=163&margin=%5Bobject%20Object%5D&name=4-2-1.png&originHeight=163&originWidth=879&size=16556&status=done&style=none&width=879)
  • AppInfo app信息库,主要存储用户信息,文章信息,用户动态,用户评论,用户认证等信息
  • Behavior 用户行为库,主要存储用户行为,包括用户的转发,点赞,评论行为等
  • WeMedia 多媒体库,主要存储多媒体人图文数据统计,账号信息,粉丝相关信息等。
  • Crawlers 爬虫库,主要存储从网络上爬取的文章信息等。
  • Admin 后台管理库,主要存储后台管理员的信息。


数据库的设计方式和规范:

  • 参考阿里巴巴开发手册,里面有对MySQL数据库一些相关的建表规范,索引规范,sql语句等相关的一些要求细则。


数据库表结构的设计工具:PowerDesigner

4.3 核心数据流转图

1585930399634.png

说明:整个项目其核心数据为文章信息,上图主要说明文章的数据流转 1 爬虫系统从外网采集数据后入爬虫库,即爬虫库中保存了一份采集的文章信息 2 自媒体人可以通过发布文章后首先进入自媒体库 3 爬虫文章和自媒体文章最后都要经过审核成功后入appinfo库,这里面的文章信息,最终是要给app端用户所查看 4 在app端用户查看的时候,需要记录用户的一些行为,如转发、评论、点赞等需要入用户行为库

4.4 冗余设计

  1. 黑马头条项目全部采用逻辑关联,没有采用主外键约束。也是方便数据源冗余,尽可能少的使用多表关联查询。冗余是为了效率,减少join。单表查询比关联查询速度要快。某个访问频繁的字段可以冗余存放在两张表里,不用关联了。
  2. 如查询一个订单表需要查询该条订单的用户名称,就必须join另外用户表,如果业务表很大,那么就会查询的很慢,这个时候我们就可以使用冗余来解决这个问题,在新建订单的同时不仅仅需要把用户ID存储,同时也需要存储用户的名称,这样我们在查询订单表的时候就不需要去join另外用户表,也能查询出该条订单的用户名称。这样的冗余可以直接的提高查询效率,单表更快。<br />![4-4.png](https://cdn.nlark.com/yuque/0/2021/png/12492094/1615356783677-8ab409f1-8ca8-4950-a061-fe955718aa68.png#align=left&display=inline&height=331&margin=%5Bobject%20Object%5D&name=4-4.png&originHeight=331&originWidth=769&size=42959&status=done&style=none&width=769)

4.5 导入数据库

当天资料文件夹下:数据库脚本

5 初始工程搭建

5.1 主体结构说明4.png

后端工程基于Spring-boot 2.1.5.RELEASE 版本构建,工程父项目为heima-leadnews,并通过继承方式集成Spring-boot。

【父项目下分4个公共子项目】:

  • heima-leadnews-common : 是整个工程的配置核心,包括所有集成三方框架的配置定义,比如redis、kafka等。除此之外还包括项目每个模块及整个项目的常量定义;
  • heima-leadnews-model :项目中用到的Dto、Pojo、Mapper、Enums定义工程;
  • heima-leadnews-utils : 工程公用工具类项目,包含加密/解密、Date、JSON等工具类;
  • heima-leadnew-apis : 整个项目微服务暴露的接口的定义项目,按每个模块进行子包拆分;


【多个微服务】:

  • heima-leadnews-login:用于实现APP+自媒体端用户的登录与注册功能;
  • heima-leadnews-user:用于实现APP端用户中心的功能,比如我的收藏、我的粉丝等功能;
  • heima-leadnews-article:用于实现APP端文章的获取与搜索等功能;还包括频道、标签等功能;
  • heima-leadnews-behavior:用于实现APP端各类行为数据的上传服务;
  • heima-leadnews-quartz:用于封装项目中所有的调度计算任务;
  • heima-leadnews-wemedia:用于实现自媒体管理端的功能;
  • heima-leadnews-admin:用于实现后台管理系统的功能;
  • heima-leadnews-gateway:网关

5.2 后端通用工程搭建

5.2.1 开发环境说明

项目依赖环境(需提前安装好):

  • JDK1.8
  • Intellij Idea
  • Tomcat 8.5
  • Git

5.2.2 IDEA开发工具配置

  • 设置本地仓库,建议使用资料中提供好的仓库

4-2-2.png

  • 设置项目编码格式

4-2-2-1.png

5.2.3 后端初始项目导入

在当天资料中解压heima-leadnews.zip文件,拷贝到一个没有中文和空格的目录,使用idea打开即可
5-2-3.png

6 后端开发-通用说明及开发规范

6.1 什么是前后端开发

项目基于前后端分离的架构进行开发,前后端分离架构总体上包括前端和服务端,通常是多人协作开发

  • 对于后端java工程师:
    把精力放在设计模式,spring+springmvc,linux,mysql事务隔离与锁机制,mongodb,http/tcp,多线程,分布式架构,弹性计算架构,微服务架构,java性能优化,以及相关的项目管理等等。
  • 对于前端工程师:
    把精力放在html5,css3,vuejs,webpack,nodejs,Google V8引擎,javascript多线程,模块化,面向切面编程,设计模式,浏览器兼容性,性能优化等等。

6.1.1前后端分离开发流程

1-3.png

6.1.2 前后端开发流程

1,需求分析

梳理用户的需求,分析业务流程

2,接口定义

根据需求分析定义接口,定义出接口文档

3,服务端和前端并行开发

服务端:依据接口文档进行服务端接口开发

前端:根据用户需求开发操作界面,并根据接口文档制作mock数据,进行测试

4,前后端集成接口联调

最终前端调用服务端接口完成业务

6.2 后端接口开发规范

6.2.1 开发原则

  • 自顶向下的设计原则:功能应该从表现层分析再到控制层、服务层、持久层逐层设计
  • 自底向上的开发原则:上层需调用下层,因此开发应从底层向上层逐层开发
    项目中开发的层次次序参考DB->中间件->持久层->服务层->控制层
  • 单一职责的开发原则:类或者方法提供的功能应该单一明确,特别越底层越应单一职责,以便维护
    项目中Mapper方法必须功能单一,参数明确,拒绝两种以上的持久逻辑使用同一个Mapper方法
  • 依赖倒置的开发原则:上层依赖下层,是依赖下层接口,并不是依赖下层的实现
    项目中每层都是通过接口调用Controller->Service->Mapper

6.2.2 开发步骤

  • 明确类定义:明确哪些是重用类,哪些是需要新增的类
  • 明确主键规则:确认操作表的ID生成规则,自增或id_work
  • ControllerApi定义:定义接口
  • Mapper实现:使用mybatis-plus封装的方法还是自定义mapper映射
  • Service实现:可用通过时序图帮助我们梳理实现逻辑
  • Controller实现:简单的Service层调用
  • 单元测试或接口测试或前端直接联调测试

6.2.3 接口版本规范说明

随着业务的复杂,同一个接口可能出现多个版本,为了方便后期切换和AB测试,需要定义接口的版本号

  • 在某一个微服务下访问controller的时候在包名下加一个版本号,如下properties com.heima.article.controller.v1

  • 在访问具体的接口方法的url映射的时候也应该加上版本说明,如下:java @RequestMapping("/api/v1/article")

6.3 接口通用请求和响应

dto(Data Transfer Object):数据传输对象,用于展示层与服务层之间的数据传输对象

6.3.1 通用的响应对象:

不分页:com.heima.model.common.dtos.ResponseResult

  1. /**
  2. * 通用的结果返回类
  3. * @param <T>
  4. */
  5. public class ResponseResult<T> implements Serializable {
  6. private String host;
  7. private Integer code;
  8. private String errorMessage;
  9. private T data;
  10. public ResponseResult() {
  11. this.code = 200;
  12. }
  13. public ResponseResult(Integer code, T data) {
  14. this.code = code;
  15. this.data = data;
  16. }
  17. public ResponseResult(Integer code, String msg, T data) {
  18. this.code = code;
  19. this.errorMessage = msg;
  20. this.data = data;
  21. }
  22. public ResponseResult(Integer code, String msg) {
  23. this.code = code;
  24. this.errorMessage = msg;
  25. }
  26. public static ResponseResult errorResult(int code, String msg) {
  27. ResponseResult result = new ResponseResult();
  28. return result.error(code, msg);
  29. }
  30. public static ResponseResult okResult(int code, String msg) {
  31. ResponseResult result = new ResponseResult();
  32. return result.ok(code, null, msg);
  33. }
  34. public static ResponseResult okResult(Object data) {
  35. ResponseResult result = setAppHttpCodeEnum(AppHttpCodeEnum.SUCCESS,AppHttpCodeEnum.SUCCESS.getErrorMessage());
  36. if(data!=null) {
  37. result.setData(data);
  38. }
  39. return result;
  40. }
  41. public static ResponseResult errorResult(AppHttpCodeEnum enums){
  42. return setAppHttpCodeEnum(enums,enums.getErrorMessage());
  43. }
  44. public static ResponseResult errorResult(AppHttpCodeEnum enums,String errorMessage){
  45. return setAppHttpCodeEnum(enums,errorMessage);
  46. }
  47. public static ResponseResult setAppHttpCodeEnum(AppHttpCodeEnum enums){
  48. return okResult(enums.getCode(),enums.getErrorMessage());
  49. }
  50. private static ResponseResult setAppHttpCodeEnum(AppHttpCodeEnum enums,String errorMessage){
  51. return okResult(enums.getCode(),errorMessage);
  52. }
  53. public ResponseResult<?> error(Integer code, String msg) {
  54. this.code = code;
  55. this.errorMessage = msg;
  56. return this;
  57. }
  58. public ResponseResult<?> ok(Integer code, T data) {
  59. this.code = code;
  60. this.data = data;
  61. return this;
  62. }
  63. public ResponseResult<?> ok(Integer code, T data, String msg) {
  64. this.code = code;
  65. this.data = data;
  66. this.errorMessage = msg;
  67. return this;
  68. }
  69. public ResponseResult<?> ok(T data) {
  70. this.data = data;
  71. return this;
  72. }
  73. public Integer getCode() {
  74. return code;
  75. }
  76. public void setCode(Integer code) {
  77. this.code = code;
  78. }
  79. public String getErrorMessage() {
  80. return errorMessage;
  81. }
  82. public void setErrorMessage(String errorMessage) {
  83. this.errorMessage = errorMessage;
  84. }
  85. public T getData() {
  86. return data;
  87. }
  88. public void setData(T data) {
  89. this.data = data;
  90. }
  91. public String getHost() {
  92. return host;
  93. }
  94. public void setHost(String host) {
  95. this.host = host;
  96. }
  97. }

分页通用返回:com.heima.model.common.dtos.PageResponseResult

  1. public class PageResponseResult extends ResponseResult {
  2. private Integer currentPage;
  3. private Integer size;
  4. private Integer total;
  5. public PageResponseResult(Integer currentPage, Integer size, Integer total) {
  6. this.currentPage = currentPage;
  7. this.size = size;
  8. this.total = total;
  9. }
  10. public int getCurrentPage() {
  11. return currentPage;
  12. }
  13. public void setCurrentPage(int currentPage) {
  14. this.currentPage = currentPage;
  15. }
  16. public int getSize() {
  17. return size;
  18. }
  19. public void setSize(int size) {
  20. this.size = size;
  21. }
  22. public int getTotal() {
  23. return total;
  24. }
  25. public void setTotal(int total) {
  26. this.total = total;
  27. }
  28. }

6.3.2 通用的请求dtos

com.heima.model.common.dtos.PageRequestDto

  1. @Data
  2. @Slf4j
  3. public class PageRequestDto {
  4. protected Integer size;
  5. protected Integer page;
  6. public void checkParam() {
  7. if (this.page == null || this.page < 0) {
  8. setPage(1);
  9. }
  10. if (this.size == null || this.size < 0 || this.size > 100) {
  11. setSize(10);
  12. }
  13. }
  14. }

6.3.3 通用的异常枚举

com.heima.model.common.enums.AppHttpCodeEnum

  1. public enum AppHttpCodeEnum {
  2. // 成功段0
  3. SUCCESS(0,"操作成功"),
  4. // 登录段1~50
  5. NEED_LOGIN(1,"需要登录后操作"),
  6. LOGIN_PASSWORD_ERROR(2,"密码错误"),
  7. // TOKEN50~100
  8. TOKEN_INVALID(50,"无效的TOKEN"),
  9. TOKEN_EXPIRE(51,"TOKEN已过期"),
  10. TOKEN_REQUIRE(52,"TOKEN是必须的"),
  11. // SIGN验签 100~120
  12. SIGN_INVALID(100,"无效的SIGN"),
  13. SIG_TIMEOUT(101,"SIGN已过期"),
  14. // 参数错误 500~1000
  15. PARAM_REQUIRE(500,"缺少参数"),
  16. PARAM_INVALID(501,"无效参数"),
  17. PARAM_IMAGE_FORMAT_ERROR(502,"图片格式有误"),
  18. SERVER_ERROR(503,"服务器内部错误"),
  19. // 数据错误 1000~2000
  20. DATA_EXIST(1000,"数据已经存在"),
  21. AP_USER_DATA_NOT_EXIST(1001,"ApUser数据不存在"),
  22. DATA_NOT_EXIST(1002,"数据不存在"),
  23. // 数据错误 3000~3500
  24. NO_OPERATOR_AUTH(3000,"无权限操作");
  25. int code;
  26. String errorMessage;
  27. AppHttpCodeEnum(int code, String errorMessage){
  28. this.code = code;
  29. this.errorMessage = errorMessage;
  30. }
  31. public int getCode() {
  32. return code;
  33. }
  34. public String getErrorMessage() {
  35. return errorMessage;
  36. }
  37. }

总结:前后端的交互的规范是什么样的,dto是什么?vo是什么

答:规范请求及响应数据结构,对于请求可以封装成一些公共的“RequestDTO”实体类,比如分页查询肯定会传size、page等参数;对于响应数据,需要统一封装ResponseResult实体,响应给前端的json数据内容中固定存在message提示信息、code响应码、data响应数据等字段,方便前端统一解析使用,一般会将响应码跟响应提示信息封装成枚举实例,方便使用。并且会定义一些ResponseResult中的静态方法封装ResponseResult构造,方便获取成功、失败的响应对象。

DTO:为数据传输实体统称,一种开发定义的实体类的规范,比如说服务间请求调用或前端请求后端调用,需要传递实体对象的参数,那么就将这类实体定义为DTO,比如QueryOrderListDTO——查询订单列表的请求条件实体类;

VO:用于响应给前端页面展示的实体统称,比如OrderListItemVO——订单列表订单响应实体类

6.4 通用环境说明

6.4.1 多环境切换

在每一个微服务的工程中的根目录下创建三个文件,方便各个环境的切换

(1)maven_dev.properties

  1. 定义开发环境的配置

(2)maven_prod.properties

  1. 定义生产环境的配置

(3)maven_test.properties

  1. 定义测试环境的配置,开发阶段使用这个测试环境

默认加载的环境为test,在打包的过程中也可以指定参数打包 package -P test/prod/dev

具体配置,请查看父工程下的maven插件的profiles配置

  1. <profiles>
  2. <profile>
  3. <id>dev</id>
  4. <build>
  5. <filters>
  6. <filter>maven_dev.properties</filter>
  7. </filters>
  8. </build>
  9. </profile>
  10. <profile>
  11. <id>test</id>
  12. <activation>
  13. <activeByDefault>true</activeByDefault>
  14. </activation>
  15. <build>
  16. <filters>
  17. <filter>maven_test.properties</filter>
  18. </filters>
  19. </build>
  20. </profile>
  21. <profile>
  22. <id>prod</id>
  23. <build>
  24. <filters>
  25. <filter>maven_prod.properties</filter>
  26. </filters>
  27. </build>
  28. </profile>
  29. </profiles>

6.4.2 实体类

所有实体类都是按业务模板划分,如下图
1583761499012.png

7 频道管理

7.1 需求说明

1584756626627.png
1584756923331.png

ad_channel 频道表
1584756753159.png

对应实体类:

  1. package com.heima.model.admin.pojos;
  2. import com.baomidou.mybatisplus.annotation.IdType;
  3. import com.baomidou.mybatisplus.annotation.TableField;
  4. import com.baomidou.mybatisplus.annotation.TableId;
  5. import com.baomidou.mybatisplus.annotation.TableName;
  6. import lombok.Data;
  7. import java.io.Serializable;
  8. import java.util.Date;
  9. /**
  10. * <p>
  11. * 频道信息表
  12. * </p>
  13. *
  14. * @author itheima
  15. */
  16. @Data
  17. @TableName("ad_channel")
  18. public class AdChannel implements Serializable {
  19. private static final long serialVersionUID = 1L;
  20. @TableId(value = "id", type = IdType.AUTO)
  21. private Integer id;
  22. /**
  23. * 频道名称
  24. */
  25. @TableField("name")
  26. private String name;
  27. /**
  28. * 频道描述
  29. */
  30. @TableField("description")
  31. private String description;
  32. /**
  33. * 是否默认频道
  34. */
  35. @TableField("is_default")
  36. private Boolean isDefault;
  37. @TableField("status")
  38. private Boolean status;
  39. /**
  40. * 默认排序
  41. */
  42. @TableField("ord")
  43. private Integer ord;
  44. /**
  45. * 创建时间
  46. */
  47. @TableField("created_time")
  48. private Date createdTime;
  49. }

7.2 平台运营微服务搭建(admin)

(1)在heima-leadnews创建模块:heima-leadnews-admin
1584757483058.png

命名规范:

  • com.heima.${模块名称}为基础包名 如平台管理就是 com.heima.admin
  • config 配置信息
  • controller.v1 控制层
  • feign 需要远程调用的feign接口
  • service 业务层
  • mapper 持久层


(2)依赖信息 pom文件

  1. <parent>
  2. <artifactId>heima-leadnews</artifactId>
  3. <groupId>com.heima</groupId>
  4. <version>1.0-SNAPSHOT</version>
  5. </parent>
  6. <modelVersion>4.0.0</modelVersion>
  7. <artifactId>heima-leadnews-admin</artifactId>
  8. <dependencies>
  9. <!-- 引入依赖模块 -->
  10. <dependency>
  11. <groupId>com.heima</groupId>
  12. <artifactId>heima-leadnews-model</artifactId>
  13. </dependency>
  14. <dependency>
  15. <groupId>com.heima</groupId>
  16. <artifactId>heima-leadnews-common</artifactId>
  17. </dependency>
  18. <dependency>
  19. <groupId>com.heima</groupId>
  20. <artifactId>heima-leadnews-apis</artifactId>
  21. </dependency>
  22. <!-- Spring boot starter -->
  23. <dependency>
  24. <groupId>org.springframework.boot</groupId>
  25. <artifactId>spring-boot-starter-web</artifactId>
  26. </dependency>
  27. <dependency>
  28. <groupId>org.springframework.boot</groupId>
  29. <artifactId>spring-boot-starter-test</artifactId>
  30. <scope>test</scope>
  31. </dependency>
  32. </dependencies>

注意:其中mybatis-plus相关的依赖在heima-leadnews-model中定义 其中实体类需要mybatis-plus的注解

  1. <dependencies>
  2. <dependency>
  3. <groupId>mysql</groupId>
  4. <artifactId>mysql-connector-java</artifactId>
  5. </dependency>
  6. <dependency>
  7. <groupId>org.mybatis</groupId>
  8. <artifactId>mybatis</artifactId>
  9. </dependency>
  10. <dependency>
  11. <groupId>org.springframework.boot</groupId>
  12. <artifactId>spring-boot-starter-web</artifactId>
  13. </dependency>
  14. <dependency>
  15. <groupId>org.projectlombok</groupId>
  16. <artifactId>lombok</artifactId>
  17. <scope>provided</scope>
  18. </dependency>
  19. <dependency>
  20. <groupId>org.apache.commons</groupId>
  21. <artifactId>commons-lang3</artifactId>
  22. </dependency>
  23. <dependency>
  24. <groupId>com.baomidou</groupId>
  25. <artifactId>mybatis-plus-boot-starter</artifactId>
  26. </dependency>
  27. </dependencies>

(3)在resources下创建application.yml文件

  1. server:
  2. port: 9001
  3. spring:
  4. application:
  5. name: leadnews-admin
  6. datasource:
  7. driver-class-name: com.mysql.jdbc.Driver
  8. url: jdbc:mysql://localhost:3306/leadnews_admin?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
  9. username: root
  10. password: root
  11. # 设置Mapper接口所对应的XML文件位置,如果你在Mapper接口中有自定义方法,需要进行该配置
  12. mybatis-plus:
  13. mapper-locations: classpath*:mapper/*.xml
  14. # 设置别名包扫描路径,通过该属性可以给包中的类注册别名
  15. type-aliases-package: com.heima.model.admin.pojos

新建日志文件:logback.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <configuration>
  3. <!--定义日志文件的存储地址,使用绝对路径-->
  4. <property name="LOG_HOME" value="e:/logs"/>
  5. <!-- Console 输出设置 -->
  6. <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
  7. <encoder>
  8. <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
  9. <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
  10. <charset>utf8</charset>
  11. </encoder>
  12. </appender>
  13. <!-- 按照每天生成日志文件 -->
  14. <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
  15. <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
  16. <!--日志文件输出的文件名-->
  17. <fileNamePattern>${LOG_HOME}/leadnews.%d{yyyy-MM-dd}.log</fileNamePattern>
  18. </rollingPolicy>
  19. <encoder>
  20. <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
  21. </encoder>
  22. </appender>
  23. <!-- 异步输出 -->
  24. <appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
  25. <!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
  26. <discardingThreshold>0</discardingThreshold>
  27. <!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
  28. <queueSize>512</queueSize>
  29. <!-- 添加附加的appender,最多只能添加一个 -->
  30. <appender-ref ref="FILE"/>
  31. </appender>
  32. <logger name="org.apache.ibatis.cache.decorators.LoggingCache" level="DEBUG" additivity="false">
  33. <appender-ref ref="CONSOLE"/>
  34. </logger>
  35. <logger name="org.springframework.boot" level="debug"/>
  36. <root level="info">
  37. <!--<appender-ref ref="ASYNC"/>-->
  38. <appender-ref ref="FILE"/>
  39. <appender-ref ref="CONSOLE"/>
  40. </root>
  41. </configuration>

(4)创建引导类:AdminApplication

  1. package com.heima.admin;
  2. import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
  3. import org.mybatis.spring.annotation.MapperScan;
  4. import org.springframework.boot.SpringApplication;
  5. import org.springframework.boot.autoconfigure.SpringBootApplication;
  6. import org.springframework.context.annotation.Bean;
  7. @SpringBootApplication
  8. @MapperScan("com.heima.admin.mapper")
  9. public class AdminApplication {
  10. public static void main(String[] args) {
  11. SpringApplication.run(AdminApplication.class,args);
  12. }
  13. /**
  14. * mybatis-plus分页插件
  15. */
  16. @Bean
  17. public PaginationInterceptor paginationInterceptor() {
  18. return new PaginationInterceptor();
  19. }
  20. }

7.3 频道列表

7.3.1 接口定义

在heima-leadnews-apis模块中定义接口com.heima.apis.admin.AdChannelControllerApi

  1. package com.heima.apis.admin;
  2. import com.heima.model.admin.dtos.ChannelDto;
  3. import com.heima.model.common.dtos.ResponseResult;
  4. public interface AdChannelControllerApi {
  5. /**
  6. * 根据名称分页查询频道列表
  7. * @param dto
  8. * @return
  9. */
  10. public ResponseResult findByNameAndPage(ChannelDto dto);
  11. }

ChannelDto

  1. package com.heima.model.admin.dtos;
  2. import com.heima.model.common.dtos.PageRequestDto;
  3. import lombok.Data;
  4. @Data
  5. public class ChannelDto extends PageRequestDto {
  6. /**
  7. * 频道名称
  8. */
  9. private String name;
  10. }

7.3.2 持久层

在com.heima.admin.mapper包下定义接口

  1. package com.heima.admin.mapper;
  2. import com.baomidou.mybatisplus.core.mapper.BaseMapper;
  3. import com.heima.model.admin.pojos.AdChannel;
  4. import org.apache.ibatis.annotations.Mapper;
  5. @Mapper
  6. public interface AdChannelMapper extends BaseMapper<AdChannel> {
  7. }

7.3.3 业务层

在com.heima.admin.service包下定义业务层接口

  1. package com.heima.admin.service;
  2. import com.baomidou.mybatisplus.extension.service.IService;
  3. import com.heima.model.admin.dtos.ChannelDto;
  4. import com.heima.model.admin.pojos.AdChannel;
  5. import com.heima.model.common.dtos.ResponseResult;
  6. public interface AdChannelService extends IService<AdChannel> {
  7. /**
  8. * 根据名称分页查询频道列表
  9. * @param dto
  10. * @return
  11. */
  12. public ResponseResult findByNameAndPage(ChannelDto dto);
  13. }

实现类:com.heima.admin.service.impl.AdChannelServiceImpl

  1. package com.heima.admin.service.impl;
  2. import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
  3. import com.baomidou.mybatisplus.core.metadata.IPage;
  4. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
  5. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
  6. import com.heima.admin.mapper.AdChannelMapper;
  7. import com.heima.admin.service.AdChannelService;
  8. import com.heima.model.admin.dtos.ChannelDto;
  9. import com.heima.model.admin.pojos.AdChannel;
  10. import com.heima.model.common.dtos.PageResponseResult;
  11. import com.heima.model.common.dtos.ResponseResult;
  12. import com.heima.model.common.enums.AppHttpCodeEnum;
  13. import org.apache.commons.lang3.StringUtils;
  14. import org.springframework.stereotype.Service;
  15. @Service
  16. public class AdChannelServiceImpl extends ServiceImpl<AdChannelMapper, AdChannel> implements AdChannelService {
  17. @Override
  18. public ResponseResult findByNameAndPage(ChannelDto dto) {
  19. //1.参数检测
  20. if(dto==null){
  21. return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
  22. }
  23. //分页参数检查
  24. dto.checkParam();
  25. //2.安装名称模糊分页查询
  26. Page page = new Page(dto.getPage(),dto.getSize());
  27. LambdaQueryWrapper<AdChannel> lambdaQueryWrapper = new LambdaQueryWrapper();
  28. if(StringUtils.isNotBlank(dto.getName())){
  29. lambdaQueryWrapper.like(AdChannel::getName,dto.getName());
  30. }
  31. IPage result = page(page, lambdaQueryWrapper);
  32. //3.结果封装
  33. ResponseResult responseResult = new PageResponseResult(dto.getPage(),dto.getSize(),(int)result.getTotal());
  34. responseResult.setData(result.getRecords());
  35. return responseResult;
  36. }
  37. }

7.3.4 控制层

定义AdChannelController实现接口AdChannelControllerApi

  1. @RestController
  2. @RequestMapping("/api/v1/channel")
  3. public class AdChannelController implements AdChannelControllerApi {
  4. @Autowired
  5. private AdChannelService channelService;
  6. @PostMapping("/list")
  7. @Override
  8. public ResponseResult findByNameAndPage(@RequestBody ChannelDto dto){
  9. return channelService.findByNameAndPage(dto);
  10. }
  11. }

7.4 接口测试工具

7.4.1 postman

(1)简介

Postman是一款功能强大的网页调试与发送网页HTTP请求的Chrome插件。postman被500万开发者和超100,000家公司用于每月访问1.3亿个API。java开发通常是作为后台开发语言,通常的项目中的接口开发需要一款测试工具来调试接口,这样无需前端页面也不耽误后台代码的开发进度,postman作为一个接口测试工具,是一个非常不错的选择。

官方网址:https://www.postman.com/

(2)安装

解压资料文件夹中的软件,安装即可

(3)请求和响应

  • 发送请求,请求方式的选择

1584768659540.png

  • 输入请求的url,并发送请求

请求图示.png

  • 通常的接口测试,请求和响应都是json(重要)


请求参数设置
1584769246226.png

结果返回:
1584769286856.png

通过以上描述,希望初学者可以快速的安装和使用postman来进行接口的测试,当然,这里描述并不详尽,postman作为一个接口测试的利器,还有很多特别强大的功能并没有介绍,后面随着项目的深入会继续使用。

7.4.2 Swagger介绍

(1)简介

Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务(https://swagger.io/)。 它的主要作用是:

  1. 使得前后端分离开发更加方便,有利于团队协作
  2. 接口的文档在线自动生成,降低后端开发人员编写接口文档的负担
  3. 功能测试
    Spring已经将Swagger纳入自身的标准,建立了Spring-swagger项目,现在叫Springfox。通过在项目中引入Springfox ,即可非常简单快捷的使用Swagger。


(2)SpringBoot集成Swagger

  • 引入依赖,在heima-leadnews-model模块中引入该依赖xml <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> </dependency>

只需要在heima-leadnews-common中进行配置即可,因为其他微服务工程都直接或间接依赖即可。

  • 在heima-leadnews-admin工程的config包中添加一个配置类
  1. package com.heima.admin.config;
  2. import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
  3. import org.springframework.context.annotation.Bean;
  4. import org.springframework.context.annotation.Configuration;
  5. import springfox.documentation.builders.ApiInfoBuilder;
  6. import springfox.documentation.builders.PathSelectors;
  7. import springfox.documentation.builders.RequestHandlerSelectors;
  8. import springfox.documentation.service.ApiInfo;
  9. import springfox.documentation.service.Contact;
  10. import springfox.documentation.spi.DocumentationType;
  11. import springfox.documentation.spring.web.plugins.Docket;
  12. import springfox.documentation.swagger2.annotations.EnableSwagger2;
  13. @Configuration
  14. @EnableSwagger2
  15. public class SwaggerConfiguration {
  16. @Bean
  17. public Docket buildDocket() {
  18. return new Docket(DocumentationType.SWAGGER_2)
  19. .apiInfo(buildApiInfo())
  20. .select()
  21. // 要扫描的API(Controller)基础包
  22. .apis(RequestHandlerSelectors.basePackage("com.heima"))
  23. .paths(PathSelectors.any())
  24. .build();
  25. }
  26. private ApiInfo buildApiInfo() {
  27. Contact contact = new Contact("黑马程序员","","");
  28. return new ApiInfoBuilder()
  29. .title("黑马头条-平台管理API文档")
  30. .description("平台管理服务api")
  31. .contact(contact)
  32. .version("1.0.0").build();
  33. }
  34. }

(3)Swagger常用注解

在Java类中添加Swagger的注解即可生成Swagger接口文档,常用Swagger注解如下:

@Api:修饰整个类,描述Controller的作用 @ApiOperation:描述一个类的一个方法,或者说一个接口 @ApiParam:单个参数的描述信息

@ApiModel:用对象来接收参数

@ApiModelProperty:用对象接收参数时,描述对象的一个字段

@ApiResponse:HTTP响应其中1个描述

@ApiResponses:HTTP响应整体描述

@ApiIgnore:使用该注解忽略这个API

@ApiError :发生错误返回的信息

@ApiImplicitParam:一个请求参数

@ApiImplicitParams:多个请求参数的描述信息

@ApiImplicitParam属性:

属性 取值 作用
paramType 查询参数类型
path 以地址的形式提交数据
query 直接跟参数完成自动映射赋值
body 以流的形式提交 仅支持POST
header 参数在request headers 里边提交
form 以form表单的形式提交 仅支持POST
dataType 参数的数据类型 只作为标志说明,并没有实际验证
Long
String
name 接收参数名
value 接收参数的意义描述
required 参数是否必填
true 必填
false 非必填
defaultValue 默认值

我们在AdChannelControllerApi中添加Swagger注解,代码如下所示:

  1. @Api(value = "频道管理", tags = "channel", description = "频道管理API")
  2. public interface AdChannelControllerApi {
  3. /**
  4. * 根据名称分页查询频道列表
  5. * @param dto
  6. * @return
  7. */
  8. @ApiOperation("频道分页列表查询")
  9. public ResponseResult findByNameAndPage(ChannelDto dto);
  10. }

ChannelDto

  1. @Data
  2. public class ChannelDto extends PageRequestDto {
  3. /**
  4. * 频道名称
  5. */
  6. @ApiModelProperty("频道名称")
  7. private String name;
  8. }

PageRequestDto

  1. @Data
  2. @Slf4j
  3. public class PageRequestDto {
  4. @ApiModelProperty(value="当前页",required = true)
  5. protected Integer size;
  6. @ApiModelProperty(value="每页显示条数",required = true)
  7. protected Integer page;
  8. public void checkParam() {
  9. if (this.page == null || this.page < 0) {
  10. setPage(1);
  11. }
  12. if (this.size == null || this.size < 0 || this.size > 100) {
  13. setSize(10);
  14. }
  15. }
  16. }

启动admin微服务,访问地址:http://localhost:9001/swagger-ui.html
1591804376739.png

查询:
1591804451763.png

先点击Try it out 输入参数,然后点击Execute,结果如下:
1591804505819.png

7.4.3 knife4j

(1)简介

knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案,前身是swagger-bootstrap-ui,取名kni4j是希望它能像一把匕首一样小巧,轻量,并且功能强悍!

gitee地址:https://gitee.com/xiaoym/knife4j

官方文档:https://doc.xiaominfo.com/

效果演示:http://knife4j.xiaominfo.com/doc.html

(2)核心功能

该UI增强包主要包括两大核心功能:文档说明 和 在线调试

  • 文档说明:根据Swagger的规范说明,详细列出接口文档的说明,包括接口地址、类型、请求示例、请求参数、响应示例、响应参数、响应码等信息,使用swagger-bootstrap-ui能根据该文档说明,对该接口的使用情况一目了然。
  • 在线调试:提供在线接口联调的强大功能,自动解析当前接口参数,同时包含表单验证,调用参数可返回接口响应内容、headers、Curl请求命令实例、响应时间、响应状态码等信息,帮助开发者在线调试,而不必通过其他测试工具测试接口是否正确,简介、强大。
  • 个性化配置:通过个性化ui配置项,可自定义UI的相关显示信息
  • 离线文档:根据标准规范,生成的在线markdown离线文档,开发者可以进行拷贝生成markdown接口文档,通过其他第三方markdown转换工具转换成html或pdf,这样也可以放弃swagger2markdown组件
  • 接口排序:自1.8.5后,ui支持了接口排序功能,例如一个注册功能主要包含了多个步骤,可以根据swagger-bootstrap-ui提供的接口排序规则实现接口的排序,step化接口操作,方便其他开发者进行接口对接


(3)快速集成

  • 在heima-leadnews-common模块中的pom.xml文件中引入knife4j的依赖,如下:
  1. <dependency>
  2. <groupId>com.github.xiaoymin</groupId>
  3. <artifactId>knife4j-spring-boot-starter</artifactId>
  4. </dependency>
  • 创建Swagger配置文件


在heima-leadnews-common模块中新建配置类

在之前新建Swagger的配置类SwaggerConfiguration.java中,创建springfox提供的Docket分组对象就是添加@EnableKnife4j、@Import(BeanValidatorPluginsConfiguration.class) 两个注解,代码如下:

  1. package com.heima.common.knife4j;
  2. import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
  3. import org.springframework.context.annotation.Bean;
  4. import org.springframework.context.annotation.Configuration;
  5. import org.springframework.context.annotation.Import;
  6. import springfox.bean.validators.configuration.BeanValidatorPluginsConfiguration;
  7. import springfox.documentation.builders.ApiInfoBuilder;
  8. import springfox.documentation.builders.PathSelectors;
  9. import springfox.documentation.builders.RequestHandlerSelectors;
  10. import springfox.documentation.service.ApiInfo;
  11. import springfox.documentation.spi.DocumentationType;
  12. import springfox.documentation.spring.web.plugins.Docket;
  13. import springfox.documentation.swagger2.annotations.EnableSwagger2;
  14. @Configuration
  15. @EnableSwagger2
  16. @EnableKnife4j
  17. @Import(BeanValidatorPluginsConfiguration.class)
  18. public class Swagger2Configuration {
  19. @Bean(value = "defaultApi2")
  20. public Docket defaultApi2() {
  21. Docket docket=new Docket(DocumentationType.SWAGGER_2)
  22. .apiInfo(apiInfo())
  23. //分组名称
  24. .groupName("1.0")
  25. .select()
  26. //这里指定Controller扫描包路径
  27. .apis(RequestHandlerSelectors.basePackage("com.heima"))
  28. .paths(PathSelectors.any())
  29. .build();
  30. return docket;
  31. }
  32. private ApiInfo apiInfo() {
  33. return new ApiInfoBuilder()
  34. .title("黑马头条API文档")
  35. .description("黑马头条API文档")
  36. .version("1.0")
  37. .build();
  38. }
  39. }

以上有两个注解需要特别说明,如下表:

注解 说明
@EnableSwagger2 该注解是Springfox-swagger框架提供的使用Swagger注解,该注解必须加
@EnableKnife4j 该注解是knife4j提供的增强注解,Ui提供了例如动态参数、参数过滤、接口排序等增强功能,如果你想使用这些增强功能就必须加该注解,否则可以不用加
  • 访问
    在heima-leadnews-admin中开启配置
    在config包下新建类KnifeConfigjava @Configuration @ComponentScan("com.heima.common.knife4j") public class KnifeConfig { }

  • 访问


在浏览器输入地址:http://host:port/doc.html
1591805143637.png

查询频道列表:
1591805201556.png

7.5 频道新增

7.5.1接口定义

在AdChannelControllerApi接口中新增save方法

  1. /**
  2. * 新增
  3. * @param channel
  4. * @return
  5. */
  6. public ResponseResult save(AdChannel channel);

7.5.2 业务层

在AdChannelService中新增方法

  1. /**
  2. * 新增
  3. * @param channel
  4. * @return
  5. */
  6. public ResponseResult insert(AdChannel channel);

实现类:

  1. @Override
  2. public ResponseResult insert(AdChannel adChannel) {
  3. //1.检查参数
  4. if(null == adChannel){
  5. return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
  6. }
  7. //2.保存
  8. adChannel.setCreatedTime(new Date());
  9. save(adChannel);
  10. return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
  11. }

7.5.3 控制层

  1. @Override
  2. @PostMapping("/save")
  3. public ResponseResult save(@RequestBody AdChannel channel) {
  4. return channelService.insert(channel);
  5. }

7.5.4 测试

1584770964630.png

7.6 频道修改&频道有效无效设置

需求说明
1584771075455.png
1584771095093.png

其中设置状态(有效和无效)和弹窗修改都属于同一个修改即可

7.6.1 接口定义

在AdChannelControllerApi接口中新增update方法

  1. /**
  2. * 修改
  3. * @param adChannel
  4. * @return
  5. */
  6. public ResponseResult update(AdChannel adChannel);

7.6.2 业务层

AdChannelService中定义修改方法

  1. /**
  2. * 修改
  3. * @param adChannel
  4. * @return
  5. */
  6. public ResponseResult update(AdChannel adChannel);

实现类:

  1. @Override
  2. public ResponseResult update(AdChannel adChannel) {
  3. //1.检查参数
  4. if(null == adChannel || adChannel.getId()==null){
  5. return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
  6. }
  7. //2.修改
  8. updateById(adChannel);
  9. return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
  10. }

7.6.3 控制层

  1. @Override
  2. @PostMapping("/update")
  3. public ResponseResult update(@RequestBody AdChannel adChannel) {
  4. return channelService.update(adChannel);
  5. }

7.6.4 测试

1584771925178.png

7.7 频道删除

1584772041676.png

如果当前状态为有效则不能删除

7.7.1 接口定义

在AdChannelControllerApi接口中新增deleteById方法,接收ChannelDto中的id参数

  1. /**
  2. * 删除
  3. * @param id
  4. * @return
  5. */
  6. public ResponseResult deleteById(Integer id);

7.7.2 业务层

AdChannelService中新增deleteById方法

  1. /**
  2. * 删除
  3. * @param id
  4. * @return
  5. */
  6. public ResponseResult deleteById(Integer id);

实现类

  1. @Override
  2. public ResponseResult deleteById(Integer id) {
  3. //1.检查参数
  4. if(id == null){
  5. return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
  6. }
  7. //2.判断当前频道是否存在 和 是否有效
  8. AdChannel adChannel = getById(id);
  9. if(adChannel==null){
  10. return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
  11. }
  12. if(adChannel.getStatus()){
  13. return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID,"频道有效不能删除");
  14. }
  15. // int i = 10/0;
  16. //3.删除频道
  17. removeById(id);
  18. return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
  19. }

7.7.3 控制层

  1. @Override
  2. @GetMapping("/del/{id}")
  3. public ResponseResult deleteById(@PathVariable("id") Integer id) {
  4. return channelService.deleteById(id);
  5. }