#!/usr/bin/env node /** * 区域数据CSV转SQL导入脚本 * 将 docs/省市区.csv 转换为 area_data 表的 INSERT 语句 * 使用方法: node docs/generate-area-sql.mjs */ import fs from 'fs'; import path from 'path'; import { fileURLToPath } from 'url'; // 获取当前目录 const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); // 文件路径 const csvFilePath = path.join(__dirname, '省市区.csv'); const outputSqlPath = path.join(__dirname, 'area_data_init.sql'); // 读取CSV文件 function readCSV(filePath) { try { const content = fs.readFileSync(filePath, 'utf-8'); return content.trim().split('\n'); } catch (error) { console.error('读取CSV文件失败:', error.message); process.exit(1); } } // 解析CSV行 function parseCSVLine(line) { // 处理包含逗号的字段 const parts = line.split(','); if (parts.length !== 3) { console.warn('跳过格式不正确的行:', line); return null; } const [id, parentId, name] = parts; const parsedId = parseInt(id.trim()); const parsedParentId = parseInt(parentId.trim()) || null; const parsedName = name.trim(); // 推断层级和行政区划代码 let level, code; if (parsedParentId === 0 || parsedParentId === null) { level = 1; // 省/直辖市 code = parsedId.toString().padStart(6, '0'); // 省级代码 } else { // 根据parentId推断层级 // 如果parentId是省级(1-33),则当前是市级 // 如果parentId是市级(34-...),则当前是区县级 if (parsedParentId >= 1 && parsedParentId <= 33) { level = 2; // 市 } else { level = 3; // 区/县 } code = parsedId.toString().padStart(6, '0'); } return { id: parsedId, parentId: parsedParentId, name: parsedName, level, code }; } // 生成SQL语句 function generateSQLStatements(data) { const statements = [ '-- 区域数据初始化脚本', '-- 来源: scripts/省市区.csv', '-- 生成时间: ' + new Date().toISOString(), '-- 数据库: PostgreSQL', '', '-- 清空现有数据(可选)', '-- TRUNCATE TABLE areas;', '', '-- 插入区域数据', 'INSERT INTO areas (id, parent_id, name, level, code, is_disabled, is_deleted, created_at, updated_at) VALUES' ]; const values = data.map(item => ` (${item.id}, ${item.parentId === null ? 'NULL' : item.parentId}, '${escapeString(item.name)}', '${item.level}', '${item.code}', 0, 0, NOW(), NOW())` ); statements.push(values.join(',\n')); statements.push('ON CONFLICT (id) DO UPDATE SET'); statements.push(' parent_id = EXCLUDED.parent_id,'); statements.push(' name = EXCLUDED.name,'); statements.push(' level = EXCLUDED.level,'); statements.push(' code = EXCLUDED.code,'); statements.push(' updated_at = NOW();'); return statements.join('\n'); } // 转义SQL字符串 function escapeString(str) { return str.replace(/'/g, "''"); } // 验证数据完整性 function validateData(data) { const ids = new Set(); const parentIds = new Set(); const levels = { 1: 0, 2: 0, 3: 0 }; for (const item of data) { if (ids.has(item.id)) { console.warn(`重复的ID: ${item.id}`); } ids.add(item.id); if (item.parentId !== null && item.parentId !== 0) { parentIds.add(item.parentId); } // 统计各层级数量 if (item.level >= 1 && item.level <= 3) { levels[item.level]++; } } // 检查所有parentId是否都存在对应的记录 for (const parentId of parentIds) { if (!ids.has(parentId)) { console.warn(`父ID不存在: ${parentId}`); } } console.log(`总计 ${data.length} 条区域数据`); console.log(`顶级区域数量: ${data.filter(item => item.parentId === 0 || item.parentId === null).length}`); console.log(`省级区域数量: ${levels[1]}`); console.log(`市级区域数量: ${levels[2]}`); console.log(`区县级区域数量: ${levels[3]}`); } // 主函数 function main() { console.log('开始生成区域数据SQL文件...'); // 检查CSV文件是否存在 if (!fs.existsSync(csvFilePath)) { console.error('CSV文件不存在:', csvFilePath); process.exit(1); } // 读取并解析CSV const lines = readCSV(csvFilePath); // 跳过表头 const header = lines[0]; if (header !== 'aId,aFId,aName') { console.warn('CSV文件格式可能不正确,表头不匹配'); } const data = []; for (let i = 1; i < lines.length; i++) { const line = lines[i].trim(); if (line) { const item = parseCSVLine(line); if (item) { data.push(item); } } } // 验证数据 validateData(data); // 生成SQL const sql = generateSQLStatements(data); // 写入SQL文件 try { fs.writeFileSync(outputSqlPath, sql, 'utf-8'); console.log('SQL文件已生成:', outputSqlPath); console.log('文件大小:', (fs.statSync(outputSqlPath).size / 1024).toFixed(2), 'KB'); } catch (error) { console.error('写入SQL文件失败:', error.message); process.exit(1); } // 生成使用说明 console.log('\n使用说明:'); console.log('1. 运行: node scripts/generate-area-sql.mjs'); console.log('2. 执行生成的SQL文件: psql -h 127.0.0.1 -U postgres -d postgres -f scripts/area_data_init.sql'); console.log('3. 或者直接在数据库管理工具中执行SQL内容'); } // 执行主函数 if (import.meta.url === `file://${process.argv[1]}`) { main(); } export { readCSV, parseCSVLine, generateSQLStatements, validateData };