Преглед на файлове

拆分type=goods的处理方法

dodo hold преди 10 месеца
родител
ревизия
92e7236547

Файловите разлики са ограничени, защото са твърде много
+ 0 - 0
molilian.api/Properties/PublishProfiles/latest.pubxml.user


+ 5 - 6
molilian.core/Core/jd/JdPoolCore.cs

@@ -70,22 +70,21 @@ namespace molilian.core
             return eligibleItems.OrderBy(l => Guid.NewGuid()).FirstOrDefault();
         }
 
-        public static async Task<JdPoolDTO?> GetOneAsync(JdAction action, string riskStrategy, int launchScene, bool? is_numeric, string parse_type = "")
+        public static async Task<JdPoolDTO?> GetOneAsync(JdAction action, bool is_numeric, UnionParseRequest request)
         {
             var list = await ListAsync();
             if (!list.Any()) return null;
 
-            if ("dp".Equals(parse_type))
+            if (!string.IsNullOrEmpty(request.Type))
             {
-
-                list = list.Where(item => "dp".Equals(item.parse_type)).ToList();
+                list = list.Where(item => request.Type.Equals(item.parse_type)).ToList();
                 if (!list.Any()) return null;
             }
             else
             {
-                if (!string.IsNullOrEmpty(riskStrategy) && !"os".Equals(riskStrategy) && launchScene != -1)
+                if (!string.IsNullOrEmpty(request.RiskStrategy) && !"os".Equals(request.RiskStrategy) && request.LaunchScene != -1)
                 {
-                    list = list.Where(item => string.IsNullOrEmpty(item.parse_type) && riskStrategy.Equals(item.riskStrategy) && item.launchScene == launchScene).ToList();
+                    list = list.Where(item => string.IsNullOrEmpty(item.parse_type) && request.RiskStrategy.Equals(item.riskStrategy) && item.launchScene == request.LaunchScene).ToList();
                     if (!list.Any()) return null;
                 }
                 else

+ 386 - 28
molilian.core/Core/pdd/PddPoolCore.cs

@@ -37,10 +37,16 @@ namespace molilian.core
             _end_point = Environment.GetEnvironmentVariable("EndPoint");
         }
 
+        /// <summary>
+        /// 获取一个账号用于处理请求
+        /// 使用平滑加权轮询算法(Smooth Weighted Round-Robin)实现流量均衡分配
+        /// </summary>
         public static PddPoolDTO? GetOne(PddUnionWorkMode mode, int accountid = 0, string parse_type = "")
         {
             var list = List();
             if (!list.Any()) return null;
+
+            // 按 parse_type 过滤
             if ("dp".Equals(parse_type))
             {
                 list = list.Where(item => "dp".Equals(item.parse_type)).ToList();
@@ -50,13 +56,291 @@ namespace molilian.core
             {
                 list = list.Where(item => string.IsNullOrEmpty(item.parse_type)).ToList();
                 if (!list.Any()) return null;
+            }
+
+            // 过滤出符合条件的候选账号
+            var candidates = list.Where(e => IsMatch(e, mode, accountid)).ToList();
+            if (!candidates.Any()) return null;
+
+            // 使用平滑加权轮询算法选择账号
+            return SmoothWeightedRoundRobin(candidates, parse_type);
+        }
+
+        /// <summary>
+        /// 基于最后使用时间的调度策略(并发安全版本)
+        /// 选择"距离上次使用时间最长"且"超过cis_limit间隔"的账号
+        ///
+        /// 优势:
+        /// 1. 不区分新老账号,一视同仁(无历史记录的账号返回DateTime.MinValue,自然会被优先选中)
+        /// 2. 自然满足cis_limit要求(距离时间 < limit 会被跳过)
+        /// 3. 优先选择"休息最久"的账号,最大化每个账号的休息时间
+        /// 4. 简单直观,易于调试和理解
+        /// 5. 并发安全:使用Redis原子操作预留账号,避免多个请求竞争同一账号
+        /// </summary>
+        private static PddPoolDTO? SmoothWeightedRoundRobin(List<PddPoolDTO> candidates, string parse_type)
+        {
+            if (!candidates.Any()) return null;
+            if (candidates.Count == 1)
+            {
+                var single = candidates[0];
+                // 尝试预留账号(并发安全)
+                if (TryReserveAccount(single.id, parse_type, single.cis_limit))
+                {
+                    return single;
+                }
+                return null; // 预留失败,说明账号已被其他请求占用
+            }
+
+            try
+            {
+                string groupKey = string.IsNullOrEmpty(parse_type) ? "default" : parse_type;
+                var now = DateTime.Now;
+
+                // 候选账号列表:记录每个账号的空闲时长
+                var accountsWithIdleTime = new List<(PddPoolDTO account, long idleMs)>();
+
+                foreach (var account in candidates)
+                {
+                    // 获取账号最后使用时间
+                    var lastUsedTime = GetAccountLastUsedTime(account.id, groupKey);
+
+                    // 计算距离现在的时间差(毫秒)
+                    long idleMs = (long)(now - lastUsedTime).TotalMilliseconds;
+
+                    // 如果设置了 cis_limit,必须满足间隔要求
+                    if (account.cis_limit > 0 && idleMs < account.cis_limit)
+                    {
+                        continue; // 跳过未满足间隔要求的账号
+                    }
+
+                    accountsWithIdleTime.Add((account, idleMs));
+                }
+
+                // 如果没有满足条件的账号,返回null
+                if (!accountsWithIdleTime.Any()) return null;
+
+                // 按照空闲时间降序排序,优先尝试"休息最久"的账号
+                var sortedAccounts = accountsWithIdleTime
+                    .OrderByDescending(x => x.idleMs)
+                    .ToList();
+
+                // 依次尝试预留账号(从空闲时间最长的开始)
+                foreach (var (account, idleMs) in sortedAccounts)
+                {
+                    // 尝试原子预留此账号
+                    if (TryReserveAccount(account.id, groupKey, account.cis_limit))
+                    {
+                        // 预留成功,更新最后使用时间
+                        SetAccountLastUsedTime(account.id, groupKey, now);
+                        Console.WriteLine($"[PDD] Selected account {account.id} ({account.name}), idle time: {Math.Round(idleMs / 1000.0, 2)}s");
+                        return account;
+                    }
+                    else
+                    {
+                        // 预留失败,说明此账号已被其他并发请求占用,尝试下一个
+                        Console.WriteLine($"[PDD] Account {account.id} ({account.name}) is reserved by another request, trying next...");
+                    }
+                }
+
+                // 所有满足条件的账号都已被占用
+                Console.WriteLine($"[PDD] All available accounts are reserved, no account available");
+                return null;
+            }
+            catch (Exception ex)
+            {
+                Console.WriteLine($"Idle time selection failed: {ex.Message}, fallback to simple strategy");
+
+                // 降级策略:使用原有的最少使用次数算法
+                return candidates
+                    .OrderBy(account => GetCurrentDayUsageFromRedis(account.id))
+                    .ThenBy(account => Guid.NewGuid())
+                    .FirstOrDefault();
+            }
+        }
+
+        /// <summary>
+        /// 尝试原子预留账号(并发安全)
+        /// 使用 Redis SET NX EX 实现原子操作,避免多个请求竞争同一账号
+        /// </summary>
+        /// <param name="accountId">账号ID</param>
+        /// <param name="groupKey">分组键</param>
+        /// <param name="cisLimit">请求间隔限制(毫秒),0表示无限制</param>
+        /// <returns>true=预留成功,false=账号已被占用</returns>
+        private static bool TryReserveAccount(int accountId, string groupKey, int cisLimit)
+        {
+            try
+            {
+                string reserveKey = $"pdd_account_reserved:{groupKey}:{accountId}";
+
+                // 如果没有设置 cis_limit,使用默认100ms的预留时间(防止极端并发)
+                int expireSeconds = cisLimit > 0 ? (int)Math.Ceiling(cisLimit / 1000.0) : 1;
+
+                // 使用 Redis SET NX EX 原子操作(一次性完成设置和过期时间)
+                // 等价于 Redis 命令: SET key value NX EX seconds
+                // 如果 key 不存在则设置成功(返回true),如果已存在则失败(返回false)
+                // 注意:必须使用原子操作,SetNx + Expire 两步操作会有竞态条件!
+                bool reserved = RedisHelper.Set(reserveKey, 1, expireSeconds, CSRedis.RedisExistence.Nx);
+                return reserved;
+            }
+            catch (Exception ex)
+            {
+                Console.WriteLine($"Failed to reserve account {accountId}: {ex.Message}");
+                // 预留失败时保守处理:假设账号不可用
+                return false;
+            }
+        }
+
+        /// <summary>
+        /// 获取账号最后使用时间
+        /// </summary>
+        private static DateTime GetAccountLastUsedTime(int accountId, string groupKey)
+        {
+            try
+            {
+                string key = $"pdd_last_used_time:{groupKey}:{accountId}";
+                var timestamp = RedisHelper.Get<long>(key);
+
+                if (timestamp > 0)
+                {
+                    return DateTimeOffset.FromUnixTimeMilliseconds(timestamp).LocalDateTime;
+                }
+
+                // 如果没有记录,返回很久以前的时间(确保新账号和长期未使用的账号能被优先选中)
+                return DateTime.MinValue;
+            }
+            catch
+            {
+                return DateTime.MinValue;
+            }
+        }
+
+        /// <summary>
+        /// 设置账号最后使用时间
+        /// </summary>
+        private static void SetAccountLastUsedTime(int accountId, string groupKey, DateTime time)
+        {
+            try
+            {
+                string key = $"pdd_last_used_time:{groupKey}:{accountId}";
+                long timestamp = new DateTimeOffset(time).ToUnixTimeMilliseconds();
+
+                // 设置过期时间为24小时,避免数据积累
+                RedisHelper.Set(key, timestamp, 86400);
+            }
+            catch (Exception ex)
+            {
+                Console.WriteLine($"Failed to set last used time for {accountId}: {ex.Message}");
+            }
+        }
+
+        /// <summary>
+        /// 获取账号的静态权重
+        /// 可以根据账号的 daily_calls_limit 或其他因素动态计算
+        /// </summary>
+        private static int GetAccountStaticWeight(int accountId)
+        {
+            try
+            {
+                string key = $"pdd_account_weight:{accountId}";
+                int weight = RedisHelper.Get<int>(key);
+
+                // 如果没有设置,返回默认权重100
+                if (weight <= 0)
+                {
+                    // 可以基于账号的限额动态计算默认权重
+                    var account = List()?.FirstOrDefault(a => a.id == accountId);
+                    if (account != null && account.daily_calls_limit > 0)
+                    {
+                        // 将日限额映射到权重:每1000次调用对应权重10
+                        weight = Math.Max(10, Math.Min(1000, account.daily_calls_limit / 100));
+                    }
+                    else
+                    {
+                        weight = 100; // 默认权重
+                    }
+                }
+
+                return weight;
+            }
+            catch
+            {
+                return 100; // 异常时返回默认权重
+            }
+        }
+
+        /// <summary>
+        /// 设置账号的静态权重
+        /// </summary>
+        public static void SetAccountStaticWeight(int accountId, int weight)
+        {
+            try
+            {
+                if (weight < 1) weight = 1;
+                if (weight > 1000) weight = 1000;
 
+                string key = $"pdd_account_weight:{accountId}";
+                RedisHelper.Set(key, weight, 30 * 86400); // 30天过期
+            }
+            catch (Exception ex)
+            {
+                Console.WriteLine($"Failed to set account weight for {accountId}: {ex.Message}");
+            }
+        }
+
+        /// <summary>
+        /// 获取账号的当前动态权重
+        /// </summary>
+        private static int GetAccountCurrentWeight(int accountId, string groupKey)
+        {
+            try
+            {
+                string key = $"pdd_current_weight:{groupKey}:{accountId}";
+                return RedisHelper.Get<int>(key);
             }
+            catch
+            {
+                return 0;
+            }
+        }
+
+        /// <summary>
+        /// 设置账号的当前动态权重
+        /// </summary>
+        private static void SetAccountCurrentWeight(int accountId, string groupKey, int weight)
+        {
+            try
+            {
+                string key = $"pdd_current_weight:{groupKey}:{accountId}";
+                RedisHelper.Set(key, weight, 3600); // 1小时过期,自动重置
+            }
+            catch (Exception ex)
+            {
+                Console.WriteLine($"Failed to set current weight for {accountId}: {ex.Message}");
+            }
+        }
+
+        /// <summary>
+        /// 重置所有账号的动态权重(用于调试或重新初始化)
+        /// </summary>
+        public static void ResetAllWeights(string groupKey = "default")
+        {
+            try
+            {
+                var accounts = List()?.Where(a => a.status && a.enable_parse).ToList();
+                if (accounts == null || !accounts.Any()) return;
+
+                foreach (var account in accounts)
+                {
+                    string key = $"pdd_current_weight:{groupKey}:{account.id}";
+                    RedisHelper.Del(key);
+                }
 
-            return list.Where(e => IsMatch(e, mode, accountid))
-                .OrderBy(account => GetCurrentDayUsageFromRedis(account.id))
-                .ThenBy(account => Guid.NewGuid()) // 相同使用次数时随机排序
-                .FirstOrDefault();
+                Console.WriteLine($"Reset weights for {accounts.Count} accounts in group '{groupKey}'");
+            }
+            catch (Exception ex)
+            {
+                Console.WriteLine($"Failed to reset weights: {ex.Message}");
+            }
         }
 
         private static int GetCurrentDayUsageFromRedis(int accountId)
@@ -163,31 +447,50 @@ namespace molilian.core
         }
 
         /// <summary>
-        /// 为新上线的账号设置平均使用次数,避免流量集中
+        /// 为新上线的账号设置平均使用次数(仅用于降级策略的统计)
+        /// 注意:基于时间间隔的调度策略不需要初始化权重,因为无历史记录的账号会自动返回DateTime.MinValue
         /// </summary>
-        public static void InitializeNewAccountUsage(int accountId)
+        public static void InitializeNewAccountUsage(int accountId, string groupKey = "default")
         {
             try
             {
                 var today = DateTime.Now.ToString("yyyyMMdd");
-                string key = $"pdd_daily_usage:{accountId}:{today}";
+                string usageKey = $"pdd_daily_usage:{accountId}:{today}";
 
                 // 检查该账号今天是否已有使用记录
-                var currentUsage = RedisHelper.Get<int>(key);
-                if (currentUsage > 0) return; // 已有记录,不需要初始化
+                var currentUsage = RedisHelper.Get<int>(usageKey);
+                if (currentUsage > 0)
+                {
+                    Console.WriteLine($"Account {accountId} already has usage record: {currentUsage}, skip initialization");
+                    return; // 已有记录,不需要初始化
+                }
+
+                // 获取所有在线账号的平均值(仅用于降级策略)
+                var activeAccounts = List()?.Where(a => a.status && a.enable_parse && a.id != accountId).ToList();
+                if (activeAccounts == null || !activeAccounts.Any())
+                {
+                    Console.WriteLine($"No other active accounts found, account {accountId} will use default value");
+                    return; // 没有其他账号,使用默认值0即可
+                }
 
-                // 获取平均使用次数
-                var averageUsage = GetAverageUsageCount();
+                // 初始化 daily_usage(用于降级策略的统计)
+                var totalUsage = 0;
+                foreach (var account in activeAccounts)
+                {
+                    totalUsage += GetCurrentDayUsageFromRedis(account.id);
+                }
+                var averageUsage = totalUsage / activeAccounts.Count;
                 if (averageUsage > 0)
                 {
-                    // 设置为平均值,避免新账号因为使用次数为0而被优先选择
-                    RedisHelper.Set(key, averageUsage, 86400);
-                    Console.WriteLine($"Initialized account {accountId} with average usage: {averageUsage}");
+                    RedisHelper.Set(usageKey, averageUsage, 86400);
+                    Console.WriteLine($"Initialized account {accountId} daily_usage with average: {averageUsage}");
                 }
+
+                Console.WriteLine($"Account {accountId} initialization complete. Time-based scheduling will use DateTime.MinValue for idle time calculation.");
             }
             catch (Exception ex)
             {
-                Console.WriteLine($"Failed to initialize new account usage for {accountId}: {ex.Message}");
+                Console.WriteLine($"Failed to initialize new account for {accountId}: {ex.Message}");
             }
         }
         /// <summary>
@@ -197,13 +500,18 @@ namespace molilian.core
         {
             try
             {
+                var result = new Dictionary<string, int>();
+                var accounts = List()?.Where(a => a.status && a.enable_parse).ToList();
+                if (accounts == null || !accounts.Any()) return result;
+
                 var today = DateTime.Now.ToString("yyyyMMdd");
-                var pattern = $"pdd_daily_usage:*:{today}";
+                foreach (var account in accounts)
+                {
+                    string key = $"pdd_daily_usage:{account.id}:{today}";
+                    int usage = RedisHelper.Get<int>(key);
+                    result[$"Account_{account.id}_{account.name}"] = usage;
+                }
 
-                // 注意:这里只是示例,实际实现可能需要根据Redis客户端API调整
-                // 生产环境中应该避免使用KEYS命令,可以考虑其他方案
-                var result = new Dictionary<string, int>();
-                // TODO: 实现Redis pattern匹配获取所有相关keys
                 return result;
             }
             catch (Exception)
@@ -212,6 +520,58 @@ namespace molilian.core
             }
         }
 
+        /// <summary>
+        /// 获取账号调度时间间隔信息(用于监控和调试)
+        /// </summary>
+        public static Dictionary<string, object> GetScheduleWeightSnapshot(string groupKey = "default")
+        {
+            try
+            {
+                var result = new Dictionary<string, object>();
+                var accounts = List()?.Where(a => a.status && a.enable_parse).ToList();
+                if (accounts == null || !accounts.Any()) return result;
+
+                var now = DateTime.Now;
+                var accountInfos = new List<Dictionary<string, object>>();
+
+                foreach (var account in accounts)
+                {
+                    var lastUsedTime = GetAccountLastUsedTime(account.id, groupKey);
+                    long idleMs = (long)(now - lastUsedTime).TotalMilliseconds;
+                    bool canUse = account.cis_limit <= 0 || idleMs >= account.cis_limit;
+
+                    var info = new Dictionary<string, object>
+                    {
+                        ["account_id"] = account.id,
+                        ["account_name"] = account.name,
+                        ["cis_limit_ms"] = account.cis_limit,
+                        ["last_used_time"] = lastUsedTime == DateTime.MinValue ? "从未使用" : lastUsedTime.ToString("yyyy-MM-dd HH:mm:ss.fff"),
+                        ["idle_time_ms"] = idleMs,
+                        ["idle_time_seconds"] = Math.Round(idleMs / 1000.0, 2),
+                        ["can_use"] = canUse,
+                        ["daily_usage"] = GetCurrentDayUsageFromRedis(account.id),
+                        ["daily_limit"] = account.daily_calls_limit
+                    };
+                    accountInfos.Add(info);
+                }
+
+                // 按照空闲时间降序排序(与选择逻辑一致)
+                accountInfos = accountInfos.OrderByDescending(x => (long)x["idle_time_ms"]).ToList();
+
+                result["accounts"] = accountInfos;
+                result["group_key"] = groupKey;
+                result["timestamp"] = now.ToString("yyyy-MM-dd HH:mm:ss.fff");
+                result["total_accounts"] = accountInfos.Count;
+                result["available_accounts"] = accountInfos.Count(x => (bool)x["can_use"]);
+
+                return result;
+            }
+            catch (Exception ex)
+            {
+                return new Dictionary<string, object> { ["error"] = ex.Message };
+            }
+        }
+
         /// <summary>
         /// 强制清理统计数据(仅用于测试或紧急情况)- Redis版本
         /// </summary>
@@ -258,12 +618,9 @@ namespace molilian.core
                 if (ts.TotalSeconds < 70) return false;
             }
 
-            if (item.cis_limit > 0)
-            {
-                string lockKey = $"pdd_cis_limit_{accountid}";
-                int cis_num = RedisHelper.Get<int>(lockKey);
-                if (cis_num > 0) return false;
-            }
+            // cis_limit 检查已移至 SmoothWeightedRoundRobin 方法中基于时间间隔判断
+            // 这里不再需要检查 Redis 锁
+
             if (item.rpm_limit > 0)
             {
                 int rpm_num = RiskControlCore.GetCalls(TkChannelEnum.pdd, item.id, DateTime.Now.ToString("yyyyMMddHHmm"));
@@ -471,8 +828,9 @@ namespace molilian.core
                 if (status)
                 {
                     _ = List(true);
-                    // 为重新上线的账号初始化使用次数
-                    InitializeNewAccountUsage(exist.id);
+                    // 为重新上线的账号初始化使用次数和权重(针对所有分组)
+                    InitializeNewAccountUsage(exist.id, "default");
+                    InitializeNewAccountUsage(exist.id, "dp");
                 }
             }
             else

+ 350 - 124
molilian.core/Core/taoke/UnionParseCore/UnionParseCore.cs

@@ -24,17 +24,24 @@ namespace molilian.core
             }
 
             request.Content = request.Content.Trim();
-
-            if ("dp".Equals(request.Type))
+            switch (request.Type)
             {
-                return request.Channel switch
-                {
-                    "jd" => await DeeplinkJdParseAsync(request),
-                    "pdd" => await DeeplinkPddParseAsync(request),
-                    "tb" => await DeeplinkTaobaoParseAsync(request),
-                };
+                case "dp":
+                    return request.Channel switch
+                    {
+                        "jd" => await DeeplinkJdParseAsync(request),
+                        "pdd" => await DeeplinkPddParseAsync(request),
+                        "tb" => await DeeplinkTaobaoParseAsync(request),
+                    };
+                case "goods":
+                    return request.Channel switch
+                    {
+                        "jd" => await GoodsJdParseAsync(request),
+                        "pdd" => await GoodsPddParseAsync(request),
+                    };
             }
 
+
             return request.Channel switch
             {
                 //"wemeet" => await WemeetParseAsync(request),
@@ -572,6 +579,181 @@ namespace molilian.core
             }, code);
         }
 
+        public static async Task<APIResult> GoodsJdParseAsync(UnionParseRequest request, CancellationToken cancellationToken = default)
+        {
+            var config = TkConfigCore.Get();
+            var result = JdUnionPlus.GetFormattedObject(request.Content, request.Ip, request.Oaid, request.ClickId);
+
+            result.rawContent = request.Content;
+            result.riskStrategy = request.RiskStrategy;
+            result.launchScene = request.LaunchScene;
+            bool IfExceptional = false;
+            bool is_hide = false;
+            string content = request.Content;
+            try
+            {
+
+                string goods_pattern = @"(?:sku=|sku%3D|sku%253D)(\d+)";
+                Match match = Regex.Match(content, goods_pattern);
+                if (match.Success)
+                {
+                    string goods_id = match.Groups[1].Value; // 返回匹配的 URL
+                    if (string.IsNullOrEmpty(goods_id))
+                    {
+                        result.success = false;
+                        result.deeplink_url = string.Empty;
+                        result.message = "放弃转链";
+                        result.reason = "无效商品ID";
+                        _ = TkLogCore.ParseLogAsync(result, is_hide);
+                        return JdParseOutput(result);
+                    }
+                    content = $"https://item.jd.com/{goods_id}.html";
+                    result.shortLinkurl = content;
+                }
+                else
+                {
+                    result.success = false;
+                    result.deeplink_url = string.Empty;
+                    result.message = "放弃转链";
+                    result.reason = "无效商品ID";
+                    _ = TkLogCore.ParseLogAsync(result, is_hide);
+                    return JdParseOutput(result);
+                }
+
+                if (!string.IsNullOrEmpty(config.jd_blacklist_regular))
+                {
+                    var blacklist = config.jd_blacklist_regular.Split('\n')
+                        .Where(line => !string.IsNullOrWhiteSpace(line))
+                        .ToList();
+                    bool anyMatch = false;
+                    foreach (string pattern in blacklist)
+                    {
+                        if (Regex.IsMatch(content, pattern, RegexOptions.IgnoreCase)) anyMatch = true;
+                    }
+                    if (anyMatch)
+                    {
+                        result.success = false;
+                        result.deeplink_url = string.IsNullOrEmpty(result.shortLinkurl) ? string.Empty : JdUnionPlus.GetDeeplink(result.shortLinkurl, request.ClickId);
+                        result.message = "放弃转链";
+                        result.reason = "规则排除";
+                        _ = TkLogCore.ParseLogAsync(result, is_hide);
+                        return JdParseOutput(result);
+                    }
+                }
+                string out_url = null;
+                string itemId = null;
+
+                //============================== 放弃转链-地区过滤 ==============================
+                if (!TestParseCore.InWhitelist(result.ip, result.oaid) && request.AccountId == 0 && JdUnionPlus.ShouldIgnoreRequest(config, request.Ip, request.Oaid, request.RiskStrategy, request.LaunchScene, out string reason))
+                {
+                    result.link_type = JdUnionPlus.GetLinkType(result.shortLinkurl, out out_url, out itemId);
+
+                    result.deeplink_url = result.link_type switch
+                    {
+                        LinkTypeEnum.other_aff or
+                        LinkTypeEnum.live or
+                        LinkTypeEnum.video or
+                        LinkTypeEnum.profile or
+                        LinkTypeEnum.goods => JdUnionPlus.GetDefaultStyleDeeplink(result.shortLinkurl),
+                        _ => JdUnionPlus.GetDefaultStyleDeeplink(result.shortLinkurl),
+                    };
+
+                    //if (result.link_type == LinkTypeEnum.other_aff)
+                    //{
+                    //    //20241204 特例
+                    //    if ("bj".Equals(EndPointCore.CurrentEndPoint))
+                    //    {
+                    //        string ruleKey = $"tmp_rules1:jd:{DateTime.Now:yyyyMMdd}";
+                    //        var count = RedisHelper.Get<int>(ruleKey);
+                    //        if (DateTime.Now < DateTime.Parse("2024-12-07") && count < 500)
+                    //        {
+                    //            result.deeplink_url = "openapp.jdmobile://virtual?params=%7B%22des%22%3A%22union%22%2C%22category%22%3A%22jump%22%2C%22url%22%3A%22https%3A%2F%2Fu.jd.com%2FugInesd%3Fe%3DCPS-11417-__ZLDEVICE__-CPS-wkfG72%22%7D&backurl=__back_url__";
+                    //            RedisHelper.IncrBy(ruleKey);
+                    //            RedisHelper.Expire(ruleKey, 86400 * 1);
+                    //        }
+
+                    //    }
+                    //}
+                    result.success = false;
+                    result.message = "放弃转链";
+                    result.reason = reason;
+                    _ = TkLogCore.ParseLogAsync(result, is_hide);
+                    return JdParseOutput(result);
+                }
+
+                result.link_type = JdUnionPlus.GetLinkType(result.shortLinkurl, out out_url, out itemId);
+
+                bool is_numeric = result.shortLinkurl.Contains(itemId);
+
+                var account = request.AccountId > 0 ?
+                    await JdPoolCore.GetOneAsync(request.AccountId, JdPoolCore.JdAction.parse) :
+                    await JdPoolCore.GetOneAsync(JdPoolCore.JdAction.parse, is_numeric, request);
+                if (account == null)
+                {
+                    result.success = false;
+                    result.message = "放弃转链";
+                    result.reason = "没有匹配账号";
+                    result.deeplink_url = result.link_type switch
+                    {
+                        LinkTypeEnum.other_aff or LinkTypeEnum.goods => JdUnionPlus.GetDefaultStyleDeeplink(result.shortLinkurl),
+                        _ => JdUnionPlus.GetDefaultStyleDeeplink(result.shortLinkurl),
+                    };
+
+                    _ = TkLogCore.ParseLogAsync(result, is_hide);
+                    return JdParseOutput(result);
+                }
+                result.accountId = account.id;
+                result.accountName = account.name;
+                is_hide = account.is_hide;
+
+                var plus = new JdUnionPlus(account);
+                result.proxy_name = plus._proxyName;
+                result.proxy_node = plus._proxy?.Address?.Host;
+
+                result = await plus.JdParseAsync(content, request.CommerceType, request.ClickId, result, cancellationToken);
+            }
+            catch (Exception ex)
+            {
+                if (ex.Message.Contains("was canceled"))
+                {
+                    result.success = false;
+                    result.message = "放弃转链";
+                    result.reason = "请求超时";
+                }
+                else
+                {
+                    IfExceptional = true;
+                    _ = new LoggerLibrary("unionParse", "jd_error")
+                        .Info(request.Ip, request.Oaid)
+                        .Info(content)
+                        .Info(ex.Message, ex.StackTrace)
+                        .SaveAsync();
+                    NotifyCore.Notify(new NifyMessage
+                    {
+                        message = $"【转链异常JD】\n{content}\n\n{ex.Message}\n{ex.StackTrace}",
+                        priority = NifyMessagePriority.high,
+                        tags = ["red_circle"]
+                    });
+                    result.success = false;
+                    result.message = "内部错误";
+                    result.reason = "转链接口异常";
+                }
+                result.deeplink_url = result.link_type switch
+                {
+                    LinkTypeEnum.other_aff or LinkTypeEnum.goods => JdUnionPlus.GetDefaultStyleDeeplink(result.shortLinkurl),
+                    _ => JdUnionPlus.GetDefaultStyleDeeplink(result.shortLinkurl),
+                };
+            }
+            if (!result.success)
+            {
+                result.deeplink_url = string.IsNullOrEmpty(result.shortLinkurl) ? string.Empty : JdUnionPlus.GetDeeplink(result.shortLinkurl, request.ClickId);
+
+            }
+            _ = TkLogCore.ParseLogAsync(result, is_hide);
+            return JdParseOutput(result, IfExceptional ? APIResultCodeEnum.NotAcceptable : APIResultCodeEnum.OK);
+
+        }
+
         public static async Task<APIResult> JdParseAsync(UnionParseRequest request, CancellationToken cancellationToken = default)
         {
             var config = TkConfigCore.Get();
@@ -583,6 +765,7 @@ namespace molilian.core
             bool IfExceptional = false;
             bool is_hide = false;
             string content = request.Content;
+            content = content.UrlDecode();
             try
             {
                 var overrideContent = await OverrideRuleCore.ProcessAsync(request);
@@ -597,40 +780,6 @@ namespace molilian.core
                     return JdParseOutput(result);
                 }
 
-                switch (request.Type)
-                {
-                    case "goods":
-                        string pattern = @"(?:sku=|sku%3D|sku%253D)(\d+)";
-                        Match match = Regex.Match(content, pattern);
-
-                        if (match.Success)
-                        {
-                            string goods_id = match.Groups[1].Value; // 返回匹配的 URL
-                            if (string.IsNullOrEmpty(goods_id))
-                            {
-                                result.success = false;
-                                result.deeplink_url = string.Empty;
-                                result.message = "放弃转链";
-                                result.reason = "无效商品ID";
-                                _ = TkLogCore.ParseLogAsync(result, is_hide);
-                                return JdParseOutput(result);
-                            }
-                            content = $"https://item.jd.com/{goods_id}.html";
-                        }
-                        else
-                        {
-                            result.success = false;
-                            result.deeplink_url = string.Empty;
-                            result.message = "放弃转链";
-                            result.reason = "无效商品ID";
-                            _ = TkLogCore.ParseLogAsync(result, is_hide);
-                            return JdParseOutput(result);
-                        }
-                        break;
-                    default:
-                        content = content.UrlDecode();
-                        break;
-                }
                 if (!string.IsNullOrEmpty(config.jd_blacklist_regular))
                 {
                     var blacklist = config.jd_blacklist_regular.Split('\n')
@@ -711,7 +860,9 @@ namespace molilian.core
 
                 bool is_numeric = result.shortLinkurl.Contains(itemId);
 
-                var account = request.AccountId > 0 ? await JdPoolCore.GetOneAsync(request.AccountId, JdPoolCore.JdAction.parse) : await JdPoolCore.GetOneAsync(JdPoolCore.JdAction.parse, request.RiskStrategy, request.LaunchScene, is_numeric);
+                var account = request.AccountId > 0 ?
+                    await JdPoolCore.GetOneAsync(request.AccountId, JdPoolCore.JdAction.parse) :
+                    await JdPoolCore.GetOneAsync(JdPoolCore.JdAction.parse, is_numeric, request);
                 if (account == null)
                 {
                     result.success = false;
@@ -872,7 +1023,6 @@ namespace molilian.core
         {
             var result = PddUnionPlus.GetFormattedObject(request.Content, request.Ip, request.Oaid);
 
-
             var overrideContent = await OverrideRuleCore.ProcessAsync(request);
             if (overrideContent != null)
             {
@@ -885,42 +1035,17 @@ namespace molilian.core
             }
 
             string content = request.Content;
-            switch (request.Type)
+            result.rawContent = content;
+            if (string.IsNullOrEmpty(result.shortLinkurl))
             {
-                case "goods":
-                    string pattern = @"(?:goods_id=|goods_id%3D|goods_id%253D)(\d+)";
-                    Match match = Regex.Match(content, pattern);
-                    if (match.Success)
-                    {
-                        string goods_id = match.Groups[1].Value; // 返回匹配的 URL
-                        if (string.IsNullOrEmpty(content))
-                        {
-                            result.success = false;
-                            result.deeplink_url = string.Empty;
-                            result.message = "放弃转链";
-                            result.reason = "无效商品ID";
-                            _ = TkLogCore.ParseLogAsync(result);
-                            return PddParseOutput(result);
-                        }
-                        //https://mobile.yangkeduo.com/goods.html?_wv=41729&_wvx=10&goods_id=665746260825
-                        content = $"https://mobile.yangkeduo.com/goods.html?goods_id={goods_id}";
-                    }
-                    else
-                    {
-                        result.success = false;
-                        result.deeplink_url = string.Empty;
-                        result.message = "放弃转链";
-                        result.reason = "无效商品ID";
-                        _ = TkLogCore.ParseLogAsync(result);
-                        return PddParseOutput(result);
-                    }
-                    break;
-                default:
-                    content = content.UrlDecode();
-                    break;
+                result.success = false;
+                result.deeplink_url = string.Empty;
+                result.message = "放弃转链";
+                result.reason = "纯口令";
+                _ = TkLogCore.ParseLogAsync(result);
+                return PddParseOutput(result);
             }
 
-            result.rawContent = content;
             bool IfExceptional = false;
             try
             {
@@ -957,76 +1082,177 @@ namespace molilian.core
                     return PddParseOutput(result);
                 }
                 PddPoolDTO account = null;
-                string lockKey;
-                long cis_num = 0;
+                // 直接调用GetOne获取账号
+                // 新的调度策略已经在内部处理了:
+                // 1. 基于时间间隔的账号选择
+                // 2. 并发安全的账号预留(Redis SETNX)
+                // 3. 依次尝试多个候选账号
+                // 因此不需要外层重试逻辑
+                account = PddPoolCore.GetOne(PddUnionWorkMode.All, request.AccountId);
 
-                for (int i = 0; i < 3; i++)
+                if (account == null)
                 {
-                    for (int j = 0; j < 3; j++)
-                    {
-                        account = PddPoolCore.GetOne(PddUnionWorkMode.All, request.AccountId);
-                        if (account == null) continue;
-
-                        if (account.cis_limit > 0)
-                        {
-                            lockKey = $"pdd_cis_limit_{result.accountId}";
-                            cis_num = RedisHelper.Get<long>(lockKey);
-                            if (cis_num > 0) continue;
-                        }
-                        break;
-                    }
-                    if (account == null)
-                    {
-                        result.success = false;
-                        result.message = "放弃转链";
-                        result.reason = "没有匹配账号";
-                        result.deeplink_url = PddUnionPlus.GetDeeplink(result.rawContent);
-                        _ = TkLogCore.ParseLogAsync(result);
-                        return PddParseOutput(result);
-                    }
-                    result.accountId = account.id;
-                    result.accountName = account.name;
-                    result.proxy_node = account.nodeName;
+                    result.success = false;
+                    result.message = "放弃转链";
+                    result.reason = "没有匹配账号";
+                    result.deeplink_url = PddUnionPlus.GetDeeplink(result.shortLinkurl);
+                    _ = TkLogCore.ParseLogAsync(result);
+                    return PddParseOutput(result);
+                }
 
-                    if (account.cis_limit > 0)
-                    {
-                        // 更新当前分钟的使用统计
-                        PddPoolCore.UpdateAccountUsage(account.id);
+                result.accountId = account.id;
+                result.accountName = account.name;
+                result.proxy_node = account.nodeName;
 
-                        lockKey = $"pdd_cis_limit_{result.accountId}";
-                        cis_num = RedisHelper.IncrBy(lockKey);
-                        if (cis_num > 1) continue;
+                // 更新当前日使用统计
+                PddPoolCore.UpdateAccountUsage(account.id);
 
-                        RedisHelper.Expire(lockKey, TimeSpan.FromMilliseconds(account.cis_limit));
-                    }
-                    break;
+                var plus = new PddUnionPlus(account);
+                result = await plus.PddParseAsync(content, request.CommerceType, result, cancellationToken);
+            }
+            catch (Exception ex)
+            {
+                if (ex.Message.Contains("was canceled"))
+                {
+                    result.success = false;
+                    result.message = "放弃转链";
+                    result.reason = "请求超时";
+                }
+                else
+                {
+                    IfExceptional = true;
+                    _ = new LoggerLibrary("unionParse", "pdd_error")
+                        .Info(request.Ip, request.Oaid)
+                        .Info(content)
+                        .Info(ex.Message, ex.StackTrace)
+                        .SaveAsync();
+                    NotifyCore.Notify(new NifyMessage
+                    {
+                        message = $"【转链异常PDD】\n{content}\n\n{ex.Message}\n{ex.StackTrace}",
+                        priority = NifyMessagePriority.high,
+                        tags = ["red_circle"]
+                    });
+                    result.success = false;
+                    result.message = "内部错误";
+                    result.reason = "转链接口异常";
                 }
+            }
+            _ = TkLogCore.ParseLogAsync(result);
+            return PddParseOutput(result, IfExceptional ? APIResultCodeEnum.NotAcceptable : APIResultCodeEnum.OK);
 
-                if (account == null)
+        }
+        public static async Task<APIResult> GoodsPddParseAsync(UnionParseRequest request, CancellationToken cancellationToken = default)
+        {
+            var result = PddUnionPlus.GetFormattedObject(request.Content, request.Ip, request.Oaid);
+
+            string content = request.Content;
+            content = content.UrlDecode();
+            result.rawContent = content;
+            string goods_pattern = @"(?:goods_id=|goods_id%3D|goods_id%253D)(\d+)";
+            Match match = Regex.Match(content, goods_pattern);
+
+            if (match.Success)
+            {
+                string goods_id = match.Groups[1].Value; // 返回匹配的 URL
+                if (string.IsNullOrEmpty(content))
                 {
                     result.success = false;
+                    result.deeplink_url = string.Empty;
                     result.message = "放弃转链";
-                    result.reason = "没有匹配账号";
-                    result.deeplink_url = PddUnionPlus.GetDeeplink(result.rawContent);
+                    result.reason = "无效商品ID";
                     _ = TkLogCore.ParseLogAsync(result);
                     return PddParseOutput(result);
                 }
-                if (account?.cis_limit > 0)
+                content = $"https://mobile.yangkeduo.com/goods.html?goods_id={goods_id}";
+                result.shortLinkurl = content;
+            }
+            else
+            {
+                result.success = false;
+                result.deeplink_url = string.Empty;
+                result.message = "放弃转链";
+                result.reason = "无效商品ID";
+                _ = TkLogCore.ParseLogAsync(result);
+                return PddParseOutput(result);
+            }
+
+            if (string.IsNullOrEmpty(result.shortLinkurl))
+            {
+                result.success = false;
+                result.deeplink_url = string.IsNullOrEmpty(result.shortLinkurl) ? string.Empty : PddUnionPlus.GetDeeplink(result.shortLinkurl);
+                result.message = "放弃转链";
+                result.reason = "纯口令";
+                _ = TkLogCore.ParseLogAsync(result);
+                return PddParseOutput(result);
+            }
+
+            bool IfExceptional = false;
+            try
+            {
+                var config = TkConfigCore.Get();
+
+                if (!string.IsNullOrEmpty(config.pdd_blacklist_regular))
                 {
-                    lockKey = $"pdd_cis_limit_{result.accountId}";
-                    cis_num = RedisHelper.Get<int>(lockKey);
-                    if (cis_num > 1)
+                    var blacklist = config.pdd_blacklist_regular.Split('\n')
+                    .Where(line => !string.IsNullOrWhiteSpace(line))
+                    .ToList();
+                    bool anyMatch = false;
+                    foreach (string pattern in blacklist)
+                    {
+                        if (Regex.IsMatch(content, pattern, RegexOptions.IgnoreCase)) anyMatch = true;
+                    }
+                    if (anyMatch)
                     {
                         result.success = false;
+                        result.deeplink_url = string.IsNullOrEmpty(result.shortLinkurl) ? string.Empty : PddUnionPlus.GetDeeplink(result.shortLinkurl);
                         result.message = "放弃转链";
-                        result.reason = $"没有匹配账号{cis_num}";
-                        result.deeplink_url = PddUnionPlus.GetDeeplink(result.rawContent);
+                        result.reason = "规则排除";
                         _ = TkLogCore.ParseLogAsync(result);
                         return PddParseOutput(result);
                     }
                 }
+                //============================== 放弃转链-地区过滤 ==============================
+                if (request.AccountId == 0 && PddUnionPlus.ShouldIgnoreRequest(config, request.Ip, request.Oaid, out string reason))
+                {
+                    result.success = false;
+                    result.message = "放弃转链";
+                    result.reason = reason;
+                    result.deeplink_url = string.IsNullOrEmpty(result.shortLinkurl) ? string.Empty : PddUnionPlus.GetDeeplink(result.shortLinkurl);
+                    _ = TkLogCore.ParseLogAsync(result);
+                    return PddParseOutput(result);
+                }
+                PddPoolDTO account = null;
+                // 直接调用GetOne获取账号
+                // 新的调度策略已经在内部处理了:
+                // 1. 基于时间间隔的账号选择
+                // 2. 并发安全的账号预留(Redis SETNX)
+                // 3. 依次尝试多个候选账号
+                // 因此不需要外层重试逻辑
+                account = PddPoolCore.GetOne(PddUnionWorkMode.All, request.AccountId);
+
+                if (account == null)
+                {
+                    result.success = false;
+                    result.message = "放弃转链";
+                    result.reason = "没有匹配账号";
+                    result.deeplink_url = string.IsNullOrEmpty(result.shortLinkurl) ? string.Empty : PddUnionPlus.GetDeeplink(result.shortLinkurl);
+                    _ = TkLogCore.ParseLogAsync(result);
+                    return PddParseOutput(result);
+                }
+
+                result.accountId = account.id;
+                result.accountName = account.name;
+                result.proxy_node = account.nodeName;
+
+                // 更新当前日使用统计
+                PddPoolCore.UpdateAccountUsage(account.id);
+
                 var plus = new PddUnionPlus(account);
                 result = await plus.PddParseAsync(content, request.CommerceType, result, cancellationToken);
+                if (!result.success)
+                {
+                    result.deeplink_url = string.IsNullOrEmpty(result.shortLinkurl) ? string.Empty : PddUnionPlus.GetDeeplink(result.shortLinkurl);
+                }
             }
             catch (Exception ex)
             {

+ 21 - 62
molilian.core/Core/taoke/UnionParseCore/dp2dp.cs

@@ -609,8 +609,9 @@ namespace molilian.core
                 result.link_type = JdUnionPlus.GetLinkType(result.shortLinkurl, out out_url, out itemId);
 
                 bool is_numeric = result.shortLinkurl.Contains(itemId);
-
-                var account = request.AccountId > 0 ? await JdPoolCore.GetOneAsync(request.AccountId, JdPoolCore.JdAction.parse) : await JdPoolCore.GetOneAsync(JdPoolCore.JdAction.parse, request.RiskStrategy, request.LaunchScene, is_numeric, "dp");
+                var account = request.AccountId > 0 ?
+                   await JdPoolCore.GetOneAsync(request.AccountId, JdPoolCore.JdAction.parse) :
+                   await JdPoolCore.GetOneAsync(JdPoolCore.JdAction.parse, is_numeric, request);
                 if (account == null)
                 {
                     result.success = false;
@@ -688,6 +689,7 @@ namespace molilian.core
             var result = PddUnionPlus.GetFormattedObject(request.Content, request.Ip, request.Oaid);
             var content = request.Content.UrlDecode();
             result.rawContent = content;
+
             result.parse_type = "dp";
             bool IfExceptional = false;
             try
@@ -733,79 +735,36 @@ namespace molilian.core
                     result.success = false;
                     result.message = "放弃转链";
                     result.reason = reason;
-                    result.deeplink_url = PddUnionPlus.GetDeeplink(result.rawContent);
+                    result.deeplink_url = PddUnionPlus.GetDeeplink(result.shortLinkurl);
                     _ = TkLogCore.ParseLogAsync(result);
                     return PddParseOutput(result);
                 }
                 PddPoolDTO account = null;
-                string lockKey;
-                long cis_num = 0;
-
-                for (int i = 0; i < 3; i++)
-                {
-                    for (int j = 0; j < 3; j++)
-                    {
-                        account = PddPoolCore.GetOne(PddUnionWorkMode.All, request.AccountId, "dp");
-                        if (account == null) continue;
-
-                        if (account.cis_limit > 0)
-                        {
-                            lockKey = $"pdd_cis_limit_{result.accountId}";
-                            cis_num = RedisHelper.Get<long>(lockKey);
-                            if (cis_num > 0) continue;
-                        }
-                        break;
-                    }
-                    if (account == null)
-                    {
-                        result.success = false;
-                        result.message = "放弃转链";
-                        result.reason = "没有匹配账号";
-                        result.deeplink_url = PddUnionPlus.GetDeeplink(result.rawContent);
-                        _ = TkLogCore.ParseLogAsync(result);
-                        return PddParseOutput(result);
-                    }
-                    result.accountId = account.id;
-                    result.accountName = account.name;
-                    result.proxy_node = account.nodeName;
-
-                    if (account.cis_limit > 0)
-                    {
-                        // 更新当前分钟的使用统计
-                        PddPoolCore.UpdateAccountUsage(account.id);
-
-                        lockKey = $"pdd_cis_limit_{result.accountId}";
-                        cis_num = RedisHelper.IncrBy(lockKey);
-                        if (cis_num > 1) continue;
-
-                        RedisHelper.Expire(lockKey, TimeSpan.FromMilliseconds(account.cis_limit));
-                    }
-                    break;
-                }
+                // 直接调用GetOne获取账号(parse_type="dp")
+                // 新的调度策略已经在内部处理了:
+                // 1. 基于时间间隔的账号选择
+                // 2. 并发安全的账号预留(Redis SETNX)
+                // 3. 依次尝试多个候选账号
+                // 因此不需要外层重试逻辑
+                account = PddPoolCore.GetOne(PddUnionWorkMode.All, request.AccountId, "dp");
 
                 if (account == null)
                 {
                     result.success = false;
                     result.message = "放弃转链";
                     result.reason = "没有匹配账号";
-                    result.deeplink_url = PddUnionPlus.GetDeeplink(result.rawContent);
+                    result.deeplink_url = PddUnionPlus.GetDeeplink(result.shortLinkurl);
                     _ = TkLogCore.ParseLogAsync(result);
                     return PddParseOutput(result);
                 }
-                if (account?.cis_limit > 0)
-                {
-                    lockKey = $"pdd_cis_limit_{result.accountId}";
-                    cis_num = RedisHelper.Get<int>(lockKey);
-                    if (cis_num > 1)
-                    {
-                        result.success = false;
-                        result.message = "放弃转链";
-                        result.reason = $"没有匹配账号{cis_num}";
-                        result.deeplink_url = PddUnionPlus.GetDeeplink(result.rawContent);
-                        _ = TkLogCore.ParseLogAsync(result);
-                        return PddParseOutput(result);
-                    }
-                }
+
+                result.accountId = account.id;
+                result.accountName = account.name;
+                result.proxy_node = account.nodeName;
+
+                // 更新当前日使用统计
+                PddPoolCore.UpdateAccountUsage(account.id);
+
                 var plus = new PddUnionPlus(account);
 
                 result = await plus.PddParseAsync(content, request.CommerceType, result, cancellationToken);

+ 0 - 50
molilian.core/Plus/JDUnion/JdUnionPlus.cs

@@ -241,56 +241,6 @@ namespace molilian.core
             return Regex.IsMatch(content, pattern);
         }
 
-        //public static JdDataDTO JdParse(JdPoolDTO account, string content, ref JdDataDTO result)
-        //{
-        //    string message = "OK";
-        //    string url = GetLink(content);
-
-        //    var plus = new JdUnionPlus(account.app_key, account.app_secret);
-
-        //    if (ContainsSpecialFormat(content))
-        //    {
-        //        result.link_type = LinkTypeEnum.other_aff;
-        //        result.success = false;
-        //        result.link_type = LinkTypeEnum.unknown;
-        //        result.channel_type = ChannelTypeEnum.jd;
-        //        result.message = "其他推广链接";
-        //        result.accountName = string.Empty;
-        //        result.content = content;
-        //        result.shortLinkurl = url;
-        //        result.deeplink_url = GetDeeplink(null);
-        //        return result;
-        //    }
-
-        //    //result.link_type = plus.IsAffLlink(url) ? LinkTypeEnum.other_aff : LinkTypeEnum.goods;
-        //    if (!string.IsNullOrEmpty(url))
-        //    {
-        //        (result.link_type, result.content) = GetLinkType(url);
-        //        if (result.link_type == LinkTypeEnum.other_aff)
-        //        {
-        //            result.success = false;
-        //            result.link_type = LinkTypeEnum.unknown;
-        //            result.channel_type = ChannelTypeEnum.jd;
-        //            result.message = "其他推广链接";
-        //            result.accountName = string.Empty;
-        //            result.content = content;
-        //            result.shortLinkurl = url;
-        //            result.deeplink_url = GetDeeplink(null);
-        //            return result;
-        //        }
-        //    }
-        //    string shortLinkurl = plus.promotion_get(account.site_id, string.IsNullOrEmpty(url) ? content : url);
-        //    var success = !string.IsNullOrEmpty(shortLinkurl);
-        //    if (!success) message = "转链失败";
-
-        //    result.content = shortLinkurl;
-        //    result.shortLinkurl = shortLinkurl;
-        //    result.success = success;
-        //    result.message = message;
-        //    result.deeplink_url = GetDeeplink(url);
-        //    return result;
-        //}
-
         public async Task<JdDataDTO> JdParseAsync(string content, int commerceType, string clickId, JdDataDTO result, CancellationToken cancellationToken = default)
         {
 

+ 1 - 1
molilian.core/Plus/pdd/PddUnionPlus.cs

@@ -125,7 +125,7 @@ namespace molilian.core
                 oaid = oaid,
                 elapsedTime = 0,
                 itemName = "点击打开拼多多APP",
-                shortLinkurl = string.Empty,
+                shortLinkurl = shortLinkurl,
                 deeplink_url = string.Empty,
                 create_time = DateTime.Now,
                 end_point = _end_point,

+ 3 - 1
molilian.core/Plus/pdd/base.cs

@@ -56,10 +56,12 @@ namespace molilian.core
             reason = string.Empty;
             try
             {
+                if (TestParseCore.InWhitelist(ip, oaid)) return false;
+
                 // IP和流量控制
                 string ignorePercentageCity = config.pddIgnorePercentageCity;
                 string? ipInfo = IP2RegionPlus.Search(ip);
-                if (!TestParseCore.InWhitelist(ip, oaid) && AlimamaPlus.IgnoreRegionIncluded(ignorePercentageCity, ipInfo, out string regionInfo))
+                if (AlimamaPlus.IgnoreRegionIncluded(ignorePercentageCity, ipInfo, out string regionInfo))
                 {
                     reason = $"地区控制:{regionInfo}";
                     return true;

Някои файлове не бяха показани, защото твърде много файлове са промени