import pg from "pg"; const { Client } = pg; const TITLE_MATCH_SQL = ` session_type = 'birth_time_rectification' and title ~ '^[0-9]{1,2}月[0-9]{1,2}日[[:space:]]*·[[:space:]]*生时校正([[:space:]]+[0-9]{2}:[0-9]{2})?$' and ( created_at is null or (substring(title from '^([0-9]{1,2})月'))::int is distinct from extract(month from created_at at time zone 'Asia/Shanghai')::int or (substring(title from '月([0-9]{1,2})日'))::int is distinct from extract(day from created_at at time zone 'Asia/Shanghai')::int ) `; const TITLE_COUNT_SQL = `select count(*)::int as n from public.chat_sessions where ${TITLE_MATCH_SQL}`; const TITLE_APPLY_SQL = ` update public.chat_sessions set title = case when created_at is null then '生时校正' else to_char(created_at at time zone 'Asia/Shanghai', 'FMMM') || '月' || to_char(created_at at time zone 'Asia/Shanghai', 'FMDD') || '日 · 生时校正' end where ${TITLE_MATCH_SQL} `; const ACTIVITY_COUNT_SQL = ` select count(*)::int as n from public.chat_sessions as session join public.agentic_rectification_cases as case_row on case_row.session_id = session.id and case_row.user_id = session.user_id left join lateral ( select max(turn.created_at) as last_turn_at from public.agentic_rectification_turns as turn where turn.case_id = case_row.id ) as turns on true where session.session_type = 'birth_time_rectification' and session.updated_at < coalesce(turns.last_turn_at, case_row.last_activity_at) `; const ACTIVITY_APPLY_SQL = ` update public.chat_sessions as session set updated_at = coalesce(turns.last_turn_at, case_row.last_activity_at) from public.agentic_rectification_cases as case_row left join lateral ( select max(turn.created_at) as last_turn_at from public.agentic_rectification_turns as turn where turn.case_id = case_row.id ) as turns on true where case_row.session_id = session.id and case_row.user_id = session.user_id and session.session_type = 'birth_time_rectification' and session.updated_at < coalesce(turns.last_turn_at, case_row.last_activity_at) `; function connectionString() { const value = process.env.SCHEMA_DATABASE_URL?.trim(); if (!value) { throw new Error("SCHEMA_DATABASE_URL is required"); } if (!/^postgres(ql)?:\/\//.test(value)) { throw new Error("SCHEMA_DATABASE_URL must be a PostgreSQL URL"); } return value; } async function main() { const apply = process.argv.includes("--apply"); const client = new Client({ connectionString: connectionString() }); await client.connect(); try { const titlesBefore = await client.query(TITLE_COUNT_SQL); const activityBefore = await client.query(ACTIVITY_COUNT_SQL); console.log(`mismatched_dated_rectification_titles=${titlesBefore.rows[0]?.n ?? 0}`); console.log(`stale_rectification_updated_at=${activityBefore.rows[0]?.n ?? 0}`); if (!apply) return; const titlesUpdated = await client.query(TITLE_APPLY_SQL); const activityUpdated = await client.query(ACTIVITY_APPLY_SQL); const titlesAfter = await client.query(TITLE_COUNT_SQL); const activityAfter = await client.query(ACTIVITY_COUNT_SQL); console.log(`updated_title_rows=${titlesUpdated.rowCount ?? 0}`); console.log(`updated_activity_rows=${activityUpdated.rowCount ?? 0}`); console.log(`mismatched_after=${titlesAfter.rows[0]?.n ?? 0}`); console.log(`stale_updated_at_after=${activityAfter.rows[0]?.n ?? 0}`); } finally { await client.end(); } } main().catch((error) => { console.error(error instanceof Error ? error.message : error); process.exit(1); });