diff --git a/backend/app/routers/ocr.py b/backend/app/routers/ocr.py index 98e80dd..07097bc 100644 --- a/backend/app/routers/ocr.py +++ b/backend/app/routers/ocr.py @@ -61,30 +61,30 @@ async def recognize_image( "Content-Type": "application/json" } - # 阿里云 DashScope API 格式 (qwen-vl-max 视觉模型) + # 阿里云 DashScope API 格式 (qwen-vl-plus 视觉模型) payload = { - "model": "qwen-vl-max", - "messages": [{ - "role": "user", - "content": [ - { - "type": "image_url", - "image_url": { - "url": f"data:image/jpeg;base64,{image_base64}" + "model": "qwen-vl-plus", + "input": { + "messages": [{ + "role": "user", + "content": [ + { + "image": f"data:{image.content_type};base64,{image_base64}" + }, + { + "text": PROFESSIONAL_PROMPT } - }, - { - "type": "text", - "text": PROFESSIONAL_PROMPT - } - ] - }], - "max_tokens": 1000 + ] + }] + }, + "parameters": { + "max_tokens": 1000 + } } async with httpx.AsyncClient(timeout=60.0) as client: response = await client.post( - "https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions", + "https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation", json=payload, headers=headers ) @@ -94,8 +94,13 @@ async def recognize_image( ocr_result = response.json() text_content = "" - if "choices" in ocr_result and len(ocr_result["choices"]) > 0: - text_content = ocr_result["choices"][0]["message"]["content"] + # 新版API返回格式 + if "output" in ocr_result and "choices" in ocr_result["output"]: + choices = ocr_result["output"]["choices"] + if choices and len(choices) > 0: + content = choices[0].get("message", {}).get("content", []) + if content and len(content) > 0: + text_content = content[0].get("text", "") fields = extract_fields(text_content) @@ -106,7 +111,9 @@ async def recognize_image( return {"success": True, "text": text_content, "fields": fields} except Exception as e: - raise HTTPException(status_code=500, detail=f"识别失败:{str(e)}") + import traceback + error_detail = f"识别失败:{str(e)}\n{traceback.format_exc()}" + raise HTTPException(status_code=500, detail=error_detail) def extract_fields(text: str) -> dict: diff --git a/config/VERSION b/config/VERSION index 7595223..56511a4 100644 --- a/config/VERSION +++ b/config/VERSION @@ -2,7 +2,7 @@ # Version Configuration for Zodiac Collection Management System # 当前版本号 (语义化版本:主版本。次版本.修订版) -VERSION=1.0.3 +VERSION=1.0.5 # 版本代号 (可选) VERSION_CODENAME="新生" diff --git a/frontend/index.html b/frontend/index.html index 0e01b5f..4abaa48 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -4,7 +4,7 @@ - 甲辰收藏 v1.0.1 + 甲辰收藏 v1.0.3-1773807001352 diff --git a/frontend/src/pages/Add.jsx b/frontend/src/pages/Add.jsx index ae69d05..e32812c 100644 --- a/frontend/src/pages/Add.jsx +++ b/frontend/src/pages/Add.jsx @@ -170,14 +170,38 @@ export default function Add() { const token = localStorage.getItem('token') const formData = new FormData() formData.append('image', selectedImage) + + // 使用 XMLHttpRequest 替代 fetch + const data = await new Promise((resolve, reject) => { + const xhr = new XMLHttpRequest() + xhr.open('POST', '/api/ocr/recognize') + xhr.setRequestHeader('Authorization', 'Bearer ' + token) + + xhr.onload = function() { + if (xhr.status >= 200 && xhr.status < 300) { + try { + const data = JSON.parse(xhr.responseText) + resolve(data) + } catch (e) { + reject(new Error('JSON解析失败: ' + xhr.responseText.substring(0, 100))) + } + } else { + try { + const data = JSON.parse(xhr.responseText) + reject(new Error(data.error?.message || data.detail || '识别失败')) + } catch (e) { + reject(new Error('请求失败: ' + xhr.status)) + } + } + } + + xhr.onerror = function() { + reject(new Error('网络错误')) + } + + xhr.send(formData) + }) try { - const res = await fetch('/api/ocr/recognize', { - method: 'POST', - headers: { 'Authorization': 'Bearer ' + token }, - body: formData - }) - const data = await res.json() - if (!res.ok) throw new Error(data.error?.message || '识别失败') if (data.fields) { const recognizedForm = { ...getDefaultForm() } // 直接对应 AI 返回的字段,不需要二次解析 diff --git a/frontend/src/pages/BatchMode.jsx b/frontend/src/pages/BatchMode.jsx index b9e6799..a8fcef7 100644 --- a/frontend/src/pages/BatchMode.jsx +++ b/frontend/src/pages/BatchMode.jsx @@ -85,7 +85,7 @@ export default function BatchMode() { const formData = new FormData() formData.append('image', file) - const res = await fetch('/api/ocr', { method: 'POST', body: formData }) + const res = await fetch('/api/ocr/recognize', { method: 'POST', body: formData }) const data = await res.json() const text = data.result || '' diff --git a/frontend/src/pages/OCR.jsx b/frontend/src/pages/OCR.jsx index bf1cd2e..0b1229f 100644 --- a/frontend/src/pages/OCR.jsx +++ b/frontend/src/pages/OCR.jsx @@ -229,21 +229,38 @@ export default function OCR() { const formData = new FormData() formData.append('image', selectedImage) - try { - const res = await fetch('/api/ocr/recognize', { - method: 'POST', - headers: { - 'Authorization': 'Bearer ' + token - }, - body: formData - }) + // 使用 XMLHttpRequest 替代 fetch,兼容性更好 + const data = await new Promise((resolve, reject) => { + const xhr = new XMLHttpRequest() + xhr.open('POST', '/api/ocr/recognize') + xhr.setRequestHeader('Authorization', 'Bearer ' + token) - const data = await res.json() - - if (!res.ok) { - throw new Error(data.error?.message || '识别失败') + xhr.onload = function() { + if (xhr.status >= 200 && xhr.status < 300) { + try { + const data = JSON.parse(xhr.responseText) + resolve(data) + } catch (e) { + reject(new Error('JSON解析失败: ' + xhr.responseText.substring(0, 100))) + } + } else { + try { + const data = JSON.parse(xhr.responseText) + reject(new Error(data.error?.message || data.detail || '识别失败')) + } catch (e) { + reject(new Error('请求失败: ' + xhr.status)) + } + } } + xhr.onerror = function() { + reject(new Error('网络错误')) + } + + xhr.send(formData) + }) + + try { // 自动填充识别结果 if (data.fields) { const recognizedForm = { ...getDefaultForm() } diff --git a/frontend/src/utils/api.js b/frontend/src/utils/api.js index 110a88a..5ae698f 100644 --- a/frontend/src/utils/api.js +++ b/frontend/src/utils/api.js @@ -203,9 +203,9 @@ export const api = { ocr: { recognize: (file) => { const formData = new FormData() - formData.append('file', file) + formData.append('image', file) - return request('/api/ocr', { + return request('/api/ocr/recognize', { method: 'POST', body: formData }) diff --git a/frontend/vite.config.js b/frontend/vite.config.js index e5d8607..ddfd244 100644 --- a/frontend/vite.config.js +++ b/frontend/vite.config.js @@ -16,7 +16,7 @@ function getVersion() { } } -const APP_VERSION = getVersion() +const APP_VERSION = getVersion() + '-' + Date.now() console.log(`📦 构建版本:v${APP_VERSION}`) // 构建时自动更新 index.html 的 title