Skip to content

feat: 在数字员工和公司设置页面添加工具批量删除功能,修复企业微信Webhook配置#277

Open
ThomasOscar wants to merge 8 commits intodataelement:mainfrom
ThomasOscar:main
Open

feat: 在数字员工和公司设置页面添加工具批量删除功能,修复企业微信Webhook配置#277
ThomasOscar wants to merge 8 commits intodataelement:mainfrom
ThomasOscar:main

Conversation

@ThomasOscar
Copy link
Copy Markdown

@ThomasOscar ThomasOscar commented Apr 3, 2026

功能1:Agent工具批量删除

功能位置

  • 数字员工详情页 → 工具 → 数字员工自行安装的工具栏
  • 公司设置 → 工具 → Agent安装栏

新增功能

  • ✅ 批量选择工具(多选框)
  • ✅ 批量删除API (DELETE /api/tools/agent-tools/bulk)
  • ✅ 确认弹窗提示
  • ✅ 权限验证 (admin/super_admin)
  • ✅ 租户隔离检查
  • ✅ 部分失败处理机制
  • ✅ 操作日志记录

文件修改

  • backend/app/api/tools.py - 批量删除API实现
  • frontend/src/pages/AgentDetail.tsx - 数字员工页面批量删除UI
  • frontend/src/pages/EnterpriseSettings.tsx - 公司设置页面批量删除UI
  • frontend/src/i18n/zh.json - 中文翻译

功能2:企业微信Webhook配置修复

问题

  • 配置Webhook模式后仍显示WebSocket状态
  • Webhook URL无法正确显示

解决方案

  • 后端:优先使用前端指定的connection_mode
  • 后端:添加消息发送日志
  • 前端:Webhook模式状态显示
  • 前端:优雅处理API失败

文件修改

  • backend/app/api/wecom.py - 连接模式优先级优化
  • frontend/src/components/ChannelConfig.tsx - Webhook状态显示

测试

工具批量删除

  1. 进入数字员工详情页或公司设置页
  2. 选择多个Agent工具
  3. 点击批量删除按钮
  4. 确认删除操作
  5. 验证删除成功

企业微信配置

  1. WebSocket模式:Bot ID + Secret → 显示WebSocket状态
  2. Webhook模式:CorpID + Secret + Token + EncodingAESKey → 显示Webhook URL

Summary

Checklist

  • Tested locally
  • No unrelated changes included

- 新增 DELETE /api/tools/agent-tools/bulk API,支持批量删除 Agent 工具分配
- 添加 admin/super_admin 角色权限验证和租户隔离检查
- 实现部分失败处理机制,返回 deleted 数量和 errors 数组
- 添加请求数量限制(最多50条),防止滥用
- 添加操作日志记录,含时间戳便于审计追踪
- 前端 AgentDetail.tsx 添加批量选择 UI 和确认弹窗
- 前端 EnterpriseSettings.tsx 添加批量删除 UI
- 新增国际化翻译 keys
问题:
- 配置Webhook模式后仍显示WebSocket连接状态
- Webhook URL无法正确显示

解决方案:
- 后端:优先使用前端指定的connection_mode,避免自动检测错误
- 后端:添加消息发送结果日志,便于调试
- 前端:添加Webhook模式状态显示
- 前端:优雅处理Webhook URL获取失败
- 前端:确保查询失效完成再重置表单

测试:
- WebSocket模式:使用Bot ID + Secret配置,显示WebSocket状态
- Webhook模式:使用CorpID + Secret + Token + EncodingAESKey配置,显示Webhook URL
@ThomasOscar ThomasOscar changed the title feat: 在数字员工(例如默认Meeseeks或者Morty)-工具-数字员工自行安装的工具栏页面 和 公司设置-工具-Agent安装栏页面:添加批量勾选删除 Agent 工具功能。 feat: 在数字员工和公司设置页面添加工具批量删除功能,修复企业微信Webhook配置 Apr 3, 2026
@wisdomqin
Copy link
Copy Markdown
Contributor

Thanks for the PR! The WeCom webhook fix looks good, but the bulk delete feature has several issues that need to be resolved before we can merge.

Required Fixes

1. Wrong role names — bulk delete endpoint returns 403 for all valid admins

The permission check uses role names that don't exist in our system:

# Current (broken) — these roles don't exist
if current_user.role not in ("admin", "super_admin"):
    ...
if current_user.role != "super_admin" and ...:
    ...

Our platform uses org_admin and platform_admin. Please update both checks:

if current_user.role not in ("org_admin", "platform_admin"):
    ...
if current_user.role != "platform_admin" and ...:
    ...

2. Async lazy-loading crash (MissingGreenlet)

Accessing at.agent.tenant_id will crash at runtime because async SQLAlchemy does not support lazy loading. You need to eagerly load the agent relationship:

at_r = await db.execute(
    select(AgentTool)
    .where(AgentTool.id == agent_tool_id)
    .options(selectinload(AgentTool.agent))  # add this
)

3. N+1 query problem

The loop issues one DB query per ID (up to 50 sequential queries). Please batch-fetch all records in a single query:

# Fetch all in one query
results = await db.execute(
    select(AgentTool)
    .where(AgentTool.id.in_(valid_ids))
    .options(selectinload(AgentTool.agent))
)
agent_tools = results.scalars().all()

4. Missing English translations

The PR only adds zh.json entries. Please add the corresponding keys to en.json as well.

5. Emoji usage

The frontend uses 🔌, 🤖, and 🗑️ in the UI. Our project does not use emoji in the interface — please replace them with text labels or Tabler icons (which are used elsewhere in the codebase).


What's Good

  • The WeCom connection_mode priority fix is correct and clean.
  • Making onSuccess async and awaiting query invalidation before resetting the form is the right approach.
  • The .catch(() => null) on the webhook URL query is a good defensive fix.
  • The partial-failure errors response design in the bulk delete API is well thought out.

Please address the required fixes above and re-push to this branch — happy to re-review after.

@ThomasOscar
Copy link
Copy Markdown
Author

感谢您反馈问题,我现在去解决这些问题。

## 修改内容

### 1. 修复角色名称
- 将 "admin", "super_admin" 改为 "org_admin", "platform_admin"
- 原代码会导致所有合法管理员收到 403 错误

### 2. 修复 N+1 查询和 MissingGreenlet 问题
- 将循环逐个查询改为单次 JOIN 查询
- 一次性获取 AgentTool 和 Agent,避免懒加载崩溃

### 3. 添加英文翻译
- 补充 en.json 中批量删除功能的翻译
- 包含 agent.tools 和 enterprise.tools 命名空间

### 4. 将 Emoji 替换为 Tabler 图标
- 将 🗑️、🤖、🔌 替换为 IconTrash、IconRobot、IconPlug
- 遵循项目使用 Tabler 图标的规范

## 为什么使用 JOIN 而非 selectinload?

审核者建议使用 selectinload(AgentTool.agent),但 AgentTool 模型
未定义 "agent" relationship。我们选择 JOIN 查询而非添加 relationship,原因如下:

1. 最小改动原则 - 无需修改数据模型
2. 项目中已有 JOIN 用法(如 list_agent_installed_tools 函数)
3. 避免添加 relationship 可能带来的副作用
合并上游的 A2A (Agent-to-Agent) 会话发送者标签显示逻辑:
- 引入 showSenderLabel 和 resolvedAvatarText 变量
- 保留 IconRobot 图标替换 emoji 的改进
- 在 Agent 对话中正确区分 "本 Agent" 和 "其他参与者"

关键变更:
- ChatMessageItem 组件新增 thisAgentName 参数
- 当发送者不是当前 Agent 时显示发送者标签
- 头像文字根据发送者身份动态计算
合并上游对 ChatMessageItem 组件的重构:
- 使用 senderLabel, avatarText, forceSenderLabel 参数替代 thisAgentName
- 组件内部逻辑简化,由调用方计算标签和头像文字
- 保留 IconRobot 图标替换 emoji 的改进

关键变更:
- ChatMessageItem 组件参数更新为上游格式
- 在 A2A 会话中正确传递 senderLabel 和 forceSenderLabel
- 普通聊天会话使用默认参数值
保留 IconRobot 图标替换 emoji 的改进,同时使用上游一致的变量命名
解决合并冲突:
1. 保留 IconRobot 图标(遵循项目不使用 emoji 的规范)
2. 合并上游的 isHumanReadonly 逻辑,支持人类只读会话
3. 统一处理 A2A 和人类只读两种场景的发送者标签显示

关键变更:
- showSenderLabel 使用 IconRobot 图标显示发送者名称
- senderLabel 和 avatarText 参数支持两种场景
- forceSenderLabel 合并两种条件
移除合并冲突遗留的重复代码:
- 删除重复的 resolvedAvatarText/resolvedSenderLabel/showSenderLabel 声明
- 修复 useMemo 依赖数组引用未定义变量的问题
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants