Files
juwan-frontend/store/wallet.ts
T

147 lines
4.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { calculateOrderIncome } from "@/lib/domain/income"
import { generateId } from "@/lib/id"
import type { WalletTransaction } from "@/lib/types"
import { useShopStore } from "@/store/shops"
import { create } from "zustand"
interface WalletState {
balance: number
transactions: WalletTransaction[]
topUp: (amount: number) => void
withdraw: (amount: number) => void
deductBalance: (orderId: string, amount: number) => boolean
refundPayment: (orderId: string, amount: number) => boolean
addIncome: (orderId: string, totalPrice: number, shopId?: string) => void
addTransaction: (transaction: WalletTransaction) => void
}
export const useWalletStore = create<WalletState>((set, get) => ({
balance: 0,
transactions: [],
topUp: (amount) => {
if (!Number.isFinite(amount) || amount <= 0) return
const now = new Date().toISOString()
set((state) => ({
balance: state.balance + amount,
transactions: [
{
id: generateId("tx"),
type: "topup",
amount,
description: "充值",
createdAt: now,
},
...state.transactions,
],
}))
},
withdraw: (amount) => {
if (!Number.isFinite(amount) || amount <= 0) return
const now = new Date().toISOString()
set((state) => ({
transactions: [
{
id: generateId("tx"),
type: "withdrawal",
amount: -amount,
description: "提现到银行卡",
createdAt: now,
},
...state.transactions,
],
}))
},
deductBalance: (orderId, amount) => {
if (!Number.isFinite(amount) || amount <= 0) return false
const state = get()
const paid = state.transactions.some(
(transaction) => transaction.type === "payment" && transaction.description.includes(orderId),
)
if (paid || state.balance < amount) {
return false
}
const now = new Date().toISOString()
set((prev) => ({
balance: prev.balance - amount,
transactions: [
{
id: generateId("tx"),
type: "payment",
amount: -amount,
description: `支付订单 ${orderId}`,
createdAt: now,
},
...prev.transactions,
],
}))
return true
},
refundPayment: (orderId, amount) => {
if (!Number.isFinite(amount) || amount <= 0) return false
const state = get()
const paid = state.transactions.some(
(transaction) => transaction.type === "payment" && transaction.description.includes(orderId),
)
const refunded = state.transactions.some(
(transaction) => transaction.type === "refund" && transaction.description.includes(orderId),
)
if (!paid || refunded) {
return false
}
const now = new Date().toISOString()
set((prev) => ({
balance: prev.balance + amount,
transactions: [
{
id: generateId("tx"),
type: "refund",
amount,
description: `订单 ${orderId} 退款`,
createdAt: now,
},
...prev.transactions,
],
}))
return true
},
addIncome: (orderId, totalPrice, shopId) => {
if (!Number.isFinite(totalPrice) || totalPrice <= 0) return
const state = get()
const exists = state.transactions.some(
(transaction) => transaction.type === "income" && transaction.description.includes(orderId),
)
if (exists) return
const shop = shopId
? useShopStore.getState().shops.find((item) => item.id === shopId)
: undefined
const { income, commissionLabel } = calculateOrderIncome(totalPrice, shop)
const now = new Date().toISOString()
set((prev) => ({
transactions: [
{
id: generateId("tx"),
type: "income",
amount: income,
description: `订单 ${orderId} 收入(${commissionLabel}`,
createdAt: now,
},
...prev.transactions,
],
}))
},
addTransaction: (transaction) => {
set((state) => ({
transactions: [transaction, ...state.transactions],
}))
},
}))