first commot

This commit is contained in:
2026-01-26 09:29:36 +08:00
commit dbb40c38c4
189 changed files with 20198 additions and 0 deletions
@@ -0,0 +1,256 @@
<?php
declare(strict_types=1);
namespace app\admin\controller\OperationLog;
use app\admin\BaseController;
use think\facade\Request;
use think\response\Json;
use app\model\OperationLog;
use app\model\AdminUser;
class OperationLogController extends BaseController
{
/**
* 获取操作日志列表
* @return Json
*/
public function getOperationLogs()
{
try {
$page = Request::param('page/d', 1);
$pageSize = Request::param('pageSize/d', 20);
$keyword = Request::param('keyword/s', '');
$module = Request::param('module/s', '');
$action = Request::param('action/s', '');
$status = Request::param('status/s', '');
$startTime = Request::param('startTime/s', '');
$endTime = Request::param('endTime/s', '');
$query = OperationLog::where('delete_time', null);
// 关键词搜索(用户姓名、URL
if ($keyword) {
$query->where(function ($q) use ($keyword) {
$q
->whereOr('user_name', 'like', "%{$keyword}%")
->whereOr('url', 'like', "%{$keyword}%");
});
}
// 模块筛选
if ($module) {
$query->where('module', $module);
}
// 操作动作筛选
if ($action) {
$query->where('action', $action);
}
// 状态筛选
if ($status !== '') {
$query->where('status', $status);
}
// 时间范围筛选
if ($startTime) {
$query->where('create_time', '>=', $startTime);
}
if ($endTime) {
$query->where('create_time', '<=', $endTime);
}
// 获取总数
$total = $query->count();
// 分页查询
$list = $query->order('id', 'desc')
->page($page, $pageSize)
->select()
->toArray();
// 获取所有唯一的 user_id
$userIds = array_unique(array_column($list, 'user_id'));
$userIds = array_filter($userIds); // 过滤掉0和null
// 批量查询用户信息
$users = [];
if (!empty($userIds)) {
$userList = AdminUser::whereIn('id', $userIds)
->where('delete_time', null)
->field('id, name')
->select()
->toArray();
// 转换为以 id 为键的数组,方便查找
foreach ($userList as $user) {
$users[$user['id']] = [
'name' => $user['name']
];
}
}
// 将用户信息合并到日志列表中
foreach ($list as &$item) {
if (isset($users[$item['user_id']])) {
$item['user_name'] = $users[$item['user_id']]['name'];
} else {
$item['user_name'] = $item['user_name'] ?? '';
}
}
unset($item); // 释放引用
return json([
'code' => 200,
'msg' => '获取成功',
'data' => [
'list' => $list,
'total' => $total,
'page' => $page,
'pageSize' => $pageSize
]
]);
} catch (\Exception $e) {
return json([
'code' => 500,
'msg' => '获取操作日志失败: ' . $e->getMessage()
]);
}
}
/**
* 获取操作日志详情
* @param int $id
* @return Json
*/
public function getOperationLogDetail(int $id)
{
try {
$log = OperationLog::where('id', $id)
->where('delete_time', null)
->find();
if (!$log) {
return json([
'code' => 404,
'msg' => '操作日志不存在'
]);
}
$logData = $log->toArray();
$this->logSuccess('操作日志', '查看操作日志详情', $logData);
return json([
'code' => 200,
'msg' => '获取成功',
'data' => $logData
]);
} catch (\Exception $e) {
$this->logFail('操作日志', '查看操作日志详情', $e->getMessage());
return json([
'code' => 500,
'msg' => '获取操作日志详情失败: ' . $e->getMessage()
]);
}
}
/**
* 删除操作日志
* @param int $id
* @return Json
*/
public function deleteOperationLog(int $id)
{
try {
$log = OperationLog::where('id', $id)
->where('delete_time', null)
->find();
if (!$log) {
return json([
'code' => 404,
'msg' => '操作日志不存在'
]);
}
// 软删除
$log->delete();
return json([
'code' => 200,
'msg' => '删除成功'
]);
} catch (\Exception $e) {
return json([
'code' => 500,
'msg' => '删除失败: ' . $e->getMessage()
]);
}
}
/**
* 批量删除操作日志
* @return Json
*/
public function batchDeleteOperationLogs()
{
try {
$ids = Request::param('ids/a', []);
if (empty($ids)) {
return json([
'code' => 400,
'msg' => '请选择要删除的操作日志'
]);
}
OperationLog::whereIn('id', $ids)
->where('delete_time', null)
->delete();
return json([
'code' => 200,
'msg' => '批量删除成功'
]);
} catch (\Exception $e) {
return json([
'code' => 500,
'msg' => '批量删除失败: ' . $e->getMessage()
]);
}
}
/**
* 获取操作统计信息(模块、动作等)
* @return Json
*/
public function getOperationStatistics()
{
try {
// 获取模块列表
$modules = OperationLog::where('delete_time', null)
->group('module')
->column('module');
// 获取动作列表
$actions = OperationLog::where('delete_time', null)
->group('action')
->column('action');
return json([
'code' => 200,
'msg' => '获取成功',
'data' => [
'modules' => $modules,
'actions' => $actions
]
]);
} catch (\Exception $e) {
return json([
'code' => 500,
'msg' => '获取统计信息失败: ' . $e->getMessage()
]);
}
}
}
@@ -0,0 +1,96 @@
<?php
namespace app\admin\controller\OperationLog;
use app\model\OperationLog;
use think\facade\Request;
use think\facade\Session;
/**
* 操作日志记录助手类
*/
class OperationLogHelper
{
/**
* 记录操作日志
* @param string $module 操作模块
* @param string $action 操作动作
* @param array $requestData 请求数据
* @param array $responseData 响应数据
* @param int $status 操作状态:1-成功,0-失败
* @param string $errorMessage 错误信息
* @param float $executionTime 执行时间(秒)
* @return bool
*/
public static function log(
string $module,
string $action,
array $requestData = [],
array $responseData = [],
int $status = 1,
string $errorMessage = '',
float $executionTime = 0.0
): bool {
try {
// 获取用户信息
$userInfo = Session::get('user');
$userId = $userInfo['id'] ?? 0;
$userAccount = $userInfo['account'] ?? '';
$userName = $userInfo['name'] ?? '';
// 获取请求信息
$method = Request::method();
$url = Request::url(true);
$ip = Request::ip();
$userAgent = Request::header('user-agent', '');
// 过滤敏感信息(如密码)
$filteredRequestData = self::filterSensitiveData($requestData);
// 记录日志
OperationLog::create([
'user_id' => $userId,
'user_account' => $userAccount,
'user_name' => $userName,
'module' => $module,
'action' => $action,
'method' => $method,
'url' => $url,
'ip' => $ip,
'user_agent' => $userAgent,
'request_data' => !empty($filteredRequestData) ? json_encode($filteredRequestData, JSON_UNESCAPED_UNICODE) : null,
'response_data' => !empty($responseData) ? json_encode($responseData, JSON_UNESCAPED_UNICODE) : null,
'status' => $status,
'error_message' => $errorMessage,
'execution_time' => $executionTime,
]);
return true;
} catch (\Exception $e) {
// 记录日志失败不应该影响主流程,只记录错误
error_log('操作日志记录失败: ' . $e->getMessage());
return false;
}
}
/**
* 过滤敏感数据
* @param array $data
* @return array
*/
private static function filterSensitiveData(array $data): array
{
$sensitiveKeys = ['password', 'pwd', 'token', 'api_key', 'secret'];
foreach ($data as $key => $value) {
if (in_array(strtolower($key), $sensitiveKeys)) {
$data[$key] = '***';
} elseif (is_array($value)) {
$data[$key] = self::filterSensitiveData($value);
}
}
return $data;
}
}
@@ -0,0 +1,97 @@
<?php
namespace app\admin\controller\OperationLog;
use think\facade\Request;
use app\model\OperationLog;
use app\service\JwtService;
/**
* 操作日志记录器(JWT版)
*/
class OperationLogger
{
private static function getUserFromToken(): array
{
return JwtService::getUserFromHeader(Request::header('Authorization', ''));
}
public static function record(
string $module,
string $action,
array $requestData = [],
array $responseData = [],
int $status = 1,
string $errorMessage = '',
array $userInfo = []
): bool {
try {
if (empty($userInfo)) {
$userInfo = self::getUserFromToken();
}
$userId = $userInfo['id'] ?? 0;
$userAccount = $userInfo['account'] ?? '';
$userName = $userInfo['name'] ?? '';
if (empty($requestData)) {
$requestData = Request::param();
}
$method = Request::method();
$url = Request::url(true);
$ip = Request::ip();
$userAgent = Request::header('user-agent', '');
$filteredRequestData = self::filterSensitiveData($requestData);
OperationLog::create([
'user_id' => $userId,
'user_account' => $userAccount,
'user_name' => $userName,
'module' => $module,
'action' => $action,
'method' => $method,
'url' => $url,
'ip' => $ip,
'user_agent' => $userAgent,
'request_data' => !empty($filteredRequestData) ? json_encode($filteredRequestData, JSON_UNESCAPED_UNICODE) : null,
'response_data' => !empty($responseData) ? json_encode($responseData, JSON_UNESCAPED_UNICODE) : null,
'status' => $status,
'error_message' => $errorMessage,
'execution_time' => 0.0,
]);
return true;
} catch (\Exception $e) {
error_log('操作日志记录失败: ' . $e->getMessage());
return false;
}
}
public static function success(string $module, string $action, array $responseData = [], array $userInfo = []): bool
{
return self::record($module, $action, [], $responseData, 1, '', $userInfo);
}
public static function fail(string $module, string $action, string $errorMessage, array $userInfo = []): bool
{
return self::record($module, $action, [], [], 0, $errorMessage, $userInfo);
}
private static function filterSensitiveData(array $data): array
{
$sensitiveKeys = ['password', 'pwd', 'token', 'api_key', 'secret'];
foreach ($data as $key => $value) {
if (in_array(strtolower($key), $sensitiveKeys)) {
$data[$key] = '***';
} elseif (is_array($value)) {
$data[$key] = self::filterSensitiveData($value);
}
}
return $data;
}
}