NAM-APJATEL-BACKEND/usecase/tower_usecase.go

345 lines
10 KiB
Go

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 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
}
// 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 {
updates["Longitude"] = *tower.Longitude
}
if tower.Latitude != nil {
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 {
updates["ExternalTower"] = *tower.ExternalTower
}
// Handle image upload
if imageFile != nil {
// Get current tower to delete old image
if 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)
}