351 lines
11 KiB
Go
351 lines
11 KiB
Go
package usecase
|
|
|
|
import (
|
|
"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 DeviceUseCase interface {
|
|
CreateDevice(device req.DeviceDTO) error
|
|
GetAllDevices() ([]res.DeviceResponse, error)
|
|
|
|
GetByID(id uuid.UUID) (res.DeviceResponse, error)
|
|
UpdateDevice(id uuid.UUID, device req.UpdateDeviceDTO) error
|
|
GetByType(deviceType string) ([]res.DeviceTypeResponse, error)
|
|
BulkUploadImages(devices []req.BulkDeviceImageUploadDTO, 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
|
|
BulkUploadImagesMultiple(devices []req.BulkDeviceImageUploadDTO, imageFiles []*multipart.FileHeader, fileDistribution []int) error
|
|
|
|
ValidateTowerExists(towerID uuid.UUID) (bool, error)
|
|
}
|
|
|
|
type deviceUseCase struct {
|
|
deviceRepo repository.DevicesRepo
|
|
validate *validator.Validate
|
|
geocoder service.GeocodingService
|
|
}
|
|
|
|
func NewDeviceUseCase(deviceRepo repository.DevicesRepo, geocoder service.GeocodingService) DeviceUseCase {
|
|
return &deviceUseCase{
|
|
deviceRepo: deviceRepo,
|
|
geocoder: geocoder,
|
|
validate: validator.New(),
|
|
}
|
|
}
|
|
|
|
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 {
|
|
err := u.validate.Struct(device)
|
|
if err != nil {
|
|
return fmt.Errorf("validation error: %w", err)
|
|
}
|
|
|
|
if device.DeviceType == "OTB" || device.DeviceType == "ODP" {
|
|
if device.PortAmount <= 0 {
|
|
return fmt.Errorf("port amount must be greater than 0 for OTB or ODP devices")
|
|
}
|
|
}
|
|
|
|
var imageURLs []string
|
|
var primaryImageURL string
|
|
|
|
if len(imageFiles) > 0 {
|
|
// Save all images
|
|
imageURLs, err = helper.SaveDeviceImagesBulk(imageFiles)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// First image becomes primary
|
|
if len(imageURLs) > 0 && imageURLs[0] != "" {
|
|
primaryImageURL = imageURLs[0]
|
|
}
|
|
}
|
|
|
|
newDevice := entity.Device{
|
|
ID: uuid.New(),
|
|
DeviceCode: device.DeviceCode,
|
|
DeviceType: entity.DeviceType(device.DeviceType),
|
|
Longitude: device.Longitude,
|
|
Latitude: device.Latitude,
|
|
PortAmount: device.PortAmount,
|
|
Status: entity.DeviceStatus(device.Status),
|
|
Province: device.Province,
|
|
City: device.City,
|
|
District: device.District,
|
|
TowerID: device.TowerID, // Add TowerID field
|
|
ImageURL: &primaryImageURL, // Primary image as pointer
|
|
ImageURLs: entity.StringSlice(imageURLs), // All images
|
|
CreatedAt: time.Now(),
|
|
UpdatedAt: time.Now(),
|
|
}
|
|
|
|
return u.deviceRepo.Post(newDevice)
|
|
}
|
|
|
|
func (u *deviceUseCase) UpdateDeviceWithMultipleImages(id uuid.UUID, device req.UpdateDeviceDTO, imageFiles []*multipart.FileHeader, replaceImages ...bool) error {
|
|
err := u.validate.Struct(device)
|
|
if err != nil {
|
|
return fmt.Errorf("validation error: %w", err)
|
|
}
|
|
|
|
updates := map[string]interface{}{}
|
|
|
|
if device.DeviceCode != nil {
|
|
updates["DeviceCode"] = *device.DeviceCode
|
|
}
|
|
if device.DeviceType != nil {
|
|
updates["DeviceType"] = *device.DeviceType
|
|
}
|
|
if device.Longitude != nil {
|
|
updates["Longitude"] = *device.Longitude
|
|
}
|
|
if device.Latitude != nil {
|
|
updates["Latitude"] = *device.Latitude
|
|
}
|
|
if device.PortAmount != nil {
|
|
updates["PortAmount"] = *device.PortAmount
|
|
}
|
|
if device.Status != nil {
|
|
updates["Status"] = *device.Status
|
|
}
|
|
if device.Province != nil {
|
|
updates["Province"] = *device.Province
|
|
}
|
|
if device.City != nil {
|
|
updates["City"] = *device.City
|
|
}
|
|
if device.District != nil {
|
|
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
|
|
if len(imageFiles) > 0 {
|
|
// Get current device to handle existing images
|
|
currentDevice, err := u.deviceRepo.GetByID(id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Save new images
|
|
newImageURLs, err := helper.SaveDeviceImagesBulk(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 currentDevice.GetAllImageURLs() {
|
|
helper.DeleteDeviceImage(oldImageURL)
|
|
}
|
|
finalImageURLs = newImageURLs
|
|
} else {
|
|
// Append to existing images
|
|
existingImages := currentDevice.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 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")
|
|
}
|
|
|
|
if len(updates) == 0 {
|
|
return fmt.Errorf("no update data")
|
|
}
|
|
|
|
updates["UpdatedAt"] = time.Now()
|
|
|
|
return u.deviceRepo.Update(id, updates)
|
|
}
|
|
func (u *deviceUseCase) BulkUploadImagesMultiple(devices []req.BulkDeviceImageUploadDTO, imageFiles []*multipart.FileHeader, fileDistribution []int) error {
|
|
if len(devices) == 0 {
|
|
return fmt.Errorf("no devices provided")
|
|
}
|
|
|
|
if len(fileDistribution) != len(devices) {
|
|
return fmt.Errorf("file distribution count must match device count")
|
|
}
|
|
|
|
// Validate all device IDs exist
|
|
for i, device := range devices {
|
|
_, err := u.deviceRepo.GetByID(device.DeviceID)
|
|
if err != nil {
|
|
return fmt.Errorf("device %d with ID %s not found", i, device.DeviceID)
|
|
}
|
|
}
|
|
|
|
// Save all images
|
|
imageURLs, err := helper.SaveDeviceImagesBulk(imageFiles)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to save images: %w", err)
|
|
}
|
|
|
|
// Distribute images to devices
|
|
updates := make(map[uuid.UUID][]string)
|
|
fileIndex := 0
|
|
|
|
for i, device := range devices {
|
|
imageCount := fileDistribution[i]
|
|
deviceImages := make([]string, 0)
|
|
|
|
for j := 0; j < imageCount && fileIndex < len(imageURLs); j++ {
|
|
if imageURLs[fileIndex] != "" {
|
|
deviceImages = append(deviceImages, imageURLs[fileIndex])
|
|
}
|
|
fileIndex++
|
|
}
|
|
|
|
if len(deviceImages) > 0 {
|
|
updates[device.DeviceID] = deviceImages
|
|
}
|
|
}
|
|
|
|
// Update database
|
|
return u.deviceRepo.BulkUpdateImagesMultiple(updates)
|
|
}
|
|
|
|
// Keep old methods for backward compatibility
|
|
func (u *deviceUseCase) CreateDevice(device req.DeviceDTO) error {
|
|
return u.CreateDeviceWithMultipleImages(device, nil)
|
|
}
|
|
|
|
func (u *deviceUseCase) GetAllDevices() ([]res.DeviceResponse, error) {
|
|
devices, err := u.deviceRepo.GetAll()
|
|
if err != nil {
|
|
return []res.DeviceResponse{}, err
|
|
}
|
|
devicesResponse,err := helper.ConvertToDeviceResponse(devices, u.geocoder)
|
|
if err != nil {
|
|
return []res.DeviceResponse{}, err
|
|
}
|
|
return devicesResponse, nil
|
|
}
|
|
|
|
func (u *deviceUseCase) BulkUploadImages(devices []req.BulkDeviceImageUploadDTO, imageFiles []*multipart.FileHeader) error {
|
|
// Default to one image per device
|
|
fileDistribution := make([]int, len(devices))
|
|
for i := range fileDistribution {
|
|
fileDistribution[i] = 1
|
|
}
|
|
return u.BulkUploadImagesMultiple(devices, imageFiles, fileDistribution)
|
|
}
|
|
|
|
func (u *deviceUseCase) GetByID(id uuid.UUID) (res.DeviceResponse, error) {
|
|
device, err := u.deviceRepo.GetByID(id)
|
|
if err != nil {
|
|
return res.DeviceResponse{}, err
|
|
}
|
|
|
|
deviceResp, err := helper.ConvertToDeviceResponseId(device, u.geocoder)
|
|
if err != nil {
|
|
return res.DeviceResponse{}, err
|
|
}
|
|
return deviceResp, nil
|
|
}
|
|
|
|
func (u *deviceUseCase) UpdateDevice(id uuid.UUID, device req.UpdateDeviceDTO) error {
|
|
err := u.validate.Struct(device)
|
|
if err != nil {
|
|
return fmt.Errorf("validation error: %w", err)
|
|
}
|
|
updates := map[string]interface{}{}
|
|
|
|
if device.DeviceCode != nil {
|
|
updates["DeviceCode"] = *device.DeviceCode
|
|
}
|
|
if device.DeviceType != nil {
|
|
updates["DeviceType"] = *device.DeviceType
|
|
}
|
|
if device.Longitude != nil {
|
|
updates["Longitude"] = *device.Longitude
|
|
}
|
|
if device.Latitude != nil {
|
|
updates["Latitude"] = *device.Latitude
|
|
}
|
|
if device.PortAmount != nil {
|
|
updates["PortAmount"] = *device.PortAmount
|
|
}
|
|
if device.Status != nil {
|
|
updates["Status"] = *device.Status
|
|
}
|
|
if device.Province != nil {
|
|
updates["Province"] = *device.Province
|
|
}
|
|
if device.City != nil {
|
|
updates["City"] = *device.City
|
|
}
|
|
if device.District != nil {
|
|
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 {
|
|
return fmt.Errorf("port amount must be greater than 0 for OTB or ODP devices")
|
|
}
|
|
|
|
if len(updates) == 0 {
|
|
return fmt.Errorf("no update data")
|
|
}
|
|
|
|
updates["UpdatedAt"] = time.Now()
|
|
|
|
|
|
|
|
return u.deviceRepo.Update(id, updates)
|
|
}
|
|
|
|
func (u *deviceUseCase) GetByType(deviceType string) ([]res.DeviceTypeResponse, error) {
|
|
devices, err := u.deviceRepo.GetByType(deviceType)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
deviceTypeResponses := helper.ConvertToDeviceTypeResponse(devices)
|
|
return deviceTypeResponses, nil
|
|
} |