init push
This commit is contained in:
332
internal/service/flight_data_service.go
Normal file
332
internal/service/flight_data_service.go
Normal file
@@ -0,0 +1,332 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
afterflightinspection "wucher/internal/domain/after_flight_inspection"
|
||||
flightdomain "wucher/internal/domain/flight"
|
||||
flightdata "wucher/internal/domain/flight_data"
|
||||
fmreport "wucher/internal/domain/fm_report"
|
||||
sharedconst "wucher/internal/shared/const"
|
||||
"wucher/internal/shared/pkg/uuidv7"
|
||||
)
|
||||
|
||||
type aogRecalculator interface {
|
||||
RecalculateAOGByMission(ctx context.Context, missionID []byte) error
|
||||
}
|
||||
|
||||
type missionFlightResolver interface {
|
||||
FlightIDByMissionID(ctx context.Context, missionID []byte) ([]byte, error)
|
||||
}
|
||||
|
||||
type fmReportLookup interface {
|
||||
GetByFlightID(ctx context.Context, flightID []byte) (*fmreport.Report, error)
|
||||
}
|
||||
|
||||
type flightLookup interface {
|
||||
GetByID(context.Context, []byte) (*flightdomain.Flight, error)
|
||||
}
|
||||
|
||||
type afterFlightInspectionLookup interface {
|
||||
GetByFlightInspectionID(context.Context, []byte) (*afterflightinspection.AfterFlightInspection, error)
|
||||
}
|
||||
|
||||
type FlightDataService struct {
|
||||
repo flightdata.Repository
|
||||
aog aogRecalculator
|
||||
mission missionFlightResolver
|
||||
flight flightLookup
|
||||
fmReports fmReportLookup
|
||||
afterFlight afterFlightInspectionLookup
|
||||
}
|
||||
|
||||
func NewFlightDataService(repo flightdata.Repository) *FlightDataService {
|
||||
return &FlightDataService{repo: repo}
|
||||
}
|
||||
|
||||
func (s *FlightDataService) WithAOGRecalculator(a aogRecalculator) *FlightDataService {
|
||||
s.aog = a
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *FlightDataService) WithMissionResolver(resolver missionFlightResolver) *FlightDataService {
|
||||
s.mission = resolver
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *FlightDataService) WithFlightLookup(lookup flightLookup) *FlightDataService {
|
||||
s.flight = lookup
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *FlightDataService) WithFMReportLookup(lookup fmReportLookup) *FlightDataService {
|
||||
s.fmReports = lookup
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *FlightDataService) WithAfterFlightInspectionLookup(lookup afterFlightInspectionLookup) *FlightDataService {
|
||||
s.afterFlight = lookup
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *FlightDataService) recalcAOG(ctx context.Context, missionID []byte) {
|
||||
if s.aog == nil || len(missionID) == 0 {
|
||||
return
|
||||
}
|
||||
_ = s.aog.RecalculateAOGByMission(ctx, missionID)
|
||||
}
|
||||
|
||||
func (s *FlightDataService) Create(ctx context.Context, row *flightdata.FlightData) error {
|
||||
if row == nil {
|
||||
return flightdata.ErrRequired
|
||||
}
|
||||
if err := validateFlightDataLocationPair(row.FromICAOID, row.FromHospitalID, true); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateFlightDataLocationPair(row.ToICAOID, row.ToHospitalID, false); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.canCreateForMission(ctx, row.MissionID); err != nil {
|
||||
return err
|
||||
}
|
||||
row.Status = flightdata.StatusInProgress
|
||||
if err := s.repo.Create(ctx, row); err != nil {
|
||||
return err
|
||||
}
|
||||
s.recalcAOG(ctx, row.MissionID)
|
||||
if err := s.syncStatus(ctx, row.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *FlightDataService) CreatePlaceholder(ctx context.Context, row *flightdata.FlightData) error {
|
||||
if row == nil {
|
||||
return flightdata.ErrRequired
|
||||
}
|
||||
if err := s.canCreateForMission(ctx, row.MissionID); err != nil {
|
||||
return err
|
||||
}
|
||||
row.Status = flightdata.StatusInProgress
|
||||
if err := s.repo.Create(ctx, row); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *FlightDataService) Update(ctx context.Context, row *flightdata.FlightData) error {
|
||||
if row == nil {
|
||||
return flightdata.ErrRequired
|
||||
}
|
||||
if err := validateFlightDataLocationPair(row.FromICAOID, row.FromHospitalID, true); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateFlightDataLocationPair(row.ToICAOID, row.ToHospitalID, false); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.ensureEditable(ctx, row.MissionID); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.repo.Update(ctx, row); err != nil {
|
||||
return err
|
||||
}
|
||||
s.recalcAOG(ctx, row.MissionID)
|
||||
if err := s.syncStatus(ctx, row.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *FlightDataService) Delete(ctx context.Context, id []byte, deletedBy []byte) error {
|
||||
var missionID []byte
|
||||
if row, err := s.repo.GetByID(ctx, id); err == nil && row != nil {
|
||||
missionID = row.MissionID
|
||||
if err := s.ensureAfterFlightInspectionNotLocked(ctx, missionID); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.ensureEditable(ctx, missionID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := s.repo.Delete(ctx, id, deletedBy); err != nil {
|
||||
return err
|
||||
}
|
||||
s.recalcAOG(ctx, missionID)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *FlightDataService) GetByID(ctx context.Context, id []byte) (*flightdata.FlightData, error) {
|
||||
return s.repo.GetByID(ctx, id)
|
||||
}
|
||||
|
||||
func (s *FlightDataService) GetByFlightID(ctx context.Context, flightID []byte) (*flightdata.FlightData, error) {
|
||||
return s.repo.GetByFlightID(ctx, flightID)
|
||||
}
|
||||
|
||||
func (s *FlightDataService) GetByMissionID(ctx context.Context, missionID []byte) (*flightdata.FlightData, error) {
|
||||
return s.repo.GetByMissionID(ctx, missionID)
|
||||
}
|
||||
|
||||
func (s *FlightDataService) ListByMissionID(ctx context.Context, missionID []byte) ([]flightdata.FlightData, error) {
|
||||
return s.repo.ListByMissionID(ctx, missionID)
|
||||
}
|
||||
|
||||
func (s *FlightDataService) List(ctx context.Context, date string, missionID, flightDataID []byte, limit, offset int) ([]flightdata.FlightData, int64, error) {
|
||||
return s.repo.List(ctx, date, missionID, flightDataID, limit, offset)
|
||||
}
|
||||
|
||||
func (s *FlightDataService) GetSPODetailsByFlightDataID(ctx context.Context, flightDataID []byte) (*flightdata.SPODetails, error) {
|
||||
return s.repo.GetSPODetailsByFlightDataID(ctx, flightDataID)
|
||||
}
|
||||
|
||||
func (s *FlightDataService) UpsertSPODetails(ctx context.Context, flightDataID []byte, data *flightdata.SPOUpsertData) error {
|
||||
return s.repo.UpsertSPODetails(ctx, flightDataID, data)
|
||||
}
|
||||
|
||||
func (s *FlightDataService) CanCreateForMission(ctx context.Context, missionID []byte) error {
|
||||
return s.canCreateForMission(ctx, missionID)
|
||||
}
|
||||
|
||||
func (s *FlightDataService) canCreateForMission(ctx context.Context, missionID []byte) error {
|
||||
if err := s.ensureAfterFlightInspectionNotLocked(ctx, missionID); err != nil {
|
||||
return err
|
||||
}
|
||||
flightID, err := s.flightIDByMissionID(ctx, missionID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(flightID) != 16 || s.fmReports == nil {
|
||||
return nil
|
||||
}
|
||||
report, err := s.fmReports.GetByFlightID(ctx, flightID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if report != nil {
|
||||
return flightdata.ErrCreateBlocked
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *FlightDataService) ensureEditable(ctx context.Context, missionID []byte) error {
|
||||
flightID, err := s.flightIDByMissionID(ctx, missionID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(flightID) != 16 || s.fmReports == nil {
|
||||
return nil
|
||||
}
|
||||
report, err := s.fmReports.GetByFlightID(ctx, flightID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if report != nil && report.CompletedAt != nil {
|
||||
return flightdata.ErrLocked
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *FlightDataService) ensureAfterFlightInspectionNotLocked(ctx context.Context, missionID []byte) error {
|
||||
flightID, err := s.flightIDByMissionID(ctx, missionID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(flightID) != 16 || s.flight == nil || s.afterFlight == nil {
|
||||
return nil
|
||||
}
|
||||
flightRow, err := s.flight.GetByID(ctx, flightID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if flightRow == nil || flightRow.Takeover == nil || flightRow.Takeover.ReserveAc == nil || len(flightRow.Takeover.ReserveAc.InspectionID) != 16 {
|
||||
return nil
|
||||
}
|
||||
afterRow, err := s.afterFlight.GetByFlightInspectionID(ctx, flightRow.Takeover.ReserveAc.InspectionID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if afterRow != nil {
|
||||
return flightdata.ErrLockedAfterFlightInspection
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *FlightDataService) flightIDByMissionID(ctx context.Context, missionID []byte) ([]byte, error) {
|
||||
if len(missionID) != 16 || s.mission == nil {
|
||||
return nil, nil
|
||||
}
|
||||
flightID, err := s.mission.FlightIDByMissionID(ctx, missionID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return flightID, nil
|
||||
}
|
||||
|
||||
func (s *FlightDataService) AttachmentReferenceType() string {
|
||||
return sharedconst.FlightDataResourceType
|
||||
}
|
||||
|
||||
func (s *FlightDataService) AttachmentReferenceID(row *flightdata.FlightData) string {
|
||||
if row == nil {
|
||||
return ""
|
||||
}
|
||||
id, err := uuidv7.BytesToString(row.ID)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
func (s *FlightDataService) syncStatus(ctx context.Context, id []byte) error {
|
||||
if len(id) != 16 {
|
||||
return nil
|
||||
}
|
||||
|
||||
row, err := s.repo.GetByID(ctx, id)
|
||||
if err != nil || row == nil {
|
||||
return err
|
||||
}
|
||||
if row.Mission == nil && len(row.MissionID) == 16 {
|
||||
if byMissionID, lookupErr := s.repo.GetByMissionID(ctx, row.MissionID); lookupErr == nil && byMissionID != nil {
|
||||
row = byMissionID
|
||||
}
|
||||
}
|
||||
|
||||
details, err := s.loadSPODetails(ctx, row)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
desired := flightdata.DeriveStatus(row, details)
|
||||
if strings.EqualFold(strings.TrimSpace(row.Status), desired) {
|
||||
return nil
|
||||
}
|
||||
row.Status = desired
|
||||
return s.repo.Update(ctx, row)
|
||||
}
|
||||
|
||||
func (s *FlightDataService) loadSPODetails(ctx context.Context, row *flightdata.FlightData) (*flightdata.SPODetails, error) {
|
||||
if row == nil || row.Mission == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if !strings.EqualFold(strings.TrimSpace(row.Mission.Type), sharedconst.MissionTypeSPO) {
|
||||
return nil, nil
|
||||
}
|
||||
details, err := s.repo.GetSPODetailsByFlightDataID(ctx, row.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return details, nil
|
||||
}
|
||||
|
||||
func validateFlightDataLocationPair(a, b []byte, isOrigin bool) error {
|
||||
hasA := len(a) > 0
|
||||
hasB := len(b) > 0
|
||||
if hasA == hasB {
|
||||
if isOrigin {
|
||||
return flightdata.ErrOriginLocationReference
|
||||
}
|
||||
return flightdata.ErrDestinationLocationReference
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user