瀏覽代碼

feat(淘宝转链): 接入万能解析并保留短链处理

dodo hold 1 天之前
父節點
當前提交
b1c4d9259e

文件差異過大導致無法顯示
+ 0 - 0
molilian.api/Properties/PublishProfiles/latest.pubxml.user


+ 49 - 48
molilian.core/Plus/Alimama/parse.cs

@@ -79,58 +79,49 @@ namespace molilian.core
             Dictionary<string, long> swData = null)
         {
             result.itemName = "点击打开淘宝APP";
-            result = await InternalTextProcessingAsync(content, result, cancellationToken, swData);
+            // 先保留短链,选定节点后再决定是否展开。
+            result = await InternalTextProcessingAsync(content, result, cancellationToken, swData, preserveShortLinks: true);
+            bool hasShortLink = result.link_type == LinkTypeEnum.baseDomain;
+            bool shortLinkExpanded = false;
 
-            if (result.link_type == LinkTypeEnum.other_aff)
+            bool ShouldStopAfterTextProcessing()
             {
-                //============================== 其他推广链接 ==============================
-                result.channel = TkChannelEnum.tb;
-                result.channel_type = ChannelTypeEnum.tb;
-                result.link_type = LinkTypeEnum.other_aff;
-                result.success = false;
-                result.message = "放弃转链";
-                result.reason = "其他推广链接";
-                result.subCode = TkSubCodeEnum.OtherPromo;
-                result.accountName = string.Empty;
-                result.content = content;
-                //result.deeplink_url = GetDeeplink(null);
-                //result.itemName = GetTitle(content);
-
-                //if (!content.Contains("助我领红包") && !content.Contains("双11超级红包主会场"))
-                //{
-                //    //20241108 特例
-                //    string ruleKey = $"tmp_rules:{DateTime.Now:yyyyMMdd}_2";
-                //    var count = RedisHelper.IncrBy(ruleKey);
-                //    if (DateTime.Now < DateTime.Parse("2024-11-11") && count < 1000)
-                //    {
-                //        string hb_url = "https://s.click.taobao.com/t?union_lens=lensId%3APUB%401730991020%400bbb0a39_0e75_193071ad936_c566%4001%40eyJmbG9vcklkIjozODg1Miiwiic3BtQiiI6Il9wb3J0YWxfdjJfcGFnZXNfYWN0aXZpdHlfb2ZmaWNpYWxfaW5kZXhfaHRtIn0ie%3BeventPageId%3A20150318020007201&e=m%3D2%26s%3DipuVKjYImbNw4vFB6t2Z2iperVdZeJviU%2F9%2F0taeK29yINtkUhsv0KWvzu%2FHMi3%2BFPhC4MirEWStw6LBB4w3HYXT%2BqQ2P5OIQVNSryTmvs7YddWYbxmvGTMNfihJN%2FGnAGIx0oe2X2hZfJ7ZQxC1%2FU3rpAhWgptfEBnEBk3xaGylLmcSHKfaX1CIFRJhZoJ2keMqUwSQcLRC0TzsMy8kp7Hfv3%2BBYcPW3eHn14rW1PB5SunYCHQHRbw6LwQYf%2F1JuyhJem%2BM5OoR2cbDLktPLiIOenYkFIXa2HzNl%2Bm1VXoil%2F2L3b1aA6a%2FoVHlY%2BqZ2kyLuBX5NHCAQVYCmAmuozC39JfnbJNrE%2FSR3F9BSjOYe76q6w0hyfybhwAASyyp5XMWEyz25ulHMwkL2n%2F7nNBiFmadXyjFDPu12TgSnbZtfjDbrVTLHb6MowVEq7z9LgOFRSuRLesvlLykGd2CaRk8ezhE61zXa8yR1eex6ip3KqbOh7CEmgCLaFxcnT%2BrGuviRFaBil8Mlu5kMKse3g%3D%3D";
-                //        result.deeplink_url = GetDeeplink(hb_url);
-                //        RedisHelper.Expire(ruleKey, 86400 * 10);
-                //    }
-                //}
-                return result;
-            }
+                if (result.link_type == LinkTypeEnum.other_aff)
+                {
+                    result.channel = TkChannelEnum.tb;
+                    result.channel_type = ChannelTypeEnum.tb;
+                    result.success = false;
+                    result.message = "放弃转链";
+                    result.reason = "其他推广链接";
+                    result.subCode = TkSubCodeEnum.OtherPromo;
+                    result.accountName = string.Empty;
+                    result.content = content;
+                    return true;
+                }
 
-            //处理过后的正文 用于转链
-            if (!result.success)
-            {
-                switch (result.link_type)
-                {
-                    case LinkTypeEnum.profile:
-                    case LinkTypeEnum.live:
-                    case LinkTypeEnum.video:
-                    case LinkTypeEnum.note:
-                    case LinkTypeEnum.goods:
-                    case LinkTypeEnum.baseDomain:
-                        result.reason = string.Empty;
-                        break;
-                    default:
-                        result.reason = "非标准链接";
-                        result.subCode = TkSubCodeEnum.NonStdLink;
-                        return result;
-                }
+                if (!result.success)
+                {
+                    switch (result.link_type)
+                    {
+                        case LinkTypeEnum.profile:
+                        case LinkTypeEnum.live:
+                        case LinkTypeEnum.video:
+                        case LinkTypeEnum.note:
+                        case LinkTypeEnum.goods:
+                        case LinkTypeEnum.baseDomain:
+                            result.reason = string.Empty;
+                            break;
+                        default:
+                            result.reason = "非标准链接";
+                            result.subCode = TkSubCodeEnum.NonStdLink;
+                            return true;
+                    }
+                }
+                return false;
             }
 
+            if (ShouldStopAfterTextProcessing()) return result;
+
             //============================== 放弃转链-地区过滤 ==============================
 
             bool is_ignore2 = FlowControlIgnoreRequest(request, out string reason);
@@ -170,6 +161,16 @@ namespace molilian.core
 
                 (int ep_id, string tkEndpoint) = await TkEndpointManager.GetConvertApiAsync(_accountId, isTaobaoUrl, _account.parseEndpoint);
                 result.tkEndpoint = ep_id;
+
+                // 其他节点只展开一次;tbk_dg 始终使用原始短链。
+                if (hasShortLink && !shortLinkExpanded && tkEndpoint != "tbk_dg" &&
+                    !string.IsNullOrEmpty(tkEndpoint) && tkEndpoint != "ALL")
+                {
+                    result = await InternalTextProcessingAsync(content, result, cancellationToken, swData);
+                    shortLinkExpanded = true;
+                    if (ShouldStopAfterTextProcessing()) return result;
+                }
+
                 switch (tkEndpoint)
                 {
                     case "moose_share":
@@ -185,7 +186,7 @@ namespace molilian.core
                         result = await endpoint_aliyun(result.content, result, cancellationToken);
                         break;
                     case "tbk_dg":
-                        result = await endpoint_tbk_dg(result.content, result, cancellationToken);
+                        result = await endpoint_tbk_dg(hasShortLink ? content : result.content, result, cancellationToken);
                         break;
                     case "aiopen":
                         result = await endpoint_aiopen(result.content, result, cancellationToken);

+ 25 - 9
molilian.core/Plus/Alimama/parse_2.cs

@@ -115,7 +115,8 @@ namespace molilian.core
         }
         public static bool IsShortLink(string url)
         {
-            if (!url.StartsWith("https://") || !url.StartsWith("https://")) url = "https://" + url;
+            if (!url.StartsWith("https://", StringComparison.OrdinalIgnoreCase) &&
+                !url.StartsWith("http://", StringComparison.OrdinalIgnoreCase)) url = "https://" + url;
             Uri uri = new(url);
             if (uri.Host.Equals("e.tb.cn", StringComparison.OrdinalIgnoreCase) ||
                 uri.Host.Equals("m.tb.cn", StringComparison.OrdinalIgnoreCase) ||
@@ -456,7 +457,8 @@ namespace molilian.core
 
         public async Task<TkDataDTO> InternalTextProcessingAsync(string content, TkDataDTO result,
             CancellationToken cancellationToken = default,
-            Dictionary<string, long> swData = null)
+            Dictionary<string, long> swData = null,
+            bool preserveShortLinks = false)
         {
             bool success;
             string resultText;
@@ -489,13 +491,18 @@ namespace molilian.core
                     //result.shortLinkurl = resultText;
                     return result;
                 }
-#if DEBUG
-                result.success = true;
-                result.content = resultText;
-                result.link_type = LinkTypeEnum.goods;
-                //result.shortLinkurl = resultText;
-                return result;
-#endif
+
+
+                if (preserveShortLinks && IsShortLink(url))
+                {
+                    result.success = false;
+                    result.message = string.Empty;
+                    result.reason = string.Empty;
+                    result.content = content;
+                    result.link_type = LinkTypeEnum.baseDomain;
+                    result.elapsedTime2 = 0;
+                    return result;
+                }
 
                 Stopwatch sw = Stopwatch.StartNew();
 
@@ -522,6 +529,15 @@ namespace molilian.core
                     return result;
                 }
 #endif
+                if (!success && resultText.Contains("<!DOCTYPE html>"))
+                {
+                    result.success = false;
+                    result.message = "放弃转链";
+                    result.reason = "短链解析错误";
+                    result.subCode = TkSubCodeEnum.Other;
+                    return result;
+                }
+
 
                 if (success)
                 {

+ 46 - 13
molilian.core/Plus/Alimama/parse_endpoint/endpoint_tbk_dg.cs

@@ -1,9 +1,5 @@
 using dodohold.core;
-using Microsoft.AspNetCore.Mvc;
-using StackExchange.Redis;
-using System.Diagnostics;
-using System.Text.Json;
-using TencentCloud.Tcm.V20210413.Models;
+using System.Text.RegularExpressions;
 
 
 namespace molilian.core
@@ -12,24 +8,49 @@ namespace molilian.core
     {
         public async Task<TkDataDTO> endpoint_tbk_dg(string content, TkDataDTO result, CancellationToken cancellationToken = default)
         {
-            string item_id = content.GetContentPart("item.htm?id=", "");
-#if DEBUG
+            cancellationToken.ThrowIfCancellationRequested();
 
-            item_id = content;
-#endif
-            if (string.IsNullOrEmpty(item_id))
+            string item_id = IsDigitsOnly(content)
+                ? content
+                : Regex.Match(content ?? string.Empty, @"(?:\?|&)(?:id|shareDetailItemId|itemIds)=([0-9]+)(?=[&#\s]|$)").Groups[1].Value;
+
+            if (!IsDigitsOnly(item_id))
+            {
+                result = await endpoint_tbk_dg_general_link_parse(content, result, cancellationToken);
+                if (!result.success) return result;
+                item_id = result.itemId;
+            }
+
+            if (!IsDigitsOnly(item_id))
             {
                 // _ = new LoggerLibrary("endpoint", "tbk_dg")
                 //     .Info(content, "response null")
                 //     .SaveAsync();
+                content = "item_id null";
+                result.content = content;
+
                 result.channel = TkChannelEnum.tb;
                 result.link_type = LinkTypeEnum.unknown;
                 result.success = false;
                 result.message = "放弃转链";
                 result.reason = "无效商品id";
+                result.subCode = TkSubCodeEnum.NoConvert;
                 return result;
             }
 
+            content = $"https://item.taobao.com/item.htm?id={item_id}";
+            result.content = content;
+            result.itemId = item_id;
+            result.num_iid = item_id;
+            result.item_url = content;
+            result.item_deeplink_url = GetDeeplink(content);
+            result.channel = TkChannelEnum.tb;
+            result.link_type = LinkTypeEnum.goods;
+            result.success = false;
+            result.message = string.Empty;
+            result.reason = string.Empty;
+            result.subCode = TkSubCodeEnum.Other;
+
             string oaid = result.oaid;
             if (string.IsNullOrEmpty(oaid)) { oaid = DateTime.Now.Convert2UnixTimestamp().ToString().MD5(); }
             TaobaoOpenPlus taobao = new(_account.appkey, _account.appSecret, _account.open_pid, false, null);
@@ -38,8 +59,19 @@ namespace molilian.core
             if (response == null)
             {
                 // _ = new LoggerLibrary("endpoint", "tbk_dg")
-                //     .Info(content, "response null")
+                //     .Info("霸下通用 web 页面-验证码")
                 //     .SaveAsync();
+
+                if (content.Contains("霸下通用 web 页面-验证码"))
+                {
+                    content = "霸下通用 web 页面-验证码";
+                }
+                else
+                {
+                    content = "response null";
+                }
+                result.content = content;
+
                 result.channel = TkChannelEnum.tb;
                 result.link_type = LinkTypeEnum.unknown;
                 result.success = false;
@@ -63,10 +95,11 @@ namespace molilian.core
                 return result;
             }
 
-            string deeplink = response?.Data?.PageDeeplink;
+            string deeplink = response.Data?.PageDeeplink ?? string.Empty;
             result.deeplink_url = deeplink;
-            string h5Url = deeplink.GetContentPart("h5Url=", "&").UrlDecode();
+            string h5Url = Regex.Match(deeplink, @"(?:\?|&)h5Url=([^&]*)").Groups[1].Value.UrlDecode();
             result.shortLinkurl = h5Url;
+            result.subCode = TkSubCodeEnum.Success;
             return result;
         }
 

+ 108 - 0
molilian.core/Plus/Alimama/parse_endpoint/endpoint_tbk_dg_general_link_parse.cs

@@ -0,0 +1,108 @@
+using dodohold.core;
+using System.Diagnostics;
+
+namespace molilian.core
+{
+    public partial class AlimamaPlus
+    {
+        public Task<TkDataDTO> endpoint_tbk_dg_general_link_parse(string content, TkDataDTO result, CancellationToken cancellationToken = default)
+        {
+            cancellationToken.ThrowIfCancellationRequested();
+            Stopwatch stopwatch = Stopwatch.StartNew();
+            result.channel = TkChannelEnum.tb;
+            result.commercial = false;
+
+            Task<TkDataDTO> Fail(string reason, TkSubCodeEnum subCode = TkSubCodeEnum.Other)
+            {
+                result.link_type = LinkTypeEnum.unknown;
+                result.success = false;
+                result.message = "解析失败";
+                result.reason = reason;
+                result.subCode = subCode;
+
+                _ = new LoggerLibrary("endpoint", "tbk_dg_general_link_parse")
+                    .Info(content, reason)
+                    .SaveAsync();
+                return Task.FromResult(result);
+            }
+
+            try
+            {
+                if (string.IsNullOrWhiteSpace(content))
+                {
+                    return Fail("待解析内容为空", TkSubCodeEnum.NoConvert);
+                }
+
+                TaobaoOpenPlus taobao = new(_account.appkey, _account.appSecret, _account.open_pid, false, null!);
+                var response = taobao.GeneralLinkParseRequest(content);
+                cancellationToken.ThrowIfCancellationRequested();
+                result.remote_response = response?.Body ?? string.Empty;
+
+                if (response == null)
+                {
+                    return Fail("接口异常:response null");
+                }
+
+                if (response.IsError)
+                {
+                    string reason = !string.IsNullOrWhiteSpace(response.SubErrMsg)
+                        ? response.SubErrMsg
+                        : !string.IsNullOrWhiteSpace(response.ErrMsg)
+                        ? response.ErrMsg
+                        : $"接口错误:{response.SubErrCode ?? response.ErrCode}";
+                    return Fail(reason);
+                }
+
+                if (response.BizErrorDesc != 0)
+                {
+                    return Fail($"{response.BizErrorDesc}:{response.ResultMsg}");
+                }
+
+                var material = response.Data?.MaterialUrlList.FirstOrDefault();
+                if (material == null)
+                {
+                    return Fail(string.IsNullOrWhiteSpace(response.ResultMsg) ? "解析结果为空" : response.ResultMsg);
+                }
+
+                if (material.Code != 0)
+                {
+                    return Fail($"{material.Code}:{material.Msg}", TkSubCodeEnum.NoConvert);
+                }
+
+                var linkInfo = material.LinkInfoDto;
+                if (linkInfo == null || string.IsNullOrWhiteSpace(linkInfo.TpwdOriginUrl))
+                {
+                    return Fail("解析结果缺少原始链接", TkSubCodeEnum.NoConvert);
+                }
+
+                string url = linkInfo.TpwdOriginUrl;
+                if (url.StartsWith("//")) url = $"https:{url}";
+
+                result.success = true;
+                result.message = string.Empty;
+                result.reason = material.ExtraInfo;
+                result.subCode = TkSubCodeEnum.Success;
+                result.link_type = linkInfo.MaterialType switch
+                {
+                    1 => LinkTypeEnum.goods,
+                    2 => LinkTypeEnum.profile,
+                    6 => LinkTypeEnum.live,
+                    _ => LinkTypeEnum.unknown
+                };
+                result.itemId = linkInfo.MaterialType == 1 ? linkInfo.MaterialId : string.Empty;
+                result.num_iid = result.itemId;
+                result.content = ReplaceUrls(content, url);
+                result.shortLinkurl = url;
+                result.deeplink_url = GetDeeplink(url);
+                result.item_url = url;
+                result.item_deeplink_url = result.deeplink_url;
+                return Task.FromResult(result);
+            }
+            finally
+            {
+                stopwatch.Stop();
+                result.elapsedTime3 = (int)stopwatch.ElapsedMilliseconds;
+            }
+        }
+    }
+}

+ 249 - 0
molilian.core/Plus/TaobaoOpen/TbkDgGeneralLinkParse.cs

@@ -0,0 +1,249 @@
+using dodohold.core;
+using System;
+using System.Collections.Generic;
+using System.Xml.Serialization;
+using Top.Api;
+using Top.Api.Request;
+using Top.Api.Response;
+using Top.Api.Util;
+
+namespace molilian.core
+{
+    public partial class TaobaoOpenPlus
+    {
+        /// <summary>
+        /// 万能解析:解析淘宝链接或淘口令,返回物料类型、物料ID及原始链接。
+        /// </summary>
+        /// <param name="materialUrl">待解析的链接或淘口令。</param>
+        /// <param name="relationId">可选的渠道管理ID。</param>
+        /// <param name="fields">可选返回字段,例如 origin_pid。</param>
+        public TbkDgGeneralLinkParseResponse GeneralLinkParseRequest(
+            string materialUrl, string? relationId = null, string? fields = null)
+        {
+            ArgumentException.ThrowIfNullOrWhiteSpace(materialUrl);
+            return GeneralLinkParseRequest(new[] { materialUrl }, relationId, fields);
+        }
+
+        /// <summary>
+        /// 批量解析淘宝链接或淘口令,每个物料的解析结果独立返回。
+        /// </summary>
+        public TbkDgGeneralLinkParseResponse GeneralLinkParseRequest(
+            IEnumerable<string> materialUrls, string? relationId = null, string? fields = null)
+        {
+            ArgumentNullException.ThrowIfNull(materialUrls);
+
+            List<TbkDgGeneralLinkParseRequest.LkMaterialDtoDomain> materials = new();
+            foreach (string materialUrl in materialUrls)
+            {
+                if (string.IsNullOrWhiteSpace(materialUrl))
+                {
+                    throw new ArgumentException("materialUrls cannot contain empty values", nameof(materialUrls));
+                }
+
+                materials.Add(new TbkDgGeneralLinkParseRequest.LkMaterialDtoDomain
+                {
+                    MaterialUrl = materialUrl
+                });
+            }
+
+            if (materials.Count == 0)
+            {
+                throw new ArgumentException("at least one material URL is required", nameof(materialUrls));
+            }
+
+            if (string.IsNullOrWhiteSpace(_pid))
+            {
+                throw new ArgumentException("pid is required", nameof(_pid));
+            }
+
+            string[] pidParts = _pid.Split('_');
+            if (pidParts.Length != 4 || pidParts[0] != "mm" ||
+                string.IsNullOrWhiteSpace(pidParts[1]) || string.IsNullOrWhiteSpace(pidParts[2]) ||
+                !long.TryParse(pidParts[3], out long adzoneId) || adzoneId <= 0)
+            {
+                throw new ArgumentException("pid format is invalid", nameof(_pid));
+            }
+
+            var client = new DefaultTopClient(_server_url, _app_key, _app_secret, "json");
+            client.SetProxy(_proxy);
+
+            TbkDgGeneralLinkParseRequest req = new()
+            {
+                AdzoneId = adzoneId,
+                MaterialDto_ = materials,
+                RelationId = relationId,
+                Fields = fields
+            };
+
+            TbkDgGeneralLinkParseResponse rsp = client.Execute(req);
+            if (_debug)
+            {
+                var requestJson = req.Convert2Json();
+                var responseJson = rsp.Convert2Json();
+                new LoggerLibrary("debug", _pid).Info(requestJson, responseJson).Save();
+            }
+            return rsp;
+        }
+    }
+}
+
+namespace Top.Api.Request
+{
+    /// <summary>
+    /// TOP API: taobao.tbk.dg.general.link.parse
+    /// </summary>
+    public class TbkDgGeneralLinkParseRequest : BaseTopRequest<TbkDgGeneralLinkParseResponse>
+    {
+        /// <summary>
+        /// 推广位ID,mm_xx_xx_xx 中的最后一段。
+        /// </summary>
+        public long? AdzoneId { get; set; }
+
+        /// <summary>
+        /// 渠道管理ID。
+        /// </summary>
+        public string? RelationId { get; set; }
+
+        /// <summary>
+        /// 链接/口令物料列表的JSON数组。
+        /// </summary>
+        public string MaterialDto { get; set; } = string.Empty;
+
+        public List<LkMaterialDtoDomain> MaterialDto_
+        {
+            set { MaterialDto = TopUtils.ObjectToJson(value); }
+        }
+
+        /// <summary>
+        /// 可选返回字段,有 origin_pid 使用场景时传入 origin_pid。
+        /// </summary>
+        public string? Fields { get; set; }
+
+        public override string GetApiName()
+        {
+            return "taobao.tbk.dg.general.link.parse";
+        }
+
+        public override IDictionary<string, string> GetParameters()
+        {
+            TopDictionary parameters = new();
+            parameters.Add("adzone_id", AdzoneId?.ToString());
+            parameters.Add("relation_id", RelationId);
+            parameters.Add("material_dto", MaterialDto);
+            parameters.Add("fields", Fields);
+            return parameters;
+        }
+
+        public override void Validate()
+        {
+            RequestValidator.ValidateRequired("adzone_id", AdzoneId);
+        }
+
+        [Serializable]
+        public class LkMaterialDtoDomain : TopObject
+        {
+            /// <summary>
+            /// 物料链接,可以为URL或淘口令。
+            /// </summary>
+            [XmlElement("material_url")]
+            public string MaterialUrl { get; set; } = string.Empty;
+        }
+    }
+}
+
+namespace Top.Api.Response
+{
+    /// <summary>
+    /// 淘宝客-推广者-万能解析响应。
+    /// </summary>
+    public class TbkDgGeneralLinkParseResponse : TopResponse
+    {
+        [XmlElement("data")]
+        public TbkLinkDtoDomain? Data { get; set; }
+
+        /// <summary>
+        /// 业务错误码。
+        /// </summary>
+        [XmlElement("biz_error_desc")]
+        public long BizErrorDesc { get; set; }
+
+        /// <summary>
+        /// 业务结果描述。
+        /// </summary>
+        [XmlElement("result_msg")]
+        public string ResultMsg { get; set; } = string.Empty;
+
+        [Serializable]
+        public class TbkLinkDtoDomain : TopObject
+        {
+            [XmlArray("material_url_list")]
+            [XmlArrayItem("material_url_list")]
+            public List<MaterialUrlListDomain> MaterialUrlList { get; set; } = new();
+        }
+
+        [Serializable]
+        public class MaterialUrlListDomain : TopObject
+        {
+            /// <summary>
+            /// 物料对应错误描述。
+            /// </summary>
+            [XmlElement("msg")]
+            public string Msg { get; set; } = string.Empty;
+
+            /// <summary>
+            /// 物料对应错误码,需逐项检查解析结果。
+            /// </summary>
+            [XmlElement("code")]
+            public long Code { get; set; }
+
+            [XmlElement("link_info_dto")]
+            public LinkInfoDtoDomain? LinkInfoDto { get; set; }
+
+            /// <summary>
+            /// 入参物料链接。
+            /// </summary>
+            [XmlElement("input_material_url")]
+            public string InputMaterialUrl { get; set; } = string.Empty;
+
+            /// <summary>
+            /// 解析成功时的补充说明。
+            /// </summary>
+            [XmlElement("extra_info")]
+            public string ExtraInfo { get; set; } = string.Empty;
+        }
+
+        [Serializable]
+        public class LinkInfoDtoDomain : TopObject
+        {
+            /// <summary>
+            /// 1-商品,2-店铺,3-会场,4-承接开放,5-优惠券,6-直播间。
+            /// </summary>
+            [XmlElement("material_type")]
+            public long MaterialType { get; set; }
+
+            /// <summary>
+            /// 入参口令/链接对应的原链接。
+            /// </summary>
+            [XmlElement("tpwd_origin_url")]
+            public string TpwdOriginUrl { get; set; } = string.Empty;
+
+            /// <summary>
+            /// 物料ID,根据物料类型区分商品ID、卖家ID或会场ID。
+            /// </summary>
+            [XmlElement("material_id")]
+            public string MaterialId { get; set; } = string.Empty;
+
+            /// <summary>
+            /// 1-联盟口令,2-主站口令;非淘口令场景不返回。
+            /// </summary>
+            [XmlElement("tk_biz_type")]
+            public long TkBizType { get; set; }
+
+            /// <summary>
+            /// 入参推广链接中的PID,需传入 fields=origin_pid,且属于当前推广者才返回。
+            /// </summary>
+            [XmlElement("origin_pid")]
+            public string OriginPid { get; set; } = string.Empty;
+        }
+    }
+}

部分文件因文件數量過多而無法顯示