From 2222dccbb7950f313c80e895a66f71989369afdf Mon Sep 17 00:00:00 2001 From: zetaloop Date: Mon, 23 Feb 2026 11:04:48 +0800 Subject: [PATCH] feat(domain): add income calculation with commission support --- lib/domain/income.ts | 33 +++++++++++++++++++++++ store/wallet.ts | 16 +++++++---- tests/income-calculation.test.ts | 46 ++++++++++++++++++++++++++++++++ 3 files changed, 90 insertions(+), 5 deletions(-) create mode 100644 lib/domain/income.ts create mode 100644 tests/income-calculation.test.ts diff --git a/lib/domain/income.ts b/lib/domain/income.ts new file mode 100644 index 0000000..bc42e71 --- /dev/null +++ b/lib/domain/income.ts @@ -0,0 +1,33 @@ +import type { Shop } from "@/lib/types" + +type ShopCommission = Pick + +export interface IncomeCalculation { + income: number + commissionLabel: string +} + +function roundCurrency(amount: number) { + return Number(amount.toFixed(2)) +} + +export function calculateOrderIncome(totalPrice: number, shop?: ShopCommission): IncomeCalculation { + if (!shop) { + return { + income: roundCurrency(totalPrice), + commissionLabel: "独立接单无抽成", + } + } + + if (shop.commissionType === "percentage") { + return { + income: roundCurrency(totalPrice * (1 - shop.commissionValue / 100)), + commissionLabel: `扣除${shop.commissionValue}%抽成`, + } + } + + return { + income: roundCurrency(Math.max(0, totalPrice - shop.commissionValue)), + commissionLabel: `扣除¥${shop.commissionValue}固定抽成`, + } +} diff --git a/store/wallet.ts b/store/wallet.ts index 38be5f4..8882e64 100644 --- a/store/wallet.ts +++ b/store/wallet.ts @@ -1,6 +1,8 @@ import { create } from "zustand" +import { calculateOrderIncome } from "@/lib/domain/income" import { generateId } from "@/lib/id" import { mockTransactions, walletBalance } from "@/lib/mock" +import { useShopStore } from "@/store/shops" import type { WalletTransaction } from "@/lib/types" interface WalletState { @@ -10,7 +12,7 @@ interface WalletState { withdraw: (amount: number) => void deductBalance: (orderId: string, amount: number) => boolean refundPayment: (orderId: string, amount: number) => boolean - addIncome: (orderId: string, amount: number) => void + addIncome: (orderId: string, totalPrice: number, shopId?: string) => void addTransaction: (transaction: WalletTransaction) => void } @@ -109,8 +111,8 @@ export const useWalletStore = create((set, get) => ({ return true }, - addIncome: (orderId, amount) => { - if (!Number.isFinite(amount) || amount <= 0) return + addIncome: (orderId, totalPrice, shopId) => { + if (!Number.isFinite(totalPrice) || totalPrice <= 0) return const state = get() const exists = state.transactions.some( @@ -118,15 +120,19 @@ export const useWalletStore = create((set, get) => ({ ) 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() - const income = Number((amount * 0.85).toFixed(2)) set((prev) => ({ transactions: [ { id: generateId("tx"), type: "income", amount: income, - description: `订单 ${orderId} 收入(扣除15%抽成)`, + description: `订单 ${orderId} 收入(${commissionLabel})`, createdAt: now, }, ...prev.transactions, diff --git a/tests/income-calculation.test.ts b/tests/income-calculation.test.ts new file mode 100644 index 0000000..6a002a6 --- /dev/null +++ b/tests/income-calculation.test.ts @@ -0,0 +1,46 @@ +import { describe, expect, it } from "vitest" +import { calculateOrderIncome } from "@/lib/domain/income" + +describe("calculateOrderIncome", () => { + it("calculates percentage commission income", () => { + const result = calculateOrderIncome(100, { + commissionType: "percentage", + commissionValue: 12, + }) + + expect(result).toEqual({ + income: 88, + commissionLabel: "扣除12%抽成", + }) + }) + + it("calculates fixed commission income", () => { + const result = calculateOrderIncome(60, { + commissionType: "fixed", + commissionValue: 8, + }) + + expect(result).toEqual({ + income: 52, + commissionLabel: "扣除¥8固定抽成", + }) + }) + + it("keeps full income for independent orders", () => { + const result = calculateOrderIncome(90) + + expect(result).toEqual({ + income: 90, + commissionLabel: "独立接单无抽成", + }) + }) + + it("does not return negative income for fixed commission", () => { + const result = calculateOrderIncome(6, { + commissionType: "fixed", + commissionValue: 8, + }) + + expect(result.income).toBe(0) + }) +})