赞
踩
注: 里面很多代码 参照 若依 开源项目
记录一下
后续会更新 支付和退款 二维码 红包 名片分享 等功能代码
引入一下微信SDK
litemall.wx 对应yml文件 微信配置
@Data @Configuration @ConfigurationProperties(prefix = "litemall.wx") public class WxProperties { private String appId; private String appSecret; private String mchId; private String mchKey; private String notifyUrl; private String keyPath; private String token; private String aesKey;
项目运行时将部分微信类初始化,后面需要时直接 @Autowired 引入对应类即可
具体代码:
@AllArgsConstructor @Configuration public class WxConfig { private final LogHandler logHandler; private final NullHandler nullHandler; private final KfSessionHandler kfSessionHandler; private final StoreCheckNotifyHandler storeCheckNotifyHandler; private final LocationHandler locationHandler; private final MenuHandler menuHandler; private final MsgHandler msgHandler; private final UnsubscribeHandler unsubscribeHandler; private final SubscribeHandler subscribeHandler; private final ScanHandler scanHandler; @Autowired private WxProperties properties; @Bean public WxMaConfig wxMaConfig() { WxMaInMemoryConfig config = new WxMaInMemoryConfig(); config.setAppid(properties.getAppId()); config.setSecret(properties.getAppSecret()); return config; } @Bean public WxMaService wxMaService(WxMaConfig maConfig) { WxMaService service = new WxMaServiceImpl(); service.setWxMaConfig(maConfig); return service; } @Bean public WxPayConfig wxPayConfig() { WxPayConfig payConfig = new WxPayConfig(); payConfig.setAppId(properties.getAppId()); payConfig.setMchId(properties.getMchId()); payConfig.setMchKey(properties.getMchKey()); payConfig.setNotifyUrl(properties.getNotifyUrl()); payConfig.setKeyPath(properties.getKeyPath()); payConfig.setTradeType("JSAPI"); payConfig.setSignType("MD5"); return payConfig; } @Bean public WxPayService wxPayService(WxPayConfig payConfig) { WxPayService wxPayService = new WxPayServiceImpl(); wxPayService.setConfig(payConfig); return wxPayService; } @Bean public WxMpService wxMpService() { WxMpService service = new WxMpServiceImpl(); WxMpDefaultConfigImpl configStorage = new WxMpDefaultConfigImpl(); configStorage.setAppId(properties.getAppId()); configStorage.setSecret(properties.getAppSecret()); configStorage.setToken(properties.getToken()); configStorage.setAesKey(properties.getAesKey()); Map<String, WxMpConfigStorage> configStorages = Maps.newHashMap(); configStorages.put("wxConfig",configStorage); service.setMultiConfigStorages(configStorages); return service; } @Bean public WxMpMessageRouter messageRouter(WxMpService wxMpService) { final WxMpMessageRouter newRouter = new WxMpMessageRouter(wxMpService); // 记录所有事件的日志 (异步执行) newRouter.rule().handler(this.logHandler).next(); // 接收客服会话管理事件 newRouter.rule().async(false).msgType(EVENT).event(KF_CREATE_SESSION) .handler(this.kfSessionHandler).end(); newRouter.rule().async(false).msgType(EVENT).event(KF_CLOSE_SESSION) .handler(this.kfSessionHandler).end(); newRouter.rule().async(false).msgType(EVENT).event(KF_SWITCH_SESSION) .handler(this.kfSessionHandler).end(); // 门店审核事件 newRouter.rule().async(false).msgType(EVENT).event(POI_CHECK_NOTIFY).handler(this.storeCheckNotifyHandler).end(); // 自定义菜单事件 newRouter.rule().async(false).msgType(EVENT).event(CLICK).handler(this.menuHandler).end(); // 点击菜单连接事件 newRouter.rule().async(false).msgType(EVENT).event(VIEW).handler(this.nullHandler).end(); // 关注事件 newRouter.rule().async(false).msgType(EVENT).event(SUBSCRIBE).handler(this.subscribeHandler).end(); // 取消关注事件 newRouter.rule().async(false).msgType(EVENT).event(UNSUBSCRIBE).handler(this.unsubscribeHandler).end(); // 上报地理位置事件 newRouter.rule().async(false).msgType(EVENT).event(WxConsts.EventType.LOCATION).handler(this.locationHandler).end(); // 接收地理位置消息 newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.LOCATION).handler(this.locationHandler).end(); // 扫码事件 newRouter.rule().async(false).msgType(EVENT).event(WxConsts.EventType.SCAN).handler(this.scanHandler).end(); // 默认 newRouter.rule().async(false).handler(this.msgHandler).end(); return newRouter; }
这里直接放在一起了,使用自行区分创建各个类
public abstract class AbstractHandler implements WxMpMessageHandler { protected Logger logger = LoggerFactory.getLogger(getClass()); } @Component public class KfSessionHandler extends AbstractHandler { @Override public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, WxMpService wxMpService, WxSessionManager sessionManager) { //TODO 对会话做处理 return null; } } @Component public class LocationHandler extends AbstractHandler { @Override public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, WxMpService wxMpService, WxSessionManager sessionManager) { if (wxMessage.getMsgType().equals(WxConsts.XmlMsgType.LOCATION)) { //TODO 接收处理用户发送的地理位置消息 try { String content = "感谢反馈,您的的地理位置已收到!"; return new TextBuilder().build(content, wxMessage, null); } catch (Exception e) { this.logger.error("位置消息接收处理失败" , e); return null; } } //上报地理位置事件 this.logger.info("上报地理位置,纬度 : {},经度 : {},精度 : {}" , wxMessage.getLatitude(), wxMessage.getLongitude(), String.valueOf(wxMessage.getPrecision())); //TODO 可以将用户地理位置信息保存到本地数据库,以便以后使用 return null; } } @Component public class LogHandler extends AbstractHandler { @Override public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, WxMpService wxMpService, WxSessionManager sessionManager) { this.logger.info("\n接收到请求消息,内容:{}" , JsonUtils.toJson(wxMessage)); return null; } } @Component public class MenuHandler extends AbstractHandler { @Override public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, WxMpService weixinService, WxSessionManager sessionManager) { String msg = String.format("type:%s, event:%s, key:%s" , wxMessage.getMsgType(), wxMessage.getEvent(), wxMessage.getEventKey()); if (WxConsts.MenuButtonType.VIEW.equals(wxMessage.getEvent())) { return null; } return WxMpXmlOutMessage.TEXT().content(msg) .fromUser(wxMessage.getToUser()).toUser(wxMessage.getFromUser()) .build(); } } @Component public class MsgHandler extends AbstractHandler { @Override public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, WxMpService weixinService, WxSessionManager sessionManager) { if (!wxMessage.getMsgType().equals(WxConsts.XmlMsgType.EVENT)) { //TODO 可以选择将消息保存到本地 } //当用户输入关键词如“你好”,“客服”等,并且有客服在线时,把消息转发给在线客服 try { if (StringUtils.startsWithAny(wxMessage.getContent(), "你好" , "客服") && weixinService.getKefuService().kfOnlineList() .getKfOnlineList().size() > 0) { return WxMpXmlOutMessage.TRANSFER_CUSTOMER_SERVICE() .fromUser(wxMessage.getToUser()) .toUser(wxMessage.getFromUser()).build(); } } catch (WxErrorException e) { e.printStackTrace(); } //TODO 组装回复消息 String content = "收到信息内容:" + JsonUtils.toJson(wxMessage); return new TextBuilder().build(content, wxMessage, weixinService); } } @Component public class NullHandler extends AbstractHandler { @Override public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, WxMpService wxMpService, WxSessionManager sessionManager) { return null; } } @Component public class ScanHandler extends AbstractHandler { @Override public WxMpXmlOutMessage handle(WxMpXmlMessage wxMpXmlMessage, Map<String, Object> map, WxMpService wxMpService, WxSessionManager wxSessionManager) throws WxErrorException { this.logger.info("新关注用户 OPENID: " + wxMpXmlMessage.getFromUser()); // 扫码事件处理 if (wxMpXmlMessage.getEventKey() != null) { System.out.println(wxMpXmlMessage.getEventKey()); } return null; } } @Component public class StoreCheckNotifyHandler extends AbstractHandler { @Override public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, WxMpService wxMpService, WxSessionManager sessionManager) { // TODO 处理门店审核事件 return null; } } @Component public class SubscribeHandler extends AbstractHandler { @Override public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, WxMpService weixinService, WxSessionManager sessionManager) throws WxErrorException { this.logger.info("新关注用户 OPENID: " + wxMessage.getFromUser()); // 获取微信用户基本信息 try { WxMpUser userWxInfo = weixinService.getUserService() .userInfo(wxMessage.getFromUser(), null); // TODO 可以添加关注用户到本地数据库 } catch (WxErrorException e) { if (e.getError().getErrorCode() == 48001) { this.logger.info("该公众号没有获取用户信息权限!"); } } WxMpXmlOutMessage responseResult = null; try { responseResult = this.handleSpecial(wxMessage); } catch (Exception e) { this.logger.error(e.getMessage(), e); } if (responseResult != null) { return responseResult; } try { return new TextBuilder().build("感谢关注" , wxMessage, weixinService); } catch (Exception e) { this.logger.error(e.getMessage(), e); } return null; } /** * 处理特殊请求,比如如果是扫码进来的,可以做相应处理 */ private WxMpXmlOutMessage handleSpecial(WxMpXmlMessage wxMessage) throws Exception { //TODO return null; } } @Component public class UnsubscribeHandler extends AbstractHandler { @Override public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, WxMpService wxMpService, WxSessionManager sessionManager) { String openId = wxMessage.getFromUser(); this.logger.info("取消关注用户 OPENID: " + openId); // TODO 可以更新本地数据库为取消关注状态 return null; } }
对应微信公众平台----》开发----》基本配置 中的 服务器配置
代码:
/** * @desc: 门户入口 * @author: * @date: */ @Slf4j @AllArgsConstructor @RestController @RequestMapping("/wx/portal/{appid}") @Validated public class WxPortalController { @Autowired private WxMpService wxService; @Autowired private WxMpMessageRouter messageRouter; @GetMapping(produces = "text/plain;charset=utf-8") public String authGet(@PathVariable String appid, @RequestParam(name = "signature" , required = false) String signature, @RequestParam(name = "timestamp" , required = false) String timestamp, @RequestParam(name = "nonce" , required = false) String nonce, @RequestParam(name = "echostr" , required = false) String echostr) { log.info("\n接收到来自微信服务器的认证消息:[{}, {}, {}, {}]" , signature, timestamp, nonce, echostr); if (StringUtils.isAnyBlank(signature, timestamp, nonce, echostr)) { throw new IllegalArgumentException("请求参数非法,请核实!"); } if (wxService.checkSignature(timestamp, nonce, signature)) { return echostr; } return "非法请求"; } @PostMapping(produces = "application/xml; charset=UTF-8") public String post(@PathVariable String appid, @RequestBody String requestBody, @RequestParam("signature") String signature, @RequestParam("timestamp") String timestamp, @RequestParam("nonce") String nonce, @RequestParam("openid") String openid, @RequestParam(name = "encrypt_type" , required = false) String encType, @RequestParam(name = "msg_signature" , required = false) String msgSignature) { log.info("\n接收" + "微信请求:[openid=[{}], [signature=[{}], encType=[{}], msgSignature=[{}]," + " timestamp=[{}], nonce=[{}], requestBody=[\n{}\n] " , openid, signature, encType, msgSignature, timestamp, nonce, requestBody); if (!this.wxService.switchover(appid)) { throw new IllegalArgumentException(String.format("未找到对应appid=[%s]的配置,请核实!" , appid)); } if (!wxService.checkSignature(timestamp, nonce, signature)) { throw new IllegalArgumentException("非法请求,可能属于伪造的请求!"); } String out = null; if (encType == null) { // 明文传输的消息 WxMpXmlMessage inMessage = WxMpXmlMessage.fromXml(requestBody); WxMpXmlOutMessage outMessage = this.route(inMessage); if (outMessage == null) { return ""; } out = outMessage.toXml(); } else if ("aes".equalsIgnoreCase(encType)) { // aes加密的消息 WxMpXmlMessage inMessage = WxMpXmlMessage.fromEncryptedXml(requestBody, wxService.getWxMpConfigStorage(), timestamp, nonce, msgSignature); log.debug("\n消息解密后内容为:\n{} " , inMessage.toString()); WxMpXmlOutMessage outMessage = this.route(inMessage); if (outMessage == null) { return ""; } out = outMessage.toEncryptedXml(wxService.getWxMpConfigStorage()); } log.debug("\n组装回复信息:{}" , out); return out; } private WxMpXmlOutMessage route(WxMpXmlMessage message) { try { return this.messageRouter.route(message); } catch (Exception e) { log.error("路由消息时出现异常!" , e); } return null; } }
代码:
@Slf4j @RestController @RequestMapping("/admin/wx") @Validated public class AdminWXController { private final Log logger = LogFactory.getLog(AdminWXController.class); @Autowired private WxMpService wxService; @RequiresPermissions("admin:wx:getMenu") @RequiresPermissionsDesc(menu = {"获取微信菜单"}, button = "配置") @GetMapping("/getMenu") public Object getMenu() throws WxErrorException { WxMpMenu.WxMpConditionalMenu menu = wxService.getMenuService().menuGet().getMenu(); return ResponseUtil.ok(menu); } @RequiresPermissions("admin:wx:saveMenu") @RequiresPermissionsDesc(menu = {"保存微信菜单"}, button = "配置") @PostMapping("/saveMenu") public Object saveMenu(@RequestBody WxMenuModel wxMenuModel) throws WxErrorException { WxMenu wxMenu = new WxMenu(); wxMenu.setButtons(wxMenuModel.getButton()); log.info("本次微信菜单配置参数{}", wxMenu.toString()); return ResponseUtil.ok(wxService.getMenuService().menuCreate(wxMenu)); } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。