feat(consignment): 停用客服手动入库,寄卖货源只走平台提报
背景:手动入库是唯一会造出「没有提报单的现货」的入口,这类货没有寄卖单号, 统一状态机/凭证链/搜索/卖家端单号锚定都串不起来,只能在界面回退显示内部仓库号 INV, 正是"一件货两个号"的残留。业务定调:寄卖货源只走「打手平台提报 → 客服审核入库」。 - 前端寄售仓库的「手动入库」入口整块删除(按钮 + 入库弹窗 + 提交逻辑 + 服务函数) - 后端同时挡住,防旧页面缓存或直接调接口绕过: POST /worker-platform/consignment/inventories 返回 409 consignment_manual_inbound_disabled; 死代码一并清掉(服务层原实现、仓库层 createConsignmentInventory) - 提报审核入库路径不受影响,照旧生成内部仓库号 INV 并写 source_submission_id, 即每件货都必然挂着寄卖单号,INV 只作为内部编号存在、界面不展示 真实库验证:接口被拒(409),且「没有提报单的现货行数」前后均为 0。
This commit is contained in:
@@ -1,10 +1,4 @@
|
||||
import {
|
||||
EyeOutlined,
|
||||
PlusOutlined,
|
||||
ReloadOutlined,
|
||||
RollbackOutlined,
|
||||
StopOutlined,
|
||||
} from '@ant-design/icons'
|
||||
import { EyeOutlined, ReloadOutlined, RollbackOutlined, StopOutlined } from '@ant-design/icons'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import {
|
||||
Alert,
|
||||
@@ -35,7 +29,6 @@ import { useState } from 'react'
|
||||
import ConsignmentEvidenceGallery from '@/components/admin/ConsignmentEvidenceGallery'
|
||||
import {
|
||||
archiveAdminConsignmentInventory,
|
||||
createAdminConsignmentInventory,
|
||||
fetchAdminConsignmentInventories,
|
||||
fetchAdminConsignmentStats,
|
||||
returnAdminConsignmentInventory,
|
||||
@@ -147,13 +140,11 @@ export default function ConsignmentInventoryPanel() {
|
||||
const [pageSize, setPageSize] = useState<number>(20)
|
||||
|
||||
// 弹窗状态
|
||||
const [inboundModalOpen, setInboundModalOpen] = useState(false)
|
||||
const [editingItem, setEditingItem] = useState<ConsignmentInventory | null>(null)
|
||||
const [viewingItem, setViewingItem] = useState<ConsignmentInventory | null>(null)
|
||||
const [returnItem, setReturnItem] = useState<ConsignmentInventory | null>(null)
|
||||
const [returnReason, setReturnReason] = useState<string>('')
|
||||
|
||||
const [inboundForm] = Form.useForm()
|
||||
const [editForm] = Form.useForm()
|
||||
|
||||
// 写操作按钮统一按 consignment.manage 门控:后端全部挂了 requireAdminPermission,
|
||||
@@ -230,60 +221,6 @@ export default function ConsignmentInventoryPanel() {
|
||||
}
|
||||
}
|
||||
|
||||
// 手动入库提交
|
||||
async function handleInboundSubmit() {
|
||||
try {
|
||||
const values = await inboundForm.validateFields()
|
||||
const count = Number(values.count || 1)
|
||||
const costPriceFen = Math.round(Number(values.costPrice || 0) * 100)
|
||||
const depositAmountFen = values.depositAmount
|
||||
? Math.round(Number(values.depositAmount) * 100)
|
||||
: undefined
|
||||
const selectedWorker = workerOptions.find((w) => w.value === values.workerId)
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
await createAdminConsignmentInventory({
|
||||
itemName: values.itemName,
|
||||
system: values.system,
|
||||
serverZone: values.serverZone,
|
||||
series: values.series,
|
||||
diamondLevel: Number(values.diamondLevel || 1),
|
||||
brillianceCount: Number(values.brillianceCount || 0),
|
||||
costPriceFen,
|
||||
buyerName: values.buyerName,
|
||||
shopName: values.shopName,
|
||||
inboundBatch: values.inboundBatch,
|
||||
sellerContact: values.sellerContact,
|
||||
sellerGameId: values.sellerGameId,
|
||||
sellerPaymentInfo: values.sellerPaymentInfo,
|
||||
sellerPhone: values.sellerPhone,
|
||||
workerId: values.workerId || undefined,
|
||||
workerName: selectedWorker?.name || undefined,
|
||||
workerPhone: selectedWorker?.phone || undefined,
|
||||
depositAmountFen,
|
||||
credentialsJson: {
|
||||
loginMethod: values.loginMethod,
|
||||
account: values.account,
|
||||
password: values.password,
|
||||
authNote: values.authNote,
|
||||
},
|
||||
screenshotsJson: values.screenshots ? [values.screenshots] : [],
|
||||
remark: values.remark,
|
||||
allowMatching: values.allowMatching !== false,
|
||||
sourceType: 'manual',
|
||||
})
|
||||
}
|
||||
|
||||
message.success(`成功入库 ${count} 件寄售物品!`)
|
||||
setInboundModalOpen(false)
|
||||
inboundForm.resetFields()
|
||||
await refresh()
|
||||
} catch (err: any) {
|
||||
if (err.errorFields) return
|
||||
message.error(err.message || '创建寄售物品失败')
|
||||
}
|
||||
}
|
||||
|
||||
// 编辑更新提交
|
||||
async function handleEditSubmit() {
|
||||
if (!editingItem) return
|
||||
@@ -807,28 +744,6 @@ export default function ConsignmentInventoryPanel() {
|
||||
<Button icon={<ReloadOutlined />} onClick={refresh}>
|
||||
刷新
|
||||
</Button>
|
||||
{canManageConsignment ? (
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<PlusOutlined />}
|
||||
onClick={() => {
|
||||
inboundForm.resetFields()
|
||||
inboundForm.setFieldsValue({
|
||||
system: '苹果',
|
||||
serverZone: '微信区',
|
||||
series: '暗潮',
|
||||
diamondLevel: 1,
|
||||
brillianceCount: 0,
|
||||
count: 1,
|
||||
allowMatching: true,
|
||||
itemName: '苹果微信暗潮1钻',
|
||||
})
|
||||
setInboundModalOpen(true)
|
||||
}}
|
||||
>
|
||||
手动入库
|
||||
</Button>
|
||||
) : null}
|
||||
</Space>
|
||||
</Space>
|
||||
</Card>
|
||||
@@ -873,245 +788,6 @@ export default function ConsignmentInventoryPanel() {
|
||||
/>
|
||||
</Card>
|
||||
|
||||
{/* 手动入库弹窗 */}
|
||||
<Modal
|
||||
title="寄售物品入库 (和平精英)"
|
||||
open={inboundModalOpen}
|
||||
width={720}
|
||||
onCancel={() => setInboundModalOpen(false)}
|
||||
onOk={handleInboundSubmit}
|
||||
destroyOnClose
|
||||
>
|
||||
<Form form={inboundForm} layout="vertical">
|
||||
<Row gutter={16}>
|
||||
<Col span={6}>
|
||||
<Form.Item name="system" label="系统" rules={[{ required: true }]}>
|
||||
<Select
|
||||
options={[
|
||||
{ label: '苹果 (iOS)', value: '苹果' },
|
||||
{ label: '安卓 (Android)', value: '安卓' },
|
||||
{ label: '通用/双端', value: '通用' },
|
||||
]}
|
||||
onChange={() => autoFillItemName(inboundForm)}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={6}>
|
||||
<Form.Item name="serverZone" label="区服" rules={[{ required: true }]}>
|
||||
<Select
|
||||
options={[
|
||||
{ label: '微信区', value: '微信区' },
|
||||
{ label: 'QQ区', value: 'QQ区' },
|
||||
]}
|
||||
onChange={() => autoFillItemName(inboundForm)}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={6}>
|
||||
<Form.Item name="series" label="系列/枪系" rules={[{ required: true }]}>
|
||||
<Select
|
||||
options={[
|
||||
{ label: '暗潮', value: '暗潮' },
|
||||
{ label: '怒嚎', value: '怒嚎' },
|
||||
{ label: '血宴', value: '血宴' },
|
||||
]}
|
||||
onChange={() => autoFillItemName(inboundForm)}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={6}>
|
||||
<Form.Item name="diamondLevel" label="钻石等级" rules={[{ required: true }]}>
|
||||
<Select
|
||||
options={[
|
||||
{ label: '1 钻', value: 1 },
|
||||
{ label: '2 钻', value: 2 },
|
||||
{ label: '3 钻', value: 3 },
|
||||
{ label: '4 钻', value: 4 },
|
||||
{ label: '5 钻', value: 5 },
|
||||
{ label: '6 钻', value: 6 },
|
||||
{ label: '7 钻', value: 7 },
|
||||
]}
|
||||
onChange={() => autoFillItemName(inboundForm)}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Row gutter={16}>
|
||||
<Col span={6}>
|
||||
<Form.Item name="brillianceCount" label="璀璨数">
|
||||
<InputNumber
|
||||
min={0}
|
||||
max={7}
|
||||
style={{ width: '100%' }}
|
||||
onChange={() => autoFillItemName(inboundForm)}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
name="itemName"
|
||||
label="物品显示名称"
|
||||
rules={[{ required: true, message: '请输入物品名称' }]}
|
||||
>
|
||||
<Input placeholder="如:苹果微信暗潮1钻" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={6}>
|
||||
<Form.Item
|
||||
name="costPrice"
|
||||
label="收购价 (元)"
|
||||
rules={[{ required: true, message: '请输入收购价格' }]}
|
||||
>
|
||||
<InputNumber min={0} step={1} style={{ width: '100%' }} placeholder="如 80" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Row gutter={16}>
|
||||
<Col span={8}>
|
||||
<Form.Item
|
||||
name="buyerName"
|
||||
label="收购人 (客服姓名)"
|
||||
rules={[{ required: true, message: '请填写收购人' }]}
|
||||
>
|
||||
<Input placeholder="如:小明" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Form.Item name="shopName" label="关联店铺">
|
||||
<Input placeholder="如:淘宝旗舰店 / 拼多多" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Form.Item name="inboundBatch" label="收购批次/日期">
|
||||
<Input placeholder="如:202609-01" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Row gutter={16}>
|
||||
<Col span={8}>
|
||||
<Form.Item name="sellerContact" label="卖家微信号/联系方式">
|
||||
<Input placeholder="微信号或QQ" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Form.Item name="sellerPhone" label="卖家电话">
|
||||
<Input placeholder="手机号" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Form.Item name="sellerGameId" label="卖家游戏ID/角色名">
|
||||
<Input placeholder="游戏内名称" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Row gutter={16}>
|
||||
<Col span={14}>
|
||||
<Form.Item
|
||||
name="workerId"
|
||||
label="走打手接单平台 (供货/履约打手)"
|
||||
tooltip="关联打手账号后,工单匹配推荐时将直接指派该打手接单丢枪并冻结押金"
|
||||
>
|
||||
<Select
|
||||
allowClear
|
||||
showSearch
|
||||
filterOption={(input, option) =>
|
||||
String(option?.label ?? '')
|
||||
.toLowerCase()
|
||||
.includes(input.toLowerCase())
|
||||
}
|
||||
placeholder="选择关联的接单平台打手账号 (选填)"
|
||||
options={workerOptions}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={10}>
|
||||
<Form.Item
|
||||
name="depositAmount"
|
||||
label="履约押金金额 (元)"
|
||||
tooltip="指派该打手接单交付手枪时,平台自动冻结的押金金额(默认100元)"
|
||||
>
|
||||
<InputNumber
|
||||
min={0}
|
||||
step={10}
|
||||
addonBefore="¥"
|
||||
style={{ width: '100%' }}
|
||||
placeholder="如 100"
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Form.Item name="sellerPaymentInfo" label="卖家收款信息 (支付宝/微信号/银行卡)">
|
||||
<Input placeholder="如:支付宝 13800000000 (张三)" />
|
||||
</Form.Item>
|
||||
|
||||
<Card
|
||||
type="inner"
|
||||
size="small"
|
||||
title="账号与上号凭据信息 (仅内部及打手履约可见)"
|
||||
style={{ marginBottom: 16 }}
|
||||
>
|
||||
<Row gutter={16}>
|
||||
<Col span={6}>
|
||||
<Form.Item name="loginMethod" label="登录方式" style={{ marginBottom: 8 }}>
|
||||
<Select
|
||||
options={[
|
||||
{ label: '微信扫码', value: 'wechat_qrcode' },
|
||||
{ label: 'QQ扫码', value: 'qq_qrcode' },
|
||||
{ label: '账号密码', value: 'account_pwd' },
|
||||
{ label: '游戏内转赠/投喂', value: 'in_game_transfer' },
|
||||
]}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={9}>
|
||||
<Form.Item name="account" label="账号/手机" style={{ marginBottom: 8 }}>
|
||||
<Input placeholder="账号" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={9}>
|
||||
<Form.Item name="password" label="密码/令牌" style={{ marginBottom: 8 }}>
|
||||
<Input.Password placeholder="密码" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
<Form.Item name="authNote" label="上号说明/安全码" style={{ marginBottom: 0 }}>
|
||||
<Input placeholder="备用验证码/二次验证说明等" />
|
||||
</Form.Item>
|
||||
</Card>
|
||||
|
||||
<Row gutter={16}>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
name="count"
|
||||
label="入库数量 (批量录入同类物品)"
|
||||
tooltip="如果大于1,系统将自动创建对应数量的独立库存物品编号"
|
||||
>
|
||||
<InputNumber min={1} max={50} style={{ width: '100%' }} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
name="allowMatching"
|
||||
label="是否立即允许匹配"
|
||||
valuePropName="checked"
|
||||
tooltip="开启后,发布新工单时系统会自动检测此库存进行推荐或一键核销"
|
||||
>
|
||||
<Switch defaultChecked checkedChildren="允许匹配" unCheckedChildren="禁止匹配" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Form.Item name="remark" label="内部备注">
|
||||
<Input.TextArea rows={2} placeholder="如:来源某老客户置换、质押等" />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
|
||||
{/* 编辑物品弹窗 */}
|
||||
<Modal
|
||||
title="编辑寄售物品"
|
||||
|
||||
@@ -23,13 +23,6 @@ export function getAdminConsignmentInventory(id: number | string) {
|
||||
return apiGet<ConsignmentInventory>(`/api/v1/admin/worker-platform/consignment/inventories/${id}`)
|
||||
}
|
||||
|
||||
export function createAdminConsignmentInventory(payload: Record<string, unknown>) {
|
||||
return apiPost<ConsignmentInventory>(
|
||||
'/api/v1/admin/worker-platform/consignment/inventories',
|
||||
payload,
|
||||
)
|
||||
}
|
||||
|
||||
export function updateAdminConsignmentInventory(
|
||||
id: number | string,
|
||||
payload: Record<string, unknown>,
|
||||
|
||||
Reference in New Issue
Block a user