Skip to content

SA 空间预约:查询与取消功能设计 (Query & Cancel PRD)

版本: v1.0 日期: 2026-06-01 状态: 初始设计稿 目标: 补齐"查询我的会议预定"和"取消会议预定"两个操作,完成会议预定功能闭环。


1. 设计背景与前提

1.1 现状

当前 SA 空间预约仅支持正向会议预定流程(Slot Filling → Time Pipeline → Resource Pipeline → 预定确认),查询和取消两个能力完全缺失,用户无法在系统内查看已有的会议预定,也无法取消。

1.2 产品约束

约束项说明
首个潜在落地客户奇瑞(使用飞书作为办公平台)
产品形态飞书工作台轻应用(小程序/H5)
能力边界不做重、不复制飞书日历功能;编辑/修改通过 AppLink 跳转飞书原生日历处理
目标轻量闭环:查得到、取消得掉,编辑走飞书

1.3 设计原则

  1. 轻量化:查询+取消仅做最简功能,无参会人管理、无会议群、无复杂编辑
  2. 飞书优先:编辑/修改→AppLink 跳飞书日历;取消→我们自己做(飞书无取消 API)

2. 功能定义

2.1 查询我的会议预定 (Query My Bookings)

用户故事:作为员工,我希望能快速查看我最近预定的会议室,以便掌握自己的日程安排。

查询范围:默认展示配置的默认查询范围的预定(当前默认未来 7 天)。用户可指定日期缩小范围。

筛选维度

维度说明
时间范围date_from + date_to,默认未来 7 天;支持指定单日或日期范围

展示上限:最多展示 20 条会议记录,超出截断不显示。组件采用固定高度 + 内部滚动(max-height: 320px),避免撑开聊天区域。房间名仅展示会议室名称,不带路径。

2.2 取消会议预定 (Cancel Booking)

用户故事:作为员工,我希望能在查看我的预定时直接取消不需要的会议,以便释放会议室资源。

交互方式:行内快捷取消(每行显示 [✕] 按钮)+ 二次确认 Sheet。

无需详情卡片,采用行内取消方案。如需编辑会议(如修改时间、添加参会人),点击蓝色会议名称通过 AppLink 跳转飞书日历处理。

取消时限会议未结束时(当前时间 < 结束时间) 可取消,[✕] 可点击;已结束的会议 [✕] 置灰。按结束时间而非开始时间判断,允许会中提前散会释放房间资源。

主流日历产品(Google Calendar、飞书日历等)均不设取消时限,会议结束后仍可取消。我们的会议室预定场景额外增加了"已结束会议置灰"规则,避免无意义的操作入口。


3. 交互原型 (ASCII Prototype)

3.1 入口:对话触发

(Agent 响应查询)

┌──────────────────────────────────────────────┐
│  📋 您的会议预定 (共3场)                       │
│  ┌──────────────────────────────────────┐    │
│  │ 今天 06/01                           │    │
│  │ ────────────────────────────────     │    │
│  │ 09:00-10:00  火星厅  站立会①    [✕] │    │ ← 未结束,可取消
│  │ 10:00-11:00  305     研发周会①   [✕̲]│    │ ← 已结束,✕ 置灰
│  │ 14:00-16:00  总裁办  客户评审①   [✕] │    │ ← 会中,可取消
│  └──────────────────────────────────────┘    │
│  ╱ 最大高度 320px,超出内部滚动 ╲             │
│                                               │
│  ① 蓝色可点击,点击拉起飞书日历编辑 AppLink   │
│  [✕] 仅未结束的会议可取消(含会中),已结束置灰  │
└──────────────────────────────────────────────┘

3.2 取消:二次确认 Sheet

用户点击 [✕] 后弹出底部 Sheet:

┌──────────────────────────────────────────────┐
│  ⚠️ 确认取消会议                               │
│                                               │
│  ┌────────────────────────────────────────┐   │
│  │                                        │   │
│  │  主题:研发周会                         │   │
│  │  时间:今天 10:00 - 11:00              │   │
│  │  地点:火星厅                            │   │
│  │  预定人:强哥                           │   │
│  │                                        │   │
│  │  取消后无法恢复,请谨慎操作             │   │
│  │                                        │   │
│  └────────────────────────────────────────┘   │
│                                               │
│  [确认取消]              [暂不取消]            │
└──────────────────────────────────────────────┘

3.3 取消:结果反馈

取消成功:
  小程序原生 wx.showToast({ title: '已取消', icon: 'success' })
  1.5s 后 Toast 自动消失,列表刷新(条目移除/变灰)

取消失败:
  小程序原生 wx.showToast({ title: '取消失败', icon: 'error' })
  Toast 消失后列表保持当前状态

3.4 空结果

(AI 话术回复,无组件)
"当前查询范围内没有会议预定"

4. Slot 定义与 Prompt 设计

4.1 查询槽位

json
{
  "intent": "my_bookings",
  "date_from": "yyyyMMdd | null",
  "date_to": "yyyyMMdd | null"
}
槽位必填说明示例
intentL2 意图标志,固定为 my_bookingsmy_bookings
date_from查询起始日期;null 默认为今天20260601
date_to查询截止日期;null 默认为今天+7 天20260607

4.2 Slot Filling Prompt(设计注意)

QUERY 与 BOOK 共用同一个 Slot Filling Prompt,由 intent 字段区分输出结构。技术实现上需注意:

  • QUERY 场景不新增 LLM 节点,避免额外延时
  • 同一 Prompt 内通过 intent: "my_bookings" 区分查询场景,BOOK 字段全填 null
  • 下游 Dify 根据 intent 字段分流:"my_bookings" → Query Pipeline;其他值 → Time + Resource Pipeline

具体 Prompt 格式与字段映射由技术团队在 Dify 编排时确定,以现有 Prompt 模板为基础扩展即可。

4.3 多智能体路由(MAS)

查询与取消功能不单独拆分为新 Sub-Agent,而是在会议预定 Agent(Meeting Specialist) 内部新增 L2 意图分支:

Master Agent (L1 路由)
  ├── 会议预定 Agent (Meeting Specialist)
  │    ├── L2: BOOK    (已有)
  │    └── L2: QUERY   (新增)   ← 取消是前端组件交互,LLM 不感知
  ├── 设备控制 Agent
  └── 工单建单 Agent

L1 路由关键词扩展:

触发关键词L1 路由L2 意图处理函数
查一下/查看/看看/有什么会/我的预定/我的会议/我的会议预定→ 会议预定 AgentQUERY → my_bookingsrenderBookingListWithCancel()

注意:查看 等关键词需优先匹配 QUERY 意图,避免被其他 Agent 拦截。


5. 编排流程 (Dify Workflow)

5.1 整体编排图

5.2 Query Pipeline 逻辑

输入: { date_from, date_to }

1. 确定查询范围
   - date_from = null → 默认为今天
   - date_to = null → 默认为配置的默认查询天数(当前为今天+7天)
   - 最终查询范围 = [date_from 00:00, date_to 23:59]

2. 调用业务系统 API
   - GET /my_bookings?user_id={current_user}&from={start}&to={end}

3. 返回列表
   - 列表为空 → 话术回复 "当前查询范围内没有会议预定"
   - 列表非空 → 按日期分组,按时间排序 → 前端渲染(最多展示 20 条,超出截断并提示)

5.3 取消操作逻辑(纯工程化,无 LLM 参与)

取消由前端组件处理,LLM 不感知、不参与。

触发条件: 用户在预定列表卡片中点击某条预定的 [✕]

1. 前端判断: 会议结束时间 > 当前时间 → [✕] 可点击(未结束);否则 [✕] 置灰(已结束)

2. 弹出确认 Sheet(展示会议详情: 主题/时间/地点)

3. 用户点击"确认取消"
   - 调用 API: POST /cancel_booking
     { booking_id, user_id, event_id }

4. 结果处理
   - 成功 → wx.showToast({ title: '已取消', icon: 'success' }) → 1.5s 后列表自动刷新
   - 失败 → wx.showToast({ title: '取消失败', icon: 'error' }) → 列表保持不变

6.1 交互方案

会议主题(topic)显示为蓝色可点击文字,点击后通过飞书 AppLink 拉起原生日历编辑页。

┌──────────────────────────────────────────────┐
│  10:00-11:00  火星厅  研发周会①        [✕]  │
│                       ↑                     │
│                 蓝色可点击,样式: color:#3370ff │
│                 点击 → 飞书日历编辑 AppLink    │
└──────────────────────────────────────────────┘

设计意图:无需额外详情页,编辑入口自然嵌入列表行。"取消"走我们自己的轻量确认 Sheet,"编辑"交给飞书原生能力,各司其职。

https://applink.feishu.cn/client/calendar/event/detail
  ?calendarId=primary
  &key={event_id}
  &originalTime=0
  &startTime={timestamp}
参数来源
event_id查询 API 返回的 event_id 字段
timestamp会议开始时间戳(秒级),由 start_time 转换

6.3 多平台适配

平台处理方式
飞书直接使用上述 AppLink 拉起飞书日历编辑页
钉钉暂不实现,降级为仅展示文案(钉钉 AppLink 格式待验证)
自有系统暂不实现,降级为仅展示文案(无移动端)

7. API 接口契约

7.1 查询我的会议预定

GET /api/v1/bookings/my
  ?user_id={string}
  &from={ISO8601}
  &to={ISO8601}
  &limit={int}          // 可选,默认 20,最大 50

Response:
{
  "bookings": [
    {
      "booking_id": "string",
      "event_id": "string",        // 飞书/钉钉 日程ID
      "room_name": "string",       // 房间展示名,已去除路径(如"火星厅"而非"F8-305火星厅")
      "room_id": "string",
      "topic": "string",
      "start_time": "ISO8601",
      "end_time": "ISO8601",
      "status": "confirmed | cancelled"
    }
  ]
}

room_name 由后端清洗后返回,前端直接展示,不做二次处理。

7.2 取消会议预定

POST /api/v1/bookings/cancel

Request:
{
  "booking_id": "string",
  "user_id": "string"          // 必须为预定本人,后端校验
}

Response:
{
  "success": true,
  "message": "string"
}

后端必须校验 user_id 为该预定的预定人(创建者),非本人取消返回 403。


8. 异常场景与话术

场景触发条件Agent 话术/UI 表现
无会议查询范围为空话术回复 "当前查询范围内没有会议预定"
会议已结束当前时间 ≥ 结束时间[✕] 置灰不可点击,无 LLM 话术
API 超时查询/取消接口超时"系统繁忙,请稍后再试"

9. 测试用例

9.1 查询用例

A. 泛化查询(不指定时间→默认查询范围)

ID用户输入预期 Slot预期结果
Q-00"查一下我的会议预定"{date_from:null, date_to:null}默认查询范围全部会议
Q-01"我的会议"{date_from:null, date_to:null}同上
Q-02"我的会议预定"{date_from:null, date_to:null}同上
Q-03"查一下我的日程"{date_from:null, date_to:null}同上(注:仅限会议预定范围,非全量日程)

B. 按指定日期

ID用户输入预期 Slot预期结果
Q-05"今天有什么会"{date_from:today, date_to:today}展示今天会议
Q-06"今天的会议安排"{date_from:today, date_to:today}同上
Q-07"我今天有哪些会"{date_from:today, date_to:today}同上
Q-08"明天有什么会"{date_from:tomorrow, date_to:tomorrow}展示明天会议
Q-09"看看明天的会"{date_from:tomorrow, date_to:tomorrow}同上
Q-10"查一下6月10号的会"{date_from:20260610, date_to:20260610}展示该日会议
Q-11"查一下2026年6月10号的会"{date_from:20260610, date_to:20260610}同上

C. 按日期范围

ID用户输入预期 Slot预期结果
Q-12"这周有什么会"{date_from:this_monday, date_to:this_sunday}展示本周全部会议
Q-13"下周的会议"{date_from:next_monday, date_to:next_sunday}展示下周全部会议
Q-14"下周一到周三有什么会"{date_from:next_monday, date_to:next_wednesday}展示下周一至三会议
Q-15"查一下6月10号到6月20号的会"{date_from:20260610, date_to:20260620}展示该区间会议
Q-16"这周五的会"{date_from:this_friday, date_to:this_friday}展示本周五会议

D. 边界场景

ID用户输入条件预期行为
Q-17"查一下我的会议预定"查询范围无任何会议话术回复"当前查询范围内没有会议预定"
Q-18"查一下我的会议预定"全部会议已取消/已过期话术回复"当前查询范围内没有会议预定"
Q-19"上周的会"日期在过去按上周日期查,过去会议照常展示

9.2 取消用例

ID前置条件操作路径预期行为
C-00列表中有会议查询列表 → 点击 [✕] → 确认取消成功 → Toast "已取消:研发周会",列表自动刷新
C-01列表中有会议查询列表 → 点击 [✕] → 暂不取消Sheet 关闭,列表保持不变
C-02列表中有会议确认取消 → API 返回失败Toast "取消失败,请重试",列表保持不变
C-03列表中有会议确认取消 → 网络超时Toast "网络异常,请稍后再试",列表保持不变
C-04会议已结束(当前 ≥ 结束时间)查看列表[✕] 置灰不可点击
C-05会议未结束(当前 < 结束时间)查看列表[✕] 可点击,含会中状态

10. 待决议题

议题状态决策
查询取消是否独立 Sub-Agent已定不拆分,并入会议预定 Agent 内作为 L2 意图
行内取消 vs 详情页取消已定行内 [✕] + 确认 Sheet,不做详情页
查询默认范围已定默认查询范围(当前为今天+7天),可配置
AppLink 是否 MVP 实现已定飞书场景直接实现,会议主题蓝色可点击跳转编辑
取消后是否通知参会人不做通知是飞书日历自身的能力,本功能不涉及
取消后是否回滚整备不做整备 Agent 有独立的日程扫描逻辑,无需联动处理

Released under the Private License.