yanjinhui
2 天以前 fe596f9db05103917c9257348edcbd3ecb5b46e8
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
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
using SqlSugar;
using System.Collections.Concurrent;
using System.Text;
using WIDESEAWCS_Core.HttpContextUser;
using WIDESEAWCS_Core.Seed;
using WIDESEAWCS_Model.Models;
using System.Security.Cryptography;
using WIDESEAWCS_Core.Helper;
using Newtonsoft.Json.Linq;
using WIDESEAWCS_DTO.RGV.FOURBOT;
using WIDESEAWCS_DTO.WMS;
using WIDESEAWCS_DTO;
 
namespace WIDESEAWCS_Core.LogHelper
{
    public static class LoggerNew
    {
        // 用于缓存当天已存在的日志(key: 日期+ApiCode+请求哈希+响应哈希)
        private static ConcurrentDictionary<string, Dt_InterfaceLog> _cacheLogs = new ConcurrentDictionary<string, Dt_InterfaceLog>();
        // 队列用于存储需要新增或更新的日志
        public static ConcurrentQueue<Dt_InterfaceLog> loggerQueueData = new ConcurrentQueue<Dt_InterfaceLog>();
 
        // 每天凌晨清空缓存
        private static DateTime _lastClearDate = DateTime.Now.Date;
 
        static LoggerNew()
        {
            Task.Run(() =>
            {
                StartWriteLog();
            });
 
            // 启动一个后台任务,每天凌晨清空缓存
            Task.Run(() =>
            {
                while (true)
                {
                    Thread.Sleep(60000); // 每分钟检查一次
                    DateTime today = DateTime.Now.Date;
                    if (_lastClearDate < today)
                    {
                        _cacheLogs.Clear();
                        _lastClearDate = today;
                        Console.WriteLine($"Logger缓存已清空,日期:{today:yyyy-MM-dd}");
                    }
                }
            });
        }
 
        static void StartWriteLog()
        {
            List<Dt_InterfaceLog> batchList = new List<Dt_InterfaceLog>();
 
            while (true)
            {
                try
                {
                    if (loggerQueueData.Count() > 0 && batchList.Count < 500)
                    {
                        DequeueToBatchList(batchList);
                        continue;
                    }
 
                    Thread.Sleep(5000);
 
                    if (batchList.Count == 0) { continue; }
 
                    SqlSugarClient sugarClient = new SqlSugarClient(new ConnectionConfig()
                    {
                        ConnectionString = DBContext.GetMainConnectionDb().Connection,
                        IsAutoCloseConnection = true,
                        DbType = DBContext.DbType,
                    });
 
                    try
                    {
                        sugarClient.BeginTran(); // 开启事务
 
                        var toInsert = new List<Dt_InterfaceLog>();
                        var toUpdate = new List<Dt_InterfaceLog>();
 
                        foreach (var log in batchList)
                        {
                            // 如果已经有 Id 或者 IsExisting 标志,直接处理
                            if (log.Id > 0)
                            {
                                toUpdate.Add(log);
                                continue;
                            }
 
                            // 根据哈希值查询是否已存在
                            var existing = sugarClient.Queryable<Dt_InterfaceLog>()
                                .First(x => x.RequestParametersHash == log.RequestParametersHash
                                         && x.ResponseParametersHash == log.ResponseParametersHash
                                         && x.ApiCode == log.ApiCode
                                         && SqlFunc.DateIsSame(x.CreateDate, DateTime.Now)); // 同一天
 
                            if (existing != null)
                            {
                                // 已存在,更新
                                log.Id = existing.Id;
                                log.PushFrequency = existing.PushFrequency + 1; // 叠加次数
                                log.ModifyDate = DateTime.Now;
                                toUpdate.Add(log);
 
                                // 更新缓存中的 Id
                                string cacheKey = GenerateCacheKey(log.ApiCode, log.RequestParametersHash, log.ResponseParametersHash);
                                if (_cacheLogs.TryGetValue(cacheKey, out var cachedLog))
                                {
                                    cachedLog.Id = existing.Id;
                                }
                            }
                            else
                            {
                                // 不存在,新增
                                toInsert.Add(log);
                            }
                        }
 
                        // 批量新增
                        if (toInsert.Any())
                        {
                            int rows = sugarClient.Fastest<Dt_InterfaceLog>().BulkCopy(toInsert);
 
                            // 获取新增日志的自增ID并更新缓存
                            foreach (var insertedLog in toInsert)
                            {
                                var savedLog = sugarClient.Queryable<Dt_InterfaceLog>()
                                    .First(x => x.RequestParametersHash == insertedLog.RequestParametersHash
                                             && x.ResponseParametersHash == insertedLog.ResponseParametersHash);
                                if (savedLog != null)
                                {
                                    string cacheKey = GenerateCacheKey(insertedLog.ApiCode,
                                        insertedLog.RequestParametersHash, insertedLog.ResponseParametersHash);
                                    if (_cacheLogs.TryGetValue(cacheKey, out var cachedLog))
                                    {
                                        cachedLog.Id = savedLog.Id;
                                    }
                                }
                            }
                        }
 
                        // 批量更新推送次数
                        if (toUpdate.Any())
                        {
                            foreach (var log in toUpdate)
                            {
                                sugarClient.Updateable<Dt_InterfaceLog>()
                                    .SetColumns(it => new Dt_InterfaceLog
                                    {
                                        PushFrequency = log.PushFrequency,
                                        PushState = log.PushState,
                                        Remark = log.Remark,
                                        //CreateDate = log.CreateDate
                                        ModifyDate = log.ModifyDate
                                    })
                                    .Where(it => it.Id == log.Id)
                                    .ExecuteCommand();
                            }
                        }
 
                        sugarClient.CommitTran(); // 提交事务
                    }
                    catch (Exception ex)
                    {
                        sugarClient.RollbackTran(); // 回滚事务
                        Console.WriteLine($"处理日志批次失败: {ex.Message}");
                    }
                    finally
                    {
                        batchList.Clear();
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.ToString());
                }
            }
        }
 
        private static void DequeueToBatchList(List<Dt_InterfaceLog> batchList)
        {
            if (loggerQueueData.TryDequeue(out Dt_InterfaceLog log) && log != null)
            {
                batchList.Add(log);
            }
        }
 
        /// <summary>
        /// 计算字符串的SHA256哈希值
        /// </summary>
        private static string ComputeHash(string input)
        {
            if (string.IsNullOrEmpty(input))
                return string.Empty;
 
            using (SHA256 sha256 = SHA256.Create())
            {
                byte[] bytes = Encoding.UTF8.GetBytes(input);
                byte[] hashBytes = sha256.ComputeHash(bytes);
                return BitConverter.ToString(hashBytes).Replace("-", "").ToLower();
            }
        }
 
        /// <summary>
        /// 生成缓存Key
        /// </summary>
        private static string GenerateCacheKey(string apiCode, string requestHash, string responseHash)
        {
            string today = DateTime.Now.ToString("yyyy-MM-dd");
            return $"{today}_{apiCode}_{requestHash}_{responseHash}";
        }
 
        /// <summary>
        /// 获取或创建日志(用于叠加推送次数)
        /// </summary>
        /// <param name="apiCode">编号</param>
        /// <param name="apiName">名称</param>
        /// <param name="apiAddress">地址</param>
        /// <param name="requestParameters">请求内容</param>
        /// <param name="responseParameters">响应内容</param>
        /// <param name="pushState">状态</param>
        /// <param name="requestorUserIP">请求用户IP</param>
        /// <param name="requestor">请求方</param>
        /// <param name="recipient">接收方</param>
        /// <param name="remark">备注</param>
        /// <returns></returns>
        private static Dt_InterfaceLog GetOrCreateLog(string apiCode, string apiName, string apiAddress,
            string requestParameters, string responseParameters, int pushState,
            string requestorUserIP, string requestor, string recipient, string remark)
        {
            string requestHash = ComputeHash(requestParameters);
            string responseHash = ComputeHash(responseParameters);
            string cacheKey = GenerateCacheKey(apiCode, requestHash, responseHash);
 
            // 尝试从缓存中获取
            if (_cacheLogs.TryGetValue(cacheKey, out Dt_InterfaceLog existingLog))
            {
                // 存在相同参数的日志,增加推送次数
                existingLog.PushFrequency++;
                existingLog.PushState = pushState; // 更新推送状态
                existingLog.Remark = remark; // 更新备注
                //existingLog.CreateDate = DateTime.Now; // 更新时间
                existingLog.ModifyDate = DateTime.Now; // 更新时间
 
                // 返回现有日志(需要更新数据库)
                return existingLog;
            }
            else
            {
                // 不存在,创建新日志
                var newLog = new Dt_InterfaceLog
                {
                    ApiCode = apiCode,
                    ApiName = apiName,
                    ApiAddress = apiAddress,
                    PushFrequency = 1,
                    PushState = pushState,
                    RequestorUserIP = requestorUserIP,
                    Requestor = requestor,
                    RequestParameters = requestParameters,
                    RequestParametersHash = requestHash,
                    Recipient = recipient,
                    ResponseParameters = responseParameters,
                    ResponseParametersHash = responseHash,
                    Remark = remark,
                    Creater = "System",
                    CreateDate = DateTime.Now
                };
 
                // 加入缓存
                _cacheLogs.TryAdd(cacheKey, newLog);
 
                return newLog;
            }
        }
 
        /// <summary>
        /// 添加接口日志(带URL、请求参数、响应参数、开始时间)
        /// </summary>
        public static void Add(string apiCode, string apiName, string apiAddress,
            string requestParameters, string responseParameters,
            int pushState = 0, string recipient = "", string remark = "")
        {
            Dt_InterfaceLog log = null;
            try
            {
                // 获取当前用户
                IUser? user = App.User;
 
                // 获取或创建日志(自动处理叠加)
                log = GetOrCreateLog(apiCode, apiName, apiAddress,
                    requestParameters, responseParameters, pushState,
                    "", user?.UserName ?? "", recipient, remark);
            }
            catch (Exception exception)
            {
                // 如果发生异常,创建基础日志对象
                log = log ?? new Dt_InterfaceLog
                {
                    ApiCode = apiCode,
                    ApiName = apiName,
                    ApiAddress = apiAddress,
                    PushFrequency = 1,
                    PushState = pushState,
                    RequestParameters = requestParameters,
                    ResponseParameters = responseParameters,
                    Creater = "System",
                    Remark = $"记录日志时发生异常:{exception.Message}"
                };
            }
 
            // 添加系统日志
            loggerQueueData.Enqueue(log);
        }
 
        /// <summary>
        /// 添加接口日志(从HttpContext自动获取信息)
        /// </summary>
        /// <param name="requestParameters">请求内容</param>
        /// <param name="responseParameters">响应内容</param>
        /// <param name="apiCode">接口编号</param>
        /// <param name="apiName">接口名称</param>
        /// <param name="pushState">推送状态</param>
        /// <param name="recipient">接收方</param>
        /// <param name="remark">备注</param>
        public static void Add(string requestParameters, string responseParameters, HttpContext context, string apiCode = "", string apiName = "", int pushState = 0, string recipient = "WCS", string remark = "")
        {
            Dt_InterfaceLog log = null;
            try
            {
                // 如果请求方法为OPTIONS,则返回
                if (context.Request.Method == "OPTIONS") return;
 
                #region 数据转换
 
                // 解析外层 JSON
                JObject outerObj = JObject.Parse(requestParameters);
 
                // 获取 BodyData 的值(它是一个字符串)
                requestParameters = outerObj["BodyData"]?.ToString() ?? requestParameters;
 
                // 构建完整的接口地址
                string apiAddress = context.Request.Scheme + "://" + context.Request.Host + context.Request.PathBase + context.Request.Path;
                // 获取客户端IP
                string clientIP = GetClientIP(context)?.Replace("::ffff:", "") ?? "";
                string requestor = "";//请求方
                if (apiAddress.Contains("/api/WMS"))
                {
                    requestor = "WMS";//秒优
                    var wMSReturn = responseParameters.DeserializeObject<WMSReturn>();
                    pushState = wMSReturn.code == 0 ? 1 : 2;
                }
                else if (apiAddress.Contains("/api/KHAGV"))
                {
                    requestor = "海康AGV";
                    var hIKROBOTReturn = responseParameters.DeserializeObject<HIKROBOTReturn>();
                    pushState = hIKROBOTReturn.code == "SUCCESS" ? 1 : 2;
                }
                else if (apiAddress.Contains("/api/KLS"))
                {
                    requestor = "凯乐士AGV";
                    ReturnGALAXIS returnGALAXIS = responseParameters.DeserializeObject<ReturnGALAXIS>();
                    pushState = returnGALAXIS.returnStatus == 0 ? 1 : 2;
                }
                else if (apiAddress.Contains("/api/RGV"))
                {
                    requestor = "四向穿梭车";//旷视河图
                    FOURBOTReturn fOURBOTReturn = responseParameters.DeserializeObject<FOURBOTReturn>();
                    pushState = fOURBOTReturn.returnCode == 0 ? 1 : 2;
                }
                apiCode = apiAddress.Split("/").Last();
                apiName = AppSettings.Configuration.GetValue($"{requestor}:{apiCode}", apiCode);
                #endregion
 
                // 获取或创建日志(自动处理叠加)
                log = GetOrCreateLog(apiCode, apiName, apiAddress,
                    requestParameters, responseParameters, pushState,
                    clientIP, requestor, recipient, remark);
            }
            catch (Exception exception)
            {
                // 如果发生异常,创建基础日志对象
                log = log ?? new Dt_InterfaceLog
                {
                    ApiCode = apiCode,
                    ApiName = apiName,
                    PushFrequency = 1,
                    PushState = pushState,
                    RequestParameters = requestParameters,
                    ResponseParameters = responseParameters,
                    Creater = "System",
                    Remark = $"记录日志时发生异常:{exception.Message}"
                };
            }
 
            // 添加系统日志
            loggerQueueData.Enqueue(log);
        }
 
        /// <summary>
        /// 手动增加推送次数(用于重试场景)
        /// </summary>
        public static void IncrementPushCount(string apiCode, string requestParameters, string responseParameters)
        {
            string requestHash = ComputeHash(requestParameters);
            string responseHash = ComputeHash(responseParameters);
            string cacheKey = GenerateCacheKey(apiCode, requestHash, responseHash);
 
            if (_cacheLogs.TryGetValue(cacheKey, out Dt_InterfaceLog existingLog))
            {
                existingLog.PushFrequency++;
                loggerQueueData.Enqueue(existingLog);
            }
        }
 
        public static string GetClientIP(HttpContext context)
        {
            // 获取请求头中的X-Forwarded-For字段,并将其转换为字符串
            var ip = context.Request.Headers["X-Forwarded-For"].ObjToString();
            // 如果X-Forwarded-For字段为空,则获取远程IP地址
            if (string.IsNullOrEmpty(ip))
            {
                ip = context.Connection.RemoteIpAddress.ObjToString();
            }
 
            // 返回IP地址
            return ip;
        }
    }
}