实现多商户履约平台基础

This commit is contained in:
yml2213
2026-07-30 12:02:32 +08:00
parent dde6f2c269
commit 83429456a6
41 changed files with 5487 additions and 504 deletions
+120
View File
@@ -0,0 +1,120 @@
package service
import (
"errors"
"strconv"
"strings"
"affiliate_dash/internal/model"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
// TenantService 负责商户成员关系与请求租户解析。
type TenantService struct {
db *gorm.DB
}
func NewTenantService(db *gorm.DB) *TenantService {
return &TenantService{db: db}
}
func (s *TenantService) ResolveMember(userID uint, merchantRef string) (*model.MerchantMember, error) {
if userID == 0 {
return nil, errors.New("无效的用户身份")
}
tx := s.db.Preload("Merchant").
Where("merchant_members.user_id = ? AND merchant_members.status = ?", userID, 1).
Joins("JOIN merchants ON merchants.id = merchant_members.merchant_id AND merchants.status = ?", model.MerchantStatusActive)
if merchantRef != "" {
if id, err := strconv.ParseUint(merchantRef, 10, 64); err == nil {
tx = tx.Where("merchant_members.merchant_id = ?", uint(id))
} else {
tx = tx.Where("merchants.code = ?", merchantRef)
}
}
var member model.MerchantMember
err := tx.Order("merchant_members.is_default DESC, merchant_members.id ASC").First(&member).Error
if err == nil {
return &member, nil
}
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, errors.New("当前账号无权访问该商户")
}
return nil, err
}
func (s *TenantService) EnsureSelfMember(userID uint, role string, status int) error {
var merchant model.Merchant
if err := s.db.Where("code = ?", "self-operated").First(&merchant).Error; err != nil {
return err
}
return s.EnsureMember(merchant.ID, userID, role, status, role == model.MemberRoleOwner)
}
func (s *TenantService) EnsureMember(merchantID, userID uint, role string, status int, isDefault bool) error {
if merchantID == 0 || userID == 0 {
return errors.New("商户和用户不能为空")
}
if !isValidMemberRole(role) {
return errors.New("无效的商户成员角色")
}
member := model.MerchantMember{
MerchantID: merchantID,
UserID: userID,
Role: role,
Status: status,
IsDefault: isDefault,
}
return s.db.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "merchant_id"}, {Name: "user_id"}},
DoUpdates: clause.Assignments(map[string]interface{}{
"role": role,
"status": status,
"is_default": isDefault,
}),
}).Create(&member).Error
}
func isValidMemberRole(role string) bool {
switch role {
case model.MemberRoleOwner, model.MemberRoleOperator, model.MemberRoleFinance, model.MemberRoleViewer:
return true
default:
return false
}
}
func MemberCanManage(memberRole string) bool {
return memberRole == model.MemberRoleOwner || memberRole == model.MemberRoleOperator
}
func MemberCanManageFinance(memberRole string) bool {
return memberRole == model.MemberRoleOwner || memberRole == model.MemberRoleFinance
}
func ParseScopes(scopes string) map[string]struct{} {
out := make(map[string]struct{})
for _, scope := range strings.FieldsFunc(scopes, func(r rune) bool {
return r == ',' || r == ' ' || r == '\n' || r == '\t'
}) {
if scope != "" {
out[scope] = struct{}{}
}
}
return out
}
func HasAnyScope(scopes string, wanted ...string) bool {
set := ParseScopes(scopes)
if _, ok := set["*"]; ok {
return true
}
for _, scope := range wanted {
if _, ok := set[scope]; ok {
return true
}
}
return false
}