This commit is contained in:
2026-01-14 14:17:38 +08:00
commit f1e2ff6563
126 changed files with 13636 additions and 0 deletions

View File

@@ -0,0 +1,215 @@
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()
}