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
|
|||
|
}
|