Files
soul/app/api/book/all-chapters/route.ts

223 lines
7.1 KiB
TypeScript

import { NextResponse } from 'next/server'
import fs from 'fs'
import path from 'path'
import { query } from '@/lib/db'
export async function GET() {
try {
// 方案1: 优先从数据库读取章节数据
try {
const dbChapters = await query(`
SELECT
id, section_id, title, section_title, content,
is_free, price, words, section_order, chapter_order,
created_at, updated_at
FROM sections
ORDER BY section_order ASC, chapter_order ASC
`) as any[]
if (dbChapters && dbChapters.length > 0) {
console.log('[All Chapters API] 从数据库读取成功,共', dbChapters.length, '条')
// 格式化并按 id 去重(保留首次出现)
const seen = new Set<string>()
const allChapters = dbChapters
.map((chapter: any) => ({
id: chapter.id,
sectionId: chapter.section_id ?? chapter.id,
title: chapter.title ?? chapter.section_title,
sectionTitle: chapter.section_title ?? chapter.title,
content: chapter.content,
isFree: !!chapter.is_free,
price: chapter.price || 0,
words: chapter.words || Math.floor(Math.random() * 3000) + 2000,
sectionOrder: chapter.section_order,
chapterOrder: chapter.chapter_order,
createdAt: chapter.created_at,
updatedAt: chapter.updated_at
}))
.filter((row: { id: string }) => {
if (seen.has(row.id)) return false
seen.add(row.id)
return true
})
return NextResponse.json({
success: true,
data: allChapters,
chapters: allChapters,
total: allChapters.length,
source: 'database'
})
}
} catch (dbError) {
console.log('[All Chapters API] sections 表读取失败,尝试 chapters 表:', (dbError as Error).message)
}
// 方案1b: 从 chapters 表读取(与 lib/db 表结构一致)
try {
const dbChapters = await query(`
SELECT id, part_id, part_title, chapter_id, chapter_title, section_title, content,
is_free, price, word_count, sort_order, created_at, updated_at
FROM chapters
ORDER BY sort_order ASC, id ASC
`) as any[]
if (dbChapters && dbChapters.length > 0) {
console.log('[All Chapters API] 从 chapters 表读取成功,共', dbChapters.length, '条')
const seen = new Set<string>()
const allChapters = dbChapters
.map((row: any) => ({
id: row.id,
sectionId: row.id,
title: row.section_title,
sectionTitle: row.section_title,
content: row.content,
isFree: !!row.is_free,
price: row.price || 0,
words: row.word_count || 0,
sectionOrder: row.sort_order ?? 0,
chapterOrder: 0,
createdAt: row.created_at,
updatedAt: row.updated_at
}))
.filter((row: { id: string }) => {
if (seen.has(row.id)) return false
seen.add(row.id)
return true
})
return NextResponse.json({
success: true,
data: allChapters,
chapters: allChapters,
total: allChapters.length,
source: 'database'
})
}
} catch (e2) {
console.log('[All Chapters API] chapters 表读取失败,尝试文件:', (e2 as Error).message)
}
// 方案2: 从JSON文件读取
const possiblePaths = [
path.join(process.cwd(), 'public/book-chapters.json'),
path.join(process.cwd(), 'data/book-chapters.json'),
'/www/wwwroot/soul/public/book-chapters.json'
]
let chaptersData: any[] = []
let usedPath = ''
for (const dataPath of possiblePaths) {
try {
if (fs.existsSync(dataPath)) {
const fileContent = fs.readFileSync(dataPath, 'utf-8')
chaptersData = JSON.parse(fileContent)
usedPath = dataPath
console.log('[All Chapters API] 从文件读取成功:', dataPath)
break
}
} catch (e) {
console.log('[All Chapters API] 读取文件失败:', dataPath)
}
}
if (chaptersData.length > 0) {
// 添加字数估算并按 id 去重
const seen = new Set<string>()
const allChapters = chaptersData
.map((chapter: any) => ({
...chapter,
id: chapter.id ?? chapter.sectionId,
words: chapter.words || Math.floor(Math.random() * 3000) + 2000
}))
.filter((row: any) => {
const id = row.id || row.sectionId
if (!id || seen.has(String(id))) return false
seen.add(String(id))
return true
})
return NextResponse.json({
success: true,
data: allChapters,
chapters: allChapters,
total: allChapters.length,
source: 'file',
path: usedPath
})
}
// 方案3: 返回默认数据
console.log('[All Chapters API] 无法读取章节数据,返回默认数据')
const defaultChapters = generateDefaultChapters()
return NextResponse.json({
success: true,
data: defaultChapters,
chapters: defaultChapters,
total: defaultChapters.length,
source: 'default'
})
} catch (error) {
console.error('[All Chapters API] Error:', error)
// 即使出错也返回默认数据,确保小程序可用
const defaultChapters = generateDefaultChapters()
return NextResponse.json({
success: true,
data: defaultChapters,
chapters: defaultChapters,
total: defaultChapters.length,
source: 'fallback',
warning: '使用默认数据'
})
}
}
// 生成默认章节数据
function generateDefaultChapters() {
const sections = [
{ id: 1, title: '第一章 创业启程', chapters: 5 },
{ id: 2, title: '第二章 找到方向', chapters: 6 },
{ id: 3, title: '第三章 打造产品', chapters: 5 },
{ id: 4, title: '第四章 增长之道', chapters: 6 },
{ id: 5, title: '第五章 团队建设', chapters: 5 },
]
const chapters: any[] = []
let chapterIndex = 0
sections.forEach((section, sectionIdx) => {
for (let i = 0; i < section.chapters; i++) {
chapterIndex++
chapters.push({
id: `ch_${chapterIndex}`,
sectionId: `section_${section.id}`,
title: `${chapterIndex}`,
sectionTitle: section.title,
content: `这是${section.title}的第${i + 1}节内容...`,
isFree: chapterIndex <= 3, // 前3章免费
price: chapterIndex <= 3 ? 0 : 9.9,
words: Math.floor(Math.random() * 3000) + 2000,
sectionOrder: sectionIdx + 1,
chapterOrder: i + 1
})
}
})
return chapters
}
function getRelativeTime(index: number): string {
if (index <= 3) return '刚刚'
if (index <= 6) return '1天前'
if (index <= 10) return '2天前'
if (index <= 15) return '3天前'
if (index <= 20) return '5天前'
if (index <= 30) return '1周前'
if (index <= 40) return '2周前'
return '1个月前'
}