puzhibing
5 天以前 8def7d50fdac0d2d464edce1d94f5bb29c519302
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
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<String, Object> 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<String, Object> params = builder.build(gdKey, sid);
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
            HttpEntity<MultiValueMap<String, Object>> 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<String, Object> params = new LinkedMultiValueMap<>();
        params.add("key", key);
        params.add("name", name);
        HttpEntity<MultiValueMap<String, Object>> 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<String, Object> 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<String, Object> 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<String, Object> 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"));
        }
    }
 
}