这个枚举只有一个值,我差点删了——然后所有代发订单都取不了号
这个枚举只有一个值我差点删了——然后所有代发订单都取不了号技术重构系列 · 第2篇抖音代发共享店铺Token设计 地址策略解耦本系列基于老系统真实改造复盘文中客户名、地址、编码值均已化名/脱敏处理坑的类型、解题思路、踩坑过程为一线实录。系列背景与为什么重构见首篇。上篇聊了奇门对接顺丰的三个暗坑这篇进入抖音代发。抖音代发是一个特殊的业务模式店铺接到订单但由另一个仓库实际发货。仓库用自己的抖音账号调API取号但订单信息来自店铺。这种模式带来三个技术挑战——Token从哪来、地址用哪个、路由怎么走。三个问题看似简单实则暗藏前任硬编码的坑。文档里一笔带过代码里却藏着业务规则的全部秘密。这篇还多送一个故事一个沉睡了半年的Token是怎么在最不该炸的时候炸出一串空指针的。一、共享店铺Token你差点手贱删掉的枚举1.1 问题背景代发模式下仓库为多个店铺代发。每个店铺有自己的抖音授权Token但某些共享店铺共享仓库的Token。系统需要判断当前订单的店铺是不是共享店铺如果是用仓库的Token如果不是用店铺自己的Token。原代码的实现方式是在一个Java枚举里硬编码共享店铺列表订单进来时先查枚举命中了就走共享逻辑。1.2 你差点犯的错如果你不知道这个业务背景看到这个枚举只有一个值可能会觉得“这个枚举是不是可以删掉只有一个值直接查数据库不行吗”但不能删。这个枚举是业务配置不是代码冗余。删除它会导致共享店铺的Token获取逻辑失效所有共享店铺的订单都会报未获取到有效的AccessToken。这种看起来没用但不能删的代码在遗留系统里特别多。删之前先搞清楚它为什么在那里。1.3 新架构的优化在新架构中Token获取逻辑被提取为独立方法增加了空值校验和友好的错误信息含店铺名称。不再是3个方法里重复3遍而是一处定义、全局复用。为什么要特意强调空值校验因为我们真的被空Token炸过一次。往下看。二、一个沉睡半年的Token炸出5处防呆2.1 事故链代发业务有明显的淡旺季可能连续几个月一张代发订单都没有。于是发生了这样一条事故链长期没有代发订单 → 没人触发Token刷新授权静默过期 → 某天突然来了一张代发单 → 系统取Token拿到 null → 拼接请求参数时对 null 做URL编码 → 空指针异常NPE用户看到的报错是什么一串和业务毫无关系的堆栈信息。没有Token过期没有请重新授权只有一个冷冰冰的空指针。2.2 排查从NPE堆栈往回推编码的入参是Token → Token从组织配置里取的 → 配置里的Token字段是空的 → 为什么是空的→ 授权早就过期了只是一直没有订单来发现它。低频链路的凭证过期不会报警只会在最需要它的时候爆炸。2.3 修复5处防呆修复分两步数据修复重新走授权流程把新Token更新到组织配置。代码防呆在5个位置增加Token空值校验——统一取号入口、两个请求构建器、两个HTTP处理器。Token为空时不再往下走直接抛出人话版异常“未获取到有效的抖音代发AccessToken请检查平台配置店铺XXX”为什么要5处而不是1处因为取号有多个入口任何一个入口漏了校验NPE还是会从那个口炸出来。防呆的原则是在每一个消费Token的地方守门而不是假设上游一定给了合法值。2.4 这个坑的通用教训防呆校验的价值不是防止出错——Token过期这种事防不住。它的价值是让错误一眼能看懂从一串空指针堆栈排查两小时变成一行带店铺名的提示一分钟定位。你的系统里有没有这种半年才走一次的低频链路它的凭证、配置、外部依赖过期了有人知道吗三、地址策略硬编码背后的三条规则3.1 表象发件人地址的选择逻辑代码里写了一堆if/else中通用一个地址邮政和顺丰用另一个地址其他情况用商家配置的地址看起来就是不同快递用不同地址没什么复杂的。3.2 背后的业务规则但仔细看规则比if/else复杂得多规则条件地址规则1中通地址A规则2邮政或顺丰且不是书局A的订单地址B规则3其他商家自己配置的地址为什么中通用地址A因为中通在地址A有网点取件方便。为什么邮政/顺丰非书局A要用地址B因为书局A在地址A有自己的收件点但其他商家没有需要用地址B的公共收件点。为什么只有非重复订单才执行规则2因为代发自身的业务逻辑差异——重复订单不重新取号不需要重新判断地址。顺带提醒一句不同平台的网点绑定规则可能完全不同。同一家快递在代发链路绑定的是这套地址换到另一个电商平台可能又是另一套。对照多平台代码或文档时看到地址规则不一样先别急着当成矛盾——大概率是平台侧网点绑定本来就不同。3.3 这个坑和渠道编码的坑如出一辙回想上篇的渠道编码坑文档说根据订单类型设置代码里却硬编码了三种值分别对应三种渠道。不知道含义就改改完就出事。地址策略也一样文档说不同快递使用不同地址代码里硬编码了两个地址和三条规则。不知道规则就改改完中通测试通过因为中通规则最简单但邮政/顺丰切换时就报错。这些坑的共同特点文档一笔带过前任把业务规则硬编码在方法里新人看到代码觉得不就是个地址选择吗殊不知背后藏着三条业务规则。3.4 未来优化方向当前地址选择逻辑保留在代码中使用常量引用消除魔法字符串。后续可以下沉到数据库配置实现新增快递只需改配置不改代码。但这个改动需要业务确认暂不动。四、DYXD路由数据库有值代码没注册4.1 问题抖音平台有5种变体——普通、供销、分销端、代发等。每种变体调API时用的请求格式不同字段名不同、参数结构不同。系统需要根据变体类型选择正确的翻译官来组装请求。测试时发现某个变体的订单报错参数缺失。打个比方你要寄国际快递系统应该找英语翻译官对应国际格式但找不到fallback 找了个中文翻译官对应国内格式。翻译官用中文格式写了一张国际快递单对方看不懂直接退回来了。4.2 根因数据库中记录了5种变体但代码里只注册了1种的翻译官。其他4种找不到对应的翻译官就用了默认的——但默认的格式和实际需要的格式不匹配。我犯的错没有先查原代码的路由逻辑凭听起来像代发的直觉直接注册为代发处理器。用户纠正“只有代发走代发其他所有变体走普通。”查了原代码确认原代码中那个变体编码出现0次——根本没有特殊处理走的就是其他所有变体的默认分支。4.3 修复后的路由表变体编码走哪个翻译官普通/默认普通格式供销普通格式分销端普通格式代发代发格式只有代发用代发格式其他所有变体用普通格式。这个坑的完整排查过程和凭直觉的反思下篇讲抖音普通订单时还会从另一个角度收个尾。五、抖音平台的快递限制回归测试中发现一个重要限制抖音平台不支持京东快递。当用户切换到京东快递时系统直接拦截“暂不支持的渠道”。这是原代码的硬编码逻辑——京东快递的电子面单系统与抖音平台没有对接。如果你不知道这个限制可能会在测试时跳过京东快递上线后用户切换时才发现报错排查半天才发现是平台限制不是代码bug。各平台快递支持对比平台不支持的快递抖音京东拼多多京东无模板奇门京东无映射微信视频号京东未配置六、回归测试抖音代发共完成7次测试3次成功快递/场景结果说明顺丰特快/电商标快成功全链路通过产品编码正确下发邮政成功全链路通过中通失败面单账户余额不足——业务问题非代码问题顺丰服务类型切换被拒平台幂等限制已取号订单不允许变更服务类型失败的每一次都定位到了明确原因且都不是新代码引入的问题。测试的目的不是证明都能通过而是把哪些不能通过、为什么提前摸清楚。七、教训这篇文章的核心信息代码里的每一个硬编码背后都可能藏着一条业务规则每一条低频链路背后都可能藏着一个过期的凭证。改代码之前先搞清楚规则上线之前先想想最久没人走的那条路。四个坑的根源都指向同一件事——知识没有沉淀。共享店铺枚举没注释、地址规则没文档、路由规则只在前任脑子里、Token过期没人知道。硬编码不是罪但要留下注释。前任把地址和快递限制硬编码在方法里这在当时的业务环境下是合理的。但问题是没有留下业务规则的注释导致后来的开发者不知道这些硬编码背后的逻辑。新架构中我们用常量替换了魔法字符串并在代码注释中记录了业务规则。这是对历史的尊重也是对未来的负责。下篇预告下一篇进入抖音普通订单《取号为什么越来越慢100次数据库查询其实只需要1次》。代发搭好的分层架构普通订单直接复用——但复用不等于照搬四个差异点一个都不能漏。另外还有一个让所有平台受益的性能优化。讨论话题你们的系统里有没有那种看起来没用但不能删的代码或者半年才走一次、一走就炸的低频链路评论区聊聊。