Files
soul-yongping/app/api/miniprogram/login/route.ts

180 lines
5.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 小程序登录API
* 使用code换取openId和session_key
*
* 小程序配置:
* - AppID: wxb8bbb2b10dec74aa
* - AppSecret: 85d3fa31584d06acdb1de4a597d25b7b
*/
import { NextResponse } from 'next/server'
const MINIPROGRAM_CONFIG = {
appId: 'wxb8bbb2b10dec74aa',
appSecret: '3c1fb1f63e6e052222bbcead9d07fe0c', // 2026-01-25 修正
}
/**
* POST - 小程序登录获取openId
*/
export async function POST(request: Request) {
try {
const body = await request.json()
const { code } = body
if (!code) {
return NextResponse.json({
success: false,
error: '缺少登录code'
}, { status: 400 })
}
console.log('[MiniLogin] 收到登录请求, code:', code.slice(0, 10) + '...')
// 调用微信接口获取openId
const wxUrl = `https://api.weixin.qq.com/sns/jscode2session?appid=${MINIPROGRAM_CONFIG.appId}&secret=${MINIPROGRAM_CONFIG.appSecret}&js_code=${code}&grant_type=authorization_code`
const response = await fetch(wxUrl)
const data = await response.json()
console.log('[MiniLogin] 微信接口返回:', {
errcode: data.errcode,
errmsg: data.errmsg,
hasOpenId: !!data.openid,
})
if (data.errcode) {
return NextResponse.json({
success: false,
error: `微信登录失败: ${data.errmsg || data.errcode}`
}, { status: 400 })
}
const openId = data.openid
const sessionKey = data.session_key
const unionId = data.unionid
if (!openId) {
return NextResponse.json({
success: false,
error: '获取openId失败'
}, { status: 500 })
}
// 创建或更新用户 - 连接数据库
let user: any = null
let isNewUser = false
try {
const { query } = await import('@/lib/db')
// 查询用户是否存在
const existingUsers = await query('SELECT * FROM users WHERE open_id = ?', [openId]) as any[]
if (existingUsers.length > 0) {
// 用户已存在更新session_key
user = existingUsers[0]
await query('UPDATE users SET session_key = ?, updated_at = NOW() WHERE open_id = ?', [sessionKey, openId])
console.log('[MiniLogin] 用户已存在:', user.id)
} else {
// 创建新用户 - 使用openId作为用户ID与微信官方标识保持一致
isNewUser = true
const userId = openId // 直接使用openId作为用户ID
const referralCode = 'SOUL' + openId.slice(-6).toUpperCase()
const nickname = '微信用户' + openId.slice(-4)
await query(`
INSERT INTO users (
id, open_id, session_key, nickname, avatar, referral_code,
has_full_book, purchased_sections, earnings, pending_earnings, referral_count
) VALUES (?, ?, ?, ?, ?, ?, FALSE, '[]', 0, 0, 0)
`, [
userId, openId, sessionKey, nickname,
'', // 头像留空,等用户授权
referralCode
])
const newUsers = await query('SELECT * FROM users WHERE id = ?', [userId]) as any[]
user = newUsers[0]
console.log('[MiniLogin] 新用户创建成功, ID=openId:', userId.slice(0, 10) + '...')
}
} catch (dbError) {
console.error('[MiniLogin] 数据库操作失败:', dbError)
// 数据库失败时使用openId作为临时用户ID
user = {
id: openId, // 使用openId作为用户ID
open_id: openId,
nickname: '微信用户',
avatar: '',
referral_code: 'SOUL' + openId.slice(-6).toUpperCase(),
purchased_sections: '[]',
has_full_book: false,
earnings: 0,
pending_earnings: 0,
referral_count: 0,
created_at: new Date().toISOString()
}
}
// === ✅ 从 orders 表查询真实购买记录 ===
let purchasedSections: string[] = []
try {
const orderRows = await query(`
SELECT DISTINCT product_id
FROM orders
WHERE user_id = ?
AND status = 'paid'
AND product_type = 'section'
`, [user.id]) as any[]
purchasedSections = orderRows.map((row: any) => row.product_id).filter(Boolean)
console.log('[MiniLogin] 查询到已购章节:', purchasedSections.length, '个')
} catch (e) {
console.warn('[MiniLogin] 查询购买记录失败:', e)
// 降级到 users.purchased_sections 字段
purchasedSections = typeof user.purchased_sections === 'string'
? JSON.parse(user.purchased_sections || '[]')
: (user.purchased_sections || [])
}
// 统一用户数据格式
const responseUser = {
id: user.id,
openId: user.open_id || openId,
nickname: user.nickname,
avatar: user.avatar,
phone: user.phone,
wechatId: user.wechat_id,
referralCode: user.referral_code,
hasFullBook: user.has_full_book || false,
purchasedSections, // ✅ 使用从 orders 表查询的真实数据
earnings: parseFloat(user.earnings) || 0,
pendingEarnings: parseFloat(user.pending_earnings) || 0,
referralCount: user.referral_count || 0,
createdAt: user.created_at
}
// 生成token
const token = `tk_${openId.slice(-8)}_${Date.now()}`
console.log('[MiniLogin] 登录成功, userId:', responseUser.id, isNewUser ? '(新用户)' : '(老用户)')
return NextResponse.json({
success: true,
data: {
openId,
user: responseUser,
token,
},
isNewUser
})
} catch (error) {
console.error('[MiniLogin] 登录失败:', error)
return NextResponse.json({
success: false,
error: '登录失败'
}, { status: 500 })
}
}