当前位置:网站首页>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=rootIn 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
边栏推荐
- Static registration and dynamic registration of JNI
- 微信小程序 uniapp 左滑 删除 带删除icon
- System table SQLite of SQLite database_ master
- 50 lines of code to crawl TOP500 books and import TXT documents
- 必须要掌握的面试重点——索引和事务(附讲B-树与B+树)
- JVM entry door (1)
- 图像二值化处理
- Decompilation of zero time technology smart contract security series articles
- Detailed explanation of MySQL mvcc mechanism
- NFTGameFi链游系统开发详解方案丨链游系统开发原理解析
猜你喜欢
随机推荐
物联网协议的王者:MQTT
最小生成树、最短路径、拓扑排序、关键路径
PC end records 515 ground sweeping robot /scan data
成功解决之Jenkins报错:The goal you specified requires a project to execute but there is no POM
Numpy's Matplotlib
JVM entry Door (1)
读书笔记:《过程咨询 III》
LeetCode 面试题29 顺时针打印矩阵
LeetCode 128最长连续序列
SQL中的并、交、差运算
Union, intersection and difference operations in SQL
[unity] use C in unity to execute external files, such as Exe or bat
Clion编译catkin_ws(ROS工作空间包的简称)加载CMakeLists.txt出现的问题
Clion compiling catkin_ WS (short for ROS workspace package) loads cmakelists Txt problems
LeetCode 238 除自身以外数组的乘积
Class inheritance of 25class
MYSQL的下载与配置 mysql远程操控
博云,站在中国容器潮头
行锁与隔离级别案例分析
Numpy之matplotlib










