feat: 初始提交云印享付费打印服务后端
This commit is contained in:
218
cloudprint-backend/internal/service/order_service.go
Normal file
218
cloudprint-backend/internal/service/order_service.go
Normal file
@@ -0,0 +1,218 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/cloudprint/cloudprint-backend/internal/model"
|
||||
"github.com/cloudprint/cloudprint-backend/internal/repository"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type OrderService struct {
|
||||
orderRepo *repository.OrderRepository
|
||||
orderItemRepo *repository.OrderItemRepository
|
||||
fileRepo *repository.FileRepository
|
||||
priceRepo *repository.PriceRepository
|
||||
userRepo *repository.UserRepository
|
||||
printerRepo *repository.PrinterRepository
|
||||
printJobRepo *repository.PrintJobRepository
|
||||
}
|
||||
|
||||
func NewOrderService(
|
||||
orderRepo *repository.OrderRepository,
|
||||
orderItemRepo *repository.OrderItemRepository,
|
||||
fileRepo *repository.FileRepository,
|
||||
priceRepo *repository.PriceRepository,
|
||||
userRepo *repository.UserRepository,
|
||||
printerRepo *repository.PrinterRepository,
|
||||
printJobRepo *repository.PrintJobRepository,
|
||||
) *OrderService {
|
||||
return &OrderService{
|
||||
orderRepo: orderRepo,
|
||||
orderItemRepo: orderItemRepo,
|
||||
fileRepo: fileRepo,
|
||||
priceRepo: priceRepo,
|
||||
userRepo: userRepo,
|
||||
printerRepo: printerRepo,
|
||||
printJobRepo: printJobRepo,
|
||||
}
|
||||
}
|
||||
|
||||
type CreateOrderItem struct {
|
||||
FileID int32
|
||||
PrinterID int32
|
||||
Copies int
|
||||
Color bool
|
||||
Duplex bool
|
||||
PageRange string
|
||||
}
|
||||
|
||||
func (s *OrderService) CreateOrder(ctx context.Context, userID int32, items []*CreateOrderItem, remark string) (*model.Order, error) {
|
||||
if len(items) == 0 {
|
||||
return nil, errors.New("order must have at least one item")
|
||||
}
|
||||
|
||||
// Generate order number
|
||||
orderNo := generateOrderNo()
|
||||
|
||||
// Calculate total amount
|
||||
var totalAmount float64
|
||||
orderItems := make([]*model.OrderItem, 0, len(items))
|
||||
|
||||
for _, item := range items {
|
||||
// Verify file exists
|
||||
file, err := s.fileRepo.GetByID(ctx, item.FileID)
|
||||
if err != nil || file == nil {
|
||||
return nil, fmt.Errorf("file not found: %d", item.FileID)
|
||||
}
|
||||
|
||||
// Calculate price
|
||||
price, err := s.calculatePrice(ctx, item)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
orderItem := &model.OrderItem{
|
||||
FileID: item.FileID,
|
||||
PrinterID: intPtr(item.PrinterID),
|
||||
Copies: item.Copies,
|
||||
Color: item.Color,
|
||||
Duplex: item.Duplex,
|
||||
PageRange: item.PageRange,
|
||||
Price: price,
|
||||
}
|
||||
orderItems = append(orderItems, orderItem)
|
||||
totalAmount += price
|
||||
}
|
||||
|
||||
// Create order
|
||||
order := &model.Order{
|
||||
OrderNo: orderNo,
|
||||
UserID: userID,
|
||||
Status: model.OrderStatusPending,
|
||||
TotalAmount: totalAmount,
|
||||
Remark: remark,
|
||||
Items: orderItems,
|
||||
}
|
||||
|
||||
// Save order with items
|
||||
if err := s.orderRepo.CreateWithItems(ctx, order, orderItems); err != nil {
|
||||
return nil, fmt.Errorf("failed to create order: %w", err)
|
||||
}
|
||||
|
||||
return order, nil
|
||||
}
|
||||
|
||||
func (s *OrderService) calculatePrice(ctx context.Context, item *CreateOrderItem) (float64, error) {
|
||||
// Get price list
|
||||
prices, err := s.priceRepo.GetAllActive(ctx)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Default price calculation (A4 B&W per page)
|
||||
defaultPrice := 0.10
|
||||
colorMultiplier := 3.0
|
||||
duplexDiscount := 0.8
|
||||
|
||||
// Calculate based on file type and settings
|
||||
// For simplicity, using default values
|
||||
// In production, look up from price list based on file type
|
||||
|
||||
pageCount := 1 // Default, should get actual page count from file
|
||||
if item.Copies <= 0 {
|
||||
item.Copies = 1
|
||||
}
|
||||
|
||||
price := float64(pageCount) * defaultPrice * float64(item.Copies)
|
||||
|
||||
if item.Color {
|
||||
price *= colorMultiplier
|
||||
}
|
||||
|
||||
if item.Duplex {
|
||||
price *= duplexDiscount
|
||||
}
|
||||
|
||||
return price, nil
|
||||
}
|
||||
|
||||
func generateOrderNo() string {
|
||||
now := time.Now()
|
||||
return fmt.Sprintf("CP%d%s", now.UnixNano()/1000000, uuid.New().String()[:8])
|
||||
}
|
||||
|
||||
func intPtr(i int32) *int32 {
|
||||
return &i
|
||||
}
|
||||
|
||||
func (s *OrderService) GetOrder(ctx context.Context, orderNo string) (*model.Order, error) {
|
||||
return s.orderRepo.GetByOrderNo(ctx, orderNo)
|
||||
}
|
||||
|
||||
func (s *OrderService) GetOrderList(ctx context.Context, userID int32, status int8, page, pageSize int) ([]*model.Order, int64, error) {
|
||||
return s.orderRepo.GetListByUserID(ctx, userID, status, page, pageSize)
|
||||
}
|
||||
|
||||
func (s *OrderService) UpdateOrderStatus(ctx context.Context, orderNo string, status int8) error {
|
||||
return s.orderRepo.UpdateStatus(ctx, orderNo, status)
|
||||
}
|
||||
|
||||
func (s *OrderService) CancelOrder(ctx context.Context, orderNo string) error {
|
||||
order, err := s.orderRepo.GetByOrderNo(ctx, orderNo)
|
||||
if err != nil || order == nil {
|
||||
return errors.New("order not found")
|
||||
}
|
||||
|
||||
if order.Status != model.OrderStatusPending {
|
||||
return errors.New("only pending orders can be cancelled")
|
||||
}
|
||||
|
||||
return s.orderRepo.UpdateStatus(ctx, orderNo, model.OrderStatusCancelled)
|
||||
}
|
||||
|
||||
// PayOrder handles payment for an order
|
||||
func (s *OrderService) PayOrder(ctx context.Context, orderNo string, payType string) error {
|
||||
order, err := s.orderRepo.GetByOrderNo(ctx, orderNo)
|
||||
if err != nil || order == nil {
|
||||
return errors.New("order not found")
|
||||
}
|
||||
|
||||
if order.Status != model.OrderStatusPending {
|
||||
return errors.New("order is not pending")
|
||||
}
|
||||
|
||||
// Update order status to paid
|
||||
if err := s.orderRepo.UpdateStatus(ctx, orderNo, model.OrderStatusPaid); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create print jobs for each order item
|
||||
for _, item := range order.Items {
|
||||
if item.PrinterID != nil {
|
||||
jobNo := generateJobNo()
|
||||
job := &model.PrintJob{
|
||||
JobNo: jobNo,
|
||||
PrinterID: *item.PrinterID,
|
||||
OrderItemID: item.ID,
|
||||
Status: model.PrintJobStatusPending,
|
||||
Progress: 0,
|
||||
}
|
||||
if err := s.printJobRepo.Create(ctx, job); err != nil {
|
||||
// Log error but don't fail
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update order status to printing
|
||||
return s.orderRepo.UpdateStatus(ctx, orderNo, model.OrderStatusPrinting)
|
||||
}
|
||||
|
||||
func generateJobNo() string {
|
||||
now := time.Now()
|
||||
return fmt.Sprintf("PJ%d%s", now.UnixNano()/1000000, uuid.New().String()[:8])
|
||||
}
|
||||
Reference in New Issue
Block a user