adding nearest device and some addtion feature more complex
This commit is contained in:
parent
d5f611e390
commit
32c775ab24
|
|
@ -0,0 +1,137 @@
|
||||||
|
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"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DeviceDetailsController struct {
|
||||||
|
deviceDetailsUC usecase.DeviceDetailsUseCase
|
||||||
|
rg *gin.RouterGroup
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDeviceDetailsController(deviceDetailsUC usecase.DeviceDetailsUseCase, rg *gin.RouterGroup) *DeviceDetailsController {
|
||||||
|
return &DeviceDetailsController{
|
||||||
|
deviceDetailsUC: deviceDetailsUC,
|
||||||
|
rg: rg,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *DeviceDetailsController) Route() {
|
||||||
|
deviceDetails := c.rg.Group("/device-details")
|
||||||
|
deviceDetails.Use(middleware.RequireAnyRole("Teknisi", "Admin", "Superadmin"))
|
||||||
|
{
|
||||||
|
deviceDetails.GET("", c.getAllDeviceDetails)
|
||||||
|
deviceDetails.POST("", c.createDeviceDetails)
|
||||||
|
deviceDetails.GET("/:id", c.getDeviceDetailsByID)
|
||||||
|
deviceDetails.PUT("/:id", c.updateDeviceDetails)
|
||||||
|
deviceDetails.DELETE("/:id", c.deleteDeviceDetails)
|
||||||
|
deviceDetails.POST("/:id/recalculate-ports", c.recalculatePortUsage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *DeviceDetailsController) getAllDeviceDetails(ctx *gin.Context) {
|
||||||
|
devices, err := c.deviceDetailsUC.GetAllDeviceDetails()
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResponses(ctx, http.StatusInternalServerError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
common.SingleResponses(ctx, "Device details retrieved successfully", devices)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *DeviceDetailsController) createDeviceDetails(ctx *gin.Context) {
|
||||||
|
var request req.DeviceDetailsDTO
|
||||||
|
if err := ctx.ShouldBindJSON(&request); err != nil {
|
||||||
|
common.ErrorResponses(ctx, http.StatusBadRequest, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err := c.deviceDetailsUC.CreateDeviceDetails(request)
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResponses(ctx, http.StatusBadRequest, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
common.SingleResponses(ctx, "Device details created successfully", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *DeviceDetailsController) getDeviceDetailsByID(ctx *gin.Context) {
|
||||||
|
id := ctx.Param("id")
|
||||||
|
deviceID, err := uuid.Parse(id)
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResponses(ctx, http.StatusBadRequest, "Invalid device ID")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
device, err := c.deviceDetailsUC.GetDeviceDetailsByID(deviceID)
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResponses(ctx, http.StatusNotFound, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
common.SingleResponses(ctx, "Device details retrieved successfully", device)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *DeviceDetailsController) updateDeviceDetails(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.UpdateDeviceDetailsDTO
|
||||||
|
if err := ctx.ShouldBindJSON(&request); err != nil {
|
||||||
|
common.ErrorResponses(ctx, http.StatusBadRequest, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = c.deviceDetailsUC.UpdateDeviceDetails(deviceID, request)
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResponses(ctx, http.StatusBadRequest, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
common.SingleResponses(ctx, "Device details updated successfully", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *DeviceDetailsController) deleteDeviceDetails(ctx *gin.Context) {
|
||||||
|
id := ctx.Param("id")
|
||||||
|
deviceID, err := uuid.Parse(id)
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResponses(ctx, http.StatusBadRequest, "Invalid device ID")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = c.deviceDetailsUC.DeleteDeviceDetails(deviceID)
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResponses(ctx, http.StatusBadRequest, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
common.SingleResponses(ctx, "Device details deleted successfully", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *DeviceDetailsController) recalculatePortUsage(ctx *gin.Context) {
|
||||||
|
id := ctx.Param("id")
|
||||||
|
deviceID, err := uuid.Parse(id)
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResponses(ctx, http.StatusBadRequest, "Invalid device ID")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = c.deviceDetailsUC.RecalculatePortUsage(deviceID)
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResponses(ctx, http.StatusInternalServerError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
common.SingleResponses(ctx, "Port usage recalculated successfully", nil)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,255 @@
|
||||||
|
package controller
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"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 DeviceInspectionController struct {
|
||||||
|
inspectionUC usecase.DeviceInspectionUseCase
|
||||||
|
rg *gin.RouterGroup
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDeviceInspectionController(inspectionUC usecase.DeviceInspectionUseCase, rg *gin.RouterGroup) *DeviceInspectionController {
|
||||||
|
return &DeviceInspectionController{
|
||||||
|
inspectionUC: inspectionUC,
|
||||||
|
rg: rg,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *DeviceInspectionController) Route() {
|
||||||
|
inspections := c.rg.Group("/device-inspections")
|
||||||
|
{
|
||||||
|
// Teknisi can create and read their own inspections
|
||||||
|
inspections.POST("", middleware.RequireAnyRole("Teknisi", "Admin", "Superadmin"), c.createInspection)
|
||||||
|
inspections.GET("/my-inspections", middleware.RequireAnyRole("Teknisi", "Admin", "Superadmin"), c.getMyInspections)
|
||||||
|
|
||||||
|
// Admin and Superadmin can see all inspections
|
||||||
|
inspections.GET("", middleware.RequireAdminRole(), c.getAllInspections)
|
||||||
|
inspections.GET("/:id", middleware.RequireAnyRole("Teknisi", "Admin", "Superadmin"), c.getInspectionByID)
|
||||||
|
|
||||||
|
// Update inspections - teknisi can update their own, admin can update any
|
||||||
|
inspections.PUT("/:id", middleware.RequireAnyRole("Teknisi", "Admin", "Superadmin"), c.updateInspection)
|
||||||
|
|
||||||
|
// Only admin can approve inspections
|
||||||
|
inspections.PATCH("/:id/approve", middleware.RequireAdminRole(), c.approveInspection)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *DeviceInspectionController) createInspection(ctx *gin.Context) {
|
||||||
|
userID, exists := ctx.Get("userID")
|
||||||
|
if !exists {
|
||||||
|
common.ErrorResponses(ctx, http.StatusUnauthorized, "User ID not found")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
uid, ok := userID.(uuid.UUID)
|
||||||
|
if !ok {
|
||||||
|
common.ErrorResponses(ctx, http.StatusBadRequest, "Invalid user ID")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var inspectionDTO req.DeviceInspectionDTO
|
||||||
|
if err := ctx.ShouldBindJSON(&inspectionDTO); err != nil {
|
||||||
|
common.ErrorResponses(ctx, http.StatusBadRequest, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err := c.inspectionUC.CreateInspection(uid, inspectionDTO)
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResponses(ctx, http.StatusBadRequest, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
common.SingleResponses(ctx, "Device inspection created successfully", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *DeviceInspectionController) getAllInspections(ctx *gin.Context) {
|
||||||
|
page, _ := strconv.Atoi(ctx.DefaultQuery("page", "1"))
|
||||||
|
limit, _ := strconv.Atoi(ctx.DefaultQuery("limit", "10"))
|
||||||
|
|
||||||
|
if page < 1 {
|
||||||
|
page = 1
|
||||||
|
}
|
||||||
|
if limit < 1 || limit > 100 {
|
||||||
|
limit = 10
|
||||||
|
}
|
||||||
|
|
||||||
|
inspections, total, err := c.inspectionUC.GetAllInspections(page, limit)
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResponses(ctx, http.StatusInternalServerError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
response := gin.H{
|
||||||
|
"inspections": inspections,
|
||||||
|
"total": total,
|
||||||
|
"page": page,
|
||||||
|
"limit": limit,
|
||||||
|
"total_pages": (total + int64(limit) - 1) / int64(limit),
|
||||||
|
}
|
||||||
|
|
||||||
|
common.SingleResponses(ctx, "All inspections retrieved successfully", response)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *DeviceInspectionController) getMyInspections(ctx *gin.Context) {
|
||||||
|
userID, exists := ctx.Get("userID")
|
||||||
|
if !exists {
|
||||||
|
common.ErrorResponses(ctx, http.StatusUnauthorized, "User ID not found")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
uid, ok := userID.(uuid.UUID)
|
||||||
|
if !ok {
|
||||||
|
common.ErrorResponses(ctx, http.StatusBadRequest, "Invalid user ID")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
page, _ := strconv.Atoi(ctx.DefaultQuery("page", "1"))
|
||||||
|
limit, _ := strconv.Atoi(ctx.DefaultQuery("limit", "10"))
|
||||||
|
|
||||||
|
if page < 1 {
|
||||||
|
page = 1
|
||||||
|
}
|
||||||
|
if limit < 1 || limit > 100 {
|
||||||
|
limit = 10
|
||||||
|
}
|
||||||
|
|
||||||
|
inspections, total, err := c.inspectionUC.GetUserInspections(uid, page, limit)
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResponses(ctx, http.StatusInternalServerError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
response := gin.H{
|
||||||
|
"inspections": inspections,
|
||||||
|
"total": total,
|
||||||
|
"page": page,
|
||||||
|
"limit": limit,
|
||||||
|
"total_pages": (total + int64(limit) - 1) / int64(limit),
|
||||||
|
}
|
||||||
|
|
||||||
|
common.SingleResponses(ctx, "User inspections retrieved successfully", response)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *DeviceInspectionController) getInspectionByID(ctx *gin.Context) {
|
||||||
|
id := ctx.Param("id")
|
||||||
|
inspectionID, err := uuid.Parse(id)
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResponses(ctx, http.StatusBadRequest, "Invalid inspection ID")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
userID, exists := ctx.Get("userID")
|
||||||
|
if !exists {
|
||||||
|
common.ErrorResponses(ctx, http.StatusUnauthorized, "User ID not found")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
uid, ok := userID.(uuid.UUID)
|
||||||
|
if !ok {
|
||||||
|
common.ErrorResponses(ctx, http.StatusBadRequest, "Invalid user ID")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
userRole, exists := ctx.Get("userRole")
|
||||||
|
if !exists {
|
||||||
|
common.ErrorResponses(ctx, http.StatusUnauthorized, "User role not found")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
role, ok := userRole.(string)
|
||||||
|
if !ok {
|
||||||
|
common.ErrorResponses(ctx, http.StatusBadRequest, "Invalid user role")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the inspection first
|
||||||
|
inspection, err := c.inspectionUC.GetInspectionByID(inspectionID)
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResponses(ctx, http.StatusNotFound, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// For teknisi, check if they own this inspection
|
||||||
|
if role == "Teknisi" {
|
||||||
|
isOwner, err := c.inspectionUC.CheckInspectionOwnership(inspectionID, uid)
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResponses(ctx, http.StatusInternalServerError, "Error checking inspection ownership")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !isOwner {
|
||||||
|
common.ErrorResponses(ctx, http.StatusForbidden, "Unauthorized: you can only view your own inspections")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
common.SingleResponses(ctx, "Inspection retrieved successfully", inspection)
|
||||||
|
}
|
||||||
|
func (c *DeviceInspectionController) updateInspection(ctx *gin.Context) {
|
||||||
|
id := ctx.Param("id")
|
||||||
|
inspectionID, err := uuid.Parse(id)
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResponses(ctx, http.StatusBadRequest, "Invalid inspection ID")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
userID, exists := ctx.Get("userID")
|
||||||
|
if !exists {
|
||||||
|
common.ErrorResponses(ctx, http.StatusUnauthorized, "User ID not found")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
uid, ok := userID.(uuid.UUID)
|
||||||
|
if !ok {
|
||||||
|
common.ErrorResponses(ctx, http.StatusBadRequest, "Invalid user ID")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
userRole, _ := ctx.Get("userRole")
|
||||||
|
role, _ := userRole.(string)
|
||||||
|
|
||||||
|
var inspectionDTO req.UpdateDeviceInspectionDTO
|
||||||
|
if err := ctx.ShouldBindJSON(&inspectionDTO); err != nil {
|
||||||
|
common.ErrorResponses(ctx, http.StatusBadRequest, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = c.inspectionUC.UpdateInspection(inspectionID, uid, inspectionDTO, role)
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResponses(ctx, http.StatusBadRequest, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
common.SingleResponses(ctx, "Inspection updated successfully", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *DeviceInspectionController) approveInspection(ctx *gin.Context) {
|
||||||
|
id := ctx.Param("id")
|
||||||
|
inspectionID, err := uuid.Parse(id)
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResponses(ctx, http.StatusBadRequest, "Invalid inspection ID")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var approvalDTO req.ApproveInspectionDTO
|
||||||
|
if err := ctx.ShouldBindJSON(&approvalDTO); err != nil {
|
||||||
|
common.ErrorResponses(ctx, http.StatusBadRequest, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = c.inspectionUC.ApproveInspection(inspectionID, approvalDTO)
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResponses(ctx, http.StatusBadRequest, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
common.SingleResponses(ctx, "Inspection approval updated successfully", nil)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,101 @@
|
||||||
|
package controller
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"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 NearestDeviceController struct {
|
||||||
|
nearestDeviceUC usecase.NearestDeviceUseCase
|
||||||
|
rg *gin.RouterGroup
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNearestDeviceController(nearestDeviceUC usecase.NearestDeviceUseCase, rg *gin.RouterGroup) *NearestDeviceController {
|
||||||
|
return &NearestDeviceController{
|
||||||
|
nearestDeviceUC: nearestDeviceUC,
|
||||||
|
rg: rg,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *NearestDeviceController) Route() {
|
||||||
|
nearestDevices := c.rg.Group("/nearest-devices")
|
||||||
|
nearestDevices.Use(middleware.RequireAnyRole("Teknisi", "Admin", "Superadmin"))
|
||||||
|
{
|
||||||
|
nearestDevices.POST("/search", c.getNearestDevices)
|
||||||
|
nearestDevices.GET("/:id", c.getNearestDeviceByID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *NearestDeviceController) getNearestDevices(ctx *gin.Context) {
|
||||||
|
var request req.NearestDeviceDTO
|
||||||
|
if err := ctx.ShouldBindJSON(&request); err != nil {
|
||||||
|
common.ErrorResponses(ctx, http.StatusBadRequest, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
devices, err := c.nearestDeviceUC.GetNearestDevices(request)
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResponses(ctx, http.StatusBadRequest, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
response := gin.H{
|
||||||
|
"devices": devices,
|
||||||
|
"total": len(devices),
|
||||||
|
"search_params": gin.H{
|
||||||
|
"latitude": request.Latitude,
|
||||||
|
"longitude": request.Longitude,
|
||||||
|
"radius": request.Radius,
|
||||||
|
"province": request.Province,
|
||||||
|
"city": request.City,
|
||||||
|
"district": request.District,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
common.SingleResponses(ctx, "Nearest devices retrieved successfully", response)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *NearestDeviceController) getNearestDeviceByID(ctx *gin.Context) {
|
||||||
|
id := ctx.Param("id")
|
||||||
|
deviceID, err := uuid.Parse(id)
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResponses(ctx, http.StatusBadRequest, "Invalid device ID")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get user coordinates from query params
|
||||||
|
latStr := ctx.Query("lat")
|
||||||
|
lngStr := ctx.Query("lng")
|
||||||
|
|
||||||
|
if latStr == "" || lngStr == "" {
|
||||||
|
common.ErrorResponses(ctx, http.StatusBadRequest, "User coordinates (lat, lng) are required")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
userLat, err := strconv.ParseFloat(latStr, 64)
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResponses(ctx, http.StatusBadRequest, "Invalid latitude")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
userLng, err := strconv.ParseFloat(lngStr, 64)
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResponses(ctx, http.StatusBadRequest, "Invalid longitude")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
device, err := c.nearestDeviceUC.GetNearestDeviceByID(deviceID, userLat, userLng)
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResponses(ctx, http.StatusNotFound, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
common.SingleResponses(ctx, "Device details retrieved successfully", device)
|
||||||
|
}
|
||||||
|
|
@ -60,6 +60,9 @@ func (s *Server) setupController() {
|
||||||
controller.NewDevicePortController(s.ucManager.NewDevicePortUsecase(), rg).Route()
|
controller.NewDevicePortController(s.ucManager.NewDevicePortUsecase(), rg).Route()
|
||||||
controller.NewCountAssetsController(s.ucManager.NewCountAssetsUsecase(), rg).Route()
|
controller.NewCountAssetsController(s.ucManager.NewCountAssetsUsecase(), rg).Route()
|
||||||
controller.NewActivityLogController(s.ucManager.NewActivityLogUsecase(), rg).Route()
|
controller.NewActivityLogController(s.ucManager.NewActivityLogUsecase(), rg).Route()
|
||||||
|
controller.NewDeviceInspectionController(s.ucManager.NewDeviceInspectionUsecase(), rg).Route()
|
||||||
|
controller.NewNearestDeviceController(s.ucManager.NewNearestDeviceUsecase(), rg).Route()
|
||||||
|
controller.NewDeviceDetailsController(s.ucManager.NewDeviceDetailsUsecase(), rg).Route()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,10 @@ type RepositoryManager interface {
|
||||||
NewDevicePortRepository() repository.DevicePortRepo
|
NewDevicePortRepository() repository.DevicePortRepo
|
||||||
NewCountAssetsRepository() repository.CountAssetsRepo
|
NewCountAssetsRepository() repository.CountAssetsRepo
|
||||||
NewActivityLogRepository() repository.ActivityLogRepo
|
NewActivityLogRepository() repository.ActivityLogRepo
|
||||||
|
NewDeviceInspectionRepository() repository.DeviceInspectionRepo
|
||||||
|
NewNearestDeviceRepository() repository.NearestDeviceRepo
|
||||||
|
|
||||||
|
NewDeviceDetailsRepository() repository.DeviceDetailsRepo
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -25,6 +29,13 @@ func NewRepositoryManager(infra InfraManager) RepositoryManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (rm *repositoryManager) NewDeviceDetailsRepository() repository.DeviceDetailsRepo {
|
||||||
|
return repository.NewDeviceDetailsRepo(rm.infra.Conn())
|
||||||
|
}
|
||||||
|
func (rm *repositoryManager) NewNearestDeviceRepository() repository.NearestDeviceRepo {
|
||||||
|
return repository.NewNearestDeviceRepo(rm.infra.Conn())
|
||||||
|
}
|
||||||
|
|
||||||
func (rm *repositoryManager) NewUserRepository() repository.UsersRepo {
|
func (rm *repositoryManager) NewUserRepository() repository.UsersRepo {
|
||||||
return repository.NewUsersRepo(rm.infra.Conn())
|
return repository.NewUsersRepo(rm.infra.Conn())
|
||||||
}
|
}
|
||||||
|
|
@ -56,3 +67,7 @@ func (rm *repositoryManager) NewCountAssetsRepository() repository.CountAssetsRe
|
||||||
func (rm *repositoryManager) NewActivityLogRepository() repository.ActivityLogRepo {
|
func (rm *repositoryManager) NewActivityLogRepository() repository.ActivityLogRepo {
|
||||||
return repository.NewActivityLogRepo(rm.infra.Conn())
|
return repository.NewActivityLogRepo(rm.infra.Conn())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (rm *repositoryManager) NewDeviceInspectionRepository() repository.DeviceInspectionRepo {
|
||||||
|
return repository.NewDeviceInspectionRepo(rm.infra.Conn())
|
||||||
|
}
|
||||||
|
|
@ -20,6 +20,12 @@ type UsecaseManager interface {
|
||||||
|
|
||||||
NewActivityLogUsecase() usecase.ActivityLogUseCase
|
NewActivityLogUsecase() usecase.ActivityLogUseCase
|
||||||
|
|
||||||
|
NewDeviceInspectionUsecase() usecase.DeviceInspectionUseCase
|
||||||
|
|
||||||
|
NewNearestDeviceUsecase() usecase.NearestDeviceUseCase
|
||||||
|
|
||||||
|
NewDeviceDetailsUsecase() usecase.DeviceDetailsUseCase
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type usecaseManager struct {
|
type usecaseManager struct {
|
||||||
|
|
@ -34,6 +40,20 @@ func NewUsecaseManager(repo RepositoryManager, cfg *config.Config) UsecaseManage
|
||||||
return &usecaseManager{repo: repo,cfg: cfg , geocoder: cachedGeocoder}
|
return &usecaseManager{repo: repo,cfg: cfg , geocoder: cachedGeocoder}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (um *usecaseManager) NewDeviceDetailsUsecase() usecase.DeviceDetailsUseCase {
|
||||||
|
return usecase.NewDeviceDetailsUseCase(
|
||||||
|
um.repo.NewDeviceDetailsRepository(),
|
||||||
|
um.geocoder,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (um *usecaseManager) NewNearestDeviceUsecase() usecase.NearestDeviceUseCase {
|
||||||
|
return usecase.NewNearestDeviceUseCase(
|
||||||
|
um.repo.NewNearestDeviceRepository(),
|
||||||
|
um.geocoder,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
func (um *usecaseManager) NewUserUsecase() usecase.UsersUsecase {
|
func (um *usecaseManager) NewUserUsecase() usecase.UsersUsecase {
|
||||||
return usecase.NewUsersUsecase(um.repo.NewUserRepository())
|
return usecase.NewUsersUsecase(um.repo.NewUserRepository())
|
||||||
}
|
}
|
||||||
|
|
@ -47,11 +67,20 @@ func (um *usecaseManager) NewDeviceUsecase() usecase.DeviceUseCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (um *usecaseManager) NewBackboneUsecase() usecase.BackboneUseCase {
|
func (um *usecaseManager) NewBackboneUsecase() usecase.BackboneUseCase {
|
||||||
return usecase.NewBackboneUseCase(um.repo.NewBackboneRepository(),um.repo.NewFishboneRepository())
|
return usecase.NewBackboneUseCase(
|
||||||
|
um.repo.NewBackboneRepository(),
|
||||||
|
um.repo.NewFishboneRepository(),
|
||||||
|
um.repo.NewDeviceDetailsRepository(), // Add this
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (um *usecaseManager) NewFishboneUsecase() usecase.FishboneUseCase {
|
func (um *usecaseManager) NewFishboneUsecase() usecase.FishboneUseCase {
|
||||||
return usecase.NewFishboneUseCase(um.repo.NewFishboneRepository())
|
return usecase.NewFishboneUseCase(
|
||||||
|
um.repo.NewFishboneRepository(),
|
||||||
|
um.repo.NewBackboneRepository(),
|
||||||
|
um.repo.NewDeviceDetailsRepository(), // Add this line
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (um *usecaseManager) NewTowerUsecase() usecase.TowerUseCase {
|
func (um *usecaseManager) NewTowerUsecase() usecase.TowerUseCase {
|
||||||
|
|
@ -70,3 +99,11 @@ func (um *usecaseManager) NewCountAssetsUsecase() usecase.CountAssetsUseCase {
|
||||||
func (um *usecaseManager) NewActivityLogUsecase() usecase.ActivityLogUseCase {
|
func (um *usecaseManager) NewActivityLogUsecase() usecase.ActivityLogUseCase {
|
||||||
return usecase.NewActivityLogUseCase(um.repo.NewActivityLogRepository())
|
return usecase.NewActivityLogUseCase(um.repo.NewActivityLogRepository())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (um *usecaseManager) NewDeviceInspectionUsecase() usecase.DeviceInspectionUseCase {
|
||||||
|
return usecase.NewDeviceInspectionUseCase(
|
||||||
|
um.repo.NewDeviceInspectionRepository(),
|
||||||
|
um.NewActivityLogUsecase(),
|
||||||
|
service.NewGeocodingService(), // Add the geocoding service
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
package req
|
||||||
|
|
||||||
|
|
||||||
|
type DeviceDetailsDTO struct {
|
||||||
|
DeviceCode string `json:"device_code" validate:"required"`
|
||||||
|
DeviceType string `json:"device_type" validate:"required,oneof=OTB ODP"`
|
||||||
|
Longitude float64 `json:"longitude" validate:"required"`
|
||||||
|
Latitude float64 `json:"latitude" validate:"required"`
|
||||||
|
PortAmount int `json:"port_amount" validate:"required,min=1,max=100"`
|
||||||
|
Status string `json:"status" validate:"required,oneof=active inactive maintenance"`
|
||||||
|
Region *string `json:"region,omitempty" validate:"omitempty,min=3"`
|
||||||
|
Province *string `json:"province,omitempty" validate:"omitempty,min=3"`
|
||||||
|
City *string `json:"city,omitempty" validate:"omitempty,min=3"`
|
||||||
|
District *string `json:"district,omitempty" validate:"omitempty,min=3"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpdateDeviceDetailsDTO struct {
|
||||||
|
DeviceCode *string `json:"device_code,omitempty" validate:"omitempty,min=3"`
|
||||||
|
DeviceType *string `json:"device_type,omitempty" validate:"omitempty,oneof=OTB ODP"`
|
||||||
|
Longitude *float64 `json:"longitude,omitempty" validate:"omitempty,longitude"`
|
||||||
|
Latitude *float64 `json:"latitude,omitempty" validate:"omitempty,latitude"`
|
||||||
|
PortAmount *int `json:"port_amount,omitempty" validate:"omitempty,min=1,max=100"`
|
||||||
|
Status *string `json:"status,omitempty" validate:"omitempty,oneof=active inactive maintenance"`
|
||||||
|
Region *string `json:"region,omitempty" validate:"omitempty,min=3"`
|
||||||
|
Province *string `json:"province,omitempty" validate:"omitempty,min=3"`
|
||||||
|
City *string `json:"city,omitempty" validate:"omitempty,min=3"`
|
||||||
|
District *string `json:"district,omitempty" validate:"omitempty,min=3"`
|
||||||
|
}
|
||||||
|
|
@ -1,28 +1,25 @@
|
||||||
package req
|
package req
|
||||||
|
|
||||||
|
|
||||||
type DeviceDTO struct {
|
type DeviceDTO struct {
|
||||||
DeviceCode string `json:"device_code" validate:"required"`
|
DeviceCode string `json:"device_code" validate:"required"`
|
||||||
DeviceType string `json:"device_type" validate:"required"`
|
DeviceType string `json:"device_type" validate:"required"`
|
||||||
Longitude float64 `json:"longitude" validate:"required"`
|
Longitude float64 `json:"longitude" validate:"required"`
|
||||||
Latitude float64 `json:"latitude" validate:"required"`
|
Latitude float64 `json:"latitude" validate:"required"`
|
||||||
PortAmount int `json:"port_amount" validate:"required"`
|
PortAmount int `json:"port_amount" validate:"required"`
|
||||||
Status string `json:"status" validate:"required,oneof=active inactive maintenance"`
|
Status string `json:"status" validate:"required,oneof=active inactive maintenance"`
|
||||||
Region string `json:"region" validate:"required"`
|
Province *string `json:"province,omitempty" validate:"omitempty,min=3"`
|
||||||
Province string `json:"province" validate:"required"`
|
City *string `json:"city,omitempty" validate:"omitempty,min=3"`
|
||||||
City string `json:"city" validate:"required"`
|
District *string `json:"district,omitempty" validate:"omitempty,min=3"`
|
||||||
District string `json:"district" validate:"required"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type UpdateDeviceDTO struct {
|
type UpdateDeviceDTO struct {
|
||||||
DeviceCode *string `json:"device_code,omitempty" validate:"omitempty,min=3"`
|
DeviceCode *string `json:"device_code,omitempty" validate:"omitempty,min=3"`
|
||||||
DeviceType *string `json:"device_type,omitempty" validate:"omitempty,min=3"`
|
DeviceType *string `json:"device_type,omitempty" validate:"omitempty,min=3"`
|
||||||
Longitude *float64 `json:"longitude,omitempty" validate:"omitempty,longitude"`
|
Longitude *float64 `json:"longitude,omitempty" validate:"omitempty,longitude"`
|
||||||
Latitude *float64 `json:"latitude,omitempty" validate:"omitempty,latitude"`
|
Latitude *float64 `json:"latitude,omitempty" validate:"omitempty,latitude"`
|
||||||
PortAmount *int `json:"port_amount,omitempty" validate:"omitempty,min=1"`
|
PortAmount *int `json:"port_amount,omitempty" validate:"omitempty,min=1"`
|
||||||
Status *string `json:"status,omitempty" validate:"omitempty,oneof=active inactive maintenance"`
|
Status *string `json:"status,omitempty" validate:"omitempty,oneof=active inactive maintenance"`
|
||||||
Region *string `json:"region,omitempty" validate:"omitempty,min=3"`
|
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"`
|
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
package req
|
||||||
|
|
||||||
|
import "github.com/google/uuid"
|
||||||
|
|
||||||
|
type DeviceInspectionDTO struct {
|
||||||
|
DeviceID uuid.UUID `json:"device_id" validate:"required"`
|
||||||
|
BackboneID *uuid.UUID `json:"backbone_id,omitempty"`
|
||||||
|
FishboneID *uuid.UUID `json:"fishbone_id,omitempty"`
|
||||||
|
TowerID *uuid.UUID `json:"tower_id,omitempty"`
|
||||||
|
Status string `json:"status" validate:"required,oneof=pending in_progress completed maintenance"`
|
||||||
|
PortUsed string `json:"port_used" validate:"required"`
|
||||||
|
PortAvailable string `json:"port_available"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
ImageURL *string `json:"image_url,omitempty"`
|
||||||
|
InspectionPlacement struct {
|
||||||
|
Longitude float64 `json:"longitude" validate:"required"`
|
||||||
|
Latitude float64 `json:"latitude" validate:"required"`
|
||||||
|
} `json:"inspection_placement" validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpdateDeviceInspectionDTO struct {
|
||||||
|
BackboneID *uuid.UUID `json:"backbone_id,omitempty"`
|
||||||
|
FishboneID *uuid.UUID `json:"fishbone_id,omitempty"`
|
||||||
|
TowerID *uuid.UUID `json:"tower_id,omitempty"`
|
||||||
|
Status *string `json:"status,omitempty" validate:"omitempty,oneof=pending in_progress completed maintenance"`
|
||||||
|
PortUsed *string `json:"port_used,omitempty"`
|
||||||
|
Description *string `json:"description,omitempty"`
|
||||||
|
ImageURL *string `json:"image_url,omitempty"`
|
||||||
|
InspectionPlacement *struct {
|
||||||
|
Longitude float64 `json:"longitude"`
|
||||||
|
Latitude float64 `json:"latitude"`
|
||||||
|
} `json:"inspection_placement,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ApproveInspectionDTO struct {
|
||||||
|
InspectionApproval string `json:"inspection_approval" validate:"required,oneof=approved rejected"`
|
||||||
|
Notes string `json:"notes,omitempty"`
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
package req
|
||||||
|
|
||||||
|
type NearestDeviceDTO struct {
|
||||||
|
Longitude float64 `json:"longitude" validate:"required"`
|
||||||
|
Latitude float64 `json:"latitude" validate:"required"`
|
||||||
|
Radius float64 `json:"radius" validate:"omitempty,min=0.1,max=50"` // Default 5km, max 50km
|
||||||
|
Limit int `json:"limit" validate:"omitempty,min=1,max=100"` // Default 10, max 100
|
||||||
|
Province *string `json:"province,omitempty"`
|
||||||
|
City *string `json:"city,omitempty"`
|
||||||
|
District *string `json:"district,omitempty"`
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
package res
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DeviceDetailsResponse struct {
|
||||||
|
ID uuid.UUID `json:"id"`
|
||||||
|
DeviceCode string `json:"device_code"`
|
||||||
|
DeviceType string `json:"device_type"`
|
||||||
|
Address string `json:"address"`
|
||||||
|
Longitude float64 `json:"longitude"`
|
||||||
|
Latitude float64 `json:"latitude"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
PortAmount int `json:"port_amount"`
|
||||||
|
PortUsed int `json:"port_used"`
|
||||||
|
PortAvailable int `json:"port_available"`
|
||||||
|
Region *string `json:"region,omitempty"`
|
||||||
|
Province *string `json:"province,omitempty"`
|
||||||
|
City *string `json:"city,omitempty"`
|
||||||
|
District *string `json:"district,omitempty"`
|
||||||
|
ImageURL *string `json:"image_url,omitempty"`
|
||||||
|
|
||||||
|
// Connection details
|
||||||
|
Backbones []BackboneConnectionInfo `json:"backbones"`
|
||||||
|
Fishbones []FishboneConnectionInfo `json:"fishbones"`
|
||||||
|
Towers []TowerConnectionDetail `json:"towers"`
|
||||||
|
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TowerConnectionDetail struct {
|
||||||
|
ID uuid.UUID `json:"id"`
|
||||||
|
TowerCode string `json:"tower_code"`
|
||||||
|
Distance float64 `json:"distance_km"` // Distance from tower to device
|
||||||
|
ImageURL *string `json:"image_url,omitempty"`
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
package res
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DeviceInspectionResponse struct {
|
||||||
|
ID uuid.UUID `json:"id"`
|
||||||
|
DeviceCode string `json:"device_code"`
|
||||||
|
DeviceType string `json:"device_type"`
|
||||||
|
UserName string `json:"user_name"`
|
||||||
|
BackboneCode *string `json:"backbone_code,omitempty"`
|
||||||
|
FishboneCode *string `json:"fishbone_code,omitempty"`
|
||||||
|
TowerCode *string `json:"tower_code,omitempty"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
PortUsed string `json:"port_used"`
|
||||||
|
PortAvailable string `json:"port_available"`
|
||||||
|
CableAmount int `json:"cable_amount"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
ImageURL *string `json:"image_url,omitempty"`
|
||||||
|
InspectionPlacement string `json:"inspection_placement"` // Full address
|
||||||
|
Longitude float64 `json:"longitude"`
|
||||||
|
Latitude float64 `json:"latitude"`
|
||||||
|
InspectionApproval string `json:"inspection_approval"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeviceInspectionDetailResponse struct {
|
||||||
|
ID uuid.UUID `json:"id"`
|
||||||
|
Device DeviceInfo `json:"device"`
|
||||||
|
User UserInfo `json:"user"`
|
||||||
|
Backbone *BackboneInfo `json:"backbone,omitempty"`
|
||||||
|
Fishbone *FishboneInfo `json:"fishbone,omitempty"`
|
||||||
|
Tower *TowerInfo `json:"tower,omitempty"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
PortUsed string `json:"port_used"`
|
||||||
|
PortAvailable string `json:"port_available"`
|
||||||
|
CableAmount int `json:"cable_amount"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
ImageURL *string `json:"image_url,omitempty"`
|
||||||
|
InspectionPlacement string `json:"inspection_placement"` // Full address
|
||||||
|
Longitude float64 `json:"longitude"`
|
||||||
|
Latitude float64 `json:"latitude"`
|
||||||
|
InspectionApproval string `json:"inspection_approval"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type DeviceInfo struct {
|
||||||
|
ID uuid.UUID `json:"id"`
|
||||||
|
DeviceCode string `json:"device_code"`
|
||||||
|
DeviceType string `json:"device_type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserInfo struct {
|
||||||
|
ID uuid.UUID `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TowerInfo struct {
|
||||||
|
ID uuid.UUID `json:"id"`
|
||||||
|
TowerCode string `json:"tower_code"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type FishboneInfo struct {
|
||||||
|
ID uuid.UUID `json:"id"`
|
||||||
|
FishboneCode string `json:"fishbone_code"`
|
||||||
|
}
|
||||||
|
|
@ -7,14 +7,17 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type DeviceResponse struct {
|
type DeviceResponse struct {
|
||||||
ID uuid.UUID `json:"id"`
|
ID uuid.UUID `json:"id"`
|
||||||
DeviceCode string `json:"device_code"`
|
DeviceCode string `json:"device_code"`
|
||||||
DeviceType string `json:"device_type"`
|
DeviceType string `json:"device_type"`
|
||||||
Longitude float64 `json:"longitude"`
|
Longitude float64 `json:"longitude"`
|
||||||
Latitude float64 `json:"latitude"`
|
Latitude float64 `json:"latitude"`
|
||||||
Address string `json:"address"`
|
Address string `json:"address"`
|
||||||
PortAmount int `json:"port_amount"`
|
PortAmount int `json:"port_amount"`
|
||||||
Status string `json:"status"`
|
Status string `json:"status"` // Always include, even if null
|
||||||
CreatedAt time.Time `json:"created_at"`
|
Province *string `json:"province"` // Always include, even if null
|
||||||
UpdatedAt time.Time `json:"updated_at"`
|
City *string `json:"city"` // Always include, even if null
|
||||||
|
District *string `json:"district"` // Always include, even if null
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,80 @@
|
||||||
|
package res
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Simplified response for list
|
||||||
|
type NearestDeviceResponse struct {
|
||||||
|
ID uuid.UUID `json:"id"`
|
||||||
|
DeviceCode string `json:"device_code"`
|
||||||
|
DeviceType string `json:"device_type"`
|
||||||
|
Distance float64 `json:"distance_km"`
|
||||||
|
Address string `json:"address"`
|
||||||
|
Longitude float64 `json:"longitude"`
|
||||||
|
Latitude float64 `json:"latitude"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
PortAmount int `json:"port_amount"`
|
||||||
|
Region *string `json:"region,omitempty"`
|
||||||
|
Province *string `json:"province,omitempty"`
|
||||||
|
City *string `json:"city,omitempty"`
|
||||||
|
District *string `json:"district,omitempty"`
|
||||||
|
|
||||||
|
// Connection counts
|
||||||
|
BackboneCount int `json:"backbone_count"`
|
||||||
|
FishboneCount int `json:"fishbone_count"`
|
||||||
|
TowerCount int `json:"tower_count"`
|
||||||
|
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detailed response for single device
|
||||||
|
type NearestDeviceDetailResponse struct {
|
||||||
|
ID uuid.UUID `json:"id"`
|
||||||
|
DeviceCode string `json:"device_code"`
|
||||||
|
DeviceType string `json:"device_type"`
|
||||||
|
Distance float64 `json:"distance_km"`
|
||||||
|
Address string `json:"address"`
|
||||||
|
Longitude float64 `json:"longitude"`
|
||||||
|
Latitude float64 `json:"latitude"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
PortAmount int `json:"port_amount"`
|
||||||
|
Region *string `json:"region,omitempty"`
|
||||||
|
Province *string `json:"province,omitempty"`
|
||||||
|
City *string `json:"city,omitempty"`
|
||||||
|
District *string `json:"district,omitempty"`
|
||||||
|
ImageURL *string `json:"image_url,omitempty"`
|
||||||
|
|
||||||
|
// Detailed connection information
|
||||||
|
Backbones []BackboneConnectionInfo `json:"backbones"`
|
||||||
|
Fishbones []FishboneConnectionInfo `json:"fishbones"`
|
||||||
|
Towers []TowerConnectionInfo `json:"towers"`
|
||||||
|
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type BackboneConnectionInfo struct {
|
||||||
|
ID uuid.UUID `json:"id"`
|
||||||
|
BackboneCode string `json:"backbone_code"`
|
||||||
|
CoreAmount int `json:"core_amount"`
|
||||||
|
IsStartDevice bool `json:"is_start_device"`
|
||||||
|
ConnectedTo string `json:"connected_to"` // Device code of the other end
|
||||||
|
}
|
||||||
|
|
||||||
|
type FishboneConnectionInfo struct {
|
||||||
|
ID uuid.UUID `json:"id"`
|
||||||
|
FishboneCode string `json:"fishbone_code"`
|
||||||
|
CoreAmount int `json:"core_amount"`
|
||||||
|
BackboneCode string `json:"backbone_code"`
|
||||||
|
IsStartDevice bool `json:"is_start_device"`
|
||||||
|
ConnectedTo string `json:"connected_to"` // Device code of the other end
|
||||||
|
}
|
||||||
|
|
||||||
|
type TowerConnectionInfo struct {
|
||||||
|
ID uuid.UUID `json:"id"`
|
||||||
|
TowerCode string `json:"tower_code"`
|
||||||
|
Distance float64 `json:"distance_km"` // Distance from tower to device
|
||||||
|
ImageURL *string `json:"image_url,omitempty"`
|
||||||
|
}
|
||||||
|
|
@ -9,8 +9,8 @@ import (
|
||||||
type TowerResponse struct {
|
type TowerResponse struct {
|
||||||
ID uuid.UUID `json:"id"`
|
ID uuid.UUID `json:"id"`
|
||||||
DeviceCode string `json:"device_code"`
|
DeviceCode string `json:"device_code"`
|
||||||
TowerCode string `json:"tower_code"`
|
TowerCode *string `json:"tower_code"`
|
||||||
Longitude float64 `json:"longtitude"`
|
Longitude float64 `json:"longitude"`
|
||||||
Latitude float64 `json:"latitude"`
|
Latitude float64 `json:"latitude"`
|
||||||
Address string `json:"address"`
|
Address string `json:"address"`
|
||||||
ImageURL string `json:"image_url"`
|
ImageURL string `json:"image_url"`
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
package entity
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DeviceDetails struct {
|
||||||
|
ID uuid.UUID `json:"id" gorm:"type:uuid;primaryKey"`
|
||||||
|
DeviceCode string `json:"device_code" gorm:"unique"`
|
||||||
|
DeviceType DeviceType `json:"device_type"`
|
||||||
|
Longitude float64 `json:"longitude"`
|
||||||
|
Latitude float64 `json:"latitude"`
|
||||||
|
PortAmount int `json:"port_amount"`
|
||||||
|
Status DeviceStatus `json:"status"`
|
||||||
|
Region *string `json:"region,omitempty" gorm:"type:varchar(255)"`
|
||||||
|
Province *string `json:"province,omitempty" gorm:"type:varchar(255)"`
|
||||||
|
City *string `json:"city,omitempty" gorm:"type:varchar(255)"`
|
||||||
|
District *string `json:"district,omitempty" gorm:"type:varchar(255)"`
|
||||||
|
ImageURL *string `json:"image_url,omitempty" gorm:"type:text"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
|
||||||
|
// Fixed Relationships - Use direct foreign keys
|
||||||
|
DevicePort DevicePort `json:"device_port" gorm:"foreignKey:DeviceID;references:ID"`
|
||||||
|
BackbonesStart []Backbone `json:"backbones_start" gorm:"foreignKey:DeviceStartID;references:ID"`
|
||||||
|
BackbonesEnd []Backbone `json:"backbones_end" gorm:"foreignKey:DeviceEndID;references:ID"`
|
||||||
|
FishbonesStart []Fishbone `json:"fishbones_start" gorm:"foreignKey:DeviceStartID;references:ID"`
|
||||||
|
FishbonesEnd []Fishbone `json:"fishbones_end" gorm:"foreignKey:DeviceEndID;references:ID"`
|
||||||
|
Towers []Tower `json:"towers" gorm:"foreignKey:DeviceID;references:ID"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (DeviceDetails) TableName() string {
|
||||||
|
return "devices" // Use same table as Device entity
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper method to get all backbones connected to this device
|
||||||
|
func (d *DeviceDetails) GetAllBackbones() []Backbone {
|
||||||
|
allBackbones := make([]Backbone, 0)
|
||||||
|
allBackbones = append(allBackbones, d.BackbonesStart...)
|
||||||
|
allBackbones = append(allBackbones, d.BackbonesEnd...)
|
||||||
|
return allBackbones
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper method to get all fishbones connected to this device
|
||||||
|
func (d *DeviceDetails) GetAllFishbones() []Fishbone {
|
||||||
|
allFishbones := make([]Fishbone, 0)
|
||||||
|
allFishbones = append(allFishbones, d.FishbonesStart...)
|
||||||
|
allFishbones = append(allFishbones, d.FishbonesEnd...)
|
||||||
|
return allFishbones
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
package entity
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DeviceInspection struct {
|
||||||
|
ID uuid.UUID `json:"id" gorm:"type:uuid;primaryKey"`
|
||||||
|
DeviceID uuid.UUID `json:"device_id" gorm:"type:uuid;column:device_id"`
|
||||||
|
BackboneID *uuid.UUID `json:"backbone_id,omitempty" gorm:"type:uuid;column:backbone_id"`
|
||||||
|
FishboneID *uuid.UUID `json:"fishbone_id,omitempty" gorm:"type:uuid;column:fishbone_id"`
|
||||||
|
TowerID *uuid.UUID `json:"tower_id,omitempty" gorm:"type:uuid;column:tower_id"`
|
||||||
|
UserID uuid.UUID `json:"user_id" gorm:"type:uuid;column:user_id"`
|
||||||
|
Status string `json:"status" gorm:"type:varchar(50);column:status"`
|
||||||
|
PortUsed string `json:"port_used" gorm:"type:varchar(50);column:port_used"`
|
||||||
|
PortAvailable string `json:"port_available" gorm:"type:varchar(50);column:port_available"`
|
||||||
|
CableAmount int `json:"cable_amount" gorm:"type:int;column:cable_amount"`
|
||||||
|
Description string `json:"description" gorm:"type:text;column:description"`
|
||||||
|
ImageURL *string `json:"image_url,omitempty" gorm:"type:text;column:image_url"`
|
||||||
|
InspectionPlacement string `json:"inspection_placement" gorm:"type:text;column:inspection_placement"` // Store the full address from OpenStreetMap
|
||||||
|
Longitude float64 `json:"longitude" gorm:"type:float;column:longitude"`
|
||||||
|
Latitude float64 `json:"latitude" gorm:"type:float;column:latitude"`
|
||||||
|
InspectionApproval string `json:"inspection_approval" gorm:"type:varchar(50);column:inspection_approval"`
|
||||||
|
CreatedAt time.Time `json:"created_at" gorm:"type:timestamp;column:created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at" gorm:"type:timestamp;column:updated_at"`
|
||||||
|
|
||||||
|
// Relationships
|
||||||
|
Device Device `json:"device" gorm:"foreignKey:DeviceID"`
|
||||||
|
Backbone *Backbone `json:"backbone,omitempty" gorm:"foreignKey:BackboneID"`
|
||||||
|
Fishbone *Fishbone `json:"fishbone,omitempty" gorm:"foreignKey:FishboneID"`
|
||||||
|
User User `json:"user" gorm:"foreignKey:UserID"`
|
||||||
|
Tower *Tower `json:"tower,omitempty" gorm:"foreignKey:TowerID"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (DeviceInspection) TableName() string {
|
||||||
|
return "device_inspection"
|
||||||
|
}
|
||||||
|
|
@ -6,13 +6,15 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type DevicePort struct {
|
type DevicePort struct {
|
||||||
ID uuid.UUID `json:"id" gorm:"type:uuid;primaryKey"`
|
ID uuid.UUID `json:"id" gorm:"type:uuid;primaryKey"`
|
||||||
DeviceID uuid.UUID `json:"device_id" gorm:"type:uuid;column:device_id"`
|
DeviceID uuid.UUID `json:"device_id" gorm:"type:uuid;column:device_id;unique"`
|
||||||
PortNumber int `json:"port_number"`
|
PortUsed int `json:"port_used" gorm:"default:0"` // Auto-calculated
|
||||||
CreatedAt time.Time `json:"created_at"`
|
PortAvailable int `json:"port_available" gorm:"default:0"` // Auto-calculated
|
||||||
UpdatedAt time.Time `json:"updated_at"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
|
||||||
Device Device `gorm:"foreignKey:DeviceID"`
|
// Relationships
|
||||||
|
Device Device `json:"device" gorm:"foreignKey:DeviceID"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (DevicePort) TableName() string {
|
func (DevicePort) TableName() string {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
package entity
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DeviceWithDistance represents a device with calculated distance
|
||||||
|
type DeviceWithDistance struct {
|
||||||
|
ID uuid.UUID `json:"id"`
|
||||||
|
DeviceCode string `json:"device_code"`
|
||||||
|
DeviceType DeviceType `json:"device_type"`
|
||||||
|
Longitude float64 `json:"longitude"`
|
||||||
|
Latitude float64 `json:"latitude"`
|
||||||
|
PortAmount int `json:"port_amount"`
|
||||||
|
Status DeviceStatus `json:"status"`
|
||||||
|
Region *string `json:"region,omitempty"`
|
||||||
|
Province *string `json:"province,omitempty"`
|
||||||
|
City *string `json:"city,omitempty"`
|
||||||
|
District *string `json:"district,omitempty"`
|
||||||
|
ImageURL *string `json:"image_url,omitempty"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
Distance float64 `json:"distance"` // Calculated distance
|
||||||
|
}
|
||||||
|
|
@ -9,10 +9,12 @@ type DeviceType string
|
||||||
type DeviceStatus string
|
type DeviceStatus string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Odp DeviceType = "ODP"
|
ODP DeviceType = "ODP"
|
||||||
activeDev DeviceStatus = "active"
|
OTB DeviceType = "OTB"
|
||||||
inactiveDev DeviceStatus = "inactive"
|
|
||||||
maintenanceDev DeviceStatus = "maintenance"
|
ActiveDev DeviceStatus = "active"
|
||||||
|
InactiveDev DeviceStatus = "inactive"
|
||||||
|
MaintenanceDev DeviceStatus = "maintenance"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Device struct {
|
type Device struct {
|
||||||
|
|
@ -23,10 +25,10 @@ type Device struct {
|
||||||
Latitude float64 `json:"latitude"`
|
Latitude float64 `json:"latitude"`
|
||||||
PortAmount int `json:"port_amount"`
|
PortAmount int `json:"port_amount"`
|
||||||
Status DeviceStatus `json:"status"`
|
Status DeviceStatus `json:"status"`
|
||||||
Region string `json:"region"`
|
Province *string `json:"province,omitempty" gorm:"type:varchar(255)"`
|
||||||
Province string `json:"province"`
|
City *string `json:"city,omitempty" gorm:"type:varchar(255)"`
|
||||||
City string `json:"city"`
|
District *string `json:"district,omitempty" gorm:"type:varchar(255)"`
|
||||||
District string `json:"district"`
|
ImageURL *string `json:"image_url,omitempty" gorm:"type:text"`
|
||||||
CreatedAt time.Time `json:"created_at"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
UpdatedAt time.Time `json:"updated_at"`
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
package entity
|
||||||
|
|
||||||
|
import "github.com/google/uuid"
|
||||||
|
|
||||||
|
type TowerInspection struct {
|
||||||
|
ID uuid.UUID `json:"id" gorm:"type:uuid;primaryKey"`
|
||||||
|
TowerID uuid.UUID `json:"tower_id" gorm:"type:uuid;column:tower_id"`
|
||||||
|
UserID uuid.UUID `json:"user_id" gorm:"type:uuid;column:user_id"`
|
||||||
|
Description string `json:"description" gorm:"type:text;column:description"`
|
||||||
|
ImageURL string `json:"image_url" gorm:"type:text;column:image_url"`
|
||||||
|
CreatedAt string `json:"created_at" gorm:"type:timestamp;column:created_at"`
|
||||||
|
UpdatedAt string `json:"updated_at" gorm:"type:timestamp;column:updated_at"`
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,254 @@
|
||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"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
|
||||||
|
CheckDeviceExists(deviceID uuid.UUID) (bool, error)
|
||||||
|
GetPortUsageByDevice(deviceID uuid.UUID) (portUsed, portAvailable int, err 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").
|
||||||
|
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").
|
||||||
|
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 {
|
||||||
|
// Get device info
|
||||||
|
var device entity.Device
|
||||||
|
if err := tx.Where("id = ?", deviceID).First(&device).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var portUsed int
|
||||||
|
|
||||||
|
if device.DeviceType == "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)
|
||||||
|
} else if device.DeviceType == "ODP" {
|
||||||
|
// For ODP: sum fishbone core amounts (each core uses 1 port)
|
||||||
|
var totalCores int64
|
||||||
|
if err := tx.Model(&entity.Fishbone{}).
|
||||||
|
Where("dev_start_id = ? OR dev_end_id = ?", deviceID, deviceID).
|
||||||
|
Select("COALESCE(SUM(core_amount), 0)").
|
||||||
|
Scan(&totalCores).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
portUsed = int(totalCores)
|
||||||
|
}
|
||||||
|
|
||||||
|
portAvailable := device.PortAmount - portUsed
|
||||||
|
|
||||||
|
return tx.Model(&entity.DevicePort{}).
|
||||||
|
Where("device_id = ?", deviceID).
|
||||||
|
Updates(map[string]interface{}{
|
||||||
|
"port_used": portUsed,
|
||||||
|
"port_available": portAvailable,
|
||||||
|
"updated_at": gorm.Expr("NOW()"),
|
||||||
|
}).Error
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
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.PortAvailable < 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) CheckDeviceExists(deviceID uuid.UUID) (bool, error) {
|
||||||
|
var count int64
|
||||||
|
err := r.db.Model(&entity.Device{}).Where("id = ?", deviceID).Count(&count).Error
|
||||||
|
return count > 0, 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
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,155 @@
|
||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"users_management/m/model/entity"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DeviceInspectionRepo interface {
|
||||||
|
Create(inspection entity.DeviceInspection) error
|
||||||
|
GetAll() ([]entity.DeviceInspection, error)
|
||||||
|
GetByID(id uuid.UUID) (entity.DeviceInspection, error)
|
||||||
|
GetByUserID(userID uuid.UUID, limit, offset int) ([]entity.DeviceInspection, error)
|
||||||
|
Update(id uuid.UUID, updates map[string]interface{}) error
|
||||||
|
CheckDeviceExists(deviceID uuid.UUID) (bool, error)
|
||||||
|
CheckBackboneExists(backboneID uuid.UUID) (bool, error)
|
||||||
|
CheckFishboneExists(fishboneID uuid.UUID) (bool, error)
|
||||||
|
CheckTowerExists(towerID uuid.UUID) (bool, error)
|
||||||
|
GetDeviceByID(deviceID uuid.UUID) (entity.Device, error)
|
||||||
|
CountCablesByDevice(deviceID uuid.UUID, deviceType string) (int, error)
|
||||||
|
CountByUserID(userID uuid.UUID) (int64, error)
|
||||||
|
CountAll() (int64, error)
|
||||||
|
UpdateDeviceStatus(deviceID uuid.UUID, status string) error
|
||||||
|
GetDevicePortByDeviceID(deviceID uuid.UUID) (entity.DevicePort, error)
|
||||||
|
UpdateDevicePort(deviceID uuid.UUID, updates map[string]interface{}) error
|
||||||
|
CheckOwnership(inspectionID, userID uuid.UUID) (bool, error) // Add this line
|
||||||
|
}
|
||||||
|
|
||||||
|
type deviceInspectionRepo struct {
|
||||||
|
db *gorm.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDeviceInspectionRepo(db *gorm.DB) DeviceInspectionRepo {
|
||||||
|
return &deviceInspectionRepo{
|
||||||
|
db: db,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *deviceInspectionRepo) CheckOwnership(inspectionID, userID uuid.UUID) (bool, error) {
|
||||||
|
var count int64
|
||||||
|
err := r.db.Model(&entity.DeviceInspection{}).
|
||||||
|
Where("id = ? AND user_id = ?", inspectionID, userID).
|
||||||
|
Count(&count).Error
|
||||||
|
return count > 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *deviceInspectionRepo) Create(inspection entity.DeviceInspection) error {
|
||||||
|
return r.db.Create(&inspection).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *deviceInspectionRepo) GetAll() ([]entity.DeviceInspection, error) {
|
||||||
|
var inspections []entity.DeviceInspection
|
||||||
|
err := r.db.Preload("Device").Preload("User").Preload("Backbone").
|
||||||
|
Preload("Fishbone").Preload("Tower").Find(&inspections).Error
|
||||||
|
return inspections, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *deviceInspectionRepo) GetByID(id uuid.UUID) (entity.DeviceInspection, error) {
|
||||||
|
var inspection entity.DeviceInspection
|
||||||
|
err := r.db.Preload("Device").Preload("User").Preload("Backbone").
|
||||||
|
Preload("Fishbone").Preload("Tower").Where("id = ?", id).First(&inspection).Error
|
||||||
|
return inspection, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *deviceInspectionRepo) GetByUserID(userID uuid.UUID, limit, offset int) ([]entity.DeviceInspection, error) {
|
||||||
|
var inspections []entity.DeviceInspection
|
||||||
|
err := r.db.Preload("Device").Preload("User").Preload("Backbone").
|
||||||
|
Preload("Fishbone").Preload("Tower").
|
||||||
|
Where("user_id = ?", userID).
|
||||||
|
Order("created_at DESC").
|
||||||
|
Limit(limit).Offset(offset).
|
||||||
|
Find(&inspections).Error
|
||||||
|
return inspections, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *deviceInspectionRepo) Update(id uuid.UUID, updates map[string]interface{}) error {
|
||||||
|
return r.db.Model(&entity.DeviceInspection{}).Where("id = ?", id).Updates(updates).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *deviceInspectionRepo) CheckDeviceExists(deviceID uuid.UUID) (bool, error) {
|
||||||
|
var count int64
|
||||||
|
err := r.db.Model(&entity.Device{}).Where("id = ?", deviceID).Count(&count).Error
|
||||||
|
return count > 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *deviceInspectionRepo) CheckBackboneExists(backboneID uuid.UUID) (bool, error) {
|
||||||
|
var count int64
|
||||||
|
err := r.db.Model(&entity.Backbone{}).Where("id = ?", backboneID).Count(&count).Error
|
||||||
|
return count > 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *deviceInspectionRepo) CheckFishboneExists(fishboneID uuid.UUID) (bool, error) {
|
||||||
|
var count int64
|
||||||
|
err := r.db.Model(&entity.Fishbone{}).Where("id = ?", fishboneID).Count(&count).Error
|
||||||
|
return count > 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *deviceInspectionRepo) CheckTowerExists(towerID uuid.UUID) (bool, error) {
|
||||||
|
var count int64
|
||||||
|
err := r.db.Model(&entity.Tower{}).Where("id = ?", towerID).Count(&count).Error
|
||||||
|
return count > 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *deviceInspectionRepo) GetDeviceByID(deviceID uuid.UUID) (entity.Device, error) {
|
||||||
|
var device entity.Device
|
||||||
|
err := r.db.Where("id = ?", deviceID).First(&device).Error
|
||||||
|
return device, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *deviceInspectionRepo) CountCablesByDevice(deviceID uuid.UUID, deviceType string) (int, error) {
|
||||||
|
var count int64
|
||||||
|
|
||||||
|
if deviceType == "OTB" {
|
||||||
|
// Count backbones using this device
|
||||||
|
err := r.db.Model(&entity.Backbone{}).
|
||||||
|
Where("dev_start_id = ? OR dev_end_id = ?", deviceID, deviceID).
|
||||||
|
Count(&count).Error
|
||||||
|
return int(count), err
|
||||||
|
} else if deviceType == "ODP" {
|
||||||
|
// Sum core amounts of fishbones using this device
|
||||||
|
var totalCores int64
|
||||||
|
err := r.db.Model(&entity.Fishbone{}).
|
||||||
|
Where("dev_start_id = ? OR dev_end_id = ?", deviceID, deviceID).
|
||||||
|
Select("COALESCE(SUM(core_amount), 0)").Scan(&totalCores).Error
|
||||||
|
return int(totalCores), err
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *deviceInspectionRepo) CountByUserID(userID uuid.UUID) (int64, error) {
|
||||||
|
var count int64
|
||||||
|
err := r.db.Model(&entity.DeviceInspection{}).Where("user_id = ?", userID).Count(&count).Error
|
||||||
|
return count, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *deviceInspectionRepo) CountAll() (int64, error) {
|
||||||
|
var count int64
|
||||||
|
err := r.db.Model(&entity.DeviceInspection{}).Count(&count).Error
|
||||||
|
return count, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *deviceInspectionRepo) UpdateDeviceStatus(deviceID uuid.UUID, status string) error {
|
||||||
|
return r.db.Model(&entity.Device{}).Where("id = ?", deviceID).Update("status", status).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *deviceInspectionRepo) GetDevicePortByDeviceID(deviceID uuid.UUID) (entity.DevicePort, error) {
|
||||||
|
var devicePort entity.DevicePort
|
||||||
|
err := r.db.Where("device_id = ?", deviceID).First(&devicePort).Error
|
||||||
|
return devicePort, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *deviceInspectionRepo) UpdateDevicePort(deviceID uuid.UUID, updates map[string]interface{}) error {
|
||||||
|
return r.db.Model(&entity.DevicePort{}).Where("device_id = ?", deviceID).Updates(updates).Error
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,118 @@
|
||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"users_management/m/model/entity"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type NearestDeviceRepo interface {
|
||||||
|
GetNearestDevices(longitude, latitude, radius float64, limit int, province, city, district *string) ([]entity.DeviceWithDistance, error)
|
||||||
|
GetDeviceByIDWithConnections(id uuid.UUID) (entity.Device, error)
|
||||||
|
GetBackbonesByDeviceID(deviceID uuid.UUID) ([]entity.Backbone, error)
|
||||||
|
GetFishbonesByDeviceID(deviceID uuid.UUID) ([]entity.Fishbone, error)
|
||||||
|
GetTowersByDeviceID(deviceID uuid.UUID) ([]entity.Tower, error)
|
||||||
|
CountConnectionsByDeviceID(deviceID uuid.UUID) (backboneCount, fishboneCount, towerCount int, err error)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
type nearestDeviceRepo struct {
|
||||||
|
db *gorm.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNearestDeviceRepo(db *gorm.DB) NearestDeviceRepo {
|
||||||
|
return &nearestDeviceRepo{
|
||||||
|
db: db,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *nearestDeviceRepo) GetNearestDevices(longitude, latitude, radius float64, limit int, province, city, district *string) ([]entity.DeviceWithDistance, error) {
|
||||||
|
var devices []entity.DeviceWithDistance
|
||||||
|
|
||||||
|
// Build the subquery first
|
||||||
|
subQuery := r.db.Table("devices").
|
||||||
|
Select(`id, device_code, device_type, longitude, latitude, port_amount, status,
|
||||||
|
region, province, city, district, image_url, created_at, updated_at,
|
||||||
|
(6371 * acos(cos(radians(?)) * cos(radians(latitude)) * cos(radians(longitude) - radians(?)) + sin(radians(?)) * sin(radians(latitude)))) AS distance`,
|
||||||
|
latitude, longitude, latitude)
|
||||||
|
|
||||||
|
// Apply location filters to subquery
|
||||||
|
if province != nil && *province != "" {
|
||||||
|
subQuery = subQuery.Where("province = ?", *province)
|
||||||
|
}
|
||||||
|
if city != nil && *city != "" {
|
||||||
|
subQuery = subQuery.Where("city = ?", *city)
|
||||||
|
}
|
||||||
|
if district != nil && *district != "" {
|
||||||
|
subQuery = subQuery.Where("district = ?", *district)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the subquery in the main query
|
||||||
|
err := r.db.Table("(?) as devices_with_distance", subQuery).
|
||||||
|
Where("distance <= ?", radius).
|
||||||
|
Order("distance ASC").
|
||||||
|
Limit(limit).
|
||||||
|
Scan(&devices).Error
|
||||||
|
|
||||||
|
return devices, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *nearestDeviceRepo) GetDeviceByIDWithConnections(id uuid.UUID) (entity.Device, error) {
|
||||||
|
var device entity.Device
|
||||||
|
err := r.db.Where("id = ?", id).First(&device).Error
|
||||||
|
return device, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *nearestDeviceRepo) 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 *nearestDeviceRepo) 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 *nearestDeviceRepo) GetTowersByDeviceID(deviceID uuid.UUID) ([]entity.Tower, error) {
|
||||||
|
var towers []entity.Tower
|
||||||
|
err := r.db.Preload("Device").
|
||||||
|
Where("dev_id = ?", deviceID).
|
||||||
|
Find(&towers).Error
|
||||||
|
return towers, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *nearestDeviceRepo) CountConnectionsByDeviceID(deviceID uuid.UUID) (backboneCount, fishboneCount, towerCount int, err error) {
|
||||||
|
var backboneCountInt64, fishboneCountInt64, towerCountInt64 int64
|
||||||
|
|
||||||
|
// Count backbones
|
||||||
|
err = r.db.Model(&entity.Backbone{}).
|
||||||
|
Where("dev_start_id = ? OR dev_end_id = ?", deviceID, deviceID).
|
||||||
|
Count(&backboneCountInt64).Error
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count fishbones
|
||||||
|
err = r.db.Model(&entity.Fishbone{}).
|
||||||
|
Where("dev_start_id = ? OR dev_end_id = ?", deviceID, deviceID).
|
||||||
|
Count(&fishboneCountInt64).Error
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count towers
|
||||||
|
err = r.db.Model(&entity.Tower{}).
|
||||||
|
Where("dev_id = ?", deviceID).
|
||||||
|
Count(&towerCountInt64).Error
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return int(backboneCountInt64), int(fishboneCountInt64), int(towerCountInt64), nil
|
||||||
|
}
|
||||||
|
|
@ -19,42 +19,80 @@ type BackboneUseCase interface {
|
||||||
|
|
||||||
GetByID(id uuid.UUID) (res.BackboneResponse, error)
|
GetByID(id uuid.UUID) (res.BackboneResponse, error)
|
||||||
UpdateBackbone(id uuid.UUID, backbone req.UpdateBackboneDTO) error
|
UpdateBackbone(id uuid.UUID, backbone req.UpdateBackboneDTO) error
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type backboneUseCase struct {
|
type backboneUseCase struct {
|
||||||
backboneRepo repository.BackboneRepo
|
backboneRepo repository.BackboneRepo
|
||||||
fishboneRepo repository.FishboneRepo
|
fishboneRepo repository.FishboneRepo
|
||||||
validate *validator.Validate
|
validate *validator.Validate
|
||||||
|
deviceDetailsRepo repository.DeviceDetailsRepo // Add this field
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBackboneUseCase(backboneRepo repository.BackboneRepo, fishboneRepo repository.FishboneRepo) BackboneUseCase {
|
func NewBackboneUseCase(backboneRepo repository.BackboneRepo, fishboneRepo repository.FishboneRepo, deviceDetailsRepo repository.DeviceDetailsRepo) BackboneUseCase {
|
||||||
return &backboneUseCase{
|
return &backboneUseCase{
|
||||||
backboneRepo: backboneRepo,
|
backboneRepo: backboneRepo,
|
||||||
fishboneRepo: fishboneRepo,
|
fishboneRepo: fishboneRepo,
|
||||||
validate: validator.New(),
|
deviceDetailsRepo: deviceDetailsRepo, // Initialize the field
|
||||||
}
|
validate: validator.New(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *backboneUseCase) CreateBackbone(backbone req.BackboneDTO) error {
|
func (u *backboneUseCase) CreateBackbone(backbone req.BackboneDTO) error {
|
||||||
err := u.validate.Struct(backbone)
|
err := u.validate.Struct(backbone)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("validation error: %w", err)
|
return fmt.Errorf("validation error: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
newBackbone := entity.Backbone{
|
// Validate that both devices exist and are OTB type
|
||||||
ID: uuid.New(),
|
startExists, err := u.deviceDetailsRepo.CheckDeviceExists(backbone.DeviceStartID)
|
||||||
BackboneCode: backbone.BackboneCode,
|
if err != nil {
|
||||||
DeviceStartID: backbone.DeviceStartID,
|
return fmt.Errorf("error checking start device: %w", err)
|
||||||
DeviceEndID: backbone.DeviceEndID,
|
}
|
||||||
CoreAmount: backbone.CoreAmount,
|
if !startExists {
|
||||||
CreatedAt: time.Now(),
|
return fmt.Errorf("start device does not exist")
|
||||||
UpdatedAt: time.Now(),
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
endExists, err := u.deviceDetailsRepo.CheckDeviceExists(backbone.DeviceEndID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error checking end device: %w", err)
|
||||||
|
}
|
||||||
|
if !endExists {
|
||||||
|
return fmt.Errorf("end device does not exist")
|
||||||
|
}
|
||||||
|
|
||||||
return u.backboneRepo.Post(newBackbone)
|
// Validate port availability (each backbone connection uses 1 port)
|
||||||
|
if err := u.deviceDetailsRepo.ValidatePortAvailability(backbone.DeviceStartID, 1); err != nil {
|
||||||
|
return fmt.Errorf("start device: %w", err)
|
||||||
|
}
|
||||||
|
if err := u.deviceDetailsRepo.ValidatePortAvailability(backbone.DeviceEndID, 1); err != nil {
|
||||||
|
return fmt.Errorf("end device: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
newBackbone := entity.Backbone{
|
||||||
|
ID: uuid.New(),
|
||||||
|
BackboneCode: backbone.BackboneCode,
|
||||||
|
DeviceStartID: backbone.DeviceStartID,
|
||||||
|
DeviceEndID: backbone.DeviceEndID,
|
||||||
|
CoreAmount: backbone.CoreAmount,
|
||||||
|
CreatedAt: time.Now(),
|
||||||
|
UpdatedAt: time.Now(),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create backbone
|
||||||
|
err = u.backboneRepo.Post(newBackbone)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update port usage for both devices
|
||||||
|
u.deviceDetailsRepo.UpdateDevicePortUsage(backbone.DeviceStartID)
|
||||||
|
u.deviceDetailsRepo.UpdateDevicePortUsage(backbone.DeviceEndID)
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (u *backboneUseCase) GetAllBackbone() ([]res.BackboneResponse, error) {
|
func (u *backboneUseCase) GetAllBackbone() ([]res.BackboneResponse, error) {
|
||||||
backbones, err := u.backboneRepo.GetAll()
|
backbones, err := u.backboneRepo.GetAll()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -95,26 +133,80 @@ func (u *backboneUseCase) GetByID(id uuid.UUID) (res.BackboneResponse, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *backboneUseCase) UpdateBackbone(id uuid.UUID, backbone req.UpdateBackboneDTO) error {
|
func (u *backboneUseCase) UpdateBackbone(id uuid.UUID, backbone req.UpdateBackboneDTO) error {
|
||||||
err := u.validate.Struct(backbone)
|
err := u.validate.Struct(backbone)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("validation error: %w", err)
|
return fmt.Errorf("validation error: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
updates := make(map[string]interface{})
|
// Get original backbone to track changes
|
||||||
|
originalBackbone, err := u.backboneRepo.GetByID(id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if backbone.DeviceStartID != nil {
|
updates := make(map[string]interface{})
|
||||||
updates["DeviceStartID"] = backbone.DeviceStartID
|
|
||||||
}
|
|
||||||
|
|
||||||
if backbone.DeviceEndID != nil {
|
// Track devices that need port recalculation
|
||||||
updates["DeviceEndID"] = backbone.DeviceEndID
|
devicesToUpdate := make(map[uuid.UUID]bool)
|
||||||
}
|
devicesToUpdate[originalBackbone.DeviceStartID] = true
|
||||||
|
devicesToUpdate[originalBackbone.DeviceEndID] = true
|
||||||
|
|
||||||
if backbone.CoreAmount != nil {
|
if backbone.DeviceStartID != nil {
|
||||||
updates["CoreAmount"] = backbone.CoreAmount
|
// Validate new start device exists and has available ports
|
||||||
}
|
exists, err := u.deviceDetailsRepo.CheckDeviceExists(*backbone.DeviceStartID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error checking new start device: %w", err)
|
||||||
|
}
|
||||||
|
if !exists {
|
||||||
|
return fmt.Errorf("new start device does not exist")
|
||||||
|
}
|
||||||
|
|
||||||
updates["UpdatedAt"] = time.Now()
|
if err := u.deviceDetailsRepo.ValidatePortAvailability(*backbone.DeviceStartID, 1); err != nil {
|
||||||
|
return fmt.Errorf("new start device: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
return u.backboneRepo.Update(id, updates)
|
updates["dev_start_id"] = *backbone.DeviceStartID
|
||||||
|
devicesToUpdate[*backbone.DeviceStartID] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if backbone.DeviceEndID != nil {
|
||||||
|
// Validate new end device exists and has available ports
|
||||||
|
exists, err := u.deviceDetailsRepo.CheckDeviceExists(*backbone.DeviceEndID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error checking new end device: %w", err)
|
||||||
|
}
|
||||||
|
if !exists {
|
||||||
|
return fmt.Errorf("new end device does not exist")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := u.deviceDetailsRepo.ValidatePortAvailability(*backbone.DeviceEndID, 1); err != nil {
|
||||||
|
return fmt.Errorf("new end device: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
updates["dev_end_id"] = *backbone.DeviceEndID
|
||||||
|
devicesToUpdate[*backbone.DeviceEndID] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if backbone.CoreAmount != nil {
|
||||||
|
updates["core_amount"] = *backbone.CoreAmount
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(updates) == 0 {
|
||||||
|
return fmt.Errorf("no fields to update")
|
||||||
|
}
|
||||||
|
|
||||||
|
updates["updated_at"] = time.Now()
|
||||||
|
|
||||||
|
// Update backbone
|
||||||
|
err = u.backboneRepo.Update(id, updates)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recalculate port usage for all affected devices
|
||||||
|
for deviceID := range devicesToUpdate {
|
||||||
|
u.deviceDetailsRepo.UpdateDevicePortUsage(deviceID)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -39,7 +39,6 @@ func (u *devicePortUseCase) CreateDevicePort(devicePort req.DevicePort) error {
|
||||||
newDevicePort := entity.DevicePort{
|
newDevicePort := entity.DevicePort{
|
||||||
ID: uuid.New(),
|
ID: uuid.New(),
|
||||||
DeviceID: devicePort.DeviceID,
|
DeviceID: devicePort.DeviceID,
|
||||||
PortNumber: devicePort.PortNumber,
|
|
||||||
CreatedAt: time.Now(),
|
CreatedAt: time.Now(),
|
||||||
UpdatedAt: time.Now(),
|
UpdatedAt: time.Now(),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,186 @@
|
||||||
|
package usecase
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
"users_management/m/model/dto/req"
|
||||||
|
"users_management/m/model/dto/res"
|
||||||
|
"users_management/m/model/entity"
|
||||||
|
"users_management/m/repository"
|
||||||
|
"users_management/m/utils/helper"
|
||||||
|
"users_management/m/utils/service"
|
||||||
|
|
||||||
|
"github.com/go-playground/validator/v10"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DeviceDetailsUseCase interface {
|
||||||
|
CreateDeviceDetails(device req.DeviceDetailsDTO) error
|
||||||
|
GetAllDeviceDetails() ([]res.DeviceDetailsResponse, error)
|
||||||
|
GetDeviceDetailsByID(id uuid.UUID) (res.DeviceDetailsResponse, error)
|
||||||
|
UpdateDeviceDetails(id uuid.UUID, device req.UpdateDeviceDetailsDTO) error
|
||||||
|
DeleteDeviceDetails(id uuid.UUID) error
|
||||||
|
|
||||||
|
// Port management
|
||||||
|
ValidatePortUsage(deviceID uuid.UUID, requiredPorts int) error
|
||||||
|
RecalculatePortUsage(deviceID uuid.UUID) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type deviceDetailsUseCase struct {
|
||||||
|
deviceDetailsRepo repository.DeviceDetailsRepo
|
||||||
|
geocoder service.GeocodingService
|
||||||
|
validate *validator.Validate
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDeviceDetailsUseCase(deviceDetailsRepo repository.DeviceDetailsRepo, geocoder service.GeocodingService) DeviceDetailsUseCase {
|
||||||
|
return &deviceDetailsUseCase{
|
||||||
|
deviceDetailsRepo: deviceDetailsRepo,
|
||||||
|
geocoder: geocoder,
|
||||||
|
validate: validator.New(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *deviceDetailsUseCase) CreateDeviceDetails(deviceDTO req.DeviceDetailsDTO) error {
|
||||||
|
err := u.validate.Struct(deviceDTO)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("validation error: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
newDevice := entity.Device{
|
||||||
|
ID: uuid.New(),
|
||||||
|
DeviceCode: deviceDTO.DeviceCode,
|
||||||
|
DeviceType: entity.DeviceType(deviceDTO.DeviceType),
|
||||||
|
Longitude: deviceDTO.Longitude,
|
||||||
|
Latitude: deviceDTO.Latitude,
|
||||||
|
PortAmount: deviceDTO.PortAmount,
|
||||||
|
Status: entity.DeviceStatus(deviceDTO.Status),
|
||||||
|
Province: deviceDTO.Province,
|
||||||
|
City: deviceDTO.City,
|
||||||
|
District: deviceDTO.District,
|
||||||
|
CreatedAt: time.Now(),
|
||||||
|
UpdatedAt: time.Now(),
|
||||||
|
}
|
||||||
|
|
||||||
|
return u.deviceDetailsRepo.Create(newDevice)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *deviceDetailsUseCase) GetAllDeviceDetails() ([]res.DeviceDetailsResponse, error) {
|
||||||
|
devices, err := u.deviceDetailsRepo.GetAll()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return helper.ConvertToDeviceDetailsResponses(devices, u.geocoder)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *deviceDetailsUseCase) GetDeviceDetailsByID(id uuid.UUID) (res.DeviceDetailsResponse, error) {
|
||||||
|
device, err := u.deviceDetailsRepo.GetByID(id)
|
||||||
|
if err != nil {
|
||||||
|
return res.DeviceDetailsResponse{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return helper.ConvertToDeviceDetailsResponse(device, u.geocoder)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *deviceDetailsUseCase) UpdateDeviceDetails(id uuid.UUID, deviceDTO req.UpdateDeviceDetailsDTO) error {
|
||||||
|
err := u.validate.Struct(deviceDTO)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("validation error: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if device exists
|
||||||
|
exists, err := u.deviceDetailsRepo.CheckDeviceExists(id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !exists {
|
||||||
|
return errors.New("device not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
updates := map[string]interface{}{}
|
||||||
|
|
||||||
|
if deviceDTO.DeviceCode != nil {
|
||||||
|
updates["device_code"] = *deviceDTO.DeviceCode
|
||||||
|
}
|
||||||
|
if deviceDTO.DeviceType != nil {
|
||||||
|
updates["device_type"] = *deviceDTO.DeviceType
|
||||||
|
}
|
||||||
|
if deviceDTO.Longitude != nil {
|
||||||
|
updates["longitude"] = *deviceDTO.Longitude
|
||||||
|
}
|
||||||
|
if deviceDTO.Latitude != nil {
|
||||||
|
updates["latitude"] = *deviceDTO.Latitude
|
||||||
|
}
|
||||||
|
if deviceDTO.PortAmount != nil {
|
||||||
|
// Validate port amount change
|
||||||
|
currentUsed, _, err := u.deviceDetailsRepo.GetPortUsageByDevice(id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if *deviceDTO.PortAmount < currentUsed {
|
||||||
|
return fmt.Errorf("cannot reduce port amount to %d, currently using %d ports", *deviceDTO.PortAmount, currentUsed)
|
||||||
|
}
|
||||||
|
updates["port_amount"] = *deviceDTO.PortAmount
|
||||||
|
}
|
||||||
|
if deviceDTO.Status != nil {
|
||||||
|
updates["status"] = *deviceDTO.Status
|
||||||
|
}
|
||||||
|
if deviceDTO.Region != nil {
|
||||||
|
updates["region"] = *deviceDTO.Region
|
||||||
|
}
|
||||||
|
if deviceDTO.Province != nil {
|
||||||
|
updates["province"] = *deviceDTO.Province
|
||||||
|
}
|
||||||
|
if deviceDTO.City != nil {
|
||||||
|
updates["city"] = *deviceDTO.City
|
||||||
|
}
|
||||||
|
if deviceDTO.District != nil {
|
||||||
|
updates["district"] = *deviceDTO.District
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(updates) == 0 {
|
||||||
|
return errors.New("no fields to update")
|
||||||
|
}
|
||||||
|
|
||||||
|
updates["updated_at"] = time.Now()
|
||||||
|
|
||||||
|
return u.deviceDetailsRepo.Update(id, updates)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *deviceDetailsUseCase) DeleteDeviceDetails(id uuid.UUID) error {
|
||||||
|
// Check if device has connections
|
||||||
|
backbones, err := u.deviceDetailsRepo.GetBackbonesByDeviceID(id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(backbones) > 0 {
|
||||||
|
return errors.New("cannot delete device with active backbone connections")
|
||||||
|
}
|
||||||
|
|
||||||
|
fishbones, err := u.deviceDetailsRepo.GetFishbonesByDeviceID(id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(fishbones) > 0 {
|
||||||
|
return errors.New("cannot delete device with active fishbone connections")
|
||||||
|
}
|
||||||
|
|
||||||
|
towers, err := u.deviceDetailsRepo.GetTowersByDeviceID(id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(towers) > 0 {
|
||||||
|
return errors.New("cannot delete device with active tower connections")
|
||||||
|
}
|
||||||
|
|
||||||
|
return u.deviceDetailsRepo.Delete(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *deviceDetailsUseCase) ValidatePortUsage(deviceID uuid.UUID, requiredPorts int) error {
|
||||||
|
return u.deviceDetailsRepo.ValidatePortAvailability(deviceID, requiredPorts)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *deviceDetailsUseCase) RecalculatePortUsage(deviceID uuid.UUID) error {
|
||||||
|
return u.deviceDetailsRepo.UpdateDevicePortUsage(deviceID)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,414 @@
|
||||||
|
package usecase
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
"users_management/m/model/dto/req"
|
||||||
|
"users_management/m/model/dto/res"
|
||||||
|
"users_management/m/model/entity"
|
||||||
|
"users_management/m/repository"
|
||||||
|
"users_management/m/utils/helper"
|
||||||
|
"users_management/m/utils/service"
|
||||||
|
|
||||||
|
"github.com/go-playground/validator/v10"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DeviceInspectionUseCase interface {
|
||||||
|
CreateInspection(userID uuid.UUID, inspection req.DeviceInspectionDTO) error
|
||||||
|
GetAllInspections(page, limit int) ([]res.DeviceInspectionResponse, int64, error)
|
||||||
|
GetUserInspections(userID uuid.UUID, page, limit int) ([]res.DeviceInspectionResponse, int64, error)
|
||||||
|
GetInspectionByID(id uuid.UUID) (res.DeviceInspectionDetailResponse, error)
|
||||||
|
|
||||||
|
|
||||||
|
UpdateInspection(id, userID uuid.UUID, inspection req.UpdateDeviceInspectionDTO, userRole string) error
|
||||||
|
ApproveInspection(id uuid.UUID, approval req.ApproveInspectionDTO) error
|
||||||
|
CheckInspectionOwnership(inspectionID, userID uuid.UUID) (bool, error) // Add this line
|
||||||
|
}
|
||||||
|
|
||||||
|
type deviceInspectionUseCase struct {
|
||||||
|
inspectionRepo repository.DeviceInspectionRepo
|
||||||
|
activityLogUC ActivityLogUseCase
|
||||||
|
geoService service.GeocodingService
|
||||||
|
validate *validator.Validate
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDeviceInspectionUseCase(inspectionRepo repository.DeviceInspectionRepo, activityLogUC ActivityLogUseCase, geoService service.GeocodingService) DeviceInspectionUseCase {
|
||||||
|
return &deviceInspectionUseCase{
|
||||||
|
inspectionRepo: inspectionRepo,
|
||||||
|
activityLogUC: activityLogUC,
|
||||||
|
geoService: geoService,
|
||||||
|
validate: validator.New(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (u *deviceInspectionUseCase) CheckInspectionOwnership(inspectionID, userID uuid.UUID) (bool, error) {
|
||||||
|
return u.inspectionRepo.CheckOwnership(inspectionID, userID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *deviceInspectionUseCase) CreateInspection(userID uuid.UUID, inspection req.DeviceInspectionDTO) error {
|
||||||
|
err := u.validate.Struct(inspection)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("validation error: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if device exists
|
||||||
|
deviceExists, err := u.inspectionRepo.CheckDeviceExists(inspection.DeviceID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !deviceExists {
|
||||||
|
return errors.New("device not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get device details
|
||||||
|
device, err := u.inspectionRepo.GetDeviceByID(inspection.DeviceID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate device type constraints
|
||||||
|
if device.DeviceType == "OTB" && inspection.BackboneID == nil {
|
||||||
|
return errors.New("backbone is required for OTB device type")
|
||||||
|
}
|
||||||
|
if device.DeviceType == "ODP" && inspection.FishboneID == nil {
|
||||||
|
return errors.New("fishbone is required for ODP device type")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate backbone exists if provided
|
||||||
|
if inspection.BackboneID != nil {
|
||||||
|
backboneExists, err := u.inspectionRepo.CheckBackboneExists(*inspection.BackboneID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !backboneExists {
|
||||||
|
return errors.New("backbone not found")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate fishbone exists if provided
|
||||||
|
if inspection.FishboneID != nil {
|
||||||
|
fishboneExists, err := u.inspectionRepo.CheckFishboneExists(*inspection.FishboneID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !fishboneExists {
|
||||||
|
return errors.New("fishbone not found")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate tower exists if provided
|
||||||
|
if inspection.TowerID != nil {
|
||||||
|
towerExists, err := u.inspectionRepo.CheckTowerExists(*inspection.TowerID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !towerExists {
|
||||||
|
return errors.New("tower not found")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check port availability
|
||||||
|
portUsedInt, err := strconv.Atoi(inspection.PortUsed)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("invalid port_used value")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get current device port usage
|
||||||
|
devicePort, err := u.inspectionRepo.GetDevicePortByDeviceID(inspection.DeviceID)
|
||||||
|
if err != nil {
|
||||||
|
// If device port doesn't exist, create it
|
||||||
|
return fmt.Errorf("device port not found for device %s: %w", inspection.DeviceID, err)
|
||||||
|
}
|
||||||
|
// Check if adding this inspection would exceed port capacity
|
||||||
|
newPortUsed := devicePort.PortUsed + portUsedInt
|
||||||
|
if newPortUsed > device.PortAmount {
|
||||||
|
return fmt.Errorf("insufficient ports available. Device has %d ports, currently %d used, requested %d additional",
|
||||||
|
device.PortAmount, devicePort.PortUsed, portUsedInt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate cable amount based on device type
|
||||||
|
cableAmount, err := u.inspectionRepo.CountCablesByDevice(inspection.DeviceID, string(device.DeviceType))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate port available
|
||||||
|
portAvailable := device.PortAmount - newPortUsed
|
||||||
|
|
||||||
|
// Get address from coordinates using OpenStreetMap
|
||||||
|
longitude := inspection.InspectionPlacement.Longitude
|
||||||
|
latitude := inspection.InspectionPlacement.Latitude
|
||||||
|
|
||||||
|
address, err := u.geoService.GetAddressFromCoordinates(latitude, longitude)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error getting address from coordinates (%.6f, %.6f): %v", latitude, longitude, err)
|
||||||
|
address = fmt.Sprintf("Coordinates: %.6f, %.6f", latitude, longitude) // Fallback to coordinates
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create inspection
|
||||||
|
newInspection := entity.DeviceInspection{
|
||||||
|
ID: uuid.New(),
|
||||||
|
DeviceID: inspection.DeviceID,
|
||||||
|
BackboneID: inspection.BackboneID,
|
||||||
|
FishboneID: inspection.FishboneID,
|
||||||
|
TowerID: inspection.TowerID,
|
||||||
|
UserID: userID,
|
||||||
|
Status: inspection.Status,
|
||||||
|
PortUsed: inspection.PortUsed,
|
||||||
|
PortAvailable: strconv.Itoa(portAvailable),
|
||||||
|
CableAmount: cableAmount,
|
||||||
|
Description: inspection.Description,
|
||||||
|
ImageURL: inspection.ImageURL,
|
||||||
|
InspectionPlacement: address, // Store the full address from OpenStreetMap
|
||||||
|
Longitude: longitude, // Store longitude
|
||||||
|
Latitude: latitude, // Store latitude
|
||||||
|
InspectionApproval: "pending",
|
||||||
|
CreatedAt: time.Now(),
|
||||||
|
UpdatedAt: time.Now(),
|
||||||
|
}
|
||||||
|
|
||||||
|
err = u.inspectionRepo.Create(newInspection)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log activity
|
||||||
|
inspectionIDStr := newInspection.ID.String()
|
||||||
|
u.activityLogUC.LogActivity(
|
||||||
|
userID,
|
||||||
|
"CREATE",
|
||||||
|
"device_inspection",
|
||||||
|
&inspectionIDStr,
|
||||||
|
nil,
|
||||||
|
fmt.Sprintf("Created device inspection for device %s at %s", device.DeviceCode, address),
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *deviceInspectionUseCase) UpdateInspection(id, userID uuid.UUID, inspection req.UpdateDeviceInspectionDTO, userRole string) error {
|
||||||
|
err := u.validate.Struct(inspection)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("validation error: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get existing inspection
|
||||||
|
existingInspection, err := u.inspectionRepo.GetByID(id)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("inspection not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if user owns this inspection (for teknisi)
|
||||||
|
if userRole == "Teknisi" && existingInspection.UserID != userID {
|
||||||
|
return errors.New("unauthorized: you can only update your own inspections")
|
||||||
|
}
|
||||||
|
|
||||||
|
updates := make(map[string]interface{})
|
||||||
|
|
||||||
|
if inspection.BackboneID != nil {
|
||||||
|
backboneExists, err := u.inspectionRepo.CheckBackboneExists(*inspection.BackboneID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !backboneExists {
|
||||||
|
return errors.New("backbone not found")
|
||||||
|
}
|
||||||
|
updates["backbone_id"] = *inspection.BackboneID
|
||||||
|
}
|
||||||
|
|
||||||
|
if inspection.FishboneID != nil {
|
||||||
|
fishboneExists, err := u.inspectionRepo.CheckFishboneExists(*inspection.FishboneID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !fishboneExists {
|
||||||
|
return errors.New("fishbone not found")
|
||||||
|
}
|
||||||
|
updates["fishbone_id"] = *inspection.FishboneID
|
||||||
|
}
|
||||||
|
|
||||||
|
if inspection.TowerID != nil {
|
||||||
|
towerExists, err := u.inspectionRepo.CheckTowerExists(*inspection.TowerID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !towerExists {
|
||||||
|
return errors.New("tower not found")
|
||||||
|
}
|
||||||
|
updates["tower_id"] = *inspection.TowerID
|
||||||
|
}
|
||||||
|
|
||||||
|
if inspection.Status != nil {
|
||||||
|
updates["status"] = *inspection.Status
|
||||||
|
}
|
||||||
|
|
||||||
|
if inspection.PortUsed != nil {
|
||||||
|
updates["port_used"] = *inspection.PortUsed
|
||||||
|
}
|
||||||
|
|
||||||
|
if inspection.Description != nil {
|
||||||
|
updates["description"] = *inspection.Description
|
||||||
|
}
|
||||||
|
|
||||||
|
if inspection.ImageURL != nil {
|
||||||
|
updates["image_url"] = *inspection.ImageURL
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle location update
|
||||||
|
if inspection.InspectionPlacement != nil {
|
||||||
|
longitude := inspection.InspectionPlacement.Longitude
|
||||||
|
latitude := inspection.InspectionPlacement.Latitude
|
||||||
|
|
||||||
|
// Get new address from coordinates
|
||||||
|
address, err := u.geoService.GetAddressFromCoordinates(latitude, longitude)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error getting address from coordinates (%.6f, %.6f): %v", latitude, longitude, err)
|
||||||
|
address = fmt.Sprintf("Coordinates: %.6f, %.6f", latitude, longitude) // Fallback to coordinates
|
||||||
|
}
|
||||||
|
|
||||||
|
updates["inspection_placement"] = address
|
||||||
|
updates["longitude"] = longitude
|
||||||
|
updates["latitude"] = latitude
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(updates) == 0 {
|
||||||
|
return errors.New("no fields to update")
|
||||||
|
}
|
||||||
|
|
||||||
|
updates["updated_at"] = time.Now()
|
||||||
|
|
||||||
|
return u.inspectionRepo.Update(id, updates)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep the rest of the methods unchanged...
|
||||||
|
func (u *deviceInspectionUseCase) GetAllInspections(page, limit int) ([]res.DeviceInspectionResponse, int64, error) {
|
||||||
|
offset := (page - 1) * limit
|
||||||
|
|
||||||
|
inspections, err := u.inspectionRepo.GetAll()
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
total, err := u.inspectionRepo.CountAll()
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply pagination manually since we need all data for processing
|
||||||
|
start := offset
|
||||||
|
end := offset + limit
|
||||||
|
if start > len(inspections) {
|
||||||
|
start = len(inspections)
|
||||||
|
}
|
||||||
|
if end > len(inspections) {
|
||||||
|
end = len(inspections)
|
||||||
|
}
|
||||||
|
|
||||||
|
paginatedInspections := inspections[start:end]
|
||||||
|
responses := helper.ConvertToDeviceInspectionResponses(paginatedInspections)
|
||||||
|
|
||||||
|
return responses, total, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *deviceInspectionUseCase) GetUserInspections(userID uuid.UUID, page, limit int) ([]res.DeviceInspectionResponse, int64, error) {
|
||||||
|
offset := (page - 1) * limit
|
||||||
|
|
||||||
|
inspections, err := u.inspectionRepo.GetByUserID(userID, limit, offset)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
total, err := u.inspectionRepo.CountByUserID(userID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
responses := helper.ConvertToDeviceInspectionResponses(inspections)
|
||||||
|
|
||||||
|
return responses, total, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *deviceInspectionUseCase) GetInspectionByID(id uuid.UUID) (res.DeviceInspectionDetailResponse, error) {
|
||||||
|
inspection, err := u.inspectionRepo.GetByID(id)
|
||||||
|
if err != nil {
|
||||||
|
return res.DeviceInspectionDetailResponse{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
response := helper.ConvertToDeviceInspectionDetailResponse(inspection)
|
||||||
|
return response, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *deviceInspectionUseCase) ApproveInspection(id uuid.UUID, approval req.ApproveInspectionDTO) error {
|
||||||
|
err := u.validate.Struct(approval)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("validation error: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get inspection details
|
||||||
|
inspection, err := u.inspectionRepo.GetByID(id)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("inspection not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
updates := map[string]interface{}{
|
||||||
|
"inspection_approval": approval.InspectionApproval,
|
||||||
|
"updated_at": time.Now(),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update inspection
|
||||||
|
err = u.inspectionRepo.Update(id, updates)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// If approved, update device port and device status
|
||||||
|
if approval.InspectionApproval == "approved" {
|
||||||
|
// Update device status
|
||||||
|
err = u.inspectionRepo.UpdateDeviceStatus(inspection.DeviceID, inspection.Status)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update device port usage
|
||||||
|
portUsedInt, _ := strconv.Atoi(inspection.PortUsed)
|
||||||
|
|
||||||
|
devicePort, err := u.inspectionRepo.GetDevicePortByDeviceID(inspection.DeviceID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
device, err := u.inspectionRepo.GetDeviceByID(inspection.DeviceID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
newPortUsed := devicePort.PortUsed + portUsedInt
|
||||||
|
newPortAvailable := device.PortAmount - newPortUsed
|
||||||
|
|
||||||
|
portUpdates := map[string]interface{}{
|
||||||
|
"port_used": newPortUsed,
|
||||||
|
"port_available": newPortAvailable,
|
||||||
|
"updated_at": time.Now(),
|
||||||
|
}
|
||||||
|
|
||||||
|
// For OTB devices, also update backbone_id
|
||||||
|
if device.DeviceType == "OTB" && inspection.BackboneID != nil {
|
||||||
|
portUpdates["backbone_id"] = *inspection.BackboneID
|
||||||
|
}
|
||||||
|
|
||||||
|
err = u.inspectionRepo.UpdateDevicePort(inspection.DeviceID, portUpdates)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
@ -43,21 +43,20 @@ func (u *deviceUseCase) CreateDevice(device req.DeviceDTO) error {
|
||||||
return fmt.Errorf("validation error: %w", err)
|
return fmt.Errorf("validation error: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
newDevice := entity.Device{
|
newDevice := entity.Device{
|
||||||
ID: uuid.New(),
|
ID: uuid.New(),
|
||||||
DeviceCode: device.DeviceCode,
|
DeviceCode: device.DeviceCode,
|
||||||
DeviceType: entity.DeviceType(device.DeviceType),
|
DeviceType: entity.DeviceType(device.DeviceType),
|
||||||
Longitude: device.Longitude,
|
Longitude: device.Longitude,
|
||||||
Latitude: device.Latitude,
|
Latitude: device.Latitude,
|
||||||
PortAmount: device.PortAmount,
|
PortAmount: device.PortAmount,
|
||||||
Status: entity.DeviceStatus(device.Status),
|
Status: entity.DeviceStatus(device.Status),
|
||||||
Region: device.Region,
|
Province: device.Province, // Now nullable
|
||||||
Province: device.Province,
|
City: device.City, // Now nullable
|
||||||
City: device.City,
|
District: device.District, // Now nullable
|
||||||
District: device.District,
|
CreatedAt: time.Now(),
|
||||||
CreatedAt: time.Now(),
|
UpdatedAt: time.Now(),
|
||||||
UpdatedAt: time.Now(),
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return u.deviceRepo.Post(newDevice)
|
return u.deviceRepo.Post(newDevice)
|
||||||
}
|
}
|
||||||
|
|
@ -113,9 +112,6 @@ func (u *deviceUseCase) UpdateDevice(id uuid.UUID, device req.UpdateDeviceDTO) e
|
||||||
if device.Status != nil {
|
if device.Status != nil {
|
||||||
updates["Status"] = *device.Status
|
updates["Status"] = *device.Status
|
||||||
}
|
}
|
||||||
if device.Region != nil {
|
|
||||||
updates["Region"] = *device.Region
|
|
||||||
}
|
|
||||||
if device.Province != nil {
|
if device.Province != nil {
|
||||||
updates["Province"] = *device.Province
|
updates["Province"] = *device.Province
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,17 @@
|
||||||
package usecase
|
package usecase
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"time"
|
"fmt"
|
||||||
"users_management/m/model/dto/req"
|
"time"
|
||||||
"users_management/m/model/dto/res"
|
"users_management/m/model/dto/req"
|
||||||
"users_management/m/model/entity"
|
"users_management/m/model/dto/res"
|
||||||
"users_management/m/repository"
|
"users_management/m/model/entity"
|
||||||
"users_management/m/utils/helper"
|
"users_management/m/repository"
|
||||||
|
"users_management/m/utils/helper"
|
||||||
|
|
||||||
"github.com/go-playground/validator/v10"
|
"github.com/go-playground/validator/v10"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
type FishboneUseCase interface {
|
type FishboneUseCase interface {
|
||||||
|
|
@ -22,53 +23,58 @@ type FishboneUseCase interface {
|
||||||
GetFishboneStats() (map[string]interface{}, error)
|
GetFishboneStats() (map[string]interface{}, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type fishboneUsecase struct {
|
type fishboneUseCase struct {
|
||||||
fishboneRepo repository.FishboneRepo
|
fishboneRepo repository.FishboneRepo
|
||||||
validate *validator.Validate
|
backboneRepo repository.BackboneRepo
|
||||||
|
deviceDetailsRepo repository.DeviceDetailsRepo // Add this field
|
||||||
|
validate *validator.Validate
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFishboneUseCase(fishboneRepo repository.FishboneRepo) FishboneUseCase {
|
func NewFishboneUseCase(fishboneRepo repository.FishboneRepo, backboneRepo repository.BackboneRepo, deviceDetailsRepo repository.DeviceDetailsRepo) FishboneUseCase {
|
||||||
return &fishboneUsecase{
|
return &fishboneUseCase{
|
||||||
fishboneRepo: fishboneRepo,
|
fishboneRepo: fishboneRepo,
|
||||||
validate: validator.New(),
|
backboneRepo: backboneRepo,
|
||||||
|
deviceDetailsRepo: deviceDetailsRepo, // Initialize the field
|
||||||
|
validate: validator.New(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *fishboneUsecase) CreateFishbone(fishbone req.FishboneDTO) error {
|
|
||||||
|
|
||||||
|
func (u *fishboneUseCase) CreateFishbone(fishbone req.FishboneDTO) error {
|
||||||
err := u.validate.Struct(fishbone)
|
err := u.validate.Struct(fishbone)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("validation error: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if backbone exists
|
// Validate that both devices exist and are ODP type
|
||||||
backboneExists, err := u.fishboneRepo.CheckBackboneExists(fishbone.BackboneID)
|
startExists, err := u.deviceDetailsRepo.CheckDeviceExists(fishbone.DeviceStartID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("error checking start device: %w", err)
|
||||||
}
|
}
|
||||||
if !backboneExists {
|
if !startExists {
|
||||||
return errors.New("backbone not found")
|
return fmt.Errorf("start device does not exist")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if devices exist
|
endExists, err := u.deviceDetailsRepo.CheckDeviceExists(fishbone.DeviceEndID)
|
||||||
deviceStartExists, err := u.fishboneRepo.CheckDeviceExists(fishbone.DeviceStartID)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("error checking end device: %w", err)
|
||||||
}
|
}
|
||||||
if !deviceStartExists {
|
if !endExists {
|
||||||
return errors.New("start device not found")
|
return fmt.Errorf("end device does not exist")
|
||||||
}
|
}
|
||||||
|
|
||||||
deviceEndExists, err := u.fishboneRepo.CheckDeviceExists(fishbone.DeviceEndID)
|
// Validate port availability for ODP devices (each core needs 1 port)
|
||||||
if err != nil {
|
if err := u.deviceDetailsRepo.ValidatePortAvailability(fishbone.DeviceStartID, fishbone.CoreAmount); err != nil {
|
||||||
return err
|
return fmt.Errorf("start device: %w", err)
|
||||||
}
|
}
|
||||||
if !deviceEndExists {
|
if err := u.deviceDetailsRepo.ValidatePortAvailability(fishbone.DeviceEndID, fishbone.CoreAmount); err != nil {
|
||||||
return errors.New("end device not found")
|
return fmt.Errorf("end device: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
newFishbone := entity.Fishbone{
|
newFishbone := entity.Fishbone{
|
||||||
ID: uuid.New(),
|
ID: uuid.New(),
|
||||||
FishboneCode: fishbone.FishboneCode,
|
FishboneCode: fishbone.FishboneCode,
|
||||||
BackboneID: fishbone.BackboneID,
|
BackboneID: fishbone.BackboneID,
|
||||||
DeviceStartID: fishbone.DeviceStartID,
|
DeviceStartID: fishbone.DeviceStartID,
|
||||||
DeviceEndID: fishbone.DeviceEndID,
|
DeviceEndID: fishbone.DeviceEndID,
|
||||||
|
|
@ -77,10 +83,20 @@ func (u *fishboneUsecase) CreateFishbone(fishbone req.FishboneDTO) error {
|
||||||
UpdatedAt: time.Now(),
|
UpdatedAt: time.Now(),
|
||||||
}
|
}
|
||||||
|
|
||||||
return u.fishboneRepo.Post(newFishbone)
|
// Create fishbone
|
||||||
|
err = u.fishboneRepo.Post(newFishbone)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update port usage for both devices
|
||||||
|
u.deviceDetailsRepo.UpdateDevicePortUsage(fishbone.DeviceStartID)
|
||||||
|
u.deviceDetailsRepo.UpdateDevicePortUsage(fishbone.DeviceEndID)
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *fishboneUsecase) GetAllFishbone() ([]res.FishboneResponse, error) {
|
func (u *fishboneUseCase) GetAllFishbone() ([]res.FishboneResponse, error) {
|
||||||
fishbones, err := u.fishboneRepo.GetAll()
|
fishbones, err := u.fishboneRepo.GetAll()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -89,7 +105,7 @@ func (u *fishboneUsecase) GetAllFishbone() ([]res.FishboneResponse, error) {
|
||||||
return helper.ConvertToSimpleFishboneResponses(fishbones), nil
|
return helper.ConvertToSimpleFishboneResponses(fishbones), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *fishboneUsecase) GetByID(id uuid.UUID) (res.FishboneDetailResponse, error) {
|
func (u *fishboneUseCase) GetByID(id uuid.UUID) (res.FishboneDetailResponse, error) {
|
||||||
fishbone, err := u.fishboneRepo.GetByIDWithRelations(id)
|
fishbone, err := u.fishboneRepo.GetByIDWithRelations(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res.FishboneDetailResponse{}, err
|
return res.FishboneDetailResponse{}, err
|
||||||
|
|
@ -99,73 +115,113 @@ func (u *fishboneUsecase) GetByID(id uuid.UUID) (res.FishboneDetailResponse, err
|
||||||
return helper.ConvertToFishboneDetailResponse(fishbone), nil
|
return helper.ConvertToFishboneDetailResponse(fishbone), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *fishboneUsecase) UpdateFishbone(id uuid.UUID, fishbone req.UpdateFishboneDTO) error {
|
func (u *fishboneUseCase) UpdateFishbone(id uuid.UUID, fishbone req.UpdateFishboneDTO) error {
|
||||||
err := u.validate.Struct(fishbone)
|
err := u.validate.Struct(fishbone)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("validation error: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if fishbone exists
|
// Get original fishbone for comparison
|
||||||
exists, err := u.fishboneRepo.CheckFishboneExists(id)
|
originalFishbone, err := u.fishboneRepo.GetByID(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !exists {
|
|
||||||
return errors.New("fishbone not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
updates := make(map[string]interface{})
|
updates := make(map[string]interface{})
|
||||||
|
|
||||||
if fishbone.FishboneCode != nil {
|
// Track devices that need port recalculation
|
||||||
updates["fishbone_code"] = *fishbone.FishboneCode
|
devicesToUpdate := make(map[uuid.UUID]bool)
|
||||||
}
|
devicesToUpdate[originalFishbone.DeviceStartID] = true
|
||||||
if fishbone.BackboneID != nil {
|
devicesToUpdate[originalFishbone.DeviceEndID] = true
|
||||||
// Validate backbone exists
|
|
||||||
backboneExists, err := u.fishboneRepo.CheckBackboneExists(*fishbone.BackboneID)
|
// If core amount is changed, validate port availability
|
||||||
if err != nil {
|
if fishbone.CoreAmount != nil && *fishbone.CoreAmount != originalFishbone.CoreAmount {
|
||||||
return err
|
coreDiff := *fishbone.CoreAmount - originalFishbone.CoreAmount
|
||||||
|
if coreDiff > 0 {
|
||||||
|
// Increasing cores - check port availability
|
||||||
|
if err := u.deviceDetailsRepo.ValidatePortAvailability(originalFishbone.DeviceStartID, coreDiff); err != nil {
|
||||||
|
return fmt.Errorf("start device: %w", err)
|
||||||
|
}
|
||||||
|
if err := u.deviceDetailsRepo.ValidatePortAvailability(originalFishbone.DeviceEndID, coreDiff); err != nil {
|
||||||
|
return fmt.Errorf("end device: %w", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if !backboneExists {
|
|
||||||
return errors.New("backbone not found")
|
|
||||||
}
|
|
||||||
updates["bb_id"] = *fishbone.BackboneID
|
|
||||||
}
|
|
||||||
if fishbone.DeviceStartID != nil {
|
|
||||||
// Validate device exists
|
|
||||||
deviceExists, err := u.fishboneRepo.CheckDeviceExists(*fishbone.DeviceStartID)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !deviceExists {
|
|
||||||
return errors.New("start device not found")
|
|
||||||
}
|
|
||||||
updates["dev_start_id"] = *fishbone.DeviceStartID
|
|
||||||
}
|
|
||||||
if fishbone.DeviceEndID != nil {
|
|
||||||
// Validate device exists
|
|
||||||
deviceExists, err := u.fishboneRepo.CheckDeviceExists(*fishbone.DeviceEndID)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !deviceExists {
|
|
||||||
return errors.New("end device not found")
|
|
||||||
}
|
|
||||||
updates["dev_end_id"] = *fishbone.DeviceEndID
|
|
||||||
}
|
|
||||||
if fishbone.CoreAmount != nil {
|
|
||||||
updates["core_amount"] = *fishbone.CoreAmount
|
updates["core_amount"] = *fishbone.CoreAmount
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if fishbone.DeviceStartID != nil {
|
||||||
|
exists, err := u.deviceDetailsRepo.CheckDeviceExists(*fishbone.DeviceStartID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error checking new start device: %w", err)
|
||||||
|
}
|
||||||
|
if !exists {
|
||||||
|
return fmt.Errorf("new start device does not exist")
|
||||||
|
}
|
||||||
|
|
||||||
|
coreAmount := originalFishbone.CoreAmount
|
||||||
|
if fishbone.CoreAmount != nil {
|
||||||
|
coreAmount = *fishbone.CoreAmount
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := u.deviceDetailsRepo.ValidatePortAvailability(*fishbone.DeviceStartID, coreAmount); err != nil {
|
||||||
|
return fmt.Errorf("new start device: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
updates["dev_start_id"] = *fishbone.DeviceStartID
|
||||||
|
devicesToUpdate[*fishbone.DeviceStartID] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if fishbone.DeviceEndID != nil {
|
||||||
|
exists, err := u.deviceDetailsRepo.CheckDeviceExists(*fishbone.DeviceEndID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error checking new end device: %w", err)
|
||||||
|
}
|
||||||
|
if !exists {
|
||||||
|
return fmt.Errorf("new end device does not exist")
|
||||||
|
}
|
||||||
|
|
||||||
|
coreAmount := originalFishbone.CoreAmount
|
||||||
|
if fishbone.CoreAmount != nil {
|
||||||
|
coreAmount = *fishbone.CoreAmount
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := u.deviceDetailsRepo.ValidatePortAvailability(*fishbone.DeviceEndID, coreAmount); err != nil {
|
||||||
|
return fmt.Errorf("new end device: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
updates["dev_end_id"] = *fishbone.DeviceEndID
|
||||||
|
devicesToUpdate[*fishbone.DeviceEndID] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if fishbone.BackboneID != nil {
|
||||||
|
updates["backbone_id"] = *fishbone.BackboneID
|
||||||
|
}
|
||||||
|
|
||||||
|
if fishbone.FishboneCode != nil {
|
||||||
|
updates["fishbone_code"] = *fishbone.FishboneCode
|
||||||
|
}
|
||||||
|
|
||||||
if len(updates) == 0 {
|
if len(updates) == 0 {
|
||||||
return errors.New("no fields to update")
|
return fmt.Errorf("no fields to update")
|
||||||
}
|
}
|
||||||
|
|
||||||
updates["updated_at"] = time.Now()
|
updates["updated_at"] = time.Now()
|
||||||
|
|
||||||
return u.fishboneRepo.Update(id, updates)
|
// Update fishbone
|
||||||
|
err = u.fishboneRepo.Update(id, updates)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recalculate port usage for all affected devices
|
||||||
|
for deviceID := range devicesToUpdate {
|
||||||
|
u.deviceDetailsRepo.UpdateDevicePortUsage(deviceID)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *fishboneUsecase) DeleteFishbone(id uuid.UUID) error {
|
func (u *fishboneUseCase) DeleteFishbone(id uuid.UUID) error {
|
||||||
// Check if fishbone exists
|
// Check if fishbone exists
|
||||||
exists, err := u.fishboneRepo.CheckFishboneExists(id)
|
exists, err := u.fishboneRepo.CheckFishboneExists(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -178,7 +234,7 @@ func (u *fishboneUsecase) DeleteFishbone(id uuid.UUID) error {
|
||||||
return u.fishboneRepo.Delete(id)
|
return u.fishboneRepo.Delete(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *fishboneUsecase) GetFishboneStats() (map[string]interface{}, error) {
|
func (u *fishboneUseCase) GetFishboneStats() (map[string]interface{}, error) {
|
||||||
fishbones, err := u.fishboneRepo.GetAll()
|
fishbones, err := u.fishboneRepo.GetAll()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,106 @@
|
||||||
|
package usecase
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"users_management/m/model/dto/req"
|
||||||
|
"users_management/m/model/dto/res"
|
||||||
|
"users_management/m/repository"
|
||||||
|
"users_management/m/utils/helper"
|
||||||
|
"users_management/m/utils/service"
|
||||||
|
|
||||||
|
"github.com/go-playground/validator/v10"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type NearestDeviceUseCase interface {
|
||||||
|
GetNearestDevices(request req.NearestDeviceDTO) ([]res.NearestDeviceResponse, error)
|
||||||
|
GetNearestDeviceByID(id uuid.UUID, userLat, userLng float64) (res.NearestDeviceDetailResponse, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type nearestDeviceUseCase struct {
|
||||||
|
nearestDeviceRepo repository.NearestDeviceRepo
|
||||||
|
geocoder service.GeocodingService
|
||||||
|
validate *validator.Validate
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNearestDeviceUseCase(nearestDeviceRepo repository.NearestDeviceRepo, geocoder service.GeocodingService) NearestDeviceUseCase {
|
||||||
|
return &nearestDeviceUseCase{
|
||||||
|
nearestDeviceRepo: nearestDeviceRepo,
|
||||||
|
geocoder: geocoder,
|
||||||
|
validate: validator.New(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *nearestDeviceUseCase) GetNearestDevices(request req.NearestDeviceDTO) ([]res.NearestDeviceResponse, error) {
|
||||||
|
err := u.validate.Struct(request)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set defaults
|
||||||
|
radius := request.Radius
|
||||||
|
if radius == 0 {
|
||||||
|
radius = 5.0 // Default 5km
|
||||||
|
}
|
||||||
|
|
||||||
|
limit := request.Limit
|
||||||
|
if limit == 0 {
|
||||||
|
limit = 10 // Default 10 devices
|
||||||
|
}
|
||||||
|
|
||||||
|
devices, err := u.nearestDeviceRepo.GetNearestDevices(
|
||||||
|
request.Longitude,
|
||||||
|
request.Latitude,
|
||||||
|
radius,
|
||||||
|
limit,
|
||||||
|
request.Province,
|
||||||
|
request.City,
|
||||||
|
request.District,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Updated function call - removed userLat, userLng parameters since distance is already calculated
|
||||||
|
responses, err := helper.ConvertToNearestDeviceResponses(devices, u.nearestDeviceRepo, u.geocoder)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return responses, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *nearestDeviceUseCase) GetNearestDeviceByID(id uuid.UUID, userLat, userLng float64) (res.NearestDeviceDetailResponse, error) {
|
||||||
|
device, err := u.nearestDeviceRepo.GetDeviceByIDWithConnections(id)
|
||||||
|
if err != nil {
|
||||||
|
return res.NearestDeviceDetailResponse{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate distance
|
||||||
|
distance := calculateDistance(userLat, userLng, device.Latitude, device.Longitude)
|
||||||
|
|
||||||
|
response, err := helper.ConvertToNearestDeviceDetailResponse(device, distance, u.nearestDeviceRepo, u.geocoder)
|
||||||
|
if err != nil {
|
||||||
|
return res.NearestDeviceDetailResponse{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return response, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculateDistance calculates the distance between two coordinates using Haversine formula
|
||||||
|
func calculateDistance(lat1, lng1, lat2, lng2 float64) float64 {
|
||||||
|
const earthRadius = 6371 // Earth's radius in kilometers
|
||||||
|
|
||||||
|
lat1Rad := lat1 * math.Pi / 180
|
||||||
|
lng1Rad := lng1 * math.Pi / 180
|
||||||
|
lat2Rad := lat2 * math.Pi / 180
|
||||||
|
lng2Rad := lng2 * math.Pi / 180
|
||||||
|
|
||||||
|
dlat := lat2Rad - lat1Rad
|
||||||
|
dlng := lng2Rad - lng1Rad
|
||||||
|
|
||||||
|
a := math.Sin(dlat/2)*math.Sin(dlat/2) + math.Cos(lat1Rad)*math.Cos(lat2Rad)*math.Sin(dlng/2)*math.Sin(dlng/2)
|
||||||
|
c := 2 * math.Atan2(math.Sqrt(a), math.Sqrt(1-a))
|
||||||
|
|
||||||
|
return earthRadius * c
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package helper
|
package helper
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"users_management/m/model/dto/res"
|
"users_management/m/model/dto/res"
|
||||||
"users_management/m/model/entity"
|
"users_management/m/model/entity"
|
||||||
|
|
@ -14,6 +15,7 @@ func ConvertToDeviceTypeResponse(devices []entity.Device) []res.DeviceTypeRespon
|
||||||
ID: devices.ID,
|
ID: devices.ID,
|
||||||
DeviceType: string(devices.DeviceType),
|
DeviceType: string(devices.DeviceType),
|
||||||
DeviceCode: devices.DeviceCode,
|
DeviceCode: devices.DeviceCode,
|
||||||
|
|
||||||
}
|
}
|
||||||
responses = append(responses, deviceResp)
|
responses = append(responses, deviceResp)
|
||||||
}
|
}
|
||||||
|
|
@ -22,41 +24,38 @@ func ConvertToDeviceTypeResponse(devices []entity.Device) []res.DeviceTypeRespon
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func ConvertToDeviceResponse (devices []entity.Device, geocoder service.GeocodingService) ([]res.DeviceResponse, error) {
|
func ConvertToDeviceResponse(devices []entity.Device, geocoder service.GeocodingService) ([]res.DeviceResponse, error) {
|
||||||
var responses []res.DeviceResponse
|
var responses []res.DeviceResponse
|
||||||
for _, devices := range devices {
|
|
||||||
var address string
|
|
||||||
|
|
||||||
if geocoder != nil {
|
for _, device := range devices {
|
||||||
generatedAddress, err := geocoder.GetAddressFromCoordinates(devices.Latitude, devices.Longitude)
|
// Get address from coordinates
|
||||||
if err != nil {
|
address, err := geocoder.GetAddressFromCoordinates(device.Latitude, device.Longitude)
|
||||||
// Log specific geocoding error
|
log.Printf("Geocoding for device %s: %.6f, %.6f -> %s", device.DeviceCode, device.Latitude, device.Longitude, address)
|
||||||
log.Printf("Geocoding error for device %s: %v", devices.DeviceCode, err)
|
if err != nil {
|
||||||
} else {
|
address = fmt.Sprintf("Coordinates: %.6f, %.6f", device.Latitude, device.Longitude)
|
||||||
address = generatedAddress
|
}
|
||||||
}
|
|
||||||
} else{
|
|
||||||
log.Println("WARNING: Geocoder is nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
deviceResp := res.DeviceResponse{
|
response := res.DeviceResponse{
|
||||||
ID: devices.ID,
|
ID: device.ID,
|
||||||
DeviceCode: devices.DeviceCode,
|
DeviceCode: device.DeviceCode,
|
||||||
DeviceType: string(devices.DeviceType),
|
DeviceType: string(device.DeviceType),
|
||||||
Longitude: devices.Longitude,
|
Longitude: device.Longitude,
|
||||||
Latitude: devices.Latitude,
|
Latitude: device.Latitude,
|
||||||
Address: address,
|
Address: address,
|
||||||
PortAmount: devices.PortAmount,
|
PortAmount: device.PortAmount,
|
||||||
Status: string(devices.Status),
|
Status: string(device.Status),
|
||||||
CreatedAt: devices.CreatedAt,
|
Province: device.Province, // Nullable field
|
||||||
UpdatedAt: devices.UpdatedAt,
|
City: device.City, // Nullable field
|
||||||
}
|
District: device.District, // Nullable field
|
||||||
responses = append(responses, deviceResp)
|
CreatedAt: device.CreatedAt,
|
||||||
}
|
UpdatedAt: device.UpdatedAt,
|
||||||
|
}
|
||||||
|
|
||||||
return responses, nil
|
responses = append(responses, response)
|
||||||
|
}
|
||||||
|
|
||||||
|
return responses, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ConvertToDeviceResponseId (devices entity.Device, geocoder service.GeocodingService) (res.DeviceResponse, error) {
|
func ConvertToDeviceResponseId (devices entity.Device, geocoder service.GeocodingService) (res.DeviceResponse, error) {
|
||||||
var address string
|
var address string
|
||||||
|
|
||||||
|
|
@ -79,6 +78,9 @@ func ConvertToDeviceResponseId (devices entity.Device, geocoder service.Geocodin
|
||||||
Longitude: devices.Longitude,
|
Longitude: devices.Longitude,
|
||||||
Latitude: devices.Latitude,
|
Latitude: devices.Latitude,
|
||||||
Address: address,
|
Address: address,
|
||||||
|
Province: devices.Province,
|
||||||
|
City: devices.City,
|
||||||
|
District: devices.District,
|
||||||
PortAmount: devices.PortAmount,
|
PortAmount: devices.PortAmount,
|
||||||
Status: string(devices.Status),
|
Status: string(devices.Status),
|
||||||
CreatedAt: devices.CreatedAt,
|
CreatedAt: devices.CreatedAt,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,125 @@
|
||||||
|
package helper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"users_management/m/model/dto/res"
|
||||||
|
"users_management/m/model/entity"
|
||||||
|
"users_management/m/utils/service"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ConvertToDeviceDetailsResponses(devices []entity.DeviceDetails, geocoder service.GeocodingService) ([]res.DeviceDetailsResponse, error) {
|
||||||
|
var responses []res.DeviceDetailsResponse
|
||||||
|
|
||||||
|
for _, device := range devices {
|
||||||
|
response, err := ConvertToDeviceDetailsResponse(device, geocoder)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
responses = append(responses, response)
|
||||||
|
}
|
||||||
|
|
||||||
|
return responses, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ConvertToDeviceDetailsResponse(device entity.DeviceDetails, geocoder service.GeocodingService) (res.DeviceDetailsResponse, error) {
|
||||||
|
// Get address
|
||||||
|
address := ""
|
||||||
|
if geocoder != nil {
|
||||||
|
addr, err := geocoder.GetAddressFromCoordinates(device.Latitude, device.Longitude)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Geocoding error for device %s: %v", device.DeviceCode, err)
|
||||||
|
address = fmt.Sprintf("Coordinates: %.6f, %.6f", device.Latitude, device.Longitude)
|
||||||
|
} else {
|
||||||
|
address = addr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all backbones connected to this device
|
||||||
|
allBackbones := device.GetAllBackbones()
|
||||||
|
backboneInfos := make([]res.BackboneConnectionInfo, 0)
|
||||||
|
for _, backbone := range allBackbones {
|
||||||
|
isStartDevice := backbone.DeviceStartID == device.ID
|
||||||
|
connectedTo := ""
|
||||||
|
|
||||||
|
if isStartDevice && backbone.DeviceEnd.DeviceCode != "" {
|
||||||
|
connectedTo = backbone.DeviceEnd.DeviceCode
|
||||||
|
} else if !isStartDevice && backbone.DeviceStart.DeviceCode != "" {
|
||||||
|
connectedTo = backbone.DeviceStart.DeviceCode
|
||||||
|
}
|
||||||
|
|
||||||
|
info := res.BackboneConnectionInfo{
|
||||||
|
ID: backbone.ID,
|
||||||
|
BackboneCode: backbone.BackboneCode,
|
||||||
|
CoreAmount: backbone.CoreAmount,
|
||||||
|
IsStartDevice: isStartDevice,
|
||||||
|
ConnectedTo: connectedTo,
|
||||||
|
}
|
||||||
|
backboneInfos = append(backboneInfos, info)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all fishbones connected to this device
|
||||||
|
allFishbones := device.GetAllFishbones()
|
||||||
|
fishboneInfos := make([]res.FishboneConnectionInfo, 0)
|
||||||
|
for _, fishbone := range allFishbones {
|
||||||
|
isStartDevice := fishbone.DeviceStartID == device.ID
|
||||||
|
connectedTo := ""
|
||||||
|
|
||||||
|
if isStartDevice && fishbone.DeviceEnd.DeviceCode != "" {
|
||||||
|
connectedTo = fishbone.DeviceEnd.DeviceCode
|
||||||
|
} else if !isStartDevice && fishbone.DeviceStart.DeviceCode != "" {
|
||||||
|
connectedTo = fishbone.DeviceStart.DeviceCode
|
||||||
|
}
|
||||||
|
|
||||||
|
info := res.FishboneConnectionInfo{
|
||||||
|
ID: fishbone.ID,
|
||||||
|
FishboneCode: fishbone.FishboneCode,
|
||||||
|
CoreAmount: fishbone.CoreAmount,
|
||||||
|
BackboneCode: fishbone.Backbone.BackboneCode,
|
||||||
|
IsStartDevice: isStartDevice,
|
||||||
|
ConnectedTo: connectedTo,
|
||||||
|
}
|
||||||
|
fishboneInfos = append(fishboneInfos, info)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert tower connections
|
||||||
|
towerInfos := make([]res.TowerConnectionDetail, 0)
|
||||||
|
for _, tower := range device.Towers {
|
||||||
|
distance := calculateDistance(device.Latitude, device.Longitude, tower.Latitude, tower.Longitude)
|
||||||
|
|
||||||
|
info := res.TowerConnectionDetail{
|
||||||
|
ID: tower.ID,
|
||||||
|
TowerCode: tower.TowerCode,
|
||||||
|
Distance: distance,
|
||||||
|
ImageURL: &tower.ImageURL,
|
||||||
|
}
|
||||||
|
towerInfos = append(towerInfos, info)
|
||||||
|
}
|
||||||
|
|
||||||
|
response := res.DeviceDetailsResponse{
|
||||||
|
ID: device.ID,
|
||||||
|
DeviceCode: device.DeviceCode,
|
||||||
|
DeviceType: string(device.DeviceType),
|
||||||
|
Address: address,
|
||||||
|
Longitude: device.Longitude,
|
||||||
|
Latitude: device.Latitude,
|
||||||
|
Status: string(device.Status),
|
||||||
|
PortAmount: device.PortAmount,
|
||||||
|
PortUsed: device.DevicePort.PortUsed,
|
||||||
|
PortAvailable: device.DevicePort.PortAvailable,
|
||||||
|
Region: device.Region,
|
||||||
|
Province: device.Province,
|
||||||
|
City: device.City,
|
||||||
|
District: device.District,
|
||||||
|
ImageURL: device.ImageURL,
|
||||||
|
Backbones: backboneInfos,
|
||||||
|
Fishbones: fishboneInfos,
|
||||||
|
Towers: towerInfos,
|
||||||
|
CreatedAt: device.CreatedAt,
|
||||||
|
UpdatedAt: device.UpdatedAt,
|
||||||
|
}
|
||||||
|
|
||||||
|
return response, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ... rest of helper functions remain the same
|
||||||
|
|
@ -0,0 +1,103 @@
|
||||||
|
package helper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"users_management/m/model/dto/res"
|
||||||
|
"users_management/m/model/entity"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IsEmptyUUID checks if a UUID is empty (all zeros)
|
||||||
|
func IsEmptyUUID(id [16]byte) bool {
|
||||||
|
return bytes.Equal(id[:], make([]byte, 16))
|
||||||
|
}
|
||||||
|
|
||||||
|
func ConvertToDeviceInspectionResponses(inspections []entity.DeviceInspection) []res.DeviceInspectionResponse {
|
||||||
|
var responses []res.DeviceInspectionResponse
|
||||||
|
|
||||||
|
for _, inspection := range inspections {
|
||||||
|
response := res.DeviceInspectionResponse{
|
||||||
|
ID: inspection.ID,
|
||||||
|
DeviceCode: inspection.Device.DeviceCode,
|
||||||
|
DeviceType: string(inspection.Device.DeviceType),
|
||||||
|
UserName: inspection.User.Name,
|
||||||
|
Status: inspection.Status,
|
||||||
|
PortUsed: inspection.PortUsed,
|
||||||
|
PortAvailable: inspection.PortAvailable,
|
||||||
|
CableAmount: inspection.CableAmount,
|
||||||
|
Description: inspection.Description,
|
||||||
|
ImageURL: inspection.ImageURL,
|
||||||
|
InspectionPlacement: inspection.InspectionPlacement, // Full address
|
||||||
|
Longitude: inspection.Longitude,
|
||||||
|
Latitude: inspection.Latitude,
|
||||||
|
InspectionApproval: inspection.InspectionApproval,
|
||||||
|
CreatedAt: inspection.CreatedAt,
|
||||||
|
UpdatedAt: inspection.UpdatedAt,
|
||||||
|
}
|
||||||
|
|
||||||
|
if inspection.Backbone != nil && !IsEmptyUUID(inspection.Backbone.ID) {
|
||||||
|
response.BackboneCode = &inspection.Backbone.BackboneCode
|
||||||
|
}
|
||||||
|
if inspection.Fishbone != nil && !IsEmptyUUID(inspection.Fishbone.ID) {
|
||||||
|
response.FishboneCode = &inspection.Fishbone.FishboneCode
|
||||||
|
}
|
||||||
|
if inspection.Tower != nil && !IsEmptyUUID(inspection.Tower.ID) {
|
||||||
|
response.TowerCode = &inspection.Tower.TowerCode
|
||||||
|
}
|
||||||
|
|
||||||
|
responses = append(responses, response)
|
||||||
|
}
|
||||||
|
|
||||||
|
return responses
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func ConvertToDeviceInspectionDetailResponse(inspection entity.DeviceInspection) res.DeviceInspectionDetailResponse {
|
||||||
|
response := res.DeviceInspectionDetailResponse{
|
||||||
|
ID: inspection.ID,
|
||||||
|
Device: res.DeviceInfo{
|
||||||
|
ID: inspection.Device.ID,
|
||||||
|
DeviceCode: inspection.Device.DeviceCode,
|
||||||
|
DeviceType: string(inspection.Device.DeviceType),
|
||||||
|
},
|
||||||
|
User: res.UserInfo{
|
||||||
|
ID: inspection.User.ID,
|
||||||
|
Name: inspection.User.Name,
|
||||||
|
Username: inspection.User.Username,
|
||||||
|
},
|
||||||
|
Status: inspection.Status,
|
||||||
|
PortUsed: inspection.PortUsed,
|
||||||
|
PortAvailable: inspection.PortAvailable,
|
||||||
|
CableAmount: inspection.CableAmount,
|
||||||
|
Description: inspection.Description,
|
||||||
|
ImageURL: inspection.ImageURL,
|
||||||
|
InspectionPlacement: inspection.InspectionPlacement, // Full address
|
||||||
|
Longitude: inspection.Longitude,
|
||||||
|
Latitude: inspection.Latitude,
|
||||||
|
InspectionApproval: inspection.InspectionApproval,
|
||||||
|
CreatedAt: inspection.CreatedAt,
|
||||||
|
UpdatedAt: inspection.UpdatedAt,
|
||||||
|
}
|
||||||
|
|
||||||
|
if inspection.Backbone != nil && !IsEmptyUUID(inspection.Backbone.ID) {
|
||||||
|
response.Backbone = &res.BackboneInfo{
|
||||||
|
ID: inspection.Backbone.ID,
|
||||||
|
BackboneCode: inspection.Backbone.BackboneCode,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if inspection.Fishbone != nil && !IsEmptyUUID(inspection.Fishbone.ID) {
|
||||||
|
response.Fishbone = &res.FishboneInfo{
|
||||||
|
ID: inspection.Fishbone.ID,
|
||||||
|
FishboneCode: inspection.Fishbone.FishboneCode,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if inspection.Tower != nil && !IsEmptyUUID(inspection.Tower.ID) {
|
||||||
|
response.Tower = &res.TowerInfo{
|
||||||
|
ID: inspection.Tower.ID,
|
||||||
|
TowerCode: inspection.Tower.TowerCode,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,212 @@
|
||||||
|
package helper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"math"
|
||||||
|
"users_management/m/model/dto/res"
|
||||||
|
"users_management/m/model/entity"
|
||||||
|
"users_management/m/repository"
|
||||||
|
"users_management/m/utils/service"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ConvertToNearestDeviceResponses(devices []entity.DeviceWithDistance, repo repository.NearestDeviceRepo, geocoder service.GeocodingService) ([]res.NearestDeviceResponse, error) {
|
||||||
|
var responses []res.NearestDeviceResponse
|
||||||
|
|
||||||
|
for _, device := range devices {
|
||||||
|
// Get address
|
||||||
|
address := ""
|
||||||
|
if geocoder != nil {
|
||||||
|
addr, err := geocoder.GetAddressFromCoordinates(device.Latitude, device.Longitude)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Geocoding error for device %s: %v", device.DeviceCode, err)
|
||||||
|
address = fmt.Sprintf("Coordinates: %.6f, %.6f", device.Latitude, device.Longitude)
|
||||||
|
} else {
|
||||||
|
address = addr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count connections
|
||||||
|
backboneCount, fishboneCount, towerCount, err := repo.CountConnectionsByDeviceID(device.ID)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error counting connections for device %s: %v", device.DeviceCode, err)
|
||||||
|
backboneCount, fishboneCount, towerCount = 0, 0, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
response := res.NearestDeviceResponse{
|
||||||
|
ID: device.ID,
|
||||||
|
DeviceCode: device.DeviceCode,
|
||||||
|
DeviceType: string(device.DeviceType),
|
||||||
|
Distance: device.Distance, // Use the calculated distance from query
|
||||||
|
Address: address,
|
||||||
|
Longitude: device.Longitude,
|
||||||
|
Latitude: device.Latitude,
|
||||||
|
Status: string(device.Status),
|
||||||
|
PortAmount: device.PortAmount,
|
||||||
|
Region: device.Region,
|
||||||
|
Province: device.Province,
|
||||||
|
City: device.City,
|
||||||
|
District: device.District,
|
||||||
|
BackboneCount: backboneCount,
|
||||||
|
FishboneCount: fishboneCount,
|
||||||
|
TowerCount: towerCount,
|
||||||
|
CreatedAt: device.CreatedAt,
|
||||||
|
}
|
||||||
|
|
||||||
|
responses = append(responses, response)
|
||||||
|
}
|
||||||
|
|
||||||
|
return responses, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ConvertToNearestDeviceDetailResponse(device entity.Device, distance float64, repo repository.NearestDeviceRepo, geocoder service.GeocodingService) (res.NearestDeviceDetailResponse, error) {
|
||||||
|
// Get address
|
||||||
|
address := ""
|
||||||
|
if geocoder != nil {
|
||||||
|
addr, err := geocoder.GetAddressFromCoordinates(device.Latitude, device.Longitude)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Geocoding error for device %s: %v", device.DeviceCode, err)
|
||||||
|
address = fmt.Sprintf("Coordinates: %.6f, %.6f", device.Latitude, device.Longitude)
|
||||||
|
} else {
|
||||||
|
address = addr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get detailed connection information
|
||||||
|
backbones, err := repo.GetBackbonesByDeviceID(device.ID)
|
||||||
|
if err != nil {
|
||||||
|
return res.NearestDeviceDetailResponse{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
fishbones, err := repo.GetFishbonesByDeviceID(device.ID)
|
||||||
|
if err != nil {
|
||||||
|
return res.NearestDeviceDetailResponse{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
towers, err := repo.GetTowersByDeviceID(device.ID)
|
||||||
|
if err != nil {
|
||||||
|
return res.NearestDeviceDetailResponse{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to response format
|
||||||
|
backboneInfos := convertToBackboneConnectionInfos(backbones, device.ID)
|
||||||
|
fishboneInfos := convertToFishboneConnectionInfos(fishbones, device.ID)
|
||||||
|
towerInfos := convertToTowerConnectionInfos(towers, device.Latitude, device.Longitude)
|
||||||
|
|
||||||
|
response := res.NearestDeviceDetailResponse{
|
||||||
|
ID: device.ID,
|
||||||
|
DeviceCode: device.DeviceCode,
|
||||||
|
DeviceType: string(device.DeviceType),
|
||||||
|
Distance: distance,
|
||||||
|
Address: address,
|
||||||
|
Longitude: device.Longitude,
|
||||||
|
Latitude: device.Latitude,
|
||||||
|
Status: string(device.Status),
|
||||||
|
PortAmount: device.PortAmount,
|
||||||
|
Province: device.Province,
|
||||||
|
City: device.City,
|
||||||
|
District: device.District,
|
||||||
|
ImageURL: device.ImageURL,
|
||||||
|
Backbones: backboneInfos,
|
||||||
|
Fishbones: fishboneInfos,
|
||||||
|
Towers: towerInfos,
|
||||||
|
CreatedAt: device.CreatedAt,
|
||||||
|
UpdatedAt: device.UpdatedAt,
|
||||||
|
}
|
||||||
|
|
||||||
|
return response, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertToBackboneConnectionInfos(backbones []entity.Backbone, deviceID uuid.UUID) []res.BackboneConnectionInfo {
|
||||||
|
var infos []res.BackboneConnectionInfo
|
||||||
|
|
||||||
|
for _, backbone := range backbones {
|
||||||
|
isStartDevice := backbone.DeviceStartID == deviceID
|
||||||
|
connectedTo := ""
|
||||||
|
|
||||||
|
if isStartDevice && backbone.DeviceEnd.DeviceCode != "" {
|
||||||
|
connectedTo = backbone.DeviceEnd.DeviceCode
|
||||||
|
} else if !isStartDevice && backbone.DeviceStart.DeviceCode != "" {
|
||||||
|
connectedTo = backbone.DeviceStart.DeviceCode
|
||||||
|
}
|
||||||
|
|
||||||
|
info := res.BackboneConnectionInfo{
|
||||||
|
ID: backbone.ID,
|
||||||
|
BackboneCode: backbone.BackboneCode,
|
||||||
|
CoreAmount: backbone.CoreAmount,
|
||||||
|
IsStartDevice: isStartDevice,
|
||||||
|
ConnectedTo: connectedTo,
|
||||||
|
}
|
||||||
|
|
||||||
|
infos = append(infos, info)
|
||||||
|
}
|
||||||
|
|
||||||
|
return infos
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertToFishboneConnectionInfos(fishbones []entity.Fishbone, deviceID uuid.UUID) []res.FishboneConnectionInfo {
|
||||||
|
var infos []res.FishboneConnectionInfo
|
||||||
|
|
||||||
|
for _, fishbone := range fishbones {
|
||||||
|
isStartDevice := fishbone.DeviceStartID == deviceID
|
||||||
|
connectedTo := ""
|
||||||
|
|
||||||
|
if isStartDevice && fishbone.DeviceEnd.DeviceCode != "" {
|
||||||
|
connectedTo = fishbone.DeviceEnd.DeviceCode
|
||||||
|
} else if !isStartDevice && fishbone.DeviceStart.DeviceCode != "" {
|
||||||
|
connectedTo = fishbone.DeviceStart.DeviceCode
|
||||||
|
}
|
||||||
|
|
||||||
|
info := res.FishboneConnectionInfo{
|
||||||
|
ID: fishbone.ID,
|
||||||
|
FishboneCode: fishbone.FishboneCode,
|
||||||
|
CoreAmount: fishbone.CoreAmount,
|
||||||
|
BackboneCode: fishbone.Backbone.BackboneCode,
|
||||||
|
IsStartDevice: isStartDevice,
|
||||||
|
ConnectedTo: connectedTo,
|
||||||
|
}
|
||||||
|
|
||||||
|
infos = append(infos, info)
|
||||||
|
}
|
||||||
|
|
||||||
|
return infos
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertToTowerConnectionInfos(towers []entity.Tower, deviceLat, deviceLng float64) []res.TowerConnectionInfo {
|
||||||
|
var infos []res.TowerConnectionInfo
|
||||||
|
|
||||||
|
for _, tower := range towers {
|
||||||
|
// Calculate distance from device to tower
|
||||||
|
distance := calculateDistance(deviceLat, deviceLng, tower.Latitude, tower.Longitude)
|
||||||
|
|
||||||
|
info := res.TowerConnectionInfo{
|
||||||
|
ID: tower.ID,
|
||||||
|
TowerCode: tower.TowerCode,
|
||||||
|
Distance: distance,
|
||||||
|
ImageURL: &tower.ImageURL,
|
||||||
|
}
|
||||||
|
|
||||||
|
infos = append(infos, info)
|
||||||
|
}
|
||||||
|
|
||||||
|
return infos
|
||||||
|
}
|
||||||
|
|
||||||
|
func calculateDistance(lat1, lng1, lat2, lng2 float64) float64 {
|
||||||
|
const earthRadius = 6371 // Earth's radius in kilometers
|
||||||
|
|
||||||
|
lat1Rad := lat1 * math.Pi / 180
|
||||||
|
lng1Rad := lng1 * math.Pi / 180
|
||||||
|
lat2Rad := lat2 * math.Pi / 180
|
||||||
|
lng2Rad := lng2 * math.Pi / 180
|
||||||
|
|
||||||
|
dlat := lat2Rad - lat1Rad
|
||||||
|
dlng := lng2Rad - lng1Rad
|
||||||
|
|
||||||
|
a := math.Sin(dlat/2)*math.Sin(dlat/2) + math.Cos(lat1Rad)*math.Cos(lat2Rad)*math.Sin(dlng/2)*math.Sin(dlng/2)
|
||||||
|
c := 2 * math.Atan2(math.Sqrt(a), math.Sqrt(1-a))
|
||||||
|
|
||||||
|
return earthRadius * c
|
||||||
|
}
|
||||||
|
|
@ -29,7 +29,7 @@ func ConvertToTowerResponses(towers []entity.Tower, geocoder service.GeocodingSe
|
||||||
towerResp := res.TowerResponse{
|
towerResp := res.TowerResponse{
|
||||||
ID: tower.ID,
|
ID: tower.ID,
|
||||||
DeviceCode: tower.Device.DeviceCode,
|
DeviceCode: tower.Device.DeviceCode,
|
||||||
TowerCode: tower.TowerCode,
|
TowerCode: &tower.TowerCode,
|
||||||
Longitude: tower.Longitude,
|
Longitude: tower.Longitude,
|
||||||
Latitude: tower.Latitude,
|
Latitude: tower.Latitude,
|
||||||
Address: address,
|
Address: address,
|
||||||
|
|
@ -59,7 +59,7 @@ func ConvertToTowerIDResponses(tower entity.Tower, geocoder service.GeocodingSer
|
||||||
towerResp := res.TowerResponse{
|
towerResp := res.TowerResponse{
|
||||||
ID: tower.ID,
|
ID: tower.ID,
|
||||||
DeviceCode: tower.Device.DeviceCode,
|
DeviceCode: tower.Device.DeviceCode,
|
||||||
TowerCode: tower.TowerCode,
|
TowerCode: &tower.TowerCode,
|
||||||
Longitude: tower.Longitude,
|
Longitude: tower.Longitude,
|
||||||
Latitude: tower.Latitude,
|
Latitude: tower.Latitude,
|
||||||
Address: address,
|
Address: address,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue