Merge pull request 'adding for towerID response and request' (#33) from feature/responses-v2 into dev
Reviewed-on: winter-access/backend_nam#33
This commit is contained in:
commit
90b8fcdef0
|
|
@ -459,6 +459,16 @@ func (c *DeviceDetailsController) updateDeviceDetails(ctx *gin.Context) {
|
||||||
deviceUpdateDTO.District = &district
|
deviceUpdateDTO.District = &district
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle TowerID
|
||||||
|
if towerIDStr := ctx.PostForm("tower_id"); towerIDStr != "" {
|
||||||
|
if towerID, err := uuid.Parse(towerIDStr); err == nil {
|
||||||
|
deviceUpdateDTO.TowerID = &towerID
|
||||||
|
} else {
|
||||||
|
common.ErrorResponses(ctx, http.StatusBadRequest, "Invalid tower ID format")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Get multiple image files
|
// Get multiple image files
|
||||||
form := ctx.Request.MultipartForm
|
form := ctx.Request.MultipartForm
|
||||||
imageFiles := form.File["images"] // Support multiple images
|
imageFiles := form.File["images"] // Support multiple images
|
||||||
|
|
|
||||||
|
|
@ -315,6 +315,7 @@ func (dc *DeviceController) UpdateDevice() gin.HandlerFunc {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Handle multipart form request
|
// Handle multipart form request
|
||||||
err = c.Request.ParseMultipartForm(50 << 20) // 50MB for multiple images
|
err = c.Request.ParseMultipartForm(50 << 20) // 50MB for multiple images
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
package req
|
package req
|
||||||
|
|
||||||
import "users_management/m/model/entity"
|
import (
|
||||||
|
"users_management/m/model/entity"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
type DeviceDetailsDTO struct {
|
type DeviceDetailsDTO struct {
|
||||||
DeviceCode string `json:"device_code" validate:"required"`
|
DeviceCode string `json:"device_code" validate:"required"`
|
||||||
|
|
@ -13,6 +17,7 @@ type DeviceDetailsDTO struct {
|
||||||
Province *string `json:"province,omitempty" validate:"omitempty,min=3"`
|
Province *string `json:"province,omitempty" validate:"omitempty,min=3"`
|
||||||
City *string `json:"city,omitempty" validate:"omitempty,min=3"`
|
City *string `json:"city,omitempty" validate:"omitempty,min=3"`
|
||||||
District *string `json:"district,omitempty" validate:"omitempty,min=3"`
|
District *string `json:"district,omitempty" validate:"omitempty,min=3"`
|
||||||
|
TowerID *uuid.UUID `json:"tower_id,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type UpdateDeviceDetailsDTO struct {
|
type UpdateDeviceDetailsDTO struct {
|
||||||
|
|
@ -26,6 +31,7 @@ type UpdateDeviceDetailsDTO struct {
|
||||||
Province *string `json:"province,omitempty" validate:"omitempty,min=3"`
|
Province *string `json:"province,omitempty" validate:"omitempty,min=3"`
|
||||||
City *string `json:"city,omitempty" validate:"omitempty,min=3"`
|
City *string `json:"city,omitempty" validate:"omitempty,min=3"`
|
||||||
District *string `json:"district,omitempty" validate:"omitempty,min=3"`
|
District *string `json:"district,omitempty" validate:"omitempty,min=3"`
|
||||||
|
TowerID *uuid.UUID `json:"tower_id,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AssignMultipleCustomersDTO struct {
|
type AssignMultipleCustomersDTO struct {
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ type DeviceDTO struct {
|
||||||
Province *string `json:"province,omitempty" validate:"omitempty,min=3"`
|
Province *string `json:"province,omitempty" validate:"omitempty,min=3"`
|
||||||
City *string `json:"city,omitempty" validate:"omitempty,min=3"`
|
City *string `json:"city,omitempty" validate:"omitempty,min=3"`
|
||||||
District *string `json:"district,omitempty" validate:"omitempty,min=3"`
|
District *string `json:"district,omitempty" validate:"omitempty,min=3"`
|
||||||
|
TowerID *uuid.UUID `json:"tower_id,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type UpdateDeviceDTO struct {
|
type UpdateDeviceDTO struct {
|
||||||
|
|
@ -24,6 +25,7 @@ type UpdateDeviceDTO struct {
|
||||||
Province *string `json:"province,omitempty" validate:"omitempty,min=3"`
|
Province *string `json:"province,omitempty" validate:"omitempty,min=3"`
|
||||||
City *string `json:"city,omitempty" validate:"omitempty,min=3"`
|
City *string `json:"city,omitempty" validate:"omitempty,min=3"`
|
||||||
District *string `json:"district,omitempty" validate:"omitempty,min=3"`
|
District *string `json:"district,omitempty" validate:"omitempty,min=3"`
|
||||||
|
TowerID *uuid.UUID `json:"tower_id,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type BulkDeviceImageUploadDTO struct {
|
type BulkDeviceImageUploadDTO struct {
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ type DeviceDetailsResponse struct {
|
||||||
District *string `json:"district,omitempty"`
|
District *string `json:"district,omitempty"`
|
||||||
ImageURL *string `json:"image_url,omitempty"`
|
ImageURL *string `json:"image_url,omitempty"`
|
||||||
ImageURLs []string `json:"image_urls"`
|
ImageURLs []string `json:"image_urls"`
|
||||||
|
TowerID *uuid.UUID `json:"tower_id,omitempty"` // Add TowerID
|
||||||
|
|
||||||
// Connection details
|
// Connection details
|
||||||
Backbones []BackboneConnectionInfo `json:"backbones"`
|
Backbones []BackboneConnectionInfo `json:"backbones"`
|
||||||
|
|
|
||||||
|
|
@ -18,16 +18,20 @@ type DeviceDetails struct {
|
||||||
City *string `json:"city,omitempty" gorm:"type:varchar(255)"`
|
City *string `json:"city,omitempty" gorm:"type:varchar(255)"`
|
||||||
District *string `json:"district,omitempty" gorm:"type:varchar(255)"`
|
District *string `json:"district,omitempty" gorm:"type:varchar(255)"`
|
||||||
ImageURL *string `json:"image_url,omitempty" gorm:"type:text"`
|
ImageURL *string `json:"image_url,omitempty" gorm:"type:text"`
|
||||||
ImageURLs StringSlice `json:"additional_images" gorm:"type:jsonb"` // Multiple images stored as JSONB
|
ImageURLs StringSlice `json:"image_urls" gorm:"type:jsonb"`
|
||||||
|
OLTID *uuid.UUID `json:"olt_id,omitempty" gorm:"type:uuid"`
|
||||||
|
TowerID *uuid.UUID `json:"tower_id,omitempty" gorm:"type:uuid"` // Add TowerID field
|
||||||
CreatedAt time.Time `json:"created_at"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
UpdatedAt time.Time `json:"updated_at"`
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
|
||||||
// Fixed Relationships - Use direct foreign keys
|
// Relationships
|
||||||
DevicePort DevicePort `json:"device_port" gorm:"foreignKey:DeviceID;references:ID"`
|
DevicePort DevicePort `json:"device_port" gorm:"foreignKey:DeviceID;references:ID"`
|
||||||
BackbonesStart []Backbone `json:"backbones_start" gorm:"foreignKey:DeviceStartID;references:ID"`
|
BackbonesStart []Backbone `json:"backbones_start" gorm:"foreignKey:DeviceStartID;references:ID"`
|
||||||
BackbonesEnd []Backbone `json:"backbones_end" gorm:"foreignKey:DeviceEndID;references:ID"`
|
BackbonesEnd []Backbone `json:"backbones_end" gorm:"foreignKey:DeviceEndID;references:ID"`
|
||||||
FishbonesStart []Fishbone `json:"fishbones_start" gorm:"foreignKey:DeviceStartID;references:ID"`
|
FishbonesStart []Fishbone `json:"fishbones_start" gorm:"foreignKey:DeviceStartID;references:ID"`
|
||||||
FishbonesEnd []Fishbone `json:"fishbones_end" gorm:"foreignKey:DeviceEndID;references:ID"`
|
FishbonesEnd []Fishbone `json:"fishbones_end" gorm:"foreignKey:DeviceEndID;references:ID"`
|
||||||
|
OLT *OLT `json:"olt,omitempty" gorm:"foreignKey:OLTID;references:ID"`
|
||||||
|
Tower *Tower `json:"tower,omitempty" gorm:"foreignKey:TowerID;references:ID"` // Add Tower relationship
|
||||||
Towers []Tower `json:"towers" gorm:"foreignKey:DeviceID;references:ID"`
|
Towers []Tower `json:"towers" gorm:"foreignKey:DeviceID;references:ID"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,10 +33,13 @@ type Device struct {
|
||||||
ImageURL *string `json:"image_url,omitempty" gorm:"type:text"`
|
ImageURL *string `json:"image_url,omitempty" gorm:"type:text"`
|
||||||
ImageURLs StringSlice `json:"image_urls" gorm:"type:jsonb"` // Store multiple images as JSONB
|
ImageURLs StringSlice `json:"image_urls" gorm:"type:jsonb"` // Store multiple images as JSONB
|
||||||
OLTID *uuid.UUID `json:"olt_id,omitempty" gorm:"type:uuid"` // Add this field
|
OLTID *uuid.UUID `json:"olt_id,omitempty" gorm:"type:uuid"` // Add this field
|
||||||
|
TowerID *uuid.UUID `json:"tower_id,omitempty" gorm:"type:uuid"` // Add TowerID field
|
||||||
|
|
||||||
CreatedAt time.Time `json:"created_at"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
UpdatedAt time.Time `json:"updated_at"`
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
|
||||||
OLT *OLT `json:"olt,omitempty" gorm:"foreignKey:OLTID;references:ID"`
|
OLT *OLT `json:"olt,omitempty" gorm:"foreignKey:OLTID;references:ID"`
|
||||||
|
Tower *Tower `json:"tower,omitempty" gorm:"foreignKey:TowerID;references:ID"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Device) GetAllImageURLs() []string {
|
func (d *Device) GetAllImageURLs() []string {
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,8 @@ type DeviceDetailsRepo interface {
|
||||||
RemoveCustomerByPort(deviceID uuid.UUID, portNumber int) error
|
RemoveCustomerByPort(deviceID uuid.UUID, portNumber int) error
|
||||||
GetDevicesWithoutTowers(deviceTypes []string) ([]entity.DeviceDetails, error)
|
GetDevicesWithoutTowers(deviceTypes []string) ([]entity.DeviceDetails, error)
|
||||||
GetDevicesWithoutConnections(deviceTypes []string) ([]entity.DeviceDetails, error)
|
GetDevicesWithoutConnections(deviceTypes []string) ([]entity.DeviceDetails, error)
|
||||||
|
ValidateTowerExists(towerID uuid.UUID) (bool, error)
|
||||||
|
SyncTowerLocationWithDevice(deviceID uuid.UUID) error
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -53,6 +55,48 @@ func NewDeviceDetailsRepo(db *gorm.DB) DeviceDetailsRepo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *deviceDetailsRepo) ValidateTowerExists(towerID uuid.UUID) (bool, error) {
|
||||||
|
var count int64
|
||||||
|
err := r.db.Model(&entity.Tower{}).Where("id = ?", towerID).Count(&count).Error
|
||||||
|
return count > 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *deviceDetailsRepo) SyncTowerLocationWithDevice(deviceID uuid.UUID) error {
|
||||||
|
return r.db.Transaction(func(tx *gorm.DB) error {
|
||||||
|
// Get device details
|
||||||
|
var device entity.Device
|
||||||
|
if err := tx.Where("id = ?", deviceID).First(&device).Error; err != nil {
|
||||||
|
return fmt.Errorf("device not found: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update tower location if device has a tower assigned
|
||||||
|
if device.TowerID != nil {
|
||||||
|
if err := tx.Model(&entity.Tower{}).
|
||||||
|
Where("id = ?", *device.TowerID).
|
||||||
|
Updates(map[string]interface{}{
|
||||||
|
"longitude": device.Longitude,
|
||||||
|
"latitude": device.Latitude,
|
||||||
|
"updated_at": time.Now(),
|
||||||
|
}).Error; err != nil {
|
||||||
|
return fmt.Errorf("failed to sync tower location: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also update any towers that reference this device via dev_id
|
||||||
|
if err := tx.Model(&entity.Tower{}).
|
||||||
|
Where("dev_id = ?", deviceID).
|
||||||
|
Updates(map[string]interface{}{
|
||||||
|
"longitude": device.Longitude,
|
||||||
|
"latitude": device.Latitude,
|
||||||
|
"updated_at": time.Now(),
|
||||||
|
}).Error; err != nil {
|
||||||
|
return fmt.Errorf("failed to sync related towers location: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (r *deviceDetailsRepo) GetDevicesWithoutConnections(deviceTypes []string) ([]entity.DeviceDetails, error) {
|
func (r *deviceDetailsRepo) GetDevicesWithoutConnections(deviceTypes []string) ([]entity.DeviceDetails, error) {
|
||||||
var devices []entity.DeviceDetails
|
var devices []entity.DeviceDetails
|
||||||
|
|
||||||
|
|
@ -401,6 +445,18 @@ func (r *deviceDetailsRepo) UpdatePortAssignments(deviceID uuid.UUID, assignment
|
||||||
|
|
||||||
func (r *deviceDetailsRepo) Create(device entity.Device) error {
|
func (r *deviceDetailsRepo) Create(device entity.Device) error {
|
||||||
return r.db.Transaction(func(tx *gorm.DB) error {
|
return r.db.Transaction(func(tx *gorm.DB) error {
|
||||||
|
// Validate tower exists if TowerID is provided
|
||||||
|
if device.TowerID != nil {
|
||||||
|
var towerExists bool
|
||||||
|
var err error
|
||||||
|
if towerExists, err = r.ValidateTowerExists(*device.TowerID); err != nil {
|
||||||
|
return fmt.Errorf("failed to validate tower: %w", err)
|
||||||
|
}
|
||||||
|
if !towerExists {
|
||||||
|
return fmt.Errorf("tower with ID %s not found", device.TowerID.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Create device
|
// Create device
|
||||||
if err := tx.Create(&device).Error; err != nil {
|
if err := tx.Create(&device).Error; err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -416,7 +472,24 @@ func (r *deviceDetailsRepo) Create(device entity.Device) error {
|
||||||
UpdatedAt: device.UpdatedAt,
|
UpdatedAt: device.UpdatedAt,
|
||||||
}
|
}
|
||||||
|
|
||||||
return tx.Create(&devicePort).Error
|
if err := tx.Create(&devicePort).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sync tower location with device location
|
||||||
|
if device.TowerID != nil {
|
||||||
|
if err := tx.Model(&entity.Tower{}).
|
||||||
|
Where("id = ?", *device.TowerID).
|
||||||
|
Updates(map[string]interface{}{
|
||||||
|
"longitude": device.Longitude,
|
||||||
|
"latitude": device.Latitude,
|
||||||
|
"updated_at": time.Now(),
|
||||||
|
}).Error; err != nil {
|
||||||
|
return fmt.Errorf("failed to sync tower location: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -438,6 +511,8 @@ func (r *deviceDetailsRepo) GetAll() ([]entity.DeviceDetails, error) {
|
||||||
Preload("FishbonesEnd.DeviceStart").
|
Preload("FishbonesEnd.DeviceStart").
|
||||||
Preload("FishbonesEnd.DeviceEnd").
|
Preload("FishbonesEnd.DeviceEnd").
|
||||||
Preload("FishbonesEnd.Backbone").
|
Preload("FishbonesEnd.Backbone").
|
||||||
|
Preload("OLT").
|
||||||
|
Preload("Tower"). // Add Tower preload
|
||||||
Preload("Towers").
|
Preload("Towers").
|
||||||
Preload("Towers.Device").
|
Preload("Towers.Device").
|
||||||
Find(&devices).Error
|
Find(&devices).Error
|
||||||
|
|
@ -462,6 +537,8 @@ func (r *deviceDetailsRepo) GetByID(id uuid.UUID) (entity.DeviceDetails, error)
|
||||||
Preload("FishbonesEnd.DeviceStart").
|
Preload("FishbonesEnd.DeviceStart").
|
||||||
Preload("FishbonesEnd.DeviceEnd").
|
Preload("FishbonesEnd.DeviceEnd").
|
||||||
Preload("FishbonesEnd.Backbone").
|
Preload("FishbonesEnd.Backbone").
|
||||||
|
Preload("OLT").
|
||||||
|
Preload("Tower"). // Add Tower preload
|
||||||
Preload("Towers").
|
Preload("Towers").
|
||||||
Preload("Towers.Device").
|
Preload("Towers.Device").
|
||||||
Where("id = ?", id).
|
Where("id = ?", id).
|
||||||
|
|
@ -476,6 +553,20 @@ func (r *deviceDetailsRepo) Update(id uuid.UUID, updates map[string]interface{})
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If TowerID is updated, sync locations
|
||||||
|
if towerID, exists := updates["tower_id"]; exists && towerID != nil {
|
||||||
|
towerUUID := towerID.(uuid.UUID)
|
||||||
|
// Validate tower exists
|
||||||
|
var towerExists bool
|
||||||
|
var err error
|
||||||
|
if towerExists, err = r.ValidateTowerExists(towerUUID); err != nil {
|
||||||
|
return fmt.Errorf("failed to validate tower: %w", err)
|
||||||
|
}
|
||||||
|
if !towerExists {
|
||||||
|
return fmt.Errorf("tower with ID %s not found", towerUUID.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If port_amount is updated, update device_port
|
// If port_amount is updated, update device_port
|
||||||
if portAmount, exists := updates["port_amount"]; exists {
|
if portAmount, exists := updates["port_amount"]; exists {
|
||||||
if err := r.updatePortAmountCascade(tx, id, portAmount.(int)); err != nil {
|
if err := r.updatePortAmountCascade(tx, id, portAmount.(int)); err != nil {
|
||||||
|
|
@ -483,6 +574,15 @@ func (r *deviceDetailsRepo) Update(id uuid.UUID, updates map[string]interface{})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If location is updated, sync with tower
|
||||||
|
if _, hasLng := updates["longitude"]; hasLng {
|
||||||
|
if _, hasLat := updates["latitude"]; hasLat {
|
||||||
|
if err := r.SyncTowerLocationWithDevice(id); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -492,7 +592,9 @@ func (r *deviceDetailsRepo) GetDevicesWithoutTowers(deviceTypes []string) ([]ent
|
||||||
|
|
||||||
query := r.db.
|
query := r.db.
|
||||||
Preload("DevicePort").
|
Preload("DevicePort").
|
||||||
|
Preload("Tower"). // Add Tower preload
|
||||||
Where("device_type IN ?", deviceTypes).
|
Where("device_type IN ?", deviceTypes).
|
||||||
|
Where("tower_id IS NULL"). // Check for NULL TowerID instead of dev_id
|
||||||
Where("id NOT IN (?)",
|
Where("id NOT IN (?)",
|
||||||
r.db.Table("towers").
|
r.db.Table("towers").
|
||||||
Select("dev_id").
|
Select("dev_id").
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,8 @@ type DevicesRepo interface {
|
||||||
GetByType(deviceType string) ([]entity.Device, error)
|
GetByType(deviceType string) ([]entity.Device, error)
|
||||||
BulkUpdateImages(updates map[uuid.UUID]string) error
|
BulkUpdateImages(updates map[uuid.UUID]string) error
|
||||||
BulkUpdateImagesMultiple(updates map[uuid.UUID][]string) error
|
BulkUpdateImagesMultiple(updates map[uuid.UUID][]string) error
|
||||||
|
SyncTowerLocationWithDevice(deviceID uuid.UUID) error
|
||||||
|
ValidateTowerExists(towerID uuid.UUID) (bool, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type devicesRepo struct {
|
type devicesRepo struct {
|
||||||
|
|
@ -30,6 +32,48 @@ func NewDevicesRepo(db *gorm.DB) DevicesRepo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *devicesRepo) SyncTowerLocationWithDevice(deviceID uuid.UUID) error {
|
||||||
|
return r.db.Transaction(func(tx *gorm.DB) error {
|
||||||
|
// Get device details
|
||||||
|
var device entity.Device
|
||||||
|
if err := tx.Where("id = ?", deviceID).First(&device).Error; err != nil {
|
||||||
|
return fmt.Errorf("device not found: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update tower location if device has a tower assigned
|
||||||
|
if device.TowerID != nil {
|
||||||
|
if err := tx.Model(&entity.Tower{}).
|
||||||
|
Where("id = ?", *device.TowerID).
|
||||||
|
Updates(map[string]interface{}{
|
||||||
|
"longitude": device.Longitude,
|
||||||
|
"latitude": device.Latitude,
|
||||||
|
"updated_at": time.Now(),
|
||||||
|
}).Error; err != nil {
|
||||||
|
return fmt.Errorf("failed to sync tower location: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also update any towers that reference this device via dev_id
|
||||||
|
if err := tx.Model(&entity.Tower{}).
|
||||||
|
Where("dev_id = ?", deviceID).
|
||||||
|
Updates(map[string]interface{}{
|
||||||
|
"longitude": device.Longitude,
|
||||||
|
"latitude": device.Latitude,
|
||||||
|
"updated_at": time.Now(),
|
||||||
|
}).Error; err != nil {
|
||||||
|
return fmt.Errorf("failed to sync related towers location: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *devicesRepo) ValidateTowerExists(towerID uuid.UUID) (bool, error) {
|
||||||
|
var count int64
|
||||||
|
err := r.db.Model(&entity.Tower{}).Where("id = ?", towerID).Count(&count).Error
|
||||||
|
return count > 0, err
|
||||||
|
}
|
||||||
|
|
||||||
func (r *devicesRepo) BulkUpdateImages(updates map[uuid.UUID]string) error {
|
func (r *devicesRepo) BulkUpdateImages(updates map[uuid.UUID]string) error {
|
||||||
multipleUpdates := make(map[uuid.UUID][]string)
|
multipleUpdates := make(map[uuid.UUID][]string)
|
||||||
for deviceID, imageURL := range updates {
|
for deviceID, imageURL := range updates {
|
||||||
|
|
@ -96,6 +140,19 @@ func (r *devicesRepo) Post(device entity.Device) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sync tower location with device location
|
||||||
|
if device.TowerID != nil {
|
||||||
|
if err := tx.Model(&entity.Tower{}).
|
||||||
|
Where("id = ?", *device.TowerID).
|
||||||
|
Updates(map[string]interface{}{
|
||||||
|
"longitude": device.Longitude,
|
||||||
|
"latitude": device.Latitude,
|
||||||
|
"updated_at": time.Now(),
|
||||||
|
}).Error; err != nil {
|
||||||
|
return fmt.Errorf("failed to sync tower location: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -108,11 +165,43 @@ func (r *devicesRepo) GetAll() ([]entity.Device, error) {
|
||||||
return devices, nil
|
return devices, nil
|
||||||
}
|
}
|
||||||
func (r *devicesRepo) Update(id uuid.UUID, updates map[string]interface{}) error {
|
func (r *devicesRepo) Update(id uuid.UUID, updates map[string]interface{}) error {
|
||||||
err := r.db.Model(&entity.Device{}).Where("id = ?", id).Updates(updates).Error
|
return r.db.Transaction(func(tx *gorm.DB) error {
|
||||||
if err != nil {
|
// Validate tower exists if TowerID is being updated
|
||||||
|
if towerID, exists := updates["TowerID"]; exists && towerID != nil {
|
||||||
|
towerUUID := towerID.(uuid.UUID)
|
||||||
|
var towerExists bool
|
||||||
|
var err error
|
||||||
|
if towerExists, err = r.ValidateTowerExists(towerUUID); err != nil {
|
||||||
|
return fmt.Errorf("failed to validate tower: %w", err)
|
||||||
|
}
|
||||||
|
if !towerExists {
|
||||||
|
return fmt.Errorf("tower with ID %s not found", towerUUID.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update device
|
||||||
|
if err := tx.Model(&entity.Device{}).Where("id = ?", id).Updates(updates).Error; err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If location is updated, sync with tower
|
||||||
|
if _, hasLng := updates["Longitude"]; hasLng {
|
||||||
|
if _, hasLat := updates["Latitude"]; hasLat {
|
||||||
|
if err := r.SyncTowerLocationWithDevice(id); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If TowerID is updated, sync location
|
||||||
|
if _, exists := updates["TowerID"]; exists {
|
||||||
|
if err := r.SyncTowerLocationWithDevice(id); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *devicesRepo) GetByID(id uuid.UUID) (entity.Device, error) {
|
func (r *devicesRepo) GetByID(id uuid.UUID) (entity.Device, error) {
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ type TowerRepo interface {
|
||||||
GetByID(id uuid.UUID) (entity.Tower, error)
|
GetByID(id uuid.UUID) (entity.Tower, error)
|
||||||
CheckDeviceExists(deviceID uuid.UUID) (bool, error)
|
CheckDeviceExists(deviceID uuid.UUID) (bool, error)
|
||||||
BulkUpdateImages(updates map[uuid.UUID]string) error
|
BulkUpdateImages(updates map[uuid.UUID]string) error
|
||||||
|
GetDeviceLocation(deviceID uuid.UUID) (longitude, latitude float64, err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type towerRepo struct {
|
type towerRepo struct {
|
||||||
|
|
@ -28,6 +29,14 @@ func NewTowerRepo(db *gorm.DB) TowerRepo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *towerRepo) GetDeviceLocation(deviceID uuid.UUID) (longitude, latitude float64, err error) {
|
||||||
|
var device entity.Device
|
||||||
|
err = r.db.Select("longitude, latitude").Where("id = ?", deviceID).First(&device).Error
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, fmt.Errorf("device not found: %w", err)
|
||||||
|
}
|
||||||
|
return device.Longitude, device.Latitude, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (r *towerRepo) Post(tower entity.Tower) error {
|
func (r *towerRepo) Post(tower entity.Tower) error {
|
||||||
err := r.db.Create(&tower).Error
|
err := r.db.Create(&tower).Error
|
||||||
|
|
|
||||||
|
|
@ -390,6 +390,17 @@ func (u *deviceDetailsUseCase) CreateDeviceDetails(deviceDTO req.DeviceDetailsDT
|
||||||
return fmt.Errorf("validation error: %w", err)
|
return fmt.Errorf("validation error: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate tower exists if TowerID is provided
|
||||||
|
if deviceDTO.TowerID != nil {
|
||||||
|
towerExists, err := u.deviceDetailsRepo.ValidateTowerExists(*deviceDTO.TowerID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to validate tower: %w", err)
|
||||||
|
}
|
||||||
|
if !towerExists {
|
||||||
|
return fmt.Errorf("tower with ID %s not found", deviceDTO.TowerID.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
newDevice := entity.Device{
|
newDevice := entity.Device{
|
||||||
ID: uuid.New(),
|
ID: uuid.New(),
|
||||||
DeviceCode: deviceDTO.DeviceCode,
|
DeviceCode: deviceDTO.DeviceCode,
|
||||||
|
|
@ -401,6 +412,7 @@ func (u *deviceDetailsUseCase) CreateDeviceDetails(deviceDTO req.DeviceDetailsDT
|
||||||
Province: deviceDTO.Province,
|
Province: deviceDTO.Province,
|
||||||
City: deviceDTO.City,
|
City: deviceDTO.City,
|
||||||
District: deviceDTO.District,
|
District: deviceDTO.District,
|
||||||
|
TowerID: deviceDTO.TowerID, // Add TowerID
|
||||||
CreatedAt: time.Now(),
|
CreatedAt: time.Now(),
|
||||||
UpdatedAt: time.Now(),
|
UpdatedAt: time.Now(),
|
||||||
}
|
}
|
||||||
|
|
@ -480,6 +492,17 @@ func (u *deviceDetailsUseCase) UpdateDeviceDetails(id uuid.UUID, deviceDTO req.U
|
||||||
if deviceDTO.District != nil {
|
if deviceDTO.District != nil {
|
||||||
updates["district"] = *deviceDTO.District
|
updates["district"] = *deviceDTO.District
|
||||||
}
|
}
|
||||||
|
if deviceDTO.TowerID != nil {
|
||||||
|
// Validate tower exists
|
||||||
|
towerExists, err := u.deviceDetailsRepo.ValidateTowerExists(*deviceDTO.TowerID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to validate tower: %w", err)
|
||||||
|
}
|
||||||
|
if !towerExists {
|
||||||
|
return fmt.Errorf("tower with ID %s not found", deviceDTO.TowerID.String())
|
||||||
|
}
|
||||||
|
updates["tower_id"] = *deviceDTO.TowerID
|
||||||
|
}
|
||||||
|
|
||||||
if len(updates) == 0 {
|
if len(updates) == 0 {
|
||||||
return errors.New("no fields to update")
|
return errors.New("no fields to update")
|
||||||
|
|
@ -489,7 +512,6 @@ func (u *deviceDetailsUseCase) UpdateDeviceDetails(id uuid.UUID, deviceDTO req.U
|
||||||
|
|
||||||
return u.deviceDetailsRepo.Update(id, updates)
|
return u.deviceDetailsRepo.Update(id, updates)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *deviceDetailsUseCase) DeleteDeviceDetails(id uuid.UUID) error {
|
func (u *deviceDetailsUseCase) DeleteDeviceDetails(id uuid.UUID) error {
|
||||||
// Check if device has connections
|
// Check if device has connections
|
||||||
backbones, err := u.deviceDetailsRepo.GetBackbonesByDeviceID(id)
|
backbones, err := u.deviceDetailsRepo.GetBackbonesByDeviceID(id)
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,8 @@ type DeviceUseCase interface {
|
||||||
CreateDeviceWithMultipleImages(device req.DeviceDTO, imageFiles []*multipart.FileHeader) error
|
CreateDeviceWithMultipleImages(device req.DeviceDTO, imageFiles []*multipart.FileHeader) error
|
||||||
UpdateDeviceWithMultipleImages(id uuid.UUID, device req.UpdateDeviceDTO, imageFiles []*multipart.FileHeader, replaceImages ...bool) error
|
UpdateDeviceWithMultipleImages(id uuid.UUID, device req.UpdateDeviceDTO, imageFiles []*multipart.FileHeader, replaceImages ...bool) error
|
||||||
BulkUploadImagesMultiple(devices []req.BulkDeviceImageUploadDTO, imageFiles []*multipart.FileHeader, fileDistribution []int) error
|
BulkUploadImagesMultiple(devices []req.BulkDeviceImageUploadDTO, imageFiles []*multipart.FileHeader, fileDistribution []int) error
|
||||||
|
|
||||||
|
ValidateTowerExists(towerID uuid.UUID) (bool, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type deviceUseCase struct {
|
type deviceUseCase struct {
|
||||||
|
|
@ -42,6 +44,10 @@ func NewDeviceUseCase(deviceRepo repository.DevicesRepo, geocoder service.Geocod
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *deviceUseCase) ValidateTowerExists(towerID uuid.UUID) (bool, error) {
|
||||||
|
return u.deviceRepo.ValidateTowerExists(towerID)
|
||||||
|
}
|
||||||
|
|
||||||
func (u *deviceUseCase) CreateDeviceWithMultipleImages(device req.DeviceDTO, imageFiles []*multipart.FileHeader) error {
|
func (u *deviceUseCase) CreateDeviceWithMultipleImages(device req.DeviceDTO, imageFiles []*multipart.FileHeader) error {
|
||||||
err := u.validate.Struct(device)
|
err := u.validate.Struct(device)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -81,6 +87,7 @@ func (u *deviceUseCase) CreateDeviceWithMultipleImages(device req.DeviceDTO, ima
|
||||||
Province: device.Province,
|
Province: device.Province,
|
||||||
City: device.City,
|
City: device.City,
|
||||||
District: device.District,
|
District: device.District,
|
||||||
|
TowerID: device.TowerID, // Add TowerID field
|
||||||
ImageURL: &primaryImageURL, // Primary image as pointer
|
ImageURL: &primaryImageURL, // Primary image as pointer
|
||||||
ImageURLs: entity.StringSlice(imageURLs), // All images
|
ImageURLs: entity.StringSlice(imageURLs), // All images
|
||||||
CreatedAt: time.Now(),
|
CreatedAt: time.Now(),
|
||||||
|
|
@ -126,6 +133,18 @@ func (u *deviceUseCase) UpdateDeviceWithMultipleImages(id uuid.UUID, device req.
|
||||||
updates["District"] = *device.District
|
updates["District"] = *device.District
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle TowerID validation and update
|
||||||
|
if device.TowerID != nil {
|
||||||
|
towerExists, err := u.ValidateTowerExists(*device.TowerID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to validate tower: %w", err)
|
||||||
|
}
|
||||||
|
if !towerExists {
|
||||||
|
return fmt.Errorf("tower with ID %s not found", device.TowerID.String())
|
||||||
|
}
|
||||||
|
updates["TowerID"] = *device.TowerID
|
||||||
|
}
|
||||||
|
|
||||||
// Handle multiple image uploads
|
// Handle multiple image uploads
|
||||||
if len(imageFiles) > 0 {
|
if len(imageFiles) > 0 {
|
||||||
// Get current device to handle existing images
|
// Get current device to handle existing images
|
||||||
|
|
@ -296,6 +315,17 @@ func (u *deviceUseCase) UpdateDevice(id uuid.UUID, device req.UpdateDeviceDTO) e
|
||||||
if device.District != nil {
|
if device.District != nil {
|
||||||
updates["District"] = *device.District
|
updates["District"] = *device.District
|
||||||
}
|
}
|
||||||
|
// Handle TowerID validation and update
|
||||||
|
if device.TowerID != nil {
|
||||||
|
towerExists, err := u.ValidateTowerExists(*device.TowerID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to validate tower: %w", err)
|
||||||
|
}
|
||||||
|
if !towerExists {
|
||||||
|
return fmt.Errorf("tower with ID %s not found", device.TowerID.String())
|
||||||
|
}
|
||||||
|
updates["TowerID"] = *device.TowerID
|
||||||
|
}
|
||||||
if device.DeviceType != nil && (*device.DeviceType == "OTB" || *device.DeviceType == "ODP") && device.PortAmount != nil && *device.PortAmount <= 0 {
|
if device.DeviceType != nil && (*device.DeviceType == "OTB" || *device.DeviceType == "ODP") && device.PortAmount != nil && *device.PortAmount <= 0 {
|
||||||
return fmt.Errorf("port amount must be greater than 0 for OTB or ODP devices")
|
return fmt.Errorf("port amount must be greater than 0 for OTB or ODP devices")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -285,12 +285,34 @@ func (u *towerUsecase) UpdateTower(id uuid.UUID, tower req.UpdateTowerDTO, image
|
||||||
if tower.TowerCode != nil {
|
if tower.TowerCode != nil {
|
||||||
updates["TowerCode"] = *tower.TowerCode
|
updates["TowerCode"] = *tower.TowerCode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IMPORTANT: Ignore longitude/latitude updates if tower is assigned to a device
|
||||||
|
currentTower, err := u.towerRepo.GetByID(id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// If tower has a device assigned, ignore location updates
|
||||||
|
if currentTower.DeviceID == nil {
|
||||||
|
// Only allow location updates for external towers (not assigned to devices)
|
||||||
if tower.Longitude != nil {
|
if tower.Longitude != nil {
|
||||||
updates["Longitude"] = *tower.Longitude
|
updates["Longitude"] = *tower.Longitude
|
||||||
}
|
}
|
||||||
if tower.Latitude != nil {
|
if tower.Latitude != nil {
|
||||||
updates["Latitude"] = *tower.Latitude
|
updates["Latitude"] = *tower.Latitude
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
// If DeviceID is being assigned, sync location with device
|
||||||
|
if tower.DeviceID != nil && currentTower.DeviceID == nil {
|
||||||
|
// Get device location using repository method
|
||||||
|
longitude, latitude, err := u.towerRepo.GetDeviceLocation(*tower.DeviceID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get device location: %w", err)
|
||||||
|
}
|
||||||
|
updates["Longitude"] = longitude
|
||||||
|
updates["Latitude"] = latitude
|
||||||
|
}
|
||||||
|
|
||||||
if tower.ExternalTower != nil {
|
if tower.ExternalTower != nil {
|
||||||
updates["ExternalTower"] = *tower.ExternalTower
|
updates["ExternalTower"] = *tower.ExternalTower
|
||||||
}
|
}
|
||||||
|
|
@ -298,8 +320,7 @@ func (u *towerUsecase) UpdateTower(id uuid.UUID, tower req.UpdateTowerDTO, image
|
||||||
// Handle image upload
|
// Handle image upload
|
||||||
if imageFile != nil {
|
if imageFile != nil {
|
||||||
// Get current tower to delete old image
|
// Get current tower to delete old image
|
||||||
currentTower, err := u.towerRepo.GetByID(id)
|
if currentTower.ImageURL != "" {
|
||||||
if err == nil && currentTower.ImageURL != "" {
|
|
||||||
// Delete old image
|
// Delete old image
|
||||||
helper.DeleteTowerImage(currentTower.ImageURL)
|
helper.DeleteTowerImage(currentTower.ImageURL)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -82,9 +82,45 @@ func ConvertToDeviceDetailsResponse(device entity.DeviceDetails, geocoder servic
|
||||||
fishboneInfos = append(fishboneInfos, info)
|
fishboneInfos = append(fishboneInfos, info)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert tower connections
|
// COMBINE ALL TOWERS INTO A SINGLE ARRAY
|
||||||
towerInfos := make([]res.TowerConnectionDetail, 0)
|
towerInfos := make([]res.TowerConnectionDetail, 0)
|
||||||
|
|
||||||
|
// First, add the assigned tower (tower referenced by device.TowerID)
|
||||||
|
if device.TowerID != nil && device.Tower != nil {
|
||||||
|
distance := calculateDistance(device.Latitude, device.Longitude, device.Tower.Latitude, device.Tower.Longitude)
|
||||||
|
|
||||||
|
var externalTower *bool
|
||||||
|
if device.Tower.ExternalTower != nil {
|
||||||
|
externalTower = device.Tower.ExternalTower
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all tower image URLs
|
||||||
|
allTowerImageURLs := device.Tower.GetAllImageURLs()
|
||||||
|
|
||||||
|
// Primary image URL
|
||||||
|
var imageURL *string
|
||||||
|
if device.Tower.ImageURL != "" {
|
||||||
|
imageURL = &device.Tower.ImageURL
|
||||||
|
}
|
||||||
|
|
||||||
|
assignedTowerInfo := res.TowerConnectionDetail{
|
||||||
|
ID: device.Tower.ID,
|
||||||
|
TowerCode: device.Tower.TowerCode,
|
||||||
|
Distance: distance,
|
||||||
|
ExternalTower: externalTower,
|
||||||
|
ImageURL: imageURL,
|
||||||
|
ImageURLs: allTowerImageURLs,
|
||||||
|
}
|
||||||
|
towerInfos = append(towerInfos, assignedTowerInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then, add towers that reference this device via dev_id (but avoid duplicates)
|
||||||
for _, tower := range device.Towers {
|
for _, tower := range device.Towers {
|
||||||
|
// Skip if this tower is already added as assigned tower
|
||||||
|
if device.TowerID != nil && tower.ID == *device.TowerID {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
distance := calculateDistance(device.Latitude, device.Longitude, tower.Latitude, tower.Longitude)
|
distance := calculateDistance(device.Latitude, device.Longitude, tower.Latitude, tower.Longitude)
|
||||||
|
|
||||||
var externalTower *bool
|
var externalTower *bool
|
||||||
|
|
@ -112,7 +148,6 @@ func ConvertToDeviceDetailsResponse(device entity.DeviceDetails, geocoder servic
|
||||||
towerInfos = append(towerInfos, info)
|
towerInfos = append(towerInfos, info)
|
||||||
}
|
}
|
||||||
|
|
||||||
// REPLACE ALL THE EXISTING PORT ASSIGNMENT CODE WITH THIS:
|
|
||||||
// Convert port assignments - only for ODP devices
|
// Convert port assignments - only for ODP devices
|
||||||
var finalPortAssignments []res.PortAssignmentResponse
|
var finalPortAssignments []res.PortAssignmentResponse
|
||||||
var customerNames []string
|
var customerNames []string
|
||||||
|
|
@ -183,9 +218,11 @@ func ConvertToDeviceDetailsResponse(device entity.DeviceDetails, geocoder servic
|
||||||
District: device.District,
|
District: device.District,
|
||||||
ImageURL: device.ImageURL,
|
ImageURL: device.ImageURL,
|
||||||
ImageURLs: allImageURLs,
|
ImageURLs: allImageURLs,
|
||||||
|
TowerID: device.TowerID, // Keep TowerID for reference
|
||||||
|
// Remove AssignedTower field - now everything is in Towers
|
||||||
Backbones: backboneInfos,
|
Backbones: backboneInfos,
|
||||||
Fishbones: fishboneInfos,
|
Fishbones: fishboneInfos,
|
||||||
Towers: towerInfos,
|
Towers: towerInfos, // All towers combined here
|
||||||
CreatedAt: device.CreatedAt,
|
CreatedAt: device.CreatedAt,
|
||||||
UpdatedAt: device.UpdatedAt,
|
UpdatedAt: device.UpdatedAt,
|
||||||
}
|
}
|
||||||
|
|
@ -194,5 +231,3 @@ func ConvertToDeviceDetailsResponse(device entity.DeviceDetails, geocoder servic
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ... rest of helper functions remain the same
|
|
||||||
Loading…
Reference in New Issue