# 路由缓存实现计划 > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** 在 RouterService 中引入 HybridCacheService 混合缓存,第一次查询加载全量路由到缓存,后续查询直接从缓存读取,AddRouters 写入后同步更新缓存。 **Architecture:** 按 InOutType 分开缓存两份(入口/出口),GetOrAdd 懒加载,写穿更新缓存,单实例部署无广播需求。 **Tech Stack:** ASP.NET Core 6.0, HybridCacheService, SqlSugar ORM --- ## 文件变更概览 **修改文件:** - `WIDESEAWCS_QuartzJob/Service/RouterService.cs` — 全部缓存逻辑改动 **不变:** - `FindRoutesInMemory` — 只改调用方,算法逻辑不变 - `QueryAllPositions` — 已有独立缓存,不依赖本方案 --- ## Task 1: 新增 GetAllRoutersFromCache 私有方法 **文件:** - Modify: `WIDESEAWCS_QuartzJob/Service/RouterService.cs` 在类成员变量声明区之后、现有方法之前,新增以下私有方法: ```csharp /// /// 从缓存获取指定类型的全量路由数据,缓存不存在时自动从数据库加载并写入缓存 /// /// 路由类型(入口/出口) /// 该类型的全部路由列表 private List GetAllRoutersFromCache(int routeType) { string cacheKey = $"Router:AllRouters:{(routeType == RouterInOutType.In.ObjToInt() ? "In" : "Out")}"; return _cacheService.GetOrAdd( cacheKey, () => BaseDal.QueryData(x => x.InOutType == routeType) ); } --- ## Task 2: 改造 QueryNextRoutes 重载(2个方法) **文件:** - Modify: `WIDESEAWCS_QuartzJob/Service/RouterService.cs:48-98` ### QueryNextRoutes(string, string) — 第48-69行 原代码: ```csharp List allRouters = BaseDal.QueryData(x => true); routers = FindRoutesInMemory(startPosi, endPosi, allRouters, null); ``` 改为: ```csharp List allRouters = GetAllRoutersFromCache(RouterInOutType.In.ObjToInt()); // 入口+出口都加载,FindRoutesInMemory 内部按 routeType==null 不做过滤 List outRouters = GetAllRoutersFromCache(RouterInOutType.Out.ObjToInt()); allRouters.AddRange(outRouters); routers = FindRoutesInMemory(startPosi, endPosi, allRouters, null); ``` ### QueryNextRoutes(string, string, int) — 第78-99行 原代码: ```csharp List allRouters = BaseDal.QueryData(x => x.InOutType == routeType); routers = FindRoutesInMemory(startPosi, endPosi, allRouters, routeType); ``` 改为: ```csharp List allRouters = GetAllRoutersFromCache(routeType); routers = FindRoutesInMemory(startPosi, endPosi, allRouters, routeType); ``` --- ## Task 3: 改造 QueryNextRoute 重载(4个方法) **文件:** - Modify: `WIDESEAWCS_QuartzJob/Service/RouterService.cs:189-314` ### QueryNextRoute(string) — 第189-205行 原代码: ```csharp List routes = BaseDal.QueryData(x => x.StartPosi == startPosi, ...); return routes.FirstOrDefault(); ``` 改为: ```csharp List routes = GetAllRoutersFromCache(RouterInOutType.In.ObjToInt()) .Where(x => x.StartPosi == startPosi) .ToList(); routes = routes.OrderByDescending(x => x.IsEnd).ToList(); return routes.FirstOrDefault(); ``` ### QueryNextRoute(string, int) — 第213-229行 原代码: ```csharp List routes = BaseDal.QueryData(x => x.StartPosi == startPosi && x.InOutType == routeType, ...); return routes.FirstOrDefault(); ``` 改为: ```csharp List routes = GetAllRoutersFromCache(routeType) .Where(x => x.StartPosi == startPosi) .ToList(); routes = routes.OrderByDescending(x => x.IsEnd).ToList(); return routes.FirstOrDefault(); ``` ### QueryNextRoute(string, string, int) — 第238-272行 原代码(第255行): ```csharp List allRouters = BaseDal.QueryData(x => x.InOutType == routeType); ``` 改为: ```csharp List allRouters = GetAllRoutersFromCache(routeType); ``` 其余直接路由判断和 `FindRoutesInMemory` 调用逻辑不变。 ### QueryNextRoute(string, string) — 第280-314行 原代码(第285行): ```csharp List routes = BaseDal.QueryData(x => x.StartPosi == startPosi, ...); ``` 改为(合并入口+出口两份缓存,与原方法全量查询语义一致): ```csharp List inRoutes = GetAllRoutersFromCache(RouterInOutType.In.ObjToInt()); List outRoutes = GetAllRoutersFromCache(RouterInOutType.Out.ObjToInt()); List routes = inRoutes.Concat(outRoutes) .Where(x => x.StartPosi == startPosi) .ToList(); routes = routes.OrderByDescending(x => x.IsEnd).ToList(); ``` 然后复用已有的直接路由判断逻辑。 --- ## Task 4: 改造 QueryRoutePath **文件:** - Modify: `WIDESEAWCS_QuartzJob/Service/RouterService.cs:323-366` 第355行附近,原代码中 `while` 循环里每次调用 `QueryNextRoute` 的逻辑**不需要改动**(因为 `QueryNextRoute` 已经在 Task3 中改造了)。本任务的改动仅确认 `QueryRoutePath` 方法本身不再直接调用 `BaseDal.QueryData`,即不需要任何改动 —— 它调用的是 `QueryNextRoute`,而后者已被改造。 **验证:** 确认 `QueryRoutePath` 方法体内没有直接调用 `BaseDal.QueryData`,只有对 `QueryNextRoute` 的调用。 --- ## Task 5: 改造 GetAllWholeRouters **文件:** - Modify: `WIDESEAWCS_QuartzJob/Service/RouterService.cs:412-446` 原代码: ```csharp List allRouters = BaseDal.QueryData(x => true); ``` 改为: ```csharp List inRouters = GetAllRoutersFromCache(RouterInOutType.In.ObjToInt()); List outRouters = GetAllRoutersFromCache(RouterInOutType.Out.ObjToInt()); List allRouters = inRouters.Concat(outRouters).ToList(); ``` 后续遍历 `dt_Routers`(取 `IsEnd == true` 的路由)的逻辑不变。 --- ## Task 6: 改造 AddRouters,写入 DB 后更新缓存 **文件:** - Modify: `WIDESEAWCS_QuartzJob/Service/RouterService.cs:511-594` 在第568行附近,找到: ```csharp // 查询数据库中已有的路由信息 List dt_Routers = BaseDal.QueryData(x => x.InOutType == routerType); ``` 这个 `dt_Routers` 是**添加之前**的已有数据,不包含本次新增的路由。在第586行 `BaseDal.AddData(routers)` **之后**、`content = WebResponseContent.Instance.OK()` **之前**,必须重新查询一次全量数据再写入缓存: ```csharp // 添加新的路由信息 BaseDal.AddData(routers); // 重新查询全量路由(此时才包含新增的路由),再写入缓存 List updatedRouters = BaseDal.QueryData(x => x.InOutType == routerType); string cacheKey = $"Router:AllRouters:{(routerType == RouterInOutType.In.ObjToInt() ? "In" : "Out")}"; _cacheService.Set(cacheKey, updatedRouters); content = WebResponseContent.Instance.OK(); ``` --- ## Task 7: 编译验证 **验证命令:** ```bash dotnet build WIDESEAWCS_Server/WIDESEAWCS_QuartzJob/WIDESEAWCS_QuartzJob.csproj ``` 预期:无编译错误(CS0103/CS0019 等类型/语法错误全部修复后) --- ## 实施顺序 1. Task 1 — 新增 `GetAllRoutersFromCache` 私有方法 2. Task 2 — 改造 `QueryNextRoutes` 两个重载 3. Task 3 — 改造 `QueryNextRoute` 四个重载 4. Task 4 — 确认 `QueryRoutePath` 无需改动(它依赖已改造的 `QueryNextRoute`) 5. Task 5 — 改造 `GetAllWholeRouters` 6. Task 6 — 改造 `AddRouters` 更新缓存 7. Task 7 — 编译验证