414 lines
13 KiB
Go
414 lines
13 KiB
Go
package usecase
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"log"
|
|
"strconv"
|
|
"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 DeviceInspectionUseCase interface {
|
|
CreateInspection(userID uuid.UUID, inspection req.DeviceInspectionDTO) error
|
|
GetAllInspections(page, limit int) ([]res.DeviceInspectionResponse, int64, error)
|
|
GetUserInspections(userID uuid.UUID, page, limit int) ([]res.DeviceInspectionResponse, int64, error)
|
|
GetInspectionByID(id uuid.UUID) (res.DeviceInspectionDetailResponse, error)
|
|
|
|
|
|
UpdateInspection(id, userID uuid.UUID, inspection req.UpdateDeviceInspectionDTO, userRole string) error
|
|
ApproveInspection(id uuid.UUID, approval req.ApproveInspectionDTO) error
|
|
CheckInspectionOwnership(inspectionID, userID uuid.UUID) (bool, error) // Add this line
|
|
}
|
|
|
|
type deviceInspectionUseCase struct {
|
|
inspectionRepo repository.DeviceInspectionRepo
|
|
activityLogUC ActivityLogUseCase
|
|
geoService service.GeocodingService
|
|
validate *validator.Validate
|
|
}
|
|
|
|
func NewDeviceInspectionUseCase(inspectionRepo repository.DeviceInspectionRepo, activityLogUC ActivityLogUseCase, geoService service.GeocodingService) DeviceInspectionUseCase {
|
|
return &deviceInspectionUseCase{
|
|
inspectionRepo: inspectionRepo,
|
|
activityLogUC: activityLogUC,
|
|
geoService: geoService,
|
|
validate: validator.New(),
|
|
}
|
|
}
|
|
|
|
|
|
func (u *deviceInspectionUseCase) CheckInspectionOwnership(inspectionID, userID uuid.UUID) (bool, error) {
|
|
return u.inspectionRepo.CheckOwnership(inspectionID, userID)
|
|
}
|
|
|
|
func (u *deviceInspectionUseCase) CreateInspection(userID uuid.UUID, inspection req.DeviceInspectionDTO) error {
|
|
err := u.validate.Struct(inspection)
|
|
if err != nil {
|
|
return fmt.Errorf("validation error: %w", err)
|
|
}
|
|
|
|
// Check if device exists
|
|
deviceExists, err := u.inspectionRepo.CheckDeviceExists(inspection.DeviceID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !deviceExists {
|
|
return errors.New("device not found")
|
|
}
|
|
|
|
// Get device details
|
|
device, err := u.inspectionRepo.GetDeviceByID(inspection.DeviceID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Validate device type constraints
|
|
if device.DeviceType == "OTB" && inspection.BackboneID == nil {
|
|
return errors.New("backbone is required for OTB device type")
|
|
}
|
|
if device.DeviceType == "ODP" && inspection.FishboneID == nil {
|
|
return errors.New("fishbone is required for ODP device type")
|
|
}
|
|
|
|
// Validate backbone exists if provided
|
|
if inspection.BackboneID != nil {
|
|
backboneExists, err := u.inspectionRepo.CheckBackboneExists(*inspection.BackboneID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !backboneExists {
|
|
return errors.New("backbone not found")
|
|
}
|
|
}
|
|
|
|
// Validate fishbone exists if provided
|
|
if inspection.FishboneID != nil {
|
|
fishboneExists, err := u.inspectionRepo.CheckFishboneExists(*inspection.FishboneID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !fishboneExists {
|
|
return errors.New("fishbone not found")
|
|
}
|
|
}
|
|
|
|
// Validate tower exists if provided
|
|
if inspection.TowerID != nil {
|
|
towerExists, err := u.inspectionRepo.CheckTowerExists(*inspection.TowerID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !towerExists {
|
|
return errors.New("tower not found")
|
|
}
|
|
}
|
|
|
|
// Check port availability
|
|
portUsedInt, err := strconv.Atoi(inspection.PortUsed)
|
|
if err != nil {
|
|
return errors.New("invalid port_used value")
|
|
}
|
|
|
|
// Get current device port usage
|
|
devicePort, err := u.inspectionRepo.GetDevicePortByDeviceID(inspection.DeviceID)
|
|
if err != nil {
|
|
// If device port doesn't exist, create it
|
|
return fmt.Errorf("device port not found for device %s: %w", inspection.DeviceID, err)
|
|
}
|
|
// Check if adding this inspection would exceed port capacity
|
|
newPortUsed := devicePort.PortUsed + portUsedInt
|
|
if newPortUsed > device.PortAmount {
|
|
return fmt.Errorf("insufficient ports available. Device has %d ports, currently %d used, requested %d additional",
|
|
device.PortAmount, devicePort.PortUsed, portUsedInt)
|
|
}
|
|
|
|
// Calculate cable amount based on device type
|
|
cableAmount, err := u.inspectionRepo.CountCablesByDevice(inspection.DeviceID, string(device.DeviceType))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Calculate port available
|
|
portAvailable := device.PortAmount - newPortUsed
|
|
|
|
// Get address from coordinates using OpenStreetMap
|
|
longitude := inspection.InspectionPlacement.Longitude
|
|
latitude := inspection.InspectionPlacement.Latitude
|
|
|
|
address, err := u.geoService.GetAddressFromCoordinates(latitude, longitude)
|
|
if err != nil {
|
|
log.Printf("Error getting address from coordinates (%.6f, %.6f): %v", latitude, longitude, err)
|
|
address = fmt.Sprintf("Coordinates: %.6f, %.6f", latitude, longitude) // Fallback to coordinates
|
|
}
|
|
|
|
// Create inspection
|
|
newInspection := entity.DeviceInspection{
|
|
ID: uuid.New(),
|
|
DeviceID: inspection.DeviceID,
|
|
BackboneID: inspection.BackboneID,
|
|
FishboneID: inspection.FishboneID,
|
|
TowerID: inspection.TowerID,
|
|
UserID: userID,
|
|
Status: inspection.Status,
|
|
PortUsed: inspection.PortUsed,
|
|
PortAvailable: strconv.Itoa(portAvailable),
|
|
CableAmount: cableAmount,
|
|
Description: inspection.Description,
|
|
ImageURL: inspection.ImageURL,
|
|
InspectionPlacement: address, // Store the full address from OpenStreetMap
|
|
Longitude: longitude, // Store longitude
|
|
Latitude: latitude, // Store latitude
|
|
InspectionApproval: "pending",
|
|
CreatedAt: time.Now(),
|
|
UpdatedAt: time.Now(),
|
|
}
|
|
|
|
err = u.inspectionRepo.Create(newInspection)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Log activity
|
|
inspectionIDStr := newInspection.ID.String()
|
|
u.activityLogUC.LogActivity(
|
|
userID,
|
|
"CREATE",
|
|
"device_inspection",
|
|
&inspectionIDStr,
|
|
nil,
|
|
fmt.Sprintf("Created device inspection for device %s at %s", device.DeviceCode, address),
|
|
"",
|
|
"",
|
|
"",
|
|
)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (u *deviceInspectionUseCase) UpdateInspection(id, userID uuid.UUID, inspection req.UpdateDeviceInspectionDTO, userRole string) error {
|
|
err := u.validate.Struct(inspection)
|
|
if err != nil {
|
|
return fmt.Errorf("validation error: %w", err)
|
|
}
|
|
|
|
// Get existing inspection
|
|
existingInspection, err := u.inspectionRepo.GetByID(id)
|
|
if err != nil {
|
|
return errors.New("inspection not found")
|
|
}
|
|
|
|
// Check if user owns this inspection (for teknisi)
|
|
if userRole == "Teknisi" && existingInspection.UserID != userID {
|
|
return errors.New("unauthorized: you can only update your own inspections")
|
|
}
|
|
|
|
updates := make(map[string]interface{})
|
|
|
|
if inspection.BackboneID != nil {
|
|
backboneExists, err := u.inspectionRepo.CheckBackboneExists(*inspection.BackboneID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !backboneExists {
|
|
return errors.New("backbone not found")
|
|
}
|
|
updates["backbone_id"] = *inspection.BackboneID
|
|
}
|
|
|
|
if inspection.FishboneID != nil {
|
|
fishboneExists, err := u.inspectionRepo.CheckFishboneExists(*inspection.FishboneID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !fishboneExists {
|
|
return errors.New("fishbone not found")
|
|
}
|
|
updates["fishbone_id"] = *inspection.FishboneID
|
|
}
|
|
|
|
if inspection.TowerID != nil {
|
|
towerExists, err := u.inspectionRepo.CheckTowerExists(*inspection.TowerID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !towerExists {
|
|
return errors.New("tower not found")
|
|
}
|
|
updates["tower_id"] = *inspection.TowerID
|
|
}
|
|
|
|
if inspection.Status != nil {
|
|
updates["status"] = *inspection.Status
|
|
}
|
|
|
|
if inspection.PortUsed != nil {
|
|
updates["port_used"] = *inspection.PortUsed
|
|
}
|
|
|
|
if inspection.Description != nil {
|
|
updates["description"] = *inspection.Description
|
|
}
|
|
|
|
if inspection.ImageURL != nil {
|
|
updates["image_url"] = *inspection.ImageURL
|
|
}
|
|
|
|
// Handle location update
|
|
if inspection.InspectionPlacement != nil {
|
|
longitude := inspection.InspectionPlacement.Longitude
|
|
latitude := inspection.InspectionPlacement.Latitude
|
|
|
|
// Get new address from coordinates
|
|
address, err := u.geoService.GetAddressFromCoordinates(latitude, longitude)
|
|
if err != nil {
|
|
log.Printf("Error getting address from coordinates (%.6f, %.6f): %v", latitude, longitude, err)
|
|
address = fmt.Sprintf("Coordinates: %.6f, %.6f", latitude, longitude) // Fallback to coordinates
|
|
}
|
|
|
|
updates["inspection_placement"] = address
|
|
updates["longitude"] = longitude
|
|
updates["latitude"] = latitude
|
|
}
|
|
|
|
if len(updates) == 0 {
|
|
return errors.New("no fields to update")
|
|
}
|
|
|
|
updates["updated_at"] = time.Now()
|
|
|
|
return u.inspectionRepo.Update(id, updates)
|
|
}
|
|
|
|
// Keep the rest of the methods unchanged...
|
|
func (u *deviceInspectionUseCase) GetAllInspections(page, limit int) ([]res.DeviceInspectionResponse, int64, error) {
|
|
offset := (page - 1) * limit
|
|
|
|
inspections, err := u.inspectionRepo.GetAll()
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
total, err := u.inspectionRepo.CountAll()
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
// Apply pagination manually since we need all data for processing
|
|
start := offset
|
|
end := offset + limit
|
|
if start > len(inspections) {
|
|
start = len(inspections)
|
|
}
|
|
if end > len(inspections) {
|
|
end = len(inspections)
|
|
}
|
|
|
|
paginatedInspections := inspections[start:end]
|
|
responses := helper.ConvertToDeviceInspectionResponses(paginatedInspections)
|
|
|
|
return responses, total, nil
|
|
}
|
|
|
|
func (u *deviceInspectionUseCase) GetUserInspections(userID uuid.UUID, page, limit int) ([]res.DeviceInspectionResponse, int64, error) {
|
|
offset := (page - 1) * limit
|
|
|
|
inspections, err := u.inspectionRepo.GetByUserID(userID, limit, offset)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
total, err := u.inspectionRepo.CountByUserID(userID)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
responses := helper.ConvertToDeviceInspectionResponses(inspections)
|
|
|
|
return responses, total, nil
|
|
}
|
|
|
|
func (u *deviceInspectionUseCase) GetInspectionByID(id uuid.UUID) (res.DeviceInspectionDetailResponse, error) {
|
|
inspection, err := u.inspectionRepo.GetByID(id)
|
|
if err != nil {
|
|
return res.DeviceInspectionDetailResponse{}, err
|
|
}
|
|
|
|
response := helper.ConvertToDeviceInspectionDetailResponse(inspection)
|
|
return response, nil
|
|
}
|
|
|
|
func (u *deviceInspectionUseCase) ApproveInspection(id uuid.UUID, approval req.ApproveInspectionDTO) error {
|
|
err := u.validate.Struct(approval)
|
|
if err != nil {
|
|
return fmt.Errorf("validation error: %w", err)
|
|
}
|
|
|
|
// Get inspection details
|
|
inspection, err := u.inspectionRepo.GetByID(id)
|
|
if err != nil {
|
|
return errors.New("inspection not found")
|
|
}
|
|
|
|
updates := map[string]interface{}{
|
|
"inspection_approval": approval.InspectionApproval,
|
|
"updated_at": time.Now(),
|
|
}
|
|
|
|
// Update inspection
|
|
err = u.inspectionRepo.Update(id, updates)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// If approved, update device port and device status
|
|
if approval.InspectionApproval == "approved" {
|
|
// Update device status
|
|
err = u.inspectionRepo.UpdateDeviceStatus(inspection.DeviceID, inspection.Status)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Update device port usage
|
|
portUsedInt, _ := strconv.Atoi(inspection.PortUsed)
|
|
|
|
devicePort, err := u.inspectionRepo.GetDevicePortByDeviceID(inspection.DeviceID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
device, err := u.inspectionRepo.GetDeviceByID(inspection.DeviceID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
newPortUsed := devicePort.PortUsed + portUsedInt
|
|
newPortAvailable := device.PortAmount - newPortUsed
|
|
|
|
portUpdates := map[string]interface{}{
|
|
"port_used": newPortUsed,
|
|
"port_available": newPortAvailable,
|
|
"updated_at": time.Now(),
|
|
}
|
|
|
|
// For OTB devices, also update backbone_id
|
|
if device.DeviceType == "OTB" && inspection.BackboneID != nil {
|
|
portUpdates["backbone_id"] = *inspection.BackboneID
|
|
}
|
|
|
|
err = u.inspectionRepo.UpdateDevicePort(inspection.DeviceID, portUpdates)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
} |