初始化:职引项目 v1.0
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
import { Controller, Post, Get, Body, HttpException, HttpStatus, UseGuards } from '@nestjs/common'
|
||||
import { InjectModel } from '@nestjs/mongoose'
|
||||
import { Model } from 'mongoose'
|
||||
import { User, UserDocument } from '../user/user.schema'
|
||||
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard'
|
||||
import { CurrentUser } from '../../common/decorators/current-user.decorator'
|
||||
import { Public } from '../../common/decorators/public.decorator'
|
||||
|
||||
const GROWTH_PRICE = 1990
|
||||
const DURATION_DAYS = 30
|
||||
const FREE_DAILY_LIMIT = 2
|
||||
|
||||
interface PlanConfig {
|
||||
id: string
|
||||
name: string
|
||||
price: number
|
||||
dailyLimit: number
|
||||
features: string[]
|
||||
}
|
||||
|
||||
const PLANS: Record<string, PlanConfig> = {
|
||||
free: {
|
||||
id: 'free',
|
||||
name: '免费版',
|
||||
price: 0,
|
||||
dailyLimit: FREE_DAILY_LIMIT,
|
||||
features: [
|
||||
'每日 2 次 AI 模拟面试',
|
||||
'基础面试报告',
|
||||
'通用题库随机出题',
|
||||
'简历诊断(限 3 次)',
|
||||
],
|
||||
},
|
||||
growth: {
|
||||
id: 'growth',
|
||||
name: '成长版',
|
||||
price: GROWTH_PRICE,
|
||||
dailyLimit: 999,
|
||||
features: [
|
||||
'免费版全部权益',
|
||||
'无限面试次数',
|
||||
'详细面试报告(四维评分)',
|
||||
'进步轨迹雷达图 + 打卡',
|
||||
'每日一题推送',
|
||||
'参考回答思路',
|
||||
'公司真题库',
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
@Controller('member')
|
||||
@UseGuards(JwtAuthGuard)
|
||||
export class MemberController {
|
||||
constructor(
|
||||
@InjectModel(User.name) private userModel: Model<UserDocument>,
|
||||
) {}
|
||||
|
||||
// 公开的套餐配置(给前端会员页和限制拦截用)
|
||||
@Public()
|
||||
@Get('plans')
|
||||
getPlans() {
|
||||
return {
|
||||
interview: {
|
||||
dailyFreeLimit: FREE_DAILY_LIMIT,
|
||||
maxRoundsFree: 5,
|
||||
maxRoundsVip: 10,
|
||||
},
|
||||
diagnosis: { dailyFreeLimit: 2 },
|
||||
optimize: { dailyFreeLimit: 2 },
|
||||
price: { monthly: GROWTH_PRICE },
|
||||
plans: Object.values(PLANS).map(p => ({
|
||||
id: p.id,
|
||||
name: p.name,
|
||||
price: p.price,
|
||||
priceDisplay: p.price === 0 ? '免费' : `¥${(p.price / 100).toFixed(1)}/月`,
|
||||
dailyLimit: p.dailyLimit,
|
||||
features: p.features,
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
@Get('status')
|
||||
async getStatus(@CurrentUser('userId') userId: string) {
|
||||
const user = await this.userModel.findById(userId).exec()
|
||||
if (!user) throw new HttpException('用户不存在', HttpStatus.NOT_FOUND)
|
||||
const planConfig = PLANS[user.plan] || PLANS.free
|
||||
return {
|
||||
plan: user.plan,
|
||||
planName: planConfig.name,
|
||||
remaining: user.remaining,
|
||||
dailyLimit: planConfig.dailyLimit,
|
||||
vipExpireAt: user.vipExpireAt,
|
||||
isVip: user.plan !== 'free',
|
||||
}
|
||||
}
|
||||
|
||||
@Post('create-order')
|
||||
async createOrder(@CurrentUser('userId') userId: string) {
|
||||
const user = await this.userModel.findById(userId).exec()
|
||||
if (!user) throw new HttpException('用户不存在', HttpStatus.NOT_FOUND)
|
||||
const orderId = `ZHI${Date.now()}${userId.slice(-4)}`
|
||||
return {
|
||||
orderId,
|
||||
planId: 'growth',
|
||||
planName: '成长版',
|
||||
amount: GROWTH_PRICE,
|
||||
amountDisplay: `¥${(GROWTH_PRICE / 100).toFixed(1)}`,
|
||||
duration: `${DURATION_DAYS} 天`,
|
||||
}
|
||||
}
|
||||
|
||||
@Post('pay')
|
||||
async pay(@CurrentUser('userId') userId: string, @Body('orderId') orderId: string) {
|
||||
const user = await this.userModel.findById(userId).exec()
|
||||
if (!user) throw new HttpException('用户不存在', HttpStatus.NOT_FOUND)
|
||||
const expireAt = new Date()
|
||||
expireAt.setDate(expireAt.getDate() + DURATION_DAYS)
|
||||
user.plan = 'growth'
|
||||
user.vipExpireAt = expireAt
|
||||
user.remaining = 999
|
||||
await user.save()
|
||||
return {
|
||||
success: true,
|
||||
plan: 'growth',
|
||||
planName: '成长版',
|
||||
expireAt,
|
||||
message: '支付成功!欢迎开通成长版',
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user