254 lines
7.8 KiB
Python
254 lines
7.8 KiB
Python
"""
|
|
Excel AI 分析服务 - 集成 Excel 解析和 LLM 分析
|
|
"""
|
|
import logging
|
|
from typing import Dict, Any, Optional, List
|
|
|
|
from app.core.document_parser import XlsxParser
|
|
from app.services.file_service import file_service
|
|
from app.services.llm_service import llm_service
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class ExcelAIService:
|
|
"""Excel AI 分析服务"""
|
|
|
|
def __init__(self):
|
|
self.parser = XlsxParser()
|
|
self.file_service = file_service
|
|
self.llm_service = llm_service
|
|
|
|
async def analyze_excel_file(
|
|
self,
|
|
file_content: bytes,
|
|
filename: str,
|
|
user_prompt: str = "",
|
|
analysis_type: str = "general",
|
|
parse_options: Optional[Dict[str, Any]] = None
|
|
) -> Dict[str, Any]:
|
|
"""
|
|
分析 Excel 文件
|
|
|
|
Args:
|
|
file_content: 文件内容字节
|
|
filename: 文件名
|
|
user_prompt: 用户自定义提示词
|
|
analysis_type: 分析类型
|
|
parse_options: 解析选项
|
|
|
|
Returns:
|
|
Dict[str, Any]: 分析结果
|
|
"""
|
|
# 1. 保存文件
|
|
try:
|
|
saved_path = self.file_service.save_uploaded_file(
|
|
file_content,
|
|
filename,
|
|
subfolder="excel"
|
|
)
|
|
logger.info(f"文件已保存: {saved_path}")
|
|
except Exception as e:
|
|
logger.error(f"文件保存失败: {str(e)}")
|
|
return {
|
|
"success": False,
|
|
"error": f"文件保存失败: {str(e)}",
|
|
"analysis": None
|
|
}
|
|
|
|
# 2. 解析 Excel 文件
|
|
try:
|
|
parse_options = parse_options or {}
|
|
parse_result = self.parser.parse(saved_path, **parse_options)
|
|
|
|
if not parse_result.success:
|
|
return {
|
|
"success": False,
|
|
"error": parse_result.error,
|
|
"analysis": None
|
|
}
|
|
|
|
excel_data = parse_result.data
|
|
logger.info(f"Excel 解析成功: {parse_result.metadata}")
|
|
|
|
except Exception as e:
|
|
logger.error(f"Excel 解析失败: {str(e)}")
|
|
return {
|
|
"success": False,
|
|
"error": f"Excel 解析失败: {str(e)}",
|
|
"analysis": None
|
|
}
|
|
|
|
# 3. 调用 LLM 进行分析
|
|
try:
|
|
# 如果有自定义提示词,使用模板分析
|
|
if user_prompt and user_prompt.strip():
|
|
llm_result = await self.llm_service.analyze_with_template(
|
|
excel_data,
|
|
user_prompt
|
|
)
|
|
else:
|
|
# 否则使用标准分析
|
|
llm_result = await self.llm_service.analyze_excel_data(
|
|
excel_data,
|
|
user_prompt,
|
|
analysis_type
|
|
)
|
|
|
|
logger.info(f"AI 分析完成: {llm_result['success']}")
|
|
|
|
# 4. 组合结果
|
|
return {
|
|
"success": True,
|
|
"excel": {
|
|
"data": excel_data,
|
|
"metadata": parse_result.metadata,
|
|
"saved_path": saved_path
|
|
},
|
|
"analysis": llm_result
|
|
}
|
|
|
|
except Exception as e:
|
|
logger.error(f"AI 分析失败: {str(e)}")
|
|
return {
|
|
"success": False,
|
|
"error": f"AI 分析失败: {str(e)}",
|
|
"excel": {
|
|
"data": excel_data,
|
|
"metadata": parse_result.metadata
|
|
},
|
|
"analysis": None
|
|
}
|
|
|
|
async def batch_analyze_sheets(
|
|
self,
|
|
file_content: bytes,
|
|
filename: str,
|
|
user_prompt: str = "",
|
|
analysis_type: str = "general"
|
|
) -> Dict[str, Any]:
|
|
"""
|
|
批量分析 Excel 文件的所有工作表
|
|
|
|
Args:
|
|
file_content: 文件内容字节
|
|
filename: 文件名
|
|
user_prompt: 用户自定义提示词
|
|
analysis_type: 分析类型
|
|
|
|
Returns:
|
|
Dict[str, Any]: 分析结果
|
|
"""
|
|
# 1. 保存文件
|
|
try:
|
|
saved_path = self.file_service.save_uploaded_file(
|
|
file_content,
|
|
filename,
|
|
subfolder="excel"
|
|
)
|
|
logger.info(f"文件已保存: {saved_path}")
|
|
except Exception as e:
|
|
logger.error(f"文件保存失败: {str(e)}")
|
|
return {
|
|
"success": False,
|
|
"error": f"文件保存失败: {str(e)}",
|
|
"analysis": None
|
|
}
|
|
|
|
# 2. 解析所有工作表
|
|
try:
|
|
parse_result = self.parser.parse_all_sheets(saved_path)
|
|
|
|
if not parse_result.success:
|
|
return {
|
|
"success": False,
|
|
"error": parse_result.error,
|
|
"analysis": None
|
|
}
|
|
|
|
sheets_data = parse_result.data.get("sheets", {})
|
|
logger.info(f"Excel 解析成功,共 {len(sheets_data)} 个工作表")
|
|
|
|
except Exception as e:
|
|
logger.error(f"Excel 解析失败: {str(e)}")
|
|
return {
|
|
"success": False,
|
|
"error": f"Excel 解析失败: {str(e)}",
|
|
"analysis": None
|
|
}
|
|
|
|
# 3. 批量分析每个工作表
|
|
sheet_analyses = {}
|
|
errors = {}
|
|
|
|
for sheet_name, sheet_data in sheets_data.items():
|
|
try:
|
|
# 调用 LLM 分析
|
|
if user_prompt and user_prompt.strip():
|
|
llm_result = await self.llm_service.analyze_with_template(
|
|
sheet_data,
|
|
user_prompt
|
|
)
|
|
else:
|
|
llm_result = await self.llm_service.analyze_excel_data(
|
|
sheet_data,
|
|
user_prompt,
|
|
analysis_type
|
|
)
|
|
|
|
sheet_analyses[sheet_name] = llm_result
|
|
|
|
if not llm_result["success"]:
|
|
errors[sheet_name] = llm_result.get("error", "未知错误")
|
|
|
|
logger.info(f"工作表 '{sheet_name}' 分析完成")
|
|
|
|
except Exception as e:
|
|
logger.error(f"工作表 '{sheet_name}' 分析失败: {str(e)}")
|
|
errors[sheet_name] = str(e)
|
|
|
|
# 4. 组合结果
|
|
return {
|
|
"success": len(errors) == 0,
|
|
"excel": {
|
|
"sheets": sheets_data,
|
|
"metadata": parse_result.metadata,
|
|
"saved_path": saved_path
|
|
},
|
|
"analysis": {
|
|
"sheets": sheet_analyses,
|
|
"total_sheets": len(sheets_data),
|
|
"successful": len(sheet_analyses) - len(errors),
|
|
"errors": errors
|
|
}
|
|
}
|
|
|
|
def get_supported_analysis_types(self) -> List[str]:
|
|
"""获取支持的分析类型"""
|
|
return [
|
|
{
|
|
"value": "general",
|
|
"label": "综合分析",
|
|
"description": "提供数据概览、关键发现、质量评估和建议"
|
|
},
|
|
{
|
|
"value": "summary",
|
|
"label": "数据摘要",
|
|
"description": "快速了解数据的结构、范围和主要内容"
|
|
},
|
|
{
|
|
"value": "statistics",
|
|
"label": "统计分析",
|
|
"description": "数值型列的统计信息和分类列的分布"
|
|
},
|
|
{
|
|
"value": "insights",
|
|
"label": "深度洞察",
|
|
"description": "深入挖掘数据,提供异常值和业务建议"
|
|
}
|
|
]
|
|
|
|
|
|
# 全局单例
|
|
excel_ai_service = ExcelAIService()
|