init push
This commit is contained in:
642
internal/repository/mysql/base_repo.go
Normal file
642
internal/repository/mysql/base_repo.go
Normal file
@@ -0,0 +1,642 @@
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql/driver"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
|
||||
"wucher/internal/domain/base"
|
||||
"wucher/internal/shared/pkg/sortkey"
|
||||
"wucher/internal/shared/pkg/uuidv7"
|
||||
)
|
||||
|
||||
type BaseRepository struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
type binaryValue struct {
|
||||
data []byte
|
||||
}
|
||||
|
||||
func (b binaryValue) Value() (driver.Value, error) {
|
||||
if len(b.data) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
return append([]byte(nil), b.data...), nil
|
||||
}
|
||||
|
||||
func NewBaseRepository(db *gorm.DB) *BaseRepository {
|
||||
return &BaseRepository{db: db}
|
||||
}
|
||||
|
||||
func (r *BaseRepository) CreateBase(ctx context.Context, row *base.Base, categoryType string) error {
|
||||
categoryID, err := r.categoryIDByType(ctx, categoryType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
row.BaseCategoryID = categoryID
|
||||
if actor := actorUserIDFromContext(ctx); len(actor) > 0 {
|
||||
row.CreatedBy = actor
|
||||
row.UpdatedBy = actor
|
||||
}
|
||||
row.DefaultStartTimeType = normalizeBaseShiftTimeType(row.DefaultStartTimeType)
|
||||
row.DefaultEndTimeType = normalizeBaseShiftTimeType(row.DefaultEndTimeType)
|
||||
row.DefaultShiftStart = baseClockStorageValue(row.DefaultShiftStart)
|
||||
row.DefaultShiftEnd = baseClockStorageValue(row.DefaultShiftEnd)
|
||||
requestedIsActive := row.IsActive
|
||||
operationalShiftTimes := row.OperationalShiftTimes
|
||||
row.OperationalShiftTimes = nil
|
||||
db := r.db.WithContext(ctx)
|
||||
return db.Transaction(func(tx *gorm.DB) error {
|
||||
if err := tx.Omit(clause.Associations).Create(row).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Model(&base.Base{}).Where("id = ?", row.ID).UpdateColumn("is_active", requestedIsActive).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := r.replaceBaseContactRolesTx(ctx, tx, row.ID, row.HEMSEDCContactIDs, row.MedPaxContactIDs, row.ResponsiblePilotContactIDs); err != nil {
|
||||
return err
|
||||
}
|
||||
return r.replaceBaseOperationalShiftTimesTx(ctx, tx, row.ID, operationalShiftTimes)
|
||||
})
|
||||
}
|
||||
|
||||
func (r *BaseRepository) UpdateBase(ctx context.Context, row *base.Base, categoryType string) error {
|
||||
categoryID, err := r.categoryIDByType(ctx, categoryType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
row.BaseCategoryID = categoryID
|
||||
if actor := actorUserIDFromContext(ctx); len(actor) > 0 {
|
||||
row.UpdatedBy = actor
|
||||
}
|
||||
row.DefaultStartTimeType = normalizeBaseShiftTimeType(row.DefaultStartTimeType)
|
||||
row.DefaultEndTimeType = normalizeBaseShiftTimeType(row.DefaultEndTimeType)
|
||||
row.DefaultShiftStart = baseClockStorageValue(row.DefaultShiftStart)
|
||||
row.DefaultShiftEnd = baseClockStorageValue(row.DefaultShiftEnd)
|
||||
return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
// Use an explicit UPDATE statement so the base shift columns are stored
|
||||
// in the same datetime shape that exists in the local schema while still
|
||||
// accepting clock-only inputs from the API layer.
|
||||
if err := tx.Exec(
|
||||
fmt.Sprintf(`UPDATE bases
|
||||
SET base = ?,
|
||||
base_category_id = ?,
|
||||
foto_attachment_id = ?,
|
||||
base_abbreviation = ?,
|
||||
address = ?,
|
||||
latitude = ?,
|
||||
longitude = ?,
|
||||
landline_number = ?,
|
||||
mobile_number = ?,
|
||||
email = ?,
|
||||
sortkey = ?,
|
||||
sms_alert = ?,
|
||||
checklist = ?,
|
||||
leg_time = ?,
|
||||
default_start_time_type = ?,
|
||||
default_end_time_type = ?,
|
||||
default_shift_start = ?,
|
||||
default_shift_end = ?,
|
||||
utc = ?,
|
||||
dry = ?,
|
||||
control_center = ?,
|
||||
dul = ?,
|
||||
notes = ?,
|
||||
is_active = ?,
|
||||
updated_by = ?,
|
||||
updated_at = ?
|
||||
WHERE id = ? AND deleted_at IS NULL`),
|
||||
row.BaseName,
|
||||
binaryValue{data: row.BaseCategoryID},
|
||||
binaryValue{data: row.FotoAttachmentID},
|
||||
row.BaseAbbreviation,
|
||||
row.Address,
|
||||
row.Latitude,
|
||||
row.Longitude,
|
||||
row.LandlineNumber,
|
||||
row.MobileNumber,
|
||||
row.Email,
|
||||
row.SortKey,
|
||||
row.SMSAlert,
|
||||
row.Checklist,
|
||||
row.LegTime,
|
||||
row.DefaultStartTimeType,
|
||||
row.DefaultEndTimeType,
|
||||
row.DefaultShiftStart,
|
||||
row.DefaultShiftEnd,
|
||||
row.UTC,
|
||||
row.Dry,
|
||||
row.ControlCenter,
|
||||
row.DUL,
|
||||
row.Notes,
|
||||
row.IsActive,
|
||||
binaryValue{data: row.UpdatedBy},
|
||||
time.Now().UTC(),
|
||||
binaryValue{data: row.ID},
|
||||
).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := r.replaceBaseContactRolesTx(ctx, tx, row.ID, row.HEMSEDCContactIDs, row.MedPaxContactIDs, row.ResponsiblePilotContactIDs); err != nil {
|
||||
return err
|
||||
}
|
||||
if row.OperationalShiftTimes == nil {
|
||||
return nil
|
||||
}
|
||||
return r.replaceBaseOperationalShiftTimesTx(ctx, tx, row.ID, row.OperationalShiftTimes)
|
||||
})
|
||||
}
|
||||
|
||||
func (r *BaseRepository) DeleteBase(ctx context.Context, id []byte, categoryType string) error {
|
||||
categoryID, err := r.categoryIDByType(ctx, categoryType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ensureNoReferenceBeforeDelete(ctx, r.db, "bases", id); err != nil {
|
||||
return err
|
||||
}
|
||||
now := time.Now().UTC()
|
||||
updates := map[string]any{
|
||||
"deleted_at": now,
|
||||
}
|
||||
if actor := actorUserIDFromContext(ctx); len(actor) > 0 {
|
||||
updates["deleted_by"] = actor
|
||||
updates["updated_by"] = actor
|
||||
}
|
||||
return mapDeleteConstraintError(r.db.WithContext(ctx).
|
||||
Model(&base.Base{}).
|
||||
Where("id = ? AND base_category_id = ? AND deleted_at IS NULL", id, categoryID).
|
||||
Updates(updates).Error)
|
||||
}
|
||||
|
||||
func (r *BaseRepository) GetBaseByID(ctx context.Context, id []byte, categoryType string) (*base.Base, error) {
|
||||
categoryID, err := r.categoryIDByType(ctx, categoryType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var row base.Base
|
||||
err = r.db.WithContext(ctx).
|
||||
Preload("FotoAttachment").
|
||||
Preload("FotoAttachment.File").
|
||||
Preload("OperationalShiftTimes", preloadBaseOperationalShiftTimes).
|
||||
Where("id = ? AND base_category_id = ? AND deleted_at IS NULL", id, categoryID).
|
||||
First(&row).Error
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, nil
|
||||
}
|
||||
if err == nil {
|
||||
if loadErr := r.loadBaseContactRoles(ctx, []*base.Base{&row}); loadErr != nil {
|
||||
return nil, loadErr
|
||||
}
|
||||
}
|
||||
return &row, err
|
||||
}
|
||||
|
||||
func (r *BaseRepository) ListBases(ctx context.Context, filter, sort string, limit, offset int, categoryType string, dul *bool) ([]base.Base, int64, error) {
|
||||
var rows []base.Base
|
||||
var total int64
|
||||
|
||||
baseQuery := r.db.WithContext(ctx).
|
||||
Model(&base.Base{}).
|
||||
Where("deleted_at IS NULL")
|
||||
if strings.TrimSpace(categoryType) != "" {
|
||||
categoryID, err := r.categoryIDByType(ctx, categoryType)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
baseQuery = baseQuery.Where("base_category_id = ?", categoryID)
|
||||
}
|
||||
if filter != "" {
|
||||
like := "%" + filter + "%"
|
||||
baseQuery = baseQuery.Where(
|
||||
"base LIKE ? OR base_abbreviation LIKE ? OR address LIKE ? OR email LIKE ? OR landline_number LIKE ? OR mobile_number LIKE ?",
|
||||
like,
|
||||
like,
|
||||
like,
|
||||
like,
|
||||
like,
|
||||
like,
|
||||
)
|
||||
}
|
||||
if dul != nil {
|
||||
baseQuery = baseQuery.Where("dul = ?", *dul)
|
||||
}
|
||||
|
||||
query := baseQuery
|
||||
if sort != "" {
|
||||
query = query.Order(sort)
|
||||
} else {
|
||||
for _, clause := range sortkey.ActivePositiveSortClauses("bases", "is_active", "sortkey", "base", false) {
|
||||
query = query.Order(clause)
|
||||
}
|
||||
}
|
||||
if err := baseQuery.Count(&total).Error; err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
if limit > 0 {
|
||||
query = query.Limit(limit).Offset(offset)
|
||||
}
|
||||
query = query.Preload("BaseCategory")
|
||||
query = query.Preload("FotoAttachment").Preload("FotoAttachment.File")
|
||||
query = query.Preload("OperationalShiftTimes", preloadBaseOperationalShiftTimes)
|
||||
if err := query.Find(&rows).Error; err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
ptrs := make([]*base.Base, 0, len(rows))
|
||||
for i := range rows {
|
||||
ptrs = append(ptrs, &rows[i])
|
||||
}
|
||||
if err := r.loadBaseContactRoles(ctx, ptrs); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return rows, total, nil
|
||||
}
|
||||
|
||||
func preloadBaseOperationalShiftTimes(db *gorm.DB) *gorm.DB {
|
||||
if db == nil {
|
||||
return db
|
||||
}
|
||||
q := db.Where("deleted_at IS NULL").Order("date_start ASC, date_end ASC")
|
||||
if db.Dialector != nil && strings.EqualFold(db.Dialector.Name(), "mysql") {
|
||||
q = q.Select(`
|
||||
id,
|
||||
base_id,
|
||||
date_start,
|
||||
date_end,
|
||||
start_time_type,
|
||||
end_time_type,
|
||||
CASE WHEN shift_start IS NULL THEN '' ELSE TIME_FORMAT(shift_start, '%H:%i:%s') END AS shift_start,
|
||||
CASE WHEN shift_end IS NULL THEN '' ELSE TIME_FORMAT(shift_end, '%H:%i:%s') END AS shift_end,
|
||||
created_at,
|
||||
created_by,
|
||||
updated_at,
|
||||
updated_by,
|
||||
deleted_at,
|
||||
deleted_by
|
||||
`)
|
||||
}
|
||||
return q
|
||||
}
|
||||
|
||||
func (r *BaseRepository) replaceBaseContactRolesTx(
|
||||
ctx context.Context,
|
||||
tx *gorm.DB,
|
||||
baseID []byte,
|
||||
hemsEDCIDs [][]byte,
|
||||
medPaxIDs [][]byte,
|
||||
pilotIDs [][]byte,
|
||||
) error {
|
||||
if len(baseID) != 16 {
|
||||
return errors.New("invalid base id")
|
||||
}
|
||||
all := append(copyIDs(hemsEDCIDs), medPaxIDs...)
|
||||
all = append(all, pilotIDs...)
|
||||
if err := ensureContactsExistTx(tx, all); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Where("base_id = ?", baseID).Delete(&base.BaseContactRole{}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
rows := make([]base.BaseContactRole, 0, len(hemsEDCIDs)+len(medPaxIDs)+len(pilotIDs))
|
||||
actor := actorUserIDFromContext(ctx)
|
||||
for _, contactID := range hemsEDCIDs {
|
||||
if len(contactID) != 16 {
|
||||
return fmt.Errorf("invalid hems_edc contact id")
|
||||
}
|
||||
rows = append(rows, base.BaseContactRole{
|
||||
BaseID: baseID,
|
||||
ContactID: contactID,
|
||||
RoleCode: base.BaseContactRoleHEMSEDC,
|
||||
CreatedBy: actor,
|
||||
UpdatedBy: actor,
|
||||
})
|
||||
}
|
||||
for _, contactID := range medPaxIDs {
|
||||
if len(contactID) != 16 {
|
||||
return fmt.Errorf("invalid med_pax contact id")
|
||||
}
|
||||
rows = append(rows, base.BaseContactRole{
|
||||
BaseID: baseID,
|
||||
ContactID: contactID,
|
||||
RoleCode: base.BaseContactRoleMedPax,
|
||||
CreatedBy: actor,
|
||||
UpdatedBy: actor,
|
||||
})
|
||||
}
|
||||
for _, contactID := range pilotIDs {
|
||||
if len(contactID) != 16 {
|
||||
return fmt.Errorf("invalid responsible_pilot contact id")
|
||||
}
|
||||
rows = append(rows, base.BaseContactRole{
|
||||
BaseID: baseID,
|
||||
ContactID: contactID,
|
||||
RoleCode: base.BaseContactRoleResponsiblePilot,
|
||||
CreatedBy: actor,
|
||||
UpdatedBy: actor,
|
||||
})
|
||||
}
|
||||
if len(rows) == 0 {
|
||||
return nil
|
||||
}
|
||||
return tx.Create(&rows).Error
|
||||
}
|
||||
|
||||
func (r *BaseRepository) replaceBaseOperationalShiftTimesTx(ctx context.Context, tx *gorm.DB, baseID []byte, items []base.BaseOperationalShiftTime) error {
|
||||
if len(baseID) != 16 {
|
||||
return errors.New("invalid base id")
|
||||
}
|
||||
if items == nil {
|
||||
return nil
|
||||
}
|
||||
actor := actorUserIDFromContext(ctx)
|
||||
if err := tx.Where("base_id = ?", baseID).Delete(&base.BaseOperationalShiftTime{}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if len(items) == 0 {
|
||||
return nil
|
||||
}
|
||||
now := time.Now().UTC()
|
||||
for i := range items {
|
||||
row := items[i]
|
||||
if row.DateStart == nil || row.DateEnd == nil || row.DateStart.IsZero() || row.DateEnd.IsZero() {
|
||||
return errors.New("invalid operational shift date range")
|
||||
}
|
||||
if row.DateEnd.Before(*row.DateStart) {
|
||||
return errors.New("operational shift date_end must be >= date_start")
|
||||
}
|
||||
if len(row.ID) == 0 {
|
||||
row.ID = uuidv7.MustBytes()
|
||||
}
|
||||
dateStart := row.DateStart.UTC().Format("2006-01-02")
|
||||
dateEnd := row.DateEnd.UTC().Format("2006-01-02")
|
||||
var createdBy any
|
||||
var updatedBy any
|
||||
if len(actor) > 0 {
|
||||
createdBy = binaryValue{data: actor}
|
||||
updatedBy = binaryValue{data: actor}
|
||||
}
|
||||
shiftStart := shiftTimeValue(row.ShiftStart)
|
||||
shiftEnd := shiftTimeValue(row.ShiftEnd)
|
||||
if err := tx.Exec(
|
||||
`INSERT INTO base_operational_shift_times
|
||||
(id, base_id, date_start, date_end, start_time_type, end_time_type, shift_start, shift_end, created_at, created_by, updated_at, updated_by, deleted_at, deleted_by)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
binaryValue{data: row.ID},
|
||||
binaryValue{data: baseID},
|
||||
dateStart,
|
||||
dateEnd,
|
||||
row.StartTimeType,
|
||||
row.EndTimeType,
|
||||
shiftStart,
|
||||
shiftEnd,
|
||||
now,
|
||||
createdBy,
|
||||
now,
|
||||
updatedBy,
|
||||
nil,
|
||||
nil,
|
||||
).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func shiftTimeValue(raw string) any {
|
||||
clock, ok := extractClockValue(raw)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return clock
|
||||
}
|
||||
|
||||
func baseClockStorageValue(raw string) string {
|
||||
clock, ok := extractClockValue(raw)
|
||||
if !ok {
|
||||
return strings.TrimSpace(raw)
|
||||
}
|
||||
return "2000-01-01 " + clock
|
||||
}
|
||||
|
||||
func normalizeBaseShiftTimeType(raw string) string {
|
||||
switch strings.ToUpper(strings.TrimSpace(raw)) {
|
||||
case "", base.ShiftTimeTypeFixed:
|
||||
return base.ShiftTimeTypeFixed
|
||||
case base.ShiftTimeTypeBMCT:
|
||||
return base.ShiftTimeTypeBMCT
|
||||
case base.ShiftTimeTypeECET:
|
||||
return base.ShiftTimeTypeECET
|
||||
default:
|
||||
return strings.ToUpper(strings.TrimSpace(raw))
|
||||
}
|
||||
}
|
||||
|
||||
func extractClockValue(raw string) (string, bool) {
|
||||
s := strings.TrimSpace(raw)
|
||||
if s == "" {
|
||||
return "", false
|
||||
}
|
||||
layouts := []string{
|
||||
"15:04:05",
|
||||
"15:04",
|
||||
"2006-01-02 15:04:05",
|
||||
"2006-01-02 15:04",
|
||||
time.RFC3339,
|
||||
time.RFC3339Nano,
|
||||
}
|
||||
for _, layout := range layouts {
|
||||
if parsed, err := time.Parse(layout, s); err == nil {
|
||||
return parsed.UTC().Format("15:04:05"), true
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
func ensureContactsExistTx(tx *gorm.DB, ids [][]byte) error {
|
||||
valid := make([][]byte, 0, len(ids))
|
||||
seen := map[string]struct{}{}
|
||||
for _, id := range ids {
|
||||
if len(id) != 16 {
|
||||
return errors.New("invalid contact id")
|
||||
}
|
||||
key := string(id)
|
||||
if _, ok := seen[key]; ok {
|
||||
continue
|
||||
}
|
||||
seen[key] = struct{}{}
|
||||
valid = append(valid, id)
|
||||
}
|
||||
if len(valid) == 0 {
|
||||
return nil
|
||||
}
|
||||
var count int64
|
||||
if err := tx.Table("users").Where("id IN ?", valid).Count(&count).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if count != int64(len(valid)) {
|
||||
return errors.New("one or more contact ids not found")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *BaseRepository) loadBaseContactRoles(ctx context.Context, bases []*base.Base) error {
|
||||
if len(bases) == 0 {
|
||||
return nil
|
||||
}
|
||||
baseIDs := make([][]byte, 0, len(bases))
|
||||
index := make(map[string]*base.Base, len(bases))
|
||||
for _, b := range bases {
|
||||
if b == nil || len(b.ID) != 16 {
|
||||
continue
|
||||
}
|
||||
baseIDs = append(baseIDs, b.ID)
|
||||
index[string(b.ID)] = b
|
||||
b.HEMSEDCContactIDs = nil
|
||||
b.MedPaxContactIDs = nil
|
||||
b.ResponsiblePilotContactIDs = nil
|
||||
b.HEMSEDCs = nil
|
||||
b.MedPax = nil
|
||||
b.ResponsiblePilots = nil
|
||||
}
|
||||
if len(baseIDs) == 0 {
|
||||
return nil
|
||||
}
|
||||
var rels []base.BaseContactRole
|
||||
if err := r.db.WithContext(ctx).
|
||||
Where("base_id IN ?", baseIDs).
|
||||
Order("created_at ASC").
|
||||
Find(&rels).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
for i := range rels {
|
||||
b := index[string(rels[i].BaseID)]
|
||||
if b == nil {
|
||||
continue
|
||||
}
|
||||
switch rels[i].RoleCode {
|
||||
case base.BaseContactRoleHEMSEDC:
|
||||
b.HEMSEDCContactIDs = append(b.HEMSEDCContactIDs, append([]byte(nil), rels[i].ContactID...))
|
||||
case base.BaseContactRoleMedPax:
|
||||
b.MedPaxContactIDs = append(b.MedPaxContactIDs, append([]byte(nil), rels[i].ContactID...))
|
||||
case base.BaseContactRoleResponsiblePilot:
|
||||
b.ResponsiblePilotContactIDs = append(b.ResponsiblePilotContactIDs, append([]byte(nil), rels[i].ContactID...))
|
||||
}
|
||||
}
|
||||
var hemsEDCContactIDs [][]byte
|
||||
var medPaxContactIDs [][]byte
|
||||
var pilotContactIDs [][]byte
|
||||
for _, b := range bases {
|
||||
for i := range b.HEMSEDCContactIDs {
|
||||
hemsEDCContactIDs = append(hemsEDCContactIDs, b.HEMSEDCContactIDs[i])
|
||||
}
|
||||
for i := range b.MedPaxContactIDs {
|
||||
medPaxContactIDs = append(medPaxContactIDs, b.MedPaxContactIDs[i])
|
||||
}
|
||||
for i := range b.ResponsiblePilotContactIDs {
|
||||
pilotContactIDs = append(pilotContactIDs, b.ResponsiblePilotContactIDs[i])
|
||||
}
|
||||
}
|
||||
allContactIDs := append(append(hemsEDCContactIDs, medPaxContactIDs...), pilotContactIDs...)
|
||||
contactMap, err := r.contactNameMap(ctx, allContactIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, b := range bases {
|
||||
for i := range b.HEMSEDCContactIDs {
|
||||
id := b.HEMSEDCContactIDs[i]
|
||||
p := contactMap[string(id)]
|
||||
p.ContactID = append([]byte(nil), id...)
|
||||
b.HEMSEDCs = append(b.HEMSEDCs, p)
|
||||
}
|
||||
for i := range b.MedPaxContactIDs {
|
||||
id := b.MedPaxContactIDs[i]
|
||||
p := contactMap[string(id)]
|
||||
p.ContactID = append([]byte(nil), id...)
|
||||
b.MedPax = append(b.MedPax, p)
|
||||
}
|
||||
for i := range b.ResponsiblePilotContactIDs {
|
||||
id := b.ResponsiblePilotContactIDs[i]
|
||||
p := contactMap[string(id)]
|
||||
p.ContactID = append([]byte(nil), id...)
|
||||
b.ResponsiblePilots = append(b.ResponsiblePilots, p)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *BaseRepository) contactNameMap(ctx context.Context, contactIDs [][]byte) (map[string]base.BaseContactPerson, error) {
|
||||
uniq := make([][]byte, 0, len(contactIDs))
|
||||
seen := map[string]struct{}{}
|
||||
for _, id := range contactIDs {
|
||||
if len(id) != 16 {
|
||||
continue
|
||||
}
|
||||
key := string(id)
|
||||
if _, ok := seen[key]; ok {
|
||||
continue
|
||||
}
|
||||
seen[key] = struct{}{}
|
||||
uniq = append(uniq, id)
|
||||
}
|
||||
if len(uniq) == 0 {
|
||||
return map[string]base.BaseContactPerson{}, nil
|
||||
}
|
||||
var rows []struct {
|
||||
ID []byte `gorm:"column:id"`
|
||||
FirstName string `gorm:"column:first_name"`
|
||||
LastName string `gorm:"column:last_name"`
|
||||
}
|
||||
if err := r.db.WithContext(ctx).
|
||||
Table("users").
|
||||
Select("id, first_name, last_name").
|
||||
Where("id IN ?", uniq).
|
||||
Find(&rows).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make(map[string]base.BaseContactPerson, len(rows))
|
||||
for i := range rows {
|
||||
out[string(rows[i].ID)] = base.BaseContactPerson{
|
||||
FirstName: rows[i].FirstName,
|
||||
LastName: rows[i].LastName,
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func copyIDs(src [][]byte) [][]byte {
|
||||
out := make([][]byte, 0, len(src))
|
||||
for _, id := range src {
|
||||
out = append(out, id)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (r *BaseRepository) categoryIDByType(ctx context.Context, categoryType string) ([]byte, error) {
|
||||
normalized, ok := base.NormalizeCategoryType(categoryType)
|
||||
if !ok {
|
||||
return nil, errors.New("invalid base category type")
|
||||
}
|
||||
return r.categoryIDByKey(ctx, normalized)
|
||||
}
|
||||
|
||||
func (r *BaseRepository) categoryIDByKey(ctx context.Context, categoryKey string) ([]byte, error) {
|
||||
var row struct {
|
||||
ID []byte `gorm:"column:id"`
|
||||
}
|
||||
if err := r.db.WithContext(ctx).
|
||||
Table("base_categories").
|
||||
Select("id").
|
||||
Where("`key` = ?", strings.ToLower(strings.TrimSpace(categoryKey))).
|
||||
Take(&row).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return append([]byte(nil), row.ID...), nil
|
||||
}
|
||||
Reference in New Issue
Block a user