当前位置:网站首页>6、 Shopping ⻋ and orders
6、 Shopping ⻋ and orders
2022-06-30 06:53:00 【Wangyuhui】
6、 ... and , shopping ⻋
6.1 shopping ⻋
shopping ⻋ It is divided into ⽤ Users log in for shopping ⻋ And unlisted shopping ⻋ operation , Jingdong, a well-known domestic e-commerce company ⽤ Users can operate shopping with or without login ⻋, If ⽤ User does not log in , Operation shopping ⻋ You can store data in Cookie perhaps WebSQL perhaps SessionStorage in ,⽤ After the user logs in, he / she purchases ⻋ Data can be stored in Redis in , Add the previous login ⼊ Shopping for ⻋ Merge into Redis Then you can .
Jingdong shopping ⻋⽅ case :
Tmall will choose ⽤ In addition ⼀ Kind of implementation ⽅ case ,⽤ If you want to add ⼊ shopping ⻋, You must log in before you can operate shopping ⻋. The shopping we realized today ⻋ It was tmall that solved ⽅ case , namely ⽤ The user must log in first to make ⽤ shopping ⻋ function .
6.1.1 shopping ⻋ analysis
(1) Demand analysis
⽤ Details of household goods ⻚ Click Add ⼊ shopping ⻋, Submit goods SKU Number and purchase quantity , Add to shopping ⻋. shopping ⻋ Exhibition ⻚⾯ as follows :
(2) shopping ⻋ Realize the idea
What we achieve is ⽤ Shopping after login ⻋,⽤ The customer adds ⼊ shopping ⻋ When , Directly add ⼊ shopping ⻋ The details of are stored in ⼊ To Redis that will do . Every time you check shopping ⻋ Directly from Redis In order to get .
(3) Table structure analysis
⽤ After the user logs in, add ⼊ shopping ⻋, Need to store product details and purchase quantities , shopping ⻋ The details are as follows : In the data order_item_ surface :
CREATE TABLE `order_item_` (
`id` varchar(20) COLLATE utf8_bin NOT NULL COMMENT 'ID',
`category_id1` int(11) DEFAULT NULL COMMENT '1 Class classification ',
`category_id2` int(11) DEFAULT NULL COMMENT '2 Class classification ',
`category_id3` int(11) DEFAULT NULL COMMENT '3 Class classification ',
`spu_id` varchar(20) COLLATE utf8_bin DEFAULT NULL COMMENT 'SPU_ID',
`sku_id` bigint(20) NOT NULL COMMENT 'SKU_ID',
`order_id` bigint(20) NOT NULL COMMENT ' Order ID',
`name` varchar(200) COLLATE utf8_bin DEFAULT NULL COMMENT ' Name of commodity ',
`price` int(20) DEFAULT NULL COMMENT ' The unit price ',
`num` int(10) DEFAULT NULL COMMENT ' Number ',
`money` int(20) DEFAULT NULL COMMENT ' total ⾦ forehead ',
`pay_money` int(11) DEFAULT NULL COMMENT ' Paid in ⾦ forehead ',
`image` varchar(200) COLLATE utf8_bin DEFAULT NULL COMMENT ' chart ⽚ Address ',
`weight` int(11) DEFAULT NULL COMMENT ' weight ',
`post_fee` int(11) DEFAULT NULL COMMENT ' The freight ',
`is_return` char(1) COLLATE utf8_bin DEFAULT NULL COMMENT ' Is it a return ',
PRIMARY KEY (`id`),
KEY `item_id` (`sku_id`),
KEY `order_id` (`order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
shopping ⻋ The detail table is actually the order detail table structure , It's just ⽬ Temporarily store data to Redis, etc. ⽤ Only after the customer places an order will the data be transferred from Redis Take out the storage ⼊ To MySQL in .
6.1.2 Set up order shopping ⻋ Microservices
Because shopping ⻋ function ⽐ Simpler , this ⾥ We put orders and shopping ⻋ Microservices are placed in ⼀ individual ⼯ Cheng Xia
(1) pom.xml
legou-order/pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>legou-parent</artifactId>
<groupId>com.lxs</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>legou-order</artifactId>
<packaging>pom</packaging>
<modules>
<module>legou-order-interface</module>
<module>legou-order-service</module>
</modules>
</project>
legou-order/legou-order-interface/pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>legou-order</artifactId>
<groupId>com.lxs</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>legou-order-interface</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>com.lxs</groupId>
<artifactId>legou-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>com.core.Starter</mainClass>
<layout>ZIP</layout>
<classifier>exec</classifier>
<includeSystemScope>true</includeSystemScope>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
legou-order/legou-order-service/pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>legou-order</artifactId>
<groupId>com.lxs</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>legou-order-service</artifactId>
<dependencies>
<!-- redis send ⽤-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</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>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<dependency>
<groupId>com.lxs</groupId>
<artifactId>legou-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.lxs</groupId>
<artifactId>legou-order-interface</artifactId>
<version>${project.version}</version>
</dependency>
<!-- Commodity micro service -->
<dependency>
<groupId>com.lxs</groupId>
<artifactId>legou-item-instance</artifactId>
<version>${project.version}</version>
</dependency>
<!-- swagger -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<!--oauth2-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
</dependencies>
</project>
(2) Initiator configuration ⽂ Pieces of
public.key Torture ⻉ that will do
legou-order/legou-order-service/src/main/resources/bootstrap.yml
spring:
application:
name: order-service
# Multiple grounding ⼝ Upper @FeignClient(“ Same service name ”) Will report a mistake ,overriding is disabled.
# Set up by true , namely allow The same name
main:
allow-bean-definition-overriding: true
config-repo/order-service.yml
server:
port: 9009
spring:
redis:
host: 192.168.220.110
port: 6379
mybatis-plus:
mapper-locations: classpath*:mybatis/*/*.xml
type-aliases-package: com.lxs.legou.*.po
configuration:
# Underline hump conversion
map-underscore-to-camel-case: true
lazy-loading-enabled: true
aggressive-lazy-loading: false
logging:
#file: demo.log
pattern:
console: "%d - %msg%n"
level:
org.springframework.web: debug
com.lxs: debug
security:
oauth2:
resource:
jwt:
key-uri: http://localhost:9098/oauth/token_key # If so ⽤JWT, You can get the public key ⽤ On token Signature verification of
legou-order/legou-order-service/src/main/java/com/legou/order/OrderApplication.java
package com.legou.order;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
/** * @author * @version 1.0 * @description Order micro service * @createDate 2022/6/20 16:50 **/
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
@EnableCircuitBreaker
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
}
(3) Configuration class
Configure classes and other resource microservices ⼯ The process is similar to , Torture ⻉ You can modify it.
legou-order/legou-order-service/src/main/java/com/legou/order/config/MybatisPlusConfig.java
package com.legou.order.config;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.extension.plugins.pagination.optimize.JsqlParserCountOptimize;
import com.github.pagehelper.PageInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/** * @author * @version 1.0 * @description * @createDate 2022/6/20 16:52 **/
@Configuration
@MapperScan("com.lxs.legou.order.dao")
public class MybatisPlusConfig {
/** * branch ⻚ plug-in unit */
@Bean
public PaginationInterceptor paginationInterceptor() {
// Turn on count Of join Optimize , Only aim at left join !!!
return new PaginationInterceptor().setCountSqlParser(new JsqlParserCountOptimize(true));
}
@Bean
public PageInterceptor pageInterceptor() {
return new PageInterceptor();
}
}
legou-order/legou-order-
service/src/main/java/com/legou/order/config/JwtConfig.java
package com.legou.order.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.util.FileCopyUtils;
import java.io.IOException;
/** * @author * @version 1.0 * @description * @createDate 2022/6/20 16:54 **/
@Configuration
public class JwtConfig {
public static final String public_cert = "mickey_public.key";
@Autowired
private JwtAccessTokenConverter jwtAccessTokenConverter;
@Bean
@Qualifier("tokenStore")
public TokenStore tokenStore() {
return new JwtTokenStore(jwtAccessTokenConverter);
}
@Bean
protected JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
Resource resource = new ClassPathResource(public_cert);
String publicKey;
try {
publicKey = new
String(FileCopyUtils.copyToByteArray(resource.getInputStream()));
}catch (IOException e) {
throw new RuntimeException(e);
}
converter.setVerifierKey(publicKey); // Set the verification public key
converter.setSigningKey("mickey"); // Set certificate signing password , Otherwise, the report will be wrong
return converter;
}
}
legou-order/legou-order-service/src/main/java/com/legou/order/config/ResourceServerConfiguration.java
package com.legou.order.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;
/** * @author * @version 1.0 * @description * @createDate 2022/6/20 16:55 **/
@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
@Autowired
private TokenStore tokenStore;
@Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/**").permitAll(); // Unauthenticated access to
// .antMatchers("/**").authenticated();
}
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws
Exception {
resources.tokenStore(tokenStore);
}
}
6.1.3 Add shopping ⻋
(1) Thought analysis
⽤ Users add shopping ⻋, Will add ⼊ shopping ⻋ Our goods are stored in ⼊ To Redis Then you can .⼀ individual ⽤ Users can add more than one item ⼊ shopping ⻋, Store in Redis The data in can be ⽤Hash type .
choose Hash The type can be ⽤ Household ⽤ Account name as namespace, Add... To the specified item ⼊ shopping ⻋, Then go to the corresponding namespace add ⼀ individual key and value,key It's merchandise ID,value It's plus ⼊ shopping ⻋ Product details , Here's the picture :
(2) Code implementation
1. Entity class
Order master
package com.lxs.legou.order.po;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.lxs.legou.core.po.BaseEntity;
import lombok.Data;
import java.util.Date;
/** * @Des New vocational course mall item ⽬ Order master * @Author cedar * @Date 2020/11/30 14:20 */
@Data
@TableName("order_")
class Order extends BaseEntity {
@TableField("total_num_")
private Integer totalNum;// The total quantity is
@TableField("total_money_")
private Integer totalMoney;//⾦ Sum up
@TableField("pre_money_")
private Integer preMoney;// Discount ⾦ forehead
@TableField("post_fee_")
private Integer postFee;// Postage
@TableField("pay_money_")
private Integer payMoney;// Paid in ⾦ forehead
@TableField("pay_type_")
private String payType;//⽀ Payment type ,1、 On-line ⽀ pay 、0 Cash on Delivery
@TableField("create_time_")
private Date createTime;// Order creation time
@TableField("update_time_")
private Date updateTime;// Order update time
@TableField("pay_time_")
private Date payTime;// Time of payment
@TableField("consign_time_")
private Date consignTime;// Delivery time
@TableField("end_time_")
private Date endTime;// Completion time
@TableField("close_time_")
private Date closeTime;// Closing time
@TableField("shipping_name_")
private String shippingName;// Logistics name order details
@TableField("shipping_code_")
private String shippingCode;// Logistics order No
@TableField("username_")
private String username;//⽤ Account name
@TableField("buyer_message_")
private String buyerMessage;// The buyer leaves ⾔
@TableField("buyer_rate_")
private String buyerRate;// Whether to evaluate
@TableField("receiver_contact_")
private String receiverContact;// Receiving goods ⼈
@TableField("receiver_mobile_")
private String receiverMobile;// Receiving goods ⼈⼿ machine
@TableField("receiver_address_")
private String receiverAddress;// Receiving goods ⼈ Address
@TableField("source_type_")
private String sourceType;// Source of order :1:web,2:app,3: WeChat official account ,4: WeChat ⼩ Program 5H5⼿ machine ⻚⾯
@TableField("transaction_id_")
private String transactionId;// Transaction flow ⽔ Number
@TableField("order_status_")
private String orderStatus;// The order status ,0: Hang in the air ,1: Completed ,2: Has returned
@TableField("pay_status_")
private String payStatus;//⽀ Payment status ,0: not ⽀ pay ,1: has ⽀ pay ,2:⽀ Payment failed
@TableField("consign_status_")
private String consignStatus;// Delivery status ,0: Not delivered ,1: Shipped ,2: Received goods
@TableField("is_delete_")
private String isDelete;// Whether or not to delete
}
The order details
package com.lxs.legou.order.po;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.lxs.legou.core.po.BaseEntity;
import lombok.Data;
/** * @author * @version 1.0 * @description The order details * @createDate 2022/6/21 16:19 **/
@Data
@TableName("order_item_")
public class OrderItem extends BaseEntity {
@TableField("category_id1_")
private Long categoryId1;//1 Class classification
@TableField("category_id2_")
private Long categoryId2;//2 Class classification
@TableField("category_id3_")
private Long categoryId3;//3 Class classification
@TableField("spu_id_")
private Long spuId;//SPU_ID
@TableField("sku_id_")
private Long skuId;//SKU_ID
@TableField("order_id_")
private String orderId;// Order ID
@TableField("name_")
private String name;// Name of commodity
@TableField("price_")
private Long price;// The unit price
@TableField("num_")
private Integer num;// Number
@TableField("money_")
private Long money;// total ⾦ forehead
@TableField("pay_money_")
private Long payMoney;// Paid in ⾦ forehead
@TableField("image_")
private String image;// chart ⽚ Address
@TableField("weight_")
private Integer weight;// weight
@TableField("post_fee_")
private Integer postFee;// The freight
@TableField("is_return_")
private String isReturn;// Is it a return ,0: Not returned ,1: Has returned
}
2. Feign Client agent
legou-order/legou-order-
service/src/main/java/com/legou/order/client/SkuClient.java
package com.legou.order.client;
import com.lxs.legou.item.api.SkuApi;
import com.lxs.legou.item.po.Sku;
import feign.hystrix.FallbackFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.List;
/** * @author * @version 1.0 * @description * @createDate 2022/6/21 15:49 **/
@FeignClient(name = "item-service", fallback =
SkuClient.SkuClientFallback.class)
public interface SkuClient extends SkuApi {
@Component
@RequestMapping("/sku-fallback")
// This can avoid the container requestMapping repeat
class SkuClientFallback implements SkuClient {
private static final Logger LOGGER = LoggerFactory.getLogger(SkuClientFallback.class);
@Override
public List<Sku> selectSkusBySpuId(Long spuId) {
LOGGER.error(" Abnormal hair ⽣, Into the ⼊fallback⽅ Law ");
return null;
}
@Override
public Sku edit(Long id) {
LOGGER.error(" Abnormal hair ⽣, Into the ⼊fallback⽅ Law ");
return null;
}
}
}
legou-order/legou-order-service/src/main/java/com/legou/order/client/SpuClient.java
package com.legou.order.client;
import com.lxs.legou.item.api.SpuApi;
import com.lxs.legou.item.po.Spu;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.List;
@FeignClient(name = "item-service", fallback = SpuClient.SpuClientFallback.class)
public interface SpuClient extends SpuApi {
@Component
@RequestMapping("/spu-fallback") // This can avoid the container requestMapping repeat
class SpuClientFallback implements SpuClient {
private static final Logger LOGGER =
LoggerFactory.getLogger(SpuClientFallback.class);
@Override
public List<Spu> selectAll() {
LOGGER.error(" Abnormal hair ⽣, Into the ⼊fallback⽅ Law ");
return null;
}
@Override
public Spu edit(Long id) {
LOGGER.error(" Abnormal hair ⽣, Into the ⼊fallback⽅ Law ");
return null;
}
}
}
3. The business layer
The business layer connects ⼝
legou-order/legou-order-service/src/main/java/com/legou/order/service/CartService.java
package com.legou.order.service;
import com.lxs.legou.order.po.OrderItem;
import java.util.List;
/** * @Des New vocational course mall item ⽬ * @Author cedar * @Date 2020/11/30 14:44 */
public interface CartService {
/** * Add shopping ⻋ * @param id sku Of ID * @param num The number of purchases * @param username Of goods purchased ⽤ Account name */
void add(Long id, Integer num, String username);
}
The business layer connects ⼝ Implementation class
legou-order/legou-order-service/src/main/java/com/legou/order/service/impl/CartServiceImpl.java
package com.legou.order.service.impl;
import com.legou.order.client.SkuClient;
import com.legou.order.client.SpuClient;
import com.legou.order.service.CartService;
import com.lxs.legou.item.po.Sku;
import com.lxs.legou.item.po.Spu;
import com.lxs.legou.order.po.OrderItem;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.List;
/** * @Des New vocational course mall item ⽬ * @Author cedar * @Date 2020/11/30 16:29 */
@Service
public class CartServiceImpl implements CartService {
@Autowired
private SkuClient skuFeign;
@Autowired
private SpuClient spuFeign;
@Autowired
private RedisTemplate redisTemplate;
@Override
public void add(Long id, Integer num, String username) {
//1. According to the SKU Of ID obtain sku The data of
Sku data = skuFeign.edit(id);
if (data != null) {
//2. according to sku Data objects for obtain The SKU Corresponding SPU The data of
Long spuId = data.getSpuId();
Spu spu = spuFeign.edit(spuId);
//3. Store data to shopping ⻋ object (order_item) in
OrderItem orderItem = new OrderItem();
orderItem.setCategoryId1(spu.getCid1());
orderItem.setCategoryId2(spu.getCid2());
orderItem.setCategoryId3(spu.getCid3());
orderItem.setSpuId(spu.getId());
orderItem.setSkuId(id);
orderItem.setName(data.getTitle());// Name of commodity sku The name of
orderItem.setPrice(data.getPrice());//sku Unit price
orderItem.setNum(num);// The number of purchases
orderItem.setPayMoney(orderItem.getNum() * orderItem.getPrice());//
The unit price * Number
orderItem.setImage(data.getImages());// A picture of a commodity ⽚dizhi
//4. Data added to redis in key:⽤ Account name field:sku Of ID value: shopping ⻋ data
(order_item)
redisTemplate.boundHashOps("Cart_" + username).put(id,
orderItem);// hset key field value hget key field
legou-order/legou-order-service/src/main/java/com/legou/order/controller/CartController.java
}
}
}
4. Control layer
legou-order/legou-order-service/src/main/java/com/legou/order/controller/CartController.java
package com.legou.order.controller;
import com.legou.order.config.TokenDecode;
import com.legou.order.service.CartService;
import com.lxs.legou.order.po.OrderItem;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
import java.util.List;
/** * @Des New vocational course mall item ⽬ * @Author cedar * @Date 2020/11/30 18:04 */
@RestController
@RequestMapping("/cart")
@CrossOrigin
public class CartController {
@Autowired
private CartService cartService;
@Autowired
private TokenDecode tokenDecode;
/** * Add shopping ⻋ * * @param id Of the goods to be purchased SKU Of ID * @param num Quantity to buy * @return Test add shopping ⻋, The effect is as follows : Request address http://localhost:9009/cart/add?num=6&id=2868393 Redis Data in cache */
@RequestMapping("/add")
public ResponseEntity add(Long id, Integer num) throws IOException {
//springsecurity Get current ⽤ Account name Pass on service
String username = "lxs";
// Map<String, String> userInfo = tokenDecode.getUserInfo();
// String username = userInfo.get("username");
System.out.println("⽤ Account name :"+username);
cartService.add(id, num, username);
return ResponseEntity.ok(" Add success ");
}
}
Test add shopping ⻋, The effect is as follows :
Request address http://localhost:9009/cart/add?num=6&id=2868393
5 feign transfer ⽤ abnormal
You can make ⽤fallbackFactory Print feign transfer ⽤ abnormal
production ⽣FallbackFactory Components
stay FeignClient In the note, by fallbackFactory Property specifies the configuration ⾯ The components of
package com.legou.order.client;
import com.lxs.legou.item.api.SkuApi;
import com.lxs.legou.item.po.Sku;
import feign.hystrix.FallbackFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.List;
@FeignClient(name = "item-service", /*fallback = SkuClient.SkuClientFallback.class*/ fallbackFactory =
SkuClient.SkuClientFallbackFactory.class)
public interface SkuClient extends SkuApi {
@Component
@RequestMapping("/sku-fallback")
// This can avoid the container requestMapping repeat
class SkuClientFallback implements SkuClient {
private static final Logger LOGGER =
LoggerFactory.getLogger(SkuClientFallback.class);
@Override
public List<Sku> selectSkusBySpuId(Long spuId) {
LOGGER.error(" Abnormal hair ⽣, Into the ⼊fallback⽅ Law ");
return null;
}
@Override
public Sku edit(Long id) {
LOGGER.error(" Abnormal hair ⽣, Into the ⼊fallback⽅ Law ");
return null;
}
}
@Component
@RequestMapping("/sku-fallback-factory")
class SkuClientFallbackFactory implements FallbackFactory<SkuClient> {
Logger logger =
LoggerFactory.getLogger(SkuClientFallbackFactory.class);
@Override
public SkuClient create(Throwable throwable) {
throwable.printStackTrace();
logger.error(throwable.getMessage());
return new SkuClient() {
@Override
public List<Sku> selectSkusBySpuId(Long spuId) {
return null;
}
@Override
public Sku edit(Long id) {
return null;
}
};
}
}
}
6.1.4 shopping ⻋ list
(1) Thought analysis
(2) Code implementation
1. The business layer
The business layer connects ⼝
/** * from redis Get the current ⽤ Household shopping ⻋ The list data of * @param username * @return */
List<OrderItem> list(String username);
The business layer connects ⼝ Implementation class
@Override
public List<OrderItem> list(String username) {
List<OrderItem> orderItemList = redisTemplate.boundHashOps("Cart_" +
username).values();
return orderItemList;
}
2. Control layer
@RequestMapping("/list")
public ResponseEntity<List<OrderItem>> list() throws IOException {
String username = "lxs";
// Map<String, String> userInfo = tokenDecode.getUserInfo();
// String username = userInfo.get("username");
System.out.println(" wow ::⽤ Account name :"+username);
List<OrderItem> orderItemList = cartService.list(username);
return new ResponseEntity<>(orderItemList, HttpStatus.OK);
}
3. test
send ⽤Postman visit GET http://localhost:9009/cart/list , The effect is as follows :
(3) Problem handling
1. Delete item shopping ⻋
We found a problem , Namely ⽤ The customer adds ⼊ shopping ⻋,⽆ Quantity is positive and negative , I'll stick to it ⾏ Add shopping ⻋, If quantity if <=0, The... Of this item should be removed .
modify changgou-service-order Of com.changgou.order.service.impl.CartServiceImpl Of add⽅ Law , Add the following code :
if(num<=0){
// Delete the original product
redisTemplate.boundHashOps("Cart_" + username).delete(id);
return;
}
6.2 Authentication between microservices
6.2.1 Pass the administrator token
send ⽤ scene : We are in the process of authorization ⼼ Microservice tuning ⽤⽤ You can directly ⽣ Become an administrator token , adopt header Pass on to ⽤ Household micro service , It was not implemented before because it was for testing ⽅ Then we are ⽤ Users can access the service without authentication ⽤ Household micro service .
After opening authentication access , The processing logic should be :
Code implementation :
production ⽣ Administrator token ⼯ have ⽅ Law :
auth-center/src/main/java/com/service/auth/serviceauth/utils/AdminToken.java
package com.service.auth.serviceauth.utils;
/** * @author * @version 1.0 * @description * @createDate 2022/6/22 10:21 **/
import org.codehaus.jackson.map.ObjectMapper;
import org.springframework.core.io.ClassPathResource;
import org.springframework.security.jwt.Jwt;
import org.springframework.security.jwt.JwtHelper;
import org.springframework.security.jwt.crypto.sign.RsaSigner;
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;
import java.io.IOException;
import java.security.KeyPair;
import java.security.interfaces.RSAPrivateKey;
import java.util.HashMap;
import java.util.Map;
public class AdminToken {
public static String adminToken() throws IOException {
// certificate ⽂ Pieces of
String key_location = "mickey.jks";
// Keystore password
String keystore_password = "mickey";
// Access certificate path
ClassPathResource resource = new ClassPathResource(key_location);
// secret key ⼯⼚
KeyStoreKeyFactory keyStoreKeyFactory = new
KeyStoreKeyFactory(resource, keystore_password.toCharArray());
// Cipher of key , This password and alias should match
String keypassword = "mickey";
// key alias
String alias = "mickey";
// Key pair ( Key and public key )
KeyPair keyPair = keyStoreKeyFactory.getKeyPair(alias,
keypassword.toCharArray());
// Private key
RSAPrivateKey aPrivate = (RSAPrivateKey) keyPair.getPrivate();
// Definition payload Information
Map<String, Object> tokenMap = new HashMap<String, Object>();
tokenMap.put("user_name", "admin");
tokenMap.put("client_id", "client");
tokenMap.put("authorities", new String[] {
"ROLE_ADMIN"});
//⽣ become jwt token
Jwt jwt = JwtHelper.encode(new
ObjectMapper().writeValueAsString(tokenMap), new RsaSigner(aPrivate));
// Take out jwt token
String token = jwt.getEncoded();
return token;
}
}
Feign Interceptor
auth-center/src/main/java/com/service/auth/serviceauth/interceptor/TokenRequestInterceptor.java
package com.service.auth.serviceauth.interceptor;
import com.service.auth.serviceauth.utils.AdminToken;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.stereotype.Component;
import java.io.IOException;
/** * @Des New vocational course mall project - Interceptor * @Author cedar * @Date 2020/12/2 15:57 */
@Component
public class TokenRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
String token = null;
try {
token = AdminToken.adminToken();
} catch (IOException e) {
e.printStackTrace();
}
requestTemplate.header("Authorization", "Bearer " + token);
}
}
6.2.2 Deliver the current ⽤ User token
send ⽤ scene : shopping ⻋ The function has been completed , but ⽤ We are all hard coded .⽤ If you want to add ⼊ shopping ⻋, You must first log in to authorize , And then through header Pass the token to the shopping ⻋ Microservices , shopping ⻋ Microservices go through Feign The interceptor adds token passing to the commodity micro service , As shown in the figure below :
1. Create interceptor
legou-order/legou-order-service/src/main/java/com/legou/order/interceptor/MyFeignInterceptor.java
package com.legou.order.interceptor;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Enumeration;
/** * @Des New vocational course mall item ⽬ * @Author cedar * @Date 2020/12/1 10:19 */
@Component
public class MyFeignInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
// Get the current request header , Pass it to the commodity microservices , Get the current thread's request Information . In this case, if you use thread isolation , A semaphore isolation scheme is required . Otherwise, it will report a mistake
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (requestAttributes != null) {
//1. Get request object
HttpServletRequest request = requestAttributes.getRequest();
Enumeration<String> headerNames = request.getHeaderNames();
if (headerNames != null) {
//2. Get all header information in the request object ( The request came through )
while (headerNames.hasMoreElements()) {
String name = headerNames.nextElement();// Name of head
String value = request.getHeader(name);// The value corresponding to the header name
System.out.println("name:" + name + "::::::::value:" +
value);
//3. Pass header information to fegin (restTemplate)
requestTemplate.header(name,value);
}
}
}
}
}
2. test
We found this ServletRequestAttributes Always empty RequestContextHolder.getRequestAttributes()
The ⽅ The law is from ThreadLocal Variable ⾥⾯ Get the corresponding information , When hystrix The isolation strategy of the circuit breaker is THREAD when , yes ⽆ Can't get ThreadLocal The value in .
solve ⽅ case :hystrix The isolation strategy is changed to SEMAPHORE
config-repo/application.yml
The test again , The effect is as follows :
6.2.3 obtain ⽤ Household data
(1) Data analysis
⽤ After login , The data will be encapsulated in SecurityContextHolder.getContext().getAuthentication() ⾥⾯,
We can take the data from here ⾥⾯ Take out , And then convert to OAuth2AuthenticationDetails , Here ⾥⾯ You can get the token information 、 Token type, etc , The code is as follows :
this ⾥ Of tokenValue Is the encrypted token data ,remoteAddress yes ⽤ Household IP Information ,tokenType Is the token type . We can get the token to encrypt the data , send ⽤ The public key enters ⾏ Decrypt , If you can decrypt the instructions, say ⽆ By mistake , If you can't decrypt ⽤ Household
I can't hold on ⾏ Here we are ⼀ Step . After decryption, you can start from the Ming Dynasty ⽂ In order to get ⽤ User information .
Code implementation
1.⼯ With the class
legou-order/legou-order-service/src/main/java/com/legou/order/config/TokenDecode.java
package com.legou.order.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.jwt.Jwt;
import org.springframework.security.jwt.JwtHelper;
import org.springframework.security.jwt.crypto.sign.RsaVerifier;
import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails;
import org.springframework.stereotype.Component;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Map;
import java.util.stream.Collectors;
/** * @Des New vocational course mall project * @Author cedar * @Date 2020/11/30 18:06 */
@Component
public class TokenDecode {
private static final String PUBLIC_KEY = "mickey_public.key";
@Autowired
private ObjectMapper objectMapper;
// Get token
public String getToken() {
OAuth2AuthenticationDetails authentication = (OAuth2AuthenticationDetails) SecurityContextHolder.getContext().getAuthentication().getDetails();
String tokenValue = authentication.getTokenValue();
return tokenValue;
}
/** * Get the user information of the currently logged in user * * @return */
public Map<String, String> getUserInfo() throws IOException {
//1. Get token
String token = getToken();
//2. Parse token Public key
String pubKey = getPubKey();
Jwt jwt = JwtHelper.decodeAndVerify(token, new RsaVerifier(pubKey));
String claims = jwt.getClaims();//{}
System.out.println(claims);
//3. return
// Map<String,String> map = JSON.parseObject(claims, Map.class);
Map<String, String> map = objectMapper.readValue(claims, Map.class);
return map;
}
private String getPubKey() {
Resource resource = new ClassPathResource(PUBLIC_KEY);
try {
InputStreamReader inputStreamReader = new InputStreamReader(resource.getInputStream());
BufferedReader br = new BufferedReader(inputStreamReader);
return br.lines().collect(Collectors.joining("\n"));
} catch (IOException ioe) {
return null;
}
}
}
2. controller
3. test
send ⽤postman Access with token
6.3 Order settlement ⻚
6.3.1 Receiving address analysis
⽤ From shopping ⻋⻚⾯ Click settle , Jump to order settlement ⻚, Settlement ⻚ Need to load ⽤ The corresponding receiving address of the account , Here's the picture :
Table structure analysis :
CREATE TABLE `address_` (
`id_` int(11) NOT NULL AUTO_INCREMENT,
`username_` varchar(50) DEFAULT NULL COMMENT '⽤ Account name ',
`province_` varchar(20) DEFAULT NULL COMMENT ' province ',
`city_` varchar(20) DEFAULT NULL COMMENT ' City ',
`area_` varchar(20) DEFAULT NULL COMMENT ' county / District ',
`phone_` varchar(20) DEFAULT NULL COMMENT ' Telephone ',
`address_` varchar(200) DEFAULT NULL COMMENT ' Detailed address ',
`contact_` varchar(50) DEFAULT NULL COMMENT ' contact ⼈',
`is_default_` varchar(1) DEFAULT NULL COMMENT ' Is it the default 1 Default 0 no ',
`alias_` varchar(50) DEFAULT NULL COMMENT ' Alias ',
PRIMARY KEY (`id_`)
) ENGINE=InnoDB AUTO_INCREMENT=66 DEFAULT CHARSET=utf8;
We can use ⽤ The user login name goes to address_ Query the corresponding data in the table .
6.3.2 Realization ⽤ Customer receiving address query
(1) Code implementation
Entity class
stay ⽤ Household micro service ⼯ Add... In the process Address
legou-security/legou-securityinstance/src/main/java/com/lxs/legou/security/po/Address.java
package com.lxs.legou.order.po;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.lxs.legou.core.po.BaseEntity;
import lombok.Data;
/** * @author * @version 1.0 * @description Shipping address * @createDate 2022/6/22 20:41 **/
@Data
@TableName("address_")
public class Address extends BaseEntity {
@TableField("username_")
private String username;//⽤ Account name
@TableField("province_")
private String provinceid;// province
@TableField("city_")
private String cityid;// City
@TableField("area_")
private String areaid;// county / District
@TableField("phone_")
private String phone;// Telephone
@TableField("address_")
private String address;// Detailed address
@TableField("contact_")
private String contact;// contact ⼈
@TableField("is_default_")
private String isDefault;// Is it the default 1 Default 0 no
@TableField("alias_")
private String alias;// Alias
}
dao
legou-security/legou-security-service/src/main/java/com/lxs/legou/security/dao/AddressDao.java
package com.lxs.legou.security.dao;
import com.lxs.legou.core.dao.ICrudDao;
import com.lxs.legou.security.po.Address;
/** * @Des New vocational course mall item ⽬ * @Author cedar * @Date 2020/12/3 11:38 */
public interface AddressDao extends ICrudDao<Address> {
}
service
legou-security/legou-security-service/src/main/java/com/lxs/legou/security/service/IAddressService.java
package com.lxs.legou.security.service;
import com.lxs.legou.core.service.ICrudService;
import com.lxs.legou.security.po.Address;
/** * @Des New vocational course mall item ⽬ * @Author cedar * @Date 2020/12/3 11:38 */
public interface IAddressService extends ICrudService<Address> {
}
legou-security/legou-securityservice/src/main/java/com/lxs/legou/security/service/impl/AddressServiceImpl.java
package com.legou.order.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.legou.order.service.IAddressService;
import com.lxs.legou.core.service.impl.CrudServiceImpl;
import com.lxs.legou.order.po.Address;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import java.util.List;
/** * @author * @version 1.0 * @description * @createDate 2022/6/22 20:48 **/
@Service
public class AddressServiceImpl extends CrudServiceImpl<Address> implements IAddressService {
@Override
public List<Address> list(Address entity) {
// according to ⽤ Account name inquiry ⽤ Customer receiving address
QueryWrapper<Address> queryWrapper = Wrappers.<Address>query();
if (StringUtils.isNotEmpty(entity.getUsername())) {
queryWrapper.eq("username_", entity.getUsername());
}
return getBaseMapper().selectList(queryWrapper);
}
}
legou-security/legou-securityservice/src/main/java/com/lxs/legou/security/controller/AddressController.java
package com.legou.order.CartController;
import com.legou.order.config.TokenDecode;
import com.legou.order.service.IAddressService;
import com.lxs.legou.core.controller.BaseController;
import com.lxs.legou.order.po.Address;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
import java.util.List;
import java.util.Map;
/** * @Des New vocational course mall item ⽬ * @Author cedar * @Date 2020/12/3 11:50 */
@RestController
@RequestMapping(value = "/address")
public class AddressController extends BaseController<IAddressService, Address>
{
@Autowired
private TokenDecode tokenDecode;
@Override
@ApiOperation(value=" Inquire about ", notes=" Query according to entity conditions ")
@RequestMapping(value = "/list", method = {
RequestMethod.POST, RequestMethod.GET})
public List<Address> list(Address entity) {
Map<String, String> user = null;
try {
user = tokenDecode.getUserInfo();
} catch (IOException e) {
e.printStackTrace();
}
String username = user.get("user_name");
entity.setUsername(username);
// Query the recipient address according to the current user
return service.list(entity);
}
}
(2) test
send ⽤postman Access with token
(3) Shipping list
Shipping lists are actually shopping ⻋ list , Direct query of previous purchases ⻋ Just list , this ⾥ No explanation .'”
6.4 Place an order
6.4.1 Business analysis
Click settle ⻚ When submitting the order , Meeting ⽴ Create order data , Creating order data will save the data ⼊ To 2 Zhang biaozhong , They are order form and order details , Here, you also need to modify the inventory quantity corresponding to the commodity , change ⽤ Points of household , Then delete the shopping that should be ⻋ data .
The structure of the order form is as follows :
package com.lxs.legou.order.po;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.lxs.legou.core.po.BaseEntity;
import lombok.Data;
import java.util.Date;
/** * @Des New vocational course mall item ⽬ Order master * @Author cedar * @Date 2020/11/30 14:20 */
@Data
@TableName("order_")
public class Order extends BaseEntity {
/** * Snowflake algorithm , Get the primary key */
@TableId(value = "id_", type = IdType.INPUT)
private Long id;
@TableField("total_num_")
private Long totalNum;// The total quantity is
@TableField("total_money_")
private Long totalMoney;//⾦ Sum up
@TableField("pre_money_")
private Long preMoney;// Discount ⾦ forehead
@TableField("post_fee_")
private Long postFee;// Postage
@TableField("pay_money_")
private Long payMoney;// Paid in ⾦ forehead
@TableField("pay_type_")
private String payType;//⽀ Payment type ,1、 On-line ⽀ pay 、0 Cash on Delivery
@TableField("create_time_")
private Date createTime;// Order creation time
@TableField("update_time_")
private Date updateTime;// Order update time
@TableField("pay_time_")
private Date payTime;// Time of payment
@TableField("consign_time_")
private Date consignTime;// Delivery time
@TableField("end_time_")
private Date endTime;// Completion time
@TableField("close_time_")
private Date closeTime;// Closing time
@TableField("shipping_name_")
private String shippingName;// Logistics name order details
@TableField("shipping_code_")
private String shippingCode;// Logistics order No
@TableField("username_")
private String username;//⽤ Account name
@TableField("buyer_message_")
private String buyerMessage;// The buyer leaves ⾔
@TableField("buyer_rate_")
private String buyerRate;// Whether to evaluate
@TableField("receiver_contact_")
private String receiverContact;// Receiving goods ⼈
@TableField("receiver_mobile_")
private String receiverMobile;// Receiving goods ⼈⼿ machine
@TableField("receiver_address_")
private String receiverAddress;// Receiving goods ⼈ Address
@TableField("source_type_")
private String sourceType;// Source of order :1:web,2:app,3: WeChat official account ,4: WeChat ⼩ Program 5H5⼿ machine ⻚⾯
@TableField("transaction_id_")
private String transactionId;// Transaction flow ⽔ Number
@TableField("order_status_")
private String orderStatus;// The order status ,0: Hang in the air ,1: Completed ,2: Has returned
@TableField("pay_status_")
private String payStatus;//⽀ Payment status ,0: not ⽀ pay ,1: has ⽀ pay ,2:⽀ Payment failed
@TableField("consign_status_")
private String consignStatus;// Delivery status ,0: Not delivered ,1: Shipped ,2: Received goods
@TableField("is_delete_")
private String isDelete;// Whether or not to delete
}
The structure of the order details is as follows :
package com.lxs.legou.order.po;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.lxs.legou.core.po.BaseEntity;
import lombok.Data;
/** * @author * @version 1.0 * @description The order details * @createDate 2022/6/21 16:19 **/
@Data
@TableName("order_item_")
public class OrderItem extends BaseEntity {
/** * Snowflake algorithm , Get the primary key */
@TableId(value = "id_", type = IdType.INPUT)
private Long id;
@TableField("category_id1_")
private Long categoryId1;//1 Class classification
@TableField("category_id2_")
private Long categoryId2;//2 Class classification
@TableField("category_id3_")
private Long categoryId3;//3 Class classification
@TableField("spu_id_")
private Long spuId;//SPU_ID
@TableField("sku_id_")
private Long skuId;//SKU_ID
@TableField("order_id_")
private long orderId;// Order ID
@TableField("name_")
private String name;// Name of commodity
@TableField("price_")
private Long price;// The unit price
@TableField("num_")
private Integer num;// Number
@TableField("money_")
private Long money;// total ⾦ forehead
@TableField("pay_money_")
private Long payMoney;// Paid in ⾦ forehead
@TableField("image_")
private String image;// chart ⽚ Address
@TableField("weight_")
private Integer weight;// weight
@TableField("post_fee_")
private Integer postFee;// The freight
@TableField("is_return_")
private String isReturn;// Is it a return ,0: Not returned ,1: Has returned
}
6.4.2 Order to achieve
When placing an order , First add the order to order Add data to the table , Add order details , Go to order_item Add data to the table .
(1) ⽣ Order No
Distributed system , Yes ⼀ Some need to make ⽤ The overall situation is ⼀ID Scene , This time in order to prevent ⽌ID Conflict can make ⽤36 Bit UUID, however UUID Yes ⼀ There are some shortcomings ,⾸ First he is opposite ⽐ a ⻓, in addition UUID⼀ Like ⽆ Preface . Sometimes we want to make ⽤⼀ Simple ⼀ More ID, And hope ID To be able to order by time ⽣ become .⽽twitter Of SnowFlake Solved this demand , first Twitter Take the storage system from MySQL Migrate to Cassandra, because Cassandra There is no order ID⽣ Mechanism , So we developed this ⼀ Set global only ⼀ID⽣ Become a service .
all ⽣ Yes id Increase in time
Distributed systems do not produce ⽣ repeat id( Because there is datacenterId and workerId To make a distinction )
⼯ With the class legou-common/src/main/java/com/lxs/legou/common/utils/IdWorker.java
package com.lxs.legou.common.utils;
import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.net.NetworkInterface;
/** * <p> name :IdWorker.java</p> * <p> describe : Distributed self growth ID</p> * <pre> * Twitter Of Snowflake JAVA Implementation scheme * </pre> * The core code is IdWorker This class implementation , Its principle and structure are as follows , I use one for each 0 It means one person , use — The function of dividing parts : * 1||0---0000000000 0000000000 0000000000 0000000000 0 --- 00000 ---00000 ---000000000000 * In the string above , The first is unused ( In fact, it can also be used as long The sign bit of ), Next 41 Bit is millisecond time , * then 5 position datacenter Identification bit ,5 Bit machine ID( It's not an identifier , It's actually identifying the thread ), * then 12 The count of the current millisecond in that millisecond , It just adds up to 64 position , For one Long type . * The advantage of this is , On the whole, it is sorted according to the increasing time , And the whole distributed system will not produce ID Collision ( from datacenter And machines ID Make a distinction ), * And it's more efficient , After testing ,snowflake Every second can produce 26 ten thousand ID about , Fully meet the needs . * <p> * 64 position ID (42( millisecond )+5( machine ID)+5( Business coding )+12( Add up again and again )) * * @author Polim */
public class IdWorker {
// Time start mark point , As a benchmark , Generally, the latest time of the system ( Once it is determined that it cannot be changed )
private final static long twepoch = 1288834974657L;
// Number of machine marks
private final static long workerIdBits = 5L;
// Data center identification number
private final static long datacenterIdBits = 5L;
// machine ID Maximum
private final static long maxWorkerId = -1L ^ (-1L << workerIdBits);
// Data Center ID Maximum
private final static long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
// Auto increment in milliseconds
private final static long sequenceBits = 12L;
// machine ID Shift to the left 12 position
private final static long workerIdShift = sequenceBits;
// Data Center ID Move left 17 position
private final static long datacenterIdShift = sequenceBits + workerIdBits;
// Time millisecond shift left 22 position
private final static long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
private final static long sequenceMask = -1L ^ (-1L << sequenceBits);
/* Last production id Time stamp */
private static long lastTimestamp = -1L;
// 0, concurrency control
private long sequence = 0L;
private final long workerId;
// Data identification id part
private final long datacenterId;
public IdWorker(){
this.datacenterId = getDatacenterId(maxDatacenterId);
this.workerId = getMaxWorkerId(datacenterId, maxWorkerId);
}
/** * @param workerId * Work the machine ID * @param datacenterId * Serial number */
public IdWorker(long workerId, long datacenterId) {
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
}
if (datacenterId > maxDatacenterId || datacenterId < 0) {
throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
}
this.workerId = workerId;
this.datacenterId = datacenterId;
}
/** * Get the next one ID * * @return */
public synchronized long nextId() {
long timestamp = timeGen();
if (timestamp < lastTimestamp) {
throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
}
if (lastTimestamp == timestamp) {
// In the current millisecond , be +1
sequence = (sequence + 1) & sequenceMask;
if (sequence == 0) {
// The current millisecond count is full , The next second
timestamp = tilNextMillis(lastTimestamp);
}
} else {
sequence = 0L;
}
lastTimestamp = timestamp;
// ID The offset combination generates the final ID, And back to ID
long nextId = ((timestamp - twepoch) << timestampLeftShift)
| (datacenterId << datacenterIdShift)
| (workerId << workerIdShift) | sequence;
return nextId;
}
private long tilNextMillis(final long lastTimestamp) {
long timestamp = this.timeGen();
while (timestamp <= lastTimestamp) {
timestamp = this.timeGen();
}
return timestamp;
}
private long timeGen() {
return System.currentTimeMillis();
}
/** * <p> * obtain maxWorkerId * </p> */
protected static long getMaxWorkerId(long datacenterId, long maxWorkerId) {
StringBuffer mpid = new StringBuffer();
mpid.append(datacenterId);
String name = ManagementFactory.getRuntimeMXBean().getName();
if (!name.isEmpty()) {
/* * GET jvmPid */
mpid.append(name.split("@")[0]);
}
/* * MAC + PID Of hashcode obtain 16 Low position */
return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1);
}
/** * <p> * Data identification id part * </p> */
protected static long getDatacenterId(long maxDatacenterId) {
long id = 0L;
try {
InetAddress ip = InetAddress.getLocalHost();
NetworkInterface network = NetworkInterface.getByInetAddress(ip);
if (network == null) {
id = 1L;
} else {
byte[] mac = network.getHardwareAddress();
id = ((0x000000FF & (long) mac[mac.length - 1])
| (0x0000FF00 & (((long) mac[mac.length - 2]) << 8))) >> 6;
id = id % (maxDatacenterId + 1);
}
} catch (Exception e) {
System.out.println(" getDatacenterId: " + e.getMessage());
}
return id;
}
}
(2)Dao
legou-order/legou-order-service/src/main/java/com/lxs/legou/order/dao/OrderDao.java
package com.lxs.legou.order.dao;
import com.lxs.legou.core.dao.ICrudDao;
import com.lxs.legou.order.po.Order;
public interface OrderDao extends ICrudDao<Order> {
}
mybatis\order\OrderDao.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.lxs.legou.order.dao.OrderDao">
<select id="selectByPage" resultType="Order">
select
*
from
order_
<where>
<if test="username != null and username != ''">
username_ = #{username}
</if>
</where>
</select>
</mapper>
com.lxs.legou.order.dao.OrderItemDao
package com.lxs.legou.order.dao;
import com.lxs.legou.core.dao.ICrudDao;
import com.lxs.legou.order.po.OrderItem;
public interface OrderItemDao extends ICrudDao<OrderItem> {
}
(3)Service
com.lxs.legou.order.service.OrderService
package com.lxs.legou.order.service;
import com.lxs.legou.core.service.ICrudService;
import com.lxs.legou.order.po.Order;
public interface OrderService extends ICrudService<Order> {
/** * Add orders * @param order */
public void add(Order order);
}
com.lxs.legou.order.service.impl.OrderServiceImpl
package com.legou.order.service.impl;
import com.legou.order.client.SkuClient;
import com.legou.order.client.UserClient;
import com.legou.order.dao.IOrderItemDao;
import com.legou.order.service.IOrderService;
import com.lxs.legou.common.utils.IdWorker;
import com.lxs.legou.core.service.impl.CrudServiceImpl;
import com.lxs.legou.order.po.Order;
import com.lxs.legou.order.po.OrderItem;
import com.netflix.discovery.converters.Auto;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.List;
@Service
public class OrderServiceImpl extends CrudServiceImpl<Order> implements IOrderService {
@Autowired
private IdWorker idWorker;
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private IOrderItemDao orderItemDao;
@Autowired
private SkuClient skuClient;
@Autowired
private UserClient userClient;
@Override
public void add(Order order) {
//1 Add order main table (Order) data
order.setId(idWorker.nextId());
//2 Recycle cart data , Add order details (OrderItem) data
List<OrderItem> cartList = redisTemplate.boundHashOps("Cart_" + order.getUsername()).values();
Long totalNum =0l; // Total order quantity
long totalMoney = 0l; // Total order amount
for (OrderItem orderItem : cartList) {
totalNum += orderItem.getNum();
totalMoney += orderItem.getPayMoney();
orderItem.setId(idWorker.nextId());// Of order options iD
orderItem.setOrderId(order.getId());// Order's iD
orderItem.setIsReturn("0");// Not returned
orderItemDao.insert(orderItem);
//3 Call commodity micro service , Reduce inventory
skuClient.decrCount(orderItem.getNum(), orderItem.getSkuId());
}
order.setTotalNum(totalNum);// Set total quantity
order.setTotalMoney(totalMoney);// Set total amount
order.setPayMoney(totalMoney);// Set paid in amount
order.setCreateTime(new Date());
order.setUpdateTime(order.getCreateTime());
order.setOrderStatus("0");//0: Hang in the air
order.setPayStatus("0");// Did not pay
order.setConsignStatus("0");// Not delivered
order.setIsDelete("0");// Not delete
getBaseMapper().insert(order);
//4 Increase user points
userClient.addPoint(10l, order.getUsername());
//5 Delete redis Shopping cart data in
redisTemplate.delete("Cart_" + order.getUsername());
}
}
(4)Controller
com.lxs.legou.order.controller.OrderController
package com.lxs.legou.order.controller;
import com.lxs.legou.order.config.TokenDecode;
import com.lxs.legou.order.service.OrderService;
import com.lxs.legou.core.controller.BaseController;
import com.lxs.legou.order.po.Order;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
@RestController
@RequestMapping("/order")
public class OrderController extends BaseController<OrderService, Order> {
@Autowired
private TokenDecode tokenDecode;
/** * Add order * @param order * @return * @throws IOException */
@PostMapping("/add")
public ResponseEntity add(@RequestBody Order order) throws IOException {
order.setUsername(tokenDecode.getUserInfo().get("user_name"));
service.add(order);
return ResponseEntity.ok(" Add success ");
}
}
(5) test
6.4.3 Inventory changes
(1) Business analysis
On ⾯ The operation only implements the order placing operation , But the corresponding inventory has not yet followed ⼀ Starting reduction , After we place the order , It should be adjusted ⽤ Commodity micro service , Reduce the inventory of goods ordered , Sales increase . For each order, the microservice only needs to ⽤ The account name is transferred to the commodity micro service , The commodity microservices passed ⽤ Account name to Redis Query the corresponding shopping in ⻋ data , Then hold ⾏ Inventory reduction , Inventory reduction needs to control the current commodity inventory >= sales volumes .
How to control inventory quantity >= Sales volume ? You can actually go through SQL Statements for , Every time you reduce the quantity , Add a condition to judge .where num>=#{num} that will do .
(2) Code implementation
Dao
modify legou-item/legou-item-service/src/main/java/com/lxs/legou/item/dao/SkuDao.java
@Update(value="update sku_ set stock_ = stock_ - #{
num} where id_ =#{
skuId}
and stock_ >= #{
num}")
public int decrCount(@Param("num") Integer num, @Param("skuId") Long
skuId);
service
modify legou-item/legou-item-service/src/main/java/com/lxs/legou/item/service/ISkuService.java
/** * Reduce inventory * @param num * @param skuId */
public void decrCount(Integer num, Long skuId);
modify legou-item/legou-item-service/src/main/java/com/lxs/legou/item/service/impl/SkuServiceImpl.java
@Override
public void decrCount(Integer num, Long skuId) {
((SkuDao) getBaseMapper()).decrCount(num, skuId);
}
controller
modify legou-item/legou-item-service/src/main/java/com/lxs/legou/item/controller/SkuController.java
/** * Reduce inventory * @param num * @param skuId */
@PostMapping(value = "/decr-count")
public void decrCount(@RequestParam("num") Integer num,
@RequestParam("skuId") Long skuId) {
service.decrCount(num, skuId);
}
Be careful : adopt Feign transfer ⽤Controller If you have more than one parameter , Must write @RequestParam, Otherwise, throw ⾯ abnormal
Caused by: java.lang.IllegalStateException: Method has too many Body
parameters: public abstract void
com.lxs.legou.item.api.SkuApi.decrCount(java.lang.Integer,java.lang.Long)
Feign Client
legou-item/legou-item-instance/src/main/java/com/lxs/legou/item/api/SkuApi.java
package com.lxs.legou.item.api;
import com.lxs.legou.item.po.Sku;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RequestMapping(value = "/sku")
public interface SkuApi {
@ApiOperation(value=" Inquire about spu Corresponding sku", notes=" according to spuId Inquire about sku aggregate ")
@GetMapping("/select-skus-by-spuid/{id}")
public List<Sku> selectSkusBySpuId(@PathVariable("id") Long spuId);
@ApiOperation(value=" load ", notes=" according to ID load ")
@GetMapping("/edit/{id}")
public Sku edit(@PathVariable Long id);
@PostMapping(value = "/decr-count")
public void decrCount(@RequestParam("num") Integer num,
@RequestParam("skuId") Long skuId);
}
legou-order/legou-order-service/src/main/java/com/lxs/legou/order/client/SkuClient.java
package com.lxs.legou.order.client;
import com.lxs.legou.item.api.SkuApi;
import com.lxs.legou.item.po.Sku;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.List;
@FeignClient(name = "item-service", fallback =
SkuClient.SkuClientFallback.class)
public interface SkuClient extends SkuApi {
@Component
@RequestMapping("/sku-fallback")
// This can avoid the container requestMapping repeat
class SkuClientFallback implements SkuClient {
private static final Logger LOGGER =
LoggerFactory.getLogger(SkuClientFallback.class);
@Override
public List<Sku> selectSkusBySpuId(Long spuId) {
LOGGER.error(" Abnormal hair ⽣, Into the ⼊fallback⽅ Law ");
return null;
}
@Override
public Sku edit(Long id) {
LOGGER.error(" Abnormal hair ⽣, Into the ⼊fallback⽅ Law ");
return null;
}
@Override
public void decrCount(Integer num, Long skuId) {
LOGGER.error(" Abnormal hair ⽣, Into the ⼊fallback⽅ Law ");
}
}
}
(3) transfer ⽤ Inventory decline
(4) test
The inventory decreases after the order is placed
(5) Oversold problem
If inventory decline causes ⽤ Next ⾯ The pseudo code of will produce ⽣ Oversold phenomenon , The essence of oversold is multi-threaded data synchronization , Because it is a distributed system , It cannot be simply locked , If you deal with ⾯ The logic of needs makes ⽤ Distributed lock , About distributed locks , after ⾯ Content explanation , Because our code directly executes ⾏ sentence , A database ⾏ Level lock , Can't produce ⽣ Oversold problem
6.4.4 Add points
⽐ For example, after each order is placed , to ⽤ The number of households increased 10 Integral points ,⽀ A coupon will be given after payment , Coupons can ⽤ On ⽀ Deduct again when paying . Let's finish the function of adding integral first . The following table :point Express ⽤ Household integral
(1) Code implementation
dao
modify legou-security/legou-security-service/src/main/java/com/lxs/legou/security/dao/UserDao.java
/** * Add points * @param point * @param userName * @return */
@Update(value="update user_ set point_ = point_ + #{
point} where user_name_ =
#{
userName}")
public int addPoint(@Param(value="point") Long point
,@Param(value="userName") String userName);
service
modify legou-security/legou-securityservice/src/main/java/com/lxs/legou/security/service/IUserService.java
/** * Increase membership points */
public void addPoint(Long point, String username);
modify legou-security/legou-securityservice/src/main/java/com/lxs/legou/security/service/impl/UserServiceImpl.java
@Override
public void addPoint(Long point, String username) {
((UserDao) getBaseMapper()).addPoint(point, username);
}
Control layer
modify legou-security/legou-securityservice/src/main/java/com/lxs/legou/security/controller/UserController.java
/** * Add points * @param point * @param username */
@GetMapping(value = "/add-point")
public void addPoint(@RequestParam("point") Long point,
@RequestParam("username") String username) {
service.addPoint(point, username);
}
Feign add to
modify legou-security/legou-security-instance/src/main/java/com/lxs/legou/security/api/UserApi.java
@GetMapping(value = "/add-point")
public void addPoint(@RequestParam("point") Long point,
@RequestParam("username") String username);
(2) Increase integral adjustment ⽤
legou-order/legou-orderservice/src/main/java/com/lxs/legou/order/service/impl/OrderServiceImpl.java
//4. Add points transfer ⽤⽤ Wechat services userfeign Add points
userClient.addPoint(10l, order.getUsername())
(3) test
边栏推荐
- 1.9 - Classification of memory
- 1.6 - CPU composition
- Fastapi learning Day1
- Gazebo model modification
- Gazebo installation, uninstall and upgrade
- 1.7 - CPU performance indicators
- High performance distributed execution framework ray
- IO streams (common streams)
- Performance comparison of random network, scale-free network, small world network and NS small world matlab simulation
- C语言:练习题三
猜你喜欢
leetcode:98. 验证二叉搜索树
Gazebo installation, uninstall and upgrade
明天!“移动云杯”大赛空宣会开播!
【转】存储器结构、cache、DMA架构分析
0 basic job transfer software test, how to achieve a monthly salary of 9.5k+
RT thread migration to s5p4418 (IV): thread synchronization
[Hot100]回文子串 与 最长回文子串
Rising posture series: fancy debugging information
Px4 control mode summary
Deep learning --- the weight of the three good students' scores (3)
随机推荐
NFS mount
成品升级程序
ROS service communication programming
第一行代码(第三版)学习笔记
1.4 - 定点数与浮点数
1.9 - Cache
史上最全一句话木马
[my creation anniversary] one year anniversary essay
Practice summary of Prometheus project in amu Laboratory
Imxq Freescale yocto project compilation record
Principle: webmvcconfigurer and webmvcconfigurationsupport pit avoidance Guide
How does the CPU recognize the code?
ftplib+ tqdm 上传下载进度条
SOC_ AHB_ SD_ IF
Gazebo installation, uninstall and upgrade
原理:WebMvcConfigurer 与 WebMvcConfigurationSupport避坑指南
Write a C program to judge whether the system is large end byte order or small end byte order
【转】存储器结构、cache、DMA架构分析
The solution of memcpy memory overlap
1.3 - 码制