# secondKill **Repository Path**: thinkaboutai_admin/second-kill ## Basic Information - **Project Name**: secondKill - **Description**: 电商典型案例 —— 商品秒杀 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2022-08-22 - **Last Updated**: 2022-08-25 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 项目案例:商品秒杀 ## 需求描述 制作一个秒杀商品的项目,需要满足以下: - 用户通过页面上的购买连接,购买一个商品(数量为1,每一个顾客只能购买一个) - 有可能同时存在多个顾客购买 - 商品的总量是固定的 - 客户看到的按钮有可能点击了以后也买不到,当然没有库存的时候,不可以点击 ## Version 1.0 请制作一个基本能实现库存减少的版本(可以满足单用户多次购买),只需要满足一个单独的用户购买流程即可: - 用户的账户管理要合乎尝试 - 库存要能相应减少 ### 1.1 建模(2) 先创建数据库的表,然后把对应的实体类创建出来 - 用户表:用于记录用户的个人信息,它主要的作用是用来在当前的网站系统中来进行唯一标识的。 - 用户账户表:用于记录某一个用户在当前时刻的余额 - 商品表:用来记录商品的基本信息 - 订单表:用来记录某一个用户购买完商品之后的用户和商品的信息(连接信息) 动手开始创建DB实例(`schema`),通常来说,一个项目(中小型)一个`schema` 用户表 ```sql CREATE TABLE `user` ( `id` int NOT NULL AUTO_INCREMENT COMMENT '主键', `username` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '登录用户名', `password` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '登录密码', PRIMARY KEY (`id`) USING BTREE, UNIQUE INDEX `uk_username`(`username` ASC) USING BTREE COMMENT '登录用户名唯一索引' ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; ``` 用户账户表 ```sql CREATE TABLE `user_account` ( `id` int NOT NULL AUTO_INCREMENT COMMENT '主键', `user_id` int NOT NULL COMMENT '关联用户表的外键', `amount` int NOT NULL COMMENT '账户余额', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; ``` 商品表 ```sql CREATE TABLE `order` ( `id` int NOT NULL AUTO_INCREMENT COMMENT '主键', `user_id` int NOT NULL COMMENT '订单关联的用户编号', `product_id` int NOT NULL COMMENT '订单关联的商品编号', `amount` int NOT NULL COMMENT '用户购买商品的数量,在秒杀中默认为1', `create_time` datetime NOT NULL COMMENT '订单生成的时间', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; ``` 订单表 ```sql CREATE TABLE `order` ( `id` int NOT NULL AUTO_INCREMENT COMMENT '主键', `user_id` int NOT NULL COMMENT '订单关联的用户编号', `product_id` int NOT NULL COMMENT '订单关联的商品编号', `amount` int NOT NULL COMMENT '用户购买商品的数量,在秒杀中默认为1', `create_time` datetime NOT NULL COMMENT '订单生成的时间', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; ``` 实体类代码见项目 > 在订单表中,按照理论知识,应该创建2个外键约束,但是为了开发和测试的方便,我们一般不在DB中创建出来这个外键,而是使用Java逻辑保障它的外键关联性 ### 1.2 概要设计(2) 主要由有经验的开发人员完成,主要是根据对需求的理解,把用户需求中的描述翻译成开发人员自己的语言 我们自己先构造一个用户的使用场景: - 在首页(`index.jsp`)展示出当前商品的名称、单价和库存,提供一个按钮,让用户点击购买 - 当用户点击完这个按钮之后,会在后台完成商品的购买过程 - 最终购买成功以后,在结果页面(`result.jsp`)显示"购买成功",如果失败,提供一个简单的失败页面(`error.jsp`)。 在概要设计的过程中,无论是前端还是后端或是其它岗位的开发,都应该产出一些“东西”来检验这个流程和结果是否朝着健康的方向发展。 - 界面原型:Axure 产品、UI(界面设计)、前端开发 - 后台程序接口:后端 对于后端开发来说,我们一般会使用`interface`来完成“后台程序接口” > 为什么要用`interface`来做一步呢?直接写一个抽象类或者类,打打草稿不就行了吗 - 接口语言,已经逐渐编程不管是后台开发才懂的语言,而且我们可以借助一些开源的工具或者框架(swagger、伪接口语言),把`interface`转换成其他岗位能看懂的内容 - 开发人员在设计接口的过程中,主要思考输入有哪些、返回什么数据(格式)、大致的逻辑(尤其是核心逻辑:SQL),无论这个功能是否能实现,开发人员在这个环节能尽早发现问题 接口中的方法如下: ```java /** *
前端用户接口,该类主要用于前端请求直接调用的
*/ public interface SecondKillApi { // 我们首先把所有的有可能要调用的方法都汇总在这里,后面再根据需要划分为不同的模块 /** *主要用来呈现秒杀某商品的首页,主要用来呈现以下信息:
*该方法是用户秒杀入口
* @param productId 商品的id * @param userId 登录用户的id */ void kill(Integer productId, Integer userId); } ``` 一旦设计出前端用户API,那么后台人员就可以开始和前端开发人员对接《用户接口》 ```java /** *订单相关操作
*/ public interface OrderApi { /** *新增一条购买记录
* @param order 订单信息 */ void saveOrder(Order order); } /** *商品相关操作
*/ public interface ProductApi { /** *查询商品基本信息
* @param productId 商品id * @return 商品基本信息 */ Product findProductById(Integer productId); /** *更新商品的库存
* @param product 商品实例 */ void updateProduct(Product product); } /** *用户账户相关操作
*/ public interface UserAccountApi { /** *查询商品基本信息
* @param userId 用户账户的id * @return 用户账户信息 */ UserAccount findUserAccountById(Integer userId); /** *更新账户的余额,实现扣款
* @param userAccount 用户账户实例 */ void updateUserAccount(UserAccount userAccount); } ``` ### 1.3 详细设计(1) 详细设计直白的理解,就是对上述较为抽象的、笼统的用户接口,进行继续细化拆分,最终,把后续所需的每一个方法都大致定义出来,然后这些方法要做什么事情,都写好文档。 根据上述4个接口各自承担的职责以及相应的代码地位,作为开发人员来讲,很难从包名、类型名称上看出来这个类型主要职责是做什么的,或者说他们之间是否有区别,因此有必要根据代码的分层思想继续划分类似于部门、模块的单位进行代码的管理:  - web层:用于直接处理前端用户提交的请求 - api包中的接口,主要用于和前端开发人员对接、交付 - controller就是api的实现类 - service层:我们又称之为业务逻辑层、服务层,主要针对某一个特定的领域、模型、表做一些聚合度较高的相关操作,impl是业务层的具体实现方式 ### 1.4 编码(1) 请根据详细设计中的接口和相关实现类的规划,完成Version 1.0.0的需求 开辟多个线程(100个),同时调用SecondKillApi中的kill,最终查看执行结果: - 一共产生了多少订单 - 商品库存还剩多少 ## Version 2.0