From 136c18e413c778e910c5da28b8a17166a79c1d82 Mon Sep 17 00:00:00 2001 From: zhenhui <1276357500@qq.com> Date: Wed, 3 Jun 2026 15:41:56 +0800 Subject: [PATCH] =?UTF-8?q?1.=E4=BC=98=E5=8C=96=E6=B8=A0=E9=81=93=E5=88=A0?= =?UTF-8?q?=E9=99=A4=E5=A4=B1=E8=B4=A5=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- saiadmin-artd/src/api/system/dept.ts | 3 +- .../plugin/dice/reward_config/index/index.vue | 20 ++- .../dept/modules/delete-channel-dialog.vue | 11 +- .../dice/service/DiceChannelConfigService.php | 71 ++++++++-- server/db/debug_check_table_type.php | 52 +++++++ server/db/debug_list_view_backup_objects.php | 47 +++++++ server/db/debug_list_views_and_definers.php | 46 +++++++ server/db/dice_flowcharts_menu.sql | 33 +++++ server/db/dice_play_record_add_remark.sql | 3 + ...dice_reward_config_tier_recommend_menu.sql | 23 ++++ server/db/fix_bt_backup_view_tables.php | 127 ++++++++++++++++++ server/db/run_dice_flowcharts_menu.php | 99 ++++++++++++++ server/db/run_dice_play_record_add_remark.php | 44 ++++++ ...dice_reward_config_tier_recommend_menu.php | 60 +++++++++ .../system/SystemDeptController.php | 5 +- 15 files changed, 626 insertions(+), 18 deletions(-) create mode 100644 server/db/debug_check_table_type.php create mode 100644 server/db/debug_list_view_backup_objects.php create mode 100644 server/db/debug_list_views_and_definers.php create mode 100644 server/db/dice_flowcharts_menu.sql create mode 100644 server/db/dice_play_record_add_remark.sql create mode 100644 server/db/dice_reward_config_tier_recommend_menu.sql create mode 100644 server/db/fix_bt_backup_view_tables.php create mode 100644 server/db/run_dice_flowcharts_menu.php create mode 100644 server/db/run_dice_play_record_add_remark.php create mode 100644 server/db/run_dice_reward_config_tier_recommend_menu.php diff --git a/saiadmin-artd/src/api/system/dept.ts b/saiadmin-artd/src/api/system/dept.ts index 47acb44..52b121a 100644 --- a/saiadmin-artd/src/api/system/dept.ts +++ b/saiadmin-artd/src/api/system/dept.ts @@ -59,7 +59,8 @@ export default { delete(params: Record) { return request.del({ url: '/core/dept/destroy', - data: params + data: params, + showErrorMessage: false }) }, diff --git a/saiadmin-artd/src/views/plugin/dice/reward_config/index/index.vue b/saiadmin-artd/src/views/plugin/dice/reward_config/index/index.vue index adbfb24..262c9b8 100644 --- a/saiadmin-artd/src/views/plugin/dice/reward_config/index/index.vue +++ b/saiadmin-artd/src/views/plugin/dice/reward_config/index/index.vue @@ -185,6 +185,7 @@
{{ @@ -672,7 +677,16 @@ return n - 1 } + /** BIGWIN 行不参与按 real_ev 推断 T1-T5,避免编辑时从大奖权重表消失 */ + function setBigwinRowWeight(row: IndexRow, v: number | number[] | undefined | null) { + const n = Array.isArray(v) ? v[0] : v + row.weight = toWeight(n) + } + function handleRealEvChange(row: IndexRow) { + if (row.tier === 'BIGWIN') { + return + } const n = rowRealEvNumber(row) syncRowTierFromRealEv(row) if (row.tier === 'T5') { @@ -1075,7 +1089,7 @@ ui_text: r.ui_text, ui_text_en: r.ui_text_en, real_ev: r.real_ev, - tier: r.tier, + tier: 'BIGWIN', remark: r.remark })) await api.batchUpdate(batchPayload, { dept_id: filterDeptId.value }) diff --git a/saiadmin-artd/src/views/system/dept/modules/delete-channel-dialog.vue b/saiadmin-artd/src/views/system/dept/modules/delete-channel-dialog.vue index ae60465..1efaeb0 100644 --- a/saiadmin-artd/src/views/system/dept/modules/delete-channel-dialog.vue +++ b/saiadmin-artd/src/views/system/dept/modules/delete-channel-dialog.vue @@ -106,8 +106,15 @@ ElMessage.success('删除成功') emit('success') handleClose() - } catch (e: any) { - ElMessage.error(e?.message || '删除失败') + } catch (e: unknown) { + let msg = '删除失败' + if (e !== null && typeof e === 'object' && 'message' in e) { + const m = Reflect.get(e, 'message') + if (typeof m === 'string' && m !== '') { + msg = m + } + } + ElMessage.error(msg) } finally { submitting.value = false } diff --git a/server/app/dice/service/DiceChannelConfigService.php b/server/app/dice/service/DiceChannelConfigService.php index 2f24092..e3bc814 100644 --- a/server/app/dice/service/DiceChannelConfigService.php +++ b/server/app/dice/service/DiceChannelConfigService.php @@ -54,6 +54,22 @@ class DiceChannelConfigService 'dice_reward_config_record' => ['label' => '权重测试记录', 'group' => 'records'], ]; + /** 关联数据删除顺序:先删流水/记录,再删玩家,最后删配置 */ + private const DELETE_TABLE_ORDER = [ + 'dice_play_record', + 'dice_play_record_test', + 'dice_player_wallet_record', + 'dice_player_ticket_record', + 'dice_reward_config_record', + 'dice_player', + 'dice_reward', + 'dice_reward_config', + 'dice_config', + 'dice_ante_config', + 'dice_lottery_pool_config', + 'dice_game', + ]; + /** * 默认模板 dept_id 统一为 0,并为固定 id 的配置表建立 (dept_id, id) 唯一约束 */ @@ -417,21 +433,54 @@ class DiceChannelConfigService if ($userCount > 0) { throw new ApiException('This channel has users, please delete or transfer them first'); } - $allowed = array_keys(self::RELATION_TABLES); - foreach ($deleteTables as $table) { - if (!in_array($table, $allowed, true)) { - continue; + $tablesToDelete = $this->sortTablesForDelete($deleteTables); + Db::startTrans(); + try { + foreach ($tablesToDelete as $table) { + if (!$this->tableHasColumn($table, 'dept_id')) { + continue; + } + Db::table($table)->where('dept_id', $deptId)->delete(); } - if (!$this->tableHasColumn($table, 'dept_id')) { - continue; - } - Db::table($table)->where('dept_id', $deptId)->delete(); + Db::name('sa_system_role_dept')->where('dept_id', $deptId)->delete(); + SystemDept::destroy($deptId, true); + Db::commit(); + } catch (\Throwable $e) { + Db::rollback(); + throw new ApiException('Channel delete failed: ' . $e->getMessage()); } - SystemDept::destroy($deptId, true); DiceRewardConfig::refreshCache($deptId); DiceReward::refreshCache($deptId); } + /** + * 按依赖顺序排列待删表(勾选顺序无关) + * + * @param array $deleteTables + * @return array + */ + private function sortTablesForDelete(array $deleteTables): array + { + $allowed = array_keys(self::RELATION_TABLES); + $picked = []; + foreach ($deleteTables as $table) { + if (is_string($table) && in_array($table, $allowed, true)) { + $picked[$table] = true; + } + } + $ordered = []; + foreach (self::DELETE_TABLE_ORDER as $table) { + if (isset($picked[$table])) { + $ordered[] = $table; + unset($picked[$table]); + } + } + foreach (array_keys($picked) as $table) { + $ordered[] = $table; + } + return $ordered; + } + /** * @return array> */ @@ -477,7 +526,9 @@ class DiceChannelConfigService if ($deptId === null || $deptId === AdminScopeHelper::DEFAULT_TEMPLATE_DEPT) { $templateId = AdminScopeHelper::DEFAULT_TEMPLATE_DEPT; $query->where(function ($q) use ($templateId) { - $q->where('dept_id', $templateId)->whereOr('dept_id', 'null'); + $q->where('dept_id', $templateId)->whereOr(function ($sub) { + $sub->whereNull('dept_id'); + }); }); } else { $query->where('dept_id', $deptId); diff --git a/server/db/debug_check_table_type.php b/server/db/debug_check_table_type.php new file mode 100644 index 0000000..38e6188 --- /dev/null +++ b/server/db/debug_check_table_type.php @@ -0,0 +1,52 @@ +load(); + } else { + \Dotenv\Dotenv::createMutable(dirname(__DIR__))->load(); + } +} + +$table = $argv[1] ?? ''; +$table = trim((string) $table); +if ($table === '') { + fwrite(STDERR, "Missing table name.\n"); + exit(1); +} + +$host = getenv('DB_HOST') ?: '127.0.0.1'; +$port = getenv('DB_PORT') ?: '3306'; +$dbName = getenv('DB_NAME') ?: ''; +$user = getenv('DB_USER') ?: ''; +$pass = getenv('DB_PASSWORD') ?: ''; + +$dsn = "mysql:host={$host};port={$port};dbname={$dbName};charset=utf8mb4"; +$pdo = new PDO($dsn, $user, $pass, [ + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, +]); + +$stmt = $pdo->prepare( + "SELECT TABLE_NAME, TABLE_TYPE + FROM information_schema.TABLES + WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = :name" +); +$stmt->execute(['name' => $table]); +$row = $stmt->fetch(PDO::FETCH_ASSOC); +if (!$row) { + echo "NOT_FOUND\t{$table}\n"; + exit(0); +} + +echo $row['TABLE_NAME'] . "\t" . $row['TABLE_TYPE'] . "\n"; + diff --git a/server/db/debug_list_view_backup_objects.php b/server/db/debug_list_view_backup_objects.php new file mode 100644 index 0000000..d3a8170 --- /dev/null +++ b/server/db/debug_list_view_backup_objects.php @@ -0,0 +1,47 @@ +load(); + } else { + \Dotenv\Dotenv::createMutable(dirname(__DIR__))->load(); + } +} + +$host = getenv('DB_HOST') ?: '127.0.0.1'; +$port = getenv('DB_PORT') ?: '3306'; +$dbName = getenv('DB_NAME') ?: ''; +$user = getenv('DB_USER') ?: ''; +$pass = getenv('DB_PASSWORD') ?: ''; + +$dsn = "mysql:host={$host};port={$port};dbname={$dbName};charset=utf8mb4"; +$pdo = new PDO($dsn, $user, $pass, [ + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, +]); + +$sql = "SELECT TABLE_NAME, TABLE_TYPE + FROM information_schema.TABLES + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME LIKE '%\\_\\_view\\_backup' + ORDER BY TABLE_NAME"; + +$rows = $pdo->query($sql)->fetchAll(PDO::FETCH_ASSOC); +if (!$rows) { + echo "No *__view_backup objects found.\n"; + exit(0); +} + +foreach ($rows as $row) { + echo $row['TABLE_NAME'] . "\t" . $row['TABLE_TYPE'] . "\n"; +} + diff --git a/server/db/debug_list_views_and_definers.php b/server/db/debug_list_views_and_definers.php new file mode 100644 index 0000000..dad04f2 --- /dev/null +++ b/server/db/debug_list_views_and_definers.php @@ -0,0 +1,46 @@ +load(); + } else { + \Dotenv\Dotenv::createMutable(dirname(__DIR__))->load(); + } +} + +$host = getenv('DB_HOST') ?: '127.0.0.1'; +$port = getenv('DB_PORT') ?: '3306'; +$dbName = getenv('DB_NAME') ?: ''; +$user = getenv('DB_USER') ?: ''; +$pass = getenv('DB_PASSWORD') ?: ''; + +$dsn = "mysql:host={$host};port={$port};dbname={$dbName};charset=utf8mb4"; +$pdo = new PDO($dsn, $user, $pass, [ + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, +]); + +$sql = "SELECT TABLE_NAME, DEFINER, SECURITY_TYPE + FROM information_schema.VIEWS + WHERE TABLE_SCHEMA = DATABASE() + ORDER BY TABLE_NAME"; + +$rows = $pdo->query($sql)->fetchAll(PDO::FETCH_ASSOC); +if (!$rows) { + echo "No views found.\n"; + exit(0); +} + +foreach ($rows as $row) { + echo $row['TABLE_NAME'] . "\t" . ($row['DEFINER'] ?? '') . "\t" . ($row['SECURITY_TYPE'] ?? '') . "\n"; +} + diff --git a/server/db/dice_flowcharts_menu.sql b/server/db/dice_flowcharts_menu.sql new file mode 100644 index 0000000..e56c986 --- /dev/null +++ b/server/db/dice_flowcharts_menu.sql @@ -0,0 +1,33 @@ +-- 抽奖流程图:两个顶级外链菜单(type=4),点击新窗口打开 public/docs/flowcharts/*.html +-- 挂载位置:与「后台操作指南」同级(parent_id=0),紧挨其下方(sort 略小,列表按 sort 降序) + +SET @now = NOW(); + +-- 移除旧的「抽奖流程说明」内嵌页菜单(若已安装) +SET @old_flow_menu_id = ( + SELECT `id` FROM `sa_system_menu` + WHERE `path` = 'flowcharts' AND `component` = '/plugin/dice/flowcharts/index/index' AND `type` = 2 + ORDER BY `id` ASC LIMIT 1 +); + +DELETE FROM `sa_system_role_menu` WHERE `menu_id` = @old_flow_menu_id; +DELETE FROM `sa_system_menu` WHERE `parent_id` = @old_flow_menu_id AND `type` = 3; +DELETE FROM `sa_system_menu` WHERE `id` = @old_flow_menu_id; + +-- 1) 为何最终抽到该奖励 +INSERT INTO `sa_system_menu` +(`parent_id`,`name`,`code`,`slug`,`type`,`path`,`component`,`method`,`icon`,`sort`,`link_url`,`is_iframe`,`is_keep_alive`,`is_hidden`,`is_fixed_tab`,`is_full_page`,`generate_id`,`generate_key`,`status`,`create_time`,`update_time`) +SELECT 0, '为何最终抽到该奖励', 'DiceFlowWhyReward', NULL, 4, 'dice_flow_why_reward', '', NULL, 'ri:question-answer-line', 4, '/docs/flowcharts/dice-为何抽到该奖励.html', 2, 2, 2, 2, 2, 0, NULL, 1, @now, @now +WHERE NOT EXISTS ( + SELECT 1 FROM `sa_system_menu` + WHERE `type` = 4 AND `link_url` = '/docs/flowcharts/dice-为何抽到该奖励.html' +); + +-- 2) 后台如何配置中奖逻辑 +INSERT INTO `sa_system_menu` +(`parent_id`,`name`,`code`,`slug`,`type`,`path`,`component`,`method`,`icon`,`sort`,`link_url`,`is_iframe`,`is_keep_alive`,`is_hidden`,`is_fixed_tab`,`is_full_page`,`generate_id`,`generate_key`,`status`,`create_time`,`update_time`) +SELECT 0, '后台如何配置中奖逻辑', 'DiceFlowAdminConfig', NULL, 4, 'dice_flow_admin_config', '', NULL, 'ri:settings-3-line', 3, '/docs/flowcharts/dice-后台中奖逻辑配置.html', 2, 2, 2, 2, 2, 0, NULL, 1, @now, @now +WHERE NOT EXISTS ( + SELECT 1 FROM `sa_system_menu` + WHERE `type` = 4 AND `link_url` = '/docs/flowcharts/dice-后台中奖逻辑配置.html' +); diff --git a/server/db/dice_play_record_add_remark.sql b/server/db/dice_play_record_add_remark.sql new file mode 100644 index 0000000..7f99d61 --- /dev/null +++ b/server/db/dice_play_record_add_remark.sql @@ -0,0 +1,3 @@ +-- dice_play_record 新增备注(T4 惩罚余额不足等场景) +ALTER TABLE `dice_play_record` + ADD COLUMN `remark` varchar(255) DEFAULT NULL COMMENT '备注(如惩罚格余额不足)' AFTER `reward_tier`; diff --git a/server/db/dice_reward_config_tier_recommend_menu.sql b/server/db/dice_reward_config_tier_recommend_menu.sql new file mode 100644 index 0000000..3fb3558 --- /dev/null +++ b/server/db/dice_reward_config_tier_recommend_menu.sql @@ -0,0 +1,23 @@ +-- 奖励配置:档位结算推荐配置(T1-T5 推荐金额、按规则生成)按钮权限 +-- 挂载在「奖励配置」菜单(type=2)下;slug 与 DiceRewardConfigController Permission 一致 + +SET @now = NOW(); + +SET @reward_menu_id = ( + SELECT `id` FROM `sa_system_menu` + WHERE `type` = 2 + AND ( + `path` = 'reward_config' + OR `component` LIKE '%reward_config%' + ) + ORDER BY `id` ASC + LIMIT 1 +); + +INSERT INTO `sa_system_menu` +(`parent_id`,`name`,`code`,`slug`,`type`,`path`,`component`,`method`,`sort`,`is_iframe`,`is_keep_alive`,`is_hidden`,`is_fixed_tab`,`is_full_page`,`generate_id`,`generate_key`,`status`,`create_time`,`update_time`) +SELECT @reward_menu_id, '档位结算推荐配置', '', 'dice:reward_config:index:tierRecommend', 3, '', '', '', 95, 2, 2, 2, 2, 2, 0, NULL, 1, @now, @now +WHERE @reward_menu_id IS NOT NULL + AND NOT EXISTS ( + SELECT 1 FROM `sa_system_menu` WHERE `slug` = 'dice:reward_config:index:tierRecommend' AND `type` = 3 + ); diff --git a/server/db/fix_bt_backup_view_tables.php b/server/db/fix_bt_backup_view_tables.php new file mode 100644 index 0000000..3954ba5 --- /dev/null +++ b/server/db/fix_bt_backup_view_tables.php @@ -0,0 +1,127 @@ +__view_backup + * - 在该表写入 SHOW CREATE VIEW 的结果(若权限不足则写入空串) + * + * 用法(在 server 目录执行): + * php db/fix_bt_backup_view_tables.php + */ + +require_once __DIR__ . '/../vendor/autoload.php'; + +if (class_exists(\Dotenv\Dotenv::class) && is_file(dirname(__DIR__) . '/.env')) { + if (method_exists(\Dotenv\Dotenv::class, 'createUnsafeMutable')) { + \Dotenv\Dotenv::createUnsafeMutable(dirname(__DIR__))->load(); + } else { + \Dotenv\Dotenv::createMutable(dirname(__DIR__))->load(); + } +} + +$host = getenv('DB_HOST') ?: '127.0.0.1'; +$port = getenv('DB_PORT') ?: '3306'; +$dbName = getenv('DB_NAME') ?: ''; +$user = getenv('DB_USER') ?: ''; +$pass = getenv('DB_PASSWORD') ?: ''; + +if ($dbName === '' || $user === '') { + fwrite(STDERR, "Missing DB_NAME/DB_USER in .env\n"); + exit(1); +} + +$dsn = "mysql:host={$host};port={$port};dbname={$dbName};charset=utf8mb4"; +$pdo = new PDO($dsn, $user, $pass, [ + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, +]); + +$views = $pdo + ->query("SELECT TABLE_NAME FROM information_schema.VIEWS WHERE TABLE_SCHEMA = DATABASE() ORDER BY TABLE_NAME") + ->fetchAll(PDO::FETCH_COLUMN); + +if (!$views) { + echo "No views found; nothing to do.\n"; + exit(0); +} + +$created = 0; +$updated = 0; +$failed = 0; + +foreach ($views as $viewName) { + $viewName = (string) $viewName; + $backupTable = $viewName . '__view_backup'; + + if (!preg_match('/^[a-zA-Z0-9_]+$/', $backupTable)) { + $failed++; + echo "[skip] invalid name: {$backupTable}\n"; + continue; + } + + try { + $pdo->exec( + "CREATE TABLE IF NOT EXISTS `{$backupTable}` ( + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + `view_name` VARCHAR(255) NOT NULL, + `create_sql` LONGTEXT NOT NULL, + `updated_at` DATETIME NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `uk_view_name` (`view_name`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4" + ); + $created++; + } catch (Throwable $e) { + $failed++; + echo "[fail] create table {$backupTable}: " . $e->getMessage() . "\n"; + continue; + } + + $createSql = ''; + try { + $stmt = $pdo->query("SHOW CREATE VIEW `{$viewName}`"); + $row = $stmt ? $stmt->fetch(PDO::FETCH_ASSOC) : false; + if ($row) { + // SHOW CREATE VIEW 返回字段名可能是 "Create View" 或类似 + foreach ($row as $k => $v) { + if (is_string($k) && stripos($k, 'create') !== false && is_string($v)) { + $createSql = $v; + break; + } + } + } + } catch (Throwable $e) { + // 权限不足也不阻断,只是留空,保证备份表存在 + $createSql = ''; + } + + try { + $stmt = $pdo->prepare( + "INSERT INTO `{$backupTable}` (`view_name`, `create_sql`, `updated_at`) + VALUES (:view_name, :create_sql, :updated_at) + ON DUPLICATE KEY UPDATE + `create_sql` = VALUES(`create_sql`), + `updated_at` = VALUES(`updated_at`)" + ); + $stmt->execute([ + 'view_name' => $viewName, + 'create_sql' => $createSql, + 'updated_at' => date('Y-m-d H:i:s'), + ]); + $updated++; + echo "[ok] {$viewName} -> {$backupTable}\n"; + } catch (Throwable $e) { + $failed++; + echo "[fail] upsert {$backupTable}: " . $e->getMessage() . "\n"; + } +} + +echo "Done. created={$created}, updated={$updated}, failed={$failed}\n"; + diff --git a/server/db/run_dice_flowcharts_menu.php b/server/db/run_dice_flowcharts_menu.php new file mode 100644 index 0000000..ad60d1e --- /dev/null +++ b/server/db/run_dice_flowcharts_menu.php @@ -0,0 +1,99 @@ +exec($statement); + $ok++; + } + echo "完成:执行 {$ok} 条语句\n"; +} + +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, + ]); +} + +echo "========== 抽奖流程图外链菜单安装 ==========\n"; +echo '数据库: ' . (getenv('DB_NAME') ?: '') . '@' . (getenv('DB_HOST') ?: '') . "\n"; + +$pdo = cliPdo(); +runSqlFile($pdo, __DIR__ . '/dice_flowcharts_menu.sql', '1. 外链菜单'); + +$menuIds = Db::name('sa_system_menu') + ->where('type', 4) + ->whereIn('link_url', [ + '/docs/flowcharts/dice-为何抽到该奖励.html', + '/docs/flowcharts/dice-后台中奖逻辑配置.html', + ]) + ->column('id'); + +if ($menuIds === [] || $menuIds === null) { + echo "错误:未找到流程图菜单\n"; + exit(1); +} + +$menuIds = array_map('intval', $menuIds); +echo "\n流程图菜单 ID: " . implode(', ', $menuIds) . "\n"; + +echo "\n=== 2. 授权超级管理员角色 ===\n"; +$adminRoleIds = Db::name('sa_system_role') + ->where('code', 'super_admin') + ->column('id'); + +if ($adminRoleIds === [] || $adminRoleIds === null) { + $adminRoleIds = Db::name('sa_system_role')->where('id', 1)->column('id'); +} + +foreach ($adminRoleIds as $roleId) { + $roleId = (int) $roleId; + foreach ($menuIds as $menuId) { + $exists = Db::name('sa_system_role_menu') + ->where('role_id', $roleId) + ->where('menu_id', $menuId) + ->count(); + if ($exists > 0) { + continue; + } + Db::name('sa_system_role_menu')->insert([ + 'role_id' => $roleId, + 'menu_id' => $menuId, + ]); + } + echo "角色 {$roleId} 已关联流程图菜单\n"; +} + +UserMenuCache::clearMenuCache(); +echo "\n已清理菜单缓存。请重新登录后台或刷新页面查看侧边栏。\n"; +echo "点击菜单将在新窗口打开 /docs/flowcharts/*.html\n"; diff --git a/server/db/run_dice_play_record_add_remark.php b/server/db/run_dice_play_record_add_remark.php new file mode 100644 index 0000000..b8b6278 --- /dev/null +++ b/server/db/run_dice_play_record_add_remark.php @@ -0,0 +1,44 @@ +load(); + } else { + \Dotenv\Dotenv::createMutable(BASE_PATH)->load(); + } +} + +$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"; +$pdo = new PDO($dsn, $user, $pass, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]); + +$sqlFile = __DIR__ . '/dice_play_record_add_remark.sql'; +$sql = file_get_contents($sqlFile); +$sql = preg_replace('/--.*$/m', '', $sql); +$parts = array_filter(array_map('trim', explode(';', $sql))); + +echo "执行: dice_play_record_add_remark.sql\n"; +echo "数据库: {$db} @ {$host}\n\n"; + +foreach ($parts as $statement) { + if ($statement === '') { + continue; + } + $pdo->exec($statement); + echo "OK\n"; +} + +echo "\n完成。\n"; diff --git a/server/db/run_dice_reward_config_tier_recommend_menu.php b/server/db/run_dice_reward_config_tier_recommend_menu.php new file mode 100644 index 0000000..9063b9b --- /dev/null +++ b/server/db/run_dice_reward_config_tier_recommend_menu.php @@ -0,0 +1,60 @@ +load(); + } else { + \Dotenv\Dotenv::createMutable(BASE_PATH)->load(); + } +} + +$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"; +$pdo = new PDO($dsn, $user, $pass, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]); + +$sqlFile = __DIR__ . '/dice_reward_config_tier_recommend_menu.sql'; +$sql = file_get_contents($sqlFile); +$sql = preg_replace('/--.*$/m', '', $sql); +$parts = array_filter(array_map('trim', explode(';', $sql))); + +echo "执行: dice_reward_config_tier_recommend_menu.sql\n"; +echo "数据库: {$db} @ {$host}\n\n"; + +foreach ($parts as $statement) { + if ($statement === '') { + continue; + } + $pdo->exec($statement); +} + +$row = $pdo->query( + "SELECT id, parent_id, name, slug FROM sa_system_menu WHERE slug = 'dice:reward_config:index:tierRecommend' AND type = 3 LIMIT 1" +)->fetch(PDO::FETCH_ASSOC); + +if ($row) { + echo "成功:已写入菜单权限\n"; + echo " id={$row['id']} parent_id={$row['parent_id']} name={$row['name']} slug={$row['slug']}\n"; +} else { + $parent = $pdo->query( + "SELECT id, name, path FROM sa_system_menu WHERE type = 2 AND (path = 'reward_config' OR component LIKE '%reward_config%') ORDER BY id ASC LIMIT 1" + )->fetch(PDO::FETCH_ASSOC); + if ($parent === false) { + echo "失败:未找到「奖励配置」父菜单 (type=2),请先创建 reward_config 菜单\n"; + exit(1); + } + echo "警告:权限 slug 未查到(可能已存在但未插入)。父菜单: id={$parent['id']} path={$parent['path']}\n"; + exit(1); +} diff --git a/server/plugin/saiadmin/app/controller/system/SystemDeptController.php b/server/plugin/saiadmin/app/controller/system/SystemDeptController.php index 5f6debf..cb6d9d2 100644 --- a/server/plugin/saiadmin/app/controller/system/SystemDeptController.php +++ b/server/plugin/saiadmin/app/controller/system/SystemDeptController.php @@ -107,11 +107,12 @@ class SystemDeptController extends BaseController #[Permission('渠道数据删除','core:dept:destroy')] public function destroy(Request $request) : Response { - $ids = $request->post('ids', ''); + // DELETE + JSON body 须用 input();post() 仅表单 POST 有效(与 SystemUserController 一致) + $ids = $request->input('ids', ''); if (empty($ids)) { return $this->fail('please select data to delete'); } - $deleteTables = $request->post('delete_tables', []); + $deleteTables = $request->input('delete_tables', []); if (!is_array($deleteTables)) { $deleteTables = []; }