Files
fanhongcai c377421a3e 新增农户功能升级:身份证OCR/OSS上传、头像采集、同名村检测、Region热度排序及表单体验优化
- Region 表新增热度列,热门省份优先显示
- 区县下拉按地级市分组、已录区县优先、支持全省搜索
- 组选择内置一组至二十组
- 银行卡号独立行+粗体预览,开户行独立行
- 新增农户类型:农户/个体户/合作社/经营集体/公司
- 身份证正反面支持高拍仪/摄像头OCR与手机扫码上传,附件存本地/OSS
- 新增农户头像采集组件(摄像头/本地上传),预留人脸识别收购扩展
- 同名村弹窗提醒(防选错乡镇)
- 编辑窗体禁止遮罩/Esc误关,整体加宽适配高频操作
- 修复二维码局域网手机无法访问(监听0.0.0.0、局域网IP识别、Vite代理兜底)
2026-08-13 16:07:35 +08:00

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()