| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317 |
- // utils/parseFullAddress.js
- // 完整的独立地址解析函数(支持返回完整格式:含代码、经纬度、POI等)
-
- // ===================== 基础配置 - 可根据实际需求扩展 =====================
- // 1. 省市区名称+代码映射表(简化版,如需完整数据可从areaData导入)
- const AREA_CODE_MAP = {
- // 省份映射(名称 -> { code, 下属城市 })
- province: {
- "北京市": { code: "110000", cities: ["北京市"] },
- "天津市": { code: "120000", cities: ["天津市"] },
- "河北省": { code: "130000", cities: ["石家庄市", "唐山市", "秦皇岛市"] },
- "山西省": { code: "140000", cities: ["太原市", "大同市"] },
- "内蒙古自治区": { code: "150000", cities: ["呼和浩特市", "包头市"] },
- "辽宁省": { code: "210000", cities: ["沈阳市", "大连市"] },
- "吉林省": { code: "220000", cities: ["长春市", "吉林市"] },
- "黑龙江省": { code: "230000", cities: ["哈尔滨市", "齐齐哈尔市"] },
- "上海市": { code: "310000", cities: ["上海市"] },
- "江苏省": { code: "320000", cities: ["南京市", "无锡市", "苏州市"] },
- "浙江省": { code: "330000", cities: ["杭州市", "宁波市", "温州市"] },
- "安徽省": { code: "340000", cities: ["合肥市", "芜湖市"] },
- "福建省": { code: "350000", cities: ["福州市", "厦门市"] },
- "江西省": { code: "360000", cities: ["南昌市", "九江市"] },
- "山东省": { code: "370000", cities: ["济南市", "青岛市"] },
- "河南省": { code: "410000", cities: ["郑州市", "洛阳市"] },
- "湖北省": { code: "420000", cities: ["武汉市", "宜昌市"] },
- "湖南省": { code: "430000", cities: ["长沙市", "株洲市"] },
- "广东省": { code: "440000", cities: ["广州市", "深圳市", "佛山市"] },
- "广西壮族自治区": { code: "450000", cities: ["南宁市", "柳州市"] },
- "海南省": { code: "460000", cities: ["海口市", "三亚市"] },
- "重庆市": { code: "500000", cities: ["重庆市"] },
- "四川省": { code: "510000", cities: ["成都市", "绵阳市"] },
- "贵州省": { code: "520000", cities: ["贵阳市", "遵义市"] },
- "云南省": { code: "530000", cities: ["昆明市", "大理市"] },
- "西藏自治区": { code: "540000", cities: ["拉萨市"] },
- "陕西省": { code: "610000", cities: ["西安市", "宝鸡市"] },
- "甘肃省": { code: "620000", cities: ["兰州市", "天水市"] },
- "青海省": { code: "630000", cities: ["西宁市"] },
- "宁夏回族自治区": { code: "640000", cities: ["银川市"] },
- "新疆维吾尔自治区": { code: "650000", cities: ["乌鲁木齐市"] },
- "台湾省": { code: "710000", cities: ["台北市", "高雄市"] },
- "香港特别行政区": { code: "810000", cities: ["香港特别行政区"] },
- "澳门特别行政区": { code: "820000", cities: ["澳门特别行政区"] }
- },
- // 城市映射(名称 -> { code, 下属区县 })
- city: {
- "北京市": { code: "110100", districts: ["东城区", "西城区", "朝阳区", "海淀区", "顺义区"] },
- "天津市": { code: "120100", districts: ["和平区", "河东区", "西青区", "北辰区"] },
- "深圳市": { code: "440300", districts: ["罗湖区", "福田区", "南山区", "宝安区", "龙岗区"] },
- "广州市": { code: "440100", districts: ["越秀区", "海珠区", "天河区"] },
- "上海市": { code: "310100", districts: ["黄浦区", "徐汇区", "长宁区"] },
- "杭州市": { code: "330100", districts: ["上城区", "拱墅区", "西湖区"] }
- },
- // 区县映射(名称 -> { code, 经纬度 })
- district: {
- "东城区": { code: "110101", lat: 39.938874, lng: 116.407413 },
- "西城区": { code: "110102", lat: 39.914745, lng: 116.366761 },
- "朝阳区": { code: "110105", lat: 39.948859, lng: 116.487501 },
- "海淀区": { code: "110108", lat: 39.992947, lng: 116.316486 },
- "顺义区": { code: "110113", lat: 40.111562, lng: 116.658249 }, // 修正你示例中的错误代码(120101是天津和平区)
- "南山区": { code: "440305", lat: 22.542895, lng: 113.941649 },
- "福田区": { code: "440304", lat: 22.543096, lng: 114.057865 },
- "罗湖区": { code: "440303", lat: 22.552726, lng: 114.104663 }
- }
- };
-
- // 2. 标准省市区名称集合(从映射表中提取,用于匹配)
- const PROVINCES = Object.keys(AREA_CODE_MAP.province);
- const CITIES = Object.keys(AREA_CODE_MAP.city);
- const DISTRICTS = Object.keys(AREA_CODE_MAP.district);
-
- // 3. POI关键词(用于提取建筑物名称)
- const POI_KEYWORDS = ["大厦", "小区", "花园", "公寓", "写字楼", "酒店", "广场", "中心", "别墅", "商铺"];
-
- // ===================== 工具函数 =====================
- /**
- * 提取手机号(纯函数)
- * @param {string} text - 输入文本
- * @returns {string} 提取的手机号
- */
- function extractPhone(text) {
- if (!text) return '';
- const phoneReg = /1[3-9]\d{9}|1[3-9]\d{2}[- ]?\d{4}[- ]?\d{4}/g;
- const match = text.match(phoneReg);
- return match ? match[0].replace(/\D/g, '') : '';
- }
-
- /**
- * 提取姓名(纯函数)
- * @param {string} text - 输入文本
- * @returns {string} 提取的姓名
- */
- function extractName(text) {
- if (!text) return '';
- const nameReg = /[\u4e00-\u9fa5]{2,4}/;
- const match = text.match(nameReg);
- return match ? match[0] : '';
- }
-
- /**
- * 提取POI名称(从详细地址中提取建筑物名称)
- * @param {string} detail - 详细地址
- * @returns {string} POI名称
- */
- function extractPOIName(detail) {
- if (!detail) return '';
- // 匹配包含POI关键词的连续字符(2-10字)
- const poiReg = new RegExp(`[\\u4e00-\\u9fa5a-zA-Z0-9]{2,10}(${POI_KEYWORDS.join('|')})`, 'g');
- const matches = detail.match(poiReg);
- return matches && matches.length > 0 ? matches[0] : '';
- }
-
- /**
- * 获取行政区划代码
- * @param {string} province - 省份名称
- * @param {string} city - 城市名称
- * @param {string} district - 区县名称
- * @returns {object} { provinceCode, cityCode, districtCode }
- */
- function getAreaCodes(province, city, district) {
- return {
- provinceCode: AREA_CODE_MAP.province[province]?.code || '',
- cityCode: AREA_CODE_MAP.city[city]?.code || '',
- districtCode: AREA_CODE_MAP.district[district]?.code || ''
- };
- }
-
- /**
- * 获取经纬度(优先区县,无则城市,无则省份)
- * @param {string} province - 省份名称
- * @param {string} city - 城市名称
- * @param {string} district - 区县名称
- * @returns {object} { latitude, longitude }
- */
- function getLatLng(province, city, district) {
- // 区县经纬度
- if (district && AREA_CODE_MAP.district[district]) {
- return {
- latitude: AREA_CODE_MAP.district[district].lat,
- longitude: AREA_CODE_MAP.district[district].lng
- };
- }
- // 城市默认经纬度(可扩展城市经纬度映射)
- const cityLatLngMap = {
- "北京市": { lat: 39.9042, lng: 116.4074 },
- "上海市": { lat: 31.2304, lng: 121.4737 },
- "深圳市": { lat: 22.5431, lng: 114.0579 },
- "广州市": { lat: 23.1289, lng: 113.2655 }
- };
- if (city && cityLatLngMap[city]) {
- return {
- latitude: cityLatLngMap[city].lat,
- longitude: cityLatLngMap[city].lng
- };
- }
- // 省份默认经纬度
- const provinceLatLngMap = {
- "广东省": { lat: 23.1289, lng: 113.2655 },
- "江苏省": { lat: 32.0473, lng: 118.7624 },
- "浙江省": { lat: 30.2795, lng: 120.1576 }
- };
- if (province && provinceLatLngMap[province]) {
- return {
- latitude: provinceLatLngMap[province].lat,
- longitude: provinceLatLngMap[province].lng
- };
- }
- // 默认经纬度(北京)
- return { latitude: 39.9042, longitude: 116.4074 };
- }
-
- /**
- * 识别省市区(纯函数,基于映射表优化匹配准确性)
- * @param {string} text - 输入文本
- * @returns {object} { province, city, district, remainingText }
- */
- function recognizeArea(text) {
- if (!text) return { province: '', city: '', district: '', remainingText: '' };
-
- let province = '';
- let city = '';
- let district = '';
- let remainingText = text.trim();
-
- // 1. 匹配省份(优先长名称,避免短名称误匹配)
- const sortedProvinces = [...PROVINCES].sort((a, b) => b.length - a.length);
- for (const p of sortedProvinces) {
- if (remainingText.includes(p)) {
- province = p;
- remainingText = remainingText.replace(p, '').trim();
- break;
- }
- }
-
- // 2. 匹配城市(基于已选省份过滤,提升准确性)
- let candidateCities = [...CITIES].sort((a, b) => b.length - a.length);
- if (province && AREA_CODE_MAP.province[province].cities) {
- candidateCities = candidateCities.filter(c => AREA_CODE_MAP.province[province].cities.includes(c));
- }
- for (const c of candidateCities) {
- if (remainingText.includes(c)) {
- city = c;
- remainingText = remainingText.replace(c, '').trim();
- break;
- }
- }
-
- // 3. 匹配区县(基于已选城市过滤)
- let candidateDistricts = [...DISTRICTS].sort((a, b) => b.length - a.length);
- if (city && AREA_CODE_MAP.city[city].districts) {
- candidateDistricts = candidateDistricts.filter(d => AREA_CODE_MAP.city[city].districts.includes(d));
- }
- for (const d of candidateDistricts) {
- if (remainingText.includes(d)) {
- district = d;
- remainingText = remainingText.replace(d, '').trim();
- break;
- }
- }
-
- return { province, city, district, remainingText };
- }
-
- /**
- * 完整地址解析(返回完整格式:含代码、经纬度、POI等)
- * @param {string} text - 输入文本
- * @param {object} [options] - 可选配置
- * @param {boolean} [options.isDefault=false] - 是否默认地址
- * @returns {object} 完整解析结果
- */
- const parseFullAddress = (text, options = {}) => {
- const { isDefault = false } = options;
- if (!text) return {
- receiver: '',
- phone: '',
- province: '',
- provinceCode: '',
- city: '',
- cityCode: '',
- district: '',
- districtCode: '',
- detail: '',
- poiName: '',
- latitude: 0,
- longitude: 0,
- isDefault
- };
-
- let remainingText = text.trim();
-
- // 1. 提取手机号
- const phone = extractPhone(remainingText);
- if (phone) {
- const phoneReg = new RegExp(
- phone.replace(/(\d)/g, '\\d') + '|' +
- phone.replace(/(\d{3})(\d{4})(\d{4})/, '$1[- ]?$2[- ]?$3'),
- 'g'
- );
- remainingText = remainingText.replace(phoneReg, '').replace(/\s+/g, ' ').trim();
- }
-
- // 2. 识别省市区
- const { province, city, district, remainingText: textAfterArea } = recognizeArea(remainingText);
- remainingText = textAfterArea;
-
- // 3. 提取姓名
- let receiver = extractName(remainingText);
- if (receiver) {
- remainingText = remainingText.replace(receiver, '').trim();
- } else {
- const reverseText = remainingText.split('').reverse().join('');
- const reverseName = extractName(reverseText);
- if (reverseName) {
- receiver = reverseName.split('').reverse().join('');
- remainingText = remainingText.replace(new RegExp(receiver + '$'), '').trim();
- }
- }
-
- // 4. 处理详细地址(过滤重复省市区)
- let detail = remainingText;
- const areaPrefix = [
- `${province}${city}${district}`,
- `${province}${city}`,
- `${city}${district}`
- ];
- for (const prefix of areaPrefix) {
- if (detail.includes(prefix)) {
- detail = detail.replace(prefix, '').trim();
- break;
- }
- }
-
- // 5. 补充扩展字段
- const { provinceCode, cityCode, districtCode } = getAreaCodes(province, city, district);
- const { latitude, longitude } = getLatLng(province, city, district);
- const poiName = extractPOIName(detail);
-
- return {
- receiver, // 收件人姓名
- phone, // 手机号
- province, // 省份名称
- provinceCode, // 省份代码(如:440000)
- city, // 城市名称
- cityCode, // 城市代码(如:440300)
- district, // 区县名称
- districtCode, // 区县代码(如:440305)
- detail, // 详细地址
- poiName, // POI名称(建筑物/小区名称)
- latitude, // 纬度
- longitude, // 经度
- isDefault // 是否默认地址(默认false)
- };
- };
-
- // 导出函数
- module.exports = { parseFullAddress };
-
|