介绍菜单制作器:Uber Eats的新菜单管理工具

0
介绍菜单制作器:Uber Eats的新菜单管理工具

餐厅的菜单可以说是它最重要的特色。在网上或通过Uber Eats的应用点餐时,潜在顾客无法透过窗户窥视餐厅,也无法闻到厨房飘来的香味,因此数字菜单实际上成为了餐厅个性的代表。

作为餐厅菜单工程团队,我们的目标是确保世界上的每一家餐厅都能在Uber Eats上准确地展示他们的产品。这个看似简单的任务实际上相当复杂,因为存在着各种各样的餐馆,从当地的面包店和食品卡车到大型连锁店和杂货店。

为了简化我们的餐厅合作伙伴的Uber Eats体验,我们建立了菜单制造商Menu Maker简单的界面和先进的功能为餐馆合作伙伴提供了极大的灵活性,同时保护消费者免受欺诈。优步员工还与Menu Maker互动,帮助我们的餐厅合作伙伴创建菜单。

从菜单1.0到菜单2.0

今天的Uber Eats的菜单看起来与几年前有很大的不同。在之前的应用迭代中,我们构建了与原始菜单数据模型(称为菜单1.0)交互的工具。,包括以下内容:

    • 菜单编辑器:我们的餐厅合作伙伴使用菜单编辑器对外调整菜单。菜单编辑器有一个极简的UI,让餐馆对他们的菜单做基本的改变。虽然这个工具对于许多菜单调整(如更新价格)已经足够了,但是它不能适应其他用例,例如,将一个项目标记为“脱销”。
    • 望远镜:Uber员工使用望远镜,一个内部用户界面,让我们可以改变餐厅合作伙伴菜单的几乎任何方面。

在Uber Eats的上下文中,“item”是指顾客可以订购的单一产品。通常,菜单重复相同的项目。例如,一个汉堡可以作为一个独立的项目出现,也可以作为一个组合的一部分。菜单1.0遭受了一些关于重复项目的缺点,包括不能:

    • 重复使用菜单上多个位置的项。同一项的每个实例都需要该项的新副本,这是一个冗长而令人困惑的过程。此外,餐馆合作伙伴还需要从原始商品中复制每个副本的元数据,比如描述。
    • 检索项目的准确度量。例如,如果我们想知道某个特定商品在同一个菜单上出现多次的总销售额,我们需要将该商品的每个副本的销售额加起来。
    • 每当餐馆合伙人用完一件商品时,在每一份商品上标记为“缺货”,这就增加了过程中不必要的时间和精力。如果我们遗漏了任何一份,我们的合作伙伴可能会提供不完整的订单,让我们的顾客不高兴。

在“优步吃”1.0版菜单上,约25%的菜单都是重复的。

2018年,我们推出了菜单2.0,我们的新菜单数据模型,支持项目共享,解决了上述所有问题。它还提供了菜单1.0所缺乏的许多其他有用的特性,比如针对更复杂菜单的灵活定价模型。

由于Uber Eats平台上有许多不同的应用程序,它们都是为了支持菜单1.0而构建的,所以迁移到菜单2.0并不是一件容易的任务。特别是,菜单编辑器和望远镜都包含需要更新的特定于菜单1.0的业务逻辑,包括用过时的软件库和工程模式构建的工具。随着Uber Eats业务的持续扩张,我们将菜单2.0视为一个机遇,以一种增长和可扩展性的思维方式来发展,整合新的市场、餐厅和商品。

在菜单2.0中,Uber Eats菜单的结构添加了一组新的字段和定义字段之间如何定义关系。菜单编辑器和望远镜使它更容易与菜单1.0数据模型交互,但菜单2.0需要一种新的方法。为了进一步简化Uber Eats菜单数据模型,我们创建了一个名为menu Maker的新工具,它合并了菜单编辑器和望远镜的元素,并让Uber工程师和餐馆合作伙伴管理菜单。

产品概述

菜单制作器是一个基于web的工具,它包含几个页面,每个页面都有自己的一组功能和信息。顶部的导航栏提供了在页面之间快速切换的方法,如下面的图1所示:

图1所示。在最初的登录页面上,“菜单制作器”提供了当前菜单结构的高级概述,包括可用时间、类别和项目。

就像文本文档由独立的组件(如标题和段落)组成一样,Uber Eats上的菜单也被分解成类似的独立实体,如条目和类别,它们代表了整个菜单的构建块。在Menu Maker上,合作伙伴可以查看特定类型实体的完整列表,如下图2所示:

图2。点击顶部导航标题中的“Items”,就会出现餐厅的整个项目列表。可以根据用户的需要对列表进行过滤或排序。在这个页面上,用户可以单击一个单独的项目,这将导致一个更详细的页面,允许用户修改该项目。

在一个位置查看特定类型的所有实体,比如价格或食品类别,可以方便地快速找到餐馆合作伙伴想要修改的实体。实体列表包括关于每个实体的信息,这些信息通过描述解释了其上下文。例如,在项目列表中,用户可以看到每个项目属于哪个类别。单击实体的名称会将用户带到另一个页面,用户可以在该页面中编辑、删除或复制该实体。

图3。当用户在商品列表中点击“pancake”后,他们会被带到一个商品页面,在那里他们可以编辑商品的各种属性,比如商品的描述和价格。

一个特定实体的页面包含一个web表单,其中包含用户可以修改的所有属性的字段。表单确保用户输入的值在保存之前是有效的。

我们添加到Menu Maker的大多数功能都是直观和用户友好的。然而,在内部,我们使用逻辑来增强这些特性,从而促进更大的功能。其中一种功能是实现上下文覆盖的能力,允许实体根据它们在菜单中的位置而做出不同的行为。

上下文覆盖

在我们的菜单1.0数据模型中不能多次重用同一项是有问题的,这导致了技术债务和不必要的复杂性。有了上下文覆盖,菜单制作器允许用户在菜单重复使用特定项目时使价格覆盖。例如,假设一般情况下,一家餐厅将薯条作为独立商品出售4美元,但作为附赠出售时是免费的。换句话说,根据上下文的不同,薯条的价格可以是4.00美元或0.00美元,如下图4和5所示:

图4。在这个特别的菜单中,炸薯条单独一项的价格是4美元。

图5。在与图4相同的菜单中,食客选择作为配菜的薯条花费0.00美元,尽管在数据模型中是相同的项目。

使用Menu Maker,我们可以使用如下所示的灵活JSON格式表示任何可覆盖的字段:

上面,T表示一个泛型类型,它根据上下文重写属于哪个字段而不同。对于像价格这样的东西,T就是一个数字。对于不同的字段,例如指定可用时间的字段,T可能是包含日期和时间的结构。

每个实体都有自己的一组可重写字段,重写仅适用于特定的实体。因此,我们的fries项目的价格字段可能是这样的:

每个重写都有一个上下文,在其中重写变得相关。在上面的例子中,当物品被用作“选择一方”的选项时,价格为0 (contextValue:“choose_side_uuid”)。如果一个菜单在很多地方重用一个项,这个字段可能会有一个包含多个重写的长列表。

Menu Maker在幕后管理上下文覆盖,将它们从UI中抽象出来,并因此提供一个无缝的、功能强大的Uber Eats菜单,可以轻松容纳重复的项目,直观地创建和维护。

检查菜单更改

为了确保Uber Eats始终提供优质的产品,我们在升级到Menu 2.0和Menu Maker时采取了保护用户的措施。为此,我们在Menu Maker中构建了对策,以防范平台上的不良行为。

大多数的变化对menu Maker中的菜单立即生效(例如,新项目的添加或减法),但在某些情况下,比如价格变化,我们会在它们上线之前暂时标记更改以进行审查。为了简化这个过程,我们在Menu Maker中创建了一个新的系统来评估和批准价格变化。

具体来说,当一家餐馆的合作伙伴想要在Menu Maker中更改价格时,它必须经过一系列具体的批准,然后我们才能进行修改。首先,餐厅合作伙伴必须提交一个调整项目成本的请求,如下图6所示:

图6。当餐馆合作伙伴试图更改某项商品的默认价格时,菜单制作者会通知他们,该商品的价格将保持当前价格,直到优步批准更改。根据调整,如果用户继续更改价格,将出现一个弹出框。这个框会通知他们Uber Eats需要获得批准。此时,餐厅合作伙伴可以点击绿色按钮请求批准或取消更改。在优步Eats团队批准该请求之前,该商品的价格将保持不变。

当餐厅合作伙伴提交了更改价格的请求时,我们将项目价格视为“待定”。“即将到来的变化带来了新的挑战;当他们处于这种状态时,就有两种不同的消息来源:“当前”的消息和“待处理”的消息。一旦我们的运营团队批准了更改,我们的后端将用未决项覆盖当前项。如果同时实现多个更改,则此重写过程将被执行击败这些改变,在过程中撤销它们。例如,如果有人更改了项的名称,则重写将把项恢复为原来的名称。

处理这些问题并非无关紧要;任何使用过软件版本控制系统的人都很熟悉所涉及的边缘情况。在我们即将进行的调整中,我们遇到了如下问题:如果批准了已删除项目的价格更改,会发生什么情况?如果有多个待定价格,我们在菜单制作器中显示什么价格?

我们采用了一种更简单的方法:锁定挂起的项,防止对它们进行任何进一步的更改,而不是使用一个解决方案来解决所有这些场景。一旦更改被批准或拒绝,我们将允许Menu Maker实现进一步的项目修改。

这种版本控制方法将问题空间缩小到更易于管理的大小,因为它确保同一项永远不会有多个挂起更改。当然,我们还需要锁定依赖于该项的任何其他实体,因为那些实体可能影响挂起项的上下文覆盖。

在下面的例子中,我们有一个名为“鸡肉三明治”的项目,在“选择三明治”下,食客可以看到它是一个选项。如果“Chicken sandwich”由于挂起的更改而被锁定,菜单生成器也会锁定“Choose a sandwich”,因为它们之间有依赖关系,如下图7所示:

图7。因为“鸡肉三明治”(蓝色圈出)有未决的更改(如图6所示),“选择三明治”被锁定,由左边的锁定符号表示(红色圈出)。此锁符号表示餐厅合作伙伴在“鸡肉三明治”的更改未获批准之前,不能更改“选择三明治”的任何内容。

锁定待修改项目的依赖实体可以让我们避免棘手的边缘情况和版本问题,这些问题可能会破坏我们的系统,反过来改善我们的工程师、食客和餐厅合作伙伴在应用程序上的体验。

推出菜单制作器

一旦我们准备好与世界共享菜单制作器,我们需要弄清楚如何以一种非破坏性的方式向习惯了我们之前的Uber Eats菜单UI的餐厅合作伙伴推出它。为了使过渡尽可能顺利,我们查看了优步Eats餐厅工具生态系统的其余部分。

餐厅经理优步Eats为餐厅合作伙伴提供的门户网站,是我们餐厅分析、订单总结和现在菜单制作的门户。我们开发菜单制作器作为一个自包含的web应用程序,因此我们可以利用它Fusion.js, Uber的开源web框架,不需要迁移Restaurant Manager的其余部分,它运行在一个旧的框架上。这一策略意味着我们现在有两个独立的应用程序,餐厅管理器和菜单制作器,我们需要有条件地在它们之间转换餐厅。

利用NGINX我们可以根据URL将用户路由到适当的web应用程序。例如,nginx代理所有以url开头的请求https://restaurant.uber.com默认给餐厅经理。

对于这个用例,我们决定在与Restaurant Manager相同的域中代理一个特定的URL到Menu Maker,这样我们就可以通过简单地调整特定餐厅的菜单编辑器链接,有选择地将其推出,如图8所示。这种方法比一次性的全球发布更可取,因为它允许我们一次只支持一个地区,并在beta发行期间快速修复漏洞。

图8。根据链接URL,当用户点击链接时,要么进入菜单编辑器(在与Restaurant Manager相同的应用程序中),要么进入菜单制作器(一个完全独立的应用程序)。

基于URL路由到不同的应用程序使我们更容易保持系统行为的一致性。在我们的测试环境中,我们维护了两个独立的应用程序,一个用于餐厅管理,另一个用于菜单制作。我们还可以在测试域中使用完全相同的代理策略,在该域中的应用版本之间进行切换。

我们的分阶段推出策略是成功的;随着时间的推移,我们能够有条不紊地向所有Uber Eats地区推出菜单制作器,让我们能够专注于每个地区出现的具体问题或漏洞。

继续使用菜单制作器

与任何大型菜单工具系统一样,building menu Maker需要大量的创造力和灵活性,并鼓励我们探索替代的技术途径。随着Uber Eats不断开拓新市场,欢迎越来越多的餐厅合作伙伴加入我们的平台,我们很乐观地认为,我们的模块化架构和用户至上的数据模型开发方法将促进无缝增长和更高的效率。

致谢

“菜单制作者”项目历时好几个月,横跨Uber Eats的多个组织;没有参与的每个人的宝贵支持,启动是不可能的。在整个过程中,我们得到了优步Eats餐厅组织的工程和产品团队的大力支持。

没有帖子显示