AddressQueryEngine.java 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460
  1. package com.skyversation.poiaddr.addquery;
  2. import com.alibaba.fastjson.JSONArray;
  3. import com.alibaba.fastjson.JSONObject;
  4. import com.skyversation.poiaddr.bean.AddressResult;
  5. import com.skyversation.poiaddr.bean.GeoJsonBean;
  6. import com.skyversation.poiaddr.bean.WDToken;
  7. import com.skyversation.poiaddr.service.AreaService;
  8. import com.skyversation.poiaddr.util.net.AddressNetTools;
  9. import com.skyversation.poiaddr.util.status.AddressLevel;
  10. import com.skyversation.poiaddr.util.status.AddressResultEnum;
  11. import org.springframework.http.HttpMethod;
  12. import org.springframework.http.ResponseEntity;
  13. import org.springframework.util.LinkedMultiValueMap;
  14. import org.springframework.util.MultiValueMap;
  15. import org.springframework.util.StringUtils;
  16. import org.apache.commons.codec.binary.Base64;
  17. import javax.crypto.Mac;
  18. import javax.crypto.spec.SecretKeySpec;
  19. import java.util.*;
  20. public class AddressQueryEngine {
  21. private static AddressQueryEngine instance = new AddressQueryEngine();
  22. private AddressQueryEngine() {
  23. }
  24. public synchronized static AddressQueryEngine getInstance() {
  25. if (instance == null) {
  26. instance = new AddressQueryEngine();
  27. }
  28. return instance;
  29. }
  30. private WDToken wdToken = null;
  31. /***
  32. * 单条地名搜索通用方法,搜索顺序 1. 武大吉奥; 2. 市四中心; 3. 高德搜索; 4. 高德高级搜索;
  33. * @param addrs
  34. * @return
  35. */
  36. public AddressResult commonSearchByName(List<String> addrs) {
  37. if (addrs == null || addrs.size() < 1) {
  38. return null;
  39. }
  40. AddressResult addressResult = new AddressResult();
  41. for (String addr : addrs) {
  42. // 创建请求
  43. addressResult = sj_szxSearchByName(addr);
  44. // addressResult = szxSearchByName(addr);
  45. if (addressResult != null) {
  46. addressResult.setCode(AddressResultEnum.SZX_SUCCESS);
  47. addressResult.setMessage("成功");
  48. return getCjWgWgwByLoc(addressResult);
  49. }
  50. }
  51. addressResult.setCode(AddressResultEnum.RESULT_NULL);
  52. addressResult.setMessage("失败");
  53. return addressResult;
  54. }
  55. /***
  56. * 武大吉奥单条地名搜索
  57. * @param address
  58. */
  59. public AddressResult wdjaSearchByName(String address) {
  60. System.out.println("<<<<<<<<<<----------开始武大吉奥地名地址搜索------------>>>>>>>>>>>>>");
  61. if (wdToken == null || System.currentTimeMillis() - wdToken.getTime() > 36000000) {
  62. wdToken = AddressTools.getInstance().getWDToken(Constant.WD_USER_NAME, Constant.WD_USER_PWD);
  63. if (wdToken == null) {
  64. return null;
  65. }
  66. wdToken.setTime(System.currentTimeMillis());
  67. }
  68. if (wdToken == null)
  69. return AddressTools.getInstance().faildQuery(AddressResultEnum.NO_TOKEN, "token获取失败");
  70. String token = wdToken.getToken();
  71. String url = Constant.GET_ADDRESS_MEG_URL + "?token=" + token + "&addr=" + address;
  72. String body = null;
  73. try {
  74. body = AddressNetTools.getInstance().wdSendGetRequest(url);
  75. } catch (Exception e) {
  76. e.toString();
  77. }
  78. if (body == null || body.equals("null") || !StringUtils.hasText(body))
  79. return AddressTools.getInstance().faildQuery(AddressResultEnum.RESULT_NULL, "搜索无结果");
  80. try {
  81. JSONObject json = JSONObject.parseObject(body);
  82. return TransfromDataTool.wdResultToResult(json);
  83. } catch (Exception e) {
  84. return AddressTools.getInstance().faildQuery(AddressResultEnum.DATA_FROMAT_FAILD, "格式化失败");
  85. }
  86. }
  87. public String getSigns(String timestamp, String appCode, String UUid, String appSecret) {
  88. // 先拼接
  89. String signData = timestamp + appCode + UUid;
  90. // 生成sign
  91. String sign = "";
  92. try {
  93. Mac sha256 = Mac.getInstance("HmacSHA256");
  94. sha256.init(new SecretKeySpec(appSecret.getBytes(), "HmacSHA256"));
  95. byte[] result = sha256.doFinal(signData.getBytes());
  96. sign = Base64.encodeBase64String(result);
  97. } catch (Exception e) {
  98. System.err.println("sign 生成异常:" + e);
  99. }
  100. return sign;
  101. }
  102. public AddressResult sj_szxSearchByName(String address) {
  103. // 鉴权
  104. Map<String, String> headerMap = new HashMap<>();
  105. String timestamp = System.currentTimeMillis() + "";
  106. String appCode = "7unv4vbwqxnq4a7m9h";
  107. String UUid = UUID.randomUUID().toString();
  108. String appSecret = "w6tawvf4k3ck4ikij9";
  109. headerMap.put("x-timestamp", timestamp);
  110. headerMap.put("x-appcode", appCode);
  111. headerMap.put("x-uuid", UUid);
  112. // 签名
  113. headerMap.put("x-sign", getSigns(timestamp, appCode, UUid, appSecret));
  114. JSONObject params = new JSONObject();
  115. JSONArray requestJson = new JSONArray();
  116. JSONObject paramObject = new JSONObject();
  117. paramObject.put("name", "query");
  118. paramObject.put("position", "QUERY");
  119. paramObject.put("value", address);
  120. requestJson.add(paramObject);
  121. // 判断是否是关键字版搜索服务
  122. if(Constant.SJ_SZX_SEARCH_BY_NAME.contains("1742459783686")){
  123. JSONObject paramObject2 = new JSONObject();
  124. paramObject2.put("name","region");
  125. paramObject2.put("position", "QUERY");
  126. paramObject2.put("value", Constant.getAMAP_CITY_CODE());
  127. requestJson.add(paramObject2);
  128. JSONObject paramObject3 = new JSONObject();
  129. paramObject3.put("name","page_size");
  130. paramObject3.put("position", "QUERY");
  131. paramObject3.put("value", "10");
  132. requestJson.add(paramObject3);
  133. }
  134. params.put("requestJson", requestJson);
  135. // 发起请求
  136. ResponseEntity response = AddressNetTools.getInstance().requestGetOrPost(HttpMethod.POST, Constant.SJ_SZX_SEARCH_BY_NAME, params, headerMap, 1);
  137. if (response != null && response.hasBody()) {
  138. String body = response.getBody() + "";
  139. if (!StringUtils.hasText(body))
  140. return AddressTools.getInstance().faildQuery(AddressResultEnum.RESULT_NULL, "搜索无结果");
  141. try {
  142. if (body.indexOf("[") != 0) {
  143. return TransfromDataTool.szxResultToResult(JSONObject.parseObject(body),address);
  144. } else {
  145. // 将得分最高的结果映射到实体类
  146. return TransfromDataTool.szxResultToResult2(com.skyversation.poiaddr.util.AddressTools.getInstance().findBestMatch(address, JSONArray.parseArray(body), "address"));
  147. }
  148. } catch (Exception e) {
  149. System.err.println("请求地址:" + address + ";返回结果:" + body + ";处理异常:" + e);
  150. return AddressTools.getInstance().faildQuery(AddressResultEnum.DATA_FROMAT_FAILD, "格式化失败");
  151. }
  152. } else {
  153. System.out.println("地址[" + address + "]未查询到数据!");
  154. return null;
  155. }
  156. }
  157. /***
  158. * 高德普通地名搜索
  159. * @param address
  160. * @return
  161. */
  162. public AddressResult gdSearchByName(String address) {
  163. String geoUrl = Constant.AMAP_GEO_URL + "?key=" + Constant.AMAP_KEY + "&address=" + address + "&city=" + Constant.getAMAP_CITY_CODE() + "&output=JSON";
  164. ResponseEntity response = AddressNetTools.getInstance().requestGetOrPost(HttpMethod.GET, geoUrl, null, null, 0);
  165. if (response != null && response.getBody() != null) {
  166. String body = response.getBody() + "";
  167. if (!StringUtils.hasText(body))
  168. return AddressTools.getInstance().faildQuery(AddressResultEnum.RESULT_NULL, "搜索无结果");
  169. try {
  170. JSONObject json = JSONObject.parseObject(body);
  171. return TransfromDataTool.gdResultToResult(json);
  172. } catch (Exception e) {
  173. System.err.println(e);
  174. return AddressTools.getInstance().faildQuery(AddressResultEnum.DATA_FROMAT_FAILD, "格式化失败");
  175. }
  176. } else {
  177. return null;
  178. }
  179. }
  180. /***
  181. * 高德高级地名搜索
  182. * @param address
  183. * @return
  184. */
  185. public AddressResult gdV3SearchByName(String address) {
  186. String geoUrl =
  187. Constant.AMAP_SEARCH_NAME_V3 + "?key=" + Constant.AMAP_KEY + "&types=" + Constant.AMAP_SEARCH_TYPES +
  188. "&keywords=" + address + "&city=" + Constant.getAMAP_CITY_CODE() + "&offset=20";
  189. ResponseEntity responseEntity = AddressNetTools.getInstance().requestGetOrPost(HttpMethod.GET, geoUrl, null, null, 0);
  190. if (responseEntity.hasBody()) {
  191. String body = responseEntity.getBody() + "";
  192. if (!StringUtils.hasText(body)) {
  193. return AddressTools.getInstance().faildQuery(AddressResultEnum.RESULT_NULL, "搜索无结果");
  194. }
  195. JSONArray pois = new JSONArray();
  196. JSONObject json = JSONObject.parseObject(body);
  197. if (json.containsKey("pois")) {
  198. pois.addAll(json.getJSONArray("pois"));
  199. }
  200. if (json.containsKey("count")) {
  201. int count = Integer.parseInt(json.get("count") + "");
  202. if (count > 20) {
  203. for (int i = 2; i < (count % 20 + 1); i++) {
  204. geoUrl =
  205. Constant.AMAP_SEARCH_NAME_V3 + "?key=" + Constant.AMAP_KEY + "&types=" + Constant.AMAP_SEARCH_TYPES +
  206. "&keywords=" + address + "&city=" + Constant.getAMAP_CITY_CODE() + "&offset=20&page=" + i;
  207. ResponseEntity responseEntity2 = AddressNetTools.getInstance().requestGetOrPost(HttpMethod.GET, geoUrl, null, null, 0);
  208. if (responseEntity2.hasBody()) {
  209. body = responseEntity2.getBody() + "";
  210. }
  211. if (!StringUtils.hasText(body)) {
  212. return AddressTools.getInstance().faildQuery(AddressResultEnum.RESULT_AGAIN_NULL, "多次搜索无结果");
  213. }
  214. JSONObject json2 = JSONObject.parseObject(body);
  215. if (json2.containsKey(pois)) {
  216. pois.addAll(JSONObject.parseObject(body).getJSONArray("pois"));
  217. }
  218. }
  219. }
  220. }
  221. try {
  222. return TransfromDataTool.gdV3ResultToResult(pois);
  223. } catch (Exception e) {
  224. System.err.println(e);
  225. return AddressTools.getInstance().faildQuery(AddressResultEnum.DATA_FROMAT_FAILD, "格式化失败");
  226. }
  227. } else {
  228. return AddressTools.getInstance().faildQuery(AddressResultEnum.RESULT_AGAIN_NULL, "高德V3分页查询无结果");
  229. }
  230. }
  231. /***
  232. * 根据搜索地址,赋值村居、网格、微格网信息
  233. * @param result
  234. * @return
  235. */
  236. public AddressResult getCjWgWgwByLoc(AddressResult result) {
  237. if (result == null || result.getData() == null || result.getData().size() < 1) {
  238. return result;
  239. } else {
  240. AreaService areaService = AreaService.getInstance();
  241. for (AddressResult.ContentBean content : result.getData()) {
  242. if ((content.getAdname() == null || content.getAdname().isEmpty() || content.getCityname() == null || content.getCityname().isEmpty()) && content.getLat() != null && content.getLon() != null) {
  243. GeoJsonBean adBean = areaService.isInadPolygon(content.getLon(), content.getLat());
  244. content.setAdJson(adBean == null ? new JSONObject() : adBean.getProperties());
  245. GeoJsonBean townBean = areaService.isInTownPolygon(content.getLon(), content.getLat());
  246. content.setTownJson(townBean == null ? new JSONObject() : townBean.getProperties());
  247. }
  248. }
  249. return result;
  250. }
  251. }
  252. //
  253. public static boolean isNotEmptyOrBlank(String str) {
  254. return str != null && !str.trim().isEmpty();
  255. }
  256. /**
  257. * 1. 搜索到结果就返回第一个;
  258. * 2. 搜索到结果去除行政区划后,存在包含关系,则使用;
  259. * 3. 搜索到结果,数字进行分词,数字匹配则使用;
  260. * 4. 搜索到结果,数字与文本均匹配,则使用;
  261. * 5. 结果与基准数据完全一致,则使用。
  262. *
  263. * @param result
  264. * @param level
  265. * @return
  266. */
  267. public AddressResult.ContentBean verificaData(AddressResult result, AddressLevel level, String addr) {
  268. switch (level) {
  269. case LEVEL_1: {
  270. if (result != null && result.getData() != null && result.getData().size() > 0) {
  271. return result.getData().get(0);
  272. } else {
  273. return null;
  274. }
  275. }
  276. // 2. 搜索到结果去除行政区划后,存在包含关系,则使用;
  277. case LEVEL_CONTAINS_2: {
  278. if (result != null && result.getData() != null && result.getData().size() > 0) {
  279. List<AddressResult.ContentBean> contentBean = result.getData();
  280. for (AddressResult.ContentBean contentBean1 : contentBean) {
  281. if (contentBean1.getAddress() != null) {
  282. String address = townReplaceAll(addressReplaceAll(contentBean1.getAddress()));
  283. String address2 = townReplaceAll(addressReplaceAll(addr));
  284. if (isNotEmptyOrBlank(address) && address.contains(address2)) {
  285. return contentBean1;
  286. }
  287. }
  288. }
  289. } else {
  290. return null;
  291. }
  292. }
  293. // 3. 搜索到结果,数字进行分词,数字匹配则使用;
  294. case LEVLE_NUMBER_3: {
  295. if (result != null && result.getData() != null && result.getData().size() > 0) {
  296. List<AddressResult.ContentBean> contentBean = result.getData();
  297. for (AddressResult.ContentBean contentBean1 : contentBean) {
  298. if (contentBean1.getAddress() != null) {
  299. Set<String> address = tokenizeString(contentBean1.getAddress()).get(1);
  300. if (address != null && address.size() > 1) {
  301. Set<String> address2 = tokenizeString(addr).get(1);
  302. int addressSize = address.size();
  303. for (String addr2 : address2) {
  304. if (address.contains(addr2)) {
  305. addressSize--;
  306. if (addressSize == 0) {
  307. return contentBean1;
  308. }
  309. }
  310. }
  311. }
  312. }
  313. }
  314. } else {
  315. return null;
  316. }
  317. }
  318. // 4. 搜索到结果,数字与文本均匹配,则使用;
  319. case LEVEL_NUMBER_TEXT_4: {
  320. if (result != null && result.getData() != null && result.getData().size() > 0) {
  321. List<AddressResult.ContentBean> contentBean = result.getData();
  322. for (AddressResult.ContentBean contentBean1 : contentBean) {
  323. if (contentBean1.getAddress() != null) {
  324. Set<String> addressString = tokenizeString(contentBean1.getAddress()).get(0);
  325. Set<String> addressNumber = tokenizeString(contentBean1.getAddress()).get(1);
  326. Set<String> address2String = tokenizeString(addr).get(0);
  327. Set<String> address2Number = tokenizeString(addr).get(1);
  328. if (addressString != null && addressString.size() > 1) {
  329. int addressStrSize = addressString.size();
  330. for (String addr2str : address2String) {
  331. if (addressString.contains(addr2str)) {
  332. addressStrSize--;
  333. if (addressStrSize == 0) {
  334. int addressNumSize = addressNumber.size();
  335. for (String addr2Num : address2Number) {
  336. if (addressNumber.contains(addr2Num)) {
  337. addressNumSize--;
  338. if (addressNumSize == 0) {
  339. return contentBean1;
  340. }
  341. }
  342. }
  343. }
  344. }
  345. }
  346. }
  347. }
  348. }
  349. } else {
  350. return null;
  351. }
  352. }
  353. // 结果与基准数据完全一致,则使用。
  354. case LEVEL_TOTAL_CONTAINS_5: {
  355. if (result != null && result.getData() != null && result.getData().size() > 0) {
  356. List<AddressResult.ContentBean> contentBean = result.getData();
  357. for (AddressResult.ContentBean contentBean1 : contentBean) {
  358. if (contentBean1.getAddress() != null) {
  359. String address = addressReplaceAll(contentBean1.getAddress());
  360. String address2 = addressReplaceAll(addr);
  361. if (isNotEmptyOrBlank(address) && address.equals(address2)) {
  362. return contentBean1;
  363. }
  364. }
  365. }
  366. } else {
  367. return null;
  368. }
  369. }
  370. default: {
  371. return null;
  372. }
  373. }
  374. }
  375. public static String addressReplaceAll(String address) {
  376. return address.replaceAll("上海市", "").replaceAll(Constant.getArea() + "区", "").replaceAll("-", "").replaceAll("_", "").replaceAll("/", "").replaceAll(" ", "").replaceAll(",", "").replaceAll("\\.", "").replaceAll(",", "").replaceAll("。", "").replaceAll("\\+", "").replaceAll("\\*", "");
  377. }
  378. public static String townReplaceAll(String address) {
  379. String[] towns = Constant.getTowns();
  380. for (String town : towns) {
  381. address = address.replaceAll(town, "");
  382. }
  383. return address.replaceAll("小区", "0").replaceAll("号", "0").replaceAll("弄", "0").replaceAll("室", "0").replaceAll("户", "0").replaceAll("单元", "0").replaceAll("幢", "0");
  384. }
  385. /**
  386. * 数字和文字分词返回List<Set<String>>
  387. *
  388. * @param input
  389. * @return
  390. */
  391. public static List<Set<String>> tokenizeString(String input) {
  392. input = townReplaceAll(input);
  393. // 初始化两个集合,一个用于存储非数字字符串,一个用于存储数字字符串
  394. Set<String> nonNumberSet = new HashSet<>();
  395. Set<String> numberSet = new HashSet<>();
  396. StringBuilder currentToken = new StringBuilder();
  397. for (int i = 0; i < input.length(); i++) {
  398. char c = input.charAt(i);
  399. if (Character.isDigit(c)) {
  400. // 如果当前字符是数字
  401. if (currentToken.length() > 0 && !Character.isDigit(currentToken.charAt(0))) {
  402. // 如果之前的 token 是非数字,将其添加到非数字集合中
  403. nonNumberSet.add(currentToken.toString());
  404. currentToken.setLength(0);
  405. }
  406. currentToken.append(c);
  407. } else {
  408. // 如果当前字符不是数字
  409. if (currentToken.length() > 0 && Character.isDigit(currentToken.charAt(0))) {
  410. // 如果之前的 token 是数字,将其添加到数字集合中
  411. numberSet.add(currentToken.toString());
  412. currentToken.setLength(0);
  413. }
  414. if (!Character.isWhitespace(c)) {
  415. currentToken.append(c);
  416. }
  417. }
  418. }
  419. // 处理最后一个 token
  420. if (currentToken.length() > 0) {
  421. if (Character.isDigit(currentToken.charAt(0))) {
  422. numberSet.add(currentToken.toString());
  423. } else {
  424. nonNumberSet.add(currentToken.toString());
  425. }
  426. }
  427. // 将两个集合添加到列表中
  428. List<Set<String>> result = new ArrayList<>();
  429. result.add(nonNumberSet);
  430. result.add(numberSet);
  431. return result;
  432. }
  433. }