feat(consignment): 发布寄卖自动从接单模板带出建议价
- 新纯函数 suggestConsignmentPriceFromRules:模板商品名(安卓暗潮1钻)用与 订单同一套规格解析器拆解,系统+系列+钻级全对上才命中;系统按通配口径 (模板写通用/双端两端都算,模板没写系统不命中——宁可不建议不给错价); 多条命中取最高价 - 新端点 GET /worker/consignment/price-suggestion(requireActiveWorker) - 发布表单规格选齐后 300ms 防抖请求:同规格去重、过期响应丢弃;命中自动 填入寄售价并提示来源与条数;卖家手改过价只提示不覆盖,换规格重新带出; 提交成功状态复位;接口失败静默(本地没配模板保持手填,行为不变) - 建议:本地模板没配不影响任何现有行为,生产库模板越多建议越准 后端 483(+建议价命中/不符排除/取最高/通用模板用例)/ 前端 54 测试全绿 docs/寄卖系统优化方案.md 记录批次 X
This commit is contained in:
@@ -408,3 +408,20 @@ export async function pruneWorkProductMatchLogs(
|
||||
remainingTotal,
|
||||
}
|
||||
}
|
||||
|
||||
/** 寄卖建议价:取所有启用且有价格的接单模板规则(发布寄卖时按规格带出建议价) */
|
||||
export async function listEnabledWorkProductRulesForSuggestion(): Promise<
|
||||
{ ruleKey: string; productName: string; rewardAmountFen: number }[]
|
||||
> {
|
||||
const result = await query<{ rule_key: string; product_name: string; reward_amount: number }>(
|
||||
`SELECT rule_key, product_name, reward_amount
|
||||
FROM work_product_rules
|
||||
WHERE enabled = TRUE AND reward_amount > 0
|
||||
ORDER BY sort_order ASC, id ASC`,
|
||||
)
|
||||
return result.rows.map((row) => ({
|
||||
ruleKey: row.rule_key,
|
||||
productName: row.product_name,
|
||||
rewardAmountFen: Number(row.reward_amount || 0),
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Router } from 'express'
|
||||
|
||||
import {
|
||||
confirmWorkerConsignmentFriendAdded,
|
||||
getWorkerConsignmentPriceSuggestion,
|
||||
listWorkerConsignments,
|
||||
publishWorkerConsignment,
|
||||
submitWorkerConsignmentGiftEvidence,
|
||||
@@ -25,6 +26,23 @@ router.post('/consignment/publish', requireActiveWorker, async (req, res) => {
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* GET /api/v1/worker/consignment/price-suggestion?system=&series=&diamondLevel=
|
||||
* 发布寄卖时按规格从「接单模板」带出建议价(安卓+暗潮+1钻 → 模板价 ¥120)
|
||||
*/
|
||||
router.get('/consignment/price-suggestion', requireActiveWorker, async (req, res) => {
|
||||
try {
|
||||
const result = await getWorkerConsignmentPriceSuggestion({
|
||||
system: req.query.system,
|
||||
series: req.query.series,
|
||||
diamondLevel: req.query.diamondLevel,
|
||||
})
|
||||
res.json({ ok: true, data: result })
|
||||
} catch (error) {
|
||||
sendRouteError(res, error, '获取建议价失败', '[worker/consignment/price-suggestion]')
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* GET /api/v1/worker/consignment/my-items
|
||||
* 打手查看自己在接单平台寄卖的手枪列表与状态
|
||||
|
||||
@@ -4,6 +4,7 @@ import test from 'node:test'
|
||||
import type { ConsignmentInventoryRow } from '../../repositories/worker-platform/consignment-repo.js'
|
||||
import {
|
||||
describeConsignmentSpecDifferences,
|
||||
suggestConsignmentPriceFromRules,
|
||||
extractPeacekeeperSpecs,
|
||||
findMatchingPeacekeeperInventories,
|
||||
matchPeacekeeperOrderWithInventory,
|
||||
@@ -396,3 +397,58 @@ test('真冲突仍然排除:互通救不了系统不符,也救不了系列
|
||||
null,
|
||||
)
|
||||
})
|
||||
|
||||
test('发布寄卖建议价:按 系统+系列+钻级 从接单模板带出(安卓暗潮1钻 → ¥120)', () => {
|
||||
const rules = [
|
||||
{ ruleKey: 'rule-6zuan', productName: '安卓暗潮6钻', rewardAmountFen: 50000 },
|
||||
{ ruleKey: 'rule-1zuan', productName: '安卓暗潮1钻', rewardAmountFen: 12000 },
|
||||
// 模板名里带区服/别名的也要能解析出来
|
||||
{ ruleKey: 'rule-1zuan-qq', productName: '安卓QQ区暗潮1钻', rewardAmountFen: 9999 },
|
||||
{ ruleKey: 'rule-5zuan-ios', productName: '苹果暗潮5钻', rewardAmountFen: 30000 },
|
||||
// 没配价格的规则不参与建议
|
||||
{ ruleKey: 'rule-2zuan-free', productName: '安卓暗潮2钻', rewardAmountFen: 0 },
|
||||
]
|
||||
|
||||
const hit = suggestConsignmentPriceFromRules(rules, {
|
||||
system: '安卓',
|
||||
series: '暗潮',
|
||||
diamondLevel: 1,
|
||||
})
|
||||
assert.ok(hit, '安卓+暗潮+1钻 应命中模板')
|
||||
assert.equal(hit.suggestedPriceFen, 12000, '多条命中取最高价(12000 > 9999)')
|
||||
assert.equal(hit.matchedRules.length, 2)
|
||||
|
||||
// 系统不符不命中:苹果的 1钻 没配价
|
||||
assert.equal(
|
||||
suggestConsignmentPriceFromRules(rules, { system: '苹果', series: '暗潮', diamondLevel: 1 }),
|
||||
null,
|
||||
)
|
||||
// 钻级不符不命中:2钻规则没价格、6钻是另一档
|
||||
assert.equal(
|
||||
suggestConsignmentPriceFromRules(rules, { system: '安卓', series: '暗潮', diamondLevel: 2 }),
|
||||
null,
|
||||
)
|
||||
// 系列不符不命中
|
||||
assert.equal(
|
||||
suggestConsignmentPriceFromRules(rules, { system: '安卓', series: '怒嚎', diamondLevel: 1 }),
|
||||
null,
|
||||
)
|
||||
})
|
||||
|
||||
test('发布寄卖建议价:模板写「通用/双端」时两端都命中', () => {
|
||||
const rules = [{ ruleKey: 'rule-dual', productName: '通用暗潮3钻', rewardAmountFen: 20000 }]
|
||||
for (const system of ['安卓', '苹果']) {
|
||||
const hit = suggestConsignmentPriceFromRules(rules, {
|
||||
system,
|
||||
series: '暗潮',
|
||||
diamondLevel: 3,
|
||||
})
|
||||
assert.equal(hit?.suggestedPriceFen, 20000, `${system} 应命中通用模板`)
|
||||
}
|
||||
// 模板没写系统则不命中(口径保守,宁可不建议也不给错价)
|
||||
const noSystem = [{ ruleKey: 'r', productName: '暗潮3钻', rewardAmountFen: 20000 }]
|
||||
assert.equal(
|
||||
suggestConsignmentPriceFromRules(noSystem, { system: '安卓', series: '暗潮', diamondLevel: 3 }),
|
||||
null,
|
||||
)
|
||||
})
|
||||
|
||||
@@ -418,3 +418,41 @@ export function findMatchingPeacekeeperInventories(
|
||||
return Number(a.inventory.id) - Number(b.inventory.id)
|
||||
})
|
||||
}
|
||||
|
||||
/** 接单模板规则(只取建议价要用的字段) */
|
||||
export type ConsignmentPriceSuggestionRule = {
|
||||
ruleKey: string
|
||||
productName: string
|
||||
rewardAmountFen: number
|
||||
}
|
||||
|
||||
/**
|
||||
* 从「接单模板」(work_product_rules)里给寄卖提报找建议价。
|
||||
*
|
||||
* 模板的商品名(如「安卓暗潮1钻」)用与订单同一套规格解析器拆解:
|
||||
* 系列、钻级必须完全一致;系统按通配口径比对(规则或提报任一侧是「通用/双端」都算匹配)。
|
||||
* 区服不参与——模板价不分微信/QQ。璀璨也不参与——模板价没按璀璨细分,卖家可自行加价。
|
||||
* 多条命中取最高价(该规格能卖到的最好价钱),卖家仍可手动改。
|
||||
*/
|
||||
export function suggestConsignmentPriceFromRules(
|
||||
rules: ConsignmentPriceSuggestionRule[],
|
||||
spec: { system: string; series: string; diamondLevel: number },
|
||||
): { suggestedPriceFen: number; matchedRules: ConsignmentPriceSuggestionRule[] } | null {
|
||||
const wantedSystem = normalizePeacekeeperSystem(spec.system)
|
||||
const wantedSeries = normalizePeacekeeperSeries(spec.series)
|
||||
const wantedDiamond = Math.round(Number(spec.diamondLevel || 0))
|
||||
if (!wantedSystem || !wantedSeries || wantedDiamond <= 0) return null
|
||||
|
||||
const matched = rules.filter((rule) => {
|
||||
const rewardFen = Math.round(Number(rule.rewardAmountFen || 0))
|
||||
if (rewardFen <= 0) return false
|
||||
const parsed = extractPeacekeeperSpecs(rule.productName)
|
||||
if (!parsed.system) return false
|
||||
if (parsed.series !== wantedSeries) return false
|
||||
if (Number(parsed.diamondLevel || 0) !== wantedDiamond) return false
|
||||
return parsed.system === wantedSystem || parsed.system === '通用' || wantedSystem === '通用'
|
||||
})
|
||||
if (matched.length === 0) return null
|
||||
const suggestedPriceFen = Math.max(...matched.map((rule) => Math.round(rule.rewardAmountFen)))
|
||||
return { suggestedPriceFen, matchedRules: matched }
|
||||
}
|
||||
|
||||
@@ -8,8 +8,10 @@ import {
|
||||
listConsignmentSubmissions,
|
||||
} from '../../repositories/worker-platform/consignment-repo.js'
|
||||
import { getWorkerUserById } from '../../repositories/worker-platform/index.js'
|
||||
import { listEnabledWorkProductRulesForSuggestion } from '../../repositories/worker-platform/product-match-repo.js'
|
||||
import { listWorkOrdersByIds } from '../../repositories/worker-platform/work-order-query-repo.js'
|
||||
import { deriveConsignmentItemStage } from '../../domain/consignment-item-stage.js'
|
||||
import { suggestConsignmentPriceFromRules } from './consignment-matching-service.js'
|
||||
import { hasInternalConsignmentFulfillment } from '../../repositories/worker-platform/work-order-gift-repo.js'
|
||||
import type { JsonObject } from '../../types/json.js'
|
||||
import { createHttpError } from '../../utils/http.js'
|
||||
@@ -420,3 +422,35 @@ export async function submitWorkerConsignmentGiftEvidence(
|
||||
const { workOrderId } = await resolveSellerConsignmentWorkOrder(workerId, submissionId)
|
||||
return submitWorkerOrderAcceptance(workOrderId, payload, session, { asConsignmentSupplier: true })
|
||||
}
|
||||
|
||||
/**
|
||||
* 发布寄卖的建议价:按 规格(系统/系列/钻级)从「接单模板」里带出价格,
|
||||
* 例如模板里有「安卓暗潮1钻 ¥120」,卖家选到 安卓+暗潮+1钻 就自动带出 120。
|
||||
* 找不到匹配的模板(本地没配、或规格没配价)就返回 null,前端保持手填。
|
||||
*/
|
||||
export async function getWorkerConsignmentPriceSuggestion(payload: {
|
||||
system?: unknown
|
||||
series?: unknown
|
||||
diamondLevel?: unknown
|
||||
}): Promise<{
|
||||
suggestedPriceFen: number | null
|
||||
matchedCount: number
|
||||
matchedRules: { ruleKey: string; productName: string; rewardAmountFen: number }[]
|
||||
}> {
|
||||
const system = String(payload.system || '').trim()
|
||||
const series = String(payload.series || '').trim()
|
||||
const diamondLevel = Math.round(Number(payload.diamondLevel || 0))
|
||||
if (!system || !series || diamondLevel <= 0) {
|
||||
return { suggestedPriceFen: null, matchedCount: 0, matchedRules: [] }
|
||||
}
|
||||
const rules = await listEnabledWorkProductRulesForSuggestion()
|
||||
const suggestion = suggestConsignmentPriceFromRules(rules, { system, series, diamondLevel })
|
||||
if (!suggestion) {
|
||||
return { suggestedPriceFen: null, matchedCount: 0, matchedRules: [] }
|
||||
}
|
||||
return {
|
||||
suggestedPriceFen: suggestion.suggestedPriceFen,
|
||||
matchedCount: suggestion.matchedRules.length,
|
||||
matchedRules: suggestion.matchedRules,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,11 +30,12 @@ import {
|
||||
Tag,
|
||||
Typography,
|
||||
} from 'antd'
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||
|
||||
import ImageUpload from '@/components/files/ImageUpload'
|
||||
import {
|
||||
confirmWorkerConsignmentFriendAdded,
|
||||
fetchWorkerConsignmentPriceSuggestion,
|
||||
fetchWorkerConsignments,
|
||||
fetchWorkerProfile,
|
||||
publishWorkerConsignment,
|
||||
@@ -87,6 +88,14 @@ export default function WorkerConsignmentPage() {
|
||||
const [evidenceSubmitting, setEvidenceSubmitting] = useState(false)
|
||||
const [cooldownNow, setCooldownNow] = useState(() => Date.now())
|
||||
const [form] = Form.useForm()
|
||||
// 建议价:规格选齐后从「接单模板」带出(安卓+暗潮+1钻 → ¥120);卖家手动改价则只提示不覆盖
|
||||
const [priceSuggestion, setPriceSuggestion] = useState<{
|
||||
fen: number
|
||||
matchedCount: number
|
||||
} | null>(null)
|
||||
const priceTouchedRef = useRef(false)
|
||||
const priceFetchTimer = useRef<ReturnType<typeof setTimeout> | null>(null)
|
||||
const lastPriceQueryKeyRef = useRef('')
|
||||
|
||||
const profileQuery = useQuery({
|
||||
queryKey: ['worker-profile'],
|
||||
@@ -183,7 +192,11 @@ export default function WorkerConsignmentPage() {
|
||||
})
|
||||
}, [profileQuery.isLoading, modal, publishDepositYuan])
|
||||
|
||||
function handleFormValuesChange() {
|
||||
function handleFormValuesChange(changed: Record<string, unknown>) {
|
||||
// 卖家手动改过价格后,本规格内不再自动覆盖(换规格会重新带出)
|
||||
if (changed && changed.askingPrice !== undefined) {
|
||||
priceTouchedRef.current = true
|
||||
}
|
||||
const sys = form.getFieldValue('system')
|
||||
const zone = form.getFieldValue('serverZone')
|
||||
const ser = form.getFieldValue('series')
|
||||
@@ -198,6 +211,44 @@ export default function WorkerConsignmentPage() {
|
||||
itemName: `${sys}${zoneLabel}${ser}${dia}钻${briStr} 苏尔南`,
|
||||
})
|
||||
}
|
||||
|
||||
// 规格选齐(系统+系列+钻级)后从「接单模板」带出建议价:安卓+暗潮+1钻 → 模板价 ¥120。
|
||||
// 防抖 + 同规格去重 + 过期响应丢弃;建议价拿不到不影响发布(静默)。
|
||||
if (priceFetchTimer.current) clearTimeout(priceFetchTimer.current)
|
||||
if (sys && ser && dia != null && Number(dia) > 0) {
|
||||
const queryKey = `${sys}|${ser}|${dia}`
|
||||
if (lastPriceQueryKeyRef.current !== queryKey) {
|
||||
lastPriceQueryKeyRef.current = queryKey
|
||||
// 换了规格:重新允许自动带价(上一规格的手改标记不再拦新规格)
|
||||
priceTouchedRef.current = false
|
||||
}
|
||||
priceFetchTimer.current = setTimeout(() => {
|
||||
void (async () => {
|
||||
try {
|
||||
const res = await fetchWorkerConsignmentPriceSuggestion({
|
||||
system: String(sys),
|
||||
series: String(ser),
|
||||
diamondLevel: Number(dia),
|
||||
})
|
||||
if (lastPriceQueryKeyRef.current !== queryKey) return
|
||||
const fen = Number(res.data?.suggestedPriceFen || 0)
|
||||
if (fen > 0) {
|
||||
setPriceSuggestion({ fen, matchedCount: Number(res.data?.matchedCount || 0) })
|
||||
if (!priceTouchedRef.current) {
|
||||
form.setFieldsValue({ askingPrice: fen / 100 })
|
||||
}
|
||||
} else {
|
||||
setPriceSuggestion(null)
|
||||
}
|
||||
} catch {
|
||||
// 建议价是锦上添花,失败不打扰卖家
|
||||
}
|
||||
})()
|
||||
}, 300)
|
||||
} else {
|
||||
lastPriceQueryKeyRef.current = ''
|
||||
setPriceSuggestion(null)
|
||||
}
|
||||
}
|
||||
|
||||
async function handlePublishSubmit(values: Record<string, any>) {
|
||||
@@ -245,6 +296,9 @@ export default function WorkerConsignmentPage() {
|
||||
|
||||
message.success(`寄卖提报成功,已暂扣履约押金 ¥${publishDepositYuan.toFixed(0)}`)
|
||||
form.resetFields()
|
||||
setPriceSuggestion(null)
|
||||
priceTouchedRef.current = false
|
||||
lastPriceQueryKeyRef.current = ''
|
||||
setScreenshots([])
|
||||
await Promise.all([myConsignmentsQuery.refetch(), profileQuery.refetch()])
|
||||
setRecordFilter('reviewing')
|
||||
@@ -576,6 +630,11 @@ export default function WorkerConsignmentPage() {
|
||||
name="askingPrice"
|
||||
label="寄售价格"
|
||||
rules={[{ required: true, message: '请填写寄售价格' }]}
|
||||
extra={
|
||||
priceSuggestion
|
||||
? `已按接单模板匹配 ${priceSuggestion.matchedCount} 条规则,建议价 ¥${(priceSuggestion.fen / 100).toFixed(0)}(可修改)`
|
||||
: undefined
|
||||
}
|
||||
className="worker-consignment-spec-card is-price"
|
||||
>
|
||||
<InputNumber
|
||||
|
||||
@@ -483,3 +483,16 @@ export function submitWorkerConsignmentGiftEvidence(
|
||||
payload,
|
||||
)
|
||||
}
|
||||
|
||||
/** 发布寄卖:按规格(系统/系列/钻级)从「接单模板」带出建议价,如 安卓+暗潮+1钻 → ¥120 */
|
||||
export function fetchWorkerConsignmentPriceSuggestion(params: {
|
||||
system: string
|
||||
series: string
|
||||
diamondLevel: number
|
||||
}) {
|
||||
return apiGet<{
|
||||
suggestedPriceFen: number | null
|
||||
matchedCount: number
|
||||
matchedRules: { ruleKey: string; productName: string; rewardAmountFen: number }[]
|
||||
}>('/api/v1/worker/consignment/price-suggestion', params)
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
| `632dddc4` | 上一轮:H5 渠道整体下线 + 凭据字段修复 + 状态机封闭(撤销守卫 / 库存绑定唯一性 / 生命周期解绑)+ 押金归属取台账 | 迁移 `079`、`consignment-*` 五个文件、`work-order-*` 三个仓库、两个前端面板 |
|
||||
| `5d11bf20` | 批次 C+D:指派部分成功补偿、凭证核销如实反馈、空规格不放行 + 履约/指派服务端规格复检 | `consignment-fulfillment-service.ts`、`consignment-matching-service.ts`、`consignment-repo.ts`、履约路由、`WorkOrdersPanel.tsx` |
|
||||
| `d6f6219f` | 批次 F1:内部现货履约停在「待加好友」,客服确认后才进赠送冷却 + 备注快捷项 | `work-order-gift-repo.ts`、履约服务、`WorkOrdersPanel.tsx`、`utils/gift-phase.ts` |
|
||||
| 本批提交(批次 X,同一提交内含本文档,故不写自身 hash) | 批次 X:发布寄卖自动带建议价——规格选齐后按 系统+系列+钻级 从「接单模板」(work_product_rules)匹配价格自动填入(安卓暗潮1钻 → ¥120),多条命中取最高,手改过不覆盖 | `consignment-matching-service.ts`、`product-match-repo.ts`、`consignment-worker-service.ts`、`routes/worker/consignment.ts`、`WorkerConsignmentPage.tsx` |
|
||||
| 本批提交(批次 W,同一提交内含本文档,故不写自身 hash) | 批次 W:规格口径定案——后台全量(系统 苹果/安卓/通用,区服 微信/QQ/互通),打手端按物品展示(苏尔南:系统二选一、区服唯一「互通」默认选中);「互通」为区服通配值,匹配引擎通配符对称化 | `utils/consignment-spec.ts`、`WorkerConsignmentPage.tsx`、两个面板、`consignment-matching-service.ts`、`consignment-worker-service.ts` |
|
||||
| 本批提交(批次 V,同一提交内含本文档,故不写自身 hash) | 批次 V:复核遗留——拼单份额验收收官补寄卖钩子(押金释放+供货货款)、手动匹配弹窗 409 刷新/关键字透传/无打手禁指派、核销反馈抽共享出口 | `work-order-settlement-repo.ts`、`ConsignmentManualMatchModal.tsx`、`utils/voucher-consume.ts` |
|
||||
| 本批提交(批次 U,同一提交内含本文档,故不写自身 hash) | 批次 U:复核修复——占库 allocation_status 撞 080 CHECK(真实库必 500)、指派非供货卖家时货款双付、手动匹配入口按 fulfill 权限门控 | `consignment-repo.ts`、`consignment-fulfillment-service.ts`、两个面板、三个测试文件 |
|
||||
@@ -291,6 +292,23 @@
|
||||
规格冲突 → 409 `consignment_spec_mismatch`;非 exact 且未显式传 `allowPartial` → 409 `consignment_spec_partial_requires_confirm`。
|
||||
前端在"部分匹配"候选上先 `modal.confirm` 危险确认,再带 `allowPartial: true` 重试,高配充低配的合法玩法保留但不可误触。
|
||||
3. ⏳ 匹配下推 SQL + 批量匹配接口 + 工单列表角标(P2-8)未做:当前仍是"取全量在库 → 内存比对"。
|
||||
|
||||
### 批次 X · 发布寄卖自动带「接单模板」建议价(本轮)
|
||||
|
||||
**需求**:接单平台已按规格配好模板价(生产库几十条,如 安卓暗潮1钻 ¥120 / 6钻 ¥500),
|
||||
卖家发布寄卖时按规格自动带出,不用手填/猜价。
|
||||
|
||||
1. ✅ **后端**:纯函数 `suggestConsignmentPriceFromRules` —— 模板商品名(「安卓暗潮1钻」)
|
||||
用与订单同一套 `extractPeacekeeperSpecs` 解析,系统+系列+钻级全对上才命中(系统按通配口径,
|
||||
模板写「通用/双端」两端都算;模板没写系统则不命中,宁可不建议不给错价);多条命中取最高价。
|
||||
新端点 `GET /worker/consignment/price-suggestion`(requireActiveWorker)。
|
||||
2. ✅ **前端**:发布表单规格选齐(系统+系列+钻级)后 300ms 防抖请求;同规格去重、过期响应丢弃;
|
||||
命中则自动填入寄售价并提示「已按接单模板匹配 N 条规则,建议价 ¥X(可修改)」;
|
||||
**卖家手动改过价则只提示不覆盖**,换规格后重新允许自动带出;提交成功后状态复位。
|
||||
建议价接口失败静默(锦上添花,不影响发布)。本地没配模板时无建议价、保持手填,行为不变。
|
||||
3. ✅ **测试**:建议价纯函数行为测试(命中/系统不符/钻级不符/系列不符/多命中取最高/通用模板两端命中/无系统模板不命中)。
|
||||
|
||||
**验证**:后端 483 / 前端 54 测试全绿;两侧 `typecheck` / `lint:check` / `format:check` / `check:sql` 通过。
|
||||
### 批次 W · 规格口径:互通通配 + 后台全量/发布端分物品展示(本轮)
|
||||
|
||||
**业务定案**:
|
||||
|
||||
Reference in New Issue
Block a user