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

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

@@ -63,8 +63,6 @@ export async function query(sql: string, params?: any[]) {
}
// mysql2 内部会读 params.length不能传 undefined
const safeParams = Array.isArray(params) ? params : []
console.log('[DB Query] SQL:', sql.slice(0, 100))
console.log('[DB Query] Params Type:', typeof params, '| Is Array:', Array.isArray(params), '| safeParams:', safeParams)
try {
const [results] = await connection.execute(sql, safeParams)
// 确保调用方拿到的始终是数组,避免 undefined.length 报错

140
lib/prisma-helpers.ts Normal file
View File

@@ -0,0 +1,140 @@
/**
* Prisma 辅助函数库
* 提供常用的数据库操作封装
*/
import { prisma } from '@/lib/prisma'
/**
* 读取系统配置
*/
export async function getPrismaConfig(key: string): Promise<any> {
try {
const config = await prisma.system_config.findUnique({
where: { config_key: key }
})
return config?.config_value
} catch (e) {
console.warn(`[Config] 读取配置 ${key} 失败:`, e)
return null
}
}
/**
* 更新系统配置
*/
export async function setPrismaConfig(key: string, value: any, description?: string): Promise<void> {
await prisma.system_config.upsert({
where: { config_key: key },
update: {
config_value: value,
description: description || null,
updated_at: new Date()
},
create: {
config_key: key,
config_value: value,
description: description || null
}
})
}
/**
* 查询用户(通过 ID 或 openId
*/
export async function findUserByIdOrOpenId(userId?: string, openId?: string) {
if (!userId && !openId) return null
return await prisma.users.findFirst({
where: userId ? { id: userId } : { open_id: openId }
})
}
/**
* 批量查询用户
*/
export async function findUsersByIds(userIds: string[]) {
return await prisma.users.findMany({
where: { id: { in: userIds } }
})
}
/**
* 记录用户追踪
*/
export async function trackUserAction(
userId: string,
action: string,
chapterId?: string,
target?: string,
extraData?: any
) {
try {
await prisma.user_tracks.create({
data: {
id: `track_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
user_id: userId,
action,
chapter_id: chapterId || null,
target: target || null,
extra_data: extraData || null
}
})
} catch (e) {
console.error('[Track] 记录失败:', e)
}
}
/**
* 检查用户是否购买章节
*/
export async function hasUserPurchasedChapter(userId: string, chapterId: string): Promise<boolean> {
const user = await prisma.users.findUnique({
where: { id: userId },
select: { has_full_book: true, purchased_sections: true }
})
if (!user) return false
if (user.has_full_book) return true
const purchasedSections = user.purchased_sections as any
if (Array.isArray(purchasedSections)) {
return purchasedSections.includes(chapterId)
}
return false
}
/**
* 获取用户购买的章节列表
*/
export async function getUserPurchasedChapters(userId: string): Promise<string[]> {
const user = await prisma.users.findUnique({
where: { id: userId },
select: { purchased_sections: true }
})
const sections = user?.purchased_sections as any
return Array.isArray(sections) ? sections : []
}
/**
* 添加用户购买的章节
*/
export async function addUserPurchasedChapter(userId: string, chapterId: string): Promise<void> {
const user = await prisma.users.findUnique({
where: { id: userId },
select: { purchased_sections: true }
})
const sections = user?.purchased_sections as any
const purchased = Array.isArray(sections) ? sections : []
if (!purchased.includes(chapterId)) {
purchased.push(chapterId)
await prisma.users.update({
where: { id: userId },
data: { purchased_sections: purchased }
})
}
}

36
lib/prisma.ts Normal file
View File

@@ -0,0 +1,36 @@
/**
* Prisma Client 单例实例
* Prisma 7 使用 engineType="client" 时必须提供 adapter
* 使用 @prisma/adapter-mariadb 连接 MySQL
*/
import { PrismaMariaDb } from '@prisma/adapter-mariadb'
import { PrismaClient } from '@/lib/generated/prisma'
const DEFAULT_DATABASE_URL =
'mysql://cdb_outerroot:Zhiqun1984@56b4c23f6853c.gz.cdb.myqcloud.com:14413/soul_miniprogram'
declare global {
// eslint-disable-next-line no-var
var prisma: PrismaClient | undefined
}
// Prisma 7 要求:使用 client 引擎时必须传入 adapter
const adapter = new PrismaMariaDb(
process.env.DATABASE_URL || DEFAULT_DATABASE_URL
)
const prismaInstance = new PrismaClient({
adapter,
log: process.env.NODE_ENV === 'development' ? ['error', 'warn'] : ['error'],
})
export const prisma = global.prisma || prismaInstance
if (process.env.NODE_ENV !== 'production') {
global.prisma = prisma
}
process.on('beforeExit', async () => {
await prisma.$disconnect()
})