diff --git a/CALENDAR_DESIGN.md b/CALENDAR_DESIGN.md new file mode 100644 index 0000000..485db8a --- /dev/null +++ b/CALENDAR_DESIGN.md @@ -0,0 +1,274 @@ +# 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 唤醒的提示词模板