using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using MiGu.Server.Fleet; using MiGu.Server.Persistence; namespace MiGu.Server.Controllers; /// 车队运维健康探针(延迟走 WatchDog TCP)+ CDM 任务平台侧快照读取。 [ApiController] [Authorize] [Route("api/fleet")] public sealed class FleetController : ControllerBase { private readonly FleetHealthService _health; private readonly CdmTaskSyncer _cdmSyncer; private readonly AlarmCollector _alarmCollector; private readonly MiGuDbContext _db; public FleetController(FleetHealthService health, CdmTaskSyncer cdmSyncer, AlarmCollector alarmCollector, MiGuDbContext db) { _health = health; _cdmSyncer = cdmSyncer; _alarmCollector = alarmCollector; _db = db; } [HttpGet("health")] public async Task>> Health(CancellationToken ct) => Ok(await _health.GetAsync(ct)); /// /// CDM 搬运任务列表(读平台快照库 cdm_tasks)。在线时先即时同步一次拿最新, /// SimpleLite 关闭时回退最近快照,并通过 online/lastSyncAt 告知前端数据是否滞后。 /// [HttpGet("tasks")] public async Task> Tasks([FromQuery] int limit = 1000, CancellationToken ct = default) { await _cdmSyncer.SyncOnceAsync(ct); var take = Math.Clamp(limit, 1, 5000); var tasks = await _db.CdmTasks.AsNoTracking() .OrderByDescending(t => t.CreateTime) .Take(take) .Select(t => new { id = t.Id, taskId = t.TaskId, missionId = t.MissionId, missionName = t.MissionName, missionTypeName = t.MissionTypeName, srcSiteId = t.SrcSiteId, srcLabel = t.SrcLabel, dstSiteId = t.DstSiteId, dstLabel = t.DstLabel, status = t.Status, statusCode = t.StatusCode, carId = t.CarId, carName = t.CarName, priority = t.Priority, createTime = t.CreateTime, startTime = t.StartTime, finishTime = t.FinishTime, stuckReason = t.StuckReason, overdue = t.Overdue }) .ToListAsync(ct); return Ok(new { online = _cdmSyncer.Online, lastSyncAt = _cdmSyncer.LastSyncAt, count = tasks.Count, tasks }); } /// /// 车辆报警列表(读平台记录 vehicle_alarms)。含活跃 + 历史;SimpleLite 离线时回退最近记录, /// 通过 online/lastSyncAt 告知数据是否滞后。activeOnly=true 仅返回未恢复的报警。 /// [HttpGet("alarms")] public async Task> Alarms( [FromQuery] int limit = 2000, [FromQuery] bool activeOnly = false, CancellationToken ct = default) { // 在线时先即时对帐一次,保证打开页面能拿到最新活跃报警。 await _alarmCollector.SyncOnceAsync(ct); var take = Math.Clamp(limit, 1, 10000); var query = _db.VehicleAlarms.AsNoTracking().AsQueryable(); if (activeOnly) query = query.Where(a => a.Status == "active"); var alarms = await query .OrderByDescending(a => a.Status == "active") .ThenByDescending(a => a.LastAt) .Take(take) .Select(a => new { id = a.Id, carId = a.CarId, carName = a.CarName, info = a.Info, level = a.Level, status = a.Status, firstAt = a.FirstAt, lastAt = a.LastAt, resolvedAt = a.ResolvedAt, durationSecs = a.DurationSecs, acknowledged = a.Acknowledged }) .ToListAsync(ct); return Ok(new { online = _alarmCollector.Online, lastSyncAt = _alarmCollector.LastSyncAt, count = alarms.Count, alarms }); } }