feat: 公众号开发

This commit is contained in:
hucs
2024-03-27 23:01:15 +08:00
parent 0ebf6cd3aa
commit 534e6d93fb
10 changed files with 278 additions and 141 deletions

View File

@@ -0,0 +1,32 @@
package com.starry.admin.common.aspect;
import com.baomidou.mybatisplus.core.conditions.AbstractWrapper;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
* @Author: huchuansai
* @Date: 2023/8/2 4:38 PM
* @Description:
*/
@Aspect
@Component
public class MybatisAspectj {
// 配置织入点
@Pointcut("execution(public * com.baomidou.mybatisplus.core.mapper.BaseMapper.selectOne(..))")
public void selectOneAspect() {
}
@Before("selectOneAspect()")
public void beforeSelect(JoinPoint point) {
Object arg = point.getArgs()[0];
if (arg instanceof AbstractWrapper) {
arg = (AbstractWrapper) arg;
((AbstractWrapper) arg).last("limit 1");
}
}
}

View File

@@ -41,7 +41,7 @@ public class GlobalExceptionHandler {
public R handleRuntimeException(RuntimeException e, HttpServletRequest request) {
String requestUrl = request.getRequestURI();
log.error("请求地址'{}',发生未知异常.", requestUrl, e);
return R.error("系统发生未知异常,请联系管理员");
return R.error(e.getMessage());
}
/**

View File

@@ -65,7 +65,7 @@ public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
"/v2/api-docs/**"
).permitAll()
// 对登录注册要允许匿名访问
.antMatchers("/login", "/captcha/get-captcha").permitAll()
.antMatchers("/login", "/captcha/get-captcha", "/wx/test/**").permitAll()
// 跨域请求会先进行一次options请求
.antMatchers(HttpMethod.OPTIONS).permitAll()
.anyRequest()// 除上面外的所有请求全部需要鉴权认证

View File

@@ -1,111 +1,111 @@
package com.starry.admin.modules.weichat.config;
import cn.hutool.core.util.StrUtil;
import com.starry.admin.modules.weichat.entity.CustomWxMpProperties;
import com.starry.admin.modules.weichat.handler.*;
import com.starry.admin.modules.weichat.utils.WxMpPropertiesUtils;
import lombok.AllArgsConstructor;
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.api.impl.WxMpServiceImpl;
import me.chanjar.weixin.mp.config.WxMpConfigStorage;
import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
import static me.chanjar.weixin.common.api.WxConsts.EventType.SUBSCRIBE;
import static me.chanjar.weixin.common.api.WxConsts.EventType.UNSUBSCRIBE;
import static me.chanjar.weixin.common.api.WxConsts.XmlMsgType.EVENT;
import static me.chanjar.weixin.mp.constant.WxMpEventConstants.CustomerService.*;
import static me.chanjar.weixin.mp.constant.WxMpEventConstants.POI_CHECK_NOTIFY;
/**
* wechat mp configuration
*
* @author admin
*/
@AllArgsConstructor
@Configuration
public class WxMpConfiguration {
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;
@Bean
public WxMpService wxMpService() {
CustomWxMpProperties customWxMpProperties = WxMpPropertiesUtils.getWeiChatProperties();
if (StrUtil.isBlankIfStr(customWxMpProperties.getAppid()) || StrUtil.isBlankIfStr(customWxMpProperties.getAppid()) || StrUtil.isBlankIfStr(customWxMpProperties.getAppid()) || StrUtil.isBlankIfStr(customWxMpProperties.getAppid())) {
throw new RuntimeException("微信配置文件出错");
}
return getWxMpService(customWxMpProperties);
}
private static WxMpService getWxMpService(CustomWxMpProperties customWxMpProperties) {
Map<String, WxMpConfigStorage> multiConfigStorages = new HashMap<>(16);
WxMpDefaultConfigImpl configStorage = new WxMpDefaultConfigImpl();
configStorage.setAppId(customWxMpProperties.getAppid());
configStorage.setSecret(customWxMpProperties.getSecret());
configStorage.setToken(customWxMpProperties.getToken());
configStorage.setAesKey(customWxMpProperties.getAesKey());
WxMpService mpService = new WxMpServiceImpl();
mpService.setMultiConfigStorages(multiConfigStorages);
return mpService;
}
@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(WxConsts.EventType.CLICK).handler(this.menuHandler).end();
// 点击菜单连接事件
newRouter.rule().async(false).msgType(EVENT).event(WxConsts.EventType.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;
}
}
//package com.starry.admin.modules.weichat.config;
//
//
//import cn.hutool.core.util.StrUtil;
//import com.starry.admin.modules.weichat.entity.CustomWxMpProperties;
//import com.starry.admin.modules.weichat.handler.*;
//import com.starry.admin.modules.weichat.utils.WxMpPropertiesUtils;
//import lombok.AllArgsConstructor;
//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.api.impl.WxMpServiceImpl;
//import me.chanjar.weixin.mp.config.WxMpConfigStorage;
//import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl;
//import org.springframework.context.annotation.Bean;
//import org.springframework.context.annotation.Configuration;
//
//import java.util.HashMap;
//import java.util.Map;
//
//import static me.chanjar.weixin.common.api.WxConsts.EventType.SUBSCRIBE;
//import static me.chanjar.weixin.common.api.WxConsts.EventType.UNSUBSCRIBE;
//import static me.chanjar.weixin.common.api.WxConsts.XmlMsgType.EVENT;
//import static me.chanjar.weixin.mp.constant.WxMpEventConstants.CustomerService.*;
//import static me.chanjar.weixin.mp.constant.WxMpEventConstants.POI_CHECK_NOTIFY;
//
///**
// * wechat mp configuration
// *
// * @author admin
// */
//@AllArgsConstructor
//@Configuration
//public class WxMpConfiguration {
// 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;
//
// @Bean
// public WxMpService wxMpService() {
// CustomWxMpProperties customWxMpProperties = WxMpPropertiesUtils.getWeiChatProperties();
// if (StrUtil.isBlankIfStr(customWxMpProperties.getAppid()) || StrUtil.isBlankIfStr(customWxMpProperties.getAppid()) || StrUtil.isBlankIfStr(customWxMpProperties.getAppid()) || StrUtil.isBlankIfStr(customWxMpProperties.getAppid())) {
// throw new RuntimeException("微信配置文件出错");
// }
// return getWxMpService(customWxMpProperties);
// }
//
// private static WxMpService getWxMpService(CustomWxMpProperties customWxMpProperties) {
// Map<String, WxMpConfigStorage> multiConfigStorages = new HashMap<>();
// WxMpDefaultConfigImpl configStorage = new WxMpDefaultConfigImpl();
// configStorage.setAppId(customWxMpProperties.getAppid());
// configStorage.setSecret(customWxMpProperties.getSecret());
// configStorage.setToken(customWxMpProperties.getToken());
// configStorage.setAesKey(customWxMpProperties.getAesKey());
//
//
// WxMpService mpService = new WxMpServiceImpl();
// mpService.setMultiConfigStorages(multiConfigStorages);
// return mpService;
// }
//
// @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(WxConsts.EventType.CLICK).handler(this.menuHandler).end();
//
// // 点击菜单连接事件
// newRouter.rule().async(false).msgType(EVENT).event(WxConsts.EventType.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;
// }
//
//}

View File

@@ -0,0 +1,29 @@
package com.starry.admin.modules.weichat.controller;
import com.starry.admin.modules.weichat.service.WxMpApi;
import com.starry.common.result.R;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @Author: huchuansai
* @Date: 2024/3/27 10:39 PM
* @Description:
*/
@RestController
@RequestMapping("/wx/test")
@RequiredArgsConstructor
public class WxMpTestController {
private final WxMpApi wxMpApi;
@GetMapping("/getToken")
public R getToken(String appId, String secret) {
return R.ok(wxMpApi.getAccessToken(appId, secret, true));
}
}

View File

@@ -3,7 +3,6 @@ package com.starry.admin.modules.weichat.controller;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.mp.api.WxMpMessageRouter;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
@@ -24,8 +23,8 @@ public class WxPortalController {
@Resource
private final WxMpService wxService;
@Resource
private final WxMpMessageRouter messageRouter;
//@Resource
//private final WxMpMessageRouter messageRouter;
@GetMapping(produces = "text/plain;charset=utf-8")
public String authGet(@PathVariable String appid,
@@ -100,11 +99,11 @@ public class WxPortalController {
}
private WxMpXmlOutMessage route(WxMpXmlMessage message) {
try {
return this.messageRouter.route(message);
} catch (Exception e) {
log.error("路由消息时出现异常!", e);
}
//try {
// return this.messageRouter.route(message);
//} catch (Exception e) {
// log.error("路由消息时出现异常!", e);
//}
return null;
}

View File

@@ -0,0 +1,56 @@
package com.starry.admin.modules.weichat.service;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.starry.admin.common.exception.CustomException;
import com.starry.admin.modules.platform.entity.SysTenantEntity;
import com.starry.admin.modules.platform.service.ISysTenantService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.config.impl.WxMpMapConfigImpl;
import org.springframework.stereotype.Service;
import java.util.Objects;
/**
* @Author: huchuansai
* @Date: 2024/3/27 10:27 PM
* @Description:
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class WxMpApi {
private final WxMpService mpService;
private final ISysTenantService sysTenantService;
public String getAccessToken(String appId, boolean forceUpdate) {
SysTenantEntity tenant = sysTenantService.getOne(Wrappers.lambdaQuery(SysTenantEntity.class).eq(SysTenantEntity::getAppId, appId));
if (Objects.isNull(tenant)) {
throw new CustomException("该APPID未查询到相关租户");
}
if (StringUtils.isEmpty(tenant.getSecret())) {
throw new CustomException("公众号秘钥不能为空");
}
return getAccessToken(appId, tenant.getSecret(), forceUpdate);
}
public String getAccessToken(String appId, String secret, boolean forceUpdate) {
WxMpMapConfigImpl config = new WxMpMapConfigImpl();
config.setAppId(appId);
config.setSecret(secret);
config.setUseStableAccessToken(true);
mpService.addConfigStorage(appId, config);
WxMpService wxMpService = mpService.switchoverTo(appId);
try {
return wxMpService.getAccessToken(forceUpdate);
} catch (WxErrorException e) {
log.error(e.getMessage(), e);
throw new RuntimeException(e);
}
}
}

View File

@@ -3,7 +3,6 @@ package com.starry.admin.modules.weichat.service.impl;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.starry.admin.modules.weichat.constant.ConfigConstant;
import com.starry.admin.modules.weichat.entity.WxUser;
import com.starry.admin.modules.weichat.handler.SubscribeHandler;
@@ -145,7 +144,7 @@ public class WxUserServiceImpl extends ServiceImpl<WxUserMapper, WxUser> impleme
if (count <= 0) {
return new ArrayList<>();
}
List<WxMpUser> list = Lists.newArrayList();
List<WxMpUser> list = new ArrayList<>();
List<WxMpUser> followersInfoList;
int a = count % 100 > 0 ? count / 100 + 1 : count / 100;
for (int i = 0; i < a; i++) {

View File

@@ -1,8 +1,10 @@
server:
port: 7002
servlet:
context-path: /api
spring:
profiles:
active: dev
active: test
# mybatis日志
mybatis-plus:
@@ -13,4 +15,18 @@ mybatis-plus:
logic-delete-value: 1
logic-not-delete-value: 0
# xml文件路径classpath* 代表所有模块的resources目录 classpath 不加星号代表当前模块下的resources目录
mapper-locations: classpath*:mapper/**/*.xml
mapper-locations: classpath*:mapper/**/*.xml
wx:
mp:
app-id: xxx
secret: ddd
config-storage:
key-prefix: peipei
http-client-type: HttpClient
redis:
host: 12
database: ${spring.redis.database}
port: ${spring.redis.port}
password: ${spring.redis.password}
type: RedisTemplate