package repository import ( "github.com/google/uuid" "gorm.io/gorm" "users_management/m/model/entity" ) type NearestDeviceRepo interface { GetNearestDevices(longitude, latitude, radius float64, limit int, province, city, district, deviceType *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) GetNearestTowers(longitude, latitude, radius float64, limit int, province, city, district *string) ([]entity.TowerWithDistance, error) GetTowerByIDWithConnections(id uuid.UUID) (entity.Tower, error) } type nearestDeviceRepo struct { db *gorm.DB } func NewNearestDeviceRepo(db *gorm.DB) NearestDeviceRepo { return &nearestDeviceRepo{ db: db, } } func (r *nearestDeviceRepo) GetNearestTowers(longitude, latitude, radius float64, limit int, province, city, district *string) ([]entity.TowerWithDistance, error) { var towers []entity.TowerWithDistance // Build the subquery first - join with devices to get location filters subQuery := r.db.Table("towers"). Select(`towers.id, towers.tower_code, towers.longitude, towers.latitude, towers.image_url, towers.external_tower, towers.dev_id, towers.created_at, towers.updated_at, devices.device_code, devices.province, devices.city, devices.district, (6371 * acos(cos(radians(?)) * cos(radians(towers.latitude)) * cos(radians(towers.longitude) - radians(?)) + sin(radians(?)) * sin(radians(towers.latitude)))) AS distance`, latitude, longitude, latitude). Joins("LEFT JOIN devices ON towers.dev_id = devices.id") // Apply location filters to subquery if province != nil && *province != "" { subQuery = subQuery.Where("devices.province = ?", *province) } if city != nil && *city != "" { subQuery = subQuery.Where("devices.city = ?", *city) } if district != nil && *district != "" { subQuery = subQuery.Where("devices.district = ?", *district) } // Use the subquery in the main query err := r.db.Table("(?) as towers_with_distance", subQuery). Where("distance <= ?", radius). Order("distance ASC"). Limit(limit). Scan(&towers).Error return towers, err } func (r *nearestDeviceRepo) GetTowerByIDWithConnections(id uuid.UUID) (entity.Tower, error) { var tower entity.Tower err := r.db.Preload("Device").Where("id = ?", id).First(&tower).Error return tower, err } func (r *nearestDeviceRepo) GetNearestDevices(longitude, latitude, radius float64, limit int, province, city, district, deviceType *string) ([]entity.DeviceWithDistance, error) { var devices []entity.DeviceWithDistance 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) 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) } if deviceType != nil && *deviceType != "" { subQuery = subQuery.Where("device_type = ?", *deviceType) } 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 }