| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194 | 
							- // Package booking provides the use-case of booking a shipping. Used by views
 
- // facing an administrator.
 
- package booking
 
- import (
 
- 	"errors"
 
- 	"time"
 
- 	shipping "github.com/longjoy/micro-go-course/section19/cargo/model"
 
- )
 
- // ErrInvalidArgument is returned when one or more arguments are invalid.
 
- var ErrInvalidArgument = errors.New("invalid argument")
 
- // Service is the interface that provides booking methods.
 
- type Service interface {
 
- 	// BookNewCargo registers a new cargo in the tracking system, not yet
 
- 	// routed.
 
- 	BookNewCargo(origin shipping.UNLocode, destination shipping.UNLocode, deadline time.Time) (shipping.TrackingID, error)
 
- 	// LoadCargo returns a read model of a shipping.
 
- 	LoadCargo(id shipping.TrackingID) (Cargo, error)
 
- 	// RequestPossibleRoutesForCargo requests a list of itineraries describing
 
- 	// possible routes for this shipping.
 
- 	RequestPossibleRoutesForCargo(id shipping.TrackingID) []shipping.Itinerary
 
- 	// AssignCargoToRoute assigns a cargo to the route specified by the
 
- 	// itinerary.
 
- 	AssignCargoToRoute(id shipping.TrackingID, itinerary shipping.Itinerary) (bool, error)
 
- 	// ChangeDestination changes the destination of a shipping.
 
- 	ChangeDestination(id shipping.TrackingID, destination shipping.UNLocode) (bool, error)
 
- 	// Cargos returns a list of all cargos that have been booked.
 
- 	Cargos() []Cargo
 
- 	// Locations returns a list of registered locations.
 
- 	Locations() []Location
 
- }
 
- type service struct {
 
- 	cargos         shipping.CargoRepository
 
- 	locations      shipping.LocationRepository
 
- 	handlingEvents shipping.HandlingEventRepository
 
- 	routingService shipping.RoutingService
 
- }
 
- func (s *service) AssignCargoToRoute(id shipping.TrackingID, itinerary shipping.Itinerary) (bool, error) {
 
- 	if id == "" || len(itinerary.Legs) == 0 {
 
- 		return false, ErrInvalidArgument
 
- 	}
 
- 	c, err := s.cargos.Find(id)
 
- 	if err != nil {
 
- 		return false, err
 
- 	}
 
- 	c.AssignToRoute(itinerary)
 
- 	return s.cargos.Store(c)
 
- }
 
- func (s *service) BookNewCargo(origin, destination shipping.UNLocode, deadline time.Time) (shipping.TrackingID, error) {
 
- 	if origin == "" || destination == "" || deadline.IsZero() {
 
- 		return "", ErrInvalidArgument
 
- 	}
 
- 	id := shipping.NextTrackingID()
 
- 	rs := shipping.RouteSpecification{
 
- 		Origin:          origin,
 
- 		Destination:     destination,
 
- 		ArrivalDeadline: deadline,
 
- 	}
 
- 	c := shipping.NewCargo(id, rs)
 
- 	if _, err := s.cargos.Store(c); err != nil {
 
- 		return "", err
 
- 	}
 
- 	return c.TrackingID, nil
 
- }
 
- func (s *service) LoadCargo(id shipping.TrackingID) (Cargo, error) {
 
- 	if id == "" {
 
- 		return Cargo{}, ErrInvalidArgument
 
- 	}
 
- 	c, err := s.cargos.Find(id)
 
- 	if err != nil {
 
- 		return Cargo{}, err
 
- 	}
 
- 	return assemble(c, s.handlingEvents), nil
 
- }
 
- func (s *service) ChangeDestination(id shipping.TrackingID, destination shipping.UNLocode) (bool, error) {
 
- 	if id == "" || destination == "" {
 
- 		return false, ErrInvalidArgument
 
- 	}
 
- 	c, err := s.cargos.Find(id)
 
- 	if err != nil {
 
- 		return false, err
 
- 	}
 
- 	l, err := s.locations.Find(destination)
 
- 	if err != nil {
 
- 		return false, err
 
- 	}
 
- 	c.SpecifyNewRoute(shipping.RouteSpecification{
 
- 		Origin:          c.Origin,
 
- 		Destination:     l.UNLocode,
 
- 		ArrivalDeadline: c.RouteSpecification.ArrivalDeadline,
 
- 	})
 
- 	if _, err := s.cargos.Store(c); err != nil {
 
- 		return false, err
 
- 	}
 
- 	return true, nil
 
- }
 
- func (s *service) RequestPossibleRoutesForCargo(id shipping.TrackingID) []shipping.Itinerary {
 
- 	if id == "" {
 
- 		return nil
 
- 	}
 
- 	c, err := s.cargos.Find(id)
 
- 	if err != nil {
 
- 		return []shipping.Itinerary{}
 
- 	}
 
- 	return s.routingService.FetchRoutesForSpecification(c.RouteSpecification)
 
- }
 
- func (s *service) Cargos() []Cargo {
 
- 	var result []Cargo
 
- 	for _, c := range s.cargos.FindAll() {
 
- 		result = append(result, assemble(c, s.handlingEvents))
 
- 	}
 
- 	return result
 
- }
 
- func (s *service) Locations() []Location {
 
- 	var result []Location
 
- 	for _, v := range s.locations.FindAll() {
 
- 		result = append(result, Location{
 
- 			UNLocode: string(v.UNLocode),
 
- 			Name:     v.Name,
 
- 		})
 
- 	}
 
- 	return result
 
- }
 
- // NewService creates a booking service with necessary dependencies.
 
- func NewService(cargos shipping.CargoRepository, locations shipping.LocationRepository, events shipping.HandlingEventRepository) Service {
 
- 	return &service{
 
- 		cargos:         cargos,
 
- 		locations:      locations,
 
- 		handlingEvents: events,
 
- 	}
 
- }
 
- // Location is a read model for booking views.
 
- type Location struct {
 
- 	UNLocode string `json:"locode"`
 
- 	Name     string `json:"name"`
 
- }
 
- // Cargo is a read model for booking views.
 
- type Cargo struct {
 
- 	ArrivalDeadline time.Time      `json:"arrival_deadline"`
 
- 	Destination     string         `json:"destination"`
 
- 	Legs            []shipping.Leg `json:"legs,omitempty"`
 
- 	Misrouted       bool           `json:"misrouted"`
 
- 	Origin          string         `json:"origin"`
 
- 	Routed          bool           `json:"routed"`
 
- 	TrackingID      string         `json:"tracking_id"`
 
- }
 
- func assemble(c *shipping.Cargo, events shipping.HandlingEventRepository) Cargo {
 
- 	return Cargo{
 
- 		TrackingID:      string(c.TrackingID),
 
- 		Origin:          string(c.Origin),
 
- 		Destination:     string(c.RouteSpecification.Destination),
 
- 		Misrouted:       c.Delivery.RoutingStatus == shipping.Misrouted,
 
- 		Routed:          !c.Itinerary.IsEmpty(),
 
- 		ArrivalDeadline: c.RouteSpecification.ArrivalDeadline,
 
- 		Legs:            c.Itinerary.Legs,
 
- 	}
 
- }
 
 
  |