Files
iboard/docs/V0.2/需求文档.md
T
cheney 5f41cb05b7
Docker Build and Push / build-image (push) Successful in 10m16s
chore(V0.2): 统一 APK 与 OTA 版本规则 —— 同一版本号, APK 裸写 / OTA 加 -ota
- readme: 版本管理章节重写, 明确 APK=0.2.0 无后缀, OTA=0.2.0-ota 多一个标记
  并补充 APK versionName/versionCode 的生成链路 (tauri.properties, major*1e6+minor*1e3+patch)
- scripts/ci/check-version.mjs: 新增四处一致性校验
  (package.json / tauri.conf.json / Cargo.toml / Cargo.lock), 并禁止 version 带 -ota
- android-build.yml: 构建前也执行版本校验 (tauri.conf.json 的 version 就是 APK versionName)
- 需求文档 F-OT-01a、设计文档 §10.2 同步该规则
2026-10-22 17:34:22 +08:00

349 lines
24 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# iBoard V0.2 需求文档 — 打卡 / 日历 / 清单
> 版本:V0.2 · 分支:`dev-0.2` · 日期:2026-10-22
> 目标项目:iboard(Tauri 2 + React 18 + antd-mobile + Express + Prisma/MariaDB,移动端优先)
> 基线版本:V0.1(`dev-0.1`)
---
## 1. 总体描述
在 iBoard 移动端新增一个「打卡」模块一个「清单」模块,满足两类日常需求:
### 打卡
1. **周期性打卡**(习惯养成):用户定义"每天 n 次 / 每周 n 次 / 每月 n 次 / 每年 n 次"的目标,
每次打卡后卡片上的**环形进度**按 `已完成次数 / 目标次数` 增长,达标时环形闭合并播放完成特效;
2. **日历回看**:以日历为载体,快速查看**今天**(要做什么)、**昨天**(做得怎样)、
**明天**(计划做什么),日期上以圆点/实心标记完成情况;
### 清单
1. **清单**(场景化一次性事项):面向**出差、回老家**等临时场景,逐条勾选确认,
避免遗漏;清单可复用为模板,下次一键生成。
### 1.1 本期范围
- 单用户使用,数据按登录用户隔离(复用现有 JWT 鉴权)。
- 移动端优先(`MobileShell` + 底部 TabBar 体系),桌面浏览器兼容可用即可。
- 数据落库 MariaDB,通过现有 Express `/api/*` 提供接口。
### 1.2 不在本期范围(明确排除)
- 多人协作 / 共享打卡 / 打卡排行榜。
- 系统通知、推送提醒、定时提醒(V0.2 不引入通知能力)。
- 复杂统计报表(周报/月报/趋势图),本期只提供完成率概览。
- 桌面端专用布局、横屏适配。
- 清单的周期性重复(如"每次出差都生成"),本期只做**临时清单 + 模板手动复用**。
---
## 2. 术语与概念定义
| 术语 | 定义 |
| --- | --- |
| **打卡项(Habit)** | 一个被跟踪的周期性目标,如"喝水 每天 8 次""健身 每周 3 次" |
| **周期类型** | `day` / `week` / `month` / `year`,决定进度统计窗口 |
| **目标次数 n** | 一个周期内需要完成的打卡次数,n ≥ 1 |
| **周期窗口** | 日=自然日;周=自然周(周一 00:00 至周日 23:59);月=自然月;年=自然年。时区统一 Asia/Shanghai |
| **打卡记录(CheckIn)** | 一次打卡动作,归属到某个**本地日历日**(可补打到过去日期) |
| **环形进度** | 卡片上的圆环组件,`进度 = min(已完成次数 / n, 1)`;达标=闭合 |
| **清单(CheckList)** | 一次性检查清单,如"出差 · 10月杭州",含若干检查项 |
| **检查项(CheckItem)** | 清单中的单条待检查事项,勾选即完成 |
| **模板(Template)** | 可被复用的清单结构(一组检查项标题),用于一键生成新清单 |
| **日期状态** | 某日的打卡完成情况:全部完成 / 部分完成 / 未开始 / 超额完成 |
---
## 3. 需求点
**编号规则**:`类型-模块-序号`。类型:F=功能需求、U=交互与体验、D=数据、N=非功能、E=异常与边界。
模块:CK=打卡、CL=日历、LS=清单、SY=通用。
**优先级**:P0 = 本期必须有;P1 = 本期应该有;P2 = 有则更好(可延后)。
### 3.1 功能需求 — 打卡(F-CK)
| 编号 | 需求描述 | 验收要点 | 优先级 |
| --- | --- | --- | --- |
| F-CK-01 | 支持新建打卡项:名称、周期类型、目标次数 n、颜色、图标、备注 | 表单校验:名称必填且 ≤ 20 字;n 为 ≥1 的整数 | P0 |
| F-CK-02 | 周期类型支持**每天 n 次 / 每周 n 次 / 每月 n 次 / 每年 n 次**四种 | 四种类型均可创建并正确统计 | P0 |
| F-CK-03 | 卡片以**环形进度**展示当前周期完成情况,环中心显示 `已完成/n` | 进度 = 已完成次数 / n,上限 100% | P0 |
| F-CK-04 | **每天 1 次**的打卡项:打卡后环形**直接闭合**(100%),卡片进入"已完成"态 | 一次点击即闭合,不出现 50% 之类的中间态 | P0 |
| F-CK-05 | **每周 n 次**(及月/年):每打卡一次环形闭合对应百分比(如 n=3,打 1 次=33.3%、2 次=66.7%、3 次=100% 闭合) | 进度随每次打卡实时刷新,带过渡动画 | P0 |
| F-CK-06 | 打卡主操作:点击卡片上的打卡按钮即完成一次打卡 | 单击即生效,响应 < 300ms(乐观更新) | P0 |
| F-CK-07 | 打卡**完成特效**:达标(进度达 100%)时播放特效——环形闭合动画 + 光晕/粒子扩散 + 轻量触感反馈 | 特效时长 ≤ 800ms,不阻塞后续操作 | P0 |
| F-CK-08 | 打卡记录需记录**发生日期**(本地日历日)与时间戳 | 支持按日回看,跨零点归属正确 | P0 |
| F-CK-09 | 支持**撤销**:可撤销本周期内最近一次打卡(主要是误触场景) | 撤销后进度回退,记录物理删除或标记撤销 | P1 |
| F-CK-10 | 支持**补打卡**:对过去日期补记一次打卡,默认允许回溯 7 天 | 补打后对应日期的状态与进度同步更新 | P1 |
| F-CK-11 | **不允许提前打卡**(未来日期不可打卡) | 明天的项只展示为"计划",按钮置灰 | P0 |
| F-CK-12 | 展示**连续完成天数/周期数(streak)** | 如"已连续 5 天",断签归零 | P1 |
| F-CK-13 | 打卡项可**编辑**(改名/改 n/改颜色/改周期)与**停用(归档)** | 归档后不出现在今日待办,历史保留 | P1 |
| F-CK-14 | 修改目标次数 n 后,**当前周期**进度按新 n 重算展示,不改写历史记录 | 例如 n 由 3 改 5,已完成 3 次 → 显示 3/5(60%) | P1 |
| F-CK-15 | **超额打卡**:周期内超过 n 仍可继续打卡,环形保持闭合,中心显示 `n+k`(如 3+1) | 不清零、不报错 | P2 |
| F-CK-16 | 打卡项支持**排序**(拖动或置顶)与分组展示 | 今日列表按"未完成在前、已完成在后"默认排序 | P2 |
| F-CK-17 | 打卡时可附带**备注**(可选,纯文本) | 列表详情中可查看 | P2 |
### 3.2 功能需求 — 日历(F-CL)
| 编号 | 需求描述 | 验收要点 | 优先级 |
| --- | --- | --- | --- |
| F-CL-01 | 提供**月历视图**:当月日历网格,可切换上/下月,点击日期查看当日详情 | 周一起始,周一~周日七列 | P0 |
| F-CL-02 | **今天**视图(默认):列出今天应打卡的项及完成状态,可直接打卡 | 打开页面默认定位今天 | P0 |
| F-CL-03 | **昨天**视图:展示昨天各打卡项的**已完成结果**(完成/未完成/超额),未完成项可进行补打 | 以回看为主,标记为"已完成的历史" | P0 |
| F-CL-04 | **明天**视图:展示**明天计划要打卡的项**(按周期规则推算),只读,不可打卡 | 明确标注"计划",打卡按钮不可用 | P0 |
| F-CL-05 | 今天/昨天/明天**快捷切换**(顶部 Segmented 或左右滑动) | 一键切换,切换有过渡动画 | P0 |
| F-CL-06 | **日期标记**:月历中每个日期按完成情况标记——全部完成(实心)、部分完成(半填充)、未开始(空心/无标记)、超额完成(特殊标记) | 一眼能看出当月坚持情况 | P0 |
| F-CL-07 | **日期详情**:点击某日期,展示当天每个打卡项的"目标次数 / 已完成次数 / 状态" | 过去日期可补打,未来日期只读 | P0 |
| F-CL-08 | 日历与打卡**数据同源**:打卡操作在日历视图与打卡列表视图中即时一致 | 任一处打卡,另一处刷新 | P0 |
| F-CL-09 | 月历顶部展示**当月完成率**(完成次数 / 应完成次数) | 百分比展示 | P1 |
| F-CL-10 | 支持左右**滑动手势**切换月份 | 与按钮切换等价 | P2 |
| F-CL-11 | 月视图支持简单的**完成热力**(按完成率深浅着色) | 可选展示,默认关闭 | P2 |
### 3.3 功能需求 — 清单(F-LS)
| 编号 | 需求描述 | 验收要点 | 优先级 |
| --- | --- | --- | --- |
| F-LS-01 | 支持新建**检查清单**:名称、场景标签(出差 / 回老家 / 自定义)、备注 | 名称必填,场景标签可空 | P0 |
| F-LS-02 | 清单内可**增删改检查项**,支持排序 | 单条 ≤ 40 字 | P0 |
| F-LS-03 | **勾选完成**:点击检查项前的复选框即打勾,已完成项置灰 + 删除线;可取消勾选 | 单击切换,状态即时保存 | P0 |
| F-LS-04 | 清单头部显示**进度**:`已完成 / 总数` + 进度条;全部完成时显示完成态 | 全部勾完触发完成提示 | P0 |
| F-LS-05 | 清单**全部完成**时播放完成特效(与 F-CK-07 同一套特效组件) | 复用,风格统一 | P1 |
| F-LS-06 | **模板**:系统预置常用模板(出差、回老家),一键基于模板生成新清单 | 至少 2 个预置模板 | P1 |
| F-LS-07 | **另存为模板**:用户可将自己的清单保存为自定义模板,后续复用 | 自定义模板可删除 | P1 |
| F-LS-08 | **复用历史清单**:从历史清单复制生成一份新清单(勾选状态重置) | 原清单保持不变 | P1 |
| F-LS-09 | 清单**归档/删除**:完成的清单可归档(不出现在进行中列表),可查看历史 | 删除需二次确认 | P1 |
| F-LS-10 | 清单项支持**备注/数量**说明(如"充电器 ×1") | 展示在标题下方,弱色小字 | P2 |
| F-LS-11 | 清单支持**一键全部勾选 / 全部取消** | 批量操作入口 | P2 |
### 3.4 功能需求 — 通用(F-SY)
| 编号 | 需求描述 | 验收要点 | 优先级 |
| --- | --- | --- | --- |
| F-SY-01 | 底部 TabBar 新增**「打卡」页**,并作为**默认首页放在最左**;「清单」不单独占 Tab,作为打卡页内的第三个子视图 | 图标风格与现有 TabIcon 一致;启动即进入 `/checkin` | P0 |
| F-SY-02 | 「打卡」页内集成 打卡/日历 两个功能,日历放在上半截,默认显示一行为周视图,下拉显示为月视图。 | 日历中选择时间与下部打卡任务联动 | P0 |
| F-SY-03 | 所有接口复用现有鉴权(`authRequired`,JWT),数据按 `userId` 隔离 | 未登录跳转 `/login` | P0 |
| F-SY-04 | 后端新增 REST 路由(建议 `/api/checkin/*`、`/api/checklist/*`),挂载到 `server/index.js` | 与现有路由风格一致 | P0 |
| F-SY-05 | 时间处理统一 **Asia/Shanghai**,前端与后端对"日历日"的判定一致 | 跨零点打卡归属当天 | P0 |
| F-SY-06 | 打卡/勾选操作采用**乐观更新**,接口失败时回滚并 Toast 提示 | 失败可见、状态不失真 | P1 |
| F-SY-07 | 写接口**幂等/防抖**:同一打卡项 1 秒内重复点击只记一次 | 前端防抖 + 后端唯一约束双保险 | P1 |
| F-SY-08 | 页面可见性变化时刷新数据(复用 `useRefreshOnVisible`) | 从后台切回自动同步 | P1 |
### 3.5 交互与体验需求(U)
| 编号 | 需求描述 | 验收要点 | 优先级 |
| --- | --- | --- | --- |
| U-01 | **环形进度组件**:SVG 圆环,背景环 + 进度环,进度变化带 400ms ease-out 动画 | 组件可复用(打卡卡片、清单进度共用) | P0 |
| U-02 | **完成特效**:达标瞬间环形闭合 + 光晕/粒子扩散,约 600ms;可重复观看不烦扰 | 不阻塞操作,动画结束自动清理 | P0 |
| U-03 | **触感反馈**:完成打卡时轻量震动(Web Vibration API / Tauri) | 固定开启,与特效一同触发 | P1 |
| U-04 | 移动端适配:375~430px 视口,复用 `globals.css` 的 `.card` 体系,安全区与底部 TabBar 不遮挡内容 | 真机无横向滚动、无遮挡 | P0 |
| U-05 | 空态 / 加载态 / 错误态三态齐全(首次使用引导"创建第一个打卡项") | 空态有明确 CTA | P0 |
| U-06 | 打卡按钮足够大(点击热区 ≥ 44×44),符合移动端触控规范 | 误触率低 | P1 |
| U-07 | 动效**性能降级**:特效保持轻量(单次 ≤900ms、`pointer-events:none`),不给低端机造成卡顿 | 不卡顿、不阻塞操作 | P1 |
| U-08 | 深浅色:跟随现有浅色主题,卡片/分割线风格与首页、监控页一致 | 视觉统一 | P1 |
| U-09 | 清单勾选有明确的勾选动画(勾形描边动画) | 反馈即时 | P2 |
### 3.6 数据需求(D)
| 编号 | 需求描述 | 验收要点 | 优先级 |
| --- | --- | --- | --- |
| D-01 | 新增 Prisma 模型:`Habit`(打卡项)、`CheckIn`(打卡记录) | 字段见附录 A | P0 |
| D-02 | 新增 Prisma 模型:`CheckList`(清单)、`CheckListItem`(检查项)、`CheckListTemplate`(模板,P1) | 字段见附录 A | P0 |
| D-03 | `Habit` / `CheckList` 均带 `userId` 外键,删除用户级联删除 | 数据隔离 | P0 |
| D-04 | `CheckIn` 建立 `(habitId, date)` 索引,支持按日期范围查询 | 日历月查询走索引 | P0 |
| D-05 | 提供迁移 SQL 并纳入 `prisma migrate deploy` 流程 | 与 V0.1 迁移流程一致 | P0 |
| D-06 | 软删除语义:`Habit.archived`、`CheckList.archived`,历史记录保留 | 不物理删除历史 | P1 |
### 3.7 非功能需求(N)
| 编号 | 需求描述 | 验收要点 | 优先级 |
| --- | --- | --- | --- |
| N-01 | 页面首屏数据加载 < 1.5s(内网/正常网络),交互响应 < 300ms(乐观更新) | 真机可感知流畅 | P0 |
| N-02 | 兼容 Android WebView(Tauri 打包)与移动端浏览器,不依赖桌面专有 API | 真机验证 | P0 |
| N-03 | 单用户数据量按"千级记录/年"设计,无需分表;月查询一次请求返回 | 日历切月无等待感 | P1 |
| N-04 | 所有接口 `authRequired`,越权访问返回 401 | 安全基线 | P0 |
| N-05 | 前端代码组织遵循现有约定:`src/pages/checkin/*` + `src/components/*` + `server/routes/*` | 结构一致 | P0 |
### 3.8 异常与边界需求(E)
| 编号 | 需求描述 | 处理约定 | 优先级 |
| --- | --- | --- | --- |
| E-01 | 重复点击打卡 | 前端防抖 + 后端唯一约束,只记一次 | P0 |
| E-02 | 接口失败 / 断网 | 乐观更新回滚 + Toast 提示,可重试 | P0 |
| E-03 | **周期中途新建**打卡项(如周三创建"每周 3 次") | 本期按**完整目标 n** 计算,不按剩余天数折算 | P1 |
| E-04 | 每月 n 次在天数不同的月份 | 按**自然月**窗口,不折算 | P0 |
| E-05 | 每年 n 次跨年 | 按**自然年**窗口,年末清零 | P0 |
| E-06 | 删除打卡项 | 归档(软删除)为主;硬删除需二次确认并提示"历史记录将一并删除" | P1 |
| E-07 | 清单项全部删除后清单进度为 0/0 | 显示空清单,不显示 100% | P1 |
| E-08 | 跨零点打卡(23:59 → 00:00) | 以**提交时刻的本地日历日**归属 | P0 |
### 3.9 功能需求 — 版本与升级(F-OT,2026-10-22 追加)
| 编号 | 需求描述 | 验收要点 | 优先级 |
| --- | --- | --- | --- |
| F-OT-01 | **OTA 包版本必须与分支对应**:`dev-0.2` 分支上 OTA 包版本应为 `0.2.0-ota` | `GET /api/ota/check/0` 返回 `version = 0.2.0-ota` | P0 |
| F-OT-01a | **APK 版本与 OTA 版本同源**:APK(含桌面包)版本为裸版本号 `0.2.0`,无后缀;OTA 版本为同一版本号加 `-ota`。两者都跟代码分支保持一致 | APK 系统信息里 versionName=`0.2.0`(versionCode=2000),OTA manifest=`0.2.0-ota` | P0 |
| F-OT-02 | 版本号错位在 CI 阶段就拦住:①`package.json`/`tauri.conf.json`/`Cargo.toml`/`Cargo.lock` 四处必须完全一致且不含 `-ota`;②分支名 `dev-x.y` 与版本主次号不一致时构建失败 | CI 输出明确报错并中断 | P0 |
| F-OT-03 | 「我」页 OTA 组内新增**「开发版本」开关**,默认**关闭** | 关闭=按版本号比较;打开=按 build sequence 比较 | P0 |
| F-OT-04 | 开关值**持久化在设备端**,重启 App 后保持上次选择 | 杀进程重进仍为上次状态 | P0 |
| F-OT-05 | 版本号比较规则:按非数字字符切成数字字段,逐段比较大小(`0.2.0 > 0.1.0`,`0.10 > 0.9`) | 单测覆盖后缀/前缀/缺位/多位段 | P0 |
| F-OT-06 | 切换开关后清空上一次的检查结果,避免误判 | 切换即回到「未检查」状态 | P1 |
---
## 4. 优先级汇总(本期交付范围)
- **P0(必须)**:F-CK-01~08、F-CK-11;F-CL-01~08;F-LS-01~04;F-SY-01~05;F-OT-01、F-OT-01a、F-OT-02~05;U-01、U-02、U-04、U-05;D-01~05;N-01、N-02、N-04、N-05;E-01、E-02、E-04、E-05、E-08。
- **P1(应该)**:F-CK-09、10、12~14;F-CL-09;F-LS-05~09;F-SY-06~08;F-OT-06;U-03、U-06~08;D-06;N-03;E-03、E-06、E-07。
- **P2(可选/延后)**:F-CK-15~17;F-CL-10、11;F-LS-10、11;U-09。
---
## 5. 里程碑建议
| 里程碑 | 内容 |
| --- | --- |
| **M1 数据与接口** | Prisma 模型 + 迁移;`server/routes/checkin.js`、`checklist.js`;周期进度计算与日期查询 |
| **M2 打卡视图** | 环形进度组件 + 打卡卡片列表 + 打卡/撤销/补打 + 完成特效 |
| **M3 日历视图** | 月历网格 + 今天/昨天/明天三视图 + 日期标记 + 日期详情 |
| **M4 清单视图** | 清单 CRUD + 勾选交互 + 进度条 + 模板与复用 |
| **M5 联调打磨** | 真机验证、动效降级、空态/错误态、性能与跨天边界 |
---
## 6. 待确认问题(评审时决策)
| # | 问题 | 建议 |
| --- | --- | --- |
| 1 | 是否新增第 5 个底部 Tab「打卡」,还是放在首页入口? | ✅ **已决策(2026-10-22)**:「打卡」作为**默认首页放在最左**;原来的「首页」取消,报警/新闻并入宏观 |
| 2 | 周起始日是周一还是周日? | 建议周一(国内习惯) |
| 3 | 是否允许"明天提前打卡"? | 建议**不允许**(F-CK-11),保持统计口径干净 |
| 4 | 删除打卡项时历史是否保留? | 建议默认归档保留,硬删除才清历史 |
| 5 | 预置模板具体包含哪些项? | 评审时列出"出差/回老家"各 8~12 条初始项 |
| 6 | 完成特效是否可全局关闭? | ✅ **已决策(2026-10-22)**:不提供关闭入口,**特效永远打开**;页面内不再有特效开关 |
---
## 附录 A:核心实体草案(供设计阶段参考,非最终表结构)
```prisma
// 打卡项
model Habit {
id Int @id @default(autoincrement())
userId Int
name String
period String // day | week | month | year
target Int @default(1) // 周期内目标次数 n
color String @default("#1677ff")
icon String @default("")
note String @default("")
archived Boolean @default(false)
sortOrder Int @default(0)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
records CheckIn[]
@@index([userId, archived])
}
// 打卡记录(一次打卡 = 一条;同日多次为多条)
model CheckIn {
id Int @id @default(autoincrement())
habitId Int
habit Habit @relation(fields: [habitId], references: [id], onDelete: Cascade)
userId Int
date String // 本地日历日 "YYYY-MM-DD" (Asia/Shanghai)
seq Int @default(1) // 当日第几次 (1..n, 超额继续累加)
note String @default("")
source String @default("user") // user | backfill(补打)
createdAt DateTime @default(now())
@@unique([habitId, date, seq])
@@index([userId, date])
@@index([habitId, date])
}
// 检查清单
model CheckList {
id Int @id @default(autoincrement())
userId Int
title String
scene String @default("") // 出差 | 回老家 | 自定义
note String @default("")
archived Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
items CheckListItem[]
@@index([userId, archived])
}
// 检查项
model CheckListItem {
id Int @id @default(autoincrement())
listId Int
list CheckList @relation(fields: [listId], references: [id], onDelete: Cascade)
title String
note String @default("")
done Boolean @default(false)
doneAt DateTime?
sortOrder Int @default(0)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([listId, sortOrder])
}
// 清单模板(P1)
model CheckListTemplate {
id Int @id @default(autoincrement())
userId Int? // null = 系统预置
name String
scene String @default("")
items Json // [{ title, note }]
builtin Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
```
> 说明:周期进度由 `CheckIn` 按周期窗口现场聚合(不冗余计数字段),避免并发与修改 n 后的一致性问题。
---
## 附录 B:实现约定(开发期补充,用于消除口径歧义)
| # | 约定 | 说明 |
| --- | --- | --- |
| B-1 | **周期窗口** | 日=自然日;周=周一 00:00 ~ 周日 23:59;月=自然月;年=自然年;时区 Asia/Shanghai |
| B-2 | **环形进度** | 周期完成度 = 周期内累计打卡次数 / n(每周 n 次:打 1 次=1/n 环;每天 1 次:打 1 次直接闭合) |
| B-3 | **日历日期标记** | 用"当日完成度":当日次数 / **日均期望次数**(每天 n 次→n;每周 n 次→ceil(n/7);每月→ceil(n/当月天数);每年→ceil(n/365))。全完成=实心绿点,部分=黄点,超额=紫点,无记录=不显示 |
| B-4 | **streak** | 从当前周期往回数连续达标的周期数;**当前周期未结束且未达标不算断签**(跳过继续往前数) |
| B-5 | **补打限制** | 只能补打最近 7 天(`BACKFILL_DAYS`),未来日期禁止打卡 |
| B-6 | **撤销语义** | 撤销删除本周期内"最近一次"打卡(按 date desc、seq desc 取第一条) |
| B-7 | **删除打卡项** | 默认归档(`archived=true`,历史保留);`?hard=1` 才物理删除并清记录 |
| B-8 | **接口前缀** | 打卡 `/api/checkin/*`、清单 `/api/checklists/*`,全部 `authRequired`,数据按 `userId` 隔离 |
| B-9 | **前端入口** | 底部 Tab 4 个:**打卡(最左,默认选中)** / 宏观 / 监控 / 我;`/` 重定向到 `/checkin`,页内三视图:打卡 / 日历 / 清单。原「首页」取消,报警/新闻并入宏观 |
| B-10 | **特效** | 完成特效 + 触感反馈**恒开**,页面内不提供开关(2026-10-22 变更,原 `localStorage iboard_celebrate` 机制已移除) |
| B-11 | **OTA 版本** | OTA 包 `version = package.json.version + '-ota'`,必须与分支一致(`dev-0.2` → `0.2.0-ota`),CI 有强校验 |
| B-12 | **OTA 判新** | 我的页「开发版本」开关(默认关,持久化 `localStorage.iboard_ota_dev_mode`):关=版本号字段比较,开=build sequence 比较 |
### 接口清单
| 方法 | 路径 | 说明 |
| --- | --- | --- |
| GET | `/api/checkin/habits?date=` | 打卡项列表 + 当前周期进度 + streak |
| POST | `/api/checkin/habits` | 新建打卡项 |
| PUT | `/api/checkin/habits/:id` | 编辑(改 n / 周期 / 归档) |
| DELETE | `/api/checkin/habits/:id?hard=1` | 归档(默认)/ 物理删除 |
| POST | `/api/checkin/habits/:id/check` | 打卡一次(可带 `date` 补打、`note`) |
| DELETE | `/api/checkin/habits/:id/check?date=` | 撤销本周期最近一次 |
| GET | `/api/checkin/day?date=` | 某日详情(今天可打卡 / 昨天可补打 / 明天只读) |
| GET | `/api/checkin/calendar?month=YYYY-MM` | 月历:每日状态 + 当月完成率 |
| GET | `/api/checklists?archived=0\|1` | 清单列表(含进度) |
| POST | `/api/checklists` | 新建清单(可带 `templateId` 从模板生成) |
| GET/PUT/DELETE | `/api/checklists/:id` | 详情 / 编辑 / 删除 |
| POST | `/api/checklists/:id/duplicate` | 复制为新清单(勾选重置) |
| POST | `/api/checklists/:id/items` | 新增检查项 |
| PUT/DELETE | `/api/checklists/items/:id` | 改标题 / 勾选(`done`) / 删除 |
| POST | `/api/checklists/:id/check-all` | 一键全选 / 全不选 |
| GET/POST | `/api/checklists/templates` | 模板列表(含系统预置)/ 另存为模板 |
| DELETE | `/api/checklists/templates/:id` | 删除自定义模板 |