From 2fddd39301d4a90cc33501e9f789e0dda60b1bc9 Mon Sep 17 00:00:00 2001 From: yuzhiran Date: Thu, 25 Jun 2026 08:12:01 +0800 Subject: [PATCH] feat: registration gravity from DB config instead of hardcoded - Add registrationGravity to pricing config (default 50, adjustable via admin) - Inject PricingService into UserService, read config for all 4 registration paths - Expose registrationGravity in /member/plans response for frontend - Login page fetches dynamic value from API, replaces hardcoded '50' - Update user.vue gravity card copy --- .../src/modules/member/member.controller.ts | 1 + .../src/modules/schemas/pricing.service.ts | 4 +++ backend/src/modules/user/user.service.ts | 10 +++++--- zhiyin-app/src/pages/login/login.vue | 25 +++++++++++++++++++ zhiyin-app/src/pages/user/user.vue | 4 +-- 5 files changed, 38 insertions(+), 6 deletions(-) diff --git a/backend/src/modules/member/member.controller.ts b/backend/src/modules/member/member.controller.ts index dd06e03..63a799e 100644 --- a/backend/src/modules/member/member.controller.ts +++ b/backend/src/modules/member/member.controller.ts @@ -53,6 +53,7 @@ export class MemberController { } return { + registrationGravity: pricing.registrationGravity, interview: { dailyFreeLimit: FREE_DAILY_LIMIT, maxRoundsFree: 5, maxRoundsVip: 10 }, gravityRates: pricing.gravityRates, products: { diff --git a/backend/src/modules/schemas/pricing.service.ts b/backend/src/modules/schemas/pricing.service.ts index dc928a3..457ff31 100644 --- a/backend/src/modules/schemas/pricing.service.ts +++ b/backend/src/modules/schemas/pricing.service.ts @@ -10,6 +10,8 @@ export interface GravityRates { } interface PricingConfig { + /** 新用户注册赠送引力值 */ + registrationGravity: number interview: { pricePerSession: number; creditsPerPurchase: number } resumeOptimize: { freeLimit: number; pricePerOptimize: number; creditsPerPurchase: number } resumeDownload: { pricePerDownload: number; creditsPerPurchase: number } @@ -21,6 +23,7 @@ interface PricingConfig { } const DEFAULT_PRICING: PricingConfig = { + registrationGravity: 50, interview: { pricePerSession: 600, creditsPerPurchase: 1 }, resumeOptimize: { freeLimit: 3, pricePerOptimize: 400, creditsPerPurchase: 1 }, resumeDownload: { pricePerDownload: 200, creditsPerPurchase: 1 }, @@ -62,6 +65,7 @@ export class PricingService { private mergeDefaults(value: any): PricingConfig { return { + registrationGravity: value?.registrationGravity ?? DEFAULT_PRICING.registrationGravity, interview: { ...DEFAULT_PRICING.interview, ...value?.interview }, resumeOptimize: { ...DEFAULT_PRICING.resumeOptimize, ...value?.resumeOptimize }, resumeDownload: { ...DEFAULT_PRICING.resumeDownload, ...value?.resumeDownload }, diff --git a/backend/src/modules/user/user.service.ts b/backend/src/modules/user/user.service.ts index bf694fd..597ab3b 100644 --- a/backend/src/modules/user/user.service.ts +++ b/backend/src/modules/user/user.service.ts @@ -5,6 +5,7 @@ import { Model } from 'mongoose' import { JwtService } from '@nestjs/jwt' import { User, UserDocument } from './user.schema' import { EmailService } from '../email/email.service' +import { PricingService } from '../schemas/pricing.service' /** 通过 IP 查询粗略地理位置(ip-api.com 免费接口) */ async function lookupIpLocation(ip: string): Promise { @@ -30,6 +31,7 @@ export class UserService { @InjectModel(User.name) private userModel: Model, private jwtService: JwtService, private emailService: EmailService, + private pricingService: PricingService, ) {} async sendCode(phone: string) { @@ -58,7 +60,7 @@ export class UserService { let user = await this.userModel.findOne({ phone }).exec() if (!user) { - user = await this.userModel.create({ phone, nickname: `用户${phone.slice(-4)}`, gravity: 5 }) + user = await this.userModel.create({ phone, nickname: `用户${phone.slice(-4)}`, gravity: (await this.pricingService.getConfig()).registrationGravity }) } await this.recordLogin(user._id.toString(), ip) @@ -94,7 +96,7 @@ export class UserService { let user = await this.userModel.findOne({ wxOpenid: openid }).exec() if (!user) { - user = await this.userModel.create({ wxOpenid: openid, nickname: '微信用户', gravity: 5 }) + user = await this.userModel.create({ wxOpenid: openid, nickname: '微信用户', gravity: (await this.pricingService.getConfig()).registrationGravity }) } await this.recordLogin(user._id.toString(), ip) @@ -170,7 +172,7 @@ export class UserService { if (!user) { isNew = true const nick = email.split('@')[0] - user = await this.userModel.create({ email, nickname: nick, remaining: 0, gravity: 5 }) + user = await this.userModel.create({ email, nickname: nick, remaining: 0, gravity: (await this.pricingService.getConfig()).registrationGravity }) } await this.recordLogin(user._id.toString(), ip) return { ...this.generateAuthResponse(user), isNew, hasPassword: !!user.password } @@ -208,7 +210,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: 0, gravity: 5 }) + const user = await this.userModel.create({ email, nickname: nick, password: hashed, remaining: 0, gravity: (await this.pricingService.getConfig()).registrationGravity }) await this.recordLogin(user._id.toString(), ip) return this.generateAuthResponse(user) } diff --git a/zhiyin-app/src/pages/login/login.vue b/zhiyin-app/src/pages/login/login.vue index 9cae9b8..4d43914 100644 --- a/zhiyin-app/src/pages/login/login.vue +++ b/zhiyin-app/src/pages/login/login.vue @@ -65,6 +65,13 @@ 创建账号 注册后享受 AI 面试模拟服务 + + 🎁 + + 注册即送 {{ registrationGravity }} 引力值 + 免费体验 AI 面试模拟 + + 邮箱 @@ -133,6 +140,7 @@ const agreed = ref(false) const mainTab = ref('login') const loginMode = ref('password') // 'password' | 'code' const isMp = ref(false) +const registrationGravity = ref(50) const email = ref('') const password = ref('') @@ -158,6 +166,17 @@ onMounted(() => { isMp.value = true mainTab.value = 'wechat' // #endif + // 获取注册赠送引力值配置 + uni.request({ + url: api('/member/plans'), + method: 'GET', + success: (res) => { + if (res.statusCode === 200 && res.data?.registrationGravity) { + registrationGravity.value = res.data.registrationGravity + } + }, + fail: () => {} // 使用默认值 50 + }) }) onBeforeUnmount(() => { if (timer) { clearTimeout(timer); timer = null } }) @@ -355,6 +374,12 @@ const goPrivacy = () => uni.navigateTo({ url: '/pages/privacy/privacy' }) .card-title { font-size: 30rpx; font-weight: 700; color: var(--color-text); display: block; } .card-sub { font-size: 22rpx; color: var(--color-text-tertiary); margin-top: 6rpx; margin-bottom: 24rpx; display: block; } +/* ===== Promo Banner ===== */ +.promo-banner { background: linear-gradient(135deg, #FEF3C7, #FDE68A); border-radius: var(--radius-md); padding: 20rpx 24rpx; margin-bottom: 24rpx; display: flex; align-items: center; gap: 16rpx; } +.promo-icon { font-size: 36rpx; } +.promo-title { font-size: 26rpx; font-weight: 700; color: #92400E; display: block; } +.promo-desc { font-size: 22rpx; color: #B45309; display: block; margin-top: 4rpx; } + /* ===== Fields ===== */ .field { margin-bottom: 20rpx; } .field-label { font-size: 22rpx; color: var(--color-text-secondary); margin-bottom: 8rpx; display: block; } diff --git a/zhiyin-app/src/pages/user/user.vue b/zhiyin-app/src/pages/user/user.vue index a0b843f..142fd6c 100644 --- a/zhiyin-app/src/pages/user/user.vue +++ b/zhiyin-app/src/pages/user/user.vue @@ -49,7 +49,7 @@ {{ memberInfo.gravity ?? 0 }} - 每次面试消耗 5 引力值 · 分享可获得更多 + 每次面试消耗 5 引力值 · 分享好友、贡献面经可获得更多 贡献面经 @@ -150,7 +150,7 @@ 🛒 购买引力值 - 小程序内直接购买,微信支付安全便捷 + ¥5 一份,微信支付安全便捷