# 数据存储(SQLite) 平台使用 **SQLite 单文件数据库** `data/ny.db`,零配置、无需额外服务。 ## 启用条件与降级 ```php Db::isConfigured() // 是否有 pdo_sqlite 扩展 Db::isConnected() // 是否真的连上了 Db::lastError() // 失败原因 ``` | 环境 | 行为 | | --- | --- | | 有 `pdo_sqlite` | 使用 `data/ny.db`(同时继续双写 JSON 文件作为备份) | | 无 `pdo_sqlite` | 整体降级为 JSON 文件存储,功能不受影响 | > 前端可通过 `config_get` 返回的 `storage`(`db` / `file`)判断当前用的是哪种。 ## 连接参数 连接建立时施加的 PRAGMA: ```sql PRAGMA journal_mode = WAL; -- 读写并发 PRAGMA synchronous = NORMAL; PRAGMA busy_timeout = 5000; -- 写锁等待 5 秒 PRAGMA foreign_keys = ON; ``` 连接**每请求只尝试一次**,失败后返回 `null` 而非抛异常,保证上层能优雅降级。 ## 表结构 所有建表 / 加列都是**幂等**的(`CREATE TABLE IF NOT EXISTS` + `PRAGMA table_info` 探测后 `ALTER TABLE`),可重复执行。 ### `ny_settings` — 全局配置 | 列 | 类型 | 说明 | | --- | --- | --- | | `k` | TEXT PK | 键,目前只有 `config` | | `v` | TEXT | 整份配置的 JSON | | `updated_at` | — | 更新时间 | ### `ny_roster` — 学号名单 | 列 | 类型 | 说明 | | --- | --- | --- | | `sid` | TEXT PK | 学号 | | `name` | TEXT | 姓名 | | `cls` | TEXT | 班级 | | `grp` | TEXT | 组别 | | `login` | TEXT | 登陆账号(仅用于抽取页白名单导出) | | `sort` | INTEGER | 排序 | > `login` 列是后期加入的,旧库会在启动时自动 `ALTER TABLE ADD COLUMN`(幂等)。 ### `ny_project_config` — 每卷独立配置 | 列 | 类型 | 说明 | | --- | --- | --- | | `project_id` | TEXT PK | 卷王试卷 ID | | `config_json` | TEXT | `{ full_score, pass_rate, exempt[] }` | | `updated_at` | — | 更新时间 | ## 迁移策略 首次在有 SQLite 的环境运行时: 1. `ny_settings` 为空 → 从 `data/config.json` 迁入; 2. `ny_roster` 为空 → 从 `data/roster.json` 迁入(`Roster::migrateFileToDbOnce()`); 3. 每卷配置同理从 `data/project_config.json` 迁入。 **迁移只发生在目标表为空时**,不会覆盖已入库的数据;重复执行安全。 ## 读写路径 | 数据 | 主存储 | 备份 | | --- | --- | --- | | 配置 | `ny_settings['config']` | `data/config.json` | | 名单 | `ny_roster` | `data/roster.json` | | 每卷配置 | `ny_project_config` | `data/project_config.json` | | 结果缓存 | 文件系统 `data/cache/` | — | | 卷王令牌 | 文件系统 `data/cache/token.json` | — | `ny_save_config()` 双写:先写库,再写文件。这样即使数据库突然不可用,也不会静默回落到出厂默认值。 ## 维护 ```bash # 备份(推荐:直接复制文件) cp data/ny.db data/ny.db.bak # 完整性检查 php -r '$p=new PDO("sqlite:data/ny.db"); var_dump($p->query("PRAGMA integrity_check")->fetchAll());' # 手动查看名单 php -r '$p=new PDO("sqlite:data/ny.db"); print_r($p->query("SELECT * FROM ny_roster LIMIT 5")->fetchAll(PDO::FETCH_ASSOC));' ``` ::: tip 命令行加载扩展 PHP CLI 常常不加载 Web 端已启用的扩展,需要显式指定: ```bash php -d extension_dir="C:/php/ext" -d extension=pdo_sqlite script.php ``` ::: ## 相关 - [架构与目录](./architecture.md) - [接口一览](./api.md)