forked from zhouruizhe/gmTouringMiniApp
Initial commit: gmTouringMiniApp project
This commit is contained in:
@@ -0,0 +1,158 @@
|
||||
import { getPoiRepository } from '@/data/poi'
|
||||
import {
|
||||
adjustLocalPlan,
|
||||
clonePlanResponse,
|
||||
cloneTravelPreferences,
|
||||
createLocalPlan,
|
||||
createRecommendedPlan,
|
||||
normalizePlanResponse,
|
||||
normalizePlanningRequest,
|
||||
normalizeTravelPreferences,
|
||||
type PlanningOrigin,
|
||||
type PlanningRequest,
|
||||
type PlanResponse,
|
||||
type TravelPreferences,
|
||||
TravelValidationError,
|
||||
} from '@/domain/travel'
|
||||
import { createRemoteRecommendations, isRemoteTravelAssistantEnabled } from './remote'
|
||||
import { loadPlan, loadPreferences, savePreferences } from './storage'
|
||||
|
||||
interface LocalSession {
|
||||
plan: PlanResponse
|
||||
preferences: TravelPreferences
|
||||
request: PlanningRequest
|
||||
planningOrigin: PlanningOrigin | null
|
||||
}
|
||||
|
||||
const localSessions = new Map<string, LocalSession>()
|
||||
|
||||
function clonePlanningRequest(request: PlanningRequest, includeOrigin = true): PlanningRequest {
|
||||
return {
|
||||
mode: request.mode,
|
||||
preferences: cloneTravelPreferences(request.preferences),
|
||||
durationMinutes: request.durationMinutes,
|
||||
selectedPoiIds: [...request.selectedPoiIds],
|
||||
...(includeOrigin && request.origin ? { origin: { ...request.origin } } : {}),
|
||||
}
|
||||
}
|
||||
|
||||
function rememberLocalSession(plan: PlanResponse, request: PlanningRequest): void {
|
||||
const repository = getPoiRepository()
|
||||
localSessions.set(plan.conversationId, {
|
||||
plan: clonePlanResponse(plan, repository),
|
||||
preferences: cloneTravelPreferences(request.preferences),
|
||||
request: clonePlanningRequest(request),
|
||||
planningOrigin: request.origin ? { ...request.origin } : null,
|
||||
})
|
||||
}
|
||||
|
||||
function restoreLocalSession(conversationId: string): LocalSession | null {
|
||||
const remembered = localSessions.get(conversationId)
|
||||
if (remembered)
|
||||
return remembered
|
||||
|
||||
const plan = loadPlan()
|
||||
const preferences = loadPreferences()
|
||||
if (!plan || !preferences || plan.conversationId !== conversationId)
|
||||
return null
|
||||
rememberLocalSession(plan, {
|
||||
mode: plan.itinerary.planningMode,
|
||||
preferences,
|
||||
durationMinutes: plan.itinerary.requestedMinutes,
|
||||
selectedPoiIds: plan.itinerary.planningMode === 'custom'
|
||||
? plan.itinerary.items.map(item => item.placeId)
|
||||
: [],
|
||||
})
|
||||
return localSessions.get(conversationId) ?? null
|
||||
}
|
||||
|
||||
function normalizePlanningOrigin(origin?: PlanningOrigin): PlanningOrigin | null {
|
||||
if (!origin
|
||||
|| origin.coordinateSystem !== 'GCJ02'
|
||||
|| !Number.isFinite(origin.longitude)
|
||||
|| !Number.isFinite(origin.latitude)
|
||||
|| origin.longitude < -180
|
||||
|| origin.longitude > 180
|
||||
|| origin.latitude < -90
|
||||
|| origin.latitude > 90) {
|
||||
return null
|
||||
}
|
||||
return { ...origin }
|
||||
}
|
||||
|
||||
function isPlanningRequest(input: TravelPreferences | PlanningRequest): input is PlanningRequest {
|
||||
return typeof input === 'object' && input !== null && 'mode' in input && 'preferences' in input
|
||||
}
|
||||
|
||||
export function createPlan(input: PlanningRequest): Promise<PlanResponse>
|
||||
export function createPlan(input: TravelPreferences, planningOrigin?: PlanningOrigin): Promise<PlanResponse>
|
||||
export async function createPlan(
|
||||
input: TravelPreferences | PlanningRequest,
|
||||
planningOrigin?: PlanningOrigin,
|
||||
): Promise<PlanResponse> {
|
||||
const repository = getPoiRepository()
|
||||
const request = normalizePlanningRequest(isPlanningRequest(input)
|
||||
? input
|
||||
: {
|
||||
mode: 'quick',
|
||||
preferences: normalizeTravelPreferences(input),
|
||||
durationMinutes: input.duration === 'half_day' ? 240 : 480,
|
||||
selectedPoiIds: [],
|
||||
...(normalizePlanningOrigin(planningOrigin) ? { origin: normalizePlanningOrigin(planningOrigin)! } : {}),
|
||||
}, repository)
|
||||
if (isRemoteTravelAssistantEnabled()) {
|
||||
const candidates = repository.getPublishedPois().map(poi => ({
|
||||
id: poi.id,
|
||||
name: poi.name,
|
||||
categoryCode: poi.categoryCode,
|
||||
tagCodes: poi.tagCodes,
|
||||
summary: poi.summary,
|
||||
recommendationIndex: poi.recommendationIndex,
|
||||
}))
|
||||
const remote = await createRemoteRecommendations(request, candidates)
|
||||
const plan = createRecommendedPlan(
|
||||
request,
|
||||
remote.recommendations,
|
||||
repository,
|
||||
remote.assistantMessage,
|
||||
remote.generationMode === 'real' ? 'remote_ai' : 'remote_mock',
|
||||
)
|
||||
rememberLocalSession(plan, request)
|
||||
return clonePlanResponse(plan, repository)
|
||||
}
|
||||
|
||||
const plan = createLocalPlan(request, repository)
|
||||
rememberLocalSession(plan, request)
|
||||
return clonePlanResponse(plan, repository)
|
||||
}
|
||||
|
||||
export async function adjustPlan(conversationId: string, message: string): Promise<PlanResponse> {
|
||||
const normalizedConversationId = conversationId.trim()
|
||||
const normalizedMessage = message.trim()
|
||||
if (!normalizedConversationId)
|
||||
throw new TravelValidationError('会话 ID 不能为空')
|
||||
if (!normalizedMessage)
|
||||
throw new TravelValidationError('请填写需要调整的内容')
|
||||
if (normalizedMessage.length > 500)
|
||||
throw new TravelValidationError('调整内容不能超过 500 字')
|
||||
|
||||
const repository = getPoiRepository()
|
||||
const session = restoreLocalSession(normalizedConversationId)
|
||||
if (!session)
|
||||
throw new Error('本机行程会话不存在,请重新生成行程')
|
||||
const adjusted = adjustLocalPlan(
|
||||
session.plan,
|
||||
session.preferences,
|
||||
normalizedMessage,
|
||||
repository,
|
||||
session.planningOrigin ?? undefined,
|
||||
session.request,
|
||||
)
|
||||
if (session.plan.source !== 'local_poc') {
|
||||
adjusted.plan.assistantMessage = '已在本机按你的补充要求重新核算路线;如需 AI 重新选点,请返回规划页重新生成。'
|
||||
adjusted.plan.source = session.plan.source
|
||||
}
|
||||
rememberLocalSession(adjusted.plan, adjusted.request)
|
||||
savePreferences(adjusted.preferences)
|
||||
return clonePlanResponse(adjusted.plan, repository)
|
||||
}
|
||||
Reference in New Issue
Block a user