├── .gitignore ├── src └── main │ ├── java │ └── com │ │ └── mall │ │ └── xiaomi │ │ ├── mapper │ │ ├── UserMapper.java │ │ ├── CarouselMapper.java │ │ ├── CategoryMapper.java │ │ ├── ProductPictureMapper.java │ │ ├── ShoppingCartMapper.java │ │ ├── CollectMapper.java │ │ ├── ProductMapper.java │ │ ├── OrderMapper.java │ │ ├── SeckillTimeMapper.java │ │ ├── SeckillMessageRecordMapper.java │ │ └── SeckillProductMapper.java │ │ ├── pojo │ │ ├── Category.java │ │ ├── User.java │ │ ├── Collect.java │ │ ├── ProductPicture.java │ │ ├── Carousel.java │ │ ├── ShoppingCart.java │ │ ├── Order.java │ │ ├── SeckillTime.java │ │ ├── SeckillMessageRecord.java │ │ ├── SeckillProduct.java │ │ └── Product.java │ │ ├── exception │ │ ├── XmException.java │ │ ├── XmExceptionHandler.java │ │ └── ExceptionEnum.java │ │ ├── vo │ │ ├── OrderVo.java │ │ ├── SeckillProductVo.java │ │ └── CartVo.java │ │ ├── util │ │ ├── RedisKey.java │ │ ├── ResultMessage.java │ │ ├── MD5Util.java │ │ ├── BeanUtil.java │ │ ├── IdWorker.java │ │ └── CookieUtil.java │ │ ├── controller │ │ ├── CarouselController.java │ │ ├── CategoryController.java │ │ ├── ProductPictureController.java │ │ ├── OrderController.java │ │ ├── CollectController.java │ │ ├── ProductController.java │ │ ├── ShoppingCartController.java │ │ ├── SeckillProductController.java │ │ └── UserController.java │ │ ├── service │ │ ├── CategoryService.java │ │ ├── CarouselService.java │ │ ├── ProductPictureService.java │ │ ├── UserService.java │ │ ├── CollectService.java │ │ ├── ProductService.java │ │ ├── ShoppingCartService.java │ │ ├── OrderService.java │ │ └── SeckillProductService.java │ │ ├── Application.java │ │ ├── config │ │ ├── RedisConfig.java │ │ └── RabbitMQConfig.java │ │ ├── task │ │ └── SeckillTask.java │ │ └── mq │ │ └── SeckillOrderQueue.java │ └── resources │ ├── application.yml │ └── mybatis │ ├── ShoppingCartMapper.xml │ ├── ProductMapper.xml │ └── SeckillMessageRecordMapper.xml ├── README.md ├── pom.xml └── xiaomi.sql /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | *.iml 3 | out 4 | gen -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/mapper/UserMapper.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.mapper; 2 | 3 | import com.mall.xiaomi.pojo.User; 4 | import tk.mybatis.mapper.common.Mapper; 5 | 6 | public interface UserMapper extends Mapper { 7 | 8 | } -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/mapper/CarouselMapper.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.mapper; 2 | 3 | import com.mall.xiaomi.pojo.Carousel; 4 | import tk.mybatis.mapper.common.Mapper; 5 | 6 | public interface CarouselMapper extends Mapper { 7 | 8 | } -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/mapper/CategoryMapper.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.mapper; 2 | 3 | import com.mall.xiaomi.pojo.Category; 4 | import tk.mybatis.mapper.common.Mapper; 5 | 6 | public interface CategoryMapper extends Mapper { 7 | 8 | } -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/mapper/ProductPictureMapper.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.mapper; 2 | 3 | import com.mall.xiaomi.pojo.ProductPicture; 4 | import tk.mybatis.mapper.common.Mapper; 5 | 6 | public interface ProductPictureMapper extends Mapper { 7 | 8 | } -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/mapper/ShoppingCartMapper.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.mapper; 2 | 3 | import com.mall.xiaomi.pojo.ShoppingCart; 4 | import org.apache.ibatis.annotations.Param; 5 | import tk.mybatis.mapper.common.Mapper; 6 | 7 | public interface ShoppingCartMapper extends Mapper { 8 | 9 | int updateCartByIdAndVersion(@Param("cart") ShoppingCart cart); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/pojo/Category.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.pojo; 2 | 3 | import lombok.Data; 4 | 5 | import javax.persistence.*; 6 | 7 | @Data 8 | @Table(name = "category") 9 | public class Category { 10 | @Id 11 | @GeneratedValue(strategy = GenerationType.IDENTITY,generator = "JDBC") 12 | private Integer categoryId; 13 | 14 | private String categoryName; 15 | 16 | } -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/pojo/User.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.pojo; 2 | 3 | import lombok.Data; 4 | 5 | import javax.persistence.*; 6 | 7 | @Data 8 | @Table(name = "`user`") 9 | public class User { 10 | @Id 11 | @GeneratedValue(strategy = GenerationType.IDENTITY, generator = "JDBC") 12 | private Integer userId; 13 | 14 | private String username; 15 | 16 | private String password; 17 | 18 | } -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/exception/XmException.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.exception; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * @Auther: wdd 9 | * @Date: 2020-03-19 16:43 10 | * @Description: 11 | */ 12 | @NoArgsConstructor 13 | @AllArgsConstructor 14 | @Getter 15 | public class XmException extends RuntimeException{ 16 | private ExceptionEnum exceptionEnum; 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/pojo/Collect.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.pojo; 2 | 3 | import lombok.Data; 4 | 5 | import javax.persistence.*; 6 | 7 | @Data 8 | @Table(name = "collect") 9 | public class Collect { 10 | @Id 11 | @GeneratedValue(strategy = GenerationType.IDENTITY,generator = "JDBC") 12 | private Integer id; 13 | 14 | private Integer userId; 15 | 16 | private Integer productId; 17 | 18 | private Long collectTime; 19 | 20 | } -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/vo/OrderVo.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.vo; 2 | 3 | import com.mall.xiaomi.pojo.Order; 4 | import lombok.Data; 5 | 6 | import java.util.Date; 7 | 8 | /** 9 | * @Auther: wdd 10 | * @Date: 2020-03-27 16:29 11 | * @Description: 12 | */ 13 | @Data 14 | public class OrderVo extends Order { 15 | 16 | private String productName; 17 | 18 | private String productPicture; 19 | 20 | private Long orderTime; 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/pojo/ProductPicture.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.pojo; 2 | 3 | import lombok.Data; 4 | 5 | import javax.persistence.*; 6 | 7 | @Data 8 | @Table(name = "product_picture") 9 | public class ProductPicture { 10 | 11 | @Id 12 | @GeneratedValue(strategy = GenerationType.IDENTITY,generator = "JDBC") 13 | private Integer id; 14 | 15 | private Integer productId; 16 | 17 | private String productPicture; 18 | 19 | private String intro; 20 | 21 | } -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/pojo/Carousel.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.pojo; 2 | 3 | import lombok.Data; 4 | 5 | import javax.persistence.*; 6 | 7 | @Data 8 | @Table(name = "carousel") 9 | public class Carousel { 10 | @Id 11 | @GeneratedValue(strategy = GenerationType.IDENTITY,generator = "JDBC") 12 | @Column(name = "carousel_id") 13 | private Integer carouselId; 14 | 15 | @Column(name = "img_path") 16 | private String imgPath; 17 | 18 | @Column(name = "describes") 19 | private String describes; 20 | 21 | } -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/mapper/CollectMapper.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.mapper; 2 | 3 | import com.mall.xiaomi.pojo.Collect; 4 | import com.mall.xiaomi.pojo.Product; 5 | import org.apache.ibatis.annotations.Select; 6 | import tk.mybatis.mapper.common.Mapper; 7 | 8 | import java.util.List; 9 | 10 | public interface CollectMapper extends Mapper { 11 | 12 | @Select("select product.* from product, collect where collect.user_id = #{userId} and collect.product_id = product.product_id") 13 | List getCollect(String userId); 14 | } -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/pojo/ShoppingCart.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.pojo; 2 | 3 | import lombok.Data; 4 | 5 | import javax.persistence.*; 6 | 7 | @Data 8 | @Table(name = "shopping_cart") 9 | public class ShoppingCart { 10 | @Id 11 | @GeneratedValue(strategy = GenerationType.IDENTITY, generator = "JDBC") 12 | private Integer id; 13 | 14 | private Integer userId; 15 | 16 | private Integer productId; 17 | 18 | private Integer num; 19 | 20 | private Integer version; // 版本号,用于乐观锁控制 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/pojo/Order.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.pojo; 2 | 3 | import lombok.Data; 4 | 5 | import javax.persistence.*; 6 | 7 | @Data 8 | @Table(name = "`order`") 9 | public class Order { 10 | @Id 11 | @GeneratedValue(strategy = GenerationType.IDENTITY,generator = "JDBC") 12 | private Integer id; 13 | 14 | private String orderId; 15 | 16 | private Integer userId; 17 | 18 | private Integer productId; 19 | 20 | private Integer productNum; 21 | 22 | private Double productPrice; 23 | 24 | private Long orderTime; 25 | 26 | } -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/vo/SeckillProductVo.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.vo; 2 | 3 | import com.mall.xiaomi.pojo.SeckillProduct; 4 | import lombok.Data; 5 | 6 | import java.io.Serializable; 7 | 8 | /** 9 | * @Auther: wdd 10 | * @Date: 2020-03-28 20:17 11 | * @Description: 12 | */ 13 | @Data 14 | public class SeckillProductVo extends SeckillProduct implements Serializable { 15 | 16 | private String productName; 17 | 18 | private Double productPrice; 19 | 20 | private String productPicture; 21 | 22 | private Long startTime; 23 | 24 | private Long endTime; 25 | 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/util/RedisKey.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.util; 2 | 3 | /** 4 | * @Auther: wdd 5 | * @Date: 2020/8/23 11:54 6 | * @Description: 7 | */ 8 | public class RedisKey { 9 | public final static String SECKILL_PRODUCT_LIST = "seckill:product:list:"; 10 | public final static String SECKILL_PRODUCT = "seckill:product:id:"; 11 | public final static String SECKILL_PRODUCT_STOCK = "seckill:product:stock:id:"; 12 | public final static String SECKILL_PRODUCT_USER_LIST = "seckill:product:user:list"; 13 | public final static String SECKILL_RABBITMQ_ID = "seckill:rabbitmq:id"; 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/mapper/ProductMapper.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.mapper; 2 | 3 | import com.mall.xiaomi.pojo.Product; 4 | import org.apache.ibatis.annotations.Param; 5 | import org.apache.ibatis.annotations.Select; 6 | import tk.mybatis.mapper.common.Mapper; 7 | 8 | import java.util.List; 9 | 10 | public interface ProductMapper extends Mapper { 11 | 12 | @Select("select product_id from product") 13 | List selectIds(); 14 | 15 | int updateStockByIdAndVersion(@Param("productId") Integer productId, @Param("saleNum") Integer saleNum, @Param("currentVersion") int currentVersion); 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/pojo/SeckillTime.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.pojo; 2 | 3 | import lombok.Data; 4 | 5 | import javax.persistence.GeneratedValue; 6 | import javax.persistence.GenerationType; 7 | import javax.persistence.Id; 8 | import javax.persistence.Table; 9 | 10 | /** 11 | * @Auther: wdd 12 | * @Date: 2020-04-22 20:57 13 | * @Description: 14 | */ 15 | @Data 16 | @Table(name = "seckill_time") 17 | public class SeckillTime { 18 | 19 | @Id 20 | @GeneratedValue(strategy = GenerationType.IDENTITY,generator = "JDBC") 21 | private Integer timeId; 22 | 23 | private Long startTime; 24 | 25 | private Long endTime; 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/vo/CartVo.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.vo; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @Auther: wdd 7 | * @Date: 2020-03-25 12:37 8 | * @Description: 9 | */ 10 | @Data 11 | public class CartVo { 12 | 13 | private Integer id; 14 | 15 | private Integer productId; 16 | 17 | private String productName; 18 | 19 | private String productImg; 20 | 21 | private Double price; 22 | 23 | private Integer num; 24 | 25 | private Integer maxNum; 26 | 27 | private boolean check; 28 | 29 | private boolean updateNum; 30 | 31 | private String updateMessage; 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/mapper/OrderMapper.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.mapper; 2 | 3 | import com.mall.xiaomi.pojo.Order; 4 | import com.mall.xiaomi.vo.OrderVo; 5 | import org.apache.ibatis.annotations.Select; 6 | import tk.mybatis.mapper.common.Mapper; 7 | 8 | import java.util.List; 9 | 10 | public interface OrderMapper extends Mapper { 11 | 12 | @Select("select `order`.*, `order`.order_time as orderTime ,product.product_name as productName, product.product_picture as productPicture " + 13 | "from `order`, product where `order`.product_id = product.product_id and `order`.user_id = #{userId} " + 14 | "ORDER BY order_time desc" 15 | ) 16 | List getOrderVoByUserId(Integer userId); 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/pojo/SeckillMessageRecord.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.pojo; 2 | 3 | import lombok.Data; 4 | 5 | import javax.persistence.Table; 6 | import java.io.Serializable; 7 | import java.util.Date; 8 | 9 | /** 10 | * seckill_message_record 11 | */ 12 | @Data 13 | @Table(name = "seckill_message_record") 14 | public class SeckillMessageRecord implements Serializable { 15 | private Long id; 16 | 17 | private String messageId; 18 | 19 | private String userId; 20 | 21 | private String seckillId; 22 | 23 | private String status; 24 | 25 | private Integer retryCount; 26 | 27 | private String errorMessage; 28 | 29 | private Date createdAt; 30 | 31 | private Date updatedAt; 32 | 33 | private static final long serialVersionUID = 1L; 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/pojo/SeckillProduct.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.pojo; 2 | 3 | import lombok.Data; 4 | 5 | import javax.persistence.GeneratedValue; 6 | import javax.persistence.GenerationType; 7 | import javax.persistence.Id; 8 | import javax.persistence.Table; 9 | import java.io.Serializable; 10 | 11 | /** 12 | * @Auther: wdd 13 | * @Date: 2020-03-28 19:40 14 | * @Description: 15 | */ 16 | @Data 17 | @Table(name = "seckill_product") 18 | public class SeckillProduct implements Serializable { 19 | 20 | @Id 21 | @GeneratedValue(strategy = GenerationType.IDENTITY,generator = "JDBC") 22 | private Integer seckillId; 23 | 24 | private Integer productId; 25 | 26 | private Double seckillPrice; 27 | 28 | private Integer seckillStock; 29 | 30 | private Integer timeId; 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/pojo/Product.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.pojo; 2 | 3 | import lombok.Data; 4 | 5 | import javax.persistence.*; 6 | 7 | @Data 8 | @Table(name = "product") 9 | public class Product { 10 | @Id 11 | @GeneratedValue(strategy = GenerationType.IDENTITY, generator = "JDBC") 12 | private Integer productId; 13 | 14 | private String productName; 15 | 16 | private Integer categoryId; 17 | 18 | private String productTitle; 19 | 20 | private String productPicture; 21 | 22 | private Double productPrice; 23 | 24 | private Double productSellingPrice; 25 | 26 | private Integer productNum; 27 | 28 | private Integer productSales; 29 | 30 | private String productIntro; 31 | 32 | private Integer version; 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/mapper/SeckillTimeMapper.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.mapper; 2 | 3 | import com.mall.xiaomi.pojo.SeckillTime; 4 | import com.mall.xiaomi.vo.SeckillProductVo; 5 | import org.apache.ibatis.annotations.Delete; 6 | import org.apache.ibatis.annotations.Select; 7 | import tk.mybatis.mapper.common.Mapper; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * @Auther: wdd 13 | * @Date: 2020-04-22 20:58 14 | * @Description: 15 | */ 16 | public interface SeckillTimeMapper extends Mapper { 17 | 18 | @Select("select * from seckill_time where end_time > #{time} limit 6") 19 | List getTime(long time); 20 | 21 | @Delete("delete from seckill_time") 22 | void deleteAll(); 23 | 24 | @Select("select end_time from seckill_time where time_id = #{timeId}") 25 | Long getEndTime(Integer timeId); 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/mapper/SeckillMessageRecordMapper.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.mapper; 2 | 3 | import com.mall.xiaomi.pojo.SeckillMessageRecord; 4 | import org.apache.ibatis.annotations.Param; 5 | import org.springframework.stereotype.Repository; 6 | 7 | @Repository 8 | public interface SeckillMessageRecordMapper { 9 | int deleteByPrimaryKey(Long id); 10 | 11 | int insert(SeckillMessageRecord record); 12 | 13 | int insertSelective(SeckillMessageRecord record); 14 | 15 | SeckillMessageRecord selectByPrimaryKey(Long id); 16 | 17 | int updateByPrimaryKeySelective(SeckillMessageRecord record); 18 | 19 | int updateByPrimaryKey(SeckillMessageRecord record); 20 | 21 | com.mall.xiaomi.pojo.SeckillMessageRecord findByMessageId(@Param("correlationId") String correlationId); 22 | 23 | int updateByMessageId(@Param("record") SeckillMessageRecord record); 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/exception/XmExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.exception; 2 | 3 | import com.mall.xiaomi.util.ResultMessage; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.web.bind.annotation.ControllerAdvice; 6 | import org.springframework.web.bind.annotation.ExceptionHandler; 7 | import org.springframework.web.bind.annotation.ResponseBody; 8 | 9 | /** 10 | * @Auther: wdd 11 | * @Date: 2020-03-19 16:45 12 | * @Description: 13 | */ 14 | @ControllerAdvice 15 | @ResponseBody 16 | public class XmExceptionHandler { 17 | 18 | @Autowired 19 | private ResultMessage resultMessage; 20 | 21 | @ExceptionHandler(XmException.class) 22 | public ResultMessage handleException(XmException e){ 23 | ExceptionEnum em = e.getExceptionEnum(); 24 | resultMessage.fail(em.getCode() + "", em.getMsg()); 25 | return resultMessage; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/controller/CarouselController.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.controller; 2 | 3 | import com.mall.xiaomi.pojo.Carousel; 4 | import com.mall.xiaomi.service.CarouselService; 5 | import com.mall.xiaomi.util.ResultMessage; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | import org.springframework.web.bind.annotation.RestController; 9 | 10 | import java.util.List; 11 | 12 | /** 13 | * @Auther: wdd 14 | * @Date: 2020-03-19 13:24 15 | * @Description: 16 | */ 17 | @RestController 18 | public class CarouselController { 19 | 20 | @Autowired 21 | private ResultMessage resultMessage; 22 | @Autowired 23 | private CarouselService carouselService; 24 | 25 | @GetMapping("/resources/carousel") 26 | public ResultMessage carousels() { 27 | List carousels = carouselService.getCarouselList(); 28 | resultMessage.success("001", carousels); 29 | return resultMessage; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 3000 3 | spring: 4 | datasource: 5 | name: xiaomi 6 | driver-class-name: com.mysql.cj.jdbc.Driver 7 | url: 8 | username: 9 | password: 10 | 11 | # Redis 配置 12 | redis: 13 | host: 14 | port: 15 | database: 16 | password: 17 | lettuce: 18 | pool: 19 | # 连接池中的最小空闲连接数 20 | min-idle: 0 21 | # 连接池中的最大空闲连接数 22 | max-idle: 8 23 | # 连接池的最大活跃连接数 24 | max-active: 8 25 | # 连接池最大阻塞等待时间,使用 PT 表示 Duration 格式 26 | max-wait: -1ms 27 | jackson: 28 | date-format: yyyy-MM-dd HH:mm:ss 29 | 30 | rabbitmq: 31 | host: localhost 32 | port: 5672 # 默认端口 33 | username: guest 34 | password: guest 35 | 36 | 37 | mybatis: 38 | mapper-locations: classpath:mybatis/*.xml 39 | configuration: 40 | mapUnderscoreToCamelCase: true 41 | log-impl: org.apache.ibatis.logging.stdout.StdOutImpl 42 | 43 | #logging: 44 | # level: 45 | # org.apache.ibatis: DEBUG 46 | # com.mall.xiaomi: DEBUG 47 | -------------------------------------------------------------------------------- /src/main/resources/mybatis/ShoppingCartMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | UPDATE shopping_cart 15 | SET num = #{cart.num}, 16 | version = version + 1 17 | WHERE id = #{cart.id} 18 | AND version = #{cart.version} 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/util/ResultMessage.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.util; 2 | 3 | import lombok.Data; 4 | import org.springframework.stereotype.Component; 5 | 6 | /** 7 | * @Auther: wdd 8 | * @Date: 2020-03-19 16:16 9 | * @Description: 10 | */ 11 | @Component 12 | @Data 13 | public class ResultMessage { 14 | 15 | private String code; 16 | private String msg; 17 | private Object data; 18 | 19 | public void success(String code, String msg, Object data) { 20 | this.code = code; 21 | this.msg = msg; 22 | this.data = data; 23 | } 24 | 25 | public void success(String code, String msg) { 26 | this.code = code; 27 | this.msg = msg; 28 | this.data = null; 29 | } 30 | 31 | public void success(String code, Object data) { 32 | this.code = code; 33 | this.msg = null; 34 | this.data = data; 35 | } 36 | 37 | public void fail(String code, String msg) { 38 | this.code = code; 39 | this.msg = msg; 40 | this.data = null; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/controller/CategoryController.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.controller; 2 | 3 | import com.mall.xiaomi.pojo.Category; 4 | import com.mall.xiaomi.service.CategoryService; 5 | import com.mall.xiaomi.util.ResultMessage; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RestController; 10 | 11 | import java.util.List; 12 | 13 | /** 14 | * @Auther: wdd 15 | * @Date: 2020-03-19 13:25 16 | * @Description: 17 | */ 18 | @RestController 19 | @RequestMapping("/category") 20 | public class CategoryController { 21 | 22 | @Autowired 23 | private ResultMessage resultMessage; 24 | @Autowired 25 | private CategoryService categoryService; 26 | 27 | @GetMapping("") 28 | public ResultMessage category() { 29 | List categories = categoryService.getAll(); 30 | resultMessage.success("001", categories); 31 | return resultMessage; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/service/CategoryService.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.service; 2 | 3 | import com.mall.xiaomi.exception.ExceptionEnum; 4 | import com.mall.xiaomi.exception.XmException; 5 | import com.mall.xiaomi.mapper.CategoryMapper; 6 | import com.mall.xiaomi.pojo.Category; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.stereotype.Service; 9 | 10 | import java.util.List; 11 | 12 | /** 13 | * @Auther: wdd 14 | * @Date: 2020-03-19 13:21 15 | * @Description: 16 | */ 17 | @Service 18 | public class CategoryService { 19 | 20 | @Autowired 21 | private CategoryMapper categoryMapper; 22 | 23 | public List getAll() { 24 | List categories = null; 25 | try { 26 | categories = categoryMapper.selectAll(); 27 | if (categories == null) { 28 | throw new XmException(ExceptionEnum.GET_CATEGORY_NOT_FOUND); 29 | } 30 | } catch (Exception e) { 31 | e.printStackTrace(); 32 | throw new XmException(ExceptionEnum.GET_CATEGORY_ERROR); 33 | } 34 | return categories; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/Application.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi; 2 | 3 | import com.mall.xiaomi.util.IdWorker; 4 | import org.apache.ibatis.session.Configuration; 5 | import org.apache.ibatis.session.SqlSessionFactory; 6 | import org.springframework.amqp.core.Queue; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.boot.SpringApplication; 9 | import org.springframework.boot.autoconfigure.SpringBootApplication; 10 | import org.springframework.context.annotation.Bean; 11 | import org.springframework.scheduling.annotation.EnableScheduling; 12 | import tk.mybatis.spring.annotation.MapperScan; 13 | 14 | import javax.annotation.PostConstruct; 15 | 16 | /** 17 | * @Auther: wdd 18 | * @Date: 2020-03-19 15:43 19 | * @Description: 20 | */ 21 | @SpringBootApplication 22 | @MapperScan("com.mall.xiaomi.mapper") 23 | @EnableScheduling 24 | public class Application { 25 | public static void main(String[] args) { 26 | SpringApplication.run(Application.class, args); 27 | } 28 | 29 | @Bean 30 | public IdWorker getIdWork() { 31 | return new IdWorker(); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/service/CarouselService.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.service; 2 | 3 | import com.mall.xiaomi.exception.ExceptionEnum; 4 | import com.mall.xiaomi.exception.XmException; 5 | import com.mall.xiaomi.mapper.CarouselMapper; 6 | import com.mall.xiaomi.pojo.Carousel; 7 | import org.apache.commons.lang3.ArrayUtils; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.stereotype.Service; 10 | 11 | import java.util.List; 12 | 13 | /** 14 | * @Auther: wdd 15 | * @Date: 2020-03-19 13:16 16 | * @Description: 17 | */ 18 | @Service 19 | public class CarouselService { 20 | 21 | @Autowired 22 | private CarouselMapper carouselMapper; 23 | 24 | public List getCarouselList() { 25 | List list = null; 26 | try { 27 | list = carouselMapper.selectAll(); 28 | if (ArrayUtils.isEmpty(list.toArray())) { 29 | throw new XmException(ExceptionEnum.GET_CAROUSEL_NOT_FOUND); 30 | } 31 | } catch (Exception e) { 32 | e.printStackTrace(); 33 | throw new XmException(ExceptionEnum.GET_CAROUSEL_ERROR); 34 | } 35 | return list; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/controller/ProductPictureController.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.controller; 2 | 3 | import com.mall.xiaomi.pojo.Product; 4 | import com.mall.xiaomi.pojo.ProductPicture; 5 | import com.mall.xiaomi.service.ProductPictureService; 6 | import com.mall.xiaomi.util.ResultMessage; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.web.bind.annotation.GetMapping; 9 | import org.springframework.web.bind.annotation.PathVariable; 10 | import org.springframework.web.bind.annotation.RequestMapping; 11 | import org.springframework.web.bind.annotation.RestController; 12 | 13 | import java.util.List; 14 | 15 | /** 16 | * @Auther: wdd 17 | * @Date: 2020-03-19 13:27 18 | * @Description: 19 | */ 20 | @RestController 21 | @RequestMapping("/productPicture") 22 | public class ProductPictureController { 23 | 24 | @Autowired 25 | private ResultMessage resultMessage; 26 | @Autowired 27 | private ProductPictureService productPictureService; 28 | 29 | @GetMapping("/product/{productId}") 30 | public ResultMessage productPicture(@PathVariable String productId) { 31 | List products = productPictureService.getProductPictureByProductId(productId); 32 | resultMessage.success("001", products); 33 | return resultMessage; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/service/ProductPictureService.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.service; 2 | 3 | import com.mall.xiaomi.exception.ExceptionEnum; 4 | import com.mall.xiaomi.exception.XmException; 5 | import com.mall.xiaomi.mapper.ProductPictureMapper; 6 | import com.mall.xiaomi.pojo.Product; 7 | import com.mall.xiaomi.pojo.ProductPicture; 8 | import org.apache.commons.lang3.ArrayUtils; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.stereotype.Service; 11 | 12 | import java.util.List; 13 | 14 | /** 15 | * @Auther: wdd 16 | * @Date: 2020-03-19 13:22 17 | * @Description: 18 | */ 19 | @Service 20 | public class ProductPictureService { 21 | 22 | @Autowired 23 | private ProductPictureMapper productPictureMapper; 24 | 25 | public List getProductPictureByProductId(String productId) { 26 | ProductPicture picture = new ProductPicture(); 27 | picture.setProductId(Integer.parseInt(productId)); 28 | List list = null; 29 | try { 30 | list = productPictureMapper.select(picture); 31 | if (ArrayUtils.isEmpty(list.toArray())) { 32 | throw new XmException(ExceptionEnum.GET_PRODUCT_PICTURE_NOT_FOUND); 33 | } 34 | } catch (Exception e) { 35 | e.printStackTrace(); 36 | throw new XmException(ExceptionEnum.GET_PRODUCT_PICTURE_ERROR); 37 | } 38 | return list; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/resources/mybatis/ProductMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | UPDATE product 21 | SET product_num = product_num - #{saleNum}, 22 | version = version + 1, 23 | product_sales = product_sales + #{saleNum} 24 | WHERE product_id = #{productId} 25 | AND product_num >= #{saleNum} 26 | AND version = #{currentVersion} 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/controller/OrderController.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.controller; 2 | 3 | import com.mall.xiaomi.pojo.Order; 4 | import com.mall.xiaomi.service.OrderService; 5 | import com.mall.xiaomi.util.ResultMessage; 6 | import com.mall.xiaomi.vo.CartVo; 7 | import com.mall.xiaomi.vo.OrderVo; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.data.redis.core.RedisTemplate; 10 | import org.springframework.web.bind.annotation.*; 11 | 12 | import java.util.List; 13 | 14 | /** 15 | * @Auther: wdd 16 | * @Date: 2020-03-19 13:25 17 | * @Description: 18 | */ 19 | @RestController 20 | @RequestMapping("/order") 21 | public class OrderController { 22 | 23 | @Autowired 24 | private ResultMessage resultMessage; 25 | @Autowired 26 | private RedisTemplate redisTemplate; 27 | @Autowired 28 | private OrderService orderService; 29 | 30 | @PostMapping("") 31 | public ResultMessage addOrder(@RequestBody List cartVoList, @CookieValue("XM_TOKEN") String cookie) { 32 | // 先判断cookie是否存在,和redis校验 33 | Integer userId = (Integer) redisTemplate.opsForHash().get(cookie, "userId"); 34 | orderService.addOrder(cartVoList, userId); 35 | resultMessage.success("001", "下单成功"); 36 | return resultMessage; 37 | } 38 | 39 | @GetMapping("") 40 | public ResultMessage getOrder(@CookieValue("XM_TOKEN") String cookie) { 41 | // 先判断cookie是否存在,和redis校验 42 | Integer userId = (Integer) redisTemplate.opsForHash().get(cookie, "userId"); 43 | List> orders = orderService.getOrder(userId); 44 | resultMessage.success("001", orders); 45 | return resultMessage; 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/exception/ExceptionEnum.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.exception; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * @Auther: wdd 9 | * @Date: 2020-03-19 16:41 10 | * @Description: 11 | */ 12 | @Getter 13 | @NoArgsConstructor 14 | @AllArgsConstructor 15 | public enum ExceptionEnum { 16 | 17 | GET_CAROUSEL_ERROR(002, "轮播图查询失败"), 18 | GET_CAROUSEL_NOT_FOUND(002, "轮播图为空"), 19 | 20 | GET_PRODUCT_ERROR(002, "商品查询失败"), 21 | GET_PRODUCT_NOT_FOUND(002, "商品为空"), 22 | 23 | 24 | GET_PRODUCT_PICTURE_ERROR(002, "商品图片查询失败"), 25 | GET_PRODUCT_PICTURE_NOT_FOUND(002, "商品图片为空"), 26 | 27 | 28 | GET_CATEGORY_ERROR(002, "分类查询异常"), 29 | GET_CATEGORY_NOT_FOUND(002, "分类查询为空"), 30 | 31 | GET_USER_NOT_FOUND(002, "用户名或密码错误"), 32 | SAVE_USER_REUSE(002, "用户名已存在"), 33 | SAVE_USER_ERROR(002, "注册用户失败"), 34 | 35 | 36 | 37 | GET_CART_ERROR(002, "购物车异常"), 38 | GET_CART_NOT_FOUND(002, "购物车为空"), 39 | 40 | 41 | SAVE_COLLECT_ERROR(002, "收藏失败"), 42 | SAVE_COLLECT_REUSE(002, "已收藏,请勿重复收藏"), 43 | GET_COLLECT_NOT_FOUND(002, "尚无商品收藏"), 44 | GET_COLLECT_ERROR(002, "获取收藏失败"), 45 | DELETE_COLLECT_ERROR(002, "删除收藏失败"), 46 | 47 | 48 | 49 | ADD_CART_NUM_UPPER(003, "该商品购物车达到上限"), 50 | UPDATE_CART_ERROR(003, "商品数量修改失败"), 51 | DELETE_CART_ERROR(003, "商品删除失败"), 52 | 53 | 54 | ADD_ORDER_ERROR(002, "生成订单失败"), 55 | GET_ORDER_NOT_FOUND(002, "订单为空"), 56 | GET_ORDER_ERROR(002, "查询订单失败"), 57 | 58 | GET_SECKILL_NOT_FOUND(002, "尚无秒杀商品"), 59 | GET_SECKILL_IS_OVER(002, "秒杀商品售罄"), 60 | GET_SECKILL_IS_REUSE(002, "秒杀重复"), 61 | GET_SECKILL_IS_NOT_START(002, "秒杀尚未开始"), 62 | 63 | ; 64 | private int code; 65 | private String msg; 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/mapper/SeckillProductMapper.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.mapper; 2 | 3 | import com.mall.xiaomi.pojo.SeckillProduct; 4 | import com.mall.xiaomi.vo.SeckillProductVo; 5 | import org.apache.ibatis.annotations.Delete; 6 | import org.apache.ibatis.annotations.Select; 7 | import org.apache.ibatis.annotations.Update; 8 | import tk.mybatis.mapper.common.Mapper; 9 | import tk.mybatis.mapper.common.MySqlMapper; 10 | 11 | import java.util.List; 12 | 13 | /** 14 | * @Auther: wdd 15 | * @Date: 2020-03-28 20:10 16 | * @Description: 17 | */ 18 | public interface SeckillProductMapper extends Mapper, MySqlMapper { 19 | 20 | @Select("select seckill_time.start_time, seckill_time.end_time, seckill_product.*, product.product_name, product.product_price, product.product_picture from seckill_product ,product, seckill_time where seckill_product.time_id = seckill_time.time_id and seckill_product.product_id = product.product_id and seckill_product.time_id = #{timeId} and seckill_time.end_time > #{time}") 21 | List getSeckillProductVos(String timeId, Long time); 22 | 23 | @Select("select seckill_time.start_time, seckill_time.end_time, seckill_product.*, product.product_name, product.product_price, product.product_picture from seckill_product ,product, seckill_time where seckill_product.time_id = seckill_time.time_id and seckill_product.product_id = product.product_id and seckill_product.seckill_id = #{seckillId}") 24 | SeckillProductVo getSeckill(String seckillId); 25 | 26 | @Update("update seckill_product set seckill_stock = seckill_stock - 1 where seckill_id = #{seckillId} and seckill_stock > 0") 27 | void decrStock(Integer seckillId); 28 | 29 | @Delete("delete from seckill_product") 30 | void deleteAll(); 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/util/MD5Util.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.util; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | import java.security.MessageDigest; 6 | 7 | public class MD5Util { 8 | private static String byteArrayToHexString(byte b[]) { 9 | StringBuffer resultSb = new StringBuffer(); 10 | for (int i = 0; i < b.length; i++) 11 | resultSb.append(byteToHexString(b[i])); 12 | 13 | return resultSb.toString(); 14 | } 15 | 16 | private static String byteToHexString(byte b) { 17 | int n = b; 18 | if (n < 0) 19 | n += 256; 20 | int d1 = n / 16; 21 | int d2 = n % 16; 22 | return hexDigits[d1] + hexDigits[d2]; 23 | } 24 | 25 | public static String MD5Encode(String origin, String charsetname) { 26 | String resultString = null; 27 | try { 28 | resultString = new String(origin); 29 | MessageDigest md = MessageDigest.getInstance("MD5"); 30 | if (charsetname == null || "".equals(charsetname)) 31 | resultString = byteArrayToHexString(md.digest(resultString 32 | .getBytes())); 33 | else 34 | resultString = byteArrayToHexString(md.digest(resultString 35 | .getBytes(charsetname))); 36 | } catch (Exception exception) { 37 | } 38 | return resultString; 39 | } 40 | 41 | private static final String hexDigits[] = {"0", "1", "2", "3", "4", "5", 42 | "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"}; 43 | 44 | 45 | public static void main(String[] args) { 46 | MD5Util md5Util = new MD5Util(); 47 | String s = md5Util.MD5Encode("123", "UTF-8"); 48 | System.out.println(s);//202cb962ac59075b964b07152d234b70 49 | } 50 | } -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/controller/CollectController.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.controller; 2 | 3 | import com.mall.xiaomi.pojo.Product; 4 | import com.mall.xiaomi.service.CollectService; 5 | import com.mall.xiaomi.util.ResultMessage; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.web.bind.annotation.*; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * @Auther: wdd 13 | * @Date: 2020-03-19 13:25 14 | * @Description: 15 | */ 16 | @RestController 17 | @RequestMapping("/collect") 18 | public class CollectController { 19 | 20 | @Autowired 21 | private ResultMessage resultMessage; 22 | @Autowired 23 | private CollectService collectService; 24 | 25 | /** 26 | * 将商品收藏 27 | * @param userId 28 | * @param productId 29 | * @return 30 | */ 31 | @PostMapping("/user/{productId}/{userId}") 32 | public ResultMessage addCollect(@PathVariable String userId, @PathVariable String productId) { 33 | collectService.addCollect(userId, productId); 34 | resultMessage.success("001", "商品收藏成功"); 35 | return resultMessage; 36 | } 37 | 38 | /** 39 | * 获取用户收藏 40 | * @param userId 41 | * @return 返回商品集合 42 | */ 43 | @GetMapping("/user/{userId}") 44 | public ResultMessage getCollect(@PathVariable String userId) { 45 | List collects = collectService.getCollect(userId); 46 | resultMessage.success("001", collects); 47 | return resultMessage; 48 | } 49 | 50 | @DeleteMapping("/user/{productId}/{userId}") 51 | public ResultMessage deleteCollect(@PathVariable String productId, @PathVariable String userId) { 52 | collectService.deleteCollect(userId, productId); 53 | resultMessage.success("001", "删除收藏成功"); 54 | return resultMessage; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/util/BeanUtil.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.util; 2 | 3 | import java.beans.BeanInfo; 4 | import java.beans.Introspector; 5 | import java.beans.PropertyDescriptor; 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | public class BeanUtil { 10 | 11 | /** 12 | * 将JavaBean对象封装到Map集合当中 13 | * @param bean 14 | * @return 15 | * @throws Exception 16 | */ 17 | public static Map bean2map(Object bean) throws Exception 18 | { 19 | //创建Map集合对象 20 | Map map=new HashMap(); 21 | //获取对象字节码信息,不要Object的属性 22 | BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass(),Object.class); 23 | //获取bean对象中的所有属性 24 | PropertyDescriptor[] list = beanInfo.getPropertyDescriptors(); 25 | for (PropertyDescriptor pd : list) { 26 | String key = pd.getName();//获取属性名 27 | Object value = pd.getReadMethod().invoke(bean);//调用getter()方法,获取内容 28 | map.put(key, value);//增加到map集合当中 29 | } 30 | return map; 31 | } 32 | 33 | 34 | /** 35 | * 将Map集合中的数据封装到JavaBean对象中 36 | * @param map 集合 37 | * @param classType 封装javabean对象 38 | * @throws Exception 39 | */ 40 | public static T map2bean(Map map,Class classType) throws Exception 41 | { 42 | //采用反射动态创建对象 43 | T obj = classType.newInstance(); 44 | //获取对象字节码信息,不要Object的属性 45 | BeanInfo beanInfo = Introspector.getBeanInfo(classType,Object.class); 46 | //获取bean对象中的所有属性 47 | PropertyDescriptor[] list = beanInfo.getPropertyDescriptors(); 48 | for (PropertyDescriptor pd : list) { 49 | String key = pd.getName(); //获取属性名 50 | Object value=map.get(key); //获取属性值 51 | pd.getWriteMethod().invoke(obj, value);//调用属性setter()方法,设置到javabean对象当中 52 | } 53 | return obj; 54 | } 55 | } -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/service/UserService.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.service; 2 | 3 | import com.mall.xiaomi.exception.ExceptionEnum; 4 | import com.mall.xiaomi.exception.XmException; 5 | import com.mall.xiaomi.mapper.UserMapper; 6 | import com.mall.xiaomi.pojo.User; 7 | import com.mall.xiaomi.util.MD5Util; 8 | import org.apache.commons.lang3.StringUtils; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.stereotype.Service; 11 | 12 | /** 13 | * @Auther: wdd 14 | * @Date: 2020-03-19 13:23 15 | * @Description: 16 | */ 17 | @Service 18 | public class UserService { 19 | 20 | @Autowired 21 | private UserMapper userMapper; 22 | 23 | public User login(User user) { 24 | user.setPassword(MD5Util.MD5Encode(user.getPassword() + "", "UTF-8")); 25 | User one = userMapper.selectOne(user); 26 | if (one == null) { 27 | throw new XmException(ExceptionEnum.GET_USER_NOT_FOUND); 28 | } 29 | return one; 30 | } 31 | 32 | public void register(User user) { 33 | User one = new User(); 34 | one.setUsername(user.getUsername()); 35 | // 先去看看用户名是否重复 36 | if (userMapper.selectCount(one) == 1) { 37 | // 用户名已存在 38 | throw new XmException(ExceptionEnum.SAVE_USER_REUSE); 39 | } 40 | // 使用md5对密码进行加密 41 | user.setPassword(MD5Util.MD5Encode(user.getPassword() + "", "UTF-8")); 42 | // 存入数据库 43 | try { 44 | userMapper.insert(user); 45 | } catch (Exception e) { 46 | e.printStackTrace(); 47 | throw new XmException(ExceptionEnum.SAVE_USER_ERROR); 48 | } 49 | } 50 | 51 | public void isUserName(String username) { 52 | User one = new User(); 53 | one.setUsername(username); 54 | // 先去看看用户名是否重复 55 | if (userMapper.selectCount(one) == 1) { 56 | // 用户名已存在 57 | throw new XmException(ExceptionEnum.SAVE_USER_REUSE); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/config/RedisConfig.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.config; 2 | 3 | import com.fasterxml.jackson.annotation.JsonAutoDetect; 4 | import com.fasterxml.jackson.annotation.PropertyAccessor; 5 | import com.fasterxml.jackson.databind.ObjectMapper; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.data.redis.connection.RedisConnectionFactory; 9 | import org.springframework.data.redis.core.RedisTemplate; 10 | import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; 11 | import org.springframework.data.redis.serializer.StringRedisSerializer; 12 | 13 | @Configuration 14 | public class RedisConfig { 15 | 16 | @Bean 17 | public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) { 18 | RedisTemplate template = new RedisTemplate<>(); 19 | template.setConnectionFactory(redisConnectionFactory); 20 | 21 | // 使用 Jackson2JsonRedisSerializer 来序列化和反序列化 redis 的 value 值 22 | Jackson2JsonRedisSerializer jacksonSerializer = new Jackson2JsonRedisSerializer<>(Object.class); 23 | ObjectMapper objectMapper = new ObjectMapper(); 24 | objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); 25 | objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); 26 | jacksonSerializer.setObjectMapper(objectMapper); 27 | 28 | // 设置 key 和 value 的序列化规则 29 | template.setKeySerializer(new StringRedisSerializer()); // key 使用 String 序列化器 30 | template.setValueSerializer(jacksonSerializer); // value 使用 Jackson 序列化器 31 | 32 | // 对 hash 的 key 和 value 设置序列化规则 33 | template.setHashKeySerializer(new StringRedisSerializer()); 34 | template.setHashValueSerializer(jacksonSerializer); 35 | 36 | template.afterPropertiesSet(); 37 | return template; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/controller/ProductController.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.controller; 2 | 3 | import com.github.pagehelper.PageInfo; 4 | import com.mall.xiaomi.pojo.Product; 5 | import com.mall.xiaomi.service.ProductService; 6 | import com.mall.xiaomi.util.ResultMessage; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.web.bind.annotation.*; 9 | 10 | import java.util.HashMap; 11 | import java.util.List; 12 | import java.util.Map; 13 | 14 | /** 15 | * @Auther: wdd 16 | * @Date: 2020-03-19 13:26 17 | * @Description: 18 | */ 19 | @RestController 20 | @RequestMapping("/product") 21 | public class ProductController { 22 | 23 | @Autowired 24 | private ResultMessage resultMessage; 25 | @Autowired 26 | private ProductService productService; 27 | 28 | @GetMapping("/category/limit/{categoryId}") 29 | public ResultMessage getProductByCategoryId(@PathVariable Integer categoryId) { 30 | List list = productService.getProductByCategoryId(categoryId); 31 | resultMessage.success("001", list); 32 | return resultMessage; 33 | 34 | } 35 | 36 | @GetMapping("/category/hot") 37 | public ResultMessage getHotProduct() { 38 | List list = productService.getHotProduct(); 39 | resultMessage.success("001", list); 40 | return resultMessage; 41 | 42 | } 43 | 44 | @GetMapping("/{productId}") 45 | public ResultMessage getProduct(@PathVariable String productId) { 46 | Product product = productService.getProductById(productId); 47 | resultMessage.success("001", product); 48 | return resultMessage; 49 | } 50 | 51 | @GetMapping("/page/{currentPage}/{pageSize}/{categoryId}") 52 | public Map getProductByPage(@PathVariable String currentPage, @PathVariable String pageSize, @PathVariable String categoryId) { 53 | PageInfo pageInfo = productService.getProductByPage(currentPage, pageSize, categoryId); 54 | HashMap map = new HashMap<>(); 55 | map.put("code", "001"); 56 | map.put("data", pageInfo.getList()); 57 | map.put("total", pageInfo.getTotal()); 58 | return map; 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 仿小米商城 2 | 3 | ## 前言 4 | 5 | 本项目是基于GitHub上一个作者[hai-27](https://github.com/hai-27)开发的补充。他是使用vue、node.js做的前后端分离项目。 6 | 7 | 我是对他的前端进行采用并进行修改,后端使用SpringBoot框架实现的一个仿小米商城。 8 | 9 | ### 感谢 [hai-27](https://github.com/hai-27) 的开源 [vue-store](https://github.com/hai-27/vue-store) 项目提供前端页面及框架支持 10 | 11 | 12 | ## 项目简介 13 | 14 | 本项目前后端分离,前端基于`Vue`+`Vue-router`+`Vuex`+`Element-ui`+`Axios`,参考小米商城实现。后端基于`SpringBoot` +`Redis`+ `RabbitMQ` + `MySQL`实现。 15 | 16 | 实现了**用户注册与登录**,**商城首页展示**,**商品分类展示**,**商品详情页**,**购物车**,**订单结算**,**我的收藏**等功能。 17 | 18 | 并在原作者的基础上添加了**商品秒杀**部分。 19 | 20 | **后端接口全部采用Resultful风格,因此前端接口以及部分内容也有修改。** 21 | 22 | 前端项目地址:https://github.com/ZeroWdd/vue-store 23 | 24 | 后端项目地址:https://github.com/ZeroWdd/Xiaomi 25 | 26 | ## 项目已部署 27 | 28 | 仿小米商城: http://47.95.254.97:8080/ 29 | 30 | ## 技术栈 31 | 32 | - **前端:**`Vue`+`Vue-router`+`Vuex`+`Element-ui`+`Axios` 33 | - **后端:**`SpringBoot` +`Redis`+ `RabbitMQ` 34 | - **数据库:**`Mysql` 35 | 36 | ## 功能实现 37 | 38 | - [x] 用户注册与登录 39 | - [x] 商品首页展示 40 | - [x] 商品分类列表展示 41 | - [x] 商品详情页 42 | - [x] 购物车 43 | - [x] 订单结算 44 | - [x] 我的收藏 45 | - [ ] 我的地址 46 | - [x] 秒杀商品 47 | - [ ] 商品支付 48 | 49 | ## 运行项目 50 | 51 | **前端运行** 52 | 53 | ``` 54 | 1. Clone project 55 | 56 | git clone https://github.com/ZeroWdd/vue-store.git 57 | 58 | 2. Project setup 59 | 60 | cd vue-store 61 | npm install 62 | 63 | 3. Compiles and hot-reloads for development 64 | 65 | npm run serve 66 | 67 | 4. Compiles and minifies for production 68 | 69 | npm run build 70 | ``` 71 | **后端运行** 72 | 73 | ``` 74 | 1. 修改application.yml文件中的mysql、redis、rabbitmq的地址 75 | 2. 运行SpringBoot项目 76 | ``` 77 | 78 | ## 页面截图 79 | 80 | **首页** 81 | 82 | ![](https://images.gitee.com/uploads/images/2020/0317/154615_cffbacfe_6502229.png "home.png") 83 | 84 | **全部商品** 85 | 86 | ![](https://images.gitee.com/uploads/images/2020/0317/154637_fa50ca7a_6502229.png "goods.png") 87 | 88 | **购物车** 89 | 90 | ![](https://images.gitee.com/uploads/images/2020/0317/154737_f0417e36_6502229.gif "shoppingCart.gif") 91 | 92 | **我的收藏** 93 | 94 | ![](https://images.gitee.com/uploads/images/2020/0317/154717_e2baa55c_6502229.png "collect.png") 95 | 96 | **我的订单** 97 | 98 | ![](https://images.gitee.com/uploads/images/2020/0317/154756_5813ae89_6502229.png "order.png") 99 | 100 | **登录** 101 | 102 | ![](https://images.gitee.com/uploads/images/2020/0317/154814_27bcc9f1_6502229.png "login.png") 103 | 104 | **注册** 105 | 106 | ![](https://images.gitee.com/uploads/images/2020/0317/154827_2399157d_6502229.png "register.png") 107 | 108 | 109 | -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/service/CollectService.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.service; 2 | 3 | import com.mall.xiaomi.exception.ExceptionEnum; 4 | import com.mall.xiaomi.exception.XmException; 5 | import com.mall.xiaomi.mapper.CollectMapper; 6 | import com.mall.xiaomi.pojo.Collect; 7 | import com.mall.xiaomi.pojo.Product; 8 | import org.apache.commons.lang3.ArrayUtils; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.stereotype.Service; 11 | import org.springframework.transaction.annotation.Transactional; 12 | 13 | import java.util.Date; 14 | import java.util.List; 15 | 16 | 17 | /** 18 | * @Auther: wdd 19 | * @Date: 2020-03-19 13:21 20 | * @Description: 21 | */ 22 | @Service 23 | public class CollectService { 24 | 25 | @Autowired 26 | private CollectMapper collectMapper; 27 | 28 | @Transactional 29 | public void addCollect(String userId, String productId) { 30 | Collect collect = new Collect(); 31 | collect.setUserId(Integer.parseInt(userId)); 32 | collect.setProductId(Integer.parseInt(productId)); 33 | // 先看看是否数据库中已存在 34 | Collect one = collectMapper.selectOne(collect); 35 | if (one != null) { 36 | throw new XmException(ExceptionEnum.SAVE_COLLECT_REUSE); 37 | } 38 | // 不存在,添加收藏 39 | collect.setCollectTime(new Date().getTime()); 40 | int count = collectMapper.insert(collect); 41 | if (count != 1) { 42 | throw new XmException(ExceptionEnum.SAVE_COLLECT_ERROR); 43 | } 44 | } 45 | 46 | public List getCollect(String userId) { 47 | List list = null; 48 | try { 49 | list = collectMapper.getCollect(userId); 50 | if (ArrayUtils.isEmpty(list.toArray())) { 51 | throw new XmException(ExceptionEnum.GET_COLLECT_NOT_FOUND); 52 | } 53 | } catch (XmException e) { 54 | e.printStackTrace(); 55 | throw new XmException(ExceptionEnum.GET_COLLECT_ERROR); 56 | } 57 | return list; 58 | } 59 | 60 | public void deleteCollect(String userId, String productId) { 61 | Collect collect = new Collect(); 62 | collect.setUserId(Integer.parseInt(userId)); 63 | collect.setProductId(Integer.parseInt(productId)); 64 | try { 65 | int count = collectMapper.delete(collect); 66 | if (count != 1) { 67 | throw new XmException(ExceptionEnum.DELETE_COLLECT_ERROR); 68 | } 69 | } catch (XmException e) { 70 | e.printStackTrace(); 71 | throw new XmException(ExceptionEnum.DELETE_COLLECT_ERROR); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/controller/ShoppingCartController.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.controller; 2 | 3 | import com.mall.xiaomi.pojo.ShoppingCart; 4 | import com.mall.xiaomi.service.ShoppingCartService; 5 | import com.mall.xiaomi.util.ResultMessage; 6 | import com.mall.xiaomi.vo.CartVo; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.web.bind.annotation.*; 9 | 10 | import java.util.List; 11 | 12 | /** 13 | * @Auther: wdd 14 | * @Date: 2020-03-19 13:27 15 | * @Description: 16 | */ 17 | @RestController 18 | @RequestMapping("/cart") 19 | public class ShoppingCartController { 20 | 21 | @Autowired 22 | private ResultMessage resultMessage; 23 | @Autowired 24 | private ShoppingCartService cartService; 25 | 26 | /** 27 | * 获取购物车信息 28 | * 29 | * @param userId 30 | * @return 31 | */ 32 | @GetMapping("/user/{userId}") 33 | public ResultMessage cart(@PathVariable String userId) { 34 | List carts = cartService.getCartByUserId(userId); 35 | resultMessage.success("001", carts); 36 | return resultMessage; 37 | } 38 | 39 | /** 40 | * 添加购物车 41 | * 42 | * @param productId 43 | * @param userId 44 | * @return 45 | */ 46 | @PostMapping("/product/user/{productId}/{userId}") 47 | public ResultMessage cart(@PathVariable String productId, @PathVariable String userId) { 48 | CartVo cartVo = cartService.addCart(productId, userId); 49 | 50 | if (cartVo != null) { 51 | 52 | if (cartVo.isUpdateNum()) { 53 | resultMessage.success("002", "添加购物车成功", cartVo.getUpdateMessage()); 54 | } 55 | resultMessage.success("001", "添加购物车成功", cartVo); 56 | } else { 57 | resultMessage.success("002", "该商品已经在购物车,数量+1"); 58 | } 59 | return resultMessage; 60 | } 61 | 62 | @PutMapping("/user/num/{cartId}/{userId}/{num}") 63 | public ResultMessage cart(@PathVariable String cartId, @PathVariable String userId, @PathVariable String num) { 64 | cartService.updateCartNum(cartId, userId, num); 65 | resultMessage.success("001", "更新成功"); 66 | return resultMessage; 67 | } 68 | 69 | @DeleteMapping("/user/{cartId}/{userId}") 70 | public ResultMessage deleteCart(@PathVariable String cartId, @PathVariable String userId) { 71 | cartService.deleteCart(cartId, userId); 72 | resultMessage.success("001", "删除成功"); 73 | return resultMessage; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/controller/SeckillProductController.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.controller; 2 | 3 | import com.github.pagehelper.PageInfo; 4 | import com.mall.xiaomi.pojo.Product; 5 | import com.mall.xiaomi.pojo.SeckillProduct; 6 | import com.mall.xiaomi.pojo.SeckillTime; 7 | import com.mall.xiaomi.service.SeckillProductService; 8 | import com.mall.xiaomi.util.ResultMessage; 9 | import com.mall.xiaomi.vo.SeckillProductVo; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.data.redis.core.RedisTemplate; 12 | import org.springframework.web.bind.annotation.*; 13 | 14 | import java.util.HashMap; 15 | import java.util.List; 16 | import java.util.Map; 17 | 18 | /** 19 | * @Auther: wdd 20 | * @Date: 2020-03-28 19:58 21 | * @Description: 22 | */ 23 | @RestController 24 | @RequestMapping("/seckill/product") 25 | public class SeckillProductController { 26 | 27 | @Autowired 28 | private ResultMessage resultMessage; 29 | @Autowired 30 | private SeckillProductService seckillProductService; 31 | @Autowired 32 | private RedisTemplate redisTemplate; 33 | 34 | /** 35 | * 根据时间id获取对应时间的秒杀商品列表 36 | * @param timeId 37 | * @return 38 | */ 39 | @GetMapping("/time/{timeId}") 40 | public ResultMessage getProduct(@PathVariable String timeId) { 41 | List seckillProductVos = seckillProductService.getProduct(timeId); 42 | resultMessage.success("001", seckillProductVos); 43 | return resultMessage; 44 | } 45 | 46 | /** 47 | * 获取秒杀商品 48 | * @param seckillId 49 | * @return 50 | */ 51 | @GetMapping("/{seckillId}") 52 | public ResultMessage getSeckill(@PathVariable String seckillId) { 53 | SeckillProductVo seckillProductVo = seckillProductService.getSeckill(seckillId); 54 | resultMessage.success("001", seckillProductVo); 55 | return resultMessage; 56 | } 57 | 58 | /** 59 | * 获取时间段 60 | * @return 61 | */ 62 | @GetMapping("/time") 63 | public ResultMessage getTime() { 64 | List seckillTimes = seckillProductService.getTime(); 65 | resultMessage.success("001", seckillTimes); 66 | return resultMessage; 67 | } 68 | 69 | /** 70 | * 添加秒杀商品 71 | * @param seckillProduct 72 | * @return 73 | */ 74 | @PostMapping("") 75 | public ResultMessage addSeckillProduct(@RequestBody SeckillProduct seckillProduct) { 76 | seckillProductService.addSeckillProduct(seckillProduct); 77 | resultMessage.success("001", "添加成功"); 78 | return resultMessage; 79 | } 80 | 81 | /** 82 | * 开始秒杀 83 | * @param seckillId 84 | * @return 85 | */ 86 | @PostMapping("/seckill/{seckillId}") 87 | public ResultMessage seckillProduct(@PathVariable String seckillId, @CookieValue("XM_TOKEN") String cookie) { 88 | // 先判断cookie是否存在,和redis校验 89 | Integer userId = (Integer) redisTemplate.opsForHash().get(cookie, "userId"); 90 | seckillProductService.seckillProduct(seckillId, userId); 91 | resultMessage.success("001", "排队中"); 92 | return resultMessage; 93 | } 94 | 95 | 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/config/RabbitMQConfig.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.config; 2 | 3 | import org.springframework.amqp.core.*; 4 | import org.springframework.amqp.rabbit.config.RetryInterceptorBuilder; 5 | import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory; 6 | import org.springframework.amqp.rabbit.connection.ConnectionFactory; 7 | import org.springframework.amqp.rabbit.core.RabbitTemplate; 8 | import org.springframework.amqp.rabbit.retry.RejectAndDontRequeueRecoverer; 9 | import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; 10 | import org.springframework.context.annotation.Bean; 11 | import org.springframework.context.annotation.Configuration; 12 | import org.springframework.retry.interceptor.RetryOperationsInterceptor; 13 | 14 | @Configuration 15 | public class RabbitMQConfig { 16 | 17 | 18 | 19 | @Bean 20 | public Queue queue() { 21 | return QueueBuilder.durable("seckill_order") 22 | .withArgument("x-dead-letter-exchange", "seckill_dead_letter_exchange") // 配置死信交换机 23 | .withArgument("x-dead-letter-routing-key", "seckill_dead_letter_key") // 配置死信路由键 24 | .build(); 25 | } 26 | 27 | // 声明死信队列 28 | @Bean 29 | public Queue deadLetterQueue() { 30 | return QueueBuilder.durable("seckill_dead_letter_queue").build(); 31 | } 32 | 33 | // 声明死信交换机 34 | @Bean 35 | public DirectExchange deadLetterExchange() { 36 | return new DirectExchange("seckill_dead_letter_exchange"); 37 | } 38 | 39 | // 绑定死信队列和死信交换机 40 | @Bean 41 | public Binding deadLetterBinding() { 42 | return BindingBuilder.bind(deadLetterQueue()) 43 | .to(deadLetterExchange()) 44 | .with("seckill_dead_letter_key"); 45 | } 46 | 47 | @Bean 48 | public RetryOperationsInterceptor retryOperationsInterceptor() { 49 | return RetryInterceptorBuilder.stateless() 50 | .maxAttempts(3) // 设置最大重试次数 51 | .backOffOptions(1000, 2.0, 10000) // 初始延迟、乘数和最大延迟 52 | .recoverer(new RejectAndDontRequeueRecoverer()) // 达到最大重试次数后的处理逻辑 53 | .build(); 54 | } 55 | 56 | 57 | 58 | @Bean 59 | public Jackson2JsonMessageConverter jsonMessageConverter() { 60 | return new Jackson2JsonMessageConverter(); 61 | } 62 | 63 | @Bean 64 | public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory, Jackson2JsonMessageConverter jsonMessageConverter) { 65 | RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory); 66 | rabbitTemplate.setMessageConverter(jsonMessageConverter); 67 | return rabbitTemplate; 68 | } 69 | 70 | @Bean 71 | public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(ConnectionFactory connectionFactory, Jackson2JsonMessageConverter jsonMessageConverter) { 72 | SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory(); 73 | factory.setConnectionFactory(connectionFactory); 74 | factory.setMessageConverter(jsonMessageConverter); 75 | factory.setAdviceChain(retryOperationsInterceptor()); 76 | factory.setAcknowledgeMode(AcknowledgeMode.MANUAL); // 手动确认 77 | return factory; 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/task/SeckillTask.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.task; 2 | 3 | import com.mall.xiaomi.mapper.ProductMapper; 4 | import com.mall.xiaomi.mapper.SeckillProductMapper; 5 | import com.mall.xiaomi.mapper.SeckillTimeMapper; 6 | import com.mall.xiaomi.pojo.Product; 7 | import com.mall.xiaomi.pojo.SeckillProduct; 8 | import com.mall.xiaomi.pojo.SeckillTime; 9 | import com.mall.xiaomi.service.SeckillProductService; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.scheduling.annotation.Scheduled; 12 | import org.springframework.stereotype.Component; 13 | 14 | import java.util.*; 15 | 16 | /** 17 | * @Auther: wdd 18 | * @Date: 2020-04-24 11:53 19 | * @Description: 20 | */ 21 | @Component 22 | public class SeckillTask { 23 | 24 | @Autowired 25 | private SeckillTimeMapper seckillTimeMapper; 26 | @Autowired 27 | private ProductMapper productMapper; 28 | @Autowired 29 | private SeckillProductMapper seckillProductMapper; 30 | 31 | @Scheduled(cron = "0 0 15 * * ?") 32 | public void execute() { 33 | // 获取商品设为秒杀商品 34 | List productIds = productMapper.selectIds(); 35 | Date time = getDate(); 36 | seckillTimeMapper.deleteAll(); 37 | seckillProductMapper.deleteAll(); 38 | for (int i = 1; i < 24; i = i + 2) { 39 | 40 | // 插入时间 41 | long startTime = time.getTime()/1000*1000 + 1000 * 60 * 60 * i; 42 | long endTime = startTime + 1000 * 60 * 60; 43 | SeckillTime seckillTime = new SeckillTime(); 44 | seckillTime.setStartTime(startTime); 45 | seckillTime.setEndTime(endTime); 46 | seckillTimeMapper.insert(seckillTime); 47 | 48 | 49 | // 随机选15个商品id 50 | HashSet set = new HashSet<>(); 51 | while (set.size() < 15) { 52 | Random random = new Random(); 53 | int nextInt = random.nextInt(productIds.size()); 54 | set.add(productIds.get(nextInt)); 55 | } 56 | ArrayList integers = new ArrayList<>(set); 57 | 58 | // System.out.println(Arrays.toString(integers.toArray())); 59 | 60 | // 添加秒杀商品 61 | ArrayList seckillProducts = new ArrayList<>(); 62 | for (int j = 0; j < 15; j++) { 63 | SeckillProduct seckillProduct = new SeckillProduct(); 64 | seckillProduct.setSeckillPrice(1000.0); 65 | seckillProduct.setSeckillStock(100); 66 | seckillProduct.setProductId(integers.get(j)); 67 | seckillProduct.setTimeId(seckillTime.getTimeId()); 68 | seckillProducts.add(seckillProduct); 69 | } 70 | 71 | seckillProductMapper.insertList(seckillProducts); 72 | // System.out.println(Arrays.toString(seckillProducts.toArray())); 73 | 74 | try { 75 | Thread.sleep(1_000); 76 | } catch (InterruptedException e) { 77 | e.printStackTrace(); 78 | } 79 | 80 | System.out.println("完成---------------------"); 81 | 82 | } 83 | 84 | System.out.println("一次添加ok-------------------------------------------"); 85 | // try { 86 | // Thread.sleep(100_000); 87 | // } catch (InterruptedException e) { 88 | // e.printStackTrace(); 89 | // } 90 | 91 | } 92 | 93 | private Date getDate() { 94 | Calendar ca = Calendar.getInstance(); 95 | ca.set(Calendar.MINUTE, 0); 96 | ca.set(Calendar.SECOND, 0); 97 | return ca.getTime(); 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/controller/UserController.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.controller; 2 | 3 | import com.mall.xiaomi.pojo.User; 4 | import com.mall.xiaomi.service.UserService; 5 | import com.mall.xiaomi.util.BeanUtil; 6 | import com.mall.xiaomi.util.CookieUtil; 7 | import com.mall.xiaomi.util.MD5Util; 8 | import com.mall.xiaomi.util.ResultMessage; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.data.redis.core.RedisTemplate; 11 | import org.springframework.web.bind.annotation.*; 12 | 13 | import javax.servlet.http.HttpServletRequest; 14 | import javax.servlet.http.HttpServletResponse; 15 | import java.util.Map; 16 | import java.util.concurrent.TimeUnit; 17 | 18 | /** 19 | * @Auther: wdd 20 | * @Date: 2020-03-19 13:27 21 | * @Description: 22 | */ 23 | @RestController 24 | @RequestMapping("/user") 25 | public class UserController { 26 | 27 | @Autowired 28 | private ResultMessage resultMessage; 29 | @Autowired 30 | private RedisTemplate redisTemplate; 31 | @Autowired 32 | private UserService userService; 33 | 34 | /** 35 | * 用户登录 36 | * @param user 37 | * @param request 38 | * @param response 39 | * @return 40 | */ 41 | @PostMapping("/login") 42 | public ResultMessage login(@RequestBody User user, HttpServletRequest request, HttpServletResponse response) { 43 | user = userService.login(user); 44 | // 添加cookie,设置唯一认证 45 | String encode = MD5Util.MD5Encode(user.getUsername() + user.getPassword(), "UTF-8"); 46 | // 进行加盐 47 | encode += "|" + user.getUserId() + "|" + user.getUsername() + "|"; 48 | CookieUtil.setCookie(request, response, "XM_TOKEN", encode, 1800); 49 | // 将encode放入redis中,用于认证 50 | try { 51 | redisTemplate.opsForHash().putAll(encode, BeanUtil.bean2map(user)); 52 | redisTemplate.expire(encode, 30 * 60, TimeUnit.SECONDS); // 设置过期时间 53 | } catch (Exception e) { 54 | e.printStackTrace(); 55 | } 56 | // 将密码设为null,返回给前端 57 | user.setPassword(null); 58 | resultMessage.success("001", "登录成功", user); 59 | return resultMessage; 60 | } 61 | 62 | /** 63 | * 用户注册 64 | * @param user 65 | * @return 66 | */ 67 | @PostMapping("/register") 68 | public ResultMessage register(@RequestBody User user) { 69 | userService.register(user); 70 | resultMessage.success("001", "注册成功"); 71 | return resultMessage; 72 | } 73 | 74 | /** 75 | * 判断用户名是否已存在 76 | * @param username 77 | * @return 78 | */ 79 | @GetMapping("/username/{username}") 80 | public ResultMessage username(@PathVariable String username) { 81 | userService.isUserName(username); 82 | resultMessage.success("001", "可注册"); 83 | return resultMessage; 84 | } 85 | 86 | /** 87 | * 根据token获取用户信息 88 | * @param token 89 | * @return 90 | */ 91 | @GetMapping("/token") 92 | public ResultMessage token(@CookieValue("XM_TOKEN") String token, HttpServletRequest request, HttpServletResponse response) throws Exception { 93 | Map map = redisTemplate.opsForHash().entries(token); 94 | // 可能map为空 , 即redis中时间已过期,但是cookie还存在。 95 | // 这个时候应该删除cookie,让用户重新登录 96 | if (map.isEmpty()) { 97 | CookieUtil.delCookie(request, token); 98 | resultMessage.fail("002", "账号过期,请重新登录"); 99 | return resultMessage; 100 | } 101 | 102 | redisTemplate.expire(token, 30 * 60, TimeUnit.SECONDS); // 设置过期时间 103 | User user = BeanUtil.map2bean(map, User.class); 104 | user.setPassword(null); 105 | resultMessage.success("001", user); 106 | return resultMessage; 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/service/ProductService.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.service; 2 | 3 | import com.github.pagehelper.PageHelper; 4 | import com.github.pagehelper.PageInfo; 5 | import com.mall.xiaomi.exception.ExceptionEnum; 6 | import com.mall.xiaomi.exception.XmException; 7 | import com.mall.xiaomi.mapper.ProductMapper; 8 | import com.mall.xiaomi.pojo.Product; 9 | import org.apache.commons.lang3.ArrayUtils; 10 | import org.apache.commons.lang3.StringUtils; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.stereotype.Service; 13 | import tk.mybatis.mapper.entity.Example; 14 | 15 | import java.util.List; 16 | import java.util.stream.Collectors; 17 | 18 | /** 19 | * @Auther: wdd 20 | * @Date: 2020-03-19 13:21 21 | * @Description: 22 | */ 23 | @Service 24 | public class ProductService { 25 | 26 | @Autowired 27 | private ProductMapper productMapper; 28 | 29 | public List getProductByCategoryId(Integer categoryId) { 30 | List list = null; // 定义一个 Product 对象的列表,用于存储查询结果 31 | 32 | Example example = new Example(Product.class); // 创建一个 MyBatis 的查询条件对象,用于构造查询条件 33 | example.orderBy("productSales").desc(); // 设置排序规则,按照 "productSales" 字段进行降序排序 34 | 35 | example.createCriteria().andEqualTo("categoryId", categoryId); // 创建查询条件,匹配 categoryId 36 | 37 | PageHelper.startPage(0, 8); // 使用 PageHelper 分页,查询前 8 个商品 38 | 39 | try { 40 | list = productMapper.selectByExample(example); // 执行查询,返回符合条件的商品列表 41 | if (ArrayUtils.isEmpty(list.toArray())) { // 检查结果是否为空,如果为空则抛出自定义异常 42 | throw new XmException(ExceptionEnum.GET_PRODUCT_NOT_FOUND); 43 | } 44 | } catch (Exception e) { 45 | e.printStackTrace(); // 打印异常堆栈信息 46 | throw new XmException(ExceptionEnum.GET_PRODUCT_ERROR); // 抛出自定义异常,表示获取商品出错 47 | } 48 | 49 | return list; // 返回查询到的商品列表 50 | } 51 | 52 | public List getHotProduct() { 53 | Example example = new Example(Product.class); 54 | example.orderBy("productSales").desc(); 55 | 56 | PageHelper.startPage(0, 8); 57 | List list = null; 58 | try { 59 | list = productMapper.selectByExample(example); 60 | if (ArrayUtils.isEmpty(list.toArray())) { 61 | throw new XmException(ExceptionEnum.GET_PRODUCT_NOT_FOUND); 62 | } 63 | } catch (Exception e) { 64 | e.printStackTrace(); 65 | throw new XmException(ExceptionEnum.GET_PRODUCT_ERROR); 66 | } 67 | return list; 68 | } 69 | 70 | public Product getProductById(String productId) { 71 | Product product = null; 72 | try { 73 | product = productMapper.selectByPrimaryKey(productId); 74 | if (product == null) { 75 | throw new XmException(ExceptionEnum.GET_PRODUCT_NOT_FOUND); 76 | } 77 | } catch (Exception e) { 78 | e.printStackTrace(); 79 | throw new XmException(ExceptionEnum.GET_PRODUCT_ERROR); 80 | } 81 | return product; 82 | } 83 | 84 | public PageInfo getProductByPage(String currentPage, String pageSize, String categoryId) { 85 | List list = null; 86 | PageHelper.startPage(Integer.parseInt(currentPage) - 1, Integer.parseInt(pageSize), true); 87 | if (categoryId.equals("0")) { // 为0,代表分页查询所有商品 88 | list = productMapper.selectAll(); 89 | } else { 90 | // 分类分页查询商品 91 | Product product = new Product(); 92 | product.setCategoryId(Integer.parseInt(categoryId)); 93 | list = productMapper.select(product); 94 | } 95 | PageInfo pageInfo = new PageInfo(list); 96 | return pageInfo; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.mall 8 | xiaomi 9 | 1.0-SNAPSHOT 10 | 11 | 12 | org.springframework.boot 13 | spring-boot-starter-parent 14 | 2.1.8.RELEASE 15 | 16 | 17 | 18 | 19 | 20 | org.springframework.boot 21 | spring-boot-starter 22 | 23 | 24 | org.springframework.boot 25 | spring-boot-starter-web 26 | 27 | 28 | org.springframework.boot 29 | spring-boot-starter-thymeleaf 30 | 31 | 32 | org.springframework.boot 33 | spring-boot-starter-data-redis 34 | 35 | 36 | org.springframework.boot 37 | spring-boot-starter-amqp 38 | 39 | 40 | mysql 41 | mysql-connector-java 42 | 43 | 44 | org.mybatis.spring.boot 45 | mybatis-spring-boot-starter 46 | 1.3.2 47 | 48 | 49 | tk.mybatis 50 | mapper-spring-boot-starter 51 | 2.0.2 52 | 53 | 54 | com.github.pagehelper 55 | pagehelper-spring-boot-starter 56 | 1.2.5 57 | 58 | 59 | com.alibaba 60 | fastjson 61 | 1.2.83 62 | 63 | 64 | org.projectlombok 65 | lombok 66 | 67 | 68 | com.aliyun 69 | aliyun-java-sdk-core 70 | 4.1.0 71 | 72 | 73 | com.aliyun 74 | aliyun-java-sdk-dysmsapi 75 | 1.1.0 76 | 77 | 78 | org.apache.commons 79 | commons-lang3 80 | 3.9 81 | 82 | 83 | org.slf4j 84 | slf4j-api 85 | 1.7.26 86 | compile 87 | 88 | 89 | 90 | org.apache.commons 91 | commons-pool2 92 | 2.11.1 93 | 94 | 95 | 96 | 97 | 98 | org.springframework.boot 99 | spring-boot-starter-test 100 | test 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | org.springframework.boot 111 | spring-boot-maven-plugin 112 | 113 | 114 | com.mall.xiaomi.Application 115 | 116 | 117 | 118 | 119 | repackage 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | src/main/java 130 | 131 | **/*.properties 132 | **/*.xml 133 | 134 | false 135 | 136 | 137 | src/main/resources 138 | 139 | **/*.properties 140 | **/*.yml 141 | **/*.xml 142 | 143 | false 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/service/ShoppingCartService.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.service; 2 | 3 | import com.mall.xiaomi.exception.ExceptionEnum; 4 | import com.mall.xiaomi.exception.XmException; 5 | import com.mall.xiaomi.mapper.ProductMapper; 6 | import com.mall.xiaomi.mapper.ShoppingCartMapper; 7 | import com.mall.xiaomi.pojo.Product; 8 | import com.mall.xiaomi.pojo.ShoppingCart; 9 | import com.mall.xiaomi.vo.CartVo; 10 | import org.apache.commons.lang3.ArrayUtils; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.stereotype.Service; 13 | import org.springframework.transaction.annotation.Transactional; 14 | 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | 18 | /** 19 | * @Auther: wdd 20 | * @Date: 2020-03-19 13:22 21 | * @Description: 22 | */ 23 | @Service 24 | public class ShoppingCartService { 25 | 26 | @Autowired 27 | private ShoppingCartMapper cartMapper; 28 | @Autowired 29 | private ProductMapper productMapper; 30 | 31 | public List getCartByUserId(String userId) { 32 | ShoppingCart cart = new ShoppingCart(); 33 | cart.setUserId(Integer.parseInt(userId)); 34 | List list = null; 35 | List cartVoList = new ArrayList<>(); 36 | try { 37 | list = cartMapper.select(cart); 38 | for (ShoppingCart c : list) { 39 | cartVoList.add(getCartVo(c)); 40 | } 41 | } catch (Exception e) { 42 | e.printStackTrace(); 43 | throw new XmException(ExceptionEnum.GET_CART_ERROR); 44 | } 45 | return cartVoList; 46 | } 47 | 48 | @Transactional 49 | public CartVo addCart(String productId, String userId) { 50 | ShoppingCart cart = new ShoppingCart(); 51 | cart.setUserId(Integer.parseInt(userId)); 52 | cart.setProductId(Integer.parseInt(productId)); 53 | // 查看数据库是否已存在,存在数量直接加1 54 | ShoppingCart one = cartMapper.selectOne(cart); 55 | 56 | if (one != null) { 57 | 58 | // 还要判断是否达到该商品规定上限 59 | if (one.getNum() >= 5) { // TODO 这里默认设为5 后期再动态修改 60 | throw new XmException(ExceptionEnum.ADD_CART_NUM_UPPER); 61 | } 62 | Integer version = one.getVersion(); 63 | // 尝试更新数量并增加版本号 64 | one.setNum(one.getNum() + 1); 65 | 66 | // 使用带有版本号判断的条件更新 67 | int updateCount = cartMapper.updateCartByIdAndVersion(one); 68 | 69 | if (updateCount == 0) { 70 | CartVo cartVo = new CartVo(); 71 | 72 | cartVo.setUpdateMessage("并发修改失败,请重试!"); 73 | cartVo.setUpdateNum(false); 74 | return cartVo; 75 | } else { 76 | return null; 77 | } 78 | } else { 79 | // 不存在 80 | cart.setNum(1); 81 | cart.setVersion(1); 82 | cartMapper.insert(cart); 83 | return getCartVo(cart); 84 | } 85 | } 86 | 87 | /** 88 | * 封装类 89 | * 90 | * @param cart 91 | * @return 92 | */ 93 | private CartVo getCartVo(ShoppingCart cart) { 94 | // 获取商品,用于封装下面的类 95 | Product product = productMapper.selectByPrimaryKey(cart.getProductId()); 96 | // 返回购物车详情 97 | CartVo cartVo = new CartVo(); 98 | cartVo.setId(cart.getId()); 99 | cartVo.setProductId(cart.getProductId()); 100 | cartVo.setProductName(product.getProductName()); 101 | cartVo.setProductImg(product.getProductPicture()); 102 | cartVo.setPrice(product.getProductSellingPrice()); 103 | cartVo.setNum(cart.getNum()); 104 | cartVo.setMaxNum(5); // TODO 这里默认设为5 后期再动态修改 105 | cartVo.setCheck(false); 106 | return cartVo; 107 | } 108 | 109 | public void updateCartNum(String cartId, String userId, String num) { 110 | ShoppingCart cart = new ShoppingCart(); 111 | cart.setId(Integer.parseInt(cartId)); 112 | cart.setUserId(Integer.parseInt(userId)); 113 | cart.setNum(Integer.parseInt(num)); 114 | try { 115 | int count = cartMapper.updateByPrimaryKeySelective(cart); 116 | if (count != 1) { 117 | throw new XmException(ExceptionEnum.UPDATE_CART_ERROR); 118 | } 119 | } catch (Exception e) { 120 | e.printStackTrace(); 121 | throw new XmException(ExceptionEnum.UPDATE_CART_ERROR); 122 | } 123 | } 124 | 125 | public void deleteCart(String cartId, String userId) { 126 | ShoppingCart cart = new ShoppingCart(); 127 | cart.setId(Integer.parseInt(cartId)); 128 | cart.setUserId(Integer.parseInt(userId)); 129 | try { 130 | cartMapper.delete(cart); 131 | } catch (XmException e) { 132 | e.printStackTrace(); 133 | throw new XmException(ExceptionEnum.DELETE_CART_ERROR); 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/util/IdWorker.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.util; 2 | 3 | import java.lang.management.ManagementFactory; 4 | import java.net.InetAddress; 5 | import java.net.NetworkInterface; 6 | 7 | /** 8 | *

名称:IdWorker.java

9 | *

描述:分布式自增长ID

10 | *
 11 |  *     Twitter的 Snowflake JAVA实现方案
 12 |  * 
13 | * 核心代码为其IdWorker这个类实现,其原理结构如下,我分别用一个0表示一位,用—分割开部分的作用: 14 | * 1||0---0000000000 0000000000 0000000000 0000000000 0 --- 00000 ---00000 ---000000000000 15 | * 在上面的字符串中,第一位为未使用(实际上也可作为long的符号位),接下来的41位为毫秒级时间, 16 | * 然后5位datacenter标识位,5位机器ID(并不算标识符,实际是为线程标识), 17 | * 然后12位该毫秒内的当前毫秒内的计数,加起来刚好64位,为一个Long型。 18 | * 这样的好处是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由datacenter和机器ID作区分), 19 | * 并且效率较高,经测试,snowflake每秒能够产生26万ID左右,完全满足需要。 20 | *

21 | * 64位ID (42(毫秒)+5(机器ID)+5(业务编码)+12(重复累加)) 22 | * 23 | * @author Polim 24 | */ 25 | public class IdWorker { 26 | // 时间起始标记点,作为基准,一般取系统的最近时间(一旦确定不能变动) 27 | private final static long twepoch = 1288834974657L; 28 | // 机器标识位数 29 | private final static long workerIdBits = 5L; 30 | // 数据中心标识位数 31 | private final static long datacenterIdBits = 5L; 32 | // 机器ID最大值 33 | private final static long maxWorkerId = -1L ^ (-1L << workerIdBits); 34 | // 数据中心ID最大值 35 | private final static long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); 36 | // 毫秒内自增位 37 | private final static long sequenceBits = 12L; 38 | // 机器ID偏左移12位 39 | private final static long workerIdShift = sequenceBits; 40 | // 数据中心ID左移17位 41 | private final static long datacenterIdShift = sequenceBits + workerIdBits; 42 | // 时间毫秒左移22位 43 | private final static long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; 44 | 45 | private final static long sequenceMask = -1L ^ (-1L << sequenceBits); 46 | /* 上次生产id时间戳 */ 47 | private static long lastTimestamp = -1L; 48 | // 0,并发控制 49 | private long sequence = 0L; 50 | 51 | private final long workerId; 52 | // 数据标识id部分 53 | private final long datacenterId; 54 | 55 | public IdWorker(){ 56 | this.datacenterId = getDatacenterId(maxDatacenterId); 57 | this.workerId = getMaxWorkerId(datacenterId, maxWorkerId); 58 | } 59 | /** 60 | * @param workerId 61 | * 工作机器ID 62 | * @param datacenterId 63 | * 序列号 64 | */ 65 | public IdWorker(long workerId, long datacenterId) { 66 | if (workerId > maxWorkerId || workerId < 0) { 67 | throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId)); 68 | } 69 | if (datacenterId > maxDatacenterId || datacenterId < 0) { 70 | throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId)); 71 | } 72 | this.workerId = workerId; 73 | this.datacenterId = datacenterId; 74 | } 75 | /** 76 | * 获取下一个ID 77 | * 78 | * @return 79 | */ 80 | public synchronized long nextId() { 81 | long timestamp = timeGen(); 82 | if (timestamp < lastTimestamp) { 83 | throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp)); 84 | } 85 | 86 | if (lastTimestamp == timestamp) { 87 | // 当前毫秒内,则+1 88 | sequence = (sequence + 1) & sequenceMask; 89 | if (sequence == 0) { 90 | // 当前毫秒内计数满了,则等待下一秒 91 | timestamp = tilNextMillis(lastTimestamp); 92 | } 93 | } else { 94 | sequence = 0L; 95 | } 96 | lastTimestamp = timestamp; 97 | // ID偏移组合生成最终的ID,并返回ID 98 | long nextId = ((timestamp - twepoch) << timestampLeftShift) 99 | | (datacenterId << datacenterIdShift) 100 | | (workerId << workerIdShift) | sequence; 101 | 102 | return nextId; 103 | } 104 | 105 | private long tilNextMillis(final long lastTimestamp) { 106 | long timestamp = this.timeGen(); 107 | while (timestamp <= lastTimestamp) { 108 | timestamp = this.timeGen(); 109 | } 110 | return timestamp; 111 | } 112 | 113 | private long timeGen() { 114 | return System.currentTimeMillis(); 115 | } 116 | 117 | /** 118 | *

119 | * 获取 maxWorkerId 120 | *

121 | */ 122 | protected static long getMaxWorkerId(long datacenterId, long maxWorkerId) { 123 | StringBuffer mpid = new StringBuffer(); 124 | mpid.append(datacenterId); 125 | String name = ManagementFactory.getRuntimeMXBean().getName(); 126 | if (!name.isEmpty()) { 127 | /* 128 | * GET jvmPid 129 | */ 130 | mpid.append(name.split("@")[0]); 131 | } 132 | /* 133 | * MAC + PID 的 hashcode 获取16个低位 134 | */ 135 | return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1); 136 | } 137 | 138 | /** 139 | *

140 | * 数据标识id部分 141 | *

142 | */ 143 | protected static long getDatacenterId(long maxDatacenterId) { 144 | long id = 0L; 145 | try { 146 | InetAddress ip = InetAddress.getLocalHost(); 147 | NetworkInterface network = NetworkInterface.getByInetAddress(ip); 148 | if (network == null) { 149 | id = 1L; 150 | } else { 151 | byte[] mac = network.getHardwareAddress(); 152 | id = ((0x000000FF & (long) mac[mac.length - 1]) 153 | | (0x0000FF00 & (((long) mac[mac.length - 2]) << 8))) >> 6; 154 | id = id % (maxDatacenterId + 1); 155 | } 156 | } catch (Exception e) { 157 | System.out.println(" getDatacenterId: " + e.getMessage()); 158 | } 159 | return id; 160 | } 161 | 162 | 163 | } 164 | -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/mq/SeckillOrderQueue.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.mq; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.mall.xiaomi.mapper.SeckillMessageRecordMapper; 5 | import com.mall.xiaomi.pojo.SeckillMessageRecord; 6 | import com.mall.xiaomi.service.OrderService; 7 | import com.mall.xiaomi.service.SeckillProductService; 8 | import com.mall.xiaomi.util.RedisKey; 9 | import com.rabbitmq.client.Channel; 10 | import org.springframework.amqp.core.Message; 11 | import org.springframework.amqp.rabbit.annotation.RabbitListener; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.data.redis.core.StringRedisTemplate; 14 | import org.springframework.stereotype.Component; 15 | 16 | import java.io.IOException; 17 | import java.util.Date; 18 | import java.util.Map; 19 | import java.util.Optional; 20 | import java.util.concurrent.TimeUnit; 21 | 22 | /** 23 | * @Auther: wdd 24 | * @Date: 2020-04-24 9:30 25 | * @Description: 26 | */ 27 | @Component 28 | public class SeckillOrderQueue { 29 | 30 | private static final int MAX_RETRY_COUNT = 3; 31 | 32 | private static final String RETRY_COUNT_KEY_PREFIX = "retryCount:"; 33 | @Autowired 34 | private OrderService orderService; 35 | @Autowired 36 | private SeckillProductService seckillProductService; 37 | @Autowired 38 | StringRedisTemplate stringRedisTemplate; 39 | 40 | @Autowired 41 | SeckillMessageRecordMapper seckillMessageRecordMapper; 42 | 43 | @Autowired 44 | ObjectMapper objectMapper; 45 | 46 | @RabbitListener(queues = "seckill_order", containerFactory = "rabbitListenerContainerFactory") 47 | public void insertOrder(Map map, Channel channel, Message message) { 48 | String correlationId = message.getMessageProperties().getCorrelationId(); 49 | String redisKey = RedisKey.SECKILL_RABBITMQ_ID + correlationId; 50 | 51 | // 幂等性检查 52 | // 从数据库中查找是否已存在该消息的记录 53 | SeckillMessageRecord record = seckillMessageRecordMapper.findByMessageId(correlationId); 54 | if (record != null && ("PROCESSED".equals(record.getStatus()) || "FAILED".equals(record.getStatus()))) { 55 | 56 | System.out.println("Message with ID " + correlationId + " has already been processed."); 57 | try { 58 | channel.basicAck(message.getMessageProperties().getDeliveryTag(), false); 59 | } catch (IOException e) { 60 | e.printStackTrace(); 61 | } 62 | return; // 已经处理过的消息直接返回 63 | } 64 | 65 | if (record == null) { 66 | // 如果记录不存在,创建一条新记录 67 | record = new SeckillMessageRecord(); 68 | record.setMessageId(correlationId); 69 | record.setUserId((String) map.get("userId")); 70 | record.setSeckillId((String) map.get("seckillId")); 71 | record.setCreatedAt(new Date()); 72 | seckillMessageRecordMapper.insert(record); 73 | } 74 | 75 | try { 76 | String seckillId = (String) map.get("seckillId"); 77 | String userId = (String) map.get("userId"); 78 | 79 | orderService.addSeckillOrder(seckillId, userId); 80 | 81 | // 消息成功处理,更新状态 82 | record.setStatus("PROCESSED"); 83 | 84 | channel.basicAck(message.getMessageProperties().getDeliveryTag(), false); 85 | } catch (Exception e) { 86 | // 增加重试次数 87 | record.setRetryCount( 88 | record.getRetryCount() == null ? 1 : record.getRetryCount() + 1 89 | ); 90 | 91 | record.setErrorMessage(e.getMessage()); 92 | 93 | if (record.getRetryCount() >= MAX_RETRY_COUNT) { 94 | // 达到最大重试次数,将消息标记为失败 95 | record.setStatus("FAILED"); 96 | try { 97 | channel.basicReject(message.getMessageProperties().getDeliveryTag(), false); 98 | } catch (IOException ioException) { 99 | ioException.printStackTrace(); 100 | } 101 | } else { 102 | // 保存重试状态,并重新入队 103 | seckillMessageRecordMapper.updateByMessageId(record); 104 | try { 105 | channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true); 106 | } catch (IOException ioException) { 107 | ioException.printStackTrace(); 108 | } 109 | } 110 | } finally { 111 | record.setUpdatedAt(new Date()); 112 | seckillMessageRecordMapper.updateByMessageId(record); 113 | } 114 | } 115 | 116 | @RabbitListener(queues = "seckill_dead_letter_queue") 117 | public void processDeadLetterMessage(Map map) { 118 | String seckillId = (String) map.get("seckillId"); 119 | String userId = (String) map.get("userId"); 120 | String messageId = seckillId + ":" + userId; 121 | // 查询数据库中是否有对应的消息记录 122 | Optional optionalRecord = Optional.ofNullable(seckillMessageRecordMapper.findByMessageId(messageId)); 123 | 124 | if (optionalRecord.isPresent()) { 125 | 126 | System.out.println("Dead letter message processed and saved: " + messageId); 127 | } else { 128 | System.out.println("Dead letter message not found in records: " + messageId); 129 | } 130 | } 131 | 132 | } 133 | -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/util/CookieUtil.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.util; 2 | 3 | import org.apache.commons.lang3.StringUtils; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | import javax.servlet.http.Cookie; 8 | import javax.servlet.http.HttpServletRequest; 9 | import javax.servlet.http.HttpServletResponse; 10 | import java.io.UnsupportedEncodingException; 11 | import java.net.URLDecoder; 12 | import java.net.URLEncoder; 13 | 14 | public final class CookieUtil { 15 | 16 | static final Logger logger = LoggerFactory.getLogger(CookieUtil.class); 17 | 18 | /** 19 | * 得到Cookie的值, 不编码 20 | * 21 | * @param request 22 | * @param cookieName 23 | * @return 24 | */ 25 | public static String getCookieValue(HttpServletRequest request, String cookieName) { 26 | return getCookieValue(request, cookieName, false); 27 | } 28 | 29 | /** 30 | * 得到Cookie的值, 31 | * 32 | * @param request 33 | * @param cookieName 34 | * @return 35 | */ 36 | public static String getCookieValue(HttpServletRequest request, String cookieName, boolean isDecoder) { 37 | Cookie[] cookieList = request.getCookies(); 38 | if (cookieList == null || cookieName == null){ 39 | return null; 40 | } 41 | String retValue = null; 42 | try { 43 | for (int i = 0; i < cookieList.length; i++) { 44 | if (cookieList[i].getName().equals(cookieName)) { 45 | if (isDecoder) { 46 | retValue = URLDecoder.decode(cookieList[i].getValue(), "UTF-8"); 47 | } else { 48 | retValue = cookieList[i].getValue(); 49 | } 50 | break; 51 | } 52 | } 53 | } catch (UnsupportedEncodingException e) { 54 | logger.error("Cookie Decode Error.", e); 55 | } 56 | return retValue; 57 | } 58 | 59 | /** 60 | * 得到Cookie的值, 61 | * 62 | * @param request 63 | * @param cookieName 64 | * @return 65 | */ 66 | public static String getCookieValue(HttpServletRequest request, String cookieName, String encodeString) { 67 | Cookie[] cookieList = request.getCookies(); 68 | if (cookieList == null || cookieName == null){ 69 | return null; 70 | } 71 | String retValue = null; 72 | try { 73 | for (int i = 0; i < cookieList.length; i++) { 74 | if (cookieList[i].getName().equals(cookieName)) { 75 | retValue = URLDecoder.decode(cookieList[i].getValue(), encodeString); 76 | break; 77 | } 78 | } 79 | } catch (UnsupportedEncodingException e) { 80 | logger.error("Cookie Decode Error.", e); 81 | } 82 | return retValue; 83 | } 84 | 85 | /** 86 | * 生成cookie,并指定编码 87 | * @param request 请求 88 | * @param response 响应 89 | * @param cookieName name 90 | * @param cookieValue value 91 | * @param encodeString 编码 92 | */ 93 | public static final void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue, String encodeString) { 94 | setCookie(request,response,cookieName,cookieValue,null,encodeString, null); 95 | } 96 | 97 | /** 98 | * 生成cookie,并指定生存时间 99 | * @param request 请求 100 | * @param response 响应 101 | * @param cookieName name 102 | * @param cookieValue value 103 | * @param cookieMaxAge 生存时间 104 | */ 105 | public static final void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue, Integer cookieMaxAge) { 106 | setCookie(request,response,cookieName,cookieValue,cookieMaxAge,null, null); 107 | } 108 | 109 | /** 110 | * 设置cookie,不指定httpOnly属性 111 | */ 112 | public static final void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue, Integer cookieMaxAge, String encodeString) { 113 | setCookie(request,response,cookieName,cookieValue,cookieMaxAge,encodeString, null); 114 | } 115 | 116 | /** 117 | * 设置Cookie的值,并使其在指定时间内生效 118 | * 119 | * @param cookieMaxAge 120 | * cookie生效的最大秒数 121 | */ 122 | public static final void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue, Integer cookieMaxAge, String encodeString, Boolean httpOnly) { 123 | try { 124 | if(StringUtils.isBlank(encodeString)) { 125 | encodeString = "utf-8"; 126 | } 127 | 128 | if (cookieValue == null) { 129 | cookieValue = ""; 130 | } else { 131 | cookieValue = URLEncoder.encode(cookieValue, encodeString); 132 | } 133 | Cookie cookie = new Cookie(cookieName, cookieValue); 134 | if (cookieMaxAge != null && cookieMaxAge > 0) 135 | cookie.setMaxAge(cookieMaxAge); 136 | if (null != request)// 设置域名的cookie 137 | cookie.setDomain(getDomainName(request)); 138 | cookie.setPath("/"); 139 | 140 | if(httpOnly != null) { 141 | cookie.setHttpOnly(httpOnly); 142 | } 143 | response.addCookie(cookie); 144 | } catch (Exception e) { 145 | logger.error("Cookie Encode Error.", e); 146 | } 147 | } 148 | 149 | public static final void delCookie(HttpServletRequest request, String cookieName) { 150 | Cookie[] cookies = request.getCookies(); // 获取cookie数组 151 | for (Cookie cookie : cookies) { 152 | if (cookieName.equals(cookie.getName())) { 153 | Cookie c = new Cookie(cookie.getName(), null); // 删除前必须要new 一个value为空; 154 | c.setPath("/"); // 路径要相同 155 | c.setMaxAge(0); // 生命周期设置为0 156 | break; 157 | } 158 | } 159 | } 160 | 161 | /** 162 | * 得到cookie的域名 163 | */ 164 | private static final String getDomainName(HttpServletRequest request) { 165 | String domainName = null; 166 | 167 | String serverName = request.getRequestURL().toString(); 168 | if (serverName == null || serverName.equals("")) { 169 | domainName = ""; 170 | } else { 171 | serverName = serverName.toLowerCase(); 172 | serverName = serverName.substring(7); 173 | final int end = serverName.indexOf("/"); 174 | serverName = serverName.substring(0, end); 175 | final String[] domains = serverName.split("\\."); 176 | int len = domains.length; 177 | if (len > 3) { 178 | // www.xxx.com.cn 179 | domainName = domains[len - 3] + "." + domains[len - 2] + "." + domains[len - 1]; 180 | } else if (len <= 3 && len > 1) { 181 | // xxx.com or xxx.cn 182 | domainName = domains[len - 2] + "." + domains[len - 1]; 183 | } else { 184 | domainName = serverName; 185 | } 186 | } 187 | 188 | if (domainName != null && domainName.indexOf(":") > 0) { 189 | String[] ary = domainName.split("\\:"); 190 | domainName = ary[0]; 191 | } 192 | return domainName; 193 | } 194 | 195 | } 196 | -------------------------------------------------------------------------------- /src/main/resources/mybatis/SeckillMessageRecordMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | id, message_id, user_id, seckill_id, `status`, retry_count, error_message, created_at, 17 | updated_at 18 | 19 | 25 | 31 | 32 | delete from seckill_message_record 33 | where id = #{id,jdbcType=BIGINT} 34 | 35 | 36 | insert into seckill_message_record (message_id, user_id, seckill_id, 37 | `status`, retry_count, error_message, 38 | created_at, updated_at) 39 | values (#{messageId,jdbcType=VARCHAR}, #{userId,jdbcType=VARCHAR}, #{seckillId,jdbcType=VARCHAR}, 40 | #{status,jdbcType=VARCHAR}, #{retryCount,jdbcType=INTEGER}, #{errorMessage,jdbcType=VARCHAR}, 41 | #{createdAt,jdbcType=TIMESTAMP}, #{updatedAt,jdbcType=TIMESTAMP}) 42 | 43 | 44 | insert into seckill_message_record 45 | 46 | 47 | message_id, 48 | 49 | 50 | user_id, 51 | 52 | 53 | seckill_id, 54 | 55 | 56 | `status`, 57 | 58 | 59 | retry_count, 60 | 61 | 62 | error_message, 63 | 64 | 65 | created_at, 66 | 67 | 68 | updated_at, 69 | 70 | 71 | 72 | 73 | #{messageId,jdbcType=VARCHAR}, 74 | 75 | 76 | #{userId,jdbcType=VARCHAR}, 77 | 78 | 79 | #{seckillId,jdbcType=VARCHAR}, 80 | 81 | 82 | #{status,jdbcType=VARCHAR}, 83 | 84 | 85 | #{retryCount,jdbcType=INTEGER}, 86 | 87 | 88 | #{errorMessage,jdbcType=VARCHAR}, 89 | 90 | 91 | #{createdAt,jdbcType=TIMESTAMP}, 92 | 93 | 94 | #{updatedAt,jdbcType=TIMESTAMP}, 95 | 96 | 97 | 98 | 99 | update seckill_message_record 100 | 101 | 102 | message_id = #{messageId,jdbcType=VARCHAR}, 103 | 104 | 105 | user_id = #{userId,jdbcType=VARCHAR}, 106 | 107 | 108 | seckill_id = #{seckillId,jdbcType=VARCHAR}, 109 | 110 | 111 | `status` = #{status,jdbcType=VARCHAR}, 112 | 113 | 114 | retry_count = #{retryCount,jdbcType=INTEGER}, 115 | 116 | 117 | error_message = #{errorMessage,jdbcType=VARCHAR}, 118 | 119 | 120 | created_at = #{createdAt,jdbcType=TIMESTAMP}, 121 | 122 | 123 | updated_at = #{updatedAt,jdbcType=TIMESTAMP}, 124 | 125 | 126 | where id = #{id,jdbcType=BIGINT} 127 | 128 | 129 | update seckill_message_record 130 | set message_id = #{messageId,jdbcType=VARCHAR}, 131 | user_id = #{userId,jdbcType=VARCHAR}, 132 | seckill_id = #{seckillId,jdbcType=VARCHAR}, 133 | `status` = #{status,jdbcType=VARCHAR}, 134 | retry_count = #{retryCount,jdbcType=INTEGER}, 135 | error_message = #{errorMessage,jdbcType=VARCHAR}, 136 | created_at = #{createdAt,jdbcType=TIMESTAMP}, 137 | updated_at = #{updatedAt,jdbcType=TIMESTAMP} 138 | where id = #{id,jdbcType=BIGINT} 139 | 140 | 141 | update seckill_message_record 142 | set `status` = #{record.status,jdbcType=VARCHAR}, 143 | retry_count = #{record.retryCount,jdbcType=INTEGER}, 144 | error_message = #{record.errorMessage,jdbcType=VARCHAR}, 145 | updated_at = #{record.updatedAt,jdbcType=TIMESTAMP} 146 | 147 | where message_id = #{record.messageId,jdbcType=VARCHAR} 148 | 149 | 150 | -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/service/OrderService.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.service; 2 | 3 | import com.mall.xiaomi.exception.ExceptionEnum; 4 | import com.mall.xiaomi.exception.XmException; 5 | import com.mall.xiaomi.mapper.OrderMapper; 6 | import com.mall.xiaomi.mapper.ProductMapper; 7 | import com.mall.xiaomi.mapper.SeckillProductMapper; 8 | import com.mall.xiaomi.mapper.ShoppingCartMapper; 9 | import com.mall.xiaomi.pojo.Order; 10 | import com.mall.xiaomi.pojo.Product; 11 | import com.mall.xiaomi.pojo.SeckillProduct; 12 | import com.mall.xiaomi.pojo.ShoppingCart; 13 | import com.mall.xiaomi.util.IdWorker; 14 | import com.mall.xiaomi.vo.CartVo; 15 | import com.mall.xiaomi.vo.OrderVo; 16 | import org.apache.commons.lang3.ArrayUtils; 17 | import org.springframework.beans.factory.annotation.Autowired; 18 | import org.springframework.data.redis.core.RedisTemplate; 19 | import org.springframework.stereotype.Service; 20 | import org.springframework.transaction.annotation.Transactional; 21 | 22 | import java.util.*; 23 | import java.util.function.Predicate; 24 | import java.util.stream.Collectors; 25 | 26 | /** 27 | * @Auther: wdd 28 | * @Date: 2020-03-19 13:21 29 | * @Description: 30 | */ 31 | @Service 32 | public class OrderService { 33 | 34 | @Autowired 35 | private IdWorker idWorker; 36 | @Autowired 37 | private RedisTemplate redisTemplate; 38 | @Autowired 39 | private OrderMapper orderMapper; 40 | @Autowired 41 | private ShoppingCartMapper cartMapper; 42 | @Autowired 43 | private ProductMapper productMapper; 44 | @Autowired 45 | private SeckillProductMapper seckillProductMapper; 46 | 47 | private final static String SECKILL_PRODUCT_USER_LIST = "seckill:product:user:list"; 48 | 49 | @Transactional 50 | public void addOrder(List cartVoList, Integer userId) { 51 | // 先添加订单 52 | String orderId = idWorker.nextId() + ""; // 订单id 53 | long time = new Date().getTime(); // 订单生成时间 54 | for (CartVo cartVo : cartVoList) { 55 | Integer productId = cartVo.getProductId(); 56 | Integer saleNum = cartVo.getNum(); 57 | Order order = new Order(); 58 | order.setOrderId(orderId); 59 | order.setOrderTime(time); 60 | order.setProductNum(saleNum); 61 | order.setProductId(productId); 62 | order.setProductPrice(cartVo.getPrice()); 63 | order.setUserId(userId); 64 | try { 65 | orderMapper.insert(order); 66 | } catch (Exception e) { 67 | e.printStackTrace(); 68 | throw new XmException(ExceptionEnum.ADD_ORDER_ERROR); 69 | } 70 | // 减去商品库存,记录卖出商品数量 71 | // TODO : 此处会产生多线程问题,即不同用户同时对这个商品操作,此时会导致数量不一致问题, 72 | 73 | /* Product product = productMapper.selectByPrimaryKey(cartVo.getProductId()); 74 | product.setProductNum(product.getProductNum() - cartVo.getNum()); 75 | product.setProductSales(product.getProductSales() + cartVo.getNum()); */ 76 | //所以利用数据库原子性+乐观锁+重试机制 通过产品数量>0 ,版本号是否相等 来进行判断, 保证库存不会出现负数 77 | // 获取当前商品信息 78 | Product product = productMapper.selectByPrimaryKey(productId); 79 | if (product == null) { 80 | throw new RuntimeException("商品不存在"); 81 | } 82 | 83 | int currentVersion = product.getVersion(); 84 | 85 | // 尝试更新库存和版本号 86 | int retryTimes = 3; 87 | boolean success = false; 88 | 89 | while (retryTimes > 0 && !success) { 90 | int updateCount = productMapper.updateStockByIdAndVersion(productId, saleNum, currentVersion); 91 | if (updateCount > 0) { 92 | success = true; 93 | } else { 94 | retryTimes--; 95 | // 重新获取最新的 product 信息 96 | product = productMapper.selectByPrimaryKey(productId); 97 | currentVersion = product.getVersion(); 98 | } 99 | } 100 | } 101 | // 删除购物车 102 | ShoppingCart cart = new ShoppingCart(); 103 | cart.setUserId(userId); 104 | try { 105 | int count = cartMapper.delete(cart); 106 | if (count == 0) { 107 | throw new XmException(ExceptionEnum.ADD_ORDER_ERROR); 108 | } 109 | } catch (Exception e) { 110 | e.printStackTrace(); 111 | throw new XmException(ExceptionEnum.ADD_ORDER_ERROR); 112 | } 113 | 114 | } 115 | 116 | public List> getOrder(Integer userId) { 117 | List list = null; 118 | ArrayList> ret = new ArrayList<>(); 119 | try { 120 | list = orderMapper.getOrderVoByUserId(userId); 121 | if (ArrayUtils.isEmpty(list.toArray())) { 122 | throw new XmException(ExceptionEnum.GET_ORDER_NOT_FOUND); 123 | } 124 | // 将同一个订单放在一组 125 | Map> collect = list.stream().collect(Collectors.groupingBy(Order::getOrderId)); 126 | 127 | // 将分组结果按 order_time 降序排列 128 | List> sortedCollect = collect.values().stream() 129 | // 将每个分组内的数据按照 order_time 降序排列 130 | .sorted((list1, list2) -> { 131 | OrderVo order1 = list1.stream().max(Comparator.comparing(OrderVo::getOrderTime)).orElse(null); 132 | OrderVo order2 = list2.stream().max(Comparator.comparing(OrderVo::getOrderTime)).orElse(null); 133 | if (order1 == null && order2 == null) return 0; 134 | if (order1 == null) return 1; 135 | if (order2 == null) return -1; 136 | return order2.getOrderTime().compareTo(order1.getOrderTime()); 137 | }) 138 | .collect(Collectors.toList()); 139 | 140 | ret.addAll(sortedCollect); 141 | } catch (XmException e) { 142 | e.printStackTrace(); 143 | throw new XmException(ExceptionEnum.GET_ORDER_ERROR); 144 | } 145 | return ret; 146 | } 147 | 148 | @Transactional 149 | public void addSeckillOrder(String seckillId, String userId) { 150 | // 订单id 151 | String orderId = idWorker.nextId() + ""; 152 | // 商品id 153 | SeckillProduct seckillProduct = new SeckillProduct(); 154 | seckillProduct.setSeckillId(Integer.parseInt(seckillId)); 155 | SeckillProduct one = seckillProductMapper.selectOne(seckillProduct); 156 | Integer productId = one.getProductId(); 157 | // 秒杀价格 158 | Double price = one.getSeckillPrice(); 159 | 160 | // 订单封装 161 | Order order = new Order(); 162 | order.setOrderId(orderId); 163 | order.setProductId(productId); 164 | order.setProductNum(1); 165 | order.setUserId(Integer.parseInt(userId)); 166 | order.setOrderTime(new Date().getTime()); 167 | order.setProductPrice(price); 168 | 169 | try { 170 | orderMapper.insert(order); 171 | // 减库存 172 | seckillProductMapper.decrStock(one.getSeckillId()); 173 | } catch (Exception e) { 174 | e.printStackTrace(); 175 | throw new XmException(ExceptionEnum.ADD_ORDER_ERROR); 176 | } 177 | 178 | // 订单创建成功, 将用户写入redis, 防止多次抢购 179 | redisTemplate.opsForList().leftPush(SECKILL_PRODUCT_USER_LIST + seckillId, userId); 180 | 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /src/main/java/com/mall/xiaomi/service/SeckillProductService.java: -------------------------------------------------------------------------------- 1 | package com.mall.xiaomi.service; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.github.pagehelper.PageInfo; 5 | import com.mall.xiaomi.exception.ExceptionEnum; 6 | import com.mall.xiaomi.exception.XmException; 7 | import com.mall.xiaomi.mapper.SeckillProductMapper; 8 | import com.mall.xiaomi.mapper.SeckillTimeMapper; 9 | import com.mall.xiaomi.pojo.SeckillProduct; 10 | import com.mall.xiaomi.pojo.SeckillTime; 11 | import com.mall.xiaomi.util.BeanUtil; 12 | import com.mall.xiaomi.util.RedisKey; 13 | import com.mall.xiaomi.vo.SeckillProductVo; 14 | import org.apache.commons.lang3.ArrayUtils; 15 | import org.springframework.amqp.AmqpException; 16 | import org.springframework.amqp.core.MessageDeliveryMode; 17 | import org.springframework.amqp.rabbit.connection.CorrelationData; 18 | import org.springframework.amqp.rabbit.core.RabbitTemplate; 19 | import org.springframework.beans.factory.annotation.Autowired; 20 | import org.springframework.data.redis.connection.ReturnType; 21 | import org.springframework.data.redis.core.RedisCallback; 22 | import org.springframework.data.redis.core.RedisTemplate; 23 | import org.springframework.data.redis.core.StringRedisTemplate; 24 | import org.springframework.stereotype.Service; 25 | import org.springframework.transaction.annotation.Transactional; 26 | 27 | import java.util.*; 28 | import java.util.concurrent.TimeUnit; 29 | 30 | /** 31 | * @Auther: wdd 32 | * @Date: 2020-03-28 20:01 33 | * @Description: 34 | */ 35 | @Service 36 | public class SeckillProductService { 37 | 38 | @Autowired 39 | private RedisTemplate redisTemplate; 40 | @Autowired 41 | private StringRedisTemplate stringRedisTemplate; 42 | @Autowired 43 | private RabbitTemplate rabbitTemplate; 44 | @Autowired 45 | private SeckillProductMapper seckillProductMapper; 46 | @Autowired 47 | private SeckillTimeMapper seckillTimeMapper; 48 | 49 | @Autowired 50 | ObjectMapper objectMapper; 51 | 52 | private HashMap localOverMap = new HashMap<>(); 53 | 54 | @Transactional 55 | public List getProduct(String timeId) { 56 | 57 | // 先查看缓存,是否有列表 58 | List seckillProductVos = redisTemplate.opsForList().range(RedisKey.SECKILL_PRODUCT_LIST + timeId, 0, -1); 59 | 60 | if (ArrayUtils.isNotEmpty(seckillProductVos.toArray())) { 61 | return seckillProductVos; 62 | } 63 | // 缓存没有,再从数据库中获取,添加到缓存 64 | seckillProductVos = seckillProductMapper.getSeckillProductVos(timeId, new Date().getTime()); 65 | if (ArrayUtils.isNotEmpty(seckillProductVos.toArray())) { 66 | redisTemplate.opsForList().leftPushAll(RedisKey.SECKILL_PRODUCT_LIST + timeId, seckillProductVos); 67 | // 设置过期时间 68 | long l = seckillProductVos.get(0).getEndTime() - new Date().getTime(); 69 | redisTemplate.expire(RedisKey.SECKILL_PRODUCT_LIST + timeId, l, TimeUnit.MILLISECONDS); 70 | } else { 71 | // 秒杀商品过期或不存在 72 | throw new XmException(ExceptionEnum.GET_SECKILL_NOT_FOUND); 73 | } 74 | return seckillProductVos; 75 | } 76 | 77 | public void addSeckillProduct(SeckillProduct seckillProduct) { 78 | // TODO: 仿添加秒杀商品 79 | Date time = getDate(); 80 | long startTime = time.getTime() / 1000 * 1000 + 1000 * 60 * 60; 81 | long endTime = startTime + 1000 * 60 * 60; 82 | SeckillTime seckillTime = new SeckillTime(); 83 | seckillTime.setStartTime(startTime); 84 | seckillTime.setEndTime(endTime); 85 | // 先查看是否有该时间段 86 | SeckillTime one = seckillTimeMapper.selectOne(seckillTime); 87 | if (one == null) { 88 | seckillTimeMapper.insert(seckillTime); 89 | seckillProduct.setTimeId(seckillTime.getTimeId()); 90 | } else { 91 | seckillProduct.setTimeId(one.getTimeId()); 92 | } 93 | seckillProductMapper.insert(seckillProduct); 94 | } 95 | 96 | /** 97 | * 获取当前时间的整点 98 | * 99 | * @return 100 | */ 101 | private Date getDate() { 102 | Calendar ca = Calendar.getInstance(); 103 | ca.set(Calendar.MINUTE, 0); 104 | ca.set(Calendar.SECOND, 0); 105 | return ca.getTime(); 106 | } 107 | 108 | public List getTime() { 109 | // 获取当前时间及往后7个时间段, 总共8个 110 | Date time = getDate(); 111 | List seckillTimes = seckillTimeMapper.getTime(time.getTime() / 1000 * 1000); 112 | return seckillTimes; 113 | } 114 | 115 | public SeckillProductVo getSeckill(String seckillId) { 116 | // 从缓存中查询 117 | Map map = redisTemplate.opsForHash().entries(RedisKey.SECKILL_PRODUCT + seckillId); 118 | if (!map.isEmpty()) { 119 | map.size(); 120 | SeckillProductVo seckillProductVo = null; 121 | try { 122 | seckillProductVo = BeanUtil.map2bean(map, SeckillProductVo.class); 123 | } catch (Exception e) { 124 | e.printStackTrace(); 125 | } 126 | return seckillProductVo; 127 | } 128 | // 从数据库中查询 129 | SeckillProductVo seckillProductVo = seckillProductMapper.getSeckill(seckillId); 130 | if (seckillProductVo != null) { 131 | try { 132 | redisTemplate.opsForHash().putAll(RedisKey.SECKILL_PRODUCT + seckillId, BeanUtil.bean2map(seckillProductVo)); 133 | redisTemplate.expire(RedisKey.SECKILL_PRODUCT + seckillId, seckillProductVo.getEndTime() - new Date().getTime(), TimeUnit.MILLISECONDS); 134 | // 将库存单独存入一个key中 135 | if (stringRedisTemplate.opsForValue().get(RedisKey.SECKILL_PRODUCT_STOCK + seckillId) == null) { 136 | stringRedisTemplate.opsForValue().set(RedisKey.SECKILL_PRODUCT_STOCK + seckillId, seckillProductVo.getSeckillStock() + "", seckillProductVo.getEndTime() - new Date().getTime(), TimeUnit.MILLISECONDS); 137 | } 138 | } catch (Exception e) { 139 | e.printStackTrace(); 140 | } 141 | return seckillProductVo; 142 | } 143 | return null; 144 | } 145 | 146 | /** 147 | * 秒杀 148 | * 149 | * @param seckillId 150 | */ 151 | @Transactional 152 | public void seckillProduct(String seckillId, Integer userId) { 153 | 154 | //这里改造成lua脚本 来判断是否售空 155 | /* if (localOverMap.get(seckillId) != null && localOverMap.get(seckillId)) { 156 | // 售空 157 | throw new XmException(ExceptionEnum.GET_SECKILL_IS_OVER); 158 | } */ 159 | 160 | // 判断秒杀是否开始, 防止路径暴露被刷 161 | Map m = redisTemplate.opsForHash().entries(RedisKey.SECKILL_PRODUCT + seckillId); 162 | if (!m.isEmpty()) { 163 | SeckillProductVo seckillProductVo = null; 164 | try { 165 | seckillProductVo = BeanUtil.map2bean(m, SeckillProductVo.class); 166 | } catch (Exception e) { 167 | e.printStackTrace(); 168 | } 169 | // 秒杀开始时间 170 | Long startTime = seckillProductVo.getStartTime(); 171 | 172 | if (startTime > new Date().getTime()) { 173 | throw new XmException(ExceptionEnum.GET_SECKILL_IS_NOT_START); 174 | } 175 | } 176 | 177 | // Lua 脚本代码 178 | String luaScript = "if (redis.call('get', KEYS[1]) == false or tonumber(redis.call('get', KEYS[1])) <= 0) then return -1 else return redis.call('decr', KEYS[1]) end"; 179 | 180 | // 执行脚本 181 | Long result = stringRedisTemplate.execute( 182 | (RedisCallback) connection -> connection.eval(luaScript.getBytes(), 183 | ReturnType.INTEGER, 1, (RedisKey.SECKILL_PRODUCT_STOCK + seckillId).getBytes()) 184 | ); 185 | 186 | if (result == -1) { 187 | // 秒杀完成,库存为空 188 | throw new XmException(ExceptionEnum.GET_SECKILL_IS_OVER); 189 | } 190 | 191 | /* // 判断是否已经秒杀到了,避免一个账户秒杀多个商品 192 | List list = redisTemplate.opsForList().range(RedisKey.SECKILL_PRODUCT_USER_LIST + seckillId, 0, -1); 193 | if (list.contains(String.valueOf(userId))) { 194 | throw new XmException(ExceptionEnum.GET_SECKILL_IS_REUSE); 195 | } */ 196 | 197 | // 预减库存:从缓存中减去库存 198 | // 利用redis中的方法,减去库存,返回值为减去1之后的值 199 | /* if (stringRedisTemplate.opsForValue().decrement(RedisKey.SECKILL_PRODUCT_STOCK + seckillId) < 0) { 200 | // 设置内存标记 201 | localOverMap.put(seckillId, true); 202 | // 秒杀完成,库存为空 203 | throw new XmException(ExceptionEnum.GET_SECKILL_IS_OVER); 204 | } */ 205 | 206 | // 使用RabbitMQ异步传输 207 | mqSend(seckillId, userId); 208 | 209 | } 210 | 211 | private void mqSend(String seckillId, Integer userId) { 212 | HashMap map = new HashMap<>(); 213 | map.put("seckillId", seckillId); 214 | map.put("userId", userId.toString()); 215 | // 设置ID,保证消息队列幂等性 216 | CorrelationData correlationData = new CorrelationData(); 217 | String correlationId = seckillId + ":" + userId; 218 | correlationData.setId(correlationId); 219 | try { 220 | // 使用 MessagePostProcessor 来设置消息的 correlationId 属性 221 | rabbitTemplate.convertAndSend("seckill_order", map, message -> { 222 | message.getMessageProperties().setCorrelationId(correlationId); 223 | message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT); // 设置消息持久化 224 | return message; 225 | }, correlationData); 226 | } catch (AmqpException e) { 227 | // 发送消息失败 228 | e.printStackTrace(); 229 | stringRedisTemplate.opsForValue().increment(RedisKey.SECKILL_PRODUCT_STOCK + seckillId); 230 | } 231 | } 232 | 233 | public Long getEndTime(String seckillId) { 234 | SeckillProductVo seckill = seckillProductMapper.getSeckill(seckillId); 235 | return seckillTimeMapper.getEndTime(seckill.getTimeId()); 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /xiaomi.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Navicat Premium Data Transfer 3 | 4 | Source Server : 47.95.254.97 5 | Source Server Type : MySQL 6 | Source Server Version : 50728 7 | Source Host : 47.95.254.97:3306 8 | Source Schema : xiaomi 9 | 10 | Target Server Type : MySQL 11 | Target Server Version : 50728 12 | File Encoding : 65001 13 | 14 | Date: 08/06/2020 20:53:52 15 | */ 16 | 17 | SET NAMES utf8mb4; 18 | SET FOREIGN_KEY_CHECKS = 0; 19 | 20 | -- ---------------------------- 21 | -- Table structure for carousel 22 | -- ---------------------------- 23 | DROP TABLE IF EXISTS `carousel`; 24 | CREATE TABLE `carousel` ( 25 | `carousel_id` int(11) NOT NULL AUTO_INCREMENT, 26 | `img_path` char(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, 27 | `describes` char(50) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, 28 | PRIMARY KEY (`carousel_id`) USING BTREE 29 | ) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; 30 | 31 | -- ---------------------------- 32 | -- Records of carousel 33 | -- ---------------------------- 34 | INSERT INTO `carousel` VALUES (1, 'http://47.115.85.237:3000/public/imgs/cms_1.jpg', NULL); 35 | INSERT INTO `carousel` VALUES (2, 'http://47.115.85.237:3000/public/imgs/cms_2.jpg', NULL); 36 | INSERT INTO `carousel` VALUES (3, 'http://47.115.85.237:3000/public/imgs/cms_3.jpg', NULL); 37 | INSERT INTO `carousel` VALUES (4, 'http://47.115.85.237:3000/public/imgs/cms_4.jpg', NULL); 38 | 39 | -- ---------------------------- 40 | -- Table structure for category 41 | -- ---------------------------- 42 | DROP TABLE IF EXISTS `category`; 43 | CREATE TABLE `category` ( 44 | `category_id` int(11) NOT NULL AUTO_INCREMENT, 45 | `category_name` char(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, 46 | PRIMARY KEY (`category_id`) USING BTREE 47 | ) ENGINE = InnoDB AUTO_INCREMENT = 9 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; 48 | 49 | -- ---------------------------- 50 | -- Records of category 51 | -- ---------------------------- 52 | INSERT INTO `category` VALUES (1, '手机'); 53 | INSERT INTO `category` VALUES (2, '电视机'); 54 | INSERT INTO `category` VALUES (3, '空调'); 55 | INSERT INTO `category` VALUES (4, '洗衣机'); 56 | INSERT INTO `category` VALUES (5, '保护套'); 57 | INSERT INTO `category` VALUES (6, '保护膜'); 58 | INSERT INTO `category` VALUES (7, '充电器'); 59 | INSERT INTO `category` VALUES (8, '充电宝'); 60 | 61 | -- ---------------------------- 62 | -- Table structure for collect 63 | -- ---------------------------- 64 | DROP TABLE IF EXISTS `collect`; 65 | CREATE TABLE `collect` ( 66 | `id` int(11) NOT NULL AUTO_INCREMENT, 67 | `user_id` int(11) NOT NULL, 68 | `product_id` int(11) NOT NULL, 69 | `collect_time` bigint(20) NOT NULL, 70 | PRIMARY KEY (`id`) USING BTREE 71 | ) ENGINE = InnoDB AUTO_INCREMENT = 12 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; 72 | 73 | -- ---------------------------- 74 | -- Records of collect 75 | -- ---------------------------- 76 | INSERT INTO `collect` VALUES (5, 5, 3, 1588781316596); 77 | INSERT INTO `collect` VALUES (6, 7, 32, 1588915938813); 78 | INSERT INTO `collect` VALUES (7, 9, 2, 1589119650819); 79 | INSERT INTO `collect` VALUES (8, 11, 3, 1589530747758); 80 | INSERT INTO `collect` VALUES (9, 12, 3, 1589613693008); 81 | INSERT INTO `collect` VALUES (10, 15, 26, 1590674262137); 82 | INSERT INTO `collect` VALUES (11, 19, 5, 1591543772605); 83 | 84 | -- ---------------------------- 85 | -- Table structure for order 86 | -- ---------------------------- 87 | DROP TABLE IF EXISTS `order`; 88 | CREATE TABLE `order` ( 89 | `id` int(11) NOT NULL AUTO_INCREMENT, 90 | `order_id` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, 91 | `user_id` int(11) NOT NULL, 92 | `product_id` int(11) NOT NULL, 93 | `product_num` int(11) NOT NULL, 94 | `product_price` double NOT NULL, 95 | `order_time` bigint(20) NOT NULL, 96 | PRIMARY KEY (`id`) USING BTREE 97 | ) ENGINE = InnoDB AUTO_INCREMENT = 13 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; 98 | 99 | -- ---------------------------- 100 | -- Records of order 101 | -- ---------------------------- 102 | INSERT INTO `order` VALUES (11, '1253562115243917312', 1, 3, 1, 2599, 1587707472989); 103 | INSERT INTO `order` VALUES (12, '1253562575002550272', 1, 3, 1, 2599, 1587707582604); 104 | 105 | -- ---------------------------- 106 | -- Table structure for product 107 | -- ---------------------------- 108 | DROP TABLE IF EXISTS `product`; 109 | CREATE TABLE `product` ( 110 | `product_id` int(11) NOT NULL AUTO_INCREMENT, 111 | `product_name` char(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, 112 | `category_id` int(11) NOT NULL, 113 | `product_title` char(30) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, 114 | `product_intro` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, 115 | `product_picture` char(200) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, 116 | `product_price` double NOT NULL, 117 | `product_selling_price` double NOT NULL, 118 | `product_num` int(11) NOT NULL, 119 | `product_sales` int(11) NOT NULL, 120 | PRIMARY KEY (`product_id`) USING BTREE 121 | ) ENGINE = InnoDB AUTO_INCREMENT = 36 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; 122 | 123 | -- ---------------------------- 124 | -- Records of product 125 | -- ---------------------------- 126 | INSERT INTO `product` VALUES (1, 'Redmi K30', 1, '120Hz流速屏,全速热爱', '120Hz高帧率流速屏/ 索尼6400万前后六摄 / 6.67\'小孔径全面屏 / 最高可选8GB+256GB大存储 / 高通骁龙730G处理器 / 3D四曲面玻璃机身 / 4500mAh+27W快充 / 多功能NFC', 'public/imgs/phone/Redmi-k30.png', 2000, 1599, 10, 0); 127 | INSERT INTO `product` VALUES (2, 'Redmi K30 5G', 1, '双模5G,120Hz流速屏', '双模5G / 三路并发 / 高通骁龙765G / 7nm 5G低功耗处理器 / 120Hz高帧率流速屏 / 6.67\'小孔径全面屏 / 索尼6400万前后六摄 / 最高可选8GB+256GB大存储 / 4500mAh+30W快充 / 3D四曲面玻璃机身 / 多功能NFC', 'public/imgs/phone/Redmi-k30-5G.png', 2599, 2599, 10, 0); 128 | INSERT INTO `product` VALUES (3, '小米CC9 Pro', 1, '1亿像素,五摄四闪', '1亿像素主摄 / 全场景五摄像头 / 四闪光灯 / 3200万自拍 / 10 倍混合光学变焦,50倍数字变焦 / 5260mAh ⼤电量 / 标配 30W疾速快充 / ⼩米⾸款超薄屏下指纹 / 德国莱茵低蓝光认证 / 多功能NFC / 红外万能遥控 / 1216超线性扬声器', 'public/imgs/phone/Mi-CC9.png', 2799, 2599, 18, 2); 129 | INSERT INTO `product` VALUES (4, 'Redmi 8', 1, '5000mAh超长续航', '5000mAh超长续航 / 高通骁龙439八核处理器 / 4GB+64GB', 'public/imgs/phone/Redmi-8.png', 799, 699, 20, 0); 130 | INSERT INTO `product` VALUES (5, 'Redmi 8A', 1, '5000mAh超长续航', '5000mAh超长续航 / 高通骁龙439八核处理器 / 4GB+64GB / 1200万AI后置相机', 'public/imgs/phone/Redmi-8A.png', 599, 699, 20, 0); 131 | INSERT INTO `product` VALUES (6, 'Redmi Note8 Pro', 1, '6400万全场景四摄', '6400万四摄小金刚拍照新旗舰超强解析力,超震撼', 'public/imgs/phone/Redmi-Note8-pro.png', 1399, 1199, 20, 0); 132 | INSERT INTO `product` VALUES (7, 'Redmi Note8', 1, '千元4800万四摄', '千元4800万四摄 | 高通骁龙665 | 小金刚品质保证', 'public/imgs/phone/Redmi-Note8.png', 999, 999, 20, 0); 133 | INSERT INTO `product` VALUES (8, 'Redmi 7A', 1, '小巧大电量 持久流畅', '小巧大电量,持久又流畅骁龙8核高性能处理器 | 4000mAh超长续航 | AI人脸解锁 | 整机防泼溅', 'public/imgs/phone/Redmi-7A.png', 599, 539, 20, 0); 134 | INSERT INTO `product` VALUES (9, '小米电视4A 32英寸', 2, '人工智能系统,高清液晶屏', '小米电视4A 32英寸 | 64位四核处理器 | 1GB+4GB大内存 | 人工智能系统', 'public/imgs/appliance/MiTv-4A-32.png', 799, 799, 10, 0); 135 | INSERT INTO `product` VALUES (10, '小米全面屏电视E55A', 2, '全面屏设计,人工智能语音', '全面屏设计 | 内置小爱同学 | 4K + HDR | 杜比DTS | PatchWall | 海量内容 | 2GB + 8GB大存储 | 64位四核处理器', 'public/imgs/appliance/MiTv-E55A.png', 2099, 1899, 10, 0); 136 | INSERT INTO `product` VALUES (11, '小米全面屏电视E65A', 2, '全面屏设计,人工智能语音', '人工智能语音系统 | 海量影视内容 | 4K 超高清屏 | 杜比音效 | 64位四核处理器 | 2GB + 8GB大存储', 'public/imgs/appliance/MiTv-E65A.png', 3999, 2799, 10, 0); 137 | INSERT INTO `product` VALUES (12, '小米电视4X 43英寸', 2, 'FHD全高清屏,人工智能语音', '人工智能语音系统 | FHD全高清屏 | 64位四核处理器 | 海量片源 | 1GB+8GB大内存 | 钢琴烤漆', 'public/imgs/appliance/MiTv-4X-43.png', 1499, 1299, 10, 0); 138 | INSERT INTO `product` VALUES (13, '小米电视4C 55英寸', 2, '4K HDR,人工智能系统', '人工智能 | 大内存 | 杜比音效 | 超窄边 | 4K HDR | 海量片源 | 纤薄机身| 钢琴烤漆', 'public/imgs/appliance/MiTv-4C-55.png', 1999, 1799, 8, 2); 139 | INSERT INTO `product` VALUES (14, '小米电视4A 65英寸', 2, '4K HDR,人工智能系统', '人工智能 | 大内存 | 杜比音效 | 超窄边 | 4K HDR | 海量片源 | 纤薄机身| 钢琴烤漆', 'public/imgs/appliance/MiTv-4A-65.png', 2999, 2799, 10, 0); 140 | INSERT INTO `product` VALUES (15, '小米壁画电视 65英寸', 2, '壁画外观,全面屏,远场语音', '纯平背板 | 通体13.9mm | 远场语音 | 4K+HDR | 杜比+DTS | 画框模式 | 内置小爱同学 | PatchWall | SoundBar+低音炮 | 四核处理器+大存储', 'public/imgs/appliance/MiTv-ArtTv-65.png', 6999, 6999, 10, 0); 141 | INSERT INTO `product` VALUES (16, '米家互联网空调C1(一级能效)', 3, '变频节能省电,自清洁', '一级能效 | 1.5匹 | 全直流变频 | 高效制冷/热 | 静音设计 | 自清洁 | 全屋互联', 'public/imgs/appliance/AirCondition-V1C1.png', 2699, 2599, 20, 10); 142 | INSERT INTO `product` VALUES (17, '米家空调', 3, '出众静音,快速制冷热', '大1匹 | 三级能效 | 静音 | 快速制冷热 | 广角送风 | 除湿功能 | 高密度过滤网 | 典雅外观', 'public/imgs/appliance/AirCondition-F3W1.png', 1799, 1699, 20, 8); 143 | INSERT INTO `product` VALUES (18, '米家互联网洗烘一体机 Pro 10kg', 4, '智能洗烘,省心省力', '国标双A+级洗烘能力 / 22种洗烘模式 / 智能投放洗涤剂 / 支持小爱同学语音遥控 / 支持OTA在线智能升级 / 智能空气洗 / 除菌率达99.9%+', 'public/imgs/appliance/Washer-Pro-10.png', 2999, 2999, 20, 7); 144 | INSERT INTO `product` VALUES (19, 'Redmi K20/ K20 Pro 怪力魔王保护壳', 5, '怪力魔王专属定制', '优选PC材料,强韧张力,经久耐用 / 精选开孔,全面贴合机身 / 手感轻薄细腻,舒适无负担 / 三款颜色可选,彰显个性,与众不同', 'public/imgs/accessory/protectingShell-RedMi-K20&pro.png', 39, 39, 20, 10); 145 | INSERT INTO `product` VALUES (20, '小米9 ARE U OK保护壳', 5, '一个与众不同的保护壳', '彰显独特个性 / 轻薄无负担 / 优选PC材料 / 贴合机身 / 潮流五色', 'public/imgs/accessory/protectingShell-Mi-9.png', 49, 39, 19, 11); 146 | INSERT INTO `product` VALUES (21, '小米CC9&小米CC9美图定制版 标准高透贴膜', 6, '高清透亮,耐磨性强', '耐磨性强,防护更出众 / 疏油疏水,有效抗水抗脏污 / 高清透亮,保留了原生屏幕的亮丽颜色和清晰度 / 操作灵敏,智能吸附 / 进口高端PET材质,裸机般手感', 'public/imgs/accessory/protectingMen-Mi-CC9.png', 19, 19, 20, 9); 147 | INSERT INTO `product` VALUES (22, '小米CC9e 标准高透贴膜', 6, '高清透亮,耐磨性强', '耐磨性强,防护更出众 / 疏油疏水,有效抗水抗脏污 / 高清透亮,保留了原生屏幕的亮丽颜色和清晰度 / 操作灵敏,智能吸附 / 进口高端PET材质,裸机般手感', 'public/imgs/accessory/protectingMen-Mi-CC9e.png', 19, 19, 20, 9); 148 | INSERT INTO `product` VALUES (23, '小米USB充电器30W快充版(1A1C)', 7, '多一种接口,多一种选择', '双口输出 / 30W 输出 / 可折叠插脚 / 小巧便携', 'public/imgs/accessory/charger-30w.png', 59, 59, 20, 8); 149 | INSERT INTO `product` VALUES (24, '小米USB充电器快充版(18W)', 7, '安卓、苹果设备均可使用', '支持QC3.0设备充电 / 支持iOS设备充电/ 美观耐用', 'public/imgs/accessory/charger-18w.png', 29, 29, 20, 8); 150 | INSERT INTO `product` VALUES (25, '小米USB充电器60W快充版(6口)', 7, '6口输出,USB-C输出接口', '6口输出 / USB-C输出接口 / 支持QC3.0快充协议 / 总输出功率60W', 'public/imgs/accessory/charger-60w.png', 129, 129, 20, 0); 151 | INSERT INTO `product` VALUES (26, '小米USB充电器36W快充版(2口)', 7, '6多重安全保护,小巧便携', '双USB输出接口 / 支持QC3.0快充协议 / 多重安全保护 / 小巧便携', 'public/imgs/accessory/charger-36w.png', 59, 59, 20, 0); 152 | INSERT INTO `product` VALUES (27, '小米车载充电器快充版', 7, '让爱车成为移动充电站', 'QC3.0 双口输出 / 智能温度控制 / 5重安全保护 / 兼容iOS&Android设备', 'public/imgs/accessory/charger-car.png', 69, 69, 20, 0); 153 | INSERT INTO `product` VALUES (28, '小米车载充电器快充版(37W)', 7, '双口快充,车载充电更安全', '单口27W 快充 / 双口输出 / 多重安全保护', 'public/imgs/accessory/charger-car-37w.png', 49, 49, 20, 0); 154 | INSERT INTO `product` VALUES (29, '小米二合一移动电源(充电器)', 7, '一个设备多种用途', '5000mAh充沛电量 / 多协议快充 / USB 口输出', 'public/imgs/accessory/charger-tio.png', 99, 99, 20, 0); 155 | INSERT INTO `product` VALUES (30, '小米无线充电宝青春版10000mAh', 8, '能量满满,无线有线都能充', '10000mAh大容量 / 支持边充边放 / 有线无线都能充 / 双向快充', 'public/imgs/accessory/charger-10000mAh.png', 129, 129, 20, 8); 156 | INSERT INTO `product` VALUES (31, '小米CC9 Pro/尊享版 撞色飘带保护壳', 5, '全面贴合,无感更舒适', '优选环保PC材质,强韧张力,全面贴合,无感更舒适', 'public/imgs/accessory/protectingShell-Mi-CC9Pro.png', 69, 69, 20, 0); 157 | INSERT INTO `product` VALUES (32, 'Redmi K20系列 幻境之心保护壳', 5, '柔软坚韧,全面贴合', '优质环保材质,柔软坚韧 / 全面贴合,有效抵抗灰尘 / 鲜亮三种颜色可选,为爱机随时换装', 'public/imgs/accessory/protectingShell-RedMi-K20.png', 39, 39, 20, 0); 158 | INSERT INTO `product` VALUES (33, '小米9 SE 街头风保护壳黑色', 5, '个性时尚,细节出众', '个性时尚 / 细节出众 / 纤薄轻巧 / 多彩时尚', 'public/imgs/accessory/protectingShell-Mi-9SE.png', 49, 49, 20, 0); 159 | INSERT INTO `product` VALUES (34, '小米9 街头风保护壳 红色', 5, '个性时尚,细节出众', '时尚多彩 / 细节出众 / 纤薄轻巧 / 是腕带也是支架', 'public/imgs/accessory/protectingShell-Mi-9-red.png', 49, 49, 20, 0); 160 | INSERT INTO `product` VALUES (35, '小米MIX 3 极简保护壳蓝色', 5, '时尚简约设计,手感细腻顺滑', '时尚简约设计,手感细腻顺滑 / 优质环保PC原材料,强韧张力,经久耐用 / 超薄 0.8MM厚度', 'public/imgs/accessory/protectingShell-Mix-3.png', 49, 12.9, 20, 0); 161 | 162 | -- ---------------------------- 163 | -- Table structure for product_picture 164 | -- ---------------------------- 165 | DROP TABLE IF EXISTS `product_picture`; 166 | CREATE TABLE `product_picture` ( 167 | `id` int(11) NOT NULL AUTO_INCREMENT, 168 | `product_id` int(11) NOT NULL, 169 | `product_picture` char(200) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, 170 | `intro` text CHARACTER SET utf8 COLLATE utf8_general_ci, 171 | PRIMARY KEY (`id`) USING BTREE 172 | ) ENGINE = InnoDB AUTO_INCREMENT = 111 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; 173 | 174 | -- ---------------------------- 175 | -- Records of product_picture 176 | -- ---------------------------- 177 | INSERT INTO `product_picture` VALUES (1, 1, 'public/imgs/phone/picture/Redmi-k30-1.png', NULL); 178 | INSERT INTO `product_picture` VALUES (2, 1, 'public/imgs/phone/picture/Redmi-k30-2.png', NULL); 179 | INSERT INTO `product_picture` VALUES (3, 1, 'public/imgs/phone/picture/Redmi-k30-3.png', NULL); 180 | INSERT INTO `product_picture` VALUES (4, 1, 'public/imgs/phone/picture/Redmi-k30-4.png', NULL); 181 | INSERT INTO `product_picture` VALUES (5, 1, 'public/imgs/phone/picture/Redmi-k30-5.png', NULL); 182 | INSERT INTO `product_picture` VALUES (6, 2, 'public/imgs/phone/picture/Redmi K30 5G-1.jpg', NULL); 183 | INSERT INTO `product_picture` VALUES (7, 2, 'public/imgs/phone/picture/Redmi K30 5G-2.jpg', NULL); 184 | INSERT INTO `product_picture` VALUES (8, 2, 'public/imgs/phone/picture/Redmi K30 5G-3.jpg', NULL); 185 | INSERT INTO `product_picture` VALUES (9, 2, 'public/imgs/phone/picture/Redmi K30 5G-4.jpg', NULL); 186 | INSERT INTO `product_picture` VALUES (10, 2, 'public/imgs/phone/picture/Redmi K30 5G-5.jpg', NULL); 187 | INSERT INTO `product_picture` VALUES (11, 3, 'public/imgs/phone/picture/MI CC9 Pro-1.jpg', NULL); 188 | INSERT INTO `product_picture` VALUES (12, 3, 'public/imgs/phone/picture/MI CC9 Pro-2.jpg', NULL); 189 | INSERT INTO `product_picture` VALUES (13, 3, 'public/imgs/phone/picture/MI CC9 Pro-3.jpg', NULL); 190 | INSERT INTO `product_picture` VALUES (14, 3, 'public/imgs/phone/picture/MI CC9 Pro-4.jpg', NULL); 191 | INSERT INTO `product_picture` VALUES (15, 3, 'public/imgs/phone/picture/MI CC9 Pro-5.jpg', NULL); 192 | INSERT INTO `product_picture` VALUES (16, 3, 'public/imgs/phone/picture/MI CC9 Pro-6.jpg', NULL); 193 | INSERT INTO `product_picture` VALUES (17, 4, 'public/imgs/phone/picture/Redmi 8-1.jpg', NULL); 194 | INSERT INTO `product_picture` VALUES (18, 4, 'public/imgs/phone/picture/Redmi 8-2.jpg', NULL); 195 | INSERT INTO `product_picture` VALUES (19, 4, 'public/imgs/phone/picture/Redmi 8-3.jpg', NULL); 196 | INSERT INTO `product_picture` VALUES (20, 4, 'public/imgs/phone/picture/Redmi 8-4.jpg', NULL); 197 | INSERT INTO `product_picture` VALUES (21, 4, 'public/imgs/phone/picture/Redmi 8-5.jpg', NULL); 198 | INSERT INTO `product_picture` VALUES (22, 5, 'public/imgs/phone/picture/Redmi 8A-1.jpg', NULL); 199 | INSERT INTO `product_picture` VALUES (23, 6, 'public/imgs/phone/picture/Redmi Note8 Pro-1.png', NULL); 200 | INSERT INTO `product_picture` VALUES (24, 6, 'public/imgs/phone/picture/Redmi Note8 Pro-2.png', NULL); 201 | INSERT INTO `product_picture` VALUES (25, 6, 'public/imgs/phone/picture/Redmi Note8 Pro-3.png', NULL); 202 | INSERT INTO `product_picture` VALUES (26, 6, 'public/imgs/phone/picture/Redmi Note8 Pro-4.png', NULL); 203 | INSERT INTO `product_picture` VALUES (27, 6, 'public/imgs/phone/picture/Redmi Note8 Pro-5.png', NULL); 204 | INSERT INTO `product_picture` VALUES (28, 7, 'public/imgs/phone/picture/Redmi Note8-1.jpg', NULL); 205 | INSERT INTO `product_picture` VALUES (29, 7, 'public/imgs/phone/picture/Redmi Note8-2.jpg', NULL); 206 | INSERT INTO `product_picture` VALUES (30, 7, 'public/imgs/phone/picture/Redmi Note8-3.jpg', NULL); 207 | INSERT INTO `product_picture` VALUES (31, 7, 'public/imgs/phone/picture/Redmi Note8-4.jpg', NULL); 208 | INSERT INTO `product_picture` VALUES (32, 7, 'public/imgs/phone/picture/Redmi Note8-5.jpg', NULL); 209 | INSERT INTO `product_picture` VALUES (33, 8, 'public/imgs/phone/picture/Redmi 7A-1.jpg', NULL); 210 | INSERT INTO `product_picture` VALUES (34, 8, 'public/imgs/phone/picture/Redmi 7A-2.jpg', NULL); 211 | INSERT INTO `product_picture` VALUES (35, 8, 'public/imgs/phone/picture/Redmi 7A-3.jpg', NULL); 212 | INSERT INTO `product_picture` VALUES (36, 8, 'public/imgs/phone/picture/Redmi 7A-4.jpg', NULL); 213 | INSERT INTO `product_picture` VALUES (37, 8, 'public/imgs/phone/picture/Redmi 7A-5.jpg', NULL); 214 | INSERT INTO `product_picture` VALUES (38, 9, 'public/imgs/phone/picture/MiTv-4A-32-1.jpg', NULL); 215 | INSERT INTO `product_picture` VALUES (39, 9, 'public/imgs/phone/picture/MiTv-4A-32-2.jpg', NULL); 216 | INSERT INTO `product_picture` VALUES (40, 9, 'public/imgs/phone/picture/MiTv-4A-32-3.jpg', NULL); 217 | INSERT INTO `product_picture` VALUES (41, 9, 'public/imgs/phone/picture/MiTv-4A-32-4.jpg', NULL); 218 | INSERT INTO `product_picture` VALUES (42, 10, 'public/imgs/phone/picture/MiTv-E55A-1.jpg', NULL); 219 | INSERT INTO `product_picture` VALUES (43, 10, 'public/imgs/phone/picture/MiTv-E55A-2.jpg', NULL); 220 | INSERT INTO `product_picture` VALUES (44, 10, 'public/imgs/phone/picture/MiTv-E55A-3.jpg', NULL); 221 | INSERT INTO `product_picture` VALUES (45, 10, 'public/imgs/phone/picture/MiTv-E55A-4.jpg', NULL); 222 | INSERT INTO `product_picture` VALUES (46, 11, 'public/imgs/phone/picture/MiTv-E65A-1.jpg', NULL); 223 | INSERT INTO `product_picture` VALUES (47, 11, 'public/imgs/phone/picture/MiTv-E65A-2.jpg', NULL); 224 | INSERT INTO `product_picture` VALUES (48, 11, 'public/imgs/phone/picture/MiTv-E65A-3.jpg', NULL); 225 | INSERT INTO `product_picture` VALUES (49, 11, 'public/imgs/phone/picture/MiTv-E65A-4.jpg', NULL); 226 | INSERT INTO `product_picture` VALUES (50, 12, 'public/imgs/phone/picture/MiTv-4X 43-1.jpg', NULL); 227 | INSERT INTO `product_picture` VALUES (51, 12, 'public/imgs/phone/picture/MiTv-4X 43-2.jpg', NULL); 228 | INSERT INTO `product_picture` VALUES (52, 12, 'public/imgs/phone/picture/MiTv-4X 43-3.jpg', NULL); 229 | INSERT INTO `product_picture` VALUES (53, 13, 'public/imgs/phone/picture/MiTv-4C 55-1.jpg', NULL); 230 | INSERT INTO `product_picture` VALUES (54, 13, 'public/imgs/phone/picture/MiTv-4C 55-2.jpg', NULL); 231 | INSERT INTO `product_picture` VALUES (55, 13, 'public/imgs/phone/picture/MiTv-4C 55-3.jpg', NULL); 232 | INSERT INTO `product_picture` VALUES (56, 14, 'public/imgs/phone/picture/MiTv-4A 65-1.jpg', NULL); 233 | INSERT INTO `product_picture` VALUES (57, 15, 'public/imgs/phone/picture/MiTv-ArtTv-65-1.jpg', NULL); 234 | INSERT INTO `product_picture` VALUES (58, 16, 'public/imgs/phone/picture/AirCondition-V1C1-1.jpg', NULL); 235 | INSERT INTO `product_picture` VALUES (59, 17, 'public/imgs/phone/picture/AirCondition-F3W1-1.jpg', NULL); 236 | INSERT INTO `product_picture` VALUES (60, 18, 'public/imgs/phone/picture/Washer-Pro-10-1.jpg', NULL); 237 | INSERT INTO `product_picture` VALUES (61, 18, 'public/imgs/phone/picture/Washer-Pro-10-2.jpg', NULL); 238 | INSERT INTO `product_picture` VALUES (62, 18, 'public/imgs/phone/picture/Washer-Pro-10-3.jpg', NULL); 239 | INSERT INTO `product_picture` VALUES (63, 18, 'public/imgs/phone/picture/Washer-Pro-10-4.jpg', NULL); 240 | INSERT INTO `product_picture` VALUES (64, 19, 'public/imgs/phone/picture/protectingShell-RedMi-K20&pro-1.jpg', NULL); 241 | INSERT INTO `product_picture` VALUES (65, 20, 'public/imgs/phone/picture/protectingShell-Mi-9-1.jpg', NULL); 242 | INSERT INTO `product_picture` VALUES (66, 20, 'public/imgs/phone/picture/protectingShell-Mi-9-2.jpg', NULL); 243 | INSERT INTO `product_picture` VALUES (67, 21, 'public/imgs/phone/picture/protectingMen-Mi-CC9-1.jpg', NULL); 244 | INSERT INTO `product_picture` VALUES (68, 22, 'public/imgs/phone/picture/protectingMen-Mi-CC9e-1.jpg', NULL); 245 | INSERT INTO `product_picture` VALUES (69, 23, 'public/imgs/phone/picture/charger-30w-1.jpg', NULL); 246 | INSERT INTO `product_picture` VALUES (70, 23, 'public/imgs/phone/picture/charger-30w-2.jpg', NULL); 247 | INSERT INTO `product_picture` VALUES (71, 23, 'public/imgs/phone/picture/charger-30w-3.jpg', NULL); 248 | INSERT INTO `product_picture` VALUES (72, 23, 'public/imgs/phone/picture/charger-30w-4.jpg', NULL); 249 | INSERT INTO `product_picture` VALUES (73, 24, 'public/imgs/phone/picture/charger-18w-1.jpg', NULL); 250 | INSERT INTO `product_picture` VALUES (74, 24, 'public/imgs/phone/picture/charger-18w-2.jpg', NULL); 251 | INSERT INTO `product_picture` VALUES (75, 24, 'public/imgs/phone/picture/charger-18w-3.jpg', NULL); 252 | INSERT INTO `product_picture` VALUES (76, 25, 'public/imgs/phone/picture/charger-60w-1.jpg', NULL); 253 | INSERT INTO `product_picture` VALUES (77, 25, 'public/imgs/phone/picture/charger-60w-2.jpg', NULL); 254 | INSERT INTO `product_picture` VALUES (78, 25, 'public/imgs/phone/picture/charger-60w-3.jpg', NULL); 255 | INSERT INTO `product_picture` VALUES (79, 25, 'public/imgs/phone/picture/charger-60w-4.jpg', NULL); 256 | INSERT INTO `product_picture` VALUES (80, 26, 'public/imgs/phone/picture/charger-36w-1.jpg', NULL); 257 | INSERT INTO `product_picture` VALUES (81, 26, 'public/imgs/phone/picture/charger-36w-2.jpg', NULL); 258 | INSERT INTO `product_picture` VALUES (82, 26, 'public/imgs/phone/picture/charger-36w-3.jpg', NULL); 259 | INSERT INTO `product_picture` VALUES (83, 26, 'public/imgs/phone/picture/charger-36w-4.jpg', NULL); 260 | INSERT INTO `product_picture` VALUES (84, 26, 'public/imgs/phone/picture/charger-36w-5.jpg', NULL); 261 | INSERT INTO `product_picture` VALUES (85, 27, 'public/imgs/phone/picture/charger-car-1.jpg', NULL); 262 | INSERT INTO `product_picture` VALUES (86, 27, 'public/imgs/phone/picture/charger-car-2.jpg', NULL); 263 | INSERT INTO `product_picture` VALUES (87, 27, 'public/imgs/phone/picture/charger-car-3.jpg', NULL); 264 | INSERT INTO `product_picture` VALUES (88, 27, 'public/imgs/phone/picture/charger-car-4.jpg', NULL); 265 | INSERT INTO `product_picture` VALUES (89, 27, 'public/imgs/phone/picture/charger-car-5.jpg', NULL); 266 | INSERT INTO `product_picture` VALUES (90, 27, 'public/imgs/phone/picture/charger-car-6.jpg', NULL); 267 | INSERT INTO `product_picture` VALUES (91, 28, 'public/imgs/phone/picture/charger-car-37w-1.jpg', NULL); 268 | INSERT INTO `product_picture` VALUES (92, 28, 'public/imgs/phone/picture/charger-car-37w-2.jpg', NULL); 269 | INSERT INTO `product_picture` VALUES (93, 28, 'public/imgs/phone/picture/charger-car-37w-3.jpg', NULL); 270 | INSERT INTO `product_picture` VALUES (94, 28, 'public/imgs/phone/picture/charger-car-37w-4.jpg', NULL); 271 | INSERT INTO `product_picture` VALUES (95, 28, 'public/imgs/phone/picture/charger-car-37w-5.jpg', NULL); 272 | INSERT INTO `product_picture` VALUES (96, 29, 'public/imgs/phone/picture/charger-tio-1.jpg', NULL); 273 | INSERT INTO `product_picture` VALUES (97, 29, 'public/imgs/phone/picture/charger-tio-2.jpg', NULL); 274 | INSERT INTO `product_picture` VALUES (98, 29, 'public/imgs/phone/picture/charger-tio-3.jpg', NULL); 275 | INSERT INTO `product_picture` VALUES (99, 29, 'public/imgs/phone/picture/charger-tio-4.jpg', NULL); 276 | INSERT INTO `product_picture` VALUES (100, 29, 'public/imgs/phone/picture/charger-tio-5.jpg', NULL); 277 | INSERT INTO `product_picture` VALUES (101, 30, 'public/imgs/phone/picture/charger-10000mAh-1.jpg', NULL); 278 | INSERT INTO `product_picture` VALUES (102, 30, 'public/imgs/phone/picture/charger-10000mAh-2.jpg', NULL); 279 | INSERT INTO `product_picture` VALUES (103, 30, 'public/imgs/phone/picture/charger-10000mAh-3.jpg', NULL); 280 | INSERT INTO `product_picture` VALUES (104, 30, 'public/imgs/phone/picture/charger-10000mAh-4.jpg', NULL); 281 | INSERT INTO `product_picture` VALUES (105, 30, 'public/imgs/phone/picture/charger-10000mAh-5.jpg', NULL); 282 | INSERT INTO `product_picture` VALUES (106, 31, 'public/imgs/phone/picture/protectingShell-Mi-CC9Pro-1.jpg', NULL); 283 | INSERT INTO `product_picture` VALUES (107, 32, 'public/imgs/phone/picture/protectingShell-RedMi-K20-1.jpg', NULL); 284 | INSERT INTO `product_picture` VALUES (108, 33, 'public/imgs/phone/picture/protectingShell-Mi-9SE-1.jpg', NULL); 285 | INSERT INTO `product_picture` VALUES (109, 34, 'public/imgs/phone/picture/protectingShell-Mi-9-red-1.jpg', NULL); 286 | INSERT INTO `product_picture` VALUES (110, 35, 'public/imgs/phone/picture/protectingShell-Mix-3-1.jpg', NULL); 287 | 288 | -- ---------------------------- 289 | -- Table structure for seckill_product 290 | -- ---------------------------- 291 | DROP TABLE IF EXISTS `seckill_product`; 292 | CREATE TABLE `seckill_product` ( 293 | `seckill_id` int(11) NOT NULL AUTO_INCREMENT, 294 | `product_id` int(11) NOT NULL, 295 | `seckill_price` double DEFAULT NULL, 296 | `seckill_stock` int(11) DEFAULT NULL, 297 | `time_id` int(11) DEFAULT NULL, 298 | PRIMARY KEY (`seckill_id`) USING BTREE 299 | ) ENGINE = InnoDB AUTO_INCREMENT = 9127 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; 300 | 301 | -- ---------------------------- 302 | -- Records of seckill_product 303 | -- ---------------------------- 304 | INSERT INTO `seckill_product` VALUES (8947, 32, 1000, 100, 694); 305 | INSERT INTO `seckill_product` VALUES (8948, 1, 1000, 100, 694); 306 | INSERT INTO `seckill_product` VALUES (8949, 2, 1000, 100, 694); 307 | INSERT INTO `seckill_product` VALUES (8950, 3, 1000, 100, 694); 308 | INSERT INTO `seckill_product` VALUES (8951, 10, 1000, 100, 694); 309 | INSERT INTO `seckill_product` VALUES (8952, 12, 1000, 100, 694); 310 | INSERT INTO `seckill_product` VALUES (8953, 14, 1000, 100, 694); 311 | INSERT INTO `seckill_product` VALUES (8954, 17, 1000, 100, 694); 312 | INSERT INTO `seckill_product` VALUES (8955, 18, 1000, 100, 694); 313 | INSERT INTO `seckill_product` VALUES (8956, 20, 1000, 100, 694); 314 | INSERT INTO `seckill_product` VALUES (8957, 21, 1000, 100, 694); 315 | INSERT INTO `seckill_product` VALUES (8958, 22, 1000, 100, 694); 316 | INSERT INTO `seckill_product` VALUES (8959, 24, 1000, 100, 694); 317 | INSERT INTO `seckill_product` VALUES (8960, 26, 1000, 100, 694); 318 | INSERT INTO `seckill_product` VALUES (8961, 27, 1000, 100, 694); 319 | INSERT INTO `seckill_product` VALUES (8962, 32, 1000, 100, 695); 320 | INSERT INTO `seckill_product` VALUES (8963, 4, 1000, 100, 695); 321 | INSERT INTO `seckill_product` VALUES (8964, 5, 1000, 100, 695); 322 | INSERT INTO `seckill_product` VALUES (8965, 6, 1000, 100, 695); 323 | INSERT INTO `seckill_product` VALUES (8966, 7, 1000, 100, 695); 324 | INSERT INTO `seckill_product` VALUES (8967, 9, 1000, 100, 695); 325 | INSERT INTO `seckill_product` VALUES (8968, 10, 1000, 100, 695); 326 | INSERT INTO `seckill_product` VALUES (8969, 12, 1000, 100, 695); 327 | INSERT INTO `seckill_product` VALUES (8970, 15, 1000, 100, 695); 328 | INSERT INTO `seckill_product` VALUES (8971, 16, 1000, 100, 695); 329 | INSERT INTO `seckill_product` VALUES (8972, 22, 1000, 100, 695); 330 | INSERT INTO `seckill_product` VALUES (8973, 25, 1000, 100, 695); 331 | INSERT INTO `seckill_product` VALUES (8974, 26, 1000, 100, 695); 332 | INSERT INTO `seckill_product` VALUES (8975, 29, 1000, 100, 695); 333 | INSERT INTO `seckill_product` VALUES (8976, 30, 1000, 100, 695); 334 | INSERT INTO `seckill_product` VALUES (8977, 2, 1000, 100, 696); 335 | INSERT INTO `seckill_product` VALUES (8978, 35, 1000, 100, 696); 336 | INSERT INTO `seckill_product` VALUES (8979, 6, 1000, 100, 696); 337 | INSERT INTO `seckill_product` VALUES (8980, 9, 1000, 100, 696); 338 | INSERT INTO `seckill_product` VALUES (8981, 10, 1000, 100, 696); 339 | INSERT INTO `seckill_product` VALUES (8982, 11, 1000, 100, 696); 340 | INSERT INTO `seckill_product` VALUES (8983, 15, 1000, 100, 696); 341 | INSERT INTO `seckill_product` VALUES (8984, 17, 1000, 100, 696); 342 | INSERT INTO `seckill_product` VALUES (8985, 18, 1000, 100, 696); 343 | INSERT INTO `seckill_product` VALUES (8986, 19, 1000, 100, 696); 344 | INSERT INTO `seckill_product` VALUES (8987, 26, 1000, 100, 696); 345 | INSERT INTO `seckill_product` VALUES (8988, 27, 1000, 100, 696); 346 | INSERT INTO `seckill_product` VALUES (8989, 28, 1000, 100, 696); 347 | INSERT INTO `seckill_product` VALUES (8990, 29, 1000, 100, 696); 348 | INSERT INTO `seckill_product` VALUES (8991, 31, 1000, 100, 696); 349 | INSERT INTO `seckill_product` VALUES (8992, 32, 1000, 100, 697); 350 | INSERT INTO `seckill_product` VALUES (8993, 33, 1000, 100, 697); 351 | INSERT INTO `seckill_product` VALUES (8994, 2, 1000, 100, 697); 352 | INSERT INTO `seckill_product` VALUES (8995, 35, 1000, 100, 697); 353 | INSERT INTO `seckill_product` VALUES (8996, 3, 1000, 100, 697); 354 | INSERT INTO `seckill_product` VALUES (8997, 8, 1000, 100, 697); 355 | INSERT INTO `seckill_product` VALUES (8998, 9, 1000, 100, 697); 356 | INSERT INTO `seckill_product` VALUES (8999, 12, 1000, 100, 697); 357 | INSERT INTO `seckill_product` VALUES (9000, 14, 1000, 100, 697); 358 | INSERT INTO `seckill_product` VALUES (9001, 17, 1000, 100, 697); 359 | INSERT INTO `seckill_product` VALUES (9002, 18, 1000, 100, 697); 360 | INSERT INTO `seckill_product` VALUES (9003, 19, 1000, 100, 697); 361 | INSERT INTO `seckill_product` VALUES (9004, 27, 1000, 100, 697); 362 | INSERT INTO `seckill_product` VALUES (9005, 28, 1000, 100, 697); 363 | INSERT INTO `seckill_product` VALUES (9006, 31, 1000, 100, 697); 364 | INSERT INTO `seckill_product` VALUES (9007, 32, 1000, 100, 698); 365 | INSERT INTO `seckill_product` VALUES (9008, 33, 1000, 100, 698); 366 | INSERT INTO `seckill_product` VALUES (9009, 4, 1000, 100, 698); 367 | INSERT INTO `seckill_product` VALUES (9010, 8, 1000, 100, 698); 368 | INSERT INTO `seckill_product` VALUES (9011, 10, 1000, 100, 698); 369 | INSERT INTO `seckill_product` VALUES (9012, 11, 1000, 100, 698); 370 | INSERT INTO `seckill_product` VALUES (9013, 17, 1000, 100, 698); 371 | INSERT INTO `seckill_product` VALUES (9014, 18, 1000, 100, 698); 372 | INSERT INTO `seckill_product` VALUES (9015, 21, 1000, 100, 698); 373 | INSERT INTO `seckill_product` VALUES (9016, 22, 1000, 100, 698); 374 | INSERT INTO `seckill_product` VALUES (9017, 25, 1000, 100, 698); 375 | INSERT INTO `seckill_product` VALUES (9018, 26, 1000, 100, 698); 376 | INSERT INTO `seckill_product` VALUES (9019, 28, 1000, 100, 698); 377 | INSERT INTO `seckill_product` VALUES (9020, 30, 1000, 100, 698); 378 | INSERT INTO `seckill_product` VALUES (9021, 31, 1000, 100, 698); 379 | INSERT INTO `seckill_product` VALUES (9022, 34, 1000, 100, 699); 380 | INSERT INTO `seckill_product` VALUES (9023, 3, 1000, 100, 699); 381 | INSERT INTO `seckill_product` VALUES (9024, 4, 1000, 100, 699); 382 | INSERT INTO `seckill_product` VALUES (9025, 6, 1000, 100, 699); 383 | INSERT INTO `seckill_product` VALUES (9026, 7, 1000, 100, 699); 384 | INSERT INTO `seckill_product` VALUES (9027, 9, 1000, 100, 699); 385 | INSERT INTO `seckill_product` VALUES (9028, 15, 1000, 100, 699); 386 | INSERT INTO `seckill_product` VALUES (9029, 17, 1000, 100, 699); 387 | INSERT INTO `seckill_product` VALUES (9030, 19, 1000, 100, 699); 388 | INSERT INTO `seckill_product` VALUES (9031, 20, 1000, 100, 699); 389 | INSERT INTO `seckill_product` VALUES (9032, 22, 1000, 100, 699); 390 | INSERT INTO `seckill_product` VALUES (9033, 23, 1000, 100, 699); 391 | INSERT INTO `seckill_product` VALUES (9034, 26, 1000, 100, 699); 392 | INSERT INTO `seckill_product` VALUES (9035, 29, 1000, 100, 699); 393 | INSERT INTO `seckill_product` VALUES (9036, 31, 1000, 100, 699); 394 | INSERT INTO `seckill_product` VALUES (9037, 32, 1000, 100, 700); 395 | INSERT INTO `seckill_product` VALUES (9038, 1, 1000, 100, 700); 396 | INSERT INTO `seckill_product` VALUES (9039, 33, 1000, 100, 700); 397 | INSERT INTO `seckill_product` VALUES (9040, 2, 1000, 100, 700); 398 | INSERT INTO `seckill_product` VALUES (9041, 35, 1000, 100, 700); 399 | INSERT INTO `seckill_product` VALUES (9042, 3, 1000, 100, 700); 400 | INSERT INTO `seckill_product` VALUES (9043, 5, 1000, 100, 700); 401 | INSERT INTO `seckill_product` VALUES (9044, 6, 1000, 100, 700); 402 | INSERT INTO `seckill_product` VALUES (9045, 8, 1000, 100, 700); 403 | INSERT INTO `seckill_product` VALUES (9046, 18, 1000, 100, 700); 404 | INSERT INTO `seckill_product` VALUES (9047, 20, 1000, 100, 700); 405 | INSERT INTO `seckill_product` VALUES (9048, 21, 1000, 100, 700); 406 | INSERT INTO `seckill_product` VALUES (9049, 24, 1000, 100, 700); 407 | INSERT INTO `seckill_product` VALUES (9050, 25, 1000, 100, 700); 408 | INSERT INTO `seckill_product` VALUES (9051, 26, 1000, 100, 700); 409 | INSERT INTO `seckill_product` VALUES (9052, 32, 1000, 100, 701); 410 | INSERT INTO `seckill_product` VALUES (9053, 33, 1000, 100, 701); 411 | INSERT INTO `seckill_product` VALUES (9054, 2, 1000, 100, 701); 412 | INSERT INTO `seckill_product` VALUES (9055, 4, 1000, 100, 701); 413 | INSERT INTO `seckill_product` VALUES (9056, 5, 1000, 100, 701); 414 | INSERT INTO `seckill_product` VALUES (9057, 7, 1000, 100, 701); 415 | INSERT INTO `seckill_product` VALUES (9058, 8, 1000, 100, 701); 416 | INSERT INTO `seckill_product` VALUES (9059, 9, 1000, 100, 701); 417 | INSERT INTO `seckill_product` VALUES (9060, 11, 1000, 100, 701); 418 | INSERT INTO `seckill_product` VALUES (9061, 16, 1000, 100, 701); 419 | INSERT INTO `seckill_product` VALUES (9062, 20, 1000, 100, 701); 420 | INSERT INTO `seckill_product` VALUES (9063, 23, 1000, 100, 701); 421 | INSERT INTO `seckill_product` VALUES (9064, 26, 1000, 100, 701); 422 | INSERT INTO `seckill_product` VALUES (9065, 28, 1000, 100, 701); 423 | INSERT INTO `seckill_product` VALUES (9066, 30, 1000, 100, 701); 424 | INSERT INTO `seckill_product` VALUES (9067, 1, 1000, 100, 702); 425 | INSERT INTO `seckill_product` VALUES (9068, 34, 1000, 100, 702); 426 | INSERT INTO `seckill_product` VALUES (9069, 35, 1000, 100, 702); 427 | INSERT INTO `seckill_product` VALUES (9070, 4, 1000, 100, 702); 428 | INSERT INTO `seckill_product` VALUES (9071, 7, 1000, 100, 702); 429 | INSERT INTO `seckill_product` VALUES (9072, 8, 1000, 100, 702); 430 | INSERT INTO `seckill_product` VALUES (9073, 9, 1000, 100, 702); 431 | INSERT INTO `seckill_product` VALUES (9074, 15, 1000, 100, 702); 432 | INSERT INTO `seckill_product` VALUES (9075, 17, 1000, 100, 702); 433 | INSERT INTO `seckill_product` VALUES (9076, 18, 1000, 100, 702); 434 | INSERT INTO `seckill_product` VALUES (9077, 19, 1000, 100, 702); 435 | INSERT INTO `seckill_product` VALUES (9078, 21, 1000, 100, 702); 436 | INSERT INTO `seckill_product` VALUES (9079, 22, 1000, 100, 702); 437 | INSERT INTO `seckill_product` VALUES (9080, 23, 1000, 100, 702); 438 | INSERT INTO `seckill_product` VALUES (9081, 27, 1000, 100, 702); 439 | INSERT INTO `seckill_product` VALUES (9082, 1, 1000, 100, 703); 440 | INSERT INTO `seckill_product` VALUES (9083, 33, 1000, 100, 703); 441 | INSERT INTO `seckill_product` VALUES (9084, 3, 1000, 100, 703); 442 | INSERT INTO `seckill_product` VALUES (9085, 35, 1000, 100, 703); 443 | INSERT INTO `seckill_product` VALUES (9086, 6, 1000, 100, 703); 444 | INSERT INTO `seckill_product` VALUES (9087, 10, 1000, 100, 703); 445 | INSERT INTO `seckill_product` VALUES (9088, 15, 1000, 100, 703); 446 | INSERT INTO `seckill_product` VALUES (9089, 16, 1000, 100, 703); 447 | INSERT INTO `seckill_product` VALUES (9090, 17, 1000, 100, 703); 448 | INSERT INTO `seckill_product` VALUES (9091, 19, 1000, 100, 703); 449 | INSERT INTO `seckill_product` VALUES (9092, 23, 1000, 100, 703); 450 | INSERT INTO `seckill_product` VALUES (9093, 25, 1000, 100, 703); 451 | INSERT INTO `seckill_product` VALUES (9094, 26, 1000, 100, 703); 452 | INSERT INTO `seckill_product` VALUES (9095, 29, 1000, 100, 703); 453 | INSERT INTO `seckill_product` VALUES (9096, 30, 1000, 100, 703); 454 | INSERT INTO `seckill_product` VALUES (9097, 1, 1000, 100, 704); 455 | INSERT INTO `seckill_product` VALUES (9098, 34, 1000, 100, 704); 456 | INSERT INTO `seckill_product` VALUES (9099, 6, 1000, 100, 704); 457 | INSERT INTO `seckill_product` VALUES (9100, 8, 1000, 100, 704); 458 | INSERT INTO `seckill_product` VALUES (9101, 9, 1000, 100, 704); 459 | INSERT INTO `seckill_product` VALUES (9102, 12, 1000, 100, 704); 460 | INSERT INTO `seckill_product` VALUES (9103, 14, 1000, 100, 704); 461 | INSERT INTO `seckill_product` VALUES (9104, 15, 1000, 100, 704); 462 | INSERT INTO `seckill_product` VALUES (9105, 16, 1000, 100, 704); 463 | INSERT INTO `seckill_product` VALUES (9106, 18, 1000, 100, 704); 464 | INSERT INTO `seckill_product` VALUES (9107, 23, 1000, 100, 704); 465 | INSERT INTO `seckill_product` VALUES (9108, 27, 1000, 100, 704); 466 | INSERT INTO `seckill_product` VALUES (9109, 29, 1000, 100, 704); 467 | INSERT INTO `seckill_product` VALUES (9110, 30, 1000, 100, 704); 468 | INSERT INTO `seckill_product` VALUES (9111, 31, 1000, 100, 704); 469 | INSERT INTO `seckill_product` VALUES (9112, 35, 1000, 100, 705); 470 | INSERT INTO `seckill_product` VALUES (9113, 5, 1000, 100, 705); 471 | INSERT INTO `seckill_product` VALUES (9114, 6, 1000, 100, 705); 472 | INSERT INTO `seckill_product` VALUES (9115, 7, 1000, 100, 705); 473 | INSERT INTO `seckill_product` VALUES (9116, 10, 1000, 100, 705); 474 | INSERT INTO `seckill_product` VALUES (9117, 13, 1000, 100, 705); 475 | INSERT INTO `seckill_product` VALUES (9118, 14, 1000, 100, 705); 476 | INSERT INTO `seckill_product` VALUES (9119, 16, 1000, 100, 705); 477 | INSERT INTO `seckill_product` VALUES (9120, 18, 1000, 100, 705); 478 | INSERT INTO `seckill_product` VALUES (9121, 19, 1000, 100, 705); 479 | INSERT INTO `seckill_product` VALUES (9122, 20, 1000, 100, 705); 480 | INSERT INTO `seckill_product` VALUES (9123, 23, 1000, 100, 705); 481 | INSERT INTO `seckill_product` VALUES (9124, 25, 1000, 100, 705); 482 | INSERT INTO `seckill_product` VALUES (9125, 26, 1000, 100, 705); 483 | INSERT INTO `seckill_product` VALUES (9126, 29, 1000, 100, 705); 484 | 485 | -- ---------------------------- 486 | -- Table structure for seckill_time 487 | -- ---------------------------- 488 | DROP TABLE IF EXISTS `seckill_time`; 489 | CREATE TABLE `seckill_time` ( 490 | `time_id` int(11) NOT NULL AUTO_INCREMENT, 491 | `start_time` bigint(20) DEFAULT NULL, 492 | `end_time` bigint(20) DEFAULT NULL, 493 | PRIMARY KEY (`time_id`) USING BTREE 494 | ) ENGINE = InnoDB AUTO_INCREMENT = 706 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; 495 | 496 | -- ---------------------------- 497 | -- Records of seckill_time 498 | -- ---------------------------- 499 | INSERT INTO `seckill_time` VALUES (694, 1591603200000, 1591606800000); 500 | INSERT INTO `seckill_time` VALUES (695, 1591610400000, 1591614000000); 501 | INSERT INTO `seckill_time` VALUES (696, 1591617600000, 1591621200000); 502 | INSERT INTO `seckill_time` VALUES (697, 1591624800000, 1591628400000); 503 | INSERT INTO `seckill_time` VALUES (698, 1591632000000, 1591635600000); 504 | INSERT INTO `seckill_time` VALUES (699, 1591639200000, 1591642800000); 505 | INSERT INTO `seckill_time` VALUES (700, 1591646400000, 1591650000000); 506 | INSERT INTO `seckill_time` VALUES (701, 1591653600000, 1591657200000); 507 | INSERT INTO `seckill_time` VALUES (702, 1591660800000, 1591664400000); 508 | INSERT INTO `seckill_time` VALUES (703, 1591668000000, 1591671600000); 509 | INSERT INTO `seckill_time` VALUES (704, 1591675200000, 1591678800000); 510 | INSERT INTO `seckill_time` VALUES (705, 1591682400000, 1591686000000); 511 | 512 | -- ---------------------------- 513 | -- Table structure for shopping_cart 514 | -- ---------------------------- 515 | DROP TABLE IF EXISTS `shopping_cart`; 516 | CREATE TABLE `shopping_cart` ( 517 | `id` int(11) NOT NULL AUTO_INCREMENT, 518 | `user_id` int(11) NOT NULL, 519 | `product_id` int(11) NOT NULL, 520 | `num` int(11) NOT NULL, 521 | PRIMARY KEY (`id`) USING BTREE 522 | ) ENGINE = InnoDB AUTO_INCREMENT = 33 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; 523 | 524 | -- ---------------------------- 525 | -- Records of shopping_cart 526 | -- ---------------------------- 527 | INSERT INTO `shopping_cart` VALUES (18, 1, 2, 4); 528 | INSERT INTO `shopping_cart` VALUES (19, 3, 30, 2); 529 | INSERT INTO `shopping_cart` VALUES (20, 3, 3, 2); 530 | INSERT INTO `shopping_cart` VALUES (21, 3, 4, 3); 531 | INSERT INTO `shopping_cart` VALUES (22, 4, 3, 1); 532 | INSERT INTO `shopping_cart` VALUES (23, 5, 3, 1); 533 | INSERT INTO `shopping_cart` VALUES (24, 6, 2, 1); 534 | INSERT INTO `shopping_cart` VALUES (25, 9, 2, 3); 535 | INSERT INTO `shopping_cart` VALUES (26, 11, 3, 1); 536 | INSERT INTO `shopping_cart` VALUES (27, 12, 3, 5); 537 | INSERT INTO `shopping_cart` VALUES (28, 6, 6, 2); 538 | INSERT INTO `shopping_cart` VALUES (29, 14, 2, 1); 539 | INSERT INTO `shopping_cart` VALUES (30, 16, 1, 1); 540 | INSERT INTO `shopping_cart` VALUES (31, 18, 3, 1); 541 | INSERT INTO `shopping_cart` VALUES (32, 19, 5, 1); 542 | 543 | -- ---------------------------- 544 | -- Table structure for user 545 | -- ---------------------------- 546 | DROP TABLE IF EXISTS `user`; 547 | CREATE TABLE `user` ( 548 | `user_id` int(11) NOT NULL AUTO_INCREMENT, 549 | `username` char(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, 550 | `password` char(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, 551 | `user_phone_number` char(11) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, 552 | PRIMARY KEY (`user_id`) USING BTREE, 553 | UNIQUE INDEX `userName`(`username`) USING BTREE 554 | ) ENGINE = InnoDB AUTO_INCREMENT = 20 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; 555 | 556 | -- ---------------------------- 557 | -- Records of user 558 | -- ---------------------------- 559 | INSERT INTO `user` VALUES (1, 'zerowdd', 'c51f9b1ec11855186a670e089d55a712', NULL); 560 | INSERT INTO `user` VALUES (2, 'testtest', '05a671c66aefea124cc08b76ea6d30bb', NULL); 561 | INSERT INTO `user` VALUES (3, 'a123456', 'dc483e80a7a0bd9ef71d8cf973673924', NULL); 562 | INSERT INTO `user` VALUES (4, 'ddwddw', '78c8b92f09f93f438b4146d345784724', NULL); 563 | INSERT INTO `user` VALUES (5, 'bababa', 'dc483e80a7a0bd9ef71d8cf973673924', NULL); 564 | INSERT INTO `user` VALUES (6, 'admin', 'dc483e80a7a0bd9ef71d8cf973673924', NULL); 565 | INSERT INTO `user` VALUES (7, 'xiaohehaokeai', '7248b7ace448685ea3e24160bf9cc85e', NULL); 566 | INSERT INTO `user` VALUES (8, 'abcde', 'e80b5017098950fc58aad83c8c14978e', NULL); 567 | INSERT INTO `user` VALUES (9, 'abcdef', 'e80b5017098950fc58aad83c8c14978e', NULL); 568 | INSERT INTO `user` VALUES (10, 'duanjiangtao', 'b61dc55947a309e0530314cee03d31bd', NULL); 569 | INSERT INTO `user` VALUES (11, 'zq789456', 'a32f8a465202f8a14ca514fbffdb4a14', NULL); 570 | INSERT INTO `user` VALUES (12, 'CCY_CCy', '5d93ceb70e2bf5daa84ec3d0cd2c731a', NULL); 571 | INSERT INTO `user` VALUES (13, 'l123456', 'c57562653c783faeb8b6cd917ef258c1', NULL); 572 | INSERT INTO `user` VALUES (14, 'yzw0323', '03918c2b726cb53556608c9da759d45b', NULL); 573 | INSERT INTO `user` VALUES (15, 'yanguo', '03918c2b726cb53556608c9da759d45b', NULL); 574 | INSERT INTO `user` VALUES (16, 'testtt', 'f55e23f49445a3cf708c19577f218a5b', NULL); 575 | INSERT INTO `user` VALUES (17, 'jayjethava1', '7762dc5d78cb8366208b0c05b6fb14ea', NULL); 576 | INSERT INTO `user` VALUES (18, 'lh235762524001', '6cfa955a82ad67f24f8daaf0e6f6e09e', NULL); 577 | INSERT INTO `user` VALUES (19, 'liyiming', '31e09a2697bff307006cfd52cc202270', NULL); 578 | 579 | SET FOREIGN_KEY_CHECKS = 1; 580 | --------------------------------------------------------------------------------