You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
97 lines
3.2 KiB
97 lines
3.2 KiB
package commands |
|
|
|
import ( |
|
"context" |
|
"errors" |
|
"fmt" |
|
"time" |
|
|
|
"autostore/internal/application/interfaces" |
|
"autostore/internal/domain/entities" |
|
"autostore/internal/domain/specifications" |
|
"autostore/internal/domain/value_objects" |
|
) |
|
|
|
var ( |
|
ErrItemCreationFailed = errors.New("failed to create item") |
|
ErrInvalidUserID = errors.New("invalid user ID") |
|
) |
|
|
|
type AddItemCommand struct { |
|
itemRepo interfaces.IItemRepository |
|
orderService interfaces.IOrderService |
|
timeProvider interfaces.ITimeProvider |
|
expirationSpec *specifications.ItemExpirationSpec |
|
logger interfaces.ILogger |
|
} |
|
|
|
func NewAddItemCommand( |
|
itemRepo interfaces.IItemRepository, |
|
orderService interfaces.IOrderService, |
|
timeProvider interfaces.ITimeProvider, |
|
expirationSpec *specifications.ItemExpirationSpec, |
|
logger interfaces.ILogger, |
|
) *AddItemCommand { |
|
return &AddItemCommand{ |
|
itemRepo: itemRepo, |
|
orderService: orderService, |
|
timeProvider: timeProvider, |
|
expirationSpec: expirationSpec, |
|
logger: logger, |
|
} |
|
} |
|
|
|
func (c *AddItemCommand) Execute(ctx context.Context, name string, expirationDate time.Time, orderURL string, userID string) (string, error) { |
|
c.logger.Info(ctx, "Executing AddItemCommand", "name", name, "userID", userID) |
|
|
|
// Convert string IDs to value objects |
|
itemID, err := value_objects.NewRandomItemID() |
|
if err != nil { |
|
c.logger.Error(ctx, "Failed to generate item ID", "error", err) |
|
return "", fmt.Errorf("%w: %v", ErrItemCreationFailed, err) |
|
} |
|
|
|
userIDObj, err := value_objects.NewUserID(userID) |
|
if err != nil { |
|
c.logger.Error(ctx, "Invalid user ID", "userID", userID, "error", err) |
|
return "", fmt.Errorf("%w: %v", ErrInvalidUserID, err) |
|
} |
|
|
|
// Create expiration date value object |
|
expirationDateObj, err := value_objects.NewExpirationDate(expirationDate) |
|
if err != nil { |
|
c.logger.Error(ctx, "Invalid expiration date", "expirationDate", expirationDate, "error", err) |
|
return "", fmt.Errorf("%w: %v", ErrItemCreationFailed, err) |
|
} |
|
|
|
// Create item entity |
|
item, err := entities.NewItem(itemID, name, expirationDateObj, orderURL, userIDObj) |
|
if err != nil { |
|
c.logger.Error(ctx, "Failed to create item entity", "error", err) |
|
return "", fmt.Errorf("%w: %v", ErrItemCreationFailed, err) |
|
} |
|
|
|
// Get current time and check if item is expired |
|
currentTime := c.timeProvider.Now() |
|
isExpired := c.expirationSpec.IsExpired(item, currentTime) |
|
|
|
// Save the item first (even if expired, as per business rule #4) |
|
if err := c.itemRepo.Save(ctx, item); err != nil { |
|
c.logger.Error(ctx, "Failed to save item", "itemID", itemID.String(), "error", err) |
|
return "", fmt.Errorf("%w: %v", ErrItemCreationFailed, err) |
|
} |
|
|
|
if isExpired { |
|
c.logger.Info(ctx, "Item is expired, triggering order", "itemID", itemID.String()) |
|
|
|
// Business rule: When an item expires, a new item of the same type is automatically ordered |
|
if err := c.orderService.OrderItem(ctx, item); err != nil { |
|
c.logger.Error(ctx, "Failed to order expired item", "itemID", itemID.String(), "error", err) |
|
// Don't fail the entire operation if ordering fails, just log it |
|
c.logger.Warn(ctx, "Item created but ordering failed", "itemID", itemID.String(), "error", err) |
|
} |
|
} |
|
|
|
c.logger.Info(ctx, "Item created successfully", "itemID", itemID.String()) |
|
return itemID.String(), nil |
|
}
|
|
|