148 lines
4.8 KiB
TypeScript
148 lines
4.8 KiB
TypeScript
// app/api/wechat/login/route.ts
|
||
// 微信小程序登录接口
|
||
|
||
import { NextRequest, NextResponse } from 'next/server'
|
||
import { query } from '@/lib/db'
|
||
|
||
// 使用真实的小程序AppID和Secret
|
||
const APPID = process.env.WECHAT_APPID || 'wxb8bbb2b10dec74aa'
|
||
const SECRET = process.env.WECHAT_APPSECRET || '3c1fb1f63e6e052222bbcead9d07fe0c'
|
||
|
||
// POST: 微信小程序登录
|
||
export async function POST(req: NextRequest) {
|
||
try {
|
||
const body = await req.json()
|
||
const { code, referralCode } = body
|
||
|
||
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
|
||
|
||
// 生成token
|
||
const token = Buffer.from(`${openid}:${Date.now()}`).toString('base64')
|
||
|
||
// 查询或创建用户
|
||
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)
|
||
|
||
// 注意:推荐绑定逻辑已移至 /api/referral/bind,这里只创建用户
|
||
// 如果有 referralCode,会在前端调用 /api/referral/bind 建立绑定关系
|
||
|
||
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, session_key, nickname,
|
||
'https://picsum.photos/200/200?random=' + openid.substr(-2),
|
||
userReferralCode
|
||
])
|
||
|
||
// 获取新创建的用户
|
||
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()
|
||
}
|
||
}
|
||
|
||
// 统一用户数据格式
|
||
const responseUser = {
|
||
id: user.id,
|
||
openId: user.open_id || openid,
|
||
unionid,
|
||
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
|
||
}
|
||
|
||
return NextResponse.json({
|
||
success: true,
|
||
token,
|
||
user: responseUser,
|
||
isNewUser,
|
||
message: isNewUser ? '注册成功' : '登录成功'
|
||
})
|
||
} 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
|
||
}
|