feat: 预设想定种子 (DefaultScenarios)
6 个预设想定首次启动自动入库,幂等跳过:无防御/管控侵入/活塞西风/喷气活性/空基东风/3架空基编队 通过 DatabaseManager.OpenMainDb() 自动种子,版本追踪 ScenarioServiceTests 期望值适配(+6 个种子想定)
This commit is contained in:
parent
d6fb206078
commit
4fa19e386c
@ -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<MetaEntry>()
|
||||
.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<ModelInfo>();
|
||||
|
||||
146
src/CounterDrone.Core/DefaultScenarios.cs
Normal file
146
src/CounterDrone.Core/DefaultScenarios.cs
Normal file
@ -0,0 +1,146 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using CounterDrone.Core.Models;
|
||||
using CounterDrone.Core.Services;
|
||||
|
||||
namespace CounterDrone.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// 预设想定种子 — 从 DefaultData 预设组装完整想定,写入数据库供前端测试。
|
||||
/// 首次运行时自动种子,同名想定跳过(幂等)。
|
||||
/// </summary>
|
||||
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<EquipmentDeployment>());
|
||||
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<EquipmentDeployment>());
|
||||
s.SaveControlZones(t.Id, new List<ControlZone>
|
||||
{
|
||||
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<EquipmentDeployment>
|
||||
{
|
||||
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<EquipmentDeployment>
|
||||
{
|
||||
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<EquipmentDeployment>
|
||||
{
|
||||
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<EquipmentDeployment>
|
||||
{
|
||||
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<Waypoint> MakeWaypoints(
|
||||
double x1, double y1, double z1, double x2, double y2, double z2, double speed)
|
||||
{
|
||||
return new List<Waypoint>
|
||||
{
|
||||
new Waypoint { PosX = x1, PosY = y1, PosZ = z1, Altitude = y1, Speed = speed },
|
||||
new Waypoint { PosX = x2, PosY = y2, PosZ = z2, Altitude = y2, Speed = speed },
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user