feat: 新增「喝什么」场景,支持奶茶/咖啡/酒吧等饮品店搜索
引入场景系统(SceneType),首页增加「吃什么」「喝什么」切换 Tab, 不同场景使用不同的高德 POI 类型、热门标签、价格区间和全链路文案。 场景信息存储在房间数据中,邀请/分享/匹配结果等页面自动适配。
This commit is contained in:
@@ -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 },
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user