# 2026-09-28 ## 食物营养成分表建表(中国食物成分表) - 新增 `src/main/resources/db/migration_add_food_nutrition.sql`(表 `food_nutrition`),并按项目惯例同步追加到 `db/schema.sql`。 - 数据来源:`https://nlc.chinanutri.cn/fq/`(中国疾控中心营养与健康所)。 - 列表接口:`POST /fq/FoodInfoQueryAction!queryFoodInfoList.do` - 参数:`categoryOne` / `categoryTwo` / `foodName` / `pageNum` / `field` / `flag` - 返回:`{currentPage, totalPages, list: [[...36 元素...]]}` - 列定义来源:`/fq/foodlist_0_0_0_0_0_1.htm` 里 `th.ysq_code_*` 的 class 名(页面为 UTF-8)。 - 数组下标 = 表头序号 + 2([0]=食物ID、[1]=图片、[2]=食物名称,之后 34 个营养素)。 - 关键字段名:`huifen`=灰分、`tshhw`=碳水化合物、`qianwei`=总膳食纤维、`afte`=α-TE(维生素E)、 `las`=硫胺素、`hhs`=核黄素、`yanSuan`=烟酸、`wssC`=维生素C;末尾 4 个是 SFA/MUFA/PUFA 百分比与合计。 - 接口空值:`""`=无数据、`"—"`=未检出,入库统一 NULL;值自带单位后缀(`12.7g`/`5.40μg`/`1478kJ`/`100%`)。 - 分类为两级:categoryOne 1..28(大类,如 1=谷类及制品),categoryTwo 30..128(子类,如 30=小麦)。 - 校验方法:碳水 = 100 - 水分 - 蛋白质 - 脂肪 - 灰分(小麦粉各行完全吻合),据此确认了 `huifen`=灰分为第 11 位。 - 修正(同上):数组共 36 元素,[0]ID [1]图片 [2]名称 [3]别名 [4]英文名 → [5]~[35] 共 **31 个**营养素,不是 34 个。 ## 抓取全量数据 + 生成 INSERT(同日完成) - 新增 `tools/fetch_food_nutrition.py`(抓取 + 生成 SQL,带磁盘缓存、可断点续跑、支持 `--offline` 重生成)、 `tools/verify_sql.py`(结构/列序/逐值一致性校验)。`tools/_cache/` 为 557 个原始 JSON 响应的缓存,已加入 `.gitignore`。 - 产物 `src/main/resources/db/data_food_nutrition.sql`:**1356 条**食物,0.68MB,7 条批量 INSERT(200 行/批,41 列)。 - 抓取策略:先用 `categoryOne=0&categoryTwo=0` 拿全量(即全集,170 页 × 8 条/页), 再遍历 20 个大类 + 91 个子类补分类标签;首页共 111 个分类链接,已全部覆盖。 - 数据结论:`Tr`(微量)513 处,是唯一无法转数字的 token;只有 `枸杞子(1571)`、`菊花(1613)` 在源站没有分类归属(留 NULL)。 - 校验结果:1356 条 × 41 字段全部正常;DDL 列序 == INSERT 列序;31 个营养素逐值与接口原始数组完全一致;`raw_values` JSON 合法。 - 脚本关键点:`to_num()` 剥离单位后缀并统一 `""`/`—`/`Tr` → NULL;`sql_str()` 转义 `\` 与 `'`; `verify_sql.py` 里按「单引号字符串 + 括号嵌套」扫描切分元组,切分前必须先剥掉最外层圆括号。