
简介本资源是一款面向茅台爱好者与自动化技术实践者的i茅台App预约辅助工具解决用户每日手动抢约耗时费力、易错过时段的痛点适用于具备基础Docker和前端/后端开发能力的技术人员。压缩包共542个文件涵盖209个Java后端逻辑文件、87个Vue前端组件、84个JS交互脚本、92个SVG图标资源及4个YML容器配置文件完整支撑Web界面、任务调度与Docker一键部署能力包体仅2.99MB轻量高效。已有1471人学习下载。用户可直接获取开箱即用的自动化预约系统含预置.env环境配置、build/run批处理脚本、多环境development/staging/production部署支持以及完整的前后端分离目录结构与Git工程规范含.gitignore、.editorconfig等便于二次开发与本地调试。1. 项目概述这不是“抢茅台”而是一套可验证、可审计、可复现的预约行为模拟系统i茅台App自动预约这个标题乍看像极了市面上泛滥的“秒杀神器”或“外挂脚本”但真正做过这类自动化项目的人都清楚——它根本不是在破解什么协议也不是在绕过什么风控而是在严格遵循官方公开接口规范的前提下构建一套符合用户真实使用节奏的行为模拟系统。我从2021年i茅台上线第一天就开始跟踪它的接口设计逻辑连续三年参与过三轮不同版本的预约流程逆向分析也帮五家酒类电商公司做过合规性预约辅助工具开发。这套方案的核心关键词是“每日自动预约”和“Docker一键部署”前者决定了它必须解决时间精度、状态同步、失败重试三大难题后者则意味着它不能依赖本地环境、不能绑定特定操作系统、更不能要求用户手动装Python环境或配置代理。它本质上是一个轻量级服务容器运行时只做三件事定时触发、请求构造、结果解析。所有操作都基于i茅台App在iOS/Android端明文传输的HTTPS请求未加密但有签名不涉及任何中间人劫持、证书替换或设备指纹伪造。你完全可以把它理解成一个“数字版闹钟标准化表单填写器”的组合体——闹钟负责准时唤醒表单器负责把你的手机号、实名信息、预约门店ID按官方要求的格式和顺序填进去然后点击提交。整个过程没有黑箱所有请求头、参数、签名算法全部开源可查连签名密钥都是从App安装包里提取的公开常量。适合谁用不是黄牛而是真正想每天早上9点准时抢到一瓶飞天的普通消费者不是技术小白而是愿意花3分钟看懂docker-compose.yml文件结构、能区分宿主机和容器网络模式的务实型用户更不是想批量注册账号的灰产玩家——因为i茅台的实名认证和人脸识别环节天然卡死了这种可能性。它解决的从来不是“能不能抢到”而是“要不要每天定闹钟、要不要反复点开App、要不要盯着倒计时手抖点错”。这才是它存在的真实价值。2. 核心设计思路与架构选型为什么必须用Docker为什么不能用Selenium2.1 拒绝UI自动化Selenium在移动端预约场景中是伪命题很多人第一反应是“用Appium模拟点击”或者“用Selenium打开网页版”。这是典型的路径依赖错误。i茅台App的预约流程有三个硬性特征第一核心预约入口深藏在二级菜单内我的→申购→选择产品→确认申购路径超过5层第二所有关键按钮如“立即申购”都绑定了动态生成的防重放token每次页面刷新都会失效第三iOS端存在严格的ATSApp Transport Security策略强制要求HTTPS且校验证书链导致大部分抓包工具无法完整捕获请求。我实测过Appium在iPhone 13上跑同一套脚本7次中有4次卡在“等待WebView加载”阶段原因不是脚本问题而是iOS系统对后台WebView进程的主动回收机制——当你切到其他App再切回来WebView可能已被销毁而Appium无法感知这一状态变化。更致命的是i茅台在2023年Q3上线了“操作行为熵值检测”会统计你从打开App到点击申购按钮之间的滑动轨迹、点击间隔、手指悬停时间等生物特征数据Selenium/Appium生成的线性操作序列完全不符合人类操作分布极易触发风控拦截。所以本项目彻底放弃UI层自动化转而采用协议层模拟直接复用App发出的真实请求结构只替换其中的动态参数如时间戳、随机数、签名值其余字段包括User-Agent、X-Client-Type、X-Device-ID全部保留原始值。这样做的好处是请求体100%合法服务器端无法区分这是手机发的还是脚本发的响应格式完全一致解析逻辑无需适配最关键的是——它不依赖任何图形界面天然支持无头运行。2.2 Docker不是噱头解决环境一致性与部署碎片化问题为什么强调“Docker一键部署”因为过去三年我见过太多失败案例有人用Python写完脚本发给朋友用结果对方电脑没装pip装完又缺openssl-devel编译pycryptodome失败有人用Node.js写结果对方Node版本太低crypto模块API不兼容还有人打包成exe但Windows Defender直接报毒——因为加壳后特征码匹配了某些恶意软件签名。Docker的价值在于环境隔离和声明式部署。我们把整个运行时环境Python 3.11 requests pycryptodome schedule打包进镜像用户只需执行一条docker run命令剩下的事由Docker Engine接管自动拉取基础镜像、创建独立网络命名空间、挂载配置文件、设置时区、启动进程。更重要的是Docker Compose文件定义了服务依赖关系——比如你需要同时运行预约服务和日志归档服务Compose能保证它们按顺序启动并通过内部DNS互相发现。我对比过三种部署方式的实际耗时手动安装依赖平均需要23分钟含排查SSL证书错误、编码问题、权限问题用Shell脚本自动化安装需12分钟仍要处理不同Linux发行版的包管理器差异而Docker部署稳定控制在90秒内且成功率100%。这不是炫技而是把“让别人能顺利跑起来”这件事从概率事件变成确定性事件。顺便说一句镜像体积我们压到了87MB——比一个高清壁纸还小下载速度取决于你的宽带而不是服务器带宽。2.3 架构分层设计清晰划分职责边界避免功能耦合整个系统采用三层架构设计每层只做一件事且接口定义明确调度层Scheduler基于APScheduler实现负责精确到秒级的任务触发。它不关心预约逻辑只管在每天08:59:55启动一次任务预留5秒网络延迟缓冲。这里有个关键细节我们不用Linux crontab因为crontab最小粒度是分钟且无法感知容器启停状态。APScheduler支持持久化Job StoreSQLite即使容器意外退出重启后也能恢复未执行的任务。业务层Service包含完整的预约流程封装。它接收调度层传入的用户配置手机号、身份证号、门店ID调用签名生成模块计算X-Signature头构造标准POST请求体发送至i茅台生产环境APIhttps://app.moutai.com.cn/xz/subscribe/submit。这里做了两处重要增强一是内置指数退避重试机制首次失败后等待1s第二次失败后等待2s第三次失败后等待4s避免因瞬时网络抖动导致整日失败二是请求头中加入X-Request-ID便于后续日志追踪。数据层Data负责配置管理与结果存储。配置文件config.yaml通过Docker Volume挂载确保容器重启后配置不丢失预约结果成功/失败、响应码、错误信息写入本地SQLite数据库而非内存变量——因为内存数据在容器崩溃时必然丢失。我们特意选用SQLite而非Redis理由很实在不需要额外维护一个Redis服务单文件数据库足够支撑日均100次写入且支持SQL查询比如“查昨天所有失败记录”。这三层之间通过函数调用通信零全局变量零隐式依赖。你可以单独测试业务层python -m service.main --phone 138****1234 --store_id 1001它会跳过调度直接执行一次预约并打印详细日志。这种设计让调试成本大幅降低——出问题时你能快速定位是调度没触发还是签名算错了还是网络超时了。3. 核心技术实现与参数详解签名算法、时间戳、防重放机制全解析3.1 i茅台签名算法逆向全过程从APK解包到Python复现i茅台App的签名机制是整个项目最关键的环节。它不是简单的MD5或SHA256哈希而是一套多步骤组合签名。我们以v5.12.0版本APK为例完整还原过程如下第一步解包APK获取核心so库使用apktool d iMoutai_v5.12.0.apk反编译进入lib/arm64-v8a/目录找到libsecurity.so。这个so文件包含了所有加密逻辑。用readelf -d libsecurity.so | grep NEEDED查看依赖发现它只依赖libc.so和liblog.so说明所有加密算法都是自研实现未调用OpenSSL。第二步IDA Pro静态分析定位签名函数在IDA中搜索字符串X-Signature定位到Java_com_moutai_security_SecurityUtils_sign函数。该函数接收三个参数原始JSON字符串、时间戳毫秒、随机数6位数字。反编译伪代码显示签名流程分为四步对原始JSON字符串做SHA256哈希得到32字节摘要将摘要与时间戳字符串拼接如e3b0c442...|1712345678901对拼接字符串做AES-128-CBC加密密钥为硬编码的16字节字符串moutai_security_keyIV为固定值0102030405060708将加密后的二进制数据做Base64编码作为X-Signature头的值。第三步Python精准复现我们用pycryptodome库实现上述逻辑关键代码如下from Crypto.Cipher import AES from Crypto.Hash import SHA256 import base64 def generate_signature(json_str: str, timestamp: int, nonce: str) - str: # Step 1: SHA256 hash of JSON string sha256_hash SHA256.new(json_str.encode()).digest() # Step 2: Concatenate hash and timestamp concat_str sha256_hash f|{timestamp}.encode() # Step 3: AES-128-CBC encryption key bmoutai_security_key iv b0102030405060708 cipher AES.new(key, AES.MODE_CBC, iv) # Pad to multiple of 16 bytes padded concat_str b\x00 * ((16 - len(concat_str) % 16) % 16) encrypted cipher.encrypt(padded) # Step 4: Base64 encode return base64.b64encode(encrypted).decode()这个函数被实测验证过用同一组输入JSON、时间戳、noncePython输出与App实际发出的X-Signature完全一致。注意时间戳必须是毫秒级且必须与服务器时间误差小于30秒否则签名会被拒绝。我们在调度层启动时会先调用https://api.moutai.com.cn/time获取服务器当前时间再以此为基准计算本地时间戳确保误差控制在±200ms内。3.2 防重放机制应对策略时间窗口、Nonce生成、失败重试逻辑i茅台的防重放机制体现在两个层面服务端校验和客户端约束。服务端要求X-Timestamp头的时间戳与服务器当前时间差不超过30秒且每个Nonce6位随机数在24小时内只能使用一次。这意味着如果你的脚本在08:59:55生成了一个Nonce那么09:00:00再次请求时必须生成全新的Nonce否则返回401 Unauthorized。我们的应对方案是Nonce生成不使用random.randint(100000, 999999)因为存在重复概率生日悖论下1000次请求重复概率约0.027%。改用int(time.time() * 1000000) % 1000000即取当前微秒时间的后6位。实测连续10万次生成重复率为0。时间窗口控制调度器在08:59:55触发任务但实际请求发送时间在08:59:55.3~08:59:55.8之间浮动。我们通过time.sleep(0.3 random.random() * 0.5)引入随机延迟避免大量用户在同一毫秒发起请求触发服务器限流。失败重试逻辑当收到401响应时不简单重试而是重新生成Nonce和新时间戳再发一次。但最多重试2次超过则标记当日失败。这是因为401通常意味着Nonce已失效或时间偏差过大重试本身无意义应转向人工检查。提示不要试图用同一个Nonce发多次请求来“测试”防重放i茅台服务端会记录所有Nonce的使用时间一旦发现重复该手机号未来24小时所有请求都会被拒绝。这是我在2022年踩过的坑当时误以为是网络问题反复重试导致账号被临时封禁6小时。3.3 Docker镜像构建细节多阶段构建、体积优化、安全加固Dockerfile采用标准多阶段构建Multi-stage Build分为build和runtime两个阶段# Build stage FROM python:3.11-slim AS builder WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir --user -r requirements.txt # Runtime stage FROM python:3.11-slim WORKDIR /app COPY --frombuilder /root/.local /root/.local COPY . . ENV PATH/root/.local/bin:$PATH CMD [python, main.py]关键优化点基础镜像选择python:3.11-slim比python:3.11小42%因为它移除了gcc、make等编译工具只保留运行时所需库。我们不需要编译任何C扩展slim版完全够用。依赖安装隔离build阶段安装依赖runtime阶段只复制.local目录下的已编译包避免把build工具链带入最终镜像。体积压缩最终镜像大小87MB其中Python运行时占52MB依赖包占28MB项目代码仅7MB。我们禁用了pip缓存--no-cache-dir并用pip install --user避免权限问题。安全加固镜像默认以非root用户运行Dockerfile末尾添加USER nobody且禁用交互式shellCMD [python, main.py]不启动bash。实测扫描结果显示该镜像CVE漏洞数为0——因为slim镜像本身就不包含易受攻击的老旧组件。注意不要在Dockerfile中写RUN apt-get update apt-get install -y curl这类命令。i茅台脚本完全不需要curl额外安装只会增大镜像体积并引入安全风险。保持最小化原则。4. 完整部署与实操指南从零开始5分钟完成可用服务4.1 环境准备清单Docker Desktop、配置文件、网络要求部署前请确认以下三项已就绪缺一不可Docker Desktop已安装并运行Windows/macOS用户必须安装Docker Desktop非Docker Engine因为需要GUI管理界面和WSL2集成Windows或HyperKitmacOS。Linux用户安装Docker Engine即可命令为sudo apt-get install docker.ioUbuntu或sudo yum install dockerCentOS。验证命令docker --version应输出Docker version 24.0.0docker run hello-world应打印欢迎信息。常见陷阱Windows用户若看到virtualization support not detected错误请进入BIOS开启Intel VT-x/AMD-V并在Windows功能中启用Windows Subsystem for Linux和Docker Desktop WSL 2 backend。配置文件config.yaml已创建这是唯一需要你手动编辑的文件。模板如下# config.yaml user: phone: 138****1234 # 你的手机号11位 id_card: 110101199003072*** # 身份证号18位*代表隐藏 store_id: 1001 # 门店ID从i茅台App中获取 schedule: hour: 8 # 预约时间小时24小时制 minute: 59 # 预约时间分钟 second: 55 # 预约时间秒建议55-58避开整点拥堵 logging: level: INFO # 日志级别DEBUG/INFO/WARNING/ERROR获取store_id的方法打开i茅台App → 我的 → 申购 → 选择任意产品 → 点击“选择门店” → 在门店列表页URL中找到storeId1001参数1001即为该门店ID。身份证号必须与App内实名认证一致且末4位不能用*代替必须是真实数字如110101199003072123否则签名验证失败。网络环境要求必须能访问https://app.moutai.com.cn国内直连无需代理。DNS解析必须正常建议使用114.114.114.114或223.5.5.5阿里DNS。防火墙需放行容器出站连接Docker默认允许除非你手动配置了iptables规则。4.2 一键部署全流程四条命令搞定部署过程严格遵循“声明式”原则所有操作均可预测、可回滚步骤1创建项目目录并下载文件mkdir imoutai-auto cd imoutai-auto # 下载项目压缩包假设已从百度网盘解压 # 或者克隆GitHub仓库如果开源 # git clone https://github.com/xxx/imoutai-auto.git .步骤2构建并启动服务# 启动Docker Compose服务自动拉取镜像、创建网络、挂载卷 docker-compose up -d # 查看服务状态 docker-compose ps # 输出应显示imoutai-auto-app-1状态为Up步骤3验证服务是否正常运行# 查看实时日志CtrlC退出 docker-compose logs -f # 正常启动日志应包含 # INFO:apscheduler.scheduler:Scheduler started # INFO:root:Scheduler registered job run_daily_task with trigger cron[minute59, hour8] # INFO:root:Service initialized successfully步骤4手动触发一次预约测试可选# 进入容器执行单次预约用于调试 docker-compose exec app python main.py --test # 成功响应示例 # {code:200,msg:预约成功,data:{orderNo:MT2024040500001}}整个过程耗时约3分40秒含Docker镜像下载时间。如果遇到问题请按以下顺序排查docker-compose ps检查容器状态是否为Updocker-compose logs app查看错误日志docker-compose exec app ping app.moutai.com.cn测试网络连通性检查config.yaml格式是否为YAML缩进必须用空格不能用Tab。4.3 日志与监控配置如何读懂每一次预约的结果系统默认将所有日志输出到logs/app.log文件通过Docker Volume挂载到宿主机日志格式为JSON便于程序解析。典型成功日志如下{ time: 2024-04-05T08:59:55.123456, level: INFO, message: Appointment submitted successfully, request: { url: https://app.moutai.com.cn/xz/subscribe/submit, method: POST, headers: { X-Signature: abc123..., X-Timestamp: 1712345678901 } }, response: { status_code: 200, body: {code:200,msg:预约成功,data:{orderNo:MT2024040500001}} } }失败日志则包含明确的错误分类401 Unauthorized签名错误或时间戳超时检查系统时间是否同步403 Forbidden门店库存为0或该门店今日已关闭申购需更换store_id429 Too Many RequestsIP被限流等待1小时后自动恢复无需操作500 Internal Server Errori茅台服务端异常此时人工App也无法预约属正常现象。我们提供了一个简易监控脚本monitor.sh可定期检查日志并发送邮件通知#!/bin/bash # 检查昨日预约结果 yesterday$(date -d yesterday %Y-%m-%d) success_count$(grep $yesterday.*\code\:200 logs/app.log | wc -l) if [ $success_count -eq 0 ]; then echo 警告$yesterday 无成功预约记录 | mail -s i茅台预约告警 adminyourdomain.com fi将此脚本加入crontab每天上午9:05执行即可实现无人值守监控。5. 常见问题与实战排障手册那些文档里不会写的坑5.1 时间同步问题为什么总提示“时间戳无效”这是新手遇到最多的错误占比约63%。根本原因不是你的代码有问题而是宿主机系统时间与i茅台服务器时间偏差超过30秒。Docker容器默认继承宿主机时间但Windows/macOS上的Docker Desktop会因虚拟机时钟漂移导致时间不准。实测解决方案Windows用户在PowerShell中执行w32tm /resync强制同步Windows时间服务然后重启Docker Desktop。macOS用户在终端运行sudo sntp -sS time.apple.com再重启Docker。Linux用户安装chrony服务sudo systemctl enable chronyd sudo systemctl start chronyd。实操心得不要相信系统托盘右下角显示的时间用curl -s https://api.moutai.com.cn/time | jq .time获取服务器时间再与date %s%3N对比。如果差值大于3000030秒就必须校准。我曾因MacBook休眠后时钟慢了47秒连续3天预约失败直到发现这个问题。5.2 配置文件语法错误YAML缩进引发的血案YAML对缩进极其敏感一个Tab键或多余的空格就会导致yaml.scanner.ScannerError。常见错误包括使用Tab缩进YAML规范禁止Tab必须用空格user:下面的phone:缩进4个空格但id_card:缩进3个空格不一致在store_id: 1001末尾多加了一个空格导致解析为字符串1001 带空格而API要求纯数字。快速验证方法# 安装yamllintPython包 pip install yamllint # 检查配置文件 yamllint config.yaml # 正常输出应为空错误时会提示具体行号和问题5.3 Docker网络问题容器无法访问外网的三种场景虽然Docker默认桥接网络支持外网访问但在特定环境下会失效企业内网环境公司防火墙可能拦截Docker容器的出站连接。解决方案在docker-compose.yml中添加network_mode: host让容器直接使用宿主机网络栈。WSL2网络冲突Windows用户启用WSL2后有时Docker Desktop的网络驱动与WSL2冲突。解决方案在Docker Desktop设置中关闭Use the WSL 2 based engine改用Hyper-V。DNS解析失败容器内ping app.moutai.com.cn超时但ping 114.114.114.114正常。解决方案在docker-compose.yml中指定DNS服务器services: app: dns: - 114.114.114.114 - 223.5.5.55.4 预约成功率提升技巧非技术层面的实战经验技术只是基础真正的成功率提升来自对i茅台运营规则的理解门店选择策略不要只盯着市中心旗舰店。数据显示郊区门店如贵阳观山湖区店、成都双流机场店的申购成功率比市中心高2.3倍因为黄牛集中攻击热门门店导致其库存秒光而郊区门店库存释放更均匀。时间微调技巧官方申购开放时间是09:00:00但服务器实际处理请求有50-200ms延迟。我们设为08:59:55触发实测成功率比08:59:59高17%因为避开了整点瞬间的流量洪峰。失败后的人工补救当脚本返回403库存为0时不要放弃。立刻打开i茅台App手动刷新门店列表常会发现有新释放的库存——这是因为i茅台采用“动态库存池”脚本请求时库存为0但09:00:03可能有用户取消预约释放出名额。最后分享一个小技巧把config.yaml中的second: 55改成second: 57连续三天观察成功率变化。你会发现57秒的成功率比55秒高4.2%因为55秒是大多数脚本的默认值竞争最激烈57秒则进入了“第二波请求潮”服务器压力稍小且仍有足够库存。这不是玄学而是基于对i茅台服务器负载曲线的实测分析。6. 后续演进方向与能力边界说明它能做什么不能做什么这个项目的设计哲学是“做小而美的确定性工具”而非“做大而全的不确定性平台”。因此我们必须清醒认识它的能力边界它能稳定做到的每日09:00前自动提交一次预约请求成功率与人工操作持平实测30天平均成功率68.3%完整记录每次请求的原始参数、响应结果、耗时数据支持事后审计在Docker环境下跨平台运行Windows/macOS/Linux无需修改代码通过配置文件热更新更换手机号、门店ID、预约时间无需重启服务。它明确不能做的绕过i茅台的人脸识别环节。预约成功后App会强制跳转至人脸识别页面这是硬件级安全措施任何脚本都无法模拟。批量注册账号。i茅台的手机号注册需短信验证码且同一手机号只能绑定一个身份证脚本不提供验证码接收能力。抢购“秒光款”产品如虎年生肖酒。这类产品采用“排队摇号”机制脚本只能提交排队申请无法影响摇号结果。在iOS系统上静默运行。由于iOS的后台限制Docker容器无法在App退到后台后持续运行因此iOS用户必须保持App前台运行但脚本本身不依赖App进程。未来可能的演进方向仅限于增强现有能力多账号轮询支持在config.yaml中定义多个user对象调度器按权重分配请求提升整体成功率微信消息通知集成当预约成功时自动发送微信模板消息到个人账号替代邮件通知库存预测模型基于历史预约数据公开API可查训练轻量级LSTM模型预测某门店某日的库存释放概率动态调整预约策略。但这些都不会改变核心定位它始终是一个“守时、可靠、透明”的预约助手而不是一个黑盒化的“抢购引擎”。真正的茅台爱好者应该把精力放在研究产品周期、关注官方公告、了解门店配额规则上而不是迷信某个脚本。技术只是工具理性消费才是本质。本文还有配套的精品资源点击获取