Browse Source

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

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

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

@@ -69,6 +69,7 @@ namespace molilian.api.Controllers
             foreach (var account in list)
             {
                 if (!account.enable_sync_order) continue;
+                if (account.is_hide) continue;
                 int accountId = account.id;
 
                 DateTime create_time = DateTime.Now.AddDays(-2);
@@ -595,6 +596,8 @@ namespace molilian.api.Controllers
                     if (account.id != 113) continue;
 #endif
                     if (!account.enable_sync_order) continue;
+                    // 隐藏号只需要当天收益(调度用),历史报表不拉取
+                    if (account.is_hide && days > 0) continue;
                     try
                     {
                         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 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);
 
             if (!validTrackId)
@@ -35,7 +43,6 @@ namespace molilian.api.Controllers
                 return new APIResult(new { success = false, message = "miss" });
             }
 
-            var link = await TracksCore.GetLinkByIdAsync(parsedTrackId);
             if (link == null)
             {
                 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";
         }
 
+        /// <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)
         {
             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)
         {
+            request.ParseRequestRecorded = true;
             var dimension = ResolveParseMetricDimension(request.Channel, request.RiskStrategy, request.LaunchScene);
             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)
         {
             if (accountId <= 0) return Task.FromResult(false);
@@ -274,14 +287,14 @@ GROUP BY
             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
             {
-                AddParseMetricCount(metric, DateTime.Now, platform, riskStrategy, launchScene, 0, 1);
+                AddParseMetricCount(metric, DateTime.Now, platform, riskStrategy, launchScene, 0, count);
                 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);
             }
@@ -417,7 +430,8 @@ GROUP BY
 
         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, "hour", metricTime.ToString("yyyyMMddHH"), platform, riskStrategy, launchScene, accountId, count);
@@ -464,7 +478,7 @@ GROUP BY
                 foreach (var item in PendingParseMetricCounters.ToArray())
                 {
                     long count = Interlocked.Exchange(ref item.Value.Count, 0);
-                    if (count <= 0) continue;
+                    if (count == 0) continue;
 
                     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)
         {
-            if (count <= 0) return;
+            if (count == 0) return;
 
             var key = new ParseMetricCounterKey(
                 metric,
@@ -543,6 +557,12 @@ GROUP BY
                 RedisHelper.IncrBy(key, 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)

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

@@ -120,21 +120,28 @@ namespace molilian.core
             {
                 var ts = DateTime.Now - response.create_time;
                 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 || response.message.Equals("转链失败"))
                 {
                     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:检测到您推广行为异常,链接生成失败
                 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;
                 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("放弃转链"))
                 {
                     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))
                 {

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

@@ -225,6 +225,39 @@ namespace molilian.core
             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)
         {
             if (!"pdd".Equals(request.Channel, StringComparison.OrdinalIgnoreCase))
@@ -417,6 +450,7 @@ namespace molilian.core
         {
             bool other_aff = false;
             AlimamaPlus alimama = null;
+            TkDataDTO? hiddenAccountFailure = null;
             //content = content.UrlDecode();
             var result = AlimamaPlus.GetFormattedObject(request);
 
@@ -717,7 +751,11 @@ namespace molilian.core
                 }
                 result.accountId = account.id;
                 result.accountName = account.company;
-                if (request.AccountId <= 0)
+                if (account.is_hide)
+                {
+                    _ = TracksCore.RevertParseRequestAsync(request);
+                }
+                else if (request.AccountId <= 0)
                 {
                     _ = TracksCore.RecordParseAccountRequestAsync(
                         request.Channel,
@@ -728,6 +766,7 @@ namespace molilian.core
 
 
                 alimama = new AlimamaPlus(account);
+                hiddenAccountFailure = account.is_hide ? CreateTaobaoFailureResult(result) : null;
 
 
                 int timeout = alimama._config.rt_max;
@@ -801,71 +840,74 @@ namespace molilian.core
                     break;
             }
 
+            var output = ResolveTaobaoOutputResult(result, hiddenAccountFailure);
+            // 隐藏号不输出埋点
+            bool hiddenAccount = hiddenAccountFailure != null;
             if ("brwsimilar".Equals(request.RiskStrategy))
             {
-                bool clearGoodsFields = !result.success;
+                bool clearGoodsFields = !output.success;
                 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,
-                    promotionImg = clearGoodsFields ? string.Empty : (object)result.PromotionImg
+                    promotionImg = clearGoodsFields ? string.Empty : (object)output.PromotionImg
                 });
             }
             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,
             });
 
@@ -1491,7 +1533,11 @@ namespace molilian.core
                 result.accountId = account.id;
                 result.accountName = account.name;
                 result.proxy_node = account.nodeName;
-                if (request.AccountId <= 0)
+                if (account.is_hide)
+                {
+                    _ = TracksCore.RevertParseRequestAsync(request);
+                }
+                else if (request.AccountId <= 0)
                 {
                     _ = TracksCore.RecordParseAccountRequestAsync(
                         request.Channel,
@@ -1688,7 +1734,11 @@ namespace molilian.core
                 result.accountId = account.id;
                 result.accountName = account.name;
                 result.proxy_node = account.nodeName;
-                if (request.AccountId <= 0)
+                if (account.is_hide)
+                {
+                    _ = TracksCore.RevertParseRequestAsync(request);
+                }
+                else if (request.AccountId <= 0)
                 {
                     _ = TracksCore.RecordParseAccountRequestAsync(
                         request.Channel,
@@ -1761,9 +1811,11 @@ namespace molilian.core
 
         private static APIResult PddParseOutput(PddDataDTO result, APIResultCodeEnum code = APIResultCodeEnum.OK, PddDataDTO? hiddenAccountFailure = null)
         {
+            // 隐藏号不输出埋点
+            bool hiddenAccount = hiddenAccountFailure != null;
             if (result.success && hiddenAccountFailure != null)
             {
-                // 只替换对外响应,保留真实转链结果供日志和统计使用。
+                // 只替换对外响应;隐藏号的日志、统计由 TkLogCore.ParseLogAsync 统一跳过。
                 hiddenAccountFailure.deeplink_url = result.deeplink_url;
                 result = hiddenAccountFailure;
             }
@@ -1783,8 +1835,8 @@ namespace molilian.core
                 success = ValidateDeeplinkIfRequired(result.success, result.deeplink_url),
                 result.message,
                 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,
                 home_page = "pinduoduo://com.xunmeng.pinduoduo/".Equals(result.deeplink_url),
                 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;
             AlimamaPlus alimama = null;
+            TkDataDTO? hiddenAccountFailure = null;
             var result = AlimamaPlus.GetFormattedObject(request);
 
             result.riskStrategy = request.RiskStrategy;
@@ -411,8 +412,10 @@ namespace molilian.core
                 }
                 result.accountId = account.id;
                 result.accountName = account.company;
+                if (account.is_hide) _ = TracksCore.RevertParseRequestAsync(request);
 
                 alimama = new AlimamaPlus(account);
+                hiddenAccountFailure = account.is_hide ? CreateTaobaoFailureResult(result) : null;
 
 
                 int timeout = alimama._config.rt_max;
@@ -491,35 +494,38 @@ namespace molilian.core
                     break;
             }
 
+            var output = ResolveTaobaoOutputResult(result, hiddenAccountFailure);
+            // 隐藏号不输出埋点
+            bool hiddenAccount = hiddenAccountFailure != null;
             return TaobaoParseOutput(result, swData, new
             {
                 //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,
             });
         }
@@ -833,6 +839,7 @@ namespace molilian.core
                 result.accountId = account.id;
                 result.accountName = account.name;
                 result.proxy_node = account.nodeName;
+                if (account.is_hide) _ = TracksCore.RevertParseRequestAsync(request);
 
                 // 更新当前日使用统计
                 PddPoolCore.UpdateAccountUsage(account.id);

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

@@ -70,5 +70,10 @@ namespace molilian.core
         /// 商品主图
         /// </summary>
         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)
         {
+            // 隐藏号不同步任何订单数据
+            if (_account.is_hide) return 0;
 
             //DateTime endTime = DateTime.Now;
             //DateTime startTime = endTime.AddDays(-90);
@@ -35,8 +37,6 @@ namespace molilian.core
 
             //#endif       
 
-            if (_account.is_hide) return total;
-
             string sql = @"
             UPDATE tk_report tr
             JOIN (
@@ -134,6 +134,9 @@ namespace molilian.core
         }
         public async Task<int> GetIncyRefundOrdersAsync(int sleep)
         {
+            // 隐藏号不同步任何订单数据
+            if (_account.is_hide) return 0;
+
             string cacheKey = $":cache:GetIncyRefundOrders:{_accountId}";
             DateTime endTime = DateTime.Now;
             DateTime startTime = endTime.AddDays(-1);
@@ -162,8 +165,6 @@ namespace molilian.core
                 RedisHelper.Set(cacheKey, max_modifiedTime);
             }
 
-            if (_account.is_hide) return total;
-
             string sql = @"
             UPDATE tk_report tr
             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";
         public async Task<int> GetSettleBillsAsync(int sleep)
         {
+            // 隐藏号不同步任何结算数据
+            if (_account.is_hide) return 0;
+
             DateTime startTime = DateTime.Parse("2024-03-01");
             DateTime endTime = DateTime.Now;
             // 确保 startTime 小于 endTime

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

@@ -153,6 +153,7 @@ namespace molilian.core
             foreach (var account in accounts)
             {
                 if (!account.enable_call_stats) continue;
+                if (account.is_hide) continue;
 
                 int accountId = account.id;
                 string accountName = $"{TkChannelEnum.tb}_{account.id}";
@@ -667,8 +668,15 @@ namespace molilian.core
                 DateTime report_date = date.Date;
                 if (report_date == DateTime.Now.Date)
                 {
+                    // 收益缓存用于 daily_income_limit 调度,隐藏号也需要
                     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, "tk_report", result);
                 }
@@ -828,6 +836,9 @@ namespace molilian.core
         /// </summary>
         public async Task FixReport(DateTime date)
         {
+            // 隐藏号不写任何报表
+            if (_account.is_hide) return;
+
             TkReportDTO result = new TkReportDTO();
             try
             {
@@ -886,7 +897,6 @@ namespace molilian.core
                 result.sett_tk_disp_tfee_56 = data.Read<decimal>("sett_tk_disp_tfee_56", 0);
 
                 DateTime report_date = date.Date;
-                if (_account.is_hide) return;
 
                 await EachSaveReportOverviewAsync(report_date, "tk_report", result);
 
@@ -923,6 +933,9 @@ namespace molilian.core
         /// </summary>
         public void SaveReportHourtrend(DateTime date)
         {
+            // 隐藏号不写任何报表
+            if (_account.is_hide) return;
+
             string t = $"{DateTime.Now.Convert2UnixTimestamp(true)}";
             string dt = date.ToString("yyyy-MM-dd");
 
@@ -1037,6 +1050,9 @@ namespace molilian.core
         /// </summary>
         public void GetViolationuWarning()
         {
+            // 隐藏号不同步违规预警
+            if (_account.is_hide) return;
+
             try
             {
                 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)
         {
+            // 隐藏号不同步任何订单数据
+            if (_account.is_hide) return 0;
+
             string lockKey = $"lock:GetHistoryOrders:{_accountId}";
 
             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);
 
-                if (_account.is_hide) return total;
-
                 string sql = @"
 UPDATE tk_report tr
 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)
         {
+            // 隐藏号不同步任何订单数据
+            if (_account.is_hide) return 0;
+
             string lockKey = $"lock:GetIncyOrders:{_accountId}";
 
             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(cacheKey2, max_modifiedTime);
                 }
-                if (_account.is_hide) return total;
                 if (total == 0) return 0;
 
                 //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)
         {
+            // 隐藏号不同步任何订单数据
+            if (_account.is_hide) return 0;
+
             string cacheKey = $":cache:GetOrdersByAdZone:{_accountName}";
             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)
         {
+            // 隐藏号不同步任何收益数据
+            if (_account.is_hide) return;
+
             var ts = DateTime.Now.Convert2UnixTimestamp(true);
             string cookies = _account.cookies;
 

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

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