v3计划内容上传

This commit is contained in:
2026-07-04 20:36:26 +08:00
parent def3ed5175
commit 743d242d52
23 changed files with 291 additions and 53 deletions
+21 -1
View File
@@ -31,12 +31,25 @@ describe('validateMinPurchase', () => {
});
describe('validateRedeemAmount', () => {
it('rejects over balance or non-positive', () => {
it('rejects over balance or non-positive amount', () => {
expect(validateRedeemAmount(100, 50).ok).toBe(true);
expect(validateRedeemAmount(100, 100).ok).toBe(true);
expect(validateRedeemAmount(50, 60).ok).toBe(false);
expect(validateRedeemAmount(100, 0).ok).toBe(false);
});
it('allows direct redeem up to total balance without a document cap', () => {
expect(validateRedeemAmount(700, 650).ok).toBe(true);
expect(validateRedeemAmount(700, 701).ok).toBe(false);
});
it('caps document-based redeem by document amount', () => {
expect(validateRedeemAmount(700, 300, 300).ok).toBe(true);
expect(validateRedeemAmount(700, 301, 300)).toEqual({
ok: false,
message: '核销金额不能超过该核销单可用金额',
});
});
});
describe('calcRedeemSettleAmount', () => {
@@ -65,6 +78,13 @@ describe('allocateBenefitCoupons', () => {
it('rejects when total balance insufficient', () => {
expect(allocateBenefitCoupons(coupons, 800).ok).toBe(false);
});
it('rejects when a document cap is lower than requested amount', () => {
expect(allocateBenefitCoupons(coupons, 301, 300)).toEqual({
ok: false,
message: '核销金额不能超过该核销单可用金额',
});
});
});
describe('calcBenefitSummary', () => {
+6 -1
View File
@@ -29,9 +29,13 @@ export function validateMinPurchase(
export function validateRedeemAmount(
balance: number,
amount: number,
documentAmount?: number | null,
): { ok: boolean; message?: string } {
if (amount <= 0) return { ok: false, message: '核销金额必须大于 0' };
if (amount > balance) return { ok: false, message: '核销金额不能超过可用余额' };
if (documentAmount != null && amount > documentAmount) {
return { ok: false, message: '核销金额不能超过该核销单可用金额' };
}
return { ok: true };
}
@@ -45,12 +49,13 @@ export interface BenefitCouponBalance {
export function allocateBenefitCoupons(
coupons: BenefitCouponBalance[],
amount: number,
documentAmount?: number | null,
): { 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);
const check = validateRedeemAmount(totalBalance, amount, documentAmount);
if (!check.ok) return { ok: false, message: check.message! };
let remaining = amount;
+2 -1
View File
@@ -77,5 +77,6 @@ export const CLIENT_APP_ACTOR_MAP: Record<ClientApp, ActorType> = {
[ClientApp.SHOP_H5]: ActorType.STORE,
};
export const REDEEM_MAX_AMOUNT = 500;
export const REDEEM_DIRECT_LIMIT_POLICY = 'TOTAL_ACTIVE_BALANCE';
export const REDEEM_DOCUMENT_LIMIT_POLICY = 'DOCUMENT_BALANCE';
export const REDEEM_TOKEN_TTL_SECONDS = 300;