# 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` | 删除自定义模板 |