基于 Spring Boot 和 UniApp 实现微信小程序消息通知_基于springboot的微信小程序
基于 Spring Boot 和 UniApp 实现微信小程序消息通知
一、前言
在现代移动应用开发中,消息通知功能是提升用户体验和应用交互性的重要手段。微信小程序提供了丰富的消息推送能力,而 UniApp 作为一款跨平台的前端开发框架,能够方便地与微信小程序集成。本文将详细介绍如何基于 Spring Boot 实现 UniApp 微信小程序的消息通知功能,包括后端服务的搭建、微信小程序的配置以及前端页面的实现。我们将通过一个实际案例,展示如何推送订单状态变更通知。
二、技术栈
- 后端:Spring Boot
- 前端:UniApp
- 消息推送服务:微信小程序订阅消息
- 开发工具:IntelliJ IDEA、HBuilderX
三、实现步骤
(一)后端服务搭建
-
创建 Spring Boot 项目
- 使用 Spring Initializr(https://start.spring.io/)快速生成项目,选择以下依赖:
- Spring Web
- Lombok(可选,用于简化代码)
- 下载并解压项目,导入到 IntelliJ IDEA 中。
- 使用 Spring Initializr(https://start.spring.io/)快速生成项目,选择以下依赖:
-
引入微信小程序相关依赖
- 在
pom.xml
文件中添加微信小程序的 Java SDK 依赖:<dependency> <groupId>com.github.binarywang</groupId> <artifactId>weixin-java-miniapp</artifactId> <version>4.6.0</version></dependency>
- 在
-
配置微信小程序参数
- 在
application.yml
文件中添加微信小程序的配置信息:wechat: miniapp: appid: wxxxxxxxxxxxxxx secret: xxxxxxxxxxxxxxxx token: xxxxxxxxxxxxxxxx aes-key: xxxxxxxxxxxxxxxx
- 在
-
实现微信消息推送服务
- 创建一个服务类
WxMaService
,用于封装微信消息推送的逻辑:import com.github.binarywang.wxpay.config.WxPayConfig;import com.github.binarywang.wxpay.service.WxPayService;import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;import com.github.binarywang.weixin.mp.config.WxMpConfigStorage;import com.github.binarywang.weixin.mp.config.impl.WxMpDefaultConfigImpl;import com.github.binarywang.weixin.mp.service.WxMpService;import com.github.binarywang.weixin.mp.service.impl.WxMpServiceImpl;import com.github.binarywang.weixin.sdk.mp.bean.template.WxMpTemplateMessage;import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Service;@Servicepublic class WxMaService { @Value(\"${wechat.miniapp.appid}\") private String appId; @Value(\"${wechat.miniapp.secret}\") private String secret; public void sendTemplateMessage(String openId, String templateId, String page, String formId, String data) { WxMpDefaultConfigImpl config = new WxMpDefaultConfigImpl(); config.setAppId(appId); config.setSecret(secret); WxMpService wxMpService = new WxMpServiceImpl(); wxMpService.setWxMpConfigStorage(config); WxMpTemplateMessage templateMessage = WxMpTemplateMessage.builder() .toUser(openId) .templateId(templateId) .page(page) .formId(formId) .data(data) .build(); try { wxMpService.getTemplateMsgService().sendTemplateMsg(templateMessage); } catch (Exception e) { e.printStackTrace(); } }}
- 创建一个服务类
-
创建消息推送接口
- 创建一个控制器类
MessageController
,提供一个接口用于触发消息推送:import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RestController;@RestControllerpublic class MessageController { @Autowired private WxMaService wxMaService; @GetMapping(\"/sendOrderNotification\") public String sendOrderNotification(@RequestParam String openId, @RequestParam String orderId) { String templateId = \"YOUR_TEMPLATE_ID\"; // 替换为实际的模板ID String page = \"/pages/orderDetail/orderDetail?orderId=\" + orderId; String formId = \"YOUR_FORM_ID\"; // 替换为实际的FormID String data = \"{\\\"keyword1\\\": {\\\"value\\\": \\\"订单号:\" + orderId + \"\\\"}, \\\"keyword2\\\": {\\\"value\\\": \\\"订单状态:已发货\\\"}}\"; wxMaService.sendTemplateMessage(openId, templateId, page, formId, data); return \"消息已发送\"; }}
- 创建一个控制器类
(二)前端页面开发
-
创建 UniApp 项目
- 使用 HBuilderX 创建一个新的 UniApp 项目,选择微信小程序模板。
-
实现订阅消息功能
- 在页面中添加一个按钮,用于触发订阅消息:
export default { data() { return { openId: null, }; }, methods: { async subscribeMessage() { const templateIds = [ \"YOUR_TEMPLATE_ID\", // 替换为实际的模板ID ]; const res = await uni.requestSubscribeMessage({ tmplIds: templateIds, success: (res) => { console.log(\"订阅成功\", res); if (res[templateIds[0]] === \"accept\") { uni.showToast({ title: \"订阅成功\", icon: \"success\", duration: 2000, }); } else { uni.showToast({ title: \"您已拒绝订阅\", icon: \"none\", duration: 2000, }); } }, fail: (err) => { console.error(\"订阅失败\", err); uni.showToast({ title: \"订阅失败\", icon: \"none\", duration: 2000, }); }, }); }, },};.container { display: flex; justify-content: center; align-items: center; height: 100vh;}button { width: 200px; height: 50px; line-height: 50px; text-align: center; background-color: #1aad19; color: white; font-size: 16px; border-radius: 5px;}
- 在页面中添加一个按钮,用于触发订阅消息:
-
获取用户
openId
- 在页面中添加一个方法用于获取
openId
:export default { data() { return { openId: null, }; }, methods: { async getOpenId() { const res = await uni.login(); const code = res.code; // 调用后端接口,通过code换取openId uni.request({ url: \"http://your-backend-server/getOpenId\", // 替换为实际的后端接口 method: \"POST\", data: { code: code, }, success: (res) => { if (res.data.success) { this.openId = res.data.openId; uni.showToast({ title: \"获取openId成功\", icon: \"success\", duration: 2000, }); } else { uni.showToast({ title: \"获取openId失败\", icon: \"none\", duration: 2000, }); } }, fail: (err) => { console.error(\"获取openId失败\", err); uni.showToast({ title: \"获取openId失败\", icon: \"none\", duration: 2000, }); }, }); }, },};
- 在页面中添加一个方法用于获取
-
后端接口用于通过
code
换取openId
- 在后端创建一个接口用于通过
code
换取openId
:import com.github.binarywang.weixin.mp.api.WxMpService;import com.github.binarywang.weixin.mp.api.impl.WxMpServiceImpl;import com.github.binarywang.weixin.mp.config.WxMpDefaultConfigImpl;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RestController;import java.util.Map;@RestControllerpublic class OpenIdController { @PostMapping(\"/getOpenId\") public Map<String, Object> getOpenId(@RequestBody Map<String, String> requestBody) { String code = requestBody.get(\"code\"); WxMpDefaultConfigImpl config = new WxMpDefaultConfigImpl(); config.setAppId(\"YOUR_APP_ID\"); config.setSecret(\"YOUR_APP_SECRET\"); WxMpService wxMpService = new WxMpServiceImpl(); wxMpService.setWxMpConfigStorage(config); try { String openId = wxMpService.getUserService().getOpenId(code); return Map.of(\"success\", true, \"openId\", openId); } catch (Exception e) { e.printStackTrace(); return Map.of(\"success\", false, \"message\", \"获取openId失败\"); } }}
- 在后端创建一个接口用于通过
四、完整代码示例
后端代码
// WxMaService.javaimport com.github.binarywang.weixin.mp.api.WxMpService;import com.github.binarywang.weixin.mp.api.impl.WxMpServiceImpl;import com.github.binarywang.weixin.mp.config.WxMpDefaultConfigImpl;import com.github.binarywang.weixin.mp.bean.template.WxMpTemplateMessage;import org.springframework.stereotype.Service;@Servicepublic class WxMaService { private final String appId = \"YOUR_APP_ID\"; private final String secret = \"YOUR_APP_SECRET\"; public void sendTemplateMessage(String openId, String templateId, String page, String formId, String data) { WxMpDefaultConfigImpl config = new WxMpDefaultConfigImpl(); config.setAppId(appId); config.setSecret(secret); WxMpService wxMpService = new WxMpServiceImpl(); wxMpService.setWxMpConfigStorage(config); WxMpTemplateMessage templateMessage = WxMpTemplateMessage.builder() .toUser(openId) .templateId(templateId) .page(page) .formId(formId) .data(data) .build(); try { wxMpService.getTemplateMsgService().sendTemplateMsg(templateMessage); } catch (Exception e) { e.printStackTrace(); } }}
// MessageController.javaimport org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RestController;@RestControllerpublic class MessageController { @Autowired private WxMaService wxMaService; @GetMapping(\"/sendOrderNotification\") public String sendOrderNotification(@RequestParam String openId, @RequestParam String orderId) { String templateId = \"YOUR_TEMPLATE_ID\"; String page = \"/pages/orderDetail/orderDetail?orderId=\" + orderId; String formId = \"YOUR_FORM_ID\"; String data = \"{\\\"keyword1\\\": {\\\"value\\\": \\\"订单号:\" + orderId + \"\\\"}, \\\"keyword2\\\": {\\\"value\\\": \\\"订单状态:已发货\\\"}}\"; wxMaService.sendTemplateMessage(openId, templateId, page, formId, data); return \"消息已发送\"; }}
// OpenIdController.javaimport com.github.binarywang.weixin.mp.api.WxMpService;import com.github.binarywang.weixin.mp.api.impl.WxMpServiceImpl;import com.github.binarywang.weixin.mp.config.WxMpDefaultConfigImpl;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind```javaimport org.springframework.web.bind.annotation.RestController;import java.util.Map;@RestControllerpublic class OpenIdController { @PostMapping(\"/getOpenId\") public Map<String, Object> getOpenId(@RequestBody Map<String, String> requestBody) { String code = requestBody.get(\"code\"); WxMpDefaultConfigImpl config = new WxMpDefaultConfigImpl(); config.setAppId(\"YOUR_APP_ID\"); config.setSecret(\"YOUR_APP_SECRET\"); WxMpService wxMpService = new WxMpServiceImpl(); wxMpService.setWxMpConfigStorage(config); try { String openId = wxMpService.getUserService().getOpenId(code); return Map.of(\"success\", true, \"openId\", openId); } catch (Exception e) { e.printStackTrace(); return Map.of(\"success\", false, \"message\", \"获取openId失败\"); } }}
前端代码
export default { data() { return { openId: null, }; }, methods: { async subscribeMessage() { const templateIds = [ \"YOUR_TEMPLATE_ID\", // 替换为实际的模板ID ]; const res = await uni.requestSubscribeMessage({ tmplIds: templateIds, success: (res) => { console.log(\"订阅成功\", res); if (res[templateIds[0]] === \"accept\") { uni.showToast({ title: \"订阅成功\", icon: \"success\", duration: 2000, }); } else { uni.showToast({ title: \"您已拒绝订阅\", icon: \"none\", duration: 2000, }); } }, fail: (err) => { console.error(\"订阅失败\", err); uni.showToast({ title: \"订阅失败\", icon: \"none\", duration: 2000, }); }, }); }, async getOpenId() { const res = await uni.login(); const code = res.code; // 调用后端接口,通过code换取openId uni.request({ url: \"http://your-backend-server/getOpenId\", // 替换为实际的后端接口 method: \"POST\", data: { code: code, }, success: (res) => { if (res.data.success) { this.openId = res.data.openId; uni.showToast({ title: \"获取openId成功\", icon: \"success\", duration: 2000, }); } else { uni.showToast({ title: \"获取openId失败\", icon: \"none\", duration: 2000, }); } }, fail: (err) => { console.error(\"获取openId失败\", err); uni.showToast({ title: \"获取openId失败\", icon: \"none\", duration: 2000, }); }, }); }, },};.container { display: flex; flex-direction: column; justify-content: center; align-items: center; height: 100vh;}button { width: 200px; height: 50px; line-height: 50px; text-align: center; background-color: #1aad19; color: white; font-size: 16px; border-radius: 5px; margin: 10px 0;}
五、完整流程整合
1. 用户订阅消息
- 用户进入小程序页面,点击“订阅消息”按钮。
- 调用
uni.requestSubscribeMessage
方法,请求用户订阅消息。 - 如果用户同意订阅,将模板ID存储到本地或后端。
2. 获取用户 openId
- 用户进入小程序页面时,自动调用
uni.login
获取code
。 - 将
code
发送到后端,后端通过微信API换取openId
。 - 将
openId
存储到本地或后端。
3. 后端触发消息推送
- 当需要推送消息时,后端调用
WxMaService.sendTemplateMessage
方法。 - 指定
openId
、模板ID、页面路径、表单ID和消息内容。 - 微信小程序收到消息后,用户点击消息即可跳转到指定页面。
六、注意事项
1. 模板消息限制
- 微信模板消息有严格的使用限制,例如每天只能发送一定数量的消息。
- 模板消息的内容需要符合微信的要求,不能包含广告或营销信息。
2. 表单ID获取
- 表单ID是通过用户在小程序中提交表单时获取的。
- 每个表单ID只能使用一次,使用后需要重新获取。
3. 安全性
- 在后端处理微信消息推送时,需要验证用户的身份,确保消息推送给正确的用户。
- 对于敏感信息,如
openId
和formId
,需要进行加密存储和传输。
七、总结
本文详细介绍了如何基于 Spring Boot 和 UniApp 实现微信小程序的消息通知功能。通过后端服务的搭建、微信小程序的配置以及前端页面的实现,我们成功实现了一个完整的消息推送流程。在实际开发中,可以根据业务需求进一步扩展和优化代码,例如增加消息推送的重试机制、优化用户体验等。希望本文对大家有所帮助!
八、扩展功能
1. 消息推送重试机制
在实际业务中,可能会遇到消息推送失败的情况。可以通过以下方式实现消息推送的重试机制:
- 在
WxMaService
中增加重试逻辑:public void sendTemplateMessage(String openId, String templateId, String page, String formId, String data) { WxMpDefaultConfigImpl config = new WxMpDefaultConfigImpl(); config.setAppId(appId); config.setSecret(secret); WxMpService wxMpService = new WxMpServiceImpl(); wxMpService.setWxMpConfigStorage(config); WxMpTemplateMessage templateMessage = WxMpTemplateMessage.builder() .toUser(openId) .templateId(templateId) .page(page) .formId(formId) .data(data) .build(); int retryCount = 3; // 重试次数 for (int i = 0; i < retryCount; i++) { try { wxMpService.getTemplateMsgService().sendTemplateMsg(templateMessage); break; // 发送成功,退出循环 } catch (Exception e) { e.printStackTrace(); if (i == retryCount - 1) { throw new RuntimeException(\"消息推送失败,重试次数已达上限\"); } } }}
2. 用户订阅状态管理
可以将用户的订阅状态存储到数据库中,以便在需要时查询用户是否已订阅相关消息:
- 创建一个用户订阅状态表:
CREATE TABLE user_subscription ( id BIGINT AUTO_INCREMENT PRIMARY KEY, open_id VARCHAR(255) NOT NULL, template_id VARCHAR(255) NOT NULL, subscribed BOOLEAN DEFAULT FALSE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP);
- 在后端更新用户订阅状态:
@PostMapping(\"/updateSubscription\")public Map<String, Object> updateSubscription(@RequestBody Map<String, String> requestBody) { String openId = requestBody.get(\"openId\"); String templateId = requestBody.get(\"templateId\"); boolean subscribed = requestBody.get(\"subscribed\").equals(\"true\"); // 更新数据库中的订阅状态 // 示例代码,具体实现根据实际数据库操作框架 userSubscriptionService.updateSubscription(openId, templateId, subscribed); return Map.of(\"success\", true);}
3. 消息内容动态生成
在实际业务中,消息内容可能需要根据业务逻辑动态生成。可以通过模板引擎(如 Thymeleaf 或 Velocity)来实现消息内容的动态生成:
- 创建一个模板文件
messageTemplate.html
:<div> <p>尊敬的用户,您的订单 {{orderId}} 已发货。</p> <p>发货时间:{{shipTime}}</p></div>
- 在后端生成消息内容:
import org.thymeleaf.TemplateEngine;import org.thymeleaf.context.Context;import org.thymeleaf.spring5.SpringTemplateEngine;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;@Servicepublic class MessageContentService { @Autowired private SpringTemplateEngine templateEngine; public String generateMessageContent(String orderId, String shipTime) { Context context = new Context(); context.setVariable(\"orderId\", orderId); context.setVariable(\"shipTime\", shipTime); return templateEngine.process(\"messageTemplate\", context); }}
九、常见问题及解决方案
1. 消息推送失败
- 原因:可能是
openId
或formId
错误,或者模板消息内容不符合微信要求。 - 解决方案:
- 确保
openId
和formId
是正确的。 - 检查模板消息内容是否符合微信的要求,避免包含敏感词汇或广告内容。
- 检查微信小程序的后台配置是否正确。
- 确保
2. 用户拒绝订阅消息
- 原因:用户可能不希望接收消息,或者对消息内容不感兴趣。
- 解决方案:
- 提供清晰的订阅提示,让用户了解订阅消息的用途和好处。
- 确保消息内容有价值,避免发送无关紧要的信息。
- 提供用户取消订阅的选项,提升用户体验。
3. 表单ID不足
- 原因:表单ID是通过用户提交表单时获取的,如果用户长时间不提交表单,可能会导致表单ID不足。
- 解决方案:
- 在用户提交表单时,及时获取并存储表单ID。
- 如果表单ID不足,可以提示用户重新提交表单以获取新的表单ID。
- 合理规划表单ID的使用,避免频繁发送消息。
十、总结与展望
通过本文的介绍,我们已经完整地实现了基于 Spring Boot 和 UniApp 的微信小程序消息通知功能。从后端服务的搭建到前端页面的实现,再到消息推送的完整流程,我们详细展示了每一步的操作步骤和代码示例。在实际开发中,可以根据业务需求进一步扩展和优化代码,例如增加消息推送的重试机制、优化用户体验等。
未来,随着微信小程序功能的不断更新和优化,消息通知功能也将有更多的扩展可能性。例如,结合微信支付、微信客服消息等功能,可以进一步提升小程序的用户体验和业务价值。希望本文能够为开发者提供有价值的参考,帮助大家更好地实现微信小程序的消息通知功能。
如果你有任何问题或建议,欢迎在评论区留言,我会及时回复!