chore: freeze guide and explain update

This commit is contained in:
lyf
2026-06-24 18:00:25 +08:00
parent feb7310a46
commit 67c6609ae6
104 changed files with 3203572 additions and 40713 deletions

View File

@@ -0,0 +1,157 @@
import {
dataSourceConfig
} from '@/config/dataSource'
import {
normalizeSameOriginPublicUrl
} from '@/utils/publicUrl'
import type {
AudioPlayTargetType
} from '@/domain/museum'
export type AudioLanguage = 'zh-CN' | 'en-US'
export interface AudioPlayInfoRequest {
targetType: AudioPlayTargetType
targetId: string
lang?: AudioLanguage
}
export interface AudioPlayInfo {
playable: boolean
targetType: AudioPlayTargetType
targetId: string | number
lang: AudioLanguage
narrationTier?: 'STANDARD' | 'EXTENDED'
audioId?: string | number | null
title?: string | null
duration?: number | null
format?: string | null
playUrl?: string | null
expiresAt?: string | null
subtitleUrl?: string | null
fallback?: boolean
fallbackReason?: string | null
reason?: string | null
}
interface AudioPlayInfoResponse {
code: number
msg?: string
data?: AudioPlayInfo
}
export interface AudioPlayInfoRepository {
getPlayInfo(request: AudioPlayInfoRequest): Promise<AudioPlayInfo>
clearCache(lang?: AudioLanguage): void
}
const reasonMessageMap: Record<string, string> = {
NO_PUBLISHED_AUDIO: '当前语言暂无语音讲解',
NO_GUIDE_STOP: '该展品暂未配置语音讲解',
NO_GUIDE_CONTENT: '该目标暂无讲解内容',
UNSUPPORTED_LANGUAGE: '不支持该语言',
UNSUPPORTED_TARGET_TYPE: '该目标类型暂不支持播放',
TARGET_NOT_FOUND: '该讲解音频暂不可用,当前提供图文讲解',
SERVICE_ERROR: '语音服务暂不可用,请稍后重试'
}
const parseJsonPayload = <T>(payload: unknown): T => {
if (typeof payload === 'string') {
return JSON.parse(payload) as T
}
return payload as T
}
const requestJson = <T>(url: string): Promise<T> => new Promise((resolve, reject) => {
uni.request({
url,
method: 'GET',
success: (response) => {
const statusCode = Number(response.statusCode || 0)
if (statusCode < 200 || statusCode >= 300) {
reject(new Error(`语音播放解析接口请求失败: ${statusCode}`))
return
}
try {
resolve(parseJsonPayload<T>(response.data))
} catch (error) {
reject(error)
}
},
fail: reject
})
})
const normalizeBaseUrl = (baseUrl: string) => baseUrl.replace(/\/+$/, '')
const resolveAudioApiBaseUrl = () => {
const baseUrl = normalizeBaseUrl(dataSourceConfig.audioApiBaseUrl)
return baseUrl.endsWith('/app-api') ? baseUrl : `${baseUrl}/app-api`
}
const audioKey = ({ targetType, targetId, lang }: Required<AudioPlayInfoRequest>) => (
`${targetType}:${targetId}:${lang}`
)
const isExpired = (expiresAt?: string | null) => (
Boolean(expiresAt) && new Date(expiresAt as string).getTime() <= Date.now()
)
export const audioReasonToText = (reason?: string | null) => (
reason ? reasonMessageMap[reason] || '该讲解暂无可播放音频' : '该讲解暂无可播放音频'
)
export class DefaultAudioPlayInfoRepository implements AudioPlayInfoRepository {
private readonly cache = new Map<string, AudioPlayInfo>()
async getPlayInfo(request: AudioPlayInfoRequest): Promise<AudioPlayInfo> {
const normalizedRequest: Required<AudioPlayInfoRequest> = {
targetType: request.targetType,
targetId: request.targetId,
lang: request.lang || (dataSourceConfig.audioLanguage as AudioLanguage)
}
const key = audioKey(normalizedRequest)
const cached = this.cache.get(key)
if (cached && !isExpired(cached.expiresAt)) {
return cached
}
const params = new URLSearchParams({
targetType: normalizedRequest.targetType,
targetId: normalizedRequest.targetId,
lang: normalizedRequest.lang
})
const url = `${resolveAudioApiBaseUrl()}/gis/guide/audio/play-info?${params.toString()}`
const response = await requestJson<AudioPlayInfoResponse>(url)
if (response.code !== 0 || !response.data) {
throw new Error(response.msg || '语音播放解析失败')
}
response.data.playUrl = normalizeSameOriginPublicUrl(response.data.playUrl)
response.data.subtitleUrl = normalizeSameOriginPublicUrl(response.data.subtitleUrl)
if (response.data.playable || response.data.reason !== 'TARGET_NOT_FOUND') {
this.cache.set(key, response.data)
}
return response.data
}
clearCache(lang?: AudioLanguage) {
if (!lang) {
this.cache.clear()
return
}
Array.from(this.cache.keys()).forEach((key) => {
if (key.endsWith(`:${lang}`)) {
this.cache.delete(key)
}
})
}
}
export const audioPlayInfoRepository = new DefaultAudioPlayInfoRepository()