|
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|
|
using System.Data;
|
|
using System.Data;
|
|
|
using System.Net;
|
|
using System.Net;
|
|
|
using System.Threading.Tasks;
|
|
using System.Threading.Tasks;
|
|
|
|
|
+using Dapper;
|
|
|
using CSRedis;
|
|
using CSRedis;
|
|
|
using dodohold.core;
|
|
using dodohold.core;
|
|
|
using System.Text.Json;
|
|
using System.Text.Json;
|
|
@@ -10,6 +11,12 @@ using YunhuiKit;
|
|
|
|
|
|
|
|
namespace molilian.core
|
|
namespace molilian.core
|
|
|
{
|
|
{
|
|
|
|
|
+ public enum TrackType
|
|
|
|
|
+ {
|
|
|
|
|
+ Expose,
|
|
|
|
|
+ Click
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
public partial class TracksCore
|
|
public partial class TracksCore
|
|
|
{
|
|
{
|
|
|
private const string RedisPrefix = ":tracks_v123";
|
|
private const string RedisPrefix = ":tracks_v123";
|
|
@@ -18,16 +25,24 @@ namespace molilian.core
|
|
|
private const string DefaultDimensionValue = "";
|
|
private const string DefaultDimensionValue = "";
|
|
|
private const int LinkCacheExpireSeconds = 30 * 86400;
|
|
private const int LinkCacheExpireSeconds = 30 * 86400;
|
|
|
private const string TrackRequestLogKey = ":tracks:request:logs";
|
|
private const string TrackRequestLogKey = ":tracks:request:logs";
|
|
|
- private static readonly HashSet<string> SupportEventTypes = new(StringComparer.OrdinalIgnoreCase) { "expose", "click" };
|
|
|
|
|
- internal static int ExposeTrackId = 2;
|
|
|
|
|
- internal static int ClickTrackId = 1;
|
|
|
|
|
|
|
+ private static readonly HashSet<string> SupportEventTypes = new(StringComparer.OrdinalIgnoreCase) { "expose", "click" };
|
|
|
|
|
+
|
|
|
|
|
|
|
|
- /// <summary>
|
|
|
|
|
- /// 生成一个监测链接(仅返回 path,不包含域名),同时落地到 track_links
|
|
|
|
|
- /// </summary>
|
|
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// 生成一个监测链接(仅返回 path,不包含域名),同时落地到 track_links
|
|
|
|
|
+ /// </summary>
|
|
|
public static Task<TrackLinkDTO> CreateLinkAsync(string eventType, string typename, string scene, string uniqueId, string description = "", bool forceNew = false)
|
|
public static Task<TrackLinkDTO> CreateLinkAsync(string eventType, string typename, string scene, string uniqueId, string description = "", bool forceNew = false)
|
|
|
|
|
+ {
|
|
|
|
|
+ return CreateLinkAsync(eventType, string.Empty, typename, scene, uniqueId, description, forceNew);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// 生成一个监测链接(仅返回 path,不包含域名),同时落地到 track_links
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ public static Task<TrackLinkDTO> CreateLinkAsync(string eventType, string platform, string typename, string scene, string uniqueId, string description = "", bool forceNew = false)
|
|
|
{
|
|
{
|
|
|
eventType = NormalizeEventType(eventType);
|
|
eventType = NormalizeEventType(eventType);
|
|
|
|
|
+ platform = Normalize(platform);
|
|
|
typename = Normalize(typename);
|
|
typename = Normalize(typename);
|
|
|
scene = NormalizeOrAll(scene);
|
|
scene = NormalizeOrAll(scene);
|
|
|
uniqueId = NormalizeOrAll(uniqueId);
|
|
uniqueId = NormalizeOrAll(uniqueId);
|
|
@@ -38,7 +53,7 @@ namespace molilian.core
|
|
|
return Task.FromResult<TrackLinkDTO>(null);
|
|
return Task.FromResult<TrackLinkDTO>(null);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- string cacheKey = $"{RedisPrefix}:link:{eventType}:{typename}:{scene}:{uniqueId}";
|
|
|
|
|
|
|
+ string cacheKey = GetLinkCacheKey(eventType, platform, typename, scene, uniqueId);
|
|
|
if (!forceNew)
|
|
if (!forceNew)
|
|
|
{
|
|
{
|
|
|
try
|
|
try
|
|
@@ -63,8 +78,8 @@ namespace molilian.core
|
|
|
if (!forceNew)
|
|
if (!forceNew)
|
|
|
{
|
|
{
|
|
|
exist = new DBContext.Table(conn, "track_links")
|
|
exist = new DBContext.Table(conn, "track_links")
|
|
|
- .Get<TrackLinkDTO>("event_type=@event_type AND platform=@platform AND scene=@scene AND unique_id=@unique_id",
|
|
|
|
|
- new { event_type = eventType, platform = typename, scene, unique_id = uniqueId });
|
|
|
|
|
|
|
+ .Get<TrackLinkDTO>("event_type=@event_type AND platform=@platform AND typename=@typename AND scene=@scene AND unique_id=@unique_id",
|
|
|
|
|
+ new { event_type = eventType, platform, typename, scene, unique_id = uniqueId });
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if (exist != null)
|
|
if (exist != null)
|
|
@@ -76,6 +91,8 @@ namespace molilian.core
|
|
|
exist.path = path;
|
|
exist.path = path;
|
|
|
new DBContext.Table(conn, "track_links")
|
|
new DBContext.Table(conn, "track_links")
|
|
|
.Add("path", path)
|
|
.Add("path", path)
|
|
|
|
|
+ .Add("platform", platform)
|
|
|
|
|
+ .Add("typename", typename)
|
|
|
.Add("description", description)
|
|
.Add("description", description)
|
|
|
.Add("update_time", DateTime.Now)
|
|
.Add("update_time", DateTime.Now)
|
|
|
.Where("id=@id", new { exist.id })
|
|
.Where("id=@id", new { exist.id })
|
|
@@ -89,7 +106,8 @@ namespace molilian.core
|
|
|
var item = new TrackLinkDTO
|
|
var item = new TrackLinkDTO
|
|
|
{
|
|
{
|
|
|
event_type = eventType,
|
|
event_type = eventType,
|
|
|
- platform = typename,
|
|
|
|
|
|
|
+ platform = platform,
|
|
|
|
|
+ typename = typename,
|
|
|
scene = scene,
|
|
scene = scene,
|
|
|
unique_id = uniqueId,
|
|
unique_id = uniqueId,
|
|
|
path = string.Empty,
|
|
path = string.Empty,
|
|
@@ -160,7 +178,7 @@ namespace molilian.core
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
|
- /// 日报:拉取昨日 Redis 计数并落地到 track_daily_report(默认统计昨天,可传 reportDate)
|
|
|
|
|
|
|
+ /// 日报:拉取指定日期 Redis 计数并落地到 track_daily_report,可高频重复执行。
|
|
|
/// </summary>
|
|
/// </summary>
|
|
|
public static async Task<int> FlushDailyAsync(DateTime targetDate)
|
|
public static async Task<int> FlushDailyAsync(DateTime targetDate)
|
|
|
{
|
|
{
|
|
@@ -172,6 +190,7 @@ namespace molilian.core
|
|
|
|
|
|
|
|
int rows = 0;
|
|
int rows = 0;
|
|
|
using var conn = DBContext.GetOpenConnection();
|
|
using var conn = DBContext.GetOpenConnection();
|
|
|
|
|
+ var linkCache = new Dictionary<int, TrackLinkDTO>();
|
|
|
foreach (var member in indexMembers)
|
|
foreach (var member in indexMembers)
|
|
|
{
|
|
{
|
|
|
var parts = member.Split('|');
|
|
var parts = member.Split('|');
|
|
@@ -185,48 +204,100 @@ namespace molilian.core
|
|
|
int total = await RedisHelper.GetAsync<int>(countKey);
|
|
int total = await RedisHelper.GetAsync<int>(countKey);
|
|
|
if (total <= 0) continue;
|
|
if (total <= 0) continue;
|
|
|
|
|
|
|
|
- var exist = new DBContext.Table(conn, "track_daily_report")
|
|
|
|
|
- .Fields("id")
|
|
|
|
|
- .Get<dynamic>("report_date=@report_date AND event_type=@event_type AND track_link_id=@trackId",
|
|
|
|
|
- new { report_date = targetDate, event_type = eventType, trackId });
|
|
|
|
|
-
|
|
|
|
|
- var update = new DBContext.Table(conn, "track_daily_report")
|
|
|
|
|
- .Add("event_count", total)
|
|
|
|
|
- .Add("track_link_id", trackId)
|
|
|
|
|
- .Add("platform", string.Empty)
|
|
|
|
|
- .Add("scene", string.Empty)
|
|
|
|
|
- .Add("unique_id", string.Empty)
|
|
|
|
|
- .Add("update_time", DateTime.Now);
|
|
|
|
|
-
|
|
|
|
|
- if (exist == null)
|
|
|
|
|
- {
|
|
|
|
|
- update.Add("report_date", targetDate)
|
|
|
|
|
- .Add("event_type", eventType)
|
|
|
|
|
- .Add("create_time", DateTime.Now)
|
|
|
|
|
- .Create();
|
|
|
|
|
- }
|
|
|
|
|
- else
|
|
|
|
|
|
|
+ if (!linkCache.TryGetValue(trackId, out var link))
|
|
|
{
|
|
{
|
|
|
- update.Where("id=@id", new { exist.id }).Update();
|
|
|
|
|
|
|
+ link = new DBContext.Table(conn, "track_links")
|
|
|
|
|
+ .Get<TrackLinkDTO>("id=@id", new { id = trackId });
|
|
|
|
|
+ if (link != null) linkCache[trackId] = link;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ await UpsertDailyReportAsync(conn, targetDate.Date, eventType, trackId, link, total);
|
|
|
rows++;
|
|
rows++;
|
|
|
}
|
|
}
|
|
|
return rows;
|
|
return rows;
|
|
|
- }
|
|
|
|
|
- public static string BuildPath(int trackId, TkDataDTO result)
|
|
|
|
|
- {
|
|
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static Task<int> UpsertDailyReportAsync(IDbConnection conn, DateTime reportDate, string eventType, int trackId, TrackLinkDTO link, int total)
|
|
|
|
|
+ {
|
|
|
|
|
+ const string sql = @"
|
|
|
|
|
+INSERT INTO track_daily_report
|
|
|
|
|
+ (report_date, event_type, track_link_id, platform, typename, scene, unique_id, event_count, create_time, update_time)
|
|
|
|
|
+VALUES
|
|
|
|
|
+ (@reportDate, @eventType, @trackId, @platform, @typename, @scene, @uniqueId, @eventCount, @now, @now)
|
|
|
|
|
+ON DUPLICATE KEY UPDATE
|
|
|
|
|
+ event_count = VALUES(event_count),
|
|
|
|
|
+ platform = VALUES(platform),
|
|
|
|
|
+ typename = VALUES(typename),
|
|
|
|
|
+ scene = VALUES(scene),
|
|
|
|
|
+ unique_id = VALUES(unique_id),
|
|
|
|
|
+ update_time = VALUES(update_time);";
|
|
|
|
|
+
|
|
|
|
|
+ return conn.ExecuteAsync(sql, new
|
|
|
|
|
+ {
|
|
|
|
|
+ reportDate,
|
|
|
|
|
+ eventType,
|
|
|
|
|
+ trackId,
|
|
|
|
|
+ platform = link?.platform ?? string.Empty,
|
|
|
|
|
+ typename = link?.typename ?? string.Empty,
|
|
|
|
|
+ scene = link?.scene ?? string.Empty,
|
|
|
|
|
+ uniqueId = link?.unique_id ?? string.Empty,
|
|
|
|
|
+ eventCount = total,
|
|
|
|
|
+ now = DateTime.Now
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public static async Task<List<TrackHourlyReportDTO>> GetHourlyReportAsync(int trackId, DateTime targetDate)
|
|
|
|
|
+ {
|
|
|
|
|
+ var result = new List<TrackHourlyReportDTO>();
|
|
|
|
|
+ if (trackId <= 0) return result;
|
|
|
|
|
+
|
|
|
|
|
+ var link = await GetLinkByIdAsync(trackId);
|
|
|
|
|
+ if (link == null) return result;
|
|
|
|
|
+
|
|
|
|
|
+ string eventType = NormalizeEventType(link.event_type);
|
|
|
|
|
+ if (!SupportEventTypes.Contains(eventType)) return result;
|
|
|
|
|
+
|
|
|
|
|
+ string dateStr = targetDate.ToString("yyyyMMdd");
|
|
|
|
|
+ for (int hour = 0; hour < 24; hour++)
|
|
|
|
|
+ {
|
|
|
|
|
+ string hourStr = $"{dateStr}{hour:00}";
|
|
|
|
|
+ string countKey = $"{RedisPrefix}:hour:{eventType}:{trackId}:{hourStr}";
|
|
|
|
|
+ int total = await RedisHelper.GetAsync<int>(countKey);
|
|
|
|
|
+
|
|
|
|
|
+ result.Add(new TrackHourlyReportDTO
|
|
|
|
|
+ {
|
|
|
|
|
+ track_link_id = trackId,
|
|
|
|
|
+ event_type = eventType,
|
|
|
|
|
+ platform = link.platform ?? string.Empty,
|
|
|
|
|
+ typename = link.typename ?? string.Empty,
|
|
|
|
|
+ scene = link.scene ?? string.Empty,
|
|
|
|
|
+ unique_id = link.unique_id ?? string.Empty,
|
|
|
|
|
+ report_date = targetDate.Date,
|
|
|
|
|
+ hour = hour,
|
|
|
|
|
+ event_count = total
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public static string BuildPath(TrackType type, TkDataDTO result)
|
|
|
|
|
+ {
|
|
|
|
|
+ int trackId = type == TrackType.Click ? 1 : 2;
|
|
|
if (result == null) return string.Empty;
|
|
if (result == null) return string.Empty;
|
|
|
string unique_id = $"1|{result.itemId}_{result.mktId}";
|
|
string unique_id = $"1|{result.itemId}_{result.mktId}";
|
|
|
return BuildPath(trackId, unique_id);
|
|
return BuildPath(trackId, unique_id);
|
|
|
}
|
|
}
|
|
|
- public static string BuildPath(int trackId, JdDataDTO result)
|
|
|
|
|
|
|
+ public static string BuildPath(TrackType type, JdDataDTO result)
|
|
|
{
|
|
{
|
|
|
|
|
+ int trackId = type == TrackType.Click ? 19 : 20;
|
|
|
if (result == null) return string.Empty;
|
|
if (result == null) return string.Empty;
|
|
|
string unique_id = $"13|{result.shortLinkurl.UrlEncode()}";
|
|
string unique_id = $"13|{result.shortLinkurl.UrlEncode()}";
|
|
|
return BuildPath(trackId, unique_id);
|
|
return BuildPath(trackId, unique_id);
|
|
|
}
|
|
}
|
|
|
- public static string BuildPath(int trackId, PddDataDTO result)
|
|
|
|
|
|
|
+ public static string BuildPath(TrackType type, PddDataDTO result)
|
|
|
{
|
|
{
|
|
|
|
|
+ int trackId = type == TrackType.Click ? 21 : 22;
|
|
|
if (result == null) return string.Empty;
|
|
if (result == null) return string.Empty;
|
|
|
string unique_id = $"9|{result.shortLinkurl.UrlEncode()}";
|
|
string unique_id = $"9|{result.shortLinkurl.UrlEncode()}";
|
|
|
return BuildPath(trackId, unique_id);
|
|
return BuildPath(trackId, unique_id);
|
|
@@ -238,12 +309,18 @@ namespace molilian.core
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public static string GetLinkCacheKey(string eventType, string typename, string scene, string uniqueId)
|
|
public static string GetLinkCacheKey(string eventType, string typename, string scene, string uniqueId)
|
|
|
|
|
+ {
|
|
|
|
|
+ return GetLinkCacheKey(eventType, string.Empty, typename, scene, uniqueId);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public static string GetLinkCacheKey(string eventType, string platform, string typename, string scene, string uniqueId)
|
|
|
{
|
|
{
|
|
|
eventType = NormalizeEventType(eventType);
|
|
eventType = NormalizeEventType(eventType);
|
|
|
|
|
+ platform = Normalize(platform);
|
|
|
typename = Normalize(typename);
|
|
typename = Normalize(typename);
|
|
|
scene = NormalizeOrAll(scene);
|
|
scene = NormalizeOrAll(scene);
|
|
|
uniqueId = NormalizeOrAll(uniqueId);
|
|
uniqueId = NormalizeOrAll(uniqueId);
|
|
|
- return $"{RedisPrefix}:link:{eventType}:{typename}:{scene}:{uniqueId}";
|
|
|
|
|
|
|
+ return $"{RedisPrefix}:link:{eventType}:{platform}:{typename}:{scene}:{uniqueId}";
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public static string GetLinkIdCacheKey(int trackId)
|
|
public static string GetLinkIdCacheKey(int trackId)
|