버튼 과정이 조금 복잡하지만 위도경도 연속추적기능도 넣음
This commit is contained in:
@@ -482,3 +482,125 @@ export const updateFieldValue = async (
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 위치 이력 저장 (연속 위치 추적용)
|
||||
* POST /api/dynamic-form/location-history
|
||||
*/
|
||||
export const saveLocationHistory = async (
|
||||
req: AuthenticatedRequest,
|
||||
res: Response
|
||||
): Promise<Response | void> => {
|
||||
try {
|
||||
const { companyCode, userId } = req.user as any;
|
||||
const {
|
||||
latitude,
|
||||
longitude,
|
||||
accuracy,
|
||||
altitude,
|
||||
speed,
|
||||
heading,
|
||||
tripId,
|
||||
tripStatus,
|
||||
departure,
|
||||
arrival,
|
||||
departureName,
|
||||
destinationName,
|
||||
recordedAt,
|
||||
vehicleId,
|
||||
} = req.body;
|
||||
|
||||
console.log("📍 [saveLocationHistory] 요청:", {
|
||||
userId,
|
||||
companyCode,
|
||||
latitude,
|
||||
longitude,
|
||||
tripId,
|
||||
});
|
||||
|
||||
// 필수 필드 검증
|
||||
if (latitude === undefined || longitude === undefined) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "필수 필드가 누락되었습니다. (latitude, longitude)",
|
||||
});
|
||||
}
|
||||
|
||||
const result = await dynamicFormService.saveLocationHistory({
|
||||
userId,
|
||||
companyCode,
|
||||
latitude,
|
||||
longitude,
|
||||
accuracy,
|
||||
altitude,
|
||||
speed,
|
||||
heading,
|
||||
tripId,
|
||||
tripStatus: tripStatus || "active",
|
||||
departure,
|
||||
arrival,
|
||||
departureName,
|
||||
destinationName,
|
||||
recordedAt: recordedAt || new Date().toISOString(),
|
||||
vehicleId,
|
||||
});
|
||||
|
||||
console.log("✅ [saveLocationHistory] 성공:", result);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: result,
|
||||
message: "위치 이력이 저장되었습니다.",
|
||||
});
|
||||
} catch (error: any) {
|
||||
console.error("❌ [saveLocationHistory] 실패:", error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: error.message || "위치 이력 저장에 실패했습니다.",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 위치 이력 조회 (경로 조회용)
|
||||
* GET /api/dynamic-form/location-history/:tripId
|
||||
*/
|
||||
export const getLocationHistory = async (
|
||||
req: AuthenticatedRequest,
|
||||
res: Response
|
||||
): Promise<Response | void> => {
|
||||
try {
|
||||
const { companyCode } = req.user as any;
|
||||
const { tripId } = req.params;
|
||||
const { userId, startDate, endDate, limit } = req.query;
|
||||
|
||||
console.log("📍 [getLocationHistory] 요청:", {
|
||||
tripId,
|
||||
userId,
|
||||
startDate,
|
||||
endDate,
|
||||
limit,
|
||||
});
|
||||
|
||||
const result = await dynamicFormService.getLocationHistory({
|
||||
companyCode,
|
||||
tripId,
|
||||
userId: userId as string,
|
||||
startDate: startDate as string,
|
||||
endDate: endDate as string,
|
||||
limit: limit ? parseInt(limit as string) : 1000,
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: result,
|
||||
count: result.length,
|
||||
});
|
||||
} catch (error: any) {
|
||||
console.error("❌ [getLocationHistory] 실패:", error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: error.message || "위치 이력 조회에 실패했습니다.",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -12,6 +12,8 @@ import {
|
||||
validateFormData,
|
||||
getTableColumns,
|
||||
getTablePrimaryKeys,
|
||||
saveLocationHistory,
|
||||
getLocationHistory,
|
||||
} from "../controllers/dynamicFormController";
|
||||
|
||||
const router = express.Router();
|
||||
@@ -40,4 +42,8 @@ router.get("/table/:tableName/columns", getTableColumns);
|
||||
// 테이블 기본키 조회
|
||||
router.get("/table/:tableName/primary-keys", getTablePrimaryKeys);
|
||||
|
||||
// 위치 이력 (연속 위치 추적)
|
||||
router.post("/location-history", saveLocationHistory);
|
||||
router.get("/location-history/:tripId", getLocationHistory);
|
||||
|
||||
export default router;
|
||||
|
||||
@@ -1731,6 +1731,191 @@ export class DynamicFormService {
|
||||
client.release();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 위치 이력 저장 (연속 위치 추적용)
|
||||
*/
|
||||
async saveLocationHistory(data: {
|
||||
userId: string;
|
||||
companyCode: string;
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
accuracy?: number;
|
||||
altitude?: number;
|
||||
speed?: number;
|
||||
heading?: number;
|
||||
tripId?: string;
|
||||
tripStatus?: string;
|
||||
departure?: string;
|
||||
arrival?: string;
|
||||
departureName?: string;
|
||||
destinationName?: string;
|
||||
recordedAt?: string;
|
||||
vehicleId?: number;
|
||||
}): Promise<{ id: number }> {
|
||||
const pool = getPool();
|
||||
const client = await pool.connect();
|
||||
|
||||
try {
|
||||
console.log("📍 [saveLocationHistory] 저장 시작:", data);
|
||||
|
||||
const sqlQuery = `
|
||||
INSERT INTO vehicle_location_history (
|
||||
user_id,
|
||||
company_code,
|
||||
latitude,
|
||||
longitude,
|
||||
accuracy,
|
||||
altitude,
|
||||
speed,
|
||||
heading,
|
||||
trip_id,
|
||||
trip_status,
|
||||
departure,
|
||||
arrival,
|
||||
departure_name,
|
||||
destination_name,
|
||||
recorded_at,
|
||||
vehicle_id
|
||||
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16)
|
||||
RETURNING id
|
||||
`;
|
||||
|
||||
const params = [
|
||||
data.userId,
|
||||
data.companyCode,
|
||||
data.latitude,
|
||||
data.longitude,
|
||||
data.accuracy || null,
|
||||
data.altitude || null,
|
||||
data.speed || null,
|
||||
data.heading || null,
|
||||
data.tripId || null,
|
||||
data.tripStatus || "active",
|
||||
data.departure || null,
|
||||
data.arrival || null,
|
||||
data.departureName || null,
|
||||
data.destinationName || null,
|
||||
data.recordedAt ? new Date(data.recordedAt) : new Date(),
|
||||
data.vehicleId || null,
|
||||
];
|
||||
|
||||
const result = await client.query(sqlQuery, params);
|
||||
|
||||
console.log("✅ [saveLocationHistory] 저장 완료:", {
|
||||
id: result.rows[0]?.id,
|
||||
});
|
||||
|
||||
return { id: result.rows[0]?.id };
|
||||
} catch (error) {
|
||||
console.error("❌ [saveLocationHistory] 오류:", error);
|
||||
throw error;
|
||||
} finally {
|
||||
client.release();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 위치 이력 조회 (경로 조회용)
|
||||
*/
|
||||
async getLocationHistory(params: {
|
||||
companyCode: string;
|
||||
tripId?: string;
|
||||
userId?: string;
|
||||
startDate?: string;
|
||||
endDate?: string;
|
||||
limit?: number;
|
||||
}): Promise<any[]> {
|
||||
const pool = getPool();
|
||||
const client = await pool.connect();
|
||||
|
||||
try {
|
||||
console.log("📍 [getLocationHistory] 조회 시작:", params);
|
||||
|
||||
const conditions: string[] = [];
|
||||
const queryParams: any[] = [];
|
||||
let paramIndex = 1;
|
||||
|
||||
// 멀티테넌시: company_code 필터
|
||||
if (params.companyCode && params.companyCode !== "*") {
|
||||
conditions.push(`company_code = $${paramIndex}`);
|
||||
queryParams.push(params.companyCode);
|
||||
paramIndex++;
|
||||
}
|
||||
|
||||
// trip_id 필터
|
||||
if (params.tripId) {
|
||||
conditions.push(`trip_id = $${paramIndex}`);
|
||||
queryParams.push(params.tripId);
|
||||
paramIndex++;
|
||||
}
|
||||
|
||||
// user_id 필터
|
||||
if (params.userId) {
|
||||
conditions.push(`user_id = $${paramIndex}`);
|
||||
queryParams.push(params.userId);
|
||||
paramIndex++;
|
||||
}
|
||||
|
||||
// 날짜 범위 필터
|
||||
if (params.startDate) {
|
||||
conditions.push(`recorded_at >= $${paramIndex}`);
|
||||
queryParams.push(new Date(params.startDate));
|
||||
paramIndex++;
|
||||
}
|
||||
|
||||
if (params.endDate) {
|
||||
conditions.push(`recorded_at <= $${paramIndex}`);
|
||||
queryParams.push(new Date(params.endDate));
|
||||
paramIndex++;
|
||||
}
|
||||
|
||||
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
||||
const limitClause = params.limit ? `LIMIT ${params.limit}` : "LIMIT 1000";
|
||||
|
||||
const sqlQuery = `
|
||||
SELECT
|
||||
id,
|
||||
user_id,
|
||||
vehicle_id,
|
||||
latitude,
|
||||
longitude,
|
||||
accuracy,
|
||||
altitude,
|
||||
speed,
|
||||
heading,
|
||||
trip_id,
|
||||
trip_status,
|
||||
departure,
|
||||
arrival,
|
||||
departure_name,
|
||||
destination_name,
|
||||
recorded_at,
|
||||
created_at,
|
||||
company_code
|
||||
FROM vehicle_location_history
|
||||
${whereClause}
|
||||
ORDER BY recorded_at ASC
|
||||
${limitClause}
|
||||
`;
|
||||
|
||||
console.log("🔍 [getLocationHistory] 쿼리:", sqlQuery);
|
||||
console.log("🔍 [getLocationHistory] 파라미터:", queryParams);
|
||||
|
||||
const result = await client.query(sqlQuery, queryParams);
|
||||
|
||||
console.log("✅ [getLocationHistory] 조회 완료:", {
|
||||
count: result.rowCount,
|
||||
});
|
||||
|
||||
return result.rows;
|
||||
} catch (error) {
|
||||
console.error("❌ [getLocationHistory] 오류:", error);
|
||||
throw error;
|
||||
} finally {
|
||||
client.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 싱글톤 인스턴스 생성 및 export
|
||||
|
||||
Reference in New Issue
Block a user