电商商城系统开发常见技术性问题

 2020-05-23 

随着用户群积累,社区的壮大,还有来自投资人对变现渴望的压力,似乎最容易想到的变现途径就是“我们也卖点东西吧”,如果直接给淘宝链接,会显得逼格太低,购买别人的系统,钱不少花,最后为了适应自己的需求,也要做相当多的工作,所以,越来越多不同的App里有了商城。当然根据不同的业务需求,复杂度也大相径庭。

这里还没有能力“大话电商系统”,只是把实际开发过程中遇到过的逻辑陷阱阐述一下,并逐级优化,给出一个这里认为的比较稳妥的方案,电商系统,博大精深,我提出的问题都比较小,并且很可能属于新手的坑。也欢迎读文章读你提出更多问题,或者更优方案。

问题1. 一个小保证,确保订单不会被恶意修改

看了文字,标题肯定觉得懵逼了。举个例子吧,例如用户A的订单已支付,用户B却可能将它变成申请退款。

怎么可能?

如果这个系统存在漏洞,并且B是一个愿意尝试的程序员!

之前Review过一些团队小伙伴的代码,简单来说,修改订单状态被描述成如下流程:

  1. 登陆验证等

  2. 通过POST接受到Client传过来的OrderID

  3. 修改订单

那么问题出现了,如果用户B利用技术手段发送了A订单的ID,会怎么样?如果系统没有做充分的校验工作,那么对不起,一个登录用户可以尝试所有数字,把所有订单都搞乱。

当然这是一件很简单的例子,当然优化的方案有很多,最简单的方案应该就是先获取订单

Order order = orderService.getOrderByIdAnOwner(orderId, ownerId)
这里获取订单同时增加了订单所有者的约束,以防止恶意更改别人的订单。

如果不涉及到其他的关于订单操作,也可以简简单单在更新的时候,确保订单所有者

Order order = orderService.updateOrderByIdAnOwner(orderId, ownerId)

  1. 扣库溢出问题(超卖问题)

之前有个朋友遇到过这个问题,他说他们销售的某些商品比较热销,导致很多人去哄抢,在停止哄抢的时候,却发现商品库存是负数。这应该是典型的超卖了吧。

如果没有过多的思考,扣库存的过程很容易写成如下这个样子:

Product product = findProductById(productId)
//库存足够
if(product.availableAmount > 0){
//做一些订单组装等工作
}
deduceProductAvailableAmount(product.id, buyAmount)
如果用户量很小,这段代码应该没有问题,如果用户变多,同一时刻有2个Tread同时运行这段代码,那么情况就很糟糕了,因为这段代码并不是线程安全的。

最简单的优化可能是

public synchronized void createOrder(userId, productId, buyAmount){

1
2
3
4
5
6
Product product = findProductById(productId)
//库存足够
if(product.availableAmount > 0){
    //做一些订单组装等工作
}
deduceProductAvailableAmount(product.id, buyAmount)

}
这样的做法牺牲效率,并且更严重的是,如果服务器分布式部署,那么还是不能解决问题。两台服务器也会并发遇到同样的问题。

方案一,一种比较常规的解决方案是利用数据库的行级锁,这里我们可以用

Select for update
语法获得数据条目,当然两个前提要保证:

  1. Service方法是Transactional

  2. 获取数据的方式是SelectByPrimaryKey,如果不是,会导致整个表被锁住。

@Transaction(readOnly=false)
public void createOrder(userId, productId, buyAmount){

1
2
3
4
5
6
Product product = findOrderByPrimaryKeyForUpdate(productId)
//库存足够
if(product.availableAmount > 0){
    //做一些订单组装等工作
}
deduceProductAvailableAmount(product.id, buyAmount)

}