# Notify 产品与技术规格

## 目标与定位
- **目标**：打造简洁明了、同时适配桌面与移动浏览器的提醒应用。
- **核心能力**：Todo 与多用户提醒、周期性规则、提前提醒、网页通知 + Bark 推送。

## 需求澄清后的产品规格
### 核心对象
- **Todo**：个人待办，支持单次与周期性，配置一个或多个提醒策略。
- **提醒任务**：可指定多个接收者（含自己），支持单次与周期性，提醒策略与触达方式同 Todo。
- **通知**：一次提醒事件在“某接收者”维度的投递记录，支持站内与 Bark 两种渠道。
- **邀请码**：仅邀请码注册，所有用户可生成/管理邀请码。

### 业务规则与边界条件
1. **周期性任务的下一次触发时间**
   - 使用 **用户时区**（默认 `Asia/Shanghai` 可配置）。
   - 规则类型：`hourly | daily | weekly | monthly | yearly`
   - 计算方式：以“本次 dueAt”为基准，按规则添加时间并做 **日历对齐**：
     - `hourly`: `dueAt + n hours`
     - `daily`: `dueAt + n days`（保持时分）
     - `weekly`: `dueAt + n weeks`（保持星期与时分）
     - `monthly`: 若下月无对应日期，则取 **该月最后一天** 同时分
     - `yearly`: 若跨闰年导致日期不存在，取 **当年同月最后一天** 同时分
2. **多提醒策略**
   - 每个任务可配置多条提前量（如 10 分钟、1 小时）。
   - 对每条提前量生成独立触发点：`triggerAt = dueAt - offsetMinutes`。
3. **浏览器不在线**
   - 服务端仍生成通知记录（状态 `pending/queued/sent`）。
   - 用户上线后，客户端拉取未读站内通知并展示（通知中心 + 弹窗）。
4. **Bark 推送失败/重试**
   - 失败进入重试队列（指数退避：1m/5m/15m/1h）。
   - 最多重试 5 次，超出后标记 `failed`。
   - 幂等：同一通知记录仅允许一次成功发送。
5. **多用户提醒**
   - 每个接收者创建独立通知记录（便于去重与投递状态追踪）。
6. **邀请码策略（默认方案）**
   - 每个邀请码 **可用次数 = 5**。
   - **有效期 = 7 天**，可撤销。
   - 邀请码可由任意已注册用户生成与管理。

## 数据模型（关系型）
### 核心表
- `users`: 用户
- `invites`: 邀请码
- `todos`: 个人待办
- `reminder_tasks`: 多用户提醒任务
- `recurrence_rules`: 周期规则
- `reminder_offsets`: 提前提醒策略
- `notifications`: 通知实例（按接收者维度）
- `delivery_logs`: 投递日志（站内/Bark）

### 字段示意（详见 schema）
- `users(id, username, password_hash, timezone, bark_url, created_at)`
- `invites(id, code, creator_id, max_uses, used_count, expires_at, revoked_at)`
- `todos(id, owner_id, title, description, due_at, recurrence_rule_id)`
- `reminder_tasks(id, creator_id, title, due_at, recurrence_rule_id)`
- `reminder_task_recipients(task_id, user_id)`
- `recurrence_rules(id, type, interval, by_weekday, by_monthday, timezone)`
- `reminder_offsets(id, target_type, target_id, offset_minutes, channel_inapp, channel_bark)`
- `notifications(id, recipient_id, target_type, target_id, trigger_at, channel, status)`
- `delivery_logs(id, notification_id, attempt_no, channel, status, response_meta)`

## 核心接口设计（REST）
### 认证与邀请码
- `POST /api/auth/register` { username, password, inviteCode }
- `POST /api/auth/login` { username, password } -> JWT
- `POST /api/invites` 创建邀请码
- `GET /api/invites` 获取邀请码列表
- `POST /api/invites/:id/revoke`

### Todo
- `GET /api/todos`
- `POST /api/todos`
- `GET /api/todos/:id`
- `PUT /api/todos/:id`
- `DELETE /api/todos/:id`

### 多用户提醒任务
- `GET /api/reminder-tasks`
- `POST /api/reminder-tasks`
- `GET /api/reminder-tasks/:id`
- `PUT /api/reminder-tasks/:id`
- `DELETE /api/reminder-tasks/:id`

### 用户与设置
- `GET /api/users?query=`
- `GET /api/me`
- `PUT /api/me/settings` { timezone, barkUrl, inappEnabled, barkEnabled }

### 通知中心
- `GET /api/notifications?status=unread`
- `POST /api/notifications/:id/read`

## Bark 推送设计
### 调用形式
- 采用 Bark 官方接口：
  - GET: `https://bark.server/push/{title}/{body}?group=notify&icon=...`
  - POST: JSON body `{ title, body, group, icon, url, badge, sound }`
### 发送内容
- `title`: 任务标题
- `body`: 触发时间 + 备注
- `group`: `notify`
- 额外参数：`url` 指向站内通知详情

## 调度方案（可靠与幂等）
### 核心思想
- 以 **通知表** 为唯一投递来源（幂等）。
- 调度器只负责生成通知实例；投递 worker 只发送 `pending` 通知。

### 通知状态机
- `pending` -> `queued` -> `sent` 或 `failed`

### 触发流程
1. 用户创建/更新任务 -> 生成 `next_due_at`
2. 生成通知实例：`trigger_at = due_at - offset`
3. Worker 扫描 `trigger_at <= now` 且 `status = pending`，锁定并投递
4. 成功则更新 `sent`，失败则记录 `failed` 并按策略重试

## 关键页面与交互
1. **登录/注册（邀请码）**
   - 注册页要求邀请码与密码确认
2. **Todo 列表**
   - 列表 + 新增/编辑弹窗
   - 支持单次/周期选择、提前提醒策略
3. **提醒任务**
   - 支持多接收者选择、搜索用户
4. **用户列表**
   - 全部用户可见，支持搜索
5. **个人设置**
   - Bark 链接、站内通知偏好
6. **通知中心**
   - 未读/历史提醒，点击标记已读

## 工程结构与实现要点
- `backend/`: REST API、调度 worker、Bark 集成
- `frontend/`: Next.js UI，响应式布局
- `docker-compose.yml`: 数据库与服务

## 示例伪代码
### 计算下一次触发
```
function nextDueAt(dueAt, rule) {
  switch (rule.type) {
    case "monthly":
      return addMonthWithClamp(dueAt, rule.interval);
    case "yearly":
      return addYearWithClamp(dueAt, rule.interval);
    // hourly/daily/weekly...
  }
}
```
