2026-01-23 05:44:21 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 小程序登录API
|
|
|
|
|
|
* 使用code换取openId和session_key
|
|
|
|
|
|
*
|
|
|
|
|
|
* 小程序配置:
|
|
|
|
|
|
* - AppID: wxb8bbb2b10dec74aa
|
|
|
|
|
|
* - AppSecret: 85d3fa31584d06acdb1de4a597d25b7b
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
import { NextResponse } from 'next/server'
|
|
|
|
|
|
|
|
|
|
|
|
const MINIPROGRAM_CONFIG = {
|
|
|
|
|
|
appId: 'wxb8bbb2b10dec74aa',
|
2026-01-25 11:40:35 +08:00
|
|
|
|
appSecret: '3c1fb1f63e6e052222bbcead9d07fe0c', // 2026-01-25 修正
|
2026-01-23 05:44:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 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 })
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-25 19:37:59 +08:00
|
|
|
|
// 创建或更新用户 - 连接数据库
|
|
|
|
|
|
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()
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 统一用户数据格式
|
|
|
|
|
|
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: typeof user.purchased_sections === 'string'
|
|
|
|
|
|
? JSON.parse(user.purchased_sections || '[]')
|
|
|
|
|
|
: (user.purchased_sections || []),
|
|
|
|
|
|
earnings: parseFloat(user.earnings) || 0,
|
|
|
|
|
|
pendingEarnings: parseFloat(user.pending_earnings) || 0,
|
|
|
|
|
|
referralCount: user.referral_count || 0,
|
|
|
|
|
|
createdAt: user.created_at
|
2026-01-23 05:44:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 生成token
|
|
|
|
|
|
const token = `tk_${openId.slice(-8)}_${Date.now()}`
|
|
|
|
|
|
|
2026-01-25 19:37:59 +08:00
|
|
|
|
console.log('[MiniLogin] 登录成功, userId:', responseUser.id, isNewUser ? '(新用户)' : '(老用户)')
|
2026-01-23 05:44:21 +08:00
|
|
|
|
|
|
|
|
|
|
return NextResponse.json({
|
|
|
|
|
|
success: true,
|
|
|
|
|
|
data: {
|
|
|
|
|
|
openId,
|
2026-01-25 19:37:59 +08:00
|
|
|
|
user: responseUser,
|
2026-01-23 05:44:21 +08:00
|
|
|
|
token,
|
2026-01-25 19:37:59 +08:00
|
|
|
|
},
|
|
|
|
|
|
isNewUser
|
2026-01-23 05:44:21 +08:00
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('[MiniLogin] 登录失败:', error)
|
|
|
|
|
|
return NextResponse.json({
|
|
|
|
|
|
success: false,
|
|
|
|
|
|
error: '登录失败'
|
|
|
|
|
|
}, { status: 500 })
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|