第1章-环境搭建.md 64 KB

第1章-环境搭建

学习目标

  • 了解项目背景
  • 虚拟机环境准备
  • 搭建微服务环境
  • 完成商品微服务分类平台属性管理
  • 使用Swagger/Knife4j/YAPI进行接口测试
  • 域名访问本地服务

随堂流程图:https://kdocs.cn/join/g45bkl9?f=101

项目代码地址:https://gitee.com/lvhonlgong/sph-0327.git

1. 课程简介

1.1 为什么我们要讲电商?

因为就互联网平台来说,电商网站有很多典型的特征:

  • 访问量大
  • 数据量大
  • 有一定的业务复杂性
  • 涉及支付考虑一定安全性{幂等性}

1.2 电商的主要模式

B2B(Business to Business)

B2B ( Business to Business)是指进行电子商务交易的供需双方都是商家(或企业、公司),她(他)们使用了互联网的技术或各种商务网络平台,完成商务交易的过程。电子商务是现代 B2B marketing的一种具体主要的表现形式。

案例:阿里巴巴1688

C2C(Consumer to Customer)

C2C即 Customer(Consumer) to Customer(Consumer),意思就是消费者个人间的电子商务行为。比如一个消费者有一台电脑,通过网络进行交易,把它出售给另外一个消费者,此种交易类型就称为C2C电子商务。

案例:淘宝、易趣、瓜子二手车、闲鱼

B2C(Business-to-Customer)

B2C是Business-to-Customer的缩写,而其中文简称为“商对客”。“商对客”是电子商务的一种模式,也就是通常说的直接面向消费者销售产品和服务商业零售模式。这种形式的电子商务一般以网络零售业为主,主要借助于互联网开展在线销售活动。B2C即企业通过互联网为消费者提供一个新型的购物环境——网上商店,消费者通过网络在网上购物、网上支付等消费行为。

案例:唯品会、乐蜂网、京东

B2B2C(Business to Business to Consumer)

B2B2C是一种电子商务类型的网络购物商业模式,B是BUSINESS的简称,C是CUSTOMER的简称,第一个B指的是商品或服务的供应商,第二个B指的是从事电子商务的企业,C则是表示消费者。

案例:京东商城、天猫商城

C2B(Consumer to Business)

C2B(Consumer to Business,即消费者到企业),是互联网经济时代新的商业模式。这一模式改变了原有生产者(企业和机构)和消费者的关系,是一种消费者贡献价值(Create Value), 企业和机构消费价值(Consume Value)。

C2B模式和我们熟知的供需模式(DSM, Demand SupplyModel)恰恰相反,真正的C2B 应该先有消费者需求产生而后有企业生产,即先有消费者提出需求,后有生产企业按需求组织生产。通常情况为消费者根据自身需求定制产品和价格,或主动参与产品设计、生产和定价,产品、价格等彰显消费者的个性化需求,生产企业进行定制化生产。

案例:猪八戒

O2O(Online To Offline)

O2O即Online To Offline(在线离线/线上到线下),是指将线下的商务机会与互联网结合,让互联网成为线下交易的平台,这个概念最早来源于美国。O2O的概念非常广泛,既可涉及到线上,又可涉及到线下,可以通称为O2O。主流商业管理课程均对O2O这种新型的商业模式有所介绍及关注。

案例:美团、饿了么

1.3 我们能从这个项目中学到什么?

锻炼能力:

  • 独立分析需求
  • 独立设计接口文档
  • 独立编码(独立解决问题)
  • 沟通能力
  • 表达能力

1.3.1 温故知新

巩固以前知识,学习技术点与技术点应用场景,掌握电商业务流程

1.3.2 核心技术

  • SpringBoot:简化新Spring应用的初始搭建以及开发过程;

  • SpringCloud:基于Spring Boot实现的云原生应用开发工具,SpringCloud使用的技术:(Spring Cloud Gateway、Spring Cloud Alibaba Nacos、Spring Cloud Alibaba Sentinel、Spring Cloud Task和Spring Cloud Feign等)

  • SpringBoot+SpringCloudAlibaba(Nacos,Sentinel)+Cloud OpenFeign

  • MyBatis-Plus:持久层框架,也依赖mybatis

  • Redis:内存做缓存

  • Redisson:基于redis的Java驻内存数据网格 - 框架;操作redis的框架

  • RabbitMQ:消息中间件;大型分布式项目是标配;分布式事务最终一致性

  • (ELK)ElasticSearch+Kibana+Logstash: 全文检索服务器+可视化数据监控:检索

  • ThreadPoolExecutor+CompletableFuture:线程池实现异步操作,提高效率

  • Thymeleaf: 页面模板技术;JSP、服务端渲染;${},前后分离

  • Swagger2/Knife4J/YAPI:Api接口文档工具

  • MinIO(私有化对象存储集群):分布式文件存储 类似于OSS(公有)

  • 支付宝支付:alipay.com

  • MySQL:关系型数据库 {shardingSphere-jdbc 进行分库,分表}

  • Lombok: 实体类的中get/set 生成的jar包

  • XXL-JOB 分布式任务调度

  • Ngrok/natapp:内网穿透

  • Docker:容器化技术; 生产环境Redis(运维人员);快速搭建环境Docker run

  • Git:代码管理工具;git使用,拉代码、提交、推送、合并、冲突解决

  • Jenkins:持续集成工具(devops):部署

前端技术栈

  • Vue.js:web 界面的渐进式框架; 对请求接口。
  • Node.js: JavaScript 运行环境
  • NPM:包管理器

1.3.3 需要掌握的解决方案

分布式架构、缓存解决方案、分布式事务、后台管理(数据库的可视化界面,renren-fast、ruoyi)、文件管理系统

课前说明:

1、 建议内存16个G以上

2、 培养自己独立阅读代码的能力

3、 帮助大家分析 解构业务需求

4、 新的知识点,难点敲

5、 重复的功能 自己开发

1.4 项目架构

一个项目:如下选型

网络:公有云,私有云

存储:(结构化存储(关系型数据库(MySQL))、非结构化的存储(Redis,mongodb))

消息:(消息队列: RabbitMQ、Kafka)

缓存:(Redis、Memcache)

检索:(ElasticSearch)

微服务治理:(配置中心(Nacos/Spring Cloud Config+Github)、注册中心(Nacos/Eureka)、流量保护(Sentinel/Hystrix),服务调用(Dubbo/OpenFeign))

监控:(运维):Prometheus + Grafana

日志:(大数据+后端),提取有用信息进行数据分析、聚类计算、用户画像

1.5 整体业务简介

)

首页 静态页面,包含了商品分类,搜索栏,商品广告位。
全文搜索 通过搜索栏填入的关键字进行搜索,并列表展示
分类查询 根据首页的商品类目进行查询
商品详情 商品的详细信息展示
购物车 将有购买意向的商品临时存放的地方
单点登录 用户统一登录的管理
结算 将购物车中勾选的商品初始化成要填写的订单
下单 填好的订单提交
订单服务 负责确认订单是否付款成功,并对接仓储物流系统。
支付服务 下单后,用户点击支付,负责对接第三方支付系统。
仓储物流 独立的管理系统,负责商品的库存。
后台管理 主要维护类目、商品、库存单元、广告位等信息。
秒杀 秒杀抢购完整方案

在线体验地址:

2. 虚拟机设置

2.1 下载虚拟机

请使用百度云盘VIP进行下载

img

2.2 配置虚拟机

第一步:

img

第二步:改NAT模式的子网IP:192.168.200.0

img

第三步:确定

第四步:启动虚拟机

img

2.3 登录虚拟机

IP:192.168.200.128

登录用户:root

登录密码:123456

2.4 查看虚拟机中安装的软件

查看所有安装的容器命令如下

  • docker ps -a {查看docker中所有的容器}

  • docker ps {表示查看正在运行的容器}

Docker常用命令:

镜像相关命令

docker search 软件  #搜索镜像
docker pull 镜像名称   #下载镜像
docker images    # 查询本地存在镜像
docker rmi 镜像名称/镜像ID     #删除镜像

容器相关命令

#创建容器指令
docker run --name 容器名称 -p 宿主机端口:容器内部端口 -e 环境变量 -v 宿主机目录文件:/容器目录文件 镜像:版本
docker ps [-a]  #查询本地容器列表
docker start/stop/restart 容器ID/容器名称
docker rm -f 容器ID/容器名称  #删除容器

容器无法正常运行

docker logs 容器ID/容器名称 # 查询容器运行日志,通过错误信息定位问题

2.5 使用SecureCRT或者FinalShell连接

img

2.6 验证容器运行状态

目前在虚拟机中安装Mysql(RPM方式安装),Redis,Nacos,Sentinel,Elasticsearch,RabbitMQ... 都是开机自启动!

URL 账号密码
MySQL-RPM 192.168.200.128:3306 root/123456
Redis 192.168.200.128:6379
Elasticsearch http://192.168.200.128:9200
Kibana http://192.168.200.128:5601
Logstash 收集日志的后台进程,无需访问
RabbitMQ http://192.168.200.128:15672 admin/admin
Zipkin http://192.168.200.128:9411
Sentinel http://192.168.200.128:8858 sentinel/sentinel
Nacos http://192.168.200.128:8848/nacos nacos/nacos
MinIO http://192.168.200.128:9001 admin/admin123456
YAPI http://192.168.200.128:3000 admin@admin.com/ymfe.org
Portainer http://192.168.200.128:19000 admin/admin1234567

3. 电商微服务模块搭建

在搭建环境前准备好数据库环境,将配套资料中的数据库脚本执行

  1. 注意:导入到虚拟机RPM安装的MySQL8.0中/本地安装MySQL8.0

  2. 查询虚拟机中RPM安装的MySQL运行状态

    systemctl status mysqld  #查询状态
    systemctl start mysqld  #启动MySQL服务
    systemctl enable mysqld  #设置为开机启动
    
  3. 通过mysql客户端导入SQL脚本(不需要手动建库)

image-20230220112746381

image-20230220112805014

创建工作目录 D:\gmall 不能有中文,不能有空格

3.0 YAPI接口服务

前后端分离开发模式下,前端跟后端工程师,需要有开发接口依据,以下信息必须包含在开发接口中

  • 接口实现业务
  • 请求方式 GET POST DELETE PUT
  • 请求路径 见名知意
  • 请求参数格式以及内容 form-data/json url path
  • 响应结果

目前企业中开发都会采用在线接口平台,例如项目中使用YAPI,不仅仅看接口信息,还能够直接进行使用

  • 前端工程师(可以访问mock地址模拟调试前端)
  • 后端工程师(可以直接通过YAPI访问后端服务进行测试)

3.1 搭建电商父工程(gmall-parent)

3.1.1 搭建gmall-parent

打开idea,选择File–>New–>Project,操作如下

img

选择下一步

img

配置:

groupId:com.atguigu.gmall

artifactId:gmall-parent

选择下一步

img

完成

工程结构如下

img

由于这是一个父工程,删除src目录

img

3.1.2 配置pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.atguigu.gmall</groupId>
    <artifactId>gmall-parent</artifactId>
    <version>1.0</version>
    
    <!--所有工程的父工程-->
    <packaging>pom</packaging>

    <!--引入spring boot 父工程-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.6.RELEASE</version>
    </parent>

    <!--定义jar包的版本号-->
    <properties>
        <elasticsearch.version>7.8.0</elasticsearch.version>
        <java.version>1.8</java.version>
        <cloud.version>Hoxton.SR7</cloud.version>
        <alibaba.version>2.2.5.RELEASE</alibaba.version>
        <gmall.version>1.0</gmall.version>
        <mybatis-plus.version>3.4.1</mybatis-plus.version>
        <mysql.version>8.0.29</mysql.version>
        <lombok.version>1.18.10</lombok.version>
        <swagger.version>2.9.2</swagger.version>
        <knife4j.version>2.0.2</knife4j.version>
        <fastjson.version>1.2.58</fastjson.version>
        <redisson.version>3.15.3</redisson.version>
        <pool2.version>2.6.0</pool2.version>
        <httpclient.version>4.5.13</httpclient.version>
    </properties>

    <!--配置dependencyManagement锁定依赖的版本 并不是实际的依赖。-->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!--mybatis-plus 持久层-->
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>${mybatis-plus.version}</version>
            </dependency>

            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>${mysql.version}</version>
            </dependency>

            <!--swagger-->
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger2</artifactId>
                <version>${swagger.version}</version>
            </dependency>
            <!--swagger ui-->
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger-ui</artifactId>
                <version>${swagger.version}</version>
            </dependency>
            <dependency>
                <groupId>com.github.xiaoymin</groupId>
                <artifactId>knife4j-spring-boot-starter</artifactId>
                <version>${knife4j.version}</version>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>${lombok.version}</version>
            </dependency>
            <!-- redisson 分布式锁-->
            <dependency>
                <groupId>org.redisson</groupId>
                <artifactId>redisson</artifactId>
                <version>${redisson.version}</version>
            </dependency>
            <!-- spring2.X集成redis所需common-pool2-->
            <dependency>
                <groupId>org.apache.commons</groupId>
                <artifactId>commons-pool2</artifactId>
                <version>${pool2.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.httpcomponents</groupId>
                <artifactId>httpclient</artifactId>
                <version>${httpclient.version}</version>
            </dependency>
            <!-- fastjson -->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>${fastjson.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

3.2 搭建gmall-common父模块

gmall-common:公共模块父节点

  • common-util:工具类模块,所有模块都可以依赖于它

  • service-util:service服务的工具包,包含service服务的公共配置类,所有service模块依赖于它

3.2.1 搭建gmall-common

点击gmall-parent,选择New–>Module,操作如下

img

选择下一步

选择下一步

完成,删除src目录,结构如下

3.2.2 修改配置pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>gmall-parent</artifactId>
        <groupId>com.atguigu.gmall</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>gmall-common</artifactId>
    
    <!--公共模块父工程打包方式设置为POM-->
    <packaging>pom</packaging>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <scope>provided</scope>
        </dependency>

        <!--lombok用来简化实体类:需要安装lombok插件-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <!--swagger-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
        </dependency>

        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
        </dependency>

        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>

        <!--用来转换json使用 {JavaObject - json | json - JavaObject}-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.29</version>
        </dependency>

        <!-- 服务调用feign -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
</project>

3.3 搭建common-util模块

3.3.1 搭建common-util

点击gmall-common,选择New–>Module,操作如下

img

选择下一步

选择下一步

完成,结构如下

3.3.2 修改配置pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>gmall-common</artifactId>
        <groupId>com.atguigu.gmall</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>common-util</artifactId>

    <packaging>jar</packaging>
    <name>common-util</name>
    <description>common-util</description>

    <dependencies>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
        </dependency>
    </dependencies>
</project>

3.3.3 添加公共工具类

GmallException 自定义全局异常
Result API统一返回结果封装类
ResultCodeEnum API统一返回结果状态信息
AuthContextHolder 获取登录用户信息类
HttpClientUtil http客户端类
MD5 通过MD5给字符串加密的工具类
IpUtil 获取Ip地址的工具类
DateUitl 日期比较工具类

3.4 搭建service-util模块

3.4.1 搭建service-util

搭建过程同common-util,如图

3.4.2 修改配置pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>gmall-common</artifactId>
        <groupId>com.atguigu.gmall</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>service-util</artifactId>

    <dependencies>
        <dependency>
            <groupId>com.atguigu.gmall</groupId>
            <artifactId>common-util</artifactId>
            <version>1.0</version>
        </dependency>

        <!-- redis -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

        <!-- spring2.X集成redis所需common-pool2-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
            <version>2.6.0</version>
        </dependency>

        <!-- redisson 分布式锁-->
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.15.3</version>
        </dependency>

        <!--mybatis-plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <scope>provided</scope>
        </dependency>
    </dependencies>
</project>

3.4.3 添加service-util工具类

MybatisPlusConfig MybatisPlus配置类
MybatisPlusConfig MybatisPlus配置类
Swagger2Config Swagger2配置类

3.5 搭建gmall-model模块

3.5.1 搭建gmall-model

点击gmall-parent 父工程 选择module模块名称:gmall-model,搭建过程同common父模块

PO-持久层对象 VO-视图对象 DTO-数据传输对象(服务间调用)

注意:搭建完成后,导入资料项目相关实体类,使用今日下发实体类

image-20221212002002659

3.5.2 修改配置pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>gmall-parent</artifactId>
        <groupId>com.atguigu.gmall</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>gmall-model</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <!--mybatis-plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <scope>provided</scope>
        </dependency>

        <!--swagger-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <scope>provided </scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
            <scope>provided </scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-mongodb</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
        </dependency>
    </dependencies>
</project>

3.6 搭建gmall-service父模块

gmall-service:service模块父节点

  • service-product:商品服务模块
  • service-xxxx:其他服务模块

3.6.1 搭建gmall-service

选中gmall-parent父工程,新建子模块:gmall-service。搭建过程同common父模块

image-20221212002754023

3.6.2 修改配置pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>gmall-parent</artifactId>
        <groupId>com.atguigu.gmall</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>gmall-service</artifactId>
    
    
    <packaging>pom</packaging>
    <dependencies>
        <!--依赖服务的工具类-->
        <dependency>
            <groupId>com.atguigu.gmall</groupId>
            <artifactId>service-util</artifactId>
            <version>1.0</version>
        </dependency>
        <!--数据载体-->
        <dependency>
            <groupId>com.atguigu.gmall</groupId>
            <artifactId>gmall-model</artifactId>
            <version>1.0</version>
        </dependency>

        <!--web 需要启动项目-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--mybatis-plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
        </dependency>

        <!--mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <!-- 服务注册 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!-- 服务配置-->
   <!--     <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>-->

        <!-- 服务调用feign -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <!-- 流量控制 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>

        <!--开发者工具-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency>

        <!--链路追踪-->
       <!-- <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zipkin</artifactId>
        </dependency>-->

    </dependencies>
</project>

3.7 product商品模块

3.7.1 搭建service-product模块

  1. gmall-service父工程下新增模块:service-product

image-20221127230022723

image-20221127230039733

3.7.2 启动类

创建启动类 包名:com.atguigu.gmall 类名:ProductApp

package com.atguigu.gmall;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * @author: atguigu
 * @create: 2023-07-22 11:50
 */
@SpringBootApplication
public class ProductApp {

    public static void main(String[] args) {
        SpringApplication.run(ProductApp.class, args);
    }
}

3.7.3 配置文件

  1. 在resources下新建配置文件application.yml 说明:后期统一上传nacos配置中心

    server:
     port: 8206
    spring:
     application:
       name: service-product
     datasource:
       type: com.zaxxer.hikari.HikariDataSource
       driver-class-name: com.mysql.jdbc.Driver
       url: jdbc:mysql://192.168.200.128:3306/gmall_product?characterEncoding=utf-8&useSSL=false
       username: root
       password: 123456
       hikari:
         connection-test-query: SELECT 1 # 自动检测连接
         connection-timeout: 60000 #数据库连接超时时间,默认30秒
         idle-timeout: 500000 #空闲连接存活最大时间,默认600000(10分钟)
         max-lifetime: 540000 #此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认1800000即30分钟
         maximum-pool-size: 12 #连接池最大连接数,默认是10
         minimum-idle: 10 #最小空闲连接数量
         pool-name: SPHHikariPool # 连接池名称
     cloud:
       nacos:
         discovery:
           server-addr: 192.168.200.128:8848
    mybatis-plus:
     configuration:
       # sql输出到控制台,方便开发调试
       log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    

4. 商品的基本知识

4.1 基本信息—分类

一般情况可以分为两级或者三级。咱们的项目一共分为三级,即一级分类、二级分类、三级分类。

比如:家用电器是一级分类,电视是二级分类,那么超薄电视就是三级分类。

img

数据库结构

img

SELECT base_category1.id,base_category1.name,base_category2.id,base_category2.name,base_category3.id,base_category3.name
FROM base_category1
LEFT JOIN base_category2 
ON base_category1.id = base_category2.category1_id
LEFT JOIN base_category3 
ON base_category2.id = base_category3.category2_id

4.2 基本信息—平台属性

平台属性和平台属性值

img

平台属性和平台属性值主要用于商品的检索,每个分类对应的属性都不同,分类包含一级分类、二级分类和三级分类,分类层级区分对应分类。

img

4.3 基本信息—销售属性与销售属性值

销售属性,就是商品详情页右边,可以通过销售属性来定位一组spu下的哪款sku。可以让当前的商品详情页,跳转到自己的“兄弟”商品。

一般每种商品的销售属性不会太多,大约1-4种。整个平台的属性种类也不会太多,大概10种以内。比如:颜色、尺寸、版本、套装等等。

img

img

4.4 基本信息—SPU与SKU

SKU=Stock Keeping Unit(库存量单位)。即库存进出计量的基本单元,可以是以件,盒,托盘等为单位。SKU这是对于大型连锁超市DC(配送中心)物流管理的一个必要的方法。现在已经被引申为产品统一编号的简称,每种产品均对应有唯一的SKU号。

SPU(Standard Product Unit):标准化产品单元。是商品信息聚合的最小单位,是一组可复用、易检索的标准化信息的集合,该集合描述了一个产品的特性。

首先通过检索搜索出来的商品列表中,每个商品都是一个sku。每个sku都有自己独立的库存数。也就是说每一个商品详情展示都是一个sku。

那spu又是干什么的呢?

img

如上图,一般的电商系统你点击进去以后,都能看到这个商品关联了其他好几个类似的商品,而且这些商品很多的信息都是共用的,比如商品图片,海报、销售属性等。

那么系统是靠什么把这些sku识别为一组的呢,那是这些sku都有一个公用的spu信息。而它们公共的信息,都放在spu信息下。所以,sku与spu的结构如下:

img

图中有两个图片信息表,其中spu_image表示整个spu相关下的所有图片信息,而sku_image表示这个spu下的某个sku使用的图片。sku_image中的图片是从spu_image中选取的。但是由于一个spu下的所有sku的海报都是一样,所以只存一份spu_poster就可以了。

5. 分类及属性查询

重点:清楚分类、品牌的表关系,独立完成分类查询的后端代码,并返回正确结果

商城的核心自然是商品,而商品多了以后,肯定要进行分类,并且不同的商品会有不同的品牌信息

分类 与 商品 是【一对多】关系

分类 与 品牌 是【多对多】关系,需要一张中间表来维护

品牌 与 商品 是【一对多】关系

简单理解:

  • 一个商品分类下有很多商品 (手机分类下有很多手机商品)
  • 一个商品分类下有很多品牌(手机分类下有苹果、华为、小米)
  • 一个品牌可以属于不同的分类 (华为品牌可以属于手机分类、也可以属于笔记本电脑分类)
  • 一个品牌下也会有很多商品 (华为品牌下可以P40、Meta40、P50 多种商品)

5.1 三级分类查询

YAPI接口文档地址:

5.1.1 控制层

编写一个controller一般需要知道四个内容,这些内容来源于开发接口文档:

  • 请求方式:决定我们用GetMapping还是PostMapping或者其他
  • 请求路径:决定映射路径
  • 请求参数:决定方法的参数
  • 返回值结果:决定方法的返回值

BaseCategoryApiController代码:

package com.atguigu.gmall.product.controller;

import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.product.model.BaseCategory1;
import com.atguigu.gmall.product.model.BaseCategory2;
import com.atguigu.gmall.product.model.BaseCategory3;
import com.atguigu.gmall.product.service.BaseCategory1Service;
import com.atguigu.gmall.product.service.BaseCategory2Service;
import com.atguigu.gmall.product.service.BaseCategory3Service;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.List;

/**
 * @author: atguigu
 * @create: 2023-08-28 15:54
 */
@RestController
@RequestMapping("/admin/product")
public class BaseCategoryController {


    @Autowired
    private BaseCategory1Service baseCategory1Service;

    @Resource //先根据BeanId(对象名称)找对象;再根据类型
    private BaseCategory2Service baseCategory2Service;

    @Autowired
    private BaseCategory3Service baseCategory3Service;

    /**
     * 查询所有一级分类列表
     *
     * @return
     */
    @ApiOperation("查询所有一级分类列表")
    @GetMapping("/getCategory1")
    public Result getCategory1() {
        List<BaseCategory1> list = baseCategory1Service.getCategory1();
        return Result.ok(list);
    }

    /**
     * 查询指定一级分类下二级分类列表
     *
     * @param category1Id
     * @return
     */
    @ApiOperation("查询指定一级分类下二级分类列表")
    @GetMapping("/getCategory2/{category1Id}")
    public Result getCategory2(@PathVariable("category1Id") Long category1Id) {
        List<BaseCategory2> list = baseCategory2Service.getCategory2(category1Id);
        return Result.ok(list);
    }

    /**
     * 查询指定二级分类下三级分类列表
     *
     * @param category2Id
     * @return
     */
    @ApiOperation("查询指定二级分类下三级分类列表")
    @GetMapping("/getCategory3/{category2Id}")
    public Result getCategory3(@PathVariable("category2Id") Long category2Id) {
        List<BaseCategory3> list = baseCategory3Service.getCategory3(category2Id);
        return Result.ok(list);
    }
}

5.1.2 业务层

5.1.2.1 Service

包名:com.atguigu.gmall.product.service 接口名:BaseCategory1|2|3Service

package com.atguigu.gmall.product.service;

import com.atguigu.gmall.product.model.BaseCategory1;
import com.baomidou.mybatisplus.extension.service.IService;

import java.util.List;

/**
 * @author: atguigu
 * @create: 2023-07-22 15:22
 */
public interface BaseCategory1Service extends IService<BaseCategory1> {
    /**
     * 查询所有一级分类列表
     *
     * @return
     */
    List<BaseCategory1> getCategory1();
}

package com.atguigu.gmall.product.service;

import com.atguigu.gmall.product.model.BaseCategory2;
import com.baomidou.mybatisplus.extension.service.IService;

import java.util.List;

/**
 * @author: atguigu
 * @create: 2023-07-22 15:22
 */
public interface BaseCategory2Service extends IService<BaseCategory2> {
    /**
     * 根据一级分类ID查询该分类下二级分类列表
     * @param category1Id
     * @return
     */
    List<BaseCategory2> getCategory2(Long category1Id);
}
package com.atguigu.gmall.product.service;

import com.atguigu.gmall.product.model.BaseCategory3;
import com.baomidou.mybatisplus.extension.service.IService;

import java.util.List;

/**
 * @author: atguigu
 * @create: 2023-07-22 15:22
 */
public interface BaseCategory3Service extends IService<BaseCategory3> {
    /**
     * 根据二级分类ID查询该分类下三级分类列表
     * @param category2Id
     * @return
     */
    List<BaseCategory3> getCategory3(Long category2Id);
}

5.1.2.2 ServiceImpl

包名:com.atguigu.gmall.product.service.impl 类名:BaseCategoryServiceImpl

package com.atguigu.gmall.product.service.impl;

import com.atguigu.gmall.product.mapper.BaseCategory1Mapper;
import com.atguigu.gmall.product.model.BaseCategory1;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * @author: atguigu
 * @create: 2023-08-28 15:53
 */
@Service
public class BaseCategory1ServiceImpl extends ServiceImpl<BaseCategory1Mapper, BaseCategory1> implements com.atguigu.gmall.product.service.BaseCategory1Service {

    /**
     * 查询所有一级分类列表
     *
     * @return
     */
    @Override
    public List<BaseCategory1> getCategory1() {
        //1.构建查询条件对象
        //QueryWrapper<BaseCategory1> queryWrapper = new QueryWrapper<>();
        ////执行查询字段
        //queryWrapper.select("id", "name");


        LambdaQueryWrapper<BaseCategory1> queryWrapper1 = new LambdaQueryWrapper<>();
        queryWrapper1.select(BaseCategory1::getId, BaseCategory1::getName);
        //方式一:调用持久层实现查询
        //baseMapper.selectList(queryWrapper);
        //方式二:调用业务层实现查询
        return this.list(queryWrapper1);
    }
}
package com.atguigu.gmall.product.service.impl;

import com.atguigu.gmall.product.mapper.BaseCategory2Mapper;
import com.atguigu.gmall.product.model.BaseCategory2;
import com.atguigu.gmall.product.service.BaseCategory2Service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * @author: atguigu
 * @create: 2023-08-28 16:21
 */
@Service
public class BaseCategory2ServiceImpl extends ServiceImpl<BaseCategory2Mapper, BaseCategory2> implements BaseCategory2Service {

    /**
     * 查询指定一级分类下二级分类列表
     *
     * @param category1Id
     * @return
     */
    @Override
    public List<BaseCategory2> getCategory2(Long category1Id) {
        //1.构建查询条件 指定查询字段
        LambdaQueryWrapper<BaseCategory2> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(BaseCategory2::getCategory1Id, category1Id)
                .select(BaseCategory2::getId, BaseCategory2::getName);

        //2.执行查询列表
        return this.list(queryWrapper);
    }
}
package com.atguigu.gmall.product.service.impl;

import com.atguigu.gmall.product.mapper.BaseCategory3Mapper;
import com.atguigu.gmall.product.model.BaseCategory2;
import com.atguigu.gmall.product.model.BaseCategory3;
import com.atguigu.gmall.product.service.BaseCategory3Service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * @author: atguigu
 * @create: 2023-08-28 16:26
 */
@Service
public class BaseCategory3ServiceImpl extends ServiceImpl<BaseCategory3Mapper, BaseCategory3> implements BaseCategory3Service {

    /**
     * 查询指定二级分类下三级分类列表
     *
     * @param category2Id
     * @return
     */
    @Override
    public List<BaseCategory3> getCategory3(Long category2Id) {
        //1.构建查询条件 指定查询字段
        LambdaQueryWrapper<BaseCategory3> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(BaseCategory3::getCategory2Id, category2Id)
                .select(BaseCategory3::getId, BaseCategory3::getName);

        //2.执行查询列表
        return this.list(queryWrapper);
    }
}

5.1.3 持久层

包名:com.atguigu.gmall.product.mapper

接口名:BaseCategory1Mapper BaseCategory2Mapper BaseCategory3Mapper

package com.atguigu.gmall.product.mapper;

import com.atguigu.gmall.product.model.BaseCategory1;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;

public interface BaseCategory1Mapper extends BaseMapper<BaseCategory1> {
}

package com.atguigu.gmall.product.mapper;

import com.atguigu.gmall.product.model.BaseCategory2;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;

public interface BaseCategory2Mapper extends BaseMapper<BaseCategory2> {
}

package com.atguigu.gmall.product.mapper;

import com.atguigu.gmall.product.model.BaseCategory3;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;

public interface BaseCategory3Mapper extends BaseMapper<BaseCategory3> {
}

5.2 平台属性查询

根据分类Id 获取平台属性集合:http://192.168.200.128:3000/project/11/interface/api/267

5.2.1 控制层

BaseAttrInfoController

package com.atguigu.gmall.product.controller;

import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.product.model.BaseAttrInfo;
import com.atguigu.gmall.product.service.BaseAttrInfoService;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * @author: atguigu
 * @create: 2023-08-28 16:36
 */
@RestController
@RequestMapping("/admin/product")
public class BaseAttrInfoController {

    @Autowired
    private BaseAttrInfoService baseAttrInfoService;

    /**
     * 根据三级分类ID查询包含平台属性列表(包含属性值)
     *
     * @param category1Id
     * @param category2Id
     * @param category3Id
     * @return
     */
    @ApiOperation("根据三级分类ID查询包含平台属性列表(包含属性值)")
    @GetMapping("/attrInfoList/{category1Id}/{category2Id}/{category3Id}")
    public Result getAttrInfoList(
            @PathVariable("category1Id") Long category1Id,
            @PathVariable("category2Id") Long category2Id,
            @PathVariable("category3Id") Long category3Id
    ) {
        List<BaseAttrInfo> list = baseAttrInfoService.getAttrInfoList(category1Id, category2Id, category3Id);
        return Result.ok(list);
    }
}

5.2.2 业务层

BaseAttrInfoService

package com.atguigu.gmall.product.service;

import com.atguigu.gmall.product.model.BaseAttrInfo;
import com.baomidou.mybatisplus.extension.service.IService;

import java.util.List;

public interface BaseAttrInfoService  extends IService<BaseAttrInfo> {
    /**
     * 根据三级分类ID查询包含平台属性列表(包含属性值)
     *
     * @param category1Id
     * @param category2Id
     * @param category3Id
     * @return
     */
    List<BaseAttrInfo> getAttrInfoList(Long category1Id, Long category2Id, Long category3Id);
}

BaseAttrInfoServiceImpl

package com.atguigu.gmall.product.service.impl;

import com.atguigu.gmall.product.mapper.BaseAttrInfoMapper;
import com.atguigu.gmall.product.model.BaseAttrInfo;
import com.atguigu.gmall.product.service.BaseAttrInfoService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * @author: atguigu
 * @create: 2023-08-28 16:34
 */
@Service
public class BaseAttrInfoServiceImpl extends ServiceImpl<BaseAttrInfoMapper, BaseAttrInfo> implements BaseAttrInfoService {

    /**
     * 根据三级分类ID查询包含平台属性列表(包含属性值)
     *
     * @param category1Id
     * @param category2Id
     * @param category3Id
     * @return
     */
    @Override
    public List<BaseAttrInfo> getAttrInfoList(Long category1Id, Long category2Id, Long category3Id) {
        //1.获取持久层mpper
        //调用mapper动态SQL
        return baseMapper.getAttrInfoList(category1Id, category2Id, category3Id);
    }
}

5.3.2 持久层

Mapper

package com.atguigu.gmall.product.mapper;

import com.atguigu.gmall.product.model.BaseAttrInfo;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;

public interface BaseAttrInfoMapper  extends BaseMapper<BaseAttrInfo> {
    /**
     * 根据三级分类ID查询包含平台属性列表(包含属性值)
     * @param category1Id
     * @param category2Id
     * @param category3Id
     * @return
     */
    List<BaseAttrInfo> getAttrInfoList(@Param("category1Id") Long category1Id,@Param("category2Id") Long category2Id, @Param("category3Id") Long category3Id);


}

MapperXml

在resources目录添加mapper文件夹,添加 BaseAttrInfoMapper.xml文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.atguigu.gmall.product.mapper.BaseAttrInfoMapper">


    <!--平台属性跟平台属性值:一对多配置-->
    <resultMap id="baseAttrMap" type="com.atguigu.gmall.product.model.BaseAttrInfo" autoMapping="true">
        <id property="id" column="id"></id>
        <!--配置一方中多方集合属性-->
        <collection property="attrValueList" ofType="com.atguigu.gmall.product.model.BaseAttrValue" autoMapping="true">
            <id property="id" column="base_attr_value_id"></id>
        </collection>
    </resultMap>


    <!--根据三级分类ID查询包含平台属性列表(包含属性值)-->
    <select id="getAttrInfoList" resultMap="baseAttrMap">
        SELECT
            bai.id,
            bai.attr_name,
            bai.category_id,
            bai.category_level,
            bav.id base_attr_value_id,
            bav.value_name
        FROM
        base_attr_info bai
        INNER JOIN base_attr_value bav ON bav.attr_id = bai.id
        <where>
            <if test="category1Id != null and category1Id != ''">
                <trim prefix="(" suffix=")">
                    bai.category_id = #{category1Id} AND bai.category_level = 1
                </trim>
            </if>
            <if test="category2Id != null and category2Id != ''">
                OR
                <trim prefix="(" suffix=")">
                    bai.category_id = #{category2Id} AND bai.category_level = 2
                </trim>
            </if>
            <if test="category3Id != null and category3Id != ''">
                OR
                <trim prefix="(" suffix=")">
                    bai.category_id = #{category3Id} AND bai.category_level = 3
                </trim>
            </if>
            AND bai.is_deleted = 0
            AND bav.is_deleted = 0
        </where>
    </select>
</mapper>

6. 接口测试工具

6.1 Swagger介绍

(1)简介

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

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

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

  3. 功能测试

Spring已经将Swagger纳入自身的标准,建立了Spring-swagger项目,现在叫Springfox。通过在项目中引入Springfox ,即可非常简单快捷的使用Swagger。

(2)SpringBoot集成Swagger

  • 引入依赖

  • service-util工程的config包中添加一个配置类

    package com.atguigu.gmall.common.config;
    
    import com.google.common.base.Predicates;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import springfox.documentation.builders.ApiInfoBuilder;
    import springfox.documentation.builders.ParameterBuilder;
    import springfox.documentation.builders.PathSelectors;
    import springfox.documentation.schema.ModelRef;
    import springfox.documentation.service.ApiInfo;
    import springfox.documentation.service.Contact;
    import springfox.documentation.service.Parameter;
    import springfox.documentation.spi.DocumentationType;
    import springfox.documentation.spring.web.plugins.Docket;
    import springfox.documentation.swagger2.annotations.EnableSwagger2;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
    * Swagger2配置信息
    */
    @Configuration
    @EnableSwagger2
    public class Swagger2Config {
    
    @Bean
    public Docket webApiConfig(){
    
        //添加head参数start
        List<Parameter> pars = new ArrayList<>();
        ParameterBuilder tokenPar = new ParameterBuilder();
        tokenPar.name("userId")
                .description("用户ID")
                .defaultValue("1")
                .modelRef(new ModelRef("string"))
                .parameterType("header")
                .required(false)
                .build();
        pars.add(tokenPar.build());
    
        ParameterBuilder tmpPar = new ParameterBuilder();
                tmpPar.name("userTempId")
                .description("临时用户ID")
                .defaultValue("1")
                .modelRef(new ModelRef("string"))
                .parameterType("header")
                .required(false)
                .build();
        pars.add(tmpPar.build());
        //添加head参数end
    
        return new Docket(DocumentationType.SWAGGER_2)
                .groupName("webApi")
                .apiInfo(webApiInfo())
                .select()
                //过滤掉admin路径下的所有页面
                .paths(Predicates.and(PathSelectors.regex("/api/.*")))
                //过滤掉所有error或error.*页面
                //.paths(Predicates.not(PathSelectors.regex("/error.*")))
                .build()
                .globalOperationParameters(pars);
    
    }
    
    @Bean
    public Docket adminApiConfig(){
    
        return new Docket(DocumentationType.SWAGGER_2)
                .groupName("adminApi")
                .apiInfo(adminApiInfo())
                .select()
                //只显示admin路径下的页面
                .paths(Predicates.and(PathSelectors.regex("/admin/.*")))
                .build();
    
    }
    
    private ApiInfo webApiInfo(){
    
        return new ApiInfoBuilder()
                .title("网站-API文档")
                .description("本文档描述了网站微服务接口定义")
                .version("1.0")
                .contact(new Contact("Helen", "http://atguigu.com", "55317332@qq.com"))
                .build();
    }
    
    private ApiInfo adminApiInfo(){
    
        return new ApiInfoBuilder()
                .title("后台管理系统-API文档")
                .description("本文档描述了后台管理系统微服务接口定义")
                .version("1.0")
                .contact(new Contact("Helen", "http://atguigu.com", "55317332@qq.com"))
                .build();
    }
    
    }
    

service-product模块中启动类会自动扫描到该配置类

(3)Swagger常用注解

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

@Api:修饰整个类,描述Controller的作用

tags 指定名称

@ApiOperation:描述一个类的一个方法,或者说一个接口

value 指定名称 notes 添加备注

@ApiParam:单个参数的描述信息

@ApiModel:用对象来接收参数

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

value 指定字段名称

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

@ApiResponses:HTTP响应整体描述

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

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

@ApiImplicitParam:一个请求参数

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

6.2 knife4j

(1)简介

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

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)快速集成

  • gmall-common模块中的pom.xml文件中引入knife4j的依赖(已导入),如下:

    <dependency>
     <groupId>com.github.xiaoymin</groupId>
     <artifactId>knife4j-spring-boot-starter</artifactId>
    </dependency>
    
  • 创建Knife4J配置文件

service-util模块中新建包package com.atguigu.gmall.common.config,在此包中新建配置类,代码如下:

package com.atguigu.gmall.common.config;

import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
import com.google.common.base.Predicates;
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.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.service.Parameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import java.util.ArrayList;
import java.util.List;

/**
 * Swagger2配置信息
 */
@Configuration
@EnableSwagger2
@EnableKnife4j
@Import(BeanValidatorPluginsConfiguration.class)
public class Knife4jConfig {

    @Bean
    public Docket webApiConfig(){

        //添加head参数start
        List<Parameter> pars = new ArrayList<>();
        ParameterBuilder tokenPar = new ParameterBuilder();
        tokenPar.name("userId")
                .description("用户ID")
                .defaultValue("1")
                .modelRef(new ModelRef("string"))
                .parameterType("header")
                .required(false)
                .build();
        pars.add(tokenPar.build());

        ParameterBuilder tmpPar = new ParameterBuilder();
                tmpPar.name("userTempId")
                .description("临时用户ID")
                .defaultValue("1")
                .modelRef(new ModelRef("string"))
                .parameterType("header")
                .required(false)
                .build();
        pars.add(tmpPar.build());
        //添加head参数end

        return new Docket(DocumentationType.SWAGGER_2)
                .groupName("webApi")
                .apiInfo(webApiInfo())
                .select()
                //过滤掉admin路径下的所有页面
                .paths(Predicates.and(PathSelectors.regex("/api/.*")))
                //过滤掉所有error或error.*页面
                //.paths(Predicates.not(PathSelectors.regex("/error.*")))
                .build()
                .globalOperationParameters(pars);

    }

    @Bean
    public Docket adminApiConfig(){

        return new Docket(DocumentationType.SWAGGER_2)
                .groupName("adminApi")
                .apiInfo(adminApiInfo())
                .select()
                //只显示admin路径下的页面
                .paths(Predicates.and(PathSelectors.regex("/admin/.*")))
                .build();

    }

    private ApiInfo webApiInfo(){

        return new ApiInfoBuilder()
                .title("网站-API文档")
                .description("本文档描述了网站微服务接口定义")
                .version("1.0")
                .contact(new Contact("Helen", "http://atguigu.com", "55317332@qq.com"))
                .build();
    }

    private ApiInfo adminApiInfo(){

        return new ApiInfoBuilder()
                .title("后台管理系统-API文档")
                .description("本文档描述了后台管理系统微服务接口定义")
                .version("1.0")
                .contact(new Contact("Helen", "http://atguigu.com", "55317332@qq.com"))
                .build();
    }
}

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

注解 说明
@EnableSwagger2 该注解是Springfox-swagger框架提供的使用Swagger注解,该注解必须加
@EnableKnife4j 该注解是knife4j提供的增强注解,Ui提供了例如动态参数、参数过滤、接口排序等增强功能,如果你想使用这些增强功能就必须加该注解,否则可以不用加
  • tips:Knife4j中以及包含了Swagger所以要将Swagger的配置类注释掉,否则会有冲突

image-20221129000413774

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

image-20221129000308206

7 使用域名访问本地项目

7.1 统一环境

我们现在访问页面使用的是:

http://localhost:port

有什么问题呢?

实际开发中,会有不同的环境:

  • 开发环境:自己的电脑
  • 测试环境:提供给测试人员使用的环境
  • 预发布环境:数据是和生成环境的数据一致,运行最新的项目代码进去测试
  • 生产环境:项目最终发布上线的环境

如果不同环境使用不同的ip去访问,可能会出现一些问题。为了保证所有环境的一致,我们会在各种环境下都使用域名来访问。

我们将使用以下域名:

主域名是:www.gmall.com - 门户系统

检索系统域名:list.gmall.com

商品系统域名:item.gmall.com

购物车系统域名:cart.gmall.com

登录系统域名: passport.gmal.com

订单系统域名:order.gmall.com

支付系统域名:payment.gmall.com

活动系统域名:activity.gmall.com

网关域名:api.gmall.com

最终,我们希望这些域名指向的还是我们本机的某个端口。

那么,当我们在浏览器输入一个域名时,浏览器是如何找到对应服务的ip和端口的呢?

7.2 域名解析

一个域名一定会被解析为一个或多个ip。这一般会包含两步:

  • 本地域名解析

浏览器会首先在本机的hosts文件中查找域名映射的IP地址,如果查找到就返回IP ,没找到则进行域名服务器解析,一般本地解析都会失败,因为默认这个文件是空的。

  • Windows下的hosts文件地址:

    C:/Windows/System32/drivers/etc/hosts

  • Linux下的hosts文件所在路径:

    /etc/hosts

示例如下:

  # gmall
  127.0.0.1 www.gmall.com item.gmall.com order.gmall.com payment.gmall.com 
  127.0.0.1 activity.gmall.com passport.gmall.com cart.gmall.com list.gmall.com api.gmall.com
  • 域名服务器解析

本地解析失败,才会进行域名服务器解析,域名服务器就是网络中的一台计算机,里面记录了所有注册备案的域名和ip映射关系,一般只要域名是正确的,并且备案通过,一定能找到。

image-20230829092446430

注意:手动修改hosts文件不会立即生效,需要刷新本地DNS才会生效

在命令行中执行命令刷新:

ipconfig/flushdns

image-20221223102851075