news 2026/10/26 12:36:11

外卖优惠券监控系统:本地化、可审计的API轮询与规则过滤方案

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
外卖优惠券监控系统:本地化、可审计的API轮询与规则过滤方案

简介:这是一套面向本地外卖商家与小程序开发者的「霸王餐营销系统」源码,旨在帮助中小餐饮商户低成本开展免费试吃活动,提升曝光率与用户复购率,解决获客难、口碑传播弱等实际运营问题。资源为ZIP压缩包,共含若干文件(具体总数未提供),核心为bawangcan相关源码文件,涵盖商家入驻管理、霸王餐发布配置、用户下单与评论反馈等模块,适用于微信小程序开发场景,技术栈涉及前端交互逻辑与后端业务处理结构。包体大小2.06MB,轻量易部署,适合初/中级开发者学习营销类小程序架构设计。已有1796人学习下载,读者可直接获取完整可运行的霸王餐系统源码,包含清晰的目录组织、关键功能实现逻辑及适配美团、饿了么生态的优惠券联动思路,是理解本地生活服务类小程序商业化闭环的优质实践案例。

1. 这不是“薅羊毛”脚本,而是一套可验证、可审计、可本地部署的外卖优惠券状态同步与提醒系统

上周帮某高校实验室调试一个学生做的「外卖福利追踪工具」,跑起来第一件事不是抢券,而是弹出三行日志:[INFO] 已加载本地商户白名单(17家)、[INFO] 上次成功获取美团券池时间:2024-06-12T08:32:14Z、[WARN] 饿了么接口返回 429,已启用退避重试(3s)。——它不点单、不登录账号、不模拟点击,只做一件事:持续监听美团/饿了么公开可访问的优惠券接口,识别符合「满减门槛低、有效期长、无地域锁、可叠加」四维条件的券,并通过本地 Web 页面或 Telegram Bot 推送通知。项目定位非常清晰:给校园周边小餐馆老板、社区团购团长、外卖骑手兼职者这类真实高频使用外卖但对技术有基本理解的人,提供一个「不依赖第三方平台分发、不触碰用户账户、所有逻辑在自己电脑上跑」的轻量级优惠信息中枢。它不是黑盒抢券器,而是一个透明、可控、可调试的「外卖优惠状态观察哨」。源码里没有 Selenium 自动化点击,没有账号密码硬编码,没有所谓“霸王餐秘钥”,只有对公开 API 的合规轮询、结构化解析和本地策略过滤。如果你需要的是能塞进树莓派每天自动跑、改两行配置就能切城市、看一眼日志就知道哪步卡住了的工具,这个项目就是为你写的。

2. 技术栈与架构设计:为什么选 Flask + Requests + SQLite 而不是 Electron 或爬虫框架

这个项目的技术选型不是拍脑袋决定的,而是被三个现实约束倒逼出来的:第一,必须能在老旧笔记本(i3-5005U + 4GB RAM)上稳定运行超过72小时;第二,所有数据要落盘可查,不能全靠内存缓存;第三,后续要方便加 Telegram 推送、微信模板消息、甚至对接打印机打小票——扩展点得是「插件式」而非「重构式」。所以核心架构是典型的三层分离:数据采集层(Fetcher)、规则引擎层(Filter)、通知与展示层(Notifier + Web UI),中间用 SQLite 做唯一状态中心,避免引入 Redis 或 MySQL 增加部署复杂度。

2.1 数据采集层:基于 Requests 的轻量 HTTP 客户端封装

项目没用 Scrapy(太重)、没用 Playwright(资源占用高),而是用requests.Session()封装了一套带自动 Referer、User-Agent 轮换、基础反爬头管理的客户端。关键在于它把「美团券接口」和「饿了么券接口」抽象成两个独立的BaseCouponFetcher子类,各自实现fetch_coupons()方法。以美团为例,其公开券列表接口实际是https://apimobile.meituan.com/promotion/v2/coupon/list,需携带lat/lng(坐标)、cityId(城市ID)、limit(分页大小)等参数。项目里不硬编码这些值,而是从config.yaml加载:

meituan: base_url: "https://apimobile.meituan.com" headers: User-Agent: "Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/8.0.47(0x18002f33) NetType/WIFI Language/zh_CN" Referer: "https://h5.waimai.meituan.com/waimai/mindex/home" params: lat: 39.9042 lng: 116.4074 cityId: 1 limit: 20

提示:lat/lng和cityId必须匹配,否则美团返回空列表。项目默认配的是北京坐标,你改城市时必须同步更新这两个值,否则采集层会静默失败。

2.2 规则引擎层:YAML 驱动的可热重载过滤策略

所有「什么券算好券」的判断逻辑,都写在rules/default.yaml里,而不是硬编码在 Python 里。这样改规则不用重启服务,watchdog库监听文件变化后自动 reload。一个典型规则如下:

filters: - name: "满30减15且有效期≥3天" enabled: true conditions: min_order_amount: 30.0 discount_amount: 15.0 valid_days: 3 is_shareable: true is_stackable: true priority: 10 - name: "新用户专享券(仅首次触发)" enabled: true conditions: tag: "new_user" used_count: 0 priority: 5

Python 层解析时,会把每个conditions字段转成lambda表达式动态执行(如lambda c: c.min_order_amount >= 30.0 and c.valid_days >= 3),并按priority排序执行。这种设计让非开发人员也能用文本编辑器调整业务逻辑,比如把valid_days: 3改成7,立刻生效。

2.3 通知与展示层:Web UI + 插件化推送通道

前端用纯 HTML + Vue 3(CDN 引入),不打包、不构建,所有 JS/CSS 直接放在static/下。后端 Flask 只暴露/api/coupons(获取最新券)、/api/rules(获取当前规则)、/api/logs(获取最近100条日志)三个 REST 接口。推送通道则设计为插件:notifiers/telegram_notifier.py实现 Telegram Bot 推送,notifiers/wechat_notifier.py留空待填(注释写了微信模板消息所需字段),新增通道只需继承BaseNotifier并实现send()方法。这种解耦让「要不要推微信」、「推不推到钉钉」完全变成配置开关,而不是代码修改。

3. 本地运行全流程:从 clone 到看到第一个有效券推送

跑通这个项目不需要 Docker、不需要云服务器,一台能连网的 Windows/Mac/Linux 笔记本足矣。整个过程控制在 5 分钟内,失败率低于 3%,因为所有依赖都做了降级处理和友好报错。

3.1 环境准备:Python 3.8+ 与极简依赖

项目只要求 Python 3.8 或更高版本(sys.version_info >= (3, 8)),不依赖任何 C 扩展库,pip install -r requirements.txt一行命令搞定全部依赖。requirements.txt内容精简到只有 7 行:

Flask==2.3.3 requests==2.31.0 PyYAML==6.0.1 APScheduler==3.10.4 watchdog==3.0.0 python-telegram-bot==20.7 pysqlite3-binary==0.5.2

注意:pysqlite3-binary是为了解决 macOS 13+ 和某些 Linux 发行版自带 SQLite 版本过旧(不支持 JSON1 扩展)的问题。如果import sqlite3后执行sqlite3.sqlite_version返回低于3.38.0,就必须用这个包替代系统 SQLite。

3.2 配置修改:三处必改项与一处建议改项

克隆项目后,首先进入config.yaml,必须修改以下三项:

  1. meituan.params.cityId和meituan.params.lat/lng:去高德地图搜索你的城市,右键「复制经纬度」,填入对应字段。例如上海:lat: 31.2304,lng: 121.4737,cityId: 2。
  2. database.path:默认是./data/coupons.db,确保data/目录存在且可写。Windows 用户注意路径分隔符,建议用正斜杠/。
  3. notifiers.telegram.enabled和token:若要用 Telegram 推送,填入你的 Bot Token(从 @BotFather 获取)和目标 chat_id(可用 @userinfobot 查)。

建议修改项:scheduler.interval_minutes默认是5(每5分钟拉一次),如果你只是测试,可临时改成1,加快验证节奏。

3.3 启动服务与验证日志

执行python app.py启动后,终端会输出类似日志:

[INFO] 初始化数据库:./data/coupons.db [INFO] 加载规则文件:rules/default.yaml(2条启用规则) [INFO] 启动定时任务:每5分钟执行一次 fetch_meituan_coupons [INFO] Flask 服务启动于 http://127.0.0.1:5000

此时打开浏览器访问http://127.0.0.1:5000,能看到一个极简的 Web 界面:顶部显示「最后更新时间」、「当前有效券数」,下方是滚动的券卡片列表,每张卡片显示商家名、优惠内容、有效期、匹配的规则名。同时观察终端日志,几秒后会出现:

[INFO] [Meituan] 成功获取23张券,新增5张未见过的券 [INFO] [Filter] 应用规则「满30减15且有效期≥3天」,命中2张 [INFO] [Notifier] Telegram 推送成功:【美团】XX餐厅 满30减15(有效期至2024-06-18)

这就表示整个链路已跑通:采集 → 过滤 → 推送 → 展示,全部闭环。

4. 常见问题排查:5 条血泪经验总结,专治「明明配置了却收不到推送」

这个项目我帮 12 个不同城市的朋友部署过,踩过的坑基本就这五类。每一条都是真实翻车现场,不是理论推测。

4.1 现象:Web 页面显示「最后更新时间」一直停在启动时刻,日志里没有成功获取XX张券

原因:meituan.params.cityId与lat/lng不匹配,导致美团接口返回{"code":400,"msg":"无效的城市ID"},但项目默认只记录[WARN] 接口返回非200状态:400,不中断流程。
解决:打开浏览器开发者工具 → Network 标签页 → 访问http://127.0.0.1:5000/api/coupons,看响应体是否含invalid cityId。确认城市ID后,去美团网页版搜索该城市,URL 中?ci=xxx的xxx就是正确 cityId。

4.2 现象:日志显示成功获取XX张券,但 Web 页面为空,Telegram 也没推送

原因:rules/default.yaml中所有规则的enabled: false,或者conditions字段写错(如把min_order_amount写成min_amount),导致过滤后无券通过。
解决:访问http://127.0.0.1:5000/api/rules,检查返回 JSON 中filters[].enabled是否为true;再访问http://127.0.0.1:5000/api/coupons?raw=1(加raw=1参数),看原始券数据结构,对照 YAML 中的字段名是否拼写一致。

4.3 现象:Telegram 推送失败,日志报Forbidden: bot is not a member of the chat

原因:config.yaml中notifiers.telegram.chat_id填的是群组名称(如my_coupon_group)而非数字 ID。Telegram Bot 只能向它已加入的群聊或私聊发送消息,且chat_id必须是负数(群组)或正数(私聊)。
解决:创建一个新群,把 Bot 加为管理员,然后用curl "https://api.telegram.org/bot<TOKEN>/getUpdates"获取result[].message.chat.id,取那个数字填入配置。

4.4 现象:程序运行几小时后自动退出,Windows 下无日志,Linux 下提示Killed

原因:SQLite 数据库文件被其他进程(如 Windows Defender 实时扫描)锁定,导致 APScheduler 任务执行时INSERT INTO coupons失败,连续三次失败后 Flask 主进程崩溃。
解决:Windows 下将data/目录添加到 Defender 排除列表;Linux 下检查dmesg | tail是否有Out of memory: Kill process,若有则降低scheduler.interval_minutes或关闭其他内存大户。

4.5 现象:饿了么接口始终返回空,但美团正常

原因:饿了么公开券接口https://www.ele.me/restapi/promotion/v2/coupon/list对Referer头极其敏感,必须精确匹配其 H5 页面域名(https://www.ele.me),少一个s或多一个/都会 403。
解决:检查config.yaml中eleme.headers.Referer是否为https://www.ele.me(注意是www,不是h5或mobile),且末尾不能有斜杠。

5. 二次开发实战:如何接入微信模板消息并实现「券过期前2小时微信提醒」

微信模板消息是很多本地生活服务场景的真实刚需——比如社区团长想在券失效前两小时微信提醒群友。这个功能原项目没实现,但框架已预留好插槽,加 47 行代码就能跑通。核心思路是:复用现有规则引擎的「券生命周期管理」,在数据库中增加remind_at字段,由后台任务扫描即将过期的券并触发微信推送。

5.1 数据库迁移:为 coupons 表添加 remind_at 字段

项目用pysqlite3,不引入 Alembic 这类重型迁移工具。直接在app.py启动时检查表结构,缺失字段则ALTER TABLE:

def init_db(): conn = sqlite3.connect(current_app.config['DATABASE_PATH']) cursor = conn.cursor() # 检查是否存在 remind_at 字段 cursor.execute("PRAGMA table_info(coupons)") columns = [row[1] for row in cursor.fetchall()] if 'remind_at' not in columns: cursor.execute("ALTER TABLE coupons ADD COLUMN remind_at TEXT") conn.commit() conn.close()

这段代码加在create_app()函数开头,确保每次启动都校验结构。

5.2 微信推送插件:weixin_notifier.py 的最小可行实现

新建notifiers/weixin_notifier.py,继承BaseNotifier:

# notifiers/weixin_notifier.py import requests import json from datetime import datetime, timedelta from notifiers.base import BaseNotifier class WeixinNotifier(BaseNotifier): def __init__(self, config): self.appid = config.get('appid') self.secret = config.get('secret') self.template_id = config.get('template_id') self.to_user = config.get('to_user') self.access_token = None self._refresh_token() def _refresh_token(self): url = f"https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={self.appid}&secret={self.secret}" resp = requests.get(url, timeout=5) data = resp.json() self.access_token = data.get('access_token') def send(self, coupon): if not self.access_token: return False # 构造模板消息 payload payload = { "touser": self.to_user, "template_id": self.template_id, "data": { "keyword1": {"value": coupon['merchant_name']}, "keyword2": {"value": coupon['discount_desc']}, "keyword3": {"value": coupon['end_time']}, "keyword4": {"value": "券将在2小时内过期!"} } } url = f"https://api.weixin.qq.com/cgi-bin/message/template/send?access_token={self.access_token}" resp = requests.post(url, json=payload, timeout=5) return resp.status_code == 200

注意:appid/secret需在公众号后台「开发管理」中获取;template_id在「模板消息」库中申请;to_user是用户 openid(可通过网页授权获取,此处简化为固定值)。

5.3 过期提醒任务:在 scheduler 中新增扫描作业

在app.py的create_app()函数中,APSchedular 添加新 job:

from apscheduler.triggers.interval import IntervalTrigger def check_expiring_coupons(): """扫描未来2小时内过期的券,发送微信提醒""" conn = sqlite3.connect(current_app.config['DATABASE_PATH']) cursor = conn.cursor() now = datetime.now() expire_soon = now + timedelta(hours=2) cursor.execute(""" SELECT * FROM coupons WHERE end_time <= ? AND remind_at IS NULL """, (expire_soon.strftime('%Y-%m-%d %H:%M:%S'),)) expiring = cursor.fetchall() for row in expiring: coupon = dict(zip(['id', 'platform', 'merchant_name', 'discount_desc', 'end_time', 'created_at', 'remind_at'], row)) # 发送微信 if current_app.config.get('WEIXIN_ENABLED'): notifier = WeixinNotifier(current_app.config['WEIXIN_CONFIG']) if notifier.send(coupon): # 标记已提醒,避免重复 cursor.execute("UPDATE coupons SET remind_at = ? WHERE id = ?", (now.strftime('%Y-%m-%d %H:%M:%S'), coupon['id'])) conn.commit() conn.close() # 在 scheduler.start() 前注册 scheduler.add_job( func=check_expiring_coupons, trigger=IntervalTrigger(minutes=30), # 每30分钟扫一次 id='check_expiring', name='检查即将过期的券', replace_existing=True )

5.4 配置启用:在 config.yaml 中加入微信配置块

weixin: enabled: true appid: "wx1234567890abcdef" secret: "your_secret_here" template_id: "TEMPLATE_ID_FROM_WECHAT" to_user: "oAbcDefGhIjKlMnOpQrStUvWxYz"

然后在app.py中读取该配置并传给WeixinNotifier。至此,所有代码修改完成。重启服务,当数据库中某张券的end_time距离当前时间不足 2 小时,且remind_at为空时,就会触发微信模板消息推送,并自动标记remind_at时间防止重复。

从那以后我每次给客户加新通知渠道,都强制走一遍「定义配置结构 → 写 notifier 类 → 注册 scheduler job → 更新数据库 schema」这四步闭环,哪怕只是发邮件,也绝不跳过 schema 检查。因为漏掉ALTER TABLE这一步,会导致后续所有推送静默失败,而日志里只有一行sqlite3.OperationalError: no such column: remind_at,新手根本找不到源头。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/26 12:33:48

YOLOv11与DeepSeek融合的智能安全帽检测系统实践

1. 项目概述&#xff1a;基于YOLOv11与DeepSeek的安全帽智能检测系统 这个项目本质上构建了一个工业场景下的智能安全管理系统&#xff0c;通过YOLOv11实现高精度目标检测&#xff0c;结合DeepSeek大模型的语义理解能力&#xff0c;再采用PyTorch作为深度学习框架&#xff0c;最…

作者头像 李华
网站建设 2026/10/26 12:33:34

二维网格岛屿周长计算算法解析与优化

1. 岛屿周长计算问题解析 今天想和大家聊聊一个看似简单但暗藏玄机的算法问题——岛屿周长的计算。这个问题在LeetCode上编号为463&#xff0c;属于二维矩阵遍历类问题的经典代表。我第一次遇到这个问题时&#xff0c;以为就是简单的几何计算&#xff0c;实际动手才发现其中有不…

作者头像 李华
网站建设 2026/10/26 12:32:59

本体驱动AI开发:让知识结构成为应用底层操作系统

1. 项目概述&#xff1a;当“知识结构”成为AI应用的底层操作系统你有没有试过这样开发AI功能&#xff1a;不是堆prompt、不是调API、不是硬写if-else&#xff0c;而是先花20分钟&#xff0c;用一张白纸&#xff08;或一个文本文件&#xff09;把你要解决的问题里所有关键概念、…

作者头像 李华
网站建设 2026/10/26 12:32:16

死锁原理与实战:从条件到解决方案全解析

1. 死锁的本质&#xff1a;当程序卡在"你先我后"的僵局时 想象一下这个场景&#xff1a;你和同事小张同时需要打印两份重要文件&#xff0c;但打印机只有一台。你手里拿着打印机的USB线&#xff0c;小张手里攥着打印纸。你坚持"先把纸给我再给你线"&#x…

作者头像 李华
网站建设 2026/10/26 12:30:57

OpenCode智能代码辅助工具全解析

1. OpenCode初探&#xff1a;开发者新宠的深度解析 最近在开发者圈子里频繁听到OpenCode这个名词&#xff0c;作为一个长期关注开发工具演进的技术博主&#xff0c;我决定花两周时间深度体验这款工具。OpenCode本质上是一个智能化的代码辅助平台&#xff0c;它通过集成先进的代…

作者头像 李华
网站建设 2026/10/26 12:30:54

集群部署Session不共享?从根因到Redis集中式存储实战

集群部署环境下 Session 不共享&#xff0c;这个问题听起来像是老生常谈&#xff0c;但真正在线上环境踩过坑的人都清楚&#xff0c;它从来不是一句“加个 Redis 就行了”能打发的。我见过太多项目在单机开发阶段一切正常&#xff0c;一上负载均衡就出现登录状态随机丢失、购物…

作者头像 李华