ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

微信公众号模板消息推送实战:用Spring Boot实现每日天气提醒全攻略

2026/9/9 21:35:11 拓冰建站 浏览量
微信公众号模板消息推送实战:用Spring Boot实现每日天气提醒全攻略 简介一份面向有一定编程基础读者的微信公众号天气推送制作教程专注于解决如何定时向女朋友推送天气、纪念日等关怀消息。资源基于GitHub每日自动运行方案无需本地长期挂机申请天行或和风API后即可部署适合用来给在意的人做一份用心小惊喜。压缩包共5个文件整体仅5KB主要包含Python主程序、GitHub Actions的YML工作流配置、TXT/MD说明文档及消息模板结构精简清晰方便直接对照修改。内容整合了最新版、简化版、单独星座版、星座天气版以及PHP版共5个版本既有成品也有源码读者可按自己的编程熟练度灵活选用。目前已有7684人浏览学习资源提供了完整的消息模板与多版本成品实现可以快速搭建公众号每日自动推送服务。结合作者亲测有效的流程能大幅降低配置门槛让温馨提醒自动化送达。 每天早上7点35分我手机的消息列表里会准时出现一条来自微信公众号的推送标题是“今日天气”下面是北京今天的气温、风力和一句“记得带伞”。收件人是我女朋友发送方是我用个人名义注册的测试公众号。这个项目做下来其实不到200行核心代码但它已经稳定跑了快一年每天一条风雨无阻。这篇文章就是完整复现这个项目的制作教程。我尽量把从思路、选型到代码、部署的全过程都写清楚包括那些你第一次做肯定会踩的坑——access_token的处理、模板消息的格式、定时任务的时间差这些细节卡住一个人的时间往往比写代码本身还长。不管你最终是为了给谁发天气看完这篇你都能搭出一条稳定的微信公众号消息推送链路。1. 项目思路怎么选型最省事1.1 为什么是微信公众号模板消息先说选型过程。最早想过直接用短信接口但一条短信一两分钱三个月下来也能喝几杯奶茶而且短信的到达时间不稳定、内容格式也受限也考虑过钉钉机器人或者企业微信群机器人那个更简单甚至可以不用服务器但问题是——不是每个人都会天天盯着钉钉看。微信是所有人每天打开次数最多的App把天气推到微信聊天列表里触达率是最高的。所以最终锁定在微信公众号上。公众号里面又有服务号和订阅号的区别订阅号没有模板消息权限普通个人服务号认证又很麻烦。这里有个务实的选择用微信官方提供的“测试号”来开发它不需要注册公司、不需要认证、不需要审核扫码登录就能拿到一套完整的接口权限包括模板消息。测试号唯一的限制就是粉丝数量不能超过100人但对于“推送给自己或家人”这种场景完全够用。1.2 整条推送链路是怎么串起来的整个系统的数据流其实非常直白拆开就是四步定时任务先触发然后程序带着自己的身份凭证appID appsecret向微信接口换取access_token拿着token去天气服务商那里查天气把天气结果组装成符合模板要求的JSON最后把JSON POST回微信服务器微信负责把消息塞到订阅者的聊天列表里。定时触发 - 获取access_token - 请求天气API - 组装模板消息 - 调用微信模板消息接口 - 送达用户这一步的骨架就是这样但真正动手时你会发现access_token是整个链路的通行证所有微信接口都要带它而且它2小时过期、接口有调用次数限制所以不能每次现取现用要做缓存。这一点后面代码部分会细讲。模板消息本身也值得说两句它不同于网页里的对话消息是公众号特有的“服务通知”形态顶部有公众号名称标记不会被折叠用户点开就能看到完整信息。微信对模板消息有严格的格式规范字段必须一一对应写错一个key接口就报错。很多新手好不容易调通接口之后的第一个大bug就出在这里后面我会给出完整对照。2. 前置准备测试号、天气API与开发环境2.1 申请微信公众号测试号访问微信公众平台的测试号申请页面mp.weixin.qq.com/debug/cgi-bin/sandbox用微信扫码登录即可整个过程两分钟。登录后页面会直接显示两行关键信息appID和appsecret这两串字符串就是以后程序访问微信接口时的账号密码务必保存好。页面下方有一块“测试号信息”这里能配置接口权限。模板消息需要在这里添加测试模板点击“新增测试模板”自己填标题比如“每日天气提醒”和模板内容模板内容里用{{xxx.DATA}}占位例如这样天气{{weather.DATA}} 温度{{temp.DATA}} 风力{{wind.DATA}} 提醒{{tips.DATA}}保存后系统会生成一个模板ID。这套测试号几乎等于一个完整的公众号后台不需要你再去补域名、服务器配置验证开发起来非常顺。如果你想用正式服务号那就得走模板库申请、IP白名单配置等流程测试号把这些前置成本直接砍掉了。2.2 天气数据源选型对比天气数据源决定了一条推送内容好不好看、代码好不好写。国内常见的免费方案主要有三个我这里直接给结论性的对比服务商免费额度请求方式返回格式备注高德开放平台个人开发者免费GET请求无需签名JSON字段齐全申请Key流程简单一次通过和风天气有免费开发版配额GET请求需要KeyJSON字段丰富需实名认证新用户审核略慢心知天气免费试用量有限GET请求JSON免费版字段较精简我实际项目里用的是高德开放平台。原因很简单注册后在控制台创建一个应用几分钟就能拿到Key天气接口地址固定带着城市编码和Key直接GET返回的标准JSON里包含天气现象、温度、湿度、风向风力。对一次推送来说这个信息量已经足够了。如果你需要更精细的生活指数穿衣、紫外线、运动建议可以再叠加和风天气的生活指数接口但免费版需要额外申请配额综合性价比不高。高德天气分实时天气和预报天气两种extensionsbase是预报接口extensionsall是实时接口。推送里我建议用base因为它能给出今天和未来三天的预报数据适合做“今天的天气”和“明天要不要带伞”的判断。2.3 开发环境搭建我这边选的开发栈是Java Spring Boot 2.7 MavenJDK 1.8以上都可以。选择Spring Boot不是因为它在微信开发上有优势而是定时任务、HTTP请求、打包部署这些基础设施它都内置好了写起来省心。如果你想用Python完全也可以思路一模一样只是把请求换成requests库定时任务换成APScheduler。自己熟哪个用哪个不用被语言绑架。建工程的时候只需要引入两个起步依赖dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter/artifactId /dependencystarter-web是为了在项目里直接使用RestTemplate发HTTP请求也可以换成WebClientstarter就是Spring Boot的基础包定时任务的注解也在这个依赖里。一个基础的Spring Boot工程目录建好大概也就十几秒接着就可以写核心代码了。3. 核心代码思路与实操实现3.1 换取并缓存access_token微信接口的调用凭证access_token有效期2小时获取接口是GET请求一次请求就能拿到。这个接口每天是有调用次数限制的虽然测试号没有硬性触发但正式应用场景下频繁请求会导致后续请求被微信拒绝。所以代码里我会用一个静态变量存token再记录一下获取时间超过7000秒就重新拉取。public class WeChatService { private String accessToken; private long tokenExpireTime; public String getAccessToken() { if (accessToken ! null System.currentTimeMillis() tokenExpireTime) { return accessToken; } String url https://api.weixin.qq.com/cgi-bin/token ?grant_typeclient_credential appid appId secret appSecret; String response restTemplate.getForObject(url, String.class); JSONObject json JSON.parseObject(response); accessToken json.getString(access_token); tokenExpireTime System.currentTimeMillis() (json.getLong(expires_in) - 200) * 1000; return accessToken; } }这里多减了200秒是为了避免token在临界点失效。注意在真实项目中不要把token只存在内存里多实例部署时共享token会互相覆盖可考虑用Redis。个人项目单机跑的话内存缓存完全够用没必要引入额外组件。3.2 调用天气接口组装推送数据拿到token之后就可以去查天气了高德天气接口格式如下GET https://restapi.amap.com/v3/weather/weatherInfo ?city110101 key你的高德Key extensionsbase这里的city比较有迷惑性它不是北京拼音而是城市的adcode行政区划编码北京城区是110101上海是310100。写死在配置里一行就能搞定。响应JSON大概是这个样子{ status: 1, count: 1, forecasts: [ { city: 北京市, adcode: 110101, casts: [ { date: 2025-06-15, dayweather: 晴, nightweather: 多云, daytemp: 33, nighttemp: 22, daywind: 南, daypower: 3 } ] } ] }我一般取第一天的dayweather作为当天主要天气nighttemp作为最低温daytemp作为最高温。需要注意接口返回的全是字符串拼温度文案时别忘记带上摄氏度符号。如果返回的status不是1那就说明请求出错了代码里要做异常兜底比如读取上一天缓存的数据或者推送一条“今天天气接口暂时罢工”的文案总比什么都不发强。3.3 文案模板与贴心细节消息内容本身也要讲究。纯推送“晴 22℃~33℃”太干用户两秒扫完就关了。我自己的文案模板是这样的今日天气北京 天气晴转多云 温度22℃~33℃ 风力南风3级 提醒温度较高注意防晒补水早晚温差大建议带一件薄外套。提醒这句是重点也是最体现用心的地方。比如温度差超过10度就提示“早晚温差大注意添衣”天气预报有雨就提示“记得带伞雨天路滑”气温超过30度就提示“天气炎热多喝水”气温低于5度就提示“注意保暖穿厚外套”。这些判断逻辑写在一个工具类里每次按照天气预报动态组装代码不复杂但效果完全不同。如果你愿意还可以在文末加一句“早安今天也要元气满满哦”按自己喜好来就好。下面给出组装消息的核心代码String templateMsg 今日天气 cityName \n 天气 dayweather \n 温度 nighttemp ℃~ daytemp ℃\n 风力 wind 风 power 级\n 提醒 buildTips(dayweather, daytemp, nighttemp);buildTips方法根据气象和温度动态生成提醒内容先判断雨雪再判断温差最后判断高温低温顺序不能反。因为如果下雨和高温同时出现雨的表达优先级更高。3.4 模板消息的格式细节与发送模板消息发送接口是POST请求带了access_token参数请求体是标准JSON。这里最容易翻车的点是模板里的字段名模板里写成{{weather.DATA}}发送时data部分的key也必须是weather一个字母都不能差String sendUrl https://api.weixin.qq.com/cgi-bin/message/template/send?access_token token; MapString, Object req new HashMap(); req.put(touser, openid); req.put(template_id, templateId); MapString, MapString, String data new HashMap(); MapString, String weatherField new HashMap(); weatherField.put(value, weatherText); data.put(weather, weatherField); MapString, String tempField new HashMap(); tempField.put(value, tempText); data.put(temp, tempField); // wind、tips 同理核心就是value字段 req.put(data, data); String response restTemplate.postForObject(sendUrl, JSON.toJSONString(req), String.class);如果你的JDK在9以上用Map.of(value, weatherText)会更简洁但为了兼容老项目我在示例里写的是传统HashMap方式。发送成功后微信会返回一个JSON里边的errcode为0就表示成功了。如果返回非0就直接把errmsg打出来看80%的问题在这个响应里就能找到答案。这里有个关键细节openid从哪里来测试号页面底部有个“测试号二维码”微信扫码关注之后页面上会出现该用户的微信号和openid列表。推送时touser字段填的就是这个openid而不是对方的微信号。如果你打算推给多个人就维护一张openid和城市、人名的映射表定时任务里遍历发送即可。3.5 定时任务与整套跑通最后给Spring Boot加上定时任务EnableScheduling public class ScheduleConfig { } Component public class WeatherPushJob { Scheduled(cron 0 30 7 * * ?) public void push() { weatherService.pushDailyWeather(); } }cron表达式“0 30 7 * * ?”的意思是每天7点30分整触发。也可以改成每工作日推、节假日不推、或者每天不同时间推。要注意Spring的定时任务默认是单线程执行如果将来任务多了稍加配置改线程池即可。任务跑完之后最好自己打印一行日志方便确认是否执行成功。部署就没什么可说的了打包成jar放在一台能访问公网的服务器上nohup java -jar weather-push.jar 启动。如果你没有云服务器用一些支持定时触发的云函数服务也能跑核心代码不动只是把入口换成函数触发方式。4. 常见问题与避坑指南4.1 对接微信接口时的报错排查我从提交到最终稳定运行中间遇到过的错误都集中在几个老问题上。下面是我的排查对照表错误码错误信息原因与解决办法40001invalid credentialaccess_token无效或过期检查appid、secrettoken是否被缓存覆盖40037invalid template_id模板ID填错或模板与当前公众号不匹配43004require subscribeopenid未关注当前公众号先扫码关注测试号45009api minute-quota reach limit接口调用超频检查上方是否在循环里反复获取token47003argument invalid模板字段不匹配data里的key和模板占位符对不上遇到问题建议先看返回的errmsg而不是瞎改代码。微信的错误码不算多查一下官方错误码文档基本都能定位。4.2 我踩过的三个“隐形坑”第一个坑在时间上。Spring Boot默认使用服务器本地时区如果服务器时区是UTCScheduled的7点30分会跑成北京时间15点30分。所以部署前检查一下服务器时间JVM启动参数里加上-Duser.timezoneAsia/Shanghai或者直接用整机时区校准这事儿能省掉你一天的白等。第二个坑在天气数据上。高德免费接口偶尔会返回status为1但casts数组为空这种异常不会在JSON解析时报错而是得通过空集合判断兜底。我最初没做这个判断结果某天推送直接成了“天气null”推送内容特别难看后来在取数据前加了非空校验才解决。第三个坑在内容转义上。如果你在提醒文案里带了特殊字符模板消息对中英文符号没有额外限制但高德接口返回的温度字段是“22”这种数字字符串拼进JSON时一定要用规范的JSON库去序列化不要自己手拼字符串。你手拼出来的JSON一旦少一个双引号接口返回的就是parse error排查起来非常浪费时间。4.3 怎么让推送更贴心、更耐看这个项目跑了一段时间之后我开始加一些不太费力的优化。比如每周五额外推一条未来三天的预告用的还是同一个模板只不过内容从当日变成了三日摘要再比如遇到节假日提醒文案会换上对应的开场白。这些通用的做法有一个共同前提——你的发送通道足够稳定。只要access_token缓存、天气接口兜底、字段校验这几关过了剩下的就全是创意问题。另外记得给推送设计失败重试。模板消息发不出去时微信一般会返回明确错误但偶尔网络抖动会导致请求超时这时候我建议对发送动作做一次简单重试间隔30秒以上避免连续打接口触发限流。这个项目前前后后我改了很多次现在每天早上的那条天气推送已经变成我们之间一个很不起眼的固定仪式。它对我最大的帮助不是那几行推送文案而是让我第一次把一个完整的微信公众号消息推送链路走通了。从token获取到模板消息下发从定时任务到异常兜底这套基础能力放到任何需要发消息、发提醒、发通知的场景里都能复用。最后再分享一个小经验千万别想着一步到位把所有功能都加上先把最原始的“每天一条天气”跑通再慢慢加人、加城市、加提醒规则。通道畅通之后你想加什么都是顺手的事。如果有人需要后面我也可以把纪念日提醒、每日一言、喝水提醒这些扩展写出来改动其实都在同一个框架里。希望这篇教程能帮你少走一点弯路尽快收到你那条准时的早安天气。本文还有配套的精品资源点击获取