@@ -0,0 +1,184 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Text; | |||
namespace HealthMonitor.Common | |||
{ | |||
public class SafeType | |||
{ | |||
/// <summary> | |||
/// Safes the int. | |||
/// </summary> | |||
/// <param name="o">The o.</param> | |||
/// <returns></returns> | |||
public static int SafeInt(object o) | |||
{ | |||
int returnValue; | |||
try | |||
{ | |||
returnValue = Convert.ToInt32(o); | |||
} | |||
catch | |||
{ | |||
returnValue = 0; | |||
} | |||
return returnValue; | |||
} | |||
/// <summary> | |||
/// Safes the int. | |||
/// </summary> | |||
/// <param name="o">The o.</param> | |||
/// <returns></returns> | |||
public static long SafeInt64(object o) | |||
{ | |||
long returnValue; | |||
try | |||
{ | |||
returnValue = Convert.ToInt64(o); | |||
} | |||
catch | |||
{ | |||
returnValue = 0; | |||
} | |||
return returnValue; | |||
} | |||
/// <summary> | |||
/// Safes the string. | |||
/// </summary> | |||
/// <param name="o">The o.</param> | |||
/// <returns></returns> | |||
public static string SafeString(object o) | |||
{ | |||
string? returnValue; | |||
try | |||
{ | |||
returnValue = Convert.ToString(o); | |||
} | |||
catch | |||
{ | |||
returnValue = ""; | |||
} | |||
return returnValue!; | |||
} | |||
/// <summary> | |||
/// Safes the bool. | |||
/// </summary> | |||
/// <param name="o">The o.</param> | |||
/// <returns></returns> | |||
public static bool SafeBool(object o) | |||
{ | |||
bool returnValue; | |||
try | |||
{ | |||
returnValue = Convert.ToBoolean(o); | |||
} | |||
catch | |||
{ | |||
returnValue = false; | |||
} | |||
return returnValue; | |||
} | |||
public static double SafeDouble(object o) | |||
{ | |||
double returnValue; | |||
try | |||
{ | |||
returnValue = Convert.ToDouble(o); | |||
} | |||
catch | |||
{ | |||
returnValue = 0; | |||
} | |||
return returnValue; | |||
} | |||
/// <summary> | |||
/// Safes the decimal. | |||
/// </summary> | |||
/// <param name="o">The o.</param> | |||
/// <returns></returns> | |||
public static decimal SafeDecimal(object o) | |||
{ | |||
decimal returnValue; | |||
try | |||
{ | |||
returnValue = Convert.ToDecimal(o); | |||
} | |||
catch | |||
{ | |||
returnValue = 0.0M; | |||
} | |||
return returnValue; | |||
} | |||
/// <summary> | |||
/// Safes the time. | |||
/// </summary> | |||
/// <param name="o">The o.</param> | |||
/// <returns></returns> | |||
public static DateTime SafeTime(object o) | |||
{ | |||
var returnValue = DateTime.Parse("1900-01-01"); | |||
try | |||
{ | |||
returnValue = Convert.ToDateTime(o); | |||
} | |||
catch | |||
{ | |||
returnValue = DateTime.Parse("1900-01-01"); | |||
} | |||
return returnValue; | |||
} | |||
/// <summary> | |||
/// OBD时间转换 | |||
/// </summary> | |||
/// <param name="o"></param> | |||
/// <returns></returns> | |||
public static DateTime OBDSafeTime(object o) | |||
{ | |||
var returnValue = DateTime.Parse("1900-01-01"); | |||
try | |||
{ | |||
returnValue = Convert.ToDateTime(o); | |||
} | |||
catch | |||
{ | |||
returnValue = DateTime.UtcNow; | |||
} | |||
return returnValue; | |||
} | |||
/// <summary> | |||
/// 是否为数字 | |||
/// </summary> | |||
/// <param name="str"></param> | |||
/// <returns></returns> | |||
public static bool IsNumber(string str) | |||
{ | |||
int n; | |||
if (int.TryParse(str, out n)) | |||
{ | |||
return true; | |||
} | |||
else | |||
{ | |||
return false; | |||
} | |||
} | |||
} | |||
} |
@@ -2,6 +2,7 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Reflection; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
@@ -30,11 +31,38 @@ namespace HealthMonitor.Model.Cache | |||
[JsonProperty("gender")] | |||
public bool Gender { get; set; } = default!; | |||
// 用于存储真实值的字段 | |||
private bool gender; | |||
//public int Age | |||
//{ | |||
// get | |||
// { | |||
// DateTime bornDate = DateTime.Parse(BornDate); | |||
// int age = DateTime.Today.Year - bornDate.Year; | |||
// if (bornDate > DateTime.Today.AddYears(-age)) age--; | |||
// return age; | |||
// } | |||
// set | |||
// { | |||
// BornDate = DateTime.Today.AddYears(-value).ToShortDateString(); | |||
// } | |||
//} | |||
[JsonProperty("age")] | |||
public int Age { get; set; } = default!; | |||
[JsonProperty("bornDate")] | |||
public string BornDate { get; set; } = default!; | |||
public DateTime? BornDate { get; set; } = default!; | |||
[JsonProperty("height")] | |||
public int Height { get; set; } = default!; | |||
[JsonProperty("weight")] | |||
public int Weight { get; set; } = default!; | |||
} | |||
public class GpsDevicePerson | |||
@@ -216,7 +216,7 @@ namespace HealthMonitor.Service.Biz.db | |||
var res = JsonConvert.DeserializeObject<TDengineRestResBase>(result!); | |||
List<dynamic> data = res?.Data!; | |||
sql = $"SELECT AVG({field}) FROM {_configTDengineService.DB}.{tbName} WHERE {condition} AND {field} < {data[0][0]} and {field} > {data[0][1]}"; | |||
sql = $"SELECT AVG({field}) FROM {_configTDengineService.DB}.{tbName} WHERE {condition} AND {field} < { (data.Count.Equals(0)? 0: data[0][0]) } and {field} > {(data.Count.Equals(0) ? 0 : data[0][1])}"; | |||
result = await GernalRestSqlResText(sql); | |||
res = JsonConvert.DeserializeObject<TDengineRestResBase>(result!); | |||
data = res?.Data!; | |||
@@ -0,0 +1,68 @@ | |||
using HealthMonitor.Core.Dal; | |||
using HealthMonitor.Util.Entities.HealthMonitor; | |||
using Microsoft.Extensions.Logging; | |||
using Newtonsoft.Json; | |||
using System.Security.Cryptography; | |||
using System.Text; | |||
namespace HealthMonitor.Service.Cache | |||
{ | |||
public class BloodPressReferenceValueCacheManager | |||
{ | |||
private readonly ILogger<BloodPressReferenceValueCacheManager> _logger; | |||
protected readonly IHealthMonitorDataAccessor _dataAccessor; | |||
private static object _syncLocker = new object(); | |||
private MD5 _md5; | |||
private const string CACHE_KEY_BP_REF_VALUE = "HM_REF_Val_"; | |||
public BloodPressReferenceValueCacheManager( | |||
IHealthMonitorDataAccessor dataAccessor, | |||
ILogger<BloodPressReferenceValueCacheManager> logger | |||
) | |||
{ | |||
_md5 = MD5.Create(); | |||
_dataAccessor = dataAccessor; | |||
_logger = logger; | |||
} | |||
public async Task<HmBloodPressReferenceValue> GetBloodPressReferenceValueAsync(int age, int gender,bool isHypertension) | |||
{ | |||
var hashModel = new | |||
{ | |||
Index = $"{age}-{gender}-{isHypertension}" | |||
}; | |||
var hash = ComputeHash(hashModel); | |||
var cacheKey = CACHE_KEY_BP_REF_VALUE + $"{hash}"; | |||
var bpRef = await RedisHelper.GetAsync<HmBloodPressReferenceValue>(cacheKey).ConfigureAwait(false); | |||
if (bpRef == null) | |||
{ | |||
bpRef = _dataAccessor | |||
.GetFirstOrDefault<HmBloodPressReferenceValue>( | |||
i => | |||
i.Age.Equals(age) && | |||
i.Gender.Equals(gender) && | |||
i.Hypertension.Equals(isHypertension) | |||
); | |||
; | |||
await RedisHelper.SetAsync(cacheKey, JsonConvert.SerializeObject(bpRef)); | |||
} | |||
return bpRef; | |||
} | |||
private string ComputeHash(object data) | |||
{ | |||
lock (_syncLocker) | |||
{ | |||
var value = JsonConvert.SerializeObject(data); | |||
value = Convert.ToBase64String(_md5.ComputeHash(Encoding.UTF8.GetBytes(value))); | |||
return value; | |||
} | |||
} | |||
} | |||
} |
@@ -15,11 +15,10 @@ using TelpoDataService.Util.QueryObjects; | |||
namespace HealthMonitor.Service.Cache | |||
{ | |||
public abstract class MyHelper2 : RedisHelper<MyHelper2> { } | |||
public class PersonCacheManager | |||
{ | |||
private const int DEFAULT_DURATION_SECONDS = 1200; //20分钟 | |||
private const string CACHE_KEY_PERSON = "Person_"; | |||
//注意要改用 redis DB7 数据库 ,Prefix=TELPO | |||
private const string CACHE_HASH_KEY_GPSDEVICEPERSON = "#GPSDEVICE_PERSON_HASH"; | |||
//注意要改用 redis DB7 数据库 ,Prefix=TELPO | |||
@@ -27,47 +26,46 @@ namespace HealthMonitor.Service.Cache | |||
private readonly GpsCardAccessorClient<GpsPerson> _personApiClient; | |||
private readonly ILogger<PersonCacheManager> _logger; | |||
private readonly IOptions<RedisConfig> _optConfigRedis; | |||
public PersonCacheManager(GpsCardAccessorClient<GpsPerson> personApiClient, | |||
ILogger<PersonCacheManager> logger, IOptions<RedisConfig> optConfigRedis) | |||
public PersonCacheManager( | |||
GpsCardAccessorClient<GpsPerson> personApiClient, | |||
ILogger<PersonCacheManager> logger) | |||
{ | |||
_optConfigRedis = optConfigRedis; | |||
_personApiClient = personApiClient; | |||
_logger = logger; | |||
} | |||
public async Task<GpsPerson?> GetPersonBySerialNoAsync(string messageId, string sn) | |||
{ | |||
if (string.IsNullOrWhiteSpace(sn)) return null; | |||
// 切换redis DB7 数据库和前缀 "TELPO" | |||
_optConfigRedis.Value.DefaultDatabase = 7; | |||
_optConfigRedis.Value.Prefix = "TELPO"; | |||
//public async Task<GpsPerson?> GetPersonBySerialNoAsync(string messageId, string sn) | |||
//{ | |||
// if (string.IsNullOrWhiteSpace(sn)) return null; | |||
// // 切换redis DB7 数据库和前缀 "TELPO" | |||
// 增加容错,防止Redis宕机造成业务中断 | |||
try | |||
{ | |||
using var csRedisDb7 = new CSRedisClient(_optConfigRedis.Value.ToString()); | |||
return await csRedisDb7.HGetAsync<GpsPerson>(CACHE_HASH_KEY_GPSDEVICEPERSON, sn); | |||
} | |||
catch (Exception ex) | |||
{ | |||
_logger.LogWarning($"Redis发生异常,将直接读取MySQL数据库,构造新实例:{ex.Message}, {ex.StackTrace}"); | |||
var param = new GeneralParam | |||
{ | |||
Filters = new List<QueryFilterCondition> | |||
{ | |||
new QueryFilterCondition | |||
{ | |||
Key=nameof(GpsPerson.Serialno), | |||
Value=sn, | |||
ValueType=QueryValueTypeEnum.String, | |||
Operator=QueryOperatorEnum.Equal | |||
} | |||
} | |||
}!; | |||
return await _personApiClient.GetFirstAsync(param, new RequestHeader { RequestId = messageId }).ConfigureAwait(false); | |||
} | |||
} | |||
// // 增加容错,防止Redis宕机造成业务中断 | |||
// try | |||
// { | |||
// return await csRedisDb7.HGetAsync<GpsPerson>(CACHE_HASH_KEY_GPSDEVICEPERSON, sn); | |||
// } | |||
// catch (Exception ex) | |||
// { | |||
// _logger.LogWarning($"Redis发生异常,将直接读取MySQL数据库,构造新实例:{ex.Message}, {ex.StackTrace}"); | |||
// var param = new GeneralParam | |||
// { | |||
// Filters = new List<QueryFilterCondition> | |||
// { | |||
// new QueryFilterCondition | |||
// { | |||
// Key=nameof(GpsPerson.Serialno), | |||
// Value=sn, | |||
// ValueType=QueryValueTypeEnum.String, | |||
// Operator=QueryOperatorEnum.Equal | |||
// } | |||
// } | |||
// }!; | |||
// return await _personApiClient.GetFirstAsync(param, new RequestHeader { RequestId = messageId }).ConfigureAwait(false); | |||
// } | |||
//} | |||
/// <summary> | |||
/// 读取个人信息(血压使用)注意要改用 redis DB7 | |||
@@ -78,10 +76,7 @@ namespace HealthMonitor.Service.Cache | |||
public async Task<GpsDevicePerson?> GetDeviceGpsPersonCacheBySerialNoAsync(string messageId, string sn) | |||
{ | |||
if (string.IsNullOrWhiteSpace(sn)) return null; | |||
// 切换redis DB7 数据库和前缀 "TELPO" | |||
_optConfigRedis.Value.DefaultDatabase = 7; | |||
_optConfigRedis.Value.Prefix = "TELPO"; | |||
// 增加容错,防止Redis宕机造成业务中断 | |||
try | |||
@@ -16,6 +16,7 @@ | |||
<ItemGroup> | |||
<ProjectReference Include="..\HealthMonitor.Common\HealthMonitor.Common.csproj" /> | |||
<ProjectReference Include="..\HealthMonitor.Core\HealthMonitor.Core.csproj" /> | |||
<ProjectReference Include="..\HealthMonitor.Model\HealthMonitor.Model.csproj" /> | |||
<ProjectReference Include="..\HealthMonitor.Util\HealthMonitor.Util.csproj" /> | |||
</ItemGroup> | |||
@@ -1,13 +1,16 @@ | |||
using HealthMonitor.Core.Dal; | |||
using HealthMonitor.Common; | |||
using HealthMonitor.Core.Dal; | |||
using HealthMonitor.Model.Cache; | |||
using HealthMonitor.Service.Biz.db; | |||
using HealthMonitor.Service.Cache; | |||
using HealthMonitor.Util.Entities.HealthMonitor; | |||
using HealthMonitor.WebApi.Configs; | |||
using Microsoft.AspNetCore.Mvc; | |||
using Newtonsoft.Json; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Security.Cryptography; | |||
using System.Text; | |||
using System.Text.RegularExpressions; | |||
using System.Threading.Tasks; | |||
@@ -21,16 +24,22 @@ namespace HealthMonitor.WebApi.Controllers.HealthMonitor | |||
[ApiController] | |||
public class HmBloodPressController:ControllerBase | |||
{ | |||
protected readonly ILogger _logger; | |||
private readonly TDengineService _serviceTDengine; | |||
private readonly PersonCacheManager _personCacheMgr; | |||
private readonly BloodPressReferenceValueCacheManager _bpRefValCacheManager; | |||
protected readonly IHealthMonitorDataAccessor _dataAccessor; | |||
public HmBloodPressController | |||
( | |||
ILogger<HmBloodPressController> logger, | |||
TDengineService serviceDengine, | |||
PersonCacheManager personCacheMgr, | |||
PersonCacheManager personCacheMgr, | |||
BloodPressReferenceValueCacheManager bpRefValCacheManager, | |||
IHealthMonitorDataAccessor dataAccessor | |||
) | |||
{ | |||
@@ -38,32 +47,70 @@ namespace HealthMonitor.WebApi.Controllers.HealthMonitor | |||
_serviceTDengine = serviceDengine; | |||
_personCacheMgr = personCacheMgr; | |||
_dataAccessor = dataAccessor; | |||
_bpRefValCacheManager = bpRefValCacheManager; | |||
} | |||
[HttpPost] | |||
public async Task<IActionResult> AddAsync([FromHeader] string requestId, HisGpsBloodPress bp1) | |||
public async Task<IActionResult> AddAsync([FromHeader] string requestId,[FromBody] HisGpsBloodPress bp) | |||
{ | |||
HisGpsBloodPress bp = new() | |||
{ | |||
BloodPressId = "261850cb-ce91-4003-8c63-a1f8f50d6495", | |||
MessageId = "1670682284342246914", | |||
Serialno = "861281060083627", | |||
SystolicValue = 114, | |||
DiastolicValue = 79, | |||
CreateTime = DateTime.Parse("2023-06-19 14:37:53"), | |||
LastUpdate = DateTime.Parse("2023-06-19 14:26:52"), | |||
Method = 1, | |||
IsDisplay = 1 | |||
}; | |||
// | |||
//HisGpsBloodPress bp = new() | |||
//{ | |||
// BloodPressId = "261850cb-ce91-4003-8c63-a1f8f50d6495", | |||
// MessageId = "1670682284342246914", | |||
// Serialno = "861281060083627", | |||
// SystolicValue = 114, | |||
// DiastolicValue = 79, | |||
// CreateTime = DateTime.Parse("2023-06-19 14:37:53"), | |||
// LastUpdate = DateTime.Parse("2023-06-19 14:26:52"), | |||
// Method = 1, | |||
// IsDisplay = 1 | |||
//}; | |||
// | |||
//861281060081969 | |||
//HisGpsBloodPress bp = new() | |||
//{ | |||
// BloodPressId = "7df62202-8d49-4f91-90da-25a4036c26fb", | |||
// MessageId = "1670992704491900929", | |||
// Serialno = "861281060081969", | |||
// SystolicValue = 110, | |||
// DiastolicValue = 72, | |||
// CreateTime = DateTime.Parse("2023-06-20 11:11:31"), | |||
// LastUpdate = DateTime.Parse("2023-06-20 10:20:40"), | |||
// Method = 1, | |||
// IsDisplay = 1 | |||
//}; | |||
// 861281060086380 | |||
//var aggregate = await _serviceTDengine.GetAvgExceptMaxMinValue("diastolic_value", "hm_bloodpress", $"ts>='{DateTime.Now.AddDays(-7):yyyy-MM-ddTHH:mm:ss.fffZ}' and ts <='{DateTime.Now:yyyy-MM-ddTHH:mm:ss.fffZ}' and serialno='861281060083627' and systolic_value < {120} and diastolic_value >{80}"); | |||
//+++++++++++++++++++++++++++++++ | |||
#region 获取个人信息 | |||
var person = await _personCacheMgr.GetDeviceGpsPersonCacheBySerialNoAsync(bp.MessageId, bp.Serialno).ConfigureAwait(false); | |||
// 验证这个信息是否存在 | |||
if (person == null || person?.Person.BornDate == null) return Ok(false); | |||
// 验证年龄是否在范围 (2 - 120) | |||
var age = SafeType.SafeInt(DateTime.Today.Year - person?.Person.BornDate!.Value.Year!); | |||
if (age < 1 || age > 120) return Ok(false); | |||
var gender = person?.Person.Gender == true ? 1 : 2; | |||
var isHypertension = SafeType.SafeBool(person?.Person.Ishypertension!); | |||
var height = SafeType.SafeDouble(person?.Person.Height!); | |||
var weight = SafeType.SafeDouble(person?.Person.Weight!); | |||
#endregion | |||
#region 插入当次BP数据 | |||
// 保存到TDengine | |||
/** | |||
var sql = $"INSERT INTO health_monitor.hm_bloodpress VALUES(" + | |||
var bpSql = $"INSERT INTO health_monitor.hm_bloodpress VALUES(" + | |||
$"'{bp.LastUpdate:yyyy-MM-ddTHH:mm:ss.fffZ}'," + | |||
$"'{bp.BloodPressId}'," + | |||
$"'{bp.MessageId}'," + | |||
@@ -75,52 +122,19 @@ namespace HealthMonitor.WebApi.Controllers.HealthMonitor | |||
$"{bp.Method}," + | |||
$"{bp.IsDisplay == 1})"; | |||
// var sql = "select * from health_monitor.hm_bloodpress order by last_update desc"; | |||
var res=await _serviceTDengine.GernalRestSql(sql); | |||
*/ | |||
#endregion | |||
#region 获取个人信息 | |||
var person = await _personCacheMgr.GetDeviceGpsPersonCacheBySerialNoAsync(bp.MessageId, bp.Serialno).ConfigureAwait(false); | |||
await _serviceTDengine.GernalRestSql(bpSql); | |||
//var person = new GpsDevicePerson() | |||
//{ | |||
// Time = DateTime.Now, | |||
// Person = new Person() | |||
// { | |||
// Age = 43, | |||
// DeviceId = "111111", | |||
// Gender = true, | |||
// Ishypertension = false, | |||
// SerialNo = bp.Serialno | |||
// } | |||
//}; | |||
#endregion | |||
#region 计算增量值 | |||
// 获取个人资料 redis | |||
var age = person?.Person.Age; | |||
var gender = person?.Person.Gender == true ? 1 : 2; | |||
var isHypertension = person?.Person.Ishypertension; | |||
var height = 0f; | |||
var weight = 0f; | |||
// 获取标定值 mysql | |||
var bpRef = _dataAccessor | |||
.GetFirstOrDefault<HmBloodPressReferenceValue>( | |||
i => | |||
i.Age.Equals(age) && | |||
i.Gender.Equals(gender) && | |||
i.Hypertension.Equals(isHypertension) | |||
); | |||
var systolicRefValue = bpRef.Systolic;//? | |||
var diastolicRefValue = bpRef.Diastolic;//? | |||
var bpRef = await _bpRefValCacheManager.GetBloodPressReferenceValueAsync(age, gender, isHypertension); | |||
var systolicRefValue = bpRef?.Systolic;//? | |||
var diastolicRefValue = bpRef?.Diastolic;//? | |||
// 获取历史数据 | |||
DateTime now = DateTime.Now; | |||
DateTime startTime = now.AddDays(-2); | |||
DateTime startTime = now.AddDays(-30); | |||
DateTime endTime = now; | |||
// 计算去除最大值和最小值和异常值的平均值 | |||
@@ -131,8 +145,8 @@ namespace HealthMonitor.WebApi.Controllers.HealthMonitor | |||
var avgOffset = 0.25M; | |||
// 增量值 | |||
var systolicInc = (int)(systolicRefValue - systolicAvg * avgOffset); | |||
var diastolicInc = (int)(diastolicRefValue - systolicAvg * avgOffset); | |||
var systolicInc = systolicAvg.Equals(0M) ? 0:(int)(systolicRefValue - systolicAvg * avgOffset)!; | |||
var diastolicInc = diastolicAvg.Equals(0M) ? 0 : (int)(diastolicRefValue - diastolicAvg * avgOffset)!; | |||
#endregion | |||
@@ -157,11 +171,7 @@ namespace HealthMonitor.WebApi.Controllers.HealthMonitor | |||
var res = await _serviceTDengine.GernalRestSql(sql); | |||
#endregion | |||
return Ok(true); | |||
return Ok(res); | |||
} | |||
} | |||
} |
@@ -142,7 +142,8 @@ namespace HealthMonitor.WebApi | |||
#region Cache | |||
builder.Services | |||
.AddSingleton<PersonCacheManager>(); | |||
.AddSingleton<PersonCacheManager>() | |||
.AddSingleton<BloodPressReferenceValueCacheManager>(); | |||
#endregion | |||