优化
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { http } from '@/shared/api/http'
|
||||
import {
|
||||
confirmWeightResult,
|
||||
correctWeightResult,
|
||||
createWeightResult,
|
||||
downloadWeightImage,
|
||||
getWeightWorkspace,
|
||||
retryWeightRecognition,
|
||||
uploadWeightImage,
|
||||
} from './weightOcr'
|
||||
|
||||
vi.mock('@/shared/api/http', () => ({
|
||||
http: { get: vi.fn(), post: vi.fn(), put: vi.fn() },
|
||||
ApiResponseError: class ApiResponseError extends Error {},
|
||||
unwrapApiResponse: <T>(response: { data: T }) => response.data,
|
||||
}))
|
||||
|
||||
describe('inbound weight OCR API', () => {
|
||||
beforeEach(() => {
|
||||
vi.mocked(http.get).mockReset()
|
||||
vi.mocked(http.post).mockReset()
|
||||
vi.mocked(http.put).mockReset()
|
||||
})
|
||||
|
||||
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 })
|
||||
|
||||
await expect(getWeightWorkspace('10001', '9007199254740993')).resolves.toEqual(detail)
|
||||
expect(http.get).toHaveBeenCalledWith(
|
||||
'/platform/v1/first-leg/inbounds/9007199254740993/measurement',
|
||||
{ params: { tenantId: '10001' } },
|
||||
)
|
||||
})
|
||||
|
||||
it('uploads and retries against the file service without converting long IDs', async () => {
|
||||
const result = { fileId: '9007199254740995', recognitionStatus: 'SUCCESS' }
|
||||
vi.mocked(http.post).mockResolvedValue({ code: '200', message: 'success', data: result })
|
||||
const file = new File(['image'], 'weight.png', { type: 'image/png' })
|
||||
|
||||
await expect(uploadWeightImage('10001', '9007199254740993', file)).resolves.toEqual(result)
|
||||
const [uploadPath, body] = vi.mocked(http.post).mock.calls[0]!
|
||||
expect(uploadPath).toBe('/platform/v1/files/measurements/10001/9007199254740993')
|
||||
expect(body).toBeInstanceOf(FormData)
|
||||
expect((body as FormData).get('file')).toBe(file)
|
||||
|
||||
await retryWeightRecognition('10001', '9007199254740993', '9007199254740995')
|
||||
expect(http.post).toHaveBeenLastCalledWith(
|
||||
'/platform/v1/files/measurements/10001/9007199254740993/9007199254740995/recognize',
|
||||
)
|
||||
})
|
||||
|
||||
it('registers, corrects, and confirms a weight result using the implemented contracts', async () => {
|
||||
vi.mocked(http.post).mockResolvedValue({ code: '200', message: 'success', data: { id: '1' } })
|
||||
vi.mocked(http.put).mockResolvedValue({ code: '200', message: 'success', data: { id: '1', version: 1 } })
|
||||
|
||||
const createPayload = {
|
||||
fileId: '9007199254740995',
|
||||
ocrProvider: 'FILE_WEIGHT_OCR',
|
||||
ocrRawText: null,
|
||||
ocrWeightValue: 12.5,
|
||||
ocrWeightUnit: 'KG',
|
||||
confidence: 0.98,
|
||||
}
|
||||
await createWeightResult('10001', '20001', createPayload)
|
||||
expect(http.post).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
'/platform/v1/first-leg/inbounds/20001/weight-results',
|
||||
createPayload,
|
||||
{ params: { tenantId: '10001' } },
|
||||
)
|
||||
|
||||
const correction = { version: 0, adjustedWeightValue: 12.8, adjustedWeightUnit: 'KG' as const, adjustmentReason: '读数模糊' }
|
||||
await correctWeightResult('10001', '20001', '30001', correction)
|
||||
expect(http.put).toHaveBeenCalledWith(
|
||||
'/platform/v1/first-leg/inbounds/20001/weight-results/30001',
|
||||
correction,
|
||||
{ params: { tenantId: '10001' } },
|
||||
)
|
||||
|
||||
await confirmWeightResult('10001', '20001', '30001', { version: 1 })
|
||||
expect(http.post).toHaveBeenLastCalledWith(
|
||||
'/platform/v1/first-leg/inbounds/20001/weight-results/30001/confirm',
|
||||
{ version: 1 },
|
||||
{ params: { tenantId: '10001' } },
|
||||
)
|
||||
})
|
||||
|
||||
it('downloads an authenticated image as a blob', async () => {
|
||||
const image = new Blob(['png'], { type: 'image/png' })
|
||||
vi.mocked(http.get).mockResolvedValue(image)
|
||||
await expect(downloadWeightImage('10001', '20001', '40001')).resolves.toBe(image)
|
||||
expect(http.get).toHaveBeenCalledWith(
|
||||
'/platform/v1/files/measurements/10001/20001/40001',
|
||||
{ responseType: 'blob' },
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
import axios from 'axios'
|
||||
import { ApiResponseError, http, unwrapApiResponse, type ApiEnvelope } from '@/shared/api/http'
|
||||
import type {
|
||||
ConfirmWeightResultRequest,
|
||||
CorrectWeightResultRequest,
|
||||
CreateWeightResultRequest,
|
||||
WeightImageUploadResult,
|
||||
WeightResultResource,
|
||||
WeightWorkspaceResource,
|
||||
} from '../types'
|
||||
|
||||
const measurementBasePath = '/platform/v1/first-leg/inbounds'
|
||||
const imageBasePath = '/platform/v1/files/measurements'
|
||||
const maxErrorEnvelopeBytes = 1024 * 1024
|
||||
|
||||
function segment(value: string) {
|
||||
return encodeURIComponent(value)
|
||||
}
|
||||
|
||||
async function parseBlobError(blob: Blob, status?: number) {
|
||||
if (!blob.type.toLowerCase().includes('json')) return null
|
||||
if (blob.size > maxErrorEnvelopeBytes) {
|
||||
return new ApiResponseError('图片接口返回了超出限制的 JSON 响应', 'INVALID_IMAGE_RESPONSE', status)
|
||||
}
|
||||
try {
|
||||
const payload = JSON.parse(await blob.text()) as Partial<ApiEnvelope<unknown>>
|
||||
return new ApiResponseError(
|
||||
payload.code === '200' ? '图片接口未返回图片内容' : payload.message || '称重图片读取失败',
|
||||
payload.code === '200' ? 'INVALID_IMAGE_RESPONSE' : payload.code || (status ? `HTTP_${status}` : 'IMAGE_FAILED'),
|
||||
status,
|
||||
payload.data,
|
||||
payload.requestId,
|
||||
payload.traceId,
|
||||
)
|
||||
} catch {
|
||||
return new ApiResponseError('图片接口返回了无法识别的响应', 'INVALID_IMAGE_RESPONSE', status)
|
||||
}
|
||||
}
|
||||
|
||||
export async function getWeightWorkspace(tenantId: string, inboundId: string) {
|
||||
const response = await http.get<ApiEnvelope<WeightWorkspaceResource>, ApiEnvelope<WeightWorkspaceResource>>(
|
||||
`${measurementBasePath}/${segment(inboundId)}/measurement`,
|
||||
{ params: { tenantId } },
|
||||
)
|
||||
return unwrapApiResponse(response)
|
||||
}
|
||||
|
||||
export async function uploadWeightImage(tenantId: string, inboundId: string, file: File) {
|
||||
const body = new FormData()
|
||||
body.append('file', file)
|
||||
const response = await http.post<ApiEnvelope<WeightImageUploadResult>, ApiEnvelope<WeightImageUploadResult>>(
|
||||
`${imageBasePath}/${segment(tenantId)}/${segment(inboundId)}`,
|
||||
body,
|
||||
)
|
||||
return unwrapApiResponse(response)
|
||||
}
|
||||
|
||||
export async function retryWeightRecognition(tenantId: string, inboundId: string, fileId: string) {
|
||||
const response = await http.post<ApiEnvelope<WeightImageUploadResult>, ApiEnvelope<WeightImageUploadResult>>(
|
||||
`${imageBasePath}/${segment(tenantId)}/${segment(inboundId)}/${segment(fileId)}/recognize`,
|
||||
)
|
||||
return unwrapApiResponse(response)
|
||||
}
|
||||
|
||||
export async function downloadWeightImage(tenantId: string, inboundId: string, fileId: string) {
|
||||
try {
|
||||
const blob = await http.get<Blob, Blob>(
|
||||
`${imageBasePath}/${segment(tenantId)}/${segment(inboundId)}/${segment(fileId)}`,
|
||||
{ responseType: 'blob' },
|
||||
)
|
||||
const responseError = await parseBlobError(blob, 200)
|
||||
if (responseError) throw responseError
|
||||
return blob
|
||||
} catch (error) {
|
||||
if (axios.isAxiosError(error) && error.response?.data instanceof Blob) {
|
||||
const responseError = await parseBlobError(error.response.data, error.response.status)
|
||||
if (responseError) throw responseError
|
||||
}
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
export async function createWeightResult(
|
||||
tenantId: string,
|
||||
inboundId: string,
|
||||
payload: CreateWeightResultRequest,
|
||||
) {
|
||||
const response = await http.post<ApiEnvelope<WeightResultResource>, ApiEnvelope<WeightResultResource>>(
|
||||
`${measurementBasePath}/${segment(inboundId)}/weight-results`,
|
||||
payload,
|
||||
{ params: { tenantId } },
|
||||
)
|
||||
return unwrapApiResponse(response)
|
||||
}
|
||||
|
||||
export async function correctWeightResult(
|
||||
tenantId: string,
|
||||
inboundId: string,
|
||||
resultId: string,
|
||||
payload: CorrectWeightResultRequest,
|
||||
) {
|
||||
const response = await http.put<ApiEnvelope<WeightResultResource>, ApiEnvelope<WeightResultResource>>(
|
||||
`${measurementBasePath}/${segment(inboundId)}/weight-results/${segment(resultId)}`,
|
||||
payload,
|
||||
{ params: { tenantId } },
|
||||
)
|
||||
return unwrapApiResponse(response)
|
||||
}
|
||||
|
||||
export async function confirmWeightResult(
|
||||
tenantId: string,
|
||||
inboundId: string,
|
||||
resultId: string,
|
||||
payload: ConfirmWeightResultRequest,
|
||||
) {
|
||||
const response = await http.post<ApiEnvelope<WeightResultResource>, ApiEnvelope<WeightResultResource>>(
|
||||
`${measurementBasePath}/${segment(inboundId)}/weight-results/${segment(resultId)}/confirm`,
|
||||
payload,
|
||||
{ params: { tenantId } },
|
||||
)
|
||||
return unwrapApiResponse(response)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user