2026-01-14 12:50:00 +08:00
|
|
|
|
// app/api/book/latest-chapters/route.ts
|
2026-02-21 20:44:38 +08:00
|
|
|
|
// 获取最新章节:有2日内更新则取最新3章,否则随机取免费章节
|
2026-01-14 12:50:00 +08:00
|
|
|
|
|
2026-02-21 20:44:38 +08:00
|
|
|
|
import { NextResponse } from 'next/server'
|
|
|
|
|
|
import { query } from '@/lib/db'
|
2026-01-14 12:50:00 +08:00
|
|
|
|
|
2026-02-21 20:44:38 +08:00
|
|
|
|
const TWO_DAYS_MS = 2 * 24 * 60 * 60 * 1000
|
|
|
|
|
|
|
|
|
|
|
|
export async function GET() {
|
2026-01-14 12:50:00 +08:00
|
|
|
|
try {
|
2026-02-21 20:44:38 +08:00
|
|
|
|
let allChapters: Array<{
|
|
|
|
|
|
id: string
|
|
|
|
|
|
title: string
|
|
|
|
|
|
part: string
|
|
|
|
|
|
isFree: boolean
|
|
|
|
|
|
price: number
|
|
|
|
|
|
updatedAt: Date | string | null
|
|
|
|
|
|
createdAt: Date | string | null
|
|
|
|
|
|
}> = []
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const dbRows = (await query(`
|
|
|
|
|
|
SELECT id, part_title, section_title, is_free, price, created_at, updated_at
|
|
|
|
|
|
FROM chapters
|
|
|
|
|
|
ORDER BY sort_order ASC, id ASC
|
|
|
|
|
|
`)) as any[]
|
|
|
|
|
|
|
|
|
|
|
|
if (dbRows?.length > 0) {
|
|
|
|
|
|
allChapters = dbRows.map((row: any) => ({
|
|
|
|
|
|
id: row.id,
|
|
|
|
|
|
title: row.section_title || row.title || '',
|
|
|
|
|
|
part: row.part_title || '真实的行业',
|
|
|
|
|
|
isFree: !!row.is_free,
|
|
|
|
|
|
price: row.price || 0,
|
|
|
|
|
|
updatedAt: row.updated_at || row.created_at,
|
|
|
|
|
|
createdAt: row.created_at
|
|
|
|
|
|
}))
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
console.log('[latest-chapters] 数据库读取失败:', (e as Error).message)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (allChapters.length === 0) {
|
|
|
|
|
|
return NextResponse.json({
|
|
|
|
|
|
success: true,
|
|
|
|
|
|
banner: { id: '1.1', title: '荷包:电动车出租的被动收入模式', part: '真实的人' },
|
|
|
|
|
|
label: '为你推荐',
|
|
|
|
|
|
chapters: [],
|
|
|
|
|
|
hasNewUpdates: false
|
2026-01-14 12:50:00 +08:00
|
|
|
|
})
|
2026-02-21 20:44:38 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const now = Date.now()
|
|
|
|
|
|
const sorted = [...allChapters].sort((a, b) => {
|
|
|
|
|
|
const ta = a.updatedAt ? new Date(a.updatedAt).getTime() : 0
|
|
|
|
|
|
const tb = b.updatedAt ? new Date(b.updatedAt).getTime() : 0
|
|
|
|
|
|
return tb - ta
|
2026-01-14 12:50:00 +08:00
|
|
|
|
})
|
|
|
|
|
|
|
2026-02-21 20:44:38 +08:00
|
|
|
|
const mostRecentTime = sorted[0]?.updatedAt ? new Date(sorted[0].updatedAt).getTime() : 0
|
|
|
|
|
|
const hasNewUpdates = now - mostRecentTime < TWO_DAYS_MS
|
|
|
|
|
|
|
|
|
|
|
|
let banner: { id: string; title: string; part: string }
|
|
|
|
|
|
let label: string
|
|
|
|
|
|
let chapters: typeof allChapters
|
|
|
|
|
|
|
|
|
|
|
|
if (hasNewUpdates && sorted.length > 0) {
|
|
|
|
|
|
chapters = sorted.slice(0, 3)
|
|
|
|
|
|
banner = { id: chapters[0].id, title: chapters[0].title, part: chapters[0].part }
|
|
|
|
|
|
label = '最新更新'
|
|
|
|
|
|
} else {
|
|
|
|
|
|
const freeChapters = allChapters.filter((c) => c.isFree || c.price === 0)
|
|
|
|
|
|
const candidates = freeChapters.length > 0 ? freeChapters : allChapters
|
|
|
|
|
|
const shuffled = [...candidates].sort(() => Math.random() - 0.5)
|
|
|
|
|
|
chapters = shuffled.slice(0, 3)
|
|
|
|
|
|
banner = chapters[0]
|
|
|
|
|
|
? { id: chapters[0].id, title: chapters[0].title, part: chapters[0].part }
|
|
|
|
|
|
: { id: allChapters[0].id, title: allChapters[0].title, part: allChapters[0].part }
|
|
|
|
|
|
label = '为你推荐'
|
|
|
|
|
|
}
|
2026-01-14 12:50:00 +08:00
|
|
|
|
|
|
|
|
|
|
return NextResponse.json({
|
|
|
|
|
|
success: true,
|
2026-02-21 20:44:38 +08:00
|
|
|
|
banner,
|
|
|
|
|
|
label,
|
|
|
|
|
|
chapters: chapters.map((c) => ({ id: c.id, title: c.title, part: c.part, isFree: c.isFree })),
|
|
|
|
|
|
hasNewUpdates
|
2026-01-14 12:50:00 +08:00
|
|
|
|
})
|
|
|
|
|
|
} catch (error) {
|
2026-02-21 20:44:38 +08:00
|
|
|
|
console.error('[latest-chapters] Error:', error)
|
2026-01-14 12:50:00 +08:00
|
|
|
|
return NextResponse.json(
|
2026-02-21 20:44:38 +08:00
|
|
|
|
{ success: false, error: '获取失败' },
|
2026-01-14 12:50:00 +08:00
|
|
|
|
{ status: 500 }
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|