This commit is contained in:
2026-08-07 17:46:27 +08:00
parent 5fae0f6d9b
commit 54d6b9a8cc
7 changed files with 57 additions and 8 deletions
@@ -56,6 +56,8 @@ describe('CarrierRatesView price-book source contract', () => {
expect(source).toContain('sourceDestinationName')
expect(source).toContain('autoSelectExactWarehouseMappings')
expect(source).toContain("'DUPLICATE_RATE'")
expect(source).toContain('.channel-tabs { width: 100%; min-width: 0;')
expect(source).toContain('.channel-preview :deep(.el-table) { width: 100%; max-width: 100%; min-width: 0; }')
expect(source).not.toContain('tenantId')
expect(source).not.toContain('客户选择')
})
@@ -847,7 +847,8 @@ onMounted(() => {
.mapping-load-error { display: flex; align-items: center; justify-content: space-between; gap: 14px; padding: 14px; border: 1px solid color-mix(in srgb, var(--cc-danger) 24%, var(--cc-border)); border-radius: 8px; background: #fff; }
.mapping-load-error strong { color: var(--cc-danger); font-size: 13px; }
.mapping-load-error p { margin: 4px 0; color: var(--cc-text); font-size: 12px; }
.channel-tabs { margin-bottom: 0; }
.channel-tabs { width: 100%; min-width: 0; margin-bottom: 0; overflow: hidden; }
.channel-preview :deep(.el-table) { width: 100%; max-width: 100%; min-width: 0; }
.channel-meta { display: flex; flex-wrap: wrap; gap: 6px 14px; margin: -4px 0 8px; color: var(--cc-muted); font-size: 12px; }
@media (max-width: 720px) {
.table-actions :deep(.el-button) { min-height: 40px; }
@@ -5,7 +5,9 @@ import {
correctWeightResult,
createWeightResult,
downloadWeightImage,
getWeightOcrRawText,
getWeightWorkspace,
normalizeWeightConfidence,
retryWeightRecognition,
uploadWeightImage,
} from './weightOcr'
@@ -23,6 +25,31 @@ describe('inbound weight OCR API', () => {
vi.mocked(http.put).mockReset()
})
it('normalizes OCR confidence to the firstleg decimal contract', () => {
expect(normalizeWeightConfidence(0.5097191334)).toBe(0.50972)
expect(normalizeWeightConfidence(1.2)).toBe(1)
expect(normalizeWeightConfidence(-0.2)).toBe(0)
expect(normalizeWeightConfidence(Number.NaN)).toBeNull()
expect(normalizeWeightConfidence(null)).toBeNull()
})
it('accepts current and compatible OCR raw-text response fields', () => {
const upload = {
fileId: '1',
fileName: 'weight.jpg',
contentType: 'image/jpeg',
sizeBytes: 100,
recognitionStatus: 'SUCCESS',
weightValue: 12.5,
weightUnit: 'KG',
confidence: 0.98,
error: null,
}
expect(getWeightOcrRawText({ ...upload, ocrRawText: ' 12.5 KG ' })).toBe('12.5 KG')
expect(getWeightOcrRawText({ ...upload, rawText: 'display: 12.5' })).toBe('display: 12.5')
expect(getWeightOcrRawText(upload)).toBeNull()
})
it('loads the measurement workspace using the real firstleg path', async () => {
const detail = { inboundId: '9007199254740993', weightResults: [] }
vi.mocked(http.get).mockResolvedValue({ code: '200', message: 'success', data: detail })
@@ -61,13 +88,13 @@ describe('inbound weight OCR API', () => {
ocrRawText: null,
ocrWeightValue: 12.5,
ocrWeightUnit: 'KG',
confidence: 0.98,
confidence: 0.5097191334,
}
await createWeightResult('10001', '20001', createPayload)
expect(http.post).toHaveBeenNthCalledWith(
1,
'/platform/v1/first-leg/inbounds/20001/weight-results',
createPayload,
{ ...createPayload, confidence: 0.50972 },
{ params: { tenantId: '10001' } },
)
@@ -97,4 +124,3 @@ describe('inbound weight OCR API', () => {
)
})
})
+19 -2
View File
@@ -12,11 +12,25 @@ import type {
const measurementBasePath = '/platform/v1/first-leg/inbounds'
const imageBasePath = '/platform/v1/files/measurements'
const maxErrorEnvelopeBytes = 1024 * 1024
const confidenceScale = 100_000
function segment(value: string) {
return encodeURIComponent(value)
}
export function normalizeWeightConfidence(value: number | null) {
if (value == null || !Number.isFinite(value)) return null
const clamped = Math.min(1, Math.max(0, value))
return Math.round(clamped * confidenceScale) / confidenceScale
}
export function getWeightOcrRawText(result: WeightImageUploadResult) {
const rawText = result.ocrRawText ?? result.rawText
if (typeof rawText !== 'string') return null
const normalized = rawText.trim()
return normalized ? normalized.slice(0, 1000) : null
}
async function parseBlobError(blob: Blob, status?: number) {
if (!blob.type.toLowerCase().includes('json')) return null
if (blob.size > maxErrorEnvelopeBytes) {
@@ -85,9 +99,13 @@ export async function createWeightResult(
inboundId: string,
payload: CreateWeightResultRequest,
) {
const normalizedPayload = {
...payload,
confidence: normalizeWeightConfidence(payload.confidence),
}
const response = await http.post<ApiEnvelope<WeightResultResource>, ApiEnvelope<WeightResultResource>>(
`${measurementBasePath}/${segment(inboundId)}/weight-results`,
payload,
normalizedPayload,
{ params: { tenantId } },
)
return unwrapApiResponse(response)
@@ -120,4 +138,3 @@ export async function confirmWeightResult(
)
return unwrapApiResponse(response)
}
@@ -29,6 +29,7 @@ describe('WeightOcrPanel contract', () => {
expect(source).toContain("['image/jpeg', 'image/png', 'image/bmp']")
expect(source).toContain('5 * 1024 * 1024')
expect(source).toContain('图片服务未返回 OCR 原文')
expect(source).toContain('ocrRawText: getWeightOcrRawText(upload)')
expect(source).toContain("adjustmentReason: 'OCR 重试识别结果'")
})
@@ -20,6 +20,7 @@ import {
correctWeightResult,
createWeightResult,
downloadWeightImage,
getWeightOcrRawText,
getWeightWorkspace,
retryWeightRecognition,
uploadWeightImage,
@@ -153,7 +154,7 @@ async function handleFileChange(event: Event) {
await createWeightResult(customerId.value, props.inboundId, {
fileId: String(upload.fileId),
ocrProvider: 'FILE_WEIGHT_OCR',
ocrRawText: null,
ocrRawText: getWeightOcrRawText(upload),
ocrWeightValue: upload.weightValue,
ocrWeightUnit: upload.weightUnit,
confidence: upload.confidence,
+2 -1
View File
@@ -37,6 +37,8 @@ export interface WeightImageUploadResult {
weightValue: number | null
weightUnit: string | null
confidence: number | null
ocrRawText?: string | null
rawText?: string | null
error: string | null
}
@@ -59,4 +61,3 @@ export interface CorrectWeightResultRequest {
export interface ConfirmWeightResultRequest {
version: number
}