# SimController 及相关接口实现建议 v4 基于 [情景模拟分析结果v3.md](file:///e:/projectJava/JavaProjectRepo/business-css/%E6%83%85%E6%99%AF%E6%A8%A1%E6%8B%9F%E5%88%86%E6%9E%90%E7%BB%93%E6%9E%9Cv3.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` 来封装数据获取逻辑。 ```java @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 events = eventService.list(new QueryWrapper().eq("scenario_id", scenarioId)); // ... 获取其他必要数据 return new SimDataPackage(project, events); } } ``` ### 2.2 激活并增强 Builder (SimBuilder) `SimBuilder` 目前被注释掉了,需要激活并实现核心转换逻辑。重点是将 `ProjectServiceImpl.initSimulation` 中的解析逻辑迁移过来。 * **`buildUnits`**: 解析 `Project.topology` JSON,提取 Device 和 Material,构建 `SimUnit` 列表。 * *关键点*:需要包含静态属性(Static Values)的解析,作为 `SimUnit` 的初始状态。 * **`buildEvents`**: 解析 `Event.attr_changes` JSON,构建 `SimEvent` 列表。 * *关键点*:区分 **普通事件 (Input)** 和 **强制覆盖事件 (Override)**。建议在 `SimEvent` 中增加 `isOverride` 标志。 * **`buildInfluenceNodes`**: 解析 `Project.topology` 中的 `properties` -> `influence` 节点,构建 `SimInfluenceNode` 列表。 ### 2.3 重构计算引擎 (SimService) 这是最核心的部分,必须修正当前的“事件 -> 计算”逻辑,改为 **标准管线**。 **建议代码结构:** ```java public class SimService { public SimContext runSimulation(List units, List events, List 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` 作为仿真功能的唯一入口。 ```java @RestController @RequestMapping("/sim") public class SimController { @PostMapping("/run") public Result run(@RequestBody SimulationRequest req) { // 1. 加载数据 SimDataPackage data = simDataFacade.loadSimulationData(req.getProjectId(), req.getScenarioId()); // 2. 构建模型 List units = SimBuilder.buildUnits(data.getProject()); List events = SimBuilder.buildEvents(data.getEvents()); List nodes = SimBuilder.buildInfluenceNodes(data.getProject()); // 3. 执行仿真 SimContext ctx = simService.runSimulation(units, events, nodes, req.getSteps()); // 4. 结果转换 (适配前端图表或 AI 推理) return Result.success(SimResultConverter.convert(ctx)); } } ``` --- ## 3. 实施路线图 1. **基础类准备**: * 完善 `SimUnit`: 增加 `Map staticProperties` 字段。 * 完善 `SimEvent`: 增加 `boolean isOverride` 字段。 * 创建 `SimDataFacade` 类。 2. **迁移解析逻辑**: * 将 `ProjectServiceImpl` 中解析 JSON (Topology, Event) 的代码块复制到 `SimBuilder` 中并适配。 * 确保单元测试覆盖 `SimBuilder`,保证解析正确性。 3. **实现计算逻辑**: * 编写 `SimService.runSimulation`,严格按照推荐的 4 步管线实现。 * 编写单元测试,验证“事件覆盖计算”和“计算基于静态值”的场景。 4. **接口接入**: * 在 `SimController` 中装配上述组件。 * 前端对接新的 `/sim/run` 接口。 5. **清理**: * 标记 `ProjectServiceImpl` 中的旧模拟代码为 `@Deprecated`,并在验证新接口无误后删除。 通过以上步骤,可以将复杂的仿真逻辑从业务 Service 中解耦,构建一个清晰、可维护、易扩展的仿真引擎。