;rgb:0000/0000/0000
gengx
This commit is contained in:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user