diff --git a/app/api/activities/route.ts b/app/api/activities/route.ts index 69c8dfc..fa1c06d 100644 --- a/app/api/activities/route.ts +++ b/app/api/activities/route.ts @@ -139,8 +139,8 @@ export async function GET(request: NextRequest) { // Client ID (possibly part of visitor_id) const clientId = eventWithFullPath.visitor_id?.split('-')[0] || 'undefined'; - // Original path (use full URL field) - const originPath = fullUrl || 'undefined'; + // Original path - 修正:使用link_original_url作为原始URL来源 + const originPath = eventWithFullPath.link_original_url || 'undefined'; // Add to CSV content csvContent += `${time},${activity},${campaign},${clientId},${originPath}\n`; diff --git a/windmill/sync_mongo_to_events.ts b/windmill/sync_mongo_to_events.ts index 72542bc..489c509 100644 --- a/windmill/sync_mongo_to_events.ts +++ b/windmill/sync_mongo_to_events.ts @@ -404,7 +404,7 @@ export async function main( // 构建查询条件,根据上次同步状态获取新记录 const query: Record = { - type: 1 // 只同步type为1的记录 + // 删除了 type: 1 的条件,将同步所有数据 }; // 根据时间范围参数构建查询条件 @@ -548,7 +548,7 @@ export async function main( return { // UUID将由ClickHouse自动生成 (event_id) event_time: eventTime.toISOString().replace('T', ' ').replace('Z', ''), - event_type: record.type === 1 ? "visit" : "custom", + event_type: "click", // 将所有event_type都设置为click event_attributes: JSON.stringify(eventAttributes), link_id: record.slugId.toString(), link_slug: shortLink?.slug || "unknown_slug", // 使用占位符 @@ -563,15 +563,15 @@ export async function main( ? new Date(shortLink.expiresAt).toISOString().replace('T', ' ').replace('Z', '') : null, link_tags: shortLink?.tags ? JSON.stringify(shortLink.tags) : "[]", - user_id: shortLink?.user || "unknown_user", // 使用占位符 + user_id: "3680f452-e404-4339-a3d2-2a8e1ff92102", // 使用占位符 user_name: "unknown_user", // 使用占位符 user_email: "", user_attributes: "{}", - team_id: shortLink?.teamId || "unknown_team", // 使用占位符 - team_name: "unknown_team", // 使用占位符 + team_id: "e02251eb-eb98-47c8-b5dd-4f6e4fdb1f49", // 使用占位符 + team_name: "", // 使用占位符 team_attributes: "{}", - project_id: shortLink?.projectId || "unknown_project", // 使用占位符 - project_name: "unknown_project", // 使用占位符 + project_id: "34cdb8b9-8b8e-4033-876a-0632002ef1f9", // 使用占位符 + project_name: "", // 使用占位符 project_attributes: "{}", qr_code_id: "", qr_code_name: "", @@ -611,12 +611,23 @@ export async function main( referrer, utm_source, utm_medium, utm_campaign, utm_term, utm_content, time_spent_sec, is_bounce, is_qr_scan, conversion_type, conversion_value, req_full_path) VALUES ${clickhouseData.map(record => { - // 确保所有字符串值都是字符串类型,并安全处理替换 + // 增强版安全替换函数,处理所有特殊字符 const safeReplace = (val: unknown): string => { // 确保值是字符串,如果是null或undefined则使用空字符串 const str = val === null || val === undefined ? "" : String(val); - // 安全替换单引号 - return str.replace(/'/g, "''"); + + // 转义所有可能导致SQL注入或格式错误的字符 + // 1. 先替换所有反斜杠 + // 2. 再替换单引号 + // 3. 替换所有控制字符和特殊字符 + return str + .replace(/\\/g, "\\\\") // 转义反斜杠 + .replace(/'/g, "\\'") // 转义单引号 + .replace(/\r/g, "\\r") // 转义回车 + .replace(/\n/g, "\\n") // 转义换行 + .replace(/\t/g, "\\t") // 转义制表符 + .replace(/\0/g, "") // 移除空字符 + .replace(/[\x00-\x1F\x7F-\x9F]/g, ""); // 移除所有控制字符 }; return `('${record.event_time}', '${safeReplace(record.event_type)}', '${safeReplace(record.event_attributes)}',