'int(11) NOT NULL AUTO_INCREMENT', 'user_id' => 'int(11) NOT NULL', 'currency' => 'varchar(20) NOT NULL', 'amount' => 'decimal(15,2) NOT NULL', 'before' => 'decimal(15,2) NOT NULL', 'after' => 'decimal(15,2) NOT NULL', 'type' => 'varchar(50) NOT NULL', 'created_at' => 'int(11) NOT NULL COMMENT \'UNIX timestamp\'', // 改为整型时间戳 'memo' => 'varchar(255) DEFAULT NULL', 'PRIMARY KEY (`id`)' ]; function getCreatedAtAttr($v){ return $v ? explode('.',$v)[0] : ''; } protected function getOptions(): array{ return array_merge(parent::getOptions(),[ 'connection' => 'mongodb', // 'append' => [ // 'from_user', // 'to_user' // ], ]); } public static function create(array|object $data, array $allowField = [], bool $replace = false):\think\model\contract\Modelable { $model = new static(); if(isset($data['currency'])){ if(in_array($data['currency'],Config('site.allow_balance_log'))){ $data['status']=isset($data['status']) ? $data['status']:1; $data['user_id'] = intval($data['user_id']); $data['amount'] = floatval($data['amount']); $data['before'] = floatval($data['before']); $data['after'] = floatval($data['after']); $data['type'] = $data['type'] instanceof \app\enum\BalanceType ? $data['type']->value : floatval($data['type']); $model->setSuffix('_'.strtolower($data['currency']))->allowField($allowField) ->replace($replace) ->save($data, true); } } return $model->fetchModel($model); } // 创建所有需要的表索引 public static function createAllIndexes(): array { $results = []; $allow_balance_log = Config('site.allow_balance_log'); foreach ($allow_balance_log as $currency) { $results[$currency] = self::createTableIndexes($currency); } return $results; } // 创建索引(适配时间戳查询) public static function createTableIndexes(string $currency): array { $table = self::getTableName($currency); $results = []; try { // 确保归档表存在 if (!self::tableExists($table)) { self::createTableStructure($table); } // 主复合索引(使用时间戳) if (!self::indexExists($table, 'idx_user_currency_type_created')) { Db::execute("ALTER TABLE `{$table}` ADD INDEX `idx_user_currency_type_created` (`user_id`, `currency`, `type`, `created_at`)"); $results[] = "Created idx_user_currency_type_created on {$table}"; } // 时间索引(降序优化) if (!self::indexExists($table, 'idx_created_at')) { Db::execute("ALTER TABLE `{$table}` ADD INDEX `idx_created_at` (`created_at` DESC)"); $results[] = "Created idx_created_at on {$table}"; } } catch (\Throwable $e) { $results['error'] = "Error on {$table}: " . $e->getMessage(); } return $results; } // 检查索引是否存在 protected static function indexExists(string $table, string $indexName): bool { $indexes = Db::query("SHOW INDEX FROM `{$table}` WHERE Key_name = ?", [$indexName]); return !empty($indexes); } // 检查表是否存在 protected static function tableExists(string $table): bool { try { Db::query("SELECT 1 FROM `{$table}` LIMIT 1"); return true; } catch (\Throwable $e) { return false; } } // 数据归档方法(可在定时任务中调用) public static function archiveData(int $days = 3): array { $results = []; $allow_balance_log = Config('site.allow_balance_log'); foreach ($allow_balance_log as $currency) { $results[$currency] = self::archiveCurrencyData($currency, $days); } return $results; } // 归档指定货币的数据 protected static function archiveCurrencyData(string $currency, int $days): array { $table = self::getTableName($currency); $archiveTable = $table . '_archive'; $cutoffTimestamp = time() - ($days * 86400); // 转为时间戳计算 $result = [ 'table' => $table, 'archived' => 0, 'messages' => [] ]; try { // 确保归档表存在 if (!self::tableExists($archiveTable)) { self::createTableStructure($archiveTable); $result['messages'][] = "Created archive table: {$archiveTable}"; } // 分批归档数据 $totalArchived = 0; Db::table($table) ->where('created_at', '<=', $cutoffTimestamp) ->chunk(1000, function($logs) use ($archiveTable, $table, &$totalArchived) { Db::table($archiveTable)->insertAll($logs); $count = count($logs); Db::table($table)->whereIn('id', array_column($logs, 'id'))->delete(); $totalArchived += $count; }); $result['archived'] = $totalArchived; $result['messages'][] = "Archived {$totalArchived} records from {$table}"; // 优化表 Db::execute("OPTIMIZE TABLE `{$table}`"); $result['messages'][] = "Optimized table: {$table}"; } catch (\Throwable $e) { $result['error'] = $e->getMessage(); } return $result; } // 查询方法(示例) public static function queryLogs($userId, $currency, $type = null, $startTime = null, $endTime = null) { $model = new static; $query = $model->setSuffix('_'.strtolower($currency))->where('currency', $currency) ->where('user_id', intval($userId)) ->order('created_at', 'desc'); if ($type) { if($type == '99999'){ $query->whereIn('type', [ \app\enum\BalanceType::OUTPUT_REWARD->value, \app\enum\BalanceType::WITHDRAW_REWARD->value, \app\enum\BalanceType::PRODUCT_INCOME->value, \app\enum\BalanceType::AGENT_COMMISSION->value, \app\enum\BalanceType::DIFFERENTIAL_COMMISSION->value ]); }else{ $query->where('type', intval($type)); } } if ($startTime) { // 支持传入时间戳或日期字符串 //$startTimestamp = is_numeric($startTime) ? intval($startTime) : strtotime($startTime); $query->where('created_at', '>=', $startTime); } if ($endTime) { // 支持传入时间戳或日期字符串 //$endTimestamp = is_numeric($endTime) ? intval($endTime) : strtotime($endTime); $query->where('created_at', '<=', $endTime); } $limit = 10; if(request()){ $limit = input('limit',10); } return $query->paginate($limit); } // 创建表结构 protected static function createTableStructure(string $table): bool { if (self::tableExists($table)) { return false; } $columns = []; foreach (self::TABLE_SCHEMA as $column => $definition) { if (strpos($definition, 'PRIMARY KEY') === false) { $columns[] = "`{$column}` {$definition}"; } } $primaryKey = self::TABLE_SCHEMA['PRIMARY KEY'] ?? 'PRIMARY KEY (`id`)'; $sql = "CREATE TABLE `{$table}` (" . implode(', ', $columns) . ", " . $primaryKey . ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci"; Db::execute($sql); return true; } }