| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167 |
- """Import API routes for XLSX file import operations."""
- from flask import request, send_file
- from flask_restx import Namespace, Resource, fields
- from werkzeug.datastructures import FileStorage
- from app.services.import_service import ImportService
- from app.utils.auth_decorator import require_auth
- import_ns = Namespace('import', description='数据导入接口')
- # File upload parser
- upload_parser = import_ns.parser()
- upload_parser.add_argument(
- 'file',
- location='files',
- type=FileStorage,
- required=True,
- help='XLSX文件'
- )
- # Response models for Swagger documentation
- error_response = import_ns.model('ImportErrorResponse', {
- 'success': fields.Boolean(description='操作是否成功'),
- 'error': fields.String(description='错误信息'),
- 'errors': fields.List(fields.String, description='错误信息列表'),
- 'code': fields.String(description='错误代码')
- })
- success_response = import_ns.model('ImportSuccessResponse', {
- 'success': fields.Boolean(description='操作是否成功'),
- 'count': fields.Integer(description='成功导入的记录数')
- })
- @import_ns.route('/template')
- class ImportTemplate(Resource):
- """Resource for downloading import template."""
-
- @import_ns.doc('download_template')
- @import_ns.response(200, 'XLSX模板文件下载')
- @import_ns.response(500, '服务器错误', error_response)
- @import_ns.produces(['application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'])
- @require_auth
- def get(self):
- """下载导入模板
-
- 下载包含以下列的XLSX模板文件:
- - 人员姓名
- - 物品名称
- - 工作日期 (格式: YYYY-MM-DD)
- - 数量
-
- Requirements: 3.6
- """
- try:
- template_file = ImportService.generate_template()
-
- return send_file(
- template_file,
- mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
- as_attachment=True,
- download_name='import_template.xlsx'
- )
- except Exception as e:
- return {
- 'success': False,
- 'error': f'生成模板失败: {str(e)}',
- 'code': 'SERVER_ERROR'
- }, 500
- @import_ns.route('/upload')
- class ImportUpload(Resource):
- """Resource for uploading and importing XLSX files."""
-
- @import_ns.doc('upload_import')
- @import_ns.expect(upload_parser)
- @import_ns.response(200, '导入成功', success_response)
- @import_ns.response(400, '验证错误', error_response)
- @import_ns.response(500, '服务器错误', error_response)
- @require_auth
- def post(self):
- """上传并导入XLSX文件
-
- 上传XLSX文件并批量创建工作记录。
- 文件大小限制: 5MB
-
- 验证规则:
- - 文件必须是XLSX格式
- - 必须包含所有必需列
- - 人员姓名必须匹配系统中已存在的人员
- - 物品名称必须匹配系统中已存在的物品
- - 工作日期格式必须为 YYYY-MM-DD
- - 数量必须为正数
-
- Requirements: 4.9, 4.11, 4.12
- """
- # Check if file is present
- if 'file' not in request.files:
- return {
- 'success': False,
- 'error': '请选择要上传的文件',
- 'code': 'VALIDATION_ERROR'
- }, 400
-
- file = request.files['file']
-
- # Check if file is selected
- if file.filename == '':
- return {
- 'success': False,
- 'error': '请选择要上传的文件',
- 'code': 'VALIDATION_ERROR'
- }, 400
-
- # Check file extension
- if not file.filename.lower().endswith('.xlsx'):
- return {
- 'success': False,
- 'error': '文件格式错误,请上传XLSX文件',
- 'code': 'VALIDATION_ERROR'
- }, 400
-
- # Read file content and check size
- file_content = file.read()
-
- # Validate file size (5MB limit)
- if len(file_content) > ImportService.MAX_FILE_SIZE:
- return {
- 'success': False,
- 'error': '文件大小超过限制(最大5MB)',
- 'code': 'VALIDATION_ERROR'
- }, 400
-
- try:
- # Parse and validate file content
- valid_records, errors = ImportService.parse_and_validate(file_content)
-
- # If there are validation errors, return them (atomic operation - no records created)
- if errors:
- return {
- 'success': False,
- 'errors': errors,
- 'code': 'VALIDATION_ERROR'
- }, 400
-
- # If no valid records found
- if not valid_records:
- return {
- 'success': False,
- 'error': '文件中没有有效的数据行',
- 'code': 'VALIDATION_ERROR'
- }, 400
-
- # Import the validated records
- count = ImportService.import_records(valid_records)
-
- return {
- 'success': True,
- 'count': count
- }, 200
-
- except Exception as e:
- return {
- 'success': False,
- 'error': f'导入失败: {str(e)}',
- 'code': 'SERVER_ERROR'
- }, 500
|