131 lines
3.6 KiB
Go
131 lines
3.6 KiB
Go
package repository
|
||
|
||
import (
|
||
"errors"
|
||
"sort"
|
||
"strings"
|
||
|
||
"hyapp-admin-server/internal/appctx"
|
||
"hyapp-admin-server/internal/model"
|
||
|
||
"gorm.io/gorm"
|
||
)
|
||
|
||
type AppAccess struct {
|
||
Mode string
|
||
AppCodes []string
|
||
}
|
||
|
||
func (access AppAccess) Allows(appCode string) bool {
|
||
appCode = strings.ToLower(strings.TrimSpace(appCode))
|
||
if appCode == "" {
|
||
return false
|
||
}
|
||
if access.Mode == model.UserAppScopeModeAll {
|
||
return true
|
||
}
|
||
for _, allowed := range access.AppCodes {
|
||
if allowed == appCode {
|
||
return true
|
||
}
|
||
}
|
||
return false
|
||
}
|
||
|
||
func (s *Store) AppAccessForUser(userID uint) (AppAccess, error) {
|
||
if userID == 0 {
|
||
return AppAccess{Mode: model.UserAppScopeModeNone}, nil
|
||
}
|
||
var user model.User
|
||
if err := s.db.Select("id", "app_scope_mode").First(&user, userID).Error; err != nil {
|
||
return AppAccess{}, err
|
||
}
|
||
mode := normalizeAppScopeMode(user.AppScopeMode)
|
||
if mode != model.UserAppScopeModeSelected {
|
||
return AppAccess{Mode: mode}, nil
|
||
}
|
||
var scopes []model.UserAppScope
|
||
if err := s.db.Where("user_id = ?", userID).Order("app_code ASC").Find(&scopes).Error; err != nil {
|
||
return AppAccess{}, err
|
||
}
|
||
appCodes := make([]string, 0, len(scopes))
|
||
for _, scope := range scopes {
|
||
appCodes = append(appCodes, scope.AppCode)
|
||
}
|
||
return AppAccess{Mode: mode, AppCodes: appCodes}, nil
|
||
}
|
||
|
||
func (s *Store) ReplaceUserAppScopes(userID uint, mode string, appCodes []string) (AppAccess, error) {
|
||
if userID == 0 {
|
||
return AppAccess{}, errors.New("user id is required")
|
||
}
|
||
rawMode := strings.ToLower(strings.TrimSpace(mode))
|
||
if rawMode != model.UserAppScopeModeAll && rawMode != model.UserAppScopeModeSelected && rawMode != model.UserAppScopeModeNone {
|
||
return AppAccess{}, errors.New("app scope mode must be all, selected, or none")
|
||
}
|
||
mode = rawMode
|
||
normalizedCodes := normalizeAppCodes(appCodes)
|
||
if mode == model.UserAppScopeModeSelected && len(normalizedCodes) == 0 {
|
||
return AppAccess{}, errors.New("selected app scope requires app codes")
|
||
}
|
||
if mode != model.UserAppScopeModeSelected {
|
||
normalizedCodes = nil
|
||
}
|
||
|
||
err := s.db.Transaction(func(tx *gorm.DB) error {
|
||
var user model.User
|
||
if err := tx.First(&user, userID).Error; err != nil {
|
||
return err
|
||
}
|
||
// 先删除旧明细再更新模式,使 all/none 不残留会被未来代码误读的 selected 行。
|
||
if err := tx.Where("user_id = ?", userID).Delete(&model.UserAppScope{}).Error; err != nil {
|
||
return err
|
||
}
|
||
if len(normalizedCodes) > 0 {
|
||
scopes := make([]model.UserAppScope, 0, len(normalizedCodes))
|
||
for _, appCode := range normalizedCodes {
|
||
scopes = append(scopes, model.UserAppScope{UserID: userID, AppCode: appCode})
|
||
}
|
||
if err := tx.Create(&scopes).Error; err != nil {
|
||
return err
|
||
}
|
||
}
|
||
return tx.Model(&user).Update("app_scope_mode", mode).Error
|
||
})
|
||
if err != nil {
|
||
return AppAccess{}, err
|
||
}
|
||
return AppAccess{Mode: mode, AppCodes: normalizedCodes}, nil
|
||
}
|
||
|
||
func normalizeAppScopeMode(mode string) string {
|
||
switch strings.ToLower(strings.TrimSpace(mode)) {
|
||
case model.UserAppScopeModeAll:
|
||
return model.UserAppScopeModeAll
|
||
case model.UserAppScopeModeSelected:
|
||
return model.UserAppScopeModeSelected
|
||
default:
|
||
return model.UserAppScopeModeNone
|
||
}
|
||
}
|
||
|
||
func normalizeAppCodes(appCodes []string) []string {
|
||
seen := make(map[string]struct{}, len(appCodes))
|
||
out := make([]string, 0, len(appCodes))
|
||
for _, value := range appCodes {
|
||
// appctx.Normalize 会把空值回退 lalu;App scope 的空值代表无授权,必须先独立过滤。
|
||
value = strings.ToLower(strings.TrimSpace(value))
|
||
if value == "" {
|
||
continue
|
||
}
|
||
value = appctx.Normalize(value)
|
||
if _, ok := seen[value]; ok {
|
||
continue
|
||
}
|
||
seen[value] = struct{}{}
|
||
out = append(out, value)
|
||
}
|
||
sort.Strings(out)
|
||
return out
|
||
}
|