解析器的自我修养:从“什么都干”到“只做解析”

发布时间:2026/6/30 23:03:53
解析器的自我修养:从“什么都干”到“只做解析” 解析器的自我修养从“什么都干”到“只做解析”摘要在多平台电子面单架构改造中解析器是最容易越权的组件——它常常把持久化、异常标记、工作单绑定等职责揽在自己身上导致代码臃肿、难以复用。本文以抖音原解析方法为反面教材详细拆解了解析器的职责边界、改造原则和标准实现模板并给出了可复用的改造检查清单。系列导航系列开篇从“能跑就行”到“整洁架构”上一篇快递公司前置校验改造本文解析器职责分离改造后续京东、拼多多等平台专项篇一、事故一个解析器引发的“血案”那天测试抖音普通订单的取号流程一切看似正常——API 调用成功响应日志也打印了。但紧接着系统报了一个莫名其妙的错误获取运单号失败: 系统异常: nullnull哪里来的null顺着日志往上翻发现问题出在解析器里。原来的抖音解析方法parseDYWaybillResponse不仅解析了响应 JSON还顺带做了这些事情调用commonDao.findByQuery清理历史运单循环调用commonDao.store(detail)保存每条运单明细调用markTocAuditException标记订单异常调用bindTocWorkDocAndWayBill绑定工作单最后还调了一次commonDao.store(ticket)保存订单一个“解析”方法干了 5 件跟解析无关的事。更糟糕的是当奇门和抖音的解析逻辑需要复用时这些“附加操作”被原封不动地复制粘贴了两遍。一旦需要修改持久化逻辑比如加缓存、改事务两个平台都要改漏一个就出 Bug。二、反面教材原始解析方法的问题以抖音原解析代码为例简化privatestaticvoidparseDYWaybillResponse(CommonDaocommonDao,TocWmsPickTicketticket,Stringresponse,inttotalPackages,intexsitJianNum,StringdyLogisticsCode,StringaccessToken){// 1. 解析 JSONJSONObjectjsonResponseJSON.parseObject(response);// 2. ❌ 直接操作数据库清理历史运单commonDao.findByQuery(FROM TocPickTicketWayBillDetailsNew t WHERE ...,...);// 3. 循环解析运单for(Objectobj:ebillInfos){// 4. 获取打印数据平台特有可保留String[]printDatagetDYPrintData(...);// 5. ❌ 直接 commonDao.store(detail)commonDao.store(detail);}// 6. ❌ 直接标记订单异常if(!hasWaybill){TocWmsExpressUtil.markTocAuditException(commonDao,ticket,errMessage);}// 7. ❌ 绑定工作单bindTocWorkDocAndWayBill(commonDao,newBillDetails,workDocs);// 8. ❌ 保存订单commonDao.store(ticket);}核心问题解析器承担了“解析 持久化 异常标记 工作单绑定 数据库清理”五项职责导致代码不可复用新增平台时必须复制所有逻辑稍有遗漏就出 Bug。测试困难测试解析逻辑必须启动数据库一个纯数据转换的测试变成了集成测试。职责混乱修改持久化逻辑要改解析器修改解析逻辑也可能误伤持久化。三、改造原则解析器只做解析在新架构中ParseStrategy接口有明确的契约publicinterfaceParseStrategy{/** * 解析平台响应返回标准运单明细列表 * param ctx 订单上下文 * param response 平台返回的原始响应字符串 * return 运单明细列表 */ListTocPickTicketWayBillDetailsNewparseResponse(WaybillContextctx,Stringresponse);}允许做的事使用 JSON/XML 解析库解析原始响应从上下文ctx中获取订单信息订单对象、已有件数、物流编码等做数量校验如运单数量不足抛出BusinessException而非直接标记订单包含平台特有的数据处理逻辑如抖音的打印数据获取作为私有方法保留在解析器内严禁做的事❌ 直接调用commonDao.store()、commonDao.findByQuery()等 DAO 方法❌ 直接修改ticket的状态或异常信息setExceptionReason、setIsAudit等❌ 调用TocWmsExpressUtil.markTocAuditException等工具类标记订单❌ 绑定工作单bindTocWorkDocAndWayBill或清理历史运单❌ 做任何事务控制设计模式视角解析器改造的本质是单一职责原则SRP的落地——让解析器只做解析这一件事。在《Java 23种设计模式从踩坑到精通》系列中我详细拆解了六大设计原则与23种模式的工程实践欢迎延伸阅读体系化地提升设计能力。四、标准实现模板以下是一个新渠道解析器的标准实现模板以抖音普通为例publicclassDouYinParseStrategyimplementsParseStrategy{privatestaticfinalLogloggerLogFactory.getLog(DouYinParseStrategy.class);OverridepublicListTocPickTicketWayBillDetailsNewparseResponse(WaybillContextctx,Stringresponse){JSONObjectjsonResponseJSON.parseObject(response);ListTocPickTicketWayBillDetailsNewdetailsnewArrayList();// 1. 定位核心数据节点JSONObjectdatajsonResponse.getJSONObject(data);if(datanull){logger.warn(抖音响应中无 data 字段返回空列表);returndetails;// 返回空列表由模板层统一标记异常}JSONArrayebillInfosdata.getJSONArray(ebill_infos);if(ebillInfosnull||ebillInfos.isEmpty()){returndetails;}// 2. 获取订单上下文信息TocWmsPickTicketticketctx.getTicket();intexsitJianNumctx.getExsitJianNum();inttotalPackagesticket.getCustomerBoxNum();// 3. 数量校验intneedCounttotalPackages-exsitJianNum;if(ebillInfos.size()needCount){StringerrMsg获取运单号数量不足需要needCount个实际返回ebillInfos.size();logger.error(errMsg);thrownewBusinessException(errMsg);// 抛异常由模板层捕获并标记订单}// 4. 遍历解析每一个运单StringlogisticsCodeticket.getLogisticsCode();StringaccessToken(String)ctx.getExt().get(WaybillContext.KEY_PLAT_ACCESS_TOKEN);for(inti0;iebillInfos.size();i){JSONObjectebillebillInfos.getJSONObject(i);StringwaybillCodeebill.getString(track_no);StringobjectIdebill.getString(pack_id);// 获取打印数据平台特有逻辑保留为私有方法String[]printDatafetchPrintData(logisticsCode,waybillCode,accessToken);TocPickTicketWayBillDetailsNewdetailnewTocPickTicketWayBillDetailsNew();detail.setCpCode(ticket.getLogisticsCode());detail.setWaybillCode(waybillCode);detail.setPrintData(printData[0]);detail.setDouDianSign(printData[1]);detail.setObjectId(objectId);detail.setTocWmsPickTicket(ticket);details.add(detail);}returndetails;}/** * 获取打印数据平台特有逻辑 * ⚠️ 此方法内也不能执行持久化操作 */privateString[]fetchPrintData(StringlogisticsCode,StringwaybillCode,StringaccessToken){// 调用平台 API 获取打印 HTML 和签名returnnewString[]{printHtml,sign};}}五、模板层如何配合解析器返回空列表或抛出BusinessException后由模板层WaybillFetchTemplate统一处理异常标记// WaybillFetchTemplate.execute() 中try{// 4. 解析响应ListTocPickTicketWayBillDetailsNewdetailsparseStrategy.parseResponse(ctx,response);if(detailsnull||details.isEmpty()){markException(ctx.getTicket(),未获取到运单号);returnfalse;}// 5. 持久化统一由 WaybillPersistence 处理persistence.saveAndBind(ctx.getTicket(),details,ctx.isFirst());returntrue;}catch(BusinessExceptione){markException(ctx.getTicket(),e.getMessage());returnfalse;}持久化、工作单绑定、历史数据清理全部由WaybillPersistence.saveAndBind统一处理解析器完全不碰。六、改造前后对比维度改造前改造后解析器职责解析 持久化 异常标记 工作单绑定 数据清理只做解析代码复用每个平台复制一份持久化逻辑持久化统一收口零重复测试难度需启动数据库集成测试Mock 上下文即可纯函数测试新增平台复制 200 行代码只需实现解析逻辑约 60 行持久化变更所有平台解析器都要改只改WaybillPersistence一处七、平台特有逻辑处理建议某些平台在解析时需要额外获取打印数据如抖音需要getDYPrintData建议在解析器内部以私有方法实现不要暴露到接口签名中。方法所需的 Token 等参数从ctx.getExt()中获取。privateString[]fetchPrintData(StringwaybillCode,WaybillContextctx){StringaccessToken(String)ctx.getExt().get(WaybillContext.KEY_PLAT_ACCESS_TOKEN);// 调用平台 API 获取打印信息returnnewString[]{printHtml,sign};}强调该私有方法内也不能执行持久化操作。八、改造检查清单在实现或改造一个渠道的ParseStrategy后请逐项自查方法签名严格实现parseResponse(WaybillContext ctx, String response)没有出现commonDao对象没有调用commonDao.store()、commonDao.findByQuery()、commonDao.update()没有调用markTocAuditException、TocWmsExpressUtil.setException等标记方法没有调用bindTocWorkDocAndWayBill、历史运单清理逻辑数量不足时抛出BusinessException而非静默返回返回的TocPickTicketWayBillDetailsNew对象中已关联ticket所有需要的扩展参数均从ctx.getExt()获取不自行查询数据库九、总结这次解析器改造的核心思想只有一个让解析器回归本职。通过将持久化收口到WaybillPersistence、异常标记收口到模板层、工作单绑定收口到saveAndBind我们实现了新增平台成本大幅降低解析器只需关心平台特有的 JSON 结构。代码质量显著提升每个组件职责单一可独立测试。Bug 修复一改全改持久化逻辑变更只需改一处。这套规范已在奇门、抖音普通两个平台验证通过后续抖音代发、京东、拼多多等渠道均照此模板执行。十、系列导航与参考本篇文章是「电商多平台电子面单对接实战」的第七篇解析器改造篇聚焦解析器的职责分离与标准化改造。系列文章目录开篇从“能跑就行”到“整洁架构”第一篇奇门对接顺丰电子面单第二篇抖音代发电子面单对接第三篇抖音普通订单电子面单对接第四篇多平台统一架构设计第五篇策略工厂复合Key路由改造第六篇快递公司前置校验改造第七篇解析器职责分离改造本文第八篇模板方法的组合与继承抉择第九篇API调用调度层Handler分组设计第十篇奇门 trade_order_list 排查实录第十一篇数据库查询优化让多包裹取号快一倍第十二篇两次架构升级完整复盘第十三篇常量与配置集中管控改造后续京东、拼多多等平台专项篇延伸阅读Java 23种设计模式实战系列本文中解析器改造的核心——单一职责原则SRP以及背后涉及的设计理念在《Java 23种设计模式从踩坑到精通》系列中有更体系化的讲解。如果你对以下问题感兴趣推荐延伸阅读如何用策略模式配合工厂实现解析器的灵活扩展模板方法模式在这里如何与组合结合控制流程骨架六大设计原则在电子面单架构中是如何一一落地的《Java 23 种设计模式从踩坑到精通》系列开篇从踩坑到精通 —— 总览与导航策略模式 —— 算法族的封装与切换模板方法模式 —— 定义算法骨架交给子类填充细节学习建议电子面单系列侧重业务落地设计模式系列侧重理论体系。两者搭配阅读既能应对面试又能反哺项目形成“理论→实战”的闭环。十一、一起交流共同进步技术之路一个人走得快一群人走得远。如果您的团队也在为解析器职责混乱头疼希望本文的改造思路能给您带来启发。关注我点击上方“关注”第一时间获取系列更新推送。留言讨论如果您在实际对接中遇到类似问题或对文章有任何建议欢迎在评论区留言我会定期回复。分享转发如果本文对您有帮助请点赞、收藏、分享让更多同行看到。