完成租户登录

This commit is contained in:
2025-10-29 23:07:53 +08:00
parent b9938b4c0d
commit ddf90424ba
43 changed files with 7761 additions and 474 deletions
+5 -3
View File
@@ -14,18 +14,20 @@ var jwtSecret = []byte("yunzer_jwt_secret_key") // 在实际应用中应从配
type Claims struct {
UserID int `json:"user_id"`
Username string `json:"username"`
TenantId int `json:"tenant_id"` // 租户ID
jwt.RegisteredClaims
}
// GenerateToken 生成JWT token
func GenerateToken(userID int, username string) (string, error) {
func GenerateToken(userID int, username string, tenantId int) (string, error) {
// 设置token过期时间
expirationTime := time.Now().Add(24 * time.Hour) // 24小时后过期
// 创建claims
claims := &Claims{
UserID: userID,
UserID: userID,
Username: username,
TenantId: tenantId,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(expirationTime),
IssuedAt: jwt.NewNumericDate(time.Now()),
@@ -63,4 +65,4 @@ func ParseToken(tokenString string) (*Claims, error) {
}
return claims, nil
}
}
+12
View File
@@ -69,6 +69,18 @@ func UpdateMenu(menu *Menu) error {
return err
}
// UpdateMenuStatus 更新菜单状态
func UpdateMenuStatus(id int, status int8) error {
o := orm.NewOrm()
menu := Menu{Id: id}
if err := o.Read(&menu); err != nil {
return err
}
menu.Status = status
_, err := o.Update(&menu, "Status")
return err
}
// DeleteMenu 删除菜单
func DeleteMenu(id int) error {
o := orm.NewOrm()
+127
View File
@@ -0,0 +1,127 @@
package models
import (
"errors"
"time"
"github.com/beego/beego/v2/client/orm"
)
type Tenant struct {
Id int `orm:"pk;auto" json:"id"`
Name string `orm:"size(100)" json:"name"`
Code string `orm:"size(50);unique" json:"code"`
Owner string `orm:"size(50)" json:"owner"`
Phone string `orm:"size(20);null" json:"phone"`
Email string `orm:"size(100);null" json:"email"`
Status string `orm:"size(20);default(enabled)" json:"status"` // enabled, disabled
AuditStatus string `orm:"size(20);default(pending)" json:"audit_status"` // pending, approved, rejected
AuditComment string `orm:"type(text);null" json:"audit_comment"`
AuditBy string `orm:"size(50);null" json:"audit_by"`
AuditTime *time.Time `orm:"null;type(datetime)" json:"audit_time"`
Remark string `orm:"type(text);null" json:"remark"`
CreateTime time.Time `orm:"auto_now_add;type(datetime)" json:"create_time"`
UpdateTime time.Time `orm:"auto_now;type(datetime)" json:"update_time"`
DeleteTime *time.Time `orm:"null;type(datetime)" json:"delete_time"`
CreateBy string `orm:"size(50);null" json:"create_by"`
UpdateBy string `orm:"size(50);null" json:"update_by"`
}
// TableName 设置表名
func (t *Tenant) TableName() string {
return "yz_tenants"
}
func init() {
orm.RegisterModel(new(Tenant))
}
// GetTenantList 获取所有租户(软删除:只返回未删除的)
func GetTenantList() ([]Tenant, error) {
o := orm.NewOrm()
var tenants []Tenant
// 使用原生 SQL 查询 delete_time IS NULL 的记录(更可靠)
_, err := o.Raw("SELECT * FROM yz_tenants WHERE delete_time IS NULL ORDER BY id DESC").QueryRows(&tenants)
return tenants, err
}
// CreateTenant 新建租户
func CreateTenant(tenant *Tenant) error {
o := orm.NewOrm()
_, err := o.Insert(tenant)
return err
}
// UpdateTenant 编辑租户(只能编辑未删除的)
func UpdateTenant(id int, data map[string]interface{}) error {
o := orm.NewOrm()
// 先检查租户是否存在且未删除
tenant := Tenant{}
err := o.Raw("SELECT * FROM yz_tenants WHERE id = ? AND delete_time IS NULL", id).QueryRow(&tenant)
if err == orm.ErrNoRows {
return errors.New("租户不存在或已被删除")
}
if err != nil {
return err
}
// 更新记录
_, err = o.QueryTable(new(Tenant)).Filter("Id", id).Update(data)
return err
}
// DeleteTenant 软删除租户(设置 delete_time
func DeleteTenant(id int) error {
o := orm.NewOrm()
deleteTime := time.Now()
_, err := o.QueryTable(new(Tenant)).Filter("Id", id).Update(orm.Params{"delete_time": deleteTime})
return err
}
// AuditTenant 审核租户(只能审核未删除的)
func AuditTenant(id int, auditStatus, auditComment, auditBy string) error {
o := orm.NewOrm()
tenant := Tenant{}
// 先检查租户是否存在且未删除
err := o.Raw("SELECT * FROM yz_tenants WHERE id = ? AND delete_time IS NULL", id).QueryRow(&tenant)
if err == orm.ErrNoRows {
return errors.New("租户不存在或已被删除")
}
if err != nil {
return err
}
now := time.Now()
tenant.AuditStatus = auditStatus
tenant.AuditComment = auditComment
tenant.AuditBy = auditBy
tenant.AuditTime = &now
_, err = o.Update(&tenant, "AuditStatus", "AuditComment", "AuditBy", "AuditTime")
return err
}
// GetTenantById 根据ID获取租户详情(只返回未删除的)
func GetTenantById(id int) (*Tenant, error) {
o := orm.NewOrm()
tenant := Tenant{}
// 使用原生 SQL 查询,只返回未删除的记录
err := o.Raw("SELECT * FROM yz_tenants WHERE id = ? AND delete_time IS NULL", id).QueryRow(&tenant)
if err == orm.ErrNoRows {
return nil, nil
}
return &tenant, err
}
// GetTenantByName 根据名称获取租户详情(只返回未删除的)
func GetTenantByName(name string) (*Tenant, error) {
o := orm.NewOrm()
tenant := Tenant{}
// 使用原生 SQL 查询,只返回未删除的记录
err := o.Raw("SELECT * FROM yz_tenants WHERE name = ? AND delete_time IS NULL", name).QueryRow(&tenant)
if err == orm.ErrNoRows {
return nil, errors.New("租户不存在")
}
if err != nil {
return nil, err
}
return &tenant, nil
}
+108 -42
View File
@@ -16,7 +16,8 @@ import (
// User 用户模型,增加Salt字段存储每个用户的唯一盐值
type User struct {
Id int `orm:"auto"`
Username string `orm:"unique"`
TenantId int `orm:"column(tenant_id);default(0)" json:"tenant_id"` // 租户ID
Username string // 用户名不再全局唯一,而是在租户内唯一(tenant_id + username 的组合唯一)
Password string // 存储加密后的密码
Salt string // 存储该用户的唯一盐值
Email string
@@ -66,15 +67,14 @@ func verifyPassword(password, salt, storedHash string) bool {
return hash == storedHash
}
// ResetPassword 重置用户密码
func ResetPassword(username, superPassword string) error {
// ResetPassword 重置用户密码(支持租户模式)
func ResetPassword(username, superPassword string, tenantId int) error {
if superPassword != "Lzq920103" {
return fmt.Errorf("超级密码错误")
}
o := orm.NewOrm()
user := &User{Username: username}
err := o.Read(user, "Username")
user, err := GetUserByUsername(username, tenantId)
if err != nil {
return fmt.Errorf("用户不存在: %v", err)
}
@@ -102,9 +102,9 @@ func ResetPassword(username, superPassword string) error {
return nil
}
// ChangePassword 修改用户密码
func ChangePassword(username, oldPassword, newPassword string) error {
user, err := GetUserByUsername(username)
// ChangePassword 修改用户密码(支持租户模式)
func ChangePassword(username, oldPassword, newPassword string, tenantId int) error {
user, err := GetUserByUsername(username, tenantId)
if err != nil {
return err
}
@@ -124,22 +124,32 @@ func ChangePassword(username, oldPassword, newPassword string) error {
return err
}
// FindAllUsers 获取所有用户
func FindAllUsers() []*User {
// FindAllUsers 获取所有用户(支持按租户过滤)
func FindAllUsers(tenantId int) []*User {
o := orm.NewOrm()
var users []*User
_, err := o.QueryTable("yz_users").All(&users)
if err != nil {
return []*User{}
if tenantId > 0 {
// 按租户ID查询
_, err := o.Raw("SELECT * FROM yz_users WHERE tenant_id = ?", tenantId).QueryRows(&users)
if err != nil {
return []*User{}
}
} else {
// 查询所有用户
_, err := o.QueryTable("yz_users").All(&users)
if err != nil {
return []*User{}
}
}
return users
}
// GetUserByUsername 根据用户名获取用户
func GetUserByUsername(username string) (*User, error) {
// GetUserByUsername 根据用户名获取用户(支持租户隔离)
func GetUserByUsername(username string, tenantId int) (*User, error) {
o := orm.NewOrm()
user := &User{Username: username}
err := o.Read(user, "Username")
user := &User{}
// 使用原生 SQL 查询,考虑租户ID
err := o.Raw("SELECT * FROM yz_users WHERE username = ? AND tenant_id = ?", username, tenantId).QueryRow(user)
if err == orm.ErrNoRows {
return nil, errors.New("用户不存在")
}
@@ -149,24 +159,74 @@ func GetUserByUsername(username string) (*User, error) {
return user, nil
}
// ValidateUser 验证用户登录信息
func ValidateUser(username, password string) (*User, error) {
user, err := GetUserByUsername(username)
// ValidateUser 验证用户登录信息(支持租户模式,根据租户名称)
// 先验证租户是否存在且有效,再验证租户下的用户
func ValidateUser(username, password string, tenantName string) (*User, error) {
o := orm.NewOrm()
// 1. 根据租户名称查询租户(只查询未删除的)
var tenant struct {
Id int
Status string
DeleteTime interface{} // 使用 interface{} 来处理 NULL 值
}
err := o.Raw("SELECT id, status, delete_time FROM yz_tenants WHERE name = ? AND delete_time IS NULL", tenantName).QueryRow(&tenant)
if err == orm.ErrNoRows {
// 租户不存在(数据库中根本没有这个名称)
return nil, errors.New("租户不存在")
}
if err != nil {
return nil, fmt.Errorf("查询租户失败: %v", err)
}
// 检查租户是否被删除(软删除)
if tenant.DeleteTime != nil {
// delete_time 不为 NULL,说明已被删除
return nil, errors.New("租户已被删除")
}
// 检查租户状态
if tenant.Status == "disabled" {
return nil, errors.New("租户已被禁用")
}
if tenant.Status != "enabled" {
return nil, fmt.Errorf("租户状态异常: %s", tenant.Status)
}
tenantId := tenant.Id
// 2. 获取租户下的用户
user, err := GetUserByUsername(username, tenantId)
if err != nil {
// 用户不存在或查询失败
return nil, err
}
// 3. 验证密码
if verifyPassword(password, user.Salt, user.Password) {
return user, nil
}
return nil, errors.New("密码不正确")
}
// AddUser 向数据库添加新用户(模型层核心方法)
func AddUser(username, password, email, nickname, avatar string) (*User, error) {
// 1. 检查用户是否存在(避免用户名重复)
existingUser, err := GetUserByUsername(username)
// AddUser 向数据库添加新用户(模型层核心方法,支持租户模式
func AddUser(username, password, email, nickname, avatar string, tenantId int) (*User, error) {
// 1. 验证租户是否存在且有效
o := orm.NewOrm()
var tenantExists bool
err := o.Raw("SELECT EXISTS(SELECT 1 FROM yz_tenants WHERE id = ? AND delete_time IS NULL AND status = 'enabled')", tenantId).QueryRow(&tenantExists)
if err != nil {
return nil, fmt.Errorf("验证租户失败: %v", err)
}
if !tenantExists {
return nil, fmt.Errorf("租户不存在或已被禁用")
}
// 2. 检查该租户下用户是否已存在(避免用户名重复,但不同租户可以有相同的用户名)
existingUser, err := GetUserByUsername(username, tenantId)
if err == nil && existingUser != nil {
return nil, fmt.Errorf("用户名已存在")
return nil, fmt.Errorf("该租户下用户名已存在")
}
if err != nil && err.Error() != "用户不存在" { // 排除"用户不存在"的正常错误
return nil, fmt.Errorf("查询用户失败: %v", err)
@@ -186,6 +246,7 @@ func AddUser(username, password, email, nickname, avatar string) (*User, error)
// 4. 构建用户对象
user := &User{
TenantId: tenantId,
Username: username,
Password: hashedPassword, // 存储加密后的密码
Salt: salt, // 存储盐值(用于后续验证)
@@ -194,8 +255,7 @@ func AddUser(username, password, email, nickname, avatar string) (*User, error)
Avatar: avatar,
}
// 5. 插入数据库
o := orm.NewOrm()
// 5. 插入数据库(使用之前定义的 o
_, err = o.Insert(user)
if err != nil {
return nil, fmt.Errorf("数据库插入失败: %v", err)
@@ -205,22 +265,25 @@ func AddUser(username, password, email, nickname, avatar string) (*User, error)
return user, nil
}
// UpdateUser 更新用户信息(模型层方法)
func UpdateUser(id int, username, email, nickname, avatar string) (*User, error) {
// 1. 根据ID查询用户是否存在
// UpdateUser 更新用户信息(模型层方法,支持租户模式
func UpdateUser(id int, username, email, nickname, avatar string, tenantId int) (*User, error) {
// 1. 根据ID和租户ID查询用户是否存在(确保只能更新自己租户下的用户)
o := orm.NewOrm()
user := &User{Id: id}
err := o.Read(user)
user := &User{}
err := o.Raw("SELECT * FROM yz_users WHERE id = ? AND tenant_id = ?", id, tenantId).QueryRow(user)
if err == orm.ErrNoRows {
return nil, fmt.Errorf("用户不存在或不属于该租户")
}
if err != nil {
return nil, fmt.Errorf("用户不存在: %v", err)
return nil, fmt.Errorf("查询用户失败: %v", err)
}
// 2. 仅更新非空字段(避免覆盖原有值)
if username != "" {
// 若更新用户名,需检查新用户名是否已被占用
existingUser, _ := GetUserByUsername(username)
// 若更新用户名,需检查同一租户下新用户名是否已被占用
existingUser, _ := GetUserByUsername(username, tenantId)
if existingUser != nil && existingUser.Id != id {
return nil, fmt.Errorf("用户名已被占用")
return nil, fmt.Errorf("该租户下用户名已被占用")
}
user.Username = username
}
@@ -243,14 +306,17 @@ func UpdateUser(id int, username, email, nickname, avatar string) (*User, error)
return user, nil
}
// DeleteUser 根据ID删除用户(模型层方法)
func DeleteUser(id int) error {
// DeleteUser 根据ID删除用户(模型层方法,支持租户模式
func DeleteUser(id int, tenantId int) error {
o := orm.NewOrm()
// 先查询用户是否存在
user := &User{Id: id}
err := o.Read(user)
// 先查询用户是否存在且属于指定租户
user := &User{}
err := o.Raw("SELECT * FROM yz_users WHERE id = ? AND tenant_id = ?", id, tenantId).QueryRow(user)
if err == orm.ErrNoRows {
return fmt.Errorf("用户不存在或不属于该租户")
}
if err != nil {
return fmt.Errorf("用户不存在: %v", err)
return fmt.Errorf("查询用户失败: %v", err)
}
// 执行删除操作