Files
think-plugs-recorder/src/Service.php
2025-08-13 10:39:09 +08:00

333 lines
10 KiB
PHP

<?php
namespace jerryyan\recorder;
use think\admin\Plugin;
use jerryyan\recorder\helper\ViewHelper;
/**
* 操作记录插件服务类
* Class Service
* @package jerryyan\recorder
*/
class Service extends Plugin
{
/**
* 插件编码
* @var string
*/
protected $appCode = 'recorder';
/**
* 插件名称
* @var string
*/
protected $appName = '操作记录';
/**
* 定义插件菜单
* @return array
*/
public static function menu(): array
{
// 暂时不提供后台菜单,仅作为服务插件使用
return [
[
'name' => '操作记录',
'subs' => [
[
'name' => '记录查询',
'icon' => 'layui-icon layui-icon-search',
'node' => 'recorder/index/index'
],
[
'name' => '记录统计',
'icon' => 'layui-icon layui-icon-chart',
'node' => 'recorder/stats/index'
]
]
]
];
}
/**
* 插件服务注册
*/
public function register()
{
// 注册事件监听器
$this->registerEventListeners();
}
/**
* 插件启动
*/
public function boot(): void
{
// 插件启动时的初始化操作
$this->initializePlugin();
// 注册模板函数
$this->registerTemplateFunctions();
}
/**
* 注册模板函数
*/
protected function registerTemplateFunctions()
{
try {
// 函数已经通过composer autoload的files自动加载
// 这里只需要确认函数是否正确加载
if (!function_exists('recorder_get_records')) {
// 如果通过autoload加载失败,手动加载
require_once __DIR__ . '/functions.php';
}
} catch (\Exception $e) {
trace("注册模板函数失败: " . $e->getMessage(), 'error');
}
}
/**
* 注册事件监听器
*/
protected function registerEventListeners()
{
// 可以在这里注册模型事件监听器,自动记录CRUD操作
// 例如:监听模型的增删改事件,自动记录操作日志
try {
// 注册全局模型事件监听
\think\facade\Event::listen('think\\model\\concern\\ModelEvent', function($event, $model) {
$this->handleModelEvent($event, $model);
});
} catch (\Exception $e) {
trace("注册事件监听器失败: " . $e->getMessage(), 'error');
}
}
/**
* 处理模型事件
* @param string $event
* @param $model
*/
protected function handleModelEvent(string $event, $model)
{
// 根据配置决定是否自动记录模型操作
$config = \jerryyan\recorder\service\RecorderService::getConfig('auto_record', []);
if (!($config['enabled'] ?? false)) {
return;
}
try {
$operationMap = [
'after_insert' => '创建',
'after_update' => '更新',
'after_delete' => '删除',
];
if (isset($operationMap[$event])) {
$operationType = $operationMap[$event];
// 检查是否排除此操作类型
$excludeOperations = $config['exclude_operations'] ?? [];
if (in_array($operationType, $excludeOperations)) {
return;
}
$tableName = $model->getTable();
$primaryKey = $model->getPk();
$primaryValue = $model->$primaryKey ?? '';
\jerryyan\recorder\service\RecorderService::autoRecord([
'operation_type' => $operationType,
'operation_desc' => "{$operationType}{$tableName}记录",
'data_type' => $tableName,
'data_id' => (string)$primaryValue,
]);
}
} catch (\Exception $e) {
trace("自动记录模型操作失败: " . $e->getMessage(), 'error');
}
}
/**
* 初始化插件
*/
protected function initializePlugin()
{
// 设置默认配置
$this->setDefaultConfig();
// 检查数据表是否存在
$this->checkDatabaseTables();
}
/**
* 设置默认配置
*/
protected function setDefaultConfig()
{
$defaultConfig = [
'enabled' => true,
'auto_record' => [
'enabled' => false,
'exclude_operations' => ['读取'],
'exclude_controllers' => [],
],
'retention_days' => 90,
'sensitive_operations' => ['删除', '导出'],
'view' => [
'default_limit' => 10,
'date_format' => 'Y-m-d H:i:s',
'theme' => 'default',
'show_user' => true,
'show_ip' => false,
'compact_mode' => false,
]
];
// 合并用户配置
$userConfig = config('recorder', []);
$finalConfig = array_merge($defaultConfig, $userConfig);
// 更新配置
config(['recorder' => $finalConfig]);
}
/**
* 检查数据表是否存在
*/
protected function checkDatabaseTables()
{
try {
$db = \think\facade\Db::connect();
// 检查操作记录表是否存在
$tables = $db->query("SHOW TABLES LIKE 'jl_recorder_log'");
if (empty($tables)) {
trace("操作记录表不存在,请运行数据库迁移脚本:php think migrate:run", 'notice');
}
} catch (\Exception $e) {
trace("检查数据表失败: " . $e->getMessage(), 'error');
}
}
/**
* 获取插件版本
* @return string
*/
public function getVersion(): string
{
return 'v1.0.0';
}
/**
* 获取插件描述
* @return string
*/
public function getDescription(): string
{
return '提供用户操作记录功能,支持手动记录和自动记录,包含完善的查询和视图展示功能';
}
/**
* 获取插件作者
* @return string
*/
public function getAuthor(): string
{
return 'Jerry Yan';
}
/**
* 插件安装
* @return bool
*/
public function install(): bool
{
try {
// 运行数据库迁移
$this->runMigrations();
// 初始化配置
$this->setDefaultConfig();
trace("操作记录插件安装成功", 'info');
return true;
} catch (\Exception $e) {
trace("操作记录插件安装失败: " . $e->getMessage(), 'error');
return false;
}
}
/**
* 插件卸载
* @return bool
*/
public function uninstall(): bool
{
try {
// 这里可以添加卸载时的清理操作
// 注意:通常不建议自动删除数据表,避免数据丢失
trace("操作记录插件卸载成功", 'info');
return true;
} catch (\Exception $e) {
trace("操作记录插件卸载失败: " . $e->getMessage(), 'error');
return false;
}
}
/**
* 运行数据库迁移
*/
protected function runMigrations()
{
// 这里可以调用Phinx迁移命令
// 或者直接执行SQL创建表结构
try {
// 示例:直接执行建表SQL
$sql = $this->getCreateTableSql();
\think\facade\Db::execute($sql);
} catch (\Exception $e) {
trace("执行数据库迁移失败: " . $e->getMessage(), 'error');
throw $e;
}
}
/**
* 获取建表SQL
* @return string
*/
protected function getCreateTableSql(): string
{
return "
CREATE TABLE IF NOT EXISTS `jl_recorder_log` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '记录ID',
`operation_type` varchar(50) NOT NULL DEFAULT '' COMMENT '操作类型',
`operation_desc` varchar(500) NOT NULL DEFAULT '' COMMENT '操作说明',
`user_id` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '操作用户ID',
`user_nickname` varchar(100) NOT NULL DEFAULT '' COMMENT '操作用户昵称',
`data_type` varchar(100) NOT NULL DEFAULT '' COMMENT '操作数据类型',
`data_id` varchar(100) NOT NULL DEFAULT '' COMMENT '操作数据ID',
`related_type` varchar(100) NOT NULL DEFAULT '' COMMENT '关联数据类型',
`related_id` varchar(100) NOT NULL DEFAULT '' COMMENT '关联数据ID',
`request_method` varchar(10) NOT NULL DEFAULT '' COMMENT '请求方法',
`request_url` varchar(500) NOT NULL DEFAULT '' COMMENT '请求URL',
`request_ip` varchar(50) NOT NULL DEFAULT '' COMMENT '请求IP',
`user_agent` varchar(500) NOT NULL DEFAULT '' COMMENT '用户代理',
`extra_data` text COMMENT '额外数据(JSON格式)',
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_user_id` (`user_id`),
KEY `idx_operation_type` (`operation_type`),
KEY `idx_data_type_id` (`data_type`, `data_id`),
KEY `idx_created_at` (`created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='操作记录表';
";
}
}