在现代移动应用开发中,消息通知功能是提升用户体验和应用交互性的重要手段。微信小程序提供了丰富的消息推送能力,而 UniApp 作为一款跨平台的前端开发框架,能够方便地与微信小程序集成。本文将详细介绍如何基于 Spring Boot 实现 UniApp 微信小程序的消息通知功能,包括后端服务的搭建、微信小程序的配置以及前端页面的实现。我们将通过一个实际案例,展示如何推送订单状态变更通知。
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;
@Service
public 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;
@RestController
public 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 "消息已发送";
}
}
<template>
<view class="container">
<button @click="subscribeMessage">订阅消息</button>
</view>
</template>
<script>
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,
});
},
});
},
},
};
</script>
<style scoped>
.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;
}
</style>
```
3. **获取用户 `openId`**
- 在页面中添加一个方法用于获取 `openId`:
```vue
<script>
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,
});
},
});
},
},
};
</script>
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;
@RestController
public 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.java
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 com.github.binarywang.weixin.mp.bean.template.WxMpTemplateMessage;
import org.springframework.stereotype.Service;
@Service
public 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.java
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;
@RestController
public 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.java
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
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
@RestController
public 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失败");
}
}
}
<template>
<view class="container">
<button @click="subscribeMessage">订阅消息</button>
<button @click="getOpenId">获取openId</button>
</view>
</template>
<script>
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,
});
},
});
},
},
};
</script>
<style scoped>
.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;
}
</style>
uni.requestSubscribeMessage
方法,请求用户订阅消息。openId
uni.login
获取 code
。code
发送到后端,后端通过微信API换取 openId
。openId
存储到本地或后端。WxMaService.sendTemplateMessage
方法。openId
、模板ID、页面路径、表单ID和消息内容。openId
和 formId
,需要进行加密存储和传输。七、总结本文详细介绍了如何基于 Spring Boot 和 UniApp 实现微信小程序的消息通知功能。通过后端服务的搭建、微信小程序的配置以及前端页面的实现,我们成功实现了一个完整的消息推送流程。在实际开发中,可以根据业务需求进一步扩展和优化代码,例如增加消息推送的重试机制、优化用户体验等。希望本文对大家有所帮助!
在实际业务中,可能会遇到消息推送失败的情况。可以通过以下方式实现消息推送的重试机制:
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("消息推送失败,重试次数已达上限");
}
}
}
}
可以将用户的订阅状态存储到数据库中,以便在需要时查询用户是否已订阅相关消息:
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);
}
在实际业务中,消息内容可能需要根据业务逻辑动态生成。可以通过模板引擎(如 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;
@Service
public 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);
}
}
openId
或 formId
错误,或者模板消息内容不符合微信要求。openId
和 formId
是正确的。通过本文的介绍,我们已经完整地实现了基于 Spring Boot 和 UniApp 的微信小程序消息通知功能。从后端服务的搭建到前端页面的实现,再到消息推送的完整流程,我们详细展示了每一步的操作步骤和代码示例。在实际开发中,可以根据业务需求进一步扩展和优化代码,例如增加消息推送的重试机制、优化用户体验等。
未来,随着微信小程序功能的不断更新和优化,消息通知功能也将有更多的扩展可能性。例如,结合微信支付、微信客服消息等功能,可以进一步提升小程序的用户体验和业务价值。希望本文能够为开发者提供有价值的参考,帮助大家更好地实现微信小程序的消息通知功能。
如果你有任何问题或建议,欢迎在评论区留言,我会及时回复!
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有