删除 android/、ios/ 两个 Capacitor 原生工程与 capacitor.config.ts、 ionic.config.json,以及 scripts/android、scripts/ios.sh 构建脚本; 根与 apps/web 的 @capacitor/* 依赖和相关 npm scripts 一并移除。 Web 侧去掉原生分支:登录只走云乐坊容器授权面板或顶层重定向, 暗色模式不再改原生状态栏,移除 Android 物理返回键处理。 保留 @nuxtjs/ionic(Web UI 框架)、云乐坊 window.ylf 桥接与 PWA。 决策记录见 docs/decisions/0004-web-only.md。
10 KiB
10 KiB
name, description, metadata
| name | description | metadata | ||||
|---|---|---|---|---|---|---|
| add-recipe | 给 Cook(食用手册)新增菜式。当用户说「增加菜式」「加几道菜」「添几个菜谱」「补点新菜」「增加 XX 类菜」「加个 XX 做的菜」时使用。按「先摸清现有数据 → 上网找数据里没有的新菜 → 拆解成食材/做法/厨具/菜系类别 → 写入并校验」的流程执行。 |
|
新增菜式
你在给 Cook(食用手册)补充菜谱数据。每次运行把若干道新菜写进 packages/data/recipe.csv,跑完校验后向用户汇报。
数量规则
- 用户明确说了数量(「加 10 道」「补 5 个」),就按用户说的来。
- 用户没说数量,默认加 5 道菜。
- 用户指定了主题或范围(「加几道川菜」「补点用砂锅的菜」),数量仍按上一条判断。
数据落点
只有一个文件要改:packages/data/recipe.csv。
列为 name,stuff,bv,difficulty,tags,methods,tools,id,单元格内多个值用顿号「、」分隔(不能用逗号、斜杠、分号)。
name,stuff,bv,difficulty,tags,methods,tools,id
青椒炒肉,青椒、猪肉,,普通,家常菜,炒,一口大锅,637
电饭煲版广式腊肠煲饭,腊肠、米,BV1NE411Q7Jj,简单,广式,煲,电饭煲,0
| 列 | 规则 |
|---|---|
| name | 必填。菜名要能让人一眼看出做法,习惯写成「厨具版+做法+主料」或「主料+做法」 |
| stuff | 必填。只能填 packages/data/food.json 里的食材标准名,具体品种(虾仁、鲈鱼)要写成标准名(虾、鱼) |
| bv | B 站 BV 号或 av 号。可以留空——应用里点菜走的是 B 站搜索链接,不是这个视频号;填了就必须是真实可播放的视频 |
| difficulty | 简单 / 普通 / 困难,或留空 |
| tags | 菜系类别 + 其它标签(快手菜、下饭、汤、杂烩、辣…),见下表 |
| methods | 做法(炒、煎、蒸、煮、煲、烤、炸…),见下表 |
| tools | 只能填 packages/data/food.json 里的厨具名 |
| id | 必填,非负整数且唯一。新菜用当前最大 id + 1(现在是 637、638…) |
id 一旦发布就不能修改、不能复用。 收藏、反馈、筛选信号都按 id 记录。新菜一律追加到文件末尾,id 递增。
取值白名单
食材(35 个,来自 packages/data/food.json)
- 蔬菜:土豆、胡萝卜、花菜、白萝卜、西葫芦、番茄、芹菜、黄瓜、洋葱、莴笋、菌菇、茄子、豆腐、包菜、白菜、青椒、韭菜、南瓜
- 肉蛋水产:午餐肉、香肠、腊肠、鸡肉、猪肉、鸡蛋、虾、牛肉、骨头、鱼、蟹
- 主食:面食、面包、米、方便面、玉米、红薯
需要列表里没有的食材时,先在 packages/data/food.json 里加,再加别名到 packages/data/aliases.json(如「柿子椒」→「青椒」),然后才写进菜谱。别名映射用户输入的写法到标准名。
厨具(6 个)
烤箱、空气炸锅、微波炉、电饭煲、一口大锅、砂锅
菜系类别(tags 里的菜系标签)
必须从下表里选标签值写进 tags,选了哪个菜系筛选就只认哪个标签:
| 菜系 | 可用标签 |
|---|---|
| 川菜 | 川菜 |
| 湘菜 | 湘菜 |
| 粤菜 | 粤菜、广式 |
| 苏菜 | 苏菜 |
| 东北菜 | 东北菜 |
| 西北菜 | 西北特色 |
| 日式 | 日式、日式菜 |
| 意式 | 意式 |
| 法式 | 法式、法餐 |
| 西式 | 西式 |
| 国宴菜 | 国宴菜 |
每道新菜都尽量打上菜系标签。 应用里一旦选了菜系,只有带该标签的菜会出现——标签不全会导致选了菜系就几乎没结果。
做法分组(methods + tags 的组合)
用户在应用里选的「做法」是分组,按下面的规则命中:
| 做法分组 | 怎么填 | 备注 |
|---|---|---|
| 炒菜 | methods 填「炒」 | |
| 汤羹 | tags 填「汤」,methods 填煮/煲/炖 | 靠标签识别,不是靠 methods |
| 炖煮 | methods 填炖、煮、煲、卤、焖、烧、灼 | |
| 蒸 | methods 填「蒸」 | |
| 炸煎 | methods 填炸、煎、烙 | |
| 烤 | methods 填烤、烘、焗 | |
| 凉拌 | methods 填拌、凉拌、腌、腌制、泡菜、油泼 | |
| 微波 | methods 填「微波加热」 | |
| 火锅 | tags 填「杂烩」,且食材 ≥10 种 | 靠标签识别;食材少于 10 种不算大杂烩 |
「辣」标签同样靠 tags 识别,选了「不吃辣」时会排除带此标签的菜。
步骤
1. 摸清现有数据
先搞清楚「已经有什么、缺什么」,不要凭印象加菜。
# 全部菜谱(638 行,含表头)
cat packages/data/recipe.csv
# 菜谱总数
wc -l packages/data/recipe.csv
# 现有菜系分布:每种菜系各有多少道
node -e "const fs=require('fs');const l=fs.readFileSync('packages/data/recipe.csv','utf8').trim().split(/\r?\n/).slice(1);const c={};l.forEach(r=>{const t=r.split(',')[4]||'';t.split('、').filter(Boolean).forEach(x=>{if(/菜|式|广式|法餐/.test(x))c[x]=(c[x]||0)+1})});console.log(c)"
# 某个食材出现在多少道菜里(找出只有 1-2 道菜的冷门食材)
node -e "const fs=require('fs');const l=fs.readFileSync('packages/data/recipe.csv','utf8').trim().split(/\r?\n/).slice(1);const c={};l.forEach(r=>r.split(',')[1].split('、').filter(Boolean).forEach(x=>c[x]=(c[x]||0)+1));console.log(Object.entries(c).sort((a,b)=>a[1]-b[1]))"
# 某个厨具下有多少道菜
node -e "const fs=require('fs');const l=fs.readFileSync('packages/data/recipe.csv','utf8').trim().split(/\r?\n/).slice(1);const c={};l.forEach(r=>r.split(',')[6].split('、').filter(Boolean).forEach(x=>c[x]=(c[x]||0)+1));console.log(c)"
同时读 packages/data/food.json(食材/厨具词典)和 packages/data/aliases.json(别名)。
重点找这几类空洞,补菜时优先填:
- 菜系空洞:某个菜系只有 1-2 道菜,用户一选就几乎没结果。
- 冷门食材:某个食材只出现在 1-2 道菜里,选它必然匹配不到。
- 厨具 × 食材交叉:某个食材配某个厨具一道菜都没有(比如砂锅只有十来道菜,覆盖的食材很窄)。
- 做法 × 菜系交叉:某菜系里缺某类做法(比如粤菜没有凉拌、日式没有汤)。
2. 上网找新菜
- 围绕要补的空洞找菜:「XX 家常做法」「XX 砂锅」「XX 快手菜」这类关键词。
- 优先选常见、做法明确、食材在词典里的家常菜。冷门菜、分子料理、需要特殊设备的不要。
- 判重:菜名和已有菜重复的、或同名同做法的,不加。先用步骤 1 的命令或直接搜 CSV 确认。
- 不要编造菜谱或视频号。找不到真实信息就换一道菜,别硬凑。
- 找视频(可选):搜索时带上
bilibili.com/video关键词,从结果里拿 BV 号。
3. 拆解并写入
对每一道新菜,逐列拆开:
- 菜名:
青椒炒肉 - 食材:拆成词典里的标准名 —— 青椒、猪肉
- 厨具:这道菜实际用什么锅做 —— 一口大锅
- 做法:methods 填具体动作 —— 炒
- 菜系类别:tags 填菜系标签 —— 川菜、家常菜
- 难度:简单 / 普通 / 困难,拿不准留空
- id:从当前最大 id + 1 开始,逐个递增
然后追加到 packages/data/recipe.csv 末尾(不要改已有行)。
4. 校验
按顺序跑完,全绿才算完成:
pnpm convert # CSV → JSON,必须先跑
pnpm validate # 有错误时退出码为 1
pnpm fixtures # 重新生成 recommend.json(改了数据必须重跑)
pnpm test -- --run
pnpm lint
常见问题:
pnpm validate报「食材/厨具不在词典中」 —— 名称不对,改成标准名;词典里确实没有就先加进food.json。pnpm test失败且是test/recommendations.test.ts里的counts断言 —— 新菜改变了推荐结果的分组数量,属于预期变化,更新这些数字即可。pnpm test报 fixtures 不一致 ——pnpm fixtures没跑或跑漏了。pnpm lint报 md 文件格式错 —— 文档类改动注意行尾和代码块内不要留多余空行。
5. 汇报
告诉用户:
- 加了几道菜,每道的:菜名、食材、厨具、做法、菜系、难度、id
- 补的是哪些空洞(比如「补了砂锅 + 香菇 3 道」「粤菜凉拌 2 道」)
- 校验结果(
pnpm validate错误数、pnpm test通过情况) - 哪些地方拿不准、需要用户定夺(比如新加的食材名、没找到合适视频的菜)
注意事项
- 不要删除或修改已有行。发现已有数据有问题(重复、失效视频)时,在汇报里指出,让用户决定。
- 不要改
packages/cook/src/utils/recommend.ts里的筛选逻辑,也不要改test/下的断言来「凑」结果——断言变化只允许是数据量自然变化导致。 packages/data/recipe.json和packages/data/fixtures/recommend.json是生成物,不要手写,改 CSV 和用例输入后用pnpm convert/pnpm fixtures重新生成。