为用户生成二维码
@PostMapping("/generate")
public AjaxResult generateQrcode(@RequestParam Long userId) {
try {
SysUser user = userService.generateUserQrcode(userId);
return AjaxResult.success(user);
} catch (Exception e) {
return AjaxResult.error("Failed to generate QR code: " + e.getMessage());
}
}
SysUser generateUserQrcode(Long userId) throws JsonProcessingException;
@Override
public SysUser generateUserQrcode(Long userId) throws JsonProcessingException {
// 使用合适的 scene_id
int sceneId = (int)(userId % 100000); // 确保在范围内
// 1. 获取用户信息
SysUser user = userMapper.selectUserById(userId);
if (user == null) {
throw new RuntimeException("用户不存在");
}
// 2. 调用微信接口生成永久二维码
String accessToken = getAccessToken(); // 获取 access_token
String qrcodeUrl = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=" + accessToken;
// 创建永久二维码的请求
Map<String, Object> scene = new HashMap<>();
scene.put("scene_id", sceneId); // 使用合法的 sceneId
Map<String, Object> actionInfo = new HashMap<>();
actionInfo.put("scene", scene);
Map<String, Object> requestBodyMap = new HashMap<>();
requestBodyMap.put("action_name", "QR_LIMIT_SCENE");
requestBodyMap.put("action_info", actionInfo);
ObjectMapper objectMapper = new ObjectMapper();
String requestBody = objectMapper.writeValueAsString(requestBodyMap);
RestTemplate restTemplate = new RestTemplate();
String response = restTemplate.postForObject(qrcodeUrl, requestBody, String.class);
// 使用 Jackson 解析返回的 JSON 字符串
JsonNode jsonResponse = objectMapper.readTree(response);
if (!jsonResponse.has("ticket")) {
throw new RuntimeException("生成二维码失败,微信接口返回异常:" + jsonResponse.toString());
}
// 3. 获取二维码的 URL 和 Ticket
String ticket = jsonResponse.get("ticket").asText();
System.out.println(""+ticket);
String qrCodeUrl = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=" + ticket;
// 4. 更新用户表中的二维码信息
user.setQrUrl(qrCodeUrl); // 假设用户表中有 qrCodeUrl 字段
// 更新用户表
userMapper.updatesUserQrcode(userId, qrCodeUrl);
// 5. 返回更新后的用户信息
return user;
}
@Update("UPDATE sys_user SET qr_url = #{qrUrl} WHERE user_id = #{userId}")
void updatesUserQrcode(@Param("userId") Long userId, @Param("qrUrl") String qrUrl);
微信公众平台的 GET 请求,用于验证 token
private static final String TOKEN = "X4yZ7k9B3mVfD1nQ5tG8wL2pR7"; // 与微信公众号后台配置的 Token 保持一致
// 微信公众平台的 GET 请求,用于验证 token
@GetMapping("/event")
public void verifyToken(@RequestParam String signature,
@RequestParam String timestamp,
@RequestParam String nonce,
@RequestParam String echostr,
HttpServletResponse response) {
System.out.println("微信官方回调了我的这个接口");
try {
// 1. 将 token, timestamp, nonce 按字典序排序
String[] arr = {TOKEN, timestamp, nonce};
Arrays.sort(arr);
// 2. 将三个参数拼接成字符串并进行 SHA1 加密
StringBuilder content = new StringBuilder();
for (String s : arr) {
content.append(s);
}
String tempSignature = DigestUtils.sha1Hex(content.toString());
// 3. 校验签名是否来自微信
if (tempSignature.equals(signature)) {
response.getWriter().write(echostr); // 验证成功,返回 echostr
} else {
response.getWriter().write("fail"); // 验证失败
}
} catch (IOException e) {
e.printStackTrace();
}
}
微信公众平台的 POST 请求,接收事件推送
@PostMapping("/event")
public void handleWechatEvents(@RequestBody String xmlData, HttpServletResponse response) {
try {
// 解析微信推送的XML数据
Document document = DocumentHelper.parseText(xmlData);
Element root = document.getRootElement();
// 获取消息类型和事件类型
String msgType = root.elementText("MsgType");
String event = root.elementText("Event");
String openId = root.elementText("FromUserName"); // 提取用户的 OpenID
// 处理事件推送
if ("event".equals(msgType)) {
if ("subscribe".equals(event)) {
// 处理用户首次关注公众号事件
String eventKey = root.elementText("EventKey");
if (eventKey != null && eventKey.startsWith("qrscene_")) {
// 提取场景值中的 userId
String userId = eventKey.replace("qrscene_", "");
System.out.println("用户通过二维码关注公众号,OpenID: " + openId + ", 用户ID: " + userId);
handleSubscribeEvent(openId, userId);
//根据userId 更新openId
// 使用 MyBatis 查询后四位匹配的用户
SysUser user = userService.queryByUserIdSuffix(userId);
if(user != null){
Long userIds = user.getUserId();
//根据userId 更新openId
boolean flag = userService.updateByuserId(userIds,openId);
System.out.println("userId1"+userIds);
}
} else {
System.out.println("用户直接关注了公众号,OpenID: " + openId);
handleSubscribeEvent(openId, null); // 如果没有场景值,userId 为 null
}
} else if ("unsubscribe".equals(event)) {
// 处理用户取消关注事件
System.out.println("用户取消关注公众号,OpenID: " + openId);
handleUnsubscribeEvent(openId);
} else if ("SCAN".equals(event)) {
// 处理已关注用户扫描二维码事件
String eventKey = root.elementText("EventKey");
System.out.println("已关注用户扫描二维码,OpenID: " + openId + ", 场景值: " + eventKey);
handleScanEvent(openId, eventKey);
}
}
// 回复微信服务器,防止重试
response.getWriter().write("success");
} catch (Exception e) {
e.printStackTrace();
try {
response.getWriter().write("fail");
} catch (IOException ioException) {
ioException.printStackTrace();
}
}
}
// 处理首次关注事件(附带二维码参数)
private void handleSubscribeEvent(String openId, String userId) {
if (userId != null) {
// 处理通过带参数二维码关注的用户
System.out.println("欢迎通过二维码关注的用户,OpenID: " + openId + ", 用户ID: " + userId);
// 在这里可以将 openId 和 userId 保存到数据库,或者发送欢迎消息
saveUserInfo(openId, userId);
} else {
// 处理直接关注的用户
System.out.println("欢迎直接关注的用户,OpenID: " + openId);
saveUserInfo(openId, null); // 如果没有场景值,可以不保存 userId
}
// 在这里可以发送欢迎消息
}
// 处理取消关注事件
private void handleUnsubscribeEvent(String openId) {
System.out.println("用户取消关注,OpenID: " + openId);
// 在这里更新数据库,记录用户的取消关注行为
updateUserStatus(openId, false);
}
// 处理已关注用户扫描二维码事件
private void handleScanEvent(String openId, String eventKey) {
String userId = extractUserIdFromEventKey(eventKey); // 提取用户ID
System.out.println("已关注用户扫描二维码,OpenID: " + openId + ", 场景值: " + eventKey);
System.out.println("已关注用户,场景值中提取的用户ID: " + userId);
// 根据场景值和用户ID执行相应的业务逻辑
saveScanEventInfo(openId, userId);
}
// 提取 userId 方法
private String extractUserIdFromEventKey(String eventKey) {
if (eventKey != null && !eventKey.isEmpty()) {
try {
// 尝试提取 userId (假设 eventKey 是数字形式的用户ID)
return eventKey.replace("qrscene_", "");
} catch (Exception e) {
System.out.println("提取 userId 时发生错误: " + e.getMessage());
}
}
return "未知用户ID";
}
// 模拟保存用户信息到数据库
private void saveUserInfo(String openId, String userId) {
// 在这里执行将 openId 和 userId 保存到数据库的操作
System.out.println("保存用户信息到数据库,OpenID: " + openId + ", 用户ID: " + userId);
}
// 模拟更新用户状态(取消关注)
private void updateUserStatus(String openId, boolean isActive) {
// 在这里执行更新数据库中用户状态的操作
System.out.println("更新用户状态为 " + (isActive ? "活跃" : "取消关注") + ",OpenID: " + openId);
}
// 保存二维码扫描事件的信息
private void saveScanEventInfo(String openId, String userId) {
// 在这里执行保存已关注用户扫描二维码后的事件信息的操作
System.out.println("保存二维码扫描事件信息,OpenID: " + openId + ", 用户ID: " + userId);
}