package mysql import ( "context" "errors" "strings" mysqldriver "github.com/go-sql-driver/mysql" "gorm.io/gorm" "gorm.io/gorm/clause" fmreport "wucher/internal/domain/fm_report" ) type FMReportRepository struct { db *gorm.DB } func NewFMReportRepository(db *gorm.DB) *FMReportRepository { return &FMReportRepository{db: db} } func (r *FMReportRepository) MaxReportSeqByHelicopter(ctx context.Context, helicopterID []byte) (int, error) { if len(helicopterID) != 16 { return 0, nil } var maxSeq *int err := r.db.WithContext(ctx). Model(&fmreport.Report{}). Where("helicopter_id = ? AND report_code IS NOT NULL AND report_code <> '' AND deleted_at IS NULL", helicopterID). Select("MAX(CAST(SUBSTRING_INDEX(report_code, '-', -1) AS UNSIGNED))"). Scan(&maxSeq).Error if err != nil { return 0, err } if maxSeq == nil { return 0, nil } return *maxSeq, nil } func (r *FMReportRepository) SetReportCode(ctx context.Context, id []byte, code string) error { if len(id) != 16 { return nil } err := r.db.WithContext(ctx). Model(&fmreport.Report{}). Where("id = ?", id). Update("report_code", code).Error if isDuplicateKeyError(err) { return fmreport.ErrDuplicateReportCode } return err } func isDuplicateKeyError(err error) bool { if err == nil { return false } if errors.Is(err, gorm.ErrDuplicatedKey) { return true } var mysqlErr *mysqldriver.MySQLError return errors.As(err, &mysqlErr) && mysqlErr.Number == 1062 } func (r *FMReportRepository) Upsert(ctx context.Context, row *fmreport.Report) error { if row == nil { return nil } return r.db.WithContext(ctx). Clauses(clause.OnConflict{ Columns: []clause.Column{{Name: "flight_id"}}, DoUpdates: clause.AssignmentColumns([]string{ "takeover_id", "flight_inspection_id", "engine1_gpc_n1", "engine1_ptc_n2", "engine2_gpc_n1", "engine2_ptc_n2", "completed_at", "completed_by", "updated_at", "updated_by", }), }).Create(row).Error } func (r *FMReportRepository) GetByFlightID(ctx context.Context, flightID []byte) (*fmreport.Report, error) { var row fmreport.Report err := r.db.WithContext(ctx). Where("flight_id = ? AND deleted_at IS NULL", flightID). First(&row).Error if errors.Is(err, gorm.ErrRecordNotFound) { return nil, nil } return &row, err } func (r *FMReportRepository) List(ctx context.Context, filter fmreport.ListFilter, limit, offset int) ([]fmreport.Report, int64, error) { rows := make([]fmreport.Report, 0) var total int64 base := r.db.WithContext(ctx). Model(&fmreport.Report{}). Joins("JOIN flights f ON f.id = fm_reports.flight_id AND f.deleted_at IS NULL"). Where("fm_reports.deleted_at IS NULL") if len(filter.FlightID) == 16 { base = base.Where("fm_reports.flight_id = ?", filter.FlightID) } if len(filter.TakeoverID) == 16 { base = base.Where("fm_reports.takeover_id = ?", filter.TakeoverID) } if len(filter.FlightInspectionID) == 16 { base = base.Where("fm_reports.flight_inspection_id = ?", filter.FlightInspectionID) } if strings.TrimSpace(filter.FlightDate) != "" { base = base.Where("f.date = ?", strings.TrimSpace(filter.FlightDate)) } if strings.TrimSpace(filter.FromDate) != "" { base = base.Where("f.date >= ?", strings.TrimSpace(filter.FromDate)) } if strings.TrimSpace(filter.ToDate) != "" { base = base.Where("f.date <= ?", strings.TrimSpace(filter.ToDate)) } if strings.TrimSpace(filter.HelicopterIdentifier) != "" { base = base. Joins("LEFT JOIN takeover_acs ta ON ta.id = fm_reports.takeover_id AND ta.deleted_at IS NULL"). Joins("LEFT JOIN reserve_acs ra ON ra.id = ta.reserve_ac_id AND ra.deleted_at IS NULL"). Joins("LEFT JOIN helicopters heli ON heli.id = ra.helicopter_id AND heli.deleted_at IS NULL"). Where("heli.identifier LIKE ?", "%"+strings.TrimSpace(filter.HelicopterIdentifier)+"%") } if strings.TrimSpace(filter.PilotName) != "" { like := "%" + strings.TrimSpace(filter.PilotName) + "%" base = base.Where( "EXISTS (SELECT 1 FROM takeover_roster_crews trc LEFT JOIN users trc_u ON trc_u.id = trc.user_id WHERE trc.takeover_id = fm_reports.takeover_id AND trc.role_code = 'PILOT' AND trc.deleted_at IS NULL AND (trc.name_label LIKE ? OR CONCAT(trc_u.first_name, ' ', trc_u.last_name) LIKE ?))", like, like, ) } if strings.TrimSpace(filter.Search) != "" { like := "%" + strings.TrimSpace(filter.Search) + "%" base = base.Where("(f.mission_code LIKE ? OR COALESCE(fm_reports.engine1_gpc_n1, '') LIKE ? OR COALESCE(fm_reports.engine1_ptc_n2, '') LIKE ? OR COALESCE(fm_reports.engine2_gpc_n1, '') LIKE ? OR COALESCE(fm_reports.engine2_ptc_n2, '') LIKE ?)", like, like, like, like, like) } if filter.HasEngine1GpcN1 != nil { if *filter.HasEngine1GpcN1 { base = base.Where("fm_reports.engine1_gpc_n1 IS NOT NULL AND fm_reports.engine1_gpc_n1 <> ''") } else { base = base.Where("(fm_reports.engine1_gpc_n1 IS NULL OR fm_reports.engine1_gpc_n1 = '')") } } if filter.HasEngine1PtcN2 != nil { if *filter.HasEngine1PtcN2 { base = base.Where("fm_reports.engine1_ptc_n2 IS NOT NULL AND fm_reports.engine1_ptc_n2 <> ''") } else { base = base.Where("(fm_reports.engine1_ptc_n2 IS NULL OR fm_reports.engine1_ptc_n2 = '')") } } if filter.HasEngine2GpcN1 != nil { if *filter.HasEngine2GpcN1 { base = base.Where("fm_reports.engine2_gpc_n1 IS NOT NULL AND fm_reports.engine2_gpc_n1 <> ''") } else { base = base.Where("(fm_reports.engine2_gpc_n1 IS NULL OR fm_reports.engine2_gpc_n1 = '')") } } if filter.HasEngine2PtcN2 != nil { if *filter.HasEngine2PtcN2 { base = base.Where("fm_reports.engine2_ptc_n2 IS NOT NULL AND fm_reports.engine2_ptc_n2 <> ''") } else { base = base.Where("(fm_reports.engine2_ptc_n2 IS NULL OR fm_reports.engine2_ptc_n2 = '')") } } if err := base.Count(&total).Error; err != nil { return nil, 0, err } query := base.Order(fmReportListSortClause(filter.Sort)) if limit > 0 { query = query.Limit(limit).Offset(offset) } if err := query.Find(&rows).Error; err != nil { return nil, 0, err } return rows, total, nil } func fmReportListSortClause(sort string) string { switch strings.ToLower(strings.TrimSpace(sort)) { case "flight_id": return "fm_reports.flight_id ASC" case "-flight_id": return "fm_reports.flight_id DESC" case "takeover_id": return "fm_reports.takeover_id ASC" case "-takeover_id": return "fm_reports.takeover_id DESC" case "flight_inspection_id": return "fm_reports.flight_inspection_id ASC" case "-flight_inspection_id": return "fm_reports.flight_inspection_id DESC" case "flight_date": return "f.date ASC" case "-flight_date": return "f.date DESC" case "created_at": return "fm_reports.created_at ASC" case "-created_at": return "fm_reports.created_at DESC" case "updated_at": return "fm_reports.updated_at ASC" case "-updated_at": return "fm_reports.updated_at DESC" case "engine1_gpc_n1": return "fm_reports.engine1_gpc_n1 ASC" case "-engine1_gpc_n1": return "fm_reports.engine1_gpc_n1 DESC" case "engine1_ptc_n2": return "fm_reports.engine1_ptc_n2 ASC" case "-engine1_ptc_n2": return "fm_reports.engine1_ptc_n2 DESC" case "engine2_gpc_n1": return "fm_reports.engine2_gpc_n1 ASC" case "-engine2_gpc_n1": return "fm_reports.engine2_gpc_n1 DESC" case "engine2_ptc_n2": return "fm_reports.engine2_ptc_n2 ASC" case "-engine2_ptc_n2": return "fm_reports.engine2_ptc_n2 DESC" default: return "f.date DESC, fm_reports.created_at DESC" } } func (r *FMReportRepository) GetByID(ctx context.Context, id []byte) (*fmreport.Report, error) { if len(id) != 16 { return nil, nil } var row fmreport.Report err := r.db.WithContext(ctx). Where("id = ? AND deleted_at IS NULL", id). First(&row).Error if errors.Is(err, gorm.ErrRecordNotFound) { return nil, nil } return &row, err } func (r *FMReportRepository) Delete(ctx context.Context, id, deletedBy []byte) error { if len(id) != 16 { return nil } return r.db.WithContext(ctx). Model(&fmreport.Report{}). Where("id = ? AND deleted_at IS NULL", id). Updates(map[string]any{ "deleted_at": gorm.Expr("NOW(3)"), "deleted_by": deletedBy, "updated_by": deletedBy, }).Error } func (r *FMReportRepository) CreateFleetHistory(ctx context.Context, row *fmreport.FleetHistory) error { if row == nil { return nil } return r.db.WithContext(ctx). Clauses(clause.OnConflict{ Columns: []clause.Column{{Name: "fm_report_id"}}, DoNothing: true, }).Create(row).Error } func (r *FMReportRepository) GetFleetHistoryByReportID(ctx context.Context, reportID []byte) (*fmreport.FleetHistory, error) { var row fmreport.FleetHistory err := r.db.WithContext(ctx). Where("fm_report_id = ?", reportID). First(&row).Error if errors.Is(err, gorm.ErrRecordNotFound) { return nil, nil } return &row, err }