# 交接 · 组卷台 2026-09-09.4（分片架构 + 题库扩到 160,524 + 源文件下载补齐 + 中考类源文件网页预览）→ difrobot.cn

> 2026-09-09 由组卷台开发会话出具。**这版部署布局和以前完全不同**，请先读「① 布局」再动手。线上现为 05.2（113,500 题）。
> A. fig 分片 —— **已定方案 A（COS 链接）**；B. 源文件 —— **已上生产机（09-09 05:03 rsync，见 ⑦）**；C. **陆老师 2026-09-09 21:50 拍板：准备部署 09.4**，顺序见 ⑪。前后端必须同批（见 ⑥）。

## ① 布局：html 变成 0.46 MB 的壳，数据在 `zj_assets/`

`组卷系统/组卷台.html`（≈451 KB，`NEED_BUILD='2026-09-09.4'`；09.3 = 09.2 + 校本卷收尾 28 卷 + 上海中考 2005 年卷；09.4 = 09.3 + 中考类源文件「📄 预览」（见 ⑨），布局不变）+ `组卷系统/zj_assets/`：

| 路径 | 内容 | 体积 | 进不进 git |
|---|---|---|---|
| `zj_assets/data/NN_k.js`（14 个） | 题库，按教材切，`ZJ.data("教材",[…])` | 101 MB，最大单文件 **12.6 MB** | **进**（Gitee 单文件 100 MB 上限解掉） |
| `zj_assets/figm.js` | 配图元数据 `{key:[w,h,分片id]}` | 8.6 MB | 进 |
| `zj_assets/vendor/tex-svg.js、docx.iife.js` | MathJax / docx（原来内嵌在 html） | 3.1 MB | 进 |
| `zj_assets/fig/sNNNN.js`（304 个） | 配图 base64，`ZJ.fig("sNNNN",{key:dataURI})`，页面**按需懒加载** | 391 MB | **不进** —— 二选一： |

- **方案 A（✅ 陆老师 2026-09-09 已定）**：沿用你们现在的抽图流程，但对象换成 `fig/*.js`：把每片里的 dataURI 换成 COS URL（图仍按内容寻址上传 COS），改完的 `fig/*.js` 每片只剩几十 KB，可以进仓库。运行时把值当 `<img src>` 用；导出需要字节时前端会自己 fetch 转 base64（COS 需允许 `difrobot.cn` 跨域读，即 CORS `GET`）。
- **方案 B**：`fig/` 整个目录原样放 COS（391 MB，304 个文件），在 html `<head>` 的第一个 `<script>` 之前注入 `<script>window.ZJ_ASSET_BASE='https://<cos>/zujuan/zj_assets/'</script>`，页面就从那里拉 `fig/sNNNN.js?v=…`。data/figm/vendor 仍走本站相对路径。
- 所有资源 URL 带 `?v=2026-09-09.4`；nginx 对 `/zujuan/zj_assets/` 建议 `expires 1y` + `gzip on`（`application/javascript`；data 分片 gzip 后 ≈20 MB，首屏传输从 118 MB 降到 ≈25 MB）。html 仍 no-cache。
- 页面启动会自检分片是否齐（`ZJ._files == 15`），缺了顶部红横幅，不会静默少题。

## ② 加工脚本要防的点

- html 里**不再有** base64 图、不再有 `const DATA=`；原来「从 html 抽 data:image」的步骤对 html 会抽到 0 张，属正常，改对 `fig/*.js` 做。
- `</body>` 出现 2 次（第 1 处仍在 doWord 模板串），锚最后一处。`</script>` 若干，全是真标签。
- 门禁注入照旧注在 html；`zj_assets/*.js` 是纯数据脚本，**不要**注入门禁代码。
- 网站窗口 09-10 起的加工链会自动注 `ui_tracker.js` 并打后端日志补丁（8c48727），10.1 起的壳照常交付即可；壳里 `</body>` 仍是 2 处（锚最后一处），未定义 `window.__difTrack`。

## ③ 后端（`组卷系统/组卷台AI助手.py`，BUILD 2026-09-09.4）

相对 05.2 的改动：
- `EXAM_TBS` 加「上海校本卷」；提示词加 AMC8 / 新思路 / 九上·新教材 / 八年级汇编两册 / 中考·分类汇编 / 校本卷 各自轴说明；`validate_spec` 加 `{"all":true}` 组。
- `_serve_source_page` 加一课一练 `8s`、新思路 `xsl6a…xsl8b` 页图路由（页图在 `成果/一课一练题库|新思路题库/<册>/pages/`，线上没有这些目录；但「对照原书」优先走 `_专题拆分/` 里的切片 PDF + pdftoppm 现渲，切片已在生产机（⑦⑩），装了 poppler 就能看）。
- `/zj_assets/` 长缓存头。
- **源文件多份**（录入窗口 09-09）：`_paper_index` 变 pid→[文件…]（原卷版=学生版在前、解析版=教师版在后），新增 `GET /api/paper/files?pid=` 列清单，`GET /api/paper?pid=&i=` 取第 i 份（缺省 0 兼容旧调用），`/api/openword {pid,i}`（本地功能，线上仍不放行）。
- **中考类源文件预览（09.4）**：`GET /api/paper/files` 多返回 `groups`（同主名 docx/pdf 并一行）；新增 `GET /api/paper/preview?pid=&i=` —— 只对 PDF，用 `pdftoppm` 渲页图（缓存 `.cache_pages/`），返回和教材类「对照原书」同款页图 HTML。**服务器必须装 poppler**（`apt install poppler-utils`，`which pdftoppm` 核一下；教材类 `/api/topic` 现渲早就依赖它，若线上从没装过说明之前教材类预览走的全是预渲页图）。
- 构建期脚本改动（服务器不需要）：`split_topics.py`、`build_precut_map.py`、`gen_组卷台.py`、各 `zujuan_*.py`。

未动：topic_source / gen_router / verify_gen / export_lib / xkw_similar / `自动出题/`。

## ④ API / nginx

- 新路径 `GET /api/paper/files`、`GET /api/paper/preview`（09.4）。现有白名单是前缀 `paper` —— 若正则形如 `^/api/(…|paper|…)` 不带结尾锚，`/api/paper/files` 已覆盖；若写成 `paper$` 或精确匹配需加 `paper/files`、`paper/preview`。**请核一下再上**。
- 静态：`/zujuan/zj_assets/` 必须可直接 GET（同域相对路径）。
- 其余不变：`version|topic|page|source|paper|similar|similar_batch|export_docx|report|compose|layout`，openword 不放行。

## ⑤ 数据体量（本版内容）

**160,524 题** / 10 套教材：胡小群 4,086 · 举一反三 2,201 · 动脑筋 4,939 · 一课一练 19,709（+八上新教材 1,178）· 新思路 5,343（新）· 初中培优 18,319（+九上·新教材、八上·期末汇编、八下·期中汇编、中考·分类汇编）· 上海中考 12,739（+76 卷补录 +2005 年）· 全国中考 55,576 · 上海校本卷 36,987（新，1,500 卷）· AMC8 625（新）。知识点标注 26,670 题（全国/校本/AMC8/新思路暂无）。

## ⑥ 为什么不能只推后端

后端 BUILD 已是 09.4，线上页面 NEED_BUILD 是 05.2 → 只推后端会让所有老师页面顶部弹红色版本横幅。前后端必须同批；而前端是新布局，需要 ① 的 COS 决策。源文件（B 项）不上的话 `/api/paper/files` 在线上照样返回空，功能等于没上。

## ⑦ 源文件（B 项，✅ 已上生产机）

录入窗口 2026-09-09 05:03 经陆老师同意 rsync 完成：`/opt/zaojuan-deploy/server/源题文件/_专题拆分/`（3,360 文件 6.4 GB）、`源题文件/中考一模二模真题/`（`_paper_index` 引用的 9,014 文件 8.4 GB，相对路径与本机一致）、`数学学习系统/data/{drafts,approved}/*/{exam_papers,source_documents}.jsonl` 14 份；两边文件数一致，服务器盘 51%。

**分两步走：**
- **步骤 0（现在就能做，零风险）**：只 `restart zaojuan`。线上 05.2 后端未命中 COS 映射时本来就回退本地 `topic_source` / `_paper_index`，文件一到位教材类 `/api/topic/files` 就有货；`_paper_index` 是**进程内缓存**，不重启不会重建。重启后中考类 `/api/paper?pid=` 每卷给 1 份（05.2 行为）。
  验收：AMC8 2023年 源文件条出现 1 行、新思路六上 1.1 两行、胡小群 初一·基础 有理数概念（一）三行、任一校本卷/全国卷有「下载」。
- **步骤 1（09.2 全量上线时）**：新后端 `/api/paper/files` 多份 + 新前端「有几份渲染几行」一起上；校本卷两行、全国云南 2022 四行到那时才有。
  ⚠ 若想在 09.2 前先让多份源件生效，唯一安全做法是把新后端的 `BUILD` 临时改回 `'2026-09-05.2'` 再推（API 向后兼容、export_lib 未动，握手号只是防「预览≠导出」）——是否这么做由网站窗口定，我不推荐多一个分叉。

## ⑧ 验收（部署后）

1. `curl /api/version` → `"build": "2026-09-09.4"`；`curl -I /zujuan/zj_assets/figm.js?v=x` → 200 且长缓存。
2. 打开页面：无红横幅；控制台 `ZJ._files===15`、`DATA.length===160524`；教材下拉 10 套。
3. 胡小群任一带图专题 → 图显示（说明 fig 分片路径/COS 通）；上海中考 2024 年任一卷 → 公式图（inl）显示；AMC8 2006 年第 10 题五个图选项显示。
4. 试卷篮放 3 道带图题 → 预览有图 → 下载 Word（后端）有图 → 本地兜底 docx 有图（COS 方案需 CORS 通过）。
5. 「✨ AI 组卷」`跨教材来 8 道鸡兔同笼，四年级` → 8 道；`南洋模范八上期中卷选 10 道` → 上海校本卷。
6. 源文件条（若 B 项已上）：AMC8 2023年 1 行、新思路六上 1.1 两行、上海校本卷任一卷两行、全国云南 2022 两行（09.4 起同主名 docx/pdf 并成一行）。
7. 中考类预览（09.4，需 ⑨ 的 PDF 已同步）：上海中考 2024 年 → 源文件条「原卷版 📄 预览 ⬇ Word ⬇ PDF / 解析版 …」→ 点「📄 预览」新开页有页图（5 页）；任一校本卷「解析版 📄 预览」有页图；`curl -s '/api/paper/preview?pid=yn-2026-zk-math&i=1' | grep -c '/api/page?'` > 0。

## ⑨ 中考类源文件 PDF（09.4 新增，需 rsync）

陆老师要求中考类源文件也能网页预览（不给「用 WPS 打开」）。本机已用 WPS 把 `源题文件/中考一模二模真题/` 下所有只有 doc/docx 的卷批量转成 PDF，放在**源文件旁边、同主名 .pdf**（源旁本来就有卷商 PDF 的不动）。第一批 4,315 份 + 补批 1,741 份，全部零失败；新增 PDF **4,279 份 / 4.2 GB**（本机终态：中考类 4,232 卷 6,408 组，缺 PDF 0 组）。

- 同步：只同步新增的 `.pdf`（mtime 晚于 09-09 05:03 那次 rsync），例如
  `rsync -av --include='*/' --include='*.pdf' --exclude='*' --prune-empty-dirs "源题文件/中考一模二模真题/" <生产机>:/opt/zaojuan-deploy/server/源题文件/中考一模二模真题/`
  （`--include '*.pdf'` 会把老 PDF 也比一遍，但内容相同不传；也可以先 `find -newer` 出清单再 `--files-from`）。**不进 git**（.gitignore 已排 源题文件/）。
- 09-09 16:40 已完成「去卷商广告页眉」：转出 PDF 1,902 份 + 卷商原件 558 份被覆盖（原件备份在 `组卷系统/_pdf_batch/vendor_orig_backup/`，不用上服务器）。源目录 8,384 份 PDF 现在卷商广告残留 0。**因此要同步的不只是新增 PDF，而是 `源题文件/中考一模二模真题/` 下所有 `.pdf`（rsync 按内容比对，没变的不传）。**
- 同步完 **restart zaojuan**（`_paper_index` 进程内缓存）。
- 预览用 `pdftoppm`，见 ③。

## ⑩ 专题切片重切（09-09 晚，需 rsync）

新思路 262 份「对照原书」切片按新页范围重切（每讲补回开头讲义页），文件名不变、内容变了：同步 `源题文件/_专题拆分/新思路/` 与 `_专题拆分/_索引.json`、`_组卷台映射.json`；另新增 3 份 `新思路/{七年级下,八年级上,八年级下}第三版/00.全册参考答案·教师版.pdf`（硬链自源目录的独立答案 PDF，rsync 会当普通文件传）。（一课一练没变；`新思路.bak-0909-讲义页前`、`一课一练.regen-0909-已弃` 两个目录不用上）。

## ⑪ 部署顺序（09.4 一次上齐）

1. **数据同步（不进 git，沿用 09-09 05:03 源文件 rsync 的口径，陆老师已同意）**：⑨ `源题文件/中考一模二模真题/**/*.pdf`（4.2 GB 新增 + 广告清理后的改动）、⑩ `_专题拆分/新思路/` + `_索引.json` + `_组卷台映射.json`。
2. 服务器 `apt install poppler-utils`，`which pdftoppm`。
3. 前端加工：html + `zj_assets/{data,figm.js,vendor}` 进仓库；`fig/*.js` 按方案 A 换 COS URL 后进仓库；门禁只注 html。
4. 后端 `组卷台AI助手.py`（BUILD 09.4）同批进仓库；nginx 白名单核 `paper/files`、`paper/preview`（④）。
5. push → 服务器 pull → restart zaojuan（`_paper_index` 与映射都是进程内缓存）。
6. 按 ⑧ 验收 7 条，回报：远端 hash = 服务器 HEAD、工作区 0 手改、7 条验收结果。

## ⑫ 09.4a 壳热修（09-09 23:15，只推 html）

线上首开一次链路瞬断截断 6 个分片（服务端已排除）。html 壳加了分片自动重试：坏片带 `&r=1/2` 同步重拉两次，仍败才横幅并点名文件。`zj_assets/` 15 个数据分片 md5 与线上一致、fig 不动、BUILD 仍 09.4、后端不动；只需重新加工并推 `组卷系统/组卷台.html`（453 KB），不用 restart。E2E `tools/e2e_retry.py` 6/6 + `e2e_shard.py` 13/13。

## ⑬ 10.1 候选（2026-09-10 00:50 本地已建，上线等陆老师定）

+ 袋鼠数学 Level A/B 历年真题 820 题（tb=袋鼠真题，年份轴 2005–2026，同年 A/B 两节，共 37 节），全库 **161,344 题 / 11 套教材**。BUILD / NEED_BUILD = **2026-09-10.1**。
- **全量部署**：data 分片 14→15 个（`ZJ._want` 15→16）、figm.js 8.7 MB、fig 304→312 片 400.7 MB（旧片清 4 个，新图 ≈900 张走 COS 增量）、html 455 KB、后端提示词（袋鼠规则）；`?v=2026-09-10.1`。
- 服务器新增目录：`成果/袋鼠题库/Level{A,B}/pages/`（159 + 169 张页图，「对照原书」路由 `/原书页/mk/(mka|mkb)/pNNN.png`）——与一课一练/新思路 pages 同属「线上没有」的那类，要一起 rsync 才有对照原书。
- 验收加两条：教材下拉 11 套；袋鼠真题 2013 年 Level B 第 1 题（选项见图）图显示；AI 组卷「袋鼠 2024 Level B 选 8 道」→ 8 道且只含 Level B。

## 本机 E2E（2026-09-09）

`组卷系统/_pdf_batch/tools/e2e_shard.py` 13 项（09.4 终版 13/13，`e2e_srcbar.py` 三类源文件条 + 预览页图也过）：分片齐全、启动不拉 fig、胡小群/上海中考/AMC8/全国 图回填数==需求数、渲染后只装了部分分片（1,171/93,858）、预览 3 道带图题真图、本地 docx 导出 36 KB、分片断网有失败标记不崩、零 JS 错误；首屏本机 0.7 s。
源文件条本机：AMC8 2023 年 1 份（原卷含答案·教师版）、新思路六上 1.1 两份、胡小群初一 有理数概念（一）三份、校本卷 2 份、全国云南 2022 四份；`/api/paper?i=1` 200、越界 404。
