更新go结构和uniapp
This commit is contained in:
+1
-1
@@ -18,7 +18,7 @@ maxmemory = 10485760
|
||||
# MySQL - 远程连接配置
|
||||
mysqluser = go-platform
|
||||
mysqlpass = FSmJCSJ5wk8pjjDC
|
||||
mysqlurls = 212.64.112.158:3388
|
||||
mysqlurls = 10.31.100.3:3306
|
||||
mysqldb = go-platform
|
||||
|
||||
# ORM配置
|
||||
|
||||
@@ -0,0 +1,381 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"server/models"
|
||||
"server/pkg/jwtutil"
|
||||
"server/services"
|
||||
|
||||
beego "github.com/beego/beego/v2/server/web"
|
||||
)
|
||||
|
||||
// AppAuthController App移动端认证控制器
|
||||
type AppAuthController struct {
|
||||
beego.Controller
|
||||
}
|
||||
|
||||
func (c *AppAuthController) serveJSON(data map[string]interface{}) {
|
||||
c.Data["json"] = data
|
||||
_ = c.ServeJSON()
|
||||
}
|
||||
|
||||
// LoginBackend App端登录(需要租户)
|
||||
func (c *AppAuthController) LoginBackend() {
|
||||
var req struct {
|
||||
TenantName string `json:"tenant_name"`
|
||||
Account string `json:"account"`
|
||||
Password string `json:"password"`
|
||||
Code string `json:"code"`
|
||||
}
|
||||
|
||||
body := c.Ctx.Input.RequestBody
|
||||
if len(body) == 0 {
|
||||
var err error
|
||||
body, err = io.ReadAll(c.Ctx.Request.Body)
|
||||
if err != nil {
|
||||
c.serveJSON(map[string]interface{}{"code": 400, "msg": "参数错误"})
|
||||
return
|
||||
}
|
||||
}
|
||||
if len(body) == 0 {
|
||||
c.serveJSON(map[string]interface{}{"code": 400, "msg": "参数错误"})
|
||||
return
|
||||
}
|
||||
if err := json.Unmarshal(body, &req); err != nil {
|
||||
c.serveJSON(map[string]interface{}{"code": 400, "msg": "参数错误"})
|
||||
return
|
||||
}
|
||||
|
||||
req.TenantName = strings.TrimSpace(req.TenantName)
|
||||
req.Account = strings.TrimSpace(req.Account)
|
||||
req.Password = strings.TrimSpace(req.Password)
|
||||
if req.TenantName == "" || req.Account == "" || req.Password == "" {
|
||||
c.serveJSON(map[string]interface{}{"code": 400, "msg": "租户名称、用户名或密码不能为空"})
|
||||
return
|
||||
}
|
||||
|
||||
token, loginUser, err := services.BackendLogin(req.TenantName, req.Account, req.Password)
|
||||
if err != nil {
|
||||
c.serveJSON(map[string]interface{}{"code": 401, "msg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.serveJSON(map[string]interface{}{
|
||||
"code": 200,
|
||||
"msg": "登录成功",
|
||||
"data": map[string]interface{}{
|
||||
"token": token,
|
||||
"user": map[string]interface{}{
|
||||
"id": loginUser.ID,
|
||||
"account": loginUser.Account,
|
||||
"name": loginUser.Name,
|
||||
"tid": loginUser.Tid,
|
||||
"rid": loginUser.Rid,
|
||||
"avatar": loginUser.Avatar,
|
||||
"role_name": loginUser.RoleName,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// GetCurrentUser App端当前登录用户信息,需 Bearer Token
|
||||
func (c *AppAuthController) GetCurrentUser() {
|
||||
authHeader := c.Ctx.Request.Header.Get("Authorization")
|
||||
if authHeader == "" {
|
||||
c.serveJSON(map[string]interface{}{"code": 401, "msg": "未登录"})
|
||||
return
|
||||
}
|
||||
authParts := strings.SplitN(authHeader, " ", 2)
|
||||
if len(authParts) != 2 || authParts[0] != "Bearer" {
|
||||
c.serveJSON(map[string]interface{}{"code": 401, "msg": "认证信息格式错误"})
|
||||
return
|
||||
}
|
||||
claims, err := jwtutil.ParseToken(authParts[1])
|
||||
if err != nil {
|
||||
c.serveJSON(map[string]interface{}{"code": 401, "msg": "无效的token"})
|
||||
return
|
||||
}
|
||||
if claims.UserType != "backend" && claims.UserType != "app" {
|
||||
c.serveJSON(map[string]interface{}{"code": 403, "msg": "无权访问"})
|
||||
return
|
||||
}
|
||||
|
||||
var tenantUser models.SystemTenantUser
|
||||
err = models.Orm.QueryTable(new(models.SystemTenantUser)).
|
||||
Filter("uid", claims.UserID).
|
||||
Filter("tid", claims.TenantId).
|
||||
One(&tenantUser)
|
||||
if err != nil {
|
||||
c.serveJSON(map[string]interface{}{"code": 401, "msg": "用户不存在"})
|
||||
return
|
||||
}
|
||||
if tenantUser.Status == 0 {
|
||||
c.serveJSON(map[string]interface{}{"code": 401, "msg": "账号已禁用"})
|
||||
return
|
||||
}
|
||||
|
||||
account := ""
|
||||
if tenantUser.Account != nil {
|
||||
account = strings.TrimSpace(*tenantUser.Account)
|
||||
}
|
||||
name := ""
|
||||
if tenantUser.Name != nil {
|
||||
name = strings.TrimSpace(*tenantUser.Name)
|
||||
}
|
||||
|
||||
c.serveJSON(map[string]interface{}{
|
||||
"code": 200,
|
||||
"msg": "success",
|
||||
"data": map[string]interface{}{
|
||||
"id": tenantUser.Uid,
|
||||
"account": account,
|
||||
"name": name,
|
||||
"tid": tenantUser.Tid,
|
||||
"rid": 0,
|
||||
"avatar": "",
|
||||
"role_name": "",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// SendLoginCode 发送App端登录验证码
|
||||
func (c *AppAuthController) SendLoginCode() {
|
||||
var req struct {
|
||||
Account string `json:"account"`
|
||||
TenantName string `json:"tenant_name"`
|
||||
Channel string `json:"channel"`
|
||||
}
|
||||
body := c.Ctx.Input.RequestBody
|
||||
if len(body) == 0 {
|
||||
var err error
|
||||
body, err = io.ReadAll(c.Ctx.Request.Body)
|
||||
if err != nil {
|
||||
c.serveJSON(map[string]interface{}{"code": 400, "msg": "参数错误"})
|
||||
return
|
||||
}
|
||||
}
|
||||
if err := json.Unmarshal(body, &req); err != nil {
|
||||
c.serveJSON(map[string]interface{}{"code": 400, "msg": "参数错误"})
|
||||
return
|
||||
}
|
||||
|
||||
cfg, _ := models.GetPlatformLoginVerify()
|
||||
if cfg.OpenVerifyEnabled != 1 {
|
||||
c.serveJSON(map[string]interface{}{"code": 400, "msg": "当前未开启验证"})
|
||||
return
|
||||
}
|
||||
channel := strings.TrimSpace(req.Channel)
|
||||
if channel == "" {
|
||||
channel = cfg.VerifyType
|
||||
}
|
||||
if channel != "sms" && channel != "email" {
|
||||
c.serveJSON(map[string]interface{}{"code": 400, "msg": "仅支持短信/邮箱验证码"})
|
||||
return
|
||||
}
|
||||
if err := services.SendBackendLoginCode(req.TenantName, req.Account, channel); err != nil {
|
||||
c.serveJSON(map[string]interface{}{"code": 400, "msg": err.Error()})
|
||||
return
|
||||
}
|
||||
c.serveJSON(map[string]interface{}{"code": 200, "msg": "验证码已发送"})
|
||||
}
|
||||
|
||||
// LoginBySms App端手机号验证码登录(占位实现)
|
||||
func (c *AppAuthController) LoginBySms() {
|
||||
c.serveJSON(map[string]interface{}{
|
||||
"code": 501,
|
||||
"msg": "手机号验证码登录暂未实现",
|
||||
})
|
||||
}
|
||||
|
||||
// Logout App端退出登录(当前为无状态直接返回成功)
|
||||
func (c *AppAuthController) Logout() {
|
||||
c.serveJSON(map[string]interface{}{
|
||||
"code": 200,
|
||||
"msg": "退出成功",
|
||||
})
|
||||
}
|
||||
|
||||
// GetGeetest3Infos 获取App端极验3.0配置
|
||||
func (c *AppAuthController) GetGeetest3Infos() {
|
||||
cfg, _ := models.GetPlatformLoginVerify()
|
||||
if cfg.Geetest3ID == nil || cfg.Geetest3Key == nil {
|
||||
c.serveJSON(map[string]interface{}{"code": 404, "msg": "未配置极验3参数"})
|
||||
return
|
||||
}
|
||||
c.serveJSON(map[string]interface{}{
|
||||
"code": 200,
|
||||
"msg": "success",
|
||||
"data": map[string]interface{}{
|
||||
"captcha_id": *cfg.Geetest3ID,
|
||||
"captcha_key": *cfg.Geetest3Key,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// GetGeetest4Infos 获取App端极验4.0配置
|
||||
func (c *AppAuthController) GetGeetest4Infos() {
|
||||
cfg, _ := models.GetPlatformLoginVerify()
|
||||
if cfg.Geetest4ID == nil || cfg.Geetest4Key == nil {
|
||||
c.serveJSON(map[string]interface{}{"code": 404, "msg": "未配置极验4参数"})
|
||||
return
|
||||
}
|
||||
c.serveJSON(map[string]interface{}{
|
||||
"code": 200,
|
||||
"msg": "success",
|
||||
"data": map[string]interface{}{
|
||||
"captcha_id": *cfg.Geetest4ID,
|
||||
"captcha_key": *cfg.Geetest4Key,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// GetOpenVerify 判断是否开启App端登录验证
|
||||
func (c *AppAuthController) GetOpenVerify() {
|
||||
cfg, _ := models.GetPlatformLoginVerify()
|
||||
openVerify := "0"
|
||||
if cfg.OpenVerifyEnabled == 1 {
|
||||
openVerify = "1"
|
||||
}
|
||||
c.serveJSON(map[string]interface{}{
|
||||
"code": 200,
|
||||
"msg": "ok",
|
||||
"data": []map[string]string{
|
||||
{
|
||||
"label": "openVerify",
|
||||
"value": openVerify,
|
||||
},
|
||||
{
|
||||
"label": "verifyType",
|
||||
"value": cfg.VerifyType,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// VerifyAccount 验证租户和账号是否存在(忘记密码第一步)
|
||||
func (c *AppAuthController) VerifyAccount() {
|
||||
var req struct {
|
||||
TenantName string `json:"tenant_name"`
|
||||
Account string `json:"account"`
|
||||
}
|
||||
body := c.Ctx.Input.RequestBody
|
||||
if len(body) == 0 {
|
||||
var err error
|
||||
body, err = io.ReadAll(c.Ctx.Request.Body)
|
||||
if err != nil {
|
||||
c.serveJSON(map[string]interface{}{"code": 400, "msg": "参数错误"})
|
||||
return
|
||||
}
|
||||
}
|
||||
if err := json.Unmarshal(body, &req); err != nil {
|
||||
c.serveJSON(map[string]interface{}{"code": 400, "msg": "参数错误"})
|
||||
return
|
||||
}
|
||||
|
||||
phone, email, err := services.VerifyTenantAccount(req.TenantName, req.Account)
|
||||
if err != nil {
|
||||
c.serveJSON(map[string]interface{}{"code": 400, "msg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.serveJSON(map[string]interface{}{
|
||||
"code": 200,
|
||||
"msg": "验证成功",
|
||||
"data": map[string]interface{}{
|
||||
"phone": phone,
|
||||
"email": email,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// SendResetCode 发送找回密码验证码(忘记密码第二步)
|
||||
func (c *AppAuthController) SendResetCode() {
|
||||
var req struct {
|
||||
TenantName string `json:"tenant_name"`
|
||||
Account string `json:"account"`
|
||||
Phone string `json:"phone"`
|
||||
Channel string `json:"channel"`
|
||||
}
|
||||
body := c.Ctx.Input.RequestBody
|
||||
if len(body) == 0 {
|
||||
var err error
|
||||
body, err = io.ReadAll(c.Ctx.Request.Body)
|
||||
if err != nil {
|
||||
c.serveJSON(map[string]interface{}{"code": 400, "msg": "参数错误"})
|
||||
return
|
||||
}
|
||||
}
|
||||
if err := json.Unmarshal(body, &req); err != nil {
|
||||
c.serveJSON(map[string]interface{}{"code": 400, "msg": "参数错误"})
|
||||
return
|
||||
}
|
||||
|
||||
channel := strings.TrimSpace(req.Channel)
|
||||
if channel == "" {
|
||||
channel = "sms"
|
||||
}
|
||||
|
||||
if err := services.SendResetCode(req.TenantName, req.Account, req.Phone, channel); err != nil {
|
||||
c.serveJSON(map[string]interface{}{"code": 400, "msg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.serveJSON(map[string]interface{}{
|
||||
"code": 200,
|
||||
"msg": "验证码已发送",
|
||||
})
|
||||
}
|
||||
|
||||
// ResetPassword 重置密码(忘记密码第三步)
|
||||
func (c *AppAuthController) ResetPassword() {
|
||||
var req struct {
|
||||
TenantName string `json:"tenant_name"`
|
||||
Account string `json:"account"`
|
||||
Phone string `json:"phone"`
|
||||
SmsCode string `json:"sms_code"`
|
||||
NewPassword string `json:"new_password"`
|
||||
ConfirmPassword string `json:"confirm_password"`
|
||||
}
|
||||
body := c.Ctx.Input.RequestBody
|
||||
if len(body) == 0 {
|
||||
var err error
|
||||
body, err = io.ReadAll(c.Ctx.Request.Body)
|
||||
if err != nil {
|
||||
c.serveJSON(map[string]interface{}{"code": 400, "msg": "参数错误"})
|
||||
return
|
||||
}
|
||||
}
|
||||
if err := json.Unmarshal(body, &req); err != nil {
|
||||
c.serveJSON(map[string]interface{}{"code": 400, "msg": "参数错误"})
|
||||
return
|
||||
}
|
||||
|
||||
if err := services.ResetPassword(req.TenantName, req.Account, req.Phone, req.SmsCode, req.NewPassword, req.ConfirmPassword); err != nil {
|
||||
c.serveJSON(map[string]interface{}{"code": 400, "msg": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.serveJSON(map[string]interface{}{
|
||||
"code": 200,
|
||||
"msg": "密码重置成功",
|
||||
})
|
||||
}
|
||||
|
||||
// Register App端注册(占位实现)
|
||||
func (c *AppAuthController) Register() {
|
||||
c.serveJSON(map[string]interface{}{
|
||||
"code": 501,
|
||||
"msg": "注册暂未实现",
|
||||
})
|
||||
}
|
||||
|
||||
// SendRegisterCode 发送App端注册验证码(占位实现)
|
||||
func (c *AppAuthController) SendRegisterCode() {
|
||||
c.serveJSON(map[string]interface{}{
|
||||
"code": 501,
|
||||
"msg": "发送注册验证码暂未实现",
|
||||
})
|
||||
}
|
||||
@@ -67,7 +67,7 @@ func (c *BackendAuthController) LoginBackend() {
|
||||
|
||||
cfg, _ := models.GetPlatformLoginVerify()
|
||||
if cfg.OpenVerifyEnabled == 1 {
|
||||
if cfg.VerifyType == "geetest4" {
|
||||
if cfg.VerifyType == "geetest4" || cfg.VerifyType == "geetest" {
|
||||
if req.LotNumber == "" || req.PassToken == "" || req.GenTime == "" || req.CaptchaOutput == "" {
|
||||
c.serveJSON(map[string]interface{}{"code": 400, "msg": "请完成人机验证"})
|
||||
return
|
||||
@@ -319,4 +319,4 @@ func (c *BackendAuthController) SendResetCode() {
|
||||
"code": 501,
|
||||
"msg": "发送找回密码验证码暂未实现",
|
||||
})
|
||||
}
|
||||
}
|
||||
+15
-14
@@ -8,25 +8,26 @@ import (
|
||||
|
||||
// Register 注册移动端(app)路由。
|
||||
func Register() {
|
||||
// 登录相关(复用 BackendAuthController)
|
||||
beego.Router("/app/login", &controllers.BackendAuthController{}, "post:LoginBackend")
|
||||
beego.Router("/app/sendLoginCode", &controllers.BackendAuthController{}, "post:SendLoginCode")
|
||||
beego.Router("/app/loginBySms", &controllers.BackendAuthController{}, "post:LoginBySms")
|
||||
beego.Router("/app/logout", &controllers.BackendAuthController{}, "post:Logout")
|
||||
// 登录相关(使用 AppAuthController)
|
||||
beego.Router("/app/login", &controllers.AppAuthController{}, "post:LoginBackend")
|
||||
beego.Router("/app/sendLoginCode", &controllers.AppAuthController{}, "post:SendLoginCode")
|
||||
beego.Router("/app/loginBySms", &controllers.AppAuthController{}, "post:LoginBySms")
|
||||
beego.Router("/app/logout", &controllers.AppAuthController{}, "post:Logout")
|
||||
|
||||
// 当前用户信息
|
||||
beego.Router("/app/currentUser", &controllers.BackendAuthController{}, "get:GetCurrentUser")
|
||||
beego.Router("/app/currentUser", &controllers.AppAuthController{}, "get:GetCurrentUser")
|
||||
|
||||
// 极验与登录验证配置
|
||||
beego.Router("/app/login/getGeetest3Infos", &controllers.BackendAuthController{}, "get:GetGeetest3Infos")
|
||||
beego.Router("/app/login/getGeetest4Infos", &controllers.BackendAuthController{}, "get:GetGeetest4Infos")
|
||||
beego.Router("/app/login/getOpenVerify", &controllers.BackendAuthController{}, "get:GetOpenVerify")
|
||||
beego.Router("/app/login/getGeetest3Infos", &controllers.AppAuthController{}, "get:GetGeetest3Infos")
|
||||
beego.Router("/app/login/getGeetest4Infos", &controllers.AppAuthController{}, "get:GetGeetest4Infos")
|
||||
beego.Router("/app/login/getOpenVerify", &controllers.AppAuthController{}, "get:GetOpenVerify")
|
||||
|
||||
// 找回密码
|
||||
beego.Router("/app/resetPassword", &controllers.BackendAuthController{}, "post:ResetPassword")
|
||||
beego.Router("/app/sendResetCode", &controllers.BackendAuthController{}, "post:SendResetCode")
|
||||
// 找回密码(三步流程)
|
||||
beego.Router("/app/verifyAccount", &controllers.AppAuthController{}, "post:VerifyAccount")
|
||||
beego.Router("/app/sendResetCode", &controllers.AppAuthController{}, "post:SendResetCode")
|
||||
beego.Router("/app/resetPassword", &controllers.AppAuthController{}, "post:ResetPassword")
|
||||
|
||||
// 注册
|
||||
beego.Router("/app/register", &controllers.BackendAuthController{}, "post:Register")
|
||||
beego.Router("/app/sendRegisterCode", &controllers.BackendAuthController{}, "post:SendRegisterCode")
|
||||
beego.Router("/app/register", &controllers.AppAuthController{}, "post:Register")
|
||||
beego.Router("/app/sendRegisterCode", &controllers.AppAuthController{}, "post:SendRegisterCode")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,260 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"server/models"
|
||||
"server/pkg/passwordutil"
|
||||
)
|
||||
|
||||
// resetCodeItem 存储找回密码的验证码
|
||||
type resetCodeItem struct {
|
||||
Code string
|
||||
Channel string
|
||||
ExpiredAt time.Time
|
||||
}
|
||||
|
||||
var resetCodeStore sync.Map
|
||||
|
||||
// resetCodeKey 生成密码重置验证码的存储key
|
||||
func resetCodeKey(tenantName, account, phone, channel string) string {
|
||||
key := strings.ToLower(strings.TrimSpace(tenantName)) + "|" +
|
||||
strings.ToLower(strings.TrimSpace(account)) + "|" +
|
||||
strings.ToLower(strings.TrimSpace(phone)) + "|" +
|
||||
strings.TrimSpace(channel)
|
||||
return key
|
||||
}
|
||||
|
||||
// VerifyTenantAccount 第一步:验证租户和账号是否存在
|
||||
// 返回该账号关联的手机号和邮箱(用于第二步选择验证方式)
|
||||
func VerifyTenantAccount(tenantName, account string) (phone, email string, err error) {
|
||||
tenantName = strings.TrimSpace(tenantName)
|
||||
account = strings.TrimSpace(account)
|
||||
|
||||
if tenantName == "" || account == "" {
|
||||
return "", "", errors.New("租户名称和账号不能为空")
|
||||
}
|
||||
|
||||
// 验证租户是否存在
|
||||
var tenant models.SystemTenant
|
||||
if err := models.Orm.QueryTable(new(models.SystemTenant)).
|
||||
Filter("tenant_name", tenantName).
|
||||
One(&tenant); err != nil {
|
||||
return "", "", errors.New("租户不存在")
|
||||
}
|
||||
if tenant.Status != 1 {
|
||||
return "", "", errors.New("租户已停用")
|
||||
}
|
||||
|
||||
// 验证该租户下的账号是否存在
|
||||
var tenantUser models.SystemTenantUser
|
||||
if err := models.Orm.QueryTable(new(models.SystemTenantUser)).
|
||||
Filter("tid", tenant.ID).
|
||||
Filter("account", account).
|
||||
One(&tenantUser); err != nil {
|
||||
return "", "", errors.New("账号不存在")
|
||||
}
|
||||
if tenantUser.Status == 0 {
|
||||
return "", "", errors.New("账号已禁用")
|
||||
}
|
||||
|
||||
// 返回该账号的手机号和邮箱
|
||||
phoneStr := ""
|
||||
if tenantUser.Phone != nil {
|
||||
phoneStr = strings.TrimSpace(*tenantUser.Phone)
|
||||
}
|
||||
emailStr := ""
|
||||
if tenantUser.Email != nil {
|
||||
emailStr = strings.TrimSpace(*tenantUser.Email)
|
||||
}
|
||||
|
||||
if phoneStr == "" && emailStr == "" {
|
||||
return "", "", errors.New("账号未绑定手机号或邮箱,无法重置密码")
|
||||
}
|
||||
|
||||
return phoneStr, emailStr, nil
|
||||
}
|
||||
|
||||
// SendResetCode 第二步:验证手机号并发送验证码
|
||||
func SendResetCode(tenantName, account, phone, channel string) error {
|
||||
tenantName = strings.TrimSpace(tenantName)
|
||||
account = strings.TrimSpace(account)
|
||||
phone = strings.TrimSpace(phone)
|
||||
channel = strings.TrimSpace(channel)
|
||||
|
||||
if tenantName == "" || account == "" || phone == "" {
|
||||
return errors.New("租户名称、账号和手机号不能为空")
|
||||
}
|
||||
if channel != "sms" && channel != "email" {
|
||||
return errors.New("仅支持短信或邮箱验证码")
|
||||
}
|
||||
|
||||
// 验证租户是否存在
|
||||
var tenant models.SystemTenant
|
||||
if err := models.Orm.QueryTable(new(models.SystemTenant)).
|
||||
Filter("tenant_name", tenantName).
|
||||
One(&tenant); err != nil {
|
||||
return errors.New("租户不存在")
|
||||
}
|
||||
|
||||
// 验证该租户下的账号和手机号是否匹配
|
||||
var tenantUser models.SystemTenantUser
|
||||
if err := models.Orm.QueryTable(new(models.SystemTenantUser)).
|
||||
Filter("tid", tenant.ID).
|
||||
Filter("account", account).
|
||||
One(&tenantUser); err != nil {
|
||||
return errors.New("账号不存在")
|
||||
}
|
||||
if tenantUser.Status == 0 {
|
||||
return errors.New("账号已禁用")
|
||||
}
|
||||
|
||||
// 根据验证渠道验证用户信息
|
||||
if channel == "sms" {
|
||||
if tenantUser.Phone == nil || strings.TrimSpace(*tenantUser.Phone) != phone {
|
||||
return errors.New("手机号不匹配,请确认您输入的手机号正确")
|
||||
}
|
||||
} else if channel == "email" {
|
||||
if tenantUser.Email == nil || strings.TrimSpace(*tenantUser.Email) != phone {
|
||||
return errors.New("邮箱不匹配,请确认您输入的邮箱正确")
|
||||
}
|
||||
}
|
||||
|
||||
// 生成验证码
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
code := fmt.Sprintf("%06d", rand.Intn(1000000))
|
||||
|
||||
// 发送验证码
|
||||
if channel == "sms" {
|
||||
content := "密码重置验证码:" + code
|
||||
if err := enqueueSMSTaskForPasswordReset(tenant.ID, phone, content, code); err != nil {
|
||||
return errors.New("短信发送失败,请重试")
|
||||
}
|
||||
}
|
||||
// TODO: 实现邮箱验证码发送逻辑
|
||||
|
||||
// 存储验证码(5分钟有效期)
|
||||
resetCodeStore.Store(resetCodeKey(tenantName, account, phone, channel), resetCodeItem{
|
||||
Code: code,
|
||||
Channel: channel,
|
||||
ExpiredAt: time.Now().Add(5 * time.Minute),
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// VerifyResetCode 验证重置密码的验证码
|
||||
func VerifyResetCode(tenantName, account, phone, channel, code string) error {
|
||||
tenantName = strings.TrimSpace(tenantName)
|
||||
account = strings.TrimSpace(account)
|
||||
phone = strings.TrimSpace(phone)
|
||||
channel = strings.TrimSpace(channel)
|
||||
code = strings.TrimSpace(code)
|
||||
|
||||
if code == "" {
|
||||
return errors.New("验证码不能为空")
|
||||
}
|
||||
|
||||
key := resetCodeKey(tenantName, account, phone, channel)
|
||||
val, ok := resetCodeStore.Load(key)
|
||||
if !ok {
|
||||
return errors.New("验证码不存在或已失效")
|
||||
}
|
||||
|
||||
item, ok := val.(resetCodeItem)
|
||||
if !ok {
|
||||
return errors.New("验证码状态异常")
|
||||
}
|
||||
|
||||
if time.Now().After(item.ExpiredAt) {
|
||||
resetCodeStore.Delete(key)
|
||||
return errors.New("验证码已过期")
|
||||
}
|
||||
|
||||
if item.Code != code {
|
||||
return errors.New("验证码错误")
|
||||
}
|
||||
|
||||
// 验证通过后删除验证码
|
||||
resetCodeStore.Delete(key)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ResetPassword 第三步:重置密码
|
||||
func ResetPassword(tenantName, account, phone, smsCode, newPassword, confirmPassword string) error {
|
||||
tenantName = strings.TrimSpace(tenantName)
|
||||
account = strings.TrimSpace(account)
|
||||
phone = strings.TrimSpace(phone)
|
||||
smsCode = strings.TrimSpace(smsCode)
|
||||
newPassword = strings.TrimSpace(newPassword)
|
||||
confirmPassword = strings.TrimSpace(confirmPassword)
|
||||
|
||||
if tenantName == "" || account == "" || phone == "" {
|
||||
return errors.New("租户名称、账号和手机号不能为空")
|
||||
}
|
||||
if newPassword == "" {
|
||||
return errors.New("新密码不能为空")
|
||||
}
|
||||
if newPassword != confirmPassword {
|
||||
return errors.New("两次密码不一致")
|
||||
}
|
||||
if len(newPassword) < 6 {
|
||||
return errors.New("密码长度不能少于6个字符")
|
||||
}
|
||||
|
||||
// 验证验证码(验证码验证后会被删除)
|
||||
if err := VerifyResetCode(tenantName, account, phone, "sms", smsCode); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 验证租户
|
||||
var tenant models.SystemTenant
|
||||
if err := models.Orm.QueryTable(new(models.SystemTenant)).
|
||||
Filter("tenant_name", tenantName).
|
||||
One(&tenant); err != nil {
|
||||
return errors.New("租户不存在")
|
||||
}
|
||||
|
||||
// 查询用户
|
||||
var tenantUser models.SystemTenantUser
|
||||
if err := models.Orm.QueryTable(new(models.SystemTenantUser)).
|
||||
Filter("tid", tenant.ID).
|
||||
Filter("account", account).
|
||||
One(&tenantUser); err != nil {
|
||||
return errors.New("账号不存在")
|
||||
}
|
||||
|
||||
// 验证手机号
|
||||
if tenantUser.Phone == nil || strings.TrimSpace(*tenantUser.Phone) != phone {
|
||||
return errors.New("手机号不匹配")
|
||||
}
|
||||
|
||||
// 哈希新密码
|
||||
hashedPassword, err := passwordutil.Hash(newPassword)
|
||||
if err != nil {
|
||||
return errors.New("密码处理失败")
|
||||
}
|
||||
|
||||
// 更新密码
|
||||
_, err = models.Orm.QueryTable(new(models.SystemTenantUser)).
|
||||
Filter("id", tenantUser.ID).
|
||||
Update(map[string]interface{}{
|
||||
"password": hashedPassword,
|
||||
})
|
||||
if err != nil {
|
||||
return errors.New("密码更新失败")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// enqueueSMSTaskForPasswordReset 发送密码重置短信任务
|
||||
func enqueueSMSTaskForPasswordReset(tid uint64, phone, content, code string) error {
|
||||
// 重用已有的短信发送逻辑
|
||||
return enqueueSMSTaskForLogin(tid, phone, content, code)
|
||||
}
|
||||
BIN
Binary file not shown.
Reference in New Issue
Block a user