Browse Source

feat(隐藏号): 淘宝转链按失败返回仅保留 deeplink,隐藏号不产生任何数据痕迹

- 淘宝转链(含 dp)命中隐藏号成功时按失败参数返回,仅 deeplink 使用成功链接
- 淘宝/拼多多隐藏号不输出 exposeTracks/clickTracks,埋点回调直接忽略
- 转链日志跳过入库队列、parse_total/parse_metric 统计、客户限额等,保留调用限额与账号状态处理
- 命中隐藏号时回退入口已计入的请求数
- 隐藏号停止订单、退款、结算、报表、违规预警、每日日志及 PDD 预估收益同步,保留当天收益缓存用于调度
dodo hold 1 day ago
parent
commit
77d0fec8fb

+ 3 - 0
molilian.api/Controllers/public/TaskController.cs

@@ -69,6 +69,7 @@ namespace molilian.api.Controllers
             foreach (var account in list)
             foreach (var account in list)
             {
             {
                 if (!account.enable_sync_order) continue;
                 if (!account.enable_sync_order) continue;
+                if (account.is_hide) continue;
                 int accountId = account.id;
                 int accountId = account.id;
 
 
                 DateTime create_time = DateTime.Now.AddDays(-2);
                 DateTime create_time = DateTime.Now.AddDays(-2);
@@ -595,6 +596,8 @@ namespace molilian.api.Controllers
                     if (account.id != 113) continue;
                     if (account.id != 113) continue;
 #endif
 #endif
                     if (!account.enable_sync_order) continue;
                     if (!account.enable_sync_order) continue;
+                    // 隐藏号只需要当天收益(调度用),历史报表不拉取
+                    if (account.is_hide && days > 0) continue;
                     try
                     try
                     {
                     {
                         var alimama = new AlimamaPlus(account);
                         var alimama = new AlimamaPlus(account);

+ 8 - 1
molilian.api/Controllers/public/TracksController.cs

@@ -28,6 +28,14 @@ namespace molilian.api.Controllers
             string requestUniqueId = unique_id ?? string.Empty;
             string requestUniqueId = unique_id ?? string.Empty;
             string requestScene = scene ?? string.Empty;
             string requestScene = scene ?? string.Empty;
 
 
+            var link = validTrackId ? await TracksCore.GetLinkByIdAsync(parsedTrackId) : null;
+
+            // 隐藏号的曝光/点击不留任何记录
+            if (await TracksCore.IsHiddenTrackAccountAsync(link, parsedAccountId))
+            {
+                return new APIResult(new { success = true, message = "ok" });
+            }
+
             await WriteTrackRequestTextLogAsync(track_id, unique_id, scene, account_id, parsedTrackId, validTrackId, parsedAccountId, validAccountId);
             await WriteTrackRequestTextLogAsync(track_id, unique_id, scene, account_id, parsedTrackId, validTrackId, parsedAccountId, validAccountId);
 
 
             if (!validTrackId)
             if (!validTrackId)
@@ -35,7 +43,6 @@ namespace molilian.api.Controllers
                 return new APIResult(new { success = false, message = "miss" });
                 return new APIResult(new { success = false, message = "miss" });
             }
             }
 
 
-            var link = await TracksCore.GetLinkByIdAsync(parsedTrackId);
             if (link == null)
             if (link == null)
             {
             {
                 return new APIResult(new { success = false, message = "not found" });
                 return new APIResult(new { success = false, message = "not found" });

+ 31 - 0
molilian.core/Core/HiddenAccountCore.cs

@@ -0,0 +1,31 @@
+namespace molilian.core
+{
+    /// <summary>
+    /// 隐藏号判断:隐藏号命中的转链只返回 deeplink,不产生任何日志、统计、上报数据
+    /// </summary>
+    public static class HiddenAccountCore
+    {
+        public static Task<bool> IsHiddenAsync(TkChannelEnum channel, int accountId)
+        {
+            return IsHiddenAsync(channel.ToString(), accountId);
+        }
+
+        public static async Task<bool> IsHiddenAsync(string channel, int accountId)
+        {
+            if (accountId <= 0) return false;
+
+            switch ((channel ?? string.Empty).ToLowerInvariant())
+            {
+                case "tb":
+                    var tbAccount = await TkPoolCore.ALLGetOneAsync(accountId);
+                    return tbAccount != null && tbAccount.is_hide;
+                case "pdd":
+                    var pddAccounts = await PddPoolCore.AllListAsync();
+                    var pddAccount = pddAccounts?.FirstOrDefault(e => e.id == accountId);
+                    return pddAccount != null && pddAccount.is_hide;
+                default:
+                    return false;
+            }
+        }
+    }
+}

+ 9 - 0
molilian.core/Core/TracksCore.cs

@@ -379,6 +379,15 @@ ON DUPLICATE KEY UPDATE
             return "tb";
             return "tb";
         }
         }
 
 
+        /// <summary>
+        /// 埋点是否属于隐藏号:隐藏号的曝光/点击不计数、不记日志
+        /// </summary>
+        public static Task<bool> IsHiddenTrackAccountAsync(TrackLinkDTO link, int accountId)
+        {
+            if (link == null || accountId <= 0) return Task.FromResult(false);
+            return HiddenAccountCore.IsHiddenAsync(GetTrackCallChannelName(link), accountId);
+        }
+
         public static string BuildPath(TrackType type, TkDataDTO result)
         public static string BuildPath(TrackType type, TkDataDTO result)
         {
         {
             int trackId = type == TrackType.Click ? 1 : 2;
             int trackId = type == TrackType.Click ? 1 : 2;

+ 26 - 6
molilian.core/Core/TracksParseMetricCore.cs

@@ -23,10 +23,23 @@ namespace molilian.core
 
 
         public static Task<bool> RecordParseRequestAsync(UnionParseRequest request)
         public static Task<bool> RecordParseRequestAsync(UnionParseRequest request)
         {
         {
+            request.ParseRequestRecorded = true;
             var dimension = ResolveParseMetricDimension(request.Channel, request.RiskStrategy, request.LaunchScene);
             var dimension = ResolveParseMetricDimension(request.Channel, request.RiskStrategy, request.LaunchScene);
             return IncrementParseMetricAsync(ParseMetricRequest, dimension.Platform, dimension.RiskStrategy, dimension.LaunchScene, request.AccountId);
             return IncrementParseMetricAsync(ParseMetricRequest, dimension.Platform, dimension.RiskStrategy, dimension.LaunchScene, request.AccountId);
         }
         }
 
 
+        /// <summary>
+        /// 命中隐藏号:回退入口处已计入的请求数,隐藏号请求不出现在任何统计中
+        /// </summary>
+        public static Task<bool> RevertParseRequestAsync(UnionParseRequest request)
+        {
+            if (!request.ParseRequestRecorded) return Task.FromResult(false);
+            request.ParseRequestRecorded = false;
+
+            var dimension = ResolveParseMetricDimension(request.Channel, request.RiskStrategy, request.LaunchScene);
+            return IncrementParseMetricAsync(ParseMetricRequest, dimension.Platform, dimension.RiskStrategy, dimension.LaunchScene, request.AccountId, -1);
+        }
+
         public static Task<bool> RecordParseAccountRequestAsync(string channel, string riskStrategy, int launchScene, int accountId)
         public static Task<bool> RecordParseAccountRequestAsync(string channel, string riskStrategy, int launchScene, int accountId)
         {
         {
             if (accountId <= 0) return Task.FromResult(false);
             if (accountId <= 0) return Task.FromResult(false);
@@ -274,14 +287,14 @@ GROUP BY
             return result;
             return result;
         }
         }
 
 
-        private static Task<bool> IncrementParseMetricAsync(string metric, string platform, string riskStrategy, int launchScene, int accountId = 0)
+        private static Task<bool> IncrementParseMetricAsync(string metric, string platform, string riskStrategy, int launchScene, int accountId = 0, int count = 1)
         {
         {
             try
             try
             {
             {
-                AddParseMetricCount(metric, DateTime.Now, platform, riskStrategy, launchScene, 0, 1);
+                AddParseMetricCount(metric, DateTime.Now, platform, riskStrategy, launchScene, 0, count);
                 if (accountId > 0)
                 if (accountId > 0)
                 {
                 {
-                    AddParseMetricCount(metric, DateTime.Now, platform, riskStrategy, launchScene, accountId, 1);
+                    AddParseMetricCount(metric, DateTime.Now, platform, riskStrategy, launchScene, accountId, count);
                 }
                 }
                 return Task.FromResult(true);
                 return Task.FromResult(true);
             }
             }
@@ -417,7 +430,8 @@ GROUP BY
 
 
         private static void AddParseMetricCount(string metric, DateTime metricTime, string platform, string riskStrategy, int launchScene, int accountId, int count)
         private static void AddParseMetricCount(string metric, DateTime metricTime, string platform, string riskStrategy, int launchScene, int accountId, int count)
         {
         {
-            if (count <= 0) return;
+            // count 为负数时用于回退(隐藏号)
+            if (count == 0) return;
 
 
             QueueParseMetricCount(metric, "daily", metricTime.ToString("yyyyMMdd"), platform, riskStrategy, launchScene, accountId, count);
             QueueParseMetricCount(metric, "daily", metricTime.ToString("yyyyMMdd"), platform, riskStrategy, launchScene, accountId, count);
             QueueParseMetricCount(metric, "hour", metricTime.ToString("yyyyMMddHH"), platform, riskStrategy, launchScene, accountId, count);
             QueueParseMetricCount(metric, "hour", metricTime.ToString("yyyyMMddHH"), platform, riskStrategy, launchScene, accountId, count);
@@ -464,7 +478,7 @@ GROUP BY
                 foreach (var item in PendingParseMetricCounters.ToArray())
                 foreach (var item in PendingParseMetricCounters.ToArray())
                 {
                 {
                     long count = Interlocked.Exchange(ref item.Value.Count, 0);
                     long count = Interlocked.Exchange(ref item.Value.Count, 0);
-                    if (count <= 0) continue;
+                    if (count == 0) continue;
 
 
                     try
                     try
                     {
                     {
@@ -498,7 +512,7 @@ GROUP BY
 
 
         private static void QueueParseMetricCount(string metric, string bucketType, string bucketValue, string platform, string riskStrategy, int launchScene, int accountId, int count)
         private static void QueueParseMetricCount(string metric, string bucketType, string bucketValue, string platform, string riskStrategy, int launchScene, int accountId, int count)
         {
         {
-            if (count <= 0) return;
+            if (count == 0) return;
 
 
             var key = new ParseMetricCounterKey(
             var key = new ParseMetricCounterKey(
                 metric,
                 metric,
@@ -543,6 +557,12 @@ GROUP BY
                 RedisHelper.IncrBy(key, delta);
                 RedisHelper.IncrBy(key, delta);
                 count -= delta;
                 count -= delta;
             }
             }
+            while (count < 0)
+            {
+                int delta = count < int.MinValue ? int.MinValue : (int)count;
+                RedisHelper.IncrBy(key, delta);
+                count -= delta;
+            }
         }
         }
 
 
         private static async Task<int> ClearParseMetricCountersForDateAsync(DateTime targetDate)
         private static async Task<int> ClearParseMetricCountersForDateAsync(DateTime targetDate)

+ 14 - 7
molilian.core/Core/log/pdd.cs

@@ -120,21 +120,28 @@ namespace molilian.core
             {
             {
                 var ts = DateTime.Now - response.create_time;
                 var ts = DateTime.Now - response.create_time;
                 response.elapsedTime = (int)ts.TotalMilliseconds;
                 response.elapsedTime = (int)ts.TotalMilliseconds;
-                _ = RedisKit.RPushAsync(queue_parse_pdd_key, response);
 
 
-                if (!TestParseCore.InWhitelist(response.ip, response.oaid))
+                bool is_hide = await HiddenAccountCore.IsHiddenAsync(TkChannelEnum.pdd, response.accountId);
+
+                // 隐藏号不产生任何日志、统计数据,只保留账号调用限额和账号状态处理
+                if (!is_hide)
                 {
                 {
-                    await RecordParseMetricsAsync(
-                        response.channel.ToString(), response.accountId, response.accountName,
-                        response.success, response.message, response.reason, response.deeplink_url,
-                        response.riskStrategy, response.launchScene);
+                    _ = RedisKit.RPushAsync(queue_parse_pdd_key, response);
+
+                    if (!TestParseCore.InWhitelist(response.ip, response.oaid))
+                    {
+                        await RecordParseMetricsAsync(
+                            response.channel.ToString(), response.accountId, response.accountName,
+                            response.success, response.message, response.reason, response.deeplink_url,
+                            response.riskStrategy, response.launchScene);
+                    }
                 }
                 }
 
 
                 //if (response.success) saveClientRequestTotal(response.channel, response.ip, response.oaid);
                 //if (response.success) saveClientRequestTotal(response.channel, response.ip, response.oaid);
                 if (response.success || response.message.Equals("转链失败"))
                 if (response.success || response.message.Equals("转链失败"))
                 {
                 {
                     await RiskControlCore.CallsIncrByAsync(response.channel, response.accountId);
                     await RiskControlCore.CallsIncrByAsync(response.channel, response.accountId);
-                    await saveClientRequestTotalAsync(response.channel, response.ip, response.oaid);
+                    if (!is_hide) await saveClientRequestTotalAsync(response.channel, response.ip, response.oaid);
                 }
                 }
                 //30007:检测到您推广行为异常,链接生成失败
                 //30007:检测到您推广行为异常,链接生成失败
                 if (response.reason.Equals("您的调用次数过高"))
                 if (response.reason.Equals("您的调用次数过高"))

+ 19 - 14
molilian.core/Core/log/taobao.cs

@@ -100,30 +100,35 @@ namespace molilian.core
             {
             {
                 var ts = DateTime.Now - response.create_time;
                 var ts = DateTime.Now - response.create_time;
                 response.elapsedTime = (int)ts.TotalMilliseconds;
                 response.elapsedTime = (int)ts.TotalMilliseconds;
-                _ = RedisKit.RPushAsync(queue_parse_tb_key, response);
 
 
-                if (!TestParseCore.InWhitelist(response.ip, response.oaid))
+                var account = await TkPoolCore.ALLGetOneAsync(response.accountId);
+                bool is_hide = false;
+                if (account != null) is_hide = account.is_hide;
+
+                // 隐藏号不产生任何日志、统计数据,只保留账号调用限额和账号状态处理
+                if (!is_hide)
                 {
                 {
-                    await RecordParseMetricsAsync(
-                        response.channel.ToString(), response.accountId, response.accountName,
-                        response.success, response.message, response.reason, response.deeplink_url,
-                        response.riskStrategy, response.launchScene);
+                    _ = RedisKit.RPushAsync(queue_parse_tb_key, response);
+
+                    if (!TestParseCore.InWhitelist(response.ip, response.oaid))
+                    {
+                        await RecordParseMetricsAsync(
+                            response.channel.ToString(), response.accountId, response.accountName,
+                            response.success, response.message, response.reason, response.deeplink_url,
+                            response.riskStrategy, response.launchScene);
+                    }
                 }
                 }
 
 
                 if (response.success || response.message.Equals("转链失败") || response.message.Equals("放弃转链"))
                 if (response.success || response.message.Equals("转链失败") || response.message.Equals("放弃转链"))
                 {
                 {
                     await RiskControlCore.CallsIncrByAsync(response.channel, response.accountId);
                     await RiskControlCore.CallsIncrByAsync(response.channel, response.accountId);
-                    await saveClientRequestTotalAsync(response.channel, response.ip, response.oaid);
+                    if (!is_hide) await saveClientRequestTotalAsync(response.channel, response.ip, response.oaid);
                 }
                 }
-                if (response.success)
+                if (!is_hide)
                 {
                 {
-                    _ = saveUnionCouponParseCacheAsync(response);
+                    if (response.success) _ = saveUnionCouponParseCacheAsync(response);
+                    if (!string.IsNullOrEmpty(response.itemId)) TkOrderTrackingCore.SaveLinkSummary(response, account);
                 }
                 }
-                var account = await TkPoolCore.ALLGetOneAsync(response.accountId);
-                if (!string.IsNullOrEmpty(response.itemId)) TkOrderTrackingCore.SaveLinkSummary(response, account);
-
-                bool is_hide = false;
-                if (account != null) is_hide = account.is_hide;
 
 
                 if (!response.success && "nologin".Equals(response.message))
                 if (!response.success && "nologin".Equals(response.message))
                 {
                 {

+ 114 - 62
molilian.core/Core/taoke/UnionParseCore/UnionParseCore.cs

@@ -225,6 +225,39 @@ namespace molilian.core
             return new APIResult(info, code);
             return new APIResult(info, code);
         }
         }
 
 
+        private static TkDataDTO CreateTaobaoFailureResult(TkDataDTO result)
+        {
+            // 转链前保存失败返回所需的字段,避免成功后的商品信息、淘口令、推广文案进入隐藏号响应。
+            return new TkDataDTO
+            {
+                success = false,
+                message = "转链失败",
+                reason = "无法转链",
+                commercial = false,
+                channel = result.channel,
+                channel_type = result.channel_type,
+                link_type = result.link_type,
+                accountId = result.accountId,
+                itemId = result.itemId,
+                mktId = result.mktId,
+                itemName = "点击打开淘宝APP",
+                content = result.content,
+                shortLinkurl = result.shortLinkurl,
+                parse_type = result.parse_type,
+                riskStrategy = result.riskStrategy,
+                launchScene = result.launchScene,
+            };
+        }
+
+        private static TkDataDTO ResolveTaobaoOutputResult(TkDataDTO result, TkDataDTO? hiddenAccountFailure)
+        {
+            if (!result.success || hiddenAccountFailure == null) return result;
+
+            // 只替换对外响应;隐藏号的日志、统计由 TkLogCore.ParseLogAsync 统一跳过。
+            hiddenAccountFailure.deeplink_url = result.deeplink_url;
+            return hiddenAccountFailure;
+        }
+
         private static ActionResult? TryRejectBusyLimitedRequest(UnionParseRequest request)
         private static ActionResult? TryRejectBusyLimitedRequest(UnionParseRequest request)
         {
         {
             if (!"pdd".Equals(request.Channel, StringComparison.OrdinalIgnoreCase))
             if (!"pdd".Equals(request.Channel, StringComparison.OrdinalIgnoreCase))
@@ -417,6 +450,7 @@ namespace molilian.core
         {
         {
             bool other_aff = false;
             bool other_aff = false;
             AlimamaPlus alimama = null;
             AlimamaPlus alimama = null;
+            TkDataDTO? hiddenAccountFailure = null;
             //content = content.UrlDecode();
             //content = content.UrlDecode();
             var result = AlimamaPlus.GetFormattedObject(request);
             var result = AlimamaPlus.GetFormattedObject(request);
 
 
@@ -717,7 +751,11 @@ namespace molilian.core
                 }
                 }
                 result.accountId = account.id;
                 result.accountId = account.id;
                 result.accountName = account.company;
                 result.accountName = account.company;
-                if (request.AccountId <= 0)
+                if (account.is_hide)
+                {
+                    _ = TracksCore.RevertParseRequestAsync(request);
+                }
+                else if (request.AccountId <= 0)
                 {
                 {
                     _ = TracksCore.RecordParseAccountRequestAsync(
                     _ = TracksCore.RecordParseAccountRequestAsync(
                         request.Channel,
                         request.Channel,
@@ -728,6 +766,7 @@ namespace molilian.core
 
 
 
 
                 alimama = new AlimamaPlus(account);
                 alimama = new AlimamaPlus(account);
+                hiddenAccountFailure = account.is_hide ? CreateTaobaoFailureResult(result) : null;
 
 
 
 
                 int timeout = alimama._config.rt_max;
                 int timeout = alimama._config.rt_max;
@@ -801,71 +840,74 @@ namespace molilian.core
                     break;
                     break;
             }
             }
 
 
+            var output = ResolveTaobaoOutputResult(result, hiddenAccountFailure);
+            // 隐藏号不输出埋点
+            bool hiddenAccount = hiddenAccountFailure != null;
             if ("brwsimilar".Equals(request.RiskStrategy))
             if ("brwsimilar".Equals(request.RiskStrategy))
             {
             {
-                bool clearGoodsFields = !result.success;
+                bool clearGoodsFields = !output.success;
                 return TaobaoParseOutput(result, swData, new
                 return TaobaoParseOutput(result, swData, new
                 {
                 {
-                    success = ValidateDeeplinkIfRequired(result.success, result.deeplink_url),
-                    result.commercial,
-                    exposeTracks = TracksCore.BuildBrwSimilarPath(TrackType.Expose, result),
-                    clickTracks = TracksCore.BuildBrwSimilarPath(TrackType.Click, result),
-                    result.with_middle_page,
-                    result.message,
-                    sub_message = result.reason,
-                    home_page = "tbopen://m.taobao.com/tbopen/index.html".Equals(result.deeplink_url),
-                    link_type = result.link_type.ToString(),
-                    channel = result?.channel.ToString(),
-                    result.channel_type,
-                    shopTitle = clearGoodsFields ? string.Empty : result.shopTitle,
-                    itemId = clearGoodsFields ? string.Empty : result.itemId,
-                    itemName = clearGoodsFields ? string.Empty : result.itemName,
-                    pic = clearGoodsFields ? string.Empty : result.pic,
-                    price = clearGoodsFields ? string.Empty : (object)result.reservePrice,
-                    zkFinalPrice = clearGoodsFields ? string.Empty : (object)result.zkFinalPrice,
-                    promotionPrice = clearGoodsFields ? string.Empty : (object)result.promotionPrice,
-                    couponAmount = clearGoodsFields ? string.Empty : (object)result.couponAmount,
-                    couponEffectiveStartTime = clearGoodsFields ? string.Empty : result.couponEffectiveStartTime,
-                    couponEffectiveEndTime = clearGoodsFields ? string.Empty : result.couponEffectiveEndTime,
-                    result.shortLinkurl,
-                    result.deeplink_url,
-                    result.riskStrategy,
-                    result.launchScene,
-                    result.content,
-                    result.taoToken,
+                    success = ValidateDeeplinkIfRequired(output.success, output.deeplink_url),
+                    output.commercial,
+                    exposeTracks = hiddenAccount ? string.Empty : TracksCore.BuildBrwSimilarPath(TrackType.Expose, output),
+                    clickTracks = hiddenAccount ? string.Empty : TracksCore.BuildBrwSimilarPath(TrackType.Click, output),
+                    output.with_middle_page,
+                    output.message,
+                    sub_message = output.reason,
+                    home_page = "tbopen://m.taobao.com/tbopen/index.html".Equals(output.deeplink_url),
+                    link_type = output.link_type.ToString(),
+                    channel = output?.channel.ToString(),
+                    output.channel_type,
+                    shopTitle = clearGoodsFields ? string.Empty : output.shopTitle,
+                    itemId = clearGoodsFields ? string.Empty : output.itemId,
+                    itemName = clearGoodsFields ? string.Empty : output.itemName,
+                    pic = clearGoodsFields ? string.Empty : output.pic,
+                    price = clearGoodsFields ? string.Empty : (object)output.reservePrice,
+                    zkFinalPrice = clearGoodsFields ? string.Empty : (object)output.zkFinalPrice,
+                    promotionPrice = clearGoodsFields ? string.Empty : (object)output.promotionPrice,
+                    couponAmount = clearGoodsFields ? string.Empty : (object)output.couponAmount,
+                    couponEffectiveStartTime = clearGoodsFields ? string.Empty : output.couponEffectiveStartTime,
+                    couponEffectiveEndTime = clearGoodsFields ? string.Empty : output.couponEffectiveEndTime,
+                    output.shortLinkurl,
+                    output.deeplink_url,
+                    output.riskStrategy,
+                    output.launchScene,
+                    output.content,
+                    output.taoToken,
                     other_aff,
                     other_aff,
-                    promotionImg = clearGoodsFields ? string.Empty : (object)result.PromotionImg
+                    promotionImg = clearGoodsFields ? string.Empty : (object)output.PromotionImg
                 });
                 });
             }
             }
             return TaobaoParseOutput(result, swData, new
             return TaobaoParseOutput(result, swData, new
             {
             {
-                success = ValidateDeeplinkIfRequired(result.success, result.deeplink_url),
-                result.commercial,
-                exposeTracks = TracksCore.BuildPath(TrackType.Expose, result),
-                clickTracks = TracksCore.BuildPath(TrackType.Click, result),
-                result.with_middle_page,
-                result.message,
-                sub_message = result.reason,
-                home_page = "tbopen://m.taobao.com/tbopen/index.html".Equals(result.deeplink_url),
-                link_type = result.link_type.ToString(),
-                channel = result?.channel.ToString(),
-                result.channel_type,
-                result.shopTitle,
-                result.itemId,
-                result.itemName,
-                result.pic,
-                result.zkFinalPrice,
-                price = result.reservePrice,
-                result.promotionPrice,
-                result.couponAmount,
-                result.couponEffectiveStartTime,
-                result?.couponEffectiveEndTime,
-                result.shortLinkurl,
-                result.deeplink_url,
-                result.riskStrategy,
-                result.launchScene,
-                result.content,
-                result.taoToken,
+                success = ValidateDeeplinkIfRequired(output.success, output.deeplink_url),
+                output.commercial,
+                exposeTracks = hiddenAccount ? string.Empty : TracksCore.BuildPath(TrackType.Expose, output),
+                clickTracks = hiddenAccount ? string.Empty : TracksCore.BuildPath(TrackType.Click, output),
+                output.with_middle_page,
+                output.message,
+                sub_message = output.reason,
+                home_page = "tbopen://m.taobao.com/tbopen/index.html".Equals(output.deeplink_url),
+                link_type = output.link_type.ToString(),
+                channel = output?.channel.ToString(),
+                output.channel_type,
+                output.shopTitle,
+                output.itemId,
+                output.itemName,
+                output.pic,
+                output.zkFinalPrice,
+                price = output.reservePrice,
+                output.promotionPrice,
+                output.couponAmount,
+                output.couponEffectiveStartTime,
+                output?.couponEffectiveEndTime,
+                output.shortLinkurl,
+                output.deeplink_url,
+                output.riskStrategy,
+                output.launchScene,
+                output.content,
+                output.taoToken,
                 other_aff,
                 other_aff,
             });
             });
 
 
@@ -1491,7 +1533,11 @@ namespace molilian.core
                 result.accountId = account.id;
                 result.accountId = account.id;
                 result.accountName = account.name;
                 result.accountName = account.name;
                 result.proxy_node = account.nodeName;
                 result.proxy_node = account.nodeName;
-                if (request.AccountId <= 0)
+                if (account.is_hide)
+                {
+                    _ = TracksCore.RevertParseRequestAsync(request);
+                }
+                else if (request.AccountId <= 0)
                 {
                 {
                     _ = TracksCore.RecordParseAccountRequestAsync(
                     _ = TracksCore.RecordParseAccountRequestAsync(
                         request.Channel,
                         request.Channel,
@@ -1688,7 +1734,11 @@ namespace molilian.core
                 result.accountId = account.id;
                 result.accountId = account.id;
                 result.accountName = account.name;
                 result.accountName = account.name;
                 result.proxy_node = account.nodeName;
                 result.proxy_node = account.nodeName;
-                if (request.AccountId <= 0)
+                if (account.is_hide)
+                {
+                    _ = TracksCore.RevertParseRequestAsync(request);
+                }
+                else if (request.AccountId <= 0)
                 {
                 {
                     _ = TracksCore.RecordParseAccountRequestAsync(
                     _ = TracksCore.RecordParseAccountRequestAsync(
                         request.Channel,
                         request.Channel,
@@ -1761,9 +1811,11 @@ namespace molilian.core
 
 
         private static APIResult PddParseOutput(PddDataDTO result, APIResultCodeEnum code = APIResultCodeEnum.OK, PddDataDTO? hiddenAccountFailure = null)
         private static APIResult PddParseOutput(PddDataDTO result, APIResultCodeEnum code = APIResultCodeEnum.OK, PddDataDTO? hiddenAccountFailure = null)
         {
         {
+            // 隐藏号不输出埋点
+            bool hiddenAccount = hiddenAccountFailure != null;
             if (result.success && hiddenAccountFailure != null)
             if (result.success && hiddenAccountFailure != null)
             {
             {
-                // 只替换对外响应,保留真实转链结果供日志和统计使用。
+                // 只替换对外响应;隐藏号的日志、统计由 TkLogCore.ParseLogAsync 统一跳过。
                 hiddenAccountFailure.deeplink_url = result.deeplink_url;
                 hiddenAccountFailure.deeplink_url = result.deeplink_url;
                 result = hiddenAccountFailure;
                 result = hiddenAccountFailure;
             }
             }
@@ -1783,8 +1835,8 @@ namespace molilian.core
                 success = ValidateDeeplinkIfRequired(result.success, result.deeplink_url),
                 success = ValidateDeeplinkIfRequired(result.success, result.deeplink_url),
                 result.message,
                 result.message,
                 result.commercial,
                 result.commercial,
-                exposeTracks = TracksCore.BuildPath(TrackType.Expose, result),
-                clickTracks = TracksCore.BuildPath(TrackType.Click, result),
+                exposeTracks = hiddenAccount ? string.Empty : TracksCore.BuildPath(TrackType.Expose, result),
+                clickTracks = hiddenAccount ? string.Empty : TracksCore.BuildPath(TrackType.Click, result),
                 sub_message = result.reason,
                 sub_message = result.reason,
                 home_page = "pinduoduo://com.xunmeng.pinduoduo/".Equals(result.deeplink_url),
                 home_page = "pinduoduo://com.xunmeng.pinduoduo/".Equals(result.deeplink_url),
                 link_type = result.link_type.ToString(),
                 link_type = result.link_type.ToString(),

+ 33 - 26
molilian.core/Core/taoke/UnionParseCore/dp2dp.cs

@@ -20,6 +20,7 @@ namespace molilian.core
         {
         {
             bool other_aff = false;
             bool other_aff = false;
             AlimamaPlus alimama = null;
             AlimamaPlus alimama = null;
+            TkDataDTO? hiddenAccountFailure = null;
             var result = AlimamaPlus.GetFormattedObject(request);
             var result = AlimamaPlus.GetFormattedObject(request);
 
 
             result.riskStrategy = request.RiskStrategy;
             result.riskStrategy = request.RiskStrategy;
@@ -411,8 +412,10 @@ namespace molilian.core
                 }
                 }
                 result.accountId = account.id;
                 result.accountId = account.id;
                 result.accountName = account.company;
                 result.accountName = account.company;
+                if (account.is_hide) _ = TracksCore.RevertParseRequestAsync(request);
 
 
                 alimama = new AlimamaPlus(account);
                 alimama = new AlimamaPlus(account);
+                hiddenAccountFailure = account.is_hide ? CreateTaobaoFailureResult(result) : null;
 
 
 
 
                 int timeout = alimama._config.rt_max;
                 int timeout = alimama._config.rt_max;
@@ -491,35 +494,38 @@ namespace molilian.core
                     break;
                     break;
             }
             }
 
 
+            var output = ResolveTaobaoOutputResult(result, hiddenAccountFailure);
+            // 隐藏号不输出埋点
+            bool hiddenAccount = hiddenAccountFailure != null;
             return TaobaoParseOutput(result, swData, new
             return TaobaoParseOutput(result, swData, new
             {
             {
                 //result.success,
                 //result.success,
-                success = ValidateDeeplinkIfRequired(result.success, result.deeplink_url),
-                result.commercial,
-                exposeTracks = TracksCore.BuildPath(TrackType.Expose, result),
-                clickTracks = TracksCore.BuildPath(TrackType.Click, result),
-                result.with_middle_page,
-                result.message,
-                sub_message = result.reason,
-                home_page = "tbopen://m.taobao.com/tbopen/index.html".Equals(result.deeplink_url),
-                link_type = result.link_type.ToString(),
-                channel = result?.channel.ToString(),
-                result.channel_type,
-                result.itemId,
-                result.itemName,
-                result.pic,
-                result.zkFinalPrice,
-                price = result.reservePrice,
-                result.promotionPrice,
-                result.couponAmount,
-                result.couponEffectiveStartTime,
-                result?.couponEffectiveEndTime,
-                result.shortLinkurl,
-                result.deeplink_url,
-                result.riskStrategy,
-                result.launchScene,
-                result.content,
-                result.taoToken,
+                success = ValidateDeeplinkIfRequired(output.success, output.deeplink_url),
+                output.commercial,
+                exposeTracks = hiddenAccount ? string.Empty : TracksCore.BuildPath(TrackType.Expose, output),
+                clickTracks = hiddenAccount ? string.Empty : TracksCore.BuildPath(TrackType.Click, output),
+                output.with_middle_page,
+                output.message,
+                sub_message = output.reason,
+                home_page = "tbopen://m.taobao.com/tbopen/index.html".Equals(output.deeplink_url),
+                link_type = output.link_type.ToString(),
+                channel = output?.channel.ToString(),
+                output.channel_type,
+                output.itemId,
+                output.itemName,
+                output.pic,
+                output.zkFinalPrice,
+                price = output.reservePrice,
+                output.promotionPrice,
+                output.couponAmount,
+                output.couponEffectiveStartTime,
+                output?.couponEffectiveEndTime,
+                output.shortLinkurl,
+                output.deeplink_url,
+                output.riskStrategy,
+                output.launchScene,
+                output.content,
+                output.taoToken,
                 other_aff,
                 other_aff,
             });
             });
         }
         }
@@ -833,6 +839,7 @@ namespace molilian.core
                 result.accountId = account.id;
                 result.accountId = account.id;
                 result.accountName = account.name;
                 result.accountName = account.name;
                 result.proxy_node = account.nodeName;
                 result.proxy_node = account.nodeName;
+                if (account.is_hide) _ = TracksCore.RevertParseRequestAsync(request);
 
 
                 // 更新当前日使用统计
                 // 更新当前日使用统计
                 PddPoolCore.UpdateAccountUsage(account.id);
                 PddPoolCore.UpdateAccountUsage(account.id);

+ 5 - 0
molilian.core/DTO/UnionParseRequest.cs

@@ -70,5 +70,10 @@ namespace molilian.core
         /// 商品主图
         /// 商品主图
         /// </summary>
         /// </summary>
         public string Pic { get; set; } = string.Empty;
         public string Pic { get; set; } = string.Empty;
+
+        /// <summary>
+        /// 是否已计入 parse_metric 请求数(命中隐藏号时用于回退)
+        /// </summary>
+        public bool ParseRequestRecorded { get; set; } = false;
     }
     }
 }
 }

+ 5 - 4
molilian.core/Plus/Alimama/RefundOrders.cs

@@ -9,6 +9,8 @@ namespace molilian.core
 
 
         public async Task<int> GetHistoryRefundOrdersAsync(int sleep, DateTime startTime, DateTime endTime)
         public async Task<int> GetHistoryRefundOrdersAsync(int sleep, DateTime startTime, DateTime endTime)
         {
         {
+            // 隐藏号不同步任何订单数据
+            if (_account.is_hide) return 0;
 
 
             //DateTime endTime = DateTime.Now;
             //DateTime endTime = DateTime.Now;
             //DateTime startTime = endTime.AddDays(-90);
             //DateTime startTime = endTime.AddDays(-90);
@@ -35,8 +37,6 @@ namespace molilian.core
 
 
             //#endif       
             //#endif       
 
 
-            if (_account.is_hide) return total;
-
             string sql = @"
             string sql = @"
             UPDATE tk_report tr
             UPDATE tk_report tr
             JOIN (
             JOIN (
@@ -134,6 +134,9 @@ namespace molilian.core
         }
         }
         public async Task<int> GetIncyRefundOrdersAsync(int sleep)
         public async Task<int> GetIncyRefundOrdersAsync(int sleep)
         {
         {
+            // 隐藏号不同步任何订单数据
+            if (_account.is_hide) return 0;
+
             string cacheKey = $":cache:GetIncyRefundOrders:{_accountId}";
             string cacheKey = $":cache:GetIncyRefundOrders:{_accountId}";
             DateTime endTime = DateTime.Now;
             DateTime endTime = DateTime.Now;
             DateTime startTime = endTime.AddDays(-1);
             DateTime startTime = endTime.AddDays(-1);
@@ -162,8 +165,6 @@ namespace molilian.core
                 RedisHelper.Set(cacheKey, max_modifiedTime);
                 RedisHelper.Set(cacheKey, max_modifiedTime);
             }
             }
 
 
-            if (_account.is_hide) return total;
-
             string sql = @"
             string sql = @"
             UPDATE tk_report tr
             UPDATE tk_report tr
             JOIN (
             JOIN (

+ 3 - 0
molilian.core/Plus/Alimama/bills.cs

@@ -8,6 +8,9 @@ namespace molilian.core
         private const string base_bill_url = "https://pub.alimama.com/openapi/param2/1/gateway.unionpub/new.settle.media.pre.income.daily.bill.detail.json";
         private const string base_bill_url = "https://pub.alimama.com/openapi/param2/1/gateway.unionpub/new.settle.media.pre.income.daily.bill.detail.json";
         public async Task<int> GetSettleBillsAsync(int sleep)
         public async Task<int> GetSettleBillsAsync(int sleep)
         {
         {
+            // 隐藏号不同步任何结算数据
+            if (_account.is_hide) return 0;
+
             DateTime startTime = DateTime.Parse("2024-03-01");
             DateTime startTime = DateTime.Parse("2024-03-01");
             DateTime endTime = DateTime.Now;
             DateTime endTime = DateTime.Now;
             // 确保 startTime 小于 endTime
             // 确保 startTime 小于 endTime

+ 18 - 2
molilian.core/Plus/Alimama/crawler.cs

@@ -153,6 +153,7 @@ namespace molilian.core
             foreach (var account in accounts)
             foreach (var account in accounts)
             {
             {
                 if (!account.enable_call_stats) continue;
                 if (!account.enable_call_stats) continue;
+                if (account.is_hide) continue;
 
 
                 int accountId = account.id;
                 int accountId = account.id;
                 string accountName = $"{TkChannelEnum.tb}_{account.id}";
                 string accountName = $"{TkChannelEnum.tb}_{account.id}";
@@ -667,8 +668,15 @@ namespace molilian.core
                 DateTime report_date = date.Date;
                 DateTime report_date = date.Date;
                 if (report_date == DateTime.Now.Date)
                 if (report_date == DateTime.Now.Date)
                 {
                 {
+                    // 收益缓存用于 daily_income_limit 调度,隐藏号也需要
                     await RiskControlCore.SaveIncomeAmtAsync(TkChannelEnum.tb, $"{_accountId}", result.eff_tk_disp_tfee);
                     await RiskControlCore.SaveIncomeAmtAsync(TkChannelEnum.tb, $"{_accountId}", result.eff_tk_disp_tfee);
-                    if (_account.is_hide) return;
+                }
+
+                // 隐藏号不写任何报表
+                if (_account.is_hide) return;
+
+                if (report_date == DateTime.Now.Date)
+                {
                     await EachSaveReportOverviewAsync(report_date.AddHours(DateTime.Now.Hour), "tk_report_hourtrend", result);
                     await EachSaveReportOverviewAsync(report_date.AddHours(DateTime.Now.Hour), "tk_report_hourtrend", result);
                     await EachSaveReportOverviewAsync(report_date, "tk_report", result);
                     await EachSaveReportOverviewAsync(report_date, "tk_report", result);
                 }
                 }
@@ -828,6 +836,9 @@ namespace molilian.core
         /// </summary>
         /// </summary>
         public async Task FixReport(DateTime date)
         public async Task FixReport(DateTime date)
         {
         {
+            // 隐藏号不写任何报表
+            if (_account.is_hide) return;
+
             TkReportDTO result = new TkReportDTO();
             TkReportDTO result = new TkReportDTO();
             try
             try
             {
             {
@@ -886,7 +897,6 @@ namespace molilian.core
                 result.sett_tk_disp_tfee_56 = data.Read<decimal>("sett_tk_disp_tfee_56", 0);
                 result.sett_tk_disp_tfee_56 = data.Read<decimal>("sett_tk_disp_tfee_56", 0);
 
 
                 DateTime report_date = date.Date;
                 DateTime report_date = date.Date;
-                if (_account.is_hide) return;
 
 
                 await EachSaveReportOverviewAsync(report_date, "tk_report", result);
                 await EachSaveReportOverviewAsync(report_date, "tk_report", result);
 
 
@@ -923,6 +933,9 @@ namespace molilian.core
         /// </summary>
         /// </summary>
         public void SaveReportHourtrend(DateTime date)
         public void SaveReportHourtrend(DateTime date)
         {
         {
+            // 隐藏号不写任何报表
+            if (_account.is_hide) return;
+
             string t = $"{DateTime.Now.Convert2UnixTimestamp(true)}";
             string t = $"{DateTime.Now.Convert2UnixTimestamp(true)}";
             string dt = date.ToString("yyyy-MM-dd");
             string dt = date.ToString("yyyy-MM-dd");
 
 
@@ -1037,6 +1050,9 @@ namespace molilian.core
         /// </summary>
         /// </summary>
         public void GetViolationuWarning()
         public void GetViolationuWarning()
         {
         {
+            // 隐藏号不同步违规预警
+            if (_account.is_hide) return;
+
             try
             try
             {
             {
                 string url = "https://media.alimama.com/violationu2/warning_instance_page_v2.json?r=mx_208&toPage=1&pageSize=40&params=";
                 string url = "https://media.alimama.com/violationu2/warning_instance_page_v2.json?r=mx_208&toPage=1&pageSize=40&params=";

+ 9 - 3
molilian.core/Plus/Alimama/orders.cs

@@ -9,6 +9,9 @@ namespace molilian.core
 
 
         public async Task<int> GetHistoryOrdersAsync(int sleep, DateTime startTime, DateTime endTime)
         public async Task<int> GetHistoryOrdersAsync(int sleep, DateTime startTime, DateTime endTime)
         {
         {
+            // 隐藏号不同步任何订单数据
+            if (_account.is_hide) return 0;
+
             string lockKey = $"lock:GetHistoryOrders:{_accountId}";
             string lockKey = $"lock:GetHistoryOrders:{_accountId}";
 
 
             if (!RedisHelper.Set(lockKey, "1", 1800))
             if (!RedisHelper.Set(lockKey, "1", 1800))
@@ -49,8 +52,6 @@ namespace molilian.core
                 //当日往前查询
                 //当日往前查询
                 (int total, DateTime max_modifiedTime, List<DateTime> orderTimes) = await GetTkOrdersAsync(startTime, endTime, DateTime.MinValue, false, sleep);
                 (int total, DateTime max_modifiedTime, List<DateTime> orderTimes) = await GetTkOrdersAsync(startTime, endTime, DateTime.MinValue, false, sleep);
 
 
-                if (_account.is_hide) return total;
-
                 string sql = @"
                 string sql = @"
 UPDATE tk_report tr
 UPDATE tk_report tr
 JOIN (
 JOIN (
@@ -140,6 +141,9 @@ SET  order_ord_num = order_ord_num_3 + order_ord_num_12 + order_ord_num_13 + ord
         }
         }
         public async Task<int> GetIncyOrdersAsync(int sleep, int retry_days = 0)
         public async Task<int> GetIncyOrdersAsync(int sleep, int retry_days = 0)
         {
         {
+            // 隐藏号不同步任何订单数据
+            if (_account.is_hide) return 0;
+
             string lockKey = $"lock:GetIncyOrders:{_accountId}";
             string lockKey = $"lock:GetIncyOrders:{_accountId}";
 
 
             if (!RedisHelper.Set(lockKey, "1", 1800))
             if (!RedisHelper.Set(lockKey, "1", 1800))
@@ -185,7 +189,6 @@ SET  order_ord_num = order_ord_num_3 + order_ord_num_12 + order_ord_num_13 + ord
                     RedisHelper.Set(cacheKey, max_modifiedTime);
                     RedisHelper.Set(cacheKey, max_modifiedTime);
                     RedisHelper.Set(cacheKey2, max_modifiedTime);
                     RedisHelper.Set(cacheKey2, max_modifiedTime);
                 }
                 }
-                if (_account.is_hide) return total;
                 if (total == 0) return 0;
                 if (total == 0) return 0;
 
 
                 //todo 根据orderTimes、settlementTimes 更新被影响的相关行 
                 //todo 根据orderTimes、settlementTimes 更新被影响的相关行 
@@ -279,6 +282,9 @@ SET  order_ord_num = order_ord_num_3 + order_ord_num_12 + order_ord_num_13 + ord
 
 
         public async Task<int> GetOrdersByAdZoneAsync(long adzoneId, DateTime startTime, DateTime endTime, int sleep)
         public async Task<int> GetOrdersByAdZoneAsync(long adzoneId, DateTime startTime, DateTime endTime, int sleep)
         {
         {
+            // 隐藏号不同步任何订单数据
+            if (_account.is_hide) return 0;
+
             string cacheKey = $":cache:GetOrdersByAdZone:{_accountName}";
             string cacheKey = $":cache:GetOrdersByAdZone:{_accountName}";
             string cacheKey2 = $":cache:GetOrdersByAdZone:{_accountId}";
             string cacheKey2 = $":cache:GetOrdersByAdZone:{_accountId}";
 
 

+ 3 - 0
molilian.core/Plus/pdd/EstimateRevenue.cs

@@ -10,6 +10,9 @@ namespace molilian.core
 
 
         public async Task queryEstimateRevenueList(DateTime startTime, DateTime endTime, CancellationToken cancellationToken = default)
         public async Task queryEstimateRevenueList(DateTime startTime, DateTime endTime, CancellationToken cancellationToken = default)
         {
         {
+            // 隐藏号不同步任何收益数据
+            if (_account.is_hide) return;
+
             var ts = DateTime.Now.Convert2UnixTimestamp(true);
             var ts = DateTime.Now.Convert2UnixTimestamp(true);
             string cookies = _account.cookies;
             string cookies = _account.cookies;
 
 

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

@@ -38,7 +38,7 @@ namespace molilian.core
             foreach (var account in accounts)
             foreach (var account in accounts)
             {
             {
                 if (!account.enable_call_stats) continue;
                 if (!account.enable_call_stats) continue;
-                //if (account.is_hide) continue;
+                if (account.is_hide) continue;
 
 
                 int accountId = account.id;
                 int accountId = account.id;
                 string accountName = $"{channelEnum}_{account.id}";
                 string accountName = $"{channelEnum}_{account.id}";