v3.1 + 批次0: 智能回复重构基线 - ApprovalMatcher + 关键词降级 + 文档速修 + v4.0任务书面化
This commit is contained in:
@@ -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,
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user