Winform开发框架之权限管理系统改进的经验总结(4)-一行代码实现表操作日志记录
在前面介绍了几篇关于我的权限系统改进的一些经验总结,本篇继续这一系列主体,介绍如何一行代码实现重要表的操作日志记录。我们知道,在很多业务系统里面,数据是很敏感的,特别对于一些增加、修改、删除等关键的操作,如果能在框架层面的支持基础上,以最少的代码实现重要表的日志记录,那么是一件非常值得庆贺的事情,也能够为我们客户的数据提供重要的日志跟踪,甚至是数据恢复的参考。
1、数据访问层的对象继承关系
首先,为了减少重复代码的编写,合理的继承关系是必要的,我们需要在数据访问层上建立合理的继承关系,如下是我的Winform开发框架的继承关系。每个数据访问对象(如ItemDetail数据访问对象)都继承一个抽象基类AbstractBaseDAL和一个IBaseDAL基类接口,同时它也有自己特殊的业务接口,如IItemDetail,关系如下所示。
有了上面的继承关系,我们就可以把常规的数据库重要操作(增删改)放到一个高一级的层次上去解决这个问题,而不需要在每个数据访问层的业务类来实现。
2、操作日志记录事件的定义和使用
为了更好实现数据操作日志的记录,我们以事件方式来触发操作日志的记录,事件的具体记录实现,可以交给外部来记录处理。如果事件被外部赋值了,那么就可以在底层触发这个事件记录,记录事件的定义代码在抽象基类进行定义,如下所示。
/// 定义一个记录操作日志的事件处理
/// </summary>
/// <param name="userId">操作的用户ID</param>
/// <param name="tableName">操作表名称</param>
/// <param name="operationType">操作类型:增加、修改、删除</param>
/// <param name="note">操作的详细记录信息</param>
/// <returns></returns>
public delegate bool OperationLogEventHandler(string userId, string tableName, string operationType, string note, DbTransaction trans = null); /// <summary>
/// 数据访问层的超级基类,所有数据库的数据访问基类都继承自这个超级基类,包括Oracle、SqlServer、Sqlite、MySql、Access等
/// </summary>
public abstract class AbstractBaseDAL<T> where T : BaseEntity, new()
{
#region 构造函数 protected string dbConfigName = ""; //数据库配置名称
protected string parameterPrefix = "@";//数据库参数化访问的占位符
protected string safeFieldFormat = "[{0}]";//防止和保留字、关键字同名的字段格式,如[value]
protected string tableName;//需要初始化的对象表名
protected string primaryKey;//数据库的主键字段名
protected string sortField;//排序字段
protected bool isDescending = true;//是否为降序
protected string selectedFields = " * ";//选择的字段,默认为所有(*)
public event OperationLogEventHandler OnOperationLog;//定义一个操作记录的事件处理 .....................
以上是抽象基类AbstractBaseDAL的部分代码,上面代码定义了一个操作记录的委托和事件对象来处理操作日志的记录,通过委托的定义,我们可以规定具体的事件接口定义,并在抽象基类的底层构造这些参数的数值,传递给外部的对象进行处理。
那么我们是如何在底层操作构造这些信息的呢?
其实就是在相应的重要操作接口函数上调用这个定义的事件。我们可以在抽象基类的插入、修改、删除等接口上调用事件进行处理即可,为了更好处理相关数据的构造逻辑,我们把调用OnOperationLog的事件封装到一个单独的函数里面进行处理,如下所示是底层更新操作的代码,通过增加一个OperationLogOfUpdate来实现数据日志的事件处理。
/// <summary>
/// 更新对象属性到数据库中
/// </summary>
/// <param name="obj">指定的对象</param>
/// <param name="primaryKeyValue">主键的值</param>
/// <param name="trans">事务对象</param>
/// <returns>执行成功返回<c>true</c>,否则为<c>false</c>。</returns>
public virtual bool Update(T obj, object primaryKeyValue, DbTransaction trans = null)
{
ArgumentValidation.CheckForNullReference(obj, "传入的对象obj为空"); OperationLogOfUpdate(obj, primaryKeyValue, trans);//根据设置记录操作日志 Hashtable hash = GetHashByEntity(obj);
return Update(primaryKeyValue, hash, trans);
}
然后我们在具体的事件处理封装函数OnOperationLog的里面添加处理逻辑即可,一般事件的标准处理为如下代码。
/// <summary>
/// 修改操作的日志记录
/// </summary>
/// <param name="id">记录ID</param>
/// <param name="obj">数据对象</param>
/// <param name="trans">事务对象</param>
protected virtual void OperationLogOfUpdate(T obj, object id, DbTransaction trans = null)
{
if (OnOperationLog != null)
{
...............................//构造相关参数
OnOperationLog(userId, this.tableName, operationType, note, trans);
}
}
}
我们知道,一般操作日志都会记录是谁进行操作的,然后把它写到日志里面,并把操作的内容可读化即可,那么在更新的时候,我们如何知道是谁操作的对象呢?因为我们没有传递具体的用户ID等标识的啊。
这个问题挺头痛,如果增加多一个参数,那么就得修改很多相关的调用逻辑,这个明显不太符合我们简约的风格,因此最好另寻其他方式来实现这个人员身份记录的问题。
我们知道,一般插入、更新操作,都是带一个操作对象的,这个操作对象是一个实体类,基类是BaseEntity,那么我们可以在它的身上定义多一个属性,这个属性不参数数据的保存,只是作为参数的传递和识别而已,实体类基类的代码如下所示。
/// <summary>
/// 框架实体类的基类
/// </summary>
[DataContract]
public class BaseEntity
{
private string m_CurrentLoginUserId; /// <summary>
/// 当前登录用户ID。该字段不保存到数据表中,只用于记录用户的操作日志。
/// </summary>
[DataMember]
public string CurrentLoginUserId
{
get { return m_CurrentLoginUserId; }
set { m_CurrentLoginUserId = value; }
}
}
}
有了这个信息,我们就可以在刚才的事件处理逻辑上进行获取用户的ID操作了。
string userId = obj.CurrentLoginUserId;
下一个问题是,如何把操作的信息可读化,我们知道,一般操作只是对部分字段进行修改,那么我们一般也不需要把所有的字段信息都弄出来显示,只需要显示那些修改的即可。
为了实现这个数据的差异化显示,我们需要在更新操作之前进行获取数据库的对象信息,然后和将要进行更新的对象进行对比,把差异的信息作为备注信息记录下来即可,具体逻辑如下所示。
/// <summary>
/// 修改操作的日志记录
/// </summary>
/// <param name="id">记录ID</param>
/// <param name="obj">数据对象</param>
/// <param name="trans">事务对象</param>
protected virtual void OperationLogOfUpdate(T obj, object id, DbTransaction trans = null)
{
if (OnOperationLog != null)
{
string operationType = "修改";
string userId = obj.CurrentLoginUserId; Hashtable recordField = GetHashByEntity(obj);
Dictionary<string, string> dictColumnNameAlias = GetColumnNameAlias(); T objInDb = FindByID(id, trans);
if (objInDb != null)
{
Hashtable dbrecordField = GetHashByEntity(objInDb);//把数据库里的实体对象数据转换为哈希表 StringBuilder sb = new StringBuilder();
foreach (string field in recordField.Keys)
{
string newValue = recordField[field].ToString();
string oldValue = dbrecordField[field].ToString();
if (newValue != oldValue)//只记录变化的内容
{
string columnAlias = "";
bool result = dictColumnNameAlias.TryGetValue(field, out columnAlias);
if (result && !string.IsNullOrEmpty(columnAlias))
{
columnAlias = string.Format("({0})", columnAlias);//字段中文名称前,增加一个括号显示,方便区分显示
} sb.AppendLine(string.Format("{0}{1}:", field, columnAlias));
sb.AppendLine(string.Format("\t {0} -> {1}", dbrecordField[field], recordField[field]));
sb.AppendLine();
}
}
sb.AppendLine();
string note = sb.ToString(); OnOperationLog(userId, this.tableName, operationType, note, trans);
}
}
}
上面是更新操作的日志记录处理,其他的插入、删除等操作类似这样的操作方式,再次不在赘述。
3、业务层对操作日志信息的处理
上面的代码只是实现了对底层操作的信息记录并传递给操作日志的记录事件,并没有知道上层是如何处理事件信息的记录的,这个问题留给上层去处理。
为了实现这个信息的记录,我们在权限系统里面增加一个单独的数据库表如T_ACL_OperationLog表用来专门记录这些信息的。
上层的处理逻辑是获取用户ID的登陆信息,包括用户名、用户IP地址、Mac地址信息等,这些信息一旦用户登陆到系统就会发生了,所以可以方便获取到,然后就是把这些信息作为一个数据库记录写入数据库表即可。
OperationLogInfo info = new OperationLogInfo();
info.TableName = tableName;
info.OperationType = operationType;
info.Note = note;
info.CreateTime = DateTime.Now; if (!string.IsNullOrEmpty(userId))
{
UserInfo userInfo = BLLFactory<User>.Instance.FindByID(userId, trans);
if (userInfo != null)
{
info.User_ID = userId;
info.LoginName = userInfo.Name;
info.FullName = userInfo.FullName;
info.Company_ID = userInfo.Company_ID;
info.CompanyName = userInfo.CompanyName;
info.MacAddress = userInfo.CurrentMacAddress;
info.IPAddress = userInfo.CurrentLoginIP;
}
} return BLLFactory<OperationLog>.Instance.Insert(info, trans);
因为这些记录的操作都是一样的,为了更方便,我们把这些逻辑封装在一个静态的方法里面,然后所有需要记录操作日志的,在业务对象里面增加一行代码,就可以轻松实现日志记录了,具体代码如下所示。
/// <summary>
/// 部门机构信息
/// </summary>
public class OU : BaseBLL<OUInfo>
{
private IOU ouDal; /// <summary>
/// 构造函数
/// </summary>
public OU() : base()
{
base.Init(this.GetType().FullName, System.Reflection.Assembly.GetExecutingAssembly().GetName().Name);
baseDal.OnOperationLog += new OperationLogEventHandler(WHC.Security.BLL.OperationLog.OnOperationLog);//如果需要记录操作日志,则实现这个事件 this.ouDal = baseDal as IOU;
}
这样数据访问类baseDal一旦事件初始化,那么就会在底层进行触发,然后交给事件的处理逻辑(上层操作)进行处理了。
为了更好控制用户的增加、修改、删除的相关事件,我们可以通过一个配置表进行登记处理,然后根据配置表的参数来决定记录那些信息,这些就是细化的问题了。
4、我的Winform开发框架的权限系统模块里面对于操作日志的支持
在我的Winform开发框架里面,权限系统是其中的一个基础部分,因此也根据上面的逻辑实现了对操作日志的参数配置和记录显示,方便对业务系统所有表的操作记录进行跟踪和处理。
通过一行代码就能实现业务表的日志记录,对我们开发新的业务模块,效率可以提高很多,同时也能给客户提供更好的数据支持服务。通过在权限系统模块里面配置参数和显示操作日志记录,能够给业务开发提供基础性的开发框架支持。
下面是我的Winform开发框架的权限系统模块的一些功能 截图,供参考学习。
双击打开记录的明细,可以看到操作记录的明细显示。
参数配置界面如下所示。
以上显示只是基于权限系统进行日志的记录,当然整个业务系统框架都可以提供上面的记录操作,因为它们所有的数据访问基类都是继承自同一个抽象对象基类的。把这个模块集成在权限系统里面,和登陆日志一样,是供基础性的记录和查阅的,和业务不太相关。
最后附上Winform开发框架的一个功能总结图形,Winform开发框架的主要功能概览如下图所示。
Winform开发框架之权限管理系统改进的经验总结(4)-一行代码实现表操作日志记录的更多相关文章
- Winform开发框架之权限管理系统改进的经验总结(2)-用户选择界面的设计
在上篇总结随笔<Winform开发框架之权限管理系统改进的经验总结(1)-TreeListLookupEdit控件的使用>介绍了权限管理模块的用户管理部分,其中主要介绍了其中的用户所属公司 ...
- Winform开发框架之权限管理系统改进的经验总结(1)-TreeListLookupEdit控件的使用
最近一直在做一些技术性的研究和框架改进工作,博客也落下好几天没有更新了,也该是时候静下心来,总结这段时间的一些技术改进的经验了.和上一阶段的CRM系统开发和技术研究一样,我都喜欢在一个项目或者模块完成 ...
- Winform开发框架之权限管理系统改进的经验总结(4)--用户分级管理
在实际的系统应用环境中,用户的分级管理一般也是比较常见的功能,小的业务系统可以不需要,但是一般涉及到集团.分子公司.或者是事业单位里面的各个处室或者某某局的人员管理,这些分级管理就显得比较必要,否则单 ...
- Winform开发框架之权限管理系统改进的经验总结(3)-系统登录黑白名单的实现
在一般的权限系统里面,可能经常会看到系统的黑名单或者白名单的拦截功能.在一般权限系统里面,常见的黑名单就是禁止用户在某些IP上登录系统,白名单就是允许用户只在某些IP上登录系统.本随笔主要介绍在我的权 ...
- Winform开发框架之权限管理系统的改进
权限管理系统,一直是很多Mis系统和一些常见的管理系统所需要的,所以一般可以作为独立的模块进行开发,需要的时候进行整合即可,不需要每次从头开发,除非特殊的系统需求.我在Winform开发框架介绍中的随 ...
- Winform开发框架之权限管理系统
本文章转载:http://www.cnblogs.com/wuhuacong/archive/2011/05/08/2040620.html 至此,权限管理模块介绍已经完毕,下面给出一个调用例子Dem ...
- Winform开发框架之权限管理系统功能介绍
权限管理系统的重要特性总结: 1) 高度集成的权限系统.独立模块,能快速整合使用.2) 符合权限的国际通用标准,基于RBAC(基于角色的访问控制)的角色权限控制.3) 多数据库架构支持,内置支持Sql ...
- Web开发框架之权限管理系统
Web开发框架之权限管理系统 记得我在很早之前,开始介绍我的Winform开发框架和我的WCF开发框架之初,我曾经给出下面的视图,介绍我整理的一个框架体系,其中包含有WInform开发框架以及我的We ...
- 基于SqlSugar的开发框架循序渐进介绍(8)-- 在基类函数封装实现用户操作日志记录
在我们对数据进行重要修改调整的时候,往往需要跟踪记录好用户操作日志.一般来说,如对重要表记录的插入.修改.删除都需要记录下来,由于用户操作日志会带来一定的额外消耗,因此我们通过配置的方式来决定记录那些 ...
随机推荐
- C++的黑科技
周二面了腾讯,之前只投了TST内推,貌似就是TST面试了 其中有一个问题,"如何产生一个不能被继承的类",这道题我反反复复只想到,将父类的构造函数私有,让子类不能调用,最后归结出一 ...
- 通过PowerShell获取Windows系统密码Hash
当你拿到了系统控制权之后如何才能更长的时间内控制已经拿到这台机器呢?作为白帽子,已经在对手防线上撕开一个口子,如果你需要进一步扩大战果,你首先需要做的就是潜伏下来,收集更多的信息便于你判断,便于有更大 ...
- A20(Cubieboard2)启动过程浅析
A20支持从NAND Flash.SPI NOR Flash.SD card(SDC 0/2)和USB启动.当系统上电时,首先检测Boot Select Pin(BSP)管脚,如果为低电平,则直接从U ...
- 使用Eclipse PDT + Xampp搭建Php开发环境
最新文章:Virson's Blog Eclipse版本:Eclipse Luna Service Release 2 (4.4.2) Xampp版本:XAMPP for Windows 5.6.8 ...
- Hbase0.98.4/Hadoop2.4.1整合小结【原创】
设定hbase的数据目录,修改conf/hbase-site.xml <configuration> <property> <name>hbase.cluster. ...
- 使用before、after伪类制作三角形
使用before.after伪类实现三角形的制作,不需要再为三角形增加不必要的DOM元素,影响阅读. <!DOCTYPE html><html><head> ...
- java 流
http://www.iteye.com/magazines/132-Java-NIO http://liyuanning.blog.163.com/blog/static/4573228620101 ...
- POJ 1804 Brainman
Brainman Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 7787 Accepted: 4247 Descript ...
- 活学活用,webapi HTTPBasicAuthorize搭建小型云应用的实践
HTTP使用BASIC认证,WebAPI使用[HTTPBasicAuthorize]标记控制器就是使用了BASIC认证. BASIC认证的缺点HTTP基本认证的目标是提供简单的用户验证功能,其认证过程 ...
- iOS开发之企业发布无线安装APP
前提是注册成为企业开发者(¥299),申请到证书并安装到本地,可以正常使用Xcode在IOS移动设备上进行Debug. 首先build看是否报错.如无错 执行下一: 执行Product—Archive ...