feat: 初始提交云印享付费打印服务后端
This commit is contained in:
194
cloudprint-backend/internal/service/print_service.go
Normal file
194
cloudprint-backend/internal/service/print_service.go
Normal file
@@ -0,0 +1,194 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/cloudprint/cloudprint-backend/internal/model"
|
||||
"github.com/cloudprint/cloudprint-backend/internal/repository"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type PrintService struct {
|
||||
printerRepo *repository.PrinterRepository
|
||||
printJobRepo *repository.PrintJobRepository
|
||||
orderRepo *repository.OrderRepository
|
||||
subscribers map[int32]chan *model.PrintJob
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
func NewPrintService(
|
||||
printerRepo *repository.PrinterRepository,
|
||||
printJobRepo *repository.PrintJobRepository,
|
||||
orderRepo *repository.OrderRepository,
|
||||
) *PrintService {
|
||||
s := &PrintService{
|
||||
printerRepo: printerRepo,
|
||||
printJobRepo: printJobRepo,
|
||||
orderRepo: orderRepo,
|
||||
subscribers: make(map[int32]chan *model.PrintJob),
|
||||
}
|
||||
// Start heartbeat checker
|
||||
go s.checkPrinterHeartbeat()
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *PrintService) GetPrinters(ctx context.Context) ([]*model.Printer, error) {
|
||||
return s.printerRepo.GetAllActive(ctx)
|
||||
}
|
||||
|
||||
func (s *PrintService) GetPrinterStatus(ctx context.Context, printerID int32) (*model.Printer, int64, error) {
|
||||
printer, err := s.printerRepo.GetByID(ctx, printerID)
|
||||
if err != nil || printer == nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
queueCount, err := s.printJobRepo.GetQueueCount(ctx, printerID)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return printer, queueCount, nil
|
||||
}
|
||||
|
||||
func (s *PrintService) RegisterPrinter(ctx context.Context, name, displayName, apiKey string) (*model.Printer, string, error) {
|
||||
// Check if printer already exists
|
||||
existing, err := s.printerRepo.GetByName(ctx, name)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if existing != nil {
|
||||
return nil, "", nil
|
||||
}
|
||||
|
||||
// Generate token for this printer
|
||||
token := uuid.New().String()
|
||||
|
||||
printer := &model.Printer{
|
||||
Name: name,
|
||||
DisplayName: displayName,
|
||||
APIKey: apiKey,
|
||||
IsActive: true,
|
||||
Status: model.PrinterStatusIdle,
|
||||
}
|
||||
|
||||
if err := s.printerRepo.Create(ctx, printer); err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
return printer, token, nil
|
||||
}
|
||||
|
||||
func (s *PrintService) PrinterHeartbeat(ctx context.Context, printerID int32, status int8, token string) error {
|
||||
// Verify token (in production, validate against stored token)
|
||||
// For now, just update heartbeat and status
|
||||
if err := s.printerRepo.UpdateHeartbeat(ctx, printerID); err != nil {
|
||||
return err
|
||||
}
|
||||
return s.printerRepo.UpdateStatus(ctx, printerID, status)
|
||||
}
|
||||
|
||||
func (s *PrintService) SubmitPrintJob(ctx context.Context, orderItemID int32, printerID int32, filePath string, copies int, color, duplex bool, pageRange string) (*model.PrintJob, error) {
|
||||
jobNo := generateJobNo()
|
||||
|
||||
job := &model.PrintJob{
|
||||
JobNo: jobNo,
|
||||
PrinterID: printerID,
|
||||
OrderItemID: orderItemID,
|
||||
Status: model.PrintJobStatusPending,
|
||||
Progress: 0,
|
||||
}
|
||||
|
||||
if err := s.printJobRepo.Create(ctx, job); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Notify subscriber
|
||||
s.mu.RLock()
|
||||
ch, ok := s.subscribers[printerID]
|
||||
s.mu.RUnlock()
|
||||
|
||||
if ok {
|
||||
select {
|
||||
case ch <- job:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
return job, nil
|
||||
}
|
||||
|
||||
func (s *PrintService) CancelPrintJob(ctx context.Context, jobNo string) error {
|
||||
return s.printJobRepo.UpdateStatus(ctx, jobNo, model.PrintJobStatusCancelled, 0, "cancelled by user")
|
||||
}
|
||||
|
||||
func (s *PrintService) SubscribeJobs(ctx context.Context, printerID int32, token string) (<-chan *model.PrintJob, error) {
|
||||
ch := make(chan *model.PrintJob, 100)
|
||||
|
||||
s.mu.Lock()
|
||||
s.subscribers[printerID] = ch
|
||||
s.mu.Unlock()
|
||||
|
||||
// Send pending jobs immediately
|
||||
go func() {
|
||||
jobs, err := s.printJobRepo.GetPendingJobs(ctx, printerID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for _, job := range jobs {
|
||||
select {
|
||||
case ch <- job:
|
||||
default:
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return ch, nil
|
||||
}
|
||||
|
||||
func (s *PrintService) UnsubscribeJobs(printerID int32) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
if ch, ok := s.subscribers[printerID]; ok {
|
||||
close(ch)
|
||||
delete(s.subscribers, printerID)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *PrintService) ReportJobStatus(ctx context.Context, jobNo string, status string, progress int, errorMsg string) error {
|
||||
// Update job status
|
||||
if err := s.printJobRepo.UpdateStatus(ctx, jobNo, status, progress, errorMsg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If completed, check if all jobs for the order are done
|
||||
if status == model.PrintJobStatusCompleted {
|
||||
job, _ := s.printJobRepo.GetByJobNo(ctx, jobNo)
|
||||
if job != nil {
|
||||
// Check if all jobs for the order are completed
|
||||
s.checkOrderCompletion(ctx, job.OrderItemID)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *PrintService) checkOrderCompletion(ctx context.Context, orderItemID int32) {
|
||||
// In production, check if all print jobs for the order are completed
|
||||
// and update order status accordingly
|
||||
}
|
||||
|
||||
func (s *PrintService) checkPrinterHeartbeat() {
|
||||
ticker := time.NewTicker(30 * time.Second)
|
||||
for range ticker.C {
|
||||
// In production, check LastHeartbeat and mark printers as offline if no heartbeat
|
||||
// This is a placeholder for the heartbeat checker
|
||||
}
|
||||
}
|
||||
|
||||
// ValidatePrinterToken validates the API key and returns the printer
|
||||
func (s *PrintService) ValidatePrinterToken(ctx context.Context, apiKey string) (*model.Printer, error) {
|
||||
return s.printerRepo.GetByAPIKey(ctx, apiKey)
|
||||
}
|
||||
Reference in New Issue
Block a user