diff --git a/apps/frontend/src/pages/admin/platform/PddPlatformPanel.tsx b/apps/frontend/src/pages/admin/platform/PddPlatformPanel.tsx index 61b4aea6..c544d26f 100644 --- a/apps/frontend/src/pages/admin/platform/PddPlatformPanel.tsx +++ b/apps/frontend/src/pages/admin/platform/PddPlatformPanel.tsx @@ -4,30 +4,40 @@ */ import { + ApiOutlined, CheckCircleOutlined, CloseCircleOutlined, - DeleteOutlined, EditOutlined, + FieldTimeOutlined, + GlobalOutlined, PlusOutlined, ReloadOutlined, + SafetyCertificateOutlined, SaveOutlined, + ShopOutlined, ThunderboltOutlined, } from '@ant-design/icons' import { Alert, + Badge, Button, Card, + Col, + Divider, Form, Input, InputNumber, Modal, Popconfirm, + Row, Space, Spin, + Statistic, Switch, Table, Tag, + Tooltip, Typography, } from 'antd' import type { TableColumnsType } from 'antd' @@ -52,6 +62,14 @@ import type { const { Text, Paragraph } = Typography +/** + * Extended test result with execution timestamp + * 带时间戳的 CK 校验结果结构 + */ +interface ExtendedTestResult extends AdminPddTestCookieResult { + testedAt?: string +} + export function PddPlatformPanel() { const [loading, setLoading] = useState(true) const [saving, setSaving] = useState(false) @@ -61,15 +79,16 @@ export function PddPlatformPanel() { stores: [], }) - // Action loading states + // Action loading states / 各操作加载状态 const [syncingAll, setSyncingAll] = useState(false) const [testingShopId, setTestingShopId] = useState(null) const [syncingShopId, setSyncingShopId] = useState(null) + const [togglingShopId, setTogglingShopId] = useState(null) - // Test results cache by shopId - const [testResults, setTestResults] = useState>({}) + // Test results cache by shopId / 店铺 CK 校验结果缓存 + const [testResults, setTestResults] = useState>({}) - // Modal State + // Modal State / 弹窗表单状态 const [modalVisible, setModalVisible] = useState(false) const [isEditing, setIsEditing] = useState(false) const [form] = Form.useForm() @@ -80,6 +99,10 @@ export function PddPlatformPanel() { void loadConfig() }, []) + /** + * Load PDD platform config + * 读取拼多多子服务配置 + */ async function loadConfig() { setLoading(true) try { @@ -94,14 +117,19 @@ export function PddPlatformPanel() { } } - async function handleSaveGlobalConfig() { + /** + * Save global configuration + * 保存全局同步调度配置 + */ + async function handleSaveGlobalConfig(overrideConfig?: AdminPddConfig) { setSaving(true) + const targetConfig = overrideConfig || config try { - const response = await saveAdminPddConfig(config) + const response = await saveAdminPddConfig(targetConfig) if (response && response.data) { setConfig(response.data) } - showSuccess('拼多多全局配置已保存') + showSuccess('拼多多全局调度配置已保存') } catch (err) { showError(err instanceof Error ? err.message : '保存拼多多全局配置失败') } finally { @@ -109,6 +137,10 @@ export function PddPlatformPanel() { } } + /** + * Trigger order synchronization for all enabled stores + * 触发所有已启用店铺的手动拉单 + */ async function handleSyncAll() { setSyncingAll(true) try { @@ -118,10 +150,10 @@ export function PddPlatformPanel() { const totalFetched = data.results.reduce((acc, r) => acc + (r.fetchedCount || 0), 0) const totalUpserted = data.results.reduce((acc, r) => acc + (r.upsertedCount || 0), 0) showSuccess( - `全部店铺同步完成:共拉取 ${totalFetched} 笔订单,成功入库/更新 ${totalUpserted} 笔`, + `全量店铺同步完成:共抓取 ${totalFetched} 笔,入库/更新 ${totalUpserted} 笔待发货订单`, ) } else { - showSuccess('同步完成') + showSuccess('全量店铺同步完成') } } catch (err) { showError(err instanceof Error ? err.message : '触发全店铺同步失败') @@ -130,6 +162,10 @@ export function PddPlatformPanel() { } } + /** + * Trigger order synchronization for a single store + * 单店铺手动触发拉单 + */ async function handleSyncSingleStore(store: AdminPddStoreItem) { setSyncingShopId(store.shopId) try { @@ -137,7 +173,7 @@ export function PddPlatformPanel() { const data = response?.data as AdminPddSyncResult | undefined if (data) { showSuccess( - `店铺【${store.shopName}】同步完成:拉取 ${data.fetchedCount} 笔,入库/更新 ${data.upsertedCount} 笔`, + `店铺【${store.shopName}】同步完成:抓取 ${data.fetchedCount} 笔,入库/更新 ${data.upsertedCount} 笔`, ) } else { showSuccess(`店铺【${store.shopName}】同步已触发`) @@ -149,8 +185,13 @@ export function PddPlatformPanel() { } } + /** + * Live test MMS Cookie validity for a store + * 针对指定店铺实时测试 Cookie 有效性 + */ async function handleTestCookie(store: AdminPddStoreItem) { setTestingShopId(store.shopId) + const nowStr = new Date().toLocaleTimeString('zh-CN', { hour12: false }) try { const response = await testAdminPddCookie({ shopId: store.shopId, @@ -159,10 +200,13 @@ export function PddPlatformPanel() { }) const result = response?.data if (result) { - setTestResults((prev) => ({ ...prev, [store.shopId]: result })) + setTestResults((prev) => ({ + ...prev, + [store.shopId]: { ...result, testedAt: nowStr }, + })) if (result.valid) { showSuccess( - `【${store.shopName}】Cookie 校验通过,店铺在线订单总数: ${result.totalOrders ?? 0}`, + `【${store.shopName}】Cookie 校验通过,在线待发货订单数: ${result.totalOrders ?? 0}`, ) } else { showError(`【${store.shopName}】Cookie 校验未通过: ${result.message}`) @@ -175,18 +219,44 @@ export function PddPlatformPanel() { } } + /** + * Delete store configuration + * 删除店铺配置 + */ async function handleDeleteStore(shopId: string) { try { const response = await deleteAdminPddStore(shopId) if (response && response.data) { setConfig(response.data) } - showSuccess('拼多多店铺已删除') + showSuccess('拼多多店铺已安全移除') } catch (err) { showError(err instanceof Error ? err.message : '删除拼多多店铺失败') } } + /** + * Toggle store enabled state + * 快捷切换店铺启用状态 + */ + async function handleToggleStoreEnabled(record: AdminPddStoreItem, checked: boolean) { + setTogglingShopId(record.shopId) + try { + const updated = { ...record, enabled: checked } + await saveAdminPddStore(record.shopId, updated) + await loadConfig() + showSuccess(`已${checked ? '启用' : '停用'}店铺【${record.shopName}】`) + } catch (err) { + showError(err instanceof Error ? err.message : '切换店铺状态失败') + } finally { + setTogglingShopId(null) + } + } + + /** + * Open modal for adding a new store + * 打开添加店铺弹窗 + */ function handleOpenAddModal() { setIsEditing(false) setModalTestResult(null) @@ -202,6 +272,10 @@ export function PddPlatformPanel() { setModalVisible(true) } + /** + * Open modal for editing existing store + * 打开编辑店铺弹窗 + */ function handleOpenEditModal(store: AdminPddStoreItem) { setIsEditing(true) setModalTestResult(null) @@ -217,11 +291,15 @@ export function PddPlatformPanel() { setModalVisible(true) } + /** + * Test Cookie inside Modal before saving + * 弹窗内免保存即时验证 Cookie + */ async function handleModalTestCookie() { const values = form.getFieldsValue() const cookie = String(values.cookie || '').trim() if (!cookie) { - showError('请先在下方输入或粘贴待测试的 Cookie 字符串') + showError('请先在输入框中粘贴待测试的 Cookie 字符串') return } @@ -251,6 +329,10 @@ export function PddPlatformPanel() { } } + /** + * Submit store Add/Edit form + * 提交店铺保存表单 + */ async function handleModalSubmit() { try { const values = await form.validateFields() @@ -267,108 +349,174 @@ export function PddPlatformPanel() { } } + // Calculate statistics / 计算统计数据 + const totalStores = config.stores.length + const enabledStores = config.stores.filter((s) => s.enabled).length + const validStores = Object.values(testResults).filter((r) => r.valid).length + + // Table columns definition / 表格列配置 const columns: TableColumnsType = [ { - title: '店铺 ID', + title: '店铺标识 (ID)', dataIndex: 'shopId', key: 'shopId', - width: 140, - render: (val: string) => {val}, + width: 150, + render: (val: string) => ( + + { + void navigator.clipboard.writeText(val) + showSuccess(`已复制: ${val}`) + }} + > + {val} + + + ), }, { title: '店铺名称', dataIndex: 'shopName', key: 'shopName', - width: 160, - render: (val: string) => {val}, + width: 180, + render: (val: string, record) => ( + + + + + {val} + + + {record.proxy ? ( + + 独立代理 + + ) : null} + + ), }, { - title: 'Cookie 凭据', + title: '商家凭证 (Cookie)', dataIndex: 'cookie', key: 'cookie', - ellipsis: true, + width: 220, render: (cookie: string) => { if (!cookie) return 未配置 const preview = - cookie.length > 30 ? `${cookie.slice(0, 15)}...${cookie.slice(-15)}` : cookie + cookie.length > 26 ? `${cookie.slice(0, 12)}...${cookie.slice(-10)}` : cookie return ( - - + + {preview} - {cookie.length} 字符 + + {cookie.length} 字符 + ) }, }, { - title: '状态', + title: '自动拉单', dataIndex: 'enabled', key: 'enabled', - width: 100, + width: 110, render: (enabled: boolean, record) => ( - { - const updated = { ...record, enabled: checked } - await saveAdminPddStore(record.shopId, updated) - await loadConfig() - showSuccess(`已${checked ? '启用' : '停用'}店铺【${record.shopName}】`) - }} - /> + + handleToggleStoreEnabled(record, checked)} + /> + + {enabled ? '调度中' : '已暂停'} + + } + /> + ), }, { - title: 'CK 校验结果', + title: 'Cookie 校验状态', key: 'testStatus', - width: 180, + width: 190, render: (_, record) => { const testRes = testResults[record.shopId] if (!testRes) { - return 未校验 + return 未校验} /> } if (testRes.valid) { return ( - } color="success"> - 有效 (订单数: {testRes.totalOrders ?? '-'}) - + + } color="success"> + 有效 (待发货: {testRes.totalOrders ?? 0}) + + {testRes.testedAt && ( + + {testRes.testedAt} 测活 + + )} + ) } return ( - } color="error" title={testRes.message}> - 失效 - + + + } color="error" style={{ cursor: 'pointer' }}> + 失效 (点击查看) + + + {testRes.testedAt && ( + + {testRes.testedAt} 测活 + + )} + ) }, }, { title: '操作', key: 'action', - width: 280, + width: 300, + fixed: 'right', render: (_, record) => ( - - - + + + + + + + handleDeleteStore(record.shopId)} okText="删除" cancelText="取消" @@ -386,64 +534,236 @@ export function PddPlatformPanel() { if (loading) { return ( -
- +
+
) } return ( - - {/* Global Sync Settings */} +
+ {/* Metrics Dashboard / 状态指标概览卡片 */} + + + + 服务运行状态} + value={config.enabled ? '运行中' : '已暂停'} + valueStyle={{ + color: config.enabled ? '#52c41a' : '#8c8c8c', + fontWeight: 600, + fontSize: 20, + }} + prefix={config.enabled ? : } + /> +
+ + 后台错峰自动拉单 + + { + const next = { ...config, enabled: checked } + setConfig(next) + void handleSaveGlobalConfig(next) + }} + /> +
+
+ + + + + 接入店铺总数} + value={totalStores} + suffix={家} + valueStyle={{ color: '#1677ff', fontWeight: 600, fontSize: 20 }} + prefix={} + /> +
+ + 已启用 {enabledStores} 家 / 待机{' '} + {totalStores - enabledStores} 家 + + {validStores > 0 && ( + + CK 有效 {validStores} + + )} +
+
+ + + + + 错峰轮询频次} + value={config.pollIntervalSeconds} + suffix={秒 / 轮} + valueStyle={{ color: '#722ed1', fontWeight: 600, fontSize: 20 }} + prefix={} + /> +
+ + 多店铺平滑均摊时间片 + +
+
+ + + + + 算法驱动核心} + value="0as Pure TS" + valueStyle={{ color: '#13c2c2', fontWeight: 600, fontSize: 20 }} + prefix={} + /> +
+ + 零浏览器依赖 · 毫秒级抗风控 + +
+
+ +
+ + {/* Global Sync Settings Card / 全局同步调度配置卡片 */} + + + 拼多多子服务同步调度配置 + + + } extra={ } > - - - 启用拼多多同步: - setConfig((prev) => ({ ...prev, enabled: checked }))} - /> - + + + + 自动拉单调度状态 + + setConfig((prev) => ({ ...prev, enabled: checked }))} + /> + + {config.enabled ? '已开启自动同步' : '同步已暂停'} + + + + - - 错峰轮询间隔 (秒): - - setConfig((prev) => ({ ...prev, pollIntervalSeconds: Number(val || 20) })) - } - /> - (建议 15~30 秒,多店铺将自动错峰拉取待发货订单) - - + + + 错峰轮询间隔 + + + setConfig((prev) => ({ + ...prev, + pollIntervalSeconds: Number(val || 20), + })) + } + /> + + (建议 15~30 秒。多店铺自动平滑错峰拉取待发货订单,保证准实时入库且防止高频风控) + + + + + - {/* Stores CRUD Table */} + {/* Stores CRUD Table Card / 拼多多店铺管理表格卡片 */} - 拼多多店铺列表 - {config.stores.length} 个店铺 + + + 拼多多店铺配置与凭据 + + {totalStores} 家店铺 } extra={ @@ -457,102 +777,195 @@ export function PddPlatformPanel() { columns={columns} dataSource={config.stores} pagination={false} - locale={{ emptyText: '暂未添加拼多多店铺,请点击右上角“添加店铺”' }} + scroll={{ x: 1100 }} + locale={{ + emptyText: ( +
+ 暂未添加拼多多店铺,请点击右上角“添加店铺”按钮接入店铺 +
+ ), + }} />
- {/* Add / Edit Store Modal */} + {/* Add / Edit Store Modal / 添加与编辑店铺弹窗 */} + + {isEditing ? '编辑拼多多店铺' : '添加拼多多店铺'} + + } open={modalVisible} onCancel={() => setModalVisible(false)} onOk={handleModalSubmit} - okText="保存" + okText="保存店铺" cancelText="取消" - width={680} + width={720} destroyOnClose >
- - - + 1. 店铺基本信息 (Basic Information) + - + + + + + + + + + + + + + + - - + 2. 商家后台凭证与即时测活 (MMS Credentials & Testing) + - 登录 MMS 商家后台(mms.pinduoduo.com),按 F12 复制包含 _nano_fp、 - PASS_ID、api_uid 的完整 Cookie。 + + 登录 MMS 商家后台(mms.pinduoduo.com),按 F12 复制包含{' '} + _nano_fp、PASS_ID、api_uid{' '} + 的完整 Cookie。 } > - + - {/* Test Cookie Action inside Modal */} -
- - - 无需先保存,可即时验证此 Cookie 是否能连通 MMS API - + {/* Dedicated Live Verification Area / 实时 Cookie 测活区域 */} + +
+ + + + 无需先保存,可即时检测是否能通过 0as 纯算防风控并连通 MMS 官方 API + + +
{modalTestResult && ( -
- -
+ +
{modalTestResult.message}
+ {modalTestResult.totalOrders !== undefined && ( +
+ 当前在线待发货订单数: {modalTestResult.totalOrders} 笔 +
+ )} +
+ } + /> )} -
+
- - - + + 3. 网络隔离与防关联 (Network & Isolation - 选填) + + + + + + + + + + + + + + + + + + 4. 运行调度 (Schedule) + - - - - -
+ ) }