Files
Incidents-Data-Portal/backend/app/blueprints/file_uploads/routes.py
T
2026-07-10 18:55:55 +08:00

262 lines
9.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from concurrent.futures import ThreadPoolExecutor, as_completed
from datetime import datetime, timedelta, timezone
from operator import or_
import os
import random
import string
import traceback
from flask import Blueprint, json, jsonify, current_app, request
from sqlalchemy.exc import SQLAlchemyError
from app.models import AccidList, DictItem, DictType, FileUpload
from app import db
file_uploads_bp = Blueprint("file_uploads_bp", __name__)
ALLOWED_EXTENSIONS={'png', 'jpg', 'jpeg', 'gif'}
def allowed_file(filename):
"""验证图片格式是否合法"""
return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
def generate_file_alias(filename):
"""生成唯一存储别名(避免重名):时间戳+6位随机字符串+扩展名"""
ext = filename.rsplit('.', 1)[1].lower()
random_str = ''.join(random.choices(string.ascii_letters + string.digits, k=6))
timestamp = int(datetime.now().timestamp())
return f"{timestamp}_{random_str}.{ext}"
# 接口1:图片上传(基础接口,返回文件ID)
@file_uploads_bp.route('/upload-image', methods=['POST'])
def upload_image():
"""
图片上传接口
- 请求方式:POST
- Content-Type: multipart/form-data
- 请求参数:
file: 图片文件(必传)
create_by: 上传人(可选,如admin
- 返回:文件ID、访问URL等信息
"""
try:
# 1. 校验文件是否存在
if 'file' not in request.files:
return jsonify({'code': 400, 'msg': '请选择要上传的图片', 'data': None}), 400
file = request.files['file']
if file.filename == '':
return jsonify({'code': 400, 'msg': '文件名不能为空', 'data': None}), 400
# 2. 校验文件格式
if not allowed_file(file.filename):
return jsonify({
'code': 400,
'msg': f'仅支持{"/".join(ALLOWED_EXTENSIONS)}格式的图片',
'data': None
}), 400
# 3. 创建上传目录(不存在则自动创建)
os.makedirs(current_app.config['UPLOAD_FOLDER'], exist_ok=True)
# 4. 生成唯一别名并保存文件
file_alias = generate_file_alias(file.filename)
file_save_path = os.path.join(current_app.config['UPLOAD_FOLDER'], file_alias)
file.save(file_save_path)
# 5. 写入FileUpload表
file_size = os.path.getsize(file_save_path) # 获取文件大小(字节)
file_type = file.content_type # 获取MIME类型(如image/jpeg
create_by = request.form.get('create_by', None)
new_file = FileUpload(
file_name=file.filename,
file_alias=file_alias,
file_path=f"/uploads/{file_alias}", # 相对路径,前端拼接域名访问
file_size=file_size,
file_type=file_type,
create_by=create_by,
business_type='case', # 固定关联事故工单业务
business_id=None # 暂不关联具体工单,后续绑定
)
db.session.add(new_file)
db.session.commit()
# 6. 返回成功响应
return jsonify({
'code': 200,
'msg': '图片上传成功',
'data': new_file.to_dict()
}), 200
except Exception as e:
db.session.rollback() # 异常回滚
return jsonify({
'code': 500,
'msg': f'上传失败:{str(e)}',
'data': None
}), 500
# 接口2:绑定图片到事故工单
@file_uploads_bp.route('/bind-image', methods=['POST'])
def bind_image_to_case():
"""
绑定图片到事故工单(更新image_ids和business_id
- 请求方式:POST
- Content-Type: application/json
- 请求参数:
case_id: 事故工单ID(必传)
file_ids: 图片ID列表(如[1,2,3],必传)
"""
try:
data = request.get_json()
case_id = data.get('case_id')
file_ids = data.get('file_ids', [])
# 1. 校验参数
if not case_id or not isinstance(file_ids, list) or len(file_ids) == 0:
return jsonify({'code': 400, 'msg': '工单ID和图片ID列表不能为空', 'data': None}), 400
# 2. 检查工单是否存在
case = AccidList.query.get(case_id)
if not case:
return jsonify({'code': 404, 'msg': '事故工单不存在', 'data': None}), 404
# 3. 拼接图片ID串(去重)
existing_ids = case.image_ids.split(',') if case.image_ids.strip() else []
new_ids = list(set(existing_ids + [str(fid) for fid in file_ids])) # 去重
case.image_ids = ','.join(new_ids)
case.update_at = db.func.current_timestamp() # 更新工单修改时间
# 4. 更新FileUpload的business_id(关联具体工单)
FileUpload.query.filter(
FileUpload.id.in_(file_ids),
FileUpload.business_type == 'case'
).update({'business_id': case_id}, synchronize_session=False)
db.session.commit()
# 5. 返回结果
return jsonify({
'code': 200,
'msg': '图片绑定成功',
'data': {
'case_id': case_id,
'image_ids': case.image_ids,
'image_count': len(new_ids)
}
}), 200
except Exception as e:
db.session.rollback()
return jsonify({
'code': 500,
'msg': f'绑定失败:{str(e)}',
'data': None
}), 500
# 接口3:查询事故工单关联的图片
@file_uploads_bp.route('/get-images/<int:case_id>', methods=['GET'])
def get_case_images(case_id):
"""
查询工单关联的所有有效图片
- 请求方式:GET
- 路径参数:case_id - 事故工单ID
"""
try:
# 1. 检查工单是否存在
case = AccidList.query.get(case_id)
if not case:
return jsonify({'code': 404, 'msg': '事故工单不存在', 'data': None}), 404
# 2. 拆分图片ID并查询
image_ids = case.image_ids.split(',') if case.image_ids.strip() else []
if not image_ids:
return jsonify({'code': 200, 'msg': '暂无关联图片', 'data': []}), 200
# 3. 查询有效图片(status=1
images = FileUpload.query.filter(
FileUpload.id.in_(image_ids),
FileUpload.status == 1
).all()
# ========== 核心修改:拼接完整图片URL ==========
image_base_url = current_app.config['IMAGE_BASE_URL'] # 获取配置的基础URL
image_data = []
for img in images:
img_dict = img.to_dict() # 原有模型的to_dict方法
# 把相对路径拼接成完整URL(覆盖原有file_path
img_dict['file_path'] = f"{image_base_url}{img_dict['file_path']}"
image_data.append(img_dict)
# 4. 返回结果(改用拼接后的image_data)
return jsonify({
'code': 200,
'msg': '查询成功',
'data': image_data # 现在data里的file_path是完整URL
}), 200
except Exception as e:
return jsonify({
'code': 500,
'msg': f'查询失败:{str(e)}',
'data': None
}), 500
# 接口4:移除工单关联的图片(逻辑删除)
@file_uploads_bp.route('/remove-image', methods=['POST'])
def remove_case_image():
"""
移除工单关联的图片(逻辑删除)
- 请求方式:POST
- Content-Type: application/json
- 请求参数:
case_id: 工单ID(必传)
file_id: 图片ID(必传)
"""
try:
data = request.get_json()
case_id = data.get('case_id')
file_id = data.get('file_id')
# 1. 校验参数
if not case_id or not file_id:
return jsonify({'code': 400, 'msg': '工单ID和图片ID不能为空', 'data': None}), 400
# 2. 检查工单和图片是否存在
case = AccidList.query.get(case_id)
file = FileUpload.query.get(file_id)
if not case:
return jsonify({'code': 404, 'msg': '事故工单不存在', 'data': None}), 404
if not file:
return jsonify({'code': 404, 'msg': '图片不存在', 'data': None}), 404
# 3. 移除工单的image_ids中的该ID
existing_ids = case.image_ids.split(',') if case.image_ids.strip() else []
if str(file_id) in existing_ids:
existing_ids.remove(str(file_id))
case.image_ids = ','.join(existing_ids)
case.update_at = db.func.current_timestamp()
# 4. 逻辑删除图片(status=0
file.status = 0
db.session.commit()
return jsonify({
'code': 200,
'msg': '图片移除成功',
'data': {'case_id': case_id, 'file_id': file_id}
}), 200
except Exception as e:
db.session.rollback()
return jsonify({
'code': 500,
'msg': f'移除失败:{str(e)}',
'data': None
}), 500