管理系统的操作日志如何做成通用的模块一直是个让我头疼的问题,不过看了博客园里的某篇文章后,现在基本解决了。
相关文章链接:《系统操作日志设计》
在开始做之前,必须把两个日志分清楚,那就是普通操作日志和业务操作日志,这两者有何区别?
在我理解,普通操作日志就是单表的操作记录,而业务操作日志则就是一系列的普通操作日志的集合。
打个比方,用户需要购买一样宝贝,已经到了下单那步,下单就是个业务,这个业务背后就是一系列的业务,如:
生成订单 → 生成商品快照 → 发送一条站内信 → 删除购物车里对应宝贝
这样一个下单操作就包含了4部分,可以把这4部分看成是4张表,分别对这4张表进行对应的操作,就实现了业务。
但今天我要讲的不是业务操作日志,因为不同项目的业务不尽相同,所以它无法做成通用模块,而我要讲的,就是普通操作日志。
上面解释了一大段,下面干货就要亮相了,先洗把脸清醒下。
……
首先,哪些地方需要记录操作日志?执行insert、update、delete这3个操作的时候,就需要进行日志,而日志执行的先后顺序如下
| insert | 在insert后执行 |
| update | 在update前后都要执行,操作前获取操作前数据,操作后获取操作后数据 |
| delete | 在delete前执行 |
顺序清楚后,就来看下我写的一份日志操作类吧,第一版随便写写的,重复代码有点多,还未来得及优化。
class LOG{
protected $primaryid;
protected $tbid;
protected $tbname;
protected $keys;
protected $values;
/**
* 参数说明
* int $tbid 查询指定表的id
* string $tbname 数据库表名
*/
public function insert($tbid, $tbname){
global $db;
//查询表注释
$db->query(\'show table status where name = "\'.$tbname.\'"\');
$tb = $db->fetch();
//插入日志主表
$returnid = $db->insert(0, 2, \'tb_log\', array(
\'adminid = \'.$_SESSION[\'admin\'][\'id\'],
\'type = 1\',
\'tableid = \'.$tbid,
\'tablename = "\'.$tbname.\'"\',
\'comment = "\'.$tb[\'Comment\'].\'"\',
\'dt = now()\'
));
//查询字段注释
$db->query(\'show full columns from \'.$tbname);
$tb = $db->fetchAll();
foreach($tb as $v){
$commentArray[$v[\'Field\']] = $v[\'Comment\'];
}
//查询所有字段信息,插入日志从表
$rs = $db->select(0, 1, $tbname, \'*\', \'and tbid = \'.$tbid);
$keys = array_keys($rs);
$values = array_values($rs);
for($i = 0; $i < count($keys); $i++){
$db->insert(0, 0, \'tb_log_content\', array(
\'logid = \'.$returnid,
\'tbkey = "\'.$keys[$i].\'"\',
\'tbvalue = "\'.$values[$i].\'"\',
\'comment = "\'.$commentArray[$keys[$i]].\'"\'
));
}
}
public function updateStart($tbid, $tbname){
global $db;
//查询表注释
$db->query(\'show table status where name = "\'.$tbname.\'"\');
$tb = $db->fetch();
//插入日志主表
$returnid = $db->insert(0, 2, \'tb_log\', array(
\'adminid = \'.$_SESSION[\'admin\'][\'id\'],
\'type = 2\',
\'tableid = \'.$tbid,
\'tablename = "\'.$tbname.\'"\',
\'comment = "\'.$tb[\'Comment\'].\'"\',
\'dt = now()\'
));
//查询修改前数据信息
$rs = $db->select(0, 1, $tbname, \'*\', \'and tbid = \'.$tbid);
$keys = array_keys($rs);
$values = array_values($rs);
$this->primaryid = $returnid;
$this->tbid = $tbid;
$this->tbname = $tbname;
$this->keys = $keys;
$this->values = $values;
}
public function updateEnd(){
global $db;
//查询字段注释
$db->query(\'show full columns from \'.$this->tbname);
$tb = $db->fetchAll();
foreach($tb as $v){
$commentArray[$v[\'Field\']] = $v[\'Comment\'];
}
//查询修改后数据信息
$rs = $db->select(0, 1, $this->tbname, \'*\', \'and tbid = \'.$this->tbid);
$currentvalues = array_values($rs);
//前后信息进行比较
for($i = 0; $i < count($currentvalues); $i++){
if($this->values[$i] !== $currentvalues[$i]){
$db->insert(0, 0, \'tb_log_content\', array(
\'logid = \'.$this->primaryid,
\'tbkey = "\'.$this->keys[$i].\'"\',
\'tbvalue = "\'.$this->values[$i].\'"\',
\'currenttbvalue = "\'.$currentvalues[$i].\'"\',
\'comment = "\'.$commentArray[$this->keys[$i]].\'"\'
));
}
}
}
public function delete($tbid, $tbname){
global $db;
//查询表注释
$db->query(\'show table status where name = "\'.$tbname.\'"\');
$tb = $db->fetch();
//插入日志主表
$returnid = $db->insert(0, 2, \'tb_log\', array(
\'adminid = \'.$_SESSION[\'admin\'][\'id\'],
\'type = 3\',
\'tableid = \'.$tbid,
\'tablename = "\'.$tbname.\'"\',
\'comment = "\'.$tb[\'Comment\'].\'"\',
\'dt = now()\'
));
//查询字段注释
$db->query(\'show full columns from \'.$tbname);
$tb = $db->fetchAll();
foreach($tb as $v){
$commentArray[$v[\'Field\']] = $v[\'Comment\'];
}
//查询所有字段信息,插入日志从表
$rs = $db->select(0, 1, $tbname, \'*\', \'and tbid = \'.$tbid);
$keys = array_keys($rs);
$values = array_values($rs);
for($i = 0; $i < count($keys); $i++){
$db->insert(0, 0, \'tb_log_content\', array(
\'logid = \'.$returnid,
\'tbkey = "\'.$keys[$i].\'"\',
\'tbvalue = "\'.$values[$i].\'"\',
\'comment = "\'.$commentArray[$keys[$i]].\'"\'
));
}
}
}
使用前,需要引入数据库操作类,这是我之前写的一份,可参考《全新的PDO数据库操作类(仅适用Mysql)》。
引入之后,就可以开始使用了。
select
$log->insert(82, \'tb_member\');
update
$log->updateStart(82, \'tb_member\'); //中间放更新操作代码 $log->updateEnd();
delete
$log->delete(82, \'tb_member\');
可以看到,一共只需要两个参数即可,分别是表ID(主键)和表名称。
另外需要强调一点,表注释和字段注释一定要完整,因为记录的信息包含注释,目的就是为了查阅的时候能清楚哪个字段是干什么用的。
下面就看下成品吧
最后把表结构分享下,一共2张表,一张主表一张从表,主表记录操作表及操作人等信息,从表记录操作的表字段信息。
-- ---------------------------- -- Table structure for `tb_log` -- ---------------------------- CREATE TABLE `tb_log` ( `tbid` bigint(20) NOT NULL AUTO_INCREMENT, `adminid` bigint(20) DEFAULT NULL COMMENT \'管理员id\', `type` tinyint(4) DEFAULT \'1\' COMMENT \'操作类型:1新增2修改3删除\', `tableid` bigint(20) DEFAULT NULL, `tablename` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT \'表名\', `comment` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, `dt` datetime DEFAULT NULL, PRIMARY KEY (`tbid`) ) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; -- ---------------------------- -- Table structure for `tb_log_content` -- ---------------------------- CREATE TABLE `tb_log_content` ( `tbid` bigint(20) NOT NULL AUTO_INCREMENT, `logid` bigint(20) DEFAULT NULL, `tbkey` longtext COLLATE utf8_unicode_ci, `tbvalue` longtext COLLATE utf8_unicode_ci, `currenttbvalue` longtext COLLATE utf8_unicode_ci, `comment` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`tbid`) ) ENGINE=InnoDB AUTO_INCREMENT=109 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;