feat: 新增「喝什么」场景,支持奶茶/咖啡/酒吧等饮品店搜索

引入场景系统(SceneType),首页增加「吃什么」「喝什么」切换 Tab,
不同场景使用不同的高德 POI 类型、热门标签、价格区间和全链路文案。
场景信息存储在房间数据中,邀请/分享/匹配结果等页面自动适配。
This commit is contained in:
2026-02-25 01:12:44 +08:00
parent 6866b70278
commit c86a6c0909
13 changed files with 197 additions and 47 deletions
+20 -11
View File
@@ -1,6 +1,7 @@
import { NextResponse } from "next/server";
import { createRoom } from "@/lib/store";
import { Restaurant } from "@/types";
import { Restaurant, SceneType } from "@/types";
import { getSceneConfig } from "@/lib/sceneConfig";
interface AmapPoiV5 {
id: string;
@@ -20,9 +21,6 @@ interface AmapPoiV5 {
photos?: { url: string }[];
}
const DEFAULT_IMAGE =
"https://images.unsplash.com/photo-1517248135467-4c7edcad34c4?w=800&q=80";
function extractCategory(type?: string): string {
if (!type) return "";
const parts = type.split(";");
@@ -35,7 +33,7 @@ function cleanField(val: unknown): string {
return String(val);
}
function mapPoiToRestaurant(poi: AmapPoiV5): Restaurant {
function mapPoiToRestaurant(poi: AmapPoiV5, defaultImage: string): Restaurant {
const ratingStr = poi.business?.rating;
const rating =
ratingStr && ratingStr !== "[]" ? parseFloat(ratingStr) || 4.0 : 4.0;
@@ -47,7 +45,7 @@ function mapPoiToRestaurant(poi: AmapPoiV5): Restaurant {
const image =
poi.photos && poi.photos.length > 0 && poi.photos[0].url
? poi.photos[0].url
: DEFAULT_IMAGE;
: defaultImage;
const openTime =
cleanField(poi.business?.opentime_week) ||
@@ -81,10 +79,16 @@ function filterByPrice(
if (isNaN(cost)) return true;
switch (priceRange) {
case "under20":
return cost < 20;
case "under50":
return cost < 50;
case "20to50":
return cost >= 20 && cost <= 50;
case "50to100":
return cost >= 50 && cost <= 100;
case "over50":
return cost > 50;
case "over100":
return cost > 100;
default:
@@ -103,8 +107,11 @@ export async function POST(req: Request) {
priceRange = "any",
cuisine = "不限",
userId = "",
scene = "eat" as SceneType,
} = body;
const sceneConfig = getSceneConfig(scene === "drink" ? "drink" : "eat");
if (!lat || !lng) {
return NextResponse.json(
{ error: "无法获取位置信息,请允许定位权限后重试" },
@@ -125,7 +132,7 @@ export async function POST(req: Request) {
url.searchParams.set("key", apiKey);
url.searchParams.set("location", `${lng},${lat}`);
url.searchParams.set("radius", String(radius));
url.searchParams.set("types", "050000");
url.searchParams.set("types", sceneConfig.poiTypes);
url.searchParams.set("show_fields", "business,photos");
url.searchParams.set("sortrule", "weight");
@@ -141,24 +148,26 @@ export async function POST(req: Request) {
let restaurants: Restaurant[] = [];
if (amapData.status === "1" && amapData.pois?.length > 0) {
let results: Restaurant[] = amapData.pois.map(mapPoiToRestaurant);
let results: Restaurant[] = amapData.pois.map(
(poi: AmapPoiV5) => mapPoiToRestaurant(poi, sceneConfig.defaultImage),
);
results = filterByPrice(results, priceRange);
restaurants = results.slice(0, 15);
}
if (restaurants.length === 0) {
return NextResponse.json(
{ error: "附近没有找到餐厅,试试扩大搜索范围或换个菜系" },
{ error: sceneConfig.emptyError },
{ status: 404 },
);
}
const roomId = await createRoom(restaurants, userId);
const roomId = await createRoom(restaurants, userId, sceneConfig.key);
return NextResponse.json({ roomId, restaurants });
} catch (e) {
console.error("Failed to create room:", e);
return NextResponse.json(
{ error: "搜索餐厅失败,请检查网络后重试" },
{ error: "搜索失败,请检查网络后重试" },
{ status: 500 },
);
}