docs: add Calendar system design document

This commit is contained in:
nav
2026-03-22 23:40:26 +00:00
parent ca15ab2593
commit 1e7c772f68

274
CALENDAR_DESIGN.md Normal file
View File

@@ -0,0 +1,274 @@
# HarborForge Calendar System — Design Document
> Date: 2026-03-22
---
## Overview
为 HarborForge 新增日程表Calendar系统支持 Agent/人类用户的任务调度、周期性计划、以及通过 OpenClaw 插件心跳自动唤醒 Agent 执行日程。
同时包含一个 bug fixacc-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 当前需要执行的日程
- 筛选条件:当天 slotstatus 为 NotStarted 或 Deferredscheduled_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 <slot-type> <scheduled-at> <estimated-duration> \
[--job <code>] [--date <yyyy-mm-dd>]
# 查看某天日程
hf calendar show [--date <yyyy-mm-dd>]
# 取消日程plan 来源的 slot 物化后断开 plan
hf calendar cancel [--date <yyyy-mm-dd>] <slot-id>
# 编辑日程plan 来源的 slot 物化后断开 plan
hf calendar edit [--date <yyyy-mm-dd>] <slot-id> \
[--slot-type <type>] [--estimated-duration <mins>] \
[--job <code>] [--scheduled-at <HH:mm>]
# 列出所有有已物化日程的未来日期(纯 plan 的不算)
hf calendar date-list
```
### 7.2 计划操作
```bash
# 创建周期性计划
hf calendar plan-schedule <slot-type> <estimated-duration> \
--at <HH:mm> [--on-day <day>] [--on-week <1-4>] [--on-month <month>]
# 列出所有计划
hf calendar plan-list
# 取消计划(不追溯过去)
hf calendar plan-cancel <plan-id>
# 编辑计划(不追溯过去)
hf calendar plan-edit <plan-id> \
[--at <HH:mm>] [--on-day <day>] [--on-week <1-4>] [--on-month <month>] \
[--slot-type <type>] [--estimated-duration <mins>]
```
### 7.3 用户创建Agent 支持)
```bash
hf user create <username> [--agent-id <id>] [--claw-identifier <id>] ...
```
- `--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 唤醒的提示词模板