v3.1 + 批次0: 智能回复重构基线 - ApprovalMatcher + 关键词降级 + 文档速修 + v4.0任务书面化
This commit is contained in:
+36
-63
@@ -24,7 +24,6 @@ import json
|
||||
import logging
|
||||
import re
|
||||
import secrets
|
||||
from collections import OrderedDict
|
||||
from datetime import datetime
|
||||
from typing import Any, Dict, List, Optional
|
||||
from urllib.parse import quote
|
||||
@@ -65,7 +64,11 @@ from app.tasks.h5_ai_task import process_h5_ai_reply
|
||||
from app.services.funny_phrase_service import FunnyPhraseService
|
||||
from app.services.ws_manager import manager as ws_manager
|
||||
from app.services.wecom_service import WecomService
|
||||
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, ERR_UNAUTHORIZED, success_response
|
||||
from app.services.closing_service import ClosingService
|
||||
from pydantic import BaseModel, Field
|
||||
@@ -919,7 +922,12 @@ async def h5_send_message(
|
||||
# WS 广播失败不阻塞消息存储,只记录 warning
|
||||
logger.warning(f"WS 广播用户消息失败(消息已存储): {ws_err}")
|
||||
|
||||
# 4. 启动后台 AI 任务(异步,不阻塞 HTTP 返回)
|
||||
# 4. 提交当前事务,确保后台任务能读到刚创建的 conversation/message
|
||||
# 为什么:asyncio.create_task 立即运行,若 HTTP 事务未提交,
|
||||
# 后台 DB session 会报"会话不存在"(race condition)
|
||||
await db.commit()
|
||||
|
||||
# 5. 启动后台 AI 任务(异步,不阻塞 HTTP 返回)
|
||||
# 为什么:AI 推理(Dify)慢(3~15s),放后台经 WS 流式推回,
|
||||
# 发送接口瞬时返回,前端不再卡"发送中"
|
||||
# 约束:后台任务使用独立 DB session,且需单 worker(见 h5_ai_task.py)
|
||||
@@ -1734,7 +1742,7 @@ async def h5_search_employees(
|
||||
return success_response(data=[])
|
||||
|
||||
try:
|
||||
# 获取组织目录(含 10 分钟 Redis 缓存 + 本地降级)
|
||||
# 获取组织目录(含 30 分钟 Redis 缓存 + 本地降级)
|
||||
directory, _ = await get_org_directory(db, redis)
|
||||
|
||||
kw_lower = kw.lower()
|
||||
@@ -1774,24 +1782,34 @@ async def h5_get_org_tree(
|
||||
):
|
||||
"""H5 员工端获取组织架构树(部门层级 + 每个部门下的员工列表)。
|
||||
|
||||
复用 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:h5,TTL 30 分钟)
|
||||
- 缓存中包含所有员工,读取后过滤掉当前登录员工自己
|
||||
|
||||
Args:
|
||||
employee_id: 当前登录员工ID
|
||||
@@ -1802,56 +1820,11 @@ async def h5_get_org_tree(
|
||||
Dict: 统一响应格式,data 为树节点列表
|
||||
"""
|
||||
try:
|
||||
# 获取组织目录(含 Redis 缓存 + 本地降级)
|
||||
directory, _ = await get_org_directory(db, redis)
|
||||
# 获取组织架构树(含独立缓存 + 排除当前员工)
|
||||
tree = await get_org_tree_cached(db, redis, "h5", employee_id)
|
||||
|
||||
# 按部门分组(OrderedDict 保持稳定插入顺序,后续再排序)
|
||||
dept_groups: "OrderedDict[str, List[Dict[str, Any]]]" = OrderedDict()
|
||||
|
||||
for emp in directory:
|
||||
# 排除当前员工自己
|
||||
if emp.get("employee_id") == employee_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,
|
||||
"department": dept_name,
|
||||
}
|
||||
for emp in employees
|
||||
],
|
||||
})
|
||||
|
||||
total_employees = sum(len(node["children"]) for node in tree)
|
||||
logger.info(f"H5组织架构树: {len(tree)} 个部门, 共 {total_employees} 人")
|
||||
total_employees = count_tree_employees(tree)
|
||||
logger.info(f"H5组织架构树: {len(tree)} 个顶层节点, 共 {total_employees} 人")
|
||||
return success_response(data=tree)
|
||||
|
||||
except AppException:
|
||||
|
||||
Reference in New Issue
Block a user