643 lines
17 KiB
Go
643 lines
17 KiB
Go
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
|
|
}
|