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

如何在Angular+ASP.Net游戏应用中处理离线运行任务?

针对游戏延迟任务的最优解决方案(Angular + ASP.NET)

核心思路

所有延迟操作的状态必须持久化到数据库,避免服务重启导致任务丢失;后台用可靠的任务调度系统触发完成逻辑,前端按需同步状态或接收实时通知。

一、后台实现(ASP.NET)

1. 数据库设计关键点

  • 给需要延迟处理的实体(比如Building、Crop)增加字段:
    • StartTime(DateTime):任务启动时间
    • ExpectedFinishTime(DateTime):预计完成时间
    • Status(枚举:Pending/InProgress/Completed/Failed):当前状态
  • 这样即使任务调度服务重启,也能通过扫描数据库重新恢复未完成的任务。

2. 任务调度选择

推荐方案:Hangfire

轻量、易集成,支持持久化任务,适合中小规模游戏:

  • 安装NuGet包:Install-Package Hangfire
  • 配置启动项(Program.cs):
    builder.Services.AddHangfire(config => 
        config.UseSqlServerStorage(builder.Configuration.GetConnectionString("DefaultConnection")));
    builder.Services.AddHangfireServer();
    
  • 提交延迟任务:
    // 启动建造时,计算完成时间,保存到数据库,同时提交延迟任务
    var finishTime = DateTime.UtcNow.AddHours(3);
    var building = new Building 
    { 
        StartTime = DateTime.UtcNow, 
        ExpectedFinishTime = finishTime, 
        Status = BuildingStatus.InProgress 
    };
    _dbContext.Buildings.Add(building);
    await _dbContext.SaveChangesAsync();
    
    // 提交3小时后执行的任务
    BackgroundJob.Schedule(() => _buildingService.CompleteBuilding(building.Id), finishTime);
    
  • 完成任务的逻辑:
    public async Task CompleteBuilding(int buildingId)
    {
        var building = await _dbContext.Buildings.FindAsync(buildingId);
        if (building == null || building.Status == BuildingStatus.Completed) return;
    
        building.Status = BuildingStatus.Completed;
        // 执行其他逻辑:增加玩家建筑数量、解锁功能等
        await _dbContext.SaveChangesAsync();
    
        // 如果需要通知前端,用SignalR推送
        await _hubContext.Clients.User(building.PlayerId.ToString()).SendAsync("BuildingCompleted", building);
    }
    

备选方案:Quartz.NET

更复杂但功能更强大,适合大规模、高并发场景:

  • 核心是创建Job和Trigger,将任务信息持久化到数据库,支持集群部署。

3. 容错机制

  • 定期扫描数据库:用Hangfire的RecurringJob或者Quartz的定时任务,每小时扫描所有InProgress状态的任务,检查ExpectedFinishTime是否已过,若已过则触发完成逻辑,避免调度任务丢失。
    RecurringJob.AddOrUpdate("recover-failed-tasks", () => _buildingService.RecoverFailedTasks(), Cron.Hourly());
    

二、前端实现(Angular)

1. 页面加载时同步状态

用户打开游戏页面时,调用API获取当前所有未完成的任务,计算剩余时间:

// building.service.ts
getUserBuildings(): Observable<Building[]> {
  return this.http.get<Building[]>('/api/buildings');
}

// building.component.ts
ngOnInit(): void {
  this.buildingService.getUserBuildings().subscribe(buildings => {
    this.buildings = buildings.map(b => {
      const remainingMs = new Date(b.expectedFinishTime).getTime() - Date.now();
      return { ...b, remainingTime: this.formatRemainingTime(remainingMs) };
    });
  });
}

2. 实时通知(可选)

用ASP.NET SignalR实现推送,任务完成时前端实时更新:

  • 安装SignalR客户端:npm install @microsoft/signalr
  • 连接Hub并监听事件:
// signalr.service.ts
private hubConnection: HubConnection;

connect(): void {
  this.hubConnection = new HubConnectionBuilder()
    .withUrl('/buildingHub')
    .build();

  this.hubConnection.start().catch(err => console.error(err));

  this.hubConnection.on('BuildingCompleted', (building: Building) => {
    // 更新页面上的建筑状态
    const index = this.buildings.findIndex(b => b.id === building.id);
    if (index !== -1) {
      this.buildings[index] = { ...building, remainingTime: '已完成' };
    }
  });
}

3. 避免不必要的轮询

不要用定时器频繁调用API,而是:

  • 页面加载时获取一次状态
  • 用SignalR接收实时更新
  • 用户切换回页面时(通过window:focus事件)重新获取状态

三、不推荐的方案

  • 前端定时器:完全不可靠,用户关闭页面或刷新后任务就会丢失,而且无法保证时间准确性。
  • ASP.NET内置的Task.Delay:服务重启后任务会丢失,不适合持久化的延迟任务。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 10:12:02