#
vincentlu
昨天 5b2e04269894ec4e6bcc1e3502bfa203e33cfe3a
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
package com.zy.acs.manager.common.interceptor;
 
import com.alibaba.fastjson.JSON;
import com.zy.acs.framework.common.Cools;
import com.zy.acs.framework.common.R;
import com.zy.acs.framework.common.SnowflakeIdWorker;
import com.zy.acs.manager.common.interceptor.IntegrationOpenApiInterceptor.IntegrationRequestContext;
import com.zy.acs.manager.common.interceptor.IntegrationOpenApiInterceptor.RequestPayload;
import com.zy.acs.manager.common.utils.IpTools;
import com.zy.acs.manager.manager.entity.IntegrationRecord;
import com.zy.acs.manager.manager.enums.IntegrationDirectionType;
import com.zy.acs.manager.manager.enums.StatusType;
import com.zy.acs.manager.manager.service.IntegrationRecordService;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
 
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.UUID;
 
@Slf4j
@ControllerAdvice
public class IntegrationRecordAdvice implements ResponseBodyAdvice<Object> {
 
    private static final String HEADER_APP_KEY = "appkey";
    private static final String HEADER_CALLER = "caller";
 
    @Autowired
    private IntegrationRecordService integrationRecordService;
    @Resource
    private SnowflakeIdWorker snowflakeIdWorker;
 
    @Override
    public boolean supports(@NotNull MethodParameter methodParameter,
                            @NotNull Class<? extends HttpMessageConverter<?>> aClass) {
        return true;
    }
 
    @Override
    public Object beforeBodyWrite(Object body,
                                  @NotNull MethodParameter methodParameter,
                                  @NotNull MediaType mediaType,
                                  @NotNull Class<? extends HttpMessageConverter<?>> aClass,
                                  @NotNull ServerHttpRequest serverHttpRequest,
                                  @NotNull ServerHttpResponse serverHttpResponse) {
        if (!(serverHttpRequest instanceof ServletServerHttpRequest)) {
            return body;
        }
        HttpServletRequest request = ((ServletServerHttpRequest) serverHttpRequest).getServletRequest();
        IntegrationRequestContext context = IntegrationOpenApiInterceptor.getContext(request);
        if (context == null) {
            return body;
        }
        IntegrationRecord record = null;
        try {
            record = buildRecord(body, request, context, null);
        } catch (Exception e) {
            log.error("Failed to build integration log for {}", context.getHandler(), e);
            record = buildRecord(null, request, context, e);
        } finally {
            if (record != null) {
                try {
                    integrationRecordService.syncRecord(record);
                } catch (Exception persistEx) {
                    log.error("Failed to persist integration log for {}", context.getHandler(), persistEx);
                }
            }
        }
        return body;
    }
 
    private IntegrationRecord buildRecord(Object responseBody,
                                          HttpServletRequest request,
                                          IntegrationRequestContext context,
                                          Exception failure) {
        Date now = new Date();
        RequestPayload payload = context.getPayload();
 
        IntegrationRecord record = new IntegrationRecord();
        record.setUuid(nextUuid());
        record.setNamespace(context.getNamespaceType().name());
        record.setUrl(payload.getUri());
        record.setAppkey(request.getHeader(HEADER_APP_KEY));
        record.setCaller(resolveCaller(request));
        record.setDirection(IntegrationDirectionType.INBOUND.value);
        record.setTimestamp(String.valueOf(context.getStartAt()));
        record.setClientIp(IpTools.gainRealIp(request));
        record.setRequest(safeToJson(payload));
        record.setResponse(safeToJson(responseBody));
        applyResult(record, responseBody, failure);
        record.setCostMs(cost(context.getStartAt()));
        record.setStatus(StatusType.ENABLE.val);
        record.setCreateTime(now);
        record.setUpdateTime(now);
        record.setMemo(context.getHandler());
        return record;
    }
 
    private void applyResult(IntegrationRecord record, Object body, Exception failure) {
        if (failure != null) {
            record.setResult(0);
            record.setErr(failure.getMessage());
            return;
        }
        if (!(body instanceof R)) {
            record.setResult(null);
            record.setErr(null);
            return;
        }
        R response = (R) body;
        Integer code = parseInteger(response.get("code"));
        if (code == null) {
            record.setResult(null);
            record.setErr(null);
            return;
        }
        boolean success = code == 200;
        record.setResult(success ? 1 : 0);
        record.setErr(success ? null : safeToString(response.get("msg")));
    }
 
    private Integer parseInteger(Object codeObj) {
        if (codeObj == null) {
            return null;
        }
        if (codeObj instanceof Integer) {
            return (Integer) codeObj;
        }
        try {
            return Integer.parseInt(String.valueOf(codeObj));
        } catch (NumberFormatException e) {
            return null;
        }
    }
 
    private String safeToJson(Object value) {
        if (value == null) {
            return null;
        }
        try {
            return JSON.toJSONString(value);
        } catch (Exception e) {
            log.warn("Failed to serialize value for integration log: {}", value.getClass().getName(), e);
            return String.valueOf(value);
        }
    }
 
    private String resolveCaller(HttpServletRequest request) {
        String caller = request.getHeader(HEADER_CALLER);
        if (Cools.isEmpty(caller)) {
            caller = request.getHeader(HEADER_APP_KEY);
        }
        return caller;
    }
 
    private int cost(long startAt) {
        long duration = System.currentTimeMillis() - startAt;
        if (duration < 0) {
            return 0;
        }
        return (int) duration;
    }
 
    private String nextUuid() {
        if (snowflakeIdWorker != null) {
            return String.valueOf(snowflakeIdWorker.nextId()).substring(3);
        }
        return UUID.randomUUID().toString().replace("-", "");
    }
 
    private String safeToString(Object value) {
        return value == null ? null : String.valueOf(value);
    }
}