NAM-APJATEL-BACKEND/usecase/device_usecase.go

321 lines
9.7 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
}
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) 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,
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 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
}
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
}