| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798 |
- using System.Collections.Concurrent;
- using System.Data;
- using System.Threading;
- using Dapper;
- using dodohold.core;
- using YunhuiKit;
- namespace molilian.core
- {
- public partial class TracksCore
- {
- private const string ParseMetricTypeName = "转链";
- private const string ParseMetricRequest = "request";
- private const string ParseMetricCall = "call";
- private const string ParseMetricSuccess = "success";
- private const int ParseMetricFlushIntervalMs = 1000;
- private static readonly ConcurrentDictionary<ParseMetricCounterKey, ParseMetricCounter> PendingParseMetricCounters = new();
- private static readonly object ParseMetricFlushTimerLock = new();
- private static Timer? ParseMetricFlushTimer;
- private static int ParseMetricFlushRunning;
- private static int ParseMetricLifecycleHooked;
- public static Task<bool> RecordParseRequestAsync(UnionParseRequest request)
- {
- var dimension = ResolveParseMetricDimension(request.Channel, request.RiskStrategy, request.LaunchScene);
- return IncrementParseMetricAsync(ParseMetricRequest, dimension.Platform, dimension.RiskStrategy, dimension.LaunchScene, request.AccountId);
- }
- public static Task<bool> RecordParseAccountRequestAsync(string channel, string riskStrategy, int launchScene, int accountId)
- {
- if (accountId <= 0) return Task.FromResult(false);
- var dimension = ResolveParseMetricDimension(channel, riskStrategy, launchScene);
- return IncrementParseAccountMetricAsync(ParseMetricRequest, dimension.Platform, dimension.RiskStrategy, dimension.LaunchScene, accountId);
- }
- public static Task<bool> RecordParseResultAsync(string channel, string riskStrategy, int launchScene, int accountId, bool success)
- {
- var dimension = ResolveParseMetricDimension(channel, riskStrategy, launchScene);
- return IncrementParseResultMetricAsync(dimension.Platform, dimension.RiskStrategy, dimension.LaunchScene, accountId, success);
- }
- public static async Task<TrackParseMetricReportResult> GetParseMetricReportAsync(
- DateTime start,
- DateTime end,
- int page,
- int size,
- bool getTotal,
- int accountId = -1,
- bool accountBreakdownOnly = false,
- string platform = "",
- string riskStrategy = "",
- int? launchScene = null)
- {
- start = start.Date;
- end = end.Date;
- if (end < start) end = start;
- page = Math.Max(page, 1);
- size = Math.Max(size, 1);
- var list = new List<TrackParseMetricReportDTO>();
- for (var date = start; date <= end; date = date.AddDays(1))
- {
- string dateStr = date.ToString("yyyyMMdd");
- string indexKey = BuildParseMetricIndexKey("daily", dateStr);
- string[] members = await TkLogCore.GetTotalKeysAsync(indexKey) ?? [];
- foreach (var member in members)
- {
- if (!TryParseParseMetricIndexValue(member, out var dimension)) continue;
- if (!IsMatchedParseMetricDimension(dimension, accountId, accountBreakdownOnly, platform, riskStrategy, launchScene)) continue;
- var requestTask = TkLogCore.GetTotalAsync(BuildParseMetricCountKey(ParseMetricRequest, "daily", dateStr, dimension.Platform, dimension.RiskStrategy, dimension.LaunchScene, dimension.AccountId));
- var callTask = TkLogCore.GetTotalAsync(BuildParseMetricCountKey(ParseMetricCall, "daily", dateStr, dimension.Platform, dimension.RiskStrategy, dimension.LaunchScene, dimension.AccountId));
- var successTask = TkLogCore.GetTotalAsync(BuildParseMetricCountKey(ParseMetricSuccess, "daily", dateStr, dimension.Platform, dimension.RiskStrategy, dimension.LaunchScene, dimension.AccountId));
- await Task.WhenAll(requestTask, callTask, successTask);
- int requestCount = requestTask.Result;
- int callCount = callTask.Result;
- int successCount = successTask.Result;
- if (requestCount <= 0 && callCount <= 0 && successCount <= 0) continue;
- list.Add(new TrackParseMetricReportDTO
- {
- report_date = date,
- platform = dimension.Platform,
- typename = ParseMetricTypeName,
- risk_strategy = dimension.RiskStrategy,
- launch_scene = dimension.LaunchScene,
- account_id = dimension.AccountId,
- request_count = requestCount,
- call_count = callCount,
- success_count = successCount
- });
- }
- }
- list = list
- .OrderByDescending(item => item.report_date)
- .ThenBy(item => item.platform)
- .ThenBy(item => item.risk_strategy)
- .ThenBy(item => item.launch_scene)
- .ThenBy(item => item.account_id)
- .ToList();
- int count = list.Count;
- list = list
- .Skip((page - 1) * size)
- .Take(size)
- .ToList();
- return new TrackParseMetricReportResult
- {
- list = list,
- count = getTotal ? count : list.Count
- };
- }
- public static async Task<List<TrackParseMetricHourlyDTO>> GetParseMetricHourlyReportAsync(
- DateTime targetDate,
- string platform,
- string riskStrategy,
- int? launchScene = null,
- int accountId = 0)
- {
- targetDate = targetDate.Date;
- platform = NormalizeParseMetricPlatform(platform);
- riskStrategy = NormalizeReportScene(riskStrategy);
- accountId = Math.Max(accountId, 0);
- var list = new List<TrackParseMetricHourlyDTO>();
- string dateStr = targetDate.ToString("yyyyMMdd");
- for (int hour = 0; hour < 24; hour++)
- {
- string hourStr = $"{dateStr}{hour:00}";
- string indexKey = BuildParseMetricIndexKey("hour", hourStr);
- string[] members = await TkLogCore.GetTotalKeysAsync(indexKey) ?? [];
- long requestCount = 0;
- long callCount = 0;
- long successCount = 0;
- foreach (var member in members)
- {
- if (!TryParseParseMetricIndexValue(member, out var dimension)) continue;
- if (!IsMatchedParseMetricDimension(dimension, accountId, false, platform, riskStrategy, launchScene)) continue;
- var requestTask = TkLogCore.GetTotalAsync(BuildParseMetricCountKey(ParseMetricRequest, "hour", hourStr, dimension.Platform, dimension.RiskStrategy, dimension.LaunchScene, dimension.AccountId));
- var callTask = TkLogCore.GetTotalAsync(BuildParseMetricCountKey(ParseMetricCall, "hour", hourStr, dimension.Platform, dimension.RiskStrategy, dimension.LaunchScene, dimension.AccountId));
- var successTask = TkLogCore.GetTotalAsync(BuildParseMetricCountKey(ParseMetricSuccess, "hour", hourStr, dimension.Platform, dimension.RiskStrategy, dimension.LaunchScene, dimension.AccountId));
- await Task.WhenAll(requestTask, callTask, successTask);
- requestCount += requestTask.Result;
- callCount += callTask.Result;
- successCount += successTask.Result;
- }
- list.Add(new TrackParseMetricHourlyDTO
- {
- report_date = targetDate,
- platform = platform,
- typename = ParseMetricTypeName,
- risk_strategy = riskStrategy,
- launch_scene = launchScene ?? -1,
- account_id = accountId,
- hour = hour,
- request_count = SafeInt(requestCount),
- call_count = SafeInt(callCount),
- success_count = SafeInt(successCount)
- });
- }
- return list;
- }
- public static async Task<TrackParseMetricBackfillResult> BackfillParseMetricCountersAsync(
- DateTime targetDate,
- string platform = "",
- string riskStrategy = "",
- int? launchScene = null)
- {
- targetDate = targetDate.Date;
- platform = NormalizeParseMetricPlatform(platform);
- riskStrategy = NormalizeReportScene(riskStrategy);
- bool hasPlatformFilter = !string.IsNullOrWhiteSpace(platform);
- bool hasRiskStrategyFilter = !string.IsNullOrWhiteSpace(riskStrategy);
- bool hasLaunchSceneFilter = launchScene.HasValue;
- bool hasDimensionFilter = hasPlatformFilter || hasRiskStrategyFilter || hasLaunchSceneFilter;
- var result = new TrackParseMetricBackfillResult
- {
- report_date = targetDate.ToString("yyyy-MM-dd")
- };
- var allSources = new[]
- {
- new ParseMetricBackfillSource("tb", $"tk_parse_logs_{targetDate:yyyyMMdd}"),
- new ParseMetricBackfillSource("jd", $"jd_parse_logs_{targetDate:yyyyMMdd}"),
- new ParseMetricBackfillSource("pdd", $"pdd_parse_logs_{targetDate:yyyyMMdd}")
- };
- var sources = hasDimensionFilter
- ? allSources.Where(source => !hasPlatformFilter || string.Equals(source.Platform, platform, StringComparison.OrdinalIgnoreCase)).ToArray()
- : allSources;
- using var conn = DBContext.GetOpenConnection();
- DateTime start = targetDate;
- DateTime end = targetDate.AddDays(1);
- result.redis_cleared_keys = hasDimensionFilter
- ? await ClearParseMetricCountersForDimensionAsync(targetDate, platform, riskStrategy, launchScene)
- : await ClearParseMetricCountersForDateAsync(targetDate);
- foreach (var source in sources)
- {
- if (!TableExists(conn, source.TableName))
- {
- result.details.Add(new { platform = source.Platform, table = source.TableName, exists = false, rows = 0, dimensions = 0 });
- continue;
- }
- const string riskStrategySql = "COALESCE(NULLIF(TRIM(riskStrategy), ''), '')";
- const string rawLaunchSceneSql = "COALESCE(launchScene, -1)";
- string launchSceneSql = $"CASE WHEN LOWER({riskStrategySql}) IN ('os','tbpush','brwsimilar','icon') AND {rawLaunchSceneSql}=-1 THEN 0 ELSE {rawLaunchSceneSql} END";
- var dimensionWhereItems = new List<string>();
- if (hasRiskStrategyFilter) dimensionWhereItems.Add($"{riskStrategySql}=@riskStrategy");
- if (hasLaunchSceneFilter) dimensionWhereItems.Add($"{launchSceneSql}=@launchScene");
- string dimensionWhere = dimensionWhereItems.Count > 0
- ? "\n AND " + string.Join("\n AND ", dimensionWhereItems)
- : string.Empty;
- string sql = $@"
- SELECT
- {riskStrategySql} AS risk_strategy,
- {launchSceneSql} AS launch_scene,
- COALESCE(accountId, 0) AS account_id,
- DATE_FORMAT(create_time, '%Y%m%d%H') AS hour_key,
- CAST(COUNT(1) AS SIGNED) AS call_count,
- CAST(SUM(CASE WHEN success=1 THEN 1 ELSE 0 END) AS SIGNED) AS success_count
- FROM `{source.TableName}`
- WHERE create_time>=@start AND create_time<@end
- {dimensionWhere}
- GROUP BY
- {riskStrategySql},
- {launchSceneSql},
- COALESCE(accountId, 0),
- DATE_FORMAT(create_time, '%Y%m%d%H')";
- var args = new DynamicParameters();
- args.Add("start", start);
- args.Add("end", end);
- if (hasRiskStrategyFilter) args.Add("riskStrategy", riskStrategy);
- if (hasLaunchSceneFilter) args.Add("launchScene", launchScene!.Value);
- var rows = SqlMapper.Query<ParseMetricBackfillRow>(conn, sql, args).ToList();
- int mysqlRows = rows.Sum(row => SafeInt(row.call_count));
- int dimensions = ApplyParseMetricBackfillRows(source.Platform, targetDate, rows);
- result.mysql_rows += mysqlRows;
- result.redis_dimensions += dimensions;
- result.details.Add(new { platform = source.Platform, table = source.TableName, exists = true, rows = mysqlRows, dimensions });
- }
- await Task.CompletedTask;
- return result;
- }
- private static Task<bool> IncrementParseMetricAsync(string metric, string platform, string riskStrategy, int launchScene, int accountId = 0)
- {
- try
- {
- AddParseMetricCount(metric, DateTime.Now, platform, riskStrategy, launchScene, 0, 1);
- if (accountId > 0)
- {
- AddParseMetricCount(metric, DateTime.Now, platform, riskStrategy, launchScene, accountId, 1);
- }
- return Task.FromResult(true);
- }
- catch (Exception ex)
- {
- _ = new LoggerLibrary("TracksCore", "RecordParseMetric")
- .Info(ex.Message, ex.StackTrace)
- .SaveAsync();
- return Task.FromResult(false);
- }
- }
- private static Task<bool> IncrementParseAccountMetricAsync(string metric, string platform, string riskStrategy, int launchScene, int accountId)
- {
- try
- {
- accountId = Math.Max(accountId, 0);
- if (accountId <= 0) return Task.FromResult(false);
- AddParseMetricCount(metric, DateTime.Now, platform, riskStrategy, launchScene, accountId, 1);
- return Task.FromResult(true);
- }
- catch (Exception ex)
- {
- _ = new LoggerLibrary("TracksCore", "RecordParseAccountMetric")
- .Info(ex.Message, ex.StackTrace)
- .SaveAsync();
- return Task.FromResult(false);
- }
- }
- private static Task<bool> IncrementParseResultMetricAsync(string platform, string riskStrategy, int launchScene, int accountId, bool success)
- {
- try
- {
- accountId = Math.Max(accountId, 0);
- AddParseMetricCount(ParseMetricCall, DateTime.Now, platform, riskStrategy, launchScene, 0, 1);
- if (success) AddParseMetricCount(ParseMetricSuccess, DateTime.Now, platform, riskStrategy, launchScene, 0, 1);
- if (accountId > 0)
- {
- AddParseMetricCount(ParseMetricCall, DateTime.Now, platform, riskStrategy, launchScene, accountId, 1);
- if (success) AddParseMetricCount(ParseMetricSuccess, DateTime.Now, platform, riskStrategy, launchScene, accountId, 1);
- }
- return Task.FromResult(true);
- }
- catch (Exception ex)
- {
- _ = new LoggerLibrary("TracksCore", "RecordParseResultMetric")
- .Info(ex.Message, ex.StackTrace)
- .SaveAsync();
- return Task.FromResult(false);
- }
- }
- private static int ApplyParseMetricBackfillRows(string platform, DateTime targetDate, List<ParseMetricBackfillRow> rows)
- {
- string dateStr = targetDate.ToString("yyyyMMdd");
- int dimensions = 0;
- var dailyGroups = rows.GroupBy(row => new
- {
- RiskStrategy = NormalizeReportScene(row.risk_strategy),
- LaunchScene = NormalizeParseMetricLaunchScene(row.risk_strategy, row.launch_scene)
- });
- foreach (var group in dailyGroups)
- {
- int callCount = group.Sum(row => SafeInt(row.call_count));
- int successCount = group.Sum(row => SafeInt(row.success_count));
- SetParseMetricCount(ParseMetricCall, "daily", dateStr, platform, group.Key.RiskStrategy, group.Key.LaunchScene, 0, callCount);
- SetParseMetricCount(ParseMetricSuccess, "daily", dateStr, platform, group.Key.RiskStrategy, group.Key.LaunchScene, 0, successCount);
- dimensions++;
- }
- var dailyAccountGroups = rows
- .Where(row => row.account_id > 0)
- .GroupBy(row => new
- {
- RiskStrategy = NormalizeReportScene(row.risk_strategy),
- launch_scene = NormalizeParseMetricLaunchScene(row.risk_strategy, row.launch_scene),
- row.account_id
- });
- foreach (var group in dailyAccountGroups)
- {
- int callCount = group.Sum(row => SafeInt(row.call_count));
- int successCount = group.Sum(row => SafeInt(row.success_count));
- SetParseMetricCount(ParseMetricRequest, "daily", dateStr, platform, group.Key.RiskStrategy, group.Key.launch_scene, group.Key.account_id, callCount);
- SetParseMetricCount(ParseMetricCall, "daily", dateStr, platform, group.Key.RiskStrategy, group.Key.launch_scene, group.Key.account_id, callCount);
- SetParseMetricCount(ParseMetricSuccess, "daily", dateStr, platform, group.Key.RiskStrategy, group.Key.launch_scene, group.Key.account_id, successCount);
- dimensions++;
- }
- var hourlyGroups = rows.GroupBy(row => new
- {
- RiskStrategy = NormalizeReportScene(row.risk_strategy),
- launch_scene = NormalizeParseMetricLaunchScene(row.risk_strategy, row.launch_scene),
- row.hour_key
- });
- foreach (var group in hourlyGroups)
- {
- int callCount = group.Sum(row => SafeInt(row.call_count));
- int successCount = group.Sum(row => SafeInt(row.success_count));
- SetParseMetricCount(ParseMetricCall, "hour", group.Key.hour_key, platform, group.Key.RiskStrategy, group.Key.launch_scene, 0, callCount);
- SetParseMetricCount(ParseMetricSuccess, "hour", group.Key.hour_key, platform, group.Key.RiskStrategy, group.Key.launch_scene, 0, successCount);
- }
- var hourlyAccountGroups = rows
- .Where(row => row.account_id > 0)
- .GroupBy(row => new
- {
- RiskStrategy = NormalizeReportScene(row.risk_strategy),
- launch_scene = NormalizeParseMetricLaunchScene(row.risk_strategy, row.launch_scene),
- row.account_id,
- row.hour_key
- });
- foreach (var group in hourlyAccountGroups)
- {
- int callCount = group.Sum(row => SafeInt(row.call_count));
- int successCount = group.Sum(row => SafeInt(row.success_count));
- SetParseMetricCount(ParseMetricRequest, "hour", group.Key.hour_key, platform, group.Key.RiskStrategy, group.Key.launch_scene, group.Key.account_id, callCount);
- SetParseMetricCount(ParseMetricCall, "hour", group.Key.hour_key, platform, group.Key.RiskStrategy, group.Key.launch_scene, group.Key.account_id, callCount);
- SetParseMetricCount(ParseMetricSuccess, "hour", group.Key.hour_key, platform, group.Key.RiskStrategy, group.Key.launch_scene, group.Key.account_id, successCount);
- }
- return dimensions;
- }
- private static void AddParseMetricCount(string metric, DateTime metricTime, string platform, string riskStrategy, int launchScene, int accountId, int count)
- {
- if (count <= 0) return;
- QueueParseMetricCount(metric, "daily", metricTime.ToString("yyyyMMdd"), platform, riskStrategy, launchScene, accountId, count);
- QueueParseMetricCount(metric, "hour", metricTime.ToString("yyyyMMddHH"), platform, riskStrategy, launchScene, accountId, count);
- }
- private static void SetParseMetricCount(string metric, string bucketType, string bucketValue, string platform, string riskStrategy, int launchScene, int accountId, int count)
- {
- if (count < 0) return;
- SetOrIncrementParseMetricCount(metric, bucketType, bucketValue, platform, riskStrategy, launchScene, accountId, count, increment: false);
- }
- private static void SetOrIncrementParseMetricCount(string metric, string bucketType, string bucketValue, string platform, string riskStrategy, int launchScene, int accountId, long count, bool increment)
- {
- platform = Normalize(platform).ToLowerInvariant();
- riskStrategy = NormalizeReportScene(riskStrategy);
- launchScene = NormalizeParseMetricLaunchScene(riskStrategy, launchScene);
- accountId = Math.Max(accountId, 0);
- string countKey = BuildParseMetricCountKey(metric, bucketType, bucketValue, platform, riskStrategy, launchScene, accountId);
- string indexKey = BuildParseMetricIndexKey(bucketType, bucketValue);
- string indexValue = BuildParseMetricIndexValue(platform, riskStrategy, launchScene, accountId);
- int expireSeconds = bucketType == "hour" ? HourlyExpireSeconds : DailyExpireSeconds;
- if (increment)
- {
- IncrementParseMetricRedisKey(countKey, count);
- }
- else
- {
- RedisHelper.Set(countKey, count, expireSeconds);
- }
- RedisHelper.Expire(countKey, expireSeconds);
- RedisHelper.SAdd(indexKey, indexValue);
- RedisHelper.Expire(indexKey, expireSeconds);
- }
- public static int FlushParseMetricCounters()
- {
- if (Interlocked.Exchange(ref ParseMetricFlushRunning, 1) == 1) return 0;
- try
- {
- int flushed = 0;
- foreach (var item in PendingParseMetricCounters.ToArray())
- {
- long count = Interlocked.Exchange(ref item.Value.Count, 0);
- if (count <= 0) continue;
- try
- {
- SetOrIncrementParseMetricCount(
- item.Key.Metric,
- item.Key.BucketType,
- item.Key.BucketValue,
- item.Key.Platform,
- item.Key.RiskStrategy,
- item.Key.LaunchScene,
- item.Key.AccountId,
- count,
- increment: true);
- flushed++;
- }
- catch (Exception ex)
- {
- Interlocked.Add(ref item.Value.Count, count);
- _ = new LoggerLibrary("TracksCore", "FlushParseMetricCounters")
- .Info(ex.Message, ex.StackTrace)
- .SaveAsync();
- }
- }
- return flushed;
- }
- finally
- {
- Interlocked.Exchange(ref ParseMetricFlushRunning, 0);
- }
- }
- private static void QueueParseMetricCount(string metric, string bucketType, string bucketValue, string platform, string riskStrategy, int launchScene, int accountId, int count)
- {
- if (count <= 0) return;
- var key = new ParseMetricCounterKey(
- metric,
- bucketType,
- bucketValue,
- Normalize(platform).ToLowerInvariant(),
- NormalizeReportScene(riskStrategy),
- NormalizeParseMetricLaunchScene(riskStrategy, launchScene),
- Math.Max(accountId, 0));
- var counter = PendingParseMetricCounters.GetOrAdd(key, _ => new ParseMetricCounter());
- Interlocked.Add(ref counter.Count, count);
- EnsureParseMetricFlushTimer();
- }
- private static void EnsureParseMetricFlushTimer()
- {
- if (ParseMetricFlushTimer != null) return;
- lock (ParseMetricFlushTimerLock)
- {
- if (ParseMetricFlushTimer != null) return;
- ParseMetricFlushTimer = new Timer(
- _ => FlushParseMetricCounters(),
- null,
- ParseMetricFlushIntervalMs,
- ParseMetricFlushIntervalMs);
- if (Interlocked.Exchange(ref ParseMetricLifecycleHooked, 1) == 0)
- {
- AppDomain.CurrentDomain.ProcessExit += (_, _) => FlushParseMetricCounters();
- AppDomain.CurrentDomain.UnhandledException += (_, _) => FlushParseMetricCounters();
- }
- }
- }
- private static void IncrementParseMetricRedisKey(string key, long count)
- {
- while (count > 0)
- {
- int delta = count > int.MaxValue ? int.MaxValue : (int)count;
- RedisHelper.IncrBy(key, delta);
- count -= delta;
- }
- }
- private static async Task<int> ClearParseMetricCountersForDateAsync(DateTime targetDate)
- {
- string dateStr = targetDate.ToString("yyyyMMdd");
- int cleared = await ClearParseMetricBucketAsync("daily", dateStr);
- for (int hour = 0; hour < 24; hour++)
- {
- cleared += await ClearParseMetricBucketAsync("hour", $"{dateStr}{hour:00}");
- }
- return cleared;
- }
- private static async Task<int> ClearParseMetricCountersForDimensionAsync(
- DateTime targetDate,
- string platform,
- string riskStrategy,
- int? launchScene)
- {
- string dateStr = targetDate.ToString("yyyyMMdd");
- int cleared = await ClearParseMetricBucketAsync(
- "daily",
- dateStr,
- dimension => IsSameParseMetricDimension(dimension, platform, riskStrategy, launchScene));
- for (int hour = 0; hour < 24; hour++)
- {
- cleared += await ClearParseMetricBucketAsync(
- "hour",
- $"{dateStr}{hour:00}",
- dimension => IsSameParseMetricDimension(dimension, platform, riskStrategy, launchScene));
- }
- return cleared;
- }
- private static async Task<int> ClearParseMetricBucketAsync(
- string bucketType,
- string bucketValue,
- Func<ParseMetricDimension, bool>? predicate = null)
- {
- string indexKey = BuildParseMetricIndexKey(bucketType, bucketValue);
- string[] members = await TkLogCore.GetTotalKeysAsync(indexKey) ?? [];
- int cleared = 0;
- foreach (var member in members)
- {
- if (!TryParseParseMetricIndexValue(member, out var dimension)) continue;
- if (predicate != null && !predicate(dimension)) continue;
- RedisHelper.Del(BuildParseMetricCountKey(ParseMetricCall, bucketType, bucketValue, dimension.Platform, dimension.RiskStrategy, dimension.LaunchScene, dimension.AccountId));
- RedisHelper.Del(BuildParseMetricCountKey(ParseMetricSuccess, bucketType, bucketValue, dimension.Platform, dimension.RiskStrategy, dimension.LaunchScene, dimension.AccountId));
- cleared += 2;
- if (dimension.AccountId > 0)
- {
- RedisHelper.Del(BuildParseMetricCountKey(ParseMetricRequest, bucketType, bucketValue, dimension.Platform, dimension.RiskStrategy, dimension.LaunchScene, dimension.AccountId));
- cleared++;
- }
- }
- return cleared;
- }
- private static string BuildParseMetricCountKey(string metric, string bucketType, string bucketValue, string platform, string riskStrategy, int launchScene, int accountId = 0)
- {
- string key = $"{RedisPrefix}:parse_metric:{bucketType}:{metric}:{bucketValue}:{EncodeIndexPart(platform)}:{EncodeIndexPart(riskStrategy)}:{launchScene}";
- if (accountId > 0) key += $":account:{accountId}";
- return key;
- }
- private static string BuildParseMetricIndexKey(string bucketType, string bucketValue)
- {
- return $"{RedisPrefix}:parse_metric:{bucketType}:index:{bucketValue}";
- }
- private static string BuildParseMetricIndexValue(string platform, string riskStrategy, int launchScene, int accountId = 0)
- {
- string value = $"{EncodeIndexPart(platform)}|{EncodeIndexPart(riskStrategy)}|{launchScene}";
- if (accountId > 0) value += $"|{accountId}";
- return value;
- }
- private static bool TryParseParseMetricIndexValue(string value, out ParseMetricDimension dimension)
- {
- dimension = new ParseMetricDimension();
- var parts = (value ?? string.Empty).Split('|');
- if (parts.Length < 3 || parts.Length > 4) return false;
- if (!int.TryParse(parts[2], out int launchScene)) return false;
- int accountId = 0;
- if (parts.Length == 4 && (!int.TryParse(parts[3], out accountId) || accountId <= 0)) return false;
- dimension = new ParseMetricDimension
- {
- Platform = DecodeIndexPart(parts[0]),
- RiskStrategy = DecodeIndexPart(parts[1]),
- LaunchScene = launchScene,
- AccountId = accountId
- };
- return true;
- }
- private static bool IsMatchedParseMetricDimension(
- ParseMetricDimension dimension,
- int accountId,
- bool accountBreakdownOnly,
- string platform,
- string riskStrategy,
- int? launchScene)
- {
- if (accountBreakdownOnly)
- {
- if (dimension.AccountId <= 0) return false;
- }
- else if (accountId > 0)
- {
- if (dimension.AccountId != accountId) return false;
- }
- else if (dimension.AccountId > 0)
- {
- return false;
- }
- if (!string.IsNullOrWhiteSpace(platform) && !string.Equals(NormalizeParseMetricPlatform(platform), dimension.Platform, StringComparison.OrdinalIgnoreCase)) return false;
- if (!string.IsNullOrWhiteSpace(riskStrategy) && !string.Equals(NormalizeReportScene(riskStrategy), dimension.RiskStrategy, StringComparison.OrdinalIgnoreCase)) return false;
- if (launchScene.HasValue && NormalizeParseMetricLaunchScene(dimension.RiskStrategy, launchScene.Value) != NormalizeParseMetricLaunchScene(dimension.RiskStrategy, dimension.LaunchScene)) return false;
- return true;
- }
- private static bool IsSameParseMetricDimension(
- ParseMetricDimension dimension,
- string platform,
- string riskStrategy,
- int? launchScene)
- {
- if (!string.IsNullOrWhiteSpace(platform)
- && !string.Equals(NormalizeParseMetricPlatform(platform), dimension.Platform, StringComparison.OrdinalIgnoreCase))
- {
- return false;
- }
- if (!string.IsNullOrWhiteSpace(riskStrategy)
- && !string.Equals(NormalizeReportScene(riskStrategy), dimension.RiskStrategy, StringComparison.OrdinalIgnoreCase))
- {
- return false;
- }
- if (launchScene.HasValue
- && NormalizeParseMetricLaunchScene(dimension.RiskStrategy, launchScene.Value) != NormalizeParseMetricLaunchScene(dimension.RiskStrategy, dimension.LaunchScene))
- {
- return false;
- }
- return true;
- }
- private static ParseMetricDimension ResolveParseMetricDimension(string channel, string riskStrategy, int launchScene)
- {
- channel = Normalize(channel).ToLowerInvariant();
- riskStrategy = NormalizeReportScene(riskStrategy);
- switch (channel)
- {
- case "tbpush":
- case "brwsimilar":
- case "icon":
- riskStrategy = channel;
- launchScene = 0;
- channel = "tb";
- break;
- }
- switch (riskStrategy)
- {
- case "tbpush":
- case "brwsimilar":
- case "icon":
- if (launchScene == -1) launchScene = 0;
- break;
- }
- launchScene = NormalizeParseMetricLaunchScene(riskStrategy, launchScene);
- return new ParseMetricDimension
- {
- Platform = channel,
- RiskStrategy = riskStrategy,
- LaunchScene = launchScene,
- AccountId = 0
- };
- }
- private static int NormalizeParseMetricLaunchScene(string riskStrategy, int launchScene)
- {
- if (launchScene != -1) return launchScene;
- return NormalizeReportScene(riskStrategy).ToLowerInvariant() switch
- {
- "os" or "tbpush" or "brwsimilar" or "icon" => 0,
- _ => launchScene
- };
- }
- private static string NormalizeParseMetricPlatform(string platform)
- {
- string value = Normalize(platform).ToLowerInvariant();
- return value switch
- {
- "淘宝" or "taobao" or "tb" or "1" => "tb",
- "京东" or "jd" or "13" => "jd",
- "拼多多" or "pdd" or "9" => "pdd",
- _ => value
- };
- }
- private static bool TableExists(IDbConnection conn, string tableName)
- {
- const string sql = @"
- SELECT COUNT(1)
- FROM information_schema.TABLES
- WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME=@tableName";
- return conn.ExecuteScalar<int>(sql, new { tableName }) > 0;
- }
- private static int SafeInt(long value)
- {
- if (value <= 0) return 0;
- return value > int.MaxValue ? int.MaxValue : (int)value;
- }
- private sealed record ParseMetricCounterKey(
- string Metric,
- string BucketType,
- string BucketValue,
- string Platform,
- string RiskStrategy,
- int LaunchScene,
- int AccountId);
- private sealed class ParseMetricCounter
- {
- public long Count = 0;
- }
- private sealed class ParseMetricDimension
- {
- public string Platform { get; set; } = string.Empty;
- public string RiskStrategy { get; set; } = string.Empty;
- public int LaunchScene { get; set; } = -1;
- public int AccountId { get; set; } = 0;
- }
- private sealed record ParseMetricBackfillSource(string Platform, string TableName);
- private sealed class ParseMetricBackfillRow
- {
- public string risk_strategy { get; set; } = string.Empty;
- public int launch_scene { get; set; } = -1;
- public int account_id { get; set; } = 0;
- public string hour_key { get; set; } = string.Empty;
- public long call_count { get; set; } = 0;
- public long success_count { get; set; } = 0;
- }
- }
- }
|