| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416 |
- 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 Dataoke;
- using Google.Protobuf.WellKnownTypes;
- using System.Diagnostics;
- using System.Text.Json;
- using TencentCloud.Tcm.V20210413.Models;
- using System.Security.Cryptography;
- using System.Data;
- using System.Threading.Channels;
- using YunhuiKit;
- using System.IO;
- namespace molilian.core
- {
- public partial class TkOrderTrackingCore
- {
- private const int DefaultLinkSummaryTtlSeconds = 24 * 3600;
- private const int MaxLinkSummaryTtlSeconds = 7 * 24 * 3600;
- private const string LinkSummaryCacheVersion = "v2";
- public class TkOrderLinkSummaryDTO
- {
- public TkChannelEnum channel { get; set; }
- public int accountId { get; set; } = 0;
- public string ip { get; set; } = string.Empty;
- public string oaid { get; set; } = string.Empty;
- public string mktId { get; set; } = string.Empty;
- public string itemId { get; set; } = string.Empty;
- public string itemName { get; set; } = string.Empty;
- public string taoToken { get; set; } = string.Empty;
- public string shortLinkurl { get; set; } = string.Empty;
- public string deeplink_url { get; set; } = string.Empty;
- public string item_url { get; set; } = string.Empty;
- public string item_deeplink_url { get; set; } = string.Empty;
- public DateTime create_time { get; set; } = DateTime.Now;
- }
- public static void SaveLinkSummary(TkDataDTO item, TkPoolDTO? account = null)
- {
- if (!ShouldSaveLinkSummary(item, account)) return;
- TkOrderLinkSummaryDTO newData = new()
- {
- accountId = item.accountId,
- channel = item.channel,
- ip = item.ip,
- oaid = item.oaid,
- mktId = item.mktId,
- itemId = item.itemId,
- itemName = item.itemName,
- taoToken = item.taoToken,
- shortLinkurl = item.shortLinkurl,
- deeplink_url = item.deeplink_url,
- item_url = item.item_url,
- item_deeplink_url = item.item_deeplink_url,
- create_time = item.create_time,
- };
- var config = TkConfigCore.Get();
- int ttl = GetLinkSummaryTtlSeconds(config);
- string cacheKey = BuildItemSummaryKey(item.accountId, item.itemId);
- if (!string.IsNullOrEmpty(cacheKey)) RedisHelper.Set(cacheKey, newData, ttl);
- string mktId = item.mktId;
- if (!string.IsNullOrEmpty(mktId) && mktId.Contains('-'))
- {
- mktId = mktId.Split('-')[^1];
- cacheKey = BuildMktSummaryKey(item.accountId, mktId);
- RedisHelper.Set(cacheKey, newData, ttl);
- }
- cacheKey = BuildTitleSummaryKey(item.accountId, item.itemName);
- if (!string.IsNullOrEmpty(cacheKey)) RedisHelper.Set(cacheKey, newData, ttl);
- }
- private static bool ShouldSaveLinkSummary(TkDataDTO item, TkPoolDTO? account)
- {
- if (item == null || item.accountId <= 0) return false;
- if (!item.success) return false;
- if (string.IsNullOrEmpty(item.itemId)) return false;
- bool hasLink = !string.IsNullOrEmpty(item.shortLinkurl) ||
- !string.IsNullOrEmpty(item.deeplink_url) ||
- !string.IsNullOrEmpty(item.item_url) ||
- !string.IsNullOrEmpty(item.item_deeplink_url);
- if (!hasLink) return false;
- account ??= GetCachedAccount(item.accountId);
- return account?.enable_order_summary_cache ?? true;
- }
- private static TkPoolDTO? GetCachedAccount(int accountId)
- {
- try
- {
- var list = TkPoolCore.GetCachedAccounts();
- return list?.FirstOrDefault(e => e.id == accountId);
- }
- catch
- {
- return null;
- }
- }
- private static int GetLinkSummaryTtlSeconds(TkConfigDTO config)
- {
- long configuredHours = config?.fake_click_ttl ?? 0;
- long ttl = configuredHours > 0 ? configuredHours * 3600 : DefaultLinkSummaryTtlSeconds;
- return (int)Math.Clamp(ttl, 3600, MaxLinkSummaryTtlSeconds);
- }
- private static string BuildItemSummaryKey(int accountId, string itemId)
- {
- if (accountId <= 0 || string.IsNullOrEmpty(itemId)) return string.Empty;
- return $":cache:order_summary:{LinkSummaryCacheVersion}:{accountId}:item:{itemId}";
- }
- private static string BuildMktSummaryKey(int accountId, string mktId)
- {
- if (accountId <= 0 || string.IsNullOrEmpty(mktId)) return string.Empty;
- return $":cache:order_summary:{LinkSummaryCacheVersion}:{accountId}:mktId:{mktId}";
- }
- private static string BuildTitleSummaryKey(int accountId, string itemTitle)
- {
- if (accountId <= 0 || string.IsNullOrEmpty(itemTitle)) return string.Empty;
- return $":cache:order_summary:{LinkSummaryCacheVersion}:{accountId}:title:{Sha256Hex(itemTitle)}";
- }
- private static string BuildLegacyItemSummaryKey(int accountId, string itemId)
- {
- if (accountId <= 0 || string.IsNullOrEmpty(itemId)) return string.Empty;
- return $":cache:order_summary:{accountId}:{itemId}";
- }
- private static string BuildLegacyMktSummaryKey(int accountId, string mktId)
- {
- if (accountId <= 0 || string.IsNullOrEmpty(mktId)) return string.Empty;
- return $":cache:order_summary:{accountId}:mktId:{mktId}";
- }
- private static string BuildLegacyTitleSummaryKey(int accountId, string itemTitle)
- {
- if (accountId <= 0 || string.IsNullOrEmpty(itemTitle)) return string.Empty;
- return $":cache:order_summary:{accountId}:{itemTitle}";
- }
- private static string Sha256Hex(string value)
- {
- byte[] hash = SHA256.HashData(Encoding.UTF8.GetBytes(value));
- return Convert.ToHexString(hash).ToLowerInvariant();
- }
- private static async Task<TkOrderLinkSummaryDTO?> GetFirstSummaryAsync(YunhuiKit.RedisClient redis, IEnumerable<string> keys)
- {
- foreach (var key in keys.Where(e => !string.IsNullOrEmpty(e)))
- {
- var result = await redis.GetAsync<TkOrderLinkSummaryDTO>(key);
- if (IsUsableOrderSummary(result)) return result;
- }
- return null;
- }
- private static bool IsUsableOrderSummary(TkOrderLinkSummaryDTO? summary)
- {
- if (summary == null) return false;
- return !string.IsNullOrEmpty(summary.shortLinkurl) ||
- !string.IsNullOrEmpty(summary.deeplink_url) ||
- !string.IsNullOrEmpty(summary.item_url) ||
- !string.IsNullOrEmpty(summary.item_deeplink_url);
- }
- public static async Task<(int, TkOrderLinkSummaryDTO)> GetLinkSummaryAsync(TkPoolDTO account, string itemId, string mktId, string itemTitle)
- {
- try
- {
- int accountId = account.id;
- mktId = !string.IsNullOrEmpty(mktId) && mktId.Contains('-')
- ? mktId.Split('-')[^1]
- : mktId;
- // 获取所有关联账户ID(包括当前账户)
- var relatedAccountIds = new List<int> { accountId };
- if (!string.IsNullOrEmpty(account.related_account_ids))
- {
- relatedAccountIds.AddRange(account.related_account_ids
- .Split(',')
- .Where(id => !string.IsNullOrWhiteSpace(id))
- .Select(int.Parse));
- }
- var tasks = EndPointCore.List()
- .Where(node => node.is_public_api && !string.IsNullOrEmpty(node.redis_server))
- .Select(async node =>
- {
- try
- {
- var redisServer = EndPointCore.GetRedisServer(node);
- if (string.IsNullOrEmpty(redisServer)) return (0, null);
- using var scope = await RedisClientFactory.CreateScopeAsync(redisServer);
- var redis = scope.Client;
- // 按优先级依次查询不同的缓存key
- TkOrderLinkSummaryDTO result = null;
- // 遍历所有关联账户ID
- foreach (var relatedId in relatedAccountIds)
- {
- if (!string.IsNullOrEmpty(mktId))
- {
- result = await GetFirstSummaryAsync(redis, new[]
- {
- BuildMktSummaryKey(relatedId, mktId),
- BuildLegacyMktSummaryKey(relatedId, mktId)
- });
- if (IsUsableOrderSummary(result)) return (1, result);
- }
- if (!string.IsNullOrEmpty(itemId))
- {
- result = await GetFirstSummaryAsync(redis, new[]
- {
- BuildItemSummaryKey(relatedId, itemId),
- BuildLegacyItemSummaryKey(relatedId, itemId)
- });
- if (IsUsableOrderSummary(result)) return (2, result);
- if (!string.IsNullOrEmpty(itemTitle))
- {
- result = await GetFirstSummaryAsync(redis, new[]
- {
- BuildTitleSummaryKey(relatedId, itemTitle),
- BuildLegacyTitleSummaryKey(relatedId, itemTitle)
- });
- if (IsUsableOrderSummary(result)) return (3, result);
- }
- }
- }
- }
- catch (Exception ex)
- {
- }
- return (0, null);
- });
- var results = await Task.WhenAll(tasks);
- return results.FirstOrDefault(r => r != (0, null));
- }
- catch (Exception)
- {
- // TODO: 添加日志记录
- return (0, null);
- }
- }
- public static async Task RemoveLinkSummaryAsync(int accountId, string itemId, string mktId, string itemTitle)
- {
- try
- {
- var tasks = EndPointCore.List()
- .Where(node => node.is_public_api && !string.IsNullOrEmpty(node.redis_server))
- .Select(async node =>
- {
- try
- {
- var redisServer = EndPointCore.GetRedisServer(node);
- if (string.IsNullOrEmpty(redisServer)) return;
- using var scope = await RedisClientFactory.CreateScopeAsync(redisServer);
- var redis = scope.Client;
- mktId = !string.IsNullOrEmpty(mktId) && mktId.Contains('-')
- ? mktId.Split('-')[^1]
- : mktId;
- var keys = new[]
- {
- BuildItemSummaryKey(accountId, itemId),
- BuildLegacyItemSummaryKey(accountId, itemId),
- BuildMktSummaryKey(accountId, mktId),
- BuildLegacyMktSummaryKey(accountId, mktId),
- BuildTitleSummaryKey(accountId, itemTitle),
- BuildLegacyTitleSummaryKey(accountId, itemTitle)
- }.Where(k => !string.IsNullOrEmpty(k));
- // 批量删除所有相关缓存
- await redis.DelAsync(keys.ToArray());
- }
- catch (Exception ex) { }
- });
- await Task.WhenAll(tasks);
- }
- catch (Exception)
- {
- // TODO: 添加日志记录
- }
- }
- //public static async void RemoveLinkSummary(int accountId, string itemId, string mktId, string itemTitle)
- //{
- // await EndPointCore.ProcessEndPointNodesAsync(node =>
- // {
- // if (!node.is_public_api) return Task.CompletedTask;
- // if (string.IsNullOrEmpty(node.redis_server)) return Task.CompletedTask;
- // var redis = RedisClientManager.GetRedisClient(node.redis_server);
- // string cacheKey = $":cache:order_summary:{accountId}:{itemId}";
- // redis.Del(cacheKey);
- // cacheKey = $":cache:order_summary:{accountId}:mktId:{mktId}";
- // redis.Del(cacheKey);
- // cacheKey = $":cache:order_summary:{accountId}:{itemTitle}";
- // redis.Del(cacheKey);
- // return Task.CompletedTask;
- // });
- //}
- public static async Task<bool> MatchOrder(TkPoolDTO account, TkOrderDetailDTO item)
- {
- DateTime now = DateTime.Now;
- if (item.tbPaidTime < now.AddDays(-24)) return false;
- (int match_type, var summary) = await GetLinkSummaryAsync(account, item.itemId, item.mktId, item.itemTitle);
- if (summary == null) return false;
- var config = TkConfigCore.Get();
- Random rand = new();
- int click_num = rand.Next(config.fake_click_min, config.fake_click_max + 1);
- if (account.fake_click_min != 0 && account.fake_click_max != 0)
- click_num = rand.Next(account.fake_click_min, account.fake_click_max + 1);
- DateTime paidTime = item.tbPaidTime;
- if (paidTime < summary.create_time) return false;
- if (paidTime < now.AddDays(-24)) return false;
- int minutes = now.Minute / 10 * 10;
- DateTime rounded = new(now.Year, now.Month, now.Day, now.Hour, minutes, 0);
- string batchId = rounded.ToString("yyyyMMddHHmm");
- string accountName = item.accountName;
- string shortLinkUrl = !string.IsNullOrEmpty(summary.item_url) ? summary.item_url : summary.shortLinkurl;
- string deeplinkUrl = !string.IsNullOrEmpty(summary.item_deeplink_url) ? summary.item_deeplink_url : summary.deeplink_url;
- switch (account.fake_click_link_type)
- {
- case FakeClickLinkType.Deeplink:
- shortLinkUrl = string.Empty;
- break;
- case FakeClickLinkType.H5:
- deeplinkUrl = string.Empty;
- break;
- }
- var exist = new DBContext.Table("tk_order_tracking").Fields("id").Get<dynamic>("tradeId=@tradeId", new { item.tradeId });
- DateTime expTime = now.Hour >= 21 ? now.AddHours(3) : now.Date.AddDays(1);
- var data = new TkOrderTrackingDTO()
- {
- match_type = match_type,
- channel = summary.channel,
- accountId = item.accountId,
- accountName = accountName,
- batchId = batchId,
- ip = summary.ip,
- oaid = summary.oaid,
- mktId = summary.mktId,
- itemId = summary.itemId,
- itemName = summary.itemName,
- taoToken = summary.taoToken,
- shortLinkUrl = shortLinkUrl,
- deeplinkUrl = deeplinkUrl,
- tradeId = item.tradeId,
- tradeParentId = item.tradeParentId,
- create_time = summary.create_time,
- click_time = item.clickTime,
- paid_time = item.tbPaidTime,
- exp_time = expTime,
- click_num = click_num,
- };
- if (exist == null)
- {
- using var conn = DBContext.GetOpenConnection();
- conn.Insert(data);
- }
- //RemoveLinkSummary(item.accountId, item.itemId, item.mktId, item.itemTitle);
- await RemoveLinkSummaryAsync(item.accountId, item.itemId, item.mktId, item.itemTitle);
- return true;
- }
- }
- }
|