2025-12-24 14:41:11 +08:00

254 lines
6.7 KiB
TypeScript

import { Pool } from 'pg'
import crypto from 'crypto'
// 创建数据库连接池
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
max: 20, // 最大连接数
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 2000,
})
// 测试连接
pool.on('connect', () => {
console.log('✅ Database connected')
})
pool.on('error', (err) => {
console.error('❌ Unexpected database error:', err)
})
export default pool
// 用户接口
export interface User {
id: number
name: string
email: string
password: string
email_verified: boolean
created_at: Date
updated_at: Date
provider?: string
wechat_unionid?: string | null
wechat_openid?: string | null
}
// 订单接口
export interface Order {
id: number
order_no: string
user_id: number
product_type: string // 'standard' | 'deployment' | 'custom'
product_name: string
amount: number
status: string // 'pending' | 'paid' | 'failed' | 'cancelled'
payment_method?: string
trade_no?: string
callback_data?: string
created_at: Date
updated_at: Date
paid_at?: Date
}
// 数据库查询函数
export const db = {
// 根据邮箱查找用户
async findUserByEmail(email: string): Promise<User | null> {
const result = await pool.query(
'SELECT * FROM users WHERE email = $1',
[email]
)
return result.rows[0] || null
},
// 根据ID查找用户
async findUserById(id: number): Promise<User | null> {
const result = await pool.query(
'SELECT * FROM users WHERE id = $1',
[id]
)
return result.rows[0] || null
},
// 创建新用户
async createUser(
name: string,
email: string,
password: string
): Promise<User> {
const result = await pool.query(
'INSERT INTO users (name, email, password, email_verified, created_at, updated_at) VALUES ($1, $2, $3, $4, NOW(), NOW()) RETURNING *',
[name, email, password, false]
)
return result.rows[0]
},
// 更新用户
async updateUser(id: number, data: Partial<User>): Promise<User> {
const fields = []
const values = []
let index = 1
if (data.name !== undefined) {
fields.push(`name = $${index++}`)
values.push(data.name)
}
if (data.email !== undefined) {
fields.push(`email = $${index++}`)
values.push(data.email)
}
if (data.password !== undefined) {
fields.push(`password = $${index++}`)
values.push(data.password)
}
if (data.email_verified !== undefined) {
fields.push(`email_verified = $${index++}`)
values.push(data.email_verified)
}
fields.push(`updated_at = NOW()`)
values.push(id)
const query = `UPDATE users SET ${fields.join(', ')} WHERE id = $${index} RETURNING *`
const result = await pool.query(query, values)
return result.rows[0]
},
// 删除用户
async deleteUser(id: number): Promise<boolean> {
const result = await pool.query('DELETE FROM users WHERE id = $1', [id])
return (result.rowCount ?? 0) > 0
},
// 获取所有用户(用于管理)
async getAllUsers(): Promise<User[]> {
const result = await pool.query(
'SELECT id, name, email, email_verified, created_at FROM users ORDER BY created_at DESC'
)
return result.rows
},
// ========== 微信用户相关 ==========
async findUserByWechat(unionid?: string | null, openid?: string | null): Promise<User | null> {
if (unionid) {
const byUnion = await pool.query('SELECT * FROM users WHERE wechat_unionid = $1', [unionid])
if (byUnion.rows[0]) return byUnion.rows[0]
}
if (openid) {
const byOpen = await pool.query('SELECT * FROM users WHERE wechat_openid = $1', [openid])
if (byOpen.rows[0]) return byOpen.rows[0]
}
return null
},
async upsertWechatUser(params: {
name?: string | null
email?: string | null
unionid?: string | null
openid?: string | null
hashedPassword: string
}): Promise<User> {
const { name, email, unionid, openid, hashedPassword } = params
const existing = await this.findUserByWechat(unionid ?? undefined, openid ?? undefined)
if (existing) {
return existing
}
const result = await pool.query(
`INSERT INTO users (name, email, password, email_verified, provider, wechat_unionid, wechat_openid, created_at, updated_at)
VALUES ($1, $2, $3, $4, $5, $6, $7, NOW(), NOW())
RETURNING *`,
[
name || '微信用户',
email || `${openid || unionid || crypto.randomUUID()}@wechat.local`,
hashedPassword,
true,
'wechat',
unionid || null,
openid || null,
]
)
return result.rows[0]
},
// ========== 订单相关操作 ==========
// 创建订单
async createOrder(
orderNo: string,
userId: number,
productType: string,
productName: string,
amount: number
): Promise<Order> {
const result = await pool.query(
'INSERT INTO orders (order_no, user_id, product_type, product_name, amount, status, created_at, updated_at) VALUES ($1, $2, $3, $4, $5, $6, NOW(), NOW()) RETURNING *',
[orderNo, userId, productType, productName, amount, 'pending']
)
return result.rows[0]
},
// 根据订单号查找订单
async findOrderByOrderNo(orderNo: string): Promise<Order | null> {
const result = await pool.query(
'SELECT * FROM orders WHERE order_no = $1',
[orderNo]
)
return result.rows[0] || null
},
// 根据ID查找订单
async findOrderById(id: number): Promise<Order | null> {
const result = await pool.query(
'SELECT * FROM orders WHERE id = $1',
[id]
)
return result.rows[0] || null
},
// 更新订单状态
async updateOrderStatus(
orderNo: string,
status: string,
tradeNo?: string,
paymentMethod?: string,
callbackData?: string
): Promise<Order> {
const updates = ['status = $2', 'updated_at = NOW()']
const values: any[] = [orderNo, status]
let index = 3
if (tradeNo) {
updates.push(`trade_no = $${index++}`)
values.push(tradeNo)
}
if (paymentMethod) {
updates.push(`payment_method = $${index++}`)
values.push(paymentMethod)
}
if (callbackData) {
updates.push(`callback_data = $${index++}`)
values.push(callbackData)
}
if (status === 'paid') {
updates.push('paid_at = NOW()')
}
const query = `UPDATE orders SET ${updates.join(', ')} WHERE order_no = $1 RETURNING *`
const result = await pool.query(query, values)
return result.rows[0]
},
// 获取用户的订单列表
async getUserOrders(userId: number): Promise<Order[]> {
const result = await pool.query(
'SELECT * FROM orders WHERE user_id = $1 ORDER BY created_at DESC',
[userId]
)
return result.rows
},
}