# HarborForge Calendar System — Design Document > Date: 2026-03-22 --- ## Overview 为 HarborForge 新增日程表(Calendar)系统,支持 Agent/人类用户的任务调度、周期性计划、以及通过 OpenClaw 插件心跳自动唤醒 Agent 执行日程。 同时包含一个 bug fix:acc-mgr 用户密码不可修改,前端隐藏修改密码入口。 --- ## 一、数据模型 ### 1.1 TimeSlot(日程槽) | 字段 | 类型 | 说明 | |------|------|------| | slot_id | int (PK) | 已物化 slot 的数据库 ID | | user_id | FK -> users.id | 所属用户 | | date | date | 日期 | | slot_type | Enum(Work, OnCall, Entertainment, System) | 槽类型 | | estimated_duration | int (1-50) | 预估时长(分钟),设计上限 50 分钟,超过需拆分 | | scheduled_at | time (00:00-23:00) | 计划开始时间 | | started_at | time, nullable | 实际开始时间 | | attended | bool, default false | 是否已出席 | | actual_duration | int (0-65535), nullable | 实际时长(分钟),无上限 | | event_type | Enum(Job, Entertainment, SystemEvent), nullable | 事件类型 | | event_data | JSON, nullable | 事件详情(见下方 Event 子类型) | | priority | int (0-99) | 优先级 | | status | Enum(NotStarted, Ongoing, Deferred, Skipped, Paused, Finished, Aborted) | 状态 | | plan_id | FK -> schedule_plans.id, nullable | 来源计划(物化自 plan 时填入,被 edit/cancel 后置 NULL) | | created_at | datetime | 创建时间 | | updated_at | datetime | 更新时间 | ### 1.2 Event 子类型(存储在 event_data JSON 中) **Job:** ```json { "type": "Task|Support|Meeting", "code": "TASK-42", "working_sessions": ["session-id-1", "session-id-2"] } ``` **SystemEvent:** ```json { "event": "ScheduleToday|SummaryToday" } ``` **Entertainment:** ``` 待设计 ``` ### 1.3 SchedulePlan(周期性计划) | 字段 | 类型 | 说明 | |------|------|------| | id | int (PK) | plan-id | | user_id | FK -> users.id | 所属用户 | | slot_type | Enum(Work, OnCall, Entertainment, System) | 槽类型 | | estimated_duration | int (1-50) | 预估时长 | | event_type | Enum(Job, Entertainment, SystemEvent), nullable | 事件类型 | | event_data | JSON, nullable | 事件详情 | | at_time | time | 每天的计划时间 (--at HH:mm) | | on_day | Enum(Sun, Mon, Tue, Wed, Thu, Fri, Sat), nullable | 星期几 (--on-day) | | on_week | int (1-4), nullable | 第几周 (--on-week) | | on_month | Enum(Jan-Dec), nullable | 月份 (--on-month) | | created_at | datetime | 创建时间 | | updated_at | datetime | 更新时间 | **周期参数层级约束:** - 使用 `--on-month` 则 `--on-week` 必须也用 - 使用 `--on-week` 则 `--on-day` 必须也用 - `--at` 始终必填 **示例:** - `--at 09:00 --on-day Sun --on-week 1 --on-month Jan` → 每年一月第一周周日 09:00 - `--at 09:00 --on-day Sun --on-week 1` → 每月第一周周日 09:00 - `--at 09:00 --on-day Sun` → 每周日 09:00 - `--at 09:00` → 每天 09:00 ### 1.4 Agent 表 | 字段 | 类型 | 说明 | |------|------|------| | id | int (PK) | | | user_id | FK -> users.id, UNIQUE | 关联用户 | | agent_id | VARCHAR, UNIQUE | OpenClaw agent name ($AGENT_ID) | | claw_identifier | VARCHAR | OpenClaw 实例 identifier(与 Monitor 碰巧一致,无 FK) | | status | Enum(Idle, OnCall, Busy, Exhausted, Offline), default Idle | Agent 当前状态 | | last_heartbeat | datetime, nullable | 最后心跳时间 | | created_at | datetime | 创建时间 | ### 1.5 MinimumWorkload(最小工作量配置) ```json { "daily": { "work": 0, "on_call": 0, "entertainment": 0 }, "weekly": { "work": 0, "on_call": 0, "entertainment": 0 }, "monthly": { "work": 0, "on_call": 0, "entertainment": 0 }, "yearly": { "work": 0, "on_call": 0, "entertainment": 0 } } ``` 值为分钟数 (0-65535)。存储方式待定(可作为用户级配置存 JSON 字段或独立表)。 --- ## 二、Slot ID 策略 - **已物化的 slot**:使用数据库自增 ID - **Plan 虚拟 slot**(未物化):使用 `plan-{plan_id}-{date}` 格式 - 当虚拟 slot 被 `edit` 或 `cancel` 时,物化到数据库,获得真实 ID,同时该日期该 plan 不再生成虚拟 slot --- ## 三、存储与缓存策略 ### 3.1 Plan 不预展开 Plan 只存规则,不为每一天生成数据行。 ### 3.2 物化时机 以下情况写入 time_slots 表: 1. `hf calendar schedule` 手动创建 2. Plan 的虚拟 slot 被 `edit` 或 `cancel`(物化后断开 plan 关联) 3. 每天预计算:服务器每日将当天所有 plan 匹配的 slot 物化到缓存/数据库 ### 3.3 当日缓存 - 每天(凌晨或首次心跳时)预计算当天所有 plan → 物化为当日 slot 缓存 - 当天新增 `schedule` / `plan-schedule` 影响当天时,同步更新缓存 ### 3.4 不可变性 - `cancel` / `edit` 不能操作过去的 slot - `plan-cancel` / `plan-edit` 不追溯过去已物化的 slot --- ## 四、时区 统一使用 HarborForge 服务器时区,不做用户级时区。 --- ## 五、验证规则 `schedule` / `plan-schedule` 提交时验证: 1. **Overlap 检测**:与同日已有 slot 时间重叠 → **拒绝,报错** 2. **最小工作量检查**:不满足 MinimumWorkload 配置 → **警告,但允许提交** --- ## 六、Agent 唤醒机制 ### 6.1 心跳流程 1. 插件每分钟向 HarborForge 服务器发送心跳 2. 服务器返回该插件(claw_identifier)对应所有 Agent 当前需要执行的日程 - 筛选条件:当天 slot,status 为 NotStarted 或 Deferred,scheduled_at 已过 3. 插件检查 Agent 状态 ### 6.2 唤醒逻辑 **Agent 状态为 Idle:** - 唤醒 Agent,提供提示词开始工作 - 向服务器设置 Agent status = Busy 或 OnCall(取决于 slot_type) - 设置 slot: attended = true, started_at = now, status = Ongoing **Agent 状态非 Idle:** - 设置 slot status = Deferred ### 6.3 多 Slot 竞争 - 选 priority 最高的执行,其余 Deferred 且 priority += 1 - 通知 Agent 当前任务完成后重新规划所有 Deferred + NotStarted 的 slot ### 6.4 状态转移 | 触发条件 | Agent Status 变化 | |---------|-----------------| | 超过 2 分钟无心跳 | → Offline | | 无待执行日程 | → Idle | | 被 TimeSlot 唤醒 | → Busy / OnCall | | 完成 TimeSlot | → Idle | --- ## 七、CLI 命令 ### 7.1 日程操作 ```bash # 创建单次日程 hf calendar schedule \ [--job ] [--date ] # 查看某天日程 hf calendar show [--date ] # 取消日程(plan 来源的 slot 物化后断开 plan) hf calendar cancel [--date ] # 编辑日程(plan 来源的 slot 物化后断开 plan) hf calendar edit [--date ] \ [--slot-type ] [--estimated-duration ] \ [--job ] [--scheduled-at ] # 列出所有有已物化日程的未来日期(纯 plan 的不算) hf calendar date-list ``` ### 7.2 计划操作 ```bash # 创建周期性计划 hf calendar plan-schedule \ --at [--on-day ] [--on-week <1-4>] [--on-month ] # 列出所有计划 hf calendar plan-list # 取消计划(不追溯过去) hf calendar plan-cancel # 编辑计划(不追溯过去) hf calendar plan-edit \ [--at ] [--on-day ] [--on-week <1-4>] [--on-month ] \ [--slot-type ] [--estimated-duration ] ``` ### 7.3 用户创建(Agent 支持) ```bash hf user create [--agent-id ] [--claw-identifier ] ... ``` - `--agent-id` + `--claw-identifier` 必须同时出现或同时不出现 - pcexec 模式下: - `--agent-id` ← `$AGENT_ID` - `--claw-identifier` ← `openclaw config get plugins.harbor-forge.identifier` - 后端 `POST /users` 扩展:接受 `agent_id` + `claw_identifier`,创建 User 同时写入 agents 表 --- ## 八、前端 - 每个用户可查看/调整自己的日程表 - Admin 可查看/调整所有用户的日程表 - 日程表页面展示(具体 UI 待设计) --- ## 九、Bug Fix - acc-mgr 用户:后端禁止修改密码(admin 也不行) - 前端:acc-mgr 用户不显示修改密码入口 --- ## 十、待定项 - Entertainment 事件子类型设计 - MinimumWorkload 存储方式(JSON 字段 vs 独立表) - 前端日程表 UI 详细设计 - Agent 唤醒的提示词模板