Skip to content

About

No description, website, or topics provided.

Resources

Stars

105 stars

Watchers

0 watching

Forks

Repository files navigation

麦麦极致省钱规划师(MCD Saver Planner)

基于麦当劳 MCP 的全渠道点餐优惠决策引擎——用一句话,算出全网最省的点餐方案。

Node MCP License Tests


📖 一句话介绍

你在 WorkBuddy / 任意 MCP 客户端里说「深圳宝安,巨无霸套餐,到店,怎么最省」, 它会自动遍历全渠道、全量优惠券组合,输出 Top3 最低到手价方案,一句话确认即可下单。


🎯 项目背景与核心价值

用户在麦当劳点餐时面临的真实痛点

打开麦当劳 App 想点一份巨无霸,你会遇到:

  • 菜单几十项,套餐与单品混杂,「巨无霸三件套 41 元」和「巨无霸 26.5 元 + 薯条」哪个更划算?
  • 优惠券分散在三个体系:门店券、麦麦省券(需先领取)、积分券,App 里往往只显示「部分可用」
  • 不知道能不能叠加:门店券和麦麦省券同时用会怎样?买张随单购卡到底亏不亏?
  • 三渠道价格不明:到店自取、得来速车道取餐、麦乐送到家,哪个更划算?

结果就是:用户要么随手点一笔(浪费权益),要么在多个页面反复比对(决策成本高)。

本项目的解法

把上述决策过程封装成一个优惠决策引擎:自然语言输入 → 意图与参数解析 → 全渠道全量券组合遍历 → 服务端真实价格验证 → Top3 方案输出 → 一键下单。

关键设计取舍:不本地估算优惠金额,而是把 calculate-price 作为唯一事实来源。 即使官方叠加规则变化,引擎依然给出正确结果,只是多几次调用——用少量调用换取 100% 的准确性。


✨ 功能亮点

# 能力 说明
1 🧠 自然语言意图解析 识别 6 类意图(指定餐品/预算推荐/活动查询/订单复购/积分兑换/通用),自动抽取餐品、预算、渠道、人数、位置,缺参主动追问
2 🧮 全量券组合遍历 跨类型券叠加规则引擎,遵守「同类型互斥、跨类型可叠加、活动券独立」规则,输出全局最优解
3 📊 三渠道并行比价 到店自取 / 得来速 / 麦乐送并行计算,按 价格 60% + 时间 25% + 距离 15% 加权评分
4 🛒 智能凑单建议 未达满减门槛时推荐最低价凑单品,并用收益校验确认凑单真的更便宜才推荐
5 ⭐ 积分价值比算法 按「券面值 ÷ 所需积分」排序而非所需积分升序,优先消耗临期积分
6 📅 活动预判决策 解析活动文案估算折扣力度,对比当前最优价给出「现在买 / 等活动买」建议
7 🔁 订单复购 读取历史订单,按今天的优惠重新计算最省方案
8 ⚡ 一键领券下单 自动领券 → 价格二次校验 → 创建订单 → 返回支付链接
9 🛡️ 稳定性保障 并行调用、TTL 缓存、指数退避、优雅降级、状态机约束,单轮调用 ≤ 15 次

🚀 快速开始

方式一:离线演示(无需 MCP Token,零配置)

项目内置了从麦当劳 MCP Server 真实响应采样的 Mock 数据,可直接跑通全链路:

node src/index.js --demo

输出示例:

👤 用户:深圳宝安,巨无霸套餐,到店,怎么最省

💰 已匹配:巨无霸(¥26.50)

【方案1】到店自取巨无霸6元券
  到手价 ¥20.50(原价 ¥26.50,省 ¥6.00)
  标签:门店券、免配送费

【方案2】到店自取原价方案(无可用优惠)
  到手价 ¥26.50(原价 ¥26.50,省 ¥0.00)

⭐ 积分建议:推荐兑换「点餐优惠券 12元」:900 积分换¥12.00券,
   价值比0.0133 元/积分(全站最优)。兑换后剩余 736 积分

📊 本轮工具调用 9 次

方式二:运行自动化测试

node src/index.js --test

输出:109 项断言全部通过,覆盖意图识别、金额单位、券叠加规则、价格引擎、多渠道比价、凑单、活动预判、积分兑换、下单执行、异常处理、状态机与端到端链路。

方式三:配置自检(随时可查 Token 状态)

node src/index.js --config

确认 Token 是否已生效,未配置时会给出逐步指引。

方式四:接入真实 MCP(交互模式)

第 1 步:申请 MCP Token

访问 麦当劳 MCP 控制台 → 右上角【控制台】→【激活】→ 复制 Token。

第 2 步:配置 Token(零依赖,无需 npm install)

# 在项目根目录执行,复制配置模板
cp .env.example .env          # Windows CMD: copy .env.example .env

然后用编辑器打开 .env,把占位符替换成你的真实 Token:

MCP_TOKEN=你的真实token

.env 已在 .gitignore 中被忽略,可安全存放本地 Token,不会被提交到仓库。

也可以临时用环境变量(优先级高于 .env,仅当前窗口有效):

# Windows PowerShell
$env:MCP_TOKEN = "your_mcp_token"

# macOS / Linux
export MCP_TOKEN="your_mcp_token"

第 3 步:验证配置

node src/index.js --config
🍔 麦麦极致省钱规划师 · 配置自检

────────────────────────────────────────────────────
MCP Server : https://mcp.mcd.cn
协议类型   : streamablehttp
MCP Token  : a1b2c3****9f8e(长度 40)
────────────────────────────────────────────────────

✅ Token 已配置,可运行:node src/index.js

第 4 步:启动

node src/index.js
🍔 麦麦极致省钱规划师 · 交互模式(输入 exit 退出)

👤 你:深圳南山,35块预算,两个人,到店
💰 按35元预算为2人搭配,合计约 ¥32.50。以下是实际可用优惠券算出的最低到手价:
【方案1】到店自取薯条买一送一券
  到手价 ¥32.50(原价 ¥39.50,省 ¥7.00)
👉 说「下单方案1」创建订单  |  说「换个搭配」重新推荐

方式五:单次执行

node src/index.js "深圳宝安巨无霸套餐到店怎么最省"
node src/index.js --order 1 --address ADDR12345   # 外送下单需指定地址

方式六:作为 WorkBuddy Skill 使用

在 WorkBuddy 中配置 MCP 连接器后,添加本仓库的 Skill 即可获得对话式体验。


💬 调用示例

🎯 想直接看效果?跑示例集

node examples/usage.js

内置 Mock 数据,无需 Token,9 个可运行示例覆盖全部场景。输出节选:

══════════════════════════════════════════════════════════════════
  示例 1:一句话拿到最省方案
══════════════════════════════════════════════════════════════════
输入:深圳宝安,巨无霸,到店,怎么最省
识别意图:指定餐品最优
匹配商品:巨无霸

  【方案1】巨无霸6元券
    到手 ¥20.50(原价 ¥26.50,省 ¥6.00)
    叠加1张券:巨无霸6元券,共省¥6.00,距离约0.7km
  【方案2】原价方案(无可用优惠)
    到手 ¥26.50(原价 ¥26.50,省 ¥0.00)

本轮工具调用:9 次

══════════════════════════════════════════════════════════════════
  示例 3:多轮对话(下单闭环)
══════════════════════════════════════════════════════════════════
第1轮 >> 深圳宝安,巨无霸,到店,怎么最省
  最优:巨无霸6元券 → ¥20.50
第2轮 >> 下单方案1
  ✅ 下单成功
     订单号:MCD-20261017-8823
     支付链接:https://pay.mcd.cn/...

📞 三种调用方式

方式 A:命令行(最简单)

# 交互模式(推荐,像聊天一样用)
node src/index.js

# 单次执行,直接出结果
node src/index.js "深圳南山,巨无霸,到店,怎么最省"

# 静默模式,只输出结果(适合脚本/录屏)
node src/index.js "深圳南山,巨无霸,到店,怎么最省" -q

# 用内置 Mock,不联网(无需 Token)
node src/index.js --offline "深圳南山,巨无霸,到店,怎么最省"

# 下单(外送需带地址)
node src/index.js --order 1
node src/index.js --order 1 --address ADDR12345

支持的交互指令:

你说 效果
任意描述需求 出方案
下单方案1 创建订单,返回支付链接
换一家店 重新比价
再便宜点 查看凑单建议
exit / 退出 退出

方式 B:作为代码库调用

import { createPlanner } from './src/factory.js';

const planner = createPlanner();            // 不传 offline = 连真实接口

// ① 一句话拿方案
const result = await planner.handle('深圳南山,巨无霸,到店,怎么最省');

if (result.type === 'plans') {
  result.plans.forEach((p, i) => {
    console.log(`方案${i + 1}: ${p.planName} → ¥${p.finalPrice / 100}`);
  });
}

// ② 下单(注意:planner 是有状态的,上面的方案还在)
const order = await planner.order(1);
console.log(order.success ? order.payUrl : order.errorMsg);

// ③ 查订单状态
const status = await planner.queryOrder(order.orderId);

方式 C:编程式多轮对话

const planner = createPlanner({ offline: true });

// planner 内部维护会话状态,可连续多轮交互
await planner.handle('深圳南山,巨无霸,到店,怎么最省');  // 第1轮:出方案
await planner.order(1);                                    // 第2轮:下单
await planner.queryOrder();                                // 第3轮:查状态

// 发起全新需求时,handle() 会自动复位会话,无需手动 reset
await planner.handle('再来一单');

返回值结构:

result.type 含义 关键字段
plans 出方案了 plans[]、matchedItems、comboAdvice、pointsAdvice
clarification 信息不足,需追问 message、missingFields
campaign 活动建议 advice.suggestion、advice.futureActivities
points 积分建议 advice.bestCoupon、advice.pointValueRatio
error 执行异常 message

完整代码示例

见 examples/usage.js,覆盖 9 个场景,全部可直接运行。


📋 对话场景示例

以下为离线 Mock 数据下的实际输出,真实结果以线上门店菜单与实时优惠为准。

场景 1:指定餐品求最优

👤 深圳宝安,巨无霸套餐,到店,怎么最省
💰 已匹配:巨无霸(¥26.50)
【方案1】到店自取巨无霸6元券  到手价 ¥20.50(省 ¥6.00)
【方案2】到店自取原价方案    到手价 ¥26.50

场景 2:按预算推荐

👤 30块预算,一个人,外送
💰 预算30元可选商品有限,已搭配最接近的方案。以下是实际可用优惠券算出的最低到手价:
【方案1】麦乐送薯条买一送一券  到手价 ¥32.50(省 ¥7.00)
【方案2】麦乐送原价方案        到手价 ¥57.50

注:以上为离线 Mock 数据下的演示输出,真实结果以线上门店菜单与实时优惠为准。

场景 3:活动预判

👤 最近巨无霸有活动吗
📅 「巨无霸 8 折限时活动」预计优惠约20%,明显优于当前方案(¥33.50),
   建议等到活动开始再下单

场景 4:积分最优兑换

👤 用积分兑换最划算的券
⭐ 推荐兑换「点餐优惠券 12元」:900 积分换¥12.00券,
   价值比0.0133 元/积分(全站最优)。兑换后剩余 736 积分

场景 5:一键下单

👤 下单方案1
✅ 订单 MCD-20261017-8823|支付链接 https://pay.mcd.cn/...
   请通过支付链接扫码或打开麦当劳 APP 完成支付

🏗️ 项目结构

mcd-saver-planner/
├── README.md                    # 项目说明(本文件)
├── CONTEST_DECLARATION.md       # 参赛声明(官方原文件,内容未改动)
├── MCP_INTEGRATION.md           # MCP 集成说明:协议、工具、流程、业务价值
├── workbuddy.md                 # WorkBuddy 开发上下文记录
├── package.json
├── .env.example                 # Token 配置示例(占位符)
├── .gitignore
├── mock/                        # 离线 Mock(真实响应采样)
│   ├── data.js                  #   门店 / 菜单 / 券 / 积分 / 活动数据
│   └── transport.js             #   Mock fetch 传输层
├── examples/
│   └── usage.js                 # 9 个可运行调用示例
└── src/
    ├── index.js                 # CLI 入口
    ├── factory.js               # 规划器工厂(解耦 CLI 与测试)
    ├── config.js                # 全局配置 + 枚举 + beType 传递规则
    ├── mcp/
    │   ├── client.js            # MCP Client:Streamable HTTP + JSON-RPC
    │   └── tools.js             # 18 个 Tool 的类型化封装
    ├── modules/
    │   ├── intent.js            # 模块 1:需求解析与参数补全
    │   ├── couponRules.js       # 券模型 + 叠加规则引擎
    │   ├── priceEngine.js       # 模块 2:最优优惠组合计算
    │   ├── channelCompare.js    # 模块 3:多渠道比价
    │   ├── combo.js             # 模块 4:智能凑单推荐
    │   ├── campaign.js          # 模块 5:活动预判决策
    │   ├── points.js            # 模块 6:积分最优兑换
    │   ├── order.js             # 模块 7:下单执行引擎
    │   └── menuMatcher.js       # 关键词 → 真实商品编码匹配
    ├── workflow/
    │   ├── mainFlow.js          # 主流程编排
    │   ├── stateMachine.js      # 会话状态机
    │   └── testSuite.js         # 验收测试(109 项断言)
    └── utils/
        ├── cache.js             # TTL 缓存
        ├── retry.js             # 指数退避 + 限流 + 降级
        ├── logger.js            # 分级日志
        └── money.js             # 金额单位归一化(内部统一「分」)

🎯 目标用户

用户 痛点 本项目的价值
🧑 重度优惠党 手里有一堆券但不知道能用哪几张 自动遍历组合,找出叠加后最优解,权益不浪费
👨‍👩‍👧 家庭/多人用餐 预算有限,要搭配又要省 按人数与预算自动搭配,并算出实际最优到手价
🏃 赶时间上班族 来不及比价 一句话出方案,选完直接下单
💤 积分沉睡用户 积分放着慢慢过期 价值比算法找出最优兑换项,优先消耗临期积分
🧓 不熟悉 App 的用户 找不到券、不敢用券 自然语言交互,无需理解任何优惠规则

🔬 核心算法

1. 最优优惠组合遍历

1. 并行拉取:门店券 / 麦麦省券 / 用户已持券
2. 券分类打标:门店券组 / 麦麦省券组 / 活动券组 / 积分券组
3. 生成可行组合:
   - 空组合(不用券)永远保留,作为兜底基线
   - 单券组合
   - 跨类型组合(门店券 + 麦麦省券),跳过互斥与活动券
4. 逐个组合调用 calculate-price,以服务端返回为准
5. 组合数超限时按「券金额 + 是否多券叠加」剪枝,控制调用次数
6. 按最终到手价升序取 Top3

2. 多渠道综合评分

价格得分 = min-max 反向归一化(finalPrice)    权重 0.60
时间得分 = min-max 反向归一化(waitTime)      权重 0.25
距离得分 = min-max 反向归一化(distance)      权重 0.15
         (外送渠道固定 0.5,避免与配送时效重复惩罚)

综合得分 = 0.60×价格 + 0.25×时间 + 0.15×距离

3. 积分价值比

价值比 = 券面值 ÷ 所需积分     (单位:元/积分,越大越划算)

例:5元券/500积分=0.0100    12元券/900积分=0.0133 ✅    30元券/3000积分=0.0100

按价值比降序而非所需积分升序,才能找到真正的最优解。

4. 凑单收益校验

凑单后总价 = 当前总价 + 凑单品价格 − 券优惠额
仅当 凑单后总价 < 不用券总价 时才推荐

避免出现「为了用券反而多花钱」的负向体验。


🛡️ 稳定性与约束

约束 实现
单轮工具调用 ≤ 15 次 组合数上限 12 + 实时计数熔断
并发 ≤ 10,间隔 ≥ 100ms runLimited() 分批执行
429 限流 指数退避 1s / 2s / 4s,最多 3 次;客户端滑动窗口预检
401 Token 失效 立即终止,不做无意义重试
缓存 菜单/活动 1 小时,用户券/积分 5 分钟
优雅降级 可选能力失败不影响主流程,返回已算结果并标注
状态机 严格流转,禁止跨状态跳转;新需求自动复位

🔌 与官方 MCP 契约的对齐

开发中通过真实调用麦当劳 MCP Server 发现并处理了几个关键契约细节,详见 MCP_INTEGRATION.md:

  1. 优惠券绑定在商品项级——couponId 是 items[] 内的字段,多券叠加时必须挂到不同商品项
  2. beCode 强关联——得来速/外送/团餐场景必传,代码中统一由 resolveChannel() 处理
  3. 金额单位不统一——价格返回「分」,菜单返回「元」,券类字段为「元」,内部统一为「分」
  4. 券的满减信息藏在标题里——query-store-coupons 不返回 threshold/discount,需从 title 文案解析
  5. items schema 兼容——同时兼容标准数组、嵌套对象、双层包裹三种形态

📄 参赛信息

本项目为麦当劳程序员节创意开发大赛参赛作品。

报名 Issue 格式:

Issue标题:麦麦极致省钱规划师 - 全渠道优惠决策引擎
Issue正文:
【参赛申请】
项目名称:麦麦极致省钱规划师
项目地址:https://github.com/你的用户名/mcd-saver-planner
项目简介:基于麦当劳MCP的智能省钱决策引擎,自动遍历全渠道全量优惠组合,输出最低到手价点餐方案,支持多渠道比价、智能凑单、积分最优兑换、一键领券下单。

⚠️ 免责声明

本项目为参赛者独立开发,非麦当劳官方产品。项目输出仅供参考,不构成医疗、营养或其他专业建议;餐品信息、价格及供应状态以麦当劳官方渠道的实时结果为准。

📄 License

MIT

About

No description, website, or topics provided.

Resources

Stars

105 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages