菜品与菜谱分类统一设计

1. 目标

在菜品列表、菜谱列表及菜品识别页的两个替换入口中,统一使用以下分类顺序。业务分类只定义一次,但两个接口各自保留真实的服务端编号契约:

展示顺序 显示标签 菜品列表 /api/consume/dishes 菜谱 /p/api/getMeal 分组 key
0 全部 空值 无独立分组
1 主食 1 1
2 荤菜 3 3
3 素菜 2 2
4 水果 5 5
5 蛋奶坚果 4 4
6 汤饮 6 6

分类顺序、服务端编码、显示资源和服务端名称别名只能在公共分类模型中定义一次。页面不得再维护分类字符串数组、数字转换 switchdishItemList1dishItemList6 的手工对应关系。

2. 当前问题

2.1 分类契约散落在页面中

NewDishLearningActivityDishMenuManagementActivityDishRecognitionActivity 分别保存了分类标签、分类顺序、请求编码转换或菜单响应分组逻辑。同一规则存在多份副本,修改任意一份时都可能遗漏其他入口。

2.2 菜品和菜谱使用不同的解释路径

两条路径的业务分类和当前数字值一致,现场最终确认均为 2=素菜、3=荤菜、4=蛋奶坚果、5=水果。模型仍分别保存菜品列表请求值和菜谱响应 key,使调用者明确选择接口边界,避免未来协议变化或页面自行换位。

2.3 异常分类被静默归入主食

菜品列表在“全部”条件下遇到无法识别的 tag.name 时会回退到“主食”。这会隐藏服务端数据异常并造成错误分类。

2.4 响应模型暴露无语义字段

GetDishMenuResponse 对外暴露 dishItemList1dishItemList6。所有调用者都需要再次知道数字与业务分类的关系,导致硬对应扩散到页面、缓存和同步策略。

3. 统一设计

3.1 公共分类模型

新增 DishCategory 分类模型,职责是定义客户端认可的分类语义及两个接口各自的编码契约,并提供安全解析能力。该类需要有明确的类级注释,说明它只负责分类编码、顺序、显示资源和服务端名称归一化,不负责网络请求和界面渲染。

每个分类包含:

公共方法至少覆盖:

全部 是查询范围,不是实际菜品类别。未知分类也不是 全部主食,解析时必须明确区分。

3.2 菜谱响应按编码索引

GetDishMenuResponse 改为按菜谱接口原始分组编码保存菜品列表,并通过分类模型中的菜谱分组映射提供以下能力:

页面和其他业务类不再访问 dishItemList1dishItemList6

菜单响应解析应遍历服务端响应对象的实际 key,通过 DishCategory.fromMenuGroupCode() 解析已知编码,而不是套用菜品列表请求编码,也不在转换器中逐项写死六个字段。

3.3 页面统一使用分类对象

菜品学习页

NewDishLearningActivity 使用公共有序分类列表创建标签。请求时读取所选分类的菜品列表接口编码,响应中的 tag.name 使用公共名称解析方法处理。

删除页面内的:

菜谱管理页

DishMenuManagementActivity 使用公共有序分类列表创建标签,并使用按分类索引的数据结构保存页面数据。页面通过分类对象从 GetDishMenuResponse 读取列表,由响应模型统一把业务分类转换为菜谱分组 key,不再维护六个具名列表,也不再手工绑定响应字段。

“全部”列表必须按照主食、荤菜、素菜、水果、蛋奶坚果、汤饮的顺序合并。

菜品识别页

DishRecognitionActivity 的“替换菜谱”和“替换后学习”两个入口都使用同一个公共分类模型:

3.4 非展示消费者统一遍历

DishMenuStoreDishManagementSyncPolicy 虽然不显示分类,但也不再逐字段读取六组菜单。两者使用 GetDishMenuResponse 提供的统一遍历或全部菜品接口,避免继续依赖旧响应结构。

DishCatalogRepository 只负责全量同步菜品目录,不解释分类标签,因此不需要修改。

4. 异常处理

5. 布局和资源

现有三个布局中的分类区域都是动态容器,不包含分类标签或分类顺序,因此不修改 XML 布局:

strings.xml 中新增或复用“全部、主食、荤菜、素菜、水果、蛋奶坚果、汤饮”的字符串资源。页面只通过分类模型持有的资源 ID 获取显示文案。

6. 测试设计

新增公共分类模型单元测试,至少验证:

  1. 标签顺序固定为全部、主食、荤菜、素菜、水果、蛋奶坚果、汤饮;
  2. 菜品列表接口编码 1~6 分别对应主食、素菜、荤菜、蛋奶坚果、水果、汤饮;
  3. 菜谱接口分组 1~6 分别对应主食、素菜、荤菜、蛋奶坚果、水果、汤饮;
  4. “肉类、菜类、汤类”等服务端名称别名归一化正确;
  5. 未知编码和未知名称不会归入主食;
  6. 菜谱响应按标准分类顺序聚合;
  7. 未知菜谱分组只出现在“全部”范围;
  8. 服务端缺少部分分组时返回空列表且不崩溃。

更新现有 DishManagementSyncPolicyTest,改用新的菜单响应写入和遍历接口。完成后运行:

./gradlew :app:testDebugUnitTest :app:assembleDebug
git diff --check

7. 影响范围

计划修改:

明确不修改:

8. 验收标准