You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET新手开发:如何实现每日统一的未完成订单统计变量初始化

解决.NET网站当日统一初始统计基准值的方案

Hey there! 作为.NET开发新手遇到这个需求太正常了——要让所有用户当天看到完全一致的初始未完成订单数基准,核心就是把这个值「固定在当天」,不管用户什么时候访问、服务器有没有重启,都能拿到同一个数。下面给你几个落地性强的方案,适配不同场景:

方案1:内存缓存+每日定时任务(单实例部署首选)

如果你的网站是单服务器部署,用ASP.NET Core的IHostedService做每日定时初始化,把基准值存到内存缓存里,过期时间设为当天结束。这样所有用户访问时直接取缓存,性能拉满还不会出错。

步骤:

  1. 写一个定时任务服务,每天凌晨0点拉取初始值:
public class DailyOrderBaseJob : IHostedService, IDisposable
{
    private readonly IServiceScopeFactory _scopeFactory;
    private readonly IMemoryCache _cache;
    private Timer _timer;

    public DailyOrderBaseJob(IServiceScopeFactory scopeFactory, IMemoryCache cache)
    {
        _scopeFactory = scopeFactory;
        _cache = cache;
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        // 首次启动立即执行一次,之后每天0点执行
        SetDailyBaseValue();
        _timer = new Timer(DoWork, null, TimeSpan.Zero, TimeSpan.FromHours(24));
        return Task.CompletedTask;
    }

    private void DoWork(object state)
    {
        SetDailyBaseValue();
    }

    private void SetDailyBaseValue()
    {
        using var scope = _scopeFactory.CreateScope();
        var dbContext = scope.ServiceProvider.GetRequiredService<YourDbContext>();
        
        // 计算当日初始未完成订单数(这里根据你的业务逻辑调整)
        var initialUnfinishedCount = dbContext.Orders
            .Where(o => !o.IsCompleted && o.CreatedDate.Date <= DateTime.Today)
            .Count();
        
        // 缓存到当天23:59:59过期
        var expireTime = new DateTime(DateTime.Today.Year, DateTime.Today.Month, DateTime.Today.Day, 23, 59, 59);
        _cache.Set("DailyInitialOrderCount", initialUnfinishedCount, expireTime);
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        _timer?.Change(Timeout.Infinite, 0);
        return Task.CompletedTask;
    }

    public void Dispose()
    {
        _timer?.Dispose();
    }
}
  1. 在Program.cs里注册这个服务:
builder.Services.AddHostedService<DailyOrderBaseJob>();
builder.Services.AddMemoryCache();
  1. 业务代码里直接取缓存:
public async Task<IActionResult> GetStatistics()
{
    // 从缓存拿当日基准值
    if (!_cache.TryGetValue("DailyInitialOrderCount", out int initialCount))
    {
        // 兜底:如果缓存没加载(比如刚重启),临时计算一次并缓存
        using var scope = _scopeFactory.CreateScope();
        var dbContext = scope.ServiceProvider.GetRequiredService<YourDbContext>();
        initialCount = dbContext.Orders.Where(o => !o.IsCompleted && o.CreatedDate.Date <= DateTime.Today).Count();
        var expireTime = new DateTime(DateTime.Today.Year, DateTime.Today.Month, DateTime.Today.Day, 23, 59, 59);
        _cache.Set("DailyInitialOrderCount", initialCount, expireTime);
    }

    // 获取实时已完成数
    var completedCount = await _dbContext.Orders.CountAsync(o => o.IsCompleted && o.CompletedDate.Date == DateTime.Today);
    
    // 计算统计结果
    var rate = initialCount == 0 ? 0 : (double)completedCount / initialCount;
    return Ok(new { InitialCount = initialCount, CompletedCount = completedCount, Rate = rate });
}

方案2:数据库存储当日基准值(多实例/需持久化场景)

如果你的网站是多服务器部署,或者担心服务器重启导致缓存丢失,直接把当日基准值存在数据库里更靠谱。逻辑是:当天第一个用户访问时,自动计算并插入基准值,之后所有用户都读这个记录。

步骤:

  1. 先建一个表(比如DailyOrderBase),字段可以是:

    • Id(主键)
    • Date(日期,唯一键)
    • InitialUnfinishedCount(初始未完成数)
    • CreatedTime(创建时间)
  2. 业务代码里的获取逻辑:

public async Task<int> GetDailyInitialOrderCount()
{
    var today = DateTime.Today;
    var dailyBase = await _dbContext.DailyOrderBase
        .FirstOrDefaultAsync(o => o.Date == today);

    if (dailyBase == null)
    {
        // 当天还没初始化,计算并插入
        var initialCount = await _dbContext.Orders
            .CountAsync(o => !o.IsCompleted && o.CreatedDate.Date <= today);
        
        dailyBase = new DailyOrderBase
        {
            Date = today,
            InitialUnfinishedCount = initialCount,
            CreatedTime = DateTime.Now
        };
        
        _dbContext.DailyOrderBase.Add(dailyBase);
        await _dbContext.SaveChangesAsync();
    }

    return dailyBase.InitialUnfinishedCount;
}

这个方案的好处是,不管多少服务器实例,都读同一个数据库记录,绝对统一;而且即使应用重启,数据也不会丢。

方案3:分布式缓存(多实例高性能场景)

如果是多实例部署又想兼顾性能,可以用Redis这类分布式缓存代替内存缓存。逻辑和方案1类似,只是把IMemoryCache换成IDistributedCache,缓存的key带上日期(比如DailyInitialOrderCount_20240520),过期时间设为当天结束。

示例代码(简化版):

public async Task<int> GetDailyInitialOrderCount(IDistributedCache cache, YourDbContext dbContext)
{
    var todayStr = DateTime.Today.ToString("yyyyMMdd");
    var cacheKey = $"DailyInitialOrderCount_{todayStr}";
    
    var cachedValue = await cache.GetStringAsync(cacheKey);
    if (!string.IsNullOrEmpty(cachedValue) && int.TryParse(cachedValue, out int initialCount))
    {
        return initialCount;
    }

    // 缓存不存在,计算并存入分布式缓存
    initialCount = await dbContext.Orders.CountAsync(o => !o.IsCompleted && o.CreatedDate.Date <= DateTime.Today);
    await cache.SetStringAsync(cacheKey, initialCount.ToString(), new DistributedCacheEntryOptions
    {
        AbsoluteExpiration = new DateTimeOffset(DateTime.Today.AddDays(1).AddSeconds(-1))
    });

    return initialCount;
}

关键注意点

  • 一定要用DateTime.Today来判断日期,避免时区问题(如果是多时区网站,要统一用UTC时间)
  • 兜底逻辑不能少:比如缓存没加载、数据库记录没创建时,要能临时计算并补全,避免用户看到错误
  • 如果是多实例,方案2和3更稳妥,方案1的内存缓存是单实例隔离的,不同服务器会有不同值

内容的提问来源于stack exchange,提问作者heyjaynell

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 08:11:27