简介:针对茅台每日预约需要手动盯守的痛点,这套以Java和Vue为核心开发的自动预约工具,能够让用户通过Docker快速部署到任意支持容器化的服务器或本地环境,尤其适合具备基础开发知识、希望稳定参与i茅台预约的消费者。压缩包共包含542个文件,整体仅2.99MB,其中Java工程承载了登录模拟、预约接口调度、定时任务等核心逻辑,Vue相关文件负责后台管理界面,JS、XML、SCSS等辅助资源则用于完善前端交互与配置解析,Docker与YAML等配置文件进一步简化了不同运行环境的适配。当前已有1484人学习下载,足以说明此类自动化预约需求在消费群体中受到持续关注。借助包内的构建脚本与启动配置,用户可以快速启动项目,并根据自身预约时段灵活调整参数;同时,清晰的前后端分离结构也能作为自动化预约与容器化部署项目的工程参考。需要留意的是,自动化预约若与平台使用条款冲突,部署前应自行评估合规风险。
1. 把i茅台自动预约跑起来:真正难的不是脚本,是让它稳定跑上一周
每天早上一睁眼先打开i茅台App点进申购页,已经是不少人的习惯。出差、开会、睡过头,只要错过一次,当天的申购机会就没了。社区里的"i茅台app自动预约"脚本因此流行起来:用代码模拟App的请求链路,到点自动提交预约,再用Docker把环境打包成一条命令就能部署。但它最反直觉的地方在于:第一次启动半小时就能成功,真正麻烦的是token过期、时区偏移、风控拦截这些后续问题。这篇笔记把这套方案的原理、配置、Docker部署和排错顺序讲清楚,适合两类人:想省心的普通用户,以及想把预约类脚本容器化的开发者。
2. 自动预约的原理拆解:四个接口、一个定时器、一层容器
2.1 预约的本质:模拟App的HTTP请求链路
i茅台App的预约动作,表面上是点一下按钮,底层是客户端向后端服务发起的一组HTTP请求。自动预约脚本做的就是把这组请求用代码还原:登录态怎么带、商品列表从哪个接口拿、门店怎么匹配、最后用哪个接口提交预约。理解这条链路,后面所有配置项的来源就清楚了。
四个关键节点按顺序来。第一步是登录态:脚本需要拿到当前账号的有效凭证,常见是一串token或sessionId,后续所有请求都带着它。第二步是拉取当日可申购商品列表,不同省份、不同门店的投放计划不一样,接口返回数据里会带上商品编码、价格和库存状态。第三步是确定申购门店,脚本一般从配置里读取预约门店的代号,替换到请求参数里。第四步才是提交预约,这一步会校验登录态、设备和时间,也是风控最容易卡住的地方。
这套接口信息从哪来?社区的常见路径是抓包。手机装好Charles或mitmproxy,开启HTTPS代理,在App里完整走一遍登录和预约流程,就能看到请求的URL、参数和返回结构。把关键接口整理成文档,再写进脚本,这就是一个预约脚本的最小原型。i茅台app逆向是另一条路径,直接分析安装包里的接口定义和加密逻辑,能拿到更稳定的参数生成方式,但门槛高不少,普通用户不建议碰。
# 伪代码示意:预约一次申购的请求顺序 login_token = load_token_from_config() # 从配置文件读登录凭证 product_list = api_get_products(login_token) # 拉取当日可申购商品列表 target = pick_target(product_list) # 按配置筛选目标商品和门店 submit_result = api_submit_reserve(login_token, target) # 提交预约这段伪代码对应脚本主流程的骨架。api_get_products和api_submit_reserve在真实项目里是具体接口封装,参数名略有差异,但结构一致。登录态字段的时效性通常只有几小时到几天,这个问题后面避坑章节会专门展开。
2.2 定时任务的设计:提前量、重试与并发
接口链路解决的是"怎么约",定时器解决的是"什么时候约"。i茅台的申购一般有固定的放量时间窗口,常见做法是每天早上某个具体时刻开放当日预约。脚本的做法是:开放时间前提前1到5分钟启动程序,先做登录态刷新和商品列表缓存,等时间窗口一到立刻提交预约。
提前量这个设计容易被忽略。临近开放时刻,接口响应会变慢,如果脚本卡在"拉商品列表"这一步,等列表返回再提交预约,窗口可能已经过去好几秒。提前几分钟把不依赖实时状态的请求做完,把时间窗口内的时间全部留给真正关键的提交动作,这是预约类脚本的通用优化思路。
# 定时任务核心逻辑(示意) schedule_time = read_config("reserve_time") # 比如 09:00:00 while now < schedule_time - 5 * 60: sleep(10) refresh_login_state() # 提前刷新登录态 product_list = fetch_product_list() # 提前缓存商品列表 for attempt in range(3): # 最多重试3次 result = submit_reserve() if result.success: notify_user("预约成功") break sleep(5)这段逻辑里两个参数值得注意:schedule_time控制每天的执行时刻,重试次数3次控制失败后的补偿行为。重试不是越多越好,提交预约这类接口,重试太激进容易触发限流。另外,预约结果的通知也在这里挂上——预约成功或失败时推送webhook通知,人不盯着日志也能第一时间知道结果。
2.3 为什么用Docker包一层:依赖、时区与可移植性
很多人拿到zip包之前,会先想直接在自己电脑上跑Python脚本。这个思路没毛病,但会遇到三个麻烦:一是依赖库版本不一致,别人能跑你跑不起来;二是定时任务依赖系统cron或计划任务,Windows和Linux配置方式完全不同;三是时区问题,脚本按北京时间定时,而容器默认UTC,差8个小时预约就废了。
Docker把这三件事一次性解决。镜像里固定好Python版本、第三方库和时区设置,compose文件里写好环境变量和挂载目录,无论宿主机是Windows还是Linux,启动命令完全一样。这就是"一键部署"的意义:部署成本从配环境半小时变成跑一条命令。镜像的构建方式分两种:项目自带Dockerfile就本地docker compose build,不带就直接拉作者构建好的镜像。拿到zip包后先看有没有Dockerfile,有就本地构建,没有就拉现成镜像。
# docker-compose.yml 的时区与环境变量片段 services: imoutai: environment: - TZ=Asia/Shanghai # 容器内时区设为北京时间 - LOG_LEVEL=info时区配置是预约类容器最容易翻车的点。TZ=Asia/Shanghai必须显式声明,否则容器内时间是UTC,定时任务会晚8小时触发,结果就是每天预约都错过窗口。基础镜像里缺tzdata也会导致时区设置不生效,这种隐蔽问题一般要等第一次预约失败才会暴露。
3. Docker一键部署:把预约脚本跑起来的最小操作
3.1 准备Docker环境与项目包
动手第一步是确认本机Docker能跑。Windows用户装Docker Desktop,装好后注意两个点:BIOS里要打开虚拟化(Intel VT-x或AMD-V),不然启动时会报"virtualization support not detected";启动Docker Desktop后确认右下角图标是运行状态,别光看界面。Linux用户装好docker.io或docker-ce,再把当前用户加进docker组,否则每条命令都要sudo。
# 验证Docker是否就绪 docker version docker compose version这两条命令缺一不可。docker version确认守护进程在跑,docker compose version确认Compose插件可用。老版本Docker需要单独装docker-compose,新版本直接内置,命令是docker compose,中间没有横杠。国内拉取镜像慢也是常见问题,如果docker pull超时,检查/etc/docker/daemon.json里有没有配置国内镜像加速地址,改完重启Docker服务生效,属于Docker基础操作,遇到了再处理。
拿到zip包后,先解压到固定目录,比如~/imoutai/。目录里一般会有这几个东西:docker-compose.yml、config目录或配置文件、Dockerfile、README或启动说明。先花五分钟把README读完,确认默认配置文件的格式,再往后操作。这一步看着多余,实际能省掉后面不少查错时间。
# 解压并查看目录结构 unzip imoutai_auto_reserve.zip -d ~/imoutai cd ~/imoutai ls -la常见结构里,config.yaml或config.json是唯一需要手动编辑的文件,其他文件基本不用动。如果目录里没有配置文件,只有.env.example,就复制一份再改,改.env即可。文件夹的路径记好,后面挂载卷和查看日志都会用到。
3.2 修改配置并启动容器
启动之前,配置文件里至少三样东西必须填:账号的登录凭证、预约门店、申购时间。字段名称因项目而异,常见叫法是token、shopId或storeCode、reserveTime。先打开配置文件,把这几项改成自己的,其他参数保持默认。改完文件后,执行启动命令。
# 构建镜像并后台启动容器 docker compose build docker compose up -ddocker compose build的作用是本地构建镜像,只有项目带Dockerfile时才需要;如果配置里直接指定了镜像名,这一步可以跳过。up -d是后台启动,-d表示detached,终端不会被日志刷屏。启动完成后用docker ps看容器状态,STATUS列应该显示Up,而不是Exited或Restarting。
# 查看容器状态和实时日志 docker ps docker logs -f imoutailogs -f中的-f是follow,持续输出新日志。首次启动重点看两行:程序是否成功加载配置文件,定时任务是否注册成功。看到类似"scheduler started"或"next run at 09:00"的输出,基本说明容器内部一切正常。如果日志里出现ModuleNotFoundError或ConfigParseError,多半是配置格式问题,回到配置文件检查。
3.3 启动后怎么确认预约任务真的在跑
容器Up不代表定时任务在跑,这两件事要分开确认。最简单的方法是把容器里的时间打出来,和北京时间对比。
# 进入容器检查时区与时间 docker exec -it imoutai date输出UTC时间说明TZ环境变量没生效,预约时间会整体偏移8小时。另一个确认思路是看日志里的下一次执行时间,大多数脚本会打印"scheduled next run: 2026-01-01 09:00:00 +0800"。看到时间和预期一致,这步才算稳。最后,部分项目提供dry-run或now模式,手动触发一次任务看结果。如果项目支持,调一次是最直接的验证,不用等第二天。
4. 核心配置参数:预约前必须确认的字段与推荐值
4.1 用户身份信息:token、deviceId与用户ID
配置文件里最关键的一块是身份信息。大多数i茅台自动预约项目不直接存账号密码,而是要求填入登录后拿到的token或sessionId。原因很简单:脚本不需要密码,密码登录涉及验证码和风控,复杂度太高。token的获取方式一般写在README里,常见路径是抓包App登录请求的返回体,或者项目自带辅助脚本输出。
# config.yaml 身份信息片段 user: token: "eyJhbGciOi..." # App登录后抓包获取 device_id: "a1b2c3d4" # 抓包或按规则生成的设备标识 user_id: "10xxxxx" # 账号对应的用户IDtoken字段不用多解释,字符串原样粘贴即可。device_id值得注意:部分版本会把设备ID参与请求签名,随意改会导致校验不通过。user_id有些项目不需要,有些项目在预约接口里必须携带,以项目文档为准。这三个字段每次重新登录App后可能更新,所以配置一次不代表永久有效。失效后会表现为预约接口返回401或同类错误码,与网络异常有明显区别。
4.2 预约策略参数:商品、门店与申购时间
身份信息解决"你是谁",策略参数解决"约什么、在哪约、几点约"。i茅台的申购规则是按门店分配库存,脚本必须明确指定一个门店才会去抢。门店在配置里通常不是中文名,而是一串门店编码,这个编码可以从抓包结果、或者App的门店列表接口里查到。
# config.yaml 预约策略片段 reserve: shop_id: "5201" # 门店编码,从门店列表接口获取 product_ids: - "10953" - "10959" reserve_time: "09:00:00" # 每日申购开放时间product_ids是商品编码列表,脚本会按顺序尝试预约列表里的商品。reserve_time是每天执行预约的时刻,注意必须和App里显示的本地区申购时间一致,不同地区、不同投放轮次的开放时间可能不一样。填错了就算脚本正常跑,预约也会因为时间不对被拒。
4.3 通知与运行参数:消息推送、日志级别与重试次数
第三类参数不影响预约本身,但影响使用体验和排查效率。通知参数是一组webhook地址,预约成功或失败时推送消息到手机。日志级别控制输出详细程度,调试时设debug,正常跑设info。重试次数前面提过,3到5次足够,别往高了调。
# config.yaml 通知与运行参数片段 notify: webhook: "https://sctapi.ftqq.com/xxx.send" # Server酱 或自建Webhook log: level: "info" # 排查问题时改为 debug retry: max_attempts: 3 # 提交预约的失败重试次数 interval_seconds: 5 # 两次重试的间隔时长webhook建议配置,否则脚本跑没跑、约没约上,完全依赖手动查。retry的三个参数保持默认就够用:重试太频繁容易触发接口限流,间隔太长又会错过时间窗口。debug日志平时不推荐开,日志文件膨胀速度会超出预期。
配置参数整体看下来,四类信息汇总如下。
| 参数 | 样例值 | 说明 | 常见失效场景 |
|---|---|---|---|
| user.token | eyJ... | App登录后的身份凭证 | 重新登录或被踢下线后失效 |
| user.device_id | a1b2c3d4 | 设备标识,参与签名 | 多账号共用时触发风控 |
| reserve.shop_id | 5201 | 预约门店编码 | 门店下架或区域调整 |
| reserve.reserve_time | 09:00:00 | 申购开放时间 | 平台调整投放规则 |
| notify.webhook | https://... | 推送通知地址 | 服务商令牌过期 |
配置文件改完,先做静态校验。常见做法是执行python -c "import yaml; yaml.safe_load(open('config.yaml'))"确认YAML格式没问题,或者运行项目自带的config check命令。格式错了,程序启动时报错和实际字段无关,容易把排查方向带偏。
5. 避坑指南:自动预约脚本的五个常见坑与排查思路
5.1 token过期导致的静默失败
现象:容器一直正常启动,日志里没有报错,但每天预约结果都是失败,接口返回的是登录态失效的错误码。
原因:token从抓包到写入配置,再到容器实际发起请求,中间隔了一段时间。App端登录态有过期时间,用户换设备或者重新登录也会让旧token失效。脚本此时不会主动提醒,只是请求被打回。
解决:重新抓包获取token,更新配置文件,重启容器(docker compose restart imoutai)。如果项目实现了登录态自动刷新,检查配置文件里是否漏填了refresh_token字段。另外,Windows下复制token容易带上不可见字符或换行,粘贴后建议用文本编辑器打开config文件确认没有多余空格。
5.2 容器时区不对,预约永远晚8小时
现象:容器正常运行,日志显示的下次执行时间是前一天下午,或者干脆没动静,预约从未成功。
原因:Docker容器默认UTC时区,比北京时间晚8小时。脚本如果依赖定时器,就会整体偏移。
解决:在compose文件的environment里显式设置TZ=Asia/Shanghai,重启容器。启动后docker exec imoutai date验证输出是北京时间。还不行就看看基础镜像是否包含tzdata,没有的话在Dockerfile里加一行安装tzdata,改完重新build。
提示:时区问题可以用一条命令验证,不用等第二天看结果。
5.3 风控拦截:请求像机器人,服务端不回应
现象:同一个账号连续几天预约失败,错误码不是登录态问题,而是提示频繁或需要验证。或者本地脚本调试一切正常,部署到服务器跑第二天就被限制。
原因:预约接口对请求频率和设备特征有校验。脚本从固定IP高频请求、缺失设备指纹参数、同一个device_id被多个账号复用,都会触发风控。
解决:不要用同一个device_id跑多个账号;重试间隔不要低于5秒;尽量保持与真实App一致的请求头顺序。这些都是社区项目里反复验证过的实际经验。目前没有根治方案,平台风控规则是黑匣子,能做的只有降低触发概率,以及准备好手动补预约的预案。
5.4 Docker启动失败:虚拟化未开启或网络异常
现象:Windows上Docker Desktop启动时报"virtualization support not detected";Linux上docker ps正常,但容器报端口占用、网络不通或目录挂载失败。
原因:前者是BIOS虚拟化没开启,或Windows的Hyper-V/WSL2组件没装全。后者一般是compose文件里配置了固定端口,而宿主机端口已被占用;或者挂载目录在宿主机上不存在,Docker自动创建了空目录导致配置读取不到。
解决:Windows去BIOS打开VT-x/AMD-V,重启后再启动Docker Desktop。端口冲突就改compose里的端口映射。目录挂载问题则在宿主机上建好config和logs目录,再把配置文件放进去。容器起不来时看docker logs容器名,报错里会明确写是权限、网络还是配置解析问题,按提示逐项排除。
5.5 重复预约:补偿请求导致的重复提交
现象:某天日志显示预约成功,但用户后来发现自己有两条预约记录,或者被提示不可重复预约。
原因:重试逻辑设计不严谨。第一次请求实际已经成功,但响应超时,脚本认为失败并重试,服务端没有做幂等校验,就产生了重复记录。
解决:把重试次数适当调小,同时确认脚本是否在提交前检查了"当日已预约"状态。部分新版脚本启动时会读取预约记录接口,发现已预约就直接跳过。没这个功能的,靠减少重试次数来降低概率。这个坑比较隐蔽,一般要翻日志对时间戳才能发现。
6. 进阶玩法:多账户并行、日志告警和预约结果的反向确认
6.1 多账户并行:从配置层面扩展
家人、朋友都想要,一台机器只跑一个账号就浪费了。多数项目支持在配置文件里写多个账号。
# config.yaml 多用户片段 users: - token: "eyJ..." device_id: "a1b2c3d4" shop_id: "5201" - token: "eyJ..." device_id: "e5f6g7h8" shop_id: "5202"改动要点:每个账号的device_id必须独立,token不能混用。脚本会逐个串行预约,这是有意的设计——串行比并行的稳定性高,单个账号的失败不会影响其他账号。
6.2 日志告警:盯任务的两种可靠方式
第一种是配置消息推送,预约成功失败都推webhook。第二种是定期看日志。每天固定时间看一眼docker logs的输出,确认success关键字出现。也可以用一条简单的grep命令快速过滤:
# 过滤预约结果关键词 docker logs imoutai --since 24h | grep -E "success|fail|error"这条命令只看最近24小时的关键结果。如果没有success也没有fail,说明脚本可能压根没触发,问题大概率出在定时任务或时区上。
6.3 反向确认:还是相信官方App
脚本再稳定,最后也应该以官方App的记录为准。日常习惯是登录i茅台App,进申购记录页,核对当天是否生成预约单。这一步是对整个链路最直接的验证,比任何日志都可靠。
我自己的习惯是每周五手动核对一次本周预约记录,确认脚本和官方App一致,顺手更新一次token。这套自动预约在稳定跑了两周、连续预约成功之后,才敢把每天的精力从这件事上挪开。希望这些细节能帮到你,少在时区、token、风控这些地方浪费一周时间。
本文还有配套的精品资源,点击获取