Commit 977cc19fe31c9874aeec3e7f7dbfbff99f3ebd64

Authored by fengdan
1 parent 93dfee8e

fix(公众号账号管理): 多公众号账号初始化配置加载

yudao-module-wechat/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/config/WxMpConfig.java deleted
1 -package cn.iocoder.yudao.module.mp.config;  
2 -  
3 -  
4 -import cn.hutool.extra.spring.SpringUtil;  
5 -import cn.iocoder.yudao.module.mp.controller.admin.account.vo.WxAccountExportReqVO;  
6 -import cn.iocoder.yudao.module.mp.dal.dataobject.account.WxAccountDO;  
7 -import cn.iocoder.yudao.module.mp.handler.*;  
8 -import cn.iocoder.yudao.module.mp.service.account.WxAccountService;  
9 -import com.google.common.collect.Maps;  
10 -import lombok.extern.slf4j.Slf4j;  
11 -import me.chanjar.weixin.common.api.WxConsts;  
12 -import me.chanjar.weixin.mp.api.WxMpMessageRouter;  
13 -import me.chanjar.weixin.mp.api.WxMpService;  
14 -import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;  
15 -import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl;  
16 -import me.chanjar.weixin.mp.constant.WxMpEventConstants;  
17 -import org.springframework.context.annotation.Configuration;  
18 -import org.springframework.scheduling.annotation.Scheduled;  
19 -import org.springframework.util.CollectionUtils;  
20 -  
21 -import javax.annotation.PostConstruct;  
22 -import javax.annotation.Resource;  
23 -import java.util.List;  
24 -import java.util.Map;  
25 -import java.util.stream.Collectors;  
26 -  
27 -// TODO @芋艿:思考有没更好的处理方式  
28 -@Slf4j  
29 -@Configuration  
30 -public class WxMpConfig {  
31 -  
32 - private static Map<String, WxMpMessageRouter> routers = Maps.newHashMap();  
33 - private static Map<String, WxMpService> mpServices = Maps.newHashMap();  
34 -  
35 - @Resource  
36 - private WxAccountService wxAccountService;  
37 -  
38 - private static final long SCHEDULER_PERIOD = 5 * 60 * 1000L;  
39 -  
40 - /**  
41 - * 初始化公众号配置  
42 - */  
43 - @PostConstruct  
44 - public synchronized void initWxConfig() {  
45 - WxAccountExportReqVO req = new WxAccountExportReqVO();  
46 - List<WxAccountDO> wxAccountList = wxAccountService.getWxAccountList(req);  
47 - if (CollectionUtils.isEmpty(wxAccountList)) {  
48 - return;  
49 - }  
50 - WxMpConfig.init(wxAccountList);  
51 - log.info("加载公众号配置成功");  
52 - }  
53 -  
54 -  
55 - @Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD)  
56 - public void schedulePeriodicRefresh() {  
57 - initWxConfig();  
58 - }  
59 -  
60 - public static void init(List<WxAccountDO> wxAccountDOS) {  
61 - mpServices = wxAccountDOS.stream().map(wxAccountDO -> {  
62 - // TODO 亚洲:使用 WxMpInMemoryConfigStorage 的话,多节点会不会存在 accessToken 冲突  
63 -  
64 - WxMpDefaultConfigImpl configStorage;  
65 - configStorage = new WxMpDefaultConfigImpl();  
66 -  
67 - configStorage.setAppId(wxAccountDO.getAppId());  
68 - configStorage.setSecret(wxAccountDO.getAppSecret());  
69 - configStorage.setToken(wxAccountDO.getToken());  
70 - configStorage.setAesKey(wxAccountDO.getAesKey());  
71 -  
72 - WxMpService service = new WxMpServiceImpl();  
73 - service.setWxMpConfigStorage(configStorage);  
74 - routers.put(wxAccountDO.getAppId(), newRouter(service));  
75 - return service;  
76 - }).collect(Collectors.toMap(s -> s.getWxMpConfigStorage().getAppId(), a -> a, (o, n) -> o));  
77 - }  
78 -  
79 - public static Map<String, WxMpMessageRouter> getRouters() {  
80 - return routers;  
81 - }  
82 -  
83 - public static Map<String, WxMpService> getMpServices() {  
84 - return mpServices;  
85 - }  
86 -  
87 - private static WxMpMessageRouter newRouter(WxMpService wxMpService) {  
88 - final WxMpMessageRouter newRouter = new WxMpMessageRouter(wxMpService);  
89 - // 记录所有事件的日志 (异步执行)  
90 - newRouter.rule().handler(SpringUtil.getBean(LogHandler.class)).next();  
91 -  
92 - // 接收客服会话管理事件  
93 - newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)  
94 - .event(WxMpEventConstants.CustomerService.KF_CREATE_SESSION)  
95 - .handler(SpringUtil.getBean(KfSessionHandler.class)).end();  
96 - newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)  
97 - .event(WxMpEventConstants.CustomerService.KF_CLOSE_SESSION)  
98 - .handler(SpringUtil.getBean(KfSessionHandler.class))  
99 - .end();  
100 - newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)  
101 - .event(WxMpEventConstants.CustomerService.KF_SWITCH_SESSION)  
102 - .handler(SpringUtil.getBean(KfSessionHandler.class)).end();  
103 -  
104 - // 门店审核事件  
105 - newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)  
106 - .event(WxMpEventConstants.POI_CHECK_NOTIFY)  
107 - .handler(SpringUtil.getBean(StoreCheckNotifyHandler.class)).end();  
108 -  
109 - // 自定义菜单事件  
110 - newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)  
111 - .event(WxConsts.MenuButtonType.CLICK).handler(SpringUtil.getBean(MenuHandler.class)).end();  
112 -  
113 - // 点击菜单连接事件  
114 - newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)  
115 - .event(WxConsts.MenuButtonType.VIEW).handler(SpringUtil.getBean(NullHandler.class)).end();  
116 -  
117 - // 关注事件  
118 - newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)  
119 - .event(WxConsts.EventType.SUBSCRIBE).handler(SpringUtil.getBean(SubscribeHandler.class))  
120 - .end();  
121 -  
122 - // 取消关注事件  
123 - newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)  
124 - .event(WxConsts.EventType.UNSUBSCRIBE)  
125 - .handler(SpringUtil.getBean(UnsubscribeHandler.class)).end();  
126 -  
127 - // 上报地理位置事件  
128 - newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)  
129 - .event(WxConsts.EventType.LOCATION).handler(SpringUtil.getBean(LocationHandler.class))  
130 - .end();  
131 -  
132 - // 接收地理位置消息  
133 - newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.LOCATION)  
134 - .handler(SpringUtil.getBean(LocationHandler.class)).end();  
135 -  
136 - // 扫码事件  
137 - newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)  
138 - .event(WxConsts.EventType.SCAN).handler(SpringUtil.getBean(ScanHandler.class)).end();  
139 -  
140 - // 默认  
141 - newRouter.rule().async(false).handler(SpringUtil.getBean(MsgHandler.class)).end();  
142 -  
143 - return newRouter;  
144 - }  
145 -}  
yudao-module-wechat/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/config/WxMpProperties.java deleted
1 -package cn.iocoder.yudao.module.mp.config;  
2 -  
3 -import lombok.Data;  
4 -import org.springframework.boot.context.properties.ConfigurationProperties;  
5 -  
6 -/**  
7 - * wechat mp properties  
8 - */  
9 -@Data  
10 -//@ConfigurationProperties(prefix = "wx.mp")  
11 -public class WxMpProperties {  
12 - /**  
13 - * 是否使用redis存储access token  
14 - */  
15 - private boolean useRedis;  
16 -  
17 - private String defaultContent;  
18 -  
19 - /**  
20 - * redis 配置  
21 - */  
22 - private RedisConfig redisConfig;  
23 -  
24 - @Data  
25 - public static class RedisConfig {  
26 - /**  
27 - * redis服务器 主机地址  
28 - */  
29 - private String host;  
30 -  
31 - /**  
32 - * redis服务器 端口号  
33 - */  
34 - private Integer port;  
35 -  
36 - /**  
37 - * redis服务器 密码  
38 - */  
39 - private String password;  
40 -  
41 - /**  
42 - * redis 服务连接超时时间  
43 - */  
44 - private Integer timeout;  
45 - }  
46 -  
47 -}  
yudao-module-wechat/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/dal/dataobject/account/WxAccountDO.java
@@ -4,7 +4,11 @@ import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; @@ -4,7 +4,11 @@ import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
4 import com.baomidou.mybatisplus.annotation.KeySequence; 4 import com.baomidou.mybatisplus.annotation.KeySequence;
5 import com.baomidou.mybatisplus.annotation.TableId; 5 import com.baomidou.mybatisplus.annotation.TableId;
6 import com.baomidou.mybatisplus.annotation.TableName; 6 import com.baomidou.mybatisplus.annotation.TableName;
  7 +import com.binarywang.spring.starter.wxjava.mp.properties.WxMpProperties;
7 import lombok.*; 8 import lombok.*;
  9 +import me.chanjar.weixin.common.redis.RedisTemplateWxRedisOps;
  10 +import me.chanjar.weixin.mp.config.WxMpConfigStorage;
  11 +import me.chanjar.weixin.mp.config.impl.WxMpRedisConfigImpl;
8 12
9 // TODO 亚洲:这个模块的相关类,使用 Mp 作为前缀哈 13 // TODO 亚洲:这个模块的相关类,使用 Mp 作为前缀哈
10 /** 14 /**
@@ -64,4 +68,12 @@ public class WxAccountDO extends BaseDO { @@ -64,4 +68,12 @@ public class WxAccountDO extends BaseDO {
64 */ 68 */
65 private String remark; 69 private String remark;
66 70
  71 + public WxMpConfigStorage toWxMpConfigStorage(RedisTemplateWxRedisOps redisTemplateWxRedisOps, WxMpProperties wxMpProperties) {
  72 + WxMpRedisConfigImpl wxMpRedisConfig = new WxMpRedisConfigImpl(redisTemplateWxRedisOps, wxMpProperties.getConfigStorage().getKeyPrefix());
  73 + wxMpRedisConfig.setAppId(appId);
  74 + wxMpRedisConfig.setSecret(appSecret);
  75 + wxMpRedisConfig.setToken(token);
  76 + wxMpRedisConfig.setAesKey(aesKey);
  77 + return wxMpRedisConfig;
  78 + }
67 } 79 }
yudao-module-wechat/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/framework/weixin/WxMpMessageRouterConfiguration.java 0 → 100644
  1 +package cn.iocoder.yudao.module.mp.framework.weixin;
  2 +
  3 +
  4 +import cn.iocoder.yudao.module.mp.handler.*;
  5 +import lombok.RequiredArgsConstructor;
  6 +import lombok.extern.slf4j.Slf4j;
  7 +import me.chanjar.weixin.common.api.WxConsts;
  8 +import me.chanjar.weixin.mp.api.WxMpMessageRouter;
  9 +import me.chanjar.weixin.mp.api.WxMpService;
  10 +import me.chanjar.weixin.mp.constant.WxMpEventConstants;
  11 +import org.springframework.context.annotation.Bean;
  12 +import org.springframework.stereotype.Component;
  13 +
  14 +import javax.annotation.Resource;
  15 +
  16 +/**
  17 + * @author fengdan
  18 + */
  19 +@Slf4j
  20 +@Component
  21 +@RequiredArgsConstructor
  22 +public class WxMpMessageRouterConfiguration {
  23 +
  24 + @Resource
  25 + private WxMpService wxMpService;
  26 + @Resource
  27 + private LogHandler logHandler;
  28 + @Resource
  29 + private KfSessionHandler kfSessionHandler;
  30 + @Resource
  31 + private StoreCheckNotifyHandler storeCheckNotifyHandler;
  32 + @Resource
  33 + private MenuHandler menuHandler;
  34 + @Resource
  35 + private NullHandler nullHandler;
  36 + @Resource
  37 + private SubscribeHandler subscribeHandler;
  38 + @Resource
  39 + private UnsubscribeHandler unsubscribeHandler;
  40 + @Resource
  41 + private LocationHandler locationHandler;
  42 + @Resource
  43 + private ScanHandler scanHandler;
  44 + @Resource
  45 + private MsgHandler msgHandler;
  46 +
  47 + @Bean
  48 + public WxMpMessageRouter messageRouter() {
  49 + final WxMpMessageRouter newRouter = new WxMpMessageRouter(wxMpService);
  50 + // 记录所有事件的日志 (异步执行)
  51 + newRouter.rule().handler(logHandler).next();
  52 +
  53 + // 接收客服会话管理事件
  54 + newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
  55 + .event(WxMpEventConstants.CustomerService.KF_CREATE_SESSION)
  56 + .handler(kfSessionHandler).end();
  57 + newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
  58 + .event(WxMpEventConstants.CustomerService.KF_CLOSE_SESSION)
  59 + .handler(kfSessionHandler)
  60 + .end();
  61 + newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
  62 + .event(WxMpEventConstants.CustomerService.KF_SWITCH_SESSION)
  63 + .handler(kfSessionHandler).end();
  64 +
  65 + // 门店审核事件
  66 + newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
  67 + .event(WxMpEventConstants.POI_CHECK_NOTIFY)
  68 + .handler(storeCheckNotifyHandler).end();
  69 +
  70 + // 自定义菜单事件
  71 + newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
  72 + .event(WxConsts.MenuButtonType.CLICK).handler(menuHandler).end();
  73 +
  74 + // 点击菜单连接事件
  75 + newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
  76 + .event(WxConsts.MenuButtonType.VIEW).handler(nullHandler).end();
  77 +
  78 + // 关注事件
  79 + newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
  80 + .event(WxConsts.EventType.SUBSCRIBE).handler(subscribeHandler)
  81 + .end();
  82 +
  83 + // 取消关注事件
  84 + newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
  85 + .event(WxConsts.EventType.UNSUBSCRIBE)
  86 + .handler(unsubscribeHandler).end();
  87 +
  88 + // 上报地理位置事件
  89 + newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
  90 + .event(WxConsts.EventType.LOCATION).handler(locationHandler)
  91 + .end();
  92 +
  93 + // 接收地理位置消息
  94 + newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.LOCATION)
  95 + .handler(locationHandler).end();
  96 +
  97 + // 扫码事件
  98 + newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
  99 + .event(WxConsts.EventType.SCAN).handler(scanHandler).end();
  100 +
  101 + // 默认
  102 + newRouter.rule().async(false).handler(msgHandler).end();
  103 +
  104 + return newRouter;
  105 + }
  106 +}
yudao-module-wechat/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/handler/MsgHandler.java
@@ -6,13 +6,10 @@ import cn.hutool.http.HttpUtil; @@ -6,13 +6,10 @@ import cn.hutool.http.HttpUtil;
6 import cn.iocoder.yudao.framework.common.util.json.JsonUtils; 6 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
7 import cn.iocoder.yudao.module.infra.api.file.FileApi; 7 import cn.iocoder.yudao.module.infra.api.file.FileApi;
8 import cn.iocoder.yudao.module.mp.builder.TextBuilder; 8 import cn.iocoder.yudao.module.mp.builder.TextBuilder;
9 -import cn.iocoder.yudao.module.mp.config.WxMpProperties;  
10 import cn.iocoder.yudao.module.mp.controller.admin.fansmsg.vo.WxFansMsgCreateReqVO; 9 import cn.iocoder.yudao.module.mp.controller.admin.fansmsg.vo.WxFansMsgCreateReqVO;
11 import cn.iocoder.yudao.module.mp.dal.dataobject.account.WxAccountDO; 10 import cn.iocoder.yudao.module.mp.dal.dataobject.account.WxAccountDO;
12 import cn.iocoder.yudao.module.mp.service.account.WxAccountService; 11 import cn.iocoder.yudao.module.mp.service.account.WxAccountService;
13 import cn.iocoder.yudao.module.mp.service.fansmsg.WxFansMsgService; 12 import cn.iocoder.yudao.module.mp.service.fansmsg.WxFansMsgService;
14 -import cn.iocoder.yudao.module.mp.service.receivetext.WxReceiveTextService;  
15 -import cn.iocoder.yudao.module.mp.service.texttemplate.WxTextTemplateService;  
16 import lombok.extern.slf4j.Slf4j; 13 import lombok.extern.slf4j.Slf4j;
17 import me.chanjar.weixin.common.api.WxConsts; 14 import me.chanjar.weixin.common.api.WxConsts;
18 import me.chanjar.weixin.common.error.WxErrorException; 15 import me.chanjar.weixin.common.error.WxErrorException;
@@ -102,7 +99,7 @@ public class MsgHandler implements WxMpMessageHandler { @@ -102,7 +99,7 @@ public class MsgHandler implements WxMpMessageHandler {
102 if (!downloadDir.exists()) { 99 if (!downloadDir.exists()) {
103 downloadDir.mkdirs(); 100 downloadDir.mkdirs();
104 } 101 }
105 - String filepath = downloadDirStr + String.valueOf(System.currentTimeMillis()) + ".png"; 102 + String filepath = downloadDirStr + System.currentTimeMillis() + ".png";
106 //微信pic url下载到本地,防止失效 103 //微信pic url下载到本地,防止失效
107 long size = HttpUtil.downloadFile(wxMessage.getPicUrl(), FileUtil.file(filepath)); 104 long size = HttpUtil.downloadFile(wxMessage.getPicUrl(), FileUtil.file(filepath));
108 log.info("download pic size : {}", size); 105 log.info("download pic size : {}", size);
yudao-module-wechat/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/mq/costomer/WxConfigDataRefreshConsumer.java
1 -package cn.iocoder.yudao.module.mp.mq.costomer.dict; 1 +package cn.iocoder.yudao.module.mp.mq.costomer;
2 2
3 import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener; 3 import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener;
4 -import cn.iocoder.yudao.module.mp.config.WxMpConfig;  
5 -import cn.iocoder.yudao.module.mp.mq.message.dict.WxConfigDataRefreshMessage; 4 +import cn.iocoder.yudao.module.mp.mq.message.WxConfigDataRefreshMessage;
  5 +import cn.iocoder.yudao.module.mp.service.account.WxAccountService;
6 import lombok.extern.slf4j.Slf4j; 6 import lombok.extern.slf4j.Slf4j;
7 import org.springframework.stereotype.Component; 7 import org.springframework.stereotype.Component;
8 8
@@ -18,12 +18,12 @@ import javax.annotation.Resource; @@ -18,12 +18,12 @@ import javax.annotation.Resource;
18 public class WxConfigDataRefreshConsumer extends AbstractChannelMessageListener<WxConfigDataRefreshMessage> { 18 public class WxConfigDataRefreshConsumer extends AbstractChannelMessageListener<WxConfigDataRefreshMessage> {
19 19
20 @Resource 20 @Resource
21 - private WxMpConfig wxMpConfig; 21 + private WxAccountService wxAccountService;
22 22
23 @Override 23 @Override
24 public void onMessage(WxConfigDataRefreshMessage message) { 24 public void onMessage(WxConfigDataRefreshMessage message) {
25 log.info("[onMessage][收到 WxConfigData 刷新消息]"); 25 log.info("[onMessage][收到 WxConfigData 刷新消息]");
26 - wxMpConfig.initWxConfig(); 26 + wxAccountService.initLoadWxMpConfigStorages();
27 } 27 }
28 28
29 } 29 }
yudao-module-wechat/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/mq/message/WxConfigDataRefreshMessage.java
1 -package cn.iocoder.yudao.module.mp.mq.message.dict; 1 +package cn.iocoder.yudao.module.mp.mq.message;
2 2
3 import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessage; 3 import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessage;
4 import lombok.Data; 4 import lombok.Data;
yudao-module-wechat/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/mq/producer/WxMpConfigDataProducer.java
1 -package cn.iocoder.yudao.module.mp.mq.producer.dict; 1 +package cn.iocoder.yudao.module.mp.mq.producer;
2 2
3 import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate; 3 import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate;
4 -import cn.iocoder.yudao.module.mp.mq.message.dict.WxConfigDataRefreshMessage; 4 +import cn.iocoder.yudao.module.mp.mq.message.WxConfigDataRefreshMessage;
5 import org.springframework.stereotype.Component; 5 import org.springframework.stereotype.Component;
6 6
7 import javax.annotation.Resource; 7 import javax.annotation.Resource;
yudao-module-wechat/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/account/WxAccountService.java
@@ -81,4 +81,9 @@ public interface WxAccountService { @@ -81,4 +81,9 @@ public interface WxAccountService {
81 * @return 81 * @return
82 */ 82 */
83 WxAccountDO findBy(SFunction<WxAccountDO, ?> field, Object val); 83 WxAccountDO findBy(SFunction<WxAccountDO, ?> field, Object val);
  84 +
  85 + /**
  86 + * 初始化
  87 + */
  88 + void initLoadWxMpConfigStorages();
84 } 89 }
yudao-module-wechat/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/service/account/WxAccountServiceImpl.java
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.mp.service.account; @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.mp.service.account;
2 2
3 import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; 3 import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
5 import cn.iocoder.yudao.module.mp.controller.admin.account.vo.WxAccountCreateReqVO; 6 import cn.iocoder.yudao.module.mp.controller.admin.account.vo.WxAccountCreateReqVO;
6 import cn.iocoder.yudao.module.mp.controller.admin.account.vo.WxAccountExportReqVO; 7 import cn.iocoder.yudao.module.mp.controller.admin.account.vo.WxAccountExportReqVO;
7 import cn.iocoder.yudao.module.mp.controller.admin.account.vo.WxAccountPageReqVO; 8 import cn.iocoder.yudao.module.mp.controller.admin.account.vo.WxAccountPageReqVO;
@@ -10,30 +11,52 @@ import cn.iocoder.yudao.module.mp.convert.account.WxAccountConvert; @@ -10,30 +11,52 @@ import cn.iocoder.yudao.module.mp.convert.account.WxAccountConvert;
10 import cn.iocoder.yudao.module.mp.dal.dataobject.account.WxAccountDO; 11 import cn.iocoder.yudao.module.mp.dal.dataobject.account.WxAccountDO;
11 import cn.iocoder.yudao.module.mp.dal.mysql.account.WxAccountMapper; 12 import cn.iocoder.yudao.module.mp.dal.mysql.account.WxAccountMapper;
12 import cn.iocoder.yudao.module.mp.enums.ErrorCodeConstants; 13 import cn.iocoder.yudao.module.mp.enums.ErrorCodeConstants;
13 -import cn.iocoder.yudao.module.mp.mq.producer.dict.WxMpConfigDataProducer; 14 +import cn.iocoder.yudao.module.mp.mq.producer.WxMpConfigDataProducer;
14 import com.baomidou.mybatisplus.core.toolkit.support.SFunction; 15 import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
  16 +import com.binarywang.spring.starter.wxjava.mp.properties.WxMpProperties;
  17 +import lombok.extern.slf4j.Slf4j;
  18 +import me.chanjar.weixin.common.redis.RedisTemplateWxRedisOps;
  19 +import me.chanjar.weixin.mp.api.WxMpService;
  20 +import me.chanjar.weixin.mp.config.WxMpConfigStorage;
  21 +import org.springframework.context.annotation.Lazy;
  22 +import org.springframework.data.redis.core.StringRedisTemplate;
  23 +import org.springframework.scheduling.annotation.Scheduled;
15 import org.springframework.stereotype.Service; 24 import org.springframework.stereotype.Service;
  25 +import org.springframework.util.CollectionUtils;
16 import org.springframework.validation.annotation.Validated; 26 import org.springframework.validation.annotation.Validated;
17 27
  28 +import javax.annotation.PostConstruct;
18 import javax.annotation.Resource; 29 import javax.annotation.Resource;
19 import java.util.Collection; 30 import java.util.Collection;
  31 +import java.util.HashMap;
20 import java.util.List; 32 import java.util.List;
  33 +import java.util.Map;
21 34
22 35
23 /** 36 /**
24 * 公众号账户 Service 实现类 37 * 公众号账户 Service 实现类
25 * 38 *
26 - * @author 芋道源码 39 + * @author fengdan
27 */ 40 */
  41 +@Slf4j
28 @Service 42 @Service
29 @Validated 43 @Validated
30 public class WxAccountServiceImpl implements WxAccountService { 44 public class WxAccountServiceImpl implements WxAccountService {
31 45
  46 + private static final long SCHEDULER_PERIOD = 5 * 60 * 1000L;
32 @Resource 47 @Resource
33 private WxAccountMapper wxAccountMapper; 48 private WxAccountMapper wxAccountMapper;
34 -  
35 @Resource 49 @Resource
36 private WxMpConfigDataProducer wxMpConfigDataProducer; 50 private WxMpConfigDataProducer wxMpConfigDataProducer;
  51 + @Resource
  52 + private StringRedisTemplate stringRedisTemplate;
  53 + @Resource
  54 + private WxMpService wxMpService;
  55 + @Resource
  56 + private WxMpProperties wxMpProperties;
  57 + @Resource
  58 + @Lazy // 注入自己,所以延迟加载
  59 + private WxAccountService self;
37 60
38 @Override 61 @Override
39 public Long createWxAccount(WxAccountCreateReqVO createReqVO) { 62 public Long createWxAccount(WxAccountCreateReqVO createReqVO) {
@@ -94,4 +117,43 @@ public class WxAccountServiceImpl implements WxAccountService { @@ -94,4 +117,43 @@ public class WxAccountServiceImpl implements WxAccountService {
94 public WxAccountDO findBy(SFunction<WxAccountDO, ?> field, Object val) { 117 public WxAccountDO findBy(SFunction<WxAccountDO, ?> field, Object val) {
95 return wxAccountMapper.selectOne(field, val); 118 return wxAccountMapper.selectOne(field, val);
96 } 119 }
  120 +
  121 + @PostConstruct
  122 + @TenantIgnore
  123 + @Override
  124 + public void initLoadWxMpConfigStorages() {
  125 + List<WxAccountDO> wxAccountList = this.wxAccountMapper.selectList();
  126 + if (CollectionUtils.isEmpty(wxAccountList)) {
  127 + log.info("未读取到公众号配置,请在管理后台添加");
  128 + return;
  129 + }
  130 + log.info("加载到{}条公众号配置", wxAccountList.size());
  131 + wxAccountList.forEach(account -> addAccountToRuntime(account, new RedisTemplateWxRedisOps(stringRedisTemplate)));
  132 + log.info("公众号配置加载完成");
  133 + }
  134 +
  135 + /**
  136 + * 添加账号到当前程序,如首次添加需初始化configStorageMap
  137 + *
  138 + * @param account 公众号
  139 + */
  140 + private synchronized void addAccountToRuntime(WxAccountDO account, RedisTemplateWxRedisOps redisOps) {
  141 + String appId = account.getAppId();
  142 + WxMpConfigStorage wxMpRedisConfig = account.toWxMpConfigStorage(redisOps, wxMpProperties);
  143 + try {
  144 + wxMpService.addConfigStorage(appId, wxMpRedisConfig);
  145 + } catch (NullPointerException e) {
  146 + log.info("需初始化configStorageMap...");
  147 + Map<String, WxMpConfigStorage> configStorages = new HashMap<>(4);
  148 + configStorages.put(appId, wxMpRedisConfig);
  149 + wxMpService.setMultiConfigStorages(configStorages, appId);
  150 + }
  151 + }
  152 +
  153 + @Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD)
  154 + public void schedulePeriodicRefresh() {
  155 + self.initLoadWxMpConfigStorages();
  156 + }
  157 +
  158 +
97 } 159 }