package services import ( "errors" "strings" "server/models" "server/pkg/jwtutil" "server/pkg/passwordutil" authsvc "server/services/auth" ) type PlatformLoginUser struct { ID uint64 Account string Name string Tid uint64 TenantName string Rid uint64 Avatar string RoleName string } func adminRoleNameByID(roleID uint64) string { if roleID == 0 { return "" } var role models.AdminRole err := models.Orm.QueryTable(new(models.AdminRole)).Filter("id", roleID).One(&role) if err != nil { return "" } return role.Name } func toPlatformLoginUser(user *models.AdminUser) *PlatformLoginUser { name := "" if user.Name != nil { name = *user.Name } avatar := "" if user.Avatar != nil { avatar = *user.Avatar } return &PlatformLoginUser{ ID: user.ID, Account: user.Account, Name: name, Tid: 0, Rid: user.RoleID, Avatar: avatar, RoleName: adminRoleNameByID(user.RoleID), } } // PlatformAdminLogin 平台端登录:仅校验 yz_system_admin_user,不需要租户。 func PlatformAdminLogin(account, password string) (string, *PlatformLoginUser, error) { account = strings.TrimSpace(account) password = strings.TrimSpace(password) if account == "" || password == "" { return "", nil, errors.New("用户名或密码不能为空") } var user models.AdminUser err := models.Orm.QueryTable(new(models.AdminUser)). Filter("account", account). One(&user) if err != nil { return "", nil, errors.New("用户名或密码错误") } if user.Status == 0 { return "", nil, errors.New("账号已禁用") } if !passwordutil.Verify(user.Password, password) { return "", nil, errors.New("用户名或密码错误") } // 历史 sha256 密码:本次登录成功后自动升级为 argon2id(失败不影响登录) upgradePasswordIfNeeded(user.Password, password, func(hashed string) error { _, err := models.Orm.QueryTable(new(models.AdminUser)). Filter("id", user.ID). Update(map[string]interface{}{"password": hashed}) return err }) const tenantID = 0 const userType = "platform" token, err := jwtutil.GenerateToken(int(user.ID), user.Account, tenantID, userType) if err != nil { return "", nil, err } loginUser := toPlatformLoginUser(&user) return token, loginUser, nil } // BackendLogin backend 登录:先校验租户,再校验租户下用户账号和密码。 func BackendLogin(tenantName, account, password string) (string, *PlatformLoginUser, error) { tenantName = strings.TrimSpace(tenantName) account = strings.TrimSpace(account) password = strings.TrimSpace(password) if tenantName == "" || account == "" || password == "" { return "", nil, errors.New("租户名称、用户名或密码不能为空") } tenant, err := findTenantByLoginName(tenantName) if err != nil { return "", nil, errors.New("租户不存在") } if tenant.Status != 1 { return "", nil, errors.New("租户已停用") } var tenantUser models.SystemTenantUser err = models.Orm.QueryTable(new(models.SystemTenantUser)). Filter("tid", tenant.ID). Filter("account", account). One(&tenantUser) if err != nil { return "", nil, errors.New("用户名或密码错误") } if tenantUser.Status == 0 { return "", nil, errors.New("账号已禁用") } if tenantUser.Password == nil || !passwordutil.Verify(*tenantUser.Password, password) { return "", nil, errors.New("用户名或密码错误") } // 历史 sha256 密码:本次登录成功后自动升级为 argon2id(失败不影响登录) upgradePasswordIfNeeded(*tenantUser.Password, password, func(hashed string) error { _, err := models.Orm.QueryTable(new(models.SystemTenantUser)). Filter("id", tenantUser.ID). Update(map[string]interface{}{"password": hashed}) return err }) tenantID := int(tenant.ID) const userType = "backend" // 令牌中的 user_id 统一使用认证中心 identity_id(与统一登录签发的一致)。 // 业务表的 uid 已全量迁移为同一套 ID,因此老登录入口无需停用也不会错位。 userID := int(tenantUser.Uid) if identityID := authsvc.FindIdentityIDByAccount(tenant.ID, account); identityID > 0 { userID = int(identityID) } token, err := jwtutil.GenerateToken(userID, account, tenantID, userType) if err != nil { return "", nil, err } loginUser := &PlatformLoginUser{ ID: tenantUser.Uid, Account: account, Name: "", Tid: tenant.ID, TenantName: tenant.TenantName, Rid: 0, Avatar: "", RoleName: "", } if tenantUser.Account != nil && strings.TrimSpace(*tenantUser.Account) != "" { loginUser.Account = strings.TrimSpace(*tenantUser.Account) } if tenantUser.Name != nil { loginUser.Name = strings.TrimSpace(*tenantUser.Name) } return token, loginUser, nil } // upgradePasswordIfNeeded 旧算法(legacy sha256)密码在登录成功后自动重新哈希为 argon2id。 // // 旧哈希无法离线转换成新算法(无法反推明文),只能借登录时拿到的明文重新哈希, // 因此采用「首次登录自动升级」的渐进方式,无需强制全员重置密码。 // 重新哈希失败不影响本次登录,仅下次登录时重试。 func upgradePasswordIfNeeded(stored, plain string, update func(hashed string) error) { if !passwordutil.NeedsRehash(stored) { return } hashed, err := passwordutil.Hash(plain) if err != nil { return } _ = update(hashed) } // PlatformGetCurrentUser 根据平台管理员用户 ID 返回登录用户信息(含角色名称)。 func PlatformGetCurrentUser(uid uint64) (*PlatformLoginUser, error) { u, err := GetAdminUserByID(uid) if err != nil { return nil, errors.New("用户不存在") } if u.Status == 0 { return nil, errors.New("账号已禁用") } return toPlatformLoginUser(u), nil }