Files
EIS-dotnet/README.md
T
biss 3162a5d3a3
Build and publish packages and Docker images / Test, build and publish (push) Failing after 9m24s
修改自动构建
2026-07-24 08:51:24 +08:00

412 lines
28 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 衡准 · 考试信息管理系统
一个完整可运行的分级权限考试信息管理系统,前端使用 Vue 3 + Vite,后端使用 ASP.NET Core 10;本地开发采用 SQLite,生产环境支持 MySQL 8.4。
## 已实现功能
### 公开服务首页
- 通知公告首页展示与详情阅读
- 已发布考试、报名时间、考试时间和科目展示
- 考试服务办理流程说明
- 考生注册与双角色登录入口
### 考生中心
- 报名号即考生账户,同一考生参加不同考试始终使用同一个号码
- 学校管理员按一个或多个班级填写人数并提交批量申领(单人也按 1 人批次走审批)
- 首次登录强制修改初始密码,完成后才能补全个人信息
- 完整维护姓名、性别、证件号码、籍贯、出生日期、民族、家庭住址、手机号、邮箱、学校、班级、监护人和紧急联系人
- 自主注册可由超级管理员随时开启或关闭;开启后系统直接生成固定报名号
- 资料审核状态与管理员审核意见
- 查看开放考试并自主选择多个报考科目
- 查看报名审核、应缴金额、缴费状态及班级负责人确认记录
- 准考证生成状态、开放时间与下载
- 已发布成绩查询,并可按科目提交成绩复议、查看审批进度与结论
- 下载带 HMAC 防伪查询码和二维码的 PDF 成绩单
- 查看正式录取结果、录取通知书编号,并下载招生学校自定义样式的 PDF 录取通知书
- 通知公告中心
### 管理后台
- 超级、校级、班级三级管理员,同一级支持多个账号
- 超级管理员管理全局事务,并可监督、修改、退回全部审批流程
- 校级管理员管理本校班级、班级管理员、考生和报名流程,按班级批量申领报名号,并提交本校考点、考场档案变更
- 班级管理员可查看并审批本班考生、报名与成绩复议流程,并维护本班考生缴费状态;成绩录入仍仅限超级管理员
- 报名终审与缴费确认相互独立,系统不接入支付 SDK;超级、校级、班级管理员均可在各自数据范围内修改缴费状态,确认缴费时记录办理人和时间
- 超级、校级、班级管理员均可按各自数据范围筛选、查看及导出 Excel 缴费名单
- 考生信息修改、考试报名、成绩复议、批量报名号申领、考点考场变更使用可配置的多步骤审批流程
- 班级和校级审批自动限定到考生所属班级、学校;同范围多名管理员按当前待办与历史分配量自动均分
- 当前处理人可将流程转交给同范围的同级管理员
- 自定义报名号生成规则,可组合年份、学校代码、性别、固定值和流水号
- 报名号在创建考生账户时只生成一次,后续考试报名自动复用
- 超级管理员只维护号码规则;校级批量申请最终批准后,系统原子生成固定报名号、随机初始密码和待补录账户
- 批次结果按班级返回校级管理员,并可导出 Excel 安全下发
- 结构化考点与考场档案,包含代码、负责人、应急电话、开放时间、交通、楼栋、楼层、容量、座位编排说明、类型和状态
- 考点新增及考点/考场修改先形成申请快照,审批通过后才整体更新正式档案
- 班级、班级管理员、报名号班级配额、考生资料、考点考场和成绩均提供 Excel 模板、导入与当前数据导出;只读角色保留对应导出能力
- Excel 导入逐行校验并返回具体行号;考生资料和考点考场的批量修改仍必须经过配置好的审批流程
- 考务指标与审计日志
- 考生资料审核、通过或退回修改
- 考试报名及科目审核
- 创建考试并结构化配置科目日期、时间、费用和满分;每科可独立选择固定分、排名前百分比或不设单科线,并汇总总分
- 支持固定总分线、总成绩排名前百分比、单科均达线及不判定四类整场合格策略
- 通知发布、草稿、撤回及首页置顶
- 按整场考试预检并批量编排准考证,支持班内、校内、县区内、市内和省内五级混编
- 预置“县区编号+考场号+座位号”“县区号+考场号+流水号”“考点学校代码+考场号+座位号”“考生学校代码+考场号+座位号”四种号码规则
- 多科目考生固定在同一考点,各科独立分配考场和座位;同科目组合优先相邻编排
- 编排前校验科目时间冲突、考点容量、档案完整性和号码唯一性,默认保留备用考场并支持稳定种子复现
- 每科可独立设置固定及格分、排名前百分比或不设单科线;成绩等级按同场同科排名百分位自动计算
- 默认排名等级区间为前 10% A+、前 25% A、前 50% B+、前 70% B、前 90% C、其余 D;同分共享名次
- 成绩管理中心按多场考试切换,展示录入/发布进度、成绩出齐人数、整场合格率、缺失科次与复议数量,并提供可筛选成绩台账
- 成绩复议终审表单展示考试、科目、原分、当前排名和达线规则;批准后在同一事务内更新成绩并只重算该考生的排名区间结论
- 成绩 Excel 采用“上传解析与逐行校验—页面暂存预览—确认后原子批量写库”的两阶段流程,预览不会修改数据库
- 超级管理员可将整场考试不可逆归档;归档后手工录入、Excel 导入、复议改分和考试配置全部锁定,历史报名、准考证与成绩默认折叠展示
- 管理员与考生接口权限隔离
### 系统能力
- PBKDF2 加盐密码哈希
- 可选 TOTP 二次验证,支持验证器扫码绑定、一次性恢复码与登录防重放
- TOTP 密钥使用 AES-256-GCM 加密存储,恢复码仅保存带服务端密钥的哈希
- HttpOnly、SameSite 登录 Cookie
- 服务端角色权限校验
- SQLite / MySQL 8.4 双数据库持久化
- 可选 Redis 公开接口缓存,支持写后版本失效、热点请求合并和故障回源
- Redis 登录状态存储,和普通接口缓存使用不同逻辑数据库,支持多实例共享会话
- 规范关系模型、外键、唯一约束和业务索引
- 业务写入与审计日志使用原子事务提交
- 关键管理操作审计日志
- 组织、学校、班级三级数据范围在服务端强制过滤
- 审批实例、当前责任人、转交和监督操作全程留痕
- 桌面端与移动端响应式布局
- Excel 文件使用 ClosedXML 生成和解析,并限制上传文件大小
- 成绩单与录取通知书以 PDF 下载,使用服务端 HMAC 防伪码支持公开验真
## 运行
需要 .NET 10 SDK、Node.js 20.19+(推荐 Node.js 24)和 npm。
```powershell
Set-Location .\src\Eis.Web\ClientApp
npm ci
npm run build
Set-Location ..\..\..
dotnet restore .\Eis.slnx
dotnet run --project .\src\Eis.Web\Eis.Web.csproj
```
打开 <http://127.0.0.1:4173>。
日常开发前端时,可以让 ASP.NET Core 继续监听 `4173`,另开一个终端启动 Vite
```powershell
Set-Location .\src\Eis.Web\ClientApp
npm run dev
```
然后打开 <http://127.0.0.1:5173>。Vite 会把 API 和 PDF 文书生成模块代理到 ASP.NET Core。前端已整体迁移为 Vue 3 单页应用,使用 Vue Router History 模式;公开服务、考生中心、管理后台和招生学校端均使用独立的语义化 URL。生产构建输出到 `src/Eis.Web/wwwroot/vue-app`,不得手工修改该目录中的文件。
账户可在“账户安全”中启用 TOTP 二次验证。生产环境必须设置至少 32 个字符的 `TOTP_ENCRYPTION_KEY`;该值用于加密 TOTP 密钥并保护恢复码哈希,部署后必须稳定保存,不能随意更换。本地开发未设置时会使用仅适合开发的稳定派生值。
生产环境还必须单独设置至少 32 个字符的 `DOCUMENT_VERIFICATION_SECRET`。系统用它为成绩单和录取通知书生成 HMAC 防伪查询码;更换该值会使此前下载文书的查询码失效,因此应独立生成、稳定保存且不得与 TOTP 密钥共用。
本地开发无需额外配置,首次运行会自动创建 `data/exam.sqlite` 和完整关系型数据库结构,但不会导入学校、考生、考试或报名测试数据。首次建库会写入系统基础配置、默认审批流程和一个超级管理员;账号、密码和显示名可通过 `INITIAL_ADMIN_USERNAME``INITIAL_ADMIN_PASSWORD``INITIAL_ADMIN_DISPLAY_NAME` 设置。当前数据库结构版本为 v20。
统一发布 Web 和数据库工具:
```powershell
pwsh.exe -NoLogo -NoProfile -NonInteractive -File .\scripts\publish.ps1
```
发布结果分别位于 `artifacts/publish/web``artifacts/publish/tools`。服务器启动 Web
```powershell
dotnet .\artifacts\publish\web\Eis.Web.dll
```
数据库工具使用编译后的 `Eis.Tools.dll`,不需要源代码或 `dotnet run`
```powershell
dotnet .\artifacts\publish\tools\Eis.Tools.dll database init --sqlite --path .\data\exam.sqlite
dotnet .\artifacts\publish\tools\Eis.Tools.dll database seed --sqlite --path .\data\demo.sqlite --dry-run
```
### Docker
项目根目录包含生产镜像和 Docker Compose 配置。默认使用 SQLite,数据库保存在命名卷 `exam-information-data` 中,因此重建容器不会丢失数据。
先创建容器环境文件,并将其中的 TOTP 主密钥、文书防伪签名密钥和初始管理员密码替换为相互独立的安全随机值:
```powershell
Copy-Item .env.docker.example .env.docker
```
然后构建并启动:
```powershell
docker compose up --build --detach
```
启动完成后访问 <http://127.0.0.1:4173>。查看状态和日志可运行:
```powershell
docker compose ps
docker compose logs --follow app
```
停止服务使用 `docker compose down`;该命令会保留数据库卷。只有明确需要删除全部 SQLite 数据时才使用 `docker compose down --volumes`
也可以只构建镜像:
```powershell
docker build --tag hengzhun-exam-system:local .
```
镜像包含 ASP.NET Core 10 Web 宿主和编译后的 `/app/tools/Eis.Tools.dll`,默认监听 `0.0.0.0:4173`,以非 root 用户运行,并通过 `/health/live` 执行健康检查。需要连接 MySQL 或 Redis 时,用运行环境变量覆盖 `DATABASE_CLIENT``DATABASE_URL`/`MYSQL_*``REDIS_URL`;认证状态默认使用同一 Redis 服务的独立 DB 1,也可以通过 `REDIS_SESSION_DB``REDIS_SESSION_URL` 单独配置。此时 SQLite 数据卷可以移除。
容器内执行数据库工具前应先停止 Web,SQLite 目标必须与命名卷路径完全一致:
```powershell
docker compose stop app
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
docker compose up --detach app
```
#### Gitea Actions 自动发布跨平台程序包与 Docker 镜像
工作流位于 `.gitea/workflows/publish.yml`。它会构建 Vue 前端、使用 .NET 10 运行测试,然后默认生成以下无需预装 .NET 运行时的自包含程序包:
- Windows`win-x64``.zip`
- Linux`linux-x64``linux-arm64``.tar.gz`
每个程序包都把 Web 与数据库工具放在同一个 `app` 目录中,使二者共享 .NET 运行时和公共依赖;根目录还包含 `README.md``LICENSE` 和环境变量示例,构建结果同时附带 `SHA256SUMS`。标签构建会把这些文件上传到同名 Gitea Release;手动构建可在 Actions 运行记录中下载归档。普通代码推送不会触发这套耗时的发布构建。
解压后,Windows 使用 `.\app\Eis.Web.exe` 启动服务,Linux/macOS 使用 `./app/Eis.Web`;数据库工具分别为 `.\app\Eis.Tools.exe``./app/Eis.Tools`
如需 Windows ARM64、macOS Intel 或 Apple Silicon 包,从 Gitea Actions 页面手动运行工作流并勾选“同时构建 Windows ARM64 和 macOS x64/ARM64”。扩展构建会在默认三种平台之外增加 `win-arm64``osx-x64``osx-arm64`
同一工作流还会构建 `linux/amd64``linux/arm64` 双架构 Docker 镜像,并同时推送到 Docker Hub 与 `git.biss.click/biss/exam-information-system`
使用前需要完成以下配置:
1. 在 Docker Hub 创建目标仓库,并创建具有该仓库 Read & Write 权限的访问令牌。
2. 在 Gitea 仓库的 Actions Variables 中添加 `DOCKERHUB_IMAGE`,值为不带 registry 和 tag 的完整镜像名,例如 `yourname/exam-information-system`
3. 在 Gitea 仓库的 Actions Secrets 中添加 `DOCKERHUB_USERNAME``DOCKERHUB_TOKEN`。前者填写 Docker Hub 用户名,后者填写访问令牌,不要填写账户密码。
4. 使用对 `biss` 组织拥有软件包写权限的 Gitea 账号,在“设置 → 应用 → 生成新令牌”中创建具有 Package Read & Write 权限的个人访问令牌。在仓库 Actions Variables 中添加 `REGISTRY_USERNAME`,值为该令牌所属的用户名;在 Actions Secrets 中添加 `REGISTRY_TOKEN`,值为个人访问令牌。自定义 Secret 不能使用 Gitea 保留的 `GITEA_` 前缀,也不能用工作流内置的 `GITEA_TOKEN` 代替该软件包令牌。
5. 确保 Gitea Actions 与仓库的软件包注册表已启用,并且 `ubuntu-latest` Runner 能访问 Docker daemon、GitHub、Docker Hub 和 `git.biss.click`。双架构构建还需要 Runner 允许 QEMU 注册步骤运行。
推送形如 `v1.2.3` 的 Git 标签后,会自动构建跨平台程序包,发布镜像标签 `1.2.3``1.2``latest` 和对应的提交标签,并在构建成功后创建带下载附件的同名正式 Gitea Release。例如:
```powershell
git tag v1.2.3
git push origin v1.2.3
```
带连字符预发布后缀的语义化版本标签会自动创建 Gitea Pre-release,例如:
```powershell
git tag v1.3.0-rc.1
git push origin v1.3.0-rc.1
```
该版本的镜像标签为 `1.3.0-rc.1`,不会覆盖稳定版的 `1.3``latest` 标签。除版本标签外,也可以从 Gitea Actions 页面手动运行工作流;普通分支推送不会触发发布。
构建缓存保存为同一 Docker Hub 仓库中的 `buildcache` 标签,以加快后续构建。Release 使用工作流内置的 `GITEA_TOKEN` 创建,无需添加额外 Secret;仓库或组织“Actions → General”中的任务令牌最大权限必须允许 Releases Write。
首次成功推送后,容器镜像会出现在 `biss` 所有者的软件包列表。Gitea 的软件包归属于用户或组织,不会天然归属于某个仓库;打开该软件包的设置页面,将它关联到 `Exam-Information-System`,即可让它显示在此仓库的“软件包”页。之后可使用 `docker pull git.biss.click/biss/exam-information-system:latest` 拉取。
应用会在空库上自动创建结构和基础配置。测试数据导入和空库重建均由编译后的 `Eis.Tools.dll` 完成;旧 Node 导入入口已移除。演示数据包内置于 `Eis.Infrastructure.dll`,生产服务器不需要 Node.js。
## 数据库配置
应用启动时会自动读取项目根目录的 `.env`,可先运行 `Copy-Item .env.example .env` 创建配置文件。命令行或部署平台已经注入的进程环境变量优先于 `.env`。应用根据 `DATABASE_CLIENT` 使用不同数据库;未设置时,开发/测试环境默认 `sqlite`ASP.NET Core 生产环境默认 `mysql`
公开首页的机构名称、机构代码、电话、地址、邮箱、主标语和页脚提示分别由 `PUBLIC_SITE_NAME``PUBLIC_SITE_CODE``PUBLIC_SITE_PHONE``PUBLIC_SITE_ADDRESS``PUBLIC_SITE_EMAIL``PUBLIC_SITE_HERO_*``PUBLIC_SITE_FOOTER_NOTICE` 配置。修改 `.env` 后需要重启应用;这些配置会覆盖数据库中的演示机构信息,且只通过公开首页接口返回非敏感展示字段。
### 本地 SQLite
```powershell
$env:DATABASE_CLIENT = 'sqlite'
$env:SQLITE_PATH = './data/exam.sqlite'
dotnet run --project .\src\Eis.Web\Eis.Web.csproj
```
`SQLITE_PATH` 可省略,默认路径就是 `./data/exam.sqlite`
### 生产 MySQL 8.4
先在 MySQL 8.4 中创建数据库和最小权限账号:
```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'@'%';
```
启动应用时设置连接信息,应用会自动创建以下关系表和系统基础配置,但不会自动写入测试业务数据:
- `schools``school_classes``users``candidate_profiles`
- `school_student_partitions`(学校学生专属表登记)
- `exams``exam_subjects`
- `exam_data_partitions`(考试专属表登记)
- `registrations``registration_subjects`
- `admission_number_rules``exam_arrangement_plans``admit_cards``admit_card_subjects`
- `results``notices``audit_logs`
- `test_centers``test_rooms``center_change_requests``center_change_rooms`
- `number_rules``number_rule_segments`
- `candidate_account_batches``candidate_account_batch_items`
- `workflow_definitions``workflow_steps``workflow_instances``workflow_actions`
- `organization``schema_metadata`
所有关联均有外键约束,账号、证件号、考试代码、报名关系、准考证号和单科成绩均有对应唯一约束。
系统采用“总表索引 + 独立物理分表”存储:创建每场考试时,会立即创建该场考试专用的
`exam_<分区键>_candidates``exam_<分区键>_admissions``exam_<分区键>_results`
`exam_<分区键>_centers` 四张表;创建每所学校时,会创建 `school_<分区键>_students`
学生专属表。分区键由业务 ID 的 SHA-256 摘要生成,不直接拼接用户输入。报名、缴费、准考证编排、
成绩和学生资料发生变化后,专属表会自动同步;总表继续承担跨考试、跨学校查询和外键完整性约束。
```powershell
$env:ASPNETCORE_ENVIRONMENT = 'Production'
$env:DATABASE_CLIENT = 'mysql'
$env:MYSQL_HOST = '127.0.0.1'
$env:MYSQL_PORT = '3306'
$env:MYSQL_USER = 'exam_app'
$env:MYSQL_PASSWORD = 'replace-with-a-strong-password'
$env:MYSQL_DATABASE = 'exam_information'
$env:ASPNETCORE_URLS = 'http://0.0.0.0:4173'
dotnet run --project .\src\Eis.Web\Eis.Web.csproj
```
也可以只设置标准连接地址 `DATABASE_URL=mysql://user:password@host:3306/database`。完整模板见 `.env.example`;将模板复制为 `.env` 后取消 MySQL 配置项的注释并填写实际连接信息即可。生产部署仍建议由部署平台注入环境变量,避免在服务器文件中保存密码。
### Redis 缓存(可选)
配置 `REDIS_URL` 后,应用会缓存公开首页、已发布公告详情和每名考生的已发布成绩查询。公开数据默认 TTL 为 60 秒,成绩默认 TTL 为 24 小时;成绩录入/发布、批量导入、考试归档和成绩复议会自动使成绩缓存失效,超级管理员也可以在成绩管理中心手动刷新全部成绩缓存。Redis 未配置或暂时不可用时,应用会自动切换到有界本机缓存(默认最多 200 条),避免热点接口反复回源;写入后的失效语义保持不变。
同一个 `REDIS_URL` 还会启用 Redis 认证状态存储:登录 Session、TOTP 登录挑战和 TOTP 绑定临时状态全部写入 Redis。普通接口缓存按 `REDIS_URL` 使用 DB 0 时,认证状态默认自动选择独立 DB 1;可通过 `REDIS_SESSION_DB` 修改逻辑 DB,或通过 `REDIS_SESSION_URL` 指向另一台 Redis。应用会拒绝让认证状态与普通缓存使用同一 Redis 端点的同一逻辑 DB。认证 Redis 配置后连接失败会阻止服务启动,避免多实例之间悄悄退回本机状态而出现随机掉线。完全未配置 Redis 时,认证状态仍使用本机内存,服务重启会要求重新登录。
数据库关系表会按写入版本复用进程内只读快照,避免每个请求重复扫描并转换全部业务表。应用自身写入会立即使快照失效;SQLite 还通过 `PRAGMA data_version` 检测其他连接的提交,MySQL 外部直写默认最多延迟 30 秒可见。可通过 `DATABASE_STATE_CACHE_TTL_MS` 调整 MySQL 快照时间,通过 `LOCAL_CACHE_MAX_ENTRIES` 控制本机接口缓存上限。
```powershell
$env:REDIS_URL = 'redis://127.0.0.1:6379/0'
$env:REDIS_CACHE_PREFIX = 'exam-information'
$env:REDIS_CACHE_TTL_SECONDS = '60'
$env:REDIS_RESULTS_CACHE_TTL_SECONDS = '86400'
$env:REDIS_SESSION_DB = '1'
$env:AUTH_SESSION_TTL_SECONDS = '28800'
dotnet run --project .\src\Eis.Web\Eis.Web.csproj
```
生产环境可使用 `redis://` 或启用 TLS 的 `rediss://` 连接地址,并通过 `REDIS_CONNECT_TIMEOUT_MS` 调整启动连接超时。若 Redis Cluster 不支持非 0 逻辑 DB,请用 `REDIS_SESSION_URL` 为认证状态配置独立 Redis 实例。
### 导入服务器 MySQL 测试数据
先停止正在运行的应用进程,确认服务器 `.env` 中已经设置 `DATABASE_CLIENT=mysql` 及完整 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
```
工具会读取 `.env`,拒绝 MySQL 系统数据库,校验 v20 结构和已有数据,并在同一事务内清理、导入。若检测到无法识别为内置演示数据的业务记录,默认拒绝覆盖。仅在确认目标确实是可完全覆盖的测试库时追加:
```powershell
dotnet .\artifacts\publish\tools\Eis.Tools.dll database seed --mysql --confirm-target exam_test --force
```
强制模式会删除该 MySQL 数据库内现有应用数据并在同一事务中写入样例数据,但不会删除数据库本身。MySQL 操作前应另行完成数据库备份;不要对生产业务库执行该命令。
## 中考志愿填报与招生录取
系统可按考试单独启用志愿填报,未启用的考试不会出现志愿入口。完整流程如下:
1. 超级管理员设置填报时间、普通志愿数、最多提交次数和当前阶段;考生只有在当次成绩全部发布后才能填报,达到提交上限后自动锁定。
2. 招生学校账号以结构化表单上传本校普通生、特长生与生源校指标分配计划,超级管理员审核后生效;超级管理员也可代上传并直接审核。
3. 生源校学校管理员按考试逐人确认指标分配资格;本校资料已完善的在册考生全部确认后,系统自动公开有无资格及对应特长类型。超级管理员和班级管理员均不能代确认。
4. 每名考生有一个专用指标分配志愿栏,只有确认有资格且招生校对本校分配了对应指标时可选;其余均为普通志愿。志愿只能由考生本人保存或修改,班级、校级管理员无权查看,超级管理员只读可见。
5. 超级管理员结束填报并执行投档。系统按总成绩降序逐个检索志愿,严格区分指标计划池与普通计划池,并遵循“分数优先、遵循志愿”。
6. 投档材料只发送到对应招生学校,包含必要考生资料与当次成绩,不包含考生其余志愿。学校可接收或填写特殊理由申请退档,退档由超级管理员统一审核。
7. 超级管理员签发正式录取后,系统按“招生学校代码 + 考试代码 + 校内独立流水号”生成稳定的录取通知书编号,并开启招生学校报到工作台。
8. 招生学校可逐人暂存 Y/N/P 报到状态,也可导出带下拉校验的 Excel、修改后导入,或扫描录取通知书二维码核验并登记;完整报到情况提交前不会进入审批。
9. 学校提交报到情况后可选择不补录或申请补录。超级管理员审批所有学校决定后,系统按缺额进入下一轮补录或结束录取;计划录取率和实际报到率在双方工作台实时显示。
10. 审批通过的报到情况会自动进入公开通知,包含计划数、正式录取数、已报到数、缺额和学校说明;无补录时标题不会出现“补录”。录取结束后继续自动发布脱敏录取名单及按学校、类别统计的录取分数线。
公开公示固定包含报名号、姓名、考生总成绩和录取学校;证件号、手机号等重要身份信息只提供脱敏值。考生档案中的特长资格按“体育 / 艺术”大类与对应小类登记,志愿页面先按学校代码选择招生校,再仅显示符合本人资格的该校类别。
学校统一在“学校管理”中维护,并可分别标记为生源校、招生校或同时具备两类职责。每场考试报名都包含独立于科目的 `feature_score`(特征分),默认 0,由超级管理员登记;招生学校可设计本校录取通知书的标题、正文、落款和配色,通知书不再添加“录取专用章”。管理后台以一级业务域分组,并把招生录取拆为录取设置、招生账户、招生计划、报到与补录、投档监督等二级菜单。
数据结构版本为 v20`admission_records` 关系表新增指标资格、资格公示和分数线公告记录,并支持 SQLite / MySQL 自动迁移。新角色值为 `admission_school`
## 手动测试数据账号
测试数据脚本会提供以下账号;其中初始超级管理员也可能由正常首次建库创建,并可通过环境变量改名、改密,其余校级、班级和考生账号不会在正常启动时创建:
为了便于临时联调,所有预置样例账号统一使用密码 `12345678`,且预置考生不会在首次登录时被要求改密。通过系统业务流程后续新建的账号仍按正式规则生成随机初始密码。
| 角色 | 账号 | 密码 |
| --- | --- | --- |
| 超级管理员 | `admin` | `12345678` |
| 超级管理员(监督演示) | `supervisor` | `12345678` |
| 校级管理员 | `school_admin` | `12345678` |
| 同校校级管理员(转交演示) | `school_admin_2` | `12345678` |
| 班级管理员 | `class_admin` | `12345678` |
| 同班班级管理员(均分演示) | `class_admin_2` | `12345678` |
| 考生 | `2026-HZ01-F-0001` | `12345678` |
## 测试结束后初始化系统
先停止应用。SQLite 重建会自动生成带时间戳的原库备份,然后恢复系统基础配置和一个初始超级管理员:
```powershell
dotnet .\artifacts\publish\tools\Eis.Tools.dll database reset --sqlite --path .\data\exam.sqlite --confirm-target .\data\exam.sqlite
```
MySQL 应先执行 `--dry-run` 确认目标,再明确填写数据库名:
```powershell
dotnet .\artifacts\publish\tools\Eis.Tools.dll database reset --mysql --dry-run
dotnet .\artifacts\publish\tools\Eis.Tools.dll database reset --mysql --confirm-target exam_test
```
MySQL 模式会自动识别由本项目生成的批量演示数据;目标包含非演示业务记录时仍会拒绝,只有确认可覆盖时才能追加 `--force`。初始化管理员由 `.env` 中的 `INITIAL_ADMIN_USERNAME``INITIAL_ADMIN_PASSWORD``INITIAL_ADMIN_DISPLAY_NAME` 决定。
## 自动化测试
```powershell
dotnet test .\Eis.slnx
pwsh.exe -NoLogo -NoProfile -NonInteractive -File .\scripts\smoke-dotnet-native.ps1
```
xUnit 测试使用独立临时 SQLite 数据库,覆盖认证与历史数据兼容性、端点启用约束、文书防伪码、文件处理、缓存以及空库 v20 初始化;测试不会连接或修改生产数据库。
## 项目结构
项目采用 ASP.NET Core 10 模块化单体架构,由一个 `Eis.Web` 进程部署;HTTP、权限、业务服务、数据库适配和前端页面按职责分层。
```text
Eis.slnx .NET 10 解决方案
src/Eis.Domain 领域模型与业务值
src/Eis.Application 应用服务契约
src/Eis.Infrastructure SQLite/MySQL、认证、缓存、Excel 与业务实现
src/Eis.Tools 可独立发布的数据库初始化、重建和演示数据工具
src/Eis.Web ASP.NET Core 宿主、端点和静态资源
src/Eis.Web/ClientApp Vue 3 单文件组件、Vite 配置与前端源码
src/Eis.Web/wwwroot/vue-app Vue 生产构建产物
src/Eis.Web/wwwroot/js 渐进迁移期间保留的业务模块
src/Eis.Web/wwwroot 浏览器入口、基础样式及 CKEditor
tests/Eis.Infrastructure.Tests xUnit 自动化测试
scripts/publish.ps1 Web 与数据库工具统一发布脚本
scripts/publish-platform-packages.sh CI 跨平台自包含程序包构建脚本
scripts/smoke-dotnet-native.ps1 纯 .NET 发布产物烟测
data/exam.sqlite 本地运行后生成的 SQLite 数据库
.env.example 开发与生产环境变量模板
```