背 景
商家营销简述
图片
在外卖平台的运营中,我们致力于通过灵活的补贴策略激励商家,与商家共同打造良好的合作关系,也会提供多样化的营销活动,帮助商家吸引更多用户下单。通过这些活动,不仅能够提高商家的销量,还能让用户感受到实际的优惠,从而增强他们对平台的粘性。
前端技术特点
业务特点:营销场景玩法多、活动类型多、活动链路长、活动规则复杂。
中后台技术特点:活动配置表单规则多、联动复杂。
前台技术特点:终端类型多(PC/EXE/PAD/PHONE)、代码重复度高、用户输入校验规则复杂。
业务技术架构
图片
活动链路
图片
商家营销相关项目迭代时间较长,积累的历史技术债务越来越多,结合业务背景和以上的技术特点,之前对历史项目进行过一波代码治理,比如:多端代码复用、后台复杂度治理等。
2023年的治理更多解的是代码重复及腐化相关的问题,而外卖商家营销活动的配置能力还处于非常基础的阶段,2024年,我们结合业务新诉求的契机,从系统架构治理维度,对商家营销前端项目做了一个全面的升级。
现状
外卖商家营销活动,按活动来源区分主要有四大类:平台招商、代运营、品牌代建、商户自营销,按类型区分主要有四大类:特价菜、买赠、免配、满减。前后端底层区分这些活动渠道及类型都是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
}
}]
}]
面临的挑战
2024年随着国际化外卖营销业务需求明显增长,比如:需要从0到1搭建连锁品牌商家自运营能力、拓展新的营销活动类型(商家券),按照现有的架构及配置能力来看,存在以下几个问题:
- 产品需求迭代支撑效率低:涉及通用字段,需要重复修改,特价菜+免配+满减+买赠,4种活动类型改4次,如果再算上活动渠道修改,需要再翻倍,4种渠道✖️4类活动 = 16 次。
- 开发遗漏:活动链路长,以当前最为复杂的招商活动为例,从运营后台配置招商计划=>商户前台报名招商活动,是一个较长的链路,由于系统数据模型不够灵活,导致修改字段及UI展示时无规律可循,经常需要梳理遗漏点。
- 可拓展性差:当前架构下,如果新增活动类型,则涉及全链路所有接口改动,可复用性低。
由于业务发展的契机,国际化外卖商家侧需要新增一个连锁品牌管理端,借助这个项目,我们进行了商家营销架构的升级。
解决方案
问题分析
商家营销配置能力薄弱主要体现在底层数据结构缺乏通用性和扩展性。
从全局配置维度来看:
图片
从数据结构现状来看:
图片
- 同一类活动,在不同平台(端、后台)数据结构不一致。
- 同一类活动,在不同活动来源场景下(自营销、招商、代运营、品牌),数据结构不一致。
- 四类活动,活动规则数据结构不一致,创建需要case by case拼装,详情需要case by case渲染。
- 差异化分支共有:自营销创建4 + 招商报名4 + 招商计划4 + 代运营1 + 品牌4 = 17。
整体思路
架构治理最重要的一环就是设计出一个统一的活动数据模型,涵盖所有平台和活动来源场景。
图片
- 抽象活动实体信息
- 统一差异化配置
- 按活动信息维度拆分组织字段,而不是按业务维度拆分(收敛类型、来源)。
- 按通用字段概括优惠类型,而不是按业务概念枚举(收敛规则)。
- 支持灵活拓展
项目成果
在抽象出活动配置模型后,为保证后续需求或者人员变更能够按照规范持续迭代,通过对应的配置模型的API文档,配套前端JSON Schema校验工具,约束后续拓展。
底层数据结构完成治理后,在新项目中,我们也对配置表单方案进行了优化,使得项目的数据流转更加清晰,确保数据的一致性和可靠性。
而在前后端交互层面,对接口字段进行了运行时校验,做到了接口安全约束,避免因数据缺陷而导致的前端错误。
数据结构对比
新版活动数据结构是一个面向对象的设计架构,采用组合式领域模型设计,通过策略模式实现业务规则的动态装配。
基础活动模型(ActInfoModel)可以被视为一个父类或者超类,定义了通用的属性和行为,而其子类(如自营销活动模型selfOpsModel)继承了基础特性,并可以实现或者重写一些特定的功能,以满足不同渠道的具体需求。
当出现新的渠道或者活动类型时,只需要创建新的子类,遵循现有的父类结构。而基础模型的修改也不会影响所有子类,只需要确保子类能够适应父类的接口变化即可。
图片
配置表单方案优化
在之前的项目里,表单间的组件通信,是传统多层组件的数据传递形式,通过父子组件层层传递。
图片
数据流:自上而下,每个组件都需要通过props接受和传递数据。
缺点:增加了代码复杂性,每个组件都需要显式传递数据,容易出现冗余代码和数据同步问题。
这种形式对于简单的表单场景来说,比较直观,但是对于商家营销活动配置场景来说,在过往需求迭代中出现了维护困难和数据同步异常的问题,在新项目里,我们使用了配置模型+依赖注入的表单方案。
图片
数据流: 数据通过依赖注入在组件树的各层之间传递,子组件直接获取所需数据
优点: 降低了组件之间的耦合性,减少了多层传递的冗余性,数据更加集中且易于管理
数据流转对比
图片
使用配置模型 + 依赖注入的方式不仅可以简化数据流转,还能实现集中管理,减少代码冗余,提高数据一致性,更容易进行维护和调试,特别是在需要动态配置或复杂业务逻辑的场景下表现尤为突出。
接口安全保障
当前数据安全问题
为了避免接口数据异常,导致前端页面白屏,我们通常会在代码中加一些字段兜底逻辑,这样带来的问题:
- 冗余的兜底逻辑:在组件中,使用“||“操作符、可选链和解构默认值等方式进行兜底处理,导致同样的逻辑在多个地方反复出现。
- 复杂的数据结构处理:对于复杂的数据结构,通常为了某个字段兜底会出现一大坨繁琐的代码,影响代码可读性与代码效率。
- 数据类型安全问题:常规兜底形式无法保证数据类型安全,可能造成不符合预期的类型错误,进而引发应用程序中的逻辑错误或页面崩溃。
在抽象出活动配置模型后,活动配置的定义是由标准的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函数功能包含:数据校验、兜底数据填充、埋点上报。
接口返回字段中若出现返回数据类型错误或者未返回的情况,将返回自定义的默认值从而保障页面正常展示,对于错误数据也做了埋点上报,当到达一定阈值时会进行报警。
效率提升
日常迭代
活动配置模型通过字段的抽象和整合,大幅提升了字段扩展的效率。原本因各活动类型和场景的数据结构差异,需要在多处修改数据结构和组件逻辑的场景,现在只需在一处进行修改即可,大大提高了开发效率。
以前台项目活动规则相关迭代为例:
图片
开发实例
以近期需求为例,我们需要新增一种券活动类型,通过采用活动配置模型和集中式状态管理的开发形式,使得开发过程中对于数据相关的处理逻辑与状态管理要比之前简易很多,开发效率提升约40%。
后续规划
以上架构治理都是针对新的项目去做的实践,而对于国际化外卖商家营销前端的其他项目同样需要做架构升级改造,后续我们计划收敛运营后台的活动配置,将最为复杂的招商活动链路进行标准化,后台配置=>前台应用,引用同一套数据模型。
国际化外卖商家营销前端架构预期
图片