package entity import ( "fmt" "time" "github.com/genex/trading-service/internal/domain/vo" ) // OrderStatus represents the lifecycle status of an order. type OrderStatus string const ( OrderPending OrderStatus = "pending" OrderPartial OrderStatus = "partial" OrderFilled OrderStatus = "filled" OrderCancelled OrderStatus = "cancelled" ) // Order is the core domain entity representing a trading order. type Order struct { ID string `json:"id"` UserID string `json:"userId"` CouponID string `json:"couponId"` Side vo.OrderSide `json:"side"` Type vo.OrderType `json:"type"` Price vo.Price `json:"price"` Quantity vo.Quantity `json:"quantity"` FilledQty vo.Quantity `json:"filledQty"` RemainingQty vo.Quantity `json:"remainingQty"` Status OrderStatus `json:"status"` CreatedAt time.Time `json:"createdAt"` UpdatedAt time.Time `json:"updatedAt"` } // NewOrder creates a new Order with validated inputs. func NewOrder(id, userID, couponID string, side vo.OrderSide, orderType vo.OrderType, price vo.Price, quantity vo.Quantity) (*Order, error) { if id == "" { return nil, fmt.Errorf("order ID cannot be empty") } if userID == "" { return nil, fmt.Errorf("user ID cannot be empty") } if couponID == "" { return nil, fmt.Errorf("coupon ID cannot be empty") } if !side.IsValid() { return nil, fmt.Errorf("invalid order side: %s", side) } if !orderType.IsValid() { return nil, fmt.Errorf("invalid order type: %s", orderType) } if orderType == vo.Limit && !price.IsPositive() { return nil, fmt.Errorf("limit order must have a positive price") } now := time.Now() return &Order{ ID: id, UserID: userID, CouponID: couponID, Side: side, Type: orderType, Price: price, Quantity: quantity, FilledQty: vo.ZeroQuantity(), RemainingQty: quantity, Status: OrderPending, CreatedAt: now, UpdatedAt: now, }, nil } // Fill records a partial or full fill of the given quantity. func (o *Order) Fill(qty vo.Quantity) { o.FilledQty = o.FilledQty.Add(qty) o.RemainingQty = o.RemainingQty.Subtract(qty) o.UpdatedAt = time.Now() if o.RemainingQty.IsZero() { o.Status = OrderFilled } else { o.Status = OrderPartial } } // Cancel marks the order as cancelled. func (o *Order) Cancel() { o.Status = OrderCancelled o.UpdatedAt = time.Now() } // IsActive returns true if the order can still participate in matching. func (o *Order) IsActive() bool { return o.Status == OrderPending || o.Status == OrderPartial } // IsFilled returns true if the order is fully filled. func (o *Order) IsFilled() bool { return o.Status == OrderFilled }