package service import ( "crypto/md5" "encoding/hex" "fmt" "os" "runtime" "ssq-desk/internal/database" "ssq-desk/internal/storage/models" "ssq-desk/internal/storage/repository" "time" "gorm.io/gorm" ) // AuthService 授权服务 type AuthService struct { repo repository.AuthRepository } // NewAuthService 创建授权服务 func NewAuthService(repo repository.AuthRepository) *AuthService { return &AuthService{repo: repo} } // GetDeviceID 获取设备ID(基于硬件信息生成) func GetDeviceID() (string, error) { var deviceInfo string // 获取主机名 hostname, err := os.Hostname() if err != nil { hostname = "unknown" } // 获取用户目录 homeDir, err := os.UserHomeDir() if err != nil { homeDir = "unknown" } // 组合设备信息 deviceInfo = fmt.Sprintf("%s-%s-%s", hostname, homeDir, runtime.GOOS) // 生成 MD5 作为设备ID hash := md5.Sum([]byte(deviceInfo)) deviceID := hex.EncodeToString(hash[:]) return deviceID, nil } // ValidateLicenseFormat 验证授权码格式 func ValidateLicenseFormat(licenseCode string) error { if licenseCode == "" { return fmt.Errorf("授权码不能为空") } // 去除空格和连字符 cleaned := "" for _, c := range licenseCode { if c != ' ' && c != '-' { cleaned += string(c) } } // 格式验证:至少16位,只包含字母和数字 if len(cleaned) < 16 { return fmt.Errorf("授权码长度不足,至少需要16位字符") } if len(cleaned) > 100 { return fmt.Errorf("授权码长度过长,最多100位字符") } // 验证字符:只允许字母和数字 for _, c := range cleaned { if !((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) { return fmt.Errorf("授权码只能包含字母和数字") } } return nil } // ValidateLicenseFromRemote 从远程数据库验证授权码 func ValidateLicenseFromRemote(licenseCode string) error { // 获取 MySQL 连接 mysqlDB := database.GetMySQL() if mysqlDB == nil { return fmt.Errorf("无法连接远程数据库,无法验证授权码") } // 清理授权码(去除空格和连字符),与格式验证保持一致 cleaned := "" for _, c := range licenseCode { if c != ' ' && c != '-' { cleaned += string(c) } } // 查询授权码是否存在且有效(支持原始格式和清理后格式) var auth models.Authorization err := mysqlDB.Where("(license_code = ? OR license_code = ?) AND status = ?", licenseCode, cleaned, 1).First(&auth).Error if err != nil { if err == gorm.ErrRecordNotFound { return fmt.Errorf("授权码无效或不存在") } return fmt.Errorf("验证授权码时发生错误: %v", err) } // 检查是否过期 if auth.ExpiresAt != nil && auth.ExpiresAt.Before(time.Now()) { return fmt.Errorf("授权码已过期") } return nil } // ValidateLicense 验证授权码 func (s *AuthService) ValidateLicense(licenseCode string) error { // 格式验证 if err := ValidateLicenseFormat(licenseCode); err != nil { return err } // 从远程数据库验证授权码有效性 if err := ValidateLicenseFromRemote(licenseCode); err != nil { return err } // 获取设备ID deviceID, err := GetDeviceID() if err != nil { return fmt.Errorf("获取设备ID失败: %v", err) } // 保存授权信息到本地 auth := &models.Authorization{ LicenseCode: licenseCode, DeviceID: deviceID, ActivatedAt: time.Now(), Status: 1, } // 检查是否已存在 existing, err := s.repo.GetByLicenseCode(licenseCode) if err == nil && existing != nil { // 更新现有授权 existing.DeviceID = deviceID existing.ActivatedAt = time.Now() existing.Status = 1 return s.repo.Update(existing) } // 创建新授权 return s.repo.Create(auth) } // CheckAuthStatus 检查授权状态 func (s *AuthService) CheckAuthStatus() (*AuthStatus, error) { deviceID, err := GetDeviceID() if err != nil { return nil, fmt.Errorf("获取设备ID失败: %v", err) } auth, err := s.repo.GetByDeviceID(deviceID) if err != nil { if err == gorm.ErrRecordNotFound { return &AuthStatus{ IsActivated: false, Message: "未激活", }, nil } return nil, err } // 检查状态 if auth.Status != 1 { return &AuthStatus{ IsActivated: false, Message: "授权已失效", }, nil } // 检查过期时间 if auth.ExpiresAt != nil && auth.ExpiresAt.Before(time.Now()) { return &AuthStatus{ IsActivated: false, Message: "授权已过期", }, nil } return &AuthStatus{ IsActivated: true, LicenseCode: auth.LicenseCode, ActivatedAt: auth.ActivatedAt, ExpiresAt: auth.ExpiresAt, Message: "已激活", }, nil } // AuthStatus 授权状态 type AuthStatus struct { IsActivated bool `json:"is_activated"` LicenseCode string `json:"license_code,omitempty"` ActivatedAt time.Time `json:"activated_at,omitempty"` ExpiresAt *time.Time `json:"expires_at,omitempty"` Message string `json:"message"` } // GetAuthInfo 获取授权信息 func (s *AuthService) GetAuthInfo() (*AuthStatus, error) { return s.CheckAuthStatus() }