学习目标:

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

    1 项目介绍

    1.1项目背景

    1. 随着智能手机的普及,人们更加习惯于通过手机来看新闻。由于生活节奏的加快,很多人只能利用碎片时间来获取信息,因此,对于移动资讯客户端的需求也越来越高。黑马头条项目正是在这样背景下开发出来。黑马头条项目采用当下火热的微服务+大数据技术架构实现。本项目主要着手于获取最新最热新闻资讯,通过大数据分析用户喜好精确推送咨询新闻<br />![Fxq4vBDTpD.png](https://cdn.nlark.com/yuque/0/2021/png/12460132/1615432037799-3bedb1fc-1c79-41e6-8bba-a2f89040ceda.png)

    1.2 项目概述

    黑马头条项目是对在线教育平台业务进行大数据统计分析的系统。碎片化、切换频繁、社交化和个性化现如今成为人们阅读行为的标签。黑马头条对海量信息进行搜集,通过系统计算分类,分析用户的兴趣进行推送从而满足用户的需求。
    WzSp62AXUW.png

    1.3 项目术语定义

  • 项目:泛指黑马头条整个项目或某一项目模块

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

2 需求说明

2.1 功能架构图

m27zDaNdKM.png

2.2 APP主要功能大纲

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

2.3 自媒体端功能大纲

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

2.4 平台管理端功能大纲

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

2.5 其它需求

uj3rOwwJtt.png

2.6 交互需求

ug4IqKwprl.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图设计

    TDjuOgb9AJ.png
    er图设计划分出了9个库,各个库主要解决的是某一个特定的业务。
    数据库设计规范,详见资料文件夹下《黑马头条-数据库规范设计说明书.md》文件。
    PowerDesinger工具使用,详见资料文件夹下’powerdesinger的基本使用’文件夹里的《powerdesinger的基本使用》文件。

    4.2 分库设计

    1. 黑马头条项目采用的分库分表设计,因为业务比较复杂,后期的访问量巨大,为了分摊数据库的压力,整个项目用的不只是一个数据库。其中核心库有**5**个,每一个数据库解决的是一个业务点,非常接近与实际项目设计。<br />![oeaD02AwXI.png](https://cdn.nlark.com/yuque/0/2021/png/12460132/1615432039894-3f828407-4ef3-4e93-8463-82f585005c3f.png)
  • AppInfo app信息库,主要存储用户信息,文章信息,用户动态,用户评论,用户认证等信息

  • Behavior 用户行为库,主要存储用户行为,包括用户的转发,点赞,评论行为等
  • WeMedia 多媒体库,主要存储多媒体人图文数据统计,账号信息,粉丝相关信息等。
  • Crawlers 爬虫库,主要存储从网络上爬取的文章信息等。
  • Admin 后台管理库,主要存储后台管理员的信息。

    4.3 核心数据流转图

    1585930399634.png

说明:整个项目其核心数据为文章信息,上图主要说明文章的数据流转

1 爬虫系统从外网采集数据后入爬虫库,即爬虫库中保存了一份采集的文章信息

2 自媒体人可以通过发布文章后首先进入自媒体库

3 爬虫文章和自媒体文章最后都要经过审核成功后入appinfo库,这里面的文章信息,最终是要给app端用户所查看

4 在app端用户查看的时候,需要记录用户的一些行为,如转发、评论、点赞等需要入用户行为库

4.4 冗余设计

  1. 黑马头条项目全部采用逻辑关联,没有采用主外键约束。也是方便数据源冗余,尽可能少的使用多表关联查询。冗余是为了效率,减少join。单表查询比关联查询速度要快。某个访问频繁的字段可以冗余存放在两张表里,不用关联了。<br /> 如查询一个订单表需要查询该条订单的用户名称,就必须join另外用户表,如果业务表很大,那么就会查询的很慢,这个时候我们就可以使用冗余来解决这个问题,在新建订单的同时不仅仅需要把用户ID存储,同时也需要存储用户的名称,这样我们在查询订单表的时候就不需要去join另外用户表,也能查询出该条订单的用户名称。这样的冗余可以直接的提高查询效率,单表更快。<br />![hO3srDpmEu.png](https://cdn.nlark.com/yuque/0/2021/png/12460132/1615432040050-ba9938e3-7d93-46f8-a9db-14e61e250e34.png)

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开发工具配置

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

3YEq4dLjK8.png

  • 设置项目编码格式

HLzi1iB3Gz.png

5.2.3 后端初始项目导入

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

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

6.1 什么是前后端开发

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

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

6.1.1前后端分离开发流程

DJo52PDNiT.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的时候在包名下加一个版本号,如下

  1. com.heima.article.controller.v1
  • 在访问具体的接口方法的url映射的时候也应该加上版本说明,如下:
  1. @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. }

6.4 通用环境说明

6.4.1 多环境切换

在每一个微服务的工程中的根目录下创建三个文件,方便各个环境的切换
(1)maven_dev.properties
定义开发环境的配置
(2)maven_prod.properties
定义生产环境的配置
(3)maven_test.properties
定义测试环境的配置,开发阶段使用这个测试环境
默认加载的环境为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.api.admin.ChannelControllerApi

  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,并发送请求

J7nAHcO9wc.png

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

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

7.4.2 Swagger介绍

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

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

(2)SpringBoot集成Swagger

  • 引入依赖,在heima-leadnews-model模块中引入该依赖
  1. <dependency>
  2. <groupId>io.springfox</groupId>
  3. <artifactId>springfox-swagger2</artifactId>
  4. </dependency>
  5. <dependency>
  6. <groupId>io.springfox</groupId>
  7. <artifactId>springfox-swagger-ui</artifactId>
  8. </dependency>

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

  • 在heima-leadnews-admin工程的config包中添加一个配置类 ``` package com.heima.admin.config;

import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.ApiInfo; import springfox.documentation.service.Contact; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2;

@Configuration @EnableSwagger2 public class SwaggerConfiguration {

@Bean public Docket buildDocket() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(buildApiInfo()) .select() // 要扫描的API(Controller)基础包 .apis(RequestHandlerSelectors.basePackage(“com.heima”)) .paths(PathSelectors.any()) .build(); }

private ApiInfo buildApiInfo() { Contact contact = new Contact(“黑马程序员”,””,””); return new ApiInfoBuilder() .title(“黑马头条-平台管理API文档”) .description(“平台管理服务api”) .contact(contact) .version(“1.0.0”).build(); } }

  1. 3Swagger常用注解<br />在Java类中添加Swagger的注解即可生成Swagger接口文档,常用Swagger注解如下:<br />@Api:修饰整个类,描述Controller的作用 @ApiOperation:描述一个类的一个方法,或者说一个接口 @ApiParam:单个参数的描述信息<br />@ApiModel:用对象来接收参数<br />@ApiModelProperty:用对象接收参数时,描述对象的一个字段<br />@ApiResponseHTTP响应其中1个描述<br />@ApiResponsesHTTP响应整体描述<br />@ApiIgnore:使用该注解忽略这个API<br />[ApiError ](/ApiError ) :发生错误返回的信息 <br />@ApiImplicitParam:一个请求参数<br />@ApiImplicitParams:多个请求参数的描述信息<br />@ApiImplicitParam属性:
  2. |
  3. 属性
  4. | 取值
  5. | 作用
  6. |
  7. | --- | --- | --- |
  8. |
  9. paramType
  10. |
  11. | 查询参数类型
  12. |
  13. |
  14. | path
  15. | 以地址的形式提交数据
  16. |
  17. |
  18. | query
  19. | 直接跟参数完成自动映射赋值
  20. |
  21. |
  22. | body
  23. | 以流的形式提交 仅支持POST
  24. |
  25. |
  26. | header
  27. | 参数在request headers 里边提交
  28. |
  29. |
  30. | form
  31. | form表单的形式提交 仅支持POST
  32. |
  33. |
  34. dataType
  35. |
  36. | 参数的数据类型 只作为标志说明,并没有实际验证
  37. |
  38. |
  39. | Long
  40. |
  41. |
  42. |
  43. | String
  44. |
  45. |
  46. |
  47. name
  48. |
  49. | 接收参数名
  50. |
  51. |
  52. value
  53. |
  54. | 接收参数的意义描述
  55. |
  56. |
  57. required
  58. |
  59. | 参数是否必填
  60. |
  61. |
  62. | true
  63. | 必填
  64. |
  65. |
  66. | false
  67. | 非必填
  68. |
  69. |
  70. defaultValue
  71. |
  72. | 默认值
  73. |
  74. 我们在AdChannelControllerApi中添加Swagger注解,代码如下所示:

@Api(value = “频道管理”, tags = “channel”, description = “频道管理API”) public interface AdChannelControllerApi {

  1. /**
  2. * 根据名称分页查询频道列表
  3. * @param dto
  4. * @return
  5. */
  6. @ApiOperation("频道分页列表查询")
  7. public ResponseResult findByNameAndPage(ChannelDto dto);

}

  1. ChannelDto

@Data public class ChannelDto extends PageRequestDto {

  1. /**
  2. * 频道名称
  3. */
  4. @ApiModelProperty("频道名称")
  5. private String name;

}

  1. PageRequestDto

@Data @Slf4j public class PageRequestDto {

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

}

  1. 启动admin微服务,访问地址:[http://localhost:9001/swagger-ui.html](http:_localhost:9001_swagger-ui)<br />![1591804376739.png](https://cdn.nlark.com/yuque/0/2021/png/12460132/1615432041934-384bdce3-2ee5-479a-9a34-a1d72f2523e7.png)<br />查询:<br />![1591804451763.png](https://cdn.nlark.com/yuque/0/2021/png/12460132/1615432042003-de2f463f-5b48-4755-b2ce-3b411b549d1d.png)<br />先点击**Try it out** 输入参数,然后点击**Execute**,结果如下:<br />![1591804505819.png](https://cdn.nlark.com/yuque/0/2021/png/12460132/1615432042069-a1e0c2e9-7409-4006-94ac-e9d028f2e694.png)
  2. #### 7.4.3 knife4j
  3. (1)简介<br />knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案,前身是swagger-bootstrap-ui,取名kni4j是希望它能像一把匕首一样小巧,轻量,并且功能强悍!<br />gitee地址:[https://gitee.com/xiaoym/knife4j](https://gitee.com/xiaoym/knife4j)<br />官方文档:[https://doc.xiaominfo.com/](https://doc.xiaominfo.com/)<br />效果演示:[http://knife4j.xiaominfo.com/doc.html](http:_knife4j.xiaominfo.com_doc)<br />(2)核心功能<br />该UI增强包主要包括两大核心功能:文档说明 和 在线调试
  4. - 文档说明:根据Swagger的规范说明,详细列出接口文档的说明,包括接口地址、类型、请求示例、请求参数、响应示例、响应参数、响应码等信息,使用swagger-bootstrap-ui能根据该文档说明,对该接口的使用情况一目了然。
  5. - 在线调试:提供在线接口联调的强大功能,自动解析当前接口参数,同时包含表单验证,调用参数可返回接口响应内容、headersCurl请求命令实例、响应时间、响应状态码等信息,帮助开发者在线调试,而不必通过其他测试工具测试接口是否正确,简介、强大。
  6. - 个性化配置:通过个性化ui配置项,可自定义UI的相关显示信息
  7. - 离线文档:根据标准规范,生成的在线markdown离线文档,开发者可以进行拷贝生成markdown接口文档,通过其他第三方markdown转换工具转换成htmlpdf,这样也可以放弃swagger2markdown组件
  8. - 接口排序:自1.8.5后,ui支持了接口排序功能,例如一个注册功能主要包含了多个步骤,可以根据swagger-bootstrap-ui提供的接口排序规则实现接口的排序,step化接口操作,方便其他开发者进行接口对接
  9. (3)快速集成
  10. - heima-leadnews-common模块中的`pom.xml`文件中引入`knife4j`的依赖,如下:
com.github.xiaoymin knife4j-spring-boot-starter
  1. - 创建Swagger配置文件
  2. heima-leadnews-common模块中新建配置类<br />新建Swagger的配置文件`SwaggerConfiguration.java`文件,创建springfox提供的Docket分组对象,代码如下:

package com.heima.common.knife4j;

import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import springfox.bean.validators.configuration.BeanValidatorPluginsConfiguration; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.ApiInfo; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2;

@Configuration @EnableSwagger2 @EnableKnife4j @Import(BeanValidatorPluginsConfiguration.class) public class Swagger2Configuration {

  1. @Bean(value = "defaultApi2")
  2. public Docket defaultApi2() {
  3. Docket docket=new Docket(DocumentationType.SWAGGER_2)
  4. .apiInfo(apiInfo())
  5. //分组名称
  6. .groupName("1.0")
  7. .select()
  8. //这里指定Controller扫描包路径
  9. .apis(RequestHandlerSelectors.basePackage("com.heima"))
  10. .paths(PathSelectors.any())
  11. .build();
  12. return docket;
  13. }
  14. private ApiInfo apiInfo() {
  15. return new ApiInfoBuilder()
  16. .title("黑马头条API文档")
  17. .description("黑马头条API文档")
  18. .version("1.0")
  19. .build();
  20. }

}

  1. 以上有两个注解需要特别说明,如下表:
  2. |
  3. 注解
  4. | 说明
  5. |
  6. | --- | --- |
  7. |
  8. `@EnableSwagger2`
  9. | 该注解是Springfox-swagger框架提供的使用Swagger注解,该注解必须加
  10. |
  11. |
  12. `@EnableKnife4j`
  13. | 该注解是`knife4j`提供的增强注解,Ui提供了例如动态参数、参数过滤、接口排序等增强功能,如果你想使用这些增强功能就必须加该注解,否则可以不用加
  14. |
  15. -
  16. 访问
  17. <br />在heima-leadnews-admin中开启配置
  18. <br />在config包下新建类KnifeConfig

@Configuration @ComponentScan(“com.heima.common.knife4j”) public class KnifeConfig { }

  1. -
  2. 访问
  3. 在浏览器输入地址:`http://host:port/doc.html`<br />![1591805143637.png](https://cdn.nlark.com/yuque/0/2021/png/12460132/1615432042152-98a02ac0-76ab-4323-9e9d-c2af70c2c028.png)<br />查询频道列表:<br />![1591805201556.png](https://cdn.nlark.com/yuque/0/2021/png/12460132/1615432042663-0a17c407-3fe8-4a2a-92ed-c4f8c2554d8d.png)
  4. ### 7.5 频道新增
  5. #### 7.5.1接口定义
  6. AdChannelControllerApi接口中新增save方法

/**

  1. * 新增
  2. * @param channel
  3. * @return
  4. */

public ResponseResult save(AdChannel channel);

  1. #### 7.5.2 业务层
  2. AdChannelService中新增方法

/**

  1. * 新增
  2. * @param channel
  3. * @return
  4. */

public ResponseResult insert(AdChannel channel);

  1. 实现类:

@Override public ResponseResult insert(AdChannel adChannel) { //1.检查参数 if(null == adChannel){ return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID); }

  1. //2.保存
  2. adChannel.setCreatedTime(new Date());
  3. save(adChannel);
  4. return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);

}

  1. #### 7.5.3 控制层

@Override @PostMapping(“/save”) public ResponseResult save(@RequestBody AdChannel channel) { return channelService.insert(channel); }

  1. #### 7.5.4 测试
  2. ![1584770964630.png](https://cdn.nlark.com/yuque/0/2021/png/12460132/1615432042756-47e6e5b9-4caa-4a40-8d29-704587763161.png)
  3. ### 7.6 频道修改&频道有效无效设置
  4. 需求说明<br />![1584771075455.png](https://cdn.nlark.com/yuque/0/2021/png/12460132/1615432042847-310cb907-dc45-4c59-95e8-43e1dcdddbf1.png)<br />![1584771095093.png](https://cdn.nlark.com/yuque/0/2021/png/12460132/1615432042952-21c47750-0608-4a65-8017-bb7f9520c8de.png)<br />其中设置状态(有效和无效)和弹窗修改都属于同一个修改即可
  5. #### 7.6.1 接口定义
  6. AdChannelControllerApi接口中新增update方法

/**

  1. * 修改
  2. * @param adChannel
  3. * @return
  4. */

public ResponseResult update(AdChannel adChannel);

  1. #### 7.6.2 业务层
  2. AdChannelService中定义修改方法

/**

  1. * 修改
  2. * @param adChannel
  3. * @return
  4. */

public ResponseResult update(AdChannel adChannel);

  1. 实现类:

@Override public ResponseResult update(AdChannel adChannel) {

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

}

  1. #### 7.6.3 控制层

@Override @PostMapping(“/update”) public ResponseResult update(@RequestBody AdChannel adChannel) { return channelService.update(adChannel); }

  1. #### 7.6.4 测试
  2. ![1584771925178.png](https://cdn.nlark.com/yuque/0/2021/png/12460132/1615432043035-f9e869a7-ed3d-4ce2-bd2e-7205bc5529c3.png)
  3. ### 7.7 频道删除
  4. ![1584772041676.png](https://cdn.nlark.com/yuque/0/2021/png/12460132/1615432043114-0a132965-c44b-4bb6-82d6-a5dab2c09417.png)<br />如果当前状态为有效则不能删除
  5. #### 7.7.1 接口定义
  6. AdChannelControllerApi接口中新增deleteById方法,接收ChannelDto中的id参数

/**

  1. * 删除
  2. * @param id
  3. * @return
  4. */

public ResponseResult deleteById(Integer id);

  1. #### 7.7.2 业务层
  2. AdChannelService中新增deleteById方法

/**

  1. * 删除
  2. * @param id
  3. * @return
  4. */

public ResponseResult deleteById(Integer id);

  1. 实现类

@Override public ResponseResult deleteById(Integer id) { //1.检查参数 if(id == null){ return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID); } //2.判断当前频道是否存在 和 是否有效 AdChannel adChannel = getById(id); if(adChannel==null){ return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST); } if(adChannel.getStatus()){ return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID,”频道有效不能删除”); }

  1. // int i = 10/0;
  2. //3.删除频道
  3. removeById(id);
  4. return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);

}

  1. #### 7.7.3 控制层

@Override @GetMapping(“/del/{id}”) public ResponseResult deleteById(@PathVariable(“id”) Integer id) { return channelService.deleteById(id); }

```