从技术债务到架构升级,滴滴国际化外卖的变革

c6d5408f7cbc5e2f87eca3bf2b9d4ee2.gif

背 景

82117221e394557f5567ae81a522a8e9.gif

商家营销简述

c2fd876f5c6e1e6279da793078e707d6.png

在外卖平台的运营中,我们致力于通过灵活的补贴策略激励商家,与商家共同打造良好的合作关系,也会提供多样化的营销活动,帮助商家吸引更多用户下单。通过这些活动,不仅能够提高商家的销量,还能让用户感受到实际的优惠,从而增强他们对平台的粘性。

7b56ce3baed28ebc8c2fcc0d7996d229.gif

前端技术特点

业务特点:营销场景玩法多、活动类型多、活动链路长、活动规则复杂。

中后台技术特点:活动配置表单规则多、联动复杂。

前台技术特点:终端类型多(PC/EXE/PAD/PHONE)、代码重复度高、用户输入校验规则复杂。

业务技术架构

0d0fae11da1b917020a91c0c1d4f8a00.png

活动链路

1460a94546612b24e2b99ba65af57640.png

商家营销相关项目迭代时间较长,积累的历史技术债务越来越多,结合业务背景和以上的技术特点,之前对历史项目进行过一波代码治理,比如:多端代码复用、后台复杂度治理等。    

2023年的治理更多解的是代码重复及腐化相关的问题,而外卖商家营销活动的配置能力还处于非常基础的阶段,2024年,我们结合业务新诉求的契机,从系统架构治理维度,对商家营销前端项目做了一个全面的升级。

34482a6fef815996e73680f0b336cc93.gif

现状

外卖商家营销活动,按活动来源区分主要有四大类:平台招商、代运营、品牌代建、商户自营销,按类型区分主要有四大类:特价菜、买赠、免配、满减。前后端底层区分这些活动渠道及类型都是case by case的形式,以招商活动创建为例:

不同活动类型,优惠信息都放在不同的字段里,特价菜是specialItemRule,免配是freeDeliveryRule,满减是reductionRule,买赠是buyGiftsRule,且规则rules字段层级嵌套冗余,字段属性没有规律可循,以下列举了两类活动的部分字段。

特价菜活动规则字段示例

"specialItemRule": [{"rulePurposeType": 0,"rules": [{"type": 3,"content": {"discountValueRange": null,"discountValueList": [10, 11, 20],"discountType": 1}}],"selectItemNumRange": {"min": 1,"max": 10},"itemPromoRangeValue": {"min": 1,"max": 50},"picLimit": 1,"priceLimit": null,"itemType": 0,"checkItemPriceDay": 7}]

满减规则字段示例

"reductionRule": [{"rulePurposeType": 0,"rules": [{"type": 1,"content": {"threshold": 10,"discount": 5}}, {"type": 1,"content": {"threshold": 20,"discount": 8}}]}]

9412be4aeb911e93a361df51e7d928a6.gif

面临的挑战

2024年随着国际化外卖营销业务需求明显增长,比如:需要从0到1搭建连锁品牌商家自运营能力、拓展新的营销活动类型(商家券),按照现有的架构及配置能力来看,存在以下几个问题:

  • 产品需求迭代支撑效率低:涉及通用字段,需要重复修改,特价菜+免配+满减+买赠,4种活动类型改4次,如果再算上活动渠道修改,需要再翻倍,4种渠道✖️4类活动 = 16 次。

  • 开发遗漏:活动链路长,以当前最为复杂的招商活动为例,从运营后台配置招商计划=>商户前台报名招商活动,是一个较长的链路,由于系统数据模型不够灵活,导致修改字段及UI展示时无规律可循,经常需要梳理遗漏点。

  • 可拓展性差:当前架构下,如果新增活动类型,则涉及全链路所有接口改动,可复用性低。

由于业务发展的契机,国际化外卖商家侧需要新增一个连锁品牌管理端,借助这个项目,我们进行了商家营销架构的升级。

88f537892ee5352b86fb544f735b2124.gif

解决方案

fff062f372f34617752d692421443699.gif

问题分析

商家营销配置能力薄弱主要体现在底层数据结构缺乏通用性和扩展性。

从全局配置维度来看:

4e3e82c6c7a5e919b9d61c70a2779e3b.png

从数据结构现状来看:

28277721b6e689a0dbe8b84da36a584e.png

  • 同一类活动,在不同平台(端、后台)数据结构不一致。

  • 同一类活动,在不同活动来源场景下(自营销、招商、代运营、品牌),数据结构不一致。

  • 四类活动,活动规则数据结构不一致,创建需要case by case拼装,详情需要case by case渲染。

  • 差异化分支共有:自营销创建4 + 招商报名4 + 招商计划4 + 代运营1 + 品牌4 = 17

bba6deab74a8bae8e4593c08c58fdbfc.gif

整体思路

架构治理最重要的一环就是设计出一个统一的活动数据模型,涵盖所有平台和活动来源场景。

67cff1624fb4aa1263b95a07036d17cd.png

  • 抽象活动实体信息

  • 统一差异化配置

  1. 按活动信息维度拆分组织字段,而不是按业务维度拆分(收敛类型、来源)。

  2. 按通用字段概括优惠类型,而不是按业务概念枚举(收敛规则)。

  • 支持灵活拓展

fe6ab9894837482b39043764d8548b87.gif

项目成果

在抽象出活动配置模型后,为保证后续需求或者人员变更能够按照规范持续迭代,通过对应的配置模型的API文档,配套前端JSON Schema校验工具,约束后续拓展。

底层数据结构完成治理后,在新项目中,我们也对配置表单方案进行了优化,使得项目的数据流转更加清晰,确保数据的一致性和可靠性。

而在前后端交互层面,对接口字段进行了运行时校验,做到了接口安全约束,避免因数据缺陷而导致的前端错误。

934da6bab9adc7d3e22c023f72e37ef7.gif

数据结构对比

新版活动数据结构是一个面向对象的设计架构,采用组合式领域模型设计,通过策略模式实现业务规则的动态装配。

基础活动模型(ActInfoModel)可以被视为一个父类或者超类,定义了通用的属性和行为,而其子类(如自营销活动模型selfOpsModel)继承了基础特性,并可以实现或者重写一些特定的功能,以满足不同渠道的具体需求。

当出现新的渠道或者活动类型时,只需要创建新的子类,遵循现有的父类结构。而基础模型的修改也不会影响所有子类,只需要确保子类能够适应父类的接口变化即可。

bb9b8e6f23b81d998fe7ced944345bcf.png

07bb9ae91e5f0bcc9241d8b4dfc1cc55.gif

配置表单方案优化

在之前的项目里,表单间的组件通信,是传统多层组件的数据传递形式,通过父子组件层层传递。

d50236d151224f79dfacc516320a80cb.png

数据流:自上而下,每个组件都需要通过props接受和传递数据。

缺点:增加了代码复杂性,每个组件都需要显式传递数据,容易出现冗余代码和数据同步问题。

这种形式对于简单的表单场景来说,比较直观,但是对于商家营销活动配置场景来说,在过往需求迭代中出现了维护困难和数据同步异常的问题,在新项目里,我们使用了配置模型+依赖注入的表单方案。

3e6e565e1e840f2950321a888ed37218.png

数据流: 数据通过依赖注入在组件树的各层之间传递,子组件直接获取所需数据

优点: 降低了组件之间的耦合性,减少了多层传递的冗余性,数据更加集中且易于管理

数据流转对比

ca949fb9baec1ef25f85c238970be44b.png

使用配置模型 + 依赖注入的方式不仅可以简化数据流转,还能实现集中管理,减少代码冗余,提高数据一致性,更容易进行维护和调试,特别是在需要动态配置或复杂业务逻辑的场景下表现尤为突出。

84ab7ce00545421977ab6e4067b8e0ea.gif

接口安全保障

当前数据安全问题

为了避免接口数据异常,导致前端页面白屏,我们通常会在代码中加一些字段兜底逻辑,这样带来的问题:

  • 冗余的兜底逻辑:在组件中,使用“||“操作符、可选链和解构默认值等方式进行兜底处理,导致同样的逻辑在多个地方反复出现。

  • 复杂的数据结构处理:对于复杂的数据结构,通常为了某个字段兜底会出现一大坨繁琐的代码,影响代码可读性与代码效率。

  • 数据类型安全问题:常规兜底形式无法保证数据类型安全,可能造成不符合预期的类型错误,进而引发应用程序中的逻辑错误或页面崩溃。

在抽象出活动配置模型后,活动配置的定义是由标准的JSON Schema描述组成的,在这个基础上,我们定义一些校验及默认填充规则,并引入集中式的兜底机制,在接口数据返回时,调用一个校验工具函数,实现统一的兜底策略。校验工具函数是借助zod这个工具库去实现的。

招商活动配置描述示例

// 招商活动规则
export const SignUpActRuleSchema = ActRuleSchema.extend({selectNumRange: z.object({min: z.number().default(0),max: z.number().default(0),}).default({ min: 0, max: 0 }),actType: z.union([z.number(), z.string()]).default(0),rule: z.array(SignUpRuleSchema).default([]),
})
export type SignUpActRule = z.infer<typeof SignUpActRuleSchema>// 招商活动详情页接口信息
export const SignUpDetailSchema = z.object({actRule: SignUpActRuleSchema.default({}),actInfo: SignUpInfoSchema.default({}),shopJoinInfo: z.array(ShopJoinInfoSchema).default([]),
})

‍接口返回处理示例

// 招商活动详情接口
// useApiSchema是统一的返回数据校验工具函数
export async function getSignUpDetail(params: object = {}): Promise<SignUpDetail> {const response = await post(GET_SIGN_UP_DETAIL, params, { returnData: false })return useApiSchema<SignUpDetail>(SignUpDetailSchema, response.data, response.traceId)
}

useApiSchema函数功能包含:数据校验、兜底数据填充、埋点上报。

接口返回字段中若出现返回数据类型错误或者未返回的情况,将返回自定义的默认值从而保障页面正常展示,对于错误数据也做了埋点上报,当到达一定阈值时会进行报警。

adb9b15fda60921b770d3c3278956f60.gif

效率提升

日常迭代

活动配置模型通过字段的抽象和整合,大幅提升了字段扩展的效率。原本因各活动类型和场景的数据结构差异,需要在多处修改数据结构和组件逻辑的场景,现在只需在一处进行修改即可,大大提高了开发效率。

以前台项目活动规则相关迭代为例:

4858dd86d23322be33302e9329c463a3.png


开发实例

以近期需求为例,我们需要新增一种券活动类型,通过采用活动配置模型和集中式状态管理的开发形式,使得开发过程中对于数据相关的处理逻辑与状态管理要比之前简易很多,开发效率提升约40%

04d7fafaf3282e0b3783108acf8f7ee7.gif

后续规划

以上架构治理都是针对新的项目去做的实践,而对于国际化外卖商家营销前端的其他项目同样需要做架构升级改造,后续我们计划收敛运营后台的活动配置,将最为复杂的招商活动链路进行标准化,后台配置=>前台应用,引用同一套数据模型。

国际化外卖商家营销前端架构预期

a6c24bc92116ebd99be0d3fb39609f96.png

b10055791bb59e1e007aeb8aaf1e116b.gif

往期文章回顾

fe660ffc84f9eb75fefe6f055703fa93.png

ace4c42c6569082cb8cc5190b6141698.gif

致谢

核心开发:杜雨轩、闫莉

关键合作方:宋亚阁、陈珏、吴召学

项目指导:董亚杰

整体方案的产出到落地实践离不开以上同学的辛苦付出,借此机会表示由衷感谢!同时,也非常感谢能够耐心阅读到这里的读者,业务技术架构治理是一个持续的过程,需要持续的关注、投入与调整,也需要与业务深度融合,以实现业务与技术目标的双赢。

欢迎感兴趣的同学拍砖指导,一起交流讨论!小编将选取5位同学,送上20W无线充电器!

934d351de39d12cf0b6e8c4c40b448cd.jpeg

//  E N D //

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

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

相关文章

英语—四级CET4考试—技巧篇—选词填空—实操教学—2014 年 6 月大学英语四级考试真题(第 2 套)

&#x1f3e0;个人主页&#xff1a;fo安方的博客✨ &#x1f482;个人简历&#xff1a;大家好&#xff0c;我是fo安方&#xff0c;目前中南大学MBA在读&#xff0c;也考取过HCIE Cloud Computing、CCIE Security、PMP、CISP、RHCE、CCNP RS、PEST 3等证书。&#x1f433; &…

线性代数中的正交和标准正交向量

在线性代数中&#xff0c;理解正交向量和正交向量至关重要&#xff0c;尤其是对于机器学习中的应用。这篇博文将简化这些概念&#xff0c;而不会太深入地深入研究复杂的数学。 正交向量 如果两个向量的点积等于零&#xff0c;则认为这两个向量是正交的。但点积到底是什么呢&am…

企业文件共享中的权限管理与安全风险防范

在企业的日常运营中&#xff0c;文件共享是必不可少的一项工作。然而&#xff0c;文件共享过程中如果权限管理不当&#xff0c;极易引发安全风险&#xff0c;导致企业敏感信息泄露。因此&#xff0c;加强文件共享中的权限管理与安全风险防范&#xff0c;对于保障企业信息安全至…

急停信号的含义

前言&#xff1a; 大家好&#xff0c;我是上位机马工&#xff0c;硕士毕业4年年入40万&#xff0c;目前在一家自动化公司担任软件经理&#xff0c;从事C#上位机软件开发8年以上&#xff01;我们在开发C#的运动控制程序的时候&#xff0c;一个必要的步骤就是确认设备按钮的急停…

数据结构:图;邻接矩阵和邻接表

邻接矩阵&#xff1a; 1.概念&#xff1a; 邻接矩阵是图的存储结构之一&#xff0c;通过二维数组表示顶点间的连接关系。 2.具体例子 &#xff1a; 一.无向图邻接矩阵示例&#xff1a; 示例图&#xff08;顶点&#xff1a;A、B、C&#xff0c;边&#xff1a;A-B、B-C&…

Kubernetes-master 组件

以下是Kubernetes Master Machine的组件。 etcd 它存储集群中每个节点可以使用的配置信息。它是一个高可用性键值存储&#xff0c;可以在多个节点之间分布。只有Kubernetes API服务器可以访问它&#xff0c;因为它可能具有一些敏感信息。这是一个分布式键值存储&#xff0c;所…

【第2章:神经网络基础与实现——2.1 前馈神经网络的结构与工作原理】

老铁们好!今天我们要来一场长达两万字的超详细技术探险,我会像拆解乐高积木一样把前馈神经网络(Feedforward Neural Network)的每个零件摆在台面上,用最接地气的方式让你彻底搞懂这个深度学习基石的工作原理。准备好了吗?我们开始吧! 第一章:神经网络的 “乐高积木” 1…

【云安全】云原生- K8S kubeconfig 文件泄露

什么是 kubeconfig 文件&#xff1f; kubeconfig 文件是 Kubernetes 的配置文件&#xff0c;用于存储集群的访问凭证、API Server 的地址和认证信息&#xff0c;允许用户和 kubectl 等工具与 Kubernetes 集群进行交互。它通常包含多个集群的配置&#xff0c;支持通过上下文&am…

【环境安装】重装Docker-26.0.2版本

【机器背景说明】Linux-Centos7&#xff1b;已有低版本的Docker 【目标环境说明】 卸载已有Docker&#xff0c;用docker-26.0.2.tgz安装包安装 1.Docker包下载 下载地址&#xff1a;Index of linux/static/stable/x86_64/ 2.卸载已有的Docker 卸载之前首先停掉服务 sudo…

字节跳动后端二面

&#x1f4cd;1. 数据库的事务性质&#xff0c;InnoDB是如何实现的&#xff1f; 数据库事务具有ACID特性&#xff0c;即原子性、一致性、隔离性和持久性。InnoDB通过以下机制实现这些特性&#xff1a; &#x1f680; 实现细节&#xff1a; 原子性&#xff1a;通过undo log实…

SpringBoot中使用MyBatis-Plus详细介绍

目录 一、MyBatis-Plus的使用步骤 1.引入MybatisPlus的起步依赖 2.定义Mapper&#xff08;也叫dao&#xff09;层的接口 3.MyBatis-Plus中常用注解 4. 使用MyBatis-Plus时要做如下配置 5.条件构造器 Wrapper 一、MyBatis-Plus的使用步骤 1.引入MybatisPlus的起步依赖 M…

vue3读取webrtc-stream 视频流

一.首先下载webrtc-stream&#xff0c;方便自己本地搭建视频流服务 https://download.csdn.net/download/cyw8998/90373521 解压后&#xff0c;启动命令 webrtc-streamer.exe -H 127.0.0.1:8020 二.vue3代码如下 <template><h1>video</h1><video id&…

vue3搭建实战项目笔记二

vue3搭建实战项目笔记二 2.1.git管理项目2.2.隐藏tabBar栏2.2.1 方案一&#xff1a;在路由元信息中设置一个参数是否显示tabBar2.2.2 方案二&#xff1a;通过全局设置相对定位样式 2.3.项目里封装axios2.3.1 发送网络请求的两种做法2.3.2 封装axios并发送网络请求2.3.2.1 对axi…

USC 安防平台之移动侦测

随着第四次科技革命的开启&#xff0c;AI技术获取了突飞猛进的发展&#xff0c;视频监控对应的视频分析技术也获取了巨大的发展。 还记得15年前采用人工提取特征做前景背景分离和提取&#xff0c;大部分依赖CPU&#xff0c;最多使用一下TI的DM642 DSP加速&#xff0c;开发难度…

Unity CommandBuffer绘制粒子系统网格显示

CommandBuffer是 Unity 提供的一种在渲染流程中插入自定义渲染命令的机制。在渲染粒子系统时&#xff0c;常规的渲染流程可能无法满足特定的渲染需求&#xff0c;而CommandBuffer允许开发者灵活地设置渲染参数、控制渲染顺序以及执行自定义的绘制操作。通过它&#xff0c;可以精…

【天地图】绘制、删除点线面

使用天地图绘制、删除点线面 实现效果图地图组件完整代码使用地图组件完整代码 实现效果图 地图组件完整代码 // 天地图组件 <template><div class"map-container"><div id"mapCon"></div></div> </template><scri…

Java八股文详细文档.2(基于黑马、ChatGPT、DeepSeek)

通过B站黑马程序员的八股文教学&#xff0c;自己也二刷了&#xff0c;结合ChatGpt、deepSeek总结了一下,Java八股文详细文档.2&#xff08;Redis篇和消息中间件篇&#xff0c;还没有写完&#xff0c;这只是一部分&#xff09; Java八股文详细文档.1&#xff08;包含JVM篇、数据…

简述 tsconfig.json 中 rootDir 和 include 之间的关系

tsconfig.json 中的 rootDir 和 include 之间有一定的关系&#xff0c;但它们的作用是不同的。理解它们的关系可以帮助你更好地配置 TypeScript 项目。 1. rootDir 的作用 rootDir 用于指定 TypeScript 编译器&#xff08;tsc&#xff09;的“根目录”。它的主要作用是&#x…

如何在Spring Boot中使用Profiles实现环境隔离

文章目录 如何在Spring Boot中使用Profiles实现环境隔离什么是Spring Profiles1.基本概念2.配置管理3.使用场景4.条件化配置5.优点Spring Profiles的基础知识1.Profile的定义2.配置文件3.激活Profiles4.条件化配置5.Profile的优先级与合并6.Profiles的最佳实践配置文件的组织1.…

SpringBoot使用TraceId日志链路追踪

项目场景&#xff1a; ??有时候一个业务调用链场景&#xff0c;很长&#xff0c;调了各种各样的方法&#xff0c;看日志的时候&#xff0c;各个接口的日志穿插&#xff0c;确实让人头大。为了解决这个痛点&#xff0c;就使用了TraceId&#xff0c;根据TraceId关键字进入服务…