清理旧Agent工具文件
删除已被 order_query.py / order_action.py 替代的旧文件: - alarm_query.py - alarm_action.py - order_tools.py
This commit is contained in:
@@ -1,125 +0,0 @@
|
||||
"""
|
||||
告警操作工具:确认接单、忽略、完成、误报
|
||||
"""
|
||||
|
||||
import json
|
||||
from langchain_core.tools import tool
|
||||
from langchain_core.runnables import RunnableConfig
|
||||
|
||||
from app.utils.logger import logger
|
||||
|
||||
|
||||
def _get_order_id_for_alarm(alarm_id: str) -> str:
|
||||
"""从 alarm_event_ext 中获取关联的工单ID"""
|
||||
from app.models import get_session, AlarmEventExt
|
||||
db = get_session()
|
||||
try:
|
||||
ext = db.query(AlarmEventExt).filter(
|
||||
AlarmEventExt.alarm_id == alarm_id,
|
||||
AlarmEventExt.ext_type == "WORK_ORDER",
|
||||
).first()
|
||||
if ext and ext.ext_data:
|
||||
return ext.ext_data.get("order_id", "")
|
||||
return ""
|
||||
except Exception as e:
|
||||
logger.error(f"查询工单ID失败: alarm={alarm_id}, error={e}")
|
||||
return ""
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
async def _update_wechat_card(alarm_id: str, user_id: str, action: str):
|
||||
"""更新企微卡片状态"""
|
||||
try:
|
||||
from app.services.wechat_service import get_wechat_service
|
||||
wechat = get_wechat_service()
|
||||
response_code = wechat.get_response_code(alarm_id)
|
||||
if not response_code:
|
||||
return
|
||||
|
||||
if action == "confirm":
|
||||
await wechat.update_alarm_card_step2(
|
||||
response_code=response_code, user_ids=[user_id],
|
||||
alarm_id=alarm_id, operator_name=user_id,
|
||||
)
|
||||
else:
|
||||
await wechat.update_alarm_card_terminal(
|
||||
response_code=response_code, user_ids=[user_id],
|
||||
alarm_id=alarm_id, action=action, operator_name=user_id,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"更新企微卡片失败: alarm={alarm_id}, error={e}")
|
||||
|
||||
|
||||
@tool
|
||||
async def update_alarm_status(alarm_id: str, action: str, config: RunnableConfig) -> str:
|
||||
"""更新告警状态:确认接单(confirm)、忽略(ignore)、处理完成(complete)、标记误报(false)
|
||||
|
||||
Args:
|
||||
alarm_id: 告警ID
|
||||
action: 操作类型 confirm=确认接单 ignore=忽略 complete=处理完成 false=标记误报
|
||||
"""
|
||||
from app.services.alarm_event_service import get_alarm_event_service
|
||||
|
||||
user_id = config.get("configurable", {}).get("user_id", "")
|
||||
svc = get_alarm_event_service()
|
||||
|
||||
# 查告警是否存在
|
||||
detail = svc.get_alarm(alarm_id)
|
||||
if not detail:
|
||||
return json.dumps({"error": f"未找到告警: {alarm_id}"}, ensure_ascii=False)
|
||||
|
||||
# 获取关联工单ID
|
||||
order_id = _get_order_id_for_alarm(alarm_id)
|
||||
|
||||
from app.services.work_order_client import get_work_order_client
|
||||
wo_client = get_work_order_client()
|
||||
|
||||
# 操作映射
|
||||
action_map = {
|
||||
"confirm": ("CONFIRMED", "HANDLING", "企微Agent确认接单"),
|
||||
"ignore": ("FALSE", "IGNORED", "企微Agent忽略"),
|
||||
"complete": ("CLOSED", "DONE", "企微Agent已处理"),
|
||||
"false": ("FALSE", "IGNORED", "企微Agent标记误报"),
|
||||
}
|
||||
|
||||
if action not in action_map:
|
||||
return json.dumps({"error": f"未知操作: {action}"}, ensure_ascii=False)
|
||||
|
||||
alarm_status, handle_status, remark = action_map[action]
|
||||
|
||||
# IoT 工单操作
|
||||
if order_id and wo_client.enabled:
|
||||
iot_success = False
|
||||
if action == "confirm":
|
||||
iot_success = await wo_client.confirm_order(order_id)
|
||||
elif action in ("ignore", "false"):
|
||||
iot_success = await wo_client.false_alarm(order_id)
|
||||
elif action == "complete":
|
||||
iot_success = await wo_client.submit_order(order_id, result=f"已处理 by {user_id}")
|
||||
if not iot_success:
|
||||
remark += "(IoT降级)"
|
||||
|
||||
# 本地状态更新(不管 IoT 成功与否)
|
||||
svc.handle_alarm(
|
||||
alarm_id=alarm_id,
|
||||
alarm_status=alarm_status,
|
||||
handle_status=handle_status,
|
||||
handler=user_id,
|
||||
remark=remark,
|
||||
)
|
||||
|
||||
# 更新企微卡片
|
||||
card_action = action if action != "confirm" else "confirm"
|
||||
await _update_wechat_card(alarm_id, user_id, card_action)
|
||||
|
||||
action_labels = {
|
||||
"confirm": "已确认接单",
|
||||
"ignore": "已忽略",
|
||||
"complete": "已处理完成",
|
||||
"false": "已标记误报",
|
||||
}
|
||||
return json.dumps(
|
||||
{"success": True, "message": f"{action_labels[action]}: {alarm_id}"},
|
||||
ensure_ascii=False,
|
||||
)
|
||||
@@ -1,196 +0,0 @@
|
||||
"""
|
||||
告警查询工具:统计、列表、详情
|
||||
"""
|
||||
|
||||
import json
|
||||
from datetime import timedelta
|
||||
from typing import Optional
|
||||
from langchain_core.tools import tool
|
||||
from langchain_core.runnables import RunnableConfig
|
||||
|
||||
from app.utils.logger import logger
|
||||
from app.utils.timezone import beijing_now
|
||||
|
||||
|
||||
# 告警类型/级别/状态 中文映射
|
||||
ALARM_TYPE_NAMES = {
|
||||
"leave_post": "人员离岗", "intrusion": "周界入侵",
|
||||
"illegal_parking": "车辆违停", "vehicle_congestion": "车辆拥堵",
|
||||
}
|
||||
ALARM_LEVEL_NAMES = {0: "紧急", 1: "重要", 2: "普通", 3: "轻微"}
|
||||
ALARM_STATUS_NAMES = {
|
||||
"NEW": "待处理", "CONFIRMED": "处理中",
|
||||
"FALSE": "误报", "CLOSED": "已关闭",
|
||||
}
|
||||
|
||||
|
||||
def _parse_time_range(time_range: str):
|
||||
"""解析时间范围,返回 (start_time, label)"""
|
||||
now = beijing_now()
|
||||
if time_range == "week":
|
||||
start = now - timedelta(days=now.weekday())
|
||||
start = start.replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
return start, "本周"
|
||||
elif time_range == "month":
|
||||
start = now.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
|
||||
return start, "本月"
|
||||
else:
|
||||
start = now.replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
return start, "今日"
|
||||
|
||||
|
||||
def _get_camera_display_name(device_id: str) -> str:
|
||||
"""同步获取摄像头显示名称"""
|
||||
try:
|
||||
import asyncio
|
||||
from app.services.camera_name_service import get_camera_name_service
|
||||
camera_service = get_camera_name_service()
|
||||
loop = asyncio.get_event_loop()
|
||||
if loop.is_running():
|
||||
import concurrent.futures
|
||||
with concurrent.futures.ThreadPoolExecutor() as pool:
|
||||
cam_info = pool.submit(
|
||||
asyncio.run, camera_service.get_camera_info(device_id)
|
||||
).result(timeout=5)
|
||||
else:
|
||||
cam_info = asyncio.run(camera_service.get_camera_info(device_id))
|
||||
return camera_service.format_display_name(device_id, cam_info)
|
||||
except Exception:
|
||||
return device_id
|
||||
|
||||
|
||||
@tool
|
||||
def query_alarm_stats(time_range: str = "today", alarm_type: str = "all") -> str:
|
||||
"""查询告警统计数据(总数、按类型分布、按状态分布)
|
||||
|
||||
Args:
|
||||
time_range: 时间范围 today=今日 week=本周 month=本月
|
||||
alarm_type: 告警类型筛选 leave_post/intrusion/illegal_parking/vehicle_congestion/all
|
||||
"""
|
||||
from app.services.alarm_event_service import get_alarm_event_service
|
||||
svc = get_alarm_event_service()
|
||||
|
||||
start, range_label = _parse_time_range(time_range)
|
||||
now = beijing_now()
|
||||
|
||||
alarm_type_filter = None if alarm_type == "all" else alarm_type
|
||||
|
||||
alarms, total = svc.get_alarms(
|
||||
alarm_type=alarm_type_filter,
|
||||
start_time=start,
|
||||
end_time=now,
|
||||
page=1,
|
||||
page_size=10000,
|
||||
)
|
||||
|
||||
type_count = {}
|
||||
status_count = {"NEW": 0, "CONFIRMED": 0, "FALSE": 0, "CLOSED": 0}
|
||||
for a in alarms:
|
||||
type_count[a.alarm_type] = type_count.get(a.alarm_type, 0) + 1
|
||||
if a.alarm_status in status_count:
|
||||
status_count[a.alarm_status] += 1
|
||||
|
||||
result = {
|
||||
"range": range_label,
|
||||
"total": total,
|
||||
"by_type": {ALARM_TYPE_NAMES.get(t, t): c for t, c in type_count.items()},
|
||||
"by_status": {ALARM_STATUS_NAMES.get(s, s): c for s, c in status_count.items()},
|
||||
}
|
||||
return json.dumps(result, ensure_ascii=False)
|
||||
|
||||
|
||||
@tool
|
||||
def list_alarms(
|
||||
time_range: str = "today",
|
||||
alarm_type: str = "all",
|
||||
alarm_status: str = "",
|
||||
limit: int = 10,
|
||||
) -> str:
|
||||
"""查询告警列表,返回最近的告警记录(含ID、类型、摄像头、状态、时间)
|
||||
|
||||
Args:
|
||||
time_range: 时间范围 today/week/month
|
||||
alarm_type: 告警类型筛选 leave_post/intrusion/illegal_parking/vehicle_congestion/all
|
||||
alarm_status: 告警状态筛选 NEW=待处理 CONFIRMED=处理中 FALSE=误报 CLOSED=已关闭
|
||||
limit: 返回条数,默认10,最多20
|
||||
"""
|
||||
from app.services.alarm_event_service import get_alarm_event_service
|
||||
svc = get_alarm_event_service()
|
||||
|
||||
start, range_label = _parse_time_range(time_range)
|
||||
now = beijing_now()
|
||||
|
||||
alarm_type_filter = None if alarm_type == "all" else alarm_type
|
||||
status_filter = alarm_status if alarm_status else None
|
||||
limit = min(limit, 20)
|
||||
|
||||
alarms, total = svc.get_alarms(
|
||||
alarm_type=alarm_type_filter,
|
||||
alarm_status=status_filter,
|
||||
start_time=start,
|
||||
end_time=now,
|
||||
page=1,
|
||||
page_size=limit,
|
||||
)
|
||||
|
||||
items = []
|
||||
for a in alarms:
|
||||
cam_name = _get_camera_display_name(a.device_id)
|
||||
event_time = ""
|
||||
if a.event_time:
|
||||
try:
|
||||
event_time = a.event_time.strftime("%m-%d %H:%M")
|
||||
except Exception:
|
||||
event_time = str(a.event_time)[:16]
|
||||
items.append({
|
||||
"alarm_id": a.alarm_id,
|
||||
"type": ALARM_TYPE_NAMES.get(a.alarm_type, a.alarm_type),
|
||||
"camera": cam_name,
|
||||
"status": ALARM_STATUS_NAMES.get(a.alarm_status, a.alarm_status),
|
||||
"level": ALARM_LEVEL_NAMES.get(a.alarm_level, "普通"),
|
||||
"time": event_time,
|
||||
})
|
||||
|
||||
result = {"range": range_label, "total": total, "items": items}
|
||||
return json.dumps(result, ensure_ascii=False)
|
||||
|
||||
|
||||
@tool
|
||||
def get_alarm_detail(alarm_id: str, config: RunnableConfig) -> str:
|
||||
"""查询单条告警的详细信息(含扩展信息和AI分析结果)
|
||||
|
||||
Args:
|
||||
alarm_id: 告警ID(如 edge_xxx 或 ALM_xxx)
|
||||
"""
|
||||
from app.services.alarm_event_service import get_alarm_event_service
|
||||
svc = get_alarm_event_service()
|
||||
|
||||
detail = svc.get_alarm(alarm_id)
|
||||
if not detail:
|
||||
return json.dumps({"error": f"未找到告警: {alarm_id}"}, ensure_ascii=False)
|
||||
|
||||
snapshot_url = detail.get("snapshot_url", "")
|
||||
|
||||
result = {
|
||||
"alarm_id": detail.get("alarm_id"),
|
||||
"alarm_type": ALARM_TYPE_NAMES.get(detail.get("alarm_type", ""), detail.get("alarm_type", "")),
|
||||
"device_id": detail.get("device_id"),
|
||||
"alarm_status": ALARM_STATUS_NAMES.get(detail.get("alarm_status", ""), detail.get("alarm_status", "")),
|
||||
"alarm_level": ALARM_LEVEL_NAMES.get(detail.get("alarm_level"), "普通"),
|
||||
"event_time": str(detail.get("event_time", ""))[:19],
|
||||
"handle_status": detail.get("handle_status"),
|
||||
"handler": detail.get("handler"),
|
||||
"has_snapshot": bool(snapshot_url),
|
||||
"snapshot_url": snapshot_url,
|
||||
}
|
||||
|
||||
# 摄像头名称
|
||||
result["camera_name"] = _get_camera_display_name(detail.get("device_id", ""))
|
||||
|
||||
# LLM 分析
|
||||
analyses = detail.get("llm_analyses", [])
|
||||
if analyses:
|
||||
latest = analyses[-1]
|
||||
result["ai_analysis"] = latest.get("summary", "")
|
||||
|
||||
return json.dumps(result, ensure_ascii=False)
|
||||
@@ -1,149 +0,0 @@
|
||||
"""
|
||||
工单工具:查询我的工单、提交处理结果
|
||||
"""
|
||||
|
||||
import json
|
||||
from typing import List, Optional
|
||||
from langchain_core.tools import tool
|
||||
from langchain_core.runnables import RunnableConfig
|
||||
|
||||
from app.utils.logger import logger
|
||||
from .alarm_query import ALARM_TYPE_NAMES, _get_camera_display_name
|
||||
from .alarm_action import _get_order_id_for_alarm
|
||||
|
||||
|
||||
@tool
|
||||
def list_my_orders(config: RunnableConfig, status: str = "HANDLING") -> str:
|
||||
"""查询我的待处理工单列表
|
||||
|
||||
Args:
|
||||
status: 工单状态筛选 HANDLING=处理中 ALL=全部
|
||||
"""
|
||||
from app.services.alarm_event_service import get_alarm_event_service
|
||||
svc = get_alarm_event_service()
|
||||
|
||||
user_id = config.get("configurable", {}).get("user_id", "")
|
||||
|
||||
alarms, total = svc.get_alarms(
|
||||
alarm_status="CONFIRMED",
|
||||
page=1,
|
||||
page_size=20,
|
||||
)
|
||||
|
||||
my_alarms = [a for a in alarms if a.handler == user_id]
|
||||
|
||||
if not my_alarms:
|
||||
return json.dumps({"total": 0, "items": [], "message": "当前没有待处理的工单"}, ensure_ascii=False)
|
||||
|
||||
items = []
|
||||
for a in my_alarms:
|
||||
cam_name = _get_camera_display_name(a.device_id)
|
||||
event_time = ""
|
||||
if a.event_time:
|
||||
try:
|
||||
event_time = a.event_time.strftime("%m-%d %H:%M")
|
||||
except Exception:
|
||||
event_time = str(a.event_time)[:16]
|
||||
items.append({
|
||||
"alarm_id": a.alarm_id,
|
||||
"type": ALARM_TYPE_NAMES.get(a.alarm_type, a.alarm_type),
|
||||
"camera": cam_name,
|
||||
"time": event_time,
|
||||
})
|
||||
|
||||
return json.dumps({"total": len(my_alarms), "items": items}, ensure_ascii=False)
|
||||
|
||||
|
||||
@tool
|
||||
async def submit_order_result(
|
||||
alarm_id: str,
|
||||
result_text: str,
|
||||
config: RunnableConfig,
|
||||
image_urls: Optional[List[str]] = None,
|
||||
) -> str:
|
||||
"""提交工单处理结果(文字描述+处理后照片URL)
|
||||
|
||||
Args:
|
||||
alarm_id: 关联的告警ID
|
||||
result_text: 处理结果描述
|
||||
image_urls: 处理后照片URL列表(COS永久URL)
|
||||
"""
|
||||
from app.services.alarm_event_service import get_alarm_event_service
|
||||
from app.services.wechat_service import get_wechat_service
|
||||
|
||||
user_id = config.get("configurable", {}).get("user_id", "")
|
||||
svc = get_alarm_event_service()
|
||||
wechat = get_wechat_service()
|
||||
|
||||
if image_urls is None:
|
||||
image_urls = []
|
||||
|
||||
# 检查告警是否存在
|
||||
detail = svc.get_alarm(alarm_id)
|
||||
if not detail:
|
||||
return json.dumps({"error": f"未找到告警: {alarm_id}"}, ensure_ascii=False)
|
||||
|
||||
# 合并 session 中暂存的图片
|
||||
from app.services.session_manager import get_session_manager
|
||||
session = get_session_manager().get(user_id)
|
||||
if session.pending_images:
|
||||
image_urls = session.pending_images + image_urls
|
||||
session.pending_images = []
|
||||
|
||||
# 获取关联工单ID
|
||||
order_id = _get_order_id_for_alarm(alarm_id)
|
||||
|
||||
from app.services.work_order_client import get_work_order_client
|
||||
wo_client = get_work_order_client()
|
||||
|
||||
remark = f"企微Agent结单: {result_text}"
|
||||
if image_urls:
|
||||
remark += f" (附{len(image_urls)}张图片)"
|
||||
|
||||
if order_id and wo_client.enabled:
|
||||
if not await wo_client.submit_order(
|
||||
order_id,
|
||||
result=f"{result_text} by {user_id}",
|
||||
result_img_urls=image_urls or None,
|
||||
):
|
||||
remark += "(IoT降级)"
|
||||
|
||||
svc.handle_alarm(alarm_id=alarm_id, alarm_status="CLOSED",
|
||||
handle_status="DONE", handler=user_id, remark=remark)
|
||||
|
||||
# 持久化处理结果图片到 alarm_event_ext
|
||||
if image_urls:
|
||||
try:
|
||||
from app.models import get_session as get_db_session, AlarmEventExt
|
||||
db = get_db_session()
|
||||
try:
|
||||
ext = AlarmEventExt(
|
||||
alarm_id=alarm_id,
|
||||
ext_type="HANDLER_RESULT",
|
||||
ext_data={"result_text": result_text, "image_urls": image_urls, "handler": user_id},
|
||||
)
|
||||
db.add(ext)
|
||||
db.commit()
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
logger.error(f"持久化处理结果图片失败: {e}")
|
||||
finally:
|
||||
db.close()
|
||||
except Exception as e:
|
||||
logger.error(f"保存处理结果失败: {e}")
|
||||
|
||||
# 更新卡片到终态
|
||||
response_code = wechat.get_response_code(alarm_id)
|
||||
if response_code:
|
||||
await wechat.update_alarm_card_terminal(
|
||||
response_code=response_code, user_ids=[user_id],
|
||||
alarm_id=alarm_id, action="complete", operator_name=user_id,
|
||||
)
|
||||
|
||||
result = {
|
||||
"success": True,
|
||||
"message": f"工单已提交: {alarm_id}",
|
||||
"result": result_text,
|
||||
"images_count": len(image_urls),
|
||||
}
|
||||
return json.dumps(result, ensure_ascii=False)
|
||||
Reference in New Issue
Block a user