package mysql import ( "context" "errors" "time" "gorm.io/gorm" "wucher/internal/domain/land" "wucher/internal/shared/pkg/sortkey" "wucher/internal/transport/http/dto" ) type LandRepository struct { db *gorm.DB } func NewLandRepository(db *gorm.DB) *LandRepository { return &LandRepository{db: db} } func (r *LandRepository) Create(ctx context.Context, row *land.Land) error { requestedIsActive := row.IsActive db := r.db.WithContext(ctx) if err := db.Create(row).Error; err != nil { return err } return db.Model(&land.Land{}).Where("id = ?", row.ID).UpdateColumn("is_active", requestedIsActive).Error } func (r *LandRepository) Update(ctx context.Context, row *land.Land) error { return r.db.WithContext(ctx).Save(row).Error } func (r *LandRepository) Delete(ctx context.Context, id []byte, deletedBy []byte) error { if err := ensureNoReferenceBeforeDelete(ctx, r.db, "lands", id); err != nil { return err } now := time.Now().UTC() updates := map[string]any{ "deleted_at": now, "deleted_by": deletedBy, "updated_by": deletedBy, } return mapDeleteConstraintError(r.db.WithContext(ctx). Model(&land.Land{}). Where("id = ? AND deleted_at IS NULL", id). Updates(updates).Error) } func (r *LandRepository) GetByID(ctx context.Context, id []byte) (*land.Land, error) { var row land.Land 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 *LandRepository) List(ctx context.Context, filter, sort string, limit, offset int) ([]land.Land, int64, error) { var rows []land.Land var total int64 base := r.db.WithContext(ctx).Model(&land.Land{}).Where("deleted_at IS NULL") if filter != "" { like := "%" + filter + "%" base = base.Where("(name LIKE ? OR land_iso_code LIKE ?)", like, like) } query := base if sort != "" { query = query.Order(sort) } else { for _, clause := range sortkey.ActivePositiveSortClauses("lands", "is_active", "sortkey", "name", false) { query = query.Order(clause) } } 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 (r *LandRepository) GetByIDView(ctx context.Context, id []byte) (*dto.LandView, error) { row, err := r.GetByID(ctx, id) if err != nil || row == nil { return nil, err } views, err := r.buildViews(ctx, []land.Land{*row}) if err != nil { return nil, err } if len(views) == 0 { return nil, nil } return &views[0], nil } func (r *LandRepository) ListView(ctx context.Context, filter, sort string, limit, offset int) ([]dto.LandView, int64, error) { rows, total, err := r.List(ctx, filter, sort, limit, offset) if err != nil { return nil, 0, err } views, err := r.buildViews(ctx, rows) if err != nil { return nil, 0, err } return views, total, nil } type landFederalStateRow struct { ID []byte `gorm:"column:id"` Name string `gorm:"column:name"` LandID []byte `gorm:"column:land_id"` } func (r *LandRepository) attachFederalStates(ctx context.Context, rows []land.Land) (map[string][]dto.LandFederalStateView, error) { if len(rows) == 0 { return map[string][]dto.LandFederalStateView{}, nil } landIDs := make([][]byte, 0, len(rows)) for i := range rows { if len(rows[i].ID) == 0 { continue } landIDs = append(landIDs, rows[i].ID) } if len(landIDs) == 0 { return map[string][]dto.LandFederalStateView{}, nil } var states []landFederalStateRow if err := r.db.WithContext(ctx). Table("federal_states"). Select("id, name, land_id"). Where("deleted_at IS NULL AND land_id IN ?", landIDs). Order("name ASC"). Find(&states).Error; err != nil { return nil, err } grouped := make(map[string][]dto.LandFederalStateView, len(landIDs)) for i := range states { s := states[i] key := string(s.LandID) grouped[key] = append(grouped[key], dto.LandFederalStateView{ ID: append([]byte(nil), s.ID...), Name: s.Name, }) } return grouped, nil } func (r *LandRepository) buildViews(ctx context.Context, rows []land.Land) ([]dto.LandView, error) { if len(rows) == 0 { return []dto.LandView{}, nil } federalStatesByLandID, err := r.attachFederalStates(ctx, rows) if err != nil { return nil, err } views := make([]dto.LandView, 0, len(rows)) for i := range rows { key := string(rows[i].ID) list := federalStatesByLandID[key] views = append(views, dto.LandView{ Row: rows[i], FederalStateTotal: len(list), FederalStateList: list, }) } return views, nil }