feat: 新增 SGS SDK 数据适配器与腾讯地图服务

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
lyf
2026-06-26 04:21:46 +08:00
parent 15fbbec12d
commit cc7c21a285
13 changed files with 2938 additions and 0 deletions

View File

@@ -0,0 +1,358 @@
import {
dataSourceConfig
} from '@/config/dataSource'
export type SgsDiagnosticsStatusPayload = 'OK' | 'WARN' | 'ERROR'
export interface SgsSdkFloorSummaryPayload {
floorId: string | number
floorCode?: string | null
floorName?: string | null
sortOrder?: number | null
modelSizeBytes?: number | null
poiCount?: number | null
spaceCount?: number | null
}
export interface SgsSdkManifestPayload {
mapId: string | number
mapName?: string | null
sdkVersion?: string | null
protocolVersion?: number | null
dataVersion?: string | null
updatedAt?: string | null
coordinateSystem?: string | null
floors: SgsSdkFloorSummaryPayload[]
capabilities?: Record<string, boolean | undefined>
}
export interface SgsPositionPayload {
x?: number | null
y?: number | null
z?: number | null
}
export interface SgsPoiPayload {
id: string | number
name?: string | null
type?: string | null
typeName?: string | null
floorCode?: string | null
floorId?: string | number | null
position?: SgsPositionPayload | null
x?: number | null
y?: number | null
z?: number | null
status?: string | null
anchorNodeName?: string | null
description?: string | null
iconUrl?: string | null
}
export interface SgsSpacePayload {
id: string | number
name?: string | null
type?: string | null
floorId?: string | number | null
boundaryWkt?: string | null
center?: SgsPositionPayload | null
sourceNodeName?: string | null
status?: string | null
colorHex?: string | null
}
export interface SgsNavigablePlacePayload {
id?: string | number | null
name?: string | null
category?: string | null
type?: string | null
typeCode?: string | null
typeName?: string | null
floorId?: string | number | null
floorCode?: string | null
floorName?: string | null
position?: SgsPositionPayload | null
x?: number | null
y?: number | null
z?: number | null
nodeId?: string | number | null
ownerName?: string | null
}
export interface SgsFloorDiagnosticsPayload {
floorId: string | number
floorCode?: string | null
floorName?: string | null
status: SgsDiagnosticsStatusPayload
modelReady?: boolean
poiCount?: number
spaceCount?: number
guideStopCount?: number
navigablePlaceCount?: number
routeNodeCount?: number
routeEdgeCount?: number
routePlanningReady?: boolean
warnings?: string[]
}
export interface SgsFloorDiagnosticsSummaryPayload extends SgsFloorDiagnosticsPayload {}
export interface SgsMapDiagnosticsPayload {
mapId: string | number
mapName?: string | null
sdkVersion?: string | null
status: SgsDiagnosticsStatusPayload
floors: SgsFloorDiagnosticsSummaryPayload[]
summary?: {
floorCount?: number
modelReadyFloorCount?: number
poiCount?: number
spaceCount?: number
guideStopCount?: number
navigablePlaceCount?: number
routeNodeCount?: number
routeEdgeCount?: number
}
warnings?: string[]
}
export interface SgsFloorBundlePayload {
floor?: SgsSdkFloorSummaryPayload | null
model?: SgsModelInfoPayload | null
pois?: SgsPoiPayload[]
spaces?: SgsSpacePayload[]
guideStops?: unknown[]
routeSummary?: {
hasRouteNetwork?: boolean
nodeCount?: number
edgeCount?: number
} | null
hiddenSceneNodeNames?: string[]
dataVersion?: string | null
updatedAt?: string | null
}
export interface SgsModelInfoPayload {
modelUrl?: string | null
fallbackModelUrl?: string | null
compressionType?: string | null
modelVersion?: string | null
nodeCount?: number | null
sizeBytes?: number | null
originSizeBytes?: number | null
}
export interface SgsRoutePlanRequestPayload {
startFloorId: number | string
startX: number
startY: number
startNodeId?: number | string | null
endFloorId: number | string
endX: number
endY: number
endNodeId?: number | string | null
wheelchair: 0 | 1
}
export interface SgsRouteStepNodePayload {
id?: string | number | null
nodeName?: string | null
floorId?: string | number | null
nodeType?: string | null
x?: number | null
y?: number | null
}
export interface SgsRouteSegmentPayload {
floorId?: string | number | null
floorName?: string | null
startNodeId?: string | number | null
endNodeId?: string | number | null
startNodeName?: string | null
endNodeName?: string | null
transferType?: string | null
distance?: number | null
duration?: number | null
pathGeoJson?: string | null
nodePathIds?: Array<string | number | null>
}
export interface SgsRoutePlanResponsePayload {
distance?: number | null
duration?: number | null
pathGeoJson?: string | null
nodePaths?: SgsRouteStepNodePayload[]
segments?: SgsRouteSegmentPayload[]
}
interface CommonResult<T> {
code: number
msg?: string
data?: T
}
export interface SgsSdkApiProvider {
getManifest(mapId?: string): Promise<SgsSdkManifestPayload>
getMapDiagnostics(mapId?: string): Promise<SgsMapDiagnosticsPayload>
getFloorDiagnostics(floorId: string): Promise<SgsFloorDiagnosticsPayload>
getFloorBundle(floorId: string): Promise<SgsFloorBundlePayload>
getFloorPois(floorId: string): Promise<SgsPoiPayload[]>
getFloorSpaces(floorId: string): Promise<SgsSpacePayload[]>
getNavigablePlaces(floorId: string): Promise<SgsNavigablePlacePayload[]>
planRoute(payload: SgsRoutePlanRequestPayload): Promise<SgsRoutePlanResponsePayload>
}
const normalizeBaseUrl = (baseUrl: string) => baseUrl.replace(/\/+$/, '')
const resolveAppApiBaseUrl = () => {
const baseUrl = normalizeBaseUrl(dataSourceConfig.sgsApiBaseUrl || dataSourceConfig.apiBaseUrl || '/app-api')
return baseUrl.endsWith('/app-api') ? baseUrl : `${baseUrl}/app-api`
}
const parseJsonPayload = <T>(payload: unknown): T => {
if (typeof payload === 'string') {
return JSON.parse(payload) as T
}
return payload as T
}
const requestJson = <T>(
path: string,
options: { method?: 'GET' | 'POST'; data?: unknown } = {}
): Promise<T> => new Promise((resolve, reject) => {
const baseUrl = resolveAppApiBaseUrl()
uni.request({
url: `${baseUrl}${path}`,
method: options.method || 'GET',
data: options.method === 'POST' ? JSON.stringify(options.data || {}) : undefined,
header: options.method === 'POST'
? {
'Content-Type': 'application/json'
}
: undefined,
timeout: dataSourceConfig.sgsSdkTimeoutMs,
success: (response) => {
const statusCode = Number(response.statusCode || 0)
if (statusCode < 200 || statusCode >= 300) {
reject(new Error(`SGS 数据接口请求失败: ${statusCode} ${path}`))
return
}
try {
const body = parseJsonPayload<CommonResult<T>>(response.data)
if (!body || body.code !== 0) {
reject(new Error(`SGS 数据接口业务失败: ${path} code=${body?.code} msg=${body?.msg || ''}`))
return
}
resolve(body.data as T)
} catch (error) {
reject(error)
}
},
fail: (error) => {
reject(new Error(`SGS 数据接口网络失败: ${path} ${JSON.stringify(error)}`))
}
})
})
export const createSgsSdkApiProvider = (): SgsSdkApiProvider => {
const manifestCache = new Map<string, SgsSdkManifestPayload>()
const mapDiagnosticsCache = new Map<string, SgsMapDiagnosticsPayload>()
const floorDiagnosticsCache = new Map<string, SgsFloorDiagnosticsPayload>()
const floorBundleCache = new Map<string, SgsFloorBundlePayload>()
const floorPoiCache = new Map<string, SgsPoiPayload[]>()
const floorSpaceCache = new Map<string, SgsSpacePayload[]>()
const navigablePlaceCache = new Map<string, SgsNavigablePlacePayload[]>()
const mapIdFor = (mapId?: string) => mapId || dataSourceConfig.sgsMapId
const floorIdFor = (floorId: string) => encodeURIComponent(floorId)
return {
async getManifest(mapId) {
const normalizedMapId = mapIdFor(mapId)
const cached = manifestCache.get(normalizedMapId)
if (cached) return cached
const manifest = await requestJson<SgsSdkManifestPayload>(
`/gis/sdk/maps/${encodeURIComponent(normalizedMapId)}/manifest`
)
manifestCache.set(normalizedMapId, manifest)
return manifest
},
async getMapDiagnostics(mapId) {
const normalizedMapId = mapIdFor(mapId)
const cached = mapDiagnosticsCache.get(normalizedMapId)
if (cached) return cached
const diagnostics = await requestJson<SgsMapDiagnosticsPayload>(
`/gis/sdk/maps/${encodeURIComponent(normalizedMapId)}/diagnostics`
)
mapDiagnosticsCache.set(normalizedMapId, diagnostics)
return diagnostics
},
async getFloorDiagnostics(floorId) {
const cached = floorDiagnosticsCache.get(floorId)
if (cached) return cached
const diagnostics = await requestJson<SgsFloorDiagnosticsPayload>(
`/gis/sdk/floors/${floorIdFor(floorId)}/diagnostics`
)
floorDiagnosticsCache.set(floorId, diagnostics)
return diagnostics
},
async getFloorBundle(floorId) {
const cached = floorBundleCache.get(floorId)
if (cached) return cached
const bundle = await requestJson<SgsFloorBundlePayload>(
`/gis/sdk/floors/${floorIdFor(floorId)}/bundle`
)
floorBundleCache.set(floorId, bundle)
return bundle
},
async getFloorPois(floorId) {
const cached = floorPoiCache.get(floorId)
if (cached) return cached
const pois = await requestJson<SgsPoiPayload[]>(
`/gis/sdk/floors/${floorIdFor(floorId)}/pois`
)
floorPoiCache.set(floorId, pois)
return pois
},
async getFloorSpaces(floorId) {
const cached = floorSpaceCache.get(floorId)
if (cached) return cached
const spaces = await requestJson<SgsSpacePayload[]>(
`/gis/sdk/floors/${floorIdFor(floorId)}/spaces`
)
floorSpaceCache.set(floorId, spaces)
return spaces
},
async getNavigablePlaces(floorId) {
const cached = navigablePlaceCache.get(floorId)
if (cached) return cached
const places = await requestJson<SgsNavigablePlacePayload[]>(
`/gis/sdk/floors/${floorIdFor(floorId)}/navigable-places`
)
navigablePlaceCache.set(floorId, places)
return places
},
async planRoute(payload) {
return requestJson<SgsRoutePlanResponsePayload>(
'/gis/sdk/routes/plan',
{
method: 'POST',
data: payload
}
)
}
}
}
export const defaultSgsSdkApiProvider = createSgsSdkApiProvider()