feat(system): 优化微信登录和用户注册逻辑

- 新增微信小程序数据解密工具类 WxMiniProgramDecryptUtil
- 重构微信登录接口,支持两种方式获取手机号
- 优化用户注册逻辑,增加租户ID参数
- 更新相关实体类和参数类,以支持新的功能
This commit is contained in:
2025-09-03 18:25:45 +08:00
parent 5961fdbcb9
commit 61dab98a6d
15 changed files with 641 additions and 72 deletions

View File

@@ -63,7 +63,6 @@ public class MybatisPlusConfig {
public boolean ignoreTable(String tableName) { public boolean ignoreTable(String tableName) {
return Arrays.asList( return Arrays.asList(
"sys_tenant", "sys_tenant",
// "sys_user",
"sys_dictionary", "sys_dictionary",
"sys_dictionary_data", "sys_dictionary_data",
"sys_user_oauth", "sys_user_oauth",

View File

@@ -0,0 +1,105 @@
package com.gxwebsoft.common.core.utils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.spec.AlgorithmParameterSpec;
/**
* 微信小程序数据解密工具类
* 用于解密微信小程序的encryptedData数据
*
* @author WebSoft
*/
public class WxMiniProgramDecryptUtil {
/**
* 解密微信小程序数据
*
* @param encryptedData 加密的数据
* @param sessionKey 会话密钥
* @param iv 初始向量
* @return 解密后的JSON字符串
* @throws Exception 解密失败时抛出异常
*/
public static String decrypt(String encryptedData, String sessionKey, String iv) throws Exception {
// Base64解码
byte[] dataByte = Base64.decodeBase64(encryptedData);
byte[] keyByte = Base64.decodeBase64(sessionKey);
byte[] ivByte = Base64.decodeBase64(iv);
try {
// 如果密钥长度不够则补齐到32位
if (keyByte.length % 16 != 0) {
int groups = keyByte.length / 16 + (keyByte.length % 16 != 0 ? 1 : 0);
byte[] temp = new byte[groups * 16];
System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
keyByte = temp;
}
// 初始化
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
AlgorithmParameterSpec paramSpec = new IvParameterSpec(ivByte);
cipher.init(Cipher.DECRYPT_MODE, spec, paramSpec);
// 解密
byte[] resultByte = cipher.doFinal(dataByte);
if (null != resultByte && resultByte.length > 0) {
String result = new String(resultByte, StandardCharsets.UTF_8);
return result;
}
} catch (Exception e) {
throw new Exception("微信小程序数据解密失败", e);
}
return null;
}
/**
* 解密手机号信息
*
* @param encryptedData 加密的数据
* @param sessionKey 会话密钥
* @param iv 初始向量
* @return 手机号码解密失败返回null
*/
public static String decryptPhoneNumber(String encryptedData, String sessionKey, String iv) {
try {
String decryptedData = decrypt(encryptedData, sessionKey, iv);
if (decryptedData != null) {
JSONObject jsonObject = JSON.parseObject(decryptedData);
return jsonObject.getString("phoneNumber");
}
} catch (Exception e) {
System.err.println("解密手机号失败: " + e.getMessage());
e.printStackTrace();
}
return null;
}
/**
* 解密用户信息
*
* @param encryptedData 加密的数据
* @param sessionKey 会话密钥
* @param iv 初始向量
* @return 解密后的用户信息JSON对象解密失败返回null
*/
public static JSONObject decryptUserInfo(String encryptedData, String sessionKey, String iv) {
try {
String decryptedData = decrypt(encryptedData, sessionKey, iv);
if (decryptedData != null) {
return JSON.parseObject(decryptedData);
}
} catch (Exception e) {
System.err.println("解密用户信息失败: " + e.getMessage());
e.printStackTrace();
}
return null;
}
}

View File

@@ -1,5 +1,6 @@
package com.gxwebsoft.common.system.controller; package com.gxwebsoft.common.system.controller;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@@ -11,6 +12,7 @@ import com.gxwebsoft.common.system.entity.Setting;
import com.gxwebsoft.common.system.entity.Tenant; import com.gxwebsoft.common.system.entity.Tenant;
import com.gxwebsoft.common.system.entity.User; import com.gxwebsoft.common.system.entity.User;
import com.gxwebsoft.common.system.param.SettingParam; import com.gxwebsoft.common.system.param.SettingParam;
import com.gxwebsoft.common.system.param.SettingUpdateParam;
import com.gxwebsoft.common.system.service.SettingService; import com.gxwebsoft.common.system.service.SettingService;
import com.gxwebsoft.common.system.service.TenantService; import com.gxwebsoft.common.system.service.TenantService;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
@@ -76,7 +78,16 @@ public class SettingController extends BaseController {
@OperationLog @OperationLog
@ApiOperation("添加系统设置") @ApiOperation("添加系统设置")
@PostMapping() @PostMapping()
public ApiResult<?> save(@RequestBody Setting setting) { public ApiResult<?> save(@RequestBody SettingUpdateParam settingParam) {
final User loginUser = getLoginUser();
if(loginUser == null){
return fail("请先登录");
}
// 转换为Setting对象
Setting setting = convertToSetting(settingParam);
setting.setTenantId(loginUser.getTenantId());
if (settingService.save(setting)) { if (settingService.save(setting)) {
return success("添加成功"); return success("添加成功");
} }
@@ -87,11 +98,15 @@ public class SettingController extends BaseController {
@OperationLog @OperationLog
@ApiOperation("修改系统设置") @ApiOperation("修改系统设置")
@PutMapping() @PutMapping()
public ApiResult<?> update(@RequestBody Setting setting) { public ApiResult<?> update(@RequestBody SettingUpdateParam settingParam) {
final User loginUser = getLoginUser(); final User loginUser = getLoginUser();
if(loginUser == null){ if(loginUser == null){
return fail("请先登录"); return fail("请先登录");
} }
// 转换为Setting对象
Setting setting = convertToSetting(settingParam);
setting.setTenantId(loginUser.getTenantId());
if (settingService.updateById(setting)) { if (settingService.updateById(setting)) {
// 更新系统设置信息到缓存 key = "" // 更新系统设置信息到缓存 key = ""
String key = setting.getSettingKey().concat(":").concat(loginUser.getTenantId().toString()); String key = setting.getSettingKey().concat(":").concat(loginUser.getTenantId().toString());
@@ -178,7 +193,7 @@ public class SettingController extends BaseController {
@ApiOperation("根据key更新系统设置") @ApiOperation("根据key更新系统设置")
@OperationLog @OperationLog
@PutMapping("/updateByKey") @PutMapping("/updateByKey")
public ApiResult<?> updateByKey(@RequestBody Setting setting) { public ApiResult<?> updateByKey(@RequestBody SettingUpdateParam settingParam) {
final User loginUser = getLoginUser(); final User loginUser = getLoginUser();
if(loginUser == null){ if(loginUser == null){
return fail("请先登录"); return fail("请先登录");
@@ -186,6 +201,11 @@ public class SettingController extends BaseController {
if(!loginUser.getIsSuperAdmin()){ if(!loginUser.getIsSuperAdmin()){
return fail("权限不足"); return fail("权限不足");
} }
// 转换为Setting对象
Setting setting = convertToSetting(settingParam);
setting.setTenantId(loginUser.getTenantId());
return success(settingService.updateByKey(setting)); return success(settingService.updateByKey(setting));
} }
@@ -193,7 +213,10 @@ public class SettingController extends BaseController {
@OperationLog @OperationLog
@ApiOperation("更新主题皮肤") @ApiOperation("更新主题皮肤")
@PutMapping("/theme") @PutMapping("/theme")
public ApiResult<?> theme(@RequestBody Setting setting) { public ApiResult<?> theme(@RequestBody SettingUpdateParam settingParam) {
// 转换为Setting对象
Setting setting = convertToSetting(settingParam);
setting.setTenantId(getTenantId());
String key = "theme:".concat(getTenantId().toString()); String key = "theme:".concat(getTenantId().toString());
// 新增 // 新增
final Setting one = settingService.getOne(new LambdaQueryWrapper<Setting>().eq(Setting::getSettingKey, setting.getSettingKey())); final Setting one = settingService.getOne(new LambdaQueryWrapper<Setting>().eq(Setting::getSettingKey, setting.getSettingKey()));
@@ -218,4 +241,156 @@ public class SettingController extends BaseController {
String key = "theme:".concat(getTenantId().toString()); String key = "theme:".concat(getTenantId().toString());
return success("获取成功",redisUtil.get(key)); return success("获取成功",redisUtil.get(key));
} }
/**
* 将SettingUpdateParam转换为Setting对象
* 根据settingKey的不同将相应的字段组装成JSON格式的content
*/
private Setting convertToSetting(SettingUpdateParam param) {
Setting setting = new Setting();
setting.setSettingKey(param.getSettingKey());
setting.setSortNumber(param.getSortNumber());
setting.setComments(param.getComments());
setting.setTenantId(param.getTenantId());
// 如果直接提供了content则使用content
if (StrUtil.isNotBlank(param.getContent())) {
setting.setContent(param.getContent());
return setting;
}
// 根据settingKey构建相应的JSON content
JSONObject contentJson = new JSONObject();
switch (param.getSettingKey()) {
case "mp-weixin":
// 微信小程序配置
if (StrUtil.isNotBlank(param.getAppId())) {
contentJson.put("appId", param.getAppId());
}
if (StrUtil.isNotBlank(param.getAppSecret())) {
contentJson.put("appSecret", param.getAppSecret());
}
break;
case "payment":
// 支付配置
if (StrUtil.isNotBlank(param.getMchId())) {
contentJson.put("mchId", param.getMchId());
}
if (StrUtil.isNotBlank(param.getApiKey())) {
contentJson.put("apiKey", param.getApiKey());
}
if (StrUtil.isNotBlank(param.getWechatApiKey())) {
contentJson.put("wechatApiKey", param.getWechatApiKey());
}
if (StrUtil.isNotBlank(param.getMerchantSerialNumber())) {
contentJson.put("merchantSerialNumber", param.getMerchantSerialNumber());
}
if (StrUtil.isNotBlank(param.getApiclientCert())) {
contentJson.put("apiclientCert", param.getApiclientCert());
}
if (StrUtil.isNotBlank(param.getApiclientKey())) {
contentJson.put("apiclientKey", param.getApiclientKey());
}
break;
case "sms":
// 短信配置
if (StrUtil.isNotBlank(param.getAccessKeyId())) {
contentJson.put("accessKeyId", param.getAccessKeyId());
}
if (StrUtil.isNotBlank(param.getAccessKeySecret())) {
contentJson.put("accessKeySecret", param.getAccessKeySecret());
}
if (StrUtil.isNotBlank(param.getSignName())) {
contentJson.put("signName", param.getSignName());
}
if (StrUtil.isNotBlank(param.getTemplateCode())) {
contentJson.put("templateCode", param.getTemplateCode());
}
break;
case "wx-work":
// 企业微信配置
if (StrUtil.isNotBlank(param.getCorpId())) {
contentJson.put("corpId", param.getCorpId());
}
if (StrUtil.isNotBlank(param.getSecret())) {
contentJson.put("secret", param.getSecret());
}
break;
case "wx-official":
// 微信公众号配置
if (StrUtil.isNotBlank(param.getAppId())) {
contentJson.put("appId", param.getAppId());
}
if (StrUtil.isNotBlank(param.getAppSecret())) {
contentJson.put("appSecret", param.getAppSecret());
}
if (StrUtil.isNotBlank(param.getToken())) {
contentJson.put("token", param.getToken());
}
if (StrUtil.isNotBlank(param.getEncodingAESKey())) {
contentJson.put("encodingAESKey", param.getEncodingAESKey());
}
break;
case "setting":
// 基本设置
if (StrUtil.isNotBlank(param.getSiteName())) {
contentJson.put("siteName", param.getSiteName());
}
if (StrUtil.isNotBlank(param.getLogo())) {
contentJson.put("logo", param.getLogo());
}
if (StrUtil.isNotBlank(param.getDescription())) {
contentJson.put("description", param.getDescription());
}
if (StrUtil.isNotBlank(param.getKeywords())) {
contentJson.put("keywords", param.getKeywords());
}
break;
case "upload":
// 上传配置
if (StrUtil.isNotBlank(param.getBucketName())) {
contentJson.put("bucketName", param.getBucketName());
}
if (StrUtil.isNotBlank(param.getBucketDomain())) {
contentJson.put("bucketDomain", param.getBucketDomain());
}
if (StrUtil.isNotBlank(param.getBucketEndpoint())) {
contentJson.put("bucketEndpoint", param.getBucketEndpoint());
}
if (StrUtil.isNotBlank(param.getAccessKeyId())) {
contentJson.put("accessKeyId", param.getAccessKeyId());
}
if (StrUtil.isNotBlank(param.getAccessKeySecret())) {
contentJson.put("accessKeySecret", param.getAccessKeySecret());
}
break;
case "printer":
// 打印机配置
if (StrUtil.isNotBlank(param.getDeviceNo())) {
contentJson.put("deviceNo", param.getDeviceNo());
}
if (StrUtil.isNotBlank(param.getPrinterKey())) {
contentJson.put("printerKey", param.getPrinterKey());
}
break;
default:
// 处理其他配置或动态字段
if (param.getAdditionalProperties() != null) {
contentJson.putAll(param.getAdditionalProperties());
}
break;
}
setting.setContent(contentJson.toJSONString());
return setting;
}
} }

View File

@@ -481,6 +481,7 @@ public class UserController extends BaseController {
u.setPoints(one.getPoints()); u.setPoints(one.getPoints());
u.setEmail(one.getEmail()); u.setEmail(one.getEmail());
u.setAddress(one.getAddress()); u.setAddress(one.getAddress());
u.setStatus(one.getStatus());
u.setComments(one.getComments()); u.setComments(one.getComments());
u.setCompanyId(one.getCompanyId()); u.setCompanyId(one.getCompanyId());
@@ -600,6 +601,7 @@ public class UserController extends BaseController {
example1.setOrganizationName("总公司"); example1.setOrganizationName("总公司");
example1.setSexName(""); example1.setSexName("");
example1.setRoleName("管理员"); example1.setRoleName("管理员");
example1.setStatus(0);
example1.setAddress("地址"); example1.setAddress("地址");
example1.setComments(""); example1.setComments("");
templateData.add(example1); templateData.add(example1);
@@ -615,6 +617,7 @@ public class UserController extends BaseController {
example2.setOrganizationName("分公司"); example2.setOrganizationName("分公司");
example2.setSexName(""); example2.setSexName("");
example2.setRoleName("注册用户"); example2.setRoleName("注册用户");
example1.setStatus(0);
example2.setAddress("地址"); example2.setAddress("地址");
example2.setComments(""); example2.setComments("");
templateData.add(example2); templateData.add(example2);

View File

@@ -17,6 +17,7 @@ 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.CommonUtil; import com.gxwebsoft.common.core.utils.CommonUtil;
import com.gxwebsoft.common.core.utils.RedisUtil; import com.gxwebsoft.common.core.utils.RedisUtil;
import com.gxwebsoft.common.core.utils.WxMiniProgramDecryptUtil;
import com.gxwebsoft.common.core.web.ApiResult; import com.gxwebsoft.common.core.web.ApiResult;
import com.gxwebsoft.common.core.web.BaseController; import com.gxwebsoft.common.core.web.BaseController;
import com.gxwebsoft.common.system.entity.*; import com.gxwebsoft.common.system.entity.*;
@@ -112,7 +113,8 @@ public class WxLoginController extends BaseController {
throw new BusinessException("授权失败,请重试"); throw new BusinessException("授权失败,请重试");
} }
// 查询是否存在 // 查询是否存在
User user = userService.getByPhone(phone);
User user = userService.getAdminByPhone(userParam);
// 超级管理员验证 // 超级管理员验证
if (userParam.getIsSuperAdmin() != null) { if (userParam.getIsSuperAdmin() != null) {
final LoginParam loginParam = new LoginParam(); final LoginParam loginParam = new LoginParam();
@@ -225,45 +227,100 @@ public class WxLoginController extends BaseController {
// 获取openid // 获取openid
private JSONObject getOpenIdByCode(UserParam userParam) { private JSONObject getOpenIdByCode(UserParam userParam) {
// 获取微信小程序配置信息 try {
JSONObject setting = settingService.getBySettingKey("mp-weixin"); // 获取微信小程序配置信息
// 获取openId JSONObject setting = settingService.getBySettingKey("mp-weixin");
String apiUrl = "https://api.weixin.qq.com/sns/jscode2session?appid=" + setting.getString("appId") + "&secret=" + setting.getString("appSecret") + "&js_code=" + userParam.getCode() + "&grant_type=authorization_code"; // 获取openId
// 执行get请求 String apiUrl = "https://api.weixin.qq.com/sns/jscode2session?appid=" + setting.getString("appId") + "&secret=" + setting.getString("appSecret") + "&js_code=" + userParam.getCode() + "&grant_type=authorization_code";
String result = HttpUtil.get(apiUrl); // 执行get请求
// 解析access_token String result = HttpUtil.get(apiUrl);
return JSON.parseObject(result);
// 验证响应内容
if (StrUtil.isBlank(result)) {
throw new BusinessException("微信接口响应为空");
}
// 清理响应中的控制字符
String cleanResult = result.trim().replaceAll("[\u0000-\u001f]", "");
System.out.println("获取openId响应: " + cleanResult);
// 解析响应
return JSON.parseObject(cleanResult);
} catch (Exception e) {
System.err.println("获取openId异常: " + e.getMessage());
e.printStackTrace();
throw new BusinessException("获取openId失败请重试");
}
} }
/** /**
* 获取微信手机号码 * 获取微信手机号码
* 支持两种方式:
* 1. 新版API方式使用code直接获取手机号
* 2. 旧版解密方式使用encryptedData、iv、sessionKey解密获取手机号
* *
* @param userParam 需要传微信凭证code * @param userParam 需要传微信凭证code或encryptedData等参数
*/ */
private String getPhoneByCode(UserParam userParam) { private String getPhoneByCode(UserParam userParam) {
// 获取手机号 // 方式1如果有encryptedData使用解密方式获取手机号
String apiUrl = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=" + getAccessToken(); if (StrUtil.isNotBlank(userParam.getEncryptedData()) &&
HashMap<String, Object> paramMap = new HashMap<>(); StrUtil.isNotBlank(userParam.getIv()) &&
if (StrUtil.isBlank(userParam.getCode())) { StrUtil.isNotBlank(userParam.getSessionKey())) {
throw new BusinessException("code不能为空"); try {
return WxMiniProgramDecryptUtil.decryptPhoneNumber(
userParam.getEncryptedData(),
userParam.getSessionKey(),
userParam.getIv()
);
} catch (Exception e) {
System.err.println("解密手机号失败: " + e.getMessage());
// 解密失败继续尝试新版API方式
}
} }
paramMap.put("code", userParam.getCode());
// 执行post请求 // 方式2使用新版API方式获取手机号
String post = HttpUtil.post(apiUrl, JSON.toJSONString(paramMap)); if (StrUtil.isNotBlank(userParam.getCode())) {
JSONObject json = JSON.parseObject(post); try {
if (json.get("errcode").equals(0)) { String apiUrl = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=" + getAccessToken();
JSONObject phoneInfo = JSON.parseObject(json.getString("phone_info")); HashMap<String, Object> paramMap = new HashMap<>();
// 微信用户的手机号码 paramMap.put("code", userParam.getCode());
final String phoneNumber = phoneInfo.getString("phoneNumber"); // 执行post请求
// 验证手机号码 String post = HttpUtil.post(apiUrl, JSON.toJSONString(paramMap));
// if (userParam.getNotVerifyPhone() == null && !Validator.isMobile(phoneNumber)) {
// String key = ACCESS_TOKEN_KEY.concat(":").concat(getTenantId().toString()); // 增加响应内容验证和清理
// redisTemplate.delete(key); if (StrUtil.isBlank(post)) {
// throw new BusinessException("手机号码格式不正确"); throw new BusinessException("微信接口响应为空");
// } }
return phoneNumber;
// 清理响应中的控制字符
String cleanResponse = post.trim().replaceAll("[\u0000-\u001f]", "");
System.out.println("微信获取手机号响应: " + cleanResponse);
JSONObject json = JSON.parseObject(cleanResponse);
if (json.get("errcode").equals(0)) {
JSONObject phoneInfo = JSON.parseObject(json.getString("phone_info"));
// 微信用户的手机号码
final String phoneNumber = phoneInfo.getString("phoneNumber");
// 验证手机号码
// if (userParam.getNotVerifyPhone() == null && !Validator.isMobile(phoneNumber)) {
// String key = ACCESS_TOKEN_KEY.concat(":").concat(getTenantId().toString());
// redisTemplate.delete(key);
// throw new BusinessException("手机号码格式不正确");
// }
return phoneNumber;
} else {
String errorMsg = json.getString("errmsg");
System.err.println("微信获取手机号失败: errcode=" + json.get("errcode") + ", errmsg=" + errorMsg);
throw new BusinessException("获取手机号失败:" + errorMsg);
}
} catch (Exception e) {
System.err.println("获取微信手机号异常: " + e.getMessage());
e.printStackTrace();
throw new BusinessException("获取手机号失败,请重试");
}
} }
return null;
throw new BusinessException("获取手机号失败,请检查参数");
} }
/** /**
@@ -300,14 +357,32 @@ public class WxLoginController extends BaseController {
// 执行get请求 // 执行get请求
String result = HttpUtil.get(url); String result = HttpUtil.get(url);
System.out.println("result = " + result); System.out.println("result = " + result);
// 解析access_token
JSONObject response = JSON.parseObject(result); try {
if (response.getString("access_token") != null) { // 验证响应内容
// 存入缓存 if (StrUtil.isBlank(result)) {
redisTemplate.opsForValue().set(key, result, 7000L, TimeUnit.SECONDS); throw new BusinessException("微信接口响应为空");
return response.getString("access_token"); }
// 清理响应中的控制字符
String cleanResult = result.trim().replaceAll("[\u0000-\u001f]", "");
// 解析access_token
JSONObject response = JSON.parseObject(cleanResult);
if (response.getString("access_token") != null) {
// 存入缓存
redisTemplate.opsForValue().set(key, cleanResult, 7000L, TimeUnit.SECONDS);
return response.getString("access_token");
} else {
String errorMsg = response.getString("errmsg");
System.err.println("获取access_token失败: " + errorMsg);
throw new BusinessException("获取access_token失败" + errorMsg);
}
} catch (Exception e) {
System.err.println("解析access_token异常: " + e.getMessage());
e.printStackTrace();
throw new BusinessException("小程序配置不正确或网络异常");
} }
throw new BusinessException("小程序配置不正确");
} }
@ApiOperation("获取微信openId并更新") @ApiOperation("获取微信openId并更新")
@@ -450,31 +525,63 @@ public class WxLoginController extends BaseController {
} }
// 请求微信接口获取openid // 请求微信接口获取openid
String apiUrl = "https://api.weixin.qq.com/sns/jscode2session"; try {
final HashMap<String, Object> map = new HashMap<>(); String apiUrl = "https://api.weixin.qq.com/sns/jscode2session";
map.put("appid", AppId); final HashMap<String, Object> map = new HashMap<>();
map.put("secret", AppSecret); map.put("appid", AppId);
map.put("js_code", mp.getCode()); map.put("secret", AppSecret);
map.put("grant_type", "authorization_code"); map.put("js_code", mp.getCode());
final String response = HttpUtil.get(apiUrl, map); map.put("grant_type", "authorization_code");
final JSONObject jsonObject = JSONObject.parseObject(response); final String response = HttpUtil.get(apiUrl, map);
String openid = jsonObject.getString("openid");
String sessionKey = jsonObject.getString("session_key");
String unionid = jsonObject.getString("unionid");
if (StrUtil.isNotBlank(openid)) { // 验证响应内容
User user = userService.getOne(new LambdaQueryWrapper<User>().eq(User::getOpenid, openid).last("limit 1")); if (StrUtil.isBlank(response)) {
final User userInfo = userService.getByIdRel(user.getUserId()); return fail("微信接口响应为空");
if (ObjectUtil.isNotEmpty(userInfo)) { }
// 签发token
String access_token = JwtUtil.buildToken(new JwtSubject(user.getUsername(), user.getTenantId()), // 清理响应中的控制字符
configProperties.getTokenExpireTime(), configProperties.getTokenKey()); String cleanResponse = response.trim().replaceAll("[\u0000-\u001f]", "");
loginRecordService.saveAsync(user.getUsername(), LoginRecord.TYPE_REGISTER, null, user.getTenantId(), request); System.out.println("获取openId响应: " + cleanResponse);
return success("登录成功", new LoginResult(access_token, userInfo));
} final JSONObject jsonObject = JSONObject.parseObject(cleanResponse);
return fail("用户未注册", openid);
// 检查微信接口是否返回错误
if (jsonObject.containsKey("errcode") && !jsonObject.getInteger("errcode").equals(0)) {
String errmsg = jsonObject.getString("errmsg");
System.err.println("微信接口返回错误: errcode=" + jsonObject.get("errcode") + ", errmsg=" + errmsg);
return fail("微信授权失败:" + errmsg);
}
String openid = jsonObject.getString("openid");
String sessionKey = jsonObject.getString("session_key");
String unionid = jsonObject.getString("unionid");
if (StrUtil.isNotBlank(openid)) {
User user = userService.getOne(new LambdaQueryWrapper<User>().eq(User::getOpenid, openid).eq(User::getDeleted,0).last("limit 1"));
// 检查用户是否存在
if (user == null) {
return fail("用户未注册", openid);
}
final User userInfo = userService.getByIdRel(user.getUserId());
if (ObjectUtil.isNotEmpty(userInfo)) {
// 签发token
String access_token = JwtUtil.buildToken(new JwtSubject(user.getUsername(), user.getTenantId()),
configProperties.getTokenExpireTime(), configProperties.getTokenKey());
loginRecordService.saveAsync(user.getUsername(), LoginRecord.TYPE_REGISTER, null, user.getTenantId(), request);
return success("登录成功", new LoginResult(access_token, userInfo));
} else {
return fail("用户信息获取失败", openid);
}
} else {
return fail("openId获取失败");
}
} catch (Exception e) {
System.err.println("openId登录异常: " + e.getMessage());
e.printStackTrace();
return fail("登录失败,请重试");
} }
return fail("openId获取失败");
} }
/** /**

View File

@@ -48,6 +48,10 @@ public class Menu implements GrantedAuthority {
@ApiModelProperty("菜单类型, 0菜单, 1按钮") @ApiModelProperty("菜单类型, 0菜单, 1按钮")
private Integer menuType; private Integer menuType;
@ApiModelProperty("打开方式, 0当前页, 1新窗口")
@TableField(exist = false)
private Integer openType;
@ApiModelProperty("排序号") @ApiModelProperty("排序号")
private Integer sortNumber; private Integer sortNumber;

View File

@@ -9,7 +9,7 @@ import lombok.Data;
import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetails;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Date; import java.time.LocalDateTime;
import java.util.List; import java.util.List;
/** /**
@@ -94,7 +94,7 @@ public class User implements UserDetails {
@ApiModelProperty("出生日期") @ApiModelProperty("出生日期")
@JsonFormat(pattern = "yyyy-MM-dd") @JsonFormat(pattern = "yyyy-MM-dd")
private Date birthday; private LocalDateTime birthday;
@ApiModelProperty(value = "年龄") @ApiModelProperty(value = "年龄")
private Integer age; private Integer age;
@@ -235,13 +235,16 @@ public class User implements UserDetails {
private String tenantName; private String tenantName;
@ApiModelProperty(value = "最后结算时间") @ApiModelProperty(value = "最后结算时间")
private Date settlementTime; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime settlementTime;
@ApiModelProperty("注册时间") @ApiModelProperty("注册时间")
private Date createTime; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
@ApiModelProperty("修改时间") @ApiModelProperty("修改时间")
private Date updateTime; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime updateTime;
@ApiModelProperty("公司名称") @ApiModelProperty("公司名称")
@TableField(exist = false) @TableField(exist = false)

View File

@@ -57,6 +57,9 @@ public interface UserMapper extends BaseMapper<User> {
@InterceptorIgnore(tenantLine = "true") @InterceptorIgnore(tenantLine = "true")
User selectAdminByPhone(@Param("param") UserParam param); User selectAdminByPhone(@Param("param") UserParam param);
@InterceptorIgnore(tenantLine = "true")
User selectAdminByPhone(@Param("param") UserParam param, @Param("tenantId") Integer tenantId);
@InterceptorIgnore(tenantLine = "true") @InterceptorIgnore(tenantLine = "true")
User selectByUserId(@Param("userId") Integer userId); User selectByUserId(@Param("userId") Integer userId);

View File

@@ -303,6 +303,9 @@
AND a.phone = #{param.phone} AND a.phone = #{param.phone}
AND a.template_id = #{param.templateId} AND a.template_id = #{param.templateId}
AND (a.username = 'superAdmin' OR a.username = 'admin' OR a.is_admin = 1) AND (a.username = 'superAdmin' OR a.username = 'admin' OR a.is_admin = 1)
<if test="param.tenantId">
AND a.tenant_id = #{param.tenantId}
</if>
LIMIT 1 LIMIT 1
</where> </where>
</select> </select>

View File

@@ -1,5 +1,6 @@
package com.gxwebsoft.common.system.param; package com.gxwebsoft.common.system.param;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableLogic; import com.baomidou.mybatisplus.annotation.TableLogic;
import com.gxwebsoft.common.core.annotation.QueryField; import com.gxwebsoft.common.core.annotation.QueryField;
import com.gxwebsoft.common.core.annotation.QueryType; import com.gxwebsoft.common.core.annotation.QueryType;
@@ -47,6 +48,10 @@ public class MenuParam extends BaseParam {
@QueryField(type = QueryType.EQ) @QueryField(type = QueryType.EQ)
private Integer menuType; private Integer menuType;
@ApiModelProperty("打开方式, 0当前页, 1新窗口")
@TableField(exist = false)
private Integer openType;
@ApiModelProperty("权限标识") @ApiModelProperty("权限标识")
private String authority; private String authority;
@@ -72,4 +77,8 @@ public class MenuParam extends BaseParam {
@QueryField(type = QueryType.EQ) @QueryField(type = QueryType.EQ)
private Integer companyId; private Integer companyId;
@ApiModelProperty("租户名称")
@TableField(exist = false)
private String tenantName;
} }

View File

@@ -0,0 +1,124 @@
package com.gxwebsoft.common.system.param;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Map;
/**
* 系统设置更新参数
* 用于处理包含配置字段的更新请求
*
* @author WebSoft
*/
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
@ApiModel(description = "系统设置更新参数")
public class SettingUpdateParam {
@ApiModelProperty(value = "设置项标示")
private String settingKey;
@ApiModelProperty(value = "设置内容json格式")
private String content;
@ApiModelProperty(value = "排序号")
private Integer sortNumber;
@ApiModelProperty(value = "备注")
private String comments;
@ApiModelProperty(value = "租户id")
private Integer tenantId;
// 微信小程序配置字段
@ApiModelProperty(value = "微信小程序AppId")
private String appId;
@ApiModelProperty(value = "微信小程序AppSecret")
private String appSecret;
// 支付配置字段
@ApiModelProperty(value = "商户号")
private String mchId;
@ApiModelProperty(value = "API密钥")
private String apiKey;
@ApiModelProperty(value = "微信支付API密钥")
private String wechatApiKey;
@ApiModelProperty(value = "商户序列号")
private String merchantSerialNumber;
@ApiModelProperty(value = "API客户端证书")
private String apiclientCert;
@ApiModelProperty(value = "API客户端密钥")
private String apiclientKey;
// 短信配置字段
@ApiModelProperty(value = "短信AccessKey")
private String accessKeyId;
@ApiModelProperty(value = "短信AccessSecret")
private String accessKeySecret;
@ApiModelProperty(value = "短信签名")
private String signName;
@ApiModelProperty(value = "短信模板")
private String templateCode;
// 企业微信配置字段
@ApiModelProperty(value = "企业ID")
private String corpId;
@ApiModelProperty(value = "应用Secret")
private String secret;
// 微信公众号配置字段
@ApiModelProperty(value = "公众号Token")
private String token;
@ApiModelProperty(value = "公众号EncodingAESKey")
private String encodingAESKey;
// 基本设置字段
@ApiModelProperty(value = "网站名称")
private String siteName;
@ApiModelProperty(value = "网站Logo")
private String logo;
@ApiModelProperty(value = "网站描述")
private String description;
@ApiModelProperty(value = "网站关键词")
private String keywords;
// 上传配置字段
@ApiModelProperty(value = "存储桶名称")
private String bucketName;
@ApiModelProperty(value = "存储桶域名")
private String bucketDomain;
@ApiModelProperty(value = "存储桶端点")
private String bucketEndpoint;
// 打印机配置字段
@ApiModelProperty(value = "打印机设备号")
private String deviceNo;
@ApiModelProperty(value = "打印机密钥")
private String printerKey;
/**
* 获取其他未定义的字段
* 用于处理动态配置字段
*/
private Map<String, Object> additionalProperties;
}

View File

@@ -42,6 +42,9 @@ public class UserImportParam implements Serializable {
@Excel(name = "角色") @Excel(name = "角色")
private String roleName; private String roleName;
@Excel(name = "状态")
private Integer status;
@Excel(name = "备注") @Excel(name = "备注")
private String comments; private String comments;

View File

@@ -296,4 +296,16 @@ public class UserParam extends BaseParam {
@ApiModelProperty(value = "是否已安装") @ApiModelProperty(value = "是否已安装")
@TableField(exist = false) @TableField(exist = false)
private Boolean installed; private Boolean installed;
@ApiModelProperty(value = "微信小程序解密数据")
@TableField(exist = false)
private String encryptedData;
@ApiModelProperty(value = "微信小程序解密向量")
@TableField(exist = false)
private String iv;
@ApiModelProperty(value = "微信小程序会话密钥")
@TableField(exist = false)
private String sessionKey;
} }

View File

@@ -98,6 +98,8 @@ public interface UserService extends IService<User>, UserDetailsService {
*/ */
User getByPhone(String phone); User getByPhone(String phone);
User getByPhone(String phone,Integer tenantId);
User getByUnionId(UserParam userParam); User getByUnionId(UserParam userParam);
User getByOauthId(UserParam userParam); User getByOauthId(UserParam userParam);
@@ -114,6 +116,8 @@ public interface UserService extends IService<User>, UserDetailsService {
User getAdminByPhone(UserParam param); User getAdminByPhone(UserParam param);
User getAdminByPhone(UserParam param,Integer tenantId);
User getAllByUserId(String userId); User getAllByUserId(String userId);
Integer userNumInPark(UserParam param); Integer userNumInPark(UserParam param);

View File

@@ -181,10 +181,22 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
return getOne( return getOne(
new LambdaQueryWrapper<User>() new LambdaQueryWrapper<User>()
.eq(User::getPhone, phone) .eq(User::getPhone, phone)
.eq(User::getDeleted,0)
.orderByDesc(User::getUserId).last("limit 1") .orderByDesc(User::getUserId).last("limit 1")
); );
} }
@Override
public User getByPhone(String phone,Integer tenantId) {
return getOne(
new LambdaQueryWrapper<User>()
.eq(User::getPhone, phone)
.eq(User::getTenantId, tenantId)
.eq(User::getDeleted,0)
.orderByDesc(User::getUserId).last("limit 1")
);
}
@Override @Override
public User getByUnionId(UserParam param) { public User getByUnionId(UserParam param) {
return getOne( return getOne(
@@ -276,10 +288,13 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
@Override @Override
public User getAdminByPhone(UserParam param) { public User getAdminByPhone(UserParam param) {
System.out.println("param222 = " + param);
return baseMapper.selectAdminByPhone(param); return baseMapper.selectAdminByPhone(param);
} }
public User getAdminByPhone(UserParam param,Integer tenantId){
return baseMapper.selectAdminByPhone(param,tenantId);
}
@Override @Override
public List<User> getAdminsByPhone(LoginParam param){ public List<User> getAdminsByPhone(LoginParam param){
final UserParam userParam = new UserParam(); final UserParam userParam = new UserParam();