2026-01-14 12:50:00 +08:00
|
|
|
|
// app/api/wechat/login/route.ts
|
|
|
|
|
|
// 微信小程序登录接口
|
|
|
|
|
|
|
|
|
|
|
|
import { NextRequest, NextResponse } from 'next/server'
|
2026-01-25 19:37:59 +08:00
|
|
|
|
import { query } from '@/lib/db'
|
2026-01-14 12:50:00 +08:00
|
|
|
|
|
2026-01-25 19:37:59 +08:00
|
|
|
|
// 使用真实的小程序AppID和Secret
|
|
|
|
|
|
const APPID = process.env.WECHAT_APPID || 'wxb8bbb2b10dec74aa'
|
|
|
|
|
|
const SECRET = process.env.WECHAT_APPSECRET || '3c1fb1f63e6e052222bbcead9d07fe0c'
|
2026-01-14 12:50:00 +08:00
|
|
|
|
|
|
|
|
|
|
// POST: 微信小程序登录
|
|
|
|
|
|
export async function POST(req: NextRequest) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const body = await req.json()
|
2026-01-25 19:37:59 +08:00
|
|
|
|
const { code, referralCode } = body
|
2026-01-14 12:50:00 +08:00
|
|
|
|
|
|
|
|
|
|
if (!code) {
|
|
|
|
|
|
return NextResponse.json(
|
|
|
|
|
|
{ error: '缺少code参数' },
|
|
|
|
|
|
{ status: 400 }
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 调用微信API获取session_key和openid
|
|
|
|
|
|
const wxUrl = `https://api.weixin.qq.com/sns/jscode2session?appid=${APPID}&secret=${SECRET}&js_code=${code}&grant_type=authorization_code`
|
|
|
|
|
|
|
|
|
|
|
|
const wxResponse = await fetch(wxUrl)
|
|
|
|
|
|
const wxData = await wxResponse.json()
|
|
|
|
|
|
|
|
|
|
|
|
if (wxData.errcode) {
|
|
|
|
|
|
console.error('微信登录失败:', wxData)
|
|
|
|
|
|
return NextResponse.json(
|
|
|
|
|
|
{ error: wxData.errmsg || '微信登录失败' },
|
|
|
|
|
|
{ status: 400 }
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const { openid, session_key, unionid } = wxData
|
|
|
|
|
|
|
2026-01-25 19:37:59 +08:00
|
|
|
|
// 生成token
|
2026-01-14 12:50:00 +08:00
|
|
|
|
const token = Buffer.from(`${openid}:${Date.now()}`).toString('base64')
|
2026-01-25 19:37:59 +08:00
|
|
|
|
|
|
|
|
|
|
// 查询或创建用户
|
|
|
|
|
|
let user: any = null
|
|
|
|
|
|
let isNewUser = false
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 先查询用户是否存在
|
|
|
|
|
|
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 = ?', [session_key, openid])
|
|
|
|
|
|
console.log('[WechatLogin] 用户已存在:', user.id)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 创建新用户
|
|
|
|
|
|
isNewUser = true
|
|
|
|
|
|
const userId = 'user_' + Date.now().toString(36) + Math.random().toString(36).substr(2, 6)
|
|
|
|
|
|
const userReferralCode = generateInviteCode(openid)
|
|
|
|
|
|
const nickname = '用户' + openid.substr(-4)
|
|
|
|
|
|
|
|
|
|
|
|
// 处理推荐绑定
|
|
|
|
|
|
let referredBy = null
|
|
|
|
|
|
if (referralCode) {
|
|
|
|
|
|
const referrers = await query('SELECT id FROM users WHERE referral_code = ?', [referralCode]) as any[]
|
|
|
|
|
|
if (referrers.length > 0) {
|
|
|
|
|
|
referredBy = referrers[0].id
|
|
|
|
|
|
// 更新推荐人的推广数量
|
|
|
|
|
|
await query('UPDATE users SET referral_count = referral_count + 1 WHERE id = ?', [referredBy])
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
await query(`
|
|
|
|
|
|
INSERT INTO users (
|
|
|
|
|
|
id, open_id, session_key, nickname, avatar, referral_code, referred_by,
|
|
|
|
|
|
has_full_book, purchased_sections, earnings, pending_earnings, referral_count
|
|
|
|
|
|
) VALUES (?, ?, ?, ?, ?, ?, ?, FALSE, '[]', 0, 0, 0)
|
|
|
|
|
|
`, [
|
|
|
|
|
|
userId, openid, session_key, nickname,
|
|
|
|
|
|
'https://picsum.photos/200/200?random=' + openid.substr(-2),
|
|
|
|
|
|
userReferralCode, referredBy
|
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
|
|
// 获取新创建的用户
|
|
|
|
|
|
const newUsers = await query('SELECT * FROM users WHERE id = ?', [userId]) as any[]
|
|
|
|
|
|
user = newUsers[0]
|
|
|
|
|
|
console.log('[WechatLogin] 新用户创建成功:', userId)
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (dbError) {
|
|
|
|
|
|
console.error('[WechatLogin] 数据库操作失败,使用临时用户:', dbError)
|
|
|
|
|
|
// 数据库失败时使用临时用户信息
|
|
|
|
|
|
user = {
|
|
|
|
|
|
id: openid,
|
|
|
|
|
|
open_id: openid,
|
|
|
|
|
|
nickname: '用户' + openid.substr(-4),
|
|
|
|
|
|
avatar: 'https://picsum.photos/200/200?random=' + openid.substr(-2),
|
|
|
|
|
|
referral_code: generateInviteCode(openid),
|
|
|
|
|
|
has_full_book: false,
|
|
|
|
|
|
purchased_sections: [],
|
|
|
|
|
|
earnings: 0,
|
|
|
|
|
|
pending_earnings: 0,
|
|
|
|
|
|
referral_count: 0,
|
|
|
|
|
|
created_at: new Date().toISOString()
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-01-14 12:50:00 +08:00
|
|
|
|
|
2026-01-25 19:37:59 +08:00
|
|
|
|
// 统一用户数据格式
|
|
|
|
|
|
const responseUser = {
|
|
|
|
|
|
id: user.id,
|
|
|
|
|
|
openId: user.open_id || openid,
|
2026-01-14 12:50:00 +08:00
|
|
|
|
unionid,
|
2026-01-25 19:37:59 +08:00
|
|
|
|
nickname: user.nickname,
|
|
|
|
|
|
avatar: user.avatar,
|
|
|
|
|
|
phone: user.phone,
|
|
|
|
|
|
wechatId: user.wechat_id,
|
|
|
|
|
|
referralCode: user.referral_code,
|
|
|
|
|
|
referredBy: user.referred_by,
|
|
|
|
|
|
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-14 12:50:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return NextResponse.json({
|
|
|
|
|
|
success: true,
|
|
|
|
|
|
token,
|
2026-01-25 19:37:59 +08:00
|
|
|
|
user: responseUser,
|
|
|
|
|
|
isNewUser,
|
|
|
|
|
|
message: isNewUser ? '注册成功' : '登录成功'
|
2026-01-14 12:50:00 +08:00
|
|
|
|
})
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('登录接口错误:', error)
|
|
|
|
|
|
return NextResponse.json(
|
|
|
|
|
|
{ error: '服务器错误' },
|
|
|
|
|
|
{ status: 500 }
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 生成邀请码
|
|
|
|
|
|
function generateInviteCode(openid: string): string {
|
|
|
|
|
|
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
|
|
|
|
|
|
const hash = openid.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0)
|
|
|
|
|
|
let code = ''
|
|
|
|
|
|
|
|
|
|
|
|
for (let i = 0; i < 6; i++) {
|
|
|
|
|
|
code += chars.charAt((hash + i) % chars.length)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return code
|
|
|
|
|
|
}
|