load(); } else { \Dotenv\Dotenv::createMutable(BASE_PATH)->load(); } } // 加载配置(排除 route.php,避免 CLI 报错) \Webman\Config::load(BASE_PATH . '/config', ['route', 'plugin']); \Webman\ThinkOrm\ThinkOrm::start(null); use app\dice\service\DiceChannelConfigService; use plugin\saiadmin\app\model\system\SystemDept; use plugin\saiadmin\app\model\system\SystemUser; use support\think\Db; function cliPdo(): PDO { $host = getenv('DB_HOST') ?: '127.0.0.1'; $port = getenv('DB_PORT') ?: '3306'; $db = getenv('DB_NAME') ?: ''; $user = getenv('DB_USER') ?: ''; $pass = getenv('DB_PASSWORD') ?: ''; $dsn = "mysql:host={$host};port={$port};dbname={$db};charset=utf8mb4"; return new PDO($dsn, $user, $pass, [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, ]); } function runSqlFile(PDO $pdo, string $path, string $label, bool $alterOnly = false): void { echo "\n=== {$label} ===\n"; if (!is_file($path)) { echo "跳过:文件不存在\n"; return; } $sql = file_get_contents($path); $sql = preg_replace('/--.*$/m', '', $sql); $parts = array_filter(array_map('trim', explode(';', $sql))); $ok = 0; $skip = 0; foreach ($parts as $statement) { if ($statement === '') { continue; } if ($alterOnly && stripos($statement, 'ALTER TABLE') !== 0) { continue; } try { $pdo->exec($statement); $ok++; } catch (PDOException $e) { $msg = $e->getMessage(); $isAlter = stripos($statement, 'ALTER TABLE') === 0; if ($isAlter && (stripos($msg, 'Duplicate column') !== false || stripos($msg, 'Duplicate key name') !== false)) { $skip++; } elseif (!$isAlter) { echo " [警告] " . substr($msg, 0, 120) . "\n"; $skip++; } else { echo " [错误] {$msg}\n"; throw $e; } } } echo "完成:成功 {$ok} 条" . ($skip > 0 ? ",跳过 {$skip} 条(字段已存在)" : '') . "\n"; } function getRootDeptId(int $deptId): ?int { $currentId = $deptId; $visited = []; while ($currentId > 0 && !isset($visited[$currentId])) { $visited[$currentId] = true; $dept = SystemDept::find($currentId); if (!$dept) { return null; } $parentId = (int) ($dept->parent_id ?? 0); if ($parentId === 0) { return $currentId; } $currentId = $parentId; } return $currentId > 0 ? $currentId : null; } echo "========== 大富翁渠道/配置初始化 ==========\n"; echo '数据库: ' . (getenv('DB_NAME') ?: '') . '@' . (getenv('DB_HOST') ?: '') . "\n"; $pdo = cliPdo(); runSqlFile($pdo, __DIR__ . '/dice_tables_add_dept_id.sql', '1. dice 表增加 dept_id', true); runSqlFile($pdo, __DIR__ . '/dept_flatten_channels.sql', '2. 渠道扁平化 SQL'); echo "\n=== 3. 渠道扁平化 PHP(多级用户归并 + 删除子渠道) ===\n"; Db::transaction(function () { $users = SystemUser::where('dept_id', '>', 0)->select(); $moved = 0; foreach ($users as $user) { $deptId = (int) $user->dept_id; $rootId = getRootDeptId($deptId); if ($rootId !== null && $rootId !== $deptId) { SystemUser::where('id', $user->id)->update(['dept_id' => $rootId]); echo " 用户 {$user->id}: dept {$deptId} -> {$rootId}\n"; $moved++; } } $childIds = SystemDept::where('parent_id', '>', 0)->column('id'); if (!empty($childIds)) { SystemDept::destroy($childIds); echo ' 已删除子渠道: ' . implode(',', $childIds) . "\n"; } else { echo " 无子渠道需删除\n"; } SystemDept::where('id', '>', 0)->update(['parent_id' => 0, 'level' => '0']); echo " 用户归并 {$moved} 人,渠道扁平化完成\n"; }); $service = new DiceChannelConfigService(); echo "\n=== 3.5 配置表复合键与默认模板 dept_id=0 ===\n"; $service->ensureConfigCompositeKeys(); echo " dice_config / dice_reward_config: ok\n"; echo "\n=== 3.6 彩金池配置按渠道唯一(dept_id + name) ===\n"; try { $indexes = Db::query("SHOW INDEX FROM `dice_lottery_pool_config` WHERE Key_name = 'dice_lottery_poll_config_unique'"); if (!empty($indexes)) { Db::execute('ALTER TABLE `dice_lottery_pool_config` DROP INDEX `dice_lottery_poll_config_unique`'); echo " 已移除 name 全局唯一索引\n"; } $uk = Db::query("SHOW INDEX FROM `dice_lottery_pool_config` WHERE Key_name = 'uk_dept_name'"); if (empty($uk)) { Db::execute('ALTER TABLE `dice_lottery_pool_config` ADD UNIQUE KEY `uk_dept_name` (`dept_id`, `name`)'); echo " 已添加 uk_dept_name(dept_id, name)\n"; } else { echo " uk_dept_name 已存在\n"; } } catch (\Throwable $e) { echo " 跳过: {$e->getMessage()}\n"; } echo "\n=== 4. 将现有配置设为默认模板(dept_id=0) ===\n"; $tables = [ 'dice_config', 'dice_ante_config', 'dice_lottery_pool_config', 'dice_reward_config', 'dice_reward', 'dice_game', ]; foreach ($tables as $table) { try { Db::table($table)->update(['dept_id' => 0]); echo " {$table}: ok\n"; } catch (\Throwable $e) { echo " {$table}: 跳过 ({$e->getMessage()})\n"; } } echo "\n=== 5. 为所有渠道从默认模板复制配置 ===\n"; $summary = $service->syncAllChannelsFromDefault(); foreach ($summary as $deptId => $info) { $copied = implode(',', $info['copied'] ?? []) ?: '无'; $skipped = implode(',', $info['skipped'] ?? []) ?: '无'; echo " 渠道 {$deptId}: 复制 [{$copied}],跳过 [{$skipped}]\n"; } echo "\n=== 6. 修复无效渠道并回填 dept_id ===\n"; echo " 无效渠道归并: "; print_r($service->repairOrphanDeptReferences()); echo " 回填: "; print_r($service->backfillDataDeptId()); $deptCount = SystemDept::count(); echo "\n当前顶级渠道数: {$deptCount}\n"; echo "========== 全部初始化完成 ==========\n";