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

@@ -70,31 +70,37 @@
</dependency> </dependency>
<!--weixin-java-common--> <!--weixin-java-common-->
<dependency> <!-- <dependency>-->
<groupId>com.github.binarywang</groupId> <!-- <groupId>com.github.binarywang</groupId>-->
<artifactId>weixin-java-common</artifactId> <!-- <artifactId>weixin-java-common</artifactId>-->
</dependency> <!-- </dependency>-->
<!--weixin-java-admin--> <!--weixin-java-admin-->
<!-- <dependency>-->
<!-- <groupId>com.github.binarywang</groupId>-->
<!-- <artifactId>weixin-java-mp</artifactId>-->
<!-- </dependency>-->
<!-- &lt;!&ndash;weixin-java-miniapp&ndash;&gt;-->
<!-- <dependency>-->
<!-- <groupId>com.github.binarywang</groupId>-->
<!-- <artifactId>weixin-java-miniapp</artifactId>-->
<!-- </dependency>-->
<dependency> <dependency>
<groupId>com.github.binarywang</groupId> <groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-mp</artifactId> <artifactId>wx-java-mp-spring-boot-starter</artifactId>
</dependency> <version>4.5.0</version>
<!--weixin-java-miniapp-->
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-miniapp</artifactId>
</dependency> </dependency>
<!--weixin-java-pay--> <!--weixin-java-pay-->
<dependency> <dependency>
<groupId>com.github.binarywang</groupId> <groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-pay</artifactId> <artifactId>weixin-java-pay</artifactId>
<exclusions> <!-- <exclusions>-->
<exclusion> <!-- <exclusion>-->
<artifactId>guava</artifactId> <!-- <artifactId>guava</artifactId>-->
<groupId>com.google.guava</groupId> <!-- <groupId>com.google.guava</groupId>-->
</exclusion> <!-- </exclusion>-->
</exclusions> <!-- </exclusions>-->
</dependency> </dependency>

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) { public R handleRuntimeException(RuntimeException e, HttpServletRequest request) {
String requestUrl = request.getRequestURI(); String requestUrl = request.getRequestURI();
log.error("请求地址'{}',发生未知异常.", requestUrl, e); 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/**" "/v2/api-docs/**"
).permitAll() ).permitAll()
// 对登录注册要允许匿名访问 // 对登录注册要允许匿名访问
.antMatchers("/login", "/captcha/get-captcha").permitAll() .antMatchers("/login", "/captcha/get-captcha", "/wx/test/**").permitAll()
// 跨域请求会先进行一次options请求 // 跨域请求会先进行一次options请求
.antMatchers(HttpMethod.OPTIONS).permitAll() .antMatchers(HttpMethod.OPTIONS).permitAll()
.anyRequest()// 除上面外的所有请求全部需要鉴权认证 .anyRequest()// 除上面外的所有请求全部需要鉴权认证

View File

@@ -1,111 +1,111 @@
package com.starry.admin.modules.weichat.config; //package com.starry.admin.modules.weichat.config;
//
//
import cn.hutool.core.util.StrUtil; //import cn.hutool.core.util.StrUtil;
import com.starry.admin.modules.weichat.entity.CustomWxMpProperties; //import com.starry.admin.modules.weichat.entity.CustomWxMpProperties;
import com.starry.admin.modules.weichat.handler.*; //import com.starry.admin.modules.weichat.handler.*;
import com.starry.admin.modules.weichat.utils.WxMpPropertiesUtils; //import com.starry.admin.modules.weichat.utils.WxMpPropertiesUtils;
import lombok.AllArgsConstructor; //import lombok.AllArgsConstructor;
import me.chanjar.weixin.common.api.WxConsts; //import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.mp.api.WxMpMessageRouter; //import me.chanjar.weixin.mp.api.WxMpMessageRouter;
import me.chanjar.weixin.mp.api.WxMpService; //import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl; //import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
import me.chanjar.weixin.mp.config.WxMpConfigStorage; //import me.chanjar.weixin.mp.config.WxMpConfigStorage;
import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl; //import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl;
import org.springframework.context.annotation.Bean; //import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; //import org.springframework.context.annotation.Configuration;
//
import java.util.HashMap; //import java.util.HashMap;
import java.util.Map; //import java.util.Map;
//
import static me.chanjar.weixin.common.api.WxConsts.EventType.SUBSCRIBE; //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.EventType.UNSUBSCRIBE;
import static me.chanjar.weixin.common.api.WxConsts.XmlMsgType.EVENT; //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.CustomerService.*;
import static me.chanjar.weixin.mp.constant.WxMpEventConstants.POI_CHECK_NOTIFY; //import static me.chanjar.weixin.mp.constant.WxMpEventConstants.POI_CHECK_NOTIFY;
//
/** ///**
* wechat mp configuration // * wechat mp configuration
* // *
* @author admin // * @author admin
*/ // */
@AllArgsConstructor //@AllArgsConstructor
@Configuration //@Configuration
public class WxMpConfiguration { //public class WxMpConfiguration {
private final LogHandler logHandler; // private final LogHandler logHandler;
private final NullHandler nullHandler; // private final NullHandler nullHandler;
private final KfSessionHandler kfSessionHandler; // private final KfSessionHandler kfSessionHandler;
private final StoreCheckNotifyHandler storeCheckNotifyHandler; // private final StoreCheckNotifyHandler storeCheckNotifyHandler;
private final LocationHandler locationHandler; // private final LocationHandler locationHandler;
private final MenuHandler menuHandler; // private final MenuHandler menuHandler;
private final MsgHandler msgHandler; // private final MsgHandler msgHandler;
private final UnsubscribeHandler unsubscribeHandler; // private final UnsubscribeHandler unsubscribeHandler;
private final SubscribeHandler subscribeHandler; // private final SubscribeHandler subscribeHandler;
private final ScanHandler scanHandler; // private final ScanHandler scanHandler;
//
@Bean // @Bean
public WxMpService wxMpService() { // public WxMpService wxMpService() {
CustomWxMpProperties customWxMpProperties = WxMpPropertiesUtils.getWeiChatProperties(); // CustomWxMpProperties customWxMpProperties = WxMpPropertiesUtils.getWeiChatProperties();
if (StrUtil.isBlankIfStr(customWxMpProperties.getAppid()) || StrUtil.isBlankIfStr(customWxMpProperties.getAppid()) || StrUtil.isBlankIfStr(customWxMpProperties.getAppid()) || StrUtil.isBlankIfStr(customWxMpProperties.getAppid())) { // if (StrUtil.isBlankIfStr(customWxMpProperties.getAppid()) || StrUtil.isBlankIfStr(customWxMpProperties.getAppid()) || StrUtil.isBlankIfStr(customWxMpProperties.getAppid()) || StrUtil.isBlankIfStr(customWxMpProperties.getAppid())) {
throw new RuntimeException("微信配置文件出错"); // throw new RuntimeException("微信配置文件出错");
} // }
return getWxMpService(customWxMpProperties); // return getWxMpService(customWxMpProperties);
} // }
//
private static WxMpService getWxMpService(CustomWxMpProperties customWxMpProperties) { // private static WxMpService getWxMpService(CustomWxMpProperties customWxMpProperties) {
Map<String, WxMpConfigStorage> multiConfigStorages = new HashMap<>(16); // Map<String, WxMpConfigStorage> multiConfigStorages = new HashMap<>();
WxMpDefaultConfigImpl configStorage = new WxMpDefaultConfigImpl(); // WxMpDefaultConfigImpl configStorage = new WxMpDefaultConfigImpl();
configStorage.setAppId(customWxMpProperties.getAppid()); // configStorage.setAppId(customWxMpProperties.getAppid());
configStorage.setSecret(customWxMpProperties.getSecret()); // configStorage.setSecret(customWxMpProperties.getSecret());
configStorage.setToken(customWxMpProperties.getToken()); // configStorage.setToken(customWxMpProperties.getToken());
configStorage.setAesKey(customWxMpProperties.getAesKey()); // configStorage.setAesKey(customWxMpProperties.getAesKey());
//
//
WxMpService mpService = new WxMpServiceImpl(); // WxMpService mpService = new WxMpServiceImpl();
mpService.setMultiConfigStorages(multiConfigStorages); // mpService.setMultiConfigStorages(multiConfigStorages);
return mpService; // return mpService;
} // }
//
@Bean // @Bean
public WxMpMessageRouter messageRouter(WxMpService wxMpService) { // public WxMpMessageRouter messageRouter(WxMpService wxMpService) {
final WxMpMessageRouter newRouter = new WxMpMessageRouter(wxMpService); // final WxMpMessageRouter newRouter = new WxMpMessageRouter(wxMpService);
//
// 记录所有事件的日志 (异步执行) // // 记录所有事件的日志 (异步执行)
newRouter.rule().handler(this.logHandler).next(); // 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_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_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(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(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.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(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(SUBSCRIBE).handler(this.subscribeHandler).end();
//
// 取消关注事件 // // 取消关注事件
newRouter.rule().async(false).msgType(EVENT).event(UNSUBSCRIBE).handler(this.unsubscribeHandler).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(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(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).msgType(EVENT).event(WxConsts.EventType.SCAN).handler(this.scanHandler).end();
//
// 默认 // // 默认
newRouter.rule().async(false).handler(this.msgHandler).end(); // newRouter.rule().async(false).handler(this.msgHandler).end();
//
return newRouter; // 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.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.mp.api.WxMpMessageRouter;
import me.chanjar.weixin.mp.api.WxMpService; 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;
@@ -24,8 +23,8 @@ public class WxPortalController {
@Resource @Resource
private final WxMpService wxService; private final WxMpService wxService;
@Resource //@Resource
private final WxMpMessageRouter messageRouter; //private final WxMpMessageRouter messageRouter;
@GetMapping(produces = "text/plain;charset=utf-8") @GetMapping(produces = "text/plain;charset=utf-8")
public String authGet(@PathVariable String appid, public String authGet(@PathVariable String appid,
@@ -100,11 +99,11 @@ public class WxPortalController {
} }
private WxMpXmlOutMessage route(WxMpXmlMessage message) { private WxMpXmlOutMessage route(WxMpXmlMessage message) {
try { //try {
return this.messageRouter.route(message); // return this.messageRouter.route(message);
} catch (Exception e) { //} catch (Exception e) {
log.error("路由消息时出现异常!", e); // log.error("路由消息时出现异常!", e);
} //}
return null; 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.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 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.constant.ConfigConstant;
import com.starry.admin.modules.weichat.entity.WxUser; import com.starry.admin.modules.weichat.entity.WxUser;
import com.starry.admin.modules.weichat.handler.SubscribeHandler; import com.starry.admin.modules.weichat.handler.SubscribeHandler;
@@ -145,7 +144,7 @@ public class WxUserServiceImpl extends ServiceImpl<WxUserMapper, WxUser> impleme
if (count <= 0) { if (count <= 0) {
return new ArrayList<>(); return new ArrayList<>();
} }
List<WxMpUser> list = Lists.newArrayList(); List<WxMpUser> list = new ArrayList<>();
List<WxMpUser> followersInfoList; List<WxMpUser> followersInfoList;
int a = count % 100 > 0 ? count / 100 + 1 : count / 100; int a = count % 100 > 0 ? count / 100 + 1 : count / 100;
for (int i = 0; i < a; i++) { for (int i = 0; i < a; i++) {

View File

@@ -1,8 +1,10 @@
server: server:
port: 7002 port: 7002
servlet:
context-path: /api
spring: spring:
profiles: profiles:
active: dev active: test
# mybatis日志 # mybatis日志
mybatis-plus: mybatis-plus:
@@ -13,4 +15,18 @@ mybatis-plus:
logic-delete-value: 1 logic-delete-value: 1
logic-not-delete-value: 0 logic-not-delete-value: 0
# xml文件路径classpath* 代表所有模块的resources目录 classpath 不加星号代表当前模块下的resources目录 # 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