基于FreeSql和AutoMapper实现可读性较高的审计追踪
作者:互联网
基础概念
系统内对数据的操作通过数据库实体保存到库,通过数据库实体我们可以获取到变化之前的数据和变化之后的数据,但是基于数据库实体记录审计追踪有很大的弊病。
数据库实体的结构是基于业务需要设计的,一般很难满足审计追踪的观赏性方面的需求,要么是多出很多用户看不懂的字段,如主键、版本号等;要么是缺少必要的解释字段,比如有用户ID但是没有用户姓名。
审计追踪DTO
为了可以控制审计追踪记录的内容,需要新建一个专用于审计追踪的DTO,去掉不希望展示给用户的字段,加上必要的解释字段,例:
public class DeviceAuditDto : TKey
{
/// <summary>
/// 名称
/// </summary>
[Description("名称")]
[GroupField]
public string Name { get; set; }
/// <summary>
/// 设备类型(字典)
/// </summary>
[Description("设备类型(字典)")]
public string DeviceType { get; set; }
/// <summary>
/// 设备型号
/// </summary>
[Description("设备型号")]
public string DeviceModel { get; set; }
}
在本系统中,为了标记审计追踪专属DTO,都需要继承自TKey
字段属性Description的内容会做为字段含义显示在前端,便于用户理解
字段属性GroupField表示该字段是数据的标识性字段,记录多条数据的审计时,会以这个字段分组
编写审计追踪的几种方式
1、自动获取变化数据的
这种方式利用了FreeSql的实体变化追踪特性,编写审计追踪代码时,不需要传入实体的变化前数据和变化后数据,而是根据FreeSql已经追踪的数据自动记录到数据库。使用者只需要传入本次操作的模块、动作、操作内容。代码如下
//记录审计追踪
var InspAuditDto = new AutoAuditTrailDto()
{
BusinessId = BusinessAuditConsts.TestConditionAudit_BusniessId,
OperateType = ActionTypeEnum.Register.Description(),
Detail = string.Format(tips["StaticData.SignUpTestCondition"], entities.Select(e => e.ProductNo).Distinct().ToArray().Join())
};
await gxpAuditTrailService.Handle(InspAuditDto);
使用这种方式有一些限制条件:
- 方法需要有【Transaction】属性,整个方法处在事务中
- 变化的实体数据已经被FreeSql追踪,如果使用了UpdateDiy、ToUpdate等不会追踪变化的FreeSql功能,将无法正确获取变化前后的实体数据
- 数据库实体一定要通过AutoMapper映射到对应的AuditDto
- 实体中包含所需要记录的全部数据。例,如果实体中有用户Id但是没有用户姓名,那么就不适合使用本方法,需要手动处理实体数据
2、手动记录单表的审计追踪
该方法需要自己传入变化前后的数据,例:
// 记录审计追踪
var auditDto = new BaseAuditTrailDto<BuildingAuditDto, ActionTypeEnum>()
{
BusinessId = "Warehouse.BusinessId",
BusinessType = "Building.BusinessType",
OperateType = ActionTypeEnum.Update,
Detail = string.Format(tips["Building.UpdateDetail"], entity.Code),
PreEntity = new List<BuildingAuditDto>
{
mapper.Map<BuildingAuditDto>(oldEntity)
},
NewEntity = new List<BuildingAuditDto>
{
mapper.Map<BuildingAuditDto>(entity)
}
};
入参中的NewEntity就是变化后的实体,PreEntity是变化前的实体,采用该方法可以记录单表的审计追踪,且数据在传入之前,可以进行手动的修改、赋值。
获取变化数据的扩展方法
为了便捷的获取变化前后的实体,在仓储层基类FreeSqlRepositoryBase中实现了快速获取变化前后数据的方法,获取到的数据已经转化为了对应的AuditDto,所以需要传入AuditDto对应的泛型和AutoMapper对象,如下:
//记录审计追踪
var InspAuditDto = new BaseAuditTrailDto<TestConditionAuditDto, ActionTypeEnum>()
{
BusinessId = BusinessAuditConsts.TestConditionAudit_BusniessId,
OperateType = ActionTypeEnum.CancelRegister,
Detail = string.Format(tips["StaticData.CancelSignUpTestCondition"], entities.Select(e => e.ProductNo).Distinct().ToArray().Join()),
NewEntity = testConditionRepository.GetNewAuditList<TestConditionAuditDto>(mapper),
PreEntity = testConditionRepository.GetBeforeAuditList<TestConditionAuditDto>(mapper)
};
await gxpAuditTrailService.Handle(InspAuditDto);
由于该方法利用也是FreeSql的特性,所以也需要满足上述1、2、3条限制才可以使用。
3、手动记录多表的审计追踪
该方式与方式2差不多,主要是处理不能使用方式1,又涉及到多表的情况,代码如下:
#region 审计追踪
var InspAuditDto = new AggregateAuditTrailDto<TestConditionAuditDto, StabilityPlanAuditDto, StabilityAuditDto, ActionTypeEnum>()
var InspAuditDto = new AutoAuditTrailDto()
{
BusinessId = BusinessAuditConsts.TestConditionAudit_BusniessId,
BusinessType = BusinessAuditConsts.TestConditionAudit_BusniessType,
OperateType = ActionTypeEnum.RegisterStart,
AuditEntity = new AggregateAuditEntity<TestConditionAuditDto>
{
NewEntities = mapper.Map<List<TestConditionAuditDto>>(entities),
PreEntities = preList
},
SecondAuditEntity = new AggregateAuditEntity<StabilityPlanAuditDto>
{
NewEntities = mapper.Map<List<StabilityPlanAuditDto>>(stabPlans)
},
ThirdAuditEntity = new AggregateAuditEntity<StabilityAuditDto>
{
NewEntities = mapper.Map<List<StabilityAuditDto>>(stablities),
PreEntities = preStablityList
},
OperateType = ActionTypeEnum.RegisterStart.Description(),
Detail = string.Format(tips["StaticData.StartTestCondition"], entities.Select(e => e.ProductNo).Distinct().ToArray().Join())
};
await gxpAuditTrailService.Handle(InspAuditDto);
标签:审计,string,可读性,FreeSql,实体,AutoMapper,new,数据,追踪 来源: https://www.cnblogs.com/ankoo/p/16055533.html