backend增加租户简称登录
This commit is contained in:
+232
-232
@@ -1,232 +1,232 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"server/models"
|
||||
)
|
||||
|
||||
type loginCodeItem struct {
|
||||
Code string
|
||||
Channel string
|
||||
ExpiredAt time.Time
|
||||
}
|
||||
|
||||
var loginCodeStore sync.Map
|
||||
|
||||
func codeKey(account, channel string) string {
|
||||
return strings.ToLower(strings.TrimSpace(account)) + "|" + strings.TrimSpace(channel)
|
||||
}
|
||||
|
||||
func SendPlatformLoginCode(account, channel string) error {
|
||||
account = strings.TrimSpace(account)
|
||||
channel = strings.TrimSpace(channel)
|
||||
if account == "" {
|
||||
return errors.New("账号不能为空")
|
||||
}
|
||||
if channel != "sms" && channel != "email" {
|
||||
return errors.New("仅支持短信或邮箱验证码")
|
||||
}
|
||||
|
||||
var u models.AdminUser
|
||||
if err := models.Orm.QueryTable(new(models.AdminUser)).Filter("account", account).One(&u); err != nil {
|
||||
return errors.New("用户不存在")
|
||||
}
|
||||
if u.Status == 0 {
|
||||
return errors.New("账号已禁用")
|
||||
}
|
||||
if channel == "sms" && (u.Phone == nil || strings.TrimSpace(*u.Phone) == "") {
|
||||
return errors.New("该账号未绑定手机号")
|
||||
}
|
||||
if channel == "email" && (u.Email == nil || strings.TrimSpace(*u.Email) == "") {
|
||||
return errors.New("该账号未绑定邮箱")
|
||||
}
|
||||
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
code := fmt.Sprintf("%06d", rand.Intn(1000000))
|
||||
loginCodeStore.Store(codeKey(account, channel), loginCodeItem{
|
||||
Code: code,
|
||||
Channel: channel,
|
||||
ExpiredAt: time.Now().Add(5 * time.Minute),
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func VerifyPlatformLoginCode(account, channel, code string) error {
|
||||
account = strings.TrimSpace(account)
|
||||
channel = strings.TrimSpace(channel)
|
||||
code = strings.TrimSpace(code)
|
||||
if account == "" || code == "" {
|
||||
return errors.New("验证码不能为空")
|
||||
}
|
||||
val, ok := loginCodeStore.Load(codeKey(account, channel))
|
||||
if !ok {
|
||||
return errors.New("验证码不存在或已失效")
|
||||
}
|
||||
item, ok := val.(loginCodeItem)
|
||||
if !ok {
|
||||
return errors.New("验证码状态异常")
|
||||
}
|
||||
if time.Now().After(item.ExpiredAt) {
|
||||
loginCodeStore.Delete(codeKey(account, channel))
|
||||
return errors.New("验证码已过期")
|
||||
}
|
||||
if item.Code != code {
|
||||
return errors.New("验证码错误")
|
||||
}
|
||||
loginCodeStore.Delete(codeKey(account, channel))
|
||||
return nil
|
||||
}
|
||||
|
||||
func SendBackendLoginCode(tenantName, account, channel string) error {
|
||||
tenantName = strings.TrimSpace(tenantName)
|
||||
account = strings.TrimSpace(account)
|
||||
channel = strings.TrimSpace(channel)
|
||||
if tenantName == "" || account == "" {
|
||||
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("租户不存在")
|
||||
}
|
||||
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
code := fmt.Sprintf("%06d", rand.Intn(1000000))
|
||||
|
||||
switch channel {
|
||||
case "sms":
|
||||
phone := account
|
||||
var user models.SystemTenantUser
|
||||
if err := models.Orm.QueryTable(new(models.SystemTenantUser)).
|
||||
Filter("tid", tenant.ID).
|
||||
Filter("phone", phone).
|
||||
One(&user); err != nil {
|
||||
return errors.New("该手机号非当前企业绑定号码,请重试")
|
||||
}
|
||||
if user.Status == 0 {
|
||||
return errors.New("账号已禁用")
|
||||
}
|
||||
if user.Phone == nil || strings.TrimSpace(*user.Phone) == "" {
|
||||
return errors.New("该手机号非当前企业绑定号码,请重试")
|
||||
}
|
||||
|
||||
content := "短信验证码:" + code
|
||||
if err := enqueueSMSTaskForLogin(tenant.ID, phone, content, code); err != nil {
|
||||
return errors.New("短信发送失败,请重试")
|
||||
}
|
||||
case "email":
|
||||
email := account
|
||||
var user models.SystemTenantUser
|
||||
if err := models.Orm.QueryTable(new(models.SystemTenantUser)).
|
||||
Filter("tid", tenant.ID).
|
||||
Filter("email", email).
|
||||
One(&user); err != nil {
|
||||
return errors.New("该账号未绑定邮箱")
|
||||
}
|
||||
if user.Status == 0 {
|
||||
return errors.New("账号已禁用")
|
||||
}
|
||||
if user.Email == nil || strings.TrimSpace(*user.Email) == "" {
|
||||
return errors.New("该账号未绑定邮箱")
|
||||
}
|
||||
}
|
||||
|
||||
loginCodeStore.Store(codeKey(tenantName+"#"+account, channel), loginCodeItem{
|
||||
Code: code,
|
||||
Channel: channel,
|
||||
ExpiredAt: time.Now().Add(5 * time.Minute),
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func VerifyBackendLoginCode(tenantName, account, channel, code string) error {
|
||||
return VerifyPlatformLoginCode(tenantName+"#"+account, channel, code)
|
||||
}
|
||||
|
||||
func getDefaultSystemSMSConfig() (backendURL string, apiKey string, err error) {
|
||||
backendURL = models.GetPlatformSettingValue("sms_custom_url", "")
|
||||
apiKey = models.GetPlatformSettingValue("sms_custom_key", "")
|
||||
if backendURL == "" || apiKey == "" {
|
||||
return "", "", fmt.Errorf("短信网关未配置")
|
||||
}
|
||||
return backendURL, apiKey, nil
|
||||
}
|
||||
|
||||
// enqueueSMSTaskForLogin 入队短信任务到网关,并写入 yz_system_sms_tasks
|
||||
func enqueueSMSTaskForLogin(tid uint64, phone, content, code string) error {
|
||||
backendURL, apiKey, err := getDefaultSystemSMSConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if backendURL == "" || apiKey == "" {
|
||||
return errors.New("短信网关未配置")
|
||||
}
|
||||
|
||||
enqueueURL := strings.TrimRight(backendURL, "/") + "/api/v1/business/outbound-tasks"
|
||||
payload := map[string]interface{}{
|
||||
"phone": phone,
|
||||
"content": content,
|
||||
}
|
||||
bs, _ := json.Marshal(payload)
|
||||
|
||||
client := &http.Client{Timeout: 10 * time.Second}
|
||||
req, err := http.NewRequest("POST", enqueueURL, bytes.NewReader(bs))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Set("X-Api-Key", apiKey)
|
||||
req.Header.Set("Content-Type", "application/json; charset=utf-8")
|
||||
req.Header.Set("Accept", "application/json")
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
bodyBytes, _ := io.ReadAll(resp.Body)
|
||||
bodyStr := strings.TrimSpace(string(bodyBytes))
|
||||
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||
return fmt.Errorf("gateway http status: %d, body: %s", resp.StatusCode, bodyStr)
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
tidCopy := tid
|
||||
contentPtr := content
|
||||
var reportPtr *string
|
||||
if bodyStr != "" {
|
||||
reportPtr = &bodyStr
|
||||
}
|
||||
|
||||
task := &models.SystemSMSTask{
|
||||
Tid: &tidCopy,
|
||||
ApiKey: apiKey,
|
||||
Phone: phone,
|
||||
Content: &contentPtr,
|
||||
Status: 3,
|
||||
Code: code,
|
||||
ReportRaw: reportPtr,
|
||||
CreateTime: &now,
|
||||
UpdateTime: &now,
|
||||
}
|
||||
|
||||
_, insertErr := models.Orm.Insert(task)
|
||||
if insertErr != nil {
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
package services
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"server/models"
|
||||
)
|
||||
|
||||
type loginCodeItem struct {
|
||||
Code string
|
||||
Channel string
|
||||
ExpiredAt time.Time
|
||||
}
|
||||
|
||||
var loginCodeStore sync.Map
|
||||
|
||||
func codeKey(account, channel string) string {
|
||||
return strings.ToLower(strings.TrimSpace(account)) + "|" + strings.TrimSpace(channel)
|
||||
}
|
||||
|
||||
func SendPlatformLoginCode(account, channel string) error {
|
||||
account = strings.TrimSpace(account)
|
||||
channel = strings.TrimSpace(channel)
|
||||
if account == "" {
|
||||
return errors.New("账号不能为空")
|
||||
}
|
||||
if channel != "sms" && channel != "email" {
|
||||
return errors.New("仅支持短信或邮箱验证码")
|
||||
}
|
||||
|
||||
var u models.AdminUser
|
||||
if err := models.Orm.QueryTable(new(models.AdminUser)).Filter("account", account).One(&u); err != nil {
|
||||
return errors.New("用户不存在")
|
||||
}
|
||||
if u.Status == 0 {
|
||||
return errors.New("账号已禁用")
|
||||
}
|
||||
if channel == "sms" && (u.Phone == nil || strings.TrimSpace(*u.Phone) == "") {
|
||||
return errors.New("该账号未绑定手机号")
|
||||
}
|
||||
if channel == "email" && (u.Email == nil || strings.TrimSpace(*u.Email) == "") {
|
||||
return errors.New("该账号未绑定邮箱")
|
||||
}
|
||||
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
code := fmt.Sprintf("%06d", rand.Intn(1000000))
|
||||
loginCodeStore.Store(codeKey(account, channel), loginCodeItem{
|
||||
Code: code,
|
||||
Channel: channel,
|
||||
ExpiredAt: time.Now().Add(5 * time.Minute),
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func VerifyPlatformLoginCode(account, channel, code string) error {
|
||||
account = strings.TrimSpace(account)
|
||||
channel = strings.TrimSpace(channel)
|
||||
code = strings.TrimSpace(code)
|
||||
if account == "" || code == "" {
|
||||
return errors.New("验证码不能为空")
|
||||
}
|
||||
val, ok := loginCodeStore.Load(codeKey(account, channel))
|
||||
if !ok {
|
||||
return errors.New("验证码不存在或已失效")
|
||||
}
|
||||
item, ok := val.(loginCodeItem)
|
||||
if !ok {
|
||||
return errors.New("验证码状态异常")
|
||||
}
|
||||
if time.Now().After(item.ExpiredAt) {
|
||||
loginCodeStore.Delete(codeKey(account, channel))
|
||||
return errors.New("验证码已过期")
|
||||
}
|
||||
if item.Code != code {
|
||||
return errors.New("验证码错误")
|
||||
}
|
||||
loginCodeStore.Delete(codeKey(account, channel))
|
||||
return nil
|
||||
}
|
||||
|
||||
func SendBackendLoginCode(tenantName, account, channel string) error {
|
||||
tenantName = strings.TrimSpace(tenantName)
|
||||
account = strings.TrimSpace(account)
|
||||
channel = strings.TrimSpace(channel)
|
||||
if tenantName == "" || account == "" {
|
||||
return errors.New("租户名称和账号不能为空")
|
||||
}
|
||||
if channel != "sms" && channel != "email" {
|
||||
return errors.New("仅支持短信或邮箱验证码")
|
||||
}
|
||||
|
||||
tenant, err := findTenantByLoginName(tenantName)
|
||||
if err != nil {
|
||||
return errors.New("租户不存在")
|
||||
}
|
||||
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
code := fmt.Sprintf("%06d", rand.Intn(1000000))
|
||||
|
||||
switch channel {
|
||||
case "sms":
|
||||
phone := account
|
||||
var user models.SystemTenantUser
|
||||
if err := models.Orm.QueryTable(new(models.SystemTenantUser)).
|
||||
Filter("tid", tenant.ID).
|
||||
Filter("phone", phone).
|
||||
One(&user); err != nil {
|
||||
return errors.New("该手机号非当前企业绑定号码,请重试")
|
||||
}
|
||||
if user.Status == 0 {
|
||||
return errors.New("账号已禁用")
|
||||
}
|
||||
if user.Phone == nil || strings.TrimSpace(*user.Phone) == "" {
|
||||
return errors.New("该手机号非当前企业绑定号码,请重试")
|
||||
}
|
||||
|
||||
content := "短信验证码:" + code
|
||||
if err := enqueueSMSTaskForLogin(tenant.ID, phone, content, code); err != nil {
|
||||
return errors.New("短信发送失败,请重试")
|
||||
}
|
||||
case "email":
|
||||
email := account
|
||||
var user models.SystemTenantUser
|
||||
if err := models.Orm.QueryTable(new(models.SystemTenantUser)).
|
||||
Filter("tid", tenant.ID).
|
||||
Filter("email", email).
|
||||
One(&user); err != nil {
|
||||
return errors.New("该账号未绑定邮箱")
|
||||
}
|
||||
if user.Status == 0 {
|
||||
return errors.New("账号已禁用")
|
||||
}
|
||||
if user.Email == nil || strings.TrimSpace(*user.Email) == "" {
|
||||
return errors.New("该账号未绑定邮箱")
|
||||
}
|
||||
}
|
||||
|
||||
loginCodeStore.Store(codeKey(tenantName+"#"+account, channel), loginCodeItem{
|
||||
Code: code,
|
||||
Channel: channel,
|
||||
ExpiredAt: time.Now().Add(5 * time.Minute),
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func VerifyBackendLoginCode(tenantName, account, channel, code string) error {
|
||||
return VerifyPlatformLoginCode(tenantName+"#"+account, channel, code)
|
||||
}
|
||||
|
||||
func getDefaultSystemSMSConfig() (backendURL string, apiKey string, err error) {
|
||||
backendURL = models.GetPlatformSettingValue("sms_custom_url", "")
|
||||
apiKey = models.GetPlatformSettingValue("sms_custom_key", "")
|
||||
if backendURL == "" || apiKey == "" {
|
||||
return "", "", fmt.Errorf("短信网关未配置")
|
||||
}
|
||||
return backendURL, apiKey, nil
|
||||
}
|
||||
|
||||
// enqueueSMSTaskForLogin 入队短信任务到网关,并写入 yz_system_sms_tasks
|
||||
func enqueueSMSTaskForLogin(tid uint64, phone, content, code string) error {
|
||||
backendURL, apiKey, err := getDefaultSystemSMSConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if backendURL == "" || apiKey == "" {
|
||||
return errors.New("短信网关未配置")
|
||||
}
|
||||
|
||||
enqueueURL := strings.TrimRight(backendURL, "/") + "/api/v1/business/outbound-tasks"
|
||||
payload := map[string]interface{}{
|
||||
"phone": phone,
|
||||
"content": content,
|
||||
}
|
||||
bs, _ := json.Marshal(payload)
|
||||
|
||||
client := &http.Client{Timeout: 10 * time.Second}
|
||||
req, err := http.NewRequest("POST", enqueueURL, bytes.NewReader(bs))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Set("X-Api-Key", apiKey)
|
||||
req.Header.Set("Content-Type", "application/json; charset=utf-8")
|
||||
req.Header.Set("Accept", "application/json")
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
bodyBytes, _ := io.ReadAll(resp.Body)
|
||||
bodyStr := strings.TrimSpace(string(bodyBytes))
|
||||
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||
return fmt.Errorf("gateway http status: %d, body: %s", resp.StatusCode, bodyStr)
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
tidCopy := tid
|
||||
contentPtr := content
|
||||
var reportPtr *string
|
||||
if bodyStr != "" {
|
||||
reportPtr = &bodyStr
|
||||
}
|
||||
|
||||
task := &models.SystemSMSTask{
|
||||
Tid: &tidCopy,
|
||||
ApiKey: apiKey,
|
||||
Phone: phone,
|
||||
Content: &contentPtr,
|
||||
Status: 3,
|
||||
Code: code,
|
||||
ReportRaw: reportPtr,
|
||||
CreateTime: &now,
|
||||
UpdateTime: &now,
|
||||
}
|
||||
|
||||
_, insertErr := models.Orm.Insert(task)
|
||||
if insertErr != nil {
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
+155
-158
@@ -1,158 +1,155 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"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
|
||||
}
|
||||
|
||||
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("用户名或密码错误")
|
||||
}
|
||||
|
||||
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("租户名称、用户名或密码不能为空")
|
||||
}
|
||||
|
||||
var tenant models.SystemTenant
|
||||
err := models.Orm.QueryTable(new(models.SystemTenant)).
|
||||
Filter("tenant_name", tenantName).
|
||||
One(&tenant)
|
||||
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("用户名或密码错误")
|
||||
}
|
||||
|
||||
tenantID := int(tenant.ID)
|
||||
const userType = "backend"
|
||||
token, err := jwtutil.GenerateToken(int(tenantUser.Uid), account, tenantID, userType)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
loginUser := &PlatformLoginUser{
|
||||
ID: tenantUser.Uid,
|
||||
Account: account,
|
||||
Name: "",
|
||||
Tid: tenant.ID,
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
package services
|
||||
|
||||
import (
|
||||
"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
|
||||
}
|
||||
|
||||
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("用户名或密码错误")
|
||||
}
|
||||
|
||||
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("用户名或密码错误")
|
||||
}
|
||||
|
||||
tenantID := int(tenant.ID)
|
||||
const userType = "backend"
|
||||
token, err := jwtutil.GenerateToken(int(tenantUser.Uid), account, tenantID, userType)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
loginUser := &PlatformLoginUser{
|
||||
ID: tenantUser.Uid,
|
||||
Account: account,
|
||||
Name: "",
|
||||
Tid: tenant.ID,
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/beego/beego/v2/client/orm"
|
||||
|
||||
"server/models"
|
||||
)
|
||||
|
||||
/* findTenantByLoginName 根据租户简称、短名或编码查找租户。
|
||||
*
|
||||
* 当前表结构中 tenant_name 的业务含义就是“租户简称”,同时兼容
|
||||
* tenant_short_name 和 tenant_code 两个历史/备用登录标识。
|
||||
*/
|
||||
func findTenantByLoginName(loginName string) (*models.SystemTenant, error) {
|
||||
loginName = strings.TrimSpace(loginName)
|
||||
if loginName == "" {
|
||||
return nil, orm.ErrNoRows
|
||||
}
|
||||
|
||||
// tenant_name 的业务含义是租户简称。使用 TRIM 兼容历史数据中字段值
|
||||
// 前后存在空格的情况;参数通过 SetArgs 绑定,避免 SQL 注入。
|
||||
tenant := &models.SystemTenant{}
|
||||
query := `
|
||||
SELECT id, tenant_code, tenant_name, tenant_short_name,
|
||||
contact_person, contact_phone, contact_email, address,
|
||||
worktime, status, remark, create_time, update_time, delete_time
|
||||
FROM yz_system_tenant
|
||||
WHERE (TRIM(tenant_name) = TRIM(?) OR
|
||||
TRIM(tenant_short_name) = TRIM(?) OR
|
||||
TRIM(tenant_code) = TRIM(?))
|
||||
AND status <> 0
|
||||
ORDER BY id ASC
|
||||
LIMIT 1`
|
||||
err := models.Orm.Raw(query).
|
||||
SetArgs(loginName, loginName, loginName).
|
||||
QueryRow(tenant)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tenant, nil
|
||||
}
|
||||
Reference in New Issue
Block a user