Explorar o código

修改同步历史订单逻辑;
增加图片搜索同款API;
增加subcode;
增加淘宝联盟订单总数统计字段;
增加 ProxyManager 类;

dodo hold %!s(int64=2) %!d(string=hai) anos
pai
achega
6ef075c4df
Modificáronse 100 ficheiros con 9271 adicións e 106 borrados
  1. 2 0
      molilian.api/Controllers/admin/TaobaoController.cs
  2. 14 6
      molilian.api/Controllers/public/TaskController.cs
  3. 65 2
      molilian.api/Controllers/public/TkController.cs
  4. 91 0
      molilian.api/Controllers/public/similarController.cs
  5. 0 0
      molilian.api/Properties/PublishProfiles/https___ccr.ccs.tencentyun.com_shaobin.pubxml.user
  6. 1 1
      molilian.api/Properties/launchSettings.json
  7. 243 0
      molilian.core/Core/taoke/TaokeOpenCore.cs
  8. 86 4
      molilian.core/Core/taoke/TkLogCore.cs
  9. 5 1
      molilian.core/Core/taoke/TkPoolCore.cs
  10. 29 1
      molilian.core/Core/taoke/UnionParseCore.cs
  11. 52 0
      molilian.core/DTO/alimama/PromotionQueryDTO.cs
  12. 1 0
      molilian.core/DTO/alimama/TkConfigDTO.cs
  13. 1 0
      molilian.core/DTO/alimama/TkDataDTO.cs
  14. 7 0
      molilian.core/DTO/alimama/TkPoolDTO.cs
  15. 11 0
      molilian.core/DTO/alimama/TkReportDTO.cs
  16. 0 0
      molilian.core/DTO/alimama/UnionCouponDTO.cs
  17. 1 0
      molilian.core/DTO/dy/DyDataDTO.cs
  18. 1 0
      molilian.core/DTO/jd/JdDataDTO.cs
  19. 198 68
      molilian.core/Plus/Alimama/orders.cs
  20. 65 13
      molilian.core/Plus/Alimama/parse.cs
  21. 3 4
      molilian.core/Plus/Alimama/parse_2.cs
  22. 1 2
      molilian.core/Plus/JDUnion/JdUnionPlus.cs
  23. 1 2
      molilian.core/Plus/Pangolin/DyUnion/DyUnionPlus.cs
  24. 113 0
      molilian.core/Plus/ProxyManager.cs
  25. 149 0
      molilian.core/Plus/TaobaoOpen/TaobaoOpenPlus.cs
  26. 1 2
      molilian.core/Plus/Tool/ToolParsePlus.cs
  27. 4 0
      molilian.core/molilian.core.csproj
  28. 6 0
      molilian.sln
  29. 39 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Aliyun/AliyunResponse.cs
  30. 315 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Aliyun/DefaultAliyunClient.cs
  31. 37 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Aliyun/IAliyunClient.cs
  32. 29 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Aliyun/IAliyunRequest.cs
  33. 19 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Aliyun/IAliyunUploadRequest.cs
  34. 203 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Aliyun/Parser/AliyunJsonParser.cs
  35. 81 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Aliyun/Parser/AliyunJsonReader.cs
  36. 82 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Aliyun/Parser/AliyunXmlParser.cs
  37. 17 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Aliyun/Parser/IAliyunParser.cs
  38. 261 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Aliyun/Util/AliyunWebUtils.cs
  39. 37 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/AutoRetryClusterTopClient.cs
  40. 143 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/AutoRetryTopClient.cs
  41. 93 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/BaseTopRequest.cs
  42. 261 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/BatchTopClient.cs
  43. 102 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Cluster/ClusterManager.cs
  44. 265 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Cluster/DnsConfig.cs
  45. 40 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/ClusterTopClient.cs
  46. 62 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Constants.cs
  47. 277 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/DefaultTopClient.cs
  48. 501 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/DefaultTopLogger.cs
  49. 63 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/DingTalk/BaseDingTalkRequest.cs
  50. 438 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/DingTalk/DefaultDingTalkClient.cs
  51. 11 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/DingTalk/DingTalkConstants.cs
  52. 240 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/DingTalk/DingTalkJsonParser.cs
  53. 12 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/DingTalk/DingTalkResponse.cs
  54. 41 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/DingTalk/IDingTalkClient.cs
  55. 43 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/DingTalk/IDingTalkRequest.cs
  56. 20 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/DingTalk/IDingTalkUploadRequest.cs
  57. 54 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/AppBaseInfo.cs
  58. 42 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/Area.cs
  59. 36 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/Brand.cs
  60. 72 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/DataPushTask.cs
  61. 24 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/Feature.cs
  62. 84 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/File.cs
  63. 680 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/Item.cs
  64. 62 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/ItemCat.cs
  65. 36 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/ItemImg.cs
  66. 177 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/ItemProp.cs
  67. 60 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/JdpUser.cs
  68. 30 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/KfcSearchResult.cs
  69. 48 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/Location.cs
  70. 108 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/Monitorstation.cs
  71. 54 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/OAuthOtherInfo.cs
  72. 204 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/OpenAccount.cs
  73. 36 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/OpenAccountResult.cs
  74. 38 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/OpenAccountSearchResult.cs
  75. 36 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/OpenAccountTokenValidateResult.cs
  76. 36 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/OpenaccountLong.cs
  77. 36 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/OpenaccountObject.cs
  78. 30 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/OpenaccountVoid.cs
  79. 159 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/Product.cs
  80. 48 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/ProductImg.cs
  81. 54 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/ProductPropImg.cs
  82. 42 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/PropImg.cs
  83. 74 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/PropValue.cs
  84. 72 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/RdsConfig.cs
  85. 34 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/SellerAuthorize.cs
  86. 144 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/Sku.cs
  87. 26 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/TmcGroup.cs
  88. 54 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/TmcMessage.cs
  89. 30 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/TmcProduceResult.cs
  90. 62 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/TmcUser.cs
  91. 48 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/TokenInfo.cs
  92. 24 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/TokenInfoExt.cs
  93. 30 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/TradeConfirmFee.cs
  94. 54 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/TradeTrace.cs
  95. 264 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/User.cs
  96. 36 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/UserCredit.cs
  97. 54 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/Video.cs
  98. 74 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Generated/FastJson/Formatter.cs
  99. 11 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Generated/FastJson/Getters.cs
  100. 1011 0
      taobao-sdk-netcore-auto_1719572210878-20240628-source/Generated/FastJson/JSON.cs

+ 2 - 0
molilian.api/Controllers/admin/TaobaoController.cs

@@ -63,6 +63,8 @@ namespace molilian.api.Controllers
                 case "enable_fake_click":
                 case "enable_fake_click":
                 case "enable_parse":
                 case "enable_parse":
                 case "enable_coupon":
                 case "enable_coupon":
+                case "enable_sync_order":
+                case "enable_promotionQuery":
                 case "status":
                 case "status":
                     bool bVal = val.Equals("True");
                     bool bVal = val.Equals("True");
                     result = new DBContext.Table("tk_pool")
                     result = new DBContext.Table("tk_pool")

+ 14 - 6
molilian.api/Controllers/public/TaskController.cs

@@ -38,6 +38,7 @@ namespace molilian.api.Controllers
             int total = 0;
             int total = 0;
             foreach (var account in list)
             foreach (var account in list)
             {
             {
+                if (!account.enable_sync_order) continue;
 #if DEBUG
 #if DEBUG
                 if (13 != account.id) continue;
                 if (13 != account.id) continue;
 #endif
 #endif
@@ -62,7 +63,7 @@ namespace molilian.api.Controllers
         }
         }
 
 
         [HttpGet]
         [HttpGet]
-        public async Task<ActionResult> GetHistoryOrders(int id, int sleep = 2000)
+        public async Task<ActionResult> GetHistoryOrders(int id, int sleep, DateTime startTime, DateTime endTime)
         {
         {
             var list = TkPoolCore.List();
             var list = TkPoolCore.List();
             if (list == null) return new APIResult(new { success = false, message = "没有有效账号", });
             if (list == null) return new APIResult(new { success = false, message = "没有有效账号", });
@@ -77,8 +78,13 @@ namespace molilian.api.Controllers
 
 
                 try
                 try
                 {
                 {
+                    //DateTime endTime = DateTime.Now;
+                    //DateTime startTime = endTime.AddDays(-90);
+                    if (account.id == 3) endTime = DateTime.Parse("2024-04-07");
+
+
                     var alimama = new AlimamaPlus(account);
                     var alimama = new AlimamaPlus(account);
-                    total += alimama.GetHistoryOrders(sleep);
+                    total += alimama.GetHistoryOrders(sleep, startTime, endTime);
                 }
                 }
                 catch (Exception ex)
                 catch (Exception ex)
                 {
                 {
@@ -107,6 +113,7 @@ namespace molilian.api.Controllers
 #if DEBUG
 #if DEBUG
                 if (account.id != 17) continue;
                 if (account.id != 17) continue;
 #endif
 #endif
+                if (!account.enable_sync_order) continue;
 
 
                 try
                 try
                 {
                 {
@@ -226,6 +233,7 @@ namespace molilian.api.Controllers
             string message = string.Empty;
             string message = string.Empty;
             foreach (var account in list)
             foreach (var account in list)
             {
             {
+                if (!account.enable_sync_order) continue;
                 try
                 try
                 {
                 {
                     var alimama = new AlimamaPlus(account);
                     var alimama = new AlimamaPlus(account);
@@ -254,6 +262,7 @@ namespace molilian.api.Controllers
             string message = string.Empty;
             string message = string.Empty;
             foreach (var account in list)
             foreach (var account in list)
             {
             {
+                if (!account.enable_sync_order) continue;
 #if DEBUG
 #if DEBUG
                 if (account.id != 5) continue;
                 if (account.id != 5) continue;
 #endif
 #endif
@@ -288,6 +297,7 @@ namespace molilian.api.Controllers
             string message = string.Empty;
             string message = string.Empty;
             foreach (var account in list)
             foreach (var account in list)
             {
             {
+                if (!account.enable_sync_order) continue;
 #if DEBUG
 #if DEBUG
                 if (account.id != 5) continue;
                 if (account.id != 5) continue;
 #endif
 #endif
@@ -319,7 +329,7 @@ namespace molilian.api.Controllers
         }
         }
 
 
         [HttpGet]
         [HttpGet]
-        public async Task<ActionResult> FixReport(string dt)
+        public async Task<ActionResult> FixReport(int id, string dt)
         {
         {
             if (!DateTime.TryParse(dt, out DateTime date))
             if (!DateTime.TryParse(dt, out DateTime date))
             {
             {
@@ -331,9 +341,7 @@ namespace molilian.api.Controllers
             string message = string.Empty;
             string message = string.Empty;
             foreach (var account in list)
             foreach (var account in list)
             {
             {
-#if DEBUG
-                if (account.id != 5) continue;
-#endif
+                if (id != account.id) continue;
                 try
                 try
                 {
                 {
                     var alimama = new AlimamaPlus(account);
                     var alimama = new AlimamaPlus(account);

+ 65 - 2
molilian.api/Controllers/public/TkController.cs

@@ -26,6 +26,69 @@ namespace molilian.api.Controllers
         }
         }
 
 
 
 
+        [HttpPost]
+        [Route("/[action]")]
+        public async Task<ActionResult> PromotionQuery([FromBody] JsonElement form, [FromQuery] string a = "", [FromQuery] int t = 0, [FromQuery] string sign = "")
+        {
+            var img = form.Read("img", string.Empty);
+            var url = form.Read("url", string.Empty);
+            var ip = form.Read("ip", string.Empty);
+            var oaid = form.Read("oaid", string.Empty);
+
+            //验证签名
+            if (string.IsNullOrEmpty(a) || string.IsNullOrEmpty(sign))
+            {
+                return new APIResult(new { success = false, message = "验证错误" }, APIResultCodeEnum.Unauthorized);
+            }
+
+            DateTime time1 = t.Convert2Datetime();
+            if (time1 < DateTime.Now.AddMinutes(-10))
+            {
+                return new APIResult(new { success = false, message = "验证错误2" }, APIResultCodeEnum.Unauthorized);
+            }
+
+            var account = ApiAccountCore.GetOne(a);
+            if (account == null)
+            {
+                return new APIResult(new { success = false, message = "验证错误3" }, APIResultCodeEnum.Unauthorized);
+            }
+            string app_sign = $"{account.api_secret}@{t}".MD5();
+            if (sign != app_sign)
+            {
+                return new APIResult(new { success = false, message = "验证错误4" }, APIResultCodeEnum.Unauthorized);
+            }
+
+            if (!string.IsNullOrEmpty(url))
+            {
+                var bytes = new WebClientUtility().Request(url).ResponseBody;
+                img = Convert.ToBase64String(bytes);
+            }
+            return await TaokeOpenCore.PromotionQueryAsync(img, oaid, ip);
+        }
+
+
+        [HttpPost]
+        [Route("/[action]")]
+        public async Task<ActionResult> unsafePromotionQuery([FromBody] JsonElement form)
+        {
+            var img = form.Read("img", string.Empty);
+            var url = form.Read("url", string.Empty);
+            var ip = form.Read("ip", string.Empty);
+            var oaid = form.Read("oaid", string.Empty);
+#if DEBUG
+            ip = "127.0.0.1";
+            oaid = "test-oaid";
+#endif
+
+            if (!string.IsNullOrEmpty(url))
+            { 
+                var bytes = new WebClientUtility().Request(url).ResponseBody;
+                img = Convert.ToBase64String(bytes);
+            }
+            return await TaokeOpenCore.PromotionQueryAsync(img, oaid, ip);
+        }
+
+
         /// <summary>
         /// <summary>
         /// oppo调用
         /// oppo调用
         /// </summary>
         /// </summary>
@@ -622,7 +685,7 @@ namespace molilian.api.Controllers
             List<object> list = [];
             List<object> list = [];
             foreach (var item in data)
             foreach (var item in data)
             {
             {
-                list.Add(new { url = item.deeplinkUrl, count = item.click_num, time = item.exp_time });
+                list.Add(new { url = item.deeplinkUrl, item.shortLinkUrl, count = item.click_num, time = item.exp_time });
             }
             }
             var clientIp = _accessor.HttpContext.GetUserIp();
             var clientIp = _accessor.HttpContext.GetUserIp();
             LoggerLibrary log = new LoggerLibrary("api", "dplist");
             LoggerLibrary log = new LoggerLibrary("api", "dplist");
@@ -666,7 +729,7 @@ namespace molilian.api.Controllers
             List<object> list = [];
             List<object> list = [];
             foreach (var item in data)
             foreach (var item in data)
             {
             {
-                list.Add(new { url = item.deeplinkUrl, count = item.click_num, time = item.exp_time });
+                list.Add(new { url = item.deeplinkUrl, item.shortLinkUrl, count = item.click_num, time = item.exp_time });
             }
             }
             var clientIp = _accessor.HttpContext.GetUserIp();
             var clientIp = _accessor.HttpContext.GetUserIp();
             LoggerLibrary log = new LoggerLibrary("api", "unsafeParseDpList");
             LoggerLibrary log = new LoggerLibrary("api", "unsafeParseDpList");

+ 91 - 0
molilian.api/Controllers/public/similarController.cs

@@ -0,0 +1,91 @@
+using molilian.core;
+using dodohold.core;
+using Microsoft.AspNetCore.Mvc;
+using Org.BouncyCastle.Ocsp;
+using System.Text.Json;
+using System.Runtime.InteropServices;
+using System.Net;
+using System.Security.Cryptography;
+using Microsoft.AspNetCore.Authentication;
+using TencentCloud.Ecm.V20190719.Models;
+using COSXML.Network;
+using System.Linq;
+using static dodohold.core.ZTOExpress.CreateOrderArgs;
+
+namespace molilian.api.Controllers
+{
+    [ApiController]
+    [Route("api/[action]")]
+    public class SimilarController : ControllerBase
+    {
+
+        protected IHttpContextAccessor _accessor;
+        public SimilarController(IHttpContextAccessor accessor)
+        {
+            _accessor = accessor;
+        }
+
+
+        [HttpPost]
+        public async Task<ActionResult> PromotionQuery([FromBody] JsonElement form, [FromQuery] string a = "", [FromQuery] int t = 0, [FromQuery] string sign = "")
+        {
+            var img = form.Read("img", string.Empty);
+            var url = form.Read("url", string.Empty);
+            var ip = form.Read("ip", string.Empty);
+            var oaid = form.Read("oaid", string.Empty);
+
+            //验证签名
+            if (string.IsNullOrEmpty(a) || string.IsNullOrEmpty(sign))
+            {
+                return new APIResult(new { success = false, message = "验证错误" }, APIResultCodeEnum.Unauthorized);
+            }
+
+            DateTime time1 = t.Convert2Datetime();
+            if (time1 < DateTime.Now.AddMinutes(-10))
+            {
+                return new APIResult(new { success = false, message = "验证错误2" }, APIResultCodeEnum.Unauthorized);
+            }
+
+            var account = ApiAccountCore.GetOne(a);
+            if (account == null)
+            {
+                return new APIResult(new { success = false, message = "验证错误3" }, APIResultCodeEnum.Unauthorized);
+            }
+            string app_sign = $"{account.api_secret}@{t}".MD5();
+            if (sign != app_sign)
+            {
+                return new APIResult(new { success = false, message = "验证错误4" }, APIResultCodeEnum.Unauthorized);
+            }
+
+            if (!string.IsNullOrEmpty(url))
+            {
+                var bytes = new WebClientUtility().Request(url).ResponseBody;
+                img = Convert.ToBase64String(bytes);
+            }
+            return await TaokeOpenCore.PromotionQueryAsync(img, oaid, ip);
+        }
+
+
+        [HttpPost]
+        public async Task<ActionResult> unsafePromotionQuery([FromBody] JsonElement form)
+        {
+            var img = form.Read("img", string.Empty);
+            var url = form.Read("url", string.Empty);
+            var ip = form.Read("ip", string.Empty);
+            var oaid = form.Read("oaid", string.Empty);
+#if DEBUG
+            ip = "127.0.0.1";
+            oaid = "test-oaid";
+#endif
+
+            if (!string.IsNullOrEmpty(url))
+            {
+                var bytes = new WebClientUtility().Request(url).ResponseBody;
+                img = Convert.ToBase64String(bytes);
+            }
+            return await TaokeOpenCore.PromotionQueryAsync(img, oaid, ip);
+        }
+
+         
+    }
+}

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
molilian.api/Properties/PublishProfiles/https___ccr.ccs.tencentyun.com_shaobin.pubxml.user


+ 1 - 1
molilian.api/Properties/launchSettings.json

@@ -10,7 +10,7 @@
         "NtfyServer": "https://ntfy.yunhui800.com/5Sq9BytXXM5WDY3G",
         "NtfyServer": "https://ntfy.yunhui800.com/5Sq9BytXXM5WDY3G",
         "ANPush": "",
         "ANPush": "",
         "DBType": "MySQL",
         "DBType": "MySQL",
-        "DBConfig": "Server=rm-2zef2t13aq33dha43vo.mysql.rds.aliyuncs.com; Port=3306; Database=taoke; Uid=taoke; Pwd=67ktWBmw5G4yMs4J;SslMode=None;CharSet=utf8mb4;ConnectionTimeout=60",
+        "DBConfig": "Server=rm-2zey1jqxnoqy9mcc0zo.rwlb.rds.aliyuncs.com; Port=3306; Database=taoke; Uid=taoke; Pwd=67ktWBmw5G4yMs4J;SslMode=None;CharSet=utf8mb4;ConnectionTimeout=60",
         "RedisConfig": "101.200.46.46:6379,password=pKBiS4ka2IpXayIdcx00,defaultDatabase=0,idleTimeout=20000,preheat=3,tryit=2,ssl=false,prefix=webhook"
         "RedisConfig": "101.200.46.46:6379,password=pKBiS4ka2IpXayIdcx00,defaultDatabase=0,idleTimeout=20000,preheat=3,tryit=2,ssl=false,prefix=webhook"
       },
       },
       "dotnetRunMessages": true,
       "dotnetRunMessages": true,

+ 243 - 0
molilian.core/Core/taoke/TaokeOpenCore.cs

@@ -0,0 +1,243 @@
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc.Controllers;
+using Microsoft.AspNetCore.Mvc.Filters;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using dodohold.core;
+using Spire.Pdf.Exporting.XPS.Schema;
+using System.Xml.Linq;
+using System.Net;
+using TencentCloud.Mrs.V20200910.Models;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Authentication;
+using Spire.Pdf.Graphics;
+using Google.Protobuf.WellKnownTypes;
+using System.Threading.Channels;
+using TencentCloud.Weilingwith.V20230427.Models;
+using System.Threading;
+using System.Text.Json;
+
+
+namespace molilian.core
+{
+    public partial class TaokeOpenCore
+    {
+        static string _test_base64String = string.Empty;
+
+        private static string _end_point;
+        static TaokeOpenCore()
+        {
+            _end_point = Environment.GetEnvironmentVariable("EndPoint");
+        }
+
+
+        public static async Task<APIResult> PromotionQueryAsync(string img, string oaid = "", string ip = "")
+        {
+            PromotionQueryDTO result = new()
+            {
+                end_point = _end_point,
+                ip = ip,
+                oaid = oaid,
+                img = img
+            };
+
+            if (string.IsNullOrEmpty(img))
+            {
+                result.success = false;
+                result.message = "调用失败";
+                result.reason = "无效图片";
+                _ = TkLogCore.PromotionImgLogAsync(result);
+                return new APIResult(new
+                {
+                    result.success,
+                    result.message,
+                    result.reason,
+                }, APIResultCodeEnum.OK, new JsonSerializerOptions
+                {
+                    Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
+                });
+            }
+            try
+            {
+                var account = TkPoolCore.GetOne(TkPoolCore.TkAction.promotionQuery);
+                if (account == null)
+                {
+                    result.success = false;
+                    result.message = "调用失败";
+                    result.reason = "没有匹配账号";
+                    _ = TkLogCore.PromotionImgLogAsync(result);
+                    return new APIResult(new
+                    {
+                        result.success,
+                        result.message,
+                        result.reason,
+                    }, APIResultCodeEnum.OK, new JsonSerializerOptions
+                    {
+                        Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
+                    });
+                }
+
+                TaobaoOpenPlus taobao = new(account.appkey, account.appSecret, account.open_pid);
+                var data = taobao.PromotionQueryRequest(img, oaid);
+                if (data.IsError)
+                {
+                    result.success = false;
+                    result.message = "调用失败";
+                    result.reason = data.ErrMsg;
+                    _ = TkLogCore.PromotionImgLogAsync(result);
+                    return new APIResult(new
+                    {
+                        result.success,
+                        result.message,
+                        result.reason,
+                    }, APIResultCodeEnum.OK, new JsonSerializerOptions
+                    {
+                        Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
+                    });
+                }
+                if (!"0".Equals(data.Status))
+                {
+                    result.success = false;
+                    result.message = "调用失败";
+                    result.reason = data.Desc;
+                    _ = TkLogCore.PromotionImgLogAsync(result);
+                    return new APIResult(new
+                    {
+                        result.success,
+                        result.message,
+                        result.reason,
+                    }, APIResultCodeEnum.OK, new JsonSerializerOptions
+                    {
+                        Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
+                    });
+                }
+                result.url = data.Url;
+
+                oaid = data.Url.GetContentPart("oaid=", "&");
+                string cookies = string.Empty;
+
+                (result.SimilarPromotion, cookies) = await taobao.GetRecommendData(TaobaoOpenPlus.RecommendRequestType.first, oaid, cookies);
+                (result.PromotionImg, cookies) = await taobao.GetRecommendData(TaobaoOpenPlus.RecommendRequestType.ori, oaid, cookies);
+                result.similarPromotion = result.SimilarPromotion.Convert2Json();
+                result.promotionImg = result.PromotionImg.Convert2Json();
+
+                result.success = true;
+                result.message = "调用成功";
+                _ = TkLogCore.PromotionImgLogAsync(result);
+                return new APIResult(new
+                {
+                    result.success,
+                    result.message,
+                    result.reason,
+                    result.SimilarPromotion,
+                    result.PromotionImg,
+                    result.url,
+                }, APIResultCodeEnum.OK, new JsonSerializerOptions
+                {
+                    Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
+                });
+
+            }
+            catch (Exception ex)
+            {
+                result.success = false;
+                result.message = "调用失败";
+                result.reason = ex.Message;
+                _ = TkLogCore.PromotionImgLogAsync(result);
+                return new APIResult(new
+                {
+                    result.success,
+                    result.message,
+                    result.reason,
+                }, APIResultCodeEnum.OK, new JsonSerializerOptions
+                {
+                    Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
+                });
+            }
+
+
+            //try
+            //{
+            //    var account = TkPoolCore.GetOne(TkPoolCore.TkAction.promotionQuery);
+            //    if (account == null)
+            //    {
+            //        result.success = false;
+            //        result.message = "放弃转链";
+            //        result.reason = "没有匹配账号";
+            //        //_ = TkLogCore.CouponLogAsync(result);
+            //        return new APIResult(new
+            //        {
+            //            result.success,
+            //            message = "没有优惠券",
+            //            channel = result.channel.ToString(),
+            //        });
+            //    }
+            //    result.accountId = account.id;
+            //    result.accountName = account.name;
+            //    alimama = new AlimamaPlus(account);
+            //    result = await alimama.PromotionQueryAsync(result);
+
+            //    if (result.success && result.couponAmount == 0)
+            //    {
+            //        result.success = false;
+            //        result.message = "没有优惠券";
+            //        result.reason = "没有优惠券";
+            //    }
+            //}
+            //catch (Exception ex)
+            //{
+            //    if (ex.Message.Contains("was canceled"))
+            //    {
+            //        result.success = false;
+            //        result.message = "放弃转链";
+            //        result.reason = "请求超时";
+            //    }
+            //    else
+            //    {
+            //        _ = new LoggerLibrary("unionCoupon", "tb_error")
+            //            .Info(ip, oaid)
+            //            .Info(content)
+            //            .Info(ex.Message, ex.StackTrace)
+            //            .SaveAsync();
+
+            //        NotifyCore.Notify(new NifyMessage
+            //        {
+            //            message = $"【转链异常TB】\n{content}\n\n{ex.Message}\n{ex.StackTrace}",
+            //            priority = NifyMessagePriority.high,
+            //            tags = ["red_circle"]
+            //        });
+            //        result.success = false;
+            //        result.message = "放弃转链";
+            //        result.reason = "转链接口异常";
+            //    }
+            //}
+            //_ = TkLogCore.CouponLogAsync(result, alimama);
+            //if (!result.success)
+            //{
+            //    return new APIResult(new
+            //    {
+            //        result.success,
+            //        message = "没有优惠券",
+            //        channel = result?.channel.ToString(),
+            //    });
+            //}
+            //int couponAmount = (int)result.couponAmount * 100;
+
+            //return new APIResult(new
+            //{
+            //    result.success,
+            //    result.message,
+            //    channel = result?.channel.ToString(),
+            //    result?.itemId,
+            //    result?.deeplink_url,
+            //    result?.pic,
+            //    result?.couponEffectiveStartTime,
+            //    result?.couponEffectiveEndTime,
+            //});
+        }
+
+    }
+
+}

+ 86 - 4
molilian.core/Core/taoke/TkLogCore.cs

@@ -32,6 +32,8 @@ namespace molilian.core
         static string queue_parse_tool_key = "queue:parse_logs:tool";
         static string queue_parse_tool_key = "queue:parse_logs:tool";
 
 
         static string queue_coupon_key = "queue:coupon_logs";
         static string queue_coupon_key = "queue:coupon_logs";
+        static string promotion_img_key = "queue:promotion:img";
+
 
 
         public static int BatchInsertLogDB(int limit)
         public static int BatchInsertLogDB(int limit)
         {
         {
@@ -67,6 +69,7 @@ namespace molilian.core
                   .Add("deeplink_url", data.deeplink_url)
                   .Add("deeplink_url", data.deeplink_url)
                   .Add("num_iid", data.num_iid)
                   .Add("num_iid", data.num_iid)
                   .Add("elapsedTime", data.elapsedTime)
                   .Add("elapsedTime", data.elapsedTime)
+                  .Add("subCode", data.subCode)
                   .Add("ip", data.ip)
                   .Add("ip", data.ip)
                   .Add("oaid", data.oaid)
                   .Add("oaid", data.oaid)
                   .Add("create_time", data.create_time)
                   .Add("create_time", data.create_time)
@@ -96,6 +99,7 @@ namespace molilian.core
                 .Add("elapsedTime", data.elapsedTime)
                 .Add("elapsedTime", data.elapsedTime)
                 .Add("elapsedTime2", data.elapsedTime2)
                 .Add("elapsedTime2", data.elapsedTime2)
                 .Add("elapsedTime3", data.elapsedTime3)
                 .Add("elapsedTime3", data.elapsedTime3)
+                .Add("subCode", data.subCode)
                 .Add("ip", data.ip)
                 .Add("ip", data.ip)
                 .Add("oaid", data.oaid)
                 .Add("oaid", data.oaid)
                 .Add("create_time", data.create_time)
                 .Add("create_time", data.create_time)
@@ -120,7 +124,8 @@ namespace molilian.core
                 {
                 {
                     var data = redis.LPop<TkDataDTO>(queue_tb_key);
                     var data = redis.LPop<TkDataDTO>(queue_tb_key);
                     if (data == null) break;
                     if (data == null) break;
-                    if ("127.0.0.1".Equals(data.ip) || data.elapsedTime > 2000)
+                    if ("3JDaxNfPy83okP3kLScwCkGiuMcyC4PcyntF424979CC9C51BCAD0C245B1C7BA2".Equals(data.oaid) ||
+                        "127.0.0.1".Equals(data.ip) || data.elapsedTime > 1000)
                     {
                     {
                         save_tk_log(data, "tk_logs_test", connection, transaction);
                         save_tk_log(data, "tk_logs_test", connection, transaction);
                     }
                     }
@@ -185,7 +190,8 @@ namespace molilian.core
                     //        break;
                     //        break;
                     //}
                     //}
 
 
-                    if ("127.0.0.1".Equals(data.ip) || data.elapsedTime > 2000)
+                    if ("3JDaxNfPy83okP3kLScwCkGiuMcyC4PcyntF424979CC9C51BCAD0C245B1C7BA2".Equals(data.oaid) ||
+                        "127.0.0.1".Equals(data.ip) || data.elapsedTime > 1000)
                     {
                     {
                         save_tk_parse_logs(data, "tk_parse_logs_test", connection, transaction);
                         save_tk_parse_logs(data, "tk_parse_logs_test", connection, transaction);
 
 
@@ -204,6 +210,15 @@ namespace molilian.core
                     {
                     {
                         save_tk_parse_logs(data, "tk_parse_logs_test2", connection, transaction);
                         save_tk_parse_logs(data, "tk_parse_logs_test2", connection, transaction);
                     }
                     }
+
+                    if (data.reason.Contains("初步筛选1.5"))
+                    {
+                        save_tk_parse_logs(data, "tk_parse_logs_multi_token", connection, transaction);
+                    }
+                    if (data.reason.Contains("霸下验证码"))
+                    {
+                        save_tk_parse_logs(data, "tk_parse_logs_captcha", connection, transaction);
+                    }
                     total++;
                     total++;
                 }
                 }
 
 
@@ -212,7 +227,7 @@ namespace molilian.core
                 {
                 {
                     var data = redis.LPop<JdDataDTO>(queue_parse_jd_key);
                     var data = redis.LPop<JdDataDTO>(queue_parse_jd_key);
                     if (data == null) break;
                     if (data == null) break;
-                    new DBContext.Table(connection, "tk_parse_logs")
+                    new DBContext.Table(connection, "jd_parse_logs")
                         .Add("end_point", data.end_point)
                         .Add("end_point", data.end_point)
                         .Add("channel", (int)data.channel)
                         .Add("channel", (int)data.channel)
                         .Add("accountId", data.accountId)
                         .Add("accountId", data.accountId)
@@ -231,6 +246,7 @@ namespace molilian.core
                         .Add("shortLinkurl", data.shortLinkurl)
                         .Add("shortLinkurl", data.shortLinkurl)
                         .Add("deeplink_url", data.deeplink_url)
                         .Add("deeplink_url", data.deeplink_url)
                         .Add("elapsedTime", data.elapsedTime)
                         .Add("elapsedTime", data.elapsedTime)
+                        .Add("subCode", data.subCode)
                         .Add("ip", data.ip)
                         .Add("ip", data.ip)
                         .Add("oaid", data.oaid)
                         .Add("oaid", data.oaid)
                         .Add("create_time", data.create_time)
                         .Add("create_time", data.create_time)
@@ -260,6 +276,7 @@ namespace molilian.core
                         .Add("shortLinkurl", data.shortLinkurl)
                         .Add("shortLinkurl", data.shortLinkurl)
                         .Add("deeplink_url", data.deeplink_url)
                         .Add("deeplink_url", data.deeplink_url)
                         .Add("elapsedTime", data.elapsedTime)
                         .Add("elapsedTime", data.elapsedTime)
+                        .Add("subCode", data.subCode)
                         .Add("ip", data.ip)
                         .Add("ip", data.ip)
                         .Add("oaid", data.oaid)
                         .Add("oaid", data.oaid)
                         .Add("create_time", data.create_time)
                         .Add("create_time", data.create_time)
@@ -297,6 +314,14 @@ namespace molilian.core
                     connection.Insert(data);
                     connection.Insert(data);
                     total++;
                     total++;
                 }
                 }
+
+                for (int i = 0; i < limit; i++)
+                {
+                    var data = redis.LPop<PromotionQueryDTO>(promotion_img_key);
+                    if (data == null) break;
+                    connection.Insert(data);
+                    total++;
+                }
                 transaction.Commit();
                 transaction.Commit();
             }
             }
             catch (Exception ex)
             catch (Exception ex)
@@ -321,6 +346,21 @@ namespace molilian.core
             return total;
             return total;
         }
         }
 
 
+        public static async Task PromotionImgLogAsync(PromotionQueryDTO response)
+        {
+            try
+            {
+                var ts = DateTime.Now - response.create_time;
+                response.elapsedTime = (int)ts.TotalMilliseconds;
+                _ = RedisHelper.RPushAsync(promotion_img_key, response);
+
+                //using var connection = DBContext.GetOpenConnection();
+                //connection.Insert(response);
+                savePromotionCache(response.accountId, response.accountName, response.success, response.message, response.reason);
+            }
+            catch (Exception ex) { }
+        }
+
         public static async Task LogAsync(JdDataDTO response)
         public static async Task LogAsync(JdDataDTO response)
         {
         {
             try
             try
@@ -581,15 +621,57 @@ namespace molilian.core
                 RedisHelper.IncrBy($":parse_total:{accountName}:{reason}:{DateTime.Now:yyyyMMdd}");
                 RedisHelper.IncrBy($":parse_total:{accountName}:{reason}:{DateTime.Now:yyyyMMdd}");
                 RedisHelper.IncrBy($":parse_total:{accountName}:{reason}:{DateTime.Now:yyyyMMddHH}");
                 RedisHelper.IncrBy($":parse_total:{accountName}:{reason}:{DateTime.Now:yyyyMMddHH}");
             }
             }
+        }
 
 
+        private static void savePromotionCache(int accountId, string accountName, bool success, string message, string reason)
+        {
+            savePromotionAccountCache("all", success, message, reason);
+            savePromotionAccountCache($"{accountId}", success, message, reason);
         }
         }
+
+        private static void savePromotionAccountCache(string accountName, bool success, string message, string reason)
+        {
+            RedisHelper.IncrBy($":promotion_total:{accountName}:{DateTime.Now:yyyyMM}");
+            RedisHelper.IncrBy($":promotion_total:{accountName}:{DateTime.Now:yyyyMMdd}");
+            RedisHelper.IncrBy($":promotion_total:{accountName}:{DateTime.Now:yyyyMMddHH}");
+
+
+            string result = success ? "success" : "fail";
+            RedisHelper.IncrBy($":promotion_total:{accountName}:{result}:{DateTime.Now:yyyyMM}");
+            RedisHelper.IncrBy($":promotion_total:{accountName}:{result}:{DateTime.Now:yyyyMMdd}");
+            RedisHelper.IncrBy($":promotion_total:{accountName}:{result}:{DateTime.Now:yyyyMMddHH}");
+
+
+            if (!string.IsNullOrEmpty(message))
+            {
+                RedisHelper.SAdd($":promotion_total:{accountName}:message:{DateTime.Now:yyyyMM}", message);
+                RedisHelper.SAdd($":promotion_total:{accountName}:message:{DateTime.Now:yyyyMMdd}", message);
+                RedisHelper.SAdd($":promotion_total:{accountName}:message:{DateTime.Now:yyyyMMddHH}", message);
+
+                RedisHelper.IncrBy($":promotion_total:{accountName}:{message}:{DateTime.Now:yyyyMM}");
+                RedisHelper.IncrBy($":promotion_total:{accountName}:{message}:{DateTime.Now:yyyyMMdd}");
+                RedisHelper.IncrBy($":promotion_total:{accountName}:{message}:{DateTime.Now:yyyyMMddHH}");
+            }
+
+            if (!string.IsNullOrEmpty(reason))
+            {
+                RedisHelper.SAdd($":promotion_total:{accountName}:reason:{DateTime.Now:yyyyMM}", reason);
+                RedisHelper.SAdd($":promotion_total:{accountName}:reason:{DateTime.Now:yyyyMMdd}", reason);
+                RedisHelper.SAdd($":promotion_total:{accountName}:reason:{DateTime.Now:yyyyMMddHH}", reason);
+
+                RedisHelper.IncrBy($":promotion_total:{accountName}:{reason}:{DateTime.Now:yyyyMM}");
+                RedisHelper.IncrBy($":promotion_total:{accountName}:{reason}:{DateTime.Now:yyyyMMdd}");
+                RedisHelper.IncrBy($":promotion_total:{accountName}:{reason}:{DateTime.Now:yyyyMMddHH}");
+            }
+        }
+
+
         private static void saveCouponCache(string channel, int accountId, string accountName, bool success, string message, string reason)
         private static void saveCouponCache(string channel, int accountId, string accountName, bool success, string message, string reason)
         {
         {
             saveAccountCouponCache("all", success, message, reason);
             saveAccountCouponCache("all", success, message, reason);
             saveAccountCouponCache($"{channel}", success, message, reason);
             saveAccountCouponCache($"{channel}", success, message, reason);
             if (accountId != 0)
             if (accountId != 0)
             {
             {
-                //todo 放着跑两天,要将读取的地方改成读取accountid
                 saveAccountCouponCache($"{channel}_{accountId}", success, message, reason);
                 saveAccountCouponCache($"{channel}_{accountId}", success, message, reason);
             }
             }
         }
         }

+ 5 - 1
molilian.core/Core/taoke/TkPoolCore.cs

@@ -20,7 +20,8 @@ namespace molilian.core
         {
         {
             all,
             all,
             parse,
             parse,
-            coupon
+            coupon,
+            promotionQuery
         }
         }
 
 
         private static string _end_point;
         private static string _end_point;
@@ -89,6 +90,9 @@ namespace molilian.core
                 case TkAction.parse:
                 case TkAction.parse:
                     if (!item.enable_parse) return false;
                     if (!item.enable_parse) return false;
                     break;
                     break;
+                case TkAction.promotionQuery:
+                    if (!item.enable_promotionQuery) return false;
+                    break;
                 case TkAction.coupon:
                 case TkAction.coupon:
                     if (!item.enable_coupon) return false;
                     if (!item.enable_coupon) return false;
                     break;
                     break;

+ 29 - 1
molilian.core/Core/taoke/UnionParseCore.cs

@@ -137,7 +137,7 @@ namespace molilian.core
             int accountid = 0)
             int accountid = 0)
         {
         {
             AlimamaPlus alimama = null;
             AlimamaPlus alimama = null;
-            content = content.UrlDecode();
+            //content = content.UrlDecode();
             content = content[..Math.Min(1000, content.Length)];
             content = content[..Math.Min(1000, content.Length)];
             var result = AlimamaPlus.GetFormattedObject(content, ip, oaid);
             var result = AlimamaPlus.GetFormattedObject(content, ip, oaid);
 
 
@@ -201,6 +201,11 @@ namespace molilian.core
                     .Where(line => !string.IsNullOrWhiteSpace(line))
                     .Where(line => !string.IsNullOrWhiteSpace(line))
                     .ToList();
                     .ToList();
 
 
+
+                var arr3 = config.multi_token_regular.Split('\n')
+                    .Where(line => !string.IsNullOrWhiteSpace(line))
+                    .ToList();
+
                 bool is_tao_url = !string.IsNullOrEmpty(result.shortLinkurl);
                 bool is_tao_url = !string.IsNullOrEmpty(result.shortLinkurl);
                 bool is_tao_token = AlimamaPlus.MatchRegexes(content, arr);
                 bool is_tao_token = AlimamaPlus.MatchRegexes(content, arr);
                 bool is_other = AlimamaPlus.MatchOtherInfo(content, arr2);
                 bool is_other = AlimamaPlus.MatchOtherInfo(content, arr2);
@@ -227,6 +232,29 @@ namespace molilian.core
                     });
                     });
                 }
                 }
 
 
+
+                bool is_multi_token = AlimamaPlus.MatchMultiToken(content, arr3);
+                //============================== 放弃转链-初步筛选1.5 ==============================
+                if (is_multi_token)
+                {
+                    //没有链接都放弃
+                    result.success = false;
+                    result.message = "放弃转链";
+                    result.reason = "初步筛选1.5";
+                    result.itemName = "";
+                    result.deeplink_url = "";
+                    _ = TkLogCore.ParseLogAsync(result);
+
+                    return TaobaoParseOutput(result, swData, new
+                    {
+                        result.success,
+                        result.message,
+                        result.content,
+                        result.itemName,
+                        result.deeplink_url,
+                    });
+                }
+
                 //============================== 放弃转链-初步筛选2 ==============================
                 //============================== 放弃转链-初步筛选2 ==============================
                 bool is_aff_link = false;
                 bool is_aff_link = false;
                 if (!string.IsNullOrEmpty(result.shortLinkurl)) is_aff_link = AlimamaPlus.IsAffLink(result.shortLinkurl);
                 if (!string.IsNullOrEmpty(result.shortLinkurl)) is_aff_link = AlimamaPlus.IsAffLink(result.shortLinkurl);

+ 52 - 0
molilian.core/DTO/alimama/PromotionQueryDTO.cs

@@ -0,0 +1,52 @@
+
+using dodohold.core;
+
+namespace molilian.core
+{
+    [Table("tk_promotion_log")]
+    public class PromotionQueryDTO
+    {
+        [Key]
+        public long id { get; set; }
+        public int accountId { get; set; } = 0;
+        public string accountName { get; set; } = string.Empty;
+        public string img { get; set; } = string.Empty;
+        public string ip { get; set; } = string.Empty;
+        public string oaid { get; set; } = string.Empty;
+        public bool success { get; set; } = false;
+        public string message { get; set; } = string.Empty;
+        public string reason { get; set; } = string.Empty;
+        public string url { get; set; } = string.Empty;
+        public string similarPromotion { get; set; } = string.Empty;
+        public string promotionImg { get; set; } = string.Empty;
+
+        public int elapsedTime { get; set; } = 0;
+        public DateTime create_time { get; set; } = DateTime.Now;
+        public string end_point { get; set; } = string.Empty;
+
+        public List<PromotionQueryItemDTO> SimilarPromotion = [];
+        public List<PromotionQueryItemDTO> PromotionImg = [];
+    }
+
+    public class PromotionQueryItemDTO
+    {
+        public string h5_url { get; set; } = string.Empty;
+        public string deeplink_url { get; set; } = string.Empty;
+        /// <summary>
+        /// 店铺信息-卖家类型:1天猫,0淘宝,3淘特
+        /// </summary>
+        public string userType { get; set; } = string.Empty;
+        public string shopTitle { get; set; } = string.Empty;
+        public string itemId { get; set; } = string.Empty;
+        public string itemName { get; set; } = string.Empty;
+        public string pic { get; set; } = string.Empty;
+        public string price { get; set; } = string.Empty;
+        public string promotionPrice { get; set; } = string.Empty;
+        public string couponAmount { get; set; } = string.Empty;
+        public string couponEffectiveStartTime { get; set; } = string.Empty;
+        public string couponEffectiveEndTime { get; set; } = string.Empty;
+        public string sellCountStr { get; set; } = string.Empty;
+        public string provcity { get; set; } = string.Empty;
+
+    }
+}

+ 1 - 0
molilian.core/DTO/alimama/TkConfigDTO.cs

@@ -18,5 +18,6 @@ namespace molilian.core
         public int rt_max { get; set; } = 0;
         public int rt_max { get; set; } = 0;
         public string tk_whitelist_regular { get; set; } = string.Empty;
         public string tk_whitelist_regular { get; set; } = string.Empty;
         public string tk_blacklist_regular { get; set; } = string.Empty;
         public string tk_blacklist_regular { get; set; } = string.Empty;
+        public string multi_token_regular { get; set; } = string.Empty;
     }
     }
 }
 }

+ 1 - 0
molilian.core/DTO/alimama/TkDataDTO.cs

@@ -45,6 +45,7 @@ namespace molilian.core
         [IgnoreInsert, IgnoreUpdate]
         [IgnoreInsert, IgnoreUpdate]
         public string couponEffectiveEndTime { get; set; } = string.Empty;
         public string couponEffectiveEndTime { get; set; } = string.Empty;
         public string end_point { get; set; } = string.Empty;
         public string end_point { get; set; } = string.Empty;
+        public int subCode { get; set; } = 0;
 
 
     }
     }
 
 

+ 7 - 0
molilian.core/DTO/alimama/TkPoolDTO.cs

@@ -25,5 +25,12 @@
         public bool enable_fake_click { get; set; } = false;
         public bool enable_fake_click { get; set; } = false;
         public bool enable_parse { get; set; } = false;
         public bool enable_parse { get; set; } = false;
         public bool enable_coupon { get; set; } = false;
         public bool enable_coupon { get; set; } = false;
+        public bool enable_sync_order { get; set; } = false;
+        public bool enable_promotionQuery { get; set; } = false;
+        
+
+        public string appkey { get; set; } = string.Empty;
+        public string appSecret { get; set; } = string.Empty;
+        public string open_pid { get; set; } = string.Empty;
     }
     }
 }
 }

+ 11 - 0
molilian.core/DTO/alimama/TkReportDTO.cs

@@ -76,6 +76,7 @@
         /// </summary>
         /// </summary>
         public int eff_ord_num { get; set; } = 0;
         public int eff_ord_num { get; set; } = 0;
 
 
+
         /// <summary>
         /// <summary>
         /// 有效付款金额(元) (订单付款金额,减去订单确认收货前发生的退款金额)
         /// 有效付款金额(元) (订单付款金额,减去订单确认收货前发生的退款金额)
         /// </summary>
         /// </summary>
@@ -117,6 +118,16 @@
         public decimal order_ord_amt_14 { get; set; } = 0;
         public decimal order_ord_amt_14 { get; set; } = 0;
         public decimal order_ord_tfee_14 { get; set; } = 0;
         public decimal order_ord_tfee_14 { get; set; } = 0;
 
 
+        /// <summary>
+        /// 来自订单表的付款笔数
+        /// </summary>
+        public int order_ord_num { get; set; } = 0;
+        public int order_ord_num_3 { get; set; } = 0;
+        public int order_ord_num_12 { get; set; } = 0;
+        public int order_ord_num_13 { get; set; } = 0;
+        public int order_ord_num_14 { get; set; } = 0;
+
+
         public int total_count { get; set; } = 0;
         public int total_count { get; set; } = 0;
         public int abandon_count { get; set; } = 0;
         public int abandon_count { get; set; } = 0;
         public int success_count { get; set; } = 0;
         public int success_count { get; set; } = 0;

+ 0 - 0
molilian.core/DTO/UnionCouponDTO.cs → molilian.core/DTO/alimama/UnionCouponDTO.cs


+ 1 - 0
molilian.core/DTO/dy/DyDataDTO.cs

@@ -28,5 +28,6 @@
         public int elapsedTime { get; set; } = 0;
         public int elapsedTime { get; set; } = 0;
         public DateTime create_time { get; set; } = DateTime.Now;
         public DateTime create_time { get; set; } = DateTime.Now;
         public string end_point { get; set; } = string.Empty;
         public string end_point { get; set; } = string.Empty;
+        public int subCode { get; set; } = 0;
     }
     }
 }
 }

+ 1 - 0
molilian.core/DTO/jd/JdDataDTO.cs

@@ -28,6 +28,7 @@
         public int elapsedTime { get; set; } = 0;
         public int elapsedTime { get; set; } = 0;
         public DateTime create_time { get; set; } = DateTime.Now;
         public DateTime create_time { get; set; } = DateTime.Now;
         public string end_point { get; set; } = string.Empty;
         public string end_point { get; set; } = string.Empty;
+        public int subCode { get; set; } = 0;
     }
     }
 
 
 }
 }

+ 198 - 68
molilian.core/Plus/Alimama/orders.cs

@@ -5,6 +5,7 @@ using System.Drawing.Printing;
 using System.Runtime.InteropServices;
 using System.Runtime.InteropServices;
 using System.Text.Json;
 using System.Text.Json;
 using System.Text.RegularExpressions;
 using System.Text.RegularExpressions;
+using static System.Runtime.InteropServices.JavaScript.JSType;
 
 
 namespace molilian.core
 namespace molilian.core
 {
 {
@@ -12,23 +13,99 @@ namespace molilian.core
     {
     {
         private const string base_orders_url = "https://pub.alimama.com/openapi/param2/1/gateway.unionpub/report.getTbkOrderDetails.json";
         private const string base_orders_url = "https://pub.alimama.com/openapi/param2/1/gateway.unionpub/report.getTbkOrderDetails.json";
 
 
-        public int GetHistoryOrders(int sleep)
+        public int GetHistoryOrders(int sleep, DateTime startTime, DateTime endTime)
         {
         {
-            DateTime endTime = DateTime.Now;
-            DateTime startTime = endTime.AddDays(-90);
+            //DateTime endTime = DateTime.Now;
+            //DateTime startTime = endTime.AddDays(-90);
+            //if (_accountId == 3) endTime = DateTime.Parse("2024-04-07");
+#if DEBUG
 
 
-            ////找出数据库最新的一天
-            //var last = new DBContext.Table("tk_order_details")
-            //     .Order("modifiedTime DESC")
-            //     .Get<TkOrderDetailDTO>("accountId=@accountId", new { accountId = _accountId });
+            startTime = DateTime.Parse("2024-03-21");
+            endTime = DateTime.Parse("2024-06-01");
+
+
+            int total = 0;
+
+            List<DateTime> orderTimes = new List<DateTime>();
+
+            for (DateTime date = startTime; date <= endTime; date = date.AddDays(1))
+            {
+                orderTimes.Add(date);
+            }
 
 
-            //if (last != null)
-            //{
-            //    startTime = last.modifiedTime;
-            //}
 
 
+#else
             //当日往前查询
             //当日往前查询
-            (int total, _, _) = GetTkOrders(startTime, endTime, DateTime.MinValue, false, sleep);
+            (int total, DateTime max_modifiedTime, List<DateTime> orderTimes) = GetTkOrders(startTime, endTime, DateTime.MinValue, false, sleep);
+
+
+#endif       
+
+            string sql = @"
+UPDATE tk_report tr
+JOIN (
+    SELECT
+        accountId, DATE(tbPaidTime) AS date, SUM(alipayTotalPrice) AS order_ord_amt,
+        COUNT(*) AS order_ord_num, SUM(pubShareFeeForCommission) AS order_ord_tfee
+    FROM tk_order_details
+    WHERE DATE(tbPaidTime) IN @orderTimes AND tkStatus=3 AND accountId=@accountId
+    GROUP BY DATE(tbPaidTime), accountId
+) AS sub
+ON tr.accountId = sub.accountId AND tr.report_date = sub.date
+SET tr.order_ord_amt = sub.order_ord_amt, tr.order_ord_num_3 = sub.order_ord_num, 
+    tr.order_ord_tfee = sub.order_ord_tfee;";
+            DBContext.Execute(sql, new { accountId = _accountId, orderTimes });
+
+            sql = @"
+UPDATE tk_report tr
+JOIN (
+    SELECT
+        accountId, DATE(tbPaidTime) AS date, SUM(alipayTotalPrice) AS order_ord_amt, 
+        COUNT(*) AS order_ord_num, SUM(pubSharePreFeeForCommission) AS order_ord_tfee
+    FROM tk_order_details
+    WHERE DATE(tbPaidTime) IN @orderTimes AND tkStatus=12 AND accountId=@accountId
+    GROUP BY DATE(tbPaidTime), accountId
+) AS sub
+ON tr.accountId = sub.accountId AND tr.report_date = sub.date
+SET tr.order_ord_amt_12 = sub.order_ord_amt, tr.order_ord_num_12 = sub.order_ord_num, 
+    tr.order_ord_tfee_12 = sub.order_ord_tfee;";
+            DBContext.Execute(sql, new { accountId = _accountId, orderTimes });
+
+            sql = @"
+UPDATE tk_report tr
+JOIN (
+    SELECT
+        accountId, DATE(tbPaidTime) AS date, SUM(alipayTotalPrice) AS order_ord_amt, 
+        COUNT(*) AS order_ord_num, SUM(pubSharePreFeeForCommission) AS order_ord_tfee
+    FROM tk_order_details
+    WHERE DATE(tbPaidTime) IN @orderTimes AND tkStatus=13 AND accountId=@accountId
+    GROUP BY DATE(tbPaidTime), accountId
+) AS sub
+ON tr.accountId = sub.accountId AND tr.report_date = sub.date
+SET tr.order_ord_amt_13 = sub.order_ord_amt, tr.order_ord_num_13 = sub.order_ord_num, 
+    tr.order_ord_tfee_13 = sub.order_ord_tfee;";
+            DBContext.Execute(sql, new { accountId = _accountId, orderTimes });
+
+            sql = @"
+UPDATE tk_report tr
+JOIN (
+    SELECT
+        accountId, DATE(tbPaidTime) AS date, SUM(alipayTotalPrice) AS order_ord_amt, 
+        COUNT(*) AS order_ord_num, SUM(pubSharePreFeeForCommission) AS order_ord_tfee
+    FROM tk_order_details
+    WHERE DATE(tbPaidTime) IN @orderTimes AND tkStatus=14 AND accountId=@accountId
+    GROUP BY DATE(tbPaidTime), accountId
+) AS sub
+ON tr.accountId = sub.accountId AND tr.report_date = sub.date
+SET tr.order_ord_amt_14 = sub.order_ord_amt, tr.order_ord_num_14 = sub.order_ord_num, 
+    tr.order_ord_tfee_14 = sub.order_ord_tfee;";
+            DBContext.Execute(sql, new { accountId = _accountId, orderTimes });
+
+            sql = @"UPDATE tk_report 
+SET  order_ord_num = order_ord_num_3 + order_ord_num_12 + order_ord_num_13 + order_ord_num_14
+ WHERE DATE(report_date) IN @orderTimes AND accountId=@accountId;";
+            DBContext.Execute(sql, new { accountId = _accountId, orderTimes });
+
             return total;
             return total;
 
 
         }
         }
@@ -62,75 +139,72 @@ namespace molilian.core
                 RedisHelper.Set(cacheKey, max_modifiedTime);
                 RedisHelper.Set(cacheKey, max_modifiedTime);
             }
             }
 
 
-            //todo 根据orderTimes、settlementTimes 更新被影响的相关行
-            /*		
-		
-UPDATE tk_report tr
-JOIN (
-    SELECT
-        accountId,
-        DATE(tbPaidTime) AS date,
-        SUM(alipayTotalPrice) AS order_ord_amt,
-        SUM(pubShareFeeForCommission) AS order_ord_tfee
-    FROM tk_order_details
-    WHERE  tkStatus=3
-    GROUP BY DATE(tbPaidTime), accountId
-) AS sub
-ON tr.accountId = sub.accountId AND tr.report_date = sub.date
-SET tr.order_ord_amt = sub.order_ord_amt,
-    tr.order_ord_tfee = sub.order_ord_tfee;
-             */
+            //todo 根据orderTimes、settlementTimes 更新被影响的相关行 
+
             string sql = @"
             string sql = @"
 UPDATE tk_report tr
 UPDATE tk_report tr
 JOIN (
 JOIN (
     SELECT
     SELECT
-        accountId, DATE(tbPaidTime) AS date, SUM(alipayTotalPrice) AS order_ord_amt, SUM(pubShareFeeForCommission) AS order_ord_tfee
+        accountId, DATE(tbPaidTime) AS date, SUM(alipayTotalPrice) AS order_ord_amt,
+        COUNT(*) AS order_ord_num, SUM(pubShareFeeForCommission) AS order_ord_tfee
     FROM tk_order_details
     FROM tk_order_details
-    WHERE DATE(tbPaidTime) IN @orderTimes AND tkStatus=3
+    WHERE DATE(tbPaidTime) IN @orderTimes AND tkStatus=3 AND accountId=@accountId
     GROUP BY DATE(tbPaidTime), accountId
     GROUP BY DATE(tbPaidTime), accountId
 ) AS sub
 ) AS sub
 ON tr.accountId = sub.accountId AND tr.report_date = sub.date
 ON tr.accountId = sub.accountId AND tr.report_date = sub.date
-SET tr.order_ord_amt = sub.order_ord_amt, tr.order_ord_tfee = sub.order_ord_tfee;";
-            DBContext.Execute(sql, new { orderTimes });
+SET tr.order_ord_amt = sub.order_ord_amt, tr.order_ord_num_3 = sub.order_ord_num, 
+    tr.order_ord_tfee = sub.order_ord_tfee;";
+            DBContext.Execute(sql, new { accountId = _accountId, orderTimes });
 
 
             sql = @"
             sql = @"
 UPDATE tk_report tr
 UPDATE tk_report tr
 JOIN (
 JOIN (
     SELECT
     SELECT
-        accountId, DATE(tbPaidTime) AS date, SUM(alipayTotalPrice) AS order_ord_amt, SUM(pubSharePreFeeForCommission) AS order_ord_tfee
+        accountId, DATE(tbPaidTime) AS date, SUM(alipayTotalPrice) AS order_ord_amt, 
+        COUNT(*) AS order_ord_num, SUM(pubSharePreFeeForCommission) AS order_ord_tfee
     FROM tk_order_details
     FROM tk_order_details
-    WHERE DATE(tbPaidTime) IN @orderTimes AND tkStatus=12
+    WHERE DATE(tbPaidTime) IN @orderTimes AND tkStatus=12 AND accountId=@accountId
     GROUP BY DATE(tbPaidTime), accountId
     GROUP BY DATE(tbPaidTime), accountId
 ) AS sub
 ) AS sub
 ON tr.accountId = sub.accountId AND tr.report_date = sub.date
 ON tr.accountId = sub.accountId AND tr.report_date = sub.date
-SET tr.order_ord_amt_12 = sub.order_ord_amt, tr.order_ord_tfee_12 = sub.order_ord_tfee;";
-            DBContext.Execute(sql, new { orderTimes });
+SET tr.order_ord_amt_12 = sub.order_ord_amt, tr.order_ord_num_12 = sub.order_ord_num, 
+    tr.order_ord_tfee_12 = sub.order_ord_tfee;";
+            DBContext.Execute(sql, new { accountId = _accountId, orderTimes });
 
 
             sql = @"
             sql = @"
 UPDATE tk_report tr
 UPDATE tk_report tr
 JOIN (
 JOIN (
     SELECT
     SELECT
-        accountId, DATE(tbPaidTime) AS date, SUM(alipayTotalPrice) AS order_ord_amt, SUM(pubSharePreFeeForCommission) AS order_ord_tfee
+        accountId, DATE(tbPaidTime) AS date, SUM(alipayTotalPrice) AS order_ord_amt, 
+        COUNT(*) AS order_ord_num, SUM(pubSharePreFeeForCommission) AS order_ord_tfee
     FROM tk_order_details
     FROM tk_order_details
-    WHERE DATE(tbPaidTime) IN @orderTimes AND tkStatus=13
+    WHERE DATE(tbPaidTime) IN @orderTimes AND tkStatus=13 AND accountId=@accountId
     GROUP BY DATE(tbPaidTime), accountId
     GROUP BY DATE(tbPaidTime), accountId
 ) AS sub
 ) AS sub
 ON tr.accountId = sub.accountId AND tr.report_date = sub.date
 ON tr.accountId = sub.accountId AND tr.report_date = sub.date
-SET tr.order_ord_amt_13 = sub.order_ord_amt, tr.order_ord_tfee_13 = sub.order_ord_tfee;";
-            DBContext.Execute(sql, new { orderTimes });
+SET tr.order_ord_amt_13 = sub.order_ord_amt, tr.order_ord_num_13 = sub.order_ord_num, 
+    tr.order_ord_tfee_13 = sub.order_ord_tfee;";
+            DBContext.Execute(sql, new { accountId = _accountId, orderTimes });
 
 
             sql = @"
             sql = @"
 UPDATE tk_report tr
 UPDATE tk_report tr
 JOIN (
 JOIN (
     SELECT
     SELECT
-        accountId, DATE(tbPaidTime) AS date, SUM(alipayTotalPrice) AS order_ord_amt, SUM(pubSharePreFeeForCommission) AS order_ord_tfee
+        accountId, DATE(tbPaidTime) AS date, SUM(alipayTotalPrice) AS order_ord_amt, 
+        COUNT(*) AS order_ord_num, SUM(pubSharePreFeeForCommission) AS order_ord_tfee
     FROM tk_order_details
     FROM tk_order_details
-    WHERE DATE(tbPaidTime) IN @orderTimes AND tkStatus=14
+    WHERE DATE(tbPaidTime) IN @orderTimes AND tkStatus=14 AND accountId=@accountId
     GROUP BY DATE(tbPaidTime), accountId
     GROUP BY DATE(tbPaidTime), accountId
 ) AS sub
 ) AS sub
 ON tr.accountId = sub.accountId AND tr.report_date = sub.date
 ON tr.accountId = sub.accountId AND tr.report_date = sub.date
-SET tr.order_ord_amt_14 = sub.order_ord_amt, tr.order_ord_tfee_14 = sub.order_ord_tfee;";
-            DBContext.Execute(sql, new { orderTimes });
+SET tr.order_ord_amt_14 = sub.order_ord_amt, tr.order_ord_num_14 = sub.order_ord_num, 
+    tr.order_ord_tfee_14 = sub.order_ord_tfee;";
+            DBContext.Execute(sql, new { accountId = _accountId, orderTimes });
+
+            sql = @"UPDATE tk_report 
+SET  order_ord_num = order_ord_num_3 + order_ord_num_12 + order_ord_num_13 + order_ord_num_14
+ WHERE DATE(report_date) IN @orderTimes AND accountId=@accountId;";
+            DBContext.Execute(sql, new { accountId = _accountId, orderTimes });
 
 
             return total;
             return total;
         }
         }
@@ -155,34 +229,88 @@ SET tr.order_ord_amt_14 = sub.order_ord_amt, tr.order_ord_tfee_14 = sub.order_or
             int total = 0;
             int total = 0;
             while (hasNext)
             while (hasNext)
             {
             {
-                var ts = DateTime.Now.Convert2UnixTimestamp();
-                //string jumpType = pageNo == 1 ? "0" : desc ? "1" : "-1";
-                string jumpType = desc ? pageNo == 1 ? "0" : "1" : "-1";
-                //string queryType = "1";
-                string queryType = "4";
-                string url = $"{base_orders_url}?t={ts}&_tb_token_={_tb_token}&pageNo={pageNo}&pageSize={pageSize}&startTime={startTime:yyyy-MM-dd}&endTime={endTime:yyyy-MM-dd}&payStatus=&queryType={queryType}&jumpType={jumpType}&tkTradeId=&positionIndex={positionIndex}";
+                var ts = DateTime.Now.Convert2UnixTimestamp(true);
+                //string jumpType = desc ? pageNo == 1 ? "0" : "1" : "-1";
+                string jumpType = pageNo == 1 ? "0" : "1";
 
 
-                var client = new WebClientUtility().SetContentType("application/json;charset=utf-8")
-                          .AddHeaders("X-Requested-With", "XMLHttpRequest")
-                          .AddHeaders("Cookie", _cookies);
 #if DEBUG
 #if DEBUG
-#else
-                client.Proxy = _proxy;
+                jumpType = pageNo == 1 ? "0" : "1";
+
+
 #endif
 #endif
-                if (!string.IsNullOrEmpty(_user_agent)) client.UserAgent = _user_agent;
-                var response = client.Request(url);
+                //string queryType = "1";
+                //1 创建时间;3结算时间 2付款时间 4更新时间
+                //string queryType = "2";
+                string queryType = desc ? "4" : "2";
+
+                string url = $"{base_orders_url}?t={ts}&_tb_token_={_tb_token}&pageNo={pageNo}&pageSize={pageSize}&startTime={startTime:yyyy-MM-dd}&endTime={endTime:yyyy-MM-dd}&payStatus=&queryType={queryType}&jumpType={jumpType}&tkTradeId=&positionIndex={positionIndex.UrlEncode()}";
+
+                //https://pub.alimama.com/openapi/param2/1/gateway.unionpub/report.getTbkOrderDetails.json?t=1719477837298&_tb_token_=7537e7ed93338&pageNo=92&pageSize=20&startTime=2024-03-31&endTime=2024-03-31&payStatus=&queryType=2&jumpType=1&tkTradeId=&positionIndex=1711883373_2vtPPr7CyYX2%7C1711883522_4zcICUeUevh2
+                //https://pub.alimama.com/openapi/param2/1/gateway.unionpub/report.getTbkOrderDetails.json?t=1719477942492&_tb_token_=7537e7ed93338&pageNo=7&pageSize=20&startTime=2024-03-31&endTime=2024-03-31&payStatus=&queryType=2&jumpType=1&tkTradeId=&positionIndex=1711899163_4zdpOH1mbTQ2%7C1711899483_4zbH9BHqgl82
+
+
+                string body = "";
 
 
-                if (response.ResponseException != null)
-                {
-                    _ = new LoggerLibrary("api_error", "fail")
-                        .Info(response.ResponseException.Message, response.ResponseException.StackTrace)
-                        .SaveAsync();
-                    throw response.ResponseException;
-                }
-                var body = response.Body();
                 JsonElement data;
                 JsonElement data;
                 try
                 try
                 {
                 {
+                    for (var i = 1; i <= 5; i++)
+                    {
+                        var client = new WebClientUtility().SetContentType("application/json;charset=utf-8")
+                              .AddHeaders("X-Requested-With", "XMLHttpRequest")
+                              .AddHeaders("Cookie", _cookies);
+#if DEBUG
+#else
+                        client.Proxy = _proxy;
+#endif
+                        if (!string.IsNullOrEmpty(_user_agent)) client.UserAgent = _user_agent;
+                        var response = client.Request(url);
+
+                        if (response.ResponseException != null)
+                        {
+                            _ = new LoggerLibrary("api_error", "fail")
+                                .Info(response.ResponseException.Message, response.ResponseException.StackTrace)
+                                .SaveAsync();
+                            throw response.ResponseException;
+                        }
+                        body = response.Body();
+#if DEBUG
+                        _ = new LoggerLibrary("debug", "GetTkOrders")
+                            .Info(body)
+                            .SaveAsync();
+#endif
+                        if (body.Contains("{\"action\":\"captcha\""))
+                        {
+                            string message = $"【GetTkOrders】【{_accountId}:{_accountName}】第 {i} 次出现滑动验证码";
+                            NotifyCore.Notify(new NifyMessage
+                            {
+                                message = message,
+                                priority = NifyMessagePriority.high,
+                                tags = ["red_circle"]
+                            });
+                            Thread.Sleep(i * 60 * 1000);
+                            continue;
+                        }
+
+                        if (body.Contains("\"resultCode\":500"))
+                        {
+                            //{"success":false,"resultCode":500,"bizErrorDesc":"系统繁忙,请稍后重试!","bizErrorCode":3001}
+
+                            string message = $"【GetTkOrders】【{_accountId}:{_accountName}】第 {i} 次出现错误\n{body}";
+                            NotifyCore.Notify(new NifyMessage
+                            {
+                                message = message,
+                                priority = NifyMessagePriority.high,
+                                tags = ["red_circle"]
+                            });
+                            Thread.Sleep(i * 60 * 1000);
+                            continue;
+                        }
+                        break;
+                    }
+
+                    /*
+<script>sessionStorage.x5referer = window.location.href;var url = window.location.protocol + "//pub.alimama.com//openapi/param2/1/gateway.unionpub/report.getTbkOrderDetails.json/_____tmd_____/punish?x5secdata=xc1iwR0XfHknuQ2tGhtadtTAvEt14YScfTDHt2ZiSRqroaYZK98uykgsVOy7o75Rw8uuycKT3Rpx30%2fB5H0gRc0%2fJVGQUcV5g%2fsAmZ%2bc46eyD7DXSdA4R3mLnmtbp9wuZ1vmrG3ZPlH7brUilFbXBdZ7EBrru1X7wPfC05Eq3g4mhDFiGq39xUtwQcC%2f%2bPbtekcm8%2fVJ%2brVt8aExunwzXzJPJJTAvEt14YScfTDHt2ZiSRqroaYZK98uykgsiXIkw%2ffMWwebJ3zxCja5CzSu%2ffTA%3d%3d__bx__pub.alimama.com%2fopenapi%2fparam2%2f1%2fgateway.unionpub%2freport.getTbkOrderDetails.json&x5step=1";window.location.replace(url);window._config_ = {"action":"captcha","url":"https://pub.alimama.com//openapi/param2/1/gateway.unionpub/report.getTbkOrderDetails.json/_____tmd_____/punish?x5secdata=xc1iwR0XfHknuQ2tGhtadtTAvEt14YScfTDHt2ZiSRqroaYZK98uykgsVOy7o75Rw8uuycKT3Rpx30%2fB5H0gRc0%2fJVGQUcV5g%2fsAmZ%2bc46eyD7DXSdA4R3mLnmtbp9wuZ1vmrG3ZPlH7brUilFbXBdZ7EBrru1X7wPfC05Eq3g4mhDFiGq39xUtwQcC%2f%2bPbtekcm8%2fVJ%2brVt8aExunwzXzJPJJTAvEt14YScfTDHt2ZiSRqroaYZK98uykgsiXIkw%2ffMWwebJ3zxCja5CzSu%2ffTA%3d%3d__bx__pub.alimama.com%2fopenapi%2fparam2%2f1%2fgateway.unionpub%2freport.getTbkOrderDetails.json&x5step=1"};</script><!--rgv587_flag:sm-->                     */
                     var root = body.Convert2JsonElement();
                     var root = body.Convert2JsonElement();
                     var success = root.Read<bool>("success");
                     var success = root.Read<bool>("success");
                     data = root.ElementRead("data");
                     data = root.ElementRead("data");
@@ -213,6 +341,7 @@ SET tr.order_ord_amt_14 = sub.order_ord_amt, tr.order_ord_tfee_14 = sub.order_or
 
 
                 try
                 try
                 {
                 {
+
                     foreach (var order in data.ElementRead("result").EnumerateArray())
                     foreach (var order in data.ElementRead("result").EnumerateArray())
                     {
                     {
                         TkOrderDetailDTO item = order.GetRawText().Convert2Object<TkOrderDetailDTO>();
                         TkOrderDetailDTO item = order.GetRawText().Convert2Object<TkOrderDetailDTO>();
@@ -227,6 +356,7 @@ SET tr.order_ord_amt_14 = sub.order_ord_amt, tr.order_ord_tfee_14 = sub.order_or
                         {
                         {
                             return (total, max_modifiedTime, orderTimes);
                             return (total, max_modifiedTime, orderTimes);
                         }
                         }
+
                         //进行订单和转链匹配
                         //进行订单和转链匹配
                         item.itemId = GetRawItemId(item.mktId);
                         item.itemId = GetRawItemId(item.mktId);
                         if (item.tkStatus == 3 && item.tbPaidTime != DateTime.MinValue) orderTimes.Add(item.tbPaidTime.Date);
                         if (item.tkStatus == 3 && item.tbPaidTime != DateTime.MinValue) orderTimes.Add(item.tbPaidTime.Date);
@@ -292,7 +422,7 @@ SET tr.order_ord_amt_14 = sub.order_ord_amt, tr.order_ord_tfee_14 = sub.order_or
                 if (!string.IsNullOrEmpty(_user_agent)) client.UserAgent = _user_agent;
                 if (!string.IsNullOrEmpty(_user_agent)) client.UserAgent = _user_agent;
                 var response = client.Request(url);
                 var response = client.Request(url);
                 if (!response.Successed)
                 if (!response.Successed)
-                { 
+                {
                     throw response.ResponseException;
                     throw response.ResponseException;
                 }
                 }
 
 

+ 65 - 13
molilian.core/Plus/Alimama/parse.cs

@@ -28,6 +28,8 @@ namespace molilian.core
         private static string _end_point;
         private static string _end_point;
         static Random _random = new Random();
         static Random _random = new Random();
 
 
+        public static string _url_pattern = @"https?://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]";
+
         static AlimamaPlus()
         static AlimamaPlus()
         {
         {
             _end_point = Environment.GetEnvironmentVariable("EndPoint");
             _end_point = Environment.GetEnvironmentVariable("EndPoint");
@@ -150,10 +152,8 @@ namespace molilian.core
         {
         {
             if (string.IsNullOrEmpty(content) || string.IsNullOrEmpty(shortLink)) return content;
             if (string.IsNullOrEmpty(content) || string.IsNullOrEmpty(shortLink)) return content;
             string result = content;
             string result = content;
-            //string pattern = @"https?:\/\/?([\da-z\.-]+)\.([a-z]{2,6})(:\d{1,5})?([\/\w\.-]*)*\/?(#[\S]+)?(\?[\S]+)?";
-            string pattern = @"https?://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]";
 
 
-            Regex regex = new(pattern);
+            Regex regex = new(_url_pattern);
             MatchCollection matches = regex.Matches(content);
             MatchCollection matches = regex.Matches(content);
 
 
             foreach (Match m in matches.Cast<Match>())
             foreach (Match m in matches.Cast<Match>())
@@ -170,20 +170,39 @@ namespace molilian.core
             return result;
             return result;
         }
         }
 
 
-        public static string GetLink(string content)
+
+
+        public static string RemoveUrls(string content)
         {
         {
             if (string.IsNullOrEmpty(content)) return content;
             if (string.IsNullOrEmpty(content)) return content;
             string result = content;
             string result = content;
+
+            Regex regex = new(_url_pattern);
+            MatchCollection matches = regex.Matches(content);
+
+            foreach (Match m in matches.Cast<Match>())
+            {
+                string url = m.Value;
+                result = result.Replace(url, string.Empty);
+
+            }
+            return result;
+        }
+
+        public static string GetLink(string content)
+        {
+            if (string.IsNullOrEmpty(content)) return content;
             //string pattern = @"https?:\/\/?([\da-z\.-]+)\.([a-z]{2,6})(:\d{1,5})?([\/\w\.-]*)*\/?(#[\S]+)?(\?[\S]+)?";
             //string pattern = @"https?:\/\/?([\da-z\.-]+)\.([a-z]{2,6})(:\d{1,5})?([\/\w\.-]*)*\/?(#[\S]+)?(\?[\S]+)?";
-            string pattern = @"https?://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]";
-            Regex regex = new(pattern);
+            Regex regex = new(_url_pattern);
             MatchCollection matches = regex.Matches(content);
             MatchCollection matches = regex.Matches(content);
 
 
+            string result = string.Empty;
             if (matches.Count > 0)
             if (matches.Count > 0)
             {
             {
-                return matches[^1].Value; // 返回第一个匹配到的 URL
+                result = matches[^1].Value; // 返回第一个匹配到的 URL
             }
             }
-            return string.Empty;
+            result = result.Replace("&nbsp;", "");
+            return result;
         }
         }
 
 
 
 
@@ -192,7 +211,7 @@ namespace molilian.core
             if (string.IsNullOrEmpty(content)) return content;
             if (string.IsNullOrEmpty(content)) return content;
             string result = content;
             string result = content;
             string pattern = @"(https?://(?:[\w-]+\.)*(?:tb\.cn|taobao\.com|juhuasuan\.com|tmall\.com|tmall\.hk)(?:/[a-zA-Z0-9\-\._~:/?#[\]@!$&'()*+,;=%]*)?)";
             string pattern = @"(https?://(?:[\w-]+\.)*(?:tb\.cn|taobao\.com|juhuasuan\.com|tmall\.com|tmall\.hk)(?:/[a-zA-Z0-9\-\._~:/?#[\]@!$&'()*+,;=%]*)?)";
-            Regex regex = new(pattern);
+            Regex regex = new(pattern, RegexOptions.IgnoreCase);
             MatchCollection matches = regex.Matches(content);
             MatchCollection matches = regex.Matches(content);
 
 
             if (matches.Count > 0)
             if (matches.Count > 0)
@@ -289,6 +308,7 @@ namespace molilian.core
 
 
         public static bool MatchRegexes(string text, List<string> extended)
         public static bool MatchRegexes(string text, List<string> extended)
         {
         {
+            text = RemoveUrls(text);
             string special_symbols = $"\\$\\(\\)\\/\\#\\&\\<\\>฿¢¥¥$🗝₰₵₲¤₪₴€₤£₳《¢💰🔑🎵✔💲🔐📲";
             string special_symbols = $"\\$\\(\\)\\/\\#\\&\\<\\>฿¢¥¥$🗝₰₵₲¤₪₴€₤£₳《¢💰🔑🎵✔💲🔐📲";
 
 
             string[] base_patterns =
             string[] base_patterns =
@@ -297,7 +317,8 @@ namespace molilian.core
                 $@"[{special_symbols}]+\s*([a-zA-Z]{{1,2}}\d{{2,4}}\s[a-zA-Z0-9]{{11}}|[a-zA-Z0-9]{{11}}[a-zA-Z]{{1,2}}\d{{2,4}}\s|[a-zA-Z0-9]{{11}})\s*[{special_symbols}]+",
                 $@"[{special_symbols}]+\s*([a-zA-Z]{{1,2}}\d{{2,4}}\s[a-zA-Z0-9]{{11}}|[a-zA-Z0-9]{{11}}[a-zA-Z]{{1,2}}\d{{2,4}}\s|[a-zA-Z0-9]{{11}})\s*[{special_symbols}]+",
                 @"🍑♭ɑ◑下单|Tao宝"
                 @"🍑♭ɑ◑下单|Tao宝"
             ];
             ];
-            List<string> mergedArray = base_patterns.Union(extended).ToList();
+
+            List<string> mergedArray = extended.Union(base_patterns).ToList();
 
 
             /*
             /*
             (.*?\\d{2}\\s[a-zA-Z]{2}\\d{4}\\s.*?[\\$\\(\\)\\/\\#\\&\\<\\>฿¢¥¥$🗝₰₵₲¤₪₴€₤£₳《¢💰🔑🎵✔💲🔐📲]+[a-zA-Z0-9\\s]+[\\$\\(\\)\\/\\#\\&\\<\\>฿¢¥¥$🗝₰₵₲¤₪₴€₤£₳《¢💰🔑🎵✔💲🔐📲])).*\n[\\$\\(\\)\\/\\#\\&\\<\\>฿¢¥¥$🗝₰₵₲¤₪₴€₤£₳《¢💰🔑🎵✔💲🔐📲]+\\s*([a-zA-Z]{1,2}\\d{2,4}\\s[a-zA-Z0-9]{11}|[a-zA-Z0-9]{11}[a-zA-Z]{1,2}\\d{2,4}\\s|[a-zA-Z0-9]{11})\\s*[\\$\\(\\)\\/\\#\\&\\<\\>฿¢¥¥$🗝₰₵₲¤₪₴€₤£₳《¢💰🔑🎵✔💲🔐📲]+\n🍑♭ɑ◑下单|Tao宝
             (.*?\\d{2}\\s[a-zA-Z]{2}\\d{4}\\s.*?[\\$\\(\\)\\/\\#\\&\\<\\>฿¢¥¥$🗝₰₵₲¤₪₴€₤£₳《¢💰🔑🎵✔💲🔐📲]+[a-zA-Z0-9\\s]+[\\$\\(\\)\\/\\#\\&\\<\\>฿¢¥¥$🗝₰₵₲¤₪₴€₤£₳《¢💰🔑🎵✔💲🔐📲])).*\n[\\$\\(\\)\\/\\#\\&\\<\\>฿¢¥¥$🗝₰₵₲¤₪₴€₤£₳《¢💰🔑🎵✔💲🔐📲]+\\s*([a-zA-Z]{1,2}\\d{2,4}\\s[a-zA-Z0-9]{11}|[a-zA-Z0-9]{11}[a-zA-Z]{1,2}\\d{2,4}\\s|[a-zA-Z0-9]{11})\\s*[\\$\\(\\)\\/\\#\\&\\<\\>฿¢¥¥$🗝₰₵₲¤₪₴€₤£₳《¢💰🔑🎵✔💲🔐📲]+\n🍑♭ɑ◑下单|Tao宝
@@ -324,7 +345,7 @@ namespace molilian.core
             bool anyMatch = false;
             bool anyMatch = false;
             foreach (string pattern in mergedArray)
             foreach (string pattern in mergedArray)
             {
             {
-                if (Regex.IsMatch(text, pattern))
+                if (Regex.IsMatch(text, pattern, RegexOptions.IgnoreCase))
                 {
                 {
                     anyMatch = true;
                     anyMatch = true;
                 }
                 }
@@ -349,12 +370,12 @@ namespace molilian.core
                 @"#腾讯会议:(\d{3}-\d{3}-\d{3})"
                 @"#腾讯会议:(\d{3}-\d{3}-\d{3})"
             ];
             ];
 
 
-            List<string> mergedArray = base_patterns.Union(extended).ToList();
+            List<string> mergedArray = extended.Union(base_patterns).ToList();
 
 
             bool anyMatch = false;
             bool anyMatch = false;
             foreach (string pattern in mergedArray)
             foreach (string pattern in mergedArray)
             {
             {
-                if (Regex.IsMatch(text, pattern))
+                if (Regex.IsMatch(text, pattern, RegexOptions.IgnoreCase))
                 {
                 {
                     anyMatch = true;
                     anyMatch = true;
                 }
                 }
@@ -362,6 +383,37 @@ namespace molilian.core
             return anyMatch;
             return anyMatch;
         }
         }
 
 
+        public static bool MatchMultiToken(string text, List<string> extended)
+        {
+            text = RemoveUrls(text);
+            string special_symbols = $"\\$\\(\\)\\/\\#\\&\\<\\>฿¢¥¥$🗝₰₵₲¤₪₴€₤£₳《¢💰🔑🎵✔💲🔐📲";
+            string[] base_patterns =
+            [
+                @"[!$¥%]+([a-zA-Z0-9]{16})[!$¥%]+",
+                @"\^v(\d+#[a-zA-Z0-9]+)(?:\?.*)?\^",
+                "##([a-zA-Z0-9\\-]+)##",
+                $@"(.*?\d{{2}}\s[a-zA-Z]{{2}}\d{{4}}\s.*?[{special_symbols}]+[a-zA-Z0-9\s]+[{special_symbols}]).*",
+                $@"[{special_symbols}]+\s*([a-zA-Z]{{1,2}}\d{{2,4}}\s[a-zA-Z0-9]{{11}}|[a-zA-Z0-9]{{11}}[a-zA-Z]{{1,2}}\d{{2,4}}\s|[a-zA-Z0-9]{{11}})\s*[{special_symbols}]+",
+            ];
+
+            // PAGE6:/^v130091#P2J6EDxN8p8?chanTag=d9dc522bdea27437514052ea50f6a787^  
+
+
+            List<string> mergedArray = extended.Union(base_patterns).ToList();
+            int matchCount = 0;
+
+            foreach (string pattern in mergedArray)
+            {
+                Match match = Regex.Match(text, pattern, RegexOptions.IgnoreCase);
+                if (match.Success)
+                {
+                    matchCount++;
+                    text = text.Replace(match.Groups[^1].Value, ""); // 移除匹配的部分文本
+                }
+            }
+            return matchCount >= 2;
+        }
+
         public static bool ShouldIgnoreRequest(string ip, string oaid, out string reason)
         public static bool ShouldIgnoreRequest(string ip, string oaid, out string reason)
         {
         {
             reason = string.Empty;
             reason = string.Empty;

+ 3 - 4
molilian.core/Plus/Alimama/parse_2.cs

@@ -479,10 +479,9 @@ namespace molilian.core
                     }
                     }
                 }
                 }
 
 
-                if ((result.content.Contains("")))
+                if ((result.content.Contains("霸下通用 web 页面-验证码")))
                 {
                 {
-                    //触发验证码
-
+                    result.reason = "霸下验证码";
                 }
                 }
 
 
                 link_type = GetLinkType(url);
                 link_type = GetLinkType(url);
@@ -519,7 +518,7 @@ namespace molilian.core
             result.link_type = LinkTypeEnum.other_aff;
             result.link_type = LinkTypeEnum.other_aff;
             result.shortLinkurl = null;
             result.shortLinkurl = null;
             return result;
             return result;
-        } 
+        }
 
 
         private LinkTypeEnum GetLinkType(string url)
         private LinkTypeEnum GetLinkType(string url)
         {
         {

+ 1 - 2
molilian.core/Plus/JDUnion/JdUnionPlus.cs

@@ -34,8 +34,7 @@ namespace molilian.core
             if (string.IsNullOrEmpty(content)) return content;
             if (string.IsNullOrEmpty(content)) return content;
             string result = content;
             string result = content;
             //string pattern = @"https?:\/\/?([\da-z\.-]+)\.([a-z]{2,6})(:\d{1,5})?([\/\w\.-]*)*\/?(#[\S]+)?(\?[\S]+)?";
             //string pattern = @"https?:\/\/?([\da-z\.-]+)\.([a-z]{2,6})(:\d{1,5})?([\/\w\.-]*)*\/?(#[\S]+)?(\?[\S]+)?";
-            string pattern = @"https?://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]";
-            Regex regex = new(pattern);
+             Regex regex = new(AlimamaPlus._url_pattern);
             MatchCollection matches = regex.Matches(content);
             MatchCollection matches = regex.Matches(content);
 
 
             foreach (Match m in matches.Cast<Match>())
             foreach (Match m in matches.Cast<Match>())

+ 1 - 2
molilian.core/Plus/Pangolin/DyUnion/DyUnionPlus.cs

@@ -104,8 +104,7 @@ namespace molilian.core
             if (string.IsNullOrEmpty(content)) return content;
             if (string.IsNullOrEmpty(content)) return content;
             string result = content;
             string result = content;
             //string pattern = @"https?:\/\/?([\da-z\.-]+)\.([a-z]{2,6})(:\d{1,5})?([\/\w\.-]*)*\/?(#[\S]+)?(\?[\S]+)?";
             //string pattern = @"https?:\/\/?([\da-z\.-]+)\.([a-z]{2,6})(:\d{1,5})?([\/\w\.-]*)*\/?(#[\S]+)?(\?[\S]+)?";
-            string pattern = @"https?://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]";
-            Regex regex = new(pattern);
+            Regex regex = new(AlimamaPlus._url_pattern);
             MatchCollection matches = regex.Matches(content);
             MatchCollection matches = regex.Matches(content);
 
 
             foreach (Match m in matches.Cast<Match>())
             foreach (Match m in matches.Cast<Match>())

+ 113 - 0
molilian.core/Plus/ProxyManager.cs

@@ -0,0 +1,113 @@
+using System;
+using System.Collections.Generic;
+using System.Net;
+using System.Threading.Tasks;
+using System.Linq;
+
+public class ProxyManager
+{
+    private List<ProxyInfo> proxyPool = new List<ProxyInfo>();
+    private object lockObj = new object();
+    private DateTime lastResetTime = DateTime.UtcNow;
+    private TimeSpan resetInterval = TimeSpan.FromHours(1); // 设置重置间隔为1小时
+
+    public ProxyManager(IEnumerable<string> proxyAddresses)
+    {
+        foreach (var address in proxyAddresses)
+        {
+            proxyPool.Add(new ProxyInfo(new WebProxy(address)));
+        }
+    }
+
+    public WebProxy GetNextProxy()
+    {
+        lock (lockObj)
+        {
+            if (DateTime.UtcNow - lastResetTime >= resetInterval) ResetCounters();
+            var nextProxy = proxyPool.OrderBy(p => p.UsageCount).FirstOrDefault(p => p.IsAvailable);
+
+            if (nextProxy != null)
+            {
+                nextProxy.UsageCount++;
+                return nextProxy.Proxy;
+            }
+            return null; // 没有可用的代理时返回 null
+        }
+    }
+
+    private void ResetCounters()
+    {
+        foreach (var proxyInfo in proxyPool)
+        {
+            proxyInfo.UsageCount = 0;
+        }
+        lastResetTime = DateTime.UtcNow;
+    }
+
+    public void ReportProxyResult(WebProxy proxy, bool isSuccess)
+    {
+        lock (lockObj)
+        {
+            var proxyInfo = proxyPool.Find(p => p.Proxy == proxy);
+            if (proxyInfo != null)
+            {
+                if (isSuccess)
+                {
+                    proxyInfo.Failures = 0;
+                    proxyInfo.IsAvailable = true;
+                }
+                else
+                {
+                    proxyInfo.Failures++;
+                    proxyInfo.IsAvailable = false;
+                    proxyInfo.LastUnavailable = DateTime.UtcNow;
+
+                    if (proxyInfo.Failures >= 3)
+                    {
+                        proxyInfo.IsAvailable = false;
+
+                        SendAlert(proxyInfo);
+
+                        // 使用异步任务恢复代理可用状态
+                        Task.Run(async () =>
+                        {
+                            await Task.Delay(TimeSpan.FromMinutes(1));
+
+                            lock (lockObj)
+                            {
+                                // 只有在这段时间内没有再次标记为不可用时才重置代理状态
+                                if (!proxyInfo.IsAvailable)
+                                {
+                                    proxyInfo.IsAvailable = true;
+                                }
+                            }
+                        });
+                    }
+                }
+            }
+        }
+    }
+
+    private void SendAlert(ProxyInfo proxyInfo)
+    {
+        Console.WriteLine($"Alert: Proxy {proxyInfo.Proxy.Address} is repeatedly failing.");
+    }
+}
+
+public class ProxyInfo
+{
+    public WebProxy Proxy { get; }
+    public bool IsAvailable { get; set; }
+    public int UsageCount { get; set; }
+    public int Failures { get; set; }
+    public DateTime LastUnavailable { get; set; }
+
+    public ProxyInfo(WebProxy proxy)
+    {
+        Proxy = proxy;
+        IsAvailable = true;
+        UsageCount = 0;
+        Failures = 0;
+        LastUnavailable = DateTime.MinValue;
+    }
+}

+ 149 - 0
molilian.core/Plus/TaobaoOpen/TaobaoOpenPlus.cs

@@ -0,0 +1,149 @@
+using dodohold.core;
+using System.Text.Json;
+using System.Xml;
+using TencentCloud.Lighthouse.V20200324.Models;
+using TencentCloud.Weilingwith.V20230427.Models;
+using Top.Api;
+using Top.Api.Request;
+using Top.Api.Response;
+
+
+namespace molilian.core
+{
+    public partial class TaobaoOpenPlus
+    {
+        public enum RecommendRequestType
+        {
+            first,
+            second,
+            ori,
+        }
+
+
+        string _app_key = string.Empty;
+        string _app_secret = string.Empty;
+        string _pid = string.Empty;
+        string _server_url = "https://eco.taobao.com/router/rest";
+
+        public TaobaoOpenPlus(string app_key, string app_secret, string pid)
+        {
+            _app_key = app_key;
+            _app_secret = app_secret;
+            _pid = pid;
+        }
+
+        public static PromotionQueryItemDTO ConverPromotionQueryItem(JsonElement item)
+        {
+            PromotionQueryItemDTO result = JsonSerializer.Deserialize<PromotionQueryItemDTO>(item.GetRawText());
+
+            string h5_url = item.Read("couponShareUrl", string.Empty);
+            if (string.IsNullOrEmpty(h5_url))
+            {
+                h5_url = item.Read("url", string.Empty);
+            }
+            if (h5_url.StartsWith("//")) h5_url = "https:" + h5_url;
+            result.h5_url = h5_url;
+            result.deeplink_url = AlimamaPlus.GetDeeplink(h5_url);
+
+            if (result.pic.StartsWith("//")) result.pic = "https:" + result.pic;
+            return result;
+        }
+
+
+        public TbkThorMfrsPromotionQueryResponse PromotionQueryRequest(string base64, string oaid)
+        {
+            ITopClient client = new DefaultTopClient(_server_url, _app_key, _app_secret, "json");
+            TbkThorMfrsPromotionQueryRequest req = new();
+            TbkThorMfrsPromotionQueryRequest.ManufacturerLaunchOptionDomain obj1 = new()
+            {
+                Img = base64,
+                Pid = _pid,
+                UniqueId = oaid,
+                UniqueIdType = "oaid",
+                SceneCode = "h5_img",
+                SceneSubCode = "Hitoch"
+            };
+            req.Option_ = obj1;
+
+            TbkThorMfrsPromotionQueryResponse rsp = client.Execute(req);
+            return rsp;
+        }
+
+
+        public async Task<(List<PromotionQueryItemDTO>, string)> GetRecommendData(RecommendRequestType type, string oaid,
+           string cookies)
+        {
+            string token = "undefined";
+            if (!string.IsNullOrEmpty(cookies)) token = cookies.GetContentPart("_m_h5_tk=", "_");
+            string url = GetRecommendUrl(_pid, oaid, token, type);
+            string body = string.Empty;
+
+            if (string.IsNullOrEmpty(cookies))
+            {
+                var res = await new WebClientUtility().RequestAsync(url);
+                body = res.Body();
+                cookies = res.ResponseMessage.GetCookiesString();
+                token = cookies.GetContentPart("_m_h5_tk=", "_");
+            }
+            url = GetRecommendUrl(_pid, oaid, token, type);
+
+            WebClientUtility client = new WebClientUtility();
+            if (!string.IsNullOrEmpty(cookies)) client.SetCookies(cookies);
+            var response = await client.RequestAsync(url);
+            body = response.Body();
+            body = body.GetContentPart("mtopjsonp1(", ")");
+
+            var root = body.Convert2JsonElement();
+            var ret = root.PathRead("ret[0]", string.Empty);
+
+            if (!"SUCCESS::调用成功".Equals(ret))
+            {
+                throw new Exception(ret);
+            }
+
+            var resultList = new List<PromotionQueryItemDTO>();
+            foreach (var data in root.ElementRead("data").ElementRead("resultList").EnumerateArray())
+            {
+                PromotionQueryItemDTO item = ConverPromotionQueryItem(data);
+                resultList.Add(item);
+            }
+            return (resultList, cookies);
+        }
+
+
+        public string GetRecommendUrl(string pid, string oaid, string token, RecommendRequestType type)
+        {
+            var first_data = $"{{\"scene\":\"pltSimilarPromotion\",\"variableMap\":\"{{\\\"pid\\\":\\\"{pid}\\\",\\\"oaid\\\":\\\"{oaid}\\\",\\\"type\\\":\\\"first\\\"}}\"}}";
+            var second_data = $"{{\"scene\":\"pltSimilarPromotion\",\"variableMap\":\"{{\\\"pid\\\":\\\"{pid}\\\",\\\"oaid\\\":\\\"{oaid}\\\",\\\"type\\\":\\\"second\\\"}}\"}}";
+            var ori_data = $"{{\"scene\":\"pltPromotionImg\",\"variableMap\":\"{{\\\"pid\\\":\\\"{pid}\\\",\\\"oaid\\\":\\\"{oaid}\\\",\\\"hasCoupon\\\":false,\\\"filterType\\\":\\\"ori\\\",\\\"seq\\\":\\\"\\\"}}\"}}";
+
+            string ts = DateTime.Now.Convert2UnixTimestamp(true).ToString();
+            string data = type switch
+            {
+                RecommendRequestType.first => first_data,
+                RecommendRequestType.second => second_data,
+                _ => ori_data,
+            };
+            string sign = GetSign(token, ts, data);
+
+            string url = $"https://h5api.m.taobao.com/h5/mtop.union.thor.landing.page.recommend/1.0/" +
+                $"?jsv=2.4.16&appKey=12574478&t={ts}&sign={sign}&v=1.0&api=mtop.union.thor.landing.page.recommend" +
+                $"&timeout=20000&AntiCreep=true&AntiFlood=true&type=jsonp&dataType=jsonp&callback=mtopjsonp1" +
+                $"&data={data.UrlEncode()}";
+
+            return url;
+        }
+        private string GetSign(string token, string ts, string data)
+        {
+            string g = "12574478";
+
+            //%7B%22scene%22%3A%22pltPromotionImg%22%2C%22variableMap%22%3A%22%7B%5C%22pid%5C%22%3A%5C%22mm_5993059531_3080100059_115689750259%5C%22%2C%5C%22oaid%5C%22%3A%5C%22-7775661578900138800%5C%22%2C%5C%22hasCoupon%5C%22%3Afalse%2C%5C%22filterType%5C%22%3A%5C%22ori%5C%22%2C%5C%22seq%5C%22%3A%5C%22%5C%22%7D%22%7D
+            //%7B%22scene%22%3A%22pltPromotionImg%22%2C%22variableMap%22%3A%22%7B%5C%22pid%5C%22%3A%5C%22mm_5993059531_3080100059_115689750259%5C%22%2C%5C%22oaid%5C%22%3A%5C%22-7775661578900138800%5C%22%2C%5C%22hasCoupon%5C%22%3Afalse%2C%5C%22filterType%5C%22%3A%5C%22ori%5C%22%2C%5C%22seq%5C%22%3A%5C%22%5C%22%7D%22%7D
+
+
+            var signString = $"{token}&{ts}&{g}&{data}";
+            return signString.MD5();
+        }
+    }
+}
+

+ 1 - 2
molilian.core/Plus/Tool/ToolParsePlus.cs

@@ -35,8 +35,7 @@ namespace molilian.core
             if (string.IsNullOrEmpty(content)) return content;
             if (string.IsNullOrEmpty(content)) return content;
             string result = content;
             string result = content;
             //string pattern = @"https?:\/\/?([\da-z\.-]+)\.([a-z]{2,6})(:\d{1,5})?([\/\w\.-]*)*\/?(#[\S]+)?(\?[\S]+)?";
             //string pattern = @"https?:\/\/?([\da-z\.-]+)\.([a-z]{2,6})(:\d{1,5})?([\/\w\.-]*)*\/?(#[\S]+)?(\?[\S]+)?";
-            string pattern = @"https?://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]";
-            Regex regex = new(pattern);
+            Regex regex = new(AlimamaPlus._url_pattern);
             MatchCollection matches = regex.Matches(content);
             MatchCollection matches = regex.Matches(content);
 
 
             foreach (Match m in matches.Cast<Match>())
             foreach (Match m in matches.Cast<Match>())

+ 4 - 0
molilian.core/molilian.core.csproj

@@ -20,4 +20,8 @@
     <PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.5.0" />
     <PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.5.0" />
   </ItemGroup>
   </ItemGroup>
 
 
+  <ItemGroup>
+    <ProjectReference Include="..\taobao-sdk-netcore-auto_1719572210878-20240628-source\topsdk-netcore.csproj" />
+  </ItemGroup>
+
 </Project>
 </Project>

+ 6 - 0
molilian.sln

@@ -7,6 +7,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "molilian.api", "molilian.ap
 EndProject
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "molilian.core", "molilian.core\molilian.core.csproj", "{AC3525E6-B196-43EB-B746-15D0B39D2E03}"
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "molilian.core", "molilian.core\molilian.core.csproj", "{AC3525E6-B196-43EB-B746-15D0B39D2E03}"
 EndProject
 EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "topsdk-netcore", "taobao-sdk-netcore-auto_1719572210878-20240628-source\topsdk-netcore.csproj", "{FF4DAE2A-8937-446C-B5D6-7736150021B6}"
+EndProject
 Global
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
 		Debug|Any CPU = Debug|Any CPU
@@ -21,6 +23,10 @@ Global
 		{AC3525E6-B196-43EB-B746-15D0B39D2E03}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{AC3525E6-B196-43EB-B746-15D0B39D2E03}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{AC3525E6-B196-43EB-B746-15D0B39D2E03}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{AC3525E6-B196-43EB-B746-15D0B39D2E03}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{AC3525E6-B196-43EB-B746-15D0B39D2E03}.Release|Any CPU.Build.0 = Release|Any CPU
 		{AC3525E6-B196-43EB-B746-15D0B39D2E03}.Release|Any CPU.Build.0 = Release|Any CPU
+		{FF4DAE2A-8937-446C-B5D6-7736150021B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{FF4DAE2A-8937-446C-B5D6-7736150021B6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{FF4DAE2A-8937-446C-B5D6-7736150021B6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{FF4DAE2A-8937-446C-B5D6-7736150021B6}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
 		HideSolutionNode = FALSE

+ 39 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Aliyun/AliyunResponse.cs

@@ -0,0 +1,39 @@
+using System;
+using System.Xml.Serialization;
+
+namespace Aliyun.Api
+{
+    [Serializable]
+    public abstract class AliyunResponse
+    {
+        /// <summary>
+        /// 错误码
+        /// </summary>
+        [XmlElement("Code")]
+        public string Code { get; set; }
+
+        /// <summary>
+        /// 错误信息
+        /// </summary>
+        [XmlElement("Message")]
+        public string Message { get; set; }
+		
+
+        /// <summary>
+        /// 响应原始内容
+        /// </summary>
+        public string Body { get; set; }
+
+
+        /// <summary>
+        /// 响应结果是否错误
+        /// </summary>
+        public bool IsError
+        {
+            get
+            {
+                return !string.IsNullOrEmpty(this.Code);
+            }
+        }
+    }
+}

+ 315 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Aliyun/DefaultAliyunClient.cs

@@ -0,0 +1,315 @@
+using Aliyun.Api.Parser;
+using Aliyun.Api.Util;
+using FastJSON;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Security.Cryptography;
+using System.Text;
+using System.Xml;
+using Top.Api;
+using Top.Api.Util;
+
+namespace Aliyun.Api
+{
+    /// <summary>
+    /// 基于REST的阿里云客户端。
+    /// </summary>
+    public class DefaultAliyunClient : IAliyunClient
+    {
+
+        public const string FORMAT_XML = "xml";
+        public const string FORMAT_JSON = "json";
+        public const string HTTP_METHOD_POST = "POST";
+        public const string SDK_VERSION = "top-sdk-net-dynamicVersionNo"; // SDK自动生成会替换成真实的版本
+
+
+        private AliyunWebUtils webUtils;
+        private ITopLogger topLogger;
+        private bool disableParser = false; // 禁用响应结果解释
+        private bool disableTrace = false; // 禁用日志调试功能
+        private IDictionary<string, string> systemParameters; // 设置所有请求共享的系统级参数
+
+        private const string ISO8601_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'";
+        private const string ENCODING_UTF8 = "UTF-8";
+
+        private string httpMethod = HTTP_METHOD_POST;
+        private string format = FORMAT_XML;
+        private string serverUrl;
+        private string accessKeyId;
+        private string accessKeySecret;
+
+        #region DefaultTopClient Constructors
+
+        public DefaultAliyunClient(string serverUrl, string accessKeyId, string accessKeySecret)
+        {
+            this.accessKeyId = accessKeyId;
+            this.accessKeySecret = accessKeySecret;
+            this.serverUrl = serverUrl;
+            this.webUtils = new AliyunWebUtils();
+            this.topLogger = Top.Api.Log.Instance;
+        }
+
+        public DefaultAliyunClient(string serverUrl, string accessKeyId, string accessKeySecret, string format)
+            : this(serverUrl, accessKeyId, accessKeySecret)
+        {
+            this.format = format;
+        }
+
+        #endregion
+
+
+        public void SetTopLogger(ITopLogger topLogger)
+        {
+            this.topLogger = topLogger;
+        }
+
+        public void SetTimeout(int timeout)
+        {
+            this.webUtils.Timeout = timeout;
+        }
+
+        public void SetDisableParser(bool disableParser)
+        {
+            this.disableParser = disableParser;
+        }
+
+        public void SetDisableTrace(bool disableTrace)
+        {
+            this.disableTrace = disableTrace;
+        }
+
+        public void SetSystemParameters(IDictionary<string, string> systemParameters)
+        {
+            this.systemParameters = systemParameters;
+        }
+
+        #region ITopClient Members
+
+        public T Execute<T>(IAliyunRequest<T> request) where T : AliyunResponse
+        {
+            return Execute<T>(request, null);
+        }
+
+        public T Execute<T>(IAliyunRequest<T> request, string session) where T : AliyunResponse
+        {
+            return Execute<T>(request, session, DateTime.Now);
+        }
+
+        public T Execute<T>(IAliyunRequest<T> request, string session, DateTime timestamp) where T : AliyunResponse
+        {
+            return DoExecute<T>(request, session, timestamp);
+        }
+
+        #endregion
+
+        private T DoExecute<T>(IAliyunRequest<T> request, string session, DateTime timestamp) where T : AliyunResponse
+        {
+            // 提前检查业务参数
+            try
+            {
+                request.Validate();
+            }
+            catch (TopException e)
+            {
+                return CreateErrorResponse<T>(e.ErrorCode, e.ErrorMsg);
+            }
+
+            // 添加协议级请求参数
+            TopDictionary txtParams = new TopDictionary(request.GetParameters());
+            txtParams.AddAll(this.systemParameters);
+            AddCommonParams(request, txtParams);
+
+            string reqUrl = webUtils.BuildGetUrl(this.serverUrl, txtParams);
+            try
+            {
+                string body;
+                if (request is IAliyunUploadRequest<T>) // 是否需要上传文件
+                {
+                    IAliyunUploadRequest<T> uRequest = (IAliyunUploadRequest<T>)request;
+                    IDictionary<string, FileItem> fileParams = TopUtils.CleanupDictionary(uRequest.GetFileParameters());
+                    body = webUtils.DoPost(this.serverUrl.TrimEnd('/'), txtParams, fileParams);
+                }
+                else
+                {
+                    body = webUtils.DoPost(this.serverUrl.TrimEnd('/'), txtParams);
+                }
+
+                // 解释响应结果
+                T rsp;
+                if (disableParser)
+                {
+                    rsp = Activator.CreateInstance<T>();
+                    rsp.Body = body;
+                }
+                else
+                {
+                    if (FORMAT_XML.Equals(format))
+                    {
+                        IAliyunParser tp = new AliyunXmlParser();
+                        rsp = tp.Parse<T>(body);
+                    }
+                    else
+                    {
+                        IAliyunParser tp = new AliyunJsonParser();
+                        rsp = tp.Parse<T>(body);
+                    }
+                }
+
+                // 追踪错误的请求
+                if (!disableTrace && rsp.IsError)
+                {
+                    StringBuilder sb = new StringBuilder(reqUrl).Append(" response error!\r\n").Append(rsp.Body);
+                    topLogger.Warn(sb.ToString());
+                }
+                return rsp;
+            }
+            catch (Exception e)
+            {
+                if (!disableTrace)
+                {
+                    StringBuilder sb = new StringBuilder(reqUrl).Append(" request error!\r\n").Append(e.StackTrace);
+                    topLogger.Error(sb.ToString());
+                }
+                throw e;
+            }
+        }
+
+        private T CreateErrorResponse<T>(string errCode, string errMsg) where T : AliyunResponse
+        {
+            T rsp = Activator.CreateInstance<T>();
+            rsp.Code = errCode;
+            rsp.Message = errMsg;
+
+            if (FORMAT_XML.Equals(format))
+            {
+                XmlDocument root = new XmlDocument();
+                XmlElement bodyE = root.CreateElement("Error");
+                XmlElement codeE = root.CreateElement("Code");
+                codeE.InnerText = errCode;
+                bodyE.AppendChild(codeE);
+                XmlElement msgE = root.CreateElement("Message");
+                msgE.InnerText = errMsg;
+                bodyE.AppendChild(msgE);
+                root.AppendChild(bodyE);
+                rsp.Body = root.OuterXml;
+            }
+            else
+            {
+                IDictionary<string, object> errObj = new Dictionary<string, object>();
+                errObj.Add("Code", errCode);
+                errObj.Add("Message", errMsg);
+
+                string body = JSON.ToJSON(errObj);
+                rsp.Body = body;
+            }
+            return rsp;
+        }
+
+        private void AddCommonParams<T>(IAliyunRequest<T> request, TopDictionary parameters) where T : AliyunResponse
+        {
+            String[] strArray = request.GetApiName().Split('.');
+            if (strArray.Length < 5)
+            {
+                throw new TopException("Wrong api name.");
+            }
+            String action = strArray[3];
+            parameters.Add("Action", action);
+
+            String version = strArray[4];
+
+            parameters.Add("Version", version);
+            parameters.Add("AccessKeyId", accessKeyId);
+            parameters.Add("Timestamp", FormatIso8601Date(DateTime.Now));
+            parameters.Add("SignatureMethod", "HMAC-SHA1");
+            parameters.Add("SignatureVersion", "1.0");
+            parameters.Add("SignatureNonce", Guid.NewGuid().ToString()); // 可以使用GUID作为SignatureNonce
+            parameters.Add("Format", format);
+
+            // 计算签名,并将签名结果加入请求参数中
+            parameters.Add("Signature", ComputeSignature(parameters));
+        }
+
+        private String ComputeSignature(TopDictionary parameters)
+        {
+
+            const String SEPARATOR = "&";
+
+            // 生成规范化请求字符串
+            StringBuilder canonicalizedQueryString = new StringBuilder();
+
+            var orderedParameters = SortDictionary(parameters);
+
+            foreach (var p in orderedParameters)
+            {
+                canonicalizedQueryString.Append("&")
+                    .Append(PercentEncode(p.Key)).Append("=")
+                    .Append(PercentEncode(p.Value));
+            }
+
+            // 生成用于计算签名的字符串 stringToSign
+            StringBuilder stringToSign = new StringBuilder();
+            stringToSign.Append(httpMethod).Append(SEPARATOR);
+            stringToSign.Append(PercentEncode("/")).Append(SEPARATOR);
+
+            stringToSign.Append(PercentEncode(
+                canonicalizedQueryString.ToString().Substring(1)));
+
+            // 注意accessKeySecret后面要加入一个字符"&"
+            String signature = CalculateSignature(accessKeySecret + "&",
+                                                  stringToSign.ToString());
+            return signature;
+        }
+
+        private static String FormatIso8601Date(DateTime date)
+        {
+            // 注意使用UTC时间
+            return date.ToUniversalTime().ToString(ISO8601_DATE_FORMAT, CultureInfo.CreateSpecificCulture("en-US"));
+        }
+
+        private static String CalculateSignature(String key, String stringToSign)
+        {
+            // 使用HmacSHA1算法计算HMAC值
+            using (var algorithm = KeyedHashAlgorithm.Create("HMACSHA1"))
+            {
+                algorithm.Key = Encoding.GetEncoding(ENCODING_UTF8).GetBytes(key.ToCharArray());
+                return Convert.ToBase64String(
+                    algorithm.ComputeHash(
+                        Encoding.GetEncoding(ENCODING_UTF8).GetBytes(stringToSign.ToCharArray())));
+            }
+        }
+
+        private static string PercentEncode(String value)
+        {
+            StringBuilder stringBuilder = new StringBuilder();
+            string text = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~";
+            byte[] bytes = Encoding.GetEncoding(ENCODING_UTF8).GetBytes(value);
+            foreach (char c in bytes)
+            {
+                if (text.IndexOf(c) >= 0)
+                {
+                    stringBuilder.Append(c);
+                }
+                else
+                {
+                    stringBuilder.Append("%").Append(
+                        string.Format(CultureInfo.InvariantCulture, "{0:X2}", (int)c));
+                }
+            }
+            return stringBuilder.ToString();
+        }
+
+        private static Dictionary<string, string> SortDictionary(Dictionary<string, string> dic)
+        {
+            ArrayList arrayList = new ArrayList(dic.Keys);
+            arrayList.Sort(StringComparer.Ordinal);
+            Dictionary<string, string> sortedDictionary = new Dictionary<string, string>();
+            foreach (string key in arrayList)
+            {
+                sortedDictionary.Add(key, dic[key]);
+            }
+            return sortedDictionary;
+        }
+    }
+}

+ 37 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Aliyun/IAliyunClient.cs

@@ -0,0 +1,37 @@
+using System;
+
+namespace Aliyun.Api
+{
+    /// <summary>
+    /// TOP客户端。
+    /// </summary>
+    public interface IAliyunClient
+    {
+        /// <summary>
+        /// 执行TOP公开API请求。
+        /// </summary>
+        /// <typeparam name="T">领域对象</typeparam>
+        /// <param name="request">具体的TOP API请求</param>
+        /// <returns>领域对象</returns>
+        T Execute<T>(IAliyunRequest<T> request) where T : AliyunResponse;
+
+        /// <summary>
+        /// 执行TOP隐私API请求。
+        /// </summary>
+        /// <typeparam name="T">领域对象</typeparam>
+        /// <param name="request">具体的TOP API请求</param>
+        /// <param name="session">用户会话码</param>
+        /// <returns>领域对象</returns>
+        T Execute<T>(IAliyunRequest<T> request, string session) where T : AliyunResponse;
+
+        /// <summary>
+        /// 执行TOP隐私API请求。
+        /// </summary>
+        /// <typeparam name="T">领域对象</typeparam>
+        /// <param name="request">具体的TOP API请求</param>
+        /// <param name="session">用户会话码</param>
+        /// <param name="timestamp">请求时间戳</param>
+        /// <returns>领域对象</returns>
+        T Execute<T>(IAliyunRequest<T> request, string session, DateTime timestamp) where T : AliyunResponse;
+    }
+}

+ 29 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Aliyun/IAliyunRequest.cs

@@ -0,0 +1,29 @@
+using System.Collections.Generic;
+
+namespace Aliyun.Api
+{
+    /// <summary>
+    /// TOP请求接口。
+    /// </summary>
+    public interface IAliyunRequest<T> where T : AliyunResponse
+    {
+        /// <summary>
+        /// 获取TOP的API名称。
+        /// </summary>
+        /// <returns>API名称</returns>
+        string GetApiName();
+
+        /// <summary>
+        /// 获取所有的Key-Value形式的文本请求参数字典。其中:
+        /// Key: 请求参数名
+        /// Value: 请求参数文本值
+        /// </summary>
+        /// <returns>文本请求参数字典</returns>
+        IDictionary<string, string> GetParameters();
+
+        /// <summary>
+        /// 提前验证参数。
+        /// </summary>
+        void Validate();
+    }
+}

+ 19 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Aliyun/IAliyunUploadRequest.cs

@@ -0,0 +1,19 @@
+using System.Collections.Generic;
+using Top.Api.Util;
+
+namespace Aliyun.Api
+{
+    /// <summary>
+    /// TOP上传请求接口,支持同时上传多个文件。
+    /// </summary>
+    public interface IAliyunUploadRequest<T> : IAliyunRequest<T> where T : AliyunResponse
+    {
+        /// <summary>
+        /// 获取所有的Key-Value形式的文件请求参数字典。其中:
+        /// Key: 请求参数名
+        /// Value: 文件对象
+        /// </summary>
+        /// <returns>文件请求参数字典</returns>
+        IDictionary<string, FileItem> GetFileParameters();
+    }
+}

+ 203 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Aliyun/Parser/AliyunJsonParser.cs

@@ -0,0 +1,203 @@
+using FastJSON;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Xml.Serialization;
+using Top.Api.Parser;
+
+namespace Aliyun.Api.Parser
+{
+    /// <summary>
+    /// TOP JSON响应通用解释器。
+    /// </summary>
+    public class AliyunJsonParser : IAliyunParser
+    {
+        private static readonly object writeLock = new object();
+        private static readonly Dictionary<string, Dictionary<string, TopAttribute>> attrs = new Dictionary<string, Dictionary<string, TopAttribute>>();
+
+        #region ITopParser Members
+
+        public T Parse<T>(string body) where T : AliyunResponse
+        {
+            T rsp = null;
+
+            IDictionary json = JSON.Parse(body) as IDictionary;
+            if (json != null)
+            {
+                ITopReader reader = new TopJsonReader(json);
+                rsp = (T)FromJson(reader, typeof(T));
+            }
+
+            if (rsp == null)
+            {
+                rsp = Activator.CreateInstance<T>();
+            }
+
+            if (rsp != null)
+            {
+                rsp.Body = body;
+            }
+
+            return rsp;
+        }
+
+        #endregion
+
+        private Dictionary<string, TopAttribute> GetTopAttributes(Type type)
+        {
+            Dictionary<string, TopAttribute> tas = null;
+            bool inc = attrs.TryGetValue(type.FullName, out tas);
+
+            if (inc && tas != null) // 从缓存中获取类属性元数据
+            {
+                return tas;
+            }
+            else // 创建新的类属性元数据缓存
+            {
+                tas = new Dictionary<string, TopAttribute>();
+            }
+
+            PropertyInfo[] pis = type.GetProperties();
+            foreach (PropertyInfo pi in pis)
+            {
+                TopAttribute ta = new TopAttribute();
+                ta.Method = pi.GetSetMethod();
+
+                // 获取对象属性名称
+                XmlElementAttribute[] xeas = pi.GetCustomAttributes(typeof(XmlElementAttribute), true) as XmlElementAttribute[];
+                if (xeas != null && xeas.Length > 0)
+                {
+                    ta.ItemName = xeas[0].ElementName;
+                }
+
+                // 获取列表属性名称
+                if (ta.ItemName == null)
+                {
+                    XmlArrayItemAttribute[] xaias = pi.GetCustomAttributes(typeof(XmlArrayItemAttribute), true) as XmlArrayItemAttribute[];
+                    if (xaias != null && xaias.Length > 0)
+                    {
+                        ta.ItemName = xaias[0].ElementName;
+                    }
+                    XmlArrayAttribute[] xaas = pi.GetCustomAttributes(typeof(XmlArrayAttribute), true) as XmlArrayAttribute[];
+                    if (xaas != null && xaas.Length > 0)
+                    {
+                        ta.ListName = xaas[0].ElementName;
+                    }
+                    if (ta.ListName == null)
+                    {
+                        continue;
+                    }
+                }
+
+                // 获取属性类型
+                if (pi.PropertyType.IsGenericType)
+                {
+                    Type[] types = pi.PropertyType.GetGenericArguments();
+                    ta.ListType = types[0];
+                }
+                else
+                {
+                    ta.ItemType = pi.PropertyType;
+                }
+
+                tas.Add(pi.Name, ta);
+            }
+
+            lock (writeLock)
+            {
+                attrs[type.FullName] = tas;
+            }
+            return tas;
+        }
+
+        public object FromJson(ITopReader reader, Type type)
+        {
+            object rsp = null;
+            Dictionary<string, TopAttribute> pas = GetTopAttributes(type);
+
+            Dictionary<string, TopAttribute>.Enumerator em = pas.GetEnumerator();
+            while (em.MoveNext())
+            {
+                KeyValuePair<string, TopAttribute> kvp = em.Current;
+                TopAttribute ta = kvp.Value;
+                string itemName = ta.ItemName;
+                string listName = ta.ListName;
+
+                if (!reader.HasReturnField(itemName) && (string.IsNullOrEmpty(listName) || !reader.HasReturnField(listName)))
+                {
+                    continue;
+                }
+
+                object value = null;
+                if (ta.ListType != null)
+                {
+                    value = reader.GetListObjects(listName, itemName, ta.ListType, FromJson);
+                }
+                else
+                {
+                    if (typeof(string) == ta.ItemType)
+                    {
+                        object tmp = reader.GetPrimitiveObject(itemName);
+                        if (typeof(string) == tmp.GetType())
+                        {
+                            value = tmp;
+                        }
+                        else
+                        {
+                            if (tmp != null)
+                            {
+                                value = tmp.ToString();
+                            }
+                        }
+                    }
+                    else if (typeof(long) == ta.ItemType)
+                    {
+                        object tmp = reader.GetPrimitiveObject(itemName);
+                        if (typeof(long) == tmp.GetType())
+                        {
+                            value = tmp;
+                        }
+                        else
+                        {
+                            if (tmp != null)
+                            {
+                                value = long.Parse(tmp.ToString());
+                            }
+                        }
+                    }
+                    else if (typeof(bool) == ta.ItemType)
+                    {
+                        object tmp = reader.GetPrimitiveObject(itemName);
+                        if (typeof(bool) == tmp.GetType())
+                        {
+                            value = tmp;
+                        }
+                        else
+                        {
+                            if (tmp != null)
+                            {
+                                value = bool.Parse(tmp.ToString());
+                            }
+                        }
+                    }
+                    else
+                    {
+                        value = reader.GetReferenceObject(itemName, ta.ItemType, FromJson);
+                    }
+                }
+
+                if (value != null)
+                {
+                    if (rsp == null)
+                    {
+                        rsp = Activator.CreateInstance(type);
+                    }
+                    ta.Method.Invoke(rsp, new object[] { value });
+                }
+            }
+
+            return rsp;
+        }
+    }
+}

+ 81 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Aliyun/Parser/AliyunJsonReader.cs

@@ -0,0 +1,81 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using Top.Api.Parser;
+
+namespace Aliyun.Api.Parser
+{
+    /// <summary>
+    /// TOP JSON响应通用读取器。
+    /// </summary>
+    public class AliyunJsonReader : ITopReader
+    {
+        private IDictionary json;
+
+        public AliyunJsonReader(IDictionary json)
+        {
+            this.json = json;
+        }
+
+        public bool HasReturnField(object name)
+        {
+            return json.Contains(name);
+        }
+
+        public object GetPrimitiveObject(object name)
+        {
+            return json[name];
+        }
+
+        public object GetReferenceObject(object name, Type type, DTopConvert convert)
+        {
+            IDictionary dict = json[name] as IDictionary;
+            if (dict != null && dict.Count > 0)
+            {
+                return convert(new TopJsonReader(dict), type);
+            }
+            else
+            {
+                return null;
+            }
+        }
+
+        public IList GetListObjects(string listName, string itemName, Type type, DTopConvert convert)
+        {
+            IList listObjs = null;
+
+            IDictionary jsonMap = json[listName] as IDictionary;
+            if (jsonMap != null && jsonMap.Count > 0)
+            {
+                IList jsonList = jsonMap[itemName] as IList;
+                if (jsonList != null && jsonList.Count > 0)
+                {
+                    Type listType = typeof(List<>).MakeGenericType(new Type[] { type });
+                    listObjs = Activator.CreateInstance(listType) as IList;
+                    foreach (object item in jsonList)
+                    {
+                        if (typeof(IDictionary).IsAssignableFrom(item.GetType())) // object
+                        {
+                            IDictionary subMap = item as IDictionary;
+                            object subObj = convert(new TopJsonReader(subMap), type);
+                            if (subObj != null)
+                            {
+                                listObjs.Add(subObj);
+                            }
+                        }
+                        else if (typeof(IList).IsAssignableFrom(item.GetType())) // list or array
+                        {
+                            // TODO not support yet
+                        }
+                        else // string, bool, long, double, null, other
+                        {
+                            listObjs.Add(item);
+                        }
+                    }
+                }
+            }
+
+            return listObjs;
+        }
+    }
+}

+ 82 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Aliyun/Parser/AliyunXmlParser.cs

@@ -0,0 +1,82 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Xml.Serialization;
+using Top.Api;
+
+namespace Aliyun.Api.Parser
+{
+    /// <summary>
+    /// TOP XML响应通用解释器。
+    /// </summary>
+    public class AliyunXmlParser : IAliyunParser
+    {
+        private static readonly Regex regex = new Regex("<(\\w+?)[ >]", RegexOptions.Compiled);
+        private static readonly object writeLock = new object();
+        private static readonly Dictionary<string, XmlSerializer> parsers = new Dictionary<string, XmlSerializer>();
+
+        #region ITopParser Members
+
+        public T Parse<T>(string body) where T : AliyunResponse
+        {
+            Type type = typeof(T);
+            string rootTagName = GetRootElement(body);
+
+            string key = type.FullName;
+            if (Constants.ERROR_RESPONSE.Equals(rootTagName))
+            {
+                key += ("_" + Constants.ERROR_RESPONSE);
+            }
+
+            XmlSerializer serializer = null;
+            bool incl = parsers.TryGetValue(key, out serializer);
+            if (!incl || serializer == null)
+            {
+                XmlAttributes rootAttrs = new XmlAttributes();
+                rootAttrs.XmlRoot = new XmlRootAttribute(rootTagName);
+
+                XmlAttributeOverrides attrOvrs = new XmlAttributeOverrides();
+                attrOvrs.Add(type, rootAttrs);
+
+                serializer = new XmlSerializer(type, attrOvrs);
+                lock (writeLock)
+                {
+                    parsers[key] = serializer;
+                }
+            }
+
+            object obj = null;
+            using (System.IO.Stream stream = new MemoryStream(Encoding.UTF8.GetBytes(body)))
+            {
+                obj = serializer.Deserialize(stream);
+            }
+
+            T rsp = (T)obj;
+            if (rsp != null)
+            {
+                rsp.Body = body;
+            }
+            return rsp;
+        }
+
+        #endregion
+
+        /// <summary>
+        /// 获取XML响应的根节点名称
+        /// </summary>
+        private string GetRootElement(string body)
+        {
+            Match match = regex.Match(body);
+            if (match.Success)
+            {
+                return match.Groups[1].ToString();
+            }
+            else
+            {
+                throw new TopException("Invalid XML response format!");
+            }
+        }
+    }
+}

+ 17 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Aliyun/Parser/IAliyunParser.cs

@@ -0,0 +1,17 @@
+
+namespace Aliyun.Api.Parser
+{
+    /// <summary>
+    /// TOP API响应解释器接口。响应格式可以是XML, JSON等等。
+    /// </summary>
+    public interface IAliyunParser
+    {
+        /// <summary>
+        /// 把响应字符串解释成相应的领域对象。
+        /// </summary>
+        /// <typeparam name="T">领域对象</typeparam>
+        /// <param name="body">响应字符串</param>
+        /// <returns>领域对象</returns>
+        T Parse<T>(string body) where T : AliyunResponse;
+    }
+}

+ 261 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Aliyun/Util/AliyunWebUtils.cs

@@ -0,0 +1,261 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Net;
+using System.Net.Security;
+using System.Security.Cryptography.X509Certificates;
+using System.Text;
+using System.Web;
+using Top.Api.Util;
+
+
+namespace Aliyun.Api.Util
+{
+    public class AliyunWebUtils
+    {
+        private int _timeout = 100000;
+
+        /// <summary>
+        /// 请求与响应的超时时间
+        /// </summary>
+        public int Timeout
+        {
+            get { return this._timeout; }
+            set { this._timeout = value; }
+        }
+
+        /// <summary>
+        /// 执行HTTP POST请求。
+        /// </summary>
+        /// <param name="url">请求地址</param>
+        /// <param name="parameters">请求参数</param>
+        /// <returns>HTTP响应</returns>
+        public string DoPost(string url, IDictionary<string, string> parameters)
+        {
+            HttpWebRequest req = GetWebRequest(url, "POST");
+            req.ContentType = "application/x-www-form-urlencoded;charset=utf-8";
+
+            byte[] postData = Encoding.UTF8.GetBytes(BuildQuery(parameters));
+            System.IO.Stream reqStream = req.GetRequestStream();
+            reqStream.Write(postData, 0, postData.Length);
+            reqStream.Close();
+            HttpWebResponse rsp = null;
+            try
+            {
+                rsp = (HttpWebResponse)req.GetResponse();
+            }
+            catch (WebException ex)
+            {
+                WebResponse wr = ex.Response;
+                System.IO.Stream st = wr.GetResponseStream();
+                StreamReader sr = new StreamReader(st, System.Text.Encoding.Default);
+                string sError = sr.ReadToEnd();
+                sr.Close();
+                st.Close();
+                return sError;
+            }
+            Encoding encoding = Encoding.GetEncoding(rsp.CharacterSet);
+            return GetResponseAsString(rsp, encoding);
+        }
+
+        /// <summary>
+        /// 执行HTTP GET请求。
+        /// </summary>
+        /// <param name="url">请求地址</param>
+        /// <param name="parameters">请求参数</param>
+        /// <returns>HTTP响应</returns>
+        public string DoGet(string url, IDictionary<string, string> parameters)
+        {
+            if (parameters != null && parameters.Count > 0)
+            {
+                if (url.Contains("?"))
+                {
+                    url = url + "&" + BuildQuery(parameters);
+                }
+                else
+                {
+                    url = url + "?" + BuildQuery(parameters);
+                }
+            }
+
+            HttpWebRequest req = GetWebRequest(url, "GET");
+            req.ContentType = "application/x-www-form-urlencoded;charset=utf-8";
+
+            HttpWebResponse rsp = (HttpWebResponse)req.GetResponse();
+            Encoding encoding = Encoding.GetEncoding(rsp.CharacterSet);
+            return GetResponseAsString(rsp, encoding);
+        }
+
+        /// <summary>
+        /// 执行带文件上传的HTTP POST请求。
+        /// </summary>
+        /// <param name="url">请求地址</param>
+        /// <param name="textParams">请求文本参数</param>
+        /// <param name="fileParams">请求文件参数</param>
+        /// <returns>HTTP响应</returns>
+        public string DoPost(string url, IDictionary<string, string> textParams, IDictionary<string, FileItem> fileParams)
+        {
+            // 如果没有文件参数,则走普通POST请求
+            if (fileParams == null || fileParams.Count == 0)
+            {
+                return DoPost(url, textParams);
+            }
+
+            string boundary = DateTime.Now.Ticks.ToString("X"); // 随机分隔线
+
+            HttpWebRequest req = GetWebRequest(url, "POST");
+            req.ContentType = "multipart/form-data;charset=utf-8;boundary=" + boundary;
+
+            System.IO.Stream reqStream = req.GetRequestStream();
+            byte[] itemBoundaryBytes = Encoding.UTF8.GetBytes("\r\n--" + boundary + "\r\n");
+            byte[] endBoundaryBytes = Encoding.UTF8.GetBytes("\r\n--" + boundary + "--\r\n");
+
+            // 组装文本请求参数
+            string textTemplate = "Content-Disposition:form-data;name=\"{0}\"\r\nContent-Type:text/plain\r\n\r\n{1}";
+            IEnumerator<KeyValuePair<string, string>> textEnum = textParams.GetEnumerator();
+            while (textEnum.MoveNext())
+            {
+                string textEntry = string.Format(textTemplate, textEnum.Current.Key, textEnum.Current.Value);
+                byte[] itemBytes = Encoding.UTF8.GetBytes(textEntry);
+                reqStream.Write(itemBoundaryBytes, 0, itemBoundaryBytes.Length);
+                reqStream.Write(itemBytes, 0, itemBytes.Length);
+            }
+
+            // 组装文件请求参数
+            string fileTemplate = "Content-Disposition:form-data;name=\"{0}\";filename=\"{1}\"\r\nContent-Type:{2}\r\n\r\n";
+            IEnumerator<KeyValuePair<string, FileItem>> fileEnum = fileParams.GetEnumerator();
+            while (fileEnum.MoveNext())
+            {
+                string key = fileEnum.Current.Key;
+                FileItem fileItem = fileEnum.Current.Value;
+                if (!fileItem.IsValid())
+                {
+                    throw new ArgumentException("FileItem is invalid");
+                }
+                string fileEntry = string.Format(fileTemplate, key, fileItem.GetFileName(), fileItem.GetMimeType());
+                byte[] itemBytes = Encoding.UTF8.GetBytes(fileEntry);
+                reqStream.Write(itemBoundaryBytes, 0, itemBoundaryBytes.Length);
+                reqStream.Write(itemBytes, 0, itemBytes.Length);
+                fileItem.Write(reqStream);
+            }
+
+            reqStream.Write(endBoundaryBytes, 0, endBoundaryBytes.Length);
+            reqStream.Close();
+
+            HttpWebResponse rsp = (HttpWebResponse)req.GetResponse();
+            Encoding encoding = Encoding.GetEncoding(rsp.CharacterSet);
+            return GetResponseAsString(rsp, encoding);
+        }
+
+        public bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
+        { //直接确认,否则打不开
+            return true;
+        }
+
+        public HttpWebRequest GetWebRequest(string url, string method)
+        {
+            HttpWebRequest req = null;
+            if (url.Contains("https"))
+            {
+                ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(CheckValidationResult);
+                req = (HttpWebRequest)WebRequest.CreateDefault(new Uri(url));
+            }
+            else
+            {
+                req = (HttpWebRequest)WebRequest.Create(url);
+            }
+
+            req.ServicePoint.Expect100Continue = false;
+            req.Method = method;
+            req.KeepAlive = true;
+            req.UserAgent = "Top4Net";
+            req.Timeout = this._timeout;
+
+            return req;
+        }
+
+        /// <summary>
+        /// 把响应流转换为文本。
+        /// </summary>
+        /// <param name="rsp">响应流对象</param>
+        /// <param name="encoding">编码方式</param>
+        /// <returns>响应文本</returns>
+        public string GetResponseAsString(HttpWebResponse rsp, Encoding encoding)
+        {
+            System.IO.Stream stream = null;
+            StreamReader reader = null;
+
+            try
+            {
+                // 以字符流的方式读取HTTP响应
+                stream = rsp.GetResponseStream();
+                reader = new StreamReader(stream, encoding);
+                return reader.ReadToEnd();
+            }
+            finally
+            {
+                // 释放资源
+                if (reader != null) reader.Close();
+                if (stream != null) stream.Close();
+                if (rsp != null) rsp.Close();
+            }
+        }
+
+        /// <summary>
+        /// 组装GET请求URL。
+        /// </summary>
+        /// <param name="url">请求地址</param>
+        /// <param name="parameters">请求参数</param>
+        /// <returns>带参数的GET请求URL</returns>
+        public string BuildGetUrl(string url, IDictionary<string, string> parameters)
+        {
+            if (parameters != null && parameters.Count > 0)
+            {
+                if (url.Contains("?"))
+                {
+                    url = url + "&" + BuildQuery(parameters);
+                }
+                else
+                {
+                    url = url + "?" + BuildQuery(parameters);
+                }
+            }
+            return url;
+        }
+
+        /// <summary>
+        /// 组装普通文本请求参数。
+        /// </summary>
+        /// <param name="parameters">Key-Value形式请求参数字典</param>
+        /// <returns>URL编码后的请求数据</returns>
+        public static string BuildQuery(IDictionary<string, string> parameters)
+        {
+            StringBuilder postData = new StringBuilder();
+            bool hasParam = false;
+
+            IEnumerator<KeyValuePair<string, string>> dem = parameters.GetEnumerator();
+            while (dem.MoveNext())
+            {
+                string name = dem.Current.Key;
+                string value = dem.Current.Value;
+                // 忽略参数名或参数值为空的参数
+                if (!string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(value))
+                {
+                    if (hasParam)
+                    {
+                        postData.Append("&");
+                    }
+
+                    postData.Append(name);
+                    postData.Append("=");
+                    postData.Append(HttpUtility.UrlEncode(value, Encoding.UTF8));
+                    hasParam = true;
+                }
+            }
+
+            return postData.ToString();
+        }
+
+    }
+}

+ 37 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/AutoRetryClusterTopClient.cs

@@ -0,0 +1,37 @@
+using Top.Api.Cluster;
+
+namespace Top.Api
+{
+    public class AutoRetryClusterTopClient : AutoRetryTopClient
+    {
+        public AutoRetryClusterTopClient(string serverUrl, string appKey, string appSecret)
+            : base(serverUrl, appKey, appSecret)
+        {
+            ClusterManager.InitRefreshThread(this);
+        }
+
+        public AutoRetryClusterTopClient(string serverUrl, string appKey, string appSecret, string format)
+            : base(serverUrl, appKey, appSecret, format)
+        {
+            ClusterManager.InitRefreshThread(this);
+        }
+
+        internal override string GetServerUrl(string serverUrl, string apiName, string session)
+        {
+            DnsConfig dnsConfig = ClusterManager.GetDnsConfigFromCache();
+            if (dnsConfig == null)
+            {
+                return serverUrl;
+            }
+            else
+            {
+                return dnsConfig.GetBestVipUrl(serverUrl, apiName, session);
+            }
+        }
+
+        internal override string GetSdkVersion()
+        {
+            return Constants.SDK_VERSION_CLUSTER;
+        }
+    }
+}

+ 143 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/AutoRetryTopClient.cs

@@ -0,0 +1,143 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading;
+
+namespace Top.Api
+{
+    /// <summary>
+    /// 调用出错自动重试客户端。
+    /// </summary>
+    public class AutoRetryTopClient : DefaultTopClient
+    {
+        private static readonly TopException RETRY_FAIL = new TopException("sdk.retry-call-fail", "API调用重试失败");
+
+        /// <summary>
+        /// 单次请求的最大重试次数,默认值为3次。
+        /// </summary>
+        private int maxRetryCount = 3;
+        /// <summary>
+        /// 重试之前休眠时间,默认值为100毫秒。
+        /// </summary>
+        private int retryWaitTime = 100;
+        /// <summary>
+        /// 超过最大重试次数时是否抛出异常。
+        /// </summary>
+        private bool throwIfOverMaxRetry = false;
+        /// <summary>
+        /// 自定义重试错误码列表。
+        /// </summary>
+        private IDictionary<string, bool> retryErrorCodes;
+
+        public AutoRetryTopClient(string serverUrl, string appKey, string appSecret)
+            : base(serverUrl, appKey, appSecret)
+        {
+        }
+
+        public AutoRetryTopClient(string serverUrl, string appKey, string appSecret, string format)
+            : base(serverUrl, appKey, appSecret, format)
+        {
+        }
+
+        public override T Execute<T>(ITopRequest<T> request)
+        {
+            return Execute<T>(request, null);
+        }
+
+        public override T Execute<T>(ITopRequest<T> request, string session)
+        {
+            return Execute<T>(request, session, DateTime.Now);
+        }
+
+        public override T Execute<T>(ITopRequest<T> request, string session, DateTime timestamp)
+        {
+            T rsp = null;
+            TopException exp = null;
+
+            for (int i = 0; i < maxRetryCount; i++)
+            {
+                if (i > 0)
+                {
+                    if ((rsp != null && ((rsp.SubErrCode != null && rsp.SubErrCode.StartsWith("isp."))
+                        || (retryErrorCodes != null && retryErrorCodes.ContainsKey(rsp.SubErrCode)))) || exp != null)
+                    {
+                        Thread.Sleep(retryWaitTime);
+                        topLogger.Warn(BuildRetryLog(request.GetApiName(), request.GetParameters(), i));
+                    }
+                    else
+                    {
+                        break;
+                    }
+                }
+
+                try
+                {
+                    rsp = base.Execute(request, session);
+                    if (rsp.IsError)
+                    {
+                        if (i == maxRetryCount && throwIfOverMaxRetry)
+                        {
+                            throw RETRY_FAIL;
+                        }
+                    }
+                    else
+                    {
+                        return rsp;
+                    }
+                }
+                catch (TopException e)
+                {
+                    if (exp == null)
+                    {
+                        exp = e;
+                    }
+                }
+            }
+
+            if (exp != null)
+            {
+                throw exp;
+            }
+            else
+            {
+                return rsp;
+            }
+        }
+
+        public void SetMaxRetryCount(int maxRetryCount)
+        {
+            this.maxRetryCount = maxRetryCount;
+        }
+
+        public void SetRetryWaitTime(int retryWaitTime)
+        {
+            this.retryWaitTime = retryWaitTime;
+        }
+
+        public void SetThrowIfOverMaxRetry(bool throwIfOverMaxRetry)
+        {
+            this.throwIfOverMaxRetry = throwIfOverMaxRetry;
+        }
+
+        public void AddRetryErrorCode(string errorCode)
+        {
+            if (this.retryErrorCodes == null)
+            {
+                this.retryErrorCodes = new Dictionary<string, bool>();
+            }
+            this.retryErrorCodes.Add(errorCode, false);
+        }
+
+        private string BuildRetryLog(string apiName, IDictionary<string, string> parameters, int retryCount)
+        {
+            StringBuilder sb = new StringBuilder();
+            sb.Append(apiName).Append(" retry call ").Append(retryCount);
+            if (parameters.ContainsKey("fields"))
+            {
+                parameters.Remove("fields");
+            }
+            sb.Append(" times, parameters=").Append(parameters);
+            return sb.ToString();
+        }
+    }
+}

+ 93 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/BaseTopRequest.cs

@@ -0,0 +1,93 @@
+using System;
+using System.Collections.Generic;
+
+namespace Top.Api
+{
+    /// <summary>
+    /// 基础TOP请求类,存放一些通用的请求参数。
+    /// </summary>
+    public abstract class BaseTopRequest<T> : ITopRequest<T> where T : TopResponse
+    {
+        /// <summary>
+        /// HTTP请求URL参数
+        /// </summary>
+        internal TopDictionary otherParams;
+        /// <summary>
+        /// HTTP请求头参数
+        /// </summary>
+        private TopDictionary headerParams;
+        /// <summary>
+        /// 请求目标AppKey
+        /// </summary>
+        private string targetAppKey;
+
+        /// <summary>
+        /// 批量API请求的用户授权码
+        /// </summary>
+        private string batchApiSession;
+
+        /// <summary>
+        /// API在批量调用中的顺序
+        /// </summary>
+        private int batchApiOrder;
+
+        public void AddOtherParameter(string key, string value)
+        {
+            if (this.otherParams == null)
+            {
+                this.otherParams = new TopDictionary();
+            }
+            this.otherParams.Add(key, value);
+        }
+
+        public void AddHeaderParameter(string key, string value)
+        {
+            GetHeaderParameters().Add(key, value);
+        }
+
+        public IDictionary<string, string> GetHeaderParameters()
+        {
+            if (this.headerParams == null)
+            {
+                this.headerParams = new TopDictionary();
+            }
+            return this.headerParams;
+        }
+
+        public string GetTargetAppKey()
+        {
+            return this.targetAppKey;
+        }
+
+        public void SetTargetAppKey(string targetAppKey)
+        {
+            this.targetAppKey = targetAppKey;
+        }
+
+        public string GetBatchApiSession()
+        {
+            return this.batchApiSession;
+        }
+
+        public void SetBatchApiSession(string session)
+        {
+            this.batchApiSession = session;
+        }
+
+        public int GetBatchApiOrder()
+        {
+            return this.batchApiOrder;
+        }
+
+        public void SetBatchApiOrder(int order)
+        {
+            this.batchApiOrder = order;
+        }
+
+        public abstract string GetApiName();
+
+        public abstract void Validate();
+
+        public abstract IDictionary<string, string> GetParameters();
+    }
+}

+ 261 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/BatchTopClient.cs

@@ -0,0 +1,261 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Top.Api.Parser;
+using Top.Api.Util;
+
+namespace Top.Api
+{
+    public class BatchTopClient : DefaultTopClient
+    {
+        private const string BATCH_API_HEADER_SPLIT = "top-api-separator"; // 批量API用户自定义分隔符Header Key
+        private const string BATCH_API_PUBLIC_PARAMETER = "#PUBLIC#"; // 批量API公共参数头
+        private const string BATCH_API_DEFAULT_SPLIT = "\r\n-S-\r\n";// 批量API默认分隔符
+        private const string BATCH_API_CONTENT_TYPE = "text/plain;charset=utf-8";// 批量API请求文档类型
+
+        private string batchServerUrl;
+        private string batchApiSeparator; // 自定义批量API分隔符
+
+        public BatchTopClient(string serverUrl, string appKey, string appSecret)
+            : base(BuildApiServerUrl(serverUrl), appKey, appSecret)
+        {
+            this.batchServerUrl = serverUrl;
+        }
+
+        public BatchTopClient(string serverUrl, string appKey, string appSecret, string format)
+            : base(BuildApiServerUrl(serverUrl), appKey, appSecret, format)
+        {
+            this.batchServerUrl = serverUrl;
+        }
+
+        public void SetBatchApiSeparator(string batchApiSeparator)
+        {
+            this.batchApiSeparator = batchApiSeparator;
+        }
+
+        public override T Execute<T>(ITopRequest<T> request)
+        {
+            return Execute<T>(request, null);
+        }
+
+        public override T Execute<T>(ITopRequest<T> request, string session)
+        {
+            return Execute<T>(request, session, DateTime.Now);
+        }
+
+        public override T Execute<T>(ITopRequest<T> request, string session, DateTime timestamp)
+        {
+            if (typeof(TopBatchRequest) == request.GetType())
+            {
+                return DoExecute<T>(request, session, timestamp);
+            }
+            else
+            {
+                return base.Execute<T>(request, session, timestamp);
+            }
+        }
+
+        private T DoExecute<T>(ITopRequest<T> request, string session, DateTime timestamp) where T : TopResponse
+        {
+            long start = DateTime.Now.Ticks;
+
+            TopBatchRequest batchRequest = request as TopBatchRequest;
+            List<ITopRequest<TopResponse>> requestList = batchRequest.RequestList;
+            if (requestList == null || requestList.Count == 0)
+            {
+                throw new TopException("40", "client-error:api request list is empty");
+            }
+
+            // 本地校验请求参数
+            if (batchRequest.PublicParams == null || batchRequest.PublicParams.Count == 0)
+            {
+                for (int i = 0; i < requestList.Count; i++)
+                {
+                    try
+                    {
+                        requestList[i].Validate();
+                    }
+                    catch (TopException e)
+                    {
+                        return CreateErrorResponse<T>(e.ErrorCode, e.ErrorMsg);
+                    }
+                }
+            }
+
+            // 添加协议级请求参数
+            TopDictionary parameters = new TopDictionary();
+            parameters.Add(Constants.VERSION, "2.0");
+            parameters.Add(Constants.APP_KEY, appKey);
+            parameters.Add(Constants.TIMESTAMP, timestamp);
+            parameters.Add(Constants.FORMAT, format);
+            parameters.Add(Constants.SIGN_METHOD, Constants.SIGN_METHOD_HMAC);
+            parameters.Add(Constants.PARTNER_ID, GetSdkVersion());
+            parameters.Add(Constants.TARGET_APP_KEY, request.GetTargetAppKey());
+            parameters.Add(Constants.SESSION, session);
+            if (Constants.FORMAT_JSON.Equals(format) && this.useSimplifyJson)
+            {
+                parameters.Add(Constants.SIMPLIFY, "true");
+            }
+
+            // 添加自定义分隔符
+            string separator = BATCH_API_DEFAULT_SPLIT;
+            if (!string.IsNullOrEmpty(batchApiSeparator))
+            {
+                batchRequest.AddHeaderParameter(BATCH_API_HEADER_SPLIT, separator = batchApiSeparator);
+            }
+
+            // 是否需要压缩响应
+            if (this.useGzipEncoding)
+            {
+                batchRequest.AddHeaderParameter(Constants.ACCEPT_ENCODING, Constants.CONTENT_ENCODING_GZIP);
+            }
+
+            try
+            {
+                // 添加公共请求头
+                if (!string.IsNullOrEmpty(batchRequest.PublicMethod))
+                {
+                    batchRequest.AddPublicParam(Constants.METHOD, batchRequest.PublicMethod);
+                }
+                else
+                {
+                    if (IsSameRequest(requestList))
+                    {
+                        batchRequest.AddPublicParam(Constants.METHOD, requestList[0].GetApiName());
+                    }
+                }
+
+                // 构建批量请求主体
+                StringBuilder requestBody = new StringBuilder();
+                string publicParamStr = WebUtils.BuildQuery(batchRequest.PublicParams);
+                if (!string.IsNullOrEmpty(publicParamStr))
+                {
+                    requestBody.Append(BATCH_API_PUBLIC_PARAMETER).Append(publicParamStr).Append(separator);
+                }
+
+                // 组装每个API的请求参数
+                for (int i = 0; i < requestList.Count; i++)
+                {
+                    ITopRequest<TopResponse> bRequest = requestList[i];
+                    bRequest.SetBatchApiOrder(i);
+                    IDictionary<string, string> apiParams = bRequest.GetParameters();
+                    // 如果单个API的方法和批量API的公共方法不一致,那么需要设置单个API的方法名称
+                    if (!string.IsNullOrEmpty(bRequest.GetApiName()) && !bRequest.GetApiName().Equals(batchRequest.PublicMethod))
+                    {
+                        apiParams.Add(Constants.METHOD, bRequest.GetApiName());
+                    }
+                    if (!string.IsNullOrEmpty(request.GetBatchApiSession()))
+                    {
+                        apiParams.Add(Constants.SESSION, bRequest.GetBatchApiSession());
+                    }
+                    if (!string.IsNullOrEmpty(request.GetTargetAppKey()))
+                    {
+                        apiParams.Add(Constants.TARGET_APP_KEY, bRequest.GetTargetAppKey());
+                    }
+
+                    string apiParamStr = WebUtils.BuildQuery(apiParams);
+                    if (string.IsNullOrEmpty(apiParamStr))
+                    {
+                        apiParamStr = "N";
+                    }
+                    requestBody.Append(apiParamStr);
+                    if (i != requestList.Count - 1)
+                    {
+                        requestBody.Append(separator);
+                    }
+                }
+
+                string apiBody = requestBody.ToString();
+
+                // 添加签名参数
+                parameters.Add(Constants.SIGN, TopUtils.SignTopRequest(parameters, apiBody, appSecret, Constants.SIGN_METHOD_HMAC));
+
+                // 发起批量请求
+                string fullUrl = WebUtils.BuildRequestUrl(this.batchServerUrl, parameters);
+                string rsp = webUtils.DoPost(fullUrl, Encoding.UTF8.GetBytes(apiBody), BATCH_API_CONTENT_TYPE, batchRequest.GetHeaderParameters());
+
+                // 构造响应解释器
+                ITopParser<TopResponse> parser = null;
+                if (Constants.FORMAT_XML.Equals(format))
+                {
+                    parser = new TopXmlParser<TopResponse>();
+                }
+                else
+                {
+                    if (this.useSimplifyJson)
+                    {
+                        parser = new TopSimplifyJsonParser<TopResponse>();
+                    }
+                    else
+                    {
+                        parser = new TopJsonParser<TopResponse>();
+                    }
+                }
+
+                // 解释响应结果
+                TopBatchResponse batchResponse = new TopBatchResponse();
+                batchResponse.Body = rsp;
+
+                string[] responseArray = batchResponse.Body.Split(new string[] { separator }, StringSplitOptions.None);
+                // 批量API在走单通道验证时没通过,如前面验证,此时只有一个报错信息
+                if (responseArray.Length > 0 && responseArray.Length != requestList.Count)
+                {
+                    TopResponse tRsp = parser.Parse(responseArray[0], requestList[0].GetType().BaseType.GetGenericArguments()[0]);
+                    batchResponse.ErrCode = tRsp.ErrCode;
+                    batchResponse.ErrMsg = tRsp.ErrMsg;
+                    batchResponse.SubErrCode = tRsp.SubErrCode;
+                    batchResponse.SubErrMsg = tRsp.SubErrMsg;
+                }
+                else
+                {
+                    for (int i = 0; i < responseArray.Length; i++)
+                    {
+                        TopResponse tRsp = parser.Parse(responseArray[i], requestList[i].GetType().BaseType.GetGenericArguments()[0]);
+                        tRsp.Body = responseArray[i];
+                        batchResponse.AddResponse(tRsp);
+                    }
+                }
+
+                if (batchResponse.IsError)
+                {
+                    TimeSpan latency = new TimeSpan(DateTime.Now.Ticks - start);
+                    TraceApiError(appKey, "BatchApi", batchServerUrl, parameters, latency.TotalMilliseconds, batchResponse.Body);
+                }
+
+                return batchResponse as T;
+            }
+            catch (Exception e)
+            {
+                TimeSpan latency = new TimeSpan(DateTime.Now.Ticks - start);
+                TraceApiError(appKey, "BatchApi", batchServerUrl, parameters, latency.TotalMilliseconds, e.GetType() + ": " + e.Message);
+                throw e;
+            }
+        }
+
+        private bool IsSameRequest<T>(List<ITopRequest<T>> requestList) where T : TopResponse
+        {
+            if (requestList != null && requestList.Count > 1) // 只有两个或以上的请求才考虑合并
+            {
+                string firstMethod = requestList[0].GetApiName();
+                for (int i = 1; i < requestList.Count; i++)
+                {
+                    string currentMethod = requestList[i].GetApiName();
+                    if (!firstMethod.Equals(currentMethod))
+                    {
+                        return false;
+                    }
+                }
+            }
+            return true;
+        }
+
+        private static string BuildApiServerUrl(string batchServerUrl)
+        {
+            if (batchServerUrl.Contains("/router/batch"))
+            {
+                return batchServerUrl.Replace("/router/batch", "/router/rest");
+            }
+            return batchServerUrl;
+        }
+    }
+}

+ 102 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Cluster/ClusterManager.cs

@@ -0,0 +1,102 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading;
+
+namespace Top.Api.Cluster
+{
+    public sealed class ClusterManager
+    {
+        private static readonly Random random = new Random();
+        private static readonly Object initLock = new Object();
+        private static volatile DnsConfig dnsConfig = null;
+        private static volatile Thread refreshThread = null;
+
+        public static T GetElementByWeight<T>(List<T> list) where T : Weightable
+        {
+            T selected = null;
+            double totalWeight = 0d;
+            foreach (T element in list)
+            {
+                double r = random.NextDouble() * (element.Weight + totalWeight);
+                if (r >= totalWeight)
+                {
+                    selected = element;
+                }
+                totalWeight += element.Weight;
+            }
+            return selected;
+        }
+
+        public static DnsConfig GetDnsConfigFromCache()
+        {
+            return dnsConfig;
+        }
+
+        public static void InitRefreshThread(ITopClient client)
+        {
+            if (refreshThread == null)
+            {
+                lock (initLock)
+                {
+                    if (refreshThread == null)
+                    {
+                        try
+                        {
+                            DnsConfig remoteConfig = GetDnsConfigFromTop(client);
+                            if (dnsConfig == null)
+                            {
+                                dnsConfig = remoteConfig;
+                            }
+                            else if (remoteConfig != null && remoteConfig.GetVersion() > dnsConfig.GetVersion())
+                            {
+                                dnsConfig = remoteConfig;
+                            }
+                        }
+                        catch (TopException e)
+                        {
+                            if ("22".Equals(e.ErrorCode))
+                            {
+                                return; // 如果HTTP DNS服务不存在,则退出守护线程
+                            }
+                        }
+
+                        refreshThread = new Thread(o =>
+                        {
+                            while (true)
+                            {
+                                try
+                                {
+                                    Thread.Sleep(dnsConfig.GetRefreshInterval() * 60 * 1000);
+                                    dnsConfig = GetDnsConfigFromTop(client);
+                                }
+                                catch (Exception e)
+                                {
+                                    Console.WriteLine(e.StackTrace);
+                                    Thread.Sleep(3 * 1000); // 出错则过3秒重试
+                                }
+                            }
+                        });
+                        refreshThread.IsBackground = true;
+                        refreshThread.Name = "HTTP_DNS_REFRESH_THREAD";
+                        refreshThread.Start();
+                    }
+                }
+            }
+        }
+
+        private static DnsConfig GetDnsConfigFromTop(ITopClient client)
+        {
+            HttpdnsGetRequest req = new HttpdnsGetRequest();
+            HttpdnsGetResponse rsp = client.Execute(req);
+            if (!rsp.IsError)
+            {
+                return DnsConfig.parse(rsp.Result);
+            }
+            else
+            {
+                throw new TopException(rsp.ErrCode, rsp.ErrMsg);
+            }
+        }
+    }
+}

+ 265 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Cluster/DnsConfig.cs

@@ -0,0 +1,265 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Xml.Serialization;
+using Top.Api.Util;
+
+namespace Top.Api.Cluster
+{
+    public class Weightable
+    {
+        public double Weight { get; set; }
+    }
+    public class ApiRule : Weightable
+    {
+        public string Name { get; set; }
+    }
+    public class ApiConfig
+    {
+        public string User { get; set; }
+
+        public List<ApiRule> Rules { get; set; }
+    }
+
+    public class VipRule : Weightable
+    {
+        public string Vip { get; set; }
+    }
+
+    public class EnvConfig
+    {
+        public string Domain { get; set; }
+
+        public string Protocol { get; set; }
+
+        public List<VipRule> VipRules { get; set; }
+    }
+    public class DnsConfig
+    {
+        public IDictionary<string, string> GlobalDict { get; set; }
+
+        public IDictionary<string, ApiConfig> ApiDict { get; set; }
+
+        public IDictionary<string, List<EnvConfig>> EnvDict { get; set; }
+
+        public IDictionary<string, IDictionary<string, string>> UserDict { get; set; }
+
+        public static DnsConfig parse(string json)
+        {
+            DnsConfig dnsConfig = new DnsConfig();
+            IDictionary root = TopUtils.JsonToObject(json) as IDictionary;
+            foreach (string configType in root.Keys)
+            {
+                if ("config".Equals(configType))
+                {
+                    dnsConfig.GlobalDict = new Dictionary<string, string>();
+                    IDictionary globalInfo = root[configType] as IDictionary;
+                    foreach (string key in globalInfo.Keys)
+                    {
+                        dnsConfig.GlobalDict[key] = globalInfo[key] as string;
+                    }
+                }
+                else if ("env".Equals(configType))
+                {
+                    IDictionary envInfos = root[configType] as IDictionary;
+                    dnsConfig.EnvDict = new Dictionary<string, List<EnvConfig>>();
+                    foreach (string envName in envInfos.Keys)
+                    {
+                        IDictionary envInfo = envInfos[envName] as IDictionary;
+                        List<EnvConfig> envConfigs = new List<EnvConfig>();
+
+                        foreach (string domainName in envInfo.Keys)
+                        {
+                            IDictionary domainInfo = envInfo[domainName] as IDictionary;
+                            EnvConfig envConfig = new EnvConfig();
+                            envConfig.Domain = domainName as string;
+                            envConfig.Protocol = domainInfo["proto"] as string;
+                            IList vipInfos = domainInfo["vip"] as IList;
+                            List<VipRule> vipRules = new List<VipRule>();
+                            foreach (string vipInfo in vipInfos)
+                            {
+                                string[] vipInfoTmp = vipInfo.ToString().Split('|');
+                                VipRule vipRule = new VipRule();
+                                vipRule.Vip = vipInfoTmp[0];
+                                vipRule.Weight = double.Parse(vipInfoTmp[1]);
+                                vipRules.Add(vipRule);
+                            }
+                            envConfig.VipRules = vipRules;
+                            envConfigs.Add(envConfig);
+                        }
+
+                        dnsConfig.EnvDict[envName] = envConfigs;
+                    }
+                }
+                else if ("api".Equals(configType))
+                {
+                    dnsConfig.ApiDict = new Dictionary<string, ApiConfig>();
+                    IDictionary apiInfos = root[configType] as IDictionary;
+                    foreach (string apiName in apiInfos.Keys)
+                    {
+                        IDictionary apiInfo = apiInfos[apiName] as IDictionary;
+                        ApiConfig apiConfig = new ApiConfig();
+                        apiConfig.User = apiInfo["user"] as string;
+                        List<ApiRule> apiRules = new List<ApiRule>();
+                        IList apiRuleInfos = apiInfo["rule"] as IList;
+                        foreach (string apiRuleInfo in apiRuleInfos)
+                        {
+                            string[] apiRuleInfoTmp = apiRuleInfo.ToString().Split('|');
+                            ApiRule apiRule = new ApiRule();
+                            apiRule.Name = apiRuleInfoTmp[0];
+                            apiRule.Weight = double.Parse(apiRuleInfoTmp[1]);
+                            apiRules.Add(apiRule);
+                        }
+                        apiConfig.Rules = apiRules;
+                        dnsConfig.ApiDict[apiName] = apiConfig;
+                    }
+                }
+                else if ("user".Equals(configType))
+                {
+                    dnsConfig.UserDict = new Dictionary<string, IDictionary<string, string>>();
+                    IDictionary userInfos = root[configType] as IDictionary;
+                    foreach (string routeName in userInfos.Keys)
+                    {
+                        IDictionary envInfos = userInfos[routeName] as IDictionary;
+                        IDictionary<string, string> tags = new Dictionary<string, string>();
+                        foreach (string envName in envInfos.Keys)
+                        {
+                            IList tagInfos = envInfos[envName] as IList;
+                            foreach (string tagName in tagInfos)
+                            {
+                                tags.Add(tagName, envName);
+                            }
+                        }
+                        dnsConfig.UserDict[routeName] = tags;
+                    }
+                }
+            }
+            return dnsConfig;
+        }
+
+        public string GetBestVipUrl(string serverUrl, string apiName, string session)
+        {
+            if (ApiDict.ContainsKey(apiName))
+            {
+                ApiConfig apiConfig = ApiDict[apiName];
+                if (!string.IsNullOrEmpty(session) && apiConfig.User != null)
+                {
+                    string flag = GetUserFlag(session);
+                    if (flag != null && UserDict.ContainsKey(apiConfig.User))
+                    {
+                        IDictionary<string, string> userEnvs = UserDict[apiConfig.User];
+                        if (userEnvs.ContainsKey(flag))
+                        {
+                            string userEnv = userEnvs[flag];
+                            if (EnvDict.ContainsKey(userEnv))
+                            {
+                                List<EnvConfig> envConfigs = EnvDict[userEnv];
+                                return GetEnvVipUrl(serverUrl, envConfigs);
+                            }
+                        }
+                    }
+                }
+                return GetApiVipUrl(serverUrl, apiConfig);
+            }
+            else
+            {
+                List<EnvConfig> envConfigs = EnvDict[GlobalDict["def_env"]];
+                return GetEnvVipUrl(serverUrl, envConfigs);
+            }
+        }
+
+        private string GetApiVipUrl(string serverUrl, ApiConfig apiConfig)
+        {
+            ApiRule apiRule = ClusterManager.GetElementByWeight(apiConfig.Rules);
+            if (EnvDict.ContainsKey(apiRule.Name))
+            {
+                List<EnvConfig> envConfigs = EnvDict[apiRule.Name];
+                return GetEnvVipUrl(serverUrl, envConfigs);
+            }
+            else
+            {
+                List<EnvConfig> envConfigs = EnvDict[GlobalDict["def_env"]];
+                return GetEnvVipUrl(serverUrl, envConfigs);
+            }
+        }
+
+        private string GetEnvVipUrl(string serverUrl, List<EnvConfig> envConfigs)
+        {
+            foreach (EnvConfig envConfig in envConfigs)
+            {
+                Uri uri = new Uri(serverUrl);
+                if (uri.Host.Equals(envConfig.Domain, StringComparison.OrdinalIgnoreCase) && uri.Scheme.Equals(envConfig.Protocol, StringComparison.OrdinalIgnoreCase))
+                {
+                    string vip = ClusterManager.GetElementByWeight<VipRule>(envConfig.VipRules).Vip;
+                    return serverUrl.Replace(envConfig.Domain, vip);
+                }
+            }
+            return serverUrl;
+        }
+
+        private string GetUserFlag(string session)
+        {
+            if (!string.IsNullOrEmpty(session) && session.Length > 5)
+            {
+                if (session.StartsWith("6") || session.StartsWith("7"))
+                {
+                    return session.Substring(session.Length - 1, 1);
+                }
+                else if (session.StartsWith("5") || session.StartsWith("8"))
+                {
+                    return session.Substring(5, 1);
+                }
+            }
+            return null;
+        }
+
+        public long GetVersion()
+        {
+            if (GlobalDict.ContainsKey("GlobalDict"))
+            {
+                return long.Parse(GlobalDict["GlobalDict"]);
+            }
+            else
+            {
+                return 0;
+            }
+        }
+
+        public int GetRefreshInterval()
+        {
+            if (GlobalDict.ContainsKey("interval"))
+            {
+                return int.Parse(GlobalDict["interval"]);
+            }
+            else
+            {
+                return 30; // 默认半小时刷新一次
+            }
+        }
+
+    }
+
+    public class HttpdnsGetResponse : TopResponse
+    {
+        [XmlElement("result")]
+        public string Result { get; set; }
+    }
+
+    public class HttpdnsGetRequest : BaseTopRequest<HttpdnsGetResponse>
+    {
+        public override string GetApiName()
+        {
+            return "taobao.httpdns.get";
+        }
+
+        public override IDictionary<string, string> GetParameters()
+        {
+            TopDictionary parameters = new TopDictionary();
+            return parameters;
+        }
+
+        public override void Validate()
+        {
+        }
+    }
+}

+ 40 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/ClusterTopClient.cs

@@ -0,0 +1,40 @@
+using Top.Api.Cluster;
+
+namespace Top.Api
+{
+    /// <summary>
+    /// 异地多活自动分配集群客户端。
+    /// </summary>
+    public class ClusterTopClient : DefaultTopClient
+    {
+        public ClusterTopClient(string serverUrl, string appKey, string appSecret)
+            : base(serverUrl, appKey, appSecret)
+        {
+            ClusterManager.InitRefreshThread(this);
+        }
+
+        public ClusterTopClient(string serverUrl, string appKey, string appSecret, string format)
+            : base(serverUrl, appKey, appSecret, format)
+        {
+            ClusterManager.InitRefreshThread(this);
+        }
+
+        internal override string GetServerUrl(string serverUrl, string apiName, string session)
+        {
+            DnsConfig dnsConfig = ClusterManager.GetDnsConfigFromCache();
+            if (dnsConfig == null)
+            {
+                return serverUrl;
+            }
+            else
+            {
+                return dnsConfig.GetBestVipUrl(serverUrl, apiName, session);
+            }
+        }
+
+        internal override string GetSdkVersion()
+        {
+            return Constants.SDK_VERSION_CLUSTER;
+        }
+    }
+}

+ 62 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Constants.cs

@@ -0,0 +1,62 @@
+
+namespace Top.Api
+{
+    public sealed class Constants
+    {
+        public const string CHARSET_UTF8 = "utf-8";
+
+        public const string DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
+        public const string DATE_TIME_MS_FORMAT = "yyyy-MM-dd HH:mm:ss.fff";
+
+        public const string SIGN_METHOD_MD5 = "md5";
+        public const string SIGN_METHOD_HMAC = "hmac";
+        public const string SIGN_METHOD_HMAC_SHA256 = "hmac-sha256";
+
+        public const string LOG_SPLIT = "^_^";
+        public const string LOG_FILE_NAME = "topsdk.log";
+
+        public const string ACCEPT_ENCODING = "Accept-Encoding";
+        public const string CONTENT_ENCODING = "Content-Encoding";
+        public const string CONTENT_ENCODING_GZIP = "gzip";
+
+        public const string ERROR_RESPONSE = "error_response";
+        public const string ERROR_CODE = "code";
+        public const string ERROR_MSG = "msg";
+
+        public const string QIMEN_CLOUD_ERROR_RESPONSE = "response";
+        public const string QIMEN_CLOUD_ERROR_CODE = "code";
+        public const string QIMEN_CLOUD_ERROR_MSG = "message";
+
+        public const string SDK_VERSION = "top-sdk-net-20220624";
+        public const string SDK_VERSION_CLUSTER = "top-sdk-net-cluster-20220624";
+
+        public const string APP_KEY = "app_key";
+        public const string FORMAT = "format";
+        public const string METHOD = "method";
+        public const string TIMESTAMP = "timestamp";
+        public const string VERSION = "v";
+        public const string SIGN = "sign";
+        public const string SIGN_METHOD = "sign_method";
+        public const string PARTNER_ID = "partner_id";
+        public const string SESSION = "session";
+        public const string FORMAT_XML = "xml";
+        public const string FORMAT_JSON = "json";
+        public const string SIMPLIFY = "simplify";
+        public const string TARGET_APP_KEY = "target_app_key";
+
+        public const string QM_ROOT_TAG_REQ = "request";
+        public const string QM_ROOT_TAG_RSP = "response";
+        public const string QM_CUSTOMER_ID = "customerId";
+        public const string QM_CONTENT_TYPE = "text/xml;charset=utf-8";
+        public const string QM_TARGETAPPKEY = "targetAppkey";
+
+        public const string CTYPE_DEFAULT = "application/octet-stream";
+        public const string CTYPE_FORM_DATA = "application/x-www-form-urlencoded";
+        public const string CTYPE_FILE_UPLOAD = "multipart/form-data";
+        public const string CTYPE_TEXT_XML = "text/xml";
+        public const string CTYPE_APPLICATION_XML = "application/xml";
+        public const string CTYPE_TEXT_PLAIN = "text/plain";
+        public const string CTYPE_APP_JSON = "application/json";
+        public const int READ_BUFFER_SIZE = 1024 * 4;
+    }
+}

+ 277 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/DefaultTopClient.cs

@@ -0,0 +1,277 @@
+using FastJSON;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Xml;
+using Top.Api.Parser;
+using Top.Api.Util;
+
+namespace Top.Api
+{
+    /// <summary>
+    /// 基于REST的TOP客户端。
+    /// </summary>
+    public class DefaultTopClient : ITopClient
+    {
+        internal string serverUrl;
+        internal string appKey;
+        internal string appSecret;
+        internal string format = Constants.FORMAT_XML;
+
+        internal WebUtils webUtils;
+        internal ITopLogger topLogger;
+        internal bool disableParser = false; // 禁用响应结果解释
+        internal bool disableTrace = false; // 禁用日志调试功能
+        internal bool useSimplifyJson = false; // 是否采用精简化的JSON返回
+        internal bool useGzipEncoding = true;  // 是否启用响应GZIP压缩
+        internal IDictionary<string, string> systemParameters; // 设置所有请求共享的系统级参数
+
+        #region DefaultTopClient Constructors
+
+        public DefaultTopClient(string serverUrl, string appKey, string appSecret)
+        {
+            this.appKey = appKey;
+            this.appSecret = appSecret;
+            this.serverUrl = serverUrl;
+            this.webUtils = new WebUtils();
+            this.topLogger = Top.Api.Log.Instance;
+        }
+
+        public DefaultTopClient(string serverUrl, string appKey, string appSecret, string format)
+            : this(serverUrl, appKey, appSecret)
+        {
+            this.format = format;
+        }
+
+        #endregion
+
+        public void SetTimeout(int timeout)
+        {
+            this.webUtils.Timeout = timeout;
+        }
+
+        public void SetReadWriteTimeout(int readWriteTimeout)
+        {
+            this.webUtils.ReadWriteTimeout = readWriteTimeout;
+        }
+
+        public void SetDisableParser(bool disableParser)
+        {
+            this.disableParser = disableParser;
+        }
+
+        public void SetDisableTrace(bool disableTrace)
+        {
+            this.disableTrace = disableTrace;
+        }
+
+        public void SetUseSimplifyJson(bool useSimplifyJson)
+        {
+            this.useSimplifyJson = useSimplifyJson;
+        }
+
+        public void SetUseGzipEncoding(bool useGzipEncoding)
+        {
+            this.useGzipEncoding = useGzipEncoding;
+        }
+
+        public void SetIgnoreSSLCheck(bool ignore)
+        {
+            this.webUtils.IgnoreSSLCheck = ignore;
+        }
+
+        /// <summary>
+        /// 禁用本地代理自动检测功能,加快连接建立速度
+        /// </summary>
+        public void SetDisableWebProxy(bool disable)
+        {
+            this.webUtils.DisableWebProxy = disable;
+        }
+
+        /// <summary>
+        /// 设置单个网站最大并发连接数,桌面端默认是2,服务器端默认是10,对于桌面端并发请求多的,可以适当调高。
+        /// </summary>
+        /// <param name="limit"></param>
+        public void SetMaxConnectionLimit(int limit)
+        {
+            System.Net.ServicePointManager.DefaultConnectionLimit = limit;
+        }
+
+        public void SetSystemParameters(IDictionary<string, string> systemParameters)
+        {
+            this.systemParameters = systemParameters;
+        }
+
+        #region ITopClient Members
+
+        public virtual T Execute<T>(ITopRequest<T> request) where T : TopResponse
+        {
+            return DoExecute<T>(request, null, DateTime.Now);
+        }
+
+        public virtual T Execute<T>(ITopRequest<T> request, string session) where T : TopResponse
+        {
+            return DoExecute<T>(request, session, DateTime.Now);
+        }
+
+        public virtual T Execute<T>(ITopRequest<T> request, string session, DateTime timestamp) where T : TopResponse
+        {
+            return DoExecute<T>(request, session, timestamp);
+        }
+
+        #endregion
+
+        private T DoExecute<T>(ITopRequest<T> request, string session, DateTime timestamp) where T : TopResponse
+        {
+            long start = DateTime.Now.Ticks;
+
+            // 提前检查业务参数
+            try
+            {
+                request.Validate();
+            }
+            catch (TopException e)
+            {
+                return CreateErrorResponse<T>(e.ErrorCode, e.ErrorMsg);
+            }
+
+            // 添加协议级请求参数
+            TopDictionary txtParams = new TopDictionary(request.GetParameters());
+            txtParams.Add(Constants.METHOD, request.GetApiName());
+            txtParams.Add(Constants.VERSION, "2.0");
+            txtParams.Add(Constants.SIGN_METHOD, Constants.SIGN_METHOD_HMAC);
+            txtParams.Add(Constants.APP_KEY, appKey);
+            txtParams.Add(Constants.FORMAT, format);
+            txtParams.Add(Constants.PARTNER_ID, GetSdkVersion());
+            txtParams.Add(Constants.TIMESTAMP, timestamp);
+            txtParams.Add(Constants.TARGET_APP_KEY, request.GetTargetAppKey());
+            txtParams.Add(Constants.SESSION, session);
+            txtParams.AddAll(this.systemParameters);
+
+            if (this.useSimplifyJson)
+            {
+                txtParams.Add(Constants.SIMPLIFY, "true");
+            }
+
+            // 添加签名参数
+            txtParams.Add(Constants.SIGN, TopUtils.SignTopRequest(txtParams, appSecret, Constants.SIGN_METHOD_HMAC));
+
+            // 添加头部参数
+            if (this.useGzipEncoding)
+            {
+                request.GetHeaderParameters()[Constants.ACCEPT_ENCODING] = Constants.CONTENT_ENCODING_GZIP;
+            }
+
+            string realServerUrl = GetServerUrl(this.serverUrl, request.GetApiName(), session);
+            string reqUrl = WebUtils.BuildRequestUrl(realServerUrl, txtParams);
+            try
+            {
+                string body;
+                if (request is ITopUploadRequest<T>) // 是否需要上传文件
+                {
+                    ITopUploadRequest<T> uRequest = (ITopUploadRequest<T>)request;
+                    IDictionary<string, FileItem> fileParams = TopUtils.CleanupDictionary(uRequest.GetFileParameters());
+                    body = webUtils.DoPost(realServerUrl, txtParams, fileParams, request.GetHeaderParameters());
+                }
+                else
+                {
+                    body = webUtils.DoPost(realServerUrl, txtParams, request.GetHeaderParameters());
+                }
+
+                // 解释响应结果
+                T rsp;
+                if (disableParser)
+                {
+                    rsp = Activator.CreateInstance<T>();
+                    rsp.Body = body;
+                }
+                else
+                {
+                    if (Constants.FORMAT_XML.Equals(format))
+                    {
+                        ITopParser<T> tp = new TopXmlParser<T>();
+                        rsp = tp.Parse(body);
+                    }
+                    else
+                    {
+                        ITopParser<T> tp;
+                        if (useSimplifyJson)
+                        {
+                            tp = new TopSimplifyJsonParser<T>();
+                        }
+                        else
+                        {
+                            tp = new TopJsonParser<T>();
+                        }
+                        rsp = tp.Parse(body);
+                    }
+                }
+
+                // 追踪错误的请求
+                if (rsp.IsError)
+                {
+                    TimeSpan latency = new TimeSpan(DateTime.Now.Ticks - start);
+                    TraceApiError(appKey, request.GetApiName(), serverUrl, txtParams, latency.TotalMilliseconds, rsp.Body);
+                }
+                return rsp;
+            }
+            catch (Exception e)
+            {
+                TimeSpan latency = new TimeSpan(DateTime.Now.Ticks - start);
+                TraceApiError(appKey, request.GetApiName(), serverUrl, txtParams, latency.TotalMilliseconds, e.GetType() + ": " + e.Message);
+                throw e;
+            }
+        }
+
+        internal virtual string GetServerUrl(string serverUrl, string apiName, string session)
+        {
+            return serverUrl;
+        }
+
+        internal virtual string GetSdkVersion()
+        {
+            return Constants.SDK_VERSION;
+        }
+
+        internal T CreateErrorResponse<T>(string errCode, string errMsg) where T : TopResponse
+        {
+            T rsp = Activator.CreateInstance<T>();
+            rsp.ErrCode = errCode;
+            rsp.ErrMsg = errMsg;
+
+            if (Constants.FORMAT_XML.Equals(format))
+            {
+                XmlDocument root = new XmlDocument();
+                XmlElement bodyE = root.CreateElement(Constants.ERROR_RESPONSE);
+                XmlElement codeE = root.CreateElement(Constants.ERROR_CODE);
+                codeE.InnerText = errCode;
+                bodyE.AppendChild(codeE);
+                XmlElement msgE = root.CreateElement(Constants.ERROR_MSG);
+                msgE.InnerText = errMsg;
+                bodyE.AppendChild(msgE);
+                root.AppendChild(bodyE);
+                rsp.Body = root.OuterXml;
+            }
+            else
+            {
+                IDictionary<string, object> errObj = new Dictionary<string, object>();
+                errObj.Add(Constants.ERROR_CODE, errCode);
+                errObj.Add(Constants.ERROR_MSG, errMsg);
+                IDictionary<string, object> root = new Dictionary<string, object>();
+                root.Add(Constants.ERROR_RESPONSE, errObj);
+
+                string body = JSON.ToJSON(root);
+                rsp.Body = body;
+            }
+            return rsp;
+        }
+
+        internal void TraceApiError(string appKey, string apiName, string url, Dictionary<string, string> parameters, double latency, string errorMessage)
+        {
+            if (!disableTrace)
+            {
+                this.topLogger.TraceApiError(appKey, apiName, url, parameters, latency, errorMessage);
+            }
+        }
+    }
+}

+ 501 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/DefaultTopLogger.cs

@@ -0,0 +1,501 @@
+using Top.Api.Util;
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Text;
+using System.Threading;
+
+namespace Top.Api
+{
+
+    /// <summary>
+    /// 日志类 
+    /// 队列 可年/月/周/日/大小分割
+    /// 调用方法:
+    ///  Log.Instance.LogDirectory=@"C:\"; 默认为程序运行目录
+    ///  Log.Instance.FileNamePrefix="cxd";默认为log_
+    ///  Log.Instance.CurrentMsgType = MsgLevel.Debug;默认为Error
+    ///  Log.Instance.logFileSplit = LogFileSplit.Daily; 日志拆分类型LogFileSplit.Sizely 大小
+    ///  Log.Instance.MaxFileSize = 1024; 只有LogFileSplit.Sizely的时候配置有效
+    ///  Log.Instance.LogWrite("aa");
+    ///  Log.Instance.LogWrite("aa", MsgLevel.Debug);
+    /// </summary>
+    public class Log : IDisposable, ITopLogger
+    {
+        private static Log _instance = null;
+        private static readonly object _synObject = new object();
+        /// <summary>
+        ///单例
+        /// </summary>
+        public static Log Instance
+        {
+            get
+            {
+                if (null == _instance)
+                {
+                    lock (_synObject)
+                    {
+                        if (null == _instance)
+                        {
+                            _instance = new Log();
+                        }
+                    }
+                }
+                return _instance;
+            }
+        }
+        /// <summary>
+        /// 日志对象的缓存队列
+        /// </summary>
+        private static Queue<Msg> _msgs;
+        /// <summary>
+        /// 日志写入线程的控制标记  ture写中|false没有写
+        /// </summary>
+        private bool _state;
+        private string _logDirectory = AppDomain.CurrentDomain.BaseDirectory;
+
+        private bool isDebugEnabled = false;
+        /// <summary>
+        /// 日志文件存放目录
+        /// </summary>
+        public string LogDirectory
+        {
+            get { return _logDirectory; }
+            set { _logDirectory = value; }
+        }
+        private LogFileSplit _logFileSplit = LogFileSplit.Sizely;
+        /// <summary>
+        /// 日志拆分类型
+        /// </summary>
+        public LogFileSplit logFileSplit
+        {
+            get { return _logFileSplit; }
+            set { _logFileSplit = value; }
+        }
+        private MsgLevel _currentLogLevel = MsgLevel.Infor;
+        /// <summary>
+        /// 当前日志记录等级
+        /// </summary>
+        public MsgLevel CurrentMsgType
+        {
+            get { return _currentLogLevel; }
+            set { _currentLogLevel = value; }
+        }
+        /// <summary>
+        /// 当前负责记录日志文件的名称
+        /// </summary>
+        private string _currentFileName = "1.log";
+        private string _fileNamePrefix = "topsdk_";
+        /// <summary>
+        /// 日志的前缀名称,默认为log_
+        /// </summary>
+        public string FileNamePrefix
+        {
+            get { return _fileNamePrefix; }
+            set { _fileNamePrefix = value; }
+        }
+        /// <summary>
+        /// 日志文件生命周期的时间标记
+        /// </summary>
+        private DateTime _CurrentFileTimeSign = new DateTime();
+        private int _maxFileSize = 1024;
+        /// <summary>
+        /// 单个日志文件默认大小(单位:兆)
+        /// </summary>
+        public int MaxFileSize
+        {
+            get { return _maxFileSize; }
+            set { _maxFileSize = value; }
+        }
+        /// <summary>
+        /// 文件后缀号
+        /// </summary>
+        private int _fileSymbol = 0;
+
+        /// <summary>
+        /// 当前文件大小(单位:B)
+        /// </summary>
+        private long _fileSize = 0;
+        /// <summary>
+        /// 日志文件写入流对象
+        /// </summary>
+        private StreamWriter _writer;
+
+        /// <summary>
+        /// 创建日志对象的新实例,根据指定的日志文件路径和指定的日志文件创建类型
+        /// </summary>
+        private Log()
+        {
+            if (_msgs == null)
+            {
+                GetCurrentFilename();
+                _state = true;
+                _msgs = new Queue<Msg>();
+                Thread thread = new Thread(work);
+                thread.Start();
+            }
+        }
+        //日志文件写入线程执行的方法
+        private void work()
+        {
+            while (true)
+            {
+                //判断队列中是否存在待写入的日志
+                if (_msgs.Count > 0)
+                {
+                    Msg msg = null;
+                    lock (_msgs)
+                    {
+                        msg = _msgs.Dequeue();
+                        if (msg != null)
+                        {
+                            FileWrite(msg);
+                        }
+                    }
+                }
+                else
+                {
+                    //判断是否已经发出终止日志并关闭的消息
+                    if (_state)
+                    {
+                        Thread.Sleep(1);
+                    }
+                    else
+                    {
+                        FileClose();
+                    }
+                }
+            }
+        }
+        /// <summary>
+        /// 根据日志类型获取日志文件名,并同时创建文件到期的时间标记
+        /// 通过判断文件的到期时间标记将决定是否创建新文件。
+        /// </summary>
+        /// <returns></returns>
+        private
+            void GetCurrentFilename()
+        {
+            DateTime now = DateTime.Now;
+            string format = "";
+            switch (_logFileSplit)
+            {
+                case LogFileSplit.Daily:
+                    _CurrentFileTimeSign = new DateTime(now.Year, now.Month, now.Day);
+                    _CurrentFileTimeSign = _CurrentFileTimeSign.AddDays(1);
+                    format = now.ToString("yyyyMMdd'.log'");
+                    break;
+                case LogFileSplit.Weekly:
+                    _CurrentFileTimeSign = new DateTime(now.Year, now.Month, now.Day);
+                    _CurrentFileTimeSign = _CurrentFileTimeSign.AddDays(7);
+                    format = now.ToString("yyyyMMdd'.log'");
+                    break;
+                case LogFileSplit.Monthly:
+                    _CurrentFileTimeSign = new DateTime(now.Year, now.Month, 1);
+                    _CurrentFileTimeSign = _CurrentFileTimeSign.AddMonths(1);
+                    format = now.ToString("yyyyMM'.log'");
+                    break;
+                case LogFileSplit.Annually:
+                    _CurrentFileTimeSign = new DateTime(now.Year, 1, 1);
+                    _CurrentFileTimeSign = _CurrentFileTimeSign.AddYears(1);
+                    format = now.ToString("yyyy'.log'");
+                    break;
+                default:
+                    _fileSymbol++;
+                    format = _fileSymbol.ToString() + ".log";
+                    break;
+            }
+            if (File.Exists(Path.Combine(LogDirectory, _currentFileName)))
+            {
+                _fileSize = new FileInfo(Path.Combine(LogDirectory, _currentFileName)).Length;
+            }
+            else
+            {
+                _fileSize = 0;
+            }
+            _currentFileName = _fileNamePrefix + format.Trim();
+        }
+        //写入日志文本到文件的方法
+        private void FileWrite(Msg msg)
+        {
+            try
+            {
+                if (_writer == null)
+                {
+                    FileOpen();
+                }
+                if (_writer != null)
+                {
+                    //判断文件到期标志,如果当前文件到期则关闭当前文件创建新的日志文件
+                    if ((_logFileSplit != LogFileSplit.Sizely && DateTime.Now >= _CurrentFileTimeSign) ||
+                        (_logFileSplit == LogFileSplit.Sizely && ((double)_fileSize / 1048576) > _maxFileSize))
+                    {
+                        GetCurrentFilename();
+                        FileClose();
+                        FileOpen();
+                    }
+                    _writer.Write(msg.datetime);
+                    _writer.Write('\t');
+                    _writer.Write(msg.type);
+                    _writer.Write('\t');
+                    _writer.WriteLine(msg.text);
+                    _fileSize += System.Text.Encoding.UTF8.GetBytes(msg.ToString()).Length;
+                    _writer.Flush();
+                }
+            }
+            catch (Exception e)
+            {
+                Console.Out.Write(e);
+            }
+        }
+        //打开文件准备写入
+        private void FileOpen()
+        {
+            _writer = new StreamWriter(LogDirectory + _currentFileName, true, Encoding.UTF8);
+        }
+        //关闭打开的日志文件
+        private void FileClose()
+        {
+            if (_writer != null)
+            {
+                _writer.Flush();
+                _writer.Close();
+                _writer.Dispose();
+                _writer = null;
+            }
+        }
+        /// <summary>
+        /// 写入新日志,根据指定的日志对象Msg
+        /// </summary>
+        /// <param name="msg">日志内容对象</param>
+        private void LogWrite(Msg msg)
+        {
+            if (msg.type < CurrentMsgType)
+                return;
+            if (_msgs != null)
+            {
+                lock (_msgs)
+                {
+                    _msgs.Enqueue(msg);
+                }
+            }
+        }
+        /// <summary>
+        /// 写入新日志,根据指定的日志内容和信息类型,采用当前时间为日志时间写入新日志
+        /// </summary>
+        /// <param name="text">日志内容</param>
+        /// <param name="type">信息类型</param>
+        public void LogWrite(string text, MsgLevel type)
+        {
+            LogWrite(new Msg(text, type));
+        }
+        /// <summary>
+        /// 写入新日志,根据指定的日志内容
+        /// </summary>
+        /// <param name="text">日志内容</param>
+        public void LogWrite(string text)
+        {
+            LogWrite(text, MsgLevel.Debug);
+        }
+        /// <summary>
+        /// 写入新日志,根据指定的日志时间、日志内容和信息类型写入新日志
+        /// </summary>
+        /// <param name="dt">日志时间</param>
+        /// <param name="text">日志内容</param>
+        /// <param name="type">信息类型</param>
+        public void LogWrite(DateTime dt, string text, MsgLevel type)
+        {
+            LogWrite(new Msg(dt, text, type));
+        }
+        /// <summary>
+        /// 写入新日志,根据指定的异常类和信息类型写入新日志
+        /// </summary>
+        /// <param name="e">异常对象</param>
+        /// <param name="type">信息类型</param>
+        public void LogWrite(Exception e)
+        {
+            LogWrite(new Msg(e.Message, MsgLevel.Error));
+        }
+        /// <summary>
+        /// 销毁日志对象
+        /// </summary>
+        public void Dispose()
+        {
+            _state = false;
+        }
+
+        public void TraceApiError(string appKey, string apiName, string url, System.Collections.Generic.Dictionary<string, string> parameters, double latency, string errorMessage)
+        {
+            StringBuilder info = new StringBuilder();
+            info.Append(appKey);
+            info.Append(Constants.LOG_SPLIT);
+            info.Append(apiName);
+            info.Append(Constants.LOG_SPLIT);
+            info.Append(TopUtils.GetIntranetIp());
+            info.Append(Constants.LOG_SPLIT);
+            info.Append(System.Environment.OSVersion.VersionString);
+            info.Append(Constants.LOG_SPLIT);
+            info.Append(latency);
+            info.Append(Constants.LOG_SPLIT);
+            info.Append(url);
+            info.Append(Constants.LOG_SPLIT);
+            info.Append(WebUtils.BuildQuery(parameters));
+            info.Append(Constants.LOG_SPLIT);
+            info.Append(errorMessage);
+            this.Error(info.ToString());
+        }
+
+        public void Error(string message)
+        {
+            this.LogWrite(message,MsgLevel.Error);
+        }
+
+        public void Error(string format, params object[] args)
+        {
+            this.LogWrite(string.Format(format, args), MsgLevel.Error);
+        }
+
+        public void Warn(string message)
+        {
+            this.LogWrite(message, MsgLevel.Warn);
+        }
+
+        public void Warn(string format, params object[] args)
+        {
+            this.LogWrite(string.Format(format, args), MsgLevel.Warn);
+        }
+
+        public void Info(string message)
+        {
+            this.LogWrite(message, MsgLevel.Infor);
+        }
+
+        public void Info(string format, params object[] args)
+        {
+            this.LogWrite(string.Format(format, args), MsgLevel.Infor);
+        }
+
+        public bool IsDebugEnabled()
+        {
+            return this.isDebugEnabled;
+        }
+
+        public void Debug(string message)
+        {
+            if (isDebugEnabled)
+            {
+                this.LogWrite(message, MsgLevel.Debug);
+            }
+        }
+
+        public void Debug(string format, params object[] args)
+        {
+            if (isDebugEnabled)
+            {
+                this.LogWrite(string.Format(format, args), MsgLevel.Debug);
+            }
+        }
+    }
+
+    /// <summary>
+    /// 一个日志记录的对象
+    /// </summary>
+    public class Msg
+    {
+        /// <summary>
+        /// 创建新的日志记录实例;日志记录的内容为空,消息类型为MsgType.Unknown,日志时间为当前时间
+        /// </summary>
+        public Msg()
+            : this("", MsgLevel.Debug)
+        {
+        }
+        /// <summary>
+        /// 创建新的日志记录实例;日志事件为当前时间
+        /// </summary>
+        /// <param name="t">日志记录的文本内容</param>
+        /// <param name="p">日志记录的消息类型</param>
+        public Msg(string t, MsgLevel p)
+            : this(DateTime.Now, t, p)
+        {
+        }
+        /// <summary>
+        /// 创建新的日志记录实例;
+        /// </summary>
+        /// <param name="dt">日志记录的时间</param>
+        /// <param name="t">日志记录的文本内容</param>
+        /// <param name="p">日志记录的消息类型</param>
+        public Msg(DateTime dt, string t, MsgLevel p)
+        {
+            datetime = dt;
+            type = p;
+            text = t;
+        }
+        /// <summary>
+        /// 日志记录的时间
+        /// </summary>
+        public DateTime datetime { get; set; }
+        /// <summary>
+        ///日志记录的内容
+        /// </summary>
+        public string text { get; set; }
+        /// <summary>
+        /// 日志等级
+        /// </summary>
+        public MsgLevel type { get; set; }
+
+        public new string ToString()
+        {
+            return datetime.ToString(CultureInfo.InvariantCulture) + "\t" + text + "\n";
+        }
+    }
+    /// <summary>
+    /// 日志文件拆分的枚举
+    /// </summary>
+    /// <remarks>日志类型枚举指示日志文件创建的方式,如果日志比较多可考虑每天创建一个日志文件
+    /// 如果日志量比较小可考虑每周、每月或每年创建一个日志文件</remarks>
+    public enum LogFileSplit
+    {
+        /// <summary>
+        /// 此枚举指示每天创建一个新的日志文件
+        /// </summary>
+        Daily,
+        /// <summary>
+        /// 此枚举指示每周创建一个新的日志文件
+        /// </summary>
+        Weekly,
+        /// <summary>
+        /// 此枚举指示每月创建一个新的日志文件
+        /// </summary>
+        Monthly,
+        /// <summary>
+        /// 此枚举指示每年创建一个新的日志文件
+        /// </summary>
+        Annually,
+        /// <summary>
+        /// 日志文件大小超过指定的创建一个新的日志文件,MaxFileSize指定大小
+        /// </summary>
+        Sizely
+    }
+    /// <summary>
+    /// 日志等级类型 Debug=0 Infor Warn Error
+    /// </summary>
+    public enum MsgLevel
+    {
+        /// <summary>
+        /// 调试信息
+        /// </summary>
+        Debug = 0,
+        /// <summary>
+        /// 指示普通信息类型的日志记录
+        /// </summary>
+        Infor,
+        /// <summary>
+        /// 指示警告信息类型的日志记录
+        /// </summary>
+        Warn,
+        /// <summary>
+        /// 指示错误信息类型的日志记录
+        /// </summary>
+        Error
+    }
+}

+ 63 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/DingTalk/BaseDingTalkRequest.cs

@@ -0,0 +1,63 @@
+using System;
+using System.Collections.Generic;
+using Top.Api;
+
+namespace DingTalk.Api
+{
+    /// <summary>
+    /// 基础TOP请求类,存放一些通用的请求参数。
+    /// </summary>
+    public abstract class BaseDingTalkRequest<T> : IDingTalkRequest<T> where T : DingTalkResponse
+    {
+        /// <summary>
+        /// HTTP请求URL参数
+        /// </summary>
+        internal TopDictionary otherParams;
+        /// <summary>
+        /// HTTP请求头参数
+        /// </summary>
+        private TopDictionary headerParams;
+
+        private String httpMethod = "POST";
+
+        public void AddOtherParameter(string key, string value)
+        {
+            if (this.otherParams == null)
+            {
+                this.otherParams = new TopDictionary();
+            }
+            this.otherParams.Add(key, value);
+        }
+
+        public void AddHeaderParameter(string key, string value)
+        {
+            GetHeaderParameters().Add(key, value);
+        }
+
+        public IDictionary<string, string> GetHeaderParameters()
+        {
+            if (this.headerParams == null)
+            {
+                this.headerParams = new TopDictionary();
+            }
+            return this.headerParams;
+        }
+
+        public abstract string GetApiName();
+
+        public abstract string GetApiCallType();
+
+        public abstract void Validate();
+
+        public abstract IDictionary<string, string> GetParameters();
+
+        public void SetHttpMethod(String httpMethod) {
+            this.httpMethod = httpMethod;
+        }
+
+        public string GetHttpMethod()
+        {
+            return httpMethod;
+        }
+    }
+}

+ 438 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/DingTalk/DefaultDingTalkClient.cs

@@ -0,0 +1,438 @@
+using FastJSON;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Xml;
+using Top.Api.Parser;
+using Top.Api.Util;
+using Top.Api;
+using Top.Api.DingTalk;
+
+namespace DingTalk.Api
+{
+    /// <summary>
+    /// 基于REST的TOP客户端。
+    /// </summary>
+    public class DefaultDingTalkClient : IDingTalkClient
+    {
+        internal string serverUrl;
+        internal string format = Constants.FORMAT_XML;
+        internal DateTime dt1970 = new DateTime(1970, 1, 1, 0, 0, 0, 0);
+
+        internal WebUtils webUtils;
+        internal ITopLogger topLogger;
+        internal bool disableParser = false; // 禁用响应结果解释
+        internal bool disableTrace = false; // 禁用日志调试功能
+        internal bool useSimplifyJson = false; // 是否采用精简化的JSON返回
+        internal bool useGzipEncoding = true;  // 是否启用响应GZIP压缩
+        internal bool useJsonString = false; // 是否采用JsonString生成
+        internal IDictionary<string, string> systemParameters; // 设置所有请求共享的系统级参数
+
+        #region DefaultDingTalkClient Constructors
+
+        public DefaultDingTalkClient(string serverUrl)
+        {
+            this.serverUrl = serverUrl;
+            this.webUtils = new WebUtils();
+            this.topLogger = Top.Api.Log.Instance;
+        }
+
+        public DefaultDingTalkClient(string serverUrl,string format)
+            : this(serverUrl)
+        {
+            this.format = format;
+        }
+
+        #endregion
+
+        public void SetTimeout(int timeout)
+        {
+            this.webUtils.Timeout = timeout;
+        }
+
+        public void SetReadWriteTimeout(int readWriteTimeout)
+        {
+            this.webUtils.ReadWriteTimeout = readWriteTimeout;
+        }
+
+        public void SetDisableParser(bool disableParser)
+        {
+            this.disableParser = disableParser;
+        }
+
+        public void SetDisableTrace(bool disableTrace)
+        {
+            this.disableTrace = disableTrace;
+        }
+
+        public void SetUseSimplifyJson(bool useSimplifyJson)
+        {
+            this.useSimplifyJson = useSimplifyJson;
+        }
+
+        public void SetUseGzipEncoding(bool useGzipEncoding)
+        {
+            this.useGzipEncoding = useGzipEncoding;
+        }
+
+        public void SetIgnoreSSLCheck(bool ignore)
+        {
+            this.webUtils.IgnoreSSLCheck = ignore;
+        }
+
+        public void SetSystemParameters(IDictionary<string, string> systemParameters)
+        {
+            this.systemParameters = systemParameters;
+        }
+
+        #region IDingTalkClient Members
+
+        public virtual T Execute<T>(IDingTalkRequest<T> request) where T : DingTalkResponse
+        {
+            return DoExecute<T>(request, null, DateTime.Now);
+        }
+
+        public virtual T Execute<T>(IDingTalkRequest<T> request, string session) where T : DingTalkResponse
+        {
+            return DoExecute<T>(request, session, DateTime.Now);
+        }
+
+        public virtual T Execute<T>(IDingTalkRequest<T> request, string session, DateTime timestamp) where T : DingTalkResponse
+        {
+            return DoExecute<T>(request, session, timestamp);
+        }
+
+        public T Execute<T>(IDingTalkRequest<T> request, string accessKey, string accessSecret) where T : DingTalkResponse
+        {
+            return Execute<T>(request, accessKey, accessSecret, null, null);
+        }
+
+        public T Execute<T>(IDingTalkRequest<T> request, string accessKey, string accessSecret, string suiteTicket) where T : DingTalkResponse
+        {
+            return Execute<T>(request, accessKey, accessSecret, suiteTicket, null);
+        }
+
+        public T Execute<T>(IDingTalkRequest<T> request, string accessKey, string accessSecret, string suiteTicket, string corpId) where T : DingTalkResponse
+        {
+            if (request.GetApiCallType() == null || request.GetApiCallType().Equals(DingTalkConstants.CALL_TYPE_TOP))
+            {
+                return DoExecuteTop(request, null, DateTime.Now);
+            }
+            else
+            {
+                return DoExecuteOApi(request, null, accessKey, accessSecret, suiteTicket, corpId, DateTime.Now);
+            }
+        }
+
+        #endregion
+
+        private T DoExecute<T>(IDingTalkRequest<T> request, string session, DateTime timestamp) where T : DingTalkResponse
+        {
+            if(request.GetApiCallType() == null || request.GetApiCallType().Equals(DingTalkConstants.CALL_TYPE_TOP)) {
+                return DoExecuteTop(request, session, timestamp);
+            } else {
+                return DoExecuteOApi(request, session, null, null, null, null, timestamp);
+            }
+        }
+
+        private T DoExecuteTop<T>(IDingTalkRequest<T> request, string session, DateTime timestamp) where T : DingTalkResponse
+        {
+            long start = DateTime.Now.Ticks;
+
+            // 提前检查业务参数
+            try
+            {
+                request.Validate();
+            }
+            catch (TopException e)
+            {
+                return CreateErrorResponse<T>(e.ErrorCode, e.ErrorMsg);
+            }
+
+            // 添加协议级请求参数
+            TopDictionary txtParams = new TopDictionary(request.GetParameters());
+            txtParams.Add(Constants.METHOD, request.GetApiName());
+            txtParams.Add(Constants.VERSION, "2.0");
+            txtParams.Add(Constants.FORMAT, format);
+            txtParams.Add(Constants.PARTNER_ID, GetSdkVersion());
+            txtParams.Add(Constants.TIMESTAMP, timestamp);
+            txtParams.Add(Constants.SESSION, session);
+            txtParams.AddAll(this.systemParameters);
+
+            if (this.useSimplifyJson)
+            {
+                txtParams.Add(Constants.SIMPLIFY, "true");
+            }
+            // 添加头部参数
+            if (this.useGzipEncoding)
+            {
+                request.GetHeaderParameters()[Constants.ACCEPT_ENCODING] = Constants.CONTENT_ENCODING_GZIP;
+            }
+
+            string realServerUrl = GetServerUrl(this.serverUrl, request.GetApiName(), session);
+            string reqUrl = WebUtils.BuildRequestUrl(realServerUrl, txtParams);
+            try
+            {
+                string body;
+
+                if (request is IDingTalkUploadRequest<T>) // 是否需要上传文件
+                {
+                    IDingTalkUploadRequest<T> uRequest = (IDingTalkUploadRequest<T>)request;
+                    IDictionary<string, FileItem> fileParams = TopUtils.CleanupDictionary(uRequest.GetFileParameters());
+                    body = webUtils.DoPost(realServerUrl, txtParams, fileParams, request.GetHeaderParameters());
+                }
+                else
+                {
+                    body = webUtils.DoPost(realServerUrl, txtParams, request.GetHeaderParameters());
+                }
+
+                // 解释响应结果
+                T rsp;
+                if (disableParser)
+                {
+                    rsp = Activator.CreateInstance<T>();
+                    rsp.Body = body;
+                }
+                else
+                {
+                    if (Constants.FORMAT_XML.Equals(format))
+                    {
+                        ITopParser<T> tp = new TopXmlParser<T>();
+                        rsp = tp.Parse(body);
+                    }
+                    else
+                    {
+                        ITopParser<T> tp;
+                        if (useSimplifyJson)
+                        {
+                            tp = new TopSimplifyJsonParser<T>();
+                        }
+                        else
+                        {
+                            tp = new TopJsonParser<T>();
+                        }
+                        rsp = tp.Parse(body);
+                    }
+                }
+
+                // 追踪错误的请求
+                if (rsp.IsError)
+                {
+                    TimeSpan latency = new TimeSpan(DateTime.Now.Ticks - start);
+                    TraceApiError(request.GetApiName(), serverUrl, txtParams, latency.TotalMilliseconds, rsp.Body);
+                }
+                return rsp;
+            }
+            catch (Exception e)
+            {
+                TimeSpan latency = new TimeSpan(DateTime.Now.Ticks - start);
+                TraceApiError(request.GetApiName(), serverUrl, txtParams, latency.TotalMilliseconds, e.GetType() + ": " + e.Message);
+                throw e;
+            }
+        }
+
+        private T DoExecuteOApi<T>(IDingTalkRequest<T> request, string session, string accessKey, string accessSecret, string suiteTicket, string corpId, DateTime timestamp) where T : DingTalkResponse
+        {
+            long start = DateTime.Now.Ticks;
+
+            // 提前检查业务参数
+            try
+            {
+                request.Validate();
+            }
+            catch (TopException e)
+            {
+                return CreateErrorResponse<T>(e.ErrorCode, e.ErrorMsg);
+            }
+
+            this.format = Constants.FORMAT_JSON;
+
+            // 添加协议级请求参数
+            TopDictionary txtParams = new TopDictionary(request.GetParameters());
+            txtParams.Add(DingTalkConstants.ACCESS_TOKEN, session);
+
+            // 添加头部参数
+            if (this.useGzipEncoding)
+            {
+                request.GetHeaderParameters()[Constants.ACCEPT_ENCODING] = Constants.CONTENT_ENCODING_GZIP;
+            }
+
+            string realServerUrl = null;
+            // 签名优先
+            if (accessKey != null)
+            {
+                long dingTimestamp = GetTimestamp(DateTime.UtcNow);
+                // 验证签名有效性
+                String canonicalString = DingTalkSignatureUtil.GetCanonicalStringForIsv(dingTimestamp, suiteTicket);
+                String signature = DingTalkSignatureUtil.ComputeSignature(accessSecret, canonicalString);
+                IDictionary<String, String> ps = new Dictionary<String, String>();
+                ps.Add("accessKey", accessKey);
+                ps.Add("signature", signature);
+                ps.Add("timestamp", dingTimestamp + "");
+                if (suiteTicket != null)
+                {
+                    ps.Add("suiteTicket", suiteTicket);
+                }
+                if (corpId != null)
+                {
+                    ps.Add("corpId", corpId);
+                }
+
+                String queryStr = DingTalkSignatureUtil.ParamToQueryString(ps, "utf-8");
+                if (this.serverUrl.IndexOf("?") > 0)
+                {
+                    realServerUrl = this.serverUrl + "&" + queryStr;
+                }
+                else
+                {
+                    realServerUrl = this.serverUrl + "?" + queryStr;
+                }
+            }
+            else 
+            {
+                if (this.serverUrl.IndexOf("?") > 0)
+                {
+                    realServerUrl = this.serverUrl + (session != null && session != "" ? ("&access_token=" + session) : "");
+                }
+                else
+                {
+                    realServerUrl = this.serverUrl + (session != null && session != "" ? ("?access_token=" + session) : "");
+                }
+            }
+
+            try
+            {
+                string body;
+
+                if(request.GetHttpMethod() == "POST")
+                {
+                    if (request is IDingTalkUploadRequest<T>) // 是否需要上传文件
+                    {
+                        IDingTalkUploadRequest<T> uRequest = (IDingTalkUploadRequest<T>)request;
+                        IDictionary<string, FileItem> fileParams = TopUtils.CleanupDictionary(uRequest.GetFileParameters());
+                        body = webUtils.DoPost(realServerUrl, null, fileParams, request.GetHeaderParameters());
+                    }
+                    else
+                    {
+                        IDictionary<String, Object> jsonParams = new Dictionary<String, Object>();
+                        foreach (string key in request.GetParameters().Keys)
+                        {
+                            string value = request.GetParameters()[key];
+                            if(useJsonString)
+                            {
+                                jsonParams.Add(key, value);
+                            }
+                            else
+                            {
+                                if (value.StartsWith("[") && value.EndsWith("]"))
+                                {
+                                    IList childMap = (IList)TopUtils.JsonToObject(value);
+                                    jsonParams.Add(key, childMap);
+                                }
+                                else if (value.StartsWith("{") && value.EndsWith("}"))
+                                {
+                                    IDictionary<string, Object> childMap = (IDictionary<string, Object>)TopUtils.JsonToObject(value);
+                                    jsonParams.Add(key, childMap);
+                                }
+                                else
+                                {
+                                    jsonParams.Add(key, value);
+                                }
+                            }
+                           
+                        }
+
+
+                        body = webUtils.DoPostWithJson(realServerUrl, jsonParams, request.GetHeaderParameters());
+                    }
+                } else 
+                {
+                    body = webUtils.DoGet(realServerUrl, request.GetParameters());
+                }
+
+                // 解释响应结果
+                T rsp;
+                if (disableParser)
+                {
+                    rsp = Activator.CreateInstance<T>();
+                    rsp.Body = body;
+                }
+                else
+                {
+                    ITopParser<T> tp = new DingTalkJsonParser<T>();
+                    rsp = tp.Parse(body);
+                }
+
+                // 追踪错误的请求
+                if (rsp.IsError)
+                {
+                    TimeSpan latency = new TimeSpan(DateTime.Now.Ticks - start);
+                    TraceApiError(request.GetApiName(), serverUrl, txtParams, latency.TotalMilliseconds, rsp.Body);
+                }
+                return rsp;
+            }
+            catch (Exception e)
+            {
+                TimeSpan latency = new TimeSpan(DateTime.Now.Ticks - start);
+                TraceApiError(request.GetApiName(), serverUrl, txtParams, latency.TotalMilliseconds, e.GetType() + ": " + e.Message);
+                throw e;
+            }
+        }
+
+        internal virtual string GetServerUrl(string serverUrl, string apiName, string session)
+        {
+            return serverUrl;
+        }
+
+        internal virtual string GetSdkVersion()
+        {
+            return Constants.SDK_VERSION;
+        }
+
+        internal T CreateErrorResponse<T>(string errCode, string errMsg) where T : DingTalkResponse
+        {
+            T rsp = Activator.CreateInstance<T>();
+            rsp.ErrCode = errCode;
+            rsp.ErrMsg = errMsg;
+
+            if (Constants.FORMAT_XML.Equals(format))
+            {
+                XmlDocument root = new XmlDocument();
+                XmlElement bodyE = root.CreateElement(Constants.ERROR_RESPONSE);
+                XmlElement codeE = root.CreateElement(Constants.ERROR_CODE);
+                codeE.InnerText = errCode;
+                bodyE.AppendChild(codeE);
+                XmlElement msgE = root.CreateElement(Constants.ERROR_MSG);
+                msgE.InnerText = errMsg;
+                bodyE.AppendChild(msgE);
+                root.AppendChild(bodyE);
+                rsp.Body = root.OuterXml;
+            }
+            else
+            {
+                IDictionary<string, object> errObj = new Dictionary<string, object>();
+                errObj.Add(Constants.ERROR_CODE, errCode);
+                errObj.Add(Constants.ERROR_MSG, errMsg);
+                IDictionary<string, object> root = new Dictionary<string, object>();
+                root.Add(Constants.ERROR_RESPONSE, errObj);
+
+                string body = JSON.ToJSON(root);
+                rsp.Body = body;
+            }
+            return rsp;
+        }
+
+        internal void TraceApiError(string apiName, string url, Dictionary<string, string> parameters, double latency, string errorMessage)
+        {
+            if (!disableTrace)
+            {
+                this.topLogger.TraceApiError("dingtalk",apiName, url, parameters, latency, errorMessage);
+            }
+        }
+
+        private long GetTimestamp(DateTime dateTime)
+        {
+            return (dateTime.Ticks - dt1970.Ticks) / 10000;
+        }
+    }
+}

+ 11 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/DingTalk/DingTalkConstants.cs

@@ -0,0 +1,11 @@
+using System;
+namespace Top.Api.DingTalk
+{
+    public sealed class DingTalkConstants
+    {
+        public const string CALL_TYPE_TOP = "top";
+        public const string CALL_TYPE_OAPI = "oapi";
+
+        public const string ACCESS_TOKEN = "access_token";
+    }
+}

+ 240 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/DingTalk/DingTalkJsonParser.cs

@@ -0,0 +1,240 @@
+using FastJSON;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Threading;
+using System.Xml.Serialization;
+
+namespace Top.Api.Parser
+{
+    /// <summary>
+    /// TOP JSON响应通用解释器。
+    /// </summary>
+    public class DingTalkJsonParser<T> : ITopParser<T> where T : TopResponse
+    {
+        private static readonly ReaderWriterLock rwLock = new ReaderWriterLock();
+        private static readonly Dictionary<string, Dictionary<string, TopAttribute>> attrs = new Dictionary<string, Dictionary<string, TopAttribute>>();
+
+        #region ITopParser Members
+
+        public virtual T Parse(string body)
+        {
+            return Parse(body, typeof(T));
+        }
+
+        public T Parse(string body, Type type)
+        {
+            T rsp = null;
+
+            IDictionary json = JSON.Parse(body) as IDictionary;
+            if (json != null)
+            {
+                IDictionary data = json;
+
+                if (data != null)
+                {
+                    ITopReader reader = new TopSimplifyJsonReader(data);
+                    rsp = (T)FromJson(reader, type);
+                }
+            }
+
+            if (rsp == null)
+            {
+                rsp = Activator.CreateInstance(type) as T;
+            }
+
+            if (rsp != null)
+            {
+                rsp.Body = body;
+            }
+
+            return rsp;
+        }
+
+        #endregion
+
+        private Dictionary<string, TopAttribute> GetTopAttributes(Type type)
+        {
+            Dictionary<string, TopAttribute> tas = null;
+            bool inc = false;
+
+            rwLock.AcquireReaderLock(50);
+            try
+            {
+                if (rwLock.IsReaderLockHeld)
+                {
+                    inc = attrs.TryGetValue(type.FullName, out tas);
+                }
+            }
+            finally
+            {
+                if (rwLock.IsReaderLockHeld)
+                {
+                    rwLock.ReleaseReaderLock();
+                }
+            }
+
+            if (inc && tas != null) // 从缓存中获取类属性元数据
+            {
+                return tas;
+            }
+            else // 创建新的类属性元数据缓存
+            {
+                tas = new Dictionary<string, TopAttribute>();
+            }
+
+            PropertyInfo[] pis = type.GetProperties();
+            foreach (PropertyInfo pi in pis)
+            {
+                TopAttribute ta = new TopAttribute();
+                ta.Method = pi.GetSetMethod();
+
+                // 获取对象属性名称
+                XmlElementAttribute[] xeas = pi.GetCustomAttributes(typeof(XmlElementAttribute), true) as XmlElementAttribute[];
+                if (xeas != null && xeas.Length > 0)
+                {
+                    ta.ItemName = xeas[0].ElementName;
+                }
+
+                // 获取列表属性名称
+                if (ta.ItemName == null)
+                {
+                    XmlArrayItemAttribute[] xaias = pi.GetCustomAttributes(typeof(XmlArrayItemAttribute), true) as XmlArrayItemAttribute[];
+                    if (xaias != null && xaias.Length > 0)
+                    {
+                        ta.ItemName = xaias[0].ElementName;
+                    }
+                    XmlArrayAttribute[] xaas = pi.GetCustomAttributes(typeof(XmlArrayAttribute), true) as XmlArrayAttribute[];
+                    if (xaas != null && xaas.Length > 0)
+                    {
+                        ta.ListName = xaas[0].ElementName;
+                    }
+                    if (ta.ListName == null)
+                    {
+                        continue;
+                    }
+                }
+
+                // 获取属性类型
+                if (pi.PropertyType.IsGenericType)
+                {
+                    Type[] types = pi.PropertyType.GetGenericArguments();
+                    ta.ListType = types[0];
+                }
+                else
+                {
+                    ta.ItemType = pi.PropertyType;
+                }
+
+                tas.Add(pi.Name, ta);
+            }
+
+            rwLock.AcquireWriterLock(50);
+            try
+            {
+                if (rwLock.IsWriterLockHeld)
+                {
+                    attrs[type.FullName] = tas;
+                }
+            }
+            finally
+            {
+                if (rwLock.IsWriterLockHeld)
+                {
+                    rwLock.ReleaseWriterLock();
+                }
+            }
+            return tas;
+        }
+
+        public object FromJson(ITopReader reader, Type type)
+        {
+            object rsp = null;
+            Dictionary<string, TopAttribute> pas = GetTopAttributes(type);
+
+            Dictionary<string, TopAttribute>.Enumerator em = pas.GetEnumerator();
+            while (em.MoveNext())
+            {
+                KeyValuePair<string, TopAttribute> kvp = em.Current;
+                TopAttribute ta = kvp.Value;
+                string itemName = ta.ItemName;
+                string listName = ta.ListName;
+
+                if (!reader.HasReturnField(itemName) && (string.IsNullOrEmpty(listName) || !reader.HasReturnField(listName)))
+                {
+                    continue;
+                }
+
+                object value = null;
+                if (ta.ListType != null)
+                {
+                    value = reader.GetListObjects(listName, itemName, ta.ListType, FromJson);
+                }
+                else
+                {
+                    if (typeof(string) == ta.ItemType)
+                    {
+                        object tmp = reader.GetPrimitiveObject(itemName);
+                        if (typeof(string) == tmp.GetType())
+                        {
+                            value = tmp;
+                        }
+                        else
+                        {
+                            if (tmp != null)
+                            {
+                                value = JSON.ToJSON(tmp);
+                            }
+                        }
+                    }
+                    else if (typeof(long) == ta.ItemType)
+                    {
+                        object tmp = reader.GetPrimitiveObject(itemName);
+                        if (typeof(long) == tmp.GetType())
+                        {
+                            value = tmp;
+                        }
+                        else
+                        {
+                            if (tmp != null)
+                            {
+                                value = long.Parse(tmp.ToString());
+                            }
+                        }
+                    }
+                    else if (typeof(bool) == ta.ItemType)
+                    {
+                        object tmp = reader.GetPrimitiveObject(itemName);
+                        if (typeof(bool) == tmp.GetType())
+                        {
+                            value = tmp;
+                        }
+                        else
+                        {
+                            if (tmp != null)
+                            {
+                                value = bool.Parse(tmp.ToString());
+                            }
+                        }
+                    }
+                    else
+                    {
+                        value = reader.GetReferenceObject(itemName, ta.ItemType, FromJson);
+                    }
+                }
+
+                if (value != null)
+                {
+                    if (rsp == null)
+                    {
+                        rsp = Activator.CreateInstance(type);
+                    }
+                    ta.Method.Invoke(rsp, new object[] { value });
+                }
+            }
+
+            return rsp;
+        }
+    }
+}

+ 12 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/DingTalk/DingTalkResponse.cs

@@ -0,0 +1,12 @@
+using System;
+using System.Xml.Serialization;
+using Top.Api;
+
+namespace DingTalk.Api
+{
+    [Serializable]
+    public abstract class DingTalkResponse : TopResponse
+    {
+
+    }
+}

+ 41 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/DingTalk/IDingTalkClient.cs

@@ -0,0 +1,41 @@
+using System;
+
+namespace DingTalk.Api
+{
+    /// <summary>
+    /// TOP客户端。
+    /// </summary>
+    public interface IDingTalkClient
+    {
+        /// <summary>
+        /// 执行TOP公开API请求。
+        /// </summary>
+        /// <typeparam name="T">领域对象</typeparam>
+        /// <param name="request">具体的TOP API请求</param>
+        /// <returns>领域对象</returns>
+        T Execute<T>(IDingTalkRequest<T> request) where T : DingTalkResponse;
+
+        /// <summary>
+        /// 执行TOP隐私API请求。
+        /// </summary>
+        /// <typeparam name="T">领域对象</typeparam>
+        /// <param name="request">具体的TOP API请求</param>
+        /// <param name="accessToken">用户会话码</param>
+        /// <returns>领域对象</returns>
+        T Execute<T>(IDingTalkRequest<T> request, string accessToken) where T : DingTalkResponse;
+
+        /// <summary>
+        /// 执行TOP隐私API请求。
+        /// </summary>
+        /// <typeparam name="T">领域对象</typeparam>
+        /// <param name="request">具体的TOP API请求</param>
+        /// <param name="accessToken">用户会话码</param>
+        /// <param name="timestamp">请求时间戳</param>
+        /// <returns>领域对象</returns>
+        T Execute<T>(IDingTalkRequest<T> request, string accessToken, DateTime timestamp) where T : DingTalkResponse;
+
+        T Execute<T>(IDingTalkRequest<T> request, string accessKey, string accessSecret) where T : DingTalkResponse;
+        T Execute<T>(IDingTalkRequest<T> request, string accessKey, string accessSecret, string suiteTicket) where T : DingTalkResponse;
+        T Execute<T>(IDingTalkRequest<T> request, string accessKey, string accessSecret, string suiteTicket, string corpId) where T : DingTalkResponse;
+    }
+}

+ 43 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/DingTalk/IDingTalkRequest.cs

@@ -0,0 +1,43 @@
+using System;
+using System.Collections.Generic;
+
+namespace DingTalk.Api
+{
+    /// <summary>
+    /// TOP请求接口。
+    /// </summary>
+    public interface IDingTalkRequest<out T> where T : DingTalkResponse
+    {
+        /// <summary>
+        /// 获取TOP的API名称。
+        /// </summary>
+        string GetApiName();
+
+        /// <summary>
+        /// 获取API请求方式。
+        /// </summary>
+        /// <returns>The API call type.</returns>
+        string GetApiCallType();
+
+        /// <summary>
+        /// 获取所有的Key-Value形式的文本请求参数字典。
+        /// </summary>
+        IDictionary<string, string> GetParameters();
+
+        /// <summary>
+        /// 获取自定义HTTP请求头参数。
+        /// </summary>
+        IDictionary<string, string> GetHeaderParameters();
+
+        /// <summary>
+        /// 客户端参数检查,减少服务端无效调用。
+        /// </summary>
+        void Validate();
+
+        /// <summary>
+        /// 获取http method
+        /// </summary>
+        /// <returns>The http method.</returns>
+        string GetHttpMethod();
+    }
+}

+ 20 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/DingTalk/IDingTalkUploadRequest.cs

@@ -0,0 +1,20 @@
+using System.Collections.Generic;
+using DingTalk.Api;
+using Top.Api.Util;
+
+namespace Top.Api
+{
+    /// <summary>
+    /// DingTalk上传请求接口,支持同时上传多个文件。
+    /// </summary>
+    public interface IDingTalkUploadRequest<T> : IDingTalkRequest<T> where T : DingTalkResponse
+    {
+        /// <summary>
+        /// 获取所有的Key-Value形式的文件请求参数字典。其中:
+        /// Key: 请求参数名
+        /// Value: 文件对象
+        /// </summary>
+        /// <returns>文件请求参数字典</returns>
+        IDictionary<string, FileItem> GetFileParameters();
+    }
+}

+ 54 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/AppBaseInfo.cs

@@ -0,0 +1,54 @@
+using System;
+using System.Xml.Serialization;
+
+namespace Top.Api.Domain
+{
+    /// <summary>
+    /// AppBaseInfo Data Structure.
+    /// </summary>
+    [Serializable]
+    public class AppBaseInfo : TopObject
+    {
+        /// <summary>
+        /// app状态,1.线上运行, 2. 正式环境测试中, 6.开发中
+        /// </summary>
+        [XmlElement("app_status")]
+        public long AppStatus { get; set; }
+
+        /// <summary>
+        /// app的唯一标识:app key
+        /// </summary>
+        [XmlElement("appkey")]
+        public string Appkey { get; set; }
+
+        /// <summary>
+        /// app描述
+        /// </summary>
+        [XmlElement("description")]
+        public string Description { get; set; }
+
+        /// <summary>
+        /// isv的数字id
+        /// </summary>
+        [XmlElement("isv_id")]
+        public long IsvId { get; set; }
+
+        /// <summary>
+        /// Null
+        /// </summary>
+        [XmlElement("support_email")]
+        public string SupportEmail { get; set; }
+
+        /// <summary>
+        /// app名称
+        /// </summary>
+        [XmlElement("title")]
+        public string Title { get; set; }
+
+        /// <summary>
+        /// 开发者旺旺
+        /// </summary>
+        [XmlElement("wangwang")]
+        public string Wangwang { get; set; }
+    }
+}

+ 42 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/Area.cs

@@ -0,0 +1,42 @@
+using System;
+using System.Xml.Serialization;
+
+namespace Top.Api.Domain
+{
+    /// <summary>
+    /// Area Data Structure.
+    /// </summary>
+    [Serializable]
+    public class Area : TopObject
+    {
+        /// <summary>
+        /// 标准行政区域代码.参考:http://www.stats.gov.cn/tjbz/xzqhdm/t20120105_402777427.htm
+        /// </summary>
+        [XmlElement("id")]
+        public long Id { get; set; }
+
+        /// <summary>
+        /// 地域名称.如北京市,杭州市,西湖区,每一个area_id 都代表了一个具体的地区.
+        /// </summary>
+        [XmlElement("name")]
+        public string Name { get; set; }
+
+        /// <summary>
+        /// 父节点区域标识.如北京市的area_id是110100,朝阳区是北京市的一个区,所以朝阳区的parent_id就是北京市的area_id.
+        /// </summary>
+        [XmlElement("parent_id")]
+        public long ParentId { get; set; }
+
+        /// <summary>
+        /// 区域类型.area区域 1:country/国家;2:province/省/自治区/直辖市;3:city/地区(省下面的地级市);4:district/县/市(县级市)/区;abroad:海外. 比如北京市的area_type = 2,朝阳区是北京市的一个区,所以朝阳区的area_type = 4.
+        /// </summary>
+        [XmlElement("type")]
+        public long Type { get; set; }
+
+        /// <summary>
+        /// 具体一个地区的邮编
+        /// </summary>
+        [XmlElement("zip")]
+        public string Zip { get; set; }
+    }
+}

+ 36 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/Brand.cs

@@ -0,0 +1,36 @@
+using System;
+using System.Xml.Serialization;
+
+namespace Top.Api.Domain
+{
+    /// <summary>
+    /// Brand Data Structure.
+    /// </summary>
+    [Serializable]
+    public class Brand : TopObject
+    {
+        /// <summary>
+        /// vid的值
+        /// </summary>
+        [XmlElement("name")]
+        public string Name { get; set; }
+
+        /// <summary>
+        /// 品牌的属性id
+        /// </summary>
+        [XmlElement("pid")]
+        public long Pid { get; set; }
+
+        /// <summary>
+        /// 品牌的属性名
+        /// </summary>
+        [XmlElement("prop_name")]
+        public string PropName { get; set; }
+
+        /// <summary>
+        /// 对应属性的 pid:vid 串中的vid
+        /// </summary>
+        [XmlElement("vid")]
+        public long Vid { get; set; }
+    }
+}

+ 72 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/DataPushTask.cs

@@ -0,0 +1,72 @@
+using System;
+using System.Xml.Serialization;
+
+namespace Top.Api.Domain
+{
+    /// <summary>
+    /// DataPushTask Data Structure.
+    /// </summary>
+    [Serializable]
+    public class DataPushTask : TopObject
+    {
+        /// <summary>
+        /// 任务所属的appkey
+        /// </summary>
+        [XmlElement("app_key")]
+        public string AppKey { get; set; }
+
+        /// <summary>
+        /// 任务创建时间
+        /// </summary>
+        [XmlElement("created")]
+        public string Created { get; set; }
+
+        /// <summary>
+        /// 任务编号
+        /// </summary>
+        [XmlElement("id")]
+        public long Id { get; set; }
+
+        /// <summary>
+        /// 下次执行时间
+        /// </summary>
+        [XmlElement("next_execute_time")]
+        public string NextExecuteTime { get; set; }
+
+        /// <summary>
+        /// 上一次同步到的时间点
+        /// </summary>
+        [XmlElement("now_sync_time")]
+        public string NowSyncTime { get; set; }
+
+        /// <summary>
+        /// 任务的业务参数,用json格式表示
+        /// </summary>
+        [XmlElement("params")]
+        public string Params { get; set; }
+
+        /// <summary>
+        /// 任务类型代码
+        /// </summary>
+        [XmlElement("type")]
+        public long Type { get; set; }
+
+        /// <summary>
+        /// 任务所属的用户编号
+        /// </summary>
+        [XmlElement("user_id")]
+        public long UserId { get; set; }
+
+        /// <summary>
+        /// 用户的nick
+        /// </summary>
+        [XmlElement("user_nick")]
+        public string UserNick { get; set; }
+
+        /// <summary>
+        /// 任务的版本号,在更新任务时,需要用到此数据
+        /// </summary>
+        [XmlElement("version")]
+        public long Version { get; set; }
+    }
+}

+ 24 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/Feature.cs

@@ -0,0 +1,24 @@
+using System;
+using System.Xml.Serialization;
+
+namespace Top.Api.Domain
+{
+    /// <summary>
+    /// Feature Data Structure.
+    /// </summary>
+    [Serializable]
+    public class Feature : TopObject
+    {
+        /// <summary>
+        /// 属性键
+        /// </summary>
+        [XmlElement("attr_key")]
+        public string AttrKey { get; set; }
+
+        /// <summary>
+        /// 属性值
+        /// </summary>
+        [XmlElement("attr_value")]
+        public string AttrValue { get; set; }
+    }
+}

+ 84 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/File.cs

@@ -0,0 +1,84 @@
+using System;
+using System.Xml.Serialization;
+
+namespace Top.Api.Domain
+{
+    /// <summary>
+    /// File Data Structure.
+    /// </summary>
+    [Serializable]
+    public class File : TopObject
+    {
+        /// <summary>
+        /// 申请cdn资源的分配的userId
+        /// </summary>
+        [XmlElement("cdn_user_id")]
+        public long CdnUserId { get; set; }
+
+        /// <summary>
+        /// 文件是否删除
+        /// </summary>
+        [XmlElement("deleted")]
+        public string Deleted { get; set; }
+
+        /// <summary>
+        /// 该文件所属目录的目录编号
+        /// </summary>
+        [XmlElement("dir_id")]
+        public long DirId { get; set; }
+
+        /// <summary>
+        /// 文件在多媒体平台的编号
+        /// </summary>
+        [XmlElement("file_id")]
+        public long FileId { get; set; }
+
+        /// <summary>
+        /// 返回的是绝对路径如:http://img07.taobaocdn.com/imgextra/i7/22670458/T2dD0kXb4cXXXXXXXX_!!22670458.jpg
+        /// </summary>
+        [XmlElement("file_path")]
+        public string FilePath { get; set; }
+
+        /// <summary>
+        /// 创建时间
+        /// </summary>
+        [XmlElement("gmt_create")]
+        public string GmtCreate { get; set; }
+
+        /// <summary>
+        /// 修改时间
+        /// </summary>
+        [XmlElement("gmt_modified")]
+        public string GmtModified { get; set; }
+
+        /// <summary>
+        /// 文件内容修改时间
+        /// </summary>
+        [XmlElement("modified")]
+        public string Modified { get; set; }
+
+        /// <summary>
+        /// 文件在多媒体平台的文件名称
+        /// </summary>
+        [XmlElement("name")]
+        public string Name { get; set; }
+
+        /// <summary>
+        /// 图片像素,如果非图片,该值为空
+        /// </summary>
+        [XmlElement("picture_pix")]
+        public string PicturePix { get; set; }
+
+        /// <summary>
+        /// 文件的大小
+        /// </summary>
+        [XmlElement("size")]
+        public long Size { get; set; }
+
+        /// <summary>
+        /// 图片状态,unfroze代表没有被冻结,froze代表被冻结,pass代表排查通过
+        /// </summary>
+        [XmlElement("status")]
+        public string Status { get; set; }
+    }
+}

+ 680 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/Item.cs

@@ -0,0 +1,680 @@
+using System;
+using System.Xml.Serialization;
+using System.Collections.Generic;
+
+namespace Top.Api.Domain
+{
+    /// <summary>
+    /// Item Data Structure.
+    /// </summary>
+    [Serializable]
+    public class Item : TopObject
+    {
+        /// <summary>
+        /// 售后服务ID,该字段仅在taobao.item.get接口中返回
+        /// </summary>
+        [XmlElement("after_sale_id")]
+        public long AfterSaleId { get; set; }
+
+        /// <summary>
+        /// 商品上传后的状态。onsale出售中,instock库中
+        /// </summary>
+        [XmlElement("approve_status")]
+        public string ApproveStatus { get; set; }
+
+        /// <summary>
+        /// 返点比例
+        /// </summary>
+        [XmlElement("auction_point")]
+        public long AuctionPoint { get; set; }
+
+        /// <summary>
+        /// 代充商品类型。只有少数类目下的商品可以标记上此字段,具体哪些类目可以上传可以通过taobao.itemcat.features.get获得。在代充商品的类目下,不传表示不标记商品类型(交易搜索中就不能通过标记搜到相关的交易了)。可选类型:time_card(点卡软件代充)fee_card(话费软件代充)
+        /// </summary>
+        [XmlElement("auto_fill")]
+        public string AutoFill { get; set; }
+
+        /// <summary>
+        /// 自动重发,true/false
+        /// </summary>
+        [XmlElement("auto_repost")]
+        public bool AutoRepost { get; set; }
+
+        /// <summary>
+        /// 商品级别的条形码
+        /// </summary>
+        [XmlElement("barcode")]
+        public string Barcode { get; set; }
+
+        /// <summary>
+        /// 基础色数据
+        /// </summary>
+        [XmlElement("change_prop")]
+        public string ChangeProp { get; set; }
+
+        /// <summary>
+        /// 天猫超市扩展字段,天猫超市专用。
+        /// </summary>
+        [XmlElement("chaoshi_extends_info")]
+        public string ChaoshiExtendsInfo { get; set; }
+
+        /// <summary>
+        /// 支持3期、6期、12期免息
+        /// </summary>
+        [XmlElement("charge_free_list")]
+        public string ChargeFreeList { get; set; }
+
+        /// <summary>
+        /// 商品所属的叶子类目 id
+        /// </summary>
+        [XmlElement("cid")]
+        public long Cid { get; set; }
+
+        /// <summary>
+        /// 货到付款运费模板ID
+        /// </summary>
+        [XmlElement("cod_postage_id")]
+        public long CodPostageId { get; set; }
+
+        /// <summary>
+        /// 属性值的备注,格式:pid:vid:备注信息1;pid2:vid2:备注信息2;
+        /// </summary>
+        [XmlElement("cpv_memo")]
+        public string CpvMemo { get; set; }
+
+        /// <summary>
+        /// Item的发布时间,目前仅供taobao.item.add和taobao.item.get可用
+        /// </summary>
+        [XmlElement("created")]
+        public string Created { get; set; }
+
+        /// <summary>
+        /// 村淘特有商品级数据结构
+        /// </summary>
+        [XmlElement("cuntao_item_specific")]
+        public string CuntaoItemSpecific { get; set; }
+
+        /// <summary>
+        /// 定制工具Id
+        /// </summary>
+        [XmlElement("custom_made_type_id")]
+        public string CustomMadeTypeId { get; set; }
+
+        /// <summary>
+        /// 下架时间(格式:yyyy-MM-dd HH:mm:ss)
+        /// </summary>
+        [XmlElement("delist_time")]
+        public string DelistTime { get; set; }
+
+        /// <summary>
+        /// 发货时间信息
+        /// </summary>
+        [XmlElement("delivery_time")]
+        public string DeliveryTime { get; set; }
+
+        /// <summary>
+        /// 商品描述, 字数要大于5个字节,小于25000个字节
+        /// </summary>
+        [XmlElement("desc")]
+        public string Desc { get; set; }
+
+        /// <summary>
+        /// 宝贝描述规范化模块锚点信息
+        /// </summary>
+        [XmlElement("desc_module_info")]
+        public string DescModuleInfo { get; set; }
+
+        /// <summary>
+        /// 商品描述模块化,模块列表,由List转化成jsonArray存入,后端逻辑验证通过,拼装成模块内容+锚点导航后存入desc中。数据结构具体参见Item_Desc_Module
+        /// </summary>
+        [XmlElement("desc_modules")]
+        public string DescModules { get; set; }
+
+        /// <summary>
+        /// 商品url
+        /// </summary>
+        [XmlElement("detail_url")]
+        public string DetailUrl { get; set; }
+
+        /// <summary>
+        /// ems费用,格式:5.00;单位:元;精确到:分
+        /// </summary>
+        [XmlElement("ems_fee")]
+        public string EmsFee { get; set; }
+
+        /// <summary>
+        /// 快递费用,格式:5.00;单位:元;精确到:分
+        /// </summary>
+        [XmlElement("express_fee")]
+        public string ExpressFee { get; set; }
+
+        /// <summary>
+        /// 宝贝特征值,只有在Top支持的特征值才能保存到宝贝上
+        /// </summary>
+        [XmlElement("features")]
+        public string Features { get; set; }
+
+        /// <summary>
+        /// 食品安全信息,包括:生产许可证编号、产品标准号、厂名、厂址等
+        /// </summary>
+        [XmlElement("food_security")]
+        public string FoodSecurity { get; set; }
+
+        /// <summary>
+        /// 运费承担方式,seller(卖家承担),buyer(买家承担)
+        /// </summary>
+        [XmlElement("freight_payer")]
+        public string FreightPayer { get; set; }
+
+        /// <summary>
+        /// 全球购商品采购地信息(地区/国家),代表全球购商品的产地信息。
+        /// </summary>
+        [XmlElement("global_stock_country")]
+        public string GlobalStockCountry { get; set; }
+
+        /// <summary>
+        /// 全球购商品发货地,发货地现在有两种类型:&ldquo;国内&rdquo;和&ldquo;海外及港澳台&rdquo;,参数值为1时代表&ldquo;国内&rdquo;,值为2时代表&ldquo;海外及港澳台&rdquo;
+        /// </summary>
+        [XmlElement("global_stock_delivery_place")]
+        public string GlobalStockDeliveryPlace { get; set; }
+
+        /// <summary>
+        /// 全球购商品卖家包税承诺,当值为true时,代表卖家承诺包税。
+        /// </summary>
+        [XmlElement("global_stock_tax_free_promise")]
+        public bool GlobalStockTaxFreePromise { get; set; }
+
+        /// <summary>
+        /// 全球购商品采购地信息(库存类型),有两种库存类型:现货和代购;参数值为1时代表现货,值为2时代表代购
+        /// </summary>
+        [XmlElement("global_stock_type")]
+        public string GlobalStockType { get; set; }
+
+        /// <summary>
+        /// 支持会员打折,true/false
+        /// </summary>
+        [XmlElement("has_discount")]
+        public bool HasDiscount { get; set; }
+
+        /// <summary>
+        /// 是否有发票,true/false
+        /// </summary>
+        [XmlElement("has_invoice")]
+        public bool HasInvoice { get; set; }
+
+        /// <summary>
+        /// 橱窗推荐,true/false
+        /// </summary>
+        [XmlElement("has_showcase")]
+        public bool HasShowcase { get; set; }
+
+        /// <summary>
+        /// 是否有保修,true/false
+        /// </summary>
+        [XmlElement("has_warranty")]
+        public bool HasWarranty { get; set; }
+
+        /// <summary>
+        /// 商品id(注意:iid近期即将废弃,请用num_iid参数)
+        /// </summary>
+        [XmlElement("iid")]
+        public string Iid { get; set; }
+
+        /// <summary>
+        /// 加价幅度。如果为0,代表系统代理幅度。在竞拍中,为了超越上一个出价,会员需要在当前出价上增加金额,这个金额就是加价幅度。卖家在发布宝贝的时候可以自定义加价幅度,也可以让系统自动代理加价。系统自动代理加价的加价幅度随着当前出价金额的增加而增加,我们建议会员使用系统自动代理加价,并请买家在出价前看清楚加价幅度的具体金额。另外需要注意是,此功能只适用于拍卖的商品。以下是系统自动代理加价幅度表:当前价(加价幅度 )1-40( 1 )、41-100( 2 )、101-200(5 )、201-500 (10)、501-1001(15)、001-2000(25)、2001-5000(50)、5001-10000(100)10001以上         200
+        /// </summary>
+        [XmlElement("increment")]
+        public string Increment { get; set; }
+
+        /// <summary>
+        /// 用户内店宝贝装修模板id
+        /// </summary>
+        [XmlElement("inner_shop_auction_template_id")]
+        public long InnerShopAuctionTemplateId { get; set; }
+
+        /// <summary>
+        /// 针对当前商品的自定义属性值
+        /// </summary>
+        [XmlElement("input_custom_cpv")]
+        public string InputCustomCpv { get; set; }
+
+        /// <summary>
+        /// 用户自行输入的类目属性ID串。结构:&quot;pid1,pid2,pid3&quot;,如:&quot;20000&quot;(表示品牌) 注:通常一个类目下用户可输入的关键属性不超过1个。
+        /// </summary>
+        [XmlElement("input_pids")]
+        public string InputPids { get; set; }
+
+        /// <summary>
+        /// 用户自行输入的子属性名和属性值,结构:&quot;父属性值;一级子属性名;一级子属性值;二级子属性名;自定义输入值,....&quot;,如:&ldquo;耐克;耐克系列;科比系列;科比系列;2K5&rdquo;,input_str需要与input_pids一一对应,注:通常一个类目下用户可输入的关键属性不超过1个。所有属性别名加起来不能超过 3999字节。
+        /// </summary>
+        [XmlElement("input_str")]
+        public string InputStr { get; set; }
+
+        /// <summary>
+        /// 是否是3D淘宝的商品
+        /// </summary>
+        [XmlElement("is_3D")]
+        public bool Is3D { get; set; }
+
+        /// <summary>
+        /// true:商品是区域限售商品;false:商品不是区域限售商品。
+        /// </summary>
+        [XmlElement("is_area_sale")]
+        public bool IsAreaSale { get; set; }
+
+        /// <summary>
+        /// 是否为达尔文挂接成功了的商品
+        /// </summary>
+        [XmlElement("is_cspu")]
+        public bool IsCspu { get; set; }
+
+        /// <summary>
+        /// 是否在外部网店显示
+        /// </summary>
+        [XmlElement("is_ex")]
+        public bool IsEx { get; set; }
+
+        /// <summary>
+        /// 非分销商品:0,代销:1,经销:2
+        /// </summary>
+        [XmlElement("is_fenxiao")]
+        public long IsFenxiao { get; set; }
+
+        /// <summary>
+        /// 是否24小时闪电发货
+        /// </summary>
+        [XmlElement("is_lightning_consignment")]
+        public bool IsLightningConsignment { get; set; }
+
+        /// <summary>
+        /// 商品是否为先行赔付taobao.items.search和taobao.items.vip.search专用
+        /// </summary>
+        [XmlElement("is_prepay")]
+        public bool IsPrepay { get; set; }
+
+        /// <summary>
+        /// 是否在淘宝显示
+        /// </summary>
+        [XmlElement("is_taobao")]
+        public bool IsTaobao { get; set; }
+
+        /// <summary>
+        /// 是否定时上架商品
+        /// </summary>
+        [XmlElement("is_timing")]
+        public bool IsTiming { get; set; }
+
+        /// <summary>
+        /// 虚拟商品的状态字段
+        /// </summary>
+        [XmlElement("is_virtual")]
+        public bool IsVirtual { get; set; }
+
+        /// <summary>
+        /// 标示商品是否为新品。值含义:true-是,false-否。
+        /// </summary>
+        [XmlElement("is_xinpin")]
+        public bool IsXinpin { get; set; }
+
+        /// <summary>
+        /// 商品图片列表(包括主图)。fields中只设置item_img可以返回ItemImg结构体中所有字段,如果设置为item_img.id、item_img.url、item_img.position等形式就只会返回相应的字段
+        /// </summary>
+        [XmlArray("item_imgs")]
+        [XmlArrayItem("item_img")]
+        public List<Top.Api.Domain.ItemImg> ItemImgs { get; set; }
+
+        /// <summary>
+        /// itemRectangleImgs
+        /// </summary>
+        [XmlArray("item_rectangle_imgs")]
+        [XmlArrayItem("item_img")]
+        public List<Top.Api.Domain.ItemImg> ItemRectangleImgs { get; set; }
+
+        /// <summary>
+        /// 表示商品的体积,用于按体积计费的运费模板。该值的单位为立方米(m3)。该值支持两种格式的设置:格式1:bulk:3,单位为立方米(m3),表示直接设置为商品的体积。格式2:weight:10;breadth:10;height:10,单位为米(m)
+        /// </summary>
+        [XmlElement("item_size")]
+        public string ItemSize { get; set; }
+
+        /// <summary>
+        /// 商品的重量,用于按重量计费的运费模板。注意:单位为kg
+        /// </summary>
+        [XmlElement("item_weight")]
+        public string ItemWeight { get; set; }
+
+        /// <summary>
+        /// 商品无线主图
+        /// </summary>
+        [XmlArray("item_wireless_imgs")]
+        [XmlArrayItem("item_img")]
+        public List<Top.Api.Domain.ItemImg> ItemWirelessImgs { get; set; }
+
+        /// <summary>
+        /// 门店大屏图
+        /// </summary>
+        [XmlElement("large_screen_image_url")]
+        public string LargeScreenImageUrl { get; set; }
+
+        /// <summary>
+        /// 上架时间(格式:yyyy-MM-dd HH:mm:ss)
+        /// </summary>
+        [XmlElement("list_time")]
+        public string ListTime { get; set; }
+
+        /// <summary>
+        /// 本地生活电子交易凭证业务,目前此字段只涉及到的信息为有效期:如果有效期为起止日期类型,此值为2012-08-06,2012-08-16如果有效期为【购买成功日 至】类型则格式为2012-08-16如果有效期为天数类型则格式为3
+        /// </summary>
+        [XmlElement("locality_life")]
+        public string LocalityLife { get; set; }
+
+        /// <summary>
+        /// 商品所在地
+        /// </summary>
+        [XmlElement("location")]
+        public Top.Api.Domain.Location Location { get; set; }
+
+        /// <summary>
+        /// 商品修改时间(格式:yyyy-MM-dd HH:mm:ss)
+        /// </summary>
+        [XmlElement("modified")]
+        public string Modified { get; set; }
+
+        /// <summary>
+        /// 宝贝主图视频的数据信息,包括:视频ID,视频缩略图URL,视频时长,视频状态等信息。
+        /// </summary>
+        [XmlElement("mpic_video")]
+        public string MpicVideo { get; set; }
+
+        /// <summary>
+        /// 家装分阶段价格数据结构
+        /// </summary>
+        [XmlElement("ms_payment")]
+        public string MsPayment { get; set; }
+
+        /// <summary>
+        /// 是否为新消保法中的7天无理由退货
+        /// </summary>
+        [XmlElement("newprepay")]
+        public string Newprepay { get; set; }
+
+        /// <summary>
+        /// 卖家昵称
+        /// </summary>
+        [XmlElement("nick")]
+        public string Nick { get; set; }
+
+        /// <summary>
+        /// 商品数量
+        /// </summary>
+        [XmlElement("num")]
+        public long Num { get; set; }
+
+        /// <summary>
+        /// 商品数字id
+        /// </summary>
+        [XmlElement("num_iid")]
+        public long NumIid { get; set; }
+
+        /// <summary>
+        /// 是否绑定o2o
+        /// </summary>
+        [XmlElement("o2o_bind_service")]
+        public bool O2oBindService { get; set; }
+
+        /// <summary>
+        /// 是否淘1站商品
+        /// </summary>
+        [XmlElement("one_station")]
+        public bool OneStation { get; set; }
+
+        /// <summary>
+        /// 商家外部编码(可与商家外部系统对接)
+        /// </summary>
+        [XmlElement("outer_id")]
+        public string OuterId { get; set; }
+
+        /// <summary>
+        /// 用户外店装修模板id
+        /// </summary>
+        [XmlElement("outer_shop_auction_template_id")]
+        public long OuterShopAuctionTemplateId { get; set; }
+
+        /// <summary>
+        /// 用于保存拍卖有关的信息
+        /// </summary>
+        [XmlElement("paimai_info")]
+        public string PaimaiInfo { get; set; }
+
+        /// <summary>
+        /// 周期销售库存
+        /// </summary>
+        [XmlElement("period_sold_quantity")]
+        public long PeriodSoldQuantity { get; set; }
+
+        /// <summary>
+        /// 商品主图片地址
+        /// </summary>
+        [XmlElement("pic_url")]
+        public string PicUrl { get; set; }
+
+        /// <summary>
+        /// 平邮费用,格式:5.00;单位:元;精确到:分
+        /// </summary>
+        [XmlElement("post_fee")]
+        public string PostFee { get; set; }
+
+        /// <summary>
+        /// 宝贝所属的运费模板ID,如果没有返回则说明没有使用运费模板
+        /// </summary>
+        [XmlElement("postage_id")]
+        public long PostageId { get; set; }
+
+        /// <summary>
+        /// 商品价格,格式:5.00;单位:元;精确到:分
+        /// </summary>
+        [XmlElement("price")]
+        public string Price { get; set; }
+
+        /// <summary>
+        /// 宝贝所属产品的id(可能为空). 该字段可以通过taobao.products.search 得到
+        /// </summary>
+        [XmlElement("product_id")]
+        public long ProductId { get; set; }
+
+        /// <summary>
+        /// 消保类型,多个类型以,分割。可取以下值:2:假一赔三;4:7天无理由退换货;taobao.items.search和taobao.items.vip.search专用
+        /// </summary>
+        [XmlElement("promoted_service")]
+        public string PromotedService { get; set; }
+
+        /// <summary>
+        /// 商品属性图片列表。fields中只设置prop_img可以返回PropImg结构体中所有字段,如果设置为prop_img.id、prop_img.url、prop_img.properties、prop_img.position等形式就只会返回相应的字段
+        /// </summary>
+        [XmlArray("prop_imgs")]
+        [XmlArrayItem("prop_img")]
+        public List<Top.Api.Domain.PropImg> PropImgs { get; set; }
+
+        /// <summary>
+        /// 属性值别名
+        /// </summary>
+        [XmlElement("property_alias")]
+        public string PropertyAlias { get; set; }
+
+        /// <summary>
+        /// 商品属性 格式:pid:vid;pid:vid
+        /// </summary>
+        [XmlElement("props")]
+        public string Props { get; set; }
+
+        /// <summary>
+        /// 商品属性名称。标识着props内容里面的pid和vid所对应的名称。格式为:pid1:vid1:pid_name1:vid_name1;pid2:vid2:pid_name2:vid_name2&hellip;&hellip;(<strong>注:</strong><font color="red">属性名称中的冒号&quot;:&quot;被转换为:&quot;#cln#&quot;;  分号&quot;;&quot;被转换为:&quot;#scln#&quot;</font>)
+        /// </summary>
+        [XmlElement("props_name")]
+        public string PropsName { get; set; }
+
+        /// <summary>
+        /// 商品资质的信息,用URLEncoder做过转换,使用时,需要URLDecoder转换回来,默认字符集为:UTF-8
+        /// </summary>
+        [XmlElement("qualification")]
+        public string Qualification { get; set; }
+
+        /// <summary>
+        /// 商品所属卖家的信用等级数,1表示1心,2表示2心&hellip;&hellip;,只有调用商品搜索:taobao.items.get和taobao.items.search的时候才能返回
+        /// </summary>
+        [XmlElement("score")]
+        public long Score { get; set; }
+
+        /// <summary>
+        /// 秒杀商品类型。打上秒杀标记的商品,用户只能下架并不能再上架,其他任何编辑或删除操作都不能进行。如果用户想取消秒杀标记,需要联系小二进行操作。如果秒杀结束需要自由编辑请联系活动负责人(小二)去掉秒杀标记。可选类型web_only(只能通过web网络秒杀)wap_only(只能通过wap网络秒杀)web_and_wap(既能通过web秒杀也能通过wap秒杀)
+        /// </summary>
+        [XmlElement("second_kill")]
+        public string SecondKill { get; set; }
+
+        /// <summary>
+        /// 商品卖点信息,天猫商家使用字段,最长150个字符。
+        /// </summary>
+        [XmlElement("sell_point")]
+        public string SellPoint { get; set; }
+
+        /// <summary>
+        /// 是否承诺退换货服务!
+        /// </summary>
+        [XmlElement("sell_promise")]
+        public bool SellPromise { get; set; }
+
+        /// <summary>
+        /// 商品所属的店铺内卖家自定义类目列表
+        /// </summary>
+        [XmlElement("seller_cids")]
+        public string SellerCids { get; set; }
+
+        /// <summary>
+        /// Sku列表。fields中只设置sku可以返回Sku结构体中所有字段,如果设置为sku.sku_id、sku.properties、sku.quantity等形式就只会返回相应的字段
+        /// </summary>
+        [XmlArray("skus")]
+        [XmlArrayItem("sku")]
+        public List<Top.Api.Domain.Sku> Skus { get; set; }
+
+        /// <summary>
+        /// 商品销量
+        /// </summary>
+        [XmlElement("sold_quantity")]
+        public long SoldQuantity { get; set; }
+
+        /// <summary>
+        /// 手机类目spu 确认信息字段
+        /// </summary>
+        [XmlElement("spu_confirm")]
+        public bool SpuConfirm { get; set; }
+
+        /// <summary>
+        /// 商品新旧程度(全新:new,闲置:unused,二手:second)
+        /// </summary>
+        [XmlElement("stuff_status")]
+        public string StuffStatus { get; set; }
+
+        /// <summary>
+        /// 商品是否支持拍下减库存:1支持;2取消支持(付款减库存);0(默认)不更改 集市卖家默认拍下减库存; 商城卖家默认付款减库存
+        /// </summary>
+        [XmlElement("sub_stock")]
+        public long SubStock { get; set; }
+
+        /// <summary>
+        /// 是否支持分期免息
+        /// </summary>
+        [XmlElement("support_charge_free")]
+        public bool SupportChargeFree { get; set; }
+
+        /// <summary>
+        /// 页面模板id
+        /// </summary>
+        [XmlElement("template_id")]
+        public string TemplateId { get; set; }
+
+        /// <summary>
+        /// 商品标题,不能超过60字节
+        /// </summary>
+        [XmlElement("title")]
+        public string Title { get; set; }
+
+        /// <summary>
+        /// 商品类型(fixed:一口价;auction:拍卖)注:取消团购
+        /// </summary>
+        [XmlElement("type")]
+        public string Type { get; set; }
+
+        /// <summary>
+        /// 有效期,7或者14(默认是14天)
+        /// </summary>
+        [XmlElement("valid_thru")]
+        public long ValidThru { get; set; }
+
+        /// <summary>
+        /// 商品竖图
+        /// </summary>
+        [XmlArray("vertical_imgs")]
+        [XmlArrayItem("item_img")]
+        public List<Top.Api.Domain.ItemImg> VerticalImgs { get; set; }
+
+        /// <summary>
+        /// 该字段废弃,请勿使用。
+        /// </summary>
+        [XmlElement("video_id")]
+        public long VideoId { get; set; }
+
+        /// <summary>
+        /// 商品视频列表(目前只支持单个视频关联)。fields中只设置video可以返回Video结构体中所有字段,如果设置为video.id、video.video_id、video.url等形式就只会返回相应的字段
+        /// </summary>
+        [XmlArray("videos")]
+        [XmlArrayItem("video")]
+        public List<Top.Api.Domain.Video> Videos { get; set; }
+
+        /// <summary>
+        /// 商品是否违规,违规:true , 不违规:false
+        /// </summary>
+        [XmlElement("violation")]
+        public bool Violation { get; set; }
+
+        /// <summary>
+        /// 商品30天交易量,只有调用商品搜索:taobao.items.get和taobao.items.search的时候才能返回
+        /// </summary>
+        [XmlElement("volume")]
+        public long Volume { get; set; }
+
+        /// <summary>
+        /// 不带html标签的desc文本信息,该字段只在taobao.item.get接口中返回
+        /// </summary>
+        [XmlElement("wap_desc")]
+        public string WapDesc { get; set; }
+
+        /// <summary>
+        /// 适合wap应用的商品详情url ,该字段只在taobao.item.get接口中返回
+        /// </summary>
+        [XmlElement("wap_detail_url")]
+        public string WapDetailUrl { get; set; }
+
+        /// <summary>
+        /// 白底图URL
+        /// </summary>
+        [XmlElement("white_bg_image")]
+        public string WhiteBgImage { get; set; }
+
+        /// <summary>
+        /// 无线的宝贝描述
+        /// </summary>
+        [XmlElement("wireless_desc")]
+        public string WirelessDesc { get; set; }
+
+        /// <summary>
+        /// 预扣库存,即付款减库存的商品现在有多少处于未付款状态的订单
+        /// </summary>
+        [XmlElement("with_hold_quantity")]
+        public long WithHoldQuantity { get; set; }
+
+        /// <summary>
+        /// 商品所属的商家的旺旺在线状况,taobao.items.search和taobao.items.vip.search专用
+        /// </summary>
+        [XmlElement("ww_status")]
+        public bool WwStatus { get; set; }
+    }
+}

+ 62 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/ItemCat.cs

@@ -0,0 +1,62 @@
+using System;
+using System.Xml.Serialization;
+using System.Collections.Generic;
+
+namespace Top.Api.Domain
+{
+    /// <summary>
+    /// ItemCat Data Structure.
+    /// </summary>
+    [Serializable]
+    public class ItemCat : TopObject
+    {
+        /// <summary>
+        /// 商品所属类目ID
+        /// </summary>
+        [XmlElement("cid")]
+        public long Cid { get; set; }
+
+        /// <summary>
+        /// Feature对象列表目前已有的属性:若Attr_key为 udsaleprop,attr_value为1 则允许卖家在改类目新增自定义销售属性,不然为不允许
+        /// </summary>
+        [XmlArray("features")]
+        [XmlArrayItem("feature")]
+        public List<Top.Api.Domain.Feature> Features { get; set; }
+
+        /// <summary>
+        /// 该类目是否为父类目(即:该类目是否还有子类目)
+        /// </summary>
+        [XmlElement("is_parent")]
+        public bool IsParent { get; set; }
+
+        /// <summary>
+        /// 类目名称
+        /// </summary>
+        [XmlElement("name")]
+        public string Name { get; set; }
+
+        /// <summary>
+        /// 父类目ID=0时,代表的是一级的类目
+        /// </summary>
+        [XmlElement("parent_cid")]
+        public long ParentCid { get; set; }
+
+        /// <summary>
+        /// 排列序号,表示同级类目的展现次序,如数值相等则按名称次序排列。取值范围:大于零的整数
+        /// </summary>
+        [XmlElement("sort_order")]
+        public long SortOrder { get; set; }
+
+        /// <summary>
+        /// 状态。可选值:normal(正常),deleted(删除)
+        /// </summary>
+        [XmlElement("status")]
+        public string Status { get; set; }
+
+        /// <summary>
+        /// 是否度量衡类目
+        /// </summary>
+        [XmlElement("taosir_cat")]
+        public bool TaosirCat { get; set; }
+    }
+}

+ 36 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/ItemImg.cs

@@ -0,0 +1,36 @@
+using System;
+using System.Xml.Serialization;
+
+namespace Top.Api.Domain
+{
+    /// <summary>
+    /// ItemImg Data Structure.
+    /// </summary>
+    [Serializable]
+    public class ItemImg : TopObject
+    {
+        /// <summary>
+        /// 图片创建时间 时间格式:yyyy-MM-dd HH:mm:ss
+        /// </summary>
+        [XmlElement("created")]
+        public string Created { get; set; }
+
+        /// <summary>
+        /// 商品图片的id,和商品相对应(主图id默认为0)
+        /// </summary>
+        [XmlElement("id")]
+        public long Id { get; set; }
+
+        /// <summary>
+        /// 图片放在第几张(多图时可设置)
+        /// </summary>
+        [XmlElement("position")]
+        public long Position { get; set; }
+
+        /// <summary>
+        /// 图片链接地址
+        /// </summary>
+        [XmlElement("url")]
+        public string Url { get; set; }
+    }
+}

+ 177 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/ItemProp.cs

@@ -0,0 +1,177 @@
+using System;
+using System.Xml.Serialization;
+using System.Collections.Generic;
+
+namespace Top.Api.Domain
+{
+    /// <summary>
+    /// ItemProp Data Structure.
+    /// </summary>
+    [Serializable]
+    public class ItemProp : TopObject
+    {
+        /// <summary>
+        /// 子属性的模板(卖家自行输入属性时需要用到)
+        /// </summary>
+        [XmlElement("child_template")]
+        public string ChildTemplate { get; set; }
+
+        /// <summary>
+        /// 类目ID
+        /// </summary>
+        [XmlElement("cid")]
+        public long Cid { get; set; }
+
+        /// <summary>
+        /// 属性的feature列表
+        /// </summary>
+        [XmlArray("features")]
+        [XmlArrayItem("feature")]
+        public List<Top.Api.Domain.Feature> Features { get; set; }
+
+        /// <summary>
+        /// 是否允许别名。可选值:true(是),false(否)
+        /// </summary>
+        [XmlElement("is_allow_alias")]
+        public bool IsAllowAlias { get; set; }
+
+        /// <summary>
+        /// 是否颜色属性。可选值:true(是),false(否)
+        /// </summary>
+        [XmlElement("is_color_prop")]
+        public bool IsColorProp { get; set; }
+
+        /// <summary>
+        /// 是否是可枚举属性。可选值:true(是),false(否)
+        /// </summary>
+        [XmlElement("is_enum_prop")]
+        public bool IsEnumProp { get; set; }
+
+        /// <summary>
+        /// 在is_enum_prop是true的前提下,是否是卖家可以自行输入的属性(注:如果is_enum_prop返回false,该参数统一返回false)。可选值:true(是),false(否)。<b>对于品牌和型号属性(包括子属性):如果用户是C卖家,则可自定义属性;如果是B卖家,则不可自定义属性,而必须要授权的属性。</b>
+        /// </summary>
+        [XmlElement("is_input_prop")]
+        public bool IsInputProp { get; set; }
+
+        /// <summary>
+        /// 是否商品属性。可选值:true(是),false(否)
+        /// </summary>
+        [XmlElement("is_item_prop")]
+        public bool IsItemProp { get; set; }
+
+        /// <summary>
+        /// 是否关键属性。可选值:true(是),false(否)
+        /// </summary>
+        [XmlElement("is_key_prop")]
+        public bool IsKeyProp { get; set; }
+
+        /// <summary>
+        /// 是否是材质 属性项
+        /// </summary>
+        [XmlElement("is_material")]
+        public bool IsMaterial { get; set; }
+
+        /// <summary>
+        /// 是否销售属性。可选值:true(是),false(否)
+        /// </summary>
+        [XmlElement("is_sale_prop")]
+        public bool IsSaleProp { get; set; }
+
+        /// <summary>
+        /// 是否度量衡属性项
+        /// </summary>
+        [XmlElement("is_taosir")]
+        public bool IsTaosir { get; set; }
+
+        /// <summary>
+        /// 材质属性信息
+        /// </summary>
+        [XmlElement("material_do")]
+        public string MaterialDo { get; set; }
+
+        /// <summary>
+        /// 属性修改时间(增量类目专用)
+        /// </summary>
+        [XmlElement("modified_time")]
+        public string ModifiedTime { get; set; }
+
+        /// <summary>
+        /// 三种枚举类型:modify,add,delete(增量类目专用)
+        /// </summary>
+        [XmlElement("modified_type")]
+        public string ModifiedType { get; set; }
+
+        /// <summary>
+        /// 发布产品或商品时是否可以多选。可选值:true(是),false(否)
+        /// </summary>
+        [XmlElement("multi")]
+        public bool Multi { get; set; }
+
+        /// <summary>
+        /// 发布产品或商品时是否为必选属性。可选值:true(是),false(否)
+        /// </summary>
+        [XmlElement("must")]
+        public bool Must { get; set; }
+
+        /// <summary>
+        /// 属性名
+        /// </summary>
+        [XmlElement("name")]
+        public string Name { get; set; }
+
+        /// <summary>
+        /// 上级属性ID
+        /// </summary>
+        [XmlElement("parent_pid")]
+        public long ParentPid { get; set; }
+
+        /// <summary>
+        /// 上级属性值ID
+        /// </summary>
+        [XmlElement("parent_vid")]
+        public long ParentVid { get; set; }
+
+        /// <summary>
+        /// 属性 ID 例:品牌的PID=20000
+        /// </summary>
+        [XmlElement("pid")]
+        public long Pid { get; set; }
+
+        /// <summary>
+        /// null
+        /// </summary>
+        [XmlArray("prop_values")]
+        [XmlArrayItem("prop_value")]
+        public List<Top.Api.Domain.PropValue> PropValues { get; set; }
+
+        /// <summary>
+        /// 发布产品或商品时是否为必选属性(与must相同)。可选值:true(是),false(否)
+        /// </summary>
+        [XmlElement("required")]
+        public bool Required { get; set; }
+
+        /// <summary>
+        /// 排列序号。取值范围:大于零的整排列序号。取值范围:大于零的整数
+        /// </summary>
+        [XmlElement("sort_order")]
+        public long SortOrder { get; set; }
+
+        /// <summary>
+        /// 状态。可选值:normal(正常),deleted(删除)
+        /// </summary>
+        [XmlElement("status")]
+        public string Status { get; set; }
+
+        /// <summary>
+        /// 度量衡相关信息
+        /// </summary>
+        [XmlElement("taosir_do")]
+        public string TaosirDo { get; set; }
+
+        /// <summary>
+        /// 属性值类型。可选值:  multiCheck(枚举多选)  optional(枚举单选)  multiCheckText(枚举可输入多选)  optionalText(枚举可输入单选)  text(非枚举可输入)
+        /// </summary>
+        [XmlElement("type")]
+        public string Type { get; set; }
+    }
+}

+ 60 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/JdpUser.cs

@@ -0,0 +1,60 @@
+using System;
+using System.Xml.Serialization;
+
+namespace Top.Api.Domain
+{
+    /// <summary>
+    /// JdpUser Data Structure.
+    /// </summary>
+    [Serializable]
+    public class JdpUser : TopObject
+    {
+        /// <summary>
+        /// 和数据回流绑定的appkey,用户的数据推到此appkey对应的rds后,会回传X_DOWNLOADED消息
+        /// </summary>
+        [XmlElement("hl_appkey")]
+        public string HlAppkey { get; set; }
+
+        /// <summary>
+        /// 用户等级,用于区分大卖家,值越大则订单量越大
+        /// </summary>
+        [XmlElement("level")]
+        public long Level { get; set; }
+
+        /// <summary>
+        /// rds的id,平台appkey会返回rds_id而不是rds_name
+        /// </summary>
+        [XmlElement("rds_id")]
+        public long RdsId { get; set; }
+
+        /// <summary>
+        /// rds数据库的实例名
+        /// </summary>
+        [XmlElement("rds_name")]
+        public string RdsName { get; set; }
+
+        /// <summary>
+        /// 卖家类型,B表示商城卖家,C表示淘宝卖家
+        /// </summary>
+        [XmlElement("seller_type")]
+        public string SellerType { get; set; }
+
+        /// <summary>
+        /// 0:暂停1:正常2:sessoin失效,停止3:已删除
+        /// </summary>
+        [XmlElement("status")]
+        public long Status { get; set; }
+
+        /// <summary>
+        /// 用户id
+        /// </summary>
+        [XmlElement("user_id")]
+        public long UserId { get; set; }
+
+        /// <summary>
+        /// 用户昵称
+        /// </summary>
+        [XmlElement("user_nick")]
+        public string UserNick { get; set; }
+    }
+}

+ 30 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/KfcSearchResult.cs

@@ -0,0 +1,30 @@
+using System;
+using System.Xml.Serialization;
+
+namespace Top.Api.Domain
+{
+    /// <summary>
+    /// KfcSearchResult Data Structure.
+    /// </summary>
+    [Serializable]
+    public class KfcSearchResult : TopObject
+    {
+        /// <summary>
+        /// 过滤后的文本:  当匹配到B等级的词时,文本中的关键词被替换为*号,content即为关键词替换后的文本;  其他情况,content始终为null
+        /// </summary>
+        [XmlElement("content")]
+        public string Content { get; set; }
+
+        /// <summary>
+        /// 匹配到的关键词的等级,值为null,或为A、B、C、D。  当匹配不到关键词时,值为null,否则值为A、B、C、D中的一个。  A、B、C、D等级按严重程度从高至低排列。
+        /// </summary>
+        [XmlElement("level")]
+        public string Level { get; set; }
+
+        /// <summary>
+        /// 是否匹配到关键词,匹配到则为true.
+        /// </summary>
+        [XmlElement("matched")]
+        public bool Matched { get; set; }
+    }
+}

+ 48 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/Location.cs

@@ -0,0 +1,48 @@
+using System;
+using System.Xml.Serialization;
+
+namespace Top.Api.Domain
+{
+    /// <summary>
+    /// Location Data Structure.
+    /// </summary>
+    [Serializable]
+    public class Location : TopObject
+    {
+        /// <summary>
+        /// 详细地址,最大256个字节(128个中文)
+        /// </summary>
+        [XmlElement("address")]
+        public string Address { get; set; }
+
+        /// <summary>
+        /// 所在城市(中文名称)
+        /// </summary>
+        [XmlElement("city")]
+        public string City { get; set; }
+
+        /// <summary>
+        /// 国家名称
+        /// </summary>
+        [XmlElement("country")]
+        public string Country { get; set; }
+
+        /// <summary>
+        /// 区/县(只适用于物流API)
+        /// </summary>
+        [XmlElement("district")]
+        public string District { get; set; }
+
+        /// <summary>
+        /// 所在省份(中文名称)
+        /// </summary>
+        [XmlElement("state")]
+        public string State { get; set; }
+
+        /// <summary>
+        /// 邮政编码
+        /// </summary>
+        [XmlElement("zip")]
+        public string Zip { get; set; }
+    }
+}

+ 108 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/Monitorstation.cs

@@ -0,0 +1,108 @@
+using System;
+using System.Xml.Serialization;
+
+namespace Top.Api.Domain
+{
+    /// <summary>
+    /// Monitorstation Data Structure.
+    /// </summary>
+    [Serializable]
+    public class Monitorstation : TopObject
+    {
+        /// <summary>
+        /// 属于流域
+        /// </summary>
+        [XmlElement("belong")]
+        public string Belong { get; set; }
+
+        /// <summary>
+        /// 日期
+        /// </summary>
+        [XmlElement("date")]
+        public string Date { get; set; }
+
+        /// <summary>
+        /// 监视站点
+        /// </summary>
+        [XmlElement("name")]
+        public string Name { get; set; }
+
+        /// <summary>
+        /// 氨氮
+        /// </summary>
+        [XmlElement("nitrogen")]
+        public string Nitrogen { get; set; }
+
+        /// <summary>
+        /// 氨氮水质类别
+        /// </summary>
+        [XmlElement("nitrogenquality")]
+        public string Nitrogenquality { get; set; }
+
+        /// <summary>
+        /// 总有机碳
+        /// </summary>
+        [XmlElement("orgacarbon")]
+        public string Orgacarbon { get; set; }
+
+        /// <summary>
+        /// 溶解氧水质类别
+        /// </summary>
+        [XmlElement("orgacarbonquality")]
+        public string Orgacarbonquality { get; set; }
+
+        /// <summary>
+        /// 溶解氧
+        /// </summary>
+        [XmlElement("oxygen")]
+        public string Oxygen { get; set; }
+
+        /// <summary>
+        /// 溶解氧水质类别
+        /// </summary>
+        [XmlElement("oxygenquality")]
+        public string Oxygenquality { get; set; }
+
+        /// <summary>
+        /// 高锰酸钾指数
+        /// </summary>
+        [XmlElement("permangan")]
+        public string Permangan { get; set; }
+
+        /// <summary>
+        /// 高锰酸钾指数水质类别
+        /// </summary>
+        [XmlElement("permanganquality")]
+        public string Permanganquality { get; set; }
+
+        /// <summary>
+        /// PH
+        /// </summary>
+        [XmlElement("ph")]
+        public string Ph { get; set; }
+
+        /// <summary>
+        /// PH水质类别
+        /// </summary>
+        [XmlElement("phquality")]
+        public string Phquality { get; set; }
+
+        /// <summary>
+        /// a
+        /// </summary>
+        [XmlElement("profile")]
+        public string Profile { get; set; }
+
+        /// <summary>
+        /// 断面属性
+        /// </summary>
+        [XmlElement("section")]
+        public string Section { get; set; }
+
+        /// <summary>
+        /// 更新时间
+        /// </summary>
+        [XmlElement("time")]
+        public string Time { get; set; }
+    }
+}

+ 54 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/OAuthOtherInfo.cs

@@ -0,0 +1,54 @@
+using System;
+using System.Xml.Serialization;
+
+namespace Top.Api.Domain
+{
+    /// <summary>
+    /// OAuthOtherInfo Data Structure.
+    /// </summary>
+    [Serializable]
+    public class OAuthOtherInfo : TopObject
+    {
+        /// <summary>
+        /// access_token
+        /// </summary>
+        [XmlElement("access_token")]
+        public string AccessToken { get; set; }
+
+        /// <summary>
+        /// avatarUrl
+        /// </summary>
+        [XmlElement("avatar_url")]
+        public string AvatarUrl { get; set; }
+
+        /// <summary>
+        /// id
+        /// </summary>
+        [XmlElement("id")]
+        public string Id { get; set; }
+
+        /// <summary>
+        /// nick
+        /// </summary>
+        [XmlElement("nick")]
+        public string Nick { get; set; }
+
+        /// <summary>
+        /// 三方平台的openId
+        /// </summary>
+        [XmlElement("open_id")]
+        public string OpenId { get; set; }
+
+        /// <summary>
+        /// 三方平台类型
+        /// </summary>
+        [XmlElement("platform_type")]
+        public long PlatformType { get; set; }
+
+        /// <summary>
+        /// 三方平台的unionId
+        /// </summary>
+        [XmlElement("union_id")]
+        public string UnionId { get; set; }
+    }
+}

+ 204 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/OpenAccount.cs

@@ -0,0 +1,204 @@
+using System;
+using System.Xml.Serialization;
+
+namespace Top.Api.Domain
+{
+    /// <summary>
+    /// OpenAccount Data Structure.
+    /// </summary>
+    [Serializable]
+    public class OpenAccount : TopObject
+    {
+        /// <summary>
+        /// 支付宝的帐号标识
+        /// </summary>
+        [XmlElement("alipay_id")]
+        public string AlipayId { get; set; }
+
+        /// <summary>
+        /// 头像url
+        /// </summary>
+        [XmlElement("avatar_url")]
+        public string AvatarUrl { get; set; }
+
+        /// <summary>
+        /// 银行卡号
+        /// </summary>
+        [XmlElement("bank_card_no")]
+        public string BankCardNo { get; set; }
+
+        /// <summary>
+        /// 银行卡的拥有者姓名
+        /// </summary>
+        [XmlElement("bank_card_owner_name")]
+        public string BankCardOwnerName { get; set; }
+
+        /// <summary>
+        /// 出生日期
+        /// </summary>
+        [XmlElement("birthday")]
+        public string Birthday { get; set; }
+
+        /// <summary>
+        /// 创建帐号的App Key
+        /// </summary>
+        [XmlElement("create_app_key")]
+        public string CreateAppKey { get; set; }
+
+        /// <summary>
+        /// 帐号创建的设备的ID
+        /// </summary>
+        [XmlElement("create_device_id")]
+        public string CreateDeviceId { get; set; }
+
+        /// <summary>
+        /// 账号创建时的位置
+        /// </summary>
+        [XmlElement("create_location")]
+        public string CreateLocation { get; set; }
+
+        /// <summary>
+        /// 展示名
+        /// </summary>
+        [XmlElement("display_name")]
+        public string DisplayName { get; set; }
+
+        /// <summary>
+        /// 数据域
+        /// </summary>
+        [XmlElement("domain_id")]
+        public long DomainId { get; set; }
+
+        /// <summary>
+        /// 邮箱
+        /// </summary>
+        [XmlElement("email")]
+        public string Email { get; set; }
+
+        /// <summary>
+        /// 自定义扩展信息Map的Json格式
+        /// </summary>
+        [XmlElement("ext_infos")]
+        public string ExtInfos { get; set; }
+
+        /// <summary>
+        /// 1男 2女
+        /// </summary>
+        [XmlElement("gender")]
+        public long Gender { get; set; }
+
+        /// <summary>
+        /// 记录创建时间
+        /// </summary>
+        [XmlElement("gmt_create")]
+        public string GmtCreate { get; set; }
+
+        /// <summary>
+        /// 记录上次更新时间
+        /// </summary>
+        [XmlElement("gmt_modified")]
+        public string GmtModified { get; set; }
+
+        /// <summary>
+        /// Open Account Id
+        /// </summary>
+        [XmlElement("id")]
+        public long Id { get; set; }
+
+        /// <summary>
+        /// 开发者自定义账号id
+        /// </summary>
+        [XmlElement("isv_account_id")]
+        public string IsvAccountId { get; set; }
+
+        /// <summary>
+        /// 地区
+        /// </summary>
+        [XmlElement("locale")]
+        public string Locale { get; set; }
+
+        /// <summary>
+        /// 登录名
+        /// </summary>
+        [XmlElement("login_id")]
+        public string LoginId { get; set; }
+
+        /// <summary>
+        /// 密码
+        /// </summary>
+        [XmlElement("login_pwd")]
+        public string LoginPwd { get; set; }
+
+        /// <summary>
+        /// 加密算法类型:1、代表单纯MD5,2:代表单一Salt的MD5,3、代表根据记录不同后的MD5
+        /// </summary>
+        [XmlElement("login_pwd_encryption")]
+        public long LoginPwdEncryption { get; set; }
+
+        /// <summary>
+        /// 密码加密强度
+        /// </summary>
+        [XmlElement("login_pwd_intensity")]
+        public long LoginPwdIntensity { get; set; }
+
+        /// <summary>
+        /// 密码salt
+        /// </summary>
+        [XmlElement("login_pwd_salt")]
+        public string LoginPwdSalt { get; set; }
+
+        /// <summary>
+        /// 手机
+        /// </summary>
+        [XmlElement("mobile")]
+        public string Mobile { get; set; }
+
+        /// <summary>
+        /// 姓名
+        /// </summary>
+        [XmlElement("name")]
+        public string Name { get; set; }
+
+        /// <summary>
+        /// TAOBAO = 1;WEIXIN = 2;WEIBO = 3;QQ = 4;
+        /// </summary>
+        [XmlElement("oauth_plateform")]
+        public long OauthPlateform { get; set; }
+
+        /// <summary>
+        /// 第三方oauth openid
+        /// </summary>
+        [XmlElement("open_id")]
+        public string OpenId { get; set; }
+
+        /// <summary>
+        /// 账号状态:1、启用,2、删除,3、禁用
+        /// </summary>
+        [XmlElement("status")]
+        public long Status { get; set; }
+
+        /// <summary>
+        /// 账号创建类型:1、通过短信创建,2、ISV批量导入,3、ISV OAuth创建
+        /// </summary>
+        [XmlElement("type")]
+        public long Type { get; set; }
+
+        /// <summary>
+        /// 记录的版本号
+        /// </summary>
+        [XmlElement("version")]
+        public long Version { get; set; }
+
+        /// <summary>
+        /// 旺旺
+        /// </summary>
+        [XmlElement("wangwang")]
+        public string Wangwang { get; set; }
+
+        /// <summary>
+        /// 微信
+        /// </summary>
+        [XmlElement("weixin")]
+        public string Weixin { get; set; }
+    }
+}

+ 36 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/OpenAccountResult.cs

@@ -0,0 +1,36 @@
+using System;
+using System.Xml.Serialization;
+
+namespace Top.Api.Domain
+{
+    /// <summary>
+    /// OpenAccountResult Data Structure.
+    /// </summary>
+    [Serializable]
+    public class OpenAccountResult : TopObject
+    {
+        /// <summary>
+        /// 错误码
+        /// </summary>
+        [XmlElement("code")]
+        public long Code { get; set; }
+
+        /// <summary>
+        /// Open Account信息
+        /// </summary>
+        [XmlElement("data")]
+        public Top.Api.Domain.OpenAccount Data { get; set; }
+
+        /// <summary>
+        /// 错误信息
+        /// </summary>
+        [XmlElement("message")]
+        public string Message { get; set; }
+
+        /// <summary>
+        /// 是否成功
+        /// </summary>
+        [XmlElement("successful")]
+        public bool Successful { get; set; }
+    }
+}

+ 38 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/OpenAccountSearchResult.cs

@@ -0,0 +1,38 @@
+using System;
+using System.Xml.Serialization;
+using System.Collections.Generic;
+
+namespace Top.Api.Domain
+{
+    /// <summary>
+    /// OpenAccountSearchResult Data Structure.
+    /// </summary>
+    [Serializable]
+    public class OpenAccountSearchResult : TopObject
+    {
+        /// <summary>
+        /// 状态码
+        /// </summary>
+        [XmlElement("code")]
+        public long Code { get; set; }
+
+        /// <summary>
+        /// OpenAccount的列表
+        /// </summary>
+        [XmlArray("datas")]
+        [XmlArrayItem("open_account")]
+        public List<Top.Api.Domain.OpenAccount> Datas { get; set; }
+
+        /// <summary>
+        /// 状态信息
+        /// </summary>
+        [XmlElement("message")]
+        public string Message { get; set; }
+
+        /// <summary>
+        /// 查询是否成功,成功返回时有可能数据为空
+        /// </summary>
+        [XmlElement("successful")]
+        public bool Successful { get; set; }
+    }
+}

+ 36 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/OpenAccountTokenValidateResult.cs

@@ -0,0 +1,36 @@
+using System;
+using System.Xml.Serialization;
+
+namespace Top.Api.Domain
+{
+    /// <summary>
+    /// OpenAccountTokenValidateResult Data Structure.
+    /// </summary>
+    [Serializable]
+    public class OpenAccountTokenValidateResult : TopObject
+    {
+        /// <summary>
+        /// 错误码
+        /// </summary>
+        [XmlElement("code")]
+        public long Code { get; set; }
+
+        /// <summary>
+        /// token中的数据
+        /// </summary>
+        [XmlElement("data")]
+        public Top.Api.Domain.TokenInfo Data { get; set; }
+
+        /// <summary>
+        /// 错误信息
+        /// </summary>
+        [XmlElement("message")]
+        public string Message { get; set; }
+
+        /// <summary>
+        /// 是否成功
+        /// </summary>
+        [XmlElement("successful")]
+        public bool Successful { get; set; }
+    }
+}

+ 36 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/OpenaccountLong.cs

@@ -0,0 +1,36 @@
+using System;
+using System.Xml.Serialization;
+
+namespace Top.Api.Domain
+{
+    /// <summary>
+    /// OpenaccountLong Data Structure.
+    /// </summary>
+    [Serializable]
+    public class OpenaccountLong : TopObject
+    {
+        /// <summary>
+        /// 返回码
+        /// </summary>
+        [XmlElement("code")]
+        public long Code { get; set; }
+
+        /// <summary>
+        /// 返回数据
+        /// </summary>
+        [XmlElement("data")]
+        public long Data { get; set; }
+
+        /// <summary>
+        /// 返回信息
+        /// </summary>
+        [XmlElement("message")]
+        public string Message { get; set; }
+
+        /// <summary>
+        /// 是否成功
+        /// </summary>
+        [XmlElement("successful")]
+        public bool Successful { get; set; }
+    }
+}

+ 36 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/OpenaccountObject.cs

@@ -0,0 +1,36 @@
+using System;
+using System.Xml.Serialization;
+
+namespace Top.Api.Domain
+{
+    /// <summary>
+    /// OpenaccountObject Data Structure.
+    /// </summary>
+    [Serializable]
+    public class OpenaccountObject : TopObject
+    {
+        /// <summary>
+        /// 错误码
+        /// </summary>
+        [XmlElement("code")]
+        public long Code { get; set; }
+
+        /// <summary>
+        /// 返回数据
+        /// </summary>
+        [XmlElement("data")]
+        public Top.Api.Domain.OpenAccount Data { get; set; }
+
+        /// <summary>
+        /// 返回信息
+        /// </summary>
+        [XmlElement("message")]
+        public string Message { get; set; }
+
+        /// <summary>
+        /// 是否成功
+        /// </summary>
+        [XmlElement("successful")]
+        public bool Successful { get; set; }
+    }
+}

+ 30 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/OpenaccountVoid.cs

@@ -0,0 +1,30 @@
+using System;
+using System.Xml.Serialization;
+
+namespace Top.Api.Domain
+{
+    /// <summary>
+    /// OpenaccountVoid Data Structure.
+    /// </summary>
+    [Serializable]
+    public class OpenaccountVoid : TopObject
+    {
+        /// <summary>
+        /// 错误码
+        /// </summary>
+        [XmlElement("code")]
+        public long Code { get; set; }
+
+        /// <summary>
+        /// 返回信息
+        /// </summary>
+        [XmlElement("message")]
+        public string Message { get; set; }
+
+        /// <summary>
+        /// 是否成功
+        /// </summary>
+        [XmlElement("successful")]
+        public bool Successful { get; set; }
+    }
+}

+ 159 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/Product.cs

@@ -0,0 +1,159 @@
+using System;
+using System.Xml.Serialization;
+using System.Collections.Generic;
+
+namespace Top.Api.Domain
+{
+    /// <summary>
+    /// Product Data Structure.
+    /// </summary>
+    [Serializable]
+    public class Product : TopObject
+    {
+        /// <summary>
+        /// 产品的非关键属性列表.格式:pid:vid;pid:vid.
+        /// </summary>
+        [XmlElement("binds")]
+        public string Binds { get; set; }
+
+        /// <summary>
+        /// 产品的非关键属性字符串列表.格式同props_str(<strong>注:</strong><font color="red">属性名称中的冒号":"被转换为:"#cln#";   分号";"被转换为:"#scln#" </font>)
+        /// </summary>
+        [XmlElement("binds_str")]
+        public string BindsStr { get; set; }
+
+        /// <summary>
+        /// 商品类目名称
+        /// </summary>
+        [XmlElement("cat_name")]
+        public string CatName { get; set; }
+
+        /// <summary>
+        /// 商品类目ID.必须是叶子类目ID
+        /// </summary>
+        [XmlElement("cid")]
+        public long Cid { get; set; }
+
+        /// <summary>
+        /// 创建时间.格式:yyyy-mm-dd hh:mm:ss
+        /// </summary>
+        [XmlElement("created")]
+        public string Created { get; set; }
+
+        /// <summary>
+        /// 用户自定义属性,结构:pid1:value1;pid2:value2 例如:“20000:优衣库”,表示“品牌:优衣库”
+        /// </summary>
+        [XmlElement("customer_props")]
+        public string CustomerProps { get; set; }
+
+        /// <summary>
+        /// 产品的描述.最大25000个字节
+        /// </summary>
+        [XmlElement("desc")]
+        public string Desc { get; set; }
+
+        /// <summary>
+        /// 修改时间.格式:yyyy-mm-dd hh:mm:ss
+        /// </summary>
+        [XmlElement("modified")]
+        public string Modified { get; set; }
+
+        /// <summary>
+        /// 产品名称
+        /// </summary>
+        [XmlElement("name")]
+        public string Name { get; set; }
+
+        /// <summary>
+        /// 外部产品ID
+        /// </summary>
+        [XmlElement("outer_id")]
+        public string OuterId { get; set; }
+
+        /// <summary>
+        /// 产品的主图片地址.(绝对地址,格式:http://host/image_path)
+        /// </summary>
+        [XmlElement("pic_url")]
+        public string PicUrl { get; set; }
+
+        /// <summary>
+        /// 产品的市场价.单位为元.精确到2位小数;如:200.07
+        /// </summary>
+        [XmlElement("price")]
+        public string Price { get; set; }
+
+        /// <summary>
+        /// 产品ID
+        /// </summary>
+        [XmlElement("product_id")]
+        public long ProductId { get; set; }
+
+        /// <summary>
+        /// 产品的子图片.目前最多支持4张。fields中设置为product_imgs.id、product_imgs.url、product_imgs.position 等形式就会返回相应的字段
+        /// </summary>
+        [XmlArray("product_imgs")]
+        [XmlArrayItem("product_img")]
+        public List<Top.Api.Domain.ProductImg> ProductImgs { get; set; }
+
+        /// <summary>
+        /// 产品的属性图片.比如说黄色对应的产品图片,绿色对应的产品图片。fields中设置为product_prop_imgs.id、   product_prop_imgs.props、product_prop_imgs.url、product_prop_imgs.position等形式就会返回相应的字段
+        /// </summary>
+        [XmlArray("product_prop_imgs")]
+        [XmlArrayItem("product_prop_img")]
+        public List<Top.Api.Domain.ProductPropImg> ProductPropImgs { get; set; }
+
+        /// <summary>
+        /// 销售属性值别名。格式为pid1:vid1:alias1;pid1:vid2:alia2。
+        /// </summary>
+        [XmlElement("property_alias")]
+        public string PropertyAlias { get; set; }
+
+        /// <summary>
+        /// 产品的关键属性列表.格式:pid:vid;pid:vid
+        /// </summary>
+        [XmlElement("props")]
+        public string Props { get; set; }
+
+        /// <summary>
+        /// 产品的关键属性字符串列表.比如:品牌:诺基亚;型号:N73(<strong>注:</strong><font color="red">属性名称中的冒号":"被转换为:"#cln#";   分号";"被转换为:"#scln#" </font>)
+        /// </summary>
+        [XmlElement("props_str")]
+        public string PropsStr { get; set; }
+
+        /// <summary>
+        /// 产品的销售属性列表.格式:pid:vid;pid:vid
+        /// </summary>
+        [XmlElement("sale_props")]
+        public string SaleProps { get; set; }
+
+        /// <summary>
+        /// 产品的销售属性字符串列表.格式同props_str(<strong>注:</strong><font color="red">属性名称中的冒号":"被转换为:"#cln#";   分号";"被转换为:"#scln#" </font>)
+        /// </summary>
+        [XmlElement("sale_props_str")]
+        public string SalePropsStr { get; set; }
+
+        /// <summary>
+        /// 产品卖点描述,长度限制20个汉字
+        /// </summary>
+        [XmlElement("sell_pt")]
+        public string SellPt { get; set; }
+
+        /// <summary>
+        /// 当前状态(0 商家确认 1 屏蔽 3 小二确认 2 未确认 -1 删除)
+        /// </summary>
+        [XmlElement("status")]
+        public long Status { get; set; }
+
+        /// <summary>
+        /// 淘宝标准产品编码
+        /// </summary>
+        [XmlElement("tsc")]
+        public string Tsc { get; set; }
+
+        /// <summary>
+        /// 垂直市场,如:3(3C),4(鞋城)
+        /// </summary>
+        [XmlElement("vertical_market")]
+        public long VerticalMarket { get; set; }
+    }
+}

+ 48 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/ProductImg.cs

@@ -0,0 +1,48 @@
+using System;
+using System.Xml.Serialization;
+
+namespace Top.Api.Domain
+{
+    /// <summary>
+    /// ProductImg Data Structure.
+    /// </summary>
+    [Serializable]
+    public class ProductImg : TopObject
+    {
+        /// <summary>
+        /// 添加时间.格式:yyyy-mm-dd hh:mm:ss
+        /// </summary>
+        [XmlElement("created")]
+        public string Created { get; set; }
+
+        /// <summary>
+        /// 产品图片ID
+        /// </summary>
+        [XmlElement("id")]
+        public long Id { get; set; }
+
+        /// <summary>
+        /// 修改时间.格式:yyyy-mm-dd hh:mm:ss
+        /// </summary>
+        [XmlElement("modified")]
+        public string Modified { get; set; }
+
+        /// <summary>
+        /// 图片序号。产品里的图片展示顺序,数据越小越靠前。要求是正整数。
+        /// </summary>
+        [XmlElement("position")]
+        public long Position { get; set; }
+
+        /// <summary>
+        /// 图片所属产品的ID
+        /// </summary>
+        [XmlElement("product_id")]
+        public long ProductId { get; set; }
+
+        /// <summary>
+        /// 图片地址.(绝对地址,格式:http://host/image_path)
+        /// </summary>
+        [XmlElement("url")]
+        public string Url { get; set; }
+    }
+}

+ 54 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/ProductPropImg.cs

@@ -0,0 +1,54 @@
+using System;
+using System.Xml.Serialization;
+
+namespace Top.Api.Domain
+{
+    /// <summary>
+    /// ProductPropImg Data Structure.
+    /// </summary>
+    [Serializable]
+    public class ProductPropImg : TopObject
+    {
+        /// <summary>
+        /// 添加时间.格式:yyyy-mm-dd hh:mm:ss
+        /// </summary>
+        [XmlElement("created")]
+        public string Created { get; set; }
+
+        /// <summary>
+        /// 产品属性图片ID
+        /// </summary>
+        [XmlElement("id")]
+        public long Id { get; set; }
+
+        /// <summary>
+        /// 修改时间.格式:yyyy-mm-dd hh:mm:ss
+        /// </summary>
+        [XmlElement("modified")]
+        public string Modified { get; set; }
+
+        /// <summary>
+        /// 图片序号。产品里的图片展示顺序,数据越小越靠前。要求是正整数。
+        /// </summary>
+        [XmlElement("position")]
+        public long Position { get; set; }
+
+        /// <summary>
+        /// 图片所属产品的ID
+        /// </summary>
+        [XmlElement("product_id")]
+        public long ProductId { get; set; }
+
+        /// <summary>
+        /// 属性串(pid:vid),目前只有颜色属性.如:颜色:红色表示为 1627207:28326
+        /// </summary>
+        [XmlElement("props")]
+        public string Props { get; set; }
+
+        /// <summary>
+        /// 图片地址.(绝对地址,格式:http://host/image_path)
+        /// </summary>
+        [XmlElement("url")]
+        public string Url { get; set; }
+    }
+}

+ 42 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/PropImg.cs

@@ -0,0 +1,42 @@
+using System;
+using System.Xml.Serialization;
+
+namespace Top.Api.Domain
+{
+    /// <summary>
+    /// PropImg Data Structure.
+    /// </summary>
+    [Serializable]
+    public class PropImg : TopObject
+    {
+        /// <summary>
+        /// 图片创建时间 时间格式:yyyy-MM-dd HH:mm:ss
+        /// </summary>
+        [XmlElement("created")]
+        public string Created { get; set; }
+
+        /// <summary>
+        /// 属性图片的id,和商品相对应
+        /// </summary>
+        [XmlElement("id")]
+        public long Id { get; set; }
+
+        /// <summary>
+        /// 图片放在第几张(多图时可设置)
+        /// </summary>
+        [XmlElement("position")]
+        public long Position { get; set; }
+
+        /// <summary>
+        /// 图片所对应的属性组合的字符串
+        /// </summary>
+        [XmlElement("properties")]
+        public string Properties { get; set; }
+
+        /// <summary>
+        /// 图片链接地址
+        /// </summary>
+        [XmlElement("url")]
+        public string Url { get; set; }
+    }
+}

+ 74 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/PropValue.cs

@@ -0,0 +1,74 @@
+using System;
+using System.Xml.Serialization;
+using System.Collections.Generic;
+
+namespace Top.Api.Domain
+{
+    /// <summary>
+    /// PropValue Data Structure.
+    /// </summary>
+    [Serializable]
+    public class PropValue : TopObject
+    {
+        /// <summary>
+        /// 类目ID
+        /// </summary>
+        [XmlElement("cid")]
+        public long Cid { get; set; }
+
+        /// <summary>
+        /// 属性值feature
+        /// </summary>
+        [XmlArray("features")]
+        [XmlArrayItem("feature")]
+        public List<Top.Api.Domain.Feature> Features { get; set; }
+
+        /// <summary>
+        /// 是否为父类目属性
+        /// </summary>
+        [XmlElement("is_parent")]
+        public bool IsParent { get; set; }
+
+        /// <summary>
+        /// 属性值
+        /// </summary>
+        [XmlElement("name")]
+        public string Name { get; set; }
+
+        /// <summary>
+        /// 属性值别名
+        /// </summary>
+        [XmlElement("name_alias")]
+        public string NameAlias { get; set; }
+
+        /// <summary>
+        /// 属性 ID
+        /// </summary>
+        [XmlElement("pid")]
+        public long Pid { get; set; }
+
+        /// <summary>
+        /// 属性名
+        /// </summary>
+        [XmlElement("prop_name")]
+        public string PropName { get; set; }
+
+        /// <summary>
+        /// 排列序号。取值范围:大于零的整数
+        /// </summary>
+        [XmlElement("sort_order")]
+        public long SortOrder { get; set; }
+
+        /// <summary>
+        /// 状态。可选值:normal(正常),deleted(删除)
+        /// </summary>
+        [XmlElement("status")]
+        public string Status { get; set; }
+
+        /// <summary>
+        /// 属性值ID
+        /// </summary>
+        [XmlElement("vid")]
+        public long Vid { get; set; }
+    }
+}

+ 72 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/RdsConfig.cs

@@ -0,0 +1,72 @@
+using System;
+using System.Xml.Serialization;
+
+namespace Top.Api.Domain
+{
+    /// <summary>
+    /// RdsConfig Data Structure.
+    /// </summary>
+    [Serializable]
+    public class RdsConfig : TopObject
+    {
+        /// <summary>
+        /// appkey
+        /// </summary>
+        [XmlElement("appkey")]
+        public string Appkey { get; set; }
+
+        /// <summary>
+        /// 数据库tdp密码
+        /// </summary>
+        [XmlElement("db_tdp_ps")]
+        public string DbTdpPs { get; set; }
+
+        /// <summary>
+        /// 数据库tdp用户名
+        /// </summary>
+        [XmlElement("db_tdp_user")]
+        public string DbTdpUser { get; set; }
+
+        /// <summary>
+        /// 数据库类型,mysql/sqlserver
+        /// </summary>
+        [XmlElement("db_type")]
+        public string DbType { get; set; }
+
+        /// <summary>
+        /// 数据库用户名
+        /// </summary>
+        [XmlElement("db_user")]
+        public string DbUser { get; set; }
+
+        /// <summary>
+        /// 主机地址
+        /// </summary>
+        [XmlElement("host")]
+        public string Host { get; set; }
+
+        /// <summary>
+        /// 数据推送rds表里的id
+        /// </summary>
+        [XmlElement("id")]
+        public long Id { get; set; }
+
+        /// <summary>
+        /// rds数据库实例名
+        /// </summary>
+        [XmlElement("name")]
+        public string Name { get; set; }
+
+        /// <summary>
+        /// rds数据库端口
+        /// </summary>
+        [XmlElement("port")]
+        public string Port { get; set; }
+
+        /// <summary>
+        /// 数据库状态
+        /// </summary>
+        [XmlElement("status")]
+        public string Status { get; set; }
+    }
+}

+ 34 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/SellerAuthorize.cs

@@ -0,0 +1,34 @@
+using System;
+using System.Xml.Serialization;
+using System.Collections.Generic;
+
+namespace Top.Api.Domain
+{
+    /// <summary>
+    /// SellerAuthorize Data Structure.
+    /// </summary>
+    [Serializable]
+    public class SellerAuthorize : TopObject
+    {
+        /// <summary>
+        /// 品牌列表
+        /// </summary>
+        [XmlArray("brands")]
+        [XmlArrayItem("brand")]
+        public List<Top.Api.Domain.Brand> Brands { get; set; }
+
+        /// <summary>
+        /// 类目列表
+        /// </summary>
+        [XmlArray("item_cats")]
+        [XmlArrayItem("item_cat")]
+        public List<Top.Api.Domain.ItemCat> ItemCats { get; set; }
+
+        /// <summary>
+        /// 商品类目结构
+        /// </summary>
+        [XmlArray("xinpin_item_cats")]
+        [XmlArrayItem("item_cat")]
+        public List<Top.Api.Domain.ItemCat> XinpinItemCats { get; set; }
+    }
+}

+ 144 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/Sku.cs

@@ -0,0 +1,144 @@
+using System;
+using System.Xml.Serialization;
+
+namespace Top.Api.Domain
+{
+    /// <summary>
+    /// Sku Data Structure.
+    /// </summary>
+    [Serializable]
+    public class Sku : TopObject
+    {
+        /// <summary>
+        /// 商品级别的条形码
+        /// </summary>
+        [XmlElement("barcode")]
+        public string Barcode { get; set; }
+
+        /// <summary>
+        /// 基础色数据
+        /// </summary>
+        [XmlElement("change_prop")]
+        public string ChangeProp { get; set; }
+
+        /// <summary>
+        /// sku创建日期 时间格式:yyyy-MM-dd HH:mm:ss
+        /// </summary>
+        [XmlElement("created")]
+        public string Created { get; set; }
+
+        /// <summary>
+        /// skuDeliveryTimeType
+        /// </summary>
+        [XmlElement("delivery_time_type")]
+        public string DeliveryTimeType { get; set; }
+
+        /// <summary>
+        /// 扩展sku的id
+        /// </summary>
+        [XmlElement("extra_id")]
+        public long ExtraId { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        [XmlElement("gmt_modified")]
+        public string GmtModified { get; set; }
+
+        /// <summary>
+        /// sku所属商品id(注意:iid近期即将废弃,请用num_iid参数)
+        /// </summary>
+        [XmlElement("iid")]
+        public string Iid { get; set; }
+
+        /// <summary>
+        /// 扩展sku的备注信息
+        /// </summary>
+        [XmlElement("memo")]
+        public string Memo { get; set; }
+
+        /// <summary>
+        /// sku最后修改日期 时间格式:yyyy-MM-dd HH:mm:ss
+        /// </summary>
+        [XmlElement("modified")]
+        public string Modified { get; set; }
+
+        /// <summary>
+        /// sku所属商品数字id
+        /// </summary>
+        [XmlElement("num_iid")]
+        public long NumIid { get; set; }
+
+        /// <summary>
+        /// 商家设置的外部id。天猫和集市的卖家,需要登录才能获取到自己的商家编码,不能获取到他人的商家编码。
+        /// </summary>
+        [XmlElement("outer_id")]
+        public string OuterId { get; set; }
+
+        /// <summary>
+        /// 属于这个sku的商品的价格 取值范围:0-100000000;精确到2位小数;单位:元。如:200.07,表示:200元7分。
+        /// </summary>
+        [XmlElement("price")]
+        public string Price { get; set; }
+
+        /// <summary>
+        /// sku的销售属性组合字符串(颜色,大小,等等,可通过类目API获取某类目下的销售属性),格式是p1:v1;p2:v2
+        /// </summary>
+        [XmlElement("properties")]
+        public string Properties { get; set; }
+
+        /// <summary>
+        /// sku所对应的销售属性的中文名字串,格式如:pid1:vid1:pid_name1:vid_name1;pid2:vid2:pid_name2:vid_name2……
+        /// </summary>
+        [XmlElement("properties_name")]
+        public string PropertiesName { get; set; }
+
+        /// <summary>
+        /// 属于这个sku的商品的数量,
+        /// </summary>
+        [XmlElement("quantity")]
+        public long Quantity { get; set; }
+
+        /// <summary>
+        /// sku级别发货时间
+        /// </summary>
+        [XmlElement("sku_delivery_time")]
+        public string SkuDeliveryTime { get; set; }
+
+        /// <summary>
+        /// skuFeature
+        /// </summary>
+        [XmlElement("sku_feature")]
+        public string SkuFeature { get; set; }
+
+        /// <summary>
+        /// sku的id
+        /// </summary>
+        [XmlElement("sku_id")]
+        public long SkuId { get; set; }
+
+        /// <summary>
+        /// 表示SKu上的产品规格信息
+        /// </summary>
+        [XmlElement("sku_spec_id")]
+        public long SkuSpecId { get; set; }
+
+        /// <summary>
+        /// specId
+        /// </summary>
+        [XmlElement("spec_id")]
+        public string SpecId { get; set; }
+
+        /// <summary>
+        /// sku状态。 normal:正常 ;delete:删除
+        /// </summary>
+        [XmlElement("status")]
+        public string Status { get; set; }
+
+        /// <summary>
+        /// 商品在付款减库存的状态下,该sku上未付款的订单数量
+        /// </summary>
+        [XmlElement("with_hold_quantity")]
+        public long WithHoldQuantity { get; set; }
+    }
+}

+ 26 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/TmcGroup.cs

@@ -0,0 +1,26 @@
+using System;
+using System.Xml.Serialization;
+using System.Collections.Generic;
+
+namespace Top.Api.Domain
+{
+    /// <summary>
+    /// TmcGroup Data Structure.
+    /// </summary>
+    [Serializable]
+    public class TmcGroup : TopObject
+    {
+        /// <summary>
+        /// 分组名称
+        /// </summary>
+        [XmlElement("name")]
+        public string Name { get; set; }
+
+        /// <summary>
+        /// 用户列表
+        /// </summary>
+        [XmlArray("users")]
+        [XmlArrayItem("string")]
+        public List<string> Users { get; set; }
+    }
+}

+ 54 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/TmcMessage.cs

@@ -0,0 +1,54 @@
+using System;
+using System.Xml.Serialization;
+
+namespace Top.Api.Domain
+{
+    /// <summary>
+    /// TmcMessage Data Structure.
+    /// </summary>
+    [Serializable]
+    public class TmcMessage : TopObject
+    {
+        /// <summary>
+        /// 消息详细内容,格式为JSON/XML
+        /// </summary>
+        [XmlElement("content")]
+        public string Content { get; set; }
+
+        /// <summary>
+        /// 消息ID
+        /// </summary>
+        [XmlElement("id")]
+        public long Id { get; set; }
+
+        /// <summary>
+        /// 消息发布者的AppKey
+        /// </summary>
+        [XmlElement("pub_app_key")]
+        public string PubAppKey { get; set; }
+
+        /// <summary>
+        /// 消息发布时间
+        /// </summary>
+        [XmlElement("pub_time")]
+        public string PubTime { get; set; }
+
+        /// <summary>
+        /// 消息所属主题
+        /// </summary>
+        [XmlElement("topic")]
+        public string Topic { get; set; }
+
+        /// <summary>
+        /// 消息所属的用户编号
+        /// </summary>
+        [XmlElement("user_id")]
+        public long UserId { get; set; }
+
+        /// <summary>
+        /// 用户的昵称
+        /// </summary>
+        [XmlElement("user_nick")]
+        public string UserNick { get; set; }
+    }
+}

+ 30 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/TmcProduceResult.cs

@@ -0,0 +1,30 @@
+using System;
+using System.Xml.Serialization;
+
+namespace Top.Api.Domain
+{
+    /// <summary>
+    /// TmcProduceResult Data Structure.
+    /// </summary>
+    [Serializable]
+    public class TmcProduceResult : TopObject
+    {
+        /// <summary>
+        /// 错误码
+        /// </summary>
+        [XmlElement("error_code")]
+        public string ErrorCode { get; set; }
+
+        /// <summary>
+        /// 错误信息
+        /// </summary>
+        [XmlElement("error_message")]
+        public string ErrorMessage { get; set; }
+
+        /// <summary>
+        /// 是否成功
+        /// </summary>
+        [XmlElement("is_success")]
+        public bool IsSuccess { get; set; }
+    }
+}

+ 62 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/TmcUser.cs

@@ -0,0 +1,62 @@
+using System;
+using System.Xml.Serialization;
+using System.Collections.Generic;
+
+namespace Top.Api.Domain
+{
+    /// <summary>
+    /// TmcUser Data Structure.
+    /// </summary>
+    [Serializable]
+    public class TmcUser : TopObject
+    {
+        /// <summary>
+        /// 用户首次开通时间
+        /// </summary>
+        [XmlElement("created")]
+        public string Created { get; set; }
+
+        /// <summary>
+        /// 接收用户消息的组名
+        /// </summary>
+        [XmlElement("group_name")]
+        public string GroupName { get; set; }
+
+        /// <summary>
+        /// 用户授权是否有效,true表示授权有效,false表示授权过期
+        /// </summary>
+        [XmlElement("is_valid")]
+        public bool IsValid { get; set; }
+
+        /// <summary>
+        /// 用户最后开通时间
+        /// </summary>
+        [XmlElement("modified")]
+        public string Modified { get; set; }
+
+        /// <summary>
+        /// 用户开通的消息类型列表。如果为空表示应用开通的所有类型
+        /// </summary>
+        [XmlArray("topics")]
+        [XmlArrayItem("string")]
+        public List<string> Topics { get; set; }
+
+        /// <summary>
+        /// 用户ID
+        /// </summary>
+        [XmlElement("user_id")]
+        public long UserId { get; set; }
+
+        /// <summary>
+        /// 用户昵称
+        /// </summary>
+        [XmlElement("user_nick")]
+        public string UserNick { get; set; }
+
+        /// <summary>
+        /// 用户所属的平台类型,tbUIC:淘宝用户; icbu: icbu用户
+        /// </summary>
+        [XmlElement("user_platform")]
+        public string UserPlatform { get; set; }
+    }
+}

+ 48 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/TokenInfo.cs

@@ -0,0 +1,48 @@
+using System;
+using System.Xml.Serialization;
+
+namespace Top.Api.Domain
+{
+    /// <summary>
+    /// TokenInfo Data Structure.
+    /// </summary>
+    [Serializable]
+    public class TokenInfo : TopObject
+    {
+        /// <summary>
+        /// token info扩展信息
+        /// </summary>
+        [XmlElement("ext")]
+        public Top.Api.Domain.TokenInfoExt Ext { get; set; }
+
+        /// <summary>
+        /// isv自己账号的唯一id
+        /// </summary>
+        [XmlElement("isv_account_id")]
+        public string IsvAccountId { get; set; }
+
+        /// <summary>
+        /// ISV APP的登录态时长
+        /// </summary>
+        [XmlElement("login_state_expire_in")]
+        public long LoginStateExpireIn { get; set; }
+
+        /// <summary>
+        /// open account id
+        /// </summary>
+        [XmlElement("open_account_id")]
+        public long OpenAccountId { get; set; }
+
+        /// <summary>
+        /// 时间戳
+        /// </summary>
+        [XmlElement("timestamp")]
+        public long Timestamp { get; set; }
+
+        /// <summary>
+        /// 用于防重放的唯一id
+        /// </summary>
+        [XmlElement("uuid")]
+        public string Uuid { get; set; }
+    }
+}

+ 24 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/TokenInfoExt.cs

@@ -0,0 +1,24 @@
+using System;
+using System.Xml.Serialization;
+
+namespace Top.Api.Domain
+{
+    /// <summary>
+    /// TokenInfoExt Data Structure.
+    /// </summary>
+    [Serializable]
+    public class TokenInfoExt : TopObject
+    {
+        /// <summary>
+        /// oauthOtherInfo
+        /// </summary>
+        [XmlElement("oauth_other_info")]
+        public Top.Api.Domain.OAuthOtherInfo OauthOtherInfo { get; set; }
+
+        /// <summary>
+        /// open account当前token info中open account id对应的open account信息
+        /// </summary>
+        [XmlElement("open_account")]
+        public Top.Api.Domain.OpenAccount OpenAccount { get; set; }
+    }
+}

+ 30 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/TradeConfirmFee.cs

@@ -0,0 +1,30 @@
+using System;
+using System.Xml.Serialization;
+
+namespace Top.Api.Domain
+{
+    /// <summary>
+    /// TradeConfirmFee Data Structure.
+    /// </summary>
+    [Serializable]
+    public class TradeConfirmFee : TopObject
+    {
+        /// <summary>
+        /// 确认收货的金额(不包含邮费)。精确到2位小数;单位:元。如:200.07,表示:200元7分
+        /// </summary>
+        [XmlElement("confirm_fee")]
+        public string ConfirmFee { get; set; }
+
+        /// <summary>
+        /// 需确认收货的邮费,当不是最后一笔收货或者没有邮费时是0.00。精确到2位小数;单位:元。如:200.07,表示:200元7分
+        /// </summary>
+        [XmlElement("confirm_post_fee")]
+        public string ConfirmPostFee { get; set; }
+
+        /// <summary>
+        /// 是否是最后一笔订单(只对子订单有效,当其他子订单都是交易完成时,返回true,否则false)
+        /// </summary>
+        [XmlElement("is_last_order")]
+        public bool IsLastOrder { get; set; }
+    }
+}

+ 54 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/TradeTrace.cs

@@ -0,0 +1,54 @@
+using System;
+using System.Xml.Serialization;
+
+namespace Top.Api.Domain
+{
+    /// <summary>
+    /// TradeTrace Data Structure.
+    /// </summary>
+    [Serializable]
+    public class TradeTrace : TopObject
+    {
+        /// <summary>
+        /// 动作发生的时间
+        /// </summary>
+        [XmlElement("action_time")]
+        public string ActionTime { get; set; }
+
+        /// <summary>
+        /// 应用标题
+        /// </summary>
+        [XmlElement("app_title")]
+        public string AppTitle { get; set; }
+
+        /// <summary>
+        /// 子订单的id列表,以逗号分割
+        /// </summary>
+        [XmlElement("order_ids")]
+        public string OrderIds { get; set; }
+
+        /// <summary>
+        /// 备注字段
+        /// </summary>
+        [XmlElement("remark")]
+        public string Remark { get; set; }
+
+        /// <summary>
+        /// 卖家的淘宝nick
+        /// </summary>
+        [XmlElement("seller_nick")]
+        public string SellerNick { get; set; }
+
+        /// <summary>
+        /// 回流的订单状态
+        /// </summary>
+        [XmlElement("status")]
+        public string Status { get; set; }
+
+        /// <summary>
+        /// 交易tid
+        /// </summary>
+        [XmlElement("tid")]
+        public long Tid { get; set; }
+    }
+}

+ 264 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/User.cs

@@ -0,0 +1,264 @@
+using System;
+using System.Xml.Serialization;
+
+namespace Top.Api.Domain
+{
+    /// <summary>
+    /// User Data Structure.
+    /// </summary>
+    [Serializable]
+    public class User : TopObject
+    {
+        /// <summary>
+        /// 支付宝账户
+        /// </summary>
+        [XmlElement("alipay_account")]
+        public string AlipayAccount { get; set; }
+
+        /// <summary>
+        /// 有无绑定。可选值:bind(绑定),notbind(未绑定)
+        /// </summary>
+        [XmlElement("alipay_bind")]
+        public string AlipayBind { get; set; }
+
+        /// <summary>
+        /// 支付宝ID
+        /// </summary>
+        [XmlElement("alipay_no")]
+        public string AlipayNo { get; set; }
+
+        /// <summary>
+        /// 是否受限制。可选值:limited(受限制),unlimited(不受限)
+        /// </summary>
+        [XmlElement("auto_repost")]
+        public string AutoRepost { get; set; }
+
+        /// <summary>
+        /// 用户头像地址
+        /// </summary>
+        [XmlElement("avatar")]
+        public string Avatar { get; set; }
+
+        /// <summary>
+        /// 生日
+        /// </summary>
+        [XmlElement("birthday")]
+        public string Birthday { get; set; }
+
+        /// <summary>
+        /// 买家信用
+        /// </summary>
+        [XmlElement("buyer_credit")]
+        public Top.Api.Domain.UserCredit BuyerCredit { get; set; }
+
+        /// <summary>
+        /// 是否参加消保
+        /// </summary>
+        [XmlElement("consumer_protection")]
+        public bool ConsumerProtection { get; set; }
+
+        /// <summary>
+        /// 用户注册时间。格式:yyyy-MM-dd HH:mm:ss
+        /// </summary>
+        [XmlElement("created")]
+        public string Created { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        [XmlElement("email")]
+        public string Email { get; set; }
+
+        /// <summary>
+        /// 是否购买多图服务。可选值:true(是),false(否)
+        /// </summary>
+        [XmlElement("has_more_pic")]
+        public bool HasMorePic { get; set; }
+
+        /// <summary>
+        /// 用户作为卖家是否开过店
+        /// </summary>
+        [XmlElement("has_shop")]
+        public bool HasShop { get; set; }
+
+        /// <summary>
+        /// 表示用户是否具备修改商品减库存逻辑的权限(一共有拍下减库存和付款减库存两种逻辑) 值含义: 1)true:是 2)false:否。
+        /// </summary>
+        [XmlElement("has_sub_stock")]
+        public bool HasSubStock { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        [XmlElement("id_card")]
+        public string IdCard { get; set; }
+
+        /// <summary>
+        /// 用户是否是金牌卖家
+        /// </summary>
+        [XmlElement("is_golden_seller")]
+        public bool IsGoldenSeller { get; set; }
+
+        /// <summary>
+        /// 是否24小时闪电发货(实物类) 
+        /// </summary>
+        [XmlElement("is_lightning_consignment")]
+        public bool IsLightningConsignment { get; set; }
+
+        /// <summary>
+        /// 是否是特价版商家,需要field查询
+        /// </summary>
+        [XmlElement("is_tjb_seller")]
+        public bool IsTjbSeller { get; set; }
+
+        /// <summary>
+        /// 可上传商品图片数量
+        /// </summary>
+        [XmlElement("item_img_num")]
+        public long ItemImgNum { get; set; }
+
+        /// <summary>
+        /// 单张商品图片最大容量(商品主图大小)。单位:k
+        /// </summary>
+        [XmlElement("item_img_size")]
+        public long ItemImgSize { get; set; }
+
+        /// <summary>
+        /// 最近登陆时间。格式:yyyy-MM-dd HH:mm:ss
+        /// </summary>
+        [XmlElement("last_visit")]
+        public string LastVisit { get; set; }
+
+        /// <summary>
+        /// 是否是无名良品用户,true or false
+        /// </summary>
+        [XmlElement("liangpin")]
+        public bool Liangpin { get; set; }
+
+        /// <summary>
+        /// 用户当前居住地公开信息。如:location.city获取其中的city数据
+        /// </summary>
+        [XmlElement("location")]
+        public Top.Api.Domain.Location Location { get; set; }
+
+        /// <summary>
+        /// 是否订阅了淘宝天下杂志
+        /// </summary>
+        [XmlElement("magazine_subscribe")]
+        public bool MagazineSubscribe { get; set; }
+
+        /// <summary>
+        /// 是否可以经营图书 ,true or false
+        /// </summary>
+        [XmlElement("manage_book")]
+        public bool ManageBook { get; set; }
+
+        /// <summary>
+        /// 联系人手机号码
+        /// </summary>
+        [XmlElement("mobile")]
+        public string Mobile { get; set; }
+
+        /// <summary>
+        /// 用户昵称
+        /// </summary>
+        [XmlElement("nick")]
+        public string Nick { get; set; }
+
+        /// <summary>
+        /// 用户是否为网游用户,属于隐私信息,需要登陆才能查看自己的。 目前仅供taobao.user.get使用
+        /// </summary>
+        [XmlElement("online_gaming")]
+        public bool OnlineGaming { get; set; }
+
+        /// <summary>
+        /// 用户openId
+        /// </summary>
+        [XmlElement("open_uid")]
+        public string OpenUid { get; set; }
+
+        /// <summary>
+        /// 有无实名认证。可选值:authentication(实名认证),not authentication(没有认证)
+        /// </summary>
+        [XmlElement("promoted_type")]
+        public string PromotedType { get; set; }
+
+        /// <summary>
+        /// 可上传属性图片数量
+        /// </summary>
+        [XmlElement("prop_img_num")]
+        public long PropImgNum { get; set; }
+
+        /// <summary>
+        /// 单张销售属性图片最大容量(非主图的商品图片和商品属性图片)。单位:k
+        /// </summary>
+        [XmlElement("prop_img_size")]
+        public long PropImgSize { get; set; }
+
+        /// <summary>
+        /// 卖家信用
+        /// </summary>
+        [XmlElement("seller_credit")]
+        public Top.Api.Domain.UserCredit SellerCredit { get; set; }
+
+        /// <summary>
+        /// 性别。可选值:m(男),f(女)
+        /// </summary>
+        [XmlElement("sex")]
+        public string Sex { get; set; }
+
+        /// <summary>
+        /// 用户是否签署消保协议(因为加入基本消保的用户未必签署了消保协议)
+        /// </summary>
+        [XmlElement("sign_consumer_protection")]
+        public bool SignConsumerProtection { get; set; }
+
+        /// <summary>
+        /// 卖家是否签署食品卖家承诺协议
+        /// </summary>
+        [XmlElement("sign_food_seller_promise")]
+        public bool SignFoodSellerPromise { get; set; }
+
+        /// <summary>
+        /// 状态。可选值:normal(正常),inactive(未激活),delete(删除),reeze(冻结),supervise(监管)
+        /// </summary>
+        [XmlElement("status")]
+        public string Status { get; set; }
+
+        /// <summary>
+        /// 用户类型。可选值:B(B商家),C(C商家)
+        /// </summary>
+        [XmlElement("type")]
+        public string Type { get; set; }
+
+        /// <summary>
+        /// 用户字符串ID
+        /// </summary>
+        [XmlElement("uid")]
+        public string Uid { get; set; }
+
+        /// <summary>
+        /// 用户数字ID
+        /// </summary>
+        [XmlElement("user_id")]
+        public long UserId { get; set; }
+
+        /// <summary>
+        /// 用户参与垂直市场类型。shoes表示鞋城垂直市场用户,3C表示3C垂直市场用户。多个类型之间用&quot;,&quot;分隔。如:一个用户既是3C用户又是鞋城用户,那么这个字段返回就是&quot;3C,shoes&quot;。如果用户不是垂直市场用户,此字段返回为&quot;&quot;。
+        /// </summary>
+        [XmlElement("vertical_market")]
+        public string VerticalMarket { get; set; }
+
+        /// <summary>
+        /// 用户的全站vip信息,可取值如下:c(普通会员),asso_vip(荣誉会员),vip1,vip2,vip3,vip4,vip5,vip6(六个等级的正式vip会员),共8种取值,其中asso_vip是由vip会员衰退而成,与主站上的vip0对应。
+        /// </summary>
+        [XmlElement("vip_info")]
+        public string VipInfo { get; set; }
+
+        /// <summary>
+        /// 用户等级身份:20 apass;10 超级会员;0 普通会员
+        /// </summary>
+        [XmlElement("vip_level")]
+        public long VipLevel { get; set; }
+    }
+}

+ 36 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/UserCredit.cs

@@ -0,0 +1,36 @@
+using System;
+using System.Xml.Serialization;
+
+namespace Top.Api.Domain
+{
+    /// <summary>
+    /// UserCredit Data Structure.
+    /// </summary>
+    [Serializable]
+    public class UserCredit : TopObject
+    {
+        /// <summary>
+        /// 收到的好评总条数。取值范围:大于零的整数
+        /// </summary>
+        [XmlElement("good_num")]
+        public long GoodNum { get; set; }
+
+        /// <summary>
+        /// 信用等级(是根据score生成的),信用等级:淘宝会员在淘宝网上的信用度,分为20个级别,级别如:level = 1 时,表示一心;level = 2 时,表示二心
+        /// </summary>
+        [XmlElement("level")]
+        public long Level { get; set; }
+
+        /// <summary>
+        /// 信用总分(“好评”加一分,“中评”不加分,“差评”扣一分。分越高,等级越高)
+        /// </summary>
+        [XmlElement("score")]
+        public long Score { get; set; }
+
+        /// <summary>
+        /// 收到的评价总条数。取值范围:大于零的整数
+        /// </summary>
+        [XmlElement("total_num")]
+        public long TotalNum { get; set; }
+    }
+}

+ 54 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Domain/Video.cs

@@ -0,0 +1,54 @@
+using System;
+using System.Xml.Serialization;
+
+namespace Top.Api.Domain
+{
+    /// <summary>
+    /// Video Data Structure.
+    /// </summary>
+    [Serializable]
+    public class Video : TopObject
+    {
+        /// <summary>
+        /// 视频关联记录创建时间(格式:yyyy-MM-dd HH:mm:ss)
+        /// </summary>
+        [XmlElement("created")]
+        public string Created { get; set; }
+
+        /// <summary>
+        /// 视频关联记录的id,和商品相对应
+        /// </summary>
+        [XmlElement("id")]
+        public long Id { get; set; }
+
+        /// <summary>
+        /// 视频记录关联的商品的数字id(注意:iid近期即将废弃,请用num_iid参数)
+        /// </summary>
+        [XmlElement("iid")]
+        public string Iid { get; set; }
+
+        /// <summary>
+        /// 视频关联记录修改时间(格式:yyyy-MM-dd HH:mm:ss)
+        /// </summary>
+        [XmlElement("modified")]
+        public string Modified { get; set; }
+
+        /// <summary>
+        /// 视频记录所关联的商品的数字id
+        /// </summary>
+        [XmlElement("num_iid")]
+        public long NumIid { get; set; }
+
+        /// <summary>
+        /// video的url连接地址。淘秀里视频记录里面存储的url地址
+        /// </summary>
+        [XmlElement("url")]
+        public string Url { get; set; }
+
+        /// <summary>
+        /// video的id,对应于视频在淘秀的存储记录
+        /// </summary>
+        [XmlElement("video_id")]
+        public long VideoId { get; set; }
+    }
+}

+ 74 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Generated/FastJson/Formatter.cs

@@ -0,0 +1,74 @@
+using System.Collections.Generic;
+using System.Text;
+
+namespace FastJSON
+{
+    internal static class Formatter
+    {
+        public static string Indent = "   ";
+
+        public static void AppendIndent(StringBuilder sb, int count)
+        {
+            for (; count > 0; --count) sb.Append(Indent);
+        }
+
+        public static string PrettyPrint(string input)
+        {
+            var output = new StringBuilder();
+            int depth = 0;
+            int len = input.Length;
+            char[] chars = input.ToCharArray();
+            for (int i = 0; i < len; ++i)
+            {
+                char ch = chars[i];
+
+                if (ch == '\"') // found string span
+                {
+                    bool str = true;
+                    while (str)
+                    {
+                        output.Append(ch);
+                        ch = chars[++i];
+                        if (ch == '\\')
+                        {
+                            output.Append(ch);
+                            ch = chars[++i];
+                        }
+                        else if (ch == '\"')
+                            str = false;
+                    }
+                }
+
+                switch (ch)
+                {
+                    case '{':
+                    case '[':
+                        output.Append(ch);
+                        output.AppendLine();
+                        AppendIndent(output, ++depth);
+                        break;
+                    case '}':
+                    case ']':
+                        output.AppendLine();
+                        AppendIndent(output, --depth);
+                        output.Append(ch);
+                        break;
+                    case ',':
+                        output.Append(ch);
+                        output.AppendLine();
+                        AppendIndent(output, depth);
+                        break;
+                    case ':':
+                        output.Append(" : ");
+                        break;
+                    default:
+                        if (!char.IsWhiteSpace(ch))
+                            output.Append(ch);
+                        break;
+                }
+            }
+
+            return output.ToString();
+        }
+    }
+}

+ 11 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Generated/FastJson/Getters.cs

@@ -0,0 +1,11 @@
+using System;
+using System.Collections.Generic;
+
+namespace FastJSON
+{
+    public sealed class DatasetSchema
+    {
+        public List<string> Info ;//{ get; set; }
+        public string Name ;//{ get; set; }
+    }
+}

+ 1011 - 0
taobao-sdk-netcore-auto_1719572210878-20240628-source/Generated/FastJson/JSON.cs

@@ -0,0 +1,1011 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+#if !SILVERLIGHT
+using System.Data;
+#endif
+using System.Globalization;
+using System.IO;
+using System.Reflection;
+using System.Reflection.Emit;
+using System.Collections.Specialized;
+
+namespace FastJSON
+{
+    public delegate string Serialize(object data);
+    public delegate object Deserialize(string data);
+
+    public sealed class JSONParameters
+    {
+        /// <summary>
+        /// Use the optimized fast Dataset Schema format (default = True)
+        /// </summary>
+        public bool UseOptimizedDatasetSchema = true;
+        /// <summary>
+        /// Use the fast GUID format (default = True)
+        /// </summary>
+        public bool UseFastGuid = true;
+        /// <summary>
+        /// Serialize null values to the output (default = True)
+        /// </summary>
+        public bool SerializeNullValues = true;
+        /// <summary>
+        /// Use the UTC date format (default = True)
+        /// </summary>
+        public bool UseUTCDateTime = true;
+        /// <summary>
+        /// Show the readonly properties of types in the output (default = False)
+        /// </summary>
+        public bool ShowReadOnlyProperties = false;
+        /// <summary>
+        /// Use the $types extension to optimise the output json (default = True)
+        /// </summary>
+        public bool UsingGlobalTypes = true;
+        /// <summary>
+        /// Ignore case when processing json and deserializing 
+        /// </summary>
+        [Obsolete("Not needed anymore and will always match")]
+        public bool IgnoreCaseOnDeserialize = false;
+        /// <summary>
+        /// Anonymous types have read only properties 
+        /// </summary>
+        public bool EnableAnonymousTypes = false;
+        /// <summary>
+        /// Enable fastJSON extensions $types, $type, $map (default = True)
+        /// </summary>
+        public bool UseExtensions = true;
+        /// <summary>
+        /// Use escaped unicode i.e. \uXXXX format for non ASCII characters (default = True)
+        /// </summary>
+        public bool UseEscapedUnicode = false;
+        /// <summary>
+        /// Output string key dictionaries as "k"/"v" format (default = False) 
+        /// </summary>
+        public bool KVStyleStringDictionary = false;
+        /// <summary>
+        /// Output Enum values instead of names (default = False)
+        /// </summary>
+        public bool UseValuesOfEnums = false;
+        /// <summary>
+        /// Ignore attributes to check for (default : XmlIgnoreAttribute)
+        /// </summary>
+        public List<Type> IgnoreAttributes = new List<Type> { typeof(System.Xml.Serialization.XmlIgnoreAttribute) };
+        /// <summary>
+        /// If you have parametric and no default constructor for you classes (default = False)
+        /// 
+        /// IMPORTANT NOTE : If True then all initial values within the class will be ignored and will be not set
+        /// </summary>
+        public bool ParametricConstructorOverride = false;
+        /// <summary>
+        /// Serialize DateTime milliseconds i.e. yyyy-MM-dd HH:mm:ss.nnn (default = false)
+        /// </summary>
+        public bool DateTimeMilliseconds = false;
+        /// <summary>
+        /// Maximum depth for circular references in inline mode (default = 20)
+        /// </summary>
+        public byte SerializerMaxDepth = 20;
+        /// <summary>
+        /// Inline circular or already seen objects instead of replacement with $i (default = False) 
+        /// </summary>
+        public bool InlineCircularReferences = false;
+        /// <summary>
+        /// Save property/field names as lowercase (default = false)
+        /// </summary>
+        public bool SerializeToLowerCaseNames = false;
+        /// <summary>
+        /// Serialize property name with underline style
+        /// </summary>
+        public bool UseApiNamingStyle = false;
+
+        public void FixValues()
+        {
+            if (UseExtensions == false) // disable conflicting params
+            {
+                UsingGlobalTypes = false;
+                InlineCircularReferences = true;
+            }
+            if (EnableAnonymousTypes)
+                ShowReadOnlyProperties = true;
+        }
+    }
+
+    public static class JSON
+    {
+        /// <summary>
+        /// Globally set-able parameters for controlling the serializer
+        /// </summary>
+        public static JSONParameters Parameters = new JSONParameters();
+        /// <summary>
+        /// Create a formatted json string (beautified) from an object
+        /// </summary>
+        /// <param name="obj"></param>
+        /// <param name="param"></param>
+        /// <returns></returns>
+        public static string ToNiceJSON(object obj, JSONParameters param)
+        {
+            string s = ToJSON(obj, param);
+
+            return Beautify(s);
+        }
+        /// <summary>
+        /// Create a json representation for an object
+        /// </summary>
+        /// <param name="obj"></param>
+        /// <returns></returns>
+        public static string ToJSON(object obj)
+        {
+            return ToJSON(obj, JSON.Parameters);
+        }
+        /// <summary>
+        /// Create a json representation for an object with parameter override on this call
+        /// </summary>
+        /// <param name="obj"></param>
+        /// <param name="param"></param>
+        /// <returns></returns>
+        public static string ToJSON(object obj, JSONParameters param)
+        {
+            param.FixValues();
+            Type t = null;
+
+            if (obj == null)
+                return "null";
+
+            if (obj.GetType().IsGenericType)
+                t = Reflection.Instance.GetGenericTypeDefinition(obj.GetType());
+            if (t == typeof(Dictionary<,>) || t == typeof(List<>))
+                param.UsingGlobalTypes = false;
+
+            // FEATURE : enable extensions when you can deserialize anon types
+            if (param.EnableAnonymousTypes) { param.UseExtensions = false; param.UsingGlobalTypes = false; }
+            return new JSONSerializer(param).ConvertToJSON(obj);
+        }
+        /// <summary>
+        /// Parse a json string and generate a Dictionary&lt;string,object&gt; or List&lt;object&gt; structure
+        /// </summary>
+        /// <param name="json"></param>
+        /// <returns></returns>
+        public static object Parse(string json)
+        {
+            return new JsonParser(json).Decode();
+        }
+#if net4
+        /// <summary>
+        /// Create a .net4 dynamic object from the json string
+        /// </summary>
+        /// <param name="json"></param>
+        /// <returns></returns>
+        public static dynamic ToDynamic(string json)
+        {
+            return new DynamicJson(json);
+        }
+#endif
+        /// <summary>
+        /// Create a typed generic object from the json
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="json"></param>
+        /// <returns></returns>
+        public static T ToObject<T>(string json)
+        {
+            return new deserializer(Parameters).ToObject<T>(json);
+        }
+        /// <summary>
+        /// Create a typed generic object from the json with parameter override on this call
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="json"></param>
+        /// <param name="param"></param>
+        /// <returns></returns>
+        public static T ToObject<T>(string json, JSONParameters param)
+        {
+            return new deserializer(param).ToObject<T>(json);
+        }
+        /// <summary>
+        /// Create an object from the json
+        /// </summary>
+        /// <param name="json"></param>
+        /// <returns></returns>
+        public static object ToObject(string json)
+        {
+            return new deserializer(Parameters).ToObject(json, null);
+        }
+        /// <summary>
+        /// Create an object from the json with parameter override on this call
+        /// </summary>
+        /// <param name="json"></param>
+        /// <param name="param"></param>
+        /// <returns></returns>
+        public static object ToObject(string json, JSONParameters param)
+        {
+            return new deserializer(param).ToObject(json, null);
+        }
+        /// <summary>
+        /// Create an object of type from the json
+        /// </summary>
+        /// <param name="json"></param>
+        /// <param name="type"></param>
+        /// <returns></returns>
+        public static object ToObject(string json, Type type)
+        {
+            return new deserializer(Parameters).ToObject(json, type);
+        }
+        /// <summary>
+        /// Fill a given object with the json represenation
+        /// </summary>
+        /// <param name="input"></param>
+        /// <param name="json"></param>
+        /// <returns></returns>
+        public static object FillObject(object input, string json)
+        {
+            Dictionary<string, object> ht = new JsonParser(json).Decode() as Dictionary<string, object>;
+            if (ht == null) return null;
+            return new deserializer(Parameters).ParseDictionary(ht, null, input.GetType(), input);
+        }
+        /// <summary>
+        /// Deep copy an object i.e. clone to a new object
+        /// </summary>
+        /// <param name="obj"></param>
+        /// <returns></returns>
+        public static object DeepCopy(object obj)
+        {
+            return new deserializer(Parameters).ToObject(ToJSON(obj));
+        }
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="obj"></param>
+        /// <returns></returns>
+        public static T DeepCopy<T>(T obj)
+        {
+            return new deserializer(Parameters).ToObject<T>(ToJSON(obj));
+        }
+
+        /// <summary>
+        /// Create a human readable string from the json 
+        /// </summary>
+        /// <param name="input"></param>
+        /// <returns></returns>
+        public static string Beautify(string input)
+        {
+            return Formatter.PrettyPrint(input);
+        }
+        /// <summary>
+        /// Register custom type handlers for your own types not natively handled by fastJSON
+        /// </summary>
+        /// <param name="type"></param>
+        /// <param name="serializer"></param>
+        /// <param name="deserializer"></param>
+        public static void RegisterCustomType(Type type, Serialize serializer, Deserialize deserializer)
+        {
+            Reflection.Instance.RegisterCustomType(type, serializer, deserializer);
+        }
+        /// <summary>
+        /// Clear the internal reflection cache so you can start from new (you will loose performance)
+        /// </summary>
+        public static void ClearReflectionCache()
+        {
+            Reflection.Instance.ClearReflectionCache();
+        }
+
+        internal static long CreateLong(out long num, string s, int index, int count)
+        {
+            num = 0;
+            bool neg = false;
+            for (int x = 0; x < count; x++, index++)
+            {
+                char cc = s[index];
+
+                if (cc == '-')
+                    neg = true;
+                else if (cc == '+')
+                    neg = false;
+                else
+                {
+                    num *= 10;
+                    num += (int)(cc - '0');
+                }
+            }
+            if (neg) num = -num;
+
+            return num;
+        }
+    }
+
+    internal class deserializer
+    {
+        public deserializer(JSONParameters param)
+        {
+            _params = param;
+        }
+
+        private JSONParameters _params;
+        private bool _usingglobals = false;
+        private Dictionary<object, int> _circobj = new Dictionary<object, int>();
+        private Dictionary<int, object> _cirrev = new Dictionary<int, object>();
+
+        public T ToObject<T>(string json)
+        {
+            Type t = typeof(T);
+            var o = ToObject(json, t);
+
+            if (t.IsArray)
+            {
+                if ((o as ICollection).Count == 0) // edge case for "[]" -> T[]
+                {
+                    Type tt = t.GetElementType();
+                    object oo = Array.CreateInstance(tt, 0);
+                    return (T)oo;
+                }
+                else
+                    return (T)o;
+            }
+            else
+                return (T)o;
+        }
+
+        public object ToObject(string json)
+        {
+            return ToObject(json, null);
+        }
+
+        public object ToObject(string json, Type type)
+        {
+            //_params = Parameters;
+            _params.FixValues();
+            Type t = null;
+            if (type != null && type.IsGenericType)
+                t = Reflection.Instance.GetGenericTypeDefinition(type);
+            if (t == typeof(Dictionary<,>) || t == typeof(List<>))
+                _params.UsingGlobalTypes = false;
+            _usingglobals = _params.UsingGlobalTypes;
+
+            object o = new JsonParser(json).Decode();
+            if (o == null)
+                return null;
+#if !SILVERLIGHT
+            if (type != null && type == typeof(DataSet))
+                return CreateDataset(o as Dictionary<string, object>, null);
+            else if (type != null && type == typeof(DataTable))
+                return CreateDataTable(o as Dictionary<string, object>, null);
+#endif
+            if (o is IDictionary)
+            {
+                if (type != null && t == typeof(Dictionary<,>)) // deserialize a dictionary
+                    return RootDictionary(o, type);
+                else // deserialize an object
+                    return ParseDictionary(o as Dictionary<string, object>, null, type, null);
+            }
+            else if (o is List<object>)
+            {
+                if (type != null && t == typeof(Dictionary<,>)) // kv format
+                    return RootDictionary(o, type);
+                else if (type != null && t == typeof(List<>)) // deserialize to generic list
+                    return RootList(o, type);
+                else if (type == typeof(Hashtable))
+                    return RootHashTable((List<object>)o);
+                else
+                    return (o as List<object>).ToArray();
+            }
+            else if (type != null && o.GetType() != type)
+                return ChangeType(o, type);
+
+            return o;
+        }
+
+        #region [   p r i v a t e   m e t h o d s   ]
+        private object RootHashTable(List<object> o)
+        {
+            Hashtable h = new Hashtable();
+
+            foreach (Dictionary<string, object> values in o)
+            {
+                object key = values["k"];
+                object val = values["v"];
+                if (key is Dictionary<string, object>)
+                    key = ParseDictionary((Dictionary<string, object>)key, null, typeof(object), null);
+
+                if (val is Dictionary<string, object>)
+                    val = ParseDictionary((Dictionary<string, object>)val, null, typeof(object), null);
+
+                h.Add(key, val);
+            }
+
+            return h;
+        }
+
+        private object ChangeType(object value, Type conversionType)
+        {
+            if (conversionType == typeof(int))
+                return (int)((long)value);
+
+            else if (conversionType == typeof(long))
+                return (long)value;
+
+            else if (conversionType == typeof(string))
+                return (string)value;
+
+            else if (conversionType.IsEnum)
+                return CreateEnum(conversionType, value);
+
+            else if (conversionType == typeof(DateTime))
+                return CreateDateTime((string)value);
+
+            else if (Reflection.Instance.IsTypeRegistered(conversionType))
+                return Reflection.Instance.CreateCustom((string)value, conversionType);
+
+            // 8-30-2014 - James Brooks - Added code for nullable types.
+            if (IsNullable(conversionType))
+            {
+                if (value == null)
+                {
+                    return value;
+                }
+                conversionType = UnderlyingTypeOf(conversionType);
+            }
+
+            // 8-30-2014 - James Brooks - Nullable Guid is a special case so it was moved after the "IsNullable" check.
+            if (conversionType == typeof(Guid))
+                return CreateGuid((string)value);
+
+            return Convert.ChangeType(value, conversionType, CultureInfo.InvariantCulture);
+        }
+
+        private bool IsNullable(Type t)
+        {
+            if (!t.IsGenericType) return false;
+            Type g = t.GetGenericTypeDefinition();
+            return (g.Equals(typeof(Nullable<>)));
+        }
+
+        private Type UnderlyingTypeOf(Type t)
+        {
+            return t.GetGenericArguments()[0];
+        }
+
+        private object RootList(object parse, Type type)
+        {
+            Type[] gtypes = Reflection.Instance.GetGenericArguments(type);
+            IList o = (IList)Reflection.Instance.FastCreateInstance(type);
+            foreach (var k in (IList)parse)
+            {
+                _usingglobals = false;
+                object v = k;
+                if (k is Dictionary<string, object>)
+                    v = ParseDictionary(k as Dictionary<string, object>, null, gtypes[0], null);
+                else
+                    v = ChangeType(k, gtypes[0]);
+
+                o.Add(v);
+            }
+            return o;
+        }
+
+        private object RootDictionary(object parse, Type type)
+        {
+            Type[] gtypes = Reflection.Instance.GetGenericArguments(type);
+            Type t1 = null;
+            Type t2 = null;
+            if (gtypes != null)
+            {
+                t1 = gtypes[0];
+                t2 = gtypes[1];
+            }
+            if (parse is Dictionary<string, object>)
+            {
+                IDictionary o = (IDictionary)Reflection.Instance.FastCreateInstance(type);
+
+                foreach (var kv in (Dictionary<string, object>)parse)
+                {
+                    object v;
+                    object k = ChangeType(kv.Key, t1);
+
+                    if (kv.Value is Dictionary<string, object>)
+                        v = ParseDictionary(kv.Value as Dictionary<string, object>, null, t2, null);
+
+                    else if (t2.IsArray)
+                        v = CreateArray((List<object>)kv.Value, t2, t2.GetElementType(), null);
+
+                    else if (kv.Value is IList)
+                        v = CreateGenericList((List<object>)kv.Value, t2, t1, null);
+
+                    else
+                        v = ChangeType(kv.Value, t2);
+
+                    o.Add(k, v);
+                }
+
+                return o;
+            }
+            if (parse is List<object>)
+                return CreateDictionary(parse as List<object>, type, gtypes, null);
+
+            return null;
+        }
+
+        internal object ParseDictionary(Dictionary<string, object> d, Dictionary<string, object> globaltypes, Type type, object input)
+        {
+            object tn = "";
+            if (type == typeof(NameValueCollection))
+                return CreateNV(d);
+            if (type == typeof(StringDictionary))
+                return CreateSD(d);
+
+            if (d.TryGetValue("$i", out tn))
+            {
+                object v = null;
+                _cirrev.TryGetValue((int)(long)tn, out v);
+                return v;
+            }
+
+            if (d.TryGetValue("$types", out tn))
+            {
+                _usingglobals = true;
+                globaltypes = new Dictionary<string, object>();
+                foreach (var kv in (Dictionary<string, object>)tn)
+                {
+                    globaltypes.Add((string)kv.Value, kv.Key);
+                }
+            }
+
+            bool found = d.TryGetValue("$type", out tn);
+#if !SILVERLIGHT
+            if (found == false && type == typeof(System.Object))
+            {
+                return d;   // CreateDataset(d, globaltypes);
+            }
+#endif
+            if (found)
+            {
+                if (_usingglobals)
+                {
+                    object tname = "";
+                    if (globaltypes != null && globaltypes.TryGetValue((string)tn, out tname))
+                        tn = tname;
+                }
+                type = Reflection.Instance.GetTypeFromCache((string)tn);
+            }
+
+            if (type == null)
+                throw new Exception("Cannot determine type");
+
+            string typename = type.FullName;
+            object o = input;
+            if (o == null)
+            {
+                if (_params.ParametricConstructorOverride)
+                    o = System.Runtime.Serialization.FormatterServices.GetUninitializedObject(type);
+                else
+                    o = Reflection.Instance.FastCreateInstance(type);
+            }
+            int circount = 0;
+            if (_circobj.TryGetValue(o, out circount) == false)
+            {
+                circount = _circobj.Count + 1;
+                _circobj.Add(o, circount);
+                _cirrev.Add(circount, o);
+            }
+
+            Dictionary<string, myPropInfo> props = Reflection.Instance.Getproperties(type, typename, Reflection.Instance.IsTypeRegistered(type));
+            foreach (var kv in d)
+            {
+                var n = kv.Key;
+                var v = kv.Value;
+                string name = n.ToLower();
+                if (name == "$map")
+                {
+                    ProcessMap(o, props, (Dictionary<string, object>)d[name]);
+                    continue;
+                }
+                myPropInfo pi;
+                if (props.TryGetValue(name, out pi) == false)
+                    continue;
+                if (pi.CanWrite)
+                {
+                    //object v = d[n];
+
+                    if (v != null)
+                    {
+                        object oset = null;
+
+                        switch (pi.Type)
+                        {
+                            case myPropInfoType.Int: oset = (int)((long)v); break;
+                            case myPropInfoType.Long: oset = (long)v; break;
+                            case myPropInfoType.String: oset = (string)v; break;
+                            case myPropInfoType.Bool: oset = (bool)v; break;
+                            case myPropInfoType.DateTime: oset = CreateDateTime((string)v); break;
+                            case myPropInfoType.Enum: oset = CreateEnum(pi.pt, v); break;
+                            case myPropInfoType.Guid: oset = CreateGuid((string)v); break;
+
+                            case myPropInfoType.Array:
+                                if (!pi.IsValueType)
+                                    oset = CreateArray((List<object>)v, pi.pt, pi.bt, globaltypes);
+                                // what about 'else'?
+                                break;
+                            case myPropInfoType.ByteArray: oset = Convert.FromBase64String((string)v); break;
+#if !SILVERLIGHT
+                            case myPropInfoType.DataSet: oset = CreateDataset((Dictionary<string, object>)v, globaltypes); break;
+                            case myPropInfoType.DataTable: oset = CreateDataTable((Dictionary<string, object>)v, globaltypes); break;
+                            case myPropInfoType.Hashtable: // same case as Dictionary
+#endif
+                            case myPropInfoType.Dictionary: oset = CreateDictionary((List<object>)v, pi.pt, pi.GenericTypes, globaltypes); break;
+                            case myPropInfoType.StringKeyDictionary: oset = CreateStringKeyDictionary((Dictionary<string, object>)v, pi.pt, pi.GenericTypes, globaltypes); break;
+                            case myPropInfoType.NameValue: oset = CreateNV((Dictionary<string, object>)v); break;
+                            case myPropInfoType.StringDictionary: oset = CreateSD((Dictionary<string, object>)v); break;
+                            case myPropInfoType.Custom: oset = Reflection.Instance.CreateCustom((string)v, pi.pt); break;
+                            default:
+                                {
+                                    if (pi.IsGenericType && pi.IsValueType == false && v is List<object>)
+                                        oset = CreateGenericList((List<object>)v, pi.pt, pi.bt, globaltypes);
+
+                                    else if ((pi.IsClass || pi.IsStruct) && v is Dictionary<string, object>)
+                                        oset = ParseDictionary((Dictionary<string, object>)v, globaltypes, pi.pt, pi.getter(o));
+
+                                    else if (v is List<object>)
+                                        oset = CreateArray((List<object>)v, pi.pt, typeof(object), globaltypes);
+
+                                    else if (pi.IsValueType)
+                                        oset = ChangeType(v, pi.changeType);
+
+                                    else
+                                        oset = v;
+                                }
+                                break;
+                        }
+
+                        o = pi.setter(o, oset);
+                    }
+                }
+            }
+            return o;
+        }
+
+        private StringDictionary CreateSD(Dictionary<string, object> d)
+        {
+            StringDictionary nv = new StringDictionary();
+
+            foreach (var o in d)
+                nv.Add(o.Key, (string)o.Value);
+
+            return nv;
+        }
+
+        private NameValueCollection CreateNV(Dictionary<string, object> d)
+        {
+            NameValueCollection nv = new NameValueCollection();
+
+            foreach (var o in d)
+                nv.Add(o.Key, (string)o.Value);
+
+            return nv;
+        }
+
+        private void ProcessMap(object obj, Dictionary<string, myPropInfo> props, Dictionary<string, object> dic)
+        {
+            foreach (KeyValuePair<string, object> kv in dic)
+            {
+                myPropInfo p = props[kv.Key];
+                object o = p.getter(obj);
+                Type t = Type.GetType((string)kv.Value);
+                if (t == typeof(Guid))
+                    p.setter(obj, CreateGuid((string)o));
+            }
+        }
+
+        private int CreateInteger(string s, int index, int count)
+        {
+            int num = 0;
+            bool neg = false;
+            for (int x = 0; x < count; x++, index++)
+            {
+                char cc = s[index];
+
+                if (cc == '-')
+                    neg = true;
+                else if (cc == '+')
+                    neg = false;
+                else
+                {
+                    num *= 10;
+                    num += (int)(cc - '0');
+                }
+            }
+            if (neg) num = -num;
+
+            return num;
+        }
+
+        private object CreateEnum(Type pt, object v)
+        {
+            // TODO : optimize create enum
+#if !SILVERLIGHT
+            return Enum.Parse(pt, v.ToString());
+#else
+            return Enum.Parse(pt, v, true);
+#endif
+        }
+
+        private Guid CreateGuid(string s)
+        {
+            if (s.Length > 30)
+                return new Guid(s);
+            else
+                return new Guid(Convert.FromBase64String(s));
+        }
+
+        private DateTime CreateDateTime(string value)
+        {
+            bool utc = false;
+            //                   0123456789012345678 9012 9/3
+            // datetime format = yyyy-MM-ddTHH:mm:ss .nnn  Z
+            int year;
+            int month;
+            int day;
+            int hour;
+            int min;
+            int sec;
+            int ms = 0;
+
+            year = CreateInteger(value, 0, 4);
+            month = CreateInteger(value, 5, 2);
+            day = CreateInteger(value, 8, 2);
+            hour = CreateInteger(value, 11, 2);
+            min = CreateInteger(value, 14, 2);
+            sec = CreateInteger(value, 17, 2);
+            if (value.Length > 21 && value[19] == '.')
+                ms = CreateInteger(value, 20, 3);
+
+            if (value[value.Length - 1] == 'Z')
+                utc = true;
+
+            if (_params.UseUTCDateTime == false && utc == false)
+                return new DateTime(year, month, day, hour, min, sec, ms);
+            else
+                return new DateTime(year, month, day, hour, min, sec, ms, DateTimeKind.Utc).ToLocalTime();
+        }
+
+        private object CreateArray(List<object> data, Type pt, Type bt, Dictionary<string, object> globalTypes)
+        {
+            Array col = Array.CreateInstance(bt, data.Count);
+            // create an array of objects
+            for (int i = 0; i < data.Count; i++)
+            {
+                object ob = data[i];
+                if (ob == null)
+                {
+                    continue;
+                }
+                if (ob is IDictionary)
+                    col.SetValue(ParseDictionary((Dictionary<string, object>)ob, globalTypes, bt, null), i);
+                else if (ob is ICollection)
+                    col.SetValue(CreateArray((List<object>)ob, bt, bt.GetElementType(), globalTypes), i);
+                else
+                    col.SetValue(ChangeType(ob, bt), i);
+            }
+
+            return col;
+        }
+
+
+        private object CreateGenericList(List<object> data, Type pt, Type bt, Dictionary<string, object> globalTypes)
+        {
+            IList col = (IList)Reflection.Instance.FastCreateInstance(pt);
+            // create an array of objects
+            foreach (object ob in data)
+            {
+
+                if (ob is IDictionary)
+                    col.Add(ParseDictionary((Dictionary<string, object>)ob, globalTypes, bt, null));
+
+                else if (ob is List<object>)
+                {
+                    if (bt.IsGenericType)
+                        col.Add((List<object>)ob);//).ToArray());
+                    else
+                        col.Add(((List<object>)ob).ToArray());
+                }
+                else
+                    col.Add(ChangeType(ob, bt));
+            }
+            return col;
+        }
+
+        private object CreateStringKeyDictionary(Dictionary<string, object> reader, Type pt, Type[] types, Dictionary<string, object> globalTypes)
+        {
+            var col = (IDictionary)Reflection.Instance.FastCreateInstance(pt);
+            Type t1 = null;
+            Type t2 = null;
+            if (types != null)
+            {
+                t1 = types[0];
+                t2 = types[1];
+            }
+
+            foreach (KeyValuePair<string, object> values in reader)
+            {
+                var key = values.Key;
+                object val = null;
+
+                if (values.Value is Dictionary<string, object>)
+                    val = ParseDictionary((Dictionary<string, object>)values.Value, globalTypes, t2, null);
+
+                else if (types != null && t2.IsArray)
+                {
+                    if (values.Value is Array)
+                        val = values.Value;
+                    else
+                        val = CreateArray((List<object>)values.Value, t2, t2.GetElementType(), globalTypes);
+                }
+                else if (values.Value is IList)
+                    val = CreateGenericList((List<object>)values.Value, t2, t1, globalTypes);
+
+                else
+                    val = ChangeType(values.Value, t2);
+
+                col.Add(key, val);
+            }
+
+            return col;
+        }
+
+        private object CreateDictionary(List<object> reader, Type pt, Type[] types, Dictionary<string, object> globalTypes)
+        {
+            IDictionary col = (IDictionary)Reflection.Instance.FastCreateInstance(pt);
+            Type t1 = null;
+            Type t2 = null;
+            if (types != null)
+            {
+                t1 = types[0];
+                t2 = types[1];
+            }
+
+            foreach (Dictionary<string, object> values in reader)
+            {
+                object key = values["k"];
+                object val = values["v"];
+
+                if (key is Dictionary<string, object>)
+                    key = ParseDictionary((Dictionary<string, object>)key, globalTypes, t1, null);
+                else
+                    key = ChangeType(key, t1);
+
+                if (val is Dictionary<string, object>)
+                    val = ParseDictionary((Dictionary<string, object>)val, globalTypes, t2, null);
+                else
+                    val = ChangeType(val, t2);
+
+                col.Add(key, val);
+            }
+
+            return col;
+        }
+
+#if !SILVERLIGHT
+        private DataSet CreateDataset(Dictionary<string, object> reader, Dictionary<string, object> globalTypes)
+        {
+            DataSet ds = new DataSet();
+            ds.EnforceConstraints = false;
+            ds.BeginInit();
+
+            // read dataset schema here
+            var schema = reader["$schema"];
+
+            if (schema is string)
+            {
+                TextReader tr = new StringReader((string)schema);
+                ds.ReadXmlSchema(tr);
+            }
+            else
+            {
+                DatasetSchema ms = (DatasetSchema)ParseDictionary((Dictionary<string, object>)schema, globalTypes, typeof(DatasetSchema), null);
+                ds.DataSetName = ms.Name;
+                for (int i = 0; i < ms.Info.Count; i += 3)
+                {
+                    if (ds.Tables.Contains(ms.Info[i]) == false)
+                        ds.Tables.Add(ms.Info[i]);
+                    ds.Tables[ms.Info[i]].Columns.Add(ms.Info[i + 1], Type.GetType(ms.Info[i + 2]));
+                }
+            }
+
+            foreach (KeyValuePair<string, object> pair in reader)
+            {
+                if (pair.Key == "$type" || pair.Key == "$schema") continue;
+
+                List<object> rows = (List<object>)pair.Value;
+                if (rows == null) continue;
+
+                DataTable dt = ds.Tables[pair.Key];
+                ReadDataTable(rows, dt);
+            }
+
+            ds.EndInit();
+
+            return ds;
+        }
+
+        private void ReadDataTable(List<object> rows, DataTable dt)
+        {
+            dt.BeginInit();
+            dt.BeginLoadData();
+            List<int> guidcols = new List<int>();
+            List<int> datecol = new List<int>();
+
+            foreach (DataColumn c in dt.Columns)
+            {
+                if (c.DataType == typeof(Guid) || c.DataType == typeof(Guid?))
+                    guidcols.Add(c.Ordinal);
+                if (_params.UseUTCDateTime && (c.DataType == typeof(DateTime) || c.DataType == typeof(DateTime?)))
+                    datecol.Add(c.Ordinal);
+            }
+
+            foreach (List<object> row in rows)
+            {
+                object[] v = new object[row.Count];
+                row.CopyTo(v, 0);
+                foreach (int i in guidcols)
+                {
+                    string s = (string)v[i];
+                    if (s != null && s.Length < 36)
+                        v[i] = new Guid(Convert.FromBase64String(s));
+                }
+                if (_params.UseUTCDateTime)
+                {
+                    foreach (int i in datecol)
+                    {
+                        string s = (string)v[i];
+                        if (s != null)
+                            v[i] = CreateDateTime(s);
+                    }
+                }
+                dt.Rows.Add(v);
+            }
+
+            dt.EndLoadData();
+            dt.EndInit();
+        }
+
+        DataTable CreateDataTable(Dictionary<string, object> reader, Dictionary<string, object> globalTypes)
+        {
+            var dt = new DataTable();
+
+            // read dataset schema here
+            var schema = reader["$schema"];
+
+            if (schema is string)
+            {
+                TextReader tr = new StringReader((string)schema);
+                dt.ReadXmlSchema(tr);
+            }
+            else
+            {
+                var ms = (DatasetSchema)this.ParseDictionary((Dictionary<string, object>)schema, globalTypes, typeof(DatasetSchema), null);
+                dt.TableName = ms.Info[0];
+                for (int i = 0; i < ms.Info.Count; i += 3)
+                {
+                    dt.Columns.Add(ms.Info[i + 1], Type.GetType(ms.Info[i + 2]));
+                }
+            }
+
+            foreach (var pair in reader)
+            {
+                if (pair.Key == "$type" || pair.Key == "$schema")
+                    continue;
+
+                var rows = (List<object>)pair.Value;
+                if (rows == null)
+                    continue;
+
+                if (!dt.TableName.Equals(pair.Key, StringComparison.InvariantCultureIgnoreCase))
+                    continue;
+
+                ReadDataTable(rows, dt);
+            }
+
+            return dt;
+        }
+#endif
+        #endregion
+    }
+
+}

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio