getConnection()->transaction(function () use ($actor, $key, $action, $operation, $authorize, $hash) { $user = MiniUser::query()->where('id', $actor->userId)->lockForUpdate()->first(); if (!$user || (int) $user->status !== 1) { throw new ApiException(40101, '登录状态已失效', 401); } // Grant changes use the same user lock. Replays must also retain current permission. $authorize(); $old = MiniCommand::query()->where([ 'user_id' => $actor->userId, 'request_key' => $key, ])->first(); if ($old) { if ($old->action !== $action || !hash_equals($old->payload_hash, $hash)) { throw new ApiException(40901, '重复请求的内容发生变化', 409); } return json_decode($old->result, true, 512, JSON_THROW_ON_ERROR) + ['replayed' => true]; } try { $result = $operation(); } catch (\plugin\saiadmin\exception\ApiException $e) { throw new ApiException(40902, $e->getMessage(), 409); } MiniCommand::query()->insert([ 'user_id' => $actor->userId, 'request_key' => $key, 'action' => $action, 'payload_hash' => $hash, 'result' => json_encode($result, JSON_THROW_ON_ERROR), 'created_at' => date('Y-m-d H:i:s'), ]); self::audit('mini', (int) $actor->userId, $action, (int) ($result['id'] ?? 0), [ 'request_key' => $key, 'payload_hash' => $hash, ]); return $result; }); } public static function audit(string $type, int $actor, string $action, int $resource, array $data): void { MiniAudit::query()->insert([ 'actor_type' => $type, 'actor_id' => $actor, 'action' => $action, 'resource_id' => $resource, 'data' => json_encode($data, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR), 'created_at' => date('Y-m-d H:i:s'), ]); } }