初始化:职引项目 v1.0

This commit is contained in:
yuzhiran
2026-06-08 16:28:00 +08:00
commit 511f60d0db
111 changed files with 27295 additions and 0 deletions
@@ -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: '支付成功!欢迎开通成长版',
}
}
}