# 食谱计算器后端接口开发文档 > **版本**:v1.0 > **创建日期**:2026-02-01 > **所属模块**:Tool(工具模块) > **配套前端**:`msh_single_uniapp/pages/tool/calculator.vue` / `calculator-result.vue` --- ## 一、功能概述 食谱计算器是**慢生活智能营养专家**的核心功能之一,面向肾病(CKD)及透析患者,根据用户输入的健康数据,自动计算: 1. **健康指标**:eGFR、标准体重、BMI、CKD分期 2. **每日营养目标**:蛋白质摄入量(g/d)、能量摄入量(kcal/d) 3. **食物份数建议**:7 类食物的每日推荐份数 4. **一日三餐配餐方案**:早餐、午餐、晚餐的菜品清单(含图片、食材用量) 5. **重要提示**:根据患者情况给出饮食注意事项 用户可进一步**采纳营养计划**,进入打卡流程(另单独接口)。 --- ## 二、接口清单 | 序号 | 接口名称 | HTTP方法 | 路径 | 说明 | |------|----------|----------|------|------| | 1 | 计算营养方案 | `POST` | `/api/front/tool/calculator/calculate` | 核心接口,输入健康数据,返回计算结果 | | 2 | 获取计算结果详情 | `GET` | `/api/front/tool/calculator/result/{id}` | 通过结果ID获取完整结果(含配餐) | | 3 | 采纳营养计划 | `POST` | `/api/front/tool/calculator/adopt` | 用户采纳后创建营养计划,进入打卡流程 | --- ## 三、接口详细设计 ### 3.1 计算营养方案 #### 基础信息 | 项目 | 值 | |------|------| | 请求路径 | `POST /api/front/tool/calculator/calculate` | | 是否鉴权 | **是**(需登录) | | Content-Type | `application/json` | #### 请求参数(Request Body) | 参数名 | 类型 | 必填 | 说明 | 校验规则 | |--------|------|------|------|----------| | `gender` | String | 是 | 性别 | 可选值:`male` / `female` | | `age` | Integer | 是 | 年龄(岁) | 1 ≤ age ≤ 150 | | `height` | Integer | 是 | 身高(cm) | 50 ≤ height ≤ 250 | | `dialysis` | Boolean | 是 | 是否透析 | `true` / `false` | | `dialysisType` | String | 否 | 透析类型 | 当 `dialysis=true` 时有效;可选值:`hemodialysis`(血透)/ `peritoneal`(腹透) | | `dryWeight` | Number | 是 | 干体重(kg) | 20 ≤ dryWeight ≤ 300,支持1位小数 | | `creatinine` | Number | 是 | 血肌酐(μmol/L) | 0 < creatinine ≤ 2000,支持2位小数 | **请求示例** ```json { "gender": "male", "age": 55, "height": 170, "dialysis": true, "dialysisType": "hemodialysis", "dryWeight": 65.5, "creatinine": 850 } ``` #### 响应参数(Response Body) | 参数名 | 类型 | 说明 | |--------|------|------| | `code` | Integer | 状态码,`200` 表示成功 | | `message` | String | 提示信息 | | `data` | Object | 计算结果对象 | **`data` 结构** | 字段名 | 类型 | 说明 | |--------|------|------| | `id` / `resultId` | Long | 计算结果唯一ID,用于后续查询/采纳 | | `healthData` | Object | 健康数据计算结果 | | `nutritionGoals` | Object | 每日营养目标 | | `foodList` | Array | 食物份数建议列表 | | `mealPlan` | Object | 一日三餐配餐方案 | | `importantTips` | Array\ | 重要提示文案列表 | | `createdAt` | String | 创建时间 ISO8601 | **`healthData` 结构** | 字段名 | 类型 | 说明 | 计算公式 | |--------|------|------|----------| | `eGFR` | String | 肾小球滤过率(ml/min/1.73m²) | CKD-EPI 公式(见附录 A) | | `standardWeight` | String | 标准体重(kg) | 男:`(height - 80) × 0.7`;女:`(height - 70) × 0.6` | | `bmi` | String | 体重指数 | `dryWeight / (height/100)²` | | `bmiStatus` | String | BMI 状态描述 | 见附录 B | | `ckdStage` | String | CKD 分期 | 见附录 C | **`nutritionGoals` 结构** | 字段名 | 类型 | 说明 | 计算公式 | |--------|------|------|----------| | `protein` | String | 每日蛋白质目标(克) | 透析期:`standardWeight × 1.2`;非透析期:`standardWeight × 0.8` | | `energy` | String | 每日能量目标(千卡) | `standardWeight × 35` | **`foodList` 结构**(数组,7 项) | 字段名 | 类型 | 说明 | |--------|------|------| | `number` | Integer | 序号 1-7 | | `name` | String | 食物类别名称(如"谷薯50g") | | `portion` | String | 推荐份数 | **`mealPlan` 结构** | 字段名 | 类型 | 说明 | |--------|------|------| | `breakfast` | Array | 早餐菜品列表 | | `lunch` | Array | 午餐菜品列表 | | `dinner` | Array | 晚餐菜品列表 | **菜品对象结构** | 字段名 | 类型 | 说明 | |--------|------|------| | `name` | String | 菜品名称 | | `image` | String | 菜品图片 URL | | `ingredients` | Array\ | 食材列表(如 `["牛奶 120g", "面条 90g"]`) | **响应示例** ```json { "code": 200, "message": "success", "data": { "id": 100234, "healthData": { "eGFR": "7.9", "standardWeight": "63.0", "bmi": "22.7", "bmiStatus": "正常", "ckdStage": "CKD 5期" }, "nutritionGoals": { "protein": "75.6", "energy": "2205" }, "foodList": [ { "number": 1, "name": "谷薯50g", "portion": "5.7" }, { "number": 2, "name": "淀粉100g", "portion": "0.77" }, { "number": 3, "name": "绿叶蔬菜200g", "portion": "1" }, { "number": 4, "name": "瓜果蔬菜200g", "portion": "2" }, { "number": 5, "name": "奶类230g", "portion": "1" }, { "number": 6, "name": "肉蛋类50/60g", "portion": "7" }, { "number": 7, "name": "油脂类10g", "portion": "5.7" } ], "mealPlan": { "breakfast": [ { "name": "牛奶", "image": "https://cdn.xxx.com/images/milk.jpg", "ingredients": ["牛奶 120g"] }, { "name": "鸡蛋拌面", "image": "https://cdn.xxx.com/images/egg-noodle.jpg", "ingredients": ["面条 90g", "鸡蛋 120g", "葱花 5g"] }, { "name": "凉拌黄瓜", "image": "https://cdn.xxx.com/images/cucumber.jpg", "ingredients": ["黄瓜 100g"] } ], "lunch": [ { "name": "米饭", "image": "https://cdn.xxx.com/images/rice.jpg", "ingredients": ["大米 100g"] }, { "name": "清蒸鲈鱼", "image": "https://cdn.xxx.com/images/bass.jpg", "ingredients": ["鲈鱼 120g", "生姜 5g", "葱 5g"] }, { "name": "蒜蓉西兰花", "image": "https://cdn.xxx.com/images/broccoli.jpg", "ingredients": ["西兰花 150g", "大蒜 5g", "植物油 8g"] }, { "name": "冬瓜汤", "image": "https://cdn.xxx.com/images/wax-gourd-soup.jpg", "ingredients": ["冬瓜 150g"] } ], "dinner": [ { "name": "杂粮饭", "image": "https://cdn.xxx.com/images/mixed-rice.jpg", "ingredients": ["大米 70g", "小米 30g"] }, { "name": "香菇炒鸡丁", "image": "https://cdn.xxx.com/images/chicken-mushroom.jpg", "ingredients": ["鸡胸肉 100g", "香菇 50g", "植物油 8g"] }, { "name": "清炒油菜", "image": "https://cdn.xxx.com/images/bok-choy.jpg", "ingredients": ["油菜 150g", "植物油 5g"] }, { "name": "番茄蛋花汤", "image": "https://cdn.xxx.com/images/tomato-egg-soup.jpg", "ingredients": ["番茄 100g", "鸡蛋 60g"] } ] }, "importantTips": [ "以上配餐由 AI 生成,仅适用于无其他并发症的单纯尿毒症人群", "透析患者需严格控制水分摄入", "建议低盐饮食(每日少于 5g)", "注意限制高钾食物(香蕉、橙子、土豆等)", "限制高磷食物(坚果、动物内脏等)", "特别提醒:尿毒症合并其他并发症人群不适用此配餐,请务必咨询专业营养师调整方案" ], "createdAt": "2026-02-01T10:30:00+08:00" } } ``` #### 错误码 | code | message | 说明 | |------|---------|------| | 400 | 参数校验失败 | 请求参数不符合校验规则 | | 401 / 410000-410002 | 未登录/登录过期 | 需重新登录 | | 500 | 系统异常 | 服务端内部错误 | --- ### 3.2 获取计算结果详情 #### 基础信息 | 项目 | 值 | |------|------| | 请求路径 | `GET /api/front/tool/calculator/result/{id}` | | 是否鉴权 | **是** | | 路径参数 | `id`:计算结果ID(Long) | #### 响应 与 3.1 的 `data` 结构相同,用于结果页刷新或分享后重新加载。 --- ### 3.3 采纳营养计划 #### 基础信息 | 项目 | 值 | |------|------| | 请求路径 | `POST /api/front/tool/calculator/adopt` | | 是否鉴权 | **是** | | Content-Type | `application/json` | #### 请求参数 | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | `resultId` | Long | 是 | 计算结果ID | **请求示例** ```json { "resultId": 100234 } ``` #### 响应 | 字段名 | 类型 | 说明 | |--------|------|------| | `code` | Integer | 200 成功 | | `message` | String | 提示信息 | | `data.planId` | Long | 新创建的营养计划ID | | `data.startDate` | String | 计划开始日期 | | `data.endDate` | String | 计划结束日期(默认 +7 天) | **响应示例** ```json { "code": 200, "message": "采纳成功", "data": { "planId": 56789, "startDate": "2026-02-01", "endDate": "2026-02-07" } } ``` #### 业务规则 1. 同一用户同一时间只能有 **1 个激活状态(`status=active`)** 的营养计划;若已存在则自动将旧计划标记为 `abandoned`。 2. 采纳成功后赠送 **+20 积分**(积分任务:采纳营养方案)。 --- ## 四、数据库表设计 ### 4.1 计算结果表(calculator_results) > 存储每次计算的输入与输出,方便查询历史、分析 ```sql CREATE TABLE calculator_results ( result_id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键', user_id BIGINT NOT NULL COMMENT '用户ID', -- 输入参数 gender VARCHAR(10) NOT NULL COMMENT '性别:male/female', age INT NOT NULL COMMENT '年龄(岁)', height INT NOT NULL COMMENT '身高(cm)', has_dialysis TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否透析:0-否 1-是', dialysis_type VARCHAR(20) COMMENT '透析类型:hemodialysis/peritoneal', dry_weight DECIMAL(5,2) NOT NULL COMMENT '干体重(kg)', creatinine DECIMAL(8,2) NOT NULL COMMENT '血肌酐(μmol/L)', -- 计算结果 egfr DECIMAL(6,2) NOT NULL COMMENT 'eGFR(ml/min/1.73m²)', standard_weight DECIMAL(5,2) NOT NULL COMMENT '标准体重(kg)', bmi DECIMAL(4,1) NOT NULL COMMENT 'BMI', bmi_status VARCHAR(20) NOT NULL COMMENT 'BMI状态描述', ckd_stage VARCHAR(50) NOT NULL COMMENT 'CKD分期', protein_intake DECIMAL(5,1) NOT NULL COMMENT '每日蛋白质目标(g)', energy_intake INT NOT NULL COMMENT '每日能量目标(kcal)', -- 配餐方案(JSON) food_list_json TEXT COMMENT '食物份数建议 JSON', meal_plan_json TEXT COMMENT '配餐方案 JSON', tips_json TEXT COMMENT '重要提示 JSON', created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, INDEX idx_user_id (user_id), INDEX idx_created_at (created_at) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='营养计算结果表'; ``` ### 4.2 营养计划表(nutrition_plans) > PRD 中已定义,补充关联 ```sql -- 见 PRD 4.6.5 节 -- 关键字段: -- plan_id, user_id, gender, age, height, weight, has_dialysis, dialysis_type, creatinine, -- egfr, standard_weight, bmi, ckd_stage, protein_intake, energy_intake, -- meal_plan_json, status, start_date, end_date, created_at, updated_at ``` 关联关系:`nutrition_plans.source_result_id → calculator_results.id`(可选字段,用于溯源) --- ## 五、核心算法 ### 5.1 eGFR 计算(CKD-EPI 2021 公式) ```java /** * CKD-EPI 2021 公式(无种族系数) * @param creatinine 血肌酐 (μmol/L) * @param age 年龄 (岁) * @param isFemale 是否女性 * @return eGFR (ml/min/1.73m²) */ public static double calculateEGFR(double creatinine, int age, boolean isFemale) { // 将 μmol/L 转换为 mg/dL double scr = creatinine / 88.4; double kappa = isFemale ? 0.7 : 0.9; double alpha = isFemale ? -0.241 : -0.302; double multi = isFemale ? 1.012 : 1.0; double minRatio = Math.min(scr / kappa, 1.0); double maxRatio = Math.max(scr / kappa, 1.0); double eGFR = 142 * Math.pow(minRatio, alpha) * Math.pow(maxRatio, -1.200) * Math.pow(0.9938, age) * multi; return Math.round(eGFR * 100.0) / 100.0; // 保留2位小数 } ``` ### 5.2 标准体重 ```java public static double calculateStandardWeight(int height, boolean isFemale) { return isFemale ? (height - 70) * 0.6 : (height - 80) * 0.7; } ``` ### 5.3 BMI ```java public static double calculateBMI(double weight, int height) { double heightM = height / 100.0; return Math.round(weight / (heightM * heightM) * 10.0) / 10.0; } ``` ### 5.4 BMI 状态 | BMI 范围 | 状态描述 | |----------|----------| | < 18.5 | 体型过轻 | | 18.5 - 23.9 | 正常 | | 24.0 - 27.9 | 超重 | | ≥ 28.0 | 肥胖 | ### 5.5 CKD 分期 | eGFR 范围 | 分期 | |-----------|------| | ≥ 90 | CKD 1期 | | 60 - 89 | CKD 2期 | | 45 - 59 | CKD 3a期 | | 30 - 44 | CKD 3b期 | | 15 - 29 | CKD 4期 | | < 15(非透析) | CKD 5期 | | 透析患者 | 透析期 | ### 5.6 营养目标 ```java public static double calculateProtein(double standardWeight, boolean hasDialysis) { return hasDialysis ? standardWeight * 1.2 // 透析期 : standardWeight * 0.8; // 非透析期 } public static int calculateEnergy(double standardWeight) { return (int) Math.round(standardWeight * 35); } ``` ### 5.7 食物份数 基于能量和蛋白质目标,按照《慢性肾脏病患者膳食指导(2017)》推荐比例分配,具体算法可参考专业营养软件或配置表。 ### 5.8 配餐方案生成 1. **规则匹配**:基于 CKD 分期、透析类型、营养目标,从预置的 **配餐模板库(recipes)** 中匹配合适方案。 2. **AI 生成(可选)**:调用 AI 模型根据营养目标动态生成菜品及食材用量。 3. **输出**:早/午/晚三餐各 2-4 道菜,包含图片 URL 和食材列表。 --- ## 六、性能与安全 ### 6.1 缓存策略 | 数据 | 缓存方式 | 过期时间 | 说明 | |------|----------|----------|------| | 计算结果 | Redis Hash | 24 小时 | key = `calc_result:{id}` | | 配餐模板库 | 本地缓存 / Redis | 1 小时 | 减少 DB 查询 | ### 6.2 限流 | 接口 | QPS 限制 | 说明 | |------|----------|------| | calculate | 10/用户/分钟 | 防止恶意刷算 | | result/{id} | 60/用户/分钟 | 允许刷新 | | adopt | 5/用户/分钟 | 防重复提交 | ### 6.3 幂等性 - **calculate**:每次计算生成新记录,允许重复调用。 - **adopt**:采纳接口需幂等设计,同一 `resultId` 多次调用只创建一个计划;可用 `user_id + result_id` 唯一索引或分布式锁。 ### 6.4 数据安全 - 所有接口均需登录鉴权(Header: `Authori-zation`)。 - 用户只能查询/采纳自己的计算结果(`user_id` 校验)。 - 敏感健康数据传输全程 HTTPS。 --- ## 七、测试用例 ### 7.1 正常场景 | 用例编号 | 描述 | 输入 | 预期输出 | |----------|------|------|----------| | TC01 | 男性透析患者计算 | gender=male, age=55, height=170, dialysis=true, dryWeight=65.5, creatinine=850 | 返回 code=200,healthData/nutritionGoals/mealPlan 完整 | | TC02 | 女性非透析患者 | gender=female, age=48, height=160, dialysis=false, dryWeight=52, creatinine=180 | 返回 code=200,ckdStage 按 eGFR 判断 | | TC03 | 查询结果详情 | GET /result/100234 | 返回与 calculate 相同结构 | | TC04 | 采纳营养计划 | resultId=100234 | 返回 planId,计划 status=active | ### 7.2 异常场景 | 用例编号 | 描述 | 输入 | 预期输出 | |----------|------|------|----------| | TC11 | 年龄超出范围 | age=200 | code=400, message 包含"年龄" | | TC12 | 未登录调用 | 无 Token | code=401/410000 | | TC13 | 查询他人结果 | id 属于其他用户 | code=403 | | TC14 | 重复采纳 | 同一 resultId 调用两次 | 第二次返回已存在的 planId(幂等) | --- ## 八、附录 ### 附录 A:CKD-EPI 2021 公式 > 无种族系数版本,适用于中国人群 ``` eGFR = 142 × min(Scr/κ, 1)^α × max(Scr/κ, 1)^(-1.200) × 0.9938^Age × (1.012 if female) 其中: - Scr = 血肌酐 (mg/dL) = μmol/L ÷ 88.4 - κ = 0.7 (女) / 0.9 (男) - α = -0.241 (女) / -0.302 (男) ``` ### 附录 B:BMI 分类标准(中国) | BMI | 分类 | |-----|------| | < 18.5 | 体型过轻 | | 18.5 - 23.9 | 正常 | | 24.0 - 27.9 | 超重 | | ≥ 28.0 | 肥胖 | ### 附录 C:CKD 分期标准 | 分期 | eGFR (ml/min/1.73m²) | 描述 | |------|----------------------|------| | 1期 | ≥ 90 | 肾功能正常或升高 | | 2期 | 60-89 | 轻度下降 | | 3a期 | 45-59 | 轻-中度下降 | | 3b期 | 30-44 | 中-重度下降 | | 4期 | 15-29 | 重度下降 | | 5期 | < 15 | 肾衰竭 | | 透析期 | — | 已接受透析治疗 | --- ## 九、变更记录 | 版本 | 日期 | 作者 | 变更内容 | |------|------|------|----------| | v1.0 | 2026-02-01 | — | 初稿 | --- *文档结束*