当前位置:   article > 正文

黑马头条 SpringBoot+SpringCloud+ Nacos等企业级微服务架构项目_黑马头条项目

黑马头条项目

各位爷,完整项目gitee如下,求star

heima-leadnews-master: 《黑马头条》项目采用的是SpringBoot+springcloud当下最流行的微服务为项目架构,配合spring cloud alibaba nacos作为项目的注册和配置中心。新课程采用快速开发的模式,主要解决真实企业开发的一些应用场景。详情请看博客:https://blog.csdn.net/m0_67184231/article/details/131439819

01环境搭建、SpringCloud微服务(注册发现、服务调用、网关)

1)课程对比

2)项目概述

2.1)能让你收获什么

2.2)项目课程大纲

2.3)项目概述

随着智能手机的普及,人们更加习惯于通过手机来看新闻。由于生活节奏的加快,很多人只能利用碎片时间来获取信息,因此,对于移动资讯客户端的需求也越来越高。

黑马头条项目正是在这样背景下开发出来。黑马头条项目采用当下火热的微服务+大数据技术架构实现。本项目主要着手于获取最新最热新闻资讯,通过大数据分析用户喜好精确推送咨询新闻

2.4)项目术语

2.5)业务说明

项目演示地址:

平台管理与自媒体为PC端,用电脑浏览器打开即可。

其中app端为移动端,打开方式有两种:

  • 谷歌浏览器打开,调成移动端模式

  • 手机浏览器打开或扫描右侧二维码

3)技术栈

  • Spring-Cloud-Gateway : 微服务之前架设的网关服务,实现服务注册中的API请求路由,以及控制流速控制和熔断处理都是常用的架构手段,而这些功能Gateway天然支持

  • 运用Spring Boot快速开发框架,构建项目工程;并结合Spring Cloud全家桶技术,实现后端个人中心、自媒体、管理中心等微服务。

  • 运用Spring Cloud Alibaba Nacos作为项目中的注册中心和配置中心

  • 运用mybatis-plus作为持久层提升开发效率

  • 运用Kafka完成内部系统消息通知;与客户端系统消息通知;以及实时数据计算

  • 运用Redis缓存技术,实现热数据的计算,提升系统性能指标

  • 使用Mysql存储用户数据,以保证上层数据查询的高性

  • 使用Mongo存储用户热数据,以保证用户热数据高扩展和高性能指标

  • 使用FastDFS作为静态资源存储器,在其上实现热静态资源缓存、淘汰等功能

  • 运用Hbase技术,存储系统中的冷数据,保证系统数据的可靠性

  • 运用ES搜索技术,对冷数据、文章数据建立索引,以保证冷数据、文章查询性能

  • 运用AI技术,来完成系统自动化功能,以提升效率及节省成本。比如实名认证自动化

  • PMD&P3C : 静态代码扫描工具,在项目中扫描项目代码,检查异常点、优化点、代码规范等,为开发团队提供规范统一,提升项目代码质量

4)nacos环境搭建

4.1)虚拟机镜像准备

1)打开当天资料文件中的镜像,拷贝到一个地方,然后解压

2)解压后,双击ContOS7-hmtt.vmx文件,前提是电脑上已经安装了VMware

  1. 修改虚拟网络地址(NAT)

①,选中VMware中的编辑

②,选择虚拟网络编辑器

③,找到NAT网卡,把网段改为200(当前挂载的虚拟机已固定ip地址)

4)修改虚拟机的网络模式为NAT

5)启动虚拟机,用户名:root 密码:itcast,当前虚拟机的ip已手动固定(静态IP), 地址为:192.168.200.130

6)使用FinalShell客户端链接

4.2)nacos安装

①:docker拉取镜像

docker pull nacos/nacos-server:1.2.0

②:创建容器

docker run --env MODE=standalone --name nacos --restart=always  -d -p 8848:8848 nacos/nacos-server:1.2.0
  • MODE=standalone 单机版

  • --restart=always 开机启动

  • -p 8848:8848 映射端口

  • -d 创建一个守护式容器在后台运行

③:访问地址:http://192.168.200.130:8848/nacos

5)初始工程搭建

5.1)环境准备

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

  • JDK1.8

  • Intellij Idea

  • maven-3.6.1

  • Git

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

③:IDEA开发工具配置

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

④:设置项目编码格式

5.2)主体结构

6)登录

6.1)需求分析

  • 户点击开始使用

登录后的用户权限较大,可以查 看,也可以操作(点赞,关注,评论)

  • 用户点击不登录,先看看

游客只有查看的权限

6.2)表结构分析

关于app端用户相关的内容较多,可以单独设置一个库leadnews_user

表名称

说明

ap_user

APP用户信息表

ap_user_fan

APP用户粉丝信息表

ap_user_follow

APP用户关注信息表

ap_user_realname

APP实名认证信息表

从当前资料中找到对应数据库并导入到mysql中

登录需要用到的是ap_user表,表结构如下:

项目中的持久层使用的mybatis-plus,一般都使用mybais-plus逆向生成对应的实体类

app_user表对应的实体类如下:

  1. package com.heima.model.user.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. * APP用户信息表
  12. * </p>
  13. *
  14. * @author itheima
  15. */
  16. @Data
  17. @TableName("ap_user")
  18. public class ApUser implements Serializable {
  19. private static final long serialVersionUID = 1L;
  20. /**
  21. * 主键
  22. */
  23. @TableId(value = "id", type = IdType.AUTO)
  24. private Integer id;
  25. /**
  26. * 密码、通信等加密盐
  27. */
  28. @TableField("salt")
  29. private String salt;
  30. /**
  31. * 用户名
  32. */
  33. @TableField("name")
  34. private String name;
  35. /**
  36. * 密码,md5加密
  37. */
  38. @TableField("password")
  39. private String password;
  40. /**
  41. * 手机号
  42. */
  43. @TableField("phone")
  44. private String phone;
  45. /**
  46. * 头像
  47. */
  48. @TableField("image")
  49. private String image;
  50. /**
  51. * 0 男
  52. 1 女
  53. 2 未知
  54. */
  55. @TableField("sex")
  56. private Boolean sex;
  57. /**
  58. * 0 未
  59. 1 是
  60. */
  61. @TableField("is_certification")
  62. private Boolean certification;
  63. /**
  64. * 是否身份认证
  65. */
  66. @TableField("is_identity_authentication")
  67. private Boolean identityAuthentication;
  68. /**
  69. * 0正常
  70. 1锁定
  71. */
  72. @TableField("status")
  73. private Boolean status;
  74. /**
  75. * 0 普通用户
  76. 1 自媒体人
  77. 2 大V
  78. */
  79. @TableField("flag")
  80. private Short flag;
  81. /**
  82. * 注册时间
  83. */
  84. @TableField("created_time")
  85. private Date createdTime;
  86. }

手动加密(md5+随机字符串)

md5是不可逆加密,md5相同的密码每次加密都一样,不太安全。在md5的基础上手动加盐(salt)处理

注册->生成盐

登录->使用盐来配合验证

6.3)思路分析

1,用户输入了用户名和密码进行登录,校验成功后返回jwt(基于当前用户的id生成)

2,用户游客登录,生成jwt返回(基于默认值0生成)

6.4)运营端微服务搭建

在heima-leadnews-service下创建工程heima-leadnews-user

引导类

  1. package com.heima.user;
  2. import org.mybatis.spring.annotation.MapperScan;
  3. import org.springframework.boot.SpringApplication;
  4. import org.springframework.boot.autoconfigure.SpringBootApplication;
  5. import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
  6. @SpringBootApplication
  7. @EnableDiscoveryClient//集成当前注册中心
  8. @MapperScan("com.heima.user.mapper")//扫描mapper
  9. public class UserApplication {
  10. public static void main(String[] args) {
  11. SpringApplication.run(UserApplication.class,args);
  12. }
  13. }

bootstrap.yml

  1. server:
  2. port: 51801
  3. spring:
  4. application:
  5. name: leadnews-user
  6. cloud:
  7. nacos:
  8. discovery:
  9. server-addr: 192.168.200.130:8848
  10. config:
  11. server-addr: 192.168.200.130:8848
  12. file-extension: yml

在nacos中创建配置文件

  1. spring:
  2. datasource:
  3. driver-class-name: com.mysql.jdbc.Driver
  4. url: jdbc:mysql://localhost:3306/leadnews_user?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
  5. username: root
  6. password: root
  7. # 设置Mapper接口所对应的XML文件位置,如果你在Mapper接口中有自定义方法,需要进行该配置
  8. mybatis-plus:
  9. mapper-locations: classpath*:mapper/*.xml
  10. # 设置别名包扫描路径,通过该属性可以给包中的类注册别名
  11. type-aliases-package: com.heima.model.user.pojos
报错:
The last packet successfully received from the server was 523 milliseconds ago. The last packet sent successfully to the server was 518 milliseconds ago.
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at com.mysql.jdbc.Util.handleNewInstance(Util.java:425)

可以把nacos的mysqlurl改成下面

url: jdbc:mysql://localhost:3306/leadnews_user?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false

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>

6.4)登录功能实现

①:接口定义

  1. @RestController
  2. @RequestMapping("/api/v1/login")
  3. public class ApUserLoginController {
  4. @PostMapping("/login_auth")
  5. public ResponseResult login(@RequestBody LoginDto dto) {
  6. return null;
  7. }
  8. }

②:持久层mapper

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

③:业务层service

  1. package com.heima.user.service;
  2. import com.baomidou.mybatisplus.extension.service.IService;
  3. import com.heima.model.common.dtos.ResponseResult;
  4. import com.heima.model.user.dtos.LoginDto;
  5. import com.heima.model.user.pojos.ApUser;
  6. public interface ApUserService extends IService<ApUser>{
  7. /**
  8. * app端登录
  9. * @param dto
  10. * @return
  11. */
  12. public ResponseResult login(LoginDto dto);
  13. }

实现类:

  1. package com.heima.user.service.impl;
  2. import com.baomidou.mybatisplus.core.toolkit.Wrappers;
  3. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
  4. import com.heima.model.common.dtos.ResponseResult;
  5. import com.heima.model.common.enums.AppHttpCodeEnum;
  6. import com.heima.model.user.dtos.LoginDto;
  7. import com.heima.model.user.pojos.ApUser;
  8. import com.heima.user.mapper.ApUserMapper;
  9. import com.heima.user.service.ApUserService;
  10. import com.heima.utils.common.AppJwtUtil;
  11. import org.apache.commons.lang3.StringUtils;
  12. import org.springframework.stereotype.Service;
  13. import org.springframework.util.DigestUtils;
  14. import java.util.HashMap;
  15. import java.util.Map;
  16. @Service
  17. public class ApUserServiceImpl extends ServiceImpl<ApUserMapper, ApUser> implements ApUserService {
  18. @Override
  19. public ResponseResult login(LoginDto dto) {
  20. //1.正常登录(手机号+密码登录)
  21. if (!StringUtils.isBlank(dto.getPhone()) && !StringUtils.isBlank(dto.getPassword())) {
  22. //1.1查询用户
  23. ApUser apUser = getOne(Wrappers.<ApUser>lambdaQuery().eq(ApUser::getPhone, dto.getPhone()));
  24. if (apUser == null) {
  25. return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST,"用户不存在");
  26. }
  27. //1.2 比对密码
  28. String salt = apUser.getSalt();
  29. String pswd = dto.getPassword();
  30. pswd = DigestUtils.md5DigestAsHex((pswd + salt).getBytes());
  31. if (!pswd.equals(apUser.getPassword())) {
  32. return ResponseResult.errorResult(AppHttpCodeEnum.LOGIN_PASSWORD_ERROR);
  33. }
  34. //1.3 返回数据 jwt
  35. Map<String, Object> map = new HashMap<>();
  36. map.put("token", AppJwtUtil.getToken(apUser.getId().longValue()));
  37.         //这两个值 置空 再返回对象
  38. apUser.setSalt("");
  39. apUser.setPassword("");
  40. map.put("user", apUser);
  41. return ResponseResult.okResult(map);
  42. } else {
  43. //2.游客 同样返回token id = 0
  44. Map<String, Object> map = new HashMap<>();
  45. map.put("token", AppJwtUtil.getToken(0l));
  46. return ResponseResult.okResult(map);
  47. }
  48. }
  49. }

④:控制层controller

  1. package com.heima.user.controller.v1;
  2. import com.heima.model.common.dtos.ResponseResult;
  3. import com.heima.model.user.dtos.LoginDto;
  4. import com.heima.user.service.ApUserService;
  5. import org.springframework.beans.factory.annotation.Autowired;
  6. import org.springframework.web.bind.annotation.PostMapping;
  7. import org.springframework.web.bind.annotation.RequestBody;
  8. import org.springframework.web.bind.annotation.RequestMapping;
  9. import org.springframework.web.bind.annotation.RestController;
  10. @RestController
  11. @RequestMapping("/api/v1/login")
  12. public class ApUserLoginController {
  13. @Autowired
  14. private ApUserService apUserService;
  15. @PostMapping("/login_auth")
  16. public ResponseResult login(@RequestBody LoginDto dto) {
  17. return apUserService.login(dto);
  18. }
  19. }

docker exec -it redis redis-cli这个进入服务器得redis界面 你再输入docker ps就能看到服务起来了;docker 中的redis没密码,要把nacos的密码去掉;

报错的兄弟们先创建redis容器a然后把nacos配置中redis密码那一行注掉就行了

docker run --name redis -p 6379:6379 -d redis

7)接口工具postman、swagger、knife4j

7.1)postman

Postman是一款功能强大的网页调试与发送网页HTTP请求的Chrome插件。postman被500万开发者和超100,000家公司用于每月访问1.3亿个API。

官方网址:Postman

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

通常的接口测试查看请求和响应,下面是登录请求的测试

http://localhost:51801/api/v1/login/login_auth

{"phone":"13511223456","password":"admin"}

7.2)swagger

(1)简介

Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务(API Documentation & Design Tools for Teams | Swagger)。 它的主要作用是:

  1. 使得前后端分离开发更加方便,有利于团队协作

  1. 接口的文档在线自动生成,降低后端开发人员编写接口文档的负担

  1. 功能测试

Spring已经将Swagger纳入自身的标准,建立了Spring-swagger项目,现在叫Springfox

通过在项目中引入Springfox ,即可非常简单快捷的使用Swagger。

(2)SpringBoot集成Swagger

  • 引入依赖,在heima-leadnews-model和heima-leadnews-common模块中引入该依赖

  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-common工程中添加一个配置类

新增:com.heima.common.swagger.SwaggerConfiguration

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

在heima-leadnews-common模块中的resources目录中新增以下目录和文件

文件:resources/META-INF/Spring.factories

  1. org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  2. com.heima.common.swagger.SwaggerConfiguration

(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

默认值

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

  1. @RestController
  2. @RequestMapping("/api/v1/login")
  3. @Api(value = "app端用户登录", tags = "ap_user", description = "app端用户登录API")
  4. public class ApUserLoginController {
  5. @Autowired
  6. private ApUserService apUserService;
  7. @PostMapping("/login_auth")
  8. @ApiOperation("用户登录")
  9. public ResponseResult login(@RequestBody LoginDto dto){
  10. return apUserService.login(dto);
  11. }
  12. }

LoginDto

  1. @Data
  2. public class LoginDto {
  3. /**
  4. * 手机号
  5. */
  6. @ApiModelProperty(value="手机号",required = true)
  7. private String phone;
  8. /**
  9. * 密码
  10. */
  11. @ApiModelProperty(value="密码",required = true)
  12. private String password;
  13. }

启动user微服务,访问地址:http://localhost:51801/swagger-ui.html

7.3)knife4j

(1)简介

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

gitee地址:knife4j: Knife4j是一个集Swagger2 和 OpenAPI3为一体的增强解决方案

官方文档:Knife4j · 集Swagger2及OpenAPI3为一体的增强解决方案. | Knife4j

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

(2)核心功能

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

  • 文档说明:根据Swagger的规范说明,详细列出接口文档的说明,包括接口地址、类型、请求示例、请求参数、响应示例、响应参数、响应码等信息,使用swagger-bootstrap-ui能根据该文档说明,对该接口的使用情况一目了然。

  • 在线调试:提供在线接口联调的强大功能,自动解析当前接口参数,同时包含表单验证,调用参数可返回接口响应内容、headers、Curl请求命令实例、响应时间、响应状态码等信息,帮助开发者在线调试,而不必通过其他测试工具测试接口是否正确,简介、强大。

  • 个性化配置(Swaager无):通过个性化ui配置项,可自定义UI的相关显示信息

  • 离线文档(Swaager无):根据标准规范,生成的在线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分组对象,代码如下:

  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提供了例如动态参数、参数过滤、接口排序等增强功能,如果你想使用这些增强功能就必须加该注解,否则可以不用加

  • 添加配置

在Spring.factories中新增配置

  1. org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  2. com.heima.common.swagger.Swagger2Configuration, \
  3. com.heima.common.swagger.SwaggerConfiguration
  • 访问

在浏览器输入地址:http://host:port/doc.html;http://localhost:51801/doc.html

8)网关

(1)在heima-leadnews-gateway导入以下依赖

pom文件

  1. <dependencies>
  2. <dependency>
  3. <groupId>org.springframework.cloud</groupId>
  4. <artifactId>spring-cloud-starter-gateway</artifactId>
  5. </dependency>
  6. <dependency>
  7. <groupId>com.alibaba.cloud</groupId>
  8. <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
  9. </dependency>
  10. <dependency>
  11. <groupId>com.alibaba.cloud</groupId>
  12. <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
  13. </dependency>
  14. <dependency>
  15. <groupId>io.jsonwebtoken</groupId>
  16. <artifactId>jjwt</artifactId>
  17. </dependency>
  18. </dependencies>

(2)在heima-leadnews-gateway下创建heima-leadnews-app-gateway微服务

引导类:

  1. package com.heima.app.gateway;
  2. import org.springframework.boot.SpringApplication;
  3. import org.springframework.boot.autoconfigure.SpringBootApplication;
  4. import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
  5. @SpringBootApplication
  6. @EnableDiscoveryClient //开启注册中心
  7. public class AppGatewayApplication {
  8. public static void main(String[] args) {
  9. SpringApplication.run(AppGatewayApplication.class,args);
  10. }
  11. }

bootstrap.yml

  1. server:
  2. port: 51601
  3. spring:
  4. application:
  5. name: leadnews-app-gateway
  6. cloud:
  7. nacos:
  8. discovery:
  9. server-addr: 192.168.200.130:8848
  10. config:
  11. server-addr: 192.168.200.130:8848
  12. file-extension: yml

在nacos的配置中心创建dataid为leadnews-app-gateway的yml配置

  1. spring:
  2. cloud:
  3. gateway:
  4. globalcors:
  5. add-to-simple-url-handler-mapping: true
  6. corsConfigurations:
  7. '[/**]':
  8. allowedHeaders: "*"
  9. allowedOrigins: "*"
  10. allowedMethods:
  11. - GET
  12. - POST
  13. - DELETE
  14. - PUT
  15. - OPTION
  16. routes:
  17. # 平台管理
  18. - id: user
  19. uri: lb://leadnews-user
  20. predicates:
  21. - Path=/user/**
  22. filters:
  23. - StripPrefix= 1

环境搭建完成以后,启动项目网关和用户两个服务,使用postman进行测试

请求地址:http://localhost:51601/user/api/v1/login/login_auth

1.3 全局过滤器实现jwt校验

思路分析:

  1. 用户进入网关开始登陆,网关过滤器进行判断,如果是登录,则路由到后台管理微服务进行登录

  1. 用户登录成功,后台管理微服务签发JWT TOKEN信息返回给用户

  1. 用户再次进入网关开始访问,网关过滤器接收用户携带的TOKEN

  1. 网关过滤器解析TOKEN ,判断是否有权限,如果有,则放行,如果没有则返回未认证错误

具体实现:

第一:

在认证过滤器中需要用到jwt的解析,所以需要把工具类拷贝一份到网关微服务

第二:

在网关微服务中新建全局过滤器:

  1. package com.heima.app.gateway.filter;
  2. import com.heima.app.gateway.util.AppJwtUtil;
  3. import io.jsonwebtoken.Claims;
  4. import lombok.extern.slf4j.Slf4j;
  5. import org.apache.commons.lang.StringUtils;
  6. import org.springframework.cloud.gateway.filter.GatewayFilterChain;
  7. import org.springframework.cloud.gateway.filter.GlobalFilter;
  8. import org.springframework.core.Ordered;
  9. import org.springframework.http.HttpStatus;
  10. import org.springframework.http.server.reactive.ServerHttpRequest;
  11. import org.springframework.http.server.reactive.ServerHttpResponse;
  12. import org.springframework.stereotype.Component;
  13. import org.springframework.web.server.ServerWebExchange;
  14. import reactor.core.publisher.Mono;
  15. @Component
  16. @Slf4j
  17. public class AuthorizeFilter implements Ordered, GlobalFilter {
  18. @Override
  19. public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
  20. //1.获取request和response对象
  21. ServerHttpRequest request = exchange.getRequest();
  22. ServerHttpResponse response = exchange.getResponse();
  23. //2.判断是否是登录
  24. if(request.getURI().getPath().contains("/login")){
  25. //放行
  26. return chain.filter(exchange);
  27. }
  28. //3.获取token
  29. String token = request.getHeaders().getFirst("token");
  30. //4.判断token是否存在
  31. if(StringUtils.isBlank(token)){
  32. response.setStatusCode(HttpStatus.UNAUTHORIZED);
  33. return response.setComplete();
  34. }
  35. //5.判断token是否有效
  36. try {
  37. Claims claimsBody = AppJwtUtil.getClaimsBody(token);
  38. //是否是过期
  39. int result = AppJwtUtil.verifyToken(claimsBody);
  40. if(result == 1 || result == 2){
  41. response.setStatusCode(HttpStatus.UNAUTHORIZED);
  42. return response.setComplete();
  43. }
  44. }catch (Exception e){
  45. e.printStackTrace();
  46. response.setStatusCode(HttpStatus.UNAUTHORIZED);
  47. return response.setComplete();
  48. }
  49. //6.放行
  50. return chain.filter(exchange);
  51. }
  52. /**
  53. * 优先级设置 值越小 优先级越高
  54. * @return
  55. */
  56. @Override
  57. public int getOrder() {
  58. return 0;
  59. }
  60. }

测试:

启动user服务,继续访问其他微服务,会提示需要认证才能访问,这个时候需要在heads中设置设置token才能正常访问。

9)前端集成

9.1)前端项目部署思路

通过nginx来进行配置,功能如下

  • 通过nginx的反向代理功能访问后台的网关资源

  • 通过nginx的静态服务器功能访问前端静态页面

9.2)配置nginx

①:解压资料文件夹中的压缩包nginx-1.18.0.zip

路径cmd,键入nginx,启动

②:解压资料文件夹中的前端项目app-web.zip

③:配置nginx.conf文件

在nginx安装的conf目录下新建一个文件夹leadnews.conf,在当前文件夹中新建heima-leadnews-app.conf文件

heima-leadnews-app.conf配置如下:

  1. upstream heima-app-gateway{
  2. server localhost:51601;
  3. }
  4. server {
  5. listen 8801;
  6. location / {
  7. root D:/workspace/app-web/;
  8. index index.html;
  9. }
  10. location ~/app/(.*) {
  11. proxy_pass http://heima-app-gateway/$1;
  12. proxy_set_header HOST $host; # 不改变源请求头的值
  13. proxy_pass_request_body on; #开启获取请求体
  14. proxy_pass_request_headers on; #开启获取请求头
  15. proxy_set_header X-Real-IP $remote_addr; # 记录真实发出请求的客户端IP
  16. proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; #记录代理信息
  17. }
  18. }

nginx.conf 把里面注释的内容和静态资源配置相关删除引入heima-leadnews-app.conf文件加载

  1. #user nobody;
  2. worker_processes 1;
  3. events {
  4. worker_connections 1024;
  5. }
  6. http {
  7. include mime.types;
  8. default_type application/octet-stream;
  9. sendfile on;
  10. keepalive_timeout 65;
  11. # 引入自定义配置文件
  12. include leadnews.conf/*.conf;
  13. }

④ :启动nginx

在nginx安装包中使用命令提示符打开,输入命令nginx启动项目

查看进程,检查nginx是否启动

重新加载配置文件:nginx -s reload

报错
使用windows版本的nginx启动时遇到(1113: No mapping for the Unicode character exists in the target multi-byte code page)这个错误

把nginx的版本升高了,依旧报错
后来查阅发现是因为解压的路径里面包含有中文的缘故,只要把解压后的文件剪切到没有包含中文的目录即可解决问题

在运行reload的时候报错:CreateFile() "D:\DevelopCode\JavaCode\Springcloud\leadnews\nginx-1.18.0/logs/nginx.pid" failed (2: The system cannot find the file specified)

加pid的文件,然后里面没东西运行失败,就start nginx,计算机自己在pid加了东西,成功reload!

打开8801 报错:500 Internal Server Error

这个错误通常表示在Windows操作系统上,Nginx无法识别路径中含有非英文字符的文件或文件夹。这可能是因为Nginx默认将其配置文件和日志文件等保存在UTF-8编码下,而Windows默认的编码方式是ANSI。
要解决这个问题,可以尝试以下方法:
将Nginx配置文件及相关路径修改为英文字符,避免包含非英文字符的路径。
修改Nginx的配置文件编码为 ANSI :在Nginx的配置文件中,找到 http 块,并在该块中添加以下指令:
plaintext复制代码http {
# ...
charset utf-8;
charset_types text/plain text/css application/javascript application/json text/javascript;
charset utf-8 off;
# ...
}
保存并重新启动Nginx服务。

⑤:打开前端项目进行测试 -- > http://localhost:8801

用谷歌浏览器打开,调试移动端模式进行访

02app端文章查看,静态化freemarker,分布式文件系统minIO

1)文章列表加载

1.1)需求分析

文章布局展示

1.2)表结构分析

ap_article 文章基本信息表

ap_article_config 文章配置表

ap_article_content 文章内容表

三张表关系分析

表的拆分—垂直分表

1.3)导入文章数据库

1.3.1)导入数据库

查看当天资料文件夹,在数据库连接工具中执行leadnews_article.sql

1.3.2)导入对应的实体类

ap_article文章表对应实体

  1. package com.heima.model.article.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("ap_article")
  18. public class ApArticle implements Serializable {
  19. @TableId(value = "id",type = IdType.ID_WORKER)
  20. private Long id;
  21. /**
  22. * 标题
  23. */
  24. private String title;
  25. /**
  26. * 作者id
  27. */
  28. @TableField("author_id")
  29. private Long authorId;
  30. /**
  31. * 作者名称
  32. */
  33. @TableField("author_name")
  34. private String authorName;
  35. /**
  36. * 频道id
  37. */
  38. @TableField("channel_id")
  39. private Integer channelId;
  40. /**
  41. * 频道名称
  42. */
  43. @TableField("channel_name")
  44. private String channelName;
  45. /**
  46. * 文章布局 0 无图文章 1 单图文章 2 多图文章
  47. */
  48. private Short layout;
  49. /**
  50. * 文章标记 0 普通文章 1 热点文章 2 置顶文章 3 精品文章 4 大V 文章
  51. */
  52. private Byte flag;
  53. /**
  54. * 文章封面图片 多张逗号分隔
  55. */
  56. private String images;
  57. /**
  58. * 标签
  59. */
  60. private String labels;
  61. /**
  62. * 点赞数量
  63. */
  64. private Integer likes;
  65. /**
  66. * 收藏数量
  67. */
  68. private Integer collection;
  69. /**
  70. * 评论数量
  71. */
  72. private Integer comment;
  73. /**
  74. * 阅读数量
  75. */
  76. private Integer views;
  77. /**
  78. * 省市
  79. */
  80. @TableField("province_id")
  81. private Integer provinceId;
  82. /**
  83. * 市区
  84. */
  85. @TableField("city_id")
  86. private Integer cityId;
  87. /**
  88. * 区县
  89. */
  90. @TableField("county_id")
  91. private Integer countyId;
  92. /**
  93. * 创建时间
  94. */
  95. @TableField("created_time")
  96. private Date createdTime;
  97. /**
  98. * 发布时间
  99. */
  100. @TableField("publish_time")
  101. private Date publishTime;
  102. /**
  103. * 同步状态
  104. */
  105. @TableField("sync_status")
  106. private Boolean syncStatus;
  107. /**
  108. * 来源
  109. */
  110. private Boolean origin;
  111. /**
  112. * 静态页面地址
  113. */
  114. @TableField("static_url")
  115. private String staticUrl;
  116. }

ap_article_config文章配置对应实体类

  1. package com.heima.model.article.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. /**
  9. * <p>
  10. * APP已发布文章配置表
  11. * </p>
  12. *
  13. * @author itheima
  14. */
  15. @Data
  16. @TableName("ap_article_config")
  17. public class ApArticleConfig implements Serializable {
  18. @TableId(value = "id",type = IdType.ID_WORKER)
  19. private Long id;
  20. /**
  21. * 文章id
  22. */
  23. @TableField("article_id")
  24. private Long articleId;
  25. /**
  26. * 是否可评论
  27. * true: 可以评论 1
  28. * false: 不可评论 0
  29. */
  30. @TableField("is_comment")
  31. private Boolean isComment;
  32. /**
  33. * 是否转发
  34. * true: 可以转发 1
  35. * false: 不可转发 0
  36. */
  37. @TableField("is_forward")
  38. private Boolean isForward;
  39. /**
  40. * 是否下架
  41. * true: 下架 1
  42. * false: 没有下架 0
  43. */
  44. @TableField("is_down")
  45. private Boolean isDown;
  46. /**
  47. * 是否已删除
  48. * true: 删除 1
  49. * false: 没有删除 0
  50. */
  51. @TableField("is_delete")
  52. private Boolean isDelete;
  53. }

ap_article_content 文章内容对应的实体类

  1. package com.heima.model.article.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. @Data
  9. @TableName("ap_article_content")
  10. public class ApArticleContent implements Serializable {
  11. @TableId(value = "id",type = IdType.ID_WORKER)
  12. private Long id;
  13. /**
  14. * 文章id
  15. */
  16. @TableField("article_id")
  17. private Long articleId;
  18. /**
  19. * 文章内容
  20. */
  21. private String content;
  22. }

1.4)实现思路

1,在默认频道展示10条文章信息

2,可以切换频道查看不同种类文章

3,当用户下拉可以加载最新的文章(分页)本页文章列表中发布时间为最大的时间为依据

4,当用户上拉可以加载更多的文章信息(按照发布时间)本页文章列表中发布时间最小的时间为依据

5,如果是当前频道的首页,前端传递默认参数

  • maxBehotTime:0(毫秒)

  • minBehotTime:20000000000000(毫秒)--->2063年

1.5)接口定义

加载首页

加载更多

加载最新

接口路径

/api/v1/article/load

/api/v1/article/loadmore

/api/v1/article/loadnew

请求方式

POST

POST

POST

参数

ArticleHomeDto

ArticleHomeDto

ArticleHomeDto

响应结果

ResponseResult

ResponseResult

ResponseResult

ArticleHomeDto

  1. package com.heima.model.article.dtos;
  2. import lombok.Data;
  3. import java.util.Date;
  4. @Data
  5. public class ArticleHomeDto {
  6. // 最大时间
  7. Date maxBehotTime;
  8. // 最小时间
  9. Date minBehotTime;
  10. // 分页size
  11. Integer size;
  12. // 频道ID
  13. String tag;
  14. }

1.6)功能实现

1.6.1):导入heima-leadnews-article微服务,资料在当天的文件夹中

注意:需要在heima-leadnews-service的pom文件夹中添加子模块信息,如下:

  1. <modules>
  2. <module>heima-leadnews-user</module>
  3. <module>heima-leadnews-article</module>
  4. </modules>

在idea中的maven中更新一下,如果工程还是灰色的,需要在重新添加文章微服务的pom文件,操作步骤如下:

需要在nacos中添加对应的配置

  1. spring:
  2. datasource:
  3. driver-class-name: com.mysql.jdbc.Driver
  4. url: jdbc:mysql://localhost:3306/leadnews_article?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
  5. username: root
  6. password: root
  7. # 设置Mapper接口所对应的XML文件位置,如果你在Mapper接口中有自定义方法,需要进行该配置
  8. mybatis-plus:
  9. mapper-locations: classpath*:mapper/*.xml
  10. # 设置别名包扫描路径,通过该属性可以给包中的类注册别名
  11. type-aliases-package: com.heima.model.article.pojos
1.6.2):定义接口
  1. package com.heima.article.controller.v1;
  2. import com.heima.model.article.dtos.ArticleHomeDto;
  3. import com.heima.model.common.dtos.ResponseResult;
  4. import org.springframework.web.bind.annotation.PostMapping;
  5. import org.springframework.web.bind.annotation.RequestBody;
  6. import org.springframework.web.bind.annotation.RequestMapping;
  7. import org.springframework.web.bind.annotation.RestController;
  8. @RestController
  9. @RequestMapping("/api/v1/article")
  10. public class ArticleHomeController {
  11. @PostMapping("/load")
  12. public ResponseResult load(@RequestBody ArticleHomeDto dto) {
  13. return null;
  14. }
  15. @PostMapping("/loadmore")
  16. public ResponseResult loadMore(@RequestBody ArticleHomeDto dto) {
  17. return null;
  18. }
  19. @PostMapping("/loadnew")
  20. public ResponseResult loadNew(@RequestBody ArticleHomeDto dto) {
  21. return null;
  22. }
  23. }
1.6.3):编写mapper文件

mybatisPlus对多表查询不太友好,所以用mybatis自定义mapper查询

  1. package com.heima.article.mapper;
  2. import com.baomidou.mybatisplus.core.mapper.BaseMapper;
  3. import com.heima.model.article.dtos.ArticleHomeDto;
  4. import com.heima.model.article.pojos.ApArticle;
  5. import org.apache.ibatis.annotations.Mapper;
  6. import org.apache.ibatis.annotations.Param;
  7. import java.util.List;
  8. @Mapper
  9. public interface ApArticleMapper extends BaseMapper<ApArticle> {
  10. public List<ApArticle> loadArticleList(@Param("dto") ArticleHomeDto dto, @Param("type") Short type);
  11. }

对应的映射文件

在resources中新建mapper/ApArticleMapper.xml 如下配置:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  3. <mapper namespace="com.heima.article.mapper.ApArticleMapper">
  4. <resultMap id="resultMap" type="com.heima.model.article.pojos.ApArticle">
  5. <id column="id" property="id"/>
  6. <result column="title" property="title"/>
  7. <result column="author_id" property="authorId"/>
  8. <result column="author_name" property="authorName"/>
  9. <result column="channel_id" property="channelId"/>
  10. <result column="channel_name" property="channelName"/>
  11. <result column="layout" property="layout"/>
  12. <result column="flag" property="flag"/>
  13. <result column="images" property="images"/>
  14. <result column="labels" property="labels"/>
  15. <result column="likes" property="likes"/>
  16. <result column="collection" property="collection"/>
  17. <result column="comment" property="comment"/>
  18. <result column="views" property="views"/>
  19. <result column="province_id" property="provinceId"/>
  20. <result column="city_id" property="cityId"/>
  21. <result column="county_id" property="countyId"/>
  22. <result column="created_time" property="createdTime"/>
  23. <result column="publish_time" property="publishTime"/>
  24. <result column="sync_status" property="syncStatus"/>
  25. <result column="static_url" property="staticUrl"/>
  26. </resultMap>
  27. <select id="loadArticleList" resultMap="resultMap">
  28. SELECT
  29. aa.*
  30. FROM
  31. `ap_article` aa
  32. LEFT JOIN ap_article_config aac ON aa.id = aac.article_id
  33. <where>
  34. and aac.is_delete != 1
  35. and aac.is_down != 1
  36. <!-- loadmore -->
  37. <if test="type != null and type == 1">
  38. and aa.publish_time <![CDATA[<]]> #{dto.minBehotTime}
  39. </if>
  40. <if test="type != null and type == 2">
  41. and aa.publish_time <![CDATA[>]]> #{dto.maxBehotTime}
  42. </if>
  43. <if test="dto.tag != '__all__'">
  44. and aa.channel_id = #{dto.tag}
  45. </if>
  46. </where>
  47. order by aa.publish_time desc
  48. limit #{dto.size}
  49. </select>
  50. </mapper>
1.6.4):编写业务层代码
  1. package com.heima.article.service;
  2. import com.baomidou.mybatisplus.extension.service.IService;
  3. import com.heima.model.article.dtos.ArticleHomeDto;
  4. import com.heima.model.article.pojos.ApArticle;
  5. import com.heima.model.common.dtos.ResponseResult;
  6. import java.io.IOException;
  7. public interface ApArticleService extends IService<ApArticle> {
  8. /**
  9. * 根据参数加载文章列表
  10. * @param loadtype 1为加载更多 2为加载最新
  11. * @param dto
  12. * @return
  13. */
  14. ResponseResult load(Short loadtype, ArticleHomeDto dto);
  15. }

实现类:

  1. package com.heima.article.service.impl;
  2. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
  3. import com.heima.article.mapper.ApArticleMapper;
  4. import com.heima.article.service.ApArticleService;
  5. import com.heima.common.constants.ArticleConstants;
  6. import com.heima.model.article.dtos.ArticleHomeDto;
  7. import com.heima.model.article.pojos.ApArticle;
  8. import com.heima.model.common.dtos.ResponseResult;
  9. import lombok.extern.slf4j.Slf4j;
  10. import org.apache.commons.lang3.StringUtils;
  11. import org.springframework.beans.factory.annotation.Autowired;
  12. import org.springframework.stereotype.Service;
  13. import org.springframework.transaction.annotation.Transactional;
  14. import java.util.Date;
  15. import java.util.List;
  16. @Service
  17. @Transactional
  18. @Slf4j
  19. public class ApArticleServiceImpl extends ServiceImpl<ApArticleMapper, ApArticle> implements ApArticleService {
  20. // 单页最大加载的数字
  21. private final static short MAX_PAGE_SIZE = 50;
  22. @Autowired
  23. private ApArticleMapper apArticleMapper;
  24. /**
  25. * 根据参数加载文章列表
  26. * @param loadtype 1为加载更多 2为加载最新
  27. * @param dto
  28. * @return
  29. */
  30. @Override
  31. public ResponseResult load(Short loadtype, ArticleHomeDto dto) {
  32. //1.校验参数
  33. Integer size = dto.getSize();
  34. if(size == null || size == 0){
  35. size = 10;
  36. }
  37. size = Math.min(size,MAX_PAGE_SIZE);
  38. dto.setSize(size);
  39. //类型参数检验
  40. if(!loadtype.equals(ArticleConstants.LOADTYPE_LOAD_MORE)&&!loadtype.equals(ArticleConstants.LOADTYPE_LOAD_NEW)){
  41. loadtype = ArticleConstants.LOADTYPE_LOAD_MORE;
  42. }
  43. //文章频道校验
  44. if(StringUtils.isEmpty(dto.getTag())){
  45. dto.setTag(ArticleConstants.DEFAULT_TAG);
  46. }
  47. //时间校验
  48. if(dto.getMaxBehotTime() == null) dto.setMaxBehotTime(new Date());
  49. if(dto.getMinBehotTime() == null) dto.setMinBehotTime(new Date());
  50. //2.查询数据
  51. List<ApArticle> apArticles = apArticleMapper.loadArticleList(dto, loadtype);
  52. //3.结果封装
  53. ResponseResult responseResult = ResponseResult.okResult(apArticles);
  54. return responseResult;
  55. }
  56. }

定义常量类

  1. package com.heima.common.constants;
  2. public class ArticleConstants {
  3. public static final Short LOADTYPE_LOAD_MORE = 1;
  4. public static final Short LOADTYPE_LOAD_NEW = 2;
  5. public static final String DEFAULT_TAG = "__all__";
  6. }
1.6.5):编写控制器代码
  1. package com.heima.article.controller.v1;
  2. import com.heima.article.service.ApArticleService;
  3. import com.heima.common.constants.ArticleConstants;
  4. import com.heima.model.article.dtos.ArticleHomeDto;
  5. import com.heima.model.common.dtos.ResponseResult;
  6. import org.springframework.beans.factory.annotation.Autowired;
  7. import org.springframework.web.bind.annotation.PostMapping;
  8. import org.springframework.web.bind.annotation.RequestBody;
  9. import org.springframework.web.bind.annotation.RequestMapping;
  10. import org.springframework.web.bind.annotation.RestController;
  11. @RestController
  12. @RequestMapping("/api/v1/article")
  13. public class ArticleHomeController {
  14. @Autowired
  15. private ApArticleService apArticleService;
  16. @PostMapping("/load")
  17. public ResponseResult load(@RequestBody ArticleHomeDto dto) {
  18. return apArticleService.load(ArticleConstants.LOADTYPE_LOAD_MORE,dto);
  19. }
  20. @PostMapping("/loadmore")
  21. public ResponseResult loadMore(@RequestBody ArticleHomeDto dto) {
  22. return apArticleService.load(ArticleConstants.LOADTYPE_LOAD_MORE,dto);
  23. }
  24. @PostMapping("/loadnew")
  25. public ResponseResult loadNew(@RequestBody ArticleHomeDto dto) {
  26. return apArticleService.load(ArticleConstants.LOADTYPE_LOAD_NEW,dto);
  27. }
  28. }
1.6.6):swagger测试或前后端联调测试

第一:在app网关的微服务的nacos的配置中心添加文章微服务的路由,完整配置如下:

  1. spring:
  2. cloud:
  3. gateway:
  4. globalcors:
  5. cors-configurations:
  6. '[/**]': # 匹配所有请求
  7. allowedOrigins: "*" #跨域处理 允许所有的域
  8. allowedMethods: # 支持的方法
  9. - GET
  10. - POST
  11. - PUT
  12. - DELETE
  13. routes:
  14. # 用户微服务
  15. - id: user
  16. uri: lb://leadnews-user
  17. predicates:
  18. - Path=/user/**
  19. filters:
  20. - StripPrefix= 1
  21. # 文章微服务
  22. - id: article
  23. uri: lb://leadnews-article
  24. predicates:
  25. - Path=/article/**
  26. filters:
  27. - StripPrefix= 1

第二:启动nginx,直接使用前端项目测试,启动文章微服务,用户微服务、app网关微服务

2)freemarker

2.1) freemarker 介绍

FreeMarker 是一款 模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本(HTML网页,电子邮件,配置文件,源代码等)的通用工具。 它不是面向最终用户的,而是一个Java类库,是一款程序员可以嵌入他们所开发产品的组件。

模板编写为FreeMarker Template Language (FTL)。它是简单的,专用的语言, 不是 像PHP那样成熟的编程语言。 那就意味着要准备数据在真实编程语言中来显示,比如数据库查询和业务运算, 之后模板显示已经准备好的数据。在模板中,你可以专注于如何展现数据, 而在模板之外可以专注于要展示什么数据

常用的java模板引擎还有哪些?

Jsp、Freemarker、Thymeleaf 、Velocity 等。

1.Jsp 为 Servlet 专用,不能单独进行使用。 已淘汰

2.Thymeleaf 为新技术,功能较为强大,但是执行的效率比较低

3.Velocity从2010年更新完 2.0 版本后,便没有在更新。Spring Boot 官方在 1.4 版本后对此也不在支持,虽然 Velocity 在 2017 年版本得到迭代,但为时已晚

2.2) 环境搭建&&快速入门

freemarker作为springmvc一种视图格式,默认情况下SpringMVC支持freemarker视图格式。

需要创建Spring Boot+Freemarker工程用于测试模板。

2.2.1) 创建测试工程

创建一个freemarker-demo 的测试工程专门用于freemarker的功能测试与模板的测试。

pom.xml如下

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <parent>
  6. <artifactId>heima-leadnews-test</artifactId>
  7. <groupId>com.heima</groupId>
  8. <version>1.0-SNAPSHOT</version>
  9. </parent>
  10. <modelVersion>4.0.0</modelVersion>
  11. <artifactId>freemarker-demo</artifactId>
  12. <properties>
  13. <maven.compiler.source>8</maven.compiler.source>
  14. <maven.compiler.target>8</maven.compiler.target>
  15. </properties>
  16. <dependencies>
  17. <dependency>
  18. <groupId>org.springframework.boot</groupId>
  19. <artifactId>spring-boot-starter-web</artifactId>
  20. </dependency>
  21. <dependency>
  22. <groupId>org.springframework.boot</groupId>
  23. <artifactId>spring-boot-starter-freemarker</artifactId>
  24. </dependency>
  25. <dependency>
  26. <groupId>org.springframework.boot</groupId>
  27. <artifactId>spring-boot-starter-test</artifactId>
  28. </dependency>
  29. <!-- lombok -->
  30. <dependency>
  31. <groupId>org.projectlombok</groupId>
  32. <artifactId>lombok</artifactId>
  33. </dependency>
  34. <!-- apache 对 java io 的封装工具库 -->
  35. <dependency>
  36. <groupId>org.apache.commons</groupId>
  37. <artifactId>commons-io</artifactId>
  38. <version>1.3.2</version>
  39. </dependency>
  40. </dependencies>
  41. </project>
2.2.2) 配置文件

配置application.yml

  1. server:
  2. port: 8881 #服务端口
  3. spring:
  4. application:
  5. name: freemarker-demo #指定服务名
  6. freemarker:
  7. cache: false #关闭模板缓存,方便测试
  8. settings:
  9. template_update_delay: 0 #检查模板更新延迟时间,设置为0表示立即检查,如果时间大于0会有缓存不方便进行模板测试
  10. suffix: .ftl #指定Freemarker模板文件的后缀名
2.2.3) 创建模型类

在freemarker的测试工程下创建模型类型用于测试

  1. package com.heima.freemarker.entity;
  2. import lombok.Data;
  3. import java.util.Date;
  4. @Data
  5. public class Student {
  6. private String name;//姓名
  7. private int age;//年龄
  8. private Date birthday;//生日
  9. private Float money;//钱包
  10. }
2.2.4) 创建模板

在resources下创建templates,此目录为freemarker的默认模板存放目录

在templates下创建模板文件 01-basic.ftl ,模板中的插值表达式最终会被freemarker替换成具体的数据。


<code class="language-plaintext hljs"><!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Hello World!</title>
</head>
<body>
<b>普通文本 String 展示:</b><br><br>
Hello ${name} <br>
<hr>
<b>对象Student中的数据展示:</b><br/>
姓名:${stu.name}<br/>
年龄:${stu.age}
<hr>
</body>
</html></code>
2.2.5) 创建controller

创建Controller类,向Map中添加name,最后返回模板文件。

  1. package com.xuecheng.test.freemarker.controller;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.stereotype.Controller;
  4. import org.springframework.web.bind.annotation.RequestMapping;
  5. import org.springframework.web.client.RestTemplate;
  6. import java.util.Map;
  7. @Controller
  8. public class HelloController {
  9. @GetMapping("/basic")
  10. public String test(Model model) {
  11. //1.纯文本形式的参数
  12. model.addAttribute("name", "freemarker");
  13. //2.实体类相关的参数
  14. Student student = new Student();
  15. student.setName("小明");
  16. student.setAge(18);
  17. model.addAttribute("stu", student);
  18. return "01-basic";
  19. }
  20. }

01-basic.ftl,使用插值表达式填充数据


<code class="language-plaintext hljs"><!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Hello World!</title>
</head>
<body>
<b>普通文本 String 展示:</b><br><br>
Hello ${name} <br>
<hr>
<b>对象Student中的数据展示:</b><br/>
姓名:${stu.name}<br/>
年龄:${stu.age}
<hr>
</body>
</html></code>
2.2.6) 创建启动类
  1. package com.heima.freemarker;
  2. import org.springframework.boot.SpringApplication;
  3. import org.springframework.boot.autoconfigure.SpringBootApplication;
  4. @SpringBootApplication
  5. public class FreemarkerDemotApplication {
  6. public static void main(String[] args) {
  7. SpringApplication.run(FreemarkerDemotApplication.class,args);
  8. }
  9. }
2.2.7) 测试

请求:http://localhost:8881/basic

2.3) freemarker基础

2.3.1) 基础语法种类
1、注释,即<#-- -->,介于其之间的内容会被freemarker忽略

<code class="language-plaintext hljs"><#--我是一个freemarker注释--></code>
2、插值(Interpolation):即 ${..} 部分,freemarker会用真实的值代替**${..}**

<code class="language-plaintext hljs">Hello ${name}</code>

3、FTL指令:和HTML标记类似,名字前加#予以区分,Freemarker会解析标签中的表达式或逻辑


<code class="language-plaintext hljs"><# >FTL指令</#> </code>

4、文本,仅文本信息,这些不是freemarker的注释、插值、FTL指令的内容会被freemarker忽略解析,直接输出内容。


<code class="language-plaintext hljs"><#--freemarker中的普通文本-->
我是一个普通的文本</code>
2.3.2) 集合指令(List和Map)

1、数据模型:

在HelloController中新增如下方法:

  1. @GetMapping("/list")
  2. public String list(Model model){
  3. //------------------------------------
  4. Student stu1 = new Student();
  5. stu1.setName("小强");
  6. stu1.setAge(18);
  7. stu1.setMoney(1000.86f);
  8. stu1.setBirthday(new Date());
  9. //小红对象模型数据
  10. Student stu2 = new Student();
  11. stu2.setName("小红");
  12. stu2.setMoney(200.1f);
  13. stu2.setAge(19);
  14. //将两个对象模型数据存放到List集合中
  15. List<Student> stus = new ArrayList<>();
  16. stus.add(stu1);
  17. stus.add(stu2);
  18. //向model中存放List集合数据
  19. model.addAttribute("stus",stus);
  20. //------------------------------------
  21. //创建Map数据
  22. HashMap<String,Student> stuMap = new HashMap<>();
  23. stuMap.put("stu1",stu1);
  24. stuMap.put("stu2",stu2);
  25. // 3.1 向model中存放Map数据
  26. model.addAttribute("stuMap", stuMap);
  27. return "02-list";
  28. }

2、模板:

在templates中新增02-list.ftl文件


<code class="language-plaintext hljs"><!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Hello World!</title>
</head>
<body>
    
<#-- list 数据的展示 -->
<b>展示list中的stu数据:</b>
<br>
<br>
<table>
    <tr>
        <td>序号</td>
        <td>姓名</td>
        <td>年龄</td>
        <td>钱包</td>
    </tr>
</table>
<hr>
    
<#-- Map 数据的展示 -->
<b>map数据的展示:</b>
<br/><br/>
<a href="###">方式一:通过map['keyname'].property</a><br/>
输出stu1的学生信息:<br/>
姓名:<br/>
年龄:<br/>
<br/>
<a href="###">方式二:通过map.keyname.property</a><br/>
输出stu2的学生信息:<br/>
姓名:<br/>
年龄:<br/>

<br/>
<a href="###">遍历map中两个学生信息:</a><br/>
<table>
    <tr>
        <td>序号</td>
        <td>姓名</td>
        <td>年龄</td>
        <td>钱包</td> 
    </tr>
</table>
<hr>
 
</body>
</html></code>

实例代码:


<code class="language-plaintext hljs"><!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Hello World!</title>
</head>
<body>
    
<#-- list 数据的展示 -->
<b>展示list中的stu数据:</b>
<br>
<br>
<table>
    <tr>
        <td>序号</td>
        <td>姓名</td>
        <td>年龄</td>
        <td>钱包</td>
    </tr>
    <#list stus as stu>
        <tr>
            <td>${stu_index+1}</td>
            <td>${stu.name}</td>
            <td>${stu.age}</td>
            <td>${stu.money}</td>
        </tr>
    </#list>

</table>
<hr>
    
<#-- Map 数据的展示 -->
<b>map数据的展示:</b>
<br/><br/>
<a href="###">方式一:通过map['keyname'].property</a><br/>
输出stu1的学生信息:<br/>
姓名:${stuMap['stu1'].name}<br/>
年龄:${stuMap['stu1'].age}<br/>
<br/>
<a href="###">方式二:通过map.keyname.property</a><br/>
输出stu2的学生信息:<br/>
姓名:${stuMap.stu2.name}<br/>
年龄:${stuMap.stu2.age}<br/>

<br/>
<a href="###">遍历map中两个学生信息:</a><br/>
<table>
    <tr>
        <td>序号</td>
        <td>姓名</td>
        <td>年龄</td>
        <td>钱包</td>
    </tr>
    <#list stuMap?keys as key >
        <tr>
            <td>${key_index}</td>
            <td>${stuMap[key].name}</td>
            <td>${stuMap[key].age}</td>
            <td>${stuMap[key].money}</td>
        </tr>
    </#list>
</table>
<hr>
 
</body>
</html></code>

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/276937
推荐阅读
相关标签