|
|
|
@ -1,26 +1,10 @@ |
|
|
|
package com.chenhai.web.controller.auth; |
|
|
|
|
|
|
|
import com.alibaba.fastjson2.JSONObject; |
|
|
|
import com.chenhai.common.constant.Constants; |
|
|
|
import com.chenhai.common.core.domain.AjaxResult; |
|
|
|
import com.chenhai.common.core.domain.entity.SysUser; |
|
|
|
import com.chenhai.common.core.domain.model.*; |
|
|
|
import com.chenhai.common.core.redis.RedisCache; |
|
|
|
import com.chenhai.common.utils.WechatDecryptUtil; |
|
|
|
import com.chenhai.framework.security.exception.WechatNeedBindException; |
|
|
|
import com.chenhai.framework.security.token.PhoneAuthenticationToken; |
|
|
|
import com.chenhai.framework.security.token.WechatAuthenticationToken; |
|
|
|
import com.chenhai.framework.web.service.SysLoginService; |
|
|
|
import com.chenhai.framework.web.service.SysPermissionService; |
|
|
|
import com.chenhai.framework.web.service.SysSmsService; |
|
|
|
import com.chenhai.framework.web.service.TokenService; |
|
|
|
import com.chenhai.muhu.service.IUserAuthService; |
|
|
|
import com.chenhai.muhu.service.WechatService; |
|
|
|
import com.chenhai.system.service.ISysConfigService; |
|
|
|
import com.chenhai.system.service.ISysUserService; |
|
|
|
import org.springframework.beans.factory.annotation.Autowired; |
|
|
|
import org.springframework.security.authentication.AuthenticationManager; |
|
|
|
import org.springframework.security.core.Authentication; |
|
|
|
import org.springframework.transaction.annotation.Transactional; |
|
|
|
import org.springframework.web.bind.annotation.PostMapping; |
|
|
|
import org.springframework.web.bind.annotation.RequestBody; |
|
|
|
@ -28,275 +12,75 @@ import org.springframework.web.bind.annotation.RequestMapping; |
|
|
|
import org.springframework.web.bind.annotation.RestController; |
|
|
|
|
|
|
|
import jakarta.validation.Valid; |
|
|
|
import java.util.Date; |
|
|
|
import java.util.HashMap; |
|
|
|
import java.util.Map; |
|
|
|
|
|
|
|
/** |
|
|
|
* 多端认证控制器 - 统一处理牧户和兽医 |
|
|
|
* 多端认证控制器 - 使用合并后的 SysLoginService |
|
|
|
*/ |
|
|
|
@RestController |
|
|
|
@RequestMapping("/auth") |
|
|
|
public class MultiAuthController { |
|
|
|
|
|
|
|
@Autowired |
|
|
|
private AuthenticationManager authenticationManager; |
|
|
|
|
|
|
|
@Autowired |
|
|
|
private TokenService tokenService; |
|
|
|
|
|
|
|
@Autowired |
|
|
|
private SysSmsService smsService; |
|
|
|
|
|
|
|
@Autowired |
|
|
|
private IUserAuthService userAuthService; |
|
|
|
|
|
|
|
@Autowired |
|
|
|
private WechatService wechatService; |
|
|
|
|
|
|
|
@Autowired |
|
|
|
private SysPermissionService permissionService; |
|
|
|
|
|
|
|
@Autowired |
|
|
|
private RedisCache redisCache; |
|
|
|
|
|
|
|
@Autowired |
|
|
|
private ISysUserService userService; |
|
|
|
|
|
|
|
@Autowired |
|
|
|
private ISysConfigService sysConfigService; |
|
|
|
|
|
|
|
@Autowired |
|
|
|
private SysLoginService loginService; |
|
|
|
|
|
|
|
/** |
|
|
|
* 微信小程序登录(统一入口) |
|
|
|
* 微信小程序登录 - 保持原逻辑 |
|
|
|
*/ |
|
|
|
@PostMapping("/wechat/login") |
|
|
|
public AjaxResult wechatLogin(@Valid @RequestBody WechatLoginBody loginBody) { |
|
|
|
// 验证参数 |
|
|
|
if (loginBody.getCode() == null || loginBody.getClientType() == null) { |
|
|
|
return AjaxResult.error("参数错误"); |
|
|
|
} |
|
|
|
|
|
|
|
// 验证clientType |
|
|
|
if (!"herdsman-app".equals(loginBody.getClientType()) && !"vet-app".equals(loginBody.getClientType())) { |
|
|
|
return AjaxResult.error("不支持的客户端类型"); |
|
|
|
} |
|
|
|
|
|
|
|
// 创建认证token |
|
|
|
WechatAuthenticationToken authToken = new WechatAuthenticationToken( |
|
|
|
Map<String, Object> result = loginService.wechatLogin( |
|
|
|
loginBody.getCode(), |
|
|
|
loginBody.getClientType() |
|
|
|
); |
|
|
|
|
|
|
|
try { |
|
|
|
// 认证 |
|
|
|
Authentication authentication = authenticationManager.authenticate(authToken); |
|
|
|
LoginUser loginUser = (LoginUser) authentication.getPrincipal(); |
|
|
|
|
|
|
|
loginService.recordLoginInfo(loginUser.getUserId()); |
|
|
|
|
|
|
|
// 生成token |
|
|
|
String token = tokenService.createToken(loginUser); |
|
|
|
|
|
|
|
// 返回结果 |
|
|
|
AjaxResult ajax = AjaxResult.success(); |
|
|
|
ajax.put(Constants.TOKEN, token); |
|
|
|
AjaxResult ajax = AjaxResult.success(result); |
|
|
|
return ajax; |
|
|
|
|
|
|
|
} catch (WechatNeedBindException e) { |
|
|
|
// 需要绑定手机号 |
|
|
|
Map<String, Object> data = new HashMap<>(); |
|
|
|
data.put("needBind", true); |
|
|
|
data.put("userType", e.getUserType()); // "herdsman" 或 "vet" |
|
|
|
data.put("clientType", e.getClientType()); // "herdsman-app" 或 "vet-app" |
|
|
|
data.put("openid", e.getOpenid()); |
|
|
|
data.put("tempCode", e.getTempCode()); |
|
|
|
data.put("authType", e.getAuthType()); |
|
|
|
data.put("message", "请绑定手机号"); |
|
|
|
return AjaxResult.success(data); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 微信绑定手机号(统一绑定接口) |
|
|
|
* 牧户和兽医使用同一套逻辑 |
|
|
|
* 微信绑定手机号 - 保持原逻辑(@Transactional 已移到Service) |
|
|
|
*/ |
|
|
|
@PostMapping("/wechat/bind") |
|
|
|
@Transactional |
|
|
|
// @Transactional // 为了兼容,保留Controller层的事务注解 |
|
|
|
public AjaxResult wechatBind(@Valid @RequestBody WechatBindRequest request) { |
|
|
|
try { |
|
|
|
// 1. 基本参数验证 |
|
|
|
if (request.getEncryptedData() == null || request.getIv() == null) { |
|
|
|
return AjaxResult.error("请通过微信授权获取手机号"); |
|
|
|
} |
|
|
|
|
|
|
|
// 2. 从Redis获取sessionKey |
|
|
|
String sessionKey = redisCache.getCacheObject("wechat:session:" + request.getTempCode()); |
|
|
|
if (sessionKey == null) { |
|
|
|
return AjaxResult.error("绑定会话已过期,请重新登录"); |
|
|
|
} |
|
|
|
|
|
|
|
// 3. 解密手机号 |
|
|
|
String phone = WechatDecryptUtil.decryptPhone( |
|
|
|
Map<String, Object> result = loginService.wechatBind( |
|
|
|
request.getEncryptedData(), |
|
|
|
request.getIv(), |
|
|
|
sessionKey |
|
|
|
); |
|
|
|
if (phone == null || phone.trim().isEmpty()) { |
|
|
|
return AjaxResult.error("获取手机号失败"); |
|
|
|
} |
|
|
|
|
|
|
|
// 4. 查询手机号是否已注册 |
|
|
|
SysUser existingUser = userService.selectUserByPhone(phone); |
|
|
|
|
|
|
|
// 5. 用户不存在,创建新用户 |
|
|
|
if (existingUser == null) { |
|
|
|
// 根据userType创建对应类型的用户 |
|
|
|
String userTypeCode = "herdsman".equals(request.getUserType()) ? "02" : "01"; |
|
|
|
|
|
|
|
existingUser = createUser( |
|
|
|
phone, |
|
|
|
userTypeCode, |
|
|
|
request.getNickName(), |
|
|
|
request.getAvatarUrl(), |
|
|
|
request.getClientType() |
|
|
|
); |
|
|
|
|
|
|
|
} else { |
|
|
|
// 6. 用户已存在,检查用户类型是否匹配 |
|
|
|
String existingUserType = existingUser.getUserType(); |
|
|
|
String expectedUserType = "herdsman".equals(request.getUserType()) ? "02" : "01"; |
|
|
|
|
|
|
|
if (!existingUserType.equals(expectedUserType)) { |
|
|
|
String existingTypeName = "01".equals(existingUserType) ? "兽医" : "牧户"; |
|
|
|
String expectedTypeName = "01".equals(expectedUserType) ? "兽医" : "牧户"; |
|
|
|
return AjaxResult.error("该手机号已注册为" + existingTypeName + ",请使用" + expectedTypeName + "小程序"); |
|
|
|
} |
|
|
|
|
|
|
|
// 7. 用户类型匹配,更新用户信息(昵称、头像等) |
|
|
|
if (request.getNickName() != null) { |
|
|
|
existingUser.setNickName(request.getNickName()); |
|
|
|
} |
|
|
|
if (request.getAvatarUrl() != null) { |
|
|
|
existingUser.setAvatar(request.getAvatarUrl()); |
|
|
|
} |
|
|
|
userService.updateUser(existingUser); |
|
|
|
} |
|
|
|
|
|
|
|
// 8. 绑定微信openid |
|
|
|
userAuthService.bindAuth( |
|
|
|
existingUser.getUserId(), |
|
|
|
request.getAuthType(), |
|
|
|
request.getTempCode(), |
|
|
|
request.getUserType(), |
|
|
|
request.getClientType(), |
|
|
|
request.getOpenid(), |
|
|
|
sessionKey |
|
|
|
); |
|
|
|
|
|
|
|
// 9. 创建登录用户 |
|
|
|
LoginUser loginUser = new LoginUser( |
|
|
|
existingUser.getUserId(), |
|
|
|
existingUser.getDeptId(), |
|
|
|
existingUser, |
|
|
|
permissionService.getMenuPermission(existingUser), |
|
|
|
request.getClientType() |
|
|
|
request.getAuthType(), |
|
|
|
request.getNickName(), |
|
|
|
request.getAvatarUrl() |
|
|
|
); |
|
|
|
|
|
|
|
loginService.recordLoginInfo(loginUser.getUserId()); |
|
|
|
|
|
|
|
// 10. 生成token |
|
|
|
String token = tokenService.createToken(loginUser); |
|
|
|
|
|
|
|
// 11. 清理Redis临时数据 |
|
|
|
redisCache.deleteObject("wechat:session:" + request.getTempCode()); |
|
|
|
|
|
|
|
AjaxResult ajax = AjaxResult.success("绑定成功"); |
|
|
|
ajax.put(Constants.TOKEN, token); |
|
|
|
return ajax; |
|
|
|
|
|
|
|
return AjaxResult.success((String) result.get("message")) |
|
|
|
.put(Constants.TOKEN, result.get("token")); |
|
|
|
} catch (Exception e) { |
|
|
|
// 保持原异常处理方式 |
|
|
|
return AjaxResult.error("绑定失败: " + e.getMessage()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 手机号+密码登录(PC端使用) |
|
|
|
* 牧户和兽医都可用 |
|
|
|
* 手机号+密码登录 - 保持原逻辑 |
|
|
|
*/ |
|
|
|
@PostMapping("/phone/login") |
|
|
|
public AjaxResult phoneLogin(@Valid @RequestBody PhoneLoginBody loginBody) { |
|
|
|
// 验证参数 |
|
|
|
if (loginBody.getPhone() == null || loginBody.getPassword() == null) { |
|
|
|
return AjaxResult.error("参数错误"); |
|
|
|
} |
|
|
|
|
|
|
|
// 根据请求来源确定clientType |
|
|
|
String clientType = loginBody.getClientType(); // 前端可传 "vet-pc" 或 "herdsman-pc" |
|
|
|
if (clientType == null) { |
|
|
|
clientType = "vet-pc"; // 默认兽医PC端 |
|
|
|
} |
|
|
|
|
|
|
|
// 创建认证token |
|
|
|
PhoneAuthenticationToken authToken = new PhoneAuthenticationToken( |
|
|
|
try { |
|
|
|
String token = loginService.phoneLogin( |
|
|
|
loginBody.getPhone(), |
|
|
|
loginBody.getPassword(), |
|
|
|
clientType |
|
|
|
loginBody.getClientType() |
|
|
|
); |
|
|
|
|
|
|
|
// 认证 |
|
|
|
Authentication authentication = authenticationManager.authenticate(authToken); |
|
|
|
LoginUser loginUser = (LoginUser) authentication.getPrincipal(); |
|
|
|
loginService.recordLoginInfo(loginUser.getUserId()); |
|
|
|
|
|
|
|
// 生成token |
|
|
|
String token = tokenService.createToken(loginUser); |
|
|
|
|
|
|
|
// 返回结果 |
|
|
|
AjaxResult ajax = AjaxResult.success(); |
|
|
|
ajax.put(Constants.TOKEN, token); |
|
|
|
return ajax; |
|
|
|
return AjaxResult.success().put(Constants.TOKEN, token); |
|
|
|
} catch (Exception e) { |
|
|
|
// 保持原异常处理方式 |
|
|
|
return AjaxResult.error(e.getMessage()); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 创建用户(牧户或兽医) |
|
|
|
*/ |
|
|
|
private SysUser createUser(String phone, String userType, |
|
|
|
String nickName, String avatarUrl, String clientType) { |
|
|
|
SysUser user = new SysUser(); |
|
|
|
|
|
|
|
// 生成用户名:前缀 + 手机后4位 + 4位随机数 |
|
|
|
String usernamePrefix = "01".equals(userType) ? "vet_" : "muhu_"; |
|
|
|
String phoneSuffix = phone.length() > 4 ? phone.substring(phone.length() - 4) : phone; |
|
|
|
String randomSuffix = String.format("%04d", (int)(Math.random() * 10000)); // 4位随机数 |
|
|
|
String username = usernamePrefix + phoneSuffix + "_" + randomSuffix; |
|
|
|
user.setUserName(username); |
|
|
|
|
|
|
|
// 设置昵称:统一为"用户" + 去掉前缀的username部分 |
|
|
|
String displayName = phoneSuffix + "_" + randomSuffix; // 去掉前缀的部分 |
|
|
|
user.setNickName(nickName != null ? nickName : ("用户" + displayName)); |
|
|
|
|
|
|
|
user.setUserType(userType); // "01":兽医, "02":牧户 |
|
|
|
user.setEmail(""); |
|
|
|
user.setPhonenumber(phone); |
|
|
|
user.setSex("0"); |
|
|
|
user.setAvatar(avatarUrl != null ? avatarUrl : ""); |
|
|
|
|
|
|
|
// 设置默认密码(微信登录用不到,但PC端登录需要) |
|
|
|
String defaultPassword = sysConfigService.selectConfigByKey("sys.user.initPassword"); |
|
|
|
user.setPassword(com.chenhai.common.utils.SecurityUtils.encryptPassword(defaultPassword)); |
|
|
|
|
|
|
|
user.setStatus("0"); // 正常状态 |
|
|
|
user.setDelFlag("0"); |
|
|
|
user.setCreateTime(new Date()); |
|
|
|
|
|
|
|
// 如果是兽医,设置初始审核状态 |
|
|
|
// if ("01".equals(userType)) { |
|
|
|
// user.setVetStatus("0"); // 0:未提交资质, 1:审核中, 2:已认证, 3:审核不通过 |
|
|
|
// } |
|
|
|
|
|
|
|
userService.insertUser(user); |
|
|
|
|
|
|
|
// 重新查询获取完整用户信息 |
|
|
|
return userService.selectUserByUserName(username); |
|
|
|
} |
|
|
|
} |