refactor(qrlogin): 重构二维码登录功能并添加租户支持

- 在 QrLoginController 中添加 tenantId 参数
- 在 QrLoginService 接口中修改 generateQrLoginToken 方法签名,添加 tenantId 参数
- 重构 QrLoginServiceImpl 中的 generateQrLoginToken 方法,使用 WxService 获取 AccessToken
- 新增 WxService 类,用于获取微信 AccessToken,并支持租户 ID
This commit is contained in:
2025-09-09 00:53:05 +08:00
parent db6eb892c4
commit e5a15f1b9f
4 changed files with 120 additions and 69 deletions

View File

@@ -35,7 +35,7 @@ public class QrLoginController extends BaseController {
@PostMapping("/generate") @PostMapping("/generate")
public ApiResult<?> generateQrLoginToken() { public ApiResult<?> generateQrLoginToken() {
try { try {
QrLoginGenerateResponse response = qrLoginService.generateQrLoginToken(); QrLoginGenerateResponse response = qrLoginService.generateQrLoginToken(getTenantId());
return success("生成成功", response); return success("生成成功", response);
} catch (Exception e) { } catch (Exception e) {
return fail(e.getMessage()); return fail(e.getMessage());

View File

@@ -17,7 +17,7 @@ public interface QrLoginService {
* *
* @return QrLoginGenerateResponse * @return QrLoginGenerateResponse
*/ */
QrLoginGenerateResponse generateQrLoginToken(); QrLoginGenerateResponse generateQrLoginToken(Integer tenantId);
/** /**
* 检查扫码登录状态 * 检查扫码登录状态

View File

@@ -14,8 +14,8 @@ import com.gxwebsoft.common.core.security.JwtSubject;
import com.gxwebsoft.common.core.security.JwtUtil; import com.gxwebsoft.common.core.security.JwtUtil;
import com.gxwebsoft.common.core.utils.RedisUtil; import com.gxwebsoft.common.core.utils.RedisUtil;
import com.gxwebsoft.common.system.entity.User; import com.gxwebsoft.common.system.entity.User;
import com.gxwebsoft.common.system.service.SettingService;
import com.gxwebsoft.common.system.service.UserService; import com.gxwebsoft.common.system.service.UserService;
import com.gxwebsoft.common.system.service.WxService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.RedisTemplate;
@@ -47,15 +47,12 @@ public class QrLoginServiceImpl implements QrLoginService {
private ConfigProperties configProperties; private ConfigProperties configProperties;
@Autowired @Autowired
private SettingService settingService; private WxService wxService;
@Autowired private static final String QR_LOGIN_TOKEN = "QR_LOGIN_TOKEN";
private RedisTemplate<String, String> redisTemplate;
private static final String ACCESS_TOKEN_KEY = "WX_ACCESS_TOKEN";
@Override @Override
public QrLoginGenerateResponse generateQrLoginToken() { public QrLoginGenerateResponse generateQrLoginToken(Integer tenantId) {
// 生成唯一的扫码登录token // 生成唯一的扫码登录token
String token = UUID.randomUUID().toString(true); String token = UUID.randomUUID().toString(true);
@@ -85,7 +82,7 @@ public class QrLoginServiceImpl implements QrLoginService {
// 生成微信小程序码 // 生成微信小程序码
try { try {
String miniprogramQrCodeUrl = generateMiniprogramQrCode(token); String miniprogramQrCodeUrl = generateMiniprogramQrCode(token,tenantId);
response.setMiniprogramQrCodeUrl(miniprogramQrCodeUrl); response.setMiniprogramQrCodeUrl(miniprogramQrCodeUrl);
} catch (Exception e) { } catch (Exception e) {
log.warn("生成微信小程序码失败: {}", e.getMessage()); log.warn("生成微信小程序码失败: {}", e.getMessage());
@@ -234,9 +231,10 @@ public class QrLoginServiceImpl implements QrLoginService {
/** /**
* 生成微信小程序码 * 生成微信小程序码
*/ */
private String generateMiniprogramQrCode(String token) { private String generateMiniprogramQrCode(String token, Integer tenantId) {
try { try {
String accessToken = getWxAccessToken(); // 使用公共的 WxService 获取 AccessToken
String accessToken = wxService.getAccessToken(tenantId);
if (StrUtil.isBlank(accessToken)) { if (StrUtil.isBlank(accessToken)) {
throw new RuntimeException("获取微信AccessToken失败"); throw new RuntimeException("获取微信AccessToken失败");
} }
@@ -275,64 +273,7 @@ public class QrLoginServiceImpl implements QrLoginService {
} }
} }
/**
* 获取微信AccessToken
*/
private String getWxAccessToken() {
try {
String key = ACCESS_TOKEN_KEY + ":10048"; // 默认租户ID可以根据需要调整
// 从缓存获取
String cachedToken = redisTemplate.opsForValue().get(key);
if (StrUtil.isNotBlank(cachedToken)) {
try {
JSONObject tokenData = JSON.parseObject(cachedToken);
String accessToken = tokenData.getString("access_token");
if (StrUtil.isNotBlank(accessToken)) {
return accessToken;
}
} catch (Exception e) {
// 解析失败,可能是旧格式,直接使用
if (!cachedToken.startsWith("{")) {
return cachedToken;
}
}
}
// 缓存中没有,重新获取
JSONObject setting = settingService.getBySettingKey("mp-weixin");
if (setting == null) {
throw new RuntimeException("请先配置微信小程序");
}
String appId = setting.getString("appId");
String appSecret = setting.getString("appSecret");
if (StrUtil.isBlank(appId) || StrUtil.isBlank(appSecret)) {
throw new RuntimeException("微信小程序配置不完整");
}
// 调用微信API获取AccessToken
String apiUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="
+ appId + "&secret=" + appSecret;
String response = HttpRequest.get(apiUrl).execute().body();
JSONObject result = JSON.parseObject(response);
String accessToken = result.getString("access_token");
if (StrUtil.isNotBlank(accessToken)) {
// 存入缓存
JSONObject tokenData = new JSONObject();
tokenData.put("access_token", accessToken);
tokenData.put("expires_in", result.get("expires_in"));
redisTemplate.opsForValue().set(key, tokenData.toJSONString(), 7000L, TimeUnit.SECONDS);
return accessToken;
} else {
throw new RuntimeException("获取AccessToken失败: " + response);
}
} catch (Exception e) {
log.error("获取微信AccessToken失败: {}", e.getMessage(), e);
return null;
}
}
/** /**
* 获取文件上传路径 * 获取文件上传路径

View File

@@ -0,0 +1,110 @@
package com.gxwebsoft.common.system.service;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
/**
* 微信公共服务类
*
* @author 科技小王子
* @since 2025-09-08
*/
@Slf4j
@Service
public class WxService {
@Autowired
private SettingService settingService;
@Autowired
private RedisTemplate<String, String> redisTemplate;
private static final String ACCESS_TOKEN_KEY = "WX_ACCESS_TOKEN";
/**
* 获取微信AccessToken使用默认租户
*/
public String getAccessToken() {
return getAccessToken(null);
}
/**
* 获取微信AccessToken支持指定租户ID
*
* @param tenantId 租户ID为null时使用默认值
* @return access_token
*/
public String getAccessToken(Integer tenantId) {
if (tenantId == null) {
tenantId = 10048; // 默认租户ID可以根据需要调整
}
String key = ACCESS_TOKEN_KEY + ":" + tenantId;
// 从缓存获取
String cachedToken = redisTemplate.opsForValue().get(key);
if (StrUtil.isNotBlank(cachedToken)) {
try {
JSONObject tokenData = JSON.parseObject(cachedToken);
String accessToken = tokenData.getString("access_token");
if (StrUtil.isNotBlank(accessToken)) {
log.debug("从缓存获取access_token: {}", accessToken);
return accessToken;
}
} catch (Exception e) {
// 解析失败,可能是旧格式,直接使用
if (!cachedToken.startsWith("{")) {
log.debug("从缓存获取access_token(旧格式): {}", cachedToken);
return cachedToken;
}
log.warn("解析缓存的access_token失败: {}", e.getMessage());
// 缓存数据异常,删除缓存,重新获取
redisTemplate.delete(key);
}
}
// 缓存中没有,重新获取
try {
JSONObject setting = settingService.getBySettingKey("mp-weixin");
if (setting == null) {
throw new RuntimeException("请先配置微信小程序");
}
String appId = setting.getString("appId");
String appSecret = setting.getString("appSecret");
if (StrUtil.isBlank(appId) || StrUtil.isBlank(appSecret)) {
throw new RuntimeException("微信小程序配置不完整");
}
// 调用微信API获取AccessToken
String apiUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="
+ appId + "&secret=" + appSecret;
String response = HttpRequest.get(apiUrl).execute().body();
JSONObject result = JSON.parseObject(response);
String accessToken = result.getString("access_token");
if (StrUtil.isNotBlank(accessToken)) {
// 存入缓存
JSONObject tokenData = new JSONObject();
tokenData.put("access_token", accessToken);
tokenData.put("expires_in", result.get("expires_in"));
redisTemplate.opsForValue().set(key, tokenData.toJSONString(), 7000L, TimeUnit.SECONDS);
log.info("获取新的access_token成功: {}", accessToken);
return accessToken;
} else {
throw new RuntimeException("获取AccessToken失败: " + response);
}
} catch (Exception e) {
log.error("获取微信AccessToken失败: {}", e.getMessage(), e);
throw new RuntimeException("获取微信AccessToken失败: " + e.getMessage());
}
}
}