Middle+
Implement the grpc server method AddItemAndOrder: 1. Add items to the cart 2. Be sure to notify potential consumers about the cart checkout via a message broker. 3. When the cart composition changes, recalculate basket.Total=sum(count*price) 4. All cart items must be unique by ProductID key 5. Change the cart status to ordered for checkout 6. No changes are allowed in finalized carts
sobes.tech AI
Answer from AI
Below is an example implementation of the AddItemAndOrder method for a gRPC server in Go, considering the requirements:
- The
Basketcontains unique items byProductID. - When adding items, the
Totalsum is recalculated. - When the basket is checked out, its status changes to
ordered. - After checkout, modifications are prohibited.
- After checkout, a message is sent via a broker (e.g., through the
MessageBrokerinterface).
package service
import (
"context"
"errors"
"sync"
)
// Example structures
type BasketStatus string
const (
StatusOpen BasketStatus = "open"
StatusOrdered BasketStatus = "ordered"
)
type BasketItem struct {
ProductID string
Count int
Price float64
}
type Basket struct {
ID string
Items map[string]*BasketItem // key - ProductID
Total float64
Status BasketStatus
mu sync.Mutex
}
// Message broker interface
type MessageBroker interface {
Publish(topic string, message interface{}) error
}
// Basket service with baskets and broker
type BasketService struct {
baskets map[string]*Basket
broker MessageBroker
}
func NewBasketService(broker MessageBroker) *BasketService {
return &BasketService{
baskets: make(map[string]*Basket),
broker: broker,
}
}
// AddItemAndOrderRequest and Response are protobuf-generated structures
func (s *BasketService) AddItemAndOrder(ctx context.Context, req *AddItemAndOrderRequest) (*AddItemAndOrderResponse, error) {
basket, ok := s.baskets[req.BasketId]
if !ok {
return nil, errors.New("basket not found")
}
basket.mu.Lock()
defer basket.mu.Unlock()
if basket.Status == StatusOrdered {
return nil, errors.New("cannot modify ordered basket")
}
// Add or update items
for _, item := range req.Items {
if existing, exists := basket.Items[item.ProductId]; exists {
existing.Count += int(item.Count)
existing.Price = item.Price // update price if needed
} else {
basket.Items[item.ProductId] = &BasketItem{
ProductID: item.ProductId,
Count: int(item.Count),
Price: item.Price,
}
}
}
// Recalculate total
var total float64
for _, item := range basket.Items {
total += float64(item.Count) * item.Price
}
basket.Total = total
// If the request has a checkout flag
if req.Order { // assume there's a boolean field Order
basket.Status = StatusOrdered
// Send message to broker
err := s.broker.Publish("basket_ordered", map[string]interface{}{
"basket_id": basket.ID,
"total": basket.Total,
"items": basket.Items,
})
if err != nil {
return nil, err
}
}
return &AddItemAndOrderResponse{
Total: basket.Total,
Status: string(basket.Status),
}, nil
}
In this example:
- A mutex protects basket data from race conditions.
- Items are stored in a map for uniqueness by
ProductID. - On checkout, the status changes and an event is sent via the broker.
- After checkout, modifications are prohibited.
For a real application, validation, error handling, and integration with a real message broker should be added.