(十)Alian 的 Spring Cloud 库存系统

一、背景

  既然我们已经搭建了配置中心了,也实现了自定义的 db starter 和公共的 API starter ,现在我们就把它整合到一起,完成一个库存系统的设计,你就可以感受到简单了。

二、数据库

1.1、创建数据库

#创建库
create databases stock;
CREATE DATABASE IF NOT EXISTS stock
DEFAULT CHARACTER SET utf8mb4 
DEFAULT COLLATE utf8mb4_general_ci;

  字符集推荐使用: utf8mb4

1.2、创建表

#创建一个库存表
CREATE TABLE `tb_goods_stock` (
  `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
  `goods_id` int unsigned NOT NULL DEFAULT '0' COMMENT '商品id',
  `inventory` int unsigned NOT NULL DEFAULT '0' COMMENT '库存',
  `create_time` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
  `update_time` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
  PRIMARY KEY (`id`),
  KEY `idx_goods_id` (`goods_id`),
  KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb3 COMMENT='库存表';

1.3、初始数据

#插入商品编号为10001和10002的库存分别为100,200
insert into tb_goods_stock(goods_id,inventory) values(10001,100);
insert into tb_goods_stock(goods_id,inventory) values(10002,200);

1.4、配置中心数据

在这里插入图片描述

三、domain-stock

3.1、实体类

GoodsStock.java

package cn.alian.domain.stock.domain;

import lombok.Getter;
import lombok.Setter;

import javax.persistence.*;
import java.io.Serializable;
import java.time.LocalDateTime;

@Setter
@Getter
@Entity
@Table(name = "tb_goods_stock")
public class GoodsStock implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * 主键
     */
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", insertable = false, nullable = false)
    private Integer id;

    /**
     * 商品id
     */
    @Column(name = "goods_id", nullable = false)
    private int goodsId;

    /**
     * 库存数量
     */
    @Column(name = "inventory", nullable = false)
    private int inventory;

    /**
     * 创建时间
     */
    @Column(name = "create_time", nullable = false, insertable = false, updatable = false)
    private LocalDateTime createTime;

    /**
     * 更新时间
     */
    @Column(name = "update_time", nullable = false, insertable = false, updatable = false)
    private LocalDateTime updateTime;

}

3.2、持久层

GoodsStockRepository.java

package cn.alian.domain.stock.repository;

import cn.alian.domain.stock.domain.GoodsStock;
import org.springframework.data.ebean.repository.EbeanRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface GoodsStockRepository extends EbeanRepository<GoodsStock, Integer> {

}

3.3、打包到私服

  通过maven自动生成Ebean QueryBean,之前的文章说了,就不再列出了,具体可以参考:(四)Alian 的 Spring Cloud Ebean自动生成Query Bean

四、maven依赖

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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>cn.alian.microservice</groupId>
        <artifactId>parent</artifactId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>

    <groupId>cn.alian.mall</groupId>
    <artifactId>stock</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <name>stock</name>
    <description>库存服务</description>

    <dependencies>
        <!--自定义的db starter-->
        <dependency>
            <groupId>cn.alian.microservice</groupId>
            <artifactId>common-db</artifactId>
            <version>1.0.0-SNAPSHOT</version>
        </dependency>
        <!--自定义的API starter-->
        <dependency>
            <groupId>cn.alian.microservice</groupId>
            <artifactId>common-api</artifactId>
            <version>1.0.0-SNAPSHOT</version>
        </dependency>
        <!--domain Query Bean-->
        <dependency>
            <groupId>cn.alian.domain</groupId>
            <artifactId>domain-stock</artifactId>
            <version>1.0.0-SNAPSHOT</version>
        </dependency>
        <!--配置中心-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <!--注册中心-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!--sql日志输出-->
        <dependency>
            <groupId>com.googlecode.log4jdbc</groupId>
            <artifactId>log4jdbc</artifactId>
        </dependency>
        <!--mysql驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

    </dependencies>

    <repositories>
        <repository>
            <id>nexus-repos</id>
            <name>Team Nexus Repository</name>
            <url>http://192.168.0.210:8081/nexus/content/groups/public</url>
        </repository>
        <repository>
            <id>nexus-repos-snapshots</id>
            <name>Team Nexus Repository Snapshots</name>
            <url>http://192.168.0.210:8081/nexus/content/groups/public-snapshots</url>
        </repository>
        <repository>
            <id>aliyun-maven-repo</id>
            <url>https://maven.aliyun.com/repository/public</url>
        </repository>
    </repositories>

    <pluginRepositories>
        <pluginRepository>
            <id>nexus-repos</id>
            <name>Team Nexus Repository</name>
            <url>http://192.168.0.210:8081/nexus/content/groups/public</url>
        </pluginRepository>
        <pluginRepository>
            <id>nexus-repos-snapshots</id>
            <name>Team Nexus Repository Snapshots</name>
            <url>http://192.168.0.210:8081/nexus/content/groups/public-snapshots</url>
        </pluginRepository>
    </pluginRepositories>

</project>

五、编码

5.1、controller

StockController.java

package cn.alian.mall.stock.controller;

import cn.alian.mall.stock.dto.DeductInventoryRequestDto;
import cn.alian.mall.stock.dto.DeductInventoryResponseDto;
import cn.alian.mall.stock.service.StockService;
import cn.alian.microservice.common.dto.ApiResponseDto;
import com.alibaba.fastjson.JSON;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;

@Slf4j
@Validated
@RestController
@RequestMapping({"/api/v1/stock"})
@Api(description = "库存相关接口")
public class StockController {

    @Autowired
    private StockService stockService;

    @ApiOperation("扣减商品库存")
    @PostMapping("/deductInventory")
    public ApiResponseDto<DeductInventoryResponseDto> request(@ApiParam @Valid @RequestBody DeductInventoryRequestDto dto) {
        log.info("扣减商品库存请求信息:{}", JSON.toJSONString(dto));
        DeductInventoryResponseDto responseDto = new DeductInventoryResponseDto();
        Pair<Boolean, String> pair = stockService.deductInventory(dto.getGoodsId(), dto.getNum());
        log.info("扣减库存返回的结果:{}", pair.getRight());
        responseDto.setResultCode(pair.getLeft() ? "0000" : "9999");
        responseDto.setMessage(pair.getRight());
        return ApiResponseDto.success(responseDto);

    }
}

5.2、service

StockService.java

package cn.alian.mall.stock.service;

import io.ebean.DB;
import io.ebean.EbeanServer;
import io.ebean.SqlUpdate;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Slf4j
@Service
public class StockService {

    @Autowired
    private EbeanServer ebeanServer;

    public Pair<Boolean, String> deductInventory(int goodsId, int num) {
        String sql = "update tb_goods_stock set inventory=inventory - " + num + " where goods_id =" + goodsId;
        SqlUpdate sqlUpdate = DB.sqlUpdate(sql);
        int execute = sqlUpdate.execute();
        log.info("商品【{}】库存操作完成", goodsId);
        if (execute != 0) {
            return Pair.of(true, "库存扣减成功");
        }
        return Pair.of(false, "库存扣减失败");
    }

}

5.3、dto

DeductInventoryRequestDto.java

package cn.alian.mall.stock.dto;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.validation.constraints.NotNull;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder(toBuilder = true)
@ApiModel(description = "扣减库存请求信息")
public class DeductInventoryRequestDto {

    @NotNull
    @ApiModelProperty("订单id")
    private Integer orderId;

    @NotNull
    @ApiModelProperty("商品id")
    private Integer goodsId;

    @NotNull
    @ApiModelProperty("数量")
    private Integer num = 1;

}

DeductInventoryResponseDto.java

package cn.alian.mall.stock.dto;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.validation.constraints.NotNull;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder(toBuilder = true)
@ApiModel(description = "扣减库存返回信息")
public class DeductInventoryResponseDto {

    @NotNull
    @ApiModelProperty("结果码")
    private String resultCode;

    @NotNull
    @ApiModelProperty("结果说明")
    private String message;

}

六、配置

6.1、主类

StockApplication.java

package cn.alian.mall.stock;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@EnableDiscoveryClient
@SpringBootApplication
public class StockApplication {

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

}

6.2、bootstrap.properties

bootstrap.properties

#应用名
spring.application.name=stock
#开发环境
spring.profiles.active=dev
#配置标签(根据配置中心,本文全是采用master)
spring.cloud.config.label=master
#配置中心地址,多个用逗号分隔
spring.cloud.config.uri=http://10.130.3.222:6666
#设置为true时,如果服务无法连接到配置中心服务器,则服务启动失败
spring.cloud.config.fail-fast=true
#日志配置,通过配置中心获取
logging.config=${spring.cloud.config.uri}/logback/${spring.application.name}.xml

6.3、ebean.properties

ebean.properties

ebean.packages=cn.alian.domain.stock

6.4、build.properties

build.properties

build.groupId=${pom.groupId}
build.artifactId=${pom.artifactId}
build.version=${pom.version}
# 生成的文档说明
build.description=${pom.description}
# spring-boot:run -Ppack时方便生成api客户端
build.pack=cn.alian.mall.stock

6.5、dozerMapping.xml

dozerMapping.xml

<?xml version="1.0" encoding="UTF-8"?>
<mappings xmlns="http://dozermapper.github.io/schema/bean-mapping"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://dozermapper.github.io/schema/bean-mapping http://dozermapper.github.io/schema/bean-mapping.xsd">
</mappings>

七、验证

  我们公共的API已经完成了swagger的整合,我们启动项目使用swagger进行测试
在这里插入图片描述
返回结果:

{
  "code": "0000",
  "msg": "success",
  "content": {
    "resultCode": "0000",
    "message": "库存扣减成功"
  },
  "timestamp": 1649741434665,
  "bizCode": null,
  "bizMsg": null,
  "success": true
}

数据库结果:

mysql> select * from tb_goods_stock;
+----+----------+-----------+-------------------------+-------------------------+
| id | goods_id | inventory | create_time             | update_time             |
+----+----------+-----------+-------------------------+-------------------------+
|  1 |    10001 |        99 | 2022-04-06 11:25:51.626 | 2022-04-12 05:30:35.662 |
|  2 |    10002 |       200 | 2022-04-12 01:56:39.974 | 2022-04-12 01:56:39.974 |
+----+----------+-----------+-------------------------+-------------------------+
2 rows in set (0.00 sec)

  从结果上来说吗,我们的系统已经通过测试了。后续我们会生成接口调用,供订单系统调用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值