Files
wecom_it_smart_desk/backend/app/api/todo_items.py
T
Simon bea288e414 feat: 2026-07-11 全量更新 - 代办集成+会议室预定+知识迭代修复+UI统一+Bug修复
== 已部署上线 (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
2026-07-11 23:13:10 +08:00

206 lines
7.1 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.
# =============================================================================
# 企微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}中操作",
}
)