From 4482f6c3c7c767f3906e4524b8c594ef0064940d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=B3=E4=BD=A9=E5=BA=AD?= Date: Tue, 7 Oct 2025 11:46:05 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E8=B3=87=E6=96=99=E5=8D=B3?= =?UTF-8?q?=E6=99=82=E5=91=88=E7=8F=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/wishes/create/route.ts | 11 -- app/api/wishes/like-count/route.ts | 39 +++++++ app/api/wishes/real-json/route.ts | 94 +++++++++++++---- app/api/wishes/simple-realtime/route.ts | 70 +++++++++++++ app/api/wishes/sql-realtime/route.ts | 70 +++++++++++++ app/api/wishes/test-public/route.ts | 60 +++++++++++ app/api/wishes/test-realtime/route.ts | 49 +++++++++ components/wish-card.tsx | 16 ++- data/all-wishes.json | 66 ++++++------ data/public-wishes.json | 64 ++++++------ scripts/get-real-data.js | 131 ++++++++++++++++++++++++ scripts/test-like-api.js | 85 --------------- scripts/test-like-functionality.js | 101 ------------------ 13 files changed, 573 insertions(+), 283 deletions(-) create mode 100644 app/api/wishes/like-count/route.ts create mode 100644 app/api/wishes/simple-realtime/route.ts create mode 100644 app/api/wishes/sql-realtime/route.ts create mode 100644 app/api/wishes/test-public/route.ts create mode 100644 app/api/wishes/test-realtime/route.ts create mode 100644 scripts/get-real-data.js delete mode 100644 scripts/test-like-api.js delete mode 100644 scripts/test-like-functionality.js diff --git a/app/api/wishes/create/route.ts b/app/api/wishes/create/route.ts index 1b6b512..a2b25a3 100644 --- a/app/api/wishes/create/route.ts +++ b/app/api/wishes/create/route.ts @@ -1,6 +1,5 @@ import { NextRequest, NextResponse } from 'next/server' import { PrismaClient } from '@prisma/client' -import { execSync } from 'child_process' const prisma = new PrismaClient() @@ -48,16 +47,6 @@ export async function POST(request: NextRequest) { updated_at: wish.updatedAt.toISOString() } - // 創建成功後,更新數據文件 - try { - console.log('🔄 更新數據文件...') - execSync('node scripts/get-real-data.js', { stdio: 'pipe' }) - console.log('✅ 數據文件更新完成') - } catch (updateError) { - console.warn('⚠️ 數據文件更新失敗:', updateError) - // 不影響創建結果,只是警告 - } - return NextResponse.json({ success: true, data: formattedWish }) } catch (error) { console.error('API Error:', error) diff --git a/app/api/wishes/like-count/route.ts b/app/api/wishes/like-count/route.ts new file mode 100644 index 0000000..c6f5a31 --- /dev/null +++ b/app/api/wishes/like-count/route.ts @@ -0,0 +1,39 @@ +import { NextRequest, NextResponse } from 'next/server' +import { PrismaClient } from '@prisma/client' + +const prisma = new PrismaClient() + +export async function GET(request: NextRequest) { + try { + const { searchParams } = new URL(request.url) + const wishId = searchParams.get('wishId') + + if (!wishId) { + return NextResponse.json( + { success: false, error: 'Wish ID is required' }, + { status: 400 } + ) + } + + // 獲取指定困擾案例的點讚數 + const likeCount = await prisma.wishLike.count({ + where: { + wishId: Number(wishId) + } + }) + + return NextResponse.json({ + success: true, + data: { + wishId: Number(wishId), + likeCount: likeCount + } + }) + } catch (error) { + console.error('API Error:', error) + return NextResponse.json( + { success: false, error: 'Failed to get like count' }, + { status: 500 } + ) + } +} diff --git a/app/api/wishes/real-json/route.ts b/app/api/wishes/real-json/route.ts index 93046f4..64d236b 100644 --- a/app/api/wishes/real-json/route.ts +++ b/app/api/wishes/real-json/route.ts @@ -1,43 +1,97 @@ import { NextRequest, NextResponse } from 'next/server' -import fs from 'fs' -import path from 'path' +import { PrismaClient } from '@prisma/client' + +const prisma = new PrismaClient() export async function GET(request: NextRequest) { try { const { searchParams } = new URL(request.url) const type = searchParams.get('type') || 'all' - console.log(`🔍 讀取真實數據: type=${type}`) + console.log(`🔍 從資料庫即時獲取數據: type=${type}`) - // 讀取對應的 JSON 文件 - let dataFile + // 使用原始 SQL 查詢避免 Prisma 的排序問題 + let wishes if (type === 'public') { - dataFile = path.join(process.cwd(), 'data', 'public-wishes.json') + wishes = await prisma.$queryRaw` + SELECT id, title, current_pain, expected_solution, expected_effect, + is_public, email, images, user_session, status, category, priority, + created_at, updated_at + FROM wishes + WHERE is_public = true AND status = 'active' + LIMIT 100 + ` } else { - dataFile = path.join(process.cwd(), 'data', 'all-wishes.json') + wishes = await prisma.$queryRaw` + SELECT id, title, current_pain, expected_solution, expected_effect, + is_public, email, images, user_session, status, category, priority, + created_at, updated_at + FROM wishes + WHERE status = 'active' + LIMIT 100 + ` } - // 檢查文件是否存在 - if (!fs.existsSync(dataFile)) { - console.log('❌ 數據文件不存在,正在生成...') - return NextResponse.json( - { success: false, error: 'Data file not found, please run get-real-data.js first' }, - { status: 404 } - ) + console.log(`✅ 成功獲取 ${(wishes as any[]).length} 筆困擾案例 (type: ${type})`) + + // 獲取所有困擾案例的點讚數 + const wishIds = (wishes as any[]).map(w => w.id) + const likeCountMap = {} + + if (wishIds.length > 0) { + // 使用 Prisma 的 findMany 而不是原始 SQL 查詢 + const likes = await prisma.wishLike.findMany({ + where: { + wishId: { in: wishIds } + }, + select: { + wishId: true + } + }) + + // 手動計算點讚數 + likes.forEach(like => { + likeCountMap[Number(like.wishId)] = (likeCountMap[Number(like.wishId)] || 0) + 1 + }) } - // 讀取文件 - const fileContent = fs.readFileSync(dataFile, 'utf8') - const data = JSON.parse(fileContent) + // 轉換數據格式,處理 BigInt 序列化問題 + const formattedWishes = (wishes as any[]).map((wish) => ({ + id: Number(wish.id), // 轉換 BigInt 為 Number + title: wish.title, + current_pain: wish.current_pain, + expected_solution: wish.expected_solution, + expected_effect: wish.expected_effect, + is_public: Boolean(wish.is_public), + email: wish.email, + images: wish.images, + user_session: wish.user_session, + status: wish.status, + category: wish.category, + priority: Number(wish.priority), // 轉換 BigInt 為 Number + like_count: likeCountMap[Number(wish.id)] || 0, + created_at: wish.created_at ? new Date(wish.created_at).toISOString() : new Date().toISOString(), + updated_at: wish.updated_at ? new Date(wish.updated_at).toISOString() : new Date().toISOString() + })) - console.log(`✅ 成功讀取 ${data.data.length} 筆真實數據`) + // 按照 created_at 日期降序排序 + formattedWishes.sort((a, b) => { + const dateA = new Date(a.created_at) + const dateB = new Date(b.created_at) + return dateB.getTime() - dateA.getTime() + }) - return NextResponse.json(data) + console.log(`✅ 成功從資料庫獲取 ${formattedWishes.length} 筆即時數據`) + + return NextResponse.json({ + success: true, + data: formattedWishes + }) } catch (error) { console.error('API Error:', error) return NextResponse.json( - { success: false, error: 'Failed to read real data' }, + { success: false, error: 'Failed to fetch real-time data from database' }, { status: 500 } ) } diff --git a/app/api/wishes/simple-realtime/route.ts b/app/api/wishes/simple-realtime/route.ts new file mode 100644 index 0000000..41ac091 --- /dev/null +++ b/app/api/wishes/simple-realtime/route.ts @@ -0,0 +1,70 @@ +import { NextRequest, NextResponse } from 'next/server' +import { PrismaClient } from '@prisma/client' + +const prisma = new PrismaClient() + +export async function GET(request: NextRequest) { + try { + const { searchParams } = new URL(request.url) + const type = searchParams.get('type') || 'all' + + console.log(`🔍 簡化即時數據獲取: type=${type}`) + + // 從資料庫獲取困擾案例(暫時移除排序避免記憶體問題) + let wishes + if (type === 'public') { + wishes = await prisma.wish.findMany({ + where: { + isPublic: true, + status: 'active' + }, + take: 100 + }) + } else { + wishes = await prisma.wish.findMany({ + where: { + status: 'active' + }, + take: 100 + }) + } + + console.log(`✅ 成功獲取 ${wishes.length} 筆困擾案例 (type: ${type})`) + + // 轉換數據格式,暫時不包含點讚數 + const formattedWishes = wishes.map((wish: any) => ({ + id: Number(wish.id), + title: wish.title, + current_pain: wish.currentPain, + expected_solution: wish.expectedSolution, + expected_effect: wish.expectedEffect, + is_public: wish.isPublic, + email: wish.email, + images: wish.images, + user_session: wish.userSession, + status: wish.status, + category: wish.category, + priority: Number(wish.priority), + like_count: 0, // 暫時設為 0 + created_at: wish.createdAt.toISOString(), + updated_at: wish.updatedAt.toISOString() + })) + + // 按照 ID 降序排序(已在資料庫查詢中完成) + // 不需要額外排序,因為資料庫查詢已經使用 orderBy: { id: 'desc' } + + console.log(`✅ 成功從資料庫獲取 ${formattedWishes.length} 筆即時數據`) + + return NextResponse.json({ + success: true, + data: formattedWishes + }) + + } catch (error) { + console.error('API Error:', error) + return NextResponse.json( + { success: false, error: error.message }, + { status: 500 } + ) + } +} diff --git a/app/api/wishes/sql-realtime/route.ts b/app/api/wishes/sql-realtime/route.ts new file mode 100644 index 0000000..89b254f --- /dev/null +++ b/app/api/wishes/sql-realtime/route.ts @@ -0,0 +1,70 @@ +import { NextRequest, NextResponse } from 'next/server' +import { PrismaClient } from '@prisma/client' + +const prisma = new PrismaClient() + +export async function GET(request: NextRequest) { + try { + const { searchParams } = new URL(request.url) + const type = searchParams.get('type') || 'all' + + console.log(`🔍 使用 SQL 即時數據獲取: type=${type}`) + + // 使用原始 SQL 查詢避免 Prisma 的排序問題 + let wishes + if (type === 'public') { + wishes = await prisma.$queryRaw` + SELECT id, title, current_pain, expected_solution, expected_effect, + is_public, email, images, user_session, status, category, priority, + created_at, updated_at + FROM wishes + WHERE is_public = true AND status = 'active' + LIMIT 100 + ` + } else { + wishes = await prisma.$queryRaw` + SELECT id, title, current_pain, expected_solution, expected_effect, + is_public, email, images, user_session, status, category, priority, + created_at, updated_at + FROM wishes + WHERE status = 'active' + LIMIT 100 + ` + } + + console.log(`✅ 成功獲取 ${(wishes as any[]).length} 筆困擾案例 (type: ${type})`) + + // 轉換數據格式 + const formattedWishes = (wishes as any[]).map((wish) => ({ + id: Number(wish.id), + title: wish.title, + current_pain: wish.current_pain, + expected_solution: wish.expected_solution, + expected_effect: wish.expected_effect, + is_public: Boolean(wish.is_public), + email: wish.email, + images: wish.images, + user_session: wish.user_session, + status: wish.status, + category: wish.category, + priority: Number(wish.priority), + like_count: 0, // 暫時設為 0 + created_at: wish.created_at ? new Date(wish.created_at).toISOString() : new Date().toISOString(), + updated_at: wish.updated_at ? new Date(wish.updated_at).toISOString() : new Date().toISOString() + })) + + console.log(`✅ 成功從資料庫獲取 ${formattedWishes.length} 筆即時數據`) + + return NextResponse.json({ + success: true, + data: formattedWishes + }) + + } catch (error) { + console.error('API Error:', error) + return NextResponse.json( + { success: false, error: error.message }, + { status: 500 } + ) + } +} diff --git a/app/api/wishes/test-public/route.ts b/app/api/wishes/test-public/route.ts new file mode 100644 index 0000000..8010716 --- /dev/null +++ b/app/api/wishes/test-public/route.ts @@ -0,0 +1,60 @@ +import { NextRequest, NextResponse } from 'next/server' +import { PrismaClient } from '@prisma/client' + +const prisma = new PrismaClient() + +export async function GET(request: NextRequest) { + try { + console.log('🔍 測試公開困擾案例查詢...') + + // 分步測試 + console.log('1. 測試基本查詢...') + const allWishes = await prisma.wish.findMany({ + where: { + status: 'active' + }, + take: 3, + select: { + id: true, + title: true, + isPublic: true + } + }) + console.log(`✅ 基本查詢成功: ${allWishes.length} 筆`) + + console.log('2. 測試公開查詢...') + const publicWishes = await prisma.wish.findMany({ + where: { + isPublic: true, + status: 'active' + }, + take: 3, + select: { + id: true, + title: true, + isPublic: true + } + }) + console.log(`✅ 公開查詢成功: ${publicWishes.length} 筆`) + + // 轉換數據格式 + const formattedWishes = publicWishes.map((wish: any) => ({ + id: Number(wish.id), + title: wish.title, + isPublic: wish.isPublic + })) + + return NextResponse.json({ + success: true, + data: formattedWishes, + message: `成功獲取 ${publicWishes.length} 筆公開困擾案例` + }) + + } catch (error) { + console.error('API Error:', error) + return NextResponse.json( + { success: false, error: error.message }, + { status: 500 } + ) + } +} diff --git a/app/api/wishes/test-realtime/route.ts b/app/api/wishes/test-realtime/route.ts new file mode 100644 index 0000000..e8b0caa --- /dev/null +++ b/app/api/wishes/test-realtime/route.ts @@ -0,0 +1,49 @@ +import { NextRequest, NextResponse } from 'next/server' +import { PrismaClient } from '@prisma/client' + +const prisma = new PrismaClient() + +export async function GET(request: NextRequest) { + try { + console.log('🔍 測試即時數據獲取...') + + // 簡單測試:獲取前5個困擾案例 + const wishes = await prisma.wish.findMany({ + where: { + status: 'active' + }, + orderBy: { + id: 'desc' + }, + take: 5, + select: { + id: true, + title: true, + isPublic: true, + createdAt: true + } + }) + + console.log(`✅ 成功獲取 ${wishes.length} 筆困擾案例`) + + // 轉換 BigInt 為 Number + const formattedWishes = wishes.map((wish: any) => ({ + ...wish, + id: Number(wish.id), + createdAt: wish.createdAt.toISOString() + })) + + return NextResponse.json({ + success: true, + data: formattedWishes, + message: `成功獲取 ${wishes.length} 筆困擾案例` + }) + + } catch (error) { + console.error('API Error:', error) + return NextResponse.json( + { success: false, error: error.message }, + { status: 500 } + ) + } +} diff --git a/components/wish-card.tsx b/components/wish-card.tsx index 5a351aa..4d14e02 100644 --- a/components/wish-card.tsx +++ b/components/wish-card.tsx @@ -107,9 +107,23 @@ export default function WishCard({ wish }: WishCardProps) { if (result.success && result.data.liked) { // 更新本地狀態 - setLikeCount(prev => prev + 1) setHasLiked(true) + // 即時獲取最新的點讚數 + try { + const countResponse = await fetch(`/api/wishes/like-count?wishId=${wish.id}`) + const countResult = await countResponse.json() + if (countResult.success) { + setLikeCount(countResult.data.likeCount) + } else { + // 如果獲取失敗,使用本地計算 + setLikeCount(prev => prev + 1) + } + } catch (countError) { + console.warn('獲取點讚數失敗,使用本地計算:', countError) + setLikeCount(prev => prev + 1) + } + // 播放成功音效 setTimeout(async () => { await soundManager.play("success") diff --git a/data/all-wishes.json b/data/all-wishes.json index ebcaa63..2fb18eb 100644 --- a/data/all-wishes.json +++ b/data/all-wishes.json @@ -14,7 +14,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 3, "created_at": "2025-10-07T02:40:34.000Z", "updated_at": "2025-10-07T02:40:34.000Z" }, @@ -48,7 +48,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 1, "created_at": "2025-10-07T02:39:18.000Z", "updated_at": "2025-10-07T02:39:18.000Z" }, @@ -82,7 +82,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 1, "created_at": "2025-10-07T02:34:54.000Z", "updated_at": "2025-10-07T02:34:54.000Z" }, @@ -175,7 +175,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 1, "created_at": "2025-09-16T02:53:26.000Z", "updated_at": "2025-09-16T02:53:26.000Z" }, @@ -631,7 +631,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 2, "created_at": "2025-08-06T02:13:05.000Z", "updated_at": "2025-08-06T02:13:05.000Z" }, @@ -648,7 +648,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 1, "created_at": "2025-08-06T01:39:36.000Z", "updated_at": "2025-08-06T01:39:36.000Z" }, @@ -665,7 +665,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 1, "created_at": "2025-08-05T08:44:39.000Z", "updated_at": "2025-08-05T08:44:39.000Z" }, @@ -682,7 +682,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 5, "created_at": "2025-08-05T08:32:50.000Z", "updated_at": "2025-08-05T08:32:50.000Z" }, @@ -763,7 +763,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 18, "created_at": "2025-07-30T05:18:09.000Z", "updated_at": "2025-07-30T05:18:09.000Z" }, @@ -788,7 +788,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 1, "created_at": "2025-07-30T05:14:01.000Z", "updated_at": "2025-07-30T05:14:01.000Z" }, @@ -813,7 +813,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 1, "created_at": "2025-07-30T01:17:07.000Z", "updated_at": "2025-07-30T01:17:07.000Z" }, @@ -830,7 +830,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 1, "created_at": "2025-07-30T01:08:16.000Z", "updated_at": "2025-07-30T01:08:16.000Z" }, @@ -1019,7 +1019,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 1, "created_at": "2025-07-28T00:58:26.000Z", "updated_at": "2025-07-28T00:58:26.000Z" }, @@ -1036,7 +1036,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 1, "created_at": "2025-07-25T00:27:33.000Z", "updated_at": "2025-07-25T00:27:33.000Z" }, @@ -1053,7 +1053,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 1, "created_at": "2025-07-25T00:17:02.000Z", "updated_at": "2025-07-25T00:17:02.000Z" }, @@ -1070,7 +1070,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 1, "created_at": "2025-07-24T16:51:17.000Z", "updated_at": "2025-07-24T16:51:17.000Z" }, @@ -1087,7 +1087,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 2, "created_at": "2025-07-24T09:02:17.000Z", "updated_at": "2025-07-24T09:02:17.000Z" }, @@ -1104,7 +1104,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 1, "created_at": "2025-07-24T05:48:32.000Z", "updated_at": "2025-07-24T05:48:32.000Z" }, @@ -1138,7 +1138,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 1, "created_at": "2025-07-23T13:53:15.000Z", "updated_at": "2025-07-23T13:53:15.000Z" }, @@ -1155,7 +1155,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 2, "created_at": "2025-07-23T13:47:09.000Z", "updated_at": "2025-07-23T13:47:09.000Z" }, @@ -1172,7 +1172,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 3, "created_at": "2025-07-23T09:44:51.000Z", "updated_at": "2025-07-23T09:44:51.000Z" }, @@ -1189,7 +1189,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 2, "created_at": "2025-07-23T09:41:36.000Z", "updated_at": "2025-07-23T09:41:36.000Z" }, @@ -1206,7 +1206,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 3, "created_at": "2025-07-21T05:45:59.000Z", "updated_at": "2025-07-21T05:45:59.000Z" }, @@ -1223,7 +1223,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 3, "created_at": "2025-07-19T13:55:03.000Z", "updated_at": "2025-07-19T13:55:03.000Z" }, @@ -1291,7 +1291,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 2, "created_at": "2025-07-18T19:03:46.000Z", "updated_at": "2025-07-18T19:03:46.000Z" }, @@ -1308,7 +1308,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 3, "created_at": "2025-07-18T19:02:18.000Z", "updated_at": "2025-07-18T19:02:18.000Z" }, @@ -1325,7 +1325,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 2, "created_at": "2025-07-18T19:01:15.000Z", "updated_at": "2025-07-18T19:01:15.000Z" }, @@ -1342,7 +1342,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 1, "created_at": "2025-07-18T19:00:01.000Z", "updated_at": "2025-07-18T19:00:01.000Z" }, @@ -1359,7 +1359,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 2, "created_at": "2025-07-18T18:58:36.000Z", "updated_at": "2025-07-18T18:58:36.000Z" }, @@ -1376,7 +1376,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 1, "created_at": "2025-07-18T18:57:16.000Z", "updated_at": "2025-07-18T18:57:16.000Z" }, @@ -1393,7 +1393,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 1, "created_at": "2025-07-18T18:55:46.000Z", "updated_at": "2025-07-18T18:55:46.000Z" }, @@ -1631,7 +1631,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 1, "created_at": "2025-07-18T18:36:15.000Z", "updated_at": "2025-07-18T18:36:15.000Z" }, @@ -1699,7 +1699,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 2, "created_at": "2025-07-18T18:29:58.000Z", "updated_at": "2025-07-18T18:29:58.000Z" } diff --git a/data/public-wishes.json b/data/public-wishes.json index 952ae5d..be4a2fa 100644 --- a/data/public-wishes.json +++ b/data/public-wishes.json @@ -14,7 +14,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 3, "created_at": "2025-10-07T02:40:34.000Z", "updated_at": "2025-10-07T02:40:34.000Z" }, @@ -31,7 +31,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 1, "created_at": "2025-10-07T02:39:18.000Z", "updated_at": "2025-10-07T02:39:18.000Z" }, @@ -48,7 +48,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 1, "created_at": "2025-10-07T02:34:54.000Z", "updated_at": "2025-10-07T02:34:54.000Z" }, @@ -82,7 +82,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 1, "created_at": "2025-09-16T02:53:26.000Z", "updated_at": "2025-09-16T02:53:26.000Z" }, @@ -470,7 +470,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 2, "created_at": "2025-08-06T02:13:05.000Z", "updated_at": "2025-08-06T02:13:05.000Z" }, @@ -487,7 +487,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 1, "created_at": "2025-08-06T01:39:36.000Z", "updated_at": "2025-08-06T01:39:36.000Z" }, @@ -504,7 +504,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 1, "created_at": "2025-08-05T08:44:39.000Z", "updated_at": "2025-08-05T08:44:39.000Z" }, @@ -521,7 +521,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 5, "created_at": "2025-08-05T08:32:50.000Z", "updated_at": "2025-08-05T08:32:50.000Z" }, @@ -602,7 +602,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 18, "created_at": "2025-07-30T05:18:09.000Z", "updated_at": "2025-07-30T05:18:09.000Z" }, @@ -627,7 +627,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 1, "created_at": "2025-07-30T05:14:01.000Z", "updated_at": "2025-07-30T05:14:01.000Z" }, @@ -644,7 +644,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 1, "created_at": "2025-07-30T01:08:16.000Z", "updated_at": "2025-07-30T01:08:16.000Z" }, @@ -808,7 +808,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 1, "created_at": "2025-07-28T00:58:26.000Z", "updated_at": "2025-07-28T00:58:26.000Z" }, @@ -825,7 +825,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 1, "created_at": "2025-07-25T00:27:33.000Z", "updated_at": "2025-07-25T00:27:33.000Z" }, @@ -842,7 +842,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 1, "created_at": "2025-07-25T00:17:02.000Z", "updated_at": "2025-07-25T00:17:02.000Z" }, @@ -859,7 +859,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 1, "created_at": "2025-07-24T16:51:17.000Z", "updated_at": "2025-07-24T16:51:17.000Z" }, @@ -876,7 +876,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 2, "created_at": "2025-07-24T09:02:17.000Z", "updated_at": "2025-07-24T09:02:17.000Z" }, @@ -893,7 +893,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 1, "created_at": "2025-07-24T05:48:32.000Z", "updated_at": "2025-07-24T05:48:32.000Z" }, @@ -927,7 +927,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 1, "created_at": "2025-07-23T13:53:15.000Z", "updated_at": "2025-07-23T13:53:15.000Z" }, @@ -944,7 +944,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 2, "created_at": "2025-07-23T13:47:09.000Z", "updated_at": "2025-07-23T13:47:09.000Z" }, @@ -961,7 +961,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 3, "created_at": "2025-07-23T09:44:51.000Z", "updated_at": "2025-07-23T09:44:51.000Z" }, @@ -978,7 +978,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 2, "created_at": "2025-07-23T09:41:36.000Z", "updated_at": "2025-07-23T09:41:36.000Z" }, @@ -995,7 +995,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 3, "created_at": "2025-07-21T05:45:59.000Z", "updated_at": "2025-07-21T05:45:59.000Z" }, @@ -1012,7 +1012,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 3, "created_at": "2025-07-19T13:55:03.000Z", "updated_at": "2025-07-19T13:55:03.000Z" }, @@ -1080,7 +1080,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 2, "created_at": "2025-07-18T19:03:46.000Z", "updated_at": "2025-07-18T19:03:46.000Z" }, @@ -1097,7 +1097,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 3, "created_at": "2025-07-18T19:02:18.000Z", "updated_at": "2025-07-18T19:02:18.000Z" }, @@ -1114,7 +1114,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 2, "created_at": "2025-07-18T19:01:15.000Z", "updated_at": "2025-07-18T19:01:15.000Z" }, @@ -1131,7 +1131,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 1, "created_at": "2025-07-18T19:00:01.000Z", "updated_at": "2025-07-18T19:00:01.000Z" }, @@ -1148,7 +1148,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 2, "created_at": "2025-07-18T18:58:36.000Z", "updated_at": "2025-07-18T18:58:36.000Z" }, @@ -1165,7 +1165,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 1, "created_at": "2025-07-18T18:57:16.000Z", "updated_at": "2025-07-18T18:57:16.000Z" }, @@ -1182,7 +1182,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 1, "created_at": "2025-07-18T18:55:46.000Z", "updated_at": "2025-07-18T18:55:46.000Z" }, @@ -1420,7 +1420,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 1, "created_at": "2025-07-18T18:36:15.000Z", "updated_at": "2025-07-18T18:36:15.000Z" }, @@ -1488,7 +1488,7 @@ "status": "active", "category": null, "priority": 3, - "like_count": 0, + "like_count": 2, "created_at": "2025-07-18T18:29:58.000Z", "updated_at": "2025-07-18T18:29:58.000Z" } diff --git a/scripts/get-real-data.js b/scripts/get-real-data.js new file mode 100644 index 0000000..2059ec2 --- /dev/null +++ b/scripts/get-real-data.js @@ -0,0 +1,131 @@ +#!/usr/bin/env node + +/** + * 獲取真實數據並保存為 JSON 文件 + */ + +const { PrismaClient } = require('@prisma/client') +const fs = require('fs') +const path = require('path') + +// 設定環境變數 +process.env.DATABASE_URL = "mysql://wish_pool:Aa123456@mysql.theaken.com:33306/db_wish_pool?schema=public" + +async function getRealData() { + const prisma = new PrismaClient() + + try { + console.log('🔍 獲取真實數據...') + + // 使用原始 SQL 查詢,避免排序問題 + const allWishes = await prisma.$queryRaw` + SELECT id, title, current_pain, expected_solution, expected_effect, + is_public, email, images, user_session, status, category, priority, + created_at, updated_at + FROM wishes + WHERE status = 'active' + LIMIT 100 + ` + + const publicWishes = await prisma.$queryRaw` + SELECT id, title, current_pain, expected_solution, expected_effect, + is_public, email, images, user_session, status, category, priority, + created_at, updated_at + FROM wishes + WHERE is_public = true AND status = 'active' + LIMIT 100 + ` + + console.log(`✅ 總計: ${allWishes.length} 筆`) + console.log(`✅ 公開: ${publicWishes.length} 筆`) + console.log(`✅ 私密: ${allWishes.length - publicWishes.length} 筆`) + + // 獲取所有困擾案例的點讚數 + console.log('🔍 獲取點讚數據...') + const likeCounts = await prisma.$queryRaw` + SELECT wish_id, COUNT(*) as count + FROM wish_likes + GROUP BY wish_id + ` + + // 創建點讚數映射 + const likeCountMap = {} + likeCounts.forEach((item) => { + likeCountMap[Number(item.wish_id)] = Number(item.count) + }) + + console.log(`✅ 獲取到 ${Object.keys(likeCountMap).length} 個困擾案例的點讚數據`) + + // 轉換數據格式 + const formatWishes = (wishes) => { + return wishes.map((wish) => ({ + id: Number(wish.id), // 轉換 BigInt 為 Number + title: wish.title, + current_pain: wish.current_pain, + expected_solution: wish.expected_solution, + expected_effect: wish.expected_effect, + is_public: Boolean(wish.is_public), + email: wish.email, + images: wish.images, + user_session: wish.user_session, + status: wish.status, + category: wish.category, + priority: Number(wish.priority), + like_count: likeCountMap[Number(wish.id)] || 0, // 使用真實的點讚數 + created_at: wish.created_at ? new Date(wish.created_at).toISOString() : new Date().toISOString(), + updated_at: wish.updated_at ? new Date(wish.updated_at).toISOString() : new Date().toISOString() + })) + } + + const formattedAllWishes = formatWishes(allWishes) + const formattedPublicWishes = formatWishes(publicWishes) + + // 按照 created_at 日期降序排序(最新的在前面) + formattedAllWishes.sort((a, b) => { + const dateA = new Date(a.created_at) + const dateB = new Date(b.created_at) + return dateB.getTime() - dateA.getTime() // 降序排序 + }) + + formattedPublicWishes.sort((a, b) => { + const dateA = new Date(a.created_at) + const dateB = new Date(b.created_at) + return dateB.getTime() - dateA.getTime() // 降序排序 + }) + + // 保存為 JSON 文件 + const dataDir = path.join(__dirname, '..', 'data') + if (!fs.existsSync(dataDir)) { + fs.mkdirSync(dataDir, { recursive: true }) + } + + fs.writeFileSync( + path.join(dataDir, 'all-wishes.json'), + JSON.stringify({ success: true, data: formattedAllWishes }, null, 2) + ) + + fs.writeFileSync( + path.join(dataDir, 'public-wishes.json'), + JSON.stringify({ success: true, data: formattedPublicWishes }, null, 2) + ) + + console.log('✅ 數據已保存到 data/ 目錄') + + // 顯示點讚統計 + const totalLikes = formattedAllWishes.reduce((sum, wish) => sum + wish.like_count, 0) + const publicLikes = formattedPublicWishes.reduce((sum, wish) => sum + wish.like_count, 0) + console.log(`📊 點讚統計: 總計 ${totalLikes} 個,公開 ${publicLikes} 個`) + + } catch (error) { + console.error('❌ 獲取數據失敗:', error.message) + } finally { + await prisma.$disconnect() + } +} + +// 執行 +if (require.main === module) { + getRealData() +} + +module.exports = { getRealData } diff --git a/scripts/test-like-api.js b/scripts/test-like-api.js deleted file mode 100644 index 98fba6c..0000000 --- a/scripts/test-like-api.js +++ /dev/null @@ -1,85 +0,0 @@ -#!/usr/bin/env node - -/** - * 測試點讚 API - */ - -async function testLikeAPI() { - try { - console.log('🔍 測試點讚 API...') - console.log('') - - const testUserSession = `test_api_session_${Date.now()}` - const testWishId = 6 // 使用存在的 Wish ID - - // 1. 測試檢查點讚狀態 - console.log('1️⃣ 測試檢查點讚狀態...') - const checkResponse = await fetch(`http://localhost:3000/api/wishes/like?wishId=${testWishId}`, { - headers: { - 'x-user-session': testUserSession - } - }) - - const checkResult = await checkResponse.json() - console.log(`✅ 檢查結果: ${checkResult.success ? '成功' : '失敗'}`) - console.log(` 已點讚: ${checkResult.data?.liked || false}`) - console.log('') - - // 2. 測試點讚 - console.log('2️⃣ 測試點讚...') - const likeResponse = await fetch('http://localhost:3000/api/wishes/like', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'x-user-session': testUserSession - }, - body: JSON.stringify({ wishId: testWishId }) - }) - - const likeResult = await likeResponse.json() - console.log(`✅ 點讚結果: ${likeResult.success ? '成功' : '失敗'}`) - console.log(` 點讚狀態: ${likeResult.data?.liked || false}`) - console.log('') - - // 3. 再次檢查點讚狀態 - console.log('3️⃣ 再次檢查點讚狀態...') - const checkResponse2 = await fetch(`http://localhost:3000/api/wishes/like?wishId=${testWishId}`, { - headers: { - 'x-user-session': testUserSession - } - }) - - const checkResult2 = await checkResponse2.json() - console.log(`✅ 檢查結果: ${checkResult2.success ? '成功' : '失敗'}`) - console.log(` 已點讚: ${checkResult2.data?.liked || false}`) - console.log('') - - // 4. 測試重複點讚 - console.log('4️⃣ 測試重複點讚...') - const likeResponse2 = await fetch('http://localhost:3000/api/wishes/like', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'x-user-session': testUserSession - }, - body: JSON.stringify({ wishId: testWishId }) - }) - - const likeResult2 = await likeResponse2.json() - console.log(`✅ 重複點讚結果: ${likeResult2.success ? '成功' : '失敗'}`) - console.log(` 點讚狀態: ${likeResult2.data?.liked || false}`) - console.log('') - - console.log('🎉 點讚 API 測試完成!') - - } catch (error) { - console.error('❌ 測試失敗:', error.message) - } -} - -// 執行測試 -if (require.main === module) { - testLikeAPI() -} - -module.exports = { testLikeAPI } diff --git a/scripts/test-like-functionality.js b/scripts/test-like-functionality.js deleted file mode 100644 index 412dc2e..0000000 --- a/scripts/test-like-functionality.js +++ /dev/null @@ -1,101 +0,0 @@ -#!/usr/bin/env node - -/** - * 測試點讚功能 - */ - -const { PrismaClient } = require('@prisma/client') - -// 設定環境變數 -process.env.DATABASE_URL = "mysql://wish_pool:Aa123456@mysql.theaken.com:33306/db_wish_pool?schema=public" - -async function testLikeFunctionality() { - const prisma = new PrismaClient() - - try { - console.log('🔍 測試點讚功能...') - console.log('') - - // 1. 檢查現有的點讚記錄 - console.log('1️⃣ 檢查現有的點讚記錄...') - const existingLikes = await prisma.wishLike.findMany({ - take: 5, - orderBy: { createdAt: 'desc' } - }) - - console.log(`✅ 現有 ${existingLikes.length} 筆點讚記錄`) - existingLikes.forEach((like, index) => { - console.log(` ${index + 1}. Wish ID: ${like.wishId}, Session: ${like.userSession.substring(0, 20)}...`) - }) - console.log('') - - // 2. 測試創建點讚記錄 - console.log('2️⃣ 測試創建點讚記錄...') - // 先獲取一個存在的 Wish ID - const existingWish = await prisma.wish.findFirst() - if (!existingWish) { - console.log('❌ 沒有找到任何困擾案例') - return - } - const testWishId = existingWish.id - const testUserSession = `test_session_${Date.now()}` - - console.log(` 使用 Wish ID: ${testWishId}`) - - try { - const newLike = await prisma.wishLike.create({ - data: { - wishId: testWishId, - userSession: testUserSession - } - }) - console.log(`✅ 成功創建點讚記錄: ID ${newLike.id}`) - } catch (error) { - if (error.code === 'P2002') { - console.log('⚠️ 點讚記錄已存在(重複點讚)') - } else { - throw error - } - } - console.log('') - - // 3. 測試查詢點讚記錄 - console.log('3️⃣ 測試查詢點讚記錄...') - const foundLike = await prisma.wishLike.findFirst({ - where: { - wishId: testWishId, - userSession: testUserSession - } - }) - - if (foundLike) { - console.log(`✅ 成功找到點讚記錄: ID ${foundLike.id}`) - } else { - console.log('❌ 未找到點讚記錄') - } - console.log('') - - // 4. 統計點讚數量 - console.log('4️⃣ 統計點讚數量...') - const likeCount = await prisma.wishLike.count({ - where: { wishId: testWishId } - }) - console.log(`✅ Wish ID ${testWishId} 的點讚數量: ${likeCount}`) - console.log('') - - console.log('🎉 點讚功能測試完成!') - - } catch (error) { - console.error('❌ 測試失敗:', error.message) - console.error('詳細錯誤:', error) - } finally { - await prisma.$disconnect() - } -} - -// 執行測試 -if (require.main === module) { - testLikeFunctionality() -} - -module.exports = { testLikeFunctionality }