Sfoglia il codice sorgente

增加pdd预检配置

dodo hold 3 mesi fa
parent
commit
cb30ec9071

+ 4 - 2
molilian.api/Controllers/admin/ConfigController.cs

@@ -68,8 +68,10 @@ namespace molilian.api.Controllers
                 _ = await conn2.UpdateAsync<TkConfigDTO>(form);
             }
 
-            string desc = ObjectComparer.PrintCompareToString(old, form).Trim();
-            OperationLogCore.LogOperation(token.AccessKey, clientIp, "config", old.Convert2Json(), desc);
+            string desc = ObjectComparer.PrintCompareToString(old, form).Trim();
+            OperationLogCore.LogOperation(token.AccessKey, clientIp, "config", old.Convert2Json(), desc);
+            TkConfigCore.Refresh();
+            PddUnionPlus.ResetPrecheckCircuitState();
             EndPointCore.NotifyReload();
             return new APIResult(new { msg = "更新成功", success = result > 0 });
         }

+ 2 - 1
molilian.api/Controllers/admin/TracksAdminController.cs

@@ -106,9 +106,10 @@ namespace molilian.api.Controllers
             var token = provider.Get(_accessor.HttpContext);
             int linkId = form.Read("track_link_id", 0);
             string dateText = form.Read("date", string.Empty);
+            string scene = form.Read("scene", string.Empty);
             DateTime date = DateTime.TryParse(dateText, out var parsed) ? parsed.Date : DateTime.Now.Date;
 
-            var list = await TracksCore.GetHourlyReportAsync(linkId, date);
+            var list = await TracksCore.GetHourlyReportAsync(linkId, date, scene);
             return new APIResult(new
             {
                 data = new

+ 4 - 3
molilian.api/Controllers/public/TracksController.cs

@@ -19,7 +19,7 @@ namespace molilian.api.Controllers
         }
 
         [HttpGet]
-        public async Task<ActionResult> Track([FromQuery] int track_id, [FromQuery] string unique_id)
+        public async Task<ActionResult> Track([FromQuery] int track_id, [FromQuery] string unique_id, [FromQuery] string scene = "")
         {
             if (track_id <= 0)
             {
@@ -32,20 +32,21 @@ namespace molilian.api.Controllers
                 return new APIResult(new { success = false, message = "not found" });
             }
 
+            string trackScene = TracksCore.ResolveMetricScene(link, scene);
             var dto = new TrackRequestLogDTO
             {
                 track_id = track_id,
                 event_type = link.event_type,
                 platform = link.platform,
                 typename = link.typename,
-                scene = link.scene,
+                scene = trackScene,
                 unique_id = unique_id,
                 ip = _accessor.HttpContext.GetUserIp(),
                 user_agent = Request.Headers.UserAgent.ToString(),
                 referer = Request.Headers.Referer.ToString()
             };
             _ = TracksCore.LogTrackRequestAsync(dto);
-            _ = TracksCore.TrackAsync(link);
+            _ = TracksCore.TrackAsync(link, trackScene);
             return new APIResult(new { success = true, message = "ok" });
         }
 

File diff suppressed because it is too large
+ 0 - 0
molilian.api/Properties/PublishProfiles/latest.pubxml.user


+ 137 - 30
molilian.core/Core/TracksCore.cs

@@ -144,16 +144,18 @@ namespace molilian.core
         /// <summary>
         /// 曝光/点击触发:计入 Redis,失败不影响返回
         /// </summary>
-        public static Task<bool> TrackAsync(TrackLinkDTO link)
+        public static Task<bool> TrackAsync(TrackLinkDTO link, string scene = "")
         {
 
             string dateStr = DateTime.Now.ToString("yyyyMMdd");
             string hourStr = DateTime.Now.ToString("yyyyMMddHH");
+            string metricScene = ResolveMetricScene(link, scene);
+            string indexValue = BuildMetricIndexValue(link.event_type, link.id, metricScene);
 
             var metrics = new List<(string key, string indexKey, string indexValue, int expire)>
             {
-                ($"{RedisPrefix}:daily:{link.event_type}:{link.id}:{dateStr}", $"{RedisPrefix}:daily:index:{dateStr}:track", $"{link.event_type}|{link.id}", DailyExpireSeconds),
-                ($"{RedisPrefix}:hour:{link.event_type}:{link.id}:{hourStr}", $"{RedisPrefix}:hour:index:{hourStr}:track", $"{link.event_type}|{link.id}", HourlyExpireSeconds),
+                (BuildDailyCountKey(link.event_type, link.id, dateStr, metricScene), $"{RedisPrefix}:daily:index:{dateStr}:track", indexValue, DailyExpireSeconds),
+                (BuildHourlyCountKey(link.event_type, link.id, hourStr, metricScene), $"{RedisPrefix}:hour:index:{hourStr}:track", indexValue, HourlyExpireSeconds),
             };
 
             try
@@ -188,19 +190,21 @@ namespace molilian.core
             var indexMembers = await RedisHelper.SMembersAsync<string>(indexKey) ?? [];
             if (indexMembers == null || indexMembers.Length == 0) return 0;
 
-            int rows = 0;
+            var buckets = new Dictionary<string, DailyReportBucket>();
             using var conn = DBContext.GetOpenConnection();
             var linkCache = new Dictionary<int, TrackLinkDTO>();
             foreach (var member in indexMembers)
             {
                 var parts = member.Split('|');
-                if (parts.Length != 2) continue;
+                if (parts.Length != 2 && parts.Length != 3) continue;
 
                 string eventType = NormalizeEventType(parts[0]);
                 if (!SupportEventTypes.Contains(eventType)) continue;
                 if (!int.TryParse(parts[1], out var trackId) || trackId <= 0) continue;
+                bool hasScenePart = parts.Length == 3;
+                string memberScene = hasScenePart ? DecodeIndexPart(parts[2]) : string.Empty;
 
-                string countKey = $"{RedisPrefix}:daily:{eventType}:{trackId}:{dateStr}";
+                string countKey = BuildDailyCountKey(eventType, trackId, dateStr, hasScenePart ? memberScene : string.Empty);
                 int total = await RedisHelper.GetAsync<int>(countKey);
                 if (total <= 0) continue;
 
@@ -211,13 +215,32 @@ namespace molilian.core
                     if (link != null) linkCache[trackId] = link;
                 }
 
-                await UpsertDailyReportAsync(conn, targetDate.Date, eventType, trackId, link, total);
+                string reportScene = ResolveMetricScene(link, memberScene);
+                string bucketKey = BuildMetricIndexValue(eventType, trackId, reportScene);
+                if (!buckets.TryGetValue(bucketKey, out var bucket))
+                {
+                    bucket = new DailyReportBucket
+                    {
+                        EventType = eventType,
+                        TrackId = trackId,
+                        Scene = reportScene,
+                        Link = link
+                    };
+                    buckets[bucketKey] = bucket;
+                }
+                bucket.Total += total;
+            }
+
+            int rows = 0;
+            foreach (var bucket in buckets.Values)
+            {
+                await UpsertDailyReportAsync(conn, targetDate.Date, bucket.EventType, bucket.TrackId, bucket.Scene, bucket.Link, bucket.Total);
                 rows++;
             }
             return rows;
         }
 
-        private static Task<int> UpsertDailyReportAsync(IDbConnection conn, DateTime reportDate, string eventType, int trackId, TrackLinkDTO link, int total)
+        private static Task<int> UpsertDailyReportAsync(IDbConnection conn, DateTime reportDate, string eventType, int trackId, string scene, TrackLinkDTO? link, int total)
         {
             const string sql = @"
 INSERT INTO track_daily_report
@@ -239,14 +262,14 @@ ON DUPLICATE KEY UPDATE
                 trackId,
                 platform = link?.platform ?? string.Empty,
                 typename = link?.typename ?? string.Empty,
-                scene = link?.scene ?? string.Empty,
+                scene,
                 uniqueId = link?.unique_id ?? string.Empty,
                 eventCount = total,
                 now = DateTime.Now
             });
         }
 
-        public static async Task<List<TrackHourlyReportDTO>> GetHourlyReportAsync(int trackId, DateTime targetDate)
+        public static async Task<List<TrackHourlyReportDTO>> GetHourlyReportAsync(int trackId, DateTime targetDate, string scene = "")
         {
             var result = new List<TrackHourlyReportDTO>();
             if (trackId <= 0) return result;
@@ -257,12 +280,17 @@ ON DUPLICATE KEY UPDATE
             string eventType = NormalizeEventType(link.event_type);
             if (!SupportEventTypes.Contains(eventType)) return result;
 
+            string metricScene = ResolveMetricScene(link, scene);
+            string linkScene = ResolveMetricScene(link, string.Empty);
             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);
+                int total = await RedisHelper.GetAsync<int>(BuildHourlyCountKey(eventType, trackId, hourStr, metricScene));
+                if (!string.IsNullOrEmpty(metricScene) && metricScene == linkScene)
+                {
+                    total += await RedisHelper.GetAsync<int>(BuildHourlyCountKey(eventType, trackId, hourStr, string.Empty));
+                }
 
                 result.Add(new TrackHourlyReportDTO
                 {
@@ -270,7 +298,7 @@ ON DUPLICATE KEY UPDATE
                     event_type = eventType,
                     platform = link.platform ?? string.Empty,
                     typename = link.typename ?? string.Empty,
-                    scene = link.scene ?? string.Empty,
+                    scene = metricScene,
                     unique_id = link.unique_id ?? string.Empty,
                     report_date = targetDate.Date,
                     hour = hour,
@@ -284,28 +312,38 @@ ON DUPLICATE KEY UPDATE
         public static string BuildPath(TrackType type, TkDataDTO result)
         {
             int trackId = type == TrackType.Click ? 1 : 2;
-            if (result == null) return string.Empty;
-            string unique_id = $"1|{result.itemId}_{result.mktId}";
-            return BuildPath(trackId, unique_id);
-        }
+            if (result == null) return string.Empty;
+            string unique_id = $"1|{result.itemId}_{result.mktId}";
+            return BuildPath(trackId, unique_id, GetTrackScene(result.parse_type, result.riskStrategy));
+        }
         public static string BuildPath(TrackType type, JdDataDTO result)
-        {
+        {
             int trackId = type == TrackType.Click ? 19 : 20;
-            if (result == null) return string.Empty;
-            string unique_id = $"13|{result.shortLinkurl.UrlEncode()}";
-            return BuildPath(trackId, unique_id);
-        }
+            if (result == null) return string.Empty;
+            string unique_id = $"13|{result.shortLinkurl.UrlEncode()}";
+            return BuildPath(trackId, unique_id, GetTrackScene(result.parse_type, result.riskStrategy));
+        }
         public static string BuildPath(TrackType type, PddDataDTO result)
-        {
+        {
             int trackId = type == TrackType.Click ? 21 : 22;
-            if (result == null) return string.Empty;
-            string unique_id = $"9|{result.shortLinkurl.UrlEncode()}";
-            return BuildPath(trackId, unique_id);
+            if (result == null) return string.Empty;
+            string unique_id = $"9|{result.shortLinkurl.UrlEncode()}";
+            return BuildPath(trackId, unique_id, GetTrackScene(result.parse_type, result.riskStrategy));
         }
-
-        public static string BuildPath(int trackId, string unique_id = "")
+
+        public static string BuildPath(int trackId, string unique_id = "", string scene = "")
+        {
+            scene = NormalizeReportScene(scene);
+            string url = $"https://api.molilian.com/tracks/track?track_id={trackId}&unique_id={unique_id}";
+            if (!string.IsNullOrEmpty(scene)) url += $"&scene={scene.UrlEncode()}";
+            return url;
+        }
+
+        public static string ResolveMetricScene(TrackLinkDTO? link, string scene = "")
         {
-            return $"https://api.molilian.com/tracks/track?track_id={trackId}&unique_id={unique_id}";
+            scene = NormalizeReportScene(scene);
+            if (!string.IsNullOrEmpty(scene)) return scene;
+            return NormalizeOrAll(link?.scene ?? string.Empty);
         }
 
         public static string GetLinkCacheKey(string eventType, string typename, string scene, string uniqueId)
@@ -411,6 +449,12 @@ ON DUPLICATE KEY UPDATE
             return (value ?? string.Empty).Trim();
         }
 
+        private static string NormalizeReportScene(string value)
+        {
+            var scene = Normalize(value);
+            return scene == "默认场景" ? string.Empty : scene;
+        }
+
         private static string NormalizeOrAll(string value)
         {
             var result = Normalize(value);
@@ -421,7 +465,70 @@ ON DUPLICATE KEY UPDATE
         {
             return Normalize(value).ToLowerInvariant();
         }
-
+
+        private static string GetTrackScene(string parseType, string riskStrategy)
+        {
+            parseType = NormalizeReportScene(parseType);
+            if (!string.IsNullOrEmpty(parseType)) return parseType;
+
+            riskStrategy = NormalizeReportScene(riskStrategy);
+            if (!string.IsNullOrEmpty(riskStrategy)) return riskStrategy;
+
+            return DefaultDimensionValue;
+        }
+
+        private static string BuildDailyCountKey(string eventType, int trackId, string dateStr, string scene)
+        {
+            eventType = NormalizeEventType(eventType);
+            scene = NormalizeReportScene(scene);
+            string key = $"{RedisPrefix}:daily:{eventType}:{trackId}:{dateStr}";
+            if (!string.IsNullOrEmpty(scene)) key += $":{EncodeIndexPart(scene)}";
+            return key;
+        }
+
+        private static string BuildHourlyCountKey(string eventType, int trackId, string hourStr, string scene)
+        {
+            eventType = NormalizeEventType(eventType);
+            scene = NormalizeReportScene(scene);
+            string key = $"{RedisPrefix}:hour:{eventType}:{trackId}:{hourStr}";
+            if (!string.IsNullOrEmpty(scene)) key += $":{EncodeIndexPart(scene)}";
+            return key;
+        }
+
+        private static string BuildMetricIndexValue(string eventType, int trackId, string scene)
+        {
+            eventType = NormalizeEventType(eventType);
+            scene = NormalizeReportScene(scene);
+            if (string.IsNullOrEmpty(scene)) return $"{eventType}|{trackId}";
+            return $"{eventType}|{trackId}|{EncodeIndexPart(scene)}";
+        }
+
+        private static string EncodeIndexPart(string value)
+        {
+            return Normalize(value).UrlEncode();
+        }
+
+        private static string DecodeIndexPart(string value)
+        {
+            try
+            {
+                return Normalize(value).UrlDecode();
+            }
+            catch
+            {
+                return Normalize(value);
+            }
+        }
+
+        private sealed class DailyReportBucket
+        {
+            public string EventType { get; set; } = string.Empty;
+            public int TrackId { get; set; }
+            public string Scene { get; set; } = string.Empty;
+            public TrackLinkDTO? Link { get; set; }
+            public int Total { get; set; }
+        }
+
         private static int GetTrackLinkId(IDbConnection conn, string eventType, string platform, string scene, string uniqueId)
         {
             try

+ 15 - 11
molilian.core/DTO/alimama/TkConfigDTO.cs

@@ -3,11 +3,11 @@ using System.ComponentModel.DataAnnotations.Schema;
 
 namespace molilian.core
 {
-    [Table("tk_config")]
-    public class TkConfigDTO
-    {
-        [Key]
-        public int id { get; set; }
+    [Table("tk_config")]
+    public class TkConfigDTO
+    {
+        [Key]
+        public int id { get; set; }
         public int ignorePercentage { get; set; } = 0;
         public string pass_salt { get; set; } = string.Empty;
         public string ignorePercentageCity { get; set; } = string.Empty;
@@ -47,12 +47,16 @@ namespace molilian.core
 
         public int pddIgnorePercentage { get; set; } = 0;
         public string pddIgnorePercentageCity { get; set; } = string.Empty;
-        public int pdd_limit_per_ip_24h { get; set; } = 0;
-        public int pdd_limit_per_oaid_24h { get; set; } = 0;
-        public string pdd_blacklist_regular { get; set; } = string.Empty;
-
-
-        public int ksIgnorePercentage { get; set; } = 0;
+        public int pdd_limit_per_ip_24h { get; set; } = 0;
+        public int pdd_limit_per_oaid_24h { get; set; } = 0;
+        public string pdd_blacklist_regular { get; set; } = string.Empty;
+        public bool pdd_rrecheck_enabled { get; set; } = true;
+        public string pdd_precheck_client_id { get; set; } = string.Empty;
+        public string pdd_precheck_client_secret { get; set; } = string.Empty;
+        public string pdd_precheck_pid { get; set; } = string.Empty;
+
+
+        public int ksIgnorePercentage { get; set; } = 0;
         public string ksIgnorePercentageCity { get; set; } = string.Empty;
         public int ks_limit_per_ip_24h { get; set; } = 0;
         public int ks_limit_per_oaid_24h { get; set; } = 0;

+ 7 - 5
molilian.core/Plus/Alimama/parse.cs

@@ -774,11 +774,13 @@ namespace molilian.core
                 itemName = "点击打开淘宝APP",
                 shortLinkurl = shortLinkurl,
                 deeplink_url = deeplink_url,
-                create_time = DateTime.Now,
-                end_point = _end_point,
-                parse_type = request.Type,
-            };
-        }
+                create_time = DateTime.Now,
+                end_point = _end_point,
+                parse_type = request.Type,
+                riskStrategy = request.RiskStrategy,
+                launchScene = request.LaunchScene,
+            };
+        }
 
 
         public static string OppoDecode(string content)

+ 7 - 5
molilian.core/Plus/JDUnion/JdUnionPlus.cs

@@ -180,11 +180,13 @@ namespace molilian.core
                 itemName = "点击打开京东APP",
                 shortLinkurl = shortLinkurl,
                 deeplink_url = deeplink_url,
-                create_time = DateTime.Now,
-                end_point = _end_point,
-                parse_type = request.Type,
-            };
-        }
+                create_time = DateTime.Now,
+                end_point = _end_point,
+                parse_type = request.Type,
+                riskStrategy = request.RiskStrategy,
+                launchScene = request.LaunchScene,
+            };
+        }
 
         public static bool IsAffLlink(string url)
         {

+ 30 - 3
molilian.core/Plus/pdd/PddUnionPlus.cs

@@ -101,6 +101,15 @@ namespace molilian.core
             }
         }
 
+        public static void ResetPrecheckCircuitState()
+        {
+            lock (_precheckCircuitLock)
+            {
+                _precheckDisabled = false;
+                _precheckConsecutiveExceptionCount = 0;
+            }
+        }
+
         private static (int count, bool disabledNow) RegisterPrecheckException()
         {
             lock (_precheckCircuitLock)
@@ -252,6 +261,12 @@ namespace molilian.core
 
         public static async Task<RrecheckGoodsIdResult> PrecheckGoodsIdAsync(string content, CancellationToken cancellationToken = default)
         {
+            var config = TkConfigCore.Get();
+            if (config != null && !config.pdd_rrecheck_enabled)
+            {
+                return RrecheckGoodsIdResult.Success;
+            }
+
             string url = GetLink(content);
             if (string.IsNullOrEmpty(url))
             {
@@ -269,9 +284,21 @@ namespace molilian.core
             if (string.IsNullOrWhiteSpace(goods_url)) return RrecheckGoodsIdResult.Empty;
             try
             {
-                const string precheckClientId = "c8823690b47842649e4fee054317e0c1";
-                const string precheckClientSecret = "0b5eeab761a6b61df59f11296387b95523a26b09";
-                const string precheckPid = "13585632_187155840";
+                string precheckClientId = (config?.pdd_precheck_client_id ?? TkConfigDTO.DefaultPddPrecheckClientId).Trim();
+                string precheckClientSecret = (config?.pdd_precheck_client_secret ?? TkConfigDTO.DefaultPddPrecheckClientSecret).Trim();
+                string precheckPid = (config?.pdd_precheck_pid ?? TkConfigDTO.DefaultPddPrecheckPid).Trim();
+                if (string.IsNullOrWhiteSpace(precheckClientId) ||
+                    string.IsNullOrWhiteSpace(precheckClientSecret) ||
+                    string.IsNullOrWhiteSpace(precheckPid))
+                {
+                    _ = new LoggerLibrary("PddUnion", "PrecheckGoodsIdAsync.Config")
+                        .Info(goods_url, "precheck config missing")
+                        .SaveAsync();
+                    var status = RegisterPrecheckException();
+                    NotifyPrecheckException(goods_url, "Config", "precheck config missing", status.count, status.disabledNow);
+                    return status.disabledNow ? RrecheckGoodsIdResult.Success : RrecheckGoodsIdResult.Exception;
+                }
+
                 var precheckAccount = new PddPoolDTO
                 {
                     app_key = precheckClientId,

Some files were not shown because too many files changed in this diff