From 376e28930ffbd3b8ae23d847ca975443f9876838 Mon Sep 17 00:00:00 2001 From: API Changelog Bot Date: Thu, 21 May 2026 10:49:56 +0800 Subject: [PATCH] =?UTF-8?q?changelog:=20refactor=20product-v2=20=E9=A4=90?= =?UTF-8?q?=E9=A5=AE=E9=80=89=E9=A1=B9=20meal=5Foption=20=E5=AD=97?= =?UTF-8?q?=E5=85=B8=E5=8C=96=20(#2778)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...21_refactor_product_v2_meal_option_dict.md | 119 ++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 changelogs/2026-05/21_refactor_product_v2_meal_option_dict.md diff --git a/changelogs/2026-05/21_refactor_product_v2_meal_option_dict.md b/changelogs/2026-05/21_refactor_product_v2_meal_option_dict.md new file mode 100644 index 0000000..ee936b3 --- /dev/null +++ b/changelogs/2026-05/21_refactor_product_v2_meal_option_dict.md @@ -0,0 +1,119 @@ +# refactor(product-v2): 餐饮选项 meal_option 由硬编码改为系统字典 + 新增「含(正餐)」 + +**日期**:2026-05-21 +**PR**:#2783(已合 dev → 已合 dev-v3 → 已部署测试服并验证) +**工单**:#2778 +**影响端**:管理后台前端(hl-ui) + +--- + +## 背景 + +原「餐饮安排」下拉框 4 个选项(含酒店 / 含营地 / 含特色餐 / 自理)在前端 `MealConfig.vue` 和后端 `ProductItineraryAggregateFacade` 都是**硬编码**。每次加一个选项都要前后端同时改代码、走 PR、上线。 + +本次改造: +- 后端 switch 硬编码 → 改为读 `meal_option` 字典 +- 字典数据库里新加一条 `REGULAR` = 「含(正餐)」 +- 以后再加选项只在字典管理后台点一下即可,**前后端代码无需任何改动** + +--- + +## 字典数据(meal_option) + +测试服已落库,可通过 `GET /admin/dict/data/meal_option` 拉到: + +| sortOrder | dictValue | dictLabel | 说明 | +|-----------|-----------|-----------|------| +| 1 | `HOTEL` | 含(酒店) | 历史值 | +| 2 | `CAMP` | 含(营地) | 历史值 | +| 3 | `SPECIAL` | 含(特色餐) | 历史值 | +| 4 | `REGULAR` | **含(正餐)** | **本次新增** | +| 5 | `SELF` | 自理 | 历史值(默认值) | + +--- + +## 🚨 前端需要做的事 + +### 1. 删除硬编码 + +文件:`hl-ui/src/views/product/edit/components/itinerary/MealConfig.vue` + +把第 22-27 行的硬编码数组**删掉**: + +```js +// 删掉这段 +const mealOptions = [ + { label: '含(酒店)', value: 'HOTEL' }, + { label: '含(营地)', value: 'CAMP' }, + { label: '含(特色餐)', value: 'SPECIAL' }, + { label: '自理', value: 'SELF' }, +] +``` + +### 2. 改成调字典接口 + +调用:`GET /admin/dict/data/meal_option`(已存在的字典 API) + +**Header**:需要 admin token + +**响应结构**: + +```json +{ + "code": 200, + "message": "成功", + "data": [ + { "dictValue": "HOTEL", "dictLabel": "含(酒店)", "sortOrder": 1, "status": "ACTIVE" }, + { "dictValue": "CAMP", "dictLabel": "含(营地)", "sortOrder": 2, "status": "ACTIVE" }, + { "dictValue": "SPECIAL", "dictLabel": "含(特色餐)", "sortOrder": 3, "status": "ACTIVE" }, + { "dictValue": "REGULAR", "dictLabel": "含(正餐)", "sortOrder": 4, "status": "ACTIVE" }, + { "dictValue": "SELF", "dictLabel": "自理", "sortOrder": 5, "status": "ACTIVE" } + ] +} +``` + +### 3. 下拉绑定动态数据 + +`n-popselect` 的 `:options` 改为从接口返回的数组映射(注意现有字典 store 可能已经封装好,可以复用): + +```js +// 推荐:直接复用全局 dictStore(如果已有) +const mealOptions = computed(() => + dictStore.getDictData('meal_option').map(d => ({ + label: d.dictLabel, + value: d.dictValue + })) +) +``` + +如果暂时没全局字典 store,组件级别 `onMounted` 拉一次即可(字典本身后端有 5 分钟 Redis 缓存,不会打到 DB)。 + +### 4. 校验逻辑无变化 + +`isSelf(value)` 仍然按 `value === 'SELF' || !value` 判断 — `SELF` 是业务语义的「自理」,前端这层判断不动。 + +--- + +## 兼容性 + +- ✅ 历史数据(HOTEL/CAMP/SPECIAL/SELF)**全兼容**,已上线的产品数据无需迁移 +- ✅ 字典服务挂掉时后端会 fallback 到 dictValue 字面值(订单详情不会显示空白) +- ✅ 字典本身有 Redis 5 分钟缓存,QPS 无压力 + +--- + +## 测试服验证记录 + +- 测试服 https://web.test.1814.love:9443 已部署 commit `b28cef1c7`(dev-v3) +- 字典查询接口返回 5 条数据 ✓ +- 产品详情接口对 5 种 meal_option(HOTEL/CAMP/SPECIAL/REGULAR/SELF)全部返回正确 dictLabel ✓ +- 无 token 调用返 `code: 401` ✓ + +--- + +## 联系人 + +后端:wx(呼籁旅行) +前端:mmg + +如有疑问或字典接口路径需要确认,可在 [#2778](https://git.1814.love:8443/wx/HL/issues/2778) 评论区留言。