Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

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