395 lines
14 KiB
Go
395 lines
14 KiB
Go
package repository
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"time"
|
|
"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) error
|
|
RemoveCustomerFromPort(deviceID uuid.UUID, customerName string) error
|
|
}
|
|
|
|
type deviceDetailsRepo struct {
|
|
db *gorm.DB
|
|
}
|
|
|
|
func NewDeviceDetailsRepo(db *gorm.DB) DeviceDetailsRepo {
|
|
return &deviceDetailsRepo{
|
|
db: db,
|
|
}
|
|
}
|
|
|
|
|
|
|
|
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 errors.New("cannot reduce port amount below current usage")
|
|
}
|
|
|
|
// Update port available
|
|
newPortAvailable := newPortAmount - devicePort.PortUsed
|
|
return tx.Model(&entity.DevicePort{}).
|
|
Where("device_id = ?", deviceID).
|
|
Updates(map[string]interface{}{
|
|
"port_available": newPortAvailable,
|
|
"updated_at": gorm.Expr("NOW()"),
|
|
}).Error
|
|
}
|
|
|
|
func (r *deviceDetailsRepo) UpdateDevicePortUsage(deviceID uuid.UUID) error {
|
|
return r.db.Transaction(func(tx *gorm.DB) error {
|
|
// Lock the device record to prevent race conditions
|
|
var device entity.Device
|
|
if err := tx.Set("gorm:query_option", "FOR UPDATE").
|
|
Where("id = ?", deviceID).First(&device).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
var portUsed int
|
|
var customerCount int
|
|
var customerNames []string
|
|
|
|
switch device.DeviceType {
|
|
case "OTB":
|
|
// For OTB: count backbones (each backbone uses 1 port)
|
|
var backboneCount int64
|
|
if err := tx.Model(&entity.Backbone{}).
|
|
Where("dev_start_id = ? OR dev_end_id = ?", deviceID, deviceID).
|
|
Count(&backboneCount).Error; err != nil {
|
|
return err
|
|
}
|
|
portUsed = int(backboneCount)
|
|
customerCount = 0 // OTB doesn't serve customers directly
|
|
|
|
case "closure":
|
|
// For closure: count fishbones where this device is the start device
|
|
var fishboneCount int64
|
|
if err := tx.Model(&entity.Fishbone{}).
|
|
Where("dev_start_id = ?", deviceID).
|
|
Count(&fishboneCount).Error; err != nil {
|
|
return err
|
|
}
|
|
portUsed = int(fishboneCount)
|
|
customerCount = 0 // Closure doesn't serve customers directly
|
|
|
|
case "ODP":
|
|
// For ODP: sum fishbone core amounts where this device is the end device
|
|
var totalCores int64
|
|
if err := tx.Model(&entity.Fishbone{}).
|
|
Where("dev_end_id = ?", deviceID).
|
|
Select("COALESCE(SUM(core_amount), 0)").
|
|
Scan(&totalCores).Error; err != nil {
|
|
return err
|
|
}
|
|
portUsed = int(totalCores)
|
|
|
|
// For ODP: customer count should equal port_used
|
|
// Get existing customer assignments
|
|
var existingDevicePort entity.DevicePort
|
|
if err := tx.Where("device_id = ?", deviceID).First(&existingDevicePort).Error; err == nil {
|
|
customerNames = existingDevicePort.CustomerNames
|
|
}
|
|
|
|
// Ensure customer count matches port_used for ODP
|
|
customerCount = portUsed
|
|
|
|
// If we have more customers than ports used, trim the list
|
|
if len(customerNames) > portUsed {
|
|
customerNames = customerNames[:portUsed]
|
|
}
|
|
|
|
default:
|
|
portUsed = 0
|
|
customerCount = 0
|
|
}
|
|
|
|
// Calculate port available
|
|
portAvailable := device.PortAmount - portUsed
|
|
if portAvailable < 0 {
|
|
portAvailable = 0
|
|
}
|
|
|
|
// Update or create DevicePort record with locking
|
|
result := tx.Set("gorm:query_option", "FOR UPDATE").
|
|
Model(&entity.DevicePort{}).
|
|
Where("device_id = ?", deviceID).
|
|
Updates(map[string]interface{}{
|
|
"port_used": portUsed,
|
|
"port_available": portAvailable,
|
|
"customer_count": customerCount,
|
|
"customer_names": customerNames,
|
|
"updated_at": gorm.Expr("NOW()"),
|
|
})
|
|
|
|
if result.Error != nil {
|
|
return result.Error
|
|
}
|
|
|
|
// If no record was updated, create a new one
|
|
if result.RowsAffected == 0 {
|
|
devicePort := entity.DevicePort{
|
|
ID: uuid.New(),
|
|
DeviceID: deviceID,
|
|
PortUsed: portUsed,
|
|
PortAvailable: portAvailable,
|
|
CustomerCount: customerCount,
|
|
CustomerNames: customerNames,
|
|
CreatedAt: time.Now(),
|
|
UpdatedAt: time.Now(),
|
|
}
|
|
return tx.Create(&devicePort).Error
|
|
}
|
|
|
|
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) 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)
|
|
}
|
|
|
|
// Check if there are available ports
|
|
if devicePort.PortAvailable <= 0 {
|
|
return fmt.Errorf("no available ports for customer assignment")
|
|
}
|
|
|
|
// Check if customer is already assigned
|
|
for _, existing := range devicePort.CustomerNames {
|
|
if existing == customerName {
|
|
return fmt.Errorf("customer %s is already assigned to this device", customerName)
|
|
}
|
|
}
|
|
|
|
// Add customer to the list
|
|
devicePort.CustomerNames = append(devicePort.CustomerNames, customerName)
|
|
devicePort.CustomerCount = len(devicePort.CustomerNames)
|
|
devicePort.PortAvailable = devicePort.PortAvailable - 1
|
|
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
|
|
})
|
|
} |