LicenseManger/internal/service/license.go

710 lines
12 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

package service
import (
"crypto/rand"
"encoding/hex"
"errors"
"fmt"
"strings"
"time"
"licserver/internal/model"
"gorm.io/gorm"
)
type LicenseService struct {
db *gorm.DB
}
func NewLicenseService(db *gorm.DB) *LicenseService {
return &LicenseService{db: db}
}
// 生成授权码
func generateLicenseCode() (string, error) {
b := make([]byte, 16)
if _, err := rand.Read(b); err != nil {
return "", err
}
return hex.EncodeToString(b), nil
}
// 创建授权码
type LicenseCreateInput struct {
LicenseType string `json:"license_type" binding:"required"`
Duration int `json:"duration"` // 时间授权的有效期(分钟)
MaxUses int `json:"max_uses"` // 次数授权的使用次数
Count int `json:"count" binding:"required,min=1"` // 生成数量
Remark string `json:"remark"` // 备注
BindCount int `json:"bind_count"` // 可绑定次数,默认为-1无限制
}
func (s *LicenseService) CreateLicenses(input *LicenseCreateInput, createdBy uint) ([]model.LicenseCode, error) {
// 验证参数
input.LicenseType = strings.ToLower(input.LicenseType) // 转为小写
switch input.LicenseType {
case "time":
if input.Duration <= 0 {
return nil, errors.New("时间授权必须指定有效期")
}
case "count":
if input.MaxUses <= 0 {
return nil, errors.New("次数授权必须指定使用次数")
}
case "permanent":
// 永久授权不需要额外参数
default:
return nil, errors.New("无效的授权类型")
}
// 如果未指定绑定次数,设置为默认值-1
if input.BindCount == 0 {
input.BindCount = -1
}
// 生成批次号
batchNo := time.Now().Format("20060102150405")
licenses := make([]model.LicenseCode, 0, input.Count)
for i := 0; i < input.Count; i++ {
code, err := generateLicenseCode()
if err != nil {
return nil, err
}
license := model.LicenseCode{
Code: code,
LicenseType: input.LicenseType,
Duration: input.Duration,
MaxUses: input.MaxUses,
Status: "unused",
CreatedBy: createdBy,
BatchNo: batchNo,
Remark: input.Remark,
BindCount: input.BindCount,
}
licenses = append(licenses, license)
}
// 批量创建授权码
if err := s.db.Create(&licenses).Error; err != nil {
return nil, err
}
return licenses, nil
}
// 验证并使用授权码
func (s *LicenseService) UseLicense(code, deviceUID, ip string) (*model.LicenseCode, error) {
var license model.LicenseCode
if err := s.db.Where("code = ?", code).First(&license).Error; err != nil {
return nil, errors.New("授权码不存在")
}
// 检查授权码状态
if license.Status != "unused" {
return nil, errors.New("授权码已被使用")
}
// 检查绑定次数
if license.BindCount == 0 {
return nil, errors.New("授权码已达到最大绑定次数限制")
}
// 更新授权码状态
updates := map[string]interface{}{
"status": "used",
"used_by": deviceUID,
"used_at": time.Now(),
}
// 如果不是无限制,减少绑定次数
if license.BindCount > 0 {
updates["bind_count"] = license.BindCount - 1
}
if err := s.db.Model(&license).Updates(updates).Error; err != nil {
return nil, err
}
// 记录使用日志
log := model.LicenseLog{
LicenseID: license.ID,
DeviceUID: deviceUID,
Action: "use",
IP: ip,
Status: "success",
Message: fmt.Sprintf("设备 %s 使用授权码", deviceUID),
}
if err := s.db.Create(&log).Error; err != nil {
return nil, err
}
return &license, nil
}
// 获取授权码列表
func (s *LicenseService) GetLicenses(status, licenseType, batchNo string, page, pageSize int) ([]model.LicenseCode, int64, error) {
var licenses []model.LicenseCode
var total int64
query := s.db.Model(&model.LicenseCode{})
if status != "" {
query = query.Where("status = ?", strings.ToLower(status))
}
if licenseType != "" {
query = query.Where("license_type = ?", strings.ToLower(licenseType))
}
if batchNo != "" {
query = query.Where("batch_no = ?", batchNo)
}
// 获取所有符合条件的授权码
var allLicenses []model.LicenseCode
if err := query.Find(&allLicenses).Error; err != nil {
return nil, 0, err
}
// 检查每个授权码的有效性
for i := range allLicenses {
if allLicenses[i].Status == "used" {
if err := s.CheckLicenseValidity(allLicenses[i].Code); err != nil {
// 如果检查失败,更新状态
s.db.Model(&allLicenses[i]).Update("status", "expired")
allLicenses[i].Status = "expired"
}
}
}
total = int64(len(allLicenses))
// 分页
if page > 0 && pageSize > 0 {
start := (page - 1) * pageSize
end := start + pageSize
if start < len(allLicenses) {
if end > len(allLicenses) {
end = len(allLicenses)
}
licenses = allLicenses[start:end]
}
} else {
licenses = allLicenses
}
return licenses, total, nil
}
// 获取授权码使用日志
func (s *LicenseService) GetLicenseLogs(licenseID uint, page, pageSize int) ([]model.LicenseLog, int64, error) {
var logs []model.LicenseLog
var total int64
query := s.db.Model(&model.LicenseLog{}).Where("license_id = ?", licenseID)
query.Count(&total)
if page > 0 && pageSize > 0 {
offset := (page - 1) * pageSize
query = query.Offset(offset).Limit(pageSize)
}
err := query.Order("created_at DESC").Find(&logs).Error
return logs, total, err
}
// ExportLogs 导出授权码日志
func (s *LicenseService) ExportLogs(licenseID uint) ([]byte, error) {
logs, _, err := s.GetLicenseLogs(licenseID, 0, 0) // 获取所有日志
if err != nil {
return nil, err
}
// 创建CSV内容
var content strings.Builder
content.WriteString("操作类,设备UID,IP地址,状态,详细信息,时间\n")
for _, log := range logs {
// 转换操作类型
action := map[string]string{
"create": "创建",
"use": "使用",
"verify": "验证",
}[log.Action]
// 转换状态
status := map[string]string{
"success": "成功",
"failed": "失败",
}[log.Status]
// 写入一行记录
content.WriteString(fmt.Sprintf("%s,%s,%s,%s,%s,%s\n",
action,
log.DeviceUID,
log.IP,
status,
log.Message,
log.CreatedAt.Format("2006-01-02 15:04:05"),
))
}
return []byte(content.String()), nil
}
// 撤销授权码
func (s *LicenseService) RevokeLicense(code string, userID uint) error {
var license model.LicenseCode
if err := s.db.Where("code = ?", code).First(&license).Error; err != nil {
return errors.New("授权码不存在")
}
// 检查权限
if license.CreatedBy != userID {
return errors.New("无权操作此授权码")
}
// 更新状态
if err := s.db.Model(&license).Update("status", "revoked").Error; err != nil {
return err
}
// 记录日志
log := model.LicenseLog{
LicenseID: license.ID,
Action: "revoke",
Status: "success",
Message: "授权码已撤销",
}
s.db.Create(&log)
return nil
}
// 批量撤销授权码
func (s *LicenseService) RevokeLicenses(codes []string, userID uint) error {
return s.db.Transaction(func(tx *gorm.DB) error {
for _, code := range codes {
var license model.LicenseCode
if err := tx.Where("code = ?", code).First(&license).Error; err != nil {
continue
}
// 检查权限
if license.CreatedBy != userID {
continue
}
// 更新状态
if err := tx.Model(&license).Update("status", "revoked").Error; err != nil {
return err
}
// 记录日志
log := model.LicenseLog{
LicenseID: license.ID,
Action: "revoke",
Status: "success",
Message: "授权码已撤销",
}
tx.Create(&log)
}
return nil
})
}
// 验证授权码
func (s *LicenseService) ValidateLicense(code string) (*model.LicenseCode, error) {
var license model.LicenseCode
if err := s.db.Where("code = ?", code).First(&license).Error; err != nil {
return nil, errors.New("无效的授权码")
}
// 检查状态
if license.Status != "unused" {
return nil, errors.New("授权码已被使用或已撤销")
}
return &license, nil
}
// 导出授权码
func (s *LicenseService) ExportLicenses(codes []string) ([]byte, error) {
var licenses []model.LicenseCode
if err := s.db.Where("code IN ?", codes).Find(&licenses).Error; err != nil {
return nil, err
}
// 创建CSV内容
var content strings.Builder
content.WriteString("授权码,授权类型,有效期(天),使用次数,状态,使用设备,使用时间,批次号,备注\n")
for _, license := range licenses {
// 转换授权类型
licenseType := map[string]string{
"time": "时间授权",
"count": "次数授权",
"permanent": "永久授权",
}[license.LicenseType]
// 转换状态
status := map[string]string{
"unused": "未使用",
"used": "已使用",
"revoked": "已撤销",
}[license.Status]
// 写入一行记录
content.WriteString(fmt.Sprintf("%s,%s,%d,%d,%s,%s,%s,%s,%s\n",
license.Code,
licenseType,
license.Duration,
license.MaxUses,
status,
license.UsedBy,
license.UsedAt.Format("2006-01-02 15:04:05"),
license.BatchNo,
license.Remark,
))
}
return []byte(content.String()), nil
}
// 获取授权码统计信息
func (s *LicenseService) GetLicenseStats() (map[string]interface{}, error) {
var stats struct {
Total int64
Unused int64
Used int64
Revoked int64
Today int64
ThisWeek int64
ThisMonth int64
}
// 获取总数
s.db.Model(&model.LicenseCode{}).Count(&stats.Total)
// 获取各状态数量
s.db.Model(&model.LicenseCode{}).Where("status = ?", "unused").Count(&stats.Unused)
s.db.Model(&model.LicenseCode{}).Where("status = ?", "used").Count(&stats.Used)
s.db.Model(&model.LicenseCode{}).Where("status = ?", "revoked").Count(&stats.Revoked)
// 获取今日创建数量
today := time.Now().Format("2006-01-02")
s.db.Model(&model.LicenseCode{}).Where("DATE(created_at) = ?", today).Count(&stats.Today)
// 获取本周创建数量
weekStart := time.Now().AddDate(0, 0, -int(time.Now().Weekday()))
s.db.Model(&model.LicenseCode{}).Where("created_at >= ?", weekStart).Count(&stats.ThisWeek)
// 获取本月创建数量
monthStart := time.Now().Format("2006-01") + "-01"
s.db.Model(&model.LicenseCode{}).Where("created_at >= ?", monthStart).Count(&stats.ThisMonth)
return map[string]interface{}{
"total": stats.Total,
"unused": stats.Unused,
"used": stats.Used,
"revoked": stats.Revoked,
"today": stats.Today,
"this_week": stats.ThisWeek,
"this_month": stats.ThisMonth,
}, nil
}
// 添加检查授权码有效性的方法
func (s *LicenseService) CheckLicenseValidity(code string) error {
var license model.LicenseCode
if err := s.db.Where("code = ?", code).First(&license).Error; err != nil {
return errors.New("授权码不存在")
}
if license.Status != "unused" && license.Status != "used" {
return errors.New("授权码已被撤销或过期")
}
// 检查授权类型特定的限制
switch license.LicenseType {
case "time":
// 计算过期时间
expireTime := license.UsedAt.Add(time.Duration(license.Duration) * time.Minute)
if time.Now().After(expireTime) {
// 更新状态为过期
s.db.Model(&license).Update("status", "expired")
return errors.New("授权码已过期")
}
case "count":
if license.UsedCount >= license.MaxUses {
// 更新状态为过期
s.db.Model(&license).Update("status", "expired")
return errors.New("授权码使用次数已达上限")
}
}
return nil
}
// GetLicenseByCode 通过授权码获取授权信息
func (s *LicenseService) GetLicenseByCode(code string) (*model.LicenseCode, error) {
var license model.LicenseCode
if err := s.db.Where("code = ?", code).First(&license).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, errors.New("授权码不存在")
}
return nil, err
}
return &license, nil
}