;rgb:0000/0000/0000

gengx
This commit is contained in:
gr
2026-09-24 11:03:44 +08:00
parent bd22e5f476
commit 68fc86a167
10 changed files with 1257 additions and 34 deletions
+28 -3
View File
@@ -20,6 +20,9 @@ class Dispatcher
{
private const SQL_TIMEOUT_SECONDS = 10;
/** 审核文件可为重型统计(业绩看板等)单独放宽单条 SQL 超时(resource.timeout),上限 60 秒 */
private const SQL_TIMEOUT_MAX_SECONDS = 60;
/**
* 执行一个资源。返回后台接口的原始信封 ['code' => 1|0, 'msg' => ..., 'data' => ...]。
*/
@@ -34,7 +37,7 @@ class Dispatcher
$app->instance('request', $request);
$app->setNamespace('app\\adminapi');
$app->http->name('adminapi');
$readOnly = self::begin();
$readOnly = self::begin(self::timeoutFor($resource));
try {
if (!empty($resource['guard']) && $resource['guard'] !== 'builtin') {
$denied = self::checkGuard($identity, $resource, $params);
@@ -279,8 +282,30 @@ class Dispatcher
return '查询失败(' . (new \ReflectionClass($e))->getShortName() . '),请换个条件或联系管理员查看服务器日志';
}
/**
* 在同样的只读事务 + SQL 超时保护里执行 MCP 模块自己的只读查询(如业绩趋势的聚合),异常原样抛出。
*/
public static function readOnly(callable $fn, int $timeoutSeconds = self::SQL_TIMEOUT_SECONDS)
{
$readOnly = self::begin(max(1, min(self::SQL_TIMEOUT_MAX_SECONDS, $timeoutSeconds)));
try {
return $fn();
} catch (\Throwable $e) {
Log::error(sprintf('[ai_mcp] 只读查询失败: %s @ %s:%d', $e->getMessage(), $e->getFile(), $e->getLine()));
throw new McpException(self::describe($e), 'error');
} finally {
self::end($readOnly);
}
}
private static function timeoutFor(array $resource): int
{
$seconds = (int) ($resource['timeout'] ?? self::SQL_TIMEOUT_SECONDS);
return max(1, min(self::SQL_TIMEOUT_MAX_SECONDS, $seconds));
}
/** 开启只读事务 + SQL 超时 */
private static function begin(): bool
private static function begin(int $timeoutSeconds = self::SQL_TIMEOUT_SECONDS): bool
{
$readOnly = true;
try {
@@ -289,7 +314,7 @@ class Dispatcher
$readOnly = false;
Log::warning('[ai_mcp] 数据库不支持只读事务,改为事务回滚保护: ' . $e->getMessage());
}
foreach (['SET SESSION max_execution_time = ' . (self::SQL_TIMEOUT_SECONDS * 1000), 'SET SESSION max_statement_time = ' . self::SQL_TIMEOUT_SECONDS] as $sql) {
foreach (['SET SESSION max_execution_time = ' . ($timeoutSeconds * 1000), 'SET SESSION max_statement_time = ' . $timeoutSeconds] as $sql) {
try {
Db::execute($sql);
break;