feat(store): dual business hours, avg price, and status lock
CI / verify (pull_request) Has been cancelled
CI / verify (pull_request) Has been cancelled
Support 1-2 hour segments and optional avgPrice; show bank settlement on HQ store detail; enforce pickup min 2 bottles; Chinese order statuses; block shop reopen after permanent close. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -14,8 +14,9 @@ export function validateMinPurchase(
|
||||
crossMinQty: number,
|
||||
): { ok: boolean; message?: string } {
|
||||
if (deliveryType === 'ON_SITE_PICKUP') {
|
||||
if (quantity < 1) {
|
||||
return { ok: false, message: '现场取货至少购买 1 瓶' };
|
||||
const min = localMinQty > 0 ? localMinQty : 2;
|
||||
if (quantity < min) {
|
||||
return { ok: false, message: `现场提货至少购买 ${min} 瓶` };
|
||||
}
|
||||
return { ok: true };
|
||||
}
|
||||
@@ -95,7 +96,56 @@ export function calcRedeemSettleAmount(amount: number, settlementRate: number):
|
||||
return Math.round(amount * settlementRate * 100) / 100;
|
||||
}
|
||||
|
||||
/** 箱规默认瓶数(跨城起购 / 物流计价 / 大单拦截) */
|
||||
const TIME_HM_RE = /^([01]\d|2[0-3]):([0-5]\d)$/;
|
||||
|
||||
export type BusinessHoursSegment = { open: string; close: string };
|
||||
|
||||
export function timeToMinutes(hhmm: string): number {
|
||||
const [h, m] = hhmm.split(':').map(Number);
|
||||
return h * 60 + m;
|
||||
}
|
||||
|
||||
/** 校验 1~2 段营业时间,例如 09:00-22:00 或 09:00-14:00 + 17:00-21:00 */
|
||||
export function validateBusinessHours(
|
||||
segments: BusinessHoursSegment[],
|
||||
): { ok: boolean; message?: string } {
|
||||
const cleaned = segments
|
||||
.map((s) => ({ open: String(s.open || '').trim(), close: String(s.close || '').trim() }))
|
||||
.filter((s) => s.open || s.close);
|
||||
if (cleaned.length < 1) return { ok: false, message: '请设置营业时间' };
|
||||
if (cleaned.length > 2) return { ok: false, message: '营业时间最多支持 2 段' };
|
||||
for (let i = 0; i < cleaned.length; i++) {
|
||||
const s = cleaned[i];
|
||||
if (!TIME_HM_RE.test(s.open) || !TIME_HM_RE.test(s.close)) {
|
||||
return { ok: false, message: `第 ${i + 1} 段时间格式须为 HH:MM` };
|
||||
}
|
||||
if (timeToMinutes(s.open) >= timeToMinutes(s.close)) {
|
||||
return { ok: false, message: `第 ${i + 1} 段结束时间须晚于开始时间` };
|
||||
}
|
||||
}
|
||||
if (cleaned.length === 2 && timeToMinutes(cleaned[0].close) >= timeToMinutes(cleaned[1].open)) {
|
||||
return { ok: false, message: '第二段开始时间须晚于第一段结束时间' };
|
||||
}
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
export function formatBusinessHours(store: {
|
||||
openTime?: string | null;
|
||||
closeTime?: string | null;
|
||||
openTime2?: string | null;
|
||||
closeTime2?: string | null;
|
||||
}): string {
|
||||
const parts: string[] = [];
|
||||
if (store.openTime && store.closeTime) {
|
||||
parts.push(`${store.openTime}-${store.closeTime}`);
|
||||
}
|
||||
if (store.openTime2 && store.closeTime2) {
|
||||
parts.push(`${store.openTime2}-${store.closeTime2}`);
|
||||
}
|
||||
return parts.length ? parts.join(',') : '10:00-22:00';
|
||||
}
|
||||
|
||||
/** 物流(快递)按瓶计价规则,如小飞侠:2瓶6元、加一瓶+2元、6瓶一箱14元 */
|
||||
export const BOTTLES_PER_BOX = 6;
|
||||
|
||||
/** 小飞侠自动推单上限箱数:达到该箱数起拦截,需总部确认后推单或自配送 */
|
||||
|
||||
Reference in New Issue
Block a user