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.

419 line
18KB

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