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" ) // EmployeeSerialsService 员工序列号服务 type EmployeeSerialsService struct{} // Generate 生成员工序列号 func (s *EmployeeSerialsService) Generate( companyName string, position string, employeeName string, quantity int, userId uint, serialPrefix string, ) ([]models.EmployeeSerial, error) { var serials []models.EmployeeSerial // 检查公司是否存在,不存在则创建 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) } } // 生成序列号前缀 if serialPrefix == "" { serialPrefix = fmt.Sprintf("EMP%d", time.Now().Year()%100) } // 标准化前缀(转大写,去除空格) serialPrefix = strings.ToUpper(strings.TrimSpace(serialPrefix)) // 预生成所有序列号 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.EmployeeSerial checkResult := database.DB.Where("serial_number = ?", serialNumber).First(&existingSerial) if checkResult.Error != nil { serialNumbers[serialNumber] = true i++ } } for serialNumber := range serialNumbers { serial := models.EmployeeSerial{ SerialNumber: strings.ToUpper(serialNumber), CompanyName: companyName, Position: position, EmployeeName: employeeName, 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 } // Query 查询员工序列号信息 func (s *EmployeeSerialsService) Query(serialNumber string) (*models.EmployeeSerial, error) { var serial models.EmployeeSerial result := database.DB.Preload("User").Where("serial_number = ?", strings.ToUpper(serialNumber)).First(&serial) if result.Error != nil { return nil, fmt.Errorf("查询员工序列号失败: %w", errors.New("序列号不存在")) } return &serial, nil } // FindAll 获取员工序列号列表 func (s *EmployeeSerialsService) FindAll(page int, limit int, search string) ([]models.EmployeeSerial, int, int, error) { var serials []models.EmployeeSerial var total int64 offset := (page - 1) * limit db := database.DB.Preload("User") // 搜索条件 if search != "" { db = db.Where("serial_number LIKE ? OR company_name LIKE ? OR department LIKE ? OR employee_name LIKE ?", "%"+search+"%", "%"+search+"%", "%"+search+"%", "%"+search+"%") } // 获取总数 countQuery := db.Model(&models.EmployeeSerial{}) if search != "" { countQuery = countQuery.Where("serial_number LIKE ? OR company_name LIKE ? OR department LIKE ? OR employee_name LIKE ?", "%"+search+"%", "%"+search+"%", "%"+search+"%", "%"+search+"%") } countQuery.Count(&total) // 分页查询 result := db.Model(&models.EmployeeSerial{}).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 *EmployeeSerialsService) Update(serialNumber string, updateData models.UpdateEmployeeSerialDTO) (*models.EmployeeSerial, error) { var serial models.EmployeeSerial 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.Position != "" { serial.Position = updateData.Position } if updateData.EmployeeName != "" { serial.EmployeeName = updateData.EmployeeName } 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 *EmployeeSerialsService) Revoke(serialNumber string) error { var serial models.EmployeeSerial 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 } // Delete 删除员工序列号(物理删除) func (s *EmployeeSerialsService) Delete(serialNumber string) error { var serial models.EmployeeSerial result := database.DB.Where("serial_number = ?", strings.ToUpper(serialNumber)).First(&serial) if result.Error != nil { return fmt.Errorf("查询员工序列号失败: %w", errors.New("序列号不存在")) } result = database.DB.Delete(&serial) if result.Error != nil { return fmt.Errorf("删除员工序列号失败: %w", result.Error) } return nil } // GenerateQRCode 生成员工二维码 func (s *EmployeeSerialsService) GenerateQRCode( serialNumber string, baseUrl string, requestHost string, protocol string, ) (string, string, error) { var serial models.EmployeeSerial 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("序列号已被禁用")) } // 确定查询 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) } // 读取文件并转换为 Base64 fileBytes, err := os.ReadFile(filePath) if err != nil { os.Remove(filePath) return "", "", fmt.Errorf("读取二维码文件失败: %w", err) } os.Remove(filePath) qrCodeBase64 := base64.StdEncoding.EncodeToString(fileBytes) return qrCodeBase64, queryURL, nil }