NAM-APJATEL-BACKEND/usecase/nearest_device.go

165 lines
4.4 KiB
Go

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)
GetNearestTowers(request req.NearestTowerDTO) ([]res.NearestTowerResponse, error)
GetNearestTowerByID(id uuid.UUID, userLat, userLng float64) (res.NearestTowerDetailResponse, 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) GetNearestTowers(request req.NearestTowerDTO) ([]res.NearestTowerResponse, 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 towers
}
towers, err := u.nearestDeviceRepo.GetNearestTowers(
request.Longitude,
request.Latitude,
radius,
limit,
request.Province,
request.City,
request.District,
)
if err != nil {
return nil, err
}
responses, err := helper.ConvertToNearestTowerResponses(towers, u.geocoder)
if err != nil {
return nil, err
}
return responses, nil
}
func (u *nearestDeviceUseCase) GetNearestTowerByID(id uuid.UUID, userLat, userLng float64) (res.NearestTowerDetailResponse, error) {
tower, err := u.nearestDeviceRepo.GetTowerByIDWithConnections(id)
if err != nil {
return res.NearestTowerDetailResponse{}, err
}
// Calculate distance
distance := calculateDistance(userLat, userLng, tower.Latitude, tower.Longitude)
response, err := helper.ConvertToNearestTowerDetailResponse(tower, distance, u.nearestDeviceRepo, u.geocoder)
if err != nil {
return res.NearestTowerDetailResponse{}, err
}
return response, nil
}
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,
request.DeviceType,
)
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
}