240 lines
6.1 KiB
Go
240 lines
6.1 KiB
Go
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 based on file type and settings
|
|
price, err := s.calculatePrice(ctx, item, file.FileType)
|
|
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,
|
|
}
|
|
|
|
// Convert to pointer slice for repository
|
|
orderItemsPtr := make([]*model.OrderItem, len(orderItems))
|
|
for i := range orderItems {
|
|
orderItemsPtr[i] = &orderItems[i]
|
|
}
|
|
|
|
// Save order with items
|
|
if err := s.orderRepo.CreateWithItems(ctx, order, orderItemsPtr); err != nil {
|
|
return nil, fmt.Errorf("failed to create order: %w", err)
|
|
}
|
|
|
|
return order, nil
|
|
}
|
|
|
|
func (s *OrderService) calculatePrice(ctx context.Context, item *CreateOrderItem, fileType string) (float64, error) {
|
|
// Get price list from database
|
|
prices, err := s.priceRepo.GetAllActive(ctx)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("failed to get price list: %w", err)
|
|
}
|
|
|
|
if len(prices) == 0 {
|
|
return 0, fmt.Errorf("no active price items found")
|
|
}
|
|
|
|
// Find matching price based on file type and color setting
|
|
var basePrice float64
|
|
for _, p := range prices {
|
|
// Match by type (A4/A3/照片纸) - default to A4 if no match
|
|
if p.Type == "A4" || (p.Type == "" && basePrice == 0) {
|
|
if !item.Color || (item.Color && p.ColorEnabled) {
|
|
basePrice = p.Price
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
// Fallback to first available price if no match found
|
|
if basePrice == 0 {
|
|
basePrice = prices[0].Price
|
|
}
|
|
|
|
// Default values if not set
|
|
if item.Copies <= 0 {
|
|
item.Copies = 1
|
|
}
|
|
|
|
// Calculate total price
|
|
price := basePrice * float64(item.Copies)
|
|
|
|
// Apply color multiplier if color printing
|
|
if item.Color {
|
|
price *= 3.0 // Color costs 3x
|
|
}
|
|
|
|
// Apply duplex discount
|
|
if item.Duplex {
|
|
price *= 0.8 // Duplex gets 20% off
|
|
}
|
|
|
|
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])
|
|
}
|