919 lines
35 KiB
Go
919 lines
35 KiB
Go
package repository
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
"users_management/m/model/dto/req"
|
|
"users_management/m/model/entity"
|
|
|
|
"github.com/google/uuid"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
type DeviceDetailsRepo interface {
|
|
Create(device entity.Device) error
|
|
GetAll() ([]entity.DeviceDetails, error)
|
|
GetByID(id uuid.UUID) (entity.DeviceDetails, error)
|
|
Update(id uuid.UUID, updates map[string]interface{}) error
|
|
Delete(id uuid.UUID) error
|
|
|
|
// Port management
|
|
UpdateDevicePortUsage(deviceID uuid.UUID) error
|
|
// ValidatePortAvailability(deviceID uuid.UUID, requiredPorts int) error
|
|
|
|
// Connection management
|
|
GetBackbonesByDeviceID(deviceID uuid.UUID) ([]entity.Backbone, error)
|
|
GetFishbonesByDeviceID(deviceID uuid.UUID) ([]entity.Fishbone, error)
|
|
GetTowersByDeviceID(deviceID uuid.UUID) ([]entity.Tower, error)
|
|
|
|
// Validation helpers
|
|
GetPortUsageByDevice(deviceID uuid.UUID) (portUsed, portAvailable int, err error)
|
|
AssignCustomerToPort(deviceID uuid.UUID, customerName string, portNumber *int) error
|
|
RemoveCustomerFromPort(deviceID uuid.UUID, customerName string) error
|
|
UpdatePortUsage(deviceID uuid.UUID, portUsed int) error
|
|
UpdatePortAssignments(deviceID uuid.UUID, assignments []req.PortAssignmentDTO) error
|
|
MigrateCustomerNamesToPortAssignments(devicePort *entity.DevicePort, devicePortAmount int) error
|
|
AssignMultipleCustomersToPort(deviceID uuid.UUID, assignments []req.AssignMultipleCustomersDTO) error
|
|
UpdateCustomerByPort(deviceID uuid.UUID, update req.UpdateCustomerByPortDTO) error
|
|
BulkUpdateCustomersByPort(deviceID uuid.UUID, updates []req.UpdateCustomerByPortDTO) error
|
|
RemoveCustomerByPort(deviceID uuid.UUID, portNumber int) error
|
|
|
|
|
|
}
|
|
|
|
type deviceDetailsRepo struct {
|
|
db *gorm.DB
|
|
}
|
|
|
|
func NewDeviceDetailsRepo(db *gorm.DB) DeviceDetailsRepo {
|
|
return &deviceDetailsRepo{
|
|
db: db,
|
|
}
|
|
}
|
|
|
|
func (r *deviceDetailsRepo) UpdateCustomerByPort(deviceID uuid.UUID, update req.UpdateCustomerByPortDTO) error {
|
|
return r.db.Transaction(func(tx *gorm.DB) error {
|
|
// Lock both device and device_port records
|
|
var device entity.Device
|
|
if err := tx.Set("gorm:query_option", "FOR UPDATE").
|
|
Where("id = ?", deviceID).First(&device).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
// Only ODP devices can have customer assignments
|
|
if device.DeviceType != "ODP" {
|
|
return fmt.Errorf("customer assignments can only be updated for ODP devices")
|
|
}
|
|
|
|
var devicePort entity.DevicePort
|
|
if err := tx.Set("gorm:query_option", "FOR UPDATE").
|
|
Where("device_id = ?", deviceID).First(&devicePort).Error; err != nil {
|
|
return fmt.Errorf("device port record not found: %w", err)
|
|
}
|
|
|
|
// Initialize port assignments if empty
|
|
if len(devicePort.PortAssignments) == 0 {
|
|
devicePort.PortAssignments = make(entity.PortAssignments, device.PortAmount)
|
|
for i := 0; i < device.PortAmount; i++ {
|
|
devicePort.PortAssignments[i] = entity.PortAssignment{
|
|
PortNumber: i + 1,
|
|
CustomerName: nil,
|
|
}
|
|
}
|
|
}
|
|
|
|
// Validate port number
|
|
if update.PortNumber > device.PortAmount {
|
|
return fmt.Errorf("port number %d exceeds device capacity (%d)", update.PortNumber, device.PortAmount)
|
|
}
|
|
|
|
portIndex := update.PortNumber - 1
|
|
|
|
// If assigning a new customer name
|
|
if update.NewCustomerName != nil && *update.NewCustomerName != "" {
|
|
// Check if customer name already exists on another port
|
|
for i, assignment := range devicePort.PortAssignments {
|
|
if i != portIndex && assignment.CustomerName != nil && *assignment.CustomerName == *update.NewCustomerName {
|
|
return fmt.Errorf("customer %s is already assigned to port %d", *update.NewCustomerName, i+1)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update the port assignment
|
|
if update.NewCustomerName == nil {
|
|
// Remove customer from port
|
|
devicePort.PortAssignments[portIndex].CustomerName = nil
|
|
} else {
|
|
// Assign/update customer on port
|
|
devicePort.PortAssignments[portIndex].CustomerName = update.NewCustomerName
|
|
}
|
|
|
|
// Update counters and backward compatibility fields
|
|
r.updateDevicePortCounters(&devicePort)
|
|
devicePort.UpdatedAt = time.Now()
|
|
|
|
return tx.Save(&devicePort).Error
|
|
})
|
|
}
|
|
|
|
func (r *deviceDetailsRepo) BulkUpdateCustomersByPort(deviceID uuid.UUID, updates []req.UpdateCustomerByPortDTO) error {
|
|
return r.db.Transaction(func(tx *gorm.DB) error {
|
|
// Lock both device and device_port records
|
|
var device entity.Device
|
|
if err := tx.Set("gorm:query_option", "FOR UPDATE").
|
|
Where("id = ?", deviceID).First(&device).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
// Only ODP devices can have customer assignments
|
|
if device.DeviceType != "ODP" {
|
|
return fmt.Errorf("customer assignments can only be updated for ODP devices")
|
|
}
|
|
|
|
var devicePort entity.DevicePort
|
|
if err := tx.Set("gorm:query_option", "FOR UPDATE").
|
|
Where("device_id = ?", deviceID).First(&devicePort).Error; err != nil {
|
|
return fmt.Errorf("device port record not found: %w", err)
|
|
}
|
|
|
|
// Initialize port assignments if empty
|
|
if len(devicePort.PortAssignments) == 0 {
|
|
devicePort.PortAssignments = make(entity.PortAssignments, device.PortAmount)
|
|
for i := 0; i < device.PortAmount; i++ {
|
|
devicePort.PortAssignments[i] = entity.PortAssignment{
|
|
PortNumber: i + 1,
|
|
CustomerName: nil,
|
|
}
|
|
}
|
|
}
|
|
|
|
// Validate all port numbers first
|
|
for _, update := range updates {
|
|
if update.PortNumber > device.PortAmount {
|
|
return fmt.Errorf("port number %d exceeds device capacity (%d)", update.PortNumber, device.PortAmount)
|
|
}
|
|
}
|
|
|
|
// Create a map of final assignments to validate for duplicates
|
|
finalAssignments := make(map[int]*string)
|
|
|
|
// Start with current assignments
|
|
for i, assignment := range devicePort.PortAssignments {
|
|
finalAssignments[i+1] = assignment.CustomerName
|
|
}
|
|
|
|
// Apply updates
|
|
for _, update := range updates {
|
|
finalAssignments[update.PortNumber] = update.NewCustomerName
|
|
}
|
|
|
|
// Check for duplicate customer names
|
|
customerNames := make(map[string]int) // customer name -> port number
|
|
for portNum, customerName := range finalAssignments {
|
|
if customerName != nil && *customerName != "" {
|
|
if existingPort, exists := customerNames[*customerName]; exists {
|
|
return fmt.Errorf("customer %s would be assigned to both port %d and port %d", *customerName, existingPort, portNum)
|
|
}
|
|
customerNames[*customerName] = portNum
|
|
}
|
|
}
|
|
|
|
// Apply all updates
|
|
for _, update := range updates {
|
|
portIndex := update.PortNumber - 1
|
|
devicePort.PortAssignments[portIndex].CustomerName = update.NewCustomerName
|
|
}
|
|
|
|
// Recalculate port_used based on actual assignments
|
|
highestUsedPort := 0
|
|
customerCount := 0
|
|
|
|
for _, assignment := range devicePort.PortAssignments {
|
|
if assignment.CustomerName != nil && *assignment.CustomerName != "" {
|
|
customerCount++
|
|
if assignment.PortNumber > highestUsedPort {
|
|
highestUsedPort = assignment.PortNumber
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update port_used to reflect actual usage
|
|
if customerCount == 0 {
|
|
devicePort.PortUsed = 0
|
|
} else {
|
|
// Set port_used to the highest port with a customer
|
|
// or keep current port_used if it's higher (for reserved ports)
|
|
if highestUsedPort > devicePort.PortUsed {
|
|
devicePort.PortUsed = highestUsedPort
|
|
}
|
|
}
|
|
|
|
// Recalculate port_available
|
|
devicePort.PortAvailable = device.PortAmount - devicePort.PortUsed
|
|
|
|
// Update counters and backward compatibility fields
|
|
r.updateDevicePortCounters(&devicePort)
|
|
devicePort.UpdatedAt = time.Now()
|
|
|
|
return tx.Save(&devicePort).Error
|
|
})
|
|
}
|
|
|
|
func (r *deviceDetailsRepo) RemoveCustomerByPort(deviceID uuid.UUID, portNumber int) error {
|
|
return r.db.Transaction(func(tx *gorm.DB) error {
|
|
var device entity.Device
|
|
if err := tx.Set("gorm:query_option", "FOR UPDATE").
|
|
Where("id = ?", deviceID).First(&device).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
var devicePort entity.DevicePort
|
|
if err := tx.Set("gorm:query_option", "FOR UPDATE").
|
|
Where("device_id = ?", deviceID).First(&devicePort).Error; err != nil {
|
|
return fmt.Errorf("device port record not found: %w", err)
|
|
}
|
|
|
|
// Validate port number
|
|
if portNumber > device.PortAmount {
|
|
return fmt.Errorf("port number %d exceeds device capacity (%d)", portNumber, device.PortAmount)
|
|
}
|
|
|
|
portIndex := portNumber - 1
|
|
|
|
// Check if port has a customer
|
|
if len(devicePort.PortAssignments) <= portIndex ||
|
|
devicePort.PortAssignments[portIndex].CustomerName == nil ||
|
|
*devicePort.PortAssignments[portIndex].CustomerName == "" {
|
|
return fmt.Errorf("port %d does not have a customer assigned", portNumber)
|
|
}
|
|
|
|
// Remove customer from port
|
|
devicePort.PortAssignments[portIndex].CustomerName = nil
|
|
|
|
// Update counters and backward compatibility fields
|
|
r.updateDevicePortCounters(&devicePort)
|
|
devicePort.UpdatedAt = time.Now()
|
|
|
|
|
|
return tx.Save(&devicePort).Error
|
|
})
|
|
}
|
|
|
|
func (r *deviceDetailsRepo) UpdatePortAssignments(deviceID uuid.UUID, assignments []req.PortAssignmentDTO) error {
|
|
return r.db.Transaction(func(tx *gorm.DB) error {
|
|
// Lock both device and device_port records
|
|
var device entity.Device
|
|
if err := tx.Set("gorm:query_option", "FOR UPDATE").
|
|
Where("id = ?", deviceID).First(&device).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
// Only ODP devices can have port assignments
|
|
if device.DeviceType != "ODP" {
|
|
return fmt.Errorf("port assignments can only be updated for ODP devices")
|
|
}
|
|
|
|
var devicePort entity.DevicePort
|
|
if err := tx.Set("gorm:query_option", "FOR UPDATE").
|
|
Where("device_id = ?", deviceID).First(&devicePort).Error; err != nil {
|
|
return fmt.Errorf("device port record not found: %w", err)
|
|
}
|
|
|
|
// Validate port numbers are within device capacity
|
|
for _, assignment := range assignments {
|
|
if assignment.PortNumber < 1 || assignment.PortNumber > device.PortAmount {
|
|
return fmt.Errorf("port number %d is out of range (1-%d)", assignment.PortNumber, device.PortAmount)
|
|
}
|
|
}
|
|
|
|
// Initialize port assignments array if needed
|
|
if len(devicePort.PortAssignments) == 0 {
|
|
devicePort.PortAssignments = make(entity.PortAssignments, device.PortAmount)
|
|
for i := 0; i < device.PortAmount; i++ {
|
|
devicePort.PortAssignments[i] = entity.PortAssignment{
|
|
PortNumber: i + 1,
|
|
CustomerName: nil,
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check for duplicate customer names (if not null)
|
|
customerNames := make(map[string]int) // map customer name to port number
|
|
for _, assignment := range assignments {
|
|
if assignment.CustomerName != nil && *assignment.CustomerName != "" {
|
|
if existingPort, exists := customerNames[*assignment.CustomerName]; exists {
|
|
return fmt.Errorf("customer %s is assigned to multiple ports (%d and %d)",
|
|
*assignment.CustomerName, existingPort, assignment.PortNumber)
|
|
}
|
|
customerNames[*assignment.CustomerName] = assignment.PortNumber
|
|
}
|
|
}
|
|
|
|
// Update port assignments
|
|
for _, assignment := range assignments {
|
|
portIndex := assignment.PortNumber - 1
|
|
if portIndex < len(devicePort.PortAssignments) {
|
|
devicePort.PortAssignments[portIndex].PortNumber = assignment.PortNumber
|
|
devicePort.PortAssignments[portIndex].CustomerName = assignment.CustomerName
|
|
}
|
|
}
|
|
|
|
// Update counters and backward compatibility fields
|
|
r.updateDevicePortCounters(&devicePort)
|
|
|
|
// Calculate port usage based on assignments
|
|
portUsed := 0
|
|
for _, assignment := range devicePort.PortAssignments {
|
|
if assignment.CustomerName != nil && *assignment.CustomerName != "" {
|
|
portUsed++
|
|
}
|
|
}
|
|
|
|
devicePort.PortUsed = portUsed
|
|
devicePort.PortAvailable = device.PortAmount - portUsed
|
|
devicePort.UpdatedAt = time.Now()
|
|
|
|
return tx.Save(&devicePort).Error
|
|
})
|
|
}
|
|
|
|
|
|
func (r *deviceDetailsRepo) Create(device entity.Device) error {
|
|
return r.db.Transaction(func(tx *gorm.DB) error {
|
|
// Create device
|
|
if err := tx.Create(&device).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
// Create corresponding device port
|
|
devicePort := entity.DevicePort{
|
|
ID: uuid.New(),
|
|
DeviceID: device.ID,
|
|
PortUsed: 0,
|
|
PortAvailable: device.PortAmount,
|
|
CreatedAt: device.CreatedAt,
|
|
UpdatedAt: device.UpdatedAt,
|
|
}
|
|
|
|
return tx.Create(&devicePort).Error
|
|
})
|
|
}
|
|
|
|
func (r *deviceDetailsRepo) GetAll() ([]entity.DeviceDetails, error) {
|
|
var devices []entity.DeviceDetails
|
|
err := r.db.
|
|
Preload("DevicePort").
|
|
Preload("BackbonesStart").
|
|
Preload("BackbonesStart.DeviceStart").
|
|
Preload("BackbonesStart.DeviceEnd").
|
|
Preload("BackbonesEnd").
|
|
Preload("BackbonesEnd.DeviceStart").
|
|
Preload("BackbonesEnd.DeviceEnd").
|
|
Preload("FishbonesStart").
|
|
Preload("FishbonesStart.DeviceStart").
|
|
Preload("FishbonesStart.DeviceEnd").
|
|
Preload("FishbonesStart.Backbone").
|
|
Preload("FishbonesEnd").
|
|
Preload("FishbonesEnd.DeviceStart").
|
|
Preload("FishbonesEnd.DeviceEnd").
|
|
Preload("FishbonesEnd.Backbone").
|
|
Preload("Towers").
|
|
Preload("Towers.Device").
|
|
Find(&devices).Error
|
|
return devices, err
|
|
}
|
|
|
|
func (r *deviceDetailsRepo) GetByID(id uuid.UUID) (entity.DeviceDetails, error) {
|
|
var device entity.DeviceDetails
|
|
err := r.db.
|
|
Preload("DevicePort").
|
|
Preload("BackbonesStart").
|
|
Preload("BackbonesStart.DeviceStart").
|
|
Preload("BackbonesStart.DeviceEnd").
|
|
Preload("BackbonesEnd").
|
|
Preload("BackbonesEnd.DeviceStart").
|
|
Preload("BackbonesEnd.DeviceEnd").
|
|
Preload("FishbonesStart").
|
|
Preload("FishbonesStart.DeviceStart").
|
|
Preload("FishbonesStart.DeviceEnd").
|
|
Preload("FishbonesStart.Backbone").
|
|
Preload("FishbonesEnd").
|
|
Preload("FishbonesEnd.DeviceStart").
|
|
Preload("FishbonesEnd.DeviceEnd").
|
|
Preload("FishbonesEnd.Backbone").
|
|
Preload("Towers").
|
|
Preload("Towers.Device").
|
|
Where("id = ?", id).
|
|
First(&device).Error
|
|
return device, err
|
|
}
|
|
|
|
func (r *deviceDetailsRepo) Update(id uuid.UUID, updates map[string]interface{}) error {
|
|
return r.db.Transaction(func(tx *gorm.DB) error {
|
|
// Update device
|
|
if err := tx.Model(&entity.Device{}).Where("id = ?", id).Updates(updates).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
// If port_amount is updated, update device_port
|
|
if portAmount, exists := updates["port_amount"]; exists {
|
|
if err := r.updatePortAmountCascade(tx, id, portAmount.(int)); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func (r *deviceDetailsRepo) updatePortAmountCascade(tx *gorm.DB, deviceID uuid.UUID, newPortAmount int) error {
|
|
// Get current port usage
|
|
var devicePort entity.DevicePort
|
|
if err := tx.Where("device_id = ?", deviceID).First(&devicePort).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
// Check if new port amount is sufficient for current usage
|
|
if newPortAmount < devicePort.PortUsed {
|
|
return fmt.Errorf("cannot reduce port amount to %d, currently using %d ports", newPortAmount, devicePort.PortUsed)
|
|
}
|
|
|
|
// Special case: if port_amount is set to 0, clear all assignments
|
|
if newPortAmount == 0 {
|
|
devicePort.PortUsed = 0
|
|
devicePort.PortAvailable = 0
|
|
devicePort.CustomerCount = 0
|
|
devicePort.CustomerNames = []string{}
|
|
devicePort.PortAssignments = entity.PortAssignments{}
|
|
devicePort.UpdatedAt = time.Now()
|
|
} else {
|
|
// Calculate new port available
|
|
newPortAvailable := newPortAmount - devicePort.PortUsed
|
|
|
|
// Update port assignments to match new port amount
|
|
if len(devicePort.PortAssignments) > 0 {
|
|
// Resize port assignments array
|
|
newPortAssignments := make(entity.PortAssignments, newPortAmount)
|
|
|
|
// Copy existing assignments up to the new port amount
|
|
for i := 0; i < newPortAmount; i++ {
|
|
if i < len(devicePort.PortAssignments) {
|
|
newPortAssignments[i] = devicePort.PortAssignments[i]
|
|
} else {
|
|
newPortAssignments[i] = entity.PortAssignment{
|
|
PortNumber: i + 1,
|
|
CustomerName: nil,
|
|
}
|
|
}
|
|
}
|
|
devicePort.PortAssignments = newPortAssignments
|
|
}
|
|
|
|
devicePort.PortAvailable = newPortAvailable
|
|
devicePort.UpdatedAt = time.Now()
|
|
}
|
|
|
|
return tx.Save(&devicePort).Error
|
|
}
|
|
|
|
func (r *deviceDetailsRepo) UpdatePortUsage(deviceID uuid.UUID, portUsed int) error {
|
|
return r.db.Transaction(func(tx *gorm.DB) error {
|
|
var device entity.Device
|
|
if err := tx.Set("gorm:query_option", "FOR UPDATE").
|
|
Where("id = ?", deviceID).First(&device).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
if portUsed > device.PortAmount {
|
|
return fmt.Errorf("port_used (%d) cannot exceed port_amount (%d)", portUsed, device.PortAmount)
|
|
}
|
|
|
|
var devicePort entity.DevicePort
|
|
if err := tx.Set("gorm:query_option", "FOR UPDATE").
|
|
Where("device_id = ?", deviceID).First(&devicePort).Error; err != nil {
|
|
return fmt.Errorf("device port record not found: %w", err)
|
|
}
|
|
|
|
// Initialize port assignments if empty and we have customers or port_used > 0
|
|
if len(devicePort.PortAssignments) == 0 && (len(devicePort.CustomerNames) > 0 || portUsed > 0) {
|
|
devicePort.PortAssignments = make(entity.PortAssignments, device.PortAmount)
|
|
for i := 0; i < device.PortAmount; i++ {
|
|
var customerName *string
|
|
// Map existing customer names to ports
|
|
if i < len(devicePort.CustomerNames) && devicePort.CustomerNames[i] != "" {
|
|
customerName = &devicePort.CustomerNames[i]
|
|
}
|
|
|
|
devicePort.PortAssignments[i] = entity.PortAssignment{
|
|
PortNumber: i + 1,
|
|
CustomerName: customerName,
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update port assignments based on new port_used value
|
|
currentCustomerCount := 0
|
|
for _, assignment := range devicePort.PortAssignments {
|
|
if assignment.CustomerName != nil && *assignment.CustomerName != "" {
|
|
currentCustomerCount++
|
|
}
|
|
}
|
|
|
|
if portUsed < currentCustomerCount {
|
|
// Need to remove some customers (keep first N customers)
|
|
customersKept := 0
|
|
for i := range devicePort.PortAssignments {
|
|
if devicePort.PortAssignments[i].CustomerName != nil && *devicePort.PortAssignments[i].CustomerName != "" {
|
|
if customersKept < portUsed {
|
|
customersKept++
|
|
} else {
|
|
devicePort.PortAssignments[i].CustomerName = nil
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update counters
|
|
devicePort.PortUsed = portUsed
|
|
devicePort.PortAvailable = device.PortAmount - portUsed
|
|
r.updateDevicePortCounters(&devicePort)
|
|
devicePort.UpdatedAt = time.Now()
|
|
|
|
return tx.Save(&devicePort).Error
|
|
})
|
|
}
|
|
|
|
func (r *deviceDetailsRepo) MigrateCustomerNamesToPortAssignments(devicePort *entity.DevicePort, devicePortAmount int) error {
|
|
// Only migrate if PortAssignments is empty but CustomerNames has data
|
|
if len(devicePort.PortAssignments) == 0 && len(devicePort.CustomerNames) > 0 {
|
|
devicePort.PortAssignments = make(entity.PortAssignments, devicePortAmount)
|
|
|
|
for i := 0; i < devicePortAmount; i++ {
|
|
var customerName *string
|
|
if i < len(devicePort.CustomerNames) && devicePort.CustomerNames[i] != "" {
|
|
customerName = &devicePort.CustomerNames[i]
|
|
}
|
|
|
|
devicePort.PortAssignments[i] = entity.PortAssignment{
|
|
PortNumber: i + 1,
|
|
CustomerName: customerName,
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// func (r *deviceDetailsRepo) ValidatePortAvailability(deviceID uuid.UUID, requiredPorts int) error {
|
|
// var devicePort entity.DevicePort
|
|
// if err := r.db.Where("device_id = ?", deviceID).First(&devicePort).Error; err != nil {
|
|
// return err
|
|
// }
|
|
|
|
// if devicePort.Portvailable < requiredPorts {
|
|
// return errors.New("insufficient available ports")
|
|
// }
|
|
|
|
// return nil
|
|
// }
|
|
|
|
func (r *deviceDetailsRepo) GetBackbonesByDeviceID(deviceID uuid.UUID) ([]entity.Backbone, error) {
|
|
var backbones []entity.Backbone
|
|
err := r.db.Preload("DeviceStart").Preload("DeviceEnd").
|
|
Where("dev_start_id = ? OR dev_end_id = ?", deviceID, deviceID).
|
|
Find(&backbones).Error
|
|
return backbones, err
|
|
}
|
|
|
|
func (r *deviceDetailsRepo) GetFishbonesByDeviceID(deviceID uuid.UUID) ([]entity.Fishbone, error) {
|
|
var fishbones []entity.Fishbone
|
|
err := r.db.Preload("Backbone").Preload("DeviceStart").Preload("DeviceEnd").
|
|
Where("dev_start_id = ? OR dev_end_id = ?", deviceID, deviceID).
|
|
Find(&fishbones).Error
|
|
return fishbones, err
|
|
}
|
|
|
|
func (r *deviceDetailsRepo) GetTowersByDeviceID(deviceID uuid.UUID) ([]entity.Tower, error) {
|
|
var towers []entity.Tower
|
|
err := r.db.Where("dev_id = ?", deviceID).Find(&towers).Error
|
|
return towers, err
|
|
}
|
|
|
|
|
|
|
|
func (r *deviceDetailsRepo) GetPortUsageByDevice(deviceID uuid.UUID) (portUsed, portAvailable int, err error) {
|
|
var devicePort entity.DevicePort
|
|
err = r.db.Where("device_id = ?", deviceID).First(&devicePort).Error
|
|
if err != nil {
|
|
return 0, 0, err
|
|
}
|
|
return devicePort.PortUsed, devicePort.PortAvailable, nil
|
|
}
|
|
|
|
func (r *deviceDetailsRepo) Delete(id uuid.UUID) error {
|
|
return r.db.Transaction(func(tx *gorm.DB) error {
|
|
// Delete device port first
|
|
if err := tx.Where("device_id = ?", id).Delete(&entity.DevicePort{}).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
// Delete device
|
|
return tx.Delete(&entity.Device{}, id).Error
|
|
})
|
|
}
|
|
|
|
func (r *deviceDetailsRepo) AssignCustomerToPort(deviceID uuid.UUID, customerName string, portNumber *int) error {
|
|
return r.db.Transaction(func(tx *gorm.DB) error {
|
|
// Lock both device and device_port records
|
|
var device entity.Device
|
|
if err := tx.Set("gorm:query_option", "FOR UPDATE").
|
|
Where("id = ?", deviceID).First(&device).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
// Only ODP devices can have customers assigned
|
|
if device.DeviceType != "ODP" {
|
|
return fmt.Errorf("customers can only be assigned to ODP devices")
|
|
}
|
|
|
|
var devicePort entity.DevicePort
|
|
if err := tx.Set("gorm:query_option", "FOR UPDATE").
|
|
Where("device_id = ?", deviceID).First(&devicePort).Error; err != nil {
|
|
return fmt.Errorf("device port record not found: %w", err)
|
|
}
|
|
|
|
// Initialize port assignments if empty
|
|
if len(devicePort.PortAssignments) == 0 {
|
|
devicePort.PortAssignments = make(entity.PortAssignments, device.PortAmount)
|
|
for i := 0; i < device.PortAmount; i++ {
|
|
devicePort.PortAssignments[i] = entity.PortAssignment{
|
|
PortNumber: i + 1,
|
|
CustomerName: nil,
|
|
}
|
|
}
|
|
}
|
|
|
|
// Determine port number to use
|
|
var targetPortNumber int
|
|
if portNumber != nil {
|
|
targetPortNumber = *portNumber
|
|
if targetPortNumber < 1 || targetPortNumber > device.PortAmount {
|
|
return fmt.Errorf("port number must be between 1 and %d", device.PortAmount)
|
|
}
|
|
} else {
|
|
// Auto-assign to first available port
|
|
targetPortNumber = -1
|
|
for i, assignment := range devicePort.PortAssignments {
|
|
if assignment.CustomerName == nil || *assignment.CustomerName == "" {
|
|
targetPortNumber = i + 1
|
|
break
|
|
}
|
|
}
|
|
if targetPortNumber == -1 {
|
|
return fmt.Errorf("no available ports for customer assignment")
|
|
}
|
|
}
|
|
|
|
// Check if the specified port is already occupied
|
|
portIndex := targetPortNumber - 1
|
|
if devicePort.PortAssignments[portIndex].CustomerName != nil &&
|
|
*devicePort.PortAssignments[portIndex].CustomerName != "" {
|
|
return fmt.Errorf("port %d is already occupied by %s", targetPortNumber, *devicePort.PortAssignments[portIndex].CustomerName)
|
|
}
|
|
|
|
// Check if customer is already assigned to another port
|
|
for i, assignment := range devicePort.PortAssignments {
|
|
if assignment.CustomerName != nil && *assignment.CustomerName == customerName {
|
|
return fmt.Errorf("customer %s is already assigned to port %d", customerName, i+1)
|
|
}
|
|
}
|
|
|
|
// Assign customer to port
|
|
devicePort.PortAssignments[portIndex].CustomerName = &customerName
|
|
|
|
// Update both PortAssignments and CustomerNames for backward compatibility
|
|
r.updateDevicePortCounters(&devicePort)
|
|
devicePort.UpdatedAt = time.Now()
|
|
|
|
return tx.Save(&devicePort).Error
|
|
})
|
|
}
|
|
|
|
func (r *deviceDetailsRepo) RemoveCustomerFromPort(deviceID uuid.UUID, customerName string) error {
|
|
return r.db.Transaction(func(tx *gorm.DB) error {
|
|
var devicePort entity.DevicePort
|
|
if err := tx.Set("gorm:query_option", "FOR UPDATE").
|
|
Where("device_id = ?", deviceID).First(&devicePort).Error; err != nil {
|
|
return fmt.Errorf("device port record not found: %w", err)
|
|
}
|
|
|
|
// Find and remove customer
|
|
newCustomerNames := make([]string, 0)
|
|
found := false
|
|
for _, existing := range devicePort.CustomerNames {
|
|
if existing != customerName {
|
|
newCustomerNames = append(newCustomerNames, existing)
|
|
} else {
|
|
found = true
|
|
}
|
|
}
|
|
|
|
if !found {
|
|
return fmt.Errorf("customer %s is not assigned to this device", customerName)
|
|
}
|
|
|
|
devicePort.CustomerNames = newCustomerNames
|
|
devicePort.CustomerCount = len(devicePort.CustomerNames)
|
|
devicePort.PortAvailable = devicePort.PortAvailable + 1
|
|
devicePort.UpdatedAt = time.Now()
|
|
|
|
return tx.Save(&devicePort).Error
|
|
})
|
|
}
|
|
|
|
func (r *deviceDetailsRepo) UpdateDevicePortUsage(deviceID uuid.UUID) error {
|
|
// This method is required by the DeviceDetailsRepo interface
|
|
return r.db.Transaction(func(tx *gorm.DB) error {
|
|
var devicePort entity.DevicePort
|
|
if err := tx.Where("device_id = ?", deviceID).First(&devicePort).Error; err != nil {
|
|
return fmt.Errorf("device port record not found: %w", err)
|
|
}
|
|
|
|
// Update based on port assignments
|
|
r.updateDevicePortCounters(&devicePort)
|
|
devicePort.PortUsed = devicePort.CustomerCount
|
|
devicePort.UpdatedAt = time.Now()
|
|
|
|
return tx.Save(&devicePort).Error
|
|
})
|
|
}
|
|
|
|
func (r *deviceDetailsRepo) updateDevicePortCounters(devicePort *entity.DevicePort) {
|
|
// Get device port amount (need to load device if not loaded)
|
|
var device entity.Device
|
|
if devicePort.Device.ID == uuid.Nil {
|
|
r.db.Where("id = ?", devicePort.DeviceID).First(&device)
|
|
devicePort.Device = device
|
|
} else {
|
|
device = devicePort.Device
|
|
}
|
|
|
|
// Count actual customers and find highest used port
|
|
customerCount := 0
|
|
customerNames := make([]string, 0)
|
|
highestCustomerPort := 0
|
|
|
|
for _, assignment := range devicePort.PortAssignments {
|
|
if assignment.CustomerName != nil && *assignment.CustomerName != "" {
|
|
customerCount++
|
|
customerNames = append(customerNames, *assignment.CustomerName) // Just customer names, not with port numbers
|
|
if assignment.PortNumber > highestCustomerPort {
|
|
highestCustomerPort = assignment.PortNumber
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set port_used to the highest port number that has a customer
|
|
// This ensures we don't have gaps in port usage
|
|
if customerCount == 0 {
|
|
devicePort.PortUsed = 0
|
|
} else {
|
|
devicePort.PortUsed = highestCustomerPort
|
|
}
|
|
|
|
// Calculate port_available
|
|
devicePort.PortAvailable = device.PortAmount - devicePort.PortUsed
|
|
|
|
// Update customer fields
|
|
devicePort.CustomerCount = customerCount
|
|
devicePort.CustomerNames = customerNames
|
|
}
|
|
|
|
func (r *deviceDetailsRepo) AssignMultipleCustomersToPort(deviceID uuid.UUID, assignments []req.AssignMultipleCustomersDTO) error {
|
|
return r.db.Transaction(func(tx *gorm.DB) error {
|
|
// Lock both device and device_port records
|
|
var device entity.Device
|
|
if err := tx.Set("gorm:query_option", "FOR UPDATE").
|
|
Where("id = ?", deviceID).First(&device).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
// Only ODP devices can have customers assigned
|
|
if device.DeviceType != "ODP" {
|
|
return fmt.Errorf("customers can only be assigned to ODP devices")
|
|
}
|
|
|
|
var devicePort entity.DevicePort
|
|
if err := tx.Set("gorm:query_option", "FOR UPDATE").
|
|
Where("device_id = ?", deviceID).First(&devicePort).Error; err != nil {
|
|
return fmt.Errorf("device port record not found: %w", err)
|
|
}
|
|
|
|
// Initialize port assignments if empty
|
|
if len(devicePort.PortAssignments) == 0 {
|
|
devicePort.PortAssignments = make(entity.PortAssignments, device.PortAmount)
|
|
for i := 0; i < device.PortAmount; i++ {
|
|
devicePort.PortAssignments[i] = entity.PortAssignment{
|
|
PortNumber: i + 1,
|
|
CustomerName: nil,
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check if there are enough available ports
|
|
availablePorts := 0
|
|
for _, assignment := range devicePort.PortAssignments {
|
|
if assignment.CustomerName == nil || *assignment.CustomerName == "" {
|
|
availablePorts++
|
|
}
|
|
}
|
|
|
|
portsNeeded := len(assignments)
|
|
if portsNeeded > availablePorts {
|
|
return fmt.Errorf("not enough available ports: need %d, available %d", portsNeeded, availablePorts)
|
|
}
|
|
|
|
// Process each assignment
|
|
for _, assignment := range assignments {
|
|
var targetPortNumber int
|
|
|
|
if assignment.PortNumber != nil {
|
|
targetPortNumber = *assignment.PortNumber
|
|
if targetPortNumber < 1 || targetPortNumber > device.PortAmount {
|
|
return fmt.Errorf("port number %d must be between 1 and %d", targetPortNumber, device.PortAmount)
|
|
}
|
|
} else {
|
|
// Auto-assign to first available port
|
|
targetPortNumber = -1
|
|
for i, portAssignment := range devicePort.PortAssignments {
|
|
if portAssignment.CustomerName == nil || *portAssignment.CustomerName == "" {
|
|
targetPortNumber = i + 1
|
|
break
|
|
}
|
|
}
|
|
if targetPortNumber == -1 {
|
|
return fmt.Errorf("no available ports for customer assignment")
|
|
}
|
|
}
|
|
|
|
// Check if the specified port is already occupied
|
|
portIndex := targetPortNumber - 1
|
|
if devicePort.PortAssignments[portIndex].CustomerName != nil &&
|
|
*devicePort.PortAssignments[portIndex].CustomerName != "" {
|
|
return fmt.Errorf("port %d is already occupied by %s", targetPortNumber, *devicePort.PortAssignments[portIndex].CustomerName)
|
|
}
|
|
|
|
// Check if customer is already assigned to another port
|
|
for i, portAssignment := range devicePort.PortAssignments {
|
|
if portAssignment.CustomerName != nil && *portAssignment.CustomerName == assignment.CustomerName {
|
|
return fmt.Errorf("customer %s is already assigned to port %d", assignment.CustomerName, i+1)
|
|
}
|
|
}
|
|
|
|
// Assign customer to port
|
|
devicePort.PortAssignments[portIndex].CustomerName = &assignment.CustomerName
|
|
}
|
|
|
|
// Update counters and backward compatibility fields
|
|
r.updateDevicePortCounters(&devicePort)
|
|
devicePort.UpdatedAt = time.Now()
|
|
|
|
return tx.Save(&devicePort).Error
|
|
})
|
|
}
|
|
|
|
func (r *deviceDetailsRepo) RecalculatePortUsageAfterBulkUpdate(deviceID uuid.UUID) error {
|
|
return r.db.Transaction(func(tx *gorm.DB) error {
|
|
var device entity.Device
|
|
if err := tx.Where("id = ?", deviceID).First(&device).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
var devicePort entity.DevicePort
|
|
if err := tx.Where("device_id = ?", deviceID).First(&devicePort).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
// Find the highest port number with a customer
|
|
highestUsedPort := 0
|
|
customerCount := 0
|
|
|
|
for _, assignment := range devicePort.PortAssignments {
|
|
if assignment.CustomerName != nil && *assignment.CustomerName != "" {
|
|
customerCount++
|
|
if assignment.PortNumber > highestUsedPort {
|
|
highestUsedPort = assignment.PortNumber
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update port usage
|
|
devicePort.PortUsed = highestUsedPort
|
|
devicePort.PortAvailable = device.PortAmount - devicePort.PortUsed
|
|
|
|
// Update other counters
|
|
r.updateDevicePortCounters(&devicePort)
|
|
devicePort.UpdatedAt = time.Now()
|
|
|
|
return tx.Save(&devicePort).Error
|
|
})
|
|
} |