Sfoglia il codice sorgente

新增定位业务

master
杨雷 6 mesi fa
parent
commit
aa60eb7663
32 ha cambiato i file con 2363 aggiunte e 14 eliminazioni
  1. +460
    -0
      TelpoPush.Position.Worker/Common/HttpHelperAsync.cs
  2. +137
    -0
      TelpoPush.Position.Worker/Common/LimitedConcurrencyLevelTaskScheduler.cs
  3. +33
    -0
      TelpoPush.Position.Worker/Common/TimeHelper.cs
  4. +40
    -0
      TelpoPush.Position.Worker/Common/Utils.cs
  5. +62
    -0
      TelpoPush.Position.Worker/Handlers/KafkaSubscribe.cs
  6. +346
    -0
      TelpoPush.Position.Worker/Handlers/PositionProcess.cs
  7. +19
    -0
      TelpoPush.Position.Worker/Models/CacheTemplates/DeviceInfoModel.cs
  8. +35
    -0
      TelpoPush.Position.Worker/Models/CacheTemplates/ManufactorPushSettingInfoModel.cs
  9. +42
    -0
      TelpoPush.Position.Worker/Models/CacheTemplates/PersonInfoModel.cs
  10. +19
    -0
      TelpoPush.Position.Worker/Models/Config/PositionConfig.cs
  11. +94
    -0
      TelpoPush.Position.Worker/Models/Config/RedisConfig.cs
  12. +32
    -0
      TelpoPush.Position.Worker/Models/Config/ServiceConfig.cs
  13. +16
    -0
      TelpoPush.Position.Worker/Models/Enum/HeadersDto.cs
  14. +19
    -0
      TelpoPush.Position.Worker/Models/Enum/MqDataTopic.cs
  15. +156
    -0
      TelpoPush.Position.Worker/Models/Enum/MqDataType.cs
  16. +26
    -0
      TelpoPush.Position.Worker/Models/Enum/MqHeader.cs
  17. +17
    -0
      TelpoPush.Position.Worker/Models/MqTemplates/BaseModel.cs
  18. +29
    -0
      TelpoPush.Position.Worker/Models/MqTemplates/MqPositionTemplate.cs
  19. +35
    -0
      TelpoPush.Position.Worker/Models/PushTemplates/PushFencePositionTemplate.cs
  20. +73
    -0
      TelpoPush.Position.Worker/Models/PushTemplates/PushPositionTemplate.cs
  21. +13
    -9
      TelpoPush.Position.Worker/Program.cs
  22. +82
    -0
      TelpoPush.Position.Worker/Service/Cache/MemoryCacheUtil.cs
  23. +208
    -0
      TelpoPush.Position.Worker/Service/Cache/RedisUtil.cs
  24. +39
    -0
      TelpoPush.Position.Worker/Service/Cache/SqlMapper.cs
  25. +9
    -0
      TelpoPush.Position.Worker/Service/Mq/IKafkaService.cs
  26. +15
    -0
      TelpoPush.Position.Worker/Service/Mq/KafkaHeader.cs
  27. +135
    -0
      TelpoPush.Position.Worker/Service/Mq/KafkaService.cs
  28. +77
    -0
      TelpoPush.Position.Worker/Service/Mq/MessageProducer.cs
  29. +70
    -0
      TelpoPush.Position.Worker/Service/Mq/MqProcessMessage.cs
  30. +15
    -0
      TelpoPush.Position.Worker/Service/Mq/TopicModel.cs
  31. +4
    -0
      TelpoPush.Position.Worker/TelpoPush.Position.Worker.csproj
  32. +6
    -5
      TelpoPush.Position.Worker/Worker.cs

+ 460
- 0
TelpoPush.Position.Worker/Common/HttpHelperAsync.cs Vedi File

@@ -0,0 +1,460 @@
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;

namespace TelpoPush.Position.Worker.Common
{
/// <summary>
/// HTTP帮助类
/// </summary>
public class HttpHelperAsync
{
private IHttpClientFactory _httpClientFactory;
private readonly ILogger<HttpHelperAsync> _logger;
public HttpHelperAsync(IHttpClientFactory httpClientFactory, ILogger<HttpHelperAsync> logger)
{
_httpClientFactory = httpClientFactory;
_logger = logger;
}


#region 异步
/// <summary>
/// 发起POST异步请求表单
/// </summary>
/// <param name="url">请求地址</param>
/// <param name="body">POST提交的内容</param>
/// <param name="bodyMediaType">POST内容的媒体类型,如:application/xml、application/json</param>
/// <param name="responseContentType">HTTP响应上的content-type内容头的值,如:application/xml、application/json、application/text、application/x-www-form-urlencoded等</param>
/// <param name="headers">请求头信息</param>
/// <param name="timeOut">请求超时时间,单位秒</param>
/// <returns>返回string</returns>
public async Task<string> PostFormAsync(string url, MultipartFormDataContent content,
Dictionary<string, string> headers = null,
int timeOut = 5)
{
try
{
var hostName = GetHostName(url);
using (HttpClient client = _httpClientFactory.CreateClient(hostName))
{
client.Timeout = TimeSpan.FromSeconds(timeOut);
if (headers?.Count > 0)
{
foreach (string key in headers.Keys)
{
client.DefaultRequestHeaders.Add(key, headers[key]);
}
}
content.Headers.Add("ContentType", "multipart/form-data");//声明头部
using (HttpResponseMessage response = await client.PostAsync(url, content))
{
return JsonConvert.SerializeObject(new { response.IsSuccessStatusCode, response.StatusCode });
//if (response.IsSuccessStatusCode)
//{
// string responseString = await response.Content.ReadAsStringAsync();
// return responseString;
//}
//else
//{
// return string.Empty;
//}
}
}
}
catch (Exception ex)
{
return $"推送完成:请求响应超过{timeOut}秒,异常 {ex.Message}";
}
}



/// <summary>
/// 发起GET异步请求
/// </summary>
/// <typeparam name="T">返回类型</typeparam>
/// <param name="url">请求地址</param>
/// <param name="headers">请求头信息</param>
/// <param name="timeOut">请求超时时间,单位秒</param>
/// <returns>返回string</returns>
public async Task<string> GetAsync(string url, Dictionary<string, string> headers = null, int timeOut = 30)
{
var hostName = GetHostName(url);
using (HttpClient client = _httpClientFactory.CreateClient(hostName))
{
client.Timeout = TimeSpan.FromSeconds(timeOut);
if (headers?.Count > 0)
{
foreach (string key in headers.Keys)
{
client.DefaultRequestHeaders.Add(key, headers[key]);
}
}
using (HttpResponseMessage response = await client.GetAsync(url))
{
if (response.IsSuccessStatusCode)
{
string responseString = await response.Content.ReadAsStringAsync();
return responseString;
}
else
{
return string.Empty;
}
}
}
}


/// <summary>
/// 发起POST异步请求
/// </summary>
/// <param name="url">请求地址</param>
/// <param name="body">POST提交的内容</param>
/// <param name="bodyMediaType">POST内容的媒体类型,如:application/xml、application/json</param>
/// <param name="responseContentType">HTTP响应上的content-type内容头的值,如:application/xml、application/json、application/text、application/x-www-form-urlencoded等</param>
/// <param name="headers">请求头信息</param>
/// <param name="timeOut">请求超时时间,单位秒</param>
/// <returns>返回string</returns>
public async Task<string> PostAsync(string url, string body,
Dictionary<string, string> headers = null,
int timeOut = 30,
string bodyMediaType = "application/json",
string responseContentType = "application/json;charset=utf-8")
{

//content.Headers.ContentType = new MediaTypeHeaderValue("application/json");

//var clientHandler = new HttpClientHandler
//{
// ServerCertificateCustomValidationCallback = (message, certificate2, arg3, arg4) => true
//};
//using (var client = new HttpClient(clientHandler))
//{
// client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

try
{
var hostName = GetHostName(url);
using (HttpClient client = _httpClientFactory.CreateClient(hostName))
{
client.Timeout = TimeSpan.FromSeconds(timeOut);
if (headers?.Count > 0)
{
foreach (string key in headers.Keys)
{
client.DefaultRequestHeaders.Add(key, headers[key]);
}
}
StringContent content = new StringContent(body, System.Text.Encoding.UTF8, mediaType: bodyMediaType);
if (!string.IsNullOrWhiteSpace(responseContentType))
{
content.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse(responseContentType);
}
using (HttpResponseMessage response = await client.PostAsync(url, content))
{
if (response.IsSuccessStatusCode)
{
string responseString = await response.Content.ReadAsStringAsync();
return responseString;
}
else
return $"请求异常:{response.IsSuccessStatusCode},{response.StatusCode}";
}
}
}
catch(Exception ex)
{
return $"请求异常:{ex.Message}";
}
}

/// <summary>
/// 发起POST异步请求
/// </summary>
/// <param name="url">请求地址</param>
/// <param name="body">POST提交的内容</param>
/// <param name="bodyMediaType">POST内容的媒体类型,如:application/xml、application/json</param>
/// <param name="responseContentType">HTTP响应上的content-type内容头的值,如:application/xml、application/json、application/text、application/x-www-form-urlencoded等</param>
/// <param name="headers">请求头信息</param>
/// <param name="timeOut">请求超时时间,单位秒</param>
/// <returns>返回string</returns>
public async Task<string> PutAsync(string url, string body,
string bodyMediaType = null,
string responseContentType = null,
Dictionary<string, string> headers = null,
int timeOut = 30)
{
var hostName = GetHostName(url);
using (HttpClient client = _httpClientFactory.CreateClient(hostName))
{
client.Timeout = TimeSpan.FromSeconds(timeOut);
if (headers?.Count > 0)
{
foreach (string key in headers.Keys)
{
client.DefaultRequestHeaders.Add(key, headers[key]);
}
}
StringContent content = new StringContent(body, System.Text.Encoding.UTF8, mediaType: bodyMediaType);
if (!string.IsNullOrWhiteSpace(responseContentType))
{
content.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse(responseContentType);
}
using (HttpResponseMessage response = await client.PutAsync(url, content))
{
if (response.IsSuccessStatusCode)
{
string responseString = await response.Content.ReadAsStringAsync();
return responseString;
}
else
{
return string.Empty;
}
}
}
}

/// <summary>
/// 发起GET异步请求
/// </summary>
/// <typeparam name="T">返回类型</typeparam>
/// <param name="url">请求地址</param>
/// <param name="headers">请求头信息</param>
/// <param name="timeOut">请求超时时间,单位秒</param>
/// <returns>返回string</returns>
public async Task<string> DeleteAsync(string url, Dictionary<string, string> headers = null, int timeOut = 30)
{
var hostName = GetHostName(url);
using (HttpClient client = _httpClientFactory.CreateClient(hostName))
{
client.Timeout = TimeSpan.FromSeconds(timeOut);
if (headers?.Count > 0)
{
foreach (string key in headers.Keys)
{
client.DefaultRequestHeaders.Add(key, headers[key]);
}
}
using (HttpResponseMessage response = await client.DeleteAsync(url))
{
if (response.IsSuccessStatusCode)
{
string responseString = await response.Content.ReadAsStringAsync();
return responseString;
}
else
{
return string.Empty;
}
}
}
}

/// <summary>
/// 发起GET异步请求
/// </summary>
/// <typeparam name="T">返回类型</typeparam>
/// <param name="url">请求地址</param>
/// <param name="headers">请求头信息</param>
/// <param name="timeOut">请求超时时间,单位秒</param>
/// <returns>返回T</returns>
public async Task<T> GetAsync<T>(string url, Dictionary<string, string> headers = null, int timeOut = 30) where T : new()
{
string responseString = await GetAsync(url, headers, timeOut);
if (!string.IsNullOrWhiteSpace(responseString))
{
return JsonConvert.DeserializeObject<T>(responseString);
}
else
{
return default(T);
}
}


/// <summary>
/// 发起POST异步请求
/// </summary>
/// <typeparam name="T">返回类型</typeparam>
/// <param name="url">请求地址</param>
/// <param name="body">POST提交的内容</param>
/// <param name="bodyMediaType">POST内容的媒体类型,如:application/xml、application/json</param>
/// <param name="responseContentType">HTTP响应上的content-type内容头的值,如:application/xml、application/json、application/text、application/x-www-form-urlencoded等</param>
/// <param name="headers">请求头信息</param>
/// <param name="timeOut">请求超时时间,单位秒</param>
/// <returns>返回T</returns>
public async Task<T> PostAsync<T>(string url, string body,
Dictionary<string, string> headers = null,
int timeOut = 30,
string bodyMediaType = "application/json",
string responseContentType = "application/json;charset=utf-8"
) where T : new()
{
string responseString = await PostAsync(url, body, headers, timeOut, bodyMediaType, responseContentType);
if (!string.IsNullOrWhiteSpace(responseString))
{
return JsonConvert.DeserializeObject<T>(responseString);
}
else
{
return default(T);
}
}

/// <summary>
/// 发起PUT异步请求
/// </summary>
/// <typeparam name="T">返回类型</typeparam>
/// <param name="url">请求地址</param>
/// <param name="body">POST提交的内容</param>
/// <param name="bodyMediaType">POST内容的媒体类型,如:application/xml、application/json</param>
/// <param name="responseContentType">HTTP响应上的content-type内容头的值,如:application/xml、application/json、application/text、application/x-www-form-urlencoded等</param>
/// <param name="headers">请求头信息</param>
/// <param name="timeOut">请求超时时间,单位秒</param>
/// <returns>返回T</returns>
public async Task<T> PutAsync<T>(string url, string body,
string bodyMediaType = null,
string responseContentType = null,
Dictionary<string, string> headers = null,
int timeOut = 30) where T : new()
{
string responseString = await PutAsync(url, body, bodyMediaType, responseContentType, headers, timeOut);
if (!string.IsNullOrWhiteSpace(responseString))
{
return JsonConvert.DeserializeObject<T>(responseString);
}
else
{
return default(T);
}
}

/// <summary>
/// 发起DELETE异步请求
/// </summary>
/// <typeparam name="T">返回类型</typeparam>
/// <param name="url">请求地址</param>
/// <param name="headers">请求头信息</param>
/// <param name="timeOut">请求超时时间,单位秒</param>
/// <returns>返回T</returns>
public async Task<T> DeleteAsync<T>(string url, Dictionary<string, string> headers = null, int timeOut = 30) where T : new()
{
string responseString = await DeleteAsync(url, headers, timeOut);
if (!string.IsNullOrWhiteSpace(responseString))
{
return JsonConvert.DeserializeObject<T>(responseString);
}
else
{
return default(T);
}
}
#region 私有函数

/// <summary>
/// 获取请求的主机名
/// </summary>
/// <param name="url"></param>
/// <returns></returns>
private static string GetHostName(string url)
{
if (!string.IsNullOrWhiteSpace(url))
{
return url.Replace("https://", "").Replace("http://", "").Split('/')[0];
}
else
{
return "AnyHost";
}
}

#endregion


#endregion

#region 同步

/// <summary>
/// 发起GET同步请求
/// </summary>
/// <param name="url"></param>
/// <param name="headers"></param>
/// <param name="contentType"></param>
/// <returns></returns>
///


public string HttpGet(string url, Dictionary<string, string> headers = null, string contentType = "application/json;charset=utf-8")
{
if (string.IsNullOrEmpty(url)) return "";
try
{
using (HttpClient client = new HttpClient())
{
if (contentType != null)
client.DefaultRequestHeaders.Add("ContentType", contentType);
if (headers != null)
{
foreach (var header in headers)
client.DefaultRequestHeaders.Add(header.Key, header.Value);
}
HttpResponseMessage response = client.GetAsync(url).Result;
return response.Content.ReadAsStringAsync().Result;
}
}
catch (Exception ex)
{
_logger.LogDebug($"HttpGet/URL:{url},headers:{JsonConvert.SerializeObject(headers)},异常:{ex.Message}|{ex.Source}|{ex.StackTrace}");
}
return "";
}
/// <summary>
/// 发起POST同步请求
/// </summary>
/// <param name="url"></param>
/// <param name="postData"></param>
/// <param name="contentType">application/xml、application/json、application/text、application/x-www-form-urlencoded</param>
/// <param name="headers">填充消息头</param>
/// <returns></returns>
public string HttpPost(string url, string postData = null, Dictionary<string, string> headers = null, string contentType = "application/json")
{
if (string.IsNullOrEmpty(url)) return "";
try
{
postData = postData ?? "";
using (HttpClient client = new HttpClient())
{
if (headers != null)
{
foreach (var header in headers)
client.DefaultRequestHeaders.Add(header.Key, header.Value);
}
using (HttpContent httpContent = new StringContent(postData, Encoding.UTF8))
{
if (contentType != null)
httpContent.Headers.ContentType = new MediaTypeHeaderValue(contentType);

HttpResponseMessage response = client.PostAsync(url, httpContent).Result;
return response.Content.ReadAsStringAsync().Result;
}
}
}
catch (Exception ex){
_logger.LogDebug($"HttpPost/URL:{url},postStr:{postData},headers:{JsonConvert.SerializeObject(headers)},异常:{ex.Message}|{ex.Source}|{ex.StackTrace}");
}
return "";
}

#endregion
}
}

+ 137
- 0
TelpoPush.Position.Worker/Common/LimitedConcurrencyLevelTaskScheduler.cs Vedi File

@@ -0,0 +1,137 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace TelpoPush.Position.Worker.Common
{
/// <summary>
/// Provides a task scheduler that ensures a maximum concurrency level while
/// running on top of the ThreadPool.
/// </summary>
public class LimitedConcurrencyLevelTaskScheduler : TaskScheduler
{
/// <summary>Whether the current thread is processing work items.</summary>
[ThreadStatic]
private static bool _currentThreadIsProcessingItems;
/// <summary>The list of tasks to be executed.</summary>
private readonly LinkedList<Task> _tasks = new LinkedList<Task>(); // protected by lock(_tasks)
/// <summary>The maximum concurrency level allowed by this scheduler.</summary>
private readonly int _maxDegreeOfParallelism;
/// <summary>Whether the scheduler is currently processing work items.</summary>
private int _delegatesQueuedOrRunning = 0; // protected by lock(_tasks)

/// <summary>
/// Initializes an instance of the LimitedConcurrencyLevelTaskScheduler class with the
/// specified degree of parallelism.
/// </summary>
/// <param name="maxDegreeOfParallelism">The maximum degree of parallelism provided by this scheduler.</param>
public LimitedConcurrencyLevelTaskScheduler(int maxDegreeOfParallelism)
{
if (maxDegreeOfParallelism < 1) throw new ArgumentOutOfRangeException("maxDegreeOfParallelism");
_maxDegreeOfParallelism = maxDegreeOfParallelism;
}

/// <summary>Queues a task to the scheduler.</summary>
/// <param name="task">The task to be queued.</param>
protected sealed override void QueueTask(Task task)
{
// Add the task to the list of tasks to be processed. If there aren't enough
// delegates currently queued or running to process tasks, schedule another.
lock (_tasks)
{
_tasks.AddLast(task);
if (_delegatesQueuedOrRunning < _maxDegreeOfParallelism)
{
++_delegatesQueuedOrRunning;
NotifyThreadPoolOfPendingWork();
}
}
}

/// <summary>
/// Informs the ThreadPool that there's work to be executed for this scheduler.
/// </summary>
private void NotifyThreadPoolOfPendingWork()
{
ThreadPool.UnsafeQueueUserWorkItem(_ =>
{
// Note that the current thread is now processing work items.
// This is necessary to enable inlining of tasks into this thread.
_currentThreadIsProcessingItems = true;
try
{
// Process all available items in the queue.
while (true)
{
Task item;
lock (_tasks)
{
// When there are no more items to be processed,
// note that we're done processing, and get out.
if (_tasks.Count == 0)
{
--_delegatesQueuedOrRunning;
break;
}

// Get the next item from the queue
item = _tasks.First.Value;
_tasks.RemoveFirst();
}

// Execute the task we pulled out of the queue
base.TryExecuteTask(item);
}
}
// We're done processing items on the current thread
finally { _currentThreadIsProcessingItems = false; }
}, null);
}

/// <summary>Attempts to execute the specified task on the current thread.</summary>
/// <param name="task">The task to be executed.</param>
/// <param name="taskWasPreviouslyQueued"></param>
/// <returns>Whether the task could be executed on the current thread.</returns>
protected sealed override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
{
// If this thread isn't already processing a task, we don't support inlining
if (!_currentThreadIsProcessingItems) return false;

// If the task was previously queued, remove it from the queue
if (taskWasPreviouslyQueued) TryDequeue(task);

// Try to run the task.
return base.TryExecuteTask(task);
}

/// <summary>Attempts to remove a previously scheduled task from the scheduler.</summary>
/// <param name="task">The task to be removed.</param>
/// <returns>Whether the task could be found and removed.</returns>
protected sealed override bool TryDequeue(Task task)
{
lock (_tasks) return _tasks.Remove(task);
}

/// <summary>Gets the maximum concurrency level supported by this scheduler.</summary>
public sealed override int MaximumConcurrencyLevel { get { return _maxDegreeOfParallelism; } }

/// <summary>Gets an enumerable of the tasks currently scheduled on this scheduler.</summary>
/// <returns>An enumerable of the tasks currently scheduled.</returns>
protected sealed override IEnumerable<Task> GetScheduledTasks()
{
bool lockTaken = false;
try
{
Monitor.TryEnter(_tasks, ref lockTaken);
if (lockTaken) return _tasks.ToArray();
else throw new NotSupportedException();
}
finally
{
if (lockTaken) Monitor.Exit(_tasks);
}
}
}
}

+ 33
- 0
TelpoPush.Position.Worker/Common/TimeHelper.cs Vedi File

@@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TelpoPush.Position.Worker.Common
{
public class TimeHelper
{
/// <summary>
/// 时间戳转成时间类型
/// </summary>
/// <param name="timeStamp"></param>
/// <returns></returns>
public static DateTime ConvertToLocalDateTime(string timeStamp)
{
DateTime dtStart = TimeZoneInfo.ConvertTime(new DateTime(1970, 1, 1), TimeZoneInfo.Utc, TimeZoneInfo.Local);
if (timeStamp.Length == 13)
{
long lTime = long.Parse(timeStamp + "0000");
TimeSpan toNow = new TimeSpan(lTime);
return dtStart.Add(toNow);
}
return dtStart.AddSeconds(long.Parse(timeStamp));
}

public static string ToDateTimeStr(DateTime dt)
{
return dt.ToString("yyyy-MM-dd HH:mm:ss");
}
}
}

+ 40
- 0
TelpoPush.Position.Worker/Common/Utils.cs Vedi File

@@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Security.Cryptography;

namespace TelpoPush.Position.Worker.Common
{
public class Utils
{
public static MultipartFormDataContent GetMultipartFormDataContent(Dictionary<string, object> dic, string appId, ref Dictionary<string, object> outDic)
{
MultipartFormDataContent mfdc = new MultipartFormDataContent();
StringBuilder sb = new StringBuilder();
if (dic != null && dic.Count > 0)
{
var dicOrderBy = dic.OrderBy(z => z.Key);
foreach (KeyValuePair<string, object> kv in dicOrderBy)
{
sb.Append($"{kv.Key}={kv.Value.ToString()}&");
mfdc.Add(new StringContent(kv.Value.ToString()), kv.Key);//参数, 内容在前,参数名称在后
}
}
string signStr = $"{sb.ToString().Trim('&')}{appId}";
byte[] bytes = Encoding.UTF8.GetBytes(signStr);
byte[] hash = SHA256.Create().ComputeHash(bytes);
StringBuilder builder = new StringBuilder();
for (int i = 0; i < hash.Length; i++)
{
builder.Append(hash[i].ToString("X2"));
}
string sign = builder.ToString().ToLower();
dic.Add("sign", sign);
mfdc.Add(new StringContent(sign), "sign");//参数, 内容在前,参数名称在后
outDic = dic;
return mfdc;
}
}
}

+ 62
- 0
TelpoPush.Position.Worker/Handlers/KafkaSubscribe.cs Vedi File

@@ -0,0 +1,62 @@
using Confluent.Kafka;
using TelpoPush.Position.Worker.Common;
using TelpoPush.Position.Worker.Service.Mq;

namespace TelpoPush.Position.Worker.Handlers
{
public class KafkaSubscribe
{
private readonly ILogger<KafkaSubscribe> _logger;
private readonly IHostEnvironment _env;
private readonly IKafkaService _kafkaService;
private readonly PositionProcess _positionProcess;


public KafkaSubscribe(
ILogger<KafkaSubscribe> logger, IHostEnvironment env,
IKafkaService kafkaService,
PositionProcess positionProcess)
{
_logger = logger;
_env = env;
_kafkaService = kafkaService;
_positionProcess = positionProcess;
}
public async Task SubscribeAsync()
{
#if DEBUG

_logger.LogInformation("11312");

var temp = new Headers();
string topic = "topic.push.position";
//temp.Add(new Header("DataType", new byte[] { 0, 0, 0, 0 }));
//temp.Add(new Header("AlarmType", new byte[] { 2, 0, 0, 0 }));
//string psych = "{\"messageId\":\"1790941606816612864\",\"topic\":\"topic.push.third\",\"time\":\"2024-05-16 11:05:27\",\"data\":{\"imei\":\"861281060093147\",\"atteryLowId\":\"861281060093147664577f9\",\"info\":\"设备电量低于15%\"}}";
//await _positionProcess.SendPosition(psych, topic, temp);

//// await _kafkaService.SubscribeAsync(DoReceive, CancellationToken.None);

#else
LimitedConcurrencyLevelTaskScheduler lcts = new LimitedConcurrencyLevelTaskScheduler(5);
TaskFactory factory = new TaskFactory(lcts);
try
{
await factory.StartNew(async () =>
{
await _kafkaService.SubscribeAsync(DoReceive, CancellationToken.None);
});
}
catch (Exception ex)
{
_logger.LogError($"Subscribe 处理Kafka数据发生异常 {ex.Message}|{ex.Source}|{ex.StackTrace}");
}
#endif
}
async void DoReceive(string topic, string message, Headers headers)
{
await _positionProcess.SendPosition(message, topic, headers);
}
}
}

+ 346
- 0
TelpoPush.Position.Worker/Handlers/PositionProcess.cs Vedi File

@@ -0,0 +1,346 @@
using Confluent.Kafka;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using TelpoPush.Position.Worker.Common;
using TelpoPush.Position.Worker.Models.Config;
using TelpoPush.Position.Worker.Models.Enum;
using TelpoPush.Position.Worker.Models.MqTemplates;
using TelpoPush.Position.Worker.Models.PushTemplates;
using TelpoPush.Position.Worker.Service.Cache;
using TelpoPush.Position.Worker.Service.Mq;

namespace TelpoPush.Position.Worker.Handlers
{
public class PositionProcess
{
private readonly static object _syncLocker = new object();
private readonly IHostEnvironment _env;
private readonly ILogger<PositionProcess> _logger;
private readonly HttpHelperAsync _httpHelper;
private readonly RedisUtil _redis;
private readonly MqProcessMessage _serviceMqProcess;
private readonly PositionConfig _positionConfig;

public PositionProcess(
IHostEnvironment env,
ILogger<PositionProcess> logger,
HttpHelperAsync httpHelper,
RedisUtil redis,
MqProcessMessage serviceMqProcess,
IOptions<PositionConfig> positionConfig
)
{
_env = env;
_logger = logger;
_httpHelper = httpHelper;
_redis = redis;
_positionConfig = positionConfig.Value;
_serviceMqProcess = serviceMqProcess;

}

public async Task SendPosition(string? message, string topic, Headers headers)
{
#region 数据初始验证
bool isHandle = true;
BaseModel model = null;
string imei = "";
if (!string.IsNullOrEmpty(message))
{
model = JsonConvert.DeserializeObject<BaseModel>(message);
if (model != null)
{
var Jo = JsonConvert.DeserializeObject<Dictionary<string, object>>(model.data.ToString());
if (Jo.ContainsKey("imei"))
imei = Jo["imei"].ToString();
if (Jo.ContainsKey("Imei"))
imei = Jo["Imei"].ToString();
else if (Jo.ContainsKey("nickname"))
imei = Jo["nickname"].ToString();
if (string.IsNullOrEmpty(imei))
{
_logger.LogInformation($"[数据信息不完整] imei信息不存在:{message}");
isHandle = false;
}
else
await _redis.GetGpsDevice(imei);
}
else
{
_logger.LogInformation($"[数据信息不完整] 数据解析异常:{message}");
isHandle = false;
}
}
else
{
_logger.LogInformation($"[数据信息不完整] message数据异常:{message}");
isHandle = false;
}
#endregion
if (isHandle)
{
lock (_syncLocker)
{
//Headers 解析
HeadersDto headersDto = new HeadersDto();
try
{
foreach (var item in headers)
{
if (item.Key == KafkaHeader.DataType)
headersDto.DataType = BitConverter.ToInt32(item.GetValueBytes(), 0);
else if (item.Key == KafkaHeader.AlarmType)
headersDto.AlarmType = BitConverter.ToInt32(item.GetValueBytes(), 0);
else if (item.Key == KafkaHeader.OperType)
headersDto.OperType = BitConverter.ToInt32(item.GetValueBytes(), 0);
}
}
catch (Exception ex)
{
_logger.LogError($"当前工作线程Headers异常,{ex.Message}|{ex.Source}|{ex.StackTrace}");
}
try
{
#region 注释
//string dataType = headersDto.DataType != null ? "_" + headersDto.DataType : "";
//string alarmType = headersDto.AlarmType != null ? "_" + headersDto.AlarmType : "";
//string operType = headersDto.OperType != null ? "_" + headersDto.OperType : "";
//string key = dataType + alarmType + operType;
//var dataStatus = _redis.IsDateStatus(model, imei, key).Result;
//过滤
//if (headersDto.DataType == (int)MqDataType.TemperatureInfo
// || headersDto.DataType == (int)MqDataType.Temperature1Info
// || headersDto.DataType == (int)MqDataType.BindDevice
// || headersDto.DataType == (int)MqDataType.PositionInfo
// || headersDto.DataType == (int)MqDataType.HeartRateInfo
// || headersDto.DataType == (int)MqDataType.HeartRate1Info
// || headersDto.DataType == (int)MqDataType.Spo2Info
// || headersDto.DataType == (int)MqDataType.Spo21Info
// || headersDto.DataType == (int)MqDataType.BloodPressInfo
// || headersDto.DataType == (int)MqDataType.BloodPress1Info
// || headersDto.DataType == (int)MqDataType.SportResult
// || headersDto.DataType == (int)MqDataType.BloodSugar
// )
// dataStatus.isPush = true;
//dataStatus.isPush = true;
//if (dataStatus.isPush)
//{
//switch (topic)
//{
// case "topic.push.third":
// switch (headersDto.DataType)
// {
// case (int)MqDataType.AlarmInfo: //报警消息
// break;
// case (int)MqDataType.TemperatureInfo: //体温消息
// break;
// case (int)MqDataType.PositionInfo: //定位消息
// // DataServicePusPosition(model, imei);
// break;
// case (int)MqDataType.StepInfo: //步数消息

// break;
// case (int)MqDataType.BatteryLevelInfo: //电量消息
// break;
// case (int)MqDataType.DeviceCallLog: //设备通话记录

// break;
// case (int)MqDataType.DeviceSmsLog: //设备短信记录

// break;
// case (int)MqDataType.DeviceConfigInfo: //设备配置信息

// break;
// case (int)MqDataType.Status: //设备状态(offline,online)

// break;
// case (int)MqDataType.Active: //设备激活状态
// break;
// case (int)MqDataType.reply: //指令回调

// break;
// case (int)MqDataType.Weather: //天气查询

// break;
// case (int)MqDataType.ReadMsg: //短消息阅读

// break;
// case (int)MqDataType.StudyAINotifyStatusUpload: //学习能力状态

// break;
// case (int)MqDataType.HeartRateInfo: //心率

// break;
// case (int)MqDataType.HeartRate1Info: //周期性心率

// break;
// case (int)MqDataType.Spo2Info: //血氧

// break;
// case (int)MqDataType.Spo21Info: //周期性血氧

// break;
// case (int)MqDataType.Temperature1Info: //周期性报体温数据

// break;
// case (int)MqDataType.DrownReportInfo: //防溺水告警

// break;
// case (int)MqDataType.WearStatusInfo: //手表未佩戴告警

// break;
// case (int)MqDataType.BloodPressInfo: //血压

// break;
// case (int)MqDataType.BloodPress1Info: //周期性血压

// break;
// case (int)MqDataType.PsychInfo: //心理监测

// break;
// case (int)MqDataType.AiCallResult: //AI呼叫结果回调

// case (int)MqDataType.CrossBorder: //越界上报(围栏进出告警)

// break;
// case (int)MqDataType.SportResult: //运动数据上报

// break;
// case (int)MqDataType.BindDevice: //绑定业务

// break;
// case (int)MqDataType.BloodSugar: //血糖业务

// break;
// default:
// break;
// }
// break;
// default:
// break;
//}
//}
//else
// _logger.LogInformation($"数据未处理(历史数据):{JsonConvert.SerializeObject(dataStatus)}");

#endregion

switch (topic)
{
case "topic.push.position":
switch (headersDto.DataType)
{
case (int)MqDataType.PositionInfo: //定位消息
DataServicePusPosition(model, imei);
break;
default:
break;
}
break;
default:
break;
}

}
catch (Exception ex)
{
_logger.LogError($"当前工作线程异常: {ex.Message}|{ex.Source}|{ex.StackTrace}");
}
}
}
}

//位置
public async Task DataServicePusPosition(BaseModel model, string imei)
{
var device = await _redis.GetGpsDevice(imei);
if (device != null)
{
await _redis.SetPersonInfoHash(imei); //更行设备用户详情缓存
var position = JsonConvert.DeserializeObject<MqPositionTemplate>(model.data.ToString());
if ((int)position.locationType != 2) // 限制条件:locationType=2 LBS定位不推送
{
Dictionary<string, int> dicHeader = new Dictionary<string, int>();
dicHeader.Add(MqHeader.DataType, (int)MqDataType.PositionInfo);

#region 定位-围栏推送服务
var fenceObj = new
{
messageId = model.messageId,
topic = "topic.push.position",
time = model.time,
data = new
{
DeviceId = device.deviceId,
imei = position.imei,
wifiInfo = position.wifiMacs,
address = position.address,
baiduLatitude = position.baiduLatitude,
baiduLongitude = position.baiduLongitude,
gaodeLatitude = position.gaodeLatitude,
gaodeLongitude = position.gaodeLongitude,
originalLatitude = position.originalLatitude,
originalLongitude = position.originalLongitude,
locationType = position.locationType,
LastUpdate = model.time,
UtcDate = DateTime.Parse(model.time).AddHours(-8).ToString("yyyy-MM-dd HH:mm:ss"),
Radius = position.radius,
}
};
await _serviceMqProcess.ProcessFenceServer(imei, fenceObj, dicHeader, "定位-围栏");
#endregion

#region 定位-JAVA数据推送服务
var settingInfos = await _redis.GetManufactorPushSettingHash(imei, _positionConfig.RzlManufactorId, (int)MqDataType.PositionInfo);
settingInfos = null;
if (settingInfos != null)
{
Dictionary<string, object> dic = new Dictionary<string, object>();
dic.Add("imei", imei);
dic.Add("locationType", (int)position.locationType);
dic.Add("altitude", position.radius);
dic.Add("gaodeLongitude", position.gaodeLongitude);
dic.Add("gaodeLatitude", position.gaodeLatitude);
dic.Add("originalLongitude", position.originalLongitude);
dic.Add("originalLatitude", position.originalLatitude);
dic.Add("baiduLongitude", position.baiduLongitude);
dic.Add("baiduLatitude", position.baiduLatitude);
dic.Add("address", position.address);
if (!string.IsNullOrEmpty(position.wifiMacs))
{
position.wifiMacs = position.wifiMacs.Replace(",|", "|").Trim(',');
dic.Add("wifiMacs", position.wifiMacs);
}
dic.Add("dataTime", model.time);
MultipartFormDataContent mfdc = Utils.GetMultipartFormDataContent(dic, _positionConfig.RzlManufactorId, ref dic);
var result = await _httpHelper.PostFormAsync(settingInfos.pushUrl, mfdc);
_logger.LogInformation($"[定位-RZL数据-替换JAVA推送服务<{imei}>] url:{settingInfos.pushUrl},参数:{JsonConvert.SerializeObject(dic)},结果:{result}");
}
else
{
if (!string.IsNullOrEmpty(position.wifiMacs))
position.wifiMacs = position.wifiMacs.Replace(",|", "|").Trim(',');
PushPositionTemplate positionInfo = new PushPositionTemplate //上报存储位置信息
{
MessageId = model.messageId,
Imei = imei,
Altitude = position.radius,
BaiduLatitude = position.baiduLatitude,
BaiduLongitude = position.baiduLongitude,
GaodeLatitude = position.gaodeLatitude,
GaodeLongitude = position.gaodeLongitude,
LocationType = (int)position.locationType,
OriginalLatitude = position.originalLatitude,
OriginalLongitude = position.originalLongitude,
Address = position.address,
wifiMacs = position.wifiMacs,
Time = model.time
};
await _serviceMqProcess.ProcessDataPushServer(imei, positionInfo, dicHeader, "定位");
}
#endregion
}
}
}
}
}

+ 19
- 0
TelpoPush.Position.Worker/Models/CacheTemplates/DeviceInfoModel.cs Vedi File

@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TelpoPush.Position.Worker.Models.CacheTemplates
{
public class DeviceInfoModel
{
public string deviceId { get; set; }
public string imei { get; set; }
public string deviceName { get; set; }
public string orgId { get; set; }
public string apiUid { get; set; }
public string activeStatus { get; set; }
public string activeTime { get; set; }
}
}

+ 35
- 0
TelpoPush.Position.Worker/Models/CacheTemplates/ManufactorPushSettingInfoModel.cs Vedi File

@@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TelpoPush.Position.Worker.Models.CacheTemplates
{

public class ManufactorPushSettingInfoModel
{
public string id { get; set; }
public string manufactorId { get; set; }
public string manufactorName { get; set; }
public string imei { get; set; }
public List<PushSettingsItem> pushSettings { get; set; }
public int settingType { get; set; }
}
public class PushSettingsItem
{
public string dataName { get; set; }
public int dataType { get; set; }
public List<SettingInfosItem> settingInfos { get; set; }
}
public class SettingInfosItem
{
public bool pushFlag { get; set; }
public string pushStartTime { get; set; }
public string pushEndTime { get; set; }
public int pushType { get; set; }
public string pushUrl { get; set; }
public string remark { get; set; }
}

}

+ 42
- 0
TelpoPush.Position.Worker/Models/CacheTemplates/PersonInfoModel.cs Vedi File

@@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TelpoPush.Position.Worker.Models.CacheTemplates
{

public class PersonInfoModel
{
public PersonModel person { get; set; }
public string time { get; set; }
}
public class PersonModel
{
public string personId { get; set; }
public string serialno { get; set; }
public string personName { get; set; }
public string deviceId { get; set; }
public string nickName { get; set; }
public bool gender { get; set; }
public int height { get; set; }
public int weight { get; set; }
public string bornDate { get; set; }
public string school { get; set; }
public string grade { get; set; }
public string className { get; set; }
public string imagePath { get; set; }
public string imagePathSmall { get; set; }
public int age { get; set; }
public string createTime { get; set; }
public string remarks { get; set; }
public int ishypertension { get; set; }
public string emotion { get; set; }
public int profession { get; set; }
public int regularity { get; set; }
public int chronicDisease { get; set; }
public string apiUid { get; set; }
public string apiRemark { get; set; }
}
}

+ 19
- 0
TelpoPush.Position.Worker/Models/Config/PositionConfig.cs Vedi File

@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TelpoPush.Position.Worker.Models.Config
{
public class PositionConfig
{
public string CoreServiceApiUrl { get; set; }
public string GpsWebApiUrl { get; set; }

public string RzlManufactorId { get; set; }
public string RzlPushTemperatureUrl { get; set; }
public string RzlPushTranspondUrl { get; set; }
public string RzlVoiceCallback { get; set; }
}
}

+ 94
- 0
TelpoPush.Position.Worker/Models/Config/RedisConfig.cs Vedi File

@@ -0,0 +1,94 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TelpoPush.Position.Worker.Models.Config
{
/// <summary>
/// Redis配置模板类
/// </summary>
public class RedisConfig
{
public string Server { get; set; }
/// <summary>
/// Redis server password
/// </summary>
public string Password { get; set; }
/// <summary>
/// Redis server database, default 0
/// </summary>
public int? DefaultDatabase { get; set; }
/// <summary>
/// The asynchronous method automatically uses pipeline, and the 10W concurrent time is 450ms(welcome to feedback)
/// </summary>
public bool? AsyncPipeline { get; set; }
/// <summary>
/// Connection pool size, default 50
/// </summary>
public int? Poolsize { get; set; }
/// <summary>
/// Idle time of elements in the connection pool(MS), suitable for connecting to remote redis server, default 20000
/// </summary>
public int? IdleTimeout { get; set; }
/// <summary>
/// Connection timeout(MS), default 5000
/// </summary>
public int? ConnectTimeout { get; set; }
/// <summary>
/// Send / receive timeout(MS), default 10000
/// </summary>
public int? SyncTimeout { get; set; }
/// <summary>
/// Preheat connections, receive values such as preheat = 5 preheat 5 connections, default 5
/// </summary>
public int? Preheat { get; set; }
/// <summary>
/// Follow system exit event to release automatically, default true
/// </summary>
public bool? AutoDispose { get; set; }
/// <summary>
/// Enable encrypted transmission, default false
/// </summary>
public bool? Ssl { get; set; }
/// <summary>
/// 是否尝试集群模式,阿里云、腾讯云集群需要设置此选项为 false, default true
/// </summary>
public bool? Testcluster { get; set; }
/// <summary>
/// Execution error, retry attempts, default 0
/// </summary>
public int? Tryit { get; set; }
/// <summary>
/// Connection name, use client list command to view
/// </summary>
public string Name { get; set; }
/// <summary>
/// key前辍,所有方法都会附带此前辍,csredis.Set(prefix + "key", 111)
/// </summary>
public string Prefix { get; set; }

public override string ToString()
{
if (string.IsNullOrWhiteSpace(Server)) throw new ArgumentNullException(nameof(Server));
var sb = new StringBuilder(Server);
if (!string.IsNullOrWhiteSpace(Password)) sb.Append($",password={Password}");
if (DefaultDatabase.HasValue) sb.Append($",defaultDatabase={DefaultDatabase}");
if (AsyncPipeline.HasValue) sb.Append($",asyncPipeline={AsyncPipeline}");
if (Poolsize.HasValue) sb.Append($",poolsize={Poolsize}");
if (IdleTimeout.HasValue) sb.Append($",idleTimeout={IdleTimeout}");
if (ConnectTimeout.HasValue) sb.Append($",connectTimeout={ConnectTimeout}");
if (SyncTimeout.HasValue) sb.Append($",syncTimeout={0}");
if (Preheat.HasValue) sb.Append($",preheat={Preheat}");
if (AutoDispose.HasValue) sb.Append($",autoDispose={AutoDispose}");
if (Ssl.HasValue) sb.Append($",ssl={Ssl}");
if (Testcluster.HasValue) sb.Append($",testcluster={Testcluster}");
if (Tryit.HasValue) sb.Append($",tryit={Tryit}");
if (!string.IsNullOrWhiteSpace(Name)) sb.Append($",name={Name}");
if (!string.IsNullOrWhiteSpace(Prefix)) sb.Append($",prefix={Prefix}");

return sb.ToString();
}
}
}

+ 32
- 0
TelpoPush.Position.Worker/Models/Config/ServiceConfig.cs Vedi File

@@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TelpoPush.Position.Worker.Models.Config
{
public class ServiceConfig
{
/// <summary>
/// 数据服务Host Url
/// </summary>
public string TelpoDataUrl { get; set; }

/// <summary>
/// Kafka服务地址
/// </summary>
public string KafkaBootstrapServers { get; set; }
public List<string> KafkaTopics { get; set; }
public string KafkaGroupId { get; set; }
//public string KafkaSaslUsername { get; set; }
//public string KafkaSaslPassword { get; set; }
//public string KafkaSslCaLocation { get; set; }

/// <summary>
/// 默认缓存时间
/// </summary>
public int CacheDurationSeconds { get; set; }
public int CacheDurationSeconds10 { get; set; }
}
}

+ 16
- 0
TelpoPush.Position.Worker/Models/Enum/HeadersDto.cs Vedi File

@@ -0,0 +1,16 @@
using Newtonsoft.Json;
namespace TelpoPush.Position.Worker.Models.Enum
{
/// <summary>
/// 消息数据头
/// </summary>
public class HeadersDto
{
[JsonProperty(PropertyName = "DataType")]
public int? DataType { get; set; }
[JsonProperty(PropertyName = "AlarmType")]
public int? AlarmType { get; set; }
[JsonProperty(PropertyName = "OperType")]
public int? OperType { get; set; }
}
}

+ 19
- 0
TelpoPush.Position.Worker/Models/Enum/MqDataTopic.cs Vedi File

@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TelpoPush.Position.Worker.Models.Enum
{

public enum MqDataTopic : int
{
/// <summary>
/// 中高实时心率
/// </summary>
ZkRealHRMonitorTopic = 1


}
}

+ 156
- 0
TelpoPush.Position.Worker/Models/Enum/MqDataType.cs Vedi File

@@ -0,0 +1,156 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TelpoPush.Position.Worker.Models.Enum
{
/// <summary>
/// 数据类型,标识发布到kafka的消息的数据类型
/// </summary>
public enum MqDataType : int
{
/// <summary>
/// 报警消息
/// </summary>
AlarmInfo = 0,

/// <summary>
/// 温度数据信息
/// </summary>
TemperatureInfo = 1,

/// <summary>
/// 步数信息
/// </summary>
StepInfo = 2,

/// <summary>
/// 电量信息
/// </summary>
BatteryLevelInfo = 3,

/// <summary>
/// 设备配置信息
/// </summary>
DeviceConfigInfo = 4,

/// <summary>
/// 设备通话记录
/// </summary>
DeviceCallLog = 5,

/// <summary>
/// 设备短信记录
/// </summary>
DeviceSmsLog = 6,

/// <summary>
/// 位置信息
/// </summary>
PositionInfo = 7,

/// <summary>
/// 支付
/// </summary>
PayInfo = 8,

/// <summary>
/// 设备状态(offline,online)
/// </summary>
Status = 9,

/// <summary>
/// 设备激活状态(激活1,未激活0)
/// </summary>
Active = 10,

/// <summary>
/// 指令回调
/// </summary>
reply = 11,

/// <summary>
/// 天气查询
/// </summary>
Weather = 12,

/// <summary>
/// 短信阅读事件
/// </summary>
ReadMsg = 13,
/// <summary>
/// 学习能力状态上报事件
/// </summary>
StudyAINotifyStatusUpload = 14,
/// <summary>
/// 心率
/// </summary>
HeartRateInfo = 15,
/// <summary>
/// 血氧
/// </summary>
Spo2Info = 16,
/// <summary>
/// 周期性报体温数据。
/// </summary>
Temperature1Info = 17,
/// <summary>
/// 周期心率。
/// </summary>
HeartRate1Info = 18,
/// <summary>
/// 周期性血氧
/// </summary>
Spo21Info = 19,
/// <summary>
/// 溺水状态
/// </summary>
DrownReportInfo = 20,
/// <summary>
/// 手表佩戴状态
/// </summary>
WearStatusInfo = 21,
/// <summary>
/// 血压
/// </summary>
BloodPressInfo = 22,
/// <summary>
/// 周期性血压
/// </summary>
BloodPress1Info = 23,
/// <summary>
/// 心理监测
/// </summary>
PsychInfo = 24,

/// <summary>
/// AI呼叫回调结果
/// </summary>
AiCallResult = 25,

/// <summary>
/// 越界上报(围栏进出告警)
/// </summary>
CrossBorder = 26,

/// <summary>
/// 运动数据上报
/// </summary>
SportResult = 27,
/// <summary>
/// 运动数据上报
/// </summary>
BloodSugar = 28,

/// <summary>
/// 中考实时心率数据上报
/// </summary>
ZkRealHeartRate = 29,
/// <summary>
/// 绑定业务
/// </summary>
BindDevice = 100
}
}

+ 26
- 0
TelpoPush.Position.Worker/Models/Enum/MqHeader.cs Vedi File

@@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TelpoPush.Position.Worker.Models.Enum
{
public static class MqHeader
{
/// <summary>
/// DataType
/// </summary>
public const string DataType = "DataType";

/// <summary>
/// OperType
/// </summary>
public const string OperType = "OperType";

/// <summary>
/// AlarmType
/// </summary>
public const string AlarmTypes = "AlarmType";
}
}

+ 17
- 0
TelpoPush.Position.Worker/Models/MqTemplates/BaseModel.cs Vedi File

@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TelpoPush.Position.Worker.Models.MqTemplates
{
public class BaseModel
{
public string messageId { get; set; }
public string topic { get; set; }
public string time { get; set; }
public object data { get; set; }
}

}

+ 29
- 0
TelpoPush.Position.Worker/Models/MqTemplates/MqPositionTemplate.cs Vedi File

@@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TelpoPush.Position.Worker.Models.MqTemplates
{
public class MqPositionTemplate
{
public string imei { get; set; }
public int locationType { get; set; }
public string address { get; set; }
public int altitude { get; set; }
public decimal baiduLatitude { get; set; }
public decimal baiduLongitude { get; set; }
public decimal gaodeLatitude { get; set; }
public decimal gaodeLongitude { get; set; }
public decimal originalLatitude { get; set; }
public decimal originalLongitude { get; set; }
public string postcode { get; set; }
public string hashParam { get; set; }
public int radius { get; set; }
public string province { get; set; }
public string city { get; set; }
public string district { get; set; }
public string wifiMacs { get; set; }
}
}

+ 35
- 0
TelpoPush.Position.Worker/Models/PushTemplates/PushFencePositionTemplate.cs Vedi File

@@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TelpoPush.Position.Worker.Models.PushTemplates
{
public class PushFencePositionTemplate
{

public string messageId { get; set; }
public string topic { get; set; }
public string time { get; set; }
public data data { get; set; }
}

public class data
{
public string DeviceId { get; set; }
public int Radius { get; set; }
public string imei { get; set; }
public string wifiInfo { get; set; }
public int locationType { get; set; }
public string address { get; set; }
public decimal baiduLatitude { get; set; }
public decimal baiduLongitude { get; set; }
public decimal gaodeLatitude { get; set; }
public decimal gaodeLongitude { get; set; }
public decimal originalLatitude { get; set; }
public decimal originalLongitude { get; set; }
public string LastUpdate { get; set; }
public string UtcDate { get; set; }
}
}

+ 73
- 0
TelpoPush.Position.Worker/Models/PushTemplates/PushPositionTemplate.cs Vedi File

@@ -0,0 +1,73 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TelpoPush.Position.Worker.Models.PushTemplates
{
public class PushPositionTemplate
{

/// <summary>
/// 服务跟踪Id
/// </summary>
public string MessageId { get; set; }

/// <summary>
/// 海拔
/// </summary>
public double Altitude { get; set; }

/// <summary>
/// 定位类型
/// </summary>
public int LocationType { get; set; }
/// <summary>
/// 设备IMEI
/// </summary>
public string Imei { get; set; }

/// <summary>
/// 时间
/// yyyy-MM-dd HH:mm:ss
/// </summary>
public string Time { get; set; }

/// <summary>
/// 原始纬度
/// </summary>
public decimal OriginalLatitude { get; set; }

/// <summary>
/// 原始经度
/// </summary>
public decimal OriginalLongitude { get; set; }

/// <summary>
/// 百度地图纬度
/// </summary>
public decimal BaiduLatitude { get; set; }

/// <summary>
/// 百度地图经度
/// </summary>
public decimal BaiduLongitude { get; set; }

/// <summary>
/// 高德地图纬度
/// </summary>
public decimal GaodeLatitude { get; set; }

/// <summary>
/// 高德地图经度
/// </summary>
public decimal GaodeLongitude { get; set; }

/// <summary>
/// 地址
/// </summary>
public string Address { get; set; }
public string wifiMacs { get; set; }
}
}

+ 13
- 9
TelpoPush.Position.Worker/Program.cs Vedi File

@@ -1,9 +1,13 @@
using Dapper;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Serilog;
using TelpoDataService.Util.Clients;
using TelpoPush.Position.Worker;
using TelpoPush.Position.Worker.Common;
using TelpoPush.Position.Worker.Handlers;
using TelpoPush.Position.Worker.Service.Cache;
using TelpoPush.Position.Worker.Service.Mq;


#region ÈÕÖ¾
@@ -76,14 +80,14 @@ try

builder.Services.AddSerilog();
builder.Services.AddHttpClient();
//builder.Services.AddTransient<HttpHelperAsync>();
//builder.Services.AddSingleton<SqlMapper>();
//builder.Services.AddSingleton<RedisUtil>();
//builder.Services.AddSingleton<IKafkaService, KafkaService>();
//builder.Services.AddSingleton<KafkaSubscribe>();
//builder.Services.AddSingleton<MessageProducer>();
//builder.Services.AddSingleton<MqProcessMessage>();
//builder.Services.AddSingleton<PositionProcess>();
builder.Services.AddTransient<HttpHelperAsync>();
builder.Services.AddSingleton<SqlMapper>();
builder.Services.AddSingleton<RedisUtil>();
builder.Services.AddSingleton<IKafkaService, KafkaService>();
builder.Services.AddSingleton<KafkaSubscribe>();
builder.Services.AddSingleton<MessageProducer>();
builder.Services.AddSingleton<MqProcessMessage>();
builder.Services.AddSingleton<PositionProcess>();
builder.Services.AddHostedService<Worker>();
var host = builder.Build();
host.Run();


+ 82
- 0
TelpoPush.Position.Worker/Service/Cache/MemoryCacheUtil.cs Vedi File

@@ -0,0 +1,82 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Memory;

namespace TelpoPush.Position.Worker.Service.Cache
{
internal class MemoryCacheUtil
{
static MemoryCache cache = new MemoryCache(new MemoryCacheOptions());
/// <summary>
/// 创建缓存项的文件
/// </summary>
/// <param name="key">缓存Key</param>
/// <param name="obj">object对象</param>
public static void Set(string key, object value)
{
if (key != null)
{
cache.Set(key, value);
}
}
/// <summary>
/// 创建缓存项过期
/// </summary>
/// <param name="key">缓存Key</param>
/// <param name="obj">object对象</param>
/// <param name="expires">过期时间(秒)</param>
public static void Set(string key, object value, int expires)
{
if (key != null)
{
cache.Set(key, value, new MemoryCacheEntryOptions()
//设置缓存时间,如果被访问重置缓存时间。设置相对过期时间x秒
.SetSlidingExpiration(TimeSpan.FromSeconds(expires)));
}
}

/// <summary>
/// 获取缓存对象
/// </summary>
/// <param name="key">缓存Key</param>
/// <returns>object对象</returns>
public static object Get(string key)
{
object val = null;
if (key != null && cache.TryGetValue(key, out val))
{

return val;
}
else
{
return default(object);
}
}

/// <summary>
/// 获取缓存对象
/// </summary>
/// <typeparam name="T">T对象</typeparam>
/// <param name="key">缓存Key</param>
/// <returns></returns>
public static T Get<T>(string key)
{
object obj = Get(key);
return obj == null ? default(T) : (T)obj;
}


/// <summary>
/// 移除缓存项的文件
/// </summary>
/// <param name="key">缓存Key</param>
public static void Remove(string key)
{
cache.Remove(key);
}
}
}

+ 208
- 0
TelpoPush.Position.Worker/Service/Cache/RedisUtil.cs Vedi File

@@ -0,0 +1,208 @@
using Microsoft.Extensions.Options;
using TelpoPush.Position.Worker.Models.CacheTemplates;
using TelpoPush.Position.Worker.Models.Config;

namespace TelpoPush.Position.Worker.Service.Cache
{
public class RedisUtil
{
private const string CACHE_HASH_KEY_TELPO_GPSDEVICE = "TELPO#GPSDEVICE_HASH";
private const string CACHE_HASH_KEY_TELPO_GPSDEVICE_PERSON = "TELPO#GPSDEVICE_PERSON_HASH";
private const string CACHE_HASH_KEY_TELPO_MANUFACTOR_CONFIG = "TELPO#MANUFACTOR_CONFG_HASH";
private const string CACHE_HASH_KEY_TELPO_GPSDEVICE_PUSHSITTTIGS = "TELPO#GPSDEVICE_PUSH_SITTINGS_HASH";

private readonly ILogger<RedisUtil> _logger;
private readonly ServiceConfig _configService;
private readonly SqlMapper _sqlMapper;
public RedisUtil(ILogger<RedisUtil> logger,IOptions<RedisConfig> optConfigRedis, IOptions<ServiceConfig> configService, SqlMapper sqlMapper)
{
_configService = configService.Value;
_logger = logger;
optConfigRedis.Value.Prefix = "";
optConfigRedis.Value.DefaultDatabase = 7;
var csredis = new CSRedis.CSRedisClient(optConfigRedis.Value.ToString());
RedisHelper.Initialization(csredis);
_sqlMapper = sqlMapper;
}

public async Task<DeviceInfoModel> GetGpsDevice(string imei)
{
if (string.IsNullOrWhiteSpace(imei)) return null;
string keyCache = $"{imei}_GpsDevice";
try
{
var objCache = MemoryCacheUtil.Get<DeviceInfoModel>(keyCache);
if (objCache == null)
{
var obj = await RedisHelper.HGetAsync<DeviceInfoModel>(CACHE_HASH_KEY_TELPO_GPSDEVICE, imei);
if (obj == null)
{
var deviceInfo = _sqlMapper.DeviceInfo(imei);
if (deviceInfo != null)
{
RedisHelper.HSetAsync(CACHE_HASH_KEY_TELPO_GPSDEVICE, imei, deviceInfo);
MemoryCacheUtil.Set(keyCache, deviceInfo, _configService.CacheDurationSeconds10);
return deviceInfo;
}
}
else
{
MemoryCacheUtil.Set(keyCache, obj, _configService.CacheDurationSeconds10);
return obj;
}
}
return objCache;
}
catch (Exception ex)
{
_logger.LogError($"GetGpsDevice,key={imei},缓存异常,重新获取数据,{ex.Message}|{ex.Source}|{ex.StackTrace}");
var deviceInfo = _sqlMapper.DeviceInfo(imei);
if (deviceInfo != null)
{
RedisHelper.HSetAsync(CACHE_HASH_KEY_TELPO_GPSDEVICE, imei, deviceInfo);
MemoryCacheUtil.Set(keyCache, deviceInfo, _configService.CacheDurationSeconds10);
return deviceInfo;
}
}
return null;
}

public async Task<PersonInfoModel> SetPersonInfoHash(string imei)
{
if (string.IsNullOrWhiteSpace(imei)) return null;
string keyCache = $"{imei}_PersonInfoHash";
var personInfo = _sqlMapper.PersonInfo(imei);
PersonInfoModel model = new PersonInfoModel()
{
person = personInfo,
time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")
};
if (personInfo != null)
{
await RedisHelper.HSetAsync(CACHE_HASH_KEY_TELPO_GPSDEVICE_PERSON, imei, model);
MemoryCacheUtil.Set(keyCache, model, 60);//1分钟
}
else
{
await RedisHelper.HDelAsync(CACHE_HASH_KEY_TELPO_GPSDEVICE_PERSON, imei);
MemoryCacheUtil.Remove(keyCache);
}
return model;
}

public async Task<string> GetHealthyDeviceKey(string imei)
{
if (string.IsNullOrWhiteSpace(imei)) return null;
string HealthyDeviceKey = $"Telpol:HealthyDeviceKey:{imei}";
string keyCache = $"{imei}_HealthyDeviceKey";
try
{
var objCache = MemoryCacheUtil.Get<string>(keyCache);
if (objCache == null)
{
string deviceKey = await RedisHelper.HGetAsync<string>(HealthyDeviceKey, "data");
if (!string.IsNullOrEmpty(deviceKey))
{
MemoryCacheUtil.Set(keyCache, deviceKey, _configService.CacheDurationSeconds10);
return deviceKey;
}
}
return objCache;
}
catch (Exception ex)
{
_logger.LogError($"GetHealthyDeviceKey,key={imei},缓存异常,重新获取数据,{ex.Message}|{ex.Source}|{ex.StackTrace}");
string deviceKey = await RedisHelper.HGetAsync<string>(HealthyDeviceKey, "data");
if (!string.IsNullOrEmpty(deviceKey))
{
MemoryCacheUtil.Set(keyCache, deviceKey, _configService.CacheDurationSeconds10);
return deviceKey;
}
else
return "";
}
}

public async Task<SettingInfosItem> GetManufactorPushSettingHash(string imei, string manufactorId, int dataType)
{
if (string.IsNullOrWhiteSpace(manufactorId)) return null;
string keyCache = $"{manufactorId}_{dataType}_ManufactorPushSettingHash";
SettingInfosItem settingInfos = null;
try
{
var objCache = MemoryCacheUtil.Get<SettingInfosItem>(keyCache);
if (objCache == null)
{
var obj = await RedisHelper.HGetAsync<ManufactorPushSettingInfoModel>(CACHE_HASH_KEY_TELPO_GPSDEVICE_PUSHSITTTIGS, manufactorId);
if (obj != null)
{
if (obj.pushSettings.Any())
{
DateTime dt = DateTime.Now;
var settingsItem = obj.pushSettings.FirstOrDefault<PushSettingsItem>(x => x.dataType == dataType);
if (settingsItem != null)
{
if (settingsItem.settingInfos.Any())
{
foreach (var item in settingsItem.settingInfos)
{
DateTime startTime = DateTime.Parse($"{dt.Year}-{dt.Month}-{dt.Day} {item.pushStartTime}");
DateTime endTime = DateTime.Parse($"{dt.Year}-{dt.Month}-{dt.Day} {item.pushEndTime}");

if (item.pushFlag && (dt > startTime && dt < endTime))
{
settingInfos = item;
break;
}
}
}
}
}
if (settingInfos != null)
MemoryCacheUtil.Set(keyCache, settingInfos, _configService.CacheDurationSeconds10);
else
MemoryCacheUtil.Remove(keyCache);
}
}
else
settingInfos = objCache;
}
catch (Exception ex)
{
_logger.LogError($"GetManufactorPushSettingHash(imei={imei},dataType={dataType}),key={manufactorId},缓存异常,重新获取数据,{ex.Message}|{ex.Source}|{ex.StackTrace}");
var obj = await RedisHelper.HGetAsync<ManufactorPushSettingInfoModel>(CACHE_HASH_KEY_TELPO_GPSDEVICE_PUSHSITTTIGS, manufactorId);
if (obj != null)
{
if (obj.pushSettings.Any())
{
DateTime dt = DateTime.Now;
var settingsItem = obj.pushSettings.FirstOrDefault<PushSettingsItem>(x => x.dataType == dataType);
if (settingsItem != null)
{
if (settingsItem.settingInfos.Any())
{
foreach (var item in settingsItem.settingInfos)
{
DateTime startTime = DateTime.Parse($"{dt.Year}-{dt.Month}-{dt.Day} {item.pushStartTime}");
DateTime endTime = DateTime.Parse($"{dt.Year}-{dt.Month}-{dt.Day} {item.pushEndTime}");

if (item.pushFlag && (dt > startTime && dt < endTime))
{
settingInfos = item;
break;
}
}
}
}
}
if (settingInfos != null)
MemoryCacheUtil.Set(keyCache, settingInfos, _configService.CacheDurationSeconds10);
else
MemoryCacheUtil.Remove(keyCache);
}
}
return settingInfos;
}

}
}

+ 39
- 0
TelpoPush.Position.Worker/Service/Cache/SqlMapper.cs Vedi File

@@ -0,0 +1,39 @@
using Dapper;
using MySql.Data.MySqlClient;
using System.Data;
using TelpoPush.Position.Worker.Models.CacheTemplates;

namespace TelpoPush.Position.Worker.Service.Cache
{
public class SqlMapper
{
private readonly IConfiguration _config;
private static string gps_conn = "";
private static string telcommon_conn = "";
private static string healthy_conn = "";
public SqlMapper(IConfiguration config)
{
_config = config;
gps_conn = _config["ConnectionStrings:DB_Connection_String"].ToString();
telcommon_conn = _config["ConnectionStrings:Telpo_common_ConnString"].ToString();
healthy_conn = _config["ConnectionStrings:Telpo_Healthy_ConnString"].ToString();
}

public DeviceInfoModel DeviceInfo(string imei)
{
using (IDbConnection connection = new MySqlConnection(gps_conn))
{
var sql = @"SELECT d.device_id deviceId, p.nick_name deviceName,p.api_uid apiUid,d.serialno imei, d.org_uid orgId, d.active_status activeStatus,active_time activeTime FROM gps_device as d LEFT JOIN `gps_person` as p on p.device_id=d.device_id WHERE d.serialno=@imei";
return connection.QueryFirstOrDefault<DeviceInfoModel>(sql, new { imei });
}
}
public PersonModel PersonInfo(string imei)
{
using (IDbConnection connection = new MySqlConnection(gps_conn))
{
var sql = @"SELECT person_id personId, serialno, person_name personName, device_id deviceId, nick_name nickName, gender, height, weight, born_date bornDate, school, grade, class_name className, image_path imagePath,image_path_small imagePathSmall, age, create_time createTime, remarks, ishypertension, emotion,profession,regularity,chronic_disease chronicDisease,api_uid apiUid,api_remark apiRemark FROM gps_person WHERE serialno=@imei";
return connection.QueryFirstOrDefault<PersonModel>(sql, new { imei });
}
}
}
}

+ 9
- 0
TelpoPush.Position.Worker/Service/Mq/IKafkaService.cs Vedi File

@@ -0,0 +1,9 @@
using Confluent.Kafka;

namespace TelpoPush.Position.Worker.Service.Mq
{
public interface IKafkaService
{
Task SubscribeAsync(Action<string, string, Headers> messageFunc, CancellationToken cancellationToken);
}
}

+ 15
- 0
TelpoPush.Position.Worker/Service/Mq/KafkaHeader.cs Vedi File

@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TelpoPush.Position.Worker.Service.Mq
{
public class KafkaHeader
{
public static readonly string DataType = "DataType";
public static readonly string AlarmType = "AlarmType";
public static readonly string OperType = "OperType";
}
}

+ 135
- 0
TelpoPush.Position.Worker/Service/Mq/KafkaService.cs Vedi File

@@ -0,0 +1,135 @@
using Confluent.Kafka;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using TelpoPush.Position.Worker.Models.Config;

namespace TelpoPush.Position.Worker.Service.Mq
{
public class KafkaService : IKafkaService
{
private readonly ConsumerConfig _consumerConfig;
private readonly IHostEnvironment env;
private readonly ILogger<KafkaService> logger;
private readonly ServiceConfig _configService;
public KafkaService(ILogger<KafkaService> _logger, IHostEnvironment _env, IOptions<ServiceConfig> optConfigService)
{
_configService = optConfigService.Value;
env = _env;
logger = _logger;

_consumerConfig = new ConsumerConfig
{
BootstrapServers = _configService.KafkaBootstrapServers,
GroupId = _configService.KafkaGroupId,
EnableAutoCommit = false,
StatisticsIntervalMs = 5000,
SessionTimeoutMs = 6000,
AutoOffsetReset = AutoOffsetReset.Earliest,
EnablePartitionEof = true,
CancellationDelayMaxMs=1
};
}

public async Task SubscribeAsync(Action<string, string, Headers> messageFunc, CancellationToken cancellationToken)
{
List<string> topics = _configService.KafkaTopics;
using (var consumer = new ConsumerBuilder<Ignore, string>(_consumerConfig)
.SetErrorHandler((_, e) =>
{
Console.WriteLine($"Error: {e.Reason}");
logger.LogError($"Error: {e.Reason}");
})
.SetStatisticsHandler((_, json) =>
{
Console.WriteLine($" - {DateTime.Now:yyyy-MM-dd HH:mm:ss} > 消息监听中..");
logger.LogInformation($" - {DateTime.Now:yyyy-MM-dd HH:mm:ss} > 消息监听中..");
})
.SetPartitionsAssignedHandler((c, partitions) =>
{
string partitionsStr = string.Join(", ", partitions);
logger.LogInformation($" - 分配的 kafka 分区: {partitionsStr}");
})
.SetPartitionsRevokedHandler((c, partitions) =>
{
string partitionsStr = string.Join(", ", partitions);
Console.WriteLine($" - 回收了 kafka 的分区: {partitionsStr}");
logger.LogInformation($" - 回收了 kafka 分区: {partitionsStr}");
})
.Build())
{

consumer.Subscribe(topics);
try
{
while (true)
{
try
{
var consumeResult = consumer.Consume(cancellationToken);
int DataType = -1, AlarmType = -1, OperType = -1;
foreach (var item in consumeResult?.Headers)
{
if (item.Key == KafkaHeader.DataType)
DataType = BitConverter.ToInt32(item.GetValueBytes(), 0);
else if (item.Key == KafkaHeader.AlarmType)
AlarmType = BitConverter.ToInt32(item.GetValueBytes(), 0);
else if (item.Key == KafkaHeader.OperType)
OperType = BitConverter.ToInt32(item.GetValueBytes(), 0);
}
var Headers = new { DataType, AlarmType, OperType };
logger.LogInformation($"Consumed topic '{consumeResult.Topic}', message '{consumeResult.Message?.Value}' , headers '{JsonConvert.SerializeObject(Headers)}', at: '{consumeResult?.TopicPartitionOffset}'.");
if (consumeResult.IsPartitionEOF)
{
logger.LogInformation($" - {DateTime.Now:yyyy-MM-dd HH:mm:ss} 已经到底了:{consumeResult.Topic}, partition {consumeResult.Partition}, offset {consumeResult.Offset}.");
Console.WriteLine($" - {DateTime.Now:yyyy-MM-dd HH:mm:ss} 已经到底了:{consumeResult.Topic}, partition {consumeResult.Partition}, offset {consumeResult.Offset}.");
continue;
}

string messageResult = null;
Headers headers = null;
try
{
messageResult = consumeResult.Message.Value;
headers = consumeResult.Message.Headers;
}
catch (Exception ex)
{
var errorMessage = $" - {DateTime.Now:yyyy-MM-dd HH:mm:ss}【Exception 消息反序列化失败,Value:{consumeResult.Message.Value}】 :{ex.StackTrace?.ToString()}";
Console.WriteLine(errorMessage);
logger.LogError(errorMessage);
messageResult = null;
}
if (!string.IsNullOrEmpty(messageResult)/* && consumeResult.Offset % commitPeriod == 0*/)
{
string topic = consumeResult.Topic;
messageFunc(topic, messageResult, headers);

try
{
consumer.Commit(consumeResult);
}
catch (KafkaException e)
{
Console.WriteLine(e.Message);
}
}
}
catch (ConsumeException e)
{
logger.LogError($"Consume error: {e.Error.Reason}");
Console.WriteLine($"Consume error: {e.Error.Reason}");
}
}
}
catch (OperationCanceledException)
{
logger.LogError("Closing consumer.");
Console.WriteLine("Closing consumer.");
consumer.Close();
}
}
await Task.CompletedTask;
}
}
}

+ 77
- 0
TelpoPush.Position.Worker/Service/Mq/MessageProducer.cs Vedi File

@@ -0,0 +1,77 @@
using Confluent.Kafka;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using TelpoPush.Position.Worker.Models.Config;

namespace TelpoPush.Position.Worker.Service.Mq
{
/// <summary>
/// 消息生产者
/// </summary>
public class MessageProducer
{
private readonly ILogger<MessageProducer> _logger;
private readonly ServiceConfig _configService;
private readonly IProducer<Null, string> _producer;

public MessageProducer(ILogger<MessageProducer> logger, IOptions<ServiceConfig> optConfigService)
{
_logger = logger;
_configService = optConfigService.Value;

var config = new ProducerConfig
{
BootstrapServers = _configService.KafkaBootstrapServers,
EnableIdempotence = true,
Acks = Acks.All,
//LingerMs=5000,
//BatchNumMessages =1000,
//BatchSize=32768,
//CompressionType= CompressionType.Lz4,
MessageSendMaxRetries = 3
};
_producer = new ProducerBuilder<Null, string>(config).Build();
}

public Headers CreateHeader(Dictionary<string, int> pair = null)
{
if (pair == null)
{
return null;
}
else
{
Headers headers = new Headers();

foreach (var item in pair)
{
headers.Add(item.Key, BitConverter.GetBytes(item.Value));
}
return headers;
}
}

public async Task ProduceAsync(List<TopicModel> topic, object message)
{
try
{
foreach (var item in topic)
{
// producer = new ProducerBuilder<Null, string>(config).Build();
await _producer.ProduceAsync(item.Topic, new Message<Null, string>
{
Headers = item.Headers,
Value = JsonConvert.SerializeObject(message)
});
}
}
catch (ProduceException<Null, string> e)
{
_logger.LogError($"推送到kafka失败,topic: {topic},\n message:{JsonConvert.SerializeObject(message)}: \n{e.Error.Reason}");
}
}



}
}

+ 70
- 0
TelpoPush.Position.Worker/Service/Mq/MqProcessMessage.cs Vedi File

@@ -0,0 +1,70 @@
using Newtonsoft.Json;
using TelpoPush.Position.Worker.Models.Enum;
using TelpoPush.Position.Worker.Models.MqTemplates;

namespace TelpoPush.Position.Worker.Service.Mq
{
public class MqProcessMessage
{
private readonly ILogger<MqProcessMessage> _logger;
private readonly MessageProducer _messageProducer;

public MqProcessMessage(ILogger<MqProcessMessage> logger, MessageProducer producer)
{
_logger = logger;
_messageProducer = producer;
}

public async Task ProcessProperty(string imei, BaseModel model, HeadersDto headers)
{
List<TopicModel> ls = new List<TopicModel>();

ls.Add(new TopicModel()
{
Topic = "topic.push.property",
Headers = _messageProducer.CreateHeader(new Dictionary<string, int>
{
{MqHeader.DataType,headers.DataType.Value }
})
});
await _messageProducer.ProduceAsync(ls, model);
_logger.LogInformation($"【成功】Third推送(topic.property):IMEI<{imei}>,pushData:{JsonConvert.SerializeObject(model)}");
}

public async Task ProcessDataPushServer(string imei, object model, Dictionary<string, int> headers, string tag)
{
List<TopicModel> ls = new List<TopicModel>();
ls.Add(new TopicModel()
{
Topic = "topic.push",
Headers = _messageProducer.CreateHeader(headers)
});
await _messageProducer.ProduceAsync(ls, model);
_logger.LogInformation($"【{tag}-成功】Third推送(topic.push):IMEI<{imei}>,Header<{JsonConvert.SerializeObject(headers)}>,pushData:{JsonConvert.SerializeObject(model)}");
}

public async Task ProcessFenceServer(string imei, object model, Dictionary<string, int> headers, string tag)
{
List<TopicModel> ls = new List<TopicModel>();
ls.Add(new TopicModel()
{
Topic = "topic.push.fence",
Headers = _messageProducer.CreateHeader(headers)
});
await _messageProducer.ProduceAsync(ls, model);
_logger.LogInformation($"【{tag}-成功】Third推送(topic.push.fence):IMEI<{imei}>,Header<{JsonConvert.SerializeObject(headers)}>,pushData:{JsonConvert.SerializeObject(model)}");
}

public async Task ProcessThirdhServer(string imei, object model, Dictionary<string, int> headers, string tag)
{
List<TopicModel> ls = new List<TopicModel>();
ls.Add(new TopicModel()
{
Topic = "topic.push.third",
Headers = _messageProducer.CreateHeader(headers)
});
await _messageProducer.ProduceAsync(ls, model);
_logger.LogInformation($"【{tag}-成功】Third推送(topic.push.third):IMEI<{imei}>,Header<{JsonConvert.SerializeObject(headers)}>,pushData:{JsonConvert.SerializeObject(model)}");
}
}
}

+ 15
- 0
TelpoPush.Position.Worker/Service/Mq/TopicModel.cs Vedi File

@@ -0,0 +1,15 @@
using Confluent.Kafka;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TelpoPush.Position.Worker.Service.Mq
{
public class TopicModel
{
public string Topic { get; set; }
public Headers Headers { get; set; }
}
}

+ 4
- 0
TelpoPush.Position.Worker/TelpoPush.Position.Worker.csproj Vedi File

@@ -23,4 +23,8 @@
<PackageReference Include="MySql.Data" Version="8.4.0" />
<PackageReference Include="TelpoDataService.Util" Version="1.6.9.28-beta1" />
</ItemGroup>

<ItemGroup>
<Folder Include="Service\Biz\" />
</ItemGroup>
</Project>

+ 6
- 5
TelpoPush.Position.Worker/Worker.cs Vedi File

@@ -1,22 +1,23 @@
using TelpoPush.Position.Worker.Handlers;

namespace TelpoPush.Position.Worker
{
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;
KafkaSubscribe _kafkaSubscribe;

public Worker(ILogger<Worker> logger)
public Worker(ILogger<Worker> logger, KafkaSubscribe kafkaSubscribe)
{
_logger = logger;
_kafkaSubscribe = kafkaSubscribe;
}

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
if (_logger.IsEnabled(LogLevel.Information))
{
_logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
}
await _kafkaSubscribe.SubscribeAsync();
await Task.Delay(1000, stoppingToken);
}
}


Loading…
Annulla
Salva