add

2026-07-24 12:00:48 +08:00 Unverified
parent 65135c72bf
commit ee5dfa6d61
11 changed files with 2730 additions and 1 deletions
+334
@@ -0,0 +1,334 @@
# 管理操作指南
本页面向超级管理员、校级管理员、班级管理员和招生学校账号。系统会同时在前端和服务端执行权限检查,管理员不能通过手工拼接 URL 越过数据范围。
## 1. 角色与权限边界
| 功能域 | 超级管理员 | 校级管理员 | 班级管理员 | 招生学校 |
| --- | --- | --- | --- | --- |
| 全局运行总览 | 全部 | 本校 | 本班 | 本校招生业务 |
| 学校与班级 | 管理全部学校 | 管理本校组织 | 只读本班相关信息 | 不适用 |
| 管理员账号 | 管理各级管理员 | 按业务入口管理本校班级相关事项 | 不适用 | 不适用 |
| 批量申领报名号 | 审批、监督 | 发起并接收结果 | 不直接发起 | 不适用 |
| 考生资料 | 全部并可监督 | 本校 | 本班 | 投档后必要资料 |
| 报名与缴费 | 全部 | 本校 | 本班 | 不适用 |
| 考试与科目 | 创建、修改、归档 | 查看相关考试 | 查看相关考试 | 查看招生关联考试 |
| 成绩录入与发布 | 可操作 | 范围内查看、导出、参与流程 | 范围内查看、参与复议 | 仅投档材料中的必要成绩 |
| 考点考场 | 全局维护、审批 | 本校档案变更申请 | 查看相关安排 | 不适用 |
| 审批流 | 设计、监督、退回 | 处理本校步骤、转交 | 处理本班步骤、转交 | 招生业务指定动作 |
| 招生录取 | 全局设置与投档签发 | 确认本校指标资格 | 不可代确认指标资格 | 计划、投档、报到、模板 |
| 通知公告 | 创建、发布、撤回 | 查看 | 查看 | 查看相关公示 |
同一级可以配置多个管理员。待办会按当前待办数和历史分配量在同一业务范围内自动均分,当前处理人还可把流程转交给同范围同级管理员。
## 2. 超级管理员首次配置
建议按以下顺序完成初始化:
1. 登录后立即修改初始密码。
2. 在“账户安全”绑定 TOTP 并离线保存恢复码。
3. 在“学校管理”建立学校,填写稳定且唯一的学校代码。
4. 根据学校职责标记生源校、招生校,或同时具备两类职责。
5. 建立校级、班级管理员,明确学校和班级数据范围。
6. 在“报名号规则”配置年份、学校代码、性别、固定值和流水号等号码段。
7. 在“流程设计”配置各业务的审批步骤。
8. 在“考试与科目”建立考试和科目。
9. 需要自主注册时才临时开启,并在结束后关闭。
生产系统完成初始化后,不要继续使用模板初始密码。
## 3. 组织、账号和报名号
### 学校与班级
学校代码会参与报名号、录取通知书编号和考务数据识别,应在投入使用前确定。系统保存学校和班级的数据范围关联,不能把权限控制仅理解为页面筛选。
### 管理员账号
- 超级管理员可建立多个同级账号,避免共享一个管理员账户。
- 校级和班级管理员必须绑定正确的学校、班级范围。
- 停用账号后,其有效登录状态会失效。
- 密码重置产生的是新凭据,管理员不能读取旧密码。
- 交接人员时,应创建或启用个人账号,不建议多人长期共用。
### 报名号规则
规则可组合:
- 年份;
- 学校代码;
- 性别;
- 固定文本;
- 流水号。
超级管理员只维护规则,不直接替代学校的批量申领流程。报名号在考生账户创建时原子生成一次,后续参加不同考试仍复用同一号码。
### 批量建号
校级管理员:
1. 选择一个或多个班级;
2. 为每班填写申请人数;
3. 提交批次审批;
4. 等待最终批准;
5. 下载按班级返回的报名号和初始密码 Excel;
6. 通过安全渠道分别下发。
即使只申请一名考生,也按一人批次进入审批。最终批准时系统一次性生成报名号、随机初始密码和待补录账户,避免部分成功造成数量不一致。
## 4. 审批流程
可配置审批的主要业务包括:
- 考生资料修改;
- 考试报名;
- 成绩复议;
- 批量报名号申领;
- 考点和考场档案变更。
处理原则:
1. 先核对完整业务上下文和申请快照。
2. 通过时确认下游步骤和最终生效条件。
3. 退回时填写可执行的修改意见。
4. 需要更换处理人时使用“转交”,不要让其他管理员共享账号操作。
5. 超级管理员监督处理时保留原因,避免无说明改写流程历史。
班级和校级步骤会自动限制到考生所属班级和学校。审批实例、责任人、转交和监督动作均留痕。
## 5. 考生资料、报名与缴费
### 考生资料
各级管理员在自身范围内查看考生资料、审核状态和流程历史。Excel 导入会逐行校验并返回具体行号;批量修改资料仍需经过配置的审批流程,不因使用 Excel 而绕过审批。
### 报名审核
核对:
- 考试和科目;
- 考生资料状态;
- 报名时间;
- 费用;
- 当前审批步骤;
- 历史处理意见。
### 缴费管理
报名终审与缴费确认彼此独立。超级、校级和班级管理员均可在各自数据范围内维护缴费状态,确认时系统记录办理人和时间。
缴费台账应通过页面筛选后导出。系统不接入支付 SDK,因此“已缴费”表示管理员已登记确认,不代表平台完成了在线支付交易。
## 6. 考试与科目
超级管理员可配置:
- 考试代码、名称和状态;
- 报名、考试、准考证、成绩等时间;
- 科目日期、开始结束时间;
- 科目费用和满分;
- 固定单科线、排名前百分比或不设单科线;
- 固定总分线、总排名前百分比、单科均达线或不判定整场合格。
默认成绩等级按同场同科排名百分位计算:
| 区间 | 等级 |
| --- | --- |
| 前 10% | A+ |
| 前 25% | A |
| 前 50% | B+ |
| 前 70% | B |
| 前 90% | C |
| 其余 | D |
同分共享名次。
### 考试归档
归档是整场考试的不可逆业务动作。归档后锁定:
- 手工成绩录入;
- 成绩 Excel 导入;
- 复议改分;
- 考试配置修改。
归档前应确认报名、编排、成绩、复议、发布和必要导出均已完成。历史报名、准考证与成绩在页面中默认折叠展示。
## 7. 考点、考场与准考证编排
考点档案包括:
- 代码、名称和状态;
- 负责人、应急电话;
- 开放时间、交通说明;
- 楼栋、楼层;
- 考场容量、类型;
- 座位编排说明。
考点新增以及考点、考场修改会先形成申请快照,审批通过后再整体更新正式档案。
### 批量编排
超级管理员选择整场考试后:
1. 选择混编范围:班内、校内、县区内、市内或省内。
2. 选择准考证号码规则。
3. 设置是否保留备用考场和稳定随机种子。
4. 先执行预检。
5. 处理所有阻断问题。
6. 再确认正式编排。
预检检查:
- 科目时间冲突;
- 考点与考场容量;
- 档案完整性;
- 准考证号码唯一性;
- 多科考生的考点一致性。
预置号码规则包括:
- 县区编号 + 考场号 + 座位号;
- 县区号 + 考场号 + 流水号;
- 考点学校代码 + 考场号 + 座位号;
- 考生学校代码 + 考场号 + 座位号。
预检只用于验证,不应写入正式编排结果;正式应用后再开放准考证下载。
## 8. 成绩管理
成绩管理中心按考试切换,提供:
- 录入和发布进度;
- 成绩出齐人数;
- 整场合格率;
- 缺失科次;
- 复议数量;
- 可筛选成绩台账;
- Excel 导入导出;
- 缓存刷新。
### Excel 导入
成绩导入采用两阶段流程:
1. 上传并逐行校验;
2. 页面暂存预览;
3. 管理员核对错误、人数和变更内容;
4. 明确确认;
5. 在事务内原子批量写库。
预览不会修改数据库。不要把“解析成功”误认为“导入完成”。
### 成绩复议
终审时查看考试、科目、原分、当前分、当前排名和达线规则。批准改分后系统更新成绩并重新计算受影响结论。考试归档后不能批准会造成改分的操作。
### 发布与缓存
成绩录入不等于发布。发布、批量导入、归档和复议改分会自动使相关结果缓存失效;超级管理员也可在成绩管理中心手动刷新全部成绩缓存。
## 9. 通知与系统公示
超级管理员可:
- 新建草稿;
- 编辑内容;
- 发布;
- 撤回;
- 设置首页置顶;
- 管理系统生成的招生公示可见性。
系统公示和人工公告来源不同。撤回、隐藏或重新公开后,应以公开首页实际显示结果为准。
招生流程会按阶段生成:
- 招生计划公示;
- 指标资格公示;
- 录取与补录公示;
- 报到情况公示;
- 脱敏录取名单;
- 按学校、类别统计的录取分数线。
## 10. 招生录取全流程
### 录取设置
超级管理员按考试配置:
- 是否启用志愿;
- 填报起止时间;
- 普通志愿数量;
- 最多提交次数;
- 当前阶段。
### 招生账户和计划
超级管理员为招生学校建立专用账号。招生学校以结构化表单提交普通生、特长生和生源校指标计划;超级管理员审核后生效,也可代为上传并直接审核。
### 指标资格
生源校校级管理员按考试逐人确认本校考生指标资格。必须完成本校全部资料已完善在册考生的确认后,系统才公开资格结果。超级管理员和班级管理员不能代替校级管理员完成此确认。
### 志愿和投档
填报结束后,超级管理员执行投档。系统按总成绩降序逐人检索志愿,区分指标与普通计划池。
投档材料只发送给对应招生学校,包含必要个人资料和当次成绩,不包含其他志愿。
### 招生学校审核
招生学校可:
- 接收投档;
- 填写特殊理由申请退档;
- 查看本校计划录取率;
- 维护录取通知书标题、正文、落款和配色。
退档由超级管理员统一审批。
### 正式录取
超级管理员签发正式录取后,系统按“招生学校代码 + 考试代码 + 校内独立流水号”生成稳定通知书编号,并开启招生学校报到工作台。
### 报到与补录
招生学校可以:
- 逐人暂存 `Y``N``P` 报到状态;
- 导出带下拉校验的 Excel
- 修改后导入;
- 扫描通知书二维码预检;
- 确认扫描后登记;
- 提交完整报到情况;
- 选择不补录或申请补录。
扫描预览只做核验,不写入报到状态;正式扫描确认才产生业务变更。学校提交前的草稿不会进入超级管理员审批。
超级管理员审批各学校决定后,按缺额进入下一轮补录或结束录取。每轮结果都应独立保留和发布,不能只保留最终总状态。
## 11. Excel 操作建议
系统为班级、管理员、报名号、考生、缴费、考点考场、成绩、志愿、录取和报到等业务提供模板、导入与导出。
操作前:
1. 从当前页面下载最新模板,不复用旧版本模板。
2. 保留隐藏标识列和表头。
3. 不随意修改单元格数据验证。
4. 使用页面提供的当前范围导出作为修改基础。
5. 导入后逐条检查错误行和预览摘要。
6. 只有确认目标范围正确后才提交写入。
只读角色可能仍保留导出能力,但不能通过修改请求完成写操作。
## 12. 审计与操作规范
- 每名管理员使用自己的账号。
- 重要审批意见说明事实和原因。
- 批量操作前先筛选并核对选中范围。
- 不通过数据库直接修改业务状态。
- 数据导出按敏感文件管理,使用后及时清理。
- 归档、正式录取、批量导入、重建数据库等高影响操作执行前保留备份或导出。
- 发现登录过期时重新登录,不要求使用者靠刷新页面恢复会话。
+297
@@ -0,0 +1,297 @@
# 系统架构
衡准考试信息管理系统采用 ASP.NET Core 10 模块化单体架构。生产环境部署一个 `Eis.Web` 进程,前端静态资源和 API 由同一宿主提供;数据库维护工具 `Eis.Tools` 独立发布。
## 1. 总体结构
```text
浏览器
├─ Vue 3 单页应用
├─ 公开页面 / 考生中心 / 管理后台 / 招生学校端
└─ PDF 文书生成模块
│ HTTPS / JSON / Cookie
Eis.WebASP.NET Core
├─ 静态资源与 SPA History 回退
├─ Minimal API 端点
├─ 身份识别、角色与数据范围校验
└─ 健康检查
Eis.Application
└─ 应用服务契约
Eis.Infrastructure
├─ 业务服务与仓储
├─ SQLite / MySQL
├─ Redis / 本机缓存
├─ Redis / 内存认证状态
├─ ClosedXML
└─ 安全与防伪服务
```
## 2. 项目依赖
### Eis.Domain
领域层保存稳定业务概念和值,不包含 Web、数据库或前端细节。
### Eis.Application
应用层以接口描述用例,包括:
- 认证;
- 考生服务;
- 公开查询;
- 管理中心;
- 组织、账号、配置;
- 考试、编排、成绩;
- 审批、招生和录取。
端点依赖这些契约,而不是直接依赖数据库实现。
### Eis.Infrastructure
基础设施层实现:
- 关系数据库连接和初始化;
- 管理员、考生、招生业务仓储;
- 缓存和会话状态;
- 密码、TOTP 和文书防伪;
- Excel 读写;
- 准考证、成绩单和录取通知书数据;
- 审批流、统计和投档业务。
部分大型业务通过同名 partial 类拆分文件,例如考生服务和招生服务,以保持单个文件可维护。
### Eis.Web
Web 层负责:
- 加载 `.env` 和运行环境;
- 组合依赖注入;
- 初始化数据库;
- HTTP 输入输出;
- 获取当前身份;
- 注册安全响应头;
- 健康检查;
- 静态资源;
- SPA 路由回退。
复杂业务规则应留在 Infrastructure 的应用实现中,而不是放在端点委托。
### Eis.Tools
数据库工具复用 Infrastructure 的数据库选项、初始化和维护服务,提供:
- `database init`
- `database reset`
- `database seed`
工具可以和 Web 一起发布,但执行时不要求 Web 正在运行。SQLite 数据维护时反而应先停止 Web,避免并发写入。
## 3. HTTP 与前端
### API 域
| 前缀 | 面向对象 |
| --- | --- |
| `/api/public` | 未登录访客 |
| `/api/auth` | 注册、登录、密码、TOTP、退出 |
| `/api/candidate` | 当前考生 |
| `/api/admin` | 超级、校级、班级管理员 |
| `/api/admission` | 当前招生学校 |
未注册的 `/api/*` 由统一兜底端点返回 JSON 404,不会回退成 HTML,也不会转发到旧 Node.js 服务。
### SPA 路由
前端使用 Vue Router History 模式:
- `/candidate/...`
- `/admin/...`
- `/admission/...`
开发时由 Vite 提供页面并代理 API;生产时 ASP.NET Core 提供 `wwwroot/vue-app`,对非 API 语义 URL 返回前端入口。
### 会话初始化
前端进入路由前加载当前会话:
1. 未登录访问受保护页面:跳转登录页并携带返回地址。
2. 已登录但角色不匹配:返回该角色首页。
3. 考生必须改初始密码或资料未完成:跳转首次登录流程。
4. 会话失效:重新登录,而不是假定刷新可恢复。
## 4. 认证与授权
### 凭据
- 密码使用 PBKDF2 加盐哈希。
- TOTP 密钥使用 `TOTP_ENCRYPTION_KEY` 派生的材料,以 AES-256-GCM 加密保存。
- 恢复码只保存带服务端密钥的哈希。
- 登录状态通过 HttpOnly、SameSite Cookie 标识。
### 状态存储
未配置 Redis时:
- 登录 Session:进程内内存;
- TOTP 登录挑战:进程内内存;
- TOTP 绑定临时状态:进程内内存;
- 进程重启后需要重新登录。
配置 Redis 时:
- 普通缓存通常使用 DB 0
- 认证状态默认自动使用 DB 1
- 也可用 `REDIS_SESSION_URL` 指向独立 Redis
- 系统拒绝让普通缓存和认证状态使用同一端点的同一逻辑 DB。
明确配置的认证 Redis 连接失败会阻止服务启动,避免多实例部署时静默退回本机内存。
### 数据范围
授权是多维度的:
```text
角色
└─ 管理级别
└─ 学校
└─ 班级
└─ 具体业务状态
```
招生学校角色另按自己的学校 ID 限制计划、投档和报到数据。投档材料不会包含考生的其他志愿。
## 5. 数据持久化
### 双数据库适配
- SQLite:本地开发、自动化测试、单机和默认 Docker 部署。
- MySQL 8.4:生产、多实例或独立数据库部署。
两种数据库共享同一业务服务,并分别使用内嵌 schema SQL。数据库结构版本记录在 `schema_metadata`,当前为 v20。
### 总表与专属分表
系统采用“总表索引 + 独立物理分表”:
- 创建考试时建立:
- `exam_<partition>_candidates`
- `exam_<partition>_admissions`
- `exam_<partition>_results`
- `exam_<partition>_centers`
- 创建学校时建立:
- `school_<partition>_students`
分区键由业务 ID 的 SHA-256 摘要生成,不直接拼接用户输入。总表继续承担跨考试、跨学校查询和外键完整性,专属表支持按业务对象组织数据。
### 事务与审计
以下操作强调原子性:
- 报名号批量生成;
- 批量成绩确认写入;
- 成绩复议改分和相关结论;
- 招生投档与正式录取;
- 业务写入和审计日志。
批量操作失败时应整体回滚,避免半批状态。预览类操作不得开启正式写入。
## 6. 数据库状态快照
关系表读取会在进程内复用只读快照,避免每个请求重复扫描和转换全部业务数据。
- 应用自身写入:立即使快照失效。
- SQLite:通过 `PRAGMA data_version` 识别其他连接的提交。
- MySQL 外部直写:默认最多延迟 `DATABASE_STATE_CACHE_TTL_MS` 后可见。
因此不建议绕过应用直接写数据库。除审计缺失外,外部直写还可能造成短时间读到旧快照。
## 7. 应用缓存
缓存命名空间:
- `public`:首页和已发布公告;
- `results`:考生已发布成绩。
特性:
- Redis 可选;
- Redis 不可用时回退到有界本机缓存;
- 热点请求合并,避免同一 key 并发回源;
- 通过命名空间版本做批量失效;
- 写入后主动失效。
公开缓存默认 60 秒,成绩缓存默认 24 小时。本机缓存默认最多 200 条。
## 8. Excel 和 PDF
### Excel
ClosedXML 在服务端生成和解析工作簿。导入一般遵循:
```text
下载模板
→ 填写
→ 上传
→ 逐行校验
→ 页面预览
→ 明确确认
→ 事务写入
```
这保证文件解析错误不会产生部分业务写入。
### PDF 文书
成绩单和录取通知书在浏览器端下载 PDF,数据由服务端按当前身份和发布状态提供。文书包含:
- 防伪查询码;
- 二维码;
- 公开验真地址;
- HMAC 签名。
验真服务只返回允许公开核对的信息。
## 9. 健康检查
### `/health/live`
用于容器和进程存活检查,返回服务名和框架。
### `/health/migration`
用于诊断:
- 旧 API 已移除;
- 原生认证、考生和管理模块状态;
- 缓存 `ready``disabled``unavailable`
- 认证状态使用 `redis``memory`
- 当前启用的原生能力。
该路径名保留了迁移阶段语义,但当前系统已经完全运行于 ASP.NET Core。
## 10. 部署模型
### 单机
```text
Eis.Web + SQLite + 内存会话/缓存
```
适合开发、演示和低并发单实例。
### 生产
```text
一个或多个 Eis.Web
├─ MySQL 8.4
├─ Redis 缓存 DB
└─ Redis 认证状态 DB/实例
```
多实例必须共享认证状态。静态资源包含在每个 Web 发布物中,不需要独立 Node.js 服务。
+228
@@ -0,0 +1,228 @@
# 配置参考
应用从进程环境变量和仓库根目录 `.env` 读取配置。进程环境变量优先:如果同名变量已经由操作系统、容器或部署平台注入,`.env` 不会覆盖它。
## 1. 配置文件
本地开发:
```powershell
Copy-Item -LiteralPath .\.env.example -Destination .\.env
```
Docker Compose
```powershell
Copy-Item -LiteralPath .\.env.docker.example -Destination .\.env.docker
```
`.env` 支持:
- 空行;
- `#` 注释;
- `KEY=value`
- 可选 `export KEY=value`
- 单引号或双引号包裹的完整值。
修改 `.env` 后需要重启应用。
## 2. 基础运行配置
| 变量 | 默认/示例 | 说明 |
| --- | --- | --- |
| `ASPNETCORE_ENVIRONMENT` | 本地 `Development` | `Production` 会启用生产密钥和数据库要求 |
| `ASPNETCORE_URLS` | `http://127.0.0.1:4173` | Kestrel 监听地址 |
| `DATABASE_CLIENT` | 开发 `sqlite`,生产 `mysql` | 仅支持 `sqlite``mysql` |
如果直接设置 `ASPNETCORE_ENVIRONMENT=Production` 而没有配置 MySQL 和两个生产密钥,应用会拒绝启动,这是预期的安全行为。
## 3. SQLite
| 变量 | 默认值 | 说明 |
| --- | --- | --- |
| `SQLITE_PATH` | `./data/exam.sqlite` | 相对路径按应用根目录解析,也可使用绝对路径 |
示例:
```powershell
$env:DATABASE_CLIENT = 'sqlite'
$env:SQLITE_PATH = './data/exam.sqlite'
dotnet run --project .\src\Eis.Web\Eis.Web.csproj
```
## 4. MySQL 8.4
可以使用分项变量:
| 变量 | 必填 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `MYSQL_HOST` | 是 | 无 | MySQL 主机 |
| `MYSQL_PORT` | 否 | `3306` | 端口 |
| `MYSQL_USER` | 是 | 无 | 应用账号 |
| `MYSQL_PASSWORD` | 视账号而定 | 空 | 密码 |
| `MYSQL_DATABASE` | 是 | 无 | 数据库名 |
| `MYSQL_CONNECTION_LIMIT` | 否 | `10` | 连接池最大连接数 |
也可以只设置:
| 变量 | 示例 |
| --- | --- |
| `DATABASE_URL` | `mysql://exam_app:password@127.0.0.1:3306/exam_information` |
`DATABASE_URL` 优先于全部 `MYSQL_*` 连接项。用户名和密码含特殊字符时必须按 URL 规则编码。
连接默认:
- 字符集 `utf8mb4`
- 连接超时 10 秒;
- 命令超时 30 秒;
- 启用连接池和连接重置。
## 5. 初始管理员
这些变量只在创建空数据库的初始管理员时使用:
| 变量 | 本地模板 | 说明 |
| --- | --- | --- |
| `INITIAL_ADMIN_USERNAME` | `admin` | 初始超级管理员账号 |
| `INITIAL_ADMIN_PASSWORD` | `Admin123!` | 初始密码,生产必须更换 |
| `INITIAL_ADMIN_DISPLAY_NAME` | `系统管理员` | 显示名 |
修改这些变量不会自动修改已经存在的管理员。已有账号应通过系统的密码修改或管理员重置功能维护。
## 6. TOTP 与文书防伪
| 变量 | 生产要求 | 说明 |
| --- | --- | --- |
| `TOTP_ENCRYPTION_KEY` | 至少 32 个字符 | 加密 TOTP 密钥并保护恢复码哈希 |
| `DOCUMENT_VERIFICATION_SECRET` | 至少 32 个字符 | 对成绩单和录取通知书防伪载荷签名 |
要求:
- 两个值相互独立;
- 不与数据库密码、Cookie 或其他系统密钥共用;
- 由秘密管理平台或部署平台注入;
- 部署后稳定保存;
- 不提交到 Git。
影响:
- 更换 `TOTP_ENCRYPTION_KEY`:已绑定 TOTP 可能无法解密。
- 更换 `DOCUMENT_VERIFICATION_SECRET`:历史文书查询码会失效。
开发环境未配置时使用仅供开发的稳定派生值。`DOCUMENT_VERIFICATION_SECRET` 未设置时还兼容读取 `SESSION_SECRET`,但新部署应使用独立变量,不依赖兼容路径。
## 7. Redis 普通缓存
| 变量 | 默认值 | 说明 |
| --- | --- | --- |
| `REDIS_URL` | 未配置 | `redis://``rediss://` 地址,可在路径指定逻辑 DB |
| `REDIS_CACHE_PREFIX` | `exam-information` | 缓存 key 前缀 |
| `REDIS_CACHE_TTL_SECONDS` | `60` | 公开数据缓存秒数,最大 86400 |
| `REDIS_RESULTS_CACHE_TTL_SECONDS` | `86400` | 已发布成绩缓存秒数 |
| `REDIS_CONNECT_TIMEOUT_MS` | `1500` | Redis 连接超时,最大 30000 |
| `LOCAL_CACHE_MAX_ENTRIES` | `200` | 本机回退缓存上限,最大 5000 |
普通缓存 Redis 暂时不可用时,应用回退到本机缓存,并保持写后失效语义。
## 8. Redis 认证状态
| 变量 | 默认值 | 说明 |
| --- | --- | --- |
| `REDIS_SESSION_URL` | 复用 `REDIS_URL` 端点 | 可指定独立 Redis |
| `REDIS_SESSION_DB` | 缓存 DB 为 0 时自动选 1,否则选 0 | 认证状态逻辑 DB |
| `REDIS_SESSION_PREFIX` | `exam-information:auth` | Session 和临时状态 key 前缀 |
| `AUTH_SESSION_TTL_SECONDS` | `28800` | 登录 Session,默认 8 小时,最大 30 天 |
| `AUTH_LOGIN_CHALLENGE_TTL_SECONDS` | `300` | TOTP 登录挑战,最大 1 小时 |
| `AUTH_TOTP_SETUP_TTL_SECONDS` | `600` | TOTP 绑定临时状态,最大 1 小时 |
普通缓存和认证状态不得使用同一 Redis 端点的同一逻辑 DB。错误配置时应用拒绝启动。
示例:
```text
REDIS_URL=redis://127.0.0.1:6379/0
REDIS_SESSION_DB=1
```
独立实例:
```text
REDIS_URL=rediss://cache.example.com:6379/0
REDIS_SESSION_URL=rediss://session.example.com:6379/0
```
Redis Cluster 通常不支持非 0 逻辑 DB,此时应使用 `REDIS_SESSION_URL` 指向独立端点或实例。
## 9. 数据状态快照
| 变量 | 默认值 | 说明 |
| --- | --- | --- |
| `DATABASE_STATE_CACHE_TTL_MS` | `30000` | MySQL 外部直写后,全量只读快照最长复用毫秒数 |
应用内写入会立即失效;SQLite 还会通过 `PRAGMA data_version` 识别外部连接提交。
## 10. 公开站点信息
| 变量 | 用途 |
| --- | --- |
| `PUBLIC_SITE_NAME` | 机构名称 |
| `PUBLIC_SITE_CODE` | 机构代码 |
| `PUBLIC_SITE_PHONE` | 联系电话 |
| `PUBLIC_SITE_ADDRESS` | 地址 |
| `PUBLIC_SITE_EMAIL` | 邮箱 |
| `PUBLIC_SITE_HERO_EYEBROW` | 首页英文眉题 |
| `PUBLIC_SITE_HERO_TITLE` | 首页主标题前半段 |
| `PUBLIC_SITE_HERO_HIGHLIGHT` | 首页主标题强调段 |
| `PUBLIC_SITE_HERO_DESCRIPTION` | 首页说明 |
| `PUBLIC_SITE_FOOTER_NOTICE` | 页脚提示 |
这些变量会覆盖数据库中的演示机构展示信息,只通过公开首页接口返回非敏感字段。
## 11. 兼容迁移开关
源码仍可读取 `AUTH_NATIVE_ENABLED``CANDIDATE_NATIVE_ENABLED` 和若干 `ADMIN_NATIVE_*` 变量,但当前 `appsettings.json` 已默认启用全部 ASP.NET Core 原生域,旧 Node.js API 已移除。
正常部署不需要设置这些变量。除非正在调试迁移兼容行为,不应把它们加入新环境模板。
## 12. 生产配置示例
以下只展示结构,密钥和密码必须替换:
```text
ASPNETCORE_ENVIRONMENT=Production
ASPNETCORE_URLS=http://0.0.0.0:4173
DATABASE_CLIENT=mysql
MYSQL_HOST=mysql.internal
MYSQL_PORT=3306
MYSQL_USER=exam_app
MYSQL_PASSWORD=replace-me
MYSQL_DATABASE=exam_information
MYSQL_CONNECTION_LIMIT=20
REDIS_URL=rediss://cache.internal:6379/0
REDIS_SESSION_URL=rediss://session.internal:6379/0
REDIS_CACHE_PREFIX=exam-information
REDIS_SESSION_PREFIX=exam-information:auth
TOTP_ENCRYPTION_KEY=replace-with-a-unique-secret-at-least-32-characters
DOCUMENT_VERIFICATION_SECRET=replace-with-another-unique-secret-at-least-32-characters
INITIAL_ADMIN_USERNAME=admin
INITIAL_ADMIN_PASSWORD=replace-with-a-strong-initial-password
INITIAL_ADMIN_DISPLAY_NAME=系统管理员
```
## 13. 上线检查
- `ASPNETCORE_ENVIRONMENT=Production`
- 数据库目标明确,不是测试库或旧 SQLite 文件。
- 两项 32 字符以上的独立密钥已由秘密管理注入。
- 初始管理员密码已更换。
- Redis 缓存与认证状态未使用同一逻辑 DB。
- 多实例已使用共享认证 Redis。
- `.env``.env.docker` 未提交到版本库。
- 公开机构信息已改成真实信息。
- `/health/live``/health/migration` 可由运维系统检查。
+344
@@ -0,0 +1,344 @@
# 数据库与数据工具
本页包含会修改数据的命令。执行前先确认目标数据库、停止应用并完成备份。示例命令不会在阅读 Wiki 时自动执行。
## 1. 支持的数据库
| 数据库 | 推荐场景 | 特点 |
| --- | --- | --- |
| SQLite | 本地开发、测试、演示、单机部署 | 单文件、零外部依赖 |
| MySQL 8.4 | 生产、多实例、集中运维 | 独立服务、连接池、并发能力更强 |
应用启动时会初始化空库结构和基础配置,但不会自动导入学校、考生、考试或报名等演示业务数据。
当前结构版本是 v20。已有数据库版本低于 v20 时,应用会拒绝直接运行并要求先备份和执行明确的升级流程。
## 2. 数据库目标识别
应用根目录通常是包含 `Eis.slnx``.env` 的目录。
SQLite
- 默认:`<应用根目录>/data/exam.sqlite`
- `SQLITE_PATH` 相对路径:按应用根目录解析
- `--path`:数据库工具命令的显式 SQLite 目标
MySQL
- `DATABASE_URL` 优先;
- 否则读取 `MYSQL_HOST``MYSQL_PORT``MYSQL_USER``MYSQL_PASSWORD``MYSQL_DATABASE`
- 数据库工具确认目标时使用数据库名,不是主机名。
执行任何写操作前,先记录工具输出中的“目标”一行。
## 3. 建立 MySQL 数据库和账号
示例:
```sql
CREATE DATABASE exam_information
CHARACTER SET utf8mb4
COLLATE utf8mb4_0900_ai_ci;
CREATE USER 'exam_app'@'%'
IDENTIFIED BY 'replace-with-a-strong-password';
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, ALTER
ON exam_information.*
TO 'exam_app'@'%';
```
应用需要创建和演进表结构,因此账号除了数据读写外还需要 `CREATE``ALTER`。不要授予 MySQL 全局管理员权限。
配置方式见[配置参考](Configuration)。
## 4. 数据库结构
主要关系表包括:
- 组织:`organization``schools``school_classes`
- 用户:`users``candidate_profiles`
- 考试:`exams``exam_subjects`
- 报名:`registrations``registration_subjects`
- 编排:`test_centers``test_rooms``exam_arrangement_plans``admit_cards``admit_card_subjects`
- 成绩:`results`
- 流程:`workflow_definitions``workflow_steps``workflow_instances``workflow_actions`
- 报名号:`number_rules``number_rule_segments``candidate_account_batches``candidate_account_batch_items`
- 招生:招生计划、志愿、投档、录取、报到及相关记录
- 公告与审计:`notices``audit_logs`
- 分表登记:`exam_data_partitions``school_student_partitions`
- 版本:`schema_metadata`
系统还为每场考试和每所学校维护专属物理分表。不要只备份总表或只复制少数业务表。
## 5. 获取数据库工具
统一发布:
```powershell
pwsh.exe -NoLogo -NoProfile -NonInteractive -File .\scripts\publish.ps1
```
工具位于:
```text
artifacts/publish/tools/Eis.Tools.dll
```
查看帮助:
```powershell
dotnet .\artifacts\publish\tools\Eis.Tools.dll --help
```
源码开发时也可以:
```powershell
dotnet run --project .\src\Eis.Tools\Eis.Tools.csproj -- --help
```
## 6. 命令概览
```text
database init 非破坏性建表和基础配置初始化
database reset 重建为空业务库
database seed 导入内置 1200 名考生演示数据
```
通用参数:
| 参数 | 说明 |
| --- | --- |
| `--sqlite` | 明确使用 SQLite |
| `--mysql` | 明确使用 MySQL |
| `--path <文件>` | SQLite 文件路径,只能与 `--sqlite` 一起使用 |
| `--confirm-target <目标>` | 对破坏性命令确认最终目标 |
| `--force` | 仅 MySQL;允许覆盖无法识别为内置演示数据的业务数据 |
| `--dry-run` | 只预检,不写入 |
| `--root <目录>` | 指定 `.env` 和相对 SQLite 路径的应用根目录 |
`--sqlite``--mysql` 不能同时使用。`database init` 不接受 `--force`
## 7. 初始化空库
### SQLite
```powershell
dotnet .\artifacts\publish\tools\Eis.Tools.dll database init `
--sqlite `
--path .\data\exam.sqlite
```
### MySQL
先配置 `.env` 或进程环境变量,然后:
```powershell
dotnet .\artifacts\publish\tools\Eis.Tools.dll database init --mysql
```
`init` 是非破坏性操作,用于建表和基础配置。它不会导入演示学校、考生或考试。
## 8. 导入演示数据
演示数据内置于 `Eis.Infrastructure.dll`,生产服务器不需要 Node.js。
### SQLite 安全流程
第一步,只预检:
```powershell
dotnet .\artifacts\publish\tools\Eis.Tools.dll database seed `
--sqlite `
--path .\data\demo.sqlite `
--dry-run
```
第二步,核对工具显示的绝对目标路径。
第三步,明确确认同一目标:
```powershell
dotnet .\artifacts\publish\tools\Eis.Tools.dll database seed `
--sqlite `
--path .\data\demo.sqlite `
--confirm-target .\data\demo.sqlite
```
如果目标文件已存在,工具先复制为:
```text
<数据库文件>.backup-<UTC时间戳>
```
### MySQL 测试库安全流程
第一步:
```powershell
dotnet .\artifacts\publish\tools\Eis.Tools.dll database seed --mysql --dry-run
```
确认输出的数据库名确实是测试库,例如 `exam_test`
第二步:
```powershell
dotnet .\artifacts\publish\tools\Eis.Tools.dll database seed `
--mysql `
--confirm-target exam_test
```
如果工具发现无法识别为内置演示数据的业务记录,会拒绝覆盖。只有在已经备份且确认这个测试库可以完全覆盖时才使用:
```powershell
dotnet .\artifacts\publish\tools\Eis.Tools.dll database seed `
--mysql `
--confirm-target exam_test `
--force
```
`--force` 不会删除数据库本身,但会改写其中的应用数据。不得对生产业务库使用。
## 9. 演示账号
演示数据中的预置账号密码统一为 `12345678`
| 角色 | 账号 |
| --- | --- |
| 超级管理员 | `admin` |
| 超级管理员(监督演示) | `supervisor` |
| 校级管理员 | `school_admin` |
| 同校校级管理员 | `school_admin_2` |
| 班级管理员 | `class_admin` |
| 同班班级管理员 | `class_admin_2` |
| 考生 | `2026-HZ01-F-0001` |
这些账号只存在于演示数据。正常启动空库时不会创建校级、班级或考生样例账号。
## 10. 测试结束后重建空系统
### SQLite
先停止应用:
```powershell
dotnet .\artifacts\publish\tools\Eis.Tools.dll database reset `
--sqlite `
--path .\data\exam.sqlite `
--confirm-target .\data\exam.sqlite
```
工具自动备份原 SQLite 文件,然后建立 v20 空业务系统和初始超级管理员。
### MySQL
先预检:
```powershell
dotnet .\artifacts\publish\tools\Eis.Tools.dll database reset --mysql --dry-run
```
再确认测试数据库名:
```powershell
dotnet .\artifacts\publish\tools\Eis.Tools.dll database reset `
--mysql `
--confirm-target exam_test
```
目标包含非演示业务数据时会拒绝。即使使用 `--force`,也应先完成 MySQL 原生备份。
## 11. Docker 中维护 SQLite
Compose 默认数据库路径是 `/app/data/exam.sqlite`,保存在命名卷 `exam-information-data`
先停止 Web
```powershell
docker compose stop app
```
再运行工具,目标和确认路径必须完全一致:
```powershell
docker compose run --rm --entrypoint dotnet app `
/app/tools/Eis.Tools.dll database seed `
--sqlite `
--path /app/data/exam.sqlite `
--confirm-target /app/data/exam.sqlite
```
完成后启动:
```powershell
docker compose up --detach app
```
不要对容器内另一个临时路径执行 seed 后误以为已经修改命名卷数据库。
## 12. 备份与恢复建议
### SQLite
维护前:
1. 停止所有使用该文件的 Web 和工具进程。
2. 记录数据库绝对路径。
3. 复制数据库文件到独立备份目录。
4. 保留工具自动生成的时间戳备份。
5. 恢复时先停止应用,再用备份文件替换目标。
不要只复制 `-wal``-shm` 文件。应用运行时直接复制数据库可能得到不一致备份,应优先停机。
### MySQL
使用组织既有的 MySQL 备份方案,例如逻辑备份、快照或托管服务备份。至少验证:
- 备份包含全部表和结构;
- 恢复到隔离测试库成功;
- 字符集为 `utf8mb4`
- 应用账号权限仍正确;
- 恢复库结构版本是 v20。
## 13. 安全保护
数据库工具会:
- 要求破坏性命令明确 `--confirm-target`
- SQLite 比较最终绝对路径;
- MySQL 比较数据库名;
- 拒绝 `mysql``information_schema``performance_schema``sys` 等系统库;
- 校验演示数据和数据库结构版本;
- 默认拒绝覆盖未知 MySQL 业务数据;
- 为已有 SQLite 文件自动建立时间戳备份。
这些保护不能代替人工确认和外部备份。
## 14. 常见错误
### “这是破坏性操作”
缺少 `--confirm-target`,或确认值和最终目标不一致。重新查看工具输出,不要盲目复制旧命令。
### “数据库结构版本低于要求”
当前应用要求 v20。停止上线,保留完整备份,并执行针对旧版本的升级流程;不要用 `reset` 假装完成生产迁移。
### “MySQL 配置不完整”
设置完整 `DATABASE_URL`,或至少设置 `MYSQL_HOST``MYSQL_USER``MYSQL_DATABASE`
### “拒绝覆盖非演示业务数据”
目标中存在工具无法确认可覆盖的数据。先核对是否选错库。只有确认是可完全覆盖的测试库并已备份时才考虑 `--force`
### SQLite 文件没有变化
检查:
- `--path` 是否指向预期文件;
- 相对路径按哪个 `--root` 解析;
- 容器中是否使用 `/app/data/exam.sqlite`
- Web 是否仍在运行并占用或继续写另一个数据库。
+383
@@ -0,0 +1,383 @@
# 部署与发布
项目支持框架依赖发布、自包含跨平台程序包和 Docker 镜像。生产部署前先完成[配置参考](Configuration)中的上线检查。
## 1. 部署前准备
至少准备:
- 生产 MySQL 8.4 数据库及最小权限账号;
- 两项相互独立且至少 32 字符的密钥;
- 强初始管理员密码;
- HTTPS 入口或反向代理;
- 持久化日志收集方式;
- 数据库备份和恢复方案;
- 多实例部署所需 Redis
- 端口、防火墙和健康检查策略。
生产环境不需要 Node.js 运行服务。Node.js 只在构建 Vue 前端时使用。
## 2. 本机统一发布
在仓库根目录运行:
```powershell
pwsh.exe -NoLogo -NoProfile -NonInteractive -File .\scripts\publish.ps1
```
脚本会:
1.`ClientApp` 执行 `npm ci`
2. 构建 Vue 生产资源;
3. 发布 `Eis.Web`
4. 发布 `Eis.Tools`
输出:
```text
artifacts/publish/web
artifacts/publish/tools
```
启动 Web
```powershell
dotnet .\artifacts\publish\web\Eis.Web.dll
```
查看数据库工具:
```powershell
dotnet .\artifacts\publish\tools\Eis.Tools.dll --help
```
这种发布方式需要服务器预装兼容的 .NET 10 Runtime。
## 3. 发布物目录建议
生产服务器可采用:
```text
eis/
├─ current/ 当前 Web 发布物
├─ tools/ 与当前版本匹配的数据库工具
├─ config/ 由平台管理,不纳入发布压缩包
├─ data/ 仅 SQLite 部署使用
├─ logs/ 若采用文件日志
└─ releases/
├─ 1.2.2/
└─ 1.2.3/
```
配置和持久化数据不要放进每次覆盖的应用发布目录。升级前保留上一版发布物,以便代码回滚;数据库发生不兼容升级时,还必须配套数据库恢复方案。
## 4. 自包含跨平台程序包
Gitea Actions 默认构建:
- `win-x64``.zip`
- `linux-x64``.tar.gz`
- `linux-arm64``.tar.gz`
手动选择扩展平台后增加:
- `win-arm64`
- `osx-x64`
- `osx-arm64`
每个包包含:
```text
eis-<版本>-<RID>/
├─ app/ Web 与数据库工具,共享运行时和依赖
├─ README.md
├─ LICENSE
└─ .env.example
```
还会生成 `SHA256SUMS`
Windows 启动:
```powershell
.\app\Eis.Web.exe
```
Windows 数据库工具:
```powershell
.\app\Eis.Tools.exe --help
```
Linux/macOS
```text
./app/Eis.Web
./app/Eis.Tools --help
```
自包含包不要求目标机预装 .NET Runtime。
## 5. Docker 镜像
本地构建:
```powershell
docker build --tag hengzhun-exam-system:local .
```
镜像:
- 基于 ASP.NET Core 10 Runtime
- 包含 Web 和 `/app/tools/Eis.Tools.dll`
- 默认监听 `0.0.0.0:4173`
- 使用非 root 用户;
- 数据目录 `/app/data`
- 通过 `/health/live` 健康检查。
### Docker Compose
创建配置:
```powershell
Copy-Item -LiteralPath .\.env.docker.example -Destination .\.env.docker
```
替换密钥和管理员密码后:
```powershell
docker compose up --build --detach
docker compose ps
docker compose logs --follow app
```
默认使用 SQLite,数据库位于命名卷:
```text
exam-information-data:/app/data
```
停止并保留数据:
```powershell
docker compose down
```
删除容器和全部命名卷数据:
```powershell
docker compose down --volumes
```
最后一个命令具有破坏性,只在明确清空 SQLite 数据时使用。
## 6. MySQL 和 Redis 容器部署
使用外部 MySQL 时,以环境变量覆盖 Compose 中的默认 SQLite 配置:
```text
DATABASE_CLIENT=mysql
DATABASE_URL=mysql://...
```
然后可以移除 SQLite 数据卷挂载,但应先确认没有需要迁移的现有 SQLite 数据。
使用 Redis
```text
REDIS_URL=redis://redis-host:6379/0
REDIS_SESSION_DB=1
```
容器中的 `127.0.0.1` 指向容器自身。MySQL 或 Redis 在其他容器或主机上时,应使用 Compose 服务名、内网 DNS 或实际主机地址。
## 7. 生产启动顺序
推荐:
1. 完成数据库备份。
2. 部署新版本到独立目录或拉取新镜像。
3. 使用与新版本匹配的工具执行非破坏性数据库预检或初始化。
4. 注入生产环境变量。
5. 单实例启动新版本。
6. 检查 `/health/live``/health/migration`
7. 检查首页、登录、静态资源和数据库连接。
8. 多实例部署时再逐步替换其余实例。
9. 完成关键业务烟测。
10. 保留上一版本和回滚记录。
若结构版本不匹配,不要继续滚动启动所有实例。
## 8. 健康检查与监控
### 存活
```text
GET /health/live
```
正常响应包含:
```json
{
"status": "healthy",
"service": "Eis.Web",
"framework": ".NET 10"
}
```
### 组件状态
```text
GET /health/migration
```
重点检查:
- `legacyApiRemoved: true`
- `cache.status`
- `authentication.stateBackend`
- 原生模块启用状态
`cache.status=unavailable` 表示普通 Redis 不可用且正在使用本机回退;认证 Redis 如果明确配置却连接失败,应用通常无法完成启动。
监控还应包含:
- HTTP 5xx
- 登录失败率;
- 数据库连接和慢查询;
- Redis 连接;
- 容器重启次数;
- 磁盘或 SQLite 卷容量;
- 关键批量任务和审批错误。
## 9. HTTPS 与代理
建议在受管入口、反向代理或负载均衡器终止 TLS,只对外提供 HTTPS。代理配置需要:
- 转发到应用 `4173`
- 保留必要的 Host 和转发协议头;
- 允许 Excel/PDF 所需响应大小;
- 对 Vue History 路由使用应用自己的回退,不把 `/api/*` 错误改写成首页;
- 对登录 Cookie 保持同站点访问模型。
代理和应用的公开域名变化后,检查文书二维码中的验真链接是否指向正确外部地址。
## 10. Gitea Actions 发布
工作流:
```text
.gitea/workflows/publish.yml
```
触发方式:
- 推送 `v*` 标签;
- Actions 页面手动运行;
- 普通分支推送不会触发耗时发布。
流水线:
1. 构建 Vue
2. 运行 Release 配置的 .NET 测试;
3. 构建自包含平台包;
4. 上传工作流制品;
5. 构建 `linux/amd64``linux/arm64` Docker 镜像;
6. 推送 Docker Hub 和 Gitea Registry
7. 标签构建创建正式 Release 或 Pre-release。
### 必要配置
Actions Variables
- `DOCKERHUB_IMAGE`
- `REGISTRY_USERNAME`
Actions Secrets
- `DOCKERHUB_USERNAME`
- `DOCKERHUB_TOKEN`
- `REGISTRY_TOKEN`
Release 使用内置 `GITEA_TOKEN`,仓库或组织的任务令牌最大权限需要允许 Releases Write。
Gitea 镜像当前固定发布到:
```text
git.biss.click/biss/eis-dotnet
```
Docker Hub 镜像名由 `DOCKERHUB_IMAGE` 变量决定。
Runner 需要访问:
- Docker daemon
- GitHub Actions 源;
- Docker Hub
- `git.biss.click`
- QEMU 注册能力。
## 11. 版本标签
稳定版本:
```powershell
git tag v1.2.3
git push origin v1.2.3
```
会生成语义化镜像标签和 `latest`
预发布:
```powershell
git tag v1.3.0-rc.1
git push origin v1.3.0-rc.1
```
带连字符的版本会创建 Gitea Pre-release,不覆盖稳定版 `latest` 或主次版本标签。
发布标签前应确认标签所指提交已经通过本地检查,标签推送后不应移动同名标签来替换已发布制品。
## 12. 升级与回滚
### 升级
- 阅读版本变更;
- 备份数据库;
- 保存旧配置和密钥;
- 验证新版本所需环境变量;
- 用隔离数据库或测试环境演练;
- 部署并检查健康状态;
- 执行核心业务烟测。
### 代码回滚
若数据库结构仍兼容:
1. 停止新版本;
2. 恢复上一发布目录或镜像标签;
3. 使用原有配置启动;
4. 检查健康状态和关键业务。
### 数据回滚
如果新版本已执行不兼容数据库变更,不能只回滚二进制。必须按事先验证的数据库恢复方案恢复匹配版本的数据。
不要通过 `database reset` 实现生产回滚。
## 13. 部署后验收
- 首页和公告正常;
- 公开验真页可访问;
- 管理员可登录和退出;
- TOTP 登录可完成;
- 各角色只能看见自己的数据范围;
- Vue 语义 URL 直接刷新正常;
- Excel 模板可下载;
- PDF 文书可生成并验真;
- 数据写入后公开和成绩缓存及时失效;
- 数据库备份任务正常;
- 监控能发现进程、数据库和 Redis 故障。
+308
@@ -0,0 +1,308 @@
# 开发指南
本页说明本地开发、代码结构、前后端联调、验证和变更约定。首次启动请先完成[快速开始](Quick-Start)。
## 1. 技术栈与版本
| 领域 | 技术 |
| --- | --- |
| 运行时 | .NET 10 |
| Web | ASP.NET Core Minimal API |
| 前端 | Vue 3.5、Vue Router 5、Vite 8 |
| 数据访问 | Microsoft.Data.Sqlite、MySqlConnector |
| 缓存与认证状态 | StackExchange.Redis |
| Excel | ClosedXML |
| HTML 清理 | AngleSharp |
| 二维码 | QRCoder |
| 测试 | xUnit |
`Directory.Build.props` 全局启用:
- Nullable
- Implicit Usings
- 最新 C# 语言版本;
- 最新分析级别;
- 警告视为错误。
依赖版本集中在 `Directory.Packages.props` 管理。
## 2. 解决方案结构
```text
Eis.slnx
├─ src/Eis.Domain
├─ src/Eis.Application
├─ src/Eis.Infrastructure
├─ src/Eis.Tools
├─ src/Eis.Web
│ ├─ ClientApp
│ └─ wwwroot
└─ tests/Eis.Infrastructure.Tests
```
职责:
- `Eis.Domain`:领域值、跨层通用业务概念。
- `Eis.Application`:服务接口和应用层契约,不依赖 Web 页面。
- `Eis.Infrastructure`SQLite/MySQL、认证、缓存、Excel、PDF 数据、仓储和业务实现。
- `Eis.Tools`:数据库初始化、重建和演示数据命令行工具。
- `Eis.Web`:进程入口、依赖注入、HTTP 端点、健康检查和静态资源托管。
- `ClientApp`Vue 源码。
- `tests`:基础设施和核心业务自动化测试。
详细依赖关系见[系统架构](Architecture)。
## 3. 日常开发启动
终端一,启动 ASP.NET Core
```powershell
dotnet run --project .\src\Eis.Web\Eis.Web.csproj
```
终端二,启动 Vite
```powershell
Set-Location .\src\Eis.Web\ClientApp
npm run dev
```
访问 <http://127.0.0.1:5173>。Vite 将 API、健康检查和旧的浏览器 PDF 模块代理到 `4173`
仅修改后端且已经有前端生产构建时,可只访问 <http://127.0.0.1:4173>。
## 4. 前端开发
### 主要入口
| 路径 | 用途 |
| --- | --- |
| `src/main.js` | Vue 应用入口 |
| `src/router/index.js` | 公开、考生、管理和招生路由 |
| `src/stores/session.js` | 会话和用户上下文 |
| `src/lib/api.js` | API 请求与错误处理 |
| `src/lib/navigation.js` | 按角色和级别生成菜单 |
| `src/views` | 页面级视图 |
| `src/components` | 可复用业务组件 |
| `src/styles` | 全局和首页样式 |
### 路由约定
- 公开:`/``/announcements``/verify/:code``/auth/...`
- 考生:`/candidate/:page`
- 管理员:`/admin/:page`
- 招生学校:`/admission/:page`
新增受保护页面时,应同时考虑:
1. 路由的 `meta.roles`
2. 菜单是否对正确管理级别显示;
3. 会话过期后的重新登录路径;
4. 服务端端点的角色和范围校验;
5. History 模式下刷新是否能由宿主正确回退。
前端隐藏按钮不是权限控制。所有敏感操作必须由服务端再次拒绝越权请求。
### API 调用
统一使用 `src/lib/api.js`,不要在组件中重复实现 Cookie、JSON 解析和错误状态处理。401 表示会话失效时,应引导重新登录,不应仅提示用户刷新页面。
### 构建产物
```powershell
Set-Location .\src\Eis.Web\ClientApp
npm run build
```
Vite 固定输出:
- `wwwroot/vue-app/app.js`
- `wwwroot/vue-app/app.css`
- 必要的 chunks 和 assets
不要直接修改构建产物;源码变更后重新运行构建。
## 5. 后端开发
### 进程启动
`src/Eis.Web/Program.cs` 的主要顺序:
1. 查找应用根目录并加载 `.env`
2. 建立配置和依赖注入;
3. 初始化数据库;
4. 检查认证后端;
5. 注册异常处理和安全响应头;
6. 注册健康检查;
7. 注册公开、认证、考生、管理端点;
8. 为未知 `/api/*` 返回原生 JSON 404
9. 映射前端静态资源和 SPA 回退。
### 新增业务
推荐顺序:
1.`Eis.Application` 定义清晰的服务契约。
2.`Eis.Infrastructure` 实现业务和持久化。
3. SQLite 与 MySQL 走相同业务规则,并分别验证 SQL 差异。
4.`Eis.Web` 只处理 HTTP 输入、身份上下文和响应映射。
5. 添加服务层或基础设施测试。
6. 再接入 Vue 页面。
不要把复杂审批、录取或成绩规则直接写在端点委托或 Vue 组件中。
### 端点约定
- `/api/public/...`:公开读取。
- `/api/auth/...`:登录、注册、密码、TOTP 和退出。
- `/api/candidate/...`:考生本人业务。
- `/api/admin/...`:管理员业务。
- `/api/admission/...`:招生学校业务。
未知 API 会返回:
```json
{
"ok": false,
"message": "API 接口不存在"
}
```
新增端点时要避免与兜底路由冲突,并保持错误响应对前端可理解。
## 6. 数据库开发
系统同时支持 SQLite 与 MySQL 8.4。数据库变更必须考虑:
- 两份 schema 资源;
- 外键、唯一约束和索引;
- v20 结构版本;
- 初始化空库;
- 已有数据库的版本检查;
- 数据库工具的 seed/reset 行为;
- 考试和学校专属物理分表;
- 对现有测试的影响。
应用只会在空库自动创建当前结构。检测到低版本数据库时会要求先备份并执行明确升级流程,不应静默猜测迁移。
业务写入与审计日志应在同一事务内提交。预览、扫描预检、Excel 解析等明确标注为 preview 的操作不得修改数据库。
涉及真实数据前先阅读[数据库与数据工具](Database-Operations)。
## 7. 认证、权限和敏感信息
- 密码通过 PBKDF2 加盐哈希保存。
- TOTP 密钥使用 AES-256-GCM 加密。
- 恢复码只保存带服务端密钥的哈希。
- 登录使用 HttpOnly、SameSite Cookie。
- 每个端点按角色和业务数据范围授权。
- 多实例部署时必须使用 Redis 共享认证状态。
实现管理功能时,至少验证:
1. 角色是否允许;
2. 管理级别是否允许;
3. 学校或班级范围是否匹配;
4. 目标业务状态是否允许写入;
5. 是否需要审批或审计;
6. 归档状态是否应锁定操作。
日志、异常和 API 响应中不要输出密码、TOTP 密钥、恢复码、数据库连接密码或完整敏感身份信息。
## 8. 缓存开发
缓存命名空间当前包括 `public``results`。写操作完成后要使受影响命名空间失效。
Redis 不可用时,公开/结果缓存可回退到有界本机缓存;认证状态不同:一旦明确配置认证 Redis,连接失败会阻止启动,避免多实例随机掉线。
新增缓存时需要考虑:
- key 是否包含完整业务范围;
- TTL
- 并发热点请求合并;
- 写后失效;
- Redis 和本机回退的一致性;
- 敏感数据是否适合缓存。
## 9. Excel 与文书
Excel 导入遵守:
- 限制文件大小;
- 逐行验证;
- 返回明确行号;
- 预览与确认分离;
- 确认阶段原子写入;
- 不绕过审批和数据范围。
成绩单和录取通知书包含 HMAC 防伪查询码。任何影响文书载荷或验真算法的变更,都要补充 `DocumentVerificationCodeServiceTests` 并检查历史兼容性。
## 10. 自动化测试
运行全部测试:
```powershell
dotnet test .\Eis.slnx
```
运行指定测试项目:
```powershell
dotnet test .\tests\Eis.Infrastructure.Tests\Eis.Infrastructure.Tests.csproj
```
按测试名称筛选:
```powershell
dotnet test .\Eis.slnx --filter FullyQualifiedName~DatabaseInitializerTests
```
前端生产构建:
```powershell
Set-Location .\src\Eis.Web\ClientApp
npm ci
npm run build
```
纯 .NET 发布产物烟测:
```powershell
pwsh.exe -NoLogo -NoProfile -NonInteractive -File .\scripts\smoke-dotnet-native.ps1
```
测试使用临时 SQLite 数据库,不应连接生产数据库。
## 11. 发布前本地检查
推荐顺序:
```powershell
dotnet test .\Eis.slnx
pwsh.exe -NoLogo -NoProfile -NonInteractive -File .\scripts\publish.ps1
pwsh.exe -NoLogo -NoProfile -NonInteractive -File .\scripts\smoke-dotnet-native.ps1
git diff --check
git status --short
```
再人工检查:
- 首页和登录;
- 不同角色菜单;
- 关键列表筛选和分页;
- 401 后重新登录;
- Excel 预览不写库;
- 归档后的写入锁定;
- 公开缓存写后失效;
- 直接刷新语义化 URL。
## 12. 变更注意事项
- 保留已有业务工作流,不用“最终状态”替代每轮即时结果。
- 敏感规则同时修改前端交互和服务端校验。
- 管理台账应提供搜索、有效筛选、结果范围选择、批量操作、分页和清楚状态文案。
- 审批详情应展示完整上下文。
- 数据脚本要可重复、可指定目标、支持 dry-run 和备份。
- 不把测试库结果当作生产库现状。
- 更新配置或脚本后,明确是否需要修改 npm/.NET 命令和部署文档。
+80 -1
@@ -1 +1,80 @@
欢迎来到百科。
# 衡准 · 考试信息管理系统 Wiki
衡准考试信息管理系统(EIS)是一套面向考试机构、学校、班级、考生和招生学校的完整考试业务平台。系统覆盖账号申领、考生建档、考试报名、缴费确认、审批流、考点考场、准考证编排、成绩与复议、中考志愿、投档录取、报到补录、通知公告和文书验真。
本 Wiki 同时面向系统使用人员、部署运维人员和开发人员。第一次接触项目时,建议先阅读[快速开始](Quick-Start);准备参与开发时,从[开发指南](Development-Guide)和[系统架构](Architecture)开始。
## 项目概览
| 项目 | 当前实现 |
| --- | --- |
| 后端 | ASP.NET Core 10 / C#,单进程模块化单体 |
| 前端 | Vue 3、Vue Router、Vite |
| 数据库 | SQLite(开发与单机部署)、MySQL 8.4(生产) |
| 缓存与会话 | 可选 Redis;未配置时使用进程内实现 |
| 文件能力 | ClosedXML 导入导出 Excel,浏览器端生成 PDF 文书 |
| 认证安全 | PBKDF2、HttpOnly Cookie、可选 TOTP、恢复码 |
| 当前数据库结构 | v20 |
| 默认服务地址 | `http://127.0.0.1:4173` |
## 按身份阅读
### 系统使用人员
- [用户使用指南](User-Guide):公开服务、考生注册登录、首次登录、报名、准考证、成绩、志愿与录取。
- [管理操作指南](Administration-Guide):超级管理员、校级管理员、班级管理员和招生学校的功能边界及推荐操作顺序。
- [常见问题与故障排查](Testing-and-Troubleshooting):登录、页面、数据、导入、缓存和部署常见问题。
### 开发人员
- [快速开始](Quick-Start):安装依赖、创建配置、构建前端、启动后端和联调。
- [开发指南](Development-Guide):项目结构、开发工作流、前后端约定、测试和提交前检查。
- [系统架构](Architecture):分层、请求流程、权限、数据库、缓存和前端结构。
- [配置参考](Configuration):全部常用环境变量、默认值和生产要求。
### 部署与运维人员
- [数据库与数据工具](Database-Operations)SQLite、MySQL、初始化、演示数据、重建和安全边界。
- [部署与发布](Deployment-and-Release):本机发布、自包含程序包、Docker、Gitea Actions 和上线检查。
- [常见问题与故障排查](Testing-and-Troubleshooting):健康检查、日志定位、版本不匹配和 Redis 故障。
## 业务角色
| 角色 | 主要职责 | 数据范围 |
| --- | --- | --- |
| 考生 | 完善资料、报名、查看准考证与成绩、申请复议、填报志愿、查看录取 | 仅本人 |
| 班级管理员 | 管理本班考生、报名审批、缴费状态、复议流程 | 指定班级 |
| 校级管理员 | 管理本校组织、批量申领报名号、审核本校业务、维护考点申请 | 指定学校 |
| 超级管理员 | 全局配置、考试与成绩、流程监督、录取管理、通知发布 | 全部数据 |
| 招生学校 | 提交招生计划、审核投档、登记报到、设计通知书 | 本招生学校 |
| 公开访客 | 阅读公告、查看公开考试信息、验证成绩单和录取通知书 | 已公开数据 |
权限不只由前端菜单控制,服务端还会按角色、管理级别、学校、班级和招生学校范围再次校验。
## 核心业务主线
1. 超级管理员维护组织、管理员、报名号规则和审批流程。
2. 校级管理员按班级申请考生账号,审批完成后安全下发报名号和初始密码。
3. 考生首次登录修改密码、完善资料,随后选择考试和科目报名。
4. 各级管理员完成资料、报名、缴费和相关审批。
5. 超级管理员配置考试、考点考场并执行准考证预检与编排。
6. 超级管理员录入并发布成绩;考生可查看结果并发起成绩复议。
7. 启用招生录取后,系统继续处理指标资格、招生计划、志愿、投档、退档、正式录取、报到与补录。
8. 系统按阶段发布公告和脱敏公示,并为成绩单及录取通知书提供防伪验真。
## 重要安全提示
> 生产环境必须分别设置至少 32 个字符的 `TOTP_ENCRYPTION_KEY` 和 `DOCUMENT_VERIFICATION_SECRET`。两者必须相互独立并长期保存。更换前者会导致已绑定 TOTP 无法解密,更换后者会导致历史文书验真码失效。
> `database reset` 和 `database seed` 会改写业务数据。执行前必须停止应用、确认数据库目标并完成备份。MySQL 生产业务库不要导入演示数据。
> `docker compose down` 会保留 SQLite 数据卷;`docker compose down --volumes` 会删除命名卷中的数据库,仅应在明确需要清空全部容器数据时使用。
## 文档维护约定
- 功能行为以当前源码和自动化测试为准。
- 环境变量新增或默认值变化时,同步更新[配置参考](Configuration)和 `.env.example`
- 数据库工具参数变化时,同步更新[数据库与数据工具](Database-Operations)。
- 发布平台或镜像名称变化时,同步更新[部署与发布](Deployment-and-Release)。
- `src/Eis.Web/wwwroot/vue-app` 是 Vite 构建产物,不应手工修改。
+159
@@ -0,0 +1,159 @@
# 快速开始
本页用于在 Windows PowerShell 环境中快速启动完整开发环境。项目根目录以下均以仓库根目录为当前目录。
## 1. 准备开发环境
需要安装:
- .NET 10 SDK。仓库 `global.json` 指定 `10.0.302`,允许使用同一特性带中的更高补丁版本。
- Node.js 20.19 或更高版本,推荐 Node.js 24。
- npm。
- Git。
- 可选:Docker Desktop,用于容器方式运行。
- 可选:MySQL 8.4 和 Redis,用于验证生产拓扑。
检查版本:
```powershell
dotnet --version
node --version
npm --version
git --version
```
## 2. 创建本地配置
复制环境变量模板:
```powershell
Copy-Item -LiteralPath .\.env.example -Destination .\.env
```
默认模板使用:
- `Development` 环境;
- `http://127.0.0.1:4173`
- SQLite
- `./data/exam.sqlite`
- 初始超级管理员 `admin`
首次本地启动可直接使用模板。若数据库是空库,应用会自动创建 v20 结构、系统基础配置、默认审批流程和一个初始超级管理员。
开发环境默认账号取自 `.env`
```text
账号:INITIAL_ADMIN_USERNAME
密码:INITIAL_ADMIN_PASSWORD
```
模板中的密码只适合本地开发,部署前必须更换。
## 3. 安装前端依赖并构建
```powershell
Set-Location .\src\Eis.Web\ClientApp
npm ci
npm run build
Set-Location ..\..\..
```
构建产物写入 `src/Eis.Web/wwwroot/vue-app`。该目录由 Vite 管理,不要直接编辑其中的 `app.js``app.css`
## 4. 恢复并启动后端
```powershell
dotnet restore .\Eis.slnx
dotnet run --project .\src\Eis.Web\Eis.Web.csproj
```
打开:
- 系统首页:<http://127.0.0.1:4173>
- 登录页:<http://127.0.0.1:4173/auth/login>
- 存活检查:<http://127.0.0.1:4173/health/live>
- 组件状态:<http://127.0.0.1:4173/health/migration>
`/health/live` 返回宿主是否存活;`/health/migration` 会展示原生模块、认证状态后端和缓存状态。
## 5. 前后端联调模式
后端继续运行在 `4173`。另开一个 PowerShell 终端:
```powershell
Set-Location .\src\Eis.Web\ClientApp
npm run dev
```
访问 <http://127.0.0.1:5173>。Vite 会把以下路径代理到 ASP.NET Core
- `/api`
- `/health`
- `/js`
前端使用 Vue Router History 模式。直接刷新 `/candidate/...``/admin/...``/admission/...` 时,由 ASP.NET Core 的前端资源回退逻辑返回 SPA 入口。
## 6. 首次登录后的基础配置
推荐按以下顺序建立可用业务环境:
1. 使用初始超级管理员登录。
2. 在“账户安全”中修改管理员密码;需要时绑定 TOTP。
3. 在“学校管理”中建立学校并标记生源校或招生校职责。
4. 建立校级和班级管理员。
5. 在“报名号规则”中确认号码段组合。
6. 在“流程设计”中确认资料、报名、复议和档案变更审批步骤。
7. 建立考试、科目、时间、费用和合格规则。
8. 由校级管理员批量申领考生账号,或由超级管理员临时开启自主注册。
如果只需要查看完整样例流程,可使用数据库工具导入内置演示数据。该操作会改写目标数据库,请先阅读[数据库与数据工具](Database-Operations),不要未经确认直接对现有数据库执行。
## 7. Docker 快速启动
创建容器配置:
```powershell
Copy-Item -LiteralPath .\.env.docker.example -Destination .\.env.docker
```
打开 `.env.docker`,至少替换:
- `TOTP_ENCRYPTION_KEY`
- `DOCUMENT_VERIFICATION_SECRET`
- `INITIAL_ADMIN_PASSWORD`
三者应使用不同的安全随机值。随后运行:
```powershell
docker compose up --build --detach
docker compose ps
docker compose logs --follow app
```
打开 <http://127.0.0.1:4173>。
停止但保留 SQLite 数据:
```powershell
docker compose down
```
## 8. 最小验证
启动成功后至少完成:
```powershell
dotnet test .\Eis.slnx
```
并在浏览器检查:
1. 首页可加载;
2. `/health/live` 返回 `healthy`
3. 初始管理员可登录;
4. `/admin/dashboard` 可进入;
5. 页面刷新不会出现 404
6. `data/exam.sqlite` 已生成。
更完整的检查方式见[测试与故障排查](Testing-and-Troubleshooting)。
+374
@@ -0,0 +1,374 @@
# 测试与故障排查
本页用于开发验证和运行故障定位。先确定当前使用的服务地址、运行环境和数据库目标,再执行检查。
## 1. 自动化测试
全部测试:
```powershell
dotnet test .\Eis.slnx
```
Release 配置:
```powershell
dotnet test .\Eis.slnx --configuration Release
```
指定项目:
```powershell
dotnet test .\tests\Eis.Infrastructure.Tests\Eis.Infrastructure.Tests.csproj
```
筛选:
```powershell
dotnet test .\Eis.slnx --filter FullyQualifiedName~DatabaseInitializerTests
```
测试覆盖:
- 空库 v20 初始化;
- 数据库维护安全;
- 认证状态后端;
- 密码和 TOTP 历史兼容;
- 文书防伪;
- 缓存;
- 公告内容处理;
- Excel
- 地区数据;
- 成绩统计;
- 招生公示和迁移能力。
xUnit 使用独立临时 SQLite 数据库,不应连接或修改生产库。
## 2. 前端验证
生产构建:
```powershell
Set-Location .\src\Eis.Web\ClientApp
npm ci
npm run build
```
开发服务器:
```powershell
npm run dev
```
检查:
- 控制台无模块加载错误;
- `/api` 请求代理到 `4173`
- 直接打开和刷新 Vue 子路由正常;
- 401 后进入登录流程;
- 不同角色菜单正确;
- 移动端布局可操作;
- `wwwroot/vue-app` 由构建生成。
项目当前没有单独的前端单元测试脚本,因此前端变更至少要通过 `npm run build` 和相关页面人工验证。
## 3. 发布烟测
```powershell
pwsh.exe -NoLogo -NoProfile -NonInteractive -File .\scripts\smoke-dotnet-native.ps1
```
脚本会:
- 发布 `Eis.Web`
- 在系统临时目录创建一次性 SQLite 数据库;
- 使用随机本机端口启动发布物;
- 检查健康状态、首页和必要静态资源;
- 使用临时超级管理员登录;
- 确认未知 API 返回 404
- 停止进程并清理临时数据。
烟测不会使用仓库 `data/exam.sqlite`
## 4. 手工烟测清单
### 公开页面
- 首页返回 200
- 公告列表和详情可用;
- 未发布内容不可见;
- 验真码成功和失败状态清楚;
- 无效 `/api/...` 返回 JSON 404。
### 认证
- 正确密码登录;
- 错误密码被拒绝;
- Cookie 为 HttpOnly
- 登出后受保护接口不可用;
- TOTP 动态码和恢复码可用;
- 恢复码只能使用一次;
- 会话过期后重新登录。
### 数据范围
- 班级管理员看不到其他班;
- 校级管理员看不到其他学校;
- 招生学校看不到其他学校投档;
- 考生只能查看本人数据;
- 手工调用隐藏端点仍被服务端拒绝。
### 关键流程
- 批量申领报名号;
- 首次登录;
- 资料和报名审批;
- 缴费确认;
- 编排预检和正式应用;
- 成绩导入预览和确认;
- 成绩发布和复议;
- 志愿、投档、正式录取;
- 报到扫描预览和确认;
- 每轮补录与公开公示。
## 5. 健康检查解释
### `/health/live` 无法访问
可能原因:
- 进程未启动;
- `ASPNETCORE_URLS` 不是预期地址;
- 端口被占用;
- 容器端口未映射;
- 应用在初始化数据库或认证 Redis 时已经退出。
先查看应用标准输出、容器日志或服务管理器日志。
### `/health/migration` 中缓存为 `disabled`
没有配置 `REDIS_URL`,应用使用本机缓存。这在单实例开发环境正常。
### 缓存为 `unavailable`
普通 Redis 配置存在但暂时连接失败,应用回退到本机缓存。检查:
- URL 和 TLS scheme
- DNS、端口和防火墙;
- 容器是否错误使用 `127.0.0.1`
- Redis 认证;
- `REDIS_CONNECT_TIMEOUT_MS`
### 认证后端为 `memory`
未配置 Redis 认证状态。单实例可用,但重启会要求重新登录;多实例部署不应使用。
## 6. 应用启动失败
### 生产环境缺少 TOTP 密钥
错误含义:原生认证已启用,`TOTP_ENCRYPTION_KEY` 少于 32 字符。
处理:通过秘密管理注入新的稳定密钥,不要把密钥写进仓库。
### 生产环境缺少文书密钥
`DOCUMENT_VERIFICATION_SECRET` 必须至少 32 字符,并与 TOTP 密钥不同。
### 默认尝试连接 MySQL
`Production` 下未设置 `DATABASE_CLIENT` 时默认使用 MySQL。若这是单机 SQLite 部署,显式设置:
```text
DATABASE_CLIENT=sqlite
SQLITE_PATH=/明确的持久化路径/exam.sqlite
```
### MySQL 配置不完整
设置 `DATABASE_URL`,或完整设置主机、用户和数据库名。检查 `.env` 是否位于应用根目录,以及部署平台是否注入了空或错误同名变量。
### Redis 认证状态与缓存使用同一 DB
设置不同的 `REDIS_SESSION_DB`,或使用独立 `REDIS_SESSION_URL`
### 数据库结构版本过低
当前要求 v20。停止应用,备份完整数据库并执行明确升级流程。不要直接 seed/reset 生产库。
## 7. 前端页面问题
### `4173` 页面显示旧版本
后端托管的是上次 Vite 构建产物。重新运行:
```powershell
Set-Location .\src\Eis.Web\ClientApp
npm run build
```
不要手工修改 `wwwroot/vue-app`
### `5173` API 请求失败
确认:
- ASP.NET Core 正在 `127.0.0.1:4173` 运行;
- Vite 使用仓库中的 `vite.config.js`
- 请求路径以 `/api``/health``/js` 开头;
- 没有另一个服务占用 `4173`
### 刷新子页面 404
开发时应访问 Vite 的 `5173` 或 ASP.NET Core 的 `4173`。生产反向代理不要自行把 `/api/*` 改写为 SPA 首页;其他前端路由应转发给 ASP.NET Core 的资源回退逻辑。
### 登录后回到首页
可能是:
- 会话请求失败;
- 用户角色与目标路由不匹配;
- 考生仍需修改初始密码或完善资料;
- Cookie 未随请求发送;
- 多实例未共享 Redis Session。
打开浏览器网络面板检查 `/api/auth/me`,不要只反复刷新。
## 8. 数据库问题
### SQLite 数据“丢失”
先确认实际绝对路径:
- 仓库启动:通常是 `data/exam.sqlite`
- 发布目录:相对路径可能按应用根目录解析;
- Docker`/app/data/exam.sqlite`
- 测试:系统临时目录中的一次性文件。
最常见原因是启动时使用了另一个工作目录或环境变量,创建了新的空 SQLite 文件。
### Docker 重建后数据不见
检查是否执行过:
```powershell
docker compose down --volumes
```
以及 Compose 是否仍挂载 `exam-information-data:/app/data`
### MySQL 外部修改暂时看不到
应用会复用只读快照,外部直写默认最长 30 秒后可见。应用自身写入会立即失效。不要把直接改数据库作为正常业务操作。
### 工具拒绝操作
这是安全边界。按[数据库与数据工具](Database-Operations)确认目标、dry-run、备份和 `--confirm-target`,不要先使用 `--force` 试错。
## 9. Redis 和缓存问题
### 写入后公开页面仍旧
确认写操作是否触发正确命名空间失效。公开公告、招生公示和可见性变更应失效 `public`;成绩录入、发布、导入、归档和复议应失效 `results`
### 多实例随机掉登录
各实例可能使用本机内存会话或连接不同 Redis。确认:
- 所有实例 `REDIS_SESSION_URL`/DB 相同;
- `REDIS_SESSION_PREFIX` 相同;
- TOTP 加密密钥相同且稳定;
- 实例没有因配置缺失退回内存。
### Redis Cluster 报逻辑 DB 错误
Cluster 常只支持 DB 0。让普通缓存和认证状态使用两个独立 Redis 端点,不要依赖 DB 0/1 分隔。
## 10. Excel 导入问题
### 返回具体行错误
按行修复后重新上传。常见原因:
- 必填列空;
- 标识 ID 被改动;
- 日期或数字格式不合法;
- 目标学校、班级、考试不存在;
- 重复唯一值;
- 数据超出当前管理员范围;
- 使用了旧版本模板。
### 预览成功但数据库没变化
预览阶段本来就不写数据库。需要在页面核对摘要后执行明确确认。
### 部分数据成功、部分失败
关键批量写入设计为原子事务。若页面显示部分变化,先确认是否查看了旧缓存、多个批次或之前已有的数据,再检查服务日志和审计记录。
## 11. 成绩与录取问题
### 管理员已录入但考生看不到
录入和发布是不同状态。确认整场或对应成绩是否正式发布。
### 无法修改成绩
检查:
- 考试是否归档;
- 当前账号是否超级管理员;
- 是否处于导入预览阶段;
- 复议流程是否允许当前动作。
### 考生无法填志愿
检查:
- 考试是否启用招生;
- 成绩是否全部发布;
- 是否在填报阶段;
- 是否达到提交上限;
- 指标资格和计划是否已经生效。
### 无法补录
检查上一轮报到是否已由所有相关学校提交并完成审批,缺额是否存在,以及当前录取阶段是否允许启动下一轮。
## 12. 日志与问题报告
报告问题时提供:
- 应用版本或 Git 提交;
- 操作系统和部署方式;
- .NET、Node、浏览器版本;
- `ASPNETCORE_ENVIRONMENT`
- 数据库类型和结构版本,不提供密码;
- Redis 是否配置及状态,不提供完整凭据;
- 复现步骤;
- 预期与实际结果;
- 浏览器控制台和网络状态;
- 服务端异常前后日志;
- 是否能在临时测试库复现。
不要在问题报告中粘贴:
- `.env` 全文;
- 数据库密码;
- TOTP 密钥;
- 恢复码;
- Session Cookie
- 完整证件号、手机号或未脱敏考生数据。
## 13. 提交前最终检查
```powershell
dotnet test .\Eis.slnx
pwsh.exe -NoLogo -NoProfile -NonInteractive -File .\scripts\publish.ps1
pwsh.exe -NoLogo -NoProfile -NonInteractive -File .\scripts\smoke-dotnet-native.ps1
git diff --check
git status --short
```
如果改动只涉及 Wiki,至少检查 Markdown 链接、命令路径和当前源码配置是否一致。
+210
@@ -0,0 +1,210 @@
# 用户使用指南
本页面向公开访客和考生。管理员和招生学校请阅读[管理操作指南](Administration-Guide)。
## 1. 公开服务
无需登录即可使用:
- 首页:查看机构信息、主标语、已发布考试、报名时间、考试时间和科目。
- 通知公告:查看已公开公告、录取与报到公示。
- 文书验真:输入成绩单或录取通知书上的防伪查询码,或直接扫描二维码。
- 登录:考生和管理员共用登录入口,系统按账号角色进入对应工作台。
- 考生注册:仅在超级管理员开启自主注册后显示并可用。
公开页面只展示已经发布或明确允许公开的数据。录取公示中的证件号、手机号等敏感信息会脱敏。
## 2. 考生账号来源
考生账号有两种来源:
### 学校统一申领
学校管理员按一个或多个班级提交人数,审批通过后系统生成:
- 固定报名号;
- 随机初始密码;
- 待补录的考生账户。
报名号创建后不会因参加新的考试而改变。同一考生后续报名、准考证、成绩和录取均复用这个号码。
### 自主注册
超级管理员可随时开启或关闭自主注册。开启后,考生按页面要求注册,系统依据当前报名号规则生成固定报名号。
请勿重复注册。已经由学校领取报名号的考生应使用学校下发的账号。
## 3. 登录与首次设置
访问 `/auth/login`,输入报名号和密码。
学校下发的新账号首次登录后必须完成:
1. 修改初始密码;
2. 填写个人资料;
3. 提交资料审核。
在上述步骤完成前,系统会把考生引导到“首次登录”页面,不能直接办理其他考试事项。
建议新密码:
- 至少 8 位;
- 不与报名号、姓名或初始密码相同;
- 不与其他网站共用;
- 由本人保存,不交给班级管理员代管。
## 4. 个人资料
“个人资料”用于维护:
- 姓名、性别、证件号码;
- 出生日期、籍贯、民族;
- 省、市、县区;
- 学校、班级;
- 手机、邮箱、家庭地址、邮编;
- 监护人、紧急联系人及联系电话;
- 体育或艺术特长资格及证明;
- 政策资格信息。
提交后可查看审核状态和管理员意见。被退回时,根据意见修改并重新提交。资料变更可能进入配置好的多步骤审批流程,页面显示成功不等于正式档案已经立即生效。
## 5. 考试报名
进入“考试报名”:
1. 查看当前开放的考试。
2. 阅读报名时间、考试时间、科目、费用和相关说明。
3. 选择一个或多个科目。
4. 提交报名。
提交后在“我的报名”中查看:
- 所报考试与科目;
- 报名审核状态;
- 应缴金额;
- 缴费状态;
- 缴费确认办理人和时间;
- 班级负责人确认记录。
报名终审和缴费确认是两套独立状态。报名审核通过不表示已经缴费,缴费已确认也不替代报名审批。
系统不接入支付 SDK。实际收费方式以考试机构通知为准,管理员只在系统中登记缴费状态。
## 6. 准考证
进入“准考证”查看:
- 是否已经编排;
- 是否到达开放下载时间;
- 考点、考场、座位;
- 各科考试时间;
- 准考证下载入口。
多科目考生会固定在同一考点,但不同科目可能安排在不同考场和座位。未到开放时间、报名未满足条件或尚未完成编排时,系统不会提供正式下载。
## 7. 成绩与复议
进入“成绩查询”:
- 只显示已经发布的成绩;
- 按考试查看各科成绩、名次、等级和达线结果;
- 查看整场总分及合格结论;
- 下载带防伪查询码和二维码的 PDF 成绩单。
如需复议:
1. 找到对应科目;
2. 填写具体复议理由;
3. 提交后查看审批进度;
4. 等待最终结论。
复议批准并发生改分后,系统会在同一事务中更新成绩,并重新计算该考生受影响的排名区间结论。已经归档的考试不能再通过复议改分。
## 8. 志愿填报与录取
只有考试启用了招生录取且到达相应阶段时,才会显示可操作入口。
### 填报前提
- 当次考试成绩已经全部发布;
- 处于允许填报的时间和阶段;
- 未超过最多提交次数;
- 志愿尚未因提交次数用尽而锁定。
### 志愿类型
- 指标志愿:仅对学校管理员已经确认有指标资格,且招生学校对生源校分配了相应指标的考生可选。
- 普通志愿:从审核生效的普通招生计划中选择。
- 特长类别:页面只显示与本人体育或艺术资格相匹配的类别。
志愿只能由考生本人保存或修改。班级和校级管理员不能查看考生志愿;超级管理员仅能在业务需要下只读查看。
系统按“分数优先、遵循志愿”处理投档,并严格区分指标计划池与普通计划池。
### 录取阶段
考生可查看:
- 投档学校和当前状态;
- 退档处理结果;
- 正式录取学校;
- 录取通知书编号;
- 招生学校自定义样式的 PDF 录取通知书;
- 报到和补录相关公开通知。
正式签发后,录取通知书编号稳定生成,不应因普通页面刷新而变化。
## 9. 通知公告
考生中心的“通知公告”用于查看与考试相关的最新通知。首页只展示公开可见内容;草稿、已撤回通知和未到公开阶段的系统公示不会显示。
请特别关注:
- 报名开放与截止时间;
- 缴费和资料补正要求;
- 准考证开放时间;
- 成绩发布时间与复议截止时间;
- 志愿填报、录取、报到和补录安排。
## 10. 账户安全
“账户安全”支持:
- 修改密码;
- 绑定 TOTP 验证器;
- 重新生成一次性恢复码;
- 关闭二次验证。
启用 TOTP 时:
1. 输入当前密码开始绑定;
2. 使用验证器应用扫描二维码;
3. 输入 6 位动态码完成启用;
4. 立即保存恢复码。
每个恢复码只能使用一次。恢复码关闭页面后不会再次显示同一组明文;重新生成后,旧恢复码失效。
## 11. 常见状态理解
| 状态 | 含义 |
| --- | --- |
| 待补录 | 账号已创建,但考生资料尚未完整建立 |
| 待审核 | 已提交,等待当前流程责任人处理 |
| 已退回 | 需要根据意见修改后重新提交 |
| 已通过 | 当前审批流程完成 |
| 未缴费 | 系统尚未记录缴费确认 |
| 已缴费 | 管理员已经记录办理人和确认时间 |
| 未发布 | 数据可能已录入,但考生端不可见 |
| 已发布 | 考生可以正式查询 |
| 已归档 | 考试进入只读历史状态,关键写操作被锁定 |
## 12. 使用问题处理
- 登录过期:返回登录页重新登录,不要反复刷新原页面。
- 忘记密码:联系有权限的管理员重置;管理员不能查看原密码。
- 看不到考试:确认报名时间、考试发布状态和个人资料是否满足要求。
- 看不到成绩:成绩可能尚未发布,不以管理员已录入为准。
- 无法填志愿:检查成绩是否全部发布、填报阶段、提交次数和指标资格。
- PDF 无法下载:确认浏览器允许下载,并检查是否到达开放时间。
- 验真失败:确认查询码完整;若系统密钥被更换,历史文书查询码可能失效。
+13
@@ -0,0 +1,13 @@
## 衡准考试系统
- [Wiki 首页](Home)
- [快速开始](Quick-Start)
- [用户使用指南](User-Guide)
- [管理操作指南](Administration-Guide)
- [开发指南](Development-Guide)
- [系统架构](Architecture)
- [配置参考](Configuration)
- [数据库与数据工具](Database-Operations)
- [部署与发布](Deployment-and-Release)
- [测试与故障排查](Testing-and-Troubleshooting)