Files
KCGL/inventory-backend/app/api/v1/import_api.py
yueli 22c7d352d6 refactor(import): API 层支持导入策略参数
- /preview 端点接收 mode 参数,update 模式下 DB 重复行不视为错误
- /execute 端点接收 mode 参数传递给 service 层
- execute 仅导入 status 为 success/update 的行,error 行不再被提交
2026-08-03 15:53:15 +08:00

131 lines
4.7 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.

"""
批量导入 API
端点:
GET /api/v1/import/template?type=material|bom → 下载Excel模板
POST /api/v1/import/preview → Dry-Run预览+验证
POST /api/v1/import/execute → 确认导入执行
"""
from flask import Blueprint, request, jsonify, send_file, current_app
from flask_jwt_extended import jwt_required
from app.utils.decorators import permission_required
from app.services.import_service import (
generate_template,
preview_material, preview_bom,
execute_material_import, execute_bom_import,
)
import traceback
import_bp = Blueprint('import_bp', __name__, url_prefix='/api/v1/import')
@import_bp.route('/template', methods=['GET'])
@jwt_required()
@permission_required('material_list')
def download_template():
"""下载导入模板"""
try:
import_type = request.args.get('type', 'material').strip()
if import_type not in ('material', 'bom'):
return jsonify({'code': 400, 'msg': 'type 必须为 material 或 bom'}), 400
file_stream = generate_template(import_type)
filename = f"{'基础信息' if import_type == 'material' else 'BOM表'}_导入模板.xlsx"
return send_file(
file_stream,
mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
as_attachment=True,
download_name=filename
)
except Exception as e:
current_app.logger.error(f'下载模板失败: {str(e)}')
return jsonify({'code': 500, 'msg': str(e)}), 500
@import_bp.route('/preview', methods=['POST'])
@jwt_required()
@permission_required('material_list:operation')
def preview_import():
"""Dry-Run 预览 + 验证(不写入数据库)
FormData:
type: 'material' | 'bom'
file: Excel 文件
mode: 'skip' (默认) | 'update' — update 模式下 DB 重复行显示为「将被更新」而非错误
"""
try:
import_type = request.form.get('type', 'material').strip()
mode = request.form.get('mode', 'skip').strip()
file = request.files.get('file')
if not file:
return jsonify({'code': 400, 'msg': '请上传文件'}), 400
if import_type not in ('material', 'bom'):
return jsonify({'code': 400, 'msg': 'type 必须为 material 或 bom'}), 400
file_stream = file.read()
from io import BytesIO
if import_type == 'material':
results = preview_material(BytesIO(file_stream), mode=mode)
else:
results = preview_bom(BytesIO(file_stream))
success_count = sum(1 for r in results if r['status'] in ('success', 'update'))
error_count = sum(1 for r in results if r['status'] == 'error')
return jsonify({
'code': 200, 'msg': '预览完成',
'data': {
'rows': results,
'total': len(results),
'success_count': success_count,
'error_count': error_count,
}
})
except Exception as e:
traceback.print_exc()
return jsonify({'code': 500, 'msg': f'预览失败: {str(e)}'}), 500
@import_bp.route('/execute', methods=['POST'])
@jwt_required()
@permission_required('material_list:operation')
def execute_import():
"""确认导入执行
Body:
type: 'material' | 'bom'
rows: 待导入行列表
mode: 'skip' (跳过重复,默认) | 'update' (覆盖更新已有记录)
"""
try:
data = request.get_json() or {}
import_type = data.get('type', 'material').strip()
rows = data.get('rows', [])
mode = data.get('mode', 'skip').strip()
if not rows:
return jsonify({'code': 400, 'msg': '导入数据不能为空'}), 400
if import_type not in ('material', 'bom'):
return jsonify({'code': 400, 'msg': 'type 必须为 material 或 bom'}), 400
if mode not in ('skip', 'update'):
return jsonify({'code': 400, 'msg': 'mode 必须为 skip 或 update'}), 400
# 仅导入状态为 success 或 update 的行error 行不参与导入
valid_rows = [r for r in rows if r.get('status') in ('success', 'update')]
if not valid_rows:
return jsonify({'code': 400, 'msg': '没有可导入的有效数据'}), 400
if import_type == 'material':
result = execute_material_import(valid_rows, mode=mode)
else:
result = execute_bom_import(valid_rows)
code = 200 if result['success'] else 400
return jsonify({'code': code, 'msg': result['msg'], 'data': result})
except Exception as e:
traceback.print_exc()
return jsonify({'code': 500, 'msg': f'导入失败: {str(e)}'}), 500