Phase 2: 想定管理 — 8个Repository + ScenarioService(CRUD/5步保存/搜索分页),47个测试全部通过

This commit is contained in:
tian 2026-06-11 14:13:35 +08:00
parent b23291c5bf
commit b33a6a87e1
11 changed files with 892 additions and 0 deletions

View File

@ -0,0 +1,10 @@
using CounterDrone.Core.Models;
using SQLite;
namespace CounterDrone.Core.Repository
{
public class CloudDispersalRepository : BaseRepository<CloudDispersal>
{
public CloudDispersalRepository(SQLiteConnection db) : base(db) { }
}
}

View File

@ -0,0 +1,10 @@
using CounterDrone.Core.Models;
using SQLite;
namespace CounterDrone.Core.Repository
{
public class CombatSceneRepository : BaseRepository<CombatScene>
{
public CombatSceneRepository(SQLiteConnection db) : base(db) { }
}
}

View File

@ -0,0 +1,27 @@
using System.Collections.Generic;
using System.Linq;
using CounterDrone.Core.Models;
using SQLite;
namespace CounterDrone.Core.Repository
{
public class ControlZoneRepository : BaseRepository<ControlZone>
{
public ControlZoneRepository(SQLiteConnection db) : base(db) { }
public List<ControlZone> GetByTaskId(string taskId)
{
return Db.Table<ControlZone>()
.Where(z => z.TaskId == taskId)
.OrderBy(z => z.OrderIndex)
.ToList();
}
public void DeleteByTaskId(string taskId)
{
var zones = GetByTaskId(taskId);
foreach (var z in zones)
Db.Delete(z);
}
}
}

View File

@ -0,0 +1,24 @@
using System.Collections.Generic;
using System.Linq;
using CounterDrone.Core.Models;
using SQLite;
namespace CounterDrone.Core.Repository
{
public class EquipmentDeploymentRepository : BaseRepository<EquipmentDeployment>
{
public EquipmentDeploymentRepository(SQLiteConnection db) : base(db) { }
public List<EquipmentDeployment> GetByTaskId(string taskId)
{
return Db.Table<EquipmentDeployment>().Where(e => e.TaskId == taskId).ToList();
}
public void DeleteByTaskId(string taskId)
{
var equips = GetByTaskId(taskId);
foreach (var e in equips)
Db.Delete(e);
}
}
}

View File

@ -0,0 +1,10 @@
using CounterDrone.Core.Models;
using SQLite;
namespace CounterDrone.Core.Repository
{
public class RoutePlanRepository : BaseRepository<RoutePlan>
{
public RoutePlanRepository(SQLiteConnection db) : base(db) { }
}
}

View File

@ -0,0 +1,34 @@
using System.Collections.Generic;
using System.Linq;
using CounterDrone.Core.Models;
using SQLite;
namespace CounterDrone.Core.Repository
{
public class SimTaskRepository : BaseRepository<SimTask>
{
public SimTaskRepository(SQLiteConnection db) : base(db) { }
public SimTask GetByTaskNumber(string taskNumber)
{
return Db.Table<SimTask>().FirstOrDefault(t => t.TaskNumber == taskNumber);
}
public List<SimTask> Search(string keyword, string? dateFrom, string? dateTo, int offset, int limit, out int totalCount)
{
var query = Db.Table<SimTask>().AsQueryable();
if (!string.IsNullOrWhiteSpace(keyword))
query = query.Where(t => t.Name.Contains(keyword) || t.TaskNumber.Contains(keyword));
if (!string.IsNullOrWhiteSpace(dateFrom))
query = query.Where(t => t.CreatedAt.CompareTo(dateFrom) >= 0);
if (!string.IsNullOrWhiteSpace(dateTo))
query = query.Where(t => t.CreatedAt.CompareTo(dateTo) <= 0);
totalCount = query.Count();
return query.OrderByDescending(t => t.CreatedAt).Skip(offset).Take(limit).ToList();
}
}
}

View File

@ -0,0 +1,24 @@
using System.Collections.Generic;
using System.Linq;
using CounterDrone.Core.Models;
using SQLite;
namespace CounterDrone.Core.Repository
{
public class TargetConfigRepository : BaseRepository<TargetConfig>
{
public TargetConfigRepository(SQLiteConnection db) : base(db) { }
public List<TargetConfig> GetByTaskId(string taskId)
{
return Db.Table<TargetConfig>().Where(t => t.TaskId == taskId).ToList();
}
public void DeleteByTaskId(string taskId)
{
var targets = GetByTaskId(taskId);
foreach (var t in targets)
Db.Delete(t);
}
}
}

View File

@ -0,0 +1,27 @@
using System.Collections.Generic;
using System.Linq;
using CounterDrone.Core.Models;
using SQLite;
namespace CounterDrone.Core.Repository
{
public class WaypointRepository : BaseRepository<Waypoint>
{
public WaypointRepository(SQLiteConnection db) : base(db) { }
public List<Waypoint> GetByTaskId(string taskId)
{
return Db.Table<Waypoint>()
.Where(w => w.TaskId == taskId)
.OrderBy(w => w.OrderIndex)
.ToList();
}
public void DeleteByTaskId(string taskId)
{
var wps = GetByTaskId(taskId);
foreach (var w in wps)
Db.Delete(w);
}
}
}

View File

@ -0,0 +1,21 @@
using System.Collections.Generic;
using CounterDrone.Core.Models;
namespace CounterDrone.Core.Services
{
public interface IScenarioService
{
SimTask CreateTask(string name, string taskNumber);
void DeleteTask(string id);
PagedResult<SimTask> SearchTasks(string keyword, string? dateFrom, string? dateTo, int page, int pageSize);
TaskFullConfig? GetTaskDetail(string id);
void SaveScene(string taskId, CombatScene scene);
void SaveControlZones(string taskId, List<ControlZone> zones);
void SaveTarget(string taskId, TargetConfig target);
void SaveDeployment(string taskId, List<EquipmentDeployment> equips);
void SaveCloudDispersal(string taskId, CloudDispersal cloud);
void SaveRoute(string taskId, RoutePlan route, List<Waypoint> waypoints);
void UpdateStep(string taskId, int step);
}
}

View File

@ -0,0 +1,222 @@
using System;
using System.Collections.Generic;
using CounterDrone.Core.Models;
using CounterDrone.Core.Repository;
namespace CounterDrone.Core.Services
{
public class ScenarioService : IScenarioService
{
private readonly SimTaskRepository _taskRepo;
private readonly CombatSceneRepository _sceneRepo;
private readonly ControlZoneRepository _zoneRepo;
private readonly TargetConfigRepository _targetRepo;
private readonly EquipmentDeploymentRepository _equipRepo;
private readonly CloudDispersalRepository _cloudRepo;
private readonly RoutePlanRepository _routeRepo;
private readonly WaypointRepository _waypointRepo;
public ScenarioService(
SimTaskRepository taskRepo,
CombatSceneRepository sceneRepo,
ControlZoneRepository zoneRepo,
TargetConfigRepository targetRepo,
EquipmentDeploymentRepository equipRepo,
CloudDispersalRepository cloudRepo,
RoutePlanRepository routeRepo,
WaypointRepository waypointRepo)
{
_taskRepo = taskRepo;
_sceneRepo = sceneRepo;
_zoneRepo = zoneRepo;
_targetRepo = targetRepo;
_equipRepo = equipRepo;
_cloudRepo = cloudRepo;
_routeRepo = routeRepo;
_waypointRepo = waypointRepo;
}
// ========== Task CRUD ==========
public SimTask CreateTask(string name, string taskNumber)
{
if (string.IsNullOrWhiteSpace(name))
throw new ArgumentException("任务名称不能为空");
if (string.IsNullOrWhiteSpace(taskNumber))
taskNumber = GenerateTaskNumber();
var existing = _taskRepo.GetByTaskNumber(taskNumber);
if (existing != null)
throw new ArgumentException($"任务编号 {taskNumber} 已存在");
var task = new SimTask
{
Name = name,
TaskNumber = taskNumber,
Status = (int)TaskStatus.Configuring,
CurrentStep = 1,
};
_taskRepo.Insert(task);
return task;
}
public void DeleteTask(string id)
{
_taskRepo.Delete(id);
}
public PagedResult<SimTask> SearchTasks(string keyword, string? dateFrom, string? dateTo, int page, int pageSize)
{
if (page < 1) page = 1;
if (pageSize < 1) pageSize = 10;
var offset = (page - 1) * pageSize;
var items = _taskRepo.Search(keyword, dateFrom, dateTo, offset, pageSize, out var total);
return new PagedResult<SimTask>
{
Items = items,
TotalCount = total,
Page = page,
PageSize = pageSize,
};
}
public TaskFullConfig? GetTaskDetail(string id)
{
var task = _taskRepo.GetById(id);
if (task == null) return null;
return new TaskFullConfig
{
Task = task,
Scene = _sceneRepo.GetById(id) ?? new CombatScene { TaskId = id },
ControlZones = _zoneRepo.GetByTaskId(id),
Targets = _targetRepo.GetByTaskId(id),
Equipment = _equipRepo.GetByTaskId(id),
Cloud = _cloudRepo.GetById(id) ?? new CloudDispersal { TaskId = id },
Route = _routeRepo.GetById(id) ?? new RoutePlan { TaskId = id },
Waypoints = _waypointRepo.GetByTaskId(id),
};
}
// ========== Step Save ==========
public void SaveScene(string taskId, CombatScene scene)
{
scene.TaskId = taskId;
var existing = _sceneRepo.GetById(taskId);
if (existing != null)
_sceneRepo.Update(scene);
else
_sceneRepo.Insert(scene);
TouchTask(taskId);
}
public void SaveControlZones(string taskId, List<ControlZone> zones)
{
_zoneRepo.DeleteByTaskId(taskId);
for (int i = 0; i < zones.Count; i++)
{
zones[i].TaskId = taskId;
zones[i].OrderIndex = i;
}
_zoneRepo.InsertAll(zones);
TouchTask(taskId);
}
public void SaveTarget(string taskId, TargetConfig target)
{
target.TaskId = taskId;
if (string.IsNullOrEmpty(target.Id))
target.Id = Guid.NewGuid().ToString();
var existing = _targetRepo.GetById(target.Id);
if (existing != null)
_targetRepo.Update(target);
else
_targetRepo.Insert(target);
TouchTask(taskId);
}
public void SaveDeployment(string taskId, List<EquipmentDeployment> equips)
{
_equipRepo.DeleteByTaskId(taskId);
foreach (var e in equips)
{
e.TaskId = taskId;
if (string.IsNullOrEmpty(e.Id))
e.Id = Guid.NewGuid().ToString();
}
_equipRepo.InsertAll(equips);
TouchTask(taskId);
}
public void SaveCloudDispersal(string taskId, CloudDispersal cloud)
{
cloud.TaskId = taskId;
var existing = _cloudRepo.GetById(taskId);
if (existing != null)
_cloudRepo.Update(cloud);
else
_cloudRepo.Insert(cloud);
TouchTask(taskId);
}
public void SaveRoute(string taskId, RoutePlan route, List<Waypoint> waypoints)
{
route.TaskId = taskId;
var existingRoute = _routeRepo.GetById(taskId);
if (existingRoute != null)
_routeRepo.Update(route);
else
_routeRepo.Insert(route);
_waypointRepo.DeleteByTaskId(taskId);
for (int i = 0; i < waypoints.Count; i++)
{
waypoints[i].TaskId = taskId;
waypoints[i].OrderIndex = i;
if (string.IsNullOrEmpty(waypoints[i].Id))
waypoints[i].Id = Guid.NewGuid().ToString();
}
_waypointRepo.InsertAll(waypoints);
TouchTask(taskId);
}
public void UpdateStep(string taskId, int step)
{
var task = _taskRepo.GetById(taskId);
if (task == null) return;
if (step < 1) step = 1;
if (step > 5) step = 5;
task.CurrentStep = step;
_taskRepo.Update(task);
}
// ========== Helpers ==========
private void TouchTask(string taskId)
{
var task = _taskRepo.GetById(taskId);
if (task != null)
{
task.UpdatedAt = DateTime.UtcNow.ToString("o");
_taskRepo.Update(task);
}
}
public static string GenerateTaskNumber()
{
var now = DateTime.Now;
var datePart = now.ToString("yyyyMMdd");
var random = new Random();
var seq = random.Next(1, 1000);
return $"SIM-{datePart}-{seq:D3}";
}
}
}

View File

@ -0,0 +1,483 @@
using System;
using System.Collections.Generic;
using System.IO;
using CounterDrone.Core;
using CounterDrone.Core.Models;
using CounterDrone.Core.Repository;
using CounterDrone.Core.Services;
using SQLite;
using Xunit;
using TaskStatus = CounterDrone.Core.Models.TaskStatus;
namespace CounterDrone.Core.Tests
{
public class ScenarioServiceTests : IDisposable
{
private readonly string _testDir;
private readonly SQLiteConnection _db;
private readonly IScenarioService _service;
public ScenarioServiceTests()
{
_testDir = Path.Combine(Path.GetTempPath(), $"cd_test_{Guid.NewGuid():N}");
var paths = new TestPathProvider(_testDir);
var dbManager = new DatabaseManager(paths);
_db = dbManager.OpenMainDb();
_service = new ScenarioService(
new SimTaskRepository(_db),
new CombatSceneRepository(_db),
new ControlZoneRepository(_db),
new TargetConfigRepository(_db),
new EquipmentDeploymentRepository(_db),
new CloudDispersalRepository(_db),
new RoutePlanRepository(_db),
new WaypointRepository(_db));
}
public void Dispose()
{
_db?.Close();
if (Directory.Exists(_testDir))
Directory.Delete(_testDir, true);
}
// ========== Task CRUD ==========
[Fact]
public void CreateTask_Valid_ReturnsTask()
{
var task = _service.CreateTask("测试任务", "SIM-20260611-001");
Assert.NotNull(task);
Assert.Equal("测试任务", task.Name);
Assert.Equal("SIM-20260611-001", task.TaskNumber);
Assert.Equal((int)TaskStatus.Configuring, task.Status);
Assert.Equal(1, task.CurrentStep);
Assert.NotEmpty(task.Id);
}
[Fact]
public void CreateTask_AutoGeneratesTaskNumber()
{
var task = _service.CreateTask("自动编号", "");
Assert.StartsWith("SIM-", task.TaskNumber);
}
[Fact]
public void CreateTask_EmptyName_Throws()
{
Assert.Throws<ArgumentException>(() =>
_service.CreateTask("", "SIM-001"));
}
[Fact]
public void CreateTask_DuplicateNumber_Throws()
{
_service.CreateTask("Task A", "SIM-001");
Assert.Throws<ArgumentException>(() =>
_service.CreateTask("Task B", "SIM-001"));
}
[Fact]
public void DeleteTask_RemovesTask()
{
var task = _service.CreateTask("ToDelete", "");
_service.DeleteTask(task.Id);
var detail = _service.GetTaskDetail(task.Id);
Assert.Null(detail);
}
[Fact]
public void GetTaskDetail_ReturnsFullConfig()
{
var task = _service.CreateTask("Full Config", "");
// 保存各步骤配置
_service.SaveScene(task.Id, new CombatScene
{
SceneType = (int)SceneType.Mountain,
WindSpeed = 10.0,
Temperature = 25.0,
});
_service.SaveTarget(task.Id, new TargetConfig
{
TargetType = (int)TargetType.FixedWing,
Quantity = 3,
});
_service.SaveCloudDispersal(task.Id, new CloudDispersal
{
AerosolType = (int)AerosolType.ActiveMaterial,
Duration = 45.0,
});
_service.UpdateStep(task.Id, 4);
// 读取完整配置
var detail = _service.GetTaskDetail(task.Id);
Assert.Equal("Full Config", detail.Task.Name);
Assert.Equal(4, detail.Task.CurrentStep);
Assert.Equal((int)SceneType.Mountain, detail.Scene.SceneType);
Assert.Equal(10.0, detail.Scene.WindSpeed);
Assert.Equal(3, detail.Targets[0].Quantity);
Assert.Equal((int)AerosolType.ActiveMaterial, detail.Cloud.AerosolType);
}
// ========== Step Save ==========
[Fact]
public void SaveScene_FirstTime_Inserts()
{
var task = _service.CreateTask("Scene Test", "");
_service.SaveScene(task.Id, new CombatScene
{
WeatherType = (int)WeatherType.Fog,
Visibility = 2000.0,
TimeOfDay = "03:00",
});
var detail = _service.GetTaskDetail(task.Id);
Assert.Equal((int)WeatherType.Fog, detail.Scene.WeatherType);
Assert.Equal(2000.0, detail.Scene.Visibility);
Assert.Equal("03:00", detail.Scene.TimeOfDay);
}
[Fact]
public void SaveScene_SecondTime_Updates()
{
var task = _service.CreateTask("Update Scene", "");
_service.SaveScene(task.Id, new CombatScene { WindSpeed = 5.0 });
_service.SaveScene(task.Id, new CombatScene { WindSpeed = 15.0 });
var detail = _service.GetTaskDetail(task.Id);
Assert.Equal(15.0, detail.Scene.WindSpeed);
}
[Fact]
public void SaveControlZones_SavesAndRetrieves()
{
var task = _service.CreateTask("Zone Test", "");
var zones = new List<ControlZone>
{
new ControlZone { Name = "禁区A", VerticesJson = "[{\"x\":0,\"y\":0,\"z\":0},{\"x\":100,\"y\":0,\"z\":0},{\"x\":100,\"y\":100,\"z\":0}]", MinAltitude = 0, MaxAltitude = 500 },
new ControlZone { Name = "禁区B", VerticesJson = "[{\"x\":200,\"y\":200,\"z\":0}]", MinAltitude = 100, MaxAltitude = 300 },
};
_service.SaveControlZones(task.Id, zones);
var detail = _service.GetTaskDetail(task.Id);
Assert.Equal(2, detail.ControlZones.Count);
Assert.Equal("禁区A", detail.ControlZones[0].Name);
Assert.Equal(0, detail.ControlZones[0].OrderIndex);
Assert.Equal("禁区B", detail.ControlZones[1].Name);
Assert.Equal(1, detail.ControlZones[1].OrderIndex);
}
[Fact]
public void SaveControlZones_ReplaceExisting()
{
var task = _service.CreateTask("Zone Replace", "");
_service.SaveControlZones(task.Id, new List<ControlZone>
{
new ControlZone { Name = "Old" }
});
_service.SaveControlZones(task.Id, new List<ControlZone>
{
new ControlZone { Name = "New1" },
new ControlZone { Name = "New2" },
});
var detail = _service.GetTaskDetail(task.Id);
Assert.Equal(2, detail.ControlZones.Count);
}
[Fact]
public void SaveTarget_SavesCorrectly()
{
var task = _service.CreateTask("Target Test", "");
_service.SaveTarget(task.Id, new TargetConfig
{
TargetType = (int)TargetType.Piston,
Quantity = 5,
PowerType = (int)PowerType.Piston,
Wingspan = 2.5,
TypicalSpeed = 120.0,
TypicalAltitude = 800.0,
});
var detail = _service.GetTaskDetail(task.Id);
var target = detail.Targets[0];
Assert.Equal((int)TargetType.Piston, target.TargetType);
Assert.Equal(5, target.Quantity);
Assert.Equal(120.0, target.TypicalSpeed);
}
[Fact]
public void SaveDeployment_SavesMultiple()
{
var task = _service.CreateTask("Equip Test", "");
var equips = new List<EquipmentDeployment>
{
new EquipmentDeployment
{
EquipmentRole = (int)EquipmentRole.LaunchPlatform,
PlatformType = (int)PlatformType.GroundBased,
Quantity = 2,
PositionX = 1000, PositionY = 0, PositionZ = 50,
MuzzleVelocity = 800.0,
MunitionCount = 3,
},
new EquipmentDeployment
{
EquipmentRole = (int)EquipmentRole.Detection,
Quantity = 1,
DetectionRadius = 5000.0,
},
};
_service.SaveDeployment(task.Id, equips);
var detail = _service.GetTaskDetail(task.Id);
Assert.Equal(2, detail.Equipment.Count);
var platform = detail.Equipment.Find(e => e.EquipmentRole == (int)EquipmentRole.LaunchPlatform);
Assert.NotNull(platform);
Assert.Equal(2, platform.Quantity);
Assert.Equal(800.0, platform.MuzzleVelocity);
}
[Fact]
public void SaveRoute_SavesWaypoints()
{
var task = _service.CreateTask("Route Test", "");
var route = new RoutePlan
{
FormationMode = (int)FormationMode.Formation,
FormationSpacing = 60.0,
};
var waypoints = new List<Waypoint>
{
new Waypoint { PosX = 0, PosY = 0, PosZ = 100, Altitude = 500, Speed = 100 },
new Waypoint { PosX = 5000, PosY = 0, PosZ = 100, Altitude = 500, Speed = 100 },
new Waypoint { PosX = 10000, PosY = 0, PosZ = 100, Altitude = 300, Speed = 80 },
};
_service.SaveRoute(task.Id, route, waypoints);
var detail = _service.GetTaskDetail(task.Id);
Assert.Equal((int)FormationMode.Formation, detail.Route.FormationMode);
Assert.Equal(3, detail.Waypoints.Count);
Assert.Equal(0, detail.Waypoints[0].OrderIndex);
Assert.Equal(2, detail.Waypoints[2].OrderIndex);
Assert.Equal(5000.0, detail.Waypoints[1].PosX);
}
// ========== Search & Pagination ==========
[Fact]
public void SearchTasks_KeywordFilter()
{
_service.CreateTask("城市防御演习", "");
_service.CreateTask("平原拦截测试", "");
_service.CreateTask("海岸边防演练", "");
var result = _service.SearchTasks("城市", null, null, 1, 10);
Assert.Equal(1, result.TotalCount);
Assert.Equal("城市防御演习", result.Items[0].Name);
}
[Fact]
public void SearchTasks_NoResults()
{
_service.CreateTask("Task A", "");
var result = _service.SearchTasks("不存在的任务", null, null, 1, 10);
Assert.Equal(0, result.TotalCount);
}
[Fact]
public void SearchTasks_Pagination()
{
for (int i = 0; i < 15; i++)
_service.CreateTask($"Task {i:D2}", "");
var page1 = _service.SearchTasks(null, null, null, 1, 5);
Assert.Equal(15, page1.TotalCount);
Assert.Equal(5, page1.Items.Count);
Assert.Equal(3, page1.TotalPages);
var page3 = _service.SearchTasks(null, null, null, 3, 5);
Assert.Equal(5, page3.Items.Count);
}
[Fact]
public void SearchTasks_DateRangeFilter()
{
// All tasks created today, so date filter should include them
_service.CreateTask("Recent Task", "");
var yesterday = DateTime.UtcNow.AddDays(-1).ToString("yyyy-MM-dd");
var tomorrow = DateTime.UtcNow.AddDays(1).ToString("yyyy-MM-dd");
var result = _service.SearchTasks(null, yesterday, tomorrow, 1, 10);
Assert.Equal(1, result.TotalCount);
var oldResult = _service.SearchTasks(null, "2020-01-01", "2020-12-31", 1, 10);
Assert.Equal(0, oldResult.TotalCount);
}
// ========== UpdateStep ==========
[Fact]
public void UpdateStep_ValidSteps()
{
var task = _service.CreateTask("Step Test", "");
Assert.Equal(1, task.CurrentStep);
_service.UpdateStep(task.Id, 3);
var detail = _service.GetTaskDetail(task.Id);
Assert.Equal(3, detail.Task.CurrentStep);
_service.UpdateStep(task.Id, 5);
detail = _service.GetTaskDetail(task.Id);
Assert.Equal(5, detail.Task.CurrentStep);
}
[Fact]
public void UpdateStep_ClampedToRange()
{
var task = _service.CreateTask("Clamp Test", "");
_service.UpdateStep(task.Id, 0);
Assert.Equal(1, _service.GetTaskDetail(task.Id).Task.CurrentStep);
_service.UpdateStep(task.Id, 10);
Assert.Equal(5, _service.GetTaskDetail(task.Id).Task.CurrentStep);
}
// ========== Full Workflow ==========
[Fact]
public void FullWorkflow_CreateConfigureSearch()
{
// 1. 创建任务
var task = _service.CreateTask("完整流程测试", "");
Assert.NotNull(task);
// 2. 步骤1作战场景
_service.SaveScene(task.Id, new CombatScene
{
SceneType = (int)SceneType.Urban,
WeatherType = (int)WeatherType.Rain,
WindSpeed = 8.0,
WindDirection = (int)WindDirection.SE,
Temperature = 18.0,
TimeOfDay = "14:00",
});
_service.UpdateStep(task.Id, 1);
// 3. 步骤1扩展管控区域
_service.SaveControlZones(task.Id, new List<ControlZone>
{
new ControlZone
{
Name = "核心区",
VerticesJson = "[{\"x\":0,\"y\":0,\"z\":0},{\"x\":500,\"y\":0,\"z\":0},{\"x\":500,\"y\":500,\"z\":0},{\"x\":0,\"y\":500,\"z\":0}]",
MinAltitude = 0, MaxAltitude = 1000,
}
});
// 4. 步骤2目标配置
_service.SaveTarget(task.Id, new TargetConfig
{
TargetType = (int)TargetType.HighSpeed,
Quantity = 2,
PowerType = (int)PowerType.Jet,
TypicalSpeed = 300.0,
});
_service.UpdateStep(task.Id, 2);
// 5. 步骤3装备部署
_service.SaveDeployment(task.Id, new List<EquipmentDeployment>
{
new EquipmentDeployment
{
EquipmentRole = (int)EquipmentRole.Detection,
Quantity = 1,
DetectionRadius = 6000.0,
},
new EquipmentDeployment
{
EquipmentRole = (int)EquipmentRole.LaunchPlatform,
PlatformType = (int)PlatformType.GroundBased,
Quantity = 3,
PositionX = 800, PositionY = 0, PositionZ = 50,
MuzzleVelocity = 850.0,
MunitionCount = 2,
},
});
_service.UpdateStep(task.Id, 3);
// 6. 步骤4云团抛撒
_service.SaveCloudDispersal(task.Id, new CloudDispersal
{
AerosolType = (int)AerosolType.ActiveMaterial,
TriggerMode = (int)TriggerMode.Area,
Duration = 50.0,
PositionX = 3000, PositionY = 0, PositionZ = 300,
});
_service.UpdateStep(task.Id, 4);
// 7. 步骤5航路规划
_service.SaveRoute(task.Id, new RoutePlan
{
FormationMode = (int)FormationMode.Swarm,
FormationSpacing = 30.0,
}, new List<Waypoint>
{
new Waypoint { PosX = 0, PosY = 100, PosZ = 200, Altitude = 600, Speed = 250 },
new Waypoint { PosX = 10000, PosY = 100, PosZ = 200, Altitude = 600, Speed = 250 },
});
_service.UpdateStep(task.Id, 5);
// 8. 验证完整配置
var detail = _service.GetTaskDetail(task.Id);
Assert.Equal("完整流程测试", detail.Task.Name);
Assert.Equal(5, detail.Task.CurrentStep);
Assert.Equal((int)TaskStatus.Configuring, detail.Task.Status);
// 场景
Assert.Equal((int)SceneType.Urban, detail.Scene.SceneType);
Assert.Equal((int)WeatherType.Rain, detail.Scene.WeatherType);
Assert.Equal("14:00", detail.Scene.TimeOfDay);
// 管控区域
Assert.Single(detail.ControlZones);
Assert.Equal("核心区", detail.ControlZones[0].Name);
// 目标
Assert.Single(detail.Targets);
Assert.Equal(2, detail.Targets[0].Quantity);
Assert.Equal(300.0, detail.Targets[0].TypicalSpeed);
// 装备
Assert.Equal(2, detail.Equipment.Count);
// 云团
Assert.Equal((int)AerosolType.ActiveMaterial, detail.Cloud.AerosolType);
Assert.Equal(50.0, detail.Cloud.Duration);
// 航路
Assert.Equal((int)FormationMode.Swarm, detail.Route.FormationMode);
Assert.Equal(2, detail.Waypoints.Count);
Assert.Equal(250.0, detail.Waypoints[0].Speed);
// 9. 搜索验证
var searchResult = _service.SearchTasks("完整流程", null, null, 1, 10);
Assert.Equal(1, searchResult.TotalCount);
}
}
}