diff --git a/admin_login.php b/admin_login.php index cb54ebb..936ffa6 100644 --- a/admin_login.php +++ b/admin_login.php @@ -18,6 +18,8 @@ if (!secAdminPassConfigured()) { } // ★ 人机验证(2026-10-02):cgWidgetHtml() 渲染挂件,未配密钥时自动放行 require_once __DIR__ . '/captcha_guard.php'; +// ★ 数据安全护栏横幅(2026-10-10):库被清空/指错库时,管理员一登录就能看到告警 +require_once __DIR__ . '/db_guard.php'; ?> @@ -63,6 +65,7 @@ require_once __DIR__ . '/captcha_guard.php';

🔐 管理后台

SDF1 插件管理系统
+
diff --git a/api/shop.php b/api/shop.php index 992538a..49b916b 100644 --- a/api/shop.php +++ b/api/shop.php @@ -205,8 +205,9 @@ function shopBuy($token) { // 更新库存(同时写admin_stock确保Java同步) if ($item['stock'] > 0) { $newStock = $item['stock'] - $amount; - $stmt = $db->prepare("UPDATE shop_items SET stock = :ns, admin_stock = :ns, hourly_sales = hourly_sales + :amount, total_sales = total_sales + :amount WHERE id = :id"); + $stmt = $db->prepare("UPDATE shop_items SET stock = :ns, admin_stock = :ns, last_modified = :t, hourly_sales = hourly_sales + :amount, total_sales = total_sales + :amount WHERE id = :id"); $stmt->bindValue(':ns', $newStock, SQLITE3_INTEGER); + $stmt->bindValue(':t', time(), SQLITE3_INTEGER); $stmt->bindValue(':amount', $amount, SQLITE3_INTEGER); $stmt->bindValue(':id', $itemId, SQLITE3_TEXT); $stmt->execute(); @@ -543,8 +544,9 @@ function shopBuyCart($token) { $item = $p['item']; if ($item['stock'] > 0) { $newStock = $item['stock'] - $p['amount']; - $stmt = $db->prepare("UPDATE shop_items SET stock = :ns, admin_stock = :ns, hourly_sales = hourly_sales + :amount, total_sales = total_sales + :amount WHERE id = :id"); + $stmt = $db->prepare("UPDATE shop_items SET stock = :ns, admin_stock = :ns, last_modified = :t, hourly_sales = hourly_sales + :amount, total_sales = total_sales + :amount WHERE id = :id"); $stmt->bindValue(':ns', $newStock, SQLITE3_INTEGER); + $stmt->bindValue(':t', time(), SQLITE3_INTEGER); $stmt->bindValue(':amount', $p['amount'], SQLITE3_INTEGER); $stmt->bindValue(':id', $p['item_id'], SQLITE3_TEXT); $stmt->execute(); diff --git a/api/sync.php b/api/sync.php index 38b44aa..d70362e 100644 --- a/api/sync.php +++ b/api/sync.php @@ -101,6 +101,24 @@ function checkPlayerRegistered() { $action = getParam('action', ''); +// ★ 数据安全护栏(2026-10-10):任一侧处于块状态 → 拒绝一切数据库操作与握手(含对账), +// 给 Java 明确失败回执;等管理员修好 sql.md 数据库配置后自动恢复放行。 +if (function_exists('dfGuardBlockedSides')) { + $dfBlocked = dfGuardBlockedSides(); + if ($dfBlocked) { + while (ob_get_level() > 0) ob_end_clean(); + header('Content-Type: application/json; charset=utf-8'); + http_response_code(503); + echo json_encode(array( + 'success' => false, + 'guard' => 'blocked', + 'message' => '数据库安全护栏:' . implode(';', $dfBlocked) + . ' 已暂停对该数据库的全部操作并拒绝握手,请管理员检查 sql.md 数据库配置段。', + ), JSON_UNESCAPED_UNICODE); + exit; + } +} + try { switch ($action) { case 'sync_shop': @@ -207,6 +225,9 @@ switch ($action) { case 'pull_shop_stock': pullShopStock(); break; + case 'push_shop_stock': + pushShopStock(); + break; case 'clear_admin_stock': clearAdminStock(); break; @@ -3829,6 +3850,42 @@ function pullShopStock() { success(['items' => $items, 'count' => count($items)]); } +// ===== 插件推送本地库存改动(双向同步:数据分叉以 Java 为主)===== +function pushShopStock() { + $secret = getParam('secret'); + if (!$secret || $secret !== SECRET_KEY) error('认证失败'); + + $db = getDB(); + $db->exec("CREATE TABLE IF NOT EXISTS shop_items ( + id TEXT PRIMARY KEY, category TEXT NOT NULL, display_name TEXT NOT NULL, + material TEXT NOT NULL, buy_price INTEGER DEFAULT 0, sell_price INTEGER DEFAULT -1, + stock INTEGER DEFAULT -1, hourly_sales INTEGER DEFAULT 0, total_sales INTEGER DEFAULT 0, + last_sync INTEGER DEFAULT 0, admin_stock INTEGER DEFAULT NULL, + admin_buy_price INTEGER DEFAULT NULL, admin_sell_price INTEGER DEFAULT NULL + )"); + + $items = getParam('items'); + if (!$items || !is_array($items)) error('缺少库存数据'); + + $time = time(); + $applied = 0; + $kept = 0; + // Java 主:直接写 stock 并清 admin_stock(Java 值即最新权威)。 + // 竞态护栏:PHP 侧该行若有与本次推送同秒更新的未拉取改动(web 购买/后台改库存), + // 保留不覆盖,等 Java 下一轮 pull 时按「哪边有改动对齐哪边」处理。 + $stmt = $db->prepare("UPDATE shop_items SET stock = :s, admin_stock = NULL, last_modified = :t + WHERE id = :id AND (admin_stock IS NULL OR last_modified <= :t)"); + foreach ($items as $it) { + if (!is_array($it) || !isset($it['id'])) continue; + $stmt->bindValue(':s', (int)$it['stock'], SQLITE3_INTEGER); + $stmt->bindValue(':t', $time, SQLITE3_INTEGER); + $stmt->bindValue(':id', (string)$it['id'], SQLITE3_TEXT); + $stmt->execute(); + if ($db->changes() > 0) { $applied++; } else { $kept++; } + } + success(['count' => count($items), 'applied' => $applied, 'kept' => $kept], '库存已接收'); +} + // ===== 插件确认已应用管理员库存改动 ===== function clearAdminStock() { $secret = getParam('secret'); diff --git a/core.php b/core.php index a9b17f4..6468553 100644 --- a/core.php +++ b/core.php @@ -3,6 +3,8 @@ require_once __DIR__ . '/config.php'; // ★ 紧急安全加固(2026-09-29):TOTP 二次验证 + IP 白名单 + 会话纪元(见 security.php) require_once __DIR__ . '/security.php'; +// ★ 数据安全护栏 / 灾后自愈(2026-10-10):dbOpen 连接后体检,块模式拒绝操作 +require_once __DIR__ . '/db_guard.php'; // ★ 确保任何前置输出(包括 BOM、空格、Warning)都被丢弃 if (ob_get_level() > 0) { @@ -88,6 +90,16 @@ if (!function_exists('dbExtractTables')) { $sql = (string)$sql; $out = array(); + // ⓪ upsert 尾段预抹(2026-10-10 灾备演练暴露): + // INSERT ... ON CONFLICT(...) DO UPDATE SET 列=...(SQLite 方言) + // INSERT ... ON DUPLICATE KEY UPDATE 列=...(MySQL 方言) + // 尾段里只有列名,会被 ③ 的 UPDATE 正则误提成表名 → dbPickSide 因混进 + // 非 Java 表而整句判 web → java 组表被送去 caoyuanphp → #1146。 + // router 判侧发生在方言翻译之前、拿的是 SQLite 原句,故两种形态都要抹; + // 只抹到分号/语句尾,绝不开跨界(单次 exec/prepare 本就是单语句)。 + $sql = preg_replace('/\bON\s+CONFLICT\b[^;]*/is', ' ', $sql); + $sql = preg_replace('/\bON\s+DUPLICATE\s+KEY\s+UPDATE\b[^;]*/is', ' ', $sql); + // ① CREATE [UNIQUE] INDEX [IF NOT EXISTS] <索引名> ON <表名>(...):只取 ON 之后的表名 $patIdx = '/\bCREATE\s+(?:UNIQUE\s+)?INDEX(?:\s+IF\s+NOT\s+EXISTS)?\s+[`"\']?[\w$]+[`"\']?\s+ON\s+[`"\']?([\w$]+)/i'; if (preg_match_all($patIdx, $sql, $mi)) { @@ -1452,21 +1464,42 @@ function dbOpen($side) { $mcfg = sqlMdDbConfig(); if (isset($mcfg['mode']) && $mcfg['mode'] === 'mysql' && isset($mcfg[$side]) && is_array($mcfg[$side])) { + // ★ 数据安全护栏(2026-10-10):先连上、再过体检,最后才干活 + $dfVerdict = 'skip'; // skip = 连接都没建成(走 SQLite 兜底,与改造前一致) + $mdb = null; try { $mdb = new SdfMySqlDb($mcfg[$side], $side); $mdb->enableExceptions(true); $mdb->busyTimeout(8000); try { $mdb->exec('ROLLBACK'); } catch (\Throwable $_) {} // 清上一请求残留事务 + // 体检放在 initTables 之前:指错库时绝不往里建表/迁移 + try { $dfVerdict = dfGuardCheck($mdb, $mcfg[$side], $side); } catch (\Throwable $e) { + debugLog('dbOpen[' . $side . ']: 数据护栏体检异常(按 ok 放行): ' . $e->getMessage()); + $dfVerdict = 'ok'; + } + } catch (\Throwable $e) { + debugLog('dbOpen[' . $side . ']: MySQL 连接失败,回退 SQLite: ' . $e->getMessage()); + } + if ($mdb !== null && $dfVerdict !== 'skip') { + if ($dfVerdict === 'block') { + // 数据安全优先:sql.md 指错库/指错机 → 硬失败,绝不软回退 SQLite + // (软回退等于把数据写进另一个库,比停机更危险) + throw new dfGuardBlockException(dfGuardBlockReason($side)); + } try { initTables($mdb, $side); } catch (\Throwable $e) { debugLog('dbOpen[' . $side . ']: initTables失败: ' . $e->getMessage()); } try { migrateDatabase($mdb, $side); } catch (\Throwable $e) { debugLog('dbOpen[' . $side . ']: migrateDatabase失败: ' . $e->getMessage()); } + if ($dfVerdict === 'recover') { + // 被清空的自愈:补表结构 + 空表从最近备份补推 SQL 数据,然后放行 Java 握手 + try { dfGuardRecover($mdb, $side); } catch (\Throwable $e) { + debugLog('dbOpen[' . $side . ']: 自愈执行失败: ' . $e->getMessage()); + } + } $cache[$side] = $mdb; return $mdb; - } catch (\Throwable $e) { - debugLog('dbOpen[' . $side . ']: MySQL 连接失败,回退 SQLite: ' . $e->getMessage()); } } diff --git a/db_guard.php b/db_guard.php new file mode 100644 index 0000000..c473313 --- /dev/null +++ b/db_guard.php @@ -0,0 +1,398 @@ + $s) { + if (is_array($s) && isset($s['mode']) && $s['mode'] === 'block' && !empty($s['alert'])) { + $out[] = $side . '库:' . $s['alert']; + } + } + return $out; +} + +/** 某侧的块原因(dbOpen 抛异常用) */ +function dfGuardBlockReason($side) +{ + $st = dfGuardReadState(); + if (isset($st[$side]['alert']) && $st[$side]['alert']) return (string)$st[$side]['alert']; + return '数据库状态异常(身份与上次运行不一致),已停止操作并拒绝握手,请检查 sql.md 数据库配置段。'; +} + +/** + * 纯判定函数(可单测):给定上次体检快照与本次观测,返回 mode。 + * @param array|null $prev 上次快照:db/host/port/tables/rows/core/ts + * @param array $now 本次观测:db/host/port/tables(array)/rows(int)/core(array 表=>行数) + * @return array array('mode' => 'ok'|'recover'|'block', 'why' => string) + */ +function dfGuardDecide($prev, $now) +{ + $db = isset($now['db']) ? (string)$now['db'] : ''; + $host = isset($now['host']) ? (string)$now['host'] : ''; + $port = isset($now['port']) ? (int)$now['port'] : 0; + + // 没上次快照 → 首次见,先记账不报警(部署/状态文件被删后的正常冷启动) + if (!is_array($prev) || empty($prev)) { + return array('mode' => 'ok', 'why' => 'bootstrap'); + } + $pdb = isset($prev['db']) ? (string)$prev['db'] : ''; + $phost = isset($prev['host']) ? (string)$prev['host'] : ''; + $pport = isset($prev['port']) ? (int)$prev['port'] : 0; + + // 身份变化(库名/主机/端口)→ 指错了地方:块,绝不往里写 + if ($pdb !== $db || $phost !== $host || $pport !== $port) { + return array('mode' => 'block', 'why' => 'identity_mismatch'); + } + + $core = dfGuardCoreTables($pdb === 'caoyuanphp' || strpos($pdb, 'php') !== false ? 'web' : 'java'); + // 用 prev 记忆的侧别更稳;这里退化用库名猜,调用方会以 $side 覆盖 core 集合 + $tables = isset($now['tables']) ? $now['tables'] : array(); + $coreNow = isset($now['core']) && is_array($now['core']) ? $now['core'] : array(); + $rows = isset($now['rows']) ? (int)$now['rows'] : -1; + $prevRows = isset($prev['rows']) ? (int)$prev['rows'] : 0; + + // 核心表一张不剩 → 库被连根清空/删库:走 recover(重建+补推+等 Java) + $haveCore = 0; + foreach ($core as $t) { if (in_array($t, $tables, true)) $haveCore++; } + if ($haveCore === 0 && !empty($prev['tables'])) { + return array('mode' => 'recover', 'why' => 'tables_wiped'); + } + + // 表都在但整体行数塌了(<=2% 或绝对空)→ 被清空:recover + if ($rows >= 0 && $prevRows > 0) { + $floor = max(50, (int)($prevRows * 0.02)); + if ($rows <= $floor) { + return array('mode' => 'recover', 'why' => 'rows_wiped'); + } + // 核心表精确计数佐证(TABLE_ROWS 是估计值,避免误判) + $zeroCore = 0; + $checkedCore = 0; + foreach ($coreNow as $t => $c) { + $checkedCore++; + if ((int)$c <= 0) $zeroCore++; + } + if ($checkedCore > 0 && $zeroCore === $checkedCore) { + return array('mode' => 'recover', 'why' => 'core_zeroed'); + } + } + + return array('mode' => 'ok', 'why' => 'healthy'); +} + +/** + * 连接后体检(在 initTables 之前调用,块模式直接抛,避免往可疑库里建表)。 + * @return string 'ok'|'recover'|'block' + */ +function dfGuardCheck($mdb, $conn, $side) +{ + $st = dfGuardReadState(); + $prev = isset($st[$side]) && is_array($st[$side]) ? $st[$side] : null; + $now = time(); + + // 节流:刚体检过 → 直接沿用缓存判定(块/恢复态也一样缓存,行为一致) + if (is_array($prev) && isset($prev['ts']) && ($now - (int)$prev['ts']) < DF_GUARD_TTL + && in_array(isset($prev['mode']) ? $prev['mode'] : '', array('ok', 'recover', 'block'), true)) { + return $prev['mode']; + } + + $dbName = isset($conn['name']) ? (string)$conn['name'] : ''; + $host = isset($conn['host']) ? (string)$conn['host'] : ''; + $port = isset($conn['port']) ? (int)$conn['port'] : 3306; + + // ---- 完整体检 ---- + $tables = array(); + $rs = $mdb->query("SELECT table_name FROM information_schema.tables WHERE table_schema='" + . $mdb->escapeString($dbName) . "'"); + if ($rs) { + while ($row = $rs->fetch_assoc()) $tables[] = (string)$row['table_name']; + } + $estRows = 0; + $rs = $mdb->query("SELECT SUM(TABLE_ROWS) s FROM information_schema.tables WHERE table_schema='" + . $mdb->escapeString($dbName) . "'"); + if ($rs && ($row = $rs->fetch_assoc())) $estRows = (int)$row['s']; + + $core = dfGuardCoreTables($side); + $coreNow = array(); + foreach ($core as $t) { + if (!in_array($t, $tables, true)) continue; + $v = dfGuardScalar($mdb, "SELECT COUNT(*) FROM `" . $t . "`"); + if ($v !== null) $coreNow[$t] = (int)$v; + } + + $nowInfo = array( + 'db' => $dbName, 'host' => $host, 'port' => $port, + 'tables' => $tables, 'rows' => $estRows, 'core' => $coreNow, + ); + $decide = dfGuardDecide($prev, $nowInfo, $side); + $mode = $decide['mode']; + + $entry = array( + 'db' => $dbName, 'host' => $host, 'port' => $port, + 'tables' => count($tables), 'rows' => $estRows, + 'core' => $coreNow, 'ts' => $now, 'mode' => $mode, + ); + if ($mode === 'block') { + $entry['alert'] = 'sql.md 里这一侧指向的数据库与上次运行不一致(上次 ' + . (is_array($prev) ? ($prev['host'] . ':' . $prev['port'] . '/' . $prev['db']) : '?') + . ' → 现在 ' . $host . ':' . $port . '/' . $dbName . ')。' + . '为避免写错库破坏数据,已停止对该库的一切操作并拒绝与 Java 握手。' + . '请登录服务器核对 sql.md 的数据库配置段(地址/端口/库名/账号),改对后本护栏会自动恢复。'; + } elseif ($mode === 'recover') { + $entry['alert'] = '检测到该库被清空(' . $decide['why'] . '),已执行自愈:补表结构并尝试从最近备份补推 SQL 数据,' + . '随后放行 Java 握手补推其余数据。PHP 侧自有表(工单/订单/流水等)只能从备份恢复,请检查 debug.log 的补推明细。'; + } else { + $entry['alert'] = null; + } + + $st[$side] = $entry; + dfGuardWriteState($st); + + if ($mode === 'block') { + dfGuardLog('dbGuard[' . $side . '] ★ 块模式:' . $entry['alert']); + } elseif ($mode === 'recover') { + dfGuardLog('dbGuard[' . $side . '] ★ 恢复模式:' . $decide['why'] . ',行数 ' + . $estRows . '(上次 ' . (is_array($prev) ? (int)$prev['rows'] : 0) . ')'); + } + return $mode; +} + +function dfGuardScalar($mdb, $sql) +{ + try { + $rs = $mdb->query($sql); + if ($rs && ($row = $rs->fetch_assoc())) { + foreach ($row as $v) return $v; + } + } catch (\Throwable $e) { return null; } + return null; +} + +function dfGuardLog($msg) +{ + if (function_exists('debugLog')) { debugLog($msg); return; } + @error_log($msg); +} + +/** 恢复模式动作:补表结构(db_schema_mysql.sql)+ 把仍为空的表从最近可读备份补推数据 */ +function dfGuardRecover($mdb, $side) +{ + $done = array('schema' => 0, 'restored' => array(), 'skipped' => array(), 'err' => array()); + + // 1) 缺的表用内置 schema 补(initTables 通常已经补过,这里是双保险) + $schemaFile = __DIR__ . '/db_schema_mysql.sql'; + if (is_file($schemaFile) && is_readable($schemaFile)) { + $done['schema'] = dfGuardApplySchema($mdb, $side, $schemaFile); + } + + // 2) 仍为空的表 → 从最近一次可读备份补推(只补 0 行表,绝不覆盖有数据的表) + try { + $dump = dfGuardPickDump($side); + if ($dump === null) { + $done['skipped'][] = 'no_readable_dump'; + } else { + $done['restored'] = dfGuardRestoreEmptyTables($mdb, $dump); + } + } catch (\Throwable $e) { + $done['err'][] = $e->getMessage(); + } + + dfGuardLog('dbGuard[' . $side . '] 自愈执行明细: ' . json_encode($done, JSON_UNESCAPED_UNICODE)); + return $done; +} + +/** 从 db_schema_mysql.sql 里把本侧缺失的表建出来(CREATE TABLE IF NOT EXISTS) */ +function dfGuardApplySchema($mdb, $side, $schemaFile) +{ + $javaTables = function_exists('dbJavaTables') ? dbJavaTables() : array(); + $sql = (string)@file_get_contents($schemaFile); + if ($sql === '') return 0; + // 只认 CREATE TABLE 段:去掉 DROP/LOCK/UNLOCK(恢复期绝不能删表) + $lines = preg_split('/\r\n|\r|\n/', $sql); + $buf = ''; + $statements = array(); + foreach ($lines as $ln) { + $t = trim($ln); + if ($t === '' || $t[0] === '#' || strpos($t, '--') === 0) continue; + if (preg_match('/^(DROP|LOCK|UNLOCK)\b/i', $t)) continue; + $buf .= $ln . "\n"; + if (substr(rtrim($t), -1) === ';') { + $statements[] = $buf; + $buf = ''; + } + } + if (trim($buf) !== '') $statements[] = $buf; + + $created = 0; + foreach ($statements as $s) { + if (!preg_match('/^CREATE TABLE\s+(?:IF NOT EXISTS\s+)?`?([A-Za-z0-9_]+)`?/i', ltrim($s), $m)) continue; + $tbl = $m[1]; + // 归属过滤:java 组表只准建到 java 侧,其余建到 web 侧 + if ($side === 'java') { + if (!in_array($tbl, $javaTables, true)) continue; + } else { + if (in_array($tbl, $javaTables, true)) continue; + } + // 表已存在就跳过 + $v = dfGuardScalar($mdb, "SELECT COUNT(*) c FROM information_schema.tables WHERE table_schema=DATABASE() AND table_name='" + . $mdb->escapeString($tbl) . "'"); + if ($v !== null && (int)$v > 0) continue; + try { + $stmt = preg_replace('/^CREATE TABLE\s+(?!IF NOT EXISTS)/i', 'CREATE TABLE IF NOT EXISTS ', ltrim($s), 1); + $mdb->exec($stmt); + $created++; + } catch (\Throwable $e) { + dfGuardLog('dbGuard: 建表失败 ' . $tbl . ': ' . $e->getMessage()); + } + } + return $created; +} + +/** 挑一份最近的可读备份(库名出现在文件名里、年龄合规、按 mtime 新优先) */ +function dfGuardPickDump($side) +{ + $st = dfGuardReadState(); + $dbName = isset($st[$side]['db']) ? (string)$st[$side]['db'] : ''; + if ($dbName === '') return null; + $cands = array_merge( + glob('/www/backup/dr*/*' . $dbName . '*.sql') ?: array(), + glob('/www/backup/*' . $dbName . '*.sql') ?: array(), + glob(__DIR__ . '/../backup/*' . $dbName . '*.sql') ?: array() + ); + $best = null; + $bestMt = 0; + $now = time(); + foreach ($cands as $f) { + if (!is_file($f) || !is_readable($f)) continue; + $mt = @filemtime($f); + if (!$mt || ($now - $mt) > DF_GUARD_BACKUP_MAX_AGE) continue; + if ($mt > $bestMt) { $bestMt = $mt; $best = $f; } + } + return $best; +} + +/** + * 备份补推:只执行「目标表当前 0 行」的 INSERT / 缺失表的 CREATE,其余语句一律跳过。 + * 返回 array('表名' => 补推行数)。 + */ +function dfGuardRestoreEmptyTables($mdb, $dumpFile) +{ + $out = array(); + $fh = @fopen($dumpFile, 'r'); + if (!$fh) return $out; + $buf = ''; + $executed = 0; + while (!feof($fh)) { + $line = fgets($fh); + if ($line === false) break; + $buf .= $line; + if (rtrim($line) === '' || substr(rtrim($line), -1) !== ';') continue; + $stmt = trim($buf); + $buf = ''; + if ($stmt === '') continue; + if ($executed >= DF_GUARD_MAX_STMTS) break; + if (preg_match('/^INSERT INTO\s+`?([A-Za-z0-9_]+)`?/i', $stmt, $m)) { + $tbl = $m[1]; + $v = dfGuardScalar($mdb, "SELECT COUNT(*) c FROM `" . $tbl . "`"); + if ($v === null) continue; // 表不存在:跳过(不该由备份建表) + if ((int)$v > 0) continue; // 有数据的表绝不覆盖 + try { + $mdb->exec($stmt); + $n = 1; + $out[$tbl] = isset($out[$tbl]) ? $out[$tbl] + $n : $n; + $executed++; + } catch (\Throwable $e) { + dfGuardLog('dbGuard: 补推 ' . $tbl . ' 失败: ' . $e->getMessage()); + } + } elseif (preg_match('/^CREATE TABLE\s+(?:IF NOT EXISTS\s+)?`?([A-Za-z0-9_]+)`?/i', $stmt, $m)) { + $tbl = $m[1]; + $v = dfGuardScalar($mdb, "SELECT COUNT(*) c FROM information_schema.tables WHERE table_schema=DATABASE() AND table_name='" + . $mdb->escapeString($tbl) . "'"); + if ($v !== null && (int)$v > 0) continue; + try { + $stmt2 = preg_replace('/^CREATE TABLE\s+(?!IF NOT EXISTS)/i', 'CREATE TABLE IF NOT EXISTS ', $stmt, 1); + $mdb->exec($stmt2); + $executed++; + } catch (\Throwable $e) { + dfGuardLog('dbGuard: 恢复期建表 ' . $tbl . ' 失败: ' . $e->getMessage()); + } + } + } + fclose($fh); + return $out; +} + +/** + * 管理登录页横幅:有未消除的护栏告警时就亮出来(块/恢复态都会显示)。 + * 告警是派生状态:数据库恢复健康后下一次体检自动转 ok,横幅随之消失。 + */ +function dfGuardAdminBanner() +{ + $st = dfGuardReadState(); + $msgs = array(); + foreach ($st as $side => $s) { + if (is_array($s) && !empty($s['alert']) && in_array($s['mode'], array('block', 'recover'), true)) { + $label = ($side === 'java') ? 'Java 库(caoyuanjava)' : 'PHP 库(caoyuanphp)'; + $msgs[] = '[' . htmlspecialchars($label) . '] ' . htmlspecialchars((string)$s['alert']) + . ' (' . htmlspecialchars($s['mode'] === 'block' ? '已停止操作并拒绝握手' : '自愈进行中') . ')'; + } + } + if (!$msgs) return ''; + $css = ''; + return $css . '
⚠ 数据库安全护栏告警:
' . implode('
', $msgs) . '
'; +} diff --git a/db_schema_mysql.sql b/db_schema_mysql.sql new file mode 100644 index 0000000..3014a21 --- /dev/null +++ b/db_schema_mysql.sql @@ -0,0 +1,845 @@ +-- SDF1 双库表结构快照(灾备自愈用,由生产 SHOW CREATE TABLE 生成) +-- 用法:db_guard.php 恢复模式据此补建缺失表(CREATE TABLE IF NOT EXISTS) + +-- ================= aoyuanjava ================= + +-- aoyuanjava.ond_cache +CREATE TABLE IF NOT EXISTS `bond_cache` ( + `player_name` varchar(500) COLLATE utf8mb4_bin NOT NULL, + `amount` bigint(20) DEFAULT '0', + `updated_at` bigint(20) DEFAULT '0', + PRIMARY KEY (`player_name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanjava.dk +CREATE TABLE IF NOT EXISTS `cdk` ( + `code` varchar(500) COLLATE utf8mb4_bin NOT NULL, + `amount` bigint(20) DEFAULT '0', + `type` varchar(500) COLLATE utf8mb4_bin DEFAULT 'bond', + `used` bigint(20) DEFAULT '0', + `used_by` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `created_at` bigint(20) DEFAULT '0', + `used_at` bigint(20) DEFAULT '0', + PRIMARY KEY (`code`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanjava.dk_validate_requests +CREATE TABLE IF NOT EXISTS `cdk_validate_requests` ( + `request_id` varchar(500) COLLATE utf8mb4_bin NOT NULL, + `code` text COLLATE utf8mb4_bin NOT NULL, + `player_name` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `status` varchar(500) COLLATE utf8mb4_bin DEFAULT 'pending', + `created_at` bigint(20) NOT NULL, + PRIMARY KEY (`request_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanjava.dk_validate_results +CREATE TABLE IF NOT EXISTS `cdk_validate_results` ( + `request_id` varchar(500) COLLATE utf8mb4_bin NOT NULL, + `code` text COLLATE utf8mb4_bin NOT NULL, + `status` text COLLATE utf8mb4_bin NOT NULL, + `amount` bigint(20) DEFAULT '0', + `message` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `used` bigint(20) DEFAULT '0', + `used_by` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `created_at` bigint(20) NOT NULL, + PRIMARY KEY (`request_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanjava.onfirmed_transactions +CREATE TABLE IF NOT EXISTS `confirmed_transactions` ( + `tx_id` varchar(325) COLLATE utf8mb4_bin NOT NULL, + `tx_type` varchar(325) COLLATE utf8mb4_bin NOT NULL, + `confirmed_at` bigint(20) NOT NULL, + PRIMARY KEY (`tx_id`,`tx_type`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanjava.mail_codes +CREATE TABLE IF NOT EXISTS `email_codes` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `player_name` text COLLATE utf8mb4_bin NOT NULL, + `email` text COLLATE utf8mb4_bin NOT NULL, + `code` text COLLATE utf8mb4_bin NOT NULL, + `expire_at` bigint(20) NOT NULL, + `used` bigint(20) DEFAULT '0', + `created_at` bigint(20) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanjava.ame_transactions +CREATE TABLE IF NOT EXISTS `game_transactions` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `java_id` bigint(20) NOT NULL, + `player_name` text COLLATE utf8mb4_bin NOT NULL, + `type` text COLLATE utf8mb4_bin NOT NULL, + `amount` bigint(20) NOT NULL, + `target_player` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `operator` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `reason` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `balance_before` bigint(20) DEFAULT '0', + `balance_after` bigint(20) DEFAULT '0', + `tx_time` bigint(20) NOT NULL, + `synced_at` bigint(20) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `java_id` (`java_id`), + KEY `idx_gt_player` (`player_name`(191),`tx_time`), + KEY `idx_gt_type` (`type`(191)) +) ENGINE=InnoDB AUTO_INCREMENT=61640 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanjava.ourly_active_players +CREATE TABLE IF NOT EXISTS `hourly_active_players` ( + `player_name` varchar(500) COLLATE utf8mb4_bin NOT NULL, + `first_seen` bigint(20) NOT NULL, + `last_seen` bigint(20) NOT NULL, + `hour_key` text COLLATE utf8mb4_bin NOT NULL, + PRIMARY KEY (`player_name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanjava.nline_player_hb +CREATE TABLE IF NOT EXISTS `online_player_hb` ( + `id` bigint(20) NOT NULL DEFAULT '1', + `last_seen` bigint(20) DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanjava.nline_players +CREATE TABLE IF NOT EXISTS `online_players` ( + `player_name` varchar(500) COLLATE utf8mb4_bin NOT NULL, + `login_time` bigint(20) DEFAULT '0', + PRIMARY KEY (`player_name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanjava.assword_reset_requests +CREATE TABLE IF NOT EXISTS `password_reset_requests` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `player_name` text COLLATE utf8mb4_bin NOT NULL, + `requested_email` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `status` varchar(500) COLLATE utf8mb4_bin DEFAULT 'pending', + `admin_approved` bigint(20) DEFAULT '0', + `admin_email` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `created_at` bigint(20) NOT NULL, + `processed_at` bigint(20) DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanjava.assword_reset_tokens +CREATE TABLE IF NOT EXISTS `password_reset_tokens` ( + `token` varchar(500) COLLATE utf8mb4_bin NOT NULL, + `player_name` text COLLATE utf8mb4_bin NOT NULL, + `expire_at` bigint(20) NOT NULL, + PRIMARY KEY (`token`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanjava.layer_checkins +CREATE TABLE IF NOT EXISTS `player_checkins` ( + `player_name` varchar(325) COLLATE utf8mb4_bin NOT NULL, + `checkin_date` varchar(325) COLLATE utf8mb4_bin NOT NULL, + `checkin_time` bigint(20) DEFAULT '0', + PRIMARY KEY (`player_name`,`checkin_date`), + KEY `idx_player_checkins_player` (`player_name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanjava.layer_daily_logins +CREATE TABLE IF NOT EXISTS `player_daily_logins` ( + `player_name` varchar(325) COLLATE utf8mb4_bin NOT NULL, + `login_date` varchar(325) COLLATE utf8mb4_bin NOT NULL, + `first_login` bigint(20) DEFAULT '0', + `last_login` bigint(20) DEFAULT '0', + PRIMARY KEY (`player_name`,`login_date`), + KEY `idx_player_daily_logins_player` (`player_name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanjava.layer_ip_changes +CREATE TABLE IF NOT EXISTS `player_ip_changes` ( + `player_name` varchar(500) COLLATE utf8mb4_bin NOT NULL, + `old_ip` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `new_ip` text COLLATE utf8mb4_bin NOT NULL, + `changed_at` bigint(20) NOT NULL, + `synced_at` bigint(20) DEFAULT '0', + PRIMARY KEY (`player_name`), + KEY `idx_player_ip_changes_player` (`player_name`), + KEY `idx_player_ip_changes_time` (`changed_at`), + KEY `idx_player_ip_changes_newip` (`new_ip`(191)) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanjava.layer_ip_locations +CREATE TABLE IF NOT EXISTS `player_ip_locations` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `ip_address` varchar(500) COLLATE utf8mb4_bin NOT NULL, + `location` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `updated_at` bigint(20) DEFAULT '0', + `player_name` varchar(500) COLLATE utf8mb4_bin DEFAULT 'global', + PRIMARY KEY (`id`), + UNIQUE KEY `uq_player_ip_locations_975887f3` (`ip_address`(191),`player_name`(191)), + KEY `idx_player_ip_locations_ip` (`ip_address`), + KEY `idx_player_ip_locations_loc` (`location`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanjava.hop_config +CREATE TABLE IF NOT EXISTS `shop_config` ( + `cfg_key` varchar(500) COLLATE utf8mb4_bin NOT NULL, + `cfg_value` text COLLATE utf8mb4_bin NOT NULL, + PRIMARY KEY (`cfg_key`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanjava.hop_items +CREATE TABLE IF NOT EXISTS `shop_items` ( + `id` varchar(500) COLLATE utf8mb4_bin NOT NULL, + `category` text COLLATE utf8mb4_bin NOT NULL, + `display_name` text COLLATE utf8mb4_bin NOT NULL, + `material` text COLLATE utf8mb4_bin NOT NULL, + `buy_price` bigint(20) DEFAULT '0', + `sell_price` bigint(20) DEFAULT '-1', + `stock` bigint(20) DEFAULT '-1', + `hourly_sales` bigint(20) DEFAULT '0', + `total_sales` bigint(20) DEFAULT '0', + `last_sync` bigint(20) DEFAULT '0', + `admin_stock` bigint(20) DEFAULT NULL, + `admin_buy_price` bigint(20) DEFAULT NULL, + `admin_sell_price` bigint(20) DEFAULT NULL, + `last_modified` bigint(20) DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanjava.ync_requests +CREATE TABLE IF NOT EXISTS `sync_requests` ( + `player_name` varchar(500) COLLATE utf8mb4_bin NOT NULL, + `created_at` bigint(20) NOT NULL, + PRIMARY KEY (`player_name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanjava.okens +CREATE TABLE IF NOT EXISTS `tokens` ( + `token` varchar(500) COLLATE utf8mb4_bin NOT NULL, + `player_name` text COLLATE utf8mb4_bin NOT NULL, + `purpose` varchar(500) COLLATE utf8mb4_bin DEFAULT 'general', + `created_at` bigint(20) NOT NULL, + `expires_at` bigint(20) NOT NULL, + `used` bigint(20) DEFAULT '0', + `used_at` bigint(20) DEFAULT '0', + PRIMARY KEY (`token`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanjava.sers +CREATE TABLE IF NOT EXISTS `users` ( + `player_name` varchar(500) COLLATE utf8mb4_bin NOT NULL, + `register_time` bigint(20) DEFAULT '0', + `last_login_time` bigint(20) DEFAULT '0', + `email` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `points` bigint(20) DEFAULT '0', + `gift_stage` bigint(20) DEFAULT '0', + `total_online_time` bigint(20) DEFAULT '0', + `last_login_location` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `last_login_ip` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `frozen` bigint(20) DEFAULT '0', + `freeze_token` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + PRIMARY KEY (`player_name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanjava.eb_admin_changes +CREATE TABLE IF NOT EXISTS `web_admin_changes` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `change_type` text COLLATE utf8mb4_bin NOT NULL, + `target_id` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `target_name` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `change_data` varchar(500) COLLATE utf8mb4_bin DEFAULT '{}', + `created_at` bigint(20) DEFAULT '0', + `acknowledged` bigint(20) DEFAULT '0', + `acked_at` bigint(20) DEFAULT '0', + `status` varchar(500) COLLATE utf8mb4_bin DEFAULT 'pending', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanjava.eb_area_config +CREATE TABLE IF NOT EXISTS `web_area_config` ( + `key` varchar(500) COLLATE utf8mb4_bin NOT NULL, + `value` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + PRIMARY KEY (`key`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanjava.eb_area_lands +CREATE TABLE IF NOT EXISTS `web_area_lands` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `name` varchar(500) COLLATE utf8mb4_bin NOT NULL, + `owner` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `world` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `x1` bigint(20) DEFAULT '0', + `z1` bigint(20) DEFAULT '0', + `x2` bigint(20) DEFAULT '0', + `z2` bigint(20) DEFAULT '0', + `y1` bigint(20) DEFAULT '0', + `y2` bigint(20) DEFAULT '0', + `area_size` bigint(20) DEFAULT '0', + `created_at` bigint(20) DEFAULT '0', + `admin_changed` bigint(20) DEFAULT '0', + `deny_pvp` bigint(20) DEFAULT '0', + `deny_fall_damage` bigint(20) DEFAULT '0', + `deny_hunger` bigint(20) DEFAULT '0', + `deny_all_damage` bigint(20) DEFAULT '0', + `deny_fire_spread` bigint(20) DEFAULT '0', + `deny_explosion` bigint(20) DEFAULT '0', + `deny_mob_grief` bigint(20) DEFAULT '0', + `deny_block_place` bigint(20) DEFAULT '0', + `deny_block_break` bigint(20) DEFAULT '0', + `y_min` bigint(20) DEFAULT '0', + `y_max` bigint(20) DEFAULT '255', + `synced_at` bigint(20) DEFAULT '0', + `clear_effects` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `give_effects` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `clear_all_bad_effects` bigint(20) DEFAULT '0', + `deny_all_effects` bigint(20) DEFAULT '0', + `confiscate_items` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `deny_use_items` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `punish_commands` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `deny_drop` bigint(20) DEFAULT '0', + `deny_mount` bigint(20) DEFAULT '0', + `deny_ender_pearl` bigint(20) DEFAULT '0', + `deny_bow` bigint(20) DEFAULT '0', + `deny_potion` bigint(20) DEFAULT '0', + `deny_raid` bigint(20) DEFAULT '0', + `deny_item_frame` bigint(20) DEFAULT '0', + `deny_move` bigint(20) DEFAULT '0', + `deny_pickup` bigint(20) DEFAULT '0', + `deny_fire` bigint(20) DEFAULT '0', + `deny_thrown_projectiles` bigint(20) DEFAULT '0', + `deny_glowing` bigint(20) DEFAULT '0', + `deny_redstone_interaction` bigint(20) DEFAULT '0', + `deny_door_interaction` bigint(20) DEFAULT '0', + `deny_noteblock_jukebox` bigint(20) DEFAULT '0', + `deny_lead` bigint(20) DEFAULT '0', + `deny_crop_harvest` bigint(20) DEFAULT '0', + `deny_wool_shear` bigint(20) DEFAULT '0', + `deny_animal_feeding` bigint(20) DEFAULT '0', + `deny_mob_attack` bigint(20) DEFAULT '0', + `deny_container` bigint(20) DEFAULT '0', + `peace_mode` bigint(20) DEFAULT '0', + `peace_mode_duration` bigint(20) DEFAULT '0', + `peace_whitelist` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `enforce_game_mode` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `mode_exempt` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `enter_msg` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `leave_msg` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `confiscate_msg` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `warp_x` double DEFAULT '0', + `warp_y` double DEFAULT '0', + `warp_z` double DEFAULT '0', + `warp_yaw` double DEFAULT '0', + `warp_pitch` double DEFAULT '0', + `warp_world` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `enable_announce` bigint(20) DEFAULT '0', + `announce_template` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `txt_content` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `deny_fluid` bigint(20) DEFAULT '0', + `clear_all_bad` bigint(20) DEFAULT '0', + `is_public_building` bigint(20) DEFAULT '0', + `deny_entity_interact` bigint(20) DEFAULT '0', + `allow_visitor_teleport` bigint(20) DEFAULT '0', + `deny_sign_edit` bigint(20) DEFAULT '0', + `deny_spawn_egg` bigint(20) DEFAULT '0', + `deny_wax` bigint(20) DEFAULT '0', + `bad_effects` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=7072 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanjava.eb_area_permissions +CREATE TABLE IF NOT EXISTS `web_area_permissions` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `land_id` bigint(20) NOT NULL, + `land_name` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `player_name` varchar(500) COLLATE utf8mb4_bin NOT NULL, + `role` varchar(500) COLLATE utf8mb4_bin NOT NULL DEFAULT 'visitor', + `permissions` varchar(1000) COLLATE utf8mb4_bin DEFAULT '', + `granted_at` bigint(20) DEFAULT '0', + `expires_at` bigint(20) DEFAULT '0', + `synced_at` bigint(20) DEFAULT '0', + PRIMARY KEY (`id`), + UNIQUE KEY `land_id` (`land_id`,`player_name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanjava.eb_area_shop +CREATE TABLE IF NOT EXISTS `web_area_shop` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `land_id` bigint(20) NOT NULL, + `land_name` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `seller` text COLLATE utf8mb4_bin NOT NULL, + `permission` varchar(500) COLLATE utf8mb4_bin DEFAULT 'visitor', + `price` bigint(20) DEFAULT '0', + `duration` bigint(20) DEFAULT '86400', + `status` varchar(500) COLLATE utf8mb4_bin DEFAULT 'active', + `buyer` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `bought_at` bigint(20) DEFAULT '0', + `created_at` bigint(20) DEFAULT '0', + `synced_at` bigint(20) DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanjava.eb_ip_blacklist +CREATE TABLE IF NOT EXISTS `web_ip_blacklist` ( + `ip` varchar(500) COLLATE utf8mb4_bin NOT NULL, + `reason` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `source` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `expire` bigint(20) DEFAULT '0', + `banned_at` bigint(20) DEFAULT '0', + PRIMARY KEY (`ip`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanjava.eb_login_confirmations +CREATE TABLE IF NOT EXISTS `web_login_confirmations` ( + `player_name` varchar(500) COLLATE utf8mb4_bin NOT NULL, + `confirmed_at` bigint(20) NOT NULL, + `consumed` bigint(20) DEFAULT '0', + PRIMARY KEY (`player_name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanjava.eb_login_requests +CREATE TABLE IF NOT EXISTS `web_login_requests` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `player_name` text COLLATE utf8mb4_bin NOT NULL, + `password` text COLLATE utf8mb4_bin NOT NULL, + `request_time` bigint(20) NOT NULL, + `status` varchar(500) COLLATE utf8mb4_bin DEFAULT 'pending', + `result` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `result_time` bigint(20) DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanjava.eb_login_verified +CREATE TABLE IF NOT EXISTS `web_login_verified` ( + `player_name` varchar(500) COLLATE utf8mb4_bin NOT NULL, + `verified_at` bigint(20) NOT NULL, + PRIMARY KEY (`player_name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanjava.eb_player_bans +CREATE TABLE IF NOT EXISTS `web_player_bans` ( + `target` varchar(325) COLLATE utf8mb4_bin NOT NULL, + `ban_type` varchar(325) COLLATE utf8mb4_bin NOT NULL DEFAULT 'name', + `reason` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `source` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `expire_time` bigint(20) DEFAULT '0', + `synced_at` bigint(20) DEFAULT '0', + PRIMARY KEY (`target`,`ban_type`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanjava.eb_plugin_admins +CREATE TABLE IF NOT EXISTS `web_plugin_admins` ( + `player_name` varchar(500) COLLATE utf8mb4_bin NOT NULL, + `synced_at` bigint(20) DEFAULT '0', + PRIMARY KEY (`player_name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanjava.eb_register_requests +CREATE TABLE IF NOT EXISTS `web_register_requests` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `player_name` text COLLATE utf8mb4_bin NOT NULL, + `password_hash` text COLLATE utf8mb4_bin NOT NULL, + `salt` text COLLATE utf8mb4_bin NOT NULL, + `email` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `ip_address` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `created_at` bigint(20) NOT NULL, + `status` varchar(500) COLLATE utf8mb4_bin DEFAULT 'pending', + `processed_at` bigint(20) DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanjava.eb_service_providers +CREATE TABLE IF NOT EXISTS `web_service_providers` ( + `player_name` varchar(500) COLLATE utf8mb4_bin NOT NULL, + `role` varchar(500) COLLATE utf8mb4_bin DEFAULT 'waiter', + `active` bigint(20) DEFAULT '1', + `join_time` bigint(20) DEFAULT '0', + PRIMARY KEY (`player_name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanjava.eb_session_log +CREATE TABLE IF NOT EXISTS `web_session_log` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `player_name` text COLLATE utf8mb4_bin NOT NULL, + `ip_address` text COLLATE utf8mb4_bin NOT NULL, + `login_time` bigint(20) NOT NULL, + `session_token` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + PRIMARY KEY (`id`), + KEY `idx_web_session_log_player` (`player_name`(191)), + KEY `idx_web_session_log_time` (`login_time`) +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanjava.eb_transactions +CREATE TABLE IF NOT EXISTS `web_transactions` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `player_name` text COLLATE utf8mb4_bin NOT NULL, + `type` text COLLATE utf8mb4_bin NOT NULL, + `amount` bigint(20) NOT NULL, + `operator` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `reason` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `detail` varchar(1059) COLLATE utf8mb4_bin DEFAULT '', + `status` varchar(500) COLLATE utf8mb4_bin DEFAULT 'pending', + `created_at` bigint(20) DEFAULT '0', + `processed_at` bigint(20) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=341 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanjava.eb_user_group_members +CREATE TABLE IF NOT EXISTS `web_user_group_members` ( + `player_name` varchar(325) COLLATE utf8mb4_bin NOT NULL, + `group_name` varchar(325) COLLATE utf8mb4_bin NOT NULL, + `added_by` varchar(500) COLLATE utf8mb4_bin DEFAULT 'system', + `added_time` bigint(20) DEFAULT '0', + `expiry_time` bigint(20) DEFAULT '0', + PRIMARY KEY (`player_name`,`group_name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanjava.eb_user_groups +CREATE TABLE IF NOT EXISTS `web_user_groups` ( + `group_name` varchar(500) COLLATE utf8mb4_bin NOT NULL, + `display_name` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `display_color` varchar(500) COLLATE utf8mb4_bin DEFAULT '�f', + `display_emoji` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `priority` bigint(20) DEFAULT '0', + `land_price_per_sqm` bigint(20) DEFAULT '-1', + `max_lands` bigint(20) DEFAULT '-1', + `max_effects` bigint(20) DEFAULT '5', + `max_effect_level` bigint(20) DEFAULT '256', + `home_limit` bigint(20) DEFAULT '0', + `join_price` bigint(20) DEFAULT '0', + `auto_renew` bigint(20) DEFAULT '0', + `renew_price` bigint(20) DEFAULT '0', + `duration_minutes` bigint(20) DEFAULT '0', + `default_perms` varchar(500) COLLATE utf8mb4_bin DEFAULT '{}', + `synced_at` bigint(20) DEFAULT '0', + PRIMARY KEY (`group_name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanjava.eblogin_credentials +CREATE TABLE IF NOT EXISTS `weblogin_credentials` ( + `player_name` varchar(500) COLLATE utf8mb4_bin NOT NULL, + `password_hash` text COLLATE utf8mb4_bin NOT NULL, + `salt` text COLLATE utf8mb4_bin NOT NULL, + `temp_password_hash` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `temp_pw_expire` bigint(20) DEFAULT '0', + `pushed_at` bigint(20) NOT NULL, + PRIMARY KEY (`player_name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanjava.eblogin_tokens +CREATE TABLE IF NOT EXISTS `weblogin_tokens` ( + `player_name` varchar(500) COLLATE utf8mb4_bin NOT NULL, + `web_token` text COLLATE utf8mb4_bin NOT NULL, + `created_at` bigint(20) NOT NULL, + `expire_seconds` bigint(20) DEFAULT '600', + `registered` bigint(20) DEFAULT '1', + PRIMARY KEY (`player_name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- ================= aoyuanphp ================= + +-- aoyuanphp.install_lock +CREATE TABLE IF NOT EXISTS `_install_lock` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `installed_at` varchar(32) NOT NULL, + `ip` varchar(45) NOT NULL DEFAULT '', + `mode` varchar(16) NOT NULL DEFAULT 'mysql', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4; + +-- aoyuanphp.dmin_sessions +CREATE TABLE IF NOT EXISTS `admin_sessions` ( + `session_id` varchar(500) COLLATE utf8mb4_bin NOT NULL, + `created_at` bigint(20) NOT NULL, + `expires_at` bigint(20) NOT NULL, + PRIMARY KEY (`session_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanphp.ashier_orders +CREATE TABLE IF NOT EXISTS `cashier_orders` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `order_no` varchar(500) COLLATE utf8mb4_bin NOT NULL, + `operator_type` varchar(500) COLLATE utf8mb4_bin DEFAULT 'cashier', + `operator_name` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `player_name` text COLLATE utf8mb4_bin NOT NULL, + `items_detail` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `subtotal` bigint(20) DEFAULT '0', + `total_price` bigint(20) DEFAULT '0', + `discount_percent` bigint(20) DEFAULT '0', + `discount_amount` bigint(20) DEFAULT '0', + `settlement` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `payment_method` varchar(500) COLLATE utf8mb4_bin DEFAULT 'bond', + `status` varchar(500) COLLATE utf8mb4_bin DEFAULT 'completed', + `created_at` bigint(20) DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanphp.ashiers +CREATE TABLE IF NOT EXISTS `cashiers` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `username` varchar(500) COLLATE utf8mb4_bin NOT NULL, + `password_hash` text COLLATE utf8mb4_bin NOT NULL, + `salt` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `discount_limit_percent` bigint(20) DEFAULT '0', + `created_at` bigint(20) DEFAULT '0', + `created_by` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `can_cash` bigint(20) DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanphp.nline_snapshots +CREATE TABLE IF NOT EXISTS `online_snapshots` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `snapshot_time` bigint(20) NOT NULL, + `online_count` bigint(20) NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `idx_snap_time` (`snapshot_time`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanphp.ay_orders +CREATE TABLE IF NOT EXISTS `pay_orders` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `out_trade_no` varchar(500) COLLATE utf8mb4_bin DEFAULT NULL, + `trade_no` text COLLATE utf8mb4_bin, + `player_name` text COLLATE utf8mb4_bin NOT NULL, + `tier_id` bigint(20) DEFAULT '0', + `money` text COLLATE utf8mb4_bin, + `bond_amount` bigint(20) DEFAULT '0', + `status` varchar(500) COLLATE utf8mb4_bin DEFAULT 'created', + `name` text COLLATE utf8mb4_bin, + `platform_sign` text COLLATE utf8mb4_bin, + `created_at` bigint(20) DEFAULT NULL, + `paid_at` bigint(20) DEFAULT NULL, + `submit_params` text COLLATE utf8mb4_bin, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanphp.ending_player_validations +CREATE TABLE IF NOT EXISTS `pending_player_validations` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `player_name` text COLLATE utf8mb4_bin NOT NULL, + `request_type` varchar(500) COLLATE utf8mb4_bin DEFAULT 'general', + `request_data` varchar(500) COLLATE utf8mb4_bin DEFAULT '{}', + `status` varchar(500) COLLATE utf8mb4_bin DEFAULT 'pending', + `created_at` bigint(20) DEFAULT '0', + `validated_at` bigint(20) DEFAULT '0', + `result` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanphp.eb_bond_records +CREATE TABLE IF NOT EXISTS `web_bond_records` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `player_name` text COLLATE utf8mb4_bin NOT NULL, + `amount` bigint(20) DEFAULT '0', + `record_type` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `recorded_by` varchar(500) COLLATE utf8mb4_bin DEFAULT 'system', + `description` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `created_at` bigint(20) DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanphp.eb_group_renew +CREATE TABLE IF NOT EXISTS `web_group_renew` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `player_name` text COLLATE utf8mb4_bin NOT NULL, + `group_name` text COLLATE utf8mb4_bin NOT NULL, + `renew_price` bigint(20) DEFAULT '0', + `duration_minutes` bigint(20) DEFAULT '0', + `req_id` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `status` varchar(500) COLLATE utf8mb4_bin DEFAULT 'pending', + `created_at` bigint(20) DEFAULT '0', + `processed_at` bigint(20) DEFAULT '0', + `remark` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanphp.eb_item_sn +CREATE TABLE IF NOT EXISTS `web_item_sn` ( + `sn` varchar(500) COLLATE utf8mb4_bin NOT NULL, + `item_type` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `owner` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `status` varchar(500) COLLATE utf8mb4_bin DEFAULT 'active', + `issue_time` bigint(20) DEFAULT '0', + `loc_type` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `loc_player` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `loc_world` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `loc_x` bigint(20) DEFAULT '0', + `loc_y` bigint(20) DEFAULT '0', + `loc_z` bigint(20) DEFAULT '0', + `container_type` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `in_land` bigint(20) DEFAULT '0', + `land_name` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `last_seen` bigint(20) DEFAULT '0', + `cancel_time` bigint(20) DEFAULT '0', + `lost_count` bigint(20) DEFAULT '0', + `lost_state` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `remark` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `bind_reason` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `bind_time` bigint(20) DEFAULT '0', + `updated_at` bigint(20) DEFAULT '0', + `detached_at` bigint(20) DEFAULT '0', + `own_chest` bigint(20) DEFAULT '0', + PRIMARY KEY (`sn`), + KEY `idx_web_item_sn_owner` (`owner`(191),`item_type`(191)) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanphp.eb_land_owner_changes +CREATE TABLE IF NOT EXISTS `web_land_owner_changes` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `land_id` bigint(20) NOT NULL, + `land_name` text COLLATE utf8mb4_bin NOT NULL, + `old_owner` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `new_owner` text COLLATE utf8mb4_bin NOT NULL, + `created_at` bigint(20) DEFAULT '0', + `synced` bigint(20) DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanphp.eb_land_transfers +CREATE TABLE IF NOT EXISTS `web_land_transfers` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `land_name` text COLLATE utf8mb4_bin NOT NULL, + `old_owner` text COLLATE utf8mb4_bin NOT NULL, + `new_owner` text COLLATE utf8mb4_bin NOT NULL, + `status` varchar(500) COLLATE utf8mb4_bin DEFAULT 'pending', + `created_at` bigint(20) DEFAULT '0', + `completed_at` bigint(20) DEFAULT '0', + `expires_at` bigint(20) DEFAULT '0', + `cooldown_until` bigint(20) DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanphp.eb_sn_commands +CREATE TABLE IF NOT EXISTS `web_sn_commands` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `cmd` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `sn` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `player` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `item_type` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `reason` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `is_force` bigint(20) DEFAULT '0', + `link_type` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `link_id` bigint(20) DEFAULT '0', + `status` varchar(500) COLLATE utf8mb4_bin DEFAULT 'pending', + `result` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `created_at` bigint(20) DEFAULT '0', + `sent_at` bigint(20) DEFAULT '0', + `done_at` bigint(20) DEFAULT '0', + PRIMARY KEY (`id`), + KEY `idx_web_sn_cmd_status` (`status`,`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanphp.eb_sn_cooldown +CREATE TABLE IF NOT EXISTS `web_sn_cooldown` ( + `player` varchar(500) COLLATE utf8mb4_bin NOT NULL, + `until` bigint(20) DEFAULT '0', + PRIMARY KEY (`player`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanphp.eb_sn_event +CREATE TABLE IF NOT EXISTS `web_sn_event` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `action` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `sn` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `player` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `detail` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `created_at` bigint(20) DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanphp.eb_sn_held +CREATE TABLE IF NOT EXISTS `web_sn_held` ( + `sn` varchar(500) COLLATE utf8mb4_bin NOT NULL, + `player` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `holder` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `owner` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `item_type` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `held` bigint(20) DEFAULT '0', + `time` bigint(20) DEFAULT '0', + PRIMARY KEY (`sn`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanphp.eb_sn_log +CREATE TABLE IF NOT EXISTS `web_sn_log` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `sn` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `item_type` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `action` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `player` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `detail` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `time` bigint(20) DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=372 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanphp.eb_sn_lost +CREATE TABLE IF NOT EXISTS `web_sn_lost` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `sn` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `player` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `reason` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `status` varchar(500) COLLATE utf8mb4_bin DEFAULT 'checking', + `result` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `report_count` bigint(20) DEFAULT '1', + `created_at` bigint(20) DEFAULT '0', + `handled_at` bigint(20) DEFAULT '0', + PRIMARY KEY (`id`), + KEY `idx_web_sn_lost_sn` (`sn`,`created_at`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanphp.eb_sn_stock +CREATE TABLE IF NOT EXISTS `web_sn_stock` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `sn` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `item_type` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `action` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `world` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `x` bigint(20) DEFAULT '0', + `y` bigint(20) DEFAULT '0', + `z` bigint(20) DEFAULT '0', + `container_type` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `in_land` bigint(20) DEFAULT '0', + `land_name` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `player` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `time` bigint(20) DEFAULT '0', + PRIMARY KEY (`id`), + KEY `idx_web_sn_stock_sn` (`sn`,`time`) +) ENGINE=InnoDB AUTO_INCREMENT=41 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanphp.eb_ticket_replies +CREATE TABLE IF NOT EXISTS `web_ticket_replies` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `ticket_id` bigint(20) NOT NULL, + `sender` text COLLATE utf8mb4_bin NOT NULL, + `role` varchar(500) COLLATE utf8mb4_bin DEFAULT 'user', + `message` text COLLATE utf8mb4_bin NOT NULL, + `created_at` bigint(20) NOT NULL, + PRIMARY KEY (`id`), + KEY `idx_web_ticket_replies_ticket` (`ticket_id`), + KEY `idx_wtr_ticket` (`ticket_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanphp.eb_tickets +CREATE TABLE IF NOT EXISTS `web_tickets` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `type` text COLLATE utf8mb4_bin NOT NULL, + `status` varchar(500) COLLATE utf8mb4_bin DEFAULT 'submitted', + `requester` text COLLATE utf8mb4_bin NOT NULL, + `assigned_to` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `title` text COLLATE utf8mb4_bin NOT NULL, + `description` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `images` varchar(500) COLLATE utf8mb4_bin DEFAULT '[]', + `reject_reason` varchar(500) COLLATE utf8mb4_bin DEFAULT '', + `created_at` bigint(20) NOT NULL, + `updated_at` bigint(20) NOT NULL, + PRIMARY KEY (`id`), + KEY `idx_web_tickets_requester` (`requester`(191)), + KEY `idx_web_tickets_status` (`status`), + KEY `idx_web_tickets_assigned` (`assigned_to`), + KEY `idx_wt_requester` (`requester`(191)), + KEY `idx_wt_status` (`status`), + KEY `idx_wt_assigned` (`assigned_to`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; + +-- aoyuanphp.eb_twofa_qr +CREATE TABLE IF NOT EXISTS `web_twofa_qr` ( + `token` varchar(500) COLLATE utf8mb4_bin NOT NULL, + `player` text COLLATE utf8mb4_bin NOT NULL, + `otpauth` text COLLATE utf8mb4_bin NOT NULL, + `expires_at` bigint(20) NOT NULL, + PRIMARY KEY (`token`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;