1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349
| @Slf4j @Service @RequiredArgsConstructor public class WxMpEventHandler {
private final IWxMpUserService wxMpUserService; private final WxMpMessageTemplate messageTemplate;
@Value("${wx.mp.miniprogram.app-id:}") private String miniprogramAppId;
@Value("${wx.mp.miniprogram.page-path:pages/index/index}") private String miniprogramPagePath;
@Value("${wx.mp.miniprogram.title:欢迎使用我们的小程序}") private String miniprogramTitle;
@Value("${wx.mp.miniprogram.thumb-media-id:}") private String miniprogramThumbMediaId;
/** * 记录所有事件的日志 */ public WxMpXmlOutMessage logHandler(WxMpXmlMessage wxMessage, Map<String, Object> context, WxMpService weixinService, WxSessionManager sessionManager) { log.info("接收到请求消息,内容:{}", wxMessage); return null; }
/** * 关注事件处理 */ public WxMpXmlOutMessage subscribeHandler(WxMpXmlMessage wxMessage, Map<String, Object> context, WxMpService weixinService, WxSessionManager sessionManager) throws WxErrorException {
String openId = wxMessage.getFromUser(); log.info("新关注用户 OPENID: {}", openId);
// 获取用户信息并保存 try { WxMpUser userInfo = weixinService.getUserService().userInfo(openId); log.info("已保存用户信息: {}", userInfo.getNickname()); } catch (Exception e) { log.error("获取用户信息失败,仅保存openId", e); }
// 异步发送完整的欢迎消息(文字 + 小程序卡片) sendWelcomeMessages(openId, weixinService);
// 立即回复简单的文字消息(避免微信超时) return WxMpXmlOutTextMessage.TEXT() .content(messageTemplate.getSubscribe().getInstantReply()) .fromUser(wxMessage.getToUser()) .toUser(wxMessage.getFromUser()) .build(); }
/** * 取消关注事件处理 */ public WxMpXmlOutMessage unsubscribeHandler(WxMpXmlMessage wxMessage, Map<String, Object> context, WxMpService weixinService, WxSessionManager sessionManager) { String openId = wxMessage.getFromUser(); log.info("取消关注用户 OPENID: {}", openId);
// 更新用户取消关注状态 wxMpUserService.handleUnsubscribe(openId);
return null; }
/** * 默认事件处理 */ public WxMpXmlOutMessage defaultHandler(WxMpXmlMessage wxMessage, Map<String, Object> context, WxMpService weixinService, WxSessionManager sessionManager) {
// 如果是文字消息,进行智能回复 if ("text".equals(wxMessage.getMsgType())) { String content = wxMessage.getContent(); log.info("收到文字消息:{}", content);
return handleTextMessage(content, wxMessage, weixinService); }
return null; }
/** * 处理文字消息 */ private WxMpXmlOutMessage handleTextMessage(String content, WxMpXmlMessage wxMessage, WxMpService weixinService) { if (content == null) { return null; }
String lowerContent = content.toLowerCase().trim(); String fromUser = wxMessage.getFromUser(); String toUser = wxMessage.getToUser();
// 关键词匹配回复 if (lowerContent.contains("小程序") || lowerContent.contains("程序")) { return handleMiniprogramRequest(fromUser, toUser, weixinService); } else if (lowerContent.contains("帮助") || lowerContent.equals("?") || lowerContent.equals("?")) { return buildTextReply(messageTemplate.getKeyword().getHelp(), fromUser, toUser); } else if (lowerContent.contains("服务")) { return buildTextReply(messageTemplate.getKeyword().getService(), fromUser, toUser); } else if (lowerContent.contains("你好") || lowerContent.contains("hi") || lowerContent.contains("hello")) { return buildTextReply("您好!很高兴为您服务!如需帮助,请回复\"帮助\"查看功能菜单。", fromUser, toUser); } else if (lowerContent.contains("客服") || lowerContent.contains("人工")) { return buildTextReply("正在为您转接人工客服,请稍候...\n\n工作时间:9:00-18:00", fromUser, toUser); }
// 默认回复 return buildTextReply("感谢您的消息!如需帮助,请回复\"帮助\"查看功能菜单。", fromUser, toUser); }
/** * 处理小程序请求 */ private WxMpXmlOutMessage handleMiniprogramRequest(String fromUser, String toUser, WxMpService weixinService) { if (miniprogramAppId != null && !miniprogramAppId.trim().isEmpty() && !miniprogramAppId.equals("your_mp_appid")) { try { // 异步发送小程序卡片 java.util.concurrent.CompletableFuture.runAsync(() -> { try { Thread.sleep(500); // 稍微延迟 sendMiniprogramCard(fromUser, weixinService);
// 发送引导消息 Thread.sleep(1000); me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage guideMessage = me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage.TEXT() .toUser(fromUser) .content(messageTemplate.getSubscribe().getMiniprogramGuide()) .build(); weixinService.getKefuService().sendKefuMessage(guideMessage); } catch (Exception e) { log.error("异步发送小程序卡片失败", e); } });
return buildTextReply(messageTemplate.getKeyword().getMiniprogramPromo(), fromUser, toUser); } catch (Exception e) { log.error("发送小程序卡片失败", e); return buildTextReply("抱歉,小程序卡片发送失败,请稍后重试。", fromUser, toUser); } } else { return buildTextReply("抱歉,小程序功能暂未配置,请联系管理员。", fromUser, toUser); } }
/** * 构建文字回复消息 */ private WxMpXmlOutTextMessage buildTextReply(String content, String fromUser, String toUser) { return WxMpXmlOutTextMessage.TEXT() .content(content) .fromUser(toUser) .toUser(fromUser) .build(); }
/** * 发送完整的欢迎消息(文字 + 小程序卡片) */ private void sendWelcomeMessages(String openId, WxMpService weixinService) { // 使用线程池异步发送,避免阻塞主流程 java.util.concurrent.CompletableFuture.runAsync(() -> { try { // 等待一秒,确保用户已关注成功 Thread.sleep(1000);
// 发送详细的欢迎文字消息 sendDetailedWelcomeText(openId, weixinService);
// 如果配置了小程序信息,发送小程序卡片 if (miniprogramAppId != null && !miniprogramAppId.trim().isEmpty() && !miniprogramAppId.equals("your_mp_appid")) { // 稍微延迟,避免消息发送过快 Thread.sleep(1000); sendMiniprogramCard(openId, weixinService); }
} catch (Exception e) { log.error("发送欢迎消息失败,openId: {}", openId, e); } }); }
/** * 发送详细的欢迎文字消息 */ private void sendDetailedWelcomeText(String openId, WxMpService weixinService) throws WxErrorException { try { me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage textMessage = me.chanjar.weixin.mp.bean.kefu.WxMpKefuMessage.TEXT() .toUser(openId) .content(messageTemplate.getSubscribe().getDetailedWelcome()) .build();
weixinService.getKefuService().sendKefuMessage(textMessage); log.info("成功发送详细欢迎文字给用户:{}", openId); } catch (Exception e) { log.error("发送详细欢迎文字失败,openId: {}", openId, e); throw e; } }
/** * 发送小程序卡片 */ private void sendMiniprogramCard(String openId, WxMpService weixinService) throws WxErrorException { try { // 检查是否配置了缩略图媒体ID if (miniprogramThumbMediaId == null || miniprogramThumbMediaId.trim().isEmpty()) { log.warn("未配置小程序卡片缩略图媒体ID,无法发送小程序卡片,openId: {}", openId); // 发送文字提示替代小程序卡片 WxMpKefuMessage textMessage = WxMpKefuMessage.TEXT() .toUser(openId) .content("点击这里打开小程序:" + miniprogramTitle + "\n\n注意:管理员需要先上传小程序卡片缩略图") .build(); weixinService.getKefuService().sendKefuMessage(textMessage); return; }
WxMpKefuMessage build = WxMpKefuMessage.MINIPROGRAMPAGE() .appId(miniprogramAppId) .title(miniprogramTitle) .pagePath(miniprogramPagePath) .thumbMediaId(miniprogramThumbMediaId) .toUser(openId) .build();
weixinService.getKefuService().sendKefuMessage(build); log.info("成功发送小程序卡片给用户:{}", openId); } catch (Exception e) { log.error("发送小程序卡片失败,openId: {}", openId, e); throw e; } }
/** * 上传临时媒体文件获取media_id * 用于小程序卡片的缩略图(有效期3天) * * @param weixinService 微信服务 * @param filePath 图片文件路径 * @param mediaType 媒体类型 (image, voice, video, thumb) * @return media_id */ public String uploadTempMedia(WxMpService weixinService, String filePath, String mediaType) throws WxErrorException { try { java.io.File file = new java.io.File(filePath); if (!file.exists()) { throw new IllegalArgumentException("文件不存在: " + filePath); }
// 使用临时媒体文件上传接口 // me.chanjar.weixin.mp.bean.result.WxMpMediaUploadResult result = // weixinService.getMediaService().upload(mediaType, file); // // String mediaId = result.getMediaId(); // log.info("成功上传临时媒体文件,media_id: {}, 文件路径: {}", mediaId, filePath); return ""; } catch (Exception e) { log.error("上传临时媒体文件失败,文件路径: {}", filePath, e); throw e; } }
/** * 上传永久素材获取media_id * 用于小程序卡片的缩略图(永久有效) * * @param weixinService 微信服务 * @param filePath 图片文件路径 * @param title 素材标题(可选) * @param introduction 素材介绍(可选) * @return media_id */ public String uploadPermanentMedia(WxMpService weixinService, String filePath, String title, String introduction) throws WxErrorException { try { java.io.File file = new java.io.File(filePath); if (!file.exists()) { throw new IllegalArgumentException("文件不存在: " + filePath); }
// 创建永久素材对象 me.chanjar.weixin.mp.bean.material.WxMpMaterial material = new me.chanjar.weixin.mp.bean.material.WxMpMaterial(); material.setFile(file); material.setName(file.getName());
if (title != null && !title.trim().isEmpty()) { material.setVideoTitle(title); } if (introduction != null && !introduction.trim().isEmpty()) { material.setVideoIntroduction(introduction); }
// 上传永久素材 me.chanjar.weixin.mp.bean.material.WxMpMaterialUploadResult result = weixinService.getMaterialService().materialFileUpload("image", material);
String mediaId = result.getMediaId(); log.info("成功上传永久素材,media_id: {}, 文件路径: {}", mediaId, filePath); return mediaId; } catch (Exception e) { log.error("上传永久素材失败,文件路径: {}", filePath, e); throw e; } }
/** * 上传图片文件作为小程序卡片缩略图(临时媒体文件,3天有效期) * 建议图片尺寸:200x200px,格式:jpg/png,大小不超过2MB * * @param weixinService 微信服务 * @param imagePath 图片文件路径 * @return thumb_media_id */ public String uploadMiniprogramThumb(WxMpService weixinService, String imagePath) throws WxErrorException { return uploadTempMedia(weixinService, imagePath, "thumb"); }
/** * 上传图片文件作为小程序卡片缩略图(永久素材,无时间限制) * 建议图片尺寸:200x200px,格式:jpg/png,大小不超过2MB * * @param weixinService 微信服务 * @param imagePath 图片文件路径 * @param title 素材标题 * @return media_id */ public String uploadPermanentMiniprogramThumb(WxMpService weixinService, String imagePath, String title) throws WxErrorException { return uploadPermanentMedia(weixinService, imagePath, title, "小程序卡片缩略图"); } }
|