- 移除 GovAI, nomifun-tauri, 算力盒子 的 submodule 引用 - 添加所有子项目的完整源代码 - 保留原始 .git 为 .git.bak 备份
60 KiB
NomiFun × 飞书 — 企业工作台集成落地方案
文档版本:v3.4
日期:2026-07-02
状态:方案评估(基于代码库深度验证 + 飞书 API 文档验证)
变更:v3.0 — 审批流程改为人工执行(AI 仅辅助查询,人在工作台 UI 操作);新增企业经营看板模块
变更:v3.1 — 落地模式优化:审批 UI 采用混合模式(原生列表/详情 + 飞书 JS SDK 表单);经营看板独立页面优先(打开即看)+ AI 对话深度查询;分阶段交付,首个可用版本提前至 10-16 天
变更:v3.2 — 代码库二次验证修正:recharts 尚未安装(需新增依赖);前端路径修正为ui/src/renderer/pages/;新增 Tauri 深链配置、飞书 JS SDK SSO 流程、数据表 Schema、错误降级策略、测试策略;澄清卡片按钮机制与 CallerCtx 角色映射链路;修正飞书卡片 chart 工作量评估
变更:v3.3 — 关键可行性修正:飞书 H5 JS SDK 仅在飞书客户端内可用,Tauri webview 不可用;审批写操作是服务端 REST API 非 JS SDK 方法;OAuth redirect_uri 改为本地 HTTP 回调方案;审批 UI 回归原生模式(React + Arco Form 动态渲染);工作量相应调整
变更:v3.4 — 审批方案简化:改用飞书 AppLink 跳转模式,NomiFun 只做查询/展示,审批写操作跳转飞书客户端完成。消除 OAuth/动态表单/后端写代理三大复杂度,Phase 1b 从 10-15 天降至 5-7 天。新增tauri-plugin-opener依赖
一、方案总览
1.1 核心命题
组织架构、审批流程、文档知识库全部托管在飞书平台,企业经营数据由数据中台加工完成。NomiFun 作为统一工作台,通过以下能力整合调用:
- 消息通道:飞书 Bot 作为 NomiFun 的统一交互入口(自然语言 → AI 理解 → 工具调用)
- MCP 工具总线:将飞书通讯录、审批查询、数据中台 API 封装为标准 MCP 工具,AI Agent 自动编排调用
- 审批辅助(非执行):AI 仅查询审批状态、辅助填表预览;审批的创建、同意、拒绝通过 AppLink 跳转飞书客户端由人完成,AI 不执行任何审批动作
- 审批 UI AppLink 跳转模式:列表/详情用原生 React 组件(轻量、UX 一致),写操作通过 AppLink 跳转飞书客户端完成(复用飞书原生表单引擎,零维护)
- 知识库同步:飞书 Wiki 空间文档增量同步为本地 Markdown 快照,供 AI 检索
- 经营看板:从数据中台获取已加工的经营指标,按用户角色/部门展示差异化看板。独立看板页面(打开即看)+ AI 自然语言对话(深度查询/下钻/对比)双模式
- 飞书卡片即看板:经营数据查询结果也可通过飞书卡片 chart 组件渲染,在聊天中直接查看
- Webhook 通知:任务完成/审批状态变更通过飞书自定义机器人卡片推送到群/个人
1.2 架构全景图
┌──────────────────────────────────────────────────────────────┐
│ 飞书 (Lark) │
│ 通讯录 │ 审批流程 │ Wiki 文档 │ 消息/卡片 │ 事件订阅 │
└────┬─────────┬──────────┬──────────┬──────────┬───────────────┘
│ │ │ │ │
▼ ▼ ▼ ▼ ▼
┌──────────────────────────────────────────────────────────┐
│ NomiFun Rust Backend │
│ │
│ ┌────────────┐ ┌─────────────┐ ┌──────────────────┐ │
│ │ Lark │ │ Feishu │ │ MCP 工具总线 │ │
│ │ Channel │ │ Connector │ │ (Registry) │ │
│ │ Plugin │ │ (知识库) │ │ │ │
│ │ │ │ │ │ 已有 132+ 工具 │ │
│ │ WebSocket │ │ 增量同步 │ │ + 新增飞书域: │ │
│ │ 事件接收 │ │ 快照→MD │ │ - feishu_org_* │ │
│ │ 消息发送 │ │ │ │ - feishu_appr_* │ │
│ │ 卡片交互 │ │ │ │ - dashboard_* │ │
│ └─────┬──────┘ └──────┬──────┘ └────────┬─────────┘ │
│ │ │ │ │
│ ┌─────▼──────────────────────────────────▼─────────────┐ │
│ │ AI Agent Engine (nomi) │ │
│ │ 自然语言理解 → 工具选择 → 执行 → 结果整合 │ │
│ │ 工具审批门控 (DangerTier × Surface) │ │
│ │ ⚠ 审批操作不经过 AI — 仅查询/辅助,人在飞书客户端操作 │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ 数据中台对接 (caps_dashboard.rs) │ │
│ │ 外部 API ← 已加工经营指标 · 角色化数据过滤 │ │
│ └──────────────────────────────────────────────────┘ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Webhook 通知服务 │ │
│ │ 飞书自定义机器人卡片 + HMAC-SHA256 签名 │ │
│ └──────────────────────────────────────────────────┘ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ 审批工作台 UI (AppLink 跳转模式) │ │
│ │ 原生: 列表/详情/时间线 (React + Arco) │ │
│ │ 写操作: AppLink 跳转飞书客户端 (tauri-opener) │ │
│ └──────────────────────────────────────────────────┘ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ 经营看板前端 (React + Arco Design + recharts) │ │
│ │ 角色化看板页面 · 指标卡片 · 趋势图表 │ │
│ │ ⚠ recharts 需新增依赖(当前未安装) │ │
│ │ 也通过飞书 Bot 卡片 chart 组件投递 │ │
│ └──────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────┘
二、现有基础设施评估(代码级验证)
2.1 ✅ 飞书消息通道 — 已完整实现
文件: crates/backend/nomifun-channel/src/plugins/lark/plugin.rs
| 能力 | 状态 | 实现位置 | 说明 |
|---|---|---|---|
| WebSocket 长连接 | ✅ 已实现 | start() 方法 |
通过飞书 SDK 获取 WS endpoint,自动重连 |
| 消息接收 | ✅ 已实现 | handle_message_event() |
解析 im.message.receive_v1 事件,转换为 UnifiedIncomingMessage |
| 事件去重 | ✅ 已实现 | event_id 去重窗口 |
防止飞书重推导致重复处理 |
| 文本/富文本发送 | ✅ 已实现 | send_message() |
支持纯文本和富文本 |
| 交互卡片发送 | ✅ 已实现 | send_card() |
调用 api.rs 的卡片消息接口 |
| 卡片更新 | ✅ 已实现 | update_card() |
用于审批状态变更后刷新卡片 |
| 卡片按钮回调 | ✅ 已实现 | card_action_trigger 事件处理 |
用户点击卡片按钮 → 回调路由 |
| Bot 菜单事件 | ✅ 已实现 | bot_menu_event 处理 |
飞书机器人菜单点击 |
| 用户配对授权 | ✅ 已实现 | ActionExecutor::handle_incoming_message() |
飞书 open_id → 内部 user_id 映射 |
消息路由链路(已验证):
飞书 WebSocket 事件
→ LarkPlugin::handle_message_event() // 解析为 UnifiedIncomingMessage
→ mpsc::Sender → ChannelManager forwarder // 加盖 channel_id
→ ChannelOrchestrator::handle_message() // 异步 spawn
→ ActionExecutor::handle_incoming_message() // 授权检查 + 路由
→ MessageResult::Dispatched // 文本消息 → AI 会话
→ MessageResult::Action // 按钮回调 → 动作执行
→ ChannelMessageService → AI Agent 引擎 // 进入 nomi agent 工具调用循环
关键发现: 消息从飞书到 AI Agent 的完整链路已经打通,无需新增代码。配置飞书自建应用即可使用。
2.2 ✅ 飞书知识库 Connector — 已完整实现
文件: crates/backend/nomifun-knowledge/src/connector_feishu.rs
| 能力 | 状态 | 实现细节 |
|---|---|---|
| Token 管理 | ✅ | tenant_access_token 获取 + 30min 缓存 + 401 自动刷新 |
| 限流 | ✅ | 4 QPS (250ms 间隔) + 429 重试 (x-ogw-ratelimit-reset) |
| Wiki 空间节点列表 | ✅ | /open-apis/wiki/v2/spaces/{space_id}/nodes 分页拉取 |
| 增量同步 | ✅ | SyncCursor.last_sync_at 游标,跳过未修改文档 |
| 文档块 → Markdown | ✅ | feishu_md::blocks_to_markdown() 转换 |
| 快照持久化 | ✅ | 写入 snapshots/ 目录,YAML frontmatter 含 source_url |
| 凭证验证 | ✅ | validate_credentials() → 获取 token 验证有效性 |
| 测试覆盖 | ✅ | wiremock 集成测试 8 个用例(token 缓存/分页/增量/错误码) |
Connector trait 架构:
// crates/backend/nomifun-knowledge/src/connector.rs
trait KnowledgeConnector {
fn kind(&self) -> &'static str; // "feishu"
async fn validate_credentials(...) -> ConnectorIdentity;
async fn list_documents(...) -> SyncPage; // 分页 + 增量
async fn fetch_document(...) -> FetchedConnectorDoc; // blocks → markdown
async fn subscribe_webhook(...) -> Option<...>; // 预留(当前 poll-only)
async fn push_document(...) -> Result<...>; // 预留(未来双向同步)
}
同步编排(已验证): KnowledgeService::sync_connector_source() 执行两阶段同步:
- Phase 1: 分页
list_documents,收集文档引用 + 墓碑(删除标记) - Phase 2: 逐个
fetch_document→ 压缩 → 写入快照文件
限制: 当前仅同步 docx 类型文档(obj_type == "docx" 过滤),不支持 Sheet/Bitable/Mindnote。
2.3 ✅ MCP 工具总线 — 已完整实现
文件: crates/backend/nomifun-gateway/src/registry/
工具注册体系:
Registry::build() → 逐域 register()
├── caps_memory (记忆)
├── caps_conversation (会话管理)
├── caps_requirement (需求管理)
├── caps_autowork (自动化工作)
├── caps_knowledge (知识库搜索)
├── caps_channel (IM 通道管理)
├── caps_agent (Agent 委托)
├── caps_terminal (终端)
├── caps_files (文件)
├── caps_mcp (MCP 管理)
├── ... 共 18 个域, 132+ 个工具
├── caps_feishu_org (待新增) ← 飞书组织架构 (Read)
├── caps_feishu_approval (待新增) ← 飞书审批查询 (Read only, 人在飞书客户端操作)
└── caps_dashboard (待新增) ← 经营看板 (Read, 对接数据中台)
权限矩阵(已验证,关键约束):
| Surface | Read | Write | Destructive | Sensitive |
|---|---|---|---|---|
| Desktop | Allow | Allow | Confirm | Confirm |
| Channel | Allow | Allow | Deny | Deny |
| Remote | Allow | Allow | Confirm | Deny |
⚠️ 关键发现: 飞书消息触发的 AI 会话走 Surface::Channel。在该 Surface 下:
Read级别工具 自动放行 — 适合审批查询、组织架构查询、经营看板查询Write级别工具 自动放行 — 但审批操作不封装为工具,避免 AI 越权Destructive和Sensitive级别工具 硬拒绝 — 即使 confirm=true 也不行- 可通过
deny_on/confirm_on覆盖特定 Surface 行为
工具定义模式(已验证):
// 新增工具只需:
// 1. 创建 caps_feishu.rs,定义参数 struct + handler
// 2. 在 lib.rs 添加 mod caps_feishu;
// 3. 在 Registry::build() 添加 crate::caps_feishu::register(&mut caps);
Capability::new(
CapabilityMeta::new(
"nomi_feishu_org_search", // 工具名,≤42 字符
"feishu", // 域标签
"Search employees in Feishu contacts", // LLM 可见描述
DangerTier::Read, // 读操作 → Channel Surface 放行
),
|deps, ctx, params| async move {
// handler 逻辑
json!({ "results": [...] })
},
)
2.4 ✅ Webhook 通知 — 已完整实现
文件: crates/backend/nomifun-webhook/src/sender.rs
| 能力 | 状态 | 说明 |
|---|---|---|
| 飞书自定义机器人卡片 | ✅ | 支持 interactive card JSON payload |
| HMAC-SHA256 签名 | ✅ | 按飞书规范签名,timestamp + key |
| Slack 格式 | ✅ | 兼容 |
| 通用 HTTP JSON | ✅ | 兜底 |
| Webhook CRUD 管理 | ✅ | service.rs 完整 CRUD + 标签绑定 |
| 事件绑定 | ✅ | tag-based event binding,AutoWork 可触发 |
2.5 ✅ AI Agent 工具调用链路 — 已完整实现
完整调用链路(已验证):
用户在飞书发送消息
→ LarkPlugin → ChannelOrchestrator → ActionExecutor
→ ChannelMessageService → AI Agent (nomi)
→ LLM 识别意图,返回 tool_use (ContentBlock::ToolUse)
→ orchestration.rs::execute_tool_calls_with_approval()
→ ToolConfirmer / ToolApprovalManager 审批门控
→ registry.get(name).execute(input) → McpToolProxy
→ McpManager::call_tool(server_name, tool_name, input)
→ stdio bridge → GatewayMcpServer (HTTP)
→ Registry::global().dispatch_opt(deps, ctx, name, args)
→ Capability.handler(deps, ctx, params) → 实际执行
→ 结果返回 → LLM 整合 → 飞书消息回复
审批门控(已验证):
ToolCategory::Info(readOnly)→ 自动批准,可并发执行ToolCategory::Exec(mutating)→ 需要用户批准(除非 auto_approve 或 allow_list)- 飞书 Channel Surface 额外受 DangerTier 矩阵约束
三、新增工作:飞书 OA 工具域 + 经营看板
3.1 模块划分
| 模块 | 文件 | 工具数 | DangerTier | 工作量 |
|---|---|---|---|---|
| 组织架构 | caps_feishu_org.rs |
3 | Read | 3-5 天 |
| 审批查询(仅读) | caps_feishu_approval.rs |
3 | Read | 3-4 天 |
| 审批列表/详情 UI(原生) | ui/src/renderer/pages/approval/ |
- | - | 3-4 天 |
| 审批写操作(AppLink 跳转) | OpenInFeishuButton.tsx + tauri-plugin-opener |
- | - | 1 天 |
| 经营看板 MCP 工具 | caps_dashboard.rs |
4 | Read | 5-8 天 |
| 看板前端页面 | ui/src/renderer/pages/dashboard/ |
- | - | 6-10 天 |
| 飞书卡片 chart 投递 | 扩展 sender.rs |
- | - | 2-3 天 |
| 卡片交互路由 | 扩展 action.rs |
- | - | 1-2 天 |
| Tauri 深链(已有)+ Opener 插件 | tauri.conf.json(已有)+ Cargo.toml 新增 |
- | - | 1 天 |
| 数据表 migration | nomifun-db/migrations/ |
- | - | 1 天 |
| 定时同步调度 | 扩展 services.rs |
- | - | 1-2 天 |
| 权限感知搜索 | 扩展 knowledge_tools.rs |
- | - | 3-5 天 |
💡 v3.4 方案变更: v3.2 的「飞书 JS SDK 混合模式」不可行(H5 SDK 仅在飞书客户端内可用);v3.3 的原生模式虽可行但复杂度高(动态表单 + OAuth + 后端写代理 = 10-15 天)。v3.4 改用 AppLink 跳转模式:NomiFun 负责查询和展示,审批写操作通过 AppLink 跳转飞书客户端完成。无需 OAuth、无需动态表单、无需后端写代理,工作量降至 3-5 天,且保留增量升级路径。
⚠️ 审批流程设计原则: AI 仅提供查询和辅助能力(查待办、查详情、辅助填表预览),审批的创建、同意、拒绝全部通过 AppLink 跳转飞书客户端由人完成。AI 不执行任何审批动作,确保审批责任链清晰,AI 不承担审批决策风险。
3.2 组织架构工具 (caps_feishu_org.rs)
飞书 API 对接:
| 工具名 | 飞书 API | DangerTier | 说明 |
|---|---|---|---|
nomi_feishu_org_search |
/open-apis/contact/v3/users/search |
Read | 按姓名/部门搜索员工 |
nomi_feishu_org_dept_tree |
/open-apis/contact/v3/departments |
Read | 获取部门树 |
nomi_feishu_org_user_detail |
/open-apis/contact/v3/users/{user_id} |
Read | 员工详情(职位、上级、邮箱) |
实现要点:
- 复用 FeishuConnector 的 token 管理: 将
connector_feishu.rs中的token_cache/rate_limit/authed_get提取为公共 trait 或共享模块,避免重复实现 - 本地缓存: 飞书通讯录 API 有严格限流(10 QPS),需在 SQLite 中维护
org_cache表,TTL 15 分钟 - PII 脱敏: 手机号/邮箱在返回给 LLM 前脱敏(
138****1234),仅在用户明确请求时解敏 - 权限边界: 飞书 app 的通讯录可见范围 = NomiFun 可访问的数据边界
参数 struct 示例:
#[derive(Deserialize, JsonSchema)]
struct OrgSearchParams {
/// 搜索关键词(姓名、拼音、工号)
query: String,
/// 限制返回数量,默认 10,最大 50
#[serde(default = "default_page_size")]
page_size: u32,
}
3.3 审批查询工具 (caps_feishu_approval.rs) — 仅查询,人在飞书客户端操作
设计原则: 审批流程全部通过 AppLink 跳转飞书客户端由人完成。AI 的角色是:
- 查询待办: "我有哪些待审批?"
- 查询详情: "这个审批的具体内容是什么?"
- 辅助填表: AI 根据用户描述生成表单预览,用户在飞书客户端中确认并提交
- 状态提醒: 审批状态变更时通过 Webhook 推送通知
飞书 API 对接(仅 Read):
| 工具名 | 飞书 API | DangerTier | 说明 |
|---|---|---|---|
nomi_feishu_appr_list |
/open-apis/approval/v4/tasks |
Read | 查询待审批列表(按用户过滤) |
nomi_feishu_appr_get |
/open-apis/approval/v4/instances/{instance_id} |
Read | 查询审批详情(表单内容、审批节点、状态) |
nomi_feishu_appr_templates |
/open-apis/approval/v4/approvals |
Read | 查询可用审批模板及表单结构定义 |
❌ 不封装为 MCP 工具的操作(人在飞书客户端执行):
| 操作 | 原因 | AppLink 跳转方式 |
|---|---|---|
| 创建审批实例 | 审批发起需人工确认 | AppLink 打开飞书发起审批页 → 填写表单 → 提交 |
| 同意审批 | 审批决策必须由人做出 | AppLink 打开飞书审批详情页 → 点击「同意」 |
| 拒绝审批 | 同上 | AppLink 打开飞书审批详情页 → 点击「拒绝」 |
AI 辅助填表流程:
用户: "帮我发起请假,7月5号到7月7号,事假"
① AI 调用 nomi_feishu_appr_templates → 获取「请假申请」模板表单结构
② AI 根据用户描述填充表单字段 → 生成预览卡片
③ 飞书回复卡片: 表单预览 + 「在飞书中发起」按钮(AppLink URL)
④ 用户点击 → AppLink 跳转飞书客户端审批发起页
⑤ 用户在飞书客户端中确认并提交审批
审批模板管理: 在 NomiFun 中维护 feishu_approval_templates 表:
approval_code↔ 飞书审批定义 codeform_schema— JSON Schema 描述表单字段,供 AI 理解如何填充display_name— 人类可读名称("请假申请"、"报销单"等)applink_path— 飞书 AppLink 路径模板(如pc/pages/create-form/index?id={approval_code})
3.4 工作台审批 UI 页面 — AppLink 跳转模式
设计原则: NomiFun 负责查询和展示(审批列表、详情预览、AI 辅助),审批写操作(创建/同意/拒绝)通过飞书 AppLink 跳转飞书客户端完成。AI 不参与审批操作。
⚠️ v3.4 方案变更: v3.3 的原生模式(动态表单 + OAuth + 后端写代理 = 10-15 天)虽可行但复杂度高。经飞书官方文档验证,飞书 AppLink 协议支持直接打开审批页面(发起/详情/审批中心),用户在飞书客户端中操作无需 OAuth。v3.4 改为 AppLink 跳转模式,大幅降低复杂度(3-5 天),且保留升级路径。
AppLink 跳转模式架构:
┌─────────────────────────────────────────────────────┐
│ NomiFun 工作台 │
│ │
│ ┌──────────────┐ ┌───────────────────────────┐ │
│ │ 审批列表/详情 │ │ AI 辅助填表预览 │ │
│ │ (原生 React) │ │ (预填数据 → AppLink 传递) │ │
│ │ 轻量·快速·一致 │ │ 跳转飞书完成实际提交 │ │
│ └──────┬───────┘ └───────────┬───────────────┘ │
│ │ │ │
│ │ MCP 查询工具 │ AppLink URL │
│ │ (AI 辅助) │ (tauri-plugin-opener)│
│ ▼ ▼ │
│ ┌──────────────────────────────────────────────┐ │
│ │ 飞书审批 OpenAPI (v4) + AppLink │ │
│ │ Read: 查询列表/详情/模板 → MCP 工具 │ │
│ │ Write: 创建/同意/拒绝 → AppLink 跳飞书 │ │
│ └──────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────┘
为什么 AppLink 跳转模式(v3.4):
| 维度 | 原生模式 (v3.3) | AppLink 跳转 (v3.4) |
|---|---|---|
| OAuth 流程 | 需要(本地 HTTP 回调 + token 管理) | 不需要(用户已在飞书登录) |
| 动态表单 | 需要 Arco Form 动态渲染 | 不需要(飞书原生表单引擎) |
| 后端写代理 | 需要 REST API 代理 | 不需要(飞书客户端直接调 API) |
| user_access_token | 需要获取和管理 | 不需要 |
| 工作量 | 10-15 天 | 3-5 天 |
| UX | 全程不离开 NomiFun | 写操作需跳转飞书(一次跳转) |
| 升级路径 | - | 可增量添加 OAuth + 写代理 |
飞书 AppLink 审批页面 URL:
// 打开审批详情页(同意/拒绝在飞书内完成)
https://applink.feishu.cn/client/mini_program/open
?appId=cli_9cb844403dbb9108
&path=pages%2Fdetail%2Findex%3FinstanceId%3D${instanceId}
// 打开发起审批页
https://applink.feishu.cn/client/mini_program/open
?appId=cli_9cb844403dbb9108
&path=pc%2Fpages%2Fcreate-form%2Findex%3Fid%3D${approvalCode}
// 打开审批中心(待办列表)
https://applink.feishu.cn/client/mini_program/open
?mode=appCenter
&appId=cli_9cb844403dbb9108
&path=pc%2Fpages%2Fin-process%2Findex
来源: 飞书官方文档 打开审批页面。
cli_9cb844403dbb9108是飞书审批应用的固定 appId。
前端页面架构:
ui/src/renderer/pages/approval/
├── ApprovalListPage.tsx // 审批待办列表(原生 React + Arco Table)
├── ApprovalDetailPage.tsx // 审批详情预览(原生:表单内容 + 时间线 + 「在飞书中处理」按钮)
├── components/
│ ├── ApprovalTimeline.tsx // 审批流程时间线(原生)
│ └── OpenInFeishuButton.tsx // AppLink 跳转按钮(调用 tauri-plugin-opener)
└── hooks/
└── useApprovalQuery.ts // 调用 MCP 查询工具获取列表/详情
对比 v3.3: 无需
DynamicForm.tsx、useFeishuAuth.ts、ApprovalCreatePage.tsx——审批表单填写和提交在飞书客户端完成。
后端审批查询 API(MCP 工具,AI 和前端共用):
| 端点 | 方法 | 说明 | MCP 工具 |
|---|---|---|---|
/api/approval/list |
GET | 获取当前用户待审批列表 | nomi_feishu_appr_list |
/api/approval/:id |
GET | 获取审批详情 | nomi_feishu_appr_get |
/api/approval/templates |
GET | 获取可用审批模板 | nomi_feishu_appr_templates |
审批写操作(AppLink 跳转飞书客户端):
| 操作 | 方式 | 认证 |
|---|---|---|
| 创建审批 | AppLink 打开飞书发起审批页 | 飞书客户端登录态 |
| 同意审批 | AppLink 打开飞书审批详情页 | 飞书客户端登录态 |
| 拒绝审批 | AppLink 打开飞书审批详情页 | 飞书客户端登录态 |
关键设计: 审批写操作通过 AppLink 跳转飞书客户端,用户在飞书中完成操作。不需要 OAuth、不需要 user_access_token、不需要后端写代理。AI Agent 无法触达审批写操作。
审批列表也可嵌入对话侧边栏: 复用
ChatSlider的extraTabs模式(参考NomiSessionMetricsPanel),在对话侧边栏中嵌入审批待办列表,实现「边聊天边查看审批」。
升级路径: 若后续需要「全程不离开 NomiFun」,可增量添加:① OAuth(2-3 天)→ ② 后端写代理(3-5 天)→ ③ 动态表单(3-4 天)。每步独立,无需推翻重来。
3.4.1 Tauri 深链配置(nomifun:// 协议)
现状: 项目已配置 Tauri v2 深链插件,nomifun scheme 已注册(见 tauri.conf.json:38-42 和 Cargo.toml:33)。
// apps/desktop/tauri.conf.json (已有配置)
"plugins": {
"deep-link": {
"desktop": {
"schemes": ["nomifun"]
}
}
}
# apps/desktop/Cargo.toml (已有依赖)
tauri-plugin-deep-link = "2"
tauri-plugin-single-instance = { version = "2", features = ["deep-link"] }
注意: Windows/Linux 上深链通过 argv 传递给第二个进程,
single-instance插件拦截并转发给主进程。macOS 通过 Apple Events 处理。已有配置已正确处理跨平台。
需新增: 前端路由监听深链事件并导航到对应页面。
// ui/src/renderer/main.tsx 或 Router.tsx 中注册
import { listen } from '@tauri-apps/api/event';
listen('deep-link', (event) => {
const url = event.payload as string; // "nomifun://approval/detail?id=xxx"
const path = url.replace('nomifun://', '/');
navigate(path);
});
支持的深链路径:
| 深链 | 目标页面 | 场景 |
|---|---|---|
nomifun://approval/list |
审批待办列表 | 查看待审批 |
nomifun://approval/detail?id=xxx |
审批详情 | 查看具体审批 |
nomifun://dashboard |
经营看板 | 查看经营数据 |
3.4.2 Tauri Opener 插件(打开外部 URL)
问题: AppLink 跳转需要从 Tauri 应用中打开外部 URL(https://applink.feishu.cn/...),需使用 Tauri 官方 opener 插件。
需新增依赖:
# apps/desktop/Cargo.toml 新增
tauri-plugin-opener = "2"
// 前端调用
import { openUrl } from '@tauri-apps/plugin-opener';
// 打开飞书审批详情页
await openUrl(
`https://applink.feishu.cn/client/mini_program/open?appId=cli_9cb844403dbb9108&path=${encodeURIComponent(`pages/detail/index?instanceId=${instanceId}`)}`
);
Capability 配置 (src-tauri/capabilities/default.json):
{
"permissions": {
"opener:allow-open-url": {
"scope": {
"allow": [{ "url": "https://applink.feishu.cn/**" }]
}
}
}
}
安全约束: opener scope 限制为
applink.feishu.cn域名,防止任意 URL 跳转。
飞书卡片中也支持 AppLink: 飞书卡片 button 组件的
default_url字段可直接配置 AppLink URL,用户在飞书聊天中点击卡片按钮即可跳转审批页面,无需经过 NomiFun。参见飞书卡片交互文档。
3.5 卡片交互闭环(跳转工作台)
现有能力: Lark Plugin 已处理 card.action.trigger 事件,将按钮回调转为 UnifiedAction(见 handle_card_action() in plugin.rs)。回调解析通过 parse_lark_callback() 按 "category:action:k=v,k=v" 格式拆分,当前仅支持 platform/system/chat 三种 category。
两种卡片按钮模式:
| 模式 | 飞书卡片按钮类型 | 行为 | 适用场景 |
|---|---|---|---|
| URL 直跳 | { "tag": "button", "text": "...", "url": "nomifun://..." } |
飞书客户端直接打开 URL → OS 层面唤起 NomiFun → Tauri 深链路由 | 简单跳转(前往审批页、前往看板) |
| Action 回调 | { "tag": "button", "text": "...", "value": { "action": "feishu:approval_preview:code=xxx" } } |
触发 card.action.trigger → handle_card_action() → parse_lark_callback() → route_action() |
需要后端处理后再回复的场景(如刷新状态) |
审批辅助场景使用 URL 直跳模式(简单可靠,无需后端介入):
{
"type": "template",
"data": {
"template_id": "approval_preview",
"template_variable": {
"title": "请假申请预览",
"applicant": "张三",
"duration": "7月5日 - 7月7日",
"type": "事假",
"actions": [
{ "tag": "button", "text": { "tag": "plain_text", "content": "在飞书中发起" }, "url": "https://applink.feishu.cn/client/mini_program/open?appId=cli_9cb844403dbb9108&path=pc%2Fpages%2Fcreate-form%2Findex%3Fid%3Dleave_v1", "type": "default" }
]
}
}
}
注意: 卡片中不包含「同意/拒绝」按钮 — 审批操作必须在飞书客户端中由人完成。URL 直跳模式不需要扩展
parse_lark_callback(),飞书客户端原生处理 URL 打开。
可选扩展(Action 回调模式): 若需要在卡片中实现「刷新审批状态」等交互,需扩展 parse_lark_callback() 的 category 白名单,增加 "feishu" 类别,并在 route_action() 中新增对应路由。工作量 1-2 天。
3.6 企业经营看板 (caps_dashboard.rs + 前端页面)
数据来源: 外部数据中台(已完成 ETL 加工的指标数据),通过 REST API 提供。
设计原则:
- 数据中台负责数据加工(聚合、清洗、建模),NomiFun 只做展示和查询
- 独立看板页面优先: 用户打开工作台即可看到角色化看板,无需对话即可获取核心指标
- AI 对话为补充: 用户需深度查询(下钻/对比/自定义时间范围)时,通过自然语言对话与 AI 交互
- 角色化数据过滤: 不同用户看到不同层级的指标(总部/部门/个人)
- 权限由后端强制执行,不信任前端传入的 scope 参数
双模式交互:
模式一:独立看板页面(打开即看)
用户打开工作台 → 看板页面自动加载角色化指标
→ 指标卡片 + 趋势图表 + KPI 进度条
→ 手动刷新 / 切换时间范围
模式二:AI 自然语言对话(深度查询)
用户在飞书或工作台中提问:
"本月华东区销售额对比上月怎么样?"
→ AI 调用 nomi_dashboard_trend + nomi_dashboard_breakdown
→ 返回图表卡片 + 文字分析
→ 可继续追问 "按产品线拆分呢?"
MCP 工具定义:
| 工具名 | DangerTier | 说明 |
|---|---|---|
nomi_dashboard_summary |
Read | 获取当前用户可见的经营概览指标 |
nomi_dashboard_trend |
Read | 获取指定指标的趋势数据(时间序列) |
nomi_dashboard_breakdown |
Read | 按维度下钻指标(部门/产品/区域) |
nomi_dashboard_compare |
Read | 多指标对比分析 |
角色化数据过滤机制:
#[derive(Deserialize, JsonSchema)]
struct DashboardSummaryParams {
/// 指标分类(如 "sales", "hr", "finance"),可选
#[serde(default)]
category: Option<String>,
/// 时间范围(如 "today", "this_week", "this_month")
#[serde(default = "default_time_range")]
time_range: String,
// 注意:scope/department/user_id 不接受前端传入
// 由 handler 从 CallerCtx.user_id 解析用户角色后注入
}
角色权限映射:
| 角色 | 可见数据范围 | 示例指标 |
|---|---|---|
| CEO/高管 | 全公司 | 总营收、总利润、人效、增长率 |
| 部门负责人 | 本部门 + 下属部门 | 部门 KPI、部门人均产出、部门预算执行率 |
| 团队主管 | 本团队 | 团队任务完成率、团队工时分布 |
| 普通员工 | 个人 | 个人 KPI、个人任务进度、个人考勤 |
实现要点:
- 用户角色获取链路:
CallerCtx.user_id→ 查询user_roles表 → 确定数据可见范围- Channel Surface 链路: 飞书消息 →
ActionExecutor::handle_incoming_message()→pairing.get_internal_user_id(open_id, "lark")→internal_user_id→ 传入ChannelMessageService→ AI Agent →CallerCtx.user_id = internal_user_id - Desktop Surface 链路: 工作台直接调用 →
CallerCtx.user_id = 当前登录用户 ID - 角色解析:
user_roles表中user_id关联internal_user_id,role字段决定数据 scope - scope 注入: handler 内部根据
role计算scope(如role=ceo→scope=all,role=dept_head→scope=dept:{dept_id}),传递给数据中台 API
- Channel Surface 链路: 飞书消息 →
- 数据中台 API 对接: 在
GatewayDeps中新增data_platform_client字段(需在deps.rs添加字段 +routes.rs注入) - 缓存策略: 高频指标在 NomiFun 侧缓存 5 分钟,减少数据中台压力
- 前端看板页面: 基于 Arco Design + recharts 构建可视化看板(需先安装 recharts 依赖)
- 飞书卡片投递: AI 也可通过飞书 Bot 以卡片形式回复经营数据查询
前端看板页面架构:
ui/src/renderer/pages/dashboard/
├── DashboardPage.tsx // 看板主页面(角色化路由)
├── components/
│ ├── MetricCard.tsx // 指标卡片组件
│ ├── TrendChart.tsx // 趋势图表(recharts)
│ ├── BreakdownTable.tsx // 下钻数据表格
│ └── KPIProgress.tsx // KPI 进度条
├── hooks/
│ └── useDashboardData.ts // 数据获取 hook(调用后端 API)
└── config/
└── roleWidgets.ts // 角色 → 看板组件配置映射
数据中台 API 约定:
GET /api/v1/metrics/summary?scope={scope}&category={category}&range={range}
→ { metrics: [{ name, value, unit, change_pct, target, target_pct }] }
GET /api/v1/metrics/trend?metric={metric}&range={range}&granularity={day|week|month}
→ { points: [{ timestamp, value }] }
GET /api/v1/metrics/breakdown?metric={metric}&dimension={department|product|region}
→ { groups: [{ label, value, share_pct }] }
NomiFun 后端在调用时注入
scope(根据用户角色计算),数据中台只返回该 scope 范围内的数据。
飞书卡片 chart 投递技术说明:
现有 build_lark_card() 仅支持 div + lark_md 文本元素(见 sender.rs:66-87)。经营看板需要在飞书卡片中展示图表,需扩展为飞书卡片 JSON 模板的 chart 组件:
// 新增:在 build_lark_card 中支持 chart 类型 element
fn build_lark_chart_element(data: &ChartSpec) -> Value {
json!({
"tag": "chart",
"chart_spec": data.spec, // 飞书卡片 chart JSON spec
"mode": "transparent"
})
}
飞书卡片 chart 组件接受 Vega-Lite 风格的 JSON spec,需将数据中台返回的指标数据转换为飞书 chart spec 格式。工作量 2-3 天(含 spec 转换 + 测试)。
3.7 定时增量同步
现状: 知识库同步通过 HTTP API 手动触发(POST /api/knowledge/:id/sync)。
增强: 在 AppServices 中添加 tokio cron 调度器:
// 每 60 分钟触发一次飞书 Wiki 增量同步
tokio::spawn(async move {
let mut interval = tokio::time::interval(Duration::from_secs(3600));
loop {
interval.tick().await;
for kb in knowledge_service.list_connector_sources("feishu").await {
let _ = knowledge_service.sync_connector_source(&kb.id).await;
}
}
});
3.8 数据表 Schema 定义
新增 SQLite 表(通过 migration 脚本创建):
feishu_approval_templates — 审批模板映射
CREATE TABLE feishu_approval_templates (
id INTEGER PRIMARY KEY AUTOINCREMENT,
approval_code TEXT NOT NULL UNIQUE, -- 飞书审批定义 code
display_name TEXT NOT NULL, -- 人类可读名称("请假申请")
form_schema TEXT NOT NULL, -- JSON Schema 描述表单字段
applink_path TEXT NOT NULL, -- 飞书 AppLink 路径模板
enabled BOOLEAN DEFAULT 1,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL
);
user_roles — 用户角色映射(经营看板权限)
CREATE TABLE user_roles (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id TEXT NOT NULL, -- internal_user_id
role TEXT NOT NULL, -- ceo | dept_head | team_lead | employee
dept_id TEXT, -- 飞书部门 ID(dept_head/team_lead 必填)
scope_override TEXT, -- 可选:手动覆盖数据可见范围
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL,
UNIQUE(user_id)
);
org_cache — 飞书通讯录本地缓存
CREATE TABLE org_cache (
open_id TEXT PRIMARY KEY,
name TEXT NOT NULL,
en_name TEXT,
department_ids TEXT, -- JSON array of dept IDs
job_title TEXT,
mobile TEXT, -- 加密存储
email TEXT, -- 加密存储
leader_open_id TEXT,
raw_json TEXT, -- 完整飞书响应(加密)
cached_at INTEGER NOT NULL,
expires_at INTEGER NOT NULL -- cached_at + TTL(15min)
);
audit_logs — 审计日志
CREATE TABLE audit_logs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
created_by TEXT NOT NULL, -- user_id
action TEXT NOT NULL, -- 如 "feishu_appr_list", "dashboard_summary"
target TEXT, -- 操作目标(如审批 instance_id)
result TEXT, -- success | error:xxx
surface TEXT, -- channel | desktop | remote
created_at INTEGER NOT NULL
);
Migration 方式: 在
nomifun-db/src/migrations/中新增xxxx_feishu_integration.sql,遵循现有 migration 命名约定。
四、数据流详解
4.1 场景一:AI 辅助审批填表(人在飞书客户端提交)
用户在飞书发送: "帮我发起请假,7月5号到7月7号,事假"
① 飞书 WebSocket → LarkPlugin::handle_message_event()
→ UnifiedIncomingMessage { text: "帮我发起请假...", user: { id: open_id } }
② ChannelOrchestrator → ActionExecutor::handle_incoming_message()
→ pairing.get_internal_user_id(open_id, "lark") → internal_user_id
→ session_mgr.get_or_create_session() → session
→ MessageResult::Dispatched { session_id, conversation_id }
③ ChannelMessageService → AI Agent (nomi)
→ LLM 接收消息 + 工具列表(含 nomi_feishu_*)
→ LLM 返回 tool_use: nomi_feishu_appr_templates (获取请假模板结构)
④ 工具执行 (仅查询):
→ McpToolProxy.execute() → stdio bridge → GatewayMcpServer
→ Registry::dispatch_opt() → Capability.handler()
→ 调用飞书审批 API 查询模板定义 → 返回表单结构
⑤ AI Agent 收到模板结构 → LLM 填充表单字段
→ 生成审批预览卡片 JSON + AppLink 跳转链接
→ ChannelMessageService → LarkPlugin::send_card()
→ 飞书渲染预览卡片(含「在飞书中发起」按钮)
⑥ 用户点击「在飞书中发起」
→ AppLink 跳转飞书客户端审批发起页
→ 用户在飞书客户端中确认并提交审批
→ ❌ AI 不参与审批提交操作
⑦ (异步) 审批状态变更
→ 飞书事件订阅 → 或 NomiFun 轮询
→ Webhook 自定义机器人卡片推送到用户/群
4.2 场景二:文档知识问答
用户在飞书发送: "我们的差旅报销标准是什么?"
①-② 同上,消息路由到 AI Agent
③ AI Agent 调用 knowledge_search MCP 工具
→ KnowledgeMcpServer 检索已同步的飞书 Wiki 快照
→ 返回匹配文档片段 + 飞书原文链接 (source_url)
④ AI 整合为自然语言回答
→ "根据《差旅报销管理办法》,国内出差住宿标准为..."
→ 附带飞书文档链接: https://open.feishu.cn/docx/xxx
⑤ LarkPlugin::send_message() → 飞书回复
4.3 场景三:组织架构查询
用户在飞书发送: "产品部有哪些人?"
①-② 同上
③ AI Agent 调用 nomi_feishu_org_dept_tree → 获取部门树
AI Agent 调用 nomi_feishu_org_search → 按部门搜索员工
④ AI 整合结果:
→ "产品部共 12 人:\n- 张三(产品总监)\n- 李四(产品经理)\n..."
→ 手机号脱敏: 138****1234
⑤ LarkPlugin::send_message() → 飞书回复
4.4 场景四:经营看板查询
用户在飞书发送: "这个月销售情况怎么样?"
①-② 同上,消息路由到 AI Agent
③ AI Agent 调用 nomi_dashboard_summary (category="sales", range="this_month")
→ handler 从 CallerCtx.user_id 解析用户角色 → 确定数据可见范围
→ 调用数据中台 API (注入 scope) → 返回已加工指标
④ AI 整合结果:
→ "本月销售概览:\n- 总销售额:¥1,250,000(环比 +12%)\n- 新增客户:85 家(环比 +8%)\n- 客单价:¥14,705(环比 +3.5%)"
→ 附带趋势图卡片(如用户为高管,含部门对比数据)
⑤ LarkPlugin::send_card() → 飞书回复指标卡片
⑥ 用户也可在 NomiFun 桌面端打开「经营看板」页面
→ DashboardPage 根据用户角色渲染差异化看板
→ 指标卡片 + 趋势图表 + 下钻表格
五、飞书应用配置清单
5.1 自建应用创建
- 登录 飞书开放平台 → 创建企业自建应用
- 获取
App ID+App Secret— 用于 NomiFun connector credential - 配置应用名称、头像、描述
5.2 权限申请
| 权限范围 | 权限标识 | 用途 |
|---|---|---|
| 通讯录 | contact:user.base:readonly |
搜索/查询员工基本信息 |
| 通讯录 | contact:user.phone:readonly |
查询手机号(需审批) |
| 通讯录 | contact:user.email:readonly |
查询邮箱(需审批) |
| 通讯录 | contact:department.base:readonly |
获取部门树 |
| 审批 | approval:approval:read |
查询审批列表/详情/模板(仅读) |
approval:approval:write |
||
| 文档 | wiki:wiki:readonly |
读取 Wiki 空间节点 |
| 文档 | docx:document:readonly |
读取文档内容(blocks) |
| 消息 | im:message |
接收消息 |
| 消息 | im:message:send_as_bot |
以机器人身份发送消息 |
| 消息 | im:chat:readonly |
获取群信息 |
5.3 事件订阅
- 模式: WebSocket(NomiFun 已支持,无需公网回调地址)
- 订阅事件:
im.message.receive_v1— 接收消息card.action.trigger— 卡片按钮回调application.bot.menu_v6— 机器人菜单点击
5.4 机器人配置
- 启用机器人能力
- 配置指令菜单(可选):
查审批、查文档、找人、看板 - 配置欢迎语
5.5 审批应用配置
- 在飞书管理后台 → 审批管理中确认审批模板
- 获取每个模板的
approval_code - 在 NomiFun 中配置
feishu_approval_templates表 - 不申请审批写入权限 — 审批操作通过 AppLink 跳转飞书客户端由人完成
5.6 数据中台对接配置
- 确认数据中台 API 地址、认证方式(API Key / OAuth)
- 在 NomiFun 中配置
data_platform连接信息(加密存储) - 定义角色 → 数据 scope 映射规则(
user_roles表) - 确认数据中台已加工好所需指标(NomiFun 不做 ETL)
六、安全与合规评估
6.1 密钥管理
| 密钥 | 存储方式 | 风险评估 |
|---|---|---|
app_id / app_secret |
NomiFun connector credential 加密存储 | ✅ 不进 Git,仅 .env.example |
tenant_access_token |
内存缓存,30min 过期 | ✅ 不持久化 |
| Webhook 签名密钥 | webhook 配置加密存储 | ✅ HMAC-SHA256 签名验证 |
| 数据中台 API Key | NomiFun 加密存储 | ✅ 不进 Git,仅 .env.example |
6.2 权限边界
- 飞书 App 可见范围 = NomiFun 可访问的数据边界
- 后端独立校验:
ActionExecutor中pairing.get_internal_user_id()强制做 open_id → internal_user_id 映射,不信任请求体中的 user_id - Surface 权限矩阵:
Destructive/Sensitive操作在 Channel Surface 硬拒绝 - 审批不封装写入工具: 审批创建/同意/拒绝不作为 MCP 工具,AI 无法越权执行
- 看板角色过滤:
scope由后端从CallerCtx.user_id解析注入,不信任前端传入
6.3 PII 脱敏
- 手机号:
138****1234 - 邮箱:
z***@company.com - 身份证:
110***********1234 - 脱敏在 MCP 工具 handler 层执行,确保不进入 LLM context
6.4 审计日志
- 所有审批查询操作记录到
audit_logs表(审批执行在飞书侧已有审计) - 看板数据查询记录到
audit_logs表 - 字段:
created_by/action/target/result/created_at - 保留 ≥ 6 个月
6.5 注入防御
- AI 辅助填表预览仅生成 AppLink 链接,不直接提交审批 → 飞书客户端校验表单有效性
- 用户输入与系统 prompt 分离 role,防 prompt injection
- LLM 输出的 tool_use 参数经
Capability::new()的 typed deserialization 校验 - 看板数据中台 API Key 不暴露给 LLM context
七、实现路线图
Phase 0: 即用即有(0 天)
| 能力 | 说明 |
|---|---|
| 飞书 Bot 消息收发 | 配置自建应用 WebSocket 即可 |
| 飞书 Wiki 文档同步 | 配置 connector credential + space_id |
| AI 知识问答 | 文档同步后自动可用 |
| Webhook 群通知 | 配置飞书自定义机器人 webhook URL |
Phase 1a: MCP 查询工具 + 经营看板页面 + 深链基础设施(11-18 天)
| 任务 | 工作量 | 依赖 |
|---|---|---|
| 提取 FeishuConnector 公共能力(token/限流/authed_get) | 1 天 | - |
caps_feishu_org.rs — 3 个组织架构工具 |
3-5 天 | 公共能力 |
caps_feishu_approval.rs — 3 个审批查询工具(仅读) |
3-4 天 | 公共能力 |
caps_dashboard.rs — 4 个经营看板工具 |
5-8 天 | 数据中台 API |
| 安装 recharts 依赖 + 看板前端页面 (DashboardPage + 组件) | 6-10 天 | 看板工具 |
飞书卡片 chart 组件投递(扩展 build_lark_card) |
2-3 天 | 看板工具 |
| 数据表 migration + Tauri opener 插件 | 1 天 | - |
| 集成测试 | 2-3 天 | 全部 |
Phase 1a 交付价值:
- 经营看板页面: 用户打开工作台即可看到角色化看板(指标卡片 + 趋势图 + KPI 进度)
- AI 聊天查询: 飞书聊天中可查组织架构、审批待办/详情、经营指标(含卡片 chart 可视化)
- 深度对话分析: 用户可通过自然语言追问下钻/对比,AI 调用 dashboard 工具返回图表 + 分析
- 深链 + Opener 基础设施: Tauri 深链已就绪 + opener 插件就绪,为 Phase 1b 审批跳转铺路
Phase 1b: 审批工作台 UI(5-7 天)
| 任务 | 工作量 | 依赖 |
|---|---|---|
| 审批列表/详情 UI(原生 React + Arco) | 3-4 天 | Phase 1a 查询工具 |
| AppLink 跳转按钮 + Tauri opener 集成 | 1 天 | Phase 1a opener |
| Tauri 深链前端路由监听 | 0.5 天 | - |
| 审批侧边栏集成(ChatSlider extraTabs) | 0.5 天 | 审批列表 UI |
| 飞书审批模板管理表 | 0.5 天 | 审批工具 |
| 集成测试 | 0.5-1 天 | 全部 |
Phase 1b 交付价值: 工作台可视化审批列表/详情,用户点击「在飞书中处理」跳转飞书客户端完成审批操作。
Phase 2: 交互增强(2-4 天)
| 任务 | 工作量 | 依赖 |
|---|---|---|
卡片交互路由扩展 (action.rs) |
1-2 天 | Phase 1a |
| 审批预览卡片模板 | 1 天 | 卡片路由 |
| 审批状态变更推送 | 1 天 | Webhook 已有 |
Phase 3: 自动化与精细化(4-7 天)
| 任务 | 工作量 | 依赖 |
|---|---|---|
| 定时增量同步调度 | 1-2 天 | Phase 0 |
| 权限感知搜索(按用户过滤文档) | 3-5 天 | Phase 1a 组织架构 |
总工作量
| 阶段 | 天数 | 累计 | 交付物 |
|---|---|---|---|
| Phase 0 | 0 | 0 | 飞书 Bot + Wiki 同步 + Webhook |
| Phase 1a | 11-18 | 11-18 | MCP 查询工具 + 经营看板页面 + 深链/opener 基础设施 + AI 聊天查询 |
| Phase 1b | 5-7 | 16-25 | 审批工作台 UI(列表/详情 + AppLink 跳转 + 深链) |
| Phase 2 | 2-4 | 18-29 | 卡片交互增强 |
| Phase 3 | 4-7 | 22-36 | 自动化调度 + 权限搜索 |
对比 v3.0: 首个可用版本 11-18 天(含看板页面 + 深链);总工作量 22-36 天。v3.4 通过 AppLink 跳转模式消除 OAuth/动态表单/后端写代理,比 v3.3 的 28-46 天减少 6-10 天。
八、风险评估与缓解
8.1 技术风险
| 风险 | 级别 | 缓解措施 |
|---|---|---|
| 飞书 API 限流导致同步失败 | 中 | Connector 已实现 4 QPS 限流 + 429 重试;通讯录加本地缓存 |
| LLM 误填审批表单 | 低 | 表单在飞书客户端中填写 + 飞书原生校验;AI 仅生成预填数据,人工确认 |
| 飞书 WebSocket 断连 | 低 | Plugin 已有自动重连逻辑 |
| 文档类型限制(仅 docx) | 低 | Phase 1 不影响;后续可扩展 Sheet/Mindnote 支持 |
| 数据中台 API 不可用 | 中 | NomiFun 侧 5min 缓存 + 降级提示「数据暂不可用」 |
| 数据中台指标口径变更 | 中 | 版本化 API 约定 + 季度对齐检查 |
| 已消除 — v3.4 不使用 H5 JS SDK,AppLink 跳转飞书原生审批 UI | ||
| 已消除 — v3.4 不需要 OAuth,AppLink 跳转利用飞书客户端登录态 | ||
| 已消除 — v3.4 不渲染表单,飞书原生表单引擎处理 | ||
| 已消除 — v3.4 不使用 user_access_token | ||
| Tauri 深链未注册 | 低 | 项目已配置 tauri-plugin-deep-link,nomifun scheme 已注册 |
| AppLink 跳转失败(未安装飞书) | 低 | AppLink 网页会提示下载飞书;NomiFun 可检测并提示用户 |
| tauri-plugin-opener 权限配置 | 低 | scope 限制为 applink.feishu.cn 域名,Tauri 官方插件 |
8.2 业务风险
| 风险 | 级别 | 缓解措施 |
|---|---|---|
| 已消除 — 审批不封装为 MCP 工具,AI 无法执行审批操作 | ||
| 组织架构数据泄露 | 中 | 飞书 App 可见范围控制;PII 脱敏;后端独立校验 |
| 用户绑定错误(open_id 映射到错误账号) | 高 | pairing 服务强制验证;首次使用需配对确认 |
8.3 错误降级策略
| 场景 | ���级行为 | 用户感知 |
|---|---|---|
| 数据中台 API 不可用 | 返回缓存数据(如有)+ 提示「数据可能非最新」 | 看板显示缓存指标 + 警告标识 |
| 数据中台 API 超时 | 5s 超时 → 返回缓存或空状态 | 看板显示「数据加载中」骨架屏 |
| 飞书审批 API 不可用 | MCP 工具返回错误 JSON → AI 告知用户 | AI 回复「审批服务暂时不可用」 |
| 飞书通讯录 API 限流 | 返回 org_cache 缓存数据(TTL 15min) |
AI 回复缓存数据(可能略旧) |
| 飞书知识库同步失败 | 保留上次成功快照 | AI 仍可检索旧快照 |
| AppLink 跳转失败 | 检测飞书是否安装;未安装时提示下载链接 | 用户可手动打开飞书审批中心 |
| Tauri 深链未注册 | 卡片 URL 改为 HTTPS 网页版降级链接 | 用户在浏览器中查看审批 |
| LLM 工具调用超时 | 30s 超时 → AI 回复「查询超时,请稍后重试」 | 用户可重新提问 |
8.4 体验风险
| 风险 | 级别 | 缓解措施 |
|---|---|---|
| AI 回复延迟(多工具调用链路长) | 中 | streaming dispatch 已支持;卡片预览减少往返 |
| 看板数据刷新延迟 | 低 | 5min 缓存 + 手动刷新按钮;飞书侧 AI 查询实时获取 |
| 飞书卡片兼容性 | 低 | 使用飞书标准 template API;测试不同端(PC/iOS/Android) |
| 群聊 @bot 误触发 | 低 | require_mention 配置项已有 |
| 深链跳转后页面状态丢失 | 中 | 深链参数写入 URL query,页面加载时从 query 恢复预填数据 |
| AppLink 跳转后用户需手动返回 NomiFun | 低 | 审批完成后 Webhook 推送状态变更通知;用户可点击通知返回 |
九、测试策略
9.1 后端单元测试
| 模块 | 测试方式 | 覆盖点 |
|---|---|---|
caps_feishu_org.rs |
wiremock 模拟飞书 API | 搜索/部门树/用户详情 + 限流 + 401 刷新 + PII 脱敏 |
caps_feishu_approval.rs |
wiremock 模拟飞书审批 API | 列表/详情/模板查询 + 空结果 + 分页 |
caps_dashboard.rs |
wiremock 模拟数据中台 API | 概览/趋势/下钻/对比 + 角色过滤 + 缓存命中 |
| 通讯录缓存 | SQLite in-memory | TTL 过期 + 缓存命中 + 缓存未命中 |
| 角色权限映射 | SQLite in-memory | 各角色 scope 计算 + scope_override |
参考:
connector_feishu.rs已有 8 个 wiremock 测试用例,新模块遵循相同模式。
9.2 前端组件测试
| 组件 | 测试方式 | 覆盖点 |
|---|---|---|
ApprovalListPage |
React Testing Library | 列表渲染 + 空状态 + 加载状态 |
ApprovalDetailPage |
React Testing Library | 详情展示 + 时间线 + 操作按钮 |
DashboardPage |
React Testing Library | 角色化渲染 + 指标卡片 + 图表 |
OpenInFeishuButton |
Mock opener plugin | AppLink URL 构造 + 跳转触发 |
| 深链路由 | Mock Tauri event | URL 解析 + 路由导航 + 参数传递 |
9.3 集成测试
| 场景 | 测试方式 | 验证点 |
|---|---|---|
| 飞书消息 → AI → 工具调用 → 回复 | 端到端 mock | 完整链路无断裂 |
| 审批辅助填表 → 卡片 → 深链跳转 | 手动 + Playwright | 预填数据正确传递到工作台 |
| 看板角色化数据 | 多用户 mock | 不同角色看到不同数据范围 |
| AppLink 跳转流程 | 手动 + Playwright | AppLink URL 正确性 + 飞书客户端唤起 + 审批页面打开 |
| 审批状态变更推送 | Mock webhook | 审批同意/拒绝后 Webhook 通知正确送达 |
9.4 回归测试
- Registry 自测: 新增工具注册后,
all_caps_modules_are_mod_declared_and_registeredCI 测试自动验证(见registry/mod.rs注释) - 权限矩阵回归: 新增
Read级工具在 Channel Surface 自动放行,无需额外配置 - 工具名长度: Registry 自测强制工具名 ≤ 42 字符
十、与企微(WeCom)的未来扩展
NomiFun 的架构设计天然支持多平台扩展:
- Channel Plugin trait:
crates/backend/nomifun-channel/src/plugin.rs定义了ChannelPlugintrait,新增企微只需实现该 trait - Connector trait:
KnowledgeConnectortrait 可新增WeComConnector实现企微文档同步 - MCP 工具域: 飞书工具命名为
nomi_feishu_*,企微工具命名为nomi_wecom_*,共享 domain 标签或独立 - Webhook:
sender.rs已支持多平台格式,新增企微格式即可 - 权限矩阵: 企微消息同样走
Surface::Channel,复用现有安全策略
预估额外工作量: 10-15 天(主要是企微 API 对接 + Channel Plugin 实现)
十一、结论
可行性评估:✅ 高度可行
NomiFun 现有架构与飞书集成高度契合:
| 基础设施 | 就绪度 | 说明 |
|---|---|---|
| 飞书消息通道 | 100% | WebSocket、消息收发、卡片交互全部已实现 |
| 飞书知识库同步 | 100% | Connector 完整实现,含增量同步、限流、测试 |
| MCP 工具总线 | 100% | 132+ 工具已注册,新增工具只需 3 步 |
| AI Agent 工具调用 | 100% | 完整链路从消息到工具执行到回复已打通 |
| Webhook 通知 | 100% | 飞书卡片 + HMAC 签名已实现 |
| 权限与安全 | 100% | Surface × DangerTier 矩阵 + 配对授权 + PII 脱敏 |
| 前端基础设施 | 75% | Arco Design 已有;recharts 需新增依赖;看板/审批页面需新建 |
| 数据中台对接 | 0% | 需新建 caps_dashboard 域 + 数据中台 API client |
核心结论
- Phase 0 零代码: 配置飞书自建应用即可启用消息通道和文档同步
- Phase 1a 首个可用版本 ~11-18 天: MCP 查询工具 + 经营看板页面 + 深链/opener 基础设施上线,用户打开工作台即可看角色化看板,AI 聊天中可深度查询
- Phase 1b 审批工作台 UI ~5-7 天: 审批列表/详情 + AppLink 跳转飞书客户端 + Tauri 深链
- 总工作量 ~22-36 天: 含交互增强、自动化调度、Tauri 深链、opener 插件
- 审批安全: AI 仅查询/辅助填表,审批操作全部由人在飞书客户端中完成(AppLink 跳转 + 飞书原生表单 + 飞书登录态)
- AppLink 跳转模式: v3.4 消除了 v3.3 的 OAuth/动态表单/后端写代理三大复杂度,工作量减少 6-10 天,且保留增量升级路径
- 看板角色化: 后端强制角色过滤,不同用户看到不同层级指标
- 安全合规: 权限矩阵、审计日志、PII 脱敏全覆盖
- 可扩展: 架构天然支持企微等未来平台扩展
- 错误降级: 每个外部依赖均有降级策略,确保单点故障不影响整体可用性
- 测试覆盖: 后端 wiremock + 前端 RTL + 端到端集成测试,遵循现有测试模式