From 4461aff5c146974acf49a06a7bb199ffadd20060 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 12 Apr 2026 10:56:57 +0700 Subject: [PATCH] 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 --- model/dto/req/tower_dto.go | 6 ++++-- model/dto/res/tower_res.go | 3 ++- model/entity/tower.go | 1 + usecase/tower_usecase.go | 27 ++++++++++++++++----------- utils/helper/towerHelperRes.go | 27 ++++++++++++++------------- 5 files changed, 37 insertions(+), 27 deletions(-) diff --git a/model/dto/req/tower_dto.go b/model/dto/req/tower_dto.go index fd05c66..e243183 100644 --- a/model/dto/req/tower_dto.go +++ b/model/dto/req/tower_dto.go @@ -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 diff --git a/model/dto/res/tower_res.go b/model/dto/res/tower_res.go index 890d28a..85b4f3e 100644 --- a/model/dto/res/tower_res.go +++ b/model/dto/res/tower_res.go @@ -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"` } \ No newline at end of file diff --git a/model/entity/tower.go b/model/entity/tower.go index 1ba0f93..b98beee 100644 --- a/model/entity/tower.go +++ b/model/entity/tower.go @@ -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"` diff --git a/usecase/tower_usecase.go b/usecase/tower_usecase.go index 1b4e449..98ea941 100644 --- a/usecase/tower_usecase.go +++ b/usecase/tower_usecase.go @@ -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 { @@ -196,15 +200,16 @@ func (u *towerUsecase) Post(tower req.TowerDTO, imageFile *multipart.FileHeader) } newTower := entity.Tower{ - ID: uuid.New(), - DeviceID: tower.DeviceID, // Now nullable - TowerCode: tower.TowerCode, - Longitude: tower.Longitude, - Latitude: tower.Latitude, - ImageURL: imageURL, - ExternalTower: tower.ExternalTower, // Now nullable - CreatedAt: time.Now(), - UpdatedAt: time.Now(), + ID: uuid.New(), + DeviceID: tower.DeviceID, + TowerCode: tower.TowerCode, + Longitude: tower.Longitude, + Latitude: tower.Latitude, + ImageURL: imageURL, + ExternalTower: tower.ExternalTower, + BackboneCode: tower.BackboneCode, + CreatedAt: time.Now(), + UpdatedAt: time.Now(), } return u.towerRepo.Post(newTower) diff --git a/utils/helper/towerHelperRes.go b/utils/helper/towerHelperRes.go index fde3648..a54f45d 100644 --- a/utils/helper/towerHelperRes.go +++ b/utils/helper/towerHelperRes.go @@ -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,21 +63,21 @@ 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 - TowerCode: &tower.TowerCode, - Longitude: tower.Longitude, - Latitude: tower.Latitude, - Address: address, - ImageURL: tower.ImageURL, - ImageURLs: allImageURLs, // All images - ExternalTower: tower.ExternalTower, // Now nullable - CreatedAt: tower.CreatedAt, - UpdatedAt: tower.UpdatedAt, + ID: tower.ID, + DeviceCode: deviceCode, + TowerCode: &tower.TowerCode, + Longitude: tower.Longitude, + Latitude: tower.Latitude, + Address: address, + ImageURL: tower.ImageURL, + ImageURLs: allImageURLs, + ExternalTower: tower.ExternalTower, + BackboneCode: tower.BackboneCode, + CreatedAt: tower.CreatedAt, + UpdatedAt: tower.UpdatedAt, } return towerResp, nil } \ No newline at end of file