网站建设必备条件,wordpress怎么做,碳晶板全屋装修的利和弊,老王传奇新开网站电子交易的一个很基本的问题#xff0c;就是避免用户下重复订单。用户明明想买一次#xff0c;结果一看下了两个单。如果没有及时发现#xff0c;就会带来额外的物流成本和扯皮。对商家的信誉也不好看。
从技术上看#xff0c;这是一个分布式一致性问题#xff1b;但实际…
电子交易的一个很基本的问题就是避免用户下重复订单。用户明明想买一次结果一看下了两个单。如果没有及时发现就会带来额外的物流成本和扯皮。对商家的信誉也不好看。
从技术上看这是一个分布式一致性问题但实际上技术无法100%解决这类问题得结合多种手段综合处理。这里就来说道说道。
为啥会下重了呢
原因1客户端bug
比如下单的按键在点按之后在没有收到服务器请求之前按键的状态没有设为已禁用状态还可以被按。又或者在触摸屏下用户手指的点按可能被手机操作系统识别为多次点击。
嗯谁能保证客户端不偶尔出个什么bug 呢。
原因2: 超时
用户的设备与服务器之间可能是不稳定的网路。这样一个下单请求过去返回不一定回得来。超时最大的问题是: 从用户的角度他无法确定下单的请求是还没到服务器还是已经到了服务器但是返回丢失了。——用户无法区分到底这个单下了还是没下。
这样在等待一个超时后UI可能会提示用户下单超时请重复再试。 下单超时
原因3: 用户的App闪退/人工强退之后重新打开重新下单
也许可以使用一些技术手段避免用户下重单但是心急的用户可能会重启流程/重启App/重启手机。在这种强制的手段下任何技术手段都会失效——用户压根就不让你的技术执行你怎么玩
在这些条件下如何避免用户多下了一笔订单呢
用幂等防止重复订单
在技术方面这是一个分布式一致性的问题即客户端和服务器端对某个订单是否成功/失败达成一致。防止重单的关键是使用一个由客户端生成的可用于避免重复的key俗称dedup keydeduplicate key之意。这个key可以用任意可以保证全局唯一性的方式生成比如uuid。客户端和服务器需要使用这个dedup key作为串联条件一起解决去重问题。
客户端的流程
客户端需要实现这样一个下单界面。用户点击【确认下单】时应该产生一个独一无二的dedup key连定订单数据发送给服务器端。在服务器返回之前该界面应该一直等待直到服务器响应成功/失败或者超时发生比如15秒后收不到服务器响应。如果超时发生应该向用户提示是否重试下单或者退出该界面。当用户点击【重试】时应该用刚刚生成的dedup key来再次发送下单请求——如果用户一直不退出这个流程每次用户点击重试都应该用这个dedup key来重试下单直到服务器正常返回或者用户放弃返回。 下单的客户端流程
后端数据表设计
后端在订单数据表中需要增加dedup_key这列并设置唯一约束。 create table order(# ...dedup_key varchar(60) not null comment key to pretend order duplication,# ...unique uniq_dedup_key(dedup_key)
);下单的实现
在实现下单逻辑时基于该dedup_key实现一个create-or-get语义的下单接口——简单说就是 如果带有指定dedup_key的订单已经存在则直接返回否则用该dedup_key下单。用伪代码表示大概是 Transactional
Order createOrder(Integer userId, String prodCode, Decimal amount, String dedupKey) {try {String orderId createOrder(userId, prodCode, amount, deupKey); // insert a new orderOrder order getOrderById(orderId); // read order from dborder.setDuplicated(false);return order;} catch(UniqueKeyViolationException e) {// if duplicated order has existedOrder order getOrderByDedupKey(dedupKey);order.setDuplicated(true);return order;} catch (Exception e) {// hanlde other errors and rollback transaction ...}
}这时这段下单代码总是能返回一个订单除非发生一些DB挂了之类的错误要么是新创建的要么就是一个已经存在的单。注意最好在订单里增加一个属性比如例子中用“duplicated”来表示这个订单是这次新生成的还是因为幂等而直接返回的。这样前端可以有针对性的对这两种情况提示不同的文案。
技术搞定幂等就足够了吗
上面的流程没有考虑一种情况就是用户中途强制退出客户端或者直接点击【返回】回到产品页重新走下单流程。这个时候客户端就无法判断用户到底是想重新下单还是想第二次下单。此时可以从产品设计上考虑一下。
比如在客户端缓存一个表记录所有没有确认结果的订单。 产品代码产品数量金额dedup key未确认订单1AAA11000xxx-yyy-zzz未确认订单2BBB2500.00Aaa-bbb-ccc... 通过这个表我们可以猜一下用户的意图。比如如果用户重新提交了一笔订单其产品代码、金额与表中记录的某条完全一致就可以提示一下用户: 提示一下用户是不是下重了
如果用户想重试可以继续用表中对应记录的dedup key重新发起下单。
这样不是绝对准确的仅仅是尽量的减少用户误操作的可能性。当然在产品设计上可以能出于用户交互简化不一定真的会这样做。这就需要其他机制来配合比如“通知”。
通知
一旦服务器下单成功可以通过某种通知机制如APNS、Websocket主动将订单推送至客户端强行让客户端重新拉取最新的订单信息并配合“未确认订单”表以通知Badge/弹框等方式提示用户刚刚一笔状态未知的订单成功/失败了。
另外一种手段就是服务器端实时扫描用户的下单数据一旦发现可能的重单就立刻通知客服主动联系用户及时处理问题。
如果还拦不住……
经过层层阻拦可能还是会有用户误操作直到收到两份商品才发现下重了。此时就得依靠运营/客服的支持了。提供用户申诉的手段让用户提出哪些订单是重复的并且由销售系统店家、商品提供者和买家三方共同根据用户操作的记录来协商如何处理。我们需要让技术帮助让这种人工处理的几率尽量小。因为每次处理都会耗费较大的人工成本和一些运营费用比如赔款、小礼品等等。
这么麻烦有必要吗
这要分业务场景对于很多电商来讲可能不是必要的。因为从用户下单到订单被审核处理进入到发货阶段需要一定的时间可能是半小时1小时并且一定是支付成功后才会开始进行下一步流程。在这个时间段用户大概率能从网络错误中恢复过来自行区分是否下重了。配合客服主动提示会极大的降低出问题的概率。
但是对于理财服务来说这种去重就非常必要了。因为
“下单支付”。用户购买理财往往是“下单支付”一起执行不可以单独下单/单独支付用户的入金可能很大。例如数万数十万准确性丢失。如果一旦下重了有可能影响用户的投资资金配置的准确性。撤销难。部分理财产品存在下单不可撤销的问题或者即便撤销资金也无法立刻回款。等到回款可能这个购入机会就错过去了。例如对于基金交易错过1个交易日价格就会发生变动。
基于这些特性在理财产品中就要竭尽全力的去重。
结论
以上所讲是处理重复订单问题的一般方法。你可以注意到无论多么好的技术也不可能100%的拦截所有的可能性必须依靠技术产品设计运营支持的综合手段才能解决这类问题。
另外本文还没涉及到关于订单支付支付也可能重复哦带来的进一步的复杂性也没有讨论在高并发情况下的性能优化仅仅讨论下单本身的问题。所以可以想象一下现实中的交易业务比这里的说的要复杂得多。
本文介绍的原理也不仅仅适用于防止下重复订单而是可以应用到任何需要“创建一个不应该重复资源”的场景比如“向用户发一条通知”“触发一次不能重复的批处理任务“…… 链接https://www.jianshu.com/p/e618cc818432 其它相对简单但并不完美的解决办法
比如客户端与服务端没有用一个唯一标识来做请求的标识符这是要怎么来解决重复下单问题呢
1 客户端在用户点击按钮发起请求后按钮要置为不可用
2 在服务端处理过程中要加锁如果是单实例的加本地缓存锁即可多个实例最好加分布式锁
部分代码如下 var isdoing Gd.CacheApi.UserCapital.GetUserConsumeLockCache(userId);if (isdoing 1){//加锁Gd.CacheApi.UserCapital.SetUserConsumeLockCache(userId);var result UserConsume(userId, consumeTypeId, consumeMoney, isAnonymous, wishContent, consumeNums, isShowWish, toUserName);call.Result result;if (result.ResultCode ! 0){call.Error result.ResultMsg;}else{call.Error ;}//移除锁Gd.CacheApi.UserCapital.RemoveUserConsumeLockCache(userId);}此种方法可以有效减少出现重复单但并不完美相对完美的还是上面的方案
--- end --- --------------------- 作者Jack2013tong 来源CSDN 原文https://blog.csdn.net/huwei2003/article/details/105712692 版权声明本文为作者原创文章转载请附上博文链接 内容解析ByCSDN,CNBLOG博客文章一键转载插件