init push

This commit is contained in:
2026-07-16 22:16:45 +07:00
commit 8b068bdb10
1021 changed files with 332816 additions and 0 deletions

View File

@@ -0,0 +1,8 @@
package fmreport
import "errors"
var (
ErrNotFound = errors.New("fm report not found")
ErrCompleted = errors.New("fm report already completed")
)

View File

@@ -0,0 +1,79 @@
package fmreport
import (
"time"
"gorm.io/gorm"
"wucher/internal/shared/pkg/uuidv7"
)
// FleetHistory is the 1:1 snapshot of the flown helicopter's fleet status, frozen
// when an FM report is created at after-flight inspection. It is read-only
// afterwards so later complaints / maintenance changes do not alter a historical
// report. Next-inspection rows use fixed columns (A/F, ENG1, ENG2, MAIN1, MAIN2)
// mirroring the report layout.
type FleetHistory struct {
ID []byte `gorm:"type:binary(16);primaryKey;column:id"`
FMReportID []byte `gorm:"type:binary(16);not null;uniqueIndex:uidx_fm_report_fleet_history_fm_report_id;column:fm_report_id"`
HelicopterStatus string `gorm:"type:varchar(16);column:helicopter_status"`
AirframeHours float64 `gorm:"type:decimal(12,2);not null;default:0;column:airframe_hours"`
Engine1Hours float64 `gorm:"type:decimal(12,2);not null;default:0;column:engine1_hours"`
Engine2Hours float64 `gorm:"type:decimal(12,2);not null;default:0;column:engine2_hours"`
AFType string `gorm:"type:varchar(255);column:af_type"`
AFDue string `gorm:"type:varchar(255);column:af_due"`
AFExt string `gorm:"type:varchar(255);column:af_ext"`
Eng1Type string `gorm:"type:varchar(255);column:eng1_type"`
Eng1Due string `gorm:"type:varchar(255);column:eng1_due"`
Eng1Ext string `gorm:"type:varchar(255);column:eng1_ext"`
Eng2Type string `gorm:"type:varchar(255);column:eng2_type"`
Eng2Due string `gorm:"type:varchar(255);column:eng2_due"`
Eng2Ext string `gorm:"type:varchar(255);column:eng2_ext"`
Main1Type string `gorm:"type:varchar(255);column:main1_type"`
Main1Due string `gorm:"type:varchar(255);column:main1_due"`
Main1Ext string `gorm:"type:varchar(255);column:main1_ext"`
Main2Type string `gorm:"type:varchar(255);column:main2_type"`
Main2Due string `gorm:"type:varchar(255);column:main2_due"`
Main2Ext string `gorm:"type:varchar(255);column:main2_ext"`
EASAReleaseID []byte `gorm:"type:binary(16);column:easa_release_id"`
EASAIsSigned bool `gorm:"not null;default:false;column:easa_is_signed"`
EASADate *time.Time `gorm:"type:date;column:easa_date"`
EASAACHours *string `gorm:"type:varchar(20);column:easa_ac_hours"`
EASALocation *string `gorm:"type:varchar(255);column:easa_location"`
EASAWONo *string `gorm:"type:varchar(100);column:easa_wo_no"`
EASAMaintManualRevAirframe *string `gorm:"type:varchar(100);column:easa_maint_manual_rev_airframe"`
EASAMaintManualRevEngine *string `gorm:"type:varchar(100);column:easa_maint_manual_rev_engine"`
EASASignerID *string `gorm:"type:varchar(100);column:easa_signer_id"`
EASAPermitNo *string `gorm:"type:varchar(100);column:easa_permit_no"`
EASASignedAt *time.Time `gorm:"type:datetime(3);column:easa_signed_at"`
EASASignedBy []byte `gorm:"type:binary(16);column:easa_signed_by"`
MCFID []byte `gorm:"type:binary(16);column:mcf_id"`
MCFIsSigned bool `gorm:"not null;default:false;column:mcf_is_signed"`
MCFDate *time.Time `gorm:"type:date;column:mcf_date"`
MCFAFHours *string `gorm:"type:varchar(20);column:mcf_af_hours"`
MCFLandingCount *int `gorm:"column:mcf_landing_count"`
MCFSignedBy []byte `gorm:"type:binary(16);column:mcf_signed_by"`
MCFSignedByName string `gorm:"type:varchar(255);column:mcf_signed_by_name"`
CreatedAt time.Time `gorm:"column:created_at"`
}
func (FleetHistory) TableName() string { return "fm_report_fleet_history" }
func (f *FleetHistory) BeforeCreate(_ *gorm.DB) error {
if len(f.ID) == 0 {
f.ID = uuidv7.MustBytes()
}
return nil
}
// HasEASA reports whether an EASA release was captured in this snapshot.
func (f *FleetHistory) HasEASA() bool { return f != nil && len(f.EASAReleaseID) == 16 }
// HasMCF reports whether an MCF was captured in this snapshot.
func (f *FleetHistory) HasMCF() bool { return f != nil && len(f.MCFID) == 16 }

View File

@@ -0,0 +1,41 @@
package fmreport
import (
"time"
"gorm.io/gorm"
"wucher/internal/shared/pkg/uuidv7"
)
type Report struct {
ID []byte `gorm:"type:binary(16);primaryKey;column:id"`
TakeoverID []byte `gorm:"type:binary(16);not null;uniqueIndex:uidx_fm_reports_takeover_id;index;column:takeover_id"`
FlightID []byte `gorm:"type:binary(16);not null;uniqueIndex:uidx_fm_reports_flight_id;index;column:flight_id"`
FlightInspectionID []byte `gorm:"type:binary(16);not null;uniqueIndex:uidx_fm_reports_flight_inspection_id;index;column:flight_inspection_id"`
HelicopterID []byte `gorm:"type:binary(16);uniqueIndex:uidx_fm_reports_helicopter_report_code,priority:1;column:helicopter_id"`
ReportCode *string `gorm:"type:varchar(32);uniqueIndex:uidx_fm_reports_helicopter_report_code,priority:2;column:report_code"`
Engine1GpcN1 *string `gorm:"type:varchar(255);null;column:engine1_gpc_n1"`
Engine1PtcN2 *string `gorm:"type:varchar(255);null;column:engine1_ptc_n2"`
Engine2GpcN1 *string `gorm:"type:varchar(255);null;column:engine2_gpc_n1"`
Engine2PtcN2 *string `gorm:"type:varchar(255);null;column:engine2_ptc_n2"`
CompletedAt *time.Time `gorm:"type:datetime(3);column:completed_at"`
CompletedBy []byte `gorm:"type:binary(16);column:completed_by"`
CreatedAt time.Time `gorm:"column:created_at"`
CreatedBy []byte `gorm:"type:binary(16);column:created_by"`
UpdatedAt time.Time `gorm:"column:updated_at"`
UpdatedBy []byte `gorm:"type:binary(16);column:updated_by"`
DeletedAt *time.Time `gorm:"column:deleted_at;index"`
DeletedBy []byte `gorm:"type:binary(16);column:deleted_by"`
}
func (Report) TableName() string { return "fm_reports" }
func (r *Report) BeforeCreate(tx *gorm.DB) error {
if len(r.ID) == 0 {
r.ID = uuidv7.MustBytes()
}
return nil
}

View File

@@ -0,0 +1,37 @@
package fmreport
import (
"context"
"errors"
)
var ErrDuplicateReportCode = errors.New("duplicate report code")
type ListFilter struct {
Search string
FlightID []byte
TakeoverID []byte
FlightInspectionID []byte
FlightDate string
HasEngine1GpcN1 *bool
HasEngine1PtcN2 *bool
HasEngine2GpcN1 *bool
HasEngine2PtcN2 *bool
Sort string
FromDate string
ToDate string
HelicopterIdentifier string
PilotName string
}
type Repository interface {
Upsert(ctx context.Context, row *Report) error
GetByFlightID(ctx context.Context, flightID []byte) (*Report, error)
GetByID(ctx context.Context, id []byte) (*Report, error)
MaxReportSeqByHelicopter(ctx context.Context, helicopterID []byte) (int, error)
SetReportCode(ctx context.Context, id []byte, code string) error
List(ctx context.Context, filter ListFilter, limit, offset int) ([]Report, int64, error)
Delete(ctx context.Context, id, deletedBy []byte) error
CreateFleetHistory(ctx context.Context, row *FleetHistory) error
GetFleetHistoryByReportID(ctx context.Context, reportID []byte) (*FleetHistory, error)
}

View File

@@ -0,0 +1,25 @@
package fmreport
import (
"context"
"wucher/internal/domain/flight"
)
type Service interface {
UpsertForFlight(ctx context.Context, flightRow *flight.Flight, flightInspectionID []byte) (*Report, error)
UpdateForFlight(ctx context.Context, flightID []byte, engine1GpcN1, engine1PtcN2, engine2GpcN1, engine2PtcN2 *string) (*Report, error)
GetByID(ctx context.Context, id []byte) (*Report, error)
GetByFlightID(ctx context.Context, flightID []byte) (*Report, error)
List(ctx context.Context, filter ListFilter, limit, offset int) ([]Report, int64, error)
GetFleetHistory(ctx context.Context, reportID []byte) (*FleetHistory, error)
Delete(ctx context.Context, id []byte) error
}
type FleetHistoryBuilder interface {
BuildFleetHistory(ctx context.Context, takeoverID []byte) *FleetHistory
}
type ReportCodeResolver interface {
ResolveHelicopterForReportCode(ctx context.Context, takeoverID []byte) (helicopterID []byte, reportPrefix string)
}