您的位置:首页 > 编程语言 > PHP开发

[PHP]基于MySQL简单的库存量流程处理接口设计

2013-03-23 16:50 381 查看
因为iwebshop原生的库存流程不太理想,参见:

iwebshop库存量的设计缺点.txt(http://blog.csdn.net/gevolution90/article/details/8709706)

试用了一下ecshop的购买到下订单的流程,发现下了订单,并且在后台点了支付及配送后,库存量都没有变。不知是什么原因了,也懒得去看源代码了。

用“php|mysql 电子商务|商城 库存量 问题”关键词在百度,Google.hk,德问上进行搜索,没有找到太有价值的资料,于是自己设计了一下库存量流程的处理接口。

btw,这个页面有参考价值:

+商城并发抢购库存减至负数问题(http://www.dewen.org/q/10230/%E5%95%86%E5%9F%8E%E5%B9%B6%E5%8F%91%E6%8A%A2%E8%B4%AD%E5%BA%93%E5%AD%98%E5%87%8F%E8%87%B3%E8%B4%9F%E6%95%B0%E9%97%AE%E9%A2%98)

要解决的问题首先是避免iwebshop原生库存流程中库存量轻易就被做空,然后是避免超售现象(即库存变为负数)。除此之外,不再考虑其它复杂的情况,不考虑如高并发,大流量,分布式这类高级东西。意在为最基本的环境:单台服务器(比如虚拟主机),mysql甚至没有innodb引擎的情况下,提供具有一定事务性的库存量功能。

因此,最基本的设计原则是,事务完整性问题能交给数据库解决的全交给数据库(哪怕是MyISAM引擎),因为数据库有实现好的锁(管它是表锁还是行锁)。若MySQL支持InnoDB引擎,则直接使用InnoDB的事务功能(所以具体会有两个实现版本,一个MyISAM版本和一个InnoDB版本)。

所有接口使用一个类进行包装,每个接口就是类中的一个方法。

先分析库存量被清0的问题,在用户实际支付之前(不考虑货到付款的情况)就扣掉库存量都会有被恶意用户低成本清0库存的风险。所以参考一些电子商务网站的方法(比如万达电影在线购票),在选中要购买的物品后,有5分钟的支付有效时间,超过时间未成功支付,所占用的物品(比如影院坐位)会被还原。

电院坐位锁定释放的响应时间要求比较高,但库存量要求不至于这么高,所以容易实现很多。但是要考虑的另一个问题是一张购物订单常会包含多个商品。

使用两个数据表处理这个我称为“异步支付”的问题(即从开始支付到支付成功是有一定的时间间隔的):

异步ID表(store_asyn_id)

aid int 自增主键

timeout int 过期时间

索引:

timeout

异步库存量记录表(store_asyn_num)

anid int 自增主键

aid int 外键 store_asyn_id.aid

store_id int 外键 store.store_id

num smallint 库存变动数量

索引:

aid

store_id

这两个表在没有innodb提供事务功能时也用于模拟事务,大概用法是:

开始处理一张订单内商品的数量时,申请一个 aid (或重用一个已申请过的 aid),并设置一个较短的过期时间(比如1分钟内),每变动一个库存量便同时把相关数据写入 store_asyn_num 表,在所有库存变动完成后,再把过期时间设为0(表示成功)。

一但库存变动过程中意外中断,可以通过超时时间和 store_asyn_num 表的记录数据对库存量进行回滚(以模拟事务的回滚功能)。

同样的用法也用于异步支付,只要把过期时间设长一点(比如5~10分钟)即可。

这两个表是关键设计了,后面的就都是按步就班的普通设计。

使用一个表保存库存量相关的数据:

库存表(store)

store_id int 库存ID,自增主键

gid int 分组ID(比如可以用于记录商品ID)

sell int 出售库存(支付成功后扣除)

deliver int 发货库存(发货成功后扣除)

status tinyint 状态{0:正常, 1:锁定}

索引:

gid

库存ID由库存表自动生成,并反过来记录在货品数据中,以达到与具体商品或货品的分离,这样库存接口就可以用于任何一个对一个数量有并发事务要求的地方,比如团购。

库存量分两种,一种是支付成功后扣除的,表示出售用的库存量,另一个是发货成功后扣除的,表示仓库中实际的库存量(发货库存)。发货库存主要是后台管理中需要用到,不需要异步支付的概念。

一个被锁定的库存量相当于没有库存。

在用户下订单时,先检查一下订单中的货品库存量是否足够(单纯的检查,不修改库存量):

array check_sell($store)

$store array [store_id] => 所需扣除的数量

@return array {0:bool 检查是否通过, 1:array 检查不通过的库存ID和剩余数量, 2:array 所有检查的库存ID和剩余数量}

可以用 list($is_ok, $store_not, $store_all) = check_sell(); 进行赋值。返回这么多数据主要是给调用者生成错误提示信息。

后台发货时需要检查一下发货库存是否足够:

array check_deliver($store)

参数与返回值都与 check_sell 一致。

在货到付款类的订单中,需要直接扣除库存量:

array take_sell($store)

参数与返回值与 check_sell 一致。

异步支付型,要求在订单数据中记录对应的异步ID,先检查订单是否申请过异步ID,若未申请,则申请一个并记录回订单数据中:

int asyn_newid()

返回一个新的异步ID

然后开始异步支付:

array take_sell_asyn($aid, $store, $timeout)

$aid int 异步ID

$store array 参见 check_sell 同名参数。

$timeout int 过期时间戳

@return array 参见 check_sell 返回值。

当用户支付完成后,把一个异步ID标记为完成(所以异步ID需要记录在订单数据中):

bool asyn_done($aid)

取消一个货到付款类订单:

bool cancel_sell($store)

$store array 参见 check_sell 同名参数

取消一个异步支付ID(比如用户点击了支付,在未成功支付时再主动点击了取消订单):

bool cancel_asyn($aid)

后台发货:

array take_deliver($store)

参数与返回值参见 check_sell.

后台需要调整货品的库存量,不提供直接设置库存量值,只提供增加和减少两个功能:

增加出售库存:

bool plus_sell($store_id, $num)

增加发货库存:

bool plus_deliver($store_id, $num)

减少出售库存:

bool reduce_sell($store_id, $num)

减少发货库存:

bool reduce_deliver($store_id, $num)

提供一个冻结(锁定)库存的功能辅助在减少库存时使用,被冻结的库存等于没有库存(即不能被购买)。

冻结:

void lock($store_id)

解锁:

void unlick($store_id)

对异步表中的过期数据进行清理,因为 MyISAM 表不适合频繁的写操作(update/delete),因此建议定时执行清理(比如一小时一次):

asyn_clean()

被清理的库存量会回滚回库存表中。

asyn_repair($store_id)

针对一个库存ID进行清理,以快速回滚一个库存被占用但已过期的库存量。

asyn_get($aid)

取一个异步ID的过期时间。

asyn_activate($aid, $timeout)

重新激活一个过期的异步ID,只能激活已过期的ID,并且不能激活已完成和正在清理中的异步ID。

如果使用 MyISAM 引擎,可以把 update 语句写成一句提交的巧让 MySQL 处理并发锁的问题:

UPDATE t1, t2 SET t1.num=t1.num+t2.num, t2.num=0 WHERE t1.id=t2.id AND ...

如果需要先INSERT再UPDATE,可以INSERT一个数量为0的记录,再进行UPDATE操作。

若是可以使用InnoDB引擎,使用事务进行一组操作就可以了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐