From c0bd161fb7dd5346fc64b63a1bfe22329d5b244b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8F=9C=E9=B8=9F=E7=94=9F=E4=BA=A7?= Date: Wed, 18 Mar 2026 14:01:02 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=9B=BE=E7=89=87=E5=AD=98=E5=82=A8?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=E9=98=BF=E9=87=8C=E4=BA=91OSS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/routers/ocr.py | 144 +++++++++++++++++++++++++------------ config/VERSION | 2 +- frontend/index.html | 2 +- frontend/vite.config.js | 2 +- 4 files changed, 102 insertions(+), 48 deletions(-) diff --git a/backend/app/routers/ocr.py b/backend/app/routers/ocr.py index 8406ca5..6a9eb29 100644 --- a/backend/app/routers/ocr.py +++ b/backend/app/routers/ocr.py @@ -14,10 +14,36 @@ router = APIRouter(prefix="/api/ocr", tags=["OCR 识别"]) # 阿里云 DashScope API 配置 DASHSCOPE_API_KEY = os.getenv("DASHSCOPE_API_KEY", "sk-9389024a37da4f7bb455ac9a6b28776f") -# 上传目录配置 +# 阿里云 OSS 配置 +OSS_CONFIG = { + "access_key_id": os.getenv("OSS_ACCESS_KEY_ID", "LTAI5t6HUnpFBLEK9194kPVG"), + "access_key_secret": os.getenv("OSS_ACCESS_KEY_SECRET", "LEr4Q8yRxb8D5b24cKfCwlt4MMoke1"), + "bucket_name": "jiachenlong-oss", + "endpoint": "oss-cn-hangzhou.aliyuncs.com", + "public_url": "https://jiachenlong-oss.oss-cn-hangzhou.aliyuncs.com" +} + +# 临时上传目录(用于OCR识别) UPLOAD_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), "uploads", "temp") os.makedirs(UPLOAD_DIR, exist_ok=True) + +def upload_to_oss(file_data, filename, bucket_name=None): + """上传文件到阿里云OSS""" + import oss2 + bucket_name = bucket_name or OSS_CONFIG["bucket_name"] + + auth = oss2.Auth(OSS_CONFIG["access_key_id"], OSS_CONFIG["access_key_secret"]) + bucket = oss2.Bucket(auth, OSS_CONFIG["endpoint"], bucket_name) + + # 上传文件 + result = bucket.put_object(filename, file_data) + + if result.status == 200: + return f"{OSS_CONFIG['public_url']}/{filename}" + else: + raise Exception(f"OSS上传失败: {result.status}") + # 专业提示词 PROFESSIONAL_PROMPT = """你是一名专业的人民币生肖纪念钞鉴定专家。请严格按照以下步骤分析这张图片,并提取准确信息。 @@ -62,15 +88,21 @@ async def recognize_image( image_data = await image.read() image_base64 = base64.b64encode(image_data).decode('utf-8') - # 生成临时文件名(识别后保存到服务器) + # 生成临时文件名(识别后保存到OSS) temp_id = str(uuid.uuid4()) ext = image.filename.split('.')[-1] if '.' in image.filename else 'jpg' - temp_filename = f"{temp_id}.{ext}" - temp_path = os.path.join(UPLOAD_DIR, temp_filename) + oss_key = f"temp/{temp_id}.{ext}" - # 保存图片到临时目录 - with open(temp_path, 'wb') as f: - f.write(image_data) + # 上传到OSS + try: + image_url = upload_to_oss(image_data, oss_key) + except Exception as oss_err: + # OSS失败时保存到本地作为备选 + temp_filename = f"{temp_id}.{ext}" + temp_path = os.path.join(UPLOAD_DIR, temp_filename) + with open(temp_path, 'wb') as f: + f.write(image_data) + image_url = f"/uploads/temp/{temp_filename}" headers = { "Authorization": f"Bearer {DASHSCOPE_API_KEY}", @@ -130,9 +162,10 @@ async def recognize_image( "fields": fields, "temp_image": { "id": temp_id, - "filename": temp_filename, - "path": f"uploads/temp/{temp_filename}", - "original_name": image.filename + "filename": oss_key.split('/')[-1], + "path": image_url, + "original_name": image.filename, + "is_oss": image_url.startswith("https://") } } @@ -221,9 +254,8 @@ async def claim_temp_image( current_user: User = Depends(get_current_user), db: Session = Depends(get_db) ): - """将临时图片移动到藏品目录""" + """将临时图片移动到正式藏品目录""" from app.models.models import Collection, CollectionImage - import shutil # 验证藏品是否存在 collection = db.query(Collection).filter( @@ -234,49 +266,71 @@ async def claim_temp_image( if not collection: raise HTTPException(status_code=404, detail="藏品不存在") - # 临时文件路径 - temp_filename = f"{temp_id}.jpg" - temp_path = os.path.join(UPLOAD_DIR, temp_filename) - - # 检查临时文件是否存在 - if not os.path.exists(temp_path): - # 尝试其他扩展名 - for ext in ['jpeg', 'png', 'gif']: - temp_filename = f"{temp_id}.{ext}" - temp_path = os.path.join(UPLOAD_DIR, temp_filename) - if os.path.exists(temp_path): - break - - if not os.path.exists(temp_path): - raise HTTPException(status_code=404, detail="临时图片不存在或已过期") - - # 创建收藏品图片目录 - collection_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), "uploads", "collections") - os.makedirs(collection_dir, exist_ok=True) - - # 生成新文件名 + # 生成正式文件名 code = collection.f01_02_code or "0000" prefix = collection.f02_10_prefix_serial or "" username = current_user.f01_01_name - new_filename = f"{username}-{code}-{prefix}.jpg" - new_path = os.path.join(collection_dir, new_filename) + import time + final_filename = f"{username}-{code}-{prefix}-{int(time.time())}.jpg" + oss_key = f"collections/{final_filename}" - # 如果文件已存在,添加随机后缀 - if os.path.exists(new_path): - name, ext = os.path.splitext(new_filename) - new_filename = f"{name}-{str(uuid.uuid4())[:8]}{ext}" - new_path = os.path.join(collection_dir, new_filename) + # 尝试从OSS获取临时图片 - 尝试多种扩展名 + temp_extensions = ['jpg', 'jpeg', 'png', 'gif'] + temp_content = None + found_ext = None - # 移动文件 - shutil.move(temp_path, new_path) + for ext in temp_extensions: + try: + temp_oss_key = f"temp/{temp_id}.{ext}" + import oss2 + auth = oss2.Auth(OSS_CONFIG["access_key_id"], OSS_CONFIG["access_key_secret"]) + bucket = oss2.Bucket(auth, OSS_CONFIG["endpoint"], OSS_CONFIG["bucket_name"]) + temp_content = bucket.get_object(temp_oss_key).read() + found_ext = ext + break + except: + continue + + if temp_content: + # 上传到正式目录 + bucket.put_object(oss_key, temp_content) + + # 删除临时图片 + try: + bucket.delete_object(f"temp/{temp_id}.{found_ext}") + except: + pass + + # OSS URL + image_path = f"{OSS_CONFIG['public_url']}/{oss_key}" + + else: + # OSS失败,使用本地文件 + temp_path = None + for ext in temp_extensions: + temp_path = os.path.join(UPLOAD_DIR, f"{temp_id}.{ext}") + if os.path.exists(temp_path): + break + + if not temp_path or not os.path.exists(temp_path): + raise HTTPException(status_code=404, detail="临时图片不存在或已过期") + + # 保存到本地 + collection_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), "uploads", "collections") + os.makedirs(collection_dir, exist_ok=True) + + new_path = os.path.join(collection_dir, final_filename) + import shutil + shutil.move(temp_path, new_path) + image_path = f"uploads/collections/{final_filename}" # 创建图片记录 image_record = CollectionImage( id=str(uuid.uuid4()), collection_id=collection.f99_90_id, - filename=new_filename, - original_name=temp_filename, - path=f"uploads/collections/{new_filename}" + filename=final_filename, + original_name=temp_id, + path=image_path ) db.add(image_record) db.commit() diff --git a/config/VERSION b/config/VERSION index 1c6d152..e46e4c4 100644 --- a/config/VERSION +++ b/config/VERSION @@ -2,7 +2,7 @@ # Version Configuration for Zodiac Collection Management System # 当前版本号 (语义化版本:主版本。次版本.修订版) -VERSION=1.1.5 +VERSION=1.1.6 # 版本代号 (可选) VERSION_CODENAME="新生" diff --git a/frontend/index.html b/frontend/index.html index 4abaa48..0719e53 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -4,7 +4,7 @@ - 甲辰收藏 v1.0.3-1773807001352 + 甲辰收藏 v1.1.5 diff --git a/frontend/vite.config.js b/frontend/vite.config.js index ddfd244..e5d8607 100644 --- a/frontend/vite.config.js +++ b/frontend/vite.config.js @@ -16,7 +16,7 @@ function getVersion() { } } -const APP_VERSION = getVersion() + '-' + Date.now() +const APP_VERSION = getVersion() console.log(`📦 构建版本:v${APP_VERSION}`) // 构建时自动更新 index.html 的 title