bea288e414
== 已部署上线 (9项) == - 代办事项真实数据源集成 (企微审批API 8bug修复链) - H5/坐席端 Logo样式统一+绿色背景 - 视频引导页修复 (localStorage key v2) - 坐席端 v9 Vue版本修复 (ElMessage._context) - 截图按钮 v10 修复 (getDisplayMedia user gesture) - 扫码样式恢复+H5扫码登录跳转修复 - H5截图快捷键提示 == 代码完成待部署 (3项) == - 知识迭代3Bug修复 (#8 POST端点/#7 MERGE幂等/#6 过期检查) - 会议室预定-小鱼易联终端 (40文件, 40/40测试通过) - IT资产升级审批推送 (asset_service.py) == 需求文档 (2项) == - 坐席端AI辅助消息框-PRD (4项新功能确认) - 坐席端布局优化建议 v2.0 (7天计划) == 新增文档 == - 日报-2026-07-11.md - 知识迭代Bug修复报告-20260711.md - 会议室预定-部署指南.md - CHANGELOG.md 更新 == 测试 == - test_todo_integration.py: 40/40 - test_meetingroom.py: 40/40 - test_bugfix_ki_suggestions.py: 21/21
206 lines
7.1 KiB
Python
206 lines
7.1 KiB
Python
# =============================================================================
|
||
# 企微IT智能服务台 — 待办事项 API
|
||
# =============================================================================
|
||
# 说明:提供待办事项的查询接口
|
||
# 接口列表:
|
||
# GET /api/todo-items — 获取当前坐席待办列表(聚合企微审批 + ITSM 工单)
|
||
# GET /api/todo-items/{id} — 获取待办详情(按 ID 前缀路由到对应数据源)
|
||
# PUT /api/todo-items/{id}/status — 更新待办状态(仅展示模式,不支持实际操作)
|
||
#
|
||
# 数据来源:
|
||
# - 企微审批(getapprovaldata + getapprovaldetail)
|
||
# - ITSM 运维平台(OpenAPI workitem/detail)
|
||
# 通过 TodoAggregatorService 聚合,Redis 缓存 TTL 45s。
|
||
# =============================================================================
|
||
|
||
import logging
|
||
from typing import Optional
|
||
|
||
from fastapi import APIRouter, Depends, Query
|
||
from pydantic import BaseModel, Field
|
||
|
||
from app.api.agents import get_current_agent
|
||
from app.config import settings
|
||
from app.models.agent import Agent
|
||
from app.services.todo_aggregator_service import TodoAggregatorService
|
||
from app.utils.response import AppException, success_response
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
# 创建路由器
|
||
router = APIRouter(prefix="/todo-items", tags=["待办事项"])
|
||
|
||
|
||
# --------------------------------------------------------------------------
|
||
# 请求 Schema
|
||
# --------------------------------------------------------------------------
|
||
|
||
class TodoStatusUpdateRequest(BaseModel):
|
||
"""更新待办状态请求 Schema。
|
||
|
||
⚠️ 仅展示模式:当前版本不支持在服务台内直接操作审批/工单状态,
|
||
代办状态更新需跳转到原系统(企微审批 / ITSM)操作。
|
||
"""
|
||
status: str = Field(..., description="新状态: pending/processing/resolved")
|
||
|
||
|
||
# --------------------------------------------------------------------------
|
||
# 辅助函数
|
||
# --------------------------------------------------------------------------
|
||
|
||
def _get_redis():
|
||
"""获取 Redis 客户端实例。"""
|
||
return settings.create_redis_client()
|
||
|
||
|
||
async def _close_redis(redis_client) -> None:
|
||
"""安全关闭 Redis 客户端连接。"""
|
||
try:
|
||
await redis_client.close()
|
||
except Exception:
|
||
pass
|
||
|
||
|
||
# --------------------------------------------------------------------------
|
||
# API 接口
|
||
# --------------------------------------------------------------------------
|
||
|
||
@router.get("")
|
||
async def list_todo_items(
|
||
type: Optional[str] = Query(None, description="按类型过滤: approval/ticket"),
|
||
_force: Optional[int] = Query(None, description="强制刷新(1=跳过缓存)"),
|
||
agent: Agent = Depends(get_current_agent),
|
||
):
|
||
"""获取当前坐席待办列表。
|
||
|
||
聚合企微审批和 ITSM 工单两个数据源,返回统一格式的待办列表。
|
||
支持 type 参数按类型过滤,默认返回全部。
|
||
支持 _force=1 参数强制跳过缓存重新查询。
|
||
|
||
流程:
|
||
1. 从认证依赖获取当前坐席 (agent.user_id)
|
||
2. 如 _force=1,先清除该坐席的所有待办缓存
|
||
3. 调用 TodoAggregatorService.get_todo_list()
|
||
- 先查 Redis 缓存(TTL 45s),命中直接返回
|
||
- 未命中则并行查询企微审批 + ITSM 工单
|
||
- 按优先级排序 urgent → high → normal
|
||
4. 返回 {items, total, cached} 格式
|
||
|
||
Args:
|
||
type: 类型过滤(approval/ticket),不传则返回全部
|
||
_force: 强制刷新(1=跳过缓存,重新查询外部 API)
|
||
agent: 当前坐席(通过认证依赖注入)
|
||
|
||
Returns:
|
||
Dict: 统一响应格式 {code:0, data:{items, total, cached}}
|
||
"""
|
||
redis_client = _get_redis()
|
||
try:
|
||
aggregator = TodoAggregatorService(redis_client)
|
||
|
||
# 强制刷新:先清除缓存
|
||
if _force == 1:
|
||
await aggregator._invalidate_cache(agent.user_id)
|
||
|
||
result = await aggregator.get_todo_list(
|
||
agent_userid=agent.user_id,
|
||
todo_type=type,
|
||
)
|
||
return success_response(data=result)
|
||
except Exception as e:
|
||
logger.error(f"获取待办列表失败: agent={agent.user_id}, error={e}", exc_info=True)
|
||
return success_response(data={"items": [], "total": 0, "cached": False})
|
||
finally:
|
||
await _close_redis(redis_client)
|
||
|
||
|
||
@router.get("/{item_id}")
|
||
async def get_todo_item(
|
||
item_id: str,
|
||
agent: Agent = Depends(get_current_agent),
|
||
):
|
||
"""获取待办事项详情。
|
||
|
||
从 item_id 解析类型前缀(approval:{sp_no} / ticket:{id}),
|
||
路由到对应数据源 Service 查询详情。
|
||
|
||
ID 格式约定:
|
||
- 审批:approval:{sp_no}(如 approval:202607110001)
|
||
- 工单:ticket:{process_instance_id}(如 ticket:12345)
|
||
|
||
Args:
|
||
item_id: 待办 ID(格式:{type}:{原始ID})
|
||
agent: 当前坐席(通过认证依赖注入)
|
||
|
||
Returns:
|
||
Dict: 统一响应格式 {code:0, data:{item: TodoItemData}}
|
||
"""
|
||
redis_client = _get_redis()
|
||
try:
|
||
aggregator = TodoAggregatorService(redis_client)
|
||
item = await aggregator.get_todo_detail(
|
||
agent_userid=agent.user_id,
|
||
item_id=item_id,
|
||
)
|
||
if item is None:
|
||
raise AppException(code=1003, message=f"待办事项 {item_id} 不存在")
|
||
|
||
return success_response(data=item)
|
||
except AppException:
|
||
raise
|
||
except Exception as e:
|
||
logger.error(f"获取待办详情失败: item_id={item_id}, error={e}", exc_info=True)
|
||
raise AppException(code=1005, message=f"获取待办详情失败: {e}")
|
||
finally:
|
||
await _close_redis(redis_client)
|
||
|
||
|
||
@router.put("/{item_id}/status")
|
||
async def update_todo_item_status(
|
||
item_id: str,
|
||
request: TodoStatusUpdateRequest,
|
||
agent: Agent = Depends(get_current_agent),
|
||
):
|
||
"""更新待办事项状态(仅展示模式)。
|
||
|
||
⚠️ 当前版本为"仅展示模式":不支持在服务台内直接操作审批/工单状态。
|
||
代办状态更新需跳转到原系统操作:
|
||
- 审批 → 在企微审批中操作
|
||
- 工单 → 在 ITSM 中操作
|
||
|
||
保留此接口用于前端兼容,实际不修改任何状态。
|
||
|
||
Args:
|
||
item_id: 待办 ID
|
||
request: 状态更新请求
|
||
agent: 当前坐席
|
||
|
||
Returns:
|
||
Dict: 提示用户跳转到原系统操作
|
||
"""
|
||
# 校验状态值
|
||
valid_statuses = {"pending", "processing", "resolved"}
|
||
if request.status not in valid_statuses:
|
||
raise AppException(
|
||
code=1001,
|
||
message=f"无效的状态值: {request.status},合法值为: {valid_statuses}",
|
||
)
|
||
|
||
# 解析类型前缀,提供对应的跳转提示
|
||
todo_type = item_id.split(":", 1)[0] if ":" in item_id else "unknown"
|
||
if todo_type == "approval":
|
||
system_name = "企微审批"
|
||
elif todo_type == "ticket":
|
||
system_name = "ITSM 运维平台"
|
||
else:
|
||
system_name = "原系统"
|
||
|
||
return success_response(
|
||
data={
|
||
"item_id": item_id,
|
||
"status": request.status,
|
||
"mode": "display_only",
|
||
"message": f"当前为仅展示模式,请在{system_name}中操作",
|
||
}
|
||
)
|