当前位置:网站首页>Commodity seckill system

Commodity seckill system

2022-06-26 18:27:00 [email protected]

Design thinking :

One 、 Learning goals

  • Understand the business of secsha
  • Master the design idea and technical architecture of second kill
  • master SpringCloud Application for second kill business
  • master redis For performance improvement
  • master RabbitMQ For asynchronous processing of business splitting

Two 、 Understand the business and technical architecture of secsha

2.1. What is seckill

For businesses , According to the size of the merchant , Second kill is divided into three forms :

1、 The platform requires second kill on time , Similar to tmall 11,11 month 11 Japan 0 Start buying at , Or Jingdong's whole point rush , All initiated by the platform .

2、 Businesses do second kill for their stores , Usually the flagship store of the manufacturer , Occupy favorable advertising space on the front page of the platform , Enter the store to do second kill .

3、 Seckill page of WeChat official account for spike , Initiated by businesses operated by the official account .

According to the merchant's promotional activities , Second kill is divided into three ways :

1、 Limit second kill : The most common form of second kill , The spike price is absolutely too low to be believed or resisted to participate , This second kill is usually after the start 1-3 The second kill will be over in seconds .

2、 Low price Limited second kill : This form can also be understood as low discount second kill , Limited and unlimited time , The second is over , In this second kill form, businesses provide a certain number of goods , Until the second is over .

3、 Low price limited time limited second kill : This form can also be understood as low discount second kill , Time limit , Within the specified time , Whether the goods are killed or not , The second kill will end .

2.2. Business characteristics of secsha

1、 The amount of instantaneous concurrency is large : A large number of users will rush to buy at the same time , Website traffic surged instantly .

2、 The stock is low : It's usually a low price limit , And the number of visits is far greater than the number of inventory , Only a few people succeeded .

3、 Simple business process : Short process , Buy now , Place the order , Reduce inventory .

4、 Pre heating : For the second kill goods that have not opened the activity , Display in countdown mode , You can only access and cannot place orders .

2.3. Design thinking

1、 Current limiting : Only a small number of successful second kill people can enter the backstage , Interact with the database , To reduce the pressure on the database server .

2、 cache : Write some business logic to the cache , for example : Restricted quantity of goods 、 Second kill policy, etc .

3、 asynchronous : Split business logic , Reduce server pressure , for example : The normal business process is to place orders 、 payment 、 Inventory reduction is completed at the same time , Business logic can be split during second kill .

4、 preheating : Business promotion , And set the second kill goods in advance 、 Second kill time 、 Limited quantity , Write the set item to  redis  cache .

5、 Exhibition : The page is divided into two layers , The first level is the product list page , The second level is the product details page , Enter the product details page through the product list page link , Before the second kill , Display goods second kill countdown , Operation not allowed to submit order , Only product details are allowed to be viewed . When the second kill begins , Display commodity spike expiration time .

6、 place order : After submitting the order, the second kill will  redis  The number in the cache is reduced , And prompt for payment .

7、 Queue operation : When payment is successful , Write the seckill success details to  rabbitMQ, The order service listens and writes the received message to the order , The inventory service listens and receives messages to reduce inventory .

8、 Time server : The page server is deployed through load , The time of each server may be inconsistent , Therefore, time service is increased , To provide a unified time .

Overall architecture :

Eureka Client:

Time service (leyouTimeServer, Port number 8000): Provide a time unified interface for page services .

Goods and services (leyouStock, Port number 7000): External interface ( List of goods 、 Goods details 、 Second kill policy ).

Inventory service (leyouStorage, Port number 6001): Queue listening , Extract messages from the queue and interact with the database to reduce inventory .

Member services (leyouUser, Port number 5000): Provide member data interface for page service , Addition of members 、 modify 、 Sign in .

Order service (leyouOrder, Port number 4000): Queue listening , Extract messages from the queue and interact with the database to generate orders .

Page services (leyouClient, Port number 3000): Provide data interface for front-end pages .

Eureka Server:

Registry Center (leyouServer, Port number 9000) All services are registered in the registry .

Configuration center  (leyouConfig): Provide the configuration required for all services .

Redis Application :

Number of cached items 、 Second kill policy .

Merchant's second kill policy 、 Set the commodity limit , Set write complete Redis.

Consumer access to product details , After submitting the order , from Redis Reduce the quantity of goods in .

Redis Access content in :

1、 Deposit when the policy is added ,key The value of is :LIMIT_POLICY_{sku_id},value The value of is the policy content

2、 When retrieving data from commodity list , adopt key(LIMIT_POLICY_{sku_id}), Extract policy content .

3、 After the policy expires , Automatically delete .

RabbitMQ Application :

Consumer submits order , Auto write order queue :

Order queue : Order service listens to order queue , After receiving the message, write the queue information to the database order table .

After consumer payment , Update order status , Write to the inventory queue after the update is successful

Inventory queue : The inventory service listens to the inventory queue , After receiving the message, write the inventory information to the database to reduce inventory .

2.5. database structure

  3、 ... and 、 Second kill environment setup ( understand )

3.1. install redis And configuration

3.1.2. To configure redis

After installation , You need to do some setup first , So that the service can run normally after startup . Using the text editor , Use here Notepad++, open Redis Service profile . Be careful : Don't make a mistake , Usually it is redis.windows-service.conf, instead of redis.windows.conf. The latter is the configuration file used by bootloader in non system service mode .

Find the containing requirepass Where the words are , Append a row , Input requirepass leyou. This is a visit Redis The password required when , You also need to set it later in the project .

Test it Redis Whether the service is normally provided . Get into Redis The catalog of ,cd C:\Program Files\Redis. Input redis-cli And return .(redis-cli It's a client program ) As shown in the picture, the normal prompt is to enter , And display the correct port number , Indicates that the service has been started .

3.1.2. To configure redis

After installation , You need to do some setup first , So that the service can run normally after startup . Using the text editor , Use here Notepad++, open Redis Service profile . Be careful : Don't make a mistake , Usually it is redis.windows-service.conf, instead of redis.windows.conf. The latter is the configuration file used by bootloader in non system service mode .

Find the containing requirepass Where the words are , Append a row , Input requirepass leyou. This is a visit Redis The password required when , You also need to set it later in the project .

Test it Redis Whether the service is normally provided . Get into Redis The catalog of ,cd C:\Program Files\Redis. Input redis-cli And return .(redis-cli It's a client program ) As shown in the picture, the normal prompt is to enter , And display the correct port number , Indicates that the service has been started .

Input  auth leyou, Show OK, The password is correct .

Actually test reading and writing . Input set mykey "abd” And return , Used to save a key value . Input again get mykey, Get the key value saved just now .

Turn on persistence ,appendonly yes – The default is no.

Persistence : Put the data ( Like objects in memory ) Save to a permanent storage device . The main application of persistence is to store objects in memory in database , Or stored in a disk file 、 XML  Data files and so on .

redis All data is cached in memory , When you restart or shut down the system , All data cached in memory will disappear , Never to be found again . So in order to keep the data for a long time , will  Redis  The data put in the cache is stored persistently .

The default write time is everysec, Per second

Can also be set to always, Write in real time , But there will be efficiency problems .

900 Seconds has a value stored in , Just persist once

300 Second has 10 Values are stored in , Just persist once

60 Second has 10000 Values are stored in , Just persist once

3.2. install RabbitMQ And configuration

3.2.1. install RabbitMQ client

install  otp_win64_20.2.exe

3.2.2. install RabbitMQ Server side

install  rabbitmq-server-3.7.4.exe

3.2.3. To configure RabbitMQ Server side

To configure RabbitMQ Client environment variable

To configure RabbitMQ Server environment variable

Add... To the environment variable RabbitMQ Server side

Installing a plug-in :rabbitmq-plugins.bat enable rabbitmq_management

restart RabbitMQ service

start-up RabbitMQ

Four 、 Seckill system creation

First, take a look at the directory structure of secsha

4.1. establish Eureka Registry Center ( Port number 9000)


First step : choice File-New-Module..., Select... From the pop-up window Spring initializr, choice Module SDK, choice Next

The second step :Group by com.itheima,Artifact by leyou,Name by leyouServer,Description by Server For leyou Project, choice Next

The third step : choice  Spring Cloud Discovery, choice  Eureka Server, choice Next

Step four :Module name  by  leyouServer,Content root Is the path +leyouServer, choice Finish, A... Will be created under the project folder  leyouServer Folder

4.1.1. To configure pom.xml

In the generated project , open pom.xml, Configuration dependency

<?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>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-parent</artifactId>
        <version>2.1.6.RELEASE</version>
    </parent>

    <groupId>com.itheima</groupId>
    <artifactId>leyou</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Greenwich.SR2</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

Click... In the window suspended in the lower right corner  Import Changes, Will automatically arrive Maven Remote warehouse to download the required jar package .

If there is no introduction , According to the following figure , stay IDEA Choose... On the right  Maven Projects, find leyouServer choice  Dependencies Right click , choice  Download Sources, I'll go too Maven For downloading from remote warehouse jar package , This is true for all subsequent projects .

4.1.2. The configuration file application.properties

In the generated project , open src\man\resources\application.properties, Configure port number, etc

server.port=9000
eureka.client.service-url.defaultZone=http://localhost:9000/eureka/
eureka.instance.hostname=localhost
spring.application.name=leyou-server
# Do not get service information from the server 
eureka.client.fetch-registry=false
# Do not register on the server 
eureka.client.register-with-eureka=false

Be careful : The port number here must not be followed by characters such as spaces , Otherwise, an error will be reported

4.1.3. Write the startup class

In the generated project , open src\main\java\com.itheima.leyou\leyouServerApplication, Add... To the generated startup class  @EnableEurekaServer, It means to start Eureka Server side

@SpringBootApplication
@EnableEurekaServer
public class ServerApplication {

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

}

Test run registry , Enter... In the browser  http://localhost:9000

Come here , Registry service completed .

4.2. Create a time service ( Port number 8000)


4.2.1. Create a time service

First step : choice File-New-Module..., Select... From the pop-up window Spring initializr, choice Module SDK, choice Next

The second step :Group by com.itheima,Artifact by leyou,Name by leyouTimeServer,Description by TimeServer For leyou Project, choice Next

The third step : choice  Spring Cloud Discovery, choice  Eureka Discovery Client, choice Next

Step four :Module name  by  leyouTimeServer,Content root Is the path +leyouTimeServer, choice Finish, A... Will be created under the project folder  leyouTimeServer Folder

In the generated project , open pom.xml, Configuration dependency , among spring-cloud-starter-netflix-eureka-client Introduced... For the project Eureka Client's jar package ,spring-boot-starter-web Introduced web scenario ,web Used in module development jar package

<?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>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-parent</artifactId>
        <version>2.1.6.RELEASE</version>
    </parent>

    <groupId>com.itheima</groupId>
    <artifactId>leyou</artifactId>
    <version>1.0-SNAPSHOT</version>
    <name>leyouTimeServer</name>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

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

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Greenwich.SR2</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    
</project>

In the generated project , open src\man\resources\application.properties, Configure port number, etc

server.port=8000
spring.application.name=leyou-time-server
eureka.client.service-url.defaultZone=http://localhost:9000/eureka/

In the generated project , open src\main\java\com.itheima.leyou\leyouTimeServerApplication, Add... To the generated startup class  @EnableEurekaClient, It means to start Eureka client

@SpringBootApplication
@EnableEurekaClient
public class TimeServerApplication {

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

}

Test run order service , stay Eureka You can see in the registry  leyou-time-server service , Prove that the service was started successfully

4.2.2. establish controller Folder , And create timeController.java file

to TimeController.java Class add comments

@RestController
public class timeController {}

4.2.3. Create a time query method (getTime)

purpose : Provide a unified time standard for front-end seckill , stay TimeController Write the following code in the :

@RequestMapping(value = "/getTime")
public String getTime(){
    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    return simpleDateFormat.format(new Date());
}

4.2.4. Test time query

Access directly from the page  getTime  Method , Then get the current time , Address :http://localhost:8000/getTime

4.3. Create goods and services ( Port number 7000)

Project structure :

4.3.1. Create product table structure ( A little )

4.3.2. Create goods and services

First step : choice File-New-Module..., Select... From the pop-up window Spring initializr, choice Module SDK, choice Next

The second step :Group by com.itheima,Artifact by leyou,Name by leyouStock,Description by Stock For leyou Project, choice Next

The third step : choice  Spring Cloud Discovery, choice  Eureka Discovery Client, choice Next

Step four :Module name  by  leyouStock,Content root Is the path +leyouStock, choice Finish, A... Will be created under the project folder  leyouStock Folder

4.3.3. To configure pom.xml

In the generated project , open pom.xml, Configuration dependency , among spring-cloud-starter-netflix-eureka-client Introduced... For the project Eureka Client's jar package ,spring-boot-starter-web Introduced web scenario ,web Used in module development jar package ( Each client must have this dependency ), utilize alibaba Of fastjson analysis json data , So the introduction of alibaba.fastjson Use of jar package ,mysql-connector-java And spring-boot-starter-data-jpa Depend on the introduction of mysql The connection uses jar package

<?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>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-parent</artifactId>
        <version>2.1.6.RELEASE</version>
    </parent>

    <groupId>com.itheima</groupId>
    <artifactId>leyou</artifactId>
    <version>1.0-SNAPSHOT</version>

    <name>leyouStock</name>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

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

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

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

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.41</version>
        </dependency>

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

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Greenwich.SR2</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

4.3.4. The configuration file application.properties

In the generated project , open src\man\resources\application.properties, Configure port number, etc

server.port=7000
spring.application.name=leyou-stock
eureka.client.service-url.defaultZone=http://localhost:9000/eureka/

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/code1_2?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root

#redis Database number , There is 0~15 common 16 A database 
spring.redis.database=0
#redis The server IP
spring.redis.host=127.0.0.1
#redis Port number 
spring.redis.port=6379
#redis password 
spring.redis.password=
#redis Request timeout , Beyond this value redis Automatically disconnect 
spring.redis.timeout=10000ms
#jedis maximum connection , If this value is exceeded, it will prompt that no connection exception can be obtained 
spring.redis.jedis.pool.max-active=32
#jedis Maximum waiting time , Exceeding this value will prompt the connection timeout exception 
spring.redis.jedis.pool.max-wait=10000ms
#jedis Maximum number of connections waiting 
spring.redis.jedis.pool.max-idle=32
#jedis Minimum number of waiting connections 
spring.redis.jedis.pool.min-idle=0

4.3.5. Write the startup class

In the generated project , open src\main\java\com.itheima.leyou\leyouStockApplication, Add... To the generated startup class  @EnableEurekaClient, It means to start Eureka client

@SpringBootApplication
@EnableEurekaClient
public class StockApplication {

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

}

Be careful : Here if  @EnableEurekaClient Report errors , It proves that the import of dependent files is wrong , such as : In dependence spring-cloud-starter-netflix-eureka-client Add one later 1, Here's the picture :

It can lead to :

Be careful : The common mistakes are starter It's written in start, It's not easy to see , You can go to maven projects Go inside to see if there are any errors

Test run goods and services , stay Eureka You can see in the registry  leyou-Stock service , Prove that the service was started successfully

4.3.6. establish controller\service\dao Folder , And create StockController.java\StockService.java\StockDao.java file

Controller: Management business (Service) Scheduling and managing jumps

Service: Manage specific functions 、 Branch judgment

Dao: Manage data interactions , Complete addition, deletion and modification

Controller Like a waiter , What customers order , At what table .

Service Like a chef , He made all the dishes on the menu requested by the front end .

Dao Like a kitchen worker , He deals with raw materials .

4.3.7. Create a product list query method (getStockList)

purpose : Provide product list data for front-end page services , It is mainly used to display the front-end product list page .

First agree on the data structure to be returned through interaction with the front end :

Back to json character string :

If the return value is wrong :{"result":"false", "msg":"****"}

If the return value is correct :{"result":"true", "msg":"", "sku_list":["id":1,"sku_id":...]}

First from Dao Method to start writing code :

establish Dao Layer interface file ,IStockDao

public interface IStockDao {}

to StockDao.java Class add comments ,@Repository Used to label data access components , namely DAO Components , Realization IStockDao Interface

@Repository
public class StockDao implements IStockDao{}

Statement  JDBCTemplate  Method , Used to connect to the database

@Autowired
private JdbcTemplate jdbcTemplate;

increase getStockList Method , First, query the data required by the commodity list from the database , Load one ArrayList variable , The reason is that the product list has multiple rows of data , It's a list , And then ArrayList return , The code is as follows :

//1、 Create a SQL
String sql = "select id AS sku_id, title, images, stock, price, indexes, own_spec " +
        "from tb_sku";

//2、 Execute this SQL
ArrayList<Map<String, Object>> list = (ArrayList<Map<String, Object>>) jdbcTemplate.queryForList(sql);

//3、 Return the data 
return list;

Rewrite Service

establish Service Layer interface file ,IStockService

public interface IStockService {}

to StockService.java Add notes , And implement IStockService Interface

@Service
public class StockService implements IStockService{}

Join in IStockDao References to interfaces

@Autowired
private IStockDao iStockDao;

increase getStockList Method , call StockDao Medium getStockList, Return to one Map

public Map<String, Object> getStockList(){
        Map<String, Object> resultMap = new HashMap<String, Object>();
        //1、 take IstockDao Methods 
        ArrayList<Map<String, Object>> list = iStockDao.getStockList();

        //2、 If the data is not taken out , Return error message 
        if (list==null||list.size()==0){
            resultMap.put("result", false);
            resultMap.put("msg", " We don't know why we didn't take out the data !");
            return resultMap;
        }

        //3、 from redis Take the data 
        resultMap = getLimitPolicy(list);

        //4、 Return to normal information 
        resultMap.put("sku_list", list);
//        resultMap.put("result", true);
        resultMap.put("msg", "");
        return resultMap;
    }

Be careful : there Service Business logic judgment , When Dao After returning data , If the data is empty , Business logic judgment should be added , No corresponding product information was found , If you don't judge , The data obtained from the front page is empty , Then the page will be blank and no data will be displayed , It will bring bad experience to the page operator , doubt : Is there a network outage ? Or server down ? So back here “ No corresponding product information was found ” A hint of , It is helpful for the friendly display of the page . Front end programmers can also use the result Parameter judgement

adopt alt+ enter , choice Create method 'getStockList', stay IStockDao Methods for automatically creating interfaces in interface files .

stay IStockDao Add... To the method in the interface file public Modifier ,

to StockController.java Class add comments

@RestController
public class StockController {}

Join in IStockService References to interfaces

@Autowired
private IStockService iStockService;

increase  getStockList  Method , call StockService Medium getStockList, Back to a Map, For the page is to get a Json character string

@RequestMapping(value = "/getStockList")
public Map<String, Object> getStockList(){
    return iStockService.getStockList();
}

test getStockList Method , Enter... In the browser  http://localhost:7000/getStockList

4.3.8. Create a product query method (getStock)

purpose : Provide product detail page data for front-end page services , Mainly used for front-end product detail page display .

stay StockDao.java Class  getStock Method , With a sku_id Parameters , It means through sku_id Search for , Return a product Map, The code is as follows :

//1、 Create a SQL
String sql = "select tb_sku.spu_id, tb_sku.title, tb_sku.images, tb_sku.stock, tb_sku.price, tb_sku.indexes, " +
        "tb_sku.own_spec, tb_sku.enable, tb_sku.create_time, tb_sku.update_time,tb_spu_detail.description," +
        "tb_sku.id AS sku_id,tb_spu_detail.special_spec " +
        "from tb_sku " +
        "INNER JOIN tb_spu_detail ON tb_spu_detail.spu_id=tb_sku.spu_id " +
        "where tb_sku.id = ?";

//2、 Execute this SQL
ArrayList<Map<String, Object>> list = (ArrayList<Map<String, Object>>) jdbcTemplate.queryForList(sql, sku_id);

//3、 Return the data 
return list;

stay StockService.java Class getStock Method , Return a product Map, The code is as follows :

public Map<String, Object> getStock(String sku_id){
        Map<String, Object> resultMap = new HashMap<String, Object>();

        //1、 Pass in the parameter 
        if (sku_id==null||sku_id.equals("")){
            resultMap.put("result", false);
            resultMap.put("msg", " What's coming from the front ?");
            return resultMap;
        }

        //2、 take IstockDao Methods 
        ArrayList<Map<String, Object>> list = iStockDao.getStock(sku_id);

        //3、 If the data is not taken out , Return error message 
        if (list==null||list.size()==0){
            resultMap.put("result", false);
            resultMap.put("msg", " What happened to the database , I can't get the data !");
            return resultMap;
        }

        //3、 from redis Take the data 
        resultMap = getLimitPolicy(list);

        //4、 Return to normal information 
        resultMap.put("sku", list);
//        resultMap.put("result", true);
//        resultMap.put("msg", "");
        return resultMap;
    }

Be careful : there Service Business logic judgment , If the passed in parameter is null , This will result in database query errors , So in the incoming Dao Judge before layer , Whether the passed in parameter is legal . When Dao After returning data , If the data is empty , Business logic judgment also needs to be added , No corresponding product information was found

meanwhile , stay IStockDao Generate corresponding methods in .

stay StockController.java Class getStock Method , Need to add notes @RequestMapping, among value = "/getStock/{sku_id}, Return a product Map, The code is as follows :

@RequestMapping(value = "/getStock/{sku_id}")
public Map<String, Object> getStock(@PathVariable("sku_id") String sku_id){
    return iStockService.getStock(sku_id);
}

test getStock Method , Enter... In the browser localhost:7000/getStock/123

Enter... In the browser localhost:7000/getStock/26816294479

from redis How to get the value in

private Map<String, Object> getLimitPolicy(ArrayList<Map<String, Object>> list){
    Map<String, Object> resultMap = new HashMap<String, Object>();

    for (Map<String, Object> skuMap: list){
        //3.1、 from redis Take out the policy 
        String policy = stringRedisTemplate.opsForValue().get("LIMIT_POLICY_"+skuMap.get("sku_id").toString());

        //3.2、 Only those who have policies can continue 
        if (policy!=null&&!policy.equals("")){
            Map<String, Object> policyInfo = JSONObject.parseObject(policy, Map.class);

            //3.3、 The start time is less than or equal to the current time , And the current time is less than or equal to the end time 
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
            String now = restTemplate.getForObject("http://leyou-time-server/getTime", String.class);
            try {
                Date end_time = simpleDateFormat.parse(policyInfo.get("end_time").toString());
                Date begin_time = simpleDateFormat.parse(policyInfo.get("begin_time").toString());
                Date now_time = simpleDateFormat.parse(now);

                if (begin_time.getTime()<=now_time.getTime()&&now_time.getTime()<=end_time.getTime()){
                    skuMap.put("limitPrice", policyInfo.get("price"));
                    skuMap.put("limitQuanty", policyInfo.get("quanty"));
                    skuMap.put("limitBeginTime", policyInfo.get("begin_time"));
                    skuMap.put("limitEndTime", policyInfo.get("end_time"));
                    skuMap.put("nowTime", now);
                }

            } catch (ParseException e) {
                e.printStackTrace();
            }
        }
    }

    resultMap.put("result", true);
    resultMap.put("msg", "");
    return resultMap;
}

 4.3.9. Second kill policy table structure

DROP TABLE IF EXISTS `tb_limit_policy`;
CREATE TABLE `tb_limit_policy` (
  `id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
  `sku_id` BIGINT(20) NOT NULL COMMENT 'skuid',
  `quanty` BIGINT(20) COMMENT ' Number ',
  `price` BIGINT(20) COMMENT ' Second kill price ',
  `begin_time` TIMESTAMP COMMENT ' Starting time ',
  `end_time` TIMESTAMP COMMENT ' End time ',
  PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;

4.3.10. Create a second kill policy to increase the method (insertLimitPolicy)

purpose : Used to create a second kill policy , And write the policy into  Redis  cache .

stay StockDao.java Add one insertLimitPolicy Method , With a map Parameters ,map Here is the information from the front page , Return to one boolean value

public boolean insertLimitPolicy(Map<String, Object> map){
    String sql = "insert into tb_limit_policy (sku_id, quanty, price, begin_time, end_time) " +
            "Values (?, ?, ?, ?, ?)";
    return jdbcTemplate.update(sql, map.get("sku_id"), map.get("quanty"), map.get("price"), map.get("begin_time"), map.get("end_time"))==1;
}

stay StockService.java One more in insertLimitPolicy Method , Return to one map, The code is as follows :

@Transactional
public Map<String, Object> insertLimitPolicy(Map<String, Object> policyMap){
        //1、 Judge whether the passed in parameters are legal 
        Map<String, Object> resultMap = new HashMap<String, Object>();
        if (policyMap==null||policyMap.isEmpty()){
            resultMap.put("result", false);
            resultMap.put("msg", " What does it bring in ");
            return resultMap;
        }

        //2、 from StockDao The interface is called insertLimitPolicy Method 
        boolean result = iStockDao.insertLimitPolicy(policyMap);

        //3、 Judge whether the execution succeeds or fails , If you fail , Return error message 
        if (!result){
            resultMap.put("result", false);
            resultMap.put("msg", " Data execution failed again ");
            return resultMap;
        }

        //4、 If it works , write in redis, Validity period needs to be written ,key The name :LIMIT_POLICY_{sku_id}
        long diff = 0;
        String now = restTemplate.getForObject("http://leyou-time-server/getTime", String.class);

        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
        // The effective period of the policy is obtained by subtracting the current date from the end date 
        try {
            Date end_time = simpleDateFormat.parse(policyMap.get("end_time").toString());
            Date now_time = simpleDateFormat.parse(now);
            diff = (end_time.getTime() - now_time.getTime())/1000;

            if (diff<0){
                resultMap.put("result", false);
                resultMap.put("msg", " The end time cannot be less than the current time ");
                return resultMap;
            }
        } catch (ParseException e) {
            resultMap.put("result", false);
            resultMap.put("msg", " Date conversion failed again ");
            return resultMap;
        }

        String policy = JSON.toJSONString(policyMap);
        stringRedisTemplate.opsForValue().set("LIMIT_POLICY_"+policyMap.get("sku_id").toString(), policy, diff, TimeUnit.SECONDS);
        ArrayList<Map<String, Object>> list = iStockDao.getStock(policyMap.get("sku_id").toString());
        String sku = JSON.toJSONString(list.get(0));
        stringRedisTemplate.opsForValue().set("SKU_"+policyMap.get("sku_id").toString(), sku, diff, TimeUnit.SECONDS);

        //5、 Return to normal information 
        resultMap.put("result", true);
        resultMap.put("msg", "");
        return resultMap;
    }

Be careful : there Service Business logic judgment , First, judge the incoming json Whether it can be successfully converted , The second is the success or failure of writing to the database .

Here's a @Transactional, The transaction needs to be started , Remember that as long as it is insert\delete\update All three statements need to start a transaction , Once writing to the database fails , You can roll back , Because the methods mentioned above are all queries , So you don't have to start a transaction

stay StockController One more in insertLimitPolicy Method , Need to add notes @RequestMapping, among value = "/insertLimitPolicy/{jsonObj}, Return a product Map, The code is as follows :

@RequestMapping(value = "/insertLimitPolicy/{jsonObj}")
public Map<String, Object> insertLimitPolicy(@PathVariable("jsonObj") String jsonObj){
    return iStockService.insertLimitPolicy(jsonObj);
}

test insertLimitPolicy Method , Enter... In the browser http://localhost:7000/insertLimitPolicy/{sku_id:'26816294479',quanty:1000,price:1000,begin_time:'2019-08-05 11:00',end_time:'2019-10-05 12:00'}

4.3.11. When the new policy is added, deposit redis

step :

1、 To configure redis rely on

2、 The configuration file application.properties increase redis To configure

3、 Write a startup class to add restTemplate, The time service needs to be called

4、 stay Service Increase when  RestTemplate  and  StringRedisTemplate  Variable declaration for

4、 Write policy database table .

5、 Write the policy into Redis,key by  LIMIT_POLICY_{sku_id}.

6、 It's time to finish , utilize Redis The deletion mechanism of , Automatically delete , To reduce memory usage . The time is obtained by subtracting the current time from the policy end time .

The code is as follows :

Statement StringRestTemplate

@Autowired
StringRedisTemplate stringRedisTemplate;

modify StockService Inside insertLimitPolicy Method , Add the following code

4.3.12. When encapsulating the product list and product details, take redis Policy approach (getLimitPolicy)

step :

1、 Circular product list

2、 Remove each sku_id The policy of

3、 Assigned to... In the item list

Encapsulation method :getLimitPolicy

The code is as follows :

private Map<String, Object> getLimitPolicy(ArrayList<Map<String, Object>> list){
    Map<String, Object> resultMap = new HashMap<String, Object>();

    for (Map<String, Object> skuMap: list){
        //3.1、 from redis Take out the policy 
        String policy = stringRedisTemplate.opsForValue().get("LIMIT_POLICY_"+skuMap.get("sku_id").toString());

        //3.2、 Only those who have policies can continue 
        if (policy!=null&&!policy.equals("")){
            Map<String, Object> policyInfo = JSONObject.parseObject(policy, Map.class);

            //3.3、 The start time is less than or equal to the current time , And the current time is less than or equal to the end time 
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
            String now = restTemplate.getForObject("http://leyou-time-server/getTime", String.class);
            try {
                Date end_time = simpleDateFormat.parse(policyInfo.get("end_time").toString());
                Date begin_time = simpleDateFormat.parse(policyInfo.get("begin_time").toString());
                Date now_time = simpleDateFormat.parse(now);

                if (begin_time.getTime()<=now_time.getTime()&&now_time.getTime()<=end_time.getTime()){
                    skuMap.put("limitPrice", policyInfo.get("price"));
                    skuMap.put("limitQuanty", policyInfo.get("quanty"));
                    skuMap.put("limitBeginTime", policyInfo.get("begin_time"));
                    skuMap.put("limitEndTime", policyInfo.get("end_time"));
                    skuMap.put("nowTime", now);
                }

            } catch (ParseException e) {
                e.printStackTrace();
            }
        }
    }

    resultMap.put("result", true);
    resultMap.put("msg", "");
    return resultMap;
}

Pay attention here : Write the policy into the list and send it to the front page

limitPrice: Policy price

limitBeginTime: Policy start time

limitEndTime: Policy end time

nowTime: current time

4.4. Create inventory service ( Port number 6001)


Be careful : The port number configured here is not 6000, It is 6001, as a result of Chrome Google browser 6000 Ports are inaccessible

4.4.1. Create inventory table structure

1、 Warehouse table

DROP TABLE IF EXISTS `tb_warehouse`;
CREATE TABLE `tb_warehouse` (
  `id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT ' warehouse id',
  `name` VARCHAR(64) NOT NULL COMMENT ' Warehouse name ',
  `create_time` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP COMMENT ' Creation time ',
  `update_time` TIMESTAMP NULL COMMENT ' Update time ',
  PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;

2、 Inventory master table

purpose : Used to store inventory stock

DROP TABLE IF EXISTS `tb_stock_storage`;
CREATE TABLE `tb_stock_storage` (
  `id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
  `warehouse_id` BIGINT(20) NOT NULL COMMENT ' warehouse id',
  `sku_id` BIGINT(20) NOT NULL COMMENT 'skuid',
  `quanty` DECIMAL(18,2) COMMENT ' The remaining quantity ',
  PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;

3、 Inventory history table

purpose : Used to store inventory issue / receipt details

DROP TABLE IF EXISTS `tb_stock_storage_history`;
CREATE TABLE `tb_stock_storage_history` (
  `id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
  `stock_storage_id` BIGINT(20) NOT NULL COMMENT ' Inventory master table id',
  `in_quanty` DECIMAL(18,2) COMMENT ' Stock in quantity ',
  `out_quanty` DECIMAL(18,2) COMMENT ' Delivery quantity ',
  PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;

Inventory write rule :

Be careful : Inventory master table , A warehouse has only one record for each commodity ,quanty What is deposited is the remaining quantity .

Inventory history table , Each commodity in each warehouse corresponds to an inventory master table ID, Record the historical quantity of stock in and stock out .

Write rule description :

1、 adopt sku_id Determine whether the inventory master table has data ;

2、 If there's data , Get the... Of the inventory master table id;

3、 If there is no data , First write to the inventory master table , Get the... Of the inventory master table id;

4、 According to the inventory master table id Write to history table ;

5、 When there is data in the inventory master table , Through the... Of the inventory master table id Update quantity again .

4.4.2. Create inventory service

First step : choice File-New-Module..., Select... From the pop-up window Spring initializr, choice Module SDK, choice Next

The second step :Group by com.itheima,Artifact by leyou,Name by leyouStorage,Description by Storage For leyou Project, choice Next

The third step : choice  Spring Cloud Discovery, choice  Eureka Discovery Client, choice Next

Step four :Module name  by  leyouStorage,Content root Is the path +leyouStorage, choice Finish, A... Will be created under the project folder  leyouStorage Folder

4.4.3. To configure pom.xml

In the generated project , open pom.xml, Configuration dependency , among spring-cloud-starter-netflix-eureka-client Introduced... For the project Eureka Client's jar package ,spring-boot-starter-web Introduced web scenario ,web Used in module development jar package , utilize alibaba Of fastjson analysis json data , So the introduction of alibaba.fastjson Use of jar package ,mysql-connector-java And spring-boot-starter-data-jpa Depend on the introduction of mysql The connection uses jar package

<?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>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-parent</artifactId>
        <version>2.1.6.RELEASE</version>
    </parent>

    <groupId>com.itheima</groupId>
    <artifactId>leyou</artifactId>
    <version>1.0-SNAPSHOT</version>

    <name>leyouStorage</name>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

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

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

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

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.41</version>
        </dependency>

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Greenwich.SR2</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    
</project>

4.4.4. The configuration file application.properties

In the generated project , open src\man\resources\application.properties, Configure port number, etc

server.port=6001
spring.application.name=leyou-storage
eureka.client.service-url.defaultZone=http://localhost:9000/eureka/
// Note the database name 
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/leyou?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root

In the generated project , open src\main\java\com.itheima.leyou\leyouStorageApplication, Add... To the generated startup class  @EnableEurekaClient, It means to start Eureka client

@SpringBootApplication
@EnableEurekaClient
public class StorageApplication {

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

}

Test run inventory service , stay Eureka You can see in the registry  leyou-Storage service , Prove that the service was started successfully

4.4.5. establish controller\service\dao Folder , And create StorageController.java\StorageService.java\StorageDao.java file

establish Dao Layer interface file ,IStorageDao

public interface IStorageDao {}

to StorageDao.java Class add comments ,@Repository Used to label data access components , namely DAO Components , Realization IStroageDao Interface

@Repository
public class StorageDao implements IStorageDao{}

Statement  JDBCTemplate  Method , Used to connect to the database

@Autowired
private JdbcTemplate jdbcTemplate;

establish Service Layer interface file ,IStorageService

to StorageService.java Class add comments , Realization IStorageService The interface of

@Service
public class StorageService implements IStorageService {}

Join in IStorageDao References to interfaces

@Autowired
private IStorageDao iStorageDao;

to StorageController.java Class add comments

@RestController
@Configuration
public class StorageController {}

Join in IStorageService References to interfaces

@Autowired
private IStorageService iStorageService;

4.4.6. Create an inventory query method (getStockStorage)

purpose : Query whether the actual inventory is deducted or increased

stay StorageDao.java Class  getStockStorage Method , With a sku_id Parameters , It means through sku_id Search for , Return a product list, The code is as follows :

public ArrayList<Map<String, Object>> getStockStorage(String sku_id){
    //1、SQL Value 
    String sql = "SELECT sku_id, quanty FROM tb_stock_storage WHERE sku_id = ?";

    //2、 Return the data 
    return (ArrayList<Map<String,Object>>) jdbcTemplate.queryForList(sql, sku_id);
}

stay StorageService.java Class getStockStorage Method , Return a product Map, The code is as follows :

public Map<String, Object> getStockStorage(String sku_id){
    //1、 First get the inventory of a commodity 
    ArrayList<Map<String ,Object>> list = new ArrayList<Map<String, Object>>();
    list = iStorageDao.getStockStorage(sku_id);

    //2、 Determine if the stockDao The item taken out is empty , Return to a hint 
    Map<String, Object> resultMap = new HashMap<String, Object>();
    if (list==null||list.isEmpty()){
        resultMap.put("result", false);
        resultMap.put("msg", " Finished , The server is down , Data not extracted !");
        return resultMap;
    }

    //3、 Judge if the goods taken out are not empty , Return the data 
    resultMap.put("storage", list);
    resultMap.put("result", true);
    resultMap.put("msg", "");
    return resultMap;
}

stay StorageController.java Class getStockStorage Method , Need to add notes @RequestMapping, among value = "/getStockStorage/{sku_id}, Return a product Map, The code is as follows :

@RequestMapping(value = "/getStockStorage/{sku_id}")
public Map<String, Object> getStockStorage(@PathVariable("sku_id") String sku_id){
    return iStorageService.getStockStorage(sku_id);
}

4.4.7. Create inventory increase / decrease method (insertStorage)

purpose : While listening to the queue , Get message , Deducting the inventory

First, understand the table structure of inventory : Inventory master table 、 Inventory history table . The inventory master table saves the current remaining quantity of inventory goods , The inventory history table is used to save the history of stock in and stock out .

for example :A The goods are in stock a There are still... In the warehouse 10 individual , Then in the main table is a warehouse ,A goods ,10 A number of

Stock purchase 20 individual , Buy out 10 individual , There are two records in the history table , One is in 20 individual , One is out 10 individual , The summarized quantity is exactly equal to the remaining quantity in the inventory master table

 Someone will ask : Then you can check the inventory quantity and directly summarize the historical table ?
 Is the answer , For example, JD's sales of a product will have 10 ten thousand +, Then the history table has 10 ten thousand + data , It will be time-consuming to summarize , You can easily get the remaining inventory quantity through the main table .

stay StorageDao.java Class  insertStorage Method , Take three sku_id,inquanty,outquanty Parameters , It means how many items are written in which warehouse , return map, Be careful : The warehouse here is a virtual one id yes 1 The warehouse of , So when writing, just put warehouse_id The assignment is 1.

The code is as follows :

public Map<String, Object> insertStorage(String sku_id, double in_quanty, double out_quanty){

    Map<String, Object> resultMap = new HashMap<String, Object>();

    //1、 Query whether the inventory master table has inventory 
    String sql = "SELECT id FROM tb_stock_storage WHERE sku_id = ?";
    ArrayList<Map<String, Object>> list = (ArrayList<Map<String, Object>>) jdbcTemplate.queryForList(sql, sku_id);

    int new_id = 0;
    double thisQuanty = in_quanty - out_quanty;
    boolean result = false;

    //2、 If there is stock , obtain id, Function 1 write history table , The second action is to update 
    if (list!=null&&list.size()>0){
        new_id = Integer.parseInt(list.get(0).get("id").toString());
    }else {
        //3、 If there is no stock , Write main table inventory , And get id, Function write history table 
        sql = "INSERT INTO tb_stock_storage (warehouse_id, sku_id, quanty) VALUES (1, "+sku_id+", "+thisQuanty+")";
        KeyHolder keyHolder = new GeneratedKeyHolder();
        final String finalSql = sql;
        result = jdbcTemplate.update(new PreparedStatementCreator() {

            public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
                PreparedStatement preparedStatement = connection.prepareStatement(finalSql, Statement.RETURN_GENERATED_KEYS);
                return preparedStatement;
            }

        }, keyHolder)==1;

        //3.1、 If the write fails , Return error message  msg
        if (!result){
            resultMap.put("result", false);
            resultMap.put("msg", " Failed to write inventory master table !");
            return resultMap;
        }

        new_id = keyHolder.getKey().intValue();
    }

    //4、 Write history table 
    sql = "INSERT INTO tb_stock_storage_history (stock_storage_id, in_quanty, out_quanty) " +
            "VALUES (?, ?, ?)";
    result = jdbcTemplate.update(sql, new_id, in_quanty, out_quanty)==1;

    //4.1、 If the write fails , Return error message  msg
    if (!result){
        resultMap.put("result", false);
        resultMap.put("msg", " Failed to write the storage history table !");
        return resultMap;
    }

    //5、 If there is stock , Update the main table in reverse 
    if (list!=null&&list.size()>0){
        sql = "UPDATE tb_stock_storage SET quanty = quanty + ? " +
                "WHERE id = ? AND quanty + ? >= 0";
        result = jdbcTemplate.update(sql, thisQuanty, new_id, thisQuanty)==1;
        //5.1、 If the write fails , Return error message  msg
        if (!result){
            resultMap.put("result", false);
            resultMap.put("msg", " Failed to update inventory master table !");
            return resultMap;
        }

    }

    //6、 Return normal data 
    resultMap.put("result", true);
    resultMap.put("msg", "");
    return resultMap;
}

First, check whether the product is in stock , This is whether the main table has this product , If there is , Prove that this commodity has been warehoused before , So update the inventory master table , without , Prove that this commodity has not been warehoused before , Therefore, it is necessary to write to the inventory master table , The reason is that the inventory balance can be directly queried in the inventory master table , Therefore, it is necessary to directly record the quantity of inventory balance .

The inventory history table is used to record the history of incoming and outgoing , Write it directly .

Code logic analysis :

  • According to the warehouse + goods SKU Query whether there is inventory in the inventory master table
  • without , Write to inventory master table , And get the... Written to the main table id, This id Used to write history table
  • If there is , Get the data of the main table directly according to the first query id
  • Use the obtained inventory master table id Write the inventory history table
  • Update the quantity of inventory master table uniformly

stay StorageService.java Class insertStorage Method , Return a product Map, The code is as follows :

@Transactional
public Map<String, Object> insertStorage(String sku_id, double in_quanty, double out_quanty){
    Map<String, Object> resultMap = new HashMap<String, Object>();

    //1、 Incoming parameter 
    if (sku_id.equals("")){
        resultMap.put("result", false);
        resultMap.put("msg", " Commodity sku Can't be empty !");
        return resultMap;
    }

    if (in_quanty==0&&out_quanty==0){
        resultMap.put("result", false);
        resultMap.put("msg", " Receipt quantity and issue quantity cannot be both 0!");
        return resultMap;
    }

    //2、 transfer StorageDao Methods 
    resultMap = iStorageDao.insertStorage(sku_id, in_quanty, out_quanty);

    //3、 return 
    return resultMap;
}

Because inventory is sensitive data , Therefore, you must make judgment when operating inventory , stay Service Judgment logic ,Dao Judge database logic in .

stay StorageController.java Class insertStorage Method , Need to add notes @RequestMapping, among value = "/insertStorage/{warehouse_id}/{sku_id}/{inquanty}/{outquanty}", Return a product Map, The code is as follows :

@RequestMapping(value = "/insertStorage/{sku_id}/{inquanty}/{outquanty}")
public Map<String, Object> insertStorage(@PathVariable("sku_id") String sku_id,
                             @PathVariable("inquanty") double inquanty, @PathVariable("outquanty") double outquanty){
    return iStorageService.insertStorage(sku_id, inquanty, outquanty);
}

4.4.8. Test inventory increase or decrease

Access directly from the page  getStorageQuanty  Method , Query on hand quantity , Reuse insertStorage  Method increase or decrease , Finally through  getStorageQuanty  Method to query again

The address to query the inventory is :http://localhost:6001/getStockStorage/26816294479

First, check the inventory 990 individual

Re pass insertStorage Methods increase or decrease inventory , Here is the minus 10 individual ,

The address for writing inventory is :http://localhost:6001/insertStorage/26816294479/10/7

The last time the inventory is queried, there are 980 individual ,

The address to query the inventory is :http://localhost:6001/getStockStorage/26816294479

4.4.9. Process inventory queue listening methods (storage_queue)

New dependency

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

stay src\main\java\com\itheima\leyou\ New folder under folder queue, stay queue New under folder java file StorageQueue

stay StorageQueue.java Write queue listening methods in , call iStorageService.insertStorage To write inventory , The code is as follows :

@Component
public class StorageQueue {

    @Autowired
    private IStorageService iStorageService;

    @RabbitListener(queues = "storage_queue")
    public void getStorageQueue(String msg){
        System.out.println("storage_queue receive messages :"+msg);

        Map<String, Object> result = new HashMap<String, Object>();
        try {
            result = iStorageService.insertStorage(msg, 0, 1);

            if (!(Boolean) result.get("result")){
                System.out.println("storage_queue Message processing failed :"+result.get("msg"));
            }
        }catch (Exception e){
            System.out.println("storage_queue Message processing failed :"+e.getMessage());
        }

        System.out.println("storage_queue Message processing completed !"+result);
    }
}

4.5. Create member services ( Port number 5000)

 


4.5.1. Create a membership table structure

DROP TABLE IF EXISTS `tb_user`;
CREATE TABLE `tb_user` (
  `id` BIGINT(20) NOT NULL AUTO_INCREMENT,
  `username` VARCHAR(32) NOT NULL COMMENT ' user name ',
  `password` VARCHAR(60) NOT NULL COMMENT ' password , Encrypted storage ',
  `phone` VARCHAR(11) DEFAULT NULL COMMENT ' Register mobile number ',
  `create_time` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP COMMENT ' Creation time ',
  `update_time` TIMESTAMP NULL COMMENT ' Update time ',
  PRIMARY KEY (`id`),
  UNIQUE KEY `username` (`username`) USING BTREE,
  UNIQUE KEY `phone` (`phone`)
) ENGINE=INNODB AUTO_INCREMENT=31 DEFAULT CHARSET=utf8 COMMENT=' User table ';

4.5.2. Create member services

First step : choice File-New-Module..., Select... From the pop-up window Spring initializr, choice Module SDK, choice Next

The second step :Group by com.itheima,Artifact by leyou,Name by leyouUser,Description by User For leyou Project, choice Next

The third step : choice  Spring Cloud Discovery, choice  Eureka Discovery Client, choice Next

Step four :Module name  by  leyouUser,Content root Is the path +leyouUser, choice Finish, A... Will be created under the project folder  leyouUser Folder

4.5.3. To configure pom.xml

In the generated project , open pom.xml, Configuration dependency , among spring-cloud-starter-netflix-eureka-client Introduced... For the project Eureka Client's jar package ,spring-boot-starter-web Introduced web scenario ,web Used in module development jar package , utilize alibaba Of fastjson analysis json data , So the introduction of alibaba.fastjson Use of jar package ,mysql-connector-java And spring-boot-starter-data-jpa Depend on the introduction of mysql The connection uses jar package

<?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>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-parent</artifactId>
        <version>2.1.6.RELEASE</version>
    </parent>

    <groupId>com.itheima</groupId>
    <artifactId>leyou</artifactId>
    <version>1.0-SNAPSHOT</version>

    <name>leyouUser</name>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

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

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

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

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.41</version>
        </dependency>

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

        <dependency>
            <groupId>org.springframework.session</groupId>
            <artifactId>spring-session-data-redis</artifactId>
            <version>2.1.7.RELEASE</version>
        </dependency>

    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Greenwich.SR2</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    
</project>

4.5.4. The configuration file application.properties

In the generated project , open src\man\resources\application.properties, Configure port number, etc

server.port=5000
spring.application.name=leyou-user
eureka.client.service-url.defaultZone=http://localhost:9000/eureka/

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/leyou?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root

#redis Database number , There is 0~15 common 16 A database 
spring.redis.database=0
#redis The server IP
spring.redis.host=127.0.0.1
#redis Port number 
spring.redis.port=6379
#redis password 
spring.redis.password=
#redis Request timeout , Beyond this value redis Automatically disconnect 
spring.redis.timeout=10000ms
#jedis maximum connection , If this value is exceeded, it will prompt that no connection exception can be obtained 
spring.redis.jedis.pool.max-active=32
#jedis Maximum waiting time , Exceeding this value will prompt the connection timeout exception 
spring.redis.jedis.pool.max-wait=10000ms
#jedis Maximum number of connections waiting 
spring.redis.jedis.pool.max-idle=32
#jedis Minimum number of waiting connections 
spring.redis.jedis.pool.min-idle=0

In the generated project , open src\main\java\com.itheima.leyou\leyouUserApplication, Add... To the generated startup class  @EnableEurekaClient, It means to start Eureka client

@SpringBootApplication
@EnableEurekaClient
public class UserApplication {

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

Test run order service , stay Eureka You can see in the registry  leyou-User service , Prove that the service was started successfully

4.5.5. establish controller\service\dao Folder , And create UserController.java\UserService.java\UserDao.java file

establish Dao Layer interface file ,IUserDao

public interface IUserDao {}

to UserDao.java Class add comments ,@Repository Used to label data access components , namely DAO Components , Realization IUserDao The interface of

@Repository
public class UserDao implements IUserDao {}

Statement  JDBCTemplate  Method , Used to connect to the database

@Autowired
private JdbcTemplate jdbcTemplate;

establish Service Layer interface ,IUserService

to UserService.java Class add comments , Realization IUserService Interface

@Service
public class UserService implements IUserService{}

Join in UserDao References to

@Autowired
private IUserDao iUserDao;

to UserController.java Class add comments

@RestController
public class UserController {}

Join in UserService References to interfaces

@Autowired
private IUserService iUserService;

4.5.6. Create a member query method (getUser)

purpose : Provide the front-end with member information data , It is mainly used for front-end member login

stay UserDao.java Class  getUser Method , Take two username,password Parameters , It means through username and password Search for , Return a member List, The code is as follows :

public ArrayList<Map<String, Object>> getUser(String username, String password){
    String sql = "select id AS user_id, username, phone, password from tb_user where username = ?";
    return (ArrayList<Map<String,Object>>) jdbcTemplate.queryForList(sql, username);
}

stay UserService.java Class getUser Method , Return a product List, The code is as follows :

public Map<String, Object> getUser(String username, String password){
    Map<String, Object> resultMap = new HashMap<String, Object>();
    //1、 Determine whether the passed in parameters are incorrect 
    if (username==null||username.equals("")){
        resultMap.put("result", false);
        resultMap.put("msg", " The username cannot be empty !");
        return resultMap;
    }

    //2、 Get the member list 
    ArrayList<Map<String, Object>> list = iUserDao.getUser(username, password);
    if (list==null||list.isEmpty()){
        resultMap.put("result", false);
        resultMap.put("msg", " No member information found !");
        return resultMap;
    }

    resultMap = list.get(0);
    resultMap.put("result", true);
    resultMap.put("msg", "");
    return resultMap;
}

4.5.7. Create a method to add members (insertUser)

purpose : Provide the front-end with member information to increase , It is mainly used for front-end member registration

stay UserDao.java Class  insertUser Method , belt username,phone,password Three parameters , Return to one Map, The code is as follows :

public int insertUser(String username, String password){
    final String sql = "insert into tb_user (username, phone, password) values ('"+username+"', '"+username+"', '"+password+"')";
    KeyHolder keyHolder = new GeneratedKeyHolder();
    jdbcTemplate.update(new PreparedStatementCreator() {
        public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
            PreparedStatement preparedStatement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
            return preparedStatement;
        }
    }, keyHolder);

    return keyHolder.getKey().intValue();
}

The code analysis :

  • First query whether the user exists according to the mobile phone number
  • If it exists, you will be prompted that the user already exists
  • Write it into the membership table according to three parameters
  • If the writing is correct, it returns result by true, Write error returns result by false

stay UserService.java Class insertUser Method , Return a product Map, The code is as follows :

@Transactional
public Map<String, Object> insertUser(String username, String password){
    Map<String, Object> resultMap = new HashMap<String, Object>();
    //1、 Determine whether the passed in parameters are incorrect 
    if (username==null||username.equals("")){
        resultMap.put("result", false);
        resultMap.put("msg", " The username cannot be empty !");
        return resultMap;
    }

    int user_id = iUserDao.insertUser(username, password);

    if (user_id<=0){
        resultMap.put("result", false);
        resultMap.put("msg", " The database did not execute successfully !");
        return resultMap;
    }

    resultMap.put("user_id", user_id);
    resultMap.put("username", username);
    resultMap.put("phone", username);
    resultMap.put("password", password);
    resultMap.put("result", true);
    resultMap.put("msg", "");
    return resultMap;
}

Be careful : there Service Business logic judgment , If Json When conversion error or the passed in parameter is null , This will cause an error in writing to the database , So in the incoming Dao Judge before layer , Whether the passed in parameter is legal . Resolved json Also need to judge , User name and phone number cannot be empty

4.5.8. stay UserController.java Class login Method

Need to add notes @RequestMapping, among value = "/login, Return to one Map, The code is as follows :

@RequestMapping(value = "/login", method = RequestMethod.POST)
public Map<String, Object> login(String username, String password, HttpServletRequest httpServletRequest){
    //1、 Get a member 
    Map<String, Object> userMap = new HashMap<String, Object>();
    userMap = iUserService.getUser(username, password);

    //2、 Did not get a member , Write member 
    if (!(Boolean) userMap.get("result")){
        userMap = iUserService.insertUser(username, password);
    }

    //3、 write in session
    HttpSession httpSession = httpServletRequest.getSession();
    String user = JSON.toJSONString(userMap);
    httpSession.setAttribute("user", user);

    Object o = httpSession.getAttribute("user");

    //4、 Return information 
    return userMap;
}

The code analysis :

  • login It uses the member query method (getUser), The method of increasing membership is also used (insertUser)
  • First, search for members according to the incoming phone number and password on the front page , If you find it , Direct login , If not , Directly register a , In general, in actual projects , There are two verification steps , One is the verification code , It is mainly used to verify the mobile phone number ; One is image verification , Mainly to prevent SMS bombing .
  • Write member information into session

4.6. Create order service ( Port number 4000)

 

4.6.1. Create order table structure

1、 Order master

purpose : Used to store order master table information , for example : The whole order amount 、 members 、 Payment type 、 Payment time, etc .

2、 Order details

purpose : Used to deposit order details , Associate with the order main table , An order may have multiple details , for example : A member buys more than one item , The quantity of each item 、 The unit price 、 Amount, etc. .

3、 Order logistics status table

purpose : Used to store the logistics status of the order , Associate with the order main table , An order will have multiple details , for example : Logistics company for each commodity 、 Logistics status 、 Whether to sign for it .

4.6.2. Create order service

First step : choice File-New-Module..., Select... From the pop-up window Spring initializr, choice Module SDK, choice Next

The second step :Group by com.itheima,Artifact by leyou,Name by leyouOrder,Description by Order For leyou Project, choice Next

The third step : choice  Spring Cloud Discovery, choice  Eureka Discovery Client, choice Next

Step four :Module name  by  leyouOrder,Content root Is the path +leyouOrder, choice Finish, A... Will be created under the project folder  leyouOrder Folder

4.6.3. To configure pom.xml

In the generated project , open pom.xml, Configuration dependency , among spring-cloud-starter-netflix-eureka-client Introduced... For the project Eureka Client's jar package ,spring-boot-starter-web Introduced web scenario ,web Used in module development jar package , utilize alibaba Of fastjson analysis json data , So the introduction of alibaba.fastjson Use of jar package ,mysql-connector-java And spring-boot-starter-data-jpa Depend on the introduction of mysql The connection uses jar package

<?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>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-parent</artifactId>
        <version>2.1.6.RELEASE</version>
    </parent>

    <groupId>com.itheima</groupId>
    <artifactId>leyou</artifactId>
    <version>1.0-SNAPSHOT</version>

    <name>leyouOrder</name>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

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

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

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

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.41</version>
        </dependency>

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

        <dependency>
            <groupId>org.springframework.session</groupId>
            <artifactId>spring-session-data-redis</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Greenwich.SR2</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    
</project>

4.6.4. The configuration file application.properties

In the generated project , open src\man\resources\application.properties, Configure port number, etc

server.port=4000
spring.application.name=leyou-order
eureka.client.service-url.defaultZone=http://localhost:9000/eureka/

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/leyou?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root


#redis Database number , There is 0~15 common 16 A database 
spring.redis.database=0
#redis The server IP
spring.redis.host=127.0.0.1
#redis Port number 
spring.redis.port=6379
#redis password 
spring.redis.password=
#redis Request timeout , Beyond this value redis Automatically disconnect 
spring.redis.timeout=10000ms
#jedis maximum connection , If this value is exceeded, it will prompt that no connection exception can be obtained 
spring.redis.jedis.pool.max-active=32
#jedis Maximum waiting time , Exceeding this value will prompt the connection timeout exception 
spring.redis.jedis.pool.max-wait=10000ms
#jedis Maximum number of connections waiting 
spring.redis.jedis.pool.max-idle=32
#jedis Minimum number of waiting connections 
spring.redis.jedis.pool.min-idle=0

In the generated project , open src\main\java\com.itheima.leyou\leyouOrderApplication, Add... To the generated startup class  @EnableEurekaClient, It means to start Eureka client

@SpringBootApplication
@EnableEurekaClient
public class OrderApplication {

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

}

Test run order service , stay Eureka You can see in the registry  leyou-Order service , Prove that the service was started successfully

4.6.5. establish controller\service\dao Folder , And create OrderController.java\OrderService.java\OrderDao.java file

establish Dao Layer interface file ,IOrderDao

public interface IOrderDao{}

to OrderDao.java Class add comments ,@Repository Used to label data access components , namely DAO Components , Realization IOrderDao Interface

@Repository
public class OrderDao implements IOrderDao {}

Statement  JDBCTemplate  Method , Used to connect to the database

@Autowired
JdbcTemplate jdbcTemplate;

establish Service Layer interface file ,IOrderService

to OrderService.java Class add comments , Realization IOrderService Interface

@Service
public class OrderService implements IOrderService {}

Join in IOrderDao References to

@Autowired
private IOrderDao iOrderDao;

to OrderController.java Class add comments

@RestController
public class OrderController {}

Join in IOrderService References to

@Autowired
private IOrderService iOrderService;

4.6.6. Add order creation method (createOrder)

It is mainly used to call the front-end submit order page , And write to the order queue ,

Be careful : We need to go back to order_id, The payment page passes order_id The query .

public Map<String, Object> createOrder(String sku_id, String user_id){
    Map<String, Object> resultMap = new HashMap<String, Object>();
    //1、 Judge sku_id
    if (sku_id==null||sku_id.equals("")){
        resultMap.put("result", false);
        resultMap.put("msg", " The front end is wrong again !");
        return resultMap;
    }

    //2、 take redis policy 
    String order_id = String.valueOf(System.currentTimeMillis());
    String policy = stringRedisTemplate.opsForValue().get("LIMIT_POLICY_"+sku_id);
    if (policy!=null&&!policy.equals("")){
        //3、 The start time is less than or equal to the current time , The current time is less than or equal to the end 
        Map<String, Object> policyMap = JSONObject.parseObject(policy, Map.class);

        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
        String now = restTemplate.getForObject("http://leyou-time-server/getTime", String.class);
        try {
            Date begin_time = simpleDateFormat.parse(policyMap.get("begin_time").toString());
            Date end_time = simpleDateFormat.parse(policyMap.get("end_time").toString());
            Date now_time = simpleDateFormat.parse(now);

            if (begin_time.getTime()<=now_time.getTime()&&now_time.getTime()<=end_time.getTime()){
                int limitQuanty = Integer.parseInt(policyMap.get("quanty").toString());

                //4、redis Counter 
                // +1+1+1=3  4

                if (stringRedisTemplate.opsForValue().increment("SKU_QUANTY_"+sku_id, 1)<=limitQuanty){
                    //5、 Written to the queue 
                    // tb_order: order_id, total_fee, actual_fee, post_fee, payment_type, user_id, status, create_time
                    // tb_order_detail: order_id, sku_id, num, title, own_spec, price, image, create_time
                    // tb_sku: sku_id, title, images, stock, price, indexes, own_spec

                    String sku = stringRedisTemplate.opsForValue().get("SKU_"+sku_id);
                    Map<String, Object> skuMap = JSONObject.parseObject(sku, Map.class);

                    Map<String, Object> orderInfo = new HashMap<String, Object>();
                    orderInfo.put("order_id", order_id);
                    orderInfo.put("total_fee", skuMap.get("price"));
                    orderInfo.put("actual_fee", policyMap.get("price"));
                    orderInfo.put("post_fee", 0);
                    orderInfo.put("payment_type", 0);
                    orderInfo.put("user_id", user_id);
                    orderInfo.put("status", 1);
                    orderInfo.put("create_time", now);

                    orderInfo.put("sku_id", skuMap.get("sku_id"));
                    orderInfo.put("num", 1);
                    orderInfo.put("title", skuMap.get("title"));
                    orderInfo.put("own_spec", skuMap.get("own_spec"));
                    orderInfo.put("price", policyMap.get("price"));
                    orderInfo.put("image", skuMap.get("images"));

                    String order = JSON.toJSONString(orderInfo);
                    try {
                        amqpTemplate.convertAndSend("order_queue", order);
                    }catch (Exception e){
                        resultMap.put("result", false);
                        resultMap.put("msg", " Write queue exception !");
                        return resultMap;
                    }

                }else {
                    // If the counter is exceeded , The returned goods have been sold out 
                    resultMap.put("result", false);
                    resultMap.put("msg", "3 Billion 9 Kicked back !");
                    return resultMap;
                }
            }else {
                // If the end time is greater than the current time , The return activity has expired 
                resultMap.put("result", false);
                resultMap.put("msg", " The activity has expired !");
                return resultMap;
            }


        } catch (ParseException e) {
            e.printStackTrace();
        }
    }else {
        // The policy does not withdraw the amount , The return activity has expired 
        resultMap.put("result", false);
        resultMap.put("msg", " The activity has expired !");
        return resultMap;
    }

    //6、 Return normal data , With the order number 
    resultMap.put("result", true);
    resultMap.put("msg", "");
    resultMap.put("order_id", order_id);
    return resultMap;

}

stay OrderController.java Add the method of creating order in , The code is as follows :

@RequestMapping(value = "/createOrder/{sku_id}")
public Map<String, Object> createOrder(@PathVariable("sku_id") String sku_id, HttpServletRequest httpServletRequest){
    Map<String, Object> resultMap = new HashMap<String, Object>();
    HttpSession httpSession = httpServletRequest.getSession();
    Object userObj = httpSession.getAttribute("user");

    if (userObj==null){
        resultMap.put("result", false);
        resultMap.put("msg", " Members cannot purchase without logging in !");
        return resultMap;
    }
    Map<String, Object> userMap = JSONObject.parseObject(userObj.toString(), Map.class);
    return iOrderService.createOrder(sku_id, userMap.get("user_id").toString());
}

Be careful : Need from session Value in , If you don't get the member information, you can't buy !

4.6.7. Create write order method (insertOrder)

It is mainly used to write orders in the queue , stay OrderDao.java add insertOrder Method , The code is as follows :

public boolean insertOrder(Map<String, Object> orderInfo){
    // Write to main table 
    String sql = "insert into tb_order (order_id, total_fee, actual_fee, post_fee, payment_type, user_id, status, create_time) " +
            "values (?, ?, ?, ?, ?, ?, ?, ?)";
    boolean result = jdbcTemplate.update(sql, orderInfo.get("order_id"), orderInfo.get("total_fee"), orderInfo.get("actual_fee"),
            orderInfo.get("post_fee"), orderInfo.get("payment_type"), orderInfo.get("user_id"), orderInfo.get("status"),
            orderInfo.get("create_time"))==1;

    // Write parts list 
    if (result){
        sql = "INSERT INTO tb_order_detail (order_id, sku_id, num, title, own_spec, price, image, create_time) " +
                "VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
        result = jdbcTemplate.update(sql, orderInfo.get("order_id"), orderInfo.get("sku_id"), orderInfo.get("num"),
                orderInfo.get("title"), orderInfo.get("own_spec"), orderInfo.get("price"), orderInfo.get("image"), orderInfo.get("create_time"))==1;
    }

    return result;
}

stay OrderService.java Increase in insertOrder Method

public Map<String, Object> insertOrder(Map<String, Object> orderInfo){
    Map<String, Object> map = new HashMap<String, Object>();
    if (orderInfo==null||orderInfo.isEmpty()){
        map.put("result", false);
        map.put("msg", " Error in incoming parameter !");
        return map;
    }

    boolean result = iOrderDao.insertOrder(orderInfo);

    if (!result){
        map.put("result", false);
        map.put("msg", " Order writing failed !");
        return map;
    }

    map.put("result", true);
    map.put("msg", "");
    return map;
}

Called by the queue iOrderService.insertOrder

4.6.8. Create order queue (order_queue)

Dependency needed

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

stay src\main\java\com\itheima\leyou\ New folder under folder queue, stay queue New under folder java file OrderQueue

@Component
public class OrderQueue {

    @Autowired
    private IOrderService iOrderService;

    @RabbitListener(queues = "order_queue")
    public void insertOrder(String msg){
        //1、 Receive messages and output 
        System.out.println("order_queue receive messages :"+msg);

        //2、 Call a write order method 
        Map<String, Object> orderInfo = JSONObject.parseObject(msg, Map.class);
        Map<String, Object> resultMap = new HashMap<String, Object>();
        resultMap = iOrderService.insertOrder(orderInfo);

        //3、 If it is not written successfully, an error message will be output 
        if (!(Boolean) resultMap.get("result")){
            System.out.println("order_queue Failed to process message :");
        }

        //4、 Successfully output the message 
        System.out.println("order_queue Message processed successfully !");
    }
}

4.6.9. Test create order

First the session Your judgment hangs ,user_id The assignment is 1.

Type in the browser  http://localhost:4000/createOrder/26816294479

 

The queue is also processed successfully .

4.6.10. Create order query method (getOrder)

purpose : Check order details , For Member Center -- My order inquiry

First, understand that the table structure of the order is : Order master 、 Order details 、 Order logistics status table , An order has a main table , Multiple details , Multiple logistics states .

stay OrderDao.java Class  getOrder Method , With a orderid Parameters , It means through orderid Search for , Return an order map, The code is as follows :

public ArrayList<Map<String, Object>> getOrder(String order_id){
    String sql = "select d.sku_id, m.order_id, d.price " +
            "from tb_order m inner join tb_order_detail d on m.order_id = d.order_id " +
            "where m.order_id = ?";
    return (ArrayList<Map<String, Object>>) jdbcTemplate.queryForList(sql, order_id);
}

stay StorageService.java Class getOrder Method , Return a product Map, The code is as follows :

public Map<String, Object> getOrder(String order_id){
    Map<String, Object> resultMap = new HashMap<String, Object>();

    if (order_id==null||order_id.equals("")){
        resultMap.put("result", false);
        resultMap.put("msg", " Error in parameter input !");
        return resultMap;
    }

    ArrayList<Map<String, Object>> list = iOrderDao.getOrder(order_id);
    resultMap.put("order", list);
    resultMap.put("result", true);
    resultMap.put("msg", "");
    return resultMap;
}

stay StorageController.java Class getOrder Method , Need to add notes @RequestMapping, among value = "/getOrder/{orderid}, Return a product Map, The code is as follows :

@RequestMapping(value = "/getOrder/{order_id}")
public Map<String, Object> getOrder(@PathVariable("order_id") String order_id){
    return iOrderService.getOrder(order_id);
}

4.6.11. Create an update order status method (updateOrderStatus)

It is mainly used to update the order status in the queue , stay OrderDao.java add updateOrderStatus Method , The code is as follows :

public boolean updateOrderStatus(String order_id){
    String sql = "update tb_order set status = 2 where order_id = ?";
    return jdbcTemplate.update(sql, order_id)==1;
}

4.6.12. Create order payment method (payOrder)

It is mainly used for calling the front-end payment page , And write the order status update queue

stay OrderService.java add payOrder Method

public Map<String, Object> payOrder(String order_id, String sku_id){
    Map<String, Object> resultMap = new HashMap<String, Object>();
    if (order_id==null||order_id.equals("")){
        resultMap.put("result", false);
        resultMap.put("msg", " The order is wrong !");
        return resultMap;
    }

    boolean result = iOrderDao.updateOrderStatus(order_id);

    if (!result){
        resultMap.put("result", false);
        resultMap.put("msg", " Order status update failed !");
        return resultMap;
    }

    amqpTemplate.convertAndSend("storage_queue", sku_id);

    resultMap.put("result", true);
    resultMap.put("msg", "");
    return resultMap;
}

stay OrderController.java add payOrder Method

@RequestMapping(value = "/payOrder/{order_id}/{sku_id}")
public Map<String, Object> payOrder(@PathVariable("order_id") String order_id, @PathVariable("sku_id") String sku_id){
    // Normally, the payment interface will be called here , The simulated payment has returned normal data 
    boolean isPay = true;
    Map<String, Object> resultMap = new HashMap<String, Object>();
    if (!isPay){
        resultMap.put("result", false);
        resultMap.put("msg", " Payment interface call failed !");
        return resultMap;
    }

    return iOrderService.payOrder(order_id, sku_id);
}

4.6.13. Test query order

Access directly from the page  getOrder  Method query ,http://localhost:4000/getOrder/1565019341112

4.7. Create gateway service ( Port number 80)


4.7.1. Create gateway service

First step : choice File-New-Module..., Select... From the pop-up window Spring initializr, choice Module SDK, choice Next

The second step :Group by com.itheima,Artifact by leyou,Name by ZuulServer,Description by Zuul For leyou Project, choice Next

The third step : choice  Spring Cloud Discovery, choice  Eureka Discovery Client, choice Next

Step four :Module name  by  ZuulServer,Content root Is the path +ZuulServer, choice Finish, A... Will be created under the project folder  ZuulServer Folder

4.7.2. To configure pom.xml

In the generated project , open pom.xml, Configuration dependency , among spring-cloud-starter-netflix-eureka-client Introduced... For the project Eureka Client's jar package ,spring-boot-starter-web Introduced web scenario ,web Used in module development jar package

<?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>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-parent</artifactId>
        <version>2.1.6.RELEASE</version>
    </parent>

    <groupId>com.itheima</groupId>
    <artifactId>leyou</artifactId>
    <version>1.0-SNAPSHOT</version>

    <name>leyouZuul</name>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>

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

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Greenwich.SR2</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

4.7.3. The configuration file application.properties

In the generated project , open src\man\resources\application.properties, Configure port number, etc

server.port=80
spring.application.name=leyou-zuul
eureka.client.service-url.defaultZone=http://localhost:9000/eureka/



# Ignore the default service mapping path of the framework 
zuul.ignored-services='*'
# Do not ignore the framework's permission related header information 
zuul.ignore-security-headers=false

zuul.host.socket-timeout-millis=60000
zuul.host.connect-timeout-millis=60000

zuul.host.max-total-connections=500

zuul.routes.leyou-client.path=/leyouClient/**
zuul.routes.leyou-client.serviceId=leyou-client
# prevent session Inconsistency 
zuul.routes.leyou-client.sensitiveHeaders="*"

zuul.routes.leyou-order.path=/leyouOrder/**
zuul.routes.leyou-order.serviceId=leyou-order
zuul.routes.leyou-order.sensitiveHeaders="*"

zuul.routes.leyou-user.path=/leyouUser/**
zuul.routes.leyou-user.serviceId=leyou-user
zuul.routes.leyou-user.sensitiveHeaders="*"

zuul.routes.leyou-stock.path=/leyouStock/**
zuul.routes.leyou-stock.serviceId=leyou-stock
zuul.routes.leyou-stock.sensitiveHeaders="*"

zuul.routes.leyou-storage.path=/leyouStorage/**
zuul.routes.leyou-storage.serviceId=leyou-storage
zuul.routes.leyou-storage.sensitiveHeaders="*"

zuul.routes.leyou-time-server.path=/leyouTimeServer/**
zuul.routes.leyou-time-server.serviceId=leyou-time-server
zuul.routes.leyou-time-server.sensitiveHeaders="*"

zuul.routes.leyou-order.path Path for each service

zuul.routes.leyou-stock.serviceId Number for each service

And so on , Configure all services

Be careful :zuul Use 80 port , You do not need to enter the port number when accessing .

In the generated project , open src\main\java\com.itheima.leyou\leyouTimeServerApplication, Add... To the generated startup class  @EnableZuulProxy, When Zuul And Eureka、Ribbon When the components are used together , We use @EnableZuulProxy

@SpringBootApplication
@EnableZuulProxy
public class ZuulApplication {

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

}

Test and run the gateway service , Use the previous method to query inventory :getStockStorage

The original  http://localhost:6001/getStockStorage/26816294479

Change it to http://localhost/leyouStorage/getStockStorage/26816294479

4.8. Create a page service ( Port number 3000)


4.8.1. Create a page service

First step : choice File-New-Module..., Select... From the pop-up window Spring initializr, choice Module SDK, choice Next

The second step :Group by com.itheima,Artifact by leyou,Name by leyouClient,Description by Client For leyou Project, choice Next

The third step : choice  Web, choice  Spring Web Starter, choice Next

Step four :Module name  by  leyouClient,Content root Is the path +leyouClient, choice Finish, A... Will be created under the project folder  leyouClient Folder

4.8.2. To configure pom.xml

In the generated project , open pom.xml, Configuration dependency , among spring-cloud-starter-netflix-eureka-client Introduced... For the project Eureka Client's jar package ,spring-boot-starter-web Introduced web scenario ,web Used in module development jar package

<?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>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-parent</artifactId>
        <version>2.1.6.RELEASE</version>
    </parent>

    <groupId>com.itheima</groupId>
    <artifactId>leyou</artifactId>
    <version>1.0-SNAPSHOT</version>

    <name>leyouClient</name>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

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

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Greenwich.SR2</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    
</project>

stay pom.xml Medium build The following resource packs must be added to the tag , Otherwise, if you visit the page, you will report 404, as a result of SpringBoot You must specify the path first , Then compile successfully and start again , To access the page

<build>
   <plugins>
      <plugin>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
   </plugins>

   <!--  The following resource packages must be introduced to solve  jsp404  The problem of  -->
   <resources>
      <resource>
         <directory>src/main/java</directory>
         <includes>
            <include>**/*.xml</include>
         </includes>
      </resource>
      <resource>
         <directory>src/main/resources</directory>
         <includes>
            <include>**/*.*</include>
         </includes>
      </resource>
      
      <resource>
         <!--  Appoint resources Which directory does the plug-in handle the resource files  -->
         <directory>src/main/webapp</directory>
         <!-- Note that this must be placed in this directory to be accessed  -->
         <targetPath>resources</targetPath>
         <includes>
            <include>**/*.*</include>
         </includes>
      </resource>
   </resources>
   <!--  solve  jsp404 -->
</build>

4.8.3. The configuration file application.properties

In the generated project , open src\man\resources\application.properties, Configure port number, etc

server.port=3000
spring.application.name=leyou-client
eureka.client.service-url.defaultZone=http://localhost:9000/eureka/

In the generated project , open src\main\java\com.itheima.leyou\leyouClientApplication, Add... To the generated startup class  @EnableEurekaClient, It means to start Eureka client

@SpringBootApplication
@EnableEurekaClient
public class ClientApplication {

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

}

Test run page service , stay Eureka You can see in the registry  leyou-client service , Prove that the service was started successfully

4.8.4. Create a page folder

1、  stay main Create webapp Folder

2、 stay webapp Create under folder page Folder , For storage html pagefile

3、 stay webapp Create under folder resources Folder , Used to store the imported plug-ins , Here's what I'm using bootstrap and jquery,bootstrap Mainly the style of the page ,jquery Mainly used ajax Interact with the background .

4.8.5. introduce bootstrap

take bootstrap-4.3.1-dist Copy folder to resources Under the folder

take jquery Copy folder to resources Under the folder

4.8.6. Create a member login page (loginPage.html)

purpose : For member registration 、 Member login

take loginPage.html Copied to the  main\webapp\page\ Under the folder

4.8.7. Create a product list page (stockListPage.html)

purpose : It is used to display the product list

take stockListPage.html Copied to the  main\webapp\page\ Under the folder

4.8.8. Create product details page (stockDetailPage.html)

purpose : Used to show product details

take stockDetailPage.html Copied to the  main\webapp\page\ Under the folder

4.8.9. Create order page (createOrderPage.html)

purpose : Used to submit orders

take createOrderPage.html Copied to the  main\webapp\page\ Under the folder

4.8.8. Create a payment page (payPage.html)

purpose : For payment of orders

take payPage.html Copied to the  main\webapp\page\ Under the folder

4.8.8. Create a seckill policy page (limitPolicyPage.html)

purpose : Used to add the second kill policy

take limitPolicyPage.html Copied to the  main\webapp\page\ Under the folder

4.9. Create configuration service ( Port number 2000)


4.9.1 Create configuration service

First step : choice File-New-Module..., Select... From the pop-up window Spring initializr, choice Module SDK, choice Next

The second step :Group by com.itheima,Artifact by leyou,Name by leyouClient,Description by Client For leyou Project, choice Next

The third step : choice  Spring Cloud Discovery, choice  Eureka Discovery Client, choice Next

Step four :Module name  by  leyouConfig,Content root Is the path +leyouConfig, choice Finish, A... Will be created under the project folder  leyouConfig Folder

4.9.2. ConfigServer To configure

In the generated project , open pom.xml, Configuration dependency , among spring-cloud-starter-netflix-eureka-client Introduced... For the project Eureka Client's jar package ,spring-boot-starter-web Introduced web scenario ,web Used in module development jar package ,spring-cloud-config-server Introduces the... Used to configure the server jar package

<?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>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-parent</artifactId>
        <version>2.1.6.RELEASE</version>
    </parent>

    <groupId>com.itheima</groupId>
    <artifactId>leyou</artifactId>
    <version>1.0-SNAPSHOT</version>

    <name>leyouConfig</name>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-config-server</artifactId>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Greenwich.SR2</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    
</project>

stay src\man\resources New under folder shared Folder , It is used to store the configuration files of various services

4.9.3. The configuration file application.properties

In the generated project , open src\man\resources\application.properties, Configure port number, etc

server.port=2000
spring.application.name=leyou-config-server
eureka.client.service-url.defaultZone=http://localhost:9000/eureka/
spring.cloud.config.server.native.search-locations=classPath:/shared
spring.cloud.config.profile=dev
spring.profiles.active=native

stay src\man\resources So let's make a new one shared Folder

Be careful : stay application.properties in spring.cloud.config.server.native.search-locations This item refers to finding the local file path , Point to shared in , In the actual project, most software companies may work on a server or a remote warehouse .

share The naming rules of the files in the folder are : In each service configuration file spring.application.name+ Configure the application.properties In the document spring.cloud.config.profile Value content .

for example :leyou-time-server It's time service (leyouTimeServer) Of spring.application.name, Configure the application.properties In the document spring.cloud.config.profile by dev, So the name of the configuration file for goods and services is leyou-time-server-dev.properties

leyou-time-server-dev.properties Configure the content :

server.port=8000
spring.application.name=leyou-time-server
eureka.client.service-url.defaultZone=http://localhost:9000/eureka/

In the generated project , open src\main\java\com.itheima.leyou\leyouConfigApplication, Add... To the generated startup class  @EnableEurekaClient, It means to start Eureka client , And add @EnableConfigServer, It means to start Config Configure the service

@SpringBootApplication
@EnableEurekaClient
@EnableConfigServer
public class ConfigApplication {

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

}

Take goods and services as an example :

Time service increases dependency

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-config-client</artifactId>
</dependency>

The original goods and services configuration file main/resources/application.properties The content in is suspended , Create a new one bootstrap.properties file , The code is as follows :

spring.application.name=leyou-time-server
spring.cloud.config.profile=dev
spring.cloud.config.uri=http://localhost:2000
spring.cloud.config.label=master
spring.profiles.active=dev

Test run configuration service and commodity service , stay Eureka You can see in the registry  leyou-config-server as well as leyou-stock service , Prove that the service configuration is successful

4.9.4. Find configuration file first

Modify the time service (leyouTimeServer) Of application.properties The configuration file

server.port=8001
spring.application.name=leyou-time-server
eureka.client.service-url.defaultZone=http://localhost:9000/eureka/

Be careful : The port number here is changed to 8001

Start configuration service , Restart the time service , The console can see that the startup is 8000

Do not start the configuration service , Restart the time service , The console can see that the startup is 8001

Conclusion : Service startup , Give priority to bootstrap.properties, If you can't find it , Look again application.properties, prove bootstrap Than application Priority of .

5、 ... and 、 Project test

5.1. New seckill policy

Address :http://localhost/leyouClient/page/limitPolicyPage.html

Choose products , Enter the second kill price 、 Seckill inventory 、 Starting time 、 End time , Point save .

5.2. The login page

Address :http://localhost/leyouClient/page/loginPage.html

5.3. Product list page

Enter your mobile number in the login page , password , Jump to the product list page

Address :http://localhost/leyouClient/page/stockListPage.html

5.4. Product details page

Select an item in the item list page

Address :http://localhost/leyouClient/page/stockDetailPage.html?sku_id=27359021557

5.5. place order

Click... On the product details page 【 Rush to buy 】

Address :http://localhost/leyouClient/page/createOrderPage.html?sku_id=27359021557

5.6. Payment page

Click... On the submit order page 【 place order 】

Address :http://localhost/leyouClient/page/payPage.html?order_id=1565061554849

Click on 【 Wechat payment 】, Tips “ Successful payment ”

About queues

原网站

版权声明
本文为[[email protected]@yxg]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/177/202206261820088246.html