// app/api/book/chapters/route.ts // 章节管理API - 支持列表查询、新增、编辑 // 开发: 卡若 // 日期: 2026-01-25 import { NextRequest, NextResponse } from 'next/server' import { query } from '@/lib/db' /** * GET - 获取章节列表 * 支持参数: * - partId: 按篇筛选 * - status: 按状态筛选 (draft/published/archived) * - page: 页码 * - pageSize: 每页数量 */ export async function GET(req: NextRequest) { try { const { searchParams } = new URL(req.url) const partId = searchParams.get('partId') const status = searchParams.get('status') || 'published' const page = parseInt(searchParams.get('page') || '1') const pageSize = parseInt(searchParams.get('pageSize') || '100') let sql = ` SELECT id, part_id, part_title, chapter_id, chapter_title, section_title, word_count, is_free, price, sort_order, status, created_at, updated_at FROM chapters WHERE 1=1 ` const params: any[] = [] if (partId) { sql += ' AND part_id = ?' params.push(partId) } if (status && status !== 'all') { sql += ' AND status = ?' params.push(status) } sql += ' ORDER BY sort_order ASC' sql += ' LIMIT ? OFFSET ?' params.push(pageSize, (page - 1) * pageSize) const results = await query(sql, params) as any[] // 获取总数 let countSql = 'SELECT COUNT(*) as total FROM chapters WHERE 1=1' const countParams: any[] = [] if (partId) { countSql += ' AND part_id = ?' countParams.push(partId) } if (status && status !== 'all') { countSql += ' AND status = ?' countParams.push(status) } const countResult = await query(countSql, countParams) as any[] const total = countResult[0]?.total || 0 return NextResponse.json({ success: true, data: { list: results, total, page, pageSize, totalPages: Math.ceil(total / pageSize) } }) } catch (error) { console.error('[Chapters API] 获取列表失败:', error) return NextResponse.json( { success: false, error: '获取章节列表失败' }, { status: 500 } ) } } /** * POST - 新增章节 */ export async function POST(req: NextRequest) { try { const body = await req.json() const { id, partId, partTitle, chapterId, chapterTitle, sectionTitle, content, isFree = false, price = 1, sortOrder, status = 'published' } = body // 验证必填字段 if (!id || !partId || !partTitle || !chapterId || !chapterTitle || !sectionTitle || !content) { return NextResponse.json( { success: false, error: '缺少必填字段' }, { status: 400 } ) } // 检查ID是否已存在 const existing = await query('SELECT id FROM chapters WHERE id = ?', [id]) as any[] if (existing.length > 0) { return NextResponse.json( { success: false, error: '章节ID已存在' }, { status: 400 } ) } // 计算字数 const wordCount = content.replace(/\s/g, '').length // 计算排序顺序(如果未提供) let order = sortOrder if (order === undefined || order === null) { const maxOrder = await query( 'SELECT MAX(sort_order) as maxOrder FROM chapters WHERE part_id = ?', [partId] ) as any[] order = (maxOrder[0]?.maxOrder || 0) + 1 } 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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) `, [id, partId, partTitle, chapterId, chapterTitle, sectionTitle, content, wordCount, isFree, isFree ? 0 : price, order, status]) console.log('[Chapters API] 新增章节成功:', id) return NextResponse.json({ success: true, message: '章节创建成功', data: { id, wordCount, sortOrder: order } }) } catch (error) { console.error('[Chapters API] 新增章节失败:', error) return NextResponse.json( { success: false, error: '新增章节失败' }, { status: 500 } ) } } /** * PUT - 编辑章节 */ export async function PUT(req: NextRequest) { try { const body = await req.json() const { id, ...updates } = body if (!id) { return NextResponse.json( { success: false, error: '缺少章节ID' }, { status: 400 } ) } // 检查章节是否存在 const existing = await query('SELECT id FROM chapters WHERE id = ?', [id]) as any[] if (existing.length === 0) { return NextResponse.json( { success: false, error: '章节不存在' }, { status: 404 } ) } // 构建更新语句 const allowedFields = ['part_id', 'part_title', 'chapter_id', 'chapter_title', 'section_title', 'content', 'is_free', 'price', 'sort_order', 'status'] const fieldMapping: Record = { partId: 'part_id', partTitle: 'part_title', chapterId: 'chapter_id', chapterTitle: 'chapter_title', sectionTitle: 'section_title', isFree: 'is_free', sortOrder: 'sort_order' } const setClauses: string[] = [] const params: any[] = [] for (const [key, value] of Object.entries(updates)) { const dbField = fieldMapping[key] || key if (allowedFields.includes(dbField) && value !== undefined) { setClauses.push(`${dbField} = ?`) params.push(value) } } // 如果更新了content,重新计算字数 if (updates.content) { const wordCount = updates.content.replace(/\s/g, '').length setClauses.push('word_count = ?') params.push(wordCount) } if (setClauses.length === 0) { return NextResponse.json( { success: false, error: '没有可更新的字段' }, { status: 400 } ) } params.push(id) await query(`UPDATE chapters SET ${setClauses.join(', ')} WHERE id = ?`, params) console.log('[Chapters API] 更新章节成功:', id) return NextResponse.json({ success: true, message: '章节更新成功' }) } catch (error) { console.error('[Chapters API] 更新章节失败:', error) return NextResponse.json( { success: false, error: '更新章节失败' }, { status: 500 } ) } } /** * DELETE - 删除章节(软删除,改状态为archived) */ export async function DELETE(req: NextRequest) { try { const { searchParams } = new URL(req.url) const id = searchParams.get('id') if (!id) { return NextResponse.json( { success: false, error: '缺少章节ID' }, { status: 400 } ) } // 软删除:改状态为archived await query("UPDATE chapters SET status = 'archived' WHERE id = ?", [id]) console.log('[Chapters API] 删除章节成功:', id) return NextResponse.json({ success: true, message: '章节已删除' }) } catch (error) { console.error('[Chapters API] 删除章节失败:', error) return NextResponse.json( { success: false, error: '删除章节失败' }, { status: 500 } ) } }