v3.1 + 批次0: 智能回复重构基线 - ApprovalMatcher + 关键词降级 + 文档速修 + v4.0任务书面化

This commit is contained in:
Simon
2026-07-17 23:08:59 +08:00
parent 5a77a89ab1
commit 3ed86d5fb3
181 changed files with 19738 additions and 2655 deletions
+97 -3
View File
@@ -19,12 +19,14 @@ from datetime import datetime
from typing import Optional
from uuid import UUID
from fastapi import APIRouter, Depends, Query
from fastapi import APIRouter, Depends, Form, Query, UploadFile
from sqlalchemy.ext.asyncio import AsyncSession
from app.api.agents import get_current_agent
from app.database import get_db
from app.models.agent import Agent
from app.models.device_inventory import DeviceInventory
from app.services.device_import_service import DeviceImportService
from app.schemas.admin import (
AgentCreateRequest,
AgentUpdateRequest,
@@ -771,7 +773,7 @@ async def test_lianruan_connection(
Dict: 包含 success(bool) 和 message(str)
"""
from app.integrations.lianruan.config import get_lianruan_client
from app.integrations.lianruan.exceptions import LianruanConfigError
from app.integrations.lianruan.exceptions import LianruanConfigError, LianruanAuthError, LianruanConnectionError
try:
client = await get_lianruan_client(db)
@@ -782,6 +784,16 @@ async def test_lianruan_connection(
"success": False,
"message": e.message,
})
except LianruanAuthError as e:
return success_response(data={
"success": False,
"message": f"认证失败: {e.message}",
})
except LianruanConnectionError as e:
return success_response(data={
"success": False,
"message": f"连接失败: {e.message}",
})
# ---------- GET /api/admin/integrations/lianruan/terminals ----------
@@ -843,6 +855,7 @@ async def list_audit_conversations(
keyword: Optional[str] = Query(None, description="按员工姓名/消息摘要搜索"),
date_from: Optional[str] = Query(None, description="开始日期 YYYY-MM-DD"),
date_to: Optional[str] = Query(None, description="结束日期 YYYY-MM-DD"),
is_archived: Optional[bool] = Query(None, description="按是否归档筛选"),
page: int = Query(1, ge=1, description="页码"),
page_size: int = Query(20, ge=1, le=100, description="每页条数"),
admin: Agent = Depends(require_admin),
@@ -851,7 +864,8 @@ async def list_audit_conversations(
"""获取会话审计列表(支持分页+多条件筛选)。"""
result = await admin_service.list_audit_conversations(
db, status=status, agent_id=agent_id, keyword=keyword,
date_from=date_from, date_to=date_to, page=page, page_size=page_size,
date_from=date_from, date_to=date_to, is_archived=is_archived,
page=page, page_size=page_size,
)
return success_response(data=result)
@@ -1108,3 +1122,83 @@ async def revoke_user_token(
"revoked_count": revoked_count,
"message": f"已撤销 {revoked_count} 个 Token" if revoked_count > 0 else "未找到该用户的有效 Token",
})
# ==========================================================================
# 15. 设备清单导入(从联软/火绒导出文件)
# ==========================================================================
# ---------- POST /api/admin/device-inventory/import ----------
@router.post("/device-inventory/import")
async def import_device_inventory(
source: str = Form(..., description="数据来源: huorong 或 lianruan"),
file: UploadFile = Form(..., description="Excel 文件"),
admin: Agent = Depends(require_admin),
db: AsyncSession = Depends(get_db),
):
"""从 Excel 文件导入设备清单。
支持从联软/火绒导出的 Excel 文件中批量导入设备信息。
导入后会根据员工账号自动匹配员工信息。
"""
import pandas as pd
from io import BytesIO
# 读取文件内容
content = await file.read()
df = pd.read_excel(BytesIO(content))
# 转换为字典列表
records = df.to_dict(orient="records")
# 导入
import_service = DeviceImportService(db)
if source == "huorong":
count = await import_service.import_huorong(records)
elif source == "lianruan":
count = await import_service.import_lianruan(records)
else:
raise AppException(4001, "无效的数据来源,仅支持 huorong 或 lianruan")
return success_response(data={"imported": count, "source": source})
# ---------- GET /api/admin/device-inventory ----------
@router.get("/device-inventory")
async def list_device_inventory(
page: int = Query(1, ge=1, description="页码"),
page_size: int = Query(50, ge=1, le=200, description="每页条数"),
source: Optional[str] = Query(None, description="数据来源筛选"),
employee_account: Optional[str] = Query(None, description="员工账号筛选"),
admin: Agent = Depends(require_admin),
db: AsyncSession = Depends(get_db),
):
"""查询设备清单列表。"""
from sqlalchemy import select, func
query = select(DeviceInventory)
count_query = select(func.count(DeviceInventory.id))
if source:
query = query.where(DeviceInventory.source == source)
count_query = count_query.where(DeviceInventory.source == source)
if employee_account:
query = query.where(DeviceInventory.employee_account == employee_account)
count_query = count_query.where(DeviceInventory.employee_account == employee_account)
# 总数
total = await db.scalar(count_query)
# 分页
offset = (page - 1) * page_size
query = query.offset(offset).limit(page_size).order_by(DeviceInventory.updated_at.desc())
result = await db.execute(query)
items = result.scalars().all()
return success_response(data={
"items": [d.model_dump() for d in items],
"total": total,
"page": page,
"page_size": page_size,
})