DetailParse.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. <?php
  2. namespace App\Handlers;
  3. use http\Env\Request;
  4. use App\Models\Area;
  5. use Illuminate\Support\Facades\DB;
  6. use Illuminate\Support\Facades\Log;
  7. class DetailParse {
  8. /**
  9. * 地址智能解析 为了提现思路,注释较多,经过测试,识别率在95%左右
  10. * 非自然语言处理,但由于国家的地址省市区街道是有特征和统计规律的
  11. * 所以本程序才能产生识别效果,但还是要考虑特殊情况,如自治州,县级市等
  12. * 可使用本仓库的SQL地区库
  13. * @param string 收货地址 不含姓名手机号
  14. * @return array 省,市,区,街道地址
  15. */
  16. public static function detail_parse($detail)
  17. {
  18. // 测试例子
  19. // $detail = '成都市高新区天府软件园B区科技大楼';
  20. // $detail = '双流县郑通路社保局区52050号';
  21. // $detail = '岳市岳阳楼区南湖求索路碧灏花园A座1101';
  22. // $detail = '四川省南充市阆中市公园路25号';
  23. // $detail = '四川省阆中市公园路25号';
  24. // $detail = '四川省 凉山州美姑县xxx小区18号院';
  25. // $detail = '重庆攀枝花市东区机场路3中学校';
  26. // $detail = '渝北区渝北中学51200街道地址';
  27. // $detail = '天津天津市红桥区水木天成1区临湾路9-3-1101';
  28. $detail_origin = $detail; //保存原始参数
  29. $detail = str_replace(' ', '', $detail);
  30. $detail = str_replace("自治区", "省", $detail); //避免自治区被错误识别
  31. $detail = str_replace("自治州", "州", $detail); //避免自治区被错误识别
  32. //返回结果
  33. $result = [];
  34. /**
  35. * 1. 三级地址识别 共有2992个三级地址 高频词为【县,区,旗,市】是整个识别系统的关键
  36. * 返回 [%第3级% 模糊地址] [街道地址]
  37. * 三级地址 前面一般2或3个字符就够用了【3个字符,比如高新区,仁和区,武侯区,占比96%】【2个字符的县和区有140个左右,占比4%,比如理县】
  38. */
  39. if (mb_strstr($detail, '县') || mb_strstr($detail, '区') || mb_strstr($detail, '旗')) {
  40. // 如果同时出现 县和区 我们可以确定的是县一定在区前面,所以下面三个if顺序是有要求的,不能随便调整
  41. if (mb_strstr($detail, '旗')) {
  42. $deep3_keyword_pos = mb_strpos($detail, '旗');
  43. $deep3_area_name = mb_substr($detail, $deep3_keyword_pos - 1, 2);
  44. }
  45. if (mb_strstr($detail, '区')) {
  46. $deep3_keyword_pos = mb_strpos($detail, '区');
  47. // 判断区、市是同时存在 同时存在 可以简单 比如【攀枝花市东区攀枝花三中高三班2010级】
  48. if (mb_strstr($detail, '市')) {
  49. $city_pos = mb_strpos($detail, '市');
  50. $zone_pos = mb_strpos($detail, '区');
  51. $deep3_area_name = mb_substr($detail, $city_pos + 1, $zone_pos - $city_pos);
  52. } else {
  53. $deep3_area_name = mb_substr($detail, $deep3_keyword_pos - 2, 3);
  54. //县名称最大的概率为3个字符 美姑县 阆中市 高新区
  55. }
  56. }
  57. if (mb_strstr($detail, '县')) {
  58. $deep3_keyword_pos = mb_strpos($detail, '县');
  59. // 判断县市是同时存在 同时存在 可以简单 比如【湖南省常德市澧县】
  60. if (mb_strstr($detail, '市')) {
  61. $city_pos = mb_strpos($detail, '市');
  62. $zone_pos = mb_strpos($detail, '县');
  63. $deep3_area_name = mb_substr($detail, $city_pos + 1, $zone_pos - $city_pos);
  64. } else {
  65. //考虑形如【甘肃省东乡族自治县布楞沟村1号】的情况
  66. if (mb_strstr($detail, '自治县')){
  67. $deep3_area_name = mb_substr($detail, $deep3_keyword_pos - 6, 7);
  68. if(in_array(mb_substr($deep3_area_name, 0, 1) , ['省', '市', '州'] )){
  69. $deep3_area_name = mb_substr($deep3_area_name, 1);
  70. }
  71. }else{
  72. $deep3_area_name = mb_substr($detail, $deep3_keyword_pos - 2, 3);
  73. }
  74. //县名称最大的概率为3个字符 美姑县 阆中市 高新区
  75. }
  76. }
  77. $street = mb_substr($detail, $deep3_keyword_pos + 1);
  78. } else {
  79. if (mb_strripos($detail, '市')) {
  80. //最大的可能性为县级市 可能的情况有【四川省南充市阆中市公园路25号,四川省南充市阆中市公园路25号】市要找【最后一次】出现的位置
  81. $deep3_keyword_pos = mb_strripos($detail, '市');
  82. $deep3_area_name = mb_substr($detail, $deep3_keyword_pos - 2, 3);
  83. $street = mb_substr($detail, $deep3_keyword_pos + 1);
  84. } else {
  85. //不能识别的解析
  86. $deep3_area_name = '';
  87. $street = $detail;
  88. }
  89. }
  90. /**
  91. * 2. 二级地址的识别 共有410个二级地址 高频词为【市,盟,州】 高频长度为3,4个字符 因为有用户可能会填写 '四川省阆中市',所以二级地址的识别可靠性并不高 需要与三级地址 综合使用
  92. * 返回 [%第2级% 模糊地址]
  93. */
  94. if (mb_strrpos($detail, '市') || mb_strstr($detail, '盟') || mb_strstr($detail, '州')) {
  95. if ($tmp_pos = mb_strrpos($detail, '市')) {
  96. $deep2_area_name = mb_substr($detail, $tmp_pos - 2, 3);
  97. }
  98. if ($tmp_pos = mb_strrpos($detail, '盟')) {
  99. $deep2_area_name = mb_substr($detail, $tmp_pos - 2, 3);
  100. }
  101. if ($tmp_pos = mb_strrpos($detail, '州')) {
  102. //考虑自治州的情况
  103. if($tmp_pos = mb_strrpos($detail, '自治州')) {
  104. $deep2_area_name = mb_substr($detail, $tmp_pos-4, 5);
  105. }else{
  106. $deep2_area_name = mb_substr($detail, $tmp_pos-2, 3);
  107. }
  108. }
  109. } else {
  110. $deep2_area_name = '';
  111. }
  112. //3. 到数据中智能匹配
  113. if ($deep3_area_name != '') {
  114. $model_area = new Area();
  115. //数据库匹配 以下的数据库匹配需要程序员根据自己的框架自行替换
  116. $condition = [];
  117. $condition['area_deep'] = 3;
  118. $condition[] = ['area_name','like', '%' . $deep3_area_name . '%'];
  119. // DB::connection()->enableQueryLog(); // 开启QueryLog
  120. $deep3_area_list = $model_area->getAreaList($condition);
  121. // \App\User::find(1);
  122. // return DB::getQueryLog();
  123. // 三级地址的匹配出现多个结果 依靠二级地址缩小范围
  124. if ($deep3_area_list && count($deep3_area_list) > 1) {
  125. if ($deep2_area_name) {
  126. $area_info_2 = $model_area->getAreaInfo(['area_name','like', '%' . $deep2_area_name . '%']);
  127. //2级地址匹配成功 再次缩小三级地址 然后确定一级地址
  128. if ($area_info_2) {
  129. $area_info_3 = $model_area->getAreaInfo(['area_parent_id' => $area_info_2['area_id'], array('area_name','like', '%' . $deep3_area_name . '%')]);
  130. }
  131. $area_info_1 = $model_area->getAreaInfo(['area_id' => $area_info_2['area_parent_id'], 'area_deep' => 1]);
  132. //获得结果
  133. $result[1]['area_id'] = $area_info_2['area_parent_id'];
  134. $result[1]['area_name'] = $area_info_1['area_name'];
  135. $result[2]['area_id'] = $area_info_2['area_id'];
  136. $result[2]['area_name'] = $area_info_2['area_name'];
  137. $result[3]['area_id'] = $area_info_3['area_id'];
  138. $result[3]['area_name'] = $area_info_3['area_name'];
  139. }
  140. } else {
  141. if ($deep3_area_list && count($deep3_area_list) == 1) {
  142. $area_info_2 = $model_area->getAreaInfo(['area_id' => $deep3_area_list[0]['area_parent_id'], 'area_deep' => 2]);
  143. if ($area_info_2) {
  144. $area_info_1 = $model_area->getAreaInfo(['area_id' => $area_info_2['area_parent_id'], 'area_deep' => 1]);
  145. //获得结果
  146. $result[1]['area_id'] = $area_info_2['area_parent_id'];
  147. $result[1]['area_name'] = $area_info_1['area_name'];
  148. $result[2]['area_id'] = $area_info_2['area_id'];
  149. $result[2]['area_name'] = $area_info_2['area_name'];
  150. $result[3]['area_id'] = $deep3_area_list[0]['area_id'];
  151. $result[3]['area_name'] = $deep3_area_list[0]['area_name'];
  152. }
  153. }elseif($deep2_area_name == $deep3_area_name){ //如出现内蒙古自治区乌兰察布市公安局交警支队车管所这种只有省市,没有区的情况
  154. $area_info_2 = $model_area->getAreaInfo(['area_deep'=>2, ['area_name','like', '%' . $deep2_area_name . '%']]);
  155. if ($area_info_2) {
  156. $area_info_1 = $model_area->getAreaInfo(['area_id' => $area_info_2['area_parent_id'], 'area_deep' => 1]);
  157. //获得结果
  158. if($area_info_1){
  159. $result[1]['area_id'] = $area_info_2['area_parent_id'];
  160. $result[1]['area_name'] = $area_info_1['area_name'];
  161. $result[2]['area_id'] = $area_info_2['area_id'];
  162. $result[2]['area_name'] = $area_info_2['area_name'];
  163. $result[3]['area_id'] = '';
  164. $result[3]['area_name'] = '';
  165. }
  166. }
  167. }
  168. }
  169. }
  170. //最终结果
  171. return $result;
  172. }
  173. }
  174. //$obj = AddressDetail::detail_parse('成都市高新区天府软件园B区科技大楼');
  175. //$obj2 = AddressDetail::detail_parse('朝阳区宵云路36号国航大厦一层');