package com.kym.miniapp.controller; import cn.dev33.satoken.annotation.SaIgnore; import com.kym.common.annotation.ApiLog; import com.kym.common.annotation.SysLog; import com.kym.common.utils.wx.WxPbUtil; import com.kym.common.R; import com.kym.service.MpRelationService; import com.kym.service.wechat.WeixinMPService; import jakarta.servlet.http.HttpServletRequest; import com.alibaba.fastjson2.JSON; import me.chanjar.weixin.mp.api.WxMpService; import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import me.chanjar.weixin.common.error.WxErrorException; import org.springframework.http.MediaType; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import java.io.PrintWriter; @Controller @RequestMapping("wx") @Slf4j public class WeixinMPController { private final WeixinMPService weixinMPService; private final MpRelationService mpRelationService; private final WxMpService wxMpService; public WeixinMPController(WeixinMPService weixinMPService, MpRelationService mpRelationService, WxMpService wxMpService) { this.weixinMPService = weixinMPService; this.mpRelationService = mpRelationService; this.wxMpService = wxMpService; } /** * 消息通知(订阅、用户消息) */ @PostMapping(value = "notify", produces = MediaType.TEXT_XML_VALUE) @ApiLog(value = "微信公众号服务器推送消息", ignoreParams = true) public void handleMessage(HttpServletRequest request, HttpServletResponse response) { weixinMPService.handleWxMPNotify(request, response); } /** * 微信服务器token验证 */ @GetMapping(value = "notify") @SaIgnore @SysLog("微信服务器token验证") public void checkSign(HttpServletResponse response, String signature, String timestamp, String nonce, String echostr) { String responseVal = echostr; try { boolean resp = WxPbUtil.checkSign(signature, timestamp, nonce); if (!resp) { responseVal = "failure"; log.error("wxpb check sign ERR!!!!"); } } catch (Exception e) { responseVal = "failure"; } finally { try { PrintWriter writer = response.getWriter(); writer.print(responseVal); writer.flush(); writer.close(); } catch (Exception e) { log.error(e.getMessage(), e); } } } /** * 导出当前公众号自定义菜单结构(调试用) */ @GetMapping(value = "/exportMenu") @ResponseBody public R exportMenu() { try { var menu = wxMpService.getMenuService().menuGet(); return R.success(JSON.toJSONString(menu)); } catch (WxErrorException e) { log.error("导出菜单失败", e); return R.failed(-1, "导出失败: " + e.getMessage()); } } /** * 通过 API 创建公众号自定义菜单(服务器推送启用后使用) */ @PostMapping(value = "/createMenu") @ResponseBody public R createMenu() { try { String menuJson = "{\"button\":[{\"type\":\"view\",\"name\":\"停车减免\",\"url\":\"https://cloud.yeswash.cn/parking.html\"},{\"type\":\"view\",\"name\":\"商家入口\",\"url\":\"https://cloud.yeswash.cn/h5#/\"}]}"; log.info("创建菜单: {}", menuJson); String result = wxMpService.getMenuService().menuCreate(menuJson); log.info("菜单创建结果: {}", result); return R.success(result); } catch (WxErrorException e) { log.error("创建菜单失败: {}", e.getMessage(), e); return R.failed(e.getError().getErrorCode(), e.getError().getErrorMsg()); } catch (Exception e) { log.error("创建菜单异常", e); return R.failed(500, e.getMessage()); } } /** * 手动同步公众号关注用户(一次性触发) */ @GetMapping(value = "/syncMpUsers") @SaIgnore @ResponseBody public R syncMpUsers() { try { mpRelationService.batchBindMpUser(); return R.success("同步完成"); } catch (WxErrorException e) { log.error("手动同步公众号用户失败", e); return R.failed(-1, "同步失败: " + e.getMessage()); } } }