From e30d4ad950cf129b01d1f42a6c67d05423ded9b5 Mon Sep 17 00:00:00 2001
From: eric
Date: Mon, 16 Mar 2026 19:17:06 -0500
Subject: [PATCH] 's'
---
app/api/join/by-wechat/route.ts | 61 +++++++++++++++-----------
app/join/page.tsx | 76 ++++++++++++++++++++++++---------
2 files changed, 92 insertions(+), 45 deletions(-)
diff --git a/app/api/join/by-wechat/route.ts b/app/api/join/by-wechat/route.ts
index ddbd95e..2d96cc0 100644
--- a/app/api/join/by-wechat/route.ts
+++ b/app/api/join/by-wechat/route.ts
@@ -13,29 +13,10 @@ const empty = {
vip: false,
};
-/** 按 wechatId 查询 solanRed 记录,优先 salonapi */
-export async function GET(request: NextRequest) {
- const wechatId = request.nextUrl.searchParams.get("wechat_id")?.trim();
- if (!wechatId) {
- return NextResponse.json(empty);
- }
-
- try {
- const { status, data } = await getSalonApi(
- request,
- `/api/salon/join/by-wechat?wechat_id=${encodeURIComponent(wechatId)}`,
- { timeoutMs: 8000 }
- );
- if (status === 200 && data && typeof data === "object") {
- return NextResponse.json(data);
- }
- } catch {
- // salonapi 不可用,回退到直连 PocketBase
- }
-
+async function getPocketBaseJoinByWechat(wechatId: string) {
const token = await getAdminToken();
if (!token) {
- return NextResponse.json(empty);
+ return null;
}
const pb = getPocketBaseConfig();
@@ -45,7 +26,7 @@ export async function GET(request: NextRequest) {
try {
const controller = new AbortController();
- const timeoutId = setTimeout(() => controller.abort(), 8000);
+ const timeoutId = setTimeout(() => controller.abort(), 4000);
const res = await fetch(
`${base}/api/collections/${pb.joinCollection}/records?filter=${filter}&sort=-created&perPage=1`,
{
@@ -54,7 +35,7 @@ export async function GET(request: NextRequest) {
signal: controller.signal,
}
).finally(() => clearTimeout(timeoutId));
- if (!res.ok) return NextResponse.json(empty);
+ if (!res.ok) return null;
const data = await res.json();
const items = Array.isArray(data?.items) ? data.items : [];
const rec = items[0] ?? null;
@@ -66,7 +47,7 @@ export async function GET(request: NextRequest) {
const isRejected = !!rec?.is_rejected;
const vip = !!rec?.vip;
- return NextResponse.json({
+ return {
hasRecord,
record: rec,
isComplete,
@@ -74,8 +55,36 @@ export async function GET(request: NextRequest) {
isApproved,
isRejected,
vip,
- });
+ };
} catch {
- return NextResponse.json(empty);
+ return null;
}
}
+
+/** 按 wechatId 查询 solanRed 记录。优先 salonapi(记录由其创建,线上部署时更可靠),失败时回退直连 PocketBase */
+export async function GET(request: NextRequest) {
+ const wechatId = request.nextUrl.searchParams.get("wechat_id")?.trim();
+ if (!wechatId) {
+ return NextResponse.json(empty);
+ }
+
+ try {
+ const { status, data } = await getSalonApi(
+ request,
+ `/api/salon/join/by-wechat?wechat_id=${encodeURIComponent(wechatId)}`,
+ { timeoutMs: 6000 }
+ );
+ if (status === 200 && data && typeof data === "object") {
+ return NextResponse.json(data);
+ }
+ } catch {
+ /* salonapi 不可用,回退直连 PocketBase */
+ }
+
+ const pbData = await getPocketBaseJoinByWechat(wechatId);
+ if (pbData) {
+ return NextResponse.json(pbData);
+ }
+
+ return NextResponse.json(empty);
+}
diff --git a/app/join/page.tsx b/app/join/page.tsx
index e88dfaa..fd7b44c 100644
--- a/app/join/page.tsx
+++ b/app/join/page.tsx
@@ -77,6 +77,28 @@ interface UserRecord {
vip: boolean;
}
+const EMPTY_USER_RECORD: UserRecord = {
+ hasRecord: false,
+ record: null,
+ isComplete: false,
+ isWechatFriend: false,
+ isApproved: false,
+ isRejected: false,
+ vip: false,
+};
+
+function toUserRecord(data: Partial & { record?: UserRecord["record"] }): UserRecord {
+ return {
+ hasRecord: !!data.hasRecord,
+ record: data.record ?? null,
+ isComplete: !!data.isComplete,
+ isWechatFriend: !!data.isWechatFriend,
+ isApproved: !!data.isApproved,
+ isRejected: !!data.isRejected,
+ vip: !!data.vip,
+ };
+}
+
function UserProfileCard({ record, showVolunteerTag }: { record: UserRecord["record"]; showVolunteerTag?: boolean }) {
if (!record || (!record.nickname && !record.media && !record.wechatId)) return null;
const isVolunteer = showVolunteerTag && record.is_volunteer;
@@ -146,23 +168,20 @@ export default function JoinPage() {
const SUBMIT_THROTTLE_MS = 3000;
const fetchByWechat = async (w: string) => {
- if (!w.trim()) return;
+ if (!w.trim()) return null;
setError(null);
setCheckingWechat(true);
try {
- const res = await fetch(`/api/join/by-wechat?wechat_id=${encodeURIComponent(w.trim())}`);
- const data = await res.json();
- setUserRecord({
- hasRecord: !!data.hasRecord,
- record: data.record ?? null,
- isComplete: !!data.isComplete,
- isWechatFriend: !!data.isWechatFriend,
- isApproved: !!data.isApproved,
- isRejected: !!data.isRejected,
- vip: !!data.vip,
+ const res = await fetch(`/api/join/by-wechat?wechat_id=${encodeURIComponent(w.trim())}`, {
+ cache: "no-store",
});
+ const data = await res.json();
+ const nextRecord = toUserRecord(data);
+ setUserRecord(nextRecord);
+ return nextRecord;
} catch {
- setUserRecord({ hasRecord: false, record: null, isComplete: false, isWechatFriend: false, isApproved: false, isRejected: false, vip: false });
+ setUserRecord(EMPTY_USER_RECORD);
+ return null;
} finally {
setCheckingWechat(false);
}
@@ -331,13 +350,23 @@ export default function JoinPage() {
setQualify(qualify);
setSubmitted(true);
try {
- await fetchByWechat(wechat.trim());
+ localStorage.setItem(WECHAT_STORAGE_KEY, wechat.trim());
} catch {
+ /* ignore */
+ }
+ let result = await fetchByWechat(wechat.trim());
+ const maxRetries = 5;
+ const retryDelayMs = 1500;
+ for (let i = 0; i < maxRetries && !result?.hasRecord; i++) {
+ await new Promise((r) => setTimeout(r, retryDelayMs));
+ result = await fetchByWechat(wechat.trim());
+ }
+ if (!result?.hasRecord) {
try {
- localStorage.setItem(WECHAT_STORAGE_KEY, wechat.trim());
- } catch {}
- window.location.replace("/join");
- return;
+ window.location.replace("/join");
+ } catch {
+ /* ignore */
+ }
}
} catch (err) {
setError(err instanceof Error ? err.message : "网络错误,请重试");
@@ -618,6 +647,12 @@ export default function JoinPage() {
用于防止临时爽约
)}
+ {paying && (
+
+
+ 正在跳转支付,请稍候…
+
+ )}