diff --git a/model/dto/req/fishbone_dto.go b/model/dto/req/fishbone_dto.go index 1a113dc..7001736 100644 --- a/model/dto/req/fishbone_dto.go +++ b/model/dto/req/fishbone_dto.go @@ -6,7 +6,7 @@ type FishboneDTO struct { FishboneCode string `json:"fishbone_code" validate:"required,min=3"` BackboneID uuid.UUID `json:"bb_id" validate:"required"` DeviceStartID uuid.UUID `json:"dev_start_id" validate:"required"` - DeviceEndID uuid.UUID `json:"dev_end_id" validate:"required"` + DeviceEndID *uuid.UUID `json:"dev_end_id" validate:"omitempty"` CoreAmount int `json:"core_amount" validate:"required,min=1"` } diff --git a/model/dto/res/fishbone_res.go b/model/dto/res/fishbone_res.go index bd29388..9996037 100644 --- a/model/dto/res/fishbone_res.go +++ b/model/dto/res/fishbone_res.go @@ -13,7 +13,7 @@ type FishboneResponse struct { DeviceStart string `json:"device_start"` DeviceEnd string `json:"device_end"` DeviceStartID uuid.UUID `json:"device_start_id"` - DeviceEndID uuid.UUID `json:"device_end_id"` + DeviceEndID *uuid.UUID `json:"device_end_id"` CoreAmount int `json:"core_amount"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` @@ -23,7 +23,7 @@ type FishboneDetailResponse struct { ID uuid.UUID `json:"id"` FishboneCode string `json:"fishbone_code"` DeviceStartID uuid.UUID `json:"device_start_id"` - DeviceEndID uuid.UUID `json:"device_end_id"` + DeviceEndID *uuid.UUID `json:"device_end_id"` BackboneCode string `json:"backbone_code"` CoreAmount int `json:"core_amount"` CreatedAt time.Time `json:"created_at"` diff --git a/model/entity/fishbone.go b/model/entity/fishbone.go index 788d7aa..c30e78f 100644 --- a/model/entity/fishbone.go +++ b/model/entity/fishbone.go @@ -10,7 +10,7 @@ type Fishbone struct { FishboneCode string `json:"fishbone_code" gorm:"unique"` BackboneID uuid.UUID `json:"bb_id" gorm:"type:uuid;column:bb_id"` DeviceStartID uuid.UUID `json:"dev_start_id" gorm:"type:uuid;column:dev_start_id"` - DeviceEndID uuid.UUID `json:"dev_end_id" gorm:"type:uuid;column:dev_end_id"` + DeviceEndID *uuid.UUID `json:"dev_end_id" gorm:"type:uuid;column:dev_end_id"` CoreAmount int `json:"core_amount" gorm:"column:core_amount"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` diff --git a/usecase/fishbone_usecase.go b/usecase/fishbone_usecase.go index fa01e0e..a4c9a12 100644 --- a/usecase/fishbone_usecase.go +++ b/usecase/fishbone_usecase.go @@ -72,45 +72,23 @@ func (u *fishboneUseCase) CreateFishbone(fishbone req.FishboneDTO) error { return fmt.Errorf("start device not found: %w", err) } - var endDevice entity.Device - if err := tx.Set("gorm:query_option", "FOR UPDATE"). - Where("id = ?", fishbone.DeviceEndID).First(&endDevice).Error; err != nil { - return fmt.Errorf("end device not found: %w", err) - } - // Validate device types if strings.ToLower(string(startDevice.DeviceType)) != "closure" { return fmt.Errorf("start device must be of type closure, got %s", startDevice.DeviceType) } - - if strings.ToUpper(string(endDevice.DeviceType)) != "ODP" { - return fmt.Errorf("end device must be of type ODP, got %s", endDevice.DeviceType) - } - // Check port availability with locking + // Check port availability for start device var startDevicePort entity.DevicePort if err := tx.Set("gorm:query_option", "FOR UPDATE"). Where("device_id = ?", fishbone.DeviceStartID).First(&startDevicePort).Error; err != nil { return fmt.Errorf("start device port record not found: %w", err) } - var endDevicePort entity.DevicePort - if err := tx.Set("gorm:query_option", "FOR UPDATE"). - Where("device_id = ?", fishbone.DeviceEndID).First(&endDevicePort).Error; err != nil { - return fmt.Errorf("end device port record not found: %w", err) - } - - // Validate port availability if startDevicePort.PortAvailable < 1 && startDevice.DeviceType == "ODP" { - return fmt.Errorf("start device has no available ports (available: %d, required: 1)", + return fmt.Errorf("start device has no available ports (available: %d, required: 1)", startDevicePort.PortAvailable) } - if endDevicePort.PortAvailable < fishbone.CoreAmount { - return fmt.Errorf("end device has insufficient available ports (available: %d, required: %d)", - endDevicePort.PortAvailable, fishbone.CoreAmount) - } - newFishbone := entity.Fishbone{ ID: uuid.New(), FishboneCode: fishbone.FishboneCode, @@ -127,13 +105,33 @@ func (u *fishboneUseCase) CreateFishbone(fishbone req.FishboneDTO) error { return err } - // Update port usage for both devices + // Update port usage for start device if err := u.updateDevicePortUsageInTx(tx, fishbone.DeviceStartID); err != nil { return fmt.Errorf("failed to update start device port usage: %w", err) } - if err := u.updateDevicePortUsageInTx(tx, fishbone.DeviceEndID); err != nil { - return fmt.Errorf("failed to update end device port usage: %w", err) + // Only validate and update end device if provided (it is optional) + if fishbone.DeviceEndID != nil { + var endDevice entity.Device + if err := tx.Set("gorm:query_option", "FOR UPDATE"). + Where("id = ?", *fishbone.DeviceEndID).First(&endDevice).Error; err != nil { + return fmt.Errorf("end device not found: %w", err) + } + if strings.ToUpper(string(endDevice.DeviceType)) != "ODP" { + return fmt.Errorf("end device must be of type ODP, got %s", endDevice.DeviceType) + } + var endDevicePort entity.DevicePort + if err := tx.Set("gorm:query_option", "FOR UPDATE"). + Where("device_id = ?", *fishbone.DeviceEndID).First(&endDevicePort).Error; err != nil { + return fmt.Errorf("end device port record not found: %w", err) + } + if endDevicePort.PortAvailable < fishbone.CoreAmount { + return fmt.Errorf("end device has insufficient available ports (available: %d, required: %d)", + endDevicePort.PortAvailable, fishbone.CoreAmount) + } + if err := u.updateDevicePortUsageInTx(tx, *fishbone.DeviceEndID); err != nil { + return fmt.Errorf("failed to update end device port usage: %w", err) + } } return nil @@ -228,7 +226,9 @@ func (u *fishboneUseCase) UpdateFishbone(id uuid.UUID, fishbone req.UpdateFishbo updates := make(map[string]interface{}) devicesToUpdate := make(map[uuid.UUID]bool) devicesToUpdate[originalFishbone.DeviceStartID] = true - devicesToUpdate[originalFishbone.DeviceEndID] = true + if originalFishbone.DeviceEndID != nil { + devicesToUpdate[*originalFishbone.DeviceEndID] = true + } // Validate device type changes if devices are being changed if fishbone.DeviceStartID != nil && *fishbone.DeviceStartID != originalFishbone.DeviceStartID { @@ -244,7 +244,7 @@ func (u *fishboneUseCase) UpdateFishbone(id uuid.UUID, fishbone req.UpdateFishbo devicesToUpdate[*fishbone.DeviceStartID] = true } - if fishbone.DeviceEndID != nil && *fishbone.DeviceEndID != originalFishbone.DeviceEndID { + if fishbone.DeviceEndID != nil && (originalFishbone.DeviceEndID == nil || *fishbone.DeviceEndID != *originalFishbone.DeviceEndID) { var newEndDevice entity.Device if err := tx.Set("gorm:query_option", "FOR UPDATE"). Where("id = ?", *fishbone.DeviceEndID).First(&newEndDevice).Error; err != nil { diff --git a/utils/helper/fishboneHelperRes.go b/utils/helper/fishboneHelperRes.go index 21f1dab..323091b 100644 --- a/utils/helper/fishboneHelperRes.go +++ b/utils/helper/fishboneHelperRes.go @@ -19,7 +19,7 @@ func ConvertToFishboneResponses(fishbones []entity.Fishbone, totalFishbone map[u DeviceStart: fishbone.DeviceStart.DeviceCode, DeviceEnd: fishbone.DeviceEnd.DeviceCode, DeviceStartID: fishbone.DeviceStart.ID, - DeviceEndID: fishbone.DeviceEnd.ID, + DeviceEndID: fishbone.DeviceEndID, CoreAmount: fishbone.CoreAmount, CreatedAt: fishbone.CreatedAt, UpdatedAt: fishbone.UpdatedAt, @@ -36,7 +36,7 @@ func ConvertToFishboneDetailResponse(fishbone entity.Fishbone) res.FishboneDetai ID: fishbone.ID, FishboneCode: fishbone.FishboneCode, DeviceStartID: fishbone.DeviceStart.ID, - DeviceEndID: fishbone.DeviceEnd.ID, + DeviceEndID: fishbone.DeviceEndID, BackboneCode: fishbone.Backbone.BackboneCode, CoreAmount: fishbone.CoreAmount, CreatedAt: fishbone.CreatedAt, @@ -56,7 +56,7 @@ func ConvertToSimpleFishboneResponses(fishbones []entity.Fishbone) []res.Fishbon DeviceStart: fishbone.DeviceStart.DeviceCode, DeviceEnd: fishbone.DeviceEnd.DeviceCode, DeviceStartID: fishbone.DeviceStart.ID, - DeviceEndID: fishbone.DeviceEnd.ID, + DeviceEndID: fishbone.DeviceEndID, CoreAmount: fishbone.CoreAmount, CreatedAt: fishbone.CreatedAt, UpdatedAt: fishbone.UpdatedAt,