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.Collections.Concurrent; using YunhuiKit; namespace molilian.core { public partial class PddPoolCore { private static readonly object _lockObj = new(); private static IEnumerable _cached; private static IEnumerable _all_cached; private static string _end_point; private static Dictionary _incomeAmt = new(); private static ConcurrentDictionary _suspend = new(); // 移除本地内存统计,改用 Redis 存储 private static SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1); static PddPoolCore() { _end_point = Environment.GetEnvironmentVariable("EndPoint"); } /// /// 获取一个账号用于处理请求 /// 使用平滑加权轮询算法(Smooth Weighted Round-Robin)实现流量均衡分配 /// public static PddPoolDTO? GetOne(PddUnionWorkMode mode, int accountid = 0, string parse_type = "") { var list = List(); if (!list.Any()) return null; // 按 parse_type 过滤 if ("dp".Equals(parse_type)) { list = list.Where(item => "dp".Equals(item.parse_type)).ToList(); if (!list.Any()) return null; } else { list = list.Where(item => string.IsNullOrEmpty(item.parse_type)).ToList(); if (!list.Any()) return null; } // 过滤出符合条件的候选账号 var candidates = list.Where(e => IsMatch(e, mode, accountid)).ToList(); if (!candidates.Any()) return null; // 使用平滑加权轮询算法选择账号 return SmoothWeightedRoundRobin(candidates, parse_type); } /// /// 基于最后使用时间的调度策略(并发安全版本) /// 选择"距离上次使用时间最长"且"超过cis_limit间隔"的账号 /// /// 优势: /// 1. 不区分新老账号,一视同仁(无历史记录的账号返回DateTime.MinValue,自然会被优先选中) /// 2. 自然满足cis_limit要求(距离时间 < limit 会被跳过) /// 3. 优先选择"休息最久"的账号,最大化每个账号的休息时间 /// 4. 简单直观,易于调试和理解 /// 5. 并发安全:使用Redis原子操作预留账号,避免多个请求竞争同一账号 /// private static PddPoolDTO? SmoothWeightedRoundRobin(List candidates, string parse_type) { if (!candidates.Any()) return null; if (candidates.Count == 1) { var single = candidates[0]; // 尝试预留账号(并发安全) if (TryReserveAccount(single.id, parse_type, single.cis_limit)) { return single; } return null; // 预留失败,说明账号已被其他请求占用 } try { string groupKey = string.IsNullOrEmpty(parse_type) ? "default" : parse_type; var now = DateTime.Now; // 候选账号列表:记录每个账号的空闲时长 var accountsWithIdleTime = new List<(PddPoolDTO account, long idleMs)>(); foreach (var account in candidates) { // 获取账号最后使用时间 var lastUsedTime = GetAccountLastUsedTime(account.id, groupKey); // 计算距离现在的时间差(毫秒) long idleMs = (long)(now - lastUsedTime).TotalMilliseconds; // 如果设置了 cis_limit,必须满足间隔要求 if (account.cis_limit > 0 && idleMs < account.cis_limit) { continue; // 跳过未满足间隔要求的账号 } accountsWithIdleTime.Add((account, idleMs)); } // 如果没有满足条件的账号,返回null if (!accountsWithIdleTime.Any()) return null; // 按照空闲时间降序排序,优先尝试"休息最久"的账号 var sortedAccounts = accountsWithIdleTime .OrderByDescending(x => x.idleMs) .ToList(); // 依次尝试预留账号(从空闲时间最长的开始) foreach (var (account, idleMs) in sortedAccounts) { // 尝试原子预留此账号 if (TryReserveAccount(account.id, groupKey, account.cis_limit)) { // 预留成功,更新最后使用时间 SetAccountLastUsedTime(account.id, groupKey, now); Console.WriteLine($"[PDD] Selected account {account.id} ({account.name}), idle time: {Math.Round(idleMs / 1000.0, 2)}s"); return account; } else { // 预留失败,说明此账号已被其他并发请求占用,尝试下一个 Console.WriteLine($"[PDD] Account {account.id} ({account.name}) is reserved by another request, trying next..."); } } // 所有满足条件的账号都已被占用 Console.WriteLine($"[PDD] All available accounts are reserved, no account available"); return null; } catch (Exception ex) { Console.WriteLine($"Idle time selection failed: {ex.Message}, fallback to simple strategy"); // 降级策略:使用原有的最少使用次数算法 return candidates .OrderBy(account => GetCurrentDayUsageFromRedis(account.id)) .ThenBy(account => Guid.NewGuid()) .FirstOrDefault(); } } /// /// 尝试原子预留账号(并发安全) /// 使用 Redis SET NX EX 实现原子操作,避免多个请求竞争同一账号 /// /// 账号ID /// 分组键 /// 请求间隔限制(毫秒),0表示无限制 /// true=预留成功,false=账号已被占用 private static bool TryReserveAccount(int accountId, string groupKey, int cisLimit) { try { string reserveKey = $"pdd_account_reserved:{groupKey}:{accountId}"; // 如果没有设置 cis_limit,使用默认100ms的预留时间(防止极端并发) int expireSeconds = cisLimit > 0 ? (int)Math.Ceiling(cisLimit / 1000.0) : 1; // 使用 Redis SET NX EX 原子操作(一次性完成设置和过期时间) // 等价于 Redis 命令: SET key value NX EX seconds // 如果 key 不存在则设置成功(返回true),如果已存在则失败(返回false) // 注意:必须使用原子操作,SetNx + Expire 两步操作会有竞态条件! bool reserved = RedisHelper.Set(reserveKey, 1, expireSeconds, CSRedis.RedisExistence.Nx); return reserved; } catch (Exception ex) { Console.WriteLine($"Failed to reserve account {accountId}: {ex.Message}"); // 预留失败时保守处理:假设账号不可用 return false; } } /// /// 获取账号最后使用时间 /// private static DateTime GetAccountLastUsedTime(int accountId, string groupKey) { try { string key = $"pdd_last_used_time:{groupKey}:{accountId}"; var timestamp = RedisHelper.Get(key); if (timestamp > 0) { return DateTimeOffset.FromUnixTimeMilliseconds(timestamp).LocalDateTime; } // 如果没有记录,返回很久以前的时间(确保新账号和长期未使用的账号能被优先选中) return DateTime.MinValue; } catch { return DateTime.MinValue; } } /// /// 设置账号最后使用时间 /// private static void SetAccountLastUsedTime(int accountId, string groupKey, DateTime time) { try { string key = $"pdd_last_used_time:{groupKey}:{accountId}"; long timestamp = new DateTimeOffset(time).ToUnixTimeMilliseconds(); // 设置过期时间为24小时,避免数据积累 RedisHelper.Set(key, timestamp, 86400); } catch (Exception ex) { Console.WriteLine($"Failed to set last used time for {accountId}: {ex.Message}"); } } /// /// 获取账号的静态权重 /// 可以根据账号的 daily_calls_limit 或其他因素动态计算 /// private static int GetAccountStaticWeight(int accountId) { try { string key = $"pdd_account_weight:{accountId}"; int weight = RedisHelper.Get(key); // 如果没有设置,返回默认权重100 if (weight <= 0) { // 可以基于账号的限额动态计算默认权重 var account = List()?.FirstOrDefault(a => a.id == accountId); if (account != null && account.daily_calls_limit > 0) { // 将日限额映射到权重:每1000次调用对应权重10 weight = Math.Max(10, Math.Min(1000, account.daily_calls_limit / 100)); } else { weight = 100; // 默认权重 } } return weight; } catch { return 100; // 异常时返回默认权重 } } /// /// 设置账号的静态权重 /// public static void SetAccountStaticWeight(int accountId, int weight) { try { if (weight < 1) weight = 1; if (weight > 1000) weight = 1000; string key = $"pdd_account_weight:{accountId}"; RedisHelper.Set(key, weight, 30 * 86400); // 30天过期 } catch (Exception ex) { Console.WriteLine($"Failed to set account weight for {accountId}: {ex.Message}"); } } /// /// 获取账号的当前动态权重 /// private static int GetAccountCurrentWeight(int accountId, string groupKey) { try { string key = $"pdd_current_weight:{groupKey}:{accountId}"; return RedisHelper.Get(key); } catch { return 0; } } /// /// 设置账号的当前动态权重 /// private static void SetAccountCurrentWeight(int accountId, string groupKey, int weight) { try { string key = $"pdd_current_weight:{groupKey}:{accountId}"; RedisHelper.Set(key, weight, 3600); // 1小时过期,自动重置 } catch (Exception ex) { Console.WriteLine($"Failed to set current weight for {accountId}: {ex.Message}"); } } /// /// 重置所有账号的动态权重(用于调试或重新初始化) /// public static void ResetAllWeights(string groupKey = "default") { try { var accounts = List()?.Where(a => a.status && a.enable_parse).ToList(); if (accounts == null || !accounts.Any()) return; foreach (var account in accounts) { string key = $"pdd_current_weight:{groupKey}:{account.id}"; RedisHelper.Del(key); } Console.WriteLine($"Reset weights for {accounts.Count} accounts in group '{groupKey}'"); } catch (Exception ex) { Console.WriteLine($"Failed to reset weights: {ex.Message}"); } } private static int GetCurrentDayUsageFromRedis(int accountId) { try { var today = DateTime.Now.ToString("yyyyMMdd"); string key = $"pdd_daily_usage:{accountId}:{today}"; return RedisHelper.Get(key); } catch (Exception) { return 0; } } internal static void UpdateAccountUsage(int accountId) { try { var today = DateTime.Now.ToString("yyyyMMdd"); string key = $"pdd_daily_usage:{accountId}:{today}"; // 递增计数并设置24小时过期时间 RedisHelper.IncrBy(key); RedisHelper.Expire(key, 86400); // 24小时后自动过期 } catch (Exception ex) { // 记录错误但不影响主流程 Console.WriteLine($"Failed to update account usage for {accountId}: {ex.Message}"); } } /// /// 获取当前所有账号的平均使用次数 /// private static int GetAverageUsageCount() { try { var activeAccounts = List()?.Where(a => a.enable_parse).ToList(); if (activeAccounts == null || !activeAccounts.Any()) return 0; var today = DateTime.Now.ToString("yyyyMMdd"); var totalUsage = 0; var validCount = 0; foreach (var account in activeAccounts) { var usage = GetCurrentDayUsageFromRedis(account.id); totalUsage += usage; validCount++; } return validCount > 0 ? totalUsage / validCount : 0; } catch (Exception ex) { Console.WriteLine($"Failed to get average usage count: {ex.Message}"); return 0; } } /// /// 重置所有在线账号的当日调用次数Redis计数器 /// 随机排序账号后,每个账号间隔1递增value /// public static int RechargeAllOnlineAccountUsage() { try { var onlineAccounts = List()?.Where(a => a.status && a.enable_parse).ToList(); if (onlineAccounts == null || !onlineAccounts.Any()) { return 0; } var today = DateTime.Now.ToString("yyyyMMdd"); // 随机排序账号 var random = new Random(); var shuffledAccounts = onlineAccounts.OrderBy(x => random.Next()).ToList(); int incrementValue = 1; int rechargedCount = 0; foreach (var account in shuffledAccounts) { string key = $"pdd_daily_usage:{account.id}:{today}"; // 设置递增的value并设置24小时过期时间 RedisHelper.Set(key, incrementValue, 86400); RedisHelper.Set(key, 0, 86400); incrementValue++; rechargedCount++; } return rechargedCount; } catch (Exception ex) { } return 0; } /// /// 为新上线的账号设置平均使用次数(仅用于降级策略的统计) /// 注意:基于时间间隔的调度策略不需要初始化权重,因为无历史记录的账号会自动返回DateTime.MinValue /// public static void InitializeNewAccountUsage(int accountId, string groupKey = "default") { try { var today = DateTime.Now.ToString("yyyyMMdd"); string usageKey = $"pdd_daily_usage:{accountId}:{today}"; // 检查该账号今天是否已有使用记录 var currentUsage = RedisHelper.Get(usageKey); if (currentUsage > 0) { Console.WriteLine($"Account {accountId} already has usage record: {currentUsage}, skip initialization"); return; // 已有记录,不需要初始化 } // 获取所有在线账号的平均值(仅用于降级策略) var activeAccounts = List()?.Where(a => a.status && a.enable_parse && a.id != accountId).ToList(); if (activeAccounts == null || !activeAccounts.Any()) { Console.WriteLine($"No other active accounts found, account {accountId} will use default value"); return; // 没有其他账号,使用默认值0即可 } // 初始化 daily_usage(用于降级策略的统计) var totalUsage = 0; foreach (var account in activeAccounts) { totalUsage += GetCurrentDayUsageFromRedis(account.id); } var averageUsage = totalUsage / activeAccounts.Count; if (averageUsage > 0) { RedisHelper.Set(usageKey, averageUsage, 86400); Console.WriteLine($"Initialized account {accountId} daily_usage with average: {averageUsage}"); } Console.WriteLine($"Account {accountId} initialization complete. Time-based scheduling will use DateTime.MinValue for idle time calculation."); } catch (Exception ex) { Console.WriteLine($"Failed to initialize new account for {accountId}: {ex.Message}"); } } /// /// 获取当前统计信息(用于监控和调试)- 改为从Redis获取 /// internal static Dictionary GetCurrentUsageSnapshot() { try { var result = new Dictionary(); var accounts = List()?.Where(a => a.status && a.enable_parse).ToList(); if (accounts == null || !accounts.Any()) return result; var today = DateTime.Now.ToString("yyyyMMdd"); foreach (var account in accounts) { string key = $"pdd_daily_usage:{account.id}:{today}"; int usage = RedisHelper.Get(key); result[$"Account_{account.id}_{account.name}"] = usage; } return result; } catch (Exception) { return new Dictionary(); } } /// /// 获取账号调度时间间隔信息(用于监控和调试) /// public static Dictionary GetScheduleWeightSnapshot(string groupKey = "default") { try { var result = new Dictionary(); var accounts = List()?.Where(a => a.status && a.enable_parse).ToList(); if (accounts == null || !accounts.Any()) return result; var now = DateTime.Now; var accountInfos = new List>(); foreach (var account in accounts) { var lastUsedTime = GetAccountLastUsedTime(account.id, groupKey); long idleMs = (long)(now - lastUsedTime).TotalMilliseconds; bool canUse = account.cis_limit <= 0 || idleMs >= account.cis_limit; var info = new Dictionary { ["account_id"] = account.id, ["account_name"] = account.name, ["cis_limit_ms"] = account.cis_limit, ["last_used_time"] = lastUsedTime == DateTime.MinValue ? "从未使用" : lastUsedTime.ToString("yyyy-MM-dd HH:mm:ss.fff"), ["idle_time_ms"] = idleMs, ["idle_time_seconds"] = Math.Round(idleMs / 1000.0, 2), ["can_use"] = canUse, ["daily_usage"] = GetCurrentDayUsageFromRedis(account.id), ["daily_limit"] = account.daily_calls_limit }; accountInfos.Add(info); } // 按照空闲时间降序排序(与选择逻辑一致) accountInfos = accountInfos.OrderByDescending(x => (long)x["idle_time_ms"]).ToList(); result["accounts"] = accountInfos; result["group_key"] = groupKey; result["timestamp"] = now.ToString("yyyy-MM-dd HH:mm:ss.fff"); result["total_accounts"] = accountInfos.Count; result["available_accounts"] = accountInfos.Count(x => (bool)x["can_use"]); return result; } catch (Exception ex) { return new Dictionary { ["error"] = ex.Message }; } } /// /// 强制清理统计数据(仅用于测试或紧急情况)- Redis版本 /// internal static void ForceClearStats() { try { var today = DateTime.Now.ToString("yyyyMMdd"); var pattern = $"pdd_daily_usage:*:{today}"; // TODO: 实现Redis批量删除相关keys // 生产环境中需要谨慎使用 Console.WriteLine("Force clear stats - Redis keys will auto-expire in 24h"); } catch (Exception ex) { Console.WriteLine($"Failed to force clear stats: {ex.Message}"); } } internal static void TempSuspend(int accountId) { _suspend.AddOrUpdate(accountId, DateTime.Now, (key, oldValue) => DateTime.Now); } private static bool IsMatch(PddPoolDTO item, PddUnionWorkMode mode, int accountid) { if (accountid != 0 && accountid != item.id) return false; if (!item.enable_parse) return false; if (mode != PddUnionWorkMode.All && mode != item.work_mode) return false; if (item.work_mode == PddUnionWorkMode.Crawler && !item.cookie_status) return false; if (!string.IsNullOrEmpty(_end_point) && !string.IsNullOrEmpty(item.end_point)) { if (item.end_point != _end_point) return false; } // 使用初始化参数创建工作时间表 if (!new WorkSchedule(item.time_range).IsWorkHour()) return false; if (_suspend.TryGetValue(accountid, out DateTime suspendTime)) { var ts = DateTime.Now - suspendTime; if (ts.TotalSeconds < 70) return false; } // cis_limit 检查已移至 SmoothWeightedRoundRobin 方法中基于时间间隔判断 // 这里不再需要检查 Redis 锁 if (item.rpm_limit > 0) { int rpm_num = RiskControlCore.GetCalls(TkChannelEnum.pdd, item.id, DateTime.Now.ToString("yyyyMMddHHmm")); if (rpm_num >= item.rpm_limit) return false; } if (item.daily_calls_limit > 0) { int daily_num = RiskControlCore.GetCalls(TkChannelEnum.pdd, item.id, DateTime.Now.ToString("yyyyMMdd")); if (daily_num >= item.daily_calls_limit) return false; } if (item.hourly_calls_limit > 0) { int hourly_num = RiskControlCore.GetCalls(TkChannelEnum.pdd, item.id, DateTime.Now.ToString("yyyyMMddHH")); if (hourly_num >= item.hourly_calls_limit) return false; } return true; } public static async Task> AllListAsync(bool force = false) { if (!force && _all_cached != default) return _all_cached; try { await _semaphore.WaitAsync(); string cache_key = $"cache:all_pdd_pool"; var list = await RedisKit.GetAsync>(cache_key); if (force || list == default) { list = new DBContext.Table("pdd_pool").Select(); if (list == null) return default; // 等待 Redis 写入完成 await RedisKit.SetAsync(cache_key, list, 30 * 86400); } _all_cached = list; return list; } catch (Exception) { // 发生异常时返回上一次的缓存,如果没有则返回默认值 return _all_cached ?? default; } finally { _semaphore.Release(); } } public static IEnumerable List(bool force = false) { if (!force && _cached != null) return _cached; string cache_key = $"cache:pdd_pool"; var list = RedisHelper.Get>(cache_key); if (force || list == null) { lock (_lockObj) { list = new DBContext.Table("pdd_pool") .Where("status=@status", new { status = 1 }) .Select(); if (list == null) return default; foreach (var item in list) { RiskControlCore.SetCallsAsync(TkChannelEnum.pdd, item.id, DateTime.Now.ToString("yyyyMMddHH"), item.current_hourly_calls); RiskControlCore.SetCallsAsync(TkChannelEnum.pdd, item.id, DateTime.Now.ToString("yyyyMMdd"), item.current_daily_calls); } RedisHelper.Set(cache_key, list, 30 * 86400); } } _cached = list; return list; } public static void Refresh() { _cached = null; _all_cached = null; _ = List(true); _ = AllListAsync(true); } public static void Disabled(string name) { string cache_key = $"cache:pdd_pool:{name}:disabled"; long count = RedisHelper.IncrBy(cache_key); RedisHelper.Expire(cache_key, 10); if (count > 1) return; new DBContext.Table("pdd_pool") .Add("status", 0) .Where("name=@name", new { name }) .Update(); _ = List(true); NotifyCore.Notify(new NifyMessage { message = $"【多多:{name}】调用异常", priority = NifyMessagePriority.high, tags = ["red_circle"] }); } public static void Disabled(int accountId, string name, string content) { string cache_key = $"cache:pdd_pool:{name}:disabled"; long count = RedisHelper.IncrBy(cache_key); RedisHelper.Expire(cache_key, 10); if (count > 1) return; var update = new DBContext.Table("pdd_pool").Add("status", 0); if (accountId > 0) { update.Where("id=@accountId", new { accountId }).Update(); } else { update.Where("name=@name", new { name }).Update(); } _ = List(true); NotifyCore.Notify(new NifyMessage { message = $"【多多{accountId}:{name}】cookie 掉线\n\n{content}", priority = NifyMessagePriority.high, tags = ["red_circle"] }); //NotifyCore.QYWeixinPushNotifyAsync("掉线", $"【多多{accountId}:{name}】cookie 掉线"); EndPointCore.NotifyReload(true); } internal static void AccountExhausted() { string cache_key = $"cache:pdd_pool:account:exhausted"; long count = RedisHelper.IncrBy(cache_key); if (count > 1) return; RedisHelper.Expire(cache_key, 3600); NotifyCore.Notify(new NifyMessage { message = $"【多多】没有匹配账号", priority = NifyMessagePriority.high, tags = ["red_circle"] }); //_ = NotifyCore.QYWeixinPushNotifyAsync("没账号", $"【多多】没有匹配账号"); } internal static async Task GetUserInfo(string cookies) { JsonElement result = default; try { string url = "https://jinbao.pinduoduo.com/network/api/account/userInfo"; WebClientUtility client = new WebClientUtility(); client.Post("{}"); client.SetCookies(cookies); client.SetContentType("application/json"); var response = await client.RequestAsync(url, "POST"); var body = response.Body(); result = body.Convert2JsonElement(); } catch (Exception ex) { } return result; } public static async Task UpdateCookies(string cookies, string user_agent, int id = 0) { if (string.IsNullOrEmpty(cookies)) return 0; var userinfo = await GetUserInfo(cookies); if (userinfo.ValueKind != JsonValueKind.Object) return 0; int duoId = userinfo.PathRead("result.duoId", 0); string company = userinfo.PathRead("result.mobile", string.Empty); string lastPid = userinfo.PathRead("result.lastPid", string.Empty); int accountId = 0; if (duoId == 0 && id == 0) return 0; string filter = id != 0 ? "id=@id" : "duoId=@duoId"; var exist = new DBContext.Table("pdd_pool").Get(filter, new { id, duoId }); if (exist != null) { var status = exist.status; var work_mode = exist.work_mode; accountId = exist.id; if (work_mode == PddUnionWorkMode.Crawler) status = true; new DBContext.Table("pdd_pool") .Add("cookies", cookies) .Add("user_agent", user_agent) .Add("status", status) .Add("cookie_status", 1) .Add("last_time", DateTime.Now) .Add("login_time", DateTime.Now) .Where("id=@id", new { exist.id }) .Update(); if (status) { _ = List(true); // 为重新上线的账号初始化使用次数和权重(针对所有分组) InitializeNewAccountUsage(exist.id, "default"); InitializeNewAccountUsage(exist.id, "dp"); } } else { accountId = new DBContext.Table("pdd_pool") .Add("duoId", duoId) .Add("name", company) .Add("company", company) .Add("description", "由cookies上报创建此记录") .Add("cookies", cookies) .Add("user_agent", user_agent) .Add("pid", lastPid) .Add("cookie_status", 1) .Add("create_time", DateTime.Now) .Add("last_time", DateTime.Now) .Add("login_time", DateTime.Now) .Add("status", 0) .Create(); // 为新创建的账号初始化使用次数(如果将来会启用的话) // InitializeNewAccountUsage(accountId); // 暂不调用,因为新创建的账号status=0 } NotifyCore.Notify(new NifyMessage { message = $"【拼多多{accountId}:{company}】cookie 上线", tags = ["green_circle"] }); EndPointCore.NotifyReload(true); //NotifyCore.QYWeixinPushNotifyAsync("上线", $"【淘宝联盟:{dnk}】cookie 上报更新"); return accountId; } public static int CookieDisabled(int id) { return new DBContext.Table("pdd_pool") .Add("cookie_status", 0) .Add("last_time", DateTime.Now) .Where("id=@id", new { id }) .Update(); } public static int Update(PddPoolDTO account) { return new DBContext.Table("pdd_pool") .Add("current_hourly_calls", account.current_hourly_calls) .Add("current_daily_calls", account.current_daily_calls) //.Add("today_clickNum", account.today_clickNum) //.Add("today_cosFee", account.today_cosFee) //.Add("today_cosPrice", account.today_cosPrice) //.Add("today_finishCosFee", account.today_finishCosFee) //.Add("today_finishCosPrice", account.today_finishCosPrice) //.Add("today_finishOrderNum", account.today_finishOrderNum) //.Add("today_orderNum", account.today_orderNum) .Add("last_time", DateTime.Now) .Where("id=@id", new { account.id }) .Update(); } } }