vivo 全球商城:电商平台通用取货码设计

vivo官网商城开发团队 - Zhou Longjian

一、背景

随着O2O线上线下业务的不断扩展,电商平台也在逐步完善交易侧相关的产品功能。在最近的需求版本中,业务方为进一步提升用户的使用体验,规划了取货码生成及订单核销相关逻辑,目的是让线上的用户在付完款之后能够到店取货或者安排导购派送。

日常生活中,我们对取货码、核销这类功能使用的经历大部分都来自:看电影前取票、吃饭后出示券码、快递柜取包裹等等,它们都有一些类似的特点,比如:

  • 取货码长度相对较短,比起动辄十几二十位订单号,几位的数字码更方便记忆和输入;

  • 除了数字取货码,还提供二维码,方便终端进行扫描并核销。

取货码使用起很简单,然而像“冰山”一样,隐藏在简单外表下面却需要严谨的设计和细致的逻辑,可以说麻雀虽小五脏俱全。本文介绍的设计也比较有趣,而且按此思路可以实现市面上大多数核销类券码的生成,同时也能满足业务的SaaS化,算是一个相对通用的能力,在此把整个设计分享给大家。

(图片来源: pixabay.com)

二、简单系统的单表业务

如果业务的体量不大,店铺流量比较小,未形成平台的规模,比如给个体经营者使用的系统。那么取货码或券码的实现就比较简单,跟订单共享一张大横表或者使用扩展表跟订单进行关联就行了,这个阶段也无需做过度设计。

表的设计如下图:

不过需要注意的是一般订单号都是比较长的,通常都在十几二十位(当然也有比较短的订单号,如果订单号比较短,取货码也可采用订单号)我们假设订单号18位,取货码8位,即订单号的取值范围远大于取货码,那么在订单号的生命周期内,取货码是有很大几率存在重复的。解决起来相对简单,我们只需要保证在任意条件下,未核销状态的数字码不重复即可,也即已核销的数字码可以回收利用。

那么取货码的生成逻辑就很清晰了,下面用伪代码模拟真实的实现逻辑:

伪代码实现

for (;;) {step1 获取随机码:String code = this.getRandomCode();step2 执行SQL:SELECT COUNT(1) FROM order_main WHERE code = ${code} AND write_off_status = 0;step3 判断是否可以插入:if ( count > 0) { continue; }step4 执行数据写入:UPDATE order_main SET code = ${code}, qr_code = ${qrCode}, write_off_status = 0 WHERE order_no = ${orderNo}
}

*注意:这里step2和step4不是原子操作,存在并发问题,实际应用中最好使用分布式锁,把操作锁住。

三、 复杂平台的分库分表业务

通过简单的单表设计,我们能管窥一斑,了解取货码大致的实现逻辑。不过我们在把简单方案往大型项目上进行落地的时候,就需要考虑很多方面,设计也需要更精巧。SaaS化的电商平台会比简单的单表业务复杂很多,重点体现在:

  1. SaaS 产品涉及的店铺很多且订单量大,需要设计大容量存储,所以订单表基本使用分库分表,显然作为订单附属的取货码表也得使用相同的策略;

  2. B端和C端用户的体验非常重要,服务端接口的设计需要充分考虑鲁棒性,完善最基本的重试及容错能力;

  3. 不同业务方对于取货码的要求可能不太一样,取货码的设计需要具有通用性以及个性化的配置属性。

3.1 详细设计

取货码表的设计推荐使用和订单一致的分库分表策略,好处是:

  1. 和订单一样,支撑海量订单行的存储;

  2. 方便利用同样的分库分表因子进行查询(例如:open_id、member_id)。

在考虑落地实现上,我们遇到了第一个讨论的点,那就是取货码是做到“门店唯一”还是“全局唯一”?

3.2 门店唯一方案

刚开始考虑使用类似饭馆取餐码类似的逻辑,保证取货码在各自门店保持唯一就行了。类似如下图交互,图中用户A和用户B持有相同的取货码,用户A、B分别去他们对应的店铺完成核销,整个交易过程就结束了。但是这得保证用户A和B能正确地在各自订单归属的店铺完成核销,显然这个方案是带有风险的!

下图所示的这种情况下,用户A、B也能正常核销,不过串单了,原本属于用户A的订单被用户B核销了。这种问题出现的本质原因在于纯粹的数字码无法带有用户的标识,虽然可以在核销前做人为的核验身份来避免,但依然属于高风险的系统设计,所以门店唯一方案不可取!

3.3 全局唯一方案

全局唯一方案风险小,但实现难度稍高一点。核心问题在于如何判定随机生成的取货码是全局唯一的,当然如果系统本身依赖ES这类存储介质,可以在插入前先查询ES,不过查询和写入ES对于实时性接口来说稍微有点重,没有直接查库表来得直接。假设某业务方分成了4个库4张表,总计16表,取货码的长度确定为8位,那如何在多库多表的Mysql中查询并保证全局唯一呢?遍历表的方式肯定不可取!

为解决上述的疑问,我们在设计的时候可以在取货码的编排上做点文章,如下步骤做具体详解:

步骤①: 可以将8位的取货码分成两个区域,“随机码区域”+“库表位置”,下图示例:

步骤②: 随机码区域暂不介绍,我们来看下2位库表如何映射到4库4表组成的16张表中。

这里也有两套方案:

【方案一】可以选择2位库表的首位作为库编号,末位作为表编号。好处是映射较为简单,但是容量不够大,如果分的库或表>9,扩展就会有点麻烦。如下图,我们把末尾“12”逻辑映射到了“1库的编号为2的表”;

【方案二】将4库4表二维结构转成一维,以0为初始值进行递增,(0库, 0表) → 00, (0库, 1表) → 01... , (3库, 3表) → 15。好处是容量变大了,最大支持99张表,不受库或表单一条件的限制,缺点就是映射逻辑写起来麻烦点,不过这不是问题。

取货码经过简单编排,我们完成了取货码的到库表的映射逻辑,解决了取货码存取的问题。其实仔细想想,关于全局唯一的问题其实也解决掉了,我们只要保证前6位随机码在单表里保证唯一即可,理论上支持单表在未核销状态下范围为:000000 ~ 999999条记录,容量是足够的。关键我们把多库多表的查询就简化成了只跑一个SQL,效率大大提升。

3.4 方案落地遇到的问题

既然本篇是介绍SaaS化的完整方案,在落地的时候或多或少会遇到一些问题,这边介绍三个实际遇到的典型问题,并给出一些解决方案:

【问题一】使用Math.random()生成的6位随机码和表里的重复了,如何处理?

【解决】其实重复的情况有两种:

  1. 可能是表里已经存在数字相同未核销的取货码;

  2. 另外一种情况就是别的事务在正在操作,正好有个分布式事务锁住了一样的数字码(概率很低,但是是有可能的)。

这两种情况的出现就需要我们进行优雅地重试了!大致思路如下伪代码:

// step1 根据分库分表因子获取库表编号,userCode-用户编号、tenantId-租户编号
String suffix = getCodeSuffix(userCode, tenantId);// step2 批量获取6位随机码
for (int i=1; i<=5; i++) {// 批量获取随机数。每次重试,取2的指数级量进行过滤,相比暴力执行for循环,这种方式能减少和DB的交互List<String> tempCodes = getRandomCodes(2 << i);// 过滤掉分布式锁filterDistributeLock(tempCodes);// 过滤掉数据库存在的随机码filterExistsCodes(tempCodes);return tempCodes;
}// step3 处理随机码,随机码入库
for (String code : codes) {// 加锁,判断加锁是否成功。推荐使用Redis分布式锁boolean hasLockd = isLocked(code);try {// 执行入库insert(object);} finally {// 解锁}
}// step4 执行后置二维码图片等逻辑

【注意】

  1. 推荐使用指数级重试的方式(2 << i),逐次递增random的数量,减少和DB的交互;

  2. 建议数字码生成完毕后加锁并执行INSERT,生成图片地址等耗时严重的动作可以后置UPDATE上去。

【问题二】项目中使用了分库分表的组件(比如:ShardingSphere-JDBC),怎么动态修改数据源?也就是同时支持分库分表因子(比如:member_id、open_id等)以及根据取货码计算的库表动态查询。

【解决】我们以ShardingSphere-JDBC作为为案例来给出一些配置及伪代码,具体可以参考:《强制路由::ShardingSphere》,其他开源的分库分表组件或者自研产品不做赘述,可以自己手动写个插件,别怕,即使再难,也要相信有光!

配置及伪代码

// ShardingSphere-JDBC依赖的配置文件jdbc-sharding.yaml
...
shardingRule:tables:...# 取货码表order_code:actualDataNodes: DS00$->{0..3}.order_pick_up_0$->{0..3}# 配置库的计算逻辑databaseStrategy:hint:algorithmClassName: com.xxx.xxxxx.xxx.service.impl.DbHintShardingAlgorithm# 配偶之表的计算逻辑tableStrategy:hint:algorithmClassName: com.xxx.xxxxx.xxx.service.impl.DbHintShardingAlgorithm...// java代码
try (HintManager hintManager = HintManager.getInstance()) {hintManager.addDatabaseShardingValue("order_code"/** 取货码表 */, DbHintShardingAlgorithm.calDbShardingValue(tenantId, code));hintManager.addTableShardingValue("order_code"/** 取货码表 */, DbHintShardingAlgorithm.calTabShardingValue(tenantId, code));Object xxx = xxxMapper.selectOne(queryDTO);
}

【注意】

  1. 这里介绍一种编程式的解决方案,好处是配置简单、比较灵活,缺点就是代码稍微多一点。其实ShardingSphere还支持注解的方式,可以自己研究下;

  2. 第一条说了比较灵活,体现在自己实现的 “DbHintShardingAlgorithm.calDbShardingValue(tenantId, code)” 方法上,这个方法可以自己定义,所以我们的入参可以是通用的分库分表因子,也可以是自定义的取货码的“库表位置”字段,非常灵活。

【问题三】如何做到更强的扩展性,适用SaaS平台以及不同的业务场景?

【解决】细心的小伙伴应该注意到了 "tenantId" 这个字段,这是个租户的编码,在实际编码会进行透传。我们可以利用这个字段针对不同的租户(或叫业务方)来做不同的配置,比如:取货码的长度、取货码编排的方式、取货码映射库表位置的策略等等做成可配,只要把主干逻辑进一步抽象,并使用策略模式进行个性化编码。

四、总结

实现取货码逻辑的时候,发现网上券码这块的方案、技术文章比较少,当时萌生了写篇文章抛砖引玉做个分享的想法。事实上,我相信大多数公司可能或多或少也是这么做的,哪怕采取了别的方案也能殊途同归。本篇文章整体只是介绍了一个思路,而这个思路类似一个简化版的订单分库分表,但这就是神奇所在,事实上我们还可以将一些常用的技术方案落地到不同的应用场景,大胆地做一些尝试,多走一些未曾设想过的道路!

主题系列文章:

  • vivo全球商城全球化演进之路—多语言解决方案

  • vivo 全球商城:商品系统架构设计与实践

  • vivo全球商城-营销价格监控方案的探索

  • vivo 全球商城:优惠券系统架构设计与实践

  • vivo 全球商城:订单中心架构设计与实践

  • vivo 全球商城:从 0 到 1 代销业务的融合之路

  • vivo 全球商城:架构演进之路

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/45236.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【轻量化网络系列(4)】ShuffleNetV1论文超详细解读(翻译 +学习笔记+代码实现)

前言 前面我们学了MobileNetV1-3&#xff0c;从这篇开始我们学习ShuffleNet系列。ShuffleNet是Face&#xff08;旷视&#xff09;在2017年发布的一个高效率可以运行在手机等移动设备的网络结构&#xff0c;论文发表在CVRP2018上。这个新的轻量级网络使用了两个新的操作&#…

CopyTranslator-复制即翻译的外文辅助阅读翻译解决方案

英语/English 复制即翻译的外文辅助阅读翻译解决方案 请尽快更新到&#xff0c;这是你没有体验过的全新版本&#xff0c;只需3分钟&#xff0c;你就会跟我一样&#xff0c;爱上这个软件。 如果您觉得软件对您有所帮助&#xff0c;不用follow&#xff0c;不用fork&#xff0c;点…

论文泛读记录(多模情感分析/探测;厌恶语音探测;属性级情感分析;CVPR2022和ACL2022 三元组/对比学习/视觉语言预训练/机器翻译/预训练模型/机器阅读理解)

文章目录 1.1 CTFN: Hierarchical Learning for Multimodal Sentiment Analysis Using Coupled-Translation Fusion NetworkJiajia Tang, Kang Li, Xuanyu Jin, Andrzej Cichocki, Qibin Zhao and Wanzeng Kong 1.2 Multimodal Sentiment Detection Based on Multi-channel Gra…

计算机毕业设计安卓旅游APP源码

开发环境及工具&#xff1a; 大于jdk1.8&#xff0c;大于mysql5.5&#xff0c;idea&#xff08;eclipse&#xff09;&#xff0c;Android Studio 技术说明&#xff1a; springboot mybatis android 代码注释齐全&#xff0c;没有多余代码&#xff0c;适合学习(毕设)&#x…

孔夫子图书商城购物系统的设计与实现(论文+源码)_kaic

目录 摘要 一、绪论 &#xff08;一&#xff09;选题背景意义 &#xff08;二&#xff09;国内外研究现状 1.国内研究现状 2.国外研究现状 &#xff08;三&#xff09;研究内容 二、系统相关技术 &#xff08;一&#xff09;SSM框架 &#xff08;二&#xff09;Mysql数据库 &am…

200+语言任意互译,新开源的大模型让粤语靓仔直接喜大普奔

Alex 发自 凹非寺量子位 | 公众号 QbitAI 这个翻译模型&#xff0c;不仅支持200语言之间任意两两互译&#xff0c;还是开源的。 Meta AI在发布开源大型预训练模型OPT之后&#xff0c;再次发布最新成果NLLB。 NLLB的全称为No Language Left Behind&#xff0c;如果套用某著名电影…

计算机毕业论文内容参考|基于Android的国际酒店预订APP的设计与实现

文章目录 导文摘要:前言:绪论:课题背景国内外现状与趋势课题内容相关技术与方法介绍:系统分析:系统设计:系统实现:系统测试:本文总结后续工作展望导文 计算机毕业论文内容参考|基于Android的国际酒店预订APP的设计与实现 摘要: 随着人们生活水平的提高和旅游业的迅速…

基于android系统的单词学习软件设计与开发【附项目源码+论文说明】

基于android系统的单词学习软件设计与开发演示 摘要 随着手机使用的普及&#xff0c;人们获取与保存信息的方式已经发生了激动人心的转变。智能手机正在逐步融入我们的生活&#xff0c;并影响和改变着我们的生活。由于现在各种智能手机的使用&#xff0c;使得人们在管理、应用…

软件推荐:论文翻译阅读 + 文献管理 + markdown笔记 + 多设备同步 + 一键导出bib参考文献

起初是重大的一个学长推荐的&#xff0c;但是我只用来存文献&#xff0c;后来研究了一下发现居然有这么多功能。 最主要的免费&#xff01;可以白嫖就是香&#xff01; 以下是我用到的一些功能&#xff0c;如果我以后发现什么新功能会继续补充。 文章目录 1 下载安装zotero2…

(附源码)app校园购物网站 毕业设计 041037

springboot校园购物网站APP 摘 要 21世纪的今天&#xff0c;随着社会的不断发展与进步&#xff0c;人们对于信息科学化的认识&#xff0c;已由低层次向高层次发展&#xff0c;由原来的感性认识向理性认识提高&#xff0c;管理工作的重要性已逐渐被人们所认识&#xff0c;科学化…

略胜知云?适合大学生的一款文献翻译神器,网页版工具

转载于微信公众号【投必得学术】 之前给大家推荐过一款翻译软件。 知云文献翻译安装教程_阅读英文文献的好帮手https://blog.csdn.net/txr152111/article/details/117256967https://blog.csdn.net/txr152111/article/details/117256967 知云阅读翻译这个软件想必大家早已耳熟…

CopyTranslator——复制即翻译的外文辅助阅读翻译解决方案

CopyTranslator官网&#xff1a;https://copytranslator.github.io/ 有道翻译VIPCopyTranslator9链接&#xff1a;https://pan.baidu.com/s/1-Gg39WFS-P3LWKjw-59Inw 提取码&#xff1a;qa3i 复制翻译 CopyTranslator监听到剪贴板变化&#xff0c;会将剪贴板内容进行处理&am…

PDF翻译神器,再也不担心读不懂英文Paper了

作者 | 琥珀 出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09; 如果你经常跟文献打交道&#xff0c;那你应该切身体验过那种令人抓狂的心情&#xff1a;流畅地阅读 PDF 外文文献&#xff0c;必要情况下还得逐字逐句地翻译出来。 例如&#xff0c;主流翻译引擎之一的谷…

【论文泛读102】TranSmart:实用的交互式机器翻译系统

贴一下汇总贴&#xff1a;论文阅读记录 论文链接&#xff1a;《TranSmart: A Practical Interactive Machine Translation System》 一、摘要 自动机器翻译在产生翻译方面非常高效&#xff0c;但不能保证其质量。该技术报告介绍了TranSmart&#xff0c;这是一种实用的人机交…

Sumo学习日记 - day1 从traci开始

Sumo学习日记 之前经常使用sumo&#xff0c;但是网络上相关教程较少&#xff0c;且并没有行成系统的教学。官方文档教育效果很棒&#xff0c;但是对于想学习sumo这个软件的萌新来说好像有点不友好&#xff0c;所以在这里开一个专题 主要介绍sumo和traci的相关使用 同时也是自己…

计算机毕业论文内容参考|基于Android的旅游攻略APP的设计与实现

文章目录 导文摘要:前言:绪论:1. 课题背景:2. 国内外现状与趋势:3. 课题内容:相关技术与方法介绍:系统分析:系统设计:系统实现系统测试总结与展望本文总结后续工作展望导文 计算机毕业论文内容参考|基于Android的旅游攻略APP的设计与实现 摘要: 本文基于Android平台…

关于android的外文论文,毕业论文外文翻译-Android开发

毕业论文外文翻译-Android开发 (11页) 本资源提供全文预览&#xff0c;点击全文预览即可全文预览,如果喜欢文档就下载吧&#xff0c;查找使用更方便哦&#xff01; 9.90 积分 毕业设计(论文)外文翻译 毕业 论文题目 基于 Android 手机通讯录的设计与实现 作 者 姓 名 所学专业…

【论文阅读笔记】里程计ODO/INS不同融合方式的性能比较

文章目录 一、里程计的工作原理二、论文中的主要结论三、总结四、参考文献 欢迎关注个人公众号&#xff1a;导航员学习札记 关于里程计的融合一般用“距离增量修正”和“速度修正”两种方式。我最近在想这两种方式在性能上有什么不同&#xff0c;因此找了两篇论文来看。本文主…

WGCNA | 不止一个组的WGCNA怎么分析嘞!?~(三)(共识网络分析-第三步-共识模块与特异模块相关联)

1写在前面 有小伙伴子留言问最近介绍的WGCNA共识网络的意义是什么&#xff0c;保守性吗&#xff01;&#xff1f;&#x1f9d0; 与把雄性小鼠和雌性小鼠的数据merge在一起&#xff0c;一起构建网络、确定模块的方式有什么区别呢&#xff01;&#xff1f;&#x1f617; 其实区别…

Same Symbol | 哇咔咔!!!盘点一下表达矩阵中重复基因的处理方法!~

1写在前面 医院天天叫我们填问卷&#xff0c;我真是不能理解。&#x1fae0; 动不动就问我们对医院的福利满意吗&#xff0c;对自己的收入满意吗&#xff0c;觉不觉得工作负荷太重了&#xff1f;&#xff1f;&#xff1f;&#x1f642; 我们满不满意&#xff0c;觉不觉得累&…