You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于CodeIgniter实现Web应用用户CRUD及全活动记录方案咨询

刚好之前在CodeIgniter项目里做过用户活动日志的功能,结合你提到的GLPI事件表的思路,给你梳理几个可行的实现方案,都是实际项目里验证过的:

一、先搞定数据库表设计(对标GLPI事件表)

首先得把日志表的结构搭好,参考GLPI的事件表逻辑,我们可以创建一个audit_logs表,核心字段如下:

  • id:主键自增,唯一标识每条日志
  • user_id:操作用户的ID(关联你的用户表,方便追溯是谁操作的)
  • action_type:操作类型,用枚举值比如add/edit/delete/login/logout/export,清晰区分不同操作
  • item_type:操作的业务对象类型,比如ticket/user/asset,对应你系统里的业务表
  • item_id:操作对象的ID(关联对应业务表,比如编辑了ID为12的工单,这里就存12)
  • action_details:操作详情,建议存JSON格式,比如修改了哪些字段、新旧值对比,或者删除的内容快照,灵活存储细节
  • ip_address:用户操作时的IP地址,方便定位操作来源
  • created_at:操作时间(默认自动填充当前时间)
  • user_agent:用户的浏览器/设备信息(可选,辅助排查问题)
二、CodeIgniter里的具体实现方案

1. 用钩子(Hooks)做全局自动监听

这种方式适合批量记录常规的增删改操作,不用每个控制器都写重复代码,一劳永逸。

步骤:

  1. 先开启钩子:打开application/config/config.php,把$config['enable_hooks']设为TRUE
  2. 定义钩子:打开application/config/hooks.php,配置要触发的钩子和对应的处理类
  3. 编写钩子处理类:在application/hooks目录下创建AuditLog.php,实现日志记录逻辑

钩子配置示例(hooks.php):

$hook['post_controller'] = [
    'class' => 'AuditLog',
    'function' => 'log_activity',
    'filename' => 'AuditLog.php',
    'filepath' => 'hooks'
];

钩子处理类示例(AuditLog.php):

class AuditLog {
    public function log_activity() {
        // 获取CI实例
        $CI =& get_instance();
        
        // 排除不需要记录的控制器/方法,比如登录页、静态展示页
        $exclude_controllers = ['login', 'welcome'];
        $exclude_methods = ['index', 'view', 'download'];
        if (in_array($CI->router->class, $exclude_controllers) || in_array($CI->router->method, $exclude_methods)) {
            return;
        }
        
        // 未登录用户不记录
        $user_id = $CI->session->userdata('user_id');
        if (!$user_id) return;
        
        // 推断操作类型和业务对象
        $action_type = '';
        $item_type = $CI->router->class; // 控制器名作为业务对象类型,比如Tickets控制器对应ticket
        $item_id = $CI->uri->segment(3); // 从URL获取操作对象ID,比如/tickets/edit/12里的12
        $action_details = '';
        
        switch($CI->router->method) {
            case 'add':
                $action_type = 'add';
                // 添加操作后,获取刚插入的记录ID
                $item_id = $CI->db->insert_id();
                $action_details = "新增了{$item_type}记录,ID:{$item_id}";
                break;
            case 'edit':
                $action_type = 'edit';
                // 对比新旧数据,记录修改的字段
                $old_data = $CI->db->get_where($item_type, ['id' => $item_id])->row_array();
                $new_data = $CI->input->post();
                // 过滤掉不需要对比的字段(比如csrf_token)
                unset($new_data['csrf_token_name']);
                $changed_fields = array_keys(array_diff_assoc($new_data, $old_data));
                $action_details = json_encode([
                    'old_data' => $old_data,
                    'new_data' => $new_data,
                    'changed_fields' => $changed_fields
                ]);
                break;
            case 'delete':
                $action_type = 'delete';
                // 记录删除前的快照
                $deleted_data = $CI->db->get_where($item_type, ['id' => $item_id])->row_array();
                $action_details = json_encode(['deleted_data' => $deleted_data]);
                break;
            default:
                // 自定义操作,比如用户修改密码、导出数据
                $action_type = 'custom';
                $action_details = "执行了自定义操作:{$CI->router->method}";
        }
        
        // 插入日志表
        $log_data = [
            'user_id' => $user_id,
            'action_type' => $action_type,
            'item_type' => $item_type,
            'item_id' => $item_id,
            'action_details' => $action_details,
            'ip_address' => $CI->input->ip_address(),
            'user_agent' => $CI->input->user_agent(),
            'created_at' => date('Y-m-d H:i:s')
        ];
        $CI->db->insert('audit_logs', $log_data);
    }
}

2. 封装日志模型,手动调用记录特殊事件

如果有些操作是特殊场景(比如用户登录成功、导出报表),全局钩子不好自动判断,那可以封装一个日志模型,在需要的地方手动调用,灵活性更高。

日志模型示例(application/models/Audit_log_model.php):

class Audit_log_model extends CI_Model {
    public function add_log($user_id, $action_type, $item_type, $item_id, $action_details = '') {
        $log_data = [
            'user_id' => $user_id,
            'action_type' => $action_type,
            'item_type' => $item_type,
            'item_id' => $item_id,
            'action_details' => $action_details,
            'ip_address' => $this->input->ip_address(),
            'user_agent' => $this->input->user_agent(),
            'created_at' => date('Y-m-d H:i:s')
        ];
        return $this->db->insert('audit_logs', $log_data);
    }
}

控制器里手动调用示例:

class Auth extends CI_Controller {
    public function login() {
        // 登录验证逻辑
        if ($this->auth->validate_login($username, $password)) {
            $user_id = $this->session->userdata('user_id');
            // 记录登录日志
            $this->load->model('audit_log_model');
            $this->audit_log_model->add_log(
                $user_id,
                'login',
                'user',
                $user_id,
                "用户登录成功,IP:{$this->input->ip_address()}"
            );
            redirect('dashboard');
        }
    }
}

3. 用自定义基类控制器统一处理

如果你的所有业务控制器都继承自自定义的基类,那可以把日志逻辑封装在基类里,这样代码更统一,维护起来也方便。

自定义基类示例(application/core/MY_Controller.php):

class MY_Controller extends CI_Controller {
    protected $audit_log_model;
    
    public function __construct() {
        parent::__construct();
        // 提前加载日志模型
        $this->load->model('audit_log_model');
    }
    
    /**
     * 封装日志记录方法,子类直接调用
     */
    protected function log_activity($action_type, $item_type, $item_id, $action_details = '') {
        $user_id = $this->session->userdata('user_id');
        if (!$user_id) return; // 未登录用户不记录
        $this->audit_log_model->add_log($user_id, $action_type, $item_type, $item_id, $action_details);
    }
}

业务控制器调用示例:

class Tickets extends MY_Controller {
    public function delete($id) {
        // 获取要删除的工单信息
        $ticket_data = $this->db->get_where('tickets', ['id' => $id])->row_array();
        // 执行删除操作
        $this->db->delete('tickets', ['id' => $id]);
        
        // 调用基类的日志方法记录
        $this->log_activity(
            'delete',
            'ticket',
            $id,
            json_encode(['deleted_ticket' => $ticket_data])
        );
        
        redirect('tickets');
    }
}
三、几个优化小建议
  • 日志展示优化:做一个后台日志查询页面,支持按用户、操作类型、时间范围筛选,把JSON格式的详情解析成友好的对比视图(比如修改前后的字段值高亮显示)
  • 避免重复记录:只记录POST请求,排除GET请求(比如页面刷新),防止日志冗余
  • 性能优化:如果日志量很大,可以用异步队列(比如结合Redis)来记录日志,避免插入日志拖慢主业务的响应速度
  • 敏感数据保护:比如用户密码修改、银行卡信息变更,不要记录明文数据,只记录“修改了密码”这类模糊描述
  • 权限控制:日志页面只开放给管理员,避免普通用户查看他人的操作记录

内容的提问来源于stack exchange,提问作者Demian

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 06:22:02