feat: unified gravity system - VIP members consume gravity instead of unlimited; add monthly gravity top-up cron
This commit is contained in:
@@ -46,14 +46,13 @@ export class UserService {
|
||||
|
||||
let user = await this.userModel.findOne({ phone }).exec()
|
||||
if (!user) {
|
||||
user = await this.userModel.create({ phone, nickname: `用户${phone.slice(-4)}` })
|
||||
user = await this.userModel.create({ phone, nickname: `用户${phone.slice(-4)}`, gravity: 5 })
|
||||
}
|
||||
|
||||
return this.generateAuthResponse(user)
|
||||
}
|
||||
|
||||
async loginByWx(code: string) {
|
||||
// WeChat silent login - exchange code for openid
|
||||
async loginByWx(code: string, userId?: string) {
|
||||
const appid = process.env.WX_APPID
|
||||
const secret = process.env.WX_SECRET
|
||||
if (!appid || !secret) {
|
||||
@@ -70,15 +69,59 @@ export class UserService {
|
||||
}
|
||||
|
||||
const openid = wxData.openid
|
||||
|
||||
if (userId) {
|
||||
const user = await this.userModel.findById(userId).exec()
|
||||
if (!user) throw new HttpException('用户不存在', HttpStatus.NOT_FOUND)
|
||||
if (user.wxOpenid) throw new HttpException('该账号已绑定微信', HttpStatus.CONFLICT)
|
||||
user.wxOpenid = openid
|
||||
await user.save()
|
||||
return this.generateAuthResponse(user)
|
||||
}
|
||||
|
||||
let user = await this.userModel.findOne({ wxOpenid: openid }).exec()
|
||||
if (!user) {
|
||||
user = await this.userModel.create({ wxOpenid: openid, nickname: '微信用户' })
|
||||
user = await this.userModel.create({ wxOpenid: openid, nickname: '微信用户', gravity: 5 })
|
||||
}
|
||||
|
||||
return this.generateAuthResponse(user)
|
||||
}
|
||||
|
||||
// 📧 邮箱验证码
|
||||
async bindWxOpenid(userId: string, code: string) {
|
||||
this.logger.log(`[bindWx] userId=${userId}, code=${code ? '已提供' : '空'}`)
|
||||
const appid = process.env.WX_APPID
|
||||
const secret = process.env.WX_SECRET
|
||||
if (!appid || !secret) {
|
||||
this.logger.error(`[bindWx] 微信配置不完整`)
|
||||
throw new HttpException('微信登录未配置', HttpStatus.SERVICE_UNAVAILABLE)
|
||||
}
|
||||
|
||||
const wxRes = await fetch(
|
||||
`https://api.weixin.qq.com/sns/jscode2session?appid=${appid}&secret=${secret}&js_code=${code}&grant_type=authorization_code`,
|
||||
)
|
||||
const wxData: any = await wxRes.json()
|
||||
this.logger.log(`[bindWx] 微信接口返回: ${JSON.stringify(wxData)}`)
|
||||
|
||||
if (wxData.errcode) {
|
||||
this.logger.error(`[bindWx] 微信登录失败: ${wxData.errmsg}, rid: ${wxData.rid || '无'}`)
|
||||
throw new HttpException(`微信登录失败: ${wxData.errmsg}`, HttpStatus.UNAUTHORIZED)
|
||||
}
|
||||
|
||||
const openid = wxData.openid
|
||||
this.logger.log(`[bindWx] 获取到openid=${openid}`)
|
||||
const existing = await this.userModel.findOne({ wxOpenid: openid }).exec()
|
||||
if (existing) {
|
||||
this.logger.warn(`[bindWx] openid=${openid} 已绑定到其他用户 ${existing._id}`)
|
||||
throw new HttpException('该微信号已绑定其他账号', HttpStatus.CONFLICT)
|
||||
}
|
||||
|
||||
const user = await this.userModel.findByIdAndUpdate(userId, { wxOpenid: openid }, { new: true }).exec()
|
||||
if (!user) { this.logger.error(`[bindWx] 用户不存在 userId=${userId}`); throw new HttpException('用户不存在', HttpStatus.NOT_FOUND) }
|
||||
this.logger.log(`[bindWx] openid=${openid} 绑定到用户 ${userId} 成功`)
|
||||
return { message: '微信绑定成功', wxOpenid: openid }
|
||||
}
|
||||
|
||||
async sendEmailCode(email: string) {
|
||||
if (!email || !email.includes('@')) {
|
||||
throw new HttpException('请输入正确的邮箱地址', HttpStatus.BAD_REQUEST)
|
||||
@@ -113,7 +156,7 @@ export class UserService {
|
||||
if (!user) {
|
||||
isNew = true
|
||||
const nick = email.split('@')[0]
|
||||
user = await this.userModel.create({ email, nickname: nick, remaining: 3 })
|
||||
user = await this.userModel.create({ email, nickname: nick, remaining: 0, gravity: 5 })
|
||||
}
|
||||
return { ...this.generateAuthResponse(user), isNew, hasPassword: !!user.password }
|
||||
}
|
||||
@@ -148,7 +191,7 @@ export class UserService {
|
||||
}
|
||||
const nick = email.split('@')[0]
|
||||
const hashed = await bcrypt.hash(password, 10)
|
||||
const user = await this.userModel.create({ email, nickname: nick, password: hashed, remaining: 3 })
|
||||
const user = await this.userModel.create({ email, nickname: nick, password: hashed, remaining: 0, gravity: 5 })
|
||||
return this.generateAuthResponse(user)
|
||||
}
|
||||
|
||||
@@ -216,6 +259,7 @@ export class UserService {
|
||||
resumeDownloadCredits: user.resumeDownloadCredits ?? 0,
|
||||
freeOptimizeUsed: user.freeOptimizeUsed ?? 0,
|
||||
shareCredits: user.shareCredits ?? 0,
|
||||
gravity: user.gravity ?? 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user