package com.stylefeng.guns.modular.system.util; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.stereotype.Component; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestTemplate; /** * 高德猎鹰服务 * key次数用完时自动切换可用key(切换后自动重建serverId) */ @Component public class GDFalconUtil implements ApplicationRunner { @Autowired private RestTemplate restTemplate; @Autowired private GDKeyManager gdKeyManager; private static String serverId = null; /** * 当前serverId对应的key(key切换后需重建serverId) */ private static volatile String currentKey = null; public String getServerId() { return serverId; } /** * 服务启动后执行的代码 * @param args * @throws Exception */ @Override public void run(ApplicationArguments args) throws Exception { this.init(); } /** * 初始化创建服务 */ public void init(){ if(serverId == null){ if (gdKeyManager == null || restTemplate == null) { // 非容器实例(如 GunsApplication.main 中 new 出来的对象),由容器内bean完成初始化 return; } ensureServerId(); } } /** * 参数构造器:key与sid切换后需按最新值重新构造 */ private interface ParamsBuilder { MultiValueMap build(String key, String sid); } /** * 确保serverId与当前使用的key对应: * key切换后自动查询/重建serverId;key用完自动切换可用key重试 */ private synchronized String ensureServerId(){ int maxAttempts = 5; String lastKey = gdKeyManager.getKey(); for (int i = 0; i < maxAttempts; i++) { String gdKey = gdKeyManager.getKey(); lastKey = gdKey; if (serverId != null && gdKey.equals(currentKey)) { return gdKey; } // 1. 查询该key下的服务列表 JSONArray jsonArray = null; try { String url = "https://tsapi.amap.com/v1/track/service/list?key=" + gdKey; String forObject = restTemplate.getForObject(url, String.class); JSONObject jsonObject = JSON.parseObject(forObject); if (GDKeyManager.isFalconExhausted(jsonObject)) { gdKeyManager.reportExhausted(gdKey); serverId = null; continue; } if (jsonObject.getIntValue("errcode") == 10000) { JSONObject data = jsonObject.getJSONObject("data"); if (null != data) { jsonArray = data.getJSONArray("results"); } } } catch (Exception e) { loggerWarn("查询猎鹰服务列表异常", e); } if (jsonArray != null && jsonArray.size() > 0) { serverId = jsonArray.getJSONObject(0).getString("sid"); currentKey = gdKey; return gdKey; } // 2. 无服务:创建新服务 String sid = createServerInternal("server", gdKey); if (sid != null) { serverId = sid; currentKey = gdKey; return gdKey; } // 3. 创建失败(可能key用完):切换key重试 gdKeyManager.reportExhausted(gdKey); serverId = null; } return lastKey; } private void loggerWarn(String msg, Exception e) { System.err.println("[GDFalconUtil] " + msg + ":" + e.getMessage()); } /** * 执行猎鹰POST表单请求:key次数用完时自动切换key并重建serverId重试 */ private String doFalconPost(String url, ParamsBuilder builder) { String lastResp = null; int maxAttempts = 5; for (int i = 0; i < maxAttempts; i++) { String gdKey = ensureServerId(); String sid = serverId; MultiValueMap params = builder.build(gdKey, sid); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); HttpEntity> requestEntity = new HttpEntity<>(params, headers); String resp = restTemplate.postForObject(url, requestEntity, String.class); lastResp = resp; JSONObject jsonObject; try { jsonObject = JSON.parseObject(resp); } catch (Exception e) { return resp; } if (GDKeyManager.isFalconExhausted(jsonObject)) { gdKeyManager.reportExhausted(gdKey); serverId = null; continue; } return resp; } return lastResp; } /** * 执行猎鹰GET请求:key次数用完时自动切换可用key重试 */ private String doFalconGet(String baseUrl) { String lastResp = null; int maxAttempts = 5; for (int i = 0; i < maxAttempts; i++) { String gdKey = gdKeyManager.getKey(); String url = baseUrl + (baseUrl.contains("?") ? "&" : "?") + "key=" + gdKey; String resp = restTemplate.getForObject(url, String.class); lastResp = resp; JSONObject jsonObject; try { jsonObject = JSON.parseObject(resp); } catch (Exception e) { return resp; } if (GDKeyManager.isFalconExhausted(jsonObject)) { gdKeyManager.reportExhausted(gdKey); continue; } return resp; } return lastResp; } /** * 创建新的服务 * @return */ public String createServer(String name){ return createServerInternal(name, gdKeyManager.getKey()); } /** * 创建新的服务(内部直连,供ensureServerId使用) */ private String createServerInternal(String name, String key){ String url = "https://tsapi.amap.com/v1/track/service/add"; //调用推送 HttpHeaders headers = new HttpHeaders(); // 以表单的方式提交 headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); //将请求头部和参数合成一个请求 MultiValueMap params = new LinkedMultiValueMap<>(); params.add("key", key); params.add("name", name); HttpEntity> requestEntity = new HttpEntity<>(params, headers); String s = restTemplate.postForObject(url, requestEntity , String.class); JSONObject jsonObject = JSON.parseObject(s); if(jsonObject.getIntValue("errcode") == 10000){ JSONObject data = jsonObject.getJSONObject("data"); if(null != data){ return data.getString("sid"); } } return null; } /** * 创建终端数据 * @param name * @return */ public String createTerminal(String name){ String tid = this.selectTerminal(name); if(tid != null){ return tid; } String s = doFalconPost("https://tsapi.amap.com/v1/track/terminal/add", (key, sid) -> { MultiValueMap params = new LinkedMultiValueMap<>(); params.add("key", key); params.add("sid", sid); params.add("name", name); return params; }); JSONObject jsonObject = JSON.parseObject(s); if(jsonObject.getIntValue("errcode") == 10000){ JSONObject data = jsonObject.getJSONObject("data"); return data.getString("tid"); } System.err.println(jsonObject.getString("errmsg")); return null; } /** * 查询终端 * @param name * @return */ public String selectTerminal(String name){ String forObject = doFalconGet("https://tsapi.amap.com/v1/track/terminal/list?name=" + name); JSONObject jsonObject = JSON.parseObject(forObject); if(jsonObject.getIntValue("errcode") == 10000){ JSONObject data = jsonObject.getJSONObject("data"); int count = 0; if(null != data){ count = data.getIntValue("count"); } if(count > 0){ JSONObject results = data.getJSONArray("results").getJSONObject(0); return results.getString("tid"); }else{ return this.createTerminal(name); } } return null; } /** * 创建轨迹 * @param tid 终端id * @return */ public String createTrack(String tid){ String s = doFalconPost("https://tsapi.amap.com/v1/track/trace/add", (key, sid) -> { MultiValueMap params = new LinkedMultiValueMap<>(); params.add("key", key); params.add("sid", sid); params.add("tid", tid); return params; }); JSONObject jsonObject = JSON.parseObject(s); if(jsonObject.getIntValue("errcode") == 10000){ return jsonObject.getString("data"); } System.err.println(jsonObject.getString("errmsg")); return null; } /** * 删除轨迹 * @param tid 终端id * @param trid 轨迹id */ public void deleteTrack(String tid, String trid){ String s = doFalconPost("https://tsapi.amap.com/v1/track/trace/delete", (key, sid) -> { MultiValueMap params = new LinkedMultiValueMap<>(); params.add("key", key); params.add("sid", sid); params.add("tid", tid); params.add("trid", trid); return params; }); JSONObject jsonObject = JSON.parseObject(s); if(jsonObject.getIntValue("errcode") != 10000){ System.err.println(jsonObject.getString("errmsg")); } } }