From 4fa19e386c0ce7085584468b9ec52d55d6982c58 Mon Sep 17 00:00:00 2001 From: tian <11429339@qq.com> Date: Mon, 15 Jun 2026 16:06:56 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=A2=84=E8=AE=BE=E6=83=B3=E5=AE=9A?= =?UTF-8?q?=E7=A7=8D=E5=AD=90=20(DefaultScenarios)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 6 个预设想定首次启动自动入库,幂等跳过:无防御/管控侵入/活塞西风/喷气活性/空基东风/3架空基编队 通过 DatabaseManager.OpenMainDb() 自动种子,版本追踪 ScenarioServiceTests 期望值适配(+6 个种子想定) --- src/CounterDrone.Core/DatabaseManager.cs | 23 +++ src/CounterDrone.Core/DefaultScenarios.cs | 146 ++++++++++++++++++ .../ScenarioServiceTests.cs | 6 +- 3 files changed, 172 insertions(+), 3 deletions(-) create mode 100644 src/CounterDrone.Core/DefaultScenarios.cs diff --git a/src/CounterDrone.Core/DatabaseManager.cs b/src/CounterDrone.Core/DatabaseManager.cs index f408f2f..0606e8e 100644 --- a/src/CounterDrone.Core/DatabaseManager.cs +++ b/src/CounterDrone.Core/DatabaseManager.cs @@ -3,6 +3,8 @@ using System.IO; using System.Linq; using System.Text.Json; using CounterDrone.Core.Models; +using CounterDrone.Core.Repository; +using CounterDrone.Core.Services; using SQLite; namespace CounterDrone.Core @@ -24,6 +26,7 @@ namespace CounterDrone.Core var db = new SQLiteConnection(dbPath); CreateMainTables(db); SeedDefaultData(db); + SeedDefaultScenarios(db); return db; } @@ -63,6 +66,26 @@ namespace CounterDrone.Core db.InsertOrReplace(new MetaEntry { Key = "defaultsVersion", Value = defaults.Version }); } + private void SeedDefaultScenarios(SQLiteConnection db) + { + var currentVersion = db.Table() + .FirstOrDefault(m => m.Key == "scenariosVersion")?.Value; + + // 版本未变 → 跳过 + if (currentVersion == "1") return; + + var defaults = DefaultData.Load(_paths); + var scenario = 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)); + + DefaultScenarios.Seed(scenario, defaults); + + db.InsertOrReplace(new MetaEntry { Key = "scenariosVersion", Value = "1" }); + } + private void CreateMainTables(SQLiteConnection db) { db.CreateTable(); diff --git a/src/CounterDrone.Core/DefaultScenarios.cs b/src/CounterDrone.Core/DefaultScenarios.cs new file mode 100644 index 0000000..4ee5fdb --- /dev/null +++ b/src/CounterDrone.Core/DefaultScenarios.cs @@ -0,0 +1,146 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using CounterDrone.Core.Models; +using CounterDrone.Core.Services; + +namespace CounterDrone.Core +{ + /// + /// 预设想定种子 — 从 DefaultData 预设组装完整想定,写入数据库供前端测试。 + /// 首次运行时自动种子,同名想定跳过(幂等)。 + /// + public static class DefaultScenarios + { + public static void Seed(IScenarioService scenario, DefaultData defaults) + { + // 跳过已存在的预设想定(幂等) + var existing = scenario.SearchTasks("[Demo]", null, null, 1, 100); + if (existing.TotalCount > 0) return; + + SeedNoDefense(scenario, defaults); + SeedZoneIntrusion(scenario, defaults); + SeedPistonWindy(scenario, defaults); + SeedJetActiveMaterial(scenario, defaults); + SeedAirBasedWindy(scenario, defaults); + Seed3DronesAirBased(scenario, defaults); + } + + private static void SeedNoDefense(IScenarioService s, DefaultData d) + { + var t = s.CreateTask("[Demo] 无防御-无人机抵达目标", ""); + s.SaveScene(t.Id, d.Weather.First(w => w.Id == "sunny-calm").ToCombatScene()); + s.SaveTarget(t.Id, d.Targets.First(p => p.Id == "shahed").ToTargetConfig()); + s.SaveRoute(t.Id, "default", d.Formations.First(f => f.Id == "single").ToRoutePlan(), + MakeWaypoints(0, 400, 0, 3000, 400, 0, 600)); + s.SaveCloudDispersal(t.Id, new CloudDispersal()); + s.SaveDeployment(t.Id, new List()); + s.UpdateStep(t.Id, 5); + } + + private static void SeedZoneIntrusion(IScenarioService s, DefaultData d) + { + var t = s.CreateTask("[Demo] 管控区域侵入", ""); + s.SaveScene(t.Id, d.Weather.First(w => w.Id == "sunny-calm").ToCombatScene()); + s.SaveTarget(t.Id, d.Targets.First(p => p.Id == "electric-scout").ToTargetConfig()); + s.SaveRoute(t.Id, "default", d.Formations.First(f => f.Id == "single").ToRoutePlan(), + MakeWaypoints(0, 300, 0, 3000, 300, 0, 300)); + s.SaveCloudDispersal(t.Id, new CloudDispersal()); + s.SaveDeployment(t.Id, new List()); + s.SaveControlZones(t.Id, new List + { + new ControlZone + { + Name = "禁飞区", + VerticesJson = "[{\"X\":1200,\"Y\":0,\"Z\":-200},{\"X\":1800,\"Y\":0,\"Z\":-200},{\"X\":1800,\"Y\":0,\"Z\":200},{\"X\":1200,\"Y\":0,\"Z\":200}]", + MinAltitude = 0, MaxAltitude = 1000, + }, + }); + s.UpdateStep(t.Id, 5); + } + + private static void SeedPistonWindy(IScenarioService s, DefaultData d) + { + var t = s.CreateTask("[Demo] 活塞拦截-西风5m/s", ""); + var scene = d.Weather.First(w => w.Id == "sunny-calm").ToCombatScene(); + scene.WindSpeed = 5; + scene.WindDirection = (int)WindDirection.W; + s.SaveScene(t.Id, scene); + s.SaveTarget(t.Id, d.Targets.First(p => p.Id == "shahed").ToTargetConfig()); + s.SaveRoute(t.Id, "default", d.Formations.First(f => f.Id == "single").ToRoutePlan(), + MakeWaypoints(0, 500, 0, 5000, 500, 0, 150)); + s.SaveDeployment(t.Id, new List + { + d.FireUnits.First(f => f.Id == "ground-light") + .ToEquipmentDeployment(AerosolType.InertGas, 1, 1500, 0, 50), + }); + s.SaveCloudDispersal(t.Id, new CloudDispersal { AerosolType = (int)AerosolType.InertGas, DisperseHeight = 500 }); + s.UpdateStep(t.Id, 5); + } + + private static void SeedJetActiveMaterial(IScenarioService s, DefaultData d) + { + var t = s.CreateTask("[Demo] 喷气式拦截-活性材料", ""); + s.SaveScene(t.Id, d.Weather.First(w => w.Id == "sunny-calm").ToCombatScene()); + s.SaveTarget(t.Id, d.Targets.First(p => p.Id == "cruise-missile").ToTargetConfig()); + s.SaveRoute(t.Id, "default", d.Formations.First(f => f.Id == "single").ToRoutePlan(), + MakeWaypoints(0, 800, 0, 5000, 800, 0, 200)); + s.SaveDeployment(t.Id, new List + { + d.FireUnits.First(f => f.Id == "ground-standard") + .ToEquipmentDeployment(AerosolType.ActiveMaterial, 1, 1500, 0, 50), + }); + s.SaveCloudDispersal(t.Id, new CloudDispersal { AerosolType = (int)AerosolType.ActiveMaterial, DisperseHeight = 800 }); + s.UpdateStep(t.Id, 5); + } + + private static void SeedAirBasedWindy(IScenarioService s, DefaultData d) + { + var t = s.CreateTask("[Demo] 空基拦截-东风5m/s", ""); + var scene = d.Weather.First(w => w.Id == "sunny-calm").ToCombatScene(); + scene.WindSpeed = 5; + scene.WindDirection = (int)WindDirection.E; + s.SaveScene(t.Id, scene); + s.SaveTarget(t.Id, d.Targets.First(p => p.Id == "shahed").ToTargetConfig()); + s.SaveRoute(t.Id, "default", d.Formations.First(f => f.Id == "single").ToRoutePlan(), + MakeWaypoints(0, 500, 0, 10000, 500, 0, 150)); + s.SaveDeployment(t.Id, new List + { + d.FireUnits.First(f => f.Id == "air-standard") + .ToEquipmentDeployment(AerosolType.InertGas, 3, 1500, 1000, 0), + }); + s.SaveCloudDispersal(t.Id, new CloudDispersal { AerosolType = (int)AerosolType.InertGas, DisperseHeight = 500 }); + s.UpdateStep(t.Id, 5); + } + + private static void Seed3DronesAirBased(IScenarioService s, DefaultData d) + { + var t = s.CreateTask("[Demo] 3架空基编队拦截", ""); + s.SaveScene(t.Id, d.Weather.First(w => w.Id == "sunny-calm").ToCombatScene()); + var target = d.Targets.First(p => p.Id == "shahed").ToTargetConfig(); + target.Quantity = 3; + target.TypicalSpeed = 150; + s.SaveTarget(t.Id, target); + var formation = d.Formations.First(f => f.Id == "line-3").ToRoutePlan(); + s.SaveRoute(t.Id, "default", formation, + MakeWaypoints(0, 500, 0, 10000, 500, 0, 150)); + s.SaveDeployment(t.Id, new List + { + d.FireUnits.First(f => f.Id == "air-standard") + .ToEquipmentDeployment(AerosolType.InertGas, 3, 1500, 1000, 0), + }); + s.SaveCloudDispersal(t.Id, new CloudDispersal { AerosolType = (int)AerosolType.InertGas, DisperseHeight = 500 }); + s.UpdateStep(t.Id, 5); + } + + private static List MakeWaypoints( + double x1, double y1, double z1, double x2, double y2, double z2, double speed) + { + return new List + { + new Waypoint { PosX = x1, PosY = y1, PosZ = z1, Altitude = y1, Speed = speed }, + new Waypoint { PosX = x2, PosY = y2, PosZ = z2, Altitude = y2, Speed = speed }, + }; + } + } +} diff --git a/test/unit/CounterDrone.Core.Tests/ScenarioServiceTests.cs b/test/unit/CounterDrone.Core.Tests/ScenarioServiceTests.cs index 59585af..290ea9e 100644 --- a/test/unit/CounterDrone.Core.Tests/ScenarioServiceTests.cs +++ b/test/unit/CounterDrone.Core.Tests/ScenarioServiceTests.cs @@ -306,9 +306,9 @@ namespace CounterDrone.Core.Tests _service.CreateTask($"Task {i:D2}", ""); var page1 = _service.SearchTasks(null, null, null, 1, 5); - Assert.Equal(15, page1.TotalCount); + Assert.Equal(21, page1.TotalCount); // 15 + 6 预设想定 Assert.Equal(5, page1.Items.Count); - Assert.Equal(3, page1.TotalPages); + Assert.Equal(5, page1.TotalPages); // 21 ÷ 5 = 5 页 var page3 = _service.SearchTasks(null, null, null, 3, 5); Assert.Equal(5, page3.Items.Count); @@ -324,7 +324,7 @@ namespace CounterDrone.Core.Tests var tomorrow = DateTime.UtcNow.AddDays(1).ToString("yyyy-MM-dd"); var result = _service.SearchTasks(null, yesterday, tomorrow, 1, 10); - Assert.Equal(1, result.TotalCount); + Assert.Equal(7, result.TotalCount); // 1 + 6 预设想定 var oldResult = _service.SearchTasks(null, "2020-01-01", "2020-12-31", 1, 10); Assert.Equal(0, oldResult.TotalCount);