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
+30 -64
View File
@@ -12,7 +12,6 @@
# =============================================================================
import logging
from collections import OrderedDict
from typing import Any, Dict, List, Optional
import redis.asyncio as aioredis
@@ -23,7 +22,11 @@ from app.api.agents import get_current_agent
from app.database import get_db
from app.dependencies import dep_redis
from app.models.agent import Agent
from app.services.employee_directory import get_org_directory
from app.services.employee_directory import (
count_tree_employees,
get_org_directory,
get_org_tree_cached,
)
from app.utils.response import AppException, success_response
logger = logging.getLogger(__name__)
@@ -63,7 +66,7 @@ async def search_employees(
return success_response(data=[])
try:
# 获取组织目录(含 10 分钟 Redis 缓存 + 本地降级,无需修改 employee_directory.py
# 获取组织目录(含 30 分钟 Redis 缓存 + 本地降级)
directory, _ = await get_org_directory(db, redis)
kw_lower = kw.lower()
@@ -107,24 +110,34 @@ async def get_org_tree(
):
"""获取组织架构树(部门层级 + 每个部门下的员工列表)。
复用 get_org_directory() 获取员工列表(已含 department 字段
在服务端按 department 分组构建树结构。排除当前登录坐席自己。
利用企微 department/list 返回的 parentid 字段构建真正的层级树
不再按部门名扁平分组。部门ID加 ``dept_`` 前缀作为唯一 key
避免同名部门合并。员工可以出现在其所属的所有部门下。
树结构示例:
树结构示例(多层级)
[
{
"id": "研发一部",
"label": "研发一部",
"id": "dept_1",
"label": "公司",
"dept_id": 1,
"parentid": 0,
"children": [
{"id": "zhangsan", "label": "张三", "isLeaf": true, "department": "研发一部"}
{
"id": "dept_2",
"label": "研发一部",
"dept_id": 2,
"parentid": 1,
"children": [
{"id": "zhangsan", "label": "张三", "isLeaf": true, "department": "研发一部"}
]
}
]
}
]
规则
- department 为空的员工归到"未分配部门"分组
- 企微返回多部门(逗号分隔)时,取第一个作为主部门
- 部门按名称排序,部门内员工按姓名排序
性能优化
- 树构建结果独立缓存(key: wecom:org_tree:agentTTL 30 分钟)
- 缓存中包含所有员工,读取后过滤掉当前登录坐席自己
Args:
current_agent: 当前坐席
@@ -135,58 +148,11 @@ async def get_org_tree(
Dict: 统一响应格式,data 为树节点列表
"""
try:
# 获取组织目录(含 Redis 缓存 + 本地降级
directory, _ = await get_org_directory(db, redis)
# 获取组织架构树(含独立缓存 + 排除当前坐席
tree = await get_org_tree_cached(db, redis, "agent", current_agent.user_id)
# 按部门分组(OrderedDict 保持稳定插入顺序,后续再排序)
dept_groups: "OrderedDict[str, List[Dict[str, Any]]]" = OrderedDict()
for emp in directory:
# 排除当前坐席自己
if emp.get("employee_id") == current_agent.user_id:
continue
# 取部门名:为空则归"未分配部门";多部门(逗号分隔)取第一个
dept = (emp.get("department") or "").strip()
if not dept:
dept = "未分配部门"
else:
dept = dept.split(",")[0].strip()
if not dept:
dept = "未分配部门"
if dept not in dept_groups:
dept_groups[dept] = []
dept_groups[dept].append(emp)
# 构建树节点(部门按名称排序,员工按姓名排序)
tree: List[Dict[str, Any]] = []
for dept_name in sorted(dept_groups.keys()):
employees = dept_groups[dept_name]
if not employees:
# 跳过空部门(理论上不会出现,防御性编程)
continue
# 部门内员工按姓名排序
employees.sort(key=lambda e: e.get("name", ""))
tree.append({
"id": dept_name,
"label": dept_name,
"children": [
{
"id": emp.get("employee_id", ""),
"label": emp.get("name", ""),
# isLeaf=true 标记为叶子节点(员工),前端 el-tree 据此区分部门/员工
"isLeaf": True,
"department": dept_name,
}
for emp in employees
],
})
total_employees = sum(len(node["children"]) for node in tree)
logger.info(f"组织架构树: {len(tree)} 个部门, 共 {total_employees}")
total_employees = count_tree_employees(tree)
logger.info(f"组织架构树: {len(tree)} 个顶层节点, 共 {total_employees}")
return success_response(data=tree)
except AppException: