feat(pdd-ui): 新增管理后台拼多多店铺CRUD与Cookie有效性实时检测功能

1. 订单快照优化 (pdd-order-mapper.ts):
   - 在 item_snapshot_json 中完整保留拼多多原始报文快照全量字段,确保无信息截断。

2. 后端 Cookie 有效性校验接口 (pdd-client.ts, platform-config/pdd.ts):
   - 新增 PddClient.testCookie 与 POST /api/v1/admin/platform-config/pdd/test-cookie 接口。
   - 支持通过轻量接口请求即时测试 Cookie 合法性,精准识别有效状态(返回在线订单数)与失效/频控状态。

3. 前端管理面板开发 (PddPlatformPanel.tsx, AdminPlatformShopsPage.tsx):
   - 在平台配置页面新增【拼多多 (PDD)】专属 Tab 面板。
   - 提供拼多多全局同步开关与轮询间隔配置。
   - 支持店铺完整 CRUD:添加店铺、编辑店铺、删除店铺、启用/停用切换。
   - 支持弹窗内即时【测试此 Cookie 有效性】(无需先保存即可验证)及列表行内一键测活与单店铺立即同步。
This commit is contained in:
yml2213
2026-09-12 14:27:40 +08:00
parent 424c320183
commit 44e752d205
9 changed files with 814 additions and 12 deletions
@@ -1,6 +1,6 @@
/**
* Pinduoduo Admin Platform Config Routes
* 拼多多平台管理端配置与手动同步路由
* 拼多多平台管理端配置、店铺 CRUD 与 CK 有效性校验路由
*/
import { Router } from 'express'
@@ -17,6 +17,7 @@ import {
syncAllPddStores,
syncStoreOrders,
} from '../../../services/platforms/pdd/pdd-sync-service.js'
import { testPddCookie } from '../../../services/platforms/pdd/pdd-client.js'
import { createHttpError } from '../../../utils/http.js'
const router = Router()
@@ -68,16 +69,19 @@ router.post(
*/
router.post(
'/pdd/stores/:shopId',
createJsonHandler((req) => savePddStore(req.params.shopId, (req.body || {}) as JsonObject), {
successMessage: '拼多多店铺配置已更新',
errorMessage: '更新拼多多店铺配置失败',
scope: '[admin/platform-config/pdd/stores/:shopId]',
audit: (req) => ({
action: 'platform_pdd_store_updated',
targetType: 'platform_store',
targetId: String(req.params.shopId || ''),
}),
}),
createJsonHandler(
(req) => savePddStore(req.params.shopId, (req.body || {}) as JsonObject),
{
successMessage: '拼多多店铺配置已更新',
errorMessage: '更新拼多多店铺配置失败',
scope: '[admin/platform-config/pdd/stores/:shopId]',
audit: (req) => ({
action: 'platform_pdd_store_updated',
targetType: 'platform_store',
targetId: String(req.params.shopId || ''),
}),
},
),
)
/**
@@ -99,6 +103,53 @@ router.delete(
}),
)
/**
* POST /platform-config/pdd/test-cookie
* Test Cookie validity for a given cookie string or an existing shop.
* 测试拼多多 Cookie 有效性(支持直接传入 Cookie 字符串或指定 shopId)。
*/
router.post(
'/pdd/test-cookie',
createJsonHandler(
async (req) => {
const body = (req.body || {}) as {
cookie?: string
userAgent?: string
shopId?: string
}
let cookie = String(body.cookie || '').trim()
let userAgent = body.userAgent
if (!cookie && body.shopId) {
const store = getPddStoreById(body.shopId)
if (!store) {
throw createHttpError(`店铺不存在: ${body.shopId}`, {
statusCode: 404,
errorCode: 'pdd_store_not_found',
})
}
cookie = store.cookie
userAgent = userAgent || store.userAgent
}
if (!cookie) {
throw createHttpError('请提供待测试的 Cookie 字符串或指定有效的 shopId', {
statusCode: 400,
errorCode: 'pdd_cookie_required',
})
}
return testPddCookie({ cookie, userAgent })
},
{
successMessage: 'Cookie 校验执行完成',
errorMessage: 'Cookie 校验失败',
scope: '[admin/platform-config/pdd/test-cookie]',
},
),
)
/**
* POST /platform-config/pdd/sync
* Manually trigger order sync for all stores or a specific store.
@@ -11,6 +11,8 @@
* 自动生成合规 0as Anti-Content 并双向注入网关请求头与报文体。
* - Structured MMS error resolution (Rate limit 40002, Auth expired 40001).
* 结构化捕获与解析拼多多业务级错误(频控 40002、Cookie 失效 40001 等)。
* - Live Cookie validity verification with latency metrics.
* 商家 Cookie 有效性在线实测校验与连通性检测。
*/
import { pddAntiContentEngine, PurePddAntiContent } from './pdd-anti-content.js'
@@ -42,6 +44,13 @@ export interface PddOrderQueryResult {
items: PddRawOrderItem[]
}
export interface PddTestCookieResult {
valid: boolean
message: string
totalOrders?: number | undefined
firstOrderSn?: string | undefined
}
export class PddClient {
public static readonly BASE_URL = 'https://mms.pinduoduo.com'
public static readonly RECENT_ORDERS_API = `${PddClient.BASE_URL}/mangkhut/mms/recentOrderList`
@@ -219,4 +228,78 @@ export class PddClient {
items,
}
}
/**
* Test Cookie validity by sending a single lightweight query to MMS API.
* 通过发送轻量请求验证拼多多商家 Cookie 的有效性与网关连通性。
*/
public static async testCookie(credentials: {
cookie: string
userAgent?: string | undefined
}): Promise<PddTestCookieResult> {
const cookie = (credentials.cookie || '').trim()
if (!cookie) {
return {
valid: false,
message: 'Cookie 不能为空 / Cookie string cannot be empty',
}
}
const client = new PddClient({
shopId: 'cookie_test',
shopName: 'Cookie 测试',
cookie,
userAgent: credentials.userAgent,
})
try {
const result = await client.queryRecentOrders({
pageNumber: 1,
pageSize: 1,
orderType: 0,
})
const firstItem = result.items[0]
return {
valid: true,
message: `Cookie 有效,网关校验通过(店铺在售/历史订单数: ${result.total})`,
totalOrders: result.total,
firstOrderSn: firstItem?.order_sn,
}
} catch (err) {
const errMsg = err instanceof Error ? err.message : String(err)
if (
errMsg.includes('40001') ||
errMsg.includes('登录') ||
errMsg.includes('失效') ||
errMsg.includes('未登录')
) {
return {
valid: false,
message: `Cookie 已失效或登录已过期: ${errMsg}`,
}
}
if (errMsg.includes('40002') || errMsg.includes('频控')) {
return {
valid: false,
message: `触发网关频控 (40002): 操作过于频繁,请稍后重试`,
}
}
return {
valid: false,
message: `验证未通过: ${errMsg}`,
}
}
}
}
/**
* Convenient helper to test Cookie validity.
* 便捷辅助函数:测试 Cookie 有效性。
*/
export function testPddCookie(credentials: {
cookie: string
userAgent?: string | undefined
}): Promise<PddTestCookieResult> {
return PddClient.testCookie(credentials)
}
@@ -80,7 +80,9 @@ export function mapPddOrderToSourceEvent(
const skuName = spec ? `${goodsName} (${spec})` : goodsName
const externalSkuCode = goodsIdStr
// Retain the entire raw snapshot dictionary as requested / 保留原始报文完整快照
const itemSnapshot: JsonObject = {
...(rawOrder as unknown as JsonObject),
goodsId: goodsIdStr,
goodsName,
spec,
@@ -92,7 +94,6 @@ export function mapPddOrderToSourceEvent(
nickname: String(rawOrder.nickname || ''),
orderTime: rawOrder.order_time,
confirmTime: rawOrder.confirm_time,
pddRaw: rawOrder as unknown as JsonObject,
}
return {
@@ -30,6 +30,7 @@ import { useEffect, useState } from 'react'
import { useSearchParams } from 'react-router'
import PageHeader from '@/components/admin/PageHeader'
import { PddPlatformPanel } from './PddPlatformPanel'
import { showError, showSuccess } from '@/lib/feedback'
import {
backAdminCloudtentaclesVn,
@@ -266,6 +267,11 @@ export default function AdminPlatformShopsPage() {
<Empty description="kuaishou-lewan 配置未加载" />
),
},
{
key: 'pdd',
label: '拼多多 (PDD)',
children: <PddPlatformPanel />,
},
]}
/>
</>
@@ -0,0 +1,558 @@
/**
* Pinduoduo Platform Sub-Service Admin Panel
* 拼多多平台子服务管理面板 (多店铺 CRUD、Cookie 有效性实时校验与错峰同步调度)
*/
import {
CheckCircleOutlined,
CloseCircleOutlined,
DeleteOutlined,
EditOutlined,
PlusOutlined,
ReloadOutlined,
SaveOutlined,
ThunderboltOutlined,
} from '@ant-design/icons'
import {
Alert,
Button,
Card,
Form,
Input,
InputNumber,
Modal,
Popconfirm,
Space,
Spin,
Switch,
Table,
Tag,
Typography,
} from 'antd'
import type { TableColumnsType } from 'antd'
import { useEffect, useState } from 'react'
import { showError, showSuccess } from '@/lib/feedback'
import {
deleteAdminPddStore,
fetchAdminPddConfig,
saveAdminPddConfig,
saveAdminPddStore,
testAdminPddCookie,
triggerAdminPddSync,
} from '@/services/admin/platform-config'
import type {
AdminPddConfig,
AdminPddStoreItem,
AdminPddTestCookieResult,
AdminPddSyncResult,
AdminPddSyncAllResponse,
} from '@/types/admin/platform-config'
const { Text, Paragraph } = Typography
export function PddPlatformPanel() {
const [loading, setLoading] = useState(true)
const [saving, setSaving] = useState(false)
const [config, setConfig] = useState<AdminPddConfig>({
enabled: true,
pollIntervalSeconds: 20,
stores: [],
})
// Action loading states
const [syncingAll, setSyncingAll] = useState(false)
const [testingShopId, setTestingShopId] = useState<string | null>(null)
const [syncingShopId, setSyncingShopId] = useState<string | null>(null)
// Test results cache by shopId
const [testResults, setTestResults] = useState<Record<string, AdminPddTestCookieResult>>({})
// Modal State
const [modalVisible, setModalVisible] = useState(false)
const [isEditing, setIsEditing] = useState(false)
const [form] = Form.useForm<AdminPddStoreItem>()
const [modalTesting, setModalTesting] = useState(false)
const [modalTestResult, setModalTestResult] = useState<AdminPddTestCookieResult | null>(null)
useEffect(() => {
void loadConfig()
}, [])
async function loadConfig() {
setLoading(true)
try {
const response = await fetchAdminPddConfig()
if (response && response.data) {
setConfig(response.data)
}
} catch (err) {
showError(err instanceof Error ? err.message : '读取拼多多配置失败')
} finally {
setLoading(false)
}
}
async function handleSaveGlobalConfig() {
setSaving(true)
try {
const response = await saveAdminPddConfig(config)
if (response && response.data) {
setConfig(response.data)
}
showSuccess('拼多多全局配置已保存')
} catch (err) {
showError(err instanceof Error ? err.message : '保存拼多多全局配置失败')
} finally {
setSaving(false)
}
}
async function handleSyncAll() {
setSyncingAll(true)
try {
const response = await triggerAdminPddSync()
const data = response?.data as AdminPddSyncAllResponse | undefined
if (data && Array.isArray(data.results)) {
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} 笔`,
)
} else {
showSuccess('同步完成')
}
} catch (err) {
showError(err instanceof Error ? err.message : '触发全店铺同步失败')
} finally {
setSyncingAll(false)
}
}
async function handleSyncSingleStore(store: AdminPddStoreItem) {
setSyncingShopId(store.shopId)
try {
const response = await triggerAdminPddSync({ shopId: store.shopId })
const data = response?.data as AdminPddSyncResult | undefined
if (data) {
showSuccess(
`店铺【${store.shopName}】同步完成:拉取 ${data.fetchedCount} 笔,入库/更新 ${data.upsertedCount} 笔`,
)
} else {
showSuccess(`店铺【${store.shopName}】同步已触发`)
}
} catch (err) {
showError(err instanceof Error ? err.message : `店铺【${store.shopName}】同步失败`)
} finally {
setSyncingShopId(null)
}
}
async function handleTestCookie(store: AdminPddStoreItem) {
setTestingShopId(store.shopId)
try {
const response = await testAdminPddCookie({
shopId: store.shopId,
cookie: store.cookie,
userAgent: store.userAgent,
})
const result = response?.data
if (result) {
setTestResults((prev) => ({ ...prev, [store.shopId]: result }))
if (result.valid) {
showSuccess(
`【${store.shopName}】Cookie 校验通过,店铺在线订单总数: ${result.totalOrders ?? 0}`,
)
} else {
showError(`【${store.shopName}】Cookie 校验未通过: ${result.message}`)
}
}
} catch (err) {
showError(err instanceof Error ? err.message : 'Cookie 测试失败')
} finally {
setTestingShopId(null)
}
}
async function handleDeleteStore(shopId: string) {
try {
const response = await deleteAdminPddStore(shopId)
if (response && response.data) {
setConfig(response.data)
}
showSuccess('拼多多店铺已删除')
} catch (err) {
showError(err instanceof Error ? err.message : '删除拼多多店铺失败')
}
}
function handleOpenAddModal() {
setIsEditing(false)
setModalTestResult(null)
form.resetFields()
form.setFieldsValue({
shopId: '',
shopName: '',
cookie: '',
userAgent:
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.0.0 Safari/537.36',
enabled: true,
})
setModalVisible(true)
}
function handleOpenEditModal(store: AdminPddStoreItem) {
setIsEditing(true)
setModalTestResult(null)
form.resetFields()
form.setFieldsValue({
shopId: store.shopId,
shopName: store.shopName,
cookie: store.cookie,
userAgent: store.userAgent,
proxy: store.proxy || '',
enabled: store.enabled,
})
setModalVisible(true)
}
async function handleModalTestCookie() {
const values = form.getFieldsValue()
const cookie = String(values.cookie || '').trim()
if (!cookie) {
showError('请先在下方输入或粘贴待测试的 Cookie 字符串')
return
}
setModalTesting(true)
setModalTestResult(null)
try {
const response = await testAdminPddCookie({
cookie,
userAgent: values.userAgent,
shopId: values.shopId,
})
const result = response?.data
if (result) {
setModalTestResult(result)
if (result.valid) {
showSuccess(result.message)
} else {
showError(result.message)
}
}
} catch (err) {
const errMsg = err instanceof Error ? err.message : '测试 Cookie 异常'
setModalTestResult({ valid: false, message: errMsg })
showError(errMsg)
} finally {
setModalTesting(false)
}
}
async function handleModalSubmit() {
try {
const values = await form.validateFields()
const shopId = values.shopId.trim()
await saveAdminPddStore(shopId, values)
showSuccess(`店铺【${values.shopName}】配置已保存`)
setModalVisible(false)
await loadConfig()
} catch (err) {
if (err instanceof Error) {
showError(err.message)
}
}
}
const columns: TableColumnsType<AdminPddStoreItem> = [
{
title: '店铺 ID',
dataIndex: 'shopId',
key: 'shopId',
width: 140,
render: (val: string) => <Tag color="blue">{val}</Tag>,
},
{
title: '店铺名称',
dataIndex: 'shopName',
key: 'shopName',
width: 160,
render: (val: string) => <Text strong>{val}</Text>,
},
{
title: 'Cookie 凭据',
dataIndex: 'cookie',
key: 'cookie',
ellipsis: true,
render: (cookie: string) => {
if (!cookie) return <Tag color="error">未配置</Tag>
const preview =
cookie.length > 30 ? `${cookie.slice(0, 15)}...${cookie.slice(-15)}` : cookie
return (
<Space size="small">
<Text code copyable={{ text: cookie }}>
{preview}
</Text>
<Tag color="cyan">{cookie.length} 字符</Tag>
</Space>
)
},
},
{
title: '状态',
dataIndex: 'enabled',
key: 'enabled',
width: 100,
render: (enabled: boolean, record) => (
<Switch
checked={enabled}
onChange={async (checked) => {
const updated = { ...record, enabled: checked }
await saveAdminPddStore(record.shopId, updated)
await loadConfig()
showSuccess(`已${checked ? '启用' : '停用'}店铺【${record.shopName}】`)
}}
/>
),
},
{
title: 'CK 校验结果',
key: 'testStatus',
width: 180,
render: (_, record) => {
const testRes = testResults[record.shopId]
if (!testRes) {
return <Text type="secondary">未校验</Text>
}
if (testRes.valid) {
return (
<Tag icon={<CheckCircleOutlined />} color="success">
有效 (订单数: {testRes.totalOrders ?? '-'})
</Tag>
)
}
return (
<Tag icon={<CloseCircleOutlined />} color="error" title={testRes.message}>
失效
</Tag>
)
},
},
{
title: '操作',
key: 'action',
width: 280,
render: (_, record) => (
<Space size="small">
<Button
size="small"
icon={<ThunderboltOutlined />}
loading={testingShopId === record.shopId}
onClick={() => handleTestCookie(record)}
>
测试CK
</Button>
<Button
size="small"
icon={<ReloadOutlined />}
loading={syncingShopId === record.shopId}
onClick={() => handleSyncSingleStore(record)}
>
立即拉单
</Button>
<Button size="small" icon={<EditOutlined />} onClick={() => handleOpenEditModal(record)}>
编辑
</Button>
<Popconfirm
title="确认删除此店铺配置?"
description="删除后将停止此店铺的自动拉单,已入库订单不会被删除。"
onConfirm={() => handleDeleteStore(record.shopId)}
okText="删除"
cancelText="取消"
okButtonProps={{ danger: true }}
>
<Button size="small" danger icon={<DeleteOutlined />}>
删除
</Button>
</Popconfirm>
</Space>
),
},
]
if (loading) {
return (
<Card>
<div style={{ textAlign: 'center', padding: '40px 0' }}>
<Spin tip="加载拼多多店铺配置中..." />
</div>
</Card>
)
}
return (
<Space direction="vertical" size="large" style={{ width: '100%' }}>
{/* Global Sync Settings */}
<Card
title="拼多多子服务全局配置"
extra={
<Space>
<Button
type="primary"
icon={<SaveOutlined />}
loading={saving}
onClick={handleSaveGlobalConfig}
>
保存全局配置
</Button>
<Button icon={<ReloadOutlined />} loading={syncingAll} onClick={handleSyncAll}>
全部店铺立即拉单
</Button>
</Space>
}
>
<Space size="large" wrap align="center">
<Space align="center">
<Text strong>启用拼多多同步:</Text>
<Switch
checked={config.enabled}
onChange={(checked) => setConfig((prev) => ({ ...prev, enabled: checked }))}
/>
</Space>
<Space align="center">
<Text strong>错峰轮询间隔 (秒):</Text>
<InputNumber
min={10}
max={3600}
value={config.pollIntervalSeconds}
onChange={(val) =>
setConfig((prev) => ({ ...prev, pollIntervalSeconds: Number(val || 20) }))
}
/>
<Text type="secondary">(建议 15~30 秒,多店铺将自动错峰拉取待发货订单)</Text>
</Space>
</Space>
</Card>
{/* Stores CRUD Table */}
<Card
title={
<Space>
<span>拼多多店铺列表</span>
<Tag color="processing">{config.stores.length} 个店铺</Tag>
</Space>
}
extra={
<Button type="primary" icon={<PlusOutlined />} onClick={handleOpenAddModal}>
添加店铺
</Button>
}
>
<Table
rowKey="shopId"
columns={columns}
dataSource={config.stores}
pagination={false}
locale={{ emptyText: '暂未添加拼多多店铺,请点击右上角“添加店铺”' }}
/>
</Card>
{/* Add / Edit Store Modal */}
<Modal
title={isEditing ? '编辑拼多多店铺' : '添加拼多多店铺'}
open={modalVisible}
onCancel={() => setModalVisible(false)}
onOk={handleModalSubmit}
okText="保存"
cancelText="取消"
width={680}
destroyOnClose
>
<Form form={form} layout="vertical" initialValues={{ enabled: true }}>
<Form.Item
name="shopId"
label="店铺标识 (shopId)"
rules={[
{ required: true, message: '请输入店铺唯一英文标识' },
{
pattern: /^[a-zA-Z0-9_-]+$/,
message: '只允许输入英文、数字、下划线或连字符',
},
]}
extra="系统内唯一标识,例如:store_pdd_01"
>
<Input placeholder="例如:store_pdd_01" disabled={isEditing} />
</Form.Item>
<Form.Item
name="shopName"
label="店铺名称 (shopName)"
rules={[{ required: true, message: '请输入店铺展示名称' }]}
extra="用于打手大厅与工单详情展示的中文名称,例如:和平精英专营1号店"
>
<Input placeholder="例如:和平精英专营1号店" />
</Form.Item>
<Form.Item
name="cookie"
label="商家后台 Cookie"
rules={[{ required: true, message: '请输入商家后台 Cookie' }]}
extra={
<Paragraph style={{ fontSize: '12px', margin: 0 }}>
登录 MMS 商家后台(mms.pinduoduo.com),按 F12 复制包含 <Text code>_nano_fp</Text>、
<Text code>PASS_ID</Text>、<Text code>api_uid</Text> 的完整 Cookie。
</Paragraph>
}
>
<Input.TextArea rows={4} placeholder="api_uid=...; _nano_fp=...; PASS_ID=...;" />
</Form.Item>
{/* Test Cookie Action inside Modal */}
<div style={{ marginBottom: 18 }}>
<Space>
<Button
icon={<ThunderboltOutlined />}
loading={modalTesting}
onClick={handleModalTestCookie}
>
测试此 Cookie 有效性
</Button>
<Text type="secondary">无需先保存,可即时验证此 Cookie 是否能连通 MMS API</Text>
</Space>
{modalTestResult && (
<div style={{ marginTop: 10 }}>
<Alert
type={modalTestResult.valid ? 'success' : 'error'}
showIcon
message={modalTestResult.valid ? 'Cookie 验证成功' : 'Cookie 验证失败'}
description={modalTestResult.message}
/>
</div>
)}
</div>
<Form.Item name="userAgent" label="浏览器 User-Agent (选填)">
<Input placeholder="留空则自动使用现实拟真 Chrome User-Agent" />
</Form.Item>
<Form.Item
name="proxy"
label="店铺独立代理 (选填)"
extra="如有多店铺防关联需求,可填写 HTTP/SOCKS5 代理地址,格式:http://user:pass@ip:port"
>
<Input placeholder="例如:http://127.0.0.1:7890" />
</Form.Item>
<Form.Item name="enabled" label="启用店铺拉单" valuePropName="checked">
<Switch />
</Form.Item>
</Form>
</Modal>
</Space>
)
}
@@ -5,3 +5,4 @@ export * from './kuaishou-feifei'
export * from './affiliate-dash'
export * from './cloudtentacles'
export * from './fulfillment-routing'
export * from './pdd'
@@ -0,0 +1,49 @@
/**
* Pinduoduo Admin Platform Config Service
* 拼多多后台平台配置、店铺 CRUD 与 CK 校验 HTTP 服务
*/
import { apiDelete, apiGet, apiPost } from '@/lib/http'
import type {
AdminPddConfig,
AdminPddStoreItem,
AdminPddTestCookieResult,
AdminPddSyncResult,
AdminPddSyncAllResponse,
} from '@/types/admin/platform-config'
export function fetchAdminPddConfig() {
return apiGet<AdminPddConfig>('/api/v1/admin/platform-config/pdd')
}
export function saveAdminPddConfig(payload: AdminPddConfig) {
return apiPost<AdminPddConfig>('/api/v1/admin/platform-config/pdd', payload)
}
export function saveAdminPddStore(shopId: string, payload: Partial<AdminPddStoreItem>) {
return apiPost<AdminPddStoreItem>(
`/api/v1/admin/platform-config/pdd/stores/${encodeURIComponent(shopId)}`,
payload,
)
}
export function deleteAdminPddStore(shopId: string) {
return apiDelete<AdminPddConfig>(
`/api/v1/admin/platform-config/pdd/stores/${encodeURIComponent(shopId)}`,
)
}
export function testAdminPddCookie(payload: {
cookie?: string
userAgent?: string
shopId?: string
}) {
return apiPost<AdminPddTestCookieResult>('/api/v1/admin/platform-config/pdd/test-cookie', payload)
}
export function triggerAdminPddSync(payload: { shopId?: string; pageSize?: number } = {}) {
return apiPost<AdminPddSyncAllResponse | AdminPddSyncResult>(
'/api/v1/admin/platform-config/pdd/sync',
payload,
)
}
@@ -78,3 +78,11 @@ export type {
AdminFulfillmentRoutingPreviewResult,
AdminFulfillmentRoutingRule,
} from './fulfillment-routing'
export type {
AdminPddStoreItem,
AdminPddConfig,
AdminPddTestCookieResult,
AdminPddSyncResult,
AdminPddSyncAllResponse,
} from './pdd'
@@ -0,0 +1,45 @@
/**
* Pinduoduo Admin Platform Config Types
* 拼多多平台配置与店铺管理前端数据结构定义
*/
export interface AdminPddStoreItem {
shopId: string
shopName: string
cookie: string
userAgent: string
proxy?: string
etag?: string
enabled: boolean
}
export interface AdminPddConfig {
enabled: boolean
pollIntervalSeconds: number
stores: AdminPddStoreItem[]
}
export interface AdminPddTestCookieResult {
valid: boolean
message: string
totalOrders?: number
firstOrderSn?: string
}
export interface AdminPddSyncResult {
shopId: string
shopName: string
total: number
fetchedCount: number
upsertedCount: number
ignoredCount: number
failedCount: number
error?: string
}
export interface AdminPddSyncAllResponse {
enabled: boolean
storeCount: number
syncedCount: number
results: AdminPddSyncResult[]
}