feat(avatar): 接入会会支付并统一积分展示 #7
@@ -58,10 +58,10 @@ def seed():
|
|||||||
db = SessionLocal()
|
db = SessionLocal()
|
||||||
try:
|
try:
|
||||||
plan_specs = [
|
plan_specs = [
|
||||||
{"id": "1", "name": "基础套餐", "amount": 2_000_000, "price": 10, "badge": "", "desc": "2M Token"},
|
{"id": "1", "name": "基础套餐", "amount": 2_000_000, "price": 10, "badge": "", "desc": "2M 积分"},
|
||||||
{"id": "2", "name": "标准套餐", "amount": 20_000_000, "price": 100, "badge": "常用", "desc": "20M Token"},
|
{"id": "2", "name": "标准套餐", "amount": 20_000_000, "price": 100, "badge": "常用", "desc": "20M 积分"},
|
||||||
{"id": "3", "name": "专业套餐", "amount": 250_000_000, "price": 1000, "badge": "加赠25%", "desc": "250M Token"},
|
{"id": "3", "name": "专业套餐", "amount": 250_000_000, "price": 1000, "badge": "加赠25%", "desc": "250M 积分"},
|
||||||
{"id": "4", "name": "企业套餐", "amount": 2_500_000_000, "price": 10000, "badge": "企业推荐", "desc": "2500M Token"},
|
{"id": "4", "name": "企业套餐", "amount": 2_500_000_000, "price": 10000, "badge": "企业推荐", "desc": "2500M 积分"},
|
||||||
]
|
]
|
||||||
for spec in plan_specs:
|
for spec in plan_specs:
|
||||||
plan = db.query(TokenPlan).filter(TokenPlan.id == spec["id"]).first()
|
plan = db.query(TokenPlan).filter(TokenPlan.id == spec["id"]).first()
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ def reserve_avatar_tokens(
|
|||||||
) -> TokenReservation:
|
) -> TokenReservation:
|
||||||
user = avatar_owner_user(db, avatar)
|
user = avatar_owner_user(db, avatar)
|
||||||
if not user:
|
if not user:
|
||||||
raise InsufficientTokensError("分身尚未关联有效用户,暂时无法使用 Token")
|
raise InsufficientTokensError("分身尚未关联有效用户,暂时无法使用积分")
|
||||||
account = get_or_create_account(db, user.id)
|
account = get_or_create_account(db, user.id)
|
||||||
reserved = estimate_request_tokens(messages, max_output_tokens)
|
reserved = estimate_request_tokens(messages, max_output_tokens)
|
||||||
updated = (
|
updated = (
|
||||||
@@ -95,7 +95,7 @@ def reserve_avatar_tokens(
|
|||||||
)
|
)
|
||||||
if updated != 1:
|
if updated != 1:
|
||||||
db.rollback()
|
db.rollback()
|
||||||
raise InsufficientTokensError("Token 余额不足,请充值后继续")
|
raise InsufficientTokensError("积分余额不足,请充值后继续")
|
||||||
db.refresh(account)
|
db.refresh(account)
|
||||||
usage = TokenUsage(
|
usage = TokenUsage(
|
||||||
user_id=user.id,
|
user_id=user.id,
|
||||||
@@ -142,7 +142,7 @@ def settle_reservation(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
if updated != 1:
|
if updated != 1:
|
||||||
raise RuntimeError("Token 账户不存在")
|
raise RuntimeError("积分账户不存在")
|
||||||
db.expire_all()
|
db.expire_all()
|
||||||
account = db.query(TokenAccount).filter(TokenAccount.user_id == reservation.user_id).first()
|
account = db.query(TokenAccount).filter(TokenAccount.user_id == reservation.user_id).first()
|
||||||
record.prompt_tokens = prompt_tokens
|
record.prompt_tokens = prompt_tokens
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ const routes: RouteRecordRaw[] = [
|
|||||||
path: '/token/charge',
|
path: '/token/charge',
|
||||||
name: 'TokenCharge',
|
name: 'TokenCharge',
|
||||||
component: () => import('@/views/TokenCharge.vue'),
|
component: () => import('@/views/TokenCharge.vue'),
|
||||||
meta: { title: 'Token充值', requiresAuth: true }
|
meta: { title: '积分充值', requiresAuth: true }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/avatar/card',
|
path: '/avatar/card',
|
||||||
|
|||||||
@@ -24,11 +24,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- Token 余额条 -->
|
<!-- 积分余额条:暂时隐藏,保留完整实现便于后续恢复。 -->
|
||||||
<section class="token-section">
|
<section v-if="SHOW_POINTS_BALANCE_CARD" class="token-section">
|
||||||
<div class="token-card">
|
<div class="token-card">
|
||||||
<div class="token-info">
|
<div class="token-info">
|
||||||
<span class="token-label">Token 余额</span>
|
<span class="token-label">积分余额</span>
|
||||||
<span class="token-amount">{{ tokenBalance.toLocaleString() }}</span>
|
<span class="token-amount">{{ tokenBalance.toLocaleString() }}</span>
|
||||||
<span class="token-used">累计使用 {{ tokenConsumed.toLocaleString() }}</span>
|
<span class="token-used">累计使用 {{ tokenConsumed.toLocaleString() }}</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -56,7 +56,7 @@
|
|||||||
<div class="avatar-details">
|
<div class="avatar-details">
|
||||||
<div class="avatar-name-row"><h2 class="avatar-name">{{ a.displayName || a.name }}</h2><span class="avatar-status"><i class="status-dot" :class="a.status"></i>{{ statusText(a.status) }}</span></div>
|
<div class="avatar-name-row"><h2 class="avatar-name">{{ a.displayName || a.name }}</h2><span class="avatar-status"><i class="status-dot" :class="a.status"></i>{{ statusText(a.status) }}</span></div>
|
||||||
<p class="avatar-desc">{{ a.description || '暂无描述' }}</p>
|
<p class="avatar-desc">{{ a.description || '暂无描述' }}</p>
|
||||||
<span class="avatar-token-usage">累计使用 {{ avatarTokenUsage(a.id).toLocaleString() }} Token</span>
|
<span class="avatar-token-usage">累计使用 {{ avatarTokenUsage(a.id).toLocaleString() }} 积分</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="avatar-actions">
|
<div class="avatar-actions">
|
||||||
@@ -92,6 +92,9 @@ const router = useRouter()
|
|||||||
const avatarStore = useAvatarStore()
|
const avatarStore = useAvatarStore()
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
|
|
||||||
|
// 临时产品开关:余额卡片代码保留,后续改为 true 即可恢复展示。
|
||||||
|
const SHOW_POINTS_BALANCE_CARD = false
|
||||||
|
|
||||||
// 当前登录会会用户的资料(头像 / 昵称)
|
// 当前登录会会用户的资料(头像 / 昵称)
|
||||||
const me = computed(() => userStore.user)
|
const me = computed(() => userStore.user)
|
||||||
|
|
||||||
@@ -289,7 +292,7 @@ onMounted(() => {
|
|||||||
background: rgba(255, 255, 255, 0.3);
|
background: rgba(255, 255, 255, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Token 余额条 */
|
/* 积分余额条 */
|
||||||
.token-section {
|
.token-section {
|
||||||
padding: 16px 20px;
|
padding: 16px 20px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<!-- 顶部导航 -->
|
<!-- 顶部导航 -->
|
||||||
<header class="page-header">
|
<header class="page-header">
|
||||||
<button class="back-btn" @click="goBack">‹</button>
|
<button class="back-btn" @click="goBack">‹</button>
|
||||||
<h1 class="page-title">Token 充值</h1>
|
<h1 class="page-title">积分充值</h1>
|
||||||
<div class="header-right"></div>
|
<div class="header-right"></div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
@@ -12,8 +12,8 @@
|
|||||||
<div class="balance-card">
|
<div class="balance-card">
|
||||||
<span class="balance-label">当前余额</span>
|
<span class="balance-label">当前余额</span>
|
||||||
<span class="balance-amount">{{ currentBalance.toLocaleString() }}</span>
|
<span class="balance-amount">{{ currentBalance.toLocaleString() }}</span>
|
||||||
<span class="balance-unit">Token</span>
|
<span class="balance-unit">积分</span>
|
||||||
<span class="balance-used">累计使用 {{ totalConsumed.toLocaleString() }} Token</span>
|
<span class="balance-used">累计使用 {{ totalConsumed.toLocaleString() }} 积分</span>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
>
|
>
|
||||||
<div class="plan-badge" v-if="plan.badge">{{ plan.badge }}</div>
|
<div class="plan-badge" v-if="plan.badge">{{ plan.badge }}</div>
|
||||||
<div class="plan-amount">{{ formatTokenAmount(plan.amount) }}</div>
|
<div class="plan-amount">{{ formatTokenAmount(plan.amount) }}</div>
|
||||||
<div class="plan-unit">Token</div>
|
<div class="plan-unit">积分</div>
|
||||||
<div class="plan-price">¥{{ plan.price }}</div>
|
<div class="plan-price">¥{{ plan.price }}</div>
|
||||||
<div class="plan-desc" v-if="plan.desc">{{ plan.desc }}</div>
|
<div class="plan-desc" v-if="plan.desc">{{ plan.desc }}</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -132,7 +132,7 @@ const doCharge = async () => {
|
|||||||
const methodText = paymentMethod.value === 'wechat' ? '微信支付' : '支付宝'
|
const methodText = paymentMethod.value === 'wechat' ? '微信支付' : '支付宝'
|
||||||
const res: any = await chargeToken(selectedPlan.value.id)
|
const res: any = await chargeToken(selectedPlan.value.id)
|
||||||
currentBalance.value = res?.balance ?? currentBalance.value
|
currentBalance.value = res?.balance ?? currentBalance.value
|
||||||
alert(`已通过${methodText}成功充值,当前余额:${currentBalance.value} Token`)
|
alert(`已通过${methodText}成功充值,当前余额:${currentBalance.value} 积分`)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert('充值失败,请重试')
|
alert('充值失败,请重试')
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
Reference in New Issue
Block a user