feat: 改进标签系统 — 新增品类/费用/强度/预约标签,timeSlot 参与选活动
- IdeaCategory 扩展 7→9,新增 experience(体验)和 nature(自然) - 替换 outdoor boolean 为 costLevel/intensity/needsBooking 三个高价值字段 - AI 标注 prompt 同步更新,行程规划新增强度交替、费用平衡、预约提醒原则 - selectIdeasForSlots 重写为四优先级:timeSlot+category > category > timeSlot > 任意 - 前端想法卡片展示费用/强度/预约标签
This commit is contained in:
+56
-21
@@ -21,24 +21,34 @@ interface TaggedIdea {
|
||||
estimatedMinutes: number;
|
||||
searchQuery: string;
|
||||
searchType: string;
|
||||
costLevel: string | null;
|
||||
intensity: string | null;
|
||||
needsBooking: boolean | null;
|
||||
}
|
||||
|
||||
const SLOT_CATEGORY_MAP: Record<string, string[]> = {
|
||||
morning: ["outdoor", "sports", "culture"],
|
||||
morning: ["outdoor", "nature", "sports", "culture"],
|
||||
lunch: ["dining"],
|
||||
afternoon: ["entertainment", "shopping", "relaxation", "outdoor", "culture"],
|
||||
afternoon: ["entertainment", "shopping", "relaxation", "outdoor", "culture", "experience"],
|
||||
dinner: ["dining"],
|
||||
evening: ["entertainment", "relaxation"],
|
||||
evening: ["entertainment", "relaxation", "experience"],
|
||||
};
|
||||
|
||||
function selectIdeasForSlots(ideas: TaggedIdea[], availableHours: number): TaggedIdea[] {
|
||||
const byCategory = new Map<string, TaggedIdea[]>();
|
||||
for (const idea of ideas) {
|
||||
const list = byCategory.get(idea.category) || [];
|
||||
list.push(idea);
|
||||
byCategory.set(idea.category, list);
|
||||
}
|
||||
const SLOT_TIME_MAP: Record<string, string[]> = {
|
||||
morning: ["morning", "flexible"],
|
||||
lunch: ["flexible"],
|
||||
afternoon: ["afternoon", "flexible"],
|
||||
dinner: ["evening", "flexible"],
|
||||
evening: ["evening", "flexible"],
|
||||
};
|
||||
|
||||
function matchesSlot(idea: TaggedIdea, slot: string): boolean {
|
||||
if (idea.timeSlot === "all_day") return true;
|
||||
const validTimes = SLOT_TIME_MAP[slot];
|
||||
return validTimes ? validTimes.includes(idea.timeSlot) : false;
|
||||
}
|
||||
|
||||
function selectIdeasForSlots(ideas: TaggedIdea[], availableHours: number): TaggedIdea[] {
|
||||
const slots: string[] = [];
|
||||
if (availableHours >= 10) {
|
||||
slots.push("morning", "lunch", "afternoon", "dinner", "evening");
|
||||
@@ -53,22 +63,41 @@ function selectIdeasForSlots(ideas: TaggedIdea[], availableHours: number): Tagge
|
||||
const selected: TaggedIdea[] = [];
|
||||
const usedIds = new Set<string>();
|
||||
|
||||
function pickRandom(pool: TaggedIdea[]): TaggedIdea | null {
|
||||
if (pool.length === 0) return null;
|
||||
return pool[Math.floor(Math.random() * pool.length)];
|
||||
}
|
||||
|
||||
for (const slot of slots) {
|
||||
const remaining = ideas.filter((i) => !usedIds.has(i.id));
|
||||
const preferredCategories = SLOT_CATEGORY_MAP[slot] || [];
|
||||
let picked: TaggedIdea | null = null;
|
||||
for (const cat of preferredCategories) {
|
||||
const pool = (byCategory.get(cat) || []).filter((i) => !usedIds.has(i.id));
|
||||
if (pool.length > 0) {
|
||||
picked = pool[Math.floor(Math.random() * pool.length)];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Priority 1: timeSlot + category both match
|
||||
const p1 = remaining.filter(
|
||||
(i) => matchesSlot(i, slot) && preferredCategories.includes(i.category),
|
||||
);
|
||||
let picked = pickRandom(p1);
|
||||
|
||||
// Priority 2: category match only (existing logic)
|
||||
if (!picked) {
|
||||
const remaining = ideas.filter((i) => !usedIds.has(i.id));
|
||||
if (remaining.length > 0) {
|
||||
picked = remaining[Math.floor(Math.random() * remaining.length)];
|
||||
for (const cat of preferredCategories) {
|
||||
const pool = remaining.filter((i) => i.category === cat);
|
||||
picked = pickRandom(pool);
|
||||
if (picked) break;
|
||||
}
|
||||
}
|
||||
|
||||
// Priority 3: timeSlot match only
|
||||
if (!picked) {
|
||||
const p3 = remaining.filter((i) => matchesSlot(i, slot));
|
||||
picked = pickRandom(p3);
|
||||
}
|
||||
|
||||
// Priority 4: any remaining
|
||||
if (!picked) {
|
||||
picked = pickRandom(remaining);
|
||||
}
|
||||
|
||||
if (picked) {
|
||||
selected.push(picked);
|
||||
usedIds.add(picked.id);
|
||||
@@ -170,6 +199,9 @@ export async function runPlanGeneration(
|
||||
estimatedMinutes: true,
|
||||
searchQuery: true,
|
||||
searchType: true,
|
||||
costLevel: true,
|
||||
intensity: true,
|
||||
needsBooking: true,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -272,6 +304,9 @@ export async function runPlanGeneration(
|
||||
estimatedMinutes: i.estimatedMinutes,
|
||||
searchQuery: i.searchQuery,
|
||||
searchType: i.searchType,
|
||||
costLevel: i.costLevel ?? undefined,
|
||||
intensity: i.intensity ?? undefined,
|
||||
needsBooking: i.needsBooking ?? undefined,
|
||||
})),
|
||||
candidates,
|
||||
userLocation: { lat: room.lat!, lng: room.lng! },
|
||||
|
||||
Reference in New Issue
Block a user