优化小程序支付流程,新增订单插入逻辑,确保支付成功后更新订单状态并处理佣金分配。同时,重构阅读页面,增强权限管理和阅读追踪功能,提升用户体验。
This commit is contained in:
108
app/api/user/check-purchased/route.ts
Normal file
108
app/api/user/check-purchased/route.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* 检查用户是否已购买指定章节/全书
|
||||
* 用于支付前校验,避免重复购买
|
||||
*
|
||||
* GET /api/user/check-purchased?userId=xxx&type=section&productId=xxx
|
||||
*/
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { query } from '@/lib/db'
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const { searchParams } = new URL(request.url)
|
||||
const userId = searchParams.get('userId')
|
||||
const type = searchParams.get('type') // 'section' | 'fullbook'
|
||||
const productId = searchParams.get('productId')
|
||||
|
||||
if (!userId) {
|
||||
return NextResponse.json({
|
||||
success: false,
|
||||
error: '缺少 userId 参数'
|
||||
}, { status: 400 })
|
||||
}
|
||||
|
||||
// 1. 查询用户是否购买全书
|
||||
const userRows = await query(`
|
||||
SELECT has_full_book FROM users WHERE id = ?
|
||||
`, [userId]) as any[]
|
||||
|
||||
if (userRows.length === 0) {
|
||||
return NextResponse.json({
|
||||
success: false,
|
||||
error: '用户不存在'
|
||||
}, { status: 404 })
|
||||
}
|
||||
|
||||
const hasFullBook = userRows[0].has_full_book || false
|
||||
|
||||
// 如果已购全书,直接返回已购买
|
||||
if (hasFullBook) {
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
data: {
|
||||
isPurchased: true,
|
||||
reason: 'has_full_book'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 2. 如果是购买全书,检查是否已有全书订单
|
||||
if (type === 'fullbook') {
|
||||
const orderRows = await query(`
|
||||
SELECT COUNT(*) as count
|
||||
FROM orders
|
||||
WHERE user_id = ?
|
||||
AND product_type = 'fullbook'
|
||||
AND status = 'paid'
|
||||
`, [userId]) as any[]
|
||||
|
||||
const hasPaid = orderRows[0].count > 0
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
data: {
|
||||
isPurchased: hasPaid,
|
||||
reason: hasPaid ? 'fullbook_order_exists' : null
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 3. 如果是购买章节,检查是否已有该章节订单
|
||||
if (type === 'section' && productId) {
|
||||
const orderRows = await query(`
|
||||
SELECT COUNT(*) as count
|
||||
FROM orders
|
||||
WHERE user_id = ?
|
||||
AND product_type = 'section'
|
||||
AND product_id = ?
|
||||
AND status = 'paid'
|
||||
`, [userId, productId]) as any[]
|
||||
|
||||
const hasPaid = orderRows[0].count > 0
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
data: {
|
||||
isPurchased: hasPaid,
|
||||
reason: hasPaid ? 'section_order_exists' : null
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
data: {
|
||||
isPurchased: false,
|
||||
reason: null
|
||||
}
|
||||
})
|
||||
|
||||
} catch (error) {
|
||||
console.error('[CheckPurchased] 查询失败:', error)
|
||||
return NextResponse.json({
|
||||
success: false,
|
||||
error: '查询购买状态失败'
|
||||
}, { status: 500 })
|
||||
}
|
||||
}
|
||||
72
app/api/user/purchase-status/route.ts
Normal file
72
app/api/user/purchase-status/route.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* 查询用户购买状态 API
|
||||
* 用于支付成功后刷新用户的购买记录
|
||||
*
|
||||
* GET /api/user/purchase-status?userId=xxx
|
||||
*/
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { query } from '@/lib/db'
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const { searchParams } = new URL(request.url)
|
||||
const userId = searchParams.get('userId')
|
||||
|
||||
if (!userId) {
|
||||
return NextResponse.json({
|
||||
success: false,
|
||||
error: '缺少 userId 参数'
|
||||
}, { status: 400 })
|
||||
}
|
||||
|
||||
// 1. 查询用户基本信息
|
||||
const userRows = await query(`
|
||||
SELECT
|
||||
id, nickname, avatar, phone, wechat_id,
|
||||
referral_code, has_full_book,
|
||||
earnings, pending_earnings, referral_count
|
||||
FROM users
|
||||
WHERE id = ?
|
||||
`, [userId]) as any[]
|
||||
|
||||
if (userRows.length === 0) {
|
||||
return NextResponse.json({
|
||||
success: false,
|
||||
error: '用户不存在'
|
||||
}, { status: 404 })
|
||||
}
|
||||
|
||||
const user = userRows[0]
|
||||
|
||||
// 2. 从 orders 表查询已购买的章节
|
||||
const orderRows = await query(`
|
||||
SELECT DISTINCT product_id
|
||||
FROM orders
|
||||
WHERE user_id = ?
|
||||
AND status = 'paid'
|
||||
AND product_type = 'section'
|
||||
`, [userId]) as any[]
|
||||
|
||||
const purchasedSections = orderRows.map((row: any) => row.product_id).filter(Boolean)
|
||||
|
||||
// 3. 返回完整购买状态
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
data: {
|
||||
hasFullBook: user.has_full_book || false,
|
||||
purchasedSections,
|
||||
purchasedCount: purchasedSections.length,
|
||||
earnings: parseFloat(user.earnings) || 0,
|
||||
pendingEarnings: parseFloat(user.pending_earnings) || 0,
|
||||
}
|
||||
})
|
||||
|
||||
} catch (error) {
|
||||
console.error('[PurchaseStatus] 查询失败:', error)
|
||||
return NextResponse.json({
|
||||
success: false,
|
||||
error: '查询购买状态失败'
|
||||
}, { status: 500 })
|
||||
}
|
||||
}
|
||||
140
app/api/user/reading-progress/route.ts
Normal file
140
app/api/user/reading-progress/route.ts
Normal file
@@ -0,0 +1,140 @@
|
||||
/**
|
||||
* 阅读进度上报接口
|
||||
* POST /api/user/reading-progress
|
||||
*
|
||||
* 接收小程序上报的阅读进度,用于数据分析和断点续读
|
||||
*/
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { query } from '@/lib/db'
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const body = await request.json()
|
||||
const { userId, sectionId, progress, duration, status, completedAt } = body
|
||||
|
||||
// 参数校验
|
||||
if (!userId || !sectionId) {
|
||||
return NextResponse.json({
|
||||
success: false,
|
||||
error: '缺少必要参数'
|
||||
}, { status: 400 })
|
||||
}
|
||||
|
||||
// 查询是否已有记录
|
||||
const existingRows = await query(`
|
||||
SELECT id, progress, duration, status, first_open_at
|
||||
FROM reading_progress
|
||||
WHERE user_id = ? AND section_id = ?
|
||||
`, [userId, sectionId]) as any[]
|
||||
|
||||
const now = new Date()
|
||||
|
||||
if (existingRows.length > 0) {
|
||||
// 更新已有记录
|
||||
const existing = existingRows[0]
|
||||
|
||||
// 只更新更大的进度
|
||||
const newProgress = Math.max(existing.progress || 0, progress || 0)
|
||||
const newDuration = (existing.duration || 0) + (duration || 0)
|
||||
const newStatus = status || existing.status || 'reading'
|
||||
|
||||
await query(`
|
||||
UPDATE reading_progress
|
||||
SET
|
||||
progress = ?,
|
||||
duration = ?,
|
||||
status = ?,
|
||||
completed_at = ?,
|
||||
last_open_at = ?,
|
||||
updated_at = ?
|
||||
WHERE user_id = ? AND section_id = ?
|
||||
`, [
|
||||
newProgress,
|
||||
newDuration,
|
||||
newStatus,
|
||||
completedAt ? new Date(completedAt) : existing.completed_at,
|
||||
now,
|
||||
now,
|
||||
userId,
|
||||
sectionId
|
||||
])
|
||||
|
||||
console.log('[ReadingProgress] 更新进度:', { userId, sectionId, progress: newProgress, duration: newDuration })
|
||||
} else {
|
||||
// 插入新记录
|
||||
await query(`
|
||||
INSERT INTO reading_progress
|
||||
(user_id, section_id, progress, duration, status, completed_at, first_open_at, last_open_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`, [
|
||||
userId,
|
||||
sectionId,
|
||||
progress || 0,
|
||||
duration || 0,
|
||||
status || 'reading',
|
||||
completedAt ? new Date(completedAt) : null,
|
||||
now,
|
||||
now
|
||||
])
|
||||
|
||||
console.log('[ReadingProgress] 新增进度:', { userId, sectionId, progress, duration })
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
message: '进度已保存'
|
||||
})
|
||||
|
||||
} catch (error) {
|
||||
console.error('[ReadingProgress] 保存失败:', error)
|
||||
return NextResponse.json({
|
||||
success: false,
|
||||
error: '保存进度失败'
|
||||
}, { status: 500 })
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询用户的阅读进度列表
|
||||
* GET /api/user/reading-progress?userId=xxx
|
||||
*/
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const { searchParams } = new URL(request.url)
|
||||
const userId = searchParams.get('userId')
|
||||
|
||||
if (!userId) {
|
||||
return NextResponse.json({
|
||||
success: false,
|
||||
error: '缺少 userId 参数'
|
||||
}, { status: 400 })
|
||||
}
|
||||
|
||||
const rows = await query(`
|
||||
SELECT
|
||||
section_id,
|
||||
progress,
|
||||
duration,
|
||||
status,
|
||||
completed_at,
|
||||
first_open_at,
|
||||
last_open_at
|
||||
FROM reading_progress
|
||||
WHERE user_id = ?
|
||||
ORDER BY last_open_at DESC
|
||||
`, [userId]) as any[]
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
data: rows
|
||||
})
|
||||
|
||||
} catch (error) {
|
||||
console.error('[ReadingProgress] 查询失败:', error)
|
||||
return NextResponse.json({
|
||||
success: false,
|
||||
error: '查询进度失败'
|
||||
}, { status: 500 })
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user