Files
soul/app/api/db/config/route.ts
卡若 ac24853aa6 feat: 管理后台增加免费章节和小程序配置
1. 系统设置页新增免费章节管理(可动态添加/删除)
2. 新增小程序配置项(API域名、购买优惠、绑定天数等)
3. 前端从后端读取免费章节配置
4. 配置API支持新格式
2026-01-25 21:09:20 +08:00

351 lines
9.0 KiB
TypeScript

/**
* 系统配置API
* 优先读取数据库配置,失败时读取本地默认配置
* 支持配置的增删改查
*/
import { NextRequest, NextResponse } from 'next/server'
import { query, getConfig, setConfig } from '@/lib/db'
// 本地默认配置(作为数据库备份)
const DEFAULT_CONFIGS: Record<string, any> = {
// 站点配置
site_config: {
siteName: 'Soul创业派对',
siteDescription: '来自派对房的真实商业故事',
logo: '/icon.svg',
keywords: ['创业', 'Soul', '私域运营', '商业案例'],
icp: '',
analytics: ''
},
// 匹配功能配置
match_config: {
matchTypes: [
{ id: 'partner', label: '创业合伙', matchLabel: '创业伙伴', icon: '⭐', matchFromDB: true, showJoinAfterMatch: false, enabled: true },
{ id: 'investor', label: '资源对接', matchLabel: '资源对接', icon: '👥', matchFromDB: false, showJoinAfterMatch: true, enabled: true },
{ id: 'mentor', label: '导师顾问', matchLabel: '商业顾问', icon: '❤️', matchFromDB: false, showJoinAfterMatch: true, enabled: true },
{ id: 'team', label: '团队招募', matchLabel: '加入项目', icon: '🎮', matchFromDB: false, showJoinAfterMatch: true, enabled: true }
],
freeMatchLimit: 3,
matchPrice: 1,
settings: {
enableFreeMatches: true,
enablePaidMatches: true,
maxMatchesPerDay: 10
}
},
// 分销配置
referral_config: {
distributorShare: 90,
minWithdrawAmount: 10,
bindingDays: 30,
userDiscount: 5,
enableAutoWithdraw: false
},
// 价格配置
price_config: {
sectionPrice: 1,
fullBookPrice: 9.9,
premiumBookPrice: 19.9,
matchPrice: 1
},
// 支付配置
payment_config: {
wechat: {
enabled: true,
appId: 'wx432c93e275548671',
mchId: '1318592501'
},
alipay: {
enabled: true,
pid: '2088511801157159'
},
wechatGroupUrl: '' // 支付成功后跳转的微信群链接
},
// 书籍配置
book_config: {
totalSections: 62,
freeSections: ['preface', 'epilogue', '1.1', 'appendix-1', 'appendix-2', 'appendix-3'],
latestSectionId: '9.14'
}
}
/**
* GET - 获取配置
* 参数: key - 配置键名,不传则返回所有配置
*/
export async function GET(request: NextRequest) {
const { searchParams } = new URL(request.url)
const key = searchParams.get('key')
const forceLocal = searchParams.get('forceLocal') === 'true'
try {
if (key) {
// 获取单个配置
let config = null
if (!forceLocal) {
// 优先从数据库读取
try {
config = await getConfig(key)
} catch (e) {
console.log(`[Config API] 数据库读取${key}失败,使用本地配置`)
}
}
// 数据库没有则使用本地默认
if (!config) {
config = DEFAULT_CONFIGS[key] || null
}
if (config) {
return NextResponse.json({
success: true,
key,
config,
source: config === DEFAULT_CONFIGS[key] ? 'local' : 'database'
})
}
return NextResponse.json({
success: false,
error: '配置不存在'
}, { status: 404 })
}
// 获取所有配置
const allConfigs: Record<string, any> = {}
const sources: Record<string, string> = {}
for (const configKey of Object.keys(DEFAULT_CONFIGS)) {
let config = null
if (!forceLocal) {
try {
config = await getConfig(configKey)
} catch (e) {
// 忽略数据库错误
}
}
if (config) {
allConfigs[configKey] = config
sources[configKey] = 'database'
} else {
allConfigs[configKey] = DEFAULT_CONFIGS[configKey]
sources[configKey] = 'local'
}
}
// 获取小程序配置
let mpConfig = null
try {
mpConfig = await getConfig('mp_config')
} catch (e) {}
// 提取前端需要的格式
const bookConfig = allConfigs.book_config || DEFAULT_CONFIGS.book_config
return NextResponse.json({
success: true,
configs: allConfigs,
sources,
// 前端直接使用的格式
freeChapters: bookConfig.freeSections || DEFAULT_CONFIGS.book_config.freeSections,
mpConfig: mpConfig || {
appId: 'wxb8bbb2b10dec74aa',
apiDomain: 'https://soul.quwanzhi.com',
buyerDiscount: 5,
referralBindDays: 30,
minWithdraw: 10
}
})
} catch (error) {
console.error('[Config API] GET错误:', error)
return NextResponse.json({
success: false,
error: '获取配置失败: ' + (error as Error).message
}, { status: 500 })
}
}
/**
* POST - 保存配置到数据库
* 支持两种格式:
* 1. { key, config } - 单个配置
* 2. { freeChapters, mpConfig } - 批量配置
*/
export async function POST(request: NextRequest) {
try {
const body = await request.json()
// 支持批量配置格式
if (body.freeChapters || body.mpConfig) {
let successCount = 0
// 保存免费章节配置
if (body.freeChapters) {
const bookConfig = {
...DEFAULT_CONFIGS.book_config,
freeSections: body.freeChapters
}
const success = await setConfig('book_config', bookConfig, '书籍配置-免费章节')
if (success) successCount++
}
// 保存小程序配置
if (body.mpConfig) {
const success = await setConfig('mp_config', body.mpConfig, '小程序配置')
if (success) successCount++
}
return NextResponse.json({
success: true,
message: `配置保存成功 (${successCount}项)`,
successCount
})
}
// 原有的单配置格式
const { key, config, description } = body
if (!key || !config) {
return NextResponse.json({
success: false,
error: '配置键名和配置值不能为空'
}, { status: 400 })
}
// 保存到数据库
const success = await setConfig(key, config, description)
if (success) {
return NextResponse.json({
success: true,
message: '配置保存成功',
key
})
} else {
return NextResponse.json({
success: false,
error: '配置保存失败'
}, { status: 500 })
}
} catch (error) {
console.error('[Config API] POST错误:', error)
return NextResponse.json({
success: false,
error: '保存配置失败: ' + (error as Error).message
}, { status: 500 })
}
}
/**
* PUT - 批量更新配置
*/
export async function PUT(request: NextRequest) {
try {
const body = await request.json()
const { configs } = body
if (!configs || typeof configs !== 'object') {
return NextResponse.json({
success: false,
error: '配置数据格式错误'
}, { status: 400 })
}
let successCount = 0
let failedCount = 0
for (const [key, config] of Object.entries(configs)) {
try {
const success = await setConfig(key, config)
if (success) {
successCount++
} else {
failedCount++
}
} catch (e) {
failedCount++
}
}
return NextResponse.json({
success: true,
message: `配置更新完成:成功${successCount}个,失败${failedCount}`,
successCount,
failedCount
})
} catch (error) {
console.error('[Config API] PUT错误:', error)
return NextResponse.json({
success: false,
error: '更新配置失败: ' + (error as Error).message
}, { status: 500 })
}
}
/**
* DELETE - 删除配置(恢复为本地默认)
*/
export async function DELETE(request: NextRequest) {
const { searchParams } = new URL(request.url)
const key = searchParams.get('key')
if (!key) {
return NextResponse.json({
success: false,
error: '配置键名不能为空'
}, { status: 400 })
}
try {
await query('DELETE FROM system_config WHERE config_key = ?', [key])
return NextResponse.json({
success: true,
message: '配置已删除,将使用本地默认值',
key
})
} catch (error) {
console.error('[Config API] DELETE错误:', error)
return NextResponse.json({
success: false,
error: '删除配置失败: ' + (error as Error).message
}, { status: 500 })
}
}
/**
* 初始化:将本地配置同步到数据库
*/
export async function syncLocalToDatabase() {
console.log('[Config] 开始同步本地配置到数据库...')
for (const [key, config] of Object.entries(DEFAULT_CONFIGS)) {
try {
// 检查数据库是否已有该配置
const existing = await getConfig(key)
if (!existing) {
// 数据库没有,则写入
await setConfig(key, config, `默认${key}配置`)
console.log(`[Config] 同步配置: ${key}`)
}
} catch (e) {
console.error(`[Config] 同步${key}失败:`, e)
}
}
console.log('[Config] 配置同步完成')
}