| 
									
										
										
										
											2025-07-28 01:46:39 +08:00
										 |  |  |  | package entities | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | import ( | 
					
						
							|  |  |  |  | 	"crypto/rand" | 
					
						
							|  |  |  |  | 	"encoding/hex" | 
					
						
							|  |  |  |  | 	"errors" | 
					
						
							|  |  |  |  | 	"io" | 
					
						
							|  |  |  |  | 	"net" | 
					
						
							|  |  |  |  | 	"time" | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	"github.com/google/uuid" | 
					
						
							|  |  |  |  | 	"gorm.io/gorm" | 
					
						
							|  |  |  |  | ) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // ApiUserStatus API用户状态 | 
					
						
							|  |  |  |  | const ( | 
					
						
							|  |  |  |  | 	ApiUserStatusNormal = "normal" | 
					
						
							|  |  |  |  | 	ApiUserStatusFrozen = "frozen" | 
					
						
							|  |  |  |  | ) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // ApiUser API用户(聚合根) | 
					
						
							|  |  |  |  | type ApiUser struct { | 
					
						
							| 
									
										
										
										
											2025-09-12 01:15:09 +08:00
										 |  |  |  | 	ID        string   `gorm:"primaryKey;type:varchar(64)" json:"id"` | 
					
						
							|  |  |  |  | 	UserId    string   `gorm:"type:varchar(36);not null;uniqueIndex" json:"user_id"` | 
					
						
							|  |  |  |  | 	AccessId  string   `gorm:"type:varchar(64);not null;uniqueIndex" json:"access_id"` | 
					
						
							|  |  |  |  | 	SecretKey string   `gorm:"type:varchar(128);not null" json:"secret_key"` | 
					
						
							|  |  |  |  | 	Status    string   `gorm:"type:varchar(20);not null;default:'normal'" json:"status"` | 
					
						
							|  |  |  |  | 	WhiteList []string `gorm:"type:json;serializer:json;default:'[]'" json:"white_list"` // 支持多个白名单 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	// 余额预警配置 | 
					
						
							|  |  |  |  | 	BalanceAlertEnabled   bool       `gorm:"default:true" json:"balance_alert_enabled" comment:"是否启用余额预警"` | 
					
						
							|  |  |  |  | 	BalanceAlertThreshold float64    `gorm:"default:200.00" json:"balance_alert_threshold" comment:"余额预警阈值"` | 
					
						
							|  |  |  |  | 	AlertPhone            string     `gorm:"type:varchar(20)" json:"alert_phone" comment:"预警手机号"` | 
					
						
							|  |  |  |  | 	LastLowBalanceAlert   *time.Time `json:"last_low_balance_alert" comment:"最后低余额预警时间"` | 
					
						
							|  |  |  |  | 	LastArrearsAlert      *time.Time `json:"last_arrears_alert" comment:"最后欠费预警时间"` | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-28 01:46:39 +08:00
										 |  |  |  | 	CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"` | 
					
						
							|  |  |  |  | 	UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"` | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // IsWhiteListed 校验IP/域名是否在白名单 | 
					
						
							|  |  |  |  | func (u *ApiUser) IsWhiteListed(target string) bool { | 
					
						
							|  |  |  |  | 	for _, w := range u.WhiteList { | 
					
						
							|  |  |  |  | 		if w == target { | 
					
						
							|  |  |  |  | 			return true | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 	return false | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // IsActive 是否可用 | 
					
						
							|  |  |  |  | func (u *ApiUser) IsActive() bool { | 
					
						
							|  |  |  |  | 	return u.Status == ApiUserStatusNormal | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // IsFrozen 是否冻结 | 
					
						
							|  |  |  |  | func (u *ApiUser) IsFrozen() bool { | 
					
						
							|  |  |  |  | 	return u.Status == ApiUserStatusFrozen | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // NewApiUser 工厂方法 | 
					
						
							| 
									
										
										
										
											2025-09-12 01:15:09 +08:00
										 |  |  |  | func NewApiUser(userId string, defaultAlertEnabled bool, defaultAlertThreshold float64) (*ApiUser, error) { | 
					
						
							| 
									
										
										
										
											2025-07-28 01:46:39 +08:00
										 |  |  |  | 	if userId == "" { | 
					
						
							|  |  |  |  | 		return nil, errors.New("用户ID不能为空") | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 	accessId, err := GenerateSecretId() | 
					
						
							|  |  |  |  | 	if err != nil { | 
					
						
							|  |  |  |  | 		return nil, err | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 	secretKey, err := GenerateSecretKey() | 
					
						
							|  |  |  |  | 	if err != nil { | 
					
						
							|  |  |  |  | 		return nil, err | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 	return &ApiUser{ | 
					
						
							| 
									
										
										
										
											2025-09-12 01:15:09 +08:00
										 |  |  |  | 		ID:                    uuid.New().String(), | 
					
						
							|  |  |  |  | 		UserId:                userId, | 
					
						
							|  |  |  |  | 		AccessId:              accessId, | 
					
						
							|  |  |  |  | 		SecretKey:             secretKey, | 
					
						
							|  |  |  |  | 		Status:                ApiUserStatusNormal, | 
					
						
							|  |  |  |  | 		WhiteList:             []string{}, | 
					
						
							|  |  |  |  | 		BalanceAlertEnabled:   defaultAlertEnabled, | 
					
						
							|  |  |  |  | 		BalanceAlertThreshold: defaultAlertThreshold, | 
					
						
							| 
									
										
										
										
											2025-07-28 01:46:39 +08:00
										 |  |  |  | 	}, nil | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // 领域行为 | 
					
						
							|  |  |  |  | func (u *ApiUser) Freeze() { | 
					
						
							|  |  |  |  | 	u.Status = ApiUserStatusFrozen | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | func (u *ApiUser) Unfreeze() { | 
					
						
							|  |  |  |  | 	u.Status = ApiUserStatusNormal | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | func (u *ApiUser) UpdateWhiteList(list []string) { | 
					
						
							|  |  |  |  | 	u.WhiteList = list | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // AddToWhiteList 新增白名单项(防御性校验) | 
					
						
							|  |  |  |  | func (u *ApiUser) AddToWhiteList(entry string) error { | 
					
						
							|  |  |  |  | 	if len(u.WhiteList) >= 10 { | 
					
						
							|  |  |  |  | 		return errors.New("白名单最多只能有10个") | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 	if net.ParseIP(entry) == nil { | 
					
						
							|  |  |  |  | 		return errors.New("非法IP") | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 	for _, w := range u.WhiteList { | 
					
						
							|  |  |  |  | 		if w == entry { | 
					
						
							|  |  |  |  | 			return errors.New("白名单已存在") | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 	u.WhiteList = append(u.WhiteList, entry) | 
					
						
							|  |  |  |  | 	return nil | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // BeforeUpdate GORM钩子:更新前确保WhiteList不为nil | 
					
						
							|  |  |  |  | func (u *ApiUser) BeforeUpdate(tx *gorm.DB) error { | 
					
						
							|  |  |  |  | 	if u.WhiteList == nil { | 
					
						
							|  |  |  |  | 		u.WhiteList = []string{} | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 	return nil | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // RemoveFromWhiteList 删除白名单项 | 
					
						
							|  |  |  |  | func (u *ApiUser) RemoveFromWhiteList(entry string) error { | 
					
						
							|  |  |  |  | 	newList := make([]string, 0, len(u.WhiteList)) | 
					
						
							|  |  |  |  | 	for _, w := range u.WhiteList { | 
					
						
							|  |  |  |  | 		if w != entry { | 
					
						
							|  |  |  |  | 			newList = append(newList, w) | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 	if len(newList) == len(u.WhiteList) { | 
					
						
							|  |  |  |  | 		return errors.New("白名单不存在") | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 	u.WhiteList = newList | 
					
						
							|  |  |  |  | 	return nil | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-12 01:15:09 +08:00
										 |  |  |  | // 余额预警相关方法 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // UpdateBalanceAlertSettings 更新余额预警设置 | 
					
						
							|  |  |  |  | func (u *ApiUser) UpdateBalanceAlertSettings(enabled bool, threshold float64, phone string) error { | 
					
						
							|  |  |  |  | 	if threshold < 0 { | 
					
						
							|  |  |  |  | 		return errors.New("预警阈值不能为负数") | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 	if phone != "" && len(phone) != 11 { | 
					
						
							|  |  |  |  | 		return errors.New("手机号格式不正确") | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	u.BalanceAlertEnabled = enabled | 
					
						
							|  |  |  |  | 	u.BalanceAlertThreshold = threshold | 
					
						
							|  |  |  |  | 	u.AlertPhone = phone | 
					
						
							|  |  |  |  | 	return nil | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // ShouldSendLowBalanceAlert 是否应该发送低余额预警(24小时冷却期) | 
					
						
							|  |  |  |  | func (u *ApiUser) ShouldSendLowBalanceAlert(balance float64) bool { | 
					
						
							|  |  |  |  | 	if !u.BalanceAlertEnabled || u.AlertPhone == "" { | 
					
						
							|  |  |  |  | 		return false | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	// 余额低于阈值 | 
					
						
							|  |  |  |  | 	if balance < u.BalanceAlertThreshold { | 
					
						
							|  |  |  |  | 		// 检查是否已经发送过预警(避免频繁发送) | 
					
						
							|  |  |  |  | 		if u.LastLowBalanceAlert != nil { | 
					
						
							|  |  |  |  | 			// 如果距离上次预警不足24小时,不发送 | 
					
						
							|  |  |  |  | 			if time.Since(*u.LastLowBalanceAlert) < 24*time.Hour { | 
					
						
							|  |  |  |  | 				return false | 
					
						
							|  |  |  |  | 			} | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 		return true | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 	return false | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // ShouldSendArrearsAlert 是否应该发送欠费预警(不受冷却期限制) | 
					
						
							|  |  |  |  | func (u *ApiUser) ShouldSendArrearsAlert(balance float64) bool { | 
					
						
							|  |  |  |  | 	if !u.BalanceAlertEnabled || u.AlertPhone == "" { | 
					
						
							|  |  |  |  | 		return false | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	// 余额为负数(欠费)- 欠费预警不受冷却期限制 | 
					
						
							|  |  |  |  | 	if balance < 0 { | 
					
						
							|  |  |  |  | 		return true | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 	return false | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // MarkLowBalanceAlertSent 标记低余额预警已发送 | 
					
						
							|  |  |  |  | func (u *ApiUser) MarkLowBalanceAlertSent() { | 
					
						
							|  |  |  |  | 	now := time.Now() | 
					
						
							|  |  |  |  | 	u.LastLowBalanceAlert = &now | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // MarkArrearsAlertSent 标记欠费预警已发送 | 
					
						
							|  |  |  |  | func (u *ApiUser) MarkArrearsAlertSent() { | 
					
						
							|  |  |  |  | 	now := time.Now() | 
					
						
							|  |  |  |  | 	u.LastArrearsAlert = &now | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-28 01:46:39 +08:00
										 |  |  |  | // Validate 校验ApiUser聚合根的业务规则 | 
					
						
							|  |  |  |  | func (u *ApiUser) Validate() error { | 
					
						
							|  |  |  |  | 	if u.UserId == "" { | 
					
						
							|  |  |  |  | 		return errors.New("用户ID不能为空") | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 	if u.AccessId == "" { | 
					
						
							|  |  |  |  | 		return errors.New("AccessId不能为空") | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 	if u.SecretKey == "" { | 
					
						
							|  |  |  |  | 		return errors.New("SecretKey不能为空") | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 	switch u.Status { | 
					
						
							|  |  |  |  | 	case ApiUserStatusNormal, ApiUserStatusFrozen: | 
					
						
							|  |  |  |  | 		// ok | 
					
						
							|  |  |  |  | 	default: | 
					
						
							|  |  |  |  | 		return errors.New("无效的用户状态") | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 	if len(u.WhiteList) > 10 { | 
					
						
							|  |  |  |  | 		return errors.New("白名单最多只能有10个") | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 	for _, ip := range u.WhiteList { | 
					
						
							|  |  |  |  | 		if net.ParseIP(ip) == nil { | 
					
						
							|  |  |  |  | 			return errors.New("白名单项必须为合法IP地址: " + ip) | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 	return nil | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // 生成AES-128密钥的函数,符合市面规范 | 
					
						
							|  |  |  |  | func GenerateSecretKey() (string, error) { | 
					
						
							|  |  |  |  | 	key := make([]byte, 16) // 16字节密钥 | 
					
						
							|  |  |  |  | 	_, err := io.ReadFull(rand.Reader, key) | 
					
						
							|  |  |  |  | 	if err != nil { | 
					
						
							|  |  |  |  | 		return "", err | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 	return hex.EncodeToString(key), nil | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | func GenerateSecretId() (string, error) { | 
					
						
							|  |  |  |  | 	// 创建一个字节数组,用于存储随机数据 | 
					
						
							|  |  |  |  | 	bytes := make([]byte, 8) // 因为每个字节表示两个16进制字符 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	// 读取随机字节到数组中 | 
					
						
							|  |  |  |  | 	_, err := rand.Read(bytes) | 
					
						
							|  |  |  |  | 	if err != nil { | 
					
						
							|  |  |  |  | 		return "", err | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	// 将字节数组转换为16进制字符串 | 
					
						
							|  |  |  |  | 	return hex.EncodeToString(bytes), nil | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // TableName 指定数据库表名 | 
					
						
							|  |  |  |  | func (ApiUser) TableName() string { | 
					
						
							|  |  |  |  | 	return "api_users" | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // BeforeCreate GORM钩子:创建前自动生成UUID并确保WhiteList不为nil | 
					
						
							|  |  |  |  | func (c *ApiUser) BeforeCreate(tx *gorm.DB) error { | 
					
						
							|  |  |  |  | 	if c.ID == "" { | 
					
						
							|  |  |  |  | 		c.ID = uuid.New().String() | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 	if c.WhiteList == nil { | 
					
						
							|  |  |  |  | 		c.WhiteList = []string{} | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 	return nil | 
					
						
							|  |  |  |  | } |