1.新增菜单后台操作指南,方便管理员查看使用
107
server/db/run_system_admin_guide_menu.php
Normal file
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
/**
|
||||
* 安装「后台操作指南」菜单与权限,并授权给超级管理员角色
|
||||
* 用法(在 server 目录): php db/run_system_admin_guide_menu.php
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
require_once __DIR__ . '/../support/bootstrap.php';
|
||||
|
||||
use plugin\saiadmin\app\cache\UserMenuCache;
|
||||
use support\think\Db;
|
||||
|
||||
function runSqlFile(PDO $pdo, string $path, string $label): void
|
||||
{
|
||||
echo "\n=== {$label} ===\n";
|
||||
if (! is_file($path)) {
|
||||
echo "跳过:文件不存在 {$path}\n";
|
||||
return;
|
||||
}
|
||||
$sql = file_get_contents($path);
|
||||
$sql = preg_replace('/--.*$/m', '', $sql);
|
||||
$parts = array_filter(array_map('trim', explode(';', $sql)));
|
||||
$ok = 0;
|
||||
foreach ($parts as $statement) {
|
||||
if ($statement === '') {
|
||||
continue;
|
||||
}
|
||||
$pdo->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__ . '/system_admin_guide_menu.sql', '1. 菜单与按钮权限');
|
||||
|
||||
$menuId = (int) Db::name('sa_system_menu')
|
||||
->where('path', 'admin_guide')
|
||||
->where('component', '/system/admin_guide/index')
|
||||
->where('type', 2)
|
||||
->value('id');
|
||||
|
||||
if ($menuId <= 0) {
|
||||
echo "错误:未找到后台操作指南菜单\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$buttonIds = Db::name('sa_system_menu')
|
||||
->where('parent_id', $menuId)
|
||||
->where('type', 3)
|
||||
->column('id');
|
||||
|
||||
$allMenuIds = array_values(array_unique(array_merge([$menuId], array_map('intval', $buttonIds ?: []))));
|
||||
|
||||
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');
|
||||
}
|
||||
|
||||
$inserted = 0;
|
||||
foreach ($adminRoleIds as $roleId) {
|
||||
$roleId = (int) $roleId;
|
||||
foreach ($allMenuIds as $mid) {
|
||||
$exists = Db::name('sa_system_role_menu')
|
||||
->where('role_id', $roleId)
|
||||
->where('menu_id', $mid)
|
||||
->count();
|
||||
if ($exists > 0) {
|
||||
continue;
|
||||
}
|
||||
Db::name('sa_system_role_menu')->insert([
|
||||
'role_id' => $roleId,
|
||||
'menu_id' => $mid,
|
||||
]);
|
||||
$inserted++;
|
||||
}
|
||||
echo " 角色 {$roleId}:新增授权 {$inserted} 条\n";
|
||||
}
|
||||
|
||||
UserMenuCache::clearMenuCache();
|
||||
\plugin\saiadmin\app\cache\UserAuthCache::clear();
|
||||
|
||||
echo "\n菜单 ID: {$menuId}\n";
|
||||
echo "按钮权限: " . implode(',', $allMenuIds) . "\n";
|
||||
echo "已清除菜单缓存,请重新登录后台查看。\n";
|
||||
echo "========== 安装完成 ==========\n";
|
||||
39
server/db/system_admin_guide_menu.sql
Normal file
@@ -0,0 +1,39 @@
|
||||
-- 后台操作指南顶级菜单与权限
|
||||
-- 说明:挂载到顶级菜单(parent_id=0),内容来源 server/docs/ADMIN_GUIDE.md
|
||||
|
||||
SET @now = NOW();
|
||||
|
||||
-- 1) 创建后台操作指南顶级菜单(type=2,parent_id=0)
|
||||
INSERT INTO `sa_system_menu`
|
||||
(`parent_id`,`name`,`code`,`slug`,`type`,`path`,`component`,`method`,`icon`,`sort`,`is_iframe`,`is_keep_alive`,`is_hidden`,`is_fixed_tab`,`is_full_page`,`generate_id`,`generate_key`,`status`,`create_time`,`update_time`)
|
||||
SELECT 0, '后台操作指南', 'AdminGuide', NULL, 2, 'admin_guide', '/system/admin_guide/index', NULL, 'ri:book-read-line', 5, 2, 2, 2, 2, 2, 0, NULL, 1, @now, @now
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM `sa_system_menu` WHERE `path` = 'admin_guide' AND `component` = '/system/admin_guide/index' AND `type` = 2
|
||||
);
|
||||
|
||||
SET @admin_guide_menu_id = (
|
||||
SELECT `id` FROM `sa_system_menu`
|
||||
WHERE `path` = 'admin_guide' AND `component` = '/system/admin_guide/index' AND `type` = 2
|
||||
ORDER BY `id` ASC LIMIT 1
|
||||
);
|
||||
|
||||
-- 2) 创建按钮权限
|
||||
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 @admin_guide_menu_id, '数据列表', '', 'system:admin_guide:index:index', 3, '', '', '', 100, 2, 2, 2, 2, 2, 0, NULL, 1, @now, @now
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `sa_system_menu` WHERE `slug` = 'system:admin_guide:index:index' AND `type` = 3);
|
||||
|
||||
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 @admin_guide_menu_id, '读取', '', 'system:admin_guide:index:read', 3, '', '', '', 100, 2, 2, 2, 2, 2, 0, NULL, 1, @now, @now
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `sa_system_menu` WHERE `slug` = 'system:admin_guide:index:read' AND `type` = 3);
|
||||
|
||||
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 @admin_guide_menu_id, '编辑', '', 'system:admin_guide:index:edit', 3, '', '', '', 100, 2, 2, 2, 2, 2, 0, NULL, 1, @now, @now
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `sa_system_menu` WHERE `slug` = 'system:admin_guide:index:edit' AND `type` = 3);
|
||||
|
||||
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 @admin_guide_menu_id, '保存', '', 'system:admin_guide:index:save', 3, '', '', '', 100, 2, 2, 2, 2, 2, 0, NULL, 1, @now, @now
|
||||
WHERE NOT EXISTS (SELECT 1 FROM `sa_system_menu` WHERE `slug` = 'system:admin_guide:index:save' AND `type` = 3);
|
||||
128
server/docs/ADMIN_GUIDE.md
Normal file
@@ -0,0 +1,128 @@
|
||||
# 大富翁-使用说明指南
|
||||
|
||||
## 菜单简单介绍
|
||||
|
||||
## 工作台/统计页面:统计数据
|
||||
|
||||

|
||||
|
||||
## 角色管理:对角色的菜单权限设置
|
||||
|
||||
按等级设定,等级越低权限越少(不要出现上级角色没有的权限,子角色有)
|
||||
避免方式:使用子角色创建下级角色,可以避免下级角色比上级角色操作权限更多的问题
|
||||
|
||||

|
||||
|
||||
这里设置角色的菜单以及按钮权限
|
||||
|
||||

|
||||
|
||||
## 彩金池配置:监听彩金池实时变化
|
||||
|
||||
可以实时监听彩金池累积金额的变化
|
||||
|
||||

|
||||
|
||||
## 游戏配置:游戏规则和平台币转化比
|
||||
|
||||
游戏配置
|
||||
|
||||

|
||||
|
||||
其中游戏玩法为进入游戏的弹窗,和规则介绍(无特殊需求不需要大改)
|
||||
|
||||

|
||||
|
||||
游戏平台币兑换币:为进入平台时平台比转化比,比如,当前设置的为1:1,如果从jk8平台转入100,那么获取的游戏币为100,如果设置1:2则获取的平台币为200
|
||||
|
||||
## 底注配置:方便玩家快速调整压注倍率
|
||||
|
||||
底注配置
|
||||
|
||||

|
||||
|
||||
对应游戏中的,其中每次游玩对局基础消耗为1游戏币(无法修改),底注的设置只是方便玩家快速修改压注金额
|
||||
|
||||

|
||||
|
||||
# 抽奖逻辑
|
||||
|
||||
## 判断抽奖档位
|
||||
|
||||
当前的抽奖逻辑时,按照抽奖档位(T1-T5)进行抽奖,在【玩家管理】菜单中的设置玩家具体的档位权重
|
||||
|
||||

|
||||
|
||||
也可以在【彩金池配置】菜单中设置玩家正常的抽奖档位权重
|
||||
|
||||
- 其中正常的档位权重为注册玩家默认绑定的档位权重,并且只有在修改完后,创建的玩家才能绑定最新的正常档位权重
|
||||
|
||||

|
||||
|
||||
- 其中free为杀分权重为如果当前彩金池(平台)盈利超过设置的安全线则制动走杀分的权重
|
||||
|
||||

|
||||
|
||||
- 剩余的两个权重可以方便快速切换用户的档位抽奖权重
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## 根据档位抽取中奖号码
|
||||
|
||||
### 设置中奖号码地图
|
||||
|
||||
在后台设置地图缩影
|
||||
|
||||

|
||||
|
||||
地图的索引参看如下
|
||||
|
||||

|
||||
|
||||
其中地图的索引可以按照需求点击图中的按规则生成
|
||||
|
||||
并且规则尽可能符合:结算金额 < 0 → T4;0 < 结算金额 < 100 → T3;100 < 结算金额 < 200 → T2;200 < 结算金额 → T1;T5「再来一次」结算金额=0
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
### 创建完地图索引后创建相应的奖励对照表
|
||||
|
||||
创建奖励对照表的原因是由于有每个号码的权重不一样,豹子号10,15,20,25有多重组合方式,所以需要设置奖励对照表中的权重配比
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
根据抽到的奖励档位,抽取号码(主要用于设置抽取豹子号的5,10,15,20,25,30的权重)
|
||||
|
||||

|
||||
|
||||
比如上图中如果不设置,色子点数5抽到的概率和其他点数的概率是一样的,可能抽7次T1奖励,就有1次中豹子号5的可能
|
||||
|
||||
由于抽到色子点数和为10,15,20,25的色子点数组合有多种,所以在抽该这四个点数时还需要单独配置相应的中大奖概率(其中豹子号5和30只有一种组合【1,1,1,1,1】和【6,6,6,6,6】,所以不需要配置),其中权重拉到最大10000,那么中奖概率为100%(只要摇到了相应的色子点数和则中奖概率为100%)
|
||||
|
||||

|
||||
|
||||
### 扩展
|
||||
|
||||
- 测试设置的中奖概率,根据如下设置可以测试当前设置权重的中奖概率,该测试数据不记录到真实数据系统中
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
能够准确的反馈抽中点数的统计
|
||||
|
||||

|
||||
|
||||
如果当前中奖概率符合预期,或则测试多组数据选取一组符合预期的导入到当前的配置中
|
||||
|
||||

|
||||
|
||||
这里可以详情查询到指定测试记录的详情,
|
||||
|
||||

|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\controller\system;
|
||||
|
||||
use plugin\saiadmin\app\logic\system\SystemAdminGuideLogic;
|
||||
use plugin\saiadmin\basic\BaseController;
|
||||
use plugin\saiadmin\service\Permission;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
|
||||
/**
|
||||
* 后台操作指南控制器
|
||||
*/
|
||||
class SystemAdminGuideController extends BaseController
|
||||
{
|
||||
private SystemAdminGuideLogic $guideLogic;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->guideLogic = new SystemAdminGuideLogic();
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取后台操作指南 Markdown 内容
|
||||
*/
|
||||
#[Permission('后台操作指南读取', 'system:admin_guide:index:read')]
|
||||
public function read(Request $request): Response
|
||||
{
|
||||
$data = $this->guideLogic->read();
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存后台操作指南 Markdown 内容
|
||||
*/
|
||||
#[Permission('后台操作指南保存', 'system:admin_guide:index:save')]
|
||||
public function save(Request $request): Response
|
||||
{
|
||||
$content = $request->post('content', '');
|
||||
if (! is_string($content)) {
|
||||
return $this->fail('invalid content');
|
||||
}
|
||||
$data = $this->guideLogic->save($content);
|
||||
return $this->success($data, 'save success');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | saiadmin [ saiadmin快速开发框架 ]
|
||||
// +----------------------------------------------------------------------
|
||||
namespace plugin\saiadmin\app\logic\system;
|
||||
|
||||
use plugin\saiadmin\exception\ApiException;
|
||||
|
||||
/**
|
||||
* 后台操作指南逻辑(读写 server/docs/ADMIN_GUIDE.md)
|
||||
*/
|
||||
class SystemAdminGuideLogic
|
||||
{
|
||||
private const GUIDE_FILENAME = 'ADMIN_GUIDE.md';
|
||||
|
||||
/**
|
||||
* 获取指南 Markdown 文件绝对路径
|
||||
*/
|
||||
public function getFilePath(): string
|
||||
{
|
||||
return base_path() . DIRECTORY_SEPARATOR . 'docs' . DIRECTORY_SEPARATOR . self::GUIDE_FILENAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取指南内容
|
||||
* @return array{content: string, file_path: string, update_time: string|null}
|
||||
*/
|
||||
public function read(): array
|
||||
{
|
||||
$filePath = $this->getFilePath();
|
||||
if (! is_file($filePath)) {
|
||||
throw new ApiException('admin guide file not found');
|
||||
}
|
||||
$content = file_get_contents($filePath);
|
||||
if ($content === false) {
|
||||
throw new ApiException('failed to read admin guide file');
|
||||
}
|
||||
|
||||
return [
|
||||
'content' => $content,
|
||||
'file_path' => 'docs/' . self::GUIDE_FILENAME,
|
||||
'update_time' => date('Y-m-d H:i:s', filemtime($filePath)),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存指南内容到 Markdown 文件
|
||||
* @param string $content
|
||||
* @return array{content: string, file_path: string, update_time: string}
|
||||
*/
|
||||
public function save(string $content): array
|
||||
{
|
||||
$filePath = $this->getFilePath();
|
||||
$dir = dirname($filePath);
|
||||
if (! is_dir($dir) && ! mkdir($dir, 0755, true) && ! is_dir($dir)) {
|
||||
throw new ApiException('failed to create docs directory');
|
||||
}
|
||||
|
||||
$result = file_put_contents($filePath, $content, LOCK_EX);
|
||||
if ($result === false) {
|
||||
throw new ApiException('failed to save admin guide file');
|
||||
}
|
||||
|
||||
clearstatcache(true, $filePath);
|
||||
|
||||
return [
|
||||
'content' => $content,
|
||||
'file_path' => 'docs/' . self::GUIDE_FILENAME,
|
||||
'update_time' => date('Y-m-d H:i:s', filemtime($filePath)),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -81,6 +81,10 @@ Route::group('/core', function () {
|
||||
Route::delete("/logs/deleteOperLog", [\plugin\saiadmin\app\controller\system\SystemLogController::class, 'deleteOperLog']);
|
||||
fastRoute("email", \plugin\saiadmin\app\controller\system\SystemMailController::class);
|
||||
|
||||
// 后台操作指南
|
||||
Route::get("/adminGuide/read", [\plugin\saiadmin\app\controller\system\SystemAdminGuideController::class, 'read']);
|
||||
Route::post("/adminGuide/save", [\plugin\saiadmin\app\controller\system\SystemAdminGuideController::class, 'save']);
|
||||
|
||||
// 服务管理
|
||||
Route::get("/server/monitor", [\plugin\saiadmin\app\controller\system\SystemServerController::class, 'monitor']);
|
||||
Route::get("/server/cache", [\plugin\saiadmin\app\controller\system\SystemServerController::class, 'cache']);
|
||||
|
||||
BIN
server/public/docs/picture/guide_01.png
Normal file
|
After Width: | Height: | Size: 122 KiB |
BIN
server/public/docs/picture/guide_02.png
Normal file
|
After Width: | Height: | Size: 92 KiB |
BIN
server/public/docs/picture/guide_03.png
Normal file
|
After Width: | Height: | Size: 156 KiB |
BIN
server/public/docs/picture/guide_04.png
Normal file
|
After Width: | Height: | Size: 141 KiB |
BIN
server/public/docs/picture/guide_05.png
Normal file
|
After Width: | Height: | Size: 166 KiB |
BIN
server/public/docs/picture/guide_06.png
Normal file
|
After Width: | Height: | Size: 671 KiB |
BIN
server/public/docs/picture/guide_07.png
Normal file
|
After Width: | Height: | Size: 121 KiB |
BIN
server/public/docs/picture/guide_08.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
server/public/docs/picture/guide_09.png
Normal file
|
After Width: | Height: | Size: 153 KiB |
BIN
server/public/docs/picture/guide_10.png
Normal file
|
After Width: | Height: | Size: 92 KiB |
BIN
server/public/docs/picture/guide_11.png
Normal file
|
After Width: | Height: | Size: 141 KiB |
BIN
server/public/docs/picture/guide_12.png
Normal file
|
After Width: | Height: | Size: 92 KiB |
BIN
server/public/docs/picture/guide_13.png
Normal file
|
After Width: | Height: | Size: 160 KiB |
BIN
server/public/docs/picture/guide_14.png
Normal file
|
After Width: | Height: | Size: 134 KiB |
BIN
server/public/docs/picture/guide_15.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
server/public/docs/picture/guide_16.png
Normal file
|
After Width: | Height: | Size: 146 KiB |
BIN
server/public/docs/picture/guide_17.png
Normal file
|
After Width: | Height: | Size: 183 KiB |
BIN
server/public/docs/picture/guide_18.png
Normal file
|
After Width: | Height: | Size: 177 KiB |
BIN
server/public/docs/picture/guide_19.png
Normal file
|
After Width: | Height: | Size: 110 KiB |
BIN
server/public/docs/picture/guide_20.png
Normal file
|
After Width: | Height: | Size: 157 KiB |
BIN
server/public/docs/picture/guide_21.png
Normal file
|
After Width: | Height: | Size: 125 KiB |
BIN
server/public/docs/picture/guide_22.png
Normal file
|
After Width: | Height: | Size: 112 KiB |
BIN
server/public/docs/picture/guide_23.png
Normal file
|
After Width: | Height: | Size: 145 KiB |
BIN
server/public/docs/picture/guide_24.png
Normal file
|
After Width: | Height: | Size: 104 KiB |
BIN
server/public/docs/picture/guide_25.png
Normal file
|
After Width: | Height: | Size: 161 KiB |
BIN
server/public/docs/picture/guide_26.png
Normal file
|
After Width: | Height: | Size: 113 KiB |