HaniBlindBox/docs/数据库迁移详细文档/阶段5-功能测试和性能优化.md
2026-01-02 01:00:52 +08:00

33 KiB
Raw Blame History

阶段5功能测试和性能优化

阶段概述

时间: 1天
目标: 全面测试迁移后的功能,优化数据库性能,确保系统稳定运行
优先级: P0 (最高优先级)

详细任务清单

5.1 功能测试 (0.4天)

任务描述

全面测试所有业务功能,确保迁移后功能正常

具体工作

  • 用户认证和登录测试
  • 商品展示和查询测试
  • 抽奖功能测试
  • 订单和支付测试
  • 财务系统测试

功能测试用例

1. 用户系统测试
// Tests/UserSystemTests.cs
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using HoneyBox.Data;
using HoneyBox.Core.Entities;
using Xunit;

namespace HoneyBox.Tests.Integration
{
    public class UserSystemTests : IClassFixture<TestWebApplicationFactory>
    {
        private readonly TestWebApplicationFactory _factory;
        private readonly HoneyBoxDbContext _context;

        public UserSystemTests(TestWebApplicationFactory factory)
        {
            _factory = factory;
            var scope = factory.Services.CreateScope();
            _context = scope.ServiceProvider.GetRequiredService<HoneyBoxDbContext>();
        }

        [Fact]
        public async Task UserLogin_NewUser_ShouldCreateUser()
        {
            // Arrange
            var client = _factory.CreateClient();
            var loginRequest = new
            {
                openId = "test_openid_001",
                unionId = "test_unionid_001",
                nickname = "测试用户",
                headImg = "https://example.com/avatar.jpg"
            };

            // Act
            var response = await client.PostAsJsonAsync("/api/user/login", loginRequest);

            // Assert
            response.EnsureSuccessStatusCode();
            var user = await _context.Users.FirstOrDefaultAsync(u => u.OpenId == loginRequest.openId);
            Assert.NotNull(user);
            Assert.Equal(loginRequest.nickname, user.Nickname);
            Assert.Equal(loginRequest.headImg, user.HeadImg);
        }

        [Fact]
        public async Task UserLogin_ExistingUser_ShouldUpdateInfo()
        {
            // Arrange
            var existingUser = new User
            {
                OpenId = "test_openid_002",
                Nickname = "旧昵称",
                HeadImg = "old_avatar.jpg",
                Uid = "U001",
                CreatedAt = DateTime.Now.AddDays(-1)
            };
            _context.Users.Add(existingUser);
            await _context.SaveChangesAsync();

            var client = _factory.CreateClient();
            var loginRequest = new
            {
                openId = "test_openid_002",
                nickname = "新昵称",
                headImg = "new_avatar.jpg"
            };

            // Act
            var response = await client.PostAsJsonAsync("/api/user/login", loginRequest);

            // Assert
            response.EnsureSuccessStatusCode();
            var updatedUser = await _context.Users.FirstOrDefaultAsync(u => u.OpenId == loginRequest.openId);
            Assert.NotNull(updatedUser);
            Assert.Equal(loginRequest.nickname, updatedUser.Nickname);
            Assert.Equal(loginRequest.headImg, updatedUser.HeadImg);
            Assert.NotNull(updatedUser.LastLoginAt);
        }

        [Fact]
        public async Task GetUserBalance_ValidUser_ShouldReturnBalance()
        {
            // Arrange
            var user = new User
            {
                OpenId = "test_openid_003",
                Nickname = "余额测试用户",
                HeadImg = "avatar.jpg",
                Uid = "U002",
                Money = 100.50m,
                Money2 = 50.25m,
                Integral = 200.75m,
                Score = 300.00m
            };
            _context.Users.Add(user);
            await _context.SaveChangesAsync();

            var client = _factory.CreateClient();

            // Act
            var response = await client.GetAsync($"/api/user/{user.Id}/balance");

            // Assert
            response.EnsureSuccessStatusCode();
            var balance = await response.Content.ReadFromJsonAsync<dynamic>();
            Assert.NotNull(balance);
        }
    }
}
2. 商品系统测试
// Tests/GoodsSystemTests.cs
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using HoneyBox.Data;
using HoneyBox.Core.Entities;
using Xunit;

namespace HoneyBox.Tests.Integration
{
    public class GoodsSystemTests : IClassFixture<TestWebApplicationFactory>
    {
        private readonly TestWebApplicationFactory _factory;
        private readonly HoneyBoxDbContext _context;

        public GoodsSystemTests(TestWebApplicationFactory factory)
        {
            _factory = factory;
            var scope = factory.Services.CreateScope();
            _context = scope.ServiceProvider.GetRequiredService<HoneyBoxDbContext>();
        }

        [Fact]
        public async Task GetGoods_WithPagination_ShouldReturnCorrectPage()
        {
            // Arrange
            await SeedGoodsData();
            var client = _factory.CreateClient();

            // Act
            var response = await client.GetAsync("/api/goods?page=1&pageSize=5");

            // Assert
            response.EnsureSuccessStatusCode();
            var goods = await response.Content.ReadFromJsonAsync<List<Goods>>();
            Assert.NotNull(goods);
            Assert.True(goods.Count <= 5);
        }

        [Fact]
        public async Task GetGoods_WithTypeFilter_ShouldReturnFilteredResults()
        {
            // Arrange
            await SeedGoodsData();
            var client = _factory.CreateClient();

            // Act
            var response = await client.GetAsync("/api/goods?type=1");

            // Assert
            response.EnsureSuccessStatusCode();
            var goods = await response.Content.ReadFromJsonAsync<List<Goods>>();
            Assert.NotNull(goods);
            Assert.All(goods, g => Assert.Equal((byte)1, g.Type));
        }

        [Fact]
        public async Task GetGoodsById_ValidId_ShouldReturnGoodsWithItems()
        {
            // Arrange
            var goods = await CreateTestGoods();
            var client = _factory.CreateClient();

            // Act
            var response = await client.GetAsync($"/api/goods/{goods.Id}");

            // Assert
            response.EnsureSuccessStatusCode();
            var result = await response.Content.ReadFromJsonAsync<Goods>();
            Assert.NotNull(result);
            Assert.Equal(goods.Id, result.Id);
            Assert.NotEmpty(result.Items);
        }

        private async Task SeedGoodsData()
        {
            var goods = new List<Goods>();
            for (int i = 1; i <= 10; i++)
            {
                goods.Add(new Goods
                {
                    Title = $"测试商品{i}",
                    ImgUrl = $"goods{i}.jpg",
                    ImgUrlDetail = $"goods{i}_detail.jpg",
                    Price = i * 10,
                    Type = (byte)(i % 3),
                    Status = 1,
                    IsShow = 1,
                    SortOrder = i
                });
            }
            _context.Goods.AddRange(goods);
            await _context.SaveChangesAsync();
        }

        private async Task<Goods> CreateTestGoods()
        {
            var goods = new Goods
            {
                Title = "测试盲盒",
                ImgUrl = "test_box.jpg",
                ImgUrlDetail = "test_box_detail.jpg",
                Price = 29.9m,
                Type = 1,
                Status = 1,
                IsShow = 1,
                Items = new List<GoodsItem>
                {
                    new GoodsItem
                    {
                        BoxNumber = 1,
                        PrizeId = 1,
                        Title = "普通奖品",
                        Stock = 100,
                        SurplusStock = 80,
                        ImgUrl = "prize1.jpg",
                        Price = 5.0m,
                        RealProbability = 0.6m
                    },
                    new GoodsItem
                    {
                        BoxNumber = 1,
                        PrizeId = 2,
                        Title = "稀有奖品",
                        Stock = 20,
                        SurplusStock = 15,
                        ImgUrl = "prize2.jpg",
                        Price = 50.0m,
                        RealProbability = 0.1m
                    }
                }
            };

            _context.Goods.Add(goods);
            await _context.SaveChangesAsync();
            return goods;
        }
    }
}
3. 抽奖系统测试
// Tests/LotterySystemTests.cs
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using HoneyBox.Data;
using HoneyBox.Core.Entities;
using HoneyBox.Core.Services;
using Xunit;

namespace HoneyBox.Tests.Integration
{
    public class LotterySystemTests : IClassFixture<TestWebApplicationFactory>
    {
        private readonly TestWebApplicationFactory _factory;
        private readonly HoneyBoxDbContext _context;
        private readonly ILotteryService _lotteryService;

        public LotterySystemTests(TestWebApplicationFactory factory)
        {
            _factory = factory;
            var scope = factory.Services.CreateScope();
            _context = scope.ServiceProvider.GetRequiredService<HoneyBoxDbContext>();
            _lotteryService = scope.ServiceProvider.GetRequiredService<ILotteryService>();
        }

        [Fact]
        public async Task DrawLottery_ValidOrder_ShouldReturnPrize()
        {
            // Arrange
            var user = await CreateTestUser();
            var goods = await CreateTestGoodsWithItems();
            var order = await CreateTestOrder(user.Id, goods.Id);

            // Act
            var result = await _lotteryService.DrawAsync(order.Id);

            // Assert
            Assert.True(result.Success);
            Assert.NotNull(result.Prize);
            
            // 验证订单状态更新
            var updatedOrder = await _context.Orders.FindAsync(order.Id);
            Assert.Equal((byte)1, updatedOrder.Status);
            
            // 验证订单详情创建
            var orderItems = await _context.OrderItems
                .Where(oi => oi.OrderId == order.OrderNo)
                .ToListAsync();
            Assert.NotEmpty(orderItems);
        }

        [Fact]
        public async Task DrawLottery_InsufficientStock_ShouldFail()
        {
            // Arrange
            var user = await CreateTestUser();
            var goods = await CreateTestGoodsWithNoStock();
            var order = await CreateTestOrder(user.Id, goods.Id);

            // Act
            var result = await _lotteryService.DrawAsync(order.Id);

            // Assert
            Assert.False(result.Success);
            Assert.Contains("库存不足", result.Message);
        }

        [Fact]
        public async Task DrawLottery_MultipleDraws_ShouldRespectProbability()
        {
            // Arrange
            var user = await CreateTestUser();
            var goods = await CreateTestGoodsWithItems();
            var drawCount = 100;
            var results = new List<string>();

            // Act
            for (int i = 0; i < drawCount; i++)
            {
                var order = await CreateTestOrder(user.Id, goods.Id);
                var result = await _lotteryService.DrawAsync(order.Id);
                if (result.Success)
                {
                    results.Add(result.Prize.Title);
                }
            }

            // Assert
            Assert.NotEmpty(results);
            
            // 验证概率分布(允许一定误差)
            var commonPrizes = results.Count(r => r.Contains("普通"));
            var rarePrizes = results.Count(r => r.Contains("稀有"));
            
            Assert.True(commonPrizes > rarePrizes, "普通奖品应该比稀有奖品多");
        }

        private async Task<User> CreateTestUser()
        {
            var user = new User
            {
                OpenId = Guid.NewGuid().ToString(),
                Nickname = "测试用户",
                HeadImg = "avatar.jpg",
                Uid = "U" + DateTime.Now.Ticks,
                Money = 1000m
            };
            _context.Users.Add(user);
            await _context.SaveChangesAsync();
            return user;
        }

        private async Task<Goods> CreateTestGoodsWithItems()
        {
            var goods = new Goods
            {
                Title = "测试盲盒",
                ImgUrl = "box.jpg",
                ImgUrlDetail = "box_detail.jpg",
                Price = 29.9m,
                Status = 1,
                Items = new List<GoodsItem>
                {
                    new GoodsItem
                    {
                        BoxNumber = 1,
                        PrizeId = 1,
                        Title = "普通奖品",
                        Stock = 1000,
                        SurplusStock = 1000,
                        ImgUrl = "common.jpg",
                        RealProbability = 0.7m
                    },
                    new GoodsItem
                    {
                        BoxNumber = 1,
                        PrizeId = 2,
                        Title = "稀有奖品",
                        Stock = 100,
                        SurplusStock = 100,
                        ImgUrl = "rare.jpg",
                        RealProbability = 0.3m
                    }
                }
            };
            _context.Goods.Add(goods);
            await _context.SaveChangesAsync();
            return goods;
        }

        private async Task<Order> CreateTestOrder(int userId, int goodsId)
        {
            var order = new Order
            {
                OrderNo = "TEST" + DateTime.Now.Ticks,
                UserId = userId,
                GoodsId = goodsId,
                BoxNumber = 1,
                GoodsTitle = "测试盲盒",
                OrderTotal = 29.9m,
                DiscountTotal = 29.9m,
                FinalPrice = 29.9m,
                DrawCount = 1,
                Status = 0
            };
            _context.Orders.Add(order);
            await _context.SaveChangesAsync();
            return order;
        }
    }
}

5.2 性能测试 (0.3天)

任务描述

测试数据库查询性能,识别性能瓶颈

具体工作

  • 查询性能基准测试
  • 并发性能测试
  • 内存使用测试
  • 响应时间测试

性能测试脚本

1. 查询性能测试
// Tests/PerformanceTests.cs
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using HoneyBox.Data;
using System.Diagnostics;
using Xunit;
using Xunit.Abstractions;

namespace HoneyBox.Tests.Performance
{
    public class PerformanceTests : IClassFixture<TestWebApplicationFactory>
    {
        private readonly TestWebApplicationFactory _factory;
        private readonly HoneyBoxDbContext _context;
        private readonly ITestOutputHelper _output;

        public PerformanceTests(TestWebApplicationFactory factory, ITestOutputHelper output)
        {
            _factory = factory;
            _output = output;
            var scope = factory.Services.CreateScope();
            _context = scope.ServiceProvider.GetRequiredService<HoneyBoxDbContext>();
        }

        [Fact]
        public async Task UserQuery_Performance_ShouldBeFast()
        {
            // Arrange
            await SeedUsers(10000);
            var stopwatch = Stopwatch.StartNew();

            // Act
            var users = await _context.Users
                .Where(u => u.Status == 1)
                .OrderByDescending(u => u.CreatedAt)
                .Take(100)
                .ToListAsync();

            stopwatch.Stop();

            // Assert
            Assert.NotEmpty(users);
            Assert.True(stopwatch.ElapsedMilliseconds < 100, 
                $"查询耗时 {stopwatch.ElapsedMilliseconds}ms超过预期的100ms");
            
            _output.WriteLine($"用户查询耗时: {stopwatch.ElapsedMilliseconds}ms");
        }

        [Fact]
        public async Task GoodsQuery_WithJoins_ShouldBeFast()
        {
            // Arrange
            await SeedGoodsWithItems(1000);
            var stopwatch = Stopwatch.StartNew();

            // Act
            var goods = await _context.Goods
                .Include(g => g.Items)
                .Where(g => g.Status == 1)
                .OrderByDescending(g => g.SortOrder)
                .Take(50)
                .ToListAsync();

            stopwatch.Stop();

            // Assert
            Assert.NotEmpty(goods);
            Assert.True(stopwatch.ElapsedMilliseconds < 200, 
                $"商品查询耗时 {stopwatch.ElapsedMilliseconds}ms超过预期的200ms");
            
            _output.WriteLine($"商品查询耗时: {stopwatch.ElapsedMilliseconds}ms");
        }

        [Fact]
        public async Task OrderQuery_ComplexJoins_ShouldBeFast()
        {
            // Arrange
            await SeedOrdersWithRelations(5000);
            var stopwatch = Stopwatch.StartNew();

            // Act
            var orders = await _context.Orders
                .Include(o => o.User)
                .Include(o => o.Goods)
                .Include(o => o.Items)
                .Where(o => o.Status == 1)
                .Where(o => o.CreatedAt >= DateTime.Now.AddDays(-30))
                .OrderByDescending(o => o.CreatedAt)
                .Take(100)
                .ToListAsync();

            stopwatch.Stop();

            // Assert
            Assert.NotEmpty(orders);
            Assert.True(stopwatch.ElapsedMilliseconds < 300, 
                $"订单查询耗时 {stopwatch.ElapsedMilliseconds}ms超过预期的300ms");
            
            _output.WriteLine($"订单查询耗时: {stopwatch.ElapsedMilliseconds}ms");
        }

        [Fact]
        public async Task ConcurrentUserLogin_ShouldHandleLoad()
        {
            // Arrange
            var tasks = new List<Task>();
            var userCount = 100;
            var stopwatch = Stopwatch.StartNew();

            // Act
            for (int i = 0; i < userCount; i++)
            {
                var openId = $"concurrent_user_{i}";
                tasks.Add(SimulateUserLogin(openId));
            }

            await Task.WhenAll(tasks);
            stopwatch.Stop();

            // Assert
            var createdUsers = await _context.Users
                .Where(u => u.OpenId.StartsWith("concurrent_user_"))
                .CountAsync();
            
            Assert.Equal(userCount, createdUsers);
            Assert.True(stopwatch.ElapsedMilliseconds < 5000, 
                $"并发登录耗时 {stopwatch.ElapsedMilliseconds}ms超过预期的5000ms");
            
            _output.WriteLine($"并发登录({userCount}用户)耗时: {stopwatch.ElapsedMilliseconds}ms");
        }

        private async Task SimulateUserLogin(string openId)
        {
            using var scope = _factory.Services.CreateScope();
            var context = scope.ServiceProvider.GetRequiredService<HoneyBoxDbContext>();
            
            var user = new User
            {
                OpenId = openId,
                Nickname = $"用户{openId}",
                HeadImg = "avatar.jpg",
                Uid = "U" + DateTime.Now.Ticks + Random.Shared.Next(1000, 9999)
            };
            
            context.Users.Add(user);
            await context.SaveChangesAsync();
        }

        private async Task SeedUsers(int count)
        {
            var users = new List<User>();
            for (int i = 0; i < count; i++)
            {
                users.Add(new User
                {
                    OpenId = $"perf_user_{i}",
                    Nickname = $"性能测试用户{i}",
                    HeadImg = "avatar.jpg",
                    Uid = $"U{i:D6}",
                    Status = (byte)(i % 10 == 0 ? 0 : 1), // 10%的用户状态为0
                    CreatedAt = DateTime.Now.AddDays(-Random.Shared.Next(0, 365))
                });
            }
            
            _context.Users.AddRange(users);
            await _context.SaveChangesAsync();
        }

        private async Task SeedGoodsWithItems(int goodsCount)
        {
            var goods = new List<Goods>();
            for (int i = 0; i < goodsCount; i++)
            {
                var goodsItem = new Goods
                {
                    Title = $"性能测试商品{i}",
                    ImgUrl = $"goods{i}.jpg",
                    ImgUrlDetail = $"goods{i}_detail.jpg",
                    Price = Random.Shared.Next(10, 100),
                    Status = 1,
                    SortOrder = Random.Shared.Next(1, 1000),
                    Items = new List<GoodsItem>()
                };

                // 为每个商品添加3-5个奖品
                var itemCount = Random.Shared.Next(3, 6);
                for (int j = 0; j < itemCount; j++)
                {
                    goodsItem.Items.Add(new GoodsItem
                    {
                        BoxNumber = 1,
                        PrizeId = j + 1,
                        Title = $"奖品{j + 1}",
                        Stock = Random.Shared.Next(50, 200),
                        SurplusStock = Random.Shared.Next(10, 50),
                        ImgUrl = $"prize{j + 1}.jpg",
                        RealProbability = Random.Shared.NextSingle()
                    });
                }

                goods.Add(goodsItem);
            }

            _context.Goods.AddRange(goods);
            await _context.SaveChangesAsync();
        }
    }
}

5.3 性能优化 (0.3天)

任务描述

根据测试结果优化数据库性能

具体工作

  • 分析查询执行计划
  • 优化索引策略
  • 调整数据库参数
  • 实施缓存策略

性能优化措施

1. 索引优化
-- 分析查询执行计划
SET STATISTICS IO ON;
SET STATISTICS TIME ON;

-- 用户查询优化
SELECT u.id, u.nickname, u.head_img, u.money, u.integral
FROM users u
WHERE u.status = 1 
  AND u.created_at >= DATEADD(DAY, -30, GETDATE())
ORDER BY u.created_at DESC;

-- 创建复合索引优化上述查询
CREATE INDEX ix_users_status_created_desc ON users(status, created_at DESC)
WHERE status = 1;

-- 商品查询优化
SELECT g.id, g.title, g.img_url, g.price, g.sort_order
FROM goods g
WHERE g.status = 1 
  AND g.is_show = 1 
  AND g.type = 1
ORDER BY g.sort_order DESC, g.created_at DESC;

-- 创建复合索引优化商品查询
CREATE INDEX ix_goods_status_show_type_sort ON goods(status, is_show, type, sort_order DESC, created_at DESC)
WHERE status = 1 AND is_show = 1;

-- 订单查询优化
SELECT o.id, o.order_no, o.user_id, o.goods_id, o.order_total, o.status, o.created_at
FROM orders o
WHERE o.user_id = @UserId 
  AND o.created_at >= DATEADD(DAY, -90, GETDATE())
ORDER BY o.created_at DESC;

-- 创建用户订单查询索引
CREATE INDEX ix_orders_user_created_desc ON orders(user_id, created_at DESC);

-- 抽奖库存查询优化
SELECT gi.id, gi.goods_id, gi.box_number, gi.surplus_stock, gi.real_probability
FROM goods_items gi
WHERE gi.goods_id = @GoodsId 
  AND gi.box_number = @BoxNumber 
  AND gi.surplus_stock > 0;

-- 创建抽奖查询索引
CREATE INDEX ix_goods_items_goods_box_stock ON goods_items(goods_id, box_number, surplus_stock)
WHERE surplus_stock > 0;
2. 查询优化
-- 启用查询存储分析慢查询
ALTER DATABASE honey_box SET QUERY_STORE = ON;
ALTER DATABASE honey_box SET QUERY_STORE (
    OPERATION_MODE = READ_WRITE,
    CLEANUP_POLICY = (STALE_QUERY_THRESHOLD_DAYS = 30),
    DATA_FLUSH_INTERVAL_SECONDS = 900,
    INTERVAL_LENGTH_MINUTES = 60,
    MAX_STORAGE_SIZE_MB = 1000
);

-- 分析最耗时的查询
SELECT TOP 10
    qst.query_sql_text,
    qrs.avg_duration/1000.0 as avg_duration_ms,
    qrs.avg_cpu_time/1000.0 as avg_cpu_time_ms,
    qrs.count_executions,
    qrs.avg_logical_io_reads,
    qrs.last_execution_time
FROM sys.query_store_query_text qst
JOIN sys.query_store_query q ON qst.query_text_id = q.query_text_id
JOIN sys.query_store_plan qsp ON q.query_id = qsp.query_id
JOIN sys.query_store_runtime_stats qrs ON qsp.plan_id = qrs.plan_id
WHERE qrs.avg_duration > 10000 -- 超过10ms的查询
ORDER BY qrs.avg_duration DESC;

-- 更新表统计信息
UPDATE STATISTICS users;
UPDATE STATISTICS goods;
UPDATE STATISTICS orders;
UPDATE STATISTICS goods_items;

-- 重建索引碎片整理
ALTER INDEX ALL ON users REORGANIZE;
ALTER INDEX ALL ON goods REORGANIZE;
ALTER INDEX ALL ON orders REORGANIZE;
3. 内存优化配置
-- 为抽奖系统创建内存优化表
CREATE TABLE goods_inventory_memory (
    goods_id INT NOT NULL,
    box_number INT NOT NULL,
    surplus_stock INT NOT NULL,
    last_update_time DATETIME2 NOT NULL DEFAULT GETDATE(),
    
    CONSTRAINT pk_goods_inventory_memory PRIMARY KEY NONCLUSTERED (goods_id, box_number)
) WITH (MEMORY_OPTIMIZED = ON, DURABILITY = SCHEMA_AND_DATA);

-- 创建内存优化的库存扣减存储过程
CREATE PROCEDURE sp_deduct_inventory_memory
    @goods_id INT,
    @box_number INT,
    @quantity INT = 1
WITH NATIVE_COMPILATION, SCHEMABINDING
AS BEGIN ATOMIC WITH (
    TRANSACTION ISOLATION LEVEL = SNAPSHOT,
    LANGUAGE = N'us_english'
)
    DECLARE @current_stock INT;
    
    SELECT @current_stock = surplus_stock 
    FROM dbo.goods_inventory_memory 
    WHERE goods_id = @goods_id AND box_number = @box_number;
    
    IF @current_stock >= @quantity
    BEGIN
        UPDATE dbo.goods_inventory_memory 
        SET surplus_stock = surplus_stock - @quantity,
            last_update_time = GETDATE()
        WHERE goods_id = @goods_id AND box_number = @box_number;
        
        RETURN 1; -- 成功
    END
    
    RETURN 0; -- 库存不足
END;
4. 缓存策略实施
// Services/CacheService.cs
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Caching.Distributed;
using System.Text.Json;

namespace HoneyBox.Core.Services
{
    public interface ICacheService
    {
        Task<T?> GetAsync<T>(string key);
        Task SetAsync<T>(string key, T value, TimeSpan? expiry = null);
        Task RemoveAsync(string key);
        Task RemovePatternAsync(string pattern);
    }

    public class CacheService : ICacheService
    {
        private readonly IMemoryCache _memoryCache;
        private readonly IDistributedCache _distributedCache;
        private readonly ILogger<CacheService> _logger;

        public CacheService(
            IMemoryCache memoryCache,
            IDistributedCache distributedCache,
            ILogger<CacheService> logger)
        {
            _memoryCache = memoryCache;
            _distributedCache = distributedCache;
            _logger = logger;
        }

        public async Task<T?> GetAsync<T>(string key)
        {
            try
            {
                // 先从内存缓存获取
                if (_memoryCache.TryGetValue(key, out T? memoryValue))
                {
                    return memoryValue;
                }

                // 再从分布式缓存获取
                var distributedValue = await _distributedCache.GetStringAsync(key);
                if (!string.IsNullOrEmpty(distributedValue))
                {
                    var value = JsonSerializer.Deserialize<T>(distributedValue);
                    
                    // 回写到内存缓存
                    _memoryCache.Set(key, value, TimeSpan.FromMinutes(5));
                    
                    return value;
                }

                return default(T);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "缓存获取失败: {Key}", key);
                return default(T);
            }
        }

        public async Task SetAsync<T>(string key, T value, TimeSpan? expiry = null)
        {
            try
            {
                var defaultExpiry = expiry ?? TimeSpan.FromMinutes(30);
                
                // 设置内存缓存
                _memoryCache.Set(key, value, TimeSpan.FromMinutes(5));
                
                // 设置分布式缓存
                var serializedValue = JsonSerializer.Serialize(value);
                var options = new DistributedCacheEntryOptions
                {
                    AbsoluteExpirationRelativeToNow = defaultExpiry
                };
                
                await _distributedCache.SetStringAsync(key, serializedValue, options);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "缓存设置失败: {Key}", key);
            }
        }

        public async Task RemoveAsync(string key)
        {
            try
            {
                _memoryCache.Remove(key);
                await _distributedCache.RemoveAsync(key);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "缓存删除失败: {Key}", key);
            }
        }

        public async Task RemovePatternAsync(string pattern)
        {
            // 实现模式匹配删除(需要根据具体缓存实现)
            _logger.LogWarning("模式删除功能需要根据具体缓存实现");
        }
    }
}

// 缓存装饰器模式
public class CachedGoodsService : IGoodsService
{
    private readonly IGoodsService _goodsService;
    private readonly ICacheService _cacheService;
    private readonly ILogger<CachedGoodsService> _logger;

    public CachedGoodsService(
        IGoodsService goodsService,
        ICacheService cacheService,
        ILogger<CachedGoodsService> logger)
    {
        _goodsService = goodsService;
        _cacheService = cacheService;
        _logger = logger;
    }

    public async Task<List<Goods>> GetGoodsAsync(int page, int pageSize, byte? type = null)
    {
        var cacheKey = $"goods:list:{page}:{pageSize}:{type}";
        
        var cachedGoods = await _cacheService.GetAsync<List<Goods>>(cacheKey);
        if (cachedGoods != null)
        {
            _logger.LogDebug("从缓存获取商品列表: {CacheKey}", cacheKey);
            return cachedGoods;
        }

        var goods = await _goodsService.GetGoodsAsync(page, pageSize, type);
        await _cacheService.SetAsync(cacheKey, goods, TimeSpan.FromMinutes(10));
        
        _logger.LogDebug("商品列表已缓存: {CacheKey}", cacheKey);
        return goods;
    }

    public async Task<Goods?> GetGoodsByIdAsync(int id)
    {
        var cacheKey = $"goods:detail:{id}";
        
        var cachedGoods = await _cacheService.GetAsync<Goods>(cacheKey);
        if (cachedGoods != null)
        {
            return cachedGoods;
        }

        var goods = await _goodsService.GetGoodsByIdAsync(id);
        if (goods != null)
        {
            await _cacheService.SetAsync(cacheKey, goods, TimeSpan.FromMinutes(30));
        }

        return goods;
    }
}

验收标准

功能测试验收

  • 用户系统测试通过(登录、注册、信息更新)
  • 商品系统测试通过(列表、详情、筛选)
  • 抽奖系统测试通过(抽奖、概率、库存)
  • 订单系统测试通过(创建、支付、查询)
  • 财务系统测试通过(余额、积分、交易记录)

性能测试验收

  • 用户查询响应时间 < 100ms
  • 商品查询响应时间 < 200ms
  • 订单查询响应时间 < 300ms
  • 并发登录处理能力 > 100用户/秒
  • 抽奖并发处理能力 > 50次/秒

性能优化验收

  • 关键查询索引优化完成
  • 查询执行计划分析完成
  • 内存优化表配置完成
  • 缓存策略实施完成
  • 数据库参数调优完成

风险点和注意事项

测试风险

  1. 测试数据污染: 测试数据可能影响生产环境
  2. 并发测试: 可能导致数据不一致
  3. 性能测试: 可能影响其他系统运行
  4. 缓存一致性: 缓存更新可能不及时

性能风险

  1. 过度优化: 可能导致维护复杂度增加
  2. 索引过多: 可能影响写入性能
  3. 内存消耗: 内存优化表消耗更多内存
  4. 缓存雪崩: 缓存同时失效导致数据库压力

解决方案

  1. 隔离测试环境: 使用独立的测试数据库
  2. 渐进式优化: 逐步实施优化措施
  3. 监控指标: 持续监控性能指标
  4. 回滚准备: 准备优化措施的回滚方案

下一阶段准备

为阶段6准备的内容

  • 生产环境部署脚本
  • 监控和告警配置
  • 备份和恢复策略
  • 运维文档

交接文档

  • 功能测试报告
  • 性能测试报告
  • 优化措施文档
  • 缓存策略说明

阶段5完成标志: 所有功能测试通过,性能指标达到预期,优化措施实施完成,系统准备好进入生产环境。