针对特定接口记录审核日志类的写入数据库的方法
导言
在后台管理一般都会涉及用户向管理员申请一些东西和操作,而审核过程有时需要记录,所以想记录一下学习了解该记录的过程。
有几种方法:
手动写入
1)创建
创建好审核相关的数据库表,实体,DTO,服务层接口,控制层(用来展示有多少条数据),因为和一般的创建方法一样故不留代码了。
2)使用
在需要审核的地方注入,正常使用就行
例:
// 请假申请控制器部分代码
...
/// <summary>
/// 审核日志接口
/// </summary>
private readonly SysAuditRecordService _sysAuditRecordService;
public LeaveController(ILogger<LeaveController> logger,TokenManager tokenManager, IBaseLeaveService leaveService,
IUnitOfWork unitOfWork,ISysDataRelationService dataRelationService, SysAuditRecordService sysAuditRecordService)
{
_logger = logger;
_tokenManager = tokenManager;
_leaveService = leaveService;
_unitOfWork = unitOfWork;
_dataRelationService = dataRelationService;
_sysAuditRecordService = sysAuditRecordService;
}
...
//所需要用到的方法
/// <summary>
/// 更新请假审核状态
/// </summary>
/// <param name="parm"></param>
/// <returns></returns>
[HttpPost]
[Authorization(Power = "PRIV_LEAVE_UPDATE")]//管理员才能访问,用于修改审核状态
public IActionResult UpdateLeaveStatus([FromBody] UpdateLeaveStatusDto parm)
{
//信息验证
var userSession = _tokenManager.GetSessionInfo();
//只跟关更新开始时间,结束时间,即原因
//查该请假信息,如果是已完成2或通过1,则不予修改
if (_leaveService.Any(m => m.ID == parm.ID && (m.LeaveStatus == 1 || m.LeaveStatus == 2)))
{
return toResponse(StatusCodeType.Error, $"该请假信息已完成/审核通过,不予修改!");
}
try
{
_unitOfWork.BeginTran();
//修改请假表
var response = _leaveService.Update(m => m.ID == parm.ID, m => new Base_Leave()
{
ID = parm.ID,
AuditDescription = parm.AuditDescription,
LeaveStatus = parm.LeaveStatus,
UserName = userSession.UserName,
UpdateID = userSession.UserID,
UpdateName = userSession.UserName,
UpdateTime = DateTime.Now
});
添加审核日志记录
//插入关系表
_sysAuditRecordService.Add(new Sys_AuditRecord
{
//常用方法函数 Guid
ID = Guid.NewGuid().ToString().ToUpper(),
//审核类型
AuditType = 3,
//申请人
ApplicationName = parm.UserName,
//申请人ID
ApplicationID = parm.UserID,
//申请状态
Status = parm.LeaveStatus,
//审核描述
AuditDiscription = parm.AuditDescription,
CreateTime = DateTime.Now,
CreateID = userSession.UserID,
CreateName = userSession.UserName,
});
_unitOfWork.CommitTran();
return toResponse(response);
}
catch (Exception ) {
_unitOfWork.RollbackTran();
throw;
}
}
3)注意
个人感觉记录之类的信息不需要修改,所以应该不用加修改日志的接口,现只搞了查询接口..
还有在需要连续改两张表以上信息时推荐将相关操作框在类似事务操作里面,不然可能会导致数据库数据有错误
中间件
由于事先了解了用这种方法会拦截所有的接口并添加,这不太符合只需记录特别接口日志的需求,且正在学习的项目已有使用NLog实现的所有日志记录,故没有尝试使用中间件实现
有关NLog及中间件实现日志记录-CSDN博客
过滤器
由于中间件只能全部拦截进行日志记录,如果只是一部分特殊的接口需要日志记录的话还要改造(具体的改造我也不会),所以试了试过滤器,想着在过滤器实现后在对应接口添加注释就行,不过之后也没有能实现成功,这个当做一次经验吧,也希望各位大佬有用这个实现的思路的话指导一下我。
思路
1)创建
过滤器:
using Meiam.System.Hostd.Extensions;
using Meiam.System.Interfaces;
using Meiam.System.Model;
using Meiam.System.Model.Dto;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Logging;
using System;
namespace Meiam.System.Hostd.Filter
{
public class AuditRecordFilter: ActionFilterAttribute
{
private readonly SysAuditRecordService _sysAuditRecordService; // 注入日志服务
/// <summary>
/// 会话管理接口
/// </summary>
private readonly TokenManager _tokenManager;
public AuditRecordFilter( SysAuditRecordService sysAuditRecordService, TokenManager tokenManager)
{
_sysAuditRecordService = sysAuditRecordService;
_tokenManager = tokenManager;
}
public override void OnActionExecuting(ActionExecutingContext context)
{
var request = context.HttpContext.Request;
// 调用日志服务保存日志到数据库
try
{
var userSession = _tokenManager.GetSessionInfo();
context.HttpContext.Items.TryGetValue("AuditType", out var AuditType);
context.HttpContext.Items.TryGetValue("ApplicationName", out var ApplicationName);
context.HttpContext.Items.TryGetValue("ApplicationID", out var ApplicationID);
context.HttpContext.Items.TryGetValue("Status", out var Status);
context.HttpContext.Items.TryGetValue("AuditDiscription", out var AuditDiscription);
//插入关系表
_sysAuditRecordService.Add(new Sys_AuditRecord
{
//常用方法函数 Guid
ID = Guid.NewGuid().ToString().ToUpper(),
//审核类型
AuditType = (int)AuditType,
//申请人
ApplicationName = (string)ApplicationName,
//申请人ID
ApplicationID = (string)ApplicationID,
//申请状态
Status = (int)Status,
//审核描述
AuditDiscription = (string)AuditDiscription,
CreateTime = DateTime.Now,
CreateID = userSession.UserID,
CreateName = userSession.UserName,
});
base.OnActionExecuting(context);
}
catch (Exception)
{
throw;
}
}
}
}
2)注册
Startup.cs
...
services.AddControllers(options =>
{
//审核日志记录 新增 实验
options.Filters.Add<AuditRecordFilter>();
})
...
3)使用
//控制层接口
[HttpPost]
[AuditRecordFilter] //审核日志记录
[Authorization(Power = "PRIV_LEAVE_UPDATE")]//管理员才能访问,用于修改审核状态
public IActionResult UpdateLeaveStatus([FromBody] UpdateLeaveStatusDto parm)
{
...
}
未能实现原因
有关C# .NET Core 过滤器的使用-CSDN博客
已解决:
解决方案
改为使用 [TypeFilter(typeof(AuditRecordFilter),Arguments =new object[] {3})]作为所需记录审核日志的接口标注,且不需要再startup.cs中注册,过滤器也不用改什么。
类型判断
可以通过
还可以通过过滤器构造器传参数:
上一张图中Arguments(3)可以在构造函数中获取(auditType)
...
private readonly int _auditType;
public AuditRecordFilter( ISysAuditRecordService sysAuditRecordService, TokenManager tokenManager,int auditType)
{
_sysAuditRecordService = sysAuditRecordService;
_tokenManager = tokenManager;
_auditType = auditType;
}
...
以此可以判断不同的审核类型
手动写入与过滤器实现的优缺点比较
第一种:手动写入
优点:
-
清晰的业务逻辑:
- 在控制器中直接编写日志添加逻辑,使得业务逻辑和日志记录之间的关系清晰,开发人员可以直观地看到日志记录在哪个业务逻辑阶段发生。
-
灵活性高:
- 开发人员可以在需要的地方自由地决定何时记录日志。可以根据不同的业务场景,动态地调整日志记录内容或是否记录日志。
-
便于调试:
- 因为日志记录代码与业务逻辑紧密结合,在调试和开发过程中可以轻松追踪日志的生成流程。
缺点:
-
代码重复:
- 如果多个控制器或多个方法需要添加相似的日志记录逻辑,可能会导致代码重复,违反**DRY(Don't Repeat Yourself)**原则。
-
日志逻辑与业务逻辑耦合:
- 日志记录代码与业务逻辑混在一起,可能会影响代码的可读性和可维护性。如果未来需要更改日志记录的方式(例如将日志存储到其他地方),需要在多个地方修改。
-
测试复杂度增加:
- 由于日志逻辑和业务逻辑耦合,单元测试时可能需要额外处理日志记录的依赖,增加了测试的复杂性。
适用场景:
- 适用于日志记录需求较少或特定场景下才需要记录日志的情况。例如,只有少数接口需要记录审核日志,或日志的生成与业务逻辑高度相关,难以抽象。
第二种:过滤器实现
优点:
-
分离关注点(Separation of Concerns):
- 将日志记录逻辑从业务逻辑中解耦出来,控制器专注于业务逻辑,而日志记录逻辑放在过滤器中,符合单一职责原则,增强了代码的可读性和可维护性。
-
代码复用:
- 过滤器可以复用。如果多个接口或控制器都需要相似的日志记录逻辑,只需在相应的地方应用该过滤器,避免了代码重复。
-
模块化与可扩展性:
- 由于日志记录被抽象到过滤器中,如果未来需要修改日志记录的逻辑或扩展功能(例如添加更多的日志细节),只需修改过滤器代码,而不需要修改每个接口。
-
便于全局应用:
- 可以将日志过滤器设置为全局过滤器,适用于整个应用程序,而不需要逐个接口标注。如果所有接口都需要日志记录,这样的方式更加高效。
缺点:
-
日志记录不够灵活:
- 过滤器适合于记录通用日志,而不适合在某些特殊情况下动态修改日志记录的内容。如果某个接口的日志记录有特殊需求,可能需要额外处理。
-
调试复杂性:
- 由于日志记录被抽象到过滤器中,调试时可能难以直接看到日志生成的时机和上下文,尤其是在涉及到复杂的过滤器配置或参数传递时。
-
依赖于过滤器的配置:
- 如果过滤器配置错误(例如没有正确传递参数或依赖项),可能会导致难以调试的运行时错误。
适用场景:
- 适用于大规模日志记录的场景,尤其是多个接口都需要记录类似的日志逻辑。通过过滤器可以提高代码复用性,减少重复代码。
- 适用于跨多个接口或模块化设计的系统,能简化日志记录的实现。