Skip to content

Architecture interface design

Lupeng Han edited this page Jul 28, 2026 · 14 revisions

语音日程系统新版架构文档

1. 方案结论

  1. 客户端不再独立承担全部语音理解和提醒编排。
  2. 客户端进入界面后建立 WebSocket 连接,作为后续实时通信主通道。
  3. 音频文件通过 HTTP 上传到服务端。
  4. 服务端完成 ASR + LLM 结构化提取后,通过 WebSocket 回传结果。
  5. 客户端用统一表单完成二次确认、地理位置补全、时间补全和提醒参数补全。
  6. 日程最终落到单一 schedules 表中,状态和关联信息一并保存。
  7. 提醒采用三段式策略,优先保证触达,再处理重复提醒控制。

这一版的重点不是“完整日历”,而是“日程创建 + 状态驱动提醒 + 语音结构化”闭环。

2. 产品功能描述

2.1 核心目标

MVP 要验证三件事:

  1. 用户能不能快速创建日程。
  2. 系统能不能在不同场景下使用不同提醒方式。
  3. 语音创建能不能稳定转成可确认、可执行的日程。

2.2 三种提醒情况

情况一:应用在前台且网络通畅

触发方式:

  1. 应用内弹窗。
  2. 页面内高亮提示。
  3. 伴随声音或震动。

适用场景:

  1. 用户正在使用 App。
  2. 可以给出最完整的上下文。
  3. 不需要系统级强打断。

情况二:应用在后台且网络通畅

触发方式:

  1. 系统级全局弹窗。
  2. 系统通知栏提醒。

适用场景:

  1. App 仍保持在线。
  2. 需要由系统层接管提醒触达。
  3. 可以通过 WS 收到实时控制消息。

情况三:不满足前两种条件

客户端根据自身能力自行决定是否降级为普通通知。

2.3 创建日程

日程支持两种入口:

  1. 手动创建。
  2. 语音创建。

二者共用一个表单。

语音创建流程:

  1. 客户端录音。
  2. 客户端通过 HTTP 上传音频文件。
  3. 服务端返回上传受理结果。
  4. 服务端完成 ASR。
  5. 服务端完成 LLM 结构化提取。
  6. 服务端通过 WS 推送结构化日程草稿。
  7. 客户端弹出统一表单。
  8. 用户补全信息并确认。
  9. 客户端提交日程最终结果。
  10. 如果日程包含时间,服务端做时间冲突检测并返回提示。
  11. 创建成功后,服务端和客户端分别进入自己的提醒准备状态。

日程允许两种主类型:

形态 schedule_type 必要信息 触发条件
时间类 time start_time 进入时间提醒窗口
地点类 location latitude + longitude 用户进入地理围栏

语音创建时,schedule_type 由 LLM 根据用户语义做意图识别后输出。手动创建时,由前端根据用户选择或填写内容确定。 schedule_type 只表示主意图类型,时间和地点同时存在时仍按 time 落库,是否触发地点提醒由字段本身判断。

2.4 地理位置

表单内部支持:

  1. 地理位置选择。
  2. 地理围栏设置。
  3. 默认提醒参数补全。

默认值建议:

  1. 时间提前提醒:15min
  2. 地理围栏半径:100m

2.5 冲突检测

创建日程时如果填写了时间,必须做时间冲突检测。

如果目标时间段已有日程,服务端返回:

  1. 冲突提示。
  2. 冲突项列表。
  3. 是否允许继续创建的建议。

冲突检测是提示,不一定是硬拦截。

3. 架构总览

3.1 客户端

客户端负责:

  1. 界面展示。
  2. WebSocket 长连接。
  3. 音频录制。
  4. 音频上传。
  5. 表单编辑和二次确认。
  6. 本地日程缓存。
  7. 系统日程和系统闹钟创建、查询与删除。
  8. 前台弹窗提醒。
  9. 后台系统弹窗/通知提醒。
  10. 位置信息上报。
  11. 根据前后台状态和系统可用性选择具体提醒通道。

3.2 服务端

服务端负责:

  1. 音频文件接收。
  2. ASR 调用。
  3. LLM 结构化提取。
  4. WebSocket 消息分发。
  5. 日程冲突检测。
  6. 日程状态监控。
  7. 地点与时间窗口判断。
  8. 提醒控制消息下发。
  9. 提醒通道选择由客户端自行完成。

3.3 外部依赖

  1. 第三方 ASR 服务。
  2. 大模型服务。
  3. 第三方地图 SDK。
  4. Android 系统提醒能力。

4. 模块拆分

4.1 客户端模块

4.1.1 界面模块

职责:

  1. 显示日程列表。
  2. 显示创建/编辑表单。
  3. 显示语音解析结果。
  4. 显示冲突提示。

4.1.2 语音录制模块

职责:

  1. 录音。
  2. 音频格式转换。
  3. 发起上传。

4.1.3 WebSocket 模块

职责:

  1. 建立和维护连接。
  2. 接收结构化草稿。
  3. 接收提醒控制消息。
  4. 上传位置信息。
  5. 上传日程确认结果。

4.1.4 本地存储模块

职责:

  1. 缓存日程。
  2. 缓存解析结果。
  3. 缓存提醒状态。
  4. 缓存提醒通道状态。

4.1.5 提醒执行模块

职责:

  1. 前台弹窗。
  2. 后台浮窗。
  3. 系统通知。
  4. 根据当前应用状态决定提醒展示方式。

4.1.6 地图与位置模块

职责:

  1. 地点搜索。
  2. 地点确认。
  3. 地理围栏设置。
  4. 实时位置上报。

4.2 服务端模块

4.2.1 音频接入模块

职责:

  1. 接收音频文件。
  2. 校验格式和大小。
  3. 生成处理任务 ID。

4.2.2 语音解析模块

职责:

  1. 调用 ASR。
  2. 调用 LLM。
  3. 识别 schedule_type
  4. 生成结构化草稿。

4.2.3 WebSocket 网关

职责:

  1. 建立客户端会话。
  2. 维护设备在线状态。
  3. 分发解析结果和提醒控制消息。
  4. 接收位置上报。

4.2.4 日程服务模块

职责:

  1. 创建日程。
  2. 编辑日程。
  3. 查询日程。
  4. 冲突检测。

4.2.5 监控与调度模块

职责:

  1. 监听日程时间。
  2. 监听地理位置。
  3. 判断时间、空间以及组合触发条件。
  4. 进入提醒窗口后先触发系统引用检查。
  5. 根据检查结果决定是否触发软件提醒。
  6. 触发系统日程和系统闹钟删除指令。

4.2.6 地点判定模块

职责:

  1. 根据上报位置和日程坐标计算距离。
  2. 判断是否进入地理围栏。

5. 数据库设计

5.1 表名:schedules

MVP 阶段只保留一张核心业务表。

用途:

  1. 存储日程本体。
  2. 存储日程状态。
  3. 存储地点与提醒关联信息。
  4. 存储地理围栏布防状态。

5.2 推荐字段

字段 类型 说明
id text 主键,日程 ID
user_id text 默认用户 ID
source_mode text manual / voice
schedule_type text time / location
status text scheduled / done / deleted
title text 标题
notes text 备注
start_time text 开始时间,ISO-8601,可以为空
end_time text 结束时间,可为空
timezone text 时区,可为空
location_name text 地点名称
location_address text 地点地址
latitude real 纬度
longitude real 经度
geofence_radius_meters integer 地理围栏半径,默认 100m
geofence_armed integer 地理围栏是否已布防,默认由服务端根据创建时用户位置计算
time_remind_offset_minutes integer 时间提前提醒,默认 15min
time_triggered_at text 时间提醒触发时间,可为空
geo_triggered_at text 地点提醒触发时间,可为空
created_at text 创建时间
updated_at text 更新时间

5.3 设计原则

  1. 核心字段尽量扁平化。
  2. 语音解析草稿只通过 WS 传递,不落库。
  3. 不保存系统侧兜底对象。
  4. 时间提醒和地理提醒可以同时存在。
  5. 只保留一张主表,MVP 不拆分额外业务表。
  6. status 只表达日程本体是否还有效,不表达监听中、已触发、已过期等过程状态。
  7. MVP 不保存用户自定义的重要程度,提醒方式由应用前后台状态、网络状态和时间/空间窗口决定。
  8. 冲突检测结果只在接口响应中返回,不写入 schedules 表。
  9. start_time 和地点信息都允许为空,但二者不能同时为空。
  10. geofence_armed 用于避免“用户在目标地点创建日程后立刻触发位置提醒”。
  11. 最近一次位置只在会话内或内存中计算,不落库。
  12. schedule_type 是前端表单必填项控制和后端校验的依据,不再使用全天字段区分业务类型。

6. 状态机

6.1 日程状态

建议状态流转:

scheduled -> done
scheduled -> deleted

状态说明:

状态 含义
scheduled 已创建,等待提醒或正在监听
done 用户已确认完成
deleted 用户删除

不进入 status 的过程信息:

信息 处理方式
草稿待确认 语音解析结果通过 WS 推给前端,用户确认前不创建正式日程
时间监听中 根据 start_timetime_remind_offset_minutes 和当前时间动态判断
地理监听中 根据经纬度、围栏半径、最近位置和 geofence_armed 动态判断
已触发提醒 写入 time_triggered_atgeo_triggered_at
已过期 根据 start_timeend_time 动态判断
创建冲突 只在接口响应中返回 conflicts,不持久化

7. 接口文档

7.0 响应约定

HTTP 接口统一使用 JSON 响应。

成功响应:

{
  "accepted": true
}

失败响应:

{
  "accepted": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "请求参数不合法",
    "details": {
      "field": "start_time"
    }
  }
}

字段说明:

字段 类型 说明
accepted boolean 请求是否被服务端接受
error.code string 程序可识别的错误码
error.message string 面向前端展示或日志记录的错误说明
error.details object/null 具体错误上下文,可为空

MVP 常用 HTTP 状态码:

状态码 含义
400 请求参数错误
404 资源不存在
409 幂等请求冲突
413 上传文件过大
415 文件格式不支持
500 服务端内部错误
502 第三方服务调用失败

WS 消息失败时,不依赖 HTTP 状态码,统一通过 typeok=false*.error 表达。

7.1 HTTP 音频上传

接口

POST /api/v1/audio/upload

作用

  1. 上传语音文件。
  2. 生成解析任务。
  3. 触发后续 ASR + LLM 流程。

请求方式

multipart/form-data

请求参数

字段 类型 必填 说明
device_id string 设备 ID
request_id string 本次上传请求 ID
audio_file file 原始音频
audio_format string wav / mp3 / m4a
duration_ms integer 时长

成功响应

{
  "accepted": true,
  "job_id": "job_audio_001",
  "request_id": "req_audio_001"
}

失败响应

文件格式不支持:

{
  "accepted": false,
  "error": {
    "code": "UNSUPPORTED_AUDIO_FORMAT",
    "message": "音频格式不支持",
    "details": {
      "audio_format": "aac",
      "supported_formats": ["wav", "mp3", "m4a"]
    }
  }
}

文件过大:

{
  "accepted": false,
  "error": {
    "code": "AUDIO_FILE_TOO_LARGE",
    "message": "音频文件过大",
    "details": {
      "max_size_mb": 20
    }
  }
}

任务创建失败:

{
  "accepted": false,
  "error": {
    "code": "AUDIO_JOB_CREATE_FAILED",
    "message": "语音解析任务创建失败",
    "details": null
  }
}

说明

  1. 接口只负责受理和入队。
  2. 真正的结构化结果通过 WS 返回。

7.2 HTTP 日程创建/更新

接口

POST /api/v1/schedules/upsert

作用

  1. 手动创建日程。
  2. 语音表单确认后提交日程。
  3. 写入最终状态。
  4. 执行时间冲突检测。

请求参数

字段 类型 必填 说明
request_id string 幂等 ID
schedule_id string 编辑时传
source_mode string manual / voice
schedule_type string time / location
title string 标题
notes string/null 备注
start_time string/null 开始时间;只有地点的日程可为空
end_time string/null 结束时间
timezone string/null 时区;有时间日程时建议填写
location_name string/null 地点名称
location_address string/null 地点地址
latitude number/null 纬度
longitude number/null 经度
geofence_radius_meters integer/null 地理围栏半径,默认 100
geofence_armed boolean/null 地理围栏是否已布防,不传则服务端计算
time_remind_offset_minutes integer/null 时间提醒提前量,默认 15

成功响应

{
  "accepted": true,
  "schedule_id": "schedule_001",
  "schedule_type": "time",
  "status": "scheduled",
  "conflicts": [],
  "geofence_armed": true
}

冲突响应

{
  "accepted": true,
  "schedule_id": "schedule_001",
  "schedule_type": "time",
  "status": "scheduled",
  "conflicts": [
    {
      "schedule_id": "schedule_older",
      "title": "已有日程",
      "start_time": "2026-07-28T15:00:00+08:00",
      "end_time": "2026-07-28T16:00:00+08:00"
    }
  ],
  "geofence_armed": true
}

失败响应

请求参数不合法:

{
  "accepted": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "请求参数不合法",
    "details": {
      "field": "schedule_type",
      "reason": "schedule_type 为 time 时 start_time 必填;为 location 时 latitude 和 longitude 必填"
    }
  }
}

时间范围不合法:

{
  "accepted": false,
  "error": {
    "code": "INVALID_TIME_RANGE",
    "message": "结束时间不能早于开始时间",
    "details": {
      "start_time": "2026-07-28T16:00:00+08:00",
      "end_time": "2026-07-28T15:00:00+08:00"
    }
  }
}

结束时间存在但开始时间为空:

{
  "accepted": false,
  "error": {
    "code": "INVALID_TIME_RANGE",
    "message": "只有结束时间但没有开始时间",
    "details": {
      "start_time": null,
      "end_time": "2026-07-28T15:00:00+08:00"
    }
  }
}

编辑的日程不存在:

{
  "accepted": false,
  "error": {
    "code": "SCHEDULE_NOT_FOUND",
    "message": "日程不存在",
    "details": {
      "schedule_id": "schedule_missing"
    }
  }
}

幂等请求冲突:

{
  "accepted": false,
  "error": {
    "code": "IDEMPOTENCY_CONFLICT",
    "message": "同一个 request_id 对应的请求内容不一致",
    "details": {
      "request_id": "req_schedule_001"
    }
  }
}

规则

  1. 手动创建和语音确认都走同一接口。
  2. 时间冲突只给提示,不默认阻断。
  3. schedule_type=time 时,前端表单要求填写时间,地点可选。
  4. schedule_type=location 时,前端表单要求填写地点,时间可选。
  5. 如果用户同时填写时间和地点,则 schedule_type 仍按 time 处理,不需要额外的中间枚举。
  6. 只有 start_time 存在时才做时间冲突检测。
  7. 只有经纬度存在时才做地理围栏监听。
  8. 如果 geofence_armed 不传,服务端根据最近一次位置上报与目标地点距离计算默认值。

7.3 HTTP 日程列表查询

接口

GET /api/v1/schedules

作用

  1. 获取当前用户的所有日程数据。
  2. 支持前端进入页面后初始化列表。
  3. 支持前端恢复本地状态和服务端状态对齐。

请求参数

字段 类型 必填 说明
user_id string 默认用户 ID;MVP 无账号体系时可不传
status string 可选:scheduled / done / deleted
include_deleted boolean 是否包含已删除数据,默认 false

成功响应

{
  "accepted": true,
  "schedules": [
    {
      "id": "schedule_001",
      "user_id": "default_user",
      "source_mode": "voice",
      "schedule_type": "time",
      "status": "scheduled",
      "title": "开会",
      "notes": null,
      "start_time": "2026-07-29T15:00:00+08:00",
      "end_time": null,
      "timezone": "Asia/Shanghai",
      "location_name": "陆家嘴",
      "location_address": null,
      "latitude": 31.2451,
      "longitude": 121.5067,
      "geofence_radius_meters": 100,
      "geofence_armed": true,
      "time_remind_offset_minutes": 15,
      "time_triggered_at": null,
      "geo_triggered_at": null,
      "created_at": "2026-07-28T12:00:00+08:00",
      "updated_at": "2026-07-28T12:00:00+08:00"
    }
  ]
}

失败响应

参数不合法:

{
  "accepted": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "请求参数不合法",
    "details": {
      "field": "status"
    }
  }
}

规则

  1. MVP 阶段默认查询 default_user 的数据。
  2. 默认只返回 scheduleddone
  3. include_deleted=true 时才返回 deleted 数据。
  4. 返回结果按 start_time asc nulls last, created_at desc 排序。

7.4 WebSocket 连接

地址

ws://<host>/ws?device_id=xxx

作用

  1. 保持设备在线。
  2. 回传语音结构化结果。
  3. 下发提醒控制消息。
  4. 下发系统日程删除指令。
  5. 下发系统闹钟删除指令。
  6. 接收位置信息和确认事件。

连接建立

客户端进入界面后先发:

{
  "type": "session.hello",
  "device_id": "android_abc123",
  "app_version": "1.0.0"
}

服务端返回:

{
  "type": "session.ready",
  "device_id": "android_abc123",
  "server_time": "2026-07-28T12:00:00Z"
}

连接失败或鉴权失败时,服务端返回错误消息后关闭连接:

{
  "type": "session.error",
  "ok": false,
  "error": {
    "code": "INVALID_DEVICE_ID",
    "message": "设备 ID 不合法",
    "details": null
  }
}

7.5 语音结构化结果推送

服务端消息

{
  "type": "voice.parse.result",
  "request_id": "req_audio_001",
  "job_id": "job_audio_001",
  "status": "ready_for_confirmation",
  "draft": {
    "schedule_type": "time",
    "title": "开会",
    "start_time": "2026-07-29T15:00:00+08:00",
    "end_time": null,
    "timezone": "Asia/Shanghai",
    "location_name": "陆家嘴",
    "geofence_radius_meters": 100,
    "time_remind_offset_minutes": 15
  },
  "missing_fields": ["location_address"],
  "ambiguous_fields": [],
  "needs_confirmation": true
}

失败消息

ASR 或 LLM 处理失败时,服务端通过 WS 推送失败结果:

{
  "type": "voice.parse.result",
  "request_id": "req_audio_001",
  "job_id": "job_audio_001",
  "status": "failed",
  "error": {
    "code": "VOICE_PARSE_FAILED",
    "message": "语音解析失败,请重新录音或手动创建",
    "details": {
      "stage": "asr"
    }
  }
}

前端处理

  1. 弹出统一表单。
  2. 默认填充结构化字段。
  3. 允许用户补全地点、时间和提醒参数。

7.6 位置信息上报

客户端消息

{
  "type": "location.report",
  "schedule_scope": "current",
  "latitude": 31.2451,
  "longitude": 121.5067,
  "accuracy": 18,
  "timestamp": "2026-07-28T12:01:00+08:00"
}

服务端响应

{
  "type": "location.report.ack",
  "ok": true
}

失败响应:

{
  "type": "location.report.ack",
  "ok": false,
  "error": {
    "code": "INVALID_LOCATION",
    "message": "位置信息不合法",
    "details": {
      "field": "latitude"
    }
  }
}

说明

  1. 服务端根据当前位置计算日程距离。
  2. 如果日程包含地点且命中围栏,先检查系统引用是否仍存在。
  3. 若日程还包含时间,再结合时间窗口决定是否下发提醒控制消息。

7.7 提醒控制消息

服务端只下发“该提醒了”,具体走哪种展示通道由客户端根据前后台状态自行决定。

服务端消息

{
  "type": "reminder.control",
  "schedule_id": "schedule_001",
  "reason": "time_window_reached",
  "action": "show"
}

客户端执行成功时回传:

{
  "type": "reminder.control.ack",
  "schedule_id": "schedule_001",
  "ok": true
}

客户端执行失败时回传:

{
  "type": "reminder.control.ack",
  "schedule_id": "schedule_001",
  "ok": false,
  "error": {
    "code": "REMINDER_DISPLAY_FAILED",
    "message": "提醒展示失败"
  }
}

7.8 系统引用检查指令

作用

在服务端准备下发软件提醒或删除系统兜底提醒之前,先确认用户是否已经在系统日程或系统闹钟中手动删除了对应事项。

如果用户已经删除系统侧提醒,说明用户可能已经表达“不再需要提醒”的意图,服务端不应继续下发应用内弹窗、系统级全局弹窗或删除指令。

服务端不能直接查询 Android 系统日程或系统闹钟,这个检查必须由客户端完成,再通过 WS 回传结果。

该指令作为独立保留的系统侧存在性检查流程,不因提醒策略调整而删除。

服务端消息

{
  "type": "system.refs.check",
  "schedule_id": "schedule_001",
  "system_schedule_ref_id": "system_schedule_001",
  "system_alarm_ref_id": "system_alarm_001",
  "reason": "before_reminder_control"
}

客户端处理

  1. 根据 system_schedule_ref_id 查询系统日程是否仍存在。
  2. 根据 system_alarm_ref_id 查询系统闹钟是否仍存在。
  3. 将查询结果通过 WS 回传服务端。
  4. 只做存在性检查,不重新创建系统日程或系统闹钟。

客户端响应

{
  "type": "system.refs.check.result",
  "schedule_id": "schedule_001",
  "ok": true,
  "system_schedule_ref_id": "system_schedule_001",
  "system_schedule_exists": false,
  "system_alarm_ref_id": "system_alarm_001",
  "system_alarm_exists": false,
  "checked_at": "2026-07-28T14:45:00+08:00"
}

查询失败时,客户端仍需回传失败原因:

{
  "type": "system.refs.check.result",
  "schedule_id": "schedule_001",
  "ok": false,
  "error": {
    "code": "SYSTEM_REF_CHECK_FAILED",
    "message": "系统引用检查失败",
    "details": {
      "reason": "permission_denied"
    }
  }
}

服务端处理

  1. 如果 system_schedule_exists=truesystem_alarm_exists=true,说明兜底提醒仍存在,继续执行软件提醒和重复提醒删除流程。
  2. 如果 system_schedule_exists=falsesystem_alarm_exists=false,说明用户可能已经手动删除系统侧提醒,服务端取消本次软件提醒。
  3. MVP 阶段不新增提醒关闭字段,服务端可将该日程状态更新为 deleted,并停止后续监听。
  4. 如果客户端超时未响应,服务端按当前在线状态继续提醒,避免漏提醒。

7.9 系统日程删除指令

服务端消息

{
  "type": "system.schedule.delete",
  "schedule_id": "schedule_001",
  "system_schedule_ref_id": "system_schedule_001",
  "reason": "ws_connected_and_time_near"
}

前端处理

  1. 删除系统日程。
  2. 取消重复通知。
  3. 继续保留业务日程本体。

客户端响应

{
  "type": "system.schedule.delete.ack",
  "schedule_id": "schedule_001",
  "ok": true,
  "system_schedule_ref_id": "system_schedule_001"
}

删除失败时:

{
  "type": "system.schedule.delete.ack",
  "schedule_id": "schedule_001",
  "ok": false,
  "system_schedule_ref_id": "system_schedule_001",
  "error": {
    "code": "SYSTEM_SCHEDULE_DELETE_FAILED",
    "message": "系统日程删除失败",
    "details": {
      "reason": "not_found"
    }
  }
}

7.10 系统闹钟删除指令

服务端消息

{
  "type": "system.alarm.delete",
  "schedule_id": "schedule_001",
  "system_alarm_ref_id": "system_alarm_001",
  "reason": "ws_connected_and_time_near"
}

前端处理

  1. 删除系统闹钟。
  2. 取消重复闹钟提醒。
  3. 继续保留业务日程本体。

客户端响应

{
  "type": "system.alarm.delete.ack",
  "schedule_id": "schedule_001",
  "ok": true,
  "system_alarm_ref_id": "system_alarm_001"
}

删除失败时:

{
  "type": "system.alarm.delete.ack",
  "schedule_id": "schedule_001",
  "ok": false,
  "system_alarm_ref_id": "system_alarm_001",
  "error": {
    "code": "SYSTEM_ALARM_DELETE_FAILED",
    "message": "系统闹钟删除失败",
    "details": {
      "reason": "not_found"
    }
  }
}

7.11 日程确认消息

作用:

  1. 由客户端主动告诉服务端,用户已经在应用内确认该日程完成。
  2. 关闭后续监听和所有提醒。
  3. system.refs.check 不同,这不是系统侧存在性检查,而是用户显式完成动作。

客户端消息

{
  "type": "schedule.confirmed",
  "schedule_id": "schedule_001",
  "confirmed": true,
  "timestamp": "2026-07-28T12:05:00+08:00"
}

服务端处理

  1. 取消监听。
  2. 终止后续提醒。
  3. 如系统日程或系统闹钟仍存在,按需删除。

服务端响应

{
  "type": "schedule.confirmed.ack",
  "schedule_id": "schedule_001",
  "ok": true
}

失败响应:

{
  "type": "schedule.confirmed.ack",
  "schedule_id": "schedule_001",
  "ok": false,
  "error": {
    "code": "SCHEDULE_CONFIRM_FAILED",
    "message": "日程确认失败",
    "details": {
      "reason": "schedule_not_found"
    }
  }
}

8. 监听与提醒策略

8.1 时间监听

规则:

  1. 只有 start_time 存在的日程才参与时间监听。
  2. 时间到达前进入监测窗口。
  3. 默认提前 15min
  4. 进入窗口后优先判断 WS 在线状态和前后台状态。
  5. 如果 WS 在线,服务端先下发 system.refs.check
  6. 如果客户端确认系统日程和系统闹钟都已不存在,服务端取消本次软件提醒并停止监听。
  7. 如果系统侧兜底提醒仍存在,服务端继续下发 reminder.control,并按需下发系统日程和系统闹钟删除指令。

8.2 空间监听

规则:

  1. 只有存在经纬度的日程才参与空间监听。
  2. 默认围栏半径 100m
  3. 客户端持续上传位置,服务端判定是否进入围栏。
  4. 创建日程时,如果用户当前位置已经在目标围栏内,服务端将 geofence_armed=false
  5. 当用户离开目标围栏后,服务端将 geofence_armed=true
  6. 只有 geofence_armed=true 且用户再次进入围栏时,才允许触发地点提醒。
  7. 如果创建时无法取得用户当前位置,服务端默认 geofence_armed=true,避免错过后续进入提醒。

8.3 触发规则

schedule_type 日程形态 触发规则
time 只有时间 时间窗口到达后触发
location 只有地点 geofence_armed=true 且用户进入地理围栏后触发

如果日程同时包含时间和地点,则 schedule_type 仍为 time,但提醒执行时同时参考时间窗口和地理围栏条件。

8.4 监听取消

满足以下任一条件时取消监听:

  1. 用户通过确认接口标记已完成。
  2. 有时间的日程时间已过。
  3. 日程被删除。
  4. 客户端确认系统日程和系统闹钟均已被用户手动删除。

9. 冲突检测

规则

  1. 只有 start_time 存在时才检查时间区间重叠。
  2. 如果已有日程落在同一时间段,返回冲突提示。
  3. 冲突结果包含已有日程的标题、时间和 ID。
  4. 只有地点、没有时间的日程不做时间冲突检测。

交互建议

  1. 提示用户“当前时段已有日程”。
  2. 用户可继续保存,也可修改时间。
  3. 冲突不是强制失败,但必须明确提示。

Clone this wiki locally