init push
This commit is contained in:
194
internal/repository/mysql/complaint_repo.go
Normal file
194
internal/repository/mysql/complaint_repo.go
Normal file
@@ -0,0 +1,194 @@
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
complaint "wucher/internal/domain/complaint"
|
||||
)
|
||||
|
||||
type ComplaintRepository struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
func NewComplaintRepository(db *gorm.DB) *ComplaintRepository { return &ComplaintRepository{db: db} }
|
||||
|
||||
func (r *ComplaintRepository) Create(ctx context.Context, row *complaint.Complaint) error {
|
||||
return r.db.WithContext(ctx).Create(row).Error
|
||||
}
|
||||
|
||||
func (r *ComplaintRepository) Update(ctx context.Context, row *complaint.Complaint) error {
|
||||
return r.db.WithContext(ctx).Save(row).Error
|
||||
}
|
||||
|
||||
func (r *ComplaintRepository) Delete(ctx context.Context, id []byte, deletedBy []byte) error {
|
||||
return r.db.WithContext(ctx).Model(&complaint.Complaint{}).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 *ComplaintRepository) GetByID(ctx context.Context, id []byte) (*complaint.Complaint, error) {
|
||||
var row complaint.Complaint
|
||||
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 *ComplaintRepository) ListByHelicopter(ctx context.Context, helicopterID []byte, limit, offset int) ([]complaint.Complaint, int64, error) {
|
||||
rows := make([]complaint.Complaint, 0)
|
||||
var total int64
|
||||
base := r.db.WithContext(ctx).
|
||||
Model(&complaint.Complaint{}).
|
||||
Where("helicopter_id = ? AND deleted_at IS NULL", helicopterID)
|
||||
if err := base.Count(&total).Error; err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
q := base.Order("reported_at DESC, created_at DESC")
|
||||
if limit > 0 {
|
||||
q = q.Limit(limit).Offset(offset)
|
||||
}
|
||||
err := q.Find(&rows).Error
|
||||
return rows, total, err
|
||||
}
|
||||
|
||||
func (r *ComplaintRepository) List(ctx context.Context, filter complaint.ListFilter, sort string, limit, offset int) ([]complaint.Complaint, int64, error) {
|
||||
rows := make([]complaint.Complaint, 0)
|
||||
var total int64
|
||||
base := r.db.WithContext(ctx).
|
||||
Model(&complaint.Complaint{}).
|
||||
Where("complaints.deleted_at IS NULL")
|
||||
if len(filter.HelicopterID) == 16 {
|
||||
base = base.Where("complaints.helicopter_id = ?", filter.HelicopterID)
|
||||
}
|
||||
if len(filter.FlightID) == 16 {
|
||||
base = base.Where("complaints.flight_id = ?", filter.FlightID)
|
||||
}
|
||||
if filter.HoldItemsOnly {
|
||||
base = base.Where("complaints.fixed_at IS NULL AND complaints.mel_severity > ?", 0)
|
||||
}
|
||||
if filter.Search != "" {
|
||||
like := "%" + filter.Search + "%"
|
||||
base = base.Where("complaints.description LIKE ? OR CAST(complaints.mel_severity AS CHAR) LIKE ?", like, like)
|
||||
}
|
||||
if filter.DateFrom != "" {
|
||||
base = base.Where("complaints.reported_at >= ?", filter.DateFrom+" 00:00:00")
|
||||
}
|
||||
if filter.DateTo != "" {
|
||||
base = base.Where("complaints.reported_at <= ?", filter.DateTo+" 23:59:59.999")
|
||||
}
|
||||
if filter.Person != "" {
|
||||
like := "%" + filter.Person + "%"
|
||||
base = base.Where(
|
||||
"EXISTS (SELECT 1 FROM users u WHERE u.id IN (complaints.reported_by, complaints.mel_classified_by, complaints.nsr_decided_by) "+
|
||||
"AND TRIM(CONCAT(COALESCE(u.first_name, ''), ' ', COALESCE(u.last_name, ''))) LIKE ?)",
|
||||
like,
|
||||
)
|
||||
}
|
||||
if err := base.Count(&total).Error; err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
q := base
|
||||
if sort != "" {
|
||||
q = q.Order(sort)
|
||||
} else {
|
||||
q = q.Order("reported_at DESC, created_at DESC")
|
||||
}
|
||||
if limit > 0 {
|
||||
q = q.Limit(limit).Offset(offset)
|
||||
}
|
||||
if err := q.Find(&rows).Error; err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
return rows, total, nil
|
||||
}
|
||||
|
||||
func (r *ComplaintRepository) HelicopterHasOpenComplaint(ctx context.Context, helicopterID []byte) (bool, error) {
|
||||
var count int64
|
||||
err := r.db.WithContext(ctx).
|
||||
Model(&complaint.Complaint{}).
|
||||
Where("helicopter_id = ? AND deleted_at IS NULL AND fixed_at IS NULL", helicopterID).
|
||||
Count(&count).Error
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return count > 0, nil
|
||||
}
|
||||
|
||||
// OpenByHelicopterIDs returns the complaints the fleet-status Complaints column shows for
|
||||
// the given helicopters, in one query (no N+1). That is every OPEN defect (fixed_at IS
|
||||
// NULL) — including NSR-deferred and MEL-within-grace items, which are still open hold
|
||||
// items — PLUS the FIXED defects from each helicopter's latest flight (the flight of its
|
||||
// most recently reported complaint), so a just-resolved defect stays visible as the last
|
||||
// state until a newer flight reports. Fixed defects from older flights drop off here but
|
||||
// remain retrievable via the complaint history. A nil/empty helicopterIDs means "all".
|
||||
func (r *ComplaintRepository) OpenByHelicopterIDs(ctx context.Context, helicopterIDs [][]byte) ([]complaint.Complaint, error) {
|
||||
rows := make([]complaint.Complaint, 0)
|
||||
q := r.db.WithContext(ctx).
|
||||
Model(&complaint.Complaint{}).
|
||||
Where("deleted_at IS NULL").
|
||||
Where("fixed_at IS NULL OR flight_id = (SELECT c2.flight_id FROM complaints c2 WHERE c2.helicopter_id = complaints.helicopter_id AND c2.deleted_at IS NULL AND c2.flight_id IS NOT NULL ORDER BY c2.reported_at DESC, c2.id DESC LIMIT 1)")
|
||||
if len(helicopterIDs) > 0 {
|
||||
q = q.Where("helicopter_id IN ?", helicopterIDs)
|
||||
}
|
||||
err := q.Order("reported_at DESC, created_at DESC").Find(&rows).Error
|
||||
return rows, err
|
||||
}
|
||||
|
||||
func (r *ComplaintRepository) ActiveGroundingByHelicopterIDs(ctx context.Context, helicopterIDs [][]byte) ([]complaint.Complaint, error) {
|
||||
rows := make([]complaint.Complaint, 0)
|
||||
// Grounding candidates: open (not fixed), not released (not NSR), and already
|
||||
// classified (mel_classified_at set). A pending complaint is not yet a grounding
|
||||
// defect — it neither grounds the aircraft nor blocks the EASA release gate until it
|
||||
// is classified. action_taken is intentionally NOT excluded — recording corrective
|
||||
// action no longer lifts the grounding (only an NSR or a signed EASA release does),
|
||||
// so IsGrounding decides. A nil/empty helicopterIDs means "all helicopters".
|
||||
q := r.db.WithContext(ctx).
|
||||
Model(&complaint.Complaint{}).
|
||||
Where("deleted_at IS NULL AND is_nsr = 0 AND fixed_at IS NULL AND mel_classified_at IS NOT NULL")
|
||||
if len(helicopterIDs) > 0 {
|
||||
q = q.Where("helicopter_id IN ?", helicopterIDs)
|
||||
}
|
||||
err := q.Find(&rows).Error
|
||||
return rows, err
|
||||
}
|
||||
|
||||
func (r *ComplaintRepository) HelicopterHasUnactionedComplaint(ctx context.Context, helicopterID []byte) (bool, error) {
|
||||
var count int64
|
||||
err := r.db.WithContext(ctx).
|
||||
Model(&complaint.Complaint{}).
|
||||
Where("helicopter_id = ? AND deleted_at IS NULL AND fixed_at IS NULL AND is_nsr = 0 AND (action_taken IS NULL OR action_taken = '')", helicopterID).
|
||||
Count(&count).Error
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return count > 0, nil
|
||||
}
|
||||
|
||||
func (r *ComplaintRepository) MarkFixedByHelicopter(ctx context.Context, helicopterID []byte, easaID []byte, actor []byte) error {
|
||||
return r.db.WithContext(ctx).
|
||||
Model(&complaint.Complaint{}).
|
||||
Where("helicopter_id = ? AND deleted_at IS NULL AND fixed_at IS NULL", helicopterID).
|
||||
Updates(map[string]any{"fixed_at": gorm.Expr("NOW(3)"), "fixed_by_easa_id": easaID, "updated_by": actor}).Error
|
||||
}
|
||||
|
||||
// MarkFixedByComplaint closes a single complaint (by id) — used when a release is
|
||||
// scoped to one specific defect via complaint_id. No-op if already fixed/deleted.
|
||||
func (r *ComplaintRepository) MarkFixedByComplaint(ctx context.Context, complaintID []byte, easaID []byte, actor []byte) error {
|
||||
return r.db.WithContext(ctx).
|
||||
Model(&complaint.Complaint{}).
|
||||
Where("id = ? AND deleted_at IS NULL AND fixed_at IS NULL", complaintID).
|
||||
Updates(map[string]any{"fixed_at": gorm.Expr("NOW(3)"), "fixed_by_easa_id": easaID, "updated_by": actor}).Error
|
||||
}
|
||||
|
||||
// MarkFixedByFlight closes every open complaint reported on a flight — used when a
|
||||
// release is scoped to a flight via flight_id. No-op for already fixed/deleted rows.
|
||||
func (r *ComplaintRepository) MarkFixedByFlight(ctx context.Context, flightID []byte, easaID []byte, actor []byte) error {
|
||||
return r.db.WithContext(ctx).
|
||||
Model(&complaint.Complaint{}).
|
||||
Where("flight_id = ? AND deleted_at IS NULL AND fixed_at IS NULL", flightID).
|
||||
Updates(map[string]any{"fixed_at": gorm.Expr("NOW(3)"), "fixed_by_easa_id": easaID, "updated_by": actor}).Error
|
||||
}
|
||||
Reference in New Issue
Block a user