# 路由缓存实现计划
> **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 — 编译验证