import os import time from flask import request, jsonify, current_app from flask_jwt_extended import jwt_required, get_jwt_identity from werkzeug.utils import secure_filename from app.constants import OperationType, UserRole from app.decorators import login_required from app.models import Operation, Model, db, User, Detection from app.routes import model_routes from app.utils import handle_operation_failure, allowed_model_file, handle_file_upload, handle_operation_success, \ adjust_page_if_needed, get_pagination_params, delete_file, user_rate_limit @model_routes.route('/upload', methods=['POST']) @jwt_required() @login_required def upload(): start_time = time.time() # 记录操作开始时间 # 获取请求中的表单数据 model_file = request.files.get('model_file') disease_category = request.form.get('disease_category') augmentation = request.form.get('augmentation', '原图') layers = int(request.form.get('layers')) parameters = int(request.form.get('parameters')) GFLOPs = float(request.form.get('GFLOPs')) box_p = float(request.form.get('box_p', 0.0)) box_r = float(request.form.get('box_r', 0.0)) box_mAP50 = float(request.form.get('box_mAP50', 0.0)) box_mAP50_95 = float(request.form.get('box_mAP50_95', 0.0)) mask_p = float(request.form.get('mask_p', 0.0)) mask_r = float(request.form.get('mask_r', 0.0)) mask_mAP50 = float(request.form.get('mask_mAP50', 0.0)) mask_mAP50_95 = float(request.form.get('mask_mAP50_95', 0.0)) f1_score = float(request.form.get('f1_score', 0.0)) fitness_score = float(request.form.get('fitness_score', 0.0)) # 创建一个新的操作记录 new_operation = Operation( operation_type=OperationType.CREATE, description="上传模型", ip_address=request.remote_addr, device_info=request.user_agent.string, ) # 获取当前用户身份(使用 access token) current_user_id = get_jwt_identity() current_user = User.query.get(current_user_id) # 先获取文件名 file_name = secure_filename(model_file.filename) if model_file else None # 检查文件名是否已存在 existing_model = Model.query.filter_by(model_name=file_name).first() if file_name else None # 校验字段 validation_checks = [ (model_file and not allowed_model_file(model_file), "【上传模型失败】模型文件不合规", 400), (current_user.role != UserRole.DEVELOPER, f"【上传模型失败】您非开发人员,无权上传模型", 403), (model_file and existing_model, f"【上传模型失败】模型 {file_name} 已存在,请重新上传", 400), (not disease_category or not layers or not parameters or not GFLOPs, "【上传模型失败】模型病害类别、层数、参数量或计算量为空", 400), ] for condition, message, code in validation_checks: if condition: new_operation = handle_operation_failure(new_operation, start_time, message, current_user_id) current_app.logger.warning(message + f', operator: {current_user}') return jsonify({ 'operation': new_operation.to_dict(), }), code # 保存文件到指定目录(返回相对路径) file_path = handle_file_upload(model_file, 'models') new_model = Model( model_name=file_name, model_path=file_path, disease_category=disease_category, augmentation=augmentation, layers=layers, parameters=parameters, GFLOPs=GFLOPs, box_p=box_p, box_r=box_r, box_mAP50=box_mAP50, box_mAP50_95=box_mAP50_95, mask_p=mask_p, mask_r=mask_r, mask_mAP50=mask_mAP50, mask_mAP50_95=mask_mAP50_95, f1_score=f1_score, fitness_score=fitness_score, owner_id=current_user_id, ) db.session.add(new_model) db.session.commit() # 记录操作 new_operation = handle_operation_success(new_operation, start_time, current_user_id) current_app.logger.info(f"【上传模型成功】new_model: {new_model}, operator: {current_user}") return jsonify({ 'operation': new_operation.to_dict(), 'new_model': new_model.to_dict(), }), 201 @model_routes.route('/detail/', methods=['GET']) @jwt_required() @login_required def detail(model_id): # 获取当前用户身份(使用 access token) current_user_id = get_jwt_identity() current_user = User.query.get(current_user_id) # 获取指定模型文件 model = Model.query.get(model_id) # 校验字段 validation_checks = [ (not model, f"【获取模型 ID={model_id} 详情失败】该模型不存在", 404), ] for condition, message, code in validation_checks: if condition: current_app.logger.warning(message + f', operator: {current_user}') return jsonify({ 'failure_message': message, }), code return jsonify({ 'model': model.to_dict(), }), 200 @model_routes.route('/update/', methods=['PUT']) @jwt_required() @login_required def update(model_id): start_time = time.time() # 记录操作开始时间 # 获取请求中的表单数据 disease_category = request.form.get('disease_category') augmentation = request.form.get('augmentation') layers = int(request.form.get('layers') or 0) parameters = int(request.form.get('parameters') or 0) GFLOPs = float(request.form.get('GFLOPs') or 0.0) box_p = float(request.form.get('box_p') or 0.0) box_r = float(request.form.get('box_r') or 0.0) box_mAP50 = float(request.form.get('box_mAP50') or 0.0) box_mAP50_95 = float(request.form.get('box_mAP50_95') or 0.0) mask_p = float(request.form.get('mask_p') or 0.0) mask_r = float(request.form.get('mask_r') or 0.0) mask_mAP50 = float(request.form.get('mask_mAP50') or 0.0) mask_mAP50_95 = float(request.form.get('mask_mAP50_95') or 0.0) f1_score = float(request.form.get('f1_score') or 0.0) fitness_score = float(request.form.get('fitness_score') or 0.0) # 创建一个新的操作记录 new_operation = Operation( operation_type=OperationType.UPDATE, description=f"更新模型 ID={model_id} 信息", ip_address=request.remote_addr, device_info=request.user_agent.string, ) # 获取当前用户的身份(使用 access token) current_user_id = get_jwt_identity() current_user = User.query.get(current_user_id) # 获取指定模型文件 updated_model = Model.query.get(model_id) # 校验字段 validation_checks = [ (not disease_category, f"【更新模型 ID={model_id} 信息失败】病害类别为空", 400), (not updated_model, f"【更新模型 ID={model_id} 信息失败】该模型不存在", 404), (updated_model and current_user.role != UserRole.DEVELOPER, f"【更新模型 ID={model_id} 信息失败】您非开发人员,权限不足", 403), ] for condition, message, code in validation_checks: if condition: new_operation = handle_operation_failure(new_operation, start_time, message, current_user_id) current_app.logger.warning(message + f', operator: {current_user}') return jsonify({ 'operation': new_operation.to_dict(), }), code # 更新模型文件信息 updated_model.disease_category = disease_category updated_model.augmentation = augmentation if augmentation else updated_model.augmentation updated_model.layers = layers updated_model.parameters = parameters updated_model.GFLOPs = GFLOPs updated_model.box_p = box_p if box_p else updated_model.box_p updated_model.box_r = box_r if box_r else updated_model.box_r updated_model.box_mAP50 = box_mAP50 if box_mAP50 else updated_model.box_mAP50 updated_model.box_mAP50_95 = box_mAP50_95 if box_mAP50_95 else updated_model.box_mAP50_95 updated_model.mask_p = mask_p if mask_p else updated_model.mask_p updated_model.mask_r = mask_r if mask_r else updated_model.mask_r updated_model.mask_mAP50 = mask_mAP50 if mask_mAP50 else updated_model.mask_mAP50 updated_model.mask_mAP50_95 = mask_mAP50_95 if mask_mAP50_95 else updated_model.mask_mAP50_95 updated_model.f1_score = f1_score if f1_score else updated_model.f1_score updated_model.fitness_score = fitness_score if fitness_score else updated_model.fitness_score db.session.commit() # 记录操作 new_operation = handle_operation_success(new_operation, start_time, current_user_id) current_app.logger.info( f"【更新模型 ID={model_id} 信息成功】updated_model: {updated_model}, operator: {current_user}") return jsonify({ 'operation': new_operation.to_dict(), 'updated_model': updated_model.to_dict(), }), 200 @model_routes.route('/delete/', methods=['DELETE']) @jwt_required() @login_required def delete_model(model_id): start_time = time.time() # 记录操作开始时间 # 创建一个新的操作记录 new_operation = Operation( operation_type=OperationType.DELETE, description=f"删除模型 ID={model_id}", ip_address=request.remote_addr, device_info=request.user_agent.string, ) # 获取当前用户身份(使用 access token) current_user_id = get_jwt_identity() current_user = User.query.get(current_user_id) # 获取指定模型 deleted_model = Model.query.get(model_id) # 校验字段 validation_checks = [ (not deleted_model, f"【删除模型 ID={model_id} 失败】该模型不存在", 404), (deleted_model and current_user.role != UserRole.DEVELOPER, f"【删除模型 ID={model_id} 失败】您非开发人员,权限不足", 403), (deleted_model and Detection.query.filter_by(model_id=model_id).first(), f"【删除模型 ID={model_id} 失败】该模型存在关联的检测分割记录,无法删除", 400), ] for condition, message, code in validation_checks: if condition: new_operation = handle_operation_failure(new_operation, start_time, message, current_user_id) current_app.logger.warning(message + f', operator: {current_user}') return jsonify({ 'operation': new_operation.to_dict(), }), code # 删除实际文件 file_abs_path = os.path.join(current_app.root_path, deleted_model.model_path) delete_file(file_abs_path) # 删除数据库记录 db.session.delete(deleted_model) db.session.commit() # 记录操作 new_operation = handle_operation_success(new_operation, start_time, current_user_id) current_app.logger.info( f"【删除模型 ID={model_id} 成功】deleted_model: {deleted_model}, operator: {current_user}") return jsonify({ 'operation': new_operation.to_dict(), 'deleted_model': deleted_model.to_dict(), }), 200 @model_routes.route('/models/all', methods=['GET']) @jwt_required() @login_required def all_models(): # 获取分页参数(从请求中获取,默认为第 1 页,每页 5 条记录) default_page = request.args.get('page', 1, type=int) default_per_page = request.args.get('per_page', 5, type=int) page, per_page = get_pagination_params(default_page, default_per_page) # 获取当前用户身份(使用 access token) current_user_id = get_jwt_identity() current_user = User.query.get(current_user_id) # 获取所有模型文件 query = ( Model.query .join(User, Model.owner_id == User.user_id) .add_columns(User.username.label('owner_username')) .order_by(Model.model_id.asc()) ) page, models_total, pages = adjust_page_if_needed(query, page, per_page) paginated = query.paginate(page=page, per_page=per_page, error_out=False) models = [] for model, owner_username in paginated.items: model_dict = model.to_dict() model_dict.update({'owner_username': owner_username}) models.append(model_dict) current_app.logger.info( f"【获取所有模型成功】total: {models_total}, per_page: {per_page}, page: {page}, pages: {pages}, models: {models}, operator: {current_user}") return jsonify({ 'models': models, 'total': models_total, 'per_page': per_page, 'page': page, 'pages': pages, }), 200 @model_routes.route('/statistics', methods=['GET']) @jwt_required() @login_required def statistics(): # 查询模型总数 total_models = Model.query.count() # 构建统计数据 models_statistics = { 'total': total_models, } return jsonify({ "models_statistics": models_statistics, }), 200