Files
hyperf-micro-svc/app/Aspect/AdminReturnLogAspect.php
2026-02-25 09:53:21 +08:00

156 lines
4.9 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
declare(strict_types=1);
namespace App\Aspect;
use App\Annotation\ResponseFormat;
use App\Common\Trait\AdminUserTrait;
use App\Common\Trait\ClientIpTrait;
use App\Lib\Log\Logger;
use App\Model\AdminUser;
use App\Model\AdminUserOperationLog;
use Hyperf\Context\Context;
use Hyperf\Coroutine\Coroutine;
use Hyperf\Di\Annotation\Aspect;
use Hyperf\Di\Aop\AbstractAspect;
use Hyperf\Di\Aop\ProceedingJoinPoint;
use Hyperf\Di\Exception\Exception;
use Hyperf\HttpServer\Contract\RequestInterface;
#[Aspect(priority: 1)]
class AdminReturnLogAspect extends AbstractAspect
{
use ClientIpTrait;
use AdminUserTrait;
/**
* 切入类
* @var array|\class-string[]
*/
public array $classes = [];
/**
* 切入方法 - 拦截所有带 ResponseFormat 注解且 format='admin' 的方法
* @var string[]
*/
public array $annotations = [
ResponseFormat::class,
];
/**
* @param Logger $logger
* @param RequestInterface $request
* @param AdminUserOperationLog $adminUserOperationLogModel
*/
public function __construct(
protected readonly Logger $logger,
protected RequestInterface $request,
protected readonly AdminUserOperationLog $adminUserOperationLogModel,
) {}
/**
* @param ProceedingJoinPoint $proceedingJoinPoint
* @return mixed
* @throws Exception
*/
public function process(ProceedingJoinPoint $proceedingJoinPoint)
{
// 检查是否是 admin 格式
$annotation = $proceedingJoinPoint->getAnnotationMetadata()->class[ResponseFormat::class]
?? $proceedingJoinPoint->getAnnotationMetadata()->method[ResponseFormat::class] ?? null;
// 只处理 admin 格式的请求
if (!$annotation || $annotation->format !== 'admin') {
return $proceedingJoinPoint->process();
}
// 获取当前登录的管理员ID在 process 方法中获取,确保 Context 已设置)
$adminId = Context::get('current_admin_id', 0);
// 没登录不记录日志
if ($adminId <= 0) {
return $proceedingJoinPoint->process();
}
// 获取请求数据
$requestData = $this->request->all();
// 执行原方法并获取返回值
$responseData = $proceedingJoinPoint->process();
// 写日志
$this->writeOperationLog($adminId, $requestData, $responseData);
// 返回
return $responseData;
}
/**
* @param int $adminId
* @param array $requestData
* @param array $responseData
* @return void
*/
private function writeOperationLog(int $adminId, array $requestData = [], array $responseData = []): void
{
$router = (string) $this->request->getUri();
$method = $this->request->getMethod();
$ip = $this->getClientIp();
$ipStr = current($ip) ?: '0.0.0.0';
$context = [
'user_id' => $adminId,
'method' => $method,
'router' => $router,
'ip' => $ip,
'request_data' => $requestData,
'response_data' => $responseData,
];
// 先记录日志
$this->logger->request()->info('admin_request_log', $context);
// 获取用户信息
$adminUserInfo = $this->getAdminUserInfo($adminId);
$username = $adminUserInfo?->username ?? '';
// 异步写入数据库
try {
Coroutine::create(function () use ($adminId, $username, $router, $method, $ipStr) {
try {
$this->adminUserOperationLogModel->create([
'admin_user_id' => $adminId,
'username' => $username,
'method' => $method,
'router' => $router,
'service_name' => '',
'ip' => $ipStr,
]);
} catch (\Throwable $e) {
$this->logger->error()->error('写入操作日志失败: ' . $e->getMessage(), [
'admin_id' => $adminId,
'router' => $router,
'exception' => $e->getMessage(),
]);
}
});
} catch (\Throwable $e) {
// 协程创建失败,降级为同步写入
$this->logger->error()->error('创建协程失败,同步写入日志: ' . $e->getMessage());
try {
$this->adminUserOperationLogModel->create([
'admin_user_id' => $adminId,
'username' => $username,
'method' => $method,
'router' => $router,
'service_name' => '',
'ip' => $ipStr,
]);
} catch (\Throwable $e2) {
$this->logger->error()->error('同步写入操作日志也失败: ' . $e2->getMessage());
}
}
}
}