using Sharp.Data;
using Sharp.Data.Context;
namespace Data.Test;
///
/// 演示 03: 审计功能
///
/// 本测试类演示:
/// 1. 如何启用审计功能
/// 2. 创建时自动记录 CreatedAt 和 CreatedBy
/// 3. 更新时自动记录 UpdatedAt 和 UpdatedBy
/// 4. 如何设置当前用户
/// 5. 审计功能对不同实体的影响
///
[TestClass]
public class _03_AuditFeatureTests
{
///
/// 演示: 基础审计功能 - 创建时自动记录
///
/// 使用 AuditableEntity 基类的实体会自动记录审计信息
///
[TestMethod]
public async Task Demo_AuditOnCreate()
{
// 创建启用审计功能的 DataContext
var builder = new DataContextBuilder();
builder.UseInMemoryDatabase($"AuditCreateDemo_{Guid.NewGuid()}")
.UseAudit(true) // 启用审计
.UseCurrentUser("zhang.san"); // 设置当前用户
using var db = builder.Build();
var repo = db.Repository();
var beforeCreate = DateTime.UtcNow;
// 创建产品
var product = new Product
{
Name = "测试产品",
Price = 99.00m,
Stock = 10
};
// 添加前,审计字段是空的
Console.WriteLine("添加前:");
Console.WriteLine($" CreatedAt: {product.CreatedAt}");
Console.WriteLine($" CreatedBy: {product.CreatedBy ?? "(null)"}");
await repo.AddAsync(product);
await db.SaveChangesAsync(); // SaveChanges 时自动填充审计信息
var afterCreate = DateTime.UtcNow;
// 添加后,审计字段自动填充
Console.WriteLine("\n添加后:");
Console.WriteLine($" ✓ CreatedAt: {product.CreatedAt:yyyy-MM-dd HH:mm:ss}");
Console.WriteLine($" ✓ CreatedBy: {product.CreatedBy}");
Console.WriteLine($" ✓ UpdatedAt: {product.UpdatedAt?.ToString("yyyy-MM-dd HH:mm:ss") ?? "(null)"}");
Console.WriteLine($" ✓ UpdatedBy: {product.UpdatedBy ?? "(null)"}");
// 验证
Assert.IsTrue(product.CreatedAt >= beforeCreate && product.CreatedAt <= afterCreate);
Assert.AreEqual("zhang.san", product.CreatedBy);
Assert.IsNull(product.UpdatedAt); // 创建时 UpdatedAt 为 null
Assert.IsNull(product.UpdatedBy);
}
///
/// 演示: 审计功能 - 更新时自动记录
///
[TestMethod]
public async Task Demo_AuditOnUpdate()
{
var builder = new DataContextBuilder();
builder.UseInMemoryDatabase($"AuditUpdateDemo_{Guid.NewGuid()}")
.UseAudit(true)
.UseCurrentUser("creator");
using var db = builder.Build();
var repo = db.Repository();
// 1. 创建产品 (创建人: creator)
var product = new Product
{
Name = "原始产品",
Price = 100m,
Stock = 10
};
await repo.AddAsync(product);
await db.SaveChangesAsync();
Console.WriteLine("创建后的审计信息:");
Console.WriteLine($" CreatedBy: {product.CreatedBy}");
Console.WriteLine($" CreatedAt: {product.CreatedAt:yyyy-MM-dd HH:mm:ss}");
var createdAt = product.CreatedAt;
var createdBy = product.CreatedBy;
// 等待一小段时间
await Task.Delay(100);
// 2. 更改当前用户并更新产品
// 注意:如果需要切换用户,请创建新的 DataContext
// (db as Sharp.Data.Context.DataContext)?.SetCurrentUser("editor");
product.Name = "更新后的产品";
product.Price = 200m;
repo.Update(product);
await db.SaveChangesAsync(); // SaveChanges 时自动填充 UpdatedAt 和 UpdatedBy
Console.WriteLine("\n更新后的审计信息:");
Console.WriteLine($" CreatedBy: {product.CreatedBy} (保持不变)");
Console.WriteLine($" CreatedAt: {product.CreatedAt:yyyy-MM-dd HH:mm:ss} (保持不变)");
Console.WriteLine($" ✓ UpdatedBy: {product.UpdatedBy}");
Console.WriteLine($" ✓ UpdatedAt: {product.UpdatedAt:yyyy-MM-dd HH:mm:ss}");
// 验证
Assert.AreEqual(createdBy, product.CreatedBy); // 创建人不变
Assert.AreEqual(createdAt, product.CreatedAt); // 创建时间不变
Assert.AreEqual("editor", product.UpdatedBy); // 更新人是新用户
Assert.IsNotNull(product.UpdatedAt);
Assert.IsTrue(product.UpdatedAt > product.CreatedAt); // 更新时间晚于创建时间
}
///
/// 演示: 多次更新的审计记录
///
[TestMethod]
public async Task Demo_MultipleUpdatesAudit()
{
var builder = new DataContextBuilder();
builder.UseInMemoryDatabase($"MultiUpdateDemo_{Guid.NewGuid()}")
.UseAudit(true)
.UseCurrentUser("user1");
using var db = builder.Build();
var repo = db.Repository();
// 创建产品
var product = new Product { Name = "产品", Price = 100m, Stock = 10 };
await repo.AddAsync(product);
await db.SaveChangesAsync();
Console.WriteLine($"创建: {product.CreatedBy} 于 {product.CreatedAt:HH:mm:ss}");
// 第一次更新
await Task.Delay(50);
// 注意:如果需要切换用户,请创建新的 DataContext
// (db as Sharp.Data.Context.DataContext)?.SetCurrentUser("user2");
product.Price = 150m;
repo.Update(product);
await db.SaveChangesAsync();
var firstUpdateTime = product.UpdatedAt!.Value;
Console.WriteLine($"第1次更新: {product.UpdatedBy} 于 {product.UpdatedAt:HH:mm:ss}");
// 第二次更新
await Task.Delay(50);
// 注意:如果需要切换用户,请创建新的 DataContext
// (db as Sharp.Data.Context.DataContext)?.SetCurrentUser("user3");
product.Price = 200m;
repo.Update(product);
await db.SaveChangesAsync();
Console.WriteLine($"第2次更新: {product.UpdatedBy} 于 {product.UpdatedAt:HH:mm:ss}");
// 验证
Assert.AreEqual("user1", product.CreatedBy); // 创建人始终不变
Assert.AreEqual("user3", product.UpdatedBy); // 最后更新人
Assert.IsTrue(product.UpdatedAt > firstUpdateTime); // 更新时间在变化
}
///
/// 演示: 动态获取当前用户
///
/// 在真实应用中,用户信息通常来自认证系统
///
[TestMethod]
public async Task Demo_DynamicUserProvider()
{
// 模拟当前登录用户(实际应用中可能来自 HttpContext、Session 等)
var currentUser = "zhang.san";
var builder = new DataContextBuilder();
builder.UseInMemoryDatabase($"DynamicUserDemo_{Guid.NewGuid()}")
.UseAudit(true)
.UseCurrentUserProvider(() =>
{
// 这里可以放置获取当前用户的逻辑
// 例如: Thread.CurrentPrincipal?.Identity?.Name
// HttpContext.Current?.User?.Identity?.Name
// 某个全局的用户服务
return currentUser;
});
using var db = builder.Build();
var repo = db.Repository();
// 用户 zhang.san 创建产品
var product = new Product { Name = "产品A", Price = 100m, Stock = 10 };
await repo.AddAsync(product);
await db.SaveChangesAsync();
Console.WriteLine($"✓ 创建人: {product.CreatedBy}");
Assert.AreEqual("zhang.san", product.CreatedBy);
// 切换用户
currentUser = "li.si";
// 用户 li.si 更新产品
product.Price = 200m;
repo.Update(product);
await db.SaveChangesAsync();
Console.WriteLine($"✓ 更新人: {product.UpdatedBy}");
Assert.AreEqual("zhang.san", product.CreatedBy); // 创建人不变
Assert.AreEqual("li.si", product.UpdatedBy); // 更新人改变
}
///
/// 演示: 实现审计接口但不使用基类
///
/// Order 实体实现了 IAuditableEntity 接口,同样享受审计功能
///
[TestMethod]
public async Task Demo_AuditWithInterface()
{
var builder = new DataContextBuilder();
builder.UseInMemoryDatabase($"InterfaceAuditDemo_{Guid.NewGuid()}")
.UseAudit(true)
.UseCurrentUser("admin");
using var db = builder.Build();
var repo = db.Repository();
// 创建订单
var order = new Order
{
OrderNumber = "ORD-001",
TotalAmount = 999.00m
};
await repo.AddAsync(order);
await db.SaveChangesAsync();
Console.WriteLine("✓ Order 实体(实现接口但不使用基类)同样享受审计功能");
Console.WriteLine($" CreatedBy: {order.CreatedBy}");
Console.WriteLine($" CreatedAt: {order.CreatedAt:yyyy-MM-dd HH:mm:ss}");
// 验证
Assert.AreEqual("admin", order.CreatedBy);
Assert.IsTrue(order.CreatedAt > DateTime.MinValue);
}
///
/// 演示: 不使用审计的实体不受影响
///
/// Category 不继承 AuditableEntity,也不实现审计接口
///
[TestMethod]
public async Task Demo_NoAuditForNonAuditableEntity()
{
var builder = new DataContextBuilder();
builder.UseInMemoryDatabase($"NoAuditDemo_{Guid.NewGuid()}")
.UseAudit(true) // 即使启用了审计功能
.UseCurrentUser("admin");
using var db = builder.Build();
var repo = db.Repository();
// 创建分类
var category = new Category { Name = "测试分类" };
await repo.AddAsync(category);
await db.SaveChangesAsync();
Console.WriteLine("✓ Category 不实现审计接口,不受审计功能影响");
Console.WriteLine($" 只有 Id 和业务字段: {category.Id}, {category.Name}");
// 验证:Category 没有审计字段
Assert.IsNotNull(category.Id);
Assert.AreEqual("测试分类", category.Name);
}
///
/// 演示: 禁用审计功能
///
[TestMethod]
public async Task Demo_DisableAudit()
{
var builder = new DataContextBuilder();
builder.UseInMemoryDatabase($"DisableAuditDemo_{Guid.NewGuid()}")
.UseAudit(false) // 禁用审计功能
.UseCurrentUser("admin");
using var db = builder.Build();
var repo = db.Repository();
// 创建产品
var product = new Product { Name = "测试产品", Price = 100m, Stock = 10 };
await repo.AddAsync(product);
await db.SaveChangesAsync();
Console.WriteLine("✓ 审计功能已禁用");
Console.WriteLine($" CreatedBy: {product.CreatedBy ?? "(未设置)"}");
Console.WriteLine($" CreatedAt: {product.CreatedAt}");
// 即使实体有审计字段,但功能禁用后,字段不会自动填充
// 但 CreatedAt 可能有默认值(DateTime.MinValue 或当前时间,取决于实现)
Console.WriteLine(" 注意:禁用审计后,即使实体有审计字段,也不会自动填充");
}
///
/// 演示: 审计功能的实际应用场景
///
[TestMethod]
public async Task Demo_RealWorldAuditScenario()
{
var builder = new DataContextBuilder();
builder.UseInMemoryDatabase($"RealWorldDemo_{Guid.NewGuid()}")
.UseAudit(true)
.UseCurrentUser("system");
using var db = builder.Build();
var repo = db.Repository();
// 场景:产品生命周期跟踪
// 1. 系统初始化时创建产品
var product = new Product
{
Name = "iPhone 15",
Price = 7999m,
Stock = 100
};
await repo.AddAsync(product);
await db.SaveChangesAsync();
Console.WriteLine("=== 产品生命周期审计 ===\n");
Console.WriteLine($"1. 产品创建");
Console.WriteLine($" 由 {product.CreatedBy} 于 {product.CreatedAt:yyyy-MM-dd HH:mm:ss} 创建");
// 2. 仓库管理员调整库存
await Task.Delay(50);
// 注意:如果需要切换用户,请创建新的 DataContext
// (db as Sharp.Data.Context.DataContext)?.SetCurrentUser("warehouse.manager");
product.Stock = 80;
repo.Update(product);
await db.SaveChangesAsync();
Console.WriteLine($"\n2. 库存调整");
Console.WriteLine($" 由 {product.UpdatedBy} 于 {product.UpdatedAt:yyyy-MM-dd HH:mm:ss} 更新");
// 3. 市场部调整价格
await Task.Delay(50);
// 注意:如果需要切换用户,请创建新的 DataContext
// (db as Sharp.Data.Context.DataContext)?.SetCurrentUser("marketing.manager");
product.Price = 6999m;
repo.Update(product);
await db.SaveChangesAsync();
Console.WriteLine($"\n3. 价格调整");
Console.WriteLine($" 由 {product.UpdatedBy} 于 {product.UpdatedAt:yyyy-MM-dd HH:mm:ss} 更新");
Console.WriteLine($"\n✓ 通过审计信息,可以追踪产品的完整变更历史");
Console.WriteLine($" - 谁创建的:{product.CreatedBy}");
Console.WriteLine($" - 何时创建:{product.CreatedAt:yyyy-MM-dd HH:mm:ss}");
Console.WriteLine($" - 谁最后修改:{product.UpdatedBy}");
Console.WriteLine($" - 何时修改:{product.UpdatedAt:yyyy-MM-dd HH:mm:ss}");
}
}