|
- using DotNetty.Buffers;
- using DotNetty.Transport.Channels;
- using Microsoft.Extensions.Logging;
- using NearCardAttendance.Common.helper;
- using NearCardAttendance.Service.TcpServer.Mapper;
- using NearCardAttendance.Service.TcpServer.Protocol;
- using Newtonsoft.Json;
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Channels;
- using System.Threading.Tasks;
- using NearCardAttendance.Model;
- using Microsoft.Extensions.Options;
- using TelpoDataService.Util.Entities.GpsCard;
- using TelpoDataService.Util;
- using TelpoDataService.Util.Clients;
- using TelpoDataService.Util.Models;
- using TelpoDataService.Util.QueryObjects;
- using NearCardAttendance.Service.MessageQueue;
- using NearCardAttendance.Service.MessageQueue.Model;
- using Newtonsoft.Json.Linq;
- using DotNetty.Common.Concurrency;
-
- namespace NearCardAttendance.Service.TcpServer.Handler
- {
- public class RegisterHandler : ChannelHandlerAdapter
- {
- private readonly ILogger<RegisterHandler> _logger;
- private readonly HttpHelper _httpHelper = default!;
- private readonly MqProcessLogic _serviceMqProcess;
- //private readonly IDisposable _loggerScope = default!;
- private readonly ServiceConfig _configService;
- private readonly GpsCardAccessorClient<GpsDeviceConfig> _deviceConfigApiClient;
- private readonly ScheduleResendManager _managerScheduleResend;
-
- //private readonly TcpClientsManager _managerTcpClients;
- //private readonly ScheduleResendManager _managerScheduleResend;
-
- public RegisterHandler(ILogger<RegisterHandler> logger, GpsCardAccessorClient<GpsDeviceConfig> deviceConfigApiClient,
- ScheduleResendManager scheduleResendManager, HttpHelper httpHelper, IOptions<ServiceConfig> configService, MqProcessLogic serviceMqProcess)
- {
- _logger = logger;
- _httpHelper = httpHelper;
- _configService = configService.Value;
- _deviceConfigApiClient= deviceConfigApiClient;
- _serviceMqProcess = serviceMqProcess;
- _managerScheduleResend = scheduleResendManager;
- }
-
- public override void ChannelActive(IChannelHandlerContext context)
- {
- base.ChannelActive(context); // Pass the event to the next handler
- }
-
- public override async void ChannelInactive(IChannelHandlerContext context)
- {
- try
- {
- await Task.CompletedTask;
- }
- catch (Exception ex)
- {
- _logger.LogInformation($"{nameof(RegisterHandler)} --- {nameof(ChannelInactive)} 出错\n{ex.Message}\n{ex.StackTrace}");
- }
- }
-
- public override void HandlerRemoved(IChannelHandlerContext context)
- {
- base.HandlerRemoved(context);
- }
-
- public override async void ChannelRead(IChannelHandlerContext context, object message)
- {
- if (message is ProtocolParser parser)
- {
- try
- {
- using (_logger.BeginScope(new Dictionary<string, object> { ["RequestId"] = parser.SeqNo }))
- {
- #region 启动keepalived业务
- // _managerScheduleResend.AddOrUpdate(context.Channel.Id.AsShortText(),(context.Channel.Id.AsShortText(),IScheduledTask));
- // 超过3分钟无任何数据就断链
- _ = _managerScheduleResend.AddOrUpdate(context.Channel.Id.AsShortText(), key =>
- {
- // 创建新的 IScheduledTask
- return context.Channel.EventLoop.Schedule(async () =>
- {
- // var channel = _managerTcpClients.FirstOrDefault(x => x.Key.Equals(imei)).Value;
- //await channel.CloseAsync();
- await context.Channel.CloseAsync();
- _logger.LogInformation($"3分钟超时无数据,主动断链");
- // 执行任务逻辑
- },
- #if DEBUG
- TimeSpan.FromSeconds(99999)
- #else
- TimeSpan.FromSeconds(180)
- #endif
- );
- },
- (oldKey, oldValue) =>
- {
- // 更新现有的 IScheduledTask
- oldValue.Cancel(); // 取消旧的任务
- return context.Channel.EventLoop.Schedule(async () =>
- {
- await context.Channel.CloseAsync();
- _logger.LogInformation($"3分钟超时无数据,主动断链");
- // 执行任务逻辑
- },
- #if DEBUG
- TimeSpan.FromSeconds(99999)
- #else
- TimeSpan.FromSeconds(180)
- #endif
- );
- });
- #endregion
- //认证
- //if (parser.FuncNo.Equals("10"))
- //{
- // #region 认证业务
- // // PHONE_AUTHEN
- // ProtocolWrapper phoneAuthWrapper = new(parser.FuncNo, parser.SeqNo, "1");
- // await SendToTcpClientAsync(phoneAuthWrapper, context.Channel);
-
- // // ABT_STATUS 延时3秒 给终端同步时间
- // await context.Channel.EventLoop.Schedule(async () =>
- // {
- // ProtocolWrapper abtStatusWrapper = new("82", parser.SeqNo, DateTime.Now.ToString("yyyyMMddHHmmss"));
- // await SendToTcpClientAsync(abtStatusWrapper, context.Channel);
- // },TimeSpan.FromSeconds(3));
-
- // #endregion
- //}
- //else if (parser.FuncNo.Equals("82"))
- //{
- // // 认证成功
- // _logger.LogInformation($"认证成功{parser.Data}.");
- //}
- //// 心跳
- //else if (parser.FuncNo.Equals("05"))
- //{
- // ProtocolWrapper stdtSignRecsWrapper = new(parser.FuncNo, parser.SeqNo,"");
- // await SendToTcpClientAsync(stdtSignRecsWrapper, context.Channel);
- //} // STDT_SIGN_RECS
- //else if (parser.FuncNo.Equals("04"))
- //{
- // // 回应设备
- // ProtocolWrapper stdtSignRecsWrapper = new(parser.FuncNo, parser.SeqNo,"1");
- // await SendToTcpClientAsync(stdtSignRecsWrapper, context.Channel);
- // // 刷卡考勤信息,需要推送给第三方平台
-
- // //var url = "";
-
- // //await _httpHelper.HttpToPostAsync(url, new object());
- //}
- //// STDT_SCHOOL_RECS
- //else if (parser.FuncNo.Equals("08"))
- //{
- // _logger.LogInformation($"接受STDT_SCHOOL_RECS 08 成功{parser.Data}.");
- // ProtocolWrapper stdtSchoolRecsWrapper = new(parser.FuncNo, parser.SeqNo, "1");
- // await SendToTcpClientAsync(stdtSchoolRecsWrapper, context.Channel);
- //}
-
-
- //switch (parser.FuncNo)
- //{
- // case "10":
- // break;
-
- // case "82":
- // break;
- // default:
- // break;
- //}
-
- switch (parser.FuncNo)
- {
- case "10":
- await HandleAuthenticationAsync(parser, context.Channel);
- break;
- case "82":
- await HandleAuthenticationSuccessAsync(parser, context.Channel);
- break;
- case "05":
- //ChannelKeepAlived(context, parser);
- await SendHeartbeatAsync(parser, context.Channel);
- break;
- case "04":
- await HandleSignRecsAsync(parser, context.Channel);
- break;
- case "08":
- await HandleStdtSchoolRecsAsync(parser, context.Channel);
- break;
- default:
- _logger.LogInformation($"未处理业务,func_no:{parser.FuncNo},seq_no{parser.SeqNo},data:{parser.Data}");
- break;
- }
- }
- }
- catch (Exception ex)
- {
- _logger.LogInformation($"{nameof(RegisterHandler)} --- {nameof(ChannelRead)} 处理消息 {parser.SeqNo} 出错\n{ex.Message}\n{ex.StackTrace}");
-
- }
-
- }
- }
-
- /// <summary>
- /// 发送到TCP客户端
- /// </summary>
- /// <param name="wrapper"></param>
- /// <param name="channel"></param>
- private async Task SendToTcpClientAsync(ProtocolWrapper wrapper, IChannel channel)
- {
-
- string protocolMsg = wrapper.GenerateProtocolString();
-
- // 发送protocolMsg到tcp客户端
- await channel
- .WriteAndFlushAsync(Unpooled.WrappedBuffer(Encoding.UTF8.GetBytes(protocolMsg)))
- .ContinueWith(Action => {
- _logger.LogInformation($"{nameof(RegisterHandler)} -- {nameof(SendToTcpClientAsync)} -- 下发设备内容:\n{protocolMsg}");
- });
- }
-
- /// <summary>
- /// 认证业务
- /// </summary>
- /// <param name="parser"></param>
- /// <param name="channel"></param>
- /// <returns></returns>
- private async Task HandleAuthenticationAsync(ProtocolParser parser, IChannel channel)
- {
- ProtocolWrapper phoneAuthWrapper = new(parser.FuncNo, parser.SeqNo, "1");
- await SendToTcpClientAsync(phoneAuthWrapper, channel);
-
- await channel.EventLoop.Schedule(async () =>
- {
- ProtocolWrapper abtStatusWrapper = new("82", parser.SeqNo, DateTime.Now.ToString("yyyyMMddHHmmss"));
- await SendToTcpClientAsync(abtStatusWrapper, channel);
- }, TimeSpan.FromSeconds(3));
- }
-
- /// <summary>
- /// 认证业务成功
- /// </summary>
- /// <param name="parser"></param>
- /// <param name="channel"></param>
- /// <returns></returns>
- private async Task HandleAuthenticationSuccessAsync(ProtocolParser parser, IChannel channel)
- {
- _logger.LogInformation($"认证成功: func_no:{parser.FuncNo},seq_no{parser.SeqNo},data:{parser.Data}.");
- await Task.CompletedTask;
-
- }
-
- /// <summary>
- /// CONNECT_STATUS 网络连接状态查询[取值:05] (心跳业务)
- /// </summary>
- /// <param name="parser"></param>
- /// <param name="channel"></param>
- /// <returns></returns>
- private async Task SendHeartbeatAsync(ProtocolParser parser, IChannel channel)
- {
-
- ProtocolWrapper stdtSignRecsWrapper = new(parser.FuncNo, parser.SeqNo, "");
- await SendToTcpClientAsync(stdtSignRecsWrapper, channel);
-
- #region 启动keepalived业务
- // // _managerScheduleResend.AddOrUpdate(context.Channel.Id.AsShortText(),(context.Channel.Id.AsShortText(),IScheduledTask));
- // // 超过3分钟无任何数据就断链
- // _ = _managerScheduleResend.AddOrUpdate(channel.Id.AsShortText(), key =>
- // {
- // // 创建新的 IScheduledTask
- // return channel.EventLoop.Schedule(async () =>
- // {
- // // var channel = _managerTcpClients.FirstOrDefault(x => x.Key.Equals(imei)).Value;
- // //await channel.CloseAsync();
- // await channel.CloseAsync();
- // // 执行任务逻辑
- // },
- //#if DEBUG
- // TimeSpan.FromSeconds(999999)
- //#else
- // TimeSpan.FromSeconds(180)
- //#endif
- // );
- // },
- // (oldKey, oldValue) =>
- // {
- // // 更新现有的 IScheduledTask
- // oldValue.Cancel(); // 取消旧的任务
- // return channel.EventLoop.Schedule(async () =>
- // {
- // await channel.CloseAsync();
- // // 执行任务逻辑
- // },
- //#if DEBUG
- // TimeSpan.FromSeconds(999999)
- //#else
- // TimeSpan.FromSeconds(180)
- //#endif
-
- // );
- // });
- #endregion
- }
-
- private void ChannelKeepAlived(IChannelHandlerContext context, ProtocolParser parser)
- {
- if (_managerScheduleResend.TryRemove(context.Channel.Id.AsShortText(), out IScheduledTask? scheduledTask))
- {
- scheduledTask.Cancel();
- scheduledTask = null;
- _logger.LogInformation($"{nameof(ChannelKeepAlived)} --{parser.SeqNo} -- {context.Channel} 活跃...");
- }
- else
- {
- Console.WriteLine("No Task");
- }
- }
- /// <summary>
- /// STDT_SIGN_RECS 学生签到记录[取值:04]
- /// </summary>
- /// <param name="parser"></param>
- /// <param name="channel"></param>
- /// <returns></returns>
- private async Task HandleSignRecsAsync(ProtocolParser parser, IChannel channel)
- {
- ProtocolWrapper stdtSignRecsWrapper = new(parser.FuncNo, parser.SeqNo, "1");
- await SendToTcpClientAsync(stdtSignRecsWrapper, channel);
-
- var deviceId = parser.Data.Substring(0, 18).TrimEnd();
- var cardId= parser.Data.Substring(18,18).TrimEnd();
- var studentId = parser.Data.Substring(36,18).TrimEnd();
- var startTime = parser.Data.Substring(54, 14).TrimEnd();
- // Push card attendance information to third-party platform
- //var url = "https://midplat.xinhualeyu.com/dev-api/user/electronicCardAttendance/receiveTbAttendanceRecord";
-
- #region IC卡转换为IMEI
- var config= await _deviceConfigApiClient.GetFirstAsync(new GeneralParam
- {
- Filters = new List<QueryFilterCondition>
- {
- new QueryFilterCondition
- {
- Key=nameof(GpsDeviceConfig.IdNumber),
- Value=cardId,
- ValueType=QueryValueTypeEnum.String,
- Operator=QueryOperatorEnum.Equal
- }
- },
- OrderBys=new List<OrderByCondition>
- {
- new OrderByCondition
- {
- IsDesc=true,
- Key=nameof(GpsDeviceConfig.LastUpdate),
- }
- }
- }, new RequestHeader { RequestId = parser.SeqNo }).ConfigureAwait(false);
-
- #endregion
-
- if (config!=null)
- {
- //var url = _configService.XinHuaLeYuUrl;
- var url = $"{_configService.XinHuaLeYuUrl}/user/electronicCardAttendance/receiveTbAttendanceRecord";
- var data = new
- {
- attendanceStatus = 2, //考勤状态: 0.进 1.出 2.未知
- attendanceTime = DateTime.ParseExact(startTime, "yyyyMMddHHmmss", null).ToString("yyyy-MM-dd HH:mm:ss"),
- imei = config.Imei
- };
- var eventData = new EventData
- {
- TopicName = "topics.storage.near_card_attendance",
- MessageId = $"{config.Imei}-{parser.SeqNo}-{Guid.NewGuid().ToString("N")[^4..]}",
- IMEI = config.Imei,
- Content = JsonConvert.SerializeObject(data)
- };
- var res = await _httpHelper.HttpToPostAsync(url, data);
-
- if (!string.IsNullOrEmpty(res))
- {
- JObject resObj = (JObject)JsonConvert.DeserializeObject(res!)!;
- if ((bool)resObj["success"]!)
- {
- _logger.LogInformation($"{nameof(HandleSignRecsAsync)} 推送 {JsonConvert.SerializeObject(data)} 结果,{res}");
- }
- else
- {
- await _serviceMqProcess.ProcessIMEIEventMessageAsync(eventData);
- _logger.LogInformation($"HTTP 响应业务失败 {res},{nameof(HandleSignRecsAsync)} 推送 {JsonConvert.SerializeObject(eventData)}");
-
- }
- }
- else
- {
- await _serviceMqProcess.ProcessIMEIEventMessageAsync(eventData);
- _logger.LogInformation($"HTTP 响应超时,{nameof(HandleSignRecsAsync)} 推送 {JsonConvert.SerializeObject(eventData)}");
-
- }
- }
- else
- {
- _logger.LogInformation("找不到IC卡转换为IMEI的对应关系");
- }
-
- }
-
- /// <summary>
- /// STDT_SCHOOL_RECS 学生进校离校记录[取值:08]
- /// </summary>
- /// <param name="parser"></param>
- /// <param name="channel"></param>
- /// <returns></returns>
- private async Task HandleStdtSchoolRecsAsync(ProtocolParser parser, IChannel channel)
- {
- _logger.LogInformation($"Received STDT_SCHOOL_RECS successfully: {parser.Data}.");
- ProtocolWrapper stdtSchoolRecsWrapper = new(parser.FuncNo, parser.SeqNo, "1");
- await SendToTcpClientAsync(stdtSchoolRecsWrapper, channel);
-
- var deviceId = parser.Data.Substring(0, 18).TrimEnd();
- var cardId = parser.Data.Substring(18, 18).TrimEnd();
- var studentId = parser.Data.Substring(36, 18).TrimEnd();
- var startTime = parser.Data.Substring(54, 14).TrimEnd();
- var optType = parser.Data.Substring(68, 1).TrimEnd();
- #region IC卡转换为IMEI
- var config = await _deviceConfigApiClient.GetFirstAsync(new GeneralParam
- {
- Filters = new List<QueryFilterCondition>
- {
- new QueryFilterCondition
- {
- Key=nameof(GpsDeviceConfig.IdNumber),
- Value=cardId,
- ValueType=QueryValueTypeEnum.String,
- Operator=QueryOperatorEnum.Equal
- }
- },
- OrderBys = new List<OrderByCondition>
- {
- new OrderByCondition
- {
- IsDesc=true,
- Key=nameof(GpsDeviceConfig.LastUpdate),
- }
- }
- }, new RequestHeader { RequestId = parser.SeqNo }).ConfigureAwait(false);
-
- #endregion
- if (config != null)
- {
- //var url = "https://midplat.xinhualeyu.com/dev-api/user/electronicCardAttendance/receiveTbAttendanceRecord";
- #if DEBUG
- var url = $"{_configService.XinHuaLeYuUrl}/user/electronicCardAttendance/receiveTbAttendanceRecord1";
-
- #else
- var url = $"{_configService.XinHuaLeYuUrl}/user/electronicCardAttendance/receiveTbAttendanceRecord";
-
- #endif
- var data = new
- {
- attendanceStatus = int.TryParse(optType, out int type) ? type : 0,
- attendanceTime = DateTime.ParseExact(startTime, "yyyyMMddHHmmss", null).ToString("yyyy-MM-dd HH:mm:ss"),
- imei = config.Imei
- };
- var eventData = new EventData
- {
- TopicName = "topics.storage.near_card_attendance",
- MessageId = $"{config.Imei}-{parser.SeqNo}-{Guid.NewGuid().ToString("N")[^4..]}",
- IMEI = config.Imei,
- Content = JsonConvert.SerializeObject(data)
- };
-
- var res = await _httpHelper.HttpToPostAsync(url, data);
-
- if (!string.IsNullOrEmpty(res))
- {
- JObject resObj = (JObject)JsonConvert.DeserializeObject(res!)!;
- if ((bool)resObj["success"]!)
- {
- _logger.LogInformation($"{nameof(HandleStdtSchoolRecsAsync)} 推送 {JsonConvert.SerializeObject(data)} 结果,{res}");
- }
- else
- {
- await _serviceMqProcess.ProcessIMEIEventMessageAsync(eventData);
- _logger.LogInformation($"HTTP 响应业务失败 {res},{nameof(HandleStdtSchoolRecsAsync)} 推送 {JsonConvert.SerializeObject(eventData)}");
-
- }
- }
- else
- {
-
- await _serviceMqProcess.ProcessIMEIEventMessageAsync(eventData);
- _logger.LogInformation($"HTTP 响应超时,{nameof(HandleStdtSchoolRecsAsync)} 推送 {JsonConvert.SerializeObject(eventData)}");
-
- }
-
-
-
- }
- else
- {
- _logger.LogInformation("找不到IC卡转换为IMEI的对应关系");
- }
- }
- }
- }
|