162 lines
5.0 KiB
TypeScript
162 lines
5.0 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: 优先从数据库读取(使用实际存在的 chapters 表,不是 sections)
|
||
try {
|
||
const dbChapters = await query(`
|
||
SELECT
|
||
id, part_id, part_title, chapter_id, chapter_title, section_title, content,
|
||
word_count, is_free, price, sort_order, created_at, updated_at
|
||
FROM chapters
|
||
WHERE status = 'published'
|
||
ORDER BY sort_order ASC
|
||
`, []) as any[]
|
||
|
||
if (dbChapters && dbChapters.length > 0) {
|
||
console.log('[All Chapters API] 从数据库 chapters 表读取成功,共', dbChapters.length, '章')
|
||
|
||
const allChapters = dbChapters.map((row: any, idx: number) => ({
|
||
id: row.id,
|
||
sectionId: row.chapter_id || row.part_id,
|
||
title: row.chapter_title || row.section_title,
|
||
sectionTitle: row.part_title || row.section_title,
|
||
content: row.content,
|
||
isFree: !!row.is_free,
|
||
price: Number(row.price) || 0,
|
||
words: row.word_count || Math.floor(Math.random() * 3000) + 2000,
|
||
sectionOrder: row.sort_order ?? idx + 1,
|
||
chapterOrder: idx + 1,
|
||
createdAt: row.created_at,
|
||
updatedAt: row.updated_at
|
||
}))
|
||
|
||
return NextResponse.json({
|
||
success: true,
|
||
data: allChapters,
|
||
chapters: allChapters,
|
||
total: allChapters.length,
|
||
source: 'database'
|
||
})
|
||
}
|
||
} catch (dbError) {
|
||
console.log('[All Chapters API] 数据库读取失败,尝试文件读取:', (dbError 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) {
|
||
// 添加字数估算
|
||
const allChapters = chaptersData.map((chapter: any) => ({
|
||
...chapter,
|
||
words: chapter.words || Math.floor(Math.random() * 3000) + 2000
|
||
}))
|
||
|
||
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个月前'
|
||
}
|