mirror of
				https://github.com/YunaiV/ruoyi-vue-pro.git
				synced 2025-10-28 20:43:38 +08:00 
			
		
		
		
	mp:增加 MpServiceFactory 管理创建的 MpService~
This commit is contained in:
		| @ -5,7 +5,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; | |||||||
| import cn.iocoder.yudao.module.mp.controller.admin.account.vo.*; | import cn.iocoder.yudao.module.mp.controller.admin.account.vo.*; | ||||||
| import cn.iocoder.yudao.module.mp.convert.account.MpAccountConvert; | import cn.iocoder.yudao.module.mp.convert.account.MpAccountConvert; | ||||||
| import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO; | import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO; | ||||||
| import cn.iocoder.yudao.module.mp.service.account.WxAccountService; | import cn.iocoder.yudao.module.mp.service.account.MpAccountService; | ||||||
| import io.swagger.annotations.Api; | import io.swagger.annotations.Api; | ||||||
| import io.swagger.annotations.ApiImplicitParam; | import io.swagger.annotations.ApiImplicitParam; | ||||||
| import io.swagger.annotations.ApiOperation; | import io.swagger.annotations.ApiOperation; | ||||||
| @ -25,7 +25,7 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | |||||||
| public class MpAccountController { | public class MpAccountController { | ||||||
|  |  | ||||||
|     @Resource |     @Resource | ||||||
|     private WxAccountService wxAccountService; |     private MpAccountService wxAccountService; | ||||||
|  |  | ||||||
|     @PostMapping("/create") |     @PostMapping("/create") | ||||||
|     @ApiOperation("创建公众号账户") |     @ApiOperation("创建公众号账户") | ||||||
|  | |||||||
| @ -63,13 +63,4 @@ public class MpAccountDO extends BaseDO { | |||||||
|      */ |      */ | ||||||
|     private String remark; |     private String remark; | ||||||
|  |  | ||||||
|     // TODO 芋艿:需要迁移走 |  | ||||||
|     public WxMpConfigStorage toWxMpConfigStorage(RedisTemplateWxRedisOps redisTemplateWxRedisOps, WxMpProperties wxMpProperties) { |  | ||||||
|         WxMpRedisConfigImpl wxMpRedisConfig = new WxMpRedisConfigImpl(redisTemplateWxRedisOps, wxMpProperties.getConfigStorage().getKeyPrefix()); |  | ||||||
|         wxMpRedisConfig.setAppId(appId); |  | ||||||
|         wxMpRedisConfig.setSecret(appSecret); |  | ||||||
|         wxMpRedisConfig.setToken(token); |  | ||||||
|         wxMpRedisConfig.setAesKey(aesKey); |  | ||||||
|         return wxMpRedisConfig; |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  | |||||||
| @ -6,6 +6,9 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; | |||||||
| import cn.iocoder.yudao.module.mp.controller.admin.account.vo.MpAccountPageReqVO; | import cn.iocoder.yudao.module.mp.controller.admin.account.vo.MpAccountPageReqVO; | ||||||
| import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO; | import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO; | ||||||
| import org.apache.ibatis.annotations.Mapper; | import org.apache.ibatis.annotations.Mapper; | ||||||
|  | import org.apache.ibatis.annotations.Select; | ||||||
|  |  | ||||||
|  | import java.time.LocalDateTime; | ||||||
|  |  | ||||||
| @Mapper | @Mapper | ||||||
| public interface MpAccountMapper extends BaseMapperX<MpAccountDO> { | public interface MpAccountMapper extends BaseMapperX<MpAccountDO> { | ||||||
| @ -18,4 +21,7 @@ public interface MpAccountMapper extends BaseMapperX<MpAccountDO> { | |||||||
|                 .orderByDesc(MpAccountDO::getId)); |                 .orderByDesc(MpAccountDO::getId)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     @Select("SELECT COUNT(*) FROM mp_account WHERE update_time > #{maxUpdateTime}") | ||||||
|  |     Long selectCountByUpdateTimeGt(LocalDateTime maxUpdateTime); | ||||||
|  |  | ||||||
| } | } | ||||||
|  | |||||||
| @ -0,0 +1,46 @@ | |||||||
|  | package cn.iocoder.yudao.module.mp.framework.mp.config; | ||||||
|  |  | ||||||
|  | import cn.iocoder.yudao.module.mp.framework.mp.core.DefaultMpServiceFactory; | ||||||
|  | import cn.iocoder.yudao.module.mp.framework.mp.core.MpServiceFactory; | ||||||
|  | import cn.iocoder.yudao.module.mp.service.fansmsgres.NullHandler; | ||||||
|  | import cn.iocoder.yudao.module.mp.service.handler.*; | ||||||
|  | import com.binarywang.spring.starter.wxjava.mp.properties.WxMpProperties; | ||||||
|  | import me.chanjar.weixin.common.redis.RedisTemplateWxRedisOps; | ||||||
|  | import org.springframework.context.annotation.Bean; | ||||||
|  | import org.springframework.context.annotation.Configuration; | ||||||
|  | import org.springframework.data.redis.core.StringRedisTemplate; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 微信公众号的配置类 | ||||||
|  |  * | ||||||
|  |  * @author 芋道源码 | ||||||
|  |  */ | ||||||
|  | @Configuration | ||||||
|  | public class MpConfiguration { | ||||||
|  |  | ||||||
|  |     @Bean | ||||||
|  |     @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") | ||||||
|  |     public RedisTemplateWxRedisOps redisTemplateWxRedisOps(StringRedisTemplate stringRedisTemplate) { | ||||||
|  |         return new RedisTemplateWxRedisOps(stringRedisTemplate); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Bean | ||||||
|  |     @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") | ||||||
|  |     public MpServiceFactory mpServiceFactory(RedisTemplateWxRedisOps redisTemplateWxRedisOps, | ||||||
|  |                                              WxMpProperties wxMpProperties, | ||||||
|  |                                              LogHandler logHandler, | ||||||
|  |                                              KfSessionHandler kfSessionHandler, | ||||||
|  |                                              StoreCheckNotifyHandler storeCheckNotifyHandler, | ||||||
|  |                                              MenuHandler menuHandler, | ||||||
|  |                                              NullHandler nullHandler, | ||||||
|  |                                              SubscribeHandler subscribeHandler, | ||||||
|  |                                              UnsubscribeHandler unsubscribeHandler, | ||||||
|  |                                              LocationHandler locationHandler, | ||||||
|  |                                              ScanHandler scanHandler, | ||||||
|  |                                              DefaultMessageHandler msgHandler) { | ||||||
|  |         return new DefaultMpServiceFactory(redisTemplateWxRedisOps, wxMpProperties, | ||||||
|  |                 logHandler, kfSessionHandler, storeCheckNotifyHandler, menuHandler, | ||||||
|  |                 nullHandler, subscribeHandler, unsubscribeHandler, locationHandler, scanHandler, msgHandler); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @ -0,0 +1,158 @@ | |||||||
|  | package cn.iocoder.yudao.module.mp.framework.mp.core; | ||||||
|  |  | ||||||
|  | import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO; | ||||||
|  | import cn.iocoder.yudao.module.mp.service.fansmsgres.NullHandler; | ||||||
|  | import cn.iocoder.yudao.module.mp.service.handler.*; | ||||||
|  | import com.binarywang.spring.starter.wxjava.mp.properties.WxMpProperties; | ||||||
|  | import com.google.common.collect.Maps; | ||||||
|  | import lombok.RequiredArgsConstructor; | ||||||
|  | import lombok.extern.slf4j.Slf4j; | ||||||
|  | import me.chanjar.weixin.common.api.WxConsts; | ||||||
|  | import me.chanjar.weixin.common.redis.RedisTemplateWxRedisOps; | ||||||
|  | import me.chanjar.weixin.mp.api.WxMpMessageRouter; | ||||||
|  | import me.chanjar.weixin.mp.api.WxMpService; | ||||||
|  | import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl; | ||||||
|  | import me.chanjar.weixin.mp.config.impl.WxMpRedisConfigImpl; | ||||||
|  | import me.chanjar.weixin.mp.constant.WxMpEventConstants; | ||||||
|  |  | ||||||
|  | import java.util.List; | ||||||
|  | import java.util.Map; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 默认的 {@link MpServiceFactory} 实现类 | ||||||
|  |  * | ||||||
|  |  * @author 芋道源码 | ||||||
|  |  */ | ||||||
|  | @Slf4j | ||||||
|  | @RequiredArgsConstructor | ||||||
|  | public class DefaultMpServiceFactory implements MpServiceFactory { | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 微信 appId 与 WxMpService 的映射 | ||||||
|  |      */ | ||||||
|  |     private volatile Map<String, WxMpService> mpServices; | ||||||
|  |     /** | ||||||
|  |      * 微信 appId 与 WxMpMessageRouter 的映射 | ||||||
|  |      */ | ||||||
|  |     private volatile Map<String, WxMpMessageRouter> mpMessageRouters; | ||||||
|  |  | ||||||
|  |     private final RedisTemplateWxRedisOps redisTemplateWxRedisOps; | ||||||
|  |     private final WxMpProperties mpProperties; | ||||||
|  |  | ||||||
|  |     // ========== 各种 Handler ========== | ||||||
|  |  | ||||||
|  |     private final LogHandler logHandler; | ||||||
|  |     private final KfSessionHandler kfSessionHandler; | ||||||
|  |     private final StoreCheckNotifyHandler storeCheckNotifyHandler; | ||||||
|  |     private final MenuHandler menuHandler; | ||||||
|  |     private final NullHandler nullHandler; | ||||||
|  |     private final SubscribeHandler subscribeHandler; | ||||||
|  |     private final UnsubscribeHandler unsubscribeHandler; | ||||||
|  |     private final LocationHandler locationHandler; | ||||||
|  |     private final ScanHandler scanHandler; | ||||||
|  |     private final DefaultMessageHandler msgHandler; | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void init(List<MpAccountDO> list) { | ||||||
|  |         Map<String, WxMpService> mpServices = Maps.newHashMap(); | ||||||
|  |         Map<String, WxMpMessageRouter> mpMessageRouters = Maps.newHashMap(); | ||||||
|  |         // 处理 list | ||||||
|  |         list.forEach(account -> { | ||||||
|  |             // 构建 WxMpService 对象 | ||||||
|  |             WxMpService mpService = buildMpService(account); | ||||||
|  |             mpServices.put(account.getAppId(), mpService); | ||||||
|  |             // 构建 WxMpMessageRouter 对象 | ||||||
|  |             WxMpMessageRouter mpMessageRouter = buildMpMessageRouter(mpService); | ||||||
|  |             mpMessageRouters.put(account.getAppId(), mpMessageRouter); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         // 设置到缓存 | ||||||
|  |         this.mpServices = mpServices; | ||||||
|  |         this.mpMessageRouters = mpMessageRouters; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public WxMpService getMpService(String appId) { | ||||||
|  |         return mpServices.get(appId); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public WxMpMessageRouter getMpMessageRouter(String appId) { | ||||||
|  |         return mpMessageRouters.get(appId); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private WxMpService buildMpService(MpAccountDO account) { | ||||||
|  |         // 第一步,创建 WxMpRedisConfigImpl 对象 | ||||||
|  |         // TODO 芋艿:需要确认下,redis key 的存储结构 | ||||||
|  |         WxMpRedisConfigImpl configStorage = new WxMpRedisConfigImpl( | ||||||
|  |                 redisTemplateWxRedisOps, mpProperties.getConfigStorage().getKeyPrefix()); | ||||||
|  |         configStorage.setAppId(account.getAppId()); | ||||||
|  |         configStorage.setSecret(account.getAppSecret()); | ||||||
|  |         configStorage.setToken(account.getToken()); | ||||||
|  |         configStorage.setAesKey(account.getAesKey()); | ||||||
|  |  | ||||||
|  |         // 第二步,创建 WxMpService 对象 | ||||||
|  |         WxMpService service = new WxMpServiceImpl(); | ||||||
|  |         service.setWxMpConfigStorage(configStorage); | ||||||
|  |         return null; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private WxMpMessageRouter buildMpMessageRouter(WxMpService mpService) { | ||||||
|  |         final WxMpMessageRouter newRouter = new WxMpMessageRouter(mpService); | ||||||
|  |         // 记录所有事件的日志(异步执行) | ||||||
|  |         newRouter.rule().handler(logHandler).next(); | ||||||
|  |  | ||||||
|  |         // 接收客服会话管理事件 | ||||||
|  |         newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) | ||||||
|  |                 .event(WxMpEventConstants.CustomerService.KF_CREATE_SESSION) | ||||||
|  |                 .handler(kfSessionHandler).end(); | ||||||
|  |         newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) | ||||||
|  |                 .event(WxMpEventConstants.CustomerService.KF_CLOSE_SESSION) | ||||||
|  |                 .handler(kfSessionHandler) | ||||||
|  |                 .end(); | ||||||
|  |         newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) | ||||||
|  |                 .event(WxMpEventConstants.CustomerService.KF_SWITCH_SESSION) | ||||||
|  |                 .handler(kfSessionHandler).end(); | ||||||
|  |  | ||||||
|  |         // 门店审核事件 | ||||||
|  |         newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) | ||||||
|  |                 .event(WxMpEventConstants.POI_CHECK_NOTIFY) | ||||||
|  |                 .handler(storeCheckNotifyHandler).end(); | ||||||
|  |  | ||||||
|  |         // 自定义菜单事件 | ||||||
|  |         newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) | ||||||
|  |                 .event(WxConsts.MenuButtonType.CLICK).handler(menuHandler).end(); | ||||||
|  |  | ||||||
|  |         // 点击菜单连接事件 | ||||||
|  |         newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) | ||||||
|  |                 .event(WxConsts.MenuButtonType.VIEW).handler(nullHandler).end(); | ||||||
|  |  | ||||||
|  |         // 关注事件 | ||||||
|  |         newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) | ||||||
|  |                 .event(WxConsts.EventType.SUBSCRIBE).handler(subscribeHandler) | ||||||
|  |                 .end(); | ||||||
|  |  | ||||||
|  |         // 取消关注事件 | ||||||
|  |         newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) | ||||||
|  |                 .event(WxConsts.EventType.UNSUBSCRIBE) | ||||||
|  |                 .handler(unsubscribeHandler).end(); | ||||||
|  |  | ||||||
|  |         // 上报地理位置事件 | ||||||
|  |         newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) | ||||||
|  |                 .event(WxConsts.EventType.LOCATION).handler(locationHandler) | ||||||
|  |                 .end(); | ||||||
|  |  | ||||||
|  |         // 接收地理位置消息 | ||||||
|  |         newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.LOCATION) | ||||||
|  |                 .handler(locationHandler).end(); | ||||||
|  |  | ||||||
|  |         // 扫码事件 | ||||||
|  |         newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) | ||||||
|  |                 .event(WxConsts.EventType.SCAN).handler(scanHandler).end(); | ||||||
|  |  | ||||||
|  |         // 默认 | ||||||
|  |         newRouter.rule().async(false).handler(msgHandler).end(); | ||||||
|  |         return newRouter; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @ -0,0 +1,38 @@ | |||||||
|  | package cn.iocoder.yudao.module.mp.framework.mp.core; | ||||||
|  |  | ||||||
|  | import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO; | ||||||
|  | import me.chanjar.weixin.mp.api.WxMpMessageRouter; | ||||||
|  | import me.chanjar.weixin.mp.api.WxMpService; | ||||||
|  |  | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * {@link WxMpService} 工厂接口 | ||||||
|  |  * | ||||||
|  |  * @author 芋道源码 | ||||||
|  |  */ | ||||||
|  | public interface MpServiceFactory { | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 基于微信公众号的账号,初始化对应的 WxMpService 与 WxMpMessageRouter 实例 | ||||||
|  |      * | ||||||
|  |      * @param list 公众号的账号列表 | ||||||
|  |      */ | ||||||
|  |     void init(List<MpAccountDO> list); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 获得 appId 对应的 WxMpService 实例 | ||||||
|  |      * | ||||||
|  |      * @param appId 微信公众号 appId | ||||||
|  |      * @return WxMpService 实例 | ||||||
|  |      */ | ||||||
|  |     WxMpService getMpService(String appId); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 获得 appId 对应的 WxMpMessageRouter 实例 | ||||||
|  |      * | ||||||
|  |      * @param appId 微信公众号 appId | ||||||
|  |      * @return WxMpMessageRouter 实例 | ||||||
|  |      */ | ||||||
|  |     WxMpMessageRouter getMpMessageRouter(String appId); | ||||||
|  | } | ||||||
| @ -1,6 +1,6 @@ | |||||||
| /** | /** | ||||||
|  * @date 2022/6/20 |  * 属于 mp 模块的 framework 封装 | ||||||
|  * @author feng-dan |  * | ||||||
|  * @version 1.0 |  * @author 芋道源码 | ||||||
|  */ |  */ | ||||||
| package cn.iocoder.yudao.module.mp.framework; | package cn.iocoder.yudao.module.mp.framework; | ||||||
|  | |||||||
| @ -1,106 +0,0 @@ | |||||||
| package cn.iocoder.yudao.module.mp.framework.weixin; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| import cn.iocoder.yudao.module.mp.handler.*; |  | ||||||
| import lombok.RequiredArgsConstructor; |  | ||||||
| import lombok.extern.slf4j.Slf4j; |  | ||||||
| import me.chanjar.weixin.common.api.WxConsts; |  | ||||||
| import me.chanjar.weixin.mp.api.WxMpMessageRouter; |  | ||||||
| import me.chanjar.weixin.mp.api.WxMpService; |  | ||||||
| import me.chanjar.weixin.mp.constant.WxMpEventConstants; |  | ||||||
| import org.springframework.context.annotation.Bean; |  | ||||||
| import org.springframework.stereotype.Component; |  | ||||||
|  |  | ||||||
| import javax.annotation.Resource; |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * @author fengdan |  | ||||||
|  */ |  | ||||||
| @Slf4j |  | ||||||
| @Component |  | ||||||
| @RequiredArgsConstructor |  | ||||||
| public class WxMpMessageRouterConfiguration { |  | ||||||
|  |  | ||||||
|     @Resource |  | ||||||
|     private WxMpService wxMpService; |  | ||||||
|     @Resource |  | ||||||
|     private LogHandler logHandler; |  | ||||||
|     @Resource |  | ||||||
|     private KfSessionHandler kfSessionHandler; |  | ||||||
|     @Resource |  | ||||||
|     private StoreCheckNotifyHandler storeCheckNotifyHandler; |  | ||||||
|     @Resource |  | ||||||
|     private MenuHandler menuHandler; |  | ||||||
|     @Resource |  | ||||||
|     private NullHandler nullHandler; |  | ||||||
|     @Resource |  | ||||||
|     private SubscribeHandler subscribeHandler; |  | ||||||
|     @Resource |  | ||||||
|     private UnsubscribeHandler unsubscribeHandler; |  | ||||||
|     @Resource |  | ||||||
|     private LocationHandler locationHandler; |  | ||||||
|     @Resource |  | ||||||
|     private ScanHandler scanHandler; |  | ||||||
|     @Resource |  | ||||||
|     private MsgHandler msgHandler; |  | ||||||
|  |  | ||||||
|     @Bean |  | ||||||
|     public WxMpMessageRouter messageRouter() { |  | ||||||
|         final WxMpMessageRouter newRouter = new WxMpMessageRouter(wxMpService); |  | ||||||
|         // 记录所有事件的日志 (异步执行) |  | ||||||
|         newRouter.rule().handler(logHandler).next(); |  | ||||||
|  |  | ||||||
|         // 接收客服会话管理事件 |  | ||||||
|         newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) |  | ||||||
|                 .event(WxMpEventConstants.CustomerService.KF_CREATE_SESSION) |  | ||||||
|                 .handler(kfSessionHandler).end(); |  | ||||||
|         newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) |  | ||||||
|                 .event(WxMpEventConstants.CustomerService.KF_CLOSE_SESSION) |  | ||||||
|                 .handler(kfSessionHandler) |  | ||||||
|                 .end(); |  | ||||||
|         newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) |  | ||||||
|                 .event(WxMpEventConstants.CustomerService.KF_SWITCH_SESSION) |  | ||||||
|                 .handler(kfSessionHandler).end(); |  | ||||||
|  |  | ||||||
|         // 门店审核事件 |  | ||||||
|         newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) |  | ||||||
|                 .event(WxMpEventConstants.POI_CHECK_NOTIFY) |  | ||||||
|                 .handler(storeCheckNotifyHandler).end(); |  | ||||||
|  |  | ||||||
|         // 自定义菜单事件 |  | ||||||
|         newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) |  | ||||||
|                 .event(WxConsts.MenuButtonType.CLICK).handler(menuHandler).end(); |  | ||||||
|  |  | ||||||
|         // 点击菜单连接事件 |  | ||||||
|         newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) |  | ||||||
|                 .event(WxConsts.MenuButtonType.VIEW).handler(nullHandler).end(); |  | ||||||
|  |  | ||||||
|         // 关注事件 |  | ||||||
|         newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) |  | ||||||
|                 .event(WxConsts.EventType.SUBSCRIBE).handler(subscribeHandler) |  | ||||||
|                 .end(); |  | ||||||
|  |  | ||||||
|         // 取消关注事件 |  | ||||||
|         newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) |  | ||||||
|                 .event(WxConsts.EventType.UNSUBSCRIBE) |  | ||||||
|                 .handler(unsubscribeHandler).end(); |  | ||||||
|  |  | ||||||
|         // 上报地理位置事件 |  | ||||||
|         newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) |  | ||||||
|                 .event(WxConsts.EventType.LOCATION).handler(locationHandler) |  | ||||||
|                 .end(); |  | ||||||
|  |  | ||||||
|         // 接收地理位置消息 |  | ||||||
|         newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.LOCATION) |  | ||||||
|                 .handler(locationHandler).end(); |  | ||||||
|  |  | ||||||
|         // 扫码事件 |  | ||||||
|         newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) |  | ||||||
|                 .event(WxConsts.EventType.SCAN).handler(scanHandler).end(); |  | ||||||
|  |  | ||||||
|         // 默认 |  | ||||||
|         newRouter.rule().async(false).handler(msgHandler).end(); |  | ||||||
|  |  | ||||||
|         return newRouter; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -0,0 +1,29 @@ | |||||||
|  | package cn.iocoder.yudao.module.mp.mq.costomer; | ||||||
|  |  | ||||||
|  | import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener; | ||||||
|  | import cn.iocoder.yudao.module.mp.mq.message.MpConfigRefreshMessage; | ||||||
|  | import cn.iocoder.yudao.module.mp.service.account.MpAccountService; | ||||||
|  | import lombok.extern.slf4j.Slf4j; | ||||||
|  | import org.springframework.stereotype.Component; | ||||||
|  |  | ||||||
|  | import javax.annotation.Resource; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 针对 {@link MpConfigRefreshMessage} 的消费者 | ||||||
|  |  * | ||||||
|  |  * @author lyz | ||||||
|  |  */ | ||||||
|  | @Component | ||||||
|  | @Slf4j | ||||||
|  | public class MpConfigRefreshConsumer extends AbstractChannelMessageListener<MpConfigRefreshMessage> { | ||||||
|  |  | ||||||
|  |     @Resource | ||||||
|  |     private MpAccountService wxAccountService; | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void onMessage(MpConfigRefreshMessage message) { | ||||||
|  |         log.info("[onMessage][收到 MpConfig 刷新消息]"); | ||||||
|  |         wxAccountService.initLocalCache(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @ -1,29 +0,0 @@ | |||||||
| package cn.iocoder.yudao.module.mp.mq.costomer; |  | ||||||
|  |  | ||||||
| import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener; |  | ||||||
| import cn.iocoder.yudao.module.mp.mq.message.WxConfigDataRefreshMessage; |  | ||||||
| import cn.iocoder.yudao.module.mp.service.account.WxAccountService; |  | ||||||
| import lombok.extern.slf4j.Slf4j; |  | ||||||
| import org.springframework.stereotype.Component; |  | ||||||
|  |  | ||||||
| import javax.annotation.Resource; |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * 针对 {@link WxConfigDataRefreshMessage} 的消费者 |  | ||||||
|  * |  | ||||||
|  * @author lyz |  | ||||||
|  */ |  | ||||||
| @Component |  | ||||||
| @Slf4j |  | ||||||
| public class WxConfigDataRefreshConsumer extends AbstractChannelMessageListener<WxConfigDataRefreshMessage> { |  | ||||||
|  |  | ||||||
|     @Resource |  | ||||||
|     private WxAccountService wxAccountService; |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public void onMessage(WxConfigDataRefreshMessage message) { |  | ||||||
|         log.info("[onMessage][收到 WxConfigData 刷新消息]"); |  | ||||||
|         wxAccountService.initLoadWxMpConfigStorages(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| } |  | ||||||
| @ -9,11 +9,11 @@ import lombok.EqualsAndHashCode; | |||||||
|  */ |  */ | ||||||
| @Data | @Data | ||||||
| @EqualsAndHashCode(callSuper = true) | @EqualsAndHashCode(callSuper = true) | ||||||
| public class WxConfigDataRefreshMessage extends AbstractChannelMessage { | public class MpConfigRefreshMessage extends AbstractChannelMessage { | ||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|     public String getChannel() { |     public String getChannel() { | ||||||
|         return "wechat-mp.wx-config-data.refresh"; |         return "mp.config-data.refresh"; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
| @ -1,7 +1,7 @@ | |||||||
| package cn.iocoder.yudao.module.mp.mq.producer; | package cn.iocoder.yudao.module.mp.mq.producer; | ||||||
| 
 | 
 | ||||||
| import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate; | import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate; | ||||||
| import cn.iocoder.yudao.module.mp.mq.message.WxConfigDataRefreshMessage; | import cn.iocoder.yudao.module.mp.mq.message.MpConfigRefreshMessage; | ||||||
| import org.springframework.stereotype.Component; | import org.springframework.stereotype.Component; | ||||||
| 
 | 
 | ||||||
| import javax.annotation.Resource; | import javax.annotation.Resource; | ||||||
| @ -10,16 +10,16 @@ import javax.annotation.Resource; | |||||||
|  * 微信配置数据相关消息的 Producer |  * 微信配置数据相关消息的 Producer | ||||||
|  */ |  */ | ||||||
| @Component | @Component | ||||||
| public class WxMpConfigDataProducer { | public class MpConfigProducer { | ||||||
| 
 | 
 | ||||||
|     @Resource |     @Resource | ||||||
|     private RedisMQTemplate redisMQTemplate; |     private RedisMQTemplate redisMQTemplate; | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * 发送 {@link WxConfigDataRefreshMessage} 消息 |      * 发送 {@link MpConfigRefreshMessage} 消息 | ||||||
|      */ |      */ | ||||||
|     public void sendDictDataRefreshMessage() { |     public void sendDictDataRefreshMessage() { | ||||||
|         WxConfigDataRefreshMessage message = new WxConfigDataRefreshMessage(); |         MpConfigRefreshMessage message = new MpConfigRefreshMessage(); | ||||||
|         redisMQTemplate.send(message); |         redisMQTemplate.send(message); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -14,7 +14,12 @@ import javax.validation.Valid; | |||||||
|  * |  * | ||||||
|  * @author 芋道源码 |  * @author 芋道源码 | ||||||
|  */ |  */ | ||||||
| public interface WxAccountService { | public interface MpAccountService { | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 初始化缓存 | ||||||
|  |      */ | ||||||
|  |     void initLocalCache(); | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * 创建公众号账户 |      * 创建公众号账户 | ||||||
| @ -64,8 +69,4 @@ public interface WxAccountService { | |||||||
|      */ |      */ | ||||||
|     MpAccountDO findBy(SFunction<MpAccountDO, ?> field, Object val); |     MpAccountDO findBy(SFunction<MpAccountDO, ?> field, Object val); | ||||||
| 
 | 
 | ||||||
|     /** |  | ||||||
|      * 初始化 |  | ||||||
|      */ |  | ||||||
|     void initLoadWxMpConfigStorages(); |  | ||||||
| } | } | ||||||
| @ -0,0 +1,159 @@ | |||||||
|  | package cn.iocoder.yudao.module.mp.service.account; | ||||||
|  |  | ||||||
|  | import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; | ||||||
|  | import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||||||
|  | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; | ||||||
|  | import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; | ||||||
|  | import cn.iocoder.yudao.module.mp.controller.admin.account.vo.MpAccountCreateReqVO; | ||||||
|  | import cn.iocoder.yudao.module.mp.controller.admin.account.vo.MpAccountPageReqVO; | ||||||
|  | import cn.iocoder.yudao.module.mp.controller.admin.account.vo.MpAccountUpdateReqVO; | ||||||
|  | import cn.iocoder.yudao.module.mp.convert.account.MpAccountConvert; | ||||||
|  | import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO; | ||||||
|  | import cn.iocoder.yudao.module.mp.dal.mysql.account.MpAccountMapper; | ||||||
|  | import cn.iocoder.yudao.module.mp.enums.ErrorCodeConstants; | ||||||
|  | import cn.iocoder.yudao.module.mp.framework.mp.core.MpServiceFactory; | ||||||
|  | import cn.iocoder.yudao.module.mp.mq.producer.MpConfigProducer; | ||||||
|  | import com.baomidou.mybatisplus.core.toolkit.support.SFunction; | ||||||
|  | import lombok.Getter; | ||||||
|  | import lombok.extern.slf4j.Slf4j; | ||||||
|  | import org.springframework.context.annotation.Lazy; | ||||||
|  | import org.springframework.scheduling.annotation.Scheduled; | ||||||
|  | import org.springframework.stereotype.Service; | ||||||
|  | import org.springframework.validation.annotation.Validated; | ||||||
|  |  | ||||||
|  | import javax.annotation.PostConstruct; | ||||||
|  | import javax.annotation.Resource; | ||||||
|  | import java.time.LocalDateTime; | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 公众号账户 Service 实现类 | ||||||
|  |  * | ||||||
|  |  * @author fengdan | ||||||
|  |  */ | ||||||
|  | @Slf4j | ||||||
|  | @Service | ||||||
|  | @Validated | ||||||
|  | public class MpAccountServiceImpl implements MpAccountService { | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 定时执行 {@link #schedulePeriodicRefresh()} 的周期 | ||||||
|  |      * 因为已经通过 Redis Pub/Sub 机制,所以频率不需要高 | ||||||
|  |      */ | ||||||
|  |     private static final long SCHEDULER_PERIOD = 5 * 60 * 1000L; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 缓存菜单的最大更新时间,用于后续的增量轮询,判断是否有更新 | ||||||
|  |      */ | ||||||
|  |     @Getter | ||||||
|  |     private volatile LocalDateTime maxUpdateTime; | ||||||
|  |  | ||||||
|  |     @Resource | ||||||
|  |     private MpAccountMapper mpAccountMapper; | ||||||
|  |  | ||||||
|  |     @Resource | ||||||
|  |     @Lazy // 延迟加载,解决循环依赖的问题 | ||||||
|  |     private MpServiceFactory mpServiceFactory; | ||||||
|  |  | ||||||
|  |     @Resource | ||||||
|  |     private MpConfigProducer mpConfigDataProducer; | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     @PostConstruct | ||||||
|  |     public void initLocalCache() { | ||||||
|  |         initLocalCacheIfUpdate(null); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD) | ||||||
|  |     public void schedulePeriodicRefresh() { | ||||||
|  |         initLocalCacheIfUpdate(this.maxUpdateTime); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 刷新本地缓存 | ||||||
|  |      * | ||||||
|  |      * @param maxUpdateTime 最大更新时间 | ||||||
|  |      *                      1. 如果 maxUpdateTime 为 null,则“强制”刷新缓存 | ||||||
|  |      *                      2. 如果 maxUpdateTime 不为 null,判断自 maxUpdateTime 是否有数据发生变化,有的情况下才刷新缓存 | ||||||
|  |      */ | ||||||
|  |     private void initLocalCacheIfUpdate(LocalDateTime maxUpdateTime) { | ||||||
|  |         // 注意:忽略自动多租户,因为要全局初始化缓存 | ||||||
|  |         TenantUtils.executeIgnore(() -> { | ||||||
|  |             // 第一步:基于 maxUpdateTime 判断缓存是否刷新。 | ||||||
|  |             // 如果没有增量的数据变化,则不进行本地缓存的刷新 | ||||||
|  |             if (maxUpdateTime != null | ||||||
|  |                     && mpAccountMapper.selectCountByUpdateTimeGt(maxUpdateTime) == 0) { | ||||||
|  |                 log.info("[initLocalCacheIfUpdate][数据未发生变化({}),本地缓存不刷新]", maxUpdateTime); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |             List<MpAccountDO> accounts = mpAccountMapper.selectList(); | ||||||
|  |             log.info("[initLocalCacheIfUpdate][缓存公众号账号,数量为:{}]", accounts.size()); | ||||||
|  |  | ||||||
|  |             // 第二步:构建缓存。创建或更新支付 Client | ||||||
|  |             mpServiceFactory.init(accounts); | ||||||
|  |  | ||||||
|  |             // 第三步:设置最新的 maxUpdateTime,用于下次的增量判断。 | ||||||
|  |             this.maxUpdateTime = CollectionUtils.getMaxValue(accounts, MpAccountDO::getUpdateTime); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public Long createAccount(MpAccountCreateReqVO createReqVO) { | ||||||
|  |         // TODO 芋艿:校验唯一性 | ||||||
|  |         // 插入 | ||||||
|  |         MpAccountDO wxAccount = MpAccountConvert.INSTANCE.convert(createReqVO); | ||||||
|  |         mpAccountMapper.insert(wxAccount); | ||||||
|  |  | ||||||
|  |         // TODO 芋艿:刷新的方式 | ||||||
|  |         mpConfigDataProducer.sendDictDataRefreshMessage(); | ||||||
|  |         // 返回 | ||||||
|  |         return wxAccount.getId(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void updateAccount(MpAccountUpdateReqVO updateReqVO) { | ||||||
|  |         // TODO 芋艿:校验唯一性 | ||||||
|  |         // 校验存在 | ||||||
|  |         validateWxAccountExists(updateReqVO.getId()); | ||||||
|  |         // 更新 | ||||||
|  |         MpAccountDO updateObj = MpAccountConvert.INSTANCE.convert(updateReqVO); | ||||||
|  |         mpAccountMapper.updateById(updateObj); | ||||||
|  |  | ||||||
|  |         // TODO 芋艿:刷新的方式 | ||||||
|  |         mpConfigDataProducer.sendDictDataRefreshMessage(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void deleteAccount(Long id) { | ||||||
|  |         // 校验存在 | ||||||
|  |         validateWxAccountExists(id); | ||||||
|  |         // 删除 | ||||||
|  |         mpAccountMapper.deleteById(id); | ||||||
|  |  | ||||||
|  |         // TODO 芋艿:刷新的方式 | ||||||
|  |         mpConfigDataProducer.sendDictDataRefreshMessage(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private void validateWxAccountExists(Long id) { | ||||||
|  |         if (mpAccountMapper.selectById(id) == null) { | ||||||
|  |             throw ServiceExceptionUtil.exception(ErrorCodeConstants.WX_ACCOUNT_NOT_EXISTS); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public MpAccountDO getAccount(Long id) { | ||||||
|  |         return mpAccountMapper.selectById(id); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public PageResult<MpAccountDO> getAccountPage(MpAccountPageReqVO pageReqVO) { | ||||||
|  |         return mpAccountMapper.selectPage(pageReqVO); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public MpAccountDO findBy(SFunction<MpAccountDO, ?> field, Object val) { | ||||||
|  |         return mpAccountMapper.selectOne(field, val); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @ -1,157 +0,0 @@ | |||||||
| package cn.iocoder.yudao.module.mp.service.account; |  | ||||||
|  |  | ||||||
| import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; |  | ||||||
| import cn.iocoder.yudao.framework.common.pojo.PageResult; |  | ||||||
| import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; |  | ||||||
| import cn.iocoder.yudao.module.mp.controller.admin.account.vo.MpAccountCreateReqVO; |  | ||||||
| import cn.iocoder.yudao.module.mp.controller.admin.account.vo.MpAccountPageReqVO; |  | ||||||
| import cn.iocoder.yudao.module.mp.controller.admin.account.vo.MpAccountUpdateReqVO; |  | ||||||
| import cn.iocoder.yudao.module.mp.convert.account.MpAccountConvert; |  | ||||||
| import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO; |  | ||||||
| import cn.iocoder.yudao.module.mp.dal.mysql.account.MpAccountMapper; |  | ||||||
| import cn.iocoder.yudao.module.mp.enums.ErrorCodeConstants; |  | ||||||
| import cn.iocoder.yudao.module.mp.mq.producer.WxMpConfigDataProducer; |  | ||||||
| import com.baomidou.mybatisplus.core.toolkit.support.SFunction; |  | ||||||
| import com.binarywang.spring.starter.wxjava.mp.properties.WxMpProperties; |  | ||||||
| import lombok.extern.slf4j.Slf4j; |  | ||||||
| import me.chanjar.weixin.common.redis.RedisTemplateWxRedisOps; |  | ||||||
| import me.chanjar.weixin.mp.api.WxMpService; |  | ||||||
| import me.chanjar.weixin.mp.config.WxMpConfigStorage; |  | ||||||
| import org.springframework.context.annotation.Lazy; |  | ||||||
| import org.springframework.data.redis.core.StringRedisTemplate; |  | ||||||
| import org.springframework.scheduling.annotation.Scheduled; |  | ||||||
| import org.springframework.stereotype.Service; |  | ||||||
| import org.springframework.util.CollectionUtils; |  | ||||||
| import org.springframework.validation.annotation.Validated; |  | ||||||
|  |  | ||||||
| import javax.annotation.PostConstruct; |  | ||||||
| import javax.annotation.Resource; |  | ||||||
| import java.util.HashMap; |  | ||||||
| import java.util.List; |  | ||||||
| import java.util.Map; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * 公众号账户 Service 实现类 |  | ||||||
|  * |  | ||||||
|  * @author fengdan |  | ||||||
|  */ |  | ||||||
| @Slf4j |  | ||||||
| @Service |  | ||||||
| @Validated |  | ||||||
| public class WxAccountServiceImpl implements WxAccountService { |  | ||||||
|  |  | ||||||
|     private static final long SCHEDULER_PERIOD = 5 * 60 * 1000L; |  | ||||||
|  |  | ||||||
|     @Resource |  | ||||||
|     private MpAccountMapper wxAccountMapper; |  | ||||||
|     @Resource |  | ||||||
|     private WxMpConfigDataProducer wxMpConfigDataProducer; |  | ||||||
|     @Resource |  | ||||||
|     private StringRedisTemplate stringRedisTemplate; |  | ||||||
|     @Resource |  | ||||||
|     private WxMpService wxMpService; |  | ||||||
|     @Resource |  | ||||||
|     private WxMpProperties wxMpProperties; |  | ||||||
|     @Resource |  | ||||||
|     @Lazy // 注入自己,所以延迟加载 |  | ||||||
|     private WxAccountService self; |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public Long createAccount(MpAccountCreateReqVO createReqVO) { |  | ||||||
|         // TODO 芋艿:校验唯一性 |  | ||||||
|         // 插入 |  | ||||||
|         MpAccountDO wxAccount = MpAccountConvert.INSTANCE.convert(createReqVO); |  | ||||||
|         wxAccountMapper.insert(wxAccount); |  | ||||||
|  |  | ||||||
|         // TODO 芋艿:刷新的方式 |  | ||||||
|         wxMpConfigDataProducer.sendDictDataRefreshMessage(); |  | ||||||
|         // 返回 |  | ||||||
|         return wxAccount.getId(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public void updateAccount(MpAccountUpdateReqVO updateReqVO) { |  | ||||||
|         // TODO 芋艿:校验唯一性 |  | ||||||
|         // 校验存在 |  | ||||||
|         validateWxAccountExists(updateReqVO.getId()); |  | ||||||
|         // 更新 |  | ||||||
|         MpAccountDO updateObj = MpAccountConvert.INSTANCE.convert(updateReqVO); |  | ||||||
|         wxAccountMapper.updateById(updateObj); |  | ||||||
|  |  | ||||||
|         // TODO 芋艿:刷新的方式 |  | ||||||
|         wxMpConfigDataProducer.sendDictDataRefreshMessage(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public void deleteAccount(Long id) { |  | ||||||
|         // 校验存在 |  | ||||||
|         validateWxAccountExists(id); |  | ||||||
|         // 删除 |  | ||||||
|         wxAccountMapper.deleteById(id); |  | ||||||
|  |  | ||||||
|         // TODO 芋艿:刷新的方式 |  | ||||||
|         wxMpConfigDataProducer.sendDictDataRefreshMessage(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private void validateWxAccountExists(Long id) { |  | ||||||
|         if (wxAccountMapper.selectById(id) == null) { |  | ||||||
|             throw ServiceExceptionUtil.exception(ErrorCodeConstants.WX_ACCOUNT_NOT_EXISTS); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public MpAccountDO getAccount(Long id) { |  | ||||||
|         return wxAccountMapper.selectById(id); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public PageResult<MpAccountDO> getAccountPage(MpAccountPageReqVO pageReqVO) { |  | ||||||
|         return wxAccountMapper.selectPage(pageReqVO); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public MpAccountDO findBy(SFunction<MpAccountDO, ?> field, Object val) { |  | ||||||
|         return wxAccountMapper.selectOne(field, val); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @PostConstruct |  | ||||||
|     @TenantIgnore |  | ||||||
|     @Override |  | ||||||
|     public void initLoadWxMpConfigStorages() { |  | ||||||
|         // TODO 芋艿:刷新的方式 |  | ||||||
|         List<MpAccountDO> wxAccountList = this.wxAccountMapper.selectList(); |  | ||||||
|         if (CollectionUtils.isEmpty(wxAccountList)) { |  | ||||||
|             log.info("未读取到公众号配置,请在管理后台添加"); |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|         log.info("加载到{}条公众号配置", wxAccountList.size()); |  | ||||||
|         wxAccountList.forEach(account -> addAccountToRuntime(account, new RedisTemplateWxRedisOps(stringRedisTemplate))); |  | ||||||
|         log.info("公众号配置加载完成"); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * 添加账号到当前程序,如首次添加需初始化configStorageMap |  | ||||||
|      * |  | ||||||
|      * @param account 公众号 |  | ||||||
|      */ |  | ||||||
|     private synchronized void addAccountToRuntime(MpAccountDO account, RedisTemplateWxRedisOps redisOps) { |  | ||||||
|         String appId = account.getAppId(); |  | ||||||
|         WxMpConfigStorage wxMpRedisConfig = account.toWxMpConfigStorage(redisOps, wxMpProperties); |  | ||||||
|         try { |  | ||||||
|             wxMpService.addConfigStorage(appId, wxMpRedisConfig); |  | ||||||
|         } catch (NullPointerException e) { |  | ||||||
|             log.info("需初始化configStorageMap..."); |  | ||||||
|             Map<String, WxMpConfigStorage> configStorages = new HashMap<>(4); |  | ||||||
|             configStorages.put(appId, wxMpRedisConfig); |  | ||||||
|             wxMpService.setMultiConfigStorages(configStorages, appId); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD) |  | ||||||
|     public void schedulePeriodicRefresh() { |  | ||||||
|         self.initLoadWxMpConfigStorages(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| } |  | ||||||
| @ -1,4 +1,4 @@ | |||||||
| package cn.iocoder.yudao.module.mp.handler; | package cn.iocoder.yudao.module.mp.service.fansmsgres; | ||||||
| 
 | 
 | ||||||
| import me.chanjar.weixin.common.session.WxSessionManager; | import me.chanjar.weixin.common.session.WxSessionManager; | ||||||
| import me.chanjar.weixin.mp.api.WxMpMessageHandler; | import me.chanjar.weixin.mp.api.WxMpMessageHandler; | ||||||
| @ -9,13 +9,15 @@ import org.springframework.stereotype.Component; | |||||||
| 
 | 
 | ||||||
| import java.util.Map; | import java.util.Map; | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  |  * 点击菜单连接的事件处理器 | ||||||
|  |  */ | ||||||
| @Component | @Component | ||||||
| public class NullHandler implements WxMpMessageHandler { | public class NullHandler implements WxMpMessageHandler { | ||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|     public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, |     public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, | ||||||
|                                     Map<String, Object> context, WxMpService wxMpService, |                                     WxMpService wxMpService, WxSessionManager sessionManager) { | ||||||
|                                     WxSessionManager sessionManager) { |  | ||||||
|         return null; |         return null; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -1,4 +1,4 @@ | |||||||
| package cn.iocoder.yudao.module.mp.handler; | package cn.iocoder.yudao.module.mp.service.handler; | ||||||
| 
 | 
 | ||||||
| import cn.hutool.core.io.FileUtil; | import cn.hutool.core.io.FileUtil; | ||||||
| import cn.hutool.http.HtmlUtil; | import cn.hutool.http.HtmlUtil; | ||||||
| @ -8,7 +8,7 @@ import cn.iocoder.yudao.module.infra.api.file.FileApi; | |||||||
| import cn.iocoder.yudao.module.mp.builder.TextBuilder; | import cn.iocoder.yudao.module.mp.builder.TextBuilder; | ||||||
| import cn.iocoder.yudao.module.mp.controller.admin.fansmsg.vo.WxFansMsgCreateReqVO; | import cn.iocoder.yudao.module.mp.controller.admin.fansmsg.vo.WxFansMsgCreateReqVO; | ||||||
| import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO; | import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO; | ||||||
| import cn.iocoder.yudao.module.mp.service.account.WxAccountService; | import cn.iocoder.yudao.module.mp.service.account.MpAccountService; | ||||||
| import cn.iocoder.yudao.module.mp.service.fansmsg.WxFansMsgService; | import cn.iocoder.yudao.module.mp.service.fansmsg.WxFansMsgService; | ||||||
| import lombok.extern.slf4j.Slf4j; | import lombok.extern.slf4j.Slf4j; | ||||||
| import me.chanjar.weixin.common.api.WxConsts; | import me.chanjar.weixin.common.api.WxConsts; | ||||||
| @ -21,6 +21,7 @@ import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; | |||||||
| import me.chanjar.weixin.mp.bean.result.WxMpUser; | import me.chanjar.weixin.mp.bean.result.WxMpUser; | ||||||
| import org.apache.commons.lang3.StringUtils; | import org.apache.commons.lang3.StringUtils; | ||||||
| import org.springframework.beans.factory.annotation.Autowired; | import org.springframework.beans.factory.annotation.Autowired; | ||||||
|  | import org.springframework.context.annotation.Lazy; | ||||||
| import org.springframework.stereotype.Component; | import org.springframework.stereotype.Component; | ||||||
| 
 | 
 | ||||||
| import javax.annotation.Resource; | import javax.annotation.Resource; | ||||||
| @ -28,11 +29,18 @@ import java.io.File; | |||||||
| import java.io.UnsupportedEncodingException; | import java.io.UnsupportedEncodingException; | ||||||
| import java.util.Map; | import java.util.Map; | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  |  * 默认所有消息的事件处理器 | ||||||
|  |  * | ||||||
|  |  * // TODO 芋艿:待实现 | ||||||
|  |  */ | ||||||
| @Component | @Component | ||||||
| @Slf4j | @Slf4j | ||||||
| public class MsgHandler implements WxMpMessageHandler { | public class DefaultMessageHandler implements WxMpMessageHandler { | ||||||
|     @Autowired | 
 | ||||||
|     private WxAccountService wxAccountService; |     @Resource | ||||||
|  |     @Lazy // 延迟加载,解决循环依赖的问题 | ||||||
|  |     private MpAccountService mpAccountService; | ||||||
| 
 | 
 | ||||||
|     @Autowired |     @Autowired | ||||||
|     private WxFansMsgService wxFansMsgService; |     private WxFansMsgService wxFansMsgService; | ||||||
| @ -55,7 +63,7 @@ public class MsgHandler implements WxMpMessageHandler { | |||||||
|                 WxMpUser wxmpUser = weixinService.getUserService() |                 WxMpUser wxmpUser = weixinService.getUserService() | ||||||
|                         .userInfo(wxMessage.getFromUser(), null); |                         .userInfo(wxMessage.getFromUser(), null); | ||||||
|                 if (wxmpUser != null) { |                 if (wxmpUser != null) { | ||||||
|                     MpAccountDO wxAccount = wxAccountService.findBy(MpAccountDO::getAccount, wxMessage.getToUser()); |                     MpAccountDO wxAccount = mpAccountService.findBy(MpAccountDO::getAccount, wxMessage.getToUser()); | ||||||
|                     if (wxAccount != null) { |                     if (wxAccount != null) { | ||||||
| 
 | 
 | ||||||
|                         if (wxMessage.getMsgType().equals(WxConsts.XmlMsgType.TEXT)) { |                         if (wxMessage.getMsgType().equals(WxConsts.XmlMsgType.TEXT)) { | ||||||
| @ -1,4 +1,4 @@ | |||||||
| package cn.iocoder.yudao.module.mp.handler; | package cn.iocoder.yudao.module.mp.service.handler; | ||||||
| 
 | 
 | ||||||
| import me.chanjar.weixin.common.session.WxSessionManager; | import me.chanjar.weixin.common.session.WxSessionManager; | ||||||
| import me.chanjar.weixin.mp.api.WxMpMessageHandler; | import me.chanjar.weixin.mp.api.WxMpMessageHandler; | ||||||
| @ -9,14 +9,18 @@ import org.springframework.stereotype.Component; | |||||||
| 
 | 
 | ||||||
| import java.util.Map; | import java.util.Map; | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  |  * 接收客服会话管理的事件处理器 | ||||||
|  |  * | ||||||
|  |  * @author 芋道源码 | ||||||
|  |  */ | ||||||
| @Component | @Component | ||||||
| public class KfSessionHandler implements WxMpMessageHandler { | public class KfSessionHandler implements WxMpMessageHandler { | ||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|     public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, |     public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, | ||||||
|                                     Map<String, Object> context, WxMpService wxMpService, |                                     WxMpService wxMpService, WxSessionManager sessionManager) { | ||||||
|                                     WxSessionManager sessionManager) { |         // TODO 对会话做处理 | ||||||
|         //TODO 对会话做处理 |  | ||||||
|         return null; |         return null; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -1,4 +1,4 @@ | |||||||
| package cn.iocoder.yudao.module.mp.handler; | package cn.iocoder.yudao.module.mp.service.handler; | ||||||
| 
 | 
 | ||||||
| import cn.iocoder.yudao.module.mp.builder.TextBuilder; | import cn.iocoder.yudao.module.mp.builder.TextBuilder; | ||||||
| import lombok.extern.slf4j.Slf4j; | import lombok.extern.slf4j.Slf4j; | ||||||
| @ -13,14 +13,18 @@ import java.util.Map; | |||||||
| 
 | 
 | ||||||
| import static me.chanjar.weixin.common.api.WxConsts.XmlMsgType; | import static me.chanjar.weixin.common.api.WxConsts.XmlMsgType; | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  |  * 上报地理位置的事件处理器 | ||||||
|  |  * | ||||||
|  |  * // TODO @芋艿:需要实现一下~ | ||||||
|  |  */ | ||||||
| @Component | @Component | ||||||
| @Slf4j | @Slf4j | ||||||
| public class LocationHandler implements WxMpMessageHandler { | public class LocationHandler implements WxMpMessageHandler { | ||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|     public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, |     public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, | ||||||
|                                     Map<String, Object> context, WxMpService wxMpService, |                                     WxMpService wxMpService, WxSessionManager sessionManager) { | ||||||
|                                     WxSessionManager sessionManager) { |  | ||||||
|         if (wxMessage.getMsgType().equals(XmlMsgType.LOCATION)) { |         if (wxMessage.getMsgType().equals(XmlMsgType.LOCATION)) { | ||||||
|             //TODO 接收处理用户发送的地理位置消息 |             //TODO 接收处理用户发送的地理位置消息 | ||||||
|             try { |             try { | ||||||
| @ -1,4 +1,4 @@ | |||||||
| package cn.iocoder.yudao.module.mp.handler; | package cn.iocoder.yudao.module.mp.service.handler; | ||||||
| 
 | 
 | ||||||
| import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | ||||||
| import lombok.extern.slf4j.Slf4j; | import lombok.extern.slf4j.Slf4j; | ||||||
| @ -11,13 +11,18 @@ import org.springframework.stereotype.Component; | |||||||
| 
 | 
 | ||||||
| import java.util.Map; | import java.util.Map; | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  |  * 保存微信消息的事件处理器 | ||||||
|  |  * | ||||||
|  |  * // TODO 芋艿:实现一下 | ||||||
|  |  */ | ||||||
| @Component | @Component | ||||||
| @Slf4j | @Slf4j | ||||||
| public class LogHandler implements WxMpMessageHandler { | public class LogHandler implements WxMpMessageHandler { | ||||||
|  | 
 | ||||||
|     @Override |     @Override | ||||||
|     public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, |     public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, | ||||||
|                                     Map<String, Object> context, WxMpService wxMpService, |                                     WxMpService wxMpService, WxSessionManager sessionManager) { | ||||||
|                                     WxSessionManager sessionManager) { |  | ||||||
|         log.info("接收到请求消息,内容:{}", JsonUtils.toJsonString(wxMessage)); |         log.info("接收到请求消息,内容:{}", JsonUtils.toJsonString(wxMessage)); | ||||||
|         return null; |         return null; | ||||||
|     } |     } | ||||||
| @ -1,4 +1,4 @@ | |||||||
| package cn.iocoder.yudao.module.mp.handler; | package cn.iocoder.yudao.module.mp.service.handler; | ||||||
| 
 | 
 | ||||||
| import me.chanjar.weixin.common.session.WxSessionManager; | import me.chanjar.weixin.common.session.WxSessionManager; | ||||||
| import me.chanjar.weixin.mp.api.WxMpMessageHandler; | import me.chanjar.weixin.mp.api.WxMpMessageHandler; | ||||||
| @ -11,6 +11,11 @@ import java.util.Map; | |||||||
| 
 | 
 | ||||||
| import static me.chanjar.weixin.common.api.WxConsts.MenuButtonType; | import static me.chanjar.weixin.common.api.WxConsts.MenuButtonType; | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  |  * 自定义菜单的事件处理器 | ||||||
|  |  * | ||||||
|  |  * // TODO 芋艿:待实现 | ||||||
|  |  */ | ||||||
| @Component | @Component | ||||||
| public class MenuHandler implements WxMpMessageHandler { | public class MenuHandler implements WxMpMessageHandler { | ||||||
| 
 | 
 | ||||||
| @ -1,4 +1,4 @@ | |||||||
| package cn.iocoder.yudao.module.mp.handler; | package cn.iocoder.yudao.module.mp.service.handler; | ||||||
| 
 | 
 | ||||||
| import me.chanjar.weixin.common.error.WxErrorException; | import me.chanjar.weixin.common.error.WxErrorException; | ||||||
| import me.chanjar.weixin.common.session.WxSessionManager; | import me.chanjar.weixin.common.session.WxSessionManager; | ||||||
| @ -10,6 +10,9 @@ import org.springframework.stereotype.Component; | |||||||
| 
 | 
 | ||||||
| import java.util.Map; | import java.util.Map; | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  |  * 扫码的事件处理器 | ||||||
|  |  */ | ||||||
| @Component | @Component | ||||||
| public class ScanHandler implements WxMpMessageHandler { | public class ScanHandler implements WxMpMessageHandler { | ||||||
| 
 | 
 | ||||||
| @ -1,4 +1,4 @@ | |||||||
| package cn.iocoder.yudao.module.mp.handler; | package cn.iocoder.yudao.module.mp.service.handler; | ||||||
| 
 | 
 | ||||||
| import me.chanjar.weixin.common.session.WxSessionManager; | import me.chanjar.weixin.common.session.WxSessionManager; | ||||||
| import me.chanjar.weixin.mp.api.WxMpMessageHandler; | import me.chanjar.weixin.mp.api.WxMpMessageHandler; | ||||||
| @ -10,8 +10,7 @@ import org.springframework.stereotype.Component; | |||||||
| import java.util.Map; | import java.util.Map; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * 门店审核事件处理 |  * 门店审核事件的事件处理器 | ||||||
|  * |  | ||||||
|  */ |  */ | ||||||
| @Component | @Component | ||||||
| public class StoreCheckNotifyHandler implements WxMpMessageHandler { | public class StoreCheckNotifyHandler implements WxMpMessageHandler { | ||||||
| @ -1,4 +1,4 @@ | |||||||
| package cn.iocoder.yudao.module.mp.handler; | package cn.iocoder.yudao.module.mp.service.handler; | ||||||
| 
 | 
 | ||||||
| import cn.hutool.core.date.DateUtil; | import cn.hutool.core.date.DateUtil; | ||||||
| import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | ||||||
| @ -9,7 +9,7 @@ import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO; | |||||||
| import cn.iocoder.yudao.module.mp.dal.dataobject.accountfans.WxAccountFansDO; | import cn.iocoder.yudao.module.mp.dal.dataobject.accountfans.WxAccountFansDO; | ||||||
| import cn.iocoder.yudao.module.mp.dal.dataobject.subscribetext.WxSubscribeTextDO; | import cn.iocoder.yudao.module.mp.dal.dataobject.subscribetext.WxSubscribeTextDO; | ||||||
| import cn.iocoder.yudao.module.mp.dal.dataobject.texttemplate.WxTextTemplateDO; | import cn.iocoder.yudao.module.mp.dal.dataobject.texttemplate.WxTextTemplateDO; | ||||||
| import cn.iocoder.yudao.module.mp.service.account.WxAccountService; | import cn.iocoder.yudao.module.mp.service.account.MpAccountService; | ||||||
| import cn.iocoder.yudao.module.mp.service.accountfans.WxAccountFansService; | import cn.iocoder.yudao.module.mp.service.accountfans.WxAccountFansService; | ||||||
| import cn.iocoder.yudao.module.mp.service.accountfanstag.WxAccountFansTagService; | import cn.iocoder.yudao.module.mp.service.accountfanstag.WxAccountFansTagService; | ||||||
| import cn.iocoder.yudao.module.mp.service.subscribetext.WxSubscribeTextService; | import cn.iocoder.yudao.module.mp.service.subscribetext.WxSubscribeTextService; | ||||||
| @ -23,11 +23,18 @@ import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; | |||||||
| import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; | import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; | ||||||
| import me.chanjar.weixin.mp.bean.result.WxMpUser; | import me.chanjar.weixin.mp.bean.result.WxMpUser; | ||||||
| import org.springframework.beans.factory.annotation.Autowired; | import org.springframework.beans.factory.annotation.Autowired; | ||||||
|  | import org.springframework.context.annotation.Lazy; | ||||||
| import org.springframework.stereotype.Component; | import org.springframework.stereotype.Component; | ||||||
| 
 | 
 | ||||||
|  | import javax.annotation.Resource; | ||||||
| import java.io.UnsupportedEncodingException; | import java.io.UnsupportedEncodingException; | ||||||
| import java.util.Map; | import java.util.Map; | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  |  * 关注的事件处理器 | ||||||
|  |  * | ||||||
|  |  * // TODO 芋艿:待实现 | ||||||
|  |  */ | ||||||
| @Component | @Component | ||||||
| @Slf4j | @Slf4j | ||||||
| public class SubscribeHandler implements WxMpMessageHandler { | public class SubscribeHandler implements WxMpMessageHandler { | ||||||
| @ -35,8 +42,9 @@ public class SubscribeHandler implements WxMpMessageHandler { | |||||||
|     @Autowired |     @Autowired | ||||||
|     private WxTextTemplateService wxTextTemplateService; |     private WxTextTemplateService wxTextTemplateService; | ||||||
| 
 | 
 | ||||||
|     @Autowired |     @Resource | ||||||
|     private WxAccountService wxAccountService; |     @Lazy // 延迟加载,解决循环依赖的问题 | ||||||
|  |     private MpAccountService mpAccountService; | ||||||
| 
 | 
 | ||||||
|     @Autowired |     @Autowired | ||||||
|     private WxSubscribeTextService wxSubscribeTextService; |     private WxSubscribeTextService wxSubscribeTextService; | ||||||
| @ -60,7 +68,7 @@ public class SubscribeHandler implements WxMpMessageHandler { | |||||||
|                     .userInfo(wxMessage.getFromUser(), null); |                     .userInfo(wxMessage.getFromUser(), null); | ||||||
|             if (wxmpUser != null) { |             if (wxmpUser != null) { | ||||||
|                 // 可以添加关注用户到本地数据库 |                 // 可以添加关注用户到本地数据库 | ||||||
|                 MpAccountDO wxAccount = wxAccountService.findBy(MpAccountDO::getAccount, wxMessage.getToUser()); |                 MpAccountDO wxAccount = mpAccountService.findBy(MpAccountDO::getAccount, wxMessage.getToUser()); | ||||||
|                 if (wxAccount != null) { |                 if (wxAccount != null) { | ||||||
|                     WxAccountFansDO wxAccountFans = wxAccountFansService.findBy(WxAccountFansDO::getOpenid, wxmpUser.getOpenId()); |                     WxAccountFansDO wxAccountFans = wxAccountFansService.findBy(WxAccountFansDO::getOpenid, wxmpUser.getOpenId()); | ||||||
|                     if (wxAccountFans == null) {//insert |                     if (wxAccountFans == null) {//insert | ||||||
| @ -128,7 +136,7 @@ public class SubscribeHandler implements WxMpMessageHandler { | |||||||
| 
 | 
 | ||||||
|         try { |         try { | ||||||
|             String content = "感谢关注!";//默认 |             String content = "感谢关注!";//默认 | ||||||
|             MpAccountDO wxAccount = wxAccountService.findBy(MpAccountDO::getAccount, wxMessage.getToUser()); |             MpAccountDO wxAccount = mpAccountService.findBy(MpAccountDO::getAccount, wxMessage.getToUser()); | ||||||
|             if (wxAccount != null) { |             if (wxAccount != null) { | ||||||
|                 WxSubscribeTextDO wxSubscribeText = wxSubscribeTextService.findBy(WxSubscribeTextDO::getWxAccountId, String.valueOf(wxAccount.getId())); |                 WxSubscribeTextDO wxSubscribeText = wxSubscribeTextService.findBy(WxSubscribeTextDO::getWxAccountId, String.valueOf(wxAccount.getId())); | ||||||
|                 if (wxSubscribeText != null) { |                 if (wxSubscribeText != null) { | ||||||
| @ -1,9 +1,9 @@ | |||||||
| package cn.iocoder.yudao.module.mp.handler; | package cn.iocoder.yudao.module.mp.service.handler; | ||||||
| 
 | 
 | ||||||
| import cn.iocoder.yudao.module.mp.controller.admin.accountfans.vo.WxAccountFansUpdateReqVO; | import cn.iocoder.yudao.module.mp.controller.admin.accountfans.vo.WxAccountFansUpdateReqVO; | ||||||
| import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO; | import cn.iocoder.yudao.module.mp.dal.dataobject.account.MpAccountDO; | ||||||
| import cn.iocoder.yudao.module.mp.dal.dataobject.accountfans.WxAccountFansDO; | import cn.iocoder.yudao.module.mp.dal.dataobject.accountfans.WxAccountFansDO; | ||||||
| import cn.iocoder.yudao.module.mp.service.account.WxAccountService; | import cn.iocoder.yudao.module.mp.service.account.MpAccountService; | ||||||
| import cn.iocoder.yudao.module.mp.service.accountfans.WxAccountFansService; | import cn.iocoder.yudao.module.mp.service.accountfans.WxAccountFansService; | ||||||
| import lombok.extern.slf4j.Slf4j; | import lombok.extern.slf4j.Slf4j; | ||||||
| import me.chanjar.weixin.common.session.WxSessionManager; | import me.chanjar.weixin.common.session.WxSessionManager; | ||||||
| @ -12,16 +12,24 @@ import me.chanjar.weixin.mp.api.WxMpService; | |||||||
| import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; | import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; | ||||||
| import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; | import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; | ||||||
| import org.springframework.beans.factory.annotation.Autowired; | import org.springframework.beans.factory.annotation.Autowired; | ||||||
|  | import org.springframework.context.annotation.Lazy; | ||||||
| import org.springframework.stereotype.Component; | import org.springframework.stereotype.Component; | ||||||
| 
 | 
 | ||||||
|  | import javax.annotation.Resource; | ||||||
| import java.util.Map; | import java.util.Map; | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  |  * 取消关注的事件处理器 | ||||||
|  |  * | ||||||
|  |  * // TODO 芋艿:待实现 | ||||||
|  |  */ | ||||||
| @Component | @Component | ||||||
| @Slf4j | @Slf4j | ||||||
| public class UnsubscribeHandler implements WxMpMessageHandler { | public class UnsubscribeHandler implements WxMpMessageHandler { | ||||||
| 
 | 
 | ||||||
|     @Autowired |     @Resource | ||||||
|     private WxAccountService wxAccountService; |     @Lazy // 延迟加载,解决循环依赖的问题 | ||||||
|  |     private MpAccountService mpAccountService; | ||||||
| 
 | 
 | ||||||
|     @Autowired |     @Autowired | ||||||
|     private WxAccountFansService wxAccountFansService; |     private WxAccountFansService wxAccountFansService; | ||||||
| @ -34,7 +42,7 @@ public class UnsubscribeHandler implements WxMpMessageHandler { | |||||||
|         log.info("取消关注用户 OPENID: " + openId); |         log.info("取消关注用户 OPENID: " + openId); | ||||||
|         // TODO 可以更新本地数据库为取消关注状态 |         // TODO 可以更新本地数据库为取消关注状态 | ||||||
| 
 | 
 | ||||||
|         MpAccountDO wxAccountDO = wxAccountService.findBy(MpAccountDO::getAccount, wxMessage.getToUser()); |         MpAccountDO wxAccountDO = mpAccountService.findBy(MpAccountDO::getAccount, wxMessage.getToUser()); | ||||||
|         if (wxAccountDO != null) { |         if (wxAccountDO != null) { | ||||||
|             WxAccountFansDO wxAccountFansDO = wxAccountFansService.findBy(WxAccountFansDO::getOpenid, openId); |             WxAccountFansDO wxAccountFansDO = wxAccountFansService.findBy(WxAccountFansDO::getOpenid, openId); | ||||||
|             if (wxAccountFansDO != null) { |             if (wxAccountFansDO != null) { | ||||||
		Reference in New Issue
	
	Block a user
	 YunaiV
					YunaiV