service.go 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. // Package booking provides the use-case of booking a shipping. Used by views
  2. // facing an administrator.
  3. package booking
  4. import (
  5. "errors"
  6. "time"
  7. shipping "github.com/longjoy/micro-go-course/section19/cargo/model"
  8. )
  9. // ErrInvalidArgument is returned when one or more arguments are invalid.
  10. var ErrInvalidArgument = errors.New("invalid argument")
  11. // Service is the interface that provides booking methods.
  12. type Service interface {
  13. // BookNewCargo registers a new cargo in the tracking system, not yet
  14. // routed.
  15. BookNewCargo(origin shipping.UNLocode, destination shipping.UNLocode, deadline time.Time) (shipping.TrackingID, error)
  16. // LoadCargo returns a read model of a shipping.
  17. LoadCargo(id shipping.TrackingID) (Cargo, error)
  18. // RequestPossibleRoutesForCargo requests a list of itineraries describing
  19. // possible routes for this shipping.
  20. RequestPossibleRoutesForCargo(id shipping.TrackingID) []shipping.Itinerary
  21. // AssignCargoToRoute assigns a cargo to the route specified by the
  22. // itinerary.
  23. AssignCargoToRoute(id shipping.TrackingID, itinerary shipping.Itinerary) (bool, error)
  24. // ChangeDestination changes the destination of a shipping.
  25. ChangeDestination(id shipping.TrackingID, destination shipping.UNLocode) (bool, error)
  26. // Cargos returns a list of all cargos that have been booked.
  27. Cargos() []Cargo
  28. // Locations returns a list of registered locations.
  29. Locations() []Location
  30. }
  31. type service struct {
  32. cargos shipping.CargoRepository
  33. locations shipping.LocationRepository
  34. handlingEvents shipping.HandlingEventRepository
  35. routingService shipping.RoutingService
  36. }
  37. func (s *service) AssignCargoToRoute(id shipping.TrackingID, itinerary shipping.Itinerary) (bool, error) {
  38. if id == "" || len(itinerary.Legs) == 0 {
  39. return false, ErrInvalidArgument
  40. }
  41. c, err := s.cargos.Find(id)
  42. if err != nil {
  43. return false, err
  44. }
  45. c.AssignToRoute(itinerary)
  46. return s.cargos.Store(c)
  47. }
  48. func (s *service) BookNewCargo(origin, destination shipping.UNLocode, deadline time.Time) (shipping.TrackingID, error) {
  49. if origin == "" || destination == "" || deadline.IsZero() {
  50. return "", ErrInvalidArgument
  51. }
  52. id := shipping.NextTrackingID()
  53. rs := shipping.RouteSpecification{
  54. Origin: origin,
  55. Destination: destination,
  56. ArrivalDeadline: deadline,
  57. }
  58. c := shipping.NewCargo(id, rs)
  59. if _, err := s.cargos.Store(c); err != nil {
  60. return "", err
  61. }
  62. return c.TrackingID, nil
  63. }
  64. func (s *service) LoadCargo(id shipping.TrackingID) (Cargo, error) {
  65. if id == "" {
  66. return Cargo{}, ErrInvalidArgument
  67. }
  68. c, err := s.cargos.Find(id)
  69. if err != nil {
  70. return Cargo{}, err
  71. }
  72. return assemble(c, s.handlingEvents), nil
  73. }
  74. func (s *service) ChangeDestination(id shipping.TrackingID, destination shipping.UNLocode) (bool, error) {
  75. if id == "" || destination == "" {
  76. return false, ErrInvalidArgument
  77. }
  78. c, err := s.cargos.Find(id)
  79. if err != nil {
  80. return false, err
  81. }
  82. l, err := s.locations.Find(destination)
  83. if err != nil {
  84. return false, err
  85. }
  86. c.SpecifyNewRoute(shipping.RouteSpecification{
  87. Origin: c.Origin,
  88. Destination: l.UNLocode,
  89. ArrivalDeadline: c.RouteSpecification.ArrivalDeadline,
  90. })
  91. if _, err := s.cargos.Store(c); err != nil {
  92. return false, err
  93. }
  94. return true, nil
  95. }
  96. func (s *service) RequestPossibleRoutesForCargo(id shipping.TrackingID) []shipping.Itinerary {
  97. if id == "" {
  98. return nil
  99. }
  100. c, err := s.cargos.Find(id)
  101. if err != nil {
  102. return []shipping.Itinerary{}
  103. }
  104. return s.routingService.FetchRoutesForSpecification(c.RouteSpecification)
  105. }
  106. func (s *service) Cargos() []Cargo {
  107. var result []Cargo
  108. for _, c := range s.cargos.FindAll() {
  109. result = append(result, assemble(c, s.handlingEvents))
  110. }
  111. return result
  112. }
  113. func (s *service) Locations() []Location {
  114. var result []Location
  115. for _, v := range s.locations.FindAll() {
  116. result = append(result, Location{
  117. UNLocode: string(v.UNLocode),
  118. Name: v.Name,
  119. })
  120. }
  121. return result
  122. }
  123. // NewService creates a booking service with necessary dependencies.
  124. func NewService(cargos shipping.CargoRepository, locations shipping.LocationRepository, events shipping.HandlingEventRepository) Service {
  125. return &service{
  126. cargos: cargos,
  127. locations: locations,
  128. handlingEvents: events,
  129. }
  130. }
  131. // Location is a read model for booking views.
  132. type Location struct {
  133. UNLocode string `json:"locode"`
  134. Name string `json:"name"`
  135. }
  136. // Cargo is a read model for booking views.
  137. type Cargo struct {
  138. ArrivalDeadline time.Time `json:"arrival_deadline"`
  139. Destination string `json:"destination"`
  140. Legs []shipping.Leg `json:"legs,omitempty"`
  141. Misrouted bool `json:"misrouted"`
  142. Origin string `json:"origin"`
  143. Routed bool `json:"routed"`
  144. TrackingID string `json:"tracking_id"`
  145. }
  146. func assemble(c *shipping.Cargo, events shipping.HandlingEventRepository) Cargo {
  147. return Cargo{
  148. TrackingID: string(c.TrackingID),
  149. Origin: string(c.Origin),
  150. Destination: string(c.RouteSpecification.Destination),
  151. Misrouted: c.Delivery.RoutingStatus == shipping.Misrouted,
  152. Routed: !c.Itinerary.IsEmpty(),
  153. ArrivalDeadline: c.RouteSpecification.ArrivalDeadline,
  154. Legs: c.Itinerary.Legs,
  155. }
  156. }