🔧 数据库配置: - 切换到腾讯云外网数据库 - 配置连接参数和连接池 🎨 界面优化: - 未登录时只显示登录按钮,隐藏其他功能 - 优化登录卡片样式 - 修复章节图标和标题对齐问题 💳 支付流程优化: - 增加重复购买检测,避免重复支付 - 优化openId获取逻辑,支持静默获取 - 已登录用户可直接支付,无需重复登录 📊 后台管理: - 创建章节管理API (/api/admin/chapters) - 创建章节管理页面 (/admin/chapters) - 支持查看所有章节、修改价格、设置免费状态
320 lines
14 KiB
TypeScript
320 lines
14 KiB
TypeScript
/**
|
||
* 章节管理API - 后台管理功能
|
||
* 用于管理书籍章节、价格、状态等
|
||
*/
|
||
|
||
import { NextResponse } from 'next/server'
|
||
import fs from 'fs'
|
||
import path from 'path'
|
||
|
||
// 获取书籍目录
|
||
const BOOK_DIR = path.join(process.cwd(), 'book')
|
||
|
||
/**
|
||
* GET - 获取所有章节列表
|
||
*/
|
||
export async function GET(request: Request) {
|
||
try {
|
||
const { searchParams } = new URL(request.url)
|
||
const includeContent = searchParams.get('content') === 'true'
|
||
|
||
// 定义书籍结构
|
||
const bookStructure = [
|
||
{
|
||
id: 'part-preface',
|
||
title: '序言',
|
||
type: 'preface',
|
||
chapters: [
|
||
{
|
||
id: 'preface',
|
||
title: '序言|为什么我每天早上6点在Soul开播?',
|
||
price: 0,
|
||
isFree: true,
|
||
status: 'published',
|
||
file: '序言|为什么我每天早上6点在Soul开播?.md'
|
||
}
|
||
]
|
||
},
|
||
{
|
||
id: 'part-1',
|
||
title: '第一篇|真实的人',
|
||
type: 'part',
|
||
chapters: [
|
||
{
|
||
id: 'chapter-1',
|
||
title: '第1章|人与人之间的底层逻辑',
|
||
sections: [
|
||
{ id: '1.1', title: '荷包:电动车出租的被动收入模式', price: 1, isFree: true, status: 'published' },
|
||
{ id: '1.2', title: '老墨:资源整合高手的社交方法', price: 1, isFree: false, status: 'published' },
|
||
{ id: '1.3', title: '笑声背后的MBTI:为什么ENTJ适合做资源,INTP适合做系统', price: 1, isFree: false, status: 'published' },
|
||
{ id: '1.4', title: '人性的三角结构:利益、情感、价值观', price: 1, isFree: false, status: 'published' },
|
||
{ id: '1.5', title: '沟通差的问题:为什么你说的别人听不懂', price: 1, isFree: false, status: 'published' }
|
||
]
|
||
},
|
||
{
|
||
id: 'chapter-2',
|
||
title: '第2章|人性困境案例',
|
||
sections: [
|
||
{ id: '2.1', title: '相亲故事:你以为找的是人,实际是在找模式', price: 1, isFree: false, status: 'published' },
|
||
{ id: '2.2', title: '找工作迷茫者:为什么简历解决不了人生', price: 1, isFree: false, status: 'published' },
|
||
{ id: '2.3', title: '撸运费险:小钱困住大脑的真实心理', price: 1, isFree: false, status: 'published' },
|
||
{ id: '2.4', title: '游戏上瘾的年轻人:不是游戏吸引他,是生活没吸引力', price: 1, isFree: false, status: 'published' },
|
||
{ id: '2.5', title: '健康焦虑(我的糖尿病经历):疾病是人生的第一次清醒', price: 1, isFree: false, status: 'published' }
|
||
]
|
||
}
|
||
]
|
||
},
|
||
{
|
||
id: 'part-2',
|
||
title: '第二篇|真实的行业',
|
||
type: 'part',
|
||
chapters: [
|
||
{
|
||
id: 'chapter-3',
|
||
title: '第3章|电商篇',
|
||
sections: [
|
||
{ id: '3.1', title: '3000万流水如何跑出来(退税模式解析)', price: 1, isFree: false, status: 'published' },
|
||
{ id: '3.2', title: '供应链之王 vs 打工人:利润不在前端', price: 1, isFree: false, status: 'published' },
|
||
{ id: '3.3', title: '社区团购的底层逻辑', price: 1, isFree: false, status: 'published' },
|
||
{ id: '3.4', title: '跨境电商与退税套利', price: 1, isFree: false, status: 'published' }
|
||
]
|
||
},
|
||
{
|
||
id: 'chapter-4',
|
||
title: '第4章|内容商业篇',
|
||
sections: [
|
||
{ id: '4.1', title: '旅游号:30天10万粉的真实逻辑', price: 1, isFree: false, status: 'published' },
|
||
{ id: '4.2', title: '做号工厂:如何让一个号变成一个机器', price: 1, isFree: false, status: 'published' },
|
||
{ id: '4.3', title: '情绪内容为什么比专业内容更赚钱', price: 1, isFree: false, status: 'published' },
|
||
{ id: '4.4', title: '猫与宠物号:为什么宠物赛道永不过时', price: 1, isFree: false, status: 'published' },
|
||
{ id: '4.5', title: '直播间里的三种人:演员、技术工、系统流', price: 1, isFree: false, status: 'published' }
|
||
]
|
||
},
|
||
{
|
||
id: 'chapter-5',
|
||
title: '第5章|传统行业篇',
|
||
sections: [
|
||
{ id: '5.1', title: '拍卖行抱朴:一天240万的摇号生意', price: 1, isFree: false, status: 'published' },
|
||
{ id: '5.2', title: '土地拍卖:招拍挂背后的游戏规则', price: 1, isFree: false, status: 'published' },
|
||
{ id: '5.3', title: '地摊经济数字化:一个月900块的餐车生意', price: 1, isFree: false, status: 'published' },
|
||
{ id: '5.4', title: '不良资产拍卖:我错过的一个亿佣金', price: 1, isFree: false, status: 'published' },
|
||
{ id: '5.5', title: '桶装水李总:跟物业合作的轻资产模式', price: 1, isFree: false, status: 'published' }
|
||
]
|
||
}
|
||
]
|
||
},
|
||
{
|
||
id: 'part-3',
|
||
title: '第三篇|真实的错误',
|
||
type: 'part',
|
||
chapters: [
|
||
{
|
||
id: 'chapter-6',
|
||
title: '第6章|我人生错过的4件大钱',
|
||
sections: [
|
||
{ id: '6.1', title: '电商财税窗口:2016年的千万级机会', price: 1, isFree: false, status: 'published' },
|
||
{ id: '6.2', title: '供应链金融:我不懂的杠杆游戏', price: 1, isFree: false, status: 'published' },
|
||
{ id: '6.3', title: '内容红利:2019年我为什么没做抖音', price: 1, isFree: false, status: 'published' },
|
||
{ id: '6.4', title: '数据资产化:我还在观望的未来机会', price: 1, isFree: false, status: 'published' }
|
||
]
|
||
},
|
||
{
|
||
id: 'chapter-7',
|
||
title: '第7章|别人犯的错误',
|
||
sections: [
|
||
{ id: '7.1', title: '投资房年轻人的迷茫:资金 vs 能力', price: 1, isFree: false, status: 'published' },
|
||
{ id: '7.2', title: '信息差骗局:永远有人靠卖学习赚钱', price: 1, isFree: false, status: 'published' },
|
||
{ id: '7.3', title: '在Soul找恋爱但想赚钱的人', price: 1, isFree: false, status: 'published' },
|
||
{ id: '7.4', title: '创业者的三种死法:冲动、轻信、没结构', price: 1, isFree: false, status: 'published' },
|
||
{ id: '7.5', title: '人情生意的终点:关系越多亏得越多', price: 1, isFree: false, status: 'published' }
|
||
]
|
||
}
|
||
]
|
||
},
|
||
{
|
||
id: 'part-4',
|
||
title: '第四篇|真实的赚钱',
|
||
type: 'part',
|
||
chapters: [
|
||
{
|
||
id: 'chapter-8',
|
||
title: '第8章|底层结构',
|
||
sections: [
|
||
{ id: '8.1', title: '流量杠杆:抖音、Soul、飞书', price: 1, isFree: false, status: 'published' },
|
||
{ id: '8.2', title: '价格杠杆:供应链与信息差', price: 1, isFree: false, status: 'published' },
|
||
{ id: '8.3', title: '时间杠杆:自动化 + AI', price: 1, isFree: false, status: 'published' },
|
||
{ id: '8.4', title: '情绪杠杆:咨询、婚恋、生意场', price: 1, isFree: false, status: 'published' },
|
||
{ id: '8.5', title: '社交杠杆:认识谁比你会什么更重要', price: 1, isFree: false, status: 'published' },
|
||
{ id: '8.6', title: '云阿米巴:分不属于自己的钱', price: 1, isFree: false, status: 'published' }
|
||
]
|
||
},
|
||
{
|
||
id: 'chapter-9',
|
||
title: '第9章|我在Soul上亲访的赚钱案例',
|
||
sections: [
|
||
{ id: '9.1', title: '游戏账号私域:账号即资产', price: 1, isFree: false, status: 'published' },
|
||
{ id: '9.2', title: '健康包模式:高复购、高毛利', price: 1, isFree: false, status: 'published' },
|
||
{ id: '9.3', title: '药物私域:长期关系赛道', price: 1, isFree: false, status: 'published' },
|
||
{ id: '9.4', title: '残疾机构合作:退税 × AI × 人力成本', price: 1, isFree: false, status: 'published' },
|
||
{ id: '9.5', title: '私域银行:粉丝即小股东', price: 1, isFree: false, status: 'published' },
|
||
{ id: '9.6', title: 'Soul派对房:陌生人成交的最快场景', price: 1, isFree: false, status: 'published' },
|
||
{ id: '9.7', title: '飞书中台:从聊天到成交的流程化体系', price: 1, isFree: false, status: 'published' },
|
||
{ id: '9.8', title: '餐饮女孩:6万营收、1万利润的死撑生意', price: 1, isFree: false, status: 'published' },
|
||
{ id: '9.9', title: '电竞生态:从陪玩到签约到酒店的完整链条', price: 1, isFree: false, status: 'published' },
|
||
{ id: '9.10', title: '淘客大佬:损耗30%的白色通道', price: 1, isFree: false, status: 'published' },
|
||
{ id: '9.11', title: '蔬菜供应链:农户才是最赚钱的人', price: 1, isFree: false, status: 'published' },
|
||
{ id: '9.12', title: '美业整合:一个人的公司如何月入十万', price: 1, isFree: false, status: 'published' },
|
||
{ id: '9.13', title: 'AI工具推广:一个隐藏的高利润赛道', price: 1, isFree: false, status: 'published' },
|
||
{ id: '9.14', title: '大健康私域:一个月150万的70后', price: 1, isFree: false, status: 'published' }
|
||
]
|
||
}
|
||
]
|
||
},
|
||
{
|
||
id: 'part-5',
|
||
title: '第五篇|真实的社会',
|
||
type: 'part',
|
||
chapters: [
|
||
{
|
||
id: 'chapter-10',
|
||
title: '第10章|未来职业的变化趋势',
|
||
sections: [
|
||
{ id: '10.1', title: 'AI时代:哪些工作会消失,哪些会崛起', price: 1, isFree: false, status: 'published' },
|
||
{ id: '10.2', title: '一人公司:为什么越来越多人选择单干', price: 1, isFree: false, status: 'published' },
|
||
{ id: '10.3', title: '为什么链接能力会成为第一价值', price: 1, isFree: false, status: 'published' },
|
||
{ id: '10.4', title: '新型公司:Soul-飞书-线下的三位一体', price: 1, isFree: false, status: 'published' }
|
||
]
|
||
},
|
||
{
|
||
id: 'chapter-11',
|
||
title: '第11章|中国社会商业生态的未来',
|
||
sections: [
|
||
{ id: '11.1', title: '私域经济:为什么流量越来越贵', price: 1, isFree: false, status: 'published' },
|
||
{ id: '11.2', title: '银发经济与孤独经济:两个被忽视的万亿市场', price: 1, isFree: false, status: 'published' },
|
||
{ id: '11.3', title: '流量红利的终局', price: 1, isFree: false, status: 'published' },
|
||
{ id: '11.4', title: '大模型 + 供应链的组合拳', price: 1, isFree: false, status: 'published' },
|
||
{ id: '11.5', title: '社会分层的最终逻辑', price: 1, isFree: false, status: 'published' }
|
||
]
|
||
}
|
||
]
|
||
},
|
||
{
|
||
id: 'part-epilogue',
|
||
title: '尾声',
|
||
type: 'epilogue',
|
||
chapters: [
|
||
{
|
||
id: 'epilogue',
|
||
title: '尾声|这本书的真实目的',
|
||
price: 0,
|
||
isFree: true,
|
||
status: 'published',
|
||
file: '尾声|这本书的真实目的.md'
|
||
}
|
||
]
|
||
},
|
||
{
|
||
id: 'part-appendix',
|
||
title: '附录',
|
||
type: 'appendix',
|
||
chapters: [
|
||
{ id: 'appendix-1', title: '附录1|Soul派对房精选对话', price: 0, isFree: true, status: 'published' },
|
||
{ id: 'appendix-2', title: '附录2|创业者自检清单', price: 0, isFree: true, status: 'published' },
|
||
{ id: 'appendix-3', title: '附录3|本书提到的工具和资源', price: 0, isFree: true, status: 'published' }
|
||
]
|
||
}
|
||
]
|
||
|
||
// 计算统计数据
|
||
let totalSections = 0
|
||
let freeSections = 0
|
||
let paidSections = 0
|
||
|
||
bookStructure.forEach(part => {
|
||
if (part.chapters) {
|
||
part.chapters.forEach(chapter => {
|
||
if (chapter.sections) {
|
||
totalSections += chapter.sections.length
|
||
chapter.sections.forEach(s => {
|
||
if (s.isFree) freeSections++
|
||
else paidSections++
|
||
})
|
||
} else {
|
||
totalSections++
|
||
if (chapter.isFree) freeSections++
|
||
else paidSections++
|
||
}
|
||
})
|
||
}
|
||
})
|
||
|
||
return NextResponse.json({
|
||
success: true,
|
||
data: {
|
||
structure: bookStructure,
|
||
stats: {
|
||
totalSections,
|
||
freeSections,
|
||
paidSections,
|
||
totalParts: bookStructure.length
|
||
}
|
||
}
|
||
})
|
||
|
||
} catch (error) {
|
||
console.error('[AdminChapters] 获取章节失败:', error)
|
||
return NextResponse.json({
|
||
success: false,
|
||
error: '获取章节失败'
|
||
}, { status: 500 })
|
||
}
|
||
}
|
||
|
||
/**
|
||
* POST - 更新章节设置
|
||
*/
|
||
export async function POST(request: Request) {
|
||
try {
|
||
const body = await request.json()
|
||
const { action, chapterId, data } = body
|
||
|
||
console.log('[AdminChapters] 更新章节:', { action, chapterId })
|
||
|
||
switch (action) {
|
||
case 'updatePrice':
|
||
// 更新章节价格
|
||
// TODO: 保存到数据库
|
||
return NextResponse.json({
|
||
success: true,
|
||
data: { message: '价格更新成功', chapterId, price: data.price }
|
||
})
|
||
|
||
case 'toggleFree':
|
||
// 切换免费状态
|
||
return NextResponse.json({
|
||
success: true,
|
||
data: { message: '免费状态更新成功', chapterId, isFree: data.isFree }
|
||
})
|
||
|
||
case 'updateStatus':
|
||
// 更新发布状态
|
||
return NextResponse.json({
|
||
success: true,
|
||
data: { message: '发布状态更新成功', chapterId, status: data.status }
|
||
})
|
||
|
||
default:
|
||
return NextResponse.json({
|
||
success: false,
|
||
error: '未知操作'
|
||
}, { status: 400 })
|
||
}
|
||
|
||
} catch (error) {
|
||
console.error('[AdminChapters] 更新章节失败:', error)
|
||
return NextResponse.json({
|
||
success: false,
|
||
error: '更新章节失败'
|
||
}, { status: 500 })
|
||
}
|
||
} |