package usecase import ( "errors" "fmt" "mime/multipart" "time" "users_management/m/model/dto/req" "users_management/m/model/dto/res" "users_management/m/model/entity" "users_management/m/repository" "users_management/m/utils/helper" "users_management/m/utils/service" "github.com/go-playground/validator/v10" "github.com/google/uuid" ) type TowerUseCase interface { Post(tower req.TowerDTO, imageFile *multipart.FileHeader) error GetAll() ([]res.TowerResponse, error) GetByID(id uuid.UUID) (res.TowerResponse, error) UpdateTower(id uuid.UUID, tower req.UpdateTowerDTO, imageFile *multipart.FileHeader) error BulkUploadImages(towers []req.BulkTowerImageUploadDTO, imageFiles []*multipart.FileHeader) error PostWithMultipleImages(tower req.TowerDTO, imageFiles []*multipart.FileHeader) error UpdateTowerWithMultipleImages(id uuid.UUID, tower req.UpdateTowerDTO, imageFiles []*multipart.FileHeader, replaceImages ...bool) error } type towerUsecase struct { towerRepo repository.TowerRepo validate *validator.Validate geocoder service.GeocodingService } func NewTowerUseCase(towerRepo repository.TowerRepo, geocoder service.GeocodingService) TowerUseCase { return &towerUsecase{ towerRepo: towerRepo, geocoder: geocoder, validate: validator.New(), } } func (u *towerUsecase) PostWithMultipleImages(tower req.TowerDTO, imageFiles []*multipart.FileHeader) error { err := u.validate.Struct(tower) if err != nil { return err } // Validate that if it's not an external tower, DeviceID must be provided if tower.ExternalTower != nil && !*tower.ExternalTower && tower.DeviceID == nil { return fmt.Errorf("device_id is required for internal towers") } // Validate that if DeviceID is provided, the device exists if tower.DeviceID != nil { deviceExists, err := u.towerRepo.CheckDeviceExists(*tower.DeviceID) if err != nil { return err } if !deviceExists { return fmt.Errorf("device not found") } } var imageURLs []string var primaryImageURL string if len(imageFiles) > 0 { // Save all images imageURLs, err = helper.SaveTowerImagesBulk(imageFiles) if err != nil { return err } // First image becomes primary if len(imageURLs) > 0 && imageURLs[0] != "" { primaryImageURL = imageURLs[0] } } newTower := entity.Tower{ ID: uuid.New(), DeviceID: tower.DeviceID, TowerCode: tower.TowerCode, Longitude: tower.Longitude, Latitude: tower.Latitude, ImageURL: primaryImageURL, // Primary image ImageURLs: entity.StringSlice(imageURLs), // All images ExternalTower: tower.ExternalTower, CreatedAt: time.Now(), UpdatedAt: time.Now(), } return u.towerRepo.Post(newTower) } func (u *towerUsecase) UpdateTowerWithMultipleImages(id uuid.UUID, tower req.UpdateTowerDTO, imageFiles []*multipart.FileHeader, replaceImages ...bool) error { err := u.validate.Struct(tower) if err != nil { return err } updates := make(map[string]interface{}) if tower.DeviceID != nil { updates["DeviceID"] = *tower.DeviceID } if tower.TowerCode != nil { updates["TowerCode"] = *tower.TowerCode } if tower.Longitude != nil { updates["Longitude"] = *tower.Longitude } if tower.Latitude != nil { updates["Latitude"] = *tower.Latitude } if tower.ExternalTower != nil { updates["ExternalTower"] = *tower.ExternalTower } if tower.ImageURL != nil { updates["ImageURL"] = *tower.ImageURL } // Handle multiple image uploads if len(imageFiles) > 0 { // Get current tower to handle existing images currentTower, err := u.towerRepo.GetByID(id) if err != nil { return err } // Save new images newImageURLs, err := helper.SaveTowerImagesBulk(imageFiles) if err != nil { return err } var finalImageURLs []string shouldReplace := len(replaceImages) > 0 && replaceImages[0] if shouldReplace { // Replace all images - delete old ones for _, oldImageURL := range currentTower.GetAllImageURLs() { helper.DeleteTowerImage(oldImageURL) } finalImageURLs = newImageURLs } else { // Append to existing images existingImages := currentTower.GetAllImageURLs() finalImageURLs = append(existingImages, newImageURLs...) } // Update primary image (first image in the final list) if len(finalImageURLs) > 0 && finalImageURLs[0] != "" { updates["ImageURL"] = finalImageURLs[0] } // Update all images updates["ImageURLs"] = entity.StringSlice(finalImageURLs) } // If no fields are updated, return an error if len(updates) == 0 { return errors.New("no fields to update") } updates["UpdatedAt"] = time.Now() return u.towerRepo.Update(id, updates) } func (u *towerUsecase) Post(tower req.TowerDTO, imageFile *multipart.FileHeader) error { err := u.validate.Struct(tower) if err != nil { return err } // Validate that if it's not an external tower, DeviceID must be provided if tower.ExternalTower != nil && !*tower.ExternalTower && tower.DeviceID == nil { return fmt.Errorf("device_id is required for internal towers") } // Validate that if DeviceID is provided, the device exists if tower.DeviceID != nil { deviceExists, err := u.towerRepo.CheckDeviceExists(*tower.DeviceID) if err != nil { return err } if !deviceExists { return fmt.Errorf("device not found") } } var imageURL string if imageFile != nil { imageURL, err = helper.SaveTowerImage(imageFile) if err != nil { return err } } 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(), } return u.towerRepo.Post(newTower) } func (u *towerUsecase) GetAll() ([]res.TowerResponse, error) { towers, err := u.towerRepo.GetAll() if err != nil { return nil, err } towerResp, err := helper.ConvertToTowerResponses(towers,u.geocoder) if err != nil { return nil, err } return towerResp, nil } func (u *towerUsecase) GetByID(id uuid.UUID) (res.TowerResponse, error) { tower, err := u.towerRepo.GetByID(id) if err != nil { return res.TowerResponse{}, err } towerResp, err := helper.ConvertToTowerIDResponses(tower,u.geocoder) if err != nil { return res.TowerResponse{}, err } return towerResp, nil } func (u *towerUsecase) BulkUploadImages(towers []req.BulkTowerImageUploadDTO, imageFiles []*multipart.FileHeader) error { // Validate input if len(towers) != len(imageFiles) { return fmt.Errorf("number of towers (%d) must match number of image files (%d)", len(towers), len(imageFiles)) } if len(towers) == 0 { return fmt.Errorf("no towers provided") } // Validate all tower IDs exist for i, tower := range towers { _, err := u.towerRepo.GetByID(tower.TowerID) if err != nil { return fmt.Errorf("tower %d with ID %s not found", i, tower.TowerID) } } // Save all images imageURLs, err := helper.SaveTowerImagesBulk(imageFiles) if err != nil { return fmt.Errorf("failed to save images: %w", err) } // Create update map updates := make(map[uuid.UUID]string) for i, tower := range towers { if i < len(imageURLs) && imageURLs[i] != "" { updates[tower.TowerID] = imageURLs[i] } } // Update database return u.towerRepo.BulkUpdateImages(updates) } func (u *towerUsecase) UpdateTower(id uuid.UUID, tower req.UpdateTowerDTO, imageFile *multipart.FileHeader) error { err := u.validate.Struct(tower) if err != nil { return err // Return validation error } updates := make(map[string]interface{}) if tower.DeviceID != nil { updates["DeviceID"] = *tower.DeviceID } if tower.TowerCode != nil { updates["TowerCode"] = *tower.TowerCode } if tower.Longitude != nil { updates["Longitude"] = *tower.Longitude } if tower.Latitude != nil { updates["Latitude"] = *tower.Latitude } if tower.ExternalTower != nil { updates["ExternalTower"] = *tower.ExternalTower } // Handle image upload if imageFile != nil { // Get current tower to delete old image currentTower, err := u.towerRepo.GetByID(id) if err == nil && currentTower.ImageURL != "" { // Delete old image helper.DeleteTowerImage(currentTower.ImageURL) } // Save new image imageURL, err := helper.SaveTowerImage(imageFile) if err != nil { return err } updates["ImageURL"] = imageURL } // If no fields are updated, return an error if len(updates) == 0 { return errors.New("no fields to update") } updates["UpdatedAt"] = time.Now() return u.towerRepo.Update(id, updates) }