You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

348 lines
15KB

  1. using DotNetty.Buffers;
  2. using DotNetty.Transport.Channels;
  3. using Microsoft.Extensions.Logging;
  4. using NearCardAttendance.Common.helper;
  5. using NearCardAttendance.Service.TcpServer.Mapper;
  6. using NearCardAttendance.Service.TcpServer.Protocol;
  7. using Newtonsoft.Json;
  8. using System;
  9. using System.Collections.Generic;
  10. using System.Linq;
  11. using System.Text;
  12. using System.Threading.Channels;
  13. using System.Threading.Tasks;
  14. using NearCardAttendance.Model;
  15. using Microsoft.Extensions.Options;
  16. using TelpoDataService.Util.Entities.GpsCard;
  17. using TelpoDataService.Util;
  18. using TelpoDataService.Util.Clients;
  19. using TelpoDataService.Util.Models;
  20. using TelpoDataService.Util.QueryObjects;
  21. namespace NearCardAttendance.Service.TcpServer.Handler
  22. {
  23. public class RegisterHandler : ChannelHandlerAdapter
  24. {
  25. private readonly ILogger<RegisterHandler> _logger;
  26. private readonly HttpHelper _httpHelper = default!;
  27. //private readonly IDisposable _loggerScope = default!;
  28. private readonly ServiceConfig _configService;
  29. private readonly GpsCardAccessorClient<GpsDeviceConfig> _deviceConfigApiClient;
  30. //private readonly TcpClientsManager _managerTcpClients;
  31. //private readonly ScheduleResendManager _managerScheduleResend;
  32. public RegisterHandler(ILogger<RegisterHandler> logger, GpsCardAccessorClient<GpsDeviceConfig> deviceConfigApiClient,HttpHelper httpHelper, IOptions<ServiceConfig> configService)
  33. {
  34. _logger = logger;
  35. _httpHelper = httpHelper;
  36. _configService = configService.Value;
  37. _deviceConfigApiClient= deviceConfigApiClient;
  38. }
  39. public override void ChannelActive(IChannelHandlerContext context)
  40. {
  41. base.ChannelActive(context); // Pass the event to the next handler
  42. }
  43. public override async void ChannelInactive(IChannelHandlerContext context)
  44. {
  45. try
  46. {
  47. await Task.CompletedTask;
  48. }
  49. catch (Exception ex)
  50. {
  51. _logger.LogInformation($"{nameof(RegisterHandler)} --- {nameof(ChannelInactive)} 出错\n{ex.Message}\n{ex.StackTrace}");
  52. }
  53. }
  54. public override void HandlerRemoved(IChannelHandlerContext context)
  55. {
  56. base.HandlerRemoved(context);
  57. }
  58. public override async void ChannelRead(IChannelHandlerContext context, object message)
  59. {
  60. if (message is ProtocolParser parser)
  61. {
  62. try
  63. {
  64. using (_logger.BeginScope(new Dictionary<string, object> { ["RequestId"] = parser.SeqNo }))
  65. {
  66. //认证
  67. //if (parser.FuncNo.Equals("10"))
  68. //{
  69. // #region 认证业务
  70. // // PHONE_AUTHEN
  71. // ProtocolWrapper phoneAuthWrapper = new(parser.FuncNo, parser.SeqNo, "1");
  72. // await SendToTcpClientAsync(phoneAuthWrapper, context.Channel);
  73. // // ABT_STATUS 延时3秒 给终端同步时间
  74. // await context.Channel.EventLoop.Schedule(async () =>
  75. // {
  76. // ProtocolWrapper abtStatusWrapper = new("82", parser.SeqNo, DateTime.Now.ToString("yyyyMMddHHmmss"));
  77. // await SendToTcpClientAsync(abtStatusWrapper, context.Channel);
  78. // },TimeSpan.FromSeconds(3));
  79. // #endregion
  80. //}
  81. //else if (parser.FuncNo.Equals("82"))
  82. //{
  83. // // 认证成功
  84. // _logger.LogInformation($"认证成功{parser.Data}.");
  85. //}
  86. //// 心跳
  87. //else if (parser.FuncNo.Equals("05"))
  88. //{
  89. // ProtocolWrapper stdtSignRecsWrapper = new(parser.FuncNo, parser.SeqNo,"");
  90. // await SendToTcpClientAsync(stdtSignRecsWrapper, context.Channel);
  91. //} // STDT_SIGN_RECS
  92. //else if (parser.FuncNo.Equals("04"))
  93. //{
  94. // // 回应设备
  95. // ProtocolWrapper stdtSignRecsWrapper = new(parser.FuncNo, parser.SeqNo,"1");
  96. // await SendToTcpClientAsync(stdtSignRecsWrapper, context.Channel);
  97. // // 刷卡考勤信息,需要推送给第三方平台
  98. // //var url = "";
  99. // //await _httpHelper.HttpToPostAsync(url, new object());
  100. //}
  101. //// STDT_SCHOOL_RECS
  102. //else if (parser.FuncNo.Equals("08"))
  103. //{
  104. // _logger.LogInformation($"接受STDT_SCHOOL_RECS 08 成功{parser.Data}.");
  105. // ProtocolWrapper stdtSchoolRecsWrapper = new(parser.FuncNo, parser.SeqNo, "1");
  106. // await SendToTcpClientAsync(stdtSchoolRecsWrapper, context.Channel);
  107. //}
  108. //switch (parser.FuncNo)
  109. //{
  110. // case "10":
  111. // break;
  112. // case "82":
  113. // break;
  114. // default:
  115. // break;
  116. //}
  117. switch (parser.FuncNo)
  118. {
  119. case "10":
  120. await HandleAuthenticationAsync(parser, context.Channel);
  121. break;
  122. case "82":
  123. await HandleAuthenticationSuccessAsync(parser, context.Channel);
  124. break;
  125. case "05":
  126. await SendHeartbeatAsync(parser, context.Channel);
  127. break;
  128. case "04":
  129. await HandleSignRecsAsync(parser, context.Channel);
  130. break;
  131. case "08":
  132. await HandleStdtSchoolRecsAsync(parser, context.Channel);
  133. break;
  134. default:
  135. _logger.LogInformation($"未处理业务,func_no:{parser.FuncNo},seq_no{parser.SeqNo},data:{parser.Data}");
  136. break;
  137. }
  138. }
  139. }
  140. catch (Exception ex)
  141. {
  142. _logger.LogInformation($"{nameof(RegisterHandler)} --- {nameof(ChannelRead)} 处理消息 {parser.SeqNo} 出错\n{ex.Message}\n{ex.StackTrace}");
  143. }
  144. }
  145. }
  146. /// <summary>
  147. /// 发送到TCP客户端
  148. /// </summary>
  149. /// <param name="wrapper"></param>
  150. /// <param name="channel"></param>
  151. private async Task SendToTcpClientAsync(ProtocolWrapper wrapper, IChannel channel)
  152. {
  153. string protocolMsg = wrapper.GenerateProtocolString();
  154. // 发送protocolMsg到tcp客户端
  155. await channel
  156. .WriteAndFlushAsync(Unpooled.WrappedBuffer(Encoding.UTF8.GetBytes(protocolMsg)))
  157. .ContinueWith(Action => {
  158. _logger.LogInformation($"{nameof(RegisterHandler)} -- {nameof(SendToTcpClientAsync)} -- 下发设备内容:\n{protocolMsg}");
  159. });
  160. }
  161. /// <summary>
  162. /// 认证业务
  163. /// </summary>
  164. /// <param name="parser"></param>
  165. /// <param name="channel"></param>
  166. /// <returns></returns>
  167. private async Task HandleAuthenticationAsync(ProtocolParser parser, IChannel channel)
  168. {
  169. ProtocolWrapper phoneAuthWrapper = new(parser.FuncNo, parser.SeqNo, "1");
  170. await SendToTcpClientAsync(phoneAuthWrapper, channel);
  171. await channel.EventLoop.Schedule(async () =>
  172. {
  173. ProtocolWrapper abtStatusWrapper = new("82", parser.SeqNo, DateTime.Now.ToString("yyyyMMddHHmmss"));
  174. await SendToTcpClientAsync(abtStatusWrapper, channel);
  175. }, TimeSpan.FromSeconds(3));
  176. }
  177. /// <summary>
  178. /// 认证业务成功
  179. /// </summary>
  180. /// <param name="parser"></param>
  181. /// <param name="channel"></param>
  182. /// <returns></returns>
  183. private async Task HandleAuthenticationSuccessAsync(ProtocolParser parser, IChannel channel)
  184. {
  185. _logger.LogInformation($"认证成功: func_no:{parser.FuncNo},seq_no{parser.SeqNo},data:{parser.Data}.");
  186. await Task.CompletedTask;
  187. }
  188. /// <summary>
  189. /// CONNECT_STATUS 网络连接状态查询[取值:05] (心跳业务)
  190. /// </summary>
  191. /// <param name="parser"></param>
  192. /// <param name="channel"></param>
  193. /// <returns></returns>
  194. private async Task SendHeartbeatAsync(ProtocolParser parser, IChannel channel)
  195. {
  196. ProtocolWrapper stdtSignRecsWrapper = new(parser.FuncNo, parser.SeqNo, "");
  197. await SendToTcpClientAsync(stdtSignRecsWrapper, channel);
  198. }
  199. /// <summary>
  200. /// STDT_SIGN_RECS 学生签到记录[取值:04]
  201. /// </summary>
  202. /// <param name="parser"></param>
  203. /// <param name="channel"></param>
  204. /// <returns></returns>
  205. private async Task HandleSignRecsAsync(ProtocolParser parser, IChannel channel)
  206. {
  207. ProtocolWrapper stdtSignRecsWrapper = new(parser.FuncNo, parser.SeqNo, "1");
  208. await SendToTcpClientAsync(stdtSignRecsWrapper, channel);
  209. var deviceId = parser.Data.Substring(0, 18).TrimEnd();
  210. var cardId= parser.Data.Substring(18,18).TrimEnd();
  211. var studentId = parser.Data.Substring(36,18).TrimEnd();
  212. var startTime = parser.Data.Substring(54, 14).TrimEnd();
  213. // Push card attendance information to third-party platform
  214. //var url = "https://midplat.xinhualeyu.com/dev-api/user/electronicCardAttendance/receiveTbAttendanceRecord";
  215. #region IC卡转换为IMEI
  216. var config= await _deviceConfigApiClient.GetFirstAsync(new GeneralParam
  217. {
  218. Filters = new List<QueryFilterCondition>
  219. {
  220. new QueryFilterCondition
  221. {
  222. Key=nameof(GpsDeviceConfig.IdNumber),
  223. Value=cardId,
  224. ValueType=QueryValueTypeEnum.String,
  225. Operator=QueryOperatorEnum.Equal
  226. }
  227. },
  228. OrderBys=new List<OrderByCondition>
  229. {
  230. new OrderByCondition
  231. {
  232. IsDesc=true,
  233. Key=nameof(GpsDeviceConfig.LastUpdate),
  234. }
  235. }
  236. }, new RequestHeader { RequestId = parser.SeqNo }).ConfigureAwait(false);
  237. #endregion
  238. if (config!=null)
  239. {
  240. var url = _configService.XinHuaLeYuUrl;
  241. var data = new
  242. {
  243. attendanceStatus = 2, //考勤状态: 0.进 1.出 2.未知
  244. attendanceTime = DateTime.ParseExact(startTime, "yyyyMMddHHmmss", null).ToString("yyyy-MM-dd HH:mm:ss"),
  245. imei = config.Imei
  246. };
  247. var res = await _httpHelper.HttpToPostAsync(url, data);
  248. _logger.LogInformation($"{nameof(HandleSignRecsAsync)} 推送 {JsonConvert.SerializeObject(data)} 结果,{res}");
  249. }
  250. else
  251. {
  252. _logger.LogInformation("找不到IC卡转换为IMEI的对应关系");
  253. }
  254. }
  255. /// <summary>
  256. /// STDT_SCHOOL_RECS 学生进校离校记录[取值:08]
  257. /// </summary>
  258. /// <param name="parser"></param>
  259. /// <param name="channel"></param>
  260. /// <returns></returns>
  261. private async Task HandleStdtSchoolRecsAsync(ProtocolParser parser, IChannel channel)
  262. {
  263. _logger.LogInformation($"Received STDT_SCHOOL_RECS successfully: {parser.Data}.");
  264. ProtocolWrapper stdtSchoolRecsWrapper = new(parser.FuncNo, parser.SeqNo, "1");
  265. await SendToTcpClientAsync(stdtSchoolRecsWrapper, channel);
  266. var deviceId = parser.Data.Substring(0, 18).TrimEnd();
  267. var cardId = parser.Data.Substring(18, 18).TrimEnd();
  268. var studentId = parser.Data.Substring(36, 18).TrimEnd();
  269. var startTime = parser.Data.Substring(54, 14).TrimEnd();
  270. var optType = parser.Data.Substring(68, 1).TrimEnd();
  271. #region IC卡转换为IMEI
  272. var config = await _deviceConfigApiClient.GetFirstAsync(new GeneralParam
  273. {
  274. Filters = new List<QueryFilterCondition>
  275. {
  276. new QueryFilterCondition
  277. {
  278. Key=nameof(GpsDeviceConfig.IdNumber),
  279. Value=cardId,
  280. ValueType=QueryValueTypeEnum.String,
  281. Operator=QueryOperatorEnum.Equal
  282. }
  283. },
  284. OrderBys = new List<OrderByCondition>
  285. {
  286. new OrderByCondition
  287. {
  288. IsDesc=true,
  289. Key=nameof(GpsDeviceConfig.LastUpdate),
  290. }
  291. }
  292. }, new RequestHeader { RequestId = parser.SeqNo }).ConfigureAwait(false);
  293. #endregion
  294. if (config != null)
  295. {
  296. //var url = "https://midplat.xinhualeyu.com/dev-api/user/electronicCardAttendance/receiveTbAttendanceRecord";
  297. var url = _configService.XinHuaLeYuUrl;
  298. var data = new
  299. {
  300. attendanceStatus = int.TryParse(optType, out int type) ? type : 0,
  301. attendanceTime = DateTime.ParseExact(startTime, "yyyyMMddHHmmss", null).ToString("yyyy-MM-dd HH:mm:ss"),
  302. imei = config.Imei
  303. };
  304. var res = await _httpHelper.HttpToPostAsync(url, data);
  305. _logger.LogInformation($"{nameof(HandleStdtSchoolRecsAsync)} 推送 {JsonConvert.SerializeObject(data)} 结果,{res}");
  306. }
  307. else
  308. {
  309. _logger.LogInformation("找不到IC卡转换为IMEI的对应关系");
  310. }
  311. }
  312. }
  313. }