f7a720204a
- 移除 GovAI, nomifun-tauri, 算力盒子 的 submodule 引用 - 添加所有子项目的完整源代码 - 保留原始 .git 为 .git.bak 备份
1178 lines
60 KiB
Markdown
1178 lines
60 KiB
Markdown
# 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 架构**:
|
||
|
||
```rust
|
||
// 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()` 执行两阶段同步:
|
||
1. **Phase 1**: 分页 `list_documents`,收集文档引用 + 墓碑(删除标记)
|
||
2. **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 行为
|
||
|
||
**工具定义模式(已验证)**:
|
||
|
||
```rust
|
||
// 新增工具只需:
|
||
// 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 | 员工详情(职位、上级、邮箱) |
|
||
|
||
**实现要点**:
|
||
|
||
1. **复用 FeishuConnector 的 token 管理**: 将 `connector_feishu.rs` 中的 `token_cache` / `rate_limit` / `authed_get` 提取为公共 trait 或共享模块,避免重复实现
|
||
2. **本地缓存**: 飞书通讯录 API 有严格限流(10 QPS),需在 SQLite 中维护 `org_cache` 表,TTL 15 分钟
|
||
3. **PII 脱敏**: 手机号/邮箱在返回给 LLM 前脱敏(`138****1234`),仅在用户明确请求时解敏
|
||
4. **权限边界**: 飞书 app 的通讯录可见范围 = NomiFun 可访问的数据边界
|
||
|
||
**参数 struct 示例**:
|
||
|
||
```rust
|
||
#[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 的角色是:
|
||
1. **查询待办**: "我有哪些待审批?"
|
||
2. **查询详情**: "这个审批的具体内容是什么?"
|
||
3. **辅助填表**: AI 根据用户描述生成表单预览,用户在飞书客户端中确认并提交
|
||
4. **状态提醒**: 审批状态变更时通过 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` ↔ 飞书审批定义 code
|
||
- `form_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
|
||
```
|
||
|
||
> **来源**: 飞书官方文档 [打开审批页面](https://open.feishu.cn/document/applink-protocol/supported-protocol/open-an-approval-page)。`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`)。
|
||
|
||
```json
|
||
// apps/desktop/tauri.conf.json (已有配置)
|
||
"plugins": {
|
||
"deep-link": {
|
||
"desktop": {
|
||
"schemes": ["nomifun"]
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
```toml
|
||
# 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 处理。已有配置已正确处理跨平台。
|
||
|
||
**需新增**: 前端路由监听深链事件并导航到对应页面。
|
||
|
||
```typescript
|
||
// 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 插件。
|
||
|
||
**需新增依赖**:
|
||
|
||
```toml
|
||
# apps/desktop/Cargo.toml 新增
|
||
tauri-plugin-opener = "2"
|
||
```
|
||
|
||
```typescript
|
||
// 前端调用
|
||
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`):
|
||
```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。参见[飞书卡片交互文档](https://open.feishu.cn/document/common-capabilities/message-card/add-card-interaction/interaction-module)。
|
||
|
||
### 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 直跳模式**(简单可靠,无需后端介入):
|
||
|
||
```json
|
||
{
|
||
"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 | 多指标对比分析 |
|
||
|
||
**角色化数据过滤机制**:
|
||
|
||
```rust
|
||
#[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、个人任务进度、个人考勤 |
|
||
|
||
**实现要点**:
|
||
|
||
1. **用户角色获取链路**: `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
|
||
2. **数据中台 API 对接**: 在 `GatewayDeps` 中新增 `data_platform_client` 字段(需在 `deps.rs` 添加字段 + `routes.rs` 注入)
|
||
3. **缓存策略**: 高频指标在 NomiFun 侧缓存 5 分钟,减少数据中台压力
|
||
4. **前端看板页面**: 基于 Arco Design + recharts 构建可视化看板(**需先安装 recharts 依赖**)
|
||
5. **飞书卡片投递**: 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` 组件:
|
||
|
||
```rust
|
||
// 新增:在 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 调度器:
|
||
|
||
```rust
|
||
// 每 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` — 审批模板映射
|
||
|
||
```sql
|
||
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` — 用户角色映射(经营看板权限)
|
||
|
||
```sql
|
||
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` — 飞书通讯录本地缓存
|
||
|
||
```sql
|
||
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` — 审计日志
|
||
|
||
```sql
|
||
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 自建应用创建
|
||
|
||
1. 登录 [飞书开放平台](https://open.feishu.cn/) → 创建企业自建应用
|
||
2. 获取 `App ID` + `App Secret` — 用于 NomiFun connector credential
|
||
3. 配置应用名称、头像、描述
|
||
|
||
### 5.2 权限申请
|
||
|
||
| 权限范围 | 权限标识 | 用途 |
|
||
|----------|----------|------|
|
||
| 通讯录 | `contact:user.base:readonly` | 搜索/查询员工基本信息 |
|
||
| 通讯录 | `contact:user.phone:readonly` | 查询手机号(需审批) |
|
||
| 通讯录 | `contact:user.email:readonly` | 查询邮箱(需审批) |
|
||
| 通讯录 | `contact:department.base:readonly` | 获取部门树 |
|
||
| 审批 | `approval:approval:read` | 查询审批列表/详情/模板(仅读) |
|
||
| ~~审批~~ | ~~`approval:approval:write`~~ | ~~不申请~~ — 审批通过 AppLink 跳转飞书客户端由人执行 |
|
||
| 文档 | `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 约定 + 季度对齐检查 |
|
||
| ~~飞书 JS SDK 兼容性~~ | ~~中~~ | **已消除** — v3.4 不使用 H5 JS SDK,AppLink 跳转飞书原生审批 UI |
|
||
| ~~飞书 OAuth redirect_uri~~ | ~~中~~ | **已消除** — v3.4 不需要 OAuth,AppLink 跳转利用飞书客户端登录态 |
|
||
| ~~动态表单字段类型覆盖不全~~ | ~~中~~ | **已消除** — v3.4 不渲染表单,飞书原生表单引擎处理 |
|
||
| ~~user_access_token 过期~~ | ~~低~~ | **已消除** — 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 业务风险
|
||
|
||
| 风险 | 级别 | 缓解措施 |
|
||
|------|------|----------|
|
||
| ~~审批权限过大(AI 可代替人审批)~~ | ~~高~~ | **已消除** — 审批不封装为 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_registered` CI 测试自动验证(见 `registry/mod.rs` 注释)
|
||
- **权限矩阵回归**: 新增 `Read` 级工具在 Channel Surface 自动放行,无需额外配置
|
||
- **工具名长度**: Registry 自测强制工具名 ≤ 42 字符
|
||
|
||
---
|
||
|
||
## 十、与企微(WeCom)的未来扩展
|
||
|
||
NomiFun 的架构设计天然支持多平台扩展:
|
||
|
||
1. **Channel Plugin trait**: `crates/backend/nomifun-channel/src/plugin.rs` 定义了 `ChannelPlugin` trait,新增企微只需实现该 trait
|
||
2. **Connector trait**: `KnowledgeConnector` trait 可新增 `WeComConnector` 实现企微文档同步
|
||
3. **MCP 工具域**: 飞书工具命名为 `nomi_feishu_*`,企微工具命名为 `nomi_wecom_*`,共享 domain 标签或独立
|
||
4. **Webhook**: `sender.rs` 已支持多平台格式,新增企微格式即可
|
||
5. **权限矩阵**: 企微消息同样走 `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 + 端到端集成测试,遵循现有测试模式
|