feat: add server selection and order flow after recommendation

- Add 'selecting' and 'ordering' status to ServerSession
- Add lastRecommendation field to store recommendation results
- Keep session alive after recommendation (don't delete immediately)
- Add selection pattern matching (1번, 첫번째, 1번 선택 등)
- Add order confirmation message with inline buttons
- Add server_order/server_cancel callback handlers
- Add ServerOrderKeyboardData type for button data

Flow: recommend → select number → confirm with buttons → order/cancel

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
kappa
2026-01-27 10:45:04 +09:00
parent e4ccff9f87
commit 0c3bfede7d
5 changed files with 297 additions and 11 deletions

View File

@@ -259,6 +259,51 @@ async function callCloudOrchestratorApi(
}
}
// 추천 데이터 조회 (포맷팅 없이 원본 반환)
export async function getRecommendationData(
args: {
tech_stack: string[];
expected_users: number;
use_case: string;
traffic_pattern?: string;
region_preference?: string[];
budget_limit?: number;
lang?: string;
},
env?: Env
): Promise<RecommendResponse | null> {
const { tech_stack, expected_users, use_case, traffic_pattern, region_preference, budget_limit, lang } = args;
// 언어 자동 감지
const detectedLang = lang || detectLanguage(use_case);
// CDN 캐시 히트율 추정
const cdnCacheHitRate = estimateCdnCacheHitRate(tech_stack, use_case);
// API 요청 body 구성
const requestBody: Record<string, unknown> = {
tech_stack,
expected_users,
use_case,
lang: detectedLang,
};
if (traffic_pattern) requestBody.traffic_pattern = traffic_pattern;
if (region_preference) requestBody.region_preference = region_preference;
if (budget_limit) requestBody.budget_limit = budget_limit;
if (cdnCacheHitRate !== null) requestBody.cdn_cache_hit_rate = cdnCacheHitRate;
// API 호출
const result = await callCloudOrchestratorApi('/api/recommend', 'POST', requestBody, env);
if (isErrorResult(result)) {
logger.error('추천 데이터 조회 실패', new Error(result.error));
return null;
}
return result as RecommendResponse;
}
// 서버 추천 결과 포맷팅 (필수 정보만)
// __DIRECT__ 마커로 AI 재해석 없이 바로 반환
function formatRecommendations(data: RecommendResponse): string {