当前位置:网站首页>Commodity seckill system
Commodity seckill system
2022-06-26 18:27:00 【[email protected]】
Design thinking :
One 、 Learning goals
- Understand the business of secsha
- Master the design idea and technical architecture of second kill
- master SpringCloud Application for second kill business
- master redis For performance improvement
- master RabbitMQ For asynchronous processing of business splitting
Two 、 Understand the business and technical architecture of secsha
2.1. What is seckill
For businesses , According to the size of the merchant , Second kill is divided into three forms :
1、 The platform requires second kill on time , Similar to tmall 11,11 month 11 Japan 0 Start buying at , Or Jingdong's whole point rush , All initiated by the platform .
2、 Businesses do second kill for their stores , Usually the flagship store of the manufacturer , Occupy favorable advertising space on the front page of the platform , Enter the store to do second kill .
3、 Seckill page of WeChat official account for spike , Initiated by businesses operated by the official account .
According to the merchant's promotional activities , Second kill is divided into three ways :
1、 Limit second kill : The most common form of second kill , The spike price is absolutely too low to be believed or resisted to participate , This second kill is usually after the start 1-3 The second kill will be over in seconds .
2、 Low price Limited second kill : This form can also be understood as low discount second kill , Limited and unlimited time , The second is over , In this second kill form, businesses provide a certain number of goods , Until the second is over .
3、 Low price limited time limited second kill : This form can also be understood as low discount second kill , Time limit , Within the specified time , Whether the goods are killed or not , The second kill will end .
2.2. Business characteristics of secsha
1、 The amount of instantaneous concurrency is large : A large number of users will rush to buy at the same time , Website traffic surged instantly .
2、 The stock is low : It's usually a low price limit , And the number of visits is far greater than the number of inventory , Only a few people succeeded .
3、 Simple business process : Short process , Buy now , Place the order , Reduce inventory .
4、 Pre heating : For the second kill goods that have not opened the activity , Display in countdown mode , You can only access and cannot place orders .
2.3. Design thinking
1、 Current limiting : Only a small number of successful second kill people can enter the backstage , Interact with the database , To reduce the pressure on the database server .
2、 cache : Write some business logic to the cache , for example : Restricted quantity of goods 、 Second kill policy, etc .
3、 asynchronous : Split business logic , Reduce server pressure , for example : The normal business process is to place orders 、 payment 、 Inventory reduction is completed at the same time , Business logic can be split during second kill .
4、 preheating : Business promotion , And set the second kill goods in advance 、 Second kill time 、 Limited quantity , Write the set item to redis cache .
5、 Exhibition : The page is divided into two layers , The first level is the product list page , The second level is the product details page , Enter the product details page through the product list page link , Before the second kill , Display goods second kill countdown , Operation not allowed to submit order , Only product details are allowed to be viewed . When the second kill begins , Display commodity spike expiration time .
6、 place order : After submitting the order, the second kill will redis The number in the cache is reduced , And prompt for payment .
7、 Queue operation : When payment is successful , Write the seckill success details to rabbitMQ, The order service listens and writes the received message to the order , The inventory service listens and receives messages to reduce inventory .
8、 Time server : The page server is deployed through load , The time of each server may be inconsistent , Therefore, time service is increased , To provide a unified time .
Overall architecture :
Eureka Client:
Time service (leyouTimeServer, Port number 8000): Provide a time unified interface for page services .
Goods and services (leyouStock, Port number 7000): External interface ( List of goods 、 Goods details 、 Second kill policy ).
Inventory service (leyouStorage, Port number 6001): Queue listening , Extract messages from the queue and interact with the database to reduce inventory .
Member services (leyouUser, Port number 5000): Provide member data interface for page service , Addition of members 、 modify 、 Sign in .
Order service (leyouOrder, Port number 4000): Queue listening , Extract messages from the queue and interact with the database to generate orders .
Page services (leyouClient, Port number 3000): Provide data interface for front-end pages .
Eureka Server:
Registry Center (leyouServer, Port number 9000) All services are registered in the registry .
Configuration center (leyouConfig): Provide the configuration required for all services .
Redis Application :
Number of cached items 、 Second kill policy .
Merchant's second kill policy 、 Set the commodity limit , Set write complete Redis.
Consumer access to product details , After submitting the order , from Redis Reduce the quantity of goods in .
Redis Access content in :
1、 Deposit when the policy is added ,key The value of is :LIMIT_POLICY_{sku_id},value The value of is the policy content
2、 When retrieving data from commodity list , adopt key(LIMIT_POLICY_{sku_id}), Extract policy content .
3、 After the policy expires , Automatically delete .
RabbitMQ Application :
Consumer submits order , Auto write order queue :
Order queue : Order service listens to order queue , After receiving the message, write the queue information to the database order table .
After consumer payment , Update order status , Write to the inventory queue after the update is successful
Inventory queue : The inventory service listens to the inventory queue , After receiving the message, write the inventory information to the database to reduce inventory .
2.5. database structure
3、 ... and 、 Second kill environment setup ( understand )
3.1. install redis And configuration
3.1.2. To configure redis
After installation , You need to do some setup first , So that the service can run normally after startup . Using the text editor , Use here Notepad++, open Redis Service profile . Be careful : Don't make a mistake , Usually it is redis.windows-service.conf, instead of redis.windows.conf. The latter is the configuration file used by bootloader in non system service mode .
Find the containing requirepass Where the words are , Append a row , Input requirepass leyou. This is a visit Redis The password required when , You also need to set it later in the project .
Test it Redis Whether the service is normally provided . Get into Redis The catalog of ,cd C:\Program Files\Redis. Input redis-cli And return .(redis-cli It's a client program ) As shown in the picture, the normal prompt is to enter , And display the correct port number , Indicates that the service has been started .
3.1.2. To configure redis
After installation , You need to do some setup first , So that the service can run normally after startup . Using the text editor , Use here Notepad++, open Redis Service profile . Be careful : Don't make a mistake , Usually it is redis.windows-service.conf, instead of redis.windows.conf. The latter is the configuration file used by bootloader in non system service mode .
Find the containing requirepass Where the words are , Append a row , Input requirepass leyou. This is a visit Redis The password required when , You also need to set it later in the project .
Test it Redis Whether the service is normally provided . Get into Redis The catalog of ,cd C:\Program Files\Redis. Input redis-cli And return .(redis-cli It's a client program ) As shown in the picture, the normal prompt is to enter , And display the correct port number , Indicates that the service has been started .
Input auth leyou, Show OK, The password is correct .
Actually test reading and writing . Input set mykey "abd” And return , Used to save a key value . Input again get mykey, Get the key value saved just now .
Turn on persistence ,appendonly yes – The default is no.
Persistence : Put the data ( Like objects in memory ) Save to a permanent storage device . The main application of persistence is to store objects in memory in database , Or stored in a disk file 、 XML Data files and so on .
redis All data is cached in memory , When you restart or shut down the system , All data cached in memory will disappear , Never to be found again . So in order to keep the data for a long time , will Redis The data put in the cache is stored persistently .
The default write time is everysec, Per second
Can also be set to always, Write in real time , But there will be efficiency problems .
900 Seconds has a value stored in , Just persist once
300 Second has 10 Values are stored in , Just persist once
60 Second has 10000 Values are stored in , Just persist once
3.2. install RabbitMQ And configuration
3.2.1. install RabbitMQ client
install otp_win64_20.2.exe
3.2.2. install RabbitMQ Server side
install rabbitmq-server-3.7.4.exe
3.2.3. To configure RabbitMQ Server side
To configure RabbitMQ Client environment variable
To configure RabbitMQ Server environment variable
Add... To the environment variable RabbitMQ Server side
Installing a plug-in :rabbitmq-plugins.bat enable rabbitmq_management
restart RabbitMQ service
start-up RabbitMQ
Four 、 Seckill system creation
First, take a look at the directory structure of secsha
4.1. establish Eureka Registry Center ( Port number 9000)
First step : choice File-New-Module..., Select... From the pop-up window Spring initializr, choice Module SDK, choice Next
The second step :Group by com.itheima,Artifact by leyou,Name by leyouServer,Description by Server For leyou Project, choice Next
The third step : choice Spring Cloud Discovery, choice Eureka Server, choice Next
Step four :Module name by leyouServer,Content root Is the path +leyouServer, choice Finish, A... Will be created under the project folder leyouServer Folder
4.1.1. To configure pom.xml
In the generated project , open pom.xml, Configuration dependency
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-parent</artifactId>
<version>2.1.6.RELEASE</version>
</parent>
<groupId>com.itheima</groupId>
<artifactId>leyou</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
Click... In the window suspended in the lower right corner Import Changes, Will automatically arrive Maven Remote warehouse to download the required jar package .
If there is no introduction , According to the following figure , stay IDEA Choose... On the right Maven Projects, find leyouServer choice Dependencies Right click , choice Download Sources, I'll go too Maven For downloading from remote warehouse jar package , This is true for all subsequent projects .
4.1.2. The configuration file application.properties
In the generated project , open src\man\resources\application.properties, Configure port number, etc
server.port=9000
eureka.client.service-url.defaultZone=http://localhost:9000/eureka/
eureka.instance.hostname=localhost
spring.application.name=leyou-server
# Do not get service information from the server
eureka.client.fetch-registry=false
# Do not register on the server
eureka.client.register-with-eureka=false
Be careful : The port number here must not be followed by characters such as spaces , Otherwise, an error will be reported
4.1.3. Write the startup class
In the generated project , open src\main\java\com.itheima.leyou\leyouServerApplication, Add... To the generated startup class @EnableEurekaServer, It means to start Eureka Server side
@SpringBootApplication
@EnableEurekaServer
public class ServerApplication {
public static void main(String[] args) {
SpringApplication.run(ServerApplication.class, args);
}
}
Test run registry , Enter... In the browser http://localhost:9000
Come here , Registry service completed .
4.2. Create a time service ( Port number 8000)
4.2.1. Create a time service
First step : choice File-New-Module..., Select... From the pop-up window Spring initializr, choice Module SDK, choice Next
The second step :Group by com.itheima,Artifact by leyou,Name by leyouTimeServer,Description by TimeServer For leyou Project, choice Next
The third step : choice Spring Cloud Discovery, choice Eureka Discovery Client, choice Next
Step four :Module name by leyouTimeServer,Content root Is the path +leyouTimeServer, choice Finish, A... Will be created under the project folder leyouTimeServer Folder
In the generated project , open pom.xml, Configuration dependency , among spring-cloud-starter-netflix-eureka-client Introduced... For the project Eureka Client's jar package ,spring-boot-starter-web Introduced web scenario ,web Used in module development jar package
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-parent</artifactId>
<version>2.1.6.RELEASE</version>
</parent>
<groupId>com.itheima</groupId>
<artifactId>leyou</artifactId>
<version>1.0-SNAPSHOT</version>
<name>leyouTimeServer</name>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
In the generated project , open src\man\resources\application.properties, Configure port number, etc
server.port=8000
spring.application.name=leyou-time-server
eureka.client.service-url.defaultZone=http://localhost:9000/eureka/
In the generated project , open src\main\java\com.itheima.leyou\leyouTimeServerApplication, Add... To the generated startup class @EnableEurekaClient, It means to start Eureka client
@SpringBootApplication
@EnableEurekaClient
public class TimeServerApplication {
public static void main(String[] args) {
SpringApplication.run(TimeServerApplication.class, args);
}
}
Test run order service , stay Eureka You can see in the registry leyou-time-server service , Prove that the service was started successfully
4.2.2. establish controller Folder , And create timeController.java file
to TimeController.java Class add comments
@RestController
public class timeController {}
4.2.3. Create a time query method (getTime)
purpose : Provide a unified time standard for front-end seckill , stay TimeController Write the following code in the :
@RequestMapping(value = "/getTime")
public String getTime(){
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return simpleDateFormat.format(new Date());
}
4.2.4. Test time query
Access directly from the page getTime Method , Then get the current time , Address :http://localhost:8000/getTime
4.3. Create goods and services ( Port number 7000)
Project structure :
4.3.1. Create product table structure ( A little )
4.3.2. Create goods and services
First step : choice File-New-Module..., Select... From the pop-up window Spring initializr, choice Module SDK, choice Next
The second step :Group by com.itheima,Artifact by leyou,Name by leyouStock,Description by Stock For leyou Project, choice Next
The third step : choice Spring Cloud Discovery, choice Eureka Discovery Client, choice Next
Step four :Module name by leyouStock,Content root Is the path +leyouStock, choice Finish, A... Will be created under the project folder leyouStock Folder
4.3.3. To configure pom.xml
In the generated project , open pom.xml, Configuration dependency , among spring-cloud-starter-netflix-eureka-client Introduced... For the project Eureka Client's jar package ,spring-boot-starter-web Introduced web scenario ,web Used in module development jar package ( Each client must have this dependency ), utilize alibaba Of fastjson analysis json data , So the introduction of alibaba.fastjson Use of jar package ,mysql-connector-java And spring-boot-starter-data-jpa Depend on the introduction of mysql The connection uses jar package
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-parent</artifactId>
<version>2.1.6.RELEASE</version>
</parent>
<groupId>com.itheima</groupId>
<artifactId>leyou</artifactId>
<version>1.0-SNAPSHOT</version>
<name>leyouStock</name>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.41</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
4.3.4. The configuration file application.properties
In the generated project , open src\man\resources\application.properties, Configure port number, etc
server.port=7000
spring.application.name=leyou-stock
eureka.client.service-url.defaultZone=http://localhost:9000/eureka/
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/code1_2?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root
#redis Database number , There is 0~15 common 16 A database
spring.redis.database=0
#redis The server IP
spring.redis.host=127.0.0.1
#redis Port number
spring.redis.port=6379
#redis password
spring.redis.password=
#redis Request timeout , Beyond this value redis Automatically disconnect
spring.redis.timeout=10000ms
#jedis maximum connection , If this value is exceeded, it will prompt that no connection exception can be obtained
spring.redis.jedis.pool.max-active=32
#jedis Maximum waiting time , Exceeding this value will prompt the connection timeout exception
spring.redis.jedis.pool.max-wait=10000ms
#jedis Maximum number of connections waiting
spring.redis.jedis.pool.max-idle=32
#jedis Minimum number of waiting connections
spring.redis.jedis.pool.min-idle=0
4.3.5. Write the startup class
In the generated project , open src\main\java\com.itheima.leyou\leyouStockApplication, Add... To the generated startup class @EnableEurekaClient, It means to start Eureka client
@SpringBootApplication
@EnableEurekaClient
public class StockApplication {
public static void main(String[] args) {
SpringApplication.run(StockApplication.class, args);
}
}
Be careful : Here if @EnableEurekaClient Report errors , It proves that the import of dependent files is wrong , such as : In dependence spring-cloud-starter-netflix-eureka-client Add one later 1, Here's the picture :
It can lead to :
Be careful : The common mistakes are starter It's written in start, It's not easy to see , You can go to maven projects Go inside to see if there are any errors
Test run goods and services , stay Eureka You can see in the registry leyou-Stock service , Prove that the service was started successfully
4.3.6. establish controller\service\dao Folder , And create StockController.java\StockService.java\StockDao.java file
Controller: Management business (Service) Scheduling and managing jumps
Service: Manage specific functions 、 Branch judgment
Dao: Manage data interactions , Complete addition, deletion and modification
Controller Like a waiter , What customers order , At what table .
Service Like a chef , He made all the dishes on the menu requested by the front end .
Dao Like a kitchen worker , He deals with raw materials .
4.3.7. Create a product list query method (getStockList)
purpose : Provide product list data for front-end page services , It is mainly used to display the front-end product list page .
First agree on the data structure to be returned through interaction with the front end :
Back to json character string :
If the return value is wrong :{"result":"false", "msg":"****"}
If the return value is correct :{"result":"true", "msg":"", "sku_list":["id":1,"sku_id":...]}
First from Dao Method to start writing code :
establish Dao Layer interface file ,IStockDao
public interface IStockDao {}
to StockDao.java Class add comments ,@Repository Used to label data access components , namely DAO Components , Realization IStockDao Interface
@Repository
public class StockDao implements IStockDao{}
Statement JDBCTemplate Method , Used to connect to the database
@Autowired
private JdbcTemplate jdbcTemplate;
increase getStockList Method , First, query the data required by the commodity list from the database , Load one ArrayList variable , The reason is that the product list has multiple rows of data , It's a list , And then ArrayList return , The code is as follows :
//1、 Create a SQL
String sql = "select id AS sku_id, title, images, stock, price, indexes, own_spec " +
"from tb_sku";
//2、 Execute this SQL
ArrayList<Map<String, Object>> list = (ArrayList<Map<String, Object>>) jdbcTemplate.queryForList(sql);
//3、 Return the data
return list;
Rewrite Service
establish Service Layer interface file ,IStockService
public interface IStockService {}
to StockService.java Add notes , And implement IStockService Interface
@Service
public class StockService implements IStockService{}
Join in IStockDao References to interfaces
@Autowired
private IStockDao iStockDao;
increase getStockList Method , call StockDao Medium getStockList, Return to one Map
public Map<String, Object> getStockList(){
Map<String, Object> resultMap = new HashMap<String, Object>();
//1、 take IstockDao Methods
ArrayList<Map<String, Object>> list = iStockDao.getStockList();
//2、 If the data is not taken out , Return error message
if (list==null||list.size()==0){
resultMap.put("result", false);
resultMap.put("msg", " We don't know why we didn't take out the data !");
return resultMap;
}
//3、 from redis Take the data
resultMap = getLimitPolicy(list);
//4、 Return to normal information
resultMap.put("sku_list", list);
// resultMap.put("result", true);
resultMap.put("msg", "");
return resultMap;
}
Be careful : there Service Business logic judgment , When Dao After returning data , If the data is empty , Business logic judgment should be added , No corresponding product information was found , If you don't judge , The data obtained from the front page is empty , Then the page will be blank and no data will be displayed , It will bring bad experience to the page operator , doubt : Is there a network outage ? Or server down ? So back here “ No corresponding product information was found ” A hint of , It is helpful for the friendly display of the page . Front end programmers can also use the result Parameter judgement
adopt alt+ enter , choice Create method 'getStockList', stay IStockDao Methods for automatically creating interfaces in interface files .
stay IStockDao Add... To the method in the interface file public Modifier ,
to StockController.java Class add comments
@RestController
public class StockController {}
Join in IStockService References to interfaces
@Autowired
private IStockService iStockService;
increase getStockList Method , call StockService Medium getStockList, Back to a Map, For the page is to get a Json character string
@RequestMapping(value = "/getStockList")
public Map<String, Object> getStockList(){
return iStockService.getStockList();
}
test getStockList Method , Enter... In the browser http://localhost:7000/getStockList
4.3.8. Create a product query method (getStock)
purpose : Provide product detail page data for front-end page services , Mainly used for front-end product detail page display .
stay StockDao.java Class getStock Method , With a sku_id Parameters , It means through sku_id Search for , Return a product Map, The code is as follows :
//1、 Create a SQL
String sql = "select tb_sku.spu_id, tb_sku.title, tb_sku.images, tb_sku.stock, tb_sku.price, tb_sku.indexes, " +
"tb_sku.own_spec, tb_sku.enable, tb_sku.create_time, tb_sku.update_time,tb_spu_detail.description," +
"tb_sku.id AS sku_id,tb_spu_detail.special_spec " +
"from tb_sku " +
"INNER JOIN tb_spu_detail ON tb_spu_detail.spu_id=tb_sku.spu_id " +
"where tb_sku.id = ?";
//2、 Execute this SQL
ArrayList<Map<String, Object>> list = (ArrayList<Map<String, Object>>) jdbcTemplate.queryForList(sql, sku_id);
//3、 Return the data
return list;
stay StockService.java Class getStock Method , Return a product Map, The code is as follows :
public Map<String, Object> getStock(String sku_id){
Map<String, Object> resultMap = new HashMap<String, Object>();
//1、 Pass in the parameter
if (sku_id==null||sku_id.equals("")){
resultMap.put("result", false);
resultMap.put("msg", " What's coming from the front ?");
return resultMap;
}
//2、 take IstockDao Methods
ArrayList<Map<String, Object>> list = iStockDao.getStock(sku_id);
//3、 If the data is not taken out , Return error message
if (list==null||list.size()==0){
resultMap.put("result", false);
resultMap.put("msg", " What happened to the database , I can't get the data !");
return resultMap;
}
//3、 from redis Take the data
resultMap = getLimitPolicy(list);
//4、 Return to normal information
resultMap.put("sku", list);
// resultMap.put("result", true);
// resultMap.put("msg", "");
return resultMap;
}
Be careful : there Service Business logic judgment , If the passed in parameter is null , This will result in database query errors , So in the incoming Dao Judge before layer , Whether the passed in parameter is legal . When Dao After returning data , If the data is empty , Business logic judgment also needs to be added , No corresponding product information was found
meanwhile , stay IStockDao Generate corresponding methods in .
stay StockController.java Class getStock Method , Need to add notes @RequestMapping, among value = "/getStock/{sku_id}, Return a product Map, The code is as follows :
@RequestMapping(value = "/getStock/{sku_id}")
public Map<String, Object> getStock(@PathVariable("sku_id") String sku_id){
return iStockService.getStock(sku_id);
}
test getStock Method , Enter... In the browser localhost:7000/getStock/123
Enter... In the browser localhost:7000/getStock/26816294479
from redis How to get the value in
private Map<String, Object> getLimitPolicy(ArrayList<Map<String, Object>> list){
Map<String, Object> resultMap = new HashMap<String, Object>();
for (Map<String, Object> skuMap: list){
//3.1、 from redis Take out the policy
String policy = stringRedisTemplate.opsForValue().get("LIMIT_POLICY_"+skuMap.get("sku_id").toString());
//3.2、 Only those who have policies can continue
if (policy!=null&&!policy.equals("")){
Map<String, Object> policyInfo = JSONObject.parseObject(policy, Map.class);
//3.3、 The start time is less than or equal to the current time , And the current time is less than or equal to the end time
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
String now = restTemplate.getForObject("http://leyou-time-server/getTime", String.class);
try {
Date end_time = simpleDateFormat.parse(policyInfo.get("end_time").toString());
Date begin_time = simpleDateFormat.parse(policyInfo.get("begin_time").toString());
Date now_time = simpleDateFormat.parse(now);
if (begin_time.getTime()<=now_time.getTime()&&now_time.getTime()<=end_time.getTime()){
skuMap.put("limitPrice", policyInfo.get("price"));
skuMap.put("limitQuanty", policyInfo.get("quanty"));
skuMap.put("limitBeginTime", policyInfo.get("begin_time"));
skuMap.put("limitEndTime", policyInfo.get("end_time"));
skuMap.put("nowTime", now);
}
} catch (ParseException e) {
e.printStackTrace();
}
}
}
resultMap.put("result", true);
resultMap.put("msg", "");
return resultMap;
}
4.3.9. Second kill policy table structure
DROP TABLE IF EXISTS `tb_limit_policy`;
CREATE TABLE `tb_limit_policy` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`sku_id` BIGINT(20) NOT NULL COMMENT 'skuid',
`quanty` BIGINT(20) COMMENT ' Number ',
`price` BIGINT(20) COMMENT ' Second kill price ',
`begin_time` TIMESTAMP COMMENT ' Starting time ',
`end_time` TIMESTAMP COMMENT ' End time ',
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
4.3.10. Create a second kill policy to increase the method (insertLimitPolicy)
purpose : Used to create a second kill policy , And write the policy into Redis cache .
stay StockDao.java Add one insertLimitPolicy Method , With a map Parameters ,map Here is the information from the front page , Return to one boolean value
public boolean insertLimitPolicy(Map<String, Object> map){
String sql = "insert into tb_limit_policy (sku_id, quanty, price, begin_time, end_time) " +
"Values (?, ?, ?, ?, ?)";
return jdbcTemplate.update(sql, map.get("sku_id"), map.get("quanty"), map.get("price"), map.get("begin_time"), map.get("end_time"))==1;
}
stay StockService.java One more in insertLimitPolicy Method , Return to one map, The code is as follows :
@Transactional
public Map<String, Object> insertLimitPolicy(Map<String, Object> policyMap){
//1、 Judge whether the passed in parameters are legal
Map<String, Object> resultMap = new HashMap<String, Object>();
if (policyMap==null||policyMap.isEmpty()){
resultMap.put("result", false);
resultMap.put("msg", " What does it bring in ");
return resultMap;
}
//2、 from StockDao The interface is called insertLimitPolicy Method
boolean result = iStockDao.insertLimitPolicy(policyMap);
//3、 Judge whether the execution succeeds or fails , If you fail , Return error message
if (!result){
resultMap.put("result", false);
resultMap.put("msg", " Data execution failed again ");
return resultMap;
}
//4、 If it works , write in redis, Validity period needs to be written ,key The name :LIMIT_POLICY_{sku_id}
long diff = 0;
String now = restTemplate.getForObject("http://leyou-time-server/getTime", String.class);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
// The effective period of the policy is obtained by subtracting the current date from the end date
try {
Date end_time = simpleDateFormat.parse(policyMap.get("end_time").toString());
Date now_time = simpleDateFormat.parse(now);
diff = (end_time.getTime() - now_time.getTime())/1000;
if (diff<0){
resultMap.put("result", false);
resultMap.put("msg", " The end time cannot be less than the current time ");
return resultMap;
}
} catch (ParseException e) {
resultMap.put("result", false);
resultMap.put("msg", " Date conversion failed again ");
return resultMap;
}
String policy = JSON.toJSONString(policyMap);
stringRedisTemplate.opsForValue().set("LIMIT_POLICY_"+policyMap.get("sku_id").toString(), policy, diff, TimeUnit.SECONDS);
ArrayList<Map<String, Object>> list = iStockDao.getStock(policyMap.get("sku_id").toString());
String sku = JSON.toJSONString(list.get(0));
stringRedisTemplate.opsForValue().set("SKU_"+policyMap.get("sku_id").toString(), sku, diff, TimeUnit.SECONDS);
//5、 Return to normal information
resultMap.put("result", true);
resultMap.put("msg", "");
return resultMap;
}
Be careful : there Service Business logic judgment , First, judge the incoming json Whether it can be successfully converted , The second is the success or failure of writing to the database .
Here's a @Transactional, The transaction needs to be started , Remember that as long as it is insert\delete\update All three statements need to start a transaction , Once writing to the database fails , You can roll back , Because the methods mentioned above are all queries , So you don't have to start a transaction
stay StockController One more in insertLimitPolicy Method , Need to add notes @RequestMapping, among value = "/insertLimitPolicy/{jsonObj}, Return a product Map, The code is as follows :
@RequestMapping(value = "/insertLimitPolicy/{jsonObj}")
public Map<String, Object> insertLimitPolicy(@PathVariable("jsonObj") String jsonObj){
return iStockService.insertLimitPolicy(jsonObj);
}
test insertLimitPolicy Method , Enter... In the browser http://localhost:7000/insertLimitPolicy/{sku_id:'26816294479',quanty:1000,price:1000,begin_time:'2019-08-05 11:00',end_time:'2019-10-05 12:00'}
4.3.11. When the new policy is added, deposit redis
step :
1、 To configure redis rely on
2、 The configuration file application.properties increase redis To configure
3、 Write a startup class to add restTemplate, The time service needs to be called
4、 stay Service Increase when RestTemplate and StringRedisTemplate Variable declaration for
4、 Write policy database table .
5、 Write the policy into Redis,key by LIMIT_POLICY_{sku_id}.
6、 It's time to finish , utilize Redis The deletion mechanism of , Automatically delete , To reduce memory usage . The time is obtained by subtracting the current time from the policy end time .
The code is as follows :
Statement StringRestTemplate
@Autowired
StringRedisTemplate stringRedisTemplate;
modify StockService Inside insertLimitPolicy Method , Add the following code
4.3.12. When encapsulating the product list and product details, take redis Policy approach (getLimitPolicy)
step :
1、 Circular product list
2、 Remove each sku_id The policy of
3、 Assigned to... In the item list
Encapsulation method :getLimitPolicy
The code is as follows :
private Map<String, Object> getLimitPolicy(ArrayList<Map<String, Object>> list){
Map<String, Object> resultMap = new HashMap<String, Object>();
for (Map<String, Object> skuMap: list){
//3.1、 from redis Take out the policy
String policy = stringRedisTemplate.opsForValue().get("LIMIT_POLICY_"+skuMap.get("sku_id").toString());
//3.2、 Only those who have policies can continue
if (policy!=null&&!policy.equals("")){
Map<String, Object> policyInfo = JSONObject.parseObject(policy, Map.class);
//3.3、 The start time is less than or equal to the current time , And the current time is less than or equal to the end time
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
String now = restTemplate.getForObject("http://leyou-time-server/getTime", String.class);
try {
Date end_time = simpleDateFormat.parse(policyInfo.get("end_time").toString());
Date begin_time = simpleDateFormat.parse(policyInfo.get("begin_time").toString());
Date now_time = simpleDateFormat.parse(now);
if (begin_time.getTime()<=now_time.getTime()&&now_time.getTime()<=end_time.getTime()){
skuMap.put("limitPrice", policyInfo.get("price"));
skuMap.put("limitQuanty", policyInfo.get("quanty"));
skuMap.put("limitBeginTime", policyInfo.get("begin_time"));
skuMap.put("limitEndTime", policyInfo.get("end_time"));
skuMap.put("nowTime", now);
}
} catch (ParseException e) {
e.printStackTrace();
}
}
}
resultMap.put("result", true);
resultMap.put("msg", "");
return resultMap;
}
Pay attention here : Write the policy into the list and send it to the front page
limitPrice: Policy price
limitBeginTime: Policy start time
limitEndTime: Policy end time
nowTime: current time
4.4. Create inventory service ( Port number 6001)
Be careful : The port number configured here is not 6000, It is 6001, as a result of Chrome Google browser 6000 Ports are inaccessible
4.4.1. Create inventory table structure
1、 Warehouse table
DROP TABLE IF EXISTS `tb_warehouse`;
CREATE TABLE `tb_warehouse` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT ' warehouse id',
`name` VARCHAR(64) NOT NULL COMMENT ' Warehouse name ',
`create_time` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP COMMENT ' Creation time ',
`update_time` TIMESTAMP NULL COMMENT ' Update time ',
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
2、 Inventory master table
purpose : Used to store inventory stock
DROP TABLE IF EXISTS `tb_stock_storage`;
CREATE TABLE `tb_stock_storage` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`warehouse_id` BIGINT(20) NOT NULL COMMENT ' warehouse id',
`sku_id` BIGINT(20) NOT NULL COMMENT 'skuid',
`quanty` DECIMAL(18,2) COMMENT ' The remaining quantity ',
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
3、 Inventory history table
purpose : Used to store inventory issue / receipt details
DROP TABLE IF EXISTS `tb_stock_storage_history`;
CREATE TABLE `tb_stock_storage_history` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`stock_storage_id` BIGINT(20) NOT NULL COMMENT ' Inventory master table id',
`in_quanty` DECIMAL(18,2) COMMENT ' Stock in quantity ',
`out_quanty` DECIMAL(18,2) COMMENT ' Delivery quantity ',
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
Inventory write rule :
Be careful : Inventory master table , A warehouse has only one record for each commodity ,quanty What is deposited is the remaining quantity .
Inventory history table , Each commodity in each warehouse corresponds to an inventory master table ID, Record the historical quantity of stock in and stock out .
Write rule description :
1、 adopt sku_id Determine whether the inventory master table has data ;
2、 If there's data , Get the... Of the inventory master table id;
3、 If there is no data , First write to the inventory master table , Get the... Of the inventory master table id;
4、 According to the inventory master table id Write to history table ;
5、 When there is data in the inventory master table , Through the... Of the inventory master table id Update quantity again .
4.4.2. Create inventory service
First step : choice File-New-Module..., Select... From the pop-up window Spring initializr, choice Module SDK, choice Next
The second step :Group by com.itheima,Artifact by leyou,Name by leyouStorage,Description by Storage For leyou Project, choice Next
The third step : choice Spring Cloud Discovery, choice Eureka Discovery Client, choice Next
Step four :Module name by leyouStorage,Content root Is the path +leyouStorage, choice Finish, A... Will be created under the project folder leyouStorage Folder
4.4.3. To configure pom.xml
In the generated project , open pom.xml, Configuration dependency , among spring-cloud-starter-netflix-eureka-client Introduced... For the project Eureka Client's jar package ,spring-boot-starter-web Introduced web scenario ,web Used in module development jar package , utilize alibaba Of fastjson analysis json data , So the introduction of alibaba.fastjson Use of jar package ,mysql-connector-java And spring-boot-starter-data-jpa Depend on the introduction of mysql The connection uses jar package
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-parent</artifactId>
<version>2.1.6.RELEASE</version>
</parent>
<groupId>com.itheima</groupId>
<artifactId>leyou</artifactId>
<version>1.0-SNAPSHOT</version>
<name>leyouStorage</name>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.41</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
4.4.4. The configuration file application.properties
In the generated project , open src\man\resources\application.properties, Configure port number, etc
server.port=6001
spring.application.name=leyou-storage
eureka.client.service-url.defaultZone=http://localhost:9000/eureka/
// Note the database name
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/leyou?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root
In the generated project , open src\main\java\com.itheima.leyou\leyouStorageApplication, Add... To the generated startup class @EnableEurekaClient, It means to start Eureka client
@SpringBootApplication
@EnableEurekaClient
public class StorageApplication {
public static void main(String[] args) {
SpringApplication.run(StorageApplication.class, args);
}
}
Test run inventory service , stay Eureka You can see in the registry leyou-Storage service , Prove that the service was started successfully
4.4.5. establish controller\service\dao Folder , And create StorageController.java\StorageService.java\StorageDao.java file
establish Dao Layer interface file ,IStorageDao
public interface IStorageDao {}
to StorageDao.java Class add comments ,@Repository Used to label data access components , namely DAO Components , Realization IStroageDao Interface
@Repository
public class StorageDao implements IStorageDao{}
Statement JDBCTemplate Method , Used to connect to the database
@Autowired
private JdbcTemplate jdbcTemplate;
establish Service Layer interface file ,IStorageService
to StorageService.java Class add comments , Realization IStorageService The interface of
@Service
public class StorageService implements IStorageService {}
Join in IStorageDao References to interfaces
@Autowired
private IStorageDao iStorageDao;
to StorageController.java Class add comments
@RestController
@Configuration
public class StorageController {}
Join in IStorageService References to interfaces
@Autowired
private IStorageService iStorageService;
4.4.6. Create an inventory query method (getStockStorage)
purpose : Query whether the actual inventory is deducted or increased
stay StorageDao.java Class getStockStorage Method , With a sku_id Parameters , It means through sku_id Search for , Return a product list, The code is as follows :
public ArrayList<Map<String, Object>> getStockStorage(String sku_id){
//1、SQL Value
String sql = "SELECT sku_id, quanty FROM tb_stock_storage WHERE sku_id = ?";
//2、 Return the data
return (ArrayList<Map<String,Object>>) jdbcTemplate.queryForList(sql, sku_id);
}
stay StorageService.java Class getStockStorage Method , Return a product Map, The code is as follows :
public Map<String, Object> getStockStorage(String sku_id){
//1、 First get the inventory of a commodity
ArrayList<Map<String ,Object>> list = new ArrayList<Map<String, Object>>();
list = iStorageDao.getStockStorage(sku_id);
//2、 Determine if the stockDao The item taken out is empty , Return to a hint
Map<String, Object> resultMap = new HashMap<String, Object>();
if (list==null||list.isEmpty()){
resultMap.put("result", false);
resultMap.put("msg", " Finished , The server is down , Data not extracted !");
return resultMap;
}
//3、 Judge if the goods taken out are not empty , Return the data
resultMap.put("storage", list);
resultMap.put("result", true);
resultMap.put("msg", "");
return resultMap;
}
stay StorageController.java Class getStockStorage Method , Need to add notes @RequestMapping, among value = "/getStockStorage/{sku_id}, Return a product Map, The code is as follows :
@RequestMapping(value = "/getStockStorage/{sku_id}")
public Map<String, Object> getStockStorage(@PathVariable("sku_id") String sku_id){
return iStorageService.getStockStorage(sku_id);
}
4.4.7. Create inventory increase / decrease method (insertStorage)
purpose : While listening to the queue , Get message , Deducting the inventory
First, understand the table structure of inventory : Inventory master table 、 Inventory history table . The inventory master table saves the current remaining quantity of inventory goods , The inventory history table is used to save the history of stock in and stock out .
for example :A The goods are in stock a There are still... In the warehouse 10 individual , Then in the main table is a warehouse ,A goods ,10 A number of
Stock purchase 20 individual , Buy out 10 individual , There are two records in the history table , One is in 20 individual , One is out 10 individual , The summarized quantity is exactly equal to the remaining quantity in the inventory master table
Someone will ask : Then you can check the inventory quantity and directly summarize the historical table ?
Is the answer , For example, JD's sales of a product will have 10 ten thousand +, Then the history table has 10 ten thousand + data , It will be time-consuming to summarize , You can easily get the remaining inventory quantity through the main table .
stay StorageDao.java Class insertStorage Method , Take three sku_id,inquanty,outquanty Parameters , It means how many items are written in which warehouse , return map, Be careful : The warehouse here is a virtual one id yes 1 The warehouse of , So when writing, just put warehouse_id The assignment is 1.
The code is as follows :
public Map<String, Object> insertStorage(String sku_id, double in_quanty, double out_quanty){
Map<String, Object> resultMap = new HashMap<String, Object>();
//1、 Query whether the inventory master table has inventory
String sql = "SELECT id FROM tb_stock_storage WHERE sku_id = ?";
ArrayList<Map<String, Object>> list = (ArrayList<Map<String, Object>>) jdbcTemplate.queryForList(sql, sku_id);
int new_id = 0;
double thisQuanty = in_quanty - out_quanty;
boolean result = false;
//2、 If there is stock , obtain id, Function 1 write history table , The second action is to update
if (list!=null&&list.size()>0){
new_id = Integer.parseInt(list.get(0).get("id").toString());
}else {
//3、 If there is no stock , Write main table inventory , And get id, Function write history table
sql = "INSERT INTO tb_stock_storage (warehouse_id, sku_id, quanty) VALUES (1, "+sku_id+", "+thisQuanty+")";
KeyHolder keyHolder = new GeneratedKeyHolder();
final String finalSql = sql;
result = jdbcTemplate.update(new PreparedStatementCreator() {
public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
PreparedStatement preparedStatement = connection.prepareStatement(finalSql, Statement.RETURN_GENERATED_KEYS);
return preparedStatement;
}
}, keyHolder)==1;
//3.1、 If the write fails , Return error message msg
if (!result){
resultMap.put("result", false);
resultMap.put("msg", " Failed to write inventory master table !");
return resultMap;
}
new_id = keyHolder.getKey().intValue();
}
//4、 Write history table
sql = "INSERT INTO tb_stock_storage_history (stock_storage_id, in_quanty, out_quanty) " +
"VALUES (?, ?, ?)";
result = jdbcTemplate.update(sql, new_id, in_quanty, out_quanty)==1;
//4.1、 If the write fails , Return error message msg
if (!result){
resultMap.put("result", false);
resultMap.put("msg", " Failed to write the storage history table !");
return resultMap;
}
//5、 If there is stock , Update the main table in reverse
if (list!=null&&list.size()>0){
sql = "UPDATE tb_stock_storage SET quanty = quanty + ? " +
"WHERE id = ? AND quanty + ? >= 0";
result = jdbcTemplate.update(sql, thisQuanty, new_id, thisQuanty)==1;
//5.1、 If the write fails , Return error message msg
if (!result){
resultMap.put("result", false);
resultMap.put("msg", " Failed to update inventory master table !");
return resultMap;
}
}
//6、 Return normal data
resultMap.put("result", true);
resultMap.put("msg", "");
return resultMap;
}
First, check whether the product is in stock , This is whether the main table has this product , If there is , Prove that this commodity has been warehoused before , So update the inventory master table , without , Prove that this commodity has not been warehoused before , Therefore, it is necessary to write to the inventory master table , The reason is that the inventory balance can be directly queried in the inventory master table , Therefore, it is necessary to directly record the quantity of inventory balance .
The inventory history table is used to record the history of incoming and outgoing , Write it directly .
Code logic analysis :
- According to the warehouse + goods SKU Query whether there is inventory in the inventory master table
- without , Write to inventory master table , And get the... Written to the main table id, This id Used to write history table
- If there is , Get the data of the main table directly according to the first query id
- Use the obtained inventory master table id Write the inventory history table
- Update the quantity of inventory master table uniformly
stay StorageService.java Class insertStorage Method , Return a product Map, The code is as follows :
@Transactional
public Map<String, Object> insertStorage(String sku_id, double in_quanty, double out_quanty){
Map<String, Object> resultMap = new HashMap<String, Object>();
//1、 Incoming parameter
if (sku_id.equals("")){
resultMap.put("result", false);
resultMap.put("msg", " Commodity sku Can't be empty !");
return resultMap;
}
if (in_quanty==0&&out_quanty==0){
resultMap.put("result", false);
resultMap.put("msg", " Receipt quantity and issue quantity cannot be both 0!");
return resultMap;
}
//2、 transfer StorageDao Methods
resultMap = iStorageDao.insertStorage(sku_id, in_quanty, out_quanty);
//3、 return
return resultMap;
}
Because inventory is sensitive data , Therefore, you must make judgment when operating inventory , stay Service Judgment logic ,Dao Judge database logic in .
stay StorageController.java Class insertStorage Method , Need to add notes @RequestMapping, among value = "/insertStorage/{warehouse_id}/{sku_id}/{inquanty}/{outquanty}", Return a product Map, The code is as follows :
@RequestMapping(value = "/insertStorage/{sku_id}/{inquanty}/{outquanty}")
public Map<String, Object> insertStorage(@PathVariable("sku_id") String sku_id,
@PathVariable("inquanty") double inquanty, @PathVariable("outquanty") double outquanty){
return iStorageService.insertStorage(sku_id, inquanty, outquanty);
}
4.4.8. Test inventory increase or decrease
Access directly from the page getStorageQuanty Method , Query on hand quantity , Reuse insertStorage Method increase or decrease , Finally through getStorageQuanty Method to query again
The address to query the inventory is :http://localhost:6001/getStockStorage/26816294479
First, check the inventory 990 individual
Re pass insertStorage Methods increase or decrease inventory , Here is the minus 10 individual ,
The address for writing inventory is :http://localhost:6001/insertStorage/26816294479/10/7
The last time the inventory is queried, there are 980 individual ,
The address to query the inventory is :http://localhost:6001/getStockStorage/26816294479
4.4.9. Process inventory queue listening methods (storage_queue)
New dependency
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
stay src\main\java\com\itheima\leyou\ New folder under folder queue, stay queue New under folder java file StorageQueue
stay StorageQueue.java Write queue listening methods in , call iStorageService.insertStorage To write inventory , The code is as follows :
@Component
public class StorageQueue {
@Autowired
private IStorageService iStorageService;
@RabbitListener(queues = "storage_queue")
public void getStorageQueue(String msg){
System.out.println("storage_queue receive messages :"+msg);
Map<String, Object> result = new HashMap<String, Object>();
try {
result = iStorageService.insertStorage(msg, 0, 1);
if (!(Boolean) result.get("result")){
System.out.println("storage_queue Message processing failed :"+result.get("msg"));
}
}catch (Exception e){
System.out.println("storage_queue Message processing failed :"+e.getMessage());
}
System.out.println("storage_queue Message processing completed !"+result);
}
}
4.5. Create member services ( Port number 5000)
4.5.1. Create a membership table structure
DROP TABLE IF EXISTS `tb_user`;
CREATE TABLE `tb_user` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`username` VARCHAR(32) NOT NULL COMMENT ' user name ',
`password` VARCHAR(60) NOT NULL COMMENT ' password , Encrypted storage ',
`phone` VARCHAR(11) DEFAULT NULL COMMENT ' Register mobile number ',
`create_time` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP COMMENT ' Creation time ',
`update_time` TIMESTAMP NULL COMMENT ' Update time ',
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`) USING BTREE,
UNIQUE KEY `phone` (`phone`)
) ENGINE=INNODB AUTO_INCREMENT=31 DEFAULT CHARSET=utf8 COMMENT=' User table ';
4.5.2. Create member services
First step : choice File-New-Module..., Select... From the pop-up window Spring initializr, choice Module SDK, choice Next
The second step :Group by com.itheima,Artifact by leyou,Name by leyouUser,Description by User For leyou Project, choice Next
The third step : choice Spring Cloud Discovery, choice Eureka Discovery Client, choice Next
Step four :Module name by leyouUser,Content root Is the path +leyouUser, choice Finish, A... Will be created under the project folder leyouUser Folder
4.5.3. To configure pom.xml
In the generated project , open pom.xml, Configuration dependency , among spring-cloud-starter-netflix-eureka-client Introduced... For the project Eureka Client's jar package ,spring-boot-starter-web Introduced web scenario ,web Used in module development jar package , utilize alibaba Of fastjson analysis json data , So the introduction of alibaba.fastjson Use of jar package ,mysql-connector-java And spring-boot-starter-data-jpa Depend on the introduction of mysql The connection uses jar package
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-parent</artifactId>
<version>2.1.6.RELEASE</version>
</parent>
<groupId>com.itheima</groupId>
<artifactId>leyou</artifactId>
<version>1.0-SNAPSHOT</version>
<name>leyouUser</name>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.41</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
<version>2.1.7.RELEASE</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
4.5.4. The configuration file application.properties
In the generated project , open src\man\resources\application.properties, Configure port number, etc
server.port=5000
spring.application.name=leyou-user
eureka.client.service-url.defaultZone=http://localhost:9000/eureka/
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/leyou?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root
#redis Database number , There is 0~15 common 16 A database
spring.redis.database=0
#redis The server IP
spring.redis.host=127.0.0.1
#redis Port number
spring.redis.port=6379
#redis password
spring.redis.password=
#redis Request timeout , Beyond this value redis Automatically disconnect
spring.redis.timeout=10000ms
#jedis maximum connection , If this value is exceeded, it will prompt that no connection exception can be obtained
spring.redis.jedis.pool.max-active=32
#jedis Maximum waiting time , Exceeding this value will prompt the connection timeout exception
spring.redis.jedis.pool.max-wait=10000ms
#jedis Maximum number of connections waiting
spring.redis.jedis.pool.max-idle=32
#jedis Minimum number of waiting connections
spring.redis.jedis.pool.min-idle=0
In the generated project , open src\main\java\com.itheima.leyou\leyouUserApplication, Add... To the generated startup class @EnableEurekaClient, It means to start Eureka client
@SpringBootApplication
@EnableEurekaClient
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class, args);
}
}
Test run order service , stay Eureka You can see in the registry leyou-User service , Prove that the service was started successfully
4.5.5. establish controller\service\dao Folder , And create UserController.java\UserService.java\UserDao.java file
establish Dao Layer interface file ,IUserDao
public interface IUserDao {}
to UserDao.java Class add comments ,@Repository Used to label data access components , namely DAO Components , Realization IUserDao The interface of
@Repository
public class UserDao implements IUserDao {}
Statement JDBCTemplate Method , Used to connect to the database
@Autowired
private JdbcTemplate jdbcTemplate;
establish Service Layer interface ,IUserService
to UserService.java Class add comments , Realization IUserService Interface
@Service
public class UserService implements IUserService{}
Join in UserDao References to
@Autowired
private IUserDao iUserDao;
to UserController.java Class add comments
@RestController
public class UserController {}
Join in UserService References to interfaces
@Autowired
private IUserService iUserService;
4.5.6. Create a member query method (getUser)
purpose : Provide the front-end with member information data , It is mainly used for front-end member login
stay UserDao.java Class getUser Method , Take two username,password Parameters , It means through username and password Search for , Return a member List, The code is as follows :
public ArrayList<Map<String, Object>> getUser(String username, String password){
String sql = "select id AS user_id, username, phone, password from tb_user where username = ?";
return (ArrayList<Map<String,Object>>) jdbcTemplate.queryForList(sql, username);
}
stay UserService.java Class getUser Method , Return a product List, The code is as follows :
public Map<String, Object> getUser(String username, String password){
Map<String, Object> resultMap = new HashMap<String, Object>();
//1、 Determine whether the passed in parameters are incorrect
if (username==null||username.equals("")){
resultMap.put("result", false);
resultMap.put("msg", " The username cannot be empty !");
return resultMap;
}
//2、 Get the member list
ArrayList<Map<String, Object>> list = iUserDao.getUser(username, password);
if (list==null||list.isEmpty()){
resultMap.put("result", false);
resultMap.put("msg", " No member information found !");
return resultMap;
}
resultMap = list.get(0);
resultMap.put("result", true);
resultMap.put("msg", "");
return resultMap;
}
4.5.7. Create a method to add members (insertUser)
purpose : Provide the front-end with member information to increase , It is mainly used for front-end member registration
stay UserDao.java Class insertUser Method , belt username,phone,password Three parameters , Return to one Map, The code is as follows :
public int insertUser(String username, String password){
final String sql = "insert into tb_user (username, phone, password) values ('"+username+"', '"+username+"', '"+password+"')";
KeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(new PreparedStatementCreator() {
public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
PreparedStatement preparedStatement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
return preparedStatement;
}
}, keyHolder);
return keyHolder.getKey().intValue();
}
The code analysis :
- First query whether the user exists according to the mobile phone number
- If it exists, you will be prompted that the user already exists
- Write it into the membership table according to three parameters
- If the writing is correct, it returns result by true, Write error returns result by false
stay UserService.java Class insertUser Method , Return a product Map, The code is as follows :
@Transactional
public Map<String, Object> insertUser(String username, String password){
Map<String, Object> resultMap = new HashMap<String, Object>();
//1、 Determine whether the passed in parameters are incorrect
if (username==null||username.equals("")){
resultMap.put("result", false);
resultMap.put("msg", " The username cannot be empty !");
return resultMap;
}
int user_id = iUserDao.insertUser(username, password);
if (user_id<=0){
resultMap.put("result", false);
resultMap.put("msg", " The database did not execute successfully !");
return resultMap;
}
resultMap.put("user_id", user_id);
resultMap.put("username", username);
resultMap.put("phone", username);
resultMap.put("password", password);
resultMap.put("result", true);
resultMap.put("msg", "");
return resultMap;
}
Be careful : there Service Business logic judgment , If Json When conversion error or the passed in parameter is null , This will cause an error in writing to the database , So in the incoming Dao Judge before layer , Whether the passed in parameter is legal . Resolved json Also need to judge , User name and phone number cannot be empty
4.5.8. stay UserController.java Class login Method
Need to add notes @RequestMapping, among value = "/login, Return to one Map, The code is as follows :
@RequestMapping(value = "/login", method = RequestMethod.POST)
public Map<String, Object> login(String username, String password, HttpServletRequest httpServletRequest){
//1、 Get a member
Map<String, Object> userMap = new HashMap<String, Object>();
userMap = iUserService.getUser(username, password);
//2、 Did not get a member , Write member
if (!(Boolean) userMap.get("result")){
userMap = iUserService.insertUser(username, password);
}
//3、 write in session
HttpSession httpSession = httpServletRequest.getSession();
String user = JSON.toJSONString(userMap);
httpSession.setAttribute("user", user);
Object o = httpSession.getAttribute("user");
//4、 Return information
return userMap;
}
The code analysis :
- login It uses the member query method (getUser), The method of increasing membership is also used (insertUser)
- First, search for members according to the incoming phone number and password on the front page , If you find it , Direct login , If not , Directly register a , In general, in actual projects , There are two verification steps , One is the verification code , It is mainly used to verify the mobile phone number ; One is image verification , Mainly to prevent SMS bombing .
- Write member information into session
4.6. Create order service ( Port number 4000)
4.6.1. Create order table structure
1、 Order master
purpose : Used to store order master table information , for example : The whole order amount 、 members 、 Payment type 、 Payment time, etc .
2、 Order details
purpose : Used to deposit order details , Associate with the order main table , An order may have multiple details , for example : A member buys more than one item , The quantity of each item 、 The unit price 、 Amount, etc. .
3、 Order logistics status table
purpose : Used to store the logistics status of the order , Associate with the order main table , An order will have multiple details , for example : Logistics company for each commodity 、 Logistics status 、 Whether to sign for it .
4.6.2. Create order service
First step : choice File-New-Module..., Select... From the pop-up window Spring initializr, choice Module SDK, choice Next
The second step :Group by com.itheima,Artifact by leyou,Name by leyouOrder,Description by Order For leyou Project, choice Next
The third step : choice Spring Cloud Discovery, choice Eureka Discovery Client, choice Next
Step four :Module name by leyouOrder,Content root Is the path +leyouOrder, choice Finish, A... Will be created under the project folder leyouOrder Folder
4.6.3. To configure pom.xml
In the generated project , open pom.xml, Configuration dependency , among spring-cloud-starter-netflix-eureka-client Introduced... For the project Eureka Client's jar package ,spring-boot-starter-web Introduced web scenario ,web Used in module development jar package , utilize alibaba Of fastjson analysis json data , So the introduction of alibaba.fastjson Use of jar package ,mysql-connector-java And spring-boot-starter-data-jpa Depend on the introduction of mysql The connection uses jar package
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-parent</artifactId>
<version>2.1.6.RELEASE</version>
</parent>
<groupId>com.itheima</groupId>
<artifactId>leyou</artifactId>
<version>1.0-SNAPSHOT</version>
<name>leyouOrder</name>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.41</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
4.6.4. The configuration file application.properties
In the generated project , open src\man\resources\application.properties, Configure port number, etc
server.port=4000
spring.application.name=leyou-order
eureka.client.service-url.defaultZone=http://localhost:9000/eureka/
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/leyou?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root
#redis Database number , There is 0~15 common 16 A database
spring.redis.database=0
#redis The server IP
spring.redis.host=127.0.0.1
#redis Port number
spring.redis.port=6379
#redis password
spring.redis.password=
#redis Request timeout , Beyond this value redis Automatically disconnect
spring.redis.timeout=10000ms
#jedis maximum connection , If this value is exceeded, it will prompt that no connection exception can be obtained
spring.redis.jedis.pool.max-active=32
#jedis Maximum waiting time , Exceeding this value will prompt the connection timeout exception
spring.redis.jedis.pool.max-wait=10000ms
#jedis Maximum number of connections waiting
spring.redis.jedis.pool.max-idle=32
#jedis Minimum number of waiting connections
spring.redis.jedis.pool.min-idle=0
In the generated project , open src\main\java\com.itheima.leyou\leyouOrderApplication, Add... To the generated startup class @EnableEurekaClient, It means to start Eureka client
@SpringBootApplication
@EnableEurekaClient
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
}
Test run order service , stay Eureka You can see in the registry leyou-Order service , Prove that the service was started successfully
4.6.5. establish controller\service\dao Folder , And create OrderController.java\OrderService.java\OrderDao.java file
establish Dao Layer interface file ,IOrderDao
public interface IOrderDao{}
to OrderDao.java Class add comments ,@Repository Used to label data access components , namely DAO Components , Realization IOrderDao Interface
@Repository
public class OrderDao implements IOrderDao {}
Statement JDBCTemplate Method , Used to connect to the database
@Autowired
JdbcTemplate jdbcTemplate;
establish Service Layer interface file ,IOrderService
to OrderService.java Class add comments , Realization IOrderService Interface
@Service
public class OrderService implements IOrderService {}
Join in IOrderDao References to
@Autowired
private IOrderDao iOrderDao;
to OrderController.java Class add comments
@RestController
public class OrderController {}
Join in IOrderService References to
@Autowired
private IOrderService iOrderService;
4.6.6. Add order creation method (createOrder)
It is mainly used to call the front-end submit order page , And write to the order queue ,
Be careful : We need to go back to order_id, The payment page passes order_id The query .
public Map<String, Object> createOrder(String sku_id, String user_id){
Map<String, Object> resultMap = new HashMap<String, Object>();
//1、 Judge sku_id
if (sku_id==null||sku_id.equals("")){
resultMap.put("result", false);
resultMap.put("msg", " The front end is wrong again !");
return resultMap;
}
//2、 take redis policy
String order_id = String.valueOf(System.currentTimeMillis());
String policy = stringRedisTemplate.opsForValue().get("LIMIT_POLICY_"+sku_id);
if (policy!=null&&!policy.equals("")){
//3、 The start time is less than or equal to the current time , The current time is less than or equal to the end
Map<String, Object> policyMap = JSONObject.parseObject(policy, Map.class);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
String now = restTemplate.getForObject("http://leyou-time-server/getTime", String.class);
try {
Date begin_time = simpleDateFormat.parse(policyMap.get("begin_time").toString());
Date end_time = simpleDateFormat.parse(policyMap.get("end_time").toString());
Date now_time = simpleDateFormat.parse(now);
if (begin_time.getTime()<=now_time.getTime()&&now_time.getTime()<=end_time.getTime()){
int limitQuanty = Integer.parseInt(policyMap.get("quanty").toString());
//4、redis Counter
// +1+1+1=3 4
if (stringRedisTemplate.opsForValue().increment("SKU_QUANTY_"+sku_id, 1)<=limitQuanty){
//5、 Written to the queue
// tb_order: order_id, total_fee, actual_fee, post_fee, payment_type, user_id, status, create_time
// tb_order_detail: order_id, sku_id, num, title, own_spec, price, image, create_time
// tb_sku: sku_id, title, images, stock, price, indexes, own_spec
String sku = stringRedisTemplate.opsForValue().get("SKU_"+sku_id);
Map<String, Object> skuMap = JSONObject.parseObject(sku, Map.class);
Map<String, Object> orderInfo = new HashMap<String, Object>();
orderInfo.put("order_id", order_id);
orderInfo.put("total_fee", skuMap.get("price"));
orderInfo.put("actual_fee", policyMap.get("price"));
orderInfo.put("post_fee", 0);
orderInfo.put("payment_type", 0);
orderInfo.put("user_id", user_id);
orderInfo.put("status", 1);
orderInfo.put("create_time", now);
orderInfo.put("sku_id", skuMap.get("sku_id"));
orderInfo.put("num", 1);
orderInfo.put("title", skuMap.get("title"));
orderInfo.put("own_spec", skuMap.get("own_spec"));
orderInfo.put("price", policyMap.get("price"));
orderInfo.put("image", skuMap.get("images"));
String order = JSON.toJSONString(orderInfo);
try {
amqpTemplate.convertAndSend("order_queue", order);
}catch (Exception e){
resultMap.put("result", false);
resultMap.put("msg", " Write queue exception !");
return resultMap;
}
}else {
// If the counter is exceeded , The returned goods have been sold out
resultMap.put("result", false);
resultMap.put("msg", "3 Billion 9 Kicked back !");
return resultMap;
}
}else {
// If the end time is greater than the current time , The return activity has expired
resultMap.put("result", false);
resultMap.put("msg", " The activity has expired !");
return resultMap;
}
} catch (ParseException e) {
e.printStackTrace();
}
}else {
// The policy does not withdraw the amount , The return activity has expired
resultMap.put("result", false);
resultMap.put("msg", " The activity has expired !");
return resultMap;
}
//6、 Return normal data , With the order number
resultMap.put("result", true);
resultMap.put("msg", "");
resultMap.put("order_id", order_id);
return resultMap;
}
stay OrderController.java Add the method of creating order in , The code is as follows :
@RequestMapping(value = "/createOrder/{sku_id}")
public Map<String, Object> createOrder(@PathVariable("sku_id") String sku_id, HttpServletRequest httpServletRequest){
Map<String, Object> resultMap = new HashMap<String, Object>();
HttpSession httpSession = httpServletRequest.getSession();
Object userObj = httpSession.getAttribute("user");
if (userObj==null){
resultMap.put("result", false);
resultMap.put("msg", " Members cannot purchase without logging in !");
return resultMap;
}
Map<String, Object> userMap = JSONObject.parseObject(userObj.toString(), Map.class);
return iOrderService.createOrder(sku_id, userMap.get("user_id").toString());
}
Be careful : Need from session Value in , If you don't get the member information, you can't buy !
4.6.7. Create write order method (insertOrder)
It is mainly used to write orders in the queue , stay OrderDao.java add insertOrder Method , The code is as follows :
public boolean insertOrder(Map<String, Object> orderInfo){
// Write to main table
String sql = "insert into tb_order (order_id, total_fee, actual_fee, post_fee, payment_type, user_id, status, create_time) " +
"values (?, ?, ?, ?, ?, ?, ?, ?)";
boolean result = jdbcTemplate.update(sql, orderInfo.get("order_id"), orderInfo.get("total_fee"), orderInfo.get("actual_fee"),
orderInfo.get("post_fee"), orderInfo.get("payment_type"), orderInfo.get("user_id"), orderInfo.get("status"),
orderInfo.get("create_time"))==1;
// Write parts list
if (result){
sql = "INSERT INTO tb_order_detail (order_id, sku_id, num, title, own_spec, price, image, create_time) " +
"VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
result = jdbcTemplate.update(sql, orderInfo.get("order_id"), orderInfo.get("sku_id"), orderInfo.get("num"),
orderInfo.get("title"), orderInfo.get("own_spec"), orderInfo.get("price"), orderInfo.get("image"), orderInfo.get("create_time"))==1;
}
return result;
}
stay OrderService.java Increase in insertOrder Method
public Map<String, Object> insertOrder(Map<String, Object> orderInfo){
Map<String, Object> map = new HashMap<String, Object>();
if (orderInfo==null||orderInfo.isEmpty()){
map.put("result", false);
map.put("msg", " Error in incoming parameter !");
return map;
}
boolean result = iOrderDao.insertOrder(orderInfo);
if (!result){
map.put("result", false);
map.put("msg", " Order writing failed !");
return map;
}
map.put("result", true);
map.put("msg", "");
return map;
}
Called by the queue iOrderService.insertOrder
4.6.8. Create order queue (order_queue)
Dependency needed
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
stay src\main\java\com\itheima\leyou\ New folder under folder queue, stay queue New under folder java file OrderQueue
@Component
public class OrderQueue {
@Autowired
private IOrderService iOrderService;
@RabbitListener(queues = "order_queue")
public void insertOrder(String msg){
//1、 Receive messages and output
System.out.println("order_queue receive messages :"+msg);
//2、 Call a write order method
Map<String, Object> orderInfo = JSONObject.parseObject(msg, Map.class);
Map<String, Object> resultMap = new HashMap<String, Object>();
resultMap = iOrderService.insertOrder(orderInfo);
//3、 If it is not written successfully, an error message will be output
if (!(Boolean) resultMap.get("result")){
System.out.println("order_queue Failed to process message :");
}
//4、 Successfully output the message
System.out.println("order_queue Message processed successfully !");
}
}
4.6.9. Test create order
First the session Your judgment hangs ,user_id The assignment is 1.
Type in the browser http://localhost:4000/createOrder/26816294479
The queue is also processed successfully .
4.6.10. Create order query method (getOrder)
purpose : Check order details , For Member Center -- My order inquiry
First, understand that the table structure of the order is : Order master 、 Order details 、 Order logistics status table , An order has a main table , Multiple details , Multiple logistics states .
stay OrderDao.java Class getOrder Method , With a orderid Parameters , It means through orderid Search for , Return an order map, The code is as follows :
public ArrayList<Map<String, Object>> getOrder(String order_id){
String sql = "select d.sku_id, m.order_id, d.price " +
"from tb_order m inner join tb_order_detail d on m.order_id = d.order_id " +
"where m.order_id = ?";
return (ArrayList<Map<String, Object>>) jdbcTemplate.queryForList(sql, order_id);
}
stay StorageService.java Class getOrder Method , Return a product Map, The code is as follows :
public Map<String, Object> getOrder(String order_id){
Map<String, Object> resultMap = new HashMap<String, Object>();
if (order_id==null||order_id.equals("")){
resultMap.put("result", false);
resultMap.put("msg", " Error in parameter input !");
return resultMap;
}
ArrayList<Map<String, Object>> list = iOrderDao.getOrder(order_id);
resultMap.put("order", list);
resultMap.put("result", true);
resultMap.put("msg", "");
return resultMap;
}
stay StorageController.java Class getOrder Method , Need to add notes @RequestMapping, among value = "/getOrder/{orderid}, Return a product Map, The code is as follows :
@RequestMapping(value = "/getOrder/{order_id}")
public Map<String, Object> getOrder(@PathVariable("order_id") String order_id){
return iOrderService.getOrder(order_id);
}
4.6.11. Create an update order status method (updateOrderStatus)
It is mainly used to update the order status in the queue , stay OrderDao.java add updateOrderStatus Method , The code is as follows :
public boolean updateOrderStatus(String order_id){
String sql = "update tb_order set status = 2 where order_id = ?";
return jdbcTemplate.update(sql, order_id)==1;
}
4.6.12. Create order payment method (payOrder)
It is mainly used for calling the front-end payment page , And write the order status update queue
stay OrderService.java add payOrder Method
public Map<String, Object> payOrder(String order_id, String sku_id){
Map<String, Object> resultMap = new HashMap<String, Object>();
if (order_id==null||order_id.equals("")){
resultMap.put("result", false);
resultMap.put("msg", " The order is wrong !");
return resultMap;
}
boolean result = iOrderDao.updateOrderStatus(order_id);
if (!result){
resultMap.put("result", false);
resultMap.put("msg", " Order status update failed !");
return resultMap;
}
amqpTemplate.convertAndSend("storage_queue", sku_id);
resultMap.put("result", true);
resultMap.put("msg", "");
return resultMap;
}
stay OrderController.java add payOrder Method
@RequestMapping(value = "/payOrder/{order_id}/{sku_id}")
public Map<String, Object> payOrder(@PathVariable("order_id") String order_id, @PathVariable("sku_id") String sku_id){
// Normally, the payment interface will be called here , The simulated payment has returned normal data
boolean isPay = true;
Map<String, Object> resultMap = new HashMap<String, Object>();
if (!isPay){
resultMap.put("result", false);
resultMap.put("msg", " Payment interface call failed !");
return resultMap;
}
return iOrderService.payOrder(order_id, sku_id);
}
4.6.13. Test query order
Access directly from the page getOrder Method query ,http://localhost:4000/getOrder/1565019341112
4.7. Create gateway service ( Port number 80)
4.7.1. Create gateway service
First step : choice File-New-Module..., Select... From the pop-up window Spring initializr, choice Module SDK, choice Next
The second step :Group by com.itheima,Artifact by leyou,Name by ZuulServer,Description by Zuul For leyou Project, choice Next
The third step : choice Spring Cloud Discovery, choice Eureka Discovery Client, choice Next
Step four :Module name by ZuulServer,Content root Is the path +ZuulServer, choice Finish, A... Will be created under the project folder ZuulServer Folder
4.7.2. To configure pom.xml
In the generated project , open pom.xml, Configuration dependency , among spring-cloud-starter-netflix-eureka-client Introduced... For the project Eureka Client's jar package ,spring-boot-starter-web Introduced web scenario ,web Used in module development jar package
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-parent</artifactId>
<version>2.1.6.RELEASE</version>
</parent>
<groupId>com.itheima</groupId>
<artifactId>leyou</artifactId>
<version>1.0-SNAPSHOT</version>
<name>leyouZuul</name>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
4.7.3. The configuration file application.properties
In the generated project , open src\man\resources\application.properties, Configure port number, etc
server.port=80
spring.application.name=leyou-zuul
eureka.client.service-url.defaultZone=http://localhost:9000/eureka/
# Ignore the default service mapping path of the framework
zuul.ignored-services='*'
# Do not ignore the framework's permission related header information
zuul.ignore-security-headers=false
zuul.host.socket-timeout-millis=60000
zuul.host.connect-timeout-millis=60000
zuul.host.max-total-connections=500
zuul.routes.leyou-client.path=/leyouClient/**
zuul.routes.leyou-client.serviceId=leyou-client
# prevent session Inconsistency
zuul.routes.leyou-client.sensitiveHeaders="*"
zuul.routes.leyou-order.path=/leyouOrder/**
zuul.routes.leyou-order.serviceId=leyou-order
zuul.routes.leyou-order.sensitiveHeaders="*"
zuul.routes.leyou-user.path=/leyouUser/**
zuul.routes.leyou-user.serviceId=leyou-user
zuul.routes.leyou-user.sensitiveHeaders="*"
zuul.routes.leyou-stock.path=/leyouStock/**
zuul.routes.leyou-stock.serviceId=leyou-stock
zuul.routes.leyou-stock.sensitiveHeaders="*"
zuul.routes.leyou-storage.path=/leyouStorage/**
zuul.routes.leyou-storage.serviceId=leyou-storage
zuul.routes.leyou-storage.sensitiveHeaders="*"
zuul.routes.leyou-time-server.path=/leyouTimeServer/**
zuul.routes.leyou-time-server.serviceId=leyou-time-server
zuul.routes.leyou-time-server.sensitiveHeaders="*"
zuul.routes.leyou-order.path Path for each service
zuul.routes.leyou-stock.serviceId Number for each service
And so on , Configure all services
Be careful :zuul Use 80 port , You do not need to enter the port number when accessing .
In the generated project , open src\main\java\com.itheima.leyou\leyouTimeServerApplication, Add... To the generated startup class @EnableZuulProxy, When Zuul And Eureka、Ribbon When the components are used together , We use @EnableZuulProxy
@SpringBootApplication
@EnableZuulProxy
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class, args);
}
}
Test and run the gateway service , Use the previous method to query inventory :getStockStorage
The original http://localhost:6001/getStockStorage/26816294479
Change it to http://localhost/leyouStorage/getStockStorage/26816294479
4.8. Create a page service ( Port number 3000)
4.8.1. Create a page service
First step : choice File-New-Module..., Select... From the pop-up window Spring initializr, choice Module SDK, choice Next
The second step :Group by com.itheima,Artifact by leyou,Name by leyouClient,Description by Client For leyou Project, choice Next
The third step : choice Web, choice Spring Web Starter, choice Next
Step four :Module name by leyouClient,Content root Is the path +leyouClient, choice Finish, A... Will be created under the project folder leyouClient Folder
4.8.2. To configure pom.xml
In the generated project , open pom.xml, Configuration dependency , among spring-cloud-starter-netflix-eureka-client Introduced... For the project Eureka Client's jar package ,spring-boot-starter-web Introduced web scenario ,web Used in module development jar package
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-parent</artifactId>
<version>2.1.6.RELEASE</version>
</parent>
<groupId>com.itheima</groupId>
<artifactId>leyou</artifactId>
<version>1.0-SNAPSHOT</version>
<name>leyouClient</name>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
stay pom.xml Medium build The following resource packs must be added to the tag , Otherwise, if you visit the page, you will report 404, as a result of SpringBoot You must specify the path first , Then compile successfully and start again , To access the page
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<!-- The following resource packages must be introduced to solve jsp404 The problem of -->
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.*</include>
</includes>
</resource>
<resource>
<!-- Appoint resources Which directory does the plug-in handle the resource files -->
<directory>src/main/webapp</directory>
<!-- Note that this must be placed in this directory to be accessed -->
<targetPath>resources</targetPath>
<includes>
<include>**/*.*</include>
</includes>
</resource>
</resources>
<!-- solve jsp404 -->
</build>
4.8.3. The configuration file application.properties
In the generated project , open src\man\resources\application.properties, Configure port number, etc
server.port=3000
spring.application.name=leyou-client
eureka.client.service-url.defaultZone=http://localhost:9000/eureka/
In the generated project , open src\main\java\com.itheima.leyou\leyouClientApplication, Add... To the generated startup class @EnableEurekaClient, It means to start Eureka client
@SpringBootApplication
@EnableEurekaClient
public class ClientApplication {
public static void main(String[] args) {
SpringApplication.run(ClientApplication.class, args);
}
}
Test run page service , stay Eureka You can see in the registry leyou-client service , Prove that the service was started successfully
4.8.4. Create a page folder
1、 stay main Create webapp Folder
2、 stay webapp Create under folder page Folder , For storage html pagefile
3、 stay webapp Create under folder resources Folder , Used to store the imported plug-ins , Here's what I'm using bootstrap and jquery,bootstrap Mainly the style of the page ,jquery Mainly used ajax Interact with the background .
4.8.5. introduce bootstrap
take bootstrap-4.3.1-dist Copy folder to resources Under the folder
take jquery Copy folder to resources Under the folder
4.8.6. Create a member login page (loginPage.html)
purpose : For member registration 、 Member login
take loginPage.html Copied to the main\webapp\page\ Under the folder
4.8.7. Create a product list page (stockListPage.html)
purpose : It is used to display the product list
take stockListPage.html Copied to the main\webapp\page\ Under the folder
4.8.8. Create product details page (stockDetailPage.html)
purpose : Used to show product details
take stockDetailPage.html Copied to the main\webapp\page\ Under the folder
4.8.9. Create order page (createOrderPage.html)
purpose : Used to submit orders
take createOrderPage.html Copied to the main\webapp\page\ Under the folder
4.8.8. Create a payment page (payPage.html)
purpose : For payment of orders
take payPage.html Copied to the main\webapp\page\ Under the folder
4.8.8. Create a seckill policy page (limitPolicyPage.html)
purpose : Used to add the second kill policy
take limitPolicyPage.html Copied to the main\webapp\page\ Under the folder
4.9. Create configuration service ( Port number 2000)
4.9.1 Create configuration service
First step : choice File-New-Module..., Select... From the pop-up window Spring initializr, choice Module SDK, choice Next
The second step :Group by com.itheima,Artifact by leyou,Name by leyouClient,Description by Client For leyou Project, choice Next
The third step : choice Spring Cloud Discovery, choice Eureka Discovery Client, choice Next
Step four :Module name by leyouConfig,Content root Is the path +leyouConfig, choice Finish, A... Will be created under the project folder leyouConfig Folder
4.9.2. ConfigServer To configure
In the generated project , open pom.xml, Configuration dependency , among spring-cloud-starter-netflix-eureka-client Introduced... For the project Eureka Client's jar package ,spring-boot-starter-web Introduced web scenario ,web Used in module development jar package ,spring-cloud-config-server Introduces the... Used to configure the server jar package
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-parent</artifactId>
<version>2.1.6.RELEASE</version>
</parent>
<groupId>com.itheima</groupId>
<artifactId>leyou</artifactId>
<version>1.0-SNAPSHOT</version>
<name>leyouConfig</name>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
stay src\man\resources New under folder shared Folder , It is used to store the configuration files of various services
4.9.3. The configuration file application.properties
In the generated project , open src\man\resources\application.properties, Configure port number, etc
server.port=2000
spring.application.name=leyou-config-server
eureka.client.service-url.defaultZone=http://localhost:9000/eureka/
spring.cloud.config.server.native.search-locations=classPath:/shared
spring.cloud.config.profile=dev
spring.profiles.active=native
stay src\man\resources So let's make a new one shared Folder
Be careful : stay application.properties in spring.cloud.config.server.native.search-locations This item refers to finding the local file path , Point to shared in , In the actual project, most software companies may work on a server or a remote warehouse .
share The naming rules of the files in the folder are : In each service configuration file spring.application.name+ Configure the application.properties In the document spring.cloud.config.profile Value content .
for example :leyou-time-server It's time service (leyouTimeServer) Of spring.application.name, Configure the application.properties In the document spring.cloud.config.profile by dev, So the name of the configuration file for goods and services is leyou-time-server-dev.properties
leyou-time-server-dev.properties Configure the content :
server.port=8000
spring.application.name=leyou-time-server
eureka.client.service-url.defaultZone=http://localhost:9000/eureka/
In the generated project , open src\main\java\com.itheima.leyou\leyouConfigApplication, Add... To the generated startup class @EnableEurekaClient, It means to start Eureka client , And add @EnableConfigServer, It means to start Config Configure the service
@SpringBootApplication
@EnableEurekaClient
@EnableConfigServer
public class ConfigApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigApplication.class, args);
}
}
Take goods and services as an example :
Time service increases dependency
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-client</artifactId>
</dependency>
The original goods and services configuration file main/resources/application.properties The content in is suspended , Create a new one bootstrap.properties file , The code is as follows :
spring.application.name=leyou-time-server
spring.cloud.config.profile=dev
spring.cloud.config.uri=http://localhost:2000
spring.cloud.config.label=master
spring.profiles.active=dev
Test run configuration service and commodity service , stay Eureka You can see in the registry leyou-config-server as well as leyou-stock service , Prove that the service configuration is successful
4.9.4. Find configuration file first
Modify the time service (leyouTimeServer) Of application.properties The configuration file
server.port=8001
spring.application.name=leyou-time-server
eureka.client.service-url.defaultZone=http://localhost:9000/eureka/
Be careful : The port number here is changed to 8001
Start configuration service , Restart the time service , The console can see that the startup is 8000
Do not start the configuration service , Restart the time service , The console can see that the startup is 8001
Conclusion : Service startup , Give priority to bootstrap.properties, If you can't find it , Look again application.properties, prove bootstrap Than application Priority of .
5、 ... and 、 Project test
5.1. New seckill policy
Address :http://localhost/leyouClient/page/limitPolicyPage.html
Choose products , Enter the second kill price 、 Seckill inventory 、 Starting time 、 End time , Point save .
5.2. The login page
Address :http://localhost/leyouClient/page/loginPage.html
5.3. Product list page
Enter your mobile number in the login page , password , Jump to the product list page
Address :http://localhost/leyouClient/page/stockListPage.html
5.4. Product details page
Select an item in the item list page
Address :http://localhost/leyouClient/page/stockDetailPage.html?sku_id=27359021557
5.5. place order
Click... On the product details page 【 Rush to buy 】
Address :http://localhost/leyouClient/page/createOrderPage.html?sku_id=27359021557
5.6. Payment page
Click... On the submit order page 【 place order 】
Address :http://localhost/leyouClient/page/payPage.html?order_id=1565061554849
Click on 【 Wechat payment 】, Tips “ Successful payment ”
About queues
版权声明
本文为[[email protected]@yxg]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/177/202206261820088246.html
边栏推荐
- Vscode 基础必备 常用插件
- Take you to resolve hash conflicts and implement a simple hash table,
- MySQL download and configuration MySQL remote control
- JVM入个门(1)
- 手机影像内卷几时休?
- Usage and difference between ros:: spinonce() and ros:: spin()
- Decompilation of zero time technology smart contract security series articles
- Using recursion to find all gray codes with n bits
- 最小生成树、最短路径、拓扑排序、关键路径
- 项目实战六:分布式事务-Seata
猜你喜欢
VCD video disc
LeetCode 面试题29 顺时针打印矩阵
商品秒杀系统
零时科技 | 智能合约安全系列文章之反编译篇
Solidity - 合约继承子合约包含构造函数时报错 及 一个合约调用另一合约view函数收取gas费用
Numpy's Matplotlib
50 lines of code to crawl TOP500 books and import TXT documents
Boyun, standing at the forefront of China's container industry
Solve the problem that each letter occupies a space in pycharm
Crawl Douban to read top250 and import it into SqList database (or excel table)
随机推荐
【Mysql系列】工作常用sql集锦(持续更新)
Clion编译catkin_ws(ROS工作空间包的简称)加载CMakeLists.txt出现的问题
元宇宙链游开发案例版 NFT元宇宙链游系统开发技术分析
知识点总结
Union, intersection and difference operations in SQL
字符串String转换为jsonArray并解析
交叉编译环境出现.so链接文件找不到问题
Plt How to keep show() not closed
JS cast
Li Kou daily question - day 28 -566 Reshape matrix
Map and list < map > transfer to corresponding objects
wm_concat()和group_concat()函数
Leetcode interview question 29 clockwise print matrix
最小生成树、最短路径、拓扑排序、关键路径
System table SQLite of SQLite database_ master
微服务架构
读书笔记:《过程咨询 III》
Enter n integers and output the number of occurrences greater than or equal to half the length of the array
Deep understanding of MySQL lock and transaction isolation level
分页查询、JOIN关联查询优化