using HuntingAppApi.Models; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Npgsql; namespace HuntingAppApi.Controllers; [ApiController] [Authorize] [Route("api/locations")] public sealed class LocationsController(NpgsqlDataSource dataSource) : ControllerBase { [HttpPost] public async Task Upsert([FromBody] UpsertLocationRequest request, CancellationToken ct) { await using var conn = await dataSource.OpenConnectionAsync(ct); await using var tx = await conn.BeginTransactionAsync(ct); await using (var current = new NpgsqlCommand(""" INSERT INTO user_current_location (participant_id, geom, accuracy_m, speed_mps, heading_degrees, battery_percent, recorded_at) VALUES (@participant_id, ST_SetSRID(ST_MakePoint(@longitude, @latitude), 4326), @accuracy_m, @speed_mps, @heading_degrees, @battery_percent, @recorded_at) ON CONFLICT (participant_id) DO UPDATE SET geom = EXCLUDED.geom, accuracy_m = EXCLUDED.accuracy_m, speed_mps = EXCLUDED.speed_mps, heading_degrees = EXCLUDED.heading_degrees, battery_percent = EXCLUDED.battery_percent, recorded_at = EXCLUDED.recorded_at, received_at = now() """, conn, tx)) { AddLocationParams(current, request); await current.ExecuteNonQueryAsync(ct); } await using (var history = new NpgsqlCommand(""" INSERT INTO user_location_history (participant_id, geom, accuracy_m, speed_mps, heading_degrees, battery_percent, recorded_at) VALUES (@participant_id, ST_SetSRID(ST_MakePoint(@longitude, @latitude), 4326), @accuracy_m, @speed_mps, @heading_degrees, @battery_percent, @recorded_at) """, conn, tx)) { AddLocationParams(history, request); await history.ExecuteNonQueryAsync(ct); } await tx.CommitAsync(ct); return NoContent(); } private static void AddLocationParams(NpgsqlCommand cmd, UpsertLocationRequest request) { cmd.Parameters.AddWithValue("participant_id", request.ParticipantId); cmd.Parameters.AddWithValue("longitude", request.Longitude); cmd.Parameters.AddWithValue("latitude", request.Latitude); cmd.Parameters.AddWithValue("accuracy_m", (object?)request.AccuracyM ?? DBNull.Value); cmd.Parameters.AddWithValue("speed_mps", (object?)request.SpeedMps ?? DBNull.Value); cmd.Parameters.AddWithValue("heading_degrees", (object?)request.HeadingDegrees ?? DBNull.Value); cmd.Parameters.AddWithValue("battery_percent", (object?)request.BatteryPercent ?? DBNull.Value); cmd.Parameters.AddWithValue("recorded_at", request.RecordedAt); } }