From 263da246c9508679e19812ac18f0e6715064ef01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=A1=E8=8B=A5?= Date: Sun, 25 Jan 2026 09:57:21 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=AB=A0=E8=8A=82=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=BA=93=E5=8C=96=20+=20=E6=94=AF=E4=BB=98=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 章节内容迁移到MySQL数据库(67篇文章) 2. 章节API改为从数据库读取,不再依赖book文件夹 3. 新增章节管理API(增删改查) 4. 更新小程序支付AppSecret 5. 整理完整API配置清单 --- app/api/book/chapter/[id]/route.ts | 184 +++----------------- app/api/book/chapters/route.ts | 263 +++++++++++++++++++++++++++++ app/api/miniprogram/pay/route.ts | 7 +- lib/db.ts | 24 +++ scripts/migrate-chapters-to-db.js | 224 ++++++++++++++++++++++++ 开发文档/API/配置清单-完整版.md | 205 ++++++++++++++++++++++ 6 files changed, 748 insertions(+), 159 deletions(-) create mode 100644 app/api/book/chapters/route.ts create mode 100644 scripts/migrate-chapters-to-db.js create mode 100644 开发文档/API/配置清单-完整版.md diff --git a/app/api/book/chapter/[id]/route.ts b/app/api/book/chapter/[id]/route.ts index c8e83ad..80f0080 100644 --- a/app/api/book/chapter/[id]/route.ts +++ b/app/api/book/chapter/[id]/route.ts @@ -1,83 +1,12 @@ // app/api/book/chapter/[id]/route.ts -// 获取章节详情 - 支持小程序和Web端 +// 获取章节详情 - 从数据库读取,支持小程序和Web端 +// 更新: 2026-01-25 改为从MySQL数据库读取章节内容 import { NextRequest, NextResponse } from 'next/server' -import fs from 'fs' -import path from 'path' -import matter from 'gray-matter' +import { query } from '@/lib/db' -const BOOK_DIR = path.join(process.cwd(), 'book') - -// 章节ID到文件路径的映射 -const CHAPTER_MAP: Record = { - 'preface': { dir: '', file: '序言|为什么我每天早上6点在Soul开播?.md', partTitle: '序言', chapterTitle: '为什么我每天早上6点在Soul开播?' }, - 'epilogue': { dir: '', file: '尾声|这本书的真实目的.md', partTitle: '尾声', chapterTitle: '这本书的真实目的' }, - '1.1': { dir: '第一篇|真实的人/第1章|人与人之间的底层逻辑', file: '1.1 荷包:电动车出租的被动收入模式.md', partTitle: '第一篇|真实的人', chapterTitle: '1.1 荷包:电动车出租的被动收入模式' }, - '1.2': { dir: '第一篇|真实的人/第1章|人与人之间的底层逻辑', file: '1.2 老墨:资源整合高手的社交方法.md', partTitle: '第一篇|真实的人', chapterTitle: '1.2 老墨:资源整合高手的社交方法' }, - '1.3': { dir: '第一篇|真实的人/第1章|人与人之间的底层逻辑', file: '1.3 笑声背后的MBTI:为什么ENTJ适合做资源,INTP适合做系统.md', partTitle: '第一篇|真实的人', chapterTitle: '1.3 笑声背后的MBTI' }, - '1.4': { dir: '第一篇|真实的人/第1章|人与人之间的底层逻辑', file: '1.4 人性的三角结构:利益、情感、价值观.md', partTitle: '第一篇|真实的人', chapterTitle: '1.4 人性的三角结构' }, - '1.5': { dir: '第一篇|真实的人/第1章|人与人之间的底层逻辑', file: '1.5 沟通差的问题:为什么你说的别人听不懂.md', partTitle: '第一篇|真实的人', chapterTitle: '1.5 沟通差的问题' }, - '2.1': { dir: '第一篇|真实的人/第2章|人性困境案例', file: '2.1 相亲故事:你以为找的是人,实际是在找模式.md', partTitle: '第一篇|真实的人', chapterTitle: '2.1 相亲故事' }, - '2.2': { dir: '第一篇|真实的人/第2章|人性困境案例', file: '2.2 找工作迷茫者:为什么简历解决不了人生.md', partTitle: '第一篇|真实的人', chapterTitle: '2.2 找工作迷茫者' }, - '2.3': { dir: '第一篇|真实的人/第2章|人性困境案例', file: '2.3 撸运费险:小钱困住大脑的真实心理.md', partTitle: '第一篇|真实的人', chapterTitle: '2.3 撸运费险' }, - '2.4': { dir: '第一篇|真实的人/第2章|人性困境案例', file: '2.4 游戏上瘾的年轻人:不是游戏吸引他,是生活没吸引力.md', partTitle: '第一篇|真实的人', chapterTitle: '2.4 游戏上瘾的年轻人' }, - '2.5': { dir: '第一篇|真实的人/第2章|人性困境案例', file: '2.5 健康焦虑(我的糖尿病经历):疾病是人生的第一次清醒.md', partTitle: '第一篇|真实的人', chapterTitle: '2.5 健康焦虑' }, - '3.1': { dir: '第二篇|真实的行业/第3章|电商篇', file: '3.1 3000万流水如何跑出来(退税模式解析).md', partTitle: '第二篇|真实的行业', chapterTitle: '3.1 3000万流水' }, - '3.2': { dir: '第二篇|真实的行业/第3章|电商篇', file: '3.2 供应链之王 vs 打工人:利润不在前端.md', partTitle: '第二篇|真实的行业', chapterTitle: '3.2 供应链之王' }, - '3.3': { dir: '第二篇|真实的行业/第3章|电商篇', file: '3.3 社区团购的底层逻辑.md', partTitle: '第二篇|真实的行业', chapterTitle: '3.3 社区团购' }, - '3.4': { dir: '第二篇|真实的行业/第3章|电商篇', file: '3.4 跨境电商与退税套利.md', partTitle: '第二篇|真实的行业', chapterTitle: '3.4 跨境电商' }, - '4.1': { dir: '第二篇|真实的行业/第4章|内容商业篇', file: '4.1 旅游号:30天10万粉的真实逻辑.md', partTitle: '第二篇|真实的行业', chapterTitle: '4.1 旅游号' }, - '4.2': { dir: '第二篇|真实的行业/第4章|内容商业篇', file: '4.2 做号工厂:如何让一个号变成一个机器.md', partTitle: '第二篇|真实的行业', chapterTitle: '4.2 做号工厂' }, - '4.3': { dir: '第二篇|真实的行业/第4章|内容商业篇', file: '4.3 情绪内容为什么比专业内容更赚钱.md', partTitle: '第二篇|真实的行业', chapterTitle: '4.3 情绪内容' }, - '4.4': { dir: '第二篇|真实的行业/第4章|内容商业篇', file: '4.4 猫与宠物号:为什么宠物赛道永不过时.md', partTitle: '第二篇|真实的行业', chapterTitle: '4.4 猫与宠物号' }, - '4.5': { dir: '第二篇|真实的行业/第4章|内容商业篇', file: '4.5 直播间里的三种人:演员、技术工、系统流.md', partTitle: '第二篇|真实的行业', chapterTitle: '4.5 直播间里的三种人' }, - '5.1': { dir: '第二篇|真实的行业/第5章|传统行业篇', file: '5.1 拍卖行抱朴:一天240万的摇号生意.md', partTitle: '第二篇|真实的行业', chapterTitle: '5.1 拍卖行抱朴' }, - '5.2': { dir: '第二篇|真实的行业/第5章|传统行业篇', file: '5.2 土地拍卖:招拍挂背后的游戏规则.md', partTitle: '第二篇|真实的行业', chapterTitle: '5.2 土地拍卖' }, - '5.3': { dir: '第二篇|真实的行业/第5章|传统行业篇', file: '5.3 地摊经济数字化:一个月900块的餐车生意.md', partTitle: '第二篇|真实的行业', chapterTitle: '5.3 地摊经济' }, - '5.4': { dir: '第二篇|真实的行业/第5章|传统行业篇', file: '5.4 不良资产拍卖:我错过的一个亿佣金.md', partTitle: '第二篇|真实的行业', chapterTitle: '5.4 不良资产拍卖' }, - '5.5': { dir: '第二篇|真实的行业/第5章|传统行业篇', file: '5.5 桶装水李总:跟物业合作的轻资产模式.md', partTitle: '第二篇|真实的行业', chapterTitle: '5.5 桶装水李总' }, - '6.1': { dir: '第三篇|真实的错误/第6章|我人生错过的4件大钱', file: '6.1 电商财税窗口:2016年的千万级机会.md', partTitle: '第三篇|真实的错误', chapterTitle: '6.1 电商财税窗口' }, - '6.2': { dir: '第三篇|真实的错误/第6章|我人生错过的4件大钱', file: '6.2 供应链金融:我不懂的杠杆游戏.md', partTitle: '第三篇|真实的错误', chapterTitle: '6.2 供应链金融' }, - '6.3': { dir: '第三篇|真实的错误/第6章|我人生错过的4件大钱', file: '6.3 内容红利:2019年我为什么没做抖音.md', partTitle: '第三篇|真实的错误', chapterTitle: '6.3 内容红利' }, - '6.4': { dir: '第三篇|真实的错误/第6章|我人生错过的4件大钱', file: '6.4 数据资产化:我还在观望的未来机会.md', partTitle: '第三篇|真实的错误', chapterTitle: '6.4 数据资产化' }, - '7.1': { dir: '第三篇|真实的错误/第7章|别人犯的错误', file: '7.1 投资房年轻人的迷茫:资金 vs 能力.md', partTitle: '第三篇|真实的错误', chapterTitle: '7.1 投资房年轻人' }, - '7.2': { dir: '第三篇|真实的错误/第7章|别人犯的错误', file: '7.2 信息差骗局:永远有人靠卖学习赚钱.md', partTitle: '第三篇|真实的错误', chapterTitle: '7.2 信息差骗局' }, - '7.3': { dir: '第三篇|真实的错误/第7章|别人犯的错误', file: '7.3 在Soul找恋爱但想赚钱的人.md', partTitle: '第三篇|真实的错误', chapterTitle: '7.3 在Soul找恋爱' }, - '7.4': { dir: '第三篇|真实的错误/第7章|别人犯的错误', file: '7.4 创业者的三种死法:冲动、轻信、没结构.md', partTitle: '第三篇|真实的错误', chapterTitle: '7.4 创业者的三种死法' }, - '7.5': { dir: '第三篇|真实的错误/第7章|别人犯的错误', file: '7.5 人情生意的终点:关系越多亏得越多.md', partTitle: '第三篇|真实的错误', chapterTitle: '7.5 人情生意' }, - '8.1': { dir: '第四篇|真实的赚钱/第8章|底层结构', file: '8.1 流量杠杆:抖音、Soul、飞书.md', partTitle: '第四篇|真实的赚钱', chapterTitle: '8.1 流量杠杆' }, - '8.2': { dir: '第四篇|真实的赚钱/第8章|底层结构', file: '8.2 价格杠杆:供应链与信息差.md', partTitle: '第四篇|真实的赚钱', chapterTitle: '8.2 价格杠杆' }, - '8.3': { dir: '第四篇|真实的赚钱/第8章|底层结构', file: '8.3 时间杠杆:自动化 + AI.md', partTitle: '第四篇|真实的赚钱', chapterTitle: '8.3 时间杠杆' }, - '8.4': { dir: '第四篇|真实的赚钱/第8章|底层结构', file: '8.4 情绪杠杆:咨询、婚恋、生意场.md', partTitle: '第四篇|真实的赚钱', chapterTitle: '8.4 情绪杠杆' }, - '8.5': { dir: '第四篇|真实的赚钱/第8章|底层结构', file: '8.5 社交杠杆:认识谁比你会什么更重要.md', partTitle: '第四篇|真实的赚钱', chapterTitle: '8.5 社交杠杆' }, - '8.6': { dir: '第四篇|真实的赚钱/第8章|底层结构', file: '8.6 云阿米巴:分不属于自己的钱.md', partTitle: '第四篇|真实的赚钱', chapterTitle: '8.6 云阿米巴' }, - '9.1': { dir: '第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例', file: '9.1 游戏账号私域:账号即资产.md', partTitle: '第四篇|真实的赚钱', chapterTitle: '9.1 游戏账号私域' }, - '9.2': { dir: '第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例', file: '9.2 健康包模式:高复购、高毛利.md', partTitle: '第四篇|真实的赚钱', chapterTitle: '9.2 健康包模式' }, - '9.3': { dir: '第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例', file: '9.3 药物私域:长期关系赛道.md', partTitle: '第四篇|真实的赚钱', chapterTitle: '9.3 药物私域' }, - '9.4': { dir: '第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例', file: '9.4 残疾机构合作:退税 × AI × 人力成本.md', partTitle: '第四篇|真实的赚钱', chapterTitle: '9.4 残疾机构合作' }, - '9.5': { dir: '第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例', file: '9.5 私域银行:粉丝即小股东.md', partTitle: '第四篇|真实的赚钱', chapterTitle: '9.5 私域银行' }, - '9.6': { dir: '第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例', file: '9.6 Soul派对房:陌生人成交的最快场景.md', partTitle: '第四篇|真实的赚钱', chapterTitle: '9.6 Soul派对房' }, - '9.7': { dir: '第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例', file: '9.7 飞书中台:从聊天到成交的流程化体系.md', partTitle: '第四篇|真实的赚钱', chapterTitle: '9.7 飞书中台' }, - '9.8': { dir: '第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例', file: '9.8 餐饮女孩:6万营收、1万利润的死撑生意.md', partTitle: '第四篇|真实的赚钱', chapterTitle: '9.8 餐饮女孩' }, - '9.9': { dir: '第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例', file: '9.9 电竞生态:从陪玩到签约到酒店的完整链条.md', partTitle: '第四篇|真实的赚钱', chapterTitle: '9.9 电竞生态' }, - '9.10': { dir: '第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例', file: '9.10 淘客大佬:损耗30%的白色通道.md', partTitle: '第四篇|真实的赚钱', chapterTitle: '9.10 淘客大佬' }, - '9.11': { dir: '第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例', file: '9.11 蔬菜供应链:农户才是最赚钱的人.md', partTitle: '第四篇|真实的赚钱', chapterTitle: '9.11 蔬菜供应链' }, - '9.12': { dir: '第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例', file: '9.12 美业整合:一个人的公司如何月入十万.md', partTitle: '第四篇|真实的赚钱', chapterTitle: '9.12 美业整合' }, - '9.13': { dir: '第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例', file: '9.13 AI工具推广:一个隐藏的高利润赛道.md', partTitle: '第四篇|真实的赚钱', chapterTitle: '9.13 AI工具推广' }, - '9.14': { dir: '第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例', file: '9.14 大健康私域:一个月150万的70后.md', partTitle: '第四篇|真实的赚钱', chapterTitle: '9.14 大健康私域' }, - '10.1': { dir: '第五篇|真实的社会/第10章|未来职业的变化趋势', file: '10.1 AI时代:哪些工作会消失,哪些会崛起.md', partTitle: '第五篇|真实的社会', chapterTitle: '10.1 AI时代' }, - '10.2': { dir: '第五篇|真实的社会/第10章|未来职业的变化趋势', file: '10.2 一人公司:为什么越来越多人选择单干.md', partTitle: '第五篇|真实的社会', chapterTitle: '10.2 一人公司' }, - '10.3': { dir: '第五篇|真实的社会/第10章|未来职业的变化趋势', file: '10.3 为什么链接能力会成为第一价值.md', partTitle: '第五篇|真实的社会', chapterTitle: '10.3 链接能力' }, - '10.4': { dir: '第五篇|真实的社会/第10章|未来职业的变化趋势', file: '10.4 新型公司:Soul-飞书-线下的三位一体.md', partTitle: '第五篇|真实的社会', chapterTitle: '10.4 新型公司' }, - '11.1': { dir: '第五篇|真实的社会/第11章|中国社会商业生态的未来', file: '11.1 私域经济:为什么流量越来越贵.md', partTitle: '第五篇|真实的社会', chapterTitle: '11.1 私域经济' }, - '11.2': { dir: '第五篇|真实的社会/第11章|中国社会商业生态的未来', file: '11.2 银发经济与孤独经济:两个被忽视的万亿市场.md', partTitle: '第五篇|真实的社会', chapterTitle: '11.2 银发经济' }, - '11.3': { dir: '第五篇|真实的社会/第11章|中国社会商业生态的未来', file: '11.3 流量红利的终局.md', partTitle: '第五篇|真实的社会', chapterTitle: '11.3 流量红利' }, - '11.4': { dir: '第五篇|真实的社会/第11章|中国社会商业生态的未来', file: '11.4 大模型 + 供应链的组合拳.md', partTitle: '第五篇|真实的社会', chapterTitle: '11.4 大模型+供应链' }, - '11.5': { dir: '第五篇|真实的社会/第11章|中国社会商业生态的未来', file: '11.5 社会分层的最终逻辑.md', partTitle: '第五篇|真实的社会', chapterTitle: '11.5 社会分层' }, - 'appendix-1': { dir: '附录', file: '附录1|Soul派对房精选对话.md', partTitle: '附录', chapterTitle: 'Soul派对房精选对话' }, - 'appendix-2': { dir: '附录', file: '附录2|创业者自检清单.md', partTitle: '附录', chapterTitle: '创业者自检清单' }, - 'appendix-3': { dir: '附录', file: '附录3|本书提到的工具和资源.md', partTitle: '附录', chapterTitle: '本书提到的工具和资源' }, -} +// 免费章节列表 +const FREE_CHAPTERS = ['preface', 'epilogue', '1.1', 'appendix-1', 'appendix-2', 'appendix-3'] export async function GET( req: NextRequest, @@ -87,53 +16,41 @@ export async function GET( const chapterId = params.id console.log('[Chapter API] 请求章节:', chapterId) - // 先从映射表查找 - const mapping = CHAPTER_MAP[chapterId] - let chapterFile: string | null = null - let partTitle = '' - let chapterTitle = '' + // 从数据库查询章节 + const results = await query( + `SELECT id, part_id, part_title, chapter_id, chapter_title, section_title, + content, word_count, is_free, price, sort_order, status, updated_at + FROM chapters + WHERE id = ? AND status = 'published'`, + [chapterId] + ) as any[] - if (mapping) { - const filePath = path.join(BOOK_DIR, mapping.dir, mapping.file) - if (fs.existsSync(filePath)) { - chapterFile = filePath - partTitle = mapping.partTitle - chapterTitle = mapping.chapterTitle - } - } - - // 如果映射找不到,尝试动态查找 - if (!chapterFile) { - chapterFile = findChapterFile(chapterId) - } - - if (!chapterFile) { + if (!results || results.length === 0) { console.log('[Chapter API] 章节不存在:', chapterId) return NextResponse.json( { error: '章节不存在', success: false }, { status: 404 } ) } - - const fileContent = fs.readFileSync(chapterFile, 'utf-8') - const { data, content } = matter(fileContent) - - // 判断是否免费章节 - const isFree = isFreeChapter(chapterId) - - console.log('[Chapter API] 返回章节内容:', chapterId, '长度:', content.length) + + const chapter = results[0] + const isFree = chapter.is_free || FREE_CHAPTERS.includes(chapterId) + + console.log('[Chapter API] 返回章节内容:', chapterId, '长度:', chapter.content?.length || 0) // 返回小程序兼容的格式 return NextResponse.json({ success: true, - id: chapterId, - title: data.title || chapterTitle || path.basename(chapterFile, '.md'), - content: content, - partTitle: partTitle || data.category || '', - chapterTitle: chapterTitle || data.title || '', - words: content.length, - updateTime: data.date || fs.statSync(chapterFile).mtime.toISOString(), + id: chapter.id, + title: chapter.section_title, + content: chapter.content, + partTitle: chapter.part_title, + chapterTitle: chapter.chapter_title, + sectionTitle: chapter.section_title, + words: chapter.word_count, + updateTime: chapter.updated_at, isFree, + price: chapter.price, needPurchase: !isFree }) } catch (error) { @@ -144,48 +61,3 @@ export async function GET( ) } } - -// 递归查找章节文件 -function findChapterFile(chapterId: string): string | null { - function searchDir(dir: string): string | null { - try { - const items = fs.readdirSync(dir) - - for (const item of items) { - const itemPath = path.join(dir, item) - const stat = fs.statSync(itemPath) - - if (stat.isDirectory()) { - const result = searchDir(itemPath) - if (result) return result - } else if (item.endsWith('.md')) { - // 检查文件名是否匹配章节ID - if (item.startsWith(chapterId + ' ') || - item.includes(chapterId) || - item.replace('.md', '') === chapterId) { - return itemPath - } - } - } - } catch (e) { - // 忽略权限错误 - } - return null - } - - return searchDir(BOOK_DIR) -} - -// 判断是否免费章节 -function isFreeChapter(chapterId: string): boolean { - const freeChapters = [ - 'preface', - 'epilogue', - '1.1', - 'appendix-1', - 'appendix-2', - 'appendix-3' - ] - - return freeChapters.includes(chapterId) -} diff --git a/app/api/book/chapters/route.ts b/app/api/book/chapters/route.ts new file mode 100644 index 0000000..4223c1f --- /dev/null +++ b/app/api/book/chapters/route.ts @@ -0,0 +1,263 @@ +// 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 } + ) + } +} diff --git a/app/api/miniprogram/pay/route.ts b/app/api/miniprogram/pay/route.ts index 7e1fd14..6230ba6 100644 --- a/app/api/miniprogram/pay/route.ts +++ b/app/api/miniprogram/pay/route.ts @@ -11,12 +11,13 @@ import { NextResponse } from 'next/server' import crypto from 'crypto' -// 微信支付配置 +// 微信支付配置 - 2026-01-25 更新 +// 小程序支付绑定状态: 审核中(申请单ID: 201554696918) const WECHAT_PAY_CONFIG = { appId: 'wxb8bbb2b10dec74aa', // 小程序AppID - appSecret: '85d3fa31584d06acdb1de4a597d25b7b', // 小程序AppSecret + appSecret: '3c1fb1f63e6e052222bbcead9d07fe0c', // 小程序AppSecret(已更新) mchId: '1318592501', // 商户号 - mchKey: 'wx3e31b068be59ddc131b068be59ddc2', // API密钥 + mchKey: 'wx3e31b068be59ddc131b068be59ddc2', // API密钥(v2) notifyUrl: 'https://soul.quwanzhi.com/api/miniprogram/pay/notify', // 支付回调地址 } diff --git a/lib/db.ts b/lib/db.ts index 492f953..95131df 100644 --- a/lib/db.ts +++ b/lib/db.ts @@ -161,6 +161,30 @@ export async function initDatabase() { ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci `) + // 章节内容表 - 存储书籍所有章节 + await query(` + CREATE TABLE IF NOT EXISTS chapters ( + id VARCHAR(20) PRIMARY KEY COMMENT '章节ID,如1.1、preface等', + part_id VARCHAR(20) NOT NULL COMMENT '所属篇ID,如part-1', + part_title VARCHAR(100) NOT NULL COMMENT '篇标题,如第一篇|真实的人', + chapter_id VARCHAR(20) NOT NULL COMMENT '所属章ID,如chapter-1', + chapter_title VARCHAR(200) NOT NULL COMMENT '章标题,如第1章|人与人之间的底层逻辑', + section_title VARCHAR(200) NOT NULL COMMENT '节标题', + content LONGTEXT NOT NULL COMMENT '章节正文内容(Markdown格式)', + word_count INT DEFAULT 0 COMMENT '字数统计', + is_free BOOLEAN DEFAULT FALSE COMMENT '是否免费章节', + price DECIMAL(10,2) DEFAULT 1.00 COMMENT '单章价格', + sort_order INT DEFAULT 0 COMMENT '排序顺序', + status ENUM('draft', 'published', 'archived') DEFAULT 'published' COMMENT '状态', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + INDEX idx_part_id (part_id), + INDEX idx_chapter_id (chapter_id), + INDEX idx_status (status), + INDEX idx_sort_order (sort_order) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci + `) + console.log('数据库表结构初始化完成') // 插入默认配置 diff --git a/scripts/migrate-chapters-to-db.js b/scripts/migrate-chapters-to-db.js new file mode 100644 index 0000000..169fd6c --- /dev/null +++ b/scripts/migrate-chapters-to-db.js @@ -0,0 +1,224 @@ +/** + * 章节迁移脚本 - 将book文件夹的Markdown文件导入数据库 + * 运行方式: node scripts/migrate-chapters-to-db.js + * + * 开发: 卡若 + * 日期: 2026-01-25 + */ + +const fs = require('fs') +const path = require('path') +const mysql = require('mysql2/promise') + +// 数据库配置 +const DB_CONFIG = { + host: '56b4c23f6853c.gz.cdb.myqcloud.com', + port: 14413, + user: 'cdb_outerroot', + password: 'Zhiqun1984', + database: 'soul_miniprogram', + charset: 'utf8mb4', +} + +// Book目录 +const BOOK_DIR = path.join(process.cwd(), 'book') + +// 免费章节列表 +const FREE_CHAPTERS = ['preface', 'epilogue', '1.1', 'appendix-1', 'appendix-2', 'appendix-3'] + +// 所有章节配置 +const CHAPTERS_CONFIG = [ + // 序言 + { id: 'preface', partId: 'intro', partTitle: '序言', chapterId: 'preface', chapterTitle: '序言', sectionTitle: '为什么我每天早上6点在Soul开播?', dir: '', file: '序言|为什么我每天早上6点在Soul开播?.md', sortOrder: 0 }, + + // 第一篇 真实的人 + { id: '1.1', partId: 'part-1', partTitle: '第一篇|真实的人', chapterId: 'chapter-1', chapterTitle: '第1章|人与人之间的底层逻辑', sectionTitle: '荷包:电动车出租的被动收入模式', dir: '第一篇|真实的人/第1章|人与人之间的底层逻辑', file: '1.1 荷包:电动车出租的被动收入模式.md', sortOrder: 1 }, + { id: '1.2', partId: 'part-1', partTitle: '第一篇|真实的人', chapterId: 'chapter-1', chapterTitle: '第1章|人与人之间的底层逻辑', sectionTitle: '老墨:资源整合高手的社交方法', dir: '第一篇|真实的人/第1章|人与人之间的底层逻辑', file: '1.2 老墨:资源整合高手的社交方法.md', sortOrder: 2 }, + { id: '1.3', partId: 'part-1', partTitle: '第一篇|真实的人', chapterId: 'chapter-1', chapterTitle: '第1章|人与人之间的底层逻辑', sectionTitle: '笑声背后的MBTI:为什么ENTJ适合做资源,INTP适合做系统', dir: '第一篇|真实的人/第1章|人与人之间的底层逻辑', file: '1.3 笑声背后的MBTI:为什么ENTJ适合做资源,INTP适合做系统.md', sortOrder: 3 }, + { id: '1.4', partId: 'part-1', partTitle: '第一篇|真实的人', chapterId: 'chapter-1', chapterTitle: '第1章|人与人之间的底层逻辑', sectionTitle: '人性的三角结构:利益、情感、价值观', dir: '第一篇|真实的人/第1章|人与人之间的底层逻辑', file: '1.4 人性的三角结构:利益、情感、价值观.md', sortOrder: 4 }, + { id: '1.5', partId: 'part-1', partTitle: '第一篇|真实的人', chapterId: 'chapter-1', chapterTitle: '第1章|人与人之间的底层逻辑', sectionTitle: '沟通差的问题:为什么你说的别人听不懂', dir: '第一篇|真实的人/第1章|人与人之间的底层逻辑', file: '1.5 沟通差的问题:为什么你说的别人听不懂.md', sortOrder: 5 }, + + { id: '2.1', partId: 'part-1', partTitle: '第一篇|真实的人', chapterId: 'chapter-2', chapterTitle: '第2章|人性困境案例', sectionTitle: '相亲故事:你以为找的是人,实际是在找模式', dir: '第一篇|真实的人/第2章|人性困境案例', file: '2.1 相亲故事:你以为找的是人,实际是在找模式.md', sortOrder: 6 }, + { id: '2.2', partId: 'part-1', partTitle: '第一篇|真实的人', chapterId: 'chapter-2', chapterTitle: '第2章|人性困境案例', sectionTitle: '找工作迷茫者:为什么简历解决不了人生', dir: '第一篇|真实的人/第2章|人性困境案例', file: '2.2 找工作迷茫者:为什么简历解决不了人生.md', sortOrder: 7 }, + { id: '2.3', partId: 'part-1', partTitle: '第一篇|真实的人', chapterId: 'chapter-2', chapterTitle: '第2章|人性困境案例', sectionTitle: '撸运费险:小钱困住大脑的真实心理', dir: '第一篇|真实的人/第2章|人性困境案例', file: '2.3 撸运费险:小钱困住大脑的真实心理.md', sortOrder: 8 }, + { id: '2.4', partId: 'part-1', partTitle: '第一篇|真实的人', chapterId: 'chapter-2', chapterTitle: '第2章|人性困境案例', sectionTitle: '游戏上瘾的年轻人:不是游戏吸引他,是生活没吸引力', dir: '第一篇|真实的人/第2章|人性困境案例', file: '2.4 游戏上瘾的年轻人:不是游戏吸引他,是生活没吸引力.md', sortOrder: 9 }, + { id: '2.5', partId: 'part-1', partTitle: '第一篇|真实的人', chapterId: 'chapter-2', chapterTitle: '第2章|人性困境案例', sectionTitle: '健康焦虑(我的糖尿病经历):疾病是人生的第一次清醒', dir: '第一篇|真实的人/第2章|人性困境案例', file: '2.5 健康焦虑(我的糖尿病经历):疾病是人生的第一次清醒.md', sortOrder: 10 }, + + // 第二篇 真实的行业 + { id: '3.1', partId: 'part-2', partTitle: '第二篇|真实的行业', chapterId: 'chapter-3', chapterTitle: '第3章|电商篇', sectionTitle: '3000万流水如何跑出来(退税模式解析)', dir: '第二篇|真实的行业/第3章|电商篇', file: '3.1 3000万流水如何跑出来(退税模式解析).md', sortOrder: 11 }, + { id: '3.2', partId: 'part-2', partTitle: '第二篇|真实的行业', chapterId: 'chapter-3', chapterTitle: '第3章|电商篇', sectionTitle: '供应链之王 vs 打工人:利润不在前端', dir: '第二篇|真实的行业/第3章|电商篇', file: '3.2 供应链之王 vs 打工人:利润不在前端.md', sortOrder: 12 }, + { id: '3.3', partId: 'part-2', partTitle: '第二篇|真实的行业', chapterId: 'chapter-3', chapterTitle: '第3章|电商篇', sectionTitle: '社区团购的底层逻辑', dir: '第二篇|真实的行业/第3章|电商篇', file: '3.3 社区团购的底层逻辑.md', sortOrder: 13 }, + { id: '3.4', partId: 'part-2', partTitle: '第二篇|真实的行业', chapterId: 'chapter-3', chapterTitle: '第3章|电商篇', sectionTitle: '跨境电商与退税套利', dir: '第二篇|真实的行业/第3章|电商篇', file: '3.4 跨境电商与退税套利.md', sortOrder: 14 }, + + { id: '4.1', partId: 'part-2', partTitle: '第二篇|真实的行业', chapterId: 'chapter-4', chapterTitle: '第4章|内容商业篇', sectionTitle: '旅游号:30天10万粉的真实逻辑', dir: '第二篇|真实的行业/第4章|内容商业篇', file: '4.1 旅游号:30天10万粉的真实逻辑.md', sortOrder: 15 }, + { id: '4.2', partId: 'part-2', partTitle: '第二篇|真实的行业', chapterId: 'chapter-4', chapterTitle: '第4章|内容商业篇', sectionTitle: '做号工厂:如何让一个号变成一个机器', dir: '第二篇|真实的行业/第4章|内容商业篇', file: '4.2 做号工厂:如何让一个号变成一个机器.md', sortOrder: 16 }, + { id: '4.3', partId: 'part-2', partTitle: '第二篇|真实的行业', chapterId: 'chapter-4', chapterTitle: '第4章|内容商业篇', sectionTitle: '情绪内容为什么比专业内容更赚钱', dir: '第二篇|真实的行业/第4章|内容商业篇', file: '4.3 情绪内容为什么比专业内容更赚钱.md', sortOrder: 17 }, + { id: '4.4', partId: 'part-2', partTitle: '第二篇|真实的行业', chapterId: 'chapter-4', chapterTitle: '第4章|内容商业篇', sectionTitle: '猫与宠物号:为什么宠物赛道永不过时', dir: '第二篇|真实的行业/第4章|内容商业篇', file: '4.4 猫与宠物号:为什么宠物赛道永不过时.md', sortOrder: 18 }, + { id: '4.5', partId: 'part-2', partTitle: '第二篇|真实的行业', chapterId: 'chapter-4', chapterTitle: '第4章|内容商业篇', sectionTitle: '直播间里的三种人:演员、技术工、系统流', dir: '第二篇|真实的行业/第4章|内容商业篇', file: '4.5 直播间里的三种人:演员、技术工、系统流.md', sortOrder: 19 }, + + { id: '5.1', partId: 'part-2', partTitle: '第二篇|真实的行业', chapterId: 'chapter-5', chapterTitle: '第5章|传统行业篇', sectionTitle: '拍卖行抱朴:一天240万的摇号生意', dir: '第二篇|真实的行业/第5章|传统行业篇', file: '5.1 拍卖行抱朴:一天240万的摇号生意.md', sortOrder: 20 }, + { id: '5.2', partId: 'part-2', partTitle: '第二篇|真实的行业', chapterId: 'chapter-5', chapterTitle: '第5章|传统行业篇', sectionTitle: '土地拍卖:招拍挂背后的游戏规则', dir: '第二篇|真实的行业/第5章|传统行业篇', file: '5.2 土地拍卖:招拍挂背后的游戏规则.md', sortOrder: 21 }, + { id: '5.3', partId: 'part-2', partTitle: '第二篇|真实的行业', chapterId: 'chapter-5', chapterTitle: '第5章|传统行业篇', sectionTitle: '地摊经济数字化:一个月900块的餐车生意', dir: '第二篇|真实的行业/第5章|传统行业篇', file: '5.3 地摊经济数字化:一个月900块的餐车生意.md', sortOrder: 22 }, + { id: '5.4', partId: 'part-2', partTitle: '第二篇|真实的行业', chapterId: 'chapter-5', chapterTitle: '第5章|传统行业篇', sectionTitle: '不良资产拍卖:我错过的一个亿佣金', dir: '第二篇|真实的行业/第5章|传统行业篇', file: '5.4 不良资产拍卖:我错过的一个亿佣金.md', sortOrder: 23 }, + { id: '5.5', partId: 'part-2', partTitle: '第二篇|真实的行业', chapterId: 'chapter-5', chapterTitle: '第5章|传统行业篇', sectionTitle: '桶装水李总:跟物业合作的轻资产模式', dir: '第二篇|真实的行业/第5章|传统行业篇', file: '5.5 桶装水李总:跟物业合作的轻资产模式.md', sortOrder: 24 }, + + // 第三篇 真实的错误 + { id: '6.1', partId: 'part-3', partTitle: '第三篇|真实的错误', chapterId: 'chapter-6', chapterTitle: '第6章|我人生错过的4件大钱', sectionTitle: '电商财税窗口:2016年的千万级机会', dir: '第三篇|真实的错误/第6章|我人生错过的4件大钱', file: '6.1 电商财税窗口:2016年的千万级机会.md', sortOrder: 25 }, + { id: '6.2', partId: 'part-3', partTitle: '第三篇|真实的错误', chapterId: 'chapter-6', chapterTitle: '第6章|我人生错过的4件大钱', sectionTitle: '供应链金融:我不懂的杠杆游戏', dir: '第三篇|真实的错误/第6章|我人生错过的4件大钱', file: '6.2 供应链金融:我不懂的杠杆游戏.md', sortOrder: 26 }, + { id: '6.3', partId: 'part-3', partTitle: '第三篇|真实的错误', chapterId: 'chapter-6', chapterTitle: '第6章|我人生错过的4件大钱', sectionTitle: '内容红利:2019年我为什么没做抖音', dir: '第三篇|真实的错误/第6章|我人生错过的4件大钱', file: '6.3 内容红利:2019年我为什么没做抖音.md', sortOrder: 27 }, + { id: '6.4', partId: 'part-3', partTitle: '第三篇|真实的错误', chapterId: 'chapter-6', chapterTitle: '第6章|我人生错过的4件大钱', sectionTitle: '数据资产化:我还在观望的未来机会', dir: '第三篇|真实的错误/第6章|我人生错过的4件大钱', file: '6.4 数据资产化:我还在观望的未来机会.md', sortOrder: 28 }, + + { id: '7.1', partId: 'part-3', partTitle: '第三篇|真实的错误', chapterId: 'chapter-7', chapterTitle: '第7章|别人犯的错误', sectionTitle: '投资房年轻人的迷茫:资金 vs 能力', dir: '第三篇|真实的错误/第7章|别人犯的错误', file: '7.1 投资房年轻人的迷茫:资金 vs 能力.md', sortOrder: 29 }, + { id: '7.2', partId: 'part-3', partTitle: '第三篇|真实的错误', chapterId: 'chapter-7', chapterTitle: '第7章|别人犯的错误', sectionTitle: '信息差骗局:永远有人靠卖学习赚钱', dir: '第三篇|真实的错误/第7章|别人犯的错误', file: '7.2 信息差骗局:永远有人靠卖学习赚钱.md', sortOrder: 30 }, + { id: '7.3', partId: 'part-3', partTitle: '第三篇|真实的错误', chapterId: 'chapter-7', chapterTitle: '第7章|别人犯的错误', sectionTitle: '在Soul找恋爱但想赚钱的人', dir: '第三篇|真实的错误/第7章|别人犯的错误', file: '7.3 在Soul找恋爱但想赚钱的人.md', sortOrder: 31 }, + { id: '7.4', partId: 'part-3', partTitle: '第三篇|真实的错误', chapterId: 'chapter-7', chapterTitle: '第7章|别人犯的错误', sectionTitle: '创业者的三种死法:冲动、轻信、没结构', dir: '第三篇|真实的错误/第7章|别人犯的错误', file: '7.4 创业者的三种死法:冲动、轻信、没结构.md', sortOrder: 32 }, + { id: '7.5', partId: 'part-3', partTitle: '第三篇|真实的错误', chapterId: 'chapter-7', chapterTitle: '第7章|别人犯的错误', sectionTitle: '人情生意的终点:关系越多亏得越多', dir: '第三篇|真实的错误/第7章|别人犯的错误', file: '7.5 人情生意的终点:关系越多亏得越多.md', sortOrder: 33 }, + + // 第四篇 真实的赚钱 + { id: '8.1', partId: 'part-4', partTitle: '第四篇|真实的赚钱', chapterId: 'chapter-8', chapterTitle: '第8章|底层结构', sectionTitle: '流量杠杆:抖音、Soul、飞书', dir: '第四篇|真实的赚钱/第8章|底层结构', file: '8.1 流量杠杆:抖音、Soul、飞书.md', sortOrder: 34 }, + { id: '8.2', partId: 'part-4', partTitle: '第四篇|真实的赚钱', chapterId: 'chapter-8', chapterTitle: '第8章|底层结构', sectionTitle: '价格杠杆:供应链与信息差', dir: '第四篇|真实的赚钱/第8章|底层结构', file: '8.2 价格杠杆:供应链与信息差.md', sortOrder: 35 }, + { id: '8.3', partId: 'part-4', partTitle: '第四篇|真实的赚钱', chapterId: 'chapter-8', chapterTitle: '第8章|底层结构', sectionTitle: '时间杠杆:自动化 + AI', dir: '第四篇|真实的赚钱/第8章|底层结构', file: '8.3 时间杠杆:自动化 + AI.md', sortOrder: 36 }, + { id: '8.4', partId: 'part-4', partTitle: '第四篇|真实的赚钱', chapterId: 'chapter-8', chapterTitle: '第8章|底层结构', sectionTitle: '情绪杠杆:咨询、婚恋、生意场', dir: '第四篇|真实的赚钱/第8章|底层结构', file: '8.4 情绪杠杆:咨询、婚恋、生意场.md', sortOrder: 37 }, + { id: '8.5', partId: 'part-4', partTitle: '第四篇|真实的赚钱', chapterId: 'chapter-8', chapterTitle: '第8章|底层结构', sectionTitle: '社交杠杆:认识谁比你会什么更重要', dir: '第四篇|真实的赚钱/第8章|底层结构', file: '8.5 社交杠杆:认识谁比你会什么更重要.md', sortOrder: 38 }, + { id: '8.6', partId: 'part-4', partTitle: '第四篇|真实的赚钱', chapterId: 'chapter-8', chapterTitle: '第8章|底层结构', sectionTitle: '云阿米巴:分不属于自己的钱', dir: '第四篇|真实的赚钱/第8章|底层结构', file: '8.6 云阿米巴:分不属于自己的钱.md', sortOrder: 39 }, + + { id: '9.1', partId: 'part-4', partTitle: '第四篇|真实的赚钱', chapterId: 'chapter-9', chapterTitle: '第9章|我在Soul上亲访的赚钱案例', sectionTitle: '游戏账号私域:账号即资产', dir: '第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例', file: '9.1 游戏账号私域:账号即资产.md', sortOrder: 40 }, + { id: '9.2', partId: 'part-4', partTitle: '第四篇|真实的赚钱', chapterId: 'chapter-9', chapterTitle: '第9章|我在Soul上亲访的赚钱案例', sectionTitle: '健康包模式:高复购、高毛利', dir: '第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例', file: '9.2 健康包模式:高复购、高毛利.md', sortOrder: 41 }, + { id: '9.3', partId: 'part-4', partTitle: '第四篇|真实的赚钱', chapterId: 'chapter-9', chapterTitle: '第9章|我在Soul上亲访的赚钱案例', sectionTitle: '药物私域:长期关系赛道', dir: '第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例', file: '9.3 药物私域:长期关系赛道.md', sortOrder: 42 }, + { id: '9.4', partId: 'part-4', partTitle: '第四篇|真实的赚钱', chapterId: 'chapter-9', chapterTitle: '第9章|我在Soul上亲访的赚钱案例', sectionTitle: '残疾机构合作:退税 × AI × 人力成本', dir: '第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例', file: '9.4 残疾机构合作:退税 × AI × 人力成本.md', sortOrder: 43 }, + { id: '9.5', partId: 'part-4', partTitle: '第四篇|真实的赚钱', chapterId: 'chapter-9', chapterTitle: '第9章|我在Soul上亲访的赚钱案例', sectionTitle: '私域银行:粉丝即小股东', dir: '第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例', file: '9.5 私域银行:粉丝即小股东.md', sortOrder: 44 }, + { id: '9.6', partId: 'part-4', partTitle: '第四篇|真实的赚钱', chapterId: 'chapter-9', chapterTitle: '第9章|我在Soul上亲访的赚钱案例', sectionTitle: 'Soul派对房:陌生人成交的最快场景', dir: '第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例', file: '9.6 Soul派对房:陌生人成交的最快场景.md', sortOrder: 45 }, + { id: '9.7', partId: 'part-4', partTitle: '第四篇|真实的赚钱', chapterId: 'chapter-9', chapterTitle: '第9章|我在Soul上亲访的赚钱案例', sectionTitle: '飞书中台:从聊天到成交的流程化体系', dir: '第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例', file: '9.7 飞书中台:从聊天到成交的流程化体系.md', sortOrder: 46 }, + { id: '9.8', partId: 'part-4', partTitle: '第四篇|真实的赚钱', chapterId: 'chapter-9', chapterTitle: '第9章|我在Soul上亲访的赚钱案例', sectionTitle: '餐饮女孩:6万营收、1万利润的死撑生意', dir: '第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例', file: '9.8 餐饮女孩:6万营收、1万利润的死撑生意.md', sortOrder: 47 }, + { id: '9.9', partId: 'part-4', partTitle: '第四篇|真实的赚钱', chapterId: 'chapter-9', chapterTitle: '第9章|我在Soul上亲访的赚钱案例', sectionTitle: '电竞生态:从陪玩到签约到酒店的完整链条', dir: '第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例', file: '9.9 电竞生态:从陪玩到签约到酒店的完整链条.md', sortOrder: 48 }, + { id: '9.10', partId: 'part-4', partTitle: '第四篇|真实的赚钱', chapterId: 'chapter-9', chapterTitle: '第9章|我在Soul上亲访的赚钱案例', sectionTitle: '淘客大佬:损耗30%的白色通道', dir: '第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例', file: '9.10 淘客大佬:损耗30%的白色通道.md', sortOrder: 49 }, + { id: '9.11', partId: 'part-4', partTitle: '第四篇|真实的赚钱', chapterId: 'chapter-9', chapterTitle: '第9章|我在Soul上亲访的赚钱案例', sectionTitle: '蔬菜供应链:农户才是最赚钱的人', dir: '第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例', file: '9.11 蔬菜供应链:农户才是最赚钱的人.md', sortOrder: 50 }, + { id: '9.12', partId: 'part-4', partTitle: '第四篇|真实的赚钱', chapterId: 'chapter-9', chapterTitle: '第9章|我在Soul上亲访的赚钱案例', sectionTitle: '美业整合:一个人的公司如何月入十万', dir: '第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例', file: '9.12 美业整合:一个人的公司如何月入十万.md', sortOrder: 51 }, + { id: '9.13', partId: 'part-4', partTitle: '第四篇|真实的赚钱', chapterId: 'chapter-9', chapterTitle: '第9章|我在Soul上亲访的赚钱案例', sectionTitle: 'AI工具推广:一个隐藏的高利润赛道', dir: '第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例', file: '9.13 AI工具推广:一个隐藏的高利润赛道.md', sortOrder: 52 }, + { id: '9.14', partId: 'part-4', partTitle: '第四篇|真实的赚钱', chapterId: 'chapter-9', chapterTitle: '第9章|我在Soul上亲访的赚钱案例', sectionTitle: '大健康私域:一个月150万的70后', dir: '第四篇|真实的赚钱/第9章|我在Soul上亲访的赚钱案例', file: '9.14 大健康私域:一个月150万的70后.md', sortOrder: 53 }, + + // 第五篇 真实的社会 + { id: '10.1', partId: 'part-5', partTitle: '第五篇|真实的社会', chapterId: 'chapter-10', chapterTitle: '第10章|未来职业的变化趋势', sectionTitle: 'AI时代:哪些工作会消失,哪些会崛起', dir: '第五篇|真实的社会/第10章|未来职业的变化趋势', file: '10.1 AI时代:哪些工作会消失,哪些会崛起.md', sortOrder: 54 }, + { id: '10.2', partId: 'part-5', partTitle: '第五篇|真实的社会', chapterId: 'chapter-10', chapterTitle: '第10章|未来职业的变化趋势', sectionTitle: '一人公司:为什么越来越多人选择单干', dir: '第五篇|真实的社会/第10章|未来职业的变化趋势', file: '10.2 一人公司:为什么越来越多人选择单干.md', sortOrder: 55 }, + { id: '10.3', partId: 'part-5', partTitle: '第五篇|真实的社会', chapterId: 'chapter-10', chapterTitle: '第10章|未来职业的变化趋势', sectionTitle: '为什么链接能力会成为第一价值', dir: '第五篇|真实的社会/第10章|未来职业的变化趋势', file: '10.3 为什么链接能力会成为第一价值.md', sortOrder: 56 }, + { id: '10.4', partId: 'part-5', partTitle: '第五篇|真实的社会', chapterId: 'chapter-10', chapterTitle: '第10章|未来职业的变化趋势', sectionTitle: '新型公司:Soul-飞书-线下的三位一体', dir: '第五篇|真实的社会/第10章|未来职业的变化趋势', file: '10.4 新型公司:Soul-飞书-线下的三位一体.md', sortOrder: 57 }, + + { id: '11.1', partId: 'part-5', partTitle: '第五篇|真实的社会', chapterId: 'chapter-11', chapterTitle: '第11章|中国社会商业生态的未来', sectionTitle: '私域经济:为什么流量越来越贵', dir: '第五篇|真实的社会/第11章|中国社会商业生态的未来', file: '11.1 私域经济:为什么流量越来越贵.md', sortOrder: 58 }, + { id: '11.2', partId: 'part-5', partTitle: '第五篇|真实的社会', chapterId: 'chapter-11', chapterTitle: '第11章|中国社会商业生态的未来', sectionTitle: '银发经济与孤独经济:两个被忽视的万亿市场', dir: '第五篇|真实的社会/第11章|中国社会商业生态的未来', file: '11.2 银发经济与孤独经济:两个被忽视的万亿市场.md', sortOrder: 59 }, + { id: '11.3', partId: 'part-5', partTitle: '第五篇|真实的社会', chapterId: 'chapter-11', chapterTitle: '第11章|中国社会商业生态的未来', sectionTitle: '流量红利的终局', dir: '第五篇|真实的社会/第11章|中国社会商业生态的未来', file: '11.3 流量红利的终局.md', sortOrder: 60 }, + { id: '11.4', partId: 'part-5', partTitle: '第五篇|真实的社会', chapterId: 'chapter-11', chapterTitle: '第11章|中国社会商业生态的未来', sectionTitle: '大模型 + 供应链的组合拳', dir: '第五篇|真实的社会/第11章|中国社会商业生态的未来', file: '11.4 大模型 + 供应链的组合拳.md', sortOrder: 61 }, + { id: '11.5', partId: 'part-5', partTitle: '第五篇|真实的社会', chapterId: 'chapter-11', chapterTitle: '第11章|中国社会商业生态的未来', sectionTitle: '社会分层的最终逻辑', dir: '第五篇|真实的社会/第11章|中国社会商业生态的未来', file: '11.5 社会分层的最终逻辑.md', sortOrder: 62 }, + + // 尾声 + { id: 'epilogue', partId: 'outro', partTitle: '尾声', chapterId: 'epilogue', chapterTitle: '尾声', sectionTitle: '这本书的真实目的', dir: '', file: '尾声|这本书的真实目的.md', sortOrder: 63 }, + + // 附录 + { id: 'appendix-1', partId: 'appendix', partTitle: '附录', chapterId: 'appendix', chapterTitle: '附录', sectionTitle: 'Soul派对房精选对话', dir: '附录', file: '附录1|Soul派对房精选对话.md', sortOrder: 64 }, + { id: 'appendix-2', partId: 'appendix', partTitle: '附录', chapterId: 'appendix', chapterTitle: '附录', sectionTitle: '创业者自检清单', dir: '附录', file: '附录2|创业者自检清单.md', sortOrder: 65 }, + { id: 'appendix-3', partId: 'appendix', partTitle: '附录', chapterId: 'appendix', chapterTitle: '附录', sectionTitle: '本书提到的工具和资源', dir: '附录', file: '附录3|本书提到的工具和资源.md', sortOrder: 66 }, +] + +async function main() { + console.log('🚀 开始迁移章节内容到数据库...') + console.log(`📁 Book目录: ${BOOK_DIR}`) + console.log(`📊 共${CHAPTERS_CONFIG.length}个章节\n`) + + // 先连接MySQL(不指定数据库),创建数据库 + const initConnection = await mysql.createConnection({ + host: DB_CONFIG.host, + port: DB_CONFIG.port, + user: DB_CONFIG.user, + password: DB_CONFIG.password, + charset: DB_CONFIG.charset, + }) + + await initConnection.execute(`CREATE DATABASE IF NOT EXISTS soul_miniprogram CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci`) + console.log('✅ 数据库 soul_miniprogram 创建/确认成功') + await initConnection.end() + + // 连接数据库 + const connection = await mysql.createConnection(DB_CONFIG) + console.log('✅ 数据库连接成功\n') + + // 创建表(如果不存在) + await connection.execute(` + CREATE TABLE IF NOT EXISTS chapters ( + id VARCHAR(20) PRIMARY KEY COMMENT '章节ID,如1.1、preface等', + part_id VARCHAR(20) NOT NULL COMMENT '所属篇ID,如part-1', + part_title VARCHAR(100) NOT NULL COMMENT '篇标题,如第一篇|真实的人', + chapter_id VARCHAR(20) NOT NULL COMMENT '所属章ID,如chapter-1', + chapter_title VARCHAR(200) NOT NULL COMMENT '章标题,如第1章|人与人之间的底层逻辑', + section_title VARCHAR(200) NOT NULL COMMENT '节标题', + content LONGTEXT NOT NULL COMMENT '章节正文内容(Markdown格式)', + word_count INT DEFAULT 0 COMMENT '字数统计', + is_free BOOLEAN DEFAULT FALSE COMMENT '是否免费章节', + price DECIMAL(10,2) DEFAULT 1.00 COMMENT '单章价格', + sort_order INT DEFAULT 0 COMMENT '排序顺序', + status ENUM('draft', 'published', 'archived') DEFAULT 'published' COMMENT '状态', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + INDEX idx_part_id (part_id), + INDEX idx_chapter_id (chapter_id), + INDEX idx_status (status), + INDEX idx_sort_order (sort_order) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci + `) + console.log('✅ chapters表创建/确认成功\n') + + // 迁移章节 + let successCount = 0 + let errorCount = 0 + + for (const config of CHAPTERS_CONFIG) { + const filePath = path.join(BOOK_DIR, config.dir, config.file) + + try { + if (!fs.existsSync(filePath)) { + console.log(`⚠️ 文件不存在: ${config.id} - ${config.file}`) + errorCount++ + continue + } + + const content = fs.readFileSync(filePath, 'utf-8') + const wordCount = content.replace(/\s/g, '').length + const isFree = FREE_CHAPTERS.includes(config.id) + + // 使用 REPLACE INTO 实现更新或插入 + await connection.execute(` + REPLACE INTO chapters (id, part_id, part_title, chapter_id, chapter_title, section_title, content, word_count, is_free, price, sort_order, status) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'published') + `, [ + config.id, + config.partId, + config.partTitle, + config.chapterId, + config.chapterTitle, + config.sectionTitle, + content, + wordCount, + isFree, + isFree ? 0 : 1, + config.sortOrder + ]) + + console.log(`✅ ${config.id}: ${config.sectionTitle} (${wordCount}字${isFree ? ' 免费' : ''})`) + successCount++ + } catch (error) { + console.log(`❌ ${config.id}: ${error.message}`) + errorCount++ + } + } + + console.log('\n' + '='.repeat(50)) + console.log(`📊 迁移完成: 成功 ${successCount} 个, 失败 ${errorCount} 个`) + console.log('='.repeat(50)) + + // 查询验证 + const [rows] = await connection.execute('SELECT COUNT(*) as count FROM chapters') + console.log(`\n📚 数据库中共有 ${rows[0].count} 个章节`) + + await connection.end() + console.log('\n✅ 数据库连接已关闭') +} + +main().catch(console.error) diff --git a/开发文档/API/配置清单-完整版.md b/开发文档/API/配置清单-完整版.md new file mode 100644 index 0000000..1ca859a --- /dev/null +++ b/开发文档/API/配置清单-完整版.md @@ -0,0 +1,205 @@ +# Soul创业实验 - API密钥与配置清单 + +> 最后更新: 2026-01-25 +> 维护人: 卡若 +> ⚠️ 本文件包含敏感信息,请勿公开 + +--- + +## 一、企业信息 + +| 项目 | 值 | +|:---|:---| +| **企业名称** | 泉州市卡若网络技术有限公司 | +| **联系电话** | 15880802661 | +| **微信号** | 28533368 | +| **邮箱** | zhiqun@qq.com / zhengzhiqun@vip.qq.com | + +--- + +## 二、微信生态 + +### 2.1 小程序(Soul创业实验) + +| 项目 | 值 | 备注 | +|:---|:---|:---| +| **AppID** | `wxb8bbb2b10dec74aa` | 小程序ID | +| **AppSecret** | `3c1fb1f63e6e052222bbcead9d07fe0c` | 小程序密钥 | +| **支付绑定状态** | 🟡 审核中 | 2026-01-25 09:43:59 提交 | + +### 2.2 服务号(玩值) + +| 项目 | 值 | 备注 | +|:---|:---|:---| +| **AppID** | `wx7c0dbf34ddba300d` | 服务号AppID | +| **AppSecret** | `f865ef18c43dfea6cbe3b1f1aebdb82e` | 服务号密钥 | +| **支付绑定状态** | ✅ 已绑定 | 绑定AppID: wx3e31b068be59ddc1 | + +### 2.3 网站应用 + +| 项目 | 值 | +|:---|:---| +| **AppID** | `wx432c93e275548671` | +| **AppSecret** | `25b7e7fdb7998e5107e242ebb6ddabd0` | + +### 2.4 微信支付 + +| 项目 | 值 | 备注 | +|:---|:---|:---| +| **商户号** | `1318592501` | 主体: 泉州市卡若网络技术有限公司 | +| **API密钥(v2)** | `wx3e31b068be59ddc131b068be59ddc2` | 32位 | +| **MP文件验证码** | `SP8AfZJyAvprRORT` | | +| **支付回调地址** | `https://soul.quwanzhi.com/api/miniprogram/pay/notify` | | + +#### 已绑定AppID + +| AppID | 类型 | 状态 | +|:---|:---|:---| +| `wx3e31b068be59ddc1` | 服务号 | ✅ 已关联 | +| `wxb8bbb2b10dec74aa` | 小程序 | 🟡 审核中 | + +--- + +## 三、支付宝 + +| 项目 | 值 | +|:---|:---| +| **PID** | `2088511801157159` | +| **MD5密钥** | `lz6ey1h3kl9zqkgtjz3avb5gk37wzbrp` | +| **账户** | zhengzhiqun@vip.qq.com | + +--- + +## 四、云服务 + +### 4.1 腾讯云 + +| 项目 | 值 | +|:---|:---| +| **APPID** | `1251077262` | +| **SecretId** | `AKIDjc6yO3nPeOuK2OKsJPBBVbTiiz0aPNHl` | +| **SecretKey** | *(见用户规则)* | + +### 4.2 阿里云 + +| 项目 | 值 | +|:---|:---| +| **AccessKey ID** | `LTAI5t9zkiWmFtHG8qmtdysW` | +| **AccessKey Secret** | `xxjXnZGLNvA2zDkj0aEBSQm3XZAaro` | + +--- + +## 五、数据库 + +### 5.1 腾讯云MySQL(生产环境) + +| 项目 | 值 | +|:---|:---| +| **主机** | `56b4c23f6853c.gz.cdb.myqcloud.com` | +| **端口** | `14413` | +| **数据库** | `soul_miniprogram` | +| **用户名** | `cdb_outerroot` | +| **密码** | `Zhiqun1984` | +| **字符集** | `utf8mb4` | + +#### 数据库表 + +| 表名 | 说明 | +|:---|:---| +| `users` | 用户表 | +| `orders` | 订单表 | +| `referral_bindings` | 推广绑定关系 | +| `match_records` | 匹配记录 | +| `system_config` | 系统配置 | +| `chapters` | **章节内容表(新)** | + +### 5.2 卡若私域数据库(内网) + +| 项目 | 值 | +|:---|:---| +| **主机** | `10.88.182.62` | +| **端口** | `3306` | +| **用户名** | `root` | +| **密码** | `Vtka(agu)-1` | + +--- + +## 六、AI服务 + +### 6.1 v0 API + +| 项目 | 值 | +|:---|:---| +| **API地址** | `https://api.v0.dev/v1` | +| **API Key** | `v1:C6mw1SlvXsJdlO4VFEXSQEVf:519gA0DPqIMbjvfMh7CXf4B2` | +| **默认模型** | `claude-opus` | + +--- + +## 七、开发工具 + +### 7.1 GitHub + +| 项目 | 值 | +|:---|:---| +| **Token** | `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` | + +--- + +## 八、项目部署信息 + +| 项目 | 值 | +|:---|:---| +| **域名** | `soul.quwanzhi.com` | +| **协议** | HTTPS | +| **服务器** | 宝塔面板 | +| **部署方式** | GitHub Webhook 自动部署 | + +--- + +## 九、邮箱账户 + +| 邮箱 | 密码 | +|:---|:---| +| `zhiqun@qq.com` | `#vtk();1984` | +| `zhengzhiqun@vip.qq.com` | `#vtk();1984` | +| `15880802661@qq.com` | `#vtk();1984` | + +--- + +## 十、配置代码引用 + +### 小程序支付配置 + +```typescript +// lib/payment/wechat-miniprogram.ts +const WECHAT_PAY_CONFIG = { + appId: 'wxb8bbb2b10dec74aa', // 小程序AppID + appSecret: '3c1fb1f63e6e052222bbcead9d07fe0c', // 小程序AppSecret + mchId: '1318592501', // 商户号 + mchKey: 'wx3e31b068be59ddc131b068be59ddc2', // API密钥(v2) + notifyUrl: 'https://soul.quwanzhi.com/api/miniprogram/pay/notify', +} +``` + +### 数据库配置 + +```typescript +// lib/db.ts +const DB_CONFIG = { + host: '56b4c23f6853c.gz.cdb.myqcloud.com', + port: 14413, + user: 'cdb_outerroot', + password: 'Zhiqun1984', + database: 'soul_miniprogram', + charset: 'utf8mb4', +} +``` + +--- + +## 更新日志 + +| 日期 | 更新内容 | +|:---|:---| +| 2026-01-25 | 创建完整配置清单;小程序支付绑定申请中;章节表迁移完成 |