|
|
@@ -1,12 +1,14 @@
|
|
|
-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.Threading;
|
|
|
-using Microsoft.AspNetCore.Mvc.RazorPages;
|
|
|
+using molilian.core;
|
|
|
+using dodohold.core;
|
|
|
+using Dapper;
|
|
|
+using Microsoft.AspNetCore.Mvc;
|
|
|
+using Org.BouncyCastle.Ocsp;
|
|
|
+using System.Text.Json;
|
|
|
+using System.Runtime.InteropServices;
|
|
|
+using System.Net;
|
|
|
+using System.Threading;
|
|
|
+using System.Data;
|
|
|
+using Microsoft.AspNetCore.Mvc.RazorPages;
|
|
|
using TencentCloud.Soe.V20180724.Models;
|
|
|
using static dodohold.core.ZTOExpress.CreateOrderArgs;
|
|
|
using static Spire.Xls.Core.Spreadsheet.HTMLOptions;
|
|
|
@@ -457,8 +459,8 @@ namespace molilian.api.Controllers
|
|
|
}
|
|
|
|
|
|
[HttpPost]
|
|
|
- public async Task<ActionResult> test1([FromBody] JsonElement form)
|
|
|
- {
|
|
|
+ public async Task<ActionResult> test1([FromBody] JsonElement form)
|
|
|
+ {
|
|
|
|
|
|
NotifyCore.Notify(new NifyMessage
|
|
|
{
|
|
|
@@ -472,12 +474,462 @@ namespace molilian.api.Controllers
|
|
|
{
|
|
|
success = true,
|
|
|
message = "ok"
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- [HttpGet]
|
|
|
- public async Task<ActionResult> xxx()
|
|
|
- {
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ [HttpGet]
|
|
|
+ public async Task<ActionResult> RepairTkDailyAccountStats(
|
|
|
+ DateTime startDate = default,
|
|
|
+ DateTime endDate = default,
|
|
|
+ string accountIds = "129,140",
|
|
|
+ string extraNames = "搜同款_楚颜_128众杰科技,搜同款_广哲2",
|
|
|
+ bool dryRun = false,
|
|
|
+ bool repairDailyLogs = true,
|
|
|
+ bool repairRedis = true,
|
|
|
+ bool repairRedisNameKeys = true,
|
|
|
+ int commandTimeoutSeconds = 600)
|
|
|
+ {
|
|
|
+ if (startDate == default) startDate = new DateTime(2026, 6, 16);
|
|
|
+ if (endDate == default) endDate = DateTime.Now.Date;
|
|
|
+ commandTimeoutSeconds = Math.Clamp(commandTimeoutSeconds, 30, 3600);
|
|
|
+
|
|
|
+ startDate = startDate.Date;
|
|
|
+ endDate = endDate.Date;
|
|
|
+ if (endDate < startDate)
|
|
|
+ {
|
|
|
+ return new APIResult(new { success = false, message = "endDate 不能早于 startDate" });
|
|
|
+ }
|
|
|
+
|
|
|
+ var ids = (accountIds ?? string.Empty)
|
|
|
+ .Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
|
|
|
+ .Select(item => int.TryParse(item, out int id) ? id : 0)
|
|
|
+ .Where(id => id > 0)
|
|
|
+ .Distinct()
|
|
|
+ .ToArray();
|
|
|
+
|
|
|
+ if (ids.Length == 0)
|
|
|
+ {
|
|
|
+ return new APIResult(new { success = false, message = "accountIds 不能为空" });
|
|
|
+ }
|
|
|
+
|
|
|
+ using var conn = CenterHub.GetOpenConnection();
|
|
|
+ if (conn.State != ConnectionState.Open) conn.Open();
|
|
|
+
|
|
|
+ var errors = new List<object>();
|
|
|
+ var affectedNames = new HashSet<string>(StringComparer.Ordinal);
|
|
|
+ var accounts = SqlMapper.Query<TkDailyRepairCountRow>(
|
|
|
+ conn,
|
|
|
+ "SELECT id accountId, company accountName FROM tk_pool WHERE id IN @ids",
|
|
|
+ new { ids },
|
|
|
+ commandTimeout: commandTimeoutSeconds).ToList();
|
|
|
+
|
|
|
+ foreach (string name in accounts.Select(item => item.accountName).Where(item => !string.IsNullOrWhiteSpace(item)))
|
|
|
+ {
|
|
|
+ affectedNames.Add(name);
|
|
|
+ }
|
|
|
+
|
|
|
+ extraNames ??= string.Empty;
|
|
|
+ foreach (string name in extraNames
|
|
|
+ .Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
|
|
|
+ .Where(item => !string.IsNullOrWhiteSpace(item)))
|
|
|
+ {
|
|
|
+ affectedNames.Add(name);
|
|
|
+ }
|
|
|
+
|
|
|
+ foreach (string name in SqlMapper.Query<string>(
|
|
|
+ conn,
|
|
|
+ @"
|
|
|
+SELECT DISTINCT accountName
|
|
|
+FROM center_daily_logs
|
|
|
+WHERE channel = 0
|
|
|
+ AND accountId IN @ids
|
|
|
+ AND log_date BETWEEN @startDate AND @endDate
|
|
|
+ AND COALESCE(accountName,'')<>''",
|
|
|
+ new { ids, startDate, endDate },
|
|
|
+ commandTimeout: commandTimeoutSeconds))
|
|
|
+ {
|
|
|
+ affectedNames.Add(name);
|
|
|
+ }
|
|
|
+
|
|
|
+ var days = EachDay(startDate, endDate).ToList();
|
|
|
+ var dailyReports = new List<object>();
|
|
|
+ var endpointCountsByDate = new Dictionary<string, List<TkDailyRepairEndpointCountRow>>();
|
|
|
+ foreach (var date in days)
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ string tableName = $"tk_parse_logs_{date:yyyyMMdd}";
|
|
|
+ if (!TableExists(conn, tableName, commandTimeoutSeconds))
|
|
|
+ {
|
|
|
+ dailyReports.Add(new { date = date.ToString("yyyy-MM-dd"), tableName, skipped = true, reason = "table not exists" });
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ var endpointCounts = SqlMapper.Query<TkDailyRepairEndpointCountRow>(
|
|
|
+ conn,
|
|
|
+ $@"
|
|
|
+SELECT
|
|
|
+ l.accountId,
|
|
|
+ COALESCE(p.company, MAX(l.accountName), '') accountName,
|
|
|
+ COALESCE(l.end_point, '') end_point,
|
|
|
+ COUNT(*) total_count,
|
|
|
+ CAST(COALESCE(SUM(l.success = 1), 0) AS SIGNED) success_count,
|
|
|
+ CAST(COALESCE(SUM(l.success = 0), 0) AS SIGNED) fail_count,
|
|
|
+ CAST(COALESCE(SUM(l.message = '放弃转链' OR l.reason = '放弃转链'), 0) AS SIGNED) abandon_count
|
|
|
+FROM {tableName} l
|
|
|
+LEFT JOIN tk_pool p ON p.id = l.accountId
|
|
|
+WHERE l.accountId IN @ids
|
|
|
+GROUP BY l.accountId, p.company, l.end_point
|
|
|
+ORDER BY l.accountId, l.end_point",
|
|
|
+ new { ids },
|
|
|
+ commandTimeout: commandTimeoutSeconds).ToList();
|
|
|
+ endpointCountsByDate[date.ToString("yyyyMMdd")] = endpointCounts;
|
|
|
+
|
|
|
+ var accountCounts = endpointCounts
|
|
|
+ .GroupBy(item => item.accountId)
|
|
|
+ .Select(group => new TkDailyRepairCountRow
|
|
|
+ {
|
|
|
+ accountId = group.Key,
|
|
|
+ accountName = accounts.FirstOrDefault(item => item.accountId == group.Key)?.accountName
|
|
|
+ ?? group.FirstOrDefault()?.accountName
|
|
|
+ ?? string.Empty,
|
|
|
+ total_count = group.Sum(item => item.total_count),
|
|
|
+ success_count = group.Sum(item => item.success_count),
|
|
|
+ fail_count = group.Sum(item => item.fail_count),
|
|
|
+ abandon_count = group.Sum(item => item.abandon_count)
|
|
|
+ })
|
|
|
+ .ToList();
|
|
|
+
|
|
|
+ int dailyLogRows = 0;
|
|
|
+ var allAccountCounts = ids
|
|
|
+ .Select(accountId => accountCounts.FirstOrDefault(item => item.accountId == accountId)
|
|
|
+ ?? new TkDailyRepairCountRow
|
|
|
+ {
|
|
|
+ accountId = accountId,
|
|
|
+ accountName = accounts.FirstOrDefault(item => item.accountId == accountId)?.accountName ?? string.Empty
|
|
|
+ })
|
|
|
+ .ToList();
|
|
|
+
|
|
|
+ foreach (var counts in allAccountCounts)
|
|
|
+ {
|
|
|
+ if (!dryRun && repairDailyLogs)
|
|
|
+ {
|
|
|
+ dailyLogRows += UpsertCenterDailyLog(conn, date, counts, commandTimeoutSeconds);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ dailyReports.Add(new
|
|
|
+ {
|
|
|
+ date = date.ToString("yyyy-MM-dd"),
|
|
|
+ tableName,
|
|
|
+ skipped = false,
|
|
|
+ dailyLogRows,
|
|
|
+ source = "mysql:tk_parse_logs_yyyyMMdd",
|
|
|
+ counts = allAccountCounts,
|
|
|
+ endpointCounts
|
|
|
+ });
|
|
|
+ }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ var error = new { scope = "daily_log", date = date.ToString("yyyy-MM-dd"), error = FormatRepairError(ex) };
|
|
|
+ errors.Add(error);
|
|
|
+ dailyReports.Add(new { date = date.ToString("yyyy-MM-dd"), skipped = true, reason = "error", error });
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ var endpointReports = new List<object>();
|
|
|
+ if (repairRedis || repairRedisNameKeys)
|
|
|
+ {
|
|
|
+ var endpoints = EndPointCore.List(true)
|
|
|
+ .Where(node => node.status && node.is_public_api && !string.IsNullOrEmpty(EndPointCore.GetRedisServer(node)))
|
|
|
+ .ToList();
|
|
|
+
|
|
|
+ foreach (var endpoint in endpoints)
|
|
|
+ {
|
|
|
+ var redisServer = EndPointCore.GetRedisServer(endpoint);
|
|
|
+ var endpointReport = new List<object>();
|
|
|
+
|
|
|
+ try
|
|
|
+ {
|
|
|
+ await using var scope = await RedisClientFactory.CreateScopeAsync(redisServer);
|
|
|
+ var redis = scope.Client;
|
|
|
+
|
|
|
+ foreach (var date in days)
|
|
|
+ {
|
|
|
+ string dateKey = date.ToString("yyyyMMdd");
|
|
|
+ if (!endpointCountsByDate.TryGetValue(dateKey, out var endpointCounts))
|
|
|
+ {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ foreach (int accountId in ids)
|
|
|
+ {
|
|
|
+ var counts = endpointCounts.FirstOrDefault(item =>
|
|
|
+ item.accountId == accountId &&
|
|
|
+ string.Equals(item.end_point, endpoint.name, StringComparison.Ordinal))
|
|
|
+ ?? new TkDailyRepairEndpointCountRow
|
|
|
+ {
|
|
|
+ accountId = accountId,
|
|
|
+ accountName = accounts.FirstOrDefault(item => item.accountId == accountId)?.accountName ?? string.Empty,
|
|
|
+ end_point = endpoint.name
|
|
|
+ };
|
|
|
+
|
|
|
+ if (!dryRun && repairRedis)
|
|
|
+ {
|
|
|
+ await WriteParseCountKeysAsync(redis, $"tb_{accountId}", dateKey, counts);
|
|
|
+ }
|
|
|
+
|
|
|
+ endpointReport.Add(new
|
|
|
+ {
|
|
|
+ date = date.ToString("yyyy-MM-dd"),
|
|
|
+ bucket = $"tb_{accountId}",
|
|
|
+ counts
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ if (repairRedisNameKeys)
|
|
|
+ {
|
|
|
+ foreach (string name in affectedNames)
|
|
|
+ {
|
|
|
+ var before = await ReadParseCountKeysAsync(redis, name, dateKey);
|
|
|
+
|
|
|
+ if (!dryRun)
|
|
|
+ {
|
|
|
+ await DeleteParseBucketKeysAsync(redis, name, dateKey);
|
|
|
+ }
|
|
|
+
|
|
|
+ endpointReport.Add(new
|
|
|
+ {
|
|
|
+ date = date.ToString("yyyy-MM-dd"),
|
|
|
+ deletedBucket = name,
|
|
|
+ before
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ errors.Add(new { scope = "redis_endpoint", endpoint = endpoint.name, error = FormatRepairError(ex) });
|
|
|
+ }
|
|
|
+
|
|
|
+ endpointReports.Add(new
|
|
|
+ {
|
|
|
+ endpoint = endpoint.name,
|
|
|
+ endpoint.description,
|
|
|
+ dryRun,
|
|
|
+ deletedBuckets = endpointReport
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return new APIResult(new
|
|
|
+ {
|
|
|
+ success = errors.Count == 0,
|
|
|
+ dryRun,
|
|
|
+ accountIds = ids,
|
|
|
+ extraNames,
|
|
|
+ repairDailyLogs,
|
|
|
+ repairRedis,
|
|
|
+ repairRedisNameKeys,
|
|
|
+ commandTimeoutSeconds,
|
|
|
+ startDate = startDate.ToString("yyyy-MM-dd"),
|
|
|
+ endDate = endDate.ToString("yyyy-MM-dd"),
|
|
|
+ affectedNames = affectedNames.OrderBy(item => item).ToList(),
|
|
|
+ mysql = dailyReports,
|
|
|
+ redis = endpointReports,
|
|
|
+ errors
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ private static IEnumerable<DateTime> EachDay(DateTime startDate, DateTime endDate)
|
|
|
+ {
|
|
|
+ for (var date = startDate.Date; date <= endDate.Date; date = date.AddDays(1))
|
|
|
+ {
|
|
|
+ yield return date;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static bool TableExists(IDbConnection conn, string tableName, int commandTimeoutSeconds)
|
|
|
+ {
|
|
|
+ const string sql = @"
|
|
|
+SELECT COUNT(*)
|
|
|
+FROM information_schema.tables
|
|
|
+WHERE table_schema = DATABASE()
|
|
|
+ AND table_name = @tableName";
|
|
|
+ return SqlMapper.ExecuteScalar<int>(
|
|
|
+ conn,
|
|
|
+ sql,
|
|
|
+ new { tableName },
|
|
|
+ commandTimeout: commandTimeoutSeconds) > 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static int UpsertCenterDailyLog(IDbConnection conn, DateTime date, TkDailyRepairCountRow counts, int commandTimeoutSeconds)
|
|
|
+ {
|
|
|
+ if (string.IsNullOrWhiteSpace(counts.accountName)) return 0;
|
|
|
+
|
|
|
+ string successPercentage = counts.total_count > 0 ? $"{counts.success_count / (double)counts.total_count * 100:f2}%" : string.Empty;
|
|
|
+ string abandonPercentage = counts.total_count > 0 ? $"{counts.abandon_count / (double)counts.total_count * 100:f2}%" : string.Empty;
|
|
|
+
|
|
|
+ var existingIds = SqlMapper.Query<int>(
|
|
|
+ conn,
|
|
|
+ @"
|
|
|
+SELECT id
|
|
|
+FROM center_daily_logs
|
|
|
+WHERE channel = 0
|
|
|
+ AND log_date = @date
|
|
|
+ AND accountId = @accountId
|
|
|
+ORDER BY id",
|
|
|
+ new { date, counts.accountId },
|
|
|
+ commandTimeout: commandTimeoutSeconds).ToList();
|
|
|
+
|
|
|
+ if (existingIds.Count > 0)
|
|
|
+ {
|
|
|
+ int affectedRows = SqlMapper.Execute(
|
|
|
+ conn,
|
|
|
+ @"
|
|
|
+UPDATE center_daily_logs
|
|
|
+SET accountName = @accountName,
|
|
|
+ parse_total_count = @totalCount,
|
|
|
+ parse_success_count = @successCount,
|
|
|
+ parse_abandon_count = @abandonCount,
|
|
|
+ parse_success_percentage = @successPercentage,
|
|
|
+ parse_abandon_percentage = @abandonPercentage,
|
|
|
+ last_time = NOW()
|
|
|
+WHERE id = @id",
|
|
|
+ new
|
|
|
+ {
|
|
|
+ id = existingIds[0],
|
|
|
+ counts.accountName,
|
|
|
+ totalCount = counts.total_count,
|
|
|
+ successCount = counts.success_count,
|
|
|
+ abandonCount = counts.abandon_count,
|
|
|
+ successPercentage,
|
|
|
+ abandonPercentage
|
|
|
+ },
|
|
|
+ commandTimeout: commandTimeoutSeconds);
|
|
|
+
|
|
|
+ if (existingIds.Count > 1)
|
|
|
+ {
|
|
|
+ affectedRows += SqlMapper.Execute(
|
|
|
+ conn,
|
|
|
+ "DELETE FROM center_daily_logs WHERE id IN @ids",
|
|
|
+ new { ids = existingIds.Skip(1).ToArray() },
|
|
|
+ commandTimeout: commandTimeoutSeconds);
|
|
|
+ }
|
|
|
+
|
|
|
+ return affectedRows;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (counts.total_count <= 0) return 0;
|
|
|
+
|
|
|
+ return SqlMapper.Execute(
|
|
|
+ conn,
|
|
|
+ @"
|
|
|
+INSERT INTO center_daily_logs
|
|
|
+ (channel, accountId, accountName, log_date, create_time, last_time,
|
|
|
+ parse_total_count, parse_success_count, parse_abandon_count,
|
|
|
+ parse_success_percentage, parse_abandon_percentage)
|
|
|
+VALUES
|
|
|
+ (0, @accountId, @accountName, @date, NOW(), NOW(),
|
|
|
+ @totalCount, @successCount, @abandonCount,
|
|
|
+ @successPercentage, @abandonPercentage)",
|
|
|
+ new
|
|
|
+ {
|
|
|
+ date,
|
|
|
+ counts.accountId,
|
|
|
+ counts.accountName,
|
|
|
+ totalCount = counts.total_count,
|
|
|
+ successCount = counts.success_count,
|
|
|
+ abandonCount = counts.abandon_count,
|
|
|
+ successPercentage,
|
|
|
+ abandonPercentage
|
|
|
+ },
|
|
|
+ commandTimeout: commandTimeoutSeconds);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static async Task<TkDailyRepairCountRow> ReadParseCountKeysAsync(YunhuiKit.RedisClient redis, string bucket, string dateKey)
|
|
|
+ {
|
|
|
+ return new TkDailyRepairCountRow
|
|
|
+ {
|
|
|
+ accountName = bucket,
|
|
|
+ total_count = await redis.GetAsync<int>($":parse_total:{bucket}:{dateKey}"),
|
|
|
+ success_count = await redis.GetAsync<int>($":parse_total:{bucket}:success:{dateKey}"),
|
|
|
+ fail_count = await redis.GetAsync<int>($":parse_total:{bucket}:fail:{dateKey}"),
|
|
|
+ abandon_count = await redis.GetAsync<int>($":parse_total:{bucket}:放弃转链:{dateKey}")
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ private static async Task DeleteParseBucketKeysAsync(YunhuiKit.RedisClient redis, string bucket, string dateKey)
|
|
|
+ {
|
|
|
+ var keys = new List<string>
|
|
|
+ {
|
|
|
+ $":parse_total:{bucket}:{dateKey}",
|
|
|
+ $":parse_total:{bucket}:success:{dateKey}",
|
|
|
+ $":parse_total:{bucket}:fail:{dateKey}",
|
|
|
+ $":parse_total:{bucket}:放弃转链:{dateKey}",
|
|
|
+ $":parse_total:{bucket}:message:{dateKey}",
|
|
|
+ $":parse_total:{bucket}:reason:{dateKey}"
|
|
|
+ };
|
|
|
+
|
|
|
+ foreach (string dpBucket in new[] { "dp_none", "dp_home", "dp_success", "dp_fail" })
|
|
|
+ {
|
|
|
+ keys.Add($":parse_total:{dpBucket}:{bucket}:{dateKey}");
|
|
|
+ keys.Add($":parse_total:{dpBucket}:{bucket}:success:{dateKey}");
|
|
|
+ keys.Add($":parse_total:{dpBucket}:{bucket}:fail:{dateKey}");
|
|
|
+ keys.Add($":parse_total:{dpBucket}:{bucket}:放弃转链:{dateKey}");
|
|
|
+ keys.Add($":parse_total:{dpBucket}:{bucket}:message:{dateKey}");
|
|
|
+ keys.Add($":parse_total:{dpBucket}:{bucket}:reason:{dateKey}");
|
|
|
+ }
|
|
|
+
|
|
|
+ await redis.DelAsync(keys.ToArray());
|
|
|
+ }
|
|
|
+
|
|
|
+ private static string FormatRepairError(Exception ex)
|
|
|
+ {
|
|
|
+ return ex.InnerException == null ? ex.Message : $"{ex.Message} | {ex.InnerException.Message}";
|
|
|
+ }
|
|
|
+
|
|
|
+ private static async Task WriteParseCountKeysAsync(YunhuiKit.RedisClient redis, string bucket, string dateKey, TkDailyRepairCountRow counts)
|
|
|
+ {
|
|
|
+ var keys = new[]
|
|
|
+ {
|
|
|
+ $":parse_total:{bucket}:{dateKey}",
|
|
|
+ $":parse_total:{bucket}:success:{dateKey}",
|
|
|
+ $":parse_total:{bucket}:fail:{dateKey}",
|
|
|
+ $":parse_total:{bucket}:放弃转链:{dateKey}"
|
|
|
+ };
|
|
|
+
|
|
|
+ if (counts.total_count <= 0)
|
|
|
+ {
|
|
|
+ await redis.DelAsync(keys);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ await redis.SetAsync(keys[0], counts.total_count, 90 * 86400);
|
|
|
+ await redis.SetAsync(keys[1], counts.success_count, 90 * 86400);
|
|
|
+ await redis.SetAsync(keys[2], counts.fail_count, 90 * 86400);
|
|
|
+ await redis.SetAsync(keys[3], counts.abandon_count, 90 * 86400);
|
|
|
+ }
|
|
|
+
|
|
|
+ private class TkDailyRepairCountRow
|
|
|
+ {
|
|
|
+ public int accountId { get; set; }
|
|
|
+ public string accountName { get; set; } = string.Empty;
|
|
|
+ public long total_count { get; set; }
|
|
|
+ public long success_count { get; set; }
|
|
|
+ public long fail_count { get; set; }
|
|
|
+ public long abandon_count { get; set; }
|
|
|
+ }
|
|
|
+
|
|
|
+ private sealed class TkDailyRepairEndpointCountRow : TkDailyRepairCountRow
|
|
|
+ {
|
|
|
+ public string end_point { get; set; } = string.Empty;
|
|
|
+ }
|
|
|
+
|
|
|
+ [HttpGet]
|
|
|
+ public async Task<ActionResult> xxx()
|
|
|
+ {
|
|
|
var list = await TkPoolCore.ListAsync();
|
|
|
if (list == null) return new APIResult(new { success = false, message = "没有有效账号", });
|
|
|
string message = string.Empty;
|
|
|
@@ -506,4 +958,4 @@ namespace molilian.api.Controllers
|
|
|
|
|
|
|
|
|
}
|
|
|
-}
|
|
|
+}
|