2025-04-15 16:05:07 +08:00
|
|
|
|
using JiShe.CollectBus.Common.Consts;
|
2025-04-14 16:41:41 +08:00
|
|
|
|
using JiShe.CollectBus.Common.Enums;
|
2025-04-15 15:49:51 +08:00
|
|
|
|
using JiShe.CollectBus.Common.Extensions;
|
2025-04-15 16:05:07 +08:00
|
|
|
|
using JiShe.CollectBus.Common.Helpers;
|
2025-03-12 09:58:37 +08:00
|
|
|
|
using JiShe.CollectBus.FreeRedisProvider;
|
|
|
|
|
|
using JiShe.CollectBus.FreeSql;
|
2024-12-19 16:07:07 +08:00
|
|
|
|
using JiShe.CollectBus.Localization;
|
|
|
|
|
|
using Microsoft.AspNetCore.Mvc;
|
2025-04-14 16:41:41 +08:00
|
|
|
|
using System;
|
2025-03-12 09:58:37 +08:00
|
|
|
|
using System.Collections.Generic;
|
2025-04-14 23:42:18 +08:00
|
|
|
|
using System.Linq;
|
2025-03-12 09:58:37 +08:00
|
|
|
|
using System.Threading.Tasks;
|
2024-12-19 16:07:07 +08:00
|
|
|
|
using Volo.Abp.Application.Services;
|
|
|
|
|
|
|
|
|
|
|
|
namespace JiShe.CollectBus;
|
|
|
|
|
|
|
|
|
|
|
|
[ApiExplorerSettings(GroupName = CollectBusDomainSharedConsts.Business)]
|
|
|
|
|
|
public abstract class CollectBusAppService : ApplicationService
|
|
|
|
|
|
{
|
|
|
|
|
|
public IFreeSqlProvider SqlProvider => LazyServiceProvider.LazyGetRequiredService<IFreeSqlProvider>();
|
2025-03-17 08:35:19 +08:00
|
|
|
|
protected IFreeRedisProvider FreeRedisProvider => LazyServiceProvider.LazyGetService<IFreeRedisProvider>()!;
|
2024-12-19 16:07:07 +08:00
|
|
|
|
|
2025-04-15 15:49:51 +08:00
|
|
|
|
|
2024-12-19 16:07:07 +08:00
|
|
|
|
protected CollectBusAppService()
|
|
|
|
|
|
{
|
|
|
|
|
|
LocalizationResource = typeof(CollectBusResource);
|
|
|
|
|
|
ObjectMapperContext = typeof(CollectBusApplicationModule);
|
2025-04-14 16:41:41 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Lua脚本批量获取缓存的表计信息
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <typeparam name="T">表信息数据对象</typeparam>
|
|
|
|
|
|
/// <param name="redisKeys">采集频率对应的缓存Key集合</param>
|
|
|
|
|
|
/// <param name="systemType"><see cref="SystemTypeConst"/> 系统类型</param>
|
|
|
|
|
|
/// <param name="serverTagName">服务器标识</param>
|
|
|
|
|
|
/// <param name="timeDensity">采集频率,1分钟、5分钟、15分钟</param>
|
|
|
|
|
|
/// <param name="meterType"><see cref="MeterTypeEnum"/> 表计类型</param>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
protected async Task<Dictionary<string, Dictionary<string, T>>> GetMeterRedisCacheDictionaryData<T>(string[] redisKeys, string systemType, string serverTagName, string timeDensity, MeterTypeEnum meterType) where T : class
|
|
|
|
|
|
{
|
2025-04-14 23:42:18 +08:00
|
|
|
|
if (redisKeys == null || redisKeys.Length <= 0 || string.IsNullOrWhiteSpace(systemType) || string.IsNullOrWhiteSpace(serverTagName) || string.IsNullOrWhiteSpace(timeDensity))
|
2025-04-14 16:41:41 +08:00
|
|
|
|
{
|
|
|
|
|
|
throw new Exception($"{nameof(GetMeterRedisCacheDictionaryData)} 获取缓存的表计信息失败,参数异常,-101");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-14 23:42:18 +08:00
|
|
|
|
var meterInfos = new Dictionary<string, Dictionary<string, T>>();
|
2025-04-14 16:41:41 +08:00
|
|
|
|
var luaScript = @"
|
2025-04-14 23:42:18 +08:00
|
|
|
|
local results = {}
|
|
|
|
|
|
for i, key in ipairs(KEYS) do
|
|
|
|
|
|
local data = redis.call('HGETALL', key)
|
|
|
|
|
|
results[i] = {key, data}
|
|
|
|
|
|
end
|
|
|
|
|
|
return results";
|
|
|
|
|
|
|
|
|
|
|
|
// 分页参数:每页处理10000个键
|
|
|
|
|
|
int pageSize = 10000;
|
|
|
|
|
|
int totalPages = (int)Math.Ceiling(redisKeys.Length / (double)pageSize);
|
2025-04-14 16:41:41 +08:00
|
|
|
|
|
2025-04-14 23:42:18 +08:00
|
|
|
|
for (int page = 0; page < totalPages; page++)
|
2025-04-14 16:41:41 +08:00
|
|
|
|
{
|
2025-04-14 23:42:18 +08:00
|
|
|
|
// 分页获取当前批次的键
|
|
|
|
|
|
var batchKeys = redisKeys
|
|
|
|
|
|
.Skip(page * pageSize)
|
|
|
|
|
|
.Take(pageSize)
|
|
|
|
|
|
.ToArray();
|
|
|
|
|
|
|
|
|
|
|
|
// 执行Lua脚本获取当前批次数据
|
|
|
|
|
|
var merterResult = await FreeRedisProvider.Instance.EvalAsync(luaScript, batchKeys);
|
|
|
|
|
|
if (merterResult == null)
|
2025-04-14 16:41:41 +08:00
|
|
|
|
{
|
2025-04-14 23:42:18 +08:00
|
|
|
|
throw new Exception($"{nameof(GetMeterRedisCacheDictionaryData)} 获取缓存的表计信息失败,第 {page + 1} 页数据未返回,-102");
|
|
|
|
|
|
}
|
2025-04-14 16:41:41 +08:00
|
|
|
|
|
2025-04-14 23:42:18 +08:00
|
|
|
|
// 解析当前批次的结果
|
|
|
|
|
|
if (merterResult is object[] arr)
|
|
|
|
|
|
{
|
|
|
|
|
|
foreach (object[] item in arr)
|
2025-04-14 16:41:41 +08:00
|
|
|
|
{
|
2025-04-14 23:42:18 +08:00
|
|
|
|
string key = (string)item[0];
|
|
|
|
|
|
object[] fieldsAndValues = (object[])item[1];
|
2025-04-16 17:36:46 +08:00
|
|
|
|
var redisCacheKey = $"{string.Format(RedisConst.CacheMeterInfoHashKey, systemType, serverTagName, meterType, timeDensity)}";
|
2025-04-14 23:42:18 +08:00
|
|
|
|
string focusAddress = key.Replace(redisCacheKey, "");
|
2025-04-14 16:41:41 +08:00
|
|
|
|
|
2025-04-14 23:42:18 +08:00
|
|
|
|
var meterHashs = new Dictionary<string, T>();
|
|
|
|
|
|
for (int i = 0; i < fieldsAndValues.Length; i += 2)
|
2025-04-14 16:41:41 +08:00
|
|
|
|
{
|
2025-04-14 23:42:18 +08:00
|
|
|
|
string meterId = (string)fieldsAndValues[i];
|
|
|
|
|
|
string meterStr = (string)fieldsAndValues[i + 1];
|
|
|
|
|
|
|
|
|
|
|
|
T meterInfo = default!;
|
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(meterStr))
|
|
|
|
|
|
{
|
|
|
|
|
|
meterInfo = meterStr.Deserialize<T>()!;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (meterInfo != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
meterHashs[meterId] = meterInfo;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
throw new Exception($"{nameof(GetMeterRedisCacheDictionaryData)} 缓存表计数据异常,集中器 {key} 的表计 {meterId} 解析失败,-103");
|
|
|
|
|
|
}
|
2025-04-14 16:41:41 +08:00
|
|
|
|
}
|
2025-04-14 23:42:18 +08:00
|
|
|
|
|
|
|
|
|
|
// 合并到总结果,若存在重复key则覆盖
|
|
|
|
|
|
if (meterInfos.ContainsKey(focusAddress))
|
2025-04-14 16:41:41 +08:00
|
|
|
|
{
|
2025-04-14 23:42:18 +08:00
|
|
|
|
foreach (var kvp in meterHashs)
|
|
|
|
|
|
{
|
|
|
|
|
|
meterInfos[focusAddress][kvp.Key] = kvp.Value;
|
|
|
|
|
|
}
|
2025-04-14 16:41:41 +08:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2025-04-14 23:42:18 +08:00
|
|
|
|
meterInfos[focusAddress] = meterHashs;
|
2025-04-14 16:41:41 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-04-14 23:42:18 +08:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
throw new Exception($"{nameof(GetMeterRedisCacheDictionaryData)} 第 {page + 1} 页数据解析失败,返回类型不符,-104");
|
2025-04-14 16:41:41 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return meterInfos;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Lua脚本批量获取缓存的表计信息
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <typeparam name="T">表信息数据对象</typeparam>
|
|
|
|
|
|
/// <param name="redisKeys">采集频率对应的缓存Key集合</param>
|
|
|
|
|
|
/// <param name="systemType"><see cref="SystemTypeConst"/> 系统类型</param>
|
|
|
|
|
|
/// <param name="serverTagName">服务器标识</param>
|
|
|
|
|
|
/// <param name="timeDensity">采集频率,1分钟、5分钟、15分钟</param>
|
|
|
|
|
|
/// <param name="meterType"><see cref="MeterTypeEnum"/> 表计类型</param>
|
|
|
|
|
|
/// <returns></returns>
|
2025-04-14 23:42:18 +08:00
|
|
|
|
protected async Task<List<T>> GetMeterRedisCacheListData<T>(string[] redisKeys, string systemType, string serverTagName, string timeDensity, MeterTypeEnum meterType) where T : class
|
2025-04-14 16:41:41 +08:00
|
|
|
|
{
|
2025-04-14 23:42:18 +08:00
|
|
|
|
if (redisKeys == null || redisKeys.Length <= 0 ||
|
|
|
|
|
|
string.IsNullOrWhiteSpace(systemType) ||
|
|
|
|
|
|
string.IsNullOrWhiteSpace(serverTagName) ||
|
|
|
|
|
|
string.IsNullOrWhiteSpace(timeDensity))
|
2025-04-14 16:41:41 +08:00
|
|
|
|
{
|
2025-04-14 23:42:18 +08:00
|
|
|
|
throw new Exception($"{nameof(GetMeterRedisCacheListData)} 参数异常,-101");
|
2025-04-14 16:41:41 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-14 23:42:18 +08:00
|
|
|
|
var meterInfos = new List<T>();
|
2025-04-14 16:41:41 +08:00
|
|
|
|
var luaScript = @"
|
2025-04-14 23:42:18 +08:00
|
|
|
|
local results = {}
|
|
|
|
|
|
for i, key in ipairs(KEYS) do
|
|
|
|
|
|
local data = redis.call('HGETALL', key)
|
|
|
|
|
|
results[i] = {key, data}
|
|
|
|
|
|
end
|
|
|
|
|
|
return results";
|
|
|
|
|
|
|
|
|
|
|
|
// 分页参数:每页10000个键
|
|
|
|
|
|
int pageSize = 10000;
|
|
|
|
|
|
int totalPages = (int)Math.Ceiling(redisKeys.Length / (double)pageSize);
|
2025-04-14 16:41:41 +08:00
|
|
|
|
|
2025-04-14 23:42:18 +08:00
|
|
|
|
for (int page = 0; page < totalPages; page++)
|
2025-04-14 16:41:41 +08:00
|
|
|
|
{
|
2025-04-14 23:42:18 +08:00
|
|
|
|
// 分页获取当前批次键
|
|
|
|
|
|
var batchKeys = redisKeys
|
|
|
|
|
|
.Skip(page * pageSize)
|
|
|
|
|
|
.Take(pageSize)
|
|
|
|
|
|
.ToArray();
|
|
|
|
|
|
|
|
|
|
|
|
// 执行Lua脚本获取当前页数据
|
|
|
|
|
|
var merterResult = await FreeRedisProvider.Instance.EvalAsync(luaScript, batchKeys);
|
|
|
|
|
|
if (merterResult == null)
|
2025-04-14 16:41:41 +08:00
|
|
|
|
{
|
2025-04-14 23:42:18 +08:00
|
|
|
|
throw new Exception($"{nameof(GetMeterRedisCacheListData)} 第 {page + 1} 页数据未返回,-102");
|
|
|
|
|
|
}
|
2025-04-14 16:41:41 +08:00
|
|
|
|
|
2025-04-14 23:42:18 +08:00
|
|
|
|
// 解析当前页结果
|
|
|
|
|
|
if (merterResult is object[] arr)
|
|
|
|
|
|
{
|
|
|
|
|
|
foreach (object[] item in arr)
|
2025-04-14 16:41:41 +08:00
|
|
|
|
{
|
2025-04-14 23:42:18 +08:00
|
|
|
|
string key = (string)item[0];
|
|
|
|
|
|
object[] fieldsAndValues = (object[])item[1];
|
|
|
|
|
|
var redisCacheKey = string.Format(
|
2025-04-16 17:36:46 +08:00
|
|
|
|
RedisConst.CacheMeterInfoHashKey,
|
2025-04-14 23:42:18 +08:00
|
|
|
|
systemType,
|
|
|
|
|
|
serverTagName,
|
|
|
|
|
|
meterType,
|
|
|
|
|
|
timeDensity
|
|
|
|
|
|
);
|
|
|
|
|
|
string focusAddress = key.Replace(redisCacheKey, "");
|
2025-04-14 16:41:41 +08:00
|
|
|
|
|
2025-04-14 23:42:18 +08:00
|
|
|
|
for (int i = 0; i < fieldsAndValues.Length; i += 2)
|
2025-04-14 16:41:41 +08:00
|
|
|
|
{
|
2025-04-14 23:42:18 +08:00
|
|
|
|
string meterId = (string)fieldsAndValues[i];
|
|
|
|
|
|
string meterStr = (string)fieldsAndValues[i + 1];
|
|
|
|
|
|
|
|
|
|
|
|
T meterInfo = default!;
|
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(meterStr))
|
|
|
|
|
|
{
|
|
|
|
|
|
meterInfo = meterStr.Deserialize<T>()!;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (meterInfo != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
meterInfos.Add(meterInfo);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
throw new Exception(
|
|
|
|
|
|
$"{nameof(GetMeterRedisCacheListData)} 表计 {meterId} 解析失败(页 {page + 1}),-103"
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
2025-04-14 16:41:41 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-04-14 23:42:18 +08:00
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
throw new Exception($"{nameof(GetMeterRedisCacheListData)} 第 {page + 1} 页数据格式错误,-104");
|
|
|
|
|
|
}
|
2025-04-14 16:41:41 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return meterInfos;
|
2025-04-15 15:49:51 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|