feat(worker-platform): 接单平台全面改用内部统一工单号,隐藏第三方平台单号防泄漏

1. 新增业务工单单号生成器 generateWorkOrderNo(),生成 WO+年月日+时分+随机数的统一内部单号(如 WO26091216358821);
2. 接单平台抢单大厅、订单列表、详情抽屉、模态弹窗及售后全面切换为展示 workOrderNo,彻底屏蔽拼多多/快手等第三方平台来源标识;
3. 打手资金明细补充关联内部工单号 relatedWorkOrderNo,避免打手在账单中识别原平台;
4. 管理后台工单面板升级为双单号清晰展示(高亮内部工单号 + 辅助可复制平台单号),兼顾打手对单与官方平台售后溯源。
This commit is contained in:
yml2213
2026-09-12 16:44:59 +08:00
parent ede9a87b97
commit 71912ef5d0
15 changed files with 57 additions and 20 deletions
@@ -152,6 +152,7 @@ export type WorkerWalletLedgerRow = {
note: string
payload_json: string | Record<string, unknown>
created_at: string
related_work_order_no?: string | null
related_platform_order_id?: string | null
}
@@ -34,7 +34,7 @@ export async function listWorkerWalletLedgers({
const offset = (page - 1) * pageSize
params.push(pageSize, offset)
const itemsResult = await query<WorkerWalletLedgerRow>(
`SELECT wwl.*, wo.platform_order_id AS related_platform_order_id
`SELECT wwl.*, wo.work_order_no AS related_work_order_no, wo.platform_order_id AS related_platform_order_id
FROM worker_wallet_ledgers wwl
LEFT JOIN work_orders wo ON wo.id = wwl.related_work_order_id
${whereClause}
@@ -12,7 +12,7 @@ import {
} from '../../repositories/worker-platform/index.js'
import type { JsonObject } from '../../types/json.js'
import { createHttpError } from '../../utils/http.js'
import { randomId } from '../../utils/random.js'
import { generateWorkOrderNo, randomId } from '../../utils/random.js'
import { nowIso } from '../../utils/time.js'
import {
getKuaishouIndustrySourceConfig,
@@ -103,7 +103,7 @@ export async function createAdminMockWorkOrder(payload: JsonObject = {}) {
},
}
const workOrder = await createWorkOrder({
workOrderNo: randomId('WO'),
workOrderNo: generateWorkOrderNo(),
platformOrderId,
productName,
categoryId,
@@ -17,7 +17,7 @@ import {
} from '../../repositories/worker-platform/index.js'
import type { JsonObject } from '../../types/json.js'
import type { OrderItemRow, OrderRow } from '../../types/repository/rows.js'
import { randomId } from '../../utils/random.js'
import { generateWorkOrderNo, randomId } from '../../utils/random.js'
import { nowIso } from '../../utils/time.js'
import { logInfo, logWarn } from '../../utils/logger.js'
import { safeParseJson } from '../admin/admin-query-utils.js'
@@ -173,7 +173,7 @@ export async function syncWorkerOrdersForSourceOrder(
const now = nowIso()
const materialComplete = fields.every((field) => !field.required)
const workOrder = await createWorkOrder({
workOrderNo: randomId('WO'),
workOrderNo: generateWorkOrderNo(),
productRuleId: Number(rule.id),
orderId: Number(order.id),
orderItemId: Number(item.id),
@@ -272,6 +272,7 @@ export function mapWalletLedger(ledger: WorkerWalletLedgerRow | null | undefined
balanceAfter: Number(ledger.balance_after || 0),
frozenAfter: Number(ledger.frozen_after || 0),
relatedWorkOrderId: ledger.related_work_order_id ? Number(ledger.related_work_order_id) : null,
relatedWorkOrderNo: String(ledger.related_work_order_no || '').trim() || null,
relatedPlatformOrderId: String(ledger.related_platform_order_id || '').trim() || null,
auditStatus: ledger.audit_status || '',
note: ledger.note || '',
+14
View File
@@ -7,3 +7,17 @@ export function randomToken(bytes = 24): string {
export function randomId(prefix: string, bytes = 6): string {
return `${prefix}${crypto.randomBytes(bytes).toString('hex')}`
}
/**
* Generate formatted internal work order number
* 生成系统标准内部工单号,如:WO26091216358821 (WO + 年月日 + 4位时分 + 4位随机)
*/
export function generateWorkOrderNo(date: Date = new Date()): string {
const yy = String(date.getFullYear()).slice(-2)
const mm = String(date.getMonth() + 1).padStart(2, "0")
const dd = String(date.getDate()).padStart(2, "0")
const hh = String(date.getHours()).padStart(2, "0")
const min = String(date.getMinutes()).padStart(2, "0")
const rand = Math.floor(1000 + Math.random() * 9000)
return `WO${yy}${mm}${dd}${hh}${min}${rand}`
}
@@ -942,6 +942,20 @@ export default function WorkOrdersPanel() {
创建时间 {formatAdminDateTime(row.createdAt)}
</Typography.Text>
</Space>
<Space size={4} align="center">
<Typography.Text type="secondary" style={{ fontSize: 12 }}>工单号:</Typography.Text>
<Typography.Text copyable={{ text: row.workOrderNo || getDisplayOrderNo(row) }} style={{ fontSize: 12, fontWeight: 500 }}>
{row.workOrderNo || getDisplayOrderNo(row)}
</Typography.Text>
</Space>
{row.platformOrderId ? (
<Space size={4} align="center">
<Typography.Text type="secondary" style={{ fontSize: 12 }}>平台号:</Typography.Text>
<Typography.Text type="secondary" copyable={{ text: row.platformOrderId }} style={{ fontSize: 12 }}>
{row.platformOrderId}
</Typography.Text>
</Space>
) : null}
{shopName || provider ? (
<Typography.Text type="secondary" style={{ fontSize: 12 }}>
{provider ? `来源:${provider}` : ''}
@@ -949,7 +963,6 @@ export default function WorkOrdersPanel() {
{shopName ? `店铺:${shopName}` : ''}
</Typography.Text>
) : null}
<Typography.Text type="secondary">{getDisplayOrderNo(row)}</Typography.Text>
</div>
)
},
@@ -1531,8 +1544,15 @@ export default function WorkOrdersPanel() {
<div className="worker-order-detail-stack">
<Card title="基础信息" size="small">
<Descriptions column={{ xs: 1, md: 2 }} bordered size="small">
<Descriptions.Item label="系统工单号">
<Typography.Text copyable={{ text: detailOrder.workOrderNo }}>
{detailOrder.workOrderNo || '-'}
</Typography.Text>
</Descriptions.Item>
<Descriptions.Item label="平台订单号">
{getDisplayOrderNo(detailOrder)}
<Typography.Text copyable={{ text: detailOrder.platformOrderId }}>
{detailOrder.platformOrderId || '-'}
</Typography.Text>
</Descriptions.Item>
<Descriptions.Item label="源订单 ID">
{detailOrder.orderId || '-'}
@@ -49,9 +49,9 @@ export function renderWorkOrderWorkers(order: WorkOrder) {
}
export function getDisplayOrderNo(
order: Pick<WorkOrder, 'platformOrderId'> | null | undefined,
order: Pick<WorkOrder, 'workOrderNo' | 'platformOrderId'> | null | undefined,
): string {
return String(order?.platformOrderId || '').trim() || '-'
return String(order?.workOrderNo || order?.platformOrderId || '').trim() || '-'
}
/** 用户付款金额(分);未知返回 null(不参与超价提示) */
@@ -264,8 +264,8 @@ function WorkerAfterSalesItem({
</Typography.Text>
</div>
<Space size={12} wrap className="worker-after-sales-item-meta">
<Typography.Text type="secondary" style={{ fontSize: 12 }} copyable>
{item.platformOrderId || item.workOrderNo || item.caseNo}
<Typography.Text type="secondary" style={{ fontSize: 12 }} copyable={{ text: item.workOrderNo || item.caseNo }}>
工单号 {item.workOrderNo || item.caseNo}
</Typography.Text>
<Typography.Text
type="secondary"
@@ -912,8 +912,8 @@ function resolveGrabDisabledReason(
return ''
}
function getDisplayOrderNo(order: Pick<WorkOrder, 'platformOrderId'> | null | undefined): string {
return String(order?.platformOrderId || '').trim() || '-'
function getDisplayOrderNo(order: Pick<WorkOrder, 'workOrderNo' | 'platformOrderId'> | null | undefined): string {
return String(order?.workOrderNo || order?.platformOrderId || '').trim() || '-'
}
function formatMoney(value: number | undefined) {
@@ -96,7 +96,7 @@ export function WorkerOrderDetailDrawer({
<Descriptions column={{ xs: 1, md: 2 }} bordered size="small">
<Descriptions.Item label="商品">{order.productName || '-'}</Descriptions.Item>
<Descriptions.Item label="分类">{order.categoryName || '-'}</Descriptions.Item>
<Descriptions.Item label="平台订单号">{getDisplayOrderNo(order)}</Descriptions.Item>
<Descriptions.Item label="工单编号">{getDisplayOrderNo(order)}</Descriptions.Item>
<Descriptions.Item label="状态">
<Tag color={getOrderStatusDisplay(order).color}>
{getOrderStatusDisplay(order).label}
@@ -141,8 +141,8 @@ export function WorkerProfileMobileLedgerRecords({
</span>
</div>
{row.note ? <div className="worker-profile-record-note">说明: {row.note}</div> : null}
{row.relatedPlatformOrderId ? (
<div className="worker-profile-record-sub">订单号 {row.relatedPlatformOrderId}</div>
{row.relatedWorkOrderNo || row.relatedPlatformOrderId ? (
<div className="worker-profile-record-sub">工单号 {row.relatedWorkOrderNo || row.relatedPlatformOrderId}</div>
) : null}
<div className="worker-profile-record-time">
{formatDateTime(String(row.createdAt || ''))}
@@ -164,9 +164,9 @@ export function hasSubmittedAcceptance(order: WorkOrder) {
}
export function getDisplayOrderNo(
order: Pick<WorkOrder, 'platformOrderId'> | null | undefined,
order: Pick<WorkOrder, 'workOrderNo' | 'platformOrderId'> | null | undefined,
): string {
return String(order?.platformOrderId || '').trim() || '-'
return String(order?.workOrderNo || order?.platformOrderId || '').trim() || '-'
}
export function getRequirementFields(order: WorkOrder | null): CollectField[] {
@@ -199,8 +199,8 @@ export const workerLedgerColumns: TableColumnsType<WorkerWalletLedger> = [
render: (_, row) => (
<div className="cell-stack">
<Typography.Text>{row.note || '-'}</Typography.Text>
{row.relatedPlatformOrderId ? (
<Typography.Text type="secondary">订单号 {row.relatedPlatformOrderId}</Typography.Text>
{row.relatedWorkOrderNo || row.relatedPlatformOrderId ? (
<Typography.Text type="secondary">工单号 {row.relatedWorkOrderNo || row.relatedPlatformOrderId}</Typography.Text>
) : null}
</div>
),
@@ -97,6 +97,7 @@ export type WorkerWalletLedger = {
balanceAfter: number
frozenAfter: number
relatedWorkOrderId: number | null
relatedWorkOrderNo?: string | null
relatedPlatformOrderId: string | null
auditStatus: string
note: string