From 1374bfa4bfb949c79dc02ca061872bcf94c39851 Mon Sep 17 00:00:00 2001 From: areeqakbr Date: Mon, 30 Jun 2025 11:12:39 +0700 Subject: [PATCH] adding olt on device creation --- delivery/controller/devices_controller.go | 52 ++++++++++-- manager/usecase_manager.go | 11 +-- model/dto/req/deviceDetails.go | 2 + model/dto/req/device_dto.go | 2 + model/dto/res/device_details.go | 8 +- usecase/device_details.go | 53 +++++++++++- usecase/device_usecase.go | 99 ++++++++++++++++++++++- utils/helper/device_details.go | 15 ++-- 8 files changed, 221 insertions(+), 21 deletions(-) diff --git a/delivery/controller/devices_controller.go b/delivery/controller/devices_controller.go index 876760a..b072ba6 100644 --- a/delivery/controller/devices_controller.go +++ b/delivery/controller/devices_controller.go @@ -201,6 +201,27 @@ func (dc *DeviceController) CreateDevice() gin.HandlerFunc { } } + var towerID *uuid.UUID + if towerIDStr := c.PostForm("tower_id"); towerIDStr != "" { + if parsedTowerID, err := uuid.Parse(towerIDStr); err == nil { + towerID = &parsedTowerID + } else { + common.ErrorResponses(c, http.StatusBadRequest, "Invalid tower ID format") + return + } + } + + // Handle OLTID in form data + var oltID *uuid.UUID + if oltIDStr := c.PostForm("olt_id"); oltIDStr != "" { + if parsedOLTID, err := uuid.Parse(oltIDStr); err == nil { + oltID = &parsedOLTID + } else { + common.ErrorResponses(c, http.StatusBadRequest, "Invalid OLT ID format") + return + } + } + // Create DTO deviceDTO := req.DeviceDTO{ DeviceCode: deviceCode, @@ -209,6 +230,8 @@ func (dc *DeviceController) CreateDevice() gin.HandlerFunc { Latitude: latitude, PortAmount: portAmount, Status: status, + TowerID: towerID, // Add TowerID + OLTID: oltID, // Add OLTID } // Handle optional string fields @@ -288,7 +311,7 @@ func (dc *DeviceController) GetDeviceByID() gin.HandlerFunc { func (dc *DeviceController) UpdateDevice() gin.HandlerFunc { return func(c *gin.Context) { id := c.Param("uuid") - uuid, err := uuid.Parse(id) + deviceUUID, err := uuid.Parse(id) // Change variable name from 'uuid' to 'deviceUUID' if err != nil { common.ErrorResponses(c, http.StatusBadRequest, "Invalid UUID") return @@ -305,9 +328,9 @@ func (dc *DeviceController) UpdateDevice() gin.HandlerFunc { return } - err = dc.du.UpdateDevice(uuid, deviceDTO) + err = dc.du.UpdateDevice(deviceUUID, deviceDTO) // Use the new variable name if err != nil { - common.ErrorResponses(c, http.StatusBadRequest, "Device not found") + common.ErrorResponses(c, http.StatusBadRequest, err.Error()) // Also fix error message return } @@ -315,7 +338,6 @@ func (dc *DeviceController) UpdateDevice() gin.HandlerFunc { return } - // Handle multipart form request err = c.Request.ParseMultipartForm(50 << 20) // 50MB for multiple images if err != nil { @@ -369,6 +391,26 @@ func (dc *DeviceController) UpdateDevice() gin.HandlerFunc { deviceUpdateDTO.District = &district } + // Handle TowerID in form data + if towerIDStr := c.PostForm("tower_id"); towerIDStr != "" { + if towerID, err := uuid.Parse(towerIDStr); err == nil { + deviceUpdateDTO.TowerID = &towerID + } else { + common.ErrorResponses(c, http.StatusBadRequest, "Invalid tower ID format") + return + } + } + + // Handle OLTID in form data + if oltIDStr := c.PostForm("olt_id"); oltIDStr != "" { + if oltID, err := uuid.Parse(oltIDStr); err == nil { + deviceUpdateDTO.OLTID = &oltID + } else { + common.ErrorResponses(c, http.StatusBadRequest, "Invalid OLT ID format") + return + } + } + // Get multiple image files form := c.Request.MultipartForm imageFiles := form.File["images"] // Support multiple images @@ -383,7 +425,7 @@ func (dc *DeviceController) UpdateDevice() gin.HandlerFunc { // Handle replace_images flag replaceImages := c.PostForm("replace_images") == "true" - err = dc.du.UpdateDeviceWithMultipleImages(uuid, deviceUpdateDTO, imageFiles, replaceImages) + err = dc.du.UpdateDeviceWithMultipleImages(deviceUUID, deviceUpdateDTO, imageFiles, replaceImages) // Use the new variable name if err != nil { common.ErrorResponses(c, http.StatusBadRequest, err.Error()) return diff --git a/manager/usecase_manager.go b/manager/usecase_manager.go index 2679de9..7dcd23b 100644 --- a/manager/usecase_manager.go +++ b/manager/usecase_manager.go @@ -42,10 +42,11 @@ func NewUsecaseManager(repo RepositoryManager, cfg *config.Config) UsecaseManage } func (um *usecaseManager) NewDeviceDetailsUsecase() usecase.DeviceDetailsUseCase { - return usecase.NewDeviceDetailsUseCase( - um.repo.NewDeviceDetailsRepository(), - um.geocoder, - ) + return usecase.NewDeviceDetailsUseCase( + um.repo.NewDeviceDetailsRepository(), + um.repo.NewOLTRepo(), + um.geocoder, + ) } func (um *usecaseManager) NewNearestDeviceUsecase() usecase.NearestDeviceUseCase { @@ -64,7 +65,7 @@ func (um *usecaseManager) NewAuthUsecase() usecase.AuthUsecase { } func (um *usecaseManager) NewDeviceUsecase() usecase.DeviceUseCase { - return usecase.NewDeviceUseCase(um.repo.NewDeviceRepository(),um.geocoder) + return usecase.NewDeviceUseCase(um.repo.NewDeviceRepository(), um.repo.NewOLTRepo(), um.geocoder) } func (um *usecaseManager) NewBackboneUsecase() usecase.BackboneUseCase { diff --git a/model/dto/req/deviceDetails.go b/model/dto/req/deviceDetails.go index 245d5c7..a941e3a 100644 --- a/model/dto/req/deviceDetails.go +++ b/model/dto/req/deviceDetails.go @@ -18,6 +18,7 @@ type DeviceDetailsDTO struct { City *string `json:"city,omitempty" validate:"omitempty,min=3"` District *string `json:"district,omitempty" validate:"omitempty,min=3"` TowerID *uuid.UUID `json:"tower_id,omitempty"` + OLTID *uuid.UUID `json:"olt_id,omitempty"` } type UpdateDeviceDetailsDTO struct { @@ -32,6 +33,7 @@ type UpdateDeviceDetailsDTO struct { City *string `json:"city,omitempty" validate:"omitempty,min=3"` District *string `json:"district,omitempty" validate:"omitempty,min=3"` TowerID *uuid.UUID `json:"tower_id,omitempty"` + OLTID *uuid.UUID `json:"olt_id,omitempty"` } type AssignMultipleCustomersDTO struct { diff --git a/model/dto/req/device_dto.go b/model/dto/req/device_dto.go index c98096f..4df8c56 100644 --- a/model/dto/req/device_dto.go +++ b/model/dto/req/device_dto.go @@ -13,6 +13,7 @@ type DeviceDTO struct { City *string `json:"city,omitempty" validate:"omitempty,min=3"` District *string `json:"district,omitempty" validate:"omitempty,min=3"` TowerID *uuid.UUID `json:"tower_id,omitempty"` + OLTID *uuid.UUID `json:"olt_id,omitempty"` } type UpdateDeviceDTO struct { @@ -26,6 +27,7 @@ type UpdateDeviceDTO struct { City *string `json:"city,omitempty" validate:"omitempty,min=3"` District *string `json:"district,omitempty" validate:"omitempty,min=3"` TowerID *uuid.UUID `json:"tower_id,omitempty"` + OLTID *uuid.UUID `json:"olt_id,omitempty"` } type BulkDeviceImageUploadDTO struct { diff --git a/model/dto/res/device_details.go b/model/dto/res/device_details.go index 9fc486c..4698588 100644 --- a/model/dto/res/device_details.go +++ b/model/dto/res/device_details.go @@ -27,8 +27,7 @@ type DeviceDetailsResponse struct { ImageURL *string `json:"image_url,omitempty"` ImageURLs []string `json:"image_urls"` TowerID *uuid.UUID `json:"tower_id,omitempty"` // Add TowerID - OLTID *uuid.UUID `json:"olt_id"` // Add OLTID - OLTName *string `json:"olt_name"` // Add OLT name + OLT *OLTInfo `json:"olt"` // Add OLT name // Connection details Backbones []BackboneConnectionInfo `json:"backbones"` @@ -39,6 +38,11 @@ type DeviceDetailsResponse struct { UpdatedAt time.Time `json:"updated_at"` } +type OLTInfo struct { + OLTID uuid.UUID `json:"olt_id"` + OLTName string `json:"olt_name"` +} + type TowerConnectionDetail struct { ID uuid.UUID `json:"id"` TowerCode string `json:"tower_code"` diff --git a/usecase/device_details.go b/usecase/device_details.go index 95efa37..156dae2 100644 --- a/usecase/device_details.go +++ b/usecase/device_details.go @@ -39,22 +39,34 @@ type DeviceDetailsUseCase interface { DeleteDeviceImage(deviceID uuid.UUID, filename string) error GetDevicesWithoutTowers(deviceTypes []string) ([]res.DeviceDetailsResponse, error) GetDevicesWithoutConnections(deviceTypes []string) ([]res.DeviceDetailsResponse, error) + ValidateOLTExists(oltID uuid.UUID) (bool, error) } type deviceDetailsUseCase struct { deviceDetailsRepo repository.DeviceDetailsRepo + oltRepo repository.OLTRepo geocoder service.GeocodingService validate *validator.Validate } -func NewDeviceDetailsUseCase(deviceDetailsRepo repository.DeviceDetailsRepo, geocoder service.GeocodingService) DeviceDetailsUseCase { +func NewDeviceDetailsUseCase(deviceDetailsRepo repository.DeviceDetailsRepo, oltRepo repository.OLTRepo,geocoder service.GeocodingService) DeviceDetailsUseCase { return &deviceDetailsUseCase{ deviceDetailsRepo: deviceDetailsRepo, + oltRepo: oltRepo, geocoder: geocoder, validate: validator.New(), } } +func (u *deviceDetailsUseCase) ValidateOLTExists(oltID uuid.UUID) (bool, error) { + _, err := u.oltRepo.GetByID(oltID) + if err != nil { + return false, nil + } + return true, nil +} + + func (u *deviceDetailsUseCase) GetDevicesWithoutConnections(deviceTypes []string) ([]res.DeviceDetailsResponse, error) { // Validate device types and convert to uppercase validTypes := map[string]bool{"CLOSURE": true, "OTB": true} @@ -233,6 +245,45 @@ func (u *deviceDetailsUseCase) UpdateDeviceDetailsWithMultipleImages(id uuid.UUI updates["district"] = *deviceDTO.District } + 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()) + } + updates["tower_id"] = *deviceDTO.TowerID + } + + // Handle OLTID validation and update + if deviceDTO.OLTID!= nil { + // Get current device to check type + currentDevice, err := u.deviceDetailsRepo.GetByID(id) + if err != nil { + return fmt.Errorf("device not found: %w", err) + } + + // Only ODP devices can be assigned to OLT + deviceType := string(currentDevice.DeviceType) + if deviceDTO.DeviceType != nil { + deviceType = *deviceDTO.DeviceType + } + + if deviceType != "ODP" { + return fmt.Errorf("only ODP devices can be assigned to OLT") + } + + oltExists, err := u.ValidateOLTExists(*deviceDTO.OLTID) + if err != nil { + return fmt.Errorf("failed to validate OLT: %w", err) + } + if !oltExists { + return fmt.Errorf("OLT with ID %s not found", deviceDTO.OLTID.String()) + } + updates["olt_id"] = *deviceDTO.OLTID + } + // Handle multiple image uploads if len(imageFiles) > 0 { // Get current device to handle existing images diff --git a/usecase/device_usecase.go b/usecase/device_usecase.go index acf98ba..a763304 100644 --- a/usecase/device_usecase.go +++ b/usecase/device_usecase.go @@ -28,22 +28,34 @@ type DeviceUseCase interface { BulkUploadImagesMultiple(devices []req.BulkDeviceImageUploadDTO, imageFiles []*multipart.FileHeader, fileDistribution []int) error ValidateTowerExists(towerID uuid.UUID) (bool, error) + ValidateOLTExists(oltID uuid.UUID) (bool, error) + } type deviceUseCase struct { deviceRepo repository.DevicesRepo + oltRepo repository.OLTRepo validate *validator.Validate geocoder service.GeocodingService } -func NewDeviceUseCase(deviceRepo repository.DevicesRepo, geocoder service.GeocodingService) DeviceUseCase { +func NewDeviceUseCase(deviceRepo repository.DevicesRepo,oltRepo repository.OLTRepo ,geocoder service.GeocodingService) DeviceUseCase { return &deviceUseCase{ deviceRepo: deviceRepo, + oltRepo: oltRepo, geocoder: geocoder, validate: validator.New(), } } +func (u *deviceUseCase) ValidateOLTExists(oltID uuid.UUID) (bool, error) { + _, err := u.oltRepo.GetByID(oltID) + if err != nil { + return false, nil + } + return true, nil +} + func (u *deviceUseCase) ValidateTowerExists(towerID uuid.UUID) (bool, error) { return u.deviceRepo.ValidateTowerExists(towerID) } @@ -60,6 +72,32 @@ func (u *deviceUseCase) CreateDeviceWithMultipleImages(device req.DeviceDTO, ima } } + 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()) + } + } + + if device.OLTID != nil { + // Only ODP devices can be assigned to OLT + if device.DeviceType != "ODP" { + return fmt.Errorf("only ODP devices can be assigned to OLT") + } + + oltExists, err := u.ValidateOLTExists(*device.OLTID) + if err != nil { + return fmt.Errorf("failed to validate OLT: %w", err) + } + if !oltExists { + return fmt.Errorf("OLT with ID %s not found", device.OLTID.String()) + } + } + + var imageURLs []string var primaryImageURL string @@ -88,6 +126,7 @@ func (u *deviceUseCase) CreateDeviceWithMultipleImages(device req.DeviceDTO, ima City: device.City, District: device.District, TowerID: device.TowerID, // Add TowerID field + OLTID: device.OLTID, ImageURL: &primaryImageURL, // Primary image as pointer ImageURLs: entity.StringSlice(imageURLs), // All images CreatedAt: time.Now(), @@ -145,6 +184,34 @@ func (u *deviceUseCase) UpdateDeviceWithMultipleImages(id uuid.UUID, device req. updates["TowerID"] = *device.TowerID } + // Handle OLTID validation and update + if device.OLTID != nil { + // Get current device to check type + currentDevice, err := u.deviceRepo.GetByID(id) + if err != nil { + return fmt.Errorf("device not found: %w", err) + } + + // Only ODP devices can be assigned to OLT + deviceType := string(currentDevice.DeviceType) + if device.DeviceType != nil { + deviceType = *device.DeviceType + } + + if deviceType != "ODP" { + return fmt.Errorf("only ODP devices can be assigned to OLT") + } + + oltExists, err := u.ValidateOLTExists(*device.OLTID) + if err != nil { + return fmt.Errorf("failed to validate OLT: %w", err) + } + if !oltExists { + return fmt.Errorf("OLT with ID %s not found", device.OLTID.String()) + } + updates["OLTID"] = *device.OLTID + } + // Handle multiple image uploads if len(imageFiles) > 0 { // Get current device to handle existing images @@ -326,6 +393,36 @@ func (u *deviceUseCase) UpdateDevice(id uuid.UUID, device req.UpdateDeviceDTO) e } updates["TowerID"] = *device.TowerID } + + + // Handle OLTID validation and update + if device.OLTID != nil { + // Get current device to check type + currentDevice, err := u.deviceRepo.GetByID(id) + if err != nil { + return fmt.Errorf("device not found: %w", err) + } + + // Only ODP devices can be assigned to OLT + deviceType := string(currentDevice.DeviceType) + if device.DeviceType != nil { + deviceType = *device.DeviceType + } + + if deviceType != "ODP" { + return fmt.Errorf("only ODP devices can be assigned to OLT") + } + + oltExists, err := u.ValidateOLTExists(*device.OLTID) + if err != nil { + return fmt.Errorf("failed to validate OLT: %w", err) + } + if !oltExists { + return fmt.Errorf("OLT with ID %s not found", device.OLTID.String()) + } + updates["OLTID"] = *device.OLTID + } + 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") } diff --git a/utils/helper/device_details.go b/utils/helper/device_details.go index b459f73..77d31d6 100644 --- a/utils/helper/device_details.go +++ b/utils/helper/device_details.go @@ -199,11 +199,13 @@ func ConvertToDeviceDetailsResponse(device entity.DeviceDetails, geocoder servic if len(allImageURLs) == 0 { allImageURLs = []string{} // Return empty array instead of nil } - - // Handle OLT information - var oltName *string - if device.OLT != nil && device.OLT.OLTName != "" { - oltName = &device.OLT.OLTName + // Handle OLT information - create nested object structure + var oltInfo *res.OLTInfo + if device.OLTID != nil && device.OLT != nil && device.OLT.OLTName != "" { + oltInfo = &res.OLTInfo{ + OLTID: *device.OLTID, + OLTName: device.OLT.OLTName, + } } response := res.DeviceDetailsResponse{ @@ -226,8 +228,7 @@ func ConvertToDeviceDetailsResponse(device entity.DeviceDetails, geocoder servic ImageURLs: allImageURLs, TowerID: device.TowerID, // Keep TowerID for reference // Remove AssignedTower field - now everything is in Towers - OLTID: device.OLTID, // Add OLTID - OLTName: oltName, // Add OLT name + OLT: oltInfo, // Add OLT name Backbones: backboneInfos, Fishbones: fishboneInfos, Towers: towerInfos, // All towers combined here