feat: 新增 SGS SDK 数据适配器与腾讯地图服务
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
358
src/data/providers/sgsSdkApiProvider.ts
Normal file
358
src/data/providers/sgsSdkApiProvider.ts
Normal 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()
|
||||
Reference in New Issue
Block a user