195 lines
5.0 KiB
Go
195 lines
5.0 KiB
Go
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)
|
|
}
|