perf(monitor): 监控配置加载缓存去重,避免重复全量反射扫描
- 新增 monitorConfigCache 进程内缓存(inflight 去重)与 carActionByTypeLookup - opsMonitorConfig 支持传入预加载快照与缓存根,保存后失效缓存 - 运维配置页/选中信息面板复用缓存,修复车型动作勾选与 model 不同步 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
/** 按车型 key 查找已保存的动作白名单(兼容 fullTypeName / shortTypeName / 后缀匹配)。 */
|
||||
export function lookupCarActionKeys(
|
||||
map: Record<string, string[] | undefined> | null | undefined,
|
||||
typeKey: string,
|
||||
shortTypeName: string
|
||||
): string[] {
|
||||
if (!map) return []
|
||||
const direct = map[typeKey] ?? map[shortTypeName]
|
||||
if (direct?.length) return [...direct]
|
||||
for (const [k, v] of Object.entries(map)) {
|
||||
if (!v?.length) continue
|
||||
if (k === typeKey || k === shortTypeName) return [...v]
|
||||
if (typeKey.endsWith('.' + k) || k.endsWith('.' + shortTypeName)) return [...v]
|
||||
if (typeKey.endsWith('.' + shortTypeName) && (k === shortTypeName || k.endsWith('.' + shortTypeName))) {
|
||||
return [...v]
|
||||
}
|
||||
}
|
||||
return []
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import { reflectionApi, type MonitorConfigPayload } from '@/api/reflection'
|
||||
|
||||
/** 进程内缓存:避免每次选中车辆都 GET /monitor-config(全量反射扫描,极慢)。 */
|
||||
let cached: MonitorConfigPayload | null = null
|
||||
let inflight: Promise<MonitorConfigPayload> | null = null
|
||||
|
||||
export function invalidateMonitorConfigCache(): void {
|
||||
cached = null
|
||||
inflight = null
|
||||
}
|
||||
|
||||
export async function fetchMonitorConfigCached(force = false): Promise<MonitorConfigPayload> {
|
||||
if (!force && cached) return cached
|
||||
if (!force && inflight) return inflight
|
||||
inflight = reflectionApi.getMonitorConfig().then((mc) => {
|
||||
cached = mc
|
||||
inflight = null
|
||||
return mc
|
||||
}).catch((e) => {
|
||||
inflight = null
|
||||
throw e
|
||||
})
|
||||
return inflight
|
||||
}
|
||||
@@ -1,7 +1,15 @@
|
||||
import { reflectionApi, emptyMonitorVisibilityMap, type MonitorVisibilityMap } from '@/api/reflection'
|
||||
import {
|
||||
reflectionApi,
|
||||
emptyMonitorVisibilityMap,
|
||||
type MonitorConfigPayload,
|
||||
type MonitorVisibilityMap
|
||||
} from '@/api/reflection'
|
||||
import { invalidateMonitorConfigCache } from '@/utils/monitorConfigCache'
|
||||
import { DEFAULT_OPS } from '@/mock/data/configs'
|
||||
import type { MonitorOpsPolicy, OpsConfig } from '@/types/config'
|
||||
|
||||
export type MonitorConfigRoot = MonitorVisibilityMap & { carActionByType?: Record<string, string[]> }
|
||||
|
||||
export function defaultMonitorPolicy(): MonitorOpsPolicy {
|
||||
return JSON.parse(JSON.stringify(DEFAULT_OPS.monitor)) as MonitorOpsPolicy
|
||||
}
|
||||
@@ -27,11 +35,22 @@ export function normalizeOpsConfig(raw?: Partial<OpsConfig> | null): OpsConfig {
|
||||
}
|
||||
}
|
||||
|
||||
type MonitorConfigRoot = MonitorVisibilityMap & { carActionByType?: Record<string, string[]> }
|
||||
/** 深拷贝车型动作表,避免与 SimpleLite 返回对象共享引用导致勾选 UI 不同步。 */
|
||||
export function cloneCarActionByType(
|
||||
src?: Record<string, string[] | readonly string[]> | null
|
||||
): Record<string, string[]> {
|
||||
const out: Record<string, string[]> = {}
|
||||
if (!src) return out
|
||||
for (const [k, v] of Object.entries(src)) {
|
||||
if (!k || !Array.isArray(v)) continue
|
||||
out[k] = [...v]
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
function pickMonitorRoot(cfg: MonitorConfigRoot) {
|
||||
return {
|
||||
carActionByType: { ...(cfg.carActionByType ?? {}) },
|
||||
carActionByType: cloneCarActionByType(cfg.carActionByType),
|
||||
siteActionKeys: [...(cfg.site?.methods ?? [])],
|
||||
trackActionKeys: [...(cfg.track?.methods ?? [])]
|
||||
}
|
||||
@@ -41,35 +60,56 @@ function pickMonitorRoot(cfg: MonitorConfigRoot) {
|
||||
* 从 SimpleLite simple.json 加载地图监控配置(运行时唯一可信源)。
|
||||
* Platform ops 里的 monitor 仅作备份;以 SimpleLite 为准覆盖表单。
|
||||
*/
|
||||
export async function loadMonitorSettingsIntoOps(payload: OpsConfig): Promise<OpsConfig> {
|
||||
export interface LoadMonitorSettingsResult {
|
||||
config: OpsConfig
|
||||
/** 与 SimpleLite 对齐的 monitor-config 快照,供保存时合并 fields/status,避免每次 POST 前再 GET。 */
|
||||
cache: MonitorConfigRoot | null
|
||||
}
|
||||
|
||||
export async function loadMonitorSettingsIntoOps(
|
||||
payload: OpsConfig,
|
||||
preloaded?: MonitorConfigPayload | null
|
||||
): Promise<LoadMonitorSettingsResult> {
|
||||
const next = normalizeOpsConfig(payload)
|
||||
let cache: MonitorConfigRoot | null = null
|
||||
try {
|
||||
const mc = await reflectionApi.getMonitorConfig()
|
||||
const picked = pickMonitorRoot(mc.config as MonitorConfigRoot)
|
||||
const mc = preloaded ?? (await reflectionApi.getMonitorConfig())
|
||||
cache = mc.config as MonitorConfigRoot
|
||||
const picked = pickMonitorRoot(cache)
|
||||
next.monitor.carActionByType = picked.carActionByType
|
||||
next.monitor.site.actionKeys = picked.siteActionKeys
|
||||
next.monitor.track.actionKeys = picked.trackActionKeys
|
||||
} catch {
|
||||
// SimpleLite 未连接:退回 Platform payload 中已有的 monitor(若有)
|
||||
}
|
||||
return next
|
||||
return { config: next, cache }
|
||||
}
|
||||
|
||||
/**
|
||||
* 将运营维护表单中的地图监控配置写入 SimpleLite(simple.json)。
|
||||
* 保留「地图监控配置」页面对 fields/status 的既有设置,只更新动作相关字段。
|
||||
*/
|
||||
export async function saveMonitorSettingsFromOps(payload: OpsConfig): Promise<void> {
|
||||
export interface SaveMonitorSettingsOptions {
|
||||
/** 页面加载时缓存的 monitor-config,避免勾选自动保存时重复 GET(该接口会全量反射扫描)。 */
|
||||
cachedRoot?: MonitorConfigRoot | null
|
||||
}
|
||||
|
||||
export async function saveMonitorSettingsFromOps(
|
||||
payload: OpsConfig,
|
||||
options?: SaveMonitorSettingsOptions
|
||||
): Promise<MonitorConfigRoot> {
|
||||
const monitor = normalizeOpsConfig(payload).monitor
|
||||
let current: MonitorConfigRoot = emptyMonitorVisibilityMap()
|
||||
try {
|
||||
const mc = await reflectionApi.getMonitorConfig()
|
||||
current = mc.config as MonitorConfigRoot
|
||||
} catch {
|
||||
// 无现有配置时用空壳,仍可写入动作白名单
|
||||
let current: MonitorConfigRoot = options?.cachedRoot ?? emptyMonitorVisibilityMap()
|
||||
if (!options?.cachedRoot) {
|
||||
try {
|
||||
const mc = await reflectionApi.getMonitorConfig()
|
||||
current = mc.config as MonitorConfigRoot
|
||||
} catch {
|
||||
// 无现有配置时用空壳,仍可写入动作白名单
|
||||
}
|
||||
}
|
||||
|
||||
await reflectionApi.saveMonitorConfig({
|
||||
const saved = await reflectionApi.saveMonitorConfig({
|
||||
car: {
|
||||
fields: [...(current.car?.fields ?? [])],
|
||||
status: [...(current.car?.status ?? [])],
|
||||
@@ -85,8 +125,10 @@ export async function saveMonitorSettingsFromOps(payload: OpsConfig): Promise<vo
|
||||
status: [...(current.track?.status ?? [])],
|
||||
methods: [...monitor.track.actionKeys]
|
||||
},
|
||||
carActionByType: { ...monitor.carActionByType }
|
||||
carActionByType: cloneCarActionByType(monitor.carActionByType)
|
||||
})
|
||||
invalidateMonitorConfigCache()
|
||||
return saved as MonitorConfigRoot
|
||||
}
|
||||
|
||||
/** @deprecated 使用 loadMonitorSettingsIntoOps */
|
||||
|
||||
Reference in New Issue
Block a user