工单导入及下载
This commit is contained in:
@@ -7,6 +7,8 @@
|
||||
import os
|
||||
import pandas as pd
|
||||
import logging
|
||||
import uuid
|
||||
import time
|
||||
from datetime import datetime
|
||||
from flask import Blueprint, request, jsonify, send_file
|
||||
from werkzeug.utils import secure_filename
|
||||
@@ -52,29 +54,48 @@ def get_assistant():
|
||||
|
||||
def _ensure_workorder_template_file() -> str:
|
||||
"""返回已有的模板xlsx路径;不做动态生成,避免运行时依赖问题"""
|
||||
template_path = os.path.join('uploads', 'workorder_template.xlsx')
|
||||
# 获取项目根目录
|
||||
current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
project_root = os.path.abspath(os.path.join(current_dir, '..', '..', '..'))
|
||||
|
||||
# 模板文件路径(项目根目录下的uploads)
|
||||
template_path = os.path.join(project_root, 'uploads', 'workorder_template.xlsx')
|
||||
|
||||
# 确保目录存在
|
||||
os.makedirs('uploads', exist_ok=True)
|
||||
uploads_dir = os.path.join(project_root, 'uploads')
|
||||
os.makedirs(uploads_dir, exist_ok=True)
|
||||
|
||||
if not os.path.exists(template_path):
|
||||
# 优先从项目根目录的 uploads 拷贝(仓库自带模板)
|
||||
project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..'))
|
||||
repo_template = os.path.join(project_root, 'uploads', 'workorder_template.xlsx')
|
||||
try:
|
||||
if os.path.exists(repo_template):
|
||||
import shutil
|
||||
shutil.copyfile(repo_template, template_path)
|
||||
else:
|
||||
# 仓库模板不存在时,自动生成一个最小可用模板
|
||||
# 尝试从其他可能的位置复制模板
|
||||
possible_locations = [
|
||||
os.path.join(project_root, 'uploads', 'workorder_template.xlsx'),
|
||||
os.path.join(current_dir, 'uploads', 'workorder_template.xlsx'),
|
||||
os.path.join(os.getcwd(), 'uploads', 'workorder_template.xlsx')
|
||||
]
|
||||
|
||||
source_found = False
|
||||
for source_path in possible_locations:
|
||||
if os.path.exists(source_path):
|
||||
try:
|
||||
import pandas as pd
|
||||
from pandas import DataFrame
|
||||
columns = ['标题', '描述', '分类', '优先级', '状态', '解决方案', '满意度']
|
||||
df: DataFrame = pd.DataFrame(columns=columns)
|
||||
df.to_excel(template_path, index=False)
|
||||
except Exception as gen_err:
|
||||
raise FileNotFoundError('模板文件缺失且自动生成失败,请检查依赖:openpyxl/pandas') from gen_err
|
||||
except Exception as copy_err:
|
||||
raise copy_err
|
||||
import shutil
|
||||
shutil.copyfile(source_path, template_path)
|
||||
source_found = True
|
||||
break
|
||||
except Exception as e:
|
||||
logger.warning(f"复制模板文件失败: {e}")
|
||||
|
||||
if not source_found:
|
||||
# 自动生成一个最小可用模板
|
||||
try:
|
||||
import pandas as pd
|
||||
from pandas import DataFrame
|
||||
columns = ['标题', '描述', '分类', '优先级', '状态', '解决方案', '满意度']
|
||||
df: DataFrame = pd.DataFrame(columns=columns)
|
||||
df.to_excel(template_path, index=False)
|
||||
logger.info(f"自动生成模板文件: {template_path}")
|
||||
except Exception as gen_err:
|
||||
raise FileNotFoundError('模板文件缺失且自动生成失败,请检查依赖:openpyxl/pandas') from gen_err
|
||||
|
||||
return template_path
|
||||
|
||||
@workorders_bp.route('')
|
||||
@@ -411,45 +432,58 @@ def import_workorders():
|
||||
if not title or title.strip() == '':
|
||||
continue
|
||||
|
||||
# 生成唯一的工单ID
|
||||
timestamp = int(time.time())
|
||||
unique_id = str(uuid.uuid4())[:8]
|
||||
order_id = f"IMP_{timestamp}_{unique_id}"
|
||||
|
||||
# 创建工单到数据库
|
||||
with db_manager.get_session() as session:
|
||||
workorder = WorkOrder(
|
||||
title=title,
|
||||
description=description,
|
||||
category=category,
|
||||
priority=priority,
|
||||
status=status,
|
||||
created_at=datetime.now(),
|
||||
updated_at=datetime.now()
|
||||
)
|
||||
|
||||
# 处理可选字段
|
||||
if pd.notna(row.get('解决方案', row.get('resolution'))):
|
||||
workorder.resolution = str(row.get('解决方案', row.get('resolution')))
|
||||
|
||||
if pd.notna(row.get('满意度', row.get('satisfaction_score'))):
|
||||
try:
|
||||
workorder.satisfaction_score = int(row.get('满意度', row.get('satisfaction_score')))
|
||||
except (ValueError, TypeError):
|
||||
workorder.satisfaction_score = None
|
||||
|
||||
session.add(workorder)
|
||||
session.commit()
|
||||
|
||||
# 添加到返回列表
|
||||
imported_workorders.append({
|
||||
"id": workorder.id,
|
||||
"order_id": workorder.order_id,
|
||||
"title": workorder.title,
|
||||
"description": workorder.description,
|
||||
"category": workorder.category,
|
||||
"priority": workorder.priority,
|
||||
"status": workorder.status,
|
||||
"created_at": workorder.created_at.isoformat() if workorder.created_at else None,
|
||||
"updated_at": workorder.updated_at.isoformat() if workorder.updated_at else None,
|
||||
"resolution": workorder.resolution,
|
||||
"satisfaction_score": workorder.satisfaction_score
|
||||
})
|
||||
try:
|
||||
with db_manager.get_session() as session:
|
||||
workorder = WorkOrder(
|
||||
order_id=order_id,
|
||||
title=title,
|
||||
description=description,
|
||||
category=category,
|
||||
priority=priority,
|
||||
status=status,
|
||||
created_at=datetime.now(),
|
||||
updated_at=datetime.now()
|
||||
)
|
||||
|
||||
# 处理可选字段
|
||||
if pd.notna(row.get('解决方案', row.get('resolution'))):
|
||||
workorder.resolution = str(row.get('解决方案', row.get('resolution')))
|
||||
|
||||
if pd.notna(row.get('满意度', row.get('satisfaction_score'))):
|
||||
try:
|
||||
workorder.satisfaction_score = int(row.get('满意度', row.get('satisfaction_score')))
|
||||
except (ValueError, TypeError):
|
||||
workorder.satisfaction_score = None
|
||||
|
||||
session.add(workorder)
|
||||
session.commit()
|
||||
|
||||
logger.info(f"成功导入工单: {order_id} - {title}")
|
||||
|
||||
except Exception as db_error:
|
||||
logger.error(f"导入工单到数据库失败: {db_error}")
|
||||
continue
|
||||
|
||||
# 添加到返回列表
|
||||
imported_workorders.append({
|
||||
"id": workorder.id,
|
||||
"order_id": workorder.order_id,
|
||||
"title": workorder.title,
|
||||
"description": workorder.description,
|
||||
"category": workorder.category,
|
||||
"priority": workorder.priority,
|
||||
"status": workorder.status,
|
||||
"created_at": workorder.created_at.isoformat() if workorder.created_at else None,
|
||||
"updated_at": workorder.updated_at.isoformat() if workorder.updated_at else None,
|
||||
"resolution": workorder.resolution,
|
||||
"satisfaction_score": workorder.satisfaction_score
|
||||
})
|
||||
|
||||
# 清理上传的文件
|
||||
os.remove(upload_path)
|
||||
@@ -488,11 +522,27 @@ def download_import_template_file():
|
||||
"""直接返回工单导入模板文件(下载)"""
|
||||
try:
|
||||
template_path = _ensure_workorder_template_file()
|
||||
|
||||
# 检查文件是否存在
|
||||
if not os.path.exists(template_path):
|
||||
logger.error(f"模板文件不存在: {template_path}")
|
||||
return jsonify({"error": "模板文件不存在"}), 404
|
||||
|
||||
# 检查文件大小
|
||||
file_size = os.path.getsize(template_path)
|
||||
if file_size == 0:
|
||||
logger.error(f"模板文件为空: {template_path}")
|
||||
return jsonify({"error": "模板文件为空"}), 500
|
||||
|
||||
logger.info(f"准备下载模板文件: {template_path}, 大小: {file_size} bytes")
|
||||
|
||||
try:
|
||||
# Flask>=2 使用 download_name
|
||||
return send_file(template_path, as_attachment=True, download_name='工单导入模板.xlsx', mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
|
||||
except TypeError:
|
||||
# 兼容 Flask<2 的 attachment_filename
|
||||
return send_file(template_path, as_attachment=True, attachment_filename='工单导入模板.xlsx', mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
|
||||
|
||||
except Exception as e:
|
||||
return jsonify({"error": str(e)}), 500
|
||||
logger.error(f"下载模板文件失败: {e}")
|
||||
return jsonify({"error": f"下载失败: {str(e)}"}), 500
|
||||
|
||||
Reference in New Issue
Block a user