710 lines
12 KiB
Go
710 lines
12 KiB
Go
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
|
||
}
|