using HoneyBox.Core.Interfaces; using HoneyBox.Core.Services; using HoneyBox.Model.Data; using HoneyBox.Model.Entities; using HoneyBox.Model.Models.Order; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.Extensions.Logging; using Moq; using Xunit; namespace HoneyBox.Tests.Integration; /// /// 订单服务集成测试 /// 测试完整的订单金额计算和订单创建流程 /// Requirements: 1.1-1.6, 2.1-2.6 /// public class OrderServiceIntegrationTests { private HoneyBoxDbContext CreateInMemoryDbContext() { var options = new DbContextOptionsBuilder() .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()) .ConfigureWarnings(w => w.Ignore(InMemoryEventId.TransactionIgnoredWarning)) .Options; return new HoneyBoxDbContext(options); } private OrderService CreateOrderService(HoneyBoxDbContext dbContext) { var mockLogger = new Mock>(); var mockLotteryEngine = new Mock(); return new OrderService(dbContext, mockLogger.Object, mockLotteryEngine.Object); } #region 测试数据准备 private async Task CreateTestUserAsync(HoneyBoxDbContext dbContext, decimal money = 100, decimal integral = 1000, decimal money2 = 500) { var user = new User { Id = 1, OpenId = "test_openid", Uid = "test_uid", Nickname = "测试用户", HeadImg = "avatar.jpg", Mobile = "13800138000", Money = money, Integral = integral, Money2 = money2, IsTest = 0, Status = 1, CreatedAt = DateTime.Now, UpdatedAt = DateTime.Now }; await dbContext.Users.AddAsync(user); await dbContext.SaveChangesAsync(); return user; } private async Task CreateTestGoodsAsync(HoneyBoxDbContext dbContext, byte type = 1, decimal price = 10, int stock = 10) { // 添加商品类型 await dbContext.GoodsTypes.AddAsync(new GoodsType { Value = type, Name = "一番赏", FlName = "一番赏", CornerText = "一番赏", PayWechat = 1, PayBalance = 1, PayCurrency = 1, PayCurrency2 = 1, PayCoupon = 1, IsDeduction = 1 }); var goods = new Good { Id = 1, Title = "测试商品", Type = type, Status = 1, ShowIs = 0, Price = price, Stock = stock, SaleStock = 0, LockIs = 0, IsShouZhe = 0, QuanjuXiangou = 0, DailyXiangou = 0, ChoujiangXianzhi = 0, ImgUrl = "img.jpg", ImgUrlDetail = "detail.jpg" }; await dbContext.Goods.AddAsync(goods); await dbContext.SaveChangesAsync(); return goods; } private async Task CreateTestGoodsItemsAsync(HoneyBoxDbContext dbContext, int goodsId, int num = 1) { // 添加奖品等级 await dbContext.PrizeLevels.AddAsync(new PrizeLevel { Id = 10, Title = "A赏", Color = "#FF0000" }); await dbContext.PrizeLevels.AddAsync(new PrizeLevel { Id = 11, Title = "B赏", Color = "#00FF00" }); await dbContext.SaveChangesAsync(); // 添加奖品 var goodsItems = new List { new() { Id = 1, GoodsId = goodsId, Num = num, Title = "A赏奖品", Stock = 5, SurplusStock = 5, Price = 100, ScMoney = 50, ShangId = 10, GoodsListId = 0, ImgUrl = "a.jpg", Sort = 1 }, new() { Id = 2, GoodsId = goodsId, Num = num, Title = "B赏奖品", Stock = 10, SurplusStock = 10, Price = 50, ScMoney = 25, ShangId = 11, GoodsListId = 0, ImgUrl = "b.jpg", Sort = 2 } }; await dbContext.GoodsItems.AddRangeAsync(goodsItems); await dbContext.SaveChangesAsync(); } #endregion #region 订单金额计算测试 (Requirements 1.1-1.6) /// /// 测试基本订单金额计算 - 无抵扣 /// Requirements: 1.1 /// [Fact] public async Task CalculateOrderMoney_BasicCalculation_ReturnsCorrectAmount() { // Arrange var dbContext = CreateInMemoryDbContext(); var service = CreateOrderService(dbContext); await CreateTestUserAsync(dbContext); await CreateTestGoodsAsync(dbContext, price: 10); await CreateTestGoodsItemsAsync(dbContext, 1); var request = new OrderMoneyRequest { GoodsId = 1, Num = 1, PrizeNum = 3, UseMoneyIs = 2, UseIntegralIs = 2, UseMoney2Is = 2 }; // Act var result = await service.CalculateOrderMoneyAsync(1, request); // Assert Assert.NotNull(result); Assert.Equal("30.00", result.OrderTotal); // 10 * 3 = 30 Assert.NotNull(result.Goods); Assert.Equal(1, result.Goods.Id); } /// /// 测试余额抵扣 /// Requirements: 1.2 /// [Fact] public async Task CalculateOrderMoney_WithBalanceDeduction_DeductsCorrectly() { // Arrange var dbContext = CreateInMemoryDbContext(); var service = CreateOrderService(dbContext); await CreateTestUserAsync(dbContext, money: 20); await CreateTestGoodsAsync(dbContext, price: 10); await CreateTestGoodsItemsAsync(dbContext, 1); var request = new OrderMoneyRequest { GoodsId = 1, Num = 1, PrizeNum = 3, UseMoneyIs = 1, // 使用余额 UseIntegralIs = 2, UseMoney2Is = 2 }; // Act var result = await service.CalculateOrderMoneyAsync(1, request); // Assert Assert.NotNull(result); Assert.Equal("20.00", result.UseMoney); // 使用20元余额 Assert.Equal("10.00", result.Price); // 30 - 20 = 10 } /// /// 测试积分抵扣 (1:100比例) /// Requirements: 1.3 /// [Fact] public async Task CalculateOrderMoney_WithIntegralDeduction_DeductsCorrectly() { // Arrange var dbContext = CreateInMemoryDbContext(); var service = CreateOrderService(dbContext); await CreateTestUserAsync(dbContext, money: 0, integral: 2000); // 2000积分 = 20元 await CreateTestGoodsAsync(dbContext, price: 10); await CreateTestGoodsItemsAsync(dbContext, 1); var request = new OrderMoneyRequest { GoodsId = 1, Num = 1, PrizeNum = 3, UseMoneyIs = 2, UseIntegralIs = 1, // 使用积分 UseMoney2Is = 2 }; // Act var result = await service.CalculateOrderMoneyAsync(1, request); // Assert Assert.NotNull(result); Assert.Equal("2000.00", result.UseIntegral); // 使用2000积分 Assert.Equal("10.00", result.Price); // 30 - 20 = 10 } /// /// 测试哈尼券抵扣 (1:100比例) /// Requirements: 1.4 /// [Fact] public async Task CalculateOrderMoney_WithMoney2Deduction_DeductsCorrectly() { // Arrange var dbContext = CreateInMemoryDbContext(); var service = CreateOrderService(dbContext); await CreateTestUserAsync(dbContext, money: 0, integral: 0, money2: 1500); // 1500哈尼券 = 15元 await CreateTestGoodsAsync(dbContext, price: 10); await CreateTestGoodsItemsAsync(dbContext, 1); var request = new OrderMoneyRequest { GoodsId = 1, Num = 1, PrizeNum = 3, UseMoneyIs = 2, UseIntegralIs = 2, UseMoney2Is = 1 // 使用哈尼券 }; // Act var result = await service.CalculateOrderMoneyAsync(1, request); // Assert Assert.NotNull(result); Assert.Equal(1500.00m, result.UseMoney2); // 使用1500哈尼券 Assert.Equal("15.00", result.Price); // 30 - 15 = 15 } /// /// 测试组合抵扣 - 余额+积分+哈尼券 /// Requirements: 1.2, 1.3, 1.4 /// [Fact] public async Task CalculateOrderMoney_WithCombinedDeductions_DeductsCorrectly() { // Arrange var dbContext = CreateInMemoryDbContext(); var service = CreateOrderService(dbContext); await CreateTestUserAsync(dbContext, money: 10, integral: 1000, money2: 500); await CreateTestGoodsAsync(dbContext, price: 10); await CreateTestGoodsItemsAsync(dbContext, 1); var request = new OrderMoneyRequest { GoodsId = 1, Num = 1, PrizeNum = 3, // 总价30元 UseMoneyIs = 1, UseIntegralIs = 1, UseMoney2Is = 1 }; // Act var result = await service.CalculateOrderMoneyAsync(1, request); // Assert Assert.NotNull(result); // 余额10 + 积分10(1000/100) + 哈尼券5(500/100) = 25元抵扣 // 30 - 25 = 5元 Assert.Equal("10.00", result.UseMoney); Assert.Equal("1000.00", result.UseIntegral); Assert.Equal(500.00m, result.UseMoney2); Assert.Equal("5.00", result.Price); } /// /// 测试商品不存在时抛出异常 /// Requirements: 1.1 /// [Fact] public async Task CalculateOrderMoney_GoodsNotFound_ThrowsException() { // Arrange var dbContext = CreateInMemoryDbContext(); var service = CreateOrderService(dbContext); await CreateTestUserAsync(dbContext); var request = new OrderMoneyRequest { GoodsId = 999, Num = 1, PrizeNum = 1 }; // Act & Assert var ex = await Assert.ThrowsAsync( () => service.CalculateOrderMoneyAsync(1, request)); Assert.Equal("盒子不存在", ex.Message); } /// /// 测试商品已下架时抛出异常 /// Requirements: 1.1 /// [Fact] public async Task CalculateOrderMoney_GoodsOffline_ThrowsException() { // Arrange var dbContext = CreateInMemoryDbContext(); var service = CreateOrderService(dbContext); await CreateTestUserAsync(dbContext); // 创建下架商品 await dbContext.GoodsTypes.AddAsync(new GoodsType { Value = 1, Name = "一番赏", FlName = "一番赏", CornerText = "一番赏" }); var goods = new Good { Id = 1, Title = "下架商品", Type = 1, Status = 0, // 下架 Price = 10, Stock = 10, ImgUrl = "img.jpg", ImgUrlDetail = "detail.jpg" }; await dbContext.Goods.AddAsync(goods); await dbContext.SaveChangesAsync(); var request = new OrderMoneyRequest { GoodsId = 1, Num = 1, PrizeNum = 1 }; // Act & Assert var ex = await Assert.ThrowsAsync( () => service.CalculateOrderMoneyAsync(1, request)); Assert.Equal("盒子已下架", ex.Message); } /// /// 测试用户不存在时抛出异常 /// Requirements: 1.1 /// [Fact] public async Task CalculateOrderMoney_UserNotFound_ThrowsException() { // Arrange var dbContext = CreateInMemoryDbContext(); var service = CreateOrderService(dbContext); await CreateTestGoodsAsync(dbContext); var request = new OrderMoneyRequest { GoodsId = 1, Num = 1, PrizeNum = 1 }; // Act & Assert var ex = await Assert.ThrowsAsync( () => service.CalculateOrderMoneyAsync(999, request)); Assert.Equal("用户不存在", ex.Message); } /// /// 测试全局限购验证 /// Requirements: 1.6 /// [Fact] public async Task CalculateOrderMoney_ExceedsGlobalLimit_ThrowsException() { // Arrange var dbContext = CreateInMemoryDbContext(); var service = CreateOrderService(dbContext); await CreateTestUserAsync(dbContext); // 创建有全局限购的商品 await dbContext.GoodsTypes.AddAsync(new GoodsType { Value = 1, Name = "一番赏", FlName = "一番赏", CornerText = "一番赏", PayWechat = 1, PayBalance = 1, IsDeduction = 1 }); var goods = new Good { Id = 1, Title = "限购商品", Type = 1, Status = 1, Price = 10, Stock = 10, QuanjuXiangou = 2, // 全局限购2次 ImgUrl = "img.jpg", ImgUrlDetail = "detail.jpg" }; await dbContext.Goods.AddAsync(goods); await CreateTestGoodsItemsAsync(dbContext, 1); // 添加已购买记录 var orderItems = new List { new() { Id = 1, OrderId = 1, UserId = 1, GoodsId = 1, ShangId = 10, ParentGoodsListId = 0, Status = 0, Addtime = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds() }, new() { Id = 2, OrderId = 1, UserId = 1, GoodsId = 1, ShangId = 10, ParentGoodsListId = 0, Status = 0, Addtime = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds() } }; await dbContext.OrderItems.AddRangeAsync(orderItems); await dbContext.SaveChangesAsync(); var request = new OrderMoneyRequest { GoodsId = 1, Num = 1, PrizeNum = 1 }; // Act & Assert var ex = await Assert.ThrowsAsync( () => service.CalculateOrderMoneyAsync(1, request)); Assert.Contains("限购", ex.Message); } #endregion #region 订单创建测试 (Requirements 2.1-2.6) /// /// 测试订单创建 - 余额全额支付 /// Requirements: 2.1, 2.2, 2.4 /// Note: This test requires a real database due to transaction usage /// [Fact(Skip = "InMemory database does not support transactions")] public async Task CreateOrder_FullBalancePayment_CreatesOrderSuccessfully() { // Arrange var dbContext = CreateInMemoryDbContext(); var service = CreateOrderService(dbContext); await CreateTestUserAsync(dbContext, money: 100); await CreateTestGoodsAsync(dbContext, price: 10); await CreateTestGoodsItemsAsync(dbContext, 1); var request = new OrderBuyRequest { GoodsId = 1, Num = 1, PrizeNum = 3, // 总价30元 UseMoneyIs = 1, // 使用余额 UseIntegralIs = 2, UseMoney2Is = 2 }; // Act var result = await service.CreateOrderAsync(1, request); // Assert Assert.NotNull(result); Assert.Equal(0, result.Status); // 已支付完成 Assert.NotEmpty(result.OrderNum); Assert.Null(result.Res); // 无需微信支付 // 验证订单已创建 var order = await dbContext.Orders.FirstOrDefaultAsync(o => o.OrderNum == result.OrderNum); Assert.NotNull(order); Assert.Equal(1, order.Status); // 已支付 Assert.Equal(30, order.OrderTotal); Assert.Equal(30, order.UseMoney); Assert.Equal(0, order.Price); } /// /// 测试订单创建 - 需要微信支付 /// Requirements: 2.1, 2.2, 2.3 /// Note: This test requires a real database due to transaction usage /// [Fact(Skip = "InMemory database does not support transactions")] public async Task CreateOrder_RequiresWechatPayment_ReturnsPayParams() { // Arrange var dbContext = CreateInMemoryDbContext(); var service = CreateOrderService(dbContext); await CreateTestUserAsync(dbContext, money: 10); // 余额不足 await CreateTestGoodsAsync(dbContext, price: 10); await CreateTestGoodsItemsAsync(dbContext, 1); var request = new OrderBuyRequest { GoodsId = 1, Num = 1, PrizeNum = 3, // 总价30元 UseMoneyIs = 1, // 使用余额 UseIntegralIs = 2, UseMoney2Is = 2 }; // Act var result = await service.CreateOrderAsync(1, request); // Assert Assert.NotNull(result); Assert.Equal(1, result.Status); // 需要支付 Assert.NotEmpty(result.OrderNum); Assert.NotNull(result.Res); // 有微信支付参数 // 验证订单已创建但未支付 var order = await dbContext.Orders.FirstOrDefaultAsync(o => o.OrderNum == result.OrderNum); Assert.NotNull(order); Assert.Equal(0, order.Status); // 待支付 } /// /// 测试订单创建 - 未绑定手机号 /// Requirements: 2.6 /// [Fact] public async Task CreateOrder_MobileNotBound_ThrowsException() { // Arrange var dbContext = CreateInMemoryDbContext(); var service = CreateOrderService(dbContext); // 创建未绑定手机号的用户 var user = new User { Id = 1, OpenId = "test_openid", Uid = "test_uid", Nickname = "测试用户", HeadImg = "avatar.jpg", Mobile = null, // 未绑定手机号 Money = 100, Status = 1, CreatedAt = DateTime.Now, UpdatedAt = DateTime.Now }; await dbContext.Users.AddAsync(user); await dbContext.SaveChangesAsync(); await CreateTestGoodsAsync(dbContext, price: 10); await CreateTestGoodsItemsAsync(dbContext, 1); var request = new OrderBuyRequest { GoodsId = 1, Num = 1, PrizeNum = 1 }; // Act & Assert var ex = await Assert.ThrowsAsync( () => service.CreateOrderAsync(1, request)); Assert.Equal("请先绑定手机号", ex.Message); } /// /// 测试订单创建 - 库存不足 /// Requirements: 2.5 /// [Fact] public async Task CreateOrder_InsufficientStock_ThrowsException() { // Arrange var dbContext = CreateInMemoryDbContext(); var service = CreateOrderService(dbContext); await CreateTestUserAsync(dbContext, money: 100); await CreateTestGoodsAsync(dbContext, price: 10); // 创建库存不足的奖品 await dbContext.PrizeLevels.AddAsync(new PrizeLevel { Id = 10, Title = "A赏" }); await dbContext.GoodsItems.AddAsync(new GoodsItem { Id = 1, GoodsId = 1, Num = 1, Title = "A赏奖品", Stock = 5, SurplusStock = 2, // 只剩2个 ShangId = 10, ImgUrl = "a.jpg" }); await dbContext.SaveChangesAsync(); var request = new OrderBuyRequest { GoodsId = 1, Num = 1, PrizeNum = 5 // 要买5个,但只有2个 }; // Act & Assert var ex = await Assert.ThrowsAsync( () => service.CreateOrderAsync(1, request)); Assert.Contains("不足", ex.Message); } /// /// 测试订单创建 - 订单号唯一性 /// Requirements: 2.2 /// Note: This test requires a real database due to transaction usage /// [Fact(Skip = "InMemory database does not support transactions")] public async Task CreateOrder_GeneratesUniqueOrderNum() { // Arrange var dbContext = CreateInMemoryDbContext(); var service = CreateOrderService(dbContext); await CreateTestUserAsync(dbContext, money: 100); await CreateTestGoodsAsync(dbContext, price: 10); await CreateTestGoodsItemsAsync(dbContext, 1); var request = new OrderBuyRequest { GoodsId = 1, Num = 1, PrizeNum = 1, UseMoneyIs = 1 }; // Act - 创建两个订单 var result1 = await service.CreateOrderAsync(1, request); var result2 = await service.CreateOrderAsync(1, request); // Assert - 订单号应该不同 Assert.NotEqual(result1.OrderNum, result2.OrderNum); } /// /// 测试订单创建 - 用户资产扣减 /// Requirements: 2.4 /// Note: This test requires a real database due to transaction usage /// [Fact(Skip = "InMemory database does not support transactions")] public async Task CreateOrder_DeductsUserAssets_Correctly() { // Arrange var dbContext = CreateInMemoryDbContext(); var service = CreateOrderService(dbContext); await CreateTestUserAsync(dbContext, money: 50, integral: 1000, money2: 500); await CreateTestGoodsAsync(dbContext, price: 10); await CreateTestGoodsItemsAsync(dbContext, 1); var request = new OrderBuyRequest { GoodsId = 1, Num = 1, PrizeNum = 3, // 总价30元 UseMoneyIs = 1, UseIntegralIs = 1, UseMoney2Is = 1 }; // Act var result = await service.CreateOrderAsync(1, request); // Assert Assert.Equal(0, result.Status); // 已支付完成 // 验证用户资产已扣减 var user = await dbContext.Users.FindAsync(1); Assert.NotNull(user); // 30元 = 余额30 或 余额+积分+哈尼券组合 // 具体扣减取决于实现逻辑 Assert.True(user.Money < 50 || user.Integral < 1000 || user.Money2 < 500); } #endregion #region 订单查询测试 (Requirements 6.1-6.4, 7.1-7.3) /// /// 测试订单列表查询 - 分页 /// Requirements: 6.1, 6.3, 6.4 /// [Fact] public async Task GetOrderList_Pagination_ReturnsCorrectPage() { // Arrange var dbContext = CreateInMemoryDbContext(); var service = CreateOrderService(dbContext); await CreateTestUserAsync(dbContext); // 创建15个订单 var orders = Enumerable.Range(1, 15).Select(i => new Order { Id = i, UserId = 1, OrderNum = $"MH_YFS{DateTime.Now:yyyyMMddHHmmss}{i:0000}", OrderTotal = 10 * i, OrderZheTotal = 10 * i, Price = 0, UseMoney = 10 * i, GoodsId = 1, GoodsTitle = $"商品{i}", GoodsImgurl = $"img{i}.jpg", PrizeNum = i, Status = 1, // 已支付 Addtime = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds() - i * 60, CreatedAt = DateTime.Now, UpdatedAt = DateTime.Now }).ToList(); await dbContext.Orders.AddRangeAsync(orders); await dbContext.SaveChangesAsync(); // Act - 第一页 var page1 = await service.GetOrderListAsync(1, new OrderListRequest { Page = 1, PageSize = 10 }); // Act - 第二页 var page2 = await service.GetOrderListAsync(1, new OrderListRequest { Page = 2, PageSize = 10 }); // Assert Assert.Equal(15, page1.Total); Assert.Equal(2, page1.LastPage); Assert.Equal(10, page1.Data.Count); Assert.Equal(5, page2.Data.Count); } /// /// 测试订单详情查询 /// Requirements: 7.1, 7.2 /// [Fact] public async Task GetOrderDetail_ReturnsCompleteInfo() { // Arrange var dbContext = CreateInMemoryDbContext(); var service = CreateOrderService(dbContext); await CreateTestUserAsync(dbContext); var orderNum = "MH_YFS202501021234561234"; var order = new Order { Id = 1, UserId = 1, OrderNum = orderNum, OrderTotal = 30, OrderZheTotal = 30, Price = 0, UseMoney = 30, GoodsId = 1, GoodsPrice = 10, GoodsTitle = "测试商品", GoodsImgurl = "img.jpg", PrizeNum = 3, Status = 1, Addtime = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds(), PayTime = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds(), CreatedAt = DateTime.Now, UpdatedAt = DateTime.Now }; await dbContext.Orders.AddAsync(order); // 添加奖品记录 var orderItems = new List { new() { Id = 1, OrderId = 1, UserId = 1, GoodsId = 1, ShangId = 10, GoodslistTitle = "A赏奖品", GoodslistImgurl = "a.jpg", GoodslistPrice = 100, GoodslistMoney = 50, Status = 0, LuckNo = 1, Addtime = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds() }, new() { Id = 2, OrderId = 1, UserId = 1, GoodsId = 1, ShangId = 11, GoodslistTitle = "B赏奖品", GoodslistImgurl = "b.jpg", GoodslistPrice = 50, GoodslistMoney = 25, Status = 0, LuckNo = 2, Addtime = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds() } }; await dbContext.OrderItems.AddRangeAsync(orderItems); await dbContext.SaveChangesAsync(); // Act var result = await service.GetOrderDetailAsync(1, orderNum); // Assert Assert.NotNull(result); Assert.NotNull(result.OrderInfo); Assert.Equal(orderNum, result.OrderInfo.OrderNum); Assert.Equal("30.00", result.OrderInfo.OrderTotal); Assert.NotNull(result.PrizeList); Assert.Equal(2, result.PrizeList.Count); } /// /// 测试订单详情查询 - 订单不存在 /// Requirements: 7.3 /// [Fact] public async Task GetOrderDetail_OrderNotFound_ThrowsException() { // Arrange var dbContext = CreateInMemoryDbContext(); var service = CreateOrderService(dbContext); await CreateTestUserAsync(dbContext); // Act & Assert var ex = await Assert.ThrowsAsync( () => service.GetOrderDetailAsync(1, "NONEXISTENT_ORDER")); Assert.Equal("订单不存在", ex.Message); } #endregion }