Skill: sj13818161942/diet-log (version 1.0.0)

--- name: diet-log description: 饮食记录与营养分析助手。当用户提到"饮食记录"、"记下我吃了"、"营养分析"、"统计饮食"、"最近X天吃了什么"等关键词时触发。功能包括：(1) 解析用户输入的饮食内容并查询营养数据；(2) 计算并记录全部营养素（热量、宏量营养素、脂肪酸、矿物质、维生素）；(3) 将饮食记录存入 meal_log.json；(4) 支持阶段性营养统计（按日/周/月）。食物营养数据文件 food_data.json 需单独下载（见 SKILL.md 同目录下的说明或 GitHub 仓库）。食物匹配采用三级策略：精确匹配 → 同类参考 → 提问确认。 --- # 饮食记录 (diet-log) ## ⚠️ 首次使用：下载食物营养数据 本 Skill 需要下载食物营养数据库文件（约 3.7MB），请在 GitHub 仓库中下载 `references/food_data.json` 放置到本 Skill 的 `references/` 目录下： - GitHub 仓库：https://github.com/sj13818161942/diet-log - 直接下载：点击 `references/food_data.json` → 点击 Download 按钮 下载后确保文件路径为： ``` diet-log/references/food_data.json ``` ## 数据来源 - 文件：`references/food_data.json`（1643条食物记录，**需手动下载**） - 原始数据来自 foodwake.com，通过 LuckyHookin/foodwake 项目获取（Apache-2.0 授权） - 食物分类：谷类/薯类/豆类/蔬菜/菌类/藻类/水果/坚果/畜肉/禽肉/乳类/蛋类/河海鲜/茶类/酒类/油类/调味品类/零食饮料 ## 食物匹配策略（三级） 当用户输入食物名称时，按以下顺序处理： ### 第一级：精确匹配 在数据库中搜索名字或别名**包含**用户输入的食物名称的记录，取第一条结果。 ### 第二级：同类参考 若精确匹配无结果，在**同一类别**中查找最接近的食物作为参考，并在记录中标注「参考值」。 常见类别映射： - 用户说"鸡腿/鸡肉" → 参考数据库中畜肉/禽肉类的鸡肉数据 - 用户说"猪肉/排骨" → 参考畜肉类 - 用户说"河鱼/草鱼" → 参考河海鲜类 ### 第三级：提问确认 若同类参考也无法找到（如罕见食材），向用户提问： > "我没有找到【XXX】的精确数据。请问它的类别是？例如：肉类/鱼类/蔬菜/豆制品/主食/其他？"或"你说的XXX是指YYY吗？" ## 营养分析维度（全字段） 分析并记录以下所有可用营养素（若数据缺失则记为0）： ### 宏量营养素（核心） | 字段 | 说明 | 单位 | | :--- | :--- | :--- | | energy_kcal | 能量 | 千卡 (kcal) | | protein_g | 蛋白质 | 克 (g) | | fat_g | 脂肪 | 克 (g) | | carbs_g | 碳水化合物 | 克 (g) | | fiber_g | 粗纤维 | 克 (g) | ### 脂肪酸 | 字段 | 说明 | | :--- | :--- | | saturated_fat_g | 饱和脂肪酸 | | monounsaturated_fat_g | 单不饱和脂肪酸 | | polyunsaturated_fat_g | 多不饱和脂肪酸 | | trans_fat_g | 反式脂肪酸 | ### 矿物质 | 字段 | 说明 | | :--- | :--- | | calcium_mg | 钙 (mg) | | magnesium_mg | 镁 (mg) | | sodium_mg | 钠 (mg) | | potassium_mg | 钾 (mg) | | phosphorus_mg | 磷 (mg) | | iron_mg | 铁 (mg) | | zinc_mg | 锌 (mg) | | selenium_mg | 硒 (mg) | | copper_mg | 铜 (mg) | | manganese_mg | 锰 (mg) | ### 维生素 | 字段 | 说明 | | :--- | :--- | | vitamin_a_mcg | 维生素A (μg) | | vitamin_c_mg | 维生素C (mg) | | vitamin_d_mcg | 维生素D (μg) | | vitamin_e_mg | 维生素E (mg) | | vitamin_k_mcg | 维生素K (μg) | | vitamin_b1_mg | 维生素B1 硫胺素 (mg) | | vitamin_b2_mg | 维生素B2 核黄素 (mg) | | vitamin_b3_mg | 维生素B3 烟酸 (mg) | | vitamin_b5_mg | 维生素B5 泛酸 (mg) | | vitamin_b6_mg | 维生素B6 (mg) | | vitamin_b7_mcg | 维生素B7 生物素 (μg) | | vitamin_b9_mcg | 维生素B9 叶酸 (μg) | | vitamin_b12_mcg | 维生素B12 (μg) | ## 工作流程 ### 流程一：记录饮食 & 全营养分析 **触发**：用户输入饮食内容并请求记录或分析。 1. **解析食物列表**：从用户输入中提取所有食物名称和估计份量。 2. **检查数据文件**：确认 `references/food_data.json` 已存在，若缺失提示用户下载。 3. **匹配食物数据**：按三级匹配策略查询每种食物。 4. **计算份量营养**：将食物营养数据按用户估计的份量进行比例换算。 5. **汇总全部营养素**：分别求和各宏量、微量营养素的总和。 6. **输出完整分析报告**（格式见下方）。 7. **保存记录**：追加写入 `references/meal_log.json`。 ### 流程二：阶段性营养统计 **触发**：用户请求"最近N天"、"上周"、"本月"等时间段的统计。 1. **确定日期范围**：根据当前日期和用户指定周期。 2. **读取记录**：从 `references/meal_log.json` 筛选日期范围内所有记录。 3. **计算周期总计+日均**：对全部营养字段进行汇总。 4. **与参考标准对比**：给出偏离度评价。 5. **输出统计报告**。 --- ## 输出格式模板 ### 单餐全营养分析 ``` 🍽️ 【{meal} · {date}】 食物列表： • {food_name}（{type}，{weight}）…… 能量 {energy}kcal 蛋白 {protein}g | 脂肪 {fat}g | 碳水 {carbs}g | 纤维 {fiber}g 维A {vitA}μg | 维C {vitC}mg | 铁 {iron}mg | 钙 {calcium}mg | ... • ... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 合计摄入（当日 {meal}）： ⚡ 能量：{total_energy} kcal 💪 蛋白质：{total_protein} g 🧈 脂肪：{total_fat} g 🍞 碳水化合物：{total_carbs} g 🌾 膳食纤维：{total_fiber} g 🧪 矿物质： 钙 {calcium}mg | 铁 {iron}mg | 锌 {zinc}mg | 钾 {potassium}mg | 钠 {sodium}mg 💊 维生素： 维A {vitA}μg | 维C {vitC}mg | 维E {vitE}mg | 维B1 {vitB1}mg | 维B2 {vitB2}mg 💡 营养评估与建议：{基于各营养素平衡的专业建议} ``` ### 阶段性统计报告（按营养类别分组） ``` 📊 【{period} 全营养统计 · {start_date}~{end_date}】 总天数：{days} 天 ━━━ 🔥 宏量营养素 ━━━ ⚡ 能量：{total_energy} kcal（日均 {avg_energy} kcal） 💪 蛋白质：{total_protein} g（日均 {avg_protein} g） 🧈 脂肪：{total_fat} g（日均 {avg_fat} g） 🍞 碳水：{total_carbs} g（日均 {avg_carbs} g） ━━━ 🧪 关键矿物质 ━━━ 钙 {calcium} mg | 铁 {iron} mg | 锌 {zinc} mg ━━━ 💊 关键维生素 ━━━ 维A {vitA} μg | 维C {vitC} mg | 维E {vitE} mg ━━━ 📅 每日能量明细 ━━━ Day 1（{date}）：{energy} kcal ... 💡 综合评估：{对比参考标准的营养摄入评价与建议} ``` --- ## 参考标准（成年人日常基础需求） | 营养素 | 日推荐量 | 备注 | | :--- | :--- | :--- | | 能量 | 1800~2400 kcal | 取决于性别、活动量 | | 蛋白质 | 50~80 g | 占总热量 15~20% | | 脂肪 | 40~70 g | 占总热量 20~30% | | 碳水 | 200~300 g | 占总热量 50~65% | | 钙 | 800~1000 mg | | | 铁 | 12~20 mg | 女性高于男性 | | 锌 | 12~15 mg | | | 维生素A | 700~800 μg | | | 维生素C | 80~100 mg | | | 维生素E | 14 mg | | ## 记录文件格式 `references/meal_log.json` 每条记录格式： ```json { "date": "2026-04-14", "meal": "breakfast", "foods": [ { "name": "面条（富强粉）", "energy_kcal": 283, "protein_g": 8.5, "fat_g": 1.6, "carbs_g": 59.5, "fiber_g": 0.5, "calcium_mg": 13, "iron_mg": 2.6, "zinc_mg": 1.07, "vitamin_a_mcg": 0, "vitamin_c_mg": 0, "vitamin_e_mg": 0.47, "vitamin_b1_mg": 0.35, "vitamin_b2_mg": 0.1, ... } ], "total": { "energy_kcal": 678, "protein_g": 51.6, "fat_g": 24.2, "carbs_g": 62.4, "fiber_g": 3.1, "calcium_mg": 95, "iron_mg": 5.8, ... }, "note": "鸡腿为参考估算值" } ``` ## 注意事项 - **首次使用必须下载** `food_data.json`（太大无法嵌入到 ClawHub 包中） - 食物数据为 **2020年快照**，来自 foodwake.com，可能与中国最新食物成分表有差异 - 营养计算为**估算值**，实际摄入因烹饪方式、食材产地等因素可能有偏差 - 对于无匹配食物，**必须向用户确认同类替代方案**，不能擅自使用不相关数据 - 数据授权为 Apache-2.0，可商用但需标注来源

path: README.md
content: # 🍽️ diet-log — OpenClaw 饮食记录与营养分析 Skill

> 接入真实食物营养数据库的 OpenClaw 智能饮食助手。记录每餐饮食、自动分析 30+ 种营养素、支持阶段性统计报告。

[English](README_EN.md) | 简体中文

---

## ✨ 功能特性

- **🔍 真实营养数据**：内置 1643 条食物营养成分数据，覆盖谷/肉/蛋/菜/果/豆等 16 大类食物
- **📊 全营养分析**：热量、蛋白质、脂肪、碳水化合物、膳食纤维、7 种脂肪酸、10 种矿物质、13 种维生素
- **🧠 智能匹配**：精确匹配 → 同类参考 → 主动提问，三级策略应对数据库无法精确识别的情况
- **📅 阶段统计**：支持按日/周/月统计，输出每日明细 + 周期均值 + 与参考标准对比
- **💾 本地存储**：所有记录保存在本地 JSON 文件，隐私安全、无需联网

---

## 📦 安装

### 方式一：ClawHub 安装（推荐）

```bash
clawhub install diet-log
```

### 方式二：手动安装

1. 下载或克隆本仓库
2. 将 `diet-log` 目录放入你的 OpenClaw workspace skills 目录：
   ```
   ~/.openclaw/workspace/skills/diet-log/
   ```
3. 重启 OpenClaw

---

## 🚀 使用方法

安装后在 OpenClaw 中直接对话即可：

### 记录饮食 & 营养分析

```
我今天早餐吃了：一碗面条、一个鸡蛋、一份青菜
```

OpenClaw 会自动：
1. 识别食物列表并查询营养数据
2. 计算本餐全部营养素摄入
3. 输出完整营养报告
4. 自动保存到本地记录

### 阶段性统计

```
统计最近7天的饮食情况
```

输出最近 7 天每日明细、周期总摄入、日均摄入，并与参考标准对比。

### 补充说明食物信息

如果数据库无法精确匹配某食材，可以补充说明：

```
那杯饮品是鲜榨橙汁，大约300ml
```

---

## 📂 数据文件

| 文件 | 说明 |
| :--- | :--- |
| `references/food_data.json` | 食物营养成分数据库（1643 条，来源：LuckyHookin/foodwake） |
| `references/meal_log.json` | 饮食记录存储（自动创建，首次使用后生成） |

---

## 🔬 营养分析维度

### 宏量营养素
能量(kcal)、蛋白质(g)、脂肪(g)、碳水化合物(g)、膳食纤维(g)

### 脂肪酸
饱和脂肪酸、单不饱和脂肪酸、多不饱和脂肪酸、反式脂肪酸

### 矿物质
钙、铁、锌、钾、钠、镁、磷、铜、锰、硒

### 维生素
维A、维C、维D、维E、维K、维B1、维B2、维B3、维B5、维B6、维B7、叶酸、维B12

---

## 📊 食物匹配策略

当数据库无法精确匹配某食物时，按以下策略处理：

1. **精确匹配**：查找名字/别名包含关键词的食物
2. **同类参考**：在同一类别（畜肉/禽肉/蔬菜/豆制品等）中寻找最接近的食物，并在记录中标注「参考值」
3. **主动提问**：无法判断时，向用户询问食物类别后再做替代

---

## 🗂️ 项目结构

```
diet-log/
├── SKILL.md                   # Skill 说明书（触发词、工作流、输出格式）
├── README.md                  # 本文件
└── references/
    ├── food_data.json         # 食物营养数据库
    └── meal_log.json          # 饮食记录存储
    └── scripts/
        ├── query_food.py      # 食物查询脚本
        └── record_meal.py     # 记录写入 & 统计分析脚本
```

---

## 📚 数据来源与许可

食物营养数据来自 [LuckyHookin/foodwake](https://github.com/LuckyHookin/foodwake)（[Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0) 协议）。

> ⚠️ 数据为 foodwake.com 的 2020 年快照，可能与中国最新食物成分表存在差异。营养计算为估算值，实际摄入因烹饪方式、食材产地等因素可能有偏差。本工具仅供一般营养参考，不作为医学诊断或治疗依据。

---

## 🤝 贡献

欢迎提交 Issue 或 Pull Request！如果你有更好的食物数据来源或功能建议，请随时联系我们。

---

## 📄 许可

本 Skill 代码（`SKILL.md`、`scripts/`）采用 [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0) 协议。

食物营养数据来源：[LuckyHookin/foodwake](https://github.com/LuckyHookin/foodwake)，遵循原项目 Apache-2.0 协议。


path: README_EN.md
content: # 🍽️ diet-log — OpenClaw Diet Logging & Nutrition Analysis Skill

> Real food nutrition data-powered OpenClaw assistant for logging meals and analyzing 30+ nutrients.

[English](README_EN.md) | [简体中文](README.md)

---

## ✨ Features

- **🔍 Real Nutrition Data**: Built-in database of 1,643 foods across 16 categories
- **📊 Full Nutrient Analysis**: Calories, protein, fat, carbs, fiber, 7 fatty acids, 10 minerals, 13 vitamins
- **🧠 Smart Matching**: Three-level strategy — exact match → similar-type reference → ask for clarification
- **📅 Periodic Statistics**: Daily / weekly / monthly reports with daily breakdowns and reference comparisons
- **💾 Local Storage**: All records saved locally in JSON, privacy-safe, no internet required

---

## 📦 Installation

### Via ClawHub (Recommended)

```bash
clawhub install diet-log
```

### Manual Installation

1. Download or clone this repo
2. Place the `diet-log` folder into your OpenClaw workspace skills directory:
   ```
   ~/.openclaw/workspace/skills/diet-log/
   ```
3. Restart OpenClaw

---

## 🚀 Quick Start

After installation, just chat with OpenClaw:

### Log a Meal

```
I had noodles, an egg, and some greens for breakfast
```

OpenClaw will automatically:
1. Identify food items and query nutrition data
2. Calculate full nutrient breakdown
3. Output a complete nutrition report
4. Save the record locally

### Periodic Statistics

```
Stats for the last 7 days
```

Returns daily breakdowns, period totals, daily averages, and comparison against reference standards.

---

## 📂 Data Files

| File | Description |
| :--- | :--- |
| `references/food_data.json` | Food nutrition database (1,643 items, source: LuckyHookin/foodwake) |
| `references/meal_log.json` | Meal log storage (auto-created on first use) |

---

## 📚 Data Source & License

Food nutrition data from [LuckyHookin/foodwake](https://github.com/LuckyHookin/foodwake) ([Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0)).

> ⚠️ Data is a 2020 snapshot from foodwake.com. Values may differ from the latest Chinese food composition tables. This tool is for general nutritional reference only, not for medical diagnosis.

---

## 🤝 Contributing

Issues and Pull Requests are welcome! If you have better food data sources or feature suggestions, feel free to reach out.

---

## 📄 License

Skill code (`SKILL.md`, `scripts/`): [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0).

Food nutrition data: [LuckyHookin/foodwake](https://github.com/LuckyHookin/foodwake), licensed under Apache-2.0.


path: scripts/query_food.py
content: #!/usr/bin/env python3
"""
饮食记录 Skill - 食物查询脚本 v2
增强版：支持多营养字段、分类匹配、维生素矿物质全量返回
用法: python query_food.py <食物关键词> [limit]
"""
import json
import sys
import os
import re

def parse_number(value_str):
    """从营养值字符串中提取数字（克/千卡/微克/毫克）"""
    if not value_str or value_str in ('-', '—', '', '克', '微克', '毫克'):
        return 0.0
    # 去掉单位
    cleaned = re.sub(r'[千微毫]?[克卡路里]|[a-zA-Z%（）()\s]', '', str(value_str))
    match = re.search(r'[\d.]+', cleaned)
    if match:
        try:
            return float(match.group())
        except:
            return 0.0
    return 0.0

def search_foods(query, food_data, limit=5):
    """
    在食物数据库中模糊搜索匹配的食物
    匹配策略（优先级递减）：
    1. 名字完全相等 > 名字以查询词开头 > 名字包含查询词
    2. 别名包含查询词
    3. 分类匹配（type 字段匹配查询词）
    4. 返回空列表（需人工介入提问）
    """
    query_lower = query.lower().strip()
    exact = []       # 优先级1：精确相等
    prefix = []      # 优先级2：名字以查询词开头
    contains = []    # 优先级3：名字包含查询词
    nickname_hit = [] # 优先级4：别名匹配
    seen_names = set()

    for item in food_data:
        name = item.get('name', '')
        nickname = item.get('nickname', '')
        if name in seen_names:
            continue

        name_lower = name.lower()
        nick_lower = nickname.lower()

        if name_lower == query_lower:
            exact.append(item)
            seen_names.add(name)
        elif name_lower.startswith(query_lower) or name_lower.startswith(query_lower.replace('类', '')):
            prefix.append(item)
            seen_names.add(name)
        elif query_lower in name_lower:
            contains.append(item)
            seen_names.add(name)
        elif query_lower in nick_lower:
            nickname_hit.append(item)
            seen_names.add(name)

    # 按优先级组装结果
    results = exact + prefix + contains + nickname_hit
    return results[:limit]

def format_food_info(item, include_all=True):
    """
    将单条食物记录格式化为完整的营养摘要
    include_all=True 时返回全部可用字段
    """
    info = item.get('info', {})

    def get_val(key):
        return info.get(key, '-')

    def get_num(key):
        return parse_number(info.get(key, '0'))

    # 宏量营养素（核心）
    macro = {
        'energy_kcal': get_num('能量'),
        'protein_g': get_num('蛋白质'),
        'fat_g': get_num('脂肪'),
        'carbs_g': get_num('碳水化合物'),
        'fiber_g': get_num('粗纤维'),
    }

    # 脂肪酸详情
    fatty_acids = {
        'saturated_fat_g': get_num('饱和脂肪酸'),
        'monounsaturated_fat_g': get_num('单不饱和脂肪酸'),
        'polyunsaturated_fat_g': get_num('多不饱和脂肪酸'),
        'trans_fat_g': get_num('反式脂肪酸'),
    }

    # 矿物质
    minerals = {
        'calcium_mg': get_num('钙'),
        'magnesium_mg': get_num('镁'),
        'sodium_mg': get_num('钠'),
        'potassium_mg': get_num('钾'),
        'phosphorus_mg': get_num('磷'),
        'iron_mg': get_num('铁'),
        'zinc_mg': get_num('锌'),
        'selenium_mg': get_num('硒'),
        'copper_mg': get_num('铜'),
        'manganese_mg': get_num('锰'),
    }

    # 维生素
    vitamins = {
        'vitamin_a_mcg': get_num('维生素A'),
        'vitamin_c_mg': get_num('维生素C'),
        'vitamin_d_mcg': get_num('维生素D'),
        'vitamin_e_mg': get_num('维生素E'),
        'vitamin_k_mcg': get_num('维生素K'),
        'vitamin_b1_mg': get_num('维生素B1（硫胺素）'),
        'vitamin_b2_mg': get_num('维生素B2（核黄素）'),
        'vitamin_b3_mg': get_num('维生素B3（烟酸）'),
        'vitamin_b5_mg': get_num('维生素B5（泛酸）'),
        'vitamin_b6_mg': get_num('维生素B6'),
        'vitamin_b7_mcg': get_num('维生素B7（生物素）'),
        'vitamin_b9_mcg': get_num('维生素B9（叶酸）'),
        'vitamin_b12_mcg': get_num('维生素B12'),
    }

    result = {
        'name': item.get('name'),
        'nickname': item.get('nickname'),
        'type': item.get('type'),
        **macro,
        **fatty_acids,
        **minerals,
        **vitamins,
        'raw_info': {
            'energy': get_val('能量'),
            'protein': get_val('蛋白质'),
            'fat': get_val('脂肪'),
            'carbs': get_val('碳水化合物'),
        }
    }

    return result

def main():
    if len(sys.argv) < 2:
        print(json.dumps({'error': '用法: python query_food.py <食物关键词> [limit]'}, ensure_ascii=False, indent=2))
        sys.exit(1)

    query = sys.argv[1]
    limit = int(sys.argv[2]) if len(sys.argv) >= 3 else 5

    script_dir = os.path.dirname(os.path.abspath(__file__))
    skill_dir = os.path.dirname(script_dir)
    data_path = os.path.join(skill_dir, 'references', 'food_data.json')

    if not os.path.exists(data_path):
        print(json.dumps({'error': f'数据文件不存在: {data_path}'}, ensure_ascii=False, indent=2))
        sys.exit(1)

    with open(data_path, 'r', encoding='utf-8') as f:
        food_data = json.load(f)

    results = search_foods(query, food_data, limit=limit)

    if not results:
        print(json.dumps({
            'query': query,
            'matches': 0,
            'results': [],
            'suggestion': '未找到匹配食物，请提供更多描述或说明食物类别'
        }, ensure_ascii=False, indent=2))
        return

    formatted = [format_food_info(item) for item in results]

    output = {
        'query': query,
        'matches': len(formatted),
        'results': formatted
    }

    print(json.dumps(output, ensure_ascii=False, indent=2))

if __name__ == '__main__':
    main()


path: scripts/record_meal.py
content: #!/usr/bin/env python3
"""
饮食记录 Skill - 记录饮食 & 统计脚本 v2
支持全部营养字段：宏量+脂肪酸+矿物质+维生素
用法:
  python record_meal.py --add <json> [--meal breakfast|lunch|dinner|snack] [--date YYYY-MM-DD]
  python record_meal.py --stats [--days N | --from YYYY-MM-DD --to YYYY-MM-DD]
"""
import json
import sys
import os
from datetime import datetime, timedelta

SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
SKILL_DIR = os.path.dirname(SCRIPT_DIR)
LOG_PATH = os.path.join(SKILL_DIR, 'references', 'meal_log.json')

def load_log():
    if os.path.exists(LOG_PATH):
        with open(LOG_PATH, 'r', encoding='utf-8') as f:
            return json.load(f)
    return []

def save_log(log):
    with open(LOG_PATH, 'w', encoding='utf-8') as f:
        json.dump(log, f, ensure_ascii=False, indent=2)

def add_meal(foods_data, meal_type='lunch', date_str=None, note=''):
    """添加一餐饮食记录（全部营养字段）"""
    log = load_log()

    if date_str is None:
        date_str = datetime.now().strftime('%Y-%m-%d')

    # 定义所有营养字段
    nutrient_keys = [
        # 宏量
        'energy_kcal', 'protein_g', 'fat_g', 'carbs_g', 'fiber_g',
        # 脂肪酸
        'saturated_fat_g', 'monounsaturated_fat_g', 'polyunsaturated_fat_g', 'trans_fat_g',
        # 矿物质
        'calcium_mg', 'magnesium_mg', 'sodium_mg', 'potassium_mg', 'phosphorus_mg',
        'iron_mg', 'zinc_mg', 'selenium_mg', 'copper_mg', 'manganese_mg',
        # 维生素
        'vitamin_a_mcg', 'vitamin_c_mg', 'vitamin_d_mcg', 'vitamin_e_mg', 'vitamin_k_mcg',
        'vitamin_b1_mg', 'vitamin_b2_mg', 'vitamin_b3_mg', 'vitamin_b5_mg',
        'vitamin_b6_mg', 'vitamin_b7_mcg', 'vitamin_b9_mcg', 'vitamin_b12_mcg',
    ]

    # 初始化总计
    total = {k: 0.0 for k in nutrient_keys}

    for food in foods_data:
        for key in nutrient_keys:
            total[key] += food.get(key, 0.0)

    # 四舍五入
    total = {k: round(v, 2) for k, v in total.items()}

    entry = {
        'date': date_str,
        'meal': meal_type,
        'foods': foods_data,
        'total': total,
        'note': note
    }

    log.append(entry)
    save_log(log)

    return entry

def get_stats(days=7, from_date=None, to_date=None):
    """获取阶段性营养统计"""
    log = load_log()

    if from_date and to_date:
        start = datetime.strptime(from_date, '%Y-%m-%d')
        end = datetime.strptime(to_date, '%Y-%m-%d')
    else:
        end = datetime.now()
        start = end - timedelta(days=days - 1)

    # 筛选日期范围内的记录
    filtered = []
    for entry in log:
        try:
            d = datetime.strptime(entry['date'], '%Y-%m-%d')
            if start <= d <= end:
                filtered.append(entry)
        except:
            continue

    nutrient_keys = [
        'energy_kcal', 'protein_g', 'fat_g', 'carbs_g', 'fiber_g',
        'saturated_fat_g', 'monounsaturated_fat_g', 'polyunsaturated_fat_g', 'trans_fat_g',
        'calcium_mg', 'magnesium_mg', 'sodium_mg', 'potassium_mg', 'phosphorus_mg',
        'iron_mg', 'zinc_mg', 'selenium_mg',
        'vitamin_a_mcg', 'vitamin_c_mg', 'vitamin_e_mg',
        'vitamin_b1_mg', 'vitamin_b2_mg', 'vitamin_b3_mg', 'vitamin_b9_mcg', 'vitamin_b12_mcg',
    ]

    if not filtered:
        return {
            'period': f'{start.strftime("%Y-%m-%d")} ~ {end.strftime("%Y-%m-%d")}',
            'days': 0,
            'total': {k: 0 for k in nutrient_keys},
            'daily_avg': {k: 0 for k in nutrient_keys},
            'daily': []
        }

    # 按日汇总
    daily_data = {}
    for entry in filtered:
        d = entry['date']
        if d not in daily_data:
            daily_data[d] = {k: 0.0 for k in nutrient_keys}
        t = entry['total']
        for k in nutrient_keys:
            daily_data[d][k] += t.get(k, 0)

    num_days = len(daily_data)

    # 总计
    total_all = {k: round(sum(v[k] for v in daily_data.values()), 2) for k in nutrient_keys}
    daily_avg = {k: round(total_all[k] / num_days, 2) for k in nutrient_keys}

    return {
        'period': f'{start.strftime("%Y-%m-%d")} ~ {end.strftime("%Y-%m-%d")}',
        'days': num_days,
        'total': total_all,
        'daily_avg': daily_avg,
        'daily': [{'date': d, ** {k: round(v, 2) for k, v in row.items()}} for d, row in sorted(daily_data.items())]
    }

def main():
    if len(sys.argv) < 2:
        print('用法:')
        print('  添加: python record_meal.py --add <json> [--meal lunch] [--date 2026-04-14]')
        print('  统计: python record_meal.py --stats [--days 7]')
        sys.exit(1)

    args = sys.argv[1:]
    action = args[0]

    if action == '--add':
        meal_type = 'lunch'
        date_str = None
        note = ''
        foods_json = None

        i = 1
        while i < len(args):
            if args[i] == '--meal' and i + 1 < len(args):
                meal_type = args[i + 1]; i += 2
            elif args[i] == '--date' and i + 1 < len(args):
                date_str = args[i + 1]; i += 2
            elif args[i] == '--note' and i + 1 < len(args):
                note = args[i + 1]; i += 2
            elif not args[i].startswith('--'):
                foods_json = args[i]; i += 1
            else:
                i += 1

        if not foods_json:
            print(json.dumps({'error': '缺少 foods JSON'}, ensure_ascii=False))
            sys.exit(1)

        try:
            foods_data = json.loads(foods_json)
        except:
            print(json.dumps({'error': f'无法解析 JSON'}, ensure_ascii=False))
            sys.exit(1)

        entry = add_meal(foods_data, meal_type, date_str, note)
        print(json.dumps({'status': 'ok', 'entry': entry}, ensure_ascii=False, indent=2))

    elif action == '--stats':
        days = 7
        from_date = None
        to_date = None

        i = 1
        while i < len(args):
            if args[i] == '--days' and i + 1 < len(args):
                days = int(args[i + 1]); i += 2
            elif args[i] == '--from' and i + 1 < len(args):
                from_date = args[i + 1]; i += 2
            elif args[i] == '--to' and i + 1 < len(args):
                to_date = args[i + 1]; i += 2
            else:
                i += 1

        stats = get_stats(days=days, from_date=from_date, to_date=to_date)
        print(json.dumps(stats, ensure_ascii=False, indent=2))

    else:
        print(json.dumps({'error': f'未知操作: {action}'}, ensure_ascii=False))

if __name__ == '__main__':
    main()


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (MEDIUM)