From c6978c0983bc16cd26d40a46a9b1776deb8415b1 Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Tue, 15 Sep 2026 20:51:09 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/inbound.js | 4 ++-- src/api/overseas.js | 7 ++++++- src/api/relabel.js | 2 +- tests/overseas-request.test.mjs | 15 +++++++++++++++ 4 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/api/inbound.js b/src/api/inbound.js index 86c326a..1d22e16 100644 --- a/src/api/inbound.js +++ b/src/api/inbound.js @@ -17,8 +17,8 @@ export const getCurrentWarehouseProfile = () => { export const getReceivingContainer = async (containerNo) => normalizeReceivingSession(await overseasRequest('GET', `${RECEIVING_BASE}/${encodeURIComponent(containerNo)}`)) export const prepareReceivingContainer = async (containerNo, request) => normalizeReceivingSession(await overseasRequest('POST', `${RECEIVING_BASE}/${encodeURIComponent(containerNo)}/prepare`, request)) export const uploadCurrentUnloadPhoto = (filePath) => overseasUpload(current('/unload-photos/files'), filePath) -export const downloadCurrentUnloadPhoto = (downloadPath) => overseasDownload(downloadPath) -export const downloadCurrentDomesticLoadingPhoto = (downloadPath) => overseasDownload(downloadPath) +export const downloadCurrentUnloadPhoto = (downloadPath) => overseasDownload(downloadPath, { expectedContentTypePrefix: 'image/' }) +export const downloadCurrentDomesticLoadingPhoto = (downloadPath) => overseasDownload(downloadPath, { expectedContentTypePrefix: 'image/' }) export const signCurrentReceiving = async (request) => normalizeReceivingSession(await overseasRequest('POST', current('/sign'), request)) export const getCurrentReceiving = async () => normalizeReceivingSession(await overseasRequest('GET', current())) export const resolveCurrentCarton = async (cartonMark) => normalizeReceivingInbound(await overseasRequest('POST', current('/cartons/resolve'), { cartonMark })) diff --git a/src/api/overseas.js b/src/api/overseas.js index c792931..44d70df 100644 --- a/src/api/overseas.js +++ b/src/api/overseas.js @@ -116,7 +116,8 @@ export async function overseasUpload(path, filePath, name = 'file', retry = true return body.data } -export async function overseasDownload(path, retry = true) { +export async function overseasDownload(path, options = {}) { + const { retry = true, expectedContentTypePrefix = '' } = options const attemptedToken = session.token let response = await rawDownload(path, attemptedToken) if (retry && unauthorized(response)) { @@ -126,6 +127,10 @@ export async function overseasDownload(path, retry = true) { if (response?.statusCode !== 200 || !response?.tempFilePath) { throw errorFrom({ ...response, data: {} }, '签收照片下载失败') } + const contentType = String(response?.header?.['content-type'] || response?.header?.['Content-Type'] || '').toLowerCase() + if (expectedContentTypePrefix && !contentType.startsWith(expectedContentTypePrefix.toLowerCase())) { + throw errorFrom({ ...response, data: {} }, '照片服务返回了无效内容,请稍后重试') + } return response.tempFilePath } diff --git a/src/api/relabel.js b/src/api/relabel.js index b3312a8..d0d24f0 100644 --- a/src/api/relabel.js +++ b/src/api/relabel.js @@ -49,4 +49,4 @@ export const reportRelabelException = (relabelNo, request) => detailAction(relab export const completeRelabelTask = (relabelNo, request) => detailAction(relabelNo, '/complete', request) export const reviewRelabelTask = (relabelNo, request) => detailAction(relabelNo, '/review', request) export const uploadRelabelPhoto = (relabelNo, filePath) => overseasUpload(`${BASE}/${encoded(relabelNo)}/photos/files`, filePath) -export const downloadRelabelPhoto = (relabelNo, fileId) => overseasDownload(`${BASE}/${encoded(relabelNo)}/photos/files/${encoded(fileId)}/download`) +export const downloadRelabelPhoto = (relabelNo, fileId) => overseasDownload(`${BASE}/${encoded(relabelNo)}/photos/files/${encoded(fileId)}/download`, { expectedContentTypePrefix: 'image/' }) diff --git a/tests/overseas-request.test.mjs b/tests/overseas-request.test.mjs index a417d44..0a2a2ee 100644 --- a/tests/overseas-request.test.mjs +++ b/tests/overseas-request.test.mjs @@ -174,6 +174,21 @@ test('authenticated photo download maps the public api path through the H5 overs assert.equal(calls[0].header.Authorization, `Bearer ${value.overseasAccessToken}`) }) +test('image download rejects a JSON error envelope returned with HTTP 200', async () => { + prepareSession() + globalThis.uni.downloadFile = (options) => options.success({ + statusCode: 200, + tempFilePath: '/tmp/error.json', + header: { 'Content-Type': 'application/json', 'X-Request-Id': 'photo-request', 'X-Trace-Id': 'photo-trace' } + }) + + await assert.rejects( + overseasDownload('/api/pda/v1/last-leg/photo', { expectedContentTypePrefix: 'image/' }), + (error) => error.message === '照片服务返回了无效内容,请稍后重试' + && error.requestId === 'photo-request' && error.traceId === 'photo-trace' + ) +}) + test('business and network errors retain diagnostics without refreshing or clearing a valid session', async () => { prepareSession(); let refreshCount = 0 globalThis.uni.request = (options) => { From 2dabdfde7cd8399f50f480fd0ecd4be92576ff34 Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Tue, 15 Sep 2026 21:05:09 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E6=94=AF=E6=8C=81PDA=E6=95=B4=E6=89=98?= =?UTF-8?q?=E6=8B=A3=E8=B4=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AGENTS.md | 2 +- src/api/fulfillment.js | 4 ++++ src/pages/outbound/outbound.vue | 12 +++++++++--- tests/fulfillment-workflows.test.mjs | 11 ++++++++--- 4 files changed, 22 insertions(+), 7 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 21f8416..6771a53 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -48,7 +48,7 @@ | 换标真实作业 | `src/pages/relabel/relabel.vue`、`src/api/relabel.js` | 同一页面按操作参数承载换标收货、执行与复核;执行页按当前单明细切换 SKU/箱唛 Tab,组合明细同时显示两类需求。SKU 逐条扫码旧新SKU并携带审批件数,箱唛扫码仓库唛头/新FBA箱唛;组合单按同箱保留两个Tab的核对输入,全部核对后只提交一次箱事实。认领同步处理中;支持拍照、异常/返工和幂等恢复 | | 现场查询真实作业 | `src/pages/query/query.vue`、`src/api/warehouse-operations.js` | 同一页面按操作参数承载库存、库位与托盘查询,只展示稳定业务标识 | | 排车拣货与发车 | `src/pages/dispatch/dispatch.vue`、`src/api/fulfillment.js`、`src/stores/fulfillment.js` | 任务列表/详情、托盘业务码扫码拣货、幂等完成;发车必须同时完成拣货核平、平台转派送并严格消费服务端 `canDepart`、`departBlockReason`,不得按本地数量自行推断;公网前缀 `/api/pda/v1/last-leg/dispatches/**` | -| 普通/一件代发出库 | `src/pages/outbound/outbound.vue`、`src/api/fulfillment.js`、`src/stores/fulfillment.js` | 任务列表/详情、稳定业务码扫码拣货和幂等完成;公网前缀 `/api/pda/v1/last-leg/outbound-tasks/**`;派送由 Admin/Client 承载 | +| 普通/一件代发出库 | `src/pages/outbound/outbound.vue`、`src/api/fulfillment.js`、`src/stores/fulfillment.js` | 任务列表/详情、逐箱扫码、扫描 DP 托盘码原子拣取整托剩余箱件及幂等完成;公网前缀 `/api/pda/v1/last-leg/outbound-tasks/**`;派送由 Admin/Client 承载 | | 扫码设备与诊断 | `src/utils/scan-broadcast.js`、`src/pages/scanner-test/scanner-test.vue` | `src/components/ScanTargetInput.vue`、声音反馈工具;广播 Action/Extra 见本文件服务边界 | | 作业记录与人员 | `src/pages/history/history.vue`、`src/pages/profile/profile.vue` | 会话隔离、退出清理和后续真实作业历史契约 | | 业务标识与内部 ID 隔离 | `tests/business-identifiers-ui.test.mjs`;页面入口 `src/pages/`、`src/components/`,会话映射 `src/stores/session.js` | 页面只展示集装箱号、入仓码、箱码、托盘码、库位码、仓库名称等业务值;不得以 orderId、inboundId、planId、warehouseId、palletId、locationId、user.id 等内部 ID 或截断值回退,错误诊断保留 code、requestId、traceId | diff --git a/src/api/fulfillment.js b/src/api/fulfillment.js index a1c74a5..0a82511 100644 --- a/src/api/fulfillment.js +++ b/src/api/fulfillment.js @@ -112,6 +112,10 @@ export function pickOutboundTask(taskNo, request) { return overseasRequest('POST', `${OUTBOUND_BASE}/${encoded(taskNo)}/pick`, request) } +export function pickOutboundPallet(taskNo, palletNo, request) { + return overseasRequest('POST', `${OUTBOUND_BASE}/${encoded(taskNo)}/pallets/${encoded(palletNo)}/pick`, request) +} + export function outboundPickPayload(item, position, packages) { return { outboundPalletItemId: item.outboundPalletItemId, diff --git a/src/pages/outbound/outbound.vue b/src/pages/outbound/outbound.vue index 9032f8d..a455740 100644 --- a/src/pages/outbound/outbound.vue +++ b/src/pages/outbound/outbound.vue @@ -3,11 +3,11 @@ - 出库任务列表仅显示普通/一件代发出库任务;扫描排车业务号会转入“排车拣货” + 出库任务一件代发扫描托盘码可整托出库;扫描排车业务号会转入“排车拣货” - 任务号直接恢复;出库单号或排车单号先由服务端解析流程,不会作为出库任务号误查。 + 任务号直接恢复;进入任务后扫描 DP 托盘码,将一次拣取该托全部未拣箱件。 正在加载出库任务… @@ -49,7 +49,7 @@