init
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
export interface ProductPricing {
|
||||
price: number;
|
||||
benefitAmount?: number | null;
|
||||
}
|
||||
|
||||
export function calcBenefitAmount(product: ProductPricing): number {
|
||||
return product.benefitAmount ?? product.price;
|
||||
}
|
||||
|
||||
export function validateMinPurchase(
|
||||
deliveryType: 'LOCAL' | 'CROSS_CITY',
|
||||
quantity: number,
|
||||
localMinQty: number,
|
||||
crossMinQty: number,
|
||||
): { ok: boolean; message?: string } {
|
||||
const min = deliveryType === 'LOCAL' ? localMinQty : crossMinQty;
|
||||
if (quantity < min) {
|
||||
return {
|
||||
ok: false,
|
||||
message:
|
||||
deliveryType === 'LOCAL'
|
||||
? `同城配送至少购买 ${min} 瓶`
|
||||
: `跨城配送至少购买 ${min} 瓶(1箱)`,
|
||||
};
|
||||
}
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
export function validateRedeemAmount(
|
||||
balance: number,
|
||||
amount: number,
|
||||
maxAmount = 500,
|
||||
): { ok: boolean; message?: string } {
|
||||
if (amount <= 0) return { ok: false, message: '核销金额必须大于 0' };
|
||||
if (amount > balance) return { ok: false, message: '核销金额不能超过可用余额' };
|
||||
if (amount > maxAmount) return { ok: false, message: `单次核销不能超过 ¥${maxAmount}` };
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
export interface BenefitCouponBalance {
|
||||
id: string;
|
||||
balance: number;
|
||||
createdAt: number;
|
||||
}
|
||||
|
||||
/** FIFO 跨多张权益券分配核销金额(购酒按单发券,展示为总余额) */
|
||||
export function allocateBenefitCoupons(
|
||||
coupons: BenefitCouponBalance[],
|
||||
amount: number,
|
||||
maxAmount = 500,
|
||||
): { ok: true; allocations: Array<{ couponId: string; amount: number }> } | { ok: false; message: string } {
|
||||
const active = coupons
|
||||
.filter((c) => c.balance > 0)
|
||||
.sort((a, b) => a.createdAt - b.createdAt);
|
||||
const totalBalance = active.reduce((sum, c) => sum + c.balance, 0);
|
||||
const check = validateRedeemAmount(totalBalance, amount, maxAmount);
|
||||
if (!check.ok) return { ok: false, message: check.message! };
|
||||
|
||||
let remaining = amount;
|
||||
const allocations: Array<{ couponId: string; amount: number }> = [];
|
||||
for (const coupon of active) {
|
||||
if (remaining <= 0) break;
|
||||
const take = Math.min(coupon.balance, remaining);
|
||||
allocations.push({ couponId: coupon.id, amount: take });
|
||||
remaining = Math.round((remaining - take) * 100) / 100;
|
||||
}
|
||||
|
||||
if (remaining > 0) {
|
||||
return { ok: false, message: '权益余额不足' };
|
||||
}
|
||||
|
||||
return { ok: true, allocations };
|
||||
}
|
||||
|
||||
export function calcBenefitSummary(
|
||||
balances: number[],
|
||||
maxAmount = 500,
|
||||
): { totalBalance: number; maxRedeemAmount: number; activeCouponCount: number } {
|
||||
const totalBalance = Math.round(balances.reduce((sum, b) => sum + b, 0) * 100) / 100;
|
||||
return {
|
||||
totalBalance,
|
||||
maxRedeemAmount: Math.min(maxAmount, totalBalance),
|
||||
activeCouponCount: balances.length,
|
||||
};
|
||||
}
|
||||
|
||||
export function calcRedeemSettleAmount(amount: number, settlementRate: number): number {
|
||||
return Math.round(amount * settlementRate * 100) / 100;
|
||||
}
|
||||
|
||||
export function generateUserNo(): string {
|
||||
const suffix = Math.floor(10000000 + Math.random() * 90000000);
|
||||
return `DK${suffix}`;
|
||||
}
|
||||
|
||||
export function generateOrderNo(): string {
|
||||
const now = new Date();
|
||||
const y = now.getFullYear();
|
||||
const m = String(now.getMonth() + 1).padStart(2, '0');
|
||||
const d = String(now.getDate()).padStart(2, '0');
|
||||
const rand = String(Math.floor(Math.random() * 100000)).padStart(5, '0');
|
||||
return `DK${y}${m}${d}${rand}`;
|
||||
}
|
||||
|
||||
export function generateCouponNo(): string {
|
||||
return `BC${Date.now()}${Math.floor(Math.random() * 1000)}`;
|
||||
}
|
||||
|
||||
export function generateRedeemNo(): string {
|
||||
return `RD${Date.now()}${Math.floor(Math.random() * 1000)}`;
|
||||
}
|
||||
|
||||
export function orderTabToStatuses(tab: string): string[] | undefined {
|
||||
switch (tab) {
|
||||
case 'pending_pay':
|
||||
return ['PENDING_PAY'];
|
||||
case 'pending_ship':
|
||||
return ['PENDING_SHIP', 'OUT_WAREHOUSE'];
|
||||
case 'pending_receive':
|
||||
return ['SHIPPING', 'PENDING_RECEIVE'];
|
||||
case 'completed':
|
||||
return ['COMPLETED'];
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user