ERP/backend/api_ai.py

310 lines
11 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.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
AI分析API端点
"""
from fastapi import APIRouter, HTTPException, Depends
from fastapi.responses import JSONResponse
from typing import Dict, Any
import asyncio
from datetime import datetime
import logging
from ai_service import AIService, get_ai_config
from api import get_db_connection
# 创建路由
router = APIRouter(prefix="/api/ai", tags=["AI分析"])
# 配置日志
logger = logging.getLogger(__name__)
@router.post("/analyze")
async def analyze_production():
"""
分析生产数据
返回AI生成的生产报表
"""
try:
# 获取数据库连接
conn = await get_db_connection()
# 获取最近30天的数据
query = """
SELECT
platform,
ts_cn,
batch,
mac,
note
FROM audit_records
WHERE ts_cn >= datetime('now', '-30 days')
ORDER BY ts_cn DESC
"""
cursor = await conn.execute(query)
rows = await cursor.fetchall()
await conn.close()
# 整理数据
pdd_data = []
yt_data = []
for row in rows:
record = {
"ts_cn": row[1],
"batch": row[2],
"mac": row[3],
"note": row[4]
}
if row[0] == "pdd":
pdd_data.append(record)
elif row[0] == "yt":
yt_data.append(record)
# 获取实时生产数据从Redis类似dashboard的逻辑
from datetime import timezone, timedelta
beijing_tz = timezone(timedelta(hours=8))
now_bj = datetime.now(beijing_tz)
today_bj = now_bj.strftime('%Y-%m-%d')
# 尝试从Redis获取实时数据
real_pdd_count = 0
real_yt_count = 0
week_pdd_count = 0
week_yt_count = 0
try:
import sys
sys.path.append('/home/hyx/work/生产管理系统')
from server import parse_audit_line, get_redis
r = get_redis()
# 获取拼多多审计数据
pdd_items = []
for key in ['mac_batch_audit_pdd', 'audit:pdd', 'pdd:audit']:
if r.exists(key) and r.type(key) == 'list':
pdd_items = r.lrange(key, 0, -1)
break
# 获取圆通审计数据
yt_items = []
for key in ['mac_batch_audit_yt', 'audit:yt', 'yt:audit']:
if r.exists(key) and r.type(key) == 'list':
yt_items = r.lrange(key, 0, -1)
break
# 解析拼多多数据
today_pdd_macs = set()
week_pdd_macs = set()
for item in pdd_items:
try:
parsed = parse_audit_line(item)
ts_str = parsed.get('ts_cn') or ''
mac = parsed.get('mac') or ''
if ts_str and mac:
# 今日产量
if ts_str.startswith(today_bj):
today_pdd_macs.add(mac)
# 本周产量最近7天
item_date = datetime.strptime(ts_str.split(' ')[0], '%Y-%m-%d')
if (now_bj - item_date).days <= 7:
week_pdd_macs.add(mac)
except:
pass
# 解析圆通数据
today_yt_macs = set()
week_yt_macs = set()
for item in yt_items:
try:
parsed = parse_audit_line(item)
ts_str = parsed.get('ts_cn') or ''
mac = parsed.get('mac') or ''
if ts_str and mac:
# 今日产量
if ts_str.startswith(today_bj):
today_yt_macs.add(mac)
# 本周产量最近7天
item_date = datetime.strptime(ts_str.split(' ')[0], '%Y-%m-%d')
if (now_bj - item_date).days <= 7:
week_yt_macs.add(mac)
except:
pass
real_pdd_count = len(today_pdd_macs)
real_yt_count = len(today_yt_macs)
week_pdd_count = len(week_pdd_macs)
week_yt_count = len(week_yt_macs)
# 添加调试日志
print(f"AI分析获取的实时数据: 今日PDD={real_pdd_count}, 今日YT={real_yt_count}, 本周PDD={week_pdd_count}, 本周YT={week_yt_count}")
except Exception as e:
print(f"Redis error in AI: {e}")
import traceback
traceback.print_exc()
# 获取发货数据统计
shipments_stats = {'total': 0, 'by_platform': {}}
try:
# 使用同一个redis连接
shipments_count = r.hlen('shipment_sn_mapping')
shipments_stats['total'] = shipments_count
# 获取各平台发货数量(简化处理,假设主要来自拼多多)
shipments_stats['by_platform'] = {'pdd': shipments_count * 0.919, 'yt': shipments_count * 0.054, 'other': shipments_count * 0.027}
except Exception as e:
print(f"Redis shipment error in AI: {e}")
# 如果Redis失败尝试重新连接
try:
import sys
sys.path.append('/home/hyx/work/生产管理系统')
from server import get_redis
r = get_redis()
shipments_count = r.hlen('shipment_sn_mapping')
shipments_stats['total'] = shipments_count
shipments_stats['by_platform'] = {'pdd': shipments_count * 0.919, 'yt': shipments_count * 0.054, 'other': shipments_count * 0.027}
except:
pass
# 获取其他统计数据
bom_stats = {'count': 0, 'products': 0}
inventory_stats = {'count': 0, 'total_qty': 0}
purchase_demand_stats = {'count': 0, 'total_required': 0}
customer_order_stats = {'count': 0, 'total_qty': 0, 'completed': 0}
reconciliation_stats = {'count': 0, 'total_qty': 0}
# 准备AI分析数据包含实时数据
print(f"\n=== AI分析数据准备 ===")
print(f"数据库记录: PDD={len(pdd_data)}, YT={len(yt_data)}")
print(f"实时数据: 今日PDD={real_pdd_count}, 今日YT={real_yt_count}")
print(f"实时数据: 本周PDD={week_pdd_count}, 本周YT={week_yt_count}")
print("========================\n")
data = {
"pdd": pdd_data,
"yt": yt_data,
"realtime": {
"today_pdd": real_pdd_count,
"today_yt": real_yt_count,
"week_pdd": week_pdd_count,
"week_yt": week_yt_count,
"total_today": real_pdd_count + real_yt_count,
"total_week": week_pdd_count + week_yt_count
},
"shipments": shipments_stats,
"bom": bom_stats,
"inventory": inventory_stats,
"purchase_demand": purchase_demand_stats,
"customer_orders": customer_order_stats,
"reconciliations": reconciliation_stats,
"analysis_time": datetime.now().isoformat()
}
# 调用AI服务
config = get_ai_config()
async with AIService(config) as ai_service:
result = await ai_service.analyze_production_data(data)
# 添加元数据
result["metadata"] = {
"generated_at": datetime.now().isoformat(),
"data_period": "最近30天",
"total_records": len(pdd_data) + len(yt_data),
"ai_provider": config.provider,
"realtime_data": {
"today_total": real_pdd_count + real_yt_count,
"week_total": week_pdd_count + week_yt_count
}
}
return JSONResponse(content=result)
except Exception as e:
logger.error(f"AI分析失败: {str(e)}")
raise HTTPException(status_code=500, detail=f"AI分析失败: {str(e)}")
@router.get("/config")
async def get_ai_config_info():
"""获取AI配置信息不包含敏感信息"""
try:
config = get_ai_config()
return {
"provider": config.provider,
"model": config.model,
"configured": bool(config.api_key or config.provider == "local")
}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@router.post("/test")
async def test_ai_connection():
"""测试AI连接"""
try:
config = get_ai_config()
# 测试数据
test_data = {
"pdd": [{"ts_cn": datetime.now().strftime("%Y-%m-%d %H:%M:%S"), "batch": "TEST", "mac": "TEST001", "note": "测试数据"}],
"yt": []
}
async with AIService(config) as ai_service:
result = await ai_service.analyze_production_data(test_data)
return {
"success": True,
"message": "AI连接测试成功",
"provider": config.provider,
"model": config.model
}
except Exception as e:
logger.error(f"AI连接测试失败: {str(e)}")
return {
"success": False,
"message": f"AI连接测试失败: {str(e)}",
"provider": config.provider if 'config' in locals() else "unknown"
}
@router.get("/providers")
async def get_supported_providers():
"""获取支持的AI提供商列表"""
return {
"providers": [
{
"id": "openai",
"name": "OpenAI",
"models": ["gpt-3.5-turbo", "gpt-4", "gpt-4-turbo"],
"description": "OpenAI GPT模型需要API Key"
},
{
"id": "qwen",
"name": "通义千问",
"models": ["qwen-turbo", "qwen-plus", "qwen-max"],
"description": "阿里云通义千问需要API Key"
},
{
"id": "wenxin",
"name": "文心一言",
"models": ["ERNIE-Bot", "ERNIE-Bot-turbo", "ERNIE-Bot-4"],
"description": "百度文心一言需要API Key"
},
{
"id": "local",
"name": "本地模型",
"models": ["llama2", "llama2:13b", "codellama", "qwen:7b"],
"description": "本地部署的模型如Ollama无需API Key"
}
]
}