From 106abeeff6f70b0826c512f2e017ea90fac9420d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=BF=97=E5=BC=BA?= <357099073@qq.com> Date: Wed, 1 Apr 2026 16:41:41 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- controllers/platform_admin_user.go | 15 ++-- controllers/platform_auth.go | 30 +++++-- controllers/platform_tenant_user.go | 25 ++++-- controllers/platform_user.go | 14 +++- middleware/permission.go | 4 +- models/admin_user.go | 72 +--------------- models/tenant_user.go | 79 ------------------ pkg/passwordutil/password.go | 55 ++++++++++++ routers/backend/backend.go | 26 ++++++ routers/router.go | 2 + services/admin_user.go | 73 ++++++++++++++++ .../permission.go | 13 +-- services/platform_auth.go | 60 ++++++++++---- services/tenant_user.go | 83 +++++++++++++++++++ 14 files changed, 357 insertions(+), 194 deletions(-) create mode 100644 pkg/passwordutil/password.go create mode 100644 services/admin_user.go rename models/permission_check.go => services/permission.go (76%) create mode 100644 services/tenant_user.go diff --git a/controllers/platform_admin_user.go b/controllers/platform_admin_user.go index 5f22f20..42016e2 100644 --- a/controllers/platform_admin_user.go +++ b/controllers/platform_admin_user.go @@ -7,6 +7,7 @@ import ( "strings" "server/models" + "server/services" beego "github.com/beego/beego/v2/server/web" ) @@ -60,7 +61,7 @@ func toAdminUserDTO(u models.AdminUser) adminUserDTO { // GetAllUsers 获取全部平台管理员用户 // GET /platform/getAllUsers func (c *PlatformAdminUserController) GetAllUsers() { - rows, total, err := models.ListAdminUsers() + rows, total, err := services.ListAdminUsers() if err != nil { c.Data["json"] = map[string]interface{}{"code": 500, "msg": "查询失败"} _ = c.ServeJSON() @@ -88,7 +89,7 @@ func (c *PlatformAdminUserController) GetUserInfo() { _ = c.ServeJSON() return } - u, err := models.GetAdminUserByID(id) + u, err := services.GetAdminUserByID(id) if err != nil { c.Data["json"] = map[string]interface{}{"code": 404, "msg": "用户不存在"} _ = c.ServeJSON() @@ -151,7 +152,7 @@ func (c *PlatformAdminUserController) AddUser() { roleID = *p.Rid } - id, err := models.CreateAdminUser(p.Account, p.Password, p.Name, p.Phone, p.Email, p.Qq, p.Avatar, sex, roleID, status) + id, err := services.CreateAdminUser(p.Account, p.Password, p.Name, p.Phone, p.Email, p.Qq, p.Avatar, sex, roleID, status) if err != nil { c.Data["json"] = map[string]interface{}{"code": 500, "msg": "添加失败"} _ = c.ServeJSON() @@ -230,14 +231,14 @@ func (c *PlatformAdminUserController) EditUser() { } if len(fields) > 0 { - if err := models.UpdateAdminUser(id, fields); err != nil { + if err := services.UpdateAdminUser(id, fields); err != nil { c.Data["json"] = map[string]interface{}{"code": 500, "msg": "编辑失败"} _ = c.ServeJSON() return } } if p.Password != nil && strings.TrimSpace(*p.Password) != "" { - if err := models.ChangeAdminUserPassword(id, *p.Password); err != nil { + if err := services.ChangeAdminUserPassword(id, *p.Password); err != nil { c.Data["json"] = map[string]interface{}{"code": 500, "msg": "密码修改失败"} _ = c.ServeJSON() return @@ -258,7 +259,7 @@ func (c *PlatformAdminUserController) DeleteUser() { _ = c.ServeJSON() return } - if err := models.DeleteAdminUser(id); err != nil { + if err := services.DeleteAdminUser(id); err != nil { c.Data["json"] = map[string]interface{}{"code": 500, "msg": "删除失败"} _ = c.ServeJSON() return @@ -292,7 +293,7 @@ func (c *PlatformAdminUserController) ChangePassword() { _ = c.ServeJSON() return } - if err := models.ChangeAdminUserPassword(p.ID, p.Password); err != nil { + if err := services.ChangeAdminUserPassword(p.ID, p.Password); err != nil { c.Data["json"] = map[string]interface{}{"code": 500, "msg": "修改失败"} _ = c.ServeJSON() return diff --git a/controllers/platform_auth.go b/controllers/platform_auth.go index 46af65a..02f6347 100644 --- a/controllers/platform_auth.go +++ b/controllers/platform_auth.go @@ -12,8 +12,9 @@ import ( ) type platformLoginRequest struct { - Account string `json:"account"` - Password string `json:"password"` + TenantName string `json:"tenant_name"` + Account string `json:"account"` + Password string `json:"password"` } // PlatformAuthController 平台端认证控制器 @@ -45,17 +46,17 @@ func (c *PlatformAuthController) Login() { return } - if req.Account == "" || req.Password == "" { + if req.TenantName == "" || req.Account == "" || req.Password == "" { c.Data["json"] = map[string]interface{}{ "code": 400, - "msg": "用户名或密码不能为空", + "msg": "租户名称、用户名或密码不能为空", } _ = c.ServeJSON() return } // 控制器只做 HTTP 解析与响应编排,业务逻辑放 services 层 - token, loginUser, err := services.PlatformLogin(req.Account, req.Password) + token, loginUser, err := services.PlatformLogin(req.TenantName, req.Account, req.Password) if err != nil { c.Data["json"] = map[string]interface{}{ "code": 401, @@ -74,6 +75,7 @@ func (c *PlatformAuthController) Login() { "id": loginUser.ID, "account": loginUser.Account, "name": loginUser.Name, + "tid": loginUser.Tid, "rid": loginUser.Rid, "avatar": loginUser.Avatar, "role_name": loginUser.RoleName, @@ -190,6 +192,24 @@ func (c *PlatformAuthController) GetOpenVerify() { _ = c.ServeJSON() } +// Register 注册(占位实现) +func (c *PlatformAuthController) Register() { + c.Data["json"] = map[string]interface{}{ + "code": 501, + "msg": "注册暂未实现", + } + _ = c.ServeJSON() +} + +// SendRegisterCode 发送注册验证码(占位实现) +func (c *PlatformAuthController) SendRegisterCode() { + c.Data["json"] = map[string]interface{}{ + "code": 501, + "msg": "发送注册验证码暂未实现", + } + _ = c.ServeJSON() +} + // ResetPassword 忘记密码重置(占位实现) func (c *PlatformAuthController) ResetPassword() { c.Data["json"] = map[string]interface{}{ diff --git a/controllers/platform_tenant_user.go b/controllers/platform_tenant_user.go index 6700807..e56e2b2 100644 --- a/controllers/platform_tenant_user.go +++ b/controllers/platform_tenant_user.go @@ -10,6 +10,8 @@ import ( "time" "server/models" + "server/pkg/passwordutil" + "server/services" "github.com/beego/beego/v2/client/orm" beego "github.com/beego/beego/v2/server/web" @@ -166,6 +168,13 @@ func (c *PlatformTenantUserController) CreateTenantUser() { _ = c.ServeJSON() return } + hashed, err := passwordutil.Hash(*p.Password) + if err != nil { + c.Data["json"] = map[string]interface{}{"code": 400, "msg": err.Error()} + _ = c.ServeJSON() + return + } + p.Password = &hashed if p.Uid == 0 { uid, err := generateTenantUID(p.Tid) if err != nil { @@ -185,14 +194,14 @@ func (c *PlatformTenantUserController) CreateTenantUser() { status = *p.Status } - id, err := models.BindTenantUser(p.Tid, p.Uid, p.Account, p.Name, p.Phone, p.Email, p.Password, isDefault, status, p.Remark) + id, err := services.BindTenantUser(p.Tid, p.Uid, p.Account, p.Name, p.Phone, p.Email, p.Password, isDefault, status, p.Remark) if err != nil { c.Data["json"] = map[string]interface{}{"code": 500, "msg": "创建失败: " + err.Error()} _ = c.ServeJSON() return } if isDefault == 1 { - _ = models.SetDefaultTenant(p.Uid, p.Tid) + _ = services.SetDefaultTenant(p.Uid, p.Tid) } c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": map[string]interface{}{"id": id}} @@ -234,7 +243,13 @@ func (c *PlatformTenantUserController) EditTenantUser() { update["email"] = p.Email } if p.Password != nil { - update["password"] = p.Password + hashed, err := passwordutil.Hash(*p.Password) + if err != nil { + c.Data["json"] = map[string]interface{}{"code": 400, "msg": err.Error()} + _ = c.ServeJSON() + return + } + update["password"] = hashed } if p.IsDefault != nil { update["is_default"] = *p.IsDefault @@ -260,7 +275,7 @@ func (c *PlatformTenantUserController) EditTenantUser() { } if p.IsDefault != nil && *p.IsDefault == 1 && p.Uid > 0 && p.Tid > 0 { - _ = models.SetDefaultTenant(p.Uid, p.Tid) + _ = services.SetDefaultTenant(p.Uid, p.Tid) } c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success"} @@ -277,7 +292,7 @@ func (c *PlatformTenantUserController) DeleteTenantUser() { return } - if err := models.UnbindTenantUser(id); err != nil { + if err := services.UnbindTenantUser(id); err != nil { c.Data["json"] = map[string]interface{}{"code": 500, "msg": "删除失败: " + err.Error()} _ = c.ServeJSON() return diff --git a/controllers/platform_user.go b/controllers/platform_user.go index 9d05d77..57bf91b 100644 --- a/controllers/platform_user.go +++ b/controllers/platform_user.go @@ -7,7 +7,8 @@ import ( "strings" "time" - "server/models" + "server/pkg/passwordutil" + "server/services" beego "github.com/beego/beego/v2/server/web" ) @@ -62,6 +63,12 @@ func (c *PlatformUserController) AddUser() { _ = c.ServeJSON() return } + hashed, err := passwordutil.Hash(p.Password) + if err != nil { + c.Data["json"] = map[string]interface{}{"code": 400, "msg": err.Error()} + _ = c.ServeJSON() + return + } status := int8(1) if p.Status != nil { @@ -78,9 +85,10 @@ func (c *PlatformUserController) AddUser() { name := &p.Name phone := &p.Phone email := &p.Email - password := &p.Password + hashedPwd := hashed + password := &hashedPwd - _, err := models.BindTenantUser(p.Tid, uid, account, name, phone, email, password, 0, status, p.Remark) + _, err := services.BindTenantUser(p.Tid, uid, account, name, phone, email, password, 0, status, p.Remark) if err == nil { c.Data["json"] = map[string]interface{}{ "code": 200, diff --git a/middleware/permission.go b/middleware/permission.go index 895326b..6e85bbe 100644 --- a/middleware/permission.go +++ b/middleware/permission.go @@ -1,7 +1,7 @@ package middleware import ( - "server/models" + "server/services" "strings" "github.com/beego/beego/v2/server/web/context" @@ -62,7 +62,7 @@ func PermissionMiddleware() func(ctx *context.Context) { } // 检查用户是否拥有该权限 - hasPermission, err := models.CheckUserPermission(userId, permission) + hasPermission, err := services.CheckUserPermission(userId, permission) if err != nil { ctx.Output.JSON(map[string]interface{}{ "success": false, diff --git a/models/admin_user.go b/models/admin_user.go index 9acea95..fc10398 100644 --- a/models/admin_user.go +++ b/models/admin_user.go @@ -1,17 +1,12 @@ package models -import ( - "crypto/md5" - "encoding/hex" - "strings" - "time" -) +import "time" // AdminUser 平台管理员信息表 yz_system_admin_user type AdminUser struct { ID uint64 `orm:"column(id);pk;auto" json:"id"` Account string `orm:"column(account);size(64)" json:"account"` - Password string `orm:"column(password);size(32)" json:"-"` + Password string `orm:"column(password);size(255)" json:"-"` Name *string `orm:"column(name);size(32);null" json:"name"` Phone *string `orm:"column(phone);size(18);null" json:"phone"` Email *string `orm:"column(email);size(255);null" json:"email"` @@ -30,66 +25,3 @@ type AdminUser struct { func (m *AdminUser) TableName() string { return "yz_system_admin_user" } - -func md5Hex(s string) string { - sum := md5.Sum([]byte(s)) - return hex.EncodeToString(sum[:]) -} - -func NormalizeAccount(s string) string { - return strings.TrimSpace(s) -} - -// CreateAdminUser 创建平台管理员用户(password 会被 md5) -func CreateAdminUser(account, password string, name, phone, email, qq, avatar *string, sex uint8, roleID uint64, status uint8) (uint64, error) { - u := &AdminUser{ - Account: NormalizeAccount(account), - Password: md5Hex(strings.TrimSpace(password)), - Name: name, - Phone: phone, - Email: email, - Qq: qq, - Avatar: avatar, - Sex: sex, - RoleID: roleID, - Status: status, - } - id, err := Orm.Insert(u) - return uint64(id), err -} - -func GetAdminUserByID(id uint64) (*AdminUser, error) { - u := &AdminUser{ID: id} - if err := Orm.Read(u); err != nil { - return nil, err - } - return u, nil -} - -// UpdateAdminUser 更新用户基础信息(不含 password) -func UpdateAdminUser(id uint64, fields map[string]interface{}) error { - _, err := Orm.QueryTable(new(AdminUser)).Filter("id", id).Update(fields) - return err -} - -func DeleteAdminUser(id uint64) error { - _, err := Orm.QueryTable(new(AdminUser)).Filter("id", id).Delete() - return err -} - -func ChangeAdminUserPassword(id uint64, newPassword string) error { - _, err := Orm.QueryTable(new(AdminUser)).Filter("id", id).Update(map[string]interface{}{ - "password": md5Hex(strings.TrimSpace(newPassword)), - }) - return err -} - -func ListAdminUsers() ([]AdminUser, int64, error) { - var rows []AdminUser - total, err := Orm.QueryTable(new(AdminUser)).Count() - if err != nil { - return nil, 0, err - } - _, err = Orm.QueryTable(new(AdminUser)).OrderBy("-id").All(&rows) - return rows, total, err -} diff --git a/models/tenant_user.go b/models/tenant_user.go index b033ded..d0f1a8b 100644 --- a/models/tenant_user.go +++ b/models/tenant_user.go @@ -24,82 +24,3 @@ type TenantUser struct { func (m *TenantUser) TableName() string { return "yz_system_tenant_user" } - -// BindTenantUser 绑定用户到租户(若已存在则更新状态/默认值) -func BindTenantUser(tid, uid uint64, account, name, phone, email, password *string, isDefault, status int8, remark *string) (uint64, error) { - var existed TenantUser - err := Orm.QueryTable(new(TenantUser)). - Filter("tid", tid). - Filter("uid", uid). - One(&existed) - if err == nil { - update := map[string]interface{}{ - "account": account, - "name": name, - "phone": phone, - "email": email, - "password": password, - "status": status, - "is_default": isDefault, - "remark": remark, - } - _, uErr := Orm.QueryTable(new(TenantUser)).Filter("id", existed.ID).Update(update) - return existed.ID, uErr - } - - m := &TenantUser{ - Tid: tid, - Uid: uid, - Account: account, - Name: name, - Phone: phone, - Email: email, - Password: password, - IsDefault: isDefault, - Status: status, - Remark: remark, - } - id, iErr := Orm.Insert(m) - return uint64(id), iErr -} - -// UnbindTenantUser 删除绑定关系 -func UnbindTenantUser(id uint64) error { - _, err := Orm.QueryTable(new(TenantUser)).Filter("id", id).Delete() - return err -} - -// ListTenantUsersByTid 根据租户ID查询绑定关系 -func ListTenantUsersByTid(tid uint64) ([]TenantUser, error) { - var rows []TenantUser - _, err := Orm.QueryTable(new(TenantUser)). - Filter("tid", tid). - OrderBy("-is_default", "-id"). - All(&rows) - return rows, err -} - -// ListTenantBindingsByUid 根据用户ID查询绑定关系 -func ListTenantBindingsByUid(uid uint64) ([]TenantUser, error) { - var rows []TenantUser - _, err := Orm.QueryTable(new(TenantUser)). - Filter("uid", uid). - OrderBy("-is_default", "-id"). - All(&rows) - return rows, err -} - -// SetDefaultTenant 设置用户默认租户(同一用户仅一个默认) -func SetDefaultTenant(uid, tid uint64) error { - _, err := Orm.QueryTable(new(TenantUser)).Filter("uid", uid).Update(map[string]interface{}{ - "is_default": 0, - }) - if err != nil { - return err - } - _, err = Orm.QueryTable(new(TenantUser)). - Filter("uid", uid). - Filter("tid", tid). - Update(map[string]interface{}{"is_default": 1}) - return err -} diff --git a/pkg/passwordutil/password.go b/pkg/passwordutil/password.go new file mode 100644 index 0000000..2d8a829 --- /dev/null +++ b/pkg/passwordutil/password.go @@ -0,0 +1,55 @@ +package passwordutil + +import ( + "crypto/rand" + "crypto/sha256" + "encoding/hex" + "errors" + "strings" +) + +const ( + saltBytes = 16 + separator = "$" + hashLength = 64 // sha256 hex length +) + +// Hash 生成 salt+hash 的存储串,格式:salt$hash(均为 hex) +func Hash(plain string) (string, error) { + plain = strings.TrimSpace(plain) + if plain == "" { + return "", errors.New("password 不能为空") + } + salt := make([]byte, saltBytes) + if _, err := rand.Read(salt); err != nil { + return "", err + } + saltHex := hex.EncodeToString(salt) + hashHex := hashHex(saltHex, plain) + return saltHex + separator + hashHex, nil +} + +// Verify 校验存储串(salt$hash)是否匹配输入明文密码。 +func Verify(stored, plain string) bool { + stored = strings.TrimSpace(stored) + plain = strings.TrimSpace(plain) + if stored == "" || plain == "" { + return false + } + parts := strings.Split(stored, separator) + if len(parts) != 2 { + return false + } + saltHex := strings.TrimSpace(parts[0]) + hashHexStored := strings.TrimSpace(parts[1]) + if saltHex == "" || len(hashHexStored) != hashLength { + return false + } + return hashHex(saltHex, plain) == strings.ToLower(hashHexStored) +} + +func hashHex(saltHex, plain string) string { + sum := sha256.Sum256([]byte(saltHex + plain)) + return hex.EncodeToString(sum[:]) +} + diff --git a/routers/backend/backend.go b/routers/backend/backend.go index cf28839..a2f2751 100644 --- a/routers/backend/backend.go +++ b/routers/backend/backend.go @@ -1,7 +1,33 @@ package backend +import ( + "server/controllers" + + beego "github.com/beego/beego/v2/server/web" +) + // Register 注册租户端(backend)路由。 // 该端不包含平台菜单配置接口。 func Register() { + RegisterAuthRoutes() } +// RegisterAuthRoutes 注册 backend 认证相关路由。 +func RegisterAuthRoutes() { + // backend 登录相关(统一走 /backend/*) + beego.Router("/backend/login", &controllers.PlatformAuthController{}, "post:Login") + beego.Router("/backend/sendLoginCode", &controllers.PlatformAuthController{}, "post:SendLoginCode") + beego.Router("/backend/loginBySms", &controllers.PlatformAuthController{}, "post:LoginBySms") + beego.Router("/backend/logout", &controllers.PlatformAuthController{}, "post:Logout") + + // 极验与登录验证配置 + beego.Router("/backend/login/getGeetest3Infos", &controllers.PlatformAuthController{}, "get:GetGeetest3Infos") + beego.Router("/backend/login/getGeetest4Infos", &controllers.PlatformAuthController{}, "get:GetGeetest4Infos") + beego.Router("/backend/login/getOpenVerify", &controllers.PlatformAuthController{}, "get:GetOpenVerify") + + // 注册与找回密码 + beego.Router("/backend/register", &controllers.PlatformAuthController{}, "post:Register") + beego.Router("/backend/sendRegisterCode", &controllers.PlatformAuthController{}, "post:SendRegisterCode") + beego.Router("/backend/resetPassword", &controllers.PlatformAuthController{}, "post:ResetPassword") + beego.Router("/backend/sendResetCode", &controllers.PlatformAuthController{}, "post:SendResetCode") +} diff --git a/routers/router.go b/routers/router.go index 15d93ae..9c77498 100644 --- a/routers/router.go +++ b/routers/router.go @@ -48,6 +48,8 @@ func init() { switch mode { case "platform": platform.Register() + // 在 platform 模式下,仍保留 backend 登录相关路由,避免后台登录 404 + backend.RegisterAuthRoutes() case "backend": backend.Register() case "index": diff --git a/services/admin_user.go b/services/admin_user.go new file mode 100644 index 0000000..b1852d5 --- /dev/null +++ b/services/admin_user.go @@ -0,0 +1,73 @@ +package services + +import ( + "strings" + + "server/models" + "server/pkg/passwordutil" +) + +func NormalizeAccount(s string) string { + return strings.TrimSpace(s) +} + +func CreateAdminUser(account, password string, name, phone, email, qq, avatar *string, sex uint8, roleID uint64, status uint8) (uint64, error) { + hashed, err := passwordutil.Hash(password) + if err != nil { + return 0, err + } + u := &models.AdminUser{ + Account: NormalizeAccount(account), + Password: hashed, + Name: name, + Phone: phone, + Email: email, + Qq: qq, + Avatar: avatar, + Sex: sex, + RoleID: roleID, + Status: status, + } + id, err := models.Orm.Insert(u) + return uint64(id), err +} + +func GetAdminUserByID(id uint64) (*models.AdminUser, error) { + u := &models.AdminUser{ID: id} + if err := models.Orm.Read(u); err != nil { + return nil, err + } + return u, nil +} + +func UpdateAdminUser(id uint64, fields map[string]interface{}) error { + _, err := models.Orm.QueryTable(new(models.AdminUser)).Filter("id", id).Update(fields) + return err +} + +func DeleteAdminUser(id uint64) error { + _, err := models.Orm.QueryTable(new(models.AdminUser)).Filter("id", id).Delete() + return err +} + +func ChangeAdminUserPassword(id uint64, newPassword string) error { + hashed, err := passwordutil.Hash(newPassword) + if err != nil { + return err + } + _, err = models.Orm.QueryTable(new(models.AdminUser)).Filter("id", id).Update(map[string]interface{}{ + "password": hashed, + }) + return err +} + +func ListAdminUsers() ([]models.AdminUser, int64, error) { + var rows []models.AdminUser + total, err := models.Orm.QueryTable(new(models.AdminUser)).Count() + if err != nil { + return nil, 0, err + } + _, err = models.Orm.QueryTable(new(models.AdminUser)).OrderBy("-id").All(&rows) + return rows, total, err +} + diff --git a/models/permission_check.go b/services/permission.go similarity index 76% rename from models/permission_check.go rename to services/permission.go index f927f91..116efe1 100644 --- a/models/permission_check.go +++ b/services/permission.go @@ -1,8 +1,10 @@ -package models +package services import ( "encoding/json" "strings" + + "server/models" ) // CheckUserPermission 校验用户是否拥有指定权限标识。 @@ -12,13 +14,13 @@ func CheckUserPermission(userID int, permission string) (bool, error) { return true, nil } - var user AdminUser - if err := Orm.QueryTable(new(AdminUser)).Filter("id", userID).One(&user); err != nil { + var user models.AdminUser + if err := models.Orm.QueryTable(new(models.AdminUser)).Filter("id", userID).One(&user); err != nil { return false, err } - var role AdminRole - if err := Orm.QueryTable(new(AdminRole)).Filter("id", user.RoleID).One(&role); err != nil { + var role models.AdminRole + if err := models.Orm.QueryTable(new(models.AdminRole)).Filter("id", user.RoleID).One(&role); err != nil { return false, err } if role.Rights == nil || strings.TrimSpace(*role.Rights) == "" { @@ -46,3 +48,4 @@ func CheckUserPermission(userID int, permission string) (bool, error) { return false, nil } + diff --git a/services/platform_auth.go b/services/platform_auth.go index afa4a9d..674a74a 100644 --- a/services/platform_auth.go +++ b/services/platform_auth.go @@ -1,19 +1,19 @@ package services import ( - "crypto/md5" - "encoding/hex" "errors" "strings" "server/models" "server/pkg/jwtutil" + "server/pkg/passwordutil" ) type PlatformLoginUser struct { ID uint64 Account string Name string + Tid uint64 Rid uint64 Avatar string RoleName string @@ -44,53 +44,77 @@ func toPlatformLoginUser(user *models.AdminUser) *PlatformLoginUser { ID: user.ID, Account: user.Account, Name: name, + Tid: 0, Rid: user.RoleID, Avatar: avatar, RoleName: adminRoleNameByID(user.RoleID), } } -func md5Hex(s string) string { - sum := md5.Sum([]byte(s)) - return hex.EncodeToString(sum[:]) -} - -// PlatformLogin 平台登录业务(仅允许平台用户 yz_system_admin_user 登录) -func PlatformLogin(account, password string) (string, *PlatformLoginUser, error) { +// PlatformLogin 登录业务:先校验租户,再校验租户下用户 +func PlatformLogin(tenantName, account, password string) (string, *PlatformLoginUser, error) { + tenantName = strings.TrimSpace(tenantName) account = strings.TrimSpace(account) password = strings.TrimSpace(password) - if account == "" || password == "" { - return "", nil, errors.New("用户名或密码不能为空") + if tenantName == "" || account == "" || password == "" { + return "", nil, errors.New("租户名称、用户名或密码不能为空") } - var user models.AdminUser - err := models.Orm.QueryTable(new(models.AdminUser)). + // 1) 校验租户名称 + var tenant models.Tenant + err := models.Orm.QueryTable(new(models.Tenant)). + Filter("tenant_name", tenantName). + One(&tenant) + if err != nil { + return "", nil, errors.New("租户不存在") + } + if tenant.Status != 1 { + return "", nil, errors.New("租户已停用") + } + + // 2) 在 tid 下校验租户用户账号和密码 + var tenantUser models.TenantUser + err = models.Orm.QueryTable(new(models.TenantUser)). + Filter("tid", tenant.ID). Filter("account", account). - One(&user) + One(&tenantUser) if err != nil { return "", nil, errors.New("用户名或密码错误") } - if user.Password != md5Hex(password) { + if tenantUser.Status == 0 { + return "", nil, errors.New("账号已禁用") + } + if tenantUser.Password == nil || !passwordutil.Verify(*tenantUser.Password, password) { return "", nil, errors.New("用户名或密码错误") } + // 3) 读取用户主档用于返回资料与角色信息 + var user models.AdminUser + err = models.Orm.QueryTable(new(models.AdminUser)). + Filter("id", tenantUser.Uid). + One(&user) + if err != nil { + return "", nil, errors.New("用户不存在") + } if user.Status == 0 { return "", nil, errors.New("账号已禁用") } - const fakeTenantID = 0 + + tenantID := int(tenant.ID) const userType = "platform" - token, err := jwtutil.GenerateToken(int(user.ID), user.Account, fakeTenantID, userType) + token, err := jwtutil.GenerateToken(int(user.ID), user.Account, tenantID, userType) if err != nil { return "", nil, err } loginUser := toPlatformLoginUser(&user) + loginUser.Tid = tenant.ID return token, loginUser, nil } // PlatformGetCurrentUser 根据平台管理员用户 ID 返回登录用户信息(含角色名称) func PlatformGetCurrentUser(uid uint64) (*PlatformLoginUser, error) { - u, err := models.GetAdminUserByID(uid) + u, err := GetAdminUserByID(uid) if err != nil { return nil, errors.New("用户不存在") } diff --git a/services/tenant_user.go b/services/tenant_user.go new file mode 100644 index 0000000..3e6a568 --- /dev/null +++ b/services/tenant_user.go @@ -0,0 +1,83 @@ +package services + +import "server/models" + +// BindTenantUser 绑定用户到租户(若已存在则更新状态/默认值) +func BindTenantUser(tid, uid uint64, account, name, phone, email, password *string, isDefault, status int8, remark *string) (uint64, error) { + var existed models.TenantUser + err := models.Orm.QueryTable(new(models.TenantUser)). + Filter("tid", tid). + Filter("uid", uid). + One(&existed) + if err == nil { + update := map[string]interface{}{ + "account": account, + "name": name, + "phone": phone, + "email": email, + "password": password, + "status": status, + "is_default": isDefault, + "remark": remark, + } + _, uErr := models.Orm.QueryTable(new(models.TenantUser)).Filter("id", existed.ID).Update(update) + return existed.ID, uErr + } + + m := &models.TenantUser{ + Tid: tid, + Uid: uid, + Account: account, + Name: name, + Phone: phone, + Email: email, + Password: password, + IsDefault: isDefault, + Status: status, + Remark: remark, + } + id, iErr := models.Orm.Insert(m) + return uint64(id), iErr +} + +// UnbindTenantUser 删除绑定关系 +func UnbindTenantUser(id uint64) error { + _, err := models.Orm.QueryTable(new(models.TenantUser)).Filter("id", id).Delete() + return err +} + +// ListTenantUsersByTid 根据租户ID查询绑定关系 +func ListTenantUsersByTid(tid uint64) ([]models.TenantUser, error) { + var rows []models.TenantUser + _, err := models.Orm.QueryTable(new(models.TenantUser)). + Filter("tid", tid). + OrderBy("-is_default", "-id"). + All(&rows) + return rows, err +} + +// ListTenantBindingsByUid 根据用户ID查询绑定关系 +func ListTenantBindingsByUid(uid uint64) ([]models.TenantUser, error) { + var rows []models.TenantUser + _, err := models.Orm.QueryTable(new(models.TenantUser)). + Filter("uid", uid). + OrderBy("-is_default", "-id"). + All(&rows) + return rows, err +} + +// SetDefaultTenant 设置用户默认租户(同一用户仅一个默认) +func SetDefaultTenant(uid, tid uint64) error { + _, err := models.Orm.QueryTable(new(models.TenantUser)).Filter("uid", uid).Update(map[string]interface{}{ + "is_default": 0, + }) + if err != nil { + return err + } + _, err = models.Orm.QueryTable(new(models.TenantUser)). + Filter("uid", uid). + Filter("tid", tid). + Update(map[string]interface{}{"is_default": 1}) + return err +} +