|
@@ -14,6 +14,7 @@ from io import BytesIO
|
|
import pytesseract
|
|
import pytesseract
|
|
import base64
|
|
import base64
|
|
from openai import OpenAI
|
|
from openai import OpenAI
|
|
|
|
+from app.config.config import DevelopmentConfig, ProductionConfig
|
|
|
|
|
|
|
|
|
|
# 测试用的解析数据接口。没有实际使用。
|
|
# 测试用的解析数据接口。没有实际使用。
|
|
@@ -29,6 +30,7 @@ def parse_data(data: Dict[str, Any]) -> Dict[str, Any]:
|
|
"""
|
|
"""
|
|
# TODO: 实现数据解析逻辑
|
|
# TODO: 实现数据解析逻辑
|
|
return {
|
|
return {
|
|
|
|
+ 'code': 200,
|
|
'status': 'success',
|
|
'status': 'success',
|
|
'message': 'Data parsed successfully',
|
|
'message': 'Data parsed successfully',
|
|
'data': data
|
|
'data': data
|
|
@@ -57,6 +59,8 @@ class BusinessCard(db.Model):
|
|
affiliation_zh = db.Column(db.String(200))
|
|
affiliation_zh = db.Column(db.String(200))
|
|
affiliation_en = db.Column(db.String(200))
|
|
affiliation_en = db.Column(db.String(200))
|
|
image_path = db.Column(db.String(255)) # MinIO中存储的路径
|
|
image_path = db.Column(db.String(255)) # MinIO中存储的路径
|
|
|
|
+ career_path = db.Column(db.JSON) # 职业轨迹,JSON格式
|
|
|
|
+ brand_group = db.Column(db.String(200)) # 品牌组合
|
|
created_at = db.Column(db.DateTime, default=datetime.now, nullable=False)
|
|
created_at = db.Column(db.DateTime, default=datetime.now, nullable=False)
|
|
updated_at = db.Column(db.DateTime, onupdate=datetime.now)
|
|
updated_at = db.Column(db.DateTime, onupdate=datetime.now)
|
|
updated_by = db.Column(db.String(50))
|
|
updated_by = db.Column(db.String(50))
|
|
@@ -83,6 +87,8 @@ class BusinessCard(db.Model):
|
|
'affiliation_zh': self.affiliation_zh,
|
|
'affiliation_zh': self.affiliation_zh,
|
|
'affiliation_en': self.affiliation_en,
|
|
'affiliation_en': self.affiliation_en,
|
|
'image_path': self.image_path,
|
|
'image_path': self.image_path,
|
|
|
|
+ 'career_path': self.career_path,
|
|
|
|
+ 'brand_group': self.brand_group,
|
|
'created_at': self.created_at.strftime('%Y-%m-%d %H:%M:%S') if self.created_at else None,
|
|
'created_at': self.created_at.strftime('%Y-%m-%d %H:%M:%S') if self.created_at else None,
|
|
'updated_at': self.updated_at.strftime('%Y-%m-%d %H:%M:%S') if self.updated_at else None,
|
|
'updated_at': self.updated_at.strftime('%Y-%m-%d %H:%M:%S') if self.updated_at else None,
|
|
'updated_by': self.updated_by,
|
|
'updated_by': self.updated_by,
|
|
@@ -92,13 +98,6 @@ class BusinessCard(db.Model):
|
|
|
|
|
|
# 名片解析功能模块
|
|
# 名片解析功能模块
|
|
|
|
|
|
-# MinIO配置
|
|
|
|
-MINIO_URL = os.environ.get('MINIO_URL', 'localhost:19000')
|
|
|
|
-MINIO_ACCESS_KEY = os.environ.get('MINIO_ACCESS_KEY', 'miniomxl')
|
|
|
|
-MINIO_SECRET_KEY = os.environ.get('MINIO_SECRET_KEY', 'minio2357!')
|
|
|
|
-MINIO_BUCKET = os.environ.get('MINIO_BUCKET', 'business-cards')
|
|
|
|
-USE_SSL = os.environ.get('MINIO_USE_SSL', 'false').lower() == 'true'
|
|
|
|
-
|
|
|
|
# DeepSeek API配置
|
|
# DeepSeek API配置
|
|
DEEPSEEK_API_KEY = os.environ.get('DEEPSEEK_API_KEY', 'sk-2aea6e8b159b448aa3c1e29acd6f4349')
|
|
DEEPSEEK_API_KEY = os.environ.get('DEEPSEEK_API_KEY', 'sk-2aea6e8b159b448aa3c1e29acd6f4349')
|
|
DEEPSEEK_API_URL = os.environ.get('DEEPSEEK_API_URL', 'https://api.deepseek.com/v1/chat/completions')
|
|
DEEPSEEK_API_URL = os.environ.get('DEEPSEEK_API_URL', 'https://api.deepseek.com/v1/chat/completions')
|
|
@@ -111,21 +110,53 @@ DEEPSEEK_API_URL_BACKUP = 'https://api.deepseek.com/v1/completions'
|
|
# OCR语言设置,支持多语言
|
|
# OCR语言设置,支持多语言
|
|
OCR_LANG = os.environ.get('OCR_LANG', 'chi_sim+eng')
|
|
OCR_LANG = os.environ.get('OCR_LANG', 'chi_sim+eng')
|
|
|
|
|
|
|
|
+
|
|
|
|
+# 根据环境选择配置
|
|
|
|
+"""
|
|
|
|
+if os.environ.get('FLASK_ENV') == 'production':
|
|
|
|
+ config = ProductionConfig()
|
|
|
|
+else:
|
|
|
|
+ config = DevelopmentConfig()
|
|
|
|
+"""
|
|
|
|
+
|
|
|
|
+# 使用配置变量,缺省认为在生产环境运行
|
|
|
|
+config = ProductionConfig()
|
|
|
|
+# 使用配置变量
|
|
|
|
+minio_url = f"{'https' if config.MINIO_SECURE else 'http'}://{config.MINIO_HOST}"
|
|
|
|
+minio_access_key = config.MINIO_USER
|
|
|
|
+minio_secret_key = config.MINIO_PASSWORD
|
|
|
|
+minio_bucket = config.MINIO_BUCKET
|
|
|
|
+use_ssl = config.MINIO_SECURE
|
|
|
|
+
|
|
def get_minio_client():
|
|
def get_minio_client():
|
|
"""获取MinIO客户端连接"""
|
|
"""获取MinIO客户端连接"""
|
|
try:
|
|
try:
|
|
|
|
+ # 使用全局配置变量
|
|
|
|
+ global minio_url, minio_access_key, minio_secret_key, minio_bucket, use_ssl
|
|
|
|
+
|
|
|
|
+ logging.info(f"尝试连接MinIO服务器: {minio_url}")
|
|
|
|
+
|
|
minio_client = boto3.client(
|
|
minio_client = boto3.client(
|
|
's3',
|
|
's3',
|
|
- endpoint_url=f'{"https" if USE_SSL else "http"}://{MINIO_URL}',
|
|
|
|
- aws_access_key_id=MINIO_ACCESS_KEY,
|
|
|
|
- aws_secret_access_key=MINIO_SECRET_KEY,
|
|
|
|
- config=Config(signature_version='s3v4'),
|
|
|
|
- region_name='us-east-1' # 可选,但某些S3客户端可能需要
|
|
|
|
|
|
+ endpoint_url=minio_url,
|
|
|
|
+ aws_access_key_id=minio_access_key,
|
|
|
|
+ aws_secret_access_key=minio_secret_key,
|
|
|
|
+ config=Config(
|
|
|
|
+ signature_version='s3v4',
|
|
|
|
+ retries={'max_attempts': 3, 'mode': 'standard'},
|
|
|
|
+ connect_timeout=10,
|
|
|
|
+ read_timeout=30
|
|
|
|
+ )
|
|
)
|
|
)
|
|
|
|
|
|
# 确保存储桶存在
|
|
# 确保存储桶存在
|
|
- if MINIO_BUCKET not in [bucket['Name'] for bucket in minio_client.list_buckets()['Buckets']]:
|
|
|
|
- minio_client.create_bucket(Bucket=MINIO_BUCKET)
|
|
|
|
|
|
+ buckets = minio_client.list_buckets()
|
|
|
|
+ bucket_names = [bucket['Name'] for bucket in buckets.get('Buckets', [])]
|
|
|
|
+ logging.info(f"成功连接到MinIO服务器,现有存储桶: {bucket_names}")
|
|
|
|
+
|
|
|
|
+ if minio_bucket not in bucket_names:
|
|
|
|
+ logging.info(f"创建存储桶: {minio_bucket}")
|
|
|
|
+ minio_client.create_bucket(Bucket=minio_bucket)
|
|
|
|
|
|
return minio_client
|
|
return minio_client
|
|
except Exception as e:
|
|
except Exception as e:
|
|
@@ -229,6 +260,8 @@ def parse_text_with_deepseek(text):
|
|
- email: 电子邮箱
|
|
- email: 电子邮箱
|
|
- address_zh: 中文地址
|
|
- address_zh: 中文地址
|
|
- address_en: 英文地址
|
|
- address_en: 英文地址
|
|
|
|
+- brand_group: 品牌组合(如有多个品牌,以逗号分隔)
|
|
|
|
+- career_path: 职业轨迹(如果能从文本中推断出,以JSON数组格式返回,包含公司名称和职位)
|
|
|
|
|
|
名片文本:
|
|
名片文本:
|
|
{text}
|
|
{text}
|
|
@@ -271,10 +304,10 @@ def parse_text_with_deepseek(text):
|
|
extracted_data = extract_fields_from_text(content)
|
|
extracted_data = extract_fields_from_text(content)
|
|
|
|
|
|
# 确保所有必要的字段都存在
|
|
# 确保所有必要的字段都存在
|
|
- required_fields = ['name', 'title', 'company', 'phone', 'email', 'address']
|
|
|
|
|
|
+ required_fields = ['name', 'title', 'company', 'phone', 'email', 'address', 'brand_group', 'career_path']
|
|
for field in required_fields:
|
|
for field in required_fields:
|
|
if field not in extracted_data:
|
|
if field not in extracted_data:
|
|
- extracted_data[field] = ""
|
|
|
|
|
|
+ extracted_data[field] = "" if field != 'career_path' else []
|
|
|
|
|
|
logging.info(f"成功从DeepSeek API获取解析结果")
|
|
logging.info(f"成功从DeepSeek API获取解析结果")
|
|
return extracted_data
|
|
return extracted_data
|
|
@@ -464,23 +497,52 @@ def parse_text_with_qwen25VLplus(image_data):
|
|
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
|
|
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
|
|
)
|
|
)
|
|
|
|
|
|
- # 构建提示语
|
|
|
|
- prompt = """请分析这张名片,提取以下信息,需分别识别中英文内容:
|
|
|
|
|
|
+ # 构建优化后的提示语
|
|
|
|
+ prompt = """你是专业的名片信息提取助手。请仔细分析图片中的名片,精确提取以下信息:
|
|
|
|
+
|
|
|
|
+## 提取要求
|
|
|
|
+- 区分中英文内容,分别提取
|
|
|
|
+- 保持提取信息的原始格式(如大小写、标点)
|
|
|
|
+- 对于无法识别或名片中不存在的信息,返回空字符串
|
|
|
|
+
|
|
|
|
+## 需提取的字段
|
|
1. 中文姓名 (name_zh)
|
|
1. 中文姓名 (name_zh)
|
|
2. 英文姓名 (name_en)
|
|
2. 英文姓名 (name_en)
|
|
3. 中文职位/头衔 (title_zh)
|
|
3. 中文职位/头衔 (title_zh)
|
|
4. 英文职位/头衔 (title_en)
|
|
4. 英文职位/头衔 (title_en)
|
|
5. 中文酒店/公司名称 (hotel_zh)
|
|
5. 中文酒店/公司名称 (hotel_zh)
|
|
6. 英文酒店/公司名称 (hotel_en)
|
|
6. 英文酒店/公司名称 (hotel_en)
|
|
-7. 手机号码 (mobile)
|
|
|
|
-8. 固定电话 (phone)
|
|
|
|
|
|
+7. 手机号码 (mobile) - 如有多个,使用逗号分隔
|
|
|
|
+8. 固定电话 (phone) - 如有多个,使用逗号分隔
|
|
9. 电子邮箱 (email)
|
|
9. 电子邮箱 (email)
|
|
10. 中文地址 (address_zh)
|
|
10. 中文地址 (address_zh)
|
|
11. 英文地址 (address_en)
|
|
11. 英文地址 (address_en)
|
|
12. 中文邮政编码 (postal_code_zh)
|
|
12. 中文邮政编码 (postal_code_zh)
|
|
13. 英文邮政编码 (postal_code_en)
|
|
13. 英文邮政编码 (postal_code_en)
|
|
|
|
+14. 品牌组合 (brand_group) - 如有多个品牌,使用逗号分隔
|
|
|
|
+15. 职业轨迹 (career_path) - 如能从名片中推断,以JSON数组格式返回
|
|
|
|
|
|
-请以JSON格式返回结果,不要有多余的文字说明。每个字段如果名片中没有相应信息,返回空字符串。"""
|
|
|
|
|
|
+## 输出格式
|
|
|
|
+请以严格的JSON格式返回结果,不要添加任何额外解释文字。JSON格式如下:
|
|
|
|
+```json
|
|
|
|
+{
|
|
|
|
+ "name_zh": "",
|
|
|
|
+ "name_en": "",
|
|
|
|
+ "title_zh": "",
|
|
|
|
+ "title_en": "",
|
|
|
|
+ "hotel_zh": "",
|
|
|
|
+ "hotel_en": "",
|
|
|
|
+ "mobile": "",
|
|
|
|
+ "phone": "",
|
|
|
|
+ "email": "",
|
|
|
|
+ "address_zh": "",
|
|
|
|
+ "address_en": "",
|
|
|
|
+ "postal_code_zh": "",
|
|
|
|
+ "postal_code_en": "",
|
|
|
|
+ "brand_group": "",
|
|
|
|
+ "career_path": []
|
|
|
|
+}
|
|
|
|
+```"""
|
|
|
|
|
|
# 调用 Qwen 2.5 VL Plus API
|
|
# 调用 Qwen 2.5 VL Plus API
|
|
logging.info("发送请求到 Qwen 2.5 VL Plus 模型")
|
|
logging.info("发送请求到 Qwen 2.5 VL Plus 模型")
|
|
@@ -494,12 +556,14 @@ def parse_text_with_qwen25VLplus(image_data):
|
|
{"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{base64_image}"}}
|
|
{"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{base64_image}"}}
|
|
]
|
|
]
|
|
}
|
|
}
|
|
- ]
|
|
|
|
|
|
+ ],
|
|
|
|
+ temperature=0.1, # 降低温度增加精确性
|
|
|
|
+ response_format={"type": "json_object"} # 要求输出JSON格式
|
|
)
|
|
)
|
|
|
|
|
|
# 解析响应
|
|
# 解析响应
|
|
response_content = completion.choices[0].message.content
|
|
response_content = completion.choices[0].message.content
|
|
- logging.info(f"成功从 Qwen 模型获取响应: {response_content[:100]}...")
|
|
|
|
|
|
+ logging.info(f"成功从 Qwen 模型获取响应: {response_content}")
|
|
|
|
|
|
# 尝试从响应中提取 JSON
|
|
# 尝试从响应中提取 JSON
|
|
try:
|
|
try:
|
|
@@ -515,12 +579,12 @@ def parse_text_with_qwen25VLplus(image_data):
|
|
'name_zh', 'name_en', 'title_zh', 'title_en',
|
|
'name_zh', 'name_en', 'title_zh', 'title_en',
|
|
'hotel_zh', 'hotel_en', 'mobile', 'phone',
|
|
'hotel_zh', 'hotel_en', 'mobile', 'phone',
|
|
'email', 'address_zh', 'address_en',
|
|
'email', 'address_zh', 'address_en',
|
|
- 'postal_code_zh', 'postal_code_en'
|
|
|
|
|
|
+ 'postal_code_zh', 'postal_code_en', 'brand_group', 'career_path'
|
|
]
|
|
]
|
|
|
|
|
|
for field in required_fields:
|
|
for field in required_fields:
|
|
if field not in extracted_data:
|
|
if field not in extracted_data:
|
|
- extracted_data[field] = ""
|
|
|
|
|
|
+ extracted_data[field] = [] if field == 'career_path' else ""
|
|
|
|
|
|
return extracted_data
|
|
return extracted_data
|
|
|
|
|
|
@@ -557,6 +621,7 @@ def process_business_card(image_file):
|
|
# extracted_data = extract_text_from_image(image_data)
|
|
# extracted_data = extract_text_from_image(image_data)
|
|
except Exception as e:
|
|
except Exception as e:
|
|
return {
|
|
return {
|
|
|
|
+ 'code': 500,
|
|
'success': False,
|
|
'success': False,
|
|
'message': f"名片解析失败: {str(e)}",
|
|
'message': f"名片解析失败: {str(e)}",
|
|
'data': None
|
|
'data': None
|
|
@@ -574,17 +639,20 @@ def process_business_card(image_file):
|
|
# 尝试上传到MinIO
|
|
# 尝试上传到MinIO
|
|
minio_client = get_minio_client()
|
|
minio_client = get_minio_client()
|
|
if minio_client:
|
|
if minio_client:
|
|
- minio_client.put_object(
|
|
|
|
- Bucket=MINIO_BUCKET,
|
|
|
|
- Key=minio_path,
|
|
|
|
- Body=image_file,
|
|
|
|
- ContentType=image_file.content_type
|
|
|
|
- )
|
|
|
|
- logging.info(f"图片已上传到MinIO: {minio_path}")
|
|
|
|
-
|
|
|
|
- # 只存储相对路径,不存储完整URL
|
|
|
|
- # 完整URL会由前端通过API代理访问
|
|
|
|
- logging.info(f"存储图片路径: {minio_path}")
|
|
|
|
|
|
+ try:
|
|
|
|
+ # 上传文件
|
|
|
|
+ logging.info(f"上传文件到MinIO: {minio_path}")
|
|
|
|
+ minio_client.put_object(
|
|
|
|
+ Bucket=minio_bucket,
|
|
|
|
+ Key=minio_path,
|
|
|
|
+ Body=image_file,
|
|
|
|
+ ContentType=image_file.content_type
|
|
|
|
+ )
|
|
|
|
+ logging.info(f"图片已上传到MinIO: {minio_path}")
|
|
|
|
+ except Exception as upload_err:
|
|
|
|
+ logging.error(f"上传文件到MinIO时出错: {str(upload_err)}")
|
|
|
|
+ # 即使上传失败,仍继续处理,但路径为None
|
|
|
|
+ minio_path = None
|
|
else:
|
|
else:
|
|
minio_path = None
|
|
minio_path = None
|
|
logging.warning("MinIO客户端未初始化,图片未上传")
|
|
logging.warning("MinIO客户端未初始化,图片未上传")
|
|
@@ -613,6 +681,8 @@ def process_business_card(image_file):
|
|
affiliation_zh=extracted_data.get('affiliation_zh', ''),
|
|
affiliation_zh=extracted_data.get('affiliation_zh', ''),
|
|
affiliation_en=extracted_data.get('affiliation_en', ''),
|
|
affiliation_en=extracted_data.get('affiliation_en', ''),
|
|
image_path=minio_path, # 存储相对路径
|
|
image_path=minio_path, # 存储相对路径
|
|
|
|
+ career_path=extracted_data.get('career_path', []), # 添加职业轨迹
|
|
|
|
+ brand_group=extracted_data.get('brand_group', ''), # 添加品牌组合
|
|
status='active',
|
|
status='active',
|
|
updated_by='system'
|
|
updated_by='system'
|
|
)
|
|
)
|
|
@@ -623,6 +693,7 @@ def process_business_card(image_file):
|
|
logging.info(f"名片信息已保存到数据库,ID: {business_card.id}")
|
|
logging.info(f"名片信息已保存到数据库,ID: {business_card.id}")
|
|
|
|
|
|
return {
|
|
return {
|
|
|
|
+ 'code': 200,
|
|
'success': True,
|
|
'success': True,
|
|
'message': '名片解析成功',
|
|
'message': '名片解析成功',
|
|
'data': business_card.to_dict()
|
|
'data': business_card.to_dict()
|
|
@@ -634,6 +705,7 @@ def process_business_card(image_file):
|
|
|
|
|
|
# 即使数据库操作失败,仍返回提取的信息
|
|
# 即使数据库操作失败,仍返回提取的信息
|
|
return {
|
|
return {
|
|
|
|
+ 'code': 500,
|
|
'success': False,
|
|
'success': False,
|
|
'message': error_msg,
|
|
'message': error_msg,
|
|
'data': {
|
|
'data': {
|
|
@@ -656,6 +728,8 @@ def process_business_card(image_file):
|
|
'affiliation_zh': extracted_data.get('affiliation_zh', ''),
|
|
'affiliation_zh': extracted_data.get('affiliation_zh', ''),
|
|
'affiliation_en': extracted_data.get('affiliation_en', ''),
|
|
'affiliation_en': extracted_data.get('affiliation_en', ''),
|
|
'image_path': minio_path, # 返回相对路径
|
|
'image_path': minio_path, # 返回相对路径
|
|
|
|
+ 'career_path': extracted_data.get('career_path', []), # 添加职业轨迹
|
|
|
|
+ 'brand_group': extracted_data.get('brand_group', ''), # 添加品牌组合
|
|
'created_at': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
|
|
'created_at': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
|
|
'updated_at': None,
|
|
'updated_at': None,
|
|
'updated_by': 'system',
|
|
'updated_by': 'system',
|
|
@@ -669,6 +743,7 @@ def process_business_card(image_file):
|
|
logging.error(error_msg, exc_info=True)
|
|
logging.error(error_msg, exc_info=True)
|
|
|
|
|
|
return {
|
|
return {
|
|
|
|
+ 'code': 500,
|
|
'success': False,
|
|
'success': False,
|
|
'message': error_msg,
|
|
'message': error_msg,
|
|
'data': None
|
|
'data': None
|
|
@@ -691,6 +766,7 @@ def update_business_card(card_id, data):
|
|
|
|
|
|
if not card:
|
|
if not card:
|
|
return {
|
|
return {
|
|
|
|
+ 'code': 500,
|
|
'success': False,
|
|
'success': False,
|
|
'message': f'未找到ID为{card_id}的名片记录',
|
|
'message': f'未找到ID为{card_id}的名片记录',
|
|
'data': None
|
|
'data': None
|
|
@@ -714,12 +790,15 @@ def update_business_card(card_id, data):
|
|
card.brand_en = data.get('brand_en', card.brand_en)
|
|
card.brand_en = data.get('brand_en', card.brand_en)
|
|
card.affiliation_zh = data.get('affiliation_zh', card.affiliation_zh)
|
|
card.affiliation_zh = data.get('affiliation_zh', card.affiliation_zh)
|
|
card.affiliation_en = data.get('affiliation_en', card.affiliation_en)
|
|
card.affiliation_en = data.get('affiliation_en', card.affiliation_en)
|
|
|
|
+ card.career_path = data.get('career_path', card.career_path) # 更新职业轨迹
|
|
|
|
+ card.brand_group = data.get('brand_group', card.brand_group) # 更新品牌组合
|
|
card.updated_by = data.get('updated_by', 'user') # 可以根据实际情况修改为当前用户
|
|
card.updated_by = data.get('updated_by', 'user') # 可以根据实际情况修改为当前用户
|
|
|
|
|
|
# 保存更新
|
|
# 保存更新
|
|
db.session.commit()
|
|
db.session.commit()
|
|
|
|
|
|
return {
|
|
return {
|
|
|
|
+ 'code': 200,
|
|
'success': True,
|
|
'success': True,
|
|
'message': '名片信息已更新',
|
|
'message': '名片信息已更新',
|
|
'data': card.to_dict()
|
|
'data': card.to_dict()
|
|
@@ -731,6 +810,7 @@ def update_business_card(card_id, data):
|
|
logging.error(error_msg, exc_info=True)
|
|
logging.error(error_msg, exc_info=True)
|
|
|
|
|
|
return {
|
|
return {
|
|
|
|
+ 'code': 500,
|
|
'success': False,
|
|
'success': False,
|
|
'message': error_msg,
|
|
'message': error_msg,
|
|
'data': None
|
|
'data': None
|
|
@@ -751,6 +831,7 @@ def get_business_cards():
|
|
cards_data = [card.to_dict() for card in cards]
|
|
cards_data = [card.to_dict() for card in cards]
|
|
|
|
|
|
return {
|
|
return {
|
|
|
|
+ 'code': 200,
|
|
'success': True,
|
|
'success': True,
|
|
'message': '获取名片列表成功',
|
|
'message': '获取名片列表成功',
|
|
'data': cards_data
|
|
'data': cards_data
|
|
@@ -761,6 +842,7 @@ def get_business_cards():
|
|
logging.error(error_msg, exc_info=True)
|
|
logging.error(error_msg, exc_info=True)
|
|
|
|
|
|
return {
|
|
return {
|
|
|
|
+ 'code': 500,
|
|
'success': False,
|
|
'success': False,
|
|
'message': error_msg,
|
|
'message': error_msg,
|
|
'data': []
|
|
'data': []
|
|
@@ -783,6 +865,7 @@ def update_business_card_status(card_id, status):
|
|
|
|
|
|
if not card:
|
|
if not card:
|
|
return {
|
|
return {
|
|
|
|
+ 'code': 500,
|
|
'success': False,
|
|
'success': False,
|
|
'message': f'未找到ID为{card_id}的名片记录',
|
|
'message': f'未找到ID为{card_id}的名片记录',
|
|
'data': None
|
|
'data': None
|
|
@@ -791,6 +874,7 @@ def update_business_card_status(card_id, status):
|
|
# 验证状态值
|
|
# 验证状态值
|
|
if status not in ['active', 'inactive']:
|
|
if status not in ['active', 'inactive']:
|
|
return {
|
|
return {
|
|
|
|
+ 'code': 500,
|
|
'success': False,
|
|
'success': False,
|
|
'message': f'无效的状态值: {status},必须为 active 或 inactive',
|
|
'message': f'无效的状态值: {status},必须为 active 或 inactive',
|
|
'data': None
|
|
'data': None
|
|
@@ -805,6 +889,7 @@ def update_business_card_status(card_id, status):
|
|
db.session.commit()
|
|
db.session.commit()
|
|
|
|
|
|
return {
|
|
return {
|
|
|
|
+ 'code': 200,
|
|
'success': True,
|
|
'success': True,
|
|
'message': f'名片状态已更新为: {status}',
|
|
'message': f'名片状态已更新为: {status}',
|
|
'data': card.to_dict()
|
|
'data': card.to_dict()
|
|
@@ -816,7 +901,55 @@ def update_business_card_status(card_id, status):
|
|
logging.error(error_msg, exc_info=True)
|
|
logging.error(error_msg, exc_info=True)
|
|
|
|
|
|
return {
|
|
return {
|
|
|
|
+ 'code': 500,
|
|
'success': False,
|
|
'success': False,
|
|
'message': error_msg,
|
|
'message': error_msg,
|
|
'data': None
|
|
'data': None
|
|
- }
|
|
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+'''
|
|
|
|
+def get_business_card_image_from_minio(image_path):
|
|
|
|
+ """
|
|
|
|
+ 从MinIO获取名片图片
|
|
|
|
+
|
|
|
|
+ Args:
|
|
|
|
+ image_path (str): MinIO中的图片路径
|
|
|
|
+
|
|
|
|
+ Returns:
|
|
|
|
+ tuple: (success, file_data, content_type, status_code)
|
|
|
|
+ - success: 是否成功获取图片
|
|
|
|
+ - file_data: 图片二进制数据
|
|
|
|
+ - content_type: 图片内容类型
|
|
|
|
+ - status_code: HTTP状态码
|
|
|
|
+ """
|
|
|
|
+ response = None
|
|
|
|
+ try:
|
|
|
|
+ minio_client = get_minio_client()
|
|
|
|
+
|
|
|
|
+ if not minio_client:
|
|
|
|
+ logging.error("MinIO客户端初始化失败")
|
|
|
|
+ return False, None, None, 500
|
|
|
|
+
|
|
|
|
+ # 获取文件
|
|
|
|
+ try:
|
|
|
|
+ response = minio_client.get_object(
|
|
|
|
+ Bucket=minio_bucket,
|
|
|
|
+ Key=image_path
|
|
|
|
+ )
|
|
|
|
+ file_data = response.read()
|
|
|
|
+ content_type = response.content_type
|
|
|
|
+
|
|
|
|
+ return True, file_data, content_type, 200
|
|
|
|
+
|
|
|
|
+ except Exception as e:
|
|
|
|
+ logging.error(f"MinIO获取图片失败: {str(e)}")
|
|
|
|
+ return False, None, None, 404
|
|
|
|
+
|
|
|
|
+ except Exception as e:
|
|
|
|
+ logging.error(f"获取图片过程中发生错误: {str(e)}")
|
|
|
|
+ return False, None, None, 500
|
|
|
|
+ finally:
|
|
|
|
+ # 关闭响应连接(如果存在)
|
|
|
|
+ if response and hasattr(response.get('Body', None), 'close'):
|
|
|
|
+ response['Body'].close()
|
|
|
|
+'''
|