Files
fm_be/internal/repository/mysql/flight_repo.go
2026-07-16 22:16:45 +07:00

386 lines
12 KiB
Go

package mysql
import (
"context"
"errors"
"time"
"gorm.io/gorm"
"wucher/internal/domain/flight"
)
type FlightRepository struct {
db *gorm.DB
}
func preloadTakeoverRelations(q *gorm.DB) *gorm.DB {
return q.
Preload("Takeover").
Preload("Takeover.Base").
Preload("Takeover.Base.OperationalShiftTimes").
Preload("Takeover.ReserveAc").
Preload("Takeover.ReserveAc.Aircraft").
Preload("Takeover.ReserveAc.Inspection").
Preload("Takeover.RosterCrews").
Preload("Takeover.OtherPeople")
}
func NewFlightRepository(db *gorm.DB) *FlightRepository {
return &FlightRepository{db: db}
}
func (r *FlightRepository) Create(ctx context.Context, row *flight.Flight) error {
return r.db.WithContext(ctx).Create(row).Error
}
func (r *FlightRepository) GetLatestMissionCodeByPrefix(ctx context.Context, prefix string) (string, error) {
var out struct {
MissionCode string `gorm:"column:mission_code"`
}
err := r.db.WithContext(ctx).
Table("flights").
Select("mission_code").
Where("mission_code LIKE ? AND deleted_at IS NULL", prefix+"%").
Order("mission_code DESC").
Limit(1).
Scan(&out).Error
if err != nil {
return "", err
}
return out.MissionCode, nil
}
func (r *FlightRepository) Update(ctx context.Context, row *flight.Flight) error {
return r.db.WithContext(ctx).Save(row).Error
}
func (r *FlightRepository) Delete(ctx context.Context, id []byte, deletedBy []byte) error {
now := time.Now().UTC()
updates := map[string]any{
"deleted_at": now,
"deleted_by": deletedBy,
"updated_by": deletedBy,
}
return r.db.WithContext(ctx).
Model(&flight.Flight{}).
Where("id = ? AND deleted_at IS NULL", id).
Updates(updates).Error
}
func (r *FlightRepository) GetByID(ctx context.Context, id []byte) (*flight.Flight, error) {
var row flight.Flight
err := r.db.WithContext(ctx).
Model(&flight.Flight{}).
Select(flightReadSelectSQL()).
Preload("Takeover").
Preload("Takeover.Base").
Preload("Takeover.Base.OperationalShiftTimes").
Preload("Takeover.ReserveAc").
Preload("Takeover.ReserveAc.Aircraft").
Preload("Takeover.ReserveAc.Inspection").
Preload("Takeover.RosterCrews").
Preload("Takeover.OtherPeople").
Joins("LEFT JOIN users cu ON cu.id = flights.created_by").
Joins("LEFT JOIN users uu ON uu.id = flights.updated_by").
Where("flights.id = ? AND flights.deleted_at IS NULL", id).
First(&row).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, nil
}
return &row, err
}
func (r *FlightRepository) ListByUserID(ctx context.Context, userID []byte, limit, offset int) ([]flight.Flight, int64, error) {
rows := make([]flight.Flight, 0)
var total int64
base := r.db.WithContext(ctx).
Model(&flight.Flight{}).
Select(flightReadSelectSQL()).
Preload("Takeover").
Preload("Takeover.Base").
Preload("Takeover.ReserveAc").
Preload("Takeover.ReserveAc.Aircraft").
Preload("Takeover.ReserveAc.Inspection").
Preload("Takeover.RosterCrews").
Preload("Takeover.OtherPeople").
Joins("LEFT JOIN users cu ON cu.id = flights.created_by").
Joins("LEFT JOIN users uu ON uu.id = flights.updated_by").
Where("flights.deleted_at IS NULL").
Where(
`EXISTS (
SELECT 1
FROM duty_roster_crews c
JOIN duty_rosters r ON r.id = c.roster_id
WHERE c.user_id = ?
AND c.deleted_at IS NULL
AND r.deleted_at IS NULL
AND r.flight_id = flights.id
)`,
userID,
)
if err := base.Count(&total).Error; err != nil {
return nil, 0, err
}
query := base.Order("flights.date DESC, flights.created_at DESC")
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 (r *FlightRepository) ListByCreatedBy(ctx context.Context, createdBy []byte, filter string, date string, sort string, limit, offset int) ([]flight.Flight, int64, error) {
rows := make([]flight.Flight, 0)
var total int64
base := r.db.WithContext(ctx).
Model(&flight.Flight{}).
Select(flightReadSelectSQL()).
Preload("Takeover").
Preload("Takeover.Base").
Preload("Takeover.ReserveAc").
Preload("Takeover.ReserveAc.Aircraft").
Preload("Takeover.ReserveAc.Inspection").
Preload("Takeover.RosterCrews").
Preload("Takeover.OtherPeople").
Joins("LEFT JOIN users cu ON cu.id = flights.created_by").
Joins("LEFT JOIN users uu ON uu.id = flights.updated_by").
Where("flights.deleted_at IS NULL").
Where("flights.created_by = ?", createdBy)
if filter != "" {
like := "%" + filter + "%"
base = base.Where("flights.mission_code LIKE ?", like)
}
if date != "" {
base = base.Where("flights.date = ?", date)
}
if err := base.Count(&total).Error; err != nil {
return nil, 0, err
}
query := base
if sort != "" {
query = query.Order(sort)
} else {
query = query.Order("flights.date DESC, flights.created_at DESC")
}
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 (r *FlightRepository) GetByReserveAcID(ctx context.Context, reserveAcID []byte) (*flight.Flight, error) {
var row flight.Flight
err := r.db.WithContext(ctx).
Model(&flight.Flight{}).
Select(flightReadSelectSQL()).
Preload("Takeover").
Preload("Takeover.Base").
Preload("Takeover.ReserveAc").
Preload("Takeover.ReserveAc.Aircraft").
Preload("Takeover.ReserveAc.Inspection").
Preload("Takeover.RosterCrews").
Preload("Takeover.OtherPeople").
Joins("LEFT JOIN users cu ON cu.id = flights.created_by").
Joins("LEFT JOIN users uu ON uu.id = flights.updated_by").
Joins("JOIN takeover_acs ta ON ta.id = flights.takeover_ac_id AND ta.deleted_at IS NULL").
Where("ta.reserve_ac_id = ? AND flights.deleted_at IS NULL", reserveAcID).
First(&row).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, nil
}
return &row, err
}
func (r *FlightRepository) ListByReserveAcID(ctx context.Context, reserveAcID []byte) ([]flight.Flight, error) {
rows := make([]flight.Flight, 0)
err := r.db.WithContext(ctx).
Model(&flight.Flight{}).
Select(flightReadSelectSQL()).
Preload("Takeover").
Preload("Takeover.Base").
Preload("Takeover.ReserveAc").
Preload("Takeover.ReserveAc.Aircraft").
Preload("Takeover.ReserveAc.Inspection").
Preload("Takeover.RosterCrews").
Preload("Takeover.OtherPeople").
Joins("LEFT JOIN users cu ON cu.id = flights.created_by").
Joins("LEFT JOIN users uu ON uu.id = flights.updated_by").
Joins("JOIN takeover_acs ta ON ta.id = flights.takeover_ac_id AND ta.deleted_at IS NULL").
Where("ta.reserve_ac_id = ? AND flights.deleted_at IS NULL", reserveAcID).
Order("flights.created_at ASC, flights.id ASC").
Find(&rows).Error
if err != nil {
return nil, err
}
return rows, nil
}
func (r *FlightRepository) GetByTakeoverAcID(ctx context.Context, takeoverAcID []byte) (*flight.Flight, error) {
var row flight.Flight
err := r.db.WithContext(ctx).
Model(&flight.Flight{}).
Select(flightReadSelectSQL()).
Preload("Takeover").
Preload("Takeover.Base").
Preload("Takeover.ReserveAc").
Preload("Takeover.ReserveAc.Aircraft").
Preload("Takeover.ReserveAc.Inspection").
Preload("Takeover.RosterCrews").
Preload("Takeover.OtherPeople").
Joins("LEFT JOIN users cu ON cu.id = flights.created_by").
Joins("LEFT JOIN users uu ON uu.id = flights.updated_by").
Where("flights.takeover_ac_id = ? AND flights.deleted_at IS NULL", takeoverAcID).
First(&row).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, nil
}
return &row, err
}
func (r *FlightRepository) ListByTakeoverAcIDs(ctx context.Context, takeoverAcIDs [][]byte) ([]flight.Flight, error) {
if len(takeoverAcIDs) == 0 {
return []flight.Flight{}, nil
}
rows := make([]flight.Flight, 0)
err := r.db.WithContext(ctx).
Model(&flight.Flight{}).
Select(flightListSelectSQL()).
Preload("Takeover").
Preload("Takeover.Base").
Preload("Takeover.ReserveAc").
Preload("Takeover.ReserveAc.Aircraft").
Preload("Takeover.ReserveAc.Inspection").
Preload("Takeover.RosterCrews").
Preload("Takeover.OtherPeople").
Where("flights.takeover_ac_id IN ? AND flights.deleted_at IS NULL", takeoverAcIDs).
Find(&rows).Error
if err != nil {
return nil, err
}
return rows, nil
}
func (r *FlightRepository) GetByDutyRosterID(ctx context.Context, dutyRosterID []byte) (*flight.Flight, error) {
var row flight.Flight
err := r.db.WithContext(ctx).
Model(&flight.Flight{}).
Select(flightListSelectSQL()).
Preload("Takeover").
Preload("Takeover.Base").
Preload("Takeover.ReserveAc").
Preload("Takeover.ReserveAc.Aircraft").
Preload("Takeover.ReserveAc.Inspection").
Preload("Takeover.RosterCrews").
Preload("Takeover.OtherPeople").
Joins("JOIN duty_rosters dr ON dr.flight_id = flights.id AND dr.deleted_at IS NULL").
Where("dr.id = ? AND flights.deleted_at IS NULL", dutyRosterID).
First(&row).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, nil
}
return &row, err
}
func (r *FlightRepository) ListByDutyRosterIDs(ctx context.Context, dutyRosterIDs [][]byte) ([]flight.Flight, error) {
if len(dutyRosterIDs) == 0 {
return []flight.Flight{}, nil
}
rows := make([]flight.Flight, 0)
err := r.db.WithContext(ctx).
Model(&flight.Flight{}).
Select(flightListSelectSQL()).
Preload("Takeover").
Preload("Takeover.ReserveAc").
Preload("Takeover.ReserveAc.Aircraft").
Preload("Takeover.ReserveAc.Inspection").
Joins("JOIN duty_rosters dr ON dr.flight_id = flights.id AND dr.deleted_at IS NULL").
Where("dr.id IN ? AND flights.deleted_at IS NULL", dutyRosterIDs).
Find(&rows).Error
if err != nil {
return nil, err
}
return rows, nil
}
func (r *FlightRepository) List(ctx context.Context, filter string, date string, sort string, limit, offset int) ([]flight.Flight, int64, error) {
rows := make([]flight.Flight, 0)
var total int64
base := r.db.WithContext(ctx).
Model(&flight.Flight{}).
Select(flightReadSelectSQL()).
Preload("Takeover").
Preload("Takeover.Base").
Preload("Takeover.ReserveAc").
Preload("Takeover.ReserveAc.Aircraft").
Preload("Takeover.ReserveAc.Inspection").
Preload("Takeover.RosterCrews").
Preload("Takeover.OtherPeople").
Joins("LEFT JOIN users cu ON cu.id = flights.created_by").
Joins("LEFT JOIN users uu ON uu.id = flights.updated_by").
Where("flights.deleted_at IS NULL")
if filter != "" {
like := "%" + filter + "%"
base = base.Where("flights.mission_code LIKE ?", like)
}
if date != "" {
base = base.Where("flights.date = ?", date)
}
query := base
if sort != "" {
query = query.Order(sort)
}
if err := base.Count(&total).Error; err != nil {
return nil, 0, err
}
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 flightListSelectSQL() string {
// created_by_name / updated_by_name are resolved via correlated subqueries so the list
// queries carry the auditor display names without needing extra user JOINs (the read
// query joins users; the list variants don't). Keeps get-all in parity with get-by-id.
return `flights.*,
(SELECT dr.id FROM duty_rosters dr WHERE dr.flight_id = flights.id AND dr.deleted_at IS NULL LIMIT 1) AS duty_roster_id,
(SELECT ta.reserve_ac_id FROM takeover_acs ta WHERE ta.id = flights.takeover_ac_id AND ta.deleted_at IS NULL LIMIT 1) AS reserve_ac_id,
COALESCE((SELECT NULLIF(TRIM(CONCAT(COALESCE(cu.first_name, ''), ' ', COALESCE(cu.last_name, ''))), '')
FROM users cu WHERE cu.id = flights.created_by), '') AS created_by_name,
COALESCE((SELECT NULLIF(TRIM(CONCAT(COALESCE(uu.first_name, ''), ' ', COALESCE(uu.last_name, ''))), '')
FROM users uu WHERE uu.id = flights.updated_by), '') AS updated_by_name`
}
func flightReadSelectSQL() string {
return `flights.*,
(SELECT dr.id FROM duty_rosters dr WHERE dr.flight_id = flights.id AND dr.deleted_at IS NULL LIMIT 1) AS duty_roster_id,
(SELECT ta.reserve_ac_id FROM takeover_acs ta WHERE ta.id = flights.takeover_ac_id AND ta.deleted_at IS NULL LIMIT 1) AS reserve_ac_id,
COALESCE(
NULLIF(TRIM(CONCAT(COALESCE(cu.first_name, ''), ' ', COALESCE(cu.last_name, ''))), ''),
''
) AS created_by_name,
COALESCE(
NULLIF(TRIM(CONCAT(COALESCE(uu.first_name, ''), ' ', COALESCE(uu.last_name, ''))), ''),
''
) AS updated_by_name`
}