Nacos服务注册中心

Nacos服务注册中心

什么是Nacos

Nacos是Spring Cloud Alibaba提供的一个软件

这个软件主要具有注册中心和配置中心(课程最后讲解)的功能

我们先学习它注册中心的功能

微服务中所有项目都必须注册到注册中心才能成为微服务的一部分

注册中心和企业中的人力资源管理部门有相似

当前微服务项目中所有的模块,在启动前,必须添加注册到Nacos的配置

所谓注册,就是将自己的信息,提交到Nacos来保存

在这里插入图片描述

Nacos的下载

https://github.com/alibaba/nacos/releases/download/1.4.3/nacos-server-1.4.3.zip

国外网站,下载困难可以多试几次

Nacos的启动

因为Nacos是java开发的

我们要启动Nacos必须保证当前系统配置了java环境变量

简单来说就是要环境变量中,有JAVA_HOME的配置,指向安装jdk的路径

确定了支持java后,就可以启动Nacos了

mac系统同学一定要到http://doc.vrd.net.cn/查看homebrew相关知识

mac系统安装Nacos推荐

https://blog.csdn.net/gongzi_9/article/details/123359171

windows的同学保证java环境变量正常后

将nacos-server-1.4.2.zip压缩包解压

双击打开解压得到的文件夹后,再双击打开其中的bin目录

在这里插入图片描述

cmd结尾的文件是windows版本的

sh结尾的文件是linux和mac版本的

startup是启动文件,shutdown是停止文件

注意: Windows下启动Nacos不能直接双击cmd文件

需要在dos窗口运行

在当前资源管理器地址栏输入cmd

E:\tools\nacos\bin>startup.cmd -m standalone

startup.cmd:windows启动nacos的命令文件

-m 表示要设置启动参数

standalone:翻译为标准的孤独的,意思是正常的使用单机模式启动

运行成功默认占用8848端口,并且在代码中提示

如果不输入standalone运行会失败

如果报了

“please set JAVA_HOME…”

表示当前项目没有配置java环境变量(主要是没有设置JAVA_HOME)

如果运行没有报错

打开浏览器输入地址

http://localhost:8848/nacos

如果是首次访问,会出现这个界面

在这里插入图片描述

登录系统

用户名:nacos

密码:nacos

登录之后可以进入后台列表

不能关闭启动nacos的dos窗口

我们要让我们编写的项目注册到Nacos,才能真正是微服务项目

创建csmall项目

我们要搭建一个项目,用于学习各种微服务知识

搭建的过程有很多新的标准,需要我们掌握和学习

业务概述

我们通过学习电商网站添加订单的业务来学习需要使用到的微服务组件

我们模拟用户选中了购物车中的商品后,点击"确认订单"按钮后的业务操作

1.如果库存足够,就减少用户购买的数量(sku表中的库存)

2.从购物车中删除用户勾选的商品

3.生成订单,将订单信息保存到数据库

我们将上面库存,购物车和订单设计为3个模块

  • 库存模块:管理库存,减少库存
  • 购物车模块:管理购物车,删除购物车信息
  • 订单模块:新增订单

除此之外,我们还要创建一个触发新增订单业务的模块

  • 业务触发模块

创建csmall父项目

创建项目名称csmall

我们微服务开发过程中,一般都会使用一个Idea中包含多个项目的形式

这个形式就是先创建一个"父项目",再向这个父项目中创建多个子项目的操作

我们首先创建一个项目:csmall

注意细节的修改

在这里插入图片描述

上面图片next之后,修改SpringBoot版本为2.7.x,之后点击finish即可

首先,将当前csmall项目修剪为父项目的格式

  • 删除csmall项目的src文件夹,因为父项目不写代码
  • 修改pom文件

最终的父项目pom文件

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <!--    ↓↓↓↓↓↓↓      -->
        <version>2.5.9</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>cn.tedu</groupId>
    <artifactId>csmall</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>csmall</name>
    <description>Demo project for Spring Boot</description>
    <!--
        父项目结构是maven提供的,而pom.xml文件就是maven的配置
        下面的配置就是表示当前项目作为一个父项目pom文件,给子项目继承
    -->
    <packaging>pom</packaging>
</project>

创建子项目

创建csmall-stock项目

在这里插入图片描述

我们每次创建一个子项目之后

都要进行"父子相认"

在父项目的pom文件中,编写子项目的存在

<!--
    父项目结构是maven提供的,而pom.xml文件就是maven的配置
    下面的配置就是表示当前项目作为一个父项目pom文件,给子项目继承
-->
<packaging>pom</packaging>
<!--    声明当前父项目包含哪些子项目(子模块),module就是模块的意思   -->
<modules>
    <module>csmall-stock</module>
</modules>

父子相认完成

这样当前子项目就可以读取父项目中的pom文件信息了

删除stock模块src\test文件夹

父项目管理依赖版本

在我们现在使用maven添加依赖的认知中

有些依赖时必须添加版本号才能执行

有些依赖则不必添加版本号

原因是我们继承的SpringBoot(2.5.9)父项目中,定义了一些常用依赖的版本号

如果我们自己编写的父项目想定义我们项目中需要的依赖版本号的话,也是可以实现的

这样做可以统一所有子项目的版本,在更新版本时,只需要修改父项目中定义的版本号即可

父项目的pom文件添加如下内容

<!--   声明当前父项目包含哪些子项目模块,module就是模块的意思    -->
<modules>
    <module>csmall-stock</module>
</modules>
<!--   properties标签    -->
<properties>
    <java.version>1.8</java.version>
    <!--
            定义mybaits的版本号为2.2.2,本质上是定义了一个变量,
            这个变量名为mybatis.version值为2.2.2
    -->
    <mybatis.version>2.2.2</mybatis.version>
    
</properties>
<!--    下面开始在父项目中设置子项目添加依赖的版本,又名"锁版本"     -->
<!--    dependencyManagement标签中的内容,并不是要添加依赖,
        而是如果子项目添加依赖会使用设置好的版本   -->
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>${mybatis.version}</version>
        </dependency>
    </dependencies>
</dependencyManagement>

子项目中如果需要mybatis的依赖只需要添加如下内容即可,无需再指定版本号

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>

上面的操作也称之为"锁版本"

加载正式项目pom文件

因为我们学习微服务的过程中需要很多微服务相关的依赖

这些依赖都需要在父项目中进行版本的管理的

所以我们直接使用分享给大家的完整版项目的父项目pom文件即可

父项目完整最终pom文件如下

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.9</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>cn.tedu</groupId>
    <artifactId>csmall</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>csmall</name>
    <description>Demo project for Spring Boot</description>
    <!--
        当前项目要成为父项目,必须作为一个pom文件,给子项目继承
        这个父子项目继承的功能是maven提供的
    -->
    <packaging>pom</packaging>
    <!--   声明当前父项目包含哪些子项目模块,module就是模块的意思    -->
    <modules>
        <module>csmall-stock</module>
    </modules>
    <!--   properties标签    -->
    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>2020.0.3</spring-cloud.version>
        <spring-cloud-alibaba.version>2.2.2.RELEASE</spring-cloud-alibaba.version>
        <spring-boot.version>2.5.4</spring-boot.version>
        <spring-boot-configuration-processor.version>2.3.0.RELEASE</spring-boot-configuration-processor.version>
        <spring-security-jwt.version>1.0.10.RELEASE</spring-security-jwt.version>
        <mybatis-spring-boot.version>2.2.0</mybatis-spring-boot.version>
        <mybaits-plus.version>3.4.1</mybaits-plus.version>
        <pagehelper-spring-boot.version>1.4.0</pagehelper-spring-boot.version>
        <mysql.version>8.0.26</mysql.version>
        <lombok.version>1.18.20</lombok.version>
        <knife4j-spring-boot.version>2.0.9</knife4j-spring-boot.version>
        <spring-rabbit-test.version>2.3.10</spring-rabbit-test.version>
        <spring-security-test.version>5.5.2</spring-security-test.version>
        <fastjson.version>1.2.45</fastjson.version>
        <druid.version>1.1.20</druid.version>
        <jjwt.version>0.9.0</jjwt.version>
        <seata-server.version>1.4.2</seata-server.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>
    <!-- 依赖管理 -->
    <dependencyManagement>
        <dependencies>
            <!--seata-all-->
            <dependency>
                <groupId>io.seata</groupId>
                <artifactId>seata-all</artifactId>
                <version>${seata-server.version}</version>
            </dependency>
            <!-- Lombok -->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>${lombok.version}</version>
            </dependency>
            <!-- MySQL -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>${mysql.version}</version>
                <scope>runtime</scope>
            </dependency>
            <!-- Alibaba Druid -->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>${druid.version}</version>
            </dependency>
            <!-- MyBatis Spring Boot:数据访问层MyBatis编程 -->
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>${mybatis-spring-boot.version}</version>
            </dependency>
            <!-- MyBatis Plus Spring Boot:MyBatis增强 -->
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>${mybaits-plus.version}</version>
            </dependency>
            <!-- MyBatis Plus Generator:代码生成器 -->
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-generator</artifactId>
                <version>${mybaits-plus.version}</version>
            </dependency>
            <!-- PageHelper Spring Boot:MyBatis分页 -->
            <dependency>
                <groupId>com.github.pagehelper</groupId>
                <artifactId>pagehelper-spring-boot-starter</artifactId>
                <version>${pagehelper-spring-boot.version}</version>
            </dependency>
            <!-- Spring Boot:基础框架 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter</artifactId>
                <version>${spring-boot.version}</version>
            </dependency>
            <!-- Spring Boot Web:WEB应用 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
                <version>${spring-boot.version}</version>
            </dependency>
            <!-- Spring Boot Freemarker:MyBaits Plus Generator的辅助项 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-freemarker</artifactId>
                <version>${spring-boot.version}</version>
            </dependency>
            <!-- Spring Boot Validation:验证请求参数 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-validation</artifactId>
                <version>${spring-boot.version}</version>
            </dependency>
            <!-- Spring Boot Security:认证授权 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-security</artifactId>
                <version>${spring-boot.version}</version>
            </dependency>
            <!-- Spring Boot Oauth2:认证授权 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-oauth2-client</artifactId>
                <version>${spring-boot.version}</version>
            </dependency>
            <!-- Spring Boot配置处理器 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-configuration-processor</artifactId>
                <version>${spring-boot-configuration-processor.version}</version>
            </dependency>
            <!-- Spring Security JWT -->
            <dependency>
                <groupId>org.springframework.security</groupId>
                <artifactId>spring-security-jwt</artifactId>
                <version>${spring-security-jwt.version}</version>
            </dependency>
            <!-- Knife4j Spring Boot:在线API -->
            <dependency>
                <groupId>com.github.xiaoymin</groupId>
                <artifactId>knife4j-spring-boot-starter</artifactId>
                <version>${knife4j-spring-boot.version}</version>
            </dependency>
            <!-- Spring Boot Data Redis:缓存 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-redis</artifactId>
                <version>${spring-boot.version}</version>
            </dependency>
            <!-- Spring Boot Data MongoDB:缓存 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-mongodb</artifactId>
                <version>${spring-boot.version}</version>
            </dependency>
            <!-- Spring Boot Data Elasticsearch:文档搜索 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
                <version>${spring-boot.version}</version>
            </dependency>
            <!-- Spring Boot AMQP:消息队列 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-amqp</artifactId>
                <version>${spring-boot.version}</version>
            </dependency>
            <!-- Spring Boot Actuator:健康监测 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</artifactId>
                <version>${spring-boot.version}</version>
            </dependency>
            <!-- Spring Cloud家族 -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!-- Spring Cloud Alibaba -->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring-cloud-alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!-- Alibaba FastJson -->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>${fastjson.version}</version>
            </dependency>
            <!-- JJWT -->
            <dependency>
                <groupId>io.jsonwebtoken</groupId>
                <artifactId>jjwt</artifactId>
                <version>${jjwt.version}</version>
            </dependency>
            <!-- Spring Boot Test:测试 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <version>${spring-boot.version}</version>
                <scope>test</scope>
            </dependency>
            <!-- Spring Rabbit Test:消息队列测试 -->
            <dependency>
                <groupId>org.springframework.amqp</groupId>
                <artifactId>spring-rabbit-test</artifactId>
                <version>${spring-rabbit-test.version}</version>
                <scope>test</scope>
            </dependency>
            <!-- Spring Security Test:Security测试 -->
            <dependency>
                <groupId>org.springframework.security</groupId>
                <artifactId>spring-security-test</artifactId>
                <version>${spring-security-test.version}</version>
                <scope>test</scope>
            </dependency>
            <!--seata整合springboot-->
            <dependency>
                <groupId>io.seata</groupId>
                <artifactId>seata-spring-boot-starter</artifactId>
                <version>${seata-server.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

创建通用模块(项目)commons

创建项目

在实际开发中

经常会出现有些类需要在多个微服务项目中使用的情况

为了减少代码的冗余

我们在父项目中创建一个子项目csmall-commons专门保存编写这样的类

然后哪个微服务需要使用,就添加对commons的依赖即可

我们先来创建csmall-commons这个项目

父子相认

<modules>
    <module>csmall-stock</module>
    <module>csmall-commons</module>
</modules>

子项目pom文件最终如下

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>cn.tedu</groupId>
        <artifactId>csmall</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>cn.tedu</groupId>
    <artifactId>csmall-commons</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>csmall-commons</name>
    <description>Demo project for Spring Boot</description>
    <dependencies>
        <!--在线api文档-->
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-spring-boot-starter</artifactId>
        </dependency>
        <!-- Spring Boot Web:WEB应用 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-json</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

</project>

当前模块只是编写通用类和代码

实际上不需要运行

所以删除运行相关的文件

删除test测试文件夹

删除resources文件夹

删除SpringBoot启动类

创建实体类

带大家编写一个我们需要的实体类

创建cn.tedu.csmall.commons.pojo.cart.dto

  • DTO:前端收集到数据发送给后端的信息

  • VO:后端从数据库查询出来要发送给前端的信息

包中创建CartAddDTO,代码如下

@Data
@ApiModel
public class CartAddDTO implements Serializable {

    @ApiModelProperty(value = "商品编号",name="commodityCode",example = "PC100")
    private String commodityCode;
    @ApiModelProperty(value = "价格",name="price",example = "20")
    private Integer price;
    @ApiModelProperty(value = "数量",name="count",example = "2")
    private Integer count;
    @ApiModelProperty(value = "用户Id",name="userId",example = "UU100")
    private String userId;
}

创建包

pojo.cart.entity

包中创建Cart

@Data
public class Cart implements Serializable {
    private Integer id;
    private String commodityCode;
    private Integer price;
    private Integer count;
    private String userId;
}

下面创建订单模块需要的类

pojo.order.dto.OrderAddDTO

@ApiModel("新增订单的DTO")
@Data
public class OrderAddDTO implements Serializable {
    @ApiModelProperty(value = "用户id",name="userId",example = "UU100")
    private String userId;
    @ApiModelProperty(value = "商品编号",name="commodityCode",example = "PC100")
    private String commodityCode;
    @ApiModelProperty(value = "商品数量",name="count",example = "5")
    private Integer count;
    @ApiModelProperty(value = "总金额",name="money",example = "50")
    private Integer money;
}

pojo.order.entity.Order

@Data
public class Order implements Serializable {
    private Integer id;
    private String userId;
    private String commodityCode;
    private Integer count;
    private Integer money;
}

最后是库存相关的类

pojo.stock.dto.StockReduceCountDTO

@ApiModel("商品减少库存DTO")
@Data
public class StockReduceCountDTO implements Serializable {
    @ApiModelProperty(value = "商品编号",name="commodityCode",example = "PC100")
    private String commodityCode;
    @ApiModelProperty(value = "减库存数",name="reduceCount",example = "5")
    private Integer reduceCount;
}

pojo.stock.entity.Stock

@Data
public class Stock implements Serializable {
    private Integer id;
    private String commodityCode;
    private Integer reduceCount;
}

创建异常相关类

除了实体类多个模块需要使用之外

像异常类和控制器返回的JsonResult类也是多个模块需要使用的类型

它们也要编写在commons中

创建cn.tedu.csmall.commons.restful包

在这个包中先创建异常响应码枚举

/**
 * 错误代码枚举类型
 */
public enum ResponseCode {

    OK(200),
    BAD_REQUEST(400),
    UNAUTHORIZED(401),
    FORBIDDEN(403),
    NOT_FOUND(404),
    NOT_ACCEPTABLE(406),
    CONFLICT(409),
    INTERNAL_SERVER_ERROR(500);

    private Integer value;

    ResponseCode(Integer value) {
        this.value = value;
    }

    public Integer getValue() {
        return value;
    }
}

下面定义自定义异常类

创建包cn.tedu.csmall.commons.exception

包中创建类CoolSharkServiceException

/**
 * 业务异常
 */
@Data
@EqualsAndHashCode(callSuper = false)
public class CoolSharkServiceException extends RuntimeException {

    private ResponseCode responseCode;

    public CoolSharkServiceException(ResponseCode responseCode, String message) {
        super(message);
        setResponseCode(responseCode);
    }
}

将restful包中用于控制器返回的JsonResult类复制

/**
 * 通用响应对象
 */
@Data
public class JsonResult<T> implements Serializable {

    /**
     * 状态码
     */
    @ApiModelProperty(value = "业务状态码", position = 1, example = "200, 400, 401, 403, 404, 409, 500")
    private Integer state;
    /**
     * 消息
     */
    @ApiModelProperty(value = "业务消息", position = 2, example = "登录失败!密码错误!")
    private String message;
    /**
     * 数据
     */
    @ApiModelProperty(value = "业务数据", position = 3)
    private T data;

    /**
     * 创建响应结果对象,表示"成功",不封装其它任何数据
     * @return 响应结果对象
     */
    public static JsonResult<Void> ok() {
        return ok("OK");
    }

    public static JsonResult ok(String message){
        JsonResult jsonResult=new JsonResult();
        jsonResult.setState(ResponseCode.OK.getValue());
        jsonResult.setMessage(message);
        jsonResult.setData(null);
        return jsonResult;
    }
    /**
     * 创建响应结果对象,表示"成功",且封装客户端期望响应的数据
     * @param data 客户端期望响应的数据
     * @return 响应结果对象
     */
    public static <T> JsonResult<T> ok(String message,T data) {
        JsonResult<T> jsonResult = new JsonResult<>();
        jsonResult.setState(ResponseCode.OK.getValue());
        jsonResult.setData(data);
        return jsonResult;
    }
    /**
     * 创建响应结果对象,表示"失败",且封装"失败"的描述
     *
     * @param e CoolSharkServiceException异常对象
     * @return 响应结果对象
     */
    public static JsonResult<Void> failed(CoolSharkServiceException e) {
        return failed(e.getResponseCode(), e);
    }

    /**
     * 创建响应结果对象,表示"失败",且封装"失败"的描述
     *
     * @param responseCode "失败"的状态码
     * @param e            "失败"时抛出的异常对象
     * @return 响应结果对象
     */
    public static JsonResult<Void> failed(ResponseCode responseCode, Throwable e) {
        return failed(responseCode, e.getMessage());
    }

    /**
     * 创建响应结果对象,表示"失败",且封装"失败"的描述
     *
     * @param responseCode "失败"的状态码
     * @param message      "失败"的描述文本
     * @return 响应结果对象
     */
    public static JsonResult<Void> failed(ResponseCode responseCode, String message) {
        JsonResult<Void> jsonResult = new JsonResult<>();
        jsonResult.setState(responseCode.getValue());
        jsonResult.setMessage(message);
        return jsonResult;
    }

}

我们编写的所有模块控制层发生异常时,也都由SpringMvc的统一异常处理类来处理

所以commons模块编写统一异常处理类也是常规操作

/**
 * 全局异常处理器
 */
@RestControllerAdvice
@Slf4j
public class GlobalControllerExceptionHandler {
    /**
     * 处理业务异常
     */
    @ExceptionHandler({CoolSharkServiceException.class})
    public JsonResult<Void> handleCoolSharkServiceException(CoolSharkServiceException e) {
        log.debug("出现业务异常,业务错误码={},描述文本={}", e.getResponseCode().getValue(), e.getMessage());
        e.printStackTrace();
        JsonResult<Void> result = JsonResult.failed(e);
        log.debug("即将返回:{}", result);
        return result;
    }

    /**
     * 处理绑定异常(通过Validation框架验证请求参数时的异常)
     */
    @ExceptionHandler(BindException.class)
    public JsonResult<Void> handleBindException(BindException e) {
        log.debug("验证请求数据时出现异常:{}", e.getClass().getName());
        e.printStackTrace();
        String message = e.getBindingResult().getFieldError().getDefaultMessage();
        JsonResult<Void> result = JsonResult.failed(ResponseCode.BAD_REQUEST, message);
        log.debug("即将返回:{}", result);
        return result;
    }

    /**
     * 处理系统(其它)异常
     */
    @ExceptionHandler({Throwable.class})
    public JsonResult<Void> handleSystemError(Throwable e) {
        log.debug("出现系统异常,异常类型={},描述文本={}", e.getClass().getName(), e.getMessage());
        e.printStackTrace();
        JsonResult<Void> result = JsonResult.failed(ResponseCode.INTERNAL_SERVER_ERROR, e);
        log.debug("即将返回:{}", result);
        return result;
    }
}

commons模块内容编写暂时告一段落

创建business模块

business:商业\生意\业务的意思

这个模块创建出来是为了触发订单业务的

在这里插入图片描述

创建项目

创建子项目csmall-business

父子相认

子项目pom文件

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>cn.tedu</groupId>
        <artifactId>csmall</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>cn.tedu</groupId>
    <artifactId>csmall-business</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>csmall-business</name>
    <description>Demo project for Spring Boot</description>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.tedu</groupId>
            <artifactId>csmall-commons</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
    </dependencies>

</project>

删除测试的test文件夹

配置yml文件内容

business模块

创建application.yml文件,删除application.properties文件

yml文件内容如下

server:
  port: 20000
#公共配置
mybatis:
  configuration:
    cache-enabled: false   # 不启用mybatis二级缓存
    map-underscore-to-camel-case: true # 映射支持驼峰命名法
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 将运行的sql输出到控制台
knife4j:
  # 开启增强配置
  enable: true
  # 生产环境屏蔽,开启将禁止访问在线API文档
  production: false
  # Basic认证功能,即是否需要通过用户名、密码验证后才可以访问在线API文档
  basic:
    # 是否开启Basic认证
    enable: false
    # 用户名,如果开启Basic认证却未配置用户名与密码,默认是:admin/123321
    username: root
    # 密码
    password: root
spring:
  profiles:
    active: dev

我们在配置末尾看到了

spring:
  profiles:
    active: dev

上面的配置含义是让SpringBoot读取额外配置文件

我们参数值编写的是dev,是可以随意修改的,这里定义的名称,就是指定配置文件的名称

例如当前我们编写的dev,SpringBoot就会额外加载名称为application-dev.yml文件

我们创建application-dev.yml文件,以备添加配置

在这里插入图片描述

SpringBoot相关配置

创建config包,编写一些必要配置

首先创建CommonsConfiguration

// 当前类是配置Spring扫描环境的配置类,必须添加注解@Configuration才能生效
@Configuration
// 扫描统一异常处理类所在的包,也就是commons模块中的包,让异常处理类生效
@ComponentScan("cn.tedu.csmall.commons.exception")
public class CommonsConfiguration {
}

下面配置knife4JConfiguration

这个类直接复制即可

/**
 * Knife4j(Swagger2)的配置
 */
@Configuration
@EnableSwagger2WebMvc
public class Knife4jConfiguration {

    /**
     * 【重要】指定Controller包路径
     */
    private String basePackage = "cn.tedu.csmall.business.controller";
    /**
     * 分组名称
     */
    private String groupName = "base-business";
    /**
     * 主机名
     */
    private String host = "http://java.tedu.cn";
    /**
     * 标题
     */
    private String title = "酷鲨商城项目案例在线API文档--基础business-web实例";
    /**
     * 简介
     */
    private String description = "构建基础business-web项目,实现购买";
    /**
     * 服务条款URL
     */
    private String termsOfServiceUrl = "http://www.apache.org/licenses/LICENSE-2.0";
    /**
     * 联系人
     */
    private String contactName = "项目研发部";
    /**
     * 联系网址
     */
    private String contactUrl = "http://java.tedu.cn";
    /**
     * 联系邮箱
     */
    private String contactEmail = "java@tedu.cn";
    /**
     * 版本号
     */
    private String version = "1.0-SNAPSHOT";

    @Autowired
    private OpenApiExtensionResolver openApiExtensionResolver;

    @Bean
    public Docket docket() {
        String groupName = "1.0-SNAPSHOT";
        Docket docket = new Docket(DocumentationType.SWAGGER_2)
                .host(host)
                .apiInfo(apiInfo())
                .groupName(groupName)
                .select()
                .apis(RequestHandlerSelectors.basePackage(basePackage))
                .paths(PathSelectors.any())
                .build()
                .extensions(openApiExtensionResolver.buildExtensions(groupName));
        return docket;
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title(title)
                .description(description)
                .termsOfServiceUrl(termsOfServiceUrl)
                .contact(new Contact(contactName, contactUrl, contactEmail))
                .version(version)
                .build();
    }

}

开发business的业务代码

因为business模块是业务的触发者,所以不需要数据库操作,直接编写业务逻辑层即可

创建service包,包中创建IBusinessService接口

public interface IBusinessService {

    // 声明触发订单提交功能业务的方法
    void buy();

}

新建service.impl包

包中创建BusinessServiceImpl类

代码如下

@Service
@Slf4j
public class BusinessServiceImpl implements IBusinessService {

    @Override
    public void buy() {
        // 编写新增订单业务雏形
        // 先实例化一个要新增的订单对象
        OrderAddDTO orderAddDTO=new OrderAddDTO();
        orderAddDTO.setUserId("UU100");
        orderAddDTO.setCommodityCode("PC100");
        orderAddDTO.setCount(5);
        orderAddDTO.setMoney(100);
        // 这个实例化好的DTO对象,后期会发送到order模块新增订单
        // 现在只是业务雏形,输出一下即可
        log.info("新增订单信息为:{}",orderAddDTO);
    }
}

创建控制层controller

创建类BusinessController代码如下

@RestController
@RequestMapping("/base/business")
@Api(tags = "业务触发模块")
public class BusinessController {

    @Autowired
    private IBusinessService businessService;

    @PostMapping("/buy")
    @ApiOperation("执行触发业务的方法")
    public JsonResult buy(){
        businessService.buy();
        return JsonResult.ok("提交订单业务已触发!");
    }
}

启动当前business项目

访问

http://localhost:20000/doc.html

点击测试,观察输出结果和控制台输出内容是否正常

在这里插入图片描述
在这里插入图片描述

将项目注册到Nacos

我们已经讲过,一个项目要想成为微服务项目体系的一部分

必须将当前项目的信息注册到Nacos

我们要添加一些配置,实现business模块启动时注册到Nacos的效果

首先business模块pom文件中添加依赖

<!--   支持当前项目注册到Nacos注册中心的依赖
        discovery: 发现
        (当前项目注册后,微服务系统中,就能发现该项目)
  -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

保证课程开始时,我们启动的Nacos仍然在运行,如果已经停止了,需要重新开启

Nacos启动之后,

重启business模块,如果启动也正常,就应该将当前项目的信息提交给Nacos

在Nacos的服务管理->服务列表中,能看到nacos-business的名称

在这里插入图片描述

Nacos心跳机制

常见面试题

心跳:周期性的操作,来表示自己是健康可用的机制

注册到Nacos的微服务项目(模块)都是会遵循这个心跳机制的

心跳机制的目的

1.是表示当前微服务模块运行状态正常的手段

2.是表示当前微服务模块和Nacos保持沟通和交换信息的机制

默认情况下,服务启动开始每隔5秒会向Nacos发送一个"心跳包",这个心跳包中包含了当前服务的基本信息

Nacos接收到这个心跳包,首先检查当前服务在不在注册列表中,如果不在,按新服务的业务进行注册,如果在,表示当前这个服务是健康状态

如果一个服务连续3次心跳(默认15秒)没有和Nacos进行信息的交互,就会将当前服务标记为不健康的状态

如果一个服务连续6次心跳(默认30秒)没有和Nacos进行信息的交互,Nacos会将这个服务从注册列表中剔除

这些时间都是可以通过配置修改的

实例类型分类

实际上Nacos的服务类型还有分类

  • 临时实例(默认)
  • 持久化实例(永久实例)

持久化实例启动时向nacos注册,nacos会对这个实例进行持久化处理

心跳包的规则和临时实例一致,只是不会将该服务从列表中剔除

一般情况下,我们创建的服务都是临时实例

只有项目的主干业务才会设置为永久实例

如果想标记一个服务为永久实例

cloud:
  nacos:
    discovery:
      # ephemeral设置当前项目启动时注册到nacos的类型 true(默认):临时实例 false:永久实例
      ephemeral: false 

使用Idea启动Nacos

之前我们启动Nacos都是使用dos命令行

启动过程比较复杂,命令比较长

Idea实际上支持我们更加方便的启动Nacos步骤1:

在这里插入图片描述

步骤2:

在这里插入图片描述

步骤3:

在这里插入图片描述

配置成功之后

Nacos就会出现在idea的启动选项中了

这种方法实际上只是为了简便大家启动nacos的操作,还是需要掌握命令行的启动方式的!

创建csmall-cart子项目

创建项目

创建csmall-cart子项目,功能是购物车的管理

父子相认

子项目pom文件为:

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>cn.tedu</groupId>
        <artifactId>csmall</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>cn.tedu</groupId>
    <artifactId>csmall-cart</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>csmall-cart</name>
    <description>Demo project for Spring Boot</description>
    <dependencies>
        <!--web实例-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--mybatis整合springboot-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>
        <!--alibaba 数据源德鲁伊-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
        </dependency>
        <!--mysql驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!--all-common依赖-->
        <dependency>
            <groupId>cn.tedu</groupId>
            <artifactId>csmall-commons</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
        <!--在线api文档-->
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-spring-boot-starter</artifactId>
        </dependency>
        <!--  Nacos注册依赖 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
    </dependencies>
</project>

删除test文件夹

删除application.properties配置文件

可以复制business模块的application.yml文件,注意修改了端口号20001

server:
  port: 20001

application-dev.yml也可以粘贴过来,修改项目名称为nacos-cart

spring:
  application:
    # 设置当前项目的名称,这个名字会提交给Nacos做当前微服务项目的名称
    name: nacos-cart

准备数据

我们cart模块需要连接数据库操作,

所以我们要创建需要的表和添加表中的数据

sql代码已经发送在csmall-finish项目的node文件夹中:csmall_db.sql

运行其中的sql代码即可

推荐大家学习使用数据库的专用可视化工具来操作数据库

heidiSQL(安装MariaDB自带的)免费的

如果电脑安装的不是MariaDB,而是mysql的话,可能没有这个工具

heidiSQL下载地址:

https://www.heidisql.com/download.php

可以上网搜索mysql可视化工具解决,例如SQLYog和Navicat

CREATE DATABASE  IF NOT EXISTS `csmall_db` DEFAULT CHARACTER SET utf8mb4;
USE `csmall_db`;

DROP TABLE IF EXISTS `cart_tbl`;

CREATE TABLE `cart_tbl` (
                            `id` int NOT NULL AUTO_INCREMENT COMMENT '购物车id',
                            `commodity_code` varchar(255) DEFAULT NULL COMMENT '商品编码',
                            `price` int DEFAULT '0' COMMENT '商品单价',
                            `count` int DEFAULT '0' COMMENT '购买数量',
                            `user_id` varchar(255) DEFAULT NULL COMMENT '用户id',
                            PRIMARY KEY (`id`),
                            UNIQUE KEY `commodity_code` (`commodity_code`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb3;


insert  into `cart_tbl`(`id`,`commodity_code`,`price`,`count`,`user_id`) values
(1,'PU201',500,10,'UU100');


DROP TABLE IF EXISTS `order_tbl`;

CREATE TABLE `order_tbl` (
                             `id` int NOT NULL AUTO_INCREMENT COMMENT '订单id',
                             `user_id` varchar(255) DEFAULT NULL COMMENT '用户id',
                             `commodity_code` varchar(255) DEFAULT NULL COMMENT '商品编码,也可以是商品id',
                             `count` int DEFAULT '0' COMMENT '购买这个商品的数量',
                             `money` int DEFAULT '0' COMMENT '订单金额',
                             PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8mb3;

insert  into `order_tbl`(`id`,`user_id`,`commodity_code`,`count`,`money`) values
(22,'UU100','PU201',10,200),
(23,'UU100','PU201',10,200),
(24,'UU100','PU201',10,200),
(25,'UU100','PU201',10,200);


DROP TABLE IF EXISTS `stock_tbl`;

CREATE TABLE `stock_tbl` (
                             `id` int NOT NULL AUTO_INCREMENT COMMENT '商品id',
                             `commodity_code` varchar(255) DEFAULT NULL COMMENT '商品编码',
                             `count` int DEFAULT '0' COMMENT '商品库存',
                             PRIMARY KEY (`id`),
                             UNIQUE KEY `commodity_code` (`commodity_code`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb3;

insert  into `stock_tbl`(`id`,`commodity_code`,`count`) values
(1,'PU201',990);

DROP TABLE IF EXISTS `undo_log`;

CREATE TABLE `undo_log` (
                            `id` bigint NOT NULL AUTO_INCREMENT,
                            `branch_id` bigint NOT NULL,
                            `xid` varchar(100) NOT NULL,
                            `context` varchar(128) NOT NULL,
                            `rollback_info` longblob NOT NULL,
                            `log_status` int NOT NULL,
                            `log_created` datetime NOT NULL,
                            `log_modified` datetime NOT NULL,
                            PRIMARY KEY (`id`),
                            UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=68 DEFAULT CHARSET=utf8mb3;

配置yml文件

我们要配置数据源,项目名称和nacos的位置

在application-dev.yml文件中添加

spring:
  application:
    # 设置当前项目的名称,这个名字会提交给Nacos做当前微服务项目的名称
    name: nacos-cart
  cloud:
    nacos:
      discovery:
        # 配置Nacos的位置,用于提交当前项目的信息
        server-addr: localhost:8848
  datasource:
    url: jdbc:mysql://localhost:3306/csmall_db?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&allowMultiQueries=true
    username: root
    password: root

Spring相关配置类

和business项目一样

我们需要创建config包,包中创建相关配置类

// 当前类是配置Spring扫描环境的配置类,必须添加@Configuration注解
@Configuration
// 扫描全局异常处理类所在的包,也就是commons模块中exception包,使其生效
@ComponentScan("cn.tedu.csmall.commons.exception")
public class CommonsConfiguration {
}

knife4j配置

@Configuration
@EnableSwagger2WebMvc
public class Knife4jConfiguration {

    /**
     * 【重要】指定Controller包路径
     */
    private String basePackage = "cn.tedu.csmall.cart.controller";
    /**
     * 分组名称
     */
    private String groupName = "base-cart";
    /**
     * 主机名
     */
    private String host = "http://java.tedu.cn";
    /**
     * 标题
     */
    private String title = "酷鲨商城项目案例在线API文档--基础cart-web实例";
    /**
     * 简介
     */
    private String description = "构建基础cart-web项目,实现购物车管理";
 	//  略....   
}

当前cart模块除了上述配置之外,还需要添加Mybatis扫描mapper包的配置

config包中再创建一个MyBatisConfiguration类,代码如下

@Configuration
// MyBatis框架要求扫描指定的包,包中生成操作数据库接口的实现类
@MapperScan("cn.tedu.csmall.cart.mapper")
public class MyBatisConfiguration {
}

我们尝试启动Nacos后

启动cart模块

观察nacos服务列表中是否包含当前服务名称

编写cart项目业务

cart模块能够正常启动,但是还没有任何业务

结合我们最终生成订单的业务

当前cart模块需要开发如下两个功能

1.新增购物车中商品

2.删除购物车中商品

开发持久层

创建mapper包

创建CartMapper接口,中编写两个方法

一个新增购物车,一个删除购物车

@Repository
public interface CartMapper {

    // 向购物车表中新增商品信息
    @Insert("insert into cart_tbl(commodity_code,price,count,user_id) values" +
            "(#{commodityCode},#{price},#{count},#{userId})")
    int insertCart(Cart cart);

    // 删除购物车表中的商品信息
    @Delete("delete from cart_tbl where user_id=#{userId} and " +
            " commodity_code=#{commodityCode}")
    int deleteCartByUserIdAndCommodityCode(
                                      @Param("userId") String userId,
                                      @Param("commodityCode") String commodityCode);
    
}
开发业务逻辑层

创建service包

包中创建ICartService

接口中编写新增和删除购物车的两个业务逻辑层方法声明

public interface ICartService {

    // 新增购物车商品的业务逻辑层
    void cartAdd(CartAddDTO cartAddDTO);

    // 删除购物车商品的业务逻辑层
    void deleteUserCart(String userId,String commodityCode);
}

如果需要设置日志输出级别

dev的yml文件中添加

logging:
  level:
    cn.tedu.csmall.cart: debug
开发控制层

创建controller包

包中创建CartController类

代码如下

@RestController
@RequestMapping("/base/cart")
@Api(tags = "购物车管理模块")
public class CartController {

    // 装配业务逻辑层接口
    @Autowired
    private ICartService cartService;

    @PostMapping("/add")
    @ApiOperation("新增商品信息到购物车")
    public JsonResult cartAdd(CartAddDTO cartAddDTO){
        cartService.cartAdd(cartAddDTO);
        return JsonResult.ok("新增商品到购物车完成!");
    }

    @PostMapping("/delete")
    @ApiOperation("删除购物车中的商品信息")
    @ApiImplicitParams({
            @ApiImplicitParam(value = "用户Id",name="userId",example = "UU100"),
            @ApiImplicitParam(value = "商品编号",name="commodityCode",example = "PC100")
    })
    public JsonResult deleteUserCart(String userId,String commodityCode){
        cartService.deleteUserCart(userId,commodityCode);
        return JsonResult.ok("删除购物车商品完成!");
    }


}

重启cart模块

测试

localhost:20001/doc.html

发送新增购物车的请求测试

然后测试删除购物车的功能

创建Order模块

创建项目

父子相认

order模块pom文件直接参考cart模块即可

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>cn.tedu</groupId>
        <artifactId>csmall</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>cn.tedu</groupId>
    <artifactId>csmall-order</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>csmall-order</name>
    <description>Demo project for Spring Boot</description>
    <dependencies>
        <!--web实例-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--mybatis整合springboot-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>
        <!--alibaba 数据源德鲁伊-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
        </dependency>
        <!--mysql驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!--all-common依赖-->
        <dependency>
            <groupId>cn.tedu</groupId>
            <artifactId>csmall-commons</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
        <!--在线api文档-->
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-spring-boot-starter</artifactId>
        </dependency>
        <!--  Nacos注册依赖 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
    </dependencies>
</project>

删除test文件夹

删除application.properties文件

可以将cart模块的application.yml文件和application-dev.yml复制过来

但是要修改需要变化的属性

application.yml端口号修改

server:
  port: 20002

application-dev.yml修改项目名称

application:
  name: nacos-order

config配置

直接复制cart模块的config包到当前order模块即可

修改内容如下

knife4j配置主要修改:

    /**
     * 【重要】指定Controller包路径
     */
    private String basePackage = "cn.tedu.csmall.order.controller";
    /**
     * 分组名称
     */
    private String groupName = "base-order";
    /**
     * 主机名
     */
    private String host = "http://java.tedu.cn";
    /**
     * 标题
     */
    private String title = "酷鲨商城项目案例在线API文档--基础order-web实例";
    /**
     * 简介
     */
    private String description = "构建基础order-web项目,实现订单管理";

Mybatis配置修改如下

@Configuration
// Mybatis框架扫描mapper包接口的注解
@MapperScan("cn.tedu.csmall.order.mapper")
public class MybatisConfiguration {
}

编写新增订单功能

开发持久层

创建mapper包

创建OrderMapper接口

添加新增订单方法

@Repository
public interface OrderMapper {

    // 新增订单的方法
    @Insert("insert into order_tbl(user_id,commodity_code,count,money) values" +
            "(#{userId},#{commodityCode},#{count},#{money})")
    int insertOrder(Order order);


}
开发业务逻辑层

创建service包

包中创建IOrderService

public interface IOrderService {

    // 声明新增订单的业务逻辑层方法
    void orderAdd(OrderAddDTO orderAddDTO);

}

创建service.impl包

包中创建OrderServiceImpl

@Service
@Slf4j
public class OrderServiceImpl implements IOrderService {

    @Autowired
    private OrderMapper orderMapper;

    @Override
    public void orderAdd(OrderAddDTO orderAddDTO) {
        // 1.这里先要进行减少数据库中库存数的操作(调用stock模块)
        // 2.还要从购物车中删除用户选中的商品(调用cart模块)

        // 3.上面两个操作完成后,在进行订单的新增
        Order order=new Order();
        BeanUtils.copyProperties(orderAddDTO,order);
        // 执行新增
        orderMapper.insertOrder(order);
        log.info("新增订单信息为:{}",order);

    }
}
开发控制层

创建controller包

创建OrderController类

@RestController
@RequestMapping("/base/order")
@Api(tags = "订单管理模块")
public class OrderController {

    @Autowired
    private IOrderService orderService;

    @PostMapping("/add")
    @ApiOperation("新增订单")
    public JsonResult orderAdd(OrderAddDTO orderAddDTO){
        orderService.orderAdd(orderAddDTO);
        return JsonResult.ok("新增订单完成!");
    }
}

启动order项目

localhost:20002/doc.html

检查nacos注册状态和knife4j测试新增订单效果

先启动nacos再启动order

搭建stock模块

项目搭建

创建csmall-stock(之前已经创建好了)

修改父项目pom文件,父子相认(也已经完成)

子项目pom文件也是可以复制cart\order模块

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>cn.tedu</groupId>
        <artifactId>csmall</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>cn.tedu</groupId>
    <artifactId>csmall-stock</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>csmall-stock</name>
    <description>Demo project for Spring Boot</description>
    <dependencies>
        <!--web实例-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--mybatis整合springboot-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>
        <!--alibaba 数据源德鲁伊-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
        </dependency>
        <!--mysql驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!--all-common依赖-->
        <dependency>
            <groupId>cn.tedu</groupId>
            <artifactId>csmall-commons</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
        <!--在线api文档-->
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-spring-boot-starter</artifactId>
        </dependency>
        <!--  Nacos注册依赖 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
    </dependencies>

</project>

删除test测试文件夹(可能已经删除了)

删除application.properties文件

复制order\cart模块的application.yml和application-dev.yml文件

修改端口号和项目名称即可

config配置

从order\cart模块复制config包

修改MybatisConfiguration\Knife4jConfiguration对应的包名

编写减少库存功能

开发持久层

首先确定修改库存业务的sql语句:

UPDATE
	stock_tbl
SET
	COUNT=COUNT-1
WHERE
	commodity_code='PC100'
AND
	COUNT>=1

上面的sql语句能够保证在库存足够时才会减少库存,库存不够时就不运行修改了

创建mapper包,包中创建StockMapper接口

@Repository
public interface StockMapper {

    // 减少指定商品库存数的方法
    @Update("update stock_tbl set count=count-#{reduceCount} " +
            " where commodity_code=#{commodityCode} and " +
            " count>=#{reduceCount}")
    int updateStockCount(@Param("commodityCode") String commodityCode,
                         @Param("reduceCount") Integer reduceCount);

}
开发业务层

在service包下创建IStockService

public interface IStockService {
    
    // 减少指定商品库存数的业务逻辑层方法
    void reduceStockCount(StockReduceCountDTO stockReduceCountDTO);
}

service.impl包下创建StockServiceImpl

@Service
@Slf4j
public class StockServiceImpl implements IStockService {

    @Autowired
    private StockMapper stockMapper;

    @Override
    public void reduceStockCount(StockReduceCountDTO stockReduceCountDTO) {

        int rows = stockMapper.updateStockCount(
                stockReduceCountDTO.getCommodityCode(),
                stockReduceCountDTO.getReduceCount());
        if(rows == 0){
            // 如果上面减少库存数的数据库操作没有影响数据库中的数据
            // 就证明这次修改没有成功减少库存,要抛出异常
            throw new CoolSharkServiceException(
                    ResponseCode.BAD_REQUEST,"库存不足或商品编号不正确");
        }
        log.info("库存减少完成!!!");
    }
}
开发控制层

controller包

StockController类

@RestController
@RequestMapping("/base/stock")
@Api(tags = "库存管理模块")
public class StockController {

    @Autowired
    private IStockService stockService;

    @PostMapping("/reduce/count")
    @ApiOperation("减少库存数的方法")
    public JsonResult reduceCount(StockReduceCountDTO stockReduceCountDTO){
        stockService.reduceStockCount(stockReduceCountDTO);
        return JsonResult.ok("库存减少完成!");
    }
}

保证Nacos是启动的

再启动stock

localhost:20003/doc.html测试

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值