const MODULE = 'procurement-sample-fulfillment' const PLATFORM = '/api/platform/v1/last-leg/overseas-procurements' const MERCHANT = '/api/merchant/v1/last-leg/overseas-procurements' const clone = value => value == null ? value : structuredClone(value) const cents = value => { const match = /^(\d+)(?:\.(\d{1,2}))?$/.exec(String(value)) if (!match) throw new Error('钱包金额缺失或精度异常') return BigInt(match[1]) * 100n + BigInt((match[2] || '').padEnd(2, '0')) } /** Internal scenario; the TEST coordinator owns isolated customer creation, funding, sessions and file upload. */ export async function verifyProcurementSample(context, inputs) { if (!context.marker || typeof context.checkpoint !== 'function' || typeof context.request !== 'function' || inputs?.marker !== context.marker || !/^[1-9]\d*$/.test(String(inputs.customerId)) || !/^[1-9]\d*$/.test(String(inputs.warehouseId)) || !inputs.customerToken || typeof inputs.upload !== 'function' || typeof inputs.readWallet !== 'function') throw new Error('缺少本轮独立客户、仓库、会话、上传或持久化验收前置') if (context.createdResources.procurementSample) throw new Error('已有采购验收证据,不自动重跑或覆盖') const proof = context.createdResources.procurementSample = { marker: context.marker, status: 'RUNNING', requests: [], checks: [], orderNo: null } const check = async (label, accepted) => { proof.checks.push({ label, passed: Boolean(accepted) }); await context.checkpoint() if (!accepted) throw new Error(`${MODULE}: ${label}`) } const customerId = String(inputs.customerId) let detail, sequence = 0 const at = () => { const value = detail?.customerView?.order?.updatedAt if (typeof value !== 'string' || !/^\d{4}-\d{2}-\d{2}T/.test(value)) throw new Error('服务端业务时间缺失') return value } const date = () => at().slice(0, 10) const key = label => `${context.marker}-${++sequence}-${label}`.slice(0, 128) const path = (operation = '') => `${PLATFORM}/${encodeURIComponent(proof.orderNo)}${operation ? '/' + operation : ''}?customerId=${encodeURIComponent(customerId)}` async function request(method, url, body, label, token = context.token, expectedCode = '200') { const entry = { method, path: url, body: clone(body), label, status: 'SENDING' } proof.requests.push(entry); await context.checkpoint() let response try { response = await context.request(method, url, body, token) } catch (error) { entry.status = 'UNKNOWN_RESULT'; await context.checkpoint(); throw error } Object.assign(entry, { status: response?.http === 0 || response?.http >= 500 || !response?.body ? 'UNKNOWN_RESULT' : 'RESPONDED', http: response?.http, code: String(response?.body?.code), requestId: response?.requestId, traceId: response?.traceId }) await context.checkpoint() await check(label, response?.http === 200 && String(response?.body?.code) === expectedCode) return response.body.data } async function read() { detail = await request('GET', path(), undefined, '回查采购聚合详情'); return detail } async function command(operation, values = {}, replay = false) { const body = { version: detail.customerView.order.version, idempotencyKey: key(operation.replaceAll('/', '-')), reason: context.marker, ...values } await request('POST', path(operation), body, operation) if (replay) await request('POST', path(operation), body, `${operation} 原请求重放`) await read(); return body } async function waitFor(label, predicate) { for (let attempt = 0; attempt < 31; attempt++) { await read() if (predicate(detail)) return if (attempt < 30) await new Promise(resolve => setTimeout(resolve, 2000)) } await check(`${label}在限定时间内完成`, false) } const fee = purpose => detail.customerView.fees.find(row => row.active && row.purpose === purpose) async function wallet(label, spent, frozen) { const current = await inputs.readWallet() proof.walletChecks ??= []; proof.walletChecks.push({ label, account: clone(current) }) await check(label, cents(current.actualBalance) === cents(proof.walletBefore.actualBalance) - spent && cents(current.frozenBalance) === cents(proof.walletBefore.frozenBalance) + frozen && cents(current.creditUsed) === cents(proof.walletBefore.creditUsed)) } try { proof.walletBefore = clone(await inputs.readWallet()) await check('客户现金余额足以完成独立样本', cents(proof.walletBefore.availableBalance) >= 500n) const recipient = { name: '采购样品验收', phone: '13800000002', address: '广东省深圳市南山区科技园测试路1号' } const create = { customerName: inputs.customerName, purchaseCategory: 'SAMPLE', productName: `${context.marker} sample`, productSpecification: 'one-spec', purchaseQuantity: 2, budgetUnitPrice: '1.00', currency: 'CNY', receivingAddress: recipient.address, contactName: recipient.name, contactPhone: recipient.phone, idempotencyKey: key('create') } const created = await request('POST', MERCHANT, create, '客户创建样品订单', inputs.customerToken) proof.orderNo = created?.order?.orderNo; await context.checkpoint() await check('客户创建响应包含本轮订单', typeof proof.orderNo === 'string' && created.order.customerId === customerId) const repeated = await request('POST', MERCHANT, create, '客户同键重放', inputs.customerToken) await check('重放没有新建采购单', repeated.order.orderNo === proof.orderNo) await read() await request('POST', path('review'), { version: detail.customerView.order.version, idempotencyKey: key('review'), decision: 'APPROVE', purchaseWarehouseId: String(inputs.warehouseId), remark: context.marker }, '审核通过并确定收货仓') await read() const purchaseFile = await inputs.upload(proof.orderNo, 'PURCHASE') const voucher = await inputs.upload(proof.orderNo, 'VOUCHER') const labelFile = await inputs.upload(proof.orderNo, 'LABEL') await check('凭证上传返回正式文件引用', [purchaseFile, voucher, labelFile].every(file => /^[1-9]\d*$/.test(String(file.fileId)))) await command('purchase', { productName: create.productName, specification: create.productSpecification, quantity: 2, amount: '1.00', currency: 'CNY', purchasedAt: at(), estimatedArrivalAt: new Date(Date.now() + 86400000).toISOString().slice(0,19), voucherFileId: String(purchaseFile.fileId) }, true) await waitFor('样品货款实扣及来源关联', () => fee('SAMPLE_PAYMENT')?.walletStatus === 'SETTLED' && fee('SAMPLE_PAYMENT')?.sourceLinked) await wallet('购买完成扣货款1元', 100n, 0n) for (let count = 0; count < 2; count++) await command('overseas-receipts', { leg: 'OVERSEAS', warehouseId: String(inputs.warehouseId), goodQuantity: 1, damagedQuantity: 0, extraQuantity: 0, receivedAt: at() }, true) await check('分次收货重放不增加数量', detail.physical.receipts.filter(row => row.leg === 'OVERSEAS' && row.active).reduce((sum,row) => sum + Number(row.goodQuantity),0) === 2) await command('overseas-receipts/complete', { leg: 'OVERSEAS' }) const receipts = detail.physical.receipts.filter(row => row.leg === 'OVERSEAS' && row.active) await command('packing', { cartons: receipts.map((row,index) => ({ cartonNo: `${context.marker}-C${index+1}`, lengthCm: '10.00', widthCm: '10.00', heightCm: '10.00', grossWeightKg: '1.000', sources: [{ receiptId: String(row.id), quantity: 1 }] })) }) await command('packing/confirm', { packingVersion: detail.physical.packingVersion }) const cartonIds = detail.physical.cartons.map(row => String(row.id)) async function labels(leg) { await command('labels', { leg, packingVersion: detail.physical.packingVersion, receiver: recipient, labels: cartonIds.map((cartonId,index) => ({ cartonId, fileId: String(labelFile.fileId), page: 1, carrier: 'TEST-EXPRESS', trackingNo: `${context.marker}-${leg}-${index+1}` })) }) return detail.transport[leg === 'OVERSEAS' ? 'overseasLabels' : 'domesticLabels'].version } const internationalLabels = await labels('OVERSEAS') await command('international-estimate', { labelVersion: internationalLabels, amount: '2.00', businessDate: date() }, true) await waitFor('预估冻结', () => fee('INTERNATIONAL_ESTIMATE')?.walletStatus === 'FROZEN') await wallet('预估2元只冻结不消费', 100n, 200n) await command('labels/apply', { labelVersion: internationalLabels, cartonIds, appliedAt: at() }) await command('overseas-shipment', { leg: 'OVERSEAS', labelVersion: internationalLabels, cartonIds, shippedAt: at() }, true) await check('国际段只有一次实际交寄', detail.transport.shipments.filter(row => row.leg === 'OVERSEAS').length === 1) for (const sourceCartonId of cartonIds) await command('domestic-receipts', { leg: 'DOMESTIC', sourceCartonId, goodQuantity: 1, damagedQuantity: 0, extraQuantity: 0, receivedAt: at() }, true) await command('domestic-receipts/complete', { leg: 'DOMESTIC' }) const actual = amount => ({ value: { determination: amount === '0.00' ? 'ZERO' : 'POSITIVE', amount, currency: 'CNY', businessDate: date(), voucherFileId: String(voucher.fileId), reason: context.marker } }) await command('charges/confirm', { internationalFreight: actual('1.50'), duty: actual('0.00'), domesticFreight: actual('0.00'), otherDetermination: 'NOT_APPLICABLE', otherFees: [] }, true) await waitFor('实际费用实扣及来源关联', () => fee('INTERNATIONAL_FREIGHT')?.walletStatus === 'SETTLED' && fee('INTERNATIONAL_FREIGHT')?.sourceLinked) await wallet('实际国际运费扣1.5元并释放剩余冻结', 250n, 0n) const domesticLabels = await labels('DOMESTIC') await command('customer-shipment', { leg: 'DOMESTIC', labelVersion: domesticLabels, cartonIds, shippedAt: at() }, true) await check('寄客户正常终态及单次交寄', detail.customerView.order.status === 'SHIPPED_TO_CUSTOMER' && detail.transport.shipments.filter(row => row.leg === 'DOMESTIC').length === 1) const customer = await request('GET', `${MERCHANT}/${encodeURIComponent(proof.orderNo)}`, undefined, '客户最终详情', inputs.customerToken) await check('客户可见终态且没有平台内部数据', customer.order.status === 'SHIPPED_TO_CUSTOMER' && !['files','purchaseFacts','controls','internalRemark','operations'].some(field => Object.hasOwn(customer,field))) await wallet('重复交寄不再次扣款', 250n, 0n) proof.walletBusinessNos = [...new Set(detail.fees.filter(row => row.walletStatus === 'SETTLED').map(row => row.walletBusinessNo))] await check('每笔成功消费保留权威钱包业务引用', proof.walletBusinessNos.length === 2 && proof.walletBusinessNos.every(value => typeof value === 'string' && value.length > 0)) proof.settledAmountCny = '2.50' proof.status = 'PASSED'; await context.checkpoint(); return proof } catch (error) { proof.status = 'FAILED'; proof.failure = error.message; await context.checkpoint(); throw error } }