// 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 };