/** * 书籍内容数据库API * 支持完整的CRUD操作 - 读取/写入/修改/删除章节 * 同时支持文件系统和数据库双写 */ import { NextRequest, NextResponse } from 'next/server' import { query } from '@/lib/db' import fs from 'fs' import path from 'path' import { bookData } from '@/lib/book-data' // 获取章节内容(从数据库或文件系统) async function getSectionContent(id: string): Promise<{content: string, source: 'db' | 'file'} | null> { try { // 先从数据库查询 const results = await query( 'SELECT content, section_title FROM chapters WHERE id = ?', [id] ) as any[] if (results.length > 0 && results[0].content) { return { content: results[0].content, source: 'db' } } } catch (e) { console.log('[Book API] 数据库查询失败,尝试从文件读取:', e) } // 从文件系统读取 const filePath = findSectionFilePath(id) if (filePath && fs.existsSync(filePath)) { try { const content = fs.readFileSync(filePath, 'utf-8') return { content, source: 'file' } } catch (e) { console.error('[Book API] 读取文件失败:', e) } } return null } // 根据section ID查找对应的文件路径 function findSectionFilePath(id: string): string | null { for (const part of bookData) { for (const chapter of part.chapters) { const section = chapter.sections.find(s => s.id === id) if (section?.filePath) { return path.join(process.cwd(), section.filePath) } } } return null } // 获取section的完整信息 function getSectionInfo(id: string) { for (const part of bookData) { for (const chapter of part.chapters) { const section = chapter.sections.find(s => s.id === id) if (section) { return { section, chapter, part, partId: part.id, chapterId: chapter.id, partTitle: part.title, chapterTitle: chapter.title } } } } return null } /** * GET - 读取章节内容 * 支持参数: * - id: 章节ID * - action: 'read' | 'export' | 'list' */ export async function GET(request: NextRequest) { const { searchParams } = new URL(request.url) const action = searchParams.get('action') || 'read' const id = searchParams.get('id') try { // 读取单个章节 if (action === 'read' && id) { const result = await getSectionContent(id) const sectionInfo = getSectionInfo(id) if (result) { return NextResponse.json({ success: true, section: { id, content: result.content, source: result.source, title: sectionInfo?.section.title || '', price: sectionInfo?.section.price || 1, partTitle: sectionInfo?.partTitle, chapterTitle: sectionInfo?.chapterTitle } }) } else { return NextResponse.json({ success: false, error: '章节不存在或无法读取' }, { status: 404 }) } } // 导出所有章节 if (action === 'export') { const sections: any[] = [] for (const part of bookData) { for (const chapter of part.chapters) { for (const section of chapter.sections) { const content = await getSectionContent(section.id) sections.push({ id: section.id, title: section.title, price: section.price, isFree: section.isFree, partId: part.id, partTitle: part.title, chapterId: chapter.id, chapterTitle: chapter.title, content: content?.content || '', source: content?.source || 'none' }) } } } const blob = JSON.stringify(sections, null, 2) return new NextResponse(blob, { headers: { 'Content-Type': 'application/json', 'Content-Disposition': `attachment; filename="book_sections_${new Date().toISOString().split('T')[0]}.json"` } }) } // 列出所有章节(不含内容) // 优先从数据库读取,确保新建章节能立即显示 if (action === 'list') { const sectionsFromDb = new Map() try { const rows = await query(` SELECT id, part_id, part_title, chapter_id, chapter_title, section_title, price, is_free, content FROM chapters ORDER BY part_id, chapter_id, id `) as any[] if (rows && rows.length > 0) { for (const r of rows) { sectionsFromDb.set(r.id, { id: r.id, title: r.section_title || '', price: r.price ?? 1, isFree: !!r.is_free, partId: r.part_id || 'part-1', partTitle: r.part_title || '', chapterId: r.chapter_id || 'chapter-1', chapterTitle: r.chapter_title || '', filePath: '' }) } } } catch (e) { console.log('[Book API] list 从数据库读取失败,回退到 bookData:', (e as Error).message) } // 合并:以数据库为准,数据库没有的用 bookData 补 const sections: any[] = [] for (const part of bookData) { for (const chapter of part.chapters) { for (const section of chapter.sections) { const dbRow = sectionsFromDb.get(section.id) sections.push(dbRow || { id: section.id, title: section.title, price: section.price, isFree: section.isFree, partId: part.id, partTitle: part.title, chapterId: chapter.id, chapterTitle: chapter.title, filePath: section.filePath }) sectionsFromDb.delete(section.id) } } } // 数据库有但 bookData 没有的(新建章节) for (const [, v] of sectionsFromDb) { sections.push(v) } // 按 id 去重,避免数据库重复或合并逻辑导致同一文章出现多次 const seen = new Set() const deduped = sections.filter((s) => { if (seen.has(s.id)) return false seen.add(s.id) return true }) return NextResponse.json({ success: true, sections: deduped, total: deduped.length }) } return NextResponse.json({ success: false, error: '无效的操作或缺少参数' }, { status: 400 }) } catch (error) { console.error('[Book API] GET错误:', error) return NextResponse.json({ success: false, error: '获取章节失败: ' + (error as Error).message }, { status: 500 }) } } /** * POST - 同步/导入章节 * 支持action: * - sync: 同步文件系统到数据库 * - import: 批量导入章节数据 */ export async function POST(request: NextRequest) { try { const body = await request.json() const { action, data } = body // 同步到数据库 if (action === 'sync') { let synced = 0 let failed = 0 for (const part of bookData) { for (const chapter of part.chapters) { for (const section of chapter.sections) { try { const filePath = path.join(process.cwd(), section.filePath) let content = '' if (fs.existsSync(filePath)) { content = fs.readFileSync(filePath, 'utf-8') } // 插入或更新到数据库 await query(` INSERT INTO chapters (id, part_id, part_title, chapter_id, chapter_title, section_title, content, word_count, is_free, price, sort_order, status) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'published') ON DUPLICATE KEY UPDATE part_title = VALUES(part_title), chapter_title = VALUES(chapter_title), section_title = VALUES(section_title), content = VALUES(content), word_count = VALUES(word_count), is_free = VALUES(is_free), price = VALUES(price), updated_at = CURRENT_TIMESTAMP `, [ section.id, part.id, part.title, chapter.id, chapter.title, section.title, content, content.length, section.isFree, section.price, synced ]) synced++ } catch (e) { console.error(`[Book API] 同步章节${section.id}失败:`, e) failed++ } } } } return NextResponse.json({ success: true, message: `同步完成:成功 ${synced} 个章节,失败 ${failed} 个`, synced, failed }) } // 导入数据 if (action === 'import' && data) { let imported = 0 let failed = 0 for (const item of data) { try { await query(` INSERT INTO chapters (id, part_id, part_title, chapter_id, chapter_title, section_title, content, word_count, is_free, price, status) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'published') ON DUPLICATE KEY UPDATE section_title = VALUES(section_title), content = VALUES(content), word_count = VALUES(word_count), is_free = VALUES(is_free), price = VALUES(price), updated_at = CURRENT_TIMESTAMP `, [ item.id, item.partId || 'part-1', item.partTitle || '未分类', item.chapterId || 'chapter-1', item.chapterTitle || '未分类', item.title, item.content || '', (item.content || '').length, item.is_free || false, item.price || 1 ]) imported++ } catch (e) { console.error(`[Book API] 导入章节${item.id}失败:`, e) failed++ } } return NextResponse.json({ success: true, message: `导入完成:成功 ${imported} 个章节,失败 ${failed} 个`, imported, failed }) } return NextResponse.json({ success: false, error: '无效的操作' }, { status: 400 }) } catch (error) { console.error('[Book API] POST错误:', error) return NextResponse.json({ success: false, error: '操作失败: ' + (error as Error).message }, { status: 500 }) } } /** * PUT - 更新章节内容 * 支持同时更新数据库和文件系统 */ export async function PUT(request: NextRequest) { try { const body = await request.json() const { id, title, content, price, saveToFile = true, partId, chapterId, partTitle, chapterTitle, isFree } = body if (!id) { return NextResponse.json({ success: false, error: '章节ID不能为空' }, { status: 400 }) } const sectionInfo = getSectionInfo(id) const finalPartId = partId || sectionInfo?.partId || 'part-1' const finalPartTitle = partTitle || sectionInfo?.partTitle || '未分类' const finalChapterId = chapterId || sectionInfo?.chapterId || 'chapter-1' const finalChapterTitle = chapterTitle || sectionInfo?.chapterTitle || '未分类' const finalPrice = price ?? sectionInfo?.section?.price ?? 1 const finalIsFree = isFree ?? sectionInfo?.section?.isFree ?? false // 更新数据库(含新建章节) try { await query(` INSERT INTO chapters (id, part_id, part_title, chapter_id, chapter_title, section_title, content, word_count, is_free, price, status) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'published') ON DUPLICATE KEY UPDATE part_id = VALUES(part_id), part_title = VALUES(part_title), chapter_id = VALUES(chapter_id), chapter_title = VALUES(chapter_title), section_title = VALUES(section_title), content = VALUES(content), word_count = VALUES(word_count), is_free = VALUES(is_free), price = VALUES(price), updated_at = CURRENT_TIMESTAMP `, [ id, finalPartId, finalPartTitle, finalChapterId, finalChapterTitle, title || sectionInfo?.section?.title || '', content || '', (content || '').length, finalIsFree, finalPrice ]) } catch (e) { console.error('[Book API] 更新数据库失败:', e) } // 同时保存到文件系统 if (saveToFile && sectionInfo?.section.filePath) { const filePath = path.join(process.cwd(), sectionInfo.section.filePath) try { // 确保目录存在 const dir = path.dirname(filePath) if (!fs.existsSync(dir)) { fs.mkdirSync(dir, { recursive: true }) } fs.writeFileSync(filePath, content || '', 'utf-8') } catch (e) { console.error('[Book API] 保存文件失败:', e) } } return NextResponse.json({ success: true, message: '章节更新成功', id }) } catch (error) { console.error('[Book API] PUT错误:', error) return NextResponse.json({ success: false, error: '更新章节失败: ' + (error as Error).message }, { status: 500 }) } } /** * DELETE - 删除章节 */ export async function DELETE(request: NextRequest) { const { searchParams } = new URL(request.url) const id = searchParams.get('id') if (!id) { return NextResponse.json({ success: false, error: '章节ID不能为空' }, { status: 400 }) } try { // 从数据库删除 await query('DELETE FROM chapters WHERE id = ?', [id]) return NextResponse.json({ success: true, message: '章节删除成功', id }) } catch (error) { console.error('[Book API] DELETE错误:', error) return NextResponse.json({ success: false, error: '删除章节失败: ' + (error as Error).message }, { status: 500 }) } }