feat: 初始提交云印享付费打印服务后端

This commit is contained in:
WuYuuuub
2026-03-29 11:55:53 +08:00
parent 5047380dd0
commit 89a7c87bf6
35 changed files with 12522 additions and 0 deletions

View File

@@ -0,0 +1,68 @@
package service
import (
"context"
"github.com/cloudprint/cloudprint-backend/internal/repository"
"github.com/cloudprint/cloudprint-backend/internal/service"
pb "github.com/cloudprint/cloudprint-backend/proto/generated"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
type AdminServiceServer struct {
pb.UnimplementedAdminServiceServer
authService *service.AuthService
orderRepo *repository.OrderRepository
printerRepo *repository.PrinterRepository
}
func NewAdminServiceServer(
authService *service.AuthService,
orderRepo *repository.OrderRepository,
printerRepo *repository.PrinterRepository,
) *AdminServiceServer {
return &AdminServiceServer{
authService: authService,
orderRepo: orderRepo,
printerRepo: printerRepo,
}
}
func (s *AdminServiceServer) Login(ctx context.Context, req *pb.AdminLoginRequest) (*pb.AdminLoginResponse, error) {
result, err := s.authService.AdminLogin(ctx, req.Username, req.Password)
if err != nil {
return nil, status.Errorf(codes.Unauthenticated, "login failed: %v", err)
}
return &pb.AdminLoginResponse{
AccessToken: result.AccessToken,
Admin: &pb.Admin{
Id: result.Admin.ID,
Username: result.Admin.Username,
Nickname: result.Admin.Nickname,
Role: int32(result.Admin.Role),
},
}, nil
}
func (s *AdminServiceServer) GetStatistics(ctx context.Context, req *pb.GetStatisticsRequest) (*pb.Statistics, error) {
totalUsers, totalOrders, todayOrders, todayRevenue, pendingOrders, err := s.orderRepo.GetStatistics(ctx)
if err != nil {
return nil, status.Errorf(codes.Internal, "get statistics failed: %v", err)
}
activePrinters, err := s.printerRepo.CountActive(ctx)
if err != nil {
return nil, status.Errorf(codes.Internal, "get printer count failed: %v", err)
}
return &pb.Statistics{
TotalUsers: totalUsers,
TotalOrders: totalOrders,
TodayOrders: todayOrders,
TodayRevenue: todayRevenue,
PendingOrders: pendingOrders,
ActivePrinters: int32(activePrinters),
}, nil
}

View File

@@ -0,0 +1,63 @@
package service
import (
"context"
"github.com/cloudprint/cloudprint-backend/internal/service"
pb "github.com/cloudprint/cloudprint-backend/proto/generated"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
type AuthServiceServer struct {
pb.UnimplementedAuthServiceServer
authService *service.AuthService
}
func NewAuthServiceServer(authService *service.AuthService) *AuthServiceServer {
return &AuthServiceServer{
authService: authService,
}
}
func (s *AuthServiceServer) Login(ctx context.Context, req *pb.LoginRequest) (*pb.LoginResponse, error) {
result, err := s.authService.Login(ctx, req.Code)
if err != nil {
return nil, status.Errorf(codes.Internal, "login failed: %v", err)
}
return &pb.LoginResponse{
AccessToken: result.AccessToken,
RefreshToken: result.RefreshToken,
User: &pb.User{
Id: result.User.ID,
Openid: result.User.Openid,
Nickname: result.User.Nickname,
Avatar: result.User.Avatar,
Balance: result.User.Balance,
},
}, nil
}
func (s *AuthServiceServer) RefreshToken(ctx context.Context, req *pb.RefreshRequest) (*pb.RefreshResponse, error) {
newToken, err := s.authService.RefreshToken(ctx, req.RefreshToken)
if err != nil {
return nil, status.Errorf(codes.Internal, "refresh token failed: %v", err)
}
return &pb.RefreshResponse{
AccessToken: newToken,
}, nil
}
func (s *AuthServiceServer) GetUserInfo(ctx context.Context, req *pb.GetUserInfoRequest) (*pb.User, error) {
// In a real implementation, extract user ID from context
// For now, this is a placeholder
return nil, status.Errorf(codes.Unimplemented, "not implemented")
}
func (s *AuthServiceServer) UpdateUser(ctx context.Context, req *pb.UpdateUserRequest) (*pb.User, error) {
// In a real implementation, extract user ID from context
// For now, this is a placeholder
return nil, status.Errorf(codes.Unimplemented, "not implemented")
}

View File

@@ -0,0 +1,141 @@
package service
import (
"context"
"io"
"github.com/cloudprint/cloudprint-backend/internal/service"
pb "github.com/cloudprint/cloudprint-backend/proto/generated"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
type FileServiceServer struct {
pb.UnimplementedFileServiceServer
fileService *service.FileService
}
func NewFileServiceServer(fileService *service.FileService) *FileServiceServer {
return &FileServiceServer{
fileService: fileService,
}
}
func (s *FileServiceServer) Upload(stream pb.FileService_UploadServer) (*pb.UploadResponse, error) {
var fileName, fileType, uploaderType string
var uploaderID int32
var totalSize int64
var chunks [][]byte
for {
req, err := stream.Recv()
if err == io.EOF {
break
}
if err != nil {
return nil, status.Errorf(codes.Internal, "receive chunk failed: %v", err)
}
if fileName == "" {
fileName = req.FileName
fileType = req.FileType
uploaderType = "user" // Extract from context in production
uploaderID = 1 // Extract from context in production
totalSize = req.FileSize
}
chunks = append(chunks, req.Data)
}
// Concatenate chunks
var data []byte
for _, chunk := range chunks {
data = append(data, chunk...)
}
file, err := s.fileService.Upload(stream.Context(), fileName, fileType, uploaderType, uploaderID, data)
if err != nil {
return nil, status.Errorf(codes.Internal, "upload failed: %v", err)
}
return &pb.UploadResponse{
FileId: file.ID,
FilePath: file.FilePath,
FileUrl: file.FilePath,
}, nil
}
func (s *FileServiceServer) GetFile(req *pb.GetFileRequest, stream pb.FileService_GetFileServer) error {
file, err := s.fileService.GetFile(stream.Context(), req.FileId)
if err != nil || file == nil {
return status.Errorf(codes.NotFound, "file not found")
}
reader, err := s.fileService.GetFileReader(stream.Context(), file)
if err != nil {
return status.Errorf(codes.Internal, "read file failed: %v", err)
}
defer reader.Close()
buf := make([]byte, 4096)
for {
n, err := reader.Read(buf)
if n > 0 {
if err := stream.Send(&pb.FileData{Data: buf[:n], IsLast: false}); err != nil {
return err
}
}
if err == io.EOF {
break
}
if err != nil {
return err
}
}
return stream.Send(&pb.FileData{IsLast: true})
}
func (s *FileServiceServer) DeleteFile(ctx context.Context, req *pb.DeleteFileRequest) (*pb.Empty, error) {
err := s.fileService.DeleteFile(ctx, req.FileId, "user", 1)
if err != nil {
return nil, status.Errorf(codes.Internal, "delete file failed: %v", err)
}
return &pb.Empty{}, nil
}
func (s *FileServiceServer) AddWatermark(ctx context.Context, req *pb.WatermarkRequest) (*pb.WatermarkResponse, error) {
file, err := s.fileService.AddWatermark(ctx, req.FileId, req.Text, req.Opacity, int(req.Position), int(req.FontSize))
if err != nil {
return nil, status.Errorf(codes.Internal, "add watermark failed: %v", err)
}
return &pb.WatermarkResponse{
FileId: file.ID,
FilePath: file.FilePath,
}, nil
}
func (s *FileServiceServer) GetFileList(ctx context.Context, req *pb.GetFileListRequest) (*pb.FileListResponse, error) {
files, total, err := s.fileService.GetFileList(ctx, req.UploaderType, req.UploaderId, int(req.Page), int(req.PageSize))
if err != nil {
return nil, status.Errorf(codes.Internal, "get file list failed: %v", err)
}
pbFiles := make([]*pb.FileInfo, 0, len(files))
for _, f := range files {
pbFiles = append(pbFiles, &pb.FileInfo{
Id: f.ID,
FileName: f.FileName,
FilePath: f.FilePath,
FileType: f.FileType,
FileSize: f.FileSize,
})
}
return &pb.FileListResponse{
Files: pbFiles,
Total: int32(total),
}, nil
}

View File

@@ -0,0 +1,91 @@
package service
import (
"context"
"github.com/cloudprint/cloudprint-backend/internal/repository"
pb "github.com/cloudprint/cloudprint-backend/proto/generated"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
type LibraryServiceServer struct {
pb.UnimplementedLibraryServiceServer
libraryRepo *repository.LibraryRepository
fileRepo *repository.FileRepository
}
func NewLibraryServiceServer(libraryRepo *repository.LibraryRepository, fileRepo *repository.FileRepository) *LibraryServiceServer {
return &LibraryServiceServer{
libraryRepo: libraryRepo,
fileRepo: fileRepo,
}
}
func (s *LibraryServiceServer) GetCategories(ctx context.Context, req *pb.GetCategoriesRequest) (*pb.CategoryListResponse, error) {
categories, err := s.libraryRepo.GetAllCategories(ctx)
if err != nil {
return nil, status.Errorf(codes.Internal, "get categories failed: %v", err)
}
pbCategories := make([]*pb.LibraryCategory, 0, len(categories))
for _, cat := range categories {
pbCategories = append(pbCategories, &pb.LibraryCategory{
Id: cat.ID,
Name: cat.Name,
Icon: cat.Icon,
SortOrder: int32(cat.SortOrder),
})
}
return &pb.CategoryListResponse{Categories: pbCategories}, nil
}
func (s *LibraryServiceServer) GetFiles(ctx context.Context, req *pb.GetLibraryFilesRequest) (*pb.LibraryFileListResponse, error) {
files, total, err := s.libraryRepo.GetFilesByCategory(ctx, req.CategoryId, int(req.Page), int(req.PageSize))
if err != nil {
return nil, status.Errorf(codes.Internal, "get files failed: %v", err)
}
pbFiles := make([]*pb.LibraryFile, 0, len(files))
for _, file := range files {
pbFile := &pb.LibraryFile{
Id: file.ID,
CategoryId: file.CategoryID,
FileId: file.FileID,
Title: file.Title,
Description: file.Description,
PageCount: int32(file.PageCount),
DownloadCount: int32(file.DownloadCount),
}
if file.File != nil {
pbFile.File = &pb.FileInfo{
Id: file.File.ID,
FileName: file.File.FileName,
FilePath: file.File.FilePath,
FileType: file.File.FileType,
FileSize: file.File.FileSize,
}
}
pbFiles = append(pbFiles, pbFile)
}
return &pb.LibraryFileListResponse{
Files: pbFiles,
Total: int32(total),
}, nil
}
func (s *LibraryServiceServer) UploadFile(stream pb.LibraryService_UploadFileServer) error {
// Placeholder for streaming upload
return status.Errorf(codes.Unimplemented, "streaming upload not implemented")
}
func (s *LibraryServiceServer) DeleteFile(ctx context.Context, req *pb.DeleteLibraryFileRequest) (*pb.Empty, error) {
err := s.libraryRepo.DeleteFile(ctx, req.LibraryFileId)
if err != nil {
return nil, status.Errorf(codes.Internal, "delete file failed: %v", err)
}
return &pb.Empty{}, nil
}

View File

@@ -0,0 +1,98 @@
package service
import (
"context"
"github.com/cloudprint/cloudprint-backend/internal/service"
pb "github.com/cloudprint/cloudprint-backend/proto/generated"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
type OrderServiceServer struct {
pb.UnimplementedOrderServiceServer
orderService *service.OrderService
}
func NewOrderServiceServer(orderService *service.OrderService) *OrderServiceServer {
return &OrderServiceServer{
orderService: orderService,
}
}
func (s *OrderServiceServer) CreateOrder(ctx context.Context, req *pb.CreateOrderRequest) (*pb.CreateOrderResponse, error) {
// Extract user ID from context (set by auth interceptor)
userID := int32(1) // Placeholder
items := make([]*service.CreateOrderItem, 0, len(req.Items))
for _, item := range req.Items {
items = append(items, &service.CreateOrderItem{
FileID: item.FileId,
PrinterID: item.PrinterId,
Copies: int(item.Copies),
Color: item.Color,
Duplex: item.Duplex,
PageRange: item.PageRange,
})
}
order, err := s.orderService.CreateOrder(ctx, userID, items, req.Remark)
if err != nil {
return nil, status.Errorf(codes.Internal, "create order failed: %v", err)
}
return &pb.CreateOrderResponse{
OrderNo: order.OrderNo,
TotalAmount: order.TotalAmount,
}, nil
}
func (s *OrderServiceServer) GetOrder(ctx context.Context, req *pb.GetOrderRequest) (*pb.Order, error) {
order, err := s.orderService.GetOrder(ctx, req.OrderNo)
if err != nil || order == nil {
return nil, status.Errorf(codes.NotFound, "order not found")
}
return convertOrderToPB(order), nil
}
func (s *OrderServiceServer) GetOrderList(ctx context.Context, req *pb.GetOrderListRequest) (*pb.OrderListResponse, error) {
orders, total, err := s.orderService.GetOrderList(ctx, req.UserId, req.Status, int(req.Page), int(req.PageSize))
if err != nil {
return nil, status.Errorf(codes.Internal, "get order list failed: %v", err)
}
pbOrders := make([]*pb.Order, 0, len(orders))
for _, order := range orders {
pbOrders = append(pbOrders, convertOrderToPB(order))
}
return &pb.OrderListResponse{
Orders: pbOrders,
Total: int32(total),
}, nil
}
func (s *OrderServiceServer) UpdateOrderStatus(ctx context.Context, req *pb.UpdateOrderStatusRequest) (*pb.Empty, error) {
err := s.orderService.UpdateOrderStatus(ctx, req.OrderNo, int8(req.Status))
if err != nil {
return nil, status.Errorf(codes.Internal, "update order status failed: %v", err)
}
return &pb.Empty{}, nil
}
func (s *OrderServiceServer) CancelOrder(ctx context.Context, req *pb.CancelOrderRequest) (*pb.Empty, error) {
err := s.orderService.CancelOrder(ctx, req.OrderNo)
if err != nil {
return nil, status.Errorf(codes.Internal, "cancel order failed: %v", err)
}
return &pb.Empty{}, nil
}
func convertOrderToPB(order *service.Order) *pb.Order {
// Note: This is a simplified conversion. In production, you would need to handle all fields properly.
// The service.Order type here is the model, not the protobuf type.
return nil // Placeholder
}

View File

@@ -0,0 +1,201 @@
package service
import (
"context"
"github.com/cloudprint/cloudprint-backend/internal/model"
"github.com/cloudprint/cloudprint-backend/internal/repository"
"github.com/cloudprint/cloudprint-backend/internal/service"
"github.com/cloudprint/cloudprint-backend/pkg/pay"
pb "github.com/cloudprint/cloudprint-backend/proto/generated"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
type PayServiceServer struct {
pb.UnimplementedPayServiceServer
orderService *service.OrderService
paymentRepo *repository.PaymentRepository
weChatPay *pay.WeChatPay
userRepo *repository.UserRepository
}
func NewPayServiceServer(
orderService *service.OrderService,
paymentRepo *repository.PaymentRepository,
weChatPay *pay.WeChatPay,
userRepo *repository.UserRepository,
) *PayServiceServer {
return &PayServiceServer{
orderService: orderService,
paymentRepo: paymentRepo,
weChatPay: weChatPay,
userRepo: userRepo,
}
}
func (s *PayServiceServer) CreateWxPayOrder(ctx context.Context, req *pb.WxPayRequest) (*pb.WxPayResponse, error) {
resp, err := s.weChatPay.UnifiedOrder(ctx, req.OrderNo, req.Amount, req.Description, req.Openid, req.ClientIp)
if err != nil {
return nil, status.Errorf(codes.Internal, "create pay order failed: %v", err)
}
if resp.ReturnCode != "SUCCESS" {
return nil, status.Errorf(codes.Internal, "wechat pay error: %s - %s", resp.ErrCode, resp.ErrMsg)
}
// Create payment record
payment := &model.Payment{
OrderNo: req.OrderNo,
PayType: "wechat",
PayStatus: model.PayStatusPaying,
}
if err := s.paymentRepo.Create(ctx, payment); err != nil {
return nil, status.Errorf(codes.Internal, "create payment record failed: %v", err)
}
return &pb.WxPayResponse{
PrepayId: resp.PrepayID,
CodeUrl: resp.CodeURL,
MwebUrl: resp.MWebURL,
}, nil
}
func (s *PayServiceServer) QueryPayStatus(ctx context.Context, req *pb.QueryPayStatusRequest) (*pb.PayStatusResponse, error) {
tradeState, _, err := s.weChatPay.QueryOrder(ctx, req.OrderNo)
if err != nil {
return nil, status.Errorf(codes.Internal, "query pay status failed: %v", err)
}
var payStatus int32
switch tradeState {
case "SUCCESS":
payStatus = int32(model.PayStatusSuccess)
case "USERPAYING", "PAYING":
payStatus = int32(model.PayStatusPaying)
case "CLOSED", "PAYERROR", "REFUND":
payStatus = int32(model.PayStatusFailed)
default:
payStatus = int32(model.PayStatusPending)
}
return &pb.PayStatusResponse{
Status: payStatus,
}, nil
}
func (s *PayServiceServer) HandlePayCallback(ctx context.Context, req *pb.PayCallbackRequest) (*pb.PayCallbackResponse, error) {
callback, err := s.weChatPay.ParseCallback([]byte(req.Body))
if err != nil {
return &pb.PayCallbackResponse{
ReturnCode: "FAIL",
ReturnMsg: "parse error",
}, nil
}
if !s.weChatPay.ValidateCallback(callback) {
return &pb.PayCallbackResponse{
ReturnCode: "FAIL",
ReturnMsg: "validate error",
}, nil
}
if callback.ReturnCode == "SUCCESS" && callback.ResultCode == "SUCCESS" {
// Check if payment already processed (idempotency)
payment, _ := s.paymentRepo.GetByOrderNo(ctx, callback.OutTradeNo)
if payment != nil && payment.PayStatus == model.PayStatusSuccess {
// Already processed, return success
return &pb.PayCallbackResponse{
ReturnCode: "SUCCESS",
ReturnMsg: "OK",
}, nil
}
// Update payment status
if err := s.paymentRepo.UpdateStatus(ctx, callback.OutTradeNo, model.PayStatusSuccess, callback.TransactionID); err != nil {
return &pb.PayCallbackResponse{
ReturnCode: "FAIL",
ReturnMsg: "update payment failed",
}, nil
}
// Update order status - pay order triggers print job creation
if err := s.orderService.PayOrder(ctx, callback.OutTradeNo, "wechat"); err != nil {
return &pb.PayCallbackResponse{
ReturnCode: "FAIL",
ReturnMsg: "pay order failed",
}, nil
}
}
return &pb.PayCallbackResponse{
ReturnCode: "SUCCESS",
ReturnMsg: "OK",
}, nil
}
func (s *PayServiceServer) PayByBalance(ctx context.Context, req *pb.PayByBalanceRequest) (*pb.StatusResponse, error) {
// Get order
order, err := s.orderService.GetOrder(ctx, req.OrderNo)
if err != nil || order == nil {
return &pb.StatusResponse{
Code: 1,
Message: "order not found",
}, nil
}
// Check if order is pending
if order.Status != model.OrderStatusPending {
return &pb.StatusResponse{
Code: 1,
Message: "order is not pending",
}, nil
}
// Get user
user, err := s.userRepo.GetByID(ctx, req.UserId)
if err != nil || user == nil {
return &pb.StatusResponse{
Code: 1,
Message: "user not found",
}, nil
}
// Check balance
if user.Balance < order.TotalAmount {
return &pb.StatusResponse{
Code: 1,
Message: "insufficient balance",
}, nil
}
// Deduct balance
if err := s.userRepo.DeductBalance(ctx, req.UserId, order.TotalAmount); err != nil {
return nil, status.Errorf(codes.Internal, "deduct balance failed: %v", err)
}
// Create payment record
payment := &model.Payment{
OrderNo: order.OrderNo,
PayType: "balance",
PayStatus: model.PayStatusSuccess,
TransactionID: "balance_" + order.OrderNo,
}
if err := s.paymentRepo.Create(ctx, payment); err != nil {
// Rollback balance deduction on failure
s.userRepo.AddBalance(ctx, req.UserId, order.TotalAmount)
return nil, status.Errorf(codes.Internal, "create payment record failed: %v", err)
}
// Pay order - this creates print jobs
if err := s.orderService.PayOrder(ctx, req.OrderNo, "balance"); err != nil {
// Rollback
s.userRepo.AddBalance(ctx, req.UserId, order.TotalAmount)
return nil, status.Errorf(codes.Internal, "pay order failed: %v", err)
}
return &pb.StatusResponse{
Code: 0,
Message: "success",
}, nil
}

View File

@@ -0,0 +1,96 @@
package service
import (
"context"
"github.com/cloudprint/cloudprint-backend/internal/repository"
pb "github.com/cloudprint/cloudprint-backend/proto/generated"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
type PriceServiceServer struct {
pb.UnimplementedPriceServiceServer
priceRepo *repository.PriceRepository
}
func NewPriceServiceServer(priceRepo *repository.PriceRepository) *PriceServiceServer {
return &PriceServiceServer{
priceRepo: priceRepo,
}
}
func (s *PriceServiceServer) GetPriceList(ctx context.Context, req *pb.GetPriceListRequest) (*pb.PriceListResponse, error) {
items, err := s.priceRepo.GetAllActive(ctx)
if err != nil {
return nil, status.Errorf(codes.Internal, "get price list failed: %v", err)
}
pbItems := make([]*pb.PriceItem, 0, len(items))
for _, item := range items {
pbItems = append(pbItems, &pb.PriceItem{
Id: item.ID,
Name: item.Name,
Type: item.Type,
Price: item.Price,
Unit: item.Unit,
ColorEnabled: item.ColorEnabled,
IsActive: item.IsActive,
})
}
return &pb.PriceListResponse{Items: pbItems}, nil
}
func (s *PriceServiceServer) CreatePriceItem(ctx context.Context, req *pb.CreatePriceItemRequest) (*pb.PriceItem, error) {
item := &model.PriceList{
Name: req.Name,
Type: req.Type,
Price: req.Price,
Unit: req.Unit,
ColorEnabled: req.ColorEnabled,
IsActive: true,
}
if err := s.priceRepo.Create(ctx, item); err != nil {
return nil, status.Errorf(codes.Internal, "create price item failed: %v", err)
}
return &pb.PriceItem{
Id: item.ID,
Name: item.Name,
Type: item.Type,
Price: item.Price,
Unit: item.Unit,
ColorEnabled: item.ColorEnabled,
IsActive: item.IsActive,
}, nil
}
func (s *PriceServiceServer) UpdatePriceItem(ctx context.Context, req *pb.UpdatePriceItemRequest) (*pb.PriceItem, error) {
item, err := s.priceRepo.GetByID(ctx, req.Id)
if err != nil || item == nil {
return nil, status.Errorf(codes.NotFound, "price item not found")
}
item.Name = req.Name
item.Type = req.Type
item.Price = req.Price
item.Unit = req.Unit
item.ColorEnabled = req.ColorEnabled
item.IsActive = req.IsActive
if err := s.priceRepo.Update(ctx, item); err != nil {
return nil, status.Errorf(codes.Internal, "update price item failed: %v", err)
}
return &pb.PriceItem{
Id: item.ID,
Name: item.Name,
Type: item.Type,
Price: item.Price,
Unit: item.Unit,
ColorEnabled: item.ColorEnabled,
IsActive: item.IsActive,
}, nil
}

View File

@@ -0,0 +1,136 @@
package service
import (
"context"
"github.com/cloudprint/cloudprint-backend/internal/service"
pb "github.com/cloudprint/cloudprint-backend/proto/generated"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
type PrintServiceServer struct {
pb.UnimplementedPrintServiceServer
printService *service.PrintService
}
func NewPrintServiceServer(printService *service.PrintService) *PrintServiceServer {
return &PrintServiceServer{
printService: printService,
}
}
func (s *PrintServiceServer) GetPrinters(ctx context.Context, req *pb.GetPrintersRequest) (*pb.PrinterListResponse, error) {
printers, err := s.printService.GetPrinters(ctx)
if err != nil {
return nil, status.Errorf(codes.Internal, "get printers failed: %v", err)
}
pbPrinters := make([]*pb.Printer, 0, len(printers))
for _, printer := range printers {
pbPrinters = append(pbPrinters, &pb.Printer{
Id: printer.ID,
Name: printer.Name,
DisplayName: printer.DisplayName,
IsActive: printer.IsActive,
Status: int32(printer.Status),
})
}
return &pb.PrinterListResponse{Printers: pbPrinters}, nil
}
func (s *PrintServiceServer) GetPrinterStatus(ctx context.Context, req *pb.GetPrinterStatusRequest) (*pb.PrinterStatusResponse, error) {
printer, queueCount, err := s.printService.GetPrinterStatus(ctx, req.PrinterId)
if err != nil || printer == nil {
return nil, status.Errorf(codes.NotFound, "printer not found")
}
return &pb.PrinterStatusResponse{
Printer: &pb.Printer{
Id: printer.ID,
Name: printer.Name,
DisplayName: printer.DisplayName,
IsActive: printer.IsActive,
Status: int32(printer.Status),
},
QueueCount: int32(queueCount),
}, nil
}
func (s *PrintServiceServer) RegisterPrinter(ctx context.Context, req *pb.RegisterPrinterRequest) (*pb.RegisterPrinterResponse, error) {
printer, token, err := s.printService.RegisterPrinter(ctx, req.Name, req.DisplayName, req.ApiKey)
if err != nil {
return nil, status.Errorf(codes.Internal, "register printer failed: %v", err)
}
if printer == nil {
return nil, status.Errorf(codes.AlreadyExists, "printer already exists")
}
return &pb.RegisterPrinterResponse{
PrinterId: printer.ID,
Token: token,
}, nil
}
func (s *PrintServiceServer) PrinterHeartbeat(ctx context.Context, req *pb.PrinterHeartbeatRequest) (*pb.Empty, error) {
err := s.printService.PrinterHeartbeat(ctx, req.PrinterId, int8(req.Status), req.Token)
if err != nil {
return nil, status.Errorf(codes.Internal, "heartbeat failed: %v", err)
}
return &pb.Empty{}, nil
}
func (s *PrintServiceServer) SubmitPrintJob(ctx context.Context, req *pb.SubmitPrintJobRequest) (*pb.SubmitPrintJobResponse, error) {
job, err := s.printService.SubmitPrintJob(ctx, req.OrderItemId, req.PrinterId, "", int(req.Copies), req.Color, req.Duplex, req.PageRange)
if err != nil {
return nil, status.Errorf(codes.Internal, "submit print job failed: %v", err)
}
return &pb.SubmitPrintJobResponse{
JobNo: job.JobNo,
}, nil
}
func (s *PrintServiceServer) CancelPrintJob(ctx context.Context, req *pb.CancelPrintJobRequest) (*pb.Empty, error) {
err := s.printService.CancelPrintJob(ctx, req.JobNo)
if err != nil {
return nil, status.Errorf(codes.Internal, "cancel print job failed: %v", err)
}
return &pb.Empty{}, nil
}
func (s *PrintServiceServer) SubscribeJobs(req *pb.SubscribeJobsRequest, stream pb.PrintService_SubscribeJobsServer) error {
ch, err := s.printService.SubscribeJobs(stream.Context(), req.PrinterId, req.Token)
if err != nil {
return status.Errorf(codes.Internal, "subscribe jobs failed: %v", err)
}
for job := range ch {
if err := stream.Send(&pb.PrintJob{
Id: job.ID,
JobNo: job.JobNo,
PrinterId: job.PrinterID,
OrderItemId: job.OrderItemID,
Status: job.Status,
Progress: int32(job.Progress),
ErrorMsg: job.ErrorMsg,
}); err != nil {
s.printService.UnsubscribeJobs(req.PrinterId)
return err
}
}
return nil
}
func (s *PrintServiceServer) ReportJobStatus(ctx context.Context, req *pb.ReportJobStatusRequest) (*pb.Empty, error) {
err := s.printService.ReportJobStatus(ctx, req.JobNo, req.Status, int(req.Progress), req.ErrorMsg)
if err != nil {
return nil, status.Errorf(codes.Internal, "report job status failed: %v", err)
}
return &pb.Empty{}, nil
}