| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380 |
- using dodohold.core;
- using Microsoft.AspNetCore.Mvc;
- using molilian.core;
- using System.Text.Json;
- namespace molilian.api.Controllers
- {
- [ApiController]
- [MyAuthorize("admin")]
- [Route("api/[controller]/[action]")]
- public class DeeplinkReportController : ControllerBase
- {
- private const int MaxRangeDays = 90;
- private const string ReportAccountName = "tool";
- readonly IAuthorizationProvider provider = new AdminProvider();
- protected IHttpContextAccessor _accessor;
- public DeeplinkReportController(IHttpContextAccessor accessor)
- {
- _accessor = accessor;
- }
- [HttpPost]
- public async Task<ActionResult> daily([FromBody] JsonElement form)
- {
- _ = provider.Get(_accessor.HttpContext);
- var queryDate = form.PathReadArray<string>("query_date[]");
- var channelName = form.Read("channel_name", string.Empty).Trim();
- DateTime start = DateTime.Now.Date.AddDays(-6);
- DateTime end = DateTime.Now.Date;
- if (queryDate.Count == 2)
- {
- if (DateTime.TryParse(queryDate[0], out var parsedStart))
- {
- start = parsedStart.Date;
- }
- if (DateTime.TryParse(queryDate[1], out var parsedEnd))
- {
- end = parsedEnd.Date;
- }
- }
- if (end < start)
- {
- (start, end) = (end, start);
- }
- if ((end - start).TotalDays >= MaxRangeDays)
- {
- end = start.AddDays(MaxRangeDays - 1);
- }
- var isChannelFiltered = !string.IsNullOrWhiteSpace(channelName);
- var reportChannels = GetReportChannels(channelName);
- var channelNames = reportChannels
- .Select(item => item.channel_name)
- .ToList();
- var channelTotals = channelNames.ToDictionary(
- item => item,
- _ => 0L,
- StringComparer.OrdinalIgnoreCase
- );
- var dailyRows = new List<DeeplinkDailyReportRow>();
- for (var date = start; date <= end; date = date.AddDays(1))
- {
- var dateKey = date.ToString("yyyyMMdd");
- var indexedChannelNames = await TkLogCore.GetTotalKeysAsync(
- $":parse_total:{ReportAccountName}:channels:{dateKey}"
- );
- var useLegacyChannelStats = indexedChannelNames.Length == 0;
- var dailyReportChannels = isChannelFiltered
- ? reportChannels
- : GetIndexedReportChannels(reportChannels, indexedChannelNames);
- var channelStats = useLegacyChannelStats
- ? await GetLegacyChannelStatsAsync(dailyReportChannels, dateKey)
- : await GetChannelStatsAsync(dailyReportChannels, dateKey);
- var reportDate = date.ToString("yyyy-MM-dd");
- var accountTotalCount = channelStats.Sum(item => item.total_count);
- var accountSuccessCount = channelStats.Sum(item => item.success_count);
- var accountFailCount = channelStats.Sum(item => item.fail_count);
- foreach (var item in channelStats)
- {
- channelTotals.TryAdd(item.channel_name, 0);
- channelTotals[item.channel_name] += item.total_count;
- }
- var row = new DeeplinkDailyReportRow
- {
- row_key = reportDate,
- row_type = "daily",
- report_date = reportDate,
- request_count = accountTotalCount,
- total_count = accountTotalCount,
- success_count = accountSuccessCount,
- fail_count = accountFailCount
- };
- row.children = channelStats
- .Select(item => new DeeplinkDailyReportRow
- {
- row_key = $"{reportDate}:{item.channel_name}",
- row_type = "channel",
- report_date = item.display_name,
- channel_name = item.channel_name,
- display_name = item.display_name,
- request_count = item.request_count,
- total_count = item.total_count,
- success_count = item.success_count,
- fail_count = item.fail_count
- })
- .ToList();
- dailyRows.Add(row);
- }
- var channels = channelTotals
- .Where(item => !string.IsNullOrWhiteSpace(channelName) || item.Value > 0)
- .OrderByDescending(item => item.Value)
- .ThenBy(item => item.Key)
- .Select(item => new DeeplinkDailyReportChannel
- {
- channel_name = item.Key,
- display_name = GetDisplayName(reportChannels, item.Key),
- request_count = item.Value,
- total_count = item.Value
- })
- .ToList();
- var activeChannelNames = channels
- .Select(item => item.channel_name)
- .ToHashSet(StringComparer.OrdinalIgnoreCase);
- foreach (var row in dailyRows)
- {
- row.children = (row.children ?? new List<DeeplinkDailyReportRow>())
- .Where(item => activeChannelNames.Contains(item.channel_name))
- .OrderByDescending(item => item.total_count)
- .ThenBy(item => item.channel_name)
- .ToList();
- if (!row.children.Any())
- {
- row.children = null;
- }
- }
- var list = dailyRows
- .OrderByDescending(item => item.report_date)
- .ToList();
- var summary = new DeeplinkDailyReportSummary
- {
- request_count = list.Sum(item => item.request_count),
- total_count = list.Sum(item => item.total_count),
- success_count = list.Sum(item => item.success_count),
- fail_count = list.Sum(item => item.fail_count)
- };
- return new APIResult(new
- {
- data = new
- {
- list,
- count = list.Count,
- summary,
- channels,
- maxRangeDays = MaxRangeDays
- }
- });
- }
- private static List<DeeplinkDailyReportChannel> GetReportChannels(string channelName)
- {
- var channels = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
- try
- {
- var rules = new DBContext.Table("deeplink_parse_rule")
- .Select<DeeplinkParseRuleDTO>();
- if (rules != null)
- {
- foreach (var rule in rules)
- {
- AddChannel(channels, rule.channel_name, rule.name);
- }
- }
- }
- catch
- {
- // Channel names are display metadata. Redis statistics remain usable if this lookup fails.
- }
- if (!string.IsNullOrWhiteSpace(channelName))
- {
- var selectedDisplayName = channels.TryGetValue(channelName, out var configuredDisplayName)
- ? configuredDisplayName
- : channelName;
- channels.Clear();
- AddChannel(channels, channelName, selectedDisplayName);
- }
- else
- {
- foreach (var name in Enum.GetNames(typeof(TkChannelEnum)))
- {
- AddChannel(channels, name, name);
- }
- }
- return channels
- .OrderBy(item => item.Key)
- .Select(item => new DeeplinkDailyReportChannel
- {
- channel_name = item.Key,
- display_name = item.Value
- })
- .ToList();
- }
- private static void AddChannel(
- Dictionary<string, string> channels,
- string channelName,
- string displayName
- )
- {
- if (string.IsNullOrWhiteSpace(channelName)) return;
- var normalizedChannel = channelName.Trim();
- var normalizedDisplay = string.IsNullOrWhiteSpace(displayName)
- ? normalizedChannel
- : displayName.Trim();
- if (!channels.ContainsKey(normalizedChannel) ||
- channels[normalizedChannel].Equals(normalizedChannel, StringComparison.OrdinalIgnoreCase))
- {
- channels[normalizedChannel] = normalizedDisplay;
- }
- }
- private static string GetDisplayName(
- List<DeeplinkDailyReportChannel> channels,
- string channelName
- )
- {
- return channels
- .FirstOrDefault(item => item.channel_name.Equals(
- channelName,
- StringComparison.OrdinalIgnoreCase
- ))
- ?.display_name ?? channelName;
- }
- private static async Task<List<DeeplinkDailyReportChannel>> GetChannelStatsAsync(
- List<DeeplinkDailyReportChannel> channels,
- string dateKey
- )
- {
- var tasks = channels.Select(async channel =>
- {
- var channelBucket = $"{ReportAccountName}:channel:{channel.channel_name}";
- var channelTotalCount = await TkLogCore.GetTotalAsync($":parse_total:{channelBucket}:{dateKey}");
- return new DeeplinkDailyReportChannel
- {
- channel_name = channel.channel_name,
- display_name = channel.display_name,
- request_count = channelTotalCount,
- total_count = channelTotalCount,
- success_count = await TkLogCore.GetTotalAsync($":parse_total:{channelBucket}:success:{dateKey}"),
- fail_count = await TkLogCore.GetTotalAsync($":parse_total:{channelBucket}:fail:{dateKey}")
- };
- });
- var results = await Task.WhenAll(tasks);
- return results.ToList();
- }
- private static async Task<List<DeeplinkDailyReportChannel>> GetLegacyChannelStatsAsync(
- List<DeeplinkDailyReportChannel> channels,
- string dateKey
- )
- {
- var tasks = channels
- .Where(channel => IsLegacyReportSourceChannel(channel.channel_name))
- .Select(async channel =>
- {
- var channelTotalCount = await TkLogCore.GetTotalAsync(
- $":parse_total:{channel.channel_name}:{dateKey}"
- );
- return new DeeplinkDailyReportChannel
- {
- channel_name = channel.channel_name,
- display_name = channel.display_name,
- request_count = channelTotalCount,
- total_count = channelTotalCount,
- success_count = await TkLogCore.GetTotalAsync(
- $":parse_total:{channel.channel_name}:success:{dateKey}"
- ),
- fail_count = await TkLogCore.GetTotalAsync(
- $":parse_total:{channel.channel_name}:fail:{dateKey}"
- )
- };
- });
- var results = await Task.WhenAll(tasks);
- return results.ToList();
- }
- private static bool IsLegacyReportSourceChannel(string channelName)
- {
- return !channelName.Equals("all", StringComparison.OrdinalIgnoreCase) &&
- !channelName.Equals(ReportAccountName, StringComparison.OrdinalIgnoreCase);
- }
- private static List<DeeplinkDailyReportChannel> GetIndexedReportChannels(
- List<DeeplinkDailyReportChannel> configuredChannels,
- IEnumerable<string> indexedChannelNames
- )
- {
- var channels = configuredChannels.ToDictionary(
- item => item.channel_name,
- item => item.display_name,
- StringComparer.OrdinalIgnoreCase
- );
- foreach (var indexedChannelName in indexedChannelNames)
- {
- AddChannel(channels, indexedChannelName, indexedChannelName);
- }
- return channels
- .OrderBy(item => item.Key)
- .Select(item => new DeeplinkDailyReportChannel
- {
- channel_name = item.Key,
- display_name = item.Value
- })
- .ToList();
- }
- }
- public class DeeplinkDailyReportChannel
- {
- public string channel_name { get; set; } = string.Empty;
- public string display_name { get; set; } = string.Empty;
- public long request_count { get; set; } = 0;
- public long total_count { get; set; } = 0;
- public long success_count { get; set; } = 0;
- public long fail_count { get; set; } = 0;
- }
- public class DeeplinkDailyReportRow
- {
- public string row_key { get; set; } = string.Empty;
- public string row_type { get; set; } = "daily";
- public string report_date { get; set; } = string.Empty;
- public string channel_name { get; set; } = string.Empty;
- public string display_name { get; set; } = string.Empty;
- public long request_count { get; set; } = 0;
- public long total_count { get; set; } = 0;
- public long success_count { get; set; } = 0;
- public long fail_count { get; set; } = 0;
- public List<DeeplinkDailyReportRow>? children { get; set; }
- }
- public class DeeplinkDailyReportSummary
- {
- public long request_count { get; set; } = 0;
- public long total_count { get; set; } = 0;
- public long success_count { get; set; } = 0;
- public long fail_count { get; set; } = 0;
- }
- }
|