新增订单推荐人和邀请码功能,优化支付流程中的订单插入逻辑,确保订单记录准确。更新小程序支付请求,支持传递邀请码以便于分销归属和对账。同时,调整数据库结构以支持新字段,提升系统的稳定性和用户体验。

This commit is contained in:
2026-02-06 18:34:02 +08:00
parent f8fac00c85
commit 2e65d68e1e
34 changed files with 3288 additions and 1255 deletions

View File

@@ -1,18 +1,12 @@
// app/api/book/chapters/route.ts
// 章节管理API - 支持列表查询、新增、编辑
// 开发: 卡若
// 日期: 2026-01-25
// 章节管理API - 使用 Prisma ORM
// 优势类型安全、防SQL注入、简化查询
import { NextRequest, NextResponse } from 'next/server'
import { query } from '@/lib/db'
import { prisma } from '@/lib/prisma'
/**
* GET - 获取章节列表
* 支持参数:
* - partId: 按篇筛选
* - status: 按状态筛选 (draft/published/archived)
* - page: 页码
* - pageSize: 每页数量
*/
export async function GET(req: NextRequest) {
try {
@@ -22,48 +16,44 @@ export async function GET(req: NextRequest) {
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[] = []
// 构建 Prisma where 条件
const where: any = {}
if (partId) where.part_id = partId
if (status && status !== 'all') where.status = status as 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
// 使用 Prisma 分页查询
const [results, total] = await Promise.all([
prisma.chapters.findMany({
where,
orderBy: { sort_order: 'asc' },
skip: (page - 1) * pageSize,
take: pageSize,
select: {
id: true,
part_id: true,
part_title: true,
chapter_id: true,
chapter_title: true,
section_title: true,
word_count: true,
is_free: true,
price: true,
sort_order: true,
status: true,
created_at: true,
updated_at: true
}
}),
prisma.chapters.count({ where })
])
return NextResponse.json({
success: true,
data: {
list: results,
list: results.map(r => ({
...r,
price: Number(r.price)
})),
total,
page,
pageSize,
@@ -86,77 +76,62 @@ 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'
id, partId, partTitle, chapterId, chapterTitle, sectionTitle,
content, wordCount, isFree, price, sortOrder, status
} = body
// 验证必填字段
if (!id || !partId || !partTitle || !chapterId || !chapterTitle || !sectionTitle || !content) {
if (!id || !partId || !chapterId) {
return NextResponse.json(
{ success: false, error: '缺少必字段' },
{ success: false, error: '缺少必字段' },
{ status: 400 }
)
}
// 检查ID是否已存在
const existing = await query('SELECT id FROM chapters WHERE id = ?', [id]) as any[]
if (existing.length > 0) {
// 使用 Prisma 创建章节
const chapter = await prisma.chapters.create({
data: {
id,
part_id: partId,
part_title: partTitle || '',
chapter_id: chapterId,
chapter_title: chapterTitle || '',
section_title: sectionTitle || '',
content: content || '',
word_count: wordCount || 0,
is_free: isFree || false,
price: price || 1,
sort_order: sortOrder || 0,
status: (status as any) || 'published'
}
})
return NextResponse.json({
success: true,
message: '章节创建成功',
data: { ...chapter, price: Number(chapter.price) }
})
} catch (error: any) {
console.error('[Chapters API] 创建失败:', error)
if (error.code === 'P2002') {
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: '新增章节失败' },
{ success: false, error: '创建章节失败' },
{ status: 500 }
)
}
}
/**
* PUT - 编辑章节
* PUT - 更新章节
*/
export async function PUT(req: NextRequest) {
try {
const body = await req.json()
const { id, ...updates } = body
const { id, ...updateData } = body
if (!id) {
return NextResponse.json(
@@ -165,63 +140,37 @@ export async function PUT(req: NextRequest) {
)
}
// 检查章节是否存在
const existing = await query('SELECT id FROM chapters WHERE id = ?', [id]) as any[]
if (existing.length === 0) {
// 构建更新数据
const data: any = { updated_at: new Date() }
if (updateData.partTitle !== undefined) data.part_title = updateData.partTitle
if (updateData.chapterTitle !== undefined) data.chapter_title = updateData.chapterTitle
if (updateData.sectionTitle !== undefined) data.section_title = updateData.sectionTitle
if (updateData.content !== undefined) data.content = updateData.content
if (updateData.wordCount !== undefined) data.word_count = updateData.wordCount
if (updateData.isFree !== undefined) data.is_free = updateData.isFree
if (updateData.price !== undefined) data.price = updateData.price
if (updateData.sortOrder !== undefined) data.sort_order = updateData.sortOrder
if (updateData.status !== undefined) data.status = updateData.status
// 使用 Prisma 更新章节
const chapter = await prisma.chapters.update({
where: { id },
data
})
return NextResponse.json({
success: true,
message: '章节更新成功',
data: { ...chapter, price: Number(chapter.price) }
})
} catch (error: any) {
console.error('[Chapters API] 更新失败:', error)
if (error.code === 'P2025') {
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<string, string> = {
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 }
@@ -230,7 +179,7 @@ export async function PUT(req: NextRequest) {
}
/**
* DELETE - 删除章节软删除改状态为archived
* DELETE - 删除章节
*/
export async function DELETE(req: NextRequest) {
try {
@@ -244,17 +193,23 @@ export async function DELETE(req: NextRequest) {
)
}
// 软删除改状态为archived
await query("UPDATE chapters SET status = 'archived' WHERE id = ?", [id])
console.log('[Chapters API] 删除章节成功:', id)
// 使用 Prisma 删除章节
await prisma.chapters.delete({
where: { id }
})
return NextResponse.json({
success: true,
message: '章节删除'
message: '章节删除成功'
})
} catch (error) {
console.error('[Chapters API] 删除章节失败:', error)
} catch (error: any) {
console.error('[Chapters API] 删除失败:', error)
if (error.code === 'P2025') {
return NextResponse.json(
{ success: false, error: '章节不存在' },
{ status: 404 }
)
}
return NextResponse.json(
{ success: false, error: '删除章节失败' },
{ status: 500 }