adding bandwidth and status on odp customer assigntment

This commit is contained in:
areeqakbr 2025-06-23 16:35:13 +07:00
parent 0c926d722b
commit a2c71ca81e
8 changed files with 218 additions and 93 deletions

View File

@ -1,5 +1,6 @@
package req package req
import "users_management/m/model/entity"
type DeviceDetailsDTO struct { type DeviceDetailsDTO struct {
DeviceCode string `json:"device_code" validate:"required"` DeviceCode string `json:"device_code" validate:"required"`
@ -25,17 +26,20 @@ type UpdateDeviceDetailsDTO struct {
Province *string `json:"province,omitempty" validate:"omitempty,min=3"` Province *string `json:"province,omitempty" validate:"omitempty,min=3"`
City *string `json:"city,omitempty" validate:"omitempty,min=3"` City *string `json:"city,omitempty" validate:"omitempty,min=3"`
District *string `json:"district,omitempty" validate:"omitempty,min=3"` District *string `json:"district,omitempty" validate:"omitempty,min=3"`
} }
type AssignMultipleCustomersDTO struct { type AssignMultipleCustomersDTO struct {
CustomerName string `json:"customer_name" binding:"required"` CustomerName string `json:"customer_name" binding:"required"`
PortNumber *int `json:"port_number,omitempty"` PortNumber *int `json:"port_number,omitempty"`
Status *entity.PortStatus `json:"status,omitempty"` // Add status field
Bandwidth *string `json:"bandwidth,omitempty"` // Add bandwidth field
} }
type UpdateCustomerByPortDTO struct { type UpdateCustomerByPortDTO struct {
PortNumber int `json:"port_number" binding:"required,min=1"` PortNumber int `json:"port_number" binding:"required,min=1"`
NewCustomerName *string `json:"new_customer_name,omitempty"` // null to remove, string to update/assign NewCustomerName *string `json:"new_customer_name,omitempty"`
Status *entity.PortStatus `json:"status,omitempty"` // Add status field
Bandwidth *string `json:"bandwidth,omitempty"` // Add bandwidth field
} }
type BulkUpdateCustomersByPortDTO struct { type BulkUpdateCustomersByPortDTO struct {

View File

@ -1,6 +1,10 @@
package req package req
import "github.com/google/uuid" import (
"users_management/m/model/entity"
"github.com/google/uuid"
)
type DevicePort struct { type DevicePort struct {
DeviceID uuid.UUID `json:"device_id"` DeviceID uuid.UUID `json:"device_id"`
@ -16,6 +20,8 @@ type UpdateDevicePort struct {
type AssignCustomerToPortDTO struct { type AssignCustomerToPortDTO struct {
CustomerName string `json:"customer_name" binding:"required"` CustomerName string `json:"customer_name" binding:"required"`
PortNumber *int `json:"port_number,omitempty"` // Optional, will auto-assign if not provided PortNumber *int `json:"port_number,omitempty"` // Optional, will auto-assign if not provided
Status *entity.PortStatus `json:"status,omitempty"` // Add status field
Bandwidth *string `json:"bandwidth,omitempty"` // Add bandwidth field
} }
type UpdatePortUsageDTO struct { type UpdatePortUsageDTO struct {
@ -25,6 +31,8 @@ type UpdatePortUsageDTO struct {
type PortAssignmentDTO struct { type PortAssignmentDTO struct {
PortNumber int `json:"port_number" binding:"required,min=1"` PortNumber int `json:"port_number" binding:"required,min=1"`
CustomerName *string `json:"customer_name,omitempty"` CustomerName *string `json:"customer_name,omitempty"`
Status *entity.PortStatus `json:"status,omitempty" validate:"omitempty,portstatus"`
Bandwidth *string `json:"bandwidth,omitempty" validate:"omitempty,min=1"`
} }
type UpdatePortAssignmentsDTO struct { type UpdatePortAssignmentsDTO struct {

View File

@ -2,6 +2,8 @@ package res
import ( import (
"time" "time"
"users_management/m/model/entity"
"github.com/google/uuid" "github.com/google/uuid"
) )
@ -47,4 +49,6 @@ type PortAssignmentResponse struct {
PortNumber int `json:"port_number"` PortNumber int `json:"port_number"`
CustomerName *string `json:"customer_name"` CustomerName *string `json:"customer_name"`
IsOccupied bool `json:"is_occupied"` IsOccupied bool `json:"is_occupied"`
Status entity.PortStatus `json:"status"` // Add status field
Bandwidth *string `json:"bandwidth"` // Add bandwidth field
} }

View File

@ -10,6 +10,15 @@ import (
"github.com/google/uuid" "github.com/google/uuid"
) )
type PortStatus string
const (
PortStatusOn PortStatus = "on"
PortStatusOff PortStatus = "off"
PortStatusDyingGasp PortStatus = "dyingGasp"
PortStatusLOS PortStatus = "los"
)
type StringSlice []string type StringSlice []string
func (s StringSlice) Value() (driver.Value, error) { func (s StringSlice) Value() (driver.Value, error) {
@ -47,12 +56,16 @@ func (s *StringSlice) Scan(value interface{}) error {
type PortAssignment struct { type PortAssignment struct {
PortNumber int `json:"port_number"` PortNumber int `json:"port_number"`
CustomerName *string `json:"customer_name"` // Nullable for empty ports CustomerName *string `json:"customer_name"` // Nullable for empty ports
Status PortStatus `json:"status"` // Add status field
Bandwidth *string `json:"bandwidth"` // Add bandwidth field (nullable)
} }
type PortAssignmentResponse struct { type PortAssignmentResponse struct {
PortNumber int `json:"port_number"` PortNumber int `json:"port_number"`
CustomerName *string `json:"customer_name"` CustomerName *string `json:"customer_name"`
IsOccupied bool `json:"is_occupied"` IsOccupied bool `json:"is_occupied"`
Status PortStatus `json:"status"` // Add status field
Bandwidth *string `json:"bandwidth"` // Add bandwidth field
} }
type PortAssignments []PortAssignment type PortAssignments []PortAssignment
@ -105,6 +118,9 @@ type DevicePort struct {
// Helper method to get customer names with port numbers // Helper method to get customer names with port numbers
func (dp *DevicePort) GetCustomerNamesWithPorts() []string { func (dp *DevicePort) GetCustomerNamesWithPorts() []string {
if dp.Device.DeviceType == "OTB" {
return []string{} // OTB devices do not have port assignments
}
var result []string var result []string
for _, assignment := range dp.PortAssignments { for _, assignment := range dp.PortAssignments {
if assignment.CustomerName != nil && *assignment.CustomerName != "" { if assignment.CustomerName != nil && *assignment.CustomerName != "" {
@ -124,6 +140,9 @@ func (dp *DevicePort) GetCustomerNamesWithPorts() []string {
// Helper method to get only customer names // Helper method to get only customer names
func (dp *DevicePort) GetCustomerNames() []string { func (dp *DevicePort) GetCustomerNames() []string {
if dp.Device.DeviceType == "OTB" {
return []string{} // OTB devices do not have port assignments
}
var result []string var result []string
for _, assignment := range dp.PortAssignments { for _, assignment := range dp.PortAssignments {
if assignment.CustomerName != nil && *assignment.CustomerName != "" { if assignment.CustomerName != nil && *assignment.CustomerName != "" {
@ -143,6 +162,11 @@ func (dp *DevicePort) GetCustomerNames() []string {
func (dp *DevicePort) GetPortAssignmentsWithDetails() []PortAssignmentResponse { func (dp *DevicePort) GetPortAssignmentsWithDetails() []PortAssignmentResponse {
// If port_used is 0, return empty array // If port_used is 0, return empty array
if dp.Device.DeviceType == "OTB" {
return []PortAssignmentResponse{}
}
if dp.PortUsed == 0 { if dp.PortUsed == 0 {
return []PortAssignmentResponse{} return []PortAssignmentResponse{}
} }
@ -150,12 +174,12 @@ func (dp *DevicePort) GetPortAssignmentsWithDetails() []PortAssignmentResponse {
var result []PortAssignmentResponse var result []PortAssignmentResponse
// Step 1: Create a map of all port assignments // Step 1: Create a map of all port assignments
portMap := make(map[int]*string) portMap := make(map[int]*PortAssignment)
maxPort := 0 maxPort := 0
if len(dp.PortAssignments) > 0 { if len(dp.PortAssignments) > 0 {
for _, assignment := range dp.PortAssignments { for _, assignment := range dp.PortAssignments {
portMap[assignment.PortNumber] = assignment.CustomerName portMap[assignment.PortNumber] = &assignment
if assignment.PortNumber > maxPort { if assignment.PortNumber > maxPort {
maxPort = assignment.PortNumber maxPort = assignment.PortNumber
} }
@ -164,8 +188,18 @@ func (dp *DevicePort) GetPortAssignmentsWithDetails() []PortAssignmentResponse {
// Fallback: use CustomerNames array if PortAssignments is empty // Fallback: use CustomerNames array if PortAssignments is empty
for i, name := range dp.CustomerNames { for i, name := range dp.CustomerNames {
portNum := i + 1 portNum := i + 1
if name != "" || portNum <= dp.PortUsed {
var customerName *string
if name != "" { if name != "" {
portMap[portNum] = &name customerName = &name
}
assignment := PortAssignment{
PortNumber: portNum,
CustomerName: customerName,
Status: PortStatusOn, // Default status
Bandwidth: nil, // Default bandwidth
}
portMap[portNum] = &assignment
if portNum > maxPort { if portNum > maxPort {
maxPort = portNum maxPort = portNum
} }
@ -179,10 +213,9 @@ func (dp *DevicePort) GetPortAssignmentsWithDetails() []PortAssignmentResponse {
} }
// Step 2: Determine which ports should be occupied // Step 2: Determine which ports should be occupied
// First, collect all ports that have customers
portsWithCustomers := make([]int, 0) portsWithCustomers := make([]int, 0)
for portNum, customerName := range portMap { for portNum, assignment := range portMap {
if customerName != nil && *customerName != "" { if assignment.CustomerName != nil && *assignment.CustomerName != "" {
portsWithCustomers = append(portsWithCustomers, portNum) portsWithCustomers = append(portsWithCustomers, portNum)
} }
} }
@ -213,20 +246,33 @@ func (dp *DevicePort) GetPortAssignmentsWithDetails() []PortAssignmentResponse {
// Step 4: Create the result for ALL ports up to maxPort // Step 4: Create the result for ALL ports up to maxPort
for i := 1; i <= maxPort; i++ { for i := 1; i <= maxPort; i++ {
var customerName *string var customerName *string
if portMap[i] != nil { var status PortStatus = PortStatusOff // Default status for unoccupied ports
customerName = portMap[i] var bandwidth *string
if assignment := portMap[i]; assignment != nil {
customerName = assignment.CustomerName
status = assignment.Status
bandwidth = assignment.Bandwidth
} else if occupiedPorts[i] {
// Port is occupied but no assignment data - use defaults
status = PortStatusOn
} }
result = append(result, PortAssignmentResponse{ result = append(result, PortAssignmentResponse{
PortNumber: i, PortNumber: i,
CustomerName: customerName, CustomerName: customerName,
IsOccupied: occupiedPorts[i], IsOccupied: occupiedPorts[i],
Status: status,
Bandwidth: bandwidth,
}) })
} }
return result return result
} }
func (dp *DevicePort) GetCustomerNamesOnly() []string { func (dp *DevicePort) GetCustomerNamesOnly() []string {
if dp.Device.DeviceType == "OTB" {
return []string{} // OTB devices do not have port assignments
}
var result []string var result []string
// If we have PortAssignments data, use it // If we have PortAssignments data, use it

View File

@ -108,6 +108,8 @@ func (r *deviceDetailsRepo) UpdateCustomerByPort(deviceID uuid.UUID, update req.
devicePort.PortAssignments[i] = entity.PortAssignment{ devicePort.PortAssignments[i] = entity.PortAssignment{
PortNumber: i + 1, PortNumber: i + 1,
CustomerName: nil, CustomerName: nil,
Status: entity.PortStatusOff,
Bandwidth: nil,
} }
} }
} }
@ -129,13 +131,26 @@ func (r *deviceDetailsRepo) UpdateCustomerByPort(deviceID uuid.UUID, update req.
} }
} }
// Update the port assignment
if update.NewCustomerName == nil { if update.NewCustomerName == nil {
// Remove customer from port // Remove customer from port
devicePort.PortAssignments[portIndex].CustomerName = nil devicePort.PortAssignments[portIndex].CustomerName = nil
devicePort.PortAssignments[portIndex].Status = entity.PortStatusOff
devicePort.PortAssignments[portIndex].Bandwidth = nil
} else { } else {
// Assign/update customer on port // Assign/update customer on port
devicePort.PortAssignments[portIndex].CustomerName = update.NewCustomerName devicePort.PortAssignments[portIndex].CustomerName = update.NewCustomerName
// Update status if provided, otherwise default to "on"
if update.Status != nil {
devicePort.PortAssignments[portIndex].Status = *update.Status
} else {
devicePort.PortAssignments[portIndex].Status = entity.PortStatusOn
}
// Update bandwidth if provided
if update.Bandwidth != nil {
devicePort.PortAssignments[portIndex].Bandwidth = update.Bandwidth
}
} }
// Update counters and backward compatibility fields // Update counters and backward compatibility fields
@ -323,6 +338,8 @@ func (r *deviceDetailsRepo) UpdatePortAssignments(deviceID uuid.UUID, assignment
devicePort.PortAssignments[i] = entity.PortAssignment{ devicePort.PortAssignments[i] = entity.PortAssignment{
PortNumber: i + 1, PortNumber: i + 1,
CustomerName: nil, CustomerName: nil,
Status: entity.PortStatusOff,
Bandwidth: nil,
} }
} }
} }
@ -345,6 +362,20 @@ func (r *deviceDetailsRepo) UpdatePortAssignments(deviceID uuid.UUID, assignment
if portIndex < len(devicePort.PortAssignments) { if portIndex < len(devicePort.PortAssignments) {
devicePort.PortAssignments[portIndex].PortNumber = assignment.PortNumber devicePort.PortAssignments[portIndex].PortNumber = assignment.PortNumber
devicePort.PortAssignments[portIndex].CustomerName = assignment.CustomerName devicePort.PortAssignments[portIndex].CustomerName = assignment.CustomerName
// Update status if provided, otherwise default based on customer presence
if assignment.Status != nil {
devicePort.PortAssignments[portIndex].Status = *assignment.Status
} else if assignment.CustomerName != nil && *assignment.CustomerName != "" {
devicePort.PortAssignments[portIndex].Status = entity.PortStatusOn
} else {
devicePort.PortAssignments[portIndex].Status = entity.PortStatusOff
}
// Update bandwidth if provided
if assignment.Bandwidth != nil {
devicePort.PortAssignments[portIndex].Bandwidth = assignment.Bandwidth
}
} }
} }
@ -693,6 +724,8 @@ func (r *deviceDetailsRepo) AssignCustomerToPort(deviceID uuid.UUID, customerNam
devicePort.PortAssignments[i] = entity.PortAssignment{ devicePort.PortAssignments[i] = entity.PortAssignment{
PortNumber: i + 1, PortNumber: i + 1,
CustomerName: nil, CustomerName: nil,
Status: entity.PortStatusOff, // Default status
Bandwidth: nil,
} }
} }
} }
@ -734,6 +767,7 @@ func (r *deviceDetailsRepo) AssignCustomerToPort(deviceID uuid.UUID, customerNam
// Assign customer to port // Assign customer to port
devicePort.PortAssignments[portIndex].CustomerName = &customerName devicePort.PortAssignments[portIndex].CustomerName = &customerName
devicePort.PortAssignments[portIndex].Status = entity.PortStatusOn // Default to "on" when assigning
// Update both PortAssignments and CustomerNames for backward compatibility // Update both PortAssignments and CustomerNames for backward compatibility
r.updateDevicePortCounters(&devicePort) r.updateDevicePortCounters(&devicePort)

View File

@ -112,31 +112,29 @@ func ConvertToDeviceDetailsResponse(device entity.DeviceDetails, geocoder servic
towerInfos = append(towerInfos, info) towerInfos = append(towerInfos, info)
} }
// Convert port assignments // REPLACE ALL THE EXISTING PORT ASSIGNMENT CODE WITH THIS:
entityPortAssignments := device.DevicePort.GetPortAssignmentsWithDetails() // Convert port assignments - only for ODP devices
resPortAssignments := make([]res.PortAssignmentResponse, len(entityPortAssignments)) var finalPortAssignments []res.PortAssignmentResponse
for i, pa := range entityPortAssignments { var customerNames []string
resPortAssignments[i] = res.PortAssignmentResponse{
PortNumber: pa.PortNumber,
CustomerName: pa.CustomerName,
IsOccupied: pa.IsOccupied,
}
}
portAssignments := device.DevicePort.GetPortAssignmentsWithDetails() if device.DeviceType == "ODP" {
// Only ODP devices have port assignments and customers
entityPortAssignments := device.DevicePort.GetPortAssignmentsWithDetails()
// Ensure we show all ports up to PortAmount // Ensure we show all ports up to PortAmount
portAssignmentMap := make(map[int]res.PortAssignmentResponse) portAssignmentMap := make(map[int]res.PortAssignmentResponse)
for _, pa := range portAssignments { for _, pa := range entityPortAssignments {
portAssignmentMap[pa.PortNumber] = res.PortAssignmentResponse{ portAssignmentMap[pa.PortNumber] = res.PortAssignmentResponse{
PortNumber: pa.PortNumber, PortNumber: pa.PortNumber,
CustomerName: pa.CustomerName, CustomerName: pa.CustomerName,
IsOccupied: pa.IsOccupied, IsOccupied: pa.IsOccupied,
Status: pa.Status,
Bandwidth: pa.Bandwidth,
} }
} }
// Fill in missing ports // Fill in missing ports
finalPortAssignments := make([]res.PortAssignmentResponse, 0) finalPortAssignments = make([]res.PortAssignmentResponse, 0)
for i := 1; i <= device.PortAmount; i++ { for i := 1; i <= device.PortAmount; i++ {
if assignment, exists := portAssignmentMap[i]; exists { if assignment, exists := portAssignmentMap[i]; exists {
finalPortAssignments = append(finalPortAssignments, assignment) finalPortAssignments = append(finalPortAssignments, assignment)
@ -145,14 +143,21 @@ func ConvertToDeviceDetailsResponse(device entity.DeviceDetails, geocoder servic
PortNumber: i, PortNumber: i,
CustomerName: nil, CustomerName: nil,
IsOccupied: false, IsOccupied: false,
Status: entity.PortStatusOff,
Bandwidth: nil,
}) })
} }
} }
// Get customer names // Get customer names for ODP devices
customerNames := device.DevicePort.GetCustomerNamesWithPorts() customerNames = device.DevicePort.GetCustomerNamesWithPorts()
} else {
// OTB and other device types don't have customers or detailed port assignments
finalPortAssignments = []res.PortAssignmentResponse{}
customerNames = []string{}
}
// DIRECT FIX: Use device.GetAllImageURLs() method directly // Get all image URLs
allImageURLs := device.GetAllImageURLs() allImageURLs := device.GetAllImageURLs()
// Handle empty slice vs nil for JSON response // Handle empty slice vs nil for JSON response
@ -171,13 +176,13 @@ func ConvertToDeviceDetailsResponse(device entity.DeviceDetails, geocoder servic
PortAmount: device.PortAmount, PortAmount: device.PortAmount,
PortUsed: device.DevicePort.PortUsed, PortUsed: device.DevicePort.PortUsed,
PortAvailable: device.DevicePort.PortAvailable, PortAvailable: device.DevicePort.PortAvailable,
CustomerNames: customerNames, CustomerNames: customerNames, // Empty for OTB
PortAssignments: finalPortAssignments, PortAssignments: finalPortAssignments, // Empty for OTB
Province: device.Province, Province: device.Province,
City: device.City, City: device.City,
District: device.District, District: device.District,
ImageURL: device.ImageURL, // Primary image ImageURL: device.ImageURL,
ImageURLs: allImageURLs, // All images using device.GetAllImageURLs() ImageURLs: allImageURLs,
Backbones: backboneInfos, Backbones: backboneInfos,
Fishbones: fishboneInfos, Fishbones: fishboneInfos,
Towers: towerInfos, Towers: towerInfos,

View File

@ -0,0 +1,22 @@
package validation
import (
"users_management/m/model/entity"
"github.com/go-playground/validator/v10"
)
// RegisterPortValidators registers port-related validation rules
func RegisterPortValidators(validate *validator.Validate) {
validate.RegisterValidation("portstatus", validatePortStatus)
}
// validatePortStatus checks if the value is a valid PortStatus
func validatePortStatus(fl validator.FieldLevel) bool {
value := fl.Field().String()
switch entity.PortStatus(value) {
case entity.PortStatusOn, entity.PortStatusOff, entity.PortStatusDyingGasp, entity.PortStatusLOS:
return true
default:
return false
}
}

View File

@ -14,6 +14,8 @@ func RegisterCustomValidators(validate *validator.Validate) {
validate.RegisterValidation("alphanumunderscore", validateAlphaNumUnderscore) validate.RegisterValidation("alphanumunderscore", validateAlphaNumUnderscore)
validate.RegisterValidation("alphaspace", validateAlphaSpace) // Add this validate.RegisterValidation("alphaspace", validateAlphaSpace) // Add this
validate.RegisterValidation("alphanum", validateAlphaNum) // Add this validate.RegisterValidation("alphanum", validateAlphaNum) // Add this
RegisterPortValidators(validate) // Ensure port validators are registered
} }
// validateNoSpace checks that the field contains no spaces // validateNoSpace checks that the field contains no spaces