@@ -300,7 +300,7 @@ body:has(.big-screen-page),
|
|||||||
.big-screen-list-head,
|
.big-screen-list-head,
|
||||||
.big-screen-row {
|
.big-screen-row {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 180px 1fr 160px 200px;
|
grid-template-columns: 208px 1fr 160px 200px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
column-gap: 16px;
|
column-gap: 16px;
|
||||||
}
|
}
|
||||||
@@ -434,6 +434,13 @@ body:has(.big-screen-page),
|
|||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-variant-numeric: tabular-nums;
|
font-variant-numeric: tabular-nums;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.big-screen-amount--6d {
|
||||||
|
font-size: 0.86em;
|
||||||
|
letter-spacing: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.big-screen-items {
|
.big-screen-items {
|
||||||
@@ -540,13 +547,14 @@ body:has(.big-screen-page),
|
|||||||
.big-screen-fx-card {
|
.big-screen-fx-card {
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
min-width: 420px;
|
min-width: min(420px, 92vw);
|
||||||
max-width: 72vw;
|
max-width: min(920px, 92vw);
|
||||||
padding: 28px 40px 32px;
|
padding: 28px 40px 32px;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
background: rgba(8, 22, 48, 0.82);
|
background: rgba(8, 22, 48, 0.82);
|
||||||
backdrop-filter: blur(8px);
|
backdrop-filter: blur(8px);
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.big-screen-fx-card--t1 {
|
.big-screen-fx-card--t1 {
|
||||||
@@ -562,7 +570,7 @@ body:has(.big-screen-page),
|
|||||||
}
|
}
|
||||||
|
|
||||||
.big-screen-fx-card--t3 {
|
.big-screen-fx-card--t3 {
|
||||||
min-width: 520px;
|
min-width: min(560px, 94vw);
|
||||||
padding: 36px 48px 40px;
|
padding: 36px 48px 40px;
|
||||||
border: 3px solid #ffd666;
|
border: 3px solid #ffd666;
|
||||||
box-shadow:
|
box-shadow:
|
||||||
@@ -623,12 +631,19 @@ body:has(.big-screen-page),
|
|||||||
}
|
}
|
||||||
|
|
||||||
.big-screen-fx-amount {
|
.big-screen-fx-amount {
|
||||||
font-size: 64px;
|
font-size: clamp(44px, 8.5vw, 64px);
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
font-variant-numeric: tabular-nums;
|
font-variant-numeric: tabular-nums;
|
||||||
letter-spacing: 2px;
|
letter-spacing: 2px;
|
||||||
line-height: 1.1;
|
line-height: 1.1;
|
||||||
color: #e6f7ff;
|
color: #e6f7ff;
|
||||||
|
max-width: 100%;
|
||||||
|
margin-inline: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.big-screen-fx-amount--6d {
|
||||||
|
font-size: clamp(36px, 7vw, 52px) !important;
|
||||||
|
letter-spacing: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.big-screen-fx-card--t1 .big-screen-fx-amount {
|
.big-screen-fx-card--t1 .big-screen-fx-amount {
|
||||||
@@ -643,7 +658,7 @@ body:has(.big-screen-page),
|
|||||||
}
|
}
|
||||||
|
|
||||||
.big-screen-fx-card--t3 .big-screen-fx-amount {
|
.big-screen-fx-card--t3 .big-screen-fx-amount {
|
||||||
font-size: 84px;
|
font-size: clamp(52px, 10vw, 84px);
|
||||||
color: #ffd666;
|
color: #ffd666;
|
||||||
text-shadow:
|
text-shadow:
|
||||||
0 0 12px #ffd666,
|
0 0 12px #ffd666,
|
||||||
@@ -651,6 +666,11 @@ body:has(.big-screen-page),
|
|||||||
animation: big-screen-amount-flash 0.9s ease-in-out infinite;
|
animation: big-screen-amount-flash 0.9s ease-in-out infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.big-screen-fx-card--t3 .big-screen-fx-amount--6d {
|
||||||
|
font-size: clamp(44px, 8.5vw, 68px) !important;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
@keyframes big-screen-amount-pop {
|
@keyframes big-screen-amount-pop {
|
||||||
0% {
|
0% {
|
||||||
transform: scale(0.7);
|
transform: scale(0.7);
|
||||||
@@ -695,14 +715,23 @@ body:has(.big-screen-page),
|
|||||||
}
|
}
|
||||||
.big-screen-list-head,
|
.big-screen-list-head,
|
||||||
.big-screen-row {
|
.big-screen-row {
|
||||||
grid-template-columns: 140px 1fr 120px 160px;
|
grid-template-columns: 172px 1fr 120px 160px;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
|
.big-screen-amount--6d {
|
||||||
|
font-size: 0.82em;
|
||||||
|
}
|
||||||
.big-screen-fx-amount {
|
.big-screen-fx-amount {
|
||||||
font-size: 44px;
|
font-size: clamp(36px, 8vw, 44px);
|
||||||
}
|
}
|
||||||
.big-screen-fx-card--t3 .big-screen-fx-amount {
|
.big-screen-fx-card--t3 .big-screen-fx-amount {
|
||||||
font-size: 56px;
|
font-size: clamp(40px, 9vw, 56px);
|
||||||
|
}
|
||||||
|
.big-screen-fx-amount--6d {
|
||||||
|
font-size: clamp(32px, 6.5vw, 40px) !important;
|
||||||
|
}
|
||||||
|
.big-screen-fx-card--t3 .big-screen-fx-amount--6d {
|
||||||
|
font-size: clamp(34px, 7vw, 48px) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -217,6 +217,12 @@ export default function ProxyOrderModal({ open, onClose, onSuccess }: ProxyOrder
|
|||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: JSON.stringify({ payMethod: 'NATIVE' }),
|
body: JSON.stringify({ payMethod: 'NATIVE' }),
|
||||||
});
|
});
|
||||||
|
if (pay.mode === 'mock') {
|
||||||
|
message.success(`支付成功:${order.orderNo}`);
|
||||||
|
resetForm();
|
||||||
|
onSuccess({ id: order.id, orderNo: order.orderNo });
|
||||||
|
return;
|
||||||
|
}
|
||||||
setCodeUrl(pay.codeUrl ?? null);
|
setCodeUrl(pay.codeUrl ?? null);
|
||||||
startPoll(order.id);
|
startPoll(order.id);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -1,71 +1,5 @@
|
|||||||
export const PACKAGE_AUDIT_CHANGED_EVENT = 'admin:package-audit-changed';
|
export const PACKAGE_AUDIT_CHANGED_EVENT = 'admin:package-audit-changed';
|
||||||
export const AUDIT_NOTICE_CHANGED_EVENT = 'dukang:audit-notice-changed';
|
export const AUDIT_NOTICE_CHANGED_EVENT = 'dukang:audit-notice-changed';
|
||||||
export const BIG_SCREEN_DEMO_CHANNEL = 'dukang-big-screen';
|
|
||||||
export const BIG_SCREEN_DEMO_STORAGE_KEY = 'dukang:big-screen-demo';
|
|
||||||
|
|
||||||
export type BigScreenDemoOrder = {
|
|
||||||
payAmount: number;
|
|
||||||
items: string;
|
|
||||||
userPhoneMasked: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type BigScreenDemoPayload = {
|
|
||||||
type: 'demo';
|
|
||||||
id: string;
|
|
||||||
at: number;
|
|
||||||
orders: BigScreenDemoOrder[];
|
|
||||||
};
|
|
||||||
|
|
||||||
export function buildBigScreenDemoPayload(): BigScreenDemoPayload {
|
|
||||||
return {
|
|
||||||
type: 'demo',
|
|
||||||
id: `demo-${Date.now()}`,
|
|
||||||
at: Date.now(),
|
|
||||||
orders: [
|
|
||||||
{ payAmount: 1288, items: '杜康君酿 · 浓香型 × 6瓶', userPhoneMasked: '138****8888' },
|
|
||||||
{ payAmount: 688, items: '杜康君酿 · 浓香型 × 2瓶', userPhoneMasked: '139****6666' },
|
|
||||||
{ payAmount: 188, items: '杜康君酿 · 浓香型 × 1瓶', userPhoneMasked: '137****1888' },
|
|
||||||
],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function triggerBigScreenDemo() {
|
|
||||||
const payload = buildBigScreenDemoPayload();
|
|
||||||
try {
|
|
||||||
localStorage.setItem(BIG_SCREEN_DEMO_STORAGE_KEY, JSON.stringify(payload));
|
|
||||||
} catch {
|
|
||||||
/* ignore quota */
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const ch = new BroadcastChannel(BIG_SCREEN_DEMO_CHANNEL);
|
|
||||||
ch.postMessage(payload);
|
|
||||||
ch.close();
|
|
||||||
} catch {
|
|
||||||
/* BroadcastChannel 不可用时靠 localStorage 冷启动 */
|
|
||||||
}
|
|
||||||
return payload;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function readPendingBigScreenDemo(maxAgeMs = 8000): BigScreenDemoPayload | null {
|
|
||||||
try {
|
|
||||||
const raw = localStorage.getItem(BIG_SCREEN_DEMO_STORAGE_KEY);
|
|
||||||
if (!raw) return null;
|
|
||||||
const parsed = JSON.parse(raw) as BigScreenDemoPayload;
|
|
||||||
if (parsed?.type !== 'demo' || !parsed.id || !Array.isArray(parsed.orders)) return null;
|
|
||||||
if (Date.now() - Number(parsed.at || 0) > maxAgeMs) return null;
|
|
||||||
return parsed;
|
|
||||||
} catch {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function clearPendingBigScreenDemo() {
|
|
||||||
try {
|
|
||||||
localStorage.removeItem(BIG_SCREEN_DEMO_STORAGE_KEY);
|
|
||||||
} catch {
|
|
||||||
/* ignore */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function notifyPackageAuditChanged() {
|
export function notifyPackageAuditChanged() {
|
||||||
window.dispatchEvent(new Event(PACKAGE_AUDIT_CHANGED_EVENT));
|
window.dispatchEvent(new Event(PACKAGE_AUDIT_CHANGED_EVENT));
|
||||||
|
|||||||
@@ -1,18 +1,14 @@
|
|||||||
import { useCallback, useEffect, useMemo, useRef, useState, type CSSProperties } from 'react';
|
import { useCallback, useEffect, useMemo, useRef, useState, type CSSProperties } from 'react';
|
||||||
import { request } from '../lib/api';
|
import { request } from '../lib/api';
|
||||||
import {
|
|
||||||
BIG_SCREEN_DEMO_CHANNEL,
|
|
||||||
clearPendingBigScreenDemo,
|
|
||||||
readPendingBigScreenDemo,
|
|
||||||
type BigScreenDemoPayload,
|
|
||||||
} from '../lib/admin-events';
|
|
||||||
|
|
||||||
export type BigScreenOrder = {
|
export type BigScreenOrder = {
|
||||||
id: string;
|
id: string;
|
||||||
orderNo: string;
|
orderNo: string;
|
||||||
payAmount: number;
|
payAmount: number;
|
||||||
items: string;
|
items: string;
|
||||||
|
/** 展示用时间(付款成功时间) */
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
|
paidAt?: string;
|
||||||
userPhoneMasked: string | null;
|
userPhoneMasked: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -23,6 +19,101 @@ const ROW_MS = 2500;
|
|||||||
const WEEKDAYS = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
|
const WEEKDAYS = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
|
||||||
const TIER_DURATION: Record<AmountTier, number> = { 1: 3000, 2: 4500, 3: 6000 };
|
const TIER_DURATION: Record<AmountTier, number> = { 1: 3000, 2: 4500, 3: 6000 };
|
||||||
|
|
||||||
|
const SEED_IDS = new Set(
|
||||||
|
['seed-1', 'seed-2', 'seed-3', 'seed-4', 'seed-5', 'seed-6', 'seed-7', 'seed-8'],
|
||||||
|
);
|
||||||
|
|
||||||
|
function seedPaidAt(h: number, m: number, s: number): string {
|
||||||
|
const d = new Date();
|
||||||
|
d.setHours(h, m, s, 0);
|
||||||
|
return d.toISOString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 发布会滚动列表垫底演示数据(不参与成交动效) */
|
||||||
|
const BIG_SCREEN_SEED_ORDERS: BigScreenOrder[] = [
|
||||||
|
{
|
||||||
|
id: 'seed-1',
|
||||||
|
orderNo: 'DK-DEMO-001',
|
||||||
|
payAmount: 1288,
|
||||||
|
items: '杜康君酿 · 浓香型 × 6瓶',
|
||||||
|
createdAt: seedPaidAt(14, 28, 36),
|
||||||
|
paidAt: seedPaidAt(14, 28, 36),
|
||||||
|
userPhoneMasked: '138****8888',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'seed-2',
|
||||||
|
orderNo: 'DK-DEMO-002',
|
||||||
|
payAmount: 688,
|
||||||
|
items: '杜康君酿 · 浓香型 × 2瓶',
|
||||||
|
createdAt: seedPaidAt(14, 22, 18),
|
||||||
|
paidAt: seedPaidAt(14, 22, 18),
|
||||||
|
userPhoneMasked: '139****6666',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'seed-3',
|
||||||
|
orderNo: 'DK-DEMO-003',
|
||||||
|
payAmount: 1588,
|
||||||
|
items: '杜康典藏 · 礼盒装 × 2套',
|
||||||
|
createdAt: seedPaidAt(14, 15, 42),
|
||||||
|
paidAt: seedPaidAt(14, 15, 42),
|
||||||
|
userPhoneMasked: '136****9527',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'seed-4',
|
||||||
|
orderNo: 'DK-DEMO-004',
|
||||||
|
payAmount: 520,
|
||||||
|
items: '杜康特曲 · 500ml × 4瓶',
|
||||||
|
createdAt: seedPaidAt(14, 8, 55),
|
||||||
|
paidAt: seedPaidAt(14, 8, 55),
|
||||||
|
userPhoneMasked: '135****3210',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'seed-5',
|
||||||
|
orderNo: 'DK-DEMO-005',
|
||||||
|
payAmount: 268,
|
||||||
|
items: '杜康君酿 · 浓香型 × 1瓶',
|
||||||
|
createdAt: seedPaidAt(13, 56, 7),
|
||||||
|
paidAt: seedPaidAt(13, 56, 7),
|
||||||
|
userPhoneMasked: '137****1888',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'seed-6',
|
||||||
|
orderNo: 'DK-DEMO-006',
|
||||||
|
payAmount: 999,
|
||||||
|
items: '杜康窖藏 · 珍藏版 × 3瓶',
|
||||||
|
createdAt: seedPaidAt(13, 48, 33),
|
||||||
|
paidAt: seedPaidAt(13, 48, 33),
|
||||||
|
userPhoneMasked: '133****7788',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'seed-7',
|
||||||
|
orderNo: 'DK-DEMO-007',
|
||||||
|
payAmount: 188,
|
||||||
|
items: '杜康小酒 · 品鉴装 × 2瓶',
|
||||||
|
createdAt: seedPaidAt(13, 35, 21),
|
||||||
|
paidAt: seedPaidAt(13, 35, 21),
|
||||||
|
userPhoneMasked: '158****0099',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'seed-8',
|
||||||
|
orderNo: 'DK-DEMO-008',
|
||||||
|
payAmount: 1200,
|
||||||
|
items: '杜康君酿 · 浓香型 × 5瓶',
|
||||||
|
createdAt: seedPaidAt(13, 22, 49),
|
||||||
|
paidAt: seedPaidAt(13, 22, 49),
|
||||||
|
userPhoneMasked: '186****5566',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
function mergeDisplayItems(live: BigScreenOrder[]): BigScreenOrder[] {
|
||||||
|
if (!live.length) return [...BIG_SCREEN_SEED_ORDERS];
|
||||||
|
return [...live, ...BIG_SCREEN_SEED_ORDERS];
|
||||||
|
}
|
||||||
|
|
||||||
|
function isSeedOrder(order: BigScreenOrder): boolean {
|
||||||
|
return SEED_IDS.has(order.id);
|
||||||
|
}
|
||||||
|
|
||||||
function pad(n: number) {
|
function pad(n: number) {
|
||||||
return String(n).padStart(2, '0');
|
return String(n).padStart(2, '0');
|
||||||
}
|
}
|
||||||
@@ -35,7 +126,7 @@ function formatDateLine(d: Date) {
|
|||||||
return `${d.getFullYear()} / ${pad(d.getMonth() + 1)} / ${pad(d.getDate())} ${WEEKDAYS[d.getDay()]}`;
|
return `${d.getFullYear()} / ${pad(d.getMonth() + 1)} / ${pad(d.getDate())} ${WEEKDAYS[d.getDay()]}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatHm(iso: string): string {
|
function formatOrderTime(iso: string): string {
|
||||||
try {
|
try {
|
||||||
return formatClock(new Date(iso));
|
return formatClock(new Date(iso));
|
||||||
} catch {
|
} catch {
|
||||||
@@ -50,6 +141,11 @@ function formatAmount(n: number): string {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function amountCssClass(payAmount: number, base: string): string {
|
||||||
|
const wide = Math.floor(Math.abs(Number(payAmount || 0))) >= 100_000;
|
||||||
|
return wide ? `${base} ${base}--6d` : base;
|
||||||
|
}
|
||||||
|
|
||||||
export function amountTier(payAmount: number): AmountTier {
|
export function amountTier(payAmount: number): AmountTier {
|
||||||
if (payAmount >= 1000) return 3;
|
if (payAmount >= 1000) return 3;
|
||||||
if (payAmount >= 500) return 2;
|
if (payAmount >= 500) return 2;
|
||||||
@@ -83,9 +179,11 @@ function OrderRow({
|
|||||||
className={`big-screen-row big-screen-row--t${tier}${latest ? ' is-latest' : ''}`}
|
className={`big-screen-row big-screen-row--t${tier}${latest ? ' is-latest' : ''}`}
|
||||||
>
|
>
|
||||||
{latest ? <span className="big-screen-row-mark" /> : null}
|
{latest ? <span className="big-screen-row-mark" /> : null}
|
||||||
<span className="big-screen-amount">¥ {formatAmount(order.payAmount)}</span>
|
<span className={amountCssClass(order.payAmount, 'big-screen-amount')}>
|
||||||
|
¥ {formatAmount(order.payAmount)}
|
||||||
|
</span>
|
||||||
<span className="big-screen-items">{order.items || '—'}</span>
|
<span className="big-screen-items">{order.items || '—'}</span>
|
||||||
<span className="big-screen-time">{formatHm(order.createdAt)}</span>
|
<span className="big-screen-time">{formatOrderTime(order.paidAt ?? order.createdAt)}</span>
|
||||||
<span className="big-screen-phone">{order.userPhoneMasked || '—'}</span>
|
<span className="big-screen-phone">{order.userPhoneMasked || '—'}</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -253,11 +351,13 @@ function CelebrateFx({ order, onDone }: { order: BigScreenOrder; onDone: () => v
|
|||||||
{tier >= 2 ? <div className="big-screen-fx-sweep big-screen-fx-sweep--alt" /> : null}
|
{tier >= 2 ? <div className="big-screen-fx-sweep big-screen-fx-sweep--alt" /> : null}
|
||||||
<div className={`big-screen-fx-card big-screen-fx-card--t${tier}`}>
|
<div className={`big-screen-fx-card big-screen-fx-card--t${tier}`}>
|
||||||
<div className="big-screen-fx-kicker">{tier === 3 ? '高额成交' : tier === 2 ? '大额成交' : '新成交'}</div>
|
<div className="big-screen-fx-kicker">{tier === 3 ? '高额成交' : tier === 2 ? '大额成交' : '新成交'}</div>
|
||||||
<div className="big-screen-fx-amount">¥ {formatAmount(displayAmount)}</div>
|
<div className={amountCssClass(displayAmount || order.payAmount, 'big-screen-fx-amount')}>
|
||||||
|
¥ {formatAmount(displayAmount)}
|
||||||
|
</div>
|
||||||
<div className="big-screen-fx-items">{order.items || '—'}</div>
|
<div className="big-screen-fx-items">{order.items || '—'}</div>
|
||||||
<div className="big-screen-fx-meta">
|
<div className="big-screen-fx-meta">
|
||||||
<span>{order.userPhoneMasked || '—'}</span>
|
<span>{order.userPhoneMasked || '—'}</span>
|
||||||
<span>{formatHm(order.createdAt)}</span>
|
<span>{formatOrderTime(order.paidAt ?? order.createdAt)}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -270,12 +370,16 @@ export default function BigScreenPage() {
|
|||||||
const [paused, setPaused] = useState(false);
|
const [paused, setPaused] = useState(false);
|
||||||
const [celebrate, setCelebrate] = useState<BigScreenOrder | null>(null);
|
const [celebrate, setCelebrate] = useState<BigScreenOrder | null>(null);
|
||||||
const seenIdsRef = useRef<Set<string> | null>(null);
|
const seenIdsRef = useRef<Set<string> | null>(null);
|
||||||
|
const celebratedIdsRef = useRef<Set<string>>(new Set());
|
||||||
const queueRef = useRef<BigScreenOrder[]>([]);
|
const queueRef = useRef<BigScreenOrder[]>([]);
|
||||||
const celebratingRef = useRef(false);
|
const celebratingRef = useRef(false);
|
||||||
const viewportRef = useRef<HTMLDivElement | null>(null);
|
const viewportRef = useRef<HTMLDivElement | null>(null);
|
||||||
const demoSeenRef = useRef<Set<string>>(new Set());
|
|
||||||
const [viewportH, setViewportH] = useState(0);
|
const [viewportH, setViewportH] = useState(0);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
for (const o of BIG_SCREEN_SEED_ORDERS) celebratedIdsRef.current.add(o.id);
|
||||||
|
}, []);
|
||||||
|
|
||||||
const playNext = useCallback(() => {
|
const playNext = useCallback(() => {
|
||||||
const next = queueRef.current.shift() ?? null;
|
const next = queueRef.current.shift() ?? null;
|
||||||
celebratingRef.current = !!next;
|
celebratingRef.current = !!next;
|
||||||
@@ -285,11 +389,13 @@ export default function BigScreenPage() {
|
|||||||
|
|
||||||
const enqueueNew = useCallback(
|
const enqueueNew = useCallback(
|
||||||
(fresh: BigScreenOrder[]) => {
|
(fresh: BigScreenOrder[]) => {
|
||||||
if (!fresh.length) return;
|
const toCelebrate = fresh.filter((o) => !celebratedIdsRef.current.has(o.id));
|
||||||
const ranked = [...fresh].sort((a, b) => {
|
if (!toCelebrate.length) return;
|
||||||
|
for (const o of toCelebrate) celebratedIdsRef.current.add(o.id);
|
||||||
|
const ranked = [...toCelebrate].sort((a, b) => {
|
||||||
const td = amountTier(b.payAmount) - amountTier(a.payAmount);
|
const td = amountTier(b.payAmount) - amountTier(a.payAmount);
|
||||||
if (td !== 0) return td;
|
if (td !== 0) return td;
|
||||||
return +new Date(b.createdAt) - +new Date(a.createdAt);
|
return +new Date(b.paidAt ?? b.createdAt) - +new Date(a.paidAt ?? a.createdAt);
|
||||||
});
|
});
|
||||||
queueRef.current.push(...ranked);
|
queueRef.current.push(...ranked);
|
||||||
if (!celebratingRef.current) playNext();
|
if (!celebratingRef.current) playNext();
|
||||||
@@ -297,38 +403,16 @@ export default function BigScreenPage() {
|
|||||||
[playNext],
|
[playNext],
|
||||||
);
|
);
|
||||||
|
|
||||||
const playDemo = useCallback(
|
|
||||||
(payload: BigScreenDemoPayload) => {
|
|
||||||
if (!payload?.orders?.length || demoSeenRef.current.has(payload.id)) return;
|
|
||||||
demoSeenRef.current.add(payload.id);
|
|
||||||
const now = new Date().toISOString();
|
|
||||||
const fake: BigScreenOrder[] = payload.orders.map((o, i) => ({
|
|
||||||
id: `${payload.id}-${i}`,
|
|
||||||
orderNo: `${payload.id}-${i}`,
|
|
||||||
payAmount: o.payAmount,
|
|
||||||
items: o.items,
|
|
||||||
createdAt: now,
|
|
||||||
userPhoneMasked: o.userPhoneMasked,
|
|
||||||
}));
|
|
||||||
setItems((prev) => [...fake, ...prev.filter((row) => !row.id.startsWith('demo-'))]);
|
|
||||||
enqueueNew(fake);
|
|
||||||
clearPendingBigScreenDemo();
|
|
||||||
},
|
|
||||||
[enqueueNew],
|
|
||||||
);
|
|
||||||
|
|
||||||
const fetchData = useCallback(() => {
|
const fetchData = useCallback(() => {
|
||||||
return request<{ items: BigScreenOrder[] }>('/admin/orders/big-screen?limit=2000')
|
return request<{ items: BigScreenOrder[] }>('/admin/orders/big-screen?limit=2000')
|
||||||
.then((d) => {
|
.then((d) => {
|
||||||
const list = d.items ?? [];
|
const list = d.items ?? [];
|
||||||
setItems((prev) => {
|
setItems(mergeDisplayItems(list));
|
||||||
const demos = prev.filter((o) => o.id.startsWith('demo-'));
|
|
||||||
return demos.length ? [...demos, ...list] : list;
|
|
||||||
});
|
|
||||||
setLoadError('');
|
setLoadError('');
|
||||||
const seen = seenIdsRef.current;
|
const seen = seenIdsRef.current;
|
||||||
if (!seen) {
|
if (!seen) {
|
||||||
seenIdsRef.current = new Set(list.map((o) => o.id));
|
seenIdsRef.current = new Set(list.map((o) => o.id));
|
||||||
|
for (const o of list) celebratedIdsRef.current.add(o.id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const fresh = list.filter((o) => !seen.has(o.id));
|
const fresh = list.filter((o) => !seen.has(o.id));
|
||||||
@@ -337,6 +421,7 @@ export default function BigScreenPage() {
|
|||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
setLoadError(e instanceof Error ? e.message : '加载失败');
|
setLoadError(e instanceof Error ? e.message : '加载失败');
|
||||||
|
setItems(mergeDisplayItems([]));
|
||||||
});
|
});
|
||||||
}, [enqueueNew]);
|
}, [enqueueNew]);
|
||||||
|
|
||||||
@@ -346,24 +431,6 @@ export default function BigScreenPage() {
|
|||||||
return () => clearInterval(id);
|
return () => clearInterval(id);
|
||||||
}, [fetchData]);
|
}, [fetchData]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const pending = readPendingBigScreenDemo();
|
|
||||||
if (pending) playDemo(pending);
|
|
||||||
|
|
||||||
let ch: BroadcastChannel | null = null;
|
|
||||||
try {
|
|
||||||
ch = new BroadcastChannel(BIG_SCREEN_DEMO_CHANNEL);
|
|
||||||
ch.onmessage = (ev: MessageEvent<BigScreenDemoPayload>) => {
|
|
||||||
if (ev.data?.type === 'demo' && ev.data.orders?.length) playDemo(ev.data);
|
|
||||||
};
|
|
||||||
} catch {
|
|
||||||
ch = null;
|
|
||||||
}
|
|
||||||
return () => {
|
|
||||||
ch?.close();
|
|
||||||
};
|
|
||||||
}, [playDemo]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const html = document.documentElement;
|
const html = document.documentElement;
|
||||||
const prevHtml = html.style.overflow;
|
const prevHtml = html.style.overflow;
|
||||||
@@ -399,7 +466,8 @@ export default function BigScreenPage() {
|
|||||||
const trackItems = useMemo(() => unitItems.concat(unitItems), [unitItems]);
|
const trackItems = useMemo(() => unitItems.concat(unitItems), [unitItems]);
|
||||||
const marqueeMs = Math.max(unitItems.length, 1) * ROW_MS;
|
const marqueeMs = Math.max(unitItems.length, 1) * ROW_MS;
|
||||||
const rolling = items.length > 0 && unitItems.length > 0;
|
const rolling = items.length > 0 && unitItems.length > 0;
|
||||||
const latestId = items[0]?.id;
|
const latestLive = items.find((o) => !isSeedOrder(o));
|
||||||
|
const latestId = latestLive?.id;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="big-screen-page">
|
<div className="big-screen-page">
|
||||||
@@ -431,7 +499,7 @@ export default function BigScreenPage() {
|
|||||||
<div className="big-screen-list-head">
|
<div className="big-screen-list-head">
|
||||||
<span>下单金额</span>
|
<span>下单金额</span>
|
||||||
<span>下单商品</span>
|
<span>下单商品</span>
|
||||||
<span>下单时间</span>
|
<span>付款时间</span>
|
||||||
<span>下单人</span>
|
<span>下单人</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="big-screen-list-body" ref={viewportRef}>
|
<div className="big-screen-list-body" ref={viewportRef}>
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import {
|
|||||||
} from 'antd';
|
} from 'antd';
|
||||||
import type { ColumnsType } from 'antd/es/table';
|
import type { ColumnsType } from 'antd/es/table';
|
||||||
import { request, type AdminOrderItem, type AdminOrderRow, type HqProfile, type Paginated } from '../lib/api';
|
import { request, type AdminOrderItem, type AdminOrderRow, type HqProfile, type Paginated } from '../lib/api';
|
||||||
import { triggerBigScreenDemo } from '../lib/admin-events';
|
|
||||||
import {
|
import {
|
||||||
ADMIN_OPTIONS_PAGE_SIZE,
|
ADMIN_OPTIONS_PAGE_SIZE,
|
||||||
DELIVERY_PROVIDER_LABELS,
|
DELIVERY_PROVIDER_LABELS,
|
||||||
@@ -192,7 +191,6 @@ export default function OrdersPage() {
|
|||||||
const [trackOpen, setTrackOpen] = useState(false);
|
const [trackOpen, setTrackOpen] = useState(false);
|
||||||
const canDeleteOrders = (profile?.permissionKeys ?? []).includes('orders_delete');
|
const canDeleteOrders = (profile?.permissionKeys ?? []).includes('orders_delete');
|
||||||
const canProxyOrder = (profile?.permissionKeys ?? []).includes('orders');
|
const canProxyOrder = (profile?.permissionKeys ?? []).includes('orders');
|
||||||
const isSuperAdmin = profile?.adminRole === 'SUPER_ADMIN';
|
|
||||||
|
|
||||||
const selectedOrders = useMemo(
|
const selectedOrders = useMemo(
|
||||||
() => (data?.items ?? []).filter((row) => selectedRowKeys.includes(row.id)),
|
() => (data?.items ?? []).filter((row) => selectedRowKeys.includes(row.id)),
|
||||||
@@ -545,17 +543,6 @@ export default function OrdersPage() {
|
|||||||
<Typography.Title level={4} style={{ margin: 0 }}>订单监控</Typography.Title>
|
<Typography.Title level={4} style={{ margin: 0 }}>订单监控</Typography.Title>
|
||||||
<Space>
|
<Space>
|
||||||
<Button onClick={() => window.open('/orders/big-screen', 'dukang-big-screen')}>大屏</Button>
|
<Button onClick={() => window.open('/orders/big-screen', 'dukang-big-screen')}>大屏</Button>
|
||||||
{isSuperAdmin ? (
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
window.open('/orders/big-screen', 'dukang-big-screen');
|
|
||||||
triggerBigScreenDemo();
|
|
||||||
message.success('已发送测试成交动效(三级 / 二级 / 一级)');
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
测试
|
|
||||||
</Button>
|
|
||||||
) : null}
|
|
||||||
{canProxyOrder ? (
|
{canProxyOrder ? (
|
||||||
<Button type="primary" onClick={() => setProxyOpen(true)}>
|
<Button type="primary" onClick={() => setProxyOpen(true)}>
|
||||||
代下单
|
代下单
|
||||||
|
|||||||
Generated
-323
@@ -168,128 +168,6 @@ importers:
|
|||||||
specifier: ^5.4.0
|
specifier: ^5.4.0
|
||||||
version: 5.4.21(@types/node@22.20.0)(sass@1.101.0)(terser@5.48.0)
|
version: 5.4.21(@types/node@22.20.0)(sass@1.101.0)(terser@5.48.0)
|
||||||
|
|
||||||
apps/h5-user:
|
|
||||||
dependencies:
|
|
||||||
'@dukang/client-logging':
|
|
||||||
specifier: workspace:*
|
|
||||||
version: link:../../packages/client-logging
|
|
||||||
'@dukang/shared-types':
|
|
||||||
specifier: workspace:*
|
|
||||||
version: link:../../packages/shared-types
|
|
||||||
'@dukang/shared-ui':
|
|
||||||
specifier: workspace:*
|
|
||||||
version: link:../../packages/shared-ui
|
|
||||||
'@dukang/weixin-sdk':
|
|
||||||
specifier: workspace:*
|
|
||||||
version: link:../../packages/weixin-sdk
|
|
||||||
element-china-area-data:
|
|
||||||
specifier: ^6.1.0
|
|
||||||
version: 6.1.0
|
|
||||||
react:
|
|
||||||
specifier: ^18.3.1
|
|
||||||
version: 18.3.1
|
|
||||||
react-dom:
|
|
||||||
specifier: ^18.3.1
|
|
||||||
version: 18.3.1(react@18.3.1)
|
|
||||||
react-router-dom:
|
|
||||||
specifier: ^6.26.0
|
|
||||||
version: 6.30.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
|
||||||
devDependencies:
|
|
||||||
'@types/react':
|
|
||||||
specifier: ^18.3.3
|
|
||||||
version: 18.3.31
|
|
||||||
'@types/react-dom':
|
|
||||||
specifier: ^18.3.0
|
|
||||||
version: 18.3.7(@types/react@18.3.31)
|
|
||||||
'@vitejs/plugin-react':
|
|
||||||
specifier: ^4.3.1
|
|
||||||
version: 4.7.0(vite@5.4.21(@types/node@22.20.0)(sass@1.101.0)(terser@5.48.0))
|
|
||||||
typescript:
|
|
||||||
specifier: ^5.4.5
|
|
||||||
version: 5.9.3
|
|
||||||
vite:
|
|
||||||
specifier: ^5.4.0
|
|
||||||
version: 5.4.21(@types/node@22.20.0)(sass@1.101.0)(terser@5.48.0)
|
|
||||||
|
|
||||||
apps/mini-hq:
|
|
||||||
dependencies:
|
|
||||||
'@babel/runtime':
|
|
||||||
specifier: ^7.24.4
|
|
||||||
version: 7.29.7
|
|
||||||
'@dukang/shared-types':
|
|
||||||
specifier: workspace:*
|
|
||||||
version: link:../../packages/shared-types
|
|
||||||
'@dukang/weixin-sdk':
|
|
||||||
specifier: workspace:*
|
|
||||||
version: link:../../packages/weixin-sdk
|
|
||||||
'@tarojs/components':
|
|
||||||
specifier: 4.2.0
|
|
||||||
version: 4.2.0(@tarojs/helper@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(postcss@8.5.15))
|
|
||||||
'@tarojs/helper':
|
|
||||||
specifier: 4.2.0
|
|
||||||
version: 4.2.0
|
|
||||||
'@tarojs/plugin-framework-react':
|
|
||||||
specifier: 4.2.0
|
|
||||||
version: 4.2.0(@tarojs/helper@4.2.0)(@tarojs/runtime@4.2.0)(@tarojs/shared@4.2.0)(@vitejs/plugin-react@4.7.0(vite@5.4.21(@types/node@22.20.0)(sass@1.101.0)(terser@5.48.0)))(react@18.3.1)(vite@5.4.21(@types/node@22.20.0)(sass@1.101.0)(terser@5.48.0))(webpack@5.97.1(postcss@8.5.15))
|
|
||||||
'@tarojs/plugin-html':
|
|
||||||
specifier: 4.2.0
|
|
||||||
version: 4.2.0(@tarojs/helper@4.2.0)(@tarojs/runtime@4.2.0)(@tarojs/shared@4.2.0)
|
|
||||||
'@tarojs/plugin-platform-h5':
|
|
||||||
specifier: 4.2.0
|
|
||||||
version: 4.2.0(@tarojs/taro@4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(postcss@8.5.15)))(@tarojs/helper@4.2.0)(@tarojs/shared@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(postcss@8.5.15)))(@types/react@18.3.31)(postcss@8.5.15)(react@18.3.1)(rollup@3.30.0)(solid-js@1.9.14)(webpack@5.97.1(postcss@8.5.15))
|
|
||||||
'@tarojs/plugin-platform-weapp':
|
|
||||||
specifier: 4.2.0
|
|
||||||
version: 4.2.0(@tarojs/service@4.2.0)(@tarojs/shared@4.2.0)
|
|
||||||
'@tarojs/react':
|
|
||||||
specifier: 4.2.0
|
|
||||||
version: 4.2.0(react@18.3.1)
|
|
||||||
'@tarojs/router':
|
|
||||||
specifier: 4.2.0
|
|
||||||
version: 4.2.0(@tarojs/runtime@4.2.0)(@tarojs/shared@4.2.0)(@tarojs/taro@4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(postcss@8.5.15)))(@tarojs/helper@4.2.0)(@tarojs/shared@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(postcss@8.5.15)))
|
|
||||||
'@tarojs/runtime':
|
|
||||||
specifier: 4.2.0
|
|
||||||
version: 4.2.0
|
|
||||||
'@tarojs/shared':
|
|
||||||
specifier: 4.2.0
|
|
||||||
version: 4.2.0
|
|
||||||
'@tarojs/taro':
|
|
||||||
specifier: 4.2.0
|
|
||||||
version: 4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(postcss@8.5.15)))(@tarojs/helper@4.2.0)(@tarojs/shared@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(postcss@8.5.15))
|
|
||||||
qrcode:
|
|
||||||
specifier: ^1.5.4
|
|
||||||
version: 1.5.4
|
|
||||||
react:
|
|
||||||
specifier: ^18.3.1
|
|
||||||
version: 18.3.1
|
|
||||||
react-dom:
|
|
||||||
specifier: ^18.3.1
|
|
||||||
version: 18.3.1(react@18.3.1)
|
|
||||||
devDependencies:
|
|
||||||
'@babel/preset-react':
|
|
||||||
specifier: ^7.24.1
|
|
||||||
version: 7.29.7(@babel/core@7.29.7)
|
|
||||||
'@tarojs/cli':
|
|
||||||
specifier: 4.2.0
|
|
||||||
version: 4.2.0
|
|
||||||
'@tarojs/vite-runner':
|
|
||||||
specifier: 4.2.0
|
|
||||||
version: 4.2.0(@tarojs/runtime@4.2.0)(@types/babel__core@7.20.5)(postcss@8.5.15)(rollup@3.30.0)(terser@5.48.0)(typescript@5.9.3)(vite@5.4.21(@types/node@22.20.0)(sass@1.101.0)(terser@5.48.0))
|
|
||||||
'@types/react':
|
|
||||||
specifier: ^18.3.3
|
|
||||||
version: 18.3.31
|
|
||||||
'@vitejs/plugin-react':
|
|
||||||
specifier: ^4.3.1
|
|
||||||
version: 4.7.0(vite@5.4.21(@types/node@22.20.0)(sass@1.101.0)(terser@5.48.0))
|
|
||||||
babel-preset-taro:
|
|
||||||
specifier: 4.2.0
|
|
||||||
version: 4.2.0(@babel/core@7.29.7)(@babel/plugin-transform-typescript@7.29.7(@babel/core@7.29.7))(@babel/preset-react@7.29.7(@babel/core@7.29.7))
|
|
||||||
typescript:
|
|
||||||
specifier: ^5.4.5
|
|
||||||
version: 5.9.3
|
|
||||||
vite:
|
|
||||||
specifier: ^5.4.0
|
|
||||||
version: 5.4.21(@types/node@22.20.0)(sass@1.101.0)(terser@5.48.0)
|
|
||||||
|
|
||||||
apps/mini-user:
|
apps/mini-user:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime':
|
'@babel/runtime':
|
||||||
@@ -2534,14 +2412,6 @@ packages:
|
|||||||
webpack:
|
webpack:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@tarojs/plugin-html@4.2.0':
|
|
||||||
resolution: {integrity: sha512-q0NJ6WVZTxWwr4MIYU9cj84uSPNOZ1BC+IdI3aTSKPyACHACUQp0yaFSL8/MGUj2+QoOU0op6/PSJUHfDNRV+g==}
|
|
||||||
engines: {node: '>= 18'}
|
|
||||||
peerDependencies:
|
|
||||||
'@tarojs/helper': 4.2.0
|
|
||||||
'@tarojs/runtime': 4.2.0
|
|
||||||
'@tarojs/shared': 4.2.0
|
|
||||||
|
|
||||||
'@tarojs/plugin-platform-h5@4.2.0':
|
'@tarojs/plugin-platform-h5@4.2.0':
|
||||||
resolution: {integrity: sha512-HoyGHYaBrxJtaIRT1Y81LK7re0pLfF9DOGQ77bNmQwtuJS9JpiFMI47Q27+2anldwJodQdQP9d3KSPRqGnRLCw==}
|
resolution: {integrity: sha512-HoyGHYaBrxJtaIRT1Y81LK7re0pLfF9DOGQ77bNmQwtuJS9JpiFMI47Q27+2anldwJodQdQP9d3KSPRqGnRLCw==}
|
||||||
|
|
||||||
@@ -8803,29 +8673,6 @@ snapshots:
|
|||||||
- webpack-chain
|
- webpack-chain
|
||||||
- webpack-dev-server
|
- webpack-dev-server
|
||||||
|
|
||||||
'@tarojs/components-react@4.2.0(@tarojs/helper@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(react@18.3.1)(rollup@3.30.0)(solid-js@1.9.14)(webpack@5.97.1(postcss@8.5.15))':
|
|
||||||
dependencies:
|
|
||||||
'@babel/runtime': 7.29.7
|
|
||||||
'@tarojs/components': 4.2.0(@tarojs/helper@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(postcss@8.5.15))
|
|
||||||
'@tarojs/shared': 4.2.0
|
|
||||||
'@tarojs/taro': 4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(postcss@8.5.15)))(@tarojs/helper@4.2.0)(@tarojs/shared@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(postcss@8.5.15))
|
|
||||||
classnames: 2.5.1
|
|
||||||
identity-obj-proxy: 3.0.0
|
|
||||||
react: 18.3.1
|
|
||||||
solid-js: 1.9.14
|
|
||||||
swiper: 11.1.15
|
|
||||||
tslib: 2.8.1
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- '@tarojs/helper'
|
|
||||||
- '@types/react'
|
|
||||||
- html-webpack-plugin
|
|
||||||
- postcss
|
|
||||||
- rollup
|
|
||||||
- vue
|
|
||||||
- webpack
|
|
||||||
- webpack-chain
|
|
||||||
- webpack-dev-server
|
|
||||||
|
|
||||||
'@tarojs/components@4.2.0(@tarojs/helper@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96)(postcss@8.5.15))':
|
'@tarojs/components@4.2.0(@tarojs/helper@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96)(postcss@8.5.15))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@stencil/core': 2.22.3
|
'@stencil/core': 2.22.3
|
||||||
@@ -8849,29 +8696,6 @@ snapshots:
|
|||||||
- webpack-chain
|
- webpack-chain
|
||||||
- webpack-dev-server
|
- webpack-dev-server
|
||||||
|
|
||||||
'@tarojs/components@4.2.0(@tarojs/helper@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(postcss@8.5.15))':
|
|
||||||
dependencies:
|
|
||||||
'@stencil/core': 2.22.3
|
|
||||||
'@tarojs/runtime': 4.2.0
|
|
||||||
'@tarojs/shared': 4.2.0
|
|
||||||
'@tarojs/taro': 4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(postcss@8.5.15)))(@tarojs/helper@4.2.0)(@tarojs/shared@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(postcss@8.5.15))
|
|
||||||
classnames: 2.5.1
|
|
||||||
hammerjs: 2.0.8
|
|
||||||
hls.js: 1.6.16
|
|
||||||
resolve-pathname: 3.0.0
|
|
||||||
swiper: 11.1.15
|
|
||||||
tslib: 2.8.1
|
|
||||||
optionalDependencies:
|
|
||||||
'@types/react': 18.3.31
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- '@tarojs/helper'
|
|
||||||
- html-webpack-plugin
|
|
||||||
- postcss
|
|
||||||
- rollup
|
|
||||||
- webpack
|
|
||||||
- webpack-chain
|
|
||||||
- webpack-dev-server
|
|
||||||
|
|
||||||
'@tarojs/helper@4.2.0':
|
'@tarojs/helper@4.2.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/core': 7.29.7
|
'@babel/core': 7.29.7
|
||||||
@@ -9009,27 +8833,6 @@ snapshots:
|
|||||||
vite: 5.4.21(@types/node@22.20.0)(sass@1.101.0)(terser@5.48.0)
|
vite: 5.4.21(@types/node@22.20.0)(sass@1.101.0)(terser@5.48.0)
|
||||||
webpack: 5.97.1(@swc/core@1.3.96)(postcss@8.5.15)
|
webpack: 5.97.1(@swc/core@1.3.96)(postcss@8.5.15)
|
||||||
|
|
||||||
'@tarojs/plugin-framework-react@4.2.0(@tarojs/helper@4.2.0)(@tarojs/runtime@4.2.0)(@tarojs/shared@4.2.0)(@vitejs/plugin-react@4.7.0(vite@5.4.21(@types/node@22.20.0)(sass@1.101.0)(terser@5.48.0)))(react@18.3.1)(vite@5.4.21(@types/node@22.20.0)(sass@1.101.0)(terser@5.48.0))(webpack@5.97.1(postcss@8.5.15))':
|
|
||||||
dependencies:
|
|
||||||
'@tarojs/helper': 4.2.0
|
|
||||||
'@tarojs/runtime': 4.2.0
|
|
||||||
'@tarojs/shared': 4.2.0
|
|
||||||
acorn: 8.17.0
|
|
||||||
acorn-walk: 8.3.5
|
|
||||||
lodash: 4.18.1
|
|
||||||
tslib: 2.8.1
|
|
||||||
optionalDependencies:
|
|
||||||
'@vitejs/plugin-react': 4.7.0(vite@5.4.21(@types/node@22.20.0)(sass@1.101.0)(terser@5.48.0))
|
|
||||||
react: 18.3.1
|
|
||||||
vite: 5.4.21(@types/node@22.20.0)(sass@1.101.0)(terser@5.48.0)
|
|
||||||
webpack: 5.97.1(postcss@8.5.15)
|
|
||||||
|
|
||||||
'@tarojs/plugin-html@4.2.0(@tarojs/helper@4.2.0)(@tarojs/runtime@4.2.0)(@tarojs/shared@4.2.0)':
|
|
||||||
dependencies:
|
|
||||||
'@tarojs/helper': 4.2.0
|
|
||||||
'@tarojs/runtime': 4.2.0
|
|
||||||
'@tarojs/shared': 4.2.0
|
|
||||||
|
|
||||||
'@tarojs/plugin-platform-h5@4.2.0(@tarojs/taro@4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96)(postcss@8.5.15)))(@tarojs/helper@4.2.0)(@tarojs/shared@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96)(postcss@8.5.15)))(@types/react@18.3.31)(postcss@8.5.15)(react@18.3.1)(rollup@3.30.0)(solid-js@1.9.14)(webpack@5.97.1(@swc/core@1.3.96)(postcss@8.5.15))':
|
'@tarojs/plugin-platform-h5@4.2.0(@tarojs/taro@4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96)(postcss@8.5.15)))(@tarojs/helper@4.2.0)(@tarojs/shared@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96)(postcss@8.5.15)))(@types/react@18.3.31)(postcss@8.5.15)(react@18.3.1)(rollup@3.30.0)(solid-js@1.9.14)(webpack@5.97.1(@swc/core@1.3.96)(postcss@8.5.15))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/core': 7.29.7
|
'@babel/core': 7.29.7
|
||||||
@@ -9059,35 +8862,6 @@ snapshots:
|
|||||||
- webpack-chain
|
- webpack-chain
|
||||||
- webpack-dev-server
|
- webpack-dev-server
|
||||||
|
|
||||||
'@tarojs/plugin-platform-h5@4.2.0(@tarojs/taro@4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(postcss@8.5.15)))(@tarojs/helper@4.2.0)(@tarojs/shared@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(postcss@8.5.15)))(@types/react@18.3.31)(postcss@8.5.15)(react@18.3.1)(rollup@3.30.0)(solid-js@1.9.14)(webpack@5.97.1(postcss@8.5.15))':
|
|
||||||
dependencies:
|
|
||||||
'@babel/core': 7.29.7
|
|
||||||
'@tarojs/components': 4.2.0(@tarojs/helper@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(postcss@8.5.15))
|
|
||||||
'@tarojs/components-react': 4.2.0(@tarojs/helper@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(react@18.3.1)(rollup@3.30.0)(solid-js@1.9.14)(webpack@5.97.1(postcss@8.5.15))
|
|
||||||
'@tarojs/helper': 4.2.0
|
|
||||||
'@tarojs/runtime': 4.2.0
|
|
||||||
'@tarojs/service': 4.2.0
|
|
||||||
'@tarojs/shared': 4.2.0
|
|
||||||
'@tarojs/taro-h5': 4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(postcss@8.5.15)))(@tarojs/taro@4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(postcss@8.5.15)))(@tarojs/helper@4.2.0)(@tarojs/shared@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(postcss@8.5.15)))
|
|
||||||
babel-plugin-transform-taroapi: 4.2.0(@babel/core@7.29.7)
|
|
||||||
change-case: 4.1.2
|
|
||||||
lodash-es: 4.17.21
|
|
||||||
tslib: 2.8.1
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- '@swc/helpers'
|
|
||||||
- '@tarojs/taro'
|
|
||||||
- '@types/react'
|
|
||||||
- html-webpack-plugin
|
|
||||||
- postcss
|
|
||||||
- react
|
|
||||||
- rollup
|
|
||||||
- solid-js
|
|
||||||
- supports-color
|
|
||||||
- vue
|
|
||||||
- webpack
|
|
||||||
- webpack-chain
|
|
||||||
- webpack-dev-server
|
|
||||||
|
|
||||||
'@tarojs/plugin-platform-weapp@4.2.0(@tarojs/service@4.2.0)(@tarojs/shared@4.2.0)':
|
'@tarojs/plugin-platform-weapp@4.2.0(@tarojs/service@4.2.0)(@tarojs/shared@4.2.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@tarojs/service': 4.2.0
|
'@tarojs/service': 4.2.0
|
||||||
@@ -9112,18 +8886,6 @@ snapshots:
|
|||||||
tslib: 2.8.1
|
tslib: 2.8.1
|
||||||
universal-router: 9.2.1
|
universal-router: 9.2.1
|
||||||
|
|
||||||
'@tarojs/router@4.2.0(@tarojs/runtime@4.2.0)(@tarojs/shared@4.2.0)(@tarojs/taro@4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(postcss@8.5.15)))(@tarojs/helper@4.2.0)(@tarojs/shared@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(postcss@8.5.15)))':
|
|
||||||
dependencies:
|
|
||||||
'@tarojs/runtime': 4.2.0
|
|
||||||
'@tarojs/shared': 4.2.0
|
|
||||||
'@tarojs/taro': 4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(postcss@8.5.15)))(@tarojs/helper@4.2.0)(@tarojs/shared@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(postcss@8.5.15))
|
|
||||||
dingtalk-jsapi: 2.15.6
|
|
||||||
history: 5.3.0
|
|
||||||
mobile-detect: 1.4.5
|
|
||||||
query-string: 9.4.1
|
|
||||||
tslib: 2.8.1
|
|
||||||
universal-router: 9.2.1
|
|
||||||
|
|
||||||
'@tarojs/runner-utils@4.2.0':
|
'@tarojs/runner-utils@4.2.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@tarojs/helper': 4.2.0
|
'@tarojs/helper': 4.2.0
|
||||||
@@ -9175,26 +8937,6 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@tarojs/taro'
|
- '@tarojs/taro'
|
||||||
|
|
||||||
'@tarojs/taro-h5@4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(postcss@8.5.15)))(@tarojs/taro@4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(postcss@8.5.15)))(@tarojs/helper@4.2.0)(@tarojs/shared@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(postcss@8.5.15)))':
|
|
||||||
dependencies:
|
|
||||||
'@tarojs/api': 4.2.0(@tarojs/runtime@4.2.0)(@tarojs/shared@4.2.0)
|
|
||||||
'@tarojs/components': 4.2.0(@tarojs/helper@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(postcss@8.5.15))
|
|
||||||
'@tarojs/router': 4.2.0(@tarojs/runtime@4.2.0)(@tarojs/shared@4.2.0)(@tarojs/taro@4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(postcss@8.5.15)))(@tarojs/helper@4.2.0)(@tarojs/shared@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(postcss@8.5.15)))
|
|
||||||
'@tarojs/runtime': 4.2.0
|
|
||||||
'@tarojs/shared': 4.2.0
|
|
||||||
abortcontroller-polyfill: 1.7.8
|
|
||||||
base64-js: 1.5.1
|
|
||||||
ics: 3.12.0
|
|
||||||
is-mobile: 4.0.0
|
|
||||||
jsonp-retry: 1.0.3
|
|
||||||
lodash-es: 4.17.21
|
|
||||||
platform: 1.3.6
|
|
||||||
query-string: 9.4.1
|
|
||||||
tslib: 2.8.1
|
|
||||||
whatwg-fetch: 3.6.20
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- '@tarojs/taro'
|
|
||||||
|
|
||||||
'@tarojs/taro@4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96)(postcss@8.5.15)))(@tarojs/helper@4.2.0)(@tarojs/shared@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96)(postcss@8.5.15))':
|
'@tarojs/taro@4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96)(postcss@8.5.15)))(@tarojs/helper@4.2.0)(@tarojs/shared@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(@swc/core@1.3.96)(postcss@8.5.15))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@tarojs/api': 4.2.0(@tarojs/runtime@4.2.0)(@tarojs/shared@4.2.0)
|
'@tarojs/api': 4.2.0(@tarojs/runtime@4.2.0)(@tarojs/shared@4.2.0)
|
||||||
@@ -9209,20 +8951,6 @@ snapshots:
|
|||||||
rollup: 3.30.0
|
rollup: 3.30.0
|
||||||
webpack: 5.97.1(@swc/core@1.3.96)(postcss@8.5.15)
|
webpack: 5.97.1(@swc/core@1.3.96)(postcss@8.5.15)
|
||||||
|
|
||||||
'@tarojs/taro@4.2.0(@tarojs/components@4.2.0(@tarojs/helper@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(postcss@8.5.15)))(@tarojs/helper@4.2.0)(@tarojs/shared@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(postcss@8.5.15))':
|
|
||||||
dependencies:
|
|
||||||
'@tarojs/api': 4.2.0(@tarojs/runtime@4.2.0)(@tarojs/shared@4.2.0)
|
|
||||||
'@tarojs/components': 4.2.0(@tarojs/helper@4.2.0)(@types/react@18.3.31)(postcss@8.5.15)(rollup@3.30.0)(webpack@5.97.1(postcss@8.5.15))
|
|
||||||
'@tarojs/helper': 4.2.0
|
|
||||||
'@tarojs/runtime': 4.2.0
|
|
||||||
'@tarojs/shared': 4.2.0
|
|
||||||
'@types/postcss-url': 10.0.4
|
|
||||||
postcss: 8.5.15
|
|
||||||
optionalDependencies:
|
|
||||||
'@types/react': 18.3.31
|
|
||||||
rollup: 3.30.0
|
|
||||||
webpack: 5.97.1(postcss@8.5.15)
|
|
||||||
|
|
||||||
'@tarojs/vite-runner@4.2.0(@tarojs/runtime@4.2.0)(@types/babel__core@7.20.5)(postcss@8.5.15)(rollup@3.30.0)(terser@5.48.0)(typescript@5.9.3)(vite@5.4.21(@types/node@22.20.0)(sass@1.101.0)(terser@5.48.0))':
|
'@tarojs/vite-runner@4.2.0(@tarojs/runtime@4.2.0)(@types/babel__core@7.20.5)(postcss@8.5.15)(rollup@3.30.0)(terser@5.48.0)(typescript@5.9.3)(vite@5.4.21(@types/node@22.20.0)(sass@1.101.0)(terser@5.48.0))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@ampproject/remapping': 2.3.0
|
'@ampproject/remapping': 2.3.0
|
||||||
@@ -13385,17 +13113,6 @@ snapshots:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@swc/core': 1.3.96
|
'@swc/core': 1.3.96
|
||||||
|
|
||||||
terser-webpack-plugin@5.6.1(postcss@8.5.15)(webpack@5.97.1(postcss@8.5.15)):
|
|
||||||
dependencies:
|
|
||||||
'@jridgewell/trace-mapping': 0.3.31
|
|
||||||
jest-worker: 27.5.1
|
|
||||||
schema-utils: 4.3.3
|
|
||||||
terser: 5.48.0
|
|
||||||
webpack: 5.97.1(postcss@8.5.15)
|
|
||||||
optionalDependencies:
|
|
||||||
postcss: 8.5.15
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
terser@5.48.0:
|
terser@5.48.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@jridgewell/source-map': 0.3.11
|
'@jridgewell/source-map': 0.3.11
|
||||||
@@ -13832,46 +13549,6 @@ snapshots:
|
|||||||
- uglify-js
|
- uglify-js
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
webpack@5.97.1(postcss@8.5.15):
|
|
||||||
dependencies:
|
|
||||||
'@types/eslint-scope': 3.7.7
|
|
||||||
'@types/estree': 1.0.9
|
|
||||||
'@webassemblyjs/ast': 1.14.1
|
|
||||||
'@webassemblyjs/wasm-edit': 1.14.1
|
|
||||||
'@webassemblyjs/wasm-parser': 1.14.1
|
|
||||||
acorn: 8.17.0
|
|
||||||
browserslist: 4.28.4
|
|
||||||
chrome-trace-event: 1.0.4
|
|
||||||
enhanced-resolve: 5.24.1
|
|
||||||
es-module-lexer: 1.7.0
|
|
||||||
eslint-scope: 5.1.1
|
|
||||||
events: 3.3.0
|
|
||||||
glob-to-regexp: 0.4.1
|
|
||||||
graceful-fs: 4.2.11
|
|
||||||
json-parse-even-better-errors: 2.3.1
|
|
||||||
loader-runner: 4.3.2
|
|
||||||
mime-types: 2.1.35
|
|
||||||
neo-async: 2.6.2
|
|
||||||
schema-utils: 3.3.0
|
|
||||||
tapable: 2.3.3
|
|
||||||
terser-webpack-plugin: 5.6.1(postcss@8.5.15)(webpack@5.97.1(postcss@8.5.15))
|
|
||||||
watchpack: 2.5.2
|
|
||||||
webpack-sources: 3.5.0
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- '@minify-html/node'
|
|
||||||
- '@swc/core'
|
|
||||||
- '@swc/css'
|
|
||||||
- '@swc/html'
|
|
||||||
- clean-css
|
|
||||||
- cssnano
|
|
||||||
- csso
|
|
||||||
- esbuild
|
|
||||||
- html-minifier-terser
|
|
||||||
- lightningcss
|
|
||||||
- postcss
|
|
||||||
- uglify-js
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
whatwg-fetch@3.6.20: {}
|
whatwg-fetch@3.6.20: {}
|
||||||
|
|
||||||
whatwg-url@5.0.0:
|
whatwg-url@5.0.0:
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
import { config } from 'dotenv';
|
||||||
|
import { resolve } from 'path';
|
||||||
|
import { PrismaClient } from '@prisma/client';
|
||||||
|
|
||||||
|
config({ path: resolve('.env'), override: true });
|
||||||
|
|
||||||
|
function mockPayEnabled() {
|
||||||
|
return process.env.MOCK_PAY !== 'false';
|
||||||
|
}
|
||||||
|
|
||||||
|
function wechatPayEnabled() {
|
||||||
|
const e = process.env;
|
||||||
|
const appId = e.WX_MINI_APP_ID?.trim() || e.WX_APP_ID?.trim();
|
||||||
|
return (
|
||||||
|
!mockPayEnabled() &&
|
||||||
|
!!(
|
||||||
|
appId &&
|
||||||
|
e.WX_MCH_ID?.trim() &&
|
||||||
|
e.WX_MCH_SERIAL_NO?.trim() &&
|
||||||
|
e.WX_MCH_PRIVATE_KEY?.trim() &&
|
||||||
|
e.WX_API_V3_KEY?.trim()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
|
console.log('=== .env only ===');
|
||||||
|
console.log('MOCK_PAY:', process.env.MOCK_PAY);
|
||||||
|
console.log('mockPayEnabled:', mockPayEnabled());
|
||||||
|
|
||||||
|
const rows = await prisma.systemConfig.findMany({
|
||||||
|
where: { configKey: { in: ['MOCK_PAY', 'MOCK_SMS', 'MOCK_WECHAT', 'WX_MCH_ID'] } },
|
||||||
|
});
|
||||||
|
console.log('\n=== system_config (DB) ===');
|
||||||
|
for (const r of rows) {
|
||||||
|
console.log(`${r.configKey} = ${r.configKey.includes('KEY') ? '(redacted)' : r.value}`);
|
||||||
|
process.env[r.configKey] = r.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('\n=== After DB overlay (API startup) ===');
|
||||||
|
console.log('MOCK_PAY:', process.env.MOCK_PAY);
|
||||||
|
console.log('mockPayEnabled:', mockPayEnabled());
|
||||||
|
console.log('wechatPayEnabled:', wechatPayEnabled());
|
||||||
|
|
||||||
|
const orders = await prisma.order.findMany({
|
||||||
|
where: { orderType: 'PROXY' },
|
||||||
|
orderBy: { id: 'desc' },
|
||||||
|
take: 5,
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
orderNo: true,
|
||||||
|
status: true,
|
||||||
|
payStatus: true,
|
||||||
|
payExternalNo: true,
|
||||||
|
channelSource: true,
|
||||||
|
createdAt: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
console.log('\n=== Recent PROXY orders ===');
|
||||||
|
for (const o of orders) {
|
||||||
|
console.log({
|
||||||
|
id: o.id.toString(),
|
||||||
|
orderNo: o.orderNo,
|
||||||
|
status: o.status,
|
||||||
|
payStatus: o.payStatus,
|
||||||
|
payExternalNo: o.payExternalNo,
|
||||||
|
channelSource: o.channelSource,
|
||||||
|
createdAt: o.createdAt.toISOString(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await prisma.$disconnect();
|
||||||
@@ -1,30 +1,19 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { loadAppConfig } from '@dukang/shared-types';
|
import { loadAppConfig } from '@dukang/shared-types';
|
||||||
import { PrismaService } from '../../common/prisma/prisma.module';
|
|
||||||
import type { IPayProvider, PayMethod, PayOrderResult, RefundOrderResult } from './pay.interface';
|
import type { IPayProvider, PayMethod, PayOrderResult, RefundOrderResult } from './pay.interface';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class PayMockProvider implements IPayProvider {
|
export class PayMockProvider implements IPayProvider {
|
||||||
constructor(private readonly prisma: PrismaService) {}
|
|
||||||
|
|
||||||
async payOrder(
|
async payOrder(
|
||||||
orderId: bigint,
|
orderId: bigint,
|
||||||
_openId?: string,
|
_openId?: string,
|
||||||
_platform?: 'h5' | 'mini',
|
_platform?: 'h5' | 'mini',
|
||||||
payMethod: PayMethod = 'JSAPI',
|
_payMethod: PayMethod = 'JSAPI',
|
||||||
): Promise<PayOrderResult> {
|
): Promise<PayOrderResult> {
|
||||||
if (!loadAppConfig().mockPay) {
|
if (!loadAppConfig().mockPay) {
|
||||||
throw new Error('Real WeChat pay requires PayWechatProvider');
|
throw new Error('Real WeChat pay requires PayWechatProvider');
|
||||||
}
|
}
|
||||||
if (payMethod === 'NATIVE') {
|
|
||||||
const order = await this.prisma.order.findUnique({ where: { id: orderId } });
|
|
||||||
const orderNo = order?.orderNo ?? orderId.toString();
|
|
||||||
return {
|
|
||||||
mode: 'native',
|
|
||||||
codeUrl: `mock://wechat-pay/native?orderNo=${encodeURIComponent(orderNo)}`,
|
|
||||||
externalNo: `MOCK-NATIVE-${Date.now()}`,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return { mode: 'mock', externalNo: `MOCK-${Date.now()}` };
|
return { mode: 'mock', externalNo: `MOCK-${Date.now()}` };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,15 +21,6 @@ export class PayWechatProvider implements IPayProvider {
|
|||||||
payMethod: PayMethod = 'JSAPI',
|
payMethod: PayMethod = 'JSAPI',
|
||||||
): Promise<PayOrderResult> {
|
): Promise<PayOrderResult> {
|
||||||
if (loadAppConfig().mockPay) {
|
if (loadAppConfig().mockPay) {
|
||||||
if (payMethod === 'NATIVE') {
|
|
||||||
const order = await this.prisma.order.findUnique({ where: { id: orderId } });
|
|
||||||
const orderNo = order?.orderNo ?? orderId.toString();
|
|
||||||
return {
|
|
||||||
mode: 'native',
|
|
||||||
codeUrl: `mock://wechat-pay/native?orderNo=${encodeURIComponent(orderNo)}`,
|
|
||||||
externalNo: `MOCK-NATIVE-${Date.now()}`,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return { mode: 'mock', externalNo: `MOCK-${Date.now()}` };
|
return { mode: 'mock', externalNo: `MOCK-${Date.now()}` };
|
||||||
}
|
}
|
||||||
if (!this.wechat.isPayEnabled()) {
|
if (!this.wechat.isPayEnabled()) {
|
||||||
|
|||||||
@@ -25,15 +25,15 @@ export class AdminOrdersService {
|
|||||||
private readonly adminRedeemService: AdminRedeemService,
|
private readonly adminRedeemService: AdminRedeemService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/** v3.5.1 #1:发布会大屏,返回全部订单(按时间倒序,上限 2000) */
|
/** v3.5.1 #1:发布会大屏,仅已付款订单;按付款时间倒序 */
|
||||||
async listBigScreen(limit?: string) {
|
async listBigScreen(limit?: string) {
|
||||||
const take = Math.min(Math.max(Number(limit) || 2000, 1), 2000);
|
const take = Math.min(Math.max(Number(limit) || 2000, 1), 2000);
|
||||||
const orders = await this.prisma.order.findMany({
|
const orders = await this.prisma.order.findMany({
|
||||||
where: {
|
where: {
|
||||||
isTest: false,
|
|
||||||
payAmount: { gte: 100 },
|
payAmount: { gte: 100 },
|
||||||
|
payStatus: 'PAID',
|
||||||
},
|
},
|
||||||
orderBy: { createdAt: 'desc' },
|
orderBy: [{ paidAt: 'desc' }, { createdAt: 'desc' }],
|
||||||
take,
|
take,
|
||||||
include: { user: { select: { phone: true } } },
|
include: { user: { select: { phone: true } } },
|
||||||
});
|
});
|
||||||
@@ -41,12 +41,14 @@ export class AdminOrdersService {
|
|||||||
items: orders.map((o) => {
|
items: orders.map((o) => {
|
||||||
const spec = o.productSpec ? ` ${o.productSpec}` : '';
|
const spec = o.productSpec ? ` ${o.productSpec}` : '';
|
||||||
const phone = o.user?.phone || o.receiverPhone;
|
const phone = o.user?.phone || o.receiverPhone;
|
||||||
|
const paidAt = o.paidAt?.toISOString() ?? o.createdAt.toISOString();
|
||||||
return {
|
return {
|
||||||
id: o.id.toString(),
|
id: o.id.toString(),
|
||||||
orderNo: o.orderNo,
|
orderNo: o.orderNo,
|
||||||
payAmount: Number(o.payAmount),
|
payAmount: Number(o.payAmount),
|
||||||
items: `${o.productName}${spec} × ${o.quantity}瓶`,
|
items: `${o.productName}${spec} × ${o.quantity}瓶`,
|
||||||
createdAt: o.createdAt.toISOString(),
|
createdAt: paidAt,
|
||||||
|
paidAt,
|
||||||
userPhoneMasked: phone ? maskContactPhone(phone) : null,
|
userPhoneMasked: phone ? maskContactPhone(phone) : null,
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import { AuthService } from '../iam/auth.service';
|
|||||||
import { PromoCodeService } from '../promo/promo-code.service';
|
import { PromoCodeService } from '../promo/promo-code.service';
|
||||||
import { TicketService } from '../common/ticket.service';
|
import { TicketService } from '../common/ticket.service';
|
||||||
import { PAY_PROVIDER, DELIVERY_PROVIDER } from '../../integrations/integrations.constants';
|
import { PAY_PROVIDER, DELIVERY_PROVIDER } from '../../integrations/integrations.constants';
|
||||||
import { IPayProvider } from '../../integrations/pay/pay.interface';
|
import { IPayProvider, type PayOrderResult } from '../../integrations/pay/pay.interface';
|
||||||
import { IDeliveryProvider } from '../../integrations/delivery/delivery.interface';
|
import { IDeliveryProvider } from '../../integrations/delivery/delivery.interface';
|
||||||
import { IpGeoService } from '../../common/geo/ip-geo.service';
|
import { IpGeoService } from '../../common/geo/ip-geo.service';
|
||||||
import { buildOrderClientLocationSnapshot } from '../../common/geo/client-location.util';
|
import { buildOrderClientLocationSnapshot } from '../../common/geo/client-location.util';
|
||||||
@@ -2173,6 +2173,9 @@ export class TradeService {
|
|||||||
throw new BadRequestException('订单已超时未支付');
|
throw new BadRequestException('订单已超时未支付');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const mockPaid = await this.tryMockPayProxyOrder(order, 'PARTNER_PROXY_MOCK_PAY');
|
||||||
|
if (mockPaid) return mockPaid;
|
||||||
|
|
||||||
const lockedMethod = this.parseProxyPayMethod(order.channelSource);
|
const lockedMethod = this.parseProxyPayMethod(order.channelSource);
|
||||||
if (lockedMethod && lockedMethod !== payMethod) {
|
if (lockedMethod && lockedMethod !== payMethod) {
|
||||||
throw new BadRequestException(
|
throw new BadRequestException(
|
||||||
@@ -2217,33 +2220,7 @@ export class TradeService {
|
|||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (payResult.mode === 'native') {
|
return this.finishProxyPayResult(order, payResult, 'PARTNER_PROXY_MOCK_PAY');
|
||||||
return {
|
|
||||||
mode: 'native' as const,
|
|
||||||
orderId: order.id.toString(),
|
|
||||||
orderNo: order.orderNo,
|
|
||||||
codeUrl: payResult.codeUrl,
|
|
||||||
payExpireAt: order.payExpireAt?.toISOString() ?? null,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (payResult.mode === 'jsapi') {
|
|
||||||
return {
|
|
||||||
mode: 'jsapi' as const,
|
|
||||||
orderId: order.id.toString(),
|
|
||||||
orderNo: order.orderNo,
|
|
||||||
prepay: payResult.prepay,
|
|
||||||
payExpireAt: order.payExpireAt?.toISOString() ?? null,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.markProxyOrderPaid(order.id, payResult.externalNo, 'PARTNER_PROXY_MOCK_PAY');
|
|
||||||
return {
|
|
||||||
mode: 'mock' as const,
|
|
||||||
orderId: order.id.toString(),
|
|
||||||
orderNo: order.orderNo,
|
|
||||||
payExpireAt: order.payExpireAt?.toISOString() ?? null,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 总部代下单支付:仅 Native 收款码 */
|
/** 总部代下单支付:仅 Native 收款码 */
|
||||||
@@ -2262,6 +2239,9 @@ export class TradeService {
|
|||||||
throw new BadRequestException('订单已超时未支付');
|
throw new BadRequestException('订单已超时未支付');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const mockPaid = await this.tryMockPayProxyOrder(order, 'HQ_PROXY_MOCK_PAY');
|
||||||
|
if (mockPaid) return mockPaid;
|
||||||
|
|
||||||
this.payRedeemAnomaly.onPayAttempt(Number(order.payAmount), {
|
this.payRedeemAnomaly.onPayAttempt(Number(order.payAmount), {
|
||||||
orderNo: order.orderNo,
|
orderNo: order.orderNo,
|
||||||
userId: order.userId,
|
userId: order.userId,
|
||||||
@@ -2277,20 +2257,15 @@ export class TradeService {
|
|||||||
});
|
});
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
if (payResult.mode !== 'native') {
|
const result = await this.finishProxyPayResult(order, payResult, 'HQ_PROXY_MOCK_PAY');
|
||||||
|
if (result.mode !== 'native' && result.mode !== 'mock') {
|
||||||
this.payRedeemAnomaly.onPayFail('无法生成收款码', {
|
this.payRedeemAnomaly.onPayFail('无法生成收款码', {
|
||||||
orderNo: order.orderNo,
|
orderNo: order.orderNo,
|
||||||
userId: order.userId,
|
userId: order.userId,
|
||||||
});
|
});
|
||||||
throw new BadRequestException('无法生成收款码');
|
throw new BadRequestException('无法生成收款码');
|
||||||
}
|
}
|
||||||
return {
|
return result;
|
||||||
mode: 'native' as const,
|
|
||||||
orderId: order.id.toString(),
|
|
||||||
orderNo: order.orderNo,
|
|
||||||
codeUrl: payResult.codeUrl,
|
|
||||||
payExpireAt: order.payExpireAt?.toISOString() ?? null,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Mock 环境确认代下单支付 */
|
/** Mock 环境确认代下单支付 */
|
||||||
@@ -2375,6 +2350,64 @@ export class TradeService {
|
|||||||
return this.fulfillmentService.getOrderTrack(orderId);
|
return this.fulfillmentService.getOrderTrack(orderId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async tryMockPayProxyOrder(
|
||||||
|
order: { id: bigint; orderNo: string; payExpireAt: Date | null },
|
||||||
|
operator: string,
|
||||||
|
) {
|
||||||
|
if (!loadAppConfig().mockPay) return null;
|
||||||
|
await this.markProxyOrderPaid(order.id, `MOCK-${Date.now()}`, operator);
|
||||||
|
return {
|
||||||
|
mode: 'mock' as const,
|
||||||
|
orderId: order.id.toString(),
|
||||||
|
orderNo: order.orderNo,
|
||||||
|
payExpireAt: order.payExpireAt?.toISOString() ?? null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 代下单支付结果:Mock 环境直接落单,真实环境返回收款码/JSAPI */
|
||||||
|
private async finishProxyPayResult(
|
||||||
|
order: { id: bigint; orderNo: string; payExpireAt: Date | null },
|
||||||
|
payResult: PayOrderResult,
|
||||||
|
operator: string,
|
||||||
|
) {
|
||||||
|
const mockPay = loadAppConfig().mockPay;
|
||||||
|
const isMockNative =
|
||||||
|
payResult.mode === 'native' &&
|
||||||
|
(mockPay || payResult.codeUrl.startsWith('mock://'));
|
||||||
|
|
||||||
|
if (payResult.mode === 'mock' || isMockNative) {
|
||||||
|
await this.markProxyOrderPaid(order.id, payResult.externalNo, operator);
|
||||||
|
return {
|
||||||
|
mode: 'mock' as const,
|
||||||
|
orderId: order.id.toString(),
|
||||||
|
orderNo: order.orderNo,
|
||||||
|
payExpireAt: order.payExpireAt?.toISOString() ?? null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (payResult.mode === 'native') {
|
||||||
|
return {
|
||||||
|
mode: 'native' as const,
|
||||||
|
orderId: order.id.toString(),
|
||||||
|
orderNo: order.orderNo,
|
||||||
|
codeUrl: payResult.codeUrl,
|
||||||
|
payExpireAt: order.payExpireAt?.toISOString() ?? null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (payResult.mode === 'jsapi') {
|
||||||
|
return {
|
||||||
|
mode: 'jsapi' as const,
|
||||||
|
orderId: order.id.toString(),
|
||||||
|
orderNo: order.orderNo,
|
||||||
|
prepay: payResult.prepay,
|
||||||
|
payExpireAt: order.payExpireAt?.toISOString() ?? null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new BadRequestException('支付发起失败');
|
||||||
|
}
|
||||||
|
|
||||||
private async markProxyOrderPaid(orderId: bigint, externalNo: string, operator: string) {
|
private async markProxyOrderPaid(orderId: bigint, externalNo: string, operator: string) {
|
||||||
const order = await this.prisma.order.findUnique({ where: { id: orderId } });
|
const order = await this.prisma.order.findUnique({ where: { id: orderId } });
|
||||||
if (!order) throw new NotFoundException('订单不存在');
|
if (!order) throw new NotFoundException('订单不存在');
|
||||||
|
|||||||
Reference in New Issue
Block a user