feat(points): apps/web/src/store/pointsStore.ts

This commit is contained in:
2026-09-15 08:37:46 +08:00
parent 05e56184d6
commit cd1c7bc26e
+83
View File
@@ -0,0 +1,83 @@
/**
* 积分 & 会员状态管理(Zustand)
* - 启动时拉取余额 & 订阅信息
* - 提供刷新、余额扣减(乐观更新)等工具
*/
import { create } from "zustand"
import type { PointsBalance, PointsRulesResponse, SubscriptionCurrent } from "@/api/points/types"
import { getCurrentSubscription, getPointsBalance, getPointsRules } from "@/api/points"
interface PointsState {
/** 积分余额 & 会员状态(来自 /points/balance) */
balance: PointsBalance | null
/** 订阅详情(来自 /subscription/current) */
subscription: SubscriptionCurrent | null
/** 积分消耗规则缓存 */
rules: PointsRulesResponse | null
loading: boolean
error: string | null
/** 初始化:拉取余额 + 订阅信息 + 规则 */
init: () => Promise<void>
/** 强制刷新余额 */
refreshBalance: () => Promise<void>
/** 乐观扣减:在支付/业务发起前调用,失败时用 refreshBalance 兜底 */
optimisticDeduct: (points: number) => void
/** 乐观增加(充值成功后调用) */
optimisticAdd: (points: number) => void
}
export const usePointsStore = create<PointsState>((set, get) => ({
balance: null,
subscription: null,
rules: null,
loading: false,
error: null,
init: async () => {
if (get().loading) return
set({ loading: true, error: null })
try {
const [balance, sub, rules] = await Promise.all([
getPointsBalance(),
getCurrentSubscription(),
getPointsRules(),
])
set({ balance, subscription: sub, rules, loading: false })
} catch (e) {
set({ error: (e as Error).message, loading: false })
}
},
refreshBalance: async () => {
try {
const balance = await getPointsBalance()
set({ balance })
} catch (e) {
set({ error: (e as Error).message })
}
},
optimisticDeduct: (points: number) => {
const b = get().balance
if (!b) return
set({
balance: {
...b,
balance: Math.max(0, b.balance - points),
total_spent: b.total_spent + points,
},
})
},
optimisticAdd: (points: number) => {
const b = get().balance
if (!b) return
set({
balance: {
...b,
balance: b.balance + points,
total_earned: b.total_earned + points,
},
})
},
}))