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,
- }
- }
|