hyapp-server/server/admin/internal/repository/money_scope_repository.go
2026-06-28 23:24:57 +08:00

216 lines
5.4 KiB
Go

package repository
import (
"errors"
"strconv"
"strings"
"time"
"hyapp-admin-server/internal/appctx"
"hyapp-admin-server/internal/model"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
type MoneyAccess struct {
UserID uint
All bool
Scopes []model.UserMoneyScope
}
func (access MoneyAccess) Allows(appCode string, regionID int64) bool {
if access.All {
return true
}
appCode = appctx.Normalize(appCode)
for _, scope := range access.Scopes {
if appctx.Normalize(scope.AppCode) != appCode {
continue
}
if scope.RegionID == 0 || scope.RegionID == regionID {
return true
}
}
return false
}
func (access MoneyAccess) AppCodes() []string {
if access.All {
return nil
}
set := map[string]struct{}{}
for _, scope := range access.Scopes {
appCode := appctx.Normalize(scope.AppCode)
if appCode != "" {
set[appCode] = struct{}{}
}
}
out := make([]string, 0, len(set))
for appCode := range set {
out = append(out, appCode)
}
return out
}
func (s *Store) MoneyAccessForUser(userID uint) (MoneyAccess, error) {
if userID == 0 {
return MoneyAccess{All: true}, nil
}
var user model.User
if err := s.db.Preload("Roles").First(&user, userID).Error; err != nil {
return MoneyAccess{}, err
}
scopes, err := s.ListUserMoneyScopes(userID)
if err != nil {
return MoneyAccess{}, err
}
if len(scopes) == 0 && userHasRole(user, "platform-admin") {
return MoneyAccess{UserID: userID, All: true}, nil
}
return MoneyAccess{UserID: userID, Scopes: scopes}, nil
}
func (s *Store) ListUserMoneyScopes(userID uint) ([]model.UserMoneyScope, error) {
var scopes []model.UserMoneyScope
err := s.db.Where("user_id = ?", userID).Order("app_code ASC, region_id ASC").Find(&scopes).Error
return scopes, err
}
func (s *Store) ReplaceUserMoneyScopes(userID uint, scopes []model.UserMoneyScope) error {
if userID == 0 {
return errors.New("user id is required")
}
normalized, err := normalizeMoneyScopes(userID, scopes)
if err != nil {
return err
}
return s.db.Transaction(func(tx *gorm.DB) error {
var user model.User
if err := tx.First(&user, userID).Error; err != nil {
return err
}
if err := tx.Where("user_id = ?", userID).Delete(&model.UserMoneyScope{}).Error; err != nil {
return err
}
if len(normalized) == 0 {
return nil
}
return tx.Create(&normalized).Error
})
}
func (s *Store) RecordTemporaryPaymentLinkOwner(owner model.TemporaryPaymentLinkOwner) error {
owner.AppCode = appctx.Normalize(owner.AppCode)
owner.OrderID = strings.TrimSpace(owner.OrderID)
if owner.AppCode == "" || owner.OrderID == "" || owner.AdminUserID == 0 || owner.RegionID <= 0 {
return errors.New("temporary payment link owner is incomplete")
}
nowMS := time.Now().UTC().UnixMilli()
if owner.CreatedAtMS == 0 {
owner.CreatedAtMS = nowMS
}
owner.UpdatedAtMS = nowMS
return s.db.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "app_code"}, {Name: "order_id"}},
DoUpdates: clause.AssignmentColumns([]string{
"admin_user_id",
"region_id",
"updated_at_ms",
}),
}).Create(&owner).Error
}
func (s *Store) TemporaryPaymentLinkOwners(appCode string, orderIDs []string) (map[string]model.TemporaryPaymentLinkOwner, error) {
appCode = appctx.Normalize(appCode)
ids := uniqueStrings(orderIDs)
out := make(map[string]model.TemporaryPaymentLinkOwner, len(ids))
if appCode == "" || len(ids) == 0 {
return out, nil
}
var owners []model.TemporaryPaymentLinkOwner
if err := s.db.Where("app_code = ? AND order_id IN ?", appCode, ids).Find(&owners).Error; err != nil {
return nil, err
}
for _, owner := range owners {
out[owner.OrderID] = owner
}
return out, nil
}
func (s *Store) FindUsersByIDs(ids []uint) (map[uint]model.User, error) {
uniqueIDs := uniqueUints(ids)
out := make(map[uint]model.User, len(uniqueIDs))
if len(uniqueIDs) == 0 {
return out, nil
}
var users []model.User
if err := s.db.Where("id IN ?", uniqueIDs).Find(&users).Error; err != nil {
return nil, err
}
for _, user := range users {
out[user.ID] = user
}
return out, nil
}
func normalizeMoneyScopes(userID uint, scopes []model.UserMoneyScope) ([]model.UserMoneyScope, error) {
seen := map[string]struct{}{}
out := make([]model.UserMoneyScope, 0, len(scopes))
for _, scope := range scopes {
appCode := appctx.Normalize(scope.AppCode)
if appCode == "" || scope.RegionID < 0 {
return nil, errors.New("money scope app code and region id are required")
}
key := appCode + ":" + strconv.FormatInt(scope.RegionID, 10)
if _, ok := seen[key]; ok {
continue
}
seen[key] = struct{}{}
out = append(out, model.UserMoneyScope{UserID: userID, AppCode: appCode, RegionID: scope.RegionID})
}
return out, nil
}
func userHasRole(user model.User, code string) bool {
for _, role := range user.Roles {
if role.Code == code {
return true
}
}
return false
}
func uniqueStrings(values []string) []string {
set := map[string]struct{}{}
out := make([]string, 0, len(values))
for _, value := range values {
value = strings.TrimSpace(value)
if value == "" {
continue
}
if _, ok := set[value]; ok {
continue
}
set[value] = struct{}{}
out = append(out, value)
}
return out
}
func uniqueUints(values []uint) []uint {
set := map[uint]struct{}{}
out := make([]uint, 0, len(values))
for _, value := range values {
if value == 0 {
continue
}
if _, ok := set[value]; ok {
continue
}
set[value] = struct{}{}
out = append(out, value)
}
return out
}