爲什麼要做操作日誌?
其實上文也描述了一些,其主要目的就是跟蹤到每一個用戶在系統的操作行爲,如對數據進行查詢、新增、編輯或刪除甚至是登錄等行爲。更進一步的理解可以說是對用戶使用系統情況的跟蹤,對數據的跟蹤防止數據意外刪除、更改時有所記錄,有所依據,以便對數據的還原,從某種程序上可以保護數據的完整性。
系統設計
場景
我們現在有一張表叫Employee:
ID | int |
Name | nvarchar(50) |
Gender | nvarchar(2) |
DateCreated | datetime |
CreateUser | nvarchar(50) |
在aspx頁面中可能會有EmployeeEdit.aspx(用來添加或更新Employee信息等操作),EmployeeList.aspx(用來查詢或進行刪除Employee信息等操作)
好了,現在我們要對Empoyee表操作的信息做一個系統日誌,那怎麼辦?
也許你可以建立多一個表跟Employee表一模一樣的,叫做EmployeeLog:
ID | int |
Name | nvarchar(50) |
Gender | nvarchar(2) |
DateCreated | datetime |
CreateUser | nvarchar(50) |
LogCreated | datetime |
OperationType | int |
其中加多了一些附屬的信息如LogCreated(日誌添加日期)和OperationType(查詢、新增、刪除、更新)
此時這種情況可能大家在做用戶登錄日誌的時候是一件很常見的事件。
但……問題來了,假如我需要對錶EmployeeIncome(員工的收入情況)做日誌那怎麼辦?
好建立多一張表叫EmployeeIncomeLog來記錄員工收入情況的操作日誌。
假如又需要對錶FixedAsset(固定資產)進行日誌記錄那又怎麼辦?
好了,大家可能意識到我們這樣做不但會造成表數量的增倍,而且大大的增加了工作量和開發時間,對數據庫表不易管理等情況。
因此我們需要一個能夠通過簡單的配置和編寫就可以完成以上功能的日誌管理
數據庫設計
包括三個表,
LogSetting(日誌設置)——用來存儲配置業務表名、業務名稱、主鍵等
LogSettingDetail(日誌設置明細)——用來存儲配置業務表需要記錄的詳細內容,如Employee表中,我們可能需要記錄字段Name、Gender等信息。
LogOperation(操作日誌)——用來記錄用戶對各種業務操作的內容情況。
下篇將討論用代碼如何實現日誌管理的功能,下面先來幾張圖:
日誌列表:
查看日誌內容:
管理系統的操作日誌:
管理系統的操作日誌如何做成通用的模塊一直是個讓我頭疼的問題,不過看了博客園裏的某篇文章後,現在基本解決了。
相關文章鏈接:《系統操作日誌設計》
在開始做之前,必須把兩個日誌分清楚,那就是普通操作日誌和業務操作日誌,這兩者有何區別?
在我理解,普通操作日誌就是單表的操作記錄,而業務操作日誌則就是一系列的普通操作日誌的集合。
打個比方,用戶需要購買一樣寶貝,已經到了下單那步,下單就是個業務,這個業務背後就是一系列的業務,如:
生成訂單 → 生成商品快照 → 發送一條站內信 → 刪除購物車裏對應寶貝
這樣一個下單操作就包含了4部分,可以把這4部分看成是4張表,分別對這4張表進行對應的操作,就實現了業務。
但今天我要講的不是業務操作日誌,因爲不同項目的業務不盡相同,所以它無法做成通用模塊,而我要講的,就是普通操作日誌。
上面解釋了一大段,下面乾貨就要亮相了,先洗把臉清醒下。
……
首先,哪些地方需要記錄操作日誌?執行insert、update、delete這3個操作的時候,就需要進行日誌,而日誌執行的先後順序如下
insert | 在insert後執行 |
update | 在update前後都要執行,操作前獲取操作前數據,操作後獲取操作後數據 |
delete | 在delete前執行 |
順序清楚後,就來看下我寫的一份日誌操作類吧,第一版隨便寫寫的,重複代碼有點多,還未來得及優化。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
|
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
1
|
$log ->insert(82, 'tb_member' ); |
update
1
2
3
|
$log ->updateStart(82, 'tb_member' ); //中間放更新操作代碼 $log ->updateEnd(); |
delete
1
|
$log -> delete (82, 'tb_member' ); |
可以看到,一共只需要兩個參數即可,分別是表ID(主鍵)和表名稱。
另外需要強調一點,表註釋和字段註釋一定要完整,因爲記錄的信息包含註釋,目的就是爲了查閱的時候能清楚哪個字段是幹什麼用的。
下面就看下成品吧
最後把表結構分享下,一共2張表,一張主表一張從表,主表記錄操作表及操作人等信息,從表記錄操作的表字段信息。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
--
---------------------------- --
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; |