#
vincentlu
昨天 313f81780f554a4962f131bbd6ab795c24d1edfe
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
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.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.AllArgsConstructor;
import lombok.Getter;
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;
        }
        try {
            IntegrationRecord record = buildRecord(body, request, context);
            integrationRecordService.syncRecord(record);
        } catch (Exception e) {
            log.error("Failed to persist integration log for {}", context.getHandler(), e);
        }
        return body;
    }
 
    private IntegrationRecord buildRecord(Object responseBody,
                                          HttpServletRequest request,
                                          IntegrationRequestContext context) {
        Date now = new Date();
        ResultView resultView = resolveResult(responseBody);
 
        IntegrationRecord record = new IntegrationRecord();
        record.setUuid(nextUuid());
        record.setNamespace(context.getNamespace());
        record.setUrl(context.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(context.getRequestSnapshot()));
        record.setResponse(safeToJson(responseBody));
        record.setErr(resultView.getError());
        record.setResult(resultView.getResult());
        record.setCostMs(cost(context.getStartAt()));
        record.setStatus(StatusType.ENABLE.val);
        record.setCreateTime(now);
        record.setUpdateTime(now);
        record.setMemo(context.getHandler());
        return record;
    }
 
    private ResultView resolveResult(Object body) {
        if (!(body instanceof R)) {
            return ResultView.unknown();
        }
        R response = (R) body;
        Integer code = parseInteger(response.get("code"));
        if (code == null) {
            return ResultView.unknown();
        }
        boolean success = code == 200;
        String error = success ? null : safeToString(response.get("msg"));
        return new ResultView(success ? 1 : 0, error);
    }
 
    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);
    }
 
    @Getter
    @AllArgsConstructor
    private static class ResultView {
        private final Integer result;
        private final String error;
 
        private static ResultView unknown() {
            return new ResultView(null, null);
        }
    }
}