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}"); } }