6.7 KiB
6.7 KiB
SimController 及相关接口实现建议 v4
基于 情景模拟分析结果v3.md 中的分析,SimController 及其配套组件 (SimService, SimBuilder, SimModel) 代表了系统向模块化、可测试化方向演进的正确路径。
当前 SimController 处于不可用状态(依赖缺失、逻辑简化、代码注释)。为了将其转化为生产可用的仿真服务,以下是具体的实现建议与重构方案。
1. 总体架构设计
目标是将仿真逻辑从 ProjectServiceImpl 中剥离,构建独立的仿真层。
- Controller 层 (
SimController): 仅负责接收 HTTP 请求,参数校验,调用 Service,返回结果。 - Facade 层 (
SimDataFacade): 新增组件。负责与现有的ProjectService,EventService,InfluenceService交互,获取原始数据(Project 实体, Event 列表等),并屏蔽数据库细节。注:原设计中的ProjectRepository等接口在本项目中没有实现,直接复用现有的 Service 层更符合现状。 - Builder 层 (
SimBuilder): 负责将原始数据(Entity/JSON)转换为仿真专用的领域模型 (SimUnit,SimEvent,SimInfluenceNode)。 - Engine 层 (
SimService): 核心计算引擎。纯内存计算,不依赖数据库。执行Static -> Event -> Influence -> Override的标准管线。
2. 详细实现建议
2.1 补齐数据获取层 (SimDataFacade)
原 SimController 依赖了不存在的 ProjectRepository 等接口。建议创建一个 SimDataFacade 来封装数据获取逻辑。
@Component
public class SimDataFacade {
private final ProjectService projectService;
private final EventService eventService;
// ... 其他 Service
// 封装获取逻辑:获取项目拓扑、事件列表、影响关系等
public SimDataPackage loadSimulationData(String projectId, String scenarioId) {
Project project = projectService.getById(projectId);
List<Event> events = eventService.list(new QueryWrapper<Event>().eq("scenario_id", scenarioId));
// ... 获取其他必要数据
return new SimDataPackage(project, events);
}
}
2.2 激活并增强 Builder (SimBuilder)
SimBuilder 目前被注释掉了,需要激活并实现核心转换逻辑。重点是将 ProjectServiceImpl.initSimulation 中的解析逻辑迁移过来。
buildUnits: 解析Project.topologyJSON,提取 Device 和 Material,构建SimUnit列表。- 关键点:需要包含静态属性(Static Values)的解析,作为
SimUnit的初始状态。
- 关键点:需要包含静态属性(Static Values)的解析,作为
buildEvents: 解析Event.attr_changesJSON,构建SimEvent列表。- 关键点:区分 普通事件 (Input) 和 强制覆盖事件 (Override)。建议在
SimEvent中增加isOverride标志。
- 关键点:区分 普通事件 (Input) 和 强制覆盖事件 (Override)。建议在
buildInfluenceNodes: 解析Project.topology中的properties->influence节点,构建SimInfluenceNode列表。
2.3 重构计算引擎 (SimService)
这是最核心的部分,必须修正当前的“事件 -> 计算”逻辑,改为 标准管线。
建议代码结构:
public class SimService {
public SimContext runSimulation(List<SimUnit> units,
List<SimEvent> events,
List<SimInfluenceNode> nodes,
int steps) {
SimContext ctx = new SimContext();
// 1. 初始化静态基线 (Static)
// 将 SimUnit 中携带的静态属性写入 ctx (t=0)
for (SimUnit unit : units) {
unit.getStaticProperties().forEach((k, v) ->
ctx.setValue(SimPropertyKey.of(unit.id(), k), v)
);
}
// 2. 时间步推进
for (int step = 0; step <= steps; step++) {
// 2.1 应用输入事件 (Event Input)
// 筛选当前 step 的普通事件,写入 ctx
applyEvents(ctx, events, step, false);
// 2.2 执行影响计算 (Influence)
// 基于当前 ctx 状态,计算所有 InfluenceNode
// 注意:为了避免计算顺序依赖,建议使用双缓冲 (Snapshot) 或 拓扑排序
// 简单实现可先计算 diff,再统一应用
applyInfluences(ctx, nodes);
// 2.3 应用强制覆盖事件 (Event Override)
// 筛选当前 step 的强制事件,再次写入 ctx,覆盖计算结果
applyEvents(ctx, events, step, true);
// 2.4 保存快照
ctx.snapshot(step);
}
return ctx;
}
}
2.4 统一 API 接口 (SimController)
建议将 SimController 作为仿真功能的唯一入口。
@RestController
@RequestMapping("/sim")
public class SimController {
@PostMapping("/run")
public Result<SimulationResult> run(@RequestBody SimulationRequest req) {
// 1. 加载数据
SimDataPackage data = simDataFacade.loadSimulationData(req.getProjectId(), req.getScenarioId());
// 2. 构建模型
List<SimUnit> units = SimBuilder.buildUnits(data.getProject());
List<SimEvent> events = SimBuilder.buildEvents(data.getEvents());
List<SimInfluenceNode> nodes = SimBuilder.buildInfluenceNodes(data.getProject());
// 3. 执行仿真
SimContext ctx = simService.runSimulation(units, events, nodes, req.getSteps());
// 4. 结果转换 (适配前端图表或 AI 推理)
return Result.success(SimResultConverter.convert(ctx));
}
}
3. 实施路线图
-
基础类准备:
- 完善
SimUnit: 增加Map<String, Double> staticProperties字段。 - 完善
SimEvent: 增加boolean isOverride字段。 - 创建
SimDataFacade类。
- 完善
-
迁移解析逻辑:
- 将
ProjectServiceImpl中解析 JSON (Topology, Event) 的代码块复制到SimBuilder中并适配。 - 确保单元测试覆盖
SimBuilder,保证解析正确性。
- 将
-
实现计算逻辑:
- 编写
SimService.runSimulation,严格按照推荐的 4 步管线实现。 - 编写单元测试,验证“事件覆盖计算”和“计算基于静态值”的场景。
- 编写
-
接口接入:
- 在
SimController中装配上述组件。 - 前端对接新的
/sim/run接口。
- 在
-
清理:
- 标记
ProjectServiceImpl中的旧模拟代码为@Deprecated,并在验证新接口无误后删除。
- 标记
通过以上步骤,可以将复杂的仿真逻辑从业务 Service 中解耦,构建一个清晰、可维护、易扩展的仿真引擎。