- Region 表新增热度列,热门省份优先显示 - 区县下拉按地级市分组、已录区县优先、支持全省搜索 - 组选择内置一组至二十组 - 银行卡号独立行+粗体预览,开户行独立行 - 新增农户类型:农户/个体户/合作社/经营集体/公司 - 身份证正反面支持高拍仪/摄像头OCR与手机扫码上传,附件存本地/OSS - 新增农户头像采集组件(摄像头/本地上传),预留人脸识别收购扩展 - 同名村弹窗提醒(防选错乡镇) - 编辑窗体禁止遮罩/Esc误关,整体加宽适配高频操作 - 修复二维码局域网手机无法访问(监听0.0.0.0、局域网IP识别、Vite代理兜底)
115 lines
4.2 KiB
JavaScript
115 lines
4.2 KiB
JavaScript
/**
|
|
* AreaCity 全国行政区划数据导入脚本
|
|
* ==================================
|
|
* 数据源: https://github.com/xiangyuecn/AreaCity-JsSpider-StatsGov
|
|
* 下载: https://github.com/xiangyuecn/AreaCity-JsSpider-StatsGov/releases/download/2025.251231.260403/ok_data_level3-4.csv.7z
|
|
* (Gitee 备用: https://gitee.com/xiangyuecn/AreaCity-JsSpider-StatsGov/releases)
|
|
* 说明: 解压后得到 ok_data_level4.csv(省市区镇四级,UTF-8 带 BOM)
|
|
* 列: id, pid, deep, name, pinyin_prefix, pinyin, ext_id, ext_name
|
|
* deep: 0=省 1=市(地级/直辖市虚拟) 2=县(区/县级市) 3=乡镇(镇/乡/街道)
|
|
*
|
|
* 用法:
|
|
* 1) 下载并解压 ok_data_level4.csv 到本目录 tmp/ok_data_level4.csv
|
|
* 2) npm i mysql2
|
|
* 3) node import-regions.mjs
|
|
* 脚本会 DROP 并重建 Regions 表后导入(幂等,可重复执行)。
|
|
*/
|
|
import fs from 'fs'
|
|
import path from 'path'
|
|
import mysql from 'mysql2/promise'
|
|
import { fileURLToPath } from 'url'
|
|
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
const root = path.resolve(__dirname, '../..')
|
|
const csvPath = path.join(__dirname, 'tmp/ok_data_level4.csv')
|
|
|
|
if (!fs.existsSync(csvPath)) {
|
|
console.error('未找到 ' + csvPath + ',请先下载并解压数据文件')
|
|
process.exit(1)
|
|
}
|
|
|
|
// ---------- 读取数据库连接串 ----------
|
|
const appCfg = JSON.parse(fs.readFileSync(path.join(root, 'backend/AgriculturalPlatform.Api/appsettings.json'), 'utf8'))
|
|
const parseCs = (s) => Object.fromEntries(
|
|
s.split(';').filter(Boolean).map(kv => {
|
|
const i = kv.indexOf('=')
|
|
return [kv.slice(0, i).trim().toLowerCase(), kv.slice(i + 1).trim()]
|
|
})
|
|
)
|
|
const dbc = parseCs(appCfg.ConnectionStrings.Default)
|
|
|
|
// ---------- 解析 CSV ----------
|
|
const raw = fs.readFileSync(csvPath, 'utf8').replace(/^\uFEFF/, '')
|
|
const lines = raw.split(/\r?\n/).filter(l => l.trim().length > 0)
|
|
lines.shift() // 去掉表头
|
|
|
|
const parseRow = (line) => {
|
|
const fields = []
|
|
let cur = '', inQ = false
|
|
for (const ch of line) {
|
|
if (ch === '"') inQ = !inQ
|
|
else if (ch === ',' && !inQ) { fields.push(cur); cur = '' }
|
|
else cur += ch
|
|
}
|
|
fields.push(cur)
|
|
return fields
|
|
}
|
|
|
|
// 常用省份(热度高的排前面,值越大越靠前,供前端优先展示)
|
|
const HOT_PROVINCES = [
|
|
'四川省', '广西壮族自治区', '陕西省', '重庆市', '安徽省',
|
|
'云南省', '贵州省', '河南省', '山东省', '湖北省', '湖南省',
|
|
'甘肃省', '新疆维吾尔自治区', '江西省', '福建省', '广东省',
|
|
'河北省', '山西省', '江苏省', '浙江省'
|
|
]
|
|
const hotOf = (deep, extName) => {
|
|
if (deep !== 0) return 0
|
|
const i = HOT_PROVINCES.indexOf(extName)
|
|
return i < 0 ? 0 : HOT_PROVINCES.length - i
|
|
}
|
|
|
|
const rows = []
|
|
for (const line of lines) {
|
|
const [id, pid, deep, name, pfx, pinyin, , extName] = parseRow(line)
|
|
if (!id) continue
|
|
rows.push([Number(id), Number(pid), Number(deep), name, extName, pinyin, pfx, hotOf(Number(deep), extName)])
|
|
}
|
|
console.log('解析完成,共 ' + rows.length + ' 条')
|
|
const stat = {}
|
|
for (const r of rows) stat[r[2]] = (stat[r[2]] || 0) + 1
|
|
console.log('层级分布(deep):', JSON.stringify(stat))
|
|
|
|
// ---------- 建表并导入 ----------
|
|
const conn = await mysql.createConnection({
|
|
host: dbc.server,
|
|
port: Number(dbc.port || 3306),
|
|
user: dbc.user,
|
|
password: dbc.password,
|
|
database: dbc.database,
|
|
charset: 'utf8mb4'
|
|
})
|
|
|
|
await conn.query('DROP TABLE IF EXISTS Regions')
|
|
await conn.query(`CREATE TABLE Regions (
|
|
Id INT NOT NULL PRIMARY KEY,
|
|
Pid INT NOT NULL,
|
|
Deep INT NOT NULL,
|
|
Name VARCHAR(100) NOT NULL,
|
|
ExtName VARCHAR(100) NOT NULL,
|
|
Pinyin VARCHAR(200) NOT NULL DEFAULT '',
|
|
PinyinPrefix VARCHAR(10) NOT NULL DEFAULT '',
|
|
Hot INT NOT NULL DEFAULT 0,
|
|
KEY idx_regions_pid (Pid),
|
|
KEY idx_regions_deep (Deep)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci`)
|
|
|
|
const BATCH = 1000
|
|
for (let i = 0; i < rows.length; i += BATCH) {
|
|
const chunk = rows.slice(i, i + BATCH)
|
|
await conn.query('INSERT INTO Regions (Id, Pid, Deep, Name, ExtName, Pinyin, PinyinPrefix, Hot) VALUES ?', [chunk])
|
|
}
|
|
|
|
const [cnt] = await conn.query('SELECT COUNT(*) AS c FROM Regions')
|
|
console.log('导入完成,Regions 表共 ' + cnt[0].c + ' 条记录')
|
|
await conn.end()
|