From 8e69a70092188a765616f93fe892071c405909c0 Mon Sep 17 00:00:00 2001 From: areeqakbr Date: Mon, 16 Jun 2025 11:42:58 +0700 Subject: [PATCH] fixing for device details and logic error on devices --- .gitignore | 3 +- model/entity/device_details.go | 76 ++++++++++++++---- usecase/device_details.go | 26 ++---- usecase/fishbone_usecase.go | 2 +- utils/helper/device_details.go | 140 +++++++++++---------------------- 5 files changed, 111 insertions(+), 136 deletions(-) diff --git a/.gitignore b/.gitignore index 98b2774..61c8306 100644 --- a/.gitignore +++ b/.gitignore @@ -42,4 +42,5 @@ node_modules/ # Coverage files coverage.out -uploads/towers \ No newline at end of file +uploads/towers +uploads/* \ No newline at end of file diff --git a/model/entity/device_details.go b/model/entity/device_details.go index 318c1de..4b4ac31 100644 --- a/model/entity/device_details.go +++ b/model/entity/device_details.go @@ -6,21 +6,21 @@ import ( ) type DeviceDetails struct { - ID uuid.UUID `json:"id" gorm:"type:uuid;primaryKey"` - DeviceCode string `json:"device_code" gorm:"unique"` - DeviceType DeviceType `json:"device_type"` - Longitude float64 `json:"longitude"` - Latitude float64 `json:"latitude"` - PortAmount int `json:"port_amount"` - Status DeviceStatus `json:"status"` - Region *string `json:"region,omitempty" gorm:"type:varchar(255)"` - Province *string `json:"province,omitempty" gorm:"type:varchar(255)"` - City *string `json:"city,omitempty" gorm:"type:varchar(255)"` - District *string `json:"district,omitempty" gorm:"type:varchar(255)"` - ImageURL *string `json:"image_url,omitempty" gorm:"type:text"` - AdditionalImages StringSlice `gorm:"type:text[]"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` + ID uuid.UUID `json:"id" gorm:"type:uuid;primaryKey"` + DeviceCode string `json:"device_code" gorm:"unique"` + DeviceType DeviceType `json:"device_type"` + Longitude float64 `json:"longitude"` + Latitude float64 `json:"latitude"` + PortAmount int `json:"port_amount"` + Status DeviceStatus `json:"status"` + Region *string `json:"region,omitempty" gorm:"type:varchar(255)"` + Province *string `json:"province,omitempty" gorm:"type:varchar(255)"` + City *string `json:"city,omitempty" gorm:"type:varchar(255)"` + District *string `json:"district,omitempty" gorm:"type:varchar(255)"` + ImageURL *string `json:"image_url,omitempty" gorm:"type:text"` + ImageURLs StringSlice `json:"additional_images" gorm:"type:jsonb"` // Multiple images stored as JSONB + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` // Fixed Relationships - Use direct foreign keys DevicePort DevicePort `json:"device_port" gorm:"foreignKey:DeviceID;references:ID"` @@ -31,8 +31,47 @@ type DeviceDetails struct { Towers []Tower `json:"towers" gorm:"foreignKey:DeviceID;references:ID"` } -func (DeviceDetails) TableName() string { - return "devices" // Use same table as Device entity + + +func (d *DeviceDetails) GetAllImageURLs() []string { + var allImages []string + + // Add main image URL if exists + if d.ImageURL != nil && *d.ImageURL != "" { + allImages = append(allImages, *d.ImageURL) + } + + // Add additional images + for _, img := range d.ImageURLs { + if img != "" { + // Avoid duplicates + isDuplicate := false + for _, existingImg := range allImages { + if existingImg == img { + isDuplicate = true + break + } + } + if !isDuplicate { + allImages = append(allImages, img) + } + } + } + + return allImages +} + +func (d *DeviceDetails) SetMultipleImages(imageURLs []string) { + if len(imageURLs) == 0 { + return + } + + // Set primary image + primaryImage := imageURLs[0] + d.ImageURL = &primaryImage + + // Set all images + d.ImageURLs = StringSlice(imageURLs) } // Helper method to get all backbones connected to this device @@ -51,3 +90,6 @@ func (d *DeviceDetails) GetAllFishbones() []Fishbone { return allFishbones } +func (DeviceDetails) TableName() string { + return "devices" // Use same table as Device entity +} \ No newline at end of file diff --git a/usecase/device_details.go b/usecase/device_details.go index b5e5e69..face209 100644 --- a/usecase/device_details.go +++ b/usecase/device_details.go @@ -71,12 +71,10 @@ func (u *deviceDetailsUseCase) UpdateDeviceDetailsWithMultipleImages(id uuid.UUI updates["latitude"] = *deviceDTO.Latitude } if deviceDTO.PortAmount != nil { - // Validate port amount change if *deviceDTO.PortAmount < 0 { return fmt.Errorf("port amount cannot be negative") } - // If not setting to 0, check current usage if *deviceDTO.PortAmount > 0 { currentUsed, _, err := u.deviceDetailsRepo.GetPortUsageByDevice(id) if err != nil { @@ -124,27 +122,13 @@ func (u *deviceDetailsUseCase) UpdateDeviceDetailsWithMultipleImages(id uuid.UUI if shouldReplace { // Replace all images - delete old ones - if currentDevice.ImageURL != nil && *currentDevice.ImageURL != "" { - helper.DeleteDeviceImage(*currentDevice.ImageURL) - } - // Delete other images if they exist - for _, imgURL := range currentDevice.AdditionalImages { - if imgURL != "" && (currentDevice.ImageURL == nil || imgURL != *currentDevice.ImageURL) { - helper.DeleteDeviceImage(imgURL) - } + for _, oldImageURL := range currentDevice.GetAllImageURLs() { + helper.DeleteDeviceImage(oldImageURL) } finalImageURLs = newImageURLs } else { // Append to existing images - existingImages := make([]string, 0) - if currentDevice.ImageURL != nil && *currentDevice.ImageURL != "" { - existingImages = append(existingImages, *currentDevice.ImageURL) - } - for _, imgURL := range currentDevice.AdditionalImages { - if imgURL != "" && (currentDevice.ImageURL == nil || imgURL != *currentDevice.ImageURL) { - existingImages = append(existingImages, imgURL) - } - } + existingImages := currentDevice.GetAllImageURLs() finalImageURLs = append(existingImages, newImageURLs...) } @@ -153,8 +137,8 @@ func (u *deviceDetailsUseCase) UpdateDeviceDetailsWithMultipleImages(id uuid.UUI updates["image_url"] = finalImageURLs[0] } - // Update all images - updates["additional_images"] = entity.StringSlice(finalImageURLs) + // Update all images using image_urls column (not additional_images) + updates["image_urls"] = entity.StringSlice(finalImageURLs) } if len(updates) == 0 { diff --git a/usecase/fishbone_usecase.go b/usecase/fishbone_usecase.go index 3745040..e45650d 100644 --- a/usecase/fishbone_usecase.go +++ b/usecase/fishbone_usecase.go @@ -78,7 +78,7 @@ func (u *fishboneUseCase) CreateFishbone(fishbone req.FishboneDTO) error { } // Validate device types - if startDevice.DeviceType != "closure" { + if startDevice.DeviceType != "CLOSURE" || startDevice.DeviceType != "closure" { return fmt.Errorf("start device must be of type closure, got %s", startDevice.DeviceType) } diff --git a/utils/helper/device_details.go b/utils/helper/device_details.go index 7c79676..6eb53b2 100644 --- a/utils/helper/device_details.go +++ b/utils/helper/device_details.go @@ -82,35 +82,37 @@ func ConvertToDeviceDetailsResponse(device entity.DeviceDetails, geocoder servic fishboneInfos = append(fishboneInfos, info) } - // Convert tower connections - safely handle nullable ExternalTower + // Convert tower connections towerInfos := make([]res.TowerConnectionDetail, 0) for _, tower := range device.Towers { distance := calculateDistance(device.Latitude, device.Longitude, tower.Latitude, tower.Longitude) - // Safely handle nullable ExternalTower field var externalTower *bool if tower.ExternalTower != nil { externalTower = tower.ExternalTower - } // If tower.ExternalTower is nil, externalTower remains nil + } - // Safely handle nullable ImageURL + // Get all tower image URLs + allTowerImageURLs := tower.GetAllImageURLs() + + // Primary image URL var imageURL *string if tower.ImageURL != "" { imageURL = &tower.ImageURL } info := res.TowerConnectionDetail{ - ID: tower.ID, - TowerCode: tower.TowerCode, - Distance: distance, - ExternalTower: externalTower, // This will be null if tower.ExternalTower is nil - ImageURL: imageURL, - ImageURLs: tower.GetAllImageURLs(), // Get all images as a slice + ID: tower.ID, + TowerCode: tower.TowerCode, + Distance: distance, + ExternalTower: externalTower, + ImageURL: imageURL, + ImageURLs: allTowerImageURLs, } towerInfos = append(towerInfos, info) } - // Convert entity.PortAssignmentResponse to res.PortAssignmentResponse + // Convert port assignments entityPortAssignments := device.DevicePort.GetPortAssignmentsWithDetails() resPortAssignments := make([]res.PortAssignmentResponse, len(entityPortAssignments)) for i, pa := range entityPortAssignments { @@ -120,6 +122,7 @@ func ConvertToDeviceDetailsResponse(device entity.DeviceDetails, geocoder servic IsOccupied: pa.IsOccupied, } } + portAssignments := device.DevicePort.GetPortAssignmentsWithDetails() // Ensure we show all ports up to PortAmount @@ -135,106 +138,51 @@ func ConvertToDeviceDetailsResponse(device entity.DeviceDetails, geocoder servic // Fill in missing ports finalPortAssignments := make([]res.PortAssignmentResponse, 0) for i := 1; i <= device.PortAmount; i++ { - if pa, exists := portAssignmentMap[i]; exists { - finalPortAssignments = append(finalPortAssignments, pa) + if assignment, exists := portAssignmentMap[i]; exists { + finalPortAssignments = append(finalPortAssignments, assignment) } else { - // Determine if this port should be occupied - isOccupied := false - - // Count occupied ports before this one - occupiedBefore := 0 - for j := 1; j < i; j++ { - if existingPA, exists := portAssignmentMap[j]; exists && existingPA.IsOccupied { - occupiedBefore++ - } - } - - // Count total ports with customers - customersCount := 0 - for _, pa := range portAssignments { - if pa.CustomerName != nil && *pa.CustomerName != "" { - customersCount++ - } - } - - // This port should be occupied if we haven't reached port_used yet - if occupiedBefore + customersCount < device.DevicePort.PortUsed { - isOccupied = true - } - finalPortAssignments = append(finalPortAssignments, res.PortAssignmentResponse{ PortNumber: i, CustomerName: nil, - IsOccupied: isOccupied, + IsOccupied: false, }) } } - var customerNames []string + // Get customer names + customerNames := device.DevicePort.GetCustomerNamesWithPorts() - // If port_used is 0, return empty port assignments - if device.PortAmount == 0 { - finalPortAssignments = []res.PortAssignmentResponse{} - customerNames = []string{} - } else if device.DevicePort.PortUsed == 0 { - // If port_used is 0, return empty port assignments but keep port_amount structure - finalPortAssignments = []res.PortAssignmentResponse{} - customerNames = []string{} - } else { - // Get port assignments with details - portAssignments := device.DevicePort.GetPortAssignmentsWithDetails() - - // Convert to response format - finalPortAssignments = make([]res.PortAssignmentResponse, len(portAssignments)) - for i, pa := range portAssignments { - finalPortAssignments[i] = res.PortAssignmentResponse{ - PortNumber: pa.PortNumber, - CustomerName: pa.CustomerName, - IsOccupied: pa.IsOccupied, - } - } - - // Get customer names - customerNames = device.DevicePort.GetCustomerNamesOnly() - } - - var deviceImageURLs []string - if device.ImageURL != nil && *device.ImageURL != "" { - deviceImageURLs = append(deviceImageURLs, *device.ImageURL) - } - - // Use deviceImageURLs instead of trying to call a non-existent method - allImageURLs := deviceImageURLs + // DIRECT FIX: Use device.GetAllImageURLs() method directly + allImageURLs := device.GetAllImageURLs() // Handle empty slice vs nil for JSON response if len(allImageURLs) == 0 { allImageURLs = []string{} // Return empty array instead of nil } - + response := res.DeviceDetailsResponse{ - ID: device.ID, - DeviceCode: device.DeviceCode, - DeviceType: string(device.DeviceType), - Address: address, - Longitude: device.Longitude, - Latitude: device.Latitude, - Status: string(device.Status), - PortAmount: device.PortAmount, - PortUsed: device.DevicePort.PortUsed, - PortAvailable: device.DevicePort.PortAvailable, - CustomerNames: customerNames, // Just customer names - PortAssignments: finalPortAssignments, // Use finalPortAssignments instead of resPortAssignments - Region: device.Region, - Province: device.Province, - City: device.City, - District: device.District, - ImageURL: device.ImageURL, - ImageURLs: allImageURLs, // Use allImageURLs to ensure empty array instead of nil - Backbones: backboneInfos, - Fishbones: fishboneInfos, - Towers: towerInfos, - CreatedAt: device.CreatedAt, - UpdatedAt: device.UpdatedAt, + ID: device.ID, + DeviceCode: device.DeviceCode, + DeviceType: string(device.DeviceType), + Address: address, + Longitude: device.Longitude, + Latitude: device.Latitude, + Status: string(device.Status), + PortAmount: device.PortAmount, + PortUsed: device.DevicePort.PortUsed, + PortAvailable: device.DevicePort.PortAvailable, + CustomerNames: customerNames, + PortAssignments: finalPortAssignments, + Province: device.Province, + City: device.City, + District: device.District, + ImageURL: device.ImageURL, // Primary image + ImageURLs: allImageURLs, // All images using device.GetAllImageURLs() + Backbones: backboneInfos, + Fishbones: fishboneInfos, + Towers: towerInfos, + CreatedAt: device.CreatedAt, + UpdatedAt: device.UpdatedAt, } return response, nil