diff --git a/chenhai-admin/pom.xml b/chenhai-admin/pom.xml
index dd3420c..c48e98b 100644
--- a/chenhai-admin/pom.xml
+++ b/chenhai-admin/pom.xml
@@ -54,6 +54,12 @@
chenhai-generator
+
+ org.projectlombok
+ lombok
+ true
+
+
diff --git a/chenhai-admin/src/main/java/com/chenhai/web/controller/auth/MultiAuthController.java b/chenhai-admin/src/main/java/com/chenhai/web/controller/auth/MultiAuthController.java
new file mode 100644
index 0000000..9804c20
--- /dev/null
+++ b/chenhai-admin/src/main/java/com/chenhai/web/controller/auth/MultiAuthController.java
@@ -0,0 +1,302 @@
+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;
+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;
+
+/**
+ * 多端认证控制器 - 统一处理牧户和兽医
+ */
+@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(
+ 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);
+ return ajax;
+
+ } catch (WechatNeedBindException e) {
+ // 需要绑定手机号
+ Map 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);
+ }
+ }
+
+ /**
+ * 微信绑定手机号(统一绑定接口)
+ * 牧户和兽医使用同一套逻辑
+ */
+ @PostMapping("/wechat/bind")
+ @Transactional
+ 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(
+ 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.getOpenid(),
+ sessionKey
+ );
+
+ // 9. 创建登录用户
+ LoginUser loginUser = new LoginUser(
+ existingUser.getUserId(),
+ existingUser.getDeptId(),
+ existingUser,
+ permissionService.getMenuPermission(existingUser),
+ request.getClientType()
+ );
+
+ 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;
+
+ } 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(
+ loginBody.getPhone(),
+ loginBody.getPassword(),
+ clientType
+ );
+
+ // 认证
+ 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;
+ }
+
+ /**
+ * 创建用户(牧户或兽医)
+ */
+ 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);
+ }
+}
\ No newline at end of file
diff --git a/chenhai-admin/src/main/resources/application.yml b/chenhai-admin/src/main/resources/application.yml
index 1563c3b..7a17ed1 100644
--- a/chenhai-admin/src/main/resources/application.yml
+++ b/chenhai-admin/src/main/resources/application.yml
@@ -94,9 +94,22 @@ token:
# 令牌自定义标识
header: Authorization
# 令牌密钥
- secret: abcdefghijklmnopqrstuvwxyz
+# secret: abcdefghijklmnopqrstuvwxyz
+ secret: e8f5b8c9d2a1f3e5c7b9d1a3f5c7e9b1d3a5f7c9e1b3d5a7f9c1e3b5d7a9f1c3e5b7d9a1f3c5e7b9d1a3f5c7e9b1d3a5f7c9e1b3d5a7f9c1e3b5d7a9f1c3e5b7d9a1f3c5
# 令牌有效期(默认30分钟)
expireTime: 30
+ # 牧户30天(30*24*3600秒)
+
+
+# application.yml
+# 微信小程序配置
+wx:
+ muhu:
+ app-id: wxb5becc8d6d8123a6
+ app-secret: 74f4211d3985aa782ff9148aa00f824e
+ vet:
+ app-id: ${WX_MINI_APPID:your_app_id}
+ app-secret: ${WX_MINI_SECRET:your_app_secret}
# MyBatis配置
mybatis:
diff --git a/chenhai-common/src/main/java/com/chenhai/common/core/domain/entity/SysUser.java b/chenhai-common/src/main/java/com/chenhai/common/core/domain/entity/SysUser.java
index 72c543a..f5c55e9 100644
--- a/chenhai-common/src/main/java/com/chenhai/common/core/domain/entity/SysUser.java
+++ b/chenhai-common/src/main/java/com/chenhai/common/core/domain/entity/SysUser.java
@@ -38,6 +38,10 @@ public class SysUser extends BaseEntity
@Excel(name = "用户名称")
private String nickName;
+ /** 用户类型 */
+ @Excel(name = "用户类型", readConverterExp = "00=系统用户,01=注册用户")
+ private String userType;
+
/** 用户邮箱 */
@Excel(name = "用户邮箱")
private String email;
@@ -145,6 +149,14 @@ public class SysUser extends BaseEntity
this.nickName = nickName;
}
+ public String getUserType() {
+ return userType;
+ }
+
+ public void setUserType(String userType) {
+ this.userType = userType;
+ }
+
@Xss(message = "用户账号不能包含脚本字符")
@NotBlank(message = "用户账号不能为空")
@Size(min = 0, max = 30, message = "用户账号长度不能超过30个字符")
diff --git a/chenhai-common/src/main/java/com/chenhai/common/core/domain/entity/SysUserAuth.java b/chenhai-common/src/main/java/com/chenhai/common/core/domain/entity/SysUserAuth.java
new file mode 100644
index 0000000..ffde8dd
--- /dev/null
+++ b/chenhai-common/src/main/java/com/chenhai/common/core/domain/entity/SysUserAuth.java
@@ -0,0 +1,99 @@
+package com.chenhai.common.core.domain.entity;
+
+import com.chenhai.common.annotation.Excel;
+import com.chenhai.common.core.domain.BaseEntity;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+/**
+ * 用户认证方式对象 sys_user_auth
+ */
+public class SysUserAuth extends BaseEntity {
+ private static final long serialVersionUID = 1L;
+
+ /** 认证ID */
+ private Long authId;
+
+ /** 用户ID */
+ private Long userId;
+
+ /** 认证类型: admin/phone/wechat_muhu/wechat_vet */
+ @Excel(name = "认证类型")
+ private String authType;
+
+ /** 认证标识: 用户名/手机号/微信openid */
+ @Excel(name = "认证标识")
+ private String authKey;
+
+ /** 认证密钥: 密码/微信session_key */
+ private String authSecret;
+
+ /** 状态(0正常 1停用) */
+ @Excel(name = "状态", readConverterExp = "0=正常,1=停用")
+ private String status;
+
+ public Long getAuthId() {
+ return authId;
+ }
+
+ public void setAuthId(Long authId) {
+ this.authId = authId;
+ }
+
+ public Long getUserId() {
+ return userId;
+ }
+
+ public void setUserId(Long userId) {
+ this.userId = userId;
+ }
+
+ public String getAuthType() {
+ return authType;
+ }
+
+ public void setAuthType(String authType) {
+ this.authType = authType;
+ }
+
+ public String getAuthKey() {
+ return authKey;
+ }
+
+ public void setAuthKey(String authKey) {
+ this.authKey = authKey;
+ }
+
+ public String getAuthSecret() {
+ return authSecret;
+ }
+
+ public void setAuthSecret(String authSecret) {
+ this.authSecret = authSecret;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
+ .append("authId", getAuthId())
+ .append("userId", getUserId())
+ .append("authType", getAuthType())
+ .append("authKey", getAuthKey())
+ .append("authSecret", getAuthSecret())
+ .append("status", getStatus())
+ .append("createBy", getCreateBy())
+ .append("createTime", getCreateTime())
+ .append("updateBy", getUpdateBy())
+ .append("updateTime", getUpdateTime())
+ .append("remark", getRemark())
+ .toString();
+ }
+}
\ No newline at end of file
diff --git a/chenhai-common/src/main/java/com/chenhai/common/core/domain/model/LoginUser.java b/chenhai-common/src/main/java/com/chenhai/common/core/domain/model/LoginUser.java
index e3d83bb..16f3c89 100644
--- a/chenhai-common/src/main/java/com/chenhai/common/core/domain/model/LoginUser.java
+++ b/chenhai-common/src/main/java/com/chenhai/common/core/domain/model/LoginUser.java
@@ -71,6 +71,12 @@ public class LoginUser implements UserDetails
*/
private SysUser user;
+ /**
+ * 客户端类型: admin/vet-pc/vet-app/herdsman-app
+ */
+ private String clientType;
+
+
public LoginUser()
{
}
@@ -89,6 +95,15 @@ public class LoginUser implements UserDetails
this.permissions = permissions;
}
+ // 修改构造函数,支持clientType
+ public LoginUser(Long userId, Long deptId, SysUser user, Set permissions, String clientType) {
+ this.userId = userId;
+ this.deptId = deptId;
+ this.user = user;
+ this.permissions = permissions;
+ this.clientType = clientType;
+ }
+
public Long getUserId()
{
return userId;
@@ -258,6 +273,14 @@ public class LoginUser implements UserDetails
this.user = user;
}
+ public String getClientType() {
+ return clientType;
+ }
+
+ public void setClientType(String clientType) {
+ this.clientType = clientType;
+ }
+
@Override
public Collection extends GrantedAuthority> getAuthorities()
{
diff --git a/chenhai-common/src/main/java/com/chenhai/common/core/domain/model/PhoneDecryptRequest.java b/chenhai-common/src/main/java/com/chenhai/common/core/domain/model/PhoneDecryptRequest.java
new file mode 100644
index 0000000..79ef2ad
--- /dev/null
+++ b/chenhai-common/src/main/java/com/chenhai/common/core/domain/model/PhoneDecryptRequest.java
@@ -0,0 +1,90 @@
+// PhoneDecryptRequest.java
+package com.chenhai.common.core.domain.model;
+
+import jakarta.validation.constraints.NotBlank;
+
+public class PhoneDecryptRequest {
+ @NotBlank private String openid;
+ @NotBlank private String tempCode;
+ @NotBlank private String encryptedData;
+ @NotBlank private String iv;
+ @NotBlank private String authType;
+ @NotBlank private String userType; // "herdsman"
+
+ // 用户基本信息(可选)
+ private String nickName;
+ private String avatarUrl;
+ private Integer gender;
+
+ public String getOpenid() {
+ return openid;
+ }
+
+ public void setOpenid(String openid) {
+ this.openid = openid;
+ }
+
+ public String getTempCode() {
+ return tempCode;
+ }
+
+ public void setTempCode(String tempCode) {
+ this.tempCode = tempCode;
+ }
+
+ public String getEncryptedData() {
+ return encryptedData;
+ }
+
+ public void setEncryptedData(String encryptedData) {
+ this.encryptedData = encryptedData;
+ }
+
+ public String getIv() {
+ return iv;
+ }
+
+ public void setIv(String iv) {
+ this.iv = iv;
+ }
+
+ public String getAuthType() {
+ return authType;
+ }
+
+ public void setAuthType(String authType) {
+ this.authType = authType;
+ }
+
+ public String getUserType() {
+ return userType;
+ }
+
+ public void setUserType(String userType) {
+ this.userType = userType;
+ }
+
+ public String getNickName() {
+ return nickName;
+ }
+
+ public void setNickName(String nickName) {
+ this.nickName = nickName;
+ }
+
+ public String getAvatarUrl() {
+ return avatarUrl;
+ }
+
+ public void setAvatarUrl(String avatarUrl) {
+ this.avatarUrl = avatarUrl;
+ }
+
+ public Integer getGender() {
+ return gender;
+ }
+
+ public void setGender(Integer gender) {
+ this.gender = gender;
+ }
+}
\ No newline at end of file
diff --git a/chenhai-common/src/main/java/com/chenhai/common/core/domain/model/PhoneLoginBody.java b/chenhai-common/src/main/java/com/chenhai/common/core/domain/model/PhoneLoginBody.java
new file mode 100644
index 0000000..b05ccb8
--- /dev/null
+++ b/chenhai-common/src/main/java/com/chenhai/common/core/domain/model/PhoneLoginBody.java
@@ -0,0 +1,33 @@
+package com.chenhai.common.core.domain.model;
+
+import jakarta.validation.constraints.NotBlank;
+
+public class PhoneLoginBody {
+ @NotBlank private String phone;
+ @NotBlank private String password;
+ private String clientType; // 可选:前端可以指定 "vet-pc" 或 "herdsman-pc"
+
+ public String getPhone() {
+ return phone;
+ }
+
+ public void setPhone(String phone) {
+ this.phone = phone;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ public String getClientType() {
+ return clientType;
+ }
+
+ public void setClientType(String clientType) {
+ this.clientType = clientType;
+ }
+}
\ No newline at end of file
diff --git a/chenhai-common/src/main/java/com/chenhai/common/core/domain/model/WechatBindRequest.java b/chenhai-common/src/main/java/com/chenhai/common/core/domain/model/WechatBindRequest.java
new file mode 100644
index 0000000..71c04ca
--- /dev/null
+++ b/chenhai-common/src/main/java/com/chenhai/common/core/domain/model/WechatBindRequest.java
@@ -0,0 +1,100 @@
+package com.chenhai.common.core.domain.model;
+
+import jakarta.validation.constraints.NotBlank;
+
+public class WechatBindRequest {
+ @NotBlank private String openid;
+ @NotBlank private String tempCode;
+ @NotBlank private String authType; // "wechat_muhu" 或 "wechat_vet"
+ @NotBlank private String userType; // "herdsman" 或 "vet"
+ @NotBlank private String clientType; // "herdsman-app" 或 "vet-app"
+
+ // 微信加密的手机号数据
+ @NotBlank private String encryptedData;
+ @NotBlank private String iv;
+
+ // 用户基本信息
+ private String nickName;
+ private String avatarUrl;
+ private Integer gender;
+
+ public String getOpenid() {
+ return openid;
+ }
+
+ public void setOpenid(String openid) {
+ this.openid = openid;
+ }
+
+ public String getTempCode() {
+ return tempCode;
+ }
+
+ public void setTempCode(String tempCode) {
+ this.tempCode = tempCode;
+ }
+
+ public String getAuthType() {
+ return authType;
+ }
+
+ public void setAuthType(String authType) {
+ this.authType = authType;
+ }
+
+ public String getUserType() {
+ return userType;
+ }
+
+ public void setUserType(String userType) {
+ this.userType = userType;
+ }
+
+ public String getClientType() {
+ return clientType;
+ }
+
+ public void setClientType(String clientType) {
+ this.clientType = clientType;
+ }
+
+ public String getEncryptedData() {
+ return encryptedData;
+ }
+
+ public void setEncryptedData(String encryptedData) {
+ this.encryptedData = encryptedData;
+ }
+
+ public String getIv() {
+ return iv;
+ }
+
+ public void setIv(String iv) {
+ this.iv = iv;
+ }
+
+ public String getNickName() {
+ return nickName;
+ }
+
+ public void setNickName(String nickName) {
+ this.nickName = nickName;
+ }
+
+ public String getAvatarUrl() {
+ return avatarUrl;
+ }
+
+ public void setAvatarUrl(String avatarUrl) {
+ this.avatarUrl = avatarUrl;
+ }
+
+ public Integer getGender() {
+ return gender;
+ }
+
+ public void setGender(Integer gender) {
+ this.gender = gender;
+ }
+}
\ No newline at end of file
diff --git a/chenhai-common/src/main/java/com/chenhai/common/core/domain/model/WechatBindResult.java b/chenhai-common/src/main/java/com/chenhai/common/core/domain/model/WechatBindResult.java
new file mode 100644
index 0000000..5b8cfed
--- /dev/null
+++ b/chenhai-common/src/main/java/com/chenhai/common/core/domain/model/WechatBindResult.java
@@ -0,0 +1,64 @@
+package com.chenhai.common.core.domain.model;
+
+/**
+ * 微信绑定结果
+ */
+public class WechatBindResult {
+
+ private boolean success;
+ private String message;
+ private String token;
+ private boolean needBind; // 是否需要绑定
+
+ public WechatBindResult() {
+ }
+
+ public WechatBindResult(boolean success, String message) {
+ this.success = success;
+ this.message = message;
+ }
+
+ public WechatBindResult(boolean success, String message, String token) {
+ this.success = success;
+ this.message = message;
+ this.token = token;
+ }
+
+// public WechatBindResult(boolean needBind, String message) {
+// this.needBind = needBind;
+// this.message = message;
+// }
+
+ // Getters and Setters
+ public boolean isSuccess() {
+ return success;
+ }
+
+ public void setSuccess(boolean success) {
+ this.success = success;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+
+ public String getToken() {
+ return token;
+ }
+
+ public void setToken(String token) {
+ this.token = token;
+ }
+
+ public boolean isNeedBind() {
+ return needBind;
+ }
+
+ public void setNeedBind(boolean needBind) {
+ this.needBind = needBind;
+ }
+}
\ No newline at end of file
diff --git a/chenhai-common/src/main/java/com/chenhai/common/core/domain/model/WechatLoginBody.java b/chenhai-common/src/main/java/com/chenhai/common/core/domain/model/WechatLoginBody.java
new file mode 100644
index 0000000..b3c5bfb
--- /dev/null
+++ b/chenhai-common/src/main/java/com/chenhai/common/core/domain/model/WechatLoginBody.java
@@ -0,0 +1,25 @@
+package com.chenhai.common.core.domain.model;
+
+/**
+ * 微信登录请求体
+ */
+public class WechatLoginBody {
+ private String code;
+ private String clientType; // herdsman-app / vet-app
+
+ public String getCode() {
+ return code;
+ }
+
+ public void setCode(String code) {
+ this.code = code;
+ }
+
+ public String getClientType() {
+ return clientType;
+ }
+
+ public void setClientType(String clientType) {
+ this.clientType = clientType;
+ }
+}
\ No newline at end of file
diff --git a/chenhai-common/src/main/java/com/chenhai/common/utils/WechatDecryptUtil.java b/chenhai-common/src/main/java/com/chenhai/common/utils/WechatDecryptUtil.java
new file mode 100644
index 0000000..d6bc13b
--- /dev/null
+++ b/chenhai-common/src/main/java/com/chenhai/common/utils/WechatDecryptUtil.java
@@ -0,0 +1,66 @@
+package com.chenhai.common.utils;
+
+import com.alibaba.fastjson2.JSONObject;
+import javax.crypto.Cipher;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+
+/**
+ * 微信解密工具类
+ */
+public class WechatDecryptUtil {
+
+ /**
+ * 解密微信加密数据(手机号)
+ */
+ public static String decryptPhone(String encryptedData, String iv, String sessionKey) {
+ try {
+ byte[] dataByte = Base64.getDecoder().decode(encryptedData);
+ byte[] keyByte = Base64.getDecoder().decode(sessionKey);
+ byte[] ivByte = Base64.getDecoder().decode(iv);
+
+ // AES-128-CBC 解密
+ Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
+ SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
+ IvParameterSpec ivSpec = new IvParameterSpec(ivByte);
+
+ cipher.init(Cipher.DECRYPT_MODE, spec, ivSpec);
+ byte[] resultByte = cipher.doFinal(dataByte);
+
+ String result = new String(resultByte, StandardCharsets.UTF_8);
+
+ // 解析JSON获取手机号
+ JSONObject jsonObject = JSONObject.parseObject(result);
+ return jsonObject.getString("phoneNumber");
+
+ } catch (Exception e) {
+ throw new RuntimeException("微信手机号解密失败", e);
+ }
+ }
+
+ /**
+ * 解密用户信息
+ */
+ public static JSONObject decryptUserInfo(String encryptedData, String iv, String sessionKey) {
+ try {
+ byte[] dataByte = Base64.getDecoder().decode(encryptedData);
+ byte[] keyByte = Base64.getDecoder().decode(sessionKey);
+ byte[] ivByte = Base64.getDecoder().decode(iv);
+
+ Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
+ SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
+ IvParameterSpec ivSpec = new IvParameterSpec(ivByte);
+
+ cipher.init(Cipher.DECRYPT_MODE, spec, ivSpec);
+ byte[] resultByte = cipher.doFinal(dataByte);
+
+ String result = new String(resultByte, StandardCharsets.UTF_8);
+ return JSONObject.parseObject(result);
+
+ } catch (Exception e) {
+ throw new RuntimeException("微信用户信息解密失败", e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/chenhai-framework/pom.xml b/chenhai-framework/pom.xml
index dfbc74b..22eaf12 100644
--- a/chenhai-framework/pom.xml
+++ b/chenhai-framework/pom.xml
@@ -59,6 +59,12 @@
chenhai-system
+
+ org.projectlombok
+ lombok
+ true
+
+
\ No newline at end of file
diff --git a/chenhai-framework/src/main/java/com/chenhai/framework/config/AuthenticationProviderConfig.java b/chenhai-framework/src/main/java/com/chenhai/framework/config/AuthenticationProviderConfig.java
new file mode 100644
index 0000000..61746a1
--- /dev/null
+++ b/chenhai-framework/src/main/java/com/chenhai/framework/config/AuthenticationProviderConfig.java
@@ -0,0 +1,80 @@
+package com.chenhai.framework.config;
+
+import com.chenhai.common.core.redis.RedisCache;
+import com.chenhai.framework.security.provider.PhoneAuthenticationProvider;
+import com.chenhai.framework.security.provider.WechatAuthenticationProvider;
+import com.chenhai.framework.web.service.UserDetailsServiceImpl;
+import com.chenhai.framework.web.service.SysPermissionService;
+import com.chenhai.framework.web.service.SysPasswordService;
+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.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+
+/**
+ * 认证提供者配置类
+ */
+@Configuration
+public class AuthenticationProviderConfig {
+
+ /**
+ * 默认的用户名密码认证提供者(用于管理后台)
+ */
+ @Bean
+ public DaoAuthenticationProvider daoAuthenticationProvider(
+ UserDetailsServiceImpl userDetailsService,
+ BCryptPasswordEncoder passwordEncoder) {
+
+ DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
+ provider.setUserDetailsService(userDetailsService);
+ provider.setPasswordEncoder(passwordEncoder);
+ provider.setHideUserNotFoundExceptions(false);
+ return provider;
+ }
+
+ /**
+ * 手机号认证提供者(用于兽医PC端)
+ */
+ @Bean
+ public PhoneAuthenticationProvider phoneAuthenticationProvider(
+ IUserAuthService userAuthService,
+ ISysUserService userService,
+ SysPermissionService permissionService,
+ SysPasswordService passwordService,
+ BCryptPasswordEncoder passwordEncoder) {
+
+ return new PhoneAuthenticationProvider(
+ userAuthService,
+ userService,
+ permissionService,
+ passwordService,
+ passwordEncoder
+ );
+ }
+
+ /**
+ * 微信认证提供者(用于小程序)
+ */
+ @Bean
+ public WechatAuthenticationProvider wechatAuthenticationProvider(
+ WechatService wechatService,
+ IUserAuthService userAuthService,
+ ISysUserService userService,
+ SysPermissionService permissionService,
+ ISysConfigService sysConfigService,
+ RedisCache redisCache) {
+
+ return new WechatAuthenticationProvider(
+ wechatService,
+ userAuthService,
+ userService,
+ permissionService,
+ sysConfigService,
+ redisCache
+ );
+ }
+}
\ No newline at end of file
diff --git a/chenhai-framework/src/main/java/com/chenhai/framework/config/SecurityConfig.java b/chenhai-framework/src/main/java/com/chenhai/framework/config/SecurityConfig.java
index ca37547..2e9cdff 100644
--- a/chenhai-framework/src/main/java/com/chenhai/framework/config/SecurityConfig.java
+++ b/chenhai-framework/src/main/java/com/chenhai/framework/config/SecurityConfig.java
@@ -1,10 +1,17 @@
package com.chenhai.framework.config;
+import com.chenhai.framework.security.provider.PhoneAuthenticationProvider;
+import com.chenhai.framework.security.provider.WechatAuthenticationProvider;
+import com.chenhai.framework.web.service.UserDetailsServiceImpl;
+import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.AuthenticationProvider;
+import org.springframework.security.authentication.ProviderManager;
+import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
@@ -19,11 +26,16 @@ import com.chenhai.framework.security.filter.JwtAuthenticationTokenFilter;
import com.chenhai.framework.security.handle.AuthenticationEntryPointImpl;
import com.chenhai.framework.security.handle.LogoutSuccessHandlerImpl;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
/**
* spring security配置
*
* @author ruoyi
*/
+@Slf4j
@EnableMethodSecurity(prePostEnabled = true, securedEnabled = true)
@Configuration
public class SecurityConfig
@@ -58,14 +70,46 @@ public class SecurityConfig
@Autowired
private PermitAllUrlProperties permitAllUrl;
+ @Autowired
+ private UserDetailsServiceImpl userDetailsService;
+
+
/**
* 身份验证实现
*/
- @Bean
- public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception
- {
- return authenticationConfiguration.getAuthenticationManager();
- }
+// @Bean
+// public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception
+// {
+// return authenticationConfiguration.getAuthenticationManager();
+// }
+ @Bean
+ public AuthenticationManager authenticationManager(
+ DaoAuthenticationProvider daoAuthenticationProvider,
+ PhoneAuthenticationProvider phoneAuthenticationProvider,
+ WechatAuthenticationProvider wechatAuthenticationProvider) {
+
+ // 组合所有Provider
+ List providers = new ArrayList<>();
+ providers.add(daoAuthenticationProvider); // 管理端用户名密码
+
+ // 添加手机号认证
+ if (phoneAuthenticationProvider != null) {
+ providers.add(phoneAuthenticationProvider);
+ log.info("PhoneAuthenticationProvider loaded successfully");
+ } else {
+ log.warn("PhoneAuthenticationProvider is not available");
+ }
+
+ // 添加微信认证
+ if (wechatAuthenticationProvider != null) {
+ providers.add(wechatAuthenticationProvider);
+ log.info("WechatAuthenticationProvider loaded successfully");
+ } else {
+ log.warn("WechatAuthenticationProvider is not available");
+ }
+
+ return new ProviderManager(providers);
+ }
/**
* anyRequest | 匹配所有请求路径
@@ -101,6 +145,7 @@ public class SecurityConfig
permitAllUrl.getUrls().forEach(url -> requests.requestMatchers(url).permitAll());
// 对于登录login 注册register 验证码captchaImage 允许匿名访问
requests.requestMatchers("/login", "/register", "/captchaImage").permitAll()
+ .requestMatchers("/auth/**").permitAll()
// 静态资源,可匿名访问
.requestMatchers(HttpMethod.GET, "/", "/*.html", "/**.html", "/**.css", "/**.js", "/profile/**").permitAll()
.requestMatchers("/swagger-ui.html", "/v3/api-docs/**", "/swagger-ui/**", "/druid/**").permitAll()
diff --git a/chenhai-framework/src/main/java/com/chenhai/framework/security/exception/WechatNeedBindException.java b/chenhai-framework/src/main/java/com/chenhai/framework/security/exception/WechatNeedBindException.java
new file mode 100644
index 0000000..cc43880
--- /dev/null
+++ b/chenhai-framework/src/main/java/com/chenhai/framework/security/exception/WechatNeedBindException.java
@@ -0,0 +1,29 @@
+package com.chenhai.framework.security.exception;
+
+import org.springframework.security.core.AuthenticationException;
+
+public class WechatNeedBindException extends AuthenticationException {
+
+ private final String openid;
+ private final String tempCode;
+ private final String authType;
+ private final String userType; // "herdsman" 或 "vet"
+ private final String clientType; // "herdsman-app" 或 "vet-app"
+
+ public WechatNeedBindException(String msg, String openid, String tempCode,
+ String authType, String userType, String clientType) {
+ super(msg);
+ this.openid = openid;
+ this.tempCode = tempCode;
+ this.authType = authType;
+ this.userType = userType;
+ this.clientType = clientType;
+ }
+
+ // Getters
+ public String getOpenid() { return openid; }
+ public String getTempCode() { return tempCode; }
+ public String getAuthType() { return authType; }
+ public String getUserType() { return userType; }
+ public String getClientType() { return clientType; }
+}
\ No newline at end of file
diff --git a/chenhai-framework/src/main/java/com/chenhai/framework/security/provider/PhoneAuthenticationProvider.java b/chenhai-framework/src/main/java/com/chenhai/framework/security/provider/PhoneAuthenticationProvider.java
new file mode 100644
index 0000000..df54b92
--- /dev/null
+++ b/chenhai-framework/src/main/java/com/chenhai/framework/security/provider/PhoneAuthenticationProvider.java
@@ -0,0 +1,90 @@
+package com.chenhai.framework.security.provider;
+
+import com.chenhai.common.core.domain.entity.SysUser;
+import com.chenhai.common.core.domain.model.LoginUser;
+import com.chenhai.common.exception.ServiceException;
+import com.chenhai.common.utils.MessageUtils;
+import com.chenhai.framework.security.token.PhoneAuthenticationToken;
+import com.chenhai.framework.web.service.SysPasswordService;
+import com.chenhai.framework.web.service.SysPermissionService;
+import com.chenhai.muhu.service.IUserAuthService;
+import com.chenhai.system.service.ISysUserService;
+import org.springframework.security.authentication.AuthenticationProvider;
+import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+/**
+ * 手机号认证Provider
+ */
+public class PhoneAuthenticationProvider implements AuthenticationProvider {
+
+ private final IUserAuthService userAuthService;
+ private final ISysUserService userService;
+ private final SysPermissionService permissionService;
+ private final SysPasswordService passwordService;
+ private final BCryptPasswordEncoder passwordEncoder;
+
+ public PhoneAuthenticationProvider(IUserAuthService userAuthService,
+ ISysUserService userService,
+ SysPermissionService permissionService,
+ SysPasswordService passwordService,
+ BCryptPasswordEncoder passwordEncoder) {
+ this.userAuthService = userAuthService;
+ this.userService = userService;
+ this.permissionService = permissionService;
+ this.passwordService = passwordService;
+ this.passwordEncoder = passwordEncoder;
+ }
+
+ @Override
+ public Authentication authenticate(Authentication authentication) throws AuthenticationException {
+ PhoneAuthenticationToken authToken = (PhoneAuthenticationToken) authentication;
+
+ String phone = authToken.getPhone();
+ String password = authToken.getPassword();
+ String clientType = authToken.getClientType();
+
+ // 1. 验证参数
+ if (phone == null || password == null) {
+ throw new BadCredentialsException("手机号或密码不能为空");
+ }
+
+ // 2. 根据手机号查找用户
+ SysUser user = userAuthService.findUserByAuth("phone", phone);
+
+ if (user == null) {
+ throw new BadCredentialsException("手机号未注册");
+ }
+
+ // 3. 验证用户状态
+ if ("1".equals(user.getStatus())) {
+ throw new ServiceException(MessageUtils.message("user.blocked"));
+ }
+ if ("2".equals(user.getDelFlag())) {
+ throw new ServiceException(MessageUtils.message("user.password.delete"));
+ }
+
+ // 4. 验证密码
+ if (!passwordEncoder.matches(password, user.getPassword())) {
+ // 密码错误计数逻辑(复用原有的)
+ throw new BadCredentialsException("密码错误");
+ }
+
+ // 5. 创建LoginUser
+ LoginUser loginUser = new LoginUser(
+ user.getUserId(),
+ user.getDeptId(),
+ user,
+ permissionService.getMenuPermission(user),
+ clientType
+ );
+
+ return new PhoneAuthenticationToken(loginUser, loginUser.getAuthorities());
+ }
+
+ @Override
+ public boolean supports(Class> authentication) {
+ return PhoneAuthenticationToken.class.isAssignableFrom(authentication);
+ }
+}
\ No newline at end of file
diff --git a/chenhai-framework/src/main/java/com/chenhai/framework/security/provider/WechatAuthenticationProvider.java b/chenhai-framework/src/main/java/com/chenhai/framework/security/provider/WechatAuthenticationProvider.java
new file mode 100644
index 0000000..7652dbc
--- /dev/null
+++ b/chenhai-framework/src/main/java/com/chenhai/framework/security/provider/WechatAuthenticationProvider.java
@@ -0,0 +1,142 @@
+package com.chenhai.framework.security.provider;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.chenhai.common.core.domain.entity.SysUser;
+import com.chenhai.common.core.domain.model.LoginUser;
+import com.chenhai.common.core.redis.RedisCache;
+import com.chenhai.framework.security.exception.WechatNeedBindException;
+import com.chenhai.framework.security.token.WechatAuthenticationToken;
+import com.chenhai.framework.web.service.SysPermissionService;
+import com.chenhai.system.service.ISysConfigService;
+import com.chenhai.system.service.ISysUserService;
+import com.chenhai.muhu.service.IUserAuthService;
+import com.chenhai.muhu.service.WechatService;
+import org.springframework.security.authentication.AuthenticationProvider;
+import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.util.DigestUtils;
+
+import java.util.Date;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 微信认证Provider - 统一处理牧户和兽医
+ */
+public class WechatAuthenticationProvider implements AuthenticationProvider {
+
+ private final WechatService wechatService;
+ private final IUserAuthService userAuthService;
+ private final ISysUserService userService;
+ private final SysPermissionService permissionService;
+ private final ISysConfigService sysConfigService;
+ private final RedisCache redisCache;
+
+ public WechatAuthenticationProvider(WechatService wechatService,
+ IUserAuthService userAuthService,
+ ISysUserService userService,
+ SysPermissionService permissionService,
+ ISysConfigService sysConfigService,
+ RedisCache redisCache) {
+ this.wechatService = wechatService;
+ this.userAuthService = userAuthService;
+ this.userService = userService;
+ this.permissionService = permissionService;
+ this.sysConfigService = sysConfigService;
+ this.redisCache = redisCache;
+ }
+
+ @Override
+ public Authentication authenticate(Authentication authentication) throws AuthenticationException {
+ WechatAuthenticationToken authToken = (WechatAuthenticationToken) authentication;
+
+ String code = authToken.getCode();
+ String clientType = authToken.getClientType();
+
+ // 1. 调用微信API获取openid
+ JSONObject wechatResult = wechatService.code2session(code, clientType);
+ String openid = wechatResult.getString("openid");
+ String sessionKey = wechatResult.getString("session_key");
+
+ if (openid == null) {
+ throw new BadCredentialsException("微信登录失败:未获取到openid");
+ }
+
+ // 2. 确定认证类型(根据clientType)
+ String authType = getAuthTypeByClientType(clientType);
+
+ // 3. 查询用户是否已绑定微信
+ SysUser user = userAuthService.findUserByAuth(authType, openid);
+
+ // 4. 已绑定用户,直接登录
+ if (user != null) {
+ return createSuccessAuthentication(user, clientType);
+ }
+
+ // 5. 未绑定,需要绑定手机号
+ String tempCode = generateTempCode(openid, authType);
+
+ // 存储sessionKey到Redis(5分钟过期)
+ redisCache.setCacheObject(
+ "wechat:session:" + tempCode,
+ sessionKey,
+ 5, TimeUnit.MINUTES
+ );
+
+ // 6. 根据客户端类型确定用户类型
+ String userType = getUserTypeByClientType(clientType);
+
+ // 7. 抛出需要绑定的异常
+ throw new WechatNeedBindException(
+ "需要绑定手机号",
+ openid,
+ tempCode,
+ authType,
+ userType, // "herdsman" 或 "vet"
+ clientType // "herdsman-app" 或 "vet-app"
+ );
+ }
+
+ @Override
+ public boolean supports(Class> authentication) {
+ return WechatAuthenticationToken.class.isAssignableFrom(authentication);
+ }
+
+ private String getAuthTypeByClientType(String clientType) {
+ switch (clientType) {
+ case "herdsman-app":
+ return "wechat_muhu";
+ case "vet-app":
+ return "wechat_vet";
+ default:
+ throw new IllegalArgumentException("不支持的客户端类型: " + clientType);
+ }
+ }
+
+ private String getUserTypeByClientType(String clientType) {
+ switch (clientType) {
+ case "herdsman-app":
+ return "herdsman";
+ case "vet-app":
+ return "vet";
+ default:
+ throw new IllegalArgumentException("不支持的客户端类型: " + clientType);
+ }
+ }
+
+ private Authentication createSuccessAuthentication(SysUser user, String clientType) {
+ LoginUser loginUser = new LoginUser(
+ user.getUserId(),
+ user.getDeptId(),
+ user,
+ permissionService.getMenuPermission(user),
+ clientType
+ );
+ return new WechatAuthenticationToken(loginUser, loginUser.getAuthorities());
+ }
+
+ private String generateTempCode(String openid, String authType) {
+ String raw = openid + authType + System.currentTimeMillis() + Math.random();
+ return DigestUtils.md5DigestAsHex(raw.getBytes()).substring(0, 16);
+ }
+}
\ No newline at end of file
diff --git a/chenhai-framework/src/main/java/com/chenhai/framework/security/token/PhoneAuthenticationToken.java b/chenhai-framework/src/main/java/com/chenhai/framework/security/token/PhoneAuthenticationToken.java
new file mode 100644
index 0000000..1e112f7
--- /dev/null
+++ b/chenhai-framework/src/main/java/com/chenhai/framework/security/token/PhoneAuthenticationToken.java
@@ -0,0 +1,69 @@
+package com.chenhai.framework.security.token;
+
+import org.springframework.security.authentication.AbstractAuthenticationToken;
+import org.springframework.security.core.GrantedAuthority;
+
+import java.util.Collection;
+
+/**
+ * 手机号认证Token
+ */
+public class PhoneAuthenticationToken extends AbstractAuthenticationToken {
+
+ private final String phone;
+ private final String password;
+ private final String clientType;
+ private Object principal;
+
+ public PhoneAuthenticationToken(String phone, String password, String clientType) {
+ super(null);
+ this.phone = phone;
+ this.password = password;
+ this.clientType = clientType;
+ this.setAuthenticated(false);
+ }
+
+ public PhoneAuthenticationToken(Object principal, Collection extends GrantedAuthority> authorities) {
+ super(authorities);
+ this.principal = principal;
+ this.phone = null;
+ this.password = null;
+ this.clientType = null;
+ super.setAuthenticated(true);
+ }
+
+ public String getPhone() {
+ return phone;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public String getClientType() {
+ return clientType;
+ }
+
+ @Override
+ public Object getCredentials() {
+ return password;
+ }
+
+ @Override
+ public Object getPrincipal() {
+ return principal;
+ }
+
+ @Override
+ public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
+ if (isAuthenticated) {
+ throw new IllegalArgumentException("Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
+ }
+ super.setAuthenticated(false);
+ }
+
+ @Override
+ public void eraseCredentials() {
+ super.eraseCredentials();
+ }
+}
\ No newline at end of file
diff --git a/chenhai-framework/src/main/java/com/chenhai/framework/security/token/WechatAuthenticationToken.java b/chenhai-framework/src/main/java/com/chenhai/framework/security/token/WechatAuthenticationToken.java
new file mode 100644
index 0000000..1c3c868
--- /dev/null
+++ b/chenhai-framework/src/main/java/com/chenhai/framework/security/token/WechatAuthenticationToken.java
@@ -0,0 +1,62 @@
+package com.chenhai.framework.security.token;
+
+import org.springframework.security.authentication.AbstractAuthenticationToken;
+import org.springframework.security.core.GrantedAuthority;
+
+import java.util.Collection;
+
+/**
+ * 微信认证Token
+ */
+public class WechatAuthenticationToken extends AbstractAuthenticationToken {
+
+ private final String code;
+ private final String clientType;
+ private Object principal;
+
+ public WechatAuthenticationToken(String code, String clientType) {
+ super(null);
+ this.code = code;
+ this.clientType = clientType;
+ this.setAuthenticated(false);
+ }
+
+ public WechatAuthenticationToken(Object principal, Collection extends GrantedAuthority> authorities) {
+ super(authorities);
+ this.principal = principal;
+ this.code = null;
+ this.clientType = null;
+ super.setAuthenticated(true);
+ }
+
+ public String getCode() {
+ return code;
+ }
+
+ public String getClientType() {
+ return clientType;
+ }
+
+ @Override
+ public Object getCredentials() {
+ return null;
+ }
+
+ @Override
+ public Object getPrincipal() {
+ return principal;
+ }
+
+ @Override
+ public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
+ if (isAuthenticated) {
+ throw new IllegalArgumentException("Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
+ }
+ super.setAuthenticated(false);
+ }
+
+ @Override
+ public void eraseCredentials() {
+ super.eraseCredentials();
+ }
+}
\ No newline at end of file
diff --git a/chenhai-framework/src/main/java/com/chenhai/framework/web/service/SysSmsService.java b/chenhai-framework/src/main/java/com/chenhai/framework/web/service/SysSmsService.java
new file mode 100644
index 0000000..4f4936d
--- /dev/null
+++ b/chenhai-framework/src/main/java/com/chenhai/framework/web/service/SysSmsService.java
@@ -0,0 +1,17 @@
+package com.chenhai.framework.web.service;
+
+/**
+ * 短信服务接口
+ */
+public interface SysSmsService {
+
+ /**
+ * 发送短信验证码
+ */
+ boolean sendSmsCode(String phone);
+
+ /**
+ * 验证短信验证码
+ */
+ boolean verifySmsCode(String phone, String code);
+}
\ No newline at end of file
diff --git a/chenhai-framework/src/main/java/com/chenhai/framework/web/service/SysSmsServiceImpl.java b/chenhai-framework/src/main/java/com/chenhai/framework/web/service/SysSmsServiceImpl.java
new file mode 100644
index 0000000..facb6d3
--- /dev/null
+++ b/chenhai-framework/src/main/java/com/chenhai/framework/web/service/SysSmsServiceImpl.java
@@ -0,0 +1,27 @@
+package com.chenhai.framework.web.service;
+
+import com.chenhai.framework.web.service.SysSmsService;
+import org.springframework.stereotype.Service;
+
+/**
+ * 短信服务实现(临时实现,用于绕过依赖检查)
+ */
+@Service
+public class SysSmsServiceImpl implements SysSmsService {
+
+ @Override
+ public boolean sendSmsCode(String phone) {
+ // 临时实现,直接返回true
+ // TODO: 后续实现真正的短信发送逻辑
+ System.out.println("发送短信验证码到: " + phone + " (模拟)");
+ return true;
+ }
+
+ @Override
+ public boolean verifySmsCode(String phone, String code) {
+ // 临时实现,验证码固定为"123456"
+ // TODO: 后续实现真正的短信验证逻辑
+ System.out.println("验证短信验证码: phone=" + phone + ", code=" + code);
+ return "123456".equals(code);
+ }
+}
\ No newline at end of file
diff --git a/chenhai-framework/src/main/java/com/chenhai/framework/web/service/TokenService.java b/chenhai-framework/src/main/java/com/chenhai/framework/web/service/TokenService.java
index ce9840b..80b227c 100644
--- a/chenhai-framework/src/main/java/com/chenhai/framework/web/service/TokenService.java
+++ b/chenhai-framework/src/main/java/com/chenhai/framework/web/service/TokenService.java
@@ -145,13 +145,25 @@ public class TokenService
*
* @param loginUser 登录信息
*/
- public void refreshToken(LoginUser loginUser)
- {
+// public void refreshToken(LoginUser loginUser)
+// {
+// loginUser.setLoginTime(System.currentTimeMillis());
+// loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE);
+// // 根据uuid将loginUser缓存
+// String userKey = getTokenKey(loginUser.getToken());
+// redisCache.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES);
+// }
+ // 2. 修改refreshToken方法,支持多端
+ public void refreshToken(LoginUser loginUser) {
loginUser.setLoginTime(System.currentTimeMillis());
- loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE);
+
+ // 根据客户端类型设置不同的过期时间
+ int customExpireTime = getExpireTimeByClientType(loginUser.getClientType());
+ loginUser.setExpireTime(loginUser.getLoginTime() + customExpireTime * MILLIS_MINUTE);
+
// 根据uuid将loginUser缓存
String userKey = getTokenKey(loginUser.getToken());
- redisCache.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES);
+ redisCache.setCacheObject(userKey, loginUser, customExpireTime, TimeUnit.MINUTES);
}
/**
@@ -229,4 +241,47 @@ public class TokenService
{
return CacheConstants.LOGIN_TOKEN_KEY + uuid;
}
+
+ /**
+ * 根据客户端类型获取token过期时间(分钟)
+ */
+ private int getExpireTimeByClientType(String clientType) {
+ if (clientType == null) {
+ return expireTime; // 默认30分钟
+ }
+
+ switch(clientType) {
+ case "herdsman-app":
+ // 从配置读取牧户token时长,默认30天
+ return 43200; // 30天(分钟)
+ case "vet-app":
+ return 10080; // 7天
+ case "vet-pc":
+ return 480; // 8小时
+ case "admin":
+ default:
+ return expireTime; // 30分钟
+ }
+ }
+
+ // 修改createToken方法,使用客户端类型决定过期时间
+// public String createToken(LoginUser loginUser) {
+// String token = IdUtils.fastUUID();
+// loginUser.setToken(token);
+// setUserAgent(loginUser);
+//
+// // 根据客户端类型设置不同的过期时间
+// int customExpireTime = getExpireTimeByClientType(loginUser.getClientType());
+// loginUser.setLoginTime(System.currentTimeMillis());
+// loginUser.setExpireTime(loginUser.getLoginTime() + customExpireTime * MILLIS_MINUTE);
+//
+// // 根据uuid将loginUser缓存
+// String userKey = getTokenKey(loginUser.getToken());
+// redisCache.setCacheObject(userKey, loginUser, customExpireTime, TimeUnit.MINUTES);
+//
+// Map claims = new HashMap<>();
+// claims.put(Constants.LOGIN_USER_KEY, token);
+// claims.put(Constants.JWT_USERNAME, loginUser.getUsername());
+// return createToken(claims);
+// }
}
diff --git a/chenhai-system/pom.xml b/chenhai-system/pom.xml
index 5af37ab..629ee65 100644
--- a/chenhai-system/pom.xml
+++ b/chenhai-system/pom.xml
@@ -23,6 +23,44 @@
chenhai-common
+
+ org.projectlombok
+ lombok
+ true
+
+
+
+
+ cn.hutool
+ hutool-all
+ 5.8.16
+
+
+
+ io.jsonwebtoken
+ jjwt-impl
+ 0.11.5
+ runtime
+
+
+ io.jsonwebtoken
+ jjwt-jackson
+ 0.11.5
+ runtime
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+
+
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+
+
\ No newline at end of file
diff --git a/chenhai-system/src/main/java/com/chenhai/muhu/domain/model/MuhuWxLoginBody.java b/chenhai-system/src/main/java/com/chenhai/muhu/domain/model/MuhuWxLoginBody.java
new file mode 100644
index 0000000..6bf9b5f
--- /dev/null
+++ b/chenhai-system/src/main/java/com/chenhai/muhu/domain/model/MuhuWxLoginBody.java
@@ -0,0 +1,57 @@
+package com.chenhai.muhu.domain.model;
+
+
+import jakarta.validation.constraints.NotBlank;
+
+/**
+ * 牧户微信登录请求体
+ */
+public class MuhuWxLoginBody {
+
+ @NotBlank(message = "微信code不能为空")
+ private String code; // 微信登录code
+ private String encryptedData; // 加密数据
+ private String iv; // 加密向量
+ private String nickName; // 昵称(可选)
+ private String avatarUrl; // 头像(可选)
+
+ public String getCode() {
+ return code;
+ }
+
+ public void setCode(String code) {
+ this.code = code;
+ }
+
+ public String getEncryptedData() {
+ return encryptedData;
+ }
+
+ public void setEncryptedData(String encryptedData) {
+ this.encryptedData = encryptedData;
+ }
+
+ public String getIv() {
+ return iv;
+ }
+
+ public void setIv(String iv) {
+ this.iv = iv;
+ }
+
+ public String getNickName() {
+ return nickName;
+ }
+
+ public void setNickName(String nickName) {
+ this.nickName = nickName;
+ }
+
+ public String getAvatarUrl() {
+ return avatarUrl;
+ }
+
+ public void setAvatarUrl(String avatarUrl) {
+ this.avatarUrl = avatarUrl;
+ }
+}
\ No newline at end of file
diff --git a/chenhai-system/src/main/java/com/chenhai/muhu/service/IUserAuthService.java b/chenhai-system/src/main/java/com/chenhai/muhu/service/IUserAuthService.java
new file mode 100644
index 0000000..6b70f22
--- /dev/null
+++ b/chenhai-system/src/main/java/com/chenhai/muhu/service/IUserAuthService.java
@@ -0,0 +1,36 @@
+package com.chenhai.muhu.service;
+
+import com.chenhai.common.core.domain.entity.SysUser;
+import com.chenhai.common.core.domain.entity.SysUserAuth;
+
+import java.util.List;
+
+/**
+ * 用户认证方式服务接口
+ */
+public interface IUserAuthService {
+ /**
+ * 根据认证类型和标识查找用户
+ */
+ SysUser findUserByAuth(String authType, String authKey);
+
+ /**
+ * 绑定新的认证方式
+ */
+ int bindAuth(Long userId, String authType, String authKey, String authSecret);
+
+ /**
+ * 解绑认证方式
+ */
+ int unbindAuth(Long userId, String authType);
+
+ /**
+ * 获取用户的所有认证方式
+ */
+ List getUserAuths(Long userId);
+
+ /**
+ * 检查认证方式是否存在
+ */
+ boolean checkAuthExists(String authType, String authKey);
+}
\ No newline at end of file
diff --git a/chenhai-system/src/main/java/com/chenhai/muhu/service/WechatService.java b/chenhai-system/src/main/java/com/chenhai/muhu/service/WechatService.java
new file mode 100644
index 0000000..46f6b77
--- /dev/null
+++ b/chenhai-system/src/main/java/com/chenhai/muhu/service/WechatService.java
@@ -0,0 +1,39 @@
+package com.chenhai.muhu.service;
+
+import com.alibaba.fastjson2.JSONObject;
+
+/**
+ * 微信服务接口
+ */
+public interface WechatService {
+ /**
+ * 小程序登录,获取openid和session_key
+ */
+ JSONObject code2session(String code, String clientType);
+
+ /**
+ * 获取微信小程序配置
+ */
+ MiniProgramConfig getMiniProgramConfig(String clientType);
+
+ /**
+ * 微信小程序配置类
+ */
+ class MiniProgramConfig {
+ private String appId;
+ private String appSecret;
+
+ public MiniProgramConfig(String appId, String appSecret) {
+ this.appId = appId;
+ this.appSecret = appSecret;
+ }
+
+ public String getAppId() {
+ return appId;
+ }
+
+ public String getAppSecret() {
+ return appSecret;
+ }
+ }
+}
\ No newline at end of file
diff --git a/chenhai-system/src/main/java/com/chenhai/muhu/service/impl/UserAuthServiceImpl.java b/chenhai-system/src/main/java/com/chenhai/muhu/service/impl/UserAuthServiceImpl.java
new file mode 100644
index 0000000..16d693c
--- /dev/null
+++ b/chenhai-system/src/main/java/com/chenhai/muhu/service/impl/UserAuthServiceImpl.java
@@ -0,0 +1,79 @@
+package com.chenhai.muhu.service.impl;
+
+import com.chenhai.common.core.domain.entity.SysUser;
+import com.chenhai.common.core.domain.entity.SysUserAuth;
+import com.chenhai.muhu.service.IUserAuthService;
+import com.chenhai.system.mapper.SysUserAuthMapper;
+import com.chenhai.system.mapper.SysUserMapper;
+import com.chenhai.system.service.ISysUserService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 用户认证方式服务实现
+ */
+@Service
+public class UserAuthServiceImpl implements IUserAuthService {
+
+ @Autowired
+ private SysUserAuthMapper sysUserAuthMapper;
+
+ @Autowired
+ private SysUserMapper sysUserMapper;
+
+ @Override
+ public SysUser findUserByAuth(String authType, String authKey) {
+ SysUserAuth auth = sysUserAuthMapper.selectByAuthTypeAndKey(authType, authKey);
+ if (auth == null) {
+ return null;
+ }
+ return sysUserMapper.selectUserById(auth.getUserId());
+ }
+
+ @Override
+ @Transactional
+ public int bindAuth(Long userId, String authType, String authKey, String authSecret) {
+ // 检查是否已存在
+ SysUserAuth existAuth = sysUserAuthMapper.selectByAuthTypeAndKey(authType, authKey);
+ if (existAuth != null) {
+ throw new RuntimeException("该认证方式已被其他用户绑定");
+ }
+
+ SysUserAuth auth = new SysUserAuth();
+ auth.setUserId(userId);
+ auth.setAuthType(authType);
+ auth.setAuthKey(authKey);
+ auth.setAuthSecret(authSecret);
+ auth.setStatus("0");
+ auth.setCreateTime(new Date());
+
+ return sysUserAuthMapper.insertSysUserAuth(auth);
+ }
+
+ @Override
+ @Transactional
+ public int unbindAuth(Long userId, String authType) {
+ List auths = sysUserAuthMapper.selectByUserIdAndType(userId, authType);
+ if (auths.isEmpty()) {
+ return 0;
+ }
+
+ Long[] authIds = auths.stream().map(SysUserAuth::getAuthId).toArray(Long[]::new);
+ return sysUserAuthMapper.deleteSysUserAuthByAuthIds(authIds);
+ }
+
+ @Override
+ public List getUserAuths(Long userId) {
+ return sysUserAuthMapper.selectByUserId(userId);
+ }
+
+ @Override
+ public boolean checkAuthExists(String authType, String authKey) {
+ SysUserAuth auth = sysUserAuthMapper.selectByAuthTypeAndKey(authType, authKey);
+ return auth != null;
+ }
+}
\ No newline at end of file
diff --git a/chenhai-system/src/main/java/com/chenhai/muhu/service/impl/WechatServiceImpl.java b/chenhai-system/src/main/java/com/chenhai/muhu/service/impl/WechatServiceImpl.java
new file mode 100644
index 0000000..c42a5b0
--- /dev/null
+++ b/chenhai-system/src/main/java/com/chenhai/muhu/service/impl/WechatServiceImpl.java
@@ -0,0 +1,76 @@
+package com.chenhai.muhu.service.impl;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.chenhai.common.utils.StringUtils;
+import com.chenhai.common.utils.http.HttpUtils;
+import com.chenhai.muhu.service.WechatService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+/**
+ * 微信服务实现
+ */
+@Service
+public class WechatServiceImpl implements WechatService {
+
+ private static final Logger log = LoggerFactory.getLogger(WechatServiceImpl.class);
+
+ // 微信API地址
+ private static final String WECHAT_API_URL = "https://api.weixin.qq.com/sns/jscode2session";
+
+ @Value("${wx.muhu.app-id:}")
+ private String muhuAppId;
+
+ @Value("${wx.muhu.app-secret:}")
+ private String muhuAppSecret;
+
+ @Value("${wx.vet.app-id:}")
+ private String vetAppId;
+
+ @Value("${wx.vet.app-secret:}")
+ private String vetAppSecret;
+
+ @Override
+ public JSONObject code2session(String code, String clientType) {
+ MiniProgramConfig config = getMiniProgramConfig(clientType);
+ if (config == null) {
+ throw new RuntimeException("未找到微信小程序配置");
+ }
+
+ String url = String.format("%s?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code",
+ WECHAT_API_URL, config.getAppId(), config.getAppSecret(), code);
+
+ try {
+ String response = HttpUtils.sendGet(url);
+ log.info("微信API响应: {}", response);
+
+ JSONObject result = JSONObject.parseObject(response);
+ if (result.containsKey("errcode")) {
+ throw new RuntimeException("微信API调用失败: " + result.getString("errmsg"));
+ }
+
+ return result;
+ } catch (Exception e) {
+ log.error("调用微信API失败", e);
+ throw new RuntimeException("微信登录失败: " + e.getMessage());
+ }
+ }
+
+ @Override
+ public MiniProgramConfig getMiniProgramConfig(String clientType) {
+ if ("herdsman-app".equals(clientType)) {
+ if (StringUtils.isEmpty(muhuAppId) || StringUtils.isEmpty(muhuAppSecret)) {
+ throw new RuntimeException("牧户小程序配置未设置");
+ }
+ return new MiniProgramConfig(muhuAppId, muhuAppSecret);
+ } else if ("vet-app".equals(clientType)) {
+ if (StringUtils.isEmpty(vetAppId) || StringUtils.isEmpty(vetAppSecret)) {
+ throw new RuntimeException("兽医小程序配置未设置");
+ }
+ return new MiniProgramConfig(vetAppId, vetAppSecret);
+ }
+ throw new RuntimeException("不支持的客户端类型: " + clientType);
+ }
+}
\ No newline at end of file
diff --git a/chenhai-system/src/main/java/com/chenhai/system/mapper/SysUserAuthMapper.java b/chenhai-system/src/main/java/com/chenhai/system/mapper/SysUserAuthMapper.java
new file mode 100644
index 0000000..0e8499b
--- /dev/null
+++ b/chenhai-system/src/main/java/com/chenhai/system/mapper/SysUserAuthMapper.java
@@ -0,0 +1,56 @@
+package com.chenhai.system.mapper;
+
+import com.chenhai.common.core.domain.entity.SysUserAuth;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * 用户认证方式Mapper接口
+ */
+public interface SysUserAuthMapper {
+ /**
+ * 查询用户认证方式
+ */
+ SysUserAuth selectSysUserAuthByAuthId(Long authId);
+
+ /**
+ * 根据认证类型和标识查询
+ */
+ SysUserAuth selectByAuthTypeAndKey(@Param("authType") String authType, @Param("authKey") String authKey);
+
+ /**
+ * 查询用户的所有认证方式
+ */
+ List selectByUserId(Long userId);
+
+ /**
+ * 查询用户特定类型的认证方式
+ */
+ List selectByUserIdAndType(@Param("userId") Long userId, @Param("authType") String authType);
+
+ /**
+ * 新增用户认证方式
+ */
+ int insertSysUserAuth(SysUserAuth sysUserAuth);
+
+ /**
+ * 修改用户认证方式
+ */
+ int updateSysUserAuth(SysUserAuth sysUserAuth);
+
+ /**
+ * 删除用户认证方式
+ */
+ int deleteSysUserAuthByAuthId(Long authId);
+
+ /**
+ * 批量删除用户认证方式
+ */
+ int deleteSysUserAuthByAuthIds(Long[] authIds);
+
+ /**
+ * 删除用户的所有认证方式
+ */
+ int deleteByUserId(Long userId);
+}
\ No newline at end of file
diff --git a/chenhai-system/src/main/java/com/chenhai/system/mapper/SysUserMapper.java b/chenhai-system/src/main/java/com/chenhai/system/mapper/SysUserMapper.java
index 229549b..d585b92 100644
--- a/chenhai-system/src/main/java/com/chenhai/system/mapper/SysUserMapper.java
+++ b/chenhai-system/src/main/java/com/chenhai/system/mapper/SysUserMapper.java
@@ -52,6 +52,14 @@ public interface SysUserMapper
*/
public SysUser selectUserById(Long userId);
+ /**
+ * 通过手机号查询用户
+ *
+ * @param phone 手机号
+ * @return 用户对象信息
+ */
+ public SysUser selectUserByPhone(String phone);
+
/**
* 新增用户信息
*
diff --git a/chenhai-system/src/main/java/com/chenhai/system/service/ISysUserService.java b/chenhai-system/src/main/java/com/chenhai/system/service/ISysUserService.java
index 838b0b6..a9190f4 100644
--- a/chenhai-system/src/main/java/com/chenhai/system/service/ISysUserService.java
+++ b/chenhai-system/src/main/java/com/chenhai/system/service/ISysUserService.java
@@ -67,6 +67,14 @@ public interface ISysUserService
*/
public String selectUserPostGroup(String userName);
+ /**
+ * 通过手机号码查询用户
+ *
+ * @param phone 手机号码
+ * @return 结果
+ */
+ public SysUser selectUserByPhone(String phone);
+
/**
* 校验用户名称是否唯一
*
diff --git a/chenhai-system/src/main/java/com/chenhai/system/service/impl/SysUserServiceImpl.java b/chenhai-system/src/main/java/com/chenhai/system/service/impl/SysUserServiceImpl.java
index 6239ef4..61d9c11 100644
--- a/chenhai-system/src/main/java/com/chenhai/system/service/impl/SysUserServiceImpl.java
+++ b/chenhai-system/src/main/java/com/chenhai/system/service/impl/SysUserServiceImpl.java
@@ -163,6 +163,11 @@ public class SysUserServiceImpl implements ISysUserService
return list.stream().map(SysPost::getPostName).collect(Collectors.joining(","));
}
+ @Override
+ public SysUser selectUserByPhone(String phone) {
+ return userMapper.selectUserByPhone(phone);
+ }
+
/**
* 校验用户名称是否唯一
*
diff --git a/chenhai-system/src/main/resources/mapper/system/SysUserAuthMapper.xml b/chenhai-system/src/main/resources/mapper/system/SysUserAuthMapper.xml
new file mode 100644
index 0000000..ab94f75
--- /dev/null
+++ b/chenhai-system/src/main/resources/mapper/system/SysUserAuthMapper.xml
@@ -0,0 +1,97 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ select auth_id, user_id, auth_type, auth_key, auth_secret, status, create_time, update_time
+ from sys_user_auth
+
+
+
+
+
+
+
+
+
+
+
+ insert into sys_user_auth
+
+ user_id,
+ auth_type,
+ auth_key,
+ auth_secret,
+ status,
+ create_time,
+ update_time,
+
+
+ #{userId},
+ #{authType},
+ #{authKey},
+ #{authSecret},
+ #{status},
+ #{createTime},
+ #{updateTime},
+
+
+
+
+ update sys_user_auth
+
+ user_id = #{userId},
+ auth_type = #{authType},
+ auth_key = #{authKey},
+ auth_secret = #{authSecret},
+ status = #{status},
+ create_time = #{createTime},
+ update_time = #{updateTime},
+
+ where auth_id = #{authId}
+
+
+
+ delete from sys_user_auth where auth_id = #{authId}
+
+
+
+ delete from sys_user_auth where auth_id in
+
+ #{authId}
+
+
+
+
+ delete from sys_user_auth where user_id = #{userId}
+
+
+
\ No newline at end of file
diff --git a/chenhai-system/src/main/resources/mapper/system/SysUserMapper.xml b/chenhai-system/src/main/resources/mapper/system/SysUserMapper.xml
index 69e29c1..024f901 100644
--- a/chenhai-system/src/main/resources/mapper/system/SysUserMapper.xml
+++ b/chenhai-system/src/main/resources/mapper/system/SysUserMapper.xml
@@ -130,6 +130,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
where u.user_id = #{userId}
+
+