updated for bulk update
This commit is contained in:
parent
8afca3054a
commit
1aee7e2bda
|
|
@ -1,13 +1,18 @@
|
|||
package controller
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"users_management/m/middleware"
|
||||
"users_management/m/model/dto/req"
|
||||
"users_management/m/usecase"
|
||||
"users_management/m/utils/common"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"users_management/m/middleware"
|
||||
"users_management/m/model/dto/req"
|
||||
"users_management/m/usecase"
|
||||
"users_management/m/utils/common"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type DeviceDetailsController struct {
|
||||
|
|
@ -35,9 +40,87 @@ func (c *DeviceDetailsController) Route() {
|
|||
deviceDetails.POST("/:id/assign-customer", c.assignCustomer)
|
||||
deviceDetails.PUT("/:id/port-usage", c.updatePortUsage) // New endpoint
|
||||
deviceDetails.PUT("/:id/port-assignments", c.updatePortAssignments) // New endpoint
|
||||
|
||||
deviceDetails.PUT("/:id/update-customer-by-port", c.updateCustomerByPort) // Updated endpoint name
|
||||
deviceDetails.PUT("/:id/bulk-update-customers-by-port", c.bulkUpdateCustomersByPort) // Updated endpoint name
|
||||
deviceDetails.DELETE("/:id/remove-customer-by-port", c.removeCustomerByPort)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func (c *DeviceDetailsController) updateCustomerByPort(ctx *gin.Context) {
|
||||
id := ctx.Param("id")
|
||||
deviceID, err := uuid.Parse(id)
|
||||
if err != nil {
|
||||
common.ErrorResponses(ctx, http.StatusBadRequest, "Invalid device ID")
|
||||
return
|
||||
}
|
||||
|
||||
var request req.UpdateCustomerByPortDTO
|
||||
if err := ctx.ShouldBindJSON(&request); err != nil {
|
||||
common.ErrorResponses(ctx, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
err = c.deviceDetailsUC.UpdateCustomerByPort(deviceID, request)
|
||||
if err != nil {
|
||||
common.ErrorResponses(ctx, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if request.NewCustomerName == nil {
|
||||
common.SingleResponses(ctx, fmt.Sprintf("Customer removed from port %d successfully", request.PortNumber), nil)
|
||||
} else {
|
||||
common.SingleResponses(ctx, fmt.Sprintf("Customer assigned to port %d successfully", request.PortNumber), nil)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *DeviceDetailsController) bulkUpdateCustomersByPort(ctx *gin.Context) {
|
||||
id := ctx.Param("id")
|
||||
deviceID, err := uuid.Parse(id)
|
||||
if err != nil {
|
||||
common.ErrorResponses(ctx, http.StatusBadRequest, "Invalid device ID")
|
||||
return
|
||||
}
|
||||
|
||||
var request req.BulkUpdateCustomersByPortDTO
|
||||
if err := ctx.ShouldBindJSON(&request); err != nil {
|
||||
common.ErrorResponses(ctx, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
err = c.deviceDetailsUC.BulkUpdateCustomersByPort(deviceID, request.Updates)
|
||||
if err != nil {
|
||||
common.ErrorResponses(ctx, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
common.SingleResponses(ctx, fmt.Sprintf("%d port assignments updated successfully", len(request.Updates)), nil)
|
||||
}
|
||||
|
||||
func (c *DeviceDetailsController) removeCustomerByPort(ctx *gin.Context) {
|
||||
id := ctx.Param("id")
|
||||
deviceID, err := uuid.Parse(id)
|
||||
if err != nil {
|
||||
common.ErrorResponses(ctx, http.StatusBadRequest, "Invalid device ID")
|
||||
return
|
||||
}
|
||||
|
||||
var request req.RemoveCustomerByPortDTO
|
||||
if err := ctx.ShouldBindJSON(&request); err != nil {
|
||||
common.ErrorResponses(ctx, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
err = c.deviceDetailsUC.RemoveCustomerByPort(deviceID, request.PortNumber)
|
||||
if err != nil {
|
||||
common.ErrorResponses(ctx, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
common.SingleResponses(ctx, fmt.Sprintf("Customer removed from port %d successfully", request.PortNumber), nil)
|
||||
}
|
||||
|
||||
func (c *DeviceDetailsController) assignCustomer(ctx *gin.Context) {
|
||||
id := ctx.Param("id")
|
||||
deviceID, err := uuid.Parse(id)
|
||||
|
|
@ -46,23 +129,58 @@ func (c *DeviceDetailsController) assignCustomer(ctx *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
var request struct {
|
||||
CustomerName string `json:"customer_name"`
|
||||
PortNumber *int `json:"port_number"`
|
||||
// Try to parse as array first
|
||||
var arrayRequest []req.AssignMultipleCustomersDTO
|
||||
|
||||
// Try to parse as single object
|
||||
var singleRequest struct {
|
||||
CustomerName string `json:"customer_name" binding:"required"`
|
||||
PortNumber *int `json:"port_number,omitempty"`
|
||||
}
|
||||
|
||||
if err := ctx.ShouldBindJSON(&request); err != nil {
|
||||
common.ErrorResponses(ctx, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
err = c.deviceDetailsUC.AssignCustomerToPort(deviceID, request.CustomerName, request.PortNumber)
|
||||
// Get raw JSON to determine the structure
|
||||
rawData, err := ctx.GetRawData()
|
||||
if err != nil {
|
||||
common.ErrorResponses(ctx, http.StatusBadRequest, err.Error())
|
||||
common.ErrorResponses(ctx, http.StatusBadRequest, "Invalid JSON data")
|
||||
return
|
||||
}
|
||||
|
||||
common.SingleResponses(ctx, "Customer assigned successfully", nil)
|
||||
// Reset the request body for subsequent parsing
|
||||
ctx.Request.Body = io.NopCloser(bytes.NewBuffer(rawData))
|
||||
|
||||
// Check if it's an array by looking at the first character
|
||||
trimmed := bytes.TrimSpace(rawData)
|
||||
if len(trimmed) > 0 && trimmed[0] == '[' {
|
||||
// It's an array
|
||||
if err := json.Unmarshal(rawData, &arrayRequest); err != nil {
|
||||
common.ErrorResponses(ctx, http.StatusBadRequest, "Invalid array format: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Process array of assignments
|
||||
err = c.deviceDetailsUC.AssignMultipleCustomersToPort(deviceID, arrayRequest)
|
||||
if err != nil {
|
||||
common.ErrorResponses(ctx, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
common.SingleResponses(ctx, fmt.Sprintf("%d customers assigned successfully", len(arrayRequest)), nil)
|
||||
} else {
|
||||
// It's a single object
|
||||
if err := json.Unmarshal(rawData, &singleRequest); err != nil {
|
||||
common.ErrorResponses(ctx, http.StatusBadRequest, "Invalid object format: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Process single assignment
|
||||
err = c.deviceDetailsUC.AssignCustomerToPort(deviceID, singleRequest.CustomerName, singleRequest.PortNumber)
|
||||
if err != nil {
|
||||
common.ErrorResponses(ctx, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
common.SingleResponses(ctx, "Customer assigned successfully", nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -26,3 +26,21 @@ type UpdateDeviceDetailsDTO struct {
|
|||
City *string `json:"city,omitempty" validate:"omitempty,min=3"`
|
||||
District *string `json:"district,omitempty" validate:"omitempty,min=3"`
|
||||
}
|
||||
|
||||
type AssignMultipleCustomersDTO struct {
|
||||
CustomerName string `json:"customer_name" binding:"required"`
|
||||
PortNumber *int `json:"port_number,omitempty"`
|
||||
}
|
||||
|
||||
type UpdateCustomerByPortDTO struct {
|
||||
PortNumber int `json:"port_number" binding:"required,min=1"`
|
||||
NewCustomerName *string `json:"new_customer_name,omitempty"` // null to remove, string to update/assign
|
||||
}
|
||||
|
||||
type BulkUpdateCustomersByPortDTO struct {
|
||||
Updates []UpdateCustomerByPortDTO `json:"updates" binding:"required"`
|
||||
}
|
||||
|
||||
type RemoveCustomerByPortDTO struct {
|
||||
PortNumber int `json:"port_number" binding:"required,min=1"`
|
||||
}
|
||||
|
|
@ -33,6 +33,10 @@ type DeviceDetailsRepo interface {
|
|||
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
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -47,6 +51,214 @@ func NewDeviceDetailsRepo(db *gorm.DB) DeviceDetailsRepo {
|
|||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
|
@ -536,17 +748,172 @@ func (r *deviceDetailsRepo) UpdateDevicePortUsage(deviceID uuid.UUID) error {
|
|||
}
|
||||
|
||||
func (r *deviceDetailsRepo) updateDevicePortCounters(devicePort *entity.DevicePort) {
|
||||
// Count actual customers and create customer names list
|
||||
// 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)
|
||||
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
|
||||
})
|
||||
}
|
||||
|
|
@ -28,6 +28,10 @@ type DeviceDetailsUseCase interface {
|
|||
AssignCustomerToPort(deviceID uuid.UUID, customerName string,portNumber *int) error
|
||||
UpdatePortUsage(deviceID uuid.UUID, portUsed int) error
|
||||
UpdatePortAssignments(deviceID uuid.UUID, portAssignments []req.PortAssignmentDTO) 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 deviceDetailsUseCase struct {
|
||||
|
|
@ -44,6 +48,84 @@ func NewDeviceDetailsUseCase(deviceDetailsRepo repository.DeviceDetailsRepo, geo
|
|||
}
|
||||
}
|
||||
|
||||
func (u *deviceDetailsUseCase) UpdateCustomerByPort(deviceID uuid.UUID, update req.UpdateCustomerByPortDTO) error {
|
||||
// Validate port number
|
||||
if update.PortNumber < 1 {
|
||||
return errors.New("port number must be greater than 0")
|
||||
}
|
||||
|
||||
return u.deviceDetailsRepo.UpdateCustomerByPort(deviceID, update)
|
||||
}
|
||||
|
||||
func (u *deviceDetailsUseCase) BulkUpdateCustomersByPort(deviceID uuid.UUID, updates []req.UpdateCustomerByPortDTO) error {
|
||||
if len(updates) == 0 {
|
||||
return errors.New("no updates provided")
|
||||
}
|
||||
|
||||
// Validate all updates
|
||||
portNumbers := make(map[int]bool)
|
||||
newCustomerNames := make(map[string]bool)
|
||||
|
||||
for _, update := range updates {
|
||||
// Check for duplicate port numbers in request
|
||||
if portNumbers[update.PortNumber] {
|
||||
return fmt.Errorf("duplicate port number in request: %d", update.PortNumber)
|
||||
}
|
||||
portNumbers[update.PortNumber] = true
|
||||
|
||||
// Check for duplicate new customer names in request (ignore nulls)
|
||||
if update.NewCustomerName != nil && *update.NewCustomerName != "" {
|
||||
if newCustomerNames[*update.NewCustomerName] {
|
||||
return fmt.Errorf("duplicate new customer name in request: %s", *update.NewCustomerName)
|
||||
}
|
||||
newCustomerNames[*update.NewCustomerName] = true
|
||||
}
|
||||
|
||||
// Validate port number
|
||||
if update.PortNumber < 1 {
|
||||
return fmt.Errorf("port number must be greater than 0: %d", update.PortNumber)
|
||||
}
|
||||
}
|
||||
|
||||
return u.deviceDetailsRepo.BulkUpdateCustomersByPort(deviceID, updates)
|
||||
}
|
||||
|
||||
func (u *deviceDetailsUseCase) RemoveCustomerByPort(deviceID uuid.UUID, portNumber int) error {
|
||||
if portNumber < 1 {
|
||||
return errors.New("port number must be greater than 0")
|
||||
}
|
||||
|
||||
return u.deviceDetailsRepo.RemoveCustomerByPort(deviceID, portNumber)
|
||||
}
|
||||
|
||||
func (u *deviceDetailsUseCase) AssignMultipleCustomersToPort(deviceID uuid.UUID, assignments []req.AssignMultipleCustomersDTO) error {
|
||||
if len(assignments) == 0 {
|
||||
return errors.New("no assignments provided")
|
||||
}
|
||||
|
||||
// Validate all assignments first
|
||||
customerNames := make(map[string]bool)
|
||||
portNumbers := make(map[int]bool)
|
||||
|
||||
for _, assignment := range assignments {
|
||||
// Check for duplicate customer names in the request
|
||||
if customerNames[assignment.CustomerName] {
|
||||
return fmt.Errorf("duplicate customer name in request: %s", assignment.CustomerName)
|
||||
}
|
||||
customerNames[assignment.CustomerName] = true
|
||||
|
||||
// Check for duplicate port numbers in the request
|
||||
if assignment.PortNumber != nil {
|
||||
if portNumbers[*assignment.PortNumber] {
|
||||
return fmt.Errorf("duplicate port number in request: %d", *assignment.PortNumber)
|
||||
}
|
||||
portNumbers[*assignment.PortNumber] = true
|
||||
}
|
||||
}
|
||||
|
||||
return u.deviceDetailsRepo.AssignMultipleCustomersToPort(deviceID, assignments)
|
||||
}
|
||||
|
||||
|
||||
func (u *deviceDetailsUseCase) UpdatePortAssignments(deviceID uuid.UUID, assignments []req.PortAssignmentDTO) error {
|
||||
// Validate the assignments
|
||||
|
|
|
|||
Loading…
Reference in New Issue