270 lines
7.3 KiB
Go
270 lines
7.3 KiB
Go
package services
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"encoding/base64"
|
|
"encoding/hex"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
qr "github.com/yeqown/go-qrcode/v2"
|
|
"github.com/yeqown/go-qrcode/writer/standard"
|
|
|
|
"git.beifan.cn/trace-system/backend-go/database"
|
|
"git.beifan.cn/trace-system/backend-go/models"
|
|
)
|
|
|
|
// SerialsService 序列号服务
|
|
type SerialsService struct{}
|
|
|
|
// Generate 生成序列号
|
|
func (s *SerialsService) Generate(
|
|
companyName string,
|
|
quantity int,
|
|
validDays int,
|
|
userId uint,
|
|
prefix ...string,
|
|
) ([]models.Serial, error) {
|
|
var serials []models.Serial
|
|
validUntil := time.Now().AddDate(0, 0, validDays)
|
|
|
|
// 检查公司是否存在,不存在则创建
|
|
var company models.Company
|
|
result := database.DB.Where("company_name = ?", companyName).First(&company)
|
|
if result.Error != nil {
|
|
company = models.Company{
|
|
CompanyName: companyName,
|
|
IsActive: true,
|
|
}
|
|
result = database.DB.Create(&company)
|
|
if result.Error != nil {
|
|
return nil, fmt.Errorf("创建公司失败: %w", result.Error)
|
|
}
|
|
}
|
|
|
|
// 生成序列号前缀
|
|
var serialPrefix string
|
|
if len(prefix) > 0 && prefix[0] != "" {
|
|
serialPrefix = strings.ToUpper(strings.ReplaceAll(prefix[0], "[^A-Z0-9]", ""))
|
|
} else {
|
|
serialPrefix = fmt.Sprintf("BF%d", time.Now().Year()%100)
|
|
}
|
|
|
|
// 预生成所有序列号
|
|
serialNumbers := make(map[string]bool)
|
|
for i := 0; i < quantity; {
|
|
randomBytes := make([]byte, 3)
|
|
if _, err := rand.Read(randomBytes); err != nil {
|
|
return nil, fmt.Errorf("生成随机数失败: %w", err)
|
|
}
|
|
randomPart := hex.EncodeToString(randomBytes)[:6]
|
|
serialNumber := fmt.Sprintf("%s%s", serialPrefix, randomPart)
|
|
|
|
if serialNumbers[serialNumber] {
|
|
continue
|
|
}
|
|
|
|
var existingSerial models.Serial
|
|
checkResult := database.DB.Where("serial_number = ?", serialNumber).First(&existingSerial)
|
|
if checkResult.Error != nil {
|
|
serialNumbers[serialNumber] = true
|
|
i++
|
|
}
|
|
}
|
|
|
|
for serialNumber := range serialNumbers {
|
|
serial := models.Serial{
|
|
SerialNumber: strings.ToUpper(serialNumber),
|
|
CompanyName: companyName,
|
|
ValidUntil: &validUntil,
|
|
CreatedBy: &userId,
|
|
IsActive: true,
|
|
}
|
|
serials = append(serials, serial)
|
|
}
|
|
|
|
// 保存到数据库
|
|
result = database.DB.Create(&serials)
|
|
if result.Error != nil {
|
|
return nil, fmt.Errorf("保存序列号失败: %w", result.Error)
|
|
}
|
|
|
|
return serials, nil
|
|
}
|
|
|
|
// GenerateQRCode 生成二维码
|
|
func (s *SerialsService) GenerateQRCode(
|
|
serialNumber string,
|
|
baseUrl string,
|
|
requestHost string,
|
|
protocol string,
|
|
) (string, string, error) {
|
|
var serial models.Serial
|
|
result := database.DB.Preload("User").Where("serial_number = ?", strings.ToUpper(serialNumber)).First(&serial)
|
|
if result.Error != nil {
|
|
return "", "", fmt.Errorf("查询序列号失败: %w", errors.New("序列号不存在"))
|
|
}
|
|
|
|
if !serial.IsActive {
|
|
return "", "", fmt.Errorf("序列号状态无效: %w", errors.New("序列号已被禁用"))
|
|
}
|
|
|
|
if serial.ValidUntil != nil && serial.ValidUntil.Before(time.Now()) {
|
|
return "", "", fmt.Errorf("序列号已过期")
|
|
}
|
|
|
|
// 确定查询 URL
|
|
if baseUrl == "" {
|
|
baseUrl = fmt.Sprintf("%s://%s/query.html", protocol, requestHost)
|
|
}
|
|
|
|
var queryUrl string
|
|
if strings.Contains(baseUrl, "?") {
|
|
queryUrl = fmt.Sprintf("%s&serial=%s", baseUrl, serial.SerialNumber)
|
|
} else {
|
|
queryUrl = fmt.Sprintf("%s?serial=%s", baseUrl, serial.SerialNumber)
|
|
}
|
|
|
|
// 生成二维码到临时文件
|
|
filePath := fmt.Sprintf("temp_qr_%s.png", uuid.New().String())
|
|
writer, err := standard.New(filePath, standard.WithQRWidth(6))
|
|
if err != nil {
|
|
return "", "", fmt.Errorf("二维码写入器创建失败: %w", err)
|
|
}
|
|
|
|
qrc, errCode := qr.New(queryUrl)
|
|
if errCode != nil {
|
|
os.Remove(filePath)
|
|
return "", "", fmt.Errorf("二维码创建失败: %w", errCode)
|
|
}
|
|
|
|
if errSave := qrc.Save(writer); errSave != nil {
|
|
os.Remove(filePath)
|
|
return "", "", fmt.Errorf("二维码保存失败: %w", errSave)
|
|
}
|
|
|
|
// 读取文件内容
|
|
fileContent, errRead := os.ReadFile(filePath)
|
|
if errRead != nil {
|
|
os.Remove(filePath)
|
|
return "", "", fmt.Errorf("二维码文件读取失败: %w", errRead)
|
|
}
|
|
|
|
// 删除临时文件
|
|
os.Remove(filePath)
|
|
|
|
// 转换为 base64
|
|
qrCodeBase64 := fmt.Sprintf("data:image/png;base64,%s", base64.StdEncoding.EncodeToString(fileContent))
|
|
return qrCodeBase64, queryUrl, nil
|
|
}
|
|
|
|
// Query 查询序列号信息
|
|
func (s *SerialsService) Query(serialNumber string) (*models.Serial, error) {
|
|
var serial models.Serial
|
|
result := database.DB.Preload("User").Where("serial_number = ?", strings.ToUpper(serialNumber)).First(&serial)
|
|
if result.Error != nil {
|
|
return nil, fmt.Errorf("查询序列号失败: %w", errors.New("序列号不存在"))
|
|
}
|
|
|
|
if serial.ValidUntil != nil && serial.ValidUntil.Before(time.Now()) {
|
|
return nil, fmt.Errorf("序列号已过期")
|
|
}
|
|
|
|
return &serial, nil
|
|
}
|
|
|
|
// FindAll 获取序列号列表
|
|
func (s *SerialsService) FindAll(page int, limit int, search string) ([]models.Serial, int, int, error) {
|
|
var serials []models.Serial
|
|
var total int64
|
|
|
|
offset := (page - 1) * limit
|
|
db := database.DB.Preload("User")
|
|
|
|
// 搜索条件
|
|
if search != "" {
|
|
db = db.Where("serial_number LIKE ? OR company_name LIKE ?", "%"+search+"%", "%"+search+"%")
|
|
}
|
|
|
|
// 获取总数
|
|
countQuery := db.Model(&models.Serial{})
|
|
if search != "" {
|
|
countQuery = countQuery.Where("serial_number LIKE ? OR company_name LIKE ?", "%"+search+"%", "%"+search+"%")
|
|
}
|
|
countQuery.Count(&total)
|
|
|
|
// 分页查询
|
|
result := db.Model(&models.Serial{}).Order("created_at DESC").Offset(offset).Limit(limit).Find(&serials)
|
|
if result.Error != nil {
|
|
return nil, 0, 0, fmt.Errorf("查询序列号列表失败: %w", result.Error)
|
|
}
|
|
|
|
totalPages := (int(total) + limit - 1) / limit
|
|
|
|
return serials, int(total), totalPages, nil
|
|
}
|
|
|
|
// Update 更新序列号信息
|
|
func (s *SerialsService) Update(serialNumber string, updateData models.UpdateSerialDTO) (*models.Serial, error) {
|
|
var serial models.Serial
|
|
result := database.DB.Where("serial_number = ?", strings.ToUpper(serialNumber)).First(&serial)
|
|
if result.Error != nil {
|
|
return nil, fmt.Errorf("查询序列号失败: %w", errors.New("序列号不存在"))
|
|
}
|
|
|
|
if updateData.CompanyName != "" {
|
|
// 检查公司是否存在
|
|
var company models.Company
|
|
companyResult := database.DB.Where("company_name = ?", updateData.CompanyName).First(&company)
|
|
if companyResult.Error != nil {
|
|
company = models.Company{
|
|
CompanyName: updateData.CompanyName,
|
|
IsActive: true,
|
|
}
|
|
database.DB.Create(&company)
|
|
}
|
|
|
|
serial.CompanyName = updateData.CompanyName
|
|
}
|
|
|
|
if updateData.ValidUntil != nil {
|
|
serial.ValidUntil = updateData.ValidUntil
|
|
}
|
|
|
|
if updateData.IsActive != nil {
|
|
serial.IsActive = *updateData.IsActive
|
|
}
|
|
|
|
result = database.DB.Save(&serial)
|
|
if result.Error != nil {
|
|
return nil, fmt.Errorf("更新序列号失败: %w", result.Error)
|
|
}
|
|
|
|
return &serial, nil
|
|
}
|
|
|
|
// Revoke 吊销序列号
|
|
func (s *SerialsService) Revoke(serialNumber string) error {
|
|
var serial models.Serial
|
|
result := database.DB.Where("serial_number = ?", strings.ToUpper(serialNumber)).First(&serial)
|
|
if result.Error != nil {
|
|
return fmt.Errorf("查询序列号失败: %w", errors.New("序列号不存在"))
|
|
}
|
|
|
|
if !serial.IsActive {
|
|
return fmt.Errorf("序列号状态无效: %w", errors.New("序列号已被吊销"))
|
|
}
|
|
|
|
serial.IsActive = false
|
|
result = database.DB.Save(&serial)
|
|
if result.Error != nil {
|
|
return fmt.Errorf("吊销序列号失败: %w", result.Error)
|
|
}
|
|
|
|
return nil
|
|
}
|