# 晨风书屋方案设计

> 适用版本：V2.3.0
> 更新日期：2026-07-12
> 本文只描述当前方案，历史 IndexedDB 方案见 `CHANGELOG.md`。

## 1. 设计原则

1. 服务器数据优先：任何设备看到和修改的都是同一 SQLite 数据。
2. 正确性优先：外部书目、规则和大模型都只是候选来源，低可信结果必须人工确认。
3. 输入方式统一：扫描枪、手机摄像头和手工录入最终走同一写接口。
4. 访客简单、管理明确：默认只读，管理员操作在服务器验证后才出现。
5. 部署与数据分离：发布代码不带数据库，恢复数据不混入普通版本迭代。
6. 家庭规模适配：用简单、可恢复的 SQLite 和无构建前端满足千本级使用。

## 2. 页面信息架构

```text
顶部工具区
  品牌 / 搜索 / 服务器状态 / 主题 / 管理员入口
  管理员登录后：录入 / 重排 / 修正 / 补封面 / 导入 / 导出

主内容区
  15 列 x 9 层书架
  搜索定位、拖拽平移、缩放和详情

辅助界面
  录入弹窗 / 摄像头扫码层 / 书籍详情 / 重排预览 / 统计面板

底部
  固定品牌、版本和返回主页
```

顶部服务器状态只表达网站 API 是否可访问，不表达 USB 扫描枪是否连接。扫描枪本质上作为键盘输入设备工作，浏览器无法可靠识别其物理连接状态。

## 3. 书架设计

### 3.1 物理映射

| 区域 | 规则 |
|:---|:---|
| G1-G13 | CLC A-Z 主书区，按实际藏书量比例分配 |
| G14 | 儿童读物固定列 |
| G15 | 相册固定列 |
| L1-L2 | 保留层，不参与日常自动分配 |
| L3-L8 | 13 x 6 = 78 个活跃格 |
| L9 | 收藏区，支持共享标签 |
| 单格 | 最多 15 本 |

位置统一为 `G{列两位}-L{层两位}-{格内序号两位}`，例如 `G03-L05-12`。

### 3.2 比例分配

对有书的 CLC 类执行两步算法：

1. 保底格数：`ceil(该类书数 / 15)`。
2. 剩余格数：按各类书量占比分配，余数按小数部分从大到小补齐。

目标是同时满足：已有书放得下、78 个活跃格恰好分完、A-Z 顺序稳定、小分类至少有一个可用格。

### 3.3 重排

重排先在浏览器中生成完整预览，用户确认后才提交。服务端在一个事务中先释放参与图书的旧位置，再依次写新位置，避免两本书交换位置时产生瞬时唯一索引冲突。

G14、G15 和 L9 的专用语义不参与普通 CLC 比例分配。若容量不足，流程应明确报错，不能静默丢书或生成越界位置。

## 4. 录入流程设计

### 4.1 USB 扫码枪

```text
扫描 ISBN
  -> ISBN checksum
  -> 国图/豆瓣/备用源查询
  -> 填充书名、作者、出版社、封面
  -> 权威 CLC 或规则建议
  -> 信息完整且可信：5 秒倒计时
  -> 立即确认或倒计时结束
  -> 写服务器并聚焦下一次扫描
```

重复 ISBN 不等于错误，因为家庭可能有同书多册。页面必须提示已有书名和位置，由管理员决定是否继续。

### 4.2 手机摄像头

- 只识别 EAN-13，并再次校验 978/979 ISBN。
- 使用后置摄像头，页面隐藏、关闭扫码层、识别成功或报错时释放媒体轨道。
- 扫码结果只填表，永不自动写库。
- 不支持 `BarcodeDetector` 时降级为手工 ISBN，不让核心录入功能失效。

### 4.3 无 ISBN 老书

- 题名必填，作者、出版社、年份和版次作为候选过滤条件。
- 当前题名搜索只提供有限补全，不自动采用为唯一事实。
- 后续多源聚合应展示多个候选、来源和置信度。
- 没有可靠候选时允许完全手工录入；CLC 可由规则或 DeepSeek 建议，但必须人工确认。

## 5. 分类设计

```text
权威来源 CLC
  -> 出版社映射
  -> 离线关键词匹配
  -> 服务器端 DeepSeek 建议
  -> 人工选择
```

- 事实字段和分类建议分开处理。
- 服务端只允许 22 个有效 CLC 大类代码。
- 模型请求限制题名、作者、出版社和摘要长度，不上传家庭备注或整库数据。
- 模型超时、额度不足或返回格式错误时，流程降级为人工选择，不阻断保存。

## 6. 管理功能设计

### 6.1 编辑与删除

打开详情后，管理员可编辑或删除。服务端重新校验书名和位置；位置冲突返回 409。删除需要清晰确认，后续应增加服务端操作审计和字段级乐观锁。

### 6.2 批量修正

- 只处理未核对或管理员选择的记录。
- 单轮最多 10 本，降低国图限流风险。
- 成功返回可信 CLC 才更新，并设置 `corrected=true`。
- 请求失败、无结果或字段不完整时不改原数据。

### 6.3 批量补封面

- 查询顺序：服务器豆瓣页面元数据，再回退 OpenLibrary `?default=false`。
- 只接受 HTTP/HTTPS URL。
- 查询与写入分离，写入使用封面专用事务接口。
- 封面接口不能覆盖书名、作者、出版社、位置、备注或分类。

### 6.4 收藏标签

L9 标签保存在服务器 `settings.shelf_labels`。旧浏览器中的 `cangshuge_custom` 只在管理员登录后迁移一次，服务器保存成功后从 `localStorage` 删除。

## 7. 导入导出设计

### 7.1 导入

导入是全量替换，必须通过以下门禁：

1. 浏览器识别格式并完整解析。
2. 校验每条书名、数值位置、范围和重复位置。
3. 展示格式、记录数和警告，用户二次确认。
4. 提交 `expectedCurrentCount`，服务器数据已变化则拒绝。
5. 服务端再次校验，创建 SQLite 快照。
6. 单事务替换书籍；旧备份无设置时保留服务器标签。
7. 页面重新读取并核对数量。

### 7.2 导出

导出是管理员操作。服务端直接生成版本化 JSON，包含：

- 固定格式标识 `chenfeng-library-backup`。
- schema 版本、导出时间和记录数。
- 完整书籍数组。
- 服务器收藏标签。

导出文件是迁移和人工灾备介质，不是浏览器日常数据源。

## 8. 主题与响应式设计

- 浅色与深色主题共用语义化色彩变量，不分别维护两套结构。
- `hl_theme` 只保存界面偏好；系统主题作为首次访问默认值。
- 桌面优先提供完整书架视野和鼠标交互。
- 手机工具栏允许横向滑动，弹窗高度受视口约束，内容区独立滚动。
- 重要按钮、输入框和扫码入口满足触控尺寸。
- 三级文字、占位符和卡片边界在两种主题中保持足够对比度。

## 9. 失败与降级

| 失败 | 处理 |
|:---|:---|
| API 不可达 | 顶部显示服务器不可用，不把本地内存当作已同步数据 |
| 管理员 401 | 清除会话，隐藏管理按钮，要求重新登录 |
| 位置 409 | 刷新服务器数据；新增最多自动重试一次 |
| 外部书目超时 | 尝试下一来源，最终允许人工录入 |
| DeepSeek 不可用 | 保留规则或人工分类，不阻断保存 |
| 导入预检失败 | 不发送写请求 |
| 导入快照失败 | 服务器拒绝替换 |
| 批量事务失败 | 整体回滚并保留原数据 |
| 摄像头不可用 | 释放资源并降级到手工 ISBN |

## 10. 设计决策记录

- 选择 SQLite：家庭单实例、千本级数据，简单可靠且便于一致性快照。
- 不保留 IndexedDB：会形成多设备数据分叉和版本更新后数据来源不清。
- Secret 分离：管理员密码与 DeepSeek Key 生命周期和用途不同，必须独立安装和保护。
- 模型只做建议：书目事实与家庭实体位置需要可追溯，不能让概率输出直接写库。
- 文档与测试同仓：下一次维护能够从代码、规则、验证和生产基线形成闭环。
