feat: add backbone_code to Tower for road routing waypoints

Towers can now be explicitly assigned to a backbone by setting
backbone_code. This field is included in create/update DTOs and
the response. The backbone OSRM routing merges these tower waypoints
with Closure waypoints, sorted by distance from OTB-start.

Run once on DB: ALTER TABLE towers ADD COLUMN backbone_code VARCHAR(255);

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
unknown 2026-04-12 10:56:57 +07:00
parent 9ae9de471d
commit 4461aff5c1
5 changed files with 37 additions and 27 deletions

View File

@ -8,7 +8,8 @@ type TowerDTO struct {
TowerCode string `json:"tower_code" validate:"required"`
Longitude float64 `json:"longitude" validate:"required"`
Latitude float64 `json:"latitude" validate:"required"`
ExternalTower *bool `json:"external_tower,omitempty"` // Make nullable
ExternalTower *bool `json:"external_tower,omitempty"`
BackboneCode *string `json:"backbone_code,omitempty"`
}
type UpdateTowerDTO struct {
@ -17,8 +18,9 @@ type UpdateTowerDTO struct {
TowerCode *string `json:"tower_code,omitempty"`
Longitude *float64 `json:"longitude,omitempty" validate:"omitempty,longitude"`
Latitude *float64 `json:"latitude,omitempty" validate:"omitempty,latitude"`
ExternalTower *bool `json:"external_tower,omitempty"` // Make nullable
ExternalTower *bool `json:"external_tower,omitempty"`
ImageURL *string `json:"image_url,omitempty"`
BackboneCode *string `json:"backbone_code,omitempty"`
}
// Add to model/dto/req/tower_dto.go

View File

@ -14,7 +14,8 @@ type TowerResponse struct {
Address string `json:"address"`
ImageURL string `json:"image_url"`
ImageURLs []string `json:"image_urls"` // Store multiple images as JSONB
ExternalTower *bool `json:"external_tower"` // Make nullable
ExternalTower *bool `json:"external_tower"`
BackboneCode *string `json:"backbone_code,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}

View File

@ -15,6 +15,7 @@ type Tower struct {
ImageURL string `json:"image_url"` // Keep for backward compatibility
ImageURLs StringSlice `json:"image_urls" gorm:"type:jsonb"` // Multiple images
ExternalTower *bool `json:"external_tower,omitempty"`
BackboneCode *string `json:"backbone_code,omitempty" gorm:"column:backbone_code"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`

View File

@ -79,9 +79,10 @@ func (u *towerUsecase) PostWithMultipleImages(tower req.TowerDTO, imageFiles []*
TowerCode: tower.TowerCode,
Longitude: tower.Longitude,
Latitude: tower.Latitude,
ImageURL: primaryImageURL, // Primary image
ImageURLs: entity.StringSlice(imageURLs), // All images
ImageURL: primaryImageURL,
ImageURLs: entity.StringSlice(imageURLs),
ExternalTower: tower.ExternalTower,
BackboneCode: tower.BackboneCode,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
@ -115,6 +116,9 @@ func (u *towerUsecase) UpdateTowerWithMultipleImages(id uuid.UUID, tower req.Upd
if tower.ImageURL != nil {
updates["ImageURL"] = *tower.ImageURL
}
if tower.BackboneCode != nil {
updates["BackboneCode"] = *tower.BackboneCode
}
// Handle multiple image uploads
if len(imageFiles) > 0 {
@ -197,12 +201,13 @@ func (u *towerUsecase) Post(tower req.TowerDTO, imageFile *multipart.FileHeader)
newTower := entity.Tower{
ID: uuid.New(),
DeviceID: tower.DeviceID, // Now nullable
DeviceID: tower.DeviceID,
TowerCode: tower.TowerCode,
Longitude: tower.Longitude,
Latitude: tower.Latitude,
ImageURL: imageURL,
ExternalTower: tower.ExternalTower, // Now nullable
ExternalTower: tower.ExternalTower,
BackboneCode: tower.BackboneCode,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}

View File

@ -37,7 +37,8 @@ func ConvertToTowerResponses(towers []entity.Tower, geocoder service.GeocodingSe
Address: address,
ImageURL: tower.ImageURL,
ImageURLs: allImageURLs, // All images
ExternalTower: tower.ExternalTower, // Now nullable
ExternalTower: tower.ExternalTower,
BackboneCode: tower.BackboneCode,
CreatedAt: tower.CreatedAt,
UpdatedAt: tower.UpdatedAt,
}
@ -62,19 +63,19 @@ func ConvertToTowerIDResponses(tower entity.Tower, geocoder service.GeocodingSer
if tower.Device.DeviceCode != "" {
deviceCode = &tower.Device.DeviceCode
}
// Get all image URLs
allImageURLs := tower.GetAllImageURLs()
towerResp := res.TowerResponse{
ID: tower.ID,
DeviceCode: deviceCode, // Now nullable
DeviceCode: deviceCode,
TowerCode: &tower.TowerCode,
Longitude: tower.Longitude,
Latitude: tower.Latitude,
Address: address,
ImageURL: tower.ImageURL,
ImageURLs: allImageURLs, // All images
ExternalTower: tower.ExternalTower, // Now nullable
ImageURLs: allImageURLs,
ExternalTower: tower.ExternalTower,
BackboneCode: tower.BackboneCode,
CreatedAt: tower.CreatedAt,
UpdatedAt: tower.UpdatedAt,
}