平台工程

站内新版块五件套模式:三次复用与起刊两坑

一句话:蛛网之上加一个内容版块 = 固定五件套(数据源/内容集合/列表详情页/首页速览/全站导航),第三次复用已收敛成套路;数据驱动让「起刊/撤稿」的成本压到增删一个数据文件

事实记录(不可修改区)

五件套模式(第三次复用,已收敛)

给数据驱动的静态站(Astro/同类 SSG)新增一个内容版块,固定做五件事,缺一件就是「孤岛页面」:

  1. 数据源:src/data/<版块>/ 下一条内容一个文件(md+frontmatter 或 JSON),schema 注释写在读取代码旁;
  2. 内容集合/加载器:构建期统一读取、校验、排序,坏文件跳过并告警不阻断构建;
  3. 列表页 + 详情页:/<版块>//<版块>/[slug]/,排印复用主站 design token(本站是杂志刊头式),零新增依赖;
  4. 首页速览版块:最新几条 + 入口,版块索引(toc)与刊眉编号用条件插入自动顺延,不硬编码编号;
  5. 全站导航入口:页头 + 页脚各补一个链接。

五件套做完,发布/撤稿/收官全部退化为数据文件操作:新内容 = 加一个文件;撤稿 = 删一个文件;状态在已有取值间流转 = 改一个 frontmatter 字段(如 status: open → closed)。这是 每日定时任务自动更新内容板块_首次自动化生产落地_v1「自动化只碰数据律」的前提结构——呈现层先数据驱动,内容操作才可能收缩为纯数据操作。

注意边界:上句只对已有状态之间的流转成立。给状态机新增一个取值不是改数据、是改代码,另有坑,见下「状态机演进」。

两坑(均首次实测)

⚠️ YAML 裸日期坑:frontmatter 的 2026-07-20 不是字符串

现象:内容集合 schema 写 date: z.string(),构建报 Expected type "string", received "date"

根因:YAML 规范把裸写的 2026-07-20 解析成 Date 对象,不是字符串;js-yaml/Astro 均遵循此行为。

修法(任选,推荐前者做在 schema 层一劳永逸):

// schema 层收敛:两种类型都接受,统一转 YYYY-MM-DD 字符串
const dateStr = z
  .union([z.string(), z.date()])
  .transform((v) => (v instanceof Date ? v.toISOString().slice(0, 10) : v));

或 frontmatter 里给日期加引号 date: "2026-07-20"(依赖每个写入者自觉,不如 schema 层收敛可靠)。

⚠️ 机器人仓库 push 竞争:人工 push 前默认 rebase

现象:功能开发完 git push 被拒(non-fast-forward),远端多出一个陌生提交。

根因:仓库挂着云端定时任务(每日快讯 cron)会自动 commit + push,人工开发窗口越长,撞车概率越高。

规则:凡是有机器人自动提交的仓库,人工 push 失败不是异常是常态,git pull --rebase 后重推即可;心智上把「push 前先 rebase」设为默认动作,不要看到 rejected 才处理。

状态机演进:新增一态 ≠ 改一个字段(2026-07-24 补,任务书首次状态更新)

任务书板从「二态式」(招募中 / 已收官)演进到四态状态机(招募中 open → 进行中 taken → 完工待打款 done → 已收官 closed),起因是三份测评任务被接取、要标注「进行中 · 接取人」。落地暴露:「状态流转=改一个字段」只对已有取值成立;新增一个取值是一次跨多处的代码改动,且藏着一个静默坑。

⚠️ 头号坑:取反式过滤会静默吞掉新状态

现象:列表页原本二分——open 进「招募中」,其余 进「已收官」归档。新增 taken 后,若不改过滤,taken 任务会被那句 status !== 'open' 静默归进「已收官」,显示成早已完成——错得离谱且构建不报错。

根因:取反 / 兜底式过滤(!== 'open'elsedefault)默认「除了我关心的,其余都算同一类」。枚举一旦扩容,新值就从这个「其余」缺口漏进错误的桶,而类型系统和构建都不会拦——它语法完全合法。

规则:状态分桶一律正向枚举,不写取反。把 closed = status !== 'open' 改成 closed = status === 'done' || status === 'closed',新增状态时这句自然不接纳它,逼你显式决定它进哪个桶。一句话律:enum 扩容时,先审所有「取反 / else / default」分支——那里是新值静默泄漏的必经之地。

新增一态要穿线的所有渲染点(清单)

一个状态值不是加进 schema 就完事,得挨个渲染点补齐,漏一处就是显示错误:

  1. schema enum:加取值(+ 本次配套加 taker 字段);
  2. 列表页过滤:正向枚举分桶(见上),并为新态开一个展示区(本次「进行中」独立成区,中性色卡片,不和招募中/已收官混);
  3. 列表页 / 详情页 / 首页速览的状态标签:三处各有一份 label 映射,全要认识新值(漏一处就 fallback 成错标签)。

教训:label 映射散落在多个页面是重复源,新增状态时最易漏。可考虑抽成单一 statusLabel(status) 共享函数收敛——本次三处仍是各写各的,记为下次重构点。

附:两个小陷阱

结果分析

关联文档

类型/平台工程主题/版块工程