修改:jingdong的识别主体接口替换
continuous-integration/drone/push Build was killed
continuous-integration/drone/promote/prod Build was killed

This commit is contained in:
Linzm committed 2026-11-04 18:37:18 +08:00
1 parent b25e3ca473
commit 4979739521
4 files changed
+173 -123

No files matched your search

+16 -73
View File
@@ -1,7 +1,5 @@
/**
* 原图分类 /classify(对齐 agent image-inspect)
* - 本地:Vite 代理 `/concept-classify` → `/v1/classify`
* - 线上:直连 seetacloud
* 图片主体识别结果(由后端 jd/subject/classify 返回后规范化)
*/
export type ImageKind = 'cartoon' | 'real_photo' | 'other' | 'uncertain'
@@ -13,6 +11,8 @@ export interface ImageInspect {
petCount: number
reviewStatus: string
conclusion: string
/** 主体识别 check_id,后续下单可用 */
checkId: number
}
export class InspectCancelled extends Error {
@@ -22,10 +22,6 @@ export class InspectCancelled extends Error {
}
}
const INSPECT_TIMEOUT_MS = 120_000
const DEFAULT_INSPECT_HOST = 'https://u252424-8570-01f615a5.cqa1.seetacloud.com:8443'
const DEFAULT_INSPECT_REMOTE_URL = `${DEFAULT_INSPECT_HOST}/v1/classify`
function asRecord(value: unknown) {
return value && typeof value === 'object' && !Array.isArray(value)
? (value as Record<string, unknown>)
@@ -42,24 +38,6 @@ export function mapInspectImageType(raw: unknown): ImageKind {
return 'uncertain'
}
function resolveInspectUrl() {
const fromEnv = String(
(import.meta as any).env?.VITE_CONCEPT_INSPECT_URL || '',
)
.trim()
.replace(/\/+$/, '')
if (fromEnv) {
if (/\/v1\/classify$/i.test(fromEnv)) return fromEnv
return `${fromEnv}/v1/classify`
}
if ((import.meta as any).env?.DEV) return '/concept-classify'
return DEFAULT_INSPECT_REMOTE_URL
}
function resolveInspectToken() {
return String((import.meta as any).env?.VITE_CONCEPT_INSPECT_TOKEN || '').trim()
}
function countField(data: Record<string, unknown>, keys: string[]) {
for (const key of keys) {
const value = Number(data[key])
@@ -68,9 +46,19 @@ function countField(data: Record<string, unknown>, keys: string[]) {
return 0
}
function normalizeInspect(body: unknown): ImageInspect {
/** 规范化 classify 接口 data(拦截器已解包 code=1000 的 data) */
export function normalizeInspect(body: unknown): ImageInspect {
const root = asRecord(body) || {}
const data = asRecord(root.data) || root
const nested = asRecord(root.data)
// 兼容完整响应 { code, data } / 已解包的 data 本体
const data =
nested &&
(nested.image_type != null ||
nested.person_count != null ||
nested.is_cartoon != null ||
nested.check_id != null)
? nested
: root
const imageType = mapInspectImageType(data.image_type ?? data.imageType)
const isCartoonRaw = data.is_cartoon ?? data.isCartoon
const isCartoon =
@@ -87,6 +75,7 @@ function normalizeInspect(body: unknown): ImageInspect {
petCount: countField(data, ['pet_count', 'petCount']),
reviewStatus: String(data.review_status || ''),
conclusion: String(data.conclusion || ''),
checkId: countField(data, ['check_id', 'checkId']),
}
}
@@ -104,49 +93,3 @@ export function formatSubjectFromInspect(result: ImageInspect) {
if (result.petCount > 0) parts.push(`${result.petCount}宠物`)
return parts.join('+')
}
export async function inspectImage(file: File, signal?: AbortSignal) {
if (signal?.aborted) throw new InspectCancelled()
const url = resolveInspectUrl()
const token = resolveInspectToken()
const form = new FormData()
form.append('image', file, file.name || 'image.jpg')
const headers: Record<string, string> = {}
if (token) headers.Authorization = `Bearer ${token}`
const controller = new AbortController()
let timedOut = false
const timer = window.setTimeout(() => {
timedOut = true
controller.abort()
}, INSPECT_TIMEOUT_MS)
const onAbort = () => controller.abort()
signal?.addEventListener('abort', onAbort, { once: true })
try {
const response = await fetch(url, {
method: 'POST',
body: form,
headers,
signal: controller.signal,
})
if (!response.ok) {
if (response.status === 400) throw new Error('图片上传错误,请检查文件后重试')
if (response.status === 401) throw new Error('图片识别鉴权失败')
if (response.status === 413) throw new Error('图片过大,请压缩后重试')
throw new Error(`图片识别失败(${response.status})`)
}
const raw: unknown = await response.json()
return normalizeInspect(raw)
} catch (error) {
if (error instanceof DOMException && error.name === 'AbortError') {
if (timedOut) throw new Error('图片识别超时,请稍后重试')
throw new InspectCancelled()
}
if (error instanceof TypeError) throw new Error('图片识别失败,请稍后重试')
throw error instanceof Error ? error : new Error('图片识别失败')
} finally {
window.clearTimeout(timer)
signal?.removeEventListener('abort', onAbort)
}
}