CYBERSWAT / 开发部自由练习 · v0.4.1

U12 · 公开的是一条便笺,不是整个账号

v0.4 · 先修:U09 的身份、可见性和分页;工作台。产品参考:Memos。本节独立用 Vue/Nest 实现合成数据简版,不要求先学其 React/Go 源码。

一条便笺可能只有一句话。我们按时间显示它,加上标签,并让作者选择公开还是私人。公开一条便笺不会公开作者的其他便笺,导出自己的资料也不应带走别人公开的资料。

动手路线:每一步都有文件和结果

先完成 统一启动与恢复手册 的首次准备。以下命令均在自己的练习根目录执行;原文中的 api/web/desktop 概念对应本节列出的现成文件,不需要猜测如何搭空项目。

  1. 打开 u12/starter/model.ts,按 timeline → exportMemos 的顺序逐个实现。下面第1节起解释规则;每完成一个函数就重跑样例/测试。
  2. 执行以下三条,分别核对样例、行为和类型。未完成的 TODO 应失败,全部实现后测试应为 fail 0:
node tools/try.mjs u12
node tools/check.mjs u12
npm run check
  1. 打开 runtime/src/u12.ts、runtime/web/u12.js,找到对自己规则函数的调用。先用一句话记录输入从哪里来、结果写到哪里;读不懂的语法回到本节先修对应章节。

  2. 先运行 node tools/lab.mjs db up,等待迁移完成。构建并启动自己的作品:

node tools/build-app.mjs u12
node tools/lab.mjs start u12
  1. 实际操作:Alice 登录保存默认私人便笺;退出再刷新不可见。Bob 登录仍不可见。Alice 登录导出只能含自己的记录;按标签筛选不改变权限。

在第二个终端运行 node tools/probe.mjs u12。脚本会添加合成数据并报告每组检查;不清空自己的数据。 首次通过后停止并重新启动服务,再运行 node tools/probe.mjs u12 --resume;数据库单元同时按统一手册重启数据库。

  1. 解释与再做一次:排序相同时间时用什么打破平局?公开可见是否意味着能导出别人的全部数据? 在自己的 LEARNING.md 先写答案,再选本节末尾的变式,列一个新失败例子后修改。保持原用例通过。仅跑通配套界面、没有完成规则与解释,记录为“参考演示已观察”,不记录为自己完成作品。

卡在规则时按“函数输入→分支条件→返回值→参考实现”逐级看提示;卡在运行时按 故障表 定位。若想单独观察参考成品,在构建命令后加 --reference;页面会明确标出参考演示。观察完再去掉该参数重建,继续自己的 starter。

1. 先建立三个视角

打开 u12/starter/model.ts,Memo 包含 owner、visibility、createdAt、tags、text。准备 Alice 的公开/私人两条与 Bob 的私人一条,分别预测匿名、Alice、Bob 看到什么。

完成 timeline:先验证日期,再按可见性和标签过滤,时间倒序,同一时间用 id 升序打破平局。只接受明确可解析的题目时间;网络输入还应限制为服务规定的 ISO 格式,创建时间由服务端生成,不信客户端随意伪造。

2. 导出范围与浏览范围分开

完成 exportMemos:必须有身份,只包含 owner 为本人的条目,不是“把当前能看到的都导出”。运行 node tools/check.mjs u12,观察 Alice 导出包含自己的两条,却不包含别人公开内容(再自行补一条公开的 Bob 便笺验证)。

3. 先接列表与详情,最后加编辑

配套 Prisma 模型保存 text、owner、visibility、createdAt、updatedAt,tags 使用 PostgreSQL 字符串数组;owner 即身份字段。数组方便本节练过滤,但不提供独立标签表的外键约束。API 身份从认证中取,列表和详情统一执行“公开或本人”。选择不泄露私人资源存在性的 404 策略,直接猜 id 同样应用。

创建默认 private,发布是作者明确操作;修改可见性只能由作者。不要在列表页隐藏后就忽略详情、导出和搜索接口。用直接请求逐条验证。

4. 做时间线,处理相同时刻

Vue 列表使用 id 作 key,显示时区说明;测试数据用完整带 Z 的时刻,显示可转换成读者本地时间,但排序依据实际时间值。标签过滤由服务器控制范围,不能先把全部私人内容发到前端再隐藏。

本节接口和页面已提供 offset/limit(页面每次5条、接口最多100条),分页沿 U09 的方式,并保持 createdAt/id 一致排序;在两页之间新增内容可能造成位移,把这个限制写清楚。不要只因固定样例没重复就声称动态数据永不重漏。

5. 导出后重新核对资料边界

导出入口使用服务端身份条件取本人数据,本版在页面输出可复制保存的 JSON 文本,含原文和标签,不含密码、令牌或其他用户资料。将导出文件重新读取,逐条比较 owner 与数量,确认中文和换行保留。下载失败时页面不能显示已经备份成功。

把一条公开便笺改回 private,匿名再次请求列表、详情和标签搜索,均不能再读到内容;若用了缓存,需要失效处理。这个检查比只看作者自己的页面更能说明权限有效。

6. 完成检查与变式

完成匿名/本人/他人可见性、直接详情、同时间稳定排序、标签空结果、导出本人数据、撤回公开、重启持久化。核心测试只覆盖数组计算,不是认证或 API 证据。

独立变式:设计基于 createdAt/id 的游标分页,先用同时间三条便笺验证翻页不重复,再在两次翻页间新增一条,记录行为边界。暂停前保存数据准备脚本与实际时区;不要用真实私人笔记做示例。