@@ -2,6 +2,7 @@ import { describe, expect, it } from 'vitest';
|
||||
import {
|
||||
calcBenefitAmount,
|
||||
calcRedeemSettleAmount,
|
||||
resolveSettlementRate,
|
||||
calcLogisticsFeeByBottles,
|
||||
calcOrderBoxCount,
|
||||
shouldHoldAutoCourierDispatch,
|
||||
@@ -92,6 +93,7 @@ describe('validateRedeemAmount', () => {
|
||||
expect(validateRedeemAmount(100, 100).ok).toBe(true);
|
||||
expect(validateRedeemAmount(50, 60).ok).toBe(false);
|
||||
expect(validateRedeemAmount(100, 0).ok).toBe(false);
|
||||
expect(validateRedeemAmount(100, Number.NaN).ok).toBe(false);
|
||||
});
|
||||
|
||||
it('allows direct redeem up to total balance without a document cap', () => {
|
||||
@@ -114,6 +116,15 @@ describe('calcRedeemSettleAmount', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('resolveSettlementRate', () => {
|
||||
it('falls back to 0.6 when missing or invalid', () => {
|
||||
expect(resolveSettlementRate(0.6)).toBe(0.6);
|
||||
expect(resolveSettlementRate(0)).toBe(0.6);
|
||||
expect(resolveSettlementRate(Number.NaN)).toBe(0.6);
|
||||
expect(resolveSettlementRate(undefined)).toBe(0.6);
|
||||
});
|
||||
});
|
||||
|
||||
describe('calcLogisticsFeeByBottles', () => {
|
||||
const xfx = {
|
||||
baseBottles: 2,
|
||||
|
||||
@@ -33,14 +33,25 @@ export function validateMinPurchase(
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
export const DEFAULT_STORE_SETTLEMENT_RATE = 0.6;
|
||||
|
||||
export function resolveSettlementRate(raw: unknown): number {
|
||||
const n = Number(raw);
|
||||
return Number.isFinite(n) && n > 0 ? n : DEFAULT_STORE_SETTLEMENT_RATE;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (!Number.isFinite(amount) || amount <= 0) {
|
||||
return { ok: false, message: '核销金额必须大于 0' };
|
||||
}
|
||||
if (!Number.isFinite(balance) || amount > balance) {
|
||||
return { ok: false, message: '核销金额不能超过可用余额' };
|
||||
}
|
||||
if (documentAmount != null && Number.isFinite(documentAmount) && amount > documentAmount) {
|
||||
return { ok: false, message: '核销金额不能超过该核销单可用金额' };
|
||||
}
|
||||
return { ok: true };
|
||||
|
||||
@@ -10,7 +10,7 @@ export const STORE_INFO_CHANGE_STATUS_LABELS: Record<StoreInfoChangeStatus, stri
|
||||
REJECTED: '已驳回',
|
||||
};
|
||||
|
||||
/** 门店基础信息可变字段白名单(提交变更 / 总部审核覆盖时使用,均为 Store 标量列) */
|
||||
/** 门店基础信息可变字段白名单(提交变更 / 总部审核覆盖时使用) */
|
||||
export const STORE_INFO_CHANGEABLE_FIELDS = [
|
||||
'name',
|
||||
'contactPhone',
|
||||
@@ -24,6 +24,10 @@ export const STORE_INFO_CHANGEABLE_FIELDS = [
|
||||
'openTime2',
|
||||
'closeTime2',
|
||||
'avgPrice',
|
||||
/** 门头照 URL(审核通过后写入 cover + common_resource) */
|
||||
'coverUrl',
|
||||
/** 环境照 URL 数组(审核通过后替换 ENV common_resource) */
|
||||
'envPhotoUrls',
|
||||
] as const;
|
||||
|
||||
export type StoreInfoChangeableField = (typeof STORE_INFO_CHANGEABLE_FIELDS)[number];
|
||||
|
||||
@@ -11,6 +11,8 @@ export interface SystemConfigFieldMeta {
|
||||
key: string;
|
||||
label: string;
|
||||
group: string;
|
||||
/** 组内子分组(如小程序分享按场景折叠) */
|
||||
subgroup?: string;
|
||||
type: SystemConfigFieldType;
|
||||
secret?: boolean;
|
||||
requiresRestart: boolean;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/** 企微群机器人 Webhook · 推送条件(v3.4.11) */
|
||||
/** 企微群机器人 Webhook · 推送条件(v3.4.11 + v3.5.3 门店审核) */
|
||||
export const WECOM_PUSH_CONDITIONS = [
|
||||
'alert.ops',
|
||||
'support_ticket.created',
|
||||
@@ -7,6 +7,8 @@ export const WECOM_PUSH_CONDITIONS = [
|
||||
'alert.system',
|
||||
'alert.settlement',
|
||||
'dev_plan.task_dispatch',
|
||||
'store.audit_pending',
|
||||
'store.package_audit_pending',
|
||||
] as const;
|
||||
|
||||
export type WecomPushCondition = (typeof WECOM_PUSH_CONDITIONS)[number];
|
||||
@@ -19,6 +21,8 @@ export const WECOM_PUSH_CONDITION_LABELS: Record<WecomPushCondition, string> = {
|
||||
'alert.system': '系统监控(5xx、回调、定时任务)',
|
||||
'alert.settlement': '结算任务告警',
|
||||
'dev_plan.task_dispatch': '开发任务评审派发',
|
||||
'store.audit_pending': '门店提交/重提待审',
|
||||
'store.package_audit_pending': '套餐变更待审',
|
||||
};
|
||||
|
||||
export const WECOM_PUSH_CONDITION_GROUPS: Array<{
|
||||
@@ -46,6 +50,11 @@ export const WECOM_PUSH_CONDITION_GROUPS: Array<{
|
||||
label: '开发计划',
|
||||
conditions: ['dev_plan.task_dispatch'],
|
||||
},
|
||||
{
|
||||
key: 'store_audit',
|
||||
label: '门店审核',
|
||||
conditions: ['store.audit_pending', 'store.package_audit_pending'],
|
||||
},
|
||||
];
|
||||
|
||||
/** 默认「运营告警」推送条件 */
|
||||
@@ -64,6 +73,18 @@ export const WECOM_PUSH_DEFAULT_DEV_DISPATCH_CONDITIONS: WecomPushCondition[] =
|
||||
'support_ticket.created',
|
||||
];
|
||||
|
||||
/** 默认「门店审核通知群」推送条件 */
|
||||
export const WECOM_PUSH_DEFAULT_STORE_AUDIT_CONDITIONS: WecomPushCondition[] = [
|
||||
'store.audit_pending',
|
||||
'store.package_audit_pending',
|
||||
];
|
||||
|
||||
export const WECOM_STORE_AUDIT_PUSH_NAME = '门店审核通知群';
|
||||
|
||||
/** 占位 webhook(未配置真实 key 时禁用,避免误推) */
|
||||
export const WECOM_STORE_AUDIT_PLACEHOLDER_WEBHOOK =
|
||||
'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=PENDING';
|
||||
|
||||
export function parseWecomPushConditions(
|
||||
raw?: string | string[] | null,
|
||||
): WecomPushCondition[] {
|
||||
|
||||
Reference in New Issue
Block a user