311 lines
8.3 KiB
JavaScript
311 lines
8.3 KiB
JavaScript
const db = require('../config/db');
|
|
const agheeraPushClient = require('../services/agheeraPushClient');
|
|
|
|
const AGHEERA_CLIENT_ID = 532;
|
|
|
|
const getDniFromLocation = (locationData) => {
|
|
if (locationData?.extras?.alias) {
|
|
return String(locationData.extras.alias);
|
|
}
|
|
|
|
if (locationData?.alias) {
|
|
return String(locationData.alias);
|
|
}
|
|
|
|
return null;
|
|
};
|
|
|
|
const getCoordinatesFromLocation = (locationData) => {
|
|
if (locationData?.location?.coords) {
|
|
return {
|
|
lat: locationData.location.coords.latitude,
|
|
lng: locationData.location.coords.longitude
|
|
};
|
|
}
|
|
|
|
if (locationData?.coords) {
|
|
return {
|
|
lat: locationData.coords.latitude,
|
|
lng: locationData.coords.longitude
|
|
};
|
|
}
|
|
|
|
if (
|
|
Object.prototype.hasOwnProperty.call(locationData || {}, 'latitude') &&
|
|
Object.prototype.hasOwnProperty.call(locationData || {}, 'longitude')
|
|
) {
|
|
return {
|
|
lat: locationData.latitude,
|
|
lng: locationData.longitude
|
|
};
|
|
}
|
|
|
|
return null;
|
|
};
|
|
|
|
const normalizeTripId = (rawValue) => {
|
|
if (rawValue === undefined || rawValue === null || String(rawValue).trim() === '') {
|
|
return null;
|
|
}
|
|
|
|
const parsed = Number.parseInt(String(rawValue).trim(), 10);
|
|
return Number.isInteger(parsed) && parsed > 0 ? parsed : null;
|
|
};
|
|
|
|
const getTripIdFromLocation = (locationData) => {
|
|
const candidates = [
|
|
locationData?.id_viaje,
|
|
locationData?.tripId,
|
|
locationData?.idViaje,
|
|
locationData?.params?.id_viaje,
|
|
locationData?.params?.tripId,
|
|
locationData?.extras?.id_viaje,
|
|
locationData?.extras?.tripId
|
|
];
|
|
|
|
for (const value of candidates) {
|
|
const normalized = normalizeTripId(value);
|
|
if (normalized !== null) {
|
|
return normalized;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
};
|
|
|
|
const getRawTimestampFromLocation = (locationData) => {
|
|
const candidates = [
|
|
locationData?.timestamp,
|
|
locationData?.location?.timestamp
|
|
];
|
|
|
|
for (const value of candidates) {
|
|
if (value !== undefined && value !== null && String(value).trim() !== '') {
|
|
return String(value).trim();
|
|
}
|
|
}
|
|
|
|
return null;
|
|
};
|
|
|
|
const getPersistedTimestampFromLocation = (locationData) => {
|
|
const rawTimestamp = getRawTimestampFromLocation(locationData);
|
|
|
|
if (rawTimestamp === null) {
|
|
return {
|
|
value: new Date(),
|
|
usedFallback: true,
|
|
reason: 'missing'
|
|
};
|
|
}
|
|
|
|
const parsedTimestamp = new Date(rawTimestamp);
|
|
if (Number.isNaN(parsedTimestamp.getTime())) {
|
|
return {
|
|
value: new Date(),
|
|
usedFallback: true,
|
|
reason: 'invalid',
|
|
rawTimestamp
|
|
};
|
|
}
|
|
|
|
return {
|
|
value: parsedTimestamp,
|
|
usedFallback: false,
|
|
reason: null,
|
|
rawTimestamp
|
|
};
|
|
};
|
|
|
|
|
|
const pushLocationToAgheera = async ({ latitude, longitude, dni, tripId, measurementTime }) => {
|
|
if (!tripId || !dni) {
|
|
return null;
|
|
}
|
|
|
|
const [tripRows] = await db.query(
|
|
`SELECT id_cliente
|
|
FROM c_viajes
|
|
WHERE id_viaje = ?
|
|
LIMIT 1`,
|
|
[tripId]
|
|
);
|
|
|
|
if (Number.parseInt(tripRows[0]?.id_cliente, 10) !== AGHEERA_CLIENT_ID) {
|
|
return null;
|
|
}
|
|
|
|
const baseResult = {
|
|
trip_id: tripId,
|
|
attempted: true,
|
|
success: false,
|
|
http_status: null,
|
|
error: null
|
|
};
|
|
|
|
const [authorizationRows] = await db.query(
|
|
`SELECT id_tipovehiculo AS matricula
|
|
FROM c_viajes_proveedor
|
|
WHERE id_viaje = ?
|
|
AND dni = ?
|
|
ORDER BY n_proveedor ASC
|
|
LIMIT 1`,
|
|
[tripId, dni]
|
|
);
|
|
|
|
const licensePlate = String(authorizationRows[0]?.matricula || '').trim();
|
|
if (!licensePlate) {
|
|
return {
|
|
...baseResult,
|
|
error: 'LICENSE_PLATE_NOT_FOUND'
|
|
};
|
|
}
|
|
|
|
try {
|
|
const result = await agheeraPushClient.pushPosition({
|
|
latitude,
|
|
longitude,
|
|
vehicleId: licensePlate,
|
|
licensePlate,
|
|
measurementTime
|
|
});
|
|
|
|
return {
|
|
...baseResult,
|
|
success: true,
|
|
http_status: result?.status ?? null
|
|
};
|
|
} catch (error) {
|
|
console.error('Agheera push failed after location update:', {
|
|
tripId,
|
|
dni,
|
|
message: error.message,
|
|
status: error.status || null
|
|
});
|
|
|
|
return {
|
|
...baseResult,
|
|
http_status: error.status || null,
|
|
error: error.message
|
|
};
|
|
}
|
|
};
|
|
|
|
const pushLocationsToAgheera = async (locationsToPush) => {
|
|
const results = [];
|
|
|
|
for (const location of locationsToPush) {
|
|
const result = await pushLocationToAgheera(location);
|
|
if (result) {
|
|
results.push(result);
|
|
}
|
|
}
|
|
|
|
return results;
|
|
};
|
|
|
|
const saveLocation = async (req, res) => {
|
|
try {
|
|
const data = req.body;
|
|
console.log('Location payload received:', JSON.stringify(data, null, 2));
|
|
|
|
let locations = [];
|
|
let globalDni = null;
|
|
let globalTripId = null;
|
|
|
|
if (req.user?.dni) {
|
|
globalDni = String(req.user.dni);
|
|
}
|
|
|
|
if (Array.isArray(data)) {
|
|
locations = data;
|
|
} else if (data?.location && Array.isArray(data.location)) {
|
|
locations = data.location;
|
|
if (!globalDni) {
|
|
if (data.alias) {
|
|
globalDni = String(data.alias);
|
|
} else if (data.params?.alias) {
|
|
globalDni = String(data.params.alias);
|
|
}
|
|
}
|
|
globalTripId = getTripIdFromLocation(data);
|
|
} else {
|
|
locations = [data];
|
|
globalTripId = getTripIdFromLocation(data);
|
|
}
|
|
|
|
const rowsToInsert = [];
|
|
const locationsToPush = [];
|
|
|
|
for (const loc of locations) {
|
|
const coords = getCoordinatesFromLocation(loc);
|
|
const dni = globalDni || getDniFromLocation(loc);
|
|
const tripId = globalTripId !== null ? globalTripId : getTripIdFromLocation(loc);
|
|
|
|
if (coords && coords.lat !== undefined && coords.lat !== null && coords.lng !== undefined && coords.lng !== null) {
|
|
const persistedTimestamp = getPersistedTimestampFromLocation(loc);
|
|
|
|
if (persistedTimestamp.usedFallback) {
|
|
console.warn('Location timestamp fallback applied:', {
|
|
reason: persistedTimestamp.reason,
|
|
rawTimestamp: persistedTimestamp.rawTimestamp || null,
|
|
uuid: loc?.uuid || loc?.location?.uuid || null,
|
|
tripId,
|
|
dni: dni || null
|
|
});
|
|
}
|
|
|
|
rowsToInsert.push([
|
|
String(coords.lat),
|
|
String(coords.lng),
|
|
dni || null,
|
|
persistedTimestamp.value,
|
|
tripId
|
|
]);
|
|
locationsToPush.push({
|
|
latitude: coords.lat,
|
|
longitude: coords.lng,
|
|
dni,
|
|
tripId,
|
|
measurementTime: persistedTimestamp.value
|
|
});
|
|
}
|
|
}
|
|
|
|
if (rowsToInsert.length === 0) {
|
|
return res.json({
|
|
success: false,
|
|
error: 'no_coords',
|
|
message: 'No valid coordinates found in batch/object'
|
|
});
|
|
}
|
|
|
|
await db.query(
|
|
`INSERT INTO c_trazabilidad_transportista
|
|
(latitud, longitud, id_usuario, fecha, id_viaje)
|
|
VALUES ?`,
|
|
[rowsToInsert]
|
|
);
|
|
|
|
const agheeraResults = await pushLocationsToAgheera(locationsToPush);
|
|
const responseBody = {
|
|
success: true,
|
|
count: rowsToInsert.length,
|
|
message: 'Locations saved'
|
|
};
|
|
|
|
if (agheeraResults.length > 0) {
|
|
responseBody.agheera_push = agheeraResults.length === 1 ? agheeraResults[0] : agheeraResults;
|
|
}
|
|
|
|
return res.json(responseBody);
|
|
} catch (error) {
|
|
console.error('Error saving location:', error);
|
|
return res.status(500).json({ success: false, error: error.message });
|
|
}
|
|
};
|
|
|
|
module.exports = {
|
|
saveLocation
|
|
};
|