Skip to content

To-knowledge/MicroSeckill

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

One SecKill Project

一个基于SpringBoot搭建的秒杀项目:rocket:,整体框架基于Data Access Object (DAO) -> Service -> Controller结构设计(本项目来源于慕课网免费课程)

技术基础

  • Spring Boot
  • Maven
  • MyBatis
  • MySQL
  • IDEA

Tips

  1. 为防止层与层之间的数据透传,每一层最好自己定义数据对象模型。
  2. 数据库表设计过程中,在建表过程中,可以将用户信息与其对应的密码信息分开建表,可以在后续业务运行过程中提升查询效率,减少不必要的信息的查询,同时也是为了安全。

基础框架之Controller层设计

  1. Controller层的统一异常处理,通过自定义一个异常处理类BusinessException实现,内部涉及一个异常信息接口CommonError以及一个异常信息枚举类EmBusinessError,处于controller.error包下。
  2. Controller层的统一返回信息定义,通过自定义类CommonReturnType实现,处于controller.response包下。

业务模型之Service层设计

用户模型管理

1.1 注册短信验证码获取

通过定义getOpt方法处理前端提交的请求,该方法需要处理的逻辑如下

public ReturnType getOpt(@RequestParam(name="telephone") String telephone){
	//按照一定规则生成验证码
	//将Opt验证码同用户手机号关联,一般采用Redis,此处先采用HttpSession实现
	httpServeletRequest.getSession.setAttribute(telephone, optCode);
	//将验证码通过短信通道发送给用户
}

此处的HttpSession可以通过自动装载HttpServletRequest(单例)得到,此处之所以可以存储多个用户信息,是由于其内部采用Threalocal实现多线程绑定,使得每个用户的先短信验证信息隔离。

1.2 前端页面简易处理

通过javascript响应事件,后端收到post请求时,调动getOpt方法进行处理。此处针对后端代码需要做如下处理:

  1. 完善RequestMapping注解的参数;
@RequestMapping(value = "/getOtp", method = {RequestMethod.POST}, consumes = {CONTENT_TYPE_FORMED})
  1. 加入跨域请求注解@CrossOrigin,加到UserController类上,加载到方法getOpt上会依然报错(后来被验证无效,在方法上加与不加一样),但是可以与后端产生交互;
//不设置参数的话,依然无法避免
@CrossOrigin(allowCredentials = "true", allowedHeaders = "*")
2.1 注册功能实现
public ReturnType register(//@RequestParam获取前端传入的数据){
	// 1.验证用户的手机号与验证码符合
	// 2.进入注册流程,分层设计
	// 3.返回注册成功信息
}

用户自增id不需要传入值,非自增字段,且没有设置默认值会导致mybatis报错。

为避免相同手机号多次注册,可以将telephone字段设为唯一索引。

3.1 登录功能实现

主要涉及通过用户手机号获取用户信息,需要在UserDOMapper.xml中加入相关的数据库查询代码,如下:

<select id="selectByTelephone" resultMap="BaseResultMap">
select
<include refid="Base_Column_List"/>
from user_info
where telephone = #{telephone, jdbcType=VARCHAR}
</select>

然后,在UserDO.java中加入对应的方法:

UserDO selectByTelephone(String telephone);

验证成功后,将登录凭证加入到用户登录成功的session内。

校验规则优化

  1. 调用Maven仓库中的Hibernate Validator Engine中的模块;
  2. 通过@NotBlank、@NotNull、@Max、@Min注解约束参数的范围

商品模型设计

优先设计领域模型(不能一上来就去数据库建表) 领域模型

public class ItemModel{
	// id字段
	private Integer id;
	//商品名称
	private String title;
	//价格
	private BigDecimal price;
	//库存
	private Integer stock;
	//商品描述
	private String description;
	//商品销量
	private Integer sales;
	//商品图片url
	private String url;
}

根据领域模型合理分表,创建对应表结构,最后通过mybatis-generator生成DOMapper.xml、DOMapper的java接口、DO类。

此处分出了两张表,一张商品信息(item)表,一张库存(item_stock)表,修改DOMopper.xml使得item的id与item_stock中的item_id关联。(删除线中的内容对于这一关联操作,是通过java代码实现的,先取出item的id,然后把它赋值给item_stock表的item_id字段)。

(ItemDOMapper.xml以及ItemStockMapper.xml)
(找到insert操作以及insertSelective操作) userGenertedKeys = "true" keyProperty = "id"(不是true) (此处的原因与id自增有关,既然如此,为何myBatis在设计的时候为啥不默认为true)(后来发现,如果不写,service层通过DO取不出来id值)
商品的service层接口

主要需要有创建商品、商品列表浏览、商品详情浏览等功能。

ItemModel createItem(ItemModel itemModel);
List<ItemModel> listItem();
ItemModel getItemById(Integer id);

创建Item的方法设计流程:

@Transactional 
public ItemModel createItem(ItemModel itemModel){
// 1.校验入参,通过先前定义的ValdationImpl实现
// 2.转化itemModel -> dataObject
// 3.写入数据库
// 4.返回创建完成的对象
// 注记:double在传到前端会有精度损失的问题,需要转化为BigDecmal
}

商品交易模型即订单模型

public class OrderModel{
	private String id;
	private Integer userId;
	private Integer itemId;
    private BigDecimal itemPrice;
	private Integer amount;
	private BigDecimal orderPrice;
}

根据领域模型定义数据库表,double类型长度为“x, y”。

OrderModel createOrder(用户, 商品, 数量){
	// 1.校验下单状态,商品是否存在,用户是否合法,购买数量是否正确
	// 2.落单减库存
	// 3.订单入库
	// 4.返回给Controller
}

新加入一个模型的一般操作流程

  1. 设计领域模型
  2. 根据领域模型建表(是否设计多张表)
  3. 使用mybatis-generator得到表对应的DOMapper.xml、DOMapper.java、以及DO数据模型
  4. 定义对应的service接口
  5. 实现service接口
  6. 实现对应Controller层的前端展示模型(VO)以及前端请求时需要的处理方法
  7. 绘制前端模板

期间对于一些新的数据库操作,可以去DOMapper.java以及DOMapper.xml中添加或者修改相应逻辑

<update id="updateByPrimaryKey" parameterType="com.miaoshaproject.dataobject.SequenceDO">
    <!--
      WARNING - @mbg.generated
      This element is automatically generated by MyBatis Generator, do not modify.
      This element was generated on Sun Jul 26 14:13:49 CST 2020.
    -->
    update sequence_info
    set step = #{step,jdbcType=INTEGER}
    <!--此处仅根据name匹配进行更新>
</update>
  <update id="updateByPrimaryKeySelective" parameterType="com.miaoshaproject.dataobject.SequenceDO">
  <!--
    WARNING - @mbg.generated
    This element is automatically generated by MyBatis Generator, do not modify.
    This element was generated on Sun Jul 26 14:13:49 CST 2020.
  -->
  update sequence_info
  <set>
    <if test="currentValue != null">
      current_value = #{currentValue,jdbcType=INTEGER},
    </if>
    <if test="step != null">
      step = #{step,jdbcType=INTEGER},
    </if>
  </set>
  where name = #{name,jdbcType=VARCHAR}
</update>
<update id="updateByPrimaryKey" parameterType="com.miaoshaproject.dataobject.SequenceDO">
  <!--
    WARNING - @mbg.generated
    This element is automatically generated by MyBatis Generator, do not modify.
    This element was generated on Sun Jul 26 14:13:49 CST 2020.
  -->
  update sequence_info
  set current_value = #{currentValue,jdbcType=INTEGER},step = #{step,jdbcType=INTEGER}
  where name = #{name,jdbcType=VARCHAR}
</update>

秒杀模型(PromoModel)

Field : id; promoName; startDate; endDate; itemId;(秒杀活动适用商品) promoItemPrice; 时间模型使用Joda;

秒杀活动与秒杀商品做聚合:将PromoModel聚合(嵌套)在ItemMOdel中。

PromoModel promoModel = promoService.getPromoModel(itemModel.getId());
if(promoModel != null && promoModel.getStatus().intValue() != 3)
	itemModel.setPromoModel(promoModel);

将数据库中的double类型转化为BigDecimal类型。 修改订单模型,在其中加入秒杀活动判断(通过前端传入的秒杀活动id判断是秒杀,还是平价)。

About

🚀 前后端分离的秒杀项目

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages